From f6b95e4ad8a1826724b90c35f5dd713f530be856 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 20 Mar 2024 14:36:48 +0000 Subject: [PATCH 001/147] update zeroize --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index e52212fce6108..c71ef85db3064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -726,6 +726,7 @@ warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" x25519-dalek = "1.2.0" +zeroize = "=1.7.0" #Override to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES move-abigen = { path = "third_party/move/move-prover/move-abigen" } From 1787d80515b1b634ca332ba4ec1883c90c3f4aa4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:56:42 +0000 Subject: [PATCH 002/147] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c71ef85db3064..4121935943aa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -726,7 +726,7 @@ warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" x25519-dalek = "1.2.0" -zeroize = "=1.7.0" #Override to this version to be compatible with Sovereign Labs +zeroize = "1.6.0" #Override to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES move-abigen = { path = "third_party/move/move-prover/move-abigen" } From 6b28c8a07f3f4d797a67c696d8e2603e355bf71a Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:02:08 +0000 Subject: [PATCH 003/147] Update Cargo.toml --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4121935943aa0..ae97982b584bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -725,8 +725,8 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -x25519-dalek = "1.2.0" -zeroize = "1.6.0" #Override to this version to be compatible with Sovereign Labs +x25519-dalek = "2.1.0" #Set to this version to be compatible with Sovereign Labs +zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES move-abigen = { path = "third_party/move/move-prover/move-abigen" } From c013d4356ac62c5df1411289ca0a3bd25f4e83cf Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:03:53 +0000 Subject: [PATCH 004/147] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ae97982b584bd..53de93562fffd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -725,7 +725,7 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -x25519-dalek = "2.1.0" #Set to this version to be compatible with Sovereign Labs +x25519-dalek = "1.9.0" #Set to this version to be compatible with Sovereign Labs zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES From bd1644729bc2598d9769fbf556797d5a4f51bf35 Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:05:09 +0000 Subject: [PATCH 005/147] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 53de93562fffd..626936b1953fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -725,7 +725,7 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -x25519-dalek = "1.9.0" #Set to this version to be compatible with Sovereign Labs +x25519-dalek = "2.0.0" #Set to this version to be compatible with Sovereign Labs zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES From 6611115e391f0738f99630f38cde6917000814e8 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 20 Mar 2024 19:31:34 +0000 Subject: [PATCH 006/147] add into --- crates/aptos-metrics-core/src/const_metric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aptos-metrics-core/src/const_metric.rs b/crates/aptos-metrics-core/src/const_metric.rs index c354b0eed4921..fa8ca0f4c6949 100644 --- a/crates/aptos-metrics-core/src/const_metric.rs +++ b/crates/aptos-metrics-core/src/const_metric.rs @@ -63,7 +63,7 @@ impl ConstMetric { let mut metric = Metric::default(); metric.set_gauge(guage); - metric.set_label(labels); + metric.set_label(labels.into()); Ok(ConstMetric { desc, From d3cc82ac6739bd1b9f2f532ddfe79fc261dceb5d Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 20 Mar 2024 19:47:08 +0000 Subject: [PATCH 007/147] more into --- crates/aptos-metrics-core/src/const_metric.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/aptos-metrics-core/src/const_metric.rs b/crates/aptos-metrics-core/src/const_metric.rs index fa8ca0f4c6949..744ff1b7c989c 100644 --- a/crates/aptos-metrics-core/src/const_metric.rs +++ b/crates/aptos-metrics-core/src/const_metric.rs @@ -37,7 +37,7 @@ impl ConstMetric { let mut metric = Metric::default(); metric.set_counter(counter); - metric.set_label(labels); + metric.set_label(labels.into()); Ok(ConstMetric { desc, @@ -84,7 +84,7 @@ impl Collector for ConstMetric { met.set_name(self.desc.fq_name.clone()); met.set_help(self.desc.help.clone()); met.set_field_type(self.metric_type); - met.set_metric(vec![self.metric.clone()]); + met.set_metric(vec![self.metric.clone()].into()); vec![met] } From bfa7d8f9a6467e4b5c84344c2024f60a306536d4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:46:19 +0000 Subject: [PATCH 008/147] update visibility --- aptos-move/aptos-vm/src/block_executor/mod.rs | 4 +- package-lock.json | 1467 +++++++++++++++++ 2 files changed, 1469 insertions(+), 2 deletions(-) create mode 100644 package-lock.json diff --git a/aptos-move/aptos-vm/src/block_executor/mod.rs b/aptos-move/aptos-vm/src/block_executor/mod.rs index 76e303eeacdfd..de8b39cc52b20 100644 --- a/aptos-move/aptos-vm/src/block_executor/mod.rs +++ b/aptos-move/aptos-vm/src/block_executor/mod.rs @@ -55,14 +55,14 @@ pub struct AptosTransactionOutput { } impl AptosTransactionOutput { - pub(crate) fn new(output: VMOutput) -> Self { + pub fn new(output: VMOutput) -> Self { Self { vm_output: Mutex::new(Some(output)), committed_output: OnceCell::new(), } } - pub(crate) fn committed_output(&self) -> &TransactionOutput { + pub fn committed_output(&self) -> &TransactionOutput { self.committed_output.get().unwrap() } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000..e5267619e1896 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1467 @@ +{ + "name": "aptos-core", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@types/node": "^18.11.0", + "prettier": "^2.7.1", + "ts-node": "^10.9.1", + "typescript": "^4.8.4", + "zx": "^7.1.1" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", + "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ps-tree": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/ps-tree/-/ps-tree-1.1.6.tgz", + "integrity": "sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ==", + "dev": true + }, + "node_modules/@types/which": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz", + "integrity": "sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fx": { + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/fx/-/fx-33.0.0.tgz", + "integrity": "sha512-uW/UAi9G04+o7dD/RyIH7mP9Cyf12TdiaWQ19QbvnxkKQ2yiffXiZMz65zqbWMstLd2vwla++G9lMabG3nXxYQ==", + "dev": true, + "bin": { + "fx": "index.js" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpod": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/webpod/-/webpod-0.0.2.tgz", + "integrity": "sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg==", + "dev": true, + "bin": { + "webpod": "dist/index.js" + } + }, + "node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/zx": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/zx/-/zx-7.2.3.tgz", + "integrity": "sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^11.0.1", + "@types/minimist": "^1.2.2", + "@types/node": "^18.16.3", + "@types/ps-tree": "^1.1.2", + "@types/which": "^3.0.0", + "chalk": "^5.2.0", + "fs-extra": "^11.1.1", + "fx": "*", + "globby": "^13.1.4", + "minimist": "^1.2.8", + "node-fetch": "3.3.1", + "ps-tree": "^1.2.0", + "webpod": "^0", + "which": "^3.0.0", + "yaml": "^2.2.2" + }, + "bin": { + "zx": "build/cli.js" + }, + "engines": { + "node": ">= 16.0.0" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "requires": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "@types/node": { + "version": "18.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", + "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/ps-tree": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/ps-tree/-/ps-tree-1.1.6.tgz", + "integrity": "sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ==", + "dev": true + }, + "@types/which": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz", + "integrity": "sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==", + "dev": true + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fx": { + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/fx/-/fx-33.0.0.tgz", + "integrity": "sha512-uW/UAi9G04+o7dD/RyIH7mP9Cyf12TdiaWQ19QbvnxkKQ2yiffXiZMz65zqbWMstLd2vwla++G9lMabG3nXxYQ==", + "dev": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, + "node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "requires": { + "through": "2" + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true + }, + "webpod": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/webpod/-/webpod-0.0.2.tgz", + "integrity": "sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg==", + "dev": true + }, + "which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "zx": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/zx/-/zx-7.2.3.tgz", + "integrity": "sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA==", + "dev": true, + "requires": { + "@types/fs-extra": "^11.0.1", + "@types/minimist": "^1.2.2", + "@types/node": "^18.16.3", + "@types/ps-tree": "^1.1.2", + "@types/which": "^3.0.0", + "chalk": "^5.2.0", + "fs-extra": "^11.1.1", + "fx": "*", + "globby": "^13.1.4", + "minimist": "^1.2.8", + "node-fetch": "3.3.1", + "ps-tree": "^1.2.0", + "webpod": "^0", + "which": "^3.0.0", + "yaml": "^2.2.2" + } + } + } +} From 830bb59ead3c5082734486adf1dd5177d349d2a1 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 4 Apr 2024 16:25:45 +0200 Subject: [PATCH 009/147] derive serde for ValidatorSigner --- types/src/validator_signer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/src/validator_signer.rs b/types/src/validator_signer.rs index 8414012654705..bc4220faf57a7 100644 --- a/types/src/validator_signer.rs +++ b/types/src/validator_signer.rs @@ -8,14 +8,14 @@ use aptos_crypto::{ Uniform, }; use rand::{rngs::StdRng, SeedableRng}; -use serde::ser::Serialize; +use serde::{Serialize, Deserialize}; use std::convert::TryFrom; /// ValidatorSigner associates an author with public and private keys with helpers for signing and /// validating. This struct can be used for all signing operations including block and network /// signing, respectively. #[derive(Debug)] -#[cfg_attr(any(test, feature = "fuzzing"), derive(Clone))] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Clone, Serialize, Deserialize))] pub struct ValidatorSigner { author: AccountAddress, private_key: bls12381::PrivateKey, From 3eb1806eca51a850e547fa384baa1bfe2b16a2df Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 8 Apr 2024 16:55:03 +0200 Subject: [PATCH 010/147] feat: monza-testing. --- Cargo.lock | 623 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 2 +- 2 files changed, 320 insertions(+), 305 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6dada90d6c061..12bd2979dd936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ name = "abstract-domain-derive" version = "0.1.0" dependencies = [ "move-stackless-bytecode", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -81,9 +81,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.8" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom 0.2.11", "once_cell", @@ -92,9 +92,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "getrandom 0.2.11", @@ -269,7 +269,7 @@ dependencies = [ "bcs 0.1.4", "bollard", "chrono", - "clap 4.4.14", + "clap 4.4.12", "clap_complete", "codespan-reporting", "dashmap", @@ -567,7 +567,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "bytes", - "clap 4.4.14", + "clap 4.4.12", "csv", "futures", "itertools 0.10.5", @@ -685,7 +685,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "criterion", "dashmap", "itertools 0.10.5", @@ -734,7 +734,7 @@ name = "aptos-cargo-cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "clap-verbosity-flag", "determinator", "env_logger", @@ -762,7 +762,7 @@ name = "aptos-cli-common" version = "1.0.0" dependencies = [ "anstyle", - "clap 4.4.14", + "clap 4.4.12", "clap_complete", ] @@ -793,7 +793,7 @@ dependencies = [ "aptos-vm-logging", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "futures", "itertools 0.10.5", "lru 0.7.8", @@ -918,7 +918,7 @@ dependencies = [ "bytes", "chrono", "claims", - "clap 4.4.14", + "clap 4.4.12", "dashmap", "enum_dispatch", "fail 0.5.1", @@ -1063,7 +1063,7 @@ name = "aptos-crypto-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -1164,7 +1164,7 @@ dependencies = [ "bcs 0.1.4", "byteorder", "claims", - "clap 4.4.14", + "clap 4.4.12", "dashmap", "either", "itertools 0.10.5", @@ -1236,7 +1236,7 @@ dependencies = [ "aptos-vm", "async-trait", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "itertools 0.10.5", "owo-colors", "tokio", @@ -1252,7 +1252,7 @@ dependencies = [ "aptos-logger", "aptos-move-debugger", "aptos-push-metrics", - "clap 4.4.14", + "clap 4.4.12", "tokio", ] @@ -1344,7 +1344,7 @@ name = "aptos-enum-conversion-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", "trybuild", @@ -1463,7 +1463,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "chrono", - "clap 4.4.14", + "clap 4.4.12", "derivative", "indicatif 0.15.0", "itertools 0.10.5", @@ -1500,7 +1500,7 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "crossbeam-channel", "ctrlc", "dashmap", @@ -1628,7 +1628,7 @@ dependencies = [ "aptos-faucet-core", "aptos-logger", "aptos-sdk", - "clap 4.4.14", + "clap 4.4.12", "tokio", ] @@ -1644,7 +1644,7 @@ dependencies = [ "aptos-sdk", "async-trait", "captcha", - "clap 4.4.14", + "clap 4.4.12", "deadpool-redis", "enum_dispatch", "futures", @@ -1686,7 +1686,7 @@ dependencies = [ "anyhow", "aptos-faucet-core", "aptos-logger", - "clap 4.4.14", + "clap 4.4.12", "tokio", ] @@ -1698,7 +1698,7 @@ dependencies = [ "aptos-logger", "aptos-node-checker", "aptos-sdk", - "clap 4.4.14", + "clap 4.4.12", "env_logger", "futures", "gcp-bigquery-client", @@ -1735,7 +1735,7 @@ dependencies = [ "aptos-transaction-generator-lib", "async-trait", "chrono", - "clap 4.4.14", + "clap 4.4.12", "either", "futures", "hex", @@ -1778,7 +1778,7 @@ dependencies = [ "aptos-testcases", "async-trait", "chrono", - "clap 4.4.14", + "clap 4.4.12", "futures", "jemallocator", "once_cell", @@ -1821,7 +1821,7 @@ dependencies = [ "bulletproofs", "byteorder", "claims", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "curve25519-dalek-ng", "either", @@ -1896,7 +1896,7 @@ dependencies = [ "aptos-types", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "float-cmp", "move-binary-format", "move-bytecode-source-map", @@ -1969,7 +1969,7 @@ dependencies = [ "aptos-package-builder", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-core-types", "move-model", "tempfile", @@ -2040,7 +2040,7 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", - "clap 4.4.14", + "clap 4.4.12", "diesel", "diesel_migrations", "field_count", @@ -2073,7 +2073,7 @@ dependencies = [ "async-trait", "backoff", "base64 0.13.1", - "clap 4.4.14", + "clap 4.4.12", "futures", "futures-core", "jemallocator", @@ -2103,7 +2103,7 @@ dependencies = [ "backtrace", "base64 0.13.1", "bytes", - "clap 4.4.14", + "clap 4.4.12", "cloud-storage", "dashmap", "enum_dispatch", @@ -2146,7 +2146,7 @@ dependencies = [ "aptos-runtimes", "async-trait", "base64 0.13.1", - "clap 4.4.14", + "clap 4.4.12", "cloud-storage", "futures", "jemallocator", @@ -2176,7 +2176,7 @@ dependencies = [ "aptos-runtimes", "async-trait", "base64 0.13.1", - "clap 4.4.14", + "clap 4.4.12", "cloud-storage", "futures", "futures-util", @@ -2269,7 +2269,7 @@ dependencies = [ "async-trait", "backoff", "base64 0.13.1", - "clap 4.4.14", + "clap 4.4.12", "futures", "futures-core", "futures-util", @@ -2301,7 +2301,7 @@ dependencies = [ "aptos-runtimes", "async-trait", "backtrace", - "clap 4.4.14", + "clap 4.4.12", "futures", "prometheus", "serde", @@ -2371,7 +2371,7 @@ dependencies = [ "backtrace", "base64 0.13.1", "chrono", - "clap 4.4.14", + "clap 4.4.12", "cloud-storage", "flate2", "futures", @@ -2646,7 +2646,7 @@ dependencies = [ name = "aptos-log-derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -2788,7 +2788,7 @@ dependencies = [ "aptos-vm-logging", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-binary-format", "move-cli", "move-compiler", @@ -2821,7 +2821,7 @@ dependencies = [ "aptos-gas-schedule", "aptos-types", "aptos-vm", - "clap 4.4.14", + "clap 4.4.12", "move-cli", "move-package", "move-prover", @@ -3040,7 +3040,7 @@ dependencies = [ "aptos-logger", "aptos-network", "aptos-types", - "clap 4.4.14", + "clap 4.4.12", "futures", "serde", "tokio", @@ -3087,7 +3087,7 @@ dependencies = [ "base64 0.13.1", "bytes", "chrono", - "clap 4.4.14", + "clap 4.4.12", "csv", "diesel", "diesel_migrations", @@ -3165,7 +3165,7 @@ dependencies = [ "aptos-validator-transaction-pool", "aptos-vm", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "either", "fail 0.5.1", "futures", @@ -3198,7 +3198,7 @@ dependencies = [ "aptos-sdk", "aptos-transaction-emitter-lib", "async-trait", - "clap 4.4.14", + "clap 4.4.12", "const_format", "env_logger", "futures", @@ -3245,7 +3245,7 @@ dependencies = [ name = "aptos-num-variants" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -3272,7 +3272,7 @@ dependencies = [ "aptos-mempool", "aptos-storage-interface", "aptos-types", - "clap 4.4.14", + "clap 4.4.12", ] [[package]] @@ -3457,7 +3457,7 @@ dependencies = [ "aptos-types", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "futures", "git2 0.16.1", "handlebars", @@ -3519,7 +3519,7 @@ dependencies = [ "aptos-types", "bcs 0.1.4", "bytes", - "clap 4.4.14", + "clap 4.4.12", "futures", "hex", "move-binary-format", @@ -3566,7 +3566,7 @@ dependencies = [ "aptos-types", "aptos-warp-webserver", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "futures", "hex", "itertools 0.10.5", @@ -3591,7 +3591,7 @@ dependencies = [ "aptos-logger", "aptos-rosetta", "aptos-types", - "clap 4.4.14", + "clap 4.4.12", "serde", "serde_json", "tokio", @@ -3708,7 +3708,7 @@ dependencies = [ "aptos-framework", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "heck 0.4.1", "move-core-types", "once_cell", @@ -4029,7 +4029,7 @@ dependencies = [ "bcs 0.1.4", "chrono", "claims", - "clap 4.4.14", + "clap 4.4.12", "debug-ignore", "flate2", "futures", @@ -4126,7 +4126,7 @@ dependencies = [ "aptos-types", "aptos-vm", "aptos-vm-logging", - "clap 4.4.14", + "clap 4.4.12", "criterion", "criterion-cpu-time", "num_cpus", @@ -4144,7 +4144,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "aptos-transaction-emitter-lib", - "clap 4.4.14", + "clap 4.4.12", "futures", "rand 0.7.3", "tokio", @@ -4166,7 +4166,7 @@ dependencies = [ "aptos-transaction-generator-lib", "aptos-types", "async-trait", - "clap 4.4.14", + "clap 4.4.12", "futures", "itertools 0.10.5", "once_cell", @@ -4188,7 +4188,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "async-trait", - "clap 4.4.14", + "clap 4.4.12", "move-binary-format", "once_cell", "rand 0.7.3", @@ -4212,7 +4212,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "datatest-stable", "hex", "move-binary-format", @@ -4418,7 +4418,7 @@ dependencies = [ "aptos-language-e2e-tests", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-binary-format", "move-bytecode-source-map", "move-core-types", @@ -4477,7 +4477,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "glob", "move-binary-format", "move-core-types", @@ -4688,7 +4688,7 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -4753,7 +4753,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -4882,7 +4882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", + "event-listener 4.0.2", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -4911,7 +4911,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 2.2.0", + "futures-lite 2.1.0", "slab", ] @@ -4926,7 +4926,7 @@ dependencies = [ "async-io 2.2.2", "async-lock 3.2.0", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.1.0", "once_cell", ] @@ -4960,7 +4960,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.1.0", "parking", "polling 3.3.1", "rustix 0.38.28", @@ -4984,7 +4984,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener 4.0.3", + "event-listener 4.0.2", "event-listener-strategy", "pin-project-lite", ] @@ -5030,9 +5030,9 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -5097,16 +5097,16 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" [[package]] name = "async-trait" @@ -5114,9 +5114,9 @@ version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -5143,7 +5143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -5155,7 +5155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -5321,9 +5321,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.6" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" [[package]] name = "base64-url" @@ -5331,7 +5337,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", ] [[package]] @@ -5422,7 +5428,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -5463,12 +5469,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "regex", "rustc-hash", "shlex", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -5624,7 +5630,7 @@ dependencies = [ "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.1.0", "piper", "tracing", ] @@ -5663,7 +5669,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f03db470b3c0213c47e978da93200259a1eb4dae2e5512cba9955e2b540a6fc6" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "bollard-stubs", "bytes", "futures-core", @@ -6085,9 +6091,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.14" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", "clap_derive 4.4.7", @@ -6099,15 +6105,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" dependencies = [ - "clap 4.4.14", + "clap 4.4.12", "log", ] [[package]] name = "clap_builder" -version = "4.4.14" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstream", "anstyle", @@ -6121,7 +6127,7 @@ version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd" dependencies = [ - "clap 4.4.14", + "clap 4.4.12", ] [[package]] @@ -6132,7 +6138,7 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -6144,9 +6150,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -6345,7 +6351,7 @@ version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "unicode-xid 0.2.4", ] @@ -6386,7 +6392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ "aes-gcm", - "base64 0.21.6", + "base64 0.21.5", "hkdf 0.12.4", "hmac 0.12.1", "percent-encoding", @@ -6462,9 +6468,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -6526,10 +6532,11 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" dependencies = [ + "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -6539,46 +6546,54 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ + "autocfg", + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" dependencies = [ + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] [[package]] name = "crossterm" @@ -6787,9 +6802,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -6844,7 +6859,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", @@ -6858,7 +6873,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", @@ -6872,10 +6887,10 @@ checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "strsim 0.10.0", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -6908,7 +6923,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -7035,7 +7050,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -7046,9 +7061,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -7058,7 +7073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "rustc_version", "syn 1.0.109", @@ -7141,9 +7156,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" dependencies = [ "diesel_table_macro_syntax", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -7163,7 +7178,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -7494,9 +7509,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -7716,9 +7731,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" dependencies = [ "concurrent-queue", "parking", @@ -7731,7 +7746,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.3", + "event-listener 4.0.2", "pin-project-lite", ] @@ -7966,9 +7981,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" [[package]] name = "field_count" @@ -8027,9 +8042,9 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "fixed" -version = "1.25.1" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29e5681dc8556fb9df1409e95eae050e12e8776394313da3546dcb8cf390c73" +checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", @@ -8148,9 +8163,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -8263,9 +8278,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ "fastrand 2.0.1", "futures-core", @@ -8280,9 +8295,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -8429,7 +8444,7 @@ dependencies = [ "aptos-network", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-core-types", "rand 0.7.3", "serde", @@ -8620,7 +8635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "931bedb2264cb00f914b0a6a5c304e34865c34306632d3932e0951a073e4a67d" dependencies = [ "async-trait", - "base64 0.21.6", + "base64 0.21.5", "google-cloud-metadata", "google-cloud-token", "home", @@ -8699,7 +8714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c57ca1d971d7c6f852c02eda4e87e88b1247b6ed8be9fa5b2768c68b0f2ca5" dependencies = [ "async-stream", - "base64 0.21.6", + "base64 0.21.5", "bytes", "futures-util", "google-cloud-auth", @@ -8761,7 +8776,7 @@ version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "114a100a9aa9f4c468a7b9e96626cdab267bb652660d8408e8f6d56d4c310edd" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.7", "camino", "cargo_metadata 0.18.1", "cfg-if", @@ -8808,9 +8823,9 @@ checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -8881,7 +8896,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.8", + "ahash 0.7.7", ] [[package]] @@ -8890,7 +8905,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.7", ] [[package]] @@ -8899,7 +8914,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.7", "allocator-api2", ] @@ -8909,7 +8924,7 @@ version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "byteorder", "flate2", "nom", @@ -8922,7 +8937,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "bytes", "headers-core", "http", @@ -9131,7 +9146,7 @@ dependencies = [ "assert-json-diff", "async-object-pool", "async-trait", - "base64 0.21.6", + "base64 0.21.5", "basic-cookies", "crossbeam-utils", "form_urlencoded", @@ -9318,9 +9333,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" dependencies = [ "crossbeam-deque", "globset", @@ -9407,7 +9422,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -9441,7 +9456,7 @@ checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" dependencies = [ "anyhow", "proc-macro-hack", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -9452,7 +9467,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", ] @@ -9515,8 +9530,8 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ - "ahash 0.8.8", - "clap 4.4.14", + "ahash 0.8.7", + "clap 4.4.12", "crossbeam-channel", "crossbeam-utils", "dashmap", @@ -9559,7 +9574,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" dependencies = [ - "ahash 0.7.8", + "ahash 0.7.7", "dashmap", "hashbrown 0.12.3", "once_cell", @@ -9767,7 +9782,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "pem 1.1.1", "ring 0.16.20", "serde", @@ -9894,7 +9909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98ff3d647085c6c025083efad0435890867f4bea042fc62d408ab3aeb1cdf66" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "serde_json", "syn 1.0.109", @@ -10038,9 +10053,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libfuzzer-sys" @@ -10195,9 +10210,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.13" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f526fdd09d99e19742883e43de41e1aa9e36db0c7ab7f935165d611c5cccc66" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -10234,7 +10249,7 @@ name = "listener" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.14", + "clap 4.4.12", "tokio", ] @@ -10348,7 +10363,7 @@ name = "macros" version = "0.1.0" source = "git+https://github.com/niroco/diesel_async_migrations?rev=11f331b73c5cfcc894380074f748d8fda710ac12#11f331b73c5cfcc894380074f748d8fda710ac12" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", ] @@ -10464,7 +10479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", ] @@ -10517,9 +10532,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -10555,7 +10570,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -10580,7 +10595,7 @@ dependencies = [ "anyhow", "aptos-framework", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-binary-format", ] @@ -10616,7 +10631,7 @@ name = "move-analyzer" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "crossbeam", "derivative", @@ -10728,7 +10743,7 @@ name = "move-bytecode-viewer" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "crossterm 0.26.1", "move-binary-format", "move-bytecode-source-map", @@ -10746,7 +10761,7 @@ dependencies = [ "anyhow", "bcs 0.1.4", "bytes", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "colored", "datatest-stable", @@ -10809,7 +10824,7 @@ version = "0.0.1" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "datatest-stable", "difference", @@ -10848,7 +10863,7 @@ dependencies = [ "abstract-domain-derive", "anyhow", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -10919,7 +10934,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "codespan", "colored", "move-binary-format", @@ -10937,7 +10952,7 @@ name = "move-disassembler" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "colored", "move-binary-format", "move-bytecode-source-map", @@ -11011,7 +11026,7 @@ name = "move-explain" version = "0.1.0" dependencies = [ "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-command-line-common", "move-core-types", ] @@ -11022,7 +11037,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "move-binary-format", "move-bytecode-source-map", "move-bytecode-verifier", @@ -11120,7 +11135,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.4.12", "colored", "datatest-stable", "dirs-next", @@ -11161,7 +11176,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.14", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -11234,7 +11249,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.14", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -11394,7 +11409,7 @@ version = "0.1.0" dependencies = [ "anyhow", "atty", - "clap 4.4.14", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -11436,7 +11451,7 @@ name = "move-transactional-test-runner" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "colored", "datatest-stable", "difference", @@ -11472,7 +11487,7 @@ dependencies = [ "anyhow", "better_any", "bytes", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "colored", "datatest-stable", @@ -11640,7 +11655,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -11764,7 +11779,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -11776,7 +11791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "066b468120587a402f0b47d8f80035c921f6a46f8209efd0632a89a16f5188a4" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -11903,7 +11918,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12069,9 +12084,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -12150,7 +12165,7 @@ checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12163,7 +12178,7 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12245,7 +12260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12257,7 +12272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ "proc-macro-crate 2.0.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12473,9 +12488,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.6" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -12484,9 +12499,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.6" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -12494,22 +12509,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.6" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] name = "pest_meta" -version = "2.7.6" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -12607,7 +12622,7 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12618,9 +12633,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -12697,9 +12712,9 @@ checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "platforms" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "plotters" @@ -12790,9 +12805,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" dependencies = [ "proc-macro-crate 2.0.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -12831,7 +12846,7 @@ dependencies = [ "indexmap 1.9.3", "mime", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "regex", "syn 1.0.109", @@ -12895,7 +12910,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597c3287a549da151aca6ada2795ecde089c7527bd5093114e8e0e1c3f0e52b1" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -12935,7 +12950,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "byteorder", "bytes", "fallible-iterator", @@ -13074,8 +13089,8 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ - "proc-macro2 1.0.76", - "syn 2.0.48", + "proc-macro2 1.0.75", + "syn 2.0.47", ] [[package]] @@ -13154,7 +13169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", "version_check", @@ -13166,7 +13181,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "version_check", ] @@ -13194,9 +13209,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" dependencies = [ "unicode-ident", ] @@ -13206,7 +13221,7 @@ name = "processor" version = "1.0.0" source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352#d44b2d209f57872ac593299c34751a5531b51352" dependencies = [ - "ahash 0.8.8", + "ahash 0.8.7", "anyhow", "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352)", "aptos-protos 1.3.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=5d1cefad0ea0d37fb08e9aec7847080745c83f9c)", @@ -13215,7 +13230,7 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", - "clap 4.4.14", + "clap 4.4.12", "diesel", "diesel-async", "diesel_async_migrations", @@ -13348,7 +13363,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -13381,7 +13396,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -13394,9 +13409,9 @@ checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -13448,7 +13463,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.4.14", + "clap 4.4.12", "codespan-reporting", "hex", "itertools 0.10.5", @@ -13584,7 +13599,7 @@ version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", ] [[package]] @@ -13830,9 +13845,9 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -13886,7 +13901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "async-compression", - "base64 0.21.6", + "base64 0.21.5", "bytes", "cookie 0.16.2", "cookie_store", @@ -14066,7 +14081,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -14168,7 +14183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" dependencies = [ "cfg-if", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "rustc_version", "syn 1.0.109", @@ -14268,9 +14283,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" dependencies = [ "log", "ring 0.17.7", @@ -14299,7 +14314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "schannel", "security-framework", @@ -14329,24 +14344,24 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", ] [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.6", + "base64 0.22.0", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" [[package]] name = "rustls-webpki" @@ -14454,7 +14469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -14466,7 +14481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -14623,7 +14638,7 @@ name = "sender" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.14", + "clap 4.4.12", "event-listener 2.5.3", "quanta", "tokio", @@ -14631,9 +14646,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" dependencies = [ "serde_derive", ] @@ -14716,20 +14731,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" dependencies = [ "indexmap 2.1.0", "itoa", @@ -14764,9 +14779,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -14796,7 +14811,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.6", + "base64 0.21.5", "chrono", "hex", "indexmap 1.9.3", @@ -14814,9 +14829,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ "darling 0.20.3", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -14872,7 +14887,7 @@ dependencies = [ "anyhow", "async-trait", "backtrace", - "clap 4.4.14", + "clap 4.4.12", "futures", "prometheus", "serde", @@ -15004,9 +15019,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "signal-hook" @@ -15299,7 +15314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -15442,7 +15457,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -15472,7 +15487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "rustversion", "syn 1.0.109", @@ -15485,10 +15500,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "rustversion", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -15543,18 +15558,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "unicode-ident", ] @@ -15729,9 +15744,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -15740,9 +15755,9 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", "test-case-core", ] @@ -15750,7 +15765,7 @@ dependencies = [ name = "test-generation" version = "0.1.0" dependencies = [ - "clap 4.4.14", + "clap 4.4.12", "crossbeam-channel", "getrandom 0.2.11", "hex", @@ -15776,7 +15791,7 @@ name = "testdiff" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.4.12", "itertools 0.10.5", "once_cell", "walkdir", @@ -15833,9 +15848,9 @@ version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -15988,9 +16003,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -16079,7 +16094,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.2", + "rustls 0.22.3", "rustls-pki-types", "tokio", ] @@ -16235,7 +16250,7 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.6", + "base64 0.21.5", "bytes", "flate2", "futures-core", @@ -16269,7 +16284,7 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.6", + "base64 0.21.5", "bytes", "flate2", "h2", @@ -16301,7 +16316,7 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.6", + "base64 0.21.5", "bytes", "flate2", "h2", @@ -16313,7 +16328,7 @@ dependencies = [ "pin-project 1.1.3", "prost 0.12.3", "rustls-native-certs 0.7.0", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -16427,7 +16442,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", "syn 1.0.109", ] @@ -16450,9 +16465,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -16572,9 +16587,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.88" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76de4f783e610194f6c98bfd53f9fc52bb2e0d02c947621e8a0f4ecc799b2880" +checksum = "ee6b2fc10a33500845468aa7c06567a9226581b24f03c6f891e6dda2dc9a1c8b" dependencies = [ "basic-toml", "glob", @@ -16940,9 +16955,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.5.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd65ff0de3304a013e6dfeb7324e1cd389a8a000e582e3c9c9fae82ced778e26" +checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" [[package]] name = "variant_count" @@ -17095,9 +17110,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", "wasm-bindgen-shared", ] @@ -17129,9 +17144,9 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -17501,9 +17516,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.33" +version = "0.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" +checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" dependencies = [ "memchr", ] @@ -17628,9 +17643,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] @@ -17648,9 +17663,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.75", "quote 1.0.35", - "syn 2.0.48", + "syn 2.0.47", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 626936b1953fb..562b163b8fcbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -725,7 +725,7 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -x25519-dalek = "2.0.0" #Set to this version to be compatible with Sovereign Labs +x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek", branch = "zeroize_v1" } #Set to this version to be compatible with Sovereign Labs zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES From 6d895d965ac97dde2e3c7353d41449cc70b69b4c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 8 Apr 2024 16:58:56 +0200 Subject: [PATCH 011/147] fix: pub api struct. --- api/src/runtime.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/api/src/runtime.rs b/api/src/runtime.rs index aacab86b4024b..1ee2c09df3218 100644 --- a/api/src/runtime.rs +++ b/api/src/runtime.rs @@ -95,6 +95,48 @@ pub fn bootstrap( // TODO: https://github.com/poem-web/poem/issues/332 // TODO: https://github.com/poem-web/poem/issues/333 +pub struct Apis { + pub accounts: AccountsApi, + pub basic: BasicApi, + pub blocks: BlocksApi, + pub events: EventsApi, + pub index: IndexApi, + pub state: StateApi, + pub transactions: TransactionsApi, + pub view_function: ViewFunctionApi, +} + +pub fn get_apis(context : Arc) -> Apis { + + Apis { + accounts: AccountsApi { + context: context.clone(), + }, + basic: BasicApi { + context: context.clone(), + }, + blocks: BlocksApi { + context: context.clone(), + }, + events: EventsApi { + context: context.clone(), + }, + index: IndexApi { + context: context.clone(), + }, + state: StateApi { + context: context.clone(), + }, + transactions: TransactionsApi { + context: context.clone(), + }, + view_function: ViewFunctionApi { + context: context.clone(), + }, + } + +} + /// Generate the top level API service pub fn get_api_service( context: Arc, From 3fc0632a1e087dcdab67998662cbc7a140cbaab2 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 8 Apr 2024 16:59:47 +0200 Subject: [PATCH 012/147] feat: pub modules. --- api/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 838394268d667..6e1637e509ab5 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -4,10 +4,10 @@ use poem_openapi::Tags; -mod accept_type; -mod accounts; -mod basic; -mod bcs_payload; +pub mod accept_type; +pub mod accounts; +pub mod basic; +pub mod bcs_payload; mod blocks; mod check_size; pub mod context; @@ -19,12 +19,12 @@ mod log; pub mod metrics; mod page; mod response; -mod runtime; +pub mod runtime; mod set_failpoints; -mod state; +pub mod state; #[cfg(test)] pub mod tests; -mod transactions; +pub mod transactions; mod view_function; /// API categories for the OpenAPI spec From 93b2626e77a5701f2469c25839c3eefa177a0357 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 12 Apr 2024 10:53:25 +0100 Subject: [PATCH 013/147] make serives pub --- Cargo.lock | 15 +++++++++++---- aptos-node/src/lib.rs | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6dada90d6c061..1012df0a52fa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17535,11 +17535,13 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "1.2.0" -source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek 3.2.0", - "rand_core 0.5.1", + "curve25519-dalek 4.1.2", + "rand_core 0.6.4", + "serde", "zeroize", ] @@ -17713,3 +17715,8 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[patch.unused]] +name = "x25519-dalek" +version = "1.2.0" +source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" diff --git a/aptos-node/src/lib.rs b/aptos-node/src/lib.rs index 22c66c652ba90..fcf393de7b273 100644 --- a/aptos-node/src/lib.rs +++ b/aptos-node/src/lib.rs @@ -9,7 +9,7 @@ mod logger; mod network; mod services; mod state_sync; -mod storage; +pub mod storage; pub mod utils; #[cfg(test)] From 316f50908ddc0fc53ced7715006215e39dae736e Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 12 Apr 2024 11:29:17 +0100 Subject: [PATCH 014/147] fix dalek --- Cargo.lock | 15 ++++----------- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1012df0a52fa8..6dada90d6c061 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17535,13 +17535,11 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +version = "1.2.0" +source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" dependencies = [ - "curve25519-dalek 4.1.2", - "rand_core 0.6.4", - "serde", + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", "zeroize", ] @@ -17715,8 +17713,3 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] - -[[patch.unused]] -name = "x25519-dalek" -version = "1.2.0" -source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" diff --git a/Cargo.toml b/Cargo.toml index 626936b1953fb..f43e645250d93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -725,7 +725,7 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -x25519-dalek = "2.0.0" #Set to this version to be compatible with Sovereign Labs +x25519-dalek = "1.2.0" #Set to this version to be compatible with Sovereign Labs zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs # MOVE DEPENDENCIES From 1b60020a1f02627eca184f35a6acdf87ba5c8f01 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 12 Apr 2024 13:46:18 +0100 Subject: [PATCH 015/147] pub mod network --- aptos-node/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aptos-node/src/lib.rs b/aptos-node/src/lib.rs index fcf393de7b273..512c97717f257 100644 --- a/aptos-node/src/lib.rs +++ b/aptos-node/src/lib.rs @@ -7,7 +7,7 @@ mod indexer; mod logger; mod network; -mod services; +pub mod services; mod state_sync; pub mod storage; pub mod utils; From 806bbf92a34e84bdbc48f58da3a2e51439107c7c Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 12 Apr 2024 14:03:10 +0100 Subject: [PATCH 016/147] pub typo --- aptos-node/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aptos-node/src/lib.rs b/aptos-node/src/lib.rs index 512c97717f257..60aeb47b9a4d3 100644 --- a/aptos-node/src/lib.rs +++ b/aptos-node/src/lib.rs @@ -6,8 +6,8 @@ mod indexer; mod logger; -mod network; -pub mod services; +pub mod network; +mod services; mod state_sync; pub mod storage; pub mod utils; From 37c6e397f6e8995fe54d8fcb5da25cb4e56829fd Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 15 Apr 2024 12:22:10 +0100 Subject: [PATCH 017/147] add error logs for deserialization --- config/src/config/identity_config.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config/src/config/identity_config.rs b/config/src/config/identity_config.rs index 9fcfd3aec0422..68688d6429b0d 100644 --- a/config/src/config/identity_config.rs +++ b/config/src/config/identity_config.rs @@ -38,7 +38,15 @@ pub struct IdentityBlob { impl IdentityBlob { pub fn from_file(path: &Path) -> anyhow::Result { - Ok(serde_yaml::from_str(&fs::read_to_string(path)?)?) + let content = fs::read_to_string(path)?; + match serde_yaml::from_str(&content) { + Ok(identity_blob) => Ok(identity_blob), + Err(e) => { + eprintln!("Deserialization error: {:?}", e); + eprintln!("YAML content:\n{}", content); + Err(e.into()) + }, + } } pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { From dfe3407d6b51617aa0cf901133263ebd1d56af15 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 16 Apr 2024 09:54:05 +0100 Subject: [PATCH 018/147] pub mod core_mempool --- mempool/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mempool/src/lib.rs b/mempool/src/lib.rs index fc35a4c056d8d..15a5a6d12c849 100644 --- a/mempool/src/lib.rs +++ b/mempool/src/lib.rs @@ -69,7 +69,7 @@ pub use shared_mempool::{ #[cfg(any(test, feature = "fuzzing"))] pub use tests::{fuzzing, mocks}; -mod core_mempool; +pub mod core_mempool; pub mod counters; mod logging; mod shared_mempool; From 595b0fd364d756e91b48bce24be56ac3db731a15 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 16 Apr 2024 17:56:36 +0300 Subject: [PATCH 019/147] Don't version control VSCode settings Let developers configure their VSCode without turning this into commit fests and battles of preferences. --- .vscode/settings.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 312e964feab2d..0000000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "rust-analyzer.rustfmt.extraArgs": [ - "+nightly" - ], - "rust-analyzer.showUnlinkedFileNotification": false -} From 6b328ea69575696d3b7da1b25512b722b9a7302e Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 17 Apr 2024 19:32:43 +0100 Subject: [PATCH 020/147] initial --- .../aptosdb/src/db/include/aptosdb_writer.rs | 81 ++++++++++++++++++- .../ledger_db/transaction_accumulator_db.rs | 20 +++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 36b0c06a0fc66..4a5189ece0744 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -498,9 +498,8 @@ impl AptosDB { &batch, )?; - - Ok(()) - })?; + Ok(()) + })?; let _timer = OTHER_TIMERS_SECONDS .with_label_values(&["commit_transaction_auxiliary_data___commit"]) @@ -631,4 +630,80 @@ impl AptosDB { Ok(()) } + + pub fn revert_last_commit( + &self, + last_version: Version, + new_root_hash: HashValue, + ledger_info_with_sigs: Option<&LedgerInfoWithSignatures>, + ) -> Result<()> { + let _timer = OTHER_TIMERS_SECONDS + .with_label_values(&["revert_last_commit"]) + .start_timer(); + + // Revert the ledger commit progress + let mut ledger_batch = SchemaBatch::new(); + ledger_batch.put::( + &DbMetadataKey::LedgerCommitProgress, + &DbMetadataValue::Version(last_version - 1), + )?; + self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + + // Revert the overall commit progress + let mut ledger_batch = SchemaBatch::new(); + ledger_batch.put::( + &DbMetadataKey::OverallCommitProgress, + &DbMetadataValue::Version(last_version - 1), + )?; + self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + + // Revert the transaction accumulator + let mut batch = SchemaBatch::new(); + self.ledger_db + .transaction_accumulator_db() + .revert_transaction_accumulator(last_version, &batch)?; + self.ledger_db + .transaction_accumulator_db() + .write_schemas(batch)?; + + // Revert the transaction info + let mut batch = SchemaBatch::new(); + TransactionInfoDb::delete_transaction_info(last_version, &batch)?; + self.ledger_db.transaction_info_db().write_schemas(batch)?; + + // Revert the events + let mut batch = SchemaBatch::new(); + self.ledger_db + .event_db() + .delete_events(last_version, &batch)?; + self.ledger_db.event_db().write_schemas(batch)?; + + // Revert the transaction auxiliary data + let mut batch = SchemaBatch::new(); + TransactionAuxiliaryDataDb::delete_transaction_auxiliary_data(last_version, &batch)?; + self.ledger_db + .transaction_auxiliary_data_db() + .write_schemas(batch)?; + + // Revert the write set + self.ledger_db + .write_set_db() + .revert_write_sets(last_version)?; + + // Revert the transactions + self.ledger_db + .transaction_db() + .revert_transactions(last_version)?; + + // Revert the state kv and ledger metadata + self.state_store + .revert_state_kv_and_ledger_metadata(last_version)?; + + // Update the latest ledger info if provided + if let Some(x) = ledger_info_with_sigs { + self.commit_ledger_info(last_version - 1, new_root_hash, Some(x))?; + } + + Ok(()) + } } diff --git a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs index 2fa1367670d5e..1b0d5da92fe28 100644 --- a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs +++ b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs @@ -53,6 +53,26 @@ impl TransactionAccumulatorDb { pub(crate) fn write_schemas(&self, batch: SchemaBatch) -> Result<()> { self.db.write_schemas(batch) } + + pub(crate) fn revert_transaction_accumulator( + &self, + version: Version, + batch: SchemaBatch, + ) -> Result<()> { + // Delete the transaction accumulator entry for the given version + batch.delete::(&version)?; + + // Update the transaction accumulator pruner progress to the previous version + let prev_version = version + .checked_sub(1) + .ok_or_else(|| anyhow::anyhow!("Cannot revert transaction accumulator at version 0"))?; + batch.put::( + &DbMetadataKey::TransactionAccumulatorPrunerProgress, + &DbMetadataValue::Version(prev_version), + )?; + + Ok(()) + } } impl TransactionAccumulatorDb { From 860baf6a0d931ee7c0aa01428be9872f3b311517 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 18 Apr 2024 16:54:46 +0100 Subject: [PATCH 021/147] add prunes --- .../aptosdb/src/db/include/aptosdb_writer.rs | 28 +++++++++++-------- storage/aptosdb/src/event_store/mod.rs | 18 ++++++++++++ storage/aptosdb/src/ledger_db/event_db.rs | 23 +++++++++++++++ storage/aptosdb/src/ledger_db/mod.rs | 4 +-- .../ledger_db/transaction_accumulator_db.rs | 5 ++-- .../src/ledger_db/transaction_info_db.rs | 8 ++++++ 6 files changed, 70 insertions(+), 16 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 4a5189ece0744..1485a0423e698 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,5 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +use crate::ledger_db::{transaction_db::TransactionDb, write_set_db::WriteSetDb}; +use aptos_types::proof::position::Position; impl DbWriter for AptosDB { /// `first_version` is the version of the first transaction in `txns_to_commit`. @@ -657,18 +659,22 @@ impl AptosDB { )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + let temp_position = Position::from_postorder_index(last_version)?; // Revert the transaction accumulator let mut batch = SchemaBatch::new(); self.ledger_db .transaction_accumulator_db() - .revert_transaction_accumulator(last_version, &batch)?; + .revert_transaction_accumulator(last_version, &batch, temp_position)?; self.ledger_db .transaction_accumulator_db() .write_schemas(batch)?; // Revert the transaction info let mut batch = SchemaBatch::new(); - TransactionInfoDb::delete_transaction_info(last_version, &batch)?; + self.ledger_db + .transaction_info_db() + .delete_transaction_info(last_version, &batch)?; + self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events @@ -680,21 +686,19 @@ impl AptosDB { // Revert the transaction auxiliary data let mut batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::delete_transaction_auxiliary_data(last_version, &batch)?; + TransactionAuxiliaryDataDb::prune(last_version, last_version + 1, &batch)?; + self.ledger_db .transaction_auxiliary_data_db() .write_schemas(batch)?; // Revert the write set - self.ledger_db - .write_set_db() - .revert_write_sets(last_version)?; - - // Revert the transactions - self.ledger_db - .transaction_db() - .revert_transactions(last_version)?; - + WriteSetDb::prune(last_version, last_version + 1, &batch)?; + self.ledger_db.transaction_db().prune_transactions( + last_version, + last_version + 1, + &batch, + )?; // Revert the state kv and ledger metadata self.state_store .revert_state_kv_and_ledger_metadata(last_version)?; diff --git a/storage/aptosdb/src/event_store/mod.rs b/storage/aptosdb/src/event_store/mod.rs index 430ef731076ba..5d2f5a1dc4f21 100644 --- a/storage/aptosdb/src/event_store/mod.rs +++ b/storage/aptosdb/src/event_store/mod.rs @@ -363,6 +363,24 @@ impl EventStore { } Ok(()) } + + pub(crate) fn delete_event_accumulator( + &self, + version: Version, + db_batch: &SchemaBatch, + ) -> anyhow::Result<()> { + let mut iter = self + .event_db + .iter::(Default::default())?; + iter.seek(&(version, Position::from_inorder_index(0)))?; + while let Some(((ver, position), _)) = iter.next().transpose()? { + if ver != version { + return Ok(()); + } + db_batch.delete::(&(version, position))?; + } + Ok(()) + } } struct EventHashReader<'a> { diff --git a/storage/aptosdb/src/ledger_db/event_db.rs b/storage/aptosdb/src/ledger_db/event_db.rs index dc5a478260970..ea3c160c94413 100644 --- a/storage/aptosdb/src/ledger_db/event_db.rs +++ b/storage/aptosdb/src/ledger_db/event_db.rs @@ -209,4 +209,27 @@ impl EventDb { .prune_event_accumulator(start, end, db_batch)?; Ok(()) } + + pub(crate) fn delete_events( + &self, + version: Version, + db_batch: &SchemaBatch, + ) -> anyhow::Result<()> { + for events in self.get_events_by_version_iter(version, 1)? { + for (idx, event) in (events?).into_iter().enumerate() { + if let ContractEvent::V1(v1) = event { + db_batch.delete::(&( + *v1.key(), + version, + v1.sequence_number(), + ))?; + db_batch.delete::(&(*v1.key(), v1.sequence_number()))?; + } + db_batch.delete::(&(version, idx as u64))?; + } + } + self.event_store + .delete_event_accumulator(version, db_batch)?; + Ok(()) + } } diff --git a/storage/aptosdb/src/ledger_db/mod.rs b/storage/aptosdb/src/ledger_db/mod.rs index 7cb8b2288b873..7a7118bb34d68 100644 --- a/storage/aptosdb/src/ledger_db/mod.rs +++ b/storage/aptosdb/src/ledger_db/mod.rs @@ -43,7 +43,7 @@ pub(crate) mod transaction_accumulator_db; pub(crate) mod transaction_auxiliary_data_db; #[cfg(test)] mod transaction_auxiliary_data_db_test; -mod transaction_db; +pub(crate) mod transaction_db; #[cfg(test)] pub(crate) mod transaction_db_test; pub(crate) mod transaction_info_db; @@ -51,7 +51,7 @@ pub(crate) mod transaction_info_db; mod transaction_info_db_test; pub(crate) mod write_set_db; #[cfg(test)] -mod write_set_db_test; +pub mod write_set_db_test; pub const LEDGER_DB_FOLDER_NAME: &str = "ledger_db"; pub const LEDGER_DB_NAME: &str = "ledger_db"; diff --git a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs index 1b0d5da92fe28..3f19bf9cd9f29 100644 --- a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs +++ b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs @@ -57,10 +57,11 @@ impl TransactionAccumulatorDb { pub(crate) fn revert_transaction_accumulator( &self, version: Version, - batch: SchemaBatch, + batch: &SchemaBatch, + position: Position, ) -> Result<()> { // Delete the transaction accumulator entry for the given version - batch.delete::(&version)?; + batch.delete::(&position)?; // Update the transaction accumulator pruner progress to the previous version let prev_version = version diff --git a/storage/aptosdb/src/ledger_db/transaction_info_db.rs b/storage/aptosdb/src/ledger_db/transaction_info_db.rs index 64e7be5d08379..fdb9f07b22ac6 100644 --- a/storage/aptosdb/src/ledger_db/transaction_info_db.rs +++ b/storage/aptosdb/src/ledger_db/transaction_info_db.rs @@ -100,4 +100,12 @@ impl TransactionInfoDb { } Ok(()) } + + pub(crate) fn delete_transaction_info( + &self, + version: Version, + batch: &SchemaBatch, + ) -> Result<()> { + batch.delete::(&version) + } } From 7f12fa3480fc15d6e464acac3c3b968b2ae4b2bb Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 18 Apr 2024 17:25:56 +0100 Subject: [PATCH 022/147] comment --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 1485a0423e698..d3c4590892846 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -699,7 +699,7 @@ impl AptosDB { last_version + 1, &batch, )?; - // Revert the state kv and ledger metadata + // Revert the state kv and ledger metadata not yet implemented self.state_store .revert_state_kv_and_ledger_metadata(last_version)?; From 0c5cae54d3ebe81c5319174f66456752fa4563e2 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 19 Apr 2024 16:02:44 +0100 Subject: [PATCH 023/147] add revert_kv method --- .../aptosdb/src/db/include/aptosdb_writer.rs | 1 + storage/aptosdb/src/state_kv_db.rs | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index d3c4590892846..27fe26e1d5d1b 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -701,6 +701,7 @@ impl AptosDB { )?; // Revert the state kv and ledger metadata not yet implemented self.state_store + .state_kv_db .revert_state_kv_and_ledger_metadata(last_version)?; // Update the latest ledger info if provided diff --git a/storage/aptosdb/src/state_kv_db.rs b/storage/aptosdb/src/state_kv_db.rs index 00272a40d35b6..f105bcf4cf669 100644 --- a/storage/aptosdb/src/state_kv_db.rs +++ b/storage/aptosdb/src/state_kv_db.rs @@ -209,6 +209,37 @@ impl StateKvDb { self.state_kv_db_shards[shard_id as usize].write_schemas(batch) } + pub(crate) fn revert_state_kv_and_ledger_metadata(&self, version: Version) -> Result<()> { + // Revert the state KV metadata DB to the given version + let mut metadata_batch = SchemaBatch::new(); + metadata_batch.put::( + &DbMetadataKey::StateKvCommitProgress, + &DbMetadataValue::Version(version), + )?; + metadata_batch.put::( + &DbMetadataKey::StateKvPrunerProgress, + &DbMetadataValue::Version(version), + )?; + self.state_kv_metadata_db.write_schemas(metadata_batch)?; + + // Revert each state KV DB shard to the corresponding version + for shard_id in 0..NUM_STATE_SHARDS { + let mut shard_batch = SchemaBatch::new(); + shard_batch.put::( + &DbMetadataKey::StateKvShardCommitProgress(shard_id as usize), + &DbMetadataValue::Version(version), + )?; + self.state_kv_db_shards[shard_id as usize].write_schemas(shard_batch)?; + } + + // Truncate the state KV DB shards if necessary + if let Some(overall_kv_commit_progress) = get_state_kv_commit_progress(self)? { + truncate_state_kv_db_shards(self, overall_kv_commit_progress, Some(version))?; + } + + Ok(()) + } + fn open_shard>( db_root_path: P, shard_id: u8, From 4b4d1d0bc34f234b12b45404d1f87e3349fdbe31 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 19 Apr 2024 19:27:33 +0100 Subject: [PATCH 024/147] add test --- storage/aptosdb/src/db/aptosdb_test.rs | 116 ++++++++++++++++++ .../aptosdb/src/db/include/aptosdb_writer.rs | 15 ++- 2 files changed, 125 insertions(+), 6 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 338d1a1686bb7..b96270212c362 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -35,6 +35,9 @@ use aptos_types::{ }; use proptest::prelude::*; use std::{collections::HashSet, sync::Arc}; +use aptos_executor_types::StateComputeResult; +use aptos_types::proof::position::Position; +use aptos_types::transaction::Transaction; use test_helper::{test_save_blocks_impl, test_sync_transactions_impl}; proptest! { @@ -312,6 +315,119 @@ pub fn test_state_merkle_pruning_impl( assert_eq!(expected_nodes, all_nodes); } + + #[test] + fn test_revert_last_commit() { + let tmp_dir = TempPath::new(); + let db = AptosDB::new_for_test(&tmp_dir); + + // Commit some transactions + let txns_to_commit = vec![ + vec![ + TransactionToCommit::new( + Transaction::UserTransaction(signed_transaction_v2()), + TransactionToCommitMetadata::new( + StateComputeResult::new( + StateChangeSet::new( + HashMap::new(), + HashMap::new(), + Version::new(1), + ), + Vec::new(), + 0, + TransactionStatus::Keep(ExecutionStatus::Success), + Arc::new(ExecutedTrees::new_empty()), + ), + None, + ), + ), + ], + vec![ + TransactionToCommit::new( + Transaction::UserTransaction(signed_transaction_v2()), + TransactionToCommitMetadata::new( + StateComputeResult::new( + StateChangeSet::new( + HashMap::new(), + HashMap::new(), + Version::new(2), + ), + Vec::new(), + 0, + TransactionStatus::Keep(ExecutionStatus::Success), + Arc::new(ExecutedTrees::new_empty()), + ), + None, + ), + ), + ], + ]; + + let ledger_info_with_sigs = generate_ledger_info_with_sigs(1, &db, None); + db.save_transactions_for_test( + &txns_to_commit[0], + 0, + 0, + Some(&ledger_info_with_sigs), + true, + ) + .unwrap(); + + let ledger_info_with_sigs = generate_ledger_info_with_sigs(2, &db, None); + db.save_transactions_for_test( + &txns_to_commit[1], + 1, + 1, + Some(&ledger_info_with_sigs), + true, + ) + .unwrap(); + + // Revert the last commit + let last_version = 2; + let new_root_hash = db + .state_store + .get_root_hash(last_version - 1) + .unwrap() + .unwrap(); + let ledger_info_with_sigs = generate_ledger_info_with_sigs(last_version - 1, &db, None); + + db.revert_last_commit(last_version, new_root_hash, Some(&ledger_info_with_sigs)) + .unwrap(); + + // Check that the latest version is now one less + assert_eq!( + db.get_latest_ledger_info().unwrap().ledger_info().version(), + last_version - 1 + ); + + // Check that the transaction at the reverted version is no longer queryable + assert!(db + .get_transaction_by_version(last_version) + .unwrap() + .is_none()); + + // Check that the transaction info at the reverted version is no longer queryable + assert!(db + .get_transaction_info_by_version(last_version) + .unwrap() + .is_none()); + + // Check that the events at the reverted version are no longer queryable + assert!(db + .get_events_by_version(last_version) + .unwrap() + .is_empty()); + + // Check that the transaction accumulator is reverted + let position = Position::from_postorder_index(last_version).unwrap(); + assert!(db + .ledger_db + .transaction_accumulator_db() + .get_hash_by_position(&position) + .unwrap() + .is_none()); + } } proptest! { diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 27fe26e1d5d1b..47594a43dda44 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -644,7 +644,7 @@ impl AptosDB { .start_timer(); // Revert the ledger commit progress - let mut ledger_batch = SchemaBatch::new(); + let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::LedgerCommitProgress, &DbMetadataValue::Version(last_version - 1), @@ -652,7 +652,7 @@ impl AptosDB { self.ledger_db.metadata_db().write_schemas(ledger_batch)?; // Revert the overall commit progress - let mut ledger_batch = SchemaBatch::new(); + let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::OverallCommitProgress, &DbMetadataValue::Version(last_version - 1), @@ -661,7 +661,7 @@ impl AptosDB { let temp_position = Position::from_postorder_index(last_version)?; // Revert the transaction accumulator - let mut batch = SchemaBatch::new(); + let batch = SchemaBatch::new(); self.ledger_db .transaction_accumulator_db() .revert_transaction_accumulator(last_version, &batch, temp_position)?; @@ -670,29 +670,32 @@ impl AptosDB { .write_schemas(batch)?; // Revert the transaction info - let mut batch = SchemaBatch::new(); + let batch = SchemaBatch::new(); self.ledger_db .transaction_info_db() .delete_transaction_info(last_version, &batch)?; + let batch = SchemaBatch::new(); self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events - let mut batch = SchemaBatch::new(); + let batch = SchemaBatch::new(); self.ledger_db .event_db() .delete_events(last_version, &batch)?; self.ledger_db.event_db().write_schemas(batch)?; // Revert the transaction auxiliary data - let mut batch = SchemaBatch::new(); + let batch = SchemaBatch::new(); TransactionAuxiliaryDataDb::prune(last_version, last_version + 1, &batch)?; + let batch = SchemaBatch::new(); self.ledger_db .transaction_auxiliary_data_db() .write_schemas(batch)?; // Revert the write set + let batch = SchemaBatch::new(); WriteSetDb::prune(last_version, last_version + 1, &batch)?; self.ledger_db.transaction_db().prune_transactions( last_version, From b6017929aa57901d5b9411186a79431f500f9ba3 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sun, 21 Apr 2024 16:04:40 +0100 Subject: [PATCH 025/147] update test --- storage/aptosdb/src/db/aptosdb_test.rs | 179 ++++++++++++++----------- storage/aptosdb/src/state_store/mod.rs | 2 +- types/src/proptest_types.rs | 16 +-- 3 files changed, 108 insertions(+), 89 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index b96270212c362..bd09df42f3ff6 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -1,6 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +use proptest::std_facade::HashMap; use crate::{ db::{ get_first_seq_num_and_limit, test_helper, @@ -18,7 +19,7 @@ use aptos_config::config::{ StateMerklePrunerConfig, StorageDirPaths, BUFFERED_STATE_TARGET_ITEMS, DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD, }; -use aptos_crypto::{hash::CryptoHash, HashValue}; +use aptos_crypto::{hash::CryptoHash, HashValue, PrivateKey, Uniform}; use aptos_storage_interface::{DbReader, ExecutedTrees, Order}; use aptos_temppath::TempPath; use aptos_types::{ @@ -34,10 +35,19 @@ use aptos_types::{ vm_status::StatusCode, }; use proptest::prelude::*; -use std::{collections::HashSet, sync::Arc}; +use std::{collections::HashSet, default, sync::Arc}; +use proptest::test_runner::TestRunner; +use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519Signature}; use aptos_executor_types::StateComputeResult; +use aptos_types::chain_id::ChainId; +use aptos_types::ledger_info::generate_ledger_info_with_sig; +use aptos_types::on_chain_config::ValidatorSet; use aptos_types::proof::position::Position; -use aptos_types::transaction::Transaction; +use aptos_types::proptest_types::{AccountInfoUniverse, BlockInfoGen, LedgerInfoGen, LedgerInfoWithSignaturesGen, ValidatorSetGen}; +use aptos_types::transaction::{RawTransaction, Script, SignedTransaction, Transaction, TransactionPayload}; +use aptos_types::write_set::WriteSet; +use move_core_types::account_address::AccountAddress; +use move_core_types::vm_status::StatusType::Execution; use test_helper::{test_save_blocks_impl, test_sync_transactions_impl}; proptest! { @@ -316,6 +326,23 @@ pub fn test_state_merkle_pruning_impl( assert_eq!(expected_nodes, all_nodes); } + fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { + let private_key = Ed25519PrivateKey::generate_for_testing(); + let public_key = private_key.public_key(); + + let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); + let raw_transaction = RawTransaction::new( + AccountAddress::random(), + 0, + transaction_payload, + 0, + gas_unit_price, + 0, + ChainId::new(10), // This is the value used in aptos testing code. + ); + SignedTransaction::new(raw_transaction, public_key, Ed25519Signature::dummy_signature()) + } + #[test] fn test_revert_last_commit() { let tmp_dir = TempPath::new(); @@ -325,108 +352,100 @@ pub fn test_state_merkle_pruning_impl( let txns_to_commit = vec![ vec![ TransactionToCommit::new( - Transaction::UserTransaction(signed_transaction_v2()), - TransactionToCommitMetadata::new( - StateComputeResult::new( - StateChangeSet::new( - HashMap::new(), - HashMap::new(), - Version::new(1), - ), - Vec::new(), - 0, - TransactionStatus::Keep(ExecutionStatus::Success), - Arc::new(ExecutedTrees::new_empty()), - ), - None, + Transaction::UserTransaction(create_signed_transaction(100)), + TransactionInfo::new_placeholder( + 0, + Some(HashValue::random()), + ExecutionStatus::Success, ), + arr_macro::arr![HashMap::new(); 16], + WriteSet::default(), + Vec::new(), + false, + TransactionAuxiliaryData::default(), ), ], vec![ TransactionToCommit::new( - Transaction::UserTransaction(signed_transaction_v2()), - TransactionToCommitMetadata::new( - StateComputeResult::new( - StateChangeSet::new( - HashMap::new(), - HashMap::new(), - Version::new(2), - ), - Vec::new(), - 0, - TransactionStatus::Keep(ExecutionStatus::Success), - Arc::new(ExecutedTrees::new_empty()), - ), - None, + Transaction::UserTransaction(create_signed_transaction(200)), + TransactionInfo::new_placeholder( + 1, + Some(HashValue::random()), + ExecutionStatus::Success, ), + arr_macro::arr![HashMap::new(); 16], + WriteSet::default(), + Vec::new(), + false, + TransactionAuxiliaryData::default(), ), ], ]; - - let ledger_info_with_sigs = generate_ledger_info_with_sigs(1, &db, None); + //let ledger_info_with_sigs = generate_ledger_info_with_sigs(1, &db, None); + + let commit_info_gen_strategy = BlockInfoGen::arbitrary(); + let commit_info_gen = commit_info_gen_strategy.new_tree(&mut TestRunner::default()).unwrap().current(); + let ledger_info_gen = LedgerInfoGen { + consensus_data_hash: HashValue::random(), + commit_info_gen, + }; + + let block_size: usize = 100; + let universe_strategy = AccountInfoUniverse::arbitrary_with(2); + let mut universe = universe_strategy.new_tree(&mut TestRunner::default()).unwrap().current(); + let ledger_info= ledger_info_gen.materialize(&mut universe, block_size); + let validator_set = universe.get_validator_set(ledger_info.epoch()); + let ledger_info_sig = generate_ledger_info_with_sig(validator_set, ledger_info); + let mut in_memory_state = db.state_store.buffered_state.lock().current_state().clone(); + let last_version= 42; db.save_transactions_for_test( &txns_to_commit[0], 0, - 0, - Some(&ledger_info_with_sigs), - true, - ) - .unwrap(); - - let ledger_info_with_sigs = generate_ledger_info_with_sigs(2, &db, None); - db.save_transactions_for_test( - &txns_to_commit[1], - 1, - 1, - Some(&ledger_info_with_sigs), - true, - ) - .unwrap(); - - // Revert the last commit - let last_version = 2; - let new_root_hash = db - .state_store - .get_root_hash(last_version - 1) - .unwrap() - .unwrap(); - let ledger_info_with_sigs = generate_ledger_info_with_sigs(last_version - 1, &db, None); + Some(0), + Some(&ledger_info_sig), + false, + in_memory_state.clone(), + ).unwrap(); - db.revert_last_commit(last_version, new_root_hash, Some(&ledger_info_with_sigs)) - .unwrap(); + db.revert_last_commit(0, Default::default(), Some(&ledger_info_sig)).unwrap(); // Check that the latest version is now one less - assert_eq!( - db.get_latest_ledger_info().unwrap().ledger_info().version(), - last_version - 1 - ); + // assert_eq!( + // db.get_latest_ledger_info().unwrap().ledger_info().version(), + // last_version - 1 + // ); + let dummy_version = 1; // Check that the transaction at the reverted version is no longer queryable assert!(db - .get_transaction_by_version(last_version) + .get_transaction_by_version(dummy_version, last_version, false) .unwrap() - .is_none()); + .events.is_none()); // Check that the transaction info at the reverted version is no longer queryable - assert!(db - .get_transaction_info_by_version(last_version) - .unwrap() - .is_none()); + + // @TODO: implement get_transaction_info_by_version on db + // assert!(db + // .get_transaction_info_by_version(last_version, false) + // .unwrap() + // .is_none()); // Check that the events at the reverted version are no longer queryable - assert!(db - .get_events_by_version(last_version) - .unwrap() - .is_empty()); + // @TODO: implement get_events_by_version on db + // assert!(db + // .get_events_by_version(last_version, false) + // .unwrap() + // .is_empty()); // Check that the transaction accumulator is reverted - let position = Position::from_postorder_index(last_version).unwrap(); - assert!(db - .ledger_db - .transaction_accumulator_db() - .get_hash_by_position(&position) - .unwrap() - .is_none()); + // @TODO implement get_hash_by_version on db + // let position = Position::from_postorder_index(last_version).unwrap(); + // assert!(db + // .ledger_db + // .transaction_accumulator_db() + // .get_hash_by_position(&position) + // .unwrap() + // .is_none()); } } diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index 5c3cd2a547c52..0d9dd1a63ee04 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -98,7 +98,7 @@ pub(crate) struct StateStore { // The `base` of buffered_state is the latest snapshot in state_merkle_db while `current` // is the latest state sparse merkle tree that is replayed from that snapshot until the latest // write set stored in ledger_db. - buffered_state: Mutex, + pub(crate) buffered_state: Mutex, buffered_state_target_items: usize, smt_ancestors: Mutex>, } diff --git a/types/src/proptest_types.rs b/types/src/proptest_types.rs index 131161d7a128e..5c12fed826ba5 100644 --- a/types/src/proptest_types.rs +++ b/types/src/proptest_types.rs @@ -999,7 +999,7 @@ impl Arbitrary for BlockMetadataExt { } #[derive(Debug)] -struct ValidatorSetGen { +pub struct ValidatorSetGen { validators: Vec, } @@ -1028,11 +1028,11 @@ impl Arbitrary for ValidatorSetGen { #[derive(Debug)] pub struct BlockInfoGen { - id: HashValue, - executed_state_id: HashValue, - timestamp_usecs: u64, - new_epoch: bool, - validator_set_gen: ValidatorSetGen, + pub id: HashValue, + pub executed_state_id: HashValue, + pub timestamp_usecs: u64, + pub new_epoch: bool, + pub validator_set_gen: ValidatorSetGen, } impl BlockInfoGen { @@ -1107,8 +1107,8 @@ impl Arbitrary for BlockInfoGen { #[derive(Arbitrary, Debug)] pub struct LedgerInfoGen { - commit_info_gen: BlockInfoGen, - consensus_data_hash: HashValue, + pub commit_info_gen: BlockInfoGen, + pub consensus_data_hash: HashValue, } impl LedgerInfoGen { From c4b62c69f05b31f5a1b6139ca043184d442b5013 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 22 Apr 2024 15:33:29 +0100 Subject: [PATCH 026/147] update tests --- storage/aptosdb/src/db/aptosdb_test.rs | 235 +++++++----------- .../aptosdb/src/db/include/aptosdb_writer.rs | 6 +- 2 files changed, 95 insertions(+), 146 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index bd09df42f3ff6..2b8124c3dd65a 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -1,13 +1,12 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use proptest::std_facade::HashMap; use crate::{ db::{ get_first_seq_num_and_limit, test_helper, test_helper::{ arb_blocks_to_commit, put_as_state_root, put_transaction_auxiliary_data, - put_transaction_infos, + put_transaction_infos, update_in_memory_state, }, AptosDB, }, @@ -19,35 +18,38 @@ use aptos_config::config::{ StateMerklePrunerConfig, StorageDirPaths, BUFFERED_STATE_TARGET_ITEMS, DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD, }; -use aptos_crypto::{hash::CryptoHash, HashValue, PrivateKey, Uniform}; +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519Signature}, + hash::CryptoHash, + HashValue, PrivateKey, Uniform, +}; +use aptos_executor_types::StateComputeResult; +use aptos_proptest_helpers::ValueGenerator; use aptos_storage_interface::{DbReader, ExecutedTrees, Order}; use aptos_temppath::TempPath; use aptos_types::{ - ledger_info::LedgerInfoWithSignatures, - proof::SparseMerkleLeafNode, + chain_id::ChainId, + ledger_info::{generate_ledger_info_with_sig, LedgerInfoWithSignatures}, + on_chain_config::ValidatorSet, + proof::{position::Position, SparseMerkleLeafNode}, + proptest_types::{ + AccountInfoUniverse, BlockInfoGen, LedgerInfoGen, LedgerInfoWithSignaturesGen, + ValidatorSetGen, + }, state_store::{ state_key::StateKey, state_storage_usage::StateStorageUsage, state_value::StateValue, }, transaction::{ - ExecutionStatus, TransactionAuxiliaryData, TransactionAuxiliaryDataV1, TransactionInfo, + ExecutionStatus, RawTransaction, Script, SignedTransaction, Transaction, + TransactionAuxiliaryData, TransactionAuxiliaryDataV1, TransactionInfo, TransactionPayload, TransactionToCommit, VMErrorDetail, Version, }, vm_status::StatusCode, + write_set::WriteSet, }; -use proptest::prelude::*; -use std::{collections::HashSet, default, sync::Arc}; -use proptest::test_runner::TestRunner; -use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519Signature}; -use aptos_executor_types::StateComputeResult; -use aptos_types::chain_id::ChainId; -use aptos_types::ledger_info::generate_ledger_info_with_sig; -use aptos_types::on_chain_config::ValidatorSet; -use aptos_types::proof::position::Position; -use aptos_types::proptest_types::{AccountInfoUniverse, BlockInfoGen, LedgerInfoGen, LedgerInfoWithSignaturesGen, ValidatorSetGen}; -use aptos_types::transaction::{RawTransaction, Script, SignedTransaction, Transaction, TransactionPayload}; -use aptos_types::write_set::WriteSet; -use move_core_types::account_address::AccountAddress; -use move_core_types::vm_status::StatusType::Execution; +use move_core_types::{account_address::AccountAddress, vm_status::StatusType::Execution}; +use proptest::{prelude::*, std_facade::HashMap, test_runner::TestRunner}; +use std::{collections::HashSet, default, ops::DerefMut, sync::Arc}; use test_helper::{test_save_blocks_impl, test_sync_transactions_impl}; proptest! { @@ -220,6 +222,77 @@ fn test_get_latest_executed_trees() { ); } +fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { + let private_key = Ed25519PrivateKey::generate_for_testing(); + let public_key = private_key.public_key(); + + let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); + let raw_transaction = RawTransaction::new( + AccountAddress::random(), + 0, + transaction_payload, + 0, + gas_unit_price, + 0, + ChainId::new(10), // This is the value used in aptos testing code. + ); + SignedTransaction::new( + raw_transaction, + public_key, + Ed25519Signature::dummy_signature(), + ) +} + +#[test] +fn test_revert_last_commit() { + aptos_logger::Logger::new().init(); + + let tmp_dir = TempPath::new(); + let db = AptosDB::new_for_test(&tmp_dir); + + let mut cur_ver: Version = 0; + let mut in_memory_state = db.buffered_state().lock().current_state().clone(); + let _ancestor = in_memory_state.base.clone(); + let mut val_generator = ValueGenerator::new(); + let blocks = val_generator.generate(arb_blocks_to_commit()); + for (txns_to_commit, ledger_info_with_sigs) in &blocks { + update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); + db.save_transactions_for_test( + txns_to_commit, + cur_ver, /* first_version */ + cur_ver.checked_sub(1), + Some(ledger_info_with_sigs), + true, /* sync_commit */ + in_memory_state.clone(), + ) + .unwrap(); + cur_ver += txns_to_commit.len() as u64; + } + println!("committed blocks"); + + // Get the latest ledger info before revert + let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); + + // Revert the last commit + let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); + let root_hash = latest_ledger_info_before_revert + .commit_info() + .executed_state_id(); + db.revert_last_commit( + last_committed_version, + root_hash, + &latest_ledger_info_before_revert, + ) + .unwrap(); + + // Check that the latest ledger info is updated correctly after the revert + let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); + assert_eq!( + latest_ledger_info_after_revert.ledger_info().version(), + last_committed_version - 1 + ); +} + pub fn test_state_merkle_pruning_impl( input: Vec<(Vec, LedgerInfoWithSignatures)>, ) { @@ -325,128 +398,6 @@ pub fn test_state_merkle_pruning_impl( assert_eq!(expected_nodes, all_nodes); } - - fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { - let private_key = Ed25519PrivateKey::generate_for_testing(); - let public_key = private_key.public_key(); - - let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); - let raw_transaction = RawTransaction::new( - AccountAddress::random(), - 0, - transaction_payload, - 0, - gas_unit_price, - 0, - ChainId::new(10), // This is the value used in aptos testing code. - ); - SignedTransaction::new(raw_transaction, public_key, Ed25519Signature::dummy_signature()) - } - - #[test] - fn test_revert_last_commit() { - let tmp_dir = TempPath::new(); - let db = AptosDB::new_for_test(&tmp_dir); - - // Commit some transactions - let txns_to_commit = vec![ - vec![ - TransactionToCommit::new( - Transaction::UserTransaction(create_signed_transaction(100)), - TransactionInfo::new_placeholder( - 0, - Some(HashValue::random()), - ExecutionStatus::Success, - ), - arr_macro::arr![HashMap::new(); 16], - WriteSet::default(), - Vec::new(), - false, - TransactionAuxiliaryData::default(), - ), - ], - vec![ - TransactionToCommit::new( - Transaction::UserTransaction(create_signed_transaction(200)), - TransactionInfo::new_placeholder( - 1, - Some(HashValue::random()), - ExecutionStatus::Success, - ), - arr_macro::arr![HashMap::new(); 16], - WriteSet::default(), - Vec::new(), - false, - TransactionAuxiliaryData::default(), - ), - ], - ]; - //let ledger_info_with_sigs = generate_ledger_info_with_sigs(1, &db, None); - - let commit_info_gen_strategy = BlockInfoGen::arbitrary(); - let commit_info_gen = commit_info_gen_strategy.new_tree(&mut TestRunner::default()).unwrap().current(); - let ledger_info_gen = LedgerInfoGen { - consensus_data_hash: HashValue::random(), - commit_info_gen, - }; - - let block_size: usize = 100; - let universe_strategy = AccountInfoUniverse::arbitrary_with(2); - let mut universe = universe_strategy.new_tree(&mut TestRunner::default()).unwrap().current(); - let ledger_info= ledger_info_gen.materialize(&mut universe, block_size); - let validator_set = universe.get_validator_set(ledger_info.epoch()); - let ledger_info_sig = generate_ledger_info_with_sig(validator_set, ledger_info); - let mut in_memory_state = db.state_store.buffered_state.lock().current_state().clone(); - let last_version= 42; - db.save_transactions_for_test( - &txns_to_commit[0], - 0, - Some(0), - Some(&ledger_info_sig), - false, - in_memory_state.clone(), - ).unwrap(); - - db.revert_last_commit(0, Default::default(), Some(&ledger_info_sig)).unwrap(); - - // Check that the latest version is now one less - // assert_eq!( - // db.get_latest_ledger_info().unwrap().ledger_info().version(), - // last_version - 1 - // ); - - let dummy_version = 1; - // Check that the transaction at the reverted version is no longer queryable - assert!(db - .get_transaction_by_version(dummy_version, last_version, false) - .unwrap() - .events.is_none()); - - // Check that the transaction info at the reverted version is no longer queryable - - // @TODO: implement get_transaction_info_by_version on db - // assert!(db - // .get_transaction_info_by_version(last_version, false) - // .unwrap() - // .is_none()); - - // Check that the events at the reverted version are no longer queryable - // @TODO: implement get_events_by_version on db - // assert!(db - // .get_events_by_version(last_version, false) - // .unwrap() - // .is_empty()); - - // Check that the transaction accumulator is reverted - // @TODO implement get_hash_by_version on db - // let position = Position::from_postorder_index(last_version).unwrap(); - // assert!(db - // .ledger_db - // .transaction_accumulator_db() - // .get_hash_by_position(&position) - // .unwrap() - // .is_none()); - } } proptest! { diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 47594a43dda44..6a2139a5f523c 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -637,7 +637,7 @@ impl AptosDB { &self, last_version: Version, new_root_hash: HashValue, - ledger_info_with_sigs: Option<&LedgerInfoWithSignatures>, + ledger_info_with_sigs: &LedgerInfoWithSignatures, ) -> Result<()> { let _timer = OTHER_TIMERS_SECONDS .with_label_values(&["revert_last_commit"]) @@ -708,9 +708,7 @@ impl AptosDB { .revert_state_kv_and_ledger_metadata(last_version)?; // Update the latest ledger info if provided - if let Some(x) = ledger_info_with_sigs { - self.commit_ledger_info(last_version - 1, new_root_hash, Some(x))?; - } + self.commit_ledger_info(last_version, new_root_hash, Some(ledger_info_with_sigs))?; Ok(()) } From b0e748e3d1eddde6ec9c038bc7a4725441b80738 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 22 Apr 2024 15:48:36 +0100 Subject: [PATCH 027/147] on reversion, version stays the same --- storage/aptosdb/src/db/aptosdb_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 2b8124c3dd65a..625db22de0155 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -289,7 +289,7 @@ fn test_revert_last_commit() { let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); assert_eq!( latest_ledger_info_after_revert.ledger_info().version(), - last_committed_version - 1 + last_committed_version ); } From b12127dbb39d244a841e70983e347685a51b4092 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 22 Apr 2024 16:01:02 +0100 Subject: [PATCH 028/147] add error assertion test --- storage/aptosdb/src/db/aptosdb_test.rs | 47 +++++++++++++++++-- .../aptosdb/src/db/include/aptosdb_writer.rs | 14 +++--- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 625db22de0155..95336cec3085a 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -268,16 +268,15 @@ fn test_revert_last_commit() { .unwrap(); cur_ver += txns_to_commit.len() as u64; } - println!("committed blocks"); // Get the latest ledger info before revert let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); - - // Revert the last commit let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); let root_hash = latest_ledger_info_before_revert .commit_info() .executed_state_id(); + + // Revert the last commit db.revert_last_commit( last_committed_version, root_hash, @@ -289,10 +288,50 @@ fn test_revert_last_commit() { let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); assert_eq!( latest_ledger_info_after_revert.ledger_info().version(), - last_committed_version + last_committed_version ); } +#[test] +fn test_revert_last_commit_should_fail_with_wrong_hash() { + aptos_logger::Logger::new().init(); + + let tmp_dir = TempPath::new(); + let db = AptosDB::new_for_test(&tmp_dir); + + let mut cur_ver: Version = 0; + let mut in_memory_state = db.buffered_state().lock().current_state().clone(); + let _ancestor = in_memory_state.base.clone(); + let mut val_generator = ValueGenerator::new(); + let blocks = val_generator.generate(arb_blocks_to_commit()); + for (txns_to_commit, ledger_info_with_sigs) in &blocks { + update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); + db.save_transactions_for_test( + txns_to_commit, + cur_ver, /* first_version */ + cur_ver.checked_sub(1), + Some(ledger_info_with_sigs), + true, /* sync_commit */ + in_memory_state.clone(), + ) + .unwrap(); + cur_ver += txns_to_commit.len() as u64; + } + + // Get the latest ledger info before revert + let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); + let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); + + // Revert the last commit + let result = db.revert_last_commit( + last_committed_version, + HashValue::random(), // A wrong hash + &latest_ledger_info_before_revert, + ); + assert!(result.is_err()); + +} + pub fn test_state_merkle_pruning_impl( input: Vec<(Vec, LedgerInfoWithSignatures)>, ) { diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 6a2139a5f523c..982b0646163eb 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -647,7 +647,8 @@ impl AptosDB { let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::LedgerCommitProgress, - &DbMetadataValue::Version(last_version - 1), + // Even though this is a revert operation, we still want to increment the version. + &DbMetadataValue::Version(last_version + 1), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -655,11 +656,12 @@ impl AptosDB { let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(last_version - 1), + &DbMetadataValue::Version(last_version + 1), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; let temp_position = Position::from_postorder_index(last_version)?; + // Revert the transaction accumulator let batch = SchemaBatch::new(); self.ledger_db @@ -687,7 +689,7 @@ impl AptosDB { // Revert the transaction auxiliary data let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(last_version, last_version + 1, &batch)?; + TransactionAuxiliaryDataDb::prune(last_version, last_version, &batch)?; let batch = SchemaBatch::new(); self.ledger_db @@ -696,10 +698,10 @@ impl AptosDB { // Revert the write set let batch = SchemaBatch::new(); - WriteSetDb::prune(last_version, last_version + 1, &batch)?; + WriteSetDb::prune(last_version - 1, last_version, &batch)?; self.ledger_db.transaction_db().prune_transactions( + last_version -1, last_version, - last_version + 1, &batch, )?; // Revert the state kv and ledger metadata not yet implemented @@ -708,7 +710,7 @@ impl AptosDB { .revert_state_kv_and_ledger_metadata(last_version)?; // Update the latest ledger info if provided - self.commit_ledger_info(last_version, new_root_hash, Some(ledger_info_with_sigs))?; + self.commit_ledger_info(last_version + 1, new_root_hash, Some(ledger_info_with_sigs))?; Ok(()) } From 3df3fb5c5dda946ac4ff9b0645860c0a469d30fa Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Apr 2024 19:20:30 +0300 Subject: [PATCH 029/147] schemadb: remove API variability Use a rocksdb type that is not dependent on the features with which the crate is compiled, since some code in this crate uses API that's specific to a particular target type of the DB alias. --- storage/schemadb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/schemadb/src/lib.rs b/storage/schemadb/src/lib.rs index 0873da08be385..43aca728740ee 100644 --- a/storage/schemadb/src/lib.rs +++ b/storage/schemadb/src/lib.rs @@ -106,7 +106,7 @@ impl SchemaBatch { #[derive(Debug)] pub struct DB { name: String, // for logging - inner: rocksdb::DB, + inner: rocksdb::DBWithThreadMode, } impl DB { From 0c52d638aabfed3449f36ed8ec4c32bed9699780 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Apr 2024 19:55:51 +0300 Subject: [PATCH 030/147] schemadb: Remove API variability (take 2) --- storage/schemadb/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/storage/schemadb/src/lib.rs b/storage/schemadb/src/lib.rs index 43aca728740ee..80cd642f01bb5 100644 --- a/storage/schemadb/src/lib.rs +++ b/storage/schemadb/src/lib.rs @@ -101,12 +101,14 @@ impl SchemaBatch { } } +type InnerDB = rocksdb::DBWithThreadMode; + /// This DB is a schematized RocksDB wrapper where all data passed in and out are typed according to /// [`Schema`]s. #[derive(Debug)] pub struct DB { name: String, // for logging - inner: rocksdb::DBWithThreadMode, + inner: InnerDB, } impl DB { @@ -138,7 +140,7 @@ impl DB { name: &str, cfds: Vec, ) -> DbResult { - let inner = rocksdb::DB::open_cf_descriptors(db_opts, path.de_unc(), cfds)?; + let inner = InnerDB::open_cf_descriptors(db_opts, path.de_unc(), cfds)?; Ok(Self::log_construct(name, inner)) } @@ -153,7 +155,7 @@ impl DB { ) -> DbResult { let error_if_log_file_exists = false; let inner = - rocksdb::DB::open_cf_for_read_only(opts, path.de_unc(), cfs, error_if_log_file_exists)?; + InnerDB::open_cf_for_read_only(opts, path.de_unc(), cfs, error_if_log_file_exists)?; Ok(Self::log_construct(name, inner)) } @@ -165,7 +167,7 @@ impl DB { name: &str, cfs: Vec, ) -> DbResult { - let inner = rocksdb::DB::open_cf_as_secondary( + let inner = InnerDB::open_cf_as_secondary( opts, primary_path.de_unc(), secondary_path.de_unc(), @@ -174,7 +176,7 @@ impl DB { Ok(Self::log_construct(name, inner)) } - fn log_construct(name: &str, inner: rocksdb::DB) -> DB { + fn log_construct(name: &str, inner: InnerDB) -> DB { info!(rocksdb_name = name, "Opened RocksDB."); DB { name: name.to_string(), From cbbf7a42233f952b81a695f08c86752ca27b5751 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Apr 2024 20:17:37 +0300 Subject: [PATCH 031/147] schemadb: fix up type of DB iterator --- storage/schemadb/src/iterator.rs | 6 ++++-- storage/schemadb/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/storage/schemadb/src/iterator.rs b/storage/schemadb/src/iterator.rs index bab01bbfb603f..365bfd2fbecbc 100644 --- a/storage/schemadb/src/iterator.rs +++ b/storage/schemadb/src/iterator.rs @@ -12,10 +12,12 @@ pub enum ScanDirection { Backward, } +type InnerDBIterator<'a> = rocksdb::DBRawIteratorWithThreadMode<'a, crate::InnerDB>; + /// DB Iterator parameterized on [`Schema`] that seeks with [`Schema::Key`] and yields /// [`Schema::Key`] and [`Schema::Value`] pub struct SchemaIterator<'a, S> { - db_iter: rocksdb::DBRawIterator<'a>, + db_iter: InnerDBIterator<'a>, direction: ScanDirection, phantom: PhantomData, } @@ -24,7 +26,7 @@ impl<'a, S> SchemaIterator<'a, S> where S: Schema, { - pub(crate) fn new(db_iter: rocksdb::DBRawIterator<'a>, direction: ScanDirection) -> Self { + pub(crate) fn new(db_iter: InnerDBIterator<'a>, direction: ScanDirection) -> Self { SchemaIterator { db_iter, direction, diff --git a/storage/schemadb/src/lib.rs b/storage/schemadb/src/lib.rs index 80cd642f01bb5..f7d0f26c298dd 100644 --- a/storage/schemadb/src/lib.rs +++ b/storage/schemadb/src/lib.rs @@ -101,7 +101,7 @@ impl SchemaBatch { } } -type InnerDB = rocksdb::DBWithThreadMode; +pub(crate) type InnerDB = rocksdb::DBWithThreadMode; /// This DB is a schematized RocksDB wrapper where all data passed in and out are typed according to /// [`Schema`]s. From 2815883c625645e123b1ecc53dc92b9056390948 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 23 Apr 2024 23:45:18 -0700 Subject: [PATCH 032/147] feat: mempool visibility. --- mempool/src/core_mempool/mempool.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mempool/src/core_mempool/mempool.rs b/mempool/src/core_mempool/mempool.rs index be096d77b01ba..8d5e7fc109961 100644 --- a/mempool/src/core_mempool/mempool.rs +++ b/mempool/src/core_mempool/mempool.rs @@ -48,12 +48,12 @@ impl Mempool { } /// This function will be called once the transaction has been stored. - pub(crate) fn commit_transaction(&mut self, sender: &AccountAddress, sequence_number: u64) { + pub fn commit_transaction(&mut self, sender: &AccountAddress, sequence_number: u64) { self.transactions .commit_transaction(sender, sequence_number); } - pub(crate) fn log_commit_transaction( + pub fn log_commit_transaction( &self, sender: &AccountAddress, sequence_number: u64, @@ -96,7 +96,7 @@ impl Mempool { } } - pub(crate) fn reject_transaction( + pub fn reject_transaction( &mut self, sender: &AccountAddress, sequence_number: u64, @@ -119,7 +119,7 @@ impl Mempool { .reject_transaction(sender, sequence_number, hash); } - pub(crate) fn log_txn_latency( + pub fn log_txn_latency( insertion_info: &InsertionInfo, bucket: &str, stage: &'static str, @@ -186,13 +186,13 @@ impl Mempool { } } - pub(crate) fn get_by_hash(&self, hash: HashValue) -> Option { + pub fn get_by_hash(&self, hash: HashValue) -> Option { self.transactions.get_by_hash(hash) } /// Used to add a transaction to the Mempool. /// Performs basic validation: checks account's sequence number. - pub(crate) fn add_txn( + pub fn add_txn( &mut self, txn: SignedTransaction, ranking_score: u64, @@ -257,7 +257,7 @@ impl Mempool { /// `exclude_transactions` - transactions that were sent to Consensus but were not committed yet /// mempool should filter out such transactions. #[allow(clippy::explicit_counter_loop)] - pub(crate) fn get_batch( + pub fn get_batch( &self, max_txns: u64, max_bytes: u64, @@ -420,18 +420,18 @@ impl Mempool { /// Periodic core mempool garbage collection. /// Removes all expired transactions and clears expired entries in metrics /// cache and sequence number cache. - pub(crate) fn gc(&mut self) { + pub fn gc(&mut self) { let now = aptos_infallible::duration_since_epoch(); self.transactions.gc_by_system_ttl(now); } /// Garbage collection based on client-specified expiration time. - pub(crate) fn gc_by_expiration_time(&mut self, block_time: Duration) { + pub fn gc_by_expiration_time(&mut self, block_time: Duration) { self.transactions.gc_by_expiration_time(block_time); } /// Returns block of transactions and new last_timeline_id. - pub(crate) fn read_timeline( + pub fn read_timeline( &self, timeline_id: &MultiBucketTimelineIndexIds, count: usize, @@ -440,7 +440,7 @@ impl Mempool { } /// Read transactions from timeline from `start_id` (exclusive) to `end_id` (inclusive). - pub(crate) fn timeline_range( + pub fn timeline_range( &self, start_end_pairs: &Vec<(u64, u64)>, ) -> Vec { From a70ac2e4ea4f9a3a710821f7a6f6dda32f9b61e1 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 17 Apr 2024 16:55:12 +0300 Subject: [PATCH 033/147] aptos-crypto: move noise support to aptos-network Apart from aptos-telemetry which we should not need, aptos-network is the only functionality that uses Noise and, therefore, currently needs the dependency on the ring crate. --- Cargo.lock | 2 + crates/aptos-crypto/Cargo.toml | 5 - crates/aptos-crypto/benches/noise.rs | 132 ----- crates/aptos-crypto/src/lib.rs | 1 - crates/aptos-crypto/src/unit_tests/mod.rs | 1 - .../aptos-crypto/src/unit_tests/noise_test.rs | 532 ------------------ network/framework/Cargo.toml | 2 + .../framework/src/noise/crypto.rs | 10 +- network/framework/src/noise/error.rs | 2 +- network/framework/src/noise/fuzzing.rs | 7 +- network/framework/src/noise/handshake.rs | 23 +- network/framework/src/noise/mod.rs | 1 + network/framework/src/noise/stream.rs | 26 +- 13 files changed, 44 insertions(+), 700 deletions(-) delete mode 100644 crates/aptos-crypto/benches/noise.rs delete mode 100644 crates/aptos-crypto/src/unit_tests/noise_test.rs rename crates/aptos-crypto/src/noise.rs => network/framework/src/noise/crypto.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 12bd2979dd936..945c6effe3192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2976,9 +2976,11 @@ dependencies = [ "rand 0.7.3", "rand 0.8.5", "rand_core 0.5.1", + "ring 0.16.20", "serde", "serde_bytes", "serde_json", + "sha2 0.9.9", "thiserror", "tokio", "tokio-retry", diff --git a/crates/aptos-crypto/Cargo.toml b/crates/aptos-crypto/Cargo.toml index 783984768a4d1..d8d11590063e6 100644 --- a/crates/aptos-crypto/Cargo.toml +++ b/crates/aptos-crypto/Cargo.toml @@ -42,7 +42,6 @@ proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } rand = { workspace = true } rand_core = { workspace = true } -ring = { workspace = true } serde = { workspace = true } serde-name = { workspace = true } serde_bytes = { workspace = true } @@ -101,10 +100,6 @@ harness = false name = "hash" harness = false -[[bench]] -name = "noise" -harness = false - [[bench]] name = "ristretto255" harness = false diff --git a/crates/aptos-crypto/benches/noise.rs b/crates/aptos-crypto/benches/noise.rs deleted file mode 100644 index e4f212aa0ef09..0000000000000 --- a/crates/aptos-crypto/benches/noise.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! Don't forget to run this benchmark with AES-NI enable. -//! You can do this by building with the following flags: -//! `RUSTFLAGS="-Ctarget-cpu=skylake -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"`. -//! - -#[macro_use] -extern crate criterion; - -use aptos_crypto::{ - noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, AES_GCM_TAGLEN}, - test_utils::TEST_SEED, - x25519, Uniform as _, ValidCryptoMaterial as _, -}; -use criterion::{Criterion, Throughput}; -use rand::SeedableRng; -use std::convert::TryFrom as _; - -const MSG_SIZE: usize = 4096; - -fn benchmarks(c: &mut Criterion) { - // bench the handshake - let mut group = c.benchmark_group("noise-handshake"); - group.throughput(Throughput::Elements(1)); - group.bench_function("connect", |b| { - // setup keys first - let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); - let initiator_static = x25519::PrivateKey::generate(&mut rng); - let initiator_static = initiator_static.to_bytes(); - let responder_static = x25519::PrivateKey::generate(&mut rng); - let responder_public = responder_static.public_key(); - let responder_static = responder_static.to_bytes(); - - let mut first_message = [0u8; handshake_init_msg_len(0)]; - let mut second_message = [0u8; handshake_resp_msg_len(0)]; - - b.iter(|| { - let initiator_static = - x25519::PrivateKey::try_from(initiator_static.clone().as_slice()).unwrap(); - let responder_static = - x25519::PrivateKey::try_from(responder_static.clone().as_slice()).unwrap(); - - let initiator = NoiseConfig::new(initiator_static); - let responder = NoiseConfig::new(responder_static); - - let initiator_state = initiator - .initiate_connection( - &mut rng, - b"prologue", - responder_public, - None, - &mut first_message, - ) - .unwrap(); - - let _ = responder - .respond_to_client_and_finalize( - &mut rng, - b"prologue", - &first_message, - None, - &mut second_message, - ) - .unwrap(); - let _ = initiator - .finalize_connection(initiator_state, &second_message) - .unwrap(); - }) - }); - group.finish(); - - let mut transport_group = c.benchmark_group("noise-transport"); - transport_group.throughput(Throughput::Bytes(MSG_SIZE as u64 * 2)); - transport_group.bench_function("AES-GCM throughput", |b| { - let mut buffer_msg = [0u8; MSG_SIZE + AES_GCM_TAGLEN]; - - // setup keys first - let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); - let initiator_static = x25519::PrivateKey::generate(&mut rng); - let responder_static = x25519::PrivateKey::generate(&mut rng); - let responder_public = responder_static.public_key(); - - // handshake first - let initiator = NoiseConfig::new(initiator_static); - let responder = NoiseConfig::new(responder_static); - - let mut first_message = [0u8; handshake_init_msg_len(0)]; - let mut second_message = [0u8; handshake_resp_msg_len(0)]; - - let initiator_state = initiator - .initiate_connection( - &mut rng, - b"prologue", - responder_public, - None, - &mut first_message, - ) - .unwrap(); - let (_, mut responder_session) = responder - .respond_to_client_and_finalize( - &mut rng, - b"prologue", - &first_message, - None, - &mut second_message, - ) - .unwrap(); - let (_, mut initiator_session) = initiator - .finalize_connection(initiator_state, &second_message) - .unwrap(); - - // bench throughput post-handshake - b.iter(move || { - let auth_tag = initiator_session - .write_message_in_place(&mut buffer_msg[..MSG_SIZE]) - .expect("session should not be closed"); - - buffer_msg[MSG_SIZE..MSG_SIZE + AES_GCM_TAGLEN].copy_from_slice(&auth_tag); - - let _plaintext = responder_session - .read_message_in_place(&mut buffer_msg[..MSG_SIZE + AES_GCM_TAGLEN]) - .expect("session should not be closed"); - }) - }); - transport_group.finish(); -} - -criterion_group!(benches, benchmarks); -criterion_main!(benches); diff --git a/crates/aptos-crypto/src/lib.rs b/crates/aptos-crypto/src/lib.rs index fc88e542bbcfd..df590bb700fdb 100644 --- a/crates/aptos-crypto/src/lib.rs +++ b/crates/aptos-crypto/src/lib.rs @@ -15,7 +15,6 @@ pub mod error; pub mod hash; pub mod hkdf; pub mod multi_ed25519; -pub mod noise; pub mod secp256k1_ecdsa; pub mod secp256r1_ecdsa; pub mod test_utils; diff --git a/crates/aptos-crypto/src/unit_tests/mod.rs b/crates/aptos-crypto/src/unit_tests/mod.rs index 34cf3348c431f..87423cb6540f1 100644 --- a/crates/aptos-crypto/src/unit_tests/mod.rs +++ b/crates/aptos-crypto/src/unit_tests/mod.rs @@ -11,6 +11,5 @@ mod ed25519_test; mod hash_test; mod hkdf_test; mod multi_ed25519_test; -mod noise_test; mod secp256k1_ecdsa_test; mod secp256r1_ecdsa_test; diff --git a/crates/aptos-crypto/src/unit_tests/noise_test.rs b/crates/aptos-crypto/src/unit_tests/noise_test.rs deleted file mode 100644 index c5245eeb54972..0000000000000 --- a/crates/aptos-crypto/src/unit_tests/noise_test.rs +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, MAX_SIZE_NOISE_MSG}, - test_utils::TEST_SEED, - x25519, Uniform as _, -}; -use rand::SeedableRng; -use serde::*; -use std::{fs::File, io::BufReader, path::PathBuf}; - -#[test] -fn simple_handshake() { - // setup peers - let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); - let initiator_private = x25519::PrivateKey::generate(&mut rng); - let initiator_public = initiator_private.public_key(); - let responder_private = x25519::PrivateKey::generate(&mut rng); - let responder_public = responder_private.public_key(); - let initiator = NoiseConfig::new(initiator_private); - let responder = NoiseConfig::new(responder_private); - - // test the two APIs - for i in 0..2 { - // initiator sends first message - let prologue = b"prologue"; - let payload1 = b"payload1"; - let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; - let initiator_state = initiator - .initiate_connection( - &mut rng, - prologue, - responder_public, - Some(payload1), - &mut first_message, - ) - .unwrap(); - - let payload2 = b"payload2"; - let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; - - // responder parses the first message and responds - let mut responder_session = if i == 0 { - let (received_payload, responder_session) = responder - .respond_to_client_and_finalize( - &mut rng, - prologue, - &first_message, - Some(payload2), - &mut second_message, - ) - .unwrap(); - let remote_static = responder_session.get_remote_static(); - assert_eq!(remote_static, initiator_public); - assert_eq!(received_payload, b"payload1"); - responder_session - } else { - let payload2 = b"payload2"; - let (remote_static, handshake_state, received_payload) = responder - .parse_client_init_message(prologue, &first_message) - .unwrap(); - assert_eq!(remote_static, initiator_public); - assert_eq!(received_payload, b"payload1"); - - responder - .respond_to_client( - &mut rng, - handshake_state, - Some(payload2), - &mut second_message, - ) - .unwrap() - }; - - // initiator parses the response - let (received_payload, mut initiator_session) = initiator - .finalize_connection(initiator_state, &second_message) - .unwrap(); - assert_eq!(received_payload, b"payload2"); - - // session usage - let mut message_sent = b"payload".to_vec(); - for i in 0..10 { - message_sent.push(i); - let mut message = message_sent.clone(); - let received_message = if i % 2 == 0 { - let auth_tag = initiator_session - .write_message_in_place(&mut message) - .expect("session should not be closed"); - message.extend_from_slice(&auth_tag); - responder_session - .read_message_in_place(&mut message) - .expect("session should not be closed") - } else { - let auth_tag = responder_session - .write_message_in_place(&mut message) - .expect("session should not be closed"); - message.extend_from_slice(&auth_tag); - initiator_session - .read_message_in_place(&mut message) - .expect("session should not be closed") - }; - assert_eq!(received_message, message_sent.as_slice()); - } - } -} - -#[test] -fn test_vectors() { - // structures needed to deserialize test vectors - #[derive(Serialize, Deserialize)] - struct TestVectors { - vectors: Vec, - } - #[derive(Serialize, Deserialize, Debug)] - struct TestVector { - protocol_name: String, - init_prologue: String, - init_static: Option, - init_ephemeral: String, - init_remote_static: Option, - resp_static: Option, - resp_ephemeral: Option, - handshake_hash: String, - messages: Vec, - } - #[derive(Serialize, Deserialize, Debug)] - struct TestMessage { - payload: String, - ciphertext: String, - } - - // EphemeralRng is used to get deterministic ephemeral keys based on test vectors - struct EphemeralRng { - ephemeral: Vec, - } - impl rand::RngCore for EphemeralRng { - fn next_u32(&mut self) -> u32 { - unreachable!() - } - - fn next_u64(&mut self) -> u64 { - unreachable!() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - dest.copy_from_slice(&self.ephemeral); - } - - fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), rand::Error> { - unreachable!() - } - } - impl rand::CryptoRng for EphemeralRng {} - - // test vectors are taken from the cacophony library - let mut test_vectors_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - test_vectors_path.push("test_vectors"); - test_vectors_path.push("noise_cacophony.txt"); - let test_vectors_path = test_vectors_path.to_str().unwrap(); - let test_vectors_file = File::open(test_vectors_path).expect("missing noise test vectors"); - let test_vectors: TestVectors = - serde_json::from_reader(BufReader::new(test_vectors_file)).unwrap(); - - // only go through Noise_IK_25519_AESGCM_SHA256 test vectors (don't exist for SHA-3) - let test_vector = test_vectors - .vectors - .iter() - .find(|vector| vector.protocol_name == "Noise_IK_25519_AESGCM_SHA256") - .expect("test vector for Noise_IK_25519_AESGCM_SHA256 should be in cacophony test vectors"); - - // initiate peers with test vector - use crate::traits::ValidCryptoMaterialStringExt; - let initiator_private = - x25519::PrivateKey::from_encoded_string(test_vector.init_static.as_ref().unwrap()).unwrap(); - let initiator_public = initiator_private.public_key(); - let responder_private = - x25519::PrivateKey::from_encoded_string(test_vector.resp_static.as_ref().unwrap()).unwrap(); - let responder_public = responder_private.public_key(); - - let initiator = NoiseConfig::new(initiator_private); - let responder = NoiseConfig::new(responder_private); - - // test the two APIs - for i in 0..2 { - // assert public keys - let init_remote_static = - hex::decode(test_vector.init_remote_static.as_ref().unwrap()).unwrap(); - assert_eq!(responder_public.as_slice(), init_remote_static.as_slice()); - - // go through handshake test messages - let prologue = hex::decode(&test_vector.init_prologue).unwrap(); - let mut messages = test_vector.messages.iter(); - - // first handshake message - let message = messages.next().unwrap(); - let payload1 = hex::decode(&message.payload).unwrap(); - let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); - let init_ephemeral = hex::decode(&test_vector.init_ephemeral).unwrap(); - let mut rng = EphemeralRng { - ephemeral: init_ephemeral, - }; - let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; - let initiator_state = initiator - .initiate_connection( - &mut rng, - &prologue, - responder_public, - Some(&payload1), - &mut first_message, - ) - .unwrap(); - assert_eq!(first_message, expected_ciphertext); - - // second handshake message - let message = messages.next().unwrap(); - let payload2 = hex::decode(&message.payload).unwrap(); - let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); - - // responder part - let resp_ephemeral = hex::decode(test_vector.resp_ephemeral.as_ref().unwrap()).unwrap(); - let mut rng = EphemeralRng { - ephemeral: resp_ephemeral, - }; - let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; - - let mut responder_session = if i == 0 { - let (received_payload, responder_session) = responder - .respond_to_client_and_finalize( - &mut rng, - &prologue, - &first_message, - Some(&payload2), - &mut second_message, - ) - .unwrap(); - assert_eq!(payload1, received_payload); - responder_session - } else { - let (remote_static, handshake_state, received_payload) = responder - .parse_client_init_message(&prologue, &first_message) - .unwrap(); - assert_eq!(remote_static, initiator_public); - assert_eq!(payload1, received_payload); - - responder - .respond_to_client( - &mut rng, - handshake_state, - Some(&payload2), - &mut second_message, - ) - .unwrap() - }; - - let remote_static = responder_session.get_remote_static(); - assert_eq!(second_message, expected_ciphertext); - assert_eq!(remote_static, initiator_public); - - // initiator part - let (received_payload, mut initiator_session) = initiator - .finalize_connection(initiator_state, &second_message) - .unwrap(); - assert_eq!(payload2, received_payload); - - // post-handshake messages - let mut client_turn = true; - for message in messages { - // decode - let payload = hex::decode(&message.payload).unwrap(); - let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); - - // initiator and responder takes turn to send messages - let mut message = payload.clone(); - if client_turn { - let auth_tag = initiator_session - .write_message_in_place(&mut message) - .expect("session should not be closed"); - message.extend_from_slice(&auth_tag); - assert_eq!(message, expected_ciphertext); - - let received_payload = responder_session - .read_message_in_place(&mut message) - .expect("session should not be closed"); - assert_eq!(payload, received_payload); - } else { - let auth_tag = responder_session - .write_message_in_place(&mut message) - .expect("session should not be closed"); - message.extend_from_slice(&auth_tag); - assert_eq!(message, expected_ciphertext); - - let received_payload = initiator_session - .read_message_in_place(&mut message) - .expect("session should not be closed"); - assert_eq!(payload, received_payload); - } - - // swap sender - client_turn = !client_turn; - } - } -} - -// Negative tests -// -------------- -// -// things that should fail during the handshake: -// - buffer to write is too small (should fail) -// - message received is too small (should fail) -// - message received is too big (should fail) -// - message received is larger than max noise size (should fail) -// - payload to write is larger than max noise size (should fail) -// -// things that should work during the handshake: -// - buffer to write is too big -// - -#[test] -fn wrong_buffer_sizes() { - // setup peers - let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); - let initiator_private = x25519::PrivateKey::generate(&mut rng); - let responder_private = x25519::PrivateKey::generate(&mut rng); - let responder_public = responder_private.public_key(); - let initiator = NoiseConfig::new(initiator_private); - let responder = NoiseConfig::new(responder_private); - - // test the two APIs - for i in 0..2 { - // initiator sends first message with buffer too small (should fail) - let payload = b"payload"; - let mut first_message_bad = vec![0u8; handshake_init_msg_len(payload.len()) - 1]; - let res = initiator.initiate_connection( - &mut rng, - b"", - responder_public, - Some(payload), - &mut first_message_bad, - ); - - assert!(res.is_err()); - - // try again with payload too large (should fail) - let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; - let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_init_msg_len(0) + 1]; - let res = initiator.initiate_connection( - &mut rng, - b"", - responder_public, - Some(&payload_too_large), - &mut large_buffer, - ); - - assert!(res.is_err()); - - // try again with buffer too large (should work) - let mut first_message_good = vec![0u8; handshake_init_msg_len(payload.len()) + 1]; - let initiator_state = initiator - .initiate_connection( - &mut rng, - b"", - responder_public, - Some(payload), - &mut first_message_good, - ) - .unwrap(); - - // responder parses the first message and responds - let mut second_message_small = vec![0u8; handshake_resp_msg_len(payload.len()) - 1]; - let mut second_message_large = vec![0u8; handshake_resp_msg_len(payload.len()) + 1]; - - let (mut responder_session, second_message_large) = if i == 0 { - // with buffer too small (shouldn't work) - let res = responder.respond_to_client_and_finalize( - &mut rng, - b"", - &first_message_good[..first_message_good.len() - 1], - Some(payload), - &mut second_message_small, - ); - - assert!(res.is_err()); - - // with first message too large (shouldn't work) - let res = responder.respond_to_client_and_finalize( - &mut rng, - b"", - &first_message_good, - Some(payload), - &mut second_message_large, - ); - - assert!(res.is_err()); - - // with incorrect prologue (should fail) - let res = responder.respond_to_client_and_finalize( - &mut rng, - b"incorrect prologue", - &first_message_good[..first_message_good.len() - 1], - Some(payload), - &mut second_message_large, - ); - - assert!(res.is_err()); - - // with payload too large (should fail) - let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; - let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; - let res = responder.respond_to_client_and_finalize( - &mut rng, - b"", - &first_message_good[..first_message_good.len() - 1], - Some(&payload_too_large), - &mut large_buffer, - ); - - assert!(res.is_err()); - - // with correct first message and buffer too large (should work) - let (_, responder_session) = responder - .respond_to_client_and_finalize( - &mut rng, - b"", - &first_message_good[..first_message_good.len() - 1], - Some(payload), - &mut second_message_large, - ) - .unwrap(); - - (responder_session, second_message_large) - } else { - // with first message too large - let res = responder.parse_client_init_message(b"", &first_message_good); - - assert!(res.is_err()); - - // with first message too small - let res = responder.parse_client_init_message( - b"", - &first_message_good[..first_message_good.len() - 2], - ); - - assert!(res.is_err()); - - // with wrong prologue - let res = responder.parse_client_init_message( - b"incorrect prologue", - &first_message_good[..first_message_good.len() - 1], - ); - - assert!(res.is_err()); - - // with first message of correct length - let (_, handshake_state, _) = responder - .parse_client_init_message(b"", &first_message_good[..first_message_good.len() - 1]) - .unwrap(); - - // write to buffer to small (should fail) - let res = responder.respond_to_client( - &mut rng, - handshake_state.clone(), - Some(payload), - &mut second_message_small, - ); - - assert!(res.is_err()); - - // with payload too large (should fail) - let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; - let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; - let res = responder.respond_to_client( - &mut rng, - handshake_state.clone(), - Some(&payload_too_large), - &mut large_buffer, - ); - - assert!(res.is_err()); - - // write to buffer too big (should work) - let responder_session = responder - .respond_to_client( - &mut rng, - handshake_state, - Some(payload), - &mut second_message_large, - ) - .unwrap(); - - (responder_session, second_message_large) - }; - - // initiator parses the response too large (should fail) - let res = initiator.finalize_connection(initiator_state.clone(), &second_message_large); - - assert!(res.is_err()); - - // initiator parses the response too small (should fail) - let res = initiator.finalize_connection( - initiator_state.clone(), - &second_message_large[..second_message_large.len() - 2], - ); - - assert!(res.is_err()); - - // initiator parses response of correct size - let (_, mut initiator_session) = initiator - .finalize_connection( - initiator_state.clone(), - &second_message_large[..second_message_large.len() - 1], - ) - .unwrap(); - - // session usage - let mut message = b"".to_vec(); - - let auth_tag = initiator_session - .write_message_in_place(&mut message) - .expect("should work"); - - // message too short to have auth tag - let res = responder_session.read_message_in_place(&mut message); - assert!(res.is_err()); - - // session should be unusable now - message.extend_from_slice(&auth_tag); - let res = responder_session.read_message_in_place(&mut message); - assert!(res.is_err()); - } -} diff --git a/network/framework/Cargo.toml b/network/framework/Cargo.toml index e2baf89a9513c..3074ef2f8ccb1 100644 --- a/network/framework/Cargo.toml +++ b/network/framework/Cargo.toml @@ -51,6 +51,8 @@ rand = { workspace = true, features = ["small_rng"] } # Note: we cannot rely on the workspace version of rand. So we use this workaround. See: # https://github.com/aptos-labs/aptos-core/blob/main/state-sync/aptos-data-client/Cargo.toml#L41. rand_latest = { package = "rand", version = "0.8.5" } +ring = { workspace = true } +sha2 = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } serde_json = { workspace = true } diff --git a/crates/aptos-crypto/src/noise.rs b/network/framework/src/noise/crypto.rs similarity index 98% rename from crates/aptos-crypto/src/noise.rs rename to network/framework/src/noise/crypto.rs index 13a08e493e7b3..aeafef6597401 100644 --- a/crates/aptos-crypto/src/noise.rs +++ b/network/framework/src/noise/crypto.rs @@ -19,10 +19,11 @@ //! Usage example: //! //! ``` -//! use aptos_crypto::{noise, x25519, traits::*}; +//! use aptos_network::noise; +//! use aptos_crypto::{x25519, traits::*}; //! use rand::prelude::*; //! -//! # fn main() -> Result<(), aptos_crypto::noise::NoiseError> { +//! # fn main() -> Result<(), noise::NoiseError> { //! let mut rng = rand::thread_rng(); //! let initiator_static = x25519::PrivateKey::generate(&mut rng); //! let responder_static = x25519::PrivateKey::generate(&mut rng); @@ -63,7 +64,9 @@ //! #![allow(clippy::arithmetic_side_effects)] -use crate::{hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519, ValidCryptoMaterial}; +use aptos_crypto::{ + hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519, ValidCryptoMaterial, +}; use ring::aead::{self, Aad, LessSafeKey, UnboundKey}; use sha2::Digest; use std::{ @@ -555,6 +558,7 @@ impl NoiseConfig { /// This function is a one-call that replaces calling the two functions parse_client_init_message /// and respond_to_client consecutively + #[allow(dead_code)] pub fn respond_to_client_and_finalize( &self, rng: &mut (impl rand::RngCore + rand::CryptoRng), diff --git a/network/framework/src/noise/error.rs b/network/framework/src/noise/error.rs index 67f2d657a6397..801b0ae3709e3 100644 --- a/network/framework/src/noise/error.rs +++ b/network/framework/src/noise/error.rs @@ -2,8 +2,8 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 +pub use super::crypto::NoiseError; use crate::application; -use aptos_crypto::noise::NoiseError; use aptos_short_hex_str::ShortHexStr; use aptos_types::PeerId; use std::io; diff --git a/network/framework/src/noise/fuzzing.rs b/network/framework/src/noise/fuzzing.rs index fed9ee70d4f9e..c574d45624295 100644 --- a/network/framework/src/noise/fuzzing.rs +++ b/network/framework/src/noise/fuzzing.rs @@ -10,11 +10,14 @@ // use crate::{ - noise::{stream::NoiseStream, AntiReplayTimestamps, HandshakeAuthMode, NoiseUpgrader}, + noise::{ + crypto::NoiseSession, stream::NoiseStream, AntiReplayTimestamps, HandshakeAuthMode, + NoiseUpgrader, + }, testutils::fake_socket::{ReadOnlyTestSocket, ReadWriteTestSocket}, }; use aptos_config::network_id::NetworkContext; -use aptos_crypto::{noise::NoiseSession, test_utils::TEST_SEED, x25519, Uniform as _}; +use aptos_crypto::{test_utils::TEST_SEED, x25519, Uniform as _}; use futures::{executor::block_on, future::join}; use futures_util::io::AsyncReadExt; use once_cell::sync::Lazy; diff --git a/network/framework/src/noise/handshake.rs b/network/framework/src/noise/handshake.rs index 1e7c2c51d358a..f8bc7c83d34ad 100644 --- a/network/framework/src/noise/handshake.rs +++ b/network/framework/src/noise/handshake.rs @@ -14,13 +14,13 @@ use crate::{ application::storage::PeersAndMetadata, logging::NetworkSchema, - noise::{error::NoiseHandshakeError, stream::NoiseStream}, + noise::{crypto, error::NoiseHandshakeError, stream::NoiseStream}, }; use aptos_config::{ config::{Peer, PeerRole}, network_id::{NetworkContext, NetworkId}, }; -use aptos_crypto::{noise, x25519}; +use aptos_crypto::x25519; use aptos_infallible::{duration_since_epoch, RwLock}; use aptos_logger::{error, trace}; use aptos_short_hex_str::{AsShortHexStr, ShortHexStr}; @@ -147,7 +147,7 @@ pub struct NoiseUpgrader { /// The validator's network context pub network_context: NetworkContext, /// Config for executing Noise handshakes. Includes our static private key. - noise_config: noise::NoiseConfig, + noise_config: crypto::NoiseConfig, /// Handshake authentication can be either mutual or server-only authentication. auth_mode: HandshakeAuthMode, } @@ -155,11 +155,11 @@ pub struct NoiseUpgrader { impl NoiseUpgrader { /// The client message consist of the prologue + a noise message with a timestamp as payload. const CLIENT_MESSAGE_SIZE: usize = - Self::PROLOGUE_SIZE + noise::handshake_init_msg_len(AntiReplayTimestamps::TIMESTAMP_SIZE); + Self::PROLOGUE_SIZE + crypto::handshake_init_msg_len(AntiReplayTimestamps::TIMESTAMP_SIZE); /// The prologue is the client's peer_id and the remote's expected public key. const PROLOGUE_SIZE: usize = PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; /// The server's message contains no payload. - const SERVER_MESSAGE_SIZE: usize = noise::handshake_resp_msg_len(0); + const SERVER_MESSAGE_SIZE: usize = crypto::handshake_resp_msg_len(0); /// Create a new NoiseConfig with the provided keypair and authentication mode. pub fn new( @@ -169,7 +169,7 @@ impl NoiseUpgrader { ) -> Self { Self { network_context, - noise_config: noise::NoiseConfig::new(key), + noise_config: crypto::NoiseConfig::new(key), auth_mode, } } @@ -743,7 +743,7 @@ mod test { let ((mut client, _), (server, server_public_key)) = build_peers(true, None); // swap in a different keypair, so the connection will be unauthenticated - client.noise_config = noise::NoiseConfig::new(client_private_key); + client.noise_config = crypto::NoiseConfig::new(client_private_key); let (client_res, server_res) = perform_handshake(&client, &server, server_public_key); client_res.unwrap_err(); @@ -1019,10 +1019,11 @@ mod test { PeerRole::ValidatorFullNode, ), ); - insert_new_trusted_peers(&peers_and_metadata, NetworkId::Public, vec![ - client_peer, - server_peer, - ]); + insert_new_trusted_peers( + &peers_and_metadata, + NetworkId::Public, + vec![client_peer, server_peer], + ); // Create an in-memory socket for testing let (dialer_socket, listener_socket) = MemorySocket::new_pair(); diff --git a/network/framework/src/noise/mod.rs b/network/framework/src/noise/mod.rs index fd3617078f53d..d72dfdfaacc3d 100644 --- a/network/framework/src/noise/mod.rs +++ b/network/framework/src/noise/mod.rs @@ -102,6 +102,7 @@ //! [ik]: https://noiseexplorer.com/patterns/IK //! [crypto]: ../aptos_crypto/noise/index.html +mod crypto; pub mod error; pub mod handshake; pub mod stream; diff --git a/network/framework/src/noise/stream.rs b/network/framework/src/noise/stream.rs index 5d61a340605ec..b81f45de626c0 100644 --- a/network/framework/src/noise/stream.rs +++ b/network/framework/src/noise/stream.rs @@ -9,7 +9,8 @@ //! //! [handshake]: crate::noise::handshake -use aptos_crypto::{noise, x25519}; +use crate::noise::crypto; +use aptos_crypto::x25519; use aptos_logger::prelude::*; use futures::{ io::{AsyncRead, AsyncWrite}, @@ -36,7 +37,7 @@ pub struct NoiseStream { /// the socket we write to and read from socket: TSocket, /// the noise session used to encrypt and decrypt messages - session: noise::NoiseSession, + session: crypto::NoiseSession, /// handy buffers to write/read buffers: Box, /// an enum used for progressively reading a noise payload @@ -47,7 +48,7 @@ pub struct NoiseStream { impl NoiseStream { /// Create a NoiseStream from a socket and a noise post-handshake session - pub fn new(socket: TSocket, session: noise::NoiseSession) -> Self { + pub fn new(socket: TSocket, session: crypto::NoiseSession) -> Self { Self { socket, session, @@ -82,7 +83,7 @@ enum ReadState { /// End of file reached, result indicated if EOF was expected or not Eof(Result<(), ()>), /// Decryption Error - DecryptionError(noise::NoiseError), + DecryptionError(crypto::NoiseError), } impl NoiseStream @@ -219,7 +220,7 @@ enum WriteState { /// End of file reached Eof, /// Encryption Error - EncryptionError(noise::NoiseError), + EncryptionError(crypto::NoiseError), } impl NoiseStream @@ -269,10 +270,11 @@ where { Ok(authentication_tag) => { // append the authentication tag - self.buffers.write_buffer[*offset..*offset + noise::AES_GCM_TAGLEN] + self.buffers.write_buffer + [*offset..*offset + crypto::AES_GCM_TAGLEN] .copy_from_slice(&authentication_tag); // calculate frame length - let frame_len = noise::encrypted_len(*offset); + let frame_len = crypto::encrypted_len(*offset); let frame_len = frame_len .try_into() .expect("offset should be able to fit in u16"); @@ -402,22 +404,22 @@ where // // encrypted messages include a tag along with the payload. -const MAX_WRITE_BUFFER_LENGTH: usize = noise::decrypted_len(noise::MAX_SIZE_NOISE_MSG); +const MAX_WRITE_BUFFER_LENGTH: usize = crypto::decrypted_len(crypto::MAX_SIZE_NOISE_MSG); /// Collection of buffers used for buffering data during the various read/write states of a /// [`NoiseStream`] struct NoiseBuffers { /// A read buffer, used for both a received ciphertext and then for its decrypted content. - read_buffer: [u8; noise::MAX_SIZE_NOISE_MSG], + read_buffer: [u8; crypto::MAX_SIZE_NOISE_MSG], /// A write buffer, used for both a plaintext to send, and then its encrypted version. - write_buffer: [u8; noise::MAX_SIZE_NOISE_MSG], + write_buffer: [u8; crypto::MAX_SIZE_NOISE_MSG], } impl NoiseBuffers { fn new() -> Self { Self { - read_buffer: [0; noise::MAX_SIZE_NOISE_MSG], - write_buffer: [0; noise::MAX_SIZE_NOISE_MSG], + read_buffer: [0; crypto::MAX_SIZE_NOISE_MSG], + write_buffer: [0; crypto::MAX_SIZE_NOISE_MSG], } } } From 6fb87aff0465398c933965a99b90a1744235e3c6 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 17 Apr 2024 12:53:34 +0300 Subject: [PATCH 034/147] types: make ring an optional dependency Introduce the "testing" feature to gate the test_utils module for external use. This feature also gates the ring dependency. --- aptos-move/e2e-move-tests/Cargo.toml | 2 +- types/Cargo.toml | 3 ++- types/src/keyless/circuit_testcases.rs | 5 +++-- types/src/keyless/configuration.rs | 14 ++++++++------ types/src/keyless/mod.rs | 8 ++++++-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/aptos-move/e2e-move-tests/Cargo.toml b/aptos-move/e2e-move-tests/Cargo.toml index a5ad28e4e45b5..35a009368cdd8 100644 --- a/aptos-move/e2e-move-tests/Cargo.toml +++ b/aptos-move/e2e-move-tests/Cargo.toml @@ -25,7 +25,7 @@ aptos-keygen = { workspace = true } aptos-language-e2e-tests = { workspace = true } aptos-logger = { workspace = true } aptos-package-builder = { workspace = true } -aptos-types = { workspace = true } +aptos-types = { workspace = true, features = ["testing"] } aptos-vm = { workspace = true, features = ["testing"] } aptos-vm-genesis = { workspace = true } bcs = { workspace = true } diff --git a/types/Cargo.toml b/types/Cargo.toml index 4748a39d19f68..ac3a292b11917 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -46,7 +46,7 @@ proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } rand = { workspace = true } rayon = { workspace = true } -ring = { workspace = true } +ring = { workspace = true, optional = true } rsa = { workspace = true } serde = { workspace = true } serde-big-array = { workspace = true } @@ -78,4 +78,5 @@ url = { workspace = true } [features] default = [] +testing = ["ring"] fuzzing = ["proptest", "proptest-derive", "aptos-crypto/fuzzing", "move-core-types/fuzzing"] diff --git a/types/src/keyless/circuit_testcases.rs b/types/src/keyless/circuit_testcases.rs index f8aad44ba60d5..50a9dc5b4c1ce 100644 --- a/types/src/keyless/circuit_testcases.rs +++ b/types/src/keyless/circuit_testcases.rs @@ -8,7 +8,8 @@ use crate::{ keyless::{ base64url_encode_str, bn254_circom::{G1Bytes, G2Bytes}, - Claims, Configuration, Groth16Proof, IdCommitment, KeylessPublicKey, OpenIdSig, Pepper, + configuration, Claims, Configuration, Groth16Proof, IdCommitment, KeylessPublicKey, + OpenIdSig, Pepper, }, transaction::authenticator::EphemeralPublicKey, }; @@ -115,7 +116,7 @@ pub(crate) const SAMPLE_UID_KEY: &str = "sub"; pub(crate) const SAMPLE_EXP_DATE: u64 = 111_111_111_111; /// ~31,710 years -pub(crate) const SAMPLE_EXP_HORIZON_SECS: u64 = 999_999_999_999; +pub(crate) const SAMPLE_EXP_HORIZON_SECS: u64 = configuration::TESTING_EXP_HORIZON_SECS; pub(crate) static SAMPLE_PEPPER: Lazy = Lazy::new(|| Pepper::from_number(76)); diff --git a/types/src/keyless/configuration.rs b/types/src/keyless/configuration.rs index 5076667a0241c..1cba6fd379d2b 100644 --- a/types/src/keyless/configuration.rs +++ b/types/src/keyless/configuration.rs @@ -1,11 +1,7 @@ // Copyright © Aptos Foundation use crate::{ - invalid_signature, - keyless::{ - circuit_constants, circuit_testcases::SAMPLE_EXP_HORIZON_SECS, KEYLESS_ACCOUNT_MODULE_NAME, - }, - move_utils::as_move_value::AsMoveValue, + invalid_signature, keyless::circuit_constants, move_utils::as_move_value::AsMoveValue, }; use move_core_types::{ ident_str, @@ -16,6 +12,12 @@ use move_core_types::{ }; use serde::{Deserialize, Serialize}; +/// The name of the Move module for keyless accounts deployed at 0x1. +pub const KEYLESS_ACCOUNT_MODULE_NAME: &str = "keyless_account"; + +/// ~31,710 years +pub(crate) const TESTING_EXP_HORIZON_SECS: u64 = 999_999_999_999; + /// Reflection of aptos_framework::keyless_account::Configuration #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] pub struct Configuration { @@ -70,7 +72,7 @@ impl Configuration { pub fn new_for_testing() -> Configuration { let mut config = Self::new_for_devnet(); - config.max_exp_horizon_secs = SAMPLE_EXP_HORIZON_SECS + 1; // ~31,689 years + config.max_exp_horizon_secs = TESTING_EXP_HORIZON_SECS + 1; // ~31,689 years config } diff --git a/types/src/keyless/mod.rs b/types/src/keyless/mod.rs index a42c94d142a34..bfacffe1f0a3b 100644 --- a/types/src/keyless/mod.rs +++ b/types/src/keyless/mod.rs @@ -25,14 +25,18 @@ use std::{ mod bn254_circom; mod circuit_constants; -mod circuit_testcases; mod configuration; mod groth16_sig; mod groth16_vk; mod openid_sig; -pub mod test_utils; mod zkp_sig; +#[cfg(any(test, feature = "testing"))] +mod circuit_testcases; + +#[cfg(any(test, feature = "testing"))] +pub mod test_utils; + use crate::keyless::circuit_constants::devnet_prepared_vk; pub use bn254_circom::{ g1_projective_str_to_affine, g2_projective_str_to_affine, get_public_inputs_hash, G1Bytes, From f226c605f43887a5b01aeca53004b3f101daeb7f Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 24 Apr 2024 11:04:26 +0300 Subject: [PATCH 035/147] Remove aptos-telemetry Telemetry uses the noise API from aptos-crypto for its own handshake implementation, which we don't want to care about at this point. Also cut out all dependent crates. --- Cargo.lock | 3997 ++--- Cargo.toml | 28 - .../aptos-e2e-comparison-testing/Cargo.toml | 56 - .../src/data_collection.rs | 268 - .../src/data_state_view.rs | 86 - .../src/execution.rs | 521 - .../aptos-e2e-comparison-testing/src/lib.rs | 599 - .../aptos-e2e-comparison-testing/src/main.rs | 171 - .../src/online_execution.rs | 240 - .../rocks_txn_idx_db/000008.sst | Bin 2413 -> 0 bytes .../rocks_txn_idx_db/000009.log | 0 .../rocks_txn_idx_db/000013.log | 0 .../rocks_txn_idx_db/000017.log | 0 .../rocks_txn_idx_db/CURRENT | 1 - .../rocks_txn_idx_db/IDENTITY | 1 - .../rocks_txn_idx_db/LOCK | 0 .../rocks_txn_idx_db/LOG | 285 - .../rocks_txn_idx_db/LOG.old.1698910458082437 | 278 - .../rocks_txn_idx_db/LOG.old.1698956094329658 | 287 - .../rocks_txn_idx_db/LOG.old.1698956220662845 | 284 - .../rocks_txn_idx_db/MANIFEST-000018 | Bin 181 -> 0 bytes .../rocks_txn_idx_db/OPTIONS-000016 | 198 - .../rocks_txn_idx_db/OPTIONS-000020 | 198 - .../state_data/10000003_state | Bin 212345 -> 0 bytes .../state_data/10000006_state | Bin 323073 -> 0 bytes .../state_data/10000007_state | Bin 197528 -> 0 bytes .../state_data/10000010_state | Bin 217099 -> 0 bytes .../state_data/10000011_state | Bin 217417 -> 0 bytes ...t-data-mainnet-10m-15-execution-result.txt | 20 - .../version_index.txt | 5 - aptos-move/aptos-gas-calibration/Cargo.toml | 31 - aptos-move/aptos-gas-calibration/README.md | 158 - .../samples/empty/Move.toml | 16 - .../samples/empty/sources/Empty.move | 5 - .../samples_ir/bootstrap/core-loop.mvir | 4567 ----- .../samples_ir/bootstrap/core.mvir | 612 - .../samples_ir/borrow/freeze_ref.mvir | 46 - .../samples_ir/borrow/imm-borrow-loc.mvir | 37 - .../samples_ir/borrow/mut-borrow-loc.mvir | 37 - .../samples_ir/borrow/read_ref.mvir | 178 - .../samples_ir/borrow/write_ref.mvir | 49 - .../samples_ir/branch/br-false.mvir | 109 - .../samples_ir/branch/br-true.mvir | 1687 -- .../samples_ir/cast/castu128.mvir | 45 - .../samples_ir/cast/castu16.mvir | 54 - .../samples_ir/cast/castu256.mvir | 54 - .../samples_ir/cast/castu32.mvir | 56 - .../samples_ir/cast/castu64.mvir | 45 - .../samples_ir/cast/castu8.mvir | 54 - .../samples_ir/function/call.mvir | 363 - .../samples_ir/function/call_recursive.mvir | 91 - .../samples_ir/function/gen.py | 65 - .../samples_ir/generics/call_generic.mvir | 483 - .../samples_ir/generics/gen.py | 73 - .../generics/imm-borrow-field-generic.mvir | 42 - .../generics/mut-borrow-field-generic.mvir | 42 - .../samples_ir/generics/pack-generic.mvir | 138 - .../samples_ir/generics/unpack-generic.mvir | 360 - .../exists-generic.mvir | 138 - .../imm-borrow-global-generic.mvir | 53 - .../move-from-generic.mvir | 132 - .../move-to-generic.mvir | 3217 ---- .../mut-borrow-global-generic.mvir | 134 - .../samples_ir/global-operations/exists.mvir | 138 - .../global-operations/imm-borrow-global.mvir | 53 - .../global-operations/move-from.mvir | 132 - .../samples_ir/global-operations/move-to.mvir | 3217 ---- .../global-operations/mut-borrow-global.mvir | 134 - .../samples_ir/ld/ld-true.mvir | 35 - .../samples_ir/ld/ldu128.mvir | 35 - .../samples_ir/ld/ldu16.mvir | 35 - .../samples_ir/ld/ldu256.mvir | 35 - .../samples_ir/ld/ldu32.mvir | 35 - .../samples_ir/ld/ldu8.mvir | 35 - .../samples_ir/operations-bool/and.mvir | 35 - .../samples_ir/operations-bool/eq.mvir | 311 - .../samples_ir/operations-bool/ge.mvir | 35 - .../samples_ir/operations-bool/gt.mvir | 35 - .../samples_ir/operations-bool/le.mvir | 35 - .../samples_ir/operations-bool/neq.mvir | 311 - .../samples_ir/operations-bool/not.mvir | 35 - .../samples_ir/operations-bool/or.mvir | 35 - .../samples_ir/operations/bitand.mvir | 35 - .../samples_ir/operations/bitor.mvir | 35 - .../samples_ir/operations/div.mvir | 47 - .../samples_ir/operations/mod.mvir | 47 - .../samples_ir/operations/mul.mvir | 78 - .../samples_ir/operations/shl.mvir | 44 - .../samples_ir/operations/shr.mvir | 44 - .../samples_ir/operations/xor.mvir | 35 - .../samples_ir/struct/imm-borrow-field.mvir | 42 - .../samples_ir/struct/mut-borrow-field.mvir | 42 - .../samples_ir/struct/pack.mvir | 186 - .../samples_ir/struct/unpack.mvir | 260 - .../samples_ir/vector/vec-generic.mvir | 230 - .../samples_ir/vector/vec-imm-borrow.mvir | 73 - .../samples_ir/vector/vec-len.mvir | 178 - .../samples_ir/vector/vec-mut-borrow.mvir | 135 - .../samples_ir/vector/vec-pack.mvir | 168 - .../samples_ir/vector/vec-pop-back.mvir | 146 - .../samples_ir/vector/vec-push-back.mvir | 134 - .../samples_ir/vector/vec-swap.mvir | 133 - .../samples_ir/vector/vec-unpack.mvir | 141 - aptos-move/aptos-gas-calibration/src/main.rs | 86 - aptos-move/aptos-gas-calibration/src/math.rs | 271 - .../src/math_interface.rs | 86 - .../aptos-gas-calibration/src/measurements.rs | 189 - .../src/measurements_helpers.rs | 291 - aptos-move/aptos-gas-calibration/src/solve.rs | 199 - aptos-move/aptos-release-builder/Cargo.toml | 52 - .../output/0-features.move | 26 - .../output/1-consensus-config.move | 54 - .../output/2-execution-config.move | 46 - .../output/3-version.move | 14 - .../output/4-oidc-provider-ops.move | 15 - .../output/5-gas-schedule.move | 1358 -- .../release.yaml | 73 - .../aptos-release-builder/data/example.yaml | 69 - .../data/example_output/0-move-stdlib.move | 1423 -- .../data/example_output/1-aptos-stdlib.move | 5983 ------- .../example_output/2-aptos-framework.move | 14097 ---------------- .../data/example_output/3-aptos-token.move | 2316 --- .../data/example_output/4-gas-schedule.move | 757 - .../data/example_output/5-features.move | 26 - .../example_output/6-consensus-config.move | 44 - .../data/example_proposals/empty.move | 9 - .../example_proposals/empty_multi_step.move | 14 - .../data/proposals/aip_28_initialization.move | 14 - .../data/proposals/aip_32_initialization.move | 15 - .../data/proposals/aip_67_initialization.move | 14 - .../data/proposals/enable_randomness.move | 21 - .../jwk_consensus_config_migration.move | 23 - .../randomness_framework_initialization.move | 24 - .../start_jwk_consensus_for_google.move | 20 - .../aptos-release-builder/data/release.yaml | 201 - .../src/components/consensus_config.rs | 47 - .../src/components/execution_config.rs | 47 - .../src/components/feature_flags.rs | 388 - .../src/components/framework.rs | 136 - .../src/components/gas.rs | 79 - .../src/components/jwk_consensus_config.rs | 46 - .../src/components/mod.rs | 809 - .../src/components/oidc_providers.rs | 77 - .../src/components/randomness_config.rs | 88 - .../src/components/transaction_fee.rs | 61 - .../src/components/version.rs | 37 - aptos-move/aptos-release-builder/src/lib.rs | 56 - aptos-move/aptos-release-builder/src/main.rs | 285 - aptos-move/aptos-release-builder/src/utils.rs | 146 - .../aptos-release-builder/src/validate.rs | 525 - aptos-move/aptos-vm-benchmarks/Cargo.toml | 17 - aptos-move/aptos-vm-benchmarks/README.md | 59 - .../samples/add-numbers/Move.toml | 6 - .../add-numbers/sources/AddNumbers.move | 11 - .../samples/do-nothing/Move.toml | 6 - .../samples/do-nothing/sources/DoNothing.move | 27 - aptos-move/aptos-vm-benchmarks/src/helper.rs | 210 - aptos-move/aptos-vm-benchmarks/src/main.rs | 77 - aptos-node/Cargo.toml | 101 - aptos-node/src/indexer.rs | 34 - aptos-node/src/lib.rs | 776 - aptos-node/src/logger.rs | 103 - aptos-node/src/main.rs | 20 - aptos-node/src/network.rs | 531 - aptos-node/src/services.rs | 259 - aptos-node/src/state_sync.rs | 274 - aptos-node/src/storage.rs | 171 - aptos-node/src/tests.rs | 115 - aptos-node/src/utils.rs | 70 - crates/aptos-inspection-service/Cargo.toml | 41 - .../src/inspection_client.rs | 108 - crates/aptos-inspection-service/src/lib.rs | 7 - .../src/server/configuration.rs | 29 - .../src/server/index.rs | 35 - .../src/server/json_encoder.rs | 217 - .../src/server/metrics.rs | 38 - .../src/server/mod.rs | 185 - .../src/server/peer_information.rs | 300 - .../src/server/system_information.rs | 42 - .../src/server/tests.rs | 286 - .../src/server/utils.rs | 130 - crates/aptos-rosetta-cli/Cargo.toml | 26 - crates/aptos-rosetta-cli/README.md | 7 - crates/aptos-rosetta-cli/src/account.rs | 75 - crates/aptos-rosetta-cli/src/block.rs | 54 - crates/aptos-rosetta-cli/src/common.rs | 116 - crates/aptos-rosetta-cli/src/construction.rs | 348 - crates/aptos-rosetta-cli/src/main.rs | 51 - crates/aptos-rosetta-cli/src/network.rs | 79 - crates/aptos-rosetta/Cargo.toml | 42 - crates/aptos-rosetta/README.md | 106 - crates/aptos-rosetta/aptos.ros | 146 - crates/aptos-rosetta/rosetta_cli.json | 70 - crates/aptos-rosetta/src/account.rs | 279 - crates/aptos-rosetta/src/block.rs | 213 - crates/aptos-rosetta/src/client.rs | 909 - crates/aptos-rosetta/src/common.rs | 342 - crates/aptos-rosetta/src/construction.rs | 1369 -- crates/aptos-rosetta/src/error.rs | 353 - crates/aptos-rosetta/src/lib.rs | 240 - crates/aptos-rosetta/src/main.rs | 286 - crates/aptos-rosetta/src/network.rs | 172 - crates/aptos-rosetta/src/types/identifiers.rs | 558 - crates/aptos-rosetta/src/types/misc.rs | 506 - crates/aptos-rosetta/src/types/mod.rs | 14 - crates/aptos-rosetta/src/types/move_types.rs | 246 - crates/aptos-rosetta/src/types/objects.rs | 2473 --- crates/aptos-rosetta/src/types/requests.rs | 569 - crates/aptos-telemetry-service/Cargo.toml | 55 - crates/aptos-telemetry-service/src/auth.rs | 205 - .../src/clients/humio.rs | 55 - .../src/clients/mod.rs | 108 - .../aptos-telemetry-service/src/constants.rs | 19 - crates/aptos-telemetry-service/src/context.rs | 238 - .../src/custom_event.rs | 222 - crates/aptos-telemetry-service/src/errors.rs | 189 - .../aptos-telemetry-service/src/gcp_logger.rs | 66 - crates/aptos-telemetry-service/src/index.rs | 111 - .../aptos-telemetry-service/src/jwt_auth.rs | 193 - crates/aptos-telemetry-service/src/lib.rs | 346 - .../aptos-telemetry-service/src/log_ingest.rs | 138 - crates/aptos-telemetry-service/src/main.rs | 13 - crates/aptos-telemetry-service/src/metrics.rs | 173 - .../src/prometheus_push_metrics.rs | 322 - .../src/remote_config.rs | 82 - .../src/tests/auth_test.rs | 319 - .../src/tests/custom_event.rs | 59 - .../aptos-telemetry-service/src/tests/mod.rs | 6 - .../src/tests/test_context.rs | 132 - .../aptos-telemetry-service/src/types/auth.rs | 65 - .../aptos-telemetry-service/src/types/mod.rs | 121 - .../src/types/telemetry.rs | 31 - .../src/validator_cache.rs | 315 - crates/aptos-telemetry/Cargo.toml | 52 - crates/aptos-telemetry/src/cli_metrics.rs | 67 - crates/aptos-telemetry/src/constants.rs | 45 - crates/aptos-telemetry/src/core_metrics.rs | 189 - crates/aptos-telemetry/src/lib.rs | 16 - crates/aptos-telemetry/src/metrics.rs | 119 - crates/aptos-telemetry/src/network_metrics.rs | 107 - crates/aptos-telemetry/src/sender.rs | 675 - crates/aptos-telemetry/src/service.rs | 554 - .../aptos-telemetry/src/system_information.rs | 173 - .../src/telemetry_log_sender.rs | 147 - crates/aptos-telemetry/src/utils.rs | 68 - crates/aptos/CHANGELOG.md | 171 - crates/aptos/Cargo.toml | 114 - crates/aptos/README.md | 5 - crates/aptos/build.rs | 6 - crates/aptos/debug-move-example/Move.toml | 9 - .../debug-move-example/sources/DebugDemo.move | 32 - crates/aptos/e2e/README.md | 105 - crates/aptos/e2e/cases/__init__.py | 0 crates/aptos/e2e/cases/account.py | 277 - crates/aptos/e2e/cases/config.py | 32 - crates/aptos/e2e/cases/init.py | 62 - crates/aptos/e2e/cases/move.py | 291 - crates/aptos/e2e/cases/node.py | 178 - crates/aptos/e2e/cases/stake.py | 418 - crates/aptos/e2e/common.py | 55 - crates/aptos/e2e/linter.sh | 17 - crates/aptos/e2e/local_testnet.py | 115 - crates/aptos/e2e/main.py | 263 - crates/aptos/e2e/poetry.lock | 685 - crates/aptos/e2e/pyproject.toml | 19 - crates/aptos/e2e/test_helpers.py | 232 - crates/aptos/e2e/test_results.py | 49 - crates/aptos/homebrew/README.md | 228 - crates/aptos/scripts/check_dynamic_deps.sh | 46 - crates/aptos/src/account/create.rs | 41 - .../src/account/create_resource_account.rs | 91 - .../src/account/derive_resource_account.rs | 109 - crates/aptos/src/account/fund.rs | 60 - crates/aptos/src/account/key_rotation.rs | 367 - crates/aptos/src/account/list.rs | 124 - crates/aptos/src/account/mod.rs | 73 - crates/aptos/src/account/multisig_account.rs | 371 - crates/aptos/src/account/transfer.rs | 115 - crates/aptos/src/common/init.rs | 458 - crates/aptos/src/common/mod.rs | 6 - crates/aptos/src/common/types.rs | 2152 --- crates/aptos/src/common/utils.rs | 555 - crates/aptos/src/config/mod.rs | 359 - crates/aptos/src/ffi.rs | 84 - crates/aptos/src/genesis/git.rs | 264 - crates/aptos/src/genesis/keys.rs | 344 - crates/aptos/src/genesis/mod.rs | 932 - crates/aptos/src/genesis/tests.rs | 434 - crates/aptos/src/genesis/tools.rs | 100 - .../aptos/src/governance/delegation_pool.rs | 269 - crates/aptos/src/governance/mod.rs | 1227 -- crates/aptos/src/governance/utils.rs | 45 - crates/aptos/src/lib.rs | 98 - crates/aptos/src/main.rs | 41 - .../src/move_tool/aptos_debug_natives.rs | 27 - .../src/move_tool/aptos_dep_example/README.md | 24 - .../aptos_dep_example/pack1/Move.toml | 6 - .../pack1/sources/hello.move | 7 - .../aptos_dep_example/pack2/Move.toml | 3 - .../aptos_dep_example/pack2/sources/m.move | 3 - crates/aptos/src/move_tool/bytecode.rs | 283 - crates/aptos/src/move_tool/coverage.rs | 191 - crates/aptos/src/move_tool/manifest.rs | 92 - crates/aptos/src/move_tool/mod.rs | 1940 --- crates/aptos/src/move_tool/package_hooks.rs | 55 - crates/aptos/src/move_tool/show.rs | 113 - crates/aptos/src/move_tool/stored_package.rs | 257 - .../src/node/analyze/analyze_validators.rs | 540 - .../aptos/src/node/analyze/fetch_metadata.rs | 337 - crates/aptos/src/node/analyze/mod.rs | 5 - crates/aptos/src/node/local_testnet/docker.rs | 309 - crates/aptos/src/node/local_testnet/faucet.rs | 89 - .../node/local_testnet/hasura_metadata.json | 2158 --- .../src/node/local_testnet/health_checker.rs | 216 - .../src/node/local_testnet/indexer_api.rs | 374 - .../aptos/src/node/local_testnet/logging.rs | 102 - crates/aptos/src/node/local_testnet/mod.rs | 464 - crates/aptos/src/node/local_testnet/node.rs | 196 - .../aptos/src/node/local_testnet/postgres.rs | 340 - .../src/node/local_testnet/processors.rs | 189 - .../src/node/local_testnet/ready_server.rs | 131 - crates/aptos/src/node/local_testnet/traits.rs | 106 - crates/aptos/src/node/local_testnet/utils.rs | 15 - crates/aptos/src/node/mod.rs | 1504 -- crates/aptos/src/op/key.rs | 398 - crates/aptos/src/op/mod.rs | 4 - crates/aptos/src/stake/mod.rs | 668 - crates/aptos/src/test/mod.rs | 1261 -- crates/aptos/src/test/tests.rs | 148 - crates/aptos/src/update/aptos.rs | 197 - crates/aptos/src/update/helpers.rs | 21 - crates/aptos/src/update/mod.rs | 91 - crates/aptos/src/update/revela.rs | 179 - crates/aptos/src/update/tool.rs | 22 - .../indexer-grpc-integration-tests/Cargo.toml | 47 - .../indexer-grpc-integration-tests/src/lib.rs | 8 - .../src/tests/fullnode_tests.rs | 361 - .../src/tests/mod.rs | 5 - testsuite/forge-cli/Cargo.toml | 43 - testsuite/forge-cli/src/README.md | 85 - testsuite/forge-cli/src/main.rs | 2749 --- testsuite/forge-cli/src/suites/dag.rs | 231 - testsuite/forge-cli/src/suites/mod.rs | 3 - testsuite/forge/Cargo.toml | 69 - testsuite/forge/src/backend/k8s/chaos.rs | 288 - .../src/backend/k8s/chaos/cpu_stress.yaml | 16 - .../forge/src/backend/k8s/chaos/netem.yaml | 32 - .../backend/k8s/chaos/network_bandwidth.yaml | 17 - .../src/backend/k8s/chaos/network_delay.yaml | 35 - .../src/backend/k8s/chaos/network_loss.yaml | 25 - .../backend/k8s/chaos/network_partition.yaml | 22 - .../forge/src/backend/k8s/chaos_schema.rs | 63 - .../forge/src/backend/k8s/cluster_helper.rs | 1224 -- testsuite/forge/src/backend/k8s/constants.rs | 54 - testsuite/forge/src/backend/k8s/fullnode.rs | 778 - .../aptos-node-default-values.yaml | 38 - testsuite/forge/src/backend/k8s/kube_api.rs | 361 - testsuite/forge/src/backend/k8s/mod.rs | 195 - testsuite/forge/src/backend/k8s/node.rs | 286 - testsuite/forge/src/backend/k8s/prometheus.rs | 354 - .../forge/src/backend/k8s/stateful_set.rs | 398 - testsuite/forge/src/backend/k8s/swarm.rs | 889 - testsuite/forge/src/backend/local/cargo.rs | 265 - testsuite/forge/src/backend/local/mod.rs | 218 - testsuite/forge/src/backend/local/node.rs | 373 - testsuite/forge/src/backend/local/swarm.rs | 727 - testsuite/forge/src/backend/mod.rs | 9 - testsuite/forge/src/github.rs | 67 - testsuite/forge/src/interface/admin.rs | 55 - testsuite/forge/src/interface/aptos.rs | 393 - testsuite/forge/src/interface/chain_info.rs | 75 - testsuite/forge/src/interface/chaos.rs | 126 - testsuite/forge/src/interface/factory.rs | 28 - testsuite/forge/src/interface/mod.rs | 54 - testsuite/forge/src/interface/network.rs | 85 - testsuite/forge/src/interface/node.rs | 255 - .../forge/src/interface/prometheus_metrics.rs | 192 - testsuite/forge/src/interface/swarm.rs | 512 - testsuite/forge/src/interface/test.rs | 66 - testsuite/forge/src/lib.rs | 31 - testsuite/forge/src/report.rs | 72 - testsuite/forge/src/runner.rs | 838 - testsuite/forge/src/slack.rs | 40 - testsuite/forge/src/success_criteria.rs | 560 - .../forge/src/test_utils/consensus_utils.rs | 324 - testsuite/forge/src/test_utils/mod.rs | 4 - testsuite/smoke-test/Cargo.toml | 82 - .../smoke-test/src/aptos/account_creation.rs | 2 - .../smoke-test/src/aptos/error_report.rs | 100 - testsuite/smoke-test/src/aptos/gas_check.rs | 96 - .../smoke-test/src/aptos/mint_transfer.rs | 94 - testsuite/smoke-test/src/aptos/mod.rs | 9 - .../smoke-test/src/aptos/move_test_helpers.rs | 30 - .../smoke-test/src/aptos/package_publish.rs | 28 - .../package_publish_modules_v1/Move.toml | 6 - .../sources/hello.move | 5 - .../package_publish_modules_v2/Move.toml | 6 - .../sources/hello.move | 11 - .../package_publish_modules_v3/Move.toml | 6 - .../sources/hello.move | 10 - testsuite/smoke-test/src/aptos_cli/account.rs | 138 - testsuite/smoke-test/src/aptos_cli/mod.rs | 7 - testsuite/smoke-test/src/aptos_cli/move.rs | 161 - .../smoke-test/src/aptos_cli/validator.rs | 1541 -- testsuite/smoke-test/src/client.rs | 146 - .../consensus/consensus_fault_tolerance.rs | 460 - .../src/consensus/consensus_only.rs | 35 - .../src/consensus/consensusdb_recovery.rs | 81 - .../src/consensus/dag/dag_fault_tolerance.rs | 255 - testsuite/smoke-test/src/consensus/dag/mod.rs | 3 - testsuite/smoke-test/src/consensus/mod.rs | 8 - .../consensus/quorum_store_fault_tolerance.rs | 399 - testsuite/smoke-test/src/execution.rs | 44 - testsuite/smoke-test/src/full_nodes.rs | 343 - testsuite/smoke-test/src/fullnode.rs | 90 - testsuite/smoke-test/src/genesis.rs | 595 - testsuite/smoke-test/src/indexer.rs | 184 - .../smoke-test/src/inspection_service.rs | 16 - .../smoke-test/src/jwks/dummy_provider/mod.rs | 109 - .../jwks/dummy_provider/request_handler.rs | 118 - .../src/jwks/jwk_consensus_basic.rs | 151 - .../src/jwks/jwk_consensus_per_issuer.rs | 99 - .../jwk_consensus_provider_change_mind.rs | 109 - testsuite/smoke-test/src/jwks/mod.rs | 140 - testsuite/smoke-test/src/keyless.rs | 400 - testsuite/smoke-test/src/lib.rs | 67 - testsuite/smoke-test/src/network.rs | 342 - .../src/randomness/disable_feature_0.rs | 86 - .../src/randomness/disable_feature_1.rs | 93 - .../src/randomness/dkg_with_validator_down.rs | 55 - .../dkg_with_validator_join_leave.rs | 140 - .../src/randomness/e2e_basic_consumption.rs | 103 - .../src/randomness/e2e_correctness.rs | 75 - .../src/randomness/enable_feature_0.rs | 104 - .../src/randomness/enable_feature_1.rs | 104 - .../src/randomness/enable_feature_2.rs | 109 - testsuite/smoke-test/src/randomness/mod.rs | 317 - .../validator_restart_during_dkg.rs | 102 - testsuite/smoke-test/src/rest_api.rs | 585 - testsuite/smoke-test/src/rosetta.rs | 2703 --- .../smoke-test/src/smoke_test_environment.rs | 213 - testsuite/smoke-test/src/state_sync.rs | 1022 -- testsuite/smoke-test/src/storage.rs | 455 - testsuite/smoke-test/src/test_smoke_tests.rs | 53 - testsuite/smoke-test/src/test_utils.rs | 206 - testsuite/smoke-test/src/transaction.rs | 116 - testsuite/smoke-test/src/txn_broadcast.rs | 105 - testsuite/smoke-test/src/txn_emitter.rs | 215 - testsuite/smoke-test/src/upgrade.rs | 288 - .../src/upgrade_multi_step_test_metadata.txt | 6 - testsuite/smoke-test/src/utils.rs | 28 - testsuite/smoke-test/src/validator_txns.rs | 61 - testsuite/smoke-test/src/workspace_builder.rs | 92 - testsuite/testcases/Cargo.toml | 50 - testsuite/testcases/src/compatibility_test.rs | 125 - .../src/consensus_reliability_tests.rs | 302 - .../testcases/src/dag_onchain_enable_test.rs | 207 - .../src/data/four_region_link_stats.csv | 13 - .../src/data/two_region_link_stats.csv | 3 - testsuite/testcases/src/forge_setup_test.rs | 85 - testsuite/testcases/src/framework_upgrade.rs | 141 - .../src/fullnode_reboot_stress_test.rs | 53 - testsuite/testcases/src/lib.rs | 529 - .../testcases/src/load_vs_perf_benchmark.rs | 479 - testsuite/testcases/src/modifiers.rs | 308 - .../src/multi_region_network_test.rs | 413 - .../testcases/src/network_bandwidth_test.rs | 71 - testsuite/testcases/src/network_loss_test.rs | 50 - .../testcases/src/network_partition_test.rs | 59 - .../testcases/src/partial_nodes_down_test.rs | 48 - testsuite/testcases/src/performance_test.rs | 22 - .../src/public_fullnode_performance.rs | 206 - .../src/quorum_store_onchain_enable_test.rs | 115 - .../testcases/src/reconfiguration_test.rs | 165 - .../testcases/src/state_sync_performance.rs | 374 - .../src/three_region_simulation_test.rs | 108 - .../testcases/src/twin_validator_test.rs | 74 - testsuite/testcases/src/two_traffics_test.rs | 84 - .../src/validator_join_leave_test.rs | 237 - .../src/validator_reboot_stress_test.rs | 67 - .../tests/forge-local-compatibility.rs | 23 - .../tests/forge-local-performance.rs | 34 - 482 files changed, 1131 insertions(+), 134543 deletions(-) delete mode 100644 aptos-move/aptos-e2e-comparison-testing/Cargo.toml delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/data_collection.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/data_state_view.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/execution.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/lib.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/main.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/src/online_execution.rs delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000008.sst delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000009.log delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000013.log delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000017.log delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/CURRENT delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/IDENTITY delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOCK delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698910458082437 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956094329658 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956220662845 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000018 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/OPTIONS-000016 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/OPTIONS-000020 delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000003_state delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000006_state delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000007_state delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000010_state delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000011_state delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/test-data-mainnet-10m-15-execution-result.txt delete mode 100644 aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/version_index.txt delete mode 100644 aptos-move/aptos-gas-calibration/Cargo.toml delete mode 100644 aptos-move/aptos-gas-calibration/README.md delete mode 100644 aptos-move/aptos-gas-calibration/samples/empty/Move.toml delete mode 100644 aptos-move/aptos-gas-calibration/samples/empty/sources/Empty.move delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core-loop.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/borrow/freeze_ref.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/borrow/imm-borrow-loc.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/borrow/mut-borrow-loc.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/borrow/read_ref.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/borrow/write_ref.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/branch/br-false.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/branch/br-true.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu128.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu16.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu256.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu32.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu64.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/cast/castu8.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/function/call.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/function/call_recursive.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/function/gen.py delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/call_generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/gen.py delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/imm-borrow-field-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/mut-borrow-field-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/pack-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/generics/unpack-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/exists-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/imm-borrow-global-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-from-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-to-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/mut-borrow-global-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations/exists.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations/imm-borrow-global.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-from.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-to.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/global-operations/mut-borrow-global.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ld-true.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ldu128.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ldu16.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ldu256.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ldu32.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/ld/ldu8.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/and.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/eq.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/ge.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/gt.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/le.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/neq.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/not.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations-bool/or.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/bitand.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/bitor.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/div.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/mod.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/mul.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/shl.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/shr.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/operations/xor.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/struct/imm-borrow-field.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/struct/mut-borrow-field.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/struct/pack.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/struct/unpack.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-generic.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-imm-borrow.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-len.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-mut-borrow.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pack.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pop-back.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-push-back.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-swap.mvir delete mode 100644 aptos-move/aptos-gas-calibration/samples_ir/vector/vec-unpack.mvir delete mode 100644 aptos-move/aptos-gas-calibration/src/main.rs delete mode 100644 aptos-move/aptos-gas-calibration/src/math.rs delete mode 100644 aptos-move/aptos-gas-calibration/src/math_interface.rs delete mode 100644 aptos-move/aptos-gas-calibration/src/measurements.rs delete mode 100644 aptos-move/aptos-gas-calibration/src/measurements_helpers.rs delete mode 100644 aptos-move/aptos-gas-calibration/src/solve.rs delete mode 100644 aptos-move/aptos-release-builder/Cargo.toml delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/0-features.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/1-consensus-config.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/2-execution-config.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/3-version.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/4-oidc-provider-ops.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/5-gas-schedule.move delete mode 100644 aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/release.yaml delete mode 100644 aptos-move/aptos-release-builder/data/example.yaml delete mode 100644 aptos-move/aptos-release-builder/data/example_output/0-move-stdlib.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/1-aptos-stdlib.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/2-aptos-framework.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/3-aptos-token.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/4-gas-schedule.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/5-features.move delete mode 100644 aptos-move/aptos-release-builder/data/example_output/6-consensus-config.move delete mode 100644 aptos-move/aptos-release-builder/data/example_proposals/empty.move delete mode 100644 aptos-move/aptos-release-builder/data/example_proposals/empty_multi_step.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/aip_28_initialization.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/aip_32_initialization.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/aip_67_initialization.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/enable_randomness.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/jwk_consensus_config_migration.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/randomness_framework_initialization.move delete mode 100644 aptos-move/aptos-release-builder/data/proposals/start_jwk_consensus_for_google.move delete mode 100644 aptos-move/aptos-release-builder/data/release.yaml delete mode 100644 aptos-move/aptos-release-builder/src/components/consensus_config.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/execution_config.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/feature_flags.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/framework.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/gas.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/jwk_consensus_config.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/mod.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/oidc_providers.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/randomness_config.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/transaction_fee.rs delete mode 100644 aptos-move/aptos-release-builder/src/components/version.rs delete mode 100644 aptos-move/aptos-release-builder/src/lib.rs delete mode 100644 aptos-move/aptos-release-builder/src/main.rs delete mode 100644 aptos-move/aptos-release-builder/src/utils.rs delete mode 100644 aptos-move/aptos-release-builder/src/validate.rs delete mode 100644 aptos-move/aptos-vm-benchmarks/Cargo.toml delete mode 100644 aptos-move/aptos-vm-benchmarks/README.md delete mode 100644 aptos-move/aptos-vm-benchmarks/samples/add-numbers/Move.toml delete mode 100644 aptos-move/aptos-vm-benchmarks/samples/add-numbers/sources/AddNumbers.move delete mode 100644 aptos-move/aptos-vm-benchmarks/samples/do-nothing/Move.toml delete mode 100644 aptos-move/aptos-vm-benchmarks/samples/do-nothing/sources/DoNothing.move delete mode 100644 aptos-move/aptos-vm-benchmarks/src/helper.rs delete mode 100644 aptos-move/aptos-vm-benchmarks/src/main.rs delete mode 100644 aptos-node/Cargo.toml delete mode 100644 aptos-node/src/indexer.rs delete mode 100644 aptos-node/src/lib.rs delete mode 100644 aptos-node/src/logger.rs delete mode 100644 aptos-node/src/main.rs delete mode 100644 aptos-node/src/network.rs delete mode 100644 aptos-node/src/services.rs delete mode 100644 aptos-node/src/state_sync.rs delete mode 100644 aptos-node/src/storage.rs delete mode 100644 aptos-node/src/tests.rs delete mode 100644 aptos-node/src/utils.rs delete mode 100644 crates/aptos-inspection-service/Cargo.toml delete mode 100644 crates/aptos-inspection-service/src/inspection_client.rs delete mode 100644 crates/aptos-inspection-service/src/lib.rs delete mode 100644 crates/aptos-inspection-service/src/server/configuration.rs delete mode 100644 crates/aptos-inspection-service/src/server/index.rs delete mode 100644 crates/aptos-inspection-service/src/server/json_encoder.rs delete mode 100644 crates/aptos-inspection-service/src/server/metrics.rs delete mode 100644 crates/aptos-inspection-service/src/server/mod.rs delete mode 100644 crates/aptos-inspection-service/src/server/peer_information.rs delete mode 100644 crates/aptos-inspection-service/src/server/system_information.rs delete mode 100644 crates/aptos-inspection-service/src/server/tests.rs delete mode 100644 crates/aptos-inspection-service/src/server/utils.rs delete mode 100644 crates/aptos-rosetta-cli/Cargo.toml delete mode 100644 crates/aptos-rosetta-cli/README.md delete mode 100644 crates/aptos-rosetta-cli/src/account.rs delete mode 100644 crates/aptos-rosetta-cli/src/block.rs delete mode 100644 crates/aptos-rosetta-cli/src/common.rs delete mode 100644 crates/aptos-rosetta-cli/src/construction.rs delete mode 100644 crates/aptos-rosetta-cli/src/main.rs delete mode 100644 crates/aptos-rosetta-cli/src/network.rs delete mode 100644 crates/aptos-rosetta/Cargo.toml delete mode 100644 crates/aptos-rosetta/README.md delete mode 100644 crates/aptos-rosetta/aptos.ros delete mode 100644 crates/aptos-rosetta/rosetta_cli.json delete mode 100644 crates/aptos-rosetta/src/account.rs delete mode 100644 crates/aptos-rosetta/src/block.rs delete mode 100644 crates/aptos-rosetta/src/client.rs delete mode 100644 crates/aptos-rosetta/src/common.rs delete mode 100644 crates/aptos-rosetta/src/construction.rs delete mode 100644 crates/aptos-rosetta/src/error.rs delete mode 100644 crates/aptos-rosetta/src/lib.rs delete mode 100644 crates/aptos-rosetta/src/main.rs delete mode 100644 crates/aptos-rosetta/src/network.rs delete mode 100644 crates/aptos-rosetta/src/types/identifiers.rs delete mode 100644 crates/aptos-rosetta/src/types/misc.rs delete mode 100644 crates/aptos-rosetta/src/types/mod.rs delete mode 100644 crates/aptos-rosetta/src/types/move_types.rs delete mode 100644 crates/aptos-rosetta/src/types/objects.rs delete mode 100644 crates/aptos-rosetta/src/types/requests.rs delete mode 100644 crates/aptos-telemetry-service/Cargo.toml delete mode 100644 crates/aptos-telemetry-service/src/auth.rs delete mode 100644 crates/aptos-telemetry-service/src/clients/humio.rs delete mode 100644 crates/aptos-telemetry-service/src/clients/mod.rs delete mode 100644 crates/aptos-telemetry-service/src/constants.rs delete mode 100644 crates/aptos-telemetry-service/src/context.rs delete mode 100644 crates/aptos-telemetry-service/src/custom_event.rs delete mode 100644 crates/aptos-telemetry-service/src/errors.rs delete mode 100644 crates/aptos-telemetry-service/src/gcp_logger.rs delete mode 100644 crates/aptos-telemetry-service/src/index.rs delete mode 100644 crates/aptos-telemetry-service/src/jwt_auth.rs delete mode 100644 crates/aptos-telemetry-service/src/lib.rs delete mode 100644 crates/aptos-telemetry-service/src/log_ingest.rs delete mode 100644 crates/aptos-telemetry-service/src/main.rs delete mode 100644 crates/aptos-telemetry-service/src/metrics.rs delete mode 100644 crates/aptos-telemetry-service/src/prometheus_push_metrics.rs delete mode 100644 crates/aptos-telemetry-service/src/remote_config.rs delete mode 100644 crates/aptos-telemetry-service/src/tests/auth_test.rs delete mode 100644 crates/aptos-telemetry-service/src/tests/custom_event.rs delete mode 100644 crates/aptos-telemetry-service/src/tests/mod.rs delete mode 100644 crates/aptos-telemetry-service/src/tests/test_context.rs delete mode 100644 crates/aptos-telemetry-service/src/types/auth.rs delete mode 100644 crates/aptos-telemetry-service/src/types/mod.rs delete mode 100644 crates/aptos-telemetry-service/src/types/telemetry.rs delete mode 100644 crates/aptos-telemetry-service/src/validator_cache.rs delete mode 100644 crates/aptos-telemetry/Cargo.toml delete mode 100644 crates/aptos-telemetry/src/cli_metrics.rs delete mode 100644 crates/aptos-telemetry/src/constants.rs delete mode 100644 crates/aptos-telemetry/src/core_metrics.rs delete mode 100644 crates/aptos-telemetry/src/lib.rs delete mode 100644 crates/aptos-telemetry/src/metrics.rs delete mode 100644 crates/aptos-telemetry/src/network_metrics.rs delete mode 100644 crates/aptos-telemetry/src/sender.rs delete mode 100644 crates/aptos-telemetry/src/service.rs delete mode 100644 crates/aptos-telemetry/src/system_information.rs delete mode 100644 crates/aptos-telemetry/src/telemetry_log_sender.rs delete mode 100644 crates/aptos-telemetry/src/utils.rs delete mode 100644 crates/aptos/CHANGELOG.md delete mode 100644 crates/aptos/Cargo.toml delete mode 100644 crates/aptos/README.md delete mode 100644 crates/aptos/build.rs delete mode 100644 crates/aptos/debug-move-example/Move.toml delete mode 100644 crates/aptos/debug-move-example/sources/DebugDemo.move delete mode 100644 crates/aptos/e2e/README.md delete mode 100644 crates/aptos/e2e/cases/__init__.py delete mode 100644 crates/aptos/e2e/cases/account.py delete mode 100644 crates/aptos/e2e/cases/config.py delete mode 100644 crates/aptos/e2e/cases/init.py delete mode 100644 crates/aptos/e2e/cases/move.py delete mode 100644 crates/aptos/e2e/cases/node.py delete mode 100644 crates/aptos/e2e/cases/stake.py delete mode 100644 crates/aptos/e2e/common.py delete mode 100755 crates/aptos/e2e/linter.sh delete mode 100644 crates/aptos/e2e/local_testnet.py delete mode 100644 crates/aptos/e2e/main.py delete mode 100644 crates/aptos/e2e/poetry.lock delete mode 100644 crates/aptos/e2e/pyproject.toml delete mode 100644 crates/aptos/e2e/test_helpers.py delete mode 100644 crates/aptos/e2e/test_results.py delete mode 100644 crates/aptos/homebrew/README.md delete mode 100755 crates/aptos/scripts/check_dynamic_deps.sh delete mode 100644 crates/aptos/src/account/create.rs delete mode 100644 crates/aptos/src/account/create_resource_account.rs delete mode 100644 crates/aptos/src/account/derive_resource_account.rs delete mode 100644 crates/aptos/src/account/fund.rs delete mode 100644 crates/aptos/src/account/key_rotation.rs delete mode 100644 crates/aptos/src/account/list.rs delete mode 100644 crates/aptos/src/account/mod.rs delete mode 100644 crates/aptos/src/account/multisig_account.rs delete mode 100644 crates/aptos/src/account/transfer.rs delete mode 100644 crates/aptos/src/common/init.rs delete mode 100644 crates/aptos/src/common/mod.rs delete mode 100644 crates/aptos/src/common/types.rs delete mode 100644 crates/aptos/src/common/utils.rs delete mode 100644 crates/aptos/src/config/mod.rs delete mode 100644 crates/aptos/src/ffi.rs delete mode 100644 crates/aptos/src/genesis/git.rs delete mode 100644 crates/aptos/src/genesis/keys.rs delete mode 100644 crates/aptos/src/genesis/mod.rs delete mode 100644 crates/aptos/src/genesis/tests.rs delete mode 100644 crates/aptos/src/genesis/tools.rs delete mode 100644 crates/aptos/src/governance/delegation_pool.rs delete mode 100644 crates/aptos/src/governance/mod.rs delete mode 100644 crates/aptos/src/governance/utils.rs delete mode 100644 crates/aptos/src/lib.rs delete mode 100644 crates/aptos/src/main.rs delete mode 100644 crates/aptos/src/move_tool/aptos_debug_natives.rs delete mode 100644 crates/aptos/src/move_tool/aptos_dep_example/README.md delete mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml delete mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move delete mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml delete mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move delete mode 100644 crates/aptos/src/move_tool/bytecode.rs delete mode 100644 crates/aptos/src/move_tool/coverage.rs delete mode 100644 crates/aptos/src/move_tool/manifest.rs delete mode 100644 crates/aptos/src/move_tool/mod.rs delete mode 100644 crates/aptos/src/move_tool/package_hooks.rs delete mode 100644 crates/aptos/src/move_tool/show.rs delete mode 100644 crates/aptos/src/move_tool/stored_package.rs delete mode 100644 crates/aptos/src/node/analyze/analyze_validators.rs delete mode 100644 crates/aptos/src/node/analyze/fetch_metadata.rs delete mode 100644 crates/aptos/src/node/analyze/mod.rs delete mode 100644 crates/aptos/src/node/local_testnet/docker.rs delete mode 100644 crates/aptos/src/node/local_testnet/faucet.rs delete mode 100644 crates/aptos/src/node/local_testnet/hasura_metadata.json delete mode 100644 crates/aptos/src/node/local_testnet/health_checker.rs delete mode 100644 crates/aptos/src/node/local_testnet/indexer_api.rs delete mode 100644 crates/aptos/src/node/local_testnet/logging.rs delete mode 100644 crates/aptos/src/node/local_testnet/mod.rs delete mode 100644 crates/aptos/src/node/local_testnet/node.rs delete mode 100644 crates/aptos/src/node/local_testnet/postgres.rs delete mode 100644 crates/aptos/src/node/local_testnet/processors.rs delete mode 100644 crates/aptos/src/node/local_testnet/ready_server.rs delete mode 100644 crates/aptos/src/node/local_testnet/traits.rs delete mode 100644 crates/aptos/src/node/local_testnet/utils.rs delete mode 100644 crates/aptos/src/node/mod.rs delete mode 100644 crates/aptos/src/op/key.rs delete mode 100644 crates/aptos/src/op/mod.rs delete mode 100644 crates/aptos/src/stake/mod.rs delete mode 100644 crates/aptos/src/test/mod.rs delete mode 100644 crates/aptos/src/test/tests.rs delete mode 100644 crates/aptos/src/update/aptos.rs delete mode 100644 crates/aptos/src/update/helpers.rs delete mode 100644 crates/aptos/src/update/mod.rs delete mode 100644 crates/aptos/src/update/revela.rs delete mode 100644 crates/aptos/src/update/tool.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/Cargo.toml delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/fullnode_tests.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs delete mode 100644 testsuite/forge-cli/Cargo.toml delete mode 100644 testsuite/forge-cli/src/README.md delete mode 100644 testsuite/forge-cli/src/main.rs delete mode 100644 testsuite/forge-cli/src/suites/dag.rs delete mode 100644 testsuite/forge-cli/src/suites/mod.rs delete mode 100644 testsuite/forge/Cargo.toml delete mode 100644 testsuite/forge/src/backend/k8s/chaos.rs delete mode 100644 testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos/netem.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos/network_delay.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos/network_loss.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos/network_partition.yaml delete mode 100644 testsuite/forge/src/backend/k8s/chaos_schema.rs delete mode 100644 testsuite/forge/src/backend/k8s/cluster_helper.rs delete mode 100644 testsuite/forge/src/backend/k8s/constants.rs delete mode 100644 testsuite/forge/src/backend/k8s/fullnode.rs delete mode 100644 testsuite/forge/src/backend/k8s/helm-values/aptos-node-default-values.yaml delete mode 100644 testsuite/forge/src/backend/k8s/kube_api.rs delete mode 100644 testsuite/forge/src/backend/k8s/mod.rs delete mode 100644 testsuite/forge/src/backend/k8s/node.rs delete mode 100644 testsuite/forge/src/backend/k8s/prometheus.rs delete mode 100644 testsuite/forge/src/backend/k8s/stateful_set.rs delete mode 100644 testsuite/forge/src/backend/k8s/swarm.rs delete mode 100644 testsuite/forge/src/backend/local/cargo.rs delete mode 100644 testsuite/forge/src/backend/local/mod.rs delete mode 100644 testsuite/forge/src/backend/local/node.rs delete mode 100644 testsuite/forge/src/backend/local/swarm.rs delete mode 100644 testsuite/forge/src/backend/mod.rs delete mode 100644 testsuite/forge/src/github.rs delete mode 100644 testsuite/forge/src/interface/admin.rs delete mode 100644 testsuite/forge/src/interface/aptos.rs delete mode 100644 testsuite/forge/src/interface/chain_info.rs delete mode 100644 testsuite/forge/src/interface/chaos.rs delete mode 100644 testsuite/forge/src/interface/factory.rs delete mode 100644 testsuite/forge/src/interface/mod.rs delete mode 100644 testsuite/forge/src/interface/network.rs delete mode 100644 testsuite/forge/src/interface/node.rs delete mode 100644 testsuite/forge/src/interface/prometheus_metrics.rs delete mode 100644 testsuite/forge/src/interface/swarm.rs delete mode 100644 testsuite/forge/src/interface/test.rs delete mode 100644 testsuite/forge/src/lib.rs delete mode 100644 testsuite/forge/src/report.rs delete mode 100644 testsuite/forge/src/runner.rs delete mode 100644 testsuite/forge/src/slack.rs delete mode 100644 testsuite/forge/src/success_criteria.rs delete mode 100644 testsuite/forge/src/test_utils/consensus_utils.rs delete mode 100644 testsuite/forge/src/test_utils/mod.rs delete mode 100644 testsuite/smoke-test/Cargo.toml delete mode 100644 testsuite/smoke-test/src/aptos/account_creation.rs delete mode 100644 testsuite/smoke-test/src/aptos/error_report.rs delete mode 100644 testsuite/smoke-test/src/aptos/gas_check.rs delete mode 100644 testsuite/smoke-test/src/aptos/mint_transfer.rs delete mode 100644 testsuite/smoke-test/src/aptos/mod.rs delete mode 100644 testsuite/smoke-test/src/aptos/move_test_helpers.rs delete mode 100644 testsuite/smoke-test/src/aptos/package_publish.rs delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v1/Move.toml delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v1/sources/hello.move delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v2/Move.toml delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v2/sources/hello.move delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v3/Move.toml delete mode 100644 testsuite/smoke-test/src/aptos/package_publish_modules_v3/sources/hello.move delete mode 100644 testsuite/smoke-test/src/aptos_cli/account.rs delete mode 100644 testsuite/smoke-test/src/aptos_cli/mod.rs delete mode 100644 testsuite/smoke-test/src/aptos_cli/move.rs delete mode 100644 testsuite/smoke-test/src/aptos_cli/validator.rs delete mode 100644 testsuite/smoke-test/src/client.rs delete mode 100644 testsuite/smoke-test/src/consensus/consensus_fault_tolerance.rs delete mode 100644 testsuite/smoke-test/src/consensus/consensus_only.rs delete mode 100644 testsuite/smoke-test/src/consensus/consensusdb_recovery.rs delete mode 100644 testsuite/smoke-test/src/consensus/dag/dag_fault_tolerance.rs delete mode 100644 testsuite/smoke-test/src/consensus/dag/mod.rs delete mode 100644 testsuite/smoke-test/src/consensus/mod.rs delete mode 100644 testsuite/smoke-test/src/consensus/quorum_store_fault_tolerance.rs delete mode 100644 testsuite/smoke-test/src/execution.rs delete mode 100644 testsuite/smoke-test/src/full_nodes.rs delete mode 100644 testsuite/smoke-test/src/fullnode.rs delete mode 100644 testsuite/smoke-test/src/genesis.rs delete mode 100644 testsuite/smoke-test/src/indexer.rs delete mode 100644 testsuite/smoke-test/src/inspection_service.rs delete mode 100644 testsuite/smoke-test/src/jwks/dummy_provider/mod.rs delete mode 100644 testsuite/smoke-test/src/jwks/dummy_provider/request_handler.rs delete mode 100644 testsuite/smoke-test/src/jwks/jwk_consensus_basic.rs delete mode 100644 testsuite/smoke-test/src/jwks/jwk_consensus_per_issuer.rs delete mode 100644 testsuite/smoke-test/src/jwks/jwk_consensus_provider_change_mind.rs delete mode 100644 testsuite/smoke-test/src/jwks/mod.rs delete mode 100644 testsuite/smoke-test/src/keyless.rs delete mode 100644 testsuite/smoke-test/src/lib.rs delete mode 100644 testsuite/smoke-test/src/network.rs delete mode 100644 testsuite/smoke-test/src/randomness/disable_feature_0.rs delete mode 100644 testsuite/smoke-test/src/randomness/disable_feature_1.rs delete mode 100644 testsuite/smoke-test/src/randomness/dkg_with_validator_down.rs delete mode 100644 testsuite/smoke-test/src/randomness/dkg_with_validator_join_leave.rs delete mode 100644 testsuite/smoke-test/src/randomness/e2e_basic_consumption.rs delete mode 100644 testsuite/smoke-test/src/randomness/e2e_correctness.rs delete mode 100644 testsuite/smoke-test/src/randomness/enable_feature_0.rs delete mode 100644 testsuite/smoke-test/src/randomness/enable_feature_1.rs delete mode 100644 testsuite/smoke-test/src/randomness/enable_feature_2.rs delete mode 100644 testsuite/smoke-test/src/randomness/mod.rs delete mode 100644 testsuite/smoke-test/src/randomness/validator_restart_during_dkg.rs delete mode 100644 testsuite/smoke-test/src/rest_api.rs delete mode 100644 testsuite/smoke-test/src/rosetta.rs delete mode 100644 testsuite/smoke-test/src/smoke_test_environment.rs delete mode 100644 testsuite/smoke-test/src/state_sync.rs delete mode 100644 testsuite/smoke-test/src/storage.rs delete mode 100644 testsuite/smoke-test/src/test_smoke_tests.rs delete mode 100644 testsuite/smoke-test/src/test_utils.rs delete mode 100644 testsuite/smoke-test/src/transaction.rs delete mode 100644 testsuite/smoke-test/src/txn_broadcast.rs delete mode 100644 testsuite/smoke-test/src/txn_emitter.rs delete mode 100644 testsuite/smoke-test/src/upgrade.rs delete mode 100644 testsuite/smoke-test/src/upgrade_multi_step_test_metadata.txt delete mode 100644 testsuite/smoke-test/src/utils.rs delete mode 100644 testsuite/smoke-test/src/validator_txns.rs delete mode 100644 testsuite/smoke-test/src/workspace_builder.rs delete mode 100644 testsuite/testcases/Cargo.toml delete mode 100644 testsuite/testcases/src/compatibility_test.rs delete mode 100644 testsuite/testcases/src/consensus_reliability_tests.rs delete mode 100644 testsuite/testcases/src/dag_onchain_enable_test.rs delete mode 100644 testsuite/testcases/src/data/four_region_link_stats.csv delete mode 100644 testsuite/testcases/src/data/two_region_link_stats.csv delete mode 100644 testsuite/testcases/src/forge_setup_test.rs delete mode 100644 testsuite/testcases/src/framework_upgrade.rs delete mode 100644 testsuite/testcases/src/fullnode_reboot_stress_test.rs delete mode 100644 testsuite/testcases/src/lib.rs delete mode 100644 testsuite/testcases/src/load_vs_perf_benchmark.rs delete mode 100644 testsuite/testcases/src/modifiers.rs delete mode 100644 testsuite/testcases/src/multi_region_network_test.rs delete mode 100644 testsuite/testcases/src/network_bandwidth_test.rs delete mode 100644 testsuite/testcases/src/network_loss_test.rs delete mode 100644 testsuite/testcases/src/network_partition_test.rs delete mode 100644 testsuite/testcases/src/partial_nodes_down_test.rs delete mode 100644 testsuite/testcases/src/performance_test.rs delete mode 100644 testsuite/testcases/src/public_fullnode_performance.rs delete mode 100644 testsuite/testcases/src/quorum_store_onchain_enable_test.rs delete mode 100644 testsuite/testcases/src/reconfiguration_test.rs delete mode 100644 testsuite/testcases/src/state_sync_performance.rs delete mode 100644 testsuite/testcases/src/three_region_simulation_test.rs delete mode 100644 testsuite/testcases/src/twin_validator_test.rs delete mode 100644 testsuite/testcases/src/two_traffics_test.rs delete mode 100644 testsuite/testcases/src/validator_join_leave_test.rs delete mode 100644 testsuite/testcases/src/validator_reboot_stress_test.rs delete mode 100644 testsuite/testcases/tests/forge-local-compatibility.rs delete mode 100644 testsuite/testcases/tests/forge-local-performance.rs diff --git a/Cargo.lock b/Cargo.lock index 945c6effe3192..d604ef464dce0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ name = "abstract-domain-derive" version = "0.1.0" dependencies = [ "move-stackless-bytecode", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -81,34 +81,33 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.14", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.11", + "getrandom 0.2.14", "once_cell", - "serde", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -121,9 +120,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -157,9 +156,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -171,9 +170,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -211,113 +210,9 @@ checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" [[package]] name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" -dependencies = [ - "backtrace", -] - -[[package]] -name = "approx" -version = "0.5.1" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "aptos" -version = "3.0.2" -dependencies = [ - "anyhow", - "aptos-api-types", - "aptos-backup-cli", - "aptos-bitvec", - "aptos-build-info", - "aptos-cached-packages", - "aptos-cli-common", - "aptos-config", - "aptos-crypto", - "aptos-faucet-core", - "aptos-framework", - "aptos-gas-profiling", - "aptos-gas-schedule", - "aptos-genesis", - "aptos-github-client", - "aptos-global-constants", - "aptos-indexer-grpc-server-framework", - "aptos-indexer-grpc-utils", - "aptos-keygen", - "aptos-ledger", - "aptos-logger", - "aptos-move-debugger", - "aptos-network-checker", - "aptos-node", - "aptos-protos 1.3.0", - "aptos-rest-client", - "aptos-sdk", - "aptos-storage-interface", - "aptos-telemetry", - "aptos-temppath", - "aptos-types", - "aptos-vm", - "aptos-vm-genesis", - "async-trait", - "base64 0.13.1", - "bcs 0.1.4", - "bollard", - "chrono", - "clap 4.4.12", - "clap_complete", - "codespan-reporting", - "dashmap", - "diesel", - "diesel-async", - "dirs", - "futures", - "hex", - "itertools 0.10.5", - "jemallocator", - "maplit", - "move-binary-format", - "move-bytecode-source-map", - "move-cli", - "move-command-line-common", - "move-compiler", - "move-core-types", - "move-coverage", - "move-disassembler", - "move-ir-types", - "move-package", - "move-symbol-pool", - "move-unit-test", - "move-vm-runtime", - "once_cell", - "pathsearch", - "poem", - "processor", - "rand 0.7.3", - "regex", - "reqwest", - "self_update", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "server-framework", - "shadow-rs", - "tempfile", - "termcolor", - "thiserror", - "tokio", - "toml 0.7.8", - "tonic 0.11.0", - "tracing", - "tracing-subscriber 0.3.18", - "version-compare", - "walkdir", -] +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "aptos-abstract-gas-usage" @@ -360,7 +255,7 @@ dependencies = [ "async-mutex", "bcs 0.1.4", "futures", - "http", + "http 0.2.12", "hyper", "lazy_static", "mime", @@ -567,7 +462,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "bytes", - "clap 4.4.12", + "clap 4.5.4", "csv", "futures", "itertools 0.10.5", @@ -575,7 +470,7 @@ dependencies = [ "move-bytecode-verifier", "num_cpus", "once_cell", - "pin-project 1.1.3", + "pin-project", "proptest", "rand 0.7.3", "regex", @@ -587,7 +482,7 @@ dependencies = [ "tokio", "tokio-io-timeout", "tokio-stream", - "tokio-util 0.7.10", + "tokio-util", "warp", ] @@ -685,7 +580,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "criterion", "dashmap", "itertools 0.10.5", @@ -734,7 +629,7 @@ name = "aptos-cargo-cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "clap-verbosity-flag", "determinator", "env_logger", @@ -757,67 +652,10 @@ dependencies = [ "tokio", ] -[[package]] -name = "aptos-cli-common" -version = "1.0.0" -dependencies = [ - "anstyle", - "clap 4.4.12", - "clap_complete", -] - [[package]] name = "aptos-collections" version = "0.1.0" -[[package]] -name = "aptos-comparison-testing" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos", - "aptos-crypto", - "aptos-framework", - "aptos-gas-meter", - "aptos-gas-profiling", - "aptos-gas-schedule", - "aptos-language-e2e-tests", - "aptos-logger", - "aptos-memory-usage-tracker", - "aptos-resource-viewer", - "aptos-rest-client", - "aptos-table-natives", - "aptos-types", - "aptos-validator-interface", - "aptos-vm", - "aptos-vm-logging", - "aptos-vm-types", - "bcs 0.1.4", - "clap 4.4.12", - "futures", - "itertools 0.10.5", - "lru 0.7.8", - "move-binary-format", - "move-cli", - "move-compiler", - "move-core-types", - "move-package", - "move-resource-viewer", - "move-vm-runtime", - "move-vm-test-utils", - "move-vm-types", - "num_cpus", - "once_cell", - "rayon", - "regex", - "rocksdb", - "serde", - "serde_json", - "tempfile", - "tokio", - "url", -] - [[package]] name = "aptos-compression" version = "0.1.0" @@ -918,7 +756,7 @@ dependencies = [ "bytes", "chrono", "claims", - "clap 4.4.12", + "clap 4.5.4", "dashmap", "enum_dispatch", "fail 0.5.1", @@ -1024,10 +862,10 @@ dependencies = [ "byteorder", "bytes", "criterion", - "curve25519-dalek 3.2.0", + "curve25519-dalek", "curve25519-dalek-ng", "digest 0.9.0", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "hex", "hkdf 0.10.0", "libsecp256k1", @@ -1042,7 +880,6 @@ dependencies = [ "proptest-derive 0.4.0", "rand 0.7.3", "rand_core 0.5.1", - "ring 0.16.20", "serde", "serde-name", "serde_bytes", @@ -1063,8 +900,8 @@ name = "aptos-crypto-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -1098,7 +935,7 @@ dependencies = [ "itertools 0.10.5", "maplit", "mockall", - "ordered-float 3.9.2", + "ordered-float", "rand 0.8.5", "serde", "thiserror", @@ -1164,7 +1001,7 @@ dependencies = [ "bcs 0.1.4", "byteorder", "claims", - "clap 4.4.12", + "clap 4.5.4", "dashmap", "either", "itertools 0.10.5", @@ -1236,7 +1073,7 @@ dependencies = [ "aptos-vm", "async-trait", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "itertools 0.10.5", "owo-colors", "tokio", @@ -1252,7 +1089,7 @@ dependencies = [ "aptos-logger", "aptos-move-debugger", "aptos-push-metrics", - "clap 4.4.12", + "clap 4.5.4", "tokio", ] @@ -1344,8 +1181,8 @@ name = "aptos-enum-conversion-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", "trybuild", ] @@ -1463,9 +1300,9 @@ dependencies = [ "async-trait", "bcs 0.1.4", "chrono", - "clap 4.4.12", + "clap 4.5.4", "derivative", - "indicatif 0.15.0", + "indicatif", "itertools 0.10.5", "jemallocator", "move-core-types", @@ -1500,7 +1337,7 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "crossbeam-channel", "ctrlc", "dashmap", @@ -1628,7 +1465,7 @@ dependencies = [ "aptos-faucet-core", "aptos-logger", "aptos-sdk", - "clap 4.4.12", + "clap 4.5.4", "tokio", ] @@ -1644,7 +1481,7 @@ dependencies = [ "aptos-sdk", "async-trait", "captcha", - "clap 4.4.12", + "clap 4.5.4", "deadpool-redis", "enum_dispatch", "futures", @@ -1686,7 +1523,7 @@ dependencies = [ "anyhow", "aptos-faucet-core", "aptos-logger", - "clap 4.4.12", + "clap 4.5.4", "tokio", ] @@ -1698,7 +1535,7 @@ dependencies = [ "aptos-logger", "aptos-node-checker", "aptos-sdk", - "clap 4.4.12", + "clap 4.5.4", "env_logger", "futures", "gcp-bigquery-client", @@ -1708,88 +1545,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "aptos-forge" -version = "0.0.0" -dependencies = [ - "again", - "anyhow", - "aptos", - "aptos-cached-packages", - "aptos-cli-common", - "aptos-config", - "aptos-db", - "aptos-framework", - "aptos-genesis", - "aptos-global-constants", - "aptos-infallible", - "aptos-inspection-service", - "aptos-logger", - "aptos-rest-client", - "aptos-retrier", - "aptos-sdk", - "aptos-secure-storage", - "aptos-short-hex-str", - "aptos-state-sync-driver", - "aptos-transaction-emitter-lib", - "aptos-transaction-generator-lib", - "async-trait", - "chrono", - "clap 4.4.12", - "either", - "futures", - "hex", - "hyper", - "hyper-tls", - "itertools 0.10.5", - "json-patch", - "k8s-openapi", - "kube", - "num_cpus", - "once_cell", - "prometheus-http-query", - "rand 0.7.3", - "rayon", - "regex", - "reqwest", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tempfile", - "termcolor", - "thiserror", - "tokio", - "url", -] - -[[package]] -name = "aptos-forge-cli" -version = "0.0.0" -dependencies = [ - "anyhow", - "aptos-cached-packages", - "aptos-config", - "aptos-forge", - "aptos-framework", - "aptos-global-constants", - "aptos-logger", - "aptos-rest-client", - "aptos-sdk", - "aptos-testcases", - "async-trait", - "chrono", - "clap 4.4.12", - "futures", - "jemallocator", - "once_cell", - "rand 0.7.3", - "random_word", - "reqwest", - "serde_yaml 0.8.26", - "tokio", - "url", -] - [[package]] name = "aptos-framework" version = "0.1.0" @@ -1821,7 +1576,7 @@ dependencies = [ "bulletproofs", "byteorder", "claims", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "curve25519-dalek-ng", "either", @@ -1878,36 +1633,6 @@ dependencies = [ "move-core-types", ] -[[package]] -name = "aptos-gas-calibration" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos", - "aptos-abstract-gas-usage", - "aptos-cached-packages", - "aptos-framework", - "aptos-gas-algebra", - "aptos-gas-meter", - "aptos-gas-schedule", - "aptos-language-e2e-tests", - "aptos-move-stdlib", - "aptos-native-interface", - "aptos-types", - "aptos-vm-types", - "bcs 0.1.4", - "clap 4.4.12", - "float-cmp", - "move-binary-format", - "move-bytecode-source-map", - "move-core-types", - "move-ir-compiler", - "move-vm-runtime", - "move-vm-test-utils", - "nalgebra", - "walkdir", -] - [[package]] name = "aptos-gas-meter" version = "0.1.0" @@ -1969,7 +1694,7 @@ dependencies = [ "aptos-package-builder", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-core-types", "move-model", "tempfile", @@ -2040,7 +1765,7 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", - "clap 4.4.12", + "clap 4.5.4", "diesel", "diesel_migrations", "field_count", @@ -2067,18 +1792,18 @@ dependencies = [ "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-metrics-core", - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", - "aptos-protos 1.3.0", + "aptos-moving-average", + "aptos-protos", "aptos-runtimes", "async-trait", "backoff", "base64 0.13.1", - "clap 4.4.12", + "clap 4.5.4", "futures", "futures-core", "jemallocator", "once_cell", - "prost 0.12.3", + "prost 0.12.4", "redis", "reqwest", "serde", @@ -2097,13 +1822,13 @@ version = "1.0.0" dependencies = [ "anyhow", "aptos-metrics-core", - "aptos-protos 1.3.0", + "aptos-protos", "async-trait", "backoff", "backtrace", "base64 0.13.1", "bytes", - "clap 4.4.12", + "clap 4.5.4", "cloud-storage", "dashmap", "enum_dispatch", @@ -2114,7 +1839,7 @@ dependencies = [ "itertools 0.10.5", "once_cell", "prometheus", - "prost 0.12.3", + "prost 0.12.4", "redis", "redis-test", "serde", @@ -2123,7 +1848,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tokio-util 0.7.10", + "tokio-util", "toml 0.7.8", "tonic 0.11.0", "tracing", @@ -2141,17 +1866,17 @@ dependencies = [ "aptos-indexer-grpc-utils", "aptos-logger", "aptos-metrics-core", - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", - "aptos-protos 1.3.0", + "aptos-moving-average", + "aptos-protos", "aptos-runtimes", "async-trait", "base64 0.13.1", - "clap 4.4.12", + "clap 4.5.4", "cloud-storage", "futures", "jemallocator", "once_cell", - "prost 0.12.3", + "prost 0.12.4", "redis", "serde", "serde_json", @@ -2171,18 +1896,18 @@ dependencies = [ "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-metrics-core", - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", - "aptos-protos 1.3.0", + "aptos-moving-average", + "aptos-protos", "aptos-runtimes", "async-trait", "base64 0.13.1", - "clap 4.4.12", + "clap 4.5.4", "cloud-storage", "futures", "futures-util", "jemallocator", "once_cell", - "prost 0.12.3", + "prost 0.12.4", "redis", "serde", "serde_json", @@ -2213,9 +1938,9 @@ dependencies = [ "aptos-mempool", "aptos-mempool-notifications", "aptos-metrics-core", - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", + "aptos-moving-average", "aptos-proptest-helpers", - "aptos-protos 1.3.0", + "aptos-protos", "aptos-runtimes", "aptos-sdk", "aptos-secure-storage", @@ -2249,59 +1974,16 @@ dependencies = [ ] [[package]] -name = "aptos-indexer-grpc-integration-tests" -version = "0.1.0" +name = "aptos-indexer-grpc-server-framework" +version = "1.0.0" dependencies = [ "anyhow", - "aptos-config", - "aptos-indexer-grpc-cache-worker", - "aptos-indexer-grpc-data-service", - "aptos-indexer-grpc-file-store", - "aptos-indexer-grpc-server-framework", - "aptos-indexer-grpc-utils", - "aptos-inspection-service", - "aptos-logger", - "aptos-protos 1.3.0", - "aptos-runtimes", - "aptos-transaction-emitter-lib", - "aptos-transaction-generator-lib", - "aptos-types", - "async-trait", - "backoff", - "base64 0.13.1", - "clap 4.4.12", - "futures", - "futures-core", - "futures-util", - "itertools 0.10.5", - "once_cell", - "prometheus", - "prost 0.12.3", - "redis", - "regex", - "reqwest", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tempfile", - "tokio", - "tonic 0.11.0", - "tracing", - "url", - "warp", -] - -[[package]] -name = "aptos-indexer-grpc-server-framework" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-admin-service", - "aptos-metrics-core", + "aptos-admin-service", + "aptos-metrics-core", "aptos-runtimes", "async-trait", "backtrace", - "clap 4.4.12", + "clap 4.5.4", "futures", "prometheus", "serde", @@ -2330,7 +2012,7 @@ dependencies = [ "aptos-logger", "aptos-mempool", "aptos-metrics-core", - "aptos-protos 1.3.0", + "aptos-protos", "aptos-rocksdb-options", "aptos-runtimes", "aptos-schemadb", @@ -2354,7 +2036,7 @@ dependencies = [ "tempfile", "tokio", "tokio-stream", - "tokio-util 0.7.10", + "tokio-util", "tonic 0.11.0", "tonic-reflection", ] @@ -2365,13 +2047,13 @@ version = "1.0.0" dependencies = [ "anyhow", "aptos-metrics-core", - "aptos-protos 1.3.0", + "aptos-protos", "async-trait", "backoff", "backtrace", "base64 0.13.1", "chrono", - "clap 4.4.12", + "clap 4.5.4", "cloud-storage", "flate2", "futures", @@ -2380,7 +2062,7 @@ dependencies = [ "itertools 0.10.5", "once_cell", "prometheus", - "prost 0.12.3", + "prost 0.12.4", "redis", "redis-test", "ripemd", @@ -2400,35 +2082,6 @@ dependencies = [ name = "aptos-infallible" version = "0.1.0" -[[package]] -name = "aptos-inspection-service" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-build-info", - "aptos-config", - "aptos-data-client", - "aptos-infallible", - "aptos-logger", - "aptos-metrics-core", - "aptos-network", - "aptos-runtimes", - "aptos-storage-interface", - "aptos-storage-service-client", - "aptos-telemetry", - "aptos-time-service", - "assert_approx_eq", - "futures", - "hyper", - "once_cell", - "prometheus", - "reqwest", - "rusty-fork", - "serde_json", - "sysinfo", - "tokio", -] - [[package]] name = "aptos-jellyfish-merkle" version = "0.1.0" @@ -2520,7 +2173,7 @@ dependencies = [ "base64 0.13.1", "bcs 0.1.4", "byteorder", - "curve25519-dalek 3.2.0", + "curve25519-dalek", "ff 0.13.0", "group 0.13.0", "hex", @@ -2646,8 +2299,8 @@ dependencies = [ name = "aptos-log-derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -2788,7 +2441,7 @@ dependencies = [ "aptos-vm-logging", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-binary-format", "move-cli", "move-compiler", @@ -2821,7 +2474,7 @@ dependencies = [ "aptos-gas-schedule", "aptos-types", "aptos-vm", - "clap 4.4.12", + "clap 4.5.4", "move-cli", "move-package", "move-prover", @@ -2869,14 +2522,6 @@ dependencies = [ "chrono", ] -[[package]] -name = "aptos-moving-average" -version = "0.1.0" -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352#d44b2d209f57872ac593299c34751a5531b51352" -dependencies = [ - "chrono", -] - [[package]] name = "aptos-mvhashmap" version = "0.1.0" @@ -2928,10 +2573,10 @@ dependencies = [ "aptos-types", "bytes", "futures", - "pin-project 1.1.3", + "pin-project", "serde", "tokio", - "tokio-util 0.7.10", + "tokio-util", "url", ] @@ -2969,8 +2614,8 @@ dependencies = [ "itertools 0.10.5", "maplit", "once_cell", - "ordered-float 3.9.2", - "pin-project 1.1.3", + "ordered-float", + "pin-project", "proptest", "proptest-derive 0.4.0", "rand 0.7.3", @@ -2984,7 +2629,7 @@ dependencies = [ "thiserror", "tokio", "tokio-retry", - "tokio-util 0.7.10", + "tokio-util", ] [[package]] @@ -3042,7 +2687,7 @@ dependencies = [ "aptos-logger", "aptos-network", "aptos-types", - "clap 4.4.12", + "clap 4.5.4", "futures", "serde", "tokio", @@ -3089,7 +2734,7 @@ dependencies = [ "base64 0.13.1", "bytes", "chrono", - "clap 4.4.12", + "clap 4.5.4", "csv", "diesel", "diesel_migrations", @@ -3109,83 +2754,6 @@ dependencies = [ "warp", ] -[[package]] -name = "aptos-node" -version = "0.0.0-main" -dependencies = [ - "anyhow", - "aptos-admin-service", - "aptos-api", - "aptos-backup-service", - "aptos-build-info", - "aptos-cached-packages", - "aptos-channels", - "aptos-config", - "aptos-consensus", - "aptos-consensus-notifications", - "aptos-crash-handler", - "aptos-crypto", - "aptos-data-client", - "aptos-data-streaming-service", - "aptos-db", - "aptos-db-indexer", - "aptos-dkg-runtime", - "aptos-event-notifications", - "aptos-executor", - "aptos-executor-types", - "aptos-framework", - "aptos-genesis", - "aptos-indexer", - "aptos-indexer-grpc-fullnode", - "aptos-indexer-grpc-table-info", - "aptos-infallible", - "aptos-inspection-service", - "aptos-jwk-consensus", - "aptos-logger", - "aptos-mempool", - "aptos-mempool-notifications", - "aptos-network", - "aptos-network-benchmark", - "aptos-network-builder", - "aptos-node-identity", - "aptos-peer-monitoring-service-client", - "aptos-peer-monitoring-service-server", - "aptos-peer-monitoring-service-types", - "aptos-runtimes", - "aptos-safety-rules", - "aptos-secure-storage", - "aptos-state-sync-driver", - "aptos-storage-interface", - "aptos-storage-service-client", - "aptos-storage-service-notifications", - "aptos-storage-service-server", - "aptos-storage-service-types", - "aptos-telemetry", - "aptos-temppath", - "aptos-time-service", - "aptos-types", - "aptos-validator-transaction-pool", - "aptos-vm", - "bcs 0.1.4", - "clap 4.4.12", - "either", - "fail 0.5.1", - "futures", - "hex", - "jemallocator", - "maplit", - "num_cpus", - "rand 0.7.3", - "rayon", - "rstack-self", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tokio", - "tokio-stream", - "url", -] - [[package]] name = "aptos-node-checker" version = "0.1.1" @@ -3200,7 +2768,7 @@ dependencies = [ "aptos-sdk", "aptos-transaction-emitter-lib", "async-trait", - "clap 4.4.12", + "clap 4.5.4", "const_format", "env_logger", "futures", @@ -3247,8 +2815,8 @@ dependencies = [ name = "aptos-num-variants" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -3274,7 +2842,7 @@ dependencies = [ "aptos-mempool", "aptos-storage-interface", "aptos-types", - "clap 4.4.12", + "clap 4.5.4", ] [[package]] @@ -3391,25 +2959,12 @@ version = "1.3.0" dependencies = [ "futures-core", "pbjson", - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.4", + "prost-types 0.12.4", "serde", "tonic 0.11.0", ] -[[package]] -name = "aptos-protos" -version = "1.3.0" -source = "git+https://github.com/aptos-labs/aptos-core.git?rev=5d1cefad0ea0d37fb08e9aec7847080745c83f9c#5d1cefad0ea0d37fb08e9aec7847080745c83f9c" -dependencies = [ - "futures-core", - "pbjson", - "prost 0.12.3", - "prost-types 0.12.3", - "serde", - "tonic 0.10.2", -] - [[package]] name = "aptos-proxy" version = "0.1.0" @@ -3435,48 +2990,9 @@ dependencies = [ "aptos-logger", "aptos-metrics-core", "futures", - "pin-project 1.1.3", - "tokio", - "tokio-util 0.7.10", -] - -[[package]] -name = "aptos-release-builder" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos", - "aptos-api-types", - "aptos-build-info", - "aptos-crypto", - "aptos-framework", - "aptos-gas-schedule-updator", - "aptos-genesis", - "aptos-infallible", - "aptos-keygen", - "aptos-rest-client", - "aptos-temppath", - "aptos-types", - "aptos-vm-genesis", - "bcs 0.1.4", - "clap 4.4.12", - "futures", - "git2 0.16.1", - "handlebars", - "hex", - "move-binary-format", - "move-core-types", - "move-model", - "once_cell", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "strum 0.24.1", - "strum_macros 0.24.3", - "tempfile", + "pin-project", "tokio", - "url", - "walkdir", + "tokio-util", ] [[package]] @@ -3521,7 +3037,7 @@ dependencies = [ "aptos-types", "bcs 0.1.4", "bytes", - "clap 4.4.12", + "clap 4.5.4", "futures", "hex", "move-binary-format", @@ -3551,55 +3067,6 @@ dependencies = [ "rocksdb", ] -[[package]] -name = "aptos-rosetta" -version = "0.0.1" -dependencies = [ - "anyhow", - "aptos-cached-packages", - "aptos-config", - "aptos-crypto", - "aptos-global-constants", - "aptos-logger", - "aptos-node", - "aptos-rest-client", - "aptos-runtimes", - "aptos-sdk", - "aptos-types", - "aptos-warp-webserver", - "bcs 0.1.4", - "clap 4.4.12", - "futures", - "hex", - "itertools 0.10.5", - "move-core-types", - "once_cell", - "percent-encoding", - "reqwest", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tokio", - "url", - "warp", -] - -[[package]] -name = "aptos-rosetta-cli" -version = "0.0.1" -dependencies = [ - "anyhow", - "aptos", - "aptos-logger", - "aptos-rosetta", - "aptos-types", - "clap 4.4.12", - "serde", - "serde_json", - "tokio", - "url", -] - [[package]] name = "aptos-runtimes" version = "0.1.0" @@ -3710,7 +3177,7 @@ dependencies = [ "aptos-framework", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "heck 0.4.1", "move-core-types", "once_cell", @@ -3730,7 +3197,7 @@ dependencies = [ "aptos-config", "aptos-logger", "aptos-metrics-core", - "aptos-protos 1.3.0", + "aptos-protos", "aptos-retrier", "bcs 0.1.4", "crossbeam-channel", @@ -3973,91 +3440,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "aptos-telemetry" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-api", - "aptos-config", - "aptos-consensus", - "aptos-crypto", - "aptos-db", - "aptos-infallible", - "aptos-logger", - "aptos-mempool", - "aptos-metrics-core", - "aptos-network", - "aptos-node-resource-metrics", - "aptos-runtimes", - "aptos-state-sync-driver", - "aptos-telemetry-service", - "aptos-types", - "flate2", - "futures", - "httpmock", - "once_cell", - "prometheus", - "rand 0.7.3", - "rand_core 0.5.1", - "reqwest", - "reqwest-middleware", - "reqwest-retry", - "serde", - "serde_json", - "sysinfo", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "url", - "uuid", -] - -[[package]] -name = "aptos-telemetry-service" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-config", - "aptos-crypto", - "aptos-crypto-derive", - "aptos-infallible", - "aptos-logger", - "aptos-metrics-core", - "aptos-rest-client", - "aptos-types", - "base64 0.13.1", - "bcs 0.1.4", - "chrono", - "claims", - "clap 4.4.12", - "debug-ignore", - "flate2", - "futures", - "gcp-bigquery-client", - "hex", - "httpmock", - "jsonwebtoken 8.3.0", - "once_cell", - "prometheus", - "rand 0.7.3", - "rand_core 0.5.1", - "reqwest", - "reqwest-middleware", - "reqwest-retry", - "serde", - "serde_json", - "serde_repr", - "serde_yaml 0.8.26", - "thiserror", - "tokio", - "tracing", - "url", - "uuid", - "warp", -] - [[package]] name = "aptos-temppath" version = "0.1.0" @@ -4066,37 +3448,6 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "aptos-testcases" -version = "0.0.0" -dependencies = [ - "anyhow", - "aptos", - "aptos-config", - "aptos-forge", - "aptos-genesis", - "aptos-global-constants", - "aptos-keygen", - "aptos-logger", - "aptos-move-examples", - "aptos-release-builder", - "aptos-rest-client", - "aptos-runtimes", - "aptos-sdk", - "aptos-temppath", - "aptos-types", - "assert_approx_eq", - "bcs 0.1.4", - "csv", - "futures", - "hex", - "itertools 0.10.5", - "rand 0.7.3", - "reqwest", - "serde_json", - "tokio", -] - [[package]] name = "aptos-time-service" version = "0.1.0" @@ -4104,7 +3455,7 @@ dependencies = [ "aptos-infallible", "enum_dispatch", "futures", - "pin-project 1.1.3", + "pin-project", "thiserror", "tokio", "tokio-test", @@ -4128,7 +3479,7 @@ dependencies = [ "aptos-types", "aptos-vm", "aptos-vm-logging", - "clap 4.4.12", + "clap 4.5.4", "criterion", "criterion-cpu-time", "num_cpus", @@ -4146,7 +3497,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "aptos-transaction-emitter-lib", - "clap 4.4.12", + "clap 4.5.4", "futures", "rand 0.7.3", "tokio", @@ -4168,7 +3519,7 @@ dependencies = [ "aptos-transaction-generator-lib", "aptos-types", "async-trait", - "clap 4.4.12", + "clap 4.5.4", "futures", "itertools 0.10.5", "once_cell", @@ -4190,7 +3541,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "async-trait", - "clap 4.4.12", + "clap 4.5.4", "move-binary-format", "once_cell", "rand 0.7.3", @@ -4214,7 +3565,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "datatest-stable", "hex", "move-binary-format", @@ -4410,22 +3761,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aptos-vm-benchmarks" -version = "0.1.0" -dependencies = [ - "aptos", - "aptos-cached-packages", - "aptos-framework", - "aptos-language-e2e-tests", - "aptos-types", - "bcs 0.1.4", - "clap 4.4.12", - "move-binary-format", - "move-bytecode-source-map", - "move-core-types", -] - [[package]] name = "aptos-vm-genesis" version = "0.1.0" @@ -4479,7 +3814,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "glob", "move-binary-format", "move-core-types", @@ -4587,9 +3922,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ark-bls12-381" @@ -4678,7 +4013,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.35", + "quote 1.0.36", "syn 1.0.109", ] @@ -4690,8 +4025,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -4755,8 +4090,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -4801,7 +4136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c6368f9ae5c6ec403ca910327ae0c9437b0a85255b6950c90d497e6177f6e5e" dependencies = [ "proc-macro-hack", - "quote 1.0.35", + "quote 1.0.36", "syn 1.0.109", ] @@ -4851,12 +4186,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "assert_approx_eq" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd" - [[package]] name = "assert_unordered" version = "0.3.5" @@ -4879,41 +4208,27 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", - "event-listener 4.0.2", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-compression" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" -dependencies = [ - "flate2", + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", - "memchr", "pin-project-lite", - "tokio", ] [[package]] name = "async-executor" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.2.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.1.0", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -4923,12 +4238,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.1.1", + "async-channel 2.2.1", "async-executor", - "async-io 2.2.2", - "async-lock 3.2.0", + "async-io 2.3.2", + "async-lock 3.3.0", "blocking", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "once_cell", ] @@ -4954,18 +4269,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ - "async-lock 3.2.0", + "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "parking", - "polling 3.3.1", - "rustix 0.38.28", + "polling 3.6.0", + "rustix 0.38.32", "slab", "tracing", "windows-sys 0.52.0", @@ -4982,12 +4297,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 4.0.2", - "event-listener-strategy", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -5022,19 +4337,19 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.28", + "rustix 0.38.32", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -5043,13 +4358,13 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.2.2", + "async-io 2.3.2", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.28", + "rustix 0.38.32", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -5099,26 +4414,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "async-task" -version = "4.6.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -5145,28 +4460,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "axum" @@ -5179,7 +4493,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -5194,7 +4508,7 @@ dependencies = [ "sync_wrapper", "tokio", "tower", - "tower-http 0.3.5", + "tower-http", "tower-layer", "tower-service", ] @@ -5210,7 +4524,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -5236,7 +4550,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "mime", "tower-layer", @@ -5252,7 +4566,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "mime", "rustversion", @@ -5281,7 +4595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.11", + "getrandom 0.2.14", "instant", "pin-project-lite", "rand 0.8.5", @@ -5290,9 +4604,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -5323,9 +4637,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -5339,7 +4653,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -5350,37 +4664,15 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-cookies" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" +checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" dependencies = [ "lalrpop", "lalrpop-util", "regex", ] -[[package]] -name = "basic-toml" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" -dependencies = [ - "serde", -] - -[[package]] -name = "bb8" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b4b0f25f18bcdc3ac72bdb486ed0acf7e185221fd4dc985bc15db5800b0ba2" -dependencies = [ - "async-trait", - "futures-channel", - "futures-util", - "parking_lot 0.12.1", - "tokio", -] - [[package]] name = "bcs" version = "0.1.4" @@ -5430,16 +4722,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "bigdecimal" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +checksum = "9324c8014cd04590682b34f1e9448d38f0674d0f7b2dc553331016ef0e4e9ebc" dependencies = [ "autocfg", "libm", @@ -5471,12 +4763,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 2.0.47", + "syn 2.0.60", ] [[package]] @@ -5508,9 +4800,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -5627,12 +4919,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.1", - "async-lock 3.2.0", + "async-channel 2.2.1", + "async-lock 3.3.0", "async-task", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "piper", "tracing", ] @@ -5665,46 +4957,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "bollard" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03db470b3c0213c47e978da93200259a1eb4dae2e5512cba9955e2b540a6fc6" -dependencies = [ - "base64 0.21.5", - "bollard-stubs", - "bytes", - "futures-core", - "futures-util", - "hex", - "http", - "hyper", - "hyperlocal", - "log", - "pin-project-lite", - "serde", - "serde_derive", - "serde_json", - "serde_repr", - "serde_urlencoded", - "thiserror", - "tokio", - "tokio-util 0.7.10", - "url", - "winapi 0.3.9", -] - -[[package]] -name = "bollard-stubs" -version = "1.43.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58071e8fd9ec1e930efd28e3a90c1251015872a2ce49f81f36421b86466932e" -dependencies = [ - "serde", - "serde_repr", - "serde_with", -] - [[package]] name = "bstr" version = "0.2.17" @@ -5718,9 +4970,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -5748,9 +5000,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -5799,15 +5051,15 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -5817,9 +5069,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -5866,9 +5118,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -5920,9 +5172,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", "libc", @@ -5939,9 +5191,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -5953,6 +5205,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "cfg_block" version = "0.1.1" @@ -5961,9 +5219,9 @@ checksum = "18758054972164c3264f7c8386f5fc6da6114cb46b619fd365d4e3b2dc3ae487" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -5971,14 +5229,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "chrono-tz" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" dependencies = [ "chrono", "chrono-tz-build", @@ -6004,9 +5262,9 @@ checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -6015,18 +5273,18 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 1.8.2", + "half 2.4.1", ] [[package]] @@ -6088,48 +5346,39 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.0", + "textwrap 0.16.1", ] [[package]] name = "clap" -version = "4.4.12" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", - "clap_derive 4.4.7", + "clap_derive 4.5.4", ] [[package]] name = "clap-verbosity-flag" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" +checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" dependencies = [ - "clap 4.4.12", + "clap 4.5.4", "log", ] [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", - "clap_lex 0.6.0", - "strsim 0.10.0", -] - -[[package]] -name = "clap_complete" -version = "4.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd" -dependencies = [ - "clap 4.4.12", + "clap_lex 0.7.0", + "strsim 0.11.1", ] [[package]] @@ -6140,21 +5389,21 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "heck 0.5.0", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -6168,9 +5417,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clear_on_drop" @@ -6250,16 +5499,16 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "futures-core", "memchr", "pin-project-lite", "tokio", - "tokio-util 0.7.10", + "tokio-util", ] [[package]] @@ -6273,15 +5522,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -6353,8 +5602,8 @@ version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "unicode-xid 0.2.4", ] @@ -6376,17 +5625,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - [[package]] name = "cookie" version = "0.17.0" @@ -6394,7 +5632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ "aes-gcm", - "base64 0.21.5", + "base64 0.21.7", "hkdf 0.12.4", "hmac 0.12.1", "percent-encoding", @@ -6407,12 +5645,12 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.16.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ - "cookie 0.16.2", - "idna 0.2.3", + "cookie", + "idna 0.3.0", "log", "publicsuffix", "serde", @@ -6451,9 +5689,9 @@ dependencies = [ [[package]] name = "coset" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c214bbc5c8b4518856d79cae4d323feaa881ecf3e31b5af6572bb5313c11d5" +checksum = "ff8aad850c1f86daa47e812913051eb5a26c4d9fb4242a89178bf99b946e4e3c" dependencies = [ "ciborium", "ciborium-io", @@ -6470,18 +5708,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -6534,11 +5772,10 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -6548,54 +5785,46 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crossterm" @@ -6729,34 +5958,34 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", + "nix 0.28.0", "windows-sys 0.52.0", ] [[package]] name = "curl" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.4.10", - "winapi 0.3.9", + "socket2 0.5.6", + "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.70+curl-8.5.0" +version = "0.4.72+curl-8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" dependencies = [ "cc", "libc", @@ -6765,7 +5994,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6781,34 +6010,6 @@ 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", - "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 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -] - [[package]] name = "curve25519-dalek-ng" version = "4.1.1" @@ -6823,16 +6024,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - [[package]] name = "darling" version = "0.14.4" @@ -6845,26 +6036,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.75", - "quote 1.0.35", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] @@ -6875,35 +6052,24 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "strsim 0.10.0", "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "strsim 0.10.0", - "syn 2.0.47", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote 1.0.35", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -6913,19 +6079,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.35", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ - "darling_core 0.20.3", - "quote 1.0.35", - "syn 2.0.47", + "darling_core 0.20.8", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -6995,9 +6161,6 @@ name = "debug-ignore" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" -dependencies = [ - "serde", -] [[package]] name = "debugid" @@ -7021,9 +6184,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid 0.9.6", "pem-rfc7468 0.7.0", @@ -7052,8 +6215,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -7063,9 +6226,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -7075,8 +6238,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustc_version", "syn 1.0.109", ] @@ -7100,18 +6263,18 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.4.2" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" +checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" [[package]] name = "diesel" -version = "2.1.4" +version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" +checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" dependencies = [ "bigdecimal", - "bitflags 2.4.1", + "bitflags 2.5.0", "byteorder", "chrono", "diesel_derives", @@ -7124,43 +6287,16 @@ dependencies = [ "serde_json", ] -[[package]] -name = "diesel-async" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" -dependencies = [ - "async-trait", - "bb8", - "diesel", - "futures-util", - "scoped-futures", - "tokio", - "tokio-postgres", -] - -[[package]] -name = "diesel_async_migrations" -version = "0.11.0" -source = "git+https://github.com/niroco/diesel_async_migrations?rev=11f331b73c5cfcc894380074f748d8fda710ac12#11f331b73c5cfcc894380074f748d8fda710ac12" -dependencies = [ - "diesel", - "diesel-async", - "macros", - "scoped-futures", - "tracing", -] - [[package]] name = "diesel_derives" -version = "2.1.2" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" dependencies = [ "diesel_table_macro_syntax", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -7180,7 +6316,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.47", + "syn 2.0.60", ] [[package]] @@ -7249,15 +6385,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -7268,18 +6395,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -7381,7 +6496,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.8", + "der 0.7.9", "digest 0.10.7", "elliptic-curve", "rfc6979", @@ -7399,24 +6514,14 @@ dependencies = [ "signature 1.6.4", ] -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8 0.10.2", - "signature 2.2.0", -] - [[package]] name = "ed25519-dalek" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519 1.5.3", + "curve25519-dalek", + "ed25519", "rand 0.7.3", "serde", "serde_bytes", @@ -7424,21 +6529,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek 4.1.2", - "ed25519 2.2.3", - "serde", - "sha2 0.10.8", - "signature 2.2.0", - "subtle", - "zeroize", -] - [[package]] name = "ed25519-dalek-bip32" version = "0.2.0" @@ -7446,16 +6536,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" dependencies = [ "derivation-path", - "ed25519-dalek 1.0.1", + "ed25519-dalek", "hmac 0.12.1", "sha2 0.10.8", ] [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" @@ -7497,30 +6587,30 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -7623,7 +6713,7 @@ dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", "impl-rlp", - "scale-info 2.10.0", + "scale-info 2.11.2", "tiny-keccak", ] @@ -7658,7 +6748,7 @@ dependencies = [ "hash256-std-hasher", "parity-scale-codec 3.6.9", "rlp", - "scale-info 2.10.0", + "scale-info 2.11.2", "serde", "sha3 0.10.8", "trie-root", @@ -7704,7 +6794,7 @@ dependencies = [ "impl-codec 0.6.0", "impl-rlp", "primitive-types 0.12.2", - "scale-info 2.10.0", + "scale-info 2.11.2", "uint", ] @@ -7733,9 +6823,20 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.2" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -7748,7 +6849,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.2", + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener 5.3.0", "pin-project-lite", ] @@ -7779,7 +6890,7 @@ version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" dependencies = [ - "auto_impl 1.1.0", + "auto_impl 1.2.0", "environmental", "ethereum 0.15.0", "evm-core 0.41.0", @@ -7789,7 +6900,7 @@ dependencies = [ "parity-scale-codec 3.6.9", "primitive-types 0.12.2", "rlp", - "scale-info 2.10.0", + "scale-info 2.11.2", "serde", "sha3 0.10.8", ] @@ -7815,7 +6926,7 @@ checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec 3.6.9", "primitive-types 0.12.2", - "scale-info 2.10.0", + "scale-info 2.11.2", "serde", ] @@ -7875,7 +6986,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" dependencies = [ - "auto_impl 1.1.0", + "auto_impl 1.2.0", "environmental", "evm-core 0.41.0", "primitive-types 0.12.2", @@ -7884,13 +6995,13 @@ dependencies = [ [[package]] name = "exr" -version = "1.71.0" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", - "half 2.2.1", + "half 2.4.1", "lebe", "miniz_oxide", "rayon-core", @@ -7920,12 +7031,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fallible_collections" version = "0.4.9" @@ -7946,15 +7051,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fdeflate" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -7981,12 +7086,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" - [[package]] name = "field_count" version = "0.1.1" @@ -8002,7 +7101,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1320970ff3b1c1cacc6a38e8cdb1aced955f29627697cd992c5ded82eb646a8" dependencies = [ - "quote 1.0.35", + "quote 1.0.36", "syn 1.0.109", ] @@ -8036,12 +7135,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - [[package]] name = "fixed" version = "1.27.0" @@ -8050,7 +7143,7 @@ checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", - "half 2.2.1", + "half 2.4.1", "typenum", ] @@ -8165,9 +7258,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -8280,11 +7373,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-core", "futures-io", "parking", @@ -8297,9 +7390,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -8316,9 +7409,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -8369,49 +7462,6 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -[[package]] -name = "gcemeta" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d460327b24cc34c86d53d60a90e9e6044817f7906ebd9baa5c3d0ee13e1ecf" -dependencies = [ - "bytes", - "hyper", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "gcloud-sdk" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a24376e7850e7864bb326debc5765a1dda4fc47603c22e2bc0ebf30ff59141b" -dependencies = [ - "async-trait", - "chrono", - "futures", - "gcemeta", - "hyper", - "jsonwebtoken 8.3.0", - "once_cell", - "prost 0.11.9", - "prost-types 0.11.9", - "reqwest", - "secret-vault-value", - "serde", - "serde_json", - "tokio", - "tonic 0.9.2", - "tower", - "tower-layer", - "tower-util", - "tracing", - "url", -] - [[package]] name = "gcp-bigquery-client" version = "0.13.0" @@ -8446,7 +7496,7 @@ dependencies = [ "aptos-network", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-core-types", "rand 0.7.3", "serde", @@ -8511,9 +7561,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -8524,19 +7574,19 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", "polyval", ] [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -8561,21 +7611,6 @@ dependencies = [ "url", ] -[[package]] -name = "git2" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" -dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - [[package]] name = "glob" version = "0.3.1" @@ -8589,10 +7624,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", - "bstr 1.9.0", + "bstr 1.9.1", "log", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -8620,14 +7655,14 @@ dependencies = [ [[package]] name = "goldenfile" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a67453a3b358bd8213aedafd4feed75eecab9fb04bed26ba6fdf94694be560" +checksum = "a0d5c44265baec620ea19c97b4ce9f068e28f8c3d7faccc483f02968b5e3c587" dependencies = [ "scopeguard", "similar-asserts", "tempfile", - "yansi 1.0.0-rc.1", + "yansi 1.0.1", ] [[package]] @@ -8637,7 +7672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "931bedb2264cb00f914b0a6a5c304e34865c34306632d3932e0951a073e4a67d" dependencies = [ "async-trait", - "base64 0.21.5", + "base64 0.21.7", "google-cloud-metadata", "google-cloud-token", "home", @@ -8652,33 +7687,6 @@ dependencies = [ "urlencoding", ] -[[package]] -name = "google-cloud-gax" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bdaaa4bc036e8318274d1b25f0f2265b3e95418b765fd1ea1c7ef938fd69bd" -dependencies = [ - "google-cloud-token", - "http", - "thiserror", - "tokio", - "tokio-retry", - "tonic 0.9.2", - "tower", - "tracing", -] - -[[package]] -name = "google-cloud-googleapis" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3b24a3f57be08afc02344e693afb55e48172c9c2ab86ff3fdb8efff550e4b9" -dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", - "tonic 0.9.2", -] - [[package]] name = "google-cloud-metadata" version = "0.3.2" @@ -8690,25 +7698,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "google-cloud-pubsub" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095b104502b6e1abbad9b9768af944b9202e032dbc7f0947d3c30d4191761071" -dependencies = [ - "async-channel 1.9.0", - "async-stream", - "google-cloud-auth", - "google-cloud-gax", - "google-cloud-googleapis", - "google-cloud-token", - "prost-types 0.11.9", - "thiserror", - "tokio", - "tokio-util 0.7.10", - "tracing", -] - [[package]] name = "google-cloud-storage" version = "0.13.1" @@ -8716,7 +7705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c57ca1d971d7c6f852c02eda4e87e88b1247b6ed8be9fa5b2768c68b0f2ca5" dependencies = [ "async-stream", - "base64 0.21.5", + "base64 0.21.7", "bytes", "futures-util", "google-cloud-auth", @@ -8774,11 +7763,11 @@ dependencies = [ [[package]] name = "guppy" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114a100a9aa9f4c468a7b9e96626cdab267bb652660d8408e8f6d56d4c310edd" +checksum = "34e99a7734579b834a076ef11789783c153c6eb5fb3520ed15bc41f483f0f317" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", "camino", "cargo_metadata 0.18.1", "cfg-if", @@ -8786,8 +7775,8 @@ dependencies = [ "fixedbitset 0.4.2", "guppy-summaries", "guppy-workspace-hack", - "indexmap 2.1.0", - "itertools 0.12.0", + "indexmap 2.2.6", + "itertools 0.12.1", "nested", "once_cell", "pathdiff", @@ -8834,26 +7823,27 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap 2.2.6", "slab", "tokio", - "tokio-util 0.7.10", + "tokio-util", "tracing", ] [[package]] name = "half" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ + "cfg-if", "crunchy", ] @@ -8898,7 +7888,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -8907,7 +7897,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", ] [[package]] @@ -8916,7 +7906,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", "allocator-api2", ] @@ -8926,7 +7916,7 @@ version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "byteorder", "flate2", "nom", @@ -8939,10 +7929,10 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "headers-core", - "http", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -8954,7 +7944,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.12", ] [[package]] @@ -8972,6 +7962,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -8983,9 +7979,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -9101,9 +8097,20 @@ checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -9117,7 +8124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] @@ -9148,7 +8155,7 @@ dependencies = [ "assert-json-diff", "async-object-pool", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "basic-cookies", "crossbeam-utils", "form_urlencoded", @@ -9193,13 +8200,13 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -9212,7 +8219,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http", + "http 0.2.12", "hyper", "log", "rustls 0.20.9", @@ -9228,7 +8235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.12", "hyper", "rustls 0.21.10", "tokio", @@ -9260,24 +8267,11 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "hyperlocal" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" -dependencies = [ - "futures-util", - "hex", - "hyper", - "pin-project 1.1.3", - "tokio", -] - [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -9302,17 +8296,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -9335,15 +8318,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.6", "same-file", "walkdir", "winapi-util", @@ -9365,9 +8348,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -9375,7 +8358,6 @@ dependencies = [ "exr", "gif", "jpeg-decoder", - "num-rational 0.4.1", "num-traits", "png", "qoi", @@ -9424,8 +8406,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -9458,8 +8440,8 @@ checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" dependencies = [ "anyhow", "proc-macro-hack", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -9469,8 +8451,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", ] [[package]] @@ -9486,9 +8468,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -9503,23 +8485,10 @@ checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ "console", "lazy_static", - "number_prefix 0.3.0", + "number_prefix", "regex", ] -[[package]] -name = "indicatif" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" -dependencies = [ - "console", - "instant", - "number_prefix 0.4.0", - "portable-atomic", - "unicode-width", -] - [[package]] name = "indoc" version = "1.0.9" @@ -9532,13 +8501,13 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ - "ahash 0.8.7", - "clap 4.4.12", + "ahash 0.8.11", + "clap 4.5.4", "crossbeam-channel", "crossbeam-utils", "dashmap", "env_logger", - "indexmap 2.1.0", + "indexmap 2.2.6", "is-terminal", "itoa", "log", @@ -9576,7 +8545,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "dashmap", "hashbrown 0.12.3", "once_cell", @@ -9598,7 +8567,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -9620,12 +8589,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.3", - "rustix 0.38.28", + "hermit-abi 0.3.9", + "libc", "windows-sys 0.52.0", ] @@ -9649,7 +8618,7 @@ dependencies = [ "encoding_rs", "event-listener 2.5.3", "futures-lite 1.13.0", - "http", + "http 0.2.12", "log", "mime", "once_cell", @@ -9682,18 +8651,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jemalloc-sys" @@ -9717,53 +8686,31 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" dependencies = [ "rayon", ] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json-patch" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" -dependencies = [ - "serde", - "serde_json", - "treediff", -] - -[[package]] -name = "jsonpath_lib" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" -dependencies = [ - "log", - "serde", - "serde_json", -] - [[package]] name = "jsonwebtoken" version = "7.2.0" @@ -9784,7 +8731,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "pem 1.1.1", "ring 0.16.20", "serde", @@ -9796,125 +8743,24 @@ dependencies = [ name = "jwt" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" -dependencies = [ - "base64 0.13.1", - "crypto-common", - "digest 0.10.7", - "hmac 0.12.1", - "serde", - "serde_json", - "sha2 0.10.8", -] - -[[package]] -name = "k8s-openapi" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f8de9873b904e74b3533f77493731ee26742418077503683db44e1b3c54aa5c" -dependencies = [ - "base64 0.13.1", - "bytes", - "chrono", - "serde", - "serde-value", - "serde_json", -] - -[[package]] -name = "kanal" -version = "0.1.0-pre8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05d55519627edaf7fd0f29981f6dc03fb52df3f5b257130eb8d0bf2801ea1d7" -dependencies = [ - "futures-core", - "lock_api", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "kube" -version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec231e9ec9e84789f9eb414d1ac40ce6c90d0517fb272a335b4233f2e272b1e" -dependencies = [ - "k8s-openapi", - "kube-client", - "kube-core", - "kube-derive", -] - -[[package]] -name = "kube-client" -version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95dddb1fcced906d79cdae530ff39079c2d3772b2d623088fdbebe610bfa8217" -dependencies = [ - "base64 0.13.1", - "bytes", - "chrono", - "dirs-next", - "either", - "futures", - "http", - "http-body", - "hyper", - "hyper-rustls 0.23.2", - "hyper-timeout", - "jsonpath_lib", - "k8s-openapi", - "kube-core", - "pem 1.1.1", - "pin-project 1.1.3", - "rustls 0.20.9", - "rustls-pemfile 0.2.1", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "thiserror", - "tokio", - "tokio-util 0.6.10", - "tower", - "tower-http 0.2.5", - "tracing", -] - -[[package]] -name = "kube-core" -version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52b6ab05d160691083430f6f431707a4e05b64903f2ffa0095ee5efde759117" -dependencies = [ - "chrono", - "form_urlencoded", - "http", - "json-patch", - "k8s-openapi", - "once_cell", +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest 0.10.7", + "hmac 0.12.1", "serde", "serde_json", - "thiserror", + "sha2 0.10.8", ] [[package]] -name = "kube-derive" -version = "0.65.0" +name = "keccak" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98ff3d647085c6c025083efad0435890867f4bea042fc62d408ab3aeb1cdf66" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ - "darling 0.13.4", - "proc-macro2 1.0.75", - "quote 1.0.35", - "serde_json", - "syn 1.0.109", + "cpufeatures", ] [[package]] @@ -9928,33 +8774,33 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.12" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", - "diff", "ena", - "is-terminal", - "itertools 0.10.5", + "itertools 0.11.0", "lalrpop-util", "petgraph 0.6.4", + "pico-args", "regex", - "regex-syntax 0.6.29", + "regex-syntax 0.8.3", "string_cache", "term", "tiny-keccak", "unicode-xid 0.2.4", + "walkdir", ] [[package]] name = "lalrpop-util" -version = "0.19.12" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex", + "regex-automata 0.4.6", ] [[package]] @@ -10055,9 +8901,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libfuzzer-sys" @@ -10078,20 +8924,18 @@ checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", - "libssh2-sys", "libz-sys", - "openssl-sys", "pkg-config", ] [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.5", ] [[package]] @@ -10102,9 +8946,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.9+1.58.0" +version = "0.1.10+1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" dependencies = [ "cc", "libc", @@ -10112,13 +8956,12 @@ dependencies = [ [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", ] [[package]] @@ -10185,20 +9028,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "libssh2-sys" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libtest-mimic" version = "0.5.2" @@ -10212,9 +9041,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", "libc", @@ -10242,16 +9071,16 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "listener" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.12", + "clap 4.5.4", "tokio", ] @@ -10267,9 +9096,9 @@ dependencies = [ [[package]] name = "lodepng" -version = "3.9.2" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f56ff9bcd5721ab172b73eac8a7d4e9439f47a98581e666178dbe7df97e13" +checksum = "a42d298694b14401847de29abd44adf278b42e989e516deac7b72018400002d8" dependencies = [ "crc32fast", "fallible_collections", @@ -10280,9 +9109,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "serde", "value-bag", @@ -10360,15 +9189,6 @@ dependencies = [ "libc", ] -[[package]] -name = "macros" -version = "0.1.0" -source = "git+https://github.com/niroco/diesel_async_migrations?rev=11f331b73c5cfcc894380074f748d8fda710ac12#11f331b73c5cfcc894380074f748d8fda710ac12" -dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", -] - [[package]] name = "maplit" version = "1.0.2" @@ -10390,12 +9210,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.5.0" @@ -10408,31 +9222,11 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "matrixmultiply" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" -dependencies = [ - "autocfg", - "rawpointer", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest 0.10.7", -] - [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -10481,8 +9275,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", ] [[package]] @@ -10524,9 +9318,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -10534,9 +9328,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -10572,8 +9366,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -10597,7 +9391,7 @@ dependencies = [ "anyhow", "aptos-framework", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-binary-format", ] @@ -10633,7 +9427,7 @@ name = "move-analyzer" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "crossbeam", "derivative", @@ -10745,7 +9539,7 @@ name = "move-bytecode-viewer" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "crossterm 0.26.1", "move-binary-format", "move-bytecode-source-map", @@ -10763,7 +9557,7 @@ dependencies = [ "anyhow", "bcs 0.1.4", "bytes", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "colored", "datatest-stable", @@ -10826,7 +9620,7 @@ version = "0.0.1" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "datatest-stable", "difference", @@ -10865,7 +9659,7 @@ dependencies = [ "abstract-domain-derive", "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "codespan", "codespan-reporting", "datatest-stable", @@ -10887,7 +9681,7 @@ dependencies = [ "move-stackless-bytecode", "move-stdlib", "move-symbol-pool", - "num 0.4.1", + "num 0.4.2", "once_cell", "petgraph 0.6.4", "serde", @@ -10915,7 +9709,7 @@ dependencies = [ "ethnum", "hashbrown 0.14.3", "hex", - "num 0.4.1", + "num 0.4.2", "once_cell", "primitive-types 0.10.1", "proptest", @@ -10936,7 +9730,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "codespan", "colored", "move-binary-format", @@ -10954,7 +9748,7 @@ name = "move-disassembler" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "colored", "move-binary-format", "move-bytecode-source-map", @@ -10981,7 +9775,7 @@ dependencies = [ "move-model", "move-prover", "move-prover-test-utils", - "num 0.4.1", + "num 0.4.2", "once_cell", "regex", "serde", @@ -11028,7 +9822,7 @@ name = "move-explain" version = "0.1.0" dependencies = [ "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-command-line-common", "move-core-types", ] @@ -11039,7 +9833,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "move-binary-format", "move-bytecode-source-map", "move-bytecode-verifier", @@ -11123,7 +9917,7 @@ dependencies = [ "move-ir-types", "move-prover-test-utils", "move-symbol-pool", - "num 0.4.1", + "num 0.4.2", "num-traits", "once_cell", "regex", @@ -11137,7 +9931,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.4", "colored", "datatest-stable", "dirs-next", @@ -11178,7 +9972,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.12", + "clap 4.5.4", "codespan", "codespan-reporting", "datatest-stable", @@ -11200,7 +9994,7 @@ dependencies = [ "move-prover-bytecode-pipeline", "move-prover-test-utils", "move-stackless-bytecode", - "num 0.4.1", + "num 0.4.2", "once_cell", "pretty", "rand 0.8.5", @@ -11232,7 +10026,7 @@ dependencies = [ "move-model", "move-prover-bytecode-pipeline", "move-stackless-bytecode", - "num 0.4.1", + "num 0.4.2", "once_cell", "pretty", "rand 0.8.5", @@ -11251,7 +10045,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.4.12", + "clap 4.5.4", "codespan", "codespan-reporting", "datatest-stable", @@ -11264,7 +10058,7 @@ dependencies = [ "move-model", "move-stackless-bytecode", "move-stackless-bytecode-test-utils", - "num 0.4.1", + "num 0.4.2", "once_cell", "pretty", "rand 0.8.5", @@ -11324,7 +10118,7 @@ dependencies = [ "move-ir-to-bytecode", "move-model", "move-stackless-bytecode-test-utils", - "num 0.4.1", + "num 0.4.2", "once_cell", "paste", "petgraph 0.5.1", @@ -11411,7 +10205,7 @@ version = "0.1.0" dependencies = [ "anyhow", "atty", - "clap 4.4.12", + "clap 4.5.4", "codespan", "codespan-reporting", "datatest-stable", @@ -11433,7 +10227,7 @@ dependencies = [ "move-prover-test-utils", "move-stackless-bytecode", "move-stdlib", - "num 0.4.1", + "num 0.4.2", "once_cell", "pretty", "primitive-types 0.10.1", @@ -11453,7 +10247,7 @@ name = "move-transactional-test-runner" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "colored", "datatest-stable", "difference", @@ -11489,7 +10283,7 @@ dependencies = [ "anyhow", "better_any", "bytes", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "colored", "datatest-stable", @@ -11625,7 +10419,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 0.2.12", "httparse", "log", "memchr", @@ -11635,33 +10429,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "nalgebra" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" -dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex 0.4.4", - "num-rational 0.4.1", - "num-traits", - "simba", - "typenum", -] - -[[package]] -name = "nalgebra-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" -dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", -] - [[package]] name = "named-lock" version = "0.2.0" @@ -11702,9 +10469,9 @@ checksum = "ca2b420f638f07fe83056b55ea190bb815f609ec5a35e7017884a10f78839c9e" [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" @@ -11723,8 +10490,20 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -11767,9 +10546,9 @@ dependencies = [ [[package]] name = "ntest" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8ec6d2b73d45307e926f5af46809768581044384637af6b3f3fe7c3c88f512" +checksum = "41cd16a2e6992865367e7ca50cd6953d09daaed93641421168733a1274afadd6" dependencies = [ "ntest_test_cases", "ntest_timeout", @@ -11777,24 +10556,24 @@ dependencies = [ [[package]] name = "ntest_test_cases" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22" +checksum = "197eff6c12b80ff5de6173e438fa3c1340a9e708118c1626e690f65aee1e5332" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "ntest_timeout" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066b468120587a402f0b47d8f80035c921f6a46f8209efd0632a89a16f5188a4" +checksum = "ef492b5cf80f90c050b287e747228a1fa6517e9d754f364b5a7e0e038e49a25f" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro-crate 2.0.2", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -11833,12 +10612,12 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint 0.4.4", - "num-complex 0.4.4", + "num-complex 0.4.5", "num-integer", "num-iter", "num-rational 0.4.1", @@ -11907,21 +10686,27 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -11937,19 +10722,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -11982,9 +10766,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -11996,15 +10780,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -12015,12 +10799,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "number_range" version = "0.3.2" @@ -12029,7 +10807,7 @@ checksum = "60080faccd4ca50ad0b801b2be686136376b13f691f6eac84817e40973b2e1bb" dependencies = [ "anyhow", "itertools 0.10.5", - "num 0.4.1", + "num 0.4.2", ] [[package]] @@ -12061,17 +10839,17 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -12086,9 +10864,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -12099,9 +10877,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -12109,21 +10887,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-float" version = "3.9.2" @@ -12167,8 +10930,8 @@ checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -12180,8 +10943,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -12262,8 +11025,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -12273,9 +11036,9 @@ version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 2.0.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro-crate 2.0.2", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -12381,11 +11144,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "ciborium", "coset", "data-encoding", - "indexmap 2.1.0", + "indexmap 2.2.6", "rand 0.8.5", "serde", "serde_json", @@ -12409,16 +11172,6 @@ dependencies = [ "camino", ] -[[package]] -name = "pathsearch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da983bc5e582ab17179c190b4b66c7d76c5943a69c6d34df2a2b6bf8a2977b05" -dependencies = [ - "anyhow", - "libc", -] - [[package]] name = "pbjson" version = "0.5.1" @@ -12490,9 +11243,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -12501,9 +11254,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -12511,22 +11264,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -12550,7 +11303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.1.0", + "indexmap 2.2.6", ] [[package]] @@ -12601,50 +11354,36 @@ dependencies = [ ] [[package]] -name = "pin-project" -version = "0.4.30" +name = "pico-args" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" -dependencies = [ - "pin-project-internal 0.4.30", -] +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal 1.1.3", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.30" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", + "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -12659,7 +11398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", ] @@ -12680,7 +11419,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.8", + "der 0.7.9", "pkcs8 0.10.2", "spki 0.7.3", ] @@ -12702,21 +11441,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", + "der 0.7.9", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" - -[[package]] -name = "platforms" -version = "3.4.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -12748,9 +11481,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -12769,10 +11502,10 @@ dependencies = [ "async-trait", "bytes", "chrono", - "cookie 0.17.0", + "cookie", "futures-util", "headers", - "http", + "http 0.2.12", "hyper", "mime", "multer", @@ -12795,7 +11528,7 @@ dependencies = [ "tokio", "tokio-rustls 0.24.1", "tokio-stream", - "tokio-util 0.7.10", + "tokio-util", "tracing", "wildmatch", ] @@ -12806,10 +11539,10 @@ version = "1.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" dependencies = [ - "proc-macro-crate 2.0.1", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro-crate 2.0.2", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -12831,7 +11564,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "serde_yaml 0.9.30", + "serde_yaml 0.9.34+deprecated", "thiserror", "tokio", "url", @@ -12844,12 +11577,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "274cf13f710999977a3c1e396c2a5000d104075a7127ce6470fbdae4706be621" dependencies = [ "darling 0.14.4", - "http", + "http 0.2.12", "indexmap 1.9.3", "mime", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "regex", "syn 1.0.109", "thiserror", @@ -12873,27 +11606,28 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi 0.3.9", "pin-project-lite", - "rustix 0.38.28", + "rustix 0.38.32", "tracing", "windows-sys 0.52.0", ] [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", "universal-hash", ] @@ -12912,17 +11646,11 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597c3287a549da151aca6ada2795ecde089c7527bd5093114e8e0e1c3f0e52b1" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] -[[package]] -name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - [[package]] name = "poseidon-ark" version = "0.0.1" @@ -12933,48 +11661,6 @@ dependencies = [ "ark-std", ] -[[package]] -name = "postgres-native-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" -dependencies = [ - "futures", - "native-tls", - "tokio", - "tokio-native-tls", - "tokio-postgres", -] - -[[package]] -name = "postgres-protocol" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" -dependencies = [ - "base64 0.21.5", - "byteorder", - "bytes", - "fallible-iterator", - "hmac 0.12.1", - "md-5", - "memchr", - "rand 0.8.5", - "sha2 0.10.8", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -13087,12 +11773,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ - "proc-macro2 1.0.75", - "syn 2.0.47", + "proc-macro2 1.0.81", + "syn 2.0.60", ] [[package]] @@ -13140,7 +11826,7 @@ dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", "impl-rlp", - "scale-info 2.10.0", + "scale-info 2.11.2", "uint", ] @@ -13156,9 +11842,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ "toml_datetime", "toml_edit 0.20.2", @@ -13171,8 +11857,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", "version_check", ] @@ -13183,89 +11869,39 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "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-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "processor" -version = "1.0.0" -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352#d44b2d209f57872ac593299c34751a5531b51352" -dependencies = [ - "ahash 0.8.7", - "anyhow", - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352)", - "aptos-protos 1.3.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=5d1cefad0ea0d37fb08e9aec7847080745c83f9c)", - "async-trait", - "base64 0.13.1", - "bcs 0.1.4", - "bigdecimal", - "chrono", - "clap 4.4.12", - "diesel", - "diesel-async", - "diesel_async_migrations", - "diesel_migrations", - "enum_dispatch", - "field_count", - "futures", - "futures-util", - "gcloud-sdk", - "google-cloud-googleapis", - "google-cloud-pubsub", - "hex", - "kanal", - "native-tls", - "num_cpus", - "once_cell", - "postgres-native-tls", - "prometheus", - "prost 0.12.3", - "prost-types 0.12.3", - "regex", - "serde", - "serde_json", - "server-framework", - "sha2 0.9.9", - "sha3 0.9.1", - "strum 0.24.1", - "tokio", - "tokio-postgres", - "tonic 0.10.2", - "tracing", - "unescape", - "url", +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", ] [[package]] @@ -13303,19 +11939,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "prometheus-http-query" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae2f6a3f14ff35c16b51ac796d1dc73c15ad6472c48836c6c467f6d52266648" -dependencies = [ - "reqwest", - "serde", - "serde_json", - "time", - "url", -] - [[package]] name = "prometheus-parse" version = "0.2.5" @@ -13323,7 +11946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "811031bea65e5a401fb2e1f37d802cca6601e204ac463809a3189352d13b78a5" dependencies = [ "chrono", - "itertools 0.12.0", + "itertools 0.12.1", "once_cell", "regex", ] @@ -13336,13 +11959,13 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.1", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "rusty-fork", "tempfile", "unarray", @@ -13365,8 +11988,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -13382,12 +12005,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", - "prost-derive 0.12.3", + "prost-derive 0.12.4", ] [[package]] @@ -13398,22 +12021,22 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.11.0", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "itertools 0.12.1", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -13427,11 +12050,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ - "prost 0.12.3", + "prost 0.12.4", ] [[package]] @@ -13465,7 +12088,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.4.12", + "clap 4.5.4", "codespan-reporting", "hex", "itertools 0.10.5", @@ -13476,7 +12099,7 @@ dependencies = [ "move-prover-boogie-backend", "move-prover-bytecode-pipeline", "move-stackless-bytecode", - "num 0.4.1", + "num 0.4.2", "plotters", "serde", "serde_json", @@ -13508,11 +12131,11 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "memchr", "unicase", ] @@ -13597,11 +12220,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.75", + "proc-macro2 1.0.81", ] [[package]] @@ -13687,7 +12310,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.14", ] [[package]] @@ -13726,16 +12349,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "random_word" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0f7171155590e912ab907550240a5764c665388ab0a1e46d783a493e816ff3" -dependencies = [ - "once_cell", - "rand 0.8.5", -] - [[package]] name = "raw-cpuid" version = "10.7.0" @@ -13745,17 +12358,11 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -13763,9 +12370,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -13789,7 +12396,7 @@ dependencies = [ "ryu", "sha1_smol", "tokio", - "tokio-util 0.7.10", + "tokio-util", "url", ] @@ -13823,11 +12430,11 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.14", "libredox", "thiserror", ] @@ -13847,21 +12454,21 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -13875,13 +12482,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -13892,26 +12499,25 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "async-compression", - "base64 0.21.5", + "base64 0.21.7", "bytes", - "cookie 0.16.2", + "cookie", "cookie_store", "encoding_rs", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -13930,30 +12536,31 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util 0.7.10", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots", "winreg", ] [[package]] name = "reqwest-middleware" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a3e86aa6053e59030e7ce2d2a3b258dd08fc2d337d52f73f6cb480f5858690" +checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" dependencies = [ "anyhow", "async-trait", - "http", + "http 0.2.12", "reqwest", "serde", "task-local-extensions", @@ -13970,8 +12577,8 @@ dependencies = [ "async-trait", "chrono", "futures", - "getrandom 0.2.11", - "http", + "getrandom 0.2.14", + "http 0.2.12", "hyper", "parking_lot 0.11.2", "reqwest", @@ -14012,9 +12619,9 @@ dependencies = [ [[package]] name = "rfc7239" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" +checksum = "b106a85eeb5b0336d16d6a20eab857f92861d4fbb1eb9a239866fb98fb6a1063" dependencies = [ "uncased", ] @@ -14045,16 +12652,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if", + "getrandom 0.2.14", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -14083,8 +12691,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -14185,8 +12793,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" dependencies = [ "cfg-if", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustc_version", "syn 1.0.109", ] @@ -14248,14 +12856,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] @@ -14278,19 +12886,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", "subtle", @@ -14322,15 +12930,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64 0.13.1", -] - [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -14346,7 +12945,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -14365,23 +12964,13 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" -[[package]] -name = "rustls-webpki" -version = "0.100.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -14391,16 +12980,16 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rusty-fork" @@ -14416,18 +13005,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "safe_arch" -version = "0.7.1" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" -dependencies = [ - "bytemuck", -] +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -14453,15 +13033,15 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" dependencies = [ "bitvec 1.0.1", "cfg-if", "derive_more", "parity-scale-codec 3.6.9", - "scale-info-derive 2.10.0", + "scale-info-derive 2.11.2", ] [[package]] @@ -14471,20 +13051,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -14506,16 +13086,6 @@ dependencies = [ "parking_lot 0.12.1", ] -[[package]] -name = "scoped-futures" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" -dependencies = [ - "cfg-if", - "pin-utils", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -14534,7 +13104,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -14551,7 +13121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.7.8", + "der 0.7.9", "generic-array 0.14.7", "pkcs8 0.10.2", "serdect", @@ -14559,24 +13129,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secret-vault-value" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f8cfb86d2019f64a4cfb49e499f401f406fbec946c1ffeea9d0504284347de" -dependencies = [ - "prost 0.12.3", - "prost-types 0.12.3", - "serde", - "serde_json", - "zeroize", -] - [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -14587,50 +13144,19 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "self-replace" -version = "1.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525db198616b2bcd0f245daf7bfd8130222f7ee6af9ff9984c19a61bf1160c55" -dependencies = [ - "fastrand 1.9.0", - "tempfile", - "windows-sys 0.48.0", -] - -[[package]] -name = "self_update" -version = "0.39.0" -source = "git+https://github.com/banool/self_update.git?rev=8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b#8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b" -dependencies = [ - "hyper", - "indicatif 0.17.7", - "log", - "quick-xml 0.23.1", - "regex", - "reqwest", - "self-replace", - "semver", - "serde_json", - "tempfile", - "urlencoding", - "zip", - "zipsign-api", -] - [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -14640,7 +13166,7 @@ name = "sender" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.12", + "clap 4.5.4", "event-listener 2.5.3", "quanta", "tokio", @@ -14648,9 +13174,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] @@ -14702,16 +13228,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float 2.10.1", - "serde", -] - [[package]] name = "serde_bytes" version = "0.11.14" @@ -14727,28 +13243,28 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ - "half 1.8.2", + "half 1.8.3", "serde", ] [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.110" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -14777,13 +13293,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -14809,16 +13325,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -14826,14 +13343,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ - "darling 0.20.3", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "darling 0.20.8", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -14850,11 +13367,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -14881,27 +13398,6 @@ dependencies = [ "proptest-derive 0.3.0", ] -[[package]] -name = "server-framework" -version = "1.0.0" -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=d44b2d209f57872ac593299c34751a5531b51352#d44b2d209f57872ac593299c34751a5531b51352" -dependencies = [ - "anyhow", - "async-trait", - "backtrace", - "clap 4.4.12", - "futures", - "prometheus", - "serde", - "serde_yaml 0.8.26", - "tempfile", - "tokio", - "toml 0.7.8", - "tracing", - "tracing-subscriber 0.3.18", - "warp", -] - [[package]] name = "sha1" version = "0.10.6" @@ -14929,7 +13425,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -14978,7 +13474,7 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -14998,7 +13494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c0ea0c68418544f725eba5401a5b965a2263254c92458d04aeae74e9d88ff4e" dependencies = [ "const_format", - "git2 0.15.0", + "git2", "is_debug", "time", "tzdb", @@ -15021,9 +13517,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" @@ -15071,19 +13567,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simba" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" -dependencies = [ - "approx", - "num-complex 0.4.4", - "num-traits", - "paste", - "wide", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -15092,9 +13575,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" dependencies = [ "bstr 0.2.17", "unicode-segmentation", @@ -15197,96 +13680,32 @@ dependencies = [ [[package]] name = "sluice" version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel 1.9.0", - "futures-core", - "futures-io", -] - -[[package]] -name = "smallbitvec" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ce4f9dc4a41b4c3476cc925f1efb11b66df373a8fde5d4b8915fa91b5d995e" - -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - -[[package]] -name = "smoke-test" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos", - "aptos-backup-cli", - "aptos-bitvec", - "aptos-cached-packages", - "aptos-config", - "aptos-consensus", - "aptos-crypto", - "aptos-db", - "aptos-debugger", - "aptos-dkg", - "aptos-faucet-core", - "aptos-forge", - "aptos-framework", - "aptos-gas-algebra", - "aptos-gas-schedule", - "aptos-genesis", - "aptos-global-constants", - "aptos-indexer", - "aptos-infallible", - "aptos-inspection-service", - "aptos-keygen", - "aptos-logger", - "aptos-move-debugger", - "aptos-release-builder", - "aptos-rest-client", - "aptos-rosetta", - "aptos-sdk", - "aptos-secure-storage", - "aptos-storage-interface", - "aptos-temppath", - "aptos-time-service", - "aptos-types", - "aptos-vault-client", - "aptos-vm", - "aptos-vm-genesis", - "async-trait", - "base64 0.13.1", - "bcs 0.1.4", - "diesel", - "digest 0.9.0", - "futures", - "hex", - "hyper", - "move-core-types", - "num-traits", - "num_cpus", - "once_cell", - "proptest", - "rand 0.7.3", - "regex", - "reqwest", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tokio", - "url", - "walkdir", +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel 1.9.0", + "futures-core", + "futures-io", ] +[[package]] +name = "smallbitvec" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3fc564a4b53fd1e8589628efafe57602d91bde78be18186b5f61e8faea470" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "smt2parser" version = "0.1.0" @@ -15316,8 +13735,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -15333,12 +13752,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -15373,7 +13792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.8", + "der 0.7.9", ] [[package]] @@ -15417,17 +13836,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "stringprep" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" -dependencies = [ - "finl_unicode", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "strsim" version = "0.8.0" @@ -15440,6 +13848,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "structopt" version = "0.3.26" @@ -15459,8 +13873,8 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -15469,9 +13883,6 @@ name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros 0.24.3", -] [[package]] name = "strum" @@ -15489,8 +13900,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustversion", "syn 1.0.109", ] @@ -15502,10 +13913,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustversion", - "syn 2.0.47", + "syn 2.0.60", ] [[package]] @@ -15560,19 +13971,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.47" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "unicode-ident", ] @@ -15643,15 +14054,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "target-spec" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b81540ee78bd9de9f7dca2378f264cf1f4193da6e2d09b54c0d595131a48f1" +checksum = "36a8e795b1824524d13cdf04f73cf8b4f244ce86c96b4d2a83a6ca1a753d2752" dependencies = [ "cfg-expr", "guppy-workspace-hack", @@ -15671,14 +14082,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall 0.4.1", - "rustix 0.38.28", + "fastrand 2.0.2", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -15746,9 +14156,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -15757,9 +14167,9 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", "test-case-core", ] @@ -15767,9 +14177,9 @@ dependencies = [ name = "test-generation" version = "0.1.0" dependencies = [ - "clap 4.4.12", + "clap 4.5.4", "crossbeam-channel", - "getrandom 0.2.11", + "getrandom 0.2.14", "hex", "itertools 0.10.5", "module-generation", @@ -15793,7 +14203,7 @@ name = "testdiff" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.4", "itertools 0.10.5", "once_cell", "walkdir", @@ -15831,35 +14241,35 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -15876,9 +14286,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", "jpeg-decoder", @@ -15887,13 +14297,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -15909,10 +14320,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -15971,9 +14383,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -15983,7 +14395,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -16005,9 +14417,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -16032,39 +14444,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-postgres" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot 0.12.1", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand 0.8.5", - "socket2 0.5.5", - "tokio", - "tokio-util 0.7.10", - "whoami", -] - [[package]] name = "tokio-retry" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" dependencies = [ - "pin-project 1.1.3", + "pin-project", "rand 0.8.5", "tokio", ] @@ -16096,7 +14482,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.3", + "rustls 0.22.4", "rustls-pki-types", "tokio", ] @@ -16113,9 +14499,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -16124,9 +14510,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", @@ -16137,9 +14523,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -16147,20 +14533,6 @@ dependencies = [ "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.10" @@ -16198,6 +14570,18 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -16225,7 +14609,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -16238,7 +14622,9 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -16249,59 +14635,21 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ - "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.5", + "base64 0.21.7", "bytes", - "flate2", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", "percent-encoding", - "pin-project 1.1.3", + "pin-project", "prost 0.11.9", - "rustls-native-certs 0.6.3", - "rustls-pemfile 1.0.4", - "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", - "webpki-roots 0.23.1", -] - -[[package]] -name = "tonic" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.6.20", - "base64 0.21.5", - "bytes", - "flate2", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project 1.1.3", - "prost 0.12.3", - "rustls 0.21.10", - "rustls-native-certs 0.6.3", - "rustls-pemfile 1.0.4", "tokio", - "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -16318,17 +14666,17 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.5", + "base64 0.21.7", "bytes", "flate2", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", "percent-encoding", - "pin-project 1.1.3", - "prost 0.12.3", + "pin-project", + "prost 0.12.4", "rustls-native-certs 0.7.0", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -16348,8 +14696,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "548c227bd5c0fae5925812c4ec6c66ffcfced23ea370cb823f4d18f0fc1cb6a7" dependencies = [ - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.4", + "prost-types 0.12.4", "tokio", "tokio-stream", "tonic 0.11.0", @@ -16364,32 +14712,12 @@ dependencies = [ "futures-core", "futures-util", "indexmap 1.9.3", - "pin-project 1.1.3", + "pin-project", "pin-project-lite", "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.10", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -16405,7 +14733,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http", + "http 0.2.12", "http-body", "http-range-header", "pin-project-lite", @@ -16426,26 +14754,14 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" -[[package]] -name = "tower-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" -dependencies = [ - "futures-core", - "futures-util", - "pin-project 0.4.30", - "tower-service", -] - [[package]] name = "trace" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -16467,9 +14783,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -16488,7 +14804,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 1.1.3", + "pin-project", "tracing", ] @@ -16543,15 +14859,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "treediff" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" -dependencies = [ - "serde_json", -] - [[package]] name = "trie-root" version = "0.18.0" @@ -16589,17 +14896,17 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.87" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6b2fc10a33500845468aa7c06567a9226581b24f03c6f891e6dda2dc9a1c8b" +checksum = "8ad7eb6319ebadebca3dacf1f85a93bc54b73dd81b9036795f73de7ddfe27d5a" dependencies = [ - "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", + "toml 0.8.2", ] [[package]] @@ -16617,14 +14924,14 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.1.0", "httparse", "log", "rand 0.8.5", @@ -16648,9 +14955,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typeshare" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44d1a2f454cb35fbe05b218c410792697e76bd868f48d3a418f2cd1a7d527d6" +checksum = "99877a66770b25072a67101e80790fd7fe9ed9eff8c69ffb81e9bf5cbea7733f" dependencies = [ "chrono", "serde", @@ -16660,12 +14967,12 @@ dependencies = [ [[package]] name = "typeshare-annotation" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9" +checksum = "ecce25dea8aeaadc44909f4c1226d22d84512fccd07d22447ecbad176bc09545" dependencies = [ - "quote 1.0.35", - "syn 1.0.109", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -16691,9 +14998,9 @@ dependencies = [ [[package]] name = "tzdb_data" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629555d2921f3f0dc0de98699415a8b2b61dfcd3a0b082a327f7ed748bbb2b76" +checksum = "d1889fdffac09d65c1d95c42d5202e9b21ad8c758f426e9fe09088817ea998d6" dependencies = [ "tz-rs", ] @@ -16724,19 +15031,13 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "uncased" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" dependencies = [ "version_check", ] -[[package]] -name = "unescape" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" - [[package]] name = "unic-char-property" version = "0.9.0" @@ -16798,9 +15099,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -16816,18 +15117,18 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" @@ -16859,9 +15160,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -16912,16 +15213,15 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utcnow" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d49a98e3bbd9b73084a7b15f96c5b2136d5a2e2799b99d19a2774d8519d1f" +checksum = "493ace370ee8579788f83a4f0eef89183c527b7551b4ad71364fac10371872b7" dependencies = [ - "autocfg", "const_fn", "errno", "js-sys", "libc", - "rustix 0.38.28", + "rustix 0.38.32", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", "winapi 0.3.9", @@ -16941,11 +15241,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.14", "serde", ] @@ -16957,9 +15257,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" [[package]] name = "variant_count" @@ -16967,7 +15267,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "quote 1.0.35", + "quote 1.0.36", "syn 1.0.109", ] @@ -16983,12 +15283,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - [[package]] name = "version_check" version = "0.9.4" @@ -17012,9 +15306,9 @@ checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -17031,32 +15325,31 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", - "http", + "http 0.2.12", "hyper", "log", "mime", "mime_guess", "multer", "percent-encoding", - "pin-project 1.1.3", - "rustls-pemfile 1.0.4", + "pin-project", + "rustls-pemfile 2.1.2", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", + "tokio-rustls 0.25.0", "tokio-tungstenite", - "tokio-util 0.7.10", + "tokio-util", "tower-service", "tracing", ] @@ -17093,11 +15386,17 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -17105,24 +15404,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -17132,38 +15431,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.35", + "quote 1.0.36", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -17189,9 +15488,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -17203,30 +15502,21 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - -[[package]] -name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -17237,29 +15527,20 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.28", + "rustix 0.38.32", ] [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "wasm-bindgen", + "redox_syscall 0.4.1", + "wasite", "web-sys", ] -[[package]] -name = "wide" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "widestring" version = "0.5.1" @@ -17268,9 +15549,9 @@ checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "wildmatch" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb" +checksum = "939e59c1bc731542357fdaad98b209ef78c8743d652bb61439d16b16a79eb025" [[package]] name = "winapi" @@ -17315,7 +15596,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -17342,7 +15623,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -17377,17 +15658,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 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", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -17404,9 +15686,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -17422,9 +15704,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -17440,9 +15722,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -17458,9 +15746,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -17476,9 +15764,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -17494,9 +15782,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -17512,15 +15800,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.32" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -17555,20 +15843,20 @@ name = "x25519-dalek" version = "1.2.0" source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" dependencies = [ - "curve25519-dalek 3.2.0", + "curve25519-dalek", "rand_core 0.5.1", "zeroize", ] [[package]] name = "xattr" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.12", - "rustix 0.38.28", + "linux-raw-sys 0.4.13", + "rustix 0.38.32", ] [[package]] @@ -17588,9 +15876,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yansi" -version = "1.0.0-rc.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yup-oauth2" @@ -17602,7 +15890,7 @@ dependencies = [ "async-trait", "base64 0.13.1", "futures", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.23.2", "itertools 0.10.5", @@ -17645,9 +15933,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -17665,32 +15953,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "byteorder", - "crc32fast", - "crossbeam-utils", - "flate2", - "time", -] - -[[package]] -name = "zipsign-api" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea" -dependencies = [ - "ed25519-dalek 2.1.1", - "thiserror", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -17714,9 +15979,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 562b163b8fcbd..d9bf2a1150463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,23 +9,19 @@ members = [ "aptos-move/aptos-abstract-gas-usage", "aptos-move/aptos-aggregator", "aptos-move/aptos-debugger", - "aptos-move/aptos-e2e-comparison-testing", "aptos-move/aptos-gas-algebra", - "aptos-move/aptos-gas-calibration", "aptos-move/aptos-gas-meter", "aptos-move/aptos-gas-profiling", "aptos-move/aptos-gas-schedule", "aptos-move/aptos-gas-schedule-updator", "aptos-move/aptos-memory-usage-tracker", "aptos-move/aptos-native-interface", - "aptos-move/aptos-release-builder", "aptos-move/aptos-resource-viewer", "aptos-move/aptos-sdk-builder", "aptos-move/aptos-transaction-benchmarks", "aptos-move/aptos-transactional-test-harness", "aptos-move/aptos-validator-interface", "aptos-move/aptos-vm", - "aptos-move/aptos-vm-benchmarks", "aptos-move/aptos-vm-logging", "aptos-move/aptos-vm-profiling", "aptos-move/aptos-vm-types", @@ -42,14 +38,12 @@ members = [ "aptos-move/package-builder", "aptos-move/vm-genesis", "aptos-move/writeset-transaction-generator", - "aptos-node", "aptos-utils", "config", "config/global-constants", "consensus", "consensus/consensus-types", "consensus/safety-rules", - "crates/aptos", "crates/aptos-admin-service", "crates/aptos-api-tester", "crates/aptos-bcs-utils", @@ -71,7 +65,6 @@ members = [ "crates/aptos-github-client", "crates/aptos-id-generator", "crates/aptos-infallible", - "crates/aptos-inspection-service", "crates/aptos-jwk-consensus", "crates/aptos-keygen", "crates/aptos-ledger", @@ -87,12 +80,8 @@ members = [ "crates/aptos-rate-limiter", "crates/aptos-rest-client", "crates/aptos-retrier", - "crates/aptos-rosetta", - "crates/aptos-rosetta-cli", "crates/aptos-runtimes", "crates/aptos-speculative-state-helper", - "crates/aptos-telemetry", - "crates/aptos-telemetry-service", "crates/aptos-temppath", "crates/aptos-time-service", "crates/aptos-warp-webserver", @@ -117,7 +106,6 @@ members = [ "ecosystem/indexer-grpc/indexer-grpc-data-service", "ecosystem/indexer-grpc/indexer-grpc-file-store", "ecosystem/indexer-grpc/indexer-grpc-fullnode", - "ecosystem/indexer-grpc/indexer-grpc-integration-tests", "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/indexer-grpc-table-info", "ecosystem/indexer-grpc/indexer-grpc-utils", @@ -173,14 +161,10 @@ members = [ "testsuite/dos/http_test", "testsuite/dos/listener", "testsuite/dos/sender", - "testsuite/forge", - "testsuite/forge-cli", "testsuite/fuzzer", "testsuite/fuzzer/fuzz", "testsuite/generate-format", "testsuite/module-publish", - "testsuite/smoke-test", - "testsuite/testcases", # third_party/move "third_party/move/extensions/async/move-async-vm", "third_party/move/extensions/move-table-extension", @@ -247,14 +231,11 @@ members = [ # # For more, see the "Conditional compilation for tests" section in documentation/coding_guidelines.md. default-members = [ - "aptos-node", "consensus/safety-rules", - "crates/aptos", "crates/aptos-debugger", "crates/aptos-faucet/service", "crates/aptos-keygen", "crates/aptos-rate-limiter", - "crates/aptos-rosetta", "crates/transaction-emitter", "aptos-move/framework", "storage/backup/backup-cli", @@ -275,7 +256,6 @@ rust-version = "1.75.0" [workspace.dependencies] # Internal crate dependencies. # Please do not add any test features here: they should be declared by the individual crate. -aptos = { path = "crates/aptos" } aptos-accumulator = { path = "storage/accumulator" } aptos-admin-service = { path = "crates/aptos-admin-service" } aptos-aggregator = { path = "aptos-move/aptos-aggregator" } @@ -325,13 +305,11 @@ aptos-faucet-core = { path = "crates/aptos-faucet/core" } aptos-faucet-service = { path = "crates/aptos-faucet/service" } aptos-faucet-metrics-server = { path = "crates/aptos-faucet/metrics-server" } aptos-fallible = { path = "crates/fallible" } -aptos-forge = { path = "testsuite/forge" } aptos-framework = { path = "aptos-move/framework" } fuzzer = { path = "testsuite/fuzzer" } aptos-abstract-gas-usage = { path = "aptos-move/aptos-abstract-gas-usage" } aptos-gas-meter = { path = "aptos-move/aptos-gas-meter" } aptos-gas-algebra = { path = "aptos-move/aptos-gas-algebra" } -aptos-gas-calibration = { path = "aptos-move/aptos-gas-calibration" } aptos-gas-profiling = { path = "aptos-move/aptos-gas-profiling" } aptos-gas-schedule = { path = "aptos-move/aptos-gas-schedule" } aptos-gas-schedule-updator = { path = "aptos-move/aptos-gas-schedule-updator" } @@ -349,7 +327,6 @@ aptos-indexer-grpc-table-info = { path = "ecosystem/indexer-grpc/indexer-grpc-ta aptos-indexer-grpc-utils = { path = "ecosystem/indexer-grpc/indexer-grpc-utils" } aptos-indexer-grpc-server-framework = { path = "ecosystem/indexer-grpc/indexer-grpc-server-framework" } aptos-infallible = { path = "crates/aptos-infallible" } -aptos-inspection-service = { path = "crates/aptos-inspection-service" } aptos-jellyfish-merkle = { path = "storage/jellyfish-merkle" } aptos-jwk-consensus = { path = "crates/aptos-jwk-consensus" } aptos-keygen = { path = "crates/aptos-keygen" } @@ -374,7 +351,6 @@ aptos-network-builder = { path = "network/builder" } aptos-network-checker = { path = "crates/aptos-network-checker" } aptos-network-discovery = { path = "network/discovery" } aptos-nft-metadata-crawler-parser = { path = "ecosystem/nft-metadata-crawler-parser" } -aptos-node = { path = "aptos-node" } aptos-node-checker = { path = "ecosystem/node-checker" } aptos-node-identity = { path = "crates/aptos-node-identity" } aptos-node-resource-metrics = { path = "crates/node-resource-metrics" } @@ -392,13 +368,11 @@ aptos-protos = { path = "protos/rust" } aptos-proxy = { path = "crates/proxy" } aptos-push-metrics = { path = "crates/aptos-push-metrics" } aptos-rate-limiter = { path = "crates/aptos-rate-limiter" } -aptos-release-builder = { path = "aptos-move/aptos-release-builder" } aptos-reliable-broadcast = { path = "crates/reliable-broadcast" } aptos-resource-viewer = { path = "aptos-move/aptos-resource-viewer" } aptos-rest-client = { path = "crates/aptos-rest-client" } aptos-retrier = { path = "crates/aptos-retrier" } aptos-rocksdb-options = { path = "storage/rocksdb-options" } -aptos-rosetta = { path = "crates/aptos-rosetta" } aptos-runtimes = { path = "crates/aptos-runtimes" } aptos-safety-rules = { path = "consensus/safety-rules" } aptos-schemadb = { path = "storage/schemadb" } @@ -415,8 +389,6 @@ aptos-storage-service-client = { path = "state-sync/storage-service/client" } aptos-storage-service-notifications = { path = "state-sync/inter-component/storage-service-notifications" } aptos-storage-service-types = { path = "state-sync/storage-service/types" } aptos-storage-service-server = { path = "state-sync/storage-service/server" } -aptos-telemetry = { path = "crates/aptos-telemetry" } -aptos-telemetry-service = { path = "crates/aptos-telemetry-service" } aptos-temppath = { path = "crates/aptos-temppath" } aptos-testcases = { path = "testsuite/testcases" } aptos-time-service = { path = "crates/aptos-time-service", features = [ diff --git a/aptos-move/aptos-e2e-comparison-testing/Cargo.toml b/aptos-move/aptos-e2e-comparison-testing/Cargo.toml deleted file mode 100644 index e57af6a499dd4..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "aptos-comparison-testing" -version = "0.1.0" -authors = ["Aptos Labs "] -description = "A tool to perform comparison testing on the compiler" -repository = "https://github.com/aptos-labs/aptos-core" -homepage = "https://aptoslabs.com" -license = "Apache-2.0" -publish = false -edition = "2021" -default-run = "aptos-comparison-testing" - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true } -aptos-crypto = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas-meter = { workspace = true } -aptos-gas-profiling = { workspace = true } -aptos-gas-schedule = { workspace = true } -aptos-language-e2e-tests = { workspace = true } -aptos-logger = { workspace = true } -aptos-memory-usage-tracker = { workspace = true } -aptos-resource-viewer = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-table-natives = { workspace = true } -aptos-types = { workspace = true } -aptos-validator-interface = { workspace = true } -aptos-vm = { workspace = true } -aptos-vm-logging = { workspace = true } -aptos-vm-types = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -itertools = { workspace = true } -lru = { workspace = true } -move-binary-format = { workspace = true } -move-cli = { workspace = true } -move-compiler = { workspace = true } -move-core-types = { workspace = true } -move-package = { workspace = true } -move-resource-viewer = { workspace = true } -move-vm-runtime = { workspace = true } -move-vm-test-utils = { workspace = true } -move-vm-types = { workspace = true } -num_cpus = { workspace = true } -once_cell = { workspace = true } -rayon = { workspace = true } -regex = { workspace = true } -rocksdb = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tempfile = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } - diff --git a/aptos-move/aptos-e2e-comparison-testing/src/data_collection.rs b/aptos-move/aptos-e2e-comparison-testing/src/data_collection.rs deleted file mode 100644 index 837f495c22287..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/data_collection.rs +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - data_state_view::DataStateView, dump_and_compile_from_package_metadata, is_aptos_package, - CompilationCache, DataManager, IndexWriter, PackageInfo, TxnIndex, -}; -use anyhow::{format_err, Result}; -use aptos_framework::natives::code::PackageMetadata; -use aptos_rest_client::Client; -use aptos_types::{ - state_store::{state_key::StateKey, state_value::StateValue, TStateView}, - transaction::{ - signature_verified_transaction::SignatureVerifiedTransaction, Transaction, - TransactionOutput, Version, - }, - write_set::TOTAL_SUPPLY_STATE_KEY, -}; -use aptos_validator_interface::{AptosValidatorInterface, FilterCondition, RestDebuggerInterface}; -use aptos_vm::{AptosVM, VMExecutor}; -use move_core_types::account_address::AccountAddress; -use std::{ - collections::HashMap, - ops::Deref, - path::PathBuf, - sync::{Arc, Mutex}, -}; - -pub struct DataCollection { - debugger: Arc, - current_dir: PathBuf, - batch_size: u64, - dump_write_set: bool, - filter_condition: FilterCondition, -} - -impl DataCollection { - pub fn new( - debugger: Arc, - current_dir: PathBuf, - batch_size: u64, - skip_failed_txns: bool, - skip_publish_txns: bool, - dump_write_set: bool, - skip_source_code: bool, - target_account: Option, - ) -> Self { - Self { - debugger, - current_dir, - batch_size, - dump_write_set, - filter_condition: FilterCondition { - skip_failed_txns, - skip_publish_txns, - check_source_code: !skip_source_code, - target_account, - }, - } - } - - pub fn new_with_rest_client( - rest_client: Client, - current_dir: PathBuf, - batch_size: u64, - skip_failed_txns: bool, - skip_publish_txns: bool, - dump_write_set: bool, - skip_source_code: bool, - target_account: Option, - ) -> Result { - Ok(Self::new( - Arc::new(RestDebuggerInterface::new(rest_client)), - current_dir, - batch_size, - skip_failed_txns, - skip_publish_txns, - dump_write_set, - skip_source_code, - target_account, - )) - } - - fn execute_transactions_at_version_with_state_view( - txns: Vec, - debugger_state_view: &DataStateView, - ) -> Result> { - let sig_verified_txns: Vec = - txns.into_iter().map(|x| x.into()).collect::>(); - // check whether total supply can be retrieved - // used for debugging the aggregator panic issue, will be removed later - // FIXME(#10412): remove the assert - let val = debugger_state_view.get_state_value(TOTAL_SUPPLY_STATE_KEY.deref()); - assert!(val.is_ok() && val.unwrap().is_some()); - AptosVM::execute_block_no_limit(&sig_verified_txns, debugger_state_view) - .map_err(|err| format_err!("Unexpected VM Error: {:?}", err)) - } - - fn dump_and_check_src( - version: Version, - address: AccountAddress, - package_name: String, - map: HashMap<(AccountAddress, String), PackageMetadata>, - compilation_cache: &mut CompilationCache, - current_dir: PathBuf, - ) -> Option { - let upgrade_number = if is_aptos_package(&package_name) { - None - } else { - let package = map.get(&(address, package_name.clone())).unwrap(); - Some(package.upgrade_number) - }; - - let package_info = PackageInfo { - address, - package_name: package_name.clone(), - upgrade_number, - }; - if compilation_cache.failed_packages_v1.contains(&package_info) { - return None; - } - if !is_aptos_package(&package_name) - && !compilation_cache - .compiled_package_map - .contains_key(&package_info) - { - let res = dump_and_compile_from_package_metadata( - package_info.clone(), - current_dir, - &map, - compilation_cache, - None, - ); - if res.is_err() { - eprintln!("{} at: {}", res.unwrap_err(), version); - return None; - } - } - Some(package_info) - } - - fn dump_txn_index( - data_manager: &mut DataManager, - txn_index: TxnIndex, - data_state: &HashMap, - epoch_result_res: Result>, - dump_write_set: bool, - ) { - // dump TxnIndex - data_manager.dump_txn_index(txn_index.version, &txn_index); - // dump state data - data_manager.dump_state_data(txn_index.version, data_state); - // dump write set - if dump_write_set { - let output = epoch_result_res.unwrap(); - assert_eq!(output.len(), 1); - let write_set = output[0].write_set(); - data_manager.dump_write_set(txn_index.version, write_set); - } - } - - pub async fn dump_data(&self, begin: Version, limit: u64) -> Result<()> { - println!("begin dumping data"); - let compilation_cache = Arc::new(Mutex::new(CompilationCache::default())); - let data_manager = Arc::new(Mutex::new(DataManager::new_with_dir_creation( - &self.current_dir, - ))); - let index_writer = Arc::new(Mutex::new(IndexWriter::new(&self.current_dir))); - - let mut cur_version = begin; - let mut module_registry_map = HashMap::new(); - while cur_version < begin + limit { - let batch = if cur_version + self.batch_size <= begin + limit { - self.batch_size - } else { - begin + limit - cur_version - }; - let res_txns = self - .debugger - .get_and_filter_committed_transactions( - cur_version, - batch, - self.filter_condition, - &mut module_registry_map, - ) - .await; - // if error happens when collecting txns, log the version range - if res_txns.is_err() { - index_writer - .lock() - .unwrap() - .write_err(&format!("{}:{}", cur_version, batch)); - } - let txns = res_txns.unwrap_or_default(); - if !txns.is_empty() { - let mut txn_execution_ths = vec![]; - for (version, txn, source_code_data) in txns { - println!("get txn at version:{}", version); - - let compilation_cache = compilation_cache.clone(); - let current_dir = self.current_dir.clone(); - let dump_write_set = self.dump_write_set; - let data_manager = data_manager.clone(); - let index = index_writer.clone(); - - let state_view = - DataStateView::new_with_data_reads(self.debugger.clone(), version); - - let txn_execution_thread = tokio::task::spawn_blocking(move || { - let epoch_result_res = - Self::execute_transactions_at_version_with_state_view( - vec![txn.clone()], - &state_view, - ); - if let Err(err) = epoch_result_res { - println!( - "execution error during transaction at version:{} :{}", - version, err - ); - return; - } - - let mut version_idx = TxnIndex { - version, - txn, - package_info: PackageInfo::non_compilable_info(), - }; - - // handle source code - if let Some((address, package_name, map)) = source_code_data { - let package_info_opt = Self::dump_and_check_src( - version, - address, - package_name, - map, - &mut compilation_cache.lock().unwrap(), - current_dir.clone(), - ); - if package_info_opt.is_none() { - return; - } - version_idx.package_info = package_info_opt.unwrap(); - } - - // dump through data_manager - Self::dump_txn_index( - &mut data_manager.lock().unwrap(), - version_idx, - &state_view.get_state_keys().lock().unwrap(), - epoch_result_res, - dump_write_set, - ); - - // Log version - index.lock().unwrap().add_version(version); - }); - txn_execution_ths.push(txn_execution_thread); - } - futures::future::join_all(txn_execution_ths).await; - // Dump version - index_writer.lock().unwrap().dump_version(); - } - cur_version += batch; - } - index_writer.lock().unwrap().flush_writer(); - Ok(()) - } -} diff --git a/aptos-move/aptos-e2e-comparison-testing/src/data_state_view.rs b/aptos-move/aptos-e2e-comparison-testing/src/data_state_view.rs deleted file mode 100644 index f5c81a5dc56e3..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/data_state_view.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_language_e2e_tests::data_store::FakeDataStore; -use aptos_types::{ - state_store::{ - state_key::StateKey, state_storage_usage::StateStorageUsage, state_value::StateValue, - Result as StateViewResult, TStateView, - }, - transaction::Version, -}; -use aptos_validator_interface::{AptosValidatorInterface, DebuggerStateView}; -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; - -pub struct DataStateView { - debugger_view: DebuggerStateView, - code_data: Option, - data_read_state_keys: Option>>>, -} -use std::ops::DerefMut; - -impl DataStateView { - pub fn new( - db: Arc, - version: Version, - code_data: FakeDataStore, - ) -> Self { - Self { - debugger_view: DebuggerStateView::new(db, version), - code_data: Some(code_data), - data_read_state_keys: None, - } - } - - pub fn new_with_data_reads( - db: Arc, - version: Version, - ) -> Self { - Self { - debugger_view: DebuggerStateView::new(db, version), - code_data: None, - data_read_state_keys: Some(Arc::new(Mutex::new(HashMap::new()))), - } - } - - pub fn get_state_keys(self) -> Arc>> { - self.data_read_state_keys.unwrap() - } -} - -impl TStateView for DataStateView { - type Key = StateKey; - - fn get_state_value(&self, state_key: &StateKey) -> StateViewResult> { - if let Some(code) = &self.code_data { - if code.contains_key(state_key) { - return code.get_state_value(state_key).map_err(Into::into); - } - } - let ret = self.debugger_view.get_state_value(state_key); - if let Some(reads) = &self.data_read_state_keys { - if !state_key.is_aptos_code() - && !reads.lock().unwrap().contains_key(state_key) - && ret.is_ok() - { - let val = ret?; - if val.is_some() { - reads - .lock() - .unwrap() - .deref_mut() - .insert(state_key.clone(), val.clone().unwrap()); - }; - return Ok(val); - } - } - ret - } - - fn get_usage(&self) -> StateViewResult { - unreachable!() - } -} diff --git a/aptos-move/aptos-e2e-comparison-testing/src/execution.rs b/aptos-move/aptos-e2e-comparison-testing/src/execution.rs deleted file mode 100644 index 0e8d84ec9d2e2..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/execution.rs +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - check_aptos_packages_availability, compile_aptos_packages, compile_package, - data_state_view::DataStateView, generate_compiled_blob, is_aptos_package, CompilationCache, - DataManager, IndexReader, PackageInfo, TxnIndex, APTOS_COMMONS, -}; -use anyhow::Result; -use aptos_framework::APTOS_PACKAGES; -use aptos_language_e2e_tests::{data_store::FakeDataStore, executor::FakeExecutor}; -use aptos_types::{ - contract_event::ContractEvent, - on_chain_config::{FeatureFlag, Features, OnChainConfig}, - transaction::{Transaction, TransactionPayload, Version}, - vm_status::VMStatus, - write_set::WriteSet, -}; -use aptos_validator_interface::AptosValidatorInterface; -use aptos_vm::data_cache::AsMoveResolver; -use clap::ValueEnum; -use itertools::Itertools; -use move_core_types::{account_address::AccountAddress, language_storage::ModuleId}; -use move_package::CompilerVersion; -use std::{cmp, collections::HashMap, path::PathBuf, sync::Arc}; - -fn load_packages_to_executor( - executor: &mut FakeExecutor, - package_info: &PackageInfo, - compiled_package_cache: &HashMap>>, -) { - if !compiled_package_cache.contains_key(package_info) { - return; - } - let compiled_package = compiled_package_cache.get(package_info).unwrap(); - for (module_id, module_blob) in compiled_package { - executor.add_module(module_id, module_blob.clone()); - } -} - -fn load_aptos_packages_to_executor( - executor: &mut FakeExecutor, - compiled_package_map: &HashMap>>, -) { - for package in APTOS_PACKAGES { - let package_info = PackageInfo { - address: AccountAddress::ONE, - package_name: package.to_string(), - upgrade_number: None, - }; - load_packages_to_executor(executor, &package_info, compiled_package_map); - } -} - -#[derive(ValueEnum, Clone, Copy, Debug, Eq, PartialEq, PartialOrd)] -pub enum ExecutionMode { - V1, - V2, - Compare, -} - -impl ExecutionMode { - pub fn is_v1(&self) -> bool { - *self == Self::V1 - } - - pub fn is_v2(&self) -> bool { - *self == Self::V2 - } - - pub fn is_compare(&self) -> bool { - *self == Self::Compare - } - - pub fn is_v1_or_compare(&self) -> bool { - self.is_v1() || self.is_compare() - } - - pub fn is_v2_or_compare(&self) -> bool { - self.is_v2() || self.is_compare() - } -} - -impl Default for ExecutionMode { - fn default() -> Self { - Self::V1 - } -} - -pub struct Execution { - input_path: PathBuf, - pub execution_mode: ExecutionMode, - pub bytecode_version: u32, -} - -impl Execution { - pub fn output_result_str(&self, msg: String) { - eprintln!("{}", msg); - } - - pub fn new(input_path: PathBuf, execution_mode: ExecutionMode) -> Self { - Self { - input_path, - execution_mode, - bytecode_version: 6, - } - } - - pub async fn execute_txns(&self, begin: Version, num_txns_to_execute: u64) -> Result<()> { - let aptos_commons_path = self.input_path.join(APTOS_COMMONS); - if !check_aptos_packages_availability(aptos_commons_path.clone()) { - return Err(anyhow::Error::msg("aptos packages are missing")); - } - - let mut compiled_cache = CompilationCache::default(); - if self.execution_mode.is_v1_or_compare() { - compile_aptos_packages( - &aptos_commons_path, - &mut compiled_cache.compiled_package_cache_v1, - false, - )?; - } - if self.execution_mode.is_v2_or_compare() { - compile_aptos_packages( - &aptos_commons_path, - &mut compiled_cache.compiled_package_cache_v2, - true, - )?; - } - - // prepare data - let data_manager = DataManager::new(&self.input_path); - if !data_manager.check_dir_availability() { - return Err(anyhow::Error::msg("data is missing")); - } - if !IndexReader::check_availability(&self.input_path) { - return Err(anyhow::Error::msg("index file is missing")); - } - let mut index_reader = IndexReader::new(&self.input_path); - - // get the first idx from the version_index file - let ver = index_reader.get_next_version_ge(begin); - if ver.is_none() { - return Err(anyhow::Error::msg( - "cannot find a version greater than or equal to the specified begin version", - )); - } - let mut cur_version = ver.unwrap(); - let mut i = 0; - while i < num_txns_to_execute { - let res = self.execute_one_txn(cur_version, &data_manager, &mut compiled_cache); - if res.is_err() { - self.output_result_str(format!( - "execution at version:{} failed, skip to the next txn", - cur_version - )); - } - let mut ver_res = index_reader.get_next_version(); - while ver_res.is_err() { - ver_res = index_reader.get_next_version(); - } - if ver_res.is_ok() { - if let Some(ver) = ver_res.unwrap() { - cur_version = ver; - } else { - break; - } - } - i += 1; - } - Ok(()) - } - - fn compile_code( - &self, - txn_idx: &TxnIndex, - compiled_cache: &mut CompilationCache, - ) -> Result<()> { - if !txn_idx.package_info.is_compilable() { - return Err(anyhow::Error::msg("not compilable")); - } - let package_info = txn_idx.package_info.clone(); - let package_dir = self.input_path.join(format!("{}", package_info)); - if !package_dir.exists() { - return Err(anyhow::Error::msg("source code is not available")); - } - let mut v1_failed = false; - let mut v2_failed = false; - if self.execution_mode.is_v1_or_compare() - && !compiled_cache - .compiled_package_cache_v1 - .contains_key(&package_info) - { - if compiled_cache.failed_packages_v1.contains(&package_info) { - v1_failed = true; - } else { - let compiled_res_v1 = compile_package( - package_dir.clone(), - &package_info, - Some(CompilerVersion::V1), - ); - if let Ok(compiled_res) = compiled_res_v1 { - generate_compiled_blob( - &package_info, - &compiled_res, - &mut compiled_cache.compiled_package_cache_v1, - ); - } else { - v1_failed = true; - compiled_cache - .failed_packages_v1 - .insert(package_info.clone()); - } - } - } - if self.execution_mode.is_v2_or_compare() - && !compiled_cache - .compiled_package_cache_v2 - .contains_key(&package_info) - { - if compiled_cache.failed_packages_v2.contains(&package_info) { - v2_failed = true; - } else { - let compiled_res_v2 = - compile_package(package_dir, &package_info, Some(CompilerVersion::V2)); - if let Ok(compiled_res) = compiled_res_v2 { - generate_compiled_blob( - &package_info, - &compiled_res, - &mut compiled_cache.compiled_package_cache_v2, - ); - } else { - v2_failed = true; - compiled_cache - .failed_packages_v2 - .insert(package_info.clone()); - } - } - } - if v1_failed || v2_failed { - let mut err_msg = "compilation failed at ".to_string(); - if v1_failed { - err_msg = format!("{} v1", err_msg); - } - if v2_failed { - err_msg = format!("{} v2", err_msg); - } - return Err(anyhow::Error::msg(err_msg)); - } - Ok(()) - } - - fn execute_one_txn( - &self, - cur_version: Version, - data_manager: &DataManager, - compiled_cache: &mut CompilationCache, - ) -> Result<()> { - if let Some(txn_idx) = data_manager.get_txn_index(cur_version) { - // compile the code if the source code is available - if txn_idx.package_info.is_compilable() - && !is_aptos_package(&txn_idx.package_info.package_name) - { - let compiled_result = self.compile_code(&txn_idx, compiled_cache); - if compiled_result.is_err() { - self.output_result_str(format!( - "compilation failed for the package:{} at version:{}", - txn_idx.package_info.package_name, cur_version - )); - return compiled_result; - } - } - // read the state data; - let state = data_manager.get_state(cur_version); - // execute and compare - self.execute_and_compare( - cur_version, - state, - &txn_idx, - &compiled_cache.compiled_package_cache_v1, - &compiled_cache.compiled_package_cache_v2, - None, - ); - } - Ok(()) - } - - pub(crate) fn execute_and_compare( - &self, - cur_version: Version, - state: FakeDataStore, - txn_idx: &TxnIndex, - compiled_package_cache: &HashMap>>, - compiled_package_cache_v2: &HashMap>>, - debugger: Option>, - ) { - let mut package_cache_main = compiled_package_cache; - let package_cache_other = compiled_package_cache_v2; - let mut v2_flag = false; - if self.execution_mode.is_v2() { - package_cache_main = compiled_package_cache_v2; - v2_flag = true; - } - let res_main_opt = self.execute_code( - cur_version, - state.clone(), - &txn_idx.package_info, - &txn_idx.txn, - package_cache_main, - debugger.clone(), - v2_flag, - ); - if self.execution_mode.is_compare() { - let res_other_opt = self.execute_code( - cur_version, - state, - &txn_idx.package_info, - &txn_idx.txn, - package_cache_other, - debugger.clone(), - true, - ); - self.print_mismatches(cur_version, &res_main_opt.unwrap(), &res_other_opt.unwrap()); - } else { - let res = res_main_opt.unwrap(); - if let Ok(res_ok) = res { - self.output_result_str(format!( - "version:{}\nwrite set:{:?}\n events:{:?}\n", - cur_version, res_ok.0, res_ok.1 - )); - } else { - self.output_result_str(format!( - "execution error {} at version: {}, error", - res.unwrap_err(), - cur_version - )); - } - } - } - - fn execute_code( - &self, - version: Version, - state: FakeDataStore, - package_info: &PackageInfo, - txn: &Transaction, - compiled_package_cache: &HashMap>>, - debugger_opt: Option>, - v2_flag: bool, - ) -> Option), VMStatus>> { - let executor = FakeExecutor::no_genesis(); - let mut executor = executor.set_not_parallel(); - *executor.data_store_mut() = state; - if let Transaction::UserTransaction(signed_trans) = txn { - let sender = signed_trans.sender(); - let payload = signed_trans.payload(); - if let TransactionPayload::EntryFunction(entry_function) = payload { - // Always load 0x1 modules - load_aptos_packages_to_executor(&mut executor, compiled_package_cache); - // Load modules - if package_info.is_compilable() { - load_packages_to_executor(&mut executor, package_info, compiled_package_cache); - } - let mut senders = vec![sender]; - senders.extend(signed_trans.authenticator().secondary_signer_addresses()); - let enable_v7 = |features: &mut Features| { - if v2_flag { - features.enable(FeatureFlag::VM_BINARY_FORMAT_V7); - } else { - features.enable(FeatureFlag::VM_BINARY_FORMAT_V6); - } - }; - if let Some(debugger) = debugger_opt { - let data_view = - DataStateView::new(debugger, version, executor.data_store().clone()); - let mut features = - Features::fetch_config(&data_view.as_move_resolver()).unwrap_or_default(); - enable_v7(&mut features); - return Some(executor.try_exec_entry_with_state_view( - senders, - entry_function, - &data_view.as_move_resolver(), - features, - )); - } else { - let mut features = - Features::fetch_config(&executor.data_store().clone().as_move_resolver()) - .unwrap_or_default(); - enable_v7(&mut features); - return Some(executor.try_exec_entry_with_state_view( - senders, - entry_function, - &executor.data_store().clone().as_move_resolver(), - features, - )); - } - } - } - if let Some(debugger) = debugger_opt { - let data_view = DataStateView::new(debugger, version, executor.data_store().clone()); - Some( - executor - .execute_transaction_block_with_state_view([txn.clone()].to_vec(), &data_view) - .map(|res| res[0].clone().into()), - ) - } else { - Some( - executor - .execute_transaction_block(vec![txn.clone()]) - .map(|res| res[0].clone().into()), - ) - } - } - - fn print_mismatches( - &self, - cur_version: u64, - res_1: &Result<(WriteSet, Vec), VMStatus>, - res_2: &Result<(WriteSet, Vec), VMStatus>, - ) { - match (res_1, res_2) { - (Err(e1), Err(e2)) => { - if e1.message() != e2.message() || e1.status_code() != e2.status_code() { - self.output_result_str(format!( - "error is different at version: {}", - cur_version - )); - self.output_result_str(format!("error {} is raised from V1", e1)); - self.output_result_str(format!("error {} is raised from V2", e2)); - } - }, - (Err(_), Ok(_)) => { - self.output_result_str(format!( - "V1 returns error while V2 does not at version: {}", - cur_version - )); - }, - (Ok(_), Err(_)) => { - self.output_result_str(format!( - "V2 returns error while V1 does not at version: {}", - cur_version - )); - }, - (Ok(res_1), Ok(res_2)) => { - // compare events - let mut event_error = false; - if res_1.1.len() != res_2.1.len() { - event_error = true; - } - for idx in 0..cmp::min(res_1.1.len(), res_2.1.len()) { - let event_1 = &res_1.1[idx]; - let event_2 = &res_2.1[idx]; - if event_1 != event_2 { - event_error = true; - self.output_result_str(format!( - "event raised from V1: {} at index: {}", - event_1, idx - )); - self.output_result_str(format!( - "event raised from V2: {} at index: {}", - event_2, idx - )); - } - } - if event_error { - self.output_result_str(format!( - "event is different at version: {}", - cur_version - )); - } - // compare write set - let mut write_set_error = false; - let res_1_write_set_vec = res_1.0.iter().collect_vec(); - let res_2_write_set_vec = res_2.0.iter().collect_vec(); - if res_1_write_set_vec.len() != res_2_write_set_vec.len() { - write_set_error = true; - } - for idx in 0..cmp::min(res_1_write_set_vec.len(), res_2_write_set_vec.len()) { - let write_set_1 = res_1_write_set_vec[idx]; - let write_set_2 = res_2_write_set_vec[idx]; - if write_set_1.0 != write_set_2.0 { - write_set_error = true; - self.output_result_str(format!( - "write set key is different at version: {}, index: {}", - cur_version, idx - )); - self.output_result_str(format!( - "state key at V1: {:?} at index: {}", - write_set_1.0, idx - )); - self.output_result_str(format!( - "state key at V2: {:?} at index: {}", - write_set_2.0, idx - )); - } - if write_set_1.1 != write_set_2.1 { - write_set_error = true; - self.output_result_str(format!( - "write set value is different at version: {}, index: {}", - cur_version, idx - )); - self.output_result_str(format!( - "state value at V1: {:?} at index: {}", - write_set_1.1, idx - )); - self.output_result_str(format!( - "state value at V2: {:?} at index: {}", - write_set_2.1, idx - )); - } - } - if write_set_error { - self.output_result_str(format!( - "write set is different at version: {}", - cur_version - )); - } - }, - } - } -} diff --git a/aptos-move/aptos-e2e-comparison-testing/src/lib.rs b/aptos-move/aptos-e2e-comparison-testing/src/lib.rs deleted file mode 100644 index 18d295bbd2af3..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/lib.rs +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_framework::{ - natives::code::PackageMetadata, unzip_metadata_str, BuiltPackage, APTOS_PACKAGES, -}; -use aptos_language_e2e_tests::data_store::FakeDataStore; -use aptos_types::{ - account_address::AccountAddress, - state_store::{state_key::StateKey, state_value::StateValue}, - transaction::Transaction, - write_set::WriteSet, -}; -use rocksdb::{DBWithThreadMode, SingleThreaded, DB}; -use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - fmt, - fs::{File, OpenOptions}, - io::{BufRead, BufReader, BufWriter, Read, Write}, - path::{Path, PathBuf}, - process::Command, -}; -use tempfile::TempDir; - -mod data_collection; -mod data_state_view; -mod execution; -mod online_execution; - -pub use data_collection::*; -pub use execution::*; -use move_compiler::compiled_unit::CompiledUnitEnum; -use move_core_types::language_storage::ModuleId; -use move_package::{ - compilation::compiled_package::CompiledPackage, - source_package::{ - manifest_parser::{parse_move_manifest_string, parse_source_manifest}, - parsed_manifest::Dependency, - }, - CompilerVersion, -}; -pub use online_execution::*; - -const APTOS_PACKAGES_DIR_NAMES: [&str; 5] = [ - "aptos-framework", - "move-stdlib", - "aptos-stdlib", - "aptos-token", - "aptos-token-objects", -]; - -const STATE_DATA: &str = "state_data"; -const WRITE_SET_DATA: &str = "write_set_data"; -const INDEX_FILE: &str = "version_index.txt"; -const ERR_LOG: &str = "err_log.txt"; -const ROCKS_INDEX_DB: &str = "rocks_txn_idx_db"; -pub const APTOS_COMMONS: &str = "aptos-commons"; -const MAX_TO_FLUSH: usize = 50000; - -struct IndexWriter { - index_writer: BufWriter, - err_logger: BufWriter, - version_vec: Vec, - counter: usize, -} - -impl IndexWriter { - pub fn new(root: &Path) -> Self { - let create_file = |file_name: &str| -> File { - let path = root.to_path_buf().join(file_name); - let file = if !path.exists() { - File::create(path).expect("Error encountered while creating file!") - } else { - OpenOptions::new() - .write(true) - .append(true) - .open(path) - .unwrap() - }; - file - }; - let index_file = create_file(INDEX_FILE); - let err_log = create_file(ERR_LOG); - Self { - index_writer: BufWriter::with_capacity(4096 * 1024 /* 4096KB */, index_file), - err_logger: BufWriter::with_capacity(4096 * 1024 /* 4096KB */, err_log), - version_vec: vec![], - counter: 0, - } - } - - pub fn reset_vec(&mut self) { - self.version_vec = vec![]; - } - - pub fn add_version(&mut self, version: u64) { - self.version_vec.push(version); - } - - pub fn dump_version(&mut self) { - self.version_vec.sort(); - self.version_vec.iter().for_each(|&version| { - self.index_writer - .write_fmt(format_args!("{}\n", version)) - .unwrap() - }); - self.counter += self.version_vec.len(); - self.reset_vec(); - if self.counter > MAX_TO_FLUSH { - self.flush_writer(); - } - } - - pub fn write_err(&mut self, err_msg: &str) { - self.err_logger - .write_fmt(format_args!("{}\n", err_msg)) - .unwrap(); - self.err_logger.flush().unwrap(); - } - - pub fn flush_writer(&mut self) { - self.index_writer.flush().unwrap(); - self.counter = 0; - } -} - -struct IndexReader { - index_reader: BufReader, - _version_cache: Vec, -} - -impl IndexReader { - pub fn check_availability(root: &Path) -> bool { - root.to_path_buf().join(INDEX_FILE).exists() - } - - pub fn new(root: &Path) -> Self { - let index_path = root.to_path_buf().join(INDEX_FILE); - let index_file = File::open(index_path).unwrap(); - let index_reader = BufReader::new(index_file); - Self { - index_reader, - _version_cache: vec![], - } - } - - pub fn _load_all_versions(&mut self) { - loop { - let next_val = self.get_next_version(); - if next_val.is_err() { - continue; - } - if let Some(val) = next_val.unwrap() { - self._version_cache.push(val); - } else { - break; - } - } - } - - pub fn get_next_version(&mut self) -> Result, ()> { - let mut cur_idx = String::new(); - let num_bytes = self.index_reader.read_line(&mut cur_idx).unwrap(); - if num_bytes == 0 { - return Ok(None); - } - let indx = cur_idx.trim().parse(); - if indx.is_ok() { - Ok(indx.ok()) - } else { - Err(()) - } - } - - pub fn get_next_version_ge(&mut self, version: u64) -> Option { - loop { - let next_val = self.get_next_version(); - if next_val.is_err() { - continue; - } - if let Some(val) = next_val.unwrap() { - if val >= version { - return Some(val); - } - } else { - break; - } - } - None - } -} - -struct DataManager { - state_data_dir_path: PathBuf, - write_set_dir_path: PathBuf, - db: DBWithThreadMode, -} - -impl DataManager { - pub fn new_with_dir_creation(root: &Path) -> Self { - let dm = Self::new(root); - if !dm.state_data_dir_path.exists() { - std::fs::create_dir_all(dm.state_data_dir_path.as_path()).unwrap(); - } - if !dm.write_set_dir_path.exists() { - std::fs::create_dir_all(dm.write_set_dir_path.as_path()).unwrap(); - } - dm - } - - pub fn new(root: &Path) -> Self { - let db = DB::open_default(root.to_path_buf().join(ROCKS_INDEX_DB)).unwrap(); - let state_data_dir_path = root.join(STATE_DATA); - let write_set_dir_path = root.join(WRITE_SET_DATA); - Self { - state_data_dir_path, - write_set_dir_path, - db, - } - } - - pub fn check_dir_availability(&self) -> bool { - if !(self.state_data_dir_path.exists() && self.write_set_dir_path.exists()) { - return false; - } - true - } - - pub fn dump_state_data(&self, version: u64, state: &HashMap) { - let state_path = self.state_data_dir_path.join(format!("{}_state", version)); - if !state_path.exists() { - let mut data_state_file = File::create(state_path).unwrap(); - let state_store = FakeDataStore::new_with_state_value(state.to_owned()); - data_state_file - .write_all(&bcs::to_bytes(&state_store).unwrap()) - .unwrap(); - } - } - - pub fn dump_write_set(&self, version: u64, write_set: &WriteSet) { - let write_set_path = self - .write_set_dir_path - .join(format!("{}_write_set", version)); - if !write_set_path.exists() { - let mut write_set_file = File::create(write_set_path).unwrap(); - write_set_file - .write_all(&bcs::to_bytes(&write_set).unwrap()) - .unwrap(); - } - } - - pub fn dump_txn_index(&self, version: u64, version_idx: &TxnIndex) { - self.db - .put( - bcs::to_bytes(&version).unwrap(), - bcs::to_bytes(&version_idx).unwrap(), - ) - .unwrap(); - } - - pub fn get_txn_index(&self, version: u64) -> Option { - let db_val = self.db.get(bcs::to_bytes(&version).unwrap()); - if let Ok(Some(val)) = db_val { - let txn_idx = bcs::from_bytes::(&val).unwrap(); - Some(txn_idx) - } else { - None - } - } - - pub fn get_state(&self, version: u64) -> FakeDataStore { - let state_path = self.state_data_dir_path.join(format!("{}_state", version)); - let mut data_state_file = File::open(state_path).unwrap(); - let mut buffer = Vec::::new(); - data_state_file.read_to_end(&mut buffer).unwrap(); - bcs::from_bytes::(&buffer).unwrap() - } -} - -fn is_aptos_package(package_name: &str) -> bool { - APTOS_PACKAGES.contains(&package_name) -} - -fn get_aptos_dir(package_name: &str) -> Option<&str> { - if is_aptos_package(package_name) { - for i in 0..APTOS_PACKAGES.len() { - if APTOS_PACKAGES[i] == package_name { - return Some(APTOS_PACKAGES_DIR_NAMES[i]); - } - } - } - None -} - -async fn download_aptos_packages(path: &Path) -> anyhow::Result<()> { - let git_url = "https://github.com/aptos-labs/aptos-core"; - let tmp_dir = TempDir::new()?; - Command::new("git") - .args(["clone", git_url, tmp_dir.path().to_str().unwrap()]) - .output() - .map_err(|_| anyhow::anyhow!("Failed to clone Git repository"))?; - let source_framework_path = PathBuf::from(tmp_dir.path()).join("aptos-move/framework"); - for package_name in APTOS_PACKAGES { - let source_framework_path = - source_framework_path.join(get_aptos_dir(package_name).unwrap()); - let target_framework_path = PathBuf::from(path).join(get_aptos_dir(package_name).unwrap()); - Command::new("cp") - .arg("-r") - .arg(source_framework_path) - .arg(target_framework_path) - .output() - .map_err(|_| anyhow::anyhow!("Failed to copy"))?; - } - - Ok(()) -} - -fn check_aptos_packages_availability(path: PathBuf) -> bool { - if !path.exists() { - return false; - } - for package in APTOS_PACKAGES { - if !path.join(get_aptos_dir(package).unwrap()).exists() { - return false; - } - } - true -} - -pub async fn prepare_aptos_packages(path: PathBuf) { - let mut success = true; - if path.exists() { - success = std::fs::remove_dir_all(path.clone()).is_ok(); - } - if success { - std::fs::create_dir_all(path.clone()).unwrap(); - download_aptos_packages(&path).await.unwrap(); - } -} - -#[derive(Default)] -struct CompilationCache { - compiled_package_map: HashMap, - failed_packages_v1: HashSet, - failed_packages_v2: HashSet, - compiled_package_cache_v1: HashMap>>, - compiled_package_cache_v2: HashMap>>, -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)] -pub(crate) struct PackageInfo { - address: AccountAddress, - package_name: String, - upgrade_number: Option, -} - -impl fmt::Display for PackageInfo { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut name = format!("{}.{}", self.package_name, self.address); - if self.upgrade_number.is_some() { - name = format!("{}.{}", name, self.upgrade_number.unwrap()); - } - write!(f, "{}", name)?; - Ok(()) - } -} - -impl PackageInfo { - pub fn is_compilable(&self) -> bool { - self.address != AccountAddress::ZERO - } - - pub fn non_compilable_info() -> Self { - Self { - address: AccountAddress::ZERO, - package_name: "".to_string(), - upgrade_number: None, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub(crate) struct TxnIndex { - version: u64, - package_info: PackageInfo, - txn: Transaction, -} - -fn generate_compiled_blob( - package_info: &PackageInfo, - compiled_package: &CompiledPackage, - compiled_blobs: &mut HashMap>>, -) { - if compiled_blobs.contains_key(package_info) { - return; - } - let root_modules = &compiled_package.root_compiled_units; - let mut blob_map = HashMap::new(); - for compiled_module in root_modules { - if let CompiledUnitEnum::Module(module) = &compiled_module.unit { - let module_blob = compiled_module.unit.serialize(None); - blob_map.insert(module.module.self_id(), module_blob); - } - } - compiled_blobs.insert(package_info.clone(), blob_map); -} - -fn compile_aptos_packages( - aptos_commons_path: &Path, - compiled_package_map: &mut HashMap>>, - v2_flag: bool, -) -> anyhow::Result<()> { - for package in APTOS_PACKAGES { - let root_package_dir = aptos_commons_path.join(get_aptos_dir(package).unwrap()); - let compiler_version = if v2_flag { - Some(CompilerVersion::V2) - } else { - Some(CompilerVersion::V1) - }; - // For simplicity, all packages including aptos token are stored under 0x1 in the map - let package_info = PackageInfo { - address: AccountAddress::ONE, - package_name: package.to_string(), - upgrade_number: None, - }; - let compiled_package = compile_package(root_package_dir, &package_info, compiler_version); - if let Ok(built_package) = compiled_package { - generate_compiled_blob(&package_info, &built_package, compiled_package_map); - } else { - return Err(anyhow::Error::msg(format!( - "package {} cannot be compiled", - package - ))); - } - } - Ok(()) -} - -fn compile_package( - root_dir: PathBuf, - package_info: &PackageInfo, - compiler_verion: Option, -) -> anyhow::Result { - let mut build_options = aptos_framework::BuildOptions { - compiler_version: compiler_verion, - ..Default::default() - }; - build_options - .named_addresses - .insert(package_info.package_name.clone(), package_info.address); - let compiled_package = BuiltPackage::build(root_dir, build_options); - if let Ok(built_package) = compiled_package { - Ok(built_package.package) - } else { - Err(anyhow::Error::msg(format!( - "compilation failed for compiler: {:?}", - compiler_verion - ))) - } -} - -fn dump_and_compile_from_package_metadata( - package_info: PackageInfo, - root_dir: PathBuf, - dep_map: &HashMap<(AccountAddress, String), PackageMetadata>, - compilation_cache: &mut CompilationCache, - execution_mode: Option, -) -> anyhow::Result<()> { - let root_package_dir = root_dir.join(format!("{}", package_info,)); - if compilation_cache.failed_packages_v1.contains(&package_info) { - return Err(anyhow::Error::msg("compilation failed")); - } - if !root_package_dir.exists() { - std::fs::create_dir_all(root_package_dir.as_path())?; - } - let root_package_metadata = dep_map - .get(&(package_info.address, package_info.package_name.clone())) - .unwrap(); - // step 1: unzip and save the source code into src into corresponding folder - let sources_dir = root_package_dir.join("sources"); - std::fs::create_dir_all(sources_dir.as_path())?; - let modules = root_package_metadata.modules.clone(); - for module in modules { - let module_path = sources_dir.join(format!("{}.move", module.name)); - if !module_path.exists() { - File::create(module_path.clone()).expect("Error encountered while creating file!"); - }; - let source_str = unzip_metadata_str(&module.source).unwrap(); - std::fs::write(&module_path.clone(), source_str).unwrap(); - } - - // step 2: unzip, parse the manifest file - let manifest_u8 = root_package_metadata.manifest.clone(); - let manifest_str = unzip_metadata_str(&manifest_u8).unwrap(); - let mut manifest = - parse_source_manifest(parse_move_manifest_string(manifest_str.clone()).unwrap()).unwrap(); - - let fix_manifest_dep = |dep: &mut Dependency, local_str: &str| { - dep.git_info = None; - dep.subst = None; - dep.version = None; - dep.digest = None; - dep.node_info = None; - dep.local = PathBuf::from("..").join(local_str); // PathBuf::from(local_str); - }; - - // step 3: fix the manifest file and recursively dump the code it depends - let manifest_deps = &mut manifest.dependencies; - for manifest_dep in manifest_deps { - let manifest_dep_name = manifest_dep.0.as_str(); - let dep = manifest_dep.1; - for pack_dep in &root_package_metadata.deps { - let pack_dep_address = pack_dep.account; - let pack_dep_name = pack_dep.clone().package_name; - if pack_dep_name == manifest_dep_name { - if is_aptos_package(&pack_dep_name) { - fix_manifest_dep( - dep, - &format!( - "{}/{}", - APTOS_COMMONS, - get_aptos_dir(&pack_dep_name).unwrap() - ), - ); - break; - } - let dep_metadata_opt = dep_map.get(&(pack_dep_address, pack_dep_name.clone())); - if let Some(dep_metadata) = dep_metadata_opt { - let package_info = PackageInfo { - address: pack_dep_address, - package_name: pack_dep_name.clone(), - upgrade_number: Some(dep_metadata.clone().upgrade_number), - }; - let path_str = format!("{}", package_info); - fix_manifest_dep(dep, &path_str); - dump_and_compile_from_package_metadata( - package_info, - root_dir.clone(), - dep_map, - compilation_cache, - execution_mode, - )?; - } - break; - } - } - } - - // step 4: dump the fixed manifest file - let toml_path = root_package_dir.join("Move.toml"); - std::fs::write(toml_path, manifest.to_string()).unwrap(); - - // step 5: test whether the code can be compiled - if !compilation_cache - .compiled_package_map - .contains_key(&package_info) - { - let package_v1 = compile_package( - root_package_dir.clone(), - &package_info, - Some(CompilerVersion::V1), - ); - if let Ok(built_package) = package_v1 { - if execution_mode.is_some_and(|mode| mode.is_v1_or_compare()) { - generate_compiled_blob( - &package_info, - &built_package, - &mut compilation_cache.compiled_package_cache_v1, - ); - } - compilation_cache - .compiled_package_map - .insert(package_info.clone(), built_package); - } else { - if !compilation_cache.failed_packages_v1.contains(&package_info) { - compilation_cache.failed_packages_v1.insert(package_info); - } - return Err(anyhow::Error::msg("compilation failed at v1")); - } - if execution_mode.is_some_and(|mode| mode.is_v2_or_compare()) { - let package_v2 = - compile_package(root_package_dir, &package_info, Some(CompilerVersion::V2)); - if let Ok(built_package) = package_v2 { - generate_compiled_blob( - &package_info, - &built_package, - &mut compilation_cache.compiled_package_cache_v2, - ); - } else { - if !compilation_cache.failed_packages_v1.contains(&package_info) { - compilation_cache.failed_packages_v1.insert(package_info); - } - return Err(anyhow::Error::msg("compilation failed at v2")); - } - } - } - Ok(()) -} diff --git a/aptos-move/aptos-e2e-comparison-testing/src/main.rs b/aptos-move/aptos-e2e-comparison-testing/src/main.rs deleted file mode 100644 index 0d50666fe345f..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/main.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use aptos_comparison_testing::{ - prepare_aptos_packages, DataCollection, Execution, ExecutionMode, OnlineExecutor, APTOS_COMMONS, -}; -use aptos_rest_client::Client; -use clap::{Parser, Subcommand}; -use move_core_types::account_address::AccountAddress; -use std::path::PathBuf; -use url::Url; - -const BATCH_SIZE: u64 = 500; - -#[derive(Subcommand)] -pub enum Cmd { - /// Collect and dump the data - Dump { - /// Endpoint url to obtain the txn data, e.g. `https://api.mainnet.aptoslabs.com/v1` for mainnet. - /// To avoid rate limiting, users need to apply for API key from `https://developers.aptoslabs.com/` - /// and set the env variable X_API_KEY using the obtained key - endpoint: String, - /// Path to the dumped data - output_path: Option, - /// Do not dump failed txns - #[clap(long, default_value_t = false)] - skip_failed_txns: bool, - /// Do not dump publish txns - #[clap(long, default_value_t = false)] - skip_publish_txns: bool, - /// Collect txns regardless whether the source code is available - #[clap(long, default_value_t = false)] - skip_source_code_check: bool, - /// Dump the write set of txns - #[clap(long, default_value_t = false)] - dump_write_set: bool, - /// With this set, only dump transactions that are sent to this account - #[clap(long)] - target_account: Option, - }, - /// Collect and execute txns without dumping the state data - Online { - /// Endpoint url to obtain the txn data, - /// e.g. `https://api.mainnet.aptoslabs.com/v1` for mainnet. - /// To avoid rate limiting, users need to apply for API key from `https://developers.aptoslabs.com/` - /// and set the env variable X_API_KEY using the obtained key - endpoint: String, - /// Path to the dumped data - output_path: Option, - /// Do not dump failed txns - #[clap(long, default_value_t = false)] - skip_failed_txns: bool, - /// Do not dump publish txns - #[clap(long, default_value_t = false)] - skip_publish_txns: bool, - /// Whether to execute against V1, V2 alone or both compilers for comparison - /// Used when execution_only is true - #[clap(long)] - execution_mode: Option, - }, - /// Execution of txns - Execute { - /// Path to the data - input_path: Option, - /// Whether to execute against V1, V2 alone or both compilers for comparison - #[clap(long)] - execution_mode: Option, - }, -} - -#[derive(Parser)] -pub struct Argument { - #[clap(subcommand)] - cmd: Cmd, - - /// Scan/execute from the txn of this version - #[clap(long)] - begin_version: u64, - - /// Number of txns to scan/execute - #[clap(long)] - limit: u64, -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Argument::parse(); - - match args.cmd { - Cmd::Dump { - endpoint, - output_path, - skip_failed_txns, - skip_publish_txns, - skip_source_code_check: skip_source_code, - dump_write_set, - target_account, - } => { - let batch_size = BATCH_SIZE; - let output = if let Some(path) = output_path { - path - } else { - PathBuf::from(".") - }; - if !output.exists() { - std::fs::create_dir_all(output.as_path()).unwrap(); - } - if !skip_source_code { - prepare_aptos_packages(output.join(APTOS_COMMONS)).await; - } - let data_collector = DataCollection::new_with_rest_client( - Client::new(Url::parse(&endpoint)?), - output.clone(), - batch_size, - skip_failed_txns, - skip_publish_txns, - dump_write_set, - skip_source_code, - target_account, - )?; - data_collector - .dump_data(args.begin_version, args.limit) - .await?; - }, - Cmd::Online { - endpoint, - output_path, - skip_failed_txns, - skip_publish_txns, - execution_mode, - } => { - let batch_size = BATCH_SIZE; - let output = if let Some(path) = output_path { - path - } else { - PathBuf::from(".") - }; - if !output.exists() { - std::fs::create_dir_all(output.as_path()).unwrap(); - } - prepare_aptos_packages(output.join(APTOS_COMMONS)).await; - let online = OnlineExecutor::new_with_rest_client( - Client::new(Url::parse(&endpoint)?), - output.clone(), - batch_size, - skip_failed_txns, - skip_publish_txns, - execution_mode.unwrap_or_default(), - endpoint, - )?; - online.execute(args.begin_version, args.limit).await?; - }, - Cmd::Execute { - input_path, - execution_mode, - } => { - let input = if let Some(path) = input_path { - path - } else { - PathBuf::from(".") - }; - prepare_aptos_packages(input.join(APTOS_COMMONS)).await; - let executor = Execution::new(input, execution_mode.unwrap_or_default()); - executor - .execute_txns(args.begin_version, args.limit) - .await?; - }, - }; - Ok(()) -} diff --git a/aptos-move/aptos-e2e-comparison-testing/src/online_execution.rs b/aptos-move/aptos-e2e-comparison-testing/src/online_execution.rs deleted file mode 100644 index 166f2ff1e808f..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/src/online_execution.rs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - compile_aptos_packages, dump_and_compile_from_package_metadata, is_aptos_package, - CompilationCache, ExecutionMode, IndexWriter, PackageInfo, TxnIndex, APTOS_COMMONS, -}; -use anyhow::Result; -use aptos_framework::natives::code::PackageMetadata; -use aptos_language_e2e_tests::data_store::FakeDataStore; -use aptos_rest_client::Client; -use aptos_types::transaction::Version; -use aptos_validator_interface::{AptosValidatorInterface, FilterCondition, RestDebuggerInterface}; -use move_core_types::account_address::AccountAddress; -use std::{ - collections::HashMap, - path::PathBuf, - sync::{Arc, Mutex}, -}; -use url::Url; - -pub struct OnlineExecutor { - debugger: Arc, - current_dir: PathBuf, - batch_size: u64, - filter_condition: FilterCondition, - execution_mode: ExecutionMode, - endpoint: String, -} - -impl OnlineExecutor { - pub fn new( - debugger: Arc, - current_dir: PathBuf, - batch_size: u64, - skip_failed_txns: bool, - skip_publish_txns: bool, - execution_mode: ExecutionMode, - endpoint: String, - ) -> Self { - Self { - debugger, - current_dir, - batch_size, - filter_condition: FilterCondition { - skip_failed_txns, - skip_publish_txns, - check_source_code: true, - target_account: None, - }, - execution_mode, - endpoint, - } - } - - pub fn new_with_rest_client( - rest_client: Client, - current_dir: PathBuf, - batch_size: u64, - skip_failed_txns: bool, - skip_publish_txns: bool, - execution_mode: ExecutionMode, - endpoint: String, - ) -> Result { - Ok(Self::new( - Arc::new(RestDebuggerInterface::new(rest_client)), - current_dir, - batch_size, - skip_failed_txns, - skip_publish_txns, - execution_mode, - endpoint, - )) - } - - fn dump_and_check_src( - version: Version, - address: AccountAddress, - package_name: String, - map: HashMap<(AccountAddress, String), PackageMetadata>, - compilation_cache: &mut CompilationCache, - execution_mode: Option, - current_dir: PathBuf, - ) -> Option { - let upgrade_number = if is_aptos_package(&package_name) { - None - } else { - let package = map.get(&(address, package_name.clone())).unwrap(); - Some(package.upgrade_number) - }; - - let package_info = PackageInfo { - address, - package_name: package_name.clone(), - upgrade_number, - }; - if compilation_cache.failed_packages_v1.contains(&package_info) { - return None; - } - if !is_aptos_package(&package_name) - && !compilation_cache - .compiled_package_map - .contains_key(&package_info) - { - let res = dump_and_compile_from_package_metadata( - package_info.clone(), - current_dir, - &map, - compilation_cache, - execution_mode, - ); - if res.is_err() { - eprintln!("{} at:{}", res.unwrap_err(), version); - return None; - } - } - Some(package_info) - } - - pub async fn execute(&self, begin: Version, limit: u64) -> Result<()> { - println!("begin executing events"); - let compilation_cache = Arc::new(Mutex::new(CompilationCache::default())); - let index_writer = Arc::new(Mutex::new(IndexWriter::new(&self.current_dir))); - - let aptos_commons_path = self.current_dir.join(APTOS_COMMONS); - if self.execution_mode.is_v1_or_compare() { - compile_aptos_packages( - &aptos_commons_path, - &mut compilation_cache.lock().unwrap().compiled_package_cache_v1, - false, - )?; - } - if self.execution_mode.is_v2_or_compare() { - compile_aptos_packages( - &aptos_commons_path, - &mut compilation_cache.lock().unwrap().compiled_package_cache_v2, - true, - )?; - } - - let mut cur_version = begin; - let mut module_registry_map = HashMap::new(); - while cur_version < begin + limit { - let batch = if cur_version + self.batch_size <= begin + limit { - self.batch_size - } else { - begin + limit - cur_version - }; - let res_txns = self - .debugger - .get_and_filter_committed_transactions( - cur_version, - batch, - self.filter_condition, - &mut module_registry_map, - ) - .await; - // if error happens when collecting txns, log the version range - if res_txns.is_err() { - index_writer.lock().unwrap().write_err(&format!( - "{}:{}:{:?}", - cur_version, - batch, - res_txns.unwrap_err() - )); - cur_version += batch; - continue; - } - let txns = res_txns.unwrap_or_default(); - if !txns.is_empty() { - let mut txn_execution_ths = vec![]; - for (version, txn, source_code_data) in txns { - println!("get txn at version:{}", version); - - let compilation_cache = compilation_cache.clone(); - let current_dir = self.current_dir.clone(); - let execution_mode = self.execution_mode; - let endpoint = self.endpoint.clone(); - - let txn_execution_thread = tokio::task::spawn_blocking(move || { - let executor = crate::Execution::new(current_dir.clone(), execution_mode); - - let mut version_idx = TxnIndex { - version, - txn: txn.clone(), - package_info: PackageInfo::non_compilable_info(), - }; - - // handle source code - if let Some((address, package_name, map)) = source_code_data { - let execution_mode_opt = Some(execution_mode); - let package_info_opt = Self::dump_and_check_src( - version, - address, - package_name, - map, - &mut compilation_cache.lock().unwrap(), - execution_mode_opt, - current_dir.clone(), - ); - if package_info_opt.is_none() { - return; - } - - version_idx.package_info = package_info_opt.unwrap(); - - let state_store = FakeDataStore::default(); - - let cache_v1 = compilation_cache - .lock() - .unwrap() - .compiled_package_cache_v1 - .clone(); - let cache_v2 = compilation_cache - .lock() - .unwrap() - .compiled_package_cache_v2 - .clone(); - - let client = Client::new(Url::parse(&endpoint).unwrap()); - let debugger = Arc::new(RestDebuggerInterface::new(client)); - executor.execute_and_compare( - version, - state_store, - &version_idx, - &cache_v1, - &cache_v2, - Some(debugger), - ); - } - }); - txn_execution_ths.push(txn_execution_thread); - } - futures::future::join_all(txn_execution_ths).await; - } - cur_version += batch; - } - Ok(()) - } -} diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000008.sst b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000008.sst deleted file mode 100644 index 7036ed0037da0d651d0b9ebaf20fe57c92bffbb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2413 zcmaJ?d0bOh7QQzG@&X|Q0T(t?P+WkRfC;GJ5CL~OItnVH^d;{Sp1dr)_Xq^hL0l0R z6c=1U9Xl$vuBD2OI#iHxDOK_3UM<$C)vAbEggSKYi&lU0&)k3BednBemhXJ$-n)uG zp!m{d;f6{8p%{vwn-~CZ#WG;rQ#4+lTezue!y9$s?u3_+cY+zdBHjQ=d-&7VnCkgG0@7~!j|=|#mZbh zpY?-o1OSv&(v}O>Ur03qydY4Tf9O5@WnA&ydavS8*XTla9V`kw8T?{C}7 z&qeI{^yH-MRiFJl@5sg7A|Vd2i-AmW#$}179=J-w1#Dl02DhhpC)~Sbjy}=wDs@rL zp!!YyYu?@qC zik)&Z$>t#5#*jvY@pUoeIiLhc<7Y!Ky8)If$93#|Zj12wYJO$h=dHe5PQ|?X_u0l$ zZbI01hBA|HXDE?iIOsYH5dDiQahA%)JGtj;mVN<=Csa|7EcMau&Iws9zir(zzW<76 zLk>LJtnkw7!y=S=hM`R)Z)N>_lVkdL`zk}VnkWLMVD@D7g>C?7TfnAaEp6oC-LtNv zvUKmV1?h+O+;67(jefnc$ADjkgwz)FuLQsMN(lOCd3E=Jik|lFsGzYchPyQ?l8d)3 z%)RSpOc_5g_wn=EqVv6Dj@2@2p4@ItPMf3+h`pj$%$_{&It=Kz>~D1k(zzChpbT==H_G>BNt*U$vs)s{c5Xhoa>ViPML?R~7AIorj(sMt%3FP|!hANoWU2f8j%$G-rwb~1=~R}qa9N1uJS`+o+fk;~#%E%Jf=4dW z#L4aAFD|CGw~-MEH)=X6$NlxX`??dJM!nCphv7pr47O`iPSF)l&j%DG1oRp*d7EQN z;GUhX%}c+VPZr;jE&H_o`r;&?`Z=!W7oW4x-^@@CYNup1{*(GPt~{AsSpLH=Q*XFl z?3&s6Rqzz)9~GW)S*{5NV(B1nMKQQr^lL;;$u`rGnz}oOS1Id5vmN(Cn*Cp0TFln2 zqpoI1hXBysE~;x6mFH`Io>4aMAI;P)aZ-Jd`OeX|55h((_Vs--Jh61g%?bU8GtWH; zxCLA5+C_v;6P?-)1`lr(gLN}-d1;E-kM9laEI$txfS(WJax7_?$qvyy2 zOXZaVYj)cA%$#^yS3Y)eM0X3UZ-M1Q$6QWxwZEGV<+;Igck6D&HpT8-HN!$cf^=Pc?1)-czPu z+!&b1B4p#d6{X)uD=_X znGtPA{8tR1XHNJvaX|F`E2mp`BvdSGxYDoH?QnQiXf ztJ%4>cnBXs18PQVbfBM@wHk9c%AhhZc%w3q9HLY_X<(3wwos@*#XD?BF!xtL8;i2& z0#z2x@Q9@?Cbhp0%+I!B(;VdZDAo`PXtXkUW{WC|G}DYjMN^=sSYjc~h(cLp9>cp6 z{$?A=3ReJw?1;hfM>%+uPje{I`iDj)abpq3(N@b83u&`Ce26tr7$Pj-@^Nz3{7eJQ zbMcXyp(B(|H}TO?F}j!$N{+{_k;xIS9-%ao1(_6WBuU2TB4ahN!=hph#wd+J=k3`m+sg4D&P+uX6Bn668b?{JIgyiCs|t69 zWV{u=+&eTrSWZsTL{Fcl(~pWuNzo?h^;6SQv~r27)uQ5QGwRy0a-SO^6;UKlsyMn3 zReMN1AYu@H-h_s6fbf9tdg)XHcR4J0r&2LE2R<7}J)xB~lf24~uQ3ceApGDvRtOEJ zITeP36?{Rt7z!c$02mulFVK7u2kXgg_kfR&bc~&R6G*&_%K&C*69mZQRi_LKW*==*4qjmir4n| Xq}szzkp2JViq##>uNHr;Z(a6Zy4-)m diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000009.log b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000009.log deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000013.log b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000013.log deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000017.log b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/000017.log deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/CURRENT b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/CURRENT deleted file mode 100644 index e417a51c357b8..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/CURRENT +++ /dev/null @@ -1 +0,0 @@ -MANIFEST-000018 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/IDENTITY b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/IDENTITY deleted file mode 100644 index 05d8318173ba8..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/IDENTITY +++ /dev/null @@ -1 +0,0 @@ -68030351-9558-4d6e-aa76-818928bc21b6 \ No newline at end of file diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOCK b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOCK deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG deleted file mode 100644 index e84095f5ab5b2..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG +++ /dev/null @@ -1,285 +0,0 @@ -2023/11/02-13:17:00.663558 8055427840 RocksDB version: 8.1.1 -2023/11/02-13:17:00.664409 8055427840 Compile date 2023-04-06 16:38:52 -2023/11/02-13:17:00.664411 8055427840 DB SUMMARY -2023/11/02-13:17:00.664412 8055427840 DB Session ID: 2G1H1OMZJVXUMOOLASCB -2023/11/02-13:17:00.664488 8055427840 CURRENT file: CURRENT -2023/11/02-13:17:00.664490 8055427840 IDENTITY file: IDENTITY -2023/11/02-13:17:00.664496 8055427840 MANIFEST file: MANIFEST-000014 size: 181 Bytes -2023/11/02-13:17:00.664509 8055427840 SST files in ./test-data-mainnet-10m-15/rocks_txn_idx_db dir, Total Num: 1, files: 000008.sst -2023/11/02-13:17:00.664511 8055427840 Write Ahead Log file in ./test-data-mainnet-10m-15/rocks_txn_idx_db: 000009.log size: 0 ; 000013.log size: 0 ; -2023/11/02-13:17:00.664513 8055427840 Options.error_if_exists: 0 -2023/11/02-13:17:00.664514 8055427840 Options.create_if_missing: 1 -2023/11/02-13:17:00.664515 8055427840 Options.paranoid_checks: 1 -2023/11/02-13:17:00.664516 8055427840 Options.flush_verify_memtable_count: 1 -2023/11/02-13:17:00.664517 8055427840 Options.track_and_verify_wals_in_manifest: 0 -2023/11/02-13:17:00.664518 8055427840 Options.verify_sst_unique_id_in_manifest: 1 -2023/11/02-13:17:00.664519 8055427840 Options.env: 0x108aae5b8 -2023/11/02-13:17:00.664520 8055427840 Options.fs: PosixFileSystem -2023/11/02-13:17:00.664521 8055427840 Options.info_log: 0x1148ec9e8 -2023/11/02-13:17:00.664522 8055427840 Options.max_file_opening_threads: 16 -2023/11/02-13:17:00.664523 8055427840 Options.statistics: 0x0 -2023/11/02-13:17:00.664524 8055427840 Options.use_fsync: 0 -2023/11/02-13:17:00.664525 8055427840 Options.max_log_file_size: 0 -2023/11/02-13:17:00.664526 8055427840 Options.max_manifest_file_size: 1073741824 -2023/11/02-13:17:00.664527 8055427840 Options.log_file_time_to_roll: 0 -2023/11/02-13:17:00.664527 8055427840 Options.keep_log_file_num: 1000 -2023/11/02-13:17:00.664528 8055427840 Options.recycle_log_file_num: 0 -2023/11/02-13:17:00.664529 8055427840 Options.allow_fallocate: 1 -2023/11/02-13:17:00.664530 8055427840 Options.allow_mmap_reads: 0 -2023/11/02-13:17:00.664531 8055427840 Options.allow_mmap_writes: 0 -2023/11/02-13:17:00.664532 8055427840 Options.use_direct_reads: 0 -2023/11/02-13:17:00.664533 8055427840 Options.use_direct_io_for_flush_and_compaction: 0 -2023/11/02-13:17:00.664534 8055427840 Options.create_missing_column_families: 0 -2023/11/02-13:17:00.664535 8055427840 Options.db_log_dir: -2023/11/02-13:17:00.664536 8055427840 Options.wal_dir: -2023/11/02-13:17:00.664536 8055427840 Options.table_cache_numshardbits: 6 -2023/11/02-13:17:00.664538 8055427840 Options.WAL_ttl_seconds: 0 -2023/11/02-13:17:00.664538 8055427840 Options.WAL_size_limit_MB: 0 -2023/11/02-13:17:00.664539 8055427840 Options.max_write_batch_group_size_bytes: 1048576 -2023/11/02-13:17:00.664540 8055427840 Options.manifest_preallocation_size: 4194304 -2023/11/02-13:17:00.664541 8055427840 Options.is_fd_close_on_exec: 1 -2023/11/02-13:17:00.664542 8055427840 Options.advise_random_on_open: 1 -2023/11/02-13:17:00.664543 8055427840 Options.db_write_buffer_size: 0 -2023/11/02-13:17:00.664544 8055427840 Options.write_buffer_manager: 0x600002801110 -2023/11/02-13:17:00.664545 8055427840 Options.access_hint_on_compaction_start: 1 -2023/11/02-13:17:00.664546 8055427840 Options.random_access_max_buffer_size: 1048576 -2023/11/02-13:17:00.664547 8055427840 Options.use_adaptive_mutex: 0 -2023/11/02-13:17:00.664548 8055427840 Options.rate_limiter: 0x0 -2023/11/02-13:17:00.664549 8055427840 Options.sst_file_manager.rate_bytes_per_sec: 0 -2023/11/02-13:17:00.664550 8055427840 Options.wal_recovery_mode: 2 -2023/11/02-13:17:00.664551 8055427840 Options.enable_thread_tracking: 0 -2023/11/02-13:17:00.664552 8055427840 Options.enable_pipelined_write: 0 -2023/11/02-13:17:00.664553 8055427840 Options.unordered_write: 0 -2023/11/02-13:17:00.664554 8055427840 Options.allow_concurrent_memtable_write: 1 -2023/11/02-13:17:00.664554 8055427840 Options.enable_write_thread_adaptive_yield: 1 -2023/11/02-13:17:00.664555 8055427840 Options.write_thread_max_yield_usec: 100 -2023/11/02-13:17:00.664556 8055427840 Options.write_thread_slow_yield_usec: 3 -2023/11/02-13:17:00.664557 8055427840 Options.row_cache: None -2023/11/02-13:17:00.664558 8055427840 Options.wal_filter: None -2023/11/02-13:17:00.664559 8055427840 Options.avoid_flush_during_recovery: 0 -2023/11/02-13:17:00.664560 8055427840 Options.allow_ingest_behind: 0 -2023/11/02-13:17:00.664561 8055427840 Options.two_write_queues: 0 -2023/11/02-13:17:00.664562 8055427840 Options.manual_wal_flush: 0 -2023/11/02-13:17:00.664563 8055427840 Options.wal_compression: 0 -2023/11/02-13:17:00.664563 8055427840 Options.atomic_flush: 0 -2023/11/02-13:17:00.664564 8055427840 Options.avoid_unnecessary_blocking_io: 0 -2023/11/02-13:17:00.664565 8055427840 Options.persist_stats_to_disk: 0 -2023/11/02-13:17:00.664566 8055427840 Options.write_dbid_to_manifest: 0 -2023/11/02-13:17:00.664567 8055427840 Options.log_readahead_size: 0 -2023/11/02-13:17:00.664568 8055427840 Options.file_checksum_gen_factory: Unknown -2023/11/02-13:17:00.664580 8055427840 Options.best_efforts_recovery: 0 -2023/11/02-13:17:00.664581 8055427840 Options.max_bgerror_resume_count: 2147483647 -2023/11/02-13:17:00.664582 8055427840 Options.bgerror_resume_retry_interval: 1000000 -2023/11/02-13:17:00.664583 8055427840 Options.allow_data_in_errors: 0 -2023/11/02-13:17:00.664584 8055427840 Options.db_host_id: __hostname__ -2023/11/02-13:17:00.664585 8055427840 Options.enforce_single_del_contracts: true -2023/11/02-13:17:00.664586 8055427840 Options.max_background_jobs: 2 -2023/11/02-13:17:00.664587 8055427840 Options.max_background_compactions: -1 -2023/11/02-13:17:00.664588 8055427840 Options.max_subcompactions: 1 -2023/11/02-13:17:00.664589 8055427840 Options.avoid_flush_during_shutdown: 0 -2023/11/02-13:17:00.664589 8055427840 Options.writable_file_max_buffer_size: 1048576 -2023/11/02-13:17:00.664590 8055427840 Options.delayed_write_rate : 16777216 -2023/11/02-13:17:00.664591 8055427840 Options.max_total_wal_size: 0 -2023/11/02-13:17:00.664592 8055427840 Options.delete_obsolete_files_period_micros: 21600000000 -2023/11/02-13:17:00.664593 8055427840 Options.stats_dump_period_sec: 600 -2023/11/02-13:17:00.664594 8055427840 Options.stats_persist_period_sec: 600 -2023/11/02-13:17:00.664595 8055427840 Options.stats_history_buffer_size: 1048576 -2023/11/02-13:17:00.664596 8055427840 Options.max_open_files: -1 -2023/11/02-13:17:00.664597 8055427840 Options.bytes_per_sync: 0 -2023/11/02-13:17:00.664598 8055427840 Options.wal_bytes_per_sync: 0 -2023/11/02-13:17:00.664599 8055427840 Options.strict_bytes_per_sync: 0 -2023/11/02-13:17:00.664599 8055427840 Options.compaction_readahead_size: 0 -2023/11/02-13:17:00.664600 8055427840 Options.max_background_flushes: -1 -2023/11/02-13:17:00.664601 8055427840 Compression algorithms supported: -2023/11/02-13:17:00.664614 8055427840 kZSTD supported: 1 -2023/11/02-13:17:00.664615 8055427840 kZlibCompression supported: 1 -2023/11/02-13:17:00.664616 8055427840 kXpressCompression supported: 0 -2023/11/02-13:17:00.664617 8055427840 kSnappyCompression supported: 1 -2023/11/02-13:17:00.664619 8055427840 kZSTDNotFinalCompression supported: 1 -2023/11/02-13:17:00.664620 8055427840 kLZ4HCCompression supported: 1 -2023/11/02-13:17:00.664621 8055427840 kLZ4Compression supported: 1 -2023/11/02-13:17:00.664622 8055427840 kBZip2Compression supported: 1 -2023/11/02-13:17:00.664628 8055427840 Fast CRC32 supported: Supported on Arm64 -2023/11/02-13:17:00.664630 8055427840 DMutex implementation: pthread_mutex_t -2023/11/02-13:17:00.664805 8055427840 [db/version_set.cc:5662] Recovering from manifest file: ./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000014 -2023/11/02-13:17:00.665058 8055427840 [db/column_family.cc:621] --------------- Options for column family [default]: -2023/11/02-13:17:00.665060 8055427840 Options.comparator: leveldb.BytewiseComparator -2023/11/02-13:17:00.665061 8055427840 Options.merge_operator: None -2023/11/02-13:17:00.665062 8055427840 Options.compaction_filter: None -2023/11/02-13:17:00.665063 8055427840 Options.compaction_filter_factory: None -2023/11/02-13:17:00.665064 8055427840 Options.sst_partitioner_factory: None -2023/11/02-13:17:00.665065 8055427840 Options.memtable_factory: SkipListFactory -2023/11/02-13:17:00.665066 8055427840 Options.table_factory: BlockBasedTable -2023/11/02-13:17:00.665077 8055427840 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0x60000122c300) - cache_index_and_filter_blocks: 0 - cache_index_and_filter_blocks_with_high_priority: 1 - pin_l0_filter_and_index_blocks_in_cache: 0 - pin_top_level_index_and_filter: 1 - index_type: 0 - data_block_index_type: 0 - index_shortening: 1 - data_block_hash_table_util_ratio: 0.750000 - checksum: 4 - no_block_cache: 0 - block_cache: 0x600002627798 - block_cache_name: LRUCache - block_cache_options: - capacity : 8388608 - num_shard_bits : 4 - strict_capacity_limit : 0 - memory_allocator : None - high_pri_pool_ratio: 0.000 - low_pri_pool_ratio: 0.000 - persistent_cache: 0x0 - block_size: 4096 - block_size_deviation: 10 - block_restart_interval: 16 - index_block_restart_interval: 1 - metadata_block_size: 4096 - partition_filters: 0 - use_delta_encoding: 1 - filter_policy: nullptr - whole_key_filtering: 1 - verify_compression: 0 - read_amp_bytes_per_bit: 0 - format_version: 5 - enable_index_compression: 1 - block_align: 0 - max_auto_readahead_size: 262144 - prepopulate_block_cache: 0 - initial_auto_readahead_size: 8192 - num_file_reads_for_auto_readahead: 2 -2023/11/02-13:17:00.665078 8055427840 Options.write_buffer_size: 67108864 -2023/11/02-13:17:00.665079 8055427840 Options.max_write_buffer_number: 2 -2023/11/02-13:17:00.665080 8055427840 Options.compression: Snappy -2023/11/02-13:17:00.665081 8055427840 Options.bottommost_compression: Disabled -2023/11/02-13:17:00.665082 8055427840 Options.prefix_extractor: nullptr -2023/11/02-13:17:00.665083 8055427840 Options.memtable_insert_with_hint_prefix_extractor: nullptr -2023/11/02-13:17:00.665084 8055427840 Options.num_levels: 7 -2023/11/02-13:17:00.665085 8055427840 Options.min_write_buffer_number_to_merge: 1 -2023/11/02-13:17:00.665086 8055427840 Options.max_write_buffer_number_to_maintain: 0 -2023/11/02-13:17:00.665087 8055427840 Options.max_write_buffer_size_to_maintain: 0 -2023/11/02-13:17:00.665088 8055427840 Options.bottommost_compression_opts.window_bits: -14 -2023/11/02-13:17:00.665089 8055427840 Options.bottommost_compression_opts.level: 32767 -2023/11/02-13:17:00.665090 8055427840 Options.bottommost_compression_opts.strategy: 0 -2023/11/02-13:17:00.665091 8055427840 Options.bottommost_compression_opts.max_dict_bytes: 0 -2023/11/02-13:17:00.665092 8055427840 Options.bottommost_compression_opts.zstd_max_train_bytes: 0 -2023/11/02-13:17:00.665093 8055427840 Options.bottommost_compression_opts.parallel_threads: 1 -2023/11/02-13:17:00.665094 8055427840 Options.bottommost_compression_opts.enabled: false -2023/11/02-13:17:00.665095 8055427840 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-13:17:00.665096 8055427840 Options.bottommost_compression_opts.use_zstd_dict_trainer: true -2023/11/02-13:17:00.665096 8055427840 Options.compression_opts.window_bits: -14 -2023/11/02-13:17:00.665097 8055427840 Options.compression_opts.level: 32767 -2023/11/02-13:17:00.665098 8055427840 Options.compression_opts.strategy: 0 -2023/11/02-13:17:00.665099 8055427840 Options.compression_opts.max_dict_bytes: 0 -2023/11/02-13:17:00.665100 8055427840 Options.compression_opts.zstd_max_train_bytes: 0 -2023/11/02-13:17:00.665101 8055427840 Options.compression_opts.use_zstd_dict_trainer: true -2023/11/02-13:17:00.665102 8055427840 Options.compression_opts.parallel_threads: 1 -2023/11/02-13:17:00.665103 8055427840 Options.compression_opts.enabled: false -2023/11/02-13:17:00.665104 8055427840 Options.compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-13:17:00.665105 8055427840 Options.level0_file_num_compaction_trigger: 4 -2023/11/02-13:17:00.665106 8055427840 Options.level0_slowdown_writes_trigger: 20 -2023/11/02-13:17:00.665107 8055427840 Options.level0_stop_writes_trigger: 36 -2023/11/02-13:17:00.665108 8055427840 Options.target_file_size_base: 67108864 -2023/11/02-13:17:00.665108 8055427840 Options.target_file_size_multiplier: 1 -2023/11/02-13:17:00.665109 8055427840 Options.max_bytes_for_level_base: 268435456 -2023/11/02-13:17:00.665110 8055427840 Options.level_compaction_dynamic_level_bytes: 0 -2023/11/02-13:17:00.665111 8055427840 Options.max_bytes_for_level_multiplier: 10.000000 -2023/11/02-13:17:00.665112 8055427840 Options.max_bytes_for_level_multiplier_addtl[0]: 1 -2023/11/02-13:17:00.665114 8055427840 Options.max_bytes_for_level_multiplier_addtl[1]: 1 -2023/11/02-13:17:00.665114 8055427840 Options.max_bytes_for_level_multiplier_addtl[2]: 1 -2023/11/02-13:17:00.665115 8055427840 Options.max_bytes_for_level_multiplier_addtl[3]: 1 -2023/11/02-13:17:00.665116 8055427840 Options.max_bytes_for_level_multiplier_addtl[4]: 1 -2023/11/02-13:17:00.665117 8055427840 Options.max_bytes_for_level_multiplier_addtl[5]: 1 -2023/11/02-13:17:00.665118 8055427840 Options.max_bytes_for_level_multiplier_addtl[6]: 1 -2023/11/02-13:17:00.665119 8055427840 Options.max_sequential_skip_in_iterations: 8 -2023/11/02-13:17:00.665120 8055427840 Options.max_compaction_bytes: 1677721600 -2023/11/02-13:17:00.665121 8055427840 Options.ignore_max_compaction_bytes_for_input: true -2023/11/02-13:17:00.665122 8055427840 Options.arena_block_size: 1048576 -2023/11/02-13:17:00.665123 8055427840 Options.soft_pending_compaction_bytes_limit: 68719476736 -2023/11/02-13:17:00.665124 8055427840 Options.hard_pending_compaction_bytes_limit: 274877906944 -2023/11/02-13:17:00.665125 8055427840 Options.disable_auto_compactions: 0 -2023/11/02-13:17:00.665126 8055427840 Options.compaction_style: kCompactionStyleLevel -2023/11/02-13:17:00.665128 8055427840 Options.compaction_pri: kMinOverlappingRatio -2023/11/02-13:17:00.665129 8055427840 Options.compaction_options_universal.size_ratio: 1 -2023/11/02-13:17:00.665130 8055427840 Options.compaction_options_universal.min_merge_width: 2 -2023/11/02-13:17:00.665131 8055427840 Options.compaction_options_universal.max_merge_width: 4294967295 -2023/11/02-13:17:00.665132 8055427840 Options.compaction_options_universal.max_size_amplification_percent: 200 -2023/11/02-13:17:00.665133 8055427840 Options.compaction_options_universal.compression_size_percent: -1 -2023/11/02-13:17:00.665134 8055427840 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize -2023/11/02-13:17:00.665135 8055427840 Options.compaction_options_fifo.max_table_files_size: 1073741824 -2023/11/02-13:17:00.665136 8055427840 Options.compaction_options_fifo.allow_compaction: 0 -2023/11/02-13:17:00.665137 8055427840 Options.table_properties_collectors: -2023/11/02-13:17:00.665138 8055427840 Options.inplace_update_support: 0 -2023/11/02-13:17:00.665139 8055427840 Options.inplace_update_num_locks: 10000 -2023/11/02-13:17:00.665140 8055427840 Options.memtable_prefix_bloom_size_ratio: 0.000000 -2023/11/02-13:17:00.665141 8055427840 Options.memtable_whole_key_filtering: 0 -2023/11/02-13:17:00.665142 8055427840 Options.memtable_huge_page_size: 0 -2023/11/02-13:17:00.665143 8055427840 Options.bloom_locality: 0 -2023/11/02-13:17:00.665144 8055427840 Options.max_successive_merges: 0 -2023/11/02-13:17:00.665145 8055427840 Options.optimize_filters_for_hits: 0 -2023/11/02-13:17:00.665146 8055427840 Options.paranoid_file_checks: 0 -2023/11/02-13:17:00.665147 8055427840 Options.force_consistency_checks: 1 -2023/11/02-13:17:00.665148 8055427840 Options.report_bg_io_stats: 0 -2023/11/02-13:17:00.665149 8055427840 Options.ttl: 2592000 -2023/11/02-13:17:00.665150 8055427840 Options.periodic_compaction_seconds: 0 -2023/11/02-13:17:00.665151 8055427840 Options.preclude_last_level_data_seconds: 0 -2023/11/02-13:17:00.665151 8055427840 Options.preserve_internal_time_seconds: 0 -2023/11/02-13:17:00.665152 8055427840 Options.enable_blob_files: false -2023/11/02-13:17:00.665153 8055427840 Options.min_blob_size: 0 -2023/11/02-13:17:00.665154 8055427840 Options.blob_file_size: 268435456 -2023/11/02-13:17:00.665155 8055427840 Options.blob_compression_type: NoCompression -2023/11/02-13:17:00.665156 8055427840 Options.enable_blob_garbage_collection: false -2023/11/02-13:17:00.665157 8055427840 Options.blob_garbage_collection_age_cutoff: 0.250000 -2023/11/02-13:17:00.665158 8055427840 Options.blob_garbage_collection_force_threshold: 1.000000 -2023/11/02-13:17:00.665159 8055427840 Options.blob_compaction_readahead_size: 0 -2023/11/02-13:17:00.665160 8055427840 Options.blob_file_starting_level: 0 -2023/11/02-13:17:00.665161 8055427840 Options.experimental_mempurge_threshold: 0.000000 -2023/11/02-13:17:00.666105 8055427840 [db/version_set.cc:5713] Recovered from manifest file:./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000014 succeeded,manifest_file_number is 14, next_file_number is 16, last_sequence is 5, log_number is 10,prev_log_number is 0,max_column_family is 0,min_log_number_to_keep is 5 -2023/11/02-13:17:00.666109 8055427840 [db/version_set.cc:5722] Column family [default] (ID 0), log number is 10 -2023/11/02-13:17:00.666162 8055427840 [db/db_impl/db_impl_open.cc:537] DB ID: 68030351-9558-4d6e-aa76-818928bc21b6 -2023/11/02-13:17:00.666313 8055427840 EVENT_LOG_v1 {"time_micros": 1698956220666308, "job": 1, "event": "recovery_started", "wal_files": [9, 13]} -2023/11/02-13:17:00.666316 8055427840 [db/db_impl/db_impl_open.cc:1018] Skipping log #9 since it is older than min log to keep #10 -2023/11/02-13:17:00.666318 8055427840 [db/db_impl/db_impl_open.cc:1031] Recovering log #13 mode 2 -2023/11/02-13:17:00.666390 8055427840 EVENT_LOG_v1 {"time_micros": 1698956220666388, "job": 1, "event": "recovery_finished"} -2023/11/02-13:17:00.666510 8055427840 [db/version_set.cc:5180] Creating manifest 18 -2023/11/02-13:17:00.670027 8055427840 [db/db_impl/db_impl_open.cc:1977] SstFileManager instance 0x11483dd20 -2023/11/02-13:17:00.670088 8055427840 DB pointer 0x117d50c00 -2023/11/02-13:17:00.670312 6127726592 [db/db_impl/db_impl.cc:1085] ------- DUMPING STATS ------- -2023/11/02-13:17:00.670317 6127726592 [db/db_impl/db_impl.cc:1086] -** DB Stats ** -Uptime(secs): 0.0 total, 0.0 interval -Cumulative writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s -Cumulative WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent -Interval writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 MB, 0.00 MB/s -Interval WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Interval stall: 00:00:0.000 H:M:S, 0.0 percent -Write Stall (count): write-buffer-manager-limit-stops: 0, -** Compaction Stats [default] ** -Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - L0 1/0 2.36 KB 0.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - Sum 1/0 2.36 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - -** Compaction Stats [default] ** -Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 - -Uptime(secs): 0.0 total, 0.0 interval -Flush(GB): cumulative 0.000, interval 0.000 -AddFile(GB): cumulative 0.000, interval 0.000 -AddFile(Total Files): cumulative 0, interval 0 -AddFile(L0 Files): cumulative 0, interval 0 -AddFile(Keys): cumulative 0, interval 0 -Cumulative compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Interval compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0x600002627798#20324 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 1 last_copies: 0 last_secs: 6.1e-05 secs_since: 0 -Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) - -** File Read Latency Histogram By Level [default] ** -2023/11/02-13:17:01.414265 8055427840 [db/db_impl/db_impl.cc:490] Shutdown: canceling all background work -2023/11/02-13:17:01.414681 8055427840 [db/db_impl/db_impl.cc:692] Shutdown complete diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698910458082437 b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698910458082437 deleted file mode 100644 index ec4a5b94a361d..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698910458082437 +++ /dev/null @@ -1,278 +0,0 @@ -2023/11/02-00:31:27.191544 8055427840 RocksDB version: 8.1.1 -2023/11/02-00:31:27.192301 8055427840 Compile date 2023-04-06 16:38:52 -2023/11/02-00:31:27.192304 8055427840 DB SUMMARY -2023/11/02-00:31:27.192305 8055427840 DB Session ID: ZA63LMYL22FIAA8L51WL -2023/11/02-00:31:27.192355 8055427840 SST files in ./test-data-mainnet-10m-15/rocks_txn_idx_db dir, Total Num: 0, files: -2023/11/02-00:31:27.192358 8055427840 Write Ahead Log file in ./test-data-mainnet-10m-15/rocks_txn_idx_db: -2023/11/02-00:31:27.192359 8055427840 Options.error_if_exists: 0 -2023/11/02-00:31:27.192360 8055427840 Options.create_if_missing: 1 -2023/11/02-00:31:27.192361 8055427840 Options.paranoid_checks: 1 -2023/11/02-00:31:27.192362 8055427840 Options.flush_verify_memtable_count: 1 -2023/11/02-00:31:27.192363 8055427840 Options.track_and_verify_wals_in_manifest: 0 -2023/11/02-00:31:27.192364 8055427840 Options.verify_sst_unique_id_in_manifest: 1 -2023/11/02-00:31:27.192365 8055427840 Options.env: 0x1056665f8 -2023/11/02-00:31:27.192366 8055427840 Options.fs: PosixFileSystem -2023/11/02-00:31:27.192367 8055427840 Options.info_log: 0x131804c38 -2023/11/02-00:31:27.192368 8055427840 Options.max_file_opening_threads: 16 -2023/11/02-00:31:27.192369 8055427840 Options.statistics: 0x0 -2023/11/02-00:31:27.192370 8055427840 Options.use_fsync: 0 -2023/11/02-00:31:27.192371 8055427840 Options.max_log_file_size: 0 -2023/11/02-00:31:27.192372 8055427840 Options.max_manifest_file_size: 1073741824 -2023/11/02-00:31:27.192373 8055427840 Options.log_file_time_to_roll: 0 -2023/11/02-00:31:27.192374 8055427840 Options.keep_log_file_num: 1000 -2023/11/02-00:31:27.192375 8055427840 Options.recycle_log_file_num: 0 -2023/11/02-00:31:27.192375 8055427840 Options.allow_fallocate: 1 -2023/11/02-00:31:27.192376 8055427840 Options.allow_mmap_reads: 0 -2023/11/02-00:31:27.192377 8055427840 Options.allow_mmap_writes: 0 -2023/11/02-00:31:27.192378 8055427840 Options.use_direct_reads: 0 -2023/11/02-00:31:27.192379 8055427840 Options.use_direct_io_for_flush_and_compaction: 0 -2023/11/02-00:31:27.192380 8055427840 Options.create_missing_column_families: 0 -2023/11/02-00:31:27.192381 8055427840 Options.db_log_dir: -2023/11/02-00:31:27.192382 8055427840 Options.wal_dir: -2023/11/02-00:31:27.192383 8055427840 Options.table_cache_numshardbits: 6 -2023/11/02-00:31:27.192384 8055427840 Options.WAL_ttl_seconds: 0 -2023/11/02-00:31:27.192384 8055427840 Options.WAL_size_limit_MB: 0 -2023/11/02-00:31:27.192385 8055427840 Options.max_write_batch_group_size_bytes: 1048576 -2023/11/02-00:31:27.192386 8055427840 Options.manifest_preallocation_size: 4194304 -2023/11/02-00:31:27.192387 8055427840 Options.is_fd_close_on_exec: 1 -2023/11/02-00:31:27.192388 8055427840 Options.advise_random_on_open: 1 -2023/11/02-00:31:27.192389 8055427840 Options.db_write_buffer_size: 0 -2023/11/02-00:31:27.192390 8055427840 Options.write_buffer_manager: 0x6000039b4000 -2023/11/02-00:31:27.192391 8055427840 Options.access_hint_on_compaction_start: 1 -2023/11/02-00:31:27.192392 8055427840 Options.random_access_max_buffer_size: 1048576 -2023/11/02-00:31:27.192393 8055427840 Options.use_adaptive_mutex: 0 -2023/11/02-00:31:27.192394 8055427840 Options.rate_limiter: 0x0 -2023/11/02-00:31:27.192395 8055427840 Options.sst_file_manager.rate_bytes_per_sec: 0 -2023/11/02-00:31:27.192396 8055427840 Options.wal_recovery_mode: 2 -2023/11/02-00:31:27.192397 8055427840 Options.enable_thread_tracking: 0 -2023/11/02-00:31:27.192398 8055427840 Options.enable_pipelined_write: 0 -2023/11/02-00:31:27.192399 8055427840 Options.unordered_write: 0 -2023/11/02-00:31:27.192399 8055427840 Options.allow_concurrent_memtable_write: 1 -2023/11/02-00:31:27.192400 8055427840 Options.enable_write_thread_adaptive_yield: 1 -2023/11/02-00:31:27.192401 8055427840 Options.write_thread_max_yield_usec: 100 -2023/11/02-00:31:27.192402 8055427840 Options.write_thread_slow_yield_usec: 3 -2023/11/02-00:31:27.192403 8055427840 Options.row_cache: None -2023/11/02-00:31:27.192404 8055427840 Options.wal_filter: None -2023/11/02-00:31:27.192405 8055427840 Options.avoid_flush_during_recovery: 0 -2023/11/02-00:31:27.192406 8055427840 Options.allow_ingest_behind: 0 -2023/11/02-00:31:27.192407 8055427840 Options.two_write_queues: 0 -2023/11/02-00:31:27.192408 8055427840 Options.manual_wal_flush: 0 -2023/11/02-00:31:27.192409 8055427840 Options.wal_compression: 0 -2023/11/02-00:31:27.192410 8055427840 Options.atomic_flush: 0 -2023/11/02-00:31:27.192410 8055427840 Options.avoid_unnecessary_blocking_io: 0 -2023/11/02-00:31:27.192411 8055427840 Options.persist_stats_to_disk: 0 -2023/11/02-00:31:27.192412 8055427840 Options.write_dbid_to_manifest: 0 -2023/11/02-00:31:27.192413 8055427840 Options.log_readahead_size: 0 -2023/11/02-00:31:27.192414 8055427840 Options.file_checksum_gen_factory: Unknown -2023/11/02-00:31:27.192425 8055427840 Options.best_efforts_recovery: 0 -2023/11/02-00:31:27.192426 8055427840 Options.max_bgerror_resume_count: 2147483647 -2023/11/02-00:31:27.192427 8055427840 Options.bgerror_resume_retry_interval: 1000000 -2023/11/02-00:31:27.192428 8055427840 Options.allow_data_in_errors: 0 -2023/11/02-00:31:27.192429 8055427840 Options.db_host_id: __hostname__ -2023/11/02-00:31:27.192430 8055427840 Options.enforce_single_del_contracts: true -2023/11/02-00:31:27.192431 8055427840 Options.max_background_jobs: 2 -2023/11/02-00:31:27.192432 8055427840 Options.max_background_compactions: -1 -2023/11/02-00:31:27.192432 8055427840 Options.max_subcompactions: 1 -2023/11/02-00:31:27.192433 8055427840 Options.avoid_flush_during_shutdown: 0 -2023/11/02-00:31:27.192434 8055427840 Options.writable_file_max_buffer_size: 1048576 -2023/11/02-00:31:27.192435 8055427840 Options.delayed_write_rate : 16777216 -2023/11/02-00:31:27.192436 8055427840 Options.max_total_wal_size: 0 -2023/11/02-00:31:27.192437 8055427840 Options.delete_obsolete_files_period_micros: 21600000000 -2023/11/02-00:31:27.192438 8055427840 Options.stats_dump_period_sec: 600 -2023/11/02-00:31:27.192439 8055427840 Options.stats_persist_period_sec: 600 -2023/11/02-00:31:27.192440 8055427840 Options.stats_history_buffer_size: 1048576 -2023/11/02-00:31:27.192441 8055427840 Options.max_open_files: -1 -2023/11/02-00:31:27.192441 8055427840 Options.bytes_per_sync: 0 -2023/11/02-00:31:27.192442 8055427840 Options.wal_bytes_per_sync: 0 -2023/11/02-00:31:27.192443 8055427840 Options.strict_bytes_per_sync: 0 -2023/11/02-00:31:27.192444 8055427840 Options.compaction_readahead_size: 0 -2023/11/02-00:31:27.192445 8055427840 Options.max_background_flushes: -1 -2023/11/02-00:31:27.192446 8055427840 Compression algorithms supported: -2023/11/02-00:31:27.192458 8055427840 kZSTD supported: 1 -2023/11/02-00:31:27.192459 8055427840 kZlibCompression supported: 1 -2023/11/02-00:31:27.192461 8055427840 kXpressCompression supported: 0 -2023/11/02-00:31:27.192462 8055427840 kSnappyCompression supported: 1 -2023/11/02-00:31:27.192463 8055427840 kZSTDNotFinalCompression supported: 1 -2023/11/02-00:31:27.192464 8055427840 kLZ4HCCompression supported: 1 -2023/11/02-00:31:27.192465 8055427840 kLZ4Compression supported: 1 -2023/11/02-00:31:27.192466 8055427840 kBZip2Compression supported: 1 -2023/11/02-00:31:27.192471 8055427840 Fast CRC32 supported: Supported on Arm64 -2023/11/02-00:31:27.192472 8055427840 DMutex implementation: pthread_mutex_t -2023/11/02-00:31:27.192803 8055427840 [db/db_impl/db_impl_open.cc:315] Creating manifest 1 -2023/11/02-00:31:27.193267 8055427840 [db/version_set.cc:5662] Recovering from manifest file: ./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000001 -2023/11/02-00:31:27.193518 8055427840 [db/column_family.cc:621] --------------- Options for column family [default]: -2023/11/02-00:31:27.193521 8055427840 Options.comparator: leveldb.BytewiseComparator -2023/11/02-00:31:27.193522 8055427840 Options.merge_operator: None -2023/11/02-00:31:27.193523 8055427840 Options.compaction_filter: None -2023/11/02-00:31:27.193524 8055427840 Options.compaction_filter_factory: None -2023/11/02-00:31:27.193525 8055427840 Options.sst_partitioner_factory: None -2023/11/02-00:31:27.193526 8055427840 Options.memtable_factory: SkipListFactory -2023/11/02-00:31:27.193527 8055427840 Options.table_factory: BlockBasedTable -2023/11/02-00:31:27.193552 8055427840 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0x6000002bc2a0) - cache_index_and_filter_blocks: 0 - cache_index_and_filter_blocks_with_high_priority: 1 - pin_l0_filter_and_index_blocks_in_cache: 0 - pin_top_level_index_and_filter: 1 - index_type: 0 - data_block_index_type: 0 - index_shortening: 1 - data_block_hash_table_util_ratio: 0.750000 - checksum: 4 - no_block_cache: 0 - block_cache: 0x6000037b40d8 - block_cache_name: LRUCache - block_cache_options: - capacity : 8388608 - num_shard_bits : 4 - strict_capacity_limit : 0 - memory_allocator : None - high_pri_pool_ratio: 0.000 - low_pri_pool_ratio: 0.000 - persistent_cache: 0x0 - block_size: 4096 - block_size_deviation: 10 - block_restart_interval: 16 - index_block_restart_interval: 1 - metadata_block_size: 4096 - partition_filters: 0 - use_delta_encoding: 1 - filter_policy: nullptr - whole_key_filtering: 1 - verify_compression: 0 - read_amp_bytes_per_bit: 0 - format_version: 5 - enable_index_compression: 1 - block_align: 0 - max_auto_readahead_size: 262144 - prepopulate_block_cache: 0 - initial_auto_readahead_size: 8192 - num_file_reads_for_auto_readahead: 2 -2023/11/02-00:31:27.193554 8055427840 Options.write_buffer_size: 67108864 -2023/11/02-00:31:27.193555 8055427840 Options.max_write_buffer_number: 2 -2023/11/02-00:31:27.193556 8055427840 Options.compression: Snappy -2023/11/02-00:31:27.193557 8055427840 Options.bottommost_compression: Disabled -2023/11/02-00:31:27.193558 8055427840 Options.prefix_extractor: nullptr -2023/11/02-00:31:27.193558 8055427840 Options.memtable_insert_with_hint_prefix_extractor: nullptr -2023/11/02-00:31:27.193559 8055427840 Options.num_levels: 7 -2023/11/02-00:31:27.193560 8055427840 Options.min_write_buffer_number_to_merge: 1 -2023/11/02-00:31:27.193561 8055427840 Options.max_write_buffer_number_to_maintain: 0 -2023/11/02-00:31:27.193562 8055427840 Options.max_write_buffer_size_to_maintain: 0 -2023/11/02-00:31:27.193563 8055427840 Options.bottommost_compression_opts.window_bits: -14 -2023/11/02-00:31:27.193564 8055427840 Options.bottommost_compression_opts.level: 32767 -2023/11/02-00:31:27.193565 8055427840 Options.bottommost_compression_opts.strategy: 0 -2023/11/02-00:31:27.193566 8055427840 Options.bottommost_compression_opts.max_dict_bytes: 0 -2023/11/02-00:31:27.193566 8055427840 Options.bottommost_compression_opts.zstd_max_train_bytes: 0 -2023/11/02-00:31:27.193567 8055427840 Options.bottommost_compression_opts.parallel_threads: 1 -2023/11/02-00:31:27.193568 8055427840 Options.bottommost_compression_opts.enabled: false -2023/11/02-00:31:27.193569 8055427840 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-00:31:27.193570 8055427840 Options.bottommost_compression_opts.use_zstd_dict_trainer: true -2023/11/02-00:31:27.193571 8055427840 Options.compression_opts.window_bits: -14 -2023/11/02-00:31:27.193572 8055427840 Options.compression_opts.level: 32767 -2023/11/02-00:31:27.193573 8055427840 Options.compression_opts.strategy: 0 -2023/11/02-00:31:27.193574 8055427840 Options.compression_opts.max_dict_bytes: 0 -2023/11/02-00:31:27.193574 8055427840 Options.compression_opts.zstd_max_train_bytes: 0 -2023/11/02-00:31:27.193575 8055427840 Options.compression_opts.use_zstd_dict_trainer: true -2023/11/02-00:31:27.193576 8055427840 Options.compression_opts.parallel_threads: 1 -2023/11/02-00:31:27.193577 8055427840 Options.compression_opts.enabled: false -2023/11/02-00:31:27.193578 8055427840 Options.compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-00:31:27.193579 8055427840 Options.level0_file_num_compaction_trigger: 4 -2023/11/02-00:31:27.193580 8055427840 Options.level0_slowdown_writes_trigger: 20 -2023/11/02-00:31:27.193581 8055427840 Options.level0_stop_writes_trigger: 36 -2023/11/02-00:31:27.193582 8055427840 Options.target_file_size_base: 67108864 -2023/11/02-00:31:27.193582 8055427840 Options.target_file_size_multiplier: 1 -2023/11/02-00:31:27.193583 8055427840 Options.max_bytes_for_level_base: 268435456 -2023/11/02-00:31:27.193584 8055427840 Options.level_compaction_dynamic_level_bytes: 0 -2023/11/02-00:31:27.193585 8055427840 Options.max_bytes_for_level_multiplier: 10.000000 -2023/11/02-00:31:27.193586 8055427840 Options.max_bytes_for_level_multiplier_addtl[0]: 1 -2023/11/02-00:31:27.193587 8055427840 Options.max_bytes_for_level_multiplier_addtl[1]: 1 -2023/11/02-00:31:27.193588 8055427840 Options.max_bytes_for_level_multiplier_addtl[2]: 1 -2023/11/02-00:31:27.193589 8055427840 Options.max_bytes_for_level_multiplier_addtl[3]: 1 -2023/11/02-00:31:27.193590 8055427840 Options.max_bytes_for_level_multiplier_addtl[4]: 1 -2023/11/02-00:31:27.193591 8055427840 Options.max_bytes_for_level_multiplier_addtl[5]: 1 -2023/11/02-00:31:27.193591 8055427840 Options.max_bytes_for_level_multiplier_addtl[6]: 1 -2023/11/02-00:31:27.193592 8055427840 Options.max_sequential_skip_in_iterations: 8 -2023/11/02-00:31:27.193593 8055427840 Options.max_compaction_bytes: 1677721600 -2023/11/02-00:31:27.193594 8055427840 Options.ignore_max_compaction_bytes_for_input: true -2023/11/02-00:31:27.193595 8055427840 Options.arena_block_size: 1048576 -2023/11/02-00:31:27.193596 8055427840 Options.soft_pending_compaction_bytes_limit: 68719476736 -2023/11/02-00:31:27.193597 8055427840 Options.hard_pending_compaction_bytes_limit: 274877906944 -2023/11/02-00:31:27.193597 8055427840 Options.disable_auto_compactions: 0 -2023/11/02-00:31:27.193599 8055427840 Options.compaction_style: kCompactionStyleLevel -2023/11/02-00:31:27.193601 8055427840 Options.compaction_pri: kMinOverlappingRatio -2023/11/02-00:31:27.193601 8055427840 Options.compaction_options_universal.size_ratio: 1 -2023/11/02-00:31:27.193602 8055427840 Options.compaction_options_universal.min_merge_width: 2 -2023/11/02-00:31:27.193603 8055427840 Options.compaction_options_universal.max_merge_width: 4294967295 -2023/11/02-00:31:27.193604 8055427840 Options.compaction_options_universal.max_size_amplification_percent: 200 -2023/11/02-00:31:27.193605 8055427840 Options.compaction_options_universal.compression_size_percent: -1 -2023/11/02-00:31:27.193607 8055427840 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize -2023/11/02-00:31:27.193608 8055427840 Options.compaction_options_fifo.max_table_files_size: 1073741824 -2023/11/02-00:31:27.193610 8055427840 Options.compaction_options_fifo.allow_compaction: 0 -2023/11/02-00:31:27.193612 8055427840 Options.table_properties_collectors: -2023/11/02-00:31:27.193612 8055427840 Options.inplace_update_support: 0 -2023/11/02-00:31:27.193613 8055427840 Options.inplace_update_num_locks: 10000 -2023/11/02-00:31:27.193614 8055427840 Options.memtable_prefix_bloom_size_ratio: 0.000000 -2023/11/02-00:31:27.193615 8055427840 Options.memtable_whole_key_filtering: 0 -2023/11/02-00:31:27.193616 8055427840 Options.memtable_huge_page_size: 0 -2023/11/02-00:31:27.193617 8055427840 Options.bloom_locality: 0 -2023/11/02-00:31:27.193618 8055427840 Options.max_successive_merges: 0 -2023/11/02-00:31:27.193619 8055427840 Options.optimize_filters_for_hits: 0 -2023/11/02-00:31:27.193619 8055427840 Options.paranoid_file_checks: 0 -2023/11/02-00:31:27.193620 8055427840 Options.force_consistency_checks: 1 -2023/11/02-00:31:27.193621 8055427840 Options.report_bg_io_stats: 0 -2023/11/02-00:31:27.193622 8055427840 Options.ttl: 2592000 -2023/11/02-00:31:27.193623 8055427840 Options.periodic_compaction_seconds: 0 -2023/11/02-00:31:27.193624 8055427840 Options.preclude_last_level_data_seconds: 0 -2023/11/02-00:31:27.193625 8055427840 Options.preserve_internal_time_seconds: 0 -2023/11/02-00:31:27.193626 8055427840 Options.enable_blob_files: false -2023/11/02-00:31:27.193626 8055427840 Options.min_blob_size: 0 -2023/11/02-00:31:27.193627 8055427840 Options.blob_file_size: 268435456 -2023/11/02-00:31:27.193628 8055427840 Options.blob_compression_type: NoCompression -2023/11/02-00:31:27.193629 8055427840 Options.enable_blob_garbage_collection: false -2023/11/02-00:31:27.193630 8055427840 Options.blob_garbage_collection_age_cutoff: 0.250000 -2023/11/02-00:31:27.193631 8055427840 Options.blob_garbage_collection_force_threshold: 1.000000 -2023/11/02-00:31:27.193632 8055427840 Options.blob_compaction_readahead_size: 0 -2023/11/02-00:31:27.193633 8055427840 Options.blob_file_starting_level: 0 -2023/11/02-00:31:27.193634 8055427840 Options.experimental_mempurge_threshold: 0.000000 -2023/11/02-00:31:27.194427 8055427840 [db/version_set.cc:5713] Recovered from manifest file:./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000001 succeeded,manifest_file_number is 1, next_file_number is 3, last_sequence is 0, log_number is 0,prev_log_number is 0,max_column_family is 0,min_log_number_to_keep is 0 -2023/11/02-00:31:27.194437 8055427840 [db/version_set.cc:5722] Column family [default] (ID 0), log number is 0 -2023/11/02-00:31:27.194528 8055427840 [db/db_impl/db_impl_open.cc:537] DB ID: 68030351-9558-4d6e-aa76-818928bc21b6 -2023/11/02-00:31:27.194912 8055427840 [db/version_set.cc:5180] Creating manifest 5 -2023/11/02-00:31:27.197797 8055427840 [db/db_impl/db_impl_open.cc:1977] SstFileManager instance 0x131804e80 -2023/11/02-00:31:27.197849 8055427840 DB pointer 0x13100e000 -2023/11/02-00:31:27.198111 6182547456 [db/db_impl/db_impl.cc:1085] ------- DUMPING STATS ------- -2023/11/02-00:31:27.198115 6182547456 [db/db_impl/db_impl.cc:1086] -** DB Stats ** -Uptime(secs): 0.0 total, 0.0 interval -Cumulative writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s -Cumulative WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent -Interval writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 MB, 0.00 MB/s -Interval WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Interval stall: 00:00:0.000 H:M:S, 0.0 percent -Write Stall (count): write-buffer-manager-limit-stops: 0, -** Compaction Stats [default] ** -Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Sum 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - -** Compaction Stats [default] ** -Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 - -Uptime(secs): 0.0 total, 0.0 interval -Flush(GB): cumulative 0.000, interval 0.000 -AddFile(GB): cumulative 0.000, interval 0.000 -AddFile(Total Files): cumulative 0, interval 0 -AddFile(L0 Files): cumulative 0, interval 0 -AddFile(Keys): cumulative 0, interval 0 -Cumulative compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Interval compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0x6000037b40d8#81515 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 1 last_copies: 0 last_secs: 6.3e-05 secs_since: 0 -Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) - -** File Read Latency Histogram By Level [default] ** -2023/11/02-00:31:43.993905 8055427840 [db/db_impl/db_impl.cc:490] Shutdown: canceling all background work -2023/11/02-00:31:43.994367 8055427840 [db/db_impl/db_impl.cc:692] Shutdown complete diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956094329658 b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956094329658 deleted file mode 100644 index ab1d63b75c4e6..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956094329658 +++ /dev/null @@ -1,287 +0,0 @@ -2023/11/02-00:34:18.083113 8055427840 RocksDB version: 8.1.1 -2023/11/02-00:34:18.083632 8055427840 Compile date 2023-04-06 16:38:52 -2023/11/02-00:34:18.083635 8055427840 DB SUMMARY -2023/11/02-00:34:18.083636 8055427840 DB Session ID: QD13XW6AB2MM5DAATVM5 -2023/11/02-00:34:18.083688 8055427840 CURRENT file: CURRENT -2023/11/02-00:34:18.083690 8055427840 IDENTITY file: IDENTITY -2023/11/02-00:34:18.083695 8055427840 MANIFEST file: MANIFEST-000005 size: 66 Bytes -2023/11/02-00:34:18.083708 8055427840 SST files in ./test-data-mainnet-10m-15/rocks_txn_idx_db dir, Total Num: 0, files: -2023/11/02-00:34:18.083710 8055427840 Write Ahead Log file in ./test-data-mainnet-10m-15/rocks_txn_idx_db: 000004.log size: 2188 ; -2023/11/02-00:34:18.083711 8055427840 Options.error_if_exists: 0 -2023/11/02-00:34:18.083712 8055427840 Options.create_if_missing: 1 -2023/11/02-00:34:18.083713 8055427840 Options.paranoid_checks: 1 -2023/11/02-00:34:18.083714 8055427840 Options.flush_verify_memtable_count: 1 -2023/11/02-00:34:18.083715 8055427840 Options.track_and_verify_wals_in_manifest: 0 -2023/11/02-00:34:18.083716 8055427840 Options.verify_sst_unique_id_in_manifest: 1 -2023/11/02-00:34:18.083717 8055427840 Options.env: 0x104d125f8 -2023/11/02-00:34:18.083718 8055427840 Options.fs: PosixFileSystem -2023/11/02-00:34:18.083719 8055427840 Options.info_log: 0x150f09828 -2023/11/02-00:34:18.083720 8055427840 Options.max_file_opening_threads: 16 -2023/11/02-00:34:18.083721 8055427840 Options.statistics: 0x0 -2023/11/02-00:34:18.083722 8055427840 Options.use_fsync: 0 -2023/11/02-00:34:18.083723 8055427840 Options.max_log_file_size: 0 -2023/11/02-00:34:18.083724 8055427840 Options.max_manifest_file_size: 1073741824 -2023/11/02-00:34:18.083724 8055427840 Options.log_file_time_to_roll: 0 -2023/11/02-00:34:18.083725 8055427840 Options.keep_log_file_num: 1000 -2023/11/02-00:34:18.083726 8055427840 Options.recycle_log_file_num: 0 -2023/11/02-00:34:18.083727 8055427840 Options.allow_fallocate: 1 -2023/11/02-00:34:18.083728 8055427840 Options.allow_mmap_reads: 0 -2023/11/02-00:34:18.083729 8055427840 Options.allow_mmap_writes: 0 -2023/11/02-00:34:18.083730 8055427840 Options.use_direct_reads: 0 -2023/11/02-00:34:18.083731 8055427840 Options.use_direct_io_for_flush_and_compaction: 0 -2023/11/02-00:34:18.083732 8055427840 Options.create_missing_column_families: 0 -2023/11/02-00:34:18.083733 8055427840 Options.db_log_dir: -2023/11/02-00:34:18.083733 8055427840 Options.wal_dir: -2023/11/02-00:34:18.083734 8055427840 Options.table_cache_numshardbits: 6 -2023/11/02-00:34:18.083735 8055427840 Options.WAL_ttl_seconds: 0 -2023/11/02-00:34:18.083736 8055427840 Options.WAL_size_limit_MB: 0 -2023/11/02-00:34:18.083737 8055427840 Options.max_write_batch_group_size_bytes: 1048576 -2023/11/02-00:34:18.083738 8055427840 Options.manifest_preallocation_size: 4194304 -2023/11/02-00:34:18.083739 8055427840 Options.is_fd_close_on_exec: 1 -2023/11/02-00:34:18.083740 8055427840 Options.advise_random_on_open: 1 -2023/11/02-00:34:18.083741 8055427840 Options.db_write_buffer_size: 0 -2023/11/02-00:34:18.083741 8055427840 Options.write_buffer_manager: 0x600001e90750 -2023/11/02-00:34:18.083742 8055427840 Options.access_hint_on_compaction_start: 1 -2023/11/02-00:34:18.083743 8055427840 Options.random_access_max_buffer_size: 1048576 -2023/11/02-00:34:18.083744 8055427840 Options.use_adaptive_mutex: 0 -2023/11/02-00:34:18.083745 8055427840 Options.rate_limiter: 0x0 -2023/11/02-00:34:18.083746 8055427840 Options.sst_file_manager.rate_bytes_per_sec: 0 -2023/11/02-00:34:18.083747 8055427840 Options.wal_recovery_mode: 2 -2023/11/02-00:34:18.083748 8055427840 Options.enable_thread_tracking: 0 -2023/11/02-00:34:18.083749 8055427840 Options.enable_pipelined_write: 0 -2023/11/02-00:34:18.083750 8055427840 Options.unordered_write: 0 -2023/11/02-00:34:18.083751 8055427840 Options.allow_concurrent_memtable_write: 1 -2023/11/02-00:34:18.083752 8055427840 Options.enable_write_thread_adaptive_yield: 1 -2023/11/02-00:34:18.083753 8055427840 Options.write_thread_max_yield_usec: 100 -2023/11/02-00:34:18.083754 8055427840 Options.write_thread_slow_yield_usec: 3 -2023/11/02-00:34:18.083754 8055427840 Options.row_cache: None -2023/11/02-00:34:18.083755 8055427840 Options.wal_filter: None -2023/11/02-00:34:18.083756 8055427840 Options.avoid_flush_during_recovery: 0 -2023/11/02-00:34:18.083757 8055427840 Options.allow_ingest_behind: 0 -2023/11/02-00:34:18.083758 8055427840 Options.two_write_queues: 0 -2023/11/02-00:34:18.083759 8055427840 Options.manual_wal_flush: 0 -2023/11/02-00:34:18.083760 8055427840 Options.wal_compression: 0 -2023/11/02-00:34:18.083761 8055427840 Options.atomic_flush: 0 -2023/11/02-00:34:18.083762 8055427840 Options.avoid_unnecessary_blocking_io: 0 -2023/11/02-00:34:18.083763 8055427840 Options.persist_stats_to_disk: 0 -2023/11/02-00:34:18.083764 8055427840 Options.write_dbid_to_manifest: 0 -2023/11/02-00:34:18.083765 8055427840 Options.log_readahead_size: 0 -2023/11/02-00:34:18.083766 8055427840 Options.file_checksum_gen_factory: Unknown -2023/11/02-00:34:18.083777 8055427840 Options.best_efforts_recovery: 0 -2023/11/02-00:34:18.083778 8055427840 Options.max_bgerror_resume_count: 2147483647 -2023/11/02-00:34:18.083779 8055427840 Options.bgerror_resume_retry_interval: 1000000 -2023/11/02-00:34:18.083780 8055427840 Options.allow_data_in_errors: 0 -2023/11/02-00:34:18.083781 8055427840 Options.db_host_id: __hostname__ -2023/11/02-00:34:18.083782 8055427840 Options.enforce_single_del_contracts: true -2023/11/02-00:34:18.083783 8055427840 Options.max_background_jobs: 2 -2023/11/02-00:34:18.083784 8055427840 Options.max_background_compactions: -1 -2023/11/02-00:34:18.083785 8055427840 Options.max_subcompactions: 1 -2023/11/02-00:34:18.083786 8055427840 Options.avoid_flush_during_shutdown: 0 -2023/11/02-00:34:18.083787 8055427840 Options.writable_file_max_buffer_size: 1048576 -2023/11/02-00:34:18.083788 8055427840 Options.delayed_write_rate : 16777216 -2023/11/02-00:34:18.083789 8055427840 Options.max_total_wal_size: 0 -2023/11/02-00:34:18.083789 8055427840 Options.delete_obsolete_files_period_micros: 21600000000 -2023/11/02-00:34:18.083790 8055427840 Options.stats_dump_period_sec: 600 -2023/11/02-00:34:18.083791 8055427840 Options.stats_persist_period_sec: 600 -2023/11/02-00:34:18.083792 8055427840 Options.stats_history_buffer_size: 1048576 -2023/11/02-00:34:18.083793 8055427840 Options.max_open_files: -1 -2023/11/02-00:34:18.083794 8055427840 Options.bytes_per_sync: 0 -2023/11/02-00:34:18.083795 8055427840 Options.wal_bytes_per_sync: 0 -2023/11/02-00:34:18.083796 8055427840 Options.strict_bytes_per_sync: 0 -2023/11/02-00:34:18.083797 8055427840 Options.compaction_readahead_size: 0 -2023/11/02-00:34:18.083798 8055427840 Options.max_background_flushes: -1 -2023/11/02-00:34:18.083799 8055427840 Compression algorithms supported: -2023/11/02-00:34:18.083812 8055427840 kZSTD supported: 1 -2023/11/02-00:34:18.083813 8055427840 kZlibCompression supported: 1 -2023/11/02-00:34:18.083814 8055427840 kXpressCompression supported: 0 -2023/11/02-00:34:18.083815 8055427840 kSnappyCompression supported: 1 -2023/11/02-00:34:18.083816 8055427840 kZSTDNotFinalCompression supported: 1 -2023/11/02-00:34:18.083817 8055427840 kLZ4HCCompression supported: 1 -2023/11/02-00:34:18.083818 8055427840 kLZ4Compression supported: 1 -2023/11/02-00:34:18.083819 8055427840 kBZip2Compression supported: 1 -2023/11/02-00:34:18.083825 8055427840 Fast CRC32 supported: Supported on Arm64 -2023/11/02-00:34:18.083826 8055427840 DMutex implementation: pthread_mutex_t -2023/11/02-00:34:18.083963 8055427840 [db/version_set.cc:5662] Recovering from manifest file: ./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000005 -2023/11/02-00:34:18.084209 8055427840 [db/column_family.cc:621] --------------- Options for column family [default]: -2023/11/02-00:34:18.084212 8055427840 Options.comparator: leveldb.BytewiseComparator -2023/11/02-00:34:18.084213 8055427840 Options.merge_operator: None -2023/11/02-00:34:18.084214 8055427840 Options.compaction_filter: None -2023/11/02-00:34:18.084215 8055427840 Options.compaction_filter_factory: None -2023/11/02-00:34:18.084216 8055427840 Options.sst_partitioner_factory: None -2023/11/02-00:34:18.084217 8055427840 Options.memtable_factory: SkipListFactory -2023/11/02-00:34:18.084218 8055427840 Options.table_factory: BlockBasedTable -2023/11/02-00:34:18.084240 8055427840 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0x6000025969c0) - cache_index_and_filter_blocks: 0 - cache_index_and_filter_blocks_with_high_priority: 1 - pin_l0_filter_and_index_blocks_in_cache: 0 - pin_top_level_index_and_filter: 1 - index_type: 0 - data_block_index_type: 0 - index_shortening: 1 - data_block_hash_table_util_ratio: 0.750000 - checksum: 4 - no_block_cache: 0 - block_cache: 0x600001094798 - block_cache_name: LRUCache - block_cache_options: - capacity : 8388608 - num_shard_bits : 4 - strict_capacity_limit : 0 - memory_allocator : None - high_pri_pool_ratio: 0.000 - low_pri_pool_ratio: 0.000 - persistent_cache: 0x0 - block_size: 4096 - block_size_deviation: 10 - block_restart_interval: 16 - index_block_restart_interval: 1 - metadata_block_size: 4096 - partition_filters: 0 - use_delta_encoding: 1 - filter_policy: nullptr - whole_key_filtering: 1 - verify_compression: 0 - read_amp_bytes_per_bit: 0 - format_version: 5 - enable_index_compression: 1 - block_align: 0 - max_auto_readahead_size: 262144 - prepopulate_block_cache: 0 - initial_auto_readahead_size: 8192 - num_file_reads_for_auto_readahead: 2 -2023/11/02-00:34:18.084241 8055427840 Options.write_buffer_size: 67108864 -2023/11/02-00:34:18.084242 8055427840 Options.max_write_buffer_number: 2 -2023/11/02-00:34:18.084244 8055427840 Options.compression: Snappy -2023/11/02-00:34:18.084245 8055427840 Options.bottommost_compression: Disabled -2023/11/02-00:34:18.084246 8055427840 Options.prefix_extractor: nullptr -2023/11/02-00:34:18.084247 8055427840 Options.memtable_insert_with_hint_prefix_extractor: nullptr -2023/11/02-00:34:18.084248 8055427840 Options.num_levels: 7 -2023/11/02-00:34:18.084249 8055427840 Options.min_write_buffer_number_to_merge: 1 -2023/11/02-00:34:18.084250 8055427840 Options.max_write_buffer_number_to_maintain: 0 -2023/11/02-00:34:18.084250 8055427840 Options.max_write_buffer_size_to_maintain: 0 -2023/11/02-00:34:18.084251 8055427840 Options.bottommost_compression_opts.window_bits: -14 -2023/11/02-00:34:18.084252 8055427840 Options.bottommost_compression_opts.level: 32767 -2023/11/02-00:34:18.084253 8055427840 Options.bottommost_compression_opts.strategy: 0 -2023/11/02-00:34:18.084254 8055427840 Options.bottommost_compression_opts.max_dict_bytes: 0 -2023/11/02-00:34:18.084255 8055427840 Options.bottommost_compression_opts.zstd_max_train_bytes: 0 -2023/11/02-00:34:18.084256 8055427840 Options.bottommost_compression_opts.parallel_threads: 1 -2023/11/02-00:34:18.084257 8055427840 Options.bottommost_compression_opts.enabled: false -2023/11/02-00:34:18.084258 8055427840 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-00:34:18.084259 8055427840 Options.bottommost_compression_opts.use_zstd_dict_trainer: true -2023/11/02-00:34:18.084260 8055427840 Options.compression_opts.window_bits: -14 -2023/11/02-00:34:18.084261 8055427840 Options.compression_opts.level: 32767 -2023/11/02-00:34:18.084262 8055427840 Options.compression_opts.strategy: 0 -2023/11/02-00:34:18.084263 8055427840 Options.compression_opts.max_dict_bytes: 0 -2023/11/02-00:34:18.084264 8055427840 Options.compression_opts.zstd_max_train_bytes: 0 -2023/11/02-00:34:18.084265 8055427840 Options.compression_opts.use_zstd_dict_trainer: true -2023/11/02-00:34:18.084266 8055427840 Options.compression_opts.parallel_threads: 1 -2023/11/02-00:34:18.084267 8055427840 Options.compression_opts.enabled: false -2023/11/02-00:34:18.084268 8055427840 Options.compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-00:34:18.084269 8055427840 Options.level0_file_num_compaction_trigger: 4 -2023/11/02-00:34:18.084270 8055427840 Options.level0_slowdown_writes_trigger: 20 -2023/11/02-00:34:18.084271 8055427840 Options.level0_stop_writes_trigger: 36 -2023/11/02-00:34:18.084272 8055427840 Options.target_file_size_base: 67108864 -2023/11/02-00:34:18.084273 8055427840 Options.target_file_size_multiplier: 1 -2023/11/02-00:34:18.084274 8055427840 Options.max_bytes_for_level_base: 268435456 -2023/11/02-00:34:18.084274 8055427840 Options.level_compaction_dynamic_level_bytes: 0 -2023/11/02-00:34:18.084275 8055427840 Options.max_bytes_for_level_multiplier: 10.000000 -2023/11/02-00:34:18.084276 8055427840 Options.max_bytes_for_level_multiplier_addtl[0]: 1 -2023/11/02-00:34:18.084277 8055427840 Options.max_bytes_for_level_multiplier_addtl[1]: 1 -2023/11/02-00:34:18.084278 8055427840 Options.max_bytes_for_level_multiplier_addtl[2]: 1 -2023/11/02-00:34:18.084279 8055427840 Options.max_bytes_for_level_multiplier_addtl[3]: 1 -2023/11/02-00:34:18.084280 8055427840 Options.max_bytes_for_level_multiplier_addtl[4]: 1 -2023/11/02-00:34:18.084281 8055427840 Options.max_bytes_for_level_multiplier_addtl[5]: 1 -2023/11/02-00:34:18.084282 8055427840 Options.max_bytes_for_level_multiplier_addtl[6]: 1 -2023/11/02-00:34:18.084283 8055427840 Options.max_sequential_skip_in_iterations: 8 -2023/11/02-00:34:18.084284 8055427840 Options.max_compaction_bytes: 1677721600 -2023/11/02-00:34:18.084285 8055427840 Options.ignore_max_compaction_bytes_for_input: true -2023/11/02-00:34:18.084286 8055427840 Options.arena_block_size: 1048576 -2023/11/02-00:34:18.084287 8055427840 Options.soft_pending_compaction_bytes_limit: 68719476736 -2023/11/02-00:34:18.084288 8055427840 Options.hard_pending_compaction_bytes_limit: 274877906944 -2023/11/02-00:34:18.084288 8055427840 Options.disable_auto_compactions: 0 -2023/11/02-00:34:18.084290 8055427840 Options.compaction_style: kCompactionStyleLevel -2023/11/02-00:34:18.084292 8055427840 Options.compaction_pri: kMinOverlappingRatio -2023/11/02-00:34:18.084292 8055427840 Options.compaction_options_universal.size_ratio: 1 -2023/11/02-00:34:18.084293 8055427840 Options.compaction_options_universal.min_merge_width: 2 -2023/11/02-00:34:18.084294 8055427840 Options.compaction_options_universal.max_merge_width: 4294967295 -2023/11/02-00:34:18.084295 8055427840 Options.compaction_options_universal.max_size_amplification_percent: 200 -2023/11/02-00:34:18.084296 8055427840 Options.compaction_options_universal.compression_size_percent: -1 -2023/11/02-00:34:18.084298 8055427840 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize -2023/11/02-00:34:18.084300 8055427840 Options.compaction_options_fifo.max_table_files_size: 1073741824 -2023/11/02-00:34:18.084301 8055427840 Options.compaction_options_fifo.allow_compaction: 0 -2023/11/02-00:34:18.084303 8055427840 Options.table_properties_collectors: -2023/11/02-00:34:18.084304 8055427840 Options.inplace_update_support: 0 -2023/11/02-00:34:18.084305 8055427840 Options.inplace_update_num_locks: 10000 -2023/11/02-00:34:18.084305 8055427840 Options.memtable_prefix_bloom_size_ratio: 0.000000 -2023/11/02-00:34:18.084306 8055427840 Options.memtable_whole_key_filtering: 0 -2023/11/02-00:34:18.084307 8055427840 Options.memtable_huge_page_size: 0 -2023/11/02-00:34:18.084308 8055427840 Options.bloom_locality: 0 -2023/11/02-00:34:18.084309 8055427840 Options.max_successive_merges: 0 -2023/11/02-00:34:18.084310 8055427840 Options.optimize_filters_for_hits: 0 -2023/11/02-00:34:18.084311 8055427840 Options.paranoid_file_checks: 0 -2023/11/02-00:34:18.084312 8055427840 Options.force_consistency_checks: 1 -2023/11/02-00:34:18.084312 8055427840 Options.report_bg_io_stats: 0 -2023/11/02-00:34:18.084313 8055427840 Options.ttl: 2592000 -2023/11/02-00:34:18.084314 8055427840 Options.periodic_compaction_seconds: 0 -2023/11/02-00:34:18.084315 8055427840 Options.preclude_last_level_data_seconds: 0 -2023/11/02-00:34:18.084316 8055427840 Options.preserve_internal_time_seconds: 0 -2023/11/02-00:34:18.084317 8055427840 Options.enable_blob_files: false -2023/11/02-00:34:18.084318 8055427840 Options.min_blob_size: 0 -2023/11/02-00:34:18.084319 8055427840 Options.blob_file_size: 268435456 -2023/11/02-00:34:18.084320 8055427840 Options.blob_compression_type: NoCompression -2023/11/02-00:34:18.084320 8055427840 Options.enable_blob_garbage_collection: false -2023/11/02-00:34:18.084321 8055427840 Options.blob_garbage_collection_age_cutoff: 0.250000 -2023/11/02-00:34:18.084322 8055427840 Options.blob_garbage_collection_force_threshold: 1.000000 -2023/11/02-00:34:18.084323 8055427840 Options.blob_compaction_readahead_size: 0 -2023/11/02-00:34:18.084324 8055427840 Options.blob_file_starting_level: 0 -2023/11/02-00:34:18.084325 8055427840 Options.experimental_mempurge_threshold: 0.000000 -2023/11/02-00:34:18.084960 8055427840 [db/version_set.cc:5713] Recovered from manifest file:./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000005 succeeded,manifest_file_number is 5, next_file_number is 7, last_sequence is 0, log_number is 0,prev_log_number is 0,max_column_family is 0,min_log_number_to_keep is 0 -2023/11/02-00:34:18.084965 8055427840 [db/version_set.cc:5722] Column family [default] (ID 0), log number is 0 -2023/11/02-00:34:18.085017 8055427840 [db/db_impl/db_impl_open.cc:537] DB ID: 68030351-9558-4d6e-aa76-818928bc21b6 -2023/11/02-00:34:18.085174 8055427840 EVENT_LOG_v1 {"time_micros": 1698910458085169, "job": 1, "event": "recovery_started", "wal_files": [4]} -2023/11/02-00:34:18.085177 8055427840 [db/db_impl/db_impl_open.cc:1031] Recovering log #4 mode 2 -2023/11/02-00:34:18.086121 8055427840 EVENT_LOG_v1 {"time_micros": 1698910458086100, "cf_name": "default", "job": 1, "event": "table_file_creation", "file_number": 8, "file_size": 2413, "file_checksum": "", "file_checksum_func_name": "Unknown", "smallest_seqno": 1, "largest_seqno": 5, "table_properties": {"data_size": 1440, "index_size": 26, "index_partitions": 0, "top_level_index_size": 0, "index_key_is_user_key": 1, "index_value_is_delta_encoded": 1, "filter_size": 0, "raw_key_size": 80, "raw_average_key_size": 16, "raw_value_size": 2033, "raw_average_value_size": 406, "num_data_blocks": 1, "num_entries": 5, "num_filter_entries": 0, "num_deletions": 0, "num_merge_operands": 0, "num_range_deletions": 0, "format_version": 0, "fixed_key_len": 0, "filter_policy": "", "column_family_name": "default", "column_family_id": 0, "comparator": "leveldb.BytewiseComparator", "merge_operator": "nullptr", "prefix_extractor_name": "nullptr", "property_collectors": "[]", "compression": "Snappy", "compression_options": "window_bits=-14; level=32767; strategy=0; max_dict_bytes=0; zstd_max_train_bytes=0; enabled=0; max_dict_buffer_bytes=0; use_zstd_dict_trainer=1; ", "creation_time": 1698910458, "oldest_key_time": 0, "file_creation_time": 0, "slow_compression_estimated_data_size": 0, "fast_compression_estimated_data_size": 0, "db_id": "68030351-9558-4d6e-aa76-818928bc21b6", "db_session_id": "QD13XW6AB2MM5DAATVM5", "orig_file_number": 8, "seqno_to_time_mapping": "N/A"}} -2023/11/02-00:34:18.086205 8055427840 EVENT_LOG_v1 {"time_micros": 1698910458086203, "job": 1, "event": "recovery_finished"} -2023/11/02-00:34:18.086339 8055427840 [db/version_set.cc:5180] Creating manifest 10 -2023/11/02-00:34:18.089165 8055427840 [file/delete_scheduler.cc:77] Deleted file ./test-data-mainnet-10m-15/rocks_txn_idx_db/000004.log immediately, rate_bytes_per_sec 0, total_trash_size 0 max_trash_db_ratio 0.250000 -2023/11/02-00:34:18.089212 8055427840 [db/db_impl/db_impl_open.cc:1977] SstFileManager instance 0x150f09bb0 -2023/11/02-00:34:18.089265 8055427840 DB pointer 0x151826a00 -2023/11/02-00:34:18.089542 6192328704 [db/db_impl/db_impl.cc:1085] ------- DUMPING STATS ------- -2023/11/02-00:34:18.089582 6192328704 [db/db_impl/db_impl.cc:1086] -** DB Stats ** -Uptime(secs): 0.0 total, 0.0 interval -Cumulative writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s -Cumulative WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent -Interval writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 MB, 0.00 MB/s -Interval WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Interval stall: 00:00:0.000 H:M:S, 0.0 percent -Write Stall (count): write-buffer-manager-limit-stops: 0, -** Compaction Stats [default] ** -Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - L0 1/0 2.36 KB 0.2 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 2.7 0.00 0.00 1 0.001 0 0 0.0 0.0 - Sum 1/0 2.36 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 2.7 0.00 0.00 1 0.001 0 0 0.0 0.0 - Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 2.7 0.00 0.00 1 0.001 0 0 0.0 0.0 - -** Compaction Stats [default] ** -Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -User 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.7 0.00 0.00 1 0.001 0 0 0.0 0.0 - -Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 - -Uptime(secs): 0.0 total, 0.0 interval -Flush(GB): cumulative 0.000, interval 0.000 -AddFile(GB): cumulative 0.000, interval 0.000 -AddFile(Total Files): cumulative 0, interval 0 -AddFile(L0 Files): cumulative 0, interval 0 -AddFile(Keys): cumulative 0, interval 0 -Cumulative compaction: 0.00 GB write, 0.43 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Interval compaction: 0.00 GB write, 0.43 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0x600001094798#82102 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 1 last_copies: 0 last_secs: 5.2e-05 secs_since: 0 -Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) - -** File Read Latency Histogram By Level [default] ** -2023/11/02-00:34:18.380812 8055427840 [db/db_impl/db_impl.cc:490] Shutdown: canceling all background work -2023/11/02-00:34:18.381317 8055427840 [db/db_impl/db_impl.cc:692] Shutdown complete diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956220662845 b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956220662845 deleted file mode 100644 index 7445b3378294b..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/LOG.old.1698956220662845 +++ /dev/null @@ -1,284 +0,0 @@ -2023/11/02-13:14:54.330383 8055427840 RocksDB version: 8.1.1 -2023/11/02-13:14:54.330688 8055427840 Compile date 2023-04-06 16:38:52 -2023/11/02-13:14:54.330690 8055427840 DB SUMMARY -2023/11/02-13:14:54.330691 8055427840 DB Session ID: GPA278ZM60CNTAIRK5I6 -2023/11/02-13:14:54.330753 8055427840 CURRENT file: CURRENT -2023/11/02-13:14:54.330755 8055427840 IDENTITY file: IDENTITY -2023/11/02-13:14:54.330761 8055427840 MANIFEST file: MANIFEST-000010 size: 180 Bytes -2023/11/02-13:14:54.330774 8055427840 SST files in ./test-data-mainnet-10m-15/rocks_txn_idx_db dir, Total Num: 1, files: 000008.sst -2023/11/02-13:14:54.330775 8055427840 Write Ahead Log file in ./test-data-mainnet-10m-15/rocks_txn_idx_db: 000009.log size: 0 ; -2023/11/02-13:14:54.330777 8055427840 Options.error_if_exists: 0 -2023/11/02-13:14:54.330778 8055427840 Options.create_if_missing: 1 -2023/11/02-13:14:54.330779 8055427840 Options.paranoid_checks: 1 -2023/11/02-13:14:54.330780 8055427840 Options.flush_verify_memtable_count: 1 -2023/11/02-13:14:54.330781 8055427840 Options.track_and_verify_wals_in_manifest: 0 -2023/11/02-13:14:54.330782 8055427840 Options.verify_sst_unique_id_in_manifest: 1 -2023/11/02-13:14:54.330782 8055427840 Options.env: 0x1057d25b8 -2023/11/02-13:14:54.330783 8055427840 Options.fs: PosixFileSystem -2023/11/02-13:14:54.330785 8055427840 Options.info_log: 0x12d669a88 -2023/11/02-13:14:54.330785 8055427840 Options.max_file_opening_threads: 16 -2023/11/02-13:14:54.330786 8055427840 Options.statistics: 0x0 -2023/11/02-13:14:54.330787 8055427840 Options.use_fsync: 0 -2023/11/02-13:14:54.330788 8055427840 Options.max_log_file_size: 0 -2023/11/02-13:14:54.330789 8055427840 Options.max_manifest_file_size: 1073741824 -2023/11/02-13:14:54.330790 8055427840 Options.log_file_time_to_roll: 0 -2023/11/02-13:14:54.330791 8055427840 Options.keep_log_file_num: 1000 -2023/11/02-13:14:54.330792 8055427840 Options.recycle_log_file_num: 0 -2023/11/02-13:14:54.330793 8055427840 Options.allow_fallocate: 1 -2023/11/02-13:14:54.330794 8055427840 Options.allow_mmap_reads: 0 -2023/11/02-13:14:54.330795 8055427840 Options.allow_mmap_writes: 0 -2023/11/02-13:14:54.330795 8055427840 Options.use_direct_reads: 0 -2023/11/02-13:14:54.330796 8055427840 Options.use_direct_io_for_flush_and_compaction: 0 -2023/11/02-13:14:54.330797 8055427840 Options.create_missing_column_families: 0 -2023/11/02-13:14:54.330798 8055427840 Options.db_log_dir: -2023/11/02-13:14:54.330799 8055427840 Options.wal_dir: -2023/11/02-13:14:54.330800 8055427840 Options.table_cache_numshardbits: 6 -2023/11/02-13:14:54.330801 8055427840 Options.WAL_ttl_seconds: 0 -2023/11/02-13:14:54.330802 8055427840 Options.WAL_size_limit_MB: 0 -2023/11/02-13:14:54.330803 8055427840 Options.max_write_batch_group_size_bytes: 1048576 -2023/11/02-13:14:54.330804 8055427840 Options.manifest_preallocation_size: 4194304 -2023/11/02-13:14:54.330804 8055427840 Options.is_fd_close_on_exec: 1 -2023/11/02-13:14:54.330805 8055427840 Options.advise_random_on_open: 1 -2023/11/02-13:14:54.330806 8055427840 Options.db_write_buffer_size: 0 -2023/11/02-13:14:54.330807 8055427840 Options.write_buffer_manager: 0x6000019001a0 -2023/11/02-13:14:54.330808 8055427840 Options.access_hint_on_compaction_start: 1 -2023/11/02-13:14:54.330809 8055427840 Options.random_access_max_buffer_size: 1048576 -2023/11/02-13:14:54.330810 8055427840 Options.use_adaptive_mutex: 0 -2023/11/02-13:14:54.330810 8055427840 Options.rate_limiter: 0x0 -2023/11/02-13:14:54.330812 8055427840 Options.sst_file_manager.rate_bytes_per_sec: 0 -2023/11/02-13:14:54.330813 8055427840 Options.wal_recovery_mode: 2 -2023/11/02-13:14:54.330814 8055427840 Options.enable_thread_tracking: 0 -2023/11/02-13:14:54.330814 8055427840 Options.enable_pipelined_write: 0 -2023/11/02-13:14:54.330815 8055427840 Options.unordered_write: 0 -2023/11/02-13:14:54.330816 8055427840 Options.allow_concurrent_memtable_write: 1 -2023/11/02-13:14:54.330817 8055427840 Options.enable_write_thread_adaptive_yield: 1 -2023/11/02-13:14:54.330818 8055427840 Options.write_thread_max_yield_usec: 100 -2023/11/02-13:14:54.330819 8055427840 Options.write_thread_slow_yield_usec: 3 -2023/11/02-13:14:54.330820 8055427840 Options.row_cache: None -2023/11/02-13:14:54.330820 8055427840 Options.wal_filter: None -2023/11/02-13:14:54.330821 8055427840 Options.avoid_flush_during_recovery: 0 -2023/11/02-13:14:54.330822 8055427840 Options.allow_ingest_behind: 0 -2023/11/02-13:14:54.330823 8055427840 Options.two_write_queues: 0 -2023/11/02-13:14:54.330824 8055427840 Options.manual_wal_flush: 0 -2023/11/02-13:14:54.330825 8055427840 Options.wal_compression: 0 -2023/11/02-13:14:54.330826 8055427840 Options.atomic_flush: 0 -2023/11/02-13:14:54.330827 8055427840 Options.avoid_unnecessary_blocking_io: 0 -2023/11/02-13:14:54.330828 8055427840 Options.persist_stats_to_disk: 0 -2023/11/02-13:14:54.330829 8055427840 Options.write_dbid_to_manifest: 0 -2023/11/02-13:14:54.330829 8055427840 Options.log_readahead_size: 0 -2023/11/02-13:14:54.330831 8055427840 Options.file_checksum_gen_factory: Unknown -2023/11/02-13:14:54.330842 8055427840 Options.best_efforts_recovery: 0 -2023/11/02-13:14:54.330843 8055427840 Options.max_bgerror_resume_count: 2147483647 -2023/11/02-13:14:54.330844 8055427840 Options.bgerror_resume_retry_interval: 1000000 -2023/11/02-13:14:54.330845 8055427840 Options.allow_data_in_errors: 0 -2023/11/02-13:14:54.330846 8055427840 Options.db_host_id: __hostname__ -2023/11/02-13:14:54.330847 8055427840 Options.enforce_single_del_contracts: true -2023/11/02-13:14:54.330848 8055427840 Options.max_background_jobs: 2 -2023/11/02-13:14:54.330848 8055427840 Options.max_background_compactions: -1 -2023/11/02-13:14:54.330849 8055427840 Options.max_subcompactions: 1 -2023/11/02-13:14:54.330850 8055427840 Options.avoid_flush_during_shutdown: 0 -2023/11/02-13:14:54.330851 8055427840 Options.writable_file_max_buffer_size: 1048576 -2023/11/02-13:14:54.330852 8055427840 Options.delayed_write_rate : 16777216 -2023/11/02-13:14:54.330853 8055427840 Options.max_total_wal_size: 0 -2023/11/02-13:14:54.330854 8055427840 Options.delete_obsolete_files_period_micros: 21600000000 -2023/11/02-13:14:54.330855 8055427840 Options.stats_dump_period_sec: 600 -2023/11/02-13:14:54.330856 8055427840 Options.stats_persist_period_sec: 600 -2023/11/02-13:14:54.330856 8055427840 Options.stats_history_buffer_size: 1048576 -2023/11/02-13:14:54.330857 8055427840 Options.max_open_files: -1 -2023/11/02-13:14:54.330858 8055427840 Options.bytes_per_sync: 0 -2023/11/02-13:14:54.330859 8055427840 Options.wal_bytes_per_sync: 0 -2023/11/02-13:14:54.330860 8055427840 Options.strict_bytes_per_sync: 0 -2023/11/02-13:14:54.330861 8055427840 Options.compaction_readahead_size: 0 -2023/11/02-13:14:54.330862 8055427840 Options.max_background_flushes: -1 -2023/11/02-13:14:54.330862 8055427840 Compression algorithms supported: -2023/11/02-13:14:54.330878 8055427840 kZSTD supported: 1 -2023/11/02-13:14:54.330879 8055427840 kZlibCompression supported: 1 -2023/11/02-13:14:54.330880 8055427840 kXpressCompression supported: 0 -2023/11/02-13:14:54.330881 8055427840 kSnappyCompression supported: 1 -2023/11/02-13:14:54.330882 8055427840 kZSTDNotFinalCompression supported: 1 -2023/11/02-13:14:54.330883 8055427840 kLZ4HCCompression supported: 1 -2023/11/02-13:14:54.330884 8055427840 kLZ4Compression supported: 1 -2023/11/02-13:14:54.330885 8055427840 kBZip2Compression supported: 1 -2023/11/02-13:14:54.330891 8055427840 Fast CRC32 supported: Supported on Arm64 -2023/11/02-13:14:54.330892 8055427840 DMutex implementation: pthread_mutex_t -2023/11/02-13:14:54.331285 8055427840 [db/version_set.cc:5662] Recovering from manifest file: ./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000010 -2023/11/02-13:14:54.331559 8055427840 [db/column_family.cc:621] --------------- Options for column family [default]: -2023/11/02-13:14:54.331562 8055427840 Options.comparator: leveldb.BytewiseComparator -2023/11/02-13:14:54.331563 8055427840 Options.merge_operator: None -2023/11/02-13:14:54.331564 8055427840 Options.compaction_filter: None -2023/11/02-13:14:54.331565 8055427840 Options.compaction_filter_factory: None -2023/11/02-13:14:54.331566 8055427840 Options.sst_partitioner_factory: None -2023/11/02-13:14:54.331567 8055427840 Options.memtable_factory: SkipListFactory -2023/11/02-13:14:54.331568 8055427840 Options.table_factory: BlockBasedTable -2023/11/02-13:14:54.331580 8055427840 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0x600002320c80) - cache_index_and_filter_blocks: 0 - cache_index_and_filter_blocks_with_high_priority: 1 - pin_l0_filter_and_index_blocks_in_cache: 0 - pin_top_level_index_and_filter: 1 - index_type: 0 - data_block_index_type: 0 - index_shortening: 1 - data_block_hash_table_util_ratio: 0.750000 - checksum: 4 - no_block_cache: 0 - block_cache: 0x6000017364d8 - block_cache_name: LRUCache - block_cache_options: - capacity : 8388608 - num_shard_bits : 4 - strict_capacity_limit : 0 - memory_allocator : None - high_pri_pool_ratio: 0.000 - low_pri_pool_ratio: 0.000 - persistent_cache: 0x0 - block_size: 4096 - block_size_deviation: 10 - block_restart_interval: 16 - index_block_restart_interval: 1 - metadata_block_size: 4096 - partition_filters: 0 - use_delta_encoding: 1 - filter_policy: nullptr - whole_key_filtering: 1 - verify_compression: 0 - read_amp_bytes_per_bit: 0 - format_version: 5 - enable_index_compression: 1 - block_align: 0 - max_auto_readahead_size: 262144 - prepopulate_block_cache: 0 - initial_auto_readahead_size: 8192 - num_file_reads_for_auto_readahead: 2 -2023/11/02-13:14:54.331582 8055427840 Options.write_buffer_size: 67108864 -2023/11/02-13:14:54.331583 8055427840 Options.max_write_buffer_number: 2 -2023/11/02-13:14:54.331584 8055427840 Options.compression: Snappy -2023/11/02-13:14:54.331585 8055427840 Options.bottommost_compression: Disabled -2023/11/02-13:14:54.331586 8055427840 Options.prefix_extractor: nullptr -2023/11/02-13:14:54.331587 8055427840 Options.memtable_insert_with_hint_prefix_extractor: nullptr -2023/11/02-13:14:54.331588 8055427840 Options.num_levels: 7 -2023/11/02-13:14:54.331588 8055427840 Options.min_write_buffer_number_to_merge: 1 -2023/11/02-13:14:54.331589 8055427840 Options.max_write_buffer_number_to_maintain: 0 -2023/11/02-13:14:54.331590 8055427840 Options.max_write_buffer_size_to_maintain: 0 -2023/11/02-13:14:54.331591 8055427840 Options.bottommost_compression_opts.window_bits: -14 -2023/11/02-13:14:54.331592 8055427840 Options.bottommost_compression_opts.level: 32767 -2023/11/02-13:14:54.331593 8055427840 Options.bottommost_compression_opts.strategy: 0 -2023/11/02-13:14:54.331594 8055427840 Options.bottommost_compression_opts.max_dict_bytes: 0 -2023/11/02-13:14:54.331595 8055427840 Options.bottommost_compression_opts.zstd_max_train_bytes: 0 -2023/11/02-13:14:54.331596 8055427840 Options.bottommost_compression_opts.parallel_threads: 1 -2023/11/02-13:14:54.331597 8055427840 Options.bottommost_compression_opts.enabled: false -2023/11/02-13:14:54.331598 8055427840 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-13:14:54.331598 8055427840 Options.bottommost_compression_opts.use_zstd_dict_trainer: true -2023/11/02-13:14:54.331599 8055427840 Options.compression_opts.window_bits: -14 -2023/11/02-13:14:54.331600 8055427840 Options.compression_opts.level: 32767 -2023/11/02-13:14:54.331601 8055427840 Options.compression_opts.strategy: 0 -2023/11/02-13:14:54.331602 8055427840 Options.compression_opts.max_dict_bytes: 0 -2023/11/02-13:14:54.331603 8055427840 Options.compression_opts.zstd_max_train_bytes: 0 -2023/11/02-13:14:54.331604 8055427840 Options.compression_opts.use_zstd_dict_trainer: true -2023/11/02-13:14:54.331605 8055427840 Options.compression_opts.parallel_threads: 1 -2023/11/02-13:14:54.331605 8055427840 Options.compression_opts.enabled: false -2023/11/02-13:14:54.331606 8055427840 Options.compression_opts.max_dict_buffer_bytes: 0 -2023/11/02-13:14:54.331607 8055427840 Options.level0_file_num_compaction_trigger: 4 -2023/11/02-13:14:54.331608 8055427840 Options.level0_slowdown_writes_trigger: 20 -2023/11/02-13:14:54.331609 8055427840 Options.level0_stop_writes_trigger: 36 -2023/11/02-13:14:54.331610 8055427840 Options.target_file_size_base: 67108864 -2023/11/02-13:14:54.331611 8055427840 Options.target_file_size_multiplier: 1 -2023/11/02-13:14:54.331611 8055427840 Options.max_bytes_for_level_base: 268435456 -2023/11/02-13:14:54.331612 8055427840 Options.level_compaction_dynamic_level_bytes: 0 -2023/11/02-13:14:54.331613 8055427840 Options.max_bytes_for_level_multiplier: 10.000000 -2023/11/02-13:14:54.331614 8055427840 Options.max_bytes_for_level_multiplier_addtl[0]: 1 -2023/11/02-13:14:54.331615 8055427840 Options.max_bytes_for_level_multiplier_addtl[1]: 1 -2023/11/02-13:14:54.331616 8055427840 Options.max_bytes_for_level_multiplier_addtl[2]: 1 -2023/11/02-13:14:54.331617 8055427840 Options.max_bytes_for_level_multiplier_addtl[3]: 1 -2023/11/02-13:14:54.331618 8055427840 Options.max_bytes_for_level_multiplier_addtl[4]: 1 -2023/11/02-13:14:54.331619 8055427840 Options.max_bytes_for_level_multiplier_addtl[5]: 1 -2023/11/02-13:14:54.331620 8055427840 Options.max_bytes_for_level_multiplier_addtl[6]: 1 -2023/11/02-13:14:54.331621 8055427840 Options.max_sequential_skip_in_iterations: 8 -2023/11/02-13:14:54.331621 8055427840 Options.max_compaction_bytes: 1677721600 -2023/11/02-13:14:54.331622 8055427840 Options.ignore_max_compaction_bytes_for_input: true -2023/11/02-13:14:54.331623 8055427840 Options.arena_block_size: 1048576 -2023/11/02-13:14:54.331624 8055427840 Options.soft_pending_compaction_bytes_limit: 68719476736 -2023/11/02-13:14:54.331625 8055427840 Options.hard_pending_compaction_bytes_limit: 274877906944 -2023/11/02-13:14:54.331626 8055427840 Options.disable_auto_compactions: 0 -2023/11/02-13:14:54.331627 8055427840 Options.compaction_style: kCompactionStyleLevel -2023/11/02-13:14:54.331629 8055427840 Options.compaction_pri: kMinOverlappingRatio -2023/11/02-13:14:54.331630 8055427840 Options.compaction_options_universal.size_ratio: 1 -2023/11/02-13:14:54.331631 8055427840 Options.compaction_options_universal.min_merge_width: 2 -2023/11/02-13:14:54.331631 8055427840 Options.compaction_options_universal.max_merge_width: 4294967295 -2023/11/02-13:14:54.331632 8055427840 Options.compaction_options_universal.max_size_amplification_percent: 200 -2023/11/02-13:14:54.331633 8055427840 Options.compaction_options_universal.compression_size_percent: -1 -2023/11/02-13:14:54.331635 8055427840 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize -2023/11/02-13:14:54.331636 8055427840 Options.compaction_options_fifo.max_table_files_size: 1073741824 -2023/11/02-13:14:54.331637 8055427840 Options.compaction_options_fifo.allow_compaction: 0 -2023/11/02-13:14:54.331638 8055427840 Options.table_properties_collectors: -2023/11/02-13:14:54.331639 8055427840 Options.inplace_update_support: 0 -2023/11/02-13:14:54.331640 8055427840 Options.inplace_update_num_locks: 10000 -2023/11/02-13:14:54.331641 8055427840 Options.memtable_prefix_bloom_size_ratio: 0.000000 -2023/11/02-13:14:54.331642 8055427840 Options.memtable_whole_key_filtering: 0 -2023/11/02-13:14:54.331643 8055427840 Options.memtable_huge_page_size: 0 -2023/11/02-13:14:54.331644 8055427840 Options.bloom_locality: 0 -2023/11/02-13:14:54.331645 8055427840 Options.max_successive_merges: 0 -2023/11/02-13:14:54.331645 8055427840 Options.optimize_filters_for_hits: 0 -2023/11/02-13:14:54.331646 8055427840 Options.paranoid_file_checks: 0 -2023/11/02-13:14:54.331647 8055427840 Options.force_consistency_checks: 1 -2023/11/02-13:14:54.331648 8055427840 Options.report_bg_io_stats: 0 -2023/11/02-13:14:54.331649 8055427840 Options.ttl: 2592000 -2023/11/02-13:14:54.331650 8055427840 Options.periodic_compaction_seconds: 0 -2023/11/02-13:14:54.331651 8055427840 Options.preclude_last_level_data_seconds: 0 -2023/11/02-13:14:54.331651 8055427840 Options.preserve_internal_time_seconds: 0 -2023/11/02-13:14:54.331652 8055427840 Options.enable_blob_files: false -2023/11/02-13:14:54.331653 8055427840 Options.min_blob_size: 0 -2023/11/02-13:14:54.331654 8055427840 Options.blob_file_size: 268435456 -2023/11/02-13:14:54.331655 8055427840 Options.blob_compression_type: NoCompression -2023/11/02-13:14:54.331656 8055427840 Options.enable_blob_garbage_collection: false -2023/11/02-13:14:54.331657 8055427840 Options.blob_garbage_collection_age_cutoff: 0.250000 -2023/11/02-13:14:54.331658 8055427840 Options.blob_garbage_collection_force_threshold: 1.000000 -2023/11/02-13:14:54.331659 8055427840 Options.blob_compaction_readahead_size: 0 -2023/11/02-13:14:54.331660 8055427840 Options.blob_file_starting_level: 0 -2023/11/02-13:14:54.331661 8055427840 Options.experimental_mempurge_threshold: 0.000000 -2023/11/02-13:14:54.332879 8055427840 [db/version_set.cc:5713] Recovered from manifest file:./test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000010 succeeded,manifest_file_number is 10, next_file_number is 12, last_sequence is 5, log_number is 5,prev_log_number is 0,max_column_family is 0,min_log_number_to_keep is 5 -2023/11/02-13:14:54.332885 8055427840 [db/version_set.cc:5722] Column family [default] (ID 0), log number is 5 -2023/11/02-13:14:54.333104 8055427840 [db/db_impl/db_impl_open.cc:537] DB ID: 68030351-9558-4d6e-aa76-818928bc21b6 -2023/11/02-13:14:54.333252 8055427840 EVENT_LOG_v1 {"time_micros": 1698956094333247, "job": 1, "event": "recovery_started", "wal_files": [9]} -2023/11/02-13:14:54.333256 8055427840 [db/db_impl/db_impl_open.cc:1031] Recovering log #9 mode 2 -2023/11/02-13:14:54.333332 8055427840 EVENT_LOG_v1 {"time_micros": 1698956094333330, "job": 1, "event": "recovery_finished"} -2023/11/02-13:14:54.333475 8055427840 [db/version_set.cc:5180] Creating manifest 14 -2023/11/02-13:14:54.336580 8055427840 [db/db_impl/db_impl_open.cc:1977] SstFileManager instance 0x12d637ee0 -2023/11/02-13:14:54.336635 8055427840 DB pointer 0x12c808200 -2023/11/02-13:14:54.336848 6181056512 [db/db_impl/db_impl.cc:1085] ------- DUMPING STATS ------- -2023/11/02-13:14:54.336851 6181056512 [db/db_impl/db_impl.cc:1086] -** DB Stats ** -Uptime(secs): 0.0 total, 0.0 interval -Cumulative writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s -Cumulative WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent -Interval writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 MB, 0.00 MB/s -Interval WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s -Interval stall: 00:00:0.000 H:M:S, 0.0 percent -Write Stall (count): write-buffer-manager-limit-stops: 0, -** Compaction Stats [default] ** -Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - L0 1/0 2.36 KB 0.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - Sum 1/0 2.36 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 - -** Compaction Stats [default] ** -Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 - -Uptime(secs): 0.0 total, 0.0 interval -Flush(GB): cumulative 0.000, interval 0.000 -AddFile(GB): cumulative 0.000, interval 0.000 -AddFile(Total Files): cumulative 0, interval 0 -AddFile(L0 Files): cumulative 0, interval 0 -AddFile(Keys): cumulative 0, interval 0 -Cumulative compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Interval compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds -Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0x6000017364d8#19704 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 1 last_copies: 0 last_secs: 4.2e-05 secs_since: 0 -Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) - -** File Read Latency Histogram By Level [default] ** -2023/11/02-13:14:55.227208 8055427840 [db/db_impl/db_impl.cc:490] Shutdown: canceling all background work -2023/11/02-13:14:55.227681 8055427840 [db/db_impl/db_impl.cc:692] Shutdown complete diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000018 b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/rocks_txn_idx_db/MANIFEST-000018 deleted file mode 100644 index abd62c88782a3e4a749a0564c2e9f93eb5f81886..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 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

r#}V1>tab96arUw7tyv9;wyCQ zk>cIgtDBY|Qg~fywRYGv%Hh^86mQ4tGqTTEluw4N2GZ?oa9`F1!@~|I+nH2&XT1I& zSFgSLf)k=k*Q@Se%E#jy{g)xcRUO@!>E&5zqm3zHM-(+JCe^%ND}M4zgM_tTUC%A! zP*GLyrT2ylU^T zA1}r;a(;1W@xI!(05VdyE$84NQ1F|qS34Eu@NVF>cW}UO9jh$trR|%DT=Q4AtMF@4 zQ2gs$*#@4d&}ut$#4cJ!#(g85nQ@=aDLcb%OOFj#TH7iqW?W_;uPSQs+D^EWo3@2^ zJGcC#0dHC#aKchT5=FeO?Kg-=6Z*exda6f#$4x~x?5=CR*5t8Y}6PttRydCU>`=@(}c6$kldfe|WJrGrdpc-!lxZ=rTDBil5gPPu4q>xqXLsQ_Zbs zi)dYcK&OVWbHCU*?2&wFUcas+nVZ4Prp%DJt-wB)Pn~pi%p?_;$|+?kY?3Vx8+qs} zl~nbLO>v!)Q!8HD%NrXMG&3$)?nQ5^kC940pWibz^k{r`V_@bjH5;`V)RLLeZimPiXwouC#xHN8=GD8a_RCKuQ zF`SJmiR6@2QK*{PD67=+>zdu!T670J@6RLXqG+>7|!+AEzy@%h&` z8ujh3r*i#?*?Q8V?Dc#%eFy@<$DA2$SI!HL(Z#`+8t|9V*tWmlNTl#;Rp{Z{>~^~S z)b{Jox4O^Z046D~wok26AJ-`bs#jc{zhZG#baO`^XT#uin~={>=47lHgNNhBJjK6} z$$=P5d&h`#cxhl(&C#2d#0!{#cQTG19L6l%2v3D$cJTem0ws==FRgTzP?}ZiV{ku- zID#T-Cz;*`yuTP%hDx{~E8-ZCla^Pwc$8B+WFot&sBIJ1LK*5-#S0teY?F47unGT;q8cj zu>QkOcQxb*gnUm*8_TXUySp>3Pm5a@*1^W>kpI1Xfo`%a25UX^He9$y9dcSk3rr)q z;}TeU)TP+;oMKJxkTUJlb{p!` zhezFx$hV#qk@zWh4030~@yP0yKIXjffNV5cZ4jsHdDXS6{uF7!YfIN)YTrRmXJHE} zfj^UDZGLOZC%k&=40fP8c;5k`8ff?)=O2nv;;%TXezTbhlxNj&&m)5f(wTY zbKh8CZ7S&CdVaA%RvhnPY;Deb8(qW1)f6H%!Lfo0{AvIlm$@iFpKB$&XA0In9*!^r ze&`$S46UH7nT^Ym#x*UKQkIb3S+(RktzYpZDMx^mxzz3ykl8i?S<~za0LMiGNE*Vl zCee=ZI3z~I6Ds-UkH(TwvO)Ugy2fOB1og4FQOrg6Fhu66+iuWQ6kGMcD8T~E4fto0 zXHtfM;iO7L7fA+s&UPwjsRXFO(f0%yx3s=lWNKmlU#?8g^|5UPJCS`if-koDFZ9&Q z!L5I^-?Jjihaeu)`$_F?&2_}^GWW}H%;Plw!HI{9NJ&+5RWGAv3I|e5jU;nDoyvaD z1Ewc+puGwkxofJxF2bU(mio>`o^MpWEewqV=jCV{mD$ZW~wDb|Eg-BZuH~<2b}{HQWju8 zHna$zj|eMT1T_49z2vYLMVcC5Z_qo++KwhRkYGO82Li!FMhGhivTmuCDQ6rw4P8OCQ8-EWge!+R6^}!b#?J6W5c6ru*mQ z<$N@cCGIyB77y~I-KLj<-St4{=lXMqAPbk*?@JH%gyd8IhG}2+`H|@&p3?mW=lU{D z2BZyfh-qTxD+2FN9T4vO1iz``@3~E(!BtVZ2dZ+;MLlC^my4&CedKSB^gW75g*D0MWYd7Y}E?Unhr1 z!CAb14eLL*a#ae0HgLsk14}Ge)wXpd>-$B_9!%L;8K)am>L#Iiq;(@C?TIG2A3Vh3 z-nEBFF@WFYDymwS(wE`ubD!aBDus7CwMW7DWcddQz%92=q^P_S!@sf##@(nuT1<}Cdy5rQ$s=~BrZA}risupLGNmU;a$%Kz!qkN ztpu+`vob&#$^vHLjyB91OctaNKP+@Hre1`|fl*th%;*xYT#$Qj7_b7%sBU7qkD3m1 z-Y_b0ew9@~XaM~>v>#MT<-^CehJWb#whu5o%&e@Zr^9PIK*|Uw>c^^VN46fJ&}L?+ zpt^EJQTL9cK?zm7<&aq>Q>bB_AULs7X}T)sz2?m;6(|I7<6vmCM={^FMofeJ;?D;f zi`wE_DB1R@0wAdz?xgrYZ45C%YXA~UC1Dw;5y6w)by8L=;KJKk=hn_E6r-<9wm)F=XrVE1aqU>BgVgx;zl|Dp$v5O%%I1y=@e zFELOnpRzSGwEb070rgd3wOAT?1AE{nQqDopAW-9+$U+O03#bkKV62o$4dqqGHX;A1 zUHYpKsV%A64=&D_2Vgvkw>jMBq~1@Rwq1aI1aec+HaXg!Kq0wlajrP*(Y)!WF7^am9mV3CVa6ngSY!&(KB0E(}l*BbtL_AV}1F zOoTjOl{p+FWgLg{{wUa7c$kn)l_ovJadilG0Ecm?eqHmSL*h{eWRpfXLcs2bj0eXv zXiR8qS{UmxYA%*6Um_q$wr%-HmBs}!9aZp;t)ei5wW^h^FM=q?hyv2cU34%8PN}G_ zkO`c-B@pM8$_DuYa`2-@yiJ#rs%#$m4b-h*w7?gm(q%*Xxk)RUG9Yzc}%84R502j zYuZ43VfYFK6$u={pS?f9R(kPWc!OFX-GDL6{w2=-PNdh}V4Vq@ZugZhorXcp|002_ zSN!SiRkQg6zB}n+@ktD#ZQ+a!wmsbD>r^W|)53Z60JZ%AF0bbsq94KdE$9Eg?{~BL zY}GjP8nf4~v|HEgeC>J90vQMaTlkFwoYEV4kYj>_!cD=*S7(4m)CvIfP=^r*cnk`G z6b`u75Thd423h^yKOB+~!xcbIGHp_{5K)s9v9N$gzyQgBJZqwT?VYGOE;269^c=_= z&A_w90RkOwd-Nc3L{bhhI)CUy;1k5mn&<7?00S;)CD5~uJa|;VTw_Kh3EHjTDjr5O zY$xyl$&6q`(w0|CM69RWAa0IGZHEwt(Rc)OA;z{0IShReZ{Lbiq%a79OHz~3h07qR z%S|(RtiH|QU-WR3;f)28&?8VhlxMWvVv!7~#T`3ufm>gn1Kr6qgmEe^pJ5_(Q)_Tf zyb01C509oq!3>qTR9cnjSB29Wp>@QxBD(K*PC!1v?*0X&wSl6ogZ~&jlz>2}G_=f6 z+1@8*8K(;`5McjE0PGR#Jo`)KVhtpQeJExR0=O4-J%R#CR{d@ zKtF@hjT!TBZ9mN=4ZNt*QXV>r2u!+=Ss=(5nn0+COgy0U*bb|3tutZ%+ixlBvjls8)2pOAHViVzTxPu9vgwdEQS{&)UJ_?{Mv;>nE@B)6s5~ra#B%J8b zD0Ck|D8ivZ`iRZvk<(P*deOidxubl4N|rlB*8pkr6Z zX#`l!?NZc=2;r2#A?`dCt;nTG{3($Dxl67{TEyBuR0z90)Ro3P|Il|(lu|Ci5Elw( zF>hExRaU1a6Mho@l3BiHA_X+kjCepGrt%=3Dxd}$BX%S>DcX8aK*^=sTr2m_r5n9k z2zH$iY#;71K9>hyT4z>Z%ZqB{q2v6Sz{8l>p4{_qUcHbe*MUaP_~!R>w`aS{JB?q^ zbnCF8jaFtj9GXP%ho63?Z{m-2@p*nbbM;35u2$pdP`%8@pT7CkMAMhwmQQu&{FA%i z5t^4XV_RHsZkfpEMuKXRG|)P7NOW zVEiKfhFkvfas`czjfoV$hS{-GZc6dDxd2(9YJDrB6W|J?#VGkopj)criHYCz>S4M$ zcy69!$JoKQp08c&2dfM=xXB*8<#AZJQs5ra>3^}J6OcfUO(jw%C_)ve0+*FZ zM5ZA7)s~VJW^9rbBunlDIcr0Q&4heukpMv%ObUqN6K^%o^FG4Loi+7L$|(C}FC-{Y zx1ngkRHu$yVN+iTS(TDPv`Yk)0{~L2w1aYzdFFt>OS+J)MKU;qTkbJCkdJvW*aX^@ zUh4Y0E*meyU_z0Ihgmc7X0(V9IixJc3Y9uTAkVnaih)xi*eFdRE~+4Kz_660juSSN zPf(h=01|}98ZhWGMi3&H`s|XpWT_17VPGsLe0jBQR+-Lt6)thIvz^9Q`v?_ZTHWWh7=rd#vfQI2A7n#WI!f8x**& z#7Ci_-h_>|gBwL&|vv-lFG$p|TNm?(5!4iy5mGqCWhbgm)eP88Nqv@-YYch|UlfiZ8OtOq$2O1~mllV~JaH{2SCTKR8dmE-rl+&w zWCLKUcw~QB9Avi^h3cfN=mkXv*$hH%exo5KwN-#npRW4%Mg&$c`G{H_JZv0U5?WI7 zzDvMS=4cE9DxEMnFUISs14(|IcRw)5i;Vou#Qg9HQ2o{E2u7b`%|sPX*(Pu5;HX}L zmQz{s^h~7Vo{NfYyq`QBGD2xE`+ioCx9Bw}sRwBRC>H$I=Tb@vJ+BZPB&v9$j*yIp zWUz|tp$)FlJ~3OPqvHd?ADD({;W>GtjX+^_VHPlbQK`0o_Ib=Ljk z7m}}+8PWLJt+ij(#x>NP?cqEuG?*rME819;*+0qb7%$S`M(7Iw;X^XgQia=?y zn@X{>8D=Gcc{K&g(gdcEKO|6rfQ2A}(_Xxc@G^>P4otL@ObD4+aEa!)Xfe67qNz|M zT`>+tT!=`PQsm>=n%oX{oQ7yZ1gV{M?O!Uj& zoK~b2rCYd8^M%$kdEaOoioG_;N2-p%wnqSO>`?FD z8|a1z`!H!A{^^HZ+mv0uw$iaz`S8L_BeG}}&SWdkNs(eB4a2{5h~B1zW(e2+l0H+E ztrT!YC^%YiUZkmVss82Q<*WXsRfM|$SZn~b5sNw0PBVmMmv7~&LLtH_K%-6@DOXGD z5`c?5rp2+;@t!#F)XiWLyH7|QtMb(y#}1JibZ4xPh+dF-RH+t4pL|D0lA$TK;%M_~ zSiGS&^j>?zjype!qxWb2IwsD#c8yS;%)sO6|Jq-Xm6h|4L$cikMqKw?T*v_ax^g7| zQN77G8%ccsxoaeJawxBcPv3tDld=0A#62xU7sjXoxW+jB6JYep@EXW?>p&zt_26K% zUYML|FQ?lrV}FmW$cAaEeeDB$(Pj_@1f~BdtIdxmxWz55;^=JEKt?(4dDuaDFcujl zl#;9}=2eiQtRPt~Vn}qMj3@A?Fe6^ok?1r~8AU|&Ip72Bet8@?q@0I3U8=cw3vONF z>&tvxL{f1AnOGtW3b3iXY79Aztc^#y zG((P}vS`E7a}FhYH8QEnhDv@D#-(h&r(83irBGy&*vP3RLXiAH0~6uT&LPU9W&ED# z=5&-KB|T<_>~R*=FrjdFNkXY9MZTi(z}56;iNwq%+4Kb!d-3Q=Qed3IA08k~X*KZ% znzFS(svwkpi6+*Aa?vkvHw`qHEF_m3lmZU{=VTiSDih5km5n*5Cf!R1X2^O@xAzs| zUJNZ3XSE#1Wh!|9v2;ibJWNfzT_H8G35I;m`SWzJ5Jj+Y*ZjIK^%L3XA?96`n4@zN zMKGEa%V5O-vA|>G3;kvuQWz2kBQVZf9cs|G0WcN5vN9Em+N9C|4u}@Z&;sf_Xo{3E zChFh=rI~I`fKcipCu7D0oF$n^o5V*RK0`NgygnW$khlWMKcAL*TKZ zD}aRQ?uU&!L1k)rZ@ZQ2G?O_RbX>_F!+7s(*FnLYZ(6fiiif ziK!#b9JZX6BGBvXkMDp1QDVYVNF; z#g2ke;saMpAiIKO-iRonpmz3Ty_n}tQOiRRr@F$aHJmq64rP7V!X)qw2I^^bnw_4a zHSx|7-K@{m5s#&oLU1Vt$W>_6l4gn$qPa{`1+=H1&ZKkYL=oyUm8a6t9;EU8Mj01$ zc{C)t=ppZ$>f=tJ&Y@?`Tv8roT`_827M#UfMH? z-<6zU@3YP^DeOOYJHOx@5a$H5iT{x*LKz!67#dmHSh{%Lp!`>?*!_>Bf!&VyACkr% zfI5_mlQ->$pc_zG{Q{Q5A;C+tJH=ntW@*VP^~r8h^@f={|J$iM)Ar5;{e-P!;eTkc zE*|E`sW>h^J~QPOHO*}37Ts}|VW#@Hkh!TUkW6P<<&+8{)M7)c`wNVh-&5HMX};8U z7Ns-xU)-uF%IBju4V1&`uH@n|68uUls(-OuUHTfwX`B-(|JpjhD+1yt?FAbQh! z6CT-+m9i-nD*Qq!CHBwr#jD46eslcVQgp+6Znt*CWwF1u8W{}+D>0JO9F@=tbtpb? zJ(#Z4P^bBb8)G3P`0atwT6EBg^{8RW&6?#>KU7Q_@}j0~G-*_#!_g|Bn!`2fdU$2) zcRFS?z_EScLsKeUb3DV>dGabjl7Fz5aZDp@rfNv`EMIS=`t7hf$&m#v!C(nRLyUF^ z3)gfQY*j#00wpJPrafm?jH)ZP!-o+A;YIUdCP8~mD@NJ^>EktxUjPHp0rp~OFM?Ji z^d}y1hU|+q1EF>tLGy{jjq=W&$EkbJ#8ik+FB?LW2Oto{5DIIj2^T^VPJkGm+UlZ@ zC@O`TbR}#?g7Xw$06-K6*y`geR^H*}&-Q-6r-yJ&G$GQb({I;eGw4M%IOqZBA$Dt% z{6Nnn{mV%~8$!rAR4%dV))^K1*gIz0>kaqv-2j+HV1T3=VD-7i2(V;O9ncQI$E#&k zOFg{c1a$)~sPh;fPfkLAT!Z+x>Btk|gEFz0&U1PU4d70M*wS5;PDvyTr&d5rF|d@a ztr@SlTn~1l*GaL^(gjyaP<`kD5Z7v9!%T2RnGh+AAGM4_Q=WOi6Ye60Q2Zhc>DQ9S z&=Vs>mlvf;1zZC)fs>V?W#krOLt}_pSKHc-<2LI((^w<9W_e}nk~dm(3a%2drz$r* zJkgv_6h#Ozi!eL=&-PH6g4*(Nan=^vpvK;^fq4zZ*>uAQ1t?;=TRGSmv9xQ|x@bTJ z0-7fBxU%d8i)5XNR|Od(I%zyD{UWOGjnwU?1L~A~6{Bm~fUZrvQmG_#|5YkaA zs)I>!6k;Vj%*zoG&4sb6n*%MZXsXmAUJ=JtkVN2->RO_~Nf@zXM_0&RTMtR(3(bkp zrEfODs(b8N2@r1o0GDfUiDYl5CEGU;989NuL#O?YB!pk;N>Z_W1!uQ_04Wm*tQNRrHSUhdm zt;lL<5XM+*CEW%A&9O`st)`&u>&^~8C4CR)Tgo(4v0ke7jHG^6duVf|S{PY7)M2p6fHCfM)8Eh}k z`IRqvV$Cz7vSf6i%|~Esbu^|_!kK2WVrIyn10T&qlu8F;5((eJA&G>oC5gh>Xv|DZ z(vo)4>yv3fv3qJiyktTr-m0va5`!a$iZWl3va9laL-QH@5(+<1!C`Cyc1Gl8S}u9e zL6pgO(&D!65s5DglE-k64*_fzHl89nlLpd-j%?0H;>!Y=vHUqIz<8Pots5x~ySmnX z8BNp+)E{r2^882L+P*}rv(GkXfC;>38FyMVfy?Uv2qulP48hbiS_mlByUK{qKmk0! z1e5scf#CFHf#roS_f?t`$)0<{ha)B6a-?A&3$?Y-O+77EBdIE;Wh*sRGd;$K?gIQ2bny*BhN4U>*nX^ z_wCHo{l=4-^PM&2$JN`>lTr6k>u+eko-2o^8u1zn3NZ}_@$KQ6?bG&kf0&;7Doku| zH64hBo&i$*RYORSQCtxbw!znQ+<`dGCLzq1Lg zayf8pujSmPP+akZ65>pv;}Pf|GUjf)a5f@qQ$gxb31|j42sV@BfCI7)`IIbQB1MVJ zl%=i=VpuNAaWESy3wJKc4TkXty1j~9@Ji%hV(+VcwL!Z+5(XNC z!LX%GE<66VCp}q~3cqC)7W`n{jUTG4J7Uj9BiuF%)Dn*PF#LqZofwrxbbx%4-PlY0 zJUZ=XA#iE;Xc~$JNRi}tWZlcSHg8_^PxI*f&2oTQ4?|UK8u`-Dc~&r>7e;&T;9cO1 z@c!l>25)T|q~vFzbI3F;8=PF|UmQ-OFR?0<#^L#Jg4J6^&wo@Q#pl z2lLKzLavV1yTW+0abrzI6`VP^z0k{~zLW+b-nURJcWouOUe`RI#Jhtckt%&vT};Jn z@v(ml9|>$?L8j+D`Q1*R{M1r^YF526Kv7&_@`J*Q@1PJ^`V36;^-)+pl`QAWf(+0r z-g-*k5Dp@?-%KwWX3(oz5!;Us*N`+k-u{}>91P)ndQRfZV8qw>>OJN_*C#DDh@GG=cuT~0PmLcUb#%+rI`M9*((wUr5!YNtZwq5>pqcbV8(h6X0E=&t#k`x-v=dn`M= z_*M9^F=9U#N7?w(oXaK=)bEmiz%GEZ!Nk>gAak^Up@559w@tM`&;};^`s|A~b3{k+ zoR`A(sPUz++!ZR0r4P10Y@UYus(&BA^pmeD$Pb>jIB&S`N`M`^lp{N;>(5zXwJRA9DgzUB%!#q-hhU|$ok zm=kli%oV)5F|b_u=Q}K$-(R#RyV-+e*=n%>oOA^y=LVHF?*LBVx1b#`Ry!Hr-&uQu zwnXNRq~=vGRPvpeP037B?j8rd;_kr4b1JBMrb>T&TItwz-rryT1w@^P&7mgx-{#Za z*1^!p@E^wQ>Hk=nx78>Ar6Hkge^Ree8jMvlFrsq19uEm{L3Bt4vL)914HOtxp^2iY zAW0sgeYicAMk1AVGil8EfgZZXjh(>m(0aiowT-6^wg!j)+Cl##P`5m|EejL8|!%v&r`669dsm@-` z#21IOj7GP(?eyIrLa$_hOZRjg6!w&^g*4;G3OjOJ8_93XV$htw2UeB0=sbEH``}3@ z*iZS((i50B++k_%pI+NN6YURTGBPSZ(aO5=#>|Xa^=79)kA_T+{YwL!XpoZ<6vX=h z617oJCPk~lt&XVA^=fOW+iTI-jj>%DFI8a!`7nzkWGCAwaM%6T3T<40KsE|*`!G-y zQP7EdxYY^*_VZ_dw5|`LNuq=@9_c*Q)i_=|NOZ-8+mc?7o=`{be+6f57m#R}VR*($ zvgl`19*^6da|!J+a!gOTU(m_hUiIw?&=$X9P z6Au=SA&$F`vhi`F?tIot-MA9tr#&kH81;?ugWsg&0GbC)TjI4DdYxoKHr;hE-tXTb z+5^#kt)IV{VLEx%} zhT6Z|p0GcKDmA?W|FZtIB!gkq`3vb|MCuxxU6cStp)sdSs;7JS zZ%&oP6=N_BXX&(^T+p22ae_aYJQ#xxb9oijP%kP1Fa=t ziZDI`9?Ik1oEl?13U)gl?+p8alQ~x+_cJdnfTB6ewVXluNaZ!pOoQf-DhIf4hf7x1|DN5Xa68vp?)YgC@5<1JIK2WSVB+y zidt-W)n8%>bn8_F23hI+MwKM5%n{PHBLsB*9bu&*IYf0{}9frw2nkZdES%gYg)0q2i z{L-I>v$g95SZJ*^qVmJ-+nrOtUU+hzl;B~Y5=|V5k!AKAuh@_8o)7rDBJf_=fo=2I zoUbo$YdTY+F2CH7ZqiOONLbACdzTe;`ES8DD~~T@zcyIHOh!o}1RFHu^kNl{KCh-# zK;Jnna6e&X9o+;Lln+~oh9@xg6qy;qy=_WJ68Zi16YKs#ngRi&@eq2GGtZ?hNPRrO zAJd>9y&RB(Je~TO$<5#onjELuPo|>zCSYP6&f$>GKU|vY#Y}?aGm2+ZV{;pUw}d@;J2)Ho zr>{`r=P2+`2X%o2`%IN61PV}**NSKdfj9cF$)i4hIX&@}PR}pT{Fk3bgJFT_-tIOx zlxVpxsQ7Qt-+$Ur*-)lg;855xygV6pi(APA-Eu*NAhS|AJf;vJn&{zddRudPTx0~s zX2&mgd^y(ig}xJpxK6_}L-jEKRQ`ITvgc;DUs+Sa#r#43Sn^`7@(&pmnsj zYjk0Ycv)R|p|5#IwTYst60{E`Vh7K1wERj^O~pf8Rb)KOo*y+tfyZAf2P7$5&@NBW ziwYS6o<$){P_gVU8k)ThOjM{~QH1ZS*gc`;wJ7d_M3GITS8SGtUSqh7`LculjozEX zfZr5H+LzH}NqlRprf}XeMVaf-XBTWXmx?z$t|G-Wt7215^y<4Hn`CLiQ>5w$PI6A9 zHhJXB@Q=sbmX18KK#l>?ShtDZ&|7r}bI(5%qj2rl1*#3_z96YUKUKh*+)&uA$`)j^ zqZq#PXH*{A%F?&q@y(WxNBwEa7rrLmYVh6m+#XViN(6==`9n+RZ1bY$MKqqtd22`0 zYr~5d&Y;>ciJqzq?s?4IRoooIdwmZpd~ zY&yn~g(#as%OY9agP~Q@G$xUu+-_U1sK26K5_~~hSNEx`W~H^&yLS0_hjCTVHFw0A zT3k}JtoJu+027|2fqUHA34i6R8!)fD_N$H7k*K${*?K)@o2|vpI$Ome4I`3Ns5tZ} zxe5R->o4+t9srAAt`Ii?tsc{lFHS`RTFdYxwYam&tXnZj%jY z&bVq#oVcKS{9DMVs~Xt+*U$`$`cte!p~z#1)81~^DoZUX*W?J4*Law=0vGYN?RJ(7 zHOAMnd-YAsZo%r+gEDG)Z^hpJ(>3f_Wp%+5E+JDTm36$}F*h?Ka!rM)&{k@IDqG=R z2t-P{NHB&VgT*N<+sbkfK2T%;M-JnmJ72WNDiMh+P&Mi_i!{~oIR<{LnN+j3WP;x3 zB*m@wn#@)Sahq&gXs^AB*p(>*-6>{Vs%VUsuZrdduH)*yvFe+2%t&ZpYRhrna*cad zSBS-QfMvKT(o68wizNRC?n}Y-DkQk(ilB$|dFu zHK}P%JGRxEFAqna?j(*{Zq03>F~Dbnop)0)5tV=~FiyxqoG{dS`juQ`b@0}lPJiXo z!x5Huo!a@lsaH2w8^wt3p=<&tiO53{KzDLU+6^0dOQQrGc!857t~IKMEjmb(6{*eg z+OiUZOm2vkUMh6!Ly?gksJ=h8t3tn0Be%S&)>F4`9-9J(K^8+jGj`mf%~@Xz)QPLr zZ>S6^idwg8>8^?F-&gjpmHg`eEcZ(4zMn1M)n&QM7`wenGBNijB)D$vEU^14=yY7; zT&rKwG}O!(JKH(g^LL-^mpruHW0iB{_J&q7tHyN2)IKoAFhF&gqYyI97$)Fa#?L#3 zL^ZFW=~T64IEQ-XCl<^riE1!Kl}OYiPIP(PH6uz@O5wPz%i^vX*cF-Hlmx<2rA-NT zvzC^Pj#maM;J!MI)^yoxdR(mV^j+95+NujVHB(KcjfM_%6|i0foi*${7*Sp3*+okQYk7xB znhjNN%&M+Bu+qJ|4s`8rDEELUu9%6H8n9g&L|&? z2mk;800092?OSPc6jzr0&acS!#1IlA!P^&M9D!}wCTuWaWO{l!9MW5*3`$iMRas!# z!GE9gGHc1&gkb0yPX{t#QB|4m-FKIB&$(GVc<`V){e3)|G}G$oY|zxFkx5BybzNQ@ zPwUH}Yi5%)t?FU*bo4sizw-dMoL$w^>UtDsgH&C)Y1Le%D*f(4Q%&7dh5iVTm-U-Jt$QjoaLC>6!7&59(08 zN^kGCYlqQr+El0KfBog>XWySc|Mlx1PycrQ;>mxVo>Vg>sz()l`00&UhI^L{FP?n= z?D5&Jznrd_!M42hO}Wbrn{8j6ArtDDhE1L8&d`z@YiYN%pqhrObe)c1aUHHIq#J@2 z7xqu;*TwY5C3cmYVHqqMT#0va;S?Syd3lA4mb=$Y)r>9^uANkG>gK8(d~NUDA?O|Z zn;%Y}o}NGX7H2mb5hzES*8fQ5P*}GfMpzhUZm=W3N2Qk95ZzzifADypEv*^#6!nRv zr|ET?VA0NXQ@Ce5m~|!AT%k&@MuT|&uxI^~XWu?~`Q+PQAOBQx=%**&oR>U$_UqI0 zum5&-`eG49&OYcY%P8IED65d(@+9R3#K_mpwHxm{%_=Nghf$(m8YN-Y(tV$!kJjB- zy+W1@29*c04NL7uWMEmKX|c}@-}VpQ*?0ECFQ+el`1z-A+Y(u~ihh7mJ^vP)I#1xQ zu4$uzL{EKJP2Xqfa^o$FzkYJ|^zrl7hd+O}ZgAg=Pu=f5cu8BnLlF)Jqc_+Y`OvkF z#YN@Q-iA*f|7Y3l>(gJ#hO3<1m5K{QdC^Q}VF#W(9#;2e!`EtcVhGmVbhtukS9xf9zT+N2BN~$imyOO{q(g zvd(M>m>j$<%Rc`R@xit25>*-uYIMd>h1ukFs>UO9>$W6^qv7%N8i8DHJDOmP&WdRp zgQ{o8^VX+7rWR@8!!ar-PDbM+Tsy*UT?;Qitz%BAuJe95Ghelzyz2$By4O42ZGw33 z<+eL;a2UVq@bmIm?zh_o-3qY$+0SP!L=|5gcRr||G*uj>;_RjxrjVvnH>n42+s(_C zZ(17;YoaI%ULmp3dFO31&xWD=1sUt%Zgj`%`tqtNC#c&K;ffU%m@oSBpA!7>Y*H8~ z484hhcJT>tH%#n~f;(Qvze0wK9hiJ^+yceC5v<`w*8)~7r{z~oy@2_7;9e}f_&&qYVOT>@5b})qjOw(o+qR0^LB@WE#nVQc0<cDTUWcckSJGhs&4m(DkNi&mL&~ zX_uD{diQ@wjBkhNJHx!f=l8^R1!;eLRN3qR?h?%%===VnS=Yw>y6IWDuUH_M(O#P3 zw;f?_Mg=r?mES@e&s*Z6)3;5bZEfof|MBxD(=Ctp^0FEA0rp`0>ZH26ZO{7w^*tZ= zdd&FM{q>aHa!r{y?ePn$=YPvHWveaU{9n7~#auq7={_$kdO!Tc*>O+$^-j5`rQ*C@ z;k8+?Yw^Q=HTKT1X82xnUw)Law=nhPCpENJy({yS*kj%bxt2EJEf}?2WuGm$Y_~CI z?o~H>-QrC>#ZaiDYnpUDE+S^MbQ3uJN_qT}U;yas#f#nT8V%M{c$Z}>lCy5v{kbw)*|KvA4k(!Jde@!r&nqfYgH{mqNx#jMSF%v_qc<5Zp9U&-b!3LEPo8q2-M zlMyr@{y^KmE+H*(9K9){x2t;G4W%ce2Hm8{yX|rGZR2>}e0g@#>awp6ZiwV9B3R+I zJE_jL12ss^a!mNBTFTf3^+LtXBXCfbIGhjTK52;YH0R`GiII2b)*Nny{M$0;pHs|s z)PFp$zp%r}i^mDw1CLv1iv6u`eev?{NA$tfitRD?uDb`v{<&xW(38LX|9tL^Ms>4U z=ewlF?xwwZns{)Wj@w~maph*T;;5bI z7U!?LJYBiDZD;$ve6+E}`m_D5^M1nJigRxE=W?{(b(XDYzqj@5B|xhj*iC?Tef}QS zv-N!HEKSQ`db;uTfPectvatDWi9zMn`NF>G{3Hkcu@BbzK7myfr0pi#0i%XdduMfg z+^sNm*W*E2E`;}oqu|CZHPZ_jN}Lqww@BNrP1Cy?WGS?aUYVhNm~^)O(I1h0wpjDgnn`KiI>ze393bm(iq01FvO@#wI^JM6g)}EC`pyqk#a$ZGb9+PyztRw zofG{tn#BHeCMiw>m4asOgwH}m5slSEQK^J;MpzlOQdv<;9nZPE&j{g}8sSY)@?Y>s zKDp>{Ia5KKXpI++I%Ts;Nl{~!6`5pMNhLSY874AyF&IyAoyS@p*pq4F7TGh020y;sUiu4@Gl;!4}T*gcS=M5h-P19cPx%jFO3jsw5LW zBb-u#V2BKBNXk+Xke8Ib^^sEePbDuM^2;DBG7tk#qTB+^i@3;8B@y!y=!BSM6BB-P zOd2O)3>k>pQ6U?KQ&5LuOhM})l5?m-7LX@Vb8R_154TH{Lm`{ zGwR+lB#p>|!*MZdD=8p`Ma}E))-T_U$ra|U_1kRo-Qc^z}&Qa9LFu+5>6YdP~ zQR9C`!3vp3z-~Y%Bx@kZG(-hAFo{^fod8(lT&XEkfi$Q9lCYBv5-HQ|l5`AS@u*Y@({zjgVo}1RDF+<0dDiz79koC1p7}IuR0ufTA|o9%KStAC*UB=4gMK0qFr*0_lxnrUix( zt^#HZ>0)qD7Yf5b-6S3&(v=3qP#H;OhTMWb70Kvk?avmY`E1x8!(5QcYIGqqJe2_1 zXl^KAFet@513Lqj0}`ZG6xo9k=LrnXkes9tU_9U&AQbp484*xQ0l%eJg|0cKQ2xrG z|F8shl>)N>Qy>L;ctQayPiXk;n9q*+AjhDez=A@|JopW38@U0s0BhC4Wbi!j$$-|W z(bf||AF-OJLT*&qH3Rh`7zx@FSSSSt1W=Yv0y3_#5LIB?8JLew0=J-5SXYz*h%j1@ zBA}U{1M{a03_2gt3M#;Ia3s)nQ?v$-k(~oufnb8d!7s=_1jlK@1%)K==;+Zve1%j7 z>WKw`bDBcNgkos2JSpfBnWDpriEHo(;&TLH5jxli9~%w6#34gOyS;|sL*vfWViuD7&<4SyStRZI3T@1@*L=_Bviwh5ExiCQCp%< z7$~sjPAh0k@Mi?pg>qWPpvpjjcoHSUG%g2+x2EV(5+HG5k0;Rh0-6%?nrF_8$7v)7 z+X5NP5qw-@9-s+9q9ua)0ThD)@i2~fBLp-C>?wK`yJ`nTgZ~-mOCd|PL&YG&EXW_o zY$~#Ya$Q72%uq_uV1!2|C{7HHlLT^9f+2cXNE1jqR5O$UcqFKv)(PY-fl-0y>oN#{ zzAPF|OUa>v94M;=xrfTbSb%WC2;_i43LB7T2tCY1zzxebSP2UvwTl}#FTx#!9Rd<8 zu;g(lJeFFGdLUAQ_7gaV5(T0MuR?5sj5z`8oP+5X(N>P+JVdfVu9I?O zq#~eJ4TcXfpx{wk%J~q$v615vjzMli90zC(LLiG#H3EeJMo6FsF%to23>k8PKv{ZD zKzaj*QK*g#wZ$xGECb1fE(7tV5$qPJ$sIm|G>XLns1wwq0mw0+5F7(sLs%(l6hzcT zh<6MhP%F@z2%IaVo@nNw0YbEpZqQ&7fP^&QP6mN=PbD>wpqQrMF2EXuKZ6Q&4uc1) z7!V|a-VzQZ1zbY703nN3E2*YL$z+lsJc@vX-l7nqE+MUBsRxM7Y&eq8fbJ$EAzLtq z7d z6r;)*?Kr_oya9DUq*Q7Kz!5B@KuD!v0oX7WGCttVqV_P1(FvmujJM!1s^Ae}A&mgN z5knOLErH1$FqCtRJpm_P7&HvP4CW$;4WzF@v%>TOu`OY8XhAdv0VpP^=pZZ%IY%Hb zAn*}yPCROg6!gY)3iAr+XIl~oy$k}VEo}ja6Tl8>%lN4%*9?@)08fU&2xbnHUI zKnr>%VBUmr00kaFMnaV#P^gZ8j{)111lWSrHL+!42PZ-)rj5q@Tkl&X?IWPs^-{a6 zzO*}>9dUgg=GUjYXGPtkbli7uvAQ2kCZjh8ckvtvPpf)6 z^pw>8yNb-}ZriaLZn)id*X9rSAMRdb?cXw|-#xRKdGPD+9WnXP!aN7$XiwFP!00002|J_(^Z`(Ey{@!14#Q;Ov)F!r*bhYCE0h$3ry9QXh zVMQ?rv_!{5WKk2TykN+G-yLaNqAbZu)&Rr&A(3_O&%N;IgM$P3yc7ynGADJ*1;UvkZ3Sg}k6pqESo$_Nchrtqtg%XqE@SBCoH0QC-ic+c+@WKyIW zw1nSTQgCHxBOfkUv?K;t6-vXLLy?NV3U0__AI=ReVkwapvPfgN6?!RBxb!~AEBNSx z7AwnsdW~`-mnB+X4f3mZd9(ZV40RG zR63qalvrg64>5fB`vu^qP#g?}=ef+M?mL+gZnc*;JknLpiuOpZGL|z#Hgt0I7qoSa z2(TpQEWQVRCzMu(9LW?ByLf-`B|QJ|>&5xIFX6=>KY#lCX#&N`5zJsX-BE+Eq*7ZT z>qO!?3}3{U{J^5VhKD|sjS1MW zPcIU_;;F7GI13ExgJY_JZ?7Q?Z$kMEwr_4oaAM^hD_Jp5Lg@mV9dP#;RR^!^< z`rDa@$)3QVF?QbqRk~oKoJ_1zZ-BC|DSp4jc!W9Cyu&zJXhyxXxusX$)p5uATxI&+ z^Gj?VT3nh-tmNAcm$n)@fC=2B47ORpcm{^0;%ve-Aj7~!DMdrM5b28Z538_pU(+Tb=P3^E%jz{!)mT}xha6zI?iXLe> z!RHjzEvA9nGg0A!q(x$w65IvoNSArc&8l;Sg}sxXtWe8v&Z6sjtE^91Oz<^9EkSgAJb}4Y<@=BAZB=6Ubwv94^r5@8(G%%7=N3a@7 z`TRrGgNL@#HCrNo=6UUveExrAVn3x8+`-+u3ZAD7oJ2pVAe=~=o2C+6CbNcas~Gl5 zL1&y(l~Gck@le_p#ijXwitFoH01Yqe)n_x-mBI>4VgcSpUqEt=vIh#w+c*~nOx}_-}FX+ z?Xf$$UsiWi!;Ra>7(=PfFLY}=VgeY~h%E+(0siK)%hqOZ+`wB&rr|P~M!iQB-tQas z^EGp|kvdh50{oSx0mDXcM(FHGD>!Ra`@liAH$A~2dq3_4XJjKe4&d#6aEKY+_Eh;6 z(|uS)#}=rouU9z?hkXl>VjhnC{^=WzyFDED@DMv>c=V_iaf-&hn~>6JkB+C8q2f0@ zXUQYly}9^Kx+-(n)zindvaQ$jsB0RGTZ#3d;}GA}dcYwb*)DIm#UJkM@bvz)eO;)} z{Y_qtx*6>GamAc`?v`mzBjXGGZC&AZtFVg2E_K*R9d%O2oz$aF>TxIaq?7vQi5>9W zSALfv@H`F|Oe96l8_z<0D}a&AIPGfM!fm&F^jUe@-_iBfd0a9z_w5Fk|5*6R#v?uv{@EamC3MS%3uiDqRF1T252-yrP~= z$P@;#5KT|QBEJU_lKIAkYr-)SY6tQ`Z zL~K2he(A6BB%@g#?Cqg>(PC$te0fCRr^t-A8Fy#8^jCX(Urt#xA4(5@>#x4G8=K zeRcBT_~jl!Vh@P7U&4fFfsp|XC`riDk8gCZHyufbe|B<0-yRVb%^gyPTtVhr<~fOx9`q=SI{OkXfo*Np(qDa<&^3%3$@$C!0Y4F z53gRme)0O~_)IWSlE1z06!}abU-*|ypyoJX%jVL*u7ZYJ2Lb~<`One&S8q;!Q>dmD zDzrz46?$lr7{L)43lyfh(9TZI4&TrhC%{q-!A=7LDYjl18hF^1CMq_(J^U~Iu(Ms7 z+OzHLo!#y2(e7k-^z(G;?RZnU9xS4m<&__1ISN&{5`VARA&tUim~)`RaslUra#1S5`MzxY7_Paq(|y5~bD`aL3?QLWXMfnZ;9zxcF#UQxls?gIq&-Nl9En!2xsbl*!xnh?r9U^dm5HpFw{{+Tg z$V{N`CP5Pa2x%5JVFngBmq2j1ibB|YaU&cD>IfThw8>yTD2@ zb5fHJ2PH$|4c0Oyvhb_`V_lMS6XtmX*B$W-(YoYa7mxL;+g*mnd&65su6N2cte5Js zp3ekhrt54BhK&R%2ZBt8(ZBSg+8%@X>fKjcF)w980YB67iEut%70&$->e+c!+hT~p z^mgG#KGscT#cV50<|@kJZs1-a{(&ZNKLh8>|H0pGYLX=f2OvJ--Ftgs40TQzKskW6 zU(axZ!F~xbVtF^M?h)FeSlXso>Q*dm(;ID5kn#E}gnN|7zOAeGEHBcSoa4*$G6SvE zgT|NC!eg!mkyFNZN6fDKEd2WkE?!ARvFp_{mq`iStY7#a@v~N{KG*76g8RX&$Oq=#2Xf#XRulF5& zndLiHm1X;pzO%bE_M-@5h~mG=r&TJ8So|?x=td-A)P(h66<`_BHv$%X8+8Q2Wg)V~ z5DOD(7s7*<=m_>;ZK=iU!wAbn%&cmJ02Nnk&4aKi3j#U#5s=}LHc2w)2j%uUmoT-U zVO;0TW$Z(NN?NHZj$Cn}TE%nn0GdW_AS2m*l%4{@C_P1NKsPXaMu(<3jVc0U%R`>l zh5h`%*j%d&{Pbh7gdTpQxsw}o#VEOWexUJmTZC=qQ+xE2!Q*~&?lU*bXFqA=YMcfE zZHik%ZHFeD>S4{Gg`w8dR1m^wD2MI=P1GHkWwa2p5db6$DY{lgwxAbqOkrDvq@=0hu&YooY=>Q~_j-rtH&$(~b?)S6#sWxe z#yLkdBNriphYxoO7yEkpc2$ju?QRcOCez~O9bKaw5W8|sWA%t$Xj9sP&@pf|d=P!a z2G^dt#TpirnW>?QwV@97d!@Ih7NWih?rDvH3PQjFn1QxrItT6LW|J)2kk3*-&gQJN zR({x`+covLWgCyij#gK~QA8Tt>&EPwoKmkcYv=oM(|gXd!mUjm2faLidiUxB0N(Th zMR0#VaJzwmXE=gp0&DDtdP4_sgKlLO9k*=OM{%l%62zFX6KbGZ)m6n!9CW9y#w}$_ zP^fW~EGwlt$6bo0vXA}ihSG}0ZwR9IQ^q+;quQgV8Uq?Zyfa_I*+cDc4o^LRT%6sD zn)yZ?t>GZwIC?cAzBG$2l_YvneZxV$_Cm-T)U^FD4%qbo;Hx@^KcLCnQGvCVziNNV zrO>5FlDL~oB{!&MGQJu)!xTuTF-q0ElXJb*J1=jAIJ%{CwFXb9_;BDNgmbSrDLyXv z^x!fHgX%y`V5?;xXGdPn!`2T2?ET^_#-6AQtuEcJ0S0$1CUK19tAix{&{r44{mIcS zhi1VJYp1O>Q3I{VrSIN=Pi2WZfh;d)@F3tX>#g{v;kM3W74qkwu8Pay&_KysalUzj zr<>N2G=5dI4T)Q2(@y#i>WGzMKyWH>O+XxdvMb;ltit2Bf`#euG8 zvL!9EezD60ftSTkiyC=1jCGk@G_*ARe zlqcIWt~D|b(=2!RiBPx+T5i3e05hgRco_z4U3^fnw-znd9S&6T60#O$Q+BT1+H*x! zwd$LqfwNIEPZ=7V+}_y~PnB417fIJkq)!077m=+Ap!(gDo}p9C5>yPeFLOpv(%xyf z96dyckRXH_Rm)Uevs0kU=eNP}9pZw+{T<<1Q0aPOLcg7F)naO62gnh8r_9S@VGOKRV~VWpfYuN5LfmUL-H?}AK-mh%|aZDU`{YL7a0w&d9cdcrvb(x z*yh@!htz5@UMt26T21R^cVKTAH+SKW6^nei$CppmFCPoq`SNYYa&>O+0zd7rUWdP9 z>GGX%N58yBU1`v9V5})>+M*=4Ja+rZ_$08hiG;&GAXa?Lk}U&!x8OMREBneP`^%JUBTve=Nprp!CqNoOL0yF zZ&QOehp$d*4&9*FiOIjpbDr11h9{Z{dE<5?<~OPPTJkf&!rC||BBm)qKhmc4Tx>tt zY<3PZ6)a$M(qxL9HM)Yy4ydQXJ>`mfCKOjesv|egkgV_wP)tp^umN?hSam0Wr@a8H zT(K51ehmZA?GX5~VB$hBbi>dkFY(TvS-bJO!Nx!hoD$M;;bULTKUon*hgM~phCYOw zD3>Se3Ee@@12givx`q=GaJ$<%A2vaF);9}dhPQ)gHOZxu>i$^8LLY@sh6cDc;>Wg6 zA15#a0lk`o;7)8|?$<+A{Zj9su@L*=k5b`yi?~SHh7V)&9FPQP4&Ki@I}iuksx|{1 zFppCl4vdC#3ey}(M|&x!)88HsJtLy`;k9GYNh3IeU{v(R*&4dXmN_l_XbzL>Ybrho zQMWp_)OSLL`(?aHaF?PwpvDY_`wxtzW}T^F8oM!#O-$(6&Dh2^HeBATQkC*>7prdX zFSktkMs+2lL0w4OeFM3Yz`!kn=|h^N^BW7>OwHG#c-n6^WGBqA{e^~QTIS6|&Dd+W z%0j?x>}xI^HW-sN54OIi=+SVQ15fXEcIJRIz7MPT%+hj{**!Kj_PeeU*BK^8D%Xs< zYpB+44V%MDOVW19v7DdWQjEghv8JNB(t3#0>V`b)<7MXU>i4ADhYsdXGm^21#3&8z z1B7;UdP=w!Hpi!#RRaSDOyki%H!YGx%Ezmo8DsprBT<5C-vkH%I4lcu@{#44j*{23 z3$hQCV6hyClH07ZH+u{|%%8Gee%h2L-L3dt+dN)< z;c-KzFRdYSIp<(EUdm`c|bYbbo~ zAp%&l9PT=RnY@XM+(s$IM=)XPWps3JGBq{VM<0s_0000000RH*J!^N{xUt{& zS8&~}BW+}d?}tly`fuzAD@2r|p?p!g_&W}svGnJ$n$X9Wc9M0=TTvmJ0%v&@t5W=Bu5^j|dA zp0AgiEzL3-m*aA<6Ya}9jpoRrJ$#L$! z5J0@tuTp<9ZIo+RHqn;*#V*|}(ujx;=q@@PpEBJ74B*==A37tSY0MypB>E>1$&h0F z(83!nr=ub>nd*%9gI?|AWoj)Ui)>x03em120@UZEBe_CU2s~q7%Hq<*CQ}DJ3{nYdm7n-`0l6Qg-%qtau7E|m{fg}90(3Io zQ;-7Jp>&z+!EAs#+VXaG+tj)V9b8C zq7mx$HeK9kpXoMDZWvs|n~{?4ZVD5^a7Iz>=6hh=BAv~5&<)1jQj$@WG0?Fjt4NxF zS3m)X8|gw(R5YWT`&>^ZO4es20al4g18sqzJ!&?$f2vtkV)TlOCE4%xTMENdZHbhC zS=?+u4269s3oW4p8)c>C`cZoc+ALJ~jGGuVG9lST^C>7M-R?%?(){rD+(1dqBxoS)qA){KuB)z8bNeDuoXl&|1x04;LDB9z-Y;S>asRhZnT7~caguCPx{5*e1+mWnEk^GLW};C+ zL01YJujRG^)HvP2#9GU+gnH&{mC91!fGq|yUCB0QxG*1OtzG!FZ}Gg z?{agy*{!o_wu{fswz5m{@9z*eEHN7Uj?3lj>>d02>38hg-~4Syv)y#PTHO4iYA%|A z^!Z=Fl2x_?{$=^+;_pNL4RntE^f++kT8Bdee>$=AFrnG-d;9wj_wa< zDxcPnLhi=hs&!o zZSUHEx@E%Ohk&DGd%!jwm$mJJ{pv#j8k+Zj-A#B!75U25!GxdRy?Xnh9}mKOnrfpN zUE%7o-j3TRfCT8-#nmN;)YK9)F$vX)OTx&eovJqcf zWENgdU%Yzx^78$qU?L;(YI^bZg%EMUQR8U=`SN-LOaM_W(FFkWxYGVbx5d&$(7M7x zQHAr@%lGfZ@P)P<9x^AZW6TQ0`9(sLYWa2%a;a?NsVvf&!|5BwS(| zS9qrKG9HS7N?J;zh%y(BZ>A=x;A8>n0>)fo5A>>vz1SJr5}0;uW3hrg{}vg5VS_3Q zA7y;zA*R6oF93(@B7`r13*g()Q?|=P7S3T$Tfj}1%l(cyM_ZZzDA;EdK?c^lA*%#H z?Y9}NaKGluA>(Vj=2$RWP-Sy&3|>|ON7d~rw_gpj@Seg>V#Vhm?2=bI+*t`3Wa^Lt z$9e-kI*bglD&BmW_nwwYJQ|-Vm8vAE``c2%u8;IP{yHi~{aV-2`{w}?UXy1@RqGeB zjB7kav%7`84D5hg2mrX*l2mg%0-Zm0K+2(!`KNflyG<@&zDZEMLs9;K_TN9e4gL%Q6Hl?1FM^H)A` zvT@&hEOBn49cSw$opKoCPheUjf;%_#$MQWj3v_7ALT0D!I)!6{8XWVL`_2=nGH@v* za*Ep4F^ir<&}he^g)90+sk~eQBIKGuBmgZwQd}(Z+A9>vI-_Dc5U-inTC9J0K&}AF zBYK5=-mX`)!6`sTns-x98)7kKKt}9Oc_{`K@-6o76jb~qu`Nxsk+3(}wAi(jJYyq| z$2CS*_VTC&qr)@3ob7DP%6DHSp@4%2%Xr;=j9)jyDIE({{uHfb!vy`u0T2Z*zV9|i@l{&3+K<{@EZa{{eK{I8lg?q-&hfJ&M0e|PBc6G8GvQ! zG;N7t{TX+V>kjxKskru3pnjLxf@;nbkas&f!&vBU4(l4558C-TAUj<58+NWm;&meG z^YM;0vByNprTU`WZnk*2s!eEJ$I3KFM;fukT`sK>Uw>frS76aK=YR~zIY4~#9i*UC z6qGqROq9d7fFoB++zQ^b^eJi1DrYcJg1R@?zY49!q@Ft-*~alzx>%m`bV$vGlYHMb?)@=G)@0|S%_C;> zaL5USq_!A~5SU`(1{al2Q6cMsWyhQGr-?{Qf)Qq(HDXZ!EsP}m|0(*^B7)so*t+fs zA_ZK_CP5E-8tq{)$<-;~uoT|rK2p{0ihJP@| zh${xS%8z(XPudMio5nl|fzjA-*z#(1?DzjzEO2lNLQ!64*!I6$I4Oyu$*W zw8BX0PvP_IO#731oBXFP!P~p4ktv7?uyP7LbxY}!{8Nsdvg06u$}Z|H-hM7 zcTd*VQSHlDY=>J8w8_n+15m>4dHC@#ri-otN ziZ1dvAxrEL_=-$Z7$N0Hr=-y1Cw_<~X6Z->Z|P;oO`A1{ezma_nBZJ;q0pr$8bjmi?Qf}M_NXbEy1s`JNrVb8I5oa_TdXm z*4>D$zM1glb!mVf<=*(SWEZmjbh`FoLS1`A@v!YwObhtn4x)nIrJSU3ZzU$f>>(&< zG|JNJ9i^*+1o-SOG(dMr+2pYYlaX6J%tx}Li?~$d?5i9%D$dA>BV8t;F}+i8|kt==`_`$GO4Zia8%9cf3!^-^HO_duH7 zMiIC!jHpZwP~k>MqJ^(RDE0fIML~hlhyP5LgV}HV$JuXmupepa&3)s6xL@Cq7pHJx zC(dYSBS7r27xPRCg>L6v1VP(^q^`+C}BphoY zG$r}cH*!EkOn?dpq34)=-Y0^F(Xst96bjp~7xkv-$6W#X+y;W!E1g9 zldE8UtL%r74%rtn*8ZL^G(WTvk*5LfBA;pq{cxggvmkhq9;E@h3cS;m(^2xjj{p=Y zfPqYB)DPk~D;-cAKq#1BUT$eaw#fHa)@WsVU*%5Z0n2x*#SK4=lXD#Yjj_uVcY8~A zC}P(0T4^P+DH0H^XXfews@guRytvjr(18%PARElBXQHZLsD+1@%FXawNRXG-WfBbFGaRZO5L~9)$P7&N8v@L}N7t0|98m8GEhb%Ouu)ZD>~wz6m_i{Y zxc9%zy!$p;>%G3+5570oY<+!IrWy}0+j%76BD5LGk#$c$GN(Uii9t>LCE;n5>|gH!{*s%}=y~mk~?GgLid@3V9tSAa?Rj@JSvb zLYFAd7_D+^*KZWhab~sQof%r0eMZV#ZRl7GtcsflnykpKT~Eq=JC^e*QtP%U4{1IH zGz0%>1*K(3BPB8xMXeKj{WV|0Sw3~Wk0q)CW{rONi#o6S&yifWaH>Gel&E$%Q?U&0 z_WrzS4)LK&N_YcW8W2HN+Yn*uWUryOD@9(9CG_vZjEj2fL9y@HILpNLEPkfRhN6jg z^Vybyp>oE}mLyt+-c4qhR0CHw++V!Oa=p$LB0|=O{!j40f-O{59M6M$HI_VIUvBj^ zXy9Q+&b4+MhKHHK&w{nbPBlz#t7uQXYVWGiu0G7lvDC~V;0QbJqlhii4!dj<+u_0& zm=ru&9=gYvhPytzm;PjGameBIMoMpTKW&-LISsR3-2#|T)6wkT4Z<7~dpyxN=ySXQ zj_EsK&P{hRG2X_Z<@Gv0m2vc=DPv*jBTD5!_CgG!9ZL7Zh{93Ai0WXmhvA|67#xhn z5eimF)PNoSULn^%Mgu{EuXZ=o(>4?>Gu%EJsKlN>xbqUk!;KLj z`me~b9+xLFIC_1xYy2|iMxYol4=UXQaNiaovVIk`gnIE)gjS3}#rt(ZXynGfYG#ca zrU?;iuZIa;g2n32PYsTYrlo8s3MCK7Sx#S6rwHNSF58>uk1=kJsRP(LJwCD311i90 z(%e5(!MtXraDV_iucR|57^n>3wgY8ZFQiX~TtokS`G~|$-c_0(R~$Y9yzWIte=*ZZq~`fs)NlT!G>hMi8oHY8p?i7$6>a zlG&SIDfZGyBg!qU+L1x#;%fAvej;Wdi!dLKJ@iJW(MnCi>@wx3)f)`Mr!d^1P!xf) zYAeR?^{>^+a&?%HY7m<5UC|Q6T@Mt*qj#Di#r84*BV{m3<7&eST>XQb9kfZfz}CUR zkT?)6#IU9Ed~m1Q>J@ZKMK3DT(1DNp_RC;;E(9%y2s`z+G8NWV@tT-CCB3D4o2feP z{-dVmdc{nKk}RT>u`(A`3)~BXi(FBb)BgPD*?6jr0EOH*LatgEovW2xjdW<=5mk0M zbqZ{E5esVdB00@*wWMGoIMD)3V!XncxEOAa6WSSL3%8_z%`i+U47JKHU|_HtUm@x5 ziumTXUn$pQEaYN{136ehPKw-)Ld@XeFiYZ_K%?hkh^+}I0s3g6IdZ>yAR(cE*>4>h zQ%WMSC4?SkoMgE}25FrpK`;C&`$>VoqKK!#LYpN1WQx#|6y{;|0UxyPC~-nfkg6R@ z<+WOASai`&11%3j1(O1qvY-&~AVm$)$q){QoC5P16{2MK5FbO-fn6mY4j(|$b)dFotSu$;ouO)@p+UAdnvdNF&8_=x`eW*kNOzmoJR+hRV-2_ z#K($TzD$_skLhCEc-;`nWITd8iG?5Yg^q5F0cGijk`{Hc6uZ|Xvi-*|%Imj=Y}!j* zGT@5OTe(3Nh|Ly9ADz?Xf3;3Edm1|Q+;rinCQ_?u@V8NhXHMekO6A4*J$_4)tx6V3 zSea{o4$uo&(}Hdd?IxY%CQ0c=C%f&NC;)=EhiE#~7`-TwqCn=K96AjYs_1wJ3ip)0 zR|3(h{qr491h0-yGkrZ$a2wW^0_&*E0Vy|zur;oeBhd9a6c`fQwu11S0}J|5{#%bhHvVz`VBM9(l-cc?^lhSiRD1;%(oMeH;~vQy z#EAGFdd*Y1Bp7VA(@kwhB4*dqae+k-m2gvD6iK$`Qq`6r2k7sswiYUybQ39OMyl~<5L&xR=m3_q+H7fuN7$*?uw8LSB6(FG; zr9olL!_}WGOh{@ro0BDp(lo&PI8W#*Ph>-`z)IsHuD4vEPl)gErkttH0C^u_r`N0Z zjp~^?dBV*yRf0XBmj9s7w_;EwhmvHfRRnPj{2b``|uhc}q{yZ0+?HbPCb$Ja-9*BAT!W5Q;A+KYZ# zciMC1(}%O-S0>x9Po8h_owwYOxF3>)hjSjjPg_ZAgm7I2T4+6St<^zcp5k`yJzt4u z#zc-ZK`@qJC3+3y)@3dz~- zq%<_ANIGbIms6!iiFT>nJ8G7_A^8O5Z>wy; zOFY^Th#XzrhlKmctOgq5)o)Oxg8@6!UuQ6maA(Y53qVYX9@*T#g-&*XdOwfse{#w~ z3SnwvH1ILTFx&`C$~}Hymd(oR_f4~ zyYXaEgR~63kUo)@52yqd?FBCDo}8AsU|JZumoLVg+&&S2Gpl_?ml*%h;*t#GVPkXP z^)WgjKS06DURoyHC^b=OxCKR*YfoQ>Z9P{_kK5nvH%ncRF44YB66lo!2k&YmxVl4x zQmO%Re38!fm9Fd`yLpZ;bscqDh#6Rfm0+DBAKg%Lg$r);UA;H%zIOR^Vx=86pI9@d zHq}h1_>f}E)KaS1b*kERs@rwylwvrv`xN|R0Um8~1$L${j+QUZnlFxMkNL6Yi$Fj}QrgA7mMtN0aSopeV?2p5#u}0&LJoZO7=df6>qK^Z}EiHfs!UH(!i^= zoNF#NLL6_%xLDKarlm^u$#`Ax!unGZM^Mu zyl|wETt9S%*!=WwsClp`E6NVcFFEiWqr>L5XTMN6n)kUCtF1-N%WR|++I zZL;}CI(8#BpIwiT2zRghOC{&B&zQOgr9krq^d>?-o2xh)-!}1hJ%0@zzMmeRFZUbp zv0Z^Sc8u{NR(cW`^fo<1aLb#=k4dkRuwqs5{Z*xn4W_L#+Zcvl4dM!SgjV`ZLlQKTc7kW`T{Y3P3LzT zp6^`6)!EILpyWWop7Bi=b?s_!HyBHGy;bb6@Bz6q{85b440`dHzM;5VG8^C$Qd zn6Ei_zENqI#?PrEOL5uQF%*@{!3C2|AUM^Y{d?SLHPXX8^EBM)nDI6u9Qj(DfN523 zd(%-nK?${u1B9pZWITob`AxEQzd!vd7`qE_(K@MeV*1^QE})*mh)&uV=2NRZ?lLB# zN~gyw!W-2_(N6O$^oeA|50~sMRP*-toKbg;nETZZvI(iST{ekjOh4g}yov(_$R>ag zK9WN6l(@Ql#ZJ5ZIiuu4YK$t-;1BMsDve&riecTM5t}%M~n@wnE`wHIOBLc;!Ky{i|$8(5=g8 z#|#q~HXwydjk*cf%<8so@AyVg+uBzN(S-w`!Ec*w8z%o&x{-n;cJB=6iX-*LuqO$8 zq2Ky};h~>K-ctsa^&MRx{=;d@-bWdu5}9b(fF9`2aIMP~(G8ur*F z(BHxbPKj#}fC&PiRc_e%d8sr*;-w-DVKooLEN1FONM{X++t@^p zTmF-VB~azUjOCrBA?OYyUs)R5#)s|CJa0S#iVacVLGTY4qVo&Nmc<;c{j`FZhbGUA z+4tsPh&>fHW*4A*smp^+!&oXU1?Ua+L_6xwlcdIFE(T8i=DVNjwz(BdzH;+bRC+~Z z`sYslMoAsnU7|UtMBj3#R_R)wHuD_G)}U6!11xYp7Q6{h2NYQ+y-v#miVyjzL8i_( zpK9!~8BOA6!#U$r&xD!rnsz8t6N@@R3A=5j(7J;JlO{Gd7>v-^zD?E(8Al zm%rQ4UK@pnDr7NET;aI4?-4N_`n+k|8i1@_(ueGMo?EMLN?cv+iV@41Wzt)YaExP= zgA!T)Y+)UY@W}=;`8`WCrQBSYB4N);?yE&qR8m8UaT$Sg-pT&WF^kj$tX7DsxM!_} z!nJIW7BiMXi++$7$Y!cB=^GQp+4tzJ!bDd-TaiCiW1d)S-&hPl_r=zj;87GeIGSl6 z^i(FB4^BssnzPkxYmxnMeJBpYh-}b<4yMeI5Bdowmdtb+uBiB??8jE*MM$1KIWyaW zg0YaoH5{%h1k`D%+cp*p&&nDu4}Q3%*2q*@D`?tN-9!n1(IV#y3gWnPy=!B&bLUu^ zW7;cpAbJC}d4pBrW*jD6QwLh8(P$FDYtBu#O>lP~+{8~3i&y5~T2oga^GZ^EG`Mxy z_-H05JMmc+(35{L7Mj%b_42|t+TaOLA4Ozj>Z17&cYIVV+b>l1(L;5})wg?TFEbE%A{= zA}>;jXt@k2ctt>mNind1*^B+092DOBEa^-Low;zE(7!kZZhn&r9|rw$CdPu_PVF{_ zI6V6uz$cG5&cr|k$ua(G#R)x7ag21F7*{;Ez+_Dr*R7VjGGC~{fD*$ZBf=yypOn_= z&lNNGZ(HPq5!{%dw1V4{x4VzW!|ehhDjghN@gvPsz+MF8*i3%FPr;3d_vrPa-21Z$ zjD`hLjV#d5Tt~Sa*a_Lvq9%^*Ru_3x7;_|s1xzF15TebOtCpCd}{P~EBR;ZrVmEuyEHaPE6^T0d|q_@?3K5om`c_++Vr?)UQqJ8 z^fyqtu9tO?2au#->40D`x59CdQ$%Fh~S_M<{#MCZ>lpPV4WdOi3FS*9HFugjL6~8%81qfI^4$w#xWL2k*5}JiGd4oL1 z94fU$zN2?%V65vZWuKUi2{NoB>jzb{3c28)9Iwj&8cI`CLKTuYk%9MO{?KsRY zrpZQ_osHG)lr{>lb@5Zee}_)zmb~K_`oYq8+$>}38{ZH))VlMxS&M-#_-i(TGW6FQ zYf8ygAf0A2shK*LL@phg&Glq0+^HUsYnu9?iMRmw&M!gP{*pnLk>Uvr&RnFg!Jf4k zGpWw4d}i^k5vlgVm_kcxEN*MW@gld&T{f^B;2allEp=NI9;b{YDltJiF5jWnu&y-C zlE6+=6asazY$=dqtl_e91|C*U5Ph`?gxo=v>`>xuQn&=8OP#>XC_uJOt~sMyFbMft zH|@Gz-_=n@fkcI)TxQmtB)%7+$&+SGM_Pfk@Re9XSrc(lue4GC%;rG2TcF2nT^p{<;priC$|ri#8h z>#r4Ml0|-_hBu@X_&f#GncW(YJW|}hk)FTnM=A-WoXrLHGY5sNY?eA@7Zcd-1pK|A zkwkVkBs(rm(I&i7q;MzU)z{tg>q`efQuAEJ3X~$1xwZf=kVfOAA0>BR)lu$R1Y!=V zEaoDbNkwMksw7jG^S{b?hGY^>h{-KHY_sipyybfc4$|aXdtW}9KuTb=N&{xic3?X_ za+RZH$7sss;%msvp55rHo2G@Rr;V6%D36gkdDwp|# zMr;PNw~99b+IfPX`7SLKDRUmnw@M99-itNrA_bay=^rw9+jWCkteTI03_y)f7 zS_M4(Pt-h^%YSm_d;XW5fABwcelghpP|orN3|y;uOKh<@Y-{cG(OobjiAVTggPjvC z-VsmbI=RViBR`%K+`RxF^(>oO{`Ia0!goY*#Ju=%yGE*nC!8jIG&ilXB4GyEY({}Q zB7gJn@S-KB&gMutLW`V3vtx&0PE$>Jq+Ix>9^v?=L$s0$5By{HWrp`9eQ>+|SUnTS zM@2fMs4OrtwBPMi|5oX+?vDM!>6>YE5-It~3f$aHAD z!}r?n4PrJGLgI*8^%?o9KPSzHAsv^Ags>1IMKlzgorUY`P>D#x>b=g4+4xs?P|~1) zDVt!##lok>t2|UY2j64VvBW_r;bmAA zGUt&U4D8Yc{<VW=^hw$ z3tXDSUoF{@9OWobe(Rx1PcFC!AjndM>%(tCpf!$xNAxepsCTBAz|u>DlF4PZzjj)0zgH!T zK55+M9*{g0pgX_D3UpuDg9O#FAHScF_HFsA!(Gm^Gfq1w zDK7##x+|J0Z7eAXd|yxt^JWfxwj7j?JwGQqc)PTo#FHjq&W;kxa(I}MKyu5lk94caVG%UrO50O2MHc^Zy z`mS^}mbcMXwG?{a2xamWZ-RuFydEL%1s#uq*Nr|I()mBJ{n-;|@B;l8H6xFkDH@9jeMG+<^yX)vH=7uwY;CrO+=Kb#;B zp=29NtdF})PU8p2lSbdLl(LurA|)$-8i4!iUn#PuJWqu15V#}ptW&zXC|ay|{^E*e z{h1c>x4GG&#o~JdUMRpo5aIQj6}D~gd3W@3(3s`EAkW55De!Dsj)J<;_OY@)ZXm%H z{k+=@zi+z!G!zlUDYCcfx}I0NMf!9|c?)lCIYLLC3it^=l}%cztMyaY7Hf}qyL4Ho z`gSlri^Nw$+unZcYn$0W2~KD|$X-&TC#9%PxP<9WlV}>$=B~-@NII5K>{G3km=9mn z{#(&&2@^DzzYXxwH#yXydYcA`LCyGBkDBi$qSjUOR%|!C)&a?KZ7h$Al>+>FT*qik z3Aa-rYO+A-XxF+J+-59-*zs*yR}Tn((Tl5Ux2f#D#oy)a7GyVUckU(G+&a8X#TXOb z+)8Y>wa@Z(IJfwzJbqXYOKg@&8tJHpHK*?l>4Q zWwkgU7hV;g1>BnnB--v~F=sQ#z)>$UC}OHeQdTIo^WD`>LOYsx$s!wdm_)L9?u-!U z&c(qIkTk7^S{4ZK$b|V5g(_85Z9)ZWqMD#l1`SdyWrm0|BI;;qPGf3L$(z#J08MSrGhDB@%|ozpGdDH9m*C_pcRPJVRjJDXb_mOcqq=Ajb-~ zrnuaGno7ou9%QWHSZrB@(rNP^DnX38eU`^Y@hNNYp=yTp@cpv6rGSZAbB{n7OFo{) zP(Evs%ul{9d)8A6hca-@n! zLSeG1YWKV*6h@wNgz0w+&Whl0-P)k4)&B34H&DFoD3(BsHE0mem5FS8-0RRP7efyF zX{;31V|vimxbXNd`tny53omI)DMPQ*L%t<$H8zLk#Yr(C`xtfO2u#0_twkh%hyIH} zS8be4>?M^lA8DNp(QbrhJ+y@e&9>;GFre{JhlEI)YA0_b0diJ7GW|(Dc2Q5~tSi+w z#}c|DTSba>0Xf(OoG1sCx;mPPuq133x*0khH5w80-b9U zdNx!r*>|E*QeL@?Wx7?%QJk}Q;LC|^FmeAfN0J+sati7KI}v32nU0G{6omURSjrqb z1-!K@nc5yY)eLShgp=wded)4S3IV67+hT`i>^rYjtZ{G_7TwAbm^^x>_wsY65zBkm z_2V30M+kP<`oANcjmVX1kcUFwfSDIsUA_uKommrK%njHzqh%TQFj$c(x`@><3g&a@h`i8Hx!);bK+Ohxpx`}%!%;) z=p=bQTs=|UK%pkJ{O?7D_|o#8JWT1>*(OsKJO3#m1D<%~PTe6@Nq*gqryyz8l?y{VeEK^?bp_ z$#QP<^n-|6InmXs`gaNvmqpZTmR_$w(x~F z?K}auO~R_I@jT`R%ieoY&fqd>(tuTl?m5-_`vpDzH=e{TPu3kX`)-py3m z#l*(a=oh@8gNwbhn3JKcsk^_Rz zot>S5L0DLng_(_+NtjKHL6nV2kVQyLlu3waD0(!Uu!r7W}5?~Kp}{MjYZL>wo0*`KqmRN9oyOMiTgCRx#G1>$S1 zH*NXn99cbzQEd4-5=5NFDNI8^ND7l9SwAVs$1;O8llD$tHNJ=V+_lb4dol&V? zmj5{EqJ-@3U28Fkf!TYlc|0*Od$DWVVyH$xc4W8lg^zO{OH~Qhg=pq`!N^7c5bOJ( z{^h~~1d|jkGH|=ZKjT1(=g;UloC@~eSL#&TwqIjF^t;w~^j#Gw zhql#h#v+1Cc`!AS39K7lAT1X8p=xkfT3mj2NfSarMPNKp)5o{JT)kg)0q~&dp)qF~ z(Z@8UuQ_S*&dF7dk(t`-h1LO-aDKC-w3Q_Cz8-9N$Ftd;^$(4Wr(8RVx zPZXBXkd}8|@g4Gd(U32#dwFMoS^OfBcTobD*=PBzwDuSVON zQw}VB-^#$Vwg})3i}Z!)*5qYRU=V3eh&(HOK|t7PLSYlZgKVSR1O((6aq||-pZr9L zi}kUiH}^J7u2+}4D{owN{Hd)QDrOM+WRmwM7N$7Pz-IR(^ZO`%J$oJt__er z7^z4Qk+f-R5fL1g;#%H&pn8|sn_Jeze&y(edzP1c6eizSe@w9}Hsy#rSb*aPqx>bu zo-qeK$JN79E@31Fq#>z@9#Vt?Iq>)S?zJ9~D@3g(QVK|G5s5hlBH=M%cZ3ZanU)J! zis7`Jw#Hy@gDs>Pv0YE|5eIa-uyqLrUPR}eEy|kiGrz#~g8pcXxva`c=0 zQeaS_C|@nZQB%2$Yb^lm)tdpc8x}|kOTDHI*}C%Cv8VC#_Ca|Vl;GY(yILalTm=Ce zI{7QA9#$JE7Cp;)+(|y>-sDyy>Lec7!KS_bp$SeS7VGga#~z9de!o*KhQ8!qv7m~K zciTO@pb&llTm8Z5{9K^su9ha#|7&^{rHtGC5aL9izaljc%huo&T=IIN8W@#8M3o9h zwekxSL`BHa_^>fUzdMDcQXfl`elfzXAdj|&yH}U4+JLLqre@G0Ih7fN0;4hOR2ry` zg`^yUSME%s2b%vFOgO1{E!AqrB=8O+t8PWhOBi%Cb9%R-W+>EgMcH!t=2Wx{=_ve` zCO~jggJ99VgRxSQtQo+9ipxu5S!-#OOHY@}0_Y>hFc!sD7kVc5@35hZiInT1PpVZ` zdRh^tg*5cu$89mhByP)y74`NaL%3;*4ZxMi35fux#6Ts`8F`wcVH|8qJE-`qM@M)(jS!rv$7G7+z@qBfCU<9Nj@ z8K}*NZLUJK`rV+sMHNG{Ub(Vp>RTzc=52|)Q=fUhKXIZ+ShY=S9L-c&$vPM+v1lVq z@tg-bv+0XAp;t`RkNFsOk(8!r(jKFe~@72)mgCr?*I zZ;vmR$9Kq|w!6Z2emknOUA1gDvhS^0gNMh|bn3Q7eWiZvF`mP%UJLOG z6g|pVXE^10VF<6u;e5ao*R>kQa$SgpW15TGIgA1FE_}1OOEz!=FQMFCJaM>wWL`Ab z3>Q+Dg4oezIIoaV%TV9u!Ym?p0uT;cefg3wM(?I z(INjcufa^+Ozm9$f&aJerl_u4tqUOfKC4eCTPYi}lZE7S00|3WTl{W-f0)#gCh?3Y zRDDJ^j{NQfw70FxMdTJPPOLKjgvB1;b5=G%BGhPDjELGWgF6&&hf3_!66k-AagT%;b?U8oB*5aEp*_zejRIxg4%r7TO)*(Es%TTW0dnTjd_ zH=RCebO)=vSc-l{5SL<}K{bdzVRQu2c*SYL%n zdbVf-gen7WbxW;W>Ju^t~pC0w?`1XJ6LKUOJ-5fiTn6uBNMbU(*?Y zMRnHH9{%}^+qovu-%OJ6nkMGe+P1NN_ct7>oG|!GmaJ%T+A`T4EZP&;|}{x*WjX>9j4S%rFEat&C+>N!NV!kRr%=sb$< z)btPn)Q+7kS!37hO63mfwWnouUmd$B)E-g0hky0IXC{Fv$+MB&6RmZu6`@W8C|zXk zaX?)r2vy&prgL5}nX37UF2+5Vl4m^m{tXh6#kv{rgdi?tOjfCM( zw<1fISfyEp+M>IGwE62K@01F$P`1()acvFLV$k6lbq6&Z@X=#46 zV8}RY)itHhqx15f)nLw^_xTV3xas`*7l*DtVm3$=5#QaOUB1D%l!o}OE+TJ_`!AV1 zjXN8tO?j)-j4RP_ZS{5_t7K|(YF_}siS#emR(hRny6c`Zu5F|@&_ux7Tx+hS6|}=p!wUnF;!e#r!t4F zru2|WyQx~YZc+7I$qkTW8dXr>-=%i%Vw8QV&)I?V=p9I1(sEPx*}=&2s7JfBh(qzPuD>cYwb+!lWoPCzez~}5RaIRgGghQkwNRd` zGjv=|p5phu^8PQ;4v|Chy3-{?R~~2HV54JeVi{oh*ycy7JGCJ6G*R27FmXR$6&Gpq zC*;iNGQZC-c`*Lrqji6+anWZ2QqRC;jj_$6 zn$fj2MDN4jMi;$Wk*v>K;?4gQKgw;&I`C?j3w4|#tXI~QLglq*>}8NpV|3cH-8Ltk zwWhp%<23dVqGwrGr;0C;Kb*Idk}KD#c#?P&y5@3l>XtV%{~32GCv$W)8QWo>z8&zq z4d}9afXgo)x5;MTdcKQ0yXt7!_G|W%uf-WMzh8Z`%PjgGv*YP{7)|G+%%-T%K3wDK z5~rqj4#92f^U?P=m95vy?|`ZOR8D~{ZQhr{GSBYWt4UHO{878KEU%>R?-seXOlzxA zl&hPqu201}omS%`=!2~AuzSY=)P5}Lv@LD%lO6HcW`Ox zjB0(NXi$|del9VKtc+h#De^7v@zbVNOa4Y|v58e%RTtg^i)PxFY~u~d*dp^E#)#jgU>3RQA53sK z(O-Q=Q2Hk5L0<`y9r!dh`Eh-%KggaGAPmP;nWv(>`(eIz;dv%x_u#=~@08tsd~ zde>;dBaPmc-0_4EmkEfUfYTn>yMwU_wS+yv|FYVir}W{QLP0C%A_%X=mF0D0)$s8e@{R~%p2=~kJ-8U(No zTxU^!bPSpGRH}}hliI?Bg z_vz^4b@buo^&Y@pLzl@Eng&Nk(~rP5jCi2Cbv>q%RiCh*cKp*){Nm$%T|YgKTE+%s zB5SdcF~bIuN;WSpFGK;7mgm*YF+8K zfdIM76yNWYqtpyLi&?o{+?=_3f3sz~xj*wRM!c;!+@;=05hYhiz_B|RaR8z44>Wxb zk14B*^27Aq{M_HC2cbg~N$MCS(?6WdX}!afjOXL!Pu^+XWFb8%Lt5Shr|LX0RCXb- z!qw&bef0qrnd{M4`i1={jF;BT$R12JynOTy5_L#EED>^)wG3dKs^&_@d2@TZV)Jcz ze~#)**qGm~fs27tWBA`mkW2kz4&p4uAItXub$*%LZccs(`(pm?*Q>)*=OlJTeFSzB zVadH8WDQhAI<52NqMXT<74Wm2)1rlvvP`<<=xK$1tbR^c`j?MinBd+vfqwSq+ugw+ z0tFeZqcR=n>2f{#F}0Y0y`+bsHJiT@Rxrh1q)L`f8!yghi}^_*`4GwR3c%_8er{Yh zx38y{Vjb~n#k&sTxJ2KDYIr;6MnaB7Niz@!t(X9gyk8hcw(bG)deI2u#J;1coYW~T zc_(pM{EAehBx~GnPb1aQBy1!k~QHf<8-R$ACtnE zN=-YQMF`liG?c?9+0zl;b>26BB|oFN zw3#yQD^ll3Shl^D*%tU@K6E^+)xWuxGiw+oT+0rc9<*tVxHjkx2vySA<>T)yJPonZ zpB4u$I0&k~7WZ)D0UeUN7~A#jk~u76f}Im?$xVX6v$As7a5iEXs~hQpvMP(hNiBJ+ zGy@g!2MM=w>JzX>`MykeV-h6+ZFB9`b!9dUE`$i@CRymA{4rn2?aAAya6Z;^j5!)@ zG9bO9`ng94ao`Nh>=gF8i!enSk4X|j4cXAuHUi~u_{ z2RWR2CNP!)qG`bTl>+viIc)#iJo7pIbxlbM6c5MAh8@()4axaMww4UGE*cNj-^}0I zU7I?>z~aLAyDeVp2kJ>+%73;}kgt<%Wd4-Vf1iI>FIw@Ek-Q=Z4|$nbLkDEc^?Spt zc1E0@Har(qbrS21wdz6M1JFUUviWU07G>A=O;y?z;KzzT;Ef6J9=bQFc|5faPbd*6 z#UM71hL@N{sIGE?YH49l)4;J+N+^@fMcSrk9+W~MpHJOUw^-I?{A4SG&t>J4X!)KN zGiIz5-wK}|;^r&z^5;thXFUF_jS2d(4DvQs(So`|>lVOEl1~=BT2I^s66H_6^4pz9aT*sa5L40{!NwfRpQdJbuY7cttQNvae$CrADNZV*GL4&%M{fRviz)jl+8H4Zp4r<7J@2<)Y>YaRdB1^mboCt zAvq_x;2#?>=w5J{Ehpg++cg{y@pY|L4Ql(mK+NwjSQRFaIuqc2IVdPc)CCnuDlgrx z&b~xvb^4o&_6Llh<&Q;u!$sk6j0Ldtr9gjhXiA`R*Q9$_n!FMGIs2U$U&fZ}K*O>= zv-m^9r4Dfczw~u6T)XPwOL6VhypmI6$I&XqSeblfn@eVUPz!Nkk!T=+#dk@^@!nU1$gUty(TJ5S~AO7*Srr@nw#0WFq4NaTa;tt zdey5bi>j_oqoSnCs?et!BStA;YQU=vKcT!=w_Q+gry|mRKca4?MjketxoaQ&HyRX3 zbYR>zh~~eN)T6l6Op?&;zgbQd+kZiZ)By&1}3Y)x|BH_G&DdL$A!g zLj6U@qL#HmbP^+>QR*{dYhg^Nx88KKe_OSkVf2i**{Mklu7 zs;I4)<+rM%`r{Z8uNYG`OzRj_8rNo~`8YT_ovWvU*$K9AK)iYS{O_vqxkMc;ucH*#RasBHqlii2b0upFzP#jek&pb=B5T~& zLX5|!k5MfUx`+bTJ;b+Kb!v|(3t{JGuO2o%QM1}g@%d7Vr}W!WWqz&Me^D#)(amaR z;PZVQ`PE&ESU8QBewWdz?yzB&B%2*|i$=@VzX4n5*d41+e#&TJxr~RE?<&hN8wq&d zu+^r3Abz_NL;QpBjEK#RbPGim==Q@mNR*s5g_)ZqSjLrU&9T9KAbjN-oyTMB-=+W`t-BwPbdig2#OcwH^HI!hoJfH z{0kFp5qS(Vmb%@`%0kCjZpK|hDRq5%lHVdZ8 zB+AVcFw3FA%g~O=_RXR;xF6`WD7mwtUEFELgIsz<9@xAU8_TlGGpCoyFzXB5;=5*( zCaZm7cuG}GrB}>keDnr$DOMEdPWwx5(;k2A#v(v`adMYz)JNU{? z^YqN&(57wH3j9yIH_`rTl6*~F%sfEu81x_gz&ok@w`V19+@g>iK*AdJ!UaqD)Uie zXcMl(D{S@M@j*C^_Z5cWRru3*2^UOOW2bcBW6UkI*^TH-m=^JgF&@uz)QJWv=uR(r zzE+g5tW>@w__@JOPBCej7icxJaWn|pB4}^Y*1%mFFgLp7BCnA!H+k`@lnakv^|4Mc zs{bx*V)%Zh$lQ^#j6z$#B*4b5hg#OYhZ}qBJ=C6K#6Ql4_B`S|NM4re; zX=s$xFCuGV#xVrS!5g0~$jgL#6#!b4nN2 z4%b9$y4Zj;+?crxxo0aG)tei(chdS%0tm%sIFWnNxWF$q`b)`$e$U6kdSU_*syy>K&l=6GI>p20dz2sGX| zbV6P(r!AxppVwdR=npTpJXL~6v`GOnpJJo4no4ImX2N{TM5^~m3wIrlJyP0GPcccW zfiPXbvW_*MU-M=bJ~Ex=XgRwGIdV7nEV>|o;{^?$vAZM(N%N%hw5Gb*y8t`(v@Y>1 zY1}{#k@?Jp0&xG+NUt!hcBlt+d1yOqOY&g#(k1J<3P5eyt*V^&8%-g4JRhw|j#}gY zKLCC}fxkhHE2s8x$(0T{li^IrwhT*OS*;6idly6pfbnr9(r&9%fYVHzwAF4ru^w53 zGctZ%QQW)oSqkKXa(<5#%g2=v{}qMvK2_lRiRiuR!oQ24e)6PfGsvuVZ1%(>dc7f> z=bO!1pGcs$9t=H~Z`K%&P0Iy~t6QX?vgI^|6FBOR9tYM!9-@m$*UXLkmTVd2IQ2ez zr+R$B?usZ{6HP1f=>^$cnf)2oMKxWmUV857Zaq3R(5ASY$0~VsaKHIG*nd=md$Y`e zmGr0&db8a9MXI~o=3H-sskcLy88p4EZ8;eT$%?1H9^T|wxLZX#^=9{>T?*x@2U+d4 z>WFO7)|PDqTZFbNgstWt>e|zmVcO%com1O(f#!5PLVDXHdHQHoeYD^YmZ!JMd3*0k zAqwsBwPg5H{i$@~JGP?rKxIa$SY~ZqE6Au^9e(nDn+F+DKxbX4`3(t4weCn~gkhY_ zgA~e5eTUX_eUFvrI2f6jddu75eKvous%1;X!9&YC6cXMp+07M(OlL3{J8*U2QyrF+ zAH41yIGTi#isHU0G?o4MJPu`#*w&l85 zc5{OOIETfe2t-}=JbANOT}?R??-~zpKb^s7rnd@0+=I=(V&2pZ!cp#uwT0C>?rvwD z)yH03vjuyrj9rDZg;X@CB8O&dd(9u!F)7pK);eW*x+u1Fv)<0ijd%I3c%9bLxj9g7 zf$}O>r(ZPADbeMQdh4S;rUcFvJKQ!na}#&jafX<`NX5|}YL&a9`B+wJy)H^;b>gpp zy7HJuLY>CWkt3i^ojq>nAlnC_{CYK;>m8Hi?yqUlO1^2g?ytQUk@6+R#$?*a~xlE$$Hy`@=frUCO&+y>21+<^`RmqJkEgjm|ZzhZt9PM!*Hcv*~z8UGDY<^ChU;otwiu%60j2Si^=$?7rUD#%vb#8odLI0Sg276j=@W&3? z|CTaS1Am?oDYI_*saz^e;Ycd&5vtFIxrKS5WAe;GPW>a4wd1=CoTpR3?m_+XVdFCj z-ALoBU1wZ$9nESf@eT^RiM}pb-6M`hdOtz#b&dbgU;WZYAq~as^nS^nooY8~zdo_r zSG3{pi_VMw4yeNkoyY8aIS`0Xht1(ZyS<(qeY2TA^|wP*ARS=ud>?T@VwXEM8$S16BWdfa&6nge{m>d7Rxa5o!Cj^ zfW>LdU0*3bm601JabyR9FP%75v1dDOq9PGFsT+AV^OHChu^0Kmmnw+;fCnyDjGlHe zl`3r~ zcBqL-C3U+CNW*P79rSOl=@_s8=-I6N4=nOM=*zS8@Vv>{$rgHdtU2kNI9O~Ae6hK{ zzN@PqE!KG*)f( z5Zj@QEdIe@Z@Jud4#C^@LTd^>8bma!28{>5-H(lZLb$g$b%>^|gsr1j@8uuvU7{h< zx&pLkZoa!7Vjl_J#iHF#88F^Xw=z_2ZZx(&+S$IR=G~tSpFUo+F2I2&x+c>4@DQ{_ zywiIV_1?lY>K2_dd4HSmu>;EaB7owwy$+pJe(l*|ML%4&gT28s`|1N_8z1j#eduPN z49EJu=ZN)G;rcJP&$#R5)0=#OXp;?8Legce%he^F;itDzG}+kZ)T-yK#ciz7#`CE9 zFvfWALl}G5)5DO}(|aBitRJXt$f*4}H^lDUM0rKwa1OnB;*uF368C>8_Gj!;s(v)0 zn{B4AMtS05k6d(}jmRO=^?dE&zfkFn_5rF@ciVQ$ZsgLbtC#iZ?yOJi3-~zBw=}nh ziF#rFvR=kuckK6lj^WIU(v6^h4>dMNGJqUC<0v=D&`cD^C*5S2%c{dikHl&E(5rzX zP4SeSlmA*3aLS2+2;LkJ!`3@u-lR+%}sFc^pO;KxTk5Nr9Go=A@P%ZcI^{D|2RZUG)DQatDg)OVc?Pkl$+Jp2U8-rVfNk5U;(5gRKN%pOdqm##Mq z$XEKWi?bJJ7T4dIpN!ZBy5B2IY$;e8hCH=}3WVYTQq_rv>}PDpt}R^K4S14-5mU%fyFtPw=Lr(lwwpvs zC}}Gv{<6FG@Se&0&Us>cfD8uPyAiNjBxpd*hc6@K)TN!MG-8R2W#k983~d`}dDnAP z7&va|*lFSY45uf^;_JHitiDVn}%yJ{lgP#XZ7`JDZbV}UH0Q^{&A|~m#5E9ra%AK`J`(Q z*b37chRSwqKN5-K`7*+M2foeYFyJzYedf8LN<7m!oJE0vd$ySrjWUOC_oA`w2A5W6X|GpA$JOaGmMvr7t0yg z37j8y31Yu+<$Y?mqp#*~HIsYF<^J>A z=X~Dp5Se{qoh^Gld%N7R^K)=KJ;koh(%p`*(X0JD8XQO8!=<=Bd$y&mTF-}-N`Dk$ zIiD4QZxBX%;=Px6u4nHB_>a}fipD>-dHAni zq<+c;XRhyfp{ruY4+EPep@TdS=bj(>%t?gnA(rK?Bc)_Mcf81!F0RB(Av*LT#L#YR z`-zAcaxDoWY0lDsyLcgB!i!bv+o_j|NX0@1h=pA@5w`C}JV;d-r$KB}zDXh3#1Y?x zj;F!|A*z=s+jHX-;kk_PbCl4+I?wlm&;=c~6WLDUyB_x>rLqu#2AoM2DH{(Wzh%2= z7lL&)Rrmo z1T6NXkG4b_rxL+!7$SpjiJ|trEm_?Dxk!_0cA97DgiV5?1A_8Qo?9_LCA(6gFQf_-5QWbWR zFyyg#BtWbmdJIjbvCm`M6EcZxCga#~oh0BLd&L|&jA&&H^^1S4Y}BhLX5QC@5lRS+|22QlRm{XnH6 ziBlIjm^g?aElPqtu|)Qf_~6(OO7JWSLL_0qr6@@f8;{6TdO@HkMunauMeHf?CvX@~ zS>nb>(50yZ`BkwJNJZF@mvW@x6a)jUxN(wDmIVTlFdDfaG4z?wRFo@&`WSa0p z8ij$&Sja*JM#T;iqLAS<^g`}qu3b9@%`VbJj*9IB!kgh!;ADID@Frlp=p) z+i|2YS`kBrr7s}2PQWCxJP;r^fk-3Q62~dhPf$q*!pP%<3+ZrM#(^I(Hx5EcNh!zY zl!QdW(p3RyV-b`?NNBR7Xzgk0ha4Por5{6oK{_-%7KtyA&9!j@uR^?_%PCX=$r}v9 zK{nK50)II^dPEx7R~Rv#M0cRZS02(}Zs0kQD`UZ-+0b(|>0+`X!O^QiRwzgrjG}Zf zP@5~XDN>mrI|Pk~phGGm-{B&5qyiH;&<1IxM8%Lpo5g8}L{kdwfNB68d=W>!Oadhm z7qqA-avUB90>Y+J3>&~Cky3KEr^1?0cyTs_gn&Y%d|exI8mDQJaF!^Bj35+R0qauX zyN;a*FBFhqLK+t-;7~9HFbdNI#3cg6hYAyhije~bV4^(hC211I9uy|A z9ZZUf+z@$T1^srzP&x`+wj(fG*>|%5y$BpI zA2Nd6wgae2fw`XafZT!7OOzq+ZKFy_MxBS2Ub z2v{CpB9-qtN_Yjxpwjpc*O=z`PJ7Xx2!G8;~M)F$WkU zph-a(!H^^dGC*j+dZehi4_u9WA4Lr}c7X#n)X1eAb;9jH_|ijm6}M~&O=1vUAGiZN zbQmzpg=pEB6w1&hAs*q5;*pao<+?s{-GL9|4`hKLJD3i#UC>trK=M3=pF)}#Kk`6i zq6FqHW-{~z*bfi}j&R|I;0^Ntk0_bI0s-Pfz%UCqhd%p)5oIu(TtRsNkdjHEAT&@U zSe^*3a~c6T0r1M@I6y;>g`c08Db&@Vp<-H_o$ zSbLaoj8}NjPG~692BJcRoB-MB?N9>OlKjo-`Ps$s#R(lYFwM~gsG@)9I9D(THi-gY zC83#BqSV=I0m0*NfxtUY_jYEJy_e!i9lIivXAb7a@fUD22m)ZC4=440;CW z;qarNVgjC$gKCcqF@6Y^;)05D4x|v!0|(0UA(?^j7@!vn19Lqvxr84%ZFa}SRiZ}oqa0Z|W_m?xK;3>jgf;&oL;R7lJ0FuSv z83yhpA-p9>4ZKJ%3T2css64#6APqSOX7MLhn;zpY zum(XBt^*$fqBB$;VpTXjkRdOk+CMxm_!)=<5;*?Qbx0~KGE~FCFfe99^2kr2M-m1T zG68{uQA2DDX@YYBZyn|@6p3&kL#eIc=O~1X0d!bFa^V6|ga%`uB3uGGpm#uyj|f^p z=Ujj+Is;_E5JtcdxY;_k1VjYzCxLYkM=+i|i4fehVNc=1aq^_$zrq&`B%s;h4EmUm zj)?^J0KQEWBNpOhr{Qy;|HuJp1dxd15l#R|7+hS%0`jL2X~7YN^7sPq0nZe|0#@5F zxv*J)3wZY6Hb>wNUoW&_X(G&>2g0@X!Q#6C*$gr&j5gAN6)1Gu$N|8`rW8W}Jchu* z6P6t41`u?$dPM>O3<7`@9vfyHhy=R_j}_55QUhoZ?km`X1OQr!D0mOx&cL^|Sp$qbAD5n}=mJMer&d?}nk7=POZC<95MHVBvC zDW(MJkXE>}K?H>J0J#W6$s_>R(VPeG*u`{0PXG#-RPsmM!9Y-%3aOwvG5SEnD?^UV41xj#z(|+CTYz-qZJTl> z39uIOh=~?1vn8ArZ60ITPW0fXWD<5-_MWiI>Y8$hKc`d(0{#e6#{or9ODGQfALM!9 z#)g#t071Sl9LNL?&1OUmf$G z0?~Y!D5+xU(t0Z-lh82yVOVfv4dBm^_QJ)0j)4>wQJ9F3ObYnXNdCYrfrKCjg0MJ3 zT8slTU_%@?fFWb9gb`s5Mdcpu!*fIc4oQGr0$~!dE+s|amjG^%<{?M&6L@U{tc6pC zh|3RB*9A0E>I&8#yh=RyBmyXj{1BIL(O_2)BO~qrvctQKVurV1doggBeFtJm0BNW2 z?qO@-Bp@3E0F(5xqo94zBXUgvBM!M{}Pzj5CeD_$=-C_Y}E%L{XJK@2a=Qy3iwFP!00002 z|D{*kZW}icefL)k^3ceDDkEu86i(AZsO>aB8wa+NJX+N5tVDQsxgohylp_D$Gvr<< zJ4(|=Z|0ISXU?3t_~hh-Zr0jSW%9OUYOLAloE@o}=C5B;VJxZD%JNEiwZsKodtR}1 zvDztT;Hi%I^3p6st zKmz=)l|;|Ml9vZYSipO74QIO0SqN;57tSXeRknOe?UScJ$54r`al0*Xcf;jQE=eql zaB)H2Braz$M_MjqYgP@Jh9iBQt?gQ8YqFdU zQdin9I%5-4^M1^a9cbY)cgK=aVr#*Jc(agA#(7{-{O||Fl;BcJp?{O$TeR@(p z3Tvt_G13_tc==jJRt92UvdH)&TO;9hroJM`2jPj3CdV|C6^{r}+uoYpq-Xvt{_R3= zBMk6RIzw5>suqK7)0-k)P|X=Tr|fRD`^tEZH3zqrC4d_5O%3yHCS{{*T8ve^koGbL zn+M6&)U>uSF5;yNyke+yw+C#?@Q-YuLhfp$Z5#bTEs;@J6R5ZWd)Zp)Sc&v|Yc1El z+)e2(Xdfo5@_x#uBG z+H=*Dk93RWZfZDcYyDn|8`GusXp*?H+|gtW2*M-CIYy~!dm8L5;`w5{?s|u??7XTo zJX+w@{*^jn5DHQNqfW04t&wf*Vq_Q`h%(=Ax4+Rl%#&%_^Cf7@682#8v5U>U0XquC zD5FwD^nEX)zV_%3vlSUn$!Wl*I`laz+B!@i2Uex&F(~btNP&FI%{^2>`BjVAgr3ma zuG*HyuSd2dpOF-d#9#~_%QhEDyFtV^q9?!wgRw^hrIyCrIV$xXk1FmyCBrunaCEva zTKwSt!PzXTB+>L0S71rB`MyO9lAeZiNNN!Difd_fo%$YXB-CMuKajFkU58X?Z@G-$ zzScbPIq zCP-iot0=JM+H^6mN6#h;h&psSyL zr04YH**79->%4<9B|YQ&Tfl$C_vY2vKES8<0VbFVR=(}~`LLqG+1EIIb$N4p{pR)S z%d4C7v) zrwo%>iaOR-67`V#7*5kJOPxs%j1DH_)l>RzjG)`468Oog6kguPG?zX4z!p6Z>l!7? z?=T$2)E>04ONae^4`0=$EKe!jXp_ue4}0vvEp|k-GJ<8);2|)wLH{2xeXjbAAsy4Q zZH}N0I1an*m(!HEOZLiC2JycwX_Z*}eB?t`x6kzFLpb^kHieQPd3N;a=pUN}o`cp7 z000005@Ba&a%E>>bZ>HBW?^G=Z*qCi2_K6H0000000RG=R)25XHW2+kpW=!Eg9NDd zG7Q6@Hr;}@$@WhJB_Cn)+Mk-Kvn3*h!uKl3R)hk2jXH5Q_-2~;?bd?)J0;|R4IR0V}lus|RRl#@$|C6{UG4X3o&d90`ss;n`A zkGP9rBcz$Z?|MtP#3TRQqpVONC5`2;a8qgHD45-%NntBp6*BItxDW;X-{sG`oR7Q& ziXbQ-(-bZg(}v;{DOtg<4O-%`pzeS9nMO&HCali`)j2%(l@PjGaYU}#U}LdYxJ#Cv8rV|Uk#Rxcb|7>D70zwNNOjFo ziH$(3HXg2Rgpx!bQ4r+;G3&9S*(}cES?HqyPG40}fGhh%rIBrtRs%MIhYxkKyp(>) zi4RB}c|D-_b-%YTx3CMsI^)l@8F;brR}k7 zrJK6szMxU&jNbJ+vO8uc?X(jcr){=0VvSpEKBj4ppY5~#d;;|0lB_G!6phj5#Yy+@ z03yu8Oq3<{A<6P$vecvWaniI`^m#l$d4`3 zn=nlWMs8s{Z3k=*v{Tx_rdiaI+NXpak|TJRH$J>Qzr4JDdwG6)b)8+DzoxIhvUgXv z*~QiScW+W?e*Ov0;B*k^W;n~d)^>Qx1nhR;9r%{o#&SEgBJpQ5$=Bs>+H*eeXpY5ra~p-jXZ9MWbHD4Ba5DFuwI9_ZLuc> zUuZ(#W~*8+Xb!9!_f-k@3X?R$WAHFW5{{pEs;)mNdAK*mAm0y2FcPKUi3CNh`3U5m z3cBr2)^O8lHYFbj`|IbjagD)nemyTLy3oLiO`+H%T|hdNsITdc#@D^jRsM274~Gsp z?u@8C`MQQ|%lc-EP(oKcZqAumxY#ob(GT~had%VWdfQ8@w4M&7x3h1U1OacL+n2mQ z(o4}*?G8ONU(}lzA{QOj{y1T+O~3n_n%!kQg#Vf9@#N5?t}q$z{i^Btzvgc@lpaq0 z16xg2Bqj|20000JZ*X*JZ*F01Utwowa%E>>bZ>I54j+pM0000000RHzT5WIJHW2=v zU%{Y2U}1`5yKAw^O|f9@iUM0xr0x2l7!+Be9WAnGP*h%8_`mN+krYKyv79DLivFfqzNuil%q6fn9idZBSuiPSWvu(7#5Egm=*}9 z>6d7hVGj|p4CRC_qfAQ}?ntfCijg9m(`bn=2|W)2 zvSKO8qbxLpe%(RvQx+H%EC~WkDWTKmvuaxqe0`6zxsQ&;*Y$KquWBi(<%i%Y{JNg@ zS*%qY=|@V>8ccqE7*hgqu|T;Y}63 zQ#2=3$%{!|qy@uyd_5G;_ydMs9ld^i^6K@`hvSp*_^($dzyERkDM01ZpV1*2PdgTV zeEYJwa-!`%K%Z!|S^?@2B_$Xoq2w8I7F|HhP+S&_ED^bY+{wr#%F<=ZhUhJU)L~~4 z(~|y#C9tW0tU!!LDJ#%``zb!785du8J}QV-auH=EMoUnDW*C*@$uC1Kt&WOL;iGqN zMCK;RK~c^?Y|KO;DUK2!{lmp%={ashx)NA0TEb!}QWyzN0YFs_@*iPKAdK+!L*c9W za4mD9%u$XnL+QxCSmy;=O)b3r#85f%8&8F-`qA3f3+`*Z@^^stmZf&jH+}I8ol#L> z%612BBhd3hq=XHH0cc;LG!!emrja7ip*~9%l6gp@e1Qk9-1PJ=Z>m2w1VcJDzyDO3 zG`0N($iLBV?P^1zztI_8KVEMq_B)&qu{F$>rhApqhRqEyY$-h99kJLbOxR=#gUVu* zT({*ZafYA_*G2SIg*)_BorGvzN21|7ujuzx5pvGpB*mgM*F6WilhGOApWR!0AP{z@ zWhpjF^93R@tc_AXmJO$-0%z5OsCSLlHmtLzZVpn~^IF&o-xTqdt4`9aq%f89v%4`*IiV?kZ>hC+NAm4-UQK=CB9!Cz4w06QH;};#cIo29d5~F+kmi zZ2tp%H_GeARXAs?ygi_9Biul`?mfL82vPtqb&ikxB}{Siv7!ZRuY-6O%KQe-h|abM zn=U^34A+Hh5XCXPTH#&vSL+>>_qQGf9AZOrcYum?kvA`UT_*y4xN4jVl5Z%xpd!< zOh$JL$#~R>$t;S`cefAi?w!T9Wn1SJ&K;408j!y;a&Wr6Sqx|XZ<8{fbTd`w3Cjj? zVu9$?G#R(-?Nnr(z3t){*V(mYN<8|EuU0r_I04MktfX*4;-hWI|33q48(obiqtR$= zWDp#qtVB1FS1;-AH<@mR?@0}xS>MOfwA~G?_4Gb!uVwmEtC;$Ke z00d)iX>Ow}ABzY8000000{`tj`)}Jg(!cLt!S(}k_I|D#=hf6{ic6EU8+=Vtq#Ok+wW zS^6m&(3}+4lnm2pG6;((P2isyDQ+h;4~LZOr%{r(e-Tr{Ns;EkFbl`@Ce1!}yM1^~ z?tUTg-!!KrF9zLiUSv@++A$x}EK4)%SvtY>y4|~T8V_6Kl>K;b4a%cYLSd$QTEQXg z_W}80$3KF8qDi6FImDlS3zI=i?`tM;?7}z*N2836!XnKqRKs{ry{dzh-{u7!2jO6l z(LASlWixD}Nb(>`hN*Zx%p#f$9NT3NknlUT<3=>0c_bHm{#^V;4)G-5NX1j}_j9tx z(Ie@Q2#S)M>!^QCfMWpF0Vz@vB~cNDar6gZPT1M^kU=g0^&T0dG*A9kka1YREM%$y zoy6%a%@8K%AhudRP4a>q?w=l=1V<-tPlLUKgR{f)^WgaC{CMwT|F=Kaz6Z-)nRl{NP&*CE@@6{X1{%bc8E zRF_!Sme>=-m0N&y__F7~MzAh0R0RBz!BQxRB}SF?&M!{S4*mPua4ae|rLCbaJ=c29 z4Q^^{oumb!Nje=}Gcx7KIQ?`yiD^Mek%dVf_ECIv_(vz_mv7%5?H?VUTm*0S-tV34 zA4=F;8tnZrNdP=(ts+ZrNs=bd{-9aP7t#S99G+jCUG85Tot^}zZvp9(e;%Hl1{bIQ zcX%QJzt90AGlyeFq&{EdiV}paLuD`+E%uI2FHbHcq?Z;XMo;9^VV3?ulQvhQ8eou& zG>;0PKal(;Dy|1vcw=n{DXss`7>^_eb+aK)6w zX)>xnKiNAz1nQp#?@v$ODR8>zo!^eH(s*uw=f5AnIelLRxn5Ct`#391iri3n9M~KQ+(J<-@fao>=)~F@(v>%1Y;mrC1 zcr>`m?w~6|(EtLDAL*oEyCpdgAx4_y=``yjTZ1OakxX&g2mMSsm)I`3O4GP}D&#d_d&#@H5Ao`NzhjRyKvJrmbN(S->XYtu9{C)N zr(*(u^XMuT;=skbZ87C#XKfo!3jRc%-B?}QUR&SVdbzgxa(#PaYaRYyf3dN(`eI|f z^I~gzt<%}q+%%Xq8AhXN#(lryIxG-2XxErKhFA1D{1m0^xVe_maUn-sT!#^j2MQ)| zRZfc*AK3>p2WOMBqYmRZz2UxuQeP>Vgc-b`U|&Prtf{GT65ofepg<^vcvjdE*O-eN6VRev!hPm$_niIR zGK99O7+c_wO<}9Ii1>Ayfh8xOXqMxK#4rTgHo&|E-09~KY1<;_uu3>V1#ytZahTze zoxeLMXXI?rdc;nq<14NQ20*-V7?+OXh=N2H?B}dhahm0EVigTZPlDGmvJP8ubYm`so@Rs8eaxBoz+HB*@1_B_@L1~j;e{YL(S>o8> z8+iP{rKFQ|p*xU$rd}%%;)m%~9OYoP&2hqZfGyxrQVh#*4U8@g@C7=nLh*C7CkUwD z;E`*S45xa86>#7I2i~ybTC&130!@uo>;M~rWcVv90&B7jthu8=jMGmvC{lYw%QQH5 z6$3K_WkJckfTW!u%d~Lqgc_-j1`hrO4ovc_e~mui2$swerqq}@1h<9?70(u9ZWJyj z&j?f{C^q&;5Lohvvdy}Sz^Tlr%dr-OmB@QZ+mffn?Pn9jn6Y4P5o4M3hv3knEqF64 zW3bIvDI_dzf>E4;2?*fHD;V*u3tnmm3|{8h%znjK)Q z^rWF4;D1pb$la8=411Yo%b3Gbklik5qkv^Q8{VvjHc_Lb!HBQQm_w~9mu=&@wpj?} zqiYWoH)gW9v&Q@gE5T(Zoft%$x)DzU2HHTAi%@zsN-{TvGa7SGHN<_K*qptAp3YVb zcP+aFiY=pk8hxU5Y%=RK^%Zp9@$av4GCGJ_ZcnVNMg>_MV3OJNkxgz!LTe~id1&sF zXyd|lnCFj+6SOUINPo&E`O>uq^oXcq$q~n6q79AQ5&@Io)9o)tWVv7+nL{&GRE=_U zeqjdLL|+6xn3s56yAd>O5;BbFxXA%*EB?@P7JrZ)9!ZY{fj}b}j2QVo?K$ZM z@Iz1YkSA#9MR_HoENRU>U4ib40ugeThcX&5||tff-oCRG5^)56damOk-GCqj@x7Y$`Vw=@`z3y+dEl3 zpI$Yj7)rVUpP+7OYV+@HeWvpz6INy>d8Q8?SK$;az_iF`9%kMIp&ufbEoU5onbeQbM3M@*TCft(1`B;{ zJ!Z!ObdH@8o8ajxUI;Lv=L*yRIVxDxTSi%yYLSPV0AOlpCX>^wJz+@xF zKdf%zaF!U{Sp(Ak;L-y_|^iOR<1+e^0c7=Th(71f)5}QI4TQFrW#j&xR;b zdr%iWH+Y!50%q39OdMwFu>VY)p@Pi_$hc!$uia)a~(nDDPTO>*Oh@7kmSiFIn*eA$f|@ z^%O)wYjh~xau!c>(}gMut8~dofedZy!;?0e0qoaB@{d!kSuAz~uiAML2G;q8656=YXa-`dp^->vW zhaC)=3LC#PxdV{Cc}QMIWtLIJta(tSiHB&bJ`(A0Ds*})Yel8a!J>D@8zYzp0bXOS zZxvo-w`{|_&=iy!>j55CAb~J{s#2P&_o!?mxn!GEu|AmF&g2PbRJ=@vPCD{>$P6k@ zlOK_etT@!giecUJSHy%u~nbO|yY|fvU z0q?Va?ao5p`|~pUD0zrHN$3r?q$~q7f;y4WQjQdm)l_Wm01x-MjNesSCbGH;1sJXo zz&wC=eMS3lf+DdkTcOjO^<3h!Nb3j_EnzZlRgfE4Dru>7FfID+W?N_|=DV9c;^?v4 zx|APOwfv=K*4U)7IzMOY*sKmNcdOGl1!U4Jko`+#10A?h4R0!j)EGm%|Hc_KX#F2Z zCeT{36wwwDlxG`iDa2bAvMp}!D#5@xR`jqQQ?Aj2#D$Vqk=fGEuiBGnXZPz{Sdg17 z*{WK=AMAwZZxT^z&_&ovv@vd2*-1YC0rlr}@N% zo`3^s)LlKAfs|yIm?op*y0L;x)+DcXo!1pwGpJ(Ryl6VIf%a;gY0dSFOAoVFw610} zof%Eh##AM><$EykHl7GkH=Zf6H%AHqLKnysDqw80OzL!Rz&mNk7Whb8AH?5_GA6bSsYneXY(rTG!nx`)N0;{QIeCxEat}>uVwfGiScF#)k)s^b! zD##Hl@!j-R=zfR-JYYFj0bZkxop!BDCTrXwOKi27qK7*r_2&Qar9P(*;JK&ka{|P6 zyAlN^@IEYd{P{#j_RwOJQbU&Kv#Ogf<|c~^aV)s9t*&`1PN5Eph(8Rj9e3o^d6>{D z7v}un%T{5tJ@EwhgRS1=MzSYKo@b7x>Iy}GHL-u!d@)G5w!sj@EzNFsJk#ZE&hR5WhT#5I-JF~` za$hS?IIewrsi6QE-z2DEPuB!5XV`;%yRmYkLzotfao#*8aS3^64bR#g`}e0aQ}w(t zccpe$eGMis3y>tW$!Kjg(?UpB(@aK*G~2WoZa1!;;#|T=`m|X;@c6(U;m&rM1TO8% zA_omRR(THJ`>CS}QCg>^2~kVQ2c(;4vAOp=7dwQKe^i94aZi=YW+7gCsFf&$wK}-> z$CFCV^ThfntBKaTy^FPBFMcXMp5&OfNM~CVcQi-JMPzM5j<7ioMB0kmLF05p81S`lR5sU}o}6v%)>>39vM#TXjSbxXQAYv#H( zbUT`2S5bHC42OxWxB$BAv>EY(EO5KI!*=scg?A8OyKb>v!XH*CC>fY*>oTEHv zETYt3t&4ptqS~)PXf{QU^Qx=l$8Httqj4}VZFgKXJAOmgo*=8>YmO=EmZ?1n_2;@P zlCM#Toz>MD`mmHKuL4xLvFd5GDx%jSn@+;XPYh@FgoV^+PTC1k9qP>&%Emge)OuYk>CQK77I<{@wwr$(ov5g&L z$F^)c>YA?tJpDf){z z$wn6{)m$f`Uey@q#Ppjm{5?qE1cj9~r;_lzJ^J-4V{W~x8N93cjI}Mgs95B~%BmMq zmbQ^b7S_Eds_&p~Z>!Na|D6KVL6WhG<7wapesUG_@V0KI0QU8lZTGTdcpm=mtYZL= zF9P*QvRm#F-k;t_u29(FoQvIHONJxc?s6VoIX&|;G(1)JD?elEW&HXKmXoxlI&zmu zTD_JFa2M7M{y63@rVx@g7p5J9K?W7CWTGRZQ zZ3KE#d%H);$b-Z1TR?XxZmF5$5XHMb8tDCXbl^T=6Am-5XQ? zATPHjUQKq>lCAvcOX6d-b*wx3V%k0$uE+bISv5_81>K9A$pL9-}m=h^01zUBK zrJf(Yzb9sM>$OJ|ZHYbt?i%zJg4!F?3y7x8+7)NcLPH*Q=#=c$Y zD&Hq>i<3^A^FpV>pWlm$*B=~hvv>5hjD3F%Fn)E`MesB$zCpd%?Oq z*^V17;&|I)_uU91n3lJHB?p+x_Q~ZY5TfXW%<@FlI|3%}&~%bG333w+J)hDO#d|K! zzsTbiDBc(3sJ{w(6BTOxQW`7M@;*-^+oK_-<1jCPbEbvj+!cp(1Y?2rA&=f}+$#NrL!NSP}b>CIWHl#;*;5oPwYy`gDEE-v{?zF%BQqyD^5uA&QxH> ztvWQZC1B~*+LGB!ZHViF`w+w|=eG$04J)vec@RKK$Y+zhJmRIPi#ba@K|byw9JGpC ze?@}Czyx(F(&;OVm3VdM-LN;cOo21*Be6A2D1Zuc#;WJF7hV8%G03~wBCbEZJA;*;J@6-oJ+>nhs`~m zKkr_*-##9y9-0(^sSWYtNgD+n#QaVfAR+?N)L3vF#)2B~gpwOWtc5}_#7H~T`bnY^ ztSM_p4qtk6@bkWrb91`|Kb~ICFPm51OsgJ3PAvHXSZjwx!TxBy>sqO)bUJA&m+XDs zlXSIp!T7g-eihC?72ab9ltr^~OU&Z59bVV2hiz?pP^b}qf@v9F;jx1HTdG58fE!dJ z;`TdG{<{B-Te;|NsG}E5NP0`{?bJ~5LRT!^`{!iO3Z*33;PVVf-J3F10*-uAM{S+~Bo< zHCH~odaun0AT*MDS5N^N)mwQz66|;yW+ePR8=OC({=Y_aP?3VptYc^=#3zaprt6ut zQ`?(Njx6xJOr*{5;*~ikx1|t+jV>?OX5IFm0z(bspm>1AQk+{X+vs>Q+w3*L*oAL0#N(_3i=?Qcx zi{eKQw^0QR-dK0!#xAh~s0N&i$C|VOv_cUX&=~>SP}IHT9}MS5PaySFqIYb88*44>0yc6LLDPOons=EIcMkWLpHAsPd@ONH~Hok^&N>dLwt#PMiJuA#d~Ww4nkv(>2}i@?Bb$CFeV>vM5}kcn~T z8@H8?C}J6fM2dPW1~6Yh4LgaJzA~w&YLl#YxoQwP1p6s~q!hC#g0~yXm#Q1V#!^or zB^0O~0*SOixMz!X89VIn_AH@WYQ<0U`&F8LPWJYeFM@*sb%tGj-;0xJA9c@$?omTY z8~vzh&%ettMDAi!31seVbG3N&(^llRbK0}Y33V=>MLf25AZHk~fvb7#!Jh8OG;Fmr zOAt%J1dibhR%j{KIe;=T+D$I3vdT+uLI&DugT&-l?YhF9S(HNPs+;>y79|q1eu3!- zwKGeH=5GAw3Nq+ZR^PSl#&7DJVOCF7j1mwG=7n=w#>YtZARp!#MUc$NZCflxb zDzM&ySExeTEGXaEA)gbl-SwHBLq3CX^hlYV{7nS$UtG)0PFRcqefUoBm%|eIwRblFw^7gVz|%6WW&sK|SqLd! zrOQo8Y#JS(1Z(bcA`O*0*zc&zjnLA7kW$1t;EI)IAg473Pj1xR_bdEvh)7U2blew# zDT6I%u70EW7O_*k!FKS@vg=D4UR|H6k;bRs(ENVL}BlLLdNp>^r-BZk?vyH#vX$9dvd3Ur_-^vr8EbJoeqr5 z=7-^Z2VnKr4p#3Uhq2XDr=p+jys<>$T;v~|9+Wa^k}G}7w&J#*XOdk6*vP zuCF~-OR6`(ky=<(7IhlPebde>sXjpu^;%+x1wEj0H9+S}X<(FOD#sOkx0;{bljw1- z17+vYhL?8Y?mEQtcpH*dWrS4iD)odwbxy}M$ia15B0*!2a^Anc1v1>nHBi{BiisWR zo7Y{O8F1~!Y`uMbpWj>+6lYH!@plwp7J7VzW2FSv9Z9=T!7)$4(bR^dJ`ad`fHVBE z1y5gnpT7so-Y$LJgP3QiE5oQDJuLIi#nv1$FtS6!T)?NG*5k?3Wwp<`DdR5iYB!t% zl?#~9!H3i98E}-eGG{;{ST2ck#CR1u=F&71 ze%B=>c&wi72n5Vit(@Yh9b~A*&(IA*NakZ3%R-K|)W+#7k$GXv^F;1vXAF%s*PR5^ zmIdJ``}zpx09EG2j#?Olvz-ZQ=yV5$I&!Yz-jDM3Ccn&)NfcxY=u$5-~s6r z$)woED)4BX0JDiKb`pztL>{R9AZ#fy@FRr4`QC|bxpqr^$q3L?O}BTa9Xv2Xu#t@N z0aJhc@}0il;`k%G%zt#Y`mtQk!R9=JjY-BdqZ3x=hkQ`^TsWTD$0$x&o1(XncwyfV z(m-P#;TeRqPHv@ncI55x$ykP$*4ZtvW|U3c{>r$|#ft~v*wX1LwpAD9)-^Yyu0Qsa zy@FVO#w5ov23z@t@F+vj>Lo0#QCN<%X!5rL>Y77FITE}QfZuMZAR8gs~d-LBBi z4^pO0*o%z`yJF1J@wI$!up7RhRCid$b$rB!$*POv<<_^C*;ULm065#t&H$t*zYq5P z;89K=f1cqCCT0}m5+^8m`O_0H*)7ewXl0N;F;gU2lg2LIL>T<2J$&kCI=pzeW-%v( zer{Kc2txH`m`6Mx*rWyiP;8m|*%gz?=Tc7RNAijb0CvSz_|!101nQgi=xW<4*tkRh za*4>!;ptH4(frS2h8xV-J;=(+3V*J}Ytl=`4x-O{`fnVT4*)`^c4o7tylOb|OA_qu z``~%??imc&ET`m64!>n6jEsO09~C5;$07^|fdp6yw+E zP`zme;OIP%5Bak9JO$z1X}z(Ys-pcfByW&5^9j)M<(bO=i#g2GCz51qNvFzllF6rQ z>@6yc>F*~X!RK?+rLzC}!LY?c@E!j3!>fQ8^1oV!cD1rI|5wR&julYC*nGwrV?JZRsD2;;~WIN(Gz|tY(x%#W2U_iM6fO z>ej4AH=LuE#Z>(;2;j}9U9|{$G{BW3O(h$Ome{Tn{VP3yl#*EK53pxIAEo1#R~D`= zq)P?1JP^(=7@@A2Qje}rC26?S8}5-UiJ3439c1xTFg9fbTaioJ`+&*m{)8hw68!SK zA>ilCfj8I4U&zMMO)ORE2bo4-4i?prKvLvsP{G2A3v04s%T~xGuppFJlzzN0;>w%R zBlirrF>IeaG*(c`sl3ZX7P_~hs3|Ci8l!KY&=Qd%4X&3wO!3Z^1I#~u9ojMx%I~b8 zzrznFGtiF7Ngt9rhlSN7f-5rJCB;xE274447QR`2z8VW)^$L=G_S?8mElH%g9H2Ek zs_2+Aou!ImY__mAP|lpTBBjSunt9*071ibuiI;v zj-LP_CEX;yo~%|UpA92yg|;%VFI?doG})E&!~FFsIdiq(jg!>ewFjc9#VilNaCe4j zC#Fu+)8^#v)y9NspO`P(oX_r@P;QsO+ZT>0Of3KP*5fB~)&iyl>-S#kkVKKrNX%6ag@|}tDn)8ds2&(`a}t?2eyU$mj9X4eSwq{WgwQ@I z7OaivpSf6$8f=C|(IE*f>t_H~yBRLg=;Qdo4_Bp*!u)qa^?;a59~&R>jWs`?bI58{ zrQ|WlIVPPEyL#J?AW5IyVII%_F8gQAikL#3M3Cq-xh@(P;=^*x zUTBBk>%$=UL{_MWV2~PquJaMwu_i--)6uRZ-`z_8?`LegRnfN~#!l#)eZcRlVng<0 z`Hb)0{3y5h=b?Z0^$v+AJXeSS1A|5#fVGmWgkt$nY(;Ty@DGNR^ve8JzlWnr)5xvw z3#L39Nbbo0r}~F*cW||`w+N&Ce{aT>{%@Dfk@+9Pyn{>&f>2j2ZRg7&M01~k3AySD zaxb!VMb;cR2(mabYb2w)!nBU}>+Nly{_TCB#8iCVN|vudkZb!F_ZN5n)=ncr0nhqj zg8B!qWY$+hW`OMoYHldAPa1|Yi)oar$_U<02smyyG_I4ziSNKbHvM3=(OGin2w!Vk~CHsbo`Pkd3)C=1AsaUG{Fjc)Ha(! zJaq(a6gdeqsE?Ts)b=M201H5*mJd zfxMa*A#I;3vWVn>J01XyYr$769#0l4_B@oMW3POzzy0F!ZRo^4R10;4qY zLDn5YuRzgfKEvZ{(K6^>_bf#f3JkkNlI{FF$lZKq3yxWs;+8q~!yhf?2-q%%2rhj(=?M19_&1Ivk-I?Oj+ z=s$0{F!TbC`;KAem=QmNx+Dt%J6+fnzfz*b4+*hut~Qn8HzZ8V0kASSYB3(F2T{2K zVm&T|H7R{D@?<`z!;4#Fq5jh_N+i#nV8L(BfAG0n5~p`SlNjTd2bTny$%4ml2Srv( zaLr_q%mpT$h*8d_hp5I8!6LwLU=tRnyDk*_;DkXB%a`V2K0l%y@9>vr953El90BWC z(^n5;3j;rA*FUvyD&*(^tfC+J6>EQwExDT-#+!mV|8T-(0cw*&Jtkz{Gs-K%0xaZ! zoSLH?j-s_j+$9*zidOJ4Koo291N`GqbCL?xU0)1Vp$Fk@KoyRUZXp^98Wm*}nj7>P zV8vIkFJ&@N>s3o9Prr-G+b*$NiA^G|AHYBR&+s0bae@vRBAPOmGIXI_Lk5y?uXGCE z{h}aWg~skxa;qNAV4LTZYrl#wL_Z56h4GCmQvy%FX+ML zTvx@7xTD@E6WH#t&y3Czny>}y6S6a~p6%>K@~~@{alyz{|JK)k(r>(pZub%T^@bO>1QIrs~-uJEN zE+lYmJMpLI4QCv z21EQ8kMw?DL2F1kTWV=FeJjsiGeLDOrtRy=D`iE0bVgR2={RTt4jbM z!Qfk`==L`&cqodIc8?YaN_n%{D_So~>`wO2dAW$PuU^@!U2Or`Y@u4)4pZ0~4{+sp z2uW)ARA-WlQq|7p?W_6U21;~n=u;>bE!ns>U|Bp8=c_zLgH(>eIxn^?)KB4vFl}GE4i*0XgM-`G?Y*b{$-UECEDv6|8N)yb!fVfq?EJZA0Gn_l8Klag4pmDl}0hV~;5(|`fAc(IdpO8StT<9PqWLB^jf`|x) zx{?Ym51M{8pnOzDpFNTFm{^Elj!r@Bc$h{c*&*VVDCm+tw`r?JpbY3X;6iDi*yBeu1g%NsXNgERPrS; z(4`iD#$0V`L4B~B*od#HK%x0RP?)t7{r06q;Dp(3PWkal%#D-8q3GEa_WO5^I++-_ zuTc2Bu_S+jat!8JUFAaK(7T7~A~+2tf~f^M@7c4LBEc7z4k!Zo`e$(Ua%wfZ zr#)|-Tw`oLtsIZ9Ks&cuWvC(dNg8BDHshaw;fc(7s*YP6rB^zi9xtX)d3%LH`J;z_ zlZ!=F0mzO$N!tEV&kd&%g)wgjvp-^uu!(%+hctv9#clHyPNM3t4D-gAU)hd*MbkGS zw;VK5LJ}sQ&8#g(6iR95aJ@_ie*25G$C>@<)Uns1

VJ>FagD!5U~Lgko$wq#6s7 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

c2T%bt4OVGCkz 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̑&YjUZFaSy{>tJ}moyeTNC_ZrM>yWPL&H42jV} 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

13(JYBS_rX^hrM)6eA;TCdC)?G7lX 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

VJ>FagD!5U~Lgko$wq#6s7%}AR@ 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

c2T%bt4OVGCkz`@(O| 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̑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 T?-#hMu6RLPYX;uT1nYkR9_9ju diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000007_state b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000007_state deleted file mode 100644 index 3acade7d727cf39baf1d13729c696c19f0e4b6e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197528 zcmafaV|1uX)@E$mc5-6dwr$(aiEZ1q?c~I^ZQGiAyXTwRGi%N2_fOR-?82_Mo?ROV z0RQC!WCZ+gEx=t(9GxueY{6CkodN(rfc%#}|5FXf^k1n%7?~Sb*y>prgA4tgkuU}T z0Q_&A005c)TN6MCQxgMc7e^B(2+{wY0RR931pA+v{r^%xw*N{Q#K_Lr1YX|2$jZRX zMA5{|!pYgu0|20G1PT~R#?IA5$=TT2!tno);b*01V`mXy6XD=sVGs+qi3cU6=G&!5M^Z(78Vp@6cl6@VBrvA6%`g15Ef!$6V}I%g!tEVfqnob z09t;u?EgKq4wS8djR^q{0ntCB5<&fMX8m2vK+8f)PXq;}WngUl*G4#*IO#w+IUE07 zMeoi)1T}}r{weVnWoTeSBc~MLe};)xoU+ZLN9cJ#Ay%&k%Byh1Y3b8BQaN z0>Lwmm1v#o@frbBRVWa?gB;H7O9s(G4>5F*E%R2g_gTp38*f>*fRHYvv;t2eWKBJxWMjG~ z-^%K(=_1wnK{MzSTP)a+9L{4J4>vT6p-j|X8!;ssS7U+nS`MHC`G!pD`NF*!Sm8ENq>b7&{UF;Zw7Qj?+d9lFv&mzb>b$XgsNwgCf^-RISm@oV1aN z^~rV%d@>$_A z02^lPLO{_`!jS`igv;(uk))Z9y&dJUF_~nUW;KR;_4xVA1_0h$mTCYf!iN1N_}+7*pcS) z2P9MU`y;ASjbXvAek6rM6ZyD+6~>guOy&aV13t-;M>xxNly}99T8Zg~EXWiz<@Gu8 zfW$b^y9Bc4EU}EFu(O03iE(H~vml?)weU`loVwp88YvGW>`7ei)0UJ&0VRhclI)NN z;Ox@wK}YT+d0CSj1&n5hEn|%Y%v8H}^>u<3TbxB7g)hlj0qD$Uu%oCm5`C%c)#~DI zg1Q6Dm4zuNq&Rq`@W;+*%5g!E`BON!y~d?5iod|;Ak3!=7zv%H5&abTEtS0c4OHGo zZnSlEii*QK_?A#$3EW-k)$&)ZmnTU9dr7_1c%QgA~_W(@3l{M#T?nbn0Xv# zGNFv|s}MkCRBDjxcTJEa(I?~g5cL|C0tD>+jHWCCltexI&C@I0M;`3fg-OG`d4D{f zpWI=d3I|^fqoTHBM@DXj5B53>-3IY@dT?=Lw{xyJET(qN$5X|5)9jvVb~kfrPGU|c zE!7BzA3)R$^^@GAz6_z)oSH%n!e`+#G2>dgY@WZ!FWE?2v8=Y zN}vMqgjuWJ7ZyOiF}m^j6*B0u>tb@s25baCsgSNp-jmZm?vvGiUuK6)$ahDuzJSL^ z$)MR0(WX##spSwr+R%mp6<3$C8vj{)$`;7Nt+(7|2BIPfEV9@UKrt&}hz>QggE7IZ zEcI#~Y&`n{W-=GY_98WsE>MLrD00EN{;dRKe)|!uD zF0D)_mnl$Iv@{nN(<()<7VB7(CLb|zzzu~uGBKF-1-mc~sP zd!uTjBnUK^^-N8i4-|O-ol19P{YxlNc?Mv?)=Xhu)&3<4bC5QZdSS;>Ws{>w`(dvU zhUBkW38oV-PM;{v4bb6+rY|lOwdAg7oYD&L87q!@+FB46wr7zpe&782Jb~Y$_pZka z5J1Vzk{Z}-qGfw=YGs&Tt||{4M^w`auIDuoD(h`@RHOE&duQiF8@JXGvX zTN2BF!lg*>D2C=jnQKLZ@c7(F@fQ~axo=wV;)6%B>z`&g$`_B~Wl$xx{o_en-5X<% z`D}1#oXtR*1!l>Sz*Ne0#8~7t1UxGF2V-{zV^GEj6HaYr{Yo3n{#Vv5SJN(7MqEtU zxw9^`b|f5npOGjlkdd^%A7*e6~qarDmrxT^HE!?wPY2jH~BM8e8---l?_z<@38Hc{o_wM>@d%XAL7SqarjR=Vi* zoPRJlNnIQp?mE9{YUN+n?;lmGdP_pZQIr(m)viLSUTQr-)f9mM z*sfPxy3A`ckLP5SOO$EzM<{i)=2zP+r=HHiQ*5R;)3|>o(w=v_>MK{PwjKIUAWLV= zZ->rUXIE!B1&6*`l;k>{3O0C4glZQcHR4=sTEIqs@qNy+9wa9(P4}XUuxgdbY;Vz zC4CpP>{OaeZcq!LP?&~R*}7ed4D5Njpr#U8L3R9;FY=Nv?0##M!YJ(QkNs60=%F9% zL?9mZizx%H&O62b$tzXGqxmg%bNx-4c$n)gPT^Mw0szGC*glB^cn_wae*Xy|ZU z`*5>m`-a2K=smQp)XaqxqHdo=5Dx&aWos~d!i}XmV+hjASRypav}UQDC9v?7k_;|_ z$|2|F8%$PX6vGVXZ(#<++`!3v67U}xCR%pN5{Lnz=SIo9wFMnGC8gmPsGTnNnNGtP z4dfqT#za)aovo@10r&u5rpcX`!lVJJH6#e|ljy+WZ85{DRA~08niik&(-=XFQSotZ zt0YAu!vhGPa8d6F7<|HMCRgO#>7cS9c$Apr<)Dtb(}cFO#EM(~QPPN=xn#MAL5p0g zDLZX!sG<3)c7?{(7Tm49#k^sXK%cV!wnE~%g@>!pi;peKxvQx+y|=fYHvHeYg?xGf zxtY)ys4Eft{}S`TTuqFe?Hp&|{#n+LhK=0@E5dh32jK8Zz{cm%o zsw>t!3KGo%n-<3Mu%xW2Ti+dA35g^VF2~jyu!Pb5_%5&Oc67#*laoVCN{i_;8WXdK zpD83HseDRw8gfqwmhf@{sdTcS2(N-0CRUa^KOs+3zCWvLrT^uzWfXxl|YVG|l#R_Ste6la6x zEL83#GlCr&I;Iq*kFp4N3stZ&aX>uE1;rpVzfl@E%RDN{8hwG30d2eTpeexPAdLF< z$&Cz(MI}oNW-n!mK3%Jn5-=*J+sEidYsqh8eSN{8#a%#vTRc;W=+}NE(c9+7`j*aV zb!kM!gBVg!f8}v$Scz#{=$yjXJJ2#iaU$kjYKfFV#pn?O;J6hLxOXTN6G`Ihvba|P zkJy}@@ue-dbG%<^TJ9(hC7eR6G!3?fs z(D4*ZpzbgLUG7iE??ZZfQy*@7QxmsuQy*+AcBMtHZ{&_5wAWPu7 z!8oSJ!E@(E-CuMZg;<}I5Lzbh+gCh{2QgzB3{oCZXd3C{bTq)ZV1ea{wR%sRPj1jO zSecCV05H{3_^aFFtRJaLjvd8)eSNb^(Pj+y{pvGnm4ZgffeR=6M2JlLn}~N1D8{aV zEd?Z0g$j+EFsA@8xd%xxAJ3Sei3IsL%$DM94A4=u@Y2weA^D6Yz zi;P)wuTENGeUZ+OL1`{}U?U3t2|2G$cQUu|1|N9OEWi8q4!rNMi*TZt%tpCWsdq5p z)s?X#r2(#8k|y8ZEa-U@pRB}xw|CtHb}b<@Jqzd{cN(@OD~!vEo=p_CT4hj1mS#h$ zh;Y?va>D5tJ_G?t-)yS|%AQ_OI|PS{$>gA=QDE0hfk zzG{h{gg2_Mp%0cn319I{Qr4y<6FN!cY@P{)&}9u<0DQrqePO)`QI(3rXunH$`zLen z!r~J~8r}~)=W`al*IJ@=>Av?%eAs3#C1E?2NzcAma?xG}2S6%Q{s7w59>fN%%(NW*d5b%1&68?#&Ve!^V+o;J9gcxzuU%2mGV>~^{f?86?Iz#@_6meraprj;(UWxm85nB#3bz=#5 zioj~UPbWhlb`kcYn{o&-R48xo)HTRxpdJ=!n1k)Of%AbmrsBu|c{}Dr=E1zHeky$Y zl$gCfEo7R8{RK>PfskaaCUFueKMVh;2%wvpIry9F ztz!5TBb0^RnP@HlRz+89FTZsMNY?}kir)E?JpilqO zO#V^>Zp2krQ}ldwFlZg`+yhFR@fD1ntQYcC7Y!`N70xtkuYbE?_UkW^8}v*`2%P{@r!0aHlWcxC% z4I_G8@TllF8>aW;`azAhxOI|c$6I73l zKDRavm5wvDeRf`ACXVPOU_|<;aM|rs)`{-=<_J@mi<2f-j#+4XaM1nhl0nSDcpIJC zB%eeKLvIbbu95l85klx3D*|hIFgtb8_2s(0;?~J0-ztGu8@kNxX7ZjEOr|$IsyBHK z+|tw-pD&=dH6Qlh;sU6NqobYUJ$w*!We^8 zF%aMvY#D-gd>P5T2n^$dGFT6k{f7hYn-xMjNOFcaX`2noq2|Cnrf|lvd*Culko!p0 zkpOZynqfI(BS1XJtCm%I`VJa`q`|%PIav0d(^hs?P>i@eSiNV`Ize*3NmIP)L!^_w zJ1=ayxGEBy2f2(1*wsG!8jh)Ag8|eF6A-VVBL-s1{CyDN%ABRnJwKUJlLDFO!?wd; zIu*y_h7+UbaMn@Q$5#bZ@98~hsOkhGDHn8}Q4qU+RsLI zp}gzWZ++i^>HH}?5FAka*v_6mc54xb*)Sr-nq3BU*_0^(kPe{_u#Qn zTYx>k_Q>sKd$Zkcy6ac6pNDa$Z?w#Lnpa1xR)D^zh)h#&e~^%&moq2~$njLrz&q^G z1Eu(sZFR@B*z#r~>yl9F{D7zlqCxjj>Z4Hc%w*ZBD~6f-Pb`49DOZY*dpw}dhH3uV#+D8=$HFd( zv6xgWQNra`j~!p1OoU{z+vMbVbvWfI7UMtwLPHFUsi^o>@U)D_yTnf6SSvGowtBGx z4+mh~8e!B`pmZi?Ae>NcC170X9Vi3L?u^TB*Bo=3k?c|KXy?uLF^es06*W>2q+nBTbZClx!nEC*fQ$fZo z0bTCRuAQ*}==T+XksQX<@D?7?j#uMY3X>Y21#I$#6wO+N96Da|s2ZH(3bw0Ppg;7f zik}abxx|uT5L}Gl{Jv%`4KDgFN*_-xqN|p`k5_v7( z?KW&yK#xmu_yhOL)Uve`789GLLa*Cqsw}X;ymz={FumOQN9d7-DL19;!$>^KKG!x# z*>wLDY?dxgzuu6-=9j;(a%haovtma0lBm8JG6no2Sas@3myiJNRt#rdTf4^X2O7%!Vhi4&NT*N8@;6|q z-Si9{?aYi;s#$7vrR&Gz{^NeerTZBs@VM|oCnm3vX&qGbr(#T4s~vR-Bgiu8Z?{6&F;dXlOi_qWHA47?bL$NzZHzMnivV3%A zCk8Fa7WU3}jsXSCG3*O)I5ETRP$%42FZ}Jmj_A@BsB(XP43J{GbokX{uJgPgRfxMW z^g8R#@0j6@k*R1Gx0`{DZNW$J!m247pv$w= zA)3+Jw+-TeUmh-3vwb$sU&yvw-t~r-EbhGIb(!DwAj&RJyoQuo&VUX^`g<8wLYgn( z|Hd9*10(BgkbjUTSxF{#fgYjzL~ZigK%%G&6w*N)pj^{|Ur-0~Y1_rRl!3(cFjlbn z+}F&Se9L;Bb~lrU$!?pwSA#aSt{@2;SXrV{1-hCZO;KD~jstG?MwI9&*GLZ$)V|-w}->Ufuy_<L8T(~9`8fVkJeiZvxB;h zAF!e&@dJdQ;WwE@it$#6H4FiV-c7zLw#wQIpv};iTgdGcGkNKJVZ}AX3#!F;gU0Os zZtOD5O5A3^8bYwzGCeryO5Tz4Z~DpeSoAJG_REp{sS^Yq-q^S7LQ``T$gYd4eqb@G z?n+Gog$2R{Q<2m6sMyw0bQyW!T#xf)(^ZRQyP;KQYdd7@BMz%b%I#UuO&40}tk4R> zswpyU2v3cT%tupW#@}jK5s0RUUv4LnjW`2t=)rMb^H43Mo+q5nmYlzhQj*DU5gXk~ z)>MPOvjV{;@3q%uqq$h5wXBwOveiOAeev3l+HP5FnI;?nF@Cc1Wkf6X3@Xnj4pPlJ zVA<7F(%J757=fQO8b>R-3^|w?ZEba8J-ZqVkmF^4uuM7#6abSIuY}8-q$Z!zm~fN@ zi2mf9zPWfsYtXEJXYYb;$VP&3eeIaxBpTg=!<-{_p#94j31w*EtoLvIK{M1phiyy! zfAj|qXMVEhn|~-HeCiGWFf(kxT42mOs?`<58-zE6O~#PN*e&*cdJZBfA`-fagz61| z64>|ewqG}$%xuS{4?!?y$Pz)1=6*?dX!Ms7_leOr7VOBzE^aI=D7iP1)m4vcWyHgn z$H#N!!ASP!YY!BlhzuuLYobLBE`7c)Ep0%C+35|y47*M{FkyEWIu)J;B@kwyxx6g0qa@@%8wk>PY`_P=U^t(Ym%v;Oaoc7QgS> zG(d?K5amM>o100s+wdHw)5VgCQX@LL#XaghtG0S%`PKSDJX+z%CFtKkM3#9!cSk8h z{GoX4BO#F^!|!F^Lox9w2z}XHT&;e{99&G1N6^_fhb)Wp5nUX_pm=9Q42e#Ux1<4G zDx`SAy2`tkWe+2ZlBLXeas`SFLVHF*hTO1r|Is&Br=tJ1i>~Bk1W>3wnGs>ek*bhK@!jQDz_OhK?EhN|LrNpaA~>VM;$mL==O8C25N`vXQ5%MDkG(!{#iqz90rElC_60N} zHi>IsnZ(0LtiGx8U@(5FYTVCH%{sbB0)@{X`Xcdmr$1Elco^j<@W5xU4{?k0?>l|- zQyH`#;0zr7sgYh`iP4uFcaV9#PkfGXS}G**)nT>B!EBW?RBsteUFLq8@K($-1_Hap z<&roR_C%LRX^)f%yf_1S&n3Y8(qeh3#*7(Gj8|P-$0UF3`vKPoqh!CTn|O|xAE1{W z0{J-8Y)Hs6NA@#3AA*9K=MD%qvIFdcTSB9rv+Bk8FwUBMaFeI=n8@Axzhrc;B;-r{ zF`FSoLXZP~pmMMq4aAb~7mbRSV=PuY?LMp5O3@wy>n(pYK6QNYk4pFq*O0tq+RB(G z4%~lC>g_03k=;T{p$;vZC+{@`-)_gi9&1 zUqgXS5TpyBNa}AoT&xkhiHN4_h6WN|AzSU z;bT(MSaSW)ppMuTqJJnS5TSerxS-dzwy@W25hQWdC;9)^RYF^RP%2+~9IyRdo67}_`_a4J> zl@45ke(f4a2w6JB>i4Bn!sn?U-D0ZBv*Y_s(vZks*TsImt zyRlx)Ms(%-l6?g_DIIw`>YLGtA-ii0}+0 zFIn0J&UARNsEMry^D-V1rkR_wYfSVwS|0n_4fH1n!4Hp9qzhQOi1ryU2CiDBD{YlX zioeDQ+JXm)@VtdTa^DAwD!bl(I?hzp>kXKy!@BYp4^YP%nFj$LI9yTFB)~8~I4G-Y zh9?HmAdB}IwtJd!)qKsSE&9%!x?(HQCqp-LvuRt7cK^ab)WuzEoC>WD)f1SmN*HZ- zU?ddG1@dg%wg7)a_!-)o5&o!sd_zcUR-gKlstJ!0PvD(jQN2`dF#F0%I)z(Gk|^-u z#_iY@v*RPZ)@jt-2KM=_o(qdVSJ@;Ytpl9LvZsbOU>OaHF8;q95B`q}P4s^#w!78- z4NTj54*m3l)0;&S?+G0e^)wF4c~+W5{So=2%9;f(vbN=+>C zi}5ip_nS^La~m5Q_JT&k+!s1XMdZ-DaeeQ2S#-c)K zjR>nGQfEPqA}Gk(0b$up;mhzX3}KQKU5H9hiXxQQPgB4b!@Pvt6+F>SjQLFQdUiZ% zVP)b|WBq*}U!MTCfsn~F7CDihA`j`tMzS05rOe9f^V~y!;Cc^6hS-}&bBG#9Hzxs( z5-}3!Eq37<5k;^BD9BJurAZOO>uwPSX$;)=a|Nla0SfXR4p1@!XD@EnM^YYs539r= zgdCG#AuwmN3?4o11X!p`q#PMaDlQ1E03k4eAf?}8XPgXS=*=LFXZxCdG4bl&($v?c zX*7by_q;Q}mD{cpbbILR6l%%S&ta*A< z7$08D%UKBL4e~+YE-Eg*o(>~gdKCmmV1UbHma?CAN}v_|OBLyD#8khpt#8&GGfO$e zzz`aozu2Cbt;61U6Wf9|_h%fQ?&k+IM(T%Q56A>()f z%-EeuYRp@B;KDWgM9_jhj0$i*iR{-gA@01TzO?!V1y@RhqzaVzFLLy}Ova)gavfy^ z1rk|d4|e#0ppX^7Bjcp`Q)IH72n{>Pl#DyfeMUn{B`4{?(FB6Brg3)1w}8w#90NBoMspH2aP#0_czXW89};5mk_$TFz|H1O3}^KMeQGX z-Q66XPv(wh4ca>W@+F8V9$>;?tPej5T8V6-d+~k2Ijpo-D|pY3@?ZEOrJ*06#;Vw9 zlTrkH@d*@yShQZH!a_^Y#q2`xw{^SVwsXIJ^L9Z@hYlw0Nko@M)1wjjDqJ31Vpbn_ zd=W2lV=$1G3xc9}*6tDnLmIeRl0?XsQN6mPtT^jh1v+v2-H*G;U-n+MhI^fZ;Uu?A zj*9!E3f>q&$0z_yJ&VMg|2lSTBG?Q2iCCGa4%U!{z+dQlhnyV0@WcV1rJQ-Oxuseu zjn#YB!HO;=b-Uij*JUxUVeKfs=PVkV{Jn1GNM<6W>FT)hU~5&`MG4>Y zg~kyy3t;n$?^87GXGbMY0DCRUo=@SLdmNnYFFYoWi5&+43rxDNm+Z)H5NY0b*dP|` zfPwRCtrXdJrM9%=)H8{gR>N1XwOclus`N)4WO@a4RZZ?O9Z%ukAv3$ElIA2U*XFud z@58+y&GY3+^HJyxQod-*BI4m?p=>r4m0ecYJ1Pp29ns3OrCdZ_yJ8M&Oknm&Js)`Y z5?PQ|BeuT@weZh`r?Y%4`CRYfRc&6H!%G6Gfka)rxZ>(H*?Q_Zn~pt>0E21$f@SRk z-3a+2>pnL$p9l)+ZJG)9l(!??=yfBvI}pGi zaL0HrqMP4gW&jIa^{(&k8@#Fa_z^%gAun*X$;Hs35Ag279!nx9o;SywnM%Y=FXKq` z2dM>CXR9b-tPh_Z{wKUx?Z?d(+P?%2JA3E9JYu_H{}DK*G_?LQ_@extsomuyXk;6d z@H2#WRmxL`rh%6VA@fQxKVv5&i>we|bBX+X$re&Xs*a=!--Cg^|lKE;*7I?laPp!|;hR&Z8qxWWvkCBu6J-;Ko$!gqPmzINe4XEa)(m;yq|= z`(3Qqh)GXAoVG>N2$J%OYS@D2DS!=YK~(Suy$r+KUmFQE9Y`^ZJ^VgZdoMORM10sB zB3zXK3_2!ur`uQFs1X~Kn&`^<;W+T<1x<|-ZOod<(1ErQkv5Too#!mjQ96r$iR|Hi zOmT{U>PpA1gMQZ!Fq8ZH;AiM_zFxw&&u=2Q=*)7WYAi%O(e zFaBQbxivw`ou#HF+f zTf#KXsSsxcD+LpX`I;1EyO5tyi%wWDfT=l71POU|?^gugdUm{YvMK)c_wTm$PFUOP zzz=wMWYy2_44uY_nOeF#rZ2b#tZ+^f7ER|dPp_9*wCC}M@7TP^O>yx{HdLWT0&3In zu+=VcOh65e1$}l44wEa1Vym~`?CBWyf%y*tGsxi$ty~jt2ML8HucbgGOGdnj?W+E1 zjT~}GFF=Ya`qG9W@*)LmPtEyQ%!k>m?Fq6v&@a@g7e^Kg1~X8c!op7R5s&e~vy3z% z!c+KUlRmy7;iwJb{1eUqxUOWPrL9Z~BCcQ0KK{RRit~3C(})>xj%gP2Q^iR|L!^#m zBB7T6bmUe&gwT>~$xIrum80VwT3wEucuInnls6Ul%8PNQ{PASr>4DB_Tq$~^M+L@z zoP(apUzo%m0In#D*UzIRQl~w0-o31poAy90G{=Ot7RyZ#BJXQDZV@S99 z?579s=3g?;2lT`G>^tflJT~cP*Ma3$LPo;?YQLe}5!wiQ;BdLV=^%G~Bmu|0eQB$B zOeE1#h^_QJy*>|5e8mkney^fhCPIHO*UMr{zX`UgKg*8M0!2pU1e2kw!t4}mb8eHC zc@_U-+gb8FFriU4$+1dkkq4{fv?;9Yq&|+iC)tR;K{r>+Nt^~K>|HPbojb*DO81@3 z@Y%JWIW`HT<0VDSpRK;r15ju|AXW*Y`%T>@w4Zku$ zRZ;B^Zg49!_|)$47QC%ALuBD+6*I(+k9NrUR1p75dbKC{@jT&6cT(nk);edRnBfrM zuJp%$5_mnVkuHbCv3imw@ekG4JW7Nk-p*boX|Rz`ldq<8*UQxzfOJN+l#j|aY)*HM zm*L8bYC6=rUbJbIES4hd(FGmM0(3leA%HJOqg~Lk_Jc&5|O1*m@(mt2;UvURYBZ#BYOo~po<+ToXiuZ^T z+U*=-E_&=0ElTPtZe~ZrJtSo&ep1gtk_X$*Va%bi8KO-%av4!wpgk#mPu$W`ZOrg-Kk*?@6UV-?$;Sy40bg5_iEpF{V zYc`EDeR_Qjm*a-Byk4egy<#8NUd?{kMFF~^e|o9J&NPxSc%qYj#eb*iFcmy^`|@D- z(kDXZloJSFXlEygV{^~^1i$wLc5CF~{yxA&ywvp}zg&_;B%{Z7S0TO&|MI}x>!9ie z;Yyf?LRUrgK*1o|>w=b!vfejxw7?g+lZvlu93SxYgzg@cTRT^)R3rB%bh0f`j{8o(;Ti=v2= z{y>RXjFoA3a3ID4LdDiR($~w<;;6s^GDuuhH3;WE5u&t?I$k>#jjp zWQE{6mgnamfbWJ1IsR`e9YHqd`l-Bl3!2b zxXur@MO6hscp@1s!>|Esq-bTA5s}qv3OmFkkXu6zxb7dc2pJ$kIQ{oT5h6NHd!a$l z%;G~?{!%V=Q!wKEEUjj&3CyqTp>Y^IJ^loE(%_MW)9{54-Pch?fP!>qypwx8;dWjW zzrxA>So_&gnv=D$N|2}IA)Br{1hHF~60h654TuO8Yw3QMQ9@*^Yb&-GRZKO-Q}~vM zCY=z#L>LXb@BnfwM@3}zof&z-9tTQ<`r+YySl&tZ83SFd^79}ss{}e=b&N}MBW@{1 zh!Po;(fbyzY$HzdWNpUgU6~$n6q)S$?#PBKaBDU-LXcl$4&vs@qPc$zZ%m z!es8HFj_65s^O89DcO>q%RO$pY6;Ijd}(Dtw~#|yRwA>_hy3;lH2)x{Ihn9bP1R;I z-<5rj09tUX^T#Ojr9%@NH~QR};(S_lJ+Y6P-1NQ0H?eA>wcM9ainF}~J)fuJHX$wt zS-6V@g5q7@r6)CXJ>&NJLQG1GjB%n$aY{;2ZDL_mOpNsxn?L6}2Oh?Rj+kd2K|NSKk4fmwh}ke!j0O;m)Ql}$u|g_T7> zNLWySp0(>gTt!V{1)v7#5ky`%h7eYUJps^Zoyj3+rRwW3NttB?$t?39uVpDZ96oq| zrgAyYYgXW`gI0hC;0}3@`BSa4x+U%ggjrt)S}PQjQ3{raNk{}OK}?hiEH@?drEcEk zuEU3J;@28+d&0~p~upVrK^4AjhxdFbJ+LFs2{JYC0ir# zC)$@u`oA~?Ze!qV&cMjN{~w`V*@-rG>+~qWy${sW%ra0~D{;=%RO;{0Ep!_r78O}T zexxJepFo*p-(Iy3iUj@$mXL^VIayA*7wj-XpH0#x(q^OSI)fU7!Kj-A=)(}r0rGWL zF*idY2iQu~BdVXS3FzsGI8{x-9;~dHGhY`9pJ6|VkmkY0Ux8Ys7b~O-)V4Gcq?avz zjvnL9?bSuaQpK-<#Y&5VJAPApqo-gNU-Z>KqZm_2QXnvLkn@xnZOk|pDx2AptQ?7s z(rxmEgS|16rr0$W`ZCUR4U~H5ojZ?_d&mJOx&0*&h`r0ME>0u+i;?Ujn=gOlQ#Ea1*GMxwRK6txL;ti@3I`r06+C-Ab%dh!V@! za>EObC{(xxQy4wNN_vH*xU%7mT!0cK_Q4di+^XrfG<8Nz>0LC7_h!gof#ue<)nC>U z3KP2~o*eOXsvt;uCETlpqp#uM#-C%!no0YliKP=eUDjT-z{0KfL)Fl;E>1431 z7>WA{bmn^NBNYwHcK|8No!7c~Dt~&-x-jh9FcOnt3{9W2ILut{^y&TdLc*iUVW3P? z^kt@<5(`6!Ryfh2M?up_2jJTUU#I^JG=TCxLZxfO^hpxC6v(hG>d0E>q(LkL_Q8 zndhbFuR&By-OM6g20iG4cB1WI@VyY9R4p4V&v*%{mEGy{fKZL>$91Rl23!sWgn4P7 zYH4g8vQU{UW9Ol=eXf=A-gfg@)tkQw@TXbRi@f8VR4k8@E$ihV9uNWwZi$G2=Sjg% zo1!}I%;2zPupVl2afOtpSxNJcmmg=L>N9$wxC~$C=(@yFef+L>5$M#wflZ0W2 zOXtLS1M&va&#>=*gA7AG-H0j+448V0DeRrFofT5%>-pTnlA4#q4$2ASpnNj&b=R72 zbys>u16BCVc z6|xc+z@YMUfSo*h)I$@PfNK(^X@Vf@hB@dqy830Nozy^mNDg@%Ao$Ld>ZdEO zB{UCfqO+C3Yl0$zQ?|X<-XL+vi|U`$iR3q zJ~uVyYV8eBNp`mZ8Y~8Z@e;1yDvR5eD!|ZY_x?rf8{DZcCW>=L>=E zi+`pdIOzn{sNhB@UaV~bB^k%>Kf6e_c2AF>=2z|=55SwI z-`mA??y~(aLd=x{Qd`m~Fw52%`F`m1UvM_L8H?|z+=Y;1Vls%-yr^!OO8h0DUA_Mt<#EXvk61`VXlGq(UxhhL#`QlgrM9X4GR77N_L z5*Um+5G@(m8?GqePltE2BvYpL76LgSOF`A#G!W0%oW!h3 zakcYk6{L-Bbymzmp-|@JZexkx1|<0W9bhr8x&lGqk-B(r(i1TiGh<+dtkip|z2dI` z@@q?f6ea~NFp@H93iBdbP=-+-5%TZ>P9?KHMOX$!^3$>$Vu8V|NqJPUv#ohoe!Kh% zc?@YYyZN_KHnIiQT4p?*qdD8q$-U%tFg`3{9LGxA#uh-sFT{w+3#`xGA7wG&C;lhd zldij5)>s8RChSI{Gw){vWpLVWsU(m?PTgBL%U|Pu#LktlA(y}6+j7Zt{>TX*a>Q?p ztp$}R8n###TXc$gQVmq%<&y$Z}LE3-Qv#s@)hR*o3w3wJs`f?NEPrPGdG;rMNhG_kPi5%y^Z-<&8|kNbXIZwy{!A4@|A0hw;w;H$(q%Ov>oVa_3&GqLtJ6pcMYZ6%GwNqH?z3>rDhSCZEDV#WUmLtz5E~_Wc!`gL?Fz{2uwL5NC=`{L9q`35v{G&b3 zPraD~Pe8QYKdy_ijnxJ{!uMacU86YC$u{OFhE3+#=$I=Z4T~;#qbT+IBjLq(?rst| z(x3mu)j0);7HrwJY}>YN+qP}nI>l4AZQHhO+qSFv+}9ts<3;T6{Mxy4<;pqd@GVU| zl(wYjUhk=IdlugKn2e!2H(Jk855Enk>-gRDX`4rQ#Dms$M)kCM}|d!(&>~ zvK8rvrbrH*8qYYE$Akb@(yTi(UBl95tQ)>nvT1|KIYL+N(UCOXr~7_(VDJ6q>+F^7 z^^xxL{Q_WjSg!QMrm|Bf(ifZ|vCy-&YisH=7^E5%Oy0wM@nbX+!KkWOT2Yvu$=1v- zdi6a$N>tH)hm$hTKGCI|Ff)4lYuIIY*>QlV0SZbVGUUdPswj85mLtsIHcSH{ori`) zW!C-zzN1luJiZRqsWdYniQ0WJXSN6a&y20((;w8Brn!okJEe1A zxL-n6=`0^0L48GYsx(tb8;=-5rWP@bIw98uLon?Cz6HDrm4O*)BLOc?W2)TvehrZ7^PcECd zbwwf4*zcSlZsR=dYq!boYm#g@@p(xRf|I)bK|$t=sZZwwzl9uE7DedVMHDo{{3cN> zDJK|5zko2_W7%kCK|YIdy#r#JR`DH$-!Y+$%M8rHW?yEXX5F^-K(d<~qC9g-$zTzd zf;8rC7i$q-9$zT2!poYG`)6W|`coShxf$Y7-IUOd0y7`h-kTy|5WgJ_@NsrFh-}y( zJMV8;ueDFo-5)Uyw;Ge?&&Az1%z5dGh@jA%e8}4kNfQVn96tCSeTn7Yrj{$?H2fP~Y%NT@eO5-O2XXtifjwo>2Pbilna|A=8O}TBsrwk?#5ln0@YH3|<&qf26=X6eUV&#OP6Y zTOSh23HDEwlXS7>F?&z~p)`{&jK1aJ_Cjd@s&Kxx?filOtRW^8a}*6gccBD|h3{K2 zhSP)~nfNWhGyo9T3JfZeJCkWzATrg8Yp!33mzf?GZ9-yfx zsDt{HlNy>z9JS6QAoN1rWyD(iW8qYc@5Gm_O&f`7|3npRx2r_q0& z1*)|us=1Jj884gjfh1QHC@wjaH&E~qs%#j=x)P+wSx}_)vX}PLF$H<=SNhNqB8v{* z3j{S~<^AqLf95-x3nTUhU)Gj29_OhGkv8NUp#G|%NcLFFiW%2vpSzAt*c{61YJHd8 zfIiAitt`uapcZcvRP=rqENvUQZJlO7jsN}p^Kfgg0_nMV1OJ!aWs4z}q_Lo?cx9`( zeVy$2?`Om196rwQD^RQV&Dr0s6#FEdm+zq*5 zXk+#f5bc+xm*szl5}ahoPz>#9_+&$@?|}#MBpkWqUK?Ew7a**}6+#s7jWX`TTC$N5MrGKMH7?Tx9G&)F2qD=*)j zrs}5$K$NHI{V1#hz*H8ma0{|*YH?7R_q$d|^JQ(S!q|3J^Y_cB7QJh?CFw4vIIO(( z8^G1!Z8IkfUtqWO8;iDnc8OT$E36r3Ec#z&cwIQ-CZyVAQHidEAcFu ztwWhsi9Mc|b@SWi6~2HRK3}YFH>RA+c@MOZ&-(0GvWMF7r?2i(`P_+LqW8z==2$!N z!};s)>+|hy&wc&u88QFId3P_ag+osk$a(hO^r-LW^IQGs(NcX~Ywv3N!ouCJpUOiE zwhpYz_wS|I9^Q_e*;}(y`P`5D^~w3j`D}M$U$%5T-sIdIr}q$S-RP5#6C>Bv`*AA_ z`r7X$ahN?b6JjvNNEUu)^#Zv?s)(=Y=`%aNhSC?m4_tIPvBP&7tms1huVb~@+UWPs zQhpth-tecjXR&3L2FLgh(bdWvQFlMA>H2>5T;GRhf61`;%nOECGv|y27%V7zV4je9Uj{oE?n8u{iV?J>99#$8`HE|B9J}!^YR0&g`~N zZ2a@icM~_p%+5z=I{74-r`y}{z8iBsE56*Vao^KZ|KEw-;KVb{$3uAho>d*v#kgnf zXXNpP?6Iin%Jy7Vbj=iBJm2T_QhlGVXZ+r;ql3!XpVP$0v!1bn*n*BVi*oINZCT#m zLwmp9b$fcg-=kS{{jYm?^xpT!)m)!u8-_|Ytfr3>FzDW)g~rd$&he>E3%>mIPH!LU ztHN=Xj_fM>Z_V09`G1Sj=jzUpZdZFrdS&w@vI#W?s~6sWwb^>1=`I%fyg6}U>a!+t zUdkUN^83DDMar*6<*QYT>GOYXH$I-V=QiltJ+CcH9sP{Vw`6-wZ$tr2GIzID!_U-; zH(Iop95}YRv;VBbp303l?I3u6mk`beC$ByoNtjoyji;B=fdjH~F=MlZjqa|gbgIIXNd9-jTVwSX~yz4_C_KUMqu<>RV}f=fDzc(`z_XEWW$ zBq!eb9x)~J=8nMu#YN?Gs(0gB5vH0*VB>lL7lEg@0WeOSe3n%F`rhVA9J$W^x?ym z9&En5wK3qko%x<2vmsp|be{dKuE)86l__9t8BBI)<0!xEP_ zmxo_3o#LZu-MrD8T1+EK@W9SKrgD57EtQhBv!>f=5ovZEZim^j>&e5{NE^%#8ri3> z+fvW0f6Bx=uEVN+W6%cJZ$yyQ;?gz^|h{lWo-pr`o6aaVgq&!PEIN z8QIcfb3nH)!Gj@&@1$i}v~gG!THU7Q;oD`)eORyd!MxQN?&H`A#lFWYTXN_ttu+<{ z3%F_}63%=Q6V|@sIevCU$9_)0e()Q_&}Fj?mf2bl@m#jogT>4CO#~iJN1BU^DAJZT ziDU=xmFot3zjAr_*5G&rhH-NcX+5LW9hnm=`@J#o$4&ETZZNPha$fQ%um*&LF~mgP zY5#iotZi4+tIM+Z4|Zx4>tbuq<)H^PUtB(}q#sL92mW+T`rTLYD^tzP-lMv^AEL@I zYOd@r!QsXmo_bHFSI|9=)0ZnV;1tk(X`=S}nIDuAzP{((aHXCX;WJP;-6pR?J>T|Z z$>gznWh^}wf5(+Au5i}+7XRg1=X3Y^c*W_koK4&~5hapRKVNI(n5)#Q<911t3TanNsv9^PY!t-zw}b^dM3>SKO#TCL)M8Rdo1 zsuGM$fp+3}HqFFIpBvrG(M|nwe^##xFt%O%ftP+${j6LGNPOq`n&cdo#_BiQNr(V& ziAPwbt_|&!WRrJ6W~ci;vUX-$beiyr&h@G3DtUJP%htBE_*VV+Y@z9*yyMM78))FL!VV3Z3$~%TO{^dLJ?(bW>Q2wv8b44@sK#|gueQJSSm!D+#kK^N zUD=RC!$#XOkeC~bIH1F;^8Eo?{zgi*eMaeIdu(t59kv@9=Ev1`NQ_-sD>cZ%ER-fl)U|jUWAh+UK&8)o|V;oLh9TY=j+)xJ`FQ4*ePgO5;7f zFS+o1wMgXje45w+L*e?h1zDoCfDpd37w>TpIIacfZ=~6XcqKiX+L#O(`=vFrhd=r# z*UQ34-L$C9G2r%LUu=9+NZmr)?)Be7zq$Nx){-94b2Z-EA~&5Ej5FLlq0H85y7Rbu z(^DP%%yAVAH-ZEk$n|QLa`S4g_GSy+Nm>XJq?!Sf*85f?mP+<1<<8@-`^PhjcsI0; z5k{?3wXklM_xW?nu$h{}i4 z`g=UIniZ0SZsoa?qt3VDOb`~15#4&3TPb0DdT+}@t4fboAM5jz%6WtqIqX@U>-aJ- zuZP-1!n2MIF_03~UU<*SxF6iiJ(`b&$P4C3x?Tc%-M_@Y)AHn*%So>Cde2gW1rfm7 zDOR^5z6pAp6fI~=_*&UDA#^7$orb7`UL@Axv_Btl+jGy%2)7@ff&#l0A>a&KXhzs1 zO`CmoY5lIBhVQ2cQS27kzK%P@F&_*naoY4`iz}yeZa`uAfOt`aey8T2@SXj^;P9V_ z7B6E+wL;}0b1x&0kJ$V@8T$C$yCq9!g|Tc@Zn2)xC@jikaip{@-VPl2jy06wR&o4^Y_T~ag65K-Fu>Ez^^v^9Lro&&53&~I`zgIVo^-Y!Wb2F=FQ1+*{~HHQrRRgA6XAGzRtveO!`4FqEDXwPgGIEkRt zP1Hl%nJyqkUt3#p^&8v4n~Ow?4S^3c4(T=M{JYWA?}jLxt`3`?ZXFe+jBt*nU+lW= zd#mFYl3e(Oox&b3>Kcl?Qvz}ggz|JaCyvtlfRw@euqNh2;S6gUph zMI1lQIrI<6D@66P!T8ogh|N*c(4s|JX++yhdVmOvFJVmM7G=}x@=D!O>Jl#(Mwu3)5t_EvbUvGf$zG(<>iSvU0c9pja<%vBQZHi?So#sKqCP3;m2%2K)DeJb` zrRr$rphooB*KlJkxSXslPQ8_!#`NZ)cBh;v>~5MH`%9|zwZ)26%=Cgzi@B0sjG3SP z=WSKQ%i{{xDH1ck*>V2+^S1WYE+kRr3hz);t@*0E%@VN2?BED4TPq0Nj_I9HGHurC)%loKb(c)G&R`lroEVxO07q;x#mps$Aqd$C=; znvNV@c5`N#O~}vwE-h>^JTKa+tERPKVeYhhnwRRU`_b1Lom{uo*BFHBo!;1xE?y0%Tv0ZY##D

r#}V1>tab96arUw7tyv9;wyCQ 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

VJ>FagD!5U~Lgko$wq#6s7 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

c2T%bt4OVGCkz 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̑&YjUZFaSy{>tJ}moyeTNC_ZrM>yWPL&H42jV} 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 Lv(wEi3wQnxBpDRk diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000010_state b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000010_state deleted file mode 100644 index 9f20fcb0b5425f6d5d5174c3705c15ab4e11b33c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217099 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

r#}V1>tab96arUw7tyv9;wyCQ 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

VJ>FagD!5U~Lgko$wq#6s7 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

c2T%bt4OVGCkz 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̑&YjUZFaSy{>tJ}moyeTNC_ZrM>yWPL&H42jV} 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

x8Q0Y0$Z&?F&)Q4646Rcf*xa6p;qAR(DK z4)c}KR-A2stpUU^8mxp6WV>W}NO?dmgg#beK97RAM+hCz97oL8C^abl$I9#as3i|P zoJiv#wWAIrLs2fJQ76Ju>kX4$jQ4o(;-i6wFM9ZC04@wQZGdZZzO-D5(Hb$lkq458bnq<1P{sxh>`JRr4z5P!*z(c*~t^ zf?yS-LPPmnBlrVJJ9;Adlo0;NAI%qy6*Uta6D=WBFU6zzl7R}1Y_j5vNPgcDc#c*= zE(VqdyJj97snM`K=6ea5V;l*JRz-d0L2w@g#Zqw4p+KXhgqSpfrxL^YfSLf$Jy_rn z8w=9U(||q7a{$Ki91)-s3H6+j741R-h$}Lm-R!_G>kif_&%=S>(RPk99_b^K@%RcS z0m1e1g?O!%LGm`OhPI{*(<2S^bC zv1_n8vrwo3C;&KAERa$-auH$X(y*IkGEqn)3TPBU(?A3Y3I#B>kPrSlIBlhwFA<7# z#PmY+q7@}ZEn*5FJ`mu6UPKL~VebXDt(d4FP89sK2on}3V(yS-nkDAEf+BRg*-*rW z#fhyq+iW9&5P{IH;qN$A+4 z{4brwJ5)ZO$IVf|HgPKEV1l8D#(8|if-2z$l|le29_=$MN2dljAOT`>m^-)vhA|M= z!bO${Q4xe9h~b;*4$48Dflv$d8;KAD2{%*3;Fhiu>`+nBRC)WJ_9AQ#`nls!+e3Zs zV3HDXsBD7-xCMX`ao4=ckIE$^&Bp;2Bt@3!BqX1Lo}f(BF_?}*c0eVh#=dT8aHB>{ z*BdorVh}+!OPI?%jZ1V*zi_1fibApa_~RYhGPb=wk~vk!$IIxni#tkyGvZ`?0K^HX z7R0R5DJFOP?GbCOm6)Co1;=OXk#no~g!r@?=y0d}AlZt~Xmj|Sp9euC9Ixzc(7{m) zCe0Ngu~w~cs>z*tYjJ;~@nI%DW{gaWZlQ;R=5#ndM7cGR*@??^1rFq~l)`yBMBKqi zDLT+AFjz8kxgquXBF41#$t*(uDhQFlSH;H-xH1;kwG!uMt&i(*y}~qbBIH)i3>Xo8 zqU|YLP0U$zDpvyrCjcMQ+p@{#td2(#;C;bL8dSCx3|@#@0ZAaLgCk8N6tSiwrFKBZ z88~B*X9i3zxM8Dq!dW_Shx#(68Lf5!w~G?H@W68liz82<#*PlyOT!9^>^$U808

  • x9<81m#O57~zG=?1U?rW-?^0_B0> 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 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000011_state b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/state_data/10000011_state deleted file mode 100644 index 098046322fca70eab47415a91193bb3e29d1d899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217417 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

    r#}V1>tab96arUw7tyv9;wyCQ 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

    VJ>FagD!5U~Lgko$wq#6s7 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

    -old_operator: address +old_locked_until_secs: u64
    -new_operator: address +new_locked_until_secs: u64
    @@ -657,13 +1132,13 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `AddStakeEvent` +## Struct `JoinValidatorSetEvent` -
    struct AddStakeEvent has drop, store
    +
    struct JoinValidatorSetEvent has drop, store
     
    @@ -678,25 +1153,20 @@ This allows the Stake module to mint rewards to stakers.
    -
    -
    -amount_added: u64 -
    -
    -
    - + -## Struct `ReactivateStakeEvent` +## Struct `JoinValidatorSet` -
    struct ReactivateStakeEvent has drop, store
    +
    #[event]
    +struct JoinValidatorSet has drop, store
     
    @@ -711,25 +1181,19 @@ This allows the Stake module to mint rewards to stakers.
    -
    -
    -amount: u64 -
    -
    -
    - + -## Struct `RotateConsensusKeyEvent` +## Struct `DistributeRewardsEvent` -
    struct RotateConsensusKeyEvent has drop, store
    +
    struct DistributeRewardsEvent has drop, store
     
    @@ -746,13 +1210,7 @@ This allows the Stake module to mint rewards to stakers.
    -old_consensus_pubkey: vector<u8> -
    -
    - -
    -
    -new_consensus_pubkey: vector<u8> +rewards_amount: u64
    @@ -762,13 +1220,14 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `UpdateNetworkAndFullnodeAddressesEvent` +## Struct `DistributeRewards` -
    struct UpdateNetworkAndFullnodeAddressesEvent has drop, store
    +
    #[event]
    +struct DistributeRewards has drop, store
     
    @@ -785,25 +1244,7 @@ This allows the Stake module to mint rewards to stakers.
    -old_network_addresses: vector<u8> -
    -
    - -
    -
    -new_network_addresses: vector<u8> -
    -
    - -
    -
    -old_fullnode_addresses: vector<u8> -
    -
    - -
    -
    -new_fullnode_addresses: vector<u8> +rewards_amount: u64
    @@ -813,13 +1254,13 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `IncreaseLockupEvent` +## Struct `UnlockStakeEvent` -
    struct IncreaseLockupEvent has drop, store
    +
    struct UnlockStakeEvent has drop, store
     
    @@ -836,13 +1277,7 @@ This allows the Stake module to mint rewards to stakers.
    -old_locked_until_secs: u64 -
    -
    - -
    -
    -new_locked_until_secs: u64 +amount_unlocked: u64
    @@ -852,13 +1287,14 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `JoinValidatorSetEvent` +## Struct `UnlockStake` -
    struct JoinValidatorSetEvent has drop, store
    +
    #[event]
    +struct UnlockStake has drop, store
     
    @@ -873,19 +1309,25 @@ This allows the Stake module to mint rewards to stakers.
    +
    +
    +amount_unlocked: u64 +
    +
    +
    - + -## Struct `DistributeRewardsEvent` +## Struct `WithdrawStakeEvent` -
    struct DistributeRewardsEvent has drop, store
    +
    struct WithdrawStakeEvent has drop, store
     
    @@ -902,7 +1344,7 @@ This allows the Stake module to mint rewards to stakers.
    -rewards_amount: u64 +amount_withdrawn: u64
    @@ -912,13 +1354,14 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `UnlockStakeEvent` +## Struct `WithdrawStake` -
    struct UnlockStakeEvent has drop, store
    +
    #[event]
    +struct WithdrawStake has drop, store
     
    @@ -935,7 +1378,7 @@ This allows the Stake module to mint rewards to stakers.
    -amount_unlocked: u64 +amount_withdrawn: u64
    @@ -945,13 +1388,13 @@ This allows the Stake module to mint rewards to stakers. - + -## Struct `WithdrawStakeEvent` +## Struct `LeaveValidatorSetEvent` -
    struct WithdrawStakeEvent has drop, store
    +
    struct LeaveValidatorSetEvent has drop, store
     
    @@ -966,25 +1409,20 @@ This allows the Stake module to mint rewards to stakers.
    -
    -
    -amount_withdrawn: u64 -
    -
    -
    - + -## Struct `LeaveValidatorSetEvent` +## Struct `LeaveValidatorSet` -
    struct LeaveValidatorSetEvent has drop, store
    +
    #[event]
    +struct LeaveValidatorSet has drop, store
     
    @@ -1186,32 +1624,32 @@ security of the testnet. This will NOT be enabled in Mainnet. - + -Limit the maximum value of rewards_rate in order to avoid any arithmetic overflow. +Account is already registered as a validator candidate. -
    const MAX_REWARDS_RATE: u64 = 1000000;
    +
    const EALREADY_REGISTERED: u64 = 8;
     
    - + -Account is already a validator or pending validator. +Limit the maximum value of rewards_rate in order to avoid any arithmetic overflow. -
    const EALREADY_ACTIVE_VALIDATOR: u64 = 4;
    +
    const MAX_REWARDS_RATE: u64 = 1000000;
     
    - + -Account is already registered as a validator candidate. +Account is already a validator or pending validator. -
    const EALREADY_REGISTERED: u64 = 8;
    +
    const EALREADY_ACTIVE_VALIDATOR: u64 = 4;
     
    @@ -1823,7 +2261,9 @@ Return the validator's config. Implementation -
    public fun get_validator_config(pool_address: address): (vector<u8>, vector<u8>, vector<u8>) acquires ValidatorConfig {
    +
    public fun get_validator_config(
    +    pool_address: address
    +): (vector<u8>, vector<u8>, vector<u8>) acquires ValidatorConfig {
         assert_stake_pool_exists(pool_address);
         let validator_config = borrow_global<ValidatorConfig>(pool_address);
         (validator_config.consensus_pubkey, validator_config.network_addresses, validator_config.fullnode_addresses)
    @@ -2115,7 +2555,9 @@ Initialize the validator account and give ownership to the signing account.
             add_stake_events: account::new_event_handle<AddStakeEvent>(owner),
             reactivate_stake_events: account::new_event_handle<ReactivateStakeEvent>(owner),
             rotate_consensus_key_events: account::new_event_handle<RotateConsensusKeyEvent>(owner),
    -        update_network_and_fullnode_addresses_events: account::new_event_handle<UpdateNetworkAndFullnodeAddressesEvent>(owner),
    +        update_network_and_fullnode_addresses_events: account::new_event_handle<UpdateNetworkAndFullnodeAddressesEvent>(
    +            owner
    +        ),
             increase_lockup_events: account::new_event_handle<IncreaseLockupEvent>(owner),
             join_validator_set_events: account::new_event_handle<JoinValidatorSetEvent>(owner),
             distribute_rewards_events: account::new_event_handle<DistributeRewardsEvent>(owner),
    @@ -2262,6 +2704,16 @@ Allows an account with ownership capability to change the operator of the stake
         let old_operator = stake_pool.operator_address;
         stake_pool.operator_address = new_operator;
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            SetOperator {
    +                pool_address,
    +                old_operator,
    +                new_operator,
    +            },
    +        );
    +    };
    +
         event::emit_event(
             &mut stake_pool.set_operator_events,
             SetOperatorEvent {
    @@ -2411,6 +2863,14 @@ Add coins into pool_address. this requires the corresp
         let voting_power = get_next_epoch_voting_power(stake_pool);
         assert!(voting_power <= maximum_stake, error::invalid_argument(ESTAKE_EXCEEDS_MAX));
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            AddStake {
    +                pool_address,
    +                amount_added: amount,
    +            },
    +        );
    +    };
         event::emit_event(
             &mut stake_pool.add_stake_events,
             AddStakeEvent {
    @@ -2485,6 +2945,14 @@ Move amount of coins from pending_inactive to active.
         let reactivated_coins = coin::extract(&mut stake_pool.pending_inactive, amount);
         coin::merge(&mut stake_pool.active, reactivated_coins);
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            ReactivateStake {
    +                pool_address,
    +                amount,
    +            },
    +        );
    +    };
         event::emit_event(
             &mut stake_pool.reactivate_stake_events,
             ReactivateStakeEvent {
    @@ -2538,6 +3006,15 @@ Rotate the consensus key of the validator, it'll take effect in next epoch.
         assert!(option::is_some(pubkey_from_pop), error::invalid_argument(EINVALID_PUBLIC_KEY));
         validator_info.consensus_pubkey = new_consensus_pubkey;
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            RotateConsensusKey {
    +                pool_address,
    +                old_consensus_pubkey,
    +                new_consensus_pubkey,
    +            },
    +        );
    +    };
         event::emit_event(
             &mut stake_pool.rotate_consensus_key_events,
             RotateConsensusKeyEvent {
    @@ -2586,6 +3063,17 @@ Update the network and full node addresses of the validator. This only takes eff
         let old_fullnode_addresses = validator_info.fullnode_addresses;
         validator_info.fullnode_addresses = new_fullnode_addresses;
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            UpdateNetworkAndFullnodeAddresses {
    +                pool_address,
    +                old_network_addresses,
    +                new_network_addresses,
    +                old_fullnode_addresses,
    +                new_fullnode_addresses,
    +            },
    +        );
    +    };
         event::emit_event(
             &mut stake_pool.update_network_and_fullnode_addresses_events,
             UpdateNetworkAndFullnodeAddressesEvent {
    @@ -2596,6 +3084,7 @@ Update the network and full node addresses of the validator. This only takes eff
                 new_fullnode_addresses,
             },
         );
    +
     }
     
    @@ -2659,6 +3148,15 @@ directly inactive if it's not from an active validator. assert!(old_locked_until_secs < new_locked_until_secs, error::invalid_argument(EINVALID_LOCKUP)); stake_pool.locked_until_secs = new_locked_until_secs; + if (std::features::module_event_migration_enabled()) { + event::emit( + IncreaseLockup { + pool_address, + old_locked_until_secs, + new_locked_until_secs, + }, + ); + }; event::emit_event( &mut stake_pool.increase_lockup_events, IncreaseLockupEvent { @@ -2756,10 +3254,18 @@ This internal version can only be called by the Genesis module during Genesis. // Validate the current validator set size has not exceeded the limit. let validator_set = borrow_global_mut<ValidatorSet>(@aptos_framework); - vector::push_back(&mut validator_set.pending_active, generate_validator_info(pool_address, stake_pool, *validator_config)); - let validator_set_size = vector::length(&validator_set.active_validators) + vector::length(&validator_set.pending_active); + vector::push_back( + &mut validator_set.pending_active, + generate_validator_info(pool_address, stake_pool, *validator_config) + ); + let validator_set_size = vector::length(&validator_set.active_validators) + vector::length( + &validator_set.pending_active + ); assert!(validator_set_size <= MAX_VALIDATOR_SET_SIZE, error::invalid_argument(EVALIDATOR_SET_TOO_LARGE)); + if (std::features::module_event_migration_enabled()) { + event::emit(JoinValidatorSet { pool_address }); + }; event::emit_event( &mut stake_pool.join_validator_set_events, JoinValidatorSetEvent { pool_address }, @@ -2833,6 +3339,14 @@ Unlock amount from the active stake. Only possible if the lockup ha let unlocked_stake = coin::extract(&mut stake_pool.active, amount); coin::merge<AptosCoin>(&mut stake_pool.pending_inactive, unlocked_stake); + if (std::features::module_event_migration_enabled()) { + event::emit( + UnlockStake { + pool_address, + amount_unlocked: amount, + }, + ); + }; event::emit_event( &mut stake_pool.unlock_stake_events, UnlockStakeEvent { @@ -2916,6 +3430,14 @@ Withdraw from pool_address's inactive stake with the corresponding withdraw_amount = min(withdraw_amount, coin::value(&stake_pool.inactive)); if (withdraw_amount == 0) return coin::zero<AptosCoin>(); + if (std::features::module_event_migration_enabled()) { + event::emit( + WithdrawStake { + pool_address, + amount_withdrawn: withdraw_amount, + }, + ); + }; event::emit_event( &mut stake_pool.withdraw_stake_events, WithdrawStakeEvent { @@ -2997,6 +3519,9 @@ Can only be called by the operator of the validator/staking pool. assert!(vector::length(&validator_set.active_validators) > 0, error::invalid_state(ELAST_VALIDATOR)); vector::push_back(&mut validator_set.pending_inactive, validator_info); + if (std::features::module_event_migration_enabled()) { + event::emit(LeaveValidatorSet { pool_address }); + }; event::emit_event( &mut stake_pool.leave_validator_set_events, LeaveValidatorSetEvent { @@ -3057,7 +3582,10 @@ This function cannot abort. Implementation -
    public(friend) fun update_performance_statistics(proposer_index: Option<u64>, failed_proposer_indices: vector<u64>) acquires ValidatorPerformance {
    +
    public(friend) fun update_performance_statistics(
    +    proposer_index: Option<u64>,
    +    failed_proposer_indices: vector<u64>
    +) acquires ValidatorPerformance {
         // Validator set cannot change until the end of the epoch, so the validator index in arguments should
         // match with those of the validators in ValidatorPerformance resource.
         let validator_perf = borrow_global_mut<ValidatorPerformance>(@aptos_framework);
    @@ -3086,7 +3614,9 @@ This function cannot abort.
         while ({
             spec {
                 invariant len(validator_perf.validators) == validator_len;
    -            invariant (option::spec_is_some(ghost_proposer_idx) && option::spec_borrow(ghost_proposer_idx) < validator_len) ==>
    +            invariant (option::spec_is_some(ghost_proposer_idx) && option::spec_borrow(
    +                ghost_proposer_idx
    +            ) < validator_len) ==>
                     (validator_perf.validators[option::spec_borrow(ghost_proposer_idx)].successful_proposals ==
                         ghost_valid_perf.validators[option::spec_borrow(ghost_proposer_idx)].successful_proposals + 1);
             };
    @@ -3134,7 +3664,8 @@ power.
     Implementation
     
     
    -
    public(friend) fun on_new_epoch() acquires StakePool, AptosCoinCapabilities, ValidatorConfig, ValidatorPerformance, ValidatorSet, ValidatorFees {
    +
    public(friend) fun on_new_epoch(
    +) acquires StakePool, AptosCoinCapabilities, ValidatorConfig, ValidatorPerformance, ValidatorSet, ValidatorFees {
         let validator_set = borrow_global_mut<ValidatorSet>(@aptos_framework);
         let config = staking_config::get();
         let validator_perf = borrow_global_mut<ValidatorPerformance>(@aptos_framework);
    @@ -3598,6 +4129,9 @@ This function shouldn't abort.
             );
         };
     
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(DistributeRewards { pool_address, rewards_amount });
    +    };
         event::emit_event(
             &mut stake_pool.distribute_rewards_events,
             DistributeRewardsEvent {
    @@ -3712,7 +4246,13 @@ Mint rewards corresponding to current epoch's acquires AptosCoinCapabilities {
         let stake_amount = coin::value(stake);
         let rewards_amount = if (stake_amount > 0) {
    -        calculate_rewards_amount(stake_amount, num_successful_proposals, num_total_proposals, rewards_rate, rewards_rate_denominator)
    +        calculate_rewards_amount(
    +            stake_amount,
    +            num_successful_proposals,
    +            num_total_proposals,
    +            rewards_rate,
    +            rewards_rate_denominator
    +        )
         } else {
             0
         };
    @@ -3926,7 +4466,10 @@ Returns validator's next epoch voting power, including pending_active, active, a
     Implementation
     
     
    -
    public fun configure_allowed_validators(aptos_framework: &signer, accounts: vector<address>) acquires AllowedValidators {
    +
    public fun configure_allowed_validators(
    +    aptos_framework: &signer,
    +    accounts: vector<address>
    +) acquires AllowedValidators {
         let aptos_framework_address = signer::address_of(aptos_framework);
         system_addresses::assert_aptos_framework(aptos_framework);
         if (!exists<AllowedValidators>(aptos_framework_address)) {
    @@ -4083,6 +4626,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     invariant [suspendable] chain_status::is_operating() ==> exists<ValidatorPerformance>(@aptos_framework);
     invariant [suspendable] chain_status::is_operating() ==> exists<ValidatorSet>(@aptos_framework);
     apply ValidatorOwnerNoChange to *;
    +apply ValidatorNotChangeDuringReconfig to * except on_new_epoch;
    +apply StakePoolNotChangeDuringReconfig to * except on_new_epoch, update_stake_pool;
     
     global ghost_valid_perf: ValidatorPerformance;
     
    @@ -4153,6 +4698,33 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    +
    +
    +
    +
    schema ValidatorNotChangeDuringReconfig {
    +    ensures (reconfiguration_state::spec_is_in_progress() && old(exists<ValidatorSet>(@aptos_framework))) ==>
    +        old(global<ValidatorSet>(@aptos_framework)) == global<ValidatorSet>(@aptos_framework);
    +}
    +
    + + + + + + + +
    schema StakePoolNotChangeDuringReconfig {
    +    ensures forall a: address where old(exists<StakePool>(a)): reconfiguration_state::spec_is_in_progress() ==>
    +        (old(global<StakePool>(a).pending_inactive) == global<StakePool>(a).pending_inactive &&
    +        old(global<StakePool>(a).pending_active) == global<StakePool>(a).pending_active &&
    +        old(global<StakePool>(a).inactive) == global<StakePool>(a).inactive &&
    +        old(global<StakePool>(a).active) == global<StakePool>(a).active);
    +}
    +
    + + + + @@ -4339,7 +4911,8 @@ Returns validator's next epoch voting power, including pending_active, active, a -
    include ResourceRequirement;
    +
    pragma verify_duration_estimate = 120;
    +include ResourceRequirement;
     let addr = signer::address_of(owner);
     ensures global<ValidatorConfig>(addr) == ValidatorConfig {
         consensus_pubkey: vector::empty(),
    @@ -4408,7 +4981,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    let owner_address = signer::address_of(owner);
    +
    pragma verify_duration_estimate = 300;
    +let owner_address = signer::address_of(owner);
     aborts_if !exists<OwnerCapability>(owner_address);
     ensures !exists<OwnerCapability>(owner_address);
     
    @@ -4486,7 +5060,9 @@ Returns validator's next epoch voting power, including pending_active, active, a -
    aborts_if reconfiguration_state::spec_is_in_progress();
    +
    pragma verify_duration_estimate = 120;
    +pragma aborts_if_is_partial;
    +aborts_if reconfiguration_state::spec_is_in_progress();
     include ResourceRequirement;
     include AddStakeAbortsIfAndEnsures;
     
    @@ -4504,7 +5080,9 @@ Returns validator's next epoch voting power, including pending_active, active, a -
    include ResourceRequirement;
    +
    pragma disable_invariants_in_body;
    +pragma verify_duration_estimate = 300;
    +include ResourceRequirement;
     let amount = coins.value;
     aborts_if reconfiguration_state::spec_is_in_progress();
     include AddStakeWithCapAbortsIfAndEnsures { amount };
    @@ -4631,7 +5209,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    aborts_if !staking_config::get_allow_validator_set_change(staking_config::get());
    +
    pragma disable_invariants_in_body;
    +aborts_if !staking_config::get_allow_validator_set_change(staking_config::get());
     aborts_if !exists<StakePool>(pool_address);
     aborts_if !exists<ValidatorConfig>(pool_address);
     aborts_if !exists<StakingConfig>(@aptos_framework);
    @@ -4681,7 +5260,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    let pool_address = owner_cap.pool_address;
    +
    pragma verify_duration_estimate = 300;
    +let pool_address = owner_cap.pool_address;
     let pre_stake_pool = global<StakePool>(pool_address);
     let post stake_pool = global<StakePool>(pool_address);
     aborts_if reconfiguration_state::spec_is_in_progress();
    @@ -4706,7 +5286,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    aborts_if reconfiguration_state::spec_is_in_progress();
    +
    pragma verify = false;
    +aborts_if reconfiguration_state::spec_is_in_progress();
     let addr = signer::address_of(owner);
     let ownership_cap = global<OwnerCapability>(addr);
     let pool_address = ownership_cap.pool_address;
    @@ -4835,7 +5416,7 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    pragma verify_duration_estimate = 120;
    +
    pragma verify = false;
     pragma disable_invariants_in_body;
     include ResourceRequirement;
     include GetReconfigStartTimeRequirement;
    @@ -4858,7 +5439,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    -
    aborts_if false;
    +
    pragma verify_duration_estimate = 300;
    +aborts_if false;
     include ResourceRequirement;
     include GetReconfigStartTimeRequirement;
     include features::spec_periodical_reward_rate_decrease_enabled() ==> staking_config::StakingRewardsConfigEnabledRequirement;
    @@ -4934,11 +5516,6 @@ Returns validator's next epoch voting power, including pending_active, active, a
         amount: u64;
         let owner_address = signer::address_of(owner);
         aborts_if !exists<OwnerCapability>(owner_address);
    -    include coin::WithdrawAbortsIf<AptosCoin>{ account: owner };
    -    let coin_store = global<coin::CoinStore<AptosCoin>>(owner_address);
    -    let balance = coin_store.coin.value;
    -    let post coin_post = global<coin::CoinStore<AptosCoin>>(owner_address).coin.value;
    -    ensures coin_post == balance - amount;
         let owner_cap = global<OwnerCapability>(owner_address);
         include AddStakeWithCapAbortsIfAndEnsures { owner_cap };
     }
    @@ -5117,6 +5694,76 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    +
    +
    +
    +
    +
    schema ResourceRequirement {
    +    requires exists<AptosCoinCapabilities>(@aptos_framework);
    +    requires exists<ValidatorPerformance>(@aptos_framework);
    +    requires exists<ValidatorSet>(@aptos_framework);
    +    requires exists<StakingConfig>(@aptos_framework);
    +    requires exists<StakingRewardsConfig>(@aptos_framework) || !features::spec_periodical_reward_rate_decrease_enabled();
    +    requires exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
    +    requires exists<ValidatorFees>(@aptos_framework);
    +}
    +
    + + + + + + + +
    fun spec_get_reward_rate_1(config: StakingConfig): num {
    +   if (features::spec_periodical_reward_rate_decrease_enabled()) {
    +       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
    +       if (epoch_rewards_rate.value == 0) {
    +           0
    +       } else {
    +           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
    +           let denominator = if (denominator_0 > MAX_U64) {
    +               MAX_U64
    +           } else {
    +               denominator_0
    +           };
    +           let nominator = aptos_std::fixed_point64::spec_multiply_u128(denominator, epoch_rewards_rate);
    +           nominator
    +       }
    +   } else {
    +           config.rewards_rate
    +   }
    +}
    +
    + + + + + + + +
    fun spec_get_reward_rate_2(config: StakingConfig): num {
    +   if (features::spec_periodical_reward_rate_decrease_enabled()) {
    +       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
    +       if (epoch_rewards_rate.value == 0) {
    +           1
    +       } else {
    +           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
    +           let denominator = if (denominator_0 > MAX_U64) {
    +               MAX_U64
    +           } else {
    +               denominator_0
    +           };
    +           denominator
    +       }
    +   } else {
    +           config.rewards_rate_denominator
    +   }
    +}
    +
    + + + ### Function `update_stake_pool` @@ -5128,7 +5775,7 @@ Returns validator's next epoch voting power, including pending_active, active, a -
    pragma verify_duration_estimate = 120;
    +
    pragma verify_duration_estimate = 300;
     include ResourceRequirement;
     include GetReconfigStartTimeRequirement;
     include staking_config::StakingRewardsConfigRequirement;
    @@ -5248,6 +5895,7 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    pragma opaque;
    +pragma verify_duration_estimate = 300;
     requires rewards_rate <= MAX_REWARDS_RATE;
     requires rewards_rate_denominator > 0;
     requires rewards_rate <= rewards_rate_denominator;
    @@ -5306,6 +5954,32 @@ Returns validator's next epoch voting power, including pending_active, active, a
     
     
     
    +
    +
    +
    +
    +
    schema DistributeRewardsAbortsIf {
    +    stake: Coin<AptosCoin>;
    +    num_successful_proposals: num;
    +    num_total_proposals: num;
    +    rewards_rate: num;
    +    rewards_rate_denominator: num;
    +    let stake_amount = coin::value(stake);
    +    let rewards_amount = if (stake_amount > 0) {
    +        spec_rewards_amount(stake_amount, num_successful_proposals, num_total_proposals, rewards_rate, rewards_rate_denominator)
    +    } else {
    +        0
    +    };
    +    let amount = rewards_amount;
    +    let addr = type_info::type_of<AptosCoin>().account_address;
    +    aborts_if (rewards_amount > 0) && !exists<coin::CoinInfo<AptosCoin>>(addr);
    +    modifies global<coin::CoinInfo<AptosCoin>>(addr);
    +    include (rewards_amount > 0) ==> coin::CoinAddAbortsIf<AptosCoin> { amount: amount };
    +}
    +
    + + + ### Function `append` @@ -5359,7 +6033,8 @@ Returns validator's next epoch voting power, including pending_active, active, a -
    aborts_if !exists<ValidatorSet>(@aptos_framework);
    +
    requires !reconfiguration_state::spec_is_in_progress();
    +aborts_if !exists<ValidatorSet>(@aptos_framework);
     aborts_if !exists<staking_config::StakingConfig>(@aptos_framework);
     let aptos = @aptos_framework;
     let pre_validator_set = global<ValidatorSet>(aptos);
    diff --git a/aptos-move/framework/aptos-framework/doc/staking_config.md b/aptos-move/framework/aptos-framework/doc/staking_config.md
    index 0f89c9afc8de1..244ed0c23647e 100644
    --- a/aptos-move/framework/aptos-framework/doc/staking_config.md
    +++ b/aptos-move/framework/aptos-framework/doc/staking_config.md
    @@ -10,6 +10,7 @@ Provides the configuration for staking and rewards
     -  [Resource `StakingRewardsConfig`](#0x1_staking_config_StakingRewardsConfig)
     -  [Constants](#@Constants_0)
     -  [Function `initialize`](#0x1_staking_config_initialize)
    +-  [Function `reward_rate`](#0x1_staking_config_reward_rate)
     -  [Function `initialize_rewards`](#0x1_staking_config_initialize_rewards)
     -  [Function `get`](#0x1_staking_config_get)
     -  [Function `get_allow_validator_set_change`](#0x1_staking_config_get_allow_validator_set_change)
    @@ -32,6 +33,7 @@ Provides the configuration for staking and rewards
         -  [Resource `StakingConfig`](#@Specification_1_StakingConfig)
         -  [Resource `StakingRewardsConfig`](#@Specification_1_StakingRewardsConfig)
         -  [Function `initialize`](#@Specification_1_initialize)
    +    -  [Function `reward_rate`](#@Specification_1_reward_rate)
         -  [Function `initialize_rewards`](#@Specification_1_initialize_rewards)
         -  [Function `get`](#@Specification_1_get)
         -  [Function `get_reward_rate`](#@Specification_1_get_reward_rate)
    @@ -389,6 +391,32 @@ Only called during genesis.
     
     
     
    +
    +
    +
    +
    +## Function `reward_rate`
    +
    +Return the reward rate of this epoch as a tuple (numerator, denominator).
    +
    +
    +
    #[view]
    +public fun reward_rate(): (u64, u64)
    +
    + + + +
    +Implementation + + +
    public fun reward_rate(): (u64, u64) acquires StakingRewardsConfig, StakingConfig {
    +    get_reward_rate(borrow_global<StakingConfig>(@aptos_framework))
    +}
    +
    + + +
    @@ -1211,6 +1239,28 @@ StakingConfig does not exist under the aptos_framework before creating it. + + +### Function `reward_rate` + + +
    #[view]
    +public fun reward_rate(): (u64, u64)
    +
    + + + + +
    let config = global<StakingConfig>(@aptos_framework);
    +aborts_if !exists<StakingConfig>(@aptos_framework);
    +include StakingRewardsConfigRequirement;
    +ensures (features::spec_periodical_reward_rate_decrease_enabled() &&
    +    (global<StakingRewardsConfig>(@aptos_framework).rewards_rate.value as u64) != 0) ==>
    +    result_1 <= MAX_REWARDS_RATE && result_2 <= MAX_U64;
    +
    + + + ### Function `initialize_rewards` @@ -1304,7 +1354,8 @@ StakingRewardsConfig does not exist under the aptos_framework before creating it -
    requires features::spec_periodical_reward_rate_decrease_enabled();
    +
    pragma verify_duration_estimate = 120;
    +requires features::spec_periodical_reward_rate_decrease_enabled();
     include StakingRewardsConfigRequirement;
     aborts_if !exists<StakingRewardsConfig>(@aptos_framework);
     
    @@ -1405,7 +1456,8 @@ Caller must be @aptos_framework. StakingRewardsConfig is under the @aptos_framework. -
    include StakingRewardsConfigRequirement;
    +
    pragma verify_duration_estimate = 120;
    +include StakingRewardsConfigRequirement;
     let addr = signer::address_of(aptos_framework);
     // This enforces high-level requirement 1:
     aborts_if addr != @aptos_framework;
    diff --git a/aptos-move/framework/aptos-framework/doc/staking_contract.md b/aptos-move/framework/aptos-framework/doc/staking_contract.md
    index a9a2f52f1182c..c2d9178ff06fc 100644
    --- a/aptos-move/framework/aptos-framework/doc/staking_contract.md
    +++ b/aptos-move/framework/aptos-framework/doc/staking_contract.md
    @@ -36,7 +36,18 @@ pool.
     -  [Resource `Store`](#0x1_staking_contract_Store)
     -  [Resource `BeneficiaryForOperator`](#0x1_staking_contract_BeneficiaryForOperator)
     -  [Struct `UpdateCommissionEvent`](#0x1_staking_contract_UpdateCommissionEvent)
    +-  [Struct `UpdateCommission`](#0x1_staking_contract_UpdateCommission)
     -  [Resource `StakingGroupUpdateCommissionEvent`](#0x1_staking_contract_StakingGroupUpdateCommissionEvent)
    +-  [Struct `CreateStakingContract`](#0x1_staking_contract_CreateStakingContract)
    +-  [Struct `UpdateVoter`](#0x1_staking_contract_UpdateVoter)
    +-  [Struct `ResetLockup`](#0x1_staking_contract_ResetLockup)
    +-  [Struct `AddStake`](#0x1_staking_contract_AddStake)
    +-  [Struct `RequestCommission`](#0x1_staking_contract_RequestCommission)
    +-  [Struct `UnlockStake`](#0x1_staking_contract_UnlockStake)
    +-  [Struct `SwitchOperator`](#0x1_staking_contract_SwitchOperator)
    +-  [Struct `AddDistribution`](#0x1_staking_contract_AddDistribution)
    +-  [Struct `Distribute`](#0x1_staking_contract_Distribute)
    +-  [Struct `SetBeneficiaryForOperator`](#0x1_staking_contract_SetBeneficiaryForOperator)
     -  [Struct `CreateStakingContractEvent`](#0x1_staking_contract_CreateStakingContractEvent)
     -  [Struct `UpdateVoterEvent`](#0x1_staking_contract_UpdateVoterEvent)
     -  [Struct `ResetLockupEvent`](#0x1_staking_contract_ResetLockupEvent)
    @@ -46,7 +57,6 @@ pool.
     -  [Struct `SwitchOperatorEvent`](#0x1_staking_contract_SwitchOperatorEvent)
     -  [Struct `AddDistributionEvent`](#0x1_staking_contract_AddDistributionEvent)
     -  [Struct `DistributeEvent`](#0x1_staking_contract_DistributeEvent)
    --  [Struct `SetBeneficiaryForOperator`](#0x1_staking_contract_SetBeneficiaryForOperator)
     -  [Constants](#@Constants_0)
     -  [Function `stake_pool_address`](#0x1_staking_contract_stake_pool_address)
     -  [Function `last_recorded_principal`](#0x1_staking_contract_last_recorded_principal)
    @@ -333,6 +343,52 @@ pool.
     
     
     
    +
    +Fields + + +
    +
    +staker: address +
    +
    + +
    +
    +operator: address +
    +
    + +
    +
    +old_commission_percentage: u64 +
    +
    + +
    +
    +new_commission_percentage: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateCommission` + + + +
    #[event]
    +struct UpdateCommission has drop, store
    +
    + + +
    Fields @@ -395,13 +451,14 @@ pool.
    - + -## Struct `CreateStakingContractEvent` +## Struct `CreateStakingContract` -
    struct CreateStakingContractEvent has drop, store
    +
    #[event]
    +struct CreateStakingContract has drop, store
     
    @@ -446,13 +503,14 @@ pool. - + -## Struct `UpdateVoterEvent` +## Struct `UpdateVoter` -
    struct UpdateVoterEvent has drop, store
    +
    #[event]
    +struct UpdateVoter has drop, store
     
    @@ -491,13 +549,14 @@ pool. - + -## Struct `ResetLockupEvent` +## Struct `ResetLockup` -
    struct ResetLockupEvent has drop, store
    +
    #[event]
    +struct ResetLockup has drop, store
     
    @@ -524,13 +583,14 @@ pool. - + -## Struct `AddStakeEvent` +## Struct `AddStake` -
    struct AddStakeEvent has drop, store
    +
    #[event]
    +struct AddStake has drop, store
     
    @@ -563,13 +623,14 @@ pool. - + -## Struct `RequestCommissionEvent` +## Struct `RequestCommission` -
    struct RequestCommissionEvent has drop, store
    +
    #[event]
    +struct RequestCommission has drop, store
     
    @@ -608,13 +669,14 @@ pool. - + -## Struct `UnlockStakeEvent` +## Struct `UnlockStake` -
    struct UnlockStakeEvent has drop, store
    +
    #[event]
    +struct UnlockStake has drop, store
     
    @@ -653,13 +715,14 @@ pool. - + -## Struct `SwitchOperatorEvent` +## Struct `SwitchOperator` -
    struct SwitchOperatorEvent has drop, store
    +
    #[event]
    +struct SwitchOperator has drop, store
     
    @@ -692,13 +755,14 @@ pool. - + -## Struct `AddDistributionEvent` +## Struct `AddDistribution` -
    struct AddDistributionEvent has drop, store
    +
    #[event]
    +struct AddDistribution has drop, store
     
    @@ -731,13 +795,14 @@ pool. - + -## Struct `DistributeEvent` +## Struct `Distribute` -
    struct DistributeEvent has drop, store
    +
    #[event]
    +struct Distribute has drop, store
     
    @@ -814,6 +879,387 @@ pool. + + + + +## Struct `CreateStakingContractEvent` + + + +
    struct CreateStakingContractEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +principal: u64 +
    +
    + +
    +
    +commission_percentage: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateVoterEvent` + + + +
    struct UpdateVoterEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_voter: address +
    +
    + +
    +
    +new_voter: address +
    +
    + +
    +
    + + +
    + + + +## Struct `ResetLockupEvent` + + + +
    struct ResetLockupEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `AddStakeEvent` + + + +
    struct AddStakeEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `RequestCommissionEvent` + + + +
    struct RequestCommissionEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +accumulated_rewards: u64 +
    +
    + +
    +
    +commission_amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `UnlockStakeEvent` + + + +
    struct UnlockStakeEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    +commission_paid: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `SwitchOperatorEvent` + + + +
    struct SwitchOperatorEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +old_operator: address +
    +
    + +
    +
    +new_operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `AddDistributionEvent` + + + +
    struct AddDistributionEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `DistributeEvent` + + + +
    struct DistributeEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +pool_address: address +
    +
    + +
    +
    +recipient: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + +
    @@ -1285,6 +1731,9 @@ Staker can call this function to create a simple staking contract with a specifi signer_cap: stake_pool_signer_cap, }); + if (std::features::module_event_migration_enabled()) { + emit(CreateStakingContract { operator, voter, pool_address, principal, commission_percentage }); + }; emit_event( &mut store.create_staking_contract_events, CreateStakingContractEvent { operator, voter, pool_address, principal, commission_percentage }, @@ -1326,6 +1775,9 @@ Add more stake to an existing staking contract. staking_contract.principal = staking_contract.principal + amount; let pool_address = staking_contract.pool_address; + if (std::features::module_event_migration_enabled()) { + emit(AddStake { operator, pool_address, amount }); + }; emit_event( &mut store.add_stake_events, AddStakeEvent { operator, pool_address, amount }, @@ -1363,10 +1815,14 @@ Convenient function to allow the staker to update the voter address in a staking let old_voter = stake::get_delegated_voter(pool_address); stake::set_delegated_voter_with_cap(&staking_contract.owner_cap, new_voter); + if (std::features::module_event_migration_enabled()) { + emit(UpdateVoter { operator, pool_address, old_voter, new_voter }); + }; emit_event( &mut store.update_voter_events, UpdateVoterEvent { operator, pool_address, old_voter, new_voter }, ); + }
    @@ -1399,6 +1855,9 @@ Convenient function to allow the staker to reset their stake pool's lockup perio let pool_address = staking_contract.pool_address; stake::increase_lockup_with_cap(&staking_contract.owner_cap); + if (std::features::module_event_migration_enabled()) { + emit(ResetLockup { operator, pool_address }); + }; emit_event(&mut store.reset_lockup_events, ResetLockupEvent { operator, pool_address }); }
    @@ -1424,7 +1883,11 @@ TODO: fix the typo in function name. commision -> commission Implementation -
    public entry fun update_commision(staker: &signer, operator: address, new_commission_percentage: u64) acquires Store, StakingGroupUpdateCommissionEvent, BeneficiaryForOperator {
    +
    public entry fun update_commision(
    +    staker: &signer,
    +    operator: address,
    +    new_commission_percentage: u64
    +) acquires Store, BeneficiaryForOperator, StakingGroupUpdateCommissionEvent {
         assert!(
             new_commission_percentage >= 0 && new_commission_percentage <= 100,
             error::invalid_argument(EINVALID_COMMISSION_PERCENTAGE),
    @@ -1445,7 +1908,19 @@ TODO: fix the typo in function name. commision -> commission
         let old_commission_percentage = staking_contract.commission_percentage;
         staking_contract.commission_percentage = new_commission_percentage;
         if (!exists<StakingGroupUpdateCommissionEvent>(staker_address)) {
    -        move_to(staker, StakingGroupUpdateCommissionEvent { update_commission_events: account::new_event_handle<UpdateCommissionEvent>(staker)})
    +        move_to(
    +            staker,
    +            StakingGroupUpdateCommissionEvent {
    +                update_commission_events: account::new_event_handle<UpdateCommissionEvent>(
    +                    staker
    +                )
    +            }
    +        )
    +    };
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            UpdateCommission { staker: staker_address, operator, old_commission_percentage, new_commission_percentage }
    +        );
         };
         emit_event(
             &mut borrow_global_mut<StakingGroupUpdateCommissionEvent>(staker_address).update_commission_events,
    @@ -1477,7 +1952,11 @@ Only staker, operator or beneficiary can call this.
     Implementation
     
     
    -
    public entry fun request_commission(account: &signer, staker: address, operator: address) acquires Store, BeneficiaryForOperator {
    +
    public entry fun request_commission(
    +    account: &signer,
    +    staker: address,
    +    operator: address
    +) acquires Store, BeneficiaryForOperator {
         let account_addr = signer::address_of(account);
         assert!(
             account_addr == staker || account_addr == operator || account_addr == beneficiary_for_operator(operator),
    @@ -1547,6 +2026,9 @@ Only staker, operator or beneficiary can call this.
         stake::unlock_with_cap(commission_amount, &staking_contract.owner_cap);
     
         let pool_address = staking_contract.pool_address;
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(RequestCommission { operator, pool_address, accumulated_rewards, commission_amount });
    +    };
         emit_event(
             request_commission_events,
             RequestCommissionEvent { operator, pool_address, accumulated_rewards, commission_amount },
    @@ -1577,7 +2059,11 @@ This also triggers paying commission to the operator for accounting simplicity.
     Implementation
     
     
    -
    public entry fun unlock_stake(staker: &signer, operator: address, amount: u64) acquires Store, BeneficiaryForOperator {
    +
    public entry fun unlock_stake(
    +    staker: &signer,
    +    operator: address,
    +    amount: u64
    +) acquires Store, BeneficiaryForOperator {
         // Short-circuit if amount is 0.
         if (amount == 0) return;
     
    @@ -1608,14 +2094,16 @@ This also triggers paying commission to the operator for accounting simplicity.
         staking_contract.principal = staking_contract.principal - amount;
     
         // Record a distribution for the staker.
    -    add_distribution(
    -        operator, staking_contract, staker_address, amount, &mut store.add_distribution_events);
    +    add_distribution(operator, staking_contract, staker_address, amount, &mut store.add_distribution_events);
     
         // Request to unlock the distribution amount from the stake pool.
         // This won't become fully unlocked until the stake pool's lockup expires.
         stake::unlock_with_cap(amount, &staking_contract.owner_cap);
     
         let pool_address = staking_contract.pool_address;
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(UnlockStake { pool_address, operator, amount, commission_paid });
    +    };
         emit_event(
             &mut store.unlock_stake_events,
             UnlockStakeEvent { pool_address, operator, amount, commission_paid },
    @@ -1743,6 +2231,9 @@ Allows staker to switch operator without going through the lenghthy process to u
     
         let pool_address = staking_contract.pool_address;
         simple_map::add(staking_contracts, new_operator, staking_contract);
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(SwitchOperator { pool_address, old_operator, new_operator });
    +    };
         emit_event(
             &mut store.switch_operator_events,
             SwitchOperatorEvent { pool_address, old_operator, new_operator }
    @@ -1772,7 +2263,10 @@ the beneficiary. An operator can set one beneficiary for staking contract pools,
     Implementation
     
     
    -
    public entry fun set_beneficiary_for_operator(operator: &signer, new_beneficiary: address) acquires BeneficiaryForOperator {
    +
    public entry fun set_beneficiary_for_operator(
    +    operator: &signer,
    +    new_beneficiary: address
    +) acquires BeneficiaryForOperator {
         assert!(features::operator_beneficiary_change_enabled(), std::error::invalid_state(
             EOPERATOR_BENEFICIARY_CHANGE_NOT_SUPPORTED
         ));
    @@ -1875,6 +2369,9 @@ Distribute all unlocked (inactive) funds according to distribution shares.
             };
             aptos_account::deposit_coins(recipient, coin::extract(&mut coins, amount_to_distribute));
     
    +        if (std::features::module_event_migration_enabled()) {
    +            emit(Distribute { operator, pool_address, recipient, amount: amount_to_distribute });
    +        };
             emit_event(
                 distribute_events,
                 DistributeEvent { operator, pool_address, recipient, amount: amount_to_distribute }
    @@ -1946,7 +2443,7 @@ Add a new distribution for recipient and amount to the
         staking_contract: &mut StakingContract,
         recipient: address,
         coins_amount: u64,
    -    add_distribution_events: &mut EventHandle<AddDistributionEvent>,
    +    add_distribution_events: &mut EventHandle<AddDistributionEvent>
     ) {
         let distribution_pool = &mut staking_contract.distribution_pool;
         let (_, _, _, total_distribution_amount) = stake::get_stake(staking_contract.pool_address);
    @@ -1955,6 +2452,9 @@ Add a new distribution for recipient and amount to the
     
         pool_u64::buy_in(distribution_pool, recipient, coins_amount);
         let pool_address = staking_contract.pool_address;
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(AddDistribution { operator, pool_address, amount: coins_amount });
    +    };
         emit_event(
             add_distribution_events,
             AddDistributionEvent { operator, pool_address, amount: coins_amount }
    @@ -2340,7 +2840,7 @@ Staking_contract exists the stacker/operator pair.
     let staking_contracts = global<Store>(staker).staking_contracts;
     let staking_contract = simple_map::spec_get(staking_contracts, operator);
     include ContractExistsAbortsIf;
    -include GetStakingContractAmountsAbortsIf{staking_contract};
    +include GetStakingContractAmountsAbortsIf { staking_contract };
     let pool_address = staking_contract.pool_address;
     let stake_pool = global<stake::StakePool>(pool_address);
     let active = coin::value(stake_pool.active);
    @@ -2441,7 +2941,7 @@ Account is not frozen and sufficient to withdraw.
     
    pragma aborts_if_is_partial;
     pragma verify_duration_estimate = 120;
     include PreconditionsInCreateContract;
    -include WithdrawAbortsIf<AptosCoin> {account: staker};
    +include WithdrawAbortsIf<AptosCoin> { account: staker };
     include CreateStakingContractWithCoinsAbortsIfAndEnsures;
     
    @@ -2483,14 +2983,14 @@ Account is not frozen and sufficient to withdraw. Staking_contract exists the stacker/operator pair. -
    pragma verify_duration_estimate = 120;
    +
    pragma verify_duration_estimate = 600;
     include stake::ResourceRequirement;
     aborts_if reconfiguration_state::spec_is_in_progress();
     let staker_address = signer::address_of(staker);
     include ContractExistsAbortsIf { staker: staker_address };
     let store = global<Store>(staker_address);
     let staking_contract = simple_map::spec_get(store.staking_contracts, operator);
    -include WithdrawAbortsIf<AptosCoin>{account: staker};
    +include WithdrawAbortsIf<AptosCoin> { account: staker };
     let balance = global<coin::CoinStore<AptosCoin>>(staker_address).coin.value;
     let post post_coin = global<coin::CoinStore<AptosCoin>>(staker_address).coin.value;
     ensures post_coin == balance - amount;
    @@ -2518,7 +3018,7 @@ Staking_contract exists the stacker/operator pair.
     
     
     
    let staker_address = signer::address_of(staker);
    -include UpdateVoterSchema {staker: staker_address};
    +include UpdateVoterSchema { staker: staker_address };
     let post store = global<Store>(staker_address);
     let post staking_contract = simple_map::spec_get(store.staking_contracts, operator);
     let post pool_address = staking_contract.owner_cap.pool_address;
    @@ -2543,8 +3043,8 @@ Only active validator can update locked_until_secs.
     
     
    let staker_address = signer::address_of(staker);
     // This enforces high-level requirement 5:
    -include ContractExistsAbortsIf{staker: staker_address};
    -include IncreaseLockupWithCapAbortsIf{staker: staker_address};
    +include ContractExistsAbortsIf { staker: staker_address };
    +include IncreaseLockupWithCapAbortsIf { staker: staker_address };
     
    @@ -2563,7 +3063,7 @@ Only active validator can update locked_until_secs.
    pragma verify = false;
     let staker_address = signer::address_of(staker);
     aborts_if new_commission_percentage > 100;
    -include ContractExistsAbortsIf{staker: staker_address};
    +include ContractExistsAbortsIf { staker: staker_address };
     
    @@ -2582,7 +3082,7 @@ Only staker or operator can call this.
    pragma verify = false;
     let account_addr = signer::address_of(account);
    -include ContractExistsAbortsIf{staker};
    +include ContractExistsAbortsIf { staker };
     aborts_if account_addr != staker && account_addr != operator;
     
    @@ -2619,7 +3119,7 @@ Only staker or operator can call this.
    pragma verify = false;
     requires amount > 0;
     let staker_address = signer::address_of(staker);
    -include ContractExistsAbortsIf{staker: staker_address};
    +include ContractExistsAbortsIf { staker: staker_address };
     
    @@ -2642,7 +3142,7 @@ Staking_contract exists the stacker/operator pair. let staker_address = signer::address_of(staker); let staking_contracts = global<Store>(staker_address).staking_contracts; let staking_contract = simple_map::spec_get(staking_contracts, operator); -include ContractExistsAbortsIf{staker: staker_address}; +include ContractExistsAbortsIf { staker: staker_address };
    @@ -2659,9 +3159,10 @@ Staking_contract exists the stacker/operator pair. Staking_contract exists the stacker/operator pair. -
    pragma aborts_if_is_partial;
    +
    pragma verify_duration_estimate = 120;
    +pragma aborts_if_is_partial;
     let staker_address = signer::address_of(staker);
    -include ContractExistsAbortsIf{staker: staker_address, operator: old_operator};
    +include ContractExistsAbortsIf { staker: staker_address, operator: old_operator };
     
    @@ -2680,7 +3181,7 @@ Staking_contract exists the stacker/operator pair.
    pragma verify = false;
     let staker_address = signer::address_of(staker);
    -include ContractExistsAbortsIf{staker: staker_address, operator: old_operator};
    +include ContractExistsAbortsIf { staker: staker_address, operator: old_operator };
     let store = global<Store>(staker_address);
     let staking_contracts = store.staking_contracts;
     aborts_if simple_map::spec_contains_key(staking_contracts, new_operator);
    @@ -2716,7 +3217,8 @@ Staking_contract exists the stacker/operator pair.
     Staking_contract exists the stacker/operator pair.
     
     
    -
    pragma aborts_if_is_partial;
    +
    pragma verify_duration_estimate = 120;
    +pragma aborts_if_is_partial;
     include ContractExistsAbortsIf;
     
    @@ -2735,7 +3237,8 @@ The StakePool exists under the pool_address of StakingContract. The value of inactive and pending_inactive in the stake_pool is up to MAX_U64. -
    pragma aborts_if_is_partial;
    +
    pragma verify_duration_estimate = 120;
    +pragma aborts_if_is_partial;
     let pool_address = staking_contract.pool_address;
     let stake_pool = borrow_global<stake::StakePool>(pool_address);
     aborts_if !exists<stake::StakePool>(pool_address);
    @@ -2790,7 +3293,8 @@ Staking_contract exists the stacker/operator pair.
     The StakePool exists under the pool_address of StakingContract.
     
     
    -
    include GetStakingContractAmountsAbortsIf;
    +
    pragma verify_duration_estimate = 120;
    +include GetStakingContractAmountsAbortsIf;
     let pool_address = staking_contract.pool_address;
     let stake_pool = global<stake::StakePool>(pool_address);
     let active = coin::value(stake_pool.active);
    @@ -2816,12 +3320,13 @@ The StakePool exists under the pool_address of StakingContract.
     
     
     
    -
    include stake::ResourceRequirement;
    +
    pragma verify_duration_estimate = 120;
    +include stake::ResourceRequirement;
     let staker_address = signer::address_of(staker);
     let seed_0 = bcs::to_bytes(staker_address);
     let seed_1 = concat(concat(concat(seed_0, bcs::to_bytes(operator)), SALT), contract_creation_seed);
     let resource_addr = account::spec_create_resource_address(staker_address, seed_1);
    -include CreateStakePoolAbortsIf {resource_addr};
    +include CreateStakePoolAbortsIf { resource_addr };
     ensures exists<account::Account>(resource_addr);
     let post post_account = global<account::Account>(resource_addr);
     ensures post_account.authentication_key == account::ZERO_AUTH_KEY;
    @@ -2980,7 +3485,9 @@ a staking_contract exists for the staker/operator pair.
         let config = global<staking_config::StakingConfig>(@aptos_framework);
         let stake_pool = global<stake::StakePool>(pool_address);
         let old_locked_until_secs = stake_pool.locked_until_secs;
    -    let seconds = global<timestamp::CurrentTimeMicroseconds>(@aptos_framework).microseconds / timestamp::MICRO_CONVERSION_FACTOR;
    +    let seconds = global<timestamp::CurrentTimeMicroseconds>(
    +        @aptos_framework
    +    ).microseconds / timestamp::MICRO_CONVERSION_FACTOR;
         let new_locked_until_secs = seconds + config.recurring_lockup_duration_secs;
         aborts_if seconds + config.recurring_lockup_duration_secs > MAX_U64;
         aborts_if old_locked_until_secs > new_locked_until_secs || old_locked_until_secs == new_locked_until_secs;
    @@ -3033,7 +3540,9 @@ a staking_contract exists for the staker/operator pair.
     
    schema PreconditionsInCreateContract {
         requires exists<stake::ValidatorPerformance>(@aptos_framework);
         requires exists<stake::ValidatorSet>(@aptos_framework);
    -    requires exists<staking_config::StakingRewardsConfig>(@aptos_framework) || !std::features::spec_periodical_reward_rate_decrease_enabled();
    +    requires exists<staking_config::StakingRewardsConfig>(
    +        @aptos_framework
    +    ) || !std::features::spec_periodical_reward_rate_decrease_enabled();
         requires exists<stake::ValidatorFees>(@aptos_framework);
         requires exists<aptos_framework::timestamp::CurrentTimeMicroseconds>(@aptos_framework);
         requires exists<stake::AptosCoinCapabilities>(@aptos_framework);
    @@ -3052,7 +3561,9 @@ a staking_contract exists for the staker/operator pair.
         voter: address;
         contract_creation_seed: vector<u8>;
         let acc = global<account::Account>(resource_addr);
    -    aborts_if exists<account::Account>(resource_addr) && (len(acc.signer_capability_offer.for.vec) != 0 || acc.sequence_number != 0);
    +    aborts_if exists<account::Account>(resource_addr) && (len(
    +        acc.signer_capability_offer.for.vec
    +    ) != 0 || acc.sequence_number != 0);
         aborts_if !exists<account::Account>(resource_addr) && len(bcs::to_bytes(resource_addr)) != 32;
         aborts_if len(account::ZERO_AUTH_KEY) != 32;
         aborts_if exists<stake::ValidatorConfig>(resource_addr);
    @@ -3060,7 +3571,9 @@ a staking_contract exists for the staker/operator pair.
         aborts_if exists<stake::AllowedValidators>(@aptos_framework) && !contains(allowed.accounts, resource_addr);
         aborts_if exists<stake::StakePool>(resource_addr);
         aborts_if exists<stake::OwnerCapability>(resource_addr);
    -    aborts_if exists<account::Account>(resource_addr) && acc.guid_creation_num + 12 >= account::MAX_GUID_CREATION_NUM;
    +    aborts_if exists<account::Account>(
    +        resource_addr
    +    ) && acc.guid_creation_num + 12 >= account::MAX_GUID_CREATION_NUM;
     }
     
    diff --git a/aptos-move/framework/aptos-framework/doc/storage_gas.md b/aptos-move/framework/aptos-framework/doc/storage_gas.md index 2ea9e134ef264..426d04272ec26 100644 --- a/aptos-move/framework/aptos-framework/doc/storage_gas.md +++ b/aptos-move/framework/aptos-framework/doc/storage_gas.md @@ -1308,7 +1308,7 @@ A non decreasing curve must ensure that next is greater than cur. 1 The module's initialization guarantees the creation of the StorageGasConfig resource with a precise configuration, including accurate gas curves for per-item and per-byte operations. Medium -The initialize function is responsible for setting up the initial state of the module, ensuring existence witqhin the module's context, and (2) the configuration of the StorageGasConfig resource includes the precise gas curves that define the behavior of per-item and per-byte operations. +The initialize function is responsible for setting up the initial state of the module, ensuring the fulfillment of the following conditions: (1) the creation of the StorageGasConfig resource, indicating its existence witqhin the module's context, and (2) the configuration of the StorageGasConfig resource includes the precise gas curves that define the behavior of per-item and per-byte operations. Formally verified via initialize. Moreover, the native gas logic has been manually audited. @@ -1545,6 +1545,7 @@ and exists after the function is executed.
    include system_addresses::AbortsIfNotAptosFramework{ account: aptos_framework };
    +pragma verify_duration_estimate = 120;
     aborts_if exists<StorageGasConfig>(@aptos_framework);
     aborts_if exists<StorageGas>(@aptos_framework);
     // This enforces high-level requirement 1:
    diff --git a/aptos-move/framework/aptos-framework/doc/transaction_context.md b/aptos-move/framework/aptos-framework/doc/transaction_context.md
    index 6c7b65cb802c9..cc7d9010ffc74 100644
    --- a/aptos-move/framework/aptos-framework/doc/transaction_context.md
    +++ b/aptos-move/framework/aptos-framework/doc/transaction_context.md
    @@ -6,6 +6,9 @@
     
     
     -  [Struct `AUID`](#0x1_transaction_context_AUID)
    +-  [Struct `EntryFunctionPayload`](#0x1_transaction_context_EntryFunctionPayload)
    +-  [Struct `MultisigPayload`](#0x1_transaction_context_MultisigPayload)
    +-  [Constants](#@Constants_0)
     -  [Function `get_txn_hash`](#0x1_transaction_context_get_txn_hash)
     -  [Function `get_transaction_hash`](#0x1_transaction_context_get_transaction_hash)
     -  [Function `generate_unique_address`](#0x1_transaction_context_generate_unique_address)
    @@ -13,58 +16,684 @@
     -  [Function `get_script_hash`](#0x1_transaction_context_get_script_hash)
     -  [Function `generate_auid`](#0x1_transaction_context_generate_auid)
     -  [Function `auid_address`](#0x1_transaction_context_auid_address)
    --  [Specification](#@Specification_0)
    -    -  [Function `get_txn_hash`](#@Specification_0_get_txn_hash)
    -    -  [Function `get_transaction_hash`](#@Specification_0_get_transaction_hash)
    -    -  [Function `generate_unique_address`](#@Specification_0_generate_unique_address)
    -    -  [Function `generate_auid_address`](#@Specification_0_generate_auid_address)
    -    -  [Function `get_script_hash`](#@Specification_0_get_script_hash)
    +-  [Function `sender`](#0x1_transaction_context_sender)
    +-  [Function `sender_internal`](#0x1_transaction_context_sender_internal)
    +-  [Function `secondary_signers`](#0x1_transaction_context_secondary_signers)
    +-  [Function `secondary_signers_internal`](#0x1_transaction_context_secondary_signers_internal)
    +-  [Function `gas_payer`](#0x1_transaction_context_gas_payer)
    +-  [Function `gas_payer_internal`](#0x1_transaction_context_gas_payer_internal)
    +-  [Function `max_gas_amount`](#0x1_transaction_context_max_gas_amount)
    +-  [Function `max_gas_amount_internal`](#0x1_transaction_context_max_gas_amount_internal)
    +-  [Function `gas_unit_price`](#0x1_transaction_context_gas_unit_price)
    +-  [Function `gas_unit_price_internal`](#0x1_transaction_context_gas_unit_price_internal)
    +-  [Function `chain_id`](#0x1_transaction_context_chain_id)
    +-  [Function `chain_id_internal`](#0x1_transaction_context_chain_id_internal)
    +-  [Function `entry_function_payload`](#0x1_transaction_context_entry_function_payload)
    +-  [Function `entry_function_payload_internal`](#0x1_transaction_context_entry_function_payload_internal)
    +-  [Function `account_address`](#0x1_transaction_context_account_address)
    +-  [Function `module_name`](#0x1_transaction_context_module_name)
    +-  [Function `function_name`](#0x1_transaction_context_function_name)
    +-  [Function `type_arg_names`](#0x1_transaction_context_type_arg_names)
    +-  [Function `args`](#0x1_transaction_context_args)
    +-  [Function `multisig_payload`](#0x1_transaction_context_multisig_payload)
    +-  [Function `multisig_payload_internal`](#0x1_transaction_context_multisig_payload_internal)
    +-  [Function `multisig_address`](#0x1_transaction_context_multisig_address)
    +-  [Function `inner_entry_function_payload`](#0x1_transaction_context_inner_entry_function_payload)
    +-  [Specification](#@Specification_1)
    +    -  [Function `get_txn_hash`](#@Specification_1_get_txn_hash)
    +    -  [Function `get_transaction_hash`](#@Specification_1_get_transaction_hash)
    +    -  [Function `generate_unique_address`](#@Specification_1_generate_unique_address)
    +    -  [Function `generate_auid_address`](#@Specification_1_generate_auid_address)
    +    -  [Function `get_script_hash`](#@Specification_1_get_script_hash)
         -  [High-level Requirements](#high-level-req)
         -  [Module-level Specification](#module-level-spec)
    -    -  [Function `auid_address`](#@Specification_0_auid_address)
    +    -  [Function `auid_address`](#@Specification_1_auid_address)
    +    -  [Function `sender_internal`](#@Specification_1_sender_internal)
    +    -  [Function `secondary_signers_internal`](#@Specification_1_secondary_signers_internal)
    +    -  [Function `gas_payer_internal`](#@Specification_1_gas_payer_internal)
    +    -  [Function `max_gas_amount_internal`](#@Specification_1_max_gas_amount_internal)
    +    -  [Function `gas_unit_price_internal`](#@Specification_1_gas_unit_price_internal)
    +    -  [Function `chain_id_internal`](#@Specification_1_chain_id_internal)
    +    -  [Function `entry_function_payload_internal`](#@Specification_1_entry_function_payload_internal)
    +    -  [Function `multisig_payload_internal`](#@Specification_1_multisig_payload_internal)
    +
    +
    +
    use 0x1::error;
    +use 0x1::features;
    +use 0x1::option;
    +use 0x1::string;
    +
    + + + + + +## Struct `AUID` + +A wrapper denoting aptos unique identifer (AUID) +for storing an address + + +
    struct AUID has drop, store
    +
    + + + +
    +Fields + + +
    +
    +unique_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `EntryFunctionPayload` + +Represents the entry function payload. + + +
    struct EntryFunctionPayload has copy, drop
    +
    + + + +
    +Fields + + +
    +
    +account_address: address +
    +
    + +
    +
    +module_name: string::String +
    +
    + +
    +
    +function_name: string::String +
    +
    + +
    +
    +ty_args_names: vector<string::String> +
    +
    + +
    +
    +args: vector<vector<u8>> +
    +
    + +
    +
    + + +
    + + + +## Struct `MultisigPayload` + +Represents the multisig payload. + + +
    struct MultisigPayload has copy, drop
    +
    + + + +
    +Fields + + +
    +
    +multisig_address: address +
    +
    + +
    +
    +entry_function_payload: option::Option<transaction_context::EntryFunctionPayload> +
    +
    + +
    +
    + + +
    + + + +## Constants + + + + +The transaction context extension feature is not enabled. + + +
    const ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED: u64 = 2;
    +
    + + + + + +Transaction context is only available in the user transaction prologue, execution, or epilogue phases. + + +
    const ETRANSACTION_CONTEXT_NOT_AVAILABLE: u64 = 1;
    +
    + + + + + +## Function `get_txn_hash` + +Returns the transaction hash of the current transaction. + + +
    fun get_txn_hash(): vector<u8>
    +
    + + + +
    +Implementation + + +
    native fun get_txn_hash(): vector<u8>;
    +
    + + + +
    + + + +## Function `get_transaction_hash` + +Returns the transaction hash of the current transaction. +Internally calls the private function get_txn_hash. +This function is created for to feature gate the get_txn_hash function. + + +
    public fun get_transaction_hash(): vector<u8>
    +
    + + + +
    +Implementation + + +
    public fun get_transaction_hash(): vector<u8> {
    +    get_txn_hash()
    +}
    +
    + + + +
    + + + +## Function `generate_unique_address` + +Returns a universally unique identifier (of type address) generated +by hashing the transaction hash of this transaction and a sequence number +specific to this transaction. This function can be called any +number of times inside a single transaction. Each such call increments +the sequence number and generates a new unique address. +Uses Scheme in types/src/transaction/authenticator.rs for domain separation +from other ways of generating unique addresses. + + +
    fun generate_unique_address(): address
    +
    + + + +
    +Implementation + + +
    native fun generate_unique_address(): address;
    +
    + + + +
    + + + +## Function `generate_auid_address` + +Returns a aptos unique identifier. Internally calls +the private function generate_unique_address. This function is +created for to feature gate the generate_unique_address function. + + +
    public fun generate_auid_address(): address
    +
    + + + +
    +Implementation + + +
    public fun generate_auid_address(): address {
    +    generate_unique_address()
    +}
    +
    + + + +
    + + + +## Function `get_script_hash` + +Returns the script hash of the current entry function. + + +
    public fun get_script_hash(): vector<u8>
    +
    + + + +
    +Implementation + + +
    public native fun get_script_hash(): vector<u8>;
    +
    + + + +
    + + + +## Function `generate_auid` + +This method runs generate_unique_address native function and returns +the generated unique address wrapped in the AUID class. + + +
    public fun generate_auid(): transaction_context::AUID
    +
    + + + +
    +Implementation + + +
    public fun generate_auid(): AUID {
    +    return AUID {
    +        unique_address: generate_unique_address()
    +    }
    +}
    +
    + + + +
    + + + +## Function `auid_address` + +Returns the unique address wrapped in the given AUID struct. + + +
    public fun auid_address(auid: &transaction_context::AUID): address
    +
    + + + +
    +Implementation + + +
    public fun auid_address(auid: &AUID): address {
    +    auid.unique_address
    +}
    +
    + + + +
    + + + +## Function `sender` + +Returns the sender's address for the current transaction. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. + + +
    public fun sender(): address
    +
    + + + +
    +Implementation + + +
    public fun sender(): address {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    sender_internal()
    +}
    +
    + + + +
    + + + +## Function `sender_internal` + + + +
    fun sender_internal(): address
    +
    + + + +
    +Implementation + + +
    native fun sender_internal(): address;
    +
    + + + +
    + + + +## Function `secondary_signers` + +Returns the list of the secondary signers for the current transaction. +If the current transaction has no secondary signers, this function returns an empty vector. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. + + +
    public fun secondary_signers(): vector<address>
    +
    + + + +
    +Implementation + + +
    public fun secondary_signers(): vector<address> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    secondary_signers_internal()
    +}
    +
    + + + +
    + + + +## Function `secondary_signers_internal` + + + +
    fun secondary_signers_internal(): vector<address>
    +
    + + + +
    +Implementation + + +
    native fun secondary_signers_internal(): vector<address>;
    +
    + + + +
    + + + +## Function `gas_payer` + +Returns the gas payer address for the current transaction. +It is either the sender's address if no separate gas fee payer is specified for the current transaction, +or the address of the separate gas fee payer if one is specified. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. + + +
    public fun gas_payer(): address
    +
    + + + +
    +Implementation + + +
    public fun gas_payer(): address {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    gas_payer_internal()
    +}
    +
    + + + +
    + + + +## Function `gas_payer_internal` + + + +
    fun gas_payer_internal(): address
    +
    + + + +
    +Implementation + + +
    native fun gas_payer_internal(): address;
    +
    + + + +
    + + + +## Function `max_gas_amount` + +Returns the max gas amount in units which is specified for the current transaction. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. + + +
    public fun max_gas_amount(): u64
    +
    + + + +
    +Implementation + + +
    public fun max_gas_amount(): u64 {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    max_gas_amount_internal()
    +}
    +
    + + + +
    + + + +## Function `max_gas_amount_internal` + + + +
    fun max_gas_amount_internal(): u64
    +
    + + + +
    +Implementation + + +
    native fun max_gas_amount_internal(): u64;
    +
    + + + +
    + + + +## Function `gas_unit_price` + +Returns the gas unit price in Octas which is specified for the current transaction. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. + + +
    public fun gas_unit_price(): u64
    +
    + + + +
    +Implementation + + +
    public fun gas_unit_price(): u64 {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    gas_unit_price_internal()
    +}
    +
    + + + +
    + + + +## Function `gas_unit_price_internal` + + + +
    fun gas_unit_price_internal(): u64
    +
    + + + +
    +Implementation + + +
    native fun gas_unit_price_internal(): u64;
    +
    + + + +
    + + +## Function `chain_id` -
    +Returns the chain ID specified for the current transaction. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. +
    public fun chain_id(): u8
    +
    - -## Struct `AUID` -A wrapper denoting aptos unique identifer (AUID) -for storing an address +
    +Implementation -
    struct AUID has drop, store
    +
    public fun chain_id(): u8 {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    chain_id_internal()
    +}
    +
    + + + +
    + + + +## Function `chain_id_internal` + + + +
    fun chain_id_internal(): u8
     
    -Fields +Implementation -
    -
    -unique_address: address -
    -
    +
    native fun chain_id_internal(): u8;
    +
    -
    -
    - + -## Function `get_txn_hash` +## Function `entry_function_payload` -Return the transaction hash of the current transaction. +Returns the entry function payload if the current transaction has such a payload. Otherwise, return None. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. -
    fun get_txn_hash(): vector<u8>
    +
    public fun entry_function_payload(): option::Option<transaction_context::EntryFunctionPayload>
     
    @@ -73,23 +702,23 @@ Return the transaction hash of the current transaction. Implementation -
    native fun get_txn_hash(): vector<u8>;
    +
    public fun entry_function_payload(): Option<EntryFunctionPayload> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    entry_function_payload_internal()
    +}
     
    - + -## Function `get_transaction_hash` +## Function `entry_function_payload_internal` -Return the transaction hash of the current transaction. -Internally calls the private function get_txn_hash. -This function is created for to feature gate the get_txn_hash function. -
    public fun get_transaction_hash(): vector<u8>
    +
    fun entry_function_payload_internal(): option::Option<transaction_context::EntryFunctionPayload>
     
    @@ -98,8 +727,32 @@ This function is created for to feature gate the get_txn_hash funct Implementation -
    public fun get_transaction_hash(): vector<u8> {
    -    get_txn_hash()
    +
    native fun entry_function_payload_internal(): Option<EntryFunctionPayload>;
    +
    + + + + + + + +## Function `account_address` + +Returns the account address of the entry function payload. + + +
    public fun account_address(payload: &transaction_context::EntryFunctionPayload): address
    +
    + + + +
    +Implementation + + +
    public fun account_address(payload: &EntryFunctionPayload): address {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.account_address
     }
     
    @@ -107,20 +760,14 @@ This function is created for to feature gate the get_txn_hash funct
    - + -## Function `generate_unique_address` +## Function `module_name` -Return a universally unique identifier (of type address) generated -by hashing the transaction hash of this transaction and a sequence number -specific to this transaction. This function can be called any -number of times inside a single transaction. Each such call increments -the sequence number and generates a new unique address. -Uses Scheme in types/src/transaction/authenticator.rs for domain separation -from other ways of generating unique addresses. +Returns the module name of the entry function payload. -
    fun generate_unique_address(): address
    +
    public fun module_name(payload: &transaction_context::EntryFunctionPayload): string::String
     
    @@ -129,23 +776,24 @@ from other ways of generating unique addresses. Implementation -
    native fun generate_unique_address(): address;
    +
    public fun module_name(payload: &EntryFunctionPayload): String {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.module_name
    +}
     
    - + -## Function `generate_auid_address` +## Function `function_name` -Return a aptos unique identifier. Internally calls -the private function generate_unique_address. This function is -created for to feature gate the generate_unique_address function. +Returns the function name of the entry function payload. -
    public fun generate_auid_address(): address
    +
    public fun function_name(payload: &transaction_context::EntryFunctionPayload): string::String
     
    @@ -154,8 +802,9 @@ created for to feature gate the generate_unique_address function. Implementation -
    public fun generate_auid_address(): address {
    -    generate_unique_address()
    +
    public fun function_name(payload: &EntryFunctionPayload): String {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.function_name
     }
     
    @@ -163,14 +812,14 @@ created for to feature gate the generate_unique_address function. - + -## Function `get_script_hash` +## Function `type_arg_names` -Return the script hash of the current entry function. +Returns the type arguments names of the entry function payload. -
    public fun get_script_hash(): vector<u8>
    +
    public fun type_arg_names(payload: &transaction_context::EntryFunctionPayload): vector<string::String>
     
    @@ -179,22 +828,24 @@ Return the script hash of the current entry function. Implementation -
    public native fun get_script_hash(): vector<u8>;
    +
    public fun type_arg_names(payload: &EntryFunctionPayload): vector<String> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.ty_args_names
    +}
     
    - + -## Function `generate_auid` +## Function `args` -This method runs generate_unique_address native function and returns -the generated unique address wrapped in the AUID class. +Returns the arguments of the entry function payload. -
    public fun generate_auid(): transaction_context::AUID
    +
    public fun args(payload: &transaction_context::EntryFunctionPayload): vector<vector<u8>>
     
    @@ -203,10 +854,9 @@ the generated unique address wrapped in the AUID class. Implementation -
    public fun generate_auid(): AUID {
    -    return AUID {
    -        unique_address: generate_unique_address()
    -    }
    +
    public fun args(payload: &EntryFunctionPayload): vector<vector<u8>> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.args
     }
     
    @@ -214,13 +864,15 @@ the generated unique address wrapped in the AUID class. - + -## Function `auid_address` +## Function `multisig_payload` +Returns the multisig payload if the current transaction has such a payload. Otherwise, return None. +This function aborts if called outside of the transaction prologue, execution, or epilogue phases. -
    public fun auid_address(auid: &transaction_context::AUID): address
    +
    public fun multisig_payload(): option::Option<transaction_context::MultisigPayload>
     
    @@ -229,8 +881,83 @@ the generated unique address wrapped in the AUID class. Implementation -
    public fun auid_address(auid: &AUID): address {
    -    auid.unique_address
    +
    public fun multisig_payload(): Option<MultisigPayload> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    multisig_payload_internal()
    +}
    +
    + + + + + + + +## Function `multisig_payload_internal` + + + +
    fun multisig_payload_internal(): option::Option<transaction_context::MultisigPayload>
    +
    + + + +
    +Implementation + + +
    native fun multisig_payload_internal(): Option<MultisigPayload>;
    +
    + + + +
    + + + +## Function `multisig_address` + +Returns the multisig account address of the multisig payload. + + +
    public fun multisig_address(payload: &transaction_context::MultisigPayload): address
    +
    + + + +
    +Implementation + + +
    public fun multisig_address(payload: &MultisigPayload): address {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.multisig_address
    +}
    +
    + + + +
    + + + +## Function `inner_entry_function_payload` + +Returns the inner entry function payload of the multisig payload. + + +
    public fun inner_entry_function_payload(payload: &transaction_context::MultisigPayload): option::Option<transaction_context::EntryFunctionPayload>
    +
    + + + +
    +Implementation + + +
    public fun inner_entry_function_payload(payload: &MultisigPayload): Option<EntryFunctionPayload> {
    +    assert!(features::transaction_context_extension_enabled(), error::invalid_state(ETRANSACTION_CONTEXT_EXTENSION_NOT_ENABLED));
    +    payload.entry_function_payload
     }
     
    @@ -238,12 +965,12 @@ the generated unique address wrapped in the AUID class.
    - + ## Specification - + ### Function `get_txn_hash` @@ -270,7 +997,7 @@ the generated unique address wrapped in the AUID class. - + ### Function `get_transaction_hash` @@ -290,7 +1017,7 @@ the generated unique address wrapped in the AUID class. - + ### Function `generate_unique_address` @@ -316,7 +1043,7 @@ the generated unique address wrapped in the AUID class. - + ### Function `generate_auid_address` @@ -334,7 +1061,7 @@ the generated unique address wrapped in the AUID class. - + ### Function `get_script_hash` @@ -414,7 +1141,7 @@ the generated unique address wrapped in the AUID class. - + ### Function `auid_address` @@ -430,4 +1157,132 @@ the generated unique address wrapped in the AUID class.
    + + + +### Function `sender_internal` + + +
    fun sender_internal(): address
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `secondary_signers_internal` + + +
    fun secondary_signers_internal(): vector<address>
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `gas_payer_internal` + + +
    fun gas_payer_internal(): address
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `max_gas_amount_internal` + + +
    fun max_gas_amount_internal(): u64
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `gas_unit_price_internal` + + +
    fun gas_unit_price_internal(): u64
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `chain_id_internal` + + +
    fun chain_id_internal(): u8
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `entry_function_payload_internal` + + +
    fun entry_function_payload_internal(): option::Option<transaction_context::EntryFunctionPayload>
    +
    + + + + +
    pragma opaque;
    +
    + + + + + +### Function `multisig_payload_internal` + + +
    fun multisig_payload_internal(): option::Option<transaction_context::MultisigPayload>
    +
    + + + + +
    pragma opaque;
    +
    + + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/transaction_fee.md b/aptos-move/framework/aptos-framework/doc/transaction_fee.md index 8138d13a5c697..09e76f34e14db 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_fee.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_fee.md @@ -7,6 +7,7 @@ This module provides an interface to burn or collect and redistribute transactio - [Resource `AptosCoinCapabilities`](#0x1_transaction_fee_AptosCoinCapabilities) +- [Resource `AptosFABurnCapabilities`](#0x1_transaction_fee_AptosFABurnCapabilities) - [Resource `AptosCoinMintCapability`](#0x1_transaction_fee_AptosCoinMintCapability) - [Resource `CollectedFeesPerBlock`](#0x1_transaction_fee_CollectedFeesPerBlock) - [Struct `FeeStatement`](#0x1_transaction_fee_FeeStatement) @@ -21,6 +22,7 @@ This module provides an interface to burn or collect and redistribute transactio - [Function `mint_and_refund`](#0x1_transaction_fee_mint_and_refund) - [Function `collect_fee`](#0x1_transaction_fee_collect_fee) - [Function `store_aptos_coin_burn_cap`](#0x1_transaction_fee_store_aptos_coin_burn_cap) +- [Function `convert_to_aptos_fa_burn_ref`](#0x1_transaction_fee_convert_to_aptos_fa_burn_ref) - [Function `store_aptos_coin_mint_cap`](#0x1_transaction_fee_store_aptos_coin_mint_cap) - [Function `initialize_storage_refund`](#0x1_transaction_fee_initialize_storage_refund) - [Function `emit_fee_statement`](#0x1_transaction_fee_emit_fee_statement) @@ -42,11 +44,15 @@ This module provides an interface to burn or collect and redistribute transactio - [Function `emit_fee_statement`](#@Specification_1_emit_fee_statement) -
    use 0x1::aptos_coin;
    +
    use 0x1::aptos_account;
    +use 0x1::aptos_coin;
     use 0x1::coin;
     use 0x1::error;
     use 0x1::event;
    +use 0x1::features;
    +use 0x1::fungible_asset;
     use 0x1::option;
    +use 0x1::signer;
     use 0x1::stake;
     use 0x1::system_addresses;
     
    @@ -79,6 +85,34 @@ Stores burn capability to burn the gas fees. + + + + +## Resource `AptosFABurnCapabilities` + +Stores burn capability to burn the gas fees. + + +
    struct AptosFABurnCapabilities has key
    +
    + + + +
    +Fields + + +
    +
    +burn_ref: fungible_asset::BurnRef +
    +
    + +
    +
    + +
    @@ -237,6 +271,15 @@ information about collected amounts is already published. + + + + +
    const EFA_GAS_CHARGING_NOT_ENABLED: u64 = 5;
    +
    + + + The burn percentage is out of range [0, 100]. @@ -515,12 +558,24 @@ Burn transaction fees in epilogue. Implementation -
    public(friend) fun burn_fee(account: address, fee: u64) acquires AptosCoinCapabilities {
    -    coin::burn_from<AptosCoin>(
    -        account,
    -        fee,
    -        &borrow_global<AptosCoinCapabilities>(@aptos_framework).burn_cap,
    -    );
    +
    public(friend) fun burn_fee(account: address, fee: u64) acquires AptosFABurnCapabilities, AptosCoinCapabilities {
    +    if (exists<AptosFABurnCapabilities>(@aptos_framework)) {
    +        let burn_ref = &borrow_global<AptosFABurnCapabilities>(@aptos_framework).burn_ref;
    +        aptos_account::burn_from_fungible_store(burn_ref, account, fee);
    +    } else {
    +        let burn_cap = &borrow_global<AptosCoinCapabilities>(@aptos_framework).burn_cap;
    +        if (features::operations_default_to_fa_apt_store_enabled()) {
    +            let (burn_ref, burn_receipt) = coin::get_paired_burn_ref(burn_cap);
    +            aptos_account::burn_from_fungible_store(&burn_ref, account, fee);
    +            coin::return_paired_burn_ref(burn_ref, burn_receipt);
    +        } else {
    +            coin::burn_from<AptosCoin>(
    +                account,
    +                fee,
    +                burn_cap,
    +            );
    +        };
    +    };
     }
     
    @@ -604,7 +659,43 @@ Only called during genesis.
    public(friend) fun store_aptos_coin_burn_cap(aptos_framework: &signer, burn_cap: BurnCapability<AptosCoin>) {
         system_addresses::assert_aptos_framework(aptos_framework);
    -    move_to(aptos_framework, AptosCoinCapabilities { burn_cap })
    +
    +    if (features::operations_default_to_fa_apt_store_enabled()) {
    +        let burn_ref = coin::convert_and_take_paired_burn_ref(burn_cap);
    +        move_to(aptos_framework, AptosFABurnCapabilities { burn_ref });
    +    } else {
    +        move_to(aptos_framework, AptosCoinCapabilities { burn_cap })
    +    }
    +}
    +
    + + + + + + + +## Function `convert_to_aptos_fa_burn_ref` + + + +
    public entry fun convert_to_aptos_fa_burn_ref(aptos_framework: &signer)
    +
    + + + +
    +Implementation + + +
    public entry fun convert_to_aptos_fa_burn_ref(aptos_framework: &signer) acquires AptosCoinCapabilities {
    +    assert!(features::operations_default_to_fa_apt_store_enabled(), EFA_GAS_CHARGING_NOT_ENABLED);
    +    system_addresses::assert_aptos_framework(aptos_framework);
    +    let AptosCoinCapabilities {
    +        burn_cap,
    +    } = move_from<AptosCoinCapabilities>(signer::address_of(aptos_framework));
    +    let burn_ref = coin::convert_and_take_paired_burn_ref(burn_cap);
    +    move_to(aptos_framework, AptosFABurnCapabilities { burn_ref });
     }
     
    @@ -745,7 +836,7 @@ Only called during genesis. 6 -The presence of the resource, indicating collected fees per block under the Aptos framework account, Registering a block proposer. Processing collected fees. +The presence of the resource, indicating collected fees per block under the Aptos framework account, is a prerequisite for the successful execution of the following functionalities: Upgrading burn percentage. Registering a block proposer. Processing collected fees. Low The functions: upgrade_burn_percentage, register_proposer_for_fee_collection, and process_collected_fees all ensure that the CollectedFeesPerBlock resource exists under aptos_framework by calling the is_fees_collection_enabled method, which returns a boolean value confirming if the resource exists or not. Formally verified via register_proposer_for_fee_collection, process_collected_fees, and upgrade_burn_percentage. @@ -761,10 +852,10 @@ Only called during genesis. ### Module-level Specification -
    pragma verify = true;
    +
    pragma verify = false;
     pragma aborts_if_is_strict;
     // This enforces high-level requirement 1:
    -invariant [suspendable] chain_status::is_operating() ==> exists<AptosCoinCapabilities>(@aptos_framework);
    +invariant [suspendable] chain_status::is_operating() ==> exists<AptosCoinCapabilities>(@aptos_framework) || exists<AptosFABurnCapabilities>(@aptos_framework);
     
    @@ -992,13 +1083,13 @@ Only called during genesis. AptosCoinCapabilities should be exists. -
    aborts_if !exists<AptosCoinCapabilities>(@aptos_framework);
    +
    pragma verify = false;
    +aborts_if !exists<AptosCoinCapabilities>(@aptos_framework);
     let account_addr = account;
     let amount = fee;
     let aptos_addr = type_info::type_of<AptosCoin>().account_address;
     let coin_store = global<CoinStore<AptosCoin>>(account_addr);
     let post post_coin_store = global<CoinStore<AptosCoin>>(account_addr);
    -modifies global<CoinStore<AptosCoin>>(account_addr);
     aborts_if amount != 0 && !(exists<CoinInfo<AptosCoin>>(aptos_addr)
         && exists<CoinStore<AptosCoin>>(account_addr));
     aborts_if coin_store.coin.value < amount;
    @@ -1031,13 +1122,11 @@ Only called during genesis.
     
     
     
    -
    pragma opaque;
    +
    pragma verify = false;
     let aptos_addr = type_info::type_of<AptosCoin>().account_address;
    -modifies global<CoinInfo<AptosCoin>>(aptos_addr);
     aborts_if (refund != 0) && !exists<CoinInfo<AptosCoin>>(aptos_addr);
     include coin::CoinAddAbortsIf<AptosCoin> { amount: refund };
     aborts_if !exists<CoinStore<AptosCoin>>(account);
    -modifies global<CoinStore<AptosCoin>>(account);
     aborts_if !exists<AptosCoinMintCapability>(@aptos_framework);
     let supply = coin::supply<AptosCoin>;
     let post post_supply = coin::supply<AptosCoin>;
    @@ -1058,7 +1147,8 @@ Only called during genesis.
     
     
     
    -
    let collected_fees = global<CollectedFeesPerBlock>(@aptos_framework).amount;
    +
    pragma verify = false;
    +let collected_fees = global<CollectedFeesPerBlock>(@aptos_framework).amount;
     let aggr = collected_fees.value;
     let coin_store = global<coin::CoinStore<AptosCoin>>(account);
     aborts_if !exists<CollectedFeesPerBlock>(@aptos_framework);
    @@ -1091,10 +1181,12 @@ Ensure caller is admin.
     Aborts if AptosCoinCapabilities already exists.
     
     
    -
    let addr = signer::address_of(aptos_framework);
    +
    pragma verify = false;
    +let addr = signer::address_of(aptos_framework);
     aborts_if !system_addresses::is_aptos_framework_address(addr);
    +aborts_if exists<AptosFABurnCapabilities>(addr);
     aborts_if exists<AptosCoinCapabilities>(addr);
    -ensures exists<AptosCoinCapabilities>(addr);
    +ensures exists<AptosFABurnCapabilities>(addr) || exists<AptosCoinCapabilities>(addr);
     
    diff --git a/aptos-move/framework/aptos-framework/doc/transaction_validation.md b/aptos-move/framework/aptos-framework/doc/transaction_validation.md index 66e6f1746abb0..8f1a41d972c96 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_validation.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_validation.md @@ -29,6 +29,7 @@
    use 0x1::account;
    +use 0x1::aptos_account;
     use 0x1::aptos_coin;
     use 0x1::bcs;
     use 0x1::chain_id;
    @@ -297,9 +298,9 @@ Only called during genesis to initialize system resources for this module.
     
         if (
             transaction_sender == gas_payer
    -        || account::exists_at(transaction_sender)
    -        || !features::sponsored_automatic_account_creation_enabled()
    -        || txn_sequence_number > 0
    +            || account::exists_at(transaction_sender)
    +            || !features::sponsored_automatic_account_creation_enabled()
    +            || txn_sequence_number > 0
         ) {
             assert!(account::exists_at(transaction_sender), error::invalid_argument(PROLOGUE_EACCOUNT_DOES_NOT_EXIST));
             assert!(
    @@ -337,12 +338,18 @@ Only called during genesis to initialize system resources for this module.
         };
     
         let max_transaction_fee = txn_gas_price * txn_max_gas_units;
    -    assert!(
    -        coin::is_account_registered<AptosCoin>(gas_payer),
    -        error::invalid_argument(PROLOGUE_ECANT_PAY_GAS_DEPOSIT),
    -    );
    -    let balance = coin::balance<AptosCoin>(gas_payer);
    -    assert!(balance >= max_transaction_fee, error::invalid_argument(PROLOGUE_ECANT_PAY_GAS_DEPOSIT));
    +
    +    if (features::operations_default_to_fa_apt_store_enabled()) {
    +        assert!(
    +            aptos_account::is_fungible_balance_at_least(gas_payer, max_transaction_fee),
    +            error::invalid_argument(PROLOGUE_ECANT_PAY_GAS_DEPOSIT)
    +        );
    +    } else {
    +        assert!(
    +            coin::is_balance_at_least<AptosCoin>(gas_payer, max_transaction_fee),
    +            error::invalid_argument(PROLOGUE_ECANT_PAY_GAS_DEPOSIT)
    +        );
    +    }
     }
     
    @@ -376,7 +383,16 @@ Only called during genesis to initialize system resources for this module. _script_hash: vector<u8>, ) { let gas_payer = signer::address_of(&sender); - prologue_common(sender, gas_payer, txn_sequence_number, txn_public_key, txn_gas_price, txn_max_gas_units, txn_expiration_time, chain_id) + prologue_common( + sender, + gas_payer, + txn_sequence_number, + txn_public_key, + txn_gas_price, + txn_max_gas_units, + txn_expiration_time, + chain_id + ) }
    @@ -460,8 +476,8 @@ Only called during genesis to initialize system resources for this module. invariant i <= num_secondary_signers; invariant forall j in 0..i: account::exists_at(secondary_signer_addresses[j]) - && secondary_signer_public_key_hashes[j] - == account::get_authentication_key(secondary_signer_addresses[j]); + && secondary_signer_public_key_hashes[j] + == account::get_authentication_key(secondary_signer_addresses[j]); }; (i < num_secondary_signers) }) { @@ -599,12 +615,20 @@ Called by the Adapter error::out_of_range(EOUT_OF_GAS) ); let transaction_fee_amount = txn_gas_price * gas_used; + // it's important to maintain the error code consistent with vm // to do failed transaction cleanup. - assert!( - coin::balance<AptosCoin>(gas_payer) >= transaction_fee_amount, - error::out_of_range(PROLOGUE_ECANT_PAY_GAS_DEPOSIT), - ); + if (features::operations_default_to_fa_apt_store_enabled()) { + assert!( + aptos_account::is_fungible_balance_at_least(gas_payer, transaction_fee_amount), + error::out_of_range(PROLOGUE_ECANT_PAY_GAS_DEPOSIT), + ); + } else { + assert!( + coin::is_balance_at_least<AptosCoin>(gas_payer, transaction_fee_amount), + error::out_of_range(PROLOGUE_ECANT_PAY_GAS_DEPOSIT), + ); + }; let amount_to_burn = if (features::collect_and_distribute_gas_fees()) { // TODO(gas): We might want to distinguish the refundable part of the charge and burn it or track @@ -675,7 +699,7 @@ Called by the Adapter 3 After successful execution, base the transaction fee on the configuration set by the features library. High -The epilogue function collects the transaction fee for either redistribution or burning based on +The epilogue function collects the transaction fee for either redistribution or burning based on the feature::collect_and_distribute_gas_fees result. Formally Verified via epilogue. Moreover, the native transaction validation patterns have been manually audited. @@ -738,14 +762,14 @@ Give some constraints that may abort according to the conditions. let transaction_sender = signer::address_of(sender); aborts_if ( !features::spec_is_enabled(features::SPONSORED_AUTOMATIC_ACCOUNT_CREATION) - || account::exists_at(transaction_sender) - || transaction_sender == gas_payer - || txn_sequence_number > 0 + || account::exists_at(transaction_sender) + || transaction_sender == gas_payer + || txn_sequence_number > 0 ) && ( !(txn_sequence_number >= global<Account>(transaction_sender).sequence_number) - || !(txn_authentication_key == global<Account>(transaction_sender).authentication_key) - || !account::exists_at(transaction_sender) - || !(txn_sequence_number == global<Account>(transaction_sender).sequence_number) + || !(txn_authentication_key == global<Account>(transaction_sender).authentication_key) + || !account::exists_at(transaction_sender) + || !(txn_sequence_number == global<Account>(transaction_sender).sequence_number) ); aborts_if features::spec_is_enabled(features::SPONSORED_AUTOMATIC_ACCOUNT_CREATION) && transaction_sender != gas_payer @@ -774,7 +798,8 @@ Give some constraints that may abort according to the conditions. -
    include PrologueCommonAbortsIf;
    +
    pragma verify = false;
    +include PrologueCommonAbortsIf;
     
    @@ -790,7 +815,8 @@ Give some constraints that may abort according to the conditions. -
    include PrologueCommonAbortsIf {
    +
    pragma verify = false;
    +include PrologueCommonAbortsIf {
         gas_payer: signer::address_of(sender),
         txn_authentication_key: txn_public_key
     };
    @@ -815,7 +841,7 @@ Give some constraints that may abort according to the conditions.
         ensures forall i in 0..num_secondary_signers:
             account::exists_at(secondary_signer_addresses[i])
                 && secondary_signer_public_key_hashes[i] ==
    -                account::get_authentication_key(secondary_signer_addresses[i]);
    +            account::get_authentication_key(secondary_signer_addresses[i]);
     }
     
    @@ -836,6 +862,7 @@ not equal the number of singers.
    pragma verify_duration_estimate = 120;
     let gas_payer = signer::address_of(sender);
    +pragma verify = false;
     include PrologueCommonAbortsIf {
         gas_payer,
         txn_sequence_number,
    @@ -912,7 +939,8 @@ Abort according to the conditions.
     Skip transaction_fee::burn_fee verification.
     
     
    -
    include EpilogueGasPayerAbortsIf { gas_payer: signer::address_of(account) };
    +
    pragma verify = false;
    +include EpilogueGasPayerAbortsIf { gas_payer: signer::address_of(account) };
     
    @@ -931,7 +959,8 @@ Abort according to the conditions. Skip transaction_fee::burn_fee verification. -
    include EpilogueGasPayerAbortsIf;
    +
    pragma verify = false;
    +include EpilogueGasPayerAbortsIf;
     
    @@ -952,15 +981,11 @@ Skip transaction_fee::burn_fee verification. aborts_if !(txn_gas_price * gas_used <= MAX_U64); let transaction_fee_amount = txn_gas_price * gas_used; let addr = signer::address_of(account); - let pre_balance = global<coin::CoinStore<AptosCoin>>(gas_payer).coin.value; - let post balance = global<coin::CoinStore<AptosCoin>>(gas_payer).coin.value; let pre_account = global<account::Account>(addr); let post account = global<account::Account>(addr); aborts_if !exists<CoinStore<AptosCoin>>(gas_payer); aborts_if !exists<Account>(addr); aborts_if !(global<Account>(addr).sequence_number < MAX_U64); - aborts_if pre_balance < transaction_fee_amount; - ensures balance == pre_balance - transaction_fee_amount + storage_fee_refunded; ensures account.sequence_number == pre_account.sequence_number + 1; let collect_fee_enabled = features::spec_is_enabled(features::COLLECT_AND_DISTRIBUTE_GAS_FEES); let collected_fees = global<CollectedFeesPerBlock>(@aptos_framework).amount; @@ -970,7 +995,7 @@ Skip transaction_fee::burn_fee verification. // This enforces high-level requirement 3: aborts_if collect_fee_enabled && !exists<CollectedFeesPerBlock>(@aptos_framework); aborts_if collect_fee_enabled && transaction_fee_amount > 0 && aggr_val + transaction_fee_amount > aggr_lim; - let amount_to_burn= if (collect_fee_enabled) { + let amount_to_burn = if (collect_fee_enabled) { 0 } else { transaction_fee_amount - storage_fee_refunded diff --git a/aptos-move/framework/aptos-framework/doc/version.md b/aptos-move/framework/aptos-framework/doc/version.md index d7b79da3e67cd..c201f455c86f1 100644 --- a/aptos-move/framework/aptos-framework/doc/version.md +++ b/aptos-move/framework/aptos-framework/doc/version.md @@ -221,7 +221,7 @@ Example usage: Only used in reconfigurations to apply the pending Version, if there is any. -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -230,9 +230,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires Version { +
    public(friend) fun on_new_epoch(framework: &signer) acquires Version {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<Version>()) {
    -        *borrow_global_mut<Version>(@aptos_framework) = config_buffer::extract<Version>();
    +        let new_value = config_buffer::extract<Version>();
    +        if (exists<Version>(@aptos_framework)) {
    +            *borrow_global_mut<Version>(@aptos_framework) = new_value;
    +        } else {
    +            move_to(framework, new_value);
    +        }
         }
     }
     
    @@ -354,7 +360,7 @@ Abort if resource already exists in @aptos_framwork when initializi
    pragma verify_duration_estimate = 120;
     include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply;
     include staking_config::StakingRewardsConfigRequirement;
    -requires chain_status::is_operating();
    +requires chain_status::is_genesis();
     requires timestamp::spec_now_microseconds() >= reconfiguration::last_reconfiguration_time();
     requires exists<stake::ValidatorFees>(@aptos_framework);
     requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
    @@ -392,13 +398,15 @@ Abort if resource already exists in @aptos_framwork when initializi
     ### Function `on_new_epoch`
     
     
    -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    -
    include config_buffer::OnNewEpochAbortsIf<Version>;
    +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<Version>;
    +aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/vesting.md b/aptos-move/framework/aptos-framework/doc/vesting.md index 64d7d85cee2c0..d2b59512b637d 100644 --- a/aptos-move/framework/aptos-framework/doc/vesting.md +++ b/aptos-move/framework/aptos-framework/doc/vesting.md @@ -45,6 +45,16 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting - [Resource `VestingContract`](#0x1_vesting_VestingContract) - [Resource `VestingAccountManagement`](#0x1_vesting_VestingAccountManagement) - [Resource `AdminStore`](#0x1_vesting_AdminStore) +- [Struct `CreateVestingContract`](#0x1_vesting_CreateVestingContract) +- [Struct `UpdateOperator`](#0x1_vesting_UpdateOperator) +- [Struct `UpdateVoter`](#0x1_vesting_UpdateVoter) +- [Struct `ResetLockup`](#0x1_vesting_ResetLockup) +- [Struct `SetBeneficiary`](#0x1_vesting_SetBeneficiary) +- [Struct `UnlockRewards`](#0x1_vesting_UnlockRewards) +- [Struct `Vest`](#0x1_vesting_Vest) +- [Struct `Distribute`](#0x1_vesting_Distribute) +- [Struct `Terminate`](#0x1_vesting_Terminate) +- [Struct `AdminWithdraw`](#0x1_vesting_AdminWithdraw) - [Struct `CreateVestingContractEvent`](#0x1_vesting_CreateVestingContractEvent) - [Struct `UpdateOperatorEvent`](#0x1_vesting_UpdateOperatorEvent) - [Struct `UpdateVoterEvent`](#0x1_vesting_UpdateVoterEvent) @@ -156,6 +166,7 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting use 0x1::coin; use 0x1::error; use 0x1::event; +use 0x1::features; use 0x1::fixed_point32; use 0x1::math64; use 0x1::pool_u64; @@ -454,6 +465,490 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting +
    + + + +## Struct `CreateVestingContract` + + + +
    #[event]
    +struct CreateVestingContract has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    +grant_amount: u64 +
    +
    + +
    +
    +withdrawal_address: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +commission_percentage: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateOperator` + + + +
    #[event]
    +struct UpdateOperator has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +old_operator: address +
    +
    + +
    +
    +new_operator: address +
    +
    + +
    +
    +commission_percentage: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateVoter` + + + +
    #[event]
    +struct UpdateVoter has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +old_voter: address +
    +
    + +
    +
    +new_voter: address +
    +
    + +
    +
    + + +
    + + + +## Struct `ResetLockup` + + + +
    #[event]
    +struct ResetLockup has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +new_lockup_expiration_secs: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `SetBeneficiary` + + + +
    #[event]
    +struct SetBeneficiary has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +shareholder: address +
    +
    + +
    +
    +old_beneficiary: address +
    +
    + +
    +
    +new_beneficiary: address +
    +
    + +
    +
    + + +
    + + + +## Struct `UnlockRewards` + + + +
    #[event]
    +struct UnlockRewards has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `Vest` + + + +
    #[event]
    +struct Vest has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +staking_pool_address: address +
    +
    + +
    +
    +period_vested: u64 +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `Distribute` + + + +
    #[event]
    +struct Distribute has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `Terminate` + + + +
    #[event]
    +struct Terminate has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `AdminWithdraw` + + + +
    #[event]
    +struct AdminWithdraw has drop, store
    +
    + + + +
    +Fields + + +
    +
    +admin: address +
    +
    + +
    +
    +vesting_contract_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + +
    @@ -1569,7 +2064,10 @@ This returns 0x0 if no shareholder is found for the given beneficiary / the addr Implementation -
    public fun shareholder(vesting_contract_address: address, shareholder_or_beneficiary: address): address acquires VestingContract {
    +
    public fun shareholder(
    +    vesting_contract_address: address,
    +    shareholder_or_beneficiary: address
    +): address acquires VestingContract {
         assert_active_vesting_contract(vesting_contract_address);
     
         let shareholders = &shareholders(vesting_contract_address);
    @@ -1713,6 +2211,19 @@ Create a vesting contract with a given configurations.
         let contract_address = signer::address_of(&contract_signer);
         let admin_store = borrow_global_mut<AdminStore>(admin_address);
         vector::push_back(&mut admin_store.vesting_contracts, contract_address);
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            CreateVestingContract {
    +                operator,
    +                voter,
    +                withdrawal_address,
    +                grant_amount,
    +                vesting_contract_address: contract_address,
    +                staking_pool_address: pool_address,
    +                commission_percentage,
    +            },
    +        );
    +    };
         emit_event(
             &mut admin_store.create_events,
             CreateVestingContractEvent {
    @@ -1871,6 +2382,17 @@ Unlock any vested portion of the grant.
         vesting_schedule.last_vested_period = next_period_to_vest;
         unlock_stake(vesting_contract, vested_amount);
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            Vest {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                staking_pool_address: vesting_contract.staking.pool_address,
    +                period_vested: next_period_to_vest,
    +                amount: vested_amount,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.vest_events,
             VestEvent {
    @@ -1947,7 +2469,7 @@ Distribute any withdrawable stake from the stake pool.
             return
         };
     
    -    // Distribute coins to all shareholders in the vesting contract.
    +    // Distribute coins to all shareholders in the vesting contract.
         let grant_pool = &vesting_contract.grant_pool;
         let shareholders = &pool_u64::shareholders(grant_pool);
         vector::for_each_ref(shareholders, |shareholder| {
    @@ -1966,6 +2488,15 @@ Distribute any withdrawable stake from the stake pool.
             coin::destroy_zero(coins);
         };
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            Distribute {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                amount: total_distribution_amount,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.distribute_events,
             DistributeEvent {
    @@ -2032,7 +2563,7 @@ Terminate the vesting contract and send all funds back to the withdrawal address
     
    public entry fun terminate_vesting_contract(admin: &signer, contract_address: address) acquires VestingContract {
         assert_active_vesting_contract(contract_address);
     
    -    // Distribute all withdrawable coins, which should have been from previous rewards withdrawal or vest.
    +    // Distribute all withdrawable coins, which should have been from previous rewards withdrawal or vest.
         distribute(contract_address);
     
         let vesting_contract = borrow_global_mut<VestingContract>(contract_address);
    @@ -2045,6 +2576,14 @@ Terminate the vesting contract and send all funds back to the withdrawal address
         vesting_contract.remaining_grant = 0;
         unlock_stake(vesting_contract, active_stake);
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            Terminate {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.terminate_events,
             TerminateEvent {
    @@ -2078,7 +2617,10 @@ has already been terminated.
     
     
    public entry fun admin_withdraw(admin: &signer, contract_address: address) acquires VestingContract {
         let vesting_contract = borrow_global<VestingContract>(contract_address);
    -    assert!(vesting_contract.state == VESTING_POOL_TERMINATED, error::invalid_state(EVESTING_CONTRACT_STILL_ACTIVE));
    +    assert!(
    +        vesting_contract.state == VESTING_POOL_TERMINATED,
    +        error::invalid_state(EVESTING_CONTRACT_STILL_ACTIVE)
    +    );
     
         let vesting_contract = borrow_global_mut<VestingContract>(contract_address);
         verify_admin(admin, vesting_contract);
    @@ -2090,6 +2632,15 @@ has already been terminated.
         };
         aptos_account::deposit_coins(vesting_contract.withdrawal_address, coins);
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            AdminWithdraw {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                amount,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.admin_withdraw_events,
             AdminWithdrawEvent {
    @@ -2134,6 +2685,18 @@ has already been terminated.
         vesting_contract.staking.operator = new_operator;
         vesting_contract.staking.commission_percentage = commission_percentage;
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            UpdateOperator {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                staking_pool_address: vesting_contract.staking.pool_address,
    +                old_operator,
    +                new_operator,
    +                commission_percentage,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.update_operator_events,
             UpdateOperatorEvent {
    @@ -2243,6 +2806,17 @@ has already been terminated.
         staking_contract::update_voter(contract_signer, vesting_contract.staking.operator, new_voter);
         vesting_contract.staking.voter = new_voter;
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            UpdateVoter {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                staking_pool_address: vesting_contract.staking.pool_address,
    +                old_voter,
    +                new_voter,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.update_voter_events,
             UpdateVoterEvent {
    @@ -2284,6 +2858,16 @@ has already been terminated.
         let contract_signer = &get_vesting_account_signer_internal(vesting_contract);
         staking_contract::reset_lockup(contract_signer, vesting_contract.staking.operator);
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            ResetLockup {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                staking_pool_address: vesting_contract.staking.pool_address,
    +                new_lockup_expiration_secs: stake::get_lockup_secs(vesting_contract.staking.pool_address),
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.reset_lockup_events,
             ResetLockupEvent {
    @@ -2337,6 +2921,17 @@ has already been terminated.
             simple_map::add(beneficiaries, shareholder, new_beneficiary);
         };
     
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            SetBeneficiary {
    +                admin: vesting_contract.admin,
    +                vesting_contract_address: contract_address,
    +                shareholder,
    +                old_beneficiary,
    +                new_beneficiary,
    +            },
    +        );
    +    };
         emit_event(
             &mut vesting_contract.set_beneficiary_events,
             SetBeneficiaryEvent {
    @@ -2802,7 +3397,7 @@ This address should be deterministic for the same admin and vesting contract cre
     2
     The vesting pool should not exceed a maximum of 30 shareholders.
     Medium
    -The maximum number of shareholders a vesting pool can support is stored as a constant in
    +The maximum number of shareholders a vesting pool can support is stored as a constant in MAXIMUM_SHAREHOLDERS which is passed to the pool_u64::create function.
     Formally verified via a global invariant.
     
     
    @@ -2818,7 +3413,7 @@ This address should be deterministic for the same admin and vesting contract cre
     4
     The shareholders should be able to start vesting only after the vesting cliff and the first vesting period have transpired.
     High
    -The end of the vesting cliff is stored under VestingContract.vesting_schedule.start_timestamp_secs. period.
    +The end of the vesting cliff is stored under VestingContract.vesting_schedule.start_timestamp_secs. The vest function always checks that timestamp::now_seconds is greater or equal to the end of the vesting cliff period.
     Audited the check for the end of vesting cliff: vest module.
     
     
    @@ -2834,7 +3429,7 @@ This address should be deterministic for the same admin and vesting contract cre
     6
     A new vesting schedule should not be allowed to start vesting in the past or to supply an empty schedule or for the period duration to be zero.
     High
    -The create_vesting_schedule function ensures that the length of the schedule vector is greater than 0, that the period duration is greater than 0 and that the start_timestamp_secs is greater or equal to
    +The create_vesting_schedule function ensures that the length of the schedule vector is greater than 0, that the period duration is greater than 0 and that the start_timestamp_secs is greater or equal to timestamp::now_seconds.
     Formally verified via create_vesting_schedule.
     
     
    @@ -2890,7 +3485,8 @@ This address should be deterministic for the same admin and vesting contract cre
     
    pragma verify = true;
     pragma aborts_if_is_strict;
     // This enforces high-level requirement 2:
    -invariant forall pool: Pool: len(pool.shareholders) <= MAXIMUM_SHAREHOLDERS;
    +invariant forall a: address where exists<VestingContract>(a):
    +    global<VestingContract>(a).grant_pool.shareholders_limit <= MAXIMUM_SHAREHOLDERS;
     
    @@ -3336,19 +3932,6 @@ This address should be deterministic for the same admin and vesting contract cre - - - - -
    schema PreconditionAbortsIf {
    -    contract_addresses: vector<address>;
    -    requires forall i in 0..len(contract_addresses): simple_map::spec_get(global<staking_contract::Store>(contract_addresses[i]).staking_contracts, global<VestingContract>(contract_addresses[i]).staking.operator).commission_percentage >= 0
    -        && simple_map::spec_get(global<staking_contract::Store>(contract_addresses[i]).staking_contracts, global<VestingContract>(contract_addresses[i]).staking.operator).commission_percentage <= 100;
    -}
    -
    - - - ### Function `distribute` @@ -3493,7 +4076,8 @@ This address should be deterministic for the same admin and vesting contract cre -
    include VerifyAdminAbortsIf;
    +
    pragma verify_duration_estimate = 300;
    +include VerifyAdminAbortsIf;
     let vesting_contract = global<VestingContract>(contract_address);
     let operator = vesting_contract.staking.operator;
     let staker = vesting_contract.signer_cap.account;
    @@ -3513,7 +4097,8 @@ This address should be deterministic for the same admin and vesting contract cre
     
     
     
    -
    aborts_if !exists<VestingContract>(contract_address);
    +
    pragma verify_duration_estimate = 300;
    +aborts_if !exists<VestingContract>(contract_address);
     let vesting_contract = global<VestingContract>(contract_address);
     aborts_if signer::address_of(admin) != vesting_contract.admin;
     let operator = vesting_contract.staking.operator;
    @@ -3539,8 +4124,10 @@ This address should be deterministic for the same admin and vesting contract cre
     
     
     
    -
    aborts_if !account::exists_at(new_beneficiary);
    -aborts_if !coin::is_account_registered<AptosCoin>(new_beneficiary);
    +
    pragma verify_duration_estimate = 300;
    +pragma aborts_if_is_partial;
    +aborts_if !account::exists_at(new_beneficiary);
    +aborts_if !coin::spec_is_account_registered<AptosCoin>(new_beneficiary);
     include VerifyAdminAbortsIf;
     let post vesting_contract = global<VestingContract>(contract_address);
     ensures simple_map::spec_contains_key(vesting_contract.beneficiaries,shareholder);
    diff --git a/aptos-move/framework/aptos-framework/doc/voting.md b/aptos-move/framework/aptos-framework/doc/voting.md
    index 6fb71c9687c5c..dfea6bb54d7ae 100644
    --- a/aptos-move/framework/aptos-framework/doc/voting.md
    +++ b/aptos-move/framework/aptos-framework/doc/voting.md
    @@ -29,10 +29,13 @@ the resolution process.
     -  [Struct `Proposal`](#0x1_voting_Proposal)
     -  [Resource `VotingForum`](#0x1_voting_VotingForum)
     -  [Struct `VotingEvents`](#0x1_voting_VotingEvents)
    +-  [Struct `CreateProposal`](#0x1_voting_CreateProposal)
    +-  [Struct `RegisterForum`](#0x1_voting_RegisterForum)
    +-  [Struct `Vote`](#0x1_voting_Vote)
    +-  [Struct `ResolveProposal`](#0x1_voting_ResolveProposal)
     -  [Struct `CreateProposalEvent`](#0x1_voting_CreateProposalEvent)
     -  [Struct `RegisterForumEvent`](#0x1_voting_RegisterForumEvent)
     -  [Struct `VoteEvent`](#0x1_voting_VoteEvent)
    --  [Struct `ResolveProposal`](#0x1_voting_ResolveProposal)
     -  [Constants](#@Constants_0)
     -  [Function `register`](#0x1_voting_register)
     -  [Function `create_proposal`](#0x1_voting_create_proposal)
    @@ -92,6 +95,7 @@ the resolution process.
     use 0x1::bcs;
     use 0x1::error;
     use 0x1::event;
    +use 0x1::features;
     use 0x1::from_bcs;
     use 0x1::option;
     use 0x1::signer;
    @@ -296,13 +300,14 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru
     
     
     
    -
    +
     
    -## Struct `CreateProposalEvent`
    +## Struct `CreateProposal`
     
     
     
    -
    struct CreateProposalEvent has drop, store
    +
    #[event]
    +struct CreateProposal has drop, store
     
    @@ -353,13 +358,14 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru - + -## Struct `RegisterForumEvent` +## Struct `RegisterForum` -
    struct RegisterForumEvent has drop, store
    +
    #[event]
    +struct RegisterForum has drop, store
     
    @@ -386,13 +392,14 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru - + -## Struct `VoteEvent` +## Struct `Vote` -
    struct VoteEvent has drop, store
    +
    #[event]
    +struct Vote has drop, store
     
    @@ -425,7 +432,8 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru -
    struct ResolveProposal has drop, store
    +
    #[event]
    +struct ResolveProposal has drop, store
     
    @@ -462,6 +470,129 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru + + + + +## Struct `CreateProposalEvent` + + + +
    struct CreateProposalEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +early_resolution_vote_threshold: option::Option<u128> +
    +
    + +
    +
    +execution_hash: vector<u8> +
    +
    + +
    +
    +expiration_secs: u64 +
    +
    + +
    +
    +metadata: simple_map::SimpleMap<string::String, vector<u8>> +
    +
    + +
    +
    +min_vote_threshold: u128 +
    +
    + +
    +
    + + +
    + + + +## Struct `RegisterForumEvent` + + + +
    struct RegisterForumEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +hosting_account: address +
    +
    + +
    +
    +proposal_type_info: type_info::TypeInfo +
    +
    + +
    +
    + + +
    + + + +## Struct `VoteEvent` + + + +
    struct VoteEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +num_votes: u64 +
    +
    + +
    +
    + +
    @@ -679,6 +810,14 @@ Key used to track the resolvable time in the proposal's metadata. } }; + if (std::features::module_event_migration_enabled()) { + event::emit( + RegisterForum { + hosting_account: addr, + proposal_type_info: type_info::type_of<ProposalType>(), + }, + ); + }; event::emit_event<RegisterForumEvent>( &mut voting_forum.events.register_forum_events, RegisterForumEvent { @@ -811,8 +950,8 @@ resolve this proposal. // This value is by default false. We turn this value to true when we start executing the multi-step proposal. This value // will be used to disable further voting after we started executing the multi-step proposal. simple_map::add(&mut metadata, is_multi_step_in_execution_key, to_bytes(&false)); - // If the proposal is a single-step proposal, we check if the metadata passed by the client has the IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY key. - // If they have the key, we will remove it, because a single-step proposal that doesn't need this key. + // If the proposal is a single-step proposal, we check if the metadata passed by the client has the IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY key. + // If they have the key, we will remove it, because a single-step proposal that doesn't need this key. } else if (simple_map::contains_key(&mut metadata, &is_multi_step_in_execution_key)) { simple_map::remove(&mut metadata, &is_multi_step_in_execution_key); }; @@ -832,6 +971,18 @@ resolve this proposal. resolution_time_secs: 0, }); + if (std::features::module_event_migration_enabled()) { + event::emit( + CreateProposal { + proposal_id, + early_resolution_vote_threshold, + execution_hash, + expiration_secs, + metadata, + min_vote_threshold, + }, + ); + }; event::emit_event<CreateProposalEvent>( &mut voting_forum.events.create_proposal_events, CreateProposalEvent { @@ -893,8 +1044,10 @@ This guarantees that voting eligibility and voting power are controlled by the r assert!(!proposal.is_resolved, error::invalid_state(EPROPOSAL_ALREADY_RESOLVED)); // Assert this proposal is single-step, or if the proposal is multi-step, it is not in execution yet. assert!(!simple_map::contains_key(&proposal.metadata, &utf8(IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY)) - || *simple_map::borrow(&proposal.metadata, &utf8(IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY)) == to_bytes(&false), - error::invalid_state(EMULTI_STEP_PROPOSAL_IN_EXECUTION)); + || *simple_map::borrow(&proposal.metadata, &utf8(IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY)) == to_bytes( + &false + ), + error::invalid_state(EMULTI_STEP_PROPOSAL_IN_EXECUTION)); if (should_pass) { proposal.yes_votes = proposal.yes_votes + (num_votes as u128); @@ -911,6 +1064,9 @@ This guarantees that voting eligibility and voting power are controlled by the r simple_map::add(&mut proposal.metadata, key, timestamp_secs_bytes); }; + if (std::features::module_event_migration_enabled()) { + event::emit(Vote { proposal_id, num_votes }); + }; event::emit_event<VoteEvent>( &mut voting_forum.events.vote_events, VoteEvent { proposal_id, num_votes }, @@ -999,13 +1155,26 @@ there are more yes votes than no. If either of these conditions is not met, this let has_multi_step_key = simple_map::contains_key(&proposal.metadata, &multi_step_key); if (has_multi_step_key) { let is_multi_step_proposal = from_bcs::to_bool(*simple_map::borrow(&proposal.metadata, &multi_step_key)); - assert!(!is_multi_step_proposal, error::permission_denied(EMULTI_STEP_PROPOSAL_CANNOT_USE_SINGLE_STEP_RESOLVE_FUNCTION)); + assert!( + !is_multi_step_proposal, + error::permission_denied(EMULTI_STEP_PROPOSAL_CANNOT_USE_SINGLE_STEP_RESOLVE_FUNCTION) + ); }; let resolved_early = can_be_resolved_early(proposal); proposal.is_resolved = true; proposal.resolution_time_secs = timestamp::now_seconds(); + if (std::features::module_event_migration_enabled()) { + event::emit( + ResolveProposal { + proposal_id, + yes_votes: proposal.yes_votes, + no_votes: proposal.no_votes, + resolved_early, + }, + ); + }; event::emit_event<ResolveProposal>( &mut voting_forum.events.resolve_proposal_events, ResolveProposal { @@ -1060,16 +1229,24 @@ there are more yes votes than no. If either of these conditions is not met, this // Update the IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY key to indicate that the multi-step proposal is in execution. let multi_step_in_execution_key = utf8(IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY); if (simple_map::contains_key(&proposal.metadata, &multi_step_in_execution_key)) { - let is_multi_step_proposal_in_execution_value = simple_map::borrow_mut(&mut proposal.metadata, &multi_step_in_execution_key); + let is_multi_step_proposal_in_execution_value = simple_map::borrow_mut( + &mut proposal.metadata, + &multi_step_in_execution_key + ); *is_multi_step_proposal_in_execution_value = to_bytes(&true); }; let multi_step_key = utf8(IS_MULTI_STEP_PROPOSAL_KEY); - let is_multi_step = simple_map::contains_key(&proposal.metadata, &multi_step_key) && from_bcs::to_bool(*simple_map::borrow(&proposal.metadata, &multi_step_key)); + let is_multi_step = simple_map::contains_key(&proposal.metadata, &multi_step_key) && from_bcs::to_bool( + *simple_map::borrow(&proposal.metadata, &multi_step_key) + ); let next_execution_hash_is_empty = vector::length(&next_execution_hash) == 0; // Assert that if this proposal is single-step, the `next_execution_hash` parameter is empty. - assert!(is_multi_step || next_execution_hash_is_empty, error::invalid_argument(ESINGLE_STEP_PROPOSAL_CANNOT_HAVE_NEXT_EXECUTION_HASH)); + assert!( + is_multi_step || next_execution_hash_is_empty, + error::invalid_argument(ESINGLE_STEP_PROPOSAL_CANNOT_HAVE_NEXT_EXECUTION_HASH) + ); // If the `next_execution_hash` parameter is empty, it means that either // - this proposal is a single-step proposal, or @@ -1081,7 +1258,10 @@ there are more yes votes than no. If either of these conditions is not met, this // Set the `IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY` value to false upon successful resolution of the last step of a multi-step proposal. if (is_multi_step) { - let is_multi_step_proposal_in_execution_value = simple_map::borrow_mut(&mut proposal.metadata, &multi_step_in_execution_key); + let is_multi_step_proposal_in_execution_value = simple_map::borrow_mut( + &mut proposal.metadata, + &multi_step_in_execution_key + ); *is_multi_step_proposal_in_execution_value = to_bytes(&false); }; } else { @@ -1094,7 +1274,17 @@ there are more yes votes than no. If either of these conditions is not met, this // For multi-step proposals, we emit one `ResolveProposal` event per step in the multi-step proposal. This means // that we emit multiple `ResolveProposal` events for the same multi-step proposal. let resolved_early = can_be_resolved_early(proposal); - event::emit_event<ResolveProposal>( + if (std::features::module_event_migration_enabled()) { + event::emit( + ResolveProposal { + proposal_id, + yes_votes: proposal.yes_votes, + no_votes: proposal.no_votes, + resolved_early, + }, + ); + }; + event::emit_event( &mut voting_forum.events.resolve_proposal_events, ResolveProposal { proposal_id, @@ -1103,6 +1293,7 @@ there are more yes votes than no. If either of these conditions is not met, this resolved_early, }, ); + }
    @@ -1127,7 +1318,7 @@ Return the next unassigned proposal id Implementation -
    public fun next_proposal_id<ProposalType: store>(voting_forum_address: address,): u64 acquires VotingForum {
    +
    public fun next_proposal_id<ProposalType: store>(voting_forum_address: address, ): u64 acquires VotingForum {
         let voting_forum = borrow_global<VotingForum<ProposalType>>(voting_forum_address);
         voting_forum.next_proposal_id
     }
    @@ -1153,7 +1344,10 @@ Return the next unassigned proposal id
     Implementation
     
     
    -
    public fun get_proposer<ProposalType: store>(voting_forum_address: address, proposal_id: u64): address acquires VotingForum {
    +
    public fun get_proposer<ProposalType: store>(
    +    voting_forum_address: address,
    +    proposal_id: u64
    +): address acquires VotingForum {
         let proposal = get_proposal<ProposalType>(voting_forum_address, proposal_id);
         proposal.proposer
     }
    @@ -1179,7 +1373,10 @@ Return the next unassigned proposal id
     Implementation
     
     
    -
    public fun is_voting_closed<ProposalType: store>(voting_forum_address: address, proposal_id: u64): bool acquires VotingForum {
    +
    public fun is_voting_closed<ProposalType: store>(
    +    voting_forum_address: address,
    +    proposal_id: u64
    +): bool acquires VotingForum {
         let proposal = get_proposal<ProposalType>(voting_forum_address, proposal_id);
         can_be_resolved_early(proposal) || is_voting_period_over(proposal)
     }
    @@ -1587,7 +1784,10 @@ Return true if the multi-step governance proposal is in execution.
         let voting_forum = borrow_global<VotingForum<ProposalType>>(voting_forum_address);
         let proposal = table::borrow(&voting_forum.proposals, proposal_id);
         let is_multi_step_in_execution_key = utf8(IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
    -    assert!(simple_map::contains_key(&proposal.metadata, &is_multi_step_in_execution_key), error::invalid_argument(EPROPOSAL_IS_SINGLE_STEP));
    +    assert!(
    +        simple_map::contains_key(&proposal.metadata, &is_multi_step_in_execution_key),
    +        error::invalid_argument(EPROPOSAL_IS_SINGLE_STEP)
    +    );
         from_bcs::to_bool(*simple_map::borrow(&proposal.metadata, &is_multi_step_in_execution_key))
     }
     
    @@ -1935,7 +2135,8 @@ Return true if the voting period of the given proposal has already ended. -
    requires chain_status::is_operating();
    +
    pragma verify_duration_estimate = 300;
    +requires chain_status::is_operating();
     include IsProposalResolvableAbortsIf<ProposalType>;
     let voting_forum = global<VotingForum<ProposalType>>(voting_forum_address);
     let proposal = table::spec_get(voting_forum.proposals, proposal_id);
    diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
    index d2ed48377de56..b27601c83b9ec 100644
    --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
    +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs
    @@ -206,6 +206,21 @@ pub enum EntryFunctionCall {
             proposal_id: u64,
         },
     
    +    /// Batch vote on proposal with proposal_id and specified voting power from multiple stake_pools.
    +    AptosGovernanceBatchPartialVote {
    +        stake_pools: Vec,
    +        proposal_id: u64,
    +        voting_power: u64,
    +        should_pass: bool,
    +    },
    +
    +    /// Vote on proposal with proposal_id and all voting power from multiple stake_pools.
    +    AptosGovernanceBatchVote {
    +        stake_pools: Vec,
    +        proposal_id: u64,
    +        should_pass: bool,
    +    },
    +
         /// Create a single-step proposal with the backing `stake_pool`.
         /// @param execution_hash Required. This is the hash of the resolution script. When the proposal is resolved,
         /// only the exact script with matching hash can be successfully executed.
    @@ -272,6 +287,18 @@ pub enum EntryFunctionCall {
             code: Vec>,
         },
     
    +    CoinCreateCoinConversionMap {},
    +
    +    /// Create APT pairing by passing `AptosCoin`.
    +    CoinCreatePairing {
    +        coin_type: TypeTag,
    +    },
    +
    +    /// Voluntarily migrate to fungible store for `CoinType` if not yet.
    +    CoinMigrateToFungibleStore {
    +        coin_type: TypeTag,
    +    },
    +
         /// Transfers `amount` of coins `CoinType` from `from` to `to`.
         CoinTransfer {
             coin_type: TypeTag,
    @@ -291,6 +318,11 @@ pub enum EntryFunctionCall {
             amount: u64,
         },
     
    +    /// Allowlist a delegator as the pool owner.
    +    DelegationPoolAllowlistDelegator {
    +        delegator_address: AccountAddress,
    +    },
    +
         /// A voter could create a governance proposal by this function. To successfully create a proposal, the voter's
         /// voting power in THIS delegation pool must be not less than the minimum required voting power specified in
         /// `aptos_governance.move`.
    @@ -309,12 +341,23 @@ pub enum EntryFunctionCall {
             new_voter: AccountAddress,
         },
     
    +    /// Disable delegators allowlisting as the pool owner. The existing allowlist will be emptied.
    +    DelegationPoolDisableDelegatorsAllowlisting {},
    +
    +    /// Enable delegators allowlisting as the pool owner.
    +    DelegationPoolEnableDelegatorsAllowlisting {},
    +
         /// Enable partial governance voting on a stake pool. The voter of this stake pool will be managed by this module.
    -    /// THe existing voter will be replaced. The function is permissionless.
    +    /// The existing voter will be replaced. The function is permissionless.
         DelegationPoolEnablePartialGovernanceVoting {
             pool_address: AccountAddress,
         },
     
    +    /// Evict a delegator that is not allowlisted by unlocking their entire stake.
    +    DelegationPoolEvictDelegator {
    +        delegator_address: AccountAddress,
    +    },
    +
         /// Initialize a delegation pool of custom fixed `operator_commission_percentage`.
         /// A resource account is created from `owner` signer and its supplied `delegation_pool_creation_seed`
         /// to host the delegation pool resource and own the underlying stake pool.
    @@ -330,8 +373,13 @@ pub enum EntryFunctionCall {
             amount: u64,
         },
     
    +    /// Remove a delegator from the allowlist as the pool owner, but do not unlock their stake.
    +    DelegationPoolRemoveDelegatorFromAllowlist {
    +        delegator_address: AccountAddress,
    +    },
    +
         /// Allows an operator to change its beneficiary. Any existing unpaid commission rewards will be paid to the new
    -    /// beneficiary. To ensures payment to the current beneficiary, one should first call `synchronize_delegation_pool`
    +    /// beneficiary. To ensure payment to the current beneficiary, one should first call `synchronize_delegation_pool`
         /// before switching the beneficiary. An operator can set one beneficiary for delegation pools, not a separate
         /// one for each pool.
         DelegationPoolSetBeneficiaryForOperator {
    @@ -860,6 +908,8 @@ pub enum EntryFunctionCall {
             new_voter: AccountAddress,
         },
     
    +    TransactionFeeConvertToAptosFaBurnRef {},
    +
         /// Used in on-chain governances to update the major version for the next epoch.
         /// Example usage:
         /// - `aptos_framework::version::set_for_next_epoch(&framework_signer, new_version);`
    @@ -1062,6 +1112,22 @@ impl EntryFunctionCall {
                 AptosGovernanceAddApprovedScriptHashScript { proposal_id } => {
                     aptos_governance_add_approved_script_hash_script(proposal_id)
                 },
    +            AptosGovernanceBatchPartialVote {
    +                stake_pools,
    +                proposal_id,
    +                voting_power,
    +                should_pass,
    +            } => aptos_governance_batch_partial_vote(
    +                stake_pools,
    +                proposal_id,
    +                voting_power,
    +                should_pass,
    +            ),
    +            AptosGovernanceBatchVote {
    +                stake_pools,
    +                proposal_id,
    +                should_pass,
    +            } => aptos_governance_batch_vote(stake_pools, proposal_id, should_pass),
                 AptosGovernanceCreateProposal {
                     stake_pool,
                     execution_hash,
    @@ -1104,6 +1170,9 @@ impl EntryFunctionCall {
                     metadata_serialized,
                     code,
                 } => code_publish_package_txn(metadata_serialized, code),
    +            CoinCreateCoinConversionMap {} => coin_create_coin_conversion_map(),
    +            CoinCreatePairing { coin_type } => coin_create_pairing(coin_type),
    +            CoinMigrateToFungibleStore { coin_type } => coin_migrate_to_fungible_store(coin_type),
                 CoinTransfer {
                     coin_type,
                     to,
    @@ -1114,6 +1183,9 @@ impl EntryFunctionCall {
                     pool_address,
                     amount,
                 } => delegation_pool_add_stake(pool_address, amount),
    +            DelegationPoolAllowlistDelegator { delegator_address } => {
    +                delegation_pool_allowlist_delegator(delegator_address)
    +            },
                 DelegationPoolCreateProposal {
                     pool_address,
                     execution_hash,
    @@ -1131,9 +1203,18 @@ impl EntryFunctionCall {
                     pool_address,
                     new_voter,
                 } => delegation_pool_delegate_voting_power(pool_address, new_voter),
    +            DelegationPoolDisableDelegatorsAllowlisting {} => {
    +                delegation_pool_disable_delegators_allowlisting()
    +            },
    +            DelegationPoolEnableDelegatorsAllowlisting {} => {
    +                delegation_pool_enable_delegators_allowlisting()
    +            },
                 DelegationPoolEnablePartialGovernanceVoting { pool_address } => {
                     delegation_pool_enable_partial_governance_voting(pool_address)
                 },
    +            DelegationPoolEvictDelegator { delegator_address } => {
    +                delegation_pool_evict_delegator(delegator_address)
    +            },
                 DelegationPoolInitializeDelegationPool {
                     operator_commission_percentage,
                     delegation_pool_creation_seed,
    @@ -1145,6 +1226,9 @@ impl EntryFunctionCall {
                     pool_address,
                     amount,
                 } => delegation_pool_reactivate_stake(pool_address, amount),
    +            DelegationPoolRemoveDelegatorFromAllowlist { delegator_address } => {
    +                delegation_pool_remove_delegator_from_allowlist(delegator_address)
    +            },
                 DelegationPoolSetBeneficiaryForOperator { new_beneficiary } => {
                     delegation_pool_set_beneficiary_for_operator(new_beneficiary)
                 },
    @@ -1487,6 +1571,9 @@ impl EntryFunctionCall {
                     operator,
                     new_voter,
                 } => staking_proxy_set_voter(operator, new_voter),
    +            TransactionFeeConvertToAptosFaBurnRef {} => {
    +                transaction_fee_convert_to_aptos_fa_burn_ref()
    +            },
                 VersionSetForNextEpoch { major } => version_set_for_next_epoch(major),
                 VersionSetVersion { major } => version_set_version(major),
                 VestingAdminWithdraw { contract_address } => vesting_admin_withdraw(contract_address),
    @@ -1993,6 +2080,56 @@ pub fn aptos_governance_add_approved_script_hash_script(proposal_id: u64) -> Tra
         ))
     }
     
    +/// Batch vote on proposal with proposal_id and specified voting power from multiple stake_pools.
    +pub fn aptos_governance_batch_partial_vote(
    +    stake_pools: Vec,
    +    proposal_id: u64,
    +    voting_power: u64,
    +    should_pass: bool,
    +) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("aptos_governance").to_owned(),
    +        ),
    +        ident_str!("batch_partial_vote").to_owned(),
    +        vec![],
    +        vec![
    +            bcs::to_bytes(&stake_pools).unwrap(),
    +            bcs::to_bytes(&proposal_id).unwrap(),
    +            bcs::to_bytes(&voting_power).unwrap(),
    +            bcs::to_bytes(&should_pass).unwrap(),
    +        ],
    +    ))
    +}
    +
    +/// Vote on proposal with proposal_id and all voting power from multiple stake_pools.
    +pub fn aptos_governance_batch_vote(
    +    stake_pools: Vec,
    +    proposal_id: u64,
    +    should_pass: bool,
    +) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("aptos_governance").to_owned(),
    +        ),
    +        ident_str!("batch_vote").to_owned(),
    +        vec![],
    +        vec![
    +            bcs::to_bytes(&stake_pools).unwrap(),
    +            bcs::to_bytes(&proposal_id).unwrap(),
    +            bcs::to_bytes(&should_pass).unwrap(),
    +        ],
    +    ))
    +}
    +
     /// Create a single-step proposal with the backing `stake_pool`.
     /// @param execution_hash Required. This is the hash of the resolution script. When the proposal is resolved,
     /// only the exact script with matching hash can be successfully executed.
    @@ -2186,6 +2323,53 @@ pub fn code_publish_package_txn(
         ))
     }
     
    +pub fn coin_create_coin_conversion_map() -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("coin").to_owned(),
    +        ),
    +        ident_str!("create_coin_conversion_map").to_owned(),
    +        vec![],
    +        vec![],
    +    ))
    +}
    +
    +/// Create APT pairing by passing `AptosCoin`.
    +pub fn coin_create_pairing(coin_type: TypeTag) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("coin").to_owned(),
    +        ),
    +        ident_str!("create_pairing").to_owned(),
    +        vec![coin_type],
    +        vec![],
    +    ))
    +}
    +
    +/// Voluntarily migrate to fungible store for `CoinType` if not yet.
    +pub fn coin_migrate_to_fungible_store(coin_type: TypeTag) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("coin").to_owned(),
    +        ),
    +        ident_str!("migrate_to_fungible_store").to_owned(),
    +        vec![coin_type],
    +        vec![],
    +    ))
    +}
    +
     /// Transfers `amount` of coins `CoinType` from `from` to `to`.
     pub fn coin_transfer(coin_type: TypeTag, to: AccountAddress, amount: u64) -> TransactionPayload {
         TransactionPayload::EntryFunction(EntryFunction::new(
    @@ -2238,6 +2422,24 @@ pub fn delegation_pool_add_stake(pool_address: AccountAddress, amount: u64) -> T
         ))
     }
     
    +/// Allowlist a delegator as the pool owner.
    +pub fn delegation_pool_allowlist_delegator(
    +    delegator_address: AccountAddress,
    +) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("delegation_pool").to_owned(),
    +        ),
    +        ident_str!("allowlist_delegator").to_owned(),
    +        vec![],
    +        vec![bcs::to_bytes(&delegator_address).unwrap()],
    +    ))
    +}
    +
     /// A voter could create a governance proposal by this function. To successfully create a proposal, the voter's
     /// voting power in THIS delegation pool must be not less than the minimum required voting power specified in
     /// `aptos_governance.move`.
    @@ -2291,8 +2493,40 @@ pub fn delegation_pool_delegate_voting_power(
         ))
     }
     
    +/// Disable delegators allowlisting as the pool owner. The existing allowlist will be emptied.
    +pub fn delegation_pool_disable_delegators_allowlisting() -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("delegation_pool").to_owned(),
    +        ),
    +        ident_str!("disable_delegators_allowlisting").to_owned(),
    +        vec![],
    +        vec![],
    +    ))
    +}
    +
    +/// Enable delegators allowlisting as the pool owner.
    +pub fn delegation_pool_enable_delegators_allowlisting() -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("delegation_pool").to_owned(),
    +        ),
    +        ident_str!("enable_delegators_allowlisting").to_owned(),
    +        vec![],
    +        vec![],
    +    ))
    +}
    +
     /// Enable partial governance voting on a stake pool. The voter of this stake pool will be managed by this module.
    -/// THe existing voter will be replaced. The function is permissionless.
    +/// The existing voter will be replaced. The function is permissionless.
     pub fn delegation_pool_enable_partial_governance_voting(
         pool_address: AccountAddress,
     ) -> TransactionPayload {
    @@ -2310,6 +2544,22 @@ pub fn delegation_pool_enable_partial_governance_voting(
         ))
     }
     
    +/// Evict a delegator that is not allowlisted by unlocking their entire stake.
    +pub fn delegation_pool_evict_delegator(delegator_address: AccountAddress) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("delegation_pool").to_owned(),
    +        ),
    +        ident_str!("evict_delegator").to_owned(),
    +        vec![],
    +        vec![bcs::to_bytes(&delegator_address).unwrap()],
    +    ))
    +}
    +
     /// Initialize a delegation pool of custom fixed `operator_commission_percentage`.
     /// A resource account is created from `owner` signer and its supplied `delegation_pool_creation_seed`
     /// to host the delegation pool resource and own the underlying stake pool.
    @@ -2357,8 +2607,26 @@ pub fn delegation_pool_reactivate_stake(
         ))
     }
     
    +/// Remove a delegator from the allowlist as the pool owner, but do not unlock their stake.
    +pub fn delegation_pool_remove_delegator_from_allowlist(
    +    delegator_address: AccountAddress,
    +) -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("delegation_pool").to_owned(),
    +        ),
    +        ident_str!("remove_delegator_from_allowlist").to_owned(),
    +        vec![],
    +        vec![bcs::to_bytes(&delegator_address).unwrap()],
    +    ))
    +}
    +
     /// Allows an operator to change its beneficiary. Any existing unpaid commission rewards will be paid to the new
    -/// beneficiary. To ensures payment to the current beneficiary, one should first call `synchronize_delegation_pool`
    +/// beneficiary. To ensure payment to the current beneficiary, one should first call `synchronize_delegation_pool`
     /// before switching the beneficiary. An operator can set one beneficiary for delegation pools, not a separate
     /// one for each pool.
     pub fn delegation_pool_set_beneficiary_for_operator(
    @@ -3968,6 +4236,21 @@ pub fn staking_proxy_set_voter(
         ))
     }
     
    +pub fn transaction_fee_convert_to_aptos_fa_burn_ref() -> TransactionPayload {
    +    TransactionPayload::EntryFunction(EntryFunction::new(
    +        ModuleId::new(
    +            AccountAddress::new([
    +                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    +                0, 0, 0, 1,
    +            ]),
    +            ident_str!("transaction_fee").to_owned(),
    +        ),
    +        ident_str!("convert_to_aptos_fa_burn_ref").to_owned(),
    +        vec![],
    +        vec![],
    +    ))
    +}
    +
     /// Used in on-chain governances to update the major version for the next epoch.
     /// Example usage:
     /// - `aptos_framework::version::set_for_next_epoch(&framework_signer, new_version);`
    @@ -4581,6 +4864,33 @@ mod decoder {
             }
         }
     
    +    pub fn aptos_governance_batch_partial_vote(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::AptosGovernanceBatchPartialVote {
    +                stake_pools: bcs::from_bytes(script.args().get(0)?).ok()?,
    +                proposal_id: bcs::from_bytes(script.args().get(1)?).ok()?,
    +                voting_power: bcs::from_bytes(script.args().get(2)?).ok()?,
    +                should_pass: bcs::from_bytes(script.args().get(3)?).ok()?,
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
    +    pub fn aptos_governance_batch_vote(payload: &TransactionPayload) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::AptosGovernanceBatchVote {
    +                stake_pools: bcs::from_bytes(script.args().get(0)?).ok()?,
    +                proposal_id: bcs::from_bytes(script.args().get(1)?).ok()?,
    +                should_pass: bcs::from_bytes(script.args().get(2)?).ok()?,
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn aptos_governance_create_proposal(
             payload: &TransactionPayload,
         ) -> Option {
    @@ -4678,6 +4988,38 @@ mod decoder {
             }
         }
     
    +    pub fn coin_create_coin_conversion_map(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(_script) = payload {
    +            Some(EntryFunctionCall::CoinCreateCoinConversionMap {})
    +        } else {
    +            None
    +        }
    +    }
    +
    +    pub fn coin_create_pairing(payload: &TransactionPayload) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::CoinCreatePairing {
    +                coin_type: script.ty_args().get(0)?.clone(),
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
    +    pub fn coin_migrate_to_fungible_store(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::CoinMigrateToFungibleStore {
    +                coin_type: script.ty_args().get(0)?.clone(),
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn coin_transfer(payload: &TransactionPayload) -> Option {
             if let TransactionPayload::EntryFunction(script) = payload {
                 Some(EntryFunctionCall::CoinTransfer {
    @@ -4711,6 +5053,18 @@ mod decoder {
             }
         }
     
    +    pub fn delegation_pool_allowlist_delegator(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::DelegationPoolAllowlistDelegator {
    +                delegator_address: bcs::from_bytes(script.args().get(0)?).ok()?,
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn delegation_pool_create_proposal(
             payload: &TransactionPayload,
         ) -> Option {
    @@ -4740,6 +5094,26 @@ mod decoder {
             }
         }
     
    +    pub fn delegation_pool_disable_delegators_allowlisting(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(_script) = payload {
    +            Some(EntryFunctionCall::DelegationPoolDisableDelegatorsAllowlisting {})
    +        } else {
    +            None
    +        }
    +    }
    +
    +    pub fn delegation_pool_enable_delegators_allowlisting(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(_script) = payload {
    +            Some(EntryFunctionCall::DelegationPoolEnableDelegatorsAllowlisting {})
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn delegation_pool_enable_partial_governance_voting(
             payload: &TransactionPayload,
         ) -> Option {
    @@ -4754,6 +5128,18 @@ mod decoder {
             }
         }
     
    +    pub fn delegation_pool_evict_delegator(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(EntryFunctionCall::DelegationPoolEvictDelegator {
    +                delegator_address: bcs::from_bytes(script.args().get(0)?).ok()?,
    +            })
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn delegation_pool_initialize_delegation_pool(
             payload: &TransactionPayload,
         ) -> Option {
    @@ -4780,6 +5166,20 @@ mod decoder {
             }
         }
     
    +    pub fn delegation_pool_remove_delegator_from_allowlist(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(script) = payload {
    +            Some(
    +                EntryFunctionCall::DelegationPoolRemoveDelegatorFromAllowlist {
    +                    delegator_address: bcs::from_bytes(script.args().get(0)?).ok()?,
    +                },
    +            )
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn delegation_pool_set_beneficiary_for_operator(
             payload: &TransactionPayload,
         ) -> Option {
    @@ -5721,6 +6121,16 @@ mod decoder {
             }
         }
     
    +    pub fn transaction_fee_convert_to_aptos_fa_burn_ref(
    +        payload: &TransactionPayload,
    +    ) -> Option {
    +        if let TransactionPayload::EntryFunction(_script) = payload {
    +            Some(EntryFunctionCall::TransactionFeeConvertToAptosFaBurnRef {})
    +        } else {
    +            None
    +        }
    +    }
    +
         pub fn version_set_for_next_epoch(payload: &TransactionPayload) -> Option {
             if let TransactionPayload::EntryFunction(script) = payload {
                 Some(EntryFunctionCall::VersionSetForNextEpoch {
    @@ -6031,6 +6441,14 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy>,
    +    remote_log_tx: Option>,
    +    is_async: bool,
    +    enable_telemetry_flush: bool,
         custom_format: Option Result>,
     }
     
    @@ -295,6 +305,9 @@ impl AptosDataBuilder {
                 remote_level: Level::Info,
                 telemetry_level: Level::Warn,
                 printer: Some(Box::new(StdoutWriter::new())),
    +            remote_log_tx: None,
    +            is_async: false,
    +            enable_telemetry_flush: true,
                 custom_format: None,
             }
         }
    @@ -334,6 +347,24 @@ impl AptosDataBuilder {
             self
         }
     
    +    pub fn remote_log_tx(
    +        &mut self,
    +        remote_log_tx: channel::mpsc::Sender,
    +    ) -> &mut Self {
    +        self.remote_log_tx = Some(remote_log_tx);
    +        self
    +    }
    +
    +    pub fn is_async(&mut self, is_async: bool) -> &mut Self {
    +        self.is_async = is_async;
    +        self
    +    }
    +
    +    pub fn enable_telemetry_flush(&mut self, enable_telemetry_flush: bool) -> &mut Self {
    +        self.enable_telemetry_flush = enable_telemetry_flush;
    +        self
    +    }
    +
         pub fn custom_format(
             &mut self,
             format: fn(&LogEntry) -> Result,
    @@ -346,8 +377,7 @@ impl AptosDataBuilder {
             self.build();
         }
     
    -    fn build_filter(&self) -> Filter {
    -        // Keep this block same as upstream, which also builds a telemetry filter
    +    fn build_filter(&self) -> FilterTuple {
             let local_filter = {
                 let mut filter_builder = Filter::builder();
     
    @@ -359,8 +389,26 @@ impl AptosDataBuilder {
     
                 filter_builder.build()
             };
    +        let telemetry_filter = {
    +            let mut filter_builder = Filter::builder();
    +
    +            if self.is_async && self.remote_log_tx.is_some() {
    +                if env::var(RUST_LOG_TELEMETRY).is_ok() {
    +                    filter_builder.with_env(RUST_LOG_TELEMETRY);
    +                } else {
    +                    filter_builder.filter_level(self.telemetry_level.into());
    +                }
    +            } else {
    +                filter_builder.filter_level(LevelFilter::Off);
    +            }
    +
    +            filter_builder.build()
    +        };
     
    -        local_filter
    +        FilterTuple {
    +            local_filter,
    +            telemetry_filter,
    +        }
         }
     
         fn build_logger(&mut self) -> Arc {
    @@ -374,13 +422,40 @@ impl AptosDataBuilder {
                 }
             }
     
    -        Arc::new(AptosData {
    -            enable_backtrace: self.enable_backtrace,
    -            sender: None,
    -            printer: self.printer.take(),
    -            filter: RwLock::new(filter),
    -            formatter: self.custom_format.take().unwrap_or(text_format),
    -        })
    +        if self.is_async {
    +            let (sender, receiver) = sync::mpsc::sync_channel(self.channel_size);
    +            let mut remote_tx = None;
    +            if let Some(tx) = &self.remote_log_tx {
    +                remote_tx = Some(tx.clone());
    +            }
    +
    +            let logger = Arc::new(AptosData {
    +                enable_backtrace: self.enable_backtrace,
    +                sender: Some(sender),
    +                printer: None,
    +                filter: RwLock::new(filter),
    +                enable_telemetry_flush: self.enable_telemetry_flush,
    +                formatter: self.custom_format.take().unwrap_or(text_format),
    +            });
    +            let service = LoggerService {
    +                receiver,
    +                printer: self.printer.take(),
    +                facade: logger.clone(),
    +                remote_tx,
    +            };
    +
    +            thread::spawn(move || service.run());
    +            logger
    +        } else {
    +            Arc::new(AptosData {
    +                enable_backtrace: self.enable_backtrace,
    +                sender: None,
    +                printer: self.printer.take(),
    +                filter: RwLock::new(filter),
    +                enable_telemetry_flush: self.enable_telemetry_flush,
    +                formatter: self.custom_format.take().unwrap_or(text_format),
    +            })
    +        }
         }
     
         pub fn build(&mut self) -> Arc {
    @@ -397,11 +472,26 @@ impl AptosDataBuilder {
         }
     }
     
    +/// A combination of `Filter`s to control where logs are written
    +pub struct FilterTuple {
    +    /// The local printer `Filter` to control what is logged in text output
    +    local_filter: Filter,
    +    /// The logging `Filter` to control what is sent to telemetry service
    +    telemetry_filter: Filter,
    +}
    +
    +impl FilterTuple {
    +    fn enabled(&self, metadata: &Metadata) -> bool {
    +        self.local_filter.enabled(metadata) || self.telemetry_filter.enabled(metadata)
    +    }
    +}
    +
     pub struct AptosData {
         enable_backtrace: bool,
         sender: Option>,
         printer: Option>,
    -    filter: RwLock,
    +    filter: RwLock,
    +    enable_telemetry_flush: bool,
         pub(crate) formatter: fn(&LogEntry) -> Result,
     }
     
    @@ -419,6 +509,7 @@ impl AptosData {
             // Create the Aptos Data Builder
             let mut builder = Self::builder();
             builder
    +            .is_async(false)
                 .enable_backtrace()
                 .printer(Box::new(StdoutWriter::new()));
     
    @@ -430,8 +521,16 @@ impl AptosData {
             builder.build();
         }
     
    +    pub fn set_filter(&self, filter_tuple: FilterTuple) {
    +        *self.filter.write() = filter_tuple;
    +    }
    +
         pub fn set_local_filter(&self, filter: Filter) {
    -        *self.filter.write() = filter;
    +        self.filter.write().local_filter = filter;
    +    }
    +
    +    pub fn set_telemetry_filter(&self, filter: Filter) {
    +        self.filter.write().telemetry_filter = filter;
         }
     
         fn send_entry(&self, entry: LogEntry) {
    @@ -489,8 +588,6 @@ enum LoggerServiceEvent {
         Flush(sync::mpsc::SyncSender<()>),
     }
     
    -<<<<<<< HEAD
    -=======
     /// A service for running a log listener, that will continually export logs through a local printer
     /// or to a `AptosData` for external logging.
     struct LoggerService {
    @@ -570,7 +667,6 @@ impl LoggerService {
         }
     }
     
    ->>>>>>> upstream/main
     /// A trait encapsulating the operations required for writing logs.
     pub trait Writer: Send + Sync {
         /// Write the log.
    @@ -679,24 +775,48 @@ fn json_format(entry: &LogEntry) -> Result {
         }
     }
     
    +/// Periodically rebuilds the filter and replaces the current logger filter.
    +/// This is useful for dynamically changing log levels at runtime via existing
    +/// environment variables such as `RUST_LOG_TELEMETRY`.
    +pub struct LoggerFilterUpdater {
    +    logger: Arc,
    +    logger_builder: AptosDataBuilder,
    +}
    +
    +impl LoggerFilterUpdater {
    +    pub fn new(logger: Arc, logger_builder: AptosDataBuilder) -> Self {
    +        Self {
    +            logger,
    +            logger_builder,
    +        }
    +    }
    +
    +    pub async fn run(self) {
    +        let mut interval = time::interval(FILTER_REFRESH_INTERVAL);
    +        loop {
    +            interval.tick().await;
    +
    +            self.update_filter();
    +        }
    +    }
    +
    +    fn update_filter(&self) {
    +        // TODO: check for change to env var before rebuilding filter.
    +        let filter = self.logger_builder.build_filter();
    +        self.logger.set_filter(filter);
    +    }
    +}
    +
     #[cfg(test)]
     mod tests {
    -<<<<<<< HEAD
    -    use super::LogEntry;
    -=======
         use super::{text_format, AptosData, LogEntry};
    ->>>>>>> upstream/main
         use crate::{
    -        aptos_logger::{json_format, TruncatedLogString},
    +        aptos_logger::{json_format, TruncatedLogString, RUST_LOG_TELEMETRY},
             debug, error, info,
             logger::Logger,
    -<<<<<<< HEAD
    -        trace, warn, Event, Key, KeyValue, Level, Metadata, Schema, Value, Visitor,
    -=======
             telemetry_log_writer::TelemetryLog,
             trace, warn, AptosDataBuilder, Event, Key, KeyValue, Level, LoggerFilterUpdater, Metadata,
             Schema, Value, Visitor, Writer,
    ->>>>>>> upstream/main
         };
         use chrono::{DateTime, Utc};
         use futures::StreamExt;
    @@ -938,8 +1058,6 @@ mod tests {
             }
         }
     
    -<<<<<<< HEAD
    -=======
         fn new_async_logger() -> (AptosDataBuilder, Arc) {
             let mut logger_builder = AptosDataBuilder::new();
             let (remote_log_tx, _) = futures::channel::mpsc::channel(10);
    @@ -1056,7 +1174,6 @@ mod tests {
                 )));
         }
     
    ->>>>>>> upstream/main
         #[test]
         fn test_log_event_truncation() {
             let log_entry = LogEntry::new(
    diff --git a/crates/aptos-logger/src/counters.rs b/crates/aptos-logger/src/counters.rs
    index 677290b61a6bd..0e0e530b98821 100644
    --- a/crates/aptos-logger/src/counters.rs
    +++ b/crates/aptos-logger/src/counters.rs
    @@ -11,8 +11,6 @@ pub static STRUCT_LOG_COUNT: Lazy = Lazy::new(|| {
         register_int_counter!("aptos_struct_log_count", "Count of the struct logs.").unwrap()
     });
     
    -<<<<<<< HEAD
    -=======
     /// Count of struct logs processed, but not necessarily sent
     pub static PROCESSED_STRUCT_LOG_COUNT: Lazy = Lazy::new(|| {
         register_int_counter!(
    @@ -30,7 +28,6 @@ pub static WARN_LOG_COUNT: Lazy =
     pub static INFO_LOG_COUNT: Lazy =
         Lazy::new(|| register_int_counter!("aptos_info_log_count", "Count of info!() logs").unwrap());
     
    ->>>>>>> upstream/main
     /// Metric for when we fail to log during sending to the queue
     pub static STRUCT_LOG_QUEUE_ERROR_COUNT: Lazy = Lazy::new(|| {
         register_int_counter!(
    @@ -47,3 +44,21 @@ pub static STRUCT_LOG_PARSE_ERROR_COUNT: Lazy = Lazy::new(|| {
         )
         .unwrap()
     });
    +
    +/// Counter for failed log ingest writes (see also: aptos-telemetry for sender metrics)
    +pub static APTOS_LOG_INGEST_WRITER_FULL: Lazy = Lazy::new(|| {
    +    register_int_counter!(
    +        "aptos_log_ingest_writer_full",
    +        "Number of log ingest writes that failed due to channel full"
    +    )
    +    .unwrap()
    +});
    +
    +/// Counter for failed log ingest writes (see also: aptos-telemetry for sender metrics)
    +pub static APTOS_LOG_INGEST_WRITER_DISCONNECTED: Lazy = Lazy::new(|| {
    +    register_int_counter!(
    +        "aptos_log_ingest_writer_disconnected",
    +        "Number of log ingest writes that failed due to channel disconnected"
    +    )
    +    .unwrap()
    +});
    diff --git a/crates/aptos-logger/src/lib.rs b/crates/aptos-logger/src/lib.rs
    index 7d56753c27a0c..26a5d0ad15ec0 100644
    --- a/crates/aptos-logger/src/lib.rs
    +++ b/crates/aptos-logger/src/lib.rs
    @@ -152,11 +152,14 @@ mod logger;
     mod macros;
     mod metadata;
     pub mod sample;
    +pub mod telemetry_log_writer;
     pub mod tracing_adapter;
     
     mod security;
     
    -pub use crate::aptos_logger::{AptosData as Logger, AptosDataBuilder, Writer, CHANNEL_SIZE};
    +pub use crate::aptos_logger::{
    +    AptosData as Logger, AptosDataBuilder, LoggerFilterUpdater, Writer, CHANNEL_SIZE,
    +};
     pub use aptos_log_derive::Schema;
     pub use event::Event;
     pub use filter::{Filter, LevelFilter};
    diff --git a/crates/aptos-logger/src/telemetry_log_writer.rs b/crates/aptos-logger/src/telemetry_log_writer.rs
    new file mode 100644
    index 0000000000000..e73503b47851e
    --- /dev/null
    +++ b/crates/aptos-logger/src/telemetry_log_writer.rs
    @@ -0,0 +1,59 @@
    +// Copyright © Aptos Foundation
    +// SPDX-License-Identifier: Apache-2.0
    +
    +use crate::counters::{APTOS_LOG_INGEST_WRITER_DISCONNECTED, APTOS_LOG_INGEST_WRITER_FULL};
    +use futures::channel;
    +use std::{
    +    io::{Error, ErrorKind},
    +    sync,
    +};
    +
    +#[derive(Debug)]
    +pub enum TelemetryLog {
    +    Log(String),
    +    Flush(sync::mpsc::SyncSender<()>),
    +}
    +
    +#[derive(Debug)]
    +pub(crate) struct TelemetryLogWriter {
    +    tx: channel::mpsc::Sender,
    +}
    +
    +impl TelemetryLogWriter {
    +    pub fn new(tx: channel::mpsc::Sender) -> Self {
    +        Self { tx }
    +    }
    +}
    +
    +impl TelemetryLogWriter {
    +    pub fn write(&mut self, log: String) -> std::io::Result {
    +        let len = log.len();
    +        match self.tx.try_send(TelemetryLog::Log(log)) {
    +            Ok(_) => Ok(len),
    +            Err(err) => {
    +                if err.is_full() {
    +                    APTOS_LOG_INGEST_WRITER_FULL.inc_by(len as u64);
    +                    Err(Error::new(ErrorKind::WouldBlock, "Channel full"))
    +                } else {
    +                    APTOS_LOG_INGEST_WRITER_DISCONNECTED.inc_by(len as u64);
    +                    Err(Error::new(ErrorKind::ConnectionRefused, "Disconnected"))
    +                }
    +            },
    +        }
    +    }
    +
    +    #[allow(dead_code)]
    +    pub fn flush(&mut self) -> std::io::Result> {
    +        let (tx, rx) = sync::mpsc::sync_channel(1);
    +        match self.tx.try_send(TelemetryLog::Flush(tx)) {
    +            Ok(_) => Ok(rx),
    +            Err(err) => {
    +                if err.is_full() {
    +                    Err(Error::new(ErrorKind::WouldBlock, "Channel full"))
    +                } else {
    +                    Err(Error::new(ErrorKind::ConnectionRefused, "Disconnected"))
    +                }
    +            },
    +        }
    +    }
    +}
    diff --git a/crates/aptos-logger/tests/logger.rs b/crates/aptos-logger/tests/logger.rs
    index c27f54e70030a..d0d8cbf5e5e65 100644
    --- a/crates/aptos-logger/tests/logger.rs
    +++ b/crates/aptos-logger/tests/logger.rs
    @@ -25,7 +25,10 @@ impl Writer for VecWriter {
     fn verify_end_to_end() {
         let writer = VecWriter::default();
         let logs = writer.logs.clone();
    -    AptosData::builder().printer(Box::new(writer)).build();
    +    AptosData::builder()
    +        .is_async(false)
    +        .printer(Box::new(writer))
    +        .build();
     
         assert_eq!(logs.read().len(), 0);
         info!("Hello");
    diff --git a/crates/aptos-logger/tests/logger_custom_format.rs b/crates/aptos-logger/tests/logger_custom_format.rs
    index 8f4f7649a8289..279e36138f171 100644
    --- a/crates/aptos-logger/tests/logger_custom_format.rs
    +++ b/crates/aptos-logger/tests/logger_custom_format.rs
    @@ -26,6 +26,7 @@ fn test_custom_formatter() {
         let writer = VecWriter::default();
         let logs = writer.logs.clone();
         AptosData::builder()
    +        .is_async(false)
             .printer(Box::new(writer))
             .custom_format(|entry| {
                 use std::fmt::Write;
    diff --git a/crates/aptos-logger/tests/tracing_translation_tests.rs b/crates/aptos-logger/tests/tracing_translation_tests.rs
    index 14a1e6cc81501..da277b0efb31c 100644
    --- a/crates/aptos-logger/tests/tracing_translation_tests.rs
    +++ b/crates/aptos-logger/tests/tracing_translation_tests.rs
    @@ -41,6 +41,7 @@ fn verify_tracing_kvs() {
         let writer = VecWriter::default();
         let logs = writer.logs.clone();
         AptosData::builder()
    +        .is_async(false)
             .tokio_console_port(None)
             .printer(Box::new(writer.write_to_stderr(false)))
             .build();
    diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs
    index eafaec939277f..6ae4184bf6aa0 100644
    --- a/storage/storage-interface/src/finality_view.rs
    +++ b/storage/storage-interface/src/finality_view.rs
    @@ -64,7 +64,7 @@ impl DbReader for FinalityView {
             Ok(fin_ledger_info.clone())
         }
     
    -    fn get_latest_version(&self) -> Result {
    +    fn get_synced_version(&self) -> Result {
             let fin_ledger_info = self.finalized_ledger_info.read().unwrap();
             fin_ledger_info
                 .as_ref()
    diff --git a/testsuite/testcases/src/data/four_region_link_stats.csv b/testsuite/testcases/src/data/four_region_link_stats.csv
    new file mode 100644
    index 0000000000000..64175bc2a8eca
    --- /dev/null
    +++ b/testsuite/testcases/src/data/four_region_link_stats.csv
    @@ -0,0 +1,13 @@
    +sending_region,receiving_region,bitrate_bps,avgrtt
    +gcp--us-central1,aws--eu-west-1,300000000,103.435
    +gcp--us-central1,aws--ap-northeast-1,300000000,133.996
    +gcp--us-central1,aws--sa-east-1,300000000,145.483
    +aws--sa-east-1,gcp--us-central1,300000000,145.703
    +aws--sa-east-1,aws--eu-west-1,300000000,176.894
    +aws--sa-east-1,aws--ap-northeast-1,300000000,255.289
    +aws--eu-west-1,gcp--us-central1,300000000,104.169
    +aws--eu-west-1,aws--sa-east-1,300000000,176.813
    +aws--eu-west-1,aws--ap-northeast-1,300000000,198.555
    +aws--ap-northeast-1,gcp--us-central1,300000000,128.999
    +aws--ap-northeast-1,aws--eu-west-1,300000000,198.539
    +aws--ap-northeast-1,aws--sa-east-1,300000000,255.323
    \ No newline at end of file
    diff --git a/testsuite/testcases/src/data/two_region_link_stats.csv b/testsuite/testcases/src/data/two_region_link_stats.csv
    new file mode 100644
    index 0000000000000..cc22397cf27f1
    --- /dev/null
    +++ b/testsuite/testcases/src/data/two_region_link_stats.csv
    @@ -0,0 +1,3 @@
    +sending_region,receiving_region,bitrate_bps,avgrtt
    +aws--sa-east-1,aws--ap-northeast-1,300000000,255.289
    +aws--ap-northeast-1,aws--sa-east-1,300000000,255.323
    \ No newline at end of file
    diff --git a/testsuite/testcases/tests/forge-local-compatibility.rs b/testsuite/testcases/tests/forge-local-compatibility.rs
    new file mode 100644
    index 0000000000000..6b45328d6cba4
    --- /dev/null
    +++ b/testsuite/testcases/tests/forge-local-compatibility.rs
    @@ -0,0 +1,23 @@
    +// Copyright © Aptos Foundation
    +// Parts of the project are originally copyright © Meta Platforms, Inc.
    +// SPDX-License-Identifier: Apache-2.0
    +
    +use aptos_forge::{forge_main, ForgeConfig, InitialVersion, LocalFactory, Options, Result};
    +use aptos_testcases::compatibility_test::SimpleValidatorUpgrade;
    +use std::num::NonZeroUsize;
    +
    +fn main() -> Result<()> {
    +    ::aptos_logger::Logger::init_for_testing();
    +
    +    let tests = ForgeConfig::default()
    +        .with_initial_validator_count(NonZeroUsize::new(4).unwrap())
    +        .with_initial_version(InitialVersion::Oldest)
    +        .add_network_test(SimpleValidatorUpgrade);
    +
    +    let options = Options::parse();
    +    forge_main(
    +        tests,
    +        LocalFactory::with_upstream_merge_base_and_workspace()?,
    +        &options,
    +    )
    +}
    diff --git a/testsuite/testcases/tests/forge-local-performance.rs b/testsuite/testcases/tests/forge-local-performance.rs
    new file mode 100644
    index 0000000000000..df5e682180b00
    --- /dev/null
    +++ b/testsuite/testcases/tests/forge-local-performance.rs
    @@ -0,0 +1,34 @@
    +// Copyright © Aptos Foundation
    +// Parts of the project are originally copyright © Meta Platforms, Inc.
    +// SPDX-License-Identifier: Apache-2.0
    +
    +use aptos_forge::{
    +    forge_main,
    +    success_criteria::{StateProgressThreshold, SuccessCriteria},
    +    EmitJobMode, EmitJobRequest, ForgeConfig, InitialVersion, LocalFactory, Options, Result,
    +};
    +use aptos_testcases::performance_test::PerformanceBenchmark;
    +use std::num::NonZeroUsize;
    +
    +fn main() -> Result<()> {
    +    ::aptos_logger::Logger::init_for_testing();
    +
    +    let tests = ForgeConfig::default()
    +        .with_initial_validator_count(NonZeroUsize::new(2).unwrap())
    +        .with_initial_version(InitialVersion::Newest)
    +        .add_network_test(PerformanceBenchmark)
    +        .with_emit_job(
    +            EmitJobRequest::default()
    +                .mode(EmitJobMode::ConstTps { tps: 30 })
    +                .gas_price(aptos_global_constants::GAS_UNIT_PRICE),
    +        )
    +        .with_success_criteria(SuccessCriteria::new(20).add_chain_progress(
    +            StateProgressThreshold {
    +                max_no_progress_secs: 0.0,
    +                max_round_gap: 0,
    +            },
    +        ));
    +
    +    let options = Options::parse();
    +    forge_main(tests, LocalFactory::from_workspace(None)?, &options)
    +}
    diff --git a/types/Cargo.toml b/types/Cargo.toml
    index 295d2a2346d97..ff6b53e1b5f90 100644
    --- a/types/Cargo.toml
    +++ b/types/Cargo.toml
    @@ -52,7 +52,7 @@ proptest-derive = { workspace = true, optional = true }
     quick_cache = { workspace = true }
     rand = { workspace = true }
     rayon = { workspace = true }
    -ring = { workspace = true, optional = true }
    +ring = { workspace = true }
     rsa = { workspace = true }
     serde = { workspace = true }
     serde-big-array = { workspace = true }
    @@ -88,7 +88,7 @@ url = { workspace = true }
     
     [features]
     default = []
    -testing = ["ring"]
    +testing = []
     fuzzing = ["proptest", "proptest-derive", "aptos-crypto/fuzzing", "move-core-types/fuzzing"]
     
     [[bench]]
    
    From d1918c1c6b046850107d72a679ac8356a97a97a3 Mon Sep 17 00:00:00 2001
    From: Liam Monninger 
    Date: Tue, 2 Jul 2024 09:13:23 +0100
    Subject: [PATCH 110/147] fix: revert commit.
    
    ---
     aptos-move/vm-genesis/Cargo.toml              |  2 +-
     aptos-move/vm-genesis/src/lib.rs              | 11 ++--
     consensus/consensus-types/src/common.rs       |  7 +--
     .../aptosdb/src/db/include/aptosdb_writer.rs  |  2 +-
     storage/storage-interface/src/lib.rs          | 53 +------------------
     storage/storage-interface/src/mock.rs         | 25 ---------
     types/src/keyless/circuit_testcases.rs        |  1 +
     7 files changed, 11 insertions(+), 90 deletions(-)
    
    diff --git a/aptos-move/vm-genesis/Cargo.toml b/aptos-move/vm-genesis/Cargo.toml
    index 59927cdd791a7..3434295311c3a 100644
    --- a/aptos-move/vm-genesis/Cargo.toml
    +++ b/aptos-move/vm-genesis/Cargo.toml
    @@ -17,7 +17,7 @@ aptos-cached-packages =  { workspace = true }
     aptos-crypto = { workspace = true }
     aptos-framework =  { workspace = true }
     aptos-gas-schedule = { workspace = true } 
    -aptos-types = { workspace = true }
    +aptos-types = { workspace = true, features = ["testing"] }
     aptos-vm = { workspace = true }
     bcs = { workspace = true }
     bytes = { workspace = true }
    diff --git a/aptos-move/vm-genesis/src/lib.rs b/aptos-move/vm-genesis/src/lib.rs
    index e500fc6dd85fa..bb1333021ca07 100644
    --- a/aptos-move/vm-genesis/src/lib.rs
    +++ b/aptos-move/vm-genesis/src/lib.rs
    @@ -404,12 +404,11 @@ fn initialize(
         // Calculate the per-epoch rewards rate, represented as 2 separate ints (numerator and
         // denominator).
         let rewards_rate_denominator = 1_000_000_000;
    -    // Fold division of the percentage by 100 into multiplication with the denominator
    -    // to minimize rounding errors due to integer division.
    -    let rewards_rate_numerator = genesis_config.rewards_apy_percentage
    -        * (rewards_rate_denominator / 100)
    -        * genesis_config.epoch_duration_secs
    -        / NUM_SECONDS_PER_YEAR;
    +    let num_epochs_in_a_year = NUM_SECONDS_PER_YEAR / genesis_config.epoch_duration_secs;
    +    // Multiplication before division to minimize rounding errors due to integer division.
    +    let rewards_rate_numerator = (genesis_config.rewards_apy_percentage * rewards_rate_denominator
    +        / 100)
    +        / num_epochs_in_a_year;
     
         // Block timestamps are in microseconds and epoch_interval is used to check if a block timestamp
         // has crossed into a new epoch. So epoch_interval also needs to be in micro seconds.
    diff --git a/consensus/consensus-types/src/common.rs b/consensus/consensus-types/src/common.rs
    index 2b51f9989377a..db4e6dfbe0cd2 100644
    --- a/consensus/consensus-types/src/common.rs
    +++ b/consensus/consensus-types/src/common.rs
    @@ -16,12 +16,7 @@ use aptos_types::{
     use once_cell::sync::OnceCell;
     use rayon::prelude::*;
     use serde::{Deserialize, Serialize};
    -<<<<<<< HEAD
    -use std::{cmp::min, collections::HashSet, fmt, fmt::Write};
    -=======
    -use std::{collections::HashSet, fmt, fmt::Write, sync::Arc};
    -use tokio::sync::oneshot;
    ->>>>>>> upstream/main
    +use std::{collections::HashSet, fmt, fmt::Write};
     
     /// The round of a block is a consensus-internal counter, which starts with 0 and increases
     /// monotonically. It is used for the protocol safety and liveness (please see the detailed
    diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs
    index 579b686c7e18d..e52966d7d5936 100644
    --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs
    +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs
    @@ -209,7 +209,7 @@ impl DbWriter for AptosDB {
                 .with_label_values(&["revert_commit"])
                 .start_timer();
     
    -        let latest_version = self.get_latest_version()?;
    +        let latest_version = self.get_synced_version()?;
             let target_version = ledger_info_with_sigs.ledger_info().version();
     
             let ledger_batch = SchemaBatch::new();
    diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs
    index 6ee7ae077d18b..eb08cf8d27b3e 100644
    --- a/storage/storage-interface/src/lib.rs
    +++ b/storage/storage-interface/src/lib.rs
    @@ -490,53 +490,6 @@ pub trait DbReader: Send + Sync {
         }
     }
     
    -impl DbReader for Arc {
    -    fn get_read_delegatee(&self) -> &dyn DbReader {
    -        &**self
    -    }
    -}
    -
    -impl MoveStorage for &dyn DbReader {
    -    fn fetch_resource_by_version(
    -        &self,
    -        access_path: AccessPath,
    -        version: Version,
    -    ) -> Result, anyhow::Error> {
    -        let state_value =
    -            self.get_state_value_by_version(&StateKey::access_path(access_path), version)?;
    -
    -        state_value
    -            .ok_or_else(|| anyhow!("no value found in DB".to_string()))
    -            .map(|value| value.bytes().to_vec())
    -    }
    -
    -    fn fetch_config_by_version(
    -        &self,
    -        config_id: ConfigID,
    -        version: Version,
    -    ) -> Result, anyhow::Error> {
    -        let config_value_option = self.get_state_value_by_version(
    -            &StateKey::access_path(AccessPath::new(
    -                CORE_CODE_ADDRESS,
    -                access_path_for_config(config_id)?.path,
    -            )),
    -            version,
    -        )?;
    -        config_value_option
    -            .map(|x| x.bytes().to_vec())
    -            .ok_or_else(|| anyhow!("no config {} found in aptos root account state", config_id))
    -    }
    -
    -    fn fetch_synced_version(&self) -> Result {
    -        self.get_latest_version().map_err(Into::into)
    -    }
    -
    -    fn fetch_latest_state_checkpoint_version(&self) -> Result {
    -        self.get_latest_state_checkpoint_version()?
    -            .ok_or_else(|| anyhow!("[MoveStorage] Latest state checkpoint not found.".to_string()))
    -    }
    -}
    -
     /// Trait that is implemented by a DB that supports certain public (to client) write APIs
     /// expected of an Aptos DB. This adds write APIs to DbReader.
     #[allow(unused_variables)]
    @@ -586,13 +539,11 @@ pub trait DbWriter: Send + Sync {
             unimplemented!()
         }
     
    -    /// Revert to committed state referred to with the ledger information.
         fn revert_commit(
             &self,
             ledger_info_with_sigs: &LedgerInfoWithSignatures,
    -    ) -> Result<()> {
    -        unimplemented!()
    -    }
    +    ) -> Result<()> ;
    +
     }
     
     #[derive(Clone)]
    diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs
    index 108930c1cae9c..5d3d367cc35b4 100644
    --- a/storage/storage-interface/src/mock.rs
    +++ b/storage/storage-interface/src/mock.rs
    @@ -5,7 +5,6 @@
     //! This module provides mock dbreader for tests.
     
     use crate::{errors::AptosDbError, DbReader, DbWriter, Result};
    -use aptos_crypto::HashValue;
     use aptos_types::{
         proof::SparseMerkleProofExt,
         state_store::{
    @@ -57,30 +56,6 @@ impl DbReader for MockDbReaderWriter {
                 .get_state_value_by_version(state_key, version)?
                 .map(|value| (version, value)))
         }
    -
    -    fn get_block_info_by_height(
    -        &self,
    -        height: u64,
    -    ) -> Result<(Version, Version, aptos_types::account_config::NewBlockEvent)> {
    -        Ok((
    -            0,
    -            1,
    -            aptos_types::account_config::NewBlockEvent::new(
    -                AccountAddress::ONE,
    -                0,
    -                0,
    -                height,
    -                vec![],
    -                AccountAddress::TWO,
    -                vec![],
    -                0,
    -            ),
    -        ))
    -    }
    -
    -    fn get_accumulator_root_hash(&self, _version: Version) -> Result {
    -        Ok(HashValue::zero())
    -    }
     }
     
     impl DbWriter for MockDbReaderWriter {}
    diff --git a/types/src/keyless/circuit_testcases.rs b/types/src/keyless/circuit_testcases.rs
    index 0748bfb5f892a..5ece96fae80ef 100644
    --- a/types/src/keyless/circuit_testcases.rs
    +++ b/types/src/keyless/circuit_testcases.rs
    @@ -10,6 +10,7 @@ use crate::{
             rsa::{INSECURE_TEST_RSA_KEY_PAIR, RSA_JWK},
         },
         keyless::{
    +        configuration,
             base64url_encode_str,
             bn254_circom::{G1Bytes, G2Bytes},
             g1_projective_str_to_affine, g2_projective_str_to_affine, Claims, Configuration,
    
    From 6e0ac4248f90237eb0afd61575f0e8df1aa05e8b Mon Sep 17 00:00:00 2001
    From: Liam Monninger 
    Date: Tue, 2 Jul 2024 09:14:15 +0100
    Subject: [PATCH 111/147] fix: revert commit.
    
    ---
     storage/storage-interface/src/lib.rs | 4 +++-
     1 file changed, 3 insertions(+), 1 deletion(-)
    
    diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs
    index eb08cf8d27b3e..084c0816764bd 100644
    --- a/storage/storage-interface/src/lib.rs
    +++ b/storage/storage-interface/src/lib.rs
    @@ -542,7 +542,9 @@ pub trait DbWriter: Send + Sync {
         fn revert_commit(
             &self,
             ledger_info_with_sigs: &LedgerInfoWithSignatures,
    -    ) -> Result<()> ;
    +    ) -> Result<()> {
    +        unimplemented!()
    +    }
     
     }
     
    
    From f3dfe09765ac32b8fc38860a1a45c2132e4b97b6 Mon Sep 17 00:00:00 2001
    From: Liam Monninger 
    Date: Tue, 2 Jul 2024 09:15:11 +0100
    Subject: [PATCH 112/147] fix: bad argument.
    
    ---
     storage/aptosdb/src/event_store/mod.rs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/storage/aptosdb/src/event_store/mod.rs b/storage/aptosdb/src/event_store/mod.rs
    index 3354f776305b3..be63d9eafc3ad 100644
    --- a/storage/aptosdb/src/event_store/mod.rs
    +++ b/storage/aptosdb/src/event_store/mod.rs
    @@ -349,7 +349,7 @@ impl EventStore {
         ) -> anyhow::Result<()> {
             let mut iter = self
                 .event_db
    -            .iter::(Default::default())?;
    +            .iter::()?;
             iter.seek(&(version, Position::from_inorder_index(0)))?;
             while let Some(((ver, position), _)) = iter.next().transpose()? {
                 if ver != version {
    
    From 643e6ec3ad2978f34db7580392cf83870c2e6d26 Mon Sep 17 00:00:00 2001
    From: Liam Monninger 
    Date: Mon, 8 Jul 2024 15:04:39 +0100
    Subject: [PATCH 113/147] fix: merge.
    
    ---
     Cargo.lock                                    | 429 +++-----
     Cargo.toml                                    |   2 +-
     aptos-move/aptos-release-builder/Cargo.toml   |   2 +-
     crates/aptos-rosetta-cli/Cargo.toml           |   2 +-
     crates/aptos/CHANGELOG.md                     |  72 +-
     crates/aptos/Cargo.toml                       |  35 +-
     crates/aptos/README.md                        |   5 +
     crates/aptos/build.rs                         |   6 +
     crates/aptos/debug-move-example/Move.toml     |   9 +
     .../debug-move-example/sources/DebugDemo.move |  32 +
     crates/aptos/e2e/README.md                    | 105 ++
     crates/aptos/e2e/cases/__init__.py            |   0
     crates/aptos/e2e/cases/account.py             |  10 +-
     crates/aptos/e2e/cases/config.py              |  32 +
     crates/aptos/e2e/cases/init.py                |   4 +-
     crates/aptos/e2e/cases/move.py                | 291 ++++++
     crates/aptos/e2e/cases/node.py                | 178 ++++
     crates/aptos/e2e/cases/stake.py               |  61 +-
     crates/aptos/e2e/common.py                    |  55 +
     crates/aptos/e2e/linter.sh                    |  17 +
     crates/aptos/e2e/local_testnet.py             |  11 +-
     crates/aptos/e2e/main.py                      |  52 +-
     crates/aptos/e2e/poetry.lock                  | 956 +++++-------------
     crates/aptos/e2e/pyproject.toml               |   4 +-
     crates/aptos/e2e/test_helpers.py              |   4 +-
     crates/aptos/e2e/test_results.py              |  49 +
     crates/aptos/homebrew/README.md               |  55 +-
     crates/aptos/scripts/check_dynamic_deps.sh    |  46 +
     crates/aptos/src/account/create.rs            |  41 +
     .../src/account/create_resource_account.rs    |  91 ++
     .../src/account/derive_resource_account.rs    | 109 ++
     crates/aptos/src/account/fund.rs              |  60 ++
     crates/aptos/src/account/key_rotation.rs      | 367 +++++++
     crates/aptos/src/account/list.rs              | 124 +++
     crates/aptos/src/account/mod.rs               |   3 -
     crates/aptos/src/account/multisig_account.rs  |  34 +-
     crates/aptos/src/account/transfer.rs          | 115 +++
     crates/aptos/src/common/init.rs               |   4 +-
     crates/aptos/src/common/mod.rs                |   1 -
     crates/aptos/src/common/types.rs              | 204 ++--
     crates/aptos/src/common/utils.rs              |  30 +-
     crates/aptos/src/config/mod.rs                | 356 +++++++
     crates/aptos/src/ffi.rs                       |  84 ++
     crates/aptos/src/genesis/git.rs               | 264 +++++
     crates/aptos/src/genesis/keys.rs              | 344 +++++++
     crates/aptos/src/genesis/mod.rs               |  26 +-
     crates/aptos/src/genesis/tests.rs             | 434 ++++++++
     crates/aptos/src/genesis/tools.rs             | 100 ++
     .../aptos/src/governance/delegation_pool.rs   |  38 +-
     crates/aptos/src/governance/mod.rs            |  44 +-
     crates/aptos/src/governance/utils.rs          |  45 +
     crates/aptos/src/lib.rs                       |  97 ++
     crates/aptos/src/main.rs                      |   4 +-
     .../src/move_tool/aptos_debug_natives.rs      |  27 +
     .../src/move_tool/aptos_dep_example/README.md |  24 +
     .../aptos_dep_example/pack1/Move.toml         |   6 +
     .../pack1/sources/hello.move                  |   7 +
     .../aptos_dep_example/pack2/Move.toml         |   3 +
     .../aptos_dep_example/pack2/sources/m.move    |   3 +
     crates/aptos/src/move_tool/coverage.rs        | 191 ++++
     crates/aptos/src/move_tool/disassembler.rs    | 148 +++
     crates/aptos/src/move_tool/manifest.rs        |  92 ++
     crates/aptos/src/move_tool/mod.rs             | 489 +--------
     crates/aptos/src/move_tool/package_hooks.rs   |  54 +
     crates/aptos/src/move_tool/show.rs            |   2 -
     crates/aptos/src/move_tool/stored_package.rs  | 215 ++++
     .../src/node/analyze/analyze_validators.rs    | 117 +--
     .../aptos/src/node/analyze/fetch_metadata.rs  | 337 ++++++
     crates/aptos/src/node/analyze/mod.rs          |   5 +
     crates/aptos/src/node/local_testnet/docker.rs |   2 +-
     crates/aptos/src/node/local_testnet/faucet.rs |   6 +-
     .../node/local_testnet/hasura_metadata.json   | 217 ++--
     .../src/node/local_testnet/health_checker.rs  | 216 ++++
     .../src/node/local_testnet/indexer_api.rs     |  10 +-
     .../aptos/src/node/local_testnet/logging.rs   | 102 ++
     crates/aptos/src/node/local_testnet/mod.rs    |  19 +-
     crates/aptos/src/node/local_testnet/node.rs   |  19 +-
     .../aptos/src/node/local_testnet/postgres.rs  |  27 +-
     .../src/node/local_testnet/processors.rs      |  76 +-
     .../src/node/local_testnet/ready_server.rs    |  10 +-
     crates/aptos/src/node/local_testnet/traits.rs | 106 ++
     crates/aptos/src/node/local_testnet/utils.rs  |  15 +
     crates/aptos/src/node/mod.rs                  |  39 +-
     crates/aptos/src/op/key.rs                    | 398 ++++++++
     crates/aptos/src/op/mod.rs                    |   4 +
     crates/aptos/src/stake/mod.rs                 | 668 ++++++++++++
     crates/aptos/src/test/mod.rs                  |  63 +-
     crates/aptos/src/test/tests.rs                | 146 +++
     crates/aptos/src/update/helpers.rs            |  77 ++
     crates/aptos/src/update/mod.rs                | 108 +-
     crates/aptos/src/update/tool.rs               | 145 +++
     testsuite/forge/Cargo.toml                    |   2 +-
     testsuite/smoke-test/Cargo.toml               |   2 +-
     testsuite/testcases/Cargo.toml                |   2 +-
     94 files changed, 7305 insertions(+), 2437 deletions(-)
     create mode 100644 crates/aptos/README.md
     create mode 100644 crates/aptos/build.rs
     create mode 100644 crates/aptos/debug-move-example/Move.toml
     create mode 100644 crates/aptos/debug-move-example/sources/DebugDemo.move
     create mode 100644 crates/aptos/e2e/README.md
     create mode 100644 crates/aptos/e2e/cases/__init__.py
     create mode 100644 crates/aptos/e2e/cases/config.py
     create mode 100644 crates/aptos/e2e/cases/move.py
     create mode 100644 crates/aptos/e2e/cases/node.py
     create mode 100644 crates/aptos/e2e/common.py
     create mode 100755 crates/aptos/e2e/linter.sh
     create mode 100644 crates/aptos/e2e/test_results.py
     create mode 100755 crates/aptos/scripts/check_dynamic_deps.sh
     create mode 100644 crates/aptos/src/account/create.rs
     create mode 100644 crates/aptos/src/account/create_resource_account.rs
     create mode 100644 crates/aptos/src/account/derive_resource_account.rs
     create mode 100644 crates/aptos/src/account/fund.rs
     create mode 100644 crates/aptos/src/account/key_rotation.rs
     create mode 100644 crates/aptos/src/account/list.rs
     create mode 100644 crates/aptos/src/account/transfer.rs
     create mode 100644 crates/aptos/src/config/mod.rs
     create mode 100644 crates/aptos/src/ffi.rs
     create mode 100644 crates/aptos/src/genesis/git.rs
     create mode 100644 crates/aptos/src/genesis/keys.rs
     create mode 100644 crates/aptos/src/genesis/tests.rs
     create mode 100644 crates/aptos/src/genesis/tools.rs
     create mode 100644 crates/aptos/src/governance/utils.rs
     create mode 100644 crates/aptos/src/lib.rs
     create mode 100644 crates/aptos/src/move_tool/aptos_debug_natives.rs
     create mode 100644 crates/aptos/src/move_tool/aptos_dep_example/README.md
     create mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml
     create mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move
     create mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml
     create mode 100644 crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move
     create mode 100644 crates/aptos/src/move_tool/coverage.rs
     create mode 100644 crates/aptos/src/move_tool/disassembler.rs
     create mode 100644 crates/aptos/src/move_tool/manifest.rs
     create mode 100644 crates/aptos/src/move_tool/package_hooks.rs
     create mode 100644 crates/aptos/src/move_tool/stored_package.rs
     create mode 100644 crates/aptos/src/node/analyze/fetch_metadata.rs
     create mode 100644 crates/aptos/src/node/analyze/mod.rs
     create mode 100644 crates/aptos/src/node/local_testnet/health_checker.rs
     create mode 100644 crates/aptos/src/node/local_testnet/logging.rs
     create mode 100644 crates/aptos/src/node/local_testnet/traits.rs
     create mode 100644 crates/aptos/src/node/local_testnet/utils.rs
     create mode 100644 crates/aptos/src/op/key.rs
     create mode 100644 crates/aptos/src/op/mod.rs
     create mode 100644 crates/aptos/src/stake/mod.rs
     create mode 100644 crates/aptos/src/test/tests.rs
     create mode 100644 crates/aptos/src/update/helpers.rs
     create mode 100644 crates/aptos/src/update/tool.rs
    
    diff --git a/Cargo.lock b/Cargo.lock
    index 9b643f5da2ade..dd9078fbbc623 100644
    --- a/Cargo.lock
    +++ b/Cargo.lock
    @@ -110,7 +110,6 @@ dependencies = [
      "cfg-if",
      "getrandom 0.2.15",
      "once_cell",
    - "serde",
      "version_check",
      "zerocopy",
     ]
    @@ -230,97 +229,6 @@ dependencies = [
      "backtrace",
     ]
     
    -[[package]]
    -name = "aptos"
    -version = "3.4.1"
    -dependencies = [
    - "anyhow",
    - "aptos-api-types",
    - "aptos-backup-cli",
    - "aptos-bitvec",
    - "aptos-build-info",
    - "aptos-cached-packages",
    - "aptos-cli-common",
    - "aptos-config",
    - "aptos-crypto",
    - "aptos-faucet-core",
    - "aptos-framework",
    - "aptos-gas-profiling",
    - "aptos-gas-schedule",
    - "aptos-genesis",
    - "aptos-github-client",
    - "aptos-global-constants",
    - "aptos-indexer-grpc-server-framework",
    - "aptos-indexer-grpc-utils",
    - "aptos-keygen",
    - "aptos-ledger",
    - "aptos-logger",
    - "aptos-move-debugger",
    - "aptos-network-checker",
    - "aptos-node",
    - "aptos-protos 1.3.0",
    - "aptos-rest-client",
    - "aptos-sdk",
    - "aptos-storage-interface",
    - "aptos-telemetry",
    - "aptos-temppath",
    - "aptos-types",
    - "aptos-vm",
    - "aptos-vm-genesis",
    - "aptos-vm-logging",
    - "aptos-vm-types",
    - "async-trait",
    - "base64 0.13.1",
    - "bcs 0.1.4",
    - "bollard",
    - "chrono",
    - "clap 4.5.8",
    - "clap_complete",
    - "dashmap",
    - "diesel",
    - "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)",
    - "dirs",
    - "futures",
    - "hex",
    - "itertools 0.12.1",
    - "jemallocator",
    - "maplit",
    - "move-binary-format",
    - "move-bytecode-source-map",
    - "move-cli",
    - "move-command-line-common",
    - "move-compiler",
    - "move-core-types",
    - "move-coverage",
    - "move-disassembler",
    - "move-ir-types",
    - "move-model",
    - "move-package",
    - "move-symbol-pool",
    - "move-unit-test",
    - "move-vm-runtime",
    - "pathsearch",
    - "poem",
    - "processor",
    - "rand 0.7.3",
    - "reqwest",
    - "self_update",
    - "serde",
    - "serde_json",
    - "serde_yaml 0.8.26",
    - "server-framework",
    - "shadow-rs",
    - "tempfile",
    - "thiserror",
    - "tokio",
    - "toml 0.7.8",
    - "tonic 0.11.0",
    - "tracing",
    - "tracing-subscriber 0.3.18",
    - "url",
    - "version-compare",
    -]
    -
     [[package]]
     name = "aptos-abstract-gas-usage"
     version = "0.1.0"
    @@ -356,7 +264,7 @@ dependencies = [
      "aptos-logger",
      "aptos-runtimes",
      "aptos-storage-interface",
    - "aptos-system-utils 0.1.0",
    + "aptos-system-utils",
      "aptos-types",
      "bcs 0.1.4",
      "http 0.2.12",
    @@ -960,10 +868,10 @@ dependencies = [
      "byteorder",
      "bytes",
      "criterion",
    - "curve25519-dalek 3.2.0",
    + "curve25519-dalek",
      "curve25519-dalek-ng",
      "digest 0.9.0",
    - "ed25519-dalek 1.0.1",
    + "ed25519-dalek",
      "ff",
      "hex",
      "hkdf 0.10.0",
    @@ -1388,7 +1296,7 @@ dependencies = [
      "aptos-logger",
      "aptos-metrics-core",
      "aptos-node-resource-metrics",
    - "aptos-profiler 0.1.0",
    + "aptos-profiler",
      "aptos-push-metrics",
      "aptos-sdk",
      "aptos-storage-interface",
    @@ -1630,7 +1538,6 @@ version = "0.0.0"
     dependencies = [
      "again",
      "anyhow",
    - "aptos",
      "aptos-cached-packages",
      "aptos-cli-common",
      "aptos-config",
    @@ -1660,6 +1567,7 @@ dependencies = [
      "json-patch",
      "k8s-openapi",
      "kube",
    + "movement",
      "num_cpus",
      "once_cell",
      "prometheus-http-query",
    @@ -1928,7 +1836,7 @@ dependencies = [
      "aptos-indexer-grpc-server-framework",
      "aptos-indexer-grpc-utils",
      "aptos-metrics-core",
    - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)",
    + "aptos-moving-average",
      "aptos-protos 1.3.0",
      "async-trait",
      "clap 4.5.8",
    @@ -1955,7 +1863,7 @@ dependencies = [
      "aptos-indexer-grpc-server-framework",
      "aptos-indexer-grpc-utils",
      "aptos-metrics-core",
    - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)",
    + "aptos-moving-average",
      "aptos-protos 1.3.0",
      "aptos-transaction-filter",
      "async-trait",
    @@ -1983,7 +1891,7 @@ dependencies = [
      "aptos-indexer-grpc-server-framework",
      "aptos-indexer-grpc-utils",
      "aptos-metrics-core",
    - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)",
    + "aptos-moving-average",
      "async-trait",
      "clap 4.5.8",
      "futures",
    @@ -2017,7 +1925,7 @@ dependencies = [
      "aptos-mempool",
      "aptos-mempool-notifications",
      "aptos-metrics-core",
    - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)",
    + "aptos-moving-average",
      "aptos-proptest-helpers",
      "aptos-protos 1.3.0",
      "aptos-runtimes",
    @@ -2096,7 +2004,7 @@ version = "1.0.0"
     dependencies = [
      "anyhow",
      "aptos-metrics-core",
    - "aptos-system-utils 0.1.0",
    + "aptos-system-utils",
      "async-trait",
      "backtrace",
      "clap 4.5.8",
    @@ -2351,7 +2259,7 @@ dependencies = [
      "bcs 0.1.4",
      "blstrs",
      "derivation-path",
    - "ed25519-dalek 1.0.1",
    + "ed25519-dalek",
      "hex",
      "hmac 0.12.1",
      "jsonwebtoken 8.3.0",
    @@ -2672,14 +2580,6 @@ dependencies = [
      "chrono",
     ]
     
    -[[package]]
    -name = "aptos-moving-average"
    -version = "0.1.0"
    -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d"
    -dependencies = [
    - "chrono",
    -]
    -
     [[package]]
     name = "aptos-mvhashmap"
     version = "0.1.0"
    @@ -3158,19 +3058,6 @@ dependencies = [
      "regex",
     ]
     
    -[[package]]
    -name = "aptos-profiler"
    -version = "0.1.0"
    -source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93"
    -dependencies = [
    - "anyhow",
    - "backtrace",
    - "jemalloc-sys",
    - "jemallocator",
    - "pprof",
    - "regex",
    -]
    -
     [[package]]
     name = "aptos-proptest-helpers"
     version = "0.1.0"
    @@ -3182,19 +3069,20 @@ dependencies = [
     
     [[package]]
     name = "aptos-protos"
    -version = "1.3.0"
    +version = "1.1.2"
    +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=af0dcea7144225a709e4f595e58f8026b99e901c#af0dcea7144225a709e4f595e58f8026b99e901c"
     dependencies = [
      "futures-core",
      "pbjson",
      "prost 0.12.6",
    + "prost-types 0.12.6",
      "serde",
    - "tonic 0.11.0",
    + "tonic 0.10.2",
     ]
     
     [[package]]
     name = "aptos-protos"
     version = "1.3.0"
    -source = "git+https://github.com/aptos-labs/aptos-core.git?tag=aptos-node-v1.12.1#4b9a2593facaee92b28df2e99b2773a7e4f930f5"
     dependencies = [
      "futures-core",
      "pbjson",
    @@ -3238,7 +3126,6 @@ name = "aptos-release-builder"
     version = "0.1.0"
     dependencies = [
      "anyhow",
    - "aptos",
      "aptos-api-types",
      "aptos-build-info",
      "aptos-crypto",
    @@ -3259,6 +3146,7 @@ dependencies = [
      "hex",
      "move-core-types",
      "move-model",
    + "movement",
      "once_cell",
      "reqwest",
      "serde",
    @@ -3381,11 +3269,11 @@ name = "aptos-rosetta-cli"
     version = "0.0.1"
     dependencies = [
      "anyhow",
    - "aptos",
      "aptos-logger",
      "aptos-rosetta",
      "aptos-types",
      "clap 4.5.8",
    + "movement",
      "serde",
      "serde_json",
      "tokio",
    @@ -3737,27 +3625,7 @@ name = "aptos-system-utils"
     version = "0.1.0"
     dependencies = [
      "anyhow",
    - "aptos-profiler 0.1.0",
    - "async-mutex",
    - "http 0.2.12",
    - "hyper",
    - "lazy_static",
    - "mime",
    - "pprof",
    - "regex",
    - "rstack-self",
    - "tokio",
    - "tracing",
    - "url",
    -]
    -
    -[[package]]
    -name = "aptos-system-utils"
    -version = "0.1.0"
    -source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93"
    -dependencies = [
    - "anyhow",
    - "aptos-profiler 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)",
    + "aptos-profiler",
      "async-mutex",
      "http 0.2.12",
      "hyper",
    @@ -3881,7 +3749,6 @@ name = "aptos-testcases"
     version = "0.0.0"
     dependencies = [
      "anyhow",
    - "aptos",
      "aptos-config",
      "aptos-forge",
      "aptos-global-constants",
    @@ -3900,6 +3767,7 @@ dependencies = [
      "futures",
      "hex",
      "itertools 0.12.1",
    + "movement",
      "rand 0.7.3",
      "reqwest",
      "tokio",
    @@ -6553,33 +6421,6 @@ dependencies = [
      "zeroize",
     ]
     
    -[[package]]
    -name = "curve25519-dalek"
    -version = "4.1.3"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
    -dependencies = [
    - "cfg-if",
    - "cpufeatures",
    - "curve25519-dalek-derive",
    - "digest 0.10.7",
    - "fiat-crypto",
    - "rustc_version",
    - "subtle",
    - "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.68",
    -]
    -
     [[package]]
     name = "curve25519-dalek-ng"
     version = "4.1.1"
    @@ -6933,6 +6774,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be"
     dependencies = [
      "async-trait",
    + "bb8",
      "diesel",
      "futures-util",
      "scoped-futures",
    @@ -7216,24 +7058,14 @@ dependencies = [
      "signature 1.6.4",
     ]
     
    -[[package]]
    -name = "ed25519"
    -version = "2.2.3"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
    -dependencies = [
    - "pkcs8 0.10.2",
    - "signature 2.2.0",
    -]
    -
     [[package]]
     name = "ed25519-dalek"
     version = "1.0.1"
     source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
     dependencies = [
    - "curve25519-dalek 3.2.0",
    - "ed25519 1.5.3",
    + "curve25519-dalek",
    + "ed25519",
      "rand 0.7.3",
      "serde",
      "serde_bytes",
    @@ -7241,21 +7073,6 @@ dependencies = [
      "zeroize",
     ]
     
    -[[package]]
    -name = "ed25519-dalek"
    -version = "2.1.1"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
    -dependencies = [
    - "curve25519-dalek 4.1.3",
    - "ed25519 2.2.3",
    - "serde",
    - "sha2 0.10.8",
    - "signature 2.2.0",
    - "subtle",
    - "zeroize",
    -]
    -
     [[package]]
     name = "ed25519-dalek-bip32"
     version = "0.2.0"
    @@ -7263,7 +7080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
     checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908"
     dependencies = [
      "derivation-path",
    - "ed25519-dalek 1.0.1",
    + "ed25519-dalek",
      "hmac 0.12.1",
      "sha2 0.10.8",
     ]
    @@ -7707,12 +7524,6 @@ dependencies = [
      "syn 1.0.109",
     ]
     
    -[[package]]
    -name = "fiat-crypto"
    -version = "0.2.9"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
    -
     [[package]]
     name = "field_count"
     version = "0.1.1"
    @@ -9522,16 +9333,6 @@ dependencies = [
      "serde_json",
     ]
     
    -[[package]]
    -name = "kanal"
    -version = "0.1.0-pre8"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "b05d55519627edaf7fd0f29981f6dc03fb52df3f5b257130eb8d0bf2801ea1d7"
    -dependencies = [
    - "futures-core",
    - "lock_api",
    -]
    -
     [[package]]
     name = "keccak"
     version = "0.1.5"
    @@ -11146,6 +10947,94 @@ dependencies = [
      "triomphe",
     ]
     
    +[[package]]
    +name = "movement"
    +version = "2.4.0"
    +dependencies = [
    + "anyhow",
    + "aptos-api-types",
    + "aptos-backup-cli",
    + "aptos-bitvec",
    + "aptos-build-info",
    + "aptos-cached-packages",
    + "aptos-cli-common",
    + "aptos-config",
    + "aptos-crypto",
    + "aptos-faucet-core",
    + "aptos-framework",
    + "aptos-gas-profiling",
    + "aptos-gas-schedule",
    + "aptos-genesis",
    + "aptos-github-client",
    + "aptos-global-constants",
    + "aptos-indexer-grpc-utils",
    + "aptos-keygen",
    + "aptos-ledger",
    + "aptos-logger",
    + "aptos-network-checker",
    + "aptos-node",
    + "aptos-protos 1.3.0",
    + "aptos-rest-client",
    + "aptos-sdk",
    + "aptos-storage-interface",
    + "aptos-temppath",
    + "aptos-types",
    + "aptos-vm",
    + "aptos-vm-genesis",
    + "async-trait",
    + "base64 0.13.1",
    + "bcs 0.1.4",
    + "bollard",
    + "chrono",
    + "clap 4.5.8",
    + "clap_complete",
    + "codespan-reporting",
    + "dashmap",
    + "diesel",
    + "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)",
    + "dirs",
    + "futures",
    + "hex",
    + "itertools 0.12.1",
    + "jemallocator",
    + "maplit",
    + "move-binary-format",
    + "move-bytecode-source-map",
    + "move-cli",
    + "move-command-line-common",
    + "move-compiler",
    + "move-core-types",
    + "move-coverage",
    + "move-disassembler",
    + "move-ir-types",
    + "move-package",
    + "move-symbol-pool",
    + "move-unit-test",
    + "move-vm-runtime",
    + "once_cell",
    + "poem",
    + "processor",
    + "rand 0.7.3",
    + "regex",
    + "reqwest",
    + "self_update",
    + "serde",
    + "serde_json",
    + "serde_yaml 0.8.26",
    + "server-framework",
    + "shadow-rs",
    + "tempfile",
    + "termcolor",
    + "thiserror",
    + "tokio",
    + "toml 0.7.8",
    + "tonic 0.11.0",
    + "tracing",
    + "tracing-subscriber 0.3.18",
    + "version-compare",
    + "walkdir",
    +]
    +
     [[package]]
     name = "multer"
     version = "2.1.0"
    @@ -11950,16 +11839,6 @@ dependencies = [
      "camino",
     ]
     
    -[[package]]
    -name = "pathsearch"
    -version = "0.2.0"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "da983bc5e582ab17179c190b4b66c7d76c5943a69c6d34df2a2b6bf8a2977b05"
    -dependencies = [
    - "anyhow",
    - "libc",
    -]
    -
     [[package]]
     name = "pbjson"
     version = "0.5.1"
    @@ -12469,19 +12348,6 @@ dependencies = [
      "ark-std",
     ]
     
    -[[package]]
    -name = "postgres-native-tls"
    -version = "0.5.0"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954"
    -dependencies = [
    - "futures",
    - "native-tls",
    - "tokio",
    - "tokio-native-tls",
    - "tokio-postgres",
    -]
    -
     [[package]]
     name = "postgres-protocol"
     version = "0.6.6"
    @@ -12741,12 +12607,11 @@ dependencies = [
     [[package]]
     name = "processor"
     version = "1.0.0"
    -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d"
    +source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf#4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf"
     dependencies = [
    - "ahash 0.8.11",
      "anyhow",
    - "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d)",
    - "aptos-protos 1.3.0 (git+https://github.com/aptos-labs/aptos-core.git?tag=aptos-node-v1.12.1)",
    + "aptos-moving-average",
    + "aptos-protos 1.1.2",
      "async-trait",
      "base64 0.13.1",
      "bcs 0.1.4",
    @@ -12754,7 +12619,7 @@ dependencies = [
      "chrono",
      "clap 4.5.8",
      "diesel",
    - "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)",
    + "diesel-async 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
      "diesel_async_migrations",
      "diesel_migrations",
      "enum_dispatch",
    @@ -12765,13 +12630,7 @@ dependencies = [
      "google-cloud-googleapis",
      "google-cloud-pubsub",
      "hex",
    - "itertools 0.12.1",
    - "jemallocator",
    - "kanal",
    - "native-tls",
    - "num_cpus",
      "once_cell",
    - "postgres-native-tls",
      "prometheus",
      "prost 0.12.6",
      "prost-types 0.12.6",
    @@ -12783,8 +12642,7 @@ dependencies = [
      "sha3 0.9.1",
      "strum 0.24.1",
      "tokio",
    - "tokio-postgres",
    - "tonic 0.11.0",
    + "tonic 0.10.2",
      "tracing",
      "unescape",
      "url",
    @@ -14068,8 +13926,9 @@ dependencies = [
     
     [[package]]
     name = "self_update"
    -version = "0.39.0"
    -source = "git+https://github.com/banool/self_update.git?rev=8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b#8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b"
    +version = "0.38.0"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "2b3c585a1ced6b97ac13bd5e56f66559e5a75f477da5913f70df98e114518446"
     dependencies = [
      "hyper",
      "indicatif 0.17.8",
    @@ -14083,7 +13942,6 @@ dependencies = [
      "tempfile",
      "urlencoding",
      "zip",
    - "zipsign-api",
     ]
     
     [[package]]
    @@ -14344,10 +14202,9 @@ dependencies = [
     [[package]]
     name = "server-framework"
     version = "1.0.0"
    -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d"
    +source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf#4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf"
     dependencies = [
      "anyhow",
    - "aptos-system-utils 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)",
      "async-trait",
      "backtrace",
      "clap 4.5.8",
    @@ -14676,7 +14533,6 @@ name = "smoke-test"
     version = "0.1.0"
     dependencies = [
      "anyhow",
    - "aptos",
      "aptos-backup-cli",
      "aptos-bitvec",
      "aptos-cached-packages",
    @@ -14721,6 +14577,7 @@ dependencies = [
      "hex",
      "hyper",
      "move-core-types",
    + "movement",
      "num_cpus",
      "once_cell",
      "rand 0.7.3",
    @@ -15740,6 +15597,38 @@ dependencies = [
      "webpki-roots 0.23.1",
     ]
     
    +[[package]]
    +name = "tonic"
    +version = "0.10.2"
    +source = "registry+https://github.com/rust-lang/crates.io-index"
    +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e"
    +dependencies = [
    + "async-stream",
    + "async-trait",
    + "axum 0.6.20",
    + "base64 0.21.7",
    + "bytes",
    + "flate2",
    + "h2",
    + "http 0.2.12",
    + "http-body",
    + "hyper",
    + "hyper-timeout",
    + "percent-encoding",
    + "pin-project 1.1.5",
    + "prost 0.12.6",
    + "rustls 0.21.12",
    + "rustls-native-certs 0.6.3",
    + "rustls-pemfile 1.0.4",
    + "tokio",
    + "tokio-rustls 0.24.1",
    + "tokio-stream",
    + "tower",
    + "tower-layer",
    + "tower-service",
    + "tracing",
    +]
    +
     [[package]]
     name = "tonic"
     version = "0.11.0"
    @@ -16972,7 +16861,7 @@ name = "x25519-dalek"
     version = "1.2.0"
     source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661"
     dependencies = [
    - "curve25519-dalek 3.2.0",
    + "curve25519-dalek",
      "rand_core 0.5.1",
      "zeroize",
     ]
    @@ -17100,16 +16989,6 @@ dependencies = [
      "time",
     ]
     
    -[[package]]
    -name = "zipsign-api"
    -version = "0.1.1"
    -source = "registry+https://github.com/rust-lang/crates.io-index"
    -checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea"
    -dependencies = [
    - "ed25519-dalek 2.1.1",
    - "thiserror",
    -]
    -
     [[package]]
     name = "zstd"
     version = "0.13.1"
    diff --git a/Cargo.toml b/Cargo.toml
    index 6aefcd6625b68..945de926e2129 100644
    --- a/Cargo.toml
    +++ b/Cargo.toml
    @@ -280,7 +280,7 @@ rust-version = "1.78.0"
     [workspace.dependencies]
     # Internal crate dependencies.
     # Please do not add any test features here: they should be declared by the individual crate.
    -aptos = { path = "crates/aptos" }
    +movement = { path = "crates/aptos" }
     aptos-accumulator = { path = "storage/accumulator" }
     aptos-admin-service = { path = "crates/aptos-admin-service" }
     aptos-aggregator = { path = "aptos-move/aptos-aggregator" }
    diff --git a/aptos-move/aptos-release-builder/Cargo.toml b/aptos-move/aptos-release-builder/Cargo.toml
    index a42cff88383d2..87a64633157c2 100644
    --- a/aptos-move/aptos-release-builder/Cargo.toml
    +++ b/aptos-move/aptos-release-builder/Cargo.toml
    @@ -14,7 +14,7 @@ rust-version = { workspace = true }
     
     [dependencies]
     anyhow = { workspace = true }
    -aptos = { workspace = true, features = [ "no-upload-proposal" ] }
    +movement = { workspace = true, features = [ "no-upload-proposal" ] }
     aptos-api-types = { workspace = true }
     aptos-build-info = { workspace = true }
     aptos-crypto = { workspace = true }
    diff --git a/crates/aptos-rosetta-cli/Cargo.toml b/crates/aptos-rosetta-cli/Cargo.toml
    index ff37ec922ae04..7f5cda8bd27b8 100644
    --- a/crates/aptos-rosetta-cli/Cargo.toml
    +++ b/crates/aptos-rosetta-cli/Cargo.toml
    @@ -14,7 +14,7 @@ rust-version = { workspace = true }
     
     [dependencies]
     anyhow = { workspace = true }
    -aptos = { workspace = true }
    +movement = { workspace = true }
     aptos-logger = { workspace = true }
     aptos-rosetta = { workspace = true }
     aptos-types = { workspace = true }
    diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md
    index c5afa9e567e26..1c5857e78aaea 100644
    --- a/crates/aptos/CHANGELOG.md
    +++ b/crates/aptos/CHANGELOG.md
    @@ -1,84 +1,44 @@
    -# Aptos CLI Changelog
    +# Movement CLI Changelog
     
    -All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
    +All notable changes to the Movement CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
     
    -## Unreleased
    -- Add balance command to easily get account balances for APT currently
    -
    -## [3.4.1] - 2024/05/31
    -- Upgraded indexer processors for localnet from ca60e51b53c3be6f9517de7c73d4711e9c1f7236 to 5244b84fa5ed872e5280dc8df032d744d62ad29d. Upgraded Hasura metadata accordingly.
    -
    -## [3.4.0] - 2024/05/30
    -- Adds a check for safe usage of randomness features. Public functions are not allowed to call randomness features unless explicitly allowed via attribute `#[lint::allow_unsafe_randomness]`.
    -- The Move syntax now supports structured attribute names, as in `#[attribute_area::attribute_name]`.
    -- Upgraded indexer processors for localnet from a11f0b6532349aa6b9a80c9a1d77524f02d8a013 to ca60e51b53c3be6f9517de7c73d4711e9c1f7236. Upgraded Hasura metadata accordingly.
    -
    -## [3.3.1] - 2024/05/21
    -- Fixed incompatibility bug that broken local simulation and gas profiling.
    -
    -## [3.3.0] - 2024/05/03
    -- **Breaking Change** Update View functions to use BCS for submission.  Allows for all arguments to be supported in view functions.  Note some input arguments that were previously inputted as strings may be handled differently.
    -- [Early beta release of the Move compiler v2](https://aptos.dev/move/compiler_v2/) is now accessible through the CLI. We now allow specifying the Move compiler version and the Move language version via the CLI.
    -
    -## [3.2.0] - 2024/03/29
    -- Renamed `run-local-testnet` to `run-localnet`. `run-local-testnet` is still supported for backwards compatibility.
    -- Updated localnet node to use latest code changes including long pull
    +Note, all changes listed before version 2.4.0 were for Aptos CLI, from which Movement CLI was derived.
     
    -## [3.1.0] - 2024/03/21
    -- Update `self_update` dependency to support situations where relevant directories (e.g. `/tmp`) exist on different filesystems.
    -- [bugfix] Rename `--value` back to `--override-size-check` for publishing packages
    -- Upgraded indexer processors for localnet from cc764f83e26aed1d83ccad0cee3ab579792a0538. This adds support for the `TransactionMetadataProcessor` among other improvements.
    -
    -## [3.0.2] - 2024/03/12
    -- Increased `max_connections` for postgres container created as part of localnet to address occasional startup failures due to overloaded DB.
    -
    -## [3.0.1] - 2024/03/05
    -- Fix bug in `aptos update revela` if default install directory doesn't exist.
    -
    -## [3.0.0] - 2024/03/05
    -- **Breaking Change**: `aptos update` is now `aptos update aptos`.
    -- Added `aptos update revela`. This installs / updates the `revela` binary, which is needed for the new `aptos move decompile` subcommand.
    -- Extended `aptos move download` with an option `--bytecode` to also download the bytecode of a module
    -- Integrated the Revela decompiler which is now available via `aptos move decompile`
    -- Extended `aptos move disassemble` and the new `aptos move decompile` to also work on entire packages instead of only single files
    -
    -## [2.5.0] - 2024/02/27
    +## Unreleased
     - Updated CLI source compilation to use rust toolchain version 1.75.0 (from 1.74.1).
    -- Upgraded indexer processors for localnet from 9936ec73cef251fb01fd2c47412e064cad3975c2 to d44b2d209f57872ac593299c34751a5531b51352. Upgraded Hasura metadata accordingly.
    -- Added support for objects processor in localnet and enabled it by default.
     
    -## [2.4.0] - 2024/01/05
    +## [2.4.0] - 2023/01/05
     - Hide the V2 compiler from input options until the V2 compiler is ready for release
     - Updated CLI source compilation to use rust toolchain version 1.74.1 (from 1.72.1).
    -- Added `for` loop.
    +- Added `for` loop. 
       - Syntax: `for (iter in lower_bound..upper_bound) { loop_body }` with integer bounds.
       - Documentation: https://aptos.dev/move/book/loops
    -- Upgraded indexer processors for localnet from 2d5cb211a89a8705674e9e1e741c841dd899c558 to 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. Upgraded Hasura metadata accordingly.
    +- Upgraded indexer processors for local testnet from 2d5cb211a89a8705674e9e1e741c841dd899c558 to 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. Upgraded Hasura metadata accordingly.
     - Upgraded Hasura GraphQL engine image from 2.35.0 to 2.36.1.
     
     ## [2.3.2] - 2023/11/28
    -- Services in the localnet now bind to 127.0.0.1 by default (unless the CLI is running inside a container, which most users should not do) rather than 0.0.0.0. You can override this behavior with the `--bind-to` flag. This fixes an issue preventing the localnet from working on Windows.
    +- Services in the local testnet now bind to 127.0.0.1 by default (unless the CLI is running inside a container, which most users should not do) rather than 0.0.0.0. You can override this behavior with the `--bind-to` flag. This fixes an issue preventing the local testnet from working on Windows.
     
     ## [2.3.1] - 2023/11/07
     ### Updated
    -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to 2d5cb211a89a8705674e9e1e741c841dd899c558.
    -- Improved reliability of inter-container networking with localnet.
    +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to 2d5cb211a89a8705674e9e1e741c841dd899c558. 
    +- Improved reliability of inter-container networking with local testnet.
     
     ## [2.3.0] - 2023/10/25
     ### Added
     - Added `--node-api-key`. This lets you set an API key for the purpose of not being ratelimited.
     
     ### Updated
    -- Made the localnet exit more quickly if a service fails to start.
    -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to bcba94c26c8a6372056d2b69ce411c5719f98965.
    +- Made the local testnet exit more quickly if a service fails to start.
    +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to bcba94c26c8a6372056d2b69ce411c5719f98965.
     
     ### Fixed
    -- Fixed an infrequent bug that caused startup failures for the localnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage.
    +- Fixed an infrequent bug that caused startup failures for the local testnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage.
     - Fixed an issue where the CLI could not find the Docker socket with some Docker Desktop configurations.
     
     ## [2.2.2] - 2023/10/16
     ### Updated
    -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to d6f55d4baba32960ea7be60878552e73ffbe8b7e.
    +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to d6f55d4baba32960ea7be60878552e73ffbe8b7e.
     
     ## [2.2.1] - 2023/10/13
     ### Fixed
    @@ -86,7 +46,7 @@ All notable changes to the Aptos CLI will be captured in this file. This project
     
     ## [2.2.0] - 2023/10/11
     ### Added
    -- Added `--with-indexer-api` to `aptos node run-local-testnet`. With this flag you can run a full processor + indexer API stack as part of your localnet. You must have Docker installed to use this feature. For more information, see https://aptos.dev/nodes/local-testnet/local-testnet-index.
    +- Added `--with-indexer-api` to `aptos node run-local-testnet`. With this flag you can run a full processor + indexer API stack as part of your local testnet. You must have Docker installed to use this feature. For more information, see https://aptos.dev/nodes/local-testnet/local-testnet-index.
     ### Updated
     - Updated CLI source compilation to use rust toolchain version 1.72.1 (from 1.71.1).
     
    @@ -132,7 +92,7 @@ All notable changes to the Aptos CLI will be captured in this file. This project
     
     ## [2.0.1] - 2023/06/05
     ### Fixed
    -- Updated txn expiration configuration for the faucet built into the CLI to make localnet startup more reliable.
    +- Updated txn expiration configuration for the faucet built into the CLI to make local testnet startup more reliable.
     
     ## [2.0.0] - 2023/06/01
     ### Added
    diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml
    index 7c5d986e6710e..592cf46f0802a 100644
    --- a/crates/aptos/Cargo.toml
    +++ b/crates/aptos/Cargo.toml
    @@ -1,7 +1,7 @@
     [package]
    -name = "aptos"
    -description = "Aptos tool for management of nodes and interacting with the blockchain"
    -version = "3.4.1"
    +name = "movement"
    +description = "Movement tool for management of nodes and interacting with Movement"
    +version = "2.4.0"
     
     # Workspace inherited keys
     authors = { workspace = true }
    @@ -29,25 +29,22 @@ aptos-gas-schedule = { workspace = true }
     aptos-genesis = { workspace = true }
     aptos-github-client = { workspace = true }
     aptos-global-constants = { workspace = true }
    -aptos-indexer-grpc-server-framework = { workspace = true }
     aptos-indexer-grpc-utils = { workspace = true }
     aptos-keygen = { workspace = true }
     aptos-ledger = { workspace = true }
     aptos-logger = { workspace = true }
    -aptos-move-debugger = { workspace = true }
    +# aptos-move-debugger = { workspace = true }
     aptos-network-checker = { workspace = true }
     aptos-node = { workspace = true }
     aptos-protos = { workspace = true }
     aptos-rest-client = { workspace = true }
     aptos-sdk = { workspace = true }
     aptos-storage-interface = { workspace = true }
    -aptos-telemetry = { workspace = true }
    +# aptos-telemetry = { workspace = true }
     aptos-temppath = { workspace = true }
     aptos-types = { workspace = true }
     aptos-vm = { workspace = true, features = ["testing"] }
     aptos-vm-genesis = { workspace = true }
    -aptos-vm-logging = { workspace = true }
    -aptos-vm-types = { workspace = true }
     async-trait = { workspace = true }
     base64 = { workspace = true }
     bcs = { workspace = true }
    @@ -55,6 +52,7 @@ bollard = { workspace = true }
     chrono = { workspace = true }
     clap = { workspace = true, features = ["env", "unstable-styles"] }
     clap_complete = { workspace = true }
    +codespan-reporting = { workspace = true }
     dashmap = { workspace = true }
     diesel = { workspace = true, features = [
         "postgres_backend",
    @@ -74,32 +72,31 @@ move-core-types = { workspace = true }
     move-coverage = { workspace = true }
     move-disassembler = { workspace = true }
     move-ir-types = { workspace = true }
    -move-model = { workspace = true }
     move-package = { workspace = true }
     move-symbol-pool = { workspace = true }
    -move-unit-test = { workspace = true, features = ["debugging"] }
    -move-vm-runtime = { workspace = true, features = ["testing"] }
    -pathsearch = { workspace = true }
    +move-unit-test = { workspace = true, features = [ "debugging" ] }
    +move-vm-runtime = { workspace = true, features = [ "testing" ] }
    +once_cell = { workspace = true }
     poem = { workspace = true }
    -# We set default-features to false so we don't onboard the libpq dep. See more here:
    -# https://github.com/aptos-labs/aptos-core/pull/12568
    -processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "5244b84fa5ed872e5280dc8df032d744d62ad29d", default-features = false }
    +processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" }
     rand = { workspace = true }
    +regex = { workspace = true }
     reqwest = { workspace = true }
    -self_update = { git = "https://github.com/banool/self_update.git", rev = "8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b", features = ["archive-zip", "compression-zip-deflate"] }
    +self_update = { version = "0.38.0", features = ["archive-zip", "compression-zip-deflate"] }
     serde = { workspace = true }
     serde_json = { workspace = true }
     serde_yaml = { workspace = true }
    -server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "5244b84fa5ed872e5280dc8df032d744d62ad29d" }
    +server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" }
     tempfile = { workspace = true }
    +termcolor = { workspace = true }
     thiserror = { workspace = true }
     tokio = { workspace = true }
     toml = { workspace = true }
     tonic = { workspace = true }
     tracing = { workspace = true }
     tracing-subscriber = { workspace = true }
    -url = { workspace = true }
     version-compare = { workspace = true }
    +walkdir = { workspace = true }
     
     [target.'cfg(unix)'.dependencies]
     jemallocator = { workspace = true }
    @@ -108,7 +105,7 @@ jemallocator = { workspace = true }
     default = []
     fuzzing = []
     no-upload-proposal = []
    -indexer = ["aptos-node/indexer"]
    +# indexer = ["aptos-node/indexer"]
     cli-framework-test-move = []
     
     [build-dependencies]
    diff --git a/crates/aptos/README.md b/crates/aptos/README.md
    new file mode 100644
    index 0000000000000..42b451e3bc703
    --- /dev/null
    +++ b/crates/aptos/README.md
    @@ -0,0 +1,5 @@
    +# Movement Command Line Interface (CLI) Tool
    +
    +The `movement` tool is a command line interface (CLI) for debugging, development, and node operation.
    +
    +See [Movement CLI Documentation](https://docs.movementlabs.xyz) for how to install the `movement` CLI tool and how to use it.
    diff --git a/crates/aptos/build.rs b/crates/aptos/build.rs
    new file mode 100644
    index 0000000000000..6cc52885b350d
    --- /dev/null
    +++ b/crates/aptos/build.rs
    @@ -0,0 +1,6 @@
    +// Copyright © Aptos Foundation
    +// SPDX-License-Identifier: Apache-2.0
    +
    +fn main() -> shadow_rs::SdResult<()> {
    +    shadow_rs::new()
    +}
    diff --git a/crates/aptos/debug-move-example/Move.toml b/crates/aptos/debug-move-example/Move.toml
    new file mode 100644
    index 0000000000000..80571522a0665
    --- /dev/null
    +++ b/crates/aptos/debug-move-example/Move.toml
    @@ -0,0 +1,9 @@
    +[package]
    +name = "DebugDemo"
    +version = "0.0.0"
    +
    +[addresses]
    +DebugDemo = "0x1"
    +
    +[dependencies]
    +AptosFramework = { local = "../../../aptos-move/framework/aptos-framework" }
    diff --git a/crates/aptos/debug-move-example/sources/DebugDemo.move b/crates/aptos/debug-move-example/sources/DebugDemo.move
    new file mode 100644
    index 0000000000000..03a4254064da6
    --- /dev/null
    +++ b/crates/aptos/debug-move-example/sources/DebugDemo.move
    @@ -0,0 +1,32 @@
    +module DebugDemo::Message {
    +    use std::string;
    +    use std::signer;
    +    use aptos_std::debug;
    +
    +    struct MessageHolder has key {
    +        message: string::String,
    +    }
    +
    +
    +    public entry fun set_message(account: signer, message_bytes: vector)
    +    acquires MessageHolder {
    +        debug::print_stack_trace();
    +        let message = string::utf8(message_bytes);
    +        let account_addr = signer::address_of(&account);
    +        if (!exists(account_addr)) {
    +            move_to(&account, MessageHolder {
    +                message,
    +            })
    +        } else {
    +            let old_message_holder = borrow_global_mut(account_addr);
    +            old_message_holder.message = message;
    +        }
    +    }
    +
    +    #[test(account = @0x1)]
    +    public entry fun sender_can_set_message(account: signer) acquires MessageHolder {
    +        let addr = signer::address_of(&account);
    +        debug::print
    (&addr); + set_message(account, b"Hello, Blockchain"); + } +} diff --git a/crates/aptos/e2e/README.md b/crates/aptos/e2e/README.md new file mode 100644 index 0000000000000..c3bcf71de0cc6 --- /dev/null +++ b/crates/aptos/e2e/README.md @@ -0,0 +1,105 @@ +# CLI test suite +This directory contains Python code to help with running the CLI test suite. + +## Requirements +We use [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management: + +``` +curl -sSL https://install.python-poetry.org | python3 - +``` + +Once you have Poetry, you can install the dependencies for the testing framework like this: +``` +poetry config virtualenvs.in-project true # This helps with IDE integration +poetry install +``` + +To learn how to use the CLI testing framework, run this: +``` +poetry run python main.py -h +``` + +For example, using the CLI from an image: +``` +poetry run python main.py --base-network mainnet --test-cli-tag nightly +``` + +Using the CLI from a local path: +``` +poetry run python main.py -d --base-network mainnet --test-cli-path ~/aptos-core/target/debug/aptos +``` + +## Debugging +If you are get an error message similar to this: + +### CPU architecture +``` +docker: no matching manifest for linux/arm64/v8 in the manifest list entries. +``` + +Try running the poetry command with this env var: +``` +DOCKER_DEFAULT_PLATFORM=linux/amd64 poetry run python main.py --base-network testnet --test-cli-path ~/aptos-core/target/debug/aptos +``` +This makes the docker commands use the x86_64 images since we don't publish images for ARM. + +### CLI config type +If you see an error like this: +``` +Traceback (most recent call last): + File "/Users/dport/a/core/crates/aptos/e2e/main.py", line 194, in + if main(): + File "/Users/dport/a/core/crates/aptos/e2e/main.py", line 156, in main + run_helper.prepare() + File "/Users/dport/a/core/crates/aptos/e2e/test_helpers.py", line 155, in prepare + self.prepare_cli() + File "/Users/dport/a/core/crates/aptos/e2e/test_helpers.py", line 189, in prepare_cli + raise RuntimeError( +RuntimeError: When using --test-cli-path you must use workspace configuration, try running `aptos config set-global-config --config-type workspace` +``` + +It is because you are using the `--test-cli-path` flag but have configured the CLI to use the `global` config type. This is not currently compatible, you must switch the config type to workspace prior to running the E2E tests: +``` +aptos config set-global-config --config-type workspace +``` + +### My test doesn't work +You might be getting output like this: +``` +2023-06-22 20:27:07,533 - ERROR - These tests failed: +2023-06-22 20:27:07,533 - ERROR - test_move_compile_script ... +``` + +The best place to look is the test framework working directory, by default `/tmp/aptos-cli-tests`. + +In this directory you will find lots of useful output. For example, you can see the stdout of one of the commands in a test case like this: +``` +$ cat /tmp/aptos-cli-tests/out/007_test_move_compile_script.stdout +{ + "Error": "Move compilation failed: Unable to resolve packages for package 'TwoByTwoTransfer': While resolving dependency 'AptosFramework' in package 'TwoByTwoTransfer': While processing dependency 'AptosFramework': Unable to find package manifest for 'AptosFramework' at \"Move.toml/move/scripts/two_by_two_transfer/../../../framework/aptos-framework\"" +} +``` + +For each test there are the following files: +- test_name.command +- test_name.stdout +- test_name.stderr + +This file might also be present if the test threw an exception: +- test_name.exception + +## Writing new test cases +To write a new test case, follow these steps: +1. (Optional) Make a new file in [cases/](cases/) if none of the existing files seem appropriate. +1. Write a new function following these guidelines: + 1. Follow the naming scheme `test_*`. + 1. Decorate the function with the `test_case` decorator. + 1. If you want to assert something, do so by raising an exception (TestError has been provided for this purpose, but any old exception does the trick). + 1. Use the `RunHelper` to invoke CLI commands. Follow the example of other test cases. +1. Register the test in the `run_tests` function in [main.py](main.py). Note that the order matters here, later tests are allowed (and encouraged) to depend on the results of earlier tests. This way we can test truly end-to-end, beyond the span of a single invocation. + +## Formatting: +``` +poetry run isort . +poetry run black . +``` diff --git a/crates/aptos/e2e/cases/__init__.py b/crates/aptos/e2e/cases/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/aptos/e2e/cases/account.py b/crates/aptos/e2e/cases/account.py index f4b4e8c02c469..34dba7d71a334 100644 --- a/crates/aptos/e2e/cases/account.py +++ b/crates/aptos/e2e/cases/account.py @@ -10,7 +10,7 @@ @test_case -async def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): +def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): amount_in_octa = 100000000000 # Fund the account. @@ -29,7 +29,7 @@ async def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): # Assert it has the requested balance. balance = int( - await run_helper.api_client.account_balance( + run_helper.api_client.account_balance( run_helper.get_account_info().account_address ) ) @@ -40,7 +40,7 @@ async def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): @test_case -async def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): +def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): # Create the new account. run_helper.run_command( test_name, @@ -56,7 +56,7 @@ async def test_account_create_and_transfer(run_helper: RunHelper, test_name=None # Assert it exists and has zero balance. balance = int( - await run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) + run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) ) if balance != 0: raise TestError( @@ -80,7 +80,7 @@ async def test_account_create_and_transfer(run_helper: RunHelper, test_name=None ) balance = int( - await run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) + run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) ) if balance != transfer_amount: diff --git a/crates/aptos/e2e/cases/config.py b/crates/aptos/e2e/cases/config.py new file mode 100644 index 0000000000000..1ec5aa12e950e --- /dev/null +++ b/crates/aptos/e2e/cases/config.py @@ -0,0 +1,32 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import json + +from common import TestError +from test_helpers import RunHelper +from test_results import test_case + + +@test_case +def test_config_show_profiles(run_helper: RunHelper, test_name=None): + # Show the profile + response = run_helper.run_command( + test_name, + [ + "aptos", + "config", + "show-profiles", + ], + ) + + expected_profile = run_helper.get_account_info() + profile = json.loads(response.stdout)["Result"]["default"] + if ( + profile["has_private_key"] != True + or profile["public_key"] != expected_profile.public_key + or profile["account"] != expected_profile.account_address + ): + raise TestError( + f"[aptos config show-profiles] shows incorrect profile {profile} -- \n expected {expected_profile}" + ) diff --git a/crates/aptos/e2e/cases/init.py b/crates/aptos/e2e/cases/init.py index 682478328f7a2..096e628ada6ad 100644 --- a/crates/aptos/e2e/cases/init.py +++ b/crates/aptos/e2e/cases/init.py @@ -35,12 +35,12 @@ def test_init(run_helper: RunHelper, test_name=None): if not account_info: raise TestError("Failed to read account info from newly created config file") - # Confirm with the localnet that it was created. + # Confirm with the local testnet that it was created. try: run_helper.api_client.account(account_info.account_address) except Exception as e: raise TestError( - f"Failed to query localnet for account {account_info.account_address}" + f"Failed to query local testnet for account {account_info.account_address}" ) from e diff --git a/crates/aptos/e2e/cases/move.py b/crates/aptos/e2e/cases/move.py new file mode 100644 index 0000000000000..712c6ce41f2bf --- /dev/null +++ b/crates/aptos/e2e/cases/move.py @@ -0,0 +1,291 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import json + +from common import TestError +from test_helpers import RunHelper +from test_results import test_case + + +@test_case +def test_move_publish(run_helper: RunHelper, test_name=None): + # Prior to this function running the move/ directory was moved into the working + # directory in the host, which is then mounted into the container. The CLI is + # then run in this directory, meaning the move/ directory is in the same directory + # as the CLI is run from. This is why we can just refer to the package dir starting + # with move/ here. + package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" + + # Publish the module. + run_helper.run_command( + test_name, + [ + "aptos", + "move", + "publish", + "--assume-yes", + "--package-dir", + package_dir, + "--named-addresses", + f"addr={run_helper.get_account_info().account_address}", + ], + ) + + # Get what modules exist on chain. + response = run_helper.run_command( + test_name, + [ + "aptos", + "account", + "list", + "--account", + run_helper.get_account_info().account_address, + "--query", + "modules", + ], + ) + + # Confirm that the module exists on chain. + response = json.loads(response.stdout) + for module in response["Result"]: + if ( + module["abi"]["address"] + == f"0x{run_helper.get_account_info().account_address}" + and module["abi"]["name"] == "cli_e2e_tests" + ): + return + + raise TestError( + "Module apparently published successfully but it could not be found on chain" + ) + + +@test_case +def test_move_compile(run_helper: RunHelper, test_name=None): + package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" + account_info = run_helper.get_account_info() + + # Compile the module. + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "compile", + "--package-dir", + package_dir, + "--named-addresses", + f"addr={account_info.account_address}", + ], + ) + + if f"{account_info.account_address}::cli_e2e_tests" not in response.stdout: + raise TestError("Module did not compile successfully") + + +@test_case +def test_move_compile_dev_mode(run_helper: RunHelper, test_name=None): + package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" + account_info = run_helper.get_account_info() + + # Compile the module. Should not need an address passed in + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "compile", + "--dev", + "--package-dir", + package_dir, + ], + ) + + if f"{account_info.account_address}::cli_e2e_tests" not in response.stdout: + raise TestError("Module did not compile successfully") + + +@test_case +def test_move_compile_script(run_helper: RunHelper, test_name=None): + package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" + account_info = run_helper.get_account_info() + + # Compile the script. + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "compile-script", + "--package-dir", + package_dir, + "--named-addresses", + f"addr={account_info.account_address}", + ], + ) + + if "script_hash" not in response.stdout: + raise TestError("Script did not compile successfully") + + +@test_case +def test_move_run(run_helper: RunHelper, test_name=None): + account_info = run_helper.get_account_info() + + # Run the min_hero entry function with default profile + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "run", + "--assume-yes", + "--function-id", + "default::cli_e2e_tests::mint_hero", + "--args", + "string:Boss", + "string:Male", + "string:Jin", + "string:Undead", + "string:", + ], + ) + + if '"success": true' not in response.stdout: + raise TestError("Move run did not execute successfully") + + # Get what modules exist on chain. + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "view", + "--assume-yes", + "--function-id", + f"0x{account_info.account_address}::cli_e2e_tests::view_hero", + "--args", + f"address:0x{account_info.account_address}", + "string:Hero Quest", + "string:Jin", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result[0].get("gender") != "Male" and result[0].get("race") != "Undead": + raise TestError( + "Data on chain (view_hero) does not match expected data from (mint_hero)" + ) + + # Run test_move_run to entry function with default profile + # Make sure other parameters are able to be called using "move run" + # Notice the entry function is not running anything but just testing the parameters + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "run", + "--assume-yes", + "--function-id", + "default::cli_e2e_tests::test_move_run", + "--args", + "string:1234", # Notice this is testing u8 vector instead of actual string + "u16:[1,2]", + "u32:[1,2]", + "u64:[1,2]", + "u128:[1,2]", + "u256:[1,2]", + 'address:["0x123","0x456"]', + "bool:[true,false]", + 'string:["abc","efg"]', + ], + ) + + if '"success": true' not in response.stdout: + raise TestError("Move run did not execute successfully") + + +@test_case +def test_move_view(run_helper: RunHelper, test_name=None): + account_info = run_helper.get_account_info() + + # Run the view function + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "view", + "--function-id", + "0x1::account::exists_at", + "--args", + f"address:{account_info.account_address}", + ], + ) + + response = json.loads(response.stdout) + if response["Result"] == None or response["Result"][0] != True: + raise TestError("View function did not return correct result") + + # Test view function with with big number arguments + expected_u64 = 18446744073709551615 + expected_128 = 340282366920938463463374607431768211455 + expected_256 = ( + 115792089237316195423570985008687907853269984665640564039457584007913129639935 + ) + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "view", + "--assume-yes", + "--function-id", + "default::cli_e2e_tests::test_big_number", + "--args", + f"u64:{expected_u64}", + f"u128:{expected_128}", + f"u256:{expected_256}", # Important to test this big number + ], + ) + + response = json.loads(response.stdout) + if ( + response["Result"] == None + or response["Result"][0] != f"{expected_u64}" + or response["Result"][1] != f"{expected_128}" + or response["Result"][2] != f"{expected_256}" + ): + raise TestError( + f"View function [test_big_number] did not return correct result" + ) + + # Test view function with with vector arguments + # Follow 2 lines are for testing vector of u16-u256 + response = run_helper.run_command( + test_name, + [ + "aptos", + "move", + "view", + "--assume-yes", + "--function-id", + "default::cli_e2e_tests::test_vector", + "--args", + "string:1234", # Notice this is testing u8 vector instead of actual string + f"u16:[1,2]", + f"u32:[1,2]", + f"u64:[1,2]", + f"u128:[1,2]", + f"u256:[1,2]", + f'address:["0x123","0x456"]', + "bool:[true,false]", + 'string:["abc","efg"]', + ], + ) + + response = json.loads(response.stdout) + if response["Result"] == None or len(response["Result"]) != 9: + raise TestError(f"View function [test_vector] did not return correct result") diff --git a/crates/aptos/e2e/cases/node.py b/crates/aptos/e2e/cases/node.py new file mode 100644 index 0000000000000..5a7e40b3a91e3 --- /dev/null +++ b/crates/aptos/e2e/cases/node.py @@ -0,0 +1,178 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import json + +from common import TestError +from test_helpers import RunHelper +from test_results import test_case + + +@test_case +def test_node_show_validator_set(run_helper: RunHelper, test_name=None): + # run the show validator set command + response = run_helper.run_command( + test_name, + [ + "aptos", + "node", + "show-validator-set", + "--profile", + "default", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result.get("scheme") == None or result.get("active_validators") == None: + raise TestError( + "Node show validator set command failed: Did not return scheme and active_validators" + ) + + validator_0 = result.get("active_validators")[0] + validator_config = validator_0.get("config") + if ( + validator_0.get("account_address") == None + or validator_config.get("consensus_public_key") == None + or validator_config.get("validator_network_addresses") == None + or validator_config.get("fullnode_network_addresses") == None + ): + raise TestError( + "Node show validator set command failed: Did not return account_address, consensus_public_key, or config" + ) + + +@test_case +def test_node_update_consensus_key(run_helper: RunHelper, test_name=None): + # run init a new profile + run_helper.run_command( + test_name, + [ + "aptos", + "init", + "--assume-yes", + "--network", + "local", + "--profile", + "consensus", + ], + input="\n", + ) + + # run initialize stake to make sure stake pool created + run_helper.run_command( + test_name, + [ + "aptos", + "stake", + "initialize-stake-owner", + "--profile", + "consensus", + "--initial-stake-amount", + "1", + "--assume-yes", + ], + ) + + # hardcode the bls12381 key + bls12381_public_key = "91067b73f284b4fa47141a023b4bbdc819e92a80058cbedfa2a39b43782c624eea1b281b0a8862aee23e77fb59a2ba75" + bls12381_pop = "857ddee36936599f98376ebc1a4d70a9ea9c066d27c03ff34300cc52ddea72760c3f8c8b8e423626cff585130d38370712945f48438ff24dcac0179dd4eb47389fd317395a36766490ce34c852b4b23fa8f0b55561275014ae56f79a86eff3ef" + + # run the update consensus key command + response = run_helper.run_command( + test_name, + [ + "aptos", + "node", + "update-consensus-key", + "--profile", + "consensus", + "--consensus-public-key", + bls12381_public_key, + "--proof-of-possession", + bls12381_pop, + "--assume-yes", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result.get("success") == None or result.get("success") != True: + raise TestError("update-consensus-key command failed: Did not return success") + + # Make sure consensus key is actually updated on chain + response = run_helper.run_command( + test_name, + [ + "aptos", + "node", + "get-stake-pool", + "--profile", + "consensus", + "--owner-address", + "consensus", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result[0] == None or result[0].get("consensus_public_key") == None: + raise TestError(f"Error: No consensus key on result") + + if result[0].get("consensus_public_key") != f"0x{bls12381_public_key}": + raise TestError( + f"Error: Expected consensus key 0x{bls12381_public_key} but got {result[0].get('consensus_public_key')}" + ) + + +@test_case +def test_node_update_validator_network_address(run_helper: RunHelper, test_name=None): + network_public_key = ( + "657dac6c023b39b53b8ae5fdc13a1e1222dacde98165324e16086a6766821628" + ) + network_host = "127.0.0.1" + network_port = "6180" + + # run the update command + response = run_helper.run_command( + test_name, + [ + "aptos", + "node", + "update-validator-network-addresses", + "--profile", + "consensus", # Assume consensus profile is already created from test_node_update_consensus_key + "--validator-network-public-key", + network_public_key, + "--validator-host", + f"{network_host}:{network_port}", + "--assume-yes", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result.get("success") == None or result.get("success") != True: + raise TestError( + "update-validator-network-addresses command failed: Did not return success" + ) + + # Make sure network address is actually updated on chain + response = run_helper.run_command( + test_name, + [ + "aptos", + "node", + "get-stake-pool", + "--profile", + "consensus", + "--owner-address", + "consensus", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result[0] == None or result[0].get("validator_network_addresses") == None: + raise TestError(f"Error: No [validator_network_addresses] key found on result") + + expected_network_address = f"/ip4/{network_host}/tcp/{network_port}/noise-ik/0x{network_public_key}/handshake/0" + if result[0].get("validator_network_addresses")[0] != expected_network_address: + raise TestError( + f"Error: Expected validator network address [{expected_network_address}] but got [{result[0].get('validator_network_addresses')[0]}]" + ) diff --git a/crates/aptos/e2e/cases/stake.py b/crates/aptos/e2e/cases/stake.py index 80693404e7fb4..2de1febab1088 100644 --- a/crates/aptos/e2e/cases/stake.py +++ b/crates/aptos/e2e/cases/stake.py @@ -1,19 +1,13 @@ # Copyright © Aptos Foundation # SPDX-License-Identifier: Apache-2.0 -import asyncio import json -import logging -from aptos_sdk.account_address import AccountAddress from common import TestError from test_helpers import RunHelper from test_results import test_case -LOG = logging.getLogger(__name__) - - @test_case def test_stake_initialize_stake_owner(run_helper: RunHelper, test_name=None): # run the initialize-stake-owner command @@ -33,7 +27,7 @@ def test_stake_initialize_stake_owner(run_helper: RunHelper, test_name=None): if result.get("success") != True: raise TestError("Did not initialize stake owner successfully") - # make sure the stake pool initialized on chain + # make sure the the stake pool initialized on chain response = run_helper.run_command( test_name, [ @@ -206,10 +200,7 @@ def test_stake_set_voter(run_helper: RunHelper, test_name=None): @test_case -async def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): - # First wait for reconfiguration to finish. - await wait_for_reconfiguration(run_helper) - +def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): # run the set-operator command # Note: This command has to run after set-operator and set-voter # because it needs to know the operator and voter addresses @@ -236,6 +227,32 @@ async def test_stake_create_staking_contract(run_helper: RunHelper, test_name=No raise TestError("Did not set create staking contract successfully") +@test_case +def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): + # run the set-operator command + response = run_helper.run_command( + test_name, + [ + "aptos", + "stake", + "create-staking-contract", + "--operator", + "operator", + "--voter", + "voter", + "--amount", + "1000000", + "--commission-percentage", + "1", + "--assume-yes", + ], + ) + + result = json.loads(response.stdout)["Result"] + if result.get("success") != True: + raise TestError("Did not set create staking contract successfully") + + @test_case def test_stake_increase_lockup(run_helper: RunHelper, test_name=None): # run the set-operator command @@ -268,9 +285,7 @@ def test_stake_unlock_stake(run_helper: RunHelper, test_name=None): @test_case -async def test_stake_withdraw_stake_after_unlock(run_helper: RunHelper, test_name=None): - await wait_for_reconfiguration(run_helper) - +def test_stake_withdraw_stake_after_unlock(run_helper: RunHelper, test_name=None): # get the current stake amount response = run_helper.run_command( test_name, @@ -401,21 +416,3 @@ def test_stake_request_commission(run_helper: RunHelper, test_name=None): result = json.loads(response.stdout)["Result"] if result.get("success") != True: raise TestError("Did not execute [request-commission] successfully") - - -async def wait_for_reconfiguration(run_helper: RunHelper): - # First wait for reconfiguration to finish. - LOG.info("Waiting for reconfiguration to finish...") - attempts = 0 - while True: - response = await run_helper.api_client.account_resource( - AccountAddress.from_str("0x1"), - "0x1::reconfiguration_state::State", - ) - if response["data"]["variant"]["data"] == "0x00": - break - attempts += 1 - if attempts > 20: - raise TestError("Reconfiguration did not finish in time") - await asyncio.sleep(0.5) - LOG.info("Reconfiguration finished") diff --git a/crates/aptos/e2e/common.py b/crates/aptos/e2e/common.py new file mode 100644 index 0000000000000..d24369a15a895 --- /dev/null +++ b/crates/aptos/e2e/common.py @@ -0,0 +1,55 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import os +from dataclasses import dataclass +from enum import Enum + +NODE_PORT = 8080 +METRICS_PORT = 9101 +FAUCET_PORT = 8081 + + +class Network(Enum): + DEVNET = "devnet" + TESTNET = "testnet" + MAINNET = "mainnet" + + def __str__(self): + return self.value + + +# Information for some accounts we use for testing. +@dataclass +class AccountInfo: + private_key: str + public_key: str + account_address: str + + +# This is an account that use for testing, for example to create it with the init +# account, send funds to it, etc. This is not the account created by the `aptos init` +# test. To get details about that account use get_account_info on the RunHelper. +OTHER_ACCOUNT_ONE = AccountInfo( + private_key="0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221", + public_key="0x25caf00522e4d4664ec0a27166a69e8a32b5078959d0fc398da70d40d2893e8f", + account_address="0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82", +) + + +def build_image_name(image_repo_with_project: str, tag: str): + # If no repo is specified, leave it that way. Otherwise make sure we have a slash + # between the image repo and the image name. + image_repo_with_project = image_repo_with_project.rstrip("/") + if image_repo_with_project != "": + image_repo_with_project = f"{image_repo_with_project}/" + return f"{image_repo_with_project}tools:{tag}" + + +# Exception to use when a test fails, for the CLI did something unexpected, an +# expected output was missing, etc. This is just a convenience, the framework +# will still work if a different error is raised. +# +# For errors within the framework itself, use RuntimeError. +class TestError(Exception): + pass diff --git a/crates/aptos/e2e/linter.sh b/crates/aptos/e2e/linter.sh new file mode 100755 index 0000000000000..484eac198295c --- /dev/null +++ b/crates/aptos/e2e/linter.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# This assumes you have already poetry installed. +# If not, please follow README.md instructions to install poetry and necessary dependencies. + +# Run the necesary linters and formatters for CLI e2e tests +echo "Start linter.sh..." + +# Run isort +echo "Running isort..." +poetry run isort . + +# Run black +echo "Running black..." +poetry run black . + +echo "linter.sh complete!" \ No newline at end of file diff --git a/crates/aptos/e2e/local_testnet.py b/crates/aptos/e2e/local_testnet.py index 3d024219ba9fc..941bef3da23fb 100644 --- a/crates/aptos/e2e/local_testnet.py +++ b/crates/aptos/e2e/local_testnet.py @@ -1,7 +1,7 @@ # Copyright © Aptos Foundation # SPDX-License-Identifier: Apache-2.0 -# This file contains functions for running the localnet. +# This file contains functions for running the local testnet. import logging import subprocess @@ -12,12 +12,12 @@ LOG = logging.getLogger(__name__) -# Run a localnet in a docker container. We choose to detach here and we'll +# Run a local testnet in a docker container. We choose to detach here and we'll # stop running it later using the container name. def run_node(network: Network, image_repo_with_project: str, pull=True): image_name = build_image_name(image_repo_with_project, network) container_name = f"aptos-tools-{network}" - LOG.info(f"Trying to run aptos CLI localnet from image: {image_name}") + LOG.info(f"Trying to run aptos CLI local testnet from image: {image_name}") # Confirm that the Docker daemon is running. try: @@ -69,13 +69,12 @@ def run_node(network: Network, image_repo_with_project: str, pull=True): ] # Run the container. - LOG.debug("Running command: %s", " ".join(args)) subprocess.run( args, **kwargs, ) - LOG.info(f"Running aptos CLI localnet from image: {image_name}. Container name: {container_name}") + LOG.info(f"Running aptos CLI local testnet from image: {image_name}") return container_name @@ -96,7 +95,7 @@ def wait_for_startup(container_name: str, timeout: int): try: api_response = requests.get(f"http://127.0.0.1:{NODE_PORT}/v1") # Try to query the legacy faucet health endpoint first. TODO: Remove this - # once all localnet images we use have the new faucet in them. + # once all local testnet images we use have the new faucet in them. faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/health") if faucet_response.status_code == 404: # If that fails, try the new faucet health endpoint. diff --git a/crates/aptos/e2e/main.py b/crates/aptos/e2e/main.py index 46bf3078e2b4e..90dbb882fcd1c 100644 --- a/crates/aptos/e2e/main.py +++ b/crates/aptos/e2e/main.py @@ -4,9 +4,9 @@ # SPDX-License-Identifier: Apache-2.0 """ -This script is how we orchestrate running a localnet and then running CLI tests against it. There are two different CLIs used for this: +This script is how we orchestrate running a local testnet and then running CLI tests against it. There are two different CLIs used for this: -1. Base: For running the localnet. This is what the --base-network flag and all other flags starting with --base are for. +1. Base: For running the local testnet. This is what the --base-network flag and all other flags starting with --base are for. 2. Test: The CLI that we're testing. This is what the --test-cli-tag / --test-cli-path and all other flags starting with --test are for. Example (testing CLI in image): @@ -15,7 +15,7 @@ Example (testing locally built CLI binary): python3 main.py --base-network devnet --test-cli-path ~/aptos-core/target/release/aptos -This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a localnet built from the testnet branch of aptos-core. +This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a local testnet built from the testnet branch of aptos-core. Example (using a different image repo): See ~/.github/workflows/cli-e2e-tests.yaml @@ -24,18 +24,15 @@ """ import argparse -import asyncio import logging -import os import pathlib -import platform import shutil import sys from cases.account import ( test_account_create_and_transfer, - test_account_fund_with_faucet, test_account_list, + test_account_fund_with_faucet, test_account_lookup_address, test_account_resource_account, test_account_rotate_key, @@ -54,7 +51,6 @@ test_node_update_consensus_key, test_node_update_validator_network_address, ) -""" from cases.stake import ( test_stake_add_stake, test_stake_create_staking_contract, @@ -67,7 +63,6 @@ test_stake_withdraw_stake_after_unlock, test_stake_withdraw_stake_before_unlock, ) -""" from common import Network from local_testnet import run_node, stop_node, wait_for_startup from test_helpers import RunHelper @@ -94,7 +89,7 @@ def parse_args(): "--image-repo-with-project", default="aptoslabs", help=( - "What docker image repo (+ project) to use for the localnet. " + "What docker image repo (+ project) to use for the local testnet. " "By default we use Docker Hub: %(default)s (so, just aptoslabs for the " "project since Docker Hub is the implied default repo). If you want to " "specify a different repo, it might look like this: " @@ -106,7 +101,7 @@ def parse_args(): required=True, type=Network, choices=list(Network), - help="What branch the Aptos CLI used for the localnet should be built from", + help="What branch the Aptos CLI used for the local testnet should be built from", ) parser.add_argument( "--base-startup-timeout", @@ -131,13 +126,13 @@ def parse_args(): parser.add_argument( "--no-pull-always", action="store_true", - help='If set, do not set "--pull always" when running the localnet. Necessary for using local images.', + help='If set, do not set "--pull always" when running the local testnet. Necessary for using local images.', ) args = parser.parse_args() return args -async def run_tests(run_helper): +def run_tests(run_helper): # Make sure the metrics port is accessible. test_metrics_accessible(run_helper) @@ -148,8 +143,8 @@ async def run_tests(run_helper): test_config_show_profiles(run_helper) # Run account tests. - await test_account_fund_with_faucet(run_helper) - await test_account_create_and_transfer(run_helper) + test_account_fund_with_faucet(run_helper) + test_account_create_and_transfer(run_helper) test_account_list(run_helper) test_account_lookup_address(run_helper) test_account_resource_account(run_helper) @@ -165,18 +160,16 @@ async def run_tests(run_helper): test_move_view(run_helper) # Run stake subcommand group tests. - """ test_stake_initialize_stake_owner(run_helper) test_stake_add_stake(run_helper) test_stake_withdraw_stake_before_unlock(run_helper) test_stake_unlock_stake(run_helper) - await test_stake_withdraw_stake_after_unlock(run_helper) + test_stake_withdraw_stake_after_unlock(run_helper) test_stake_increase_lockup(run_helper) test_stake_set_operator(run_helper) test_stake_set_voter(run_helper) - await test_stake_create_staking_contract(run_helper) + test_stake_create_staking_contract(run_helper) test_stake_request_commission(run_helper) - """ # Run node subcommand group tests. test_node_show_validator_set(run_helper) @@ -187,7 +180,7 @@ async def run_tests(run_helper): test_account_rotate_key(run_helper) -async def main(): +def main(): args = parse_args() if args.debug: @@ -200,26 +193,13 @@ async def main(): shutil.rmtree(args.working_directory, ignore_errors=True) pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True) - # If we're on Mac and DOCKER_DEFAULT_PLATFORM is not already set, set it to - # linux/amd64 since we only publish images for that platform. - if ( - platform.system().lower() == "darwin" - and platform.processor().lower().startswith("arm") - ): - if not os.environ.get("DOCKER_DEFAULT_PLATFORM"): - os.environ["DOCKER_DEFAULT_PLATFORM"] = "linux/amd64" - LOG.info( - "Detected ARM Mac and DOCKER_DEFAULT_PLATFORM was not set, setting it " - "to linux/amd64" - ) - # Run a node + faucet and wait for them to start up. container_name = run_node( args.base_network, args.image_repo_with_project, not args.no_pull_always ) # We run these in a try finally so that if something goes wrong, such as the - # localnet not starting up correctly or some unexpected error in the + # local testnet not starting up correctly or some unexpected error in the # test framework, we still stop the node + faucet. try: wait_for_startup(container_name, args.base_startup_timeout) @@ -237,7 +217,7 @@ async def main(): run_helper.prepare() # Run tests. - await run_tests(run_helper) + run_tests(run_helper) finally: # Stop the node + faucet. stop_node(container_name) @@ -265,7 +245,7 @@ async def main(): if __name__ == "__main__": - if asyncio.run(main()): + if main(): sys.exit(0) else: sys.exit(1) diff --git a/crates/aptos/e2e/poetry.lock b/crates/aptos/e2e/poetry.lock index fdcae8345acce..bd8def62ff7ce 100644 --- a/crates/aptos/e2e/poetry.lock +++ b/crates/aptos/e2e/poetry.lock @@ -1,185 +1,41 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. -[[package]] -name = "aiohttp" -version = "3.9.5" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, - {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, - {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, - {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, - {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, - {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, - {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, - {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, - {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, - {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, -] - -[package.dependencies] -aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "brotlicffi"] - -[[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - [[package]] name = "anyio" -version = "4.3.0" +version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] [[package]] name = "aptos-sdk" -version = "0.8.6" -description = "Aptos SDK" +version = "0.5.1" +description = "" optional = false -python-versions = ">=3.8.1" +python-versions = ">=3.7" files = [ - {file = "aptos_sdk-0.8.6-py3-none-any.whl", hash = "sha256:94818c54ac18b5eb631e744edfb5dcf721b8fdf28aa8b471814bcab3c6f6f7a5"}, - {file = "aptos_sdk-0.8.6.tar.gz", hash = "sha256:57fe71d966903b096933b905c94f0c835e57ca0781c098e38b637bf861c4470c"}, + {file = "aptos_sdk-0.5.1.tar.gz", hash = "sha256:3711ad2bf1120fff463cd5f494162c4658f03dd6bfbf1f523ee9aea01a4cb0f0"}, ] [package.dependencies] -ecdsa = "0.18.0" -httpx = ">=0.27.0,<0.28.0" +httpx = ">=0.23.0,<0.24.0" +mypy = ">=0.982,<0.983" PyNaCl = ">=1.5.0,<2.0.0" -python-graphql-client = ">=0.4.3,<0.5.0" -tomli = ">=2.0.1,<3.0.0" -typing-extensions = ">=4.4.0,<5.0.0" - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "attrs" -version = "23.2.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "black" @@ -208,6 +64,7 @@ mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] @@ -218,74 +75,86 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[package]] name = "cffi" -version = "1.16.0" +version = "1.15.1" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] [package.dependencies] @@ -403,6 +272,7 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -415,124 +285,20 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "ecdsa" -version = "0.18.0" -description = "ECDSA cryptographic signature library (pure python)" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, - {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, -] - -[package.dependencies] -six = ">=1.9.0" - -[package.extras] -gmpy = ["gmpy"] -gmpy2 = ["gmpy2"] - [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "frozenlist" -version = "1.4.1" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, -] - [[package]] name = "h11" version = "0.14.0" @@ -544,175 +310,145 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + [[package]] name = "httpcore" -version = "1.0.5" +version = "0.16.3" description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, + {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, + {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, ] [package.dependencies] +anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" +sniffio = "==1.*" [package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.23.3" description = "The next generation HTTP client." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, + {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, ] [package.dependencies] -anyio = "*" certifi = "*" -httpcore = "==1.*" -idna = "*" +httpcore = ">=0.15.0,<0.17.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] [[package]] name = "idna" -version = "3.7" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] +[[package]] +name = "importlib-metadata" +version = "6.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, + {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + [[package]] name = "isort" -version = "5.13.2" +version = "5.11.5" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.7.0" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, ] [package.extras] -colors = ["colorama (>=0.4.6)"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] -name = "multidict" -version = "6.0.5" -description = "multidict implementation" +name = "mypy" +version = "0.982" +description = "Optional static typing for Python" optional = false python-versions = ">=3.7" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, + {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, + {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, + {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, + {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, + {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, + {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, + {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, + {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, + {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, + {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, + {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, + {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, + {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, + {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, + {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, + {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, + {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, + {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, + {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, + {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, + {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, ] +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -726,40 +462,42 @@ files = [ [[package]] name = "pathspec" -version = "0.12.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +version = "4.0.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, + {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} + [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pycparser" -version = "2.22" +version = "2.21" description = "C parser in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] [[package]] @@ -788,34 +526,15 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] -[[package]] -name = "python-graphql-client" -version = "0.4.3" -description = "Python GraphQL Client" -optional = false -python-versions = "*" -files = [ - {file = "python_graphql_client-0.4.3-py3-none-any.whl", hash = "sha256:c5eb996702acf46110b352f61819c46065ea4f4f106158535cd471e66490b25e"}, - {file = "python_graphql_client-0.4.3.tar.gz", hash = "sha256:fdbd03115dde8776db02e60414b83b018d7d95e5752d6d5fabf21c99265f5b9d"}, -] - -[package.dependencies] -aiohttp = ">=3.0,<4.0" -requests = ">=2.0,<3.0" -websockets = ">=5.0" - -[package.extras] -dev = ["black", "flake8", "flake8-black", "flake8-docstrings", "flake8-isort", "gitchangelog", "pre-commit", "pystache"] - [[package]] name = "requests" -version = "2.32.2" +version = "2.31.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] @@ -829,25 +548,31 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "*" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + [[package]] name = "sniffio" -version = "1.3.1" +version = "1.3.0" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] [[package]] @@ -861,219 +586,100 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "typed-ast" +version = "1.5.5" +description = "a fork of Python 2 and 3 ast modules with type comment support" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, + {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, + {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, + {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, + {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, + {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, + {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, + {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, + {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, + {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, +] + [[package]] name = "typing-extensions" -version = "4.11.0" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[[package]] -name = "yarl" -version = "1.9.4" -description = "Yet another URL library" +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" -python-versions = ">=3.8.1,<=4" -content-hash = "494e13a2e2742cb22d504ba34cf18391f5727928283ad845214ac0b84f9bd3ec" +python-versions = ">=3.7 <=4" +content-hash = "a521ce7dc5692587334cf5116fa19b834a12ab556c1ab32cd8f6793dd5d5568c" diff --git a/crates/aptos/e2e/pyproject.toml b/crates/aptos/e2e/pyproject.toml index 8368895122074..23b3274f6d7f0 100644 --- a/crates/aptos/e2e/pyproject.toml +++ b/crates/aptos/e2e/pyproject.toml @@ -6,8 +6,8 @@ authors = ["Aptos Labs "] license = "Apache-2.0" [tool.poetry.dependencies] -python = ">=3.8.1,<=4" -aptos-sdk = "^0.8.6" +python = ">=3.7 <=4" +aptos-sdk = "^0.5.1" requests = "^2.31.0" [tool.poetry.dev-dependencies] diff --git a/crates/aptos/e2e/test_helpers.py b/crates/aptos/e2e/test_helpers.py index 67d2655674f3f..dbca11238b8f0 100644 --- a/crates/aptos/e2e/test_helpers.py +++ b/crates/aptos/e2e/test_helpers.py @@ -10,7 +10,7 @@ import traceback from dataclasses import dataclass -from aptos_sdk.async_client import RestClient +from aptos_sdk.client import RestClient from common import METRICS_PORT, NODE_PORT, AccountInfo, Network, build_image_name LOG = logging.getLogger(__name__) @@ -30,7 +30,7 @@ class RunHelper: test_count: int - # This can be used by the tests to query the localnet node. + # This can be used by the tests to query the local testnet node. api_client: RestClient def __init__( diff --git a/crates/aptos/e2e/test_results.py b/crates/aptos/e2e/test_results.py new file mode 100644 index 0000000000000..c206d5a60ad40 --- /dev/null +++ b/crates/aptos/e2e/test_results.py @@ -0,0 +1,49 @@ +# Copyright © Aptos Foundation +# SPDX-License-Identifier: Apache-2.0 + +import logging +import typing +from dataclasses import dataclass, field +from functools import wraps + +LOG = logging.getLogger(__name__) + + +# This class holds info about passed / failed tests. +@dataclass(init=True) +class TestResults: + passed: typing.List[str] = field(default_factory=list) + failed: typing.List[typing.Tuple[str, Exception]] = field(default_factory=list) + + +# This is a decorator that you put above every test case. It handles capturing test +# success / failure so it can be reported at the end of the test suite. It also handles +# passing in test_name based on the name of the function so the caller doesn't have to. +def build_test_case_decorator(test_results: TestResults): + def test_case_inner(f): + @wraps(f) + def wrapper(*args, **kwds): + LOG.info(f"Running test: {f.__name__}") + try: + result = f(*args, test_name=f.__name__, **kwds) + test_results.passed.append(f.__name__) + return result + except Exception as e: + test_results.failed.append((f.__name__, e)) + return None + + return wrapper + + return test_case_inner + + +# We now define one TestResults that we'll use for every test case. This is a bit of a +# hack but it is the only way to then be able to provide a decorator that works out of +# the box. The alternative was to use a context manager and wrap every function call in +# it, but not only is that more verbose, but you'd have to provide the name of each test +# case manually to the context manager, whereas with this approach the name can be +# inferred from the function being decorated directly. +test_results = TestResults() + +# Then we define an instance of the decorator that uses that TestResults instance. +test_case = build_test_case_decorator(test_results) diff --git a/crates/aptos/homebrew/README.md b/crates/aptos/homebrew/README.md index 16242bea2174a..8639ea40ece72 100644 --- a/crates/aptos/homebrew/README.md +++ b/crates/aptos/homebrew/README.md @@ -3,15 +3,14 @@ Homebrew is a package manager that works for MacOS Silicon and Intel chips as well as Linux distributions like Debian and Ubuntu. -The [Aptos command line interface (CLI)](https://aptos.dev/tools/aptos-cli/install-cli/) may be installed -via [Homebrew](https://brew.sh/) for simplicity. This is an in-depth overview of Homebrew and the Aptos formula. In this +The [Movement command line interface (CLI)](https://github.com/movementlabsxyz/aptos-core) may be installed +via [Homebrew](https://brew.sh/) for simplicity. This is an in-depth overview of Homebrew and the Movement formula. In this guide, we go over each section of the Homebrew formula and steps to implement changes in the future. ## Quick guide -- [Formula in Homebrew GitHub](https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb) -- [Aptos 1.0.3 New Formula PR for GitHub](https://github.com/Homebrew/homebrew-core/pull/119832) -- [Aptos Formula Fix PR to use build_cli_release.sh](https://github.com/Homebrew/homebrew-core/pull/120051) +- [Formula in Homebrew GitHub](https://github.com/Homebrew/homebrew-core/blob/master/Formula/movement.rb) +- [Movement New Formula PR for GitHub]() ## Getting started @@ -27,29 +26,29 @@ brew help Once homebrew is installed, run ```bash -brew install aptos +brew install movement ``` to test that it installed correctly, try ```bash -aptos --help +movement --help # This should return something like -# aptos 1.0.5 -# Aptos Labs -# Command Line Interface (CLI) for developing and interacting with the Aptos blockchain +# movement 3.3.1 +# Movement Labs +# Command Line Interface (CLI) for developing and interacting with the Movement network # ... ``` ## Change guide -Note: This guide is for developers who are trying to update the Aptos homebrew formula. +Note: This guide is for developers who are trying to update the Movement homebrew formula. -You can get the latest formula here: https://github.com/Homebrew/homebrew-core/blob/master/Formula/a/aptos.rb +You can get the latest formula here: https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb -Copy the `aptos.rb` file to your `homebrew` `formula` directory. For example, on macOS with an M1, this will likely be: +Copy the `movement.rb` file to your `homebrew` `formula` directory. For example, on macOS with an M1, this will likely be: ```bash /opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula @@ -57,7 +56,7 @@ Copy the `aptos.rb` file to your `homebrew` `formula` directory. For example, on ### Development -After you've copied `aptos.rb` to your local `homebrew` `formula` directory, you can modify it and use the commands +After you've copied `movement.rb` to your local `homebrew` `formula` directory, you can modify it and use the commands below for testing. ```bash @@ -65,17 +64,17 @@ below for testing. /opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula # Before submitting changes run -brew audit --new-formula aptos # For new formula -brew audit aptos --strict --online -brew install aptos -brew test aptos +brew audit --new-formula movement # For new formula +brew audit movement --strict --online +brew install movement +brew test movement # For debugging issues during the installation process you can do -brew install aptos --interactive # Interactive, gives you access to the shell -brew install aptos -d # Debug mode +brew install movement --interactive # Interactive, gives you access to the shell +brew install movement -d # Debug mode # Livecheck -brew livecheck --debug aptos +brew livecheck --debug movement ``` ### Committing changes @@ -92,11 +91,11 @@ Once you have audited and tested your brew formula using the commands above, mak ### Header ```ruby -class Aptos < Formula - desc "Layer 1 blockchain built to support fair access to decentralized assets for all" - homepage "https://aptoslabs.com/" - url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" - sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" +class Movement < Formula + desc "The first MoveVM Zk Layer 2 on Ethereum" + homepage "https://movementlabs.xyz" + url "" + sha256 "" license "Apache-2.0" ... ``` @@ -152,7 +151,7 @@ discussion. To run livecheck for testing, we recommend including the `--debug` argument: ```bash -brew livecheck --debug aptos +brew livecheck --debug movement ``` ### Depends on and installation @@ -203,7 +202,7 @@ brew livecheck --debug aptos To conduct tests, run: ```bash -brew test aptos +brew test movement ``` The current test generates a new key via the Aptos CLI and ensures the shell output matches the filename(s) for that diff --git a/crates/aptos/scripts/check_dynamic_deps.sh b/crates/aptos/scripts/check_dynamic_deps.sh new file mode 100755 index 0000000000000..f5dc6683b79b2 --- /dev/null +++ b/crates/aptos/scripts/check_dynamic_deps.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# This script checks if the CLI depends on external deps that it shouldn't. We run this +# in CI to make sure we don't accidentally reintroduce deps that would make the CLI +# unusable on most systems. +# +# While it would be more reliable to actually build the CLI and check what libraries it +# links to, e.g. with otool, it is much cheaper to use cargo tree. As far as I can tell +# the entire Rust ecosystem makes use of these `x-sys` libraries to depend on external +# dynamically linked libraries. +# +# We can almost use cargo deny but it doesn't support checking specific build paths. We +# don't care if openssl-sys for example is used at build time (which it is, indirectly +# by shadow-rs), only at run time. See more here: +# https://github.com/EmbarkStudios/cargo-deny/issues/563 +# +# It assumes cargo and friends are available. +# +# Run this from the root of the repo. + +declare -a deps=("pq-sys" "openssl-sys") + +for dep in "${deps[@]}"; do + echo "Checking for banned dependency $dep..." + + # Check for deps. As you can see, we only check for MacOS right now. + out=`cargo tree -e features,no-build,no-dev --target aarch64-apple-darwin -p aptos -i "$dep"` + + # If the exit status was non-zero, great, the dep couldn't be found. + if [ $? -ne 0 ]; then + continue + fi + + # If the exit status was zero we have to check the output to see if the dep is in + # use. If it is in the output, it is in use. + if [[ $out != *"$dep"* ]]; then + continue + fi + + echo "Banned dependency $dep found!" + exit 1 +done + +echo +echo "None of the banned dependencies are in use, great!" +exit 0 diff --git a/crates/aptos/src/account/create.rs b/crates/aptos/src/account/create.rs new file mode 100644 index 0000000000000..cbabbf1c6045a --- /dev/null +++ b/crates/aptos/src/account/create.rs @@ -0,0 +1,41 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}; +use aptos_cached_packages::aptos_stdlib; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; + +// 1 APT +pub const DEFAULT_FUNDED_COINS: u64 = 100_000_000; + +/// Create a new account on-chain +/// +/// An account can be created by transferring coins, or by making an explicit +/// call to create an account. This will create an account with no coins, and +/// any coins will have to transferred afterwards. +#[derive(Debug, Parser)] +pub struct CreateAccount { + /// Address of the new account + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub(crate) account: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for CreateAccount { + fn command_name(&self) -> &'static str { + "CreateAccount" + } + + async fn execute(self) -> CliTypedResult { + let address = self.account; + self.txn_options + .submit_transaction(aptos_stdlib::aptos_account_create_account(address)) + .await + .map(TransactionSummary::from) + } +} diff --git a/crates/aptos/src/account/create_resource_account.rs b/crates/aptos/src/account/create_resource_account.rs new file mode 100644 index 0000000000000..54af8e2bbae83 --- /dev/null +++ b/crates/aptos/src/account/create_resource_account.rs @@ -0,0 +1,91 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::derive_resource_account::ResourceAccountSeed, + common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}, +}; +use aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account; +use aptos_rest_client::{ + aptos_api_types::{WriteResource, WriteSetChange}, + Transaction, +}; +use aptos_types::{account_address::AccountAddress, transaction::authenticator::AuthenticationKey}; +use async_trait::async_trait; +use clap::Parser; +use serde::Serialize; +use std::str::FromStr; + +/// Create a resource account on-chain +/// +/// This will create a resource account which can be used as an autonomous account +/// not controlled directly by one account. +#[derive(Debug, Parser)] +pub struct CreateResourceAccount { + /// Optional Resource Account authentication key. + #[clap(long, value_parser = AuthenticationKey::from_str)] + pub(crate) authentication_key: Option, + + #[clap(flatten)] + pub(crate) seed_args: ResourceAccountSeed, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +/// A shortened create resource account output +#[derive(Clone, Debug, Serialize)] +pub struct CreateResourceAccountSummary { + pub resource_account: Option, + #[serde(flatten)] + pub transaction_summary: TransactionSummary, +} + +impl From for CreateResourceAccountSummary { + fn from(transaction: Transaction) -> Self { + let transaction_summary = TransactionSummary::from(&transaction); + + let mut summary = CreateResourceAccountSummary { + transaction_summary, + resource_account: None, + }; + + if let Transaction::UserTransaction(txn) = transaction { + summary.resource_account = txn.info.changes.iter().find_map(|change| match change { + WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { + if data.typ.name.as_str() == "Account" + && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() + { + Some(*address.inner()) + } else { + None + } + }, + _ => None, + }); + } + + summary + } +} + +#[async_trait] +impl CliCommand for CreateResourceAccount { + fn command_name(&self) -> &'static str { + "CreateResourceAccount" + } + + async fn execute(self) -> CliTypedResult { + let authentication_key: Vec = if let Some(key) = self.authentication_key { + bcs::to_bytes(&key)? + } else { + vec![] + }; + self.txn_options + .submit_transaction(resource_account_create_resource_account( + self.seed_args.seed()?, + authentication_key, + )) + .await + .map(CreateResourceAccountSummary::from) + } +} diff --git a/crates/aptos/src/account/derive_resource_account.rs b/crates/aptos/src/account/derive_resource_account.rs new file mode 100644 index 0000000000000..e2e3815668c7b --- /dev/null +++ b/crates/aptos/src/account/derive_resource_account.rs @@ -0,0 +1,109 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliError, CliTypedResult}; +use aptos_sdk::rest_client::aptos_api_types::HexEncodedBytes; +use aptos_types::account_address::{create_resource_address, AccountAddress}; +use async_trait::async_trait; +use clap::Parser; +use std::{fmt::Formatter, str::FromStr}; + +/// Encoding for the Resource account seed +#[derive(Debug, Default, Clone, Copy)] +pub enum SeedEncoding { + #[default] + Bcs, + Hex, + Utf8, +} + +const BCS: &str = "bcs"; +const UTF_8: &str = "utf8"; +const HEX: &str = "hex"; + +impl std::fmt::Display for SeedEncoding { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + SeedEncoding::Bcs => BCS, + SeedEncoding::Hex => HEX, + SeedEncoding::Utf8 => UTF_8, + }) + } +} + +impl FromStr for SeedEncoding { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + BCS => Ok(Self::Bcs), + HEX => Ok(Self::Hex), + UTF_8 | "utf-8" | "utf_8" => Ok(Self::Utf8), + _ => Err(CliError::UnableToParse( + "seed-encoding", + "For --seed-encoding please provide one of ['bcs','hex', 'utf8']".to_string(), + )), + } + } +} + +/// A generic interface for allowing for different types of seed phrase inputs +/// +/// The easiest to use is `string_seed` as it will match directly with the b"string" notation in Move. +#[derive(Debug, Parser)] +pub struct ResourceAccountSeed { + /// Resource account seed + /// + /// Seed used in generation of the AccountId of the resource account + /// The seed will be converted to bytes using the encoding from `--seed-encoding`, defaults to `BCS` + #[clap(long)] + pub(crate) seed: String, + + /// Resource account seed encoding + /// + /// The encoding can be one of `Bcs`, `Utf8`, and `Hex`. + /// + /// - Bcs is the legacy functionality of the CLI, it will BCS encode the string, but can be confusing for users e.g. `"ab" -> vector[0x2, 0x61, 0x62]` + /// - Utf8 will encode the string as raw UTF-8 bytes, similar to in Move `b"string"` e.g. `"ab" -> vector[0x61, 0x62]` + /// - Hex will encode the string as raw hex encoded bytes e.g. `"0x6162" -> vector[0x61, 0x62]` + #[clap(long, default_value_t = SeedEncoding::Bcs)] + pub(crate) seed_encoding: SeedEncoding, +} + +impl ResourceAccountSeed { + pub fn seed(self) -> CliTypedResult> { + match self.seed_encoding { + SeedEncoding::Bcs => Ok(bcs::to_bytes(self.seed.as_str())?), + SeedEncoding::Utf8 => Ok(self.seed.as_bytes().to_vec()), + SeedEncoding::Hex => HexEncodedBytes::from_str(self.seed.as_str()) + .map(|inner| inner.0) + .map_err(|err| CliError::UnableToParse("seed", err.to_string())), + } + } +} + +/// Derive the address for a resource account +/// +/// This will not create a resource account, but instead give the deterministic address given +/// a source address and seed. +#[derive(Debug, Parser)] +pub struct DeriveResourceAccount { + /// Address of the creator's account + #[clap(long, alias = "account", value_parser = crate::common::types::load_account_arg)] + pub(crate) address: AccountAddress, + + #[clap(flatten)] + pub(crate) seed_args: ResourceAccountSeed, +} + +#[async_trait] +impl CliCommand for DeriveResourceAccount { + fn command_name(&self) -> &'static str { + "DeriveResourceAccountAddress" + } + + async fn execute(self) -> CliTypedResult { + let seed = self.seed_args.seed()?; + Ok(create_resource_address(self.address, &seed)) + } +} diff --git a/crates/aptos/src/account/fund.rs b/crates/aptos/src/account/fund.rs new file mode 100644 index 0000000000000..5bc6be9df1105 --- /dev/null +++ b/crates/aptos/src/account/fund.rs @@ -0,0 +1,60 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account::create::DEFAULT_FUNDED_COINS, + common::types::{CliCommand, CliTypedResult, FaucetOptions, ProfileOptions, RestOptions}, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; + +/// Fund an account with tokens from a faucet +/// +/// This will create an account if it doesn't exist with the faucet. This is mostly useful +/// for local development and devnet. +#[derive(Debug, Parser)] +pub struct FundWithFaucet { + /// Address to fund + /// + /// If the account wasn't previously created, it will be created when being funded + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub(crate) account: Option, + + /// Number of Octas to fund the account from the faucet + /// + /// The amount added to the account may be limited by the faucet, and may be less + /// than the amount requested. + #[clap(long, default_value_t = DEFAULT_FUNDED_COINS)] + pub(crate) amount: u64, + + #[clap(flatten)] + pub(crate) faucet_options: FaucetOptions, + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand for FundWithFaucet { + fn command_name(&self) -> &'static str { + "FundWithFaucet" + } + + async fn execute(self) -> CliTypedResult { + let address = if let Some(account) = self.account { + account + } else { + self.profile_options.account_address()? + }; + let client = self.rest_options.client(&self.profile_options)?; + self.faucet_options + .fund_account(client, &self.profile_options, self.amount, address) + .await?; + return Ok(format!( + "Added {} Octas to account {}", + self.amount, address + )); + } +} diff --git a/crates/aptos/src/account/key_rotation.rs b/crates/aptos/src/account/key_rotation.rs new file mode 100644 index 0000000000000..e964d4e93ee4c --- /dev/null +++ b/crates/aptos/src/account/key_rotation.rs @@ -0,0 +1,367 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{ + types::{ + account_address_from_auth_key, account_address_from_public_key, + AuthenticationKeyInputOptions, CliCommand, CliConfig, CliError, CliTypedResult, + ConfigSearchMode, EncodingOptions, ExtractPublicKey, ParsePrivateKey, ProfileConfig, + ProfileOptions, PublicKeyInputOptions, RestOptions, TransactionOptions, TransactionSummary, + }, + utils::{prompt_yes, prompt_yes_with_override, read_line}, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, + encoding_type::EncodingType, + PrivateKey, SigningKey, +}; +use aptos_rest_client::{ + aptos_api_types::{AptosError, AptosErrorCode}, + error::{AptosErrorResponse, RestError}, + Client, +}; +use aptos_types::{ + account_address::AccountAddress, + account_config::{RotationProofChallenge, CORE_CODE_ADDRESS}, + transaction::authenticator::AuthenticationKey, +}; +use async_trait::async_trait; +use clap::Parser; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, path::PathBuf}; + +/// Rotate an account's authentication key +/// +/// Rotating the account's authentication key allows you to use a new +/// private key. You must provide a new private key. Once it is +/// rotated you will need to use the original account address, with the +/// new private key. There is an interactive prompt to help you add it +/// to a new profile. +#[derive(Debug, Parser)] +pub struct RotateKey { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + + /// File name that contains the new private key encoded in the type from `--encoding` + #[clap(long, group = "new_private_key_inputs", value_parser)] + pub(crate) new_private_key_file: Option, + + /// New private key encoded in the type from `--encoding` + #[clap(long, group = "new_private_key_inputs")] + pub(crate) new_private_key: Option, + + /// Name of the profile to save the new private key + /// + /// If not provided, it will interactively have you save a profile, + /// unless `--skip_saving_profile` is provided + #[clap(long)] + pub(crate) save_to_profile: Option, + + /// Skip saving profile + /// + /// This skips the interactive profile saving after rotating the authentication key + #[clap(long)] + pub(crate) skip_saving_profile: bool, +} + +impl ParsePrivateKey for RotateKey {} + +impl RotateKey { + /// Extract private key from CLI args + pub fn extract_private_key( + &self, + encoding: EncodingType, + ) -> CliTypedResult> { + self.parse_private_key( + encoding, + self.new_private_key_file.clone(), + self.new_private_key.clone(), + ) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RotateSummary { + message: Option, + transaction: TransactionSummary, +} + +#[async_trait] +impl CliCommand for RotateKey { + fn command_name(&self) -> &'static str { + "RotateKey" + } + + async fn execute(self) -> CliTypedResult { + let new_private_key = self + .extract_private_key(self.txn_options.encoding_options.encoding)? + .ok_or_else(|| { + CliError::CommandArgumentError( + "One of ['--new-private-key', '--new-private-key-file'] must be used" + .to_string(), + ) + })?; + + let (current_private_key, sender_address) = self.txn_options.get_key_and_address()?; + + if new_private_key == current_private_key { + return Err(CliError::CommandArgumentError( + "New private key cannot be the same as the current private key".to_string(), + )); + } + + // Get sequence number for account + let sequence_number = self.txn_options.sequence_number(sender_address).await?; + let auth_key = self.txn_options.auth_key(sender_address).await?; + + let rotation_proof = RotationProofChallenge { + account_address: CORE_CODE_ADDRESS, + module_name: "account".to_string(), + struct_name: "RotationProofChallenge".to_string(), + sequence_number, + originator: sender_address, + current_auth_key: AccountAddress::from_bytes(auth_key) + .map_err(|err| CliError::UnableToParse("auth_key", err.to_string()))?, + new_public_key: new_private_key.public_key().to_bytes().to_vec(), + }; + + let rotation_msg = + bcs::to_bytes(&rotation_proof).map_err(|err| CliError::BCS("rotation_proof", err))?; + + // Signs the struct using both the current private key and the next private key + let rotation_proof_signed_by_current_private_key = + current_private_key.sign_arbitrary_message(&rotation_msg.clone()); + let rotation_proof_signed_by_new_private_key = + new_private_key.sign_arbitrary_message(&rotation_msg); + + let txn_summary = self + .txn_options + .submit_transaction(aptos_stdlib::account_rotate_authentication_key( + 0, + // Existing public key + current_private_key.public_key().to_bytes().to_vec(), + 0, + // New public key + new_private_key.public_key().to_bytes().to_vec(), + rotation_proof_signed_by_current_private_key + .to_bytes() + .to_vec(), + rotation_proof_signed_by_new_private_key.to_bytes().to_vec(), + )) + .await + .map(TransactionSummary::from)?; + + let string = serde_json::to_string_pretty(&txn_summary) + .map_err(|err| CliError::UnableToParse("transaction summary", err.to_string()))?; + + eprintln!("{}", string); + + if let Some(txn_success) = txn_summary.success { + if !txn_success { + return Err(CliError::ApiError( + "Transaction was not executed successfully".to_string(), + )); + } + } else { + return Err(CliError::UnexpectedError( + "Malformed transaction response".to_string(), + )); + } + + let mut profile_name: String; + + if self.save_to_profile.is_none() { + if self.skip_saving_profile + || !prompt_yes("Do you want to create a profile for the new key?") + { + return Ok(RotateSummary { + transaction: txn_summary, + message: None, + }); + } + + eprintln!("Enter the name for the profile"); + profile_name = read_line("Profile name")?.trim().to_string(); + } else { + // We can safely unwrap here + profile_name = self.save_to_profile.unwrap(); + } + + // Check if profile name exists + let mut config = CliConfig::load(ConfigSearchMode::CurrentDirAndParents)?; + + if let Some(ref profiles) = config.profiles { + if profiles.contains_key(&profile_name) { + if let Err(cli_err) = prompt_yes_with_override( + format!( + "Profile {} exits. Do you want to provide a new profile name?", + profile_name + ) + .as_str(), + self.txn_options.prompt_options, + ) { + match cli_err { + CliError::AbortedError => { + return Ok(RotateSummary { + transaction: txn_summary, + message: None, + }); + }, + _ => { + return Err(cli_err); + }, + } + } + + eprintln!("Enter the name for the profile"); + profile_name = read_line("Profile name")?.trim().to_string(); + } + } + + if profile_name.is_empty() { + return Err(CliError::AbortedError); + } + + let mut profile_config = ProfileConfig { + private_key: Some(new_private_key.clone()), + public_key: Some(new_private_key.public_key()), + account: Some(sender_address), + ..self.txn_options.profile_options.profile()? + }; + + if let Some(url) = self.txn_options.rest_options.url { + profile_config.rest_url = Some(url.into()); + } + + if config.profiles.is_none() { + config.profiles = Some(BTreeMap::new()); + } + + config + .profiles + .as_mut() + .unwrap() + .insert(profile_name.clone(), profile_config); + config.save()?; + + eprintln!("Profile {} is saved.", profile_name); + + Ok(RotateSummary { + transaction: txn_summary, + message: Some(format!("Profile {} is saved.", profile_name)), + }) + } +} + +/// Lookup the account address through the on-chain lookup table +/// +/// If the account is rotated, it will provide the address accordingly. If the account was not +/// rotated, it will provide the derived address only if the account exists onchain. +#[derive(Debug, Parser)] +pub struct LookupAddress { + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, + + #[clap(flatten)] + pub(crate) public_key_options: PublicKeyInputOptions, + + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + + #[clap(flatten)] + pub(crate) authentication_key_options: AuthenticationKeyInputOptions, +} + +impl LookupAddress { + pub(crate) fn public_key(&self) -> CliTypedResult { + self.public_key_options + .extract_public_key(self.encoding_options.encoding, &self.profile_options) + } + + pub(crate) fn auth_key(&self) -> CliTypedResult> { + self.authentication_key_options + .extract_auth_key(self.encoding_options.encoding) + } + + /// Builds a rest client + fn rest_client(&self) -> CliTypedResult { + self.rest_options.client(&self.profile_options) + } +} + +#[async_trait] +impl CliCommand for LookupAddress { + fn command_name(&self) -> &'static str { + "LookupAddress" + } + + async fn execute(self) -> CliTypedResult { + let rest_client = self.rest_client()?; + + // TODO: Support arbitrary auth key to support other types like multie25519 + let address = match self.auth_key()? { + Some(key) => account_address_from_auth_key(&key), + None => account_address_from_public_key(&self.public_key()?), + }; + Ok(lookup_address(&rest_client, address, true).await?) + } +} + +pub async fn lookup_address( + rest_client: &Client, + address_key: AccountAddress, + must_exist: bool, +) -> Result { + let originating_resource: OriginatingResource = rest_client + .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::account::OriginatingAddress") + .await? + .into_inner(); + + let table_handle = originating_resource.address_map.handle; + + // The derived address that can be used to look up the original address + match rest_client + .get_table_item_bcs( + table_handle, + "address", + "address", + address_key.to_hex_literal(), + ) + .await + { + Ok(inner) => Ok(inner.into_inner()), + Err(RestError::Api(AptosErrorResponse { + error: + AptosError { + error_code: AptosErrorCode::TableItemNotFound, + .. + }, + .. + })) => { + // If the table item wasn't found, we may check if the account exists + if !must_exist { + Ok(address_key) + } else { + rest_client + .get_account_bcs(address_key) + .await + .map(|_| address_key) + } + }, + Err(err) => Err(err), + } +} + +#[derive(Deserialize)] +pub struct OriginatingResource { + pub address_map: Table, +} + +#[derive(Deserialize)] +pub struct Table { + pub handle: AccountAddress, +} diff --git a/crates/aptos/src/account/list.rs b/crates/aptos/src/account/list.rs new file mode 100644 index 0000000000000..9ee32ce7ecdd4 --- /dev/null +++ b/crates/aptos/src/account/list.rs @@ -0,0 +1,124 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{ + CliCommand, CliConfig, CliError, CliTypedResult, ConfigSearchMode, ProfileOptions, RestOptions, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::{Parser, ValueEnum}; +use serde_json::json; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +#[derive(ValueEnum, Clone, Copy, Debug)] +pub enum ListQuery { + Balance, + Modules, + Resources, +} + +impl Display for ListQuery { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + ListQuery::Balance => "balance", + ListQuery::Modules => "modules", + ListQuery::Resources => "resources", + }; + write!(f, "{}", str) + } +} + +impl FromStr for ListQuery { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "balance" => Ok(ListQuery::Balance), + "modules" => Ok(ListQuery::Modules), + "resources" => Ok(ListQuery::Resources), + _ => Err("Invalid query. Valid values are balance, modules, resources"), + } + } +} + +/// List resources, modules, or balance owned by an address +/// +/// This allows you to list the current resources at the time of query. This can change due to +/// any transactions that have occurred after the request. +#[derive(Debug, Parser)] +pub struct ListAccount { + /// Address of the account you want to list resources/modules/balance for + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub(crate) account: Option, + + /// Type of items to list: [balance, resources, modules] + #[clap(long, value_enum, ignore_case = true, default_value_t = ListQuery::Resources)] + pub(crate) query: ListQuery, + + #[clap(flatten)] + pub(crate) rest_options: RestOptions, + #[clap(flatten)] + pub(crate) profile_options: ProfileOptions, +} + +#[async_trait] +impl CliCommand> for ListAccount { + fn command_name(&self) -> &'static str { + "ListAccount" + } + + async fn execute(self) -> CliTypedResult> { + let account = if let Some(account) = self.account { + account + } else if let Some(Some(account)) = CliConfig::load_profile( + self.profile_options.profile_name(), + ConfigSearchMode::CurrentDirAndParents, + )? + .map(|p| p.account) + { + account + } else { + return Err(CliError::CommandArgumentError( + "Please provide an account using --account or run movement init".to_string(), + )); + }; + + let client = self.rest_options.client(&self.profile_options)?; + let response = match self.query { + ListQuery::Balance => vec![ + client + .get_account_resource( + account, + "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", + ) + .await? + .into_inner() + .unwrap() + .data, + ], + ListQuery::Modules => client + .get_account_modules(account) + .await? + .into_inner() + .into_iter() + .map(|module| json!(module.try_parse_abi().unwrap())) + .collect::>(), + ListQuery::Resources => client + .get_account_resources(account) + .await? + .into_inner() + .into_iter() + .map(|resource| { + let mut map = serde_json::Map::new(); + map.insert(resource.resource_type.to_string(), resource.data); + serde_json::Value::Object(map) + }) + .collect::>(), + }; + + Ok(response) + } +} diff --git a/crates/aptos/src/account/mod.rs b/crates/aptos/src/account/mod.rs index 18e769ecad85b..2421a74327360 100644 --- a/crates/aptos/src/account/mod.rs +++ b/crates/aptos/src/account/mod.rs @@ -4,7 +4,6 @@ use crate::common::types::{CliCommand, CliResult}; use clap::Subcommand; -pub mod balance; pub mod create; pub mod create_resource_account; pub mod derive_resource_account; @@ -24,7 +23,6 @@ pub enum AccountTool { CreateResourceAccount(create_resource_account::CreateResourceAccount), DeriveResourceAccountAddress(derive_resource_account::DeriveResourceAccount), FundWithFaucet(fund::FundWithFaucet), - Balance(balance::Balance), List(list::ListAccount), LookupAddress(key_rotation::LookupAddress), RotateKey(key_rotation::RotateKey), @@ -38,7 +36,6 @@ impl AccountTool { AccountTool::CreateResourceAccount(tool) => tool.execute_serialized().await, AccountTool::DeriveResourceAccountAddress(tool) => tool.execute_serialized().await, AccountTool::FundWithFaucet(tool) => tool.execute_serialized().await, - AccountTool::Balance(tool) => tool.execute_serialized().await, AccountTool::List(tool) => tool.execute_serialized().await, AccountTool::LookupAddress(tool) => tool.execute_serialized().await, AccountTool::RotateKey(tool) => tool.execute_serialized().await, diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index a65a351cec56f..e37664e9992a7 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -8,11 +8,12 @@ use crate::common::{ }, utils::view_json_option_str, }; -use aptos_api_types::ViewFunction; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_rest_client::{ - aptos_api_types::{HexEncodedBytes, WriteResource, WriteSetChange}, + aptos_api_types::{ + EntryFunctionId, HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange, + }, Transaction, }; use aptos_types::{ @@ -22,10 +23,13 @@ use aptos_types::{ use async_trait::async_trait; use bcs::to_bytes; use clap::Parser; -use move_core_types::{ident_str, language_storage::ModuleId}; +use once_cell::sync::Lazy; use serde::Serialize; use serde_json::json; +static GET_TRANSACTION_ENTRY_FUNCTION: Lazy = + Lazy::new(|| "0x1::multisig_account::get_transaction".parse().unwrap()); + /// Create a new multisig account (v2) on-chain. /// /// This will create a new multisig account and make the sender one of the owners. @@ -167,23 +171,21 @@ impl CliCommand for VerifyProposal { // Get multisig transaction via view function. let multisig_transaction = &self .txn_options - .view(ViewFunction { - module: ModuleId::new( - AccountAddress::ONE, - ident_str!("multisig_account").to_owned(), - ), - function: ident_str!("get_transaction").to_owned(), - ty_args: vec![], - args: vec![ - bcs::to_bytes( + .view(ViewRequest { + function: GET_TRANSACTION_ENTRY_FUNCTION.clone(), + type_arguments: vec![], + arguments: vec![ + serde_json::Value::String(String::from( &self .multisig_account_with_sequence_number .multisig_account .multisig_address, - ) - .unwrap(), - bcs::to_bytes(&self.multisig_account_with_sequence_number.sequence_number) - .unwrap(), + )), + serde_json::Value::String( + self.multisig_account_with_sequence_number + .sequence_number + .to_string(), + ), ], }) .await?[0]; diff --git a/crates/aptos/src/account/transfer.rs b/crates/aptos/src/account/transfer.rs new file mode 100644 index 0000000000000..68023e355b43f --- /dev/null +++ b/crates/aptos/src/account/transfer.rs @@ -0,0 +1,115 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions}; +use aptos_cached_packages::aptos_stdlib; +use aptos_rest_client::{ + aptos_api_types::{HashValue, WriteResource, WriteSetChange}, + Transaction, +}; +use aptos_types::account_address::AccountAddress; +use async_trait::async_trait; +use clap::Parser; +use serde::Serialize; +use std::collections::BTreeMap; + +// TODO: Add ability to transfer non-APT coins +// TODO: Add ability to not create account by default +/// Transfer APT between accounts +/// +#[derive(Debug, Parser)] +pub struct TransferCoins { + /// Address of account to send APT to + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub(crate) account: AccountAddress, + + /// Amount of Octas (10^-8 APT) to transfer + #[clap(long)] + pub(crate) amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for TransferCoins { + fn command_name(&self) -> &'static str { + "TransferCoins" + } + + async fn execute(self) -> CliTypedResult { + self.txn_options + .submit_transaction(aptos_stdlib::aptos_account_transfer( + self.account, + self.amount, + )) + .await + .map(TransferSummary::from) + } +} + +const SUPPORTED_COINS: [&str; 1] = ["0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"]; + +/// A shortened transaction output +#[derive(Clone, Debug, Serialize)] +pub struct TransferSummary { + pub gas_unit_price: u64, + pub gas_used: u64, + pub balance_changes: BTreeMap, + pub sender: AccountAddress, + pub success: bool, + pub version: u64, + pub vm_status: String, + pub transaction_hash: HashValue, +} + +impl TransferSummary { + pub fn octa_spent(&self) -> u64 { + self.gas_unit_price * self.gas_used + } +} + +impl From for TransferSummary { + fn from(transaction: Transaction) -> Self { + if let Transaction::UserTransaction(txn) = transaction { + let vm_status = txn.info.vm_status; + let success = txn.info.success; + let sender = *txn.request.sender.inner(); + let gas_unit_price = txn.request.gas_unit_price.0; + let gas_used = txn.info.gas_used.0; + let transaction_hash = txn.info.hash; + let version = txn.info.version.0; + let balance_changes = txn + .info + .changes + .into_iter() + .filter_map(|change| match change { + WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { + if SUPPORTED_COINS.contains(&data.typ.to_string().as_str()) { + Some(( + *address.inner(), + serde_json::to_value(data.data).unwrap_or_default(), + )) + } else { + None + } + }, + _ => None, + }) + .collect(); + + TransferSummary { + gas_unit_price, + gas_used, + balance_changes, + sender, + success, + version, + vm_status, + transaction_hash, + } + } else { + panic!("Can't call From for a non UserTransaction") + } + } +} diff --git a/crates/aptos/src/common/init.rs b/crates/aptos/src/common/init.rs index 904ca225706ec..45bb0be2c66be 100644 --- a/crates/aptos/src/common/init.rs +++ b/crates/aptos/src/common/init.rs @@ -27,7 +27,7 @@ use std::{collections::BTreeMap, str::FromStr}; /// 1 APT (might not actually get that much, depending on the faucet) const NUM_DEFAULT_OCTAS: u64 = 100000000; -/// Tool to initialize current directory for the aptos tool +/// Tool to initialize current directory for the Movement tool /// /// Configuration will be pushed into .aptos/config.yaml #[derive(Debug, Parser)] @@ -183,7 +183,7 @@ impl CliCommand<()> for InitTool { }; // Set the derivation_path to the one user chose - profile_config.derivation_path.clone_from(&derivation_path); + profile_config.derivation_path = derivation_path.clone(); // Private key let private_key = if self.is_hardware_wallet() { diff --git a/crates/aptos/src/common/mod.rs b/crates/aptos/src/common/mod.rs index 016e3e18ca6f4..1be94da1d100b 100644 --- a/crates/aptos/src/common/mod.rs +++ b/crates/aptos/src/common/mod.rs @@ -2,6 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 pub mod init; -pub mod local_simulation; pub mod types; pub mod utils; diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index 496bc523dae72..dabe07a42cb12 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -5,7 +5,6 @@ use super::utils::fund_account; use crate::{ common::{ init::Network, - local_simulation, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, get_account_with_state, get_auth_key, get_sequence_number, parse_json_file, @@ -19,12 +18,12 @@ use crate::{ move_tool::{ArgWithType, FunctionArgType, MemberId}, }; use anyhow::Context; -use aptos_api_types::ViewFunction; use aptos_crypto::{ ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, encoding_type::{EncodingError, EncodingType}, x25519, PrivateKey, ValidCryptoMaterialStringExt, }; +use aptos_gas_profiling::FrameName; use aptos_global_constants::adjust_gas_headroom; use aptos_keygen::KeyGen; use aptos_logger::Level; @@ -45,15 +44,11 @@ use aptos_types::{ SignedTransaction, TransactionArgument, TransactionPayload, TransactionStatus, }, }; -use aptos_vm_types::output::VMOutput; use async_trait::async_trait; use clap::{Parser, ValueEnum}; use hex::FromHexError; -use move_core_types::{ - account_address::AccountAddress, language_storage::TypeTag, vm_status::VMStatus, -}; -use move_model::metadata::{CompilerVersion, LanguageVersion}; -use move_package::source_package::std_lib::StdVersion; +use move_core_types::{account_address::AccountAddress, language_storage::TypeTag}; +use move_package::CompilerVersion; use serde::{Deserialize, Serialize}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; @@ -62,7 +57,7 @@ use std::{ convert::TryFrom, fmt::{Debug, Display, Formatter}, fs::OpenOptions, - path::PathBuf, + path::{Path, PathBuf}, str::FromStr, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; @@ -684,8 +679,8 @@ pub struct HardwareWalletOptions { /// Index of your account in hardware wallet /// - /// This is the simpler version of derivation path e.g `format - [0]` - /// we will translate this index into `[m/44'/637'/0'/0'/0]` + /// This is the simpler version of derivation path e.g format - [0] + /// we will translate this index into [m/44'/637'/0'/0'/0] #[clap(long)] pub derivation_index: Option, } @@ -1049,10 +1044,6 @@ pub struct MovePackageDir { #[clap(long, value_parser = crate::common::utils::parse_map::, default_value = "")] pub(crate) named_addresses: BTreeMap, - /// Override the standard library version by mainnet/testnet/devnet - #[clap(long, value_parser)] - pub override_std: Option, - /// Skip pulling the latest git dependencies /// /// If you don't have a network connection, the compiler may fail due @@ -1066,22 +1057,18 @@ pub struct MovePackageDir { pub bytecode_version: Option, /// Specify the version of the compiler. - /// Currently, default to `v1` - #[clap(long, value_parser = clap::value_parser!(CompilerVersion))] + /// + /// Currently hidden until the official launch of Compiler V2 + #[clap(long, hide = true)] pub compiler_version: Option, - /// Specify the language version to be supported. - /// Currently, default to `v1` - #[clap(long, value_parser = clap::value_parser!(LanguageVersion))] - pub language_version: Option, - /// Do not complain about unknown attributes in Move code. #[clap(long)] pub skip_attribute_checks: bool, /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code. /// NOTE: this behavior will become the default in the future. - /// See + /// See https://github.com/aptos-labs/aptos-core/issues/10335 #[clap(long, env = "APTOS_CHECK_TEST_CODE")] pub check_test_code: bool, } @@ -1093,11 +1080,9 @@ impl MovePackageDir { package_dir: Some(package_dir), output_dir: None, named_addresses: Default::default(), - override_std: None, skip_fetch_latest_git_deps: true, bytecode_version: None, compiler_version: None, - language_version: None, skip_attribute_checks: false, check_test_code: false, } @@ -1352,29 +1337,17 @@ impl From<&Transaction> for TransactionSummary { pending: None, sequence_number: None, }, - Transaction::BlockEpilogueTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - timestamp_us: Some(txn.timestamp.0), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - }, Transaction::ValidatorTransaction(txn) => TransactionSummary { - transaction_hash: txn.transaction_info().hash, + transaction_hash: txn.info.hash, gas_used: None, gas_unit_price: None, pending: None, sender: None, sequence_number: None, - success: Some(txn.transaction_info().success), - timestamp_us: Some(txn.timestamp().0), - version: Some(txn.transaction_info().version.0), - vm_status: Some(txn.transaction_info().vm_status.clone()), + success: Some(txn.info.success), + timestamp_us: Some(txn.timestamp.0), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), }, } } @@ -1528,14 +1501,6 @@ pub struct TransactionOptions { #[clap(flatten)] pub(crate) prompt_options: PromptOptions, - /// If this option is set, simulate the transaction locally. - #[clap(long)] - pub(crate) local: bool, - - /// If this option is set, benchmark the transaction locally. - #[clap(long)] - pub(crate) benchmark: bool, - /// If this option is set, simulate the transaction locally using the debugger and generate /// flamegraphs that reflect the gas usage. #[clap(long)] @@ -1612,12 +1577,9 @@ impl TransactionOptions { get_sequence_number(&client, sender_address).await } - pub async fn view(&self, payload: ViewFunction) -> CliTypedResult> { + pub async fn view(&self, payload: ViewRequest) -> CliTypedResult> { let client = self.rest_client()?; - Ok(client - .view_bcs_with_json_response(&payload, None) - .await? - .into_inner()) + Ok(client.view(&payload, None).await?.into_inner()) } /// Submit a transaction @@ -1760,20 +1722,14 @@ impl TransactionOptions { } } - /// Simulates a transaction locally, using the debugger to fetch required data from remote. - async fn simulate_using_debugger( + /// Simulate the transaction locally using the debugger, with the gas profiler enabled. + pub async fn profile_gas( &self, payload: TransactionPayload, - execute: F, - ) -> CliTypedResult - where - F: FnOnce( - &AptosDebugger, - u64, - SignedTransaction, - aptos_crypto::HashValue, - ) -> CliTypedResult<(VMStatus, VMOutput)>, - { + ) -> CliTypedResult { + println!(); + println!("Simulating transaction locally with the gas profiler..."); + let client = self.rest_client()?; // Fetch the chain states required for the simulation @@ -1805,6 +1761,7 @@ impl TransactionOptions { } }); + // Create and sign the transaction let transaction_factory = TransactionFactory::new(chain_id) .with_gas_unit_price(gas_unit_price) .with_max_gas_amount(max_gas) @@ -1812,19 +1769,49 @@ impl TransactionOptions { let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); let transaction = sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); - let hash = transaction.committed_hash(); + let hash = transaction.clone().committed_hash(); + // Execute the transaction using the debugger let debugger = AptosDebugger::rest_client(client).unwrap(); - let (vm_status, vm_output) = execute(&debugger, version, transaction, hash)?; + let res = debugger.execute_transaction_at_version_with_gas_profiler(version, transaction); + let (vm_status, output, gas_log) = res.map_err(|err| { + CliError::UnexpectedError(format!("failed to simulate txn with gas profiler: {}", err)) + })?; + + // Generate a humen-readable name for the report + let entry_point = gas_log.entry_point(); + + let human_readable_name = match entry_point { + FrameName::Script => "script".to_string(), + FrameName::Function { + module_id, name, .. + } => { + let addr_short = module_id.address().short_str_lossless(); + let addr_truncated = if addr_short.len() > 4 { + &addr_short[..4] + } else { + addr_short.as_str() + }; + format!("0x{}-{}-{}", addr_truncated, module_id.name(), name) + }, + }; + let raw_file_name = format!("txn-{}-{}", hash, human_readable_name); + + // Generate the report + let path = Path::new("gas-profiling").join(raw_file_name); + gas_log.generate_html_report(path, format!("Gas Report - {}", human_readable_name))?; - let success = match vm_output.status() { + // Generate the transaction summary + + // TODO(Gas): double check if this is correct. + let success = match output.status() { TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), TransactionStatus::Discard(_) | TransactionStatus::Retry => None, }; - let summary = TransactionSummary { + Ok(TransactionSummary { transaction_hash: hash.into(), - gas_used: Some(vm_output.gas_used()), + gas_used: Some(output.gas_used()), gas_unit_price: Some(gas_unit_price), pending: None, sender: Some(sender_address), @@ -1833,53 +1820,7 @@ impl TransactionOptions { timestamp_us: None, version: Some(version), // The transaction is not comitted so there is no new version. vm_status: Some(vm_status.to_string()), - }; - - Ok(summary) - } - - /// Simulates a transaction locally. - pub async fn simulate_locally( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Simulating transaction locally..."); - - self.simulate_using_debugger(payload, local_simulation::run_transaction_using_debugger) - .await - } - - /// Benchmarks the transaction payload locally. - /// The transaction is executed multiple times, and the median value is calculated to improve - /// the accuracy of the measurement results. - pub async fn benchmark_locally( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Benchmarking transaction locally..."); - - self.simulate_using_debugger( - payload, - local_simulation::benchmark_transaction_using_debugger, - ) - .await - } - - /// Simulates the transaction locally with the gas profiler enabled. - pub async fn profile_gas( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Simulating transaction locally using the gas profiler..."); - - self.simulate_using_debugger( - payload, - local_simulation::profile_transaction_using_debugger, - ) - .await + }) } pub async fn estimate_gas_price(&self) -> CliTypedResult { @@ -2082,21 +2023,6 @@ impl TryInto for EntryFunctionArguments { } } -impl TryInto for EntryFunctionArguments { - type Error = CliError; - - fn try_into(self) -> Result { - let view_function_args = self.check_input_style()?; - let function_id: MemberId = (&view_function_args).try_into()?; - Ok(ViewFunction { - module: function_id.module_id, - function: function_id.member_id, - ty_args: view_function_args.type_arg_vec.try_into()?, - args: view_function_args.arg_vec.try_into()?, - }) - } -} - impl TryInto for EntryFunctionArguments { type Error = CliError; @@ -2214,13 +2140,3 @@ impl TryInto for ScriptFunctionArgumentsJSON { }) } } - -#[derive(Parser)] -pub struct OverrideSizeCheckOption { - /// Whether to override the check for maximal size of published data - /// - /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it - /// will still be blocked from publishing. - #[clap(long)] - pub(crate) override_size_check: bool, -} diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index 9fcd7bc507c08..a1a053e5c3a97 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -14,6 +14,7 @@ use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; use aptos_keygen::KeyGen; use aptos_logger::{debug, Level}; use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, FaucetClient, State}; +#[cfg(feature = "aptos")] use aptos_telemetry::service::telemetry_is_disabled; use aptos_types::{ account_address::create_multisig_account_address, @@ -85,6 +86,7 @@ pub async fn to_common_result( ) -> CliResult { let latency = start_time.elapsed(); + #[cfg(feature = "aptos")] if !telemetry_is_disabled() { let error = if let Err(ref error) = result { // Only print the error type @@ -129,6 +131,7 @@ async fn send_telemetry_event(command: &str, latency: Duration, error: Option<&s // Collect the build information let build_information = cli_build_information(); + #[cfg(feature = "aptos")] // Send the event aptos_telemetry::cli_metrics::send_cli_telemetry_event( build_information, @@ -428,22 +431,6 @@ pub fn read_line(input_name: &'static str) -> CliTypedResult { Ok(input_buf) } -/// Lists the content of a directory -pub fn read_dir_files( - path: &Path, - predicate: impl Fn(&Path) -> bool, -) -> CliTypedResult> { - let to_cli_err = |err| CliError::IO(path.display().to_string(), err); - let mut result = vec![]; - for entry in std::fs::read_dir(path).map_err(to_cli_err)? { - let path = entry.map_err(to_cli_err)?.path(); - if predicate(path.as_path()) { - result.push(path) - } - } - Ok(result) -} - /// Fund account (and possibly create it) from a faucet. This function waits for the /// transaction on behalf of the caller. pub async fn fund_account( @@ -488,6 +475,7 @@ pub async fn wait_for_transactions( pub fn start_logger(level: Level) { let mut logger = aptos_logger::Logger::new(); + #[cfg(feature = "aptos")] logger.channel_size(1000).is_async(false).level(level); logger.build(); } @@ -497,19 +485,9 @@ pub async fn profile_or_submit( payload: TransactionPayload, txn_options_ref: &TransactionOptions, ) -> CliTypedResult { - if txn_options_ref.profile_gas && txn_options_ref.benchmark { - return Err(CliError::UnexpectedError( - "Cannot perform benchmarking and gas profiling at the same time.".to_string(), - )); - } - // Profile gas if needed. if txn_options_ref.profile_gas { txn_options_ref.profile_gas(payload).await - } else if txn_options_ref.benchmark { - txn_options_ref.benchmark_locally(payload).await - } else if txn_options_ref.local { - txn_options_ref.simulate_locally(payload).await } else { // Otherwise submit the transaction. txn_options_ref diff --git a/crates/aptos/src/config/mod.rs b/crates/aptos/src/config/mod.rs new file mode 100644 index 0000000000000..dae3b77695f39 --- /dev/null +++ b/crates/aptos/src/config/mod.rs @@ -0,0 +1,356 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + CliCommand, CliConfig, CliError, CliResult, CliTypedResult, ConfigSearchMode, + ProfileSummary, CONFIG_FOLDER, + }, + utils::{create_dir_if_not_exist, current_dir, read_from_file, write_to_user_only_file}, + }, + genesis::git::{from_yaml, to_yaml}, + Tool, +}; +use aptos_cli_common::generate_cli_completions; +use async_trait::async_trait; +use clap::{Parser, ValueEnum}; +use clap_complete::Shell; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt::Formatter, path::PathBuf, str::FromStr}; + +/// Tool for interacting with configuration of the Movement CLI tool +/// +/// This tool handles the global configuration of the CLI tool for +/// default configuration, and user specific settings. +#[derive(Parser)] +pub enum ConfigTool { + GenerateShellCompletions(GenerateShellCompletions), + SetGlobalConfig(SetGlobalConfig), + ShowGlobalConfig(ShowGlobalConfig), + ShowProfiles(ShowProfiles), +} + +impl ConfigTool { + pub async fn execute(self) -> CliResult { + match self { + ConfigTool::GenerateShellCompletions(tool) => tool.execute_serialized_success().await, + ConfigTool::SetGlobalConfig(tool) => tool.execute_serialized().await, + ConfigTool::ShowGlobalConfig(tool) => tool.execute_serialized().await, + ConfigTool::ShowProfiles(tool) => tool.execute_serialized().await, + } + } +} + +/// Generate shell completion files +/// +/// First generate the completion file, then follow the shell specific directions on how +/// to install the completion file. +#[derive(Parser)] +pub struct GenerateShellCompletions { + /// Shell to generate completions + #[clap(long, value_enum, ignore_case = true)] + shell: Shell, + + /// File to output shell completions to + #[clap(long, value_parser)] + output_file: PathBuf, +} + +#[async_trait] +impl CliCommand<()> for GenerateShellCompletions { + fn command_name(&self) -> &'static str { + "GenerateShellCompletions" + } + + async fn execute(self) -> CliTypedResult<()> { + generate_cli_completions::("aptos", self.shell, self.output_file.as_path()) + .map_err(|err| CliError::IO(self.output_file.display().to_string(), err)) + } +} + +/// Set global configuration settings +/// +/// Any configuration flags that are not provided will not be changed +#[derive(Parser, Debug)] +pub struct SetGlobalConfig { + /// A configuration for where to place and use the config + /// + /// `Workspace` will put the `.aptos/` folder in the current directory, where + /// `Global` will put the `.aptos/` folder in your home directory + #[clap(long)] + config_type: Option, + /// A configuration for how to expect the prompt response + /// + /// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where + /// "no" runs cli with "--assume-no", default: "prompt" + #[clap(long)] + default_prompt_response: Option, +} + +#[async_trait] +impl CliCommand for SetGlobalConfig { + fn command_name(&self) -> &'static str { + "SetGlobalConfig" + } + + async fn execute(self) -> CliTypedResult { + // Load the global config + let mut config = GlobalConfig::load()?; + + // Enable all features that are actually listed + if let Some(config_type) = self.config_type { + config.config_type = Some(config_type); + } + + if let Some(default_prompt_response) = self.default_prompt_response { + config.default_prompt_response = default_prompt_response; + } + + config.save()?; + config.display() + } +} + +/// Shows the current profiles available +/// +/// This will only show public information and will not show +/// private information +#[derive(Parser, Debug)] +pub struct ShowProfiles { + /// Which profile to show + /// + /// If provided, show only this profile + #[clap(long)] + profile: Option, +} + +#[async_trait] +impl CliCommand> for ShowProfiles { + fn command_name(&self) -> &'static str { + "ShowProfiles" + } + + async fn execute(self) -> CliTypedResult> { + // Load the profile config + let config = CliConfig::load(ConfigSearchMode::CurrentDir)?; + Ok(config + .profiles + .unwrap_or_default() + .into_iter() + .filter(|(key, _)| { + if let Some(ref profile) = self.profile { + profile == key + } else { + true + } + }) + .map(|(key, profile)| (key, ProfileSummary::from(&profile))) + .collect()) + } +} + +/// Shows the properties in the global config +#[derive(Parser, Debug)] +pub struct ShowGlobalConfig {} + +#[async_trait] +impl CliCommand for ShowGlobalConfig { + fn command_name(&self) -> &'static str { + "ShowGlobalConfig" + } + + async fn execute(self) -> CliTypedResult { + // Load the global config + let config = GlobalConfig::load()?; + + config.display() + } +} + +const GLOBAL_CONFIG_FILE: &str = "global_config.yaml"; + +/// A global configuration for global settings related to a user +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct GlobalConfig { + /// Whether to be using Global or Workspace mode + #[serde(skip_serializing_if = "Option::is_none")] + pub config_type: Option, + /// Prompt response type + #[serde(default)] + pub default_prompt_response: PromptResponseType, +} + +impl GlobalConfig { + /// Fill in defaults for display via the CLI + pub fn display(mut self) -> CliTypedResult { + if self.config_type.is_none() { + self.config_type = Some(ConfigType::default()); + } + + Ok(self) + } + + pub fn load() -> CliTypedResult { + let path = global_folder()?.join(GLOBAL_CONFIG_FILE); + if path.exists() { + from_yaml(&String::from_utf8(read_from_file(path.as_path())?)?) + } else { + // If we don't have a config, let's load the default + Ok(GlobalConfig::default()) + } + } + + /// Get the config location based on the type + pub fn get_config_location(&self, mode: ConfigSearchMode) -> CliTypedResult { + match self.config_type.unwrap_or_default() { + ConfigType::Global => global_folder(), + ConfigType::Workspace => find_workspace_config(current_dir()?, mode), + } + } + + /// Get the prompt options from global config + pub fn get_default_prompt_response(&self) -> Option { + match self.default_prompt_response { + PromptResponseType::Prompt => None, // prompt + PromptResponseType::Yes => Some(true), // assume_yes + PromptResponseType::No => Some(false), // assume_no + } + } + + fn save(&self) -> CliTypedResult<()> { + let global_folder = global_folder()?; + create_dir_if_not_exist(global_folder.as_path())?; + + write_to_user_only_file( + global_folder.join(GLOBAL_CONFIG_FILE).as_path(), + "Global Config", + &to_yaml(&self)?.into_bytes(), + ) + } +} + +fn global_folder() -> CliTypedResult { + if let Some(dir) = dirs::home_dir() { + Ok(dir.join(CONFIG_FOLDER)) + } else { + Err(CliError::UnexpectedError( + "Unable to retrieve home directory".to_string(), + )) + } +} + +fn find_workspace_config( + starting_path: PathBuf, + mode: ConfigSearchMode, +) -> CliTypedResult { + match mode { + ConfigSearchMode::CurrentDir => Ok(starting_path.join(CONFIG_FOLDER)), + ConfigSearchMode::CurrentDirAndParents => { + let mut current_path = starting_path.clone(); + loop { + current_path.push(CONFIG_FOLDER); + if current_path.is_dir() { + break Ok(current_path); + } else if !(current_path.pop() && current_path.pop()) { + // If we aren't able to find the folder, we'll create a new one right here + break Ok(starting_path.join(CONFIG_FOLDER)); + } + } + }, + } +} + +const GLOBAL: &str = "global"; +const WORKSPACE: &str = "workspace"; + +/// A configuration for where to place and use the config +/// +/// Workspace allows for multiple configs based on location, where +/// Global allows for one config for every part of the code +#[derive(Debug, Copy, Clone, Serialize, Deserialize, ValueEnum)] +pub enum ConfigType { + /// Per system user configuration put in `/.aptos` + Global, + /// Per directory configuration put in `/.aptos` + Workspace, +} + +impl Default for ConfigType { + fn default() -> Self { + // TODO: When we version up, we can change this to global + Self::Workspace + } +} + +impl std::fmt::Display for ConfigType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + ConfigType::Global => GLOBAL, + ConfigType::Workspace => WORKSPACE, + }) + } +} + +impl FromStr for ConfigType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + GLOBAL => Ok(Self::Global), + WORKSPACE => Ok(Self::Workspace), + _ => Err(CliError::CommandArgumentError( + "Invalid config type, must be one of [global, workspace]".to_string(), + )), + } + } +} + +const PROMPT: &str = "prompt"; +const ASSUME_YES: &str = "yes"; +const ASSUME_NO: &str = "no"; + +/// A configuration for how to expect the prompt response +/// +/// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where +/// "no" runs cli with "--assume-no", default: "prompt" +#[derive(Debug, Copy, Clone, Serialize, Deserialize, ValueEnum)] +pub enum PromptResponseType { + /// normal prompt + Prompt, + /// `--assume-yes` + Yes, + /// `--assume-no` + No, +} + +impl Default for PromptResponseType { + fn default() -> Self { + Self::Prompt + } +} + +impl std::fmt::Display for PromptResponseType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + PromptResponseType::Prompt => PROMPT, + PromptResponseType::Yes => ASSUME_YES, + PromptResponseType::No => ASSUME_NO, + }) + } +} + +impl FromStr for PromptResponseType { + type Err = CliError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + PROMPT => Ok(Self::Prompt), + ASSUME_YES => Ok(Self::Yes), + ASSUME_NO => Ok(Self::No), + _ => Err(CliError::CommandArgumentError( + "Invalid prompt response type, must be one of [yes, no, prompt]".to_string(), + )), + } + } +} diff --git a/crates/aptos/src/ffi.rs b/crates/aptos/src/ffi.rs new file mode 100644 index 0000000000000..41964747f5149 --- /dev/null +++ b/crates/aptos/src/ffi.rs @@ -0,0 +1,84 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![allow(unsafe_code)] + +use crate::Tool; +use clap::Parser; +use std::{ + ffi::{c_char, CStr, CString}, + thread, +}; +use tokio::runtime::Runtime; + +/// # Safety +/// +/// Run the aptos CLI synchronously +/// Note: This function should only be called from other SDK (i.g Typescript) +/// +/// Return: the pointer to CLIResult c string +#[no_mangle] +pub unsafe extern "C" fn run_aptos_sync(s: *const c_char) -> *const c_char { + let c_str = unsafe { + assert!(!s.is_null()); + CStr::from_ptr(s) + }; + + // split string by spaces + let input_string = c_str.to_str().unwrap().split_whitespace(); + + // Create a new Tokio runtime and block on the execution of `cli.execute()` + let result_string = Runtime::new().unwrap().block_on(async move { + let cli = Tool::parse_from(input_string); + cli.execute().await + }); + + let res_cstr = CString::new(result_string.unwrap()).unwrap(); + + // Return a pointer to the C string + res_cstr.into_raw() +} + +/// # Safety +/// +/// Run the aptos CLI async; Use this function if you are expecting the aptos CLI command +/// to run in the background, or different thread +/// Note: This function should only be called from other SDK (i.g Typescript) +/// +/// Return: the pointer to c string: 'true' +#[no_mangle] +pub unsafe extern "C" fn run_aptos_async(s: *mut c_char) -> *mut c_char { + println!("Running aptos..."); + let c_str = unsafe { + assert!(!s.is_null()); + CStr::from_ptr(s) + }; + + // Spawn a new thread to run the CLI + thread::spawn(move || { + let rt = Runtime::new().unwrap(); + let input_string = c_str.to_str().unwrap().split_whitespace(); + let cli = Tool::parse_from(input_string); + + // Run the CLI once + rt.block_on(async { cli.execute().await }) + .expect("Failed to run CLI"); + }); + + // Return pointer + CString::new("true").unwrap().into_raw() +} + +/// # Safety +/// +/// After running the aptos CLI using FFI. Make sure to invoke this method to free up or +/// deallocate the memory +#[no_mangle] +pub unsafe extern "C" fn free_cstring(s: *mut c_char) { + unsafe { + if s.is_null() { + return; + } + let _ = CString::from_raw(s); + }; +} diff --git a/crates/aptos/src/genesis/git.rs b/crates/aptos/src/genesis/git.rs new file mode 100644 index 0000000000000..baf4e278c25c3 --- /dev/null +++ b/crates/aptos/src/genesis/git.rs @@ -0,0 +1,264 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{CliError, CliTypedResult}, + utils::{create_dir_if_not_exist, write_to_file}, + }, + CliCommand, +}; +use aptos_config::config::Token; +use aptos_framework::ReleaseBundle; +use aptos_genesis::config::Layout; +use aptos_github_client::Client as GithubClient; +use async_trait::async_trait; +use clap::Parser; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + fmt::Debug, + io::Read, + path::{Path, PathBuf}, + str::FromStr, +}; + +pub const LAYOUT_FILE: &str = "layout.yaml"; +pub const OPERATOR_FILE: &str = "operator.yaml"; +pub const OWNER_FILE: &str = "owner.yaml"; +pub const FRAMEWORK_NAME: &str = "framework.mrb"; +pub const BALANCES_FILE: &str = "balances.yaml"; +pub const EMPLOYEE_VESTING_ACCOUNTS_FILE: &str = "employee_vesting_accounts.yaml"; + +/// Setup a shared Git repository for Genesis +/// +/// This will setup a folder or an online Github repository to be used +/// for Genesis. If it's the local, it will create the folders but not +/// set up a Git repository. +#[derive(Parser)] +pub struct SetupGit { + #[clap(flatten)] + pub(crate) git_options: GitOptions, + + /// Path to the `Layout` file which defines where all the files are + #[clap(long, value_parser)] + pub(crate) layout_file: PathBuf, +} + +#[async_trait] +impl CliCommand<()> for SetupGit { + fn command_name(&self) -> &'static str { + "SetupGit" + } + + async fn execute(self) -> CliTypedResult<()> { + let layout = Layout::from_disk(&self.layout_file)?; + + // Upload layout file to ensure we can read later + let client = self.git_options.get_client()?; + client.put(Path::new(LAYOUT_FILE), &layout)?; + + Ok(()) + } +} + +#[derive(Clone, Debug, Default)] +pub struct GithubRepo { + owner: String, + repository: String, +} + +impl FromStr for GithubRepo { + type Err = CliError; + + fn from_str(s: &str) -> Result { + let parts: Vec<_> = s.split('/').collect(); + if parts.len() != 2 { + Err(CliError::CommandArgumentError("Invalid repository must be of the form 'owner/repository` e.g. 'aptos-labs/aptos-core'".to_string())) + } else { + Ok(GithubRepo { + owner: parts.first().unwrap().to_string(), + repository: parts.get(1).unwrap().to_string(), + }) + } + } +} + +#[derive(Clone, Default, Parser)] +pub struct GitOptions { + /// Github repository e.g. 'aptos-labs/aptos-core' + /// + /// Mutually exclusive with `--local-repository-dir` + #[clap(long)] + pub(crate) github_repository: Option, + + /// Github repository branch e.g. main + #[clap(long, default_value = "main")] + pub(crate) github_branch: String, + + /// Path to Github API token. Token must have repo:* permissions + #[clap(long, value_parser)] + pub(crate) github_token_file: Option, + + /// Path to local git repository + /// + /// Mutually exclusive with `--github-repository` + #[clap(long, value_parser)] + pub(crate) local_repository_dir: Option, +} + +impl GitOptions { + pub fn get_client(self) -> CliTypedResult { + if self.github_repository.is_none() + && self.github_token_file.is_none() + && self.local_repository_dir.is_some() + { + Ok(Client::local(self.local_repository_dir.unwrap())) + } else if self.github_repository.is_some() + && self.github_token_file.is_some() + && self.local_repository_dir.is_none() + { + Client::github( + self.github_repository.unwrap(), + self.github_branch, + self.github_token_file.unwrap(), + ) + } else { + Err(CliError::CommandArgumentError("Must provide either only --local-repository-dir or both --github-repository and --github-token-path".to_string())) + } + } +} + +/// A client for abstracting away local vs Github storage +/// +/// Note: Writes do not commit locally +pub enum Client { + Local(PathBuf), + Github(GithubClient), +} + +impl Client { + pub fn local(path: PathBuf) -> Client { + Client::Local(path) + } + + pub fn github( + repository: GithubRepo, + branch: String, + token_path: PathBuf, + ) -> CliTypedResult { + let token = Token::FromDisk(token_path).read_token()?; + Ok(Client::Github(GithubClient::new( + repository.owner, + repository.repository, + branch, + token, + ))) + } + + /// Retrieves an object as a YAML encoded file from the appropriate storage + pub fn get(&self, path: &Path) -> CliTypedResult { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(path); + + if !path.exists() { + return Err(CliError::UnableToReadFile( + path.display().to_string(), + "File not found".to_string(), + )); + } + + eprintln!("Reading {}", path.display()); + let mut file = std::fs::File::open(path.as_path()) + .map_err(|e| CliError::IO(path.display().to_string(), e))?; + + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|e| CliError::IO(path.display().to_string(), e))?; + from_yaml(&contents) + }, + Client::Github(client) => { + from_base64_encoded_yaml(&client.get_file(&path.display().to_string())?) + }, + } + } + + /// Puts an object as a YAML encoded file to the appropriate storage + pub fn put(&self, name: &Path, input: &T) -> CliTypedResult<()> { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(name); + + // Create repository path and any sub-directories + if let Some(dir) = path.parent() { + self.create_dir(dir)?; + } else { + return Err(CliError::UnexpectedError(format!( + "Path should always have a parent {}", + path.display() + ))); + } + write_to_file( + path.as_path(), + &path.display().to_string(), + to_yaml(input)?.as_bytes(), + )?; + }, + Client::Github(client) => { + client.put(&name.display().to_string(), &to_base64_encoded_yaml(input)?)?; + }, + } + + Ok(()) + } + + pub fn create_dir(&self, dir: &Path) -> CliTypedResult<()> { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(dir); + create_dir_if_not_exist(path.as_path())?; + }, + Client::Github(_) => { + // There's no such thing as an empty directory in Git, so do nothing + }, + } + + Ok(()) + } + + /// Retrieve framework release bundle. + pub fn get_framework(&self) -> CliTypedResult { + match self { + Client::Local(local_repository_path) => { + let path = local_repository_path.join(FRAMEWORK_NAME); + if !path.exists() { + return Err(CliError::UnableToReadFile( + path.display().to_string(), + "File not found".to_string(), + )); + } + Ok(ReleaseBundle::read(path)?) + }, + Client::Github(client) => { + let bytes = base64::decode(client.get_file(FRAMEWORK_NAME)?)?; + Ok(bcs::from_bytes::(&bytes)?) + }, + } + } +} + +pub fn to_yaml(input: &T) -> CliTypedResult { + Ok(serde_yaml::to_string(input)?) +} + +pub fn from_yaml(input: &str) -> CliTypedResult { + Ok(serde_yaml::from_str(input)?) +} + +pub fn to_base64_encoded_yaml(input: &T) -> CliTypedResult { + Ok(base64::encode(to_yaml(input)?)) +} + +pub fn from_base64_encoded_yaml(input: &str) -> CliTypedResult { + from_yaml(&String::from_utf8(base64::decode(input)?)?) +} diff --git a/crates/aptos/src/genesis/keys.rs b/crates/aptos/src/genesis/keys.rs new file mode 100644 index 0000000000000..d4b619f6d04e5 --- /dev/null +++ b/crates/aptos/src/genesis/keys.rs @@ -0,0 +1,344 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{CliError, CliTypedResult, OptionalPoolAddressArgs, PromptOptions, RngArgs}, + utils::{ + check_if_file_exists, create_dir_if_not_exist, current_dir, dir_default_to_current, + read_from_file, write_to_user_only_file, + }, + }, + genesis::git::{from_yaml, to_yaml, GitOptions, LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE}, + governance::CompileScriptFunction, + CliCommand, +}; +use aptos_genesis::{ + config::{HostAndPort, Layout, OperatorConfiguration, OwnerConfiguration}, + keys::{generate_key_objects, PublicIdentity}, +}; +use aptos_types::{ + account_address::AccountAddress, + transaction::{Script, Transaction, WriteSetPayload}, +}; +use async_trait::async_trait; +use clap::Parser; +use std::path::{Path, PathBuf}; + +const PRIVATE_KEYS_FILE: &str = "private-keys.yaml"; +pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml"; +const VALIDATOR_FILE: &str = "validator-identity.yaml"; +const VFN_FILE: &str = "validator-full-node-identity.yaml"; + +/// Generate keys for a new validator +/// +/// Generates account key, consensus key, and network key for a validator +/// These keys are used for running a validator or operator in a network +#[derive(Parser)] +pub struct GenerateKeys { + /// Output directory for the key files + #[clap(long, value_parser)] + pub(crate) output_dir: Option, + + #[clap(flatten)] + pub(crate) pool_address_args: OptionalPoolAddressArgs, + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, + #[clap(flatten)] + pub rng_args: RngArgs, +} + +#[async_trait] +impl CliCommand> for GenerateKeys { + fn command_name(&self) -> &'static str { + "GenerateKeys" + } + + async fn execute(self) -> CliTypedResult> { + let output_dir = dir_default_to_current(self.output_dir.clone())?; + + let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE); + let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE); + let validator_file = output_dir.join(VALIDATOR_FILE); + let vfn_file = output_dir.join(VFN_FILE); + check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?; + check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?; + check_if_file_exists(validator_file.as_path(), self.prompt_options)?; + check_if_file_exists(vfn_file.as_path(), self.prompt_options)?; + + let mut key_generator = self.rng_args.key_generator()?; + let (mut validator_blob, mut vfn_blob, private_identity, public_identity) = + generate_key_objects(&mut key_generator)?; + + // Allow for the owner to be different than the operator + if let Some(pool_address) = self.pool_address_args.pool_address { + validator_blob.account_address = Some(pool_address); + vfn_blob.account_address = Some(pool_address); + } + + // Create the directory if it doesn't exist + create_dir_if_not_exist(output_dir.as_path())?; + + write_to_user_only_file( + private_keys_file.as_path(), + PRIVATE_KEYS_FILE, + to_yaml(&private_identity)?.as_bytes(), + )?; + write_to_user_only_file( + public_keys_file.as_path(), + PUBLIC_KEYS_FILE, + to_yaml(&public_identity)?.as_bytes(), + )?; + write_to_user_only_file( + validator_file.as_path(), + VALIDATOR_FILE, + to_yaml(&validator_blob)?.as_bytes(), + )?; + write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?; + Ok(vec![ + public_keys_file, + private_keys_file, + validator_file, + vfn_file, + ]) + } +} + +/// Set validator configuration for a single validator +/// +/// This will set the validator configuration for a single validator in the git repository. +/// It will have to be run for each validator expected at genesis. +#[derive(Parser)] +pub struct SetValidatorConfiguration { + /// Name of the validator + #[clap(long)] + pub(crate) username: String, + + /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180 + #[clap(long)] + pub(crate) validator_host: HostAndPort, + + /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180 + #[clap(long)] + pub(crate) full_node_host: Option, + + /// Stake amount for stake distribution + #[clap(long, default_value_t = 1)] + pub(crate) stake_amount: u64, + + /// Commission rate to pay operator + /// + /// This is a percentage between 0% and 100% + #[clap(long, default_value_t = 0)] + pub(crate) commission_percentage: u64, + + /// Whether the validator will be joining the genesis validator set + /// + /// If set this validator will already be in the validator set at genesis + #[clap(long)] + pub(crate) join_during_genesis: bool, + + /// Path to private identity generated from GenerateKeys + #[clap(long, value_parser)] + pub(crate) owner_public_identity_file: Option, + + /// Path to operator public identity, defaults to owner identity + #[clap(long, value_parser)] + pub(crate) operator_public_identity_file: Option, + + /// Path to voter public identity, defaults to owner identity + #[clap(long, value_parser)] + pub(crate) voter_public_identity_file: Option, + + #[clap(flatten)] + pub(crate) git_options: GitOptions, +} + +#[async_trait] +impl CliCommand<()> for SetValidatorConfiguration { + fn command_name(&self) -> &'static str { + "SetValidatorConfiguration" + } + + async fn execute(self) -> CliTypedResult<()> { + // Load owner + let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file { + owner_keys_file + } else { + current_dir()?.join(PUBLIC_KEYS_FILE) + }; + let owner_identity = read_public_identity_file(owner_keys_file.as_path())?; + + // Load voter + let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file { + read_public_identity_file(voter_keys_file.as_path())? + } else { + owner_identity.clone() + }; + + // Load operator + let (operator_identity, operator_keys_file) = + if let Some(operator_keys_file) = self.operator_public_identity_file { + ( + read_public_identity_file(operator_keys_file.as_path())?, + operator_keys_file, + ) + } else { + (owner_identity.clone(), owner_keys_file) + }; + + // Extract the possible optional fields + let consensus_public_key = + if let Some(consensus_public_key) = operator_identity.consensus_public_key { + consensus_public_key + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read consensus public key from public identity file {}", + operator_keys_file.display() + ))); + }; + + let validator_network_public_key = if let Some(validator_network_public_key) = + operator_identity.validator_network_public_key + { + validator_network_public_key + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read validator network public key from public identity file {}", + operator_keys_file.display() + ))); + }; + + let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) = + operator_identity.consensus_proof_of_possession + { + consensus_proof_of_possession + } else { + return Err(CliError::CommandArgumentError(format!( + "Failed to read consensus proof of possession from public identity file {}", + operator_keys_file.display() + ))); + }; + + // Only add the public key if there is a full node + let full_node_network_public_key = if self.full_node_host.is_some() { + operator_identity.full_node_network_public_key + } else { + None + }; + + // Build operator configuration file + let operator_config = OperatorConfiguration { + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key.clone(), + consensus_public_key, + consensus_proof_of_possession, + validator_network_public_key, + validator_host: self.validator_host, + full_node_network_public_key, + full_node_host: self.full_node_host, + }; + + let owner_config = OwnerConfiguration { + owner_account_address: owner_identity.account_address.into(), + owner_account_public_key: owner_identity.account_public_key, + voter_account_address: voter_identity.account_address.into(), + voter_account_public_key: voter_identity.account_public_key, + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key, + stake_amount: self.stake_amount, + commission_percentage: self.commission_percentage, + join_during_genesis: self.join_during_genesis, + }; + + let directory = PathBuf::from(&self.username); + let operator_file = directory.join(OPERATOR_FILE); + let owner_file = directory.join(OWNER_FILE); + + let git_client = self.git_options.get_client()?; + git_client.put(operator_file.as_path(), &operator_config)?; + git_client.put(owner_file.as_path(), &owner_config) + } +} + +pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult { + let bytes = read_from_file(public_identity_file)?; + from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?) +} + +/// Generate a Layout template file +/// +/// This will generate a layout template file for genesis with some default values. To start a +/// new chain, these defaults should be carefully thought through and chosen. +#[derive(Parser)] +pub struct GenerateLayoutTemplate { + /// Path of the output layout template + #[clap(long, value_parser, default_value = LAYOUT_FILE)] + pub(crate) output_file: PathBuf, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<()> for GenerateLayoutTemplate { + fn command_name(&self) -> &'static str { + "GenerateLayoutTemplate" + } + + async fn execute(self) -> CliTypedResult<()> { + check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; + let layout = Layout::default(); + + write_to_user_only_file( + self.output_file.as_path(), + &self.output_file.display().to_string(), + to_yaml(&layout)?.as_bytes(), + ) + } +} + +/// Generate a WriteSet genesis +/// +/// This will compile a Move script and generate a writeset from that script. +#[derive(Parser)] +pub struct GenerateAdminWriteSet { + /// Path of the output genesis file + #[clap(long, value_parser)] + pub(crate) output_file: PathBuf, + + /// Address of the account which execute this script. + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub(crate) execute_as: AccountAddress, + + #[clap(flatten)] + pub(crate) compile_proposal_args: CompileScriptFunction, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand<()> for GenerateAdminWriteSet { + fn command_name(&self) -> &'static str { + "GenerateAdminWriteSet" + } + + async fn execute(self) -> CliTypedResult<()> { + check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; + let (bytecode, _script_hash) = self + .compile_proposal_args + .compile("GenerateAdminWriteSet", self.prompt_options)?; + + let txn = Transaction::GenesisTransaction(WriteSetPayload::Script { + execute_as: self.execute_as, + script: Script::new(bytecode, vec![], vec![]), + }); + + write_to_user_only_file( + self.output_file.as_path(), + &self.output_file.display().to_string(), + &bcs::to_bytes(&txn).map_err(CliError::from)?, + ) + } +} diff --git a/crates/aptos/src/genesis/mod.rs b/crates/aptos/src/genesis/mod.rs index 7dabbc9c609e5..e4b4c0bb281b5 100644 --- a/crates/aptos/src/genesis/mod.rs +++ b/crates/aptos/src/genesis/mod.rs @@ -257,9 +257,9 @@ pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult CliTypedResult PublicIdentity { + let path = base_dir.join(name).join(PUBLIC_KEYS_FILE); + from_yaml(&String::from_utf8(read_from_file(path.as_path()).unwrap()).unwrap()).unwrap() +} + +async fn create_users( + num_validators: u8, + num_other_users: u8, + dir: &TempPath, + commission_rates: &mut Vec, + is_mainnet: bool, +) -> GitOptions { + let mut users: HashMap = HashMap::new(); + for i in 0..num_validators { + let name = format!("owner-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + + let name = format!("operator-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + + let name = format!("voter-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + } + for i in 0..num_other_users { + let name = format!("other-{}", i); + let output_dir = generate_keys(dir.path(), &name).await; + users.insert(name, output_dir); + } + + // Get the validator's names + let validator_names = users + .keys() + .map(|key| key.to_string()) + .filter(|name| name.starts_with("owner")) + .collect(); + let mut key_gen = KeyGen::from_seed([num_validators.saturating_add(1); 32]); + + // First step is setup the local git repo + let root_private_key = if !is_mainnet { + Some(key_gen.generate_ed25519_private_key()) + } else { + None + }; + let git_options = + setup_git_dir(root_private_key.as_ref(), validator_names, ChainId::test()).await; + + // Only write validators to folders + for i in 0..num_validators { + let owner_name = format!("owner-{}", i); + let owner_identity = users.get(&owner_name).unwrap().join(PUBLIC_KEYS_FILE); + let operator_identity = users + .get(&format!("operator-{}", i)) + .unwrap() + .join(PUBLIC_KEYS_FILE); + let voter_identity = users + .get(&format!("voter-{}", i)) + .unwrap() + .join(PUBLIC_KEYS_FILE); + let commission_rate = if commission_rates.is_empty() { + 0 + } else { + commission_rates.remove(0) + }; + set_validator_config( + owner_name, + git_options.clone(), + owner_identity.as_path(), + operator_identity.as_path(), + voter_identity.as_path(), + commission_rate, + i as u16, + ) + .await; + } + git_options +} + +/// Generate genesis and waypoint +async fn generate_genesis(git_options: GitOptions, output_dir: PathBuf, mainnet: bool) { + let command = GenerateGenesis { + prompt_options: PromptOptions::yes(), + git_options, + output_dir: Some(output_dir), + mainnet, + }; + let _ = command.execute().await.unwrap(); +} + +/// Setup a temporary repo location and add all required pieces +async fn setup_git_dir( + root_private_key: Option<&Ed25519PrivateKey>, + users: Vec, + chain_id: ChainId, +) -> GitOptions { + let git_options = git_options(); + let layout_file = TempPath::new(); + layout_file.create_as_file().unwrap(); + let layout_file = layout_file.path(); + + create_layout_file( + layout_file, + root_private_key.map(|inner| inner.public_key()), + users, + chain_id, + ) + .await; + let setup_command = SetupGit { + git_options: git_options.clone(), + layout_file: PathBuf::from(layout_file), + }; + + setup_command + .execute() + .await + .expect("Should not fail creating repo folder"); + + // Add framework + add_framework_to_dir(git_options.local_repository_dir.as_ref().unwrap().as_path()); + git_options +} + +/// Add framework to git directory +fn add_framework_to_dir(git_dir: &Path) { + aptos_cached_packages::head_release_bundle() + .write(git_dir.join(FRAMEWORK_NAME)) + .unwrap() +} + +/// Local git options for testing +fn git_options() -> GitOptions { + let temp_path = TempPath::new(); + let path = PathBuf::from(temp_path.path()); + GitOptions { + local_repository_dir: Some(path), + ..Default::default() + } +} + +/// Create a layout file for the repo +async fn create_layout_file( + file: &Path, + root_public_key: Option, + users: Vec, + chain_id: ChainId, +) { + GenerateLayoutTemplate { + output_file: PathBuf::from(file), + prompt_options: PromptOptions::yes(), + } + .execute() + .await + .expect("Expected to create layout template"); + + // Update layout file + let mut layout: Layout = + from_yaml(&String::from_utf8(read_from_file(file).unwrap()).unwrap()).unwrap(); + layout.root_key = root_public_key; + layout.users = users; + layout.chain_id = chain_id; + layout.is_test = true; + layout.total_supply = Some(INITIAL_BALANCE * 16); + + write_to_file( + file, + "Layout file", + serde_yaml::to_string(&layout).unwrap().as_bytes(), + ) + .unwrap(); +} + +/// Generate keys for a "user" +async fn generate_keys(dir: &Path, name: &str) -> PathBuf { + let output_dir = dir.join(name); + let command = GenerateKeys { + pool_address_args: OptionalPoolAddressArgs { pool_address: None }, + rng_args: RngArgs::from_string_seed(name), + prompt_options: PromptOptions::yes(), + output_dir: Some(output_dir.clone()), + }; + let _ = command.execute().await.unwrap(); + output_dir +} + +/// Set validator configuration for a user +async fn set_validator_config( + username: String, + git_options: GitOptions, + owner_identity_file: &Path, + operator_identity_file: &Path, + voter_identity_file: &Path, + commission_percentage: u64, + port: u16, +) { + let command = SetValidatorConfiguration { + username, + git_options, + owner_public_identity_file: Some(owner_identity_file.to_path_buf()), + validator_host: HostAndPort::from_str(&format!("localhost:{}", port)).unwrap(), + stake_amount: 100_000_000_000_000, + full_node_host: None, + operator_public_identity_file: Some(operator_identity_file.to_path_buf()), + voter_public_identity_file: Some(voter_identity_file.to_path_buf()), + commission_percentage, + join_during_genesis: true, + }; + + command.execute().await.unwrap() +} + +async fn create_account_balances_file(path: PathBuf, addresses: Vec) { + let account_balances: Vec = addresses + .iter() + .map(|account_address| AccountBalance { + account_address: *account_address, + balance: INITIAL_BALANCE, + }) + .collect(); + + let balance_map = AccountBalanceMap::try_from(account_balances).unwrap(); + + write_to_file( + &path.join(BALANCES_FILE), + BALANCES_FILE, + serde_yaml::to_string(&balance_map).unwrap().as_bytes(), + ) + .unwrap(); +} + +async fn create_employee_vesting_accounts_file( + path: PathBuf, + admin_identities: &[PublicIdentity], + operator_identities: &[PublicIdentity], + employee_groups: &[Vec], + join_during_genesis: &[bool], +) { + TestValidator::new_test_set(Some(employee_groups.len()), Some(INITIAL_BALANCE)); + let employee_vesting_accounts: Vec<_> = employee_groups + .iter() + .enumerate() + .map(|(index, accounts)| { + let admin_identity = admin_identities[index].clone(); + let operator_identity = operator_identities[index].clone(); + let validator_config = if *join_during_genesis.get(index).unwrap() { + ValidatorConfiguration { + owner_account_address: admin_identity.account_address.into(), + owner_account_public_key: admin_identity.account_public_key.clone(), + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key.clone(), + voter_account_address: admin_identity.account_address.into(), + voter_account_public_key: admin_identity.account_public_key, + consensus_public_key: operator_identity.consensus_public_key, + proof_of_possession: operator_identity.consensus_proof_of_possession, + validator_network_public_key: operator_identity.validator_network_public_key, + validator_host: Some(HostAndPort::from_str("localhost:8080").unwrap()), + full_node_network_public_key: operator_identity.full_node_network_public_key, + full_node_host: Some(HostAndPort::from_str("localhost:8081").unwrap()), + stake_amount: 2 * INITIAL_BALANCE, + commission_percentage: 0, + join_during_genesis: true, + } + } else { + ValidatorConfiguration { + owner_account_address: admin_identity.account_address.into(), + owner_account_public_key: admin_identity.account_public_key.clone(), + operator_account_address: operator_identity.account_address.into(), + operator_account_public_key: operator_identity.account_public_key, + voter_account_address: admin_identity.account_address.into(), + voter_account_public_key: admin_identity.account_public_key, + consensus_public_key: None, + proof_of_possession: None, + validator_network_public_key: None, + validator_host: None, + full_node_network_public_key: None, + full_node_host: None, + stake_amount: 2 * INITIAL_BALANCE, + commission_percentage: 0, + join_during_genesis: false, + } + }; + + EmployeePoolConfig { + accounts: accounts.iter().map(|addr| addr.into()).collect(), + validator: validator_config, + vesting_schedule_numerators: vec![3, 3, 3, 3, 1], + vesting_schedule_denominator: 48, + beneficiary_resetter: AccountAddress::from_hex_literal("0x101").unwrap().into(), + } + }) + .collect(); + let employee_vesting_map = EmployeePoolMap { + inner: employee_vesting_accounts, + }; + write_to_file( + &path.join(EMPLOYEE_VESTING_ACCOUNTS_FILE), + EMPLOYEE_VESTING_ACCOUNTS_FILE, + serde_yaml::to_string(&employee_vesting_map) + .unwrap() + .as_bytes(), + ) + .unwrap(); +} diff --git a/crates/aptos/src/genesis/tools.rs b/crates/aptos/src/genesis/tools.rs new file mode 100644 index 0000000000000..d9d00125cca11 --- /dev/null +++ b/crates/aptos/src/genesis/tools.rs @@ -0,0 +1,100 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::PromptOptions, + utils::{dir_default_to_current, write_to_file}, + }, + genesis::{ + get_validator_configs, + git::{GitOptions, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE}, + parse_error, + }, + CliCommand, CliTypedResult, +}; +use aptos_genesis::config::{EmployeePoolMap, Layout}; +use aptos_sdk::move_types::account_address::AccountAddress; +use aptos_types::account_address::{create_vesting_pool_address, default_stake_pool_address}; +use async_trait::async_trait; +use clap::Parser; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; + +const POOL_ADDRESSES: &str = "pool-addresses.yaml"; +const EMPLOYEE_POOL_ADDRESSES: &str = "employee-pool-addresses.yaml"; + +/// Get pool addresses from a mainnet genesis setup +/// +/// Outputs all pool addresses to a file from the genesis files +#[derive(Parser)] +pub struct PoolAddresses { + /// Output directory for pool addresses + #[clap(long, value_parser)] + output_dir: Option, + + #[clap(flatten)] + prompt_options: PromptOptions, + #[clap(flatten)] + git_options: GitOptions, +} + +#[async_trait] +impl CliCommand> for PoolAddresses { + fn command_name(&self) -> &'static str { + "GetPoolAddresses" + } + + async fn execute(self) -> CliTypedResult> { + let output_dir = dir_default_to_current(self.output_dir.clone())?; + let client = self.git_options.get_client()?; + let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; + let employee_vesting_accounts: EmployeePoolMap = + client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; + let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; + + let mut address_to_pool = BTreeMap::::new(); + + for validator in validators { + let stake_pool_address = default_stake_pool_address( + validator.owner_account_address.into(), + validator.operator_account_address.into(), + ); + address_to_pool.insert(validator.owner_account_address.into(), stake_pool_address); + } + + let mut employee_address_to_pool = BTreeMap::::new(); + + for employee_pool in employee_vesting_accounts.inner.iter() { + let stake_pool_address = create_vesting_pool_address( + employee_pool.validator.owner_account_address.into(), + employee_pool.validator.operator_account_address.into(), + 0, + &[], + ); + + employee_address_to_pool.insert( + employee_pool.validator.owner_account_address.into(), + stake_pool_address, + ); + } + + let pool_addresses_file = output_dir.join(POOL_ADDRESSES); + let employee_pool_addresses_file = output_dir.join(EMPLOYEE_POOL_ADDRESSES); + + write_to_file( + pool_addresses_file.as_path(), + POOL_ADDRESSES, + serde_yaml::to_string(&address_to_pool)?.as_bytes(), + )?; + write_to_file( + employee_pool_addresses_file.as_path(), + EMPLOYEE_POOL_ADDRESSES, + serde_yaml::to_string(&employee_address_to_pool)?.as_bytes(), + )?; + + Ok(vec![pool_addresses_file, employee_pool_addresses_file]) + } +} diff --git a/crates/aptos/src/governance/delegation_pool.rs b/crates/aptos/src/governance/delegation_pool.rs index 5d836241af694..a2263a941043e 100644 --- a/crates/aptos/src/governance/delegation_pool.rs +++ b/crates/aptos/src/governance/delegation_pool.rs @@ -209,15 +209,13 @@ async fn is_partial_governance_voting_enabled_for_delegation_pool( pool_address: AccountAddress, ) -> CliTypedResult { let response = client - .view_bcs_with_json_response( - &ViewFunction { - module: ModuleId::new( - AccountAddress::ONE, - ident_str!("delegation_pool").to_owned(), - ), - function: ident_str!("partial_governance_voting_enabled").to_owned(), - ty_args: vec![], - args: vec![bcs::to_bytes(&pool_address).unwrap()], + .view( + &ViewRequest { + function: "0x1::delegation_pool::partial_governance_voting_enabled" + .parse() + .unwrap(), + type_arguments: vec![], + arguments: vec![serde_json::Value::String(pool_address.to_string())], }, None, ) @@ -238,18 +236,16 @@ async fn get_remaining_voting_power( proposal_id: u64, ) -> CliTypedResult { let response = client - .view_bcs_with_json_response( - &ViewFunction { - module: ModuleId::new( - AccountAddress::ONE, - ident_str!("delegation_pool").to_owned(), - ), - function: ident_str!("calculate_and_update_remaining_voting_power").to_owned(), - ty_args: vec![], - args: vec![ - bcs::to_bytes(&pool_address).unwrap(), - bcs::to_bytes(&voter_address).unwrap(), - bcs::to_bytes(&proposal_id).unwrap(), + .view( + &ViewRequest { + function: "0x1::delegation_pool::calculate_and_update_remaining_voting_power" + .parse() + .unwrap(), + type_arguments: vec![], + arguments: vec![ + serde_json::Value::String(pool_address.to_string()), + serde_json::Value::String(voter_address.to_string()), + serde_json::Value::String(proposal_id.to_string()), ], }, None, diff --git a/crates/aptos/src/governance/mod.rs b/crates/aptos/src/governance/mod.rs index 883e06fa9f2c1..a781f12dbee32 100644 --- a/crates/aptos/src/governance/mod.rs +++ b/crates/aptos/src/governance/mod.rs @@ -18,7 +18,7 @@ use crate::{ move_tool::{FrameworkPackageArgs, IncludedArtifacts}, CliCommand, CliResult, }; -use aptos_api_types::ViewFunction; +use aptos_api_types::ViewRequest; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; @@ -38,10 +38,7 @@ use aptos_types::{ }; use async_trait::async_trait; use clap::Parser; -use move_core_types::{ - ident_str, language_storage::ModuleId, parser::parse_type_tag, - transaction_argument::TransactionArgument, -}; +use move_core_types::transaction_argument::TransactionArgument; use reqwest::Url; use serde::{Deserialize, Serialize}; use std::{ @@ -608,15 +605,14 @@ impl SubmitVote { let is_proposal_closed = self .args .txn_options - .view(ViewFunction { - module: ModuleId::new(AccountAddress::ONE, ident_str!("voting").to_owned()), - function: ident_str!("is_voting_closed").to_owned(), - ty_args: vec![ - parse_type_tag("0x1::governance_proposal::GovernanceProposal").unwrap(), - ], - args: vec![ - bcs::to_bytes(&AccountAddress::ONE).unwrap(), - bcs::to_bytes(&proposal_id).unwrap(), + .view(ViewRequest { + function: "0x1::voting::is_voting_closed".parse().unwrap(), + type_arguments: vec!["0x1::governance_proposal::GovernanceProposal" + .parse() + .unwrap()], + arguments: vec![ + serde_json::Value::String("0x1".to_string()), + serde_json::Value::String(proposal_id.to_string()), ], }) .await?[0] @@ -634,16 +630,14 @@ impl SubmitVote { let remaining_voting_power = self .args .txn_options - .view(ViewFunction { - module: ModuleId::new( - AccountAddress::ONE, - ident_str!("aptos_governance").to_owned(), - ), - function: ident_str!("get_remaining_voting_power").to_owned(), - ty_args: vec![], - args: vec![ - bcs::to_bytes(&pool_address).unwrap(), - bcs::to_bytes(&proposal_id).unwrap(), + .view(ViewRequest { + function: "0x1::aptos_governance::get_remaining_voting_power" + .parse() + .unwrap(), + type_arguments: vec![], + arguments: vec![ + serde_json::Value::String(pool_address.to_string()), + serde_json::Value::String(proposal_id.to_string()), ], }) .await?[0] @@ -1001,10 +995,8 @@ impl CliCommand<()> for GenerateUpgradeProposal { move_options.dev, move_options.skip_fetch_latest_git_deps, move_options.named_addresses(), - move_options.override_std, move_options.bytecode_version, move_options.compiler_version, - move_options.language_version, move_options.skip_attribute_checks, move_options.check_test_code, ); diff --git a/crates/aptos/src/governance/utils.rs b/crates/aptos/src/governance/utils.rs new file mode 100644 index 0000000000000..c936778595da0 --- /dev/null +++ b/crates/aptos/src/governance/utils.rs @@ -0,0 +1,45 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{governance::*, *}; +use aptos_types::on_chain_config::FeatureFlag; + +pub fn vote_to_string(vote: bool) -> &'static str { + if vote { + "Yes" + } else { + "No" + } +} + +pub fn check_remaining_voting_power( + remaining_voting_power: u64, + specified_voting_power: Option, +) -> u64 { + let mut voting_power = remaining_voting_power; + if let Some(specified_voting_power) = specified_voting_power { + if specified_voting_power > voting_power { + println!( + "Stake pool only has {} voting power on proposal.", + voting_power + ); + } else { + voting_power = specified_voting_power; + }; + }; + voting_power +} + +pub async fn is_partial_governance_voting_enabled(client: &Client) -> CliTypedResult { + common::utils::get_feature_flag(client, FeatureFlag::PARTIAL_GOVERNANCE_VOTING).await +} + +pub async fn is_delegation_pool_partial_governance_voting_enabled( + client: &Client, +) -> CliTypedResult { + common::utils::get_feature_flag( + client, + FeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING, + ) + .await +} diff --git a/crates/aptos/src/lib.rs b/crates/aptos/src/lib.rs new file mode 100644 index 0000000000000..9f2863a8a0e58 --- /dev/null +++ b/crates/aptos/src/lib.rs @@ -0,0 +1,97 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![deny(unsafe_code)] + +pub mod account; +pub mod common; +pub mod config; +pub mod ffi; +pub mod genesis; +pub mod governance; +pub mod move_tool; +pub mod node; +pub mod op; +pub mod stake; +#[cfg(any(test, feature = "fuzzing"))] +pub mod test; +pub mod update; + +use crate::common::{ + types::{CliCommand, CliResult, CliTypedResult}, + utils::cli_build_information, +}; +use async_trait::async_trait; +use clap::Parser; +use std::collections::BTreeMap; + +/// Command Line Interface (CLI) for developing and interacting with Movement +#[derive(Parser)] +#[clap(name = "aptos", author, version, propagate_version = true, styles = aptos_cli_common::aptos_cli_style())] +pub enum Tool { + #[clap(subcommand)] + Account(account::AccountTool), + #[clap(subcommand)] + Config(config::ConfigTool), + #[clap(subcommand)] + Genesis(genesis::GenesisTool), + #[clap(subcommand)] + Governance(governance::GovernanceTool), + Info(InfoTool), + Init(common::init::InitTool), + #[clap(subcommand)] + Key(op::key::KeyTool), + #[clap(subcommand)] + Move(move_tool::MoveTool), + #[clap(subcommand)] + Multisig(account::MultisigAccountTool), + #[clap(subcommand)] + Node(node::NodeTool), + #[clap(subcommand)] + Stake(stake::StakeTool), + Update(update::UpdateTool), +} + +impl Tool { + pub async fn execute(self) -> CliResult { + use Tool::*; + match self { + Account(tool) => tool.execute().await, + Config(tool) => tool.execute().await, + Genesis(tool) => tool.execute().await, + Governance(tool) => tool.execute().await, + Info(tool) => tool.execute_serialized().await, + // TODO: Replace entirely with config init + Init(tool) => tool.execute_serialized_success().await, + Key(tool) => tool.execute().await, + Move(tool) => tool.execute().await, + Multisig(tool) => tool.execute().await, + Node(tool) => tool.execute().await, + Stake(tool) => tool.execute().await, + Update(tool) => tool.execute_serialized().await, + } + } +} + +/// Show build information about the CLI +/// +/// This is useful for debugging as well as determining what versions are compatible with the CLI +#[derive(Parser)] +pub struct InfoTool {} + +#[async_trait] +impl CliCommand> for InfoTool { + fn command_name(&self) -> &'static str { + "GetCLIInfo" + } + + async fn execute(self) -> CliTypedResult> { + Ok(cli_build_information()) + } +} + +#[test] +fn verify_tool() { + use clap::CommandFactory; + Tool::command().debug_assert() +} diff --git a/crates/aptos/src/main.rs b/crates/aptos/src/main.rs index 86d950d2098bd..657c2d22488b4 100644 --- a/crates/aptos/src/main.rs +++ b/crates/aptos/src/main.rs @@ -9,7 +9,7 @@ #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -use aptos::{move_tool, Tool}; +use movement::{move_tool, Tool}; use clap::Parser; use std::{process::exit, time::Duration}; @@ -28,7 +28,7 @@ fn main() { // Shutdown the runtime with a timeout. We do this to make sure that we don't sit // here waiting forever waiting for tasks that sometimes don't want to exit on - // their own (e.g. telemetry, containers spawned by the localnet, etc). + // their own (e.g. telemetry, containers spawned by the local testnet, etc). runtime.shutdown_timeout(Duration::from_millis(50)); match result { diff --git a/crates/aptos/src/move_tool/aptos_debug_natives.rs b/crates/aptos/src/move_tool/aptos_debug_natives.rs new file mode 100644 index 0000000000000..926f1c48826ca --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_debug_natives.rs @@ -0,0 +1,27 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_framework::extended_checks; +use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters, LATEST_GAS_FEATURE_VERSION}; +use aptos_types::on_chain_config::{Features, TimedFeaturesBuilder}; +use aptos_vm::natives; +use move_vm_runtime::native_functions::NativeFunctionTable; + +// move_stdlib has the testing feature enabled to include debug native functions +pub fn aptos_debug_natives( + native_gas_parameters: NativeGasParameters, + misc_gas_params: MiscGasParameters, +) -> NativeFunctionTable { + // As a side effect, also configure for unit testing + natives::configure_for_unit_test(); + extended_checks::configure_extended_checks_for_unit_test(); + // Return all natives -- build with the 'testing' feature, therefore containing + // debug related functions. + natives::aptos_natives( + LATEST_GAS_FEATURE_VERSION, + native_gas_parameters, + misc_gas_params, + TimedFeaturesBuilder::enable_all().build(), + Features::default(), + ) +} diff --git a/crates/aptos/src/move_tool/aptos_dep_example/README.md b/crates/aptos/src/move_tool/aptos_dep_example/README.md new file mode 100644 index 0000000000000..89d702218476a --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_dep_example/README.md @@ -0,0 +1,24 @@ +This is a small example of using the new `aptos` dependency. This shall be removed once we have +documentation/tests. + +`pack2` contains a package which is used by `pack1` as follows: + +``` +[dependencies] +Pack2 = { aptos = "http://localhost:8080", address = "default" } +``` + +To see it working: + +```shell +# Start a node with an account +aptos node run-local-testnet & +aptos account create --account default --use-faucet +# Compile and publish pack2 +cd pack2 +aptos move compile --named-addresses project=default +aptos move publish --named-addresses project=default +# Compile pack1 agains the published pack2 +cd ../pack1 +aptos move compile --named-addresses project=default +``` diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml b/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml new file mode 100644 index 0000000000000..dab5d3fe70e9e --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml @@ -0,0 +1,6 @@ +[package] +name = "Pack1" +version = "0.0.0" + +[dependencies] +Pack2 = { aptos = "http://localhost:8080", address = "default" } diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move b/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move new file mode 100644 index 0000000000000..fe091952d3dda --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move @@ -0,0 +1,7 @@ +module project::test { + use project::m; + + public entry fun test(_sender: &signer) { + assert!(m::add(1, 2) == 1 + 2, 1); + } +} diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml b/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml new file mode 100644 index 0000000000000..c5e6c7d008c72 --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml @@ -0,0 +1,3 @@ +[package] +name = "Pack2" +version = "0.0.0" diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move b/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move new file mode 100644 index 0000000000000..ade22fadb5cfe --- /dev/null +++ b/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move @@ -0,0 +1,3 @@ +module project::m { + public fun add(x: u64, y: u64): u64 { x + y } +} diff --git a/crates/aptos/src/move_tool/coverage.rs b/crates/aptos/src/move_tool/coverage.rs new file mode 100644 index 0000000000000..102a22e3c9054 --- /dev/null +++ b/crates/aptos/src/move_tool/coverage.rs @@ -0,0 +1,191 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; +use aptos_framework::extended_checks; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; +use move_compiler::compiled_unit::{CompiledUnit, NamedCompiledModule}; +use move_coverage::{ + coverage_map::CoverageMap, format_csv_summary, format_human_summary, + source_coverage::SourceCoverageBuilder, summary::summarize_inst_cov, +}; +use move_disassembler::disassembler::Disassembler; +use move_package::{compilation::compiled_package::CompiledPackage, BuildConfig, CompilerConfig}; + +/// Display a coverage summary for all modules in a package +/// +#[derive(Debug, Parser)] +pub struct SummaryCoverage { + /// Display function coverage summaries + /// + /// When provided, it will include coverage on a function level + #[clap(long)] + pub summarize_functions: bool, + /// Output CSV data of coverage + #[clap(long = "csv")] + pub output_csv: bool, + /// A filter string to determine which unit tests to compute coverage on + #[clap(long, short)] + pub filter: Option, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +impl SummaryCoverage { + pub fn coverage(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let modules: Vec<_> = package + .root_modules() + .filter_map(|unit| { + let mut retain = true; + if let Some(filter_str) = &self.filter { + if !&unit.unit.name().as_str().contains(filter_str.as_str()) { + retain = false; + } + } + match &unit.unit { + CompiledUnit::Module(NamedCompiledModule { module, .. }) if retain => { + Some(module.clone()) + }, + _ => None, + } + }) + .collect(); + let coverage_map = coverage_map.to_unified_exec_map(); + if self.output_csv { + format_csv_summary( + modules.as_slice(), + &coverage_map, + summarize_inst_cov, + &mut std::io::stdout(), + ) + } else { + format_human_summary( + modules.as_slice(), + &coverage_map, + summarize_inst_cov, + &mut std::io::stdout(), + self.summarize_functions, + ) + } + Ok(()) + } +} + +#[async_trait] +impl CliCommand<()> for SummaryCoverage { + fn command_name(&self) -> &'static str { + "SummaryCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + self.coverage() + } +} + +/// Display coverage information about the module against source code +#[derive(Debug, Parser)] +pub struct SourceCoverage { + #[clap(long = "module")] + pub module_name: String, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand<()> for SourceCoverage { + fn command_name(&self) -> &'static str { + "SourceCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let unit = package.get_module_by_name_from_root(&self.module_name)?; + let source_path = &unit.source_path; + let (module, source_map) = match &unit.unit { + CompiledUnit::Module(NamedCompiledModule { + module, source_map, .. + }) => (module, source_map), + _ => panic!("Should all be modules"), + }; + let source_coverage = SourceCoverageBuilder::new(module, &coverage_map, source_map); + source_coverage + .compute_source_coverage(source_path) + .output_source_coverage(&mut std::io::stdout()) + .map_err(|err| CliError::UnexpectedError(format!("Failed to get coverage {}", err))) + } +} + +/// Display coverage information about the module against disassembled bytecode +#[derive(Debug, Parser)] +pub struct BytecodeCoverage { + #[clap(long = "module")] + pub module_name: String, + #[clap(flatten)] + pub move_options: MovePackageDir, +} + +#[async_trait] +impl CliCommand<()> for BytecodeCoverage { + fn command_name(&self) -> &'static str { + "BytecodeCoverage" + } + + async fn execute(self) -> CliTypedResult<()> { + let (coverage_map, package) = compile_coverage(self.move_options)?; + let unit = package.get_module_by_name_from_root(&self.module_name)?; + let mut disassembler = Disassembler::from_unit(&unit.unit); + disassembler.add_coverage_map(coverage_map.to_unified_exec_map()); + println!("{}", disassembler.disassemble()?); + Ok(()) + } +} + +fn compile_coverage( + move_options: MovePackageDir, +) -> CliTypedResult<(CoverageMap, CompiledPackage)> { + let config = BuildConfig { + dev_mode: move_options.dev, + additional_named_addresses: move_options.named_addresses(), + test_mode: false, + install_dir: move_options.output_dir.clone(), + compiler_config: CompilerConfig { + known_attributes: extended_checks::get_all_attribute_names().clone(), + skip_attribute_checks: false, + ..Default::default() + }, + ..Default::default() + }; + let path = move_options.get_package_path()?; + let coverage_map = + CoverageMap::from_binary_file(path.join(".coverage_map.mvcov")).map_err(|err| { + CliError::UnexpectedError(format!("Failed to retrieve coverage map {}", err)) + })?; + let package = config + .compile_package(path.as_path(), &mut Vec::new()) + .map_err(|err| CliError::MoveCompilationError(err.to_string()))?; + + Ok((coverage_map, package)) +} + +/// Computes coverage for a package +/// +/// Computes coverage on a previous unit test run for a package. Coverage input must +/// first be built with `aptos move test --coverage` +#[derive(Subcommand)] +pub enum CoveragePackage { + Summary(SummaryCoverage), + Source(SourceCoverage), + Bytecode(BytecodeCoverage), +} + +impl CoveragePackage { + pub async fn execute(self) -> CliResult { + match self { + Self::Summary(tool) => tool.execute_serialized_success().await, + Self::Source(tool) => tool.execute_serialized_success().await, + Self::Bytecode(tool) => tool.execute_serialized_success().await, + } + } +} diff --git a/crates/aptos/src/move_tool/disassembler.rs b/crates/aptos/src/move_tool/disassembler.rs new file mode 100644 index 0000000000000..709bdd460c590 --- /dev/null +++ b/crates/aptos/src/move_tool/disassembler.rs @@ -0,0 +1,148 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{ + types::{CliCommand, CliError, CliTypedResult, PromptOptions}, + utils::{ + check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, read_from_file, + write_to_user_only_file, + }, +}; +use anyhow::Context; +use async_trait::async_trait; +use clap::Parser; +use move_binary_format::{ + binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, +}; +use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; +use move_command_line_common::files::{ + MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, +}; +use move_coverage::coverage_map::CoverageMap; +use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; +use move_ir_types::location::Spanned; +use std::{fs, path::PathBuf}; + +const DISASSEMBLED_CODE_FILE: &str = "disassembled-code.move"; + +/// Disassemble the Move bytecode pointed to +/// +/// For example, if you want to disassemble on chain module: +/// 1. Download the package - aptos move download +/// 2. Compile the package - aptos move compile +/// 3. Cd to package and disassemble - aptos move disassemble --bytecode-path ./test.mv +#[derive(Debug, Parser)] +pub struct Disassemble { + /// Treat input file as a script (default is to treat file as a module) + #[clap(long)] + pub is_script: bool, + + /// The path to the bytecode file to disassemble; + /// + /// let's call it file.mv. We assume that two other files reside under the same directory: + /// a source map file.mvsm (possibly) and the Move source code file.move. + #[clap(long)] + pub bytecode_path: PathBuf, + + /// (Optional) Path to a coverage file for the VM in order to print trace information in the + /// disassembled output. + #[clap(long)] + pub code_coverage_path: Option, + + /// Output directory for the key files + #[clap(long, value_parser)] + pub(crate) output_dir: Option, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +#[async_trait] +impl CliCommand for Disassemble { + fn command_name(&self) -> &'static str { + "Disassemble" + } + + async fn execute(self) -> CliTypedResult { + let bytecode_path = self.bytecode_path.as_path(); + let extension = bytecode_path + .extension() + .context("Missing file extension for bytecode file")?; + if extension != MOVE_COMPILED_EXTENSION { + return Err(CliError::UnexpectedError(format!( + "Bad source file extension {:?}; expected {}", + extension, MOVE_COMPILED_EXTENSION + ))); + } + + let bytecode_bytes = read_from_file(bytecode_path)?; + let move_path = bytecode_path.with_extension(MOVE_EXTENSION); + let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); + + let source = fs::read_to_string(move_path).ok(); + let source_map = source_map_from_file(&source_map_path); + + let disassembler_options = DisassemblerOptions { + print_code: true, + only_externally_visible: false, + print_basic_blocks: true, + print_locals: true, + }; + + let no_loc = Spanned::unsafe_no_loc(()).loc; + let module: CompiledModule; + let script: CompiledScript; + let bytecode = if self.is_script { + script = CompiledScript::deserialize(&bytecode_bytes) + .context("Script blob can't be deserialized")?; + BinaryIndexedView::Script(&script) + } else { + module = CompiledModule::deserialize(&bytecode_bytes) + .context("Module blob can't be deserialized")?; + BinaryIndexedView::Module(&module) + }; + + let mut source_mapping = if let Ok(s) = source_map { + SourceMapping::new(s, bytecode) + } else { + SourceMapping::new_from_view(bytecode, no_loc) + .context("Unable to build dummy source mapping")? + }; + + if let Some(source_code) = source { + source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); + } + + let mut disassembler = Disassembler::new(source_mapping, disassembler_options); + + if let Some(file_path) = &self.code_coverage_path { + disassembler.add_coverage_map( + CoverageMap::from_binary_file(file_path) + .map_err(|_err| { + CliError::UnexpectedError("Unable to read from file_path".to_string()) + })? + .to_unified_exec_map(), + ); + } + + let disassemble_string = disassembler + .disassemble() + .map_err(|_err| CliError::UnexpectedError("Unable to disassemble".to_string()))?; + + let output_dir = dir_default_to_current(self.output_dir.clone())?; + let disassemble_file = output_dir.join(DISASSEMBLED_CODE_FILE); + check_if_file_exists(disassemble_file.as_path(), self.prompt_options)?; + + // Create the directory if it doesn't exist + create_dir_if_not_exist(output_dir.as_path())?; + + // write to file + write_to_user_only_file( + disassemble_file.as_path(), + DISASSEMBLED_CODE_FILE, + disassemble_string.as_bytes(), + )?; + + Ok(disassemble_file.as_path().display().to_string()) + } +} diff --git a/crates/aptos/src/move_tool/manifest.rs b/crates/aptos/src/move_tool/manifest.rs new file mode 100644 index 0000000000000..5850562e26400 --- /dev/null +++ b/crates/aptos/src/move_tool/manifest.rs @@ -0,0 +1,92 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::types::load_manifest_account_arg; +use aptos_types::account_address::AccountAddress; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::collections::BTreeMap; + +/// A Rust representation of the Move package manifest +/// +/// Note: The original Move package manifest object used by the package system +/// can't be serialized because it uses a symbol mapping +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MovePackageManifest { + pub package: PackageInfo, + pub addresses: BTreeMap, + #[serde(rename = "dev-addresses")] + pub dev_addresses: BTreeMap, + pub dependencies: BTreeMap, + #[serde(rename = "dev-dependencies")] + pub dev_dependencies: BTreeMap, +} +/// Representation of an option address so we can print it as "_" +#[derive(Debug, Clone)] +pub struct ManifestNamedAddress { + pub address: Option, +} + +impl From> for ManifestNamedAddress { + fn from(opt: Option) -> Self { + ManifestNamedAddress { address: opt } + } +} + +impl From for Option { + fn from(addr: ManifestNamedAddress) -> Self { + addr.address + } +} + +impl Serialize for ManifestNamedAddress { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(address) = self.address { + serializer.serialize_str(&address.to_hex_literal()) + } else { + serializer.serialize_str("_") + } + } +} + +impl<'de> Deserialize<'de> for ManifestNamedAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let str = ::deserialize(deserializer)?; + Ok(ManifestNamedAddress { + // TODO: Cleanup unwrap + address: load_manifest_account_arg(&str).unwrap(), + }) + } +} + +/// A Rust representation of a move dependency +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Dependency { + #[serde(skip_serializing_if = "Option::is_none")] + pub local: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub git: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rev: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub subdir: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub aptos: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option, +} + +/// A Rust representation of the package info +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PackageInfo { + pub name: String, + pub version: String, + pub authors: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option, +} diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs index d5dee099d6d2e..e8ff54c7b7fb9 100644 --- a/crates/aptos/src/move_tool/mod.rs +++ b/crates/aptos/src/move_tool/mod.rs @@ -1,16 +1,22 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +mod aptos_debug_natives; +pub mod coverage; +mod disassembler; +mod manifest; +pub mod package_hooks; +mod show; +pub mod stored_package; + use crate::{ account::derive_resource_account::ResourceAccountSeed, common::{ - local_simulation, types::{ load_account_arg, ArgWithTypeJSON, CliConfig, CliError, CliTypedResult, ConfigSearchMode, EntryFunctionArguments, EntryFunctionArgumentsJSON, - MoveManifestAccountWrapper, MovePackageDir, OverrideSizeCheckOption, ProfileOptions, - PromptOptions, RestOptions, SaveFile, ScriptFunctionArguments, TransactionOptions, - TransactionSummary, + MoveManifestAccountWrapper, MovePackageDir, ProfileOptions, PromptOptions, RestOptions, + SaveFile, ScriptFunctionArguments, TransactionOptions, TransactionSummary, }, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, @@ -19,8 +25,8 @@ use crate::{ }, governance::CompileScriptFunction, move_tool::{ - bytecode::{Decompile, Disassemble}, coverage::SummaryCoverage, + disassembler::Disassemble, manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo}, }, CliCommand, CliResult, @@ -31,28 +37,21 @@ use aptos_framework::{ BuildOptions, BuiltPackage, }; use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters}; -use aptos_move_debugger::aptos_debugger::AptosDebugger; -use aptos_rest_client::{ - aptos_api_types::{EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId}, - Client, +use aptos_rest_client::aptos_api_types::{ + EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, }; use aptos_types::{ account_address::{create_resource_address, AccountAddress}, - object_address::create_object_code_deployment_address, - on_chain_config::aptos_test_feature_flags_genesis, - transaction::{Transaction, TransactionArgument, TransactionPayload, TransactionStatus}, + transaction::{TransactionArgument, TransactionPayload}, }; -use aptos_vm::data_cache::AsMoveResolver; use async_trait::async_trait; use clap::{Parser, Subcommand, ValueEnum}; use itertools::Itertools; use move_cli::{self, base::test::UnitTestResult}; use move_command_line_common::env::MOVE_HOME; use move_core_types::{identifier::Identifier, language_storage::ModuleId, u256::U256}; -use move_model::metadata::{CompilerVersion, LanguageVersion}; use move_package::{ - source_package::{layout::SourcePackageLayout, std_lib::StdVersion}, - BuildConfig, CompilerConfig, + source_package::layout::SourcePackageLayout, BuildConfig, CompilerConfig, CompilerVersion, }; use move_unit_test::UnitTestingConfig; pub use package_hooks::*; @@ -66,15 +65,6 @@ use std::{ }; pub use stored_package::*; use tokio::task; -use url::Url; - -mod aptos_debug_natives; -mod bytecode; -pub mod coverage; -mod manifest; -pub mod package_hooks; -mod show; -pub mod stored_package; /// Tool for Move related operations /// @@ -89,11 +79,8 @@ pub enum MoveTool { CompileScript(CompileScript), #[clap(subcommand)] Coverage(coverage::CoveragePackage), - CreateObjectAndPublishPackage(CreateObjectAndPublishPackage), - UpgradeObjectPackage(UpgradeObjectPackage), CreateResourceAccountAndPublishPackage(CreateResourceAccountAndPublishPackage), Disassemble(Disassemble), - Decompile(Decompile), Document(DocumentPackage), Download(DownloadPackage), Init(InitPackage), @@ -107,7 +94,6 @@ pub enum MoveTool { Test(TestPackage), VerifyPackage(VerifyPackage), View(ViewFunction), - Replay(Replay), } impl MoveTool { @@ -118,15 +104,10 @@ impl MoveTool { MoveTool::Compile(tool) => tool.execute_serialized().await, MoveTool::CompileScript(tool) => tool.execute_serialized().await, MoveTool::Coverage(tool) => tool.execute().await, - MoveTool::CreateObjectAndPublishPackage(tool) => { - tool.execute_serialized_success().await - }, - MoveTool::UpgradeObjectPackage(tool) => tool.execute_serialized_success().await, MoveTool::CreateResourceAccountAndPublishPackage(tool) => { tool.execute_serialized_success().await }, MoveTool::Disassemble(tool) => tool.execute_serialized().await, - MoveTool::Decompile(tool) => tool.execute_serialized().await, MoveTool::Document(tool) => tool.execute_serialized().await, MoveTool::Download(tool) => tool.execute_serialized().await, MoveTool::Init(tool) => tool.execute_serialized_success().await, @@ -139,7 +120,6 @@ impl MoveTool { MoveTool::Test(tool) => tool.execute_serialized().await, MoveTool::VerifyPackage(tool) => tool.execute_serialized().await, MoveTool::View(tool) => tool.execute_serialized().await, - MoveTool::Replay(tool) => tool.execute_serialized().await, } } } @@ -265,11 +245,7 @@ pub struct InitPackage { /// Example: alice=0x1234,bob=0x5678,greg=_ /// /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap( - long, - value_parser = crate::common::utils::parse_map::, - default_value = "" - )] + #[clap(long, value_parser = crate::common::utils::parse_map::, default_value = "")] pub(crate) named_addresses: BTreeMap, #[clap(flatten)] @@ -334,10 +310,8 @@ impl CliCommand> for CompilePackage { self.move_options.dev, self.move_options.skip_fetch_latest_git_deps, self.move_options.named_addresses(), - self.move_options.override_std.clone(), self.move_options.bytecode_version, self.move_options.compiler_version, - self.move_options.language_version, self.move_options.skip_attribute_checks, self.move_options.check_test_code, ) @@ -398,10 +372,8 @@ impl CompileScript { self.move_options.dev, self.move_options.skip_fetch_latest_git_deps, self.move_options.named_addresses(), - self.move_options.override_std.clone(), self.move_options.bytecode_version, self.move_options.compiler_version, - self.move_options.language_version, self.move_options.skip_attribute_checks, self.move_options.check_test_code, ) @@ -509,7 +481,7 @@ impl CliCommand<&'static str> for TestPackage { NativeGasParameters::zeros(), MiscGasParameters::zeros(), ), - aptos_test_feature_flags_genesis(), + move_core_types::effects::ChangeSet::new(), None, self.compute_coverage, &mut std::io::stdout(), @@ -569,8 +541,6 @@ impl CliCommand<&'static str> for ProvePackage { move_options.get_package_path()?.as_path(), move_options.named_addresses(), move_options.bytecode_version, - move_options.compiler_version, - move_options.language_version, move_options.skip_attribute_checks, extended_checks::get_all_attribute_names(), ) @@ -616,12 +586,10 @@ impl CliCommand<&'static str> for DocumentPackage { with_docs: true, install_dir: None, named_addresses: move_options.named_addresses(), - override_std: move_options.override_std.clone(), docgen_options: Some(docgen_options), skip_fetch_latest_git_deps: move_options.skip_fetch_latest_git_deps, bytecode_version: move_options.bytecode_version, compiler_version: move_options.compiler_version, - language_version: move_options.language_version, skip_attribute_checks: move_options.skip_attribute_checks, check_test_code: move_options.check_test_code, known_attributes: extended_checks::get_all_attribute_names().clone(), @@ -649,8 +617,9 @@ pub struct IncludedArtifactsArgs { /// Publishes the modules in a Move package to the Aptos blockchain #[derive(Parser)] pub struct PublishPackage { - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, + /// Whether to override the check for maximal size of published data + #[clap(long)] + pub(crate) override_size_check: bool, #[clap(flatten)] pub(crate) included_artifacts_args: IncludedArtifactsArgs, @@ -688,10 +657,8 @@ impl TryInto for &PublishPackage { self.move_options.dev, self.move_options.skip_fetch_latest_git_deps, self.move_options.named_addresses(), - self.move_options.override_std.clone(), self.move_options.bytecode_version, self.move_options.compiler_version, - self.move_options.language_version, self.move_options.skip_attribute_checks, self.move_options.check_test_code, ); @@ -706,7 +673,7 @@ impl TryInto for &PublishPackage { ); let size = bcs::serialized_size(&payload)?; println!("package size {} bytes", size); - if !self.override_size_check_option.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { + if !self.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { return Err(CliError::UnexpectedError(format!( "The package is larger than {} bytes ({} bytes)! To lower the size \ you may want to include fewer artifacts via `--included-artifacts`. \ @@ -760,10 +727,8 @@ impl IncludedArtifacts { dev: bool, skip_fetch_latest_git_deps: bool, named_addresses: BTreeMap, - override_std: Option, bytecode_version: Option, compiler_version: Option, - language_version: Option, skip_attribute_checks: bool, check_test_code: bool, ) -> BuildOptions { @@ -777,11 +742,9 @@ impl IncludedArtifacts { // Always enable error map bytecode injection with_error_map: true, named_addresses, - override_std, skip_fetch_latest_git_deps, bytecode_version, compiler_version, - language_version, skip_attribute_checks, check_test_code, known_attributes: extended_checks::get_all_attribute_names().clone(), @@ -794,11 +757,9 @@ impl IncludedArtifacts { with_source_maps: false, with_error_map: true, named_addresses, - override_std, skip_fetch_latest_git_deps, bytecode_version, compiler_version, - language_version, skip_attribute_checks, check_test_code, known_attributes: extended_checks::get_all_attribute_names().clone(), @@ -811,11 +772,9 @@ impl IncludedArtifacts { with_source_maps: true, with_error_map: true, named_addresses, - override_std, skip_fetch_latest_git_deps, bytecode_version, compiler_version, - language_version, skip_attribute_checks, check_test_code, known_attributes: extended_checks::get_all_attribute_names().clone(), @@ -895,189 +854,6 @@ impl CliCommand for BuildPublishPayload { } } -/// Publishes the modules in a Move package to the Aptos blockchain, under an object. -#[derive(Parser)] -pub struct CreateObjectAndPublishPackage { - /// The named address for compiling and using in the contract - /// - /// This will take the derived account address for the object and put it in this location - #[clap(long)] - pub(crate) address_name: String, - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateObjectAndPublishPackage { - fn command_name(&self) -> &'static str { - "CreateObjectAndPublishPackage" - } - - async fn execute(mut self) -> CliTypedResult { - let sender_address = self.txn_options.get_public_key_and_address()?.1; - let sequence_number = self.txn_options.sequence_number(sender_address).await? + 1; - let object_address = create_object_code_deployment_address(sender_address, sequence_number); - - self.move_options - .add_named_address(self.address_name, object_address.to_string()); - - let options = self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.override_std.clone(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.language_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ); - let package = BuiltPackage::build(self.move_options.get_package_path()?, options)?; - let message = format!( - "Do you want to publish this package at object address {}", - object_address - ); - prompt_yes_with_override(&message, self.txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::object_code_deployment_publish( - bcs::to_bytes(&package.extract_metadata()?) - .expect("Failed to serialize PackageMetadata"), - package.extract_code(), - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - - if !self.override_size_check_option.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - let result = self - .txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from); - - if result.is_ok() { - println!( - "Code was successfully deployed to object address {}.", - object_address - ); - } - result - } -} - -#[derive(Parser)] -pub struct UpgradeObjectPackage { - /// Address of the object the package was deployed to - /// - /// This must be an already deployed object containing the package - /// if the package is not already created, it will fail. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) object_address: AccountAddress, - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for UpgradeObjectPackage { - fn command_name(&self) -> &'static str { - "UpgradeObjectPackage" - } - - async fn execute(self) -> CliTypedResult { - let options = self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.override_std.clone(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.language_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ); - let built_package = BuiltPackage::build(self.move_options.get_package_path()?, options)?; - let url = self - .txn_options - .rest_options - .url(&self.txn_options.profile_options)?; - - // Get the `PackageRegistry` at the given object address. - let registry = CachedPackageRegistry::create(url, self.object_address, false).await?; - let package = registry - .get_package(built_package.name()) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - - if package.upgrade_policy() == UpgradePolicy::immutable() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `immutable` cannot be upgraded".to_owned(), - )); - } - - let message = format!( - "Do you want to upgrade the package '{}' at object address {}", - package.name(), - self.object_address - ); - prompt_yes_with_override(&message, self.txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::object_code_deployment_upgrade( - bcs::to_bytes(&built_package.extract_metadata()?) - .expect("Failed to serialize PackageMetadata"), - built_package.extract_code(), - self.object_address, - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - - if !self.override_size_check_option.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - let result = self - .txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from); - - if result.is_ok() { - println!( - "Code was successfully upgraded at object address {}.", - self.object_address - ); - } - result - } -} - /// Publishes the modules in a Move package to the Aptos blockchain under a resource account #[derive(Parser)] pub struct CreateResourceAccountAndPublishPackage { @@ -1087,8 +863,12 @@ pub struct CreateResourceAccountAndPublishPackage { #[clap(long)] pub(crate) address_name: String, - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, + /// Whether to override the check for maximal size of published data + /// + /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it + /// will still be blocked from publishing. + #[clap(long)] + pub(crate) override_size_check: bool, #[clap(flatten)] pub(crate) seed_args: ResourceAccountSeed, @@ -1111,7 +891,7 @@ impl CliCommand for CreateResourceAccountAndPublishPackage { address_name, mut move_options, txn_options, - override_size_check_option, + override_size_check, included_artifacts_args, seed_args, } = self; @@ -1138,10 +918,8 @@ impl CliCommand for CreateResourceAccountAndPublishPackage { move_options.dev, move_options.skip_fetch_latest_git_deps, move_options.named_addresses(), - move_options.override_std, move_options.bytecode_version, move_options.compiler_version, - move_options.language_version, move_options.skip_attribute_checks, move_options.check_test_code, ); @@ -1164,7 +942,7 @@ impl CliCommand for CreateResourceAccountAndPublishPackage { ); let size = bcs::serialized_size(&payload)?; println!("package size {} bytes", size); - if !override_size_check_option.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { + if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { return Err(CliError::UnexpectedError(format!( "The package is larger than {} bytes ({} bytes)! To lower the size \ you may want to include less artifacts via `--included-artifacts`. \ @@ -1197,10 +975,6 @@ pub struct DownloadPackage { #[clap(long, value_parser)] pub output_dir: Option, - /// Whether to download the bytecode of the package. - #[clap(long, short)] - pub bytecode: bool, - #[clap(flatten)] pub(crate) rest_options: RestOptions, #[clap(flatten)] @@ -1218,7 +992,7 @@ impl CliCommand<&'static str> for DownloadPackage { async fn execute(self) -> CliTypedResult<&'static str> { let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, self.bytecode).await?; + let registry = CachedPackageRegistry::create(url, self.account).await?; let output_dir = dir_default_to_current(self.output_dir)?; let package = registry @@ -1239,13 +1013,6 @@ impl CliCommand<&'static str> for DownloadPackage { package .save_package_to_disk(package_path.as_path()) .map_err(|e| CliError::UnexpectedError(format!("Failed to save package: {}", e)))?; - if self.bytecode { - for module in package.module_names() { - if let Some(bytecode) = registry.get_bytecode(module).await? { - package.save_bytecode_to_disk(package_path.as_path(), module, bytecode)? - } - } - }; println!( "Saved package with {} module(s) to `{}`", package.module_names().len(), @@ -1291,10 +1058,8 @@ impl CliCommand<&'static str> for VerifyPackage { self.move_options.dev, self.move_options.skip_fetch_latest_git_deps, self.move_options.named_addresses(), - self.move_options.override_std.clone(), self.move_options.bytecode_version, self.move_options.compiler_version, - self.move_options.language_version, self.move_options.skip_attribute_checks, self.move_options.check_test_code, ) @@ -1305,7 +1070,7 @@ impl CliCommand<&'static str> for VerifyPackage { // Now pull the compiled package let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, false).await?; + let registry = CachedPackageRegistry::create(url, self.account).await?; let package = registry .get_package(pack.name()) .await @@ -1378,7 +1143,7 @@ impl CliCommand<&'static str> for ListPackage { async fn execute(self) -> CliTypedResult<&'static str> { let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, false).await?; + let registry = CachedPackageRegistry::create(url, self.account).await?; match self.query { MoveListQuery::Packages => { for name in registry.package_names() { @@ -1513,167 +1278,6 @@ impl CliCommand for RunScript { } } -#[derive(Clone, Debug)] -pub enum ReplayNetworkSelection { - Mainnet, - Testnet, - Devnet, - RestEndpoint(String), -} - -/// Replay a comitted transaction using a local VM. -#[derive(Parser, Debug)] -pub struct Replay { - /// The network to replay on. - /// - /// Possible values: - /// mainnet, testnet, - #[clap(long)] - pub(crate) network: ReplayNetworkSelection, - - /// The id of the transaction to replay. Also being referred to as "version" in some contexts. - #[clap(long)] - pub(crate) txn_id: u64, - - /// If this option is set, benchmark the transaction and report the running time(s). - #[clap(long)] - pub(crate) benchmark: bool, - - /// If this option is set, profile the transaction and generate a detailed report of its gas usage. - #[clap(long)] - pub(crate) profile_gas: bool, - - /// If present, skip the comparison against the expected transaction output. - #[clap(long)] - pub(crate) skip_comparison: bool, -} - -impl FromStr for ReplayNetworkSelection { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(match s { - "mainnet" => Self::Mainnet, - "testnet" => Self::Testnet, - "devnet" => Self::Devnet, - _ => Self::RestEndpoint(s.to_owned()), - }) - } -} - -#[async_trait] -impl CliCommand for Replay { - fn command_name(&self) -> &'static str { - "Replay" - } - - async fn execute(self) -> CliTypedResult { - use ReplayNetworkSelection::*; - - if self.profile_gas && self.benchmark { - return Err(CliError::UnexpectedError( - "Cannot perform benchmarking and gas profiling at the same time.".to_string(), - )); - } - - let rest_endpoint = match &self.network { - Mainnet => "https://fullnode.mainnet.aptoslabs.com", - Testnet => "https://fullnode.testnet.aptoslabs.com", - Devnet => "https://fullnode.devnet.aptoslabs.com", - RestEndpoint(url) => url, - }; - - let debugger = AptosDebugger::rest_client(Client::new( - Url::parse(rest_endpoint) - .map_err(|_err| CliError::UnableToParse("url", rest_endpoint.to_string()))?, - ))?; - - // Fetch the transaction to replay. - let (txn, txn_info) = debugger - .get_committed_transaction_at_version(self.txn_id) - .await?; - - let txn = match txn { - Transaction::UserTransaction(txn) => txn, - _ => { - return Err(CliError::UnexpectedError( - "Unsupported transaction type. Only user transactions are supported." - .to_string(), - )) - }, - }; - - let hash = txn.committed_hash(); - - // Execute the transaction. - let (vm_status, vm_output) = if self.profile_gas { - println!("Profiling transaction..."); - local_simulation::profile_transaction_using_debugger( - &debugger, - self.txn_id, - txn.clone(), - hash, - )? - } else if self.benchmark { - println!("Benchmarking transaction..."); - local_simulation::benchmark_transaction_using_debugger( - &debugger, - self.txn_id, - txn.clone(), - hash, - )? - } else { - println!("Replaying transaction..."); - local_simulation::run_transaction_using_debugger( - &debugger, - self.txn_id, - txn.clone(), - hash, - )? - }; - - // Materialize into transaction output and check if the outputs match. - let state_view = debugger.state_view_at_version(self.txn_id); - let resolver = state_view.as_move_resolver(); - - let txn_output = vm_output - .try_materialize_into_transaction_output(&resolver) - .map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to materialize into transaction output: {}", - err - )) - })?; - - if !self.skip_comparison { - txn_output - .ensure_match_transaction_info(self.txn_id, &txn_info, None, None) - .map_err(|msg| CliError::UnexpectedError(msg.to_string()))?; - } - - // Generate the transaction summary. - let success = match txn_output.status() { - TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), - TransactionStatus::Discard(_) | TransactionStatus::Retry => None, - }; - - let summary = TransactionSummary { - transaction_hash: txn.committed_hash().into(), - gas_used: Some(txn_output.gas_used()), - gas_unit_price: Some(txn.gas_unit_price()), - pending: None, - sender: Some(txn.sender()), - sequence_number: Some(txn.sequence_number()), - success, - timestamp_us: None, - version: Some(self.txn_id), - vm_status: Some(vm_status.to_string()), - }; - - Ok(summary) - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FunctionArgType { Address, @@ -1852,21 +1456,20 @@ impl FromStr for FunctionArgType { "u128" => Ok(FunctionArgType::U128), "u256" => Ok(FunctionArgType::U256), "raw" => Ok(FunctionArgType::Raw), - str => { - Err(CliError::CommandArgumentError(format!( - "Invalid arg type '{}'. Must be one of: ['{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}']", - str, - FunctionArgType::Address, - FunctionArgType::Bool, - FunctionArgType::Hex, - FunctionArgType::String, - FunctionArgType::U8, - FunctionArgType::U16, - FunctionArgType::U32, - FunctionArgType::U64, - FunctionArgType::U128, - FunctionArgType::U256, - FunctionArgType::Raw))) + str => {Err(CliError::CommandArgumentError(format!( + "Invalid arg type '{}'. Must be one of: ['{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}']", + str, + FunctionArgType::Address, + FunctionArgType::Bool, + FunctionArgType::Hex, + FunctionArgType::String, + FunctionArgType::U8, + FunctionArgType::U16, + FunctionArgType::U32, + FunctionArgType::U64, + FunctionArgType::U128, + FunctionArgType::U256, + FunctionArgType::Raw))) } } } diff --git a/crates/aptos/src/move_tool/package_hooks.rs b/crates/aptos/src/move_tool/package_hooks.rs new file mode 100644 index 0000000000000..ae76b5b6d8c34 --- /dev/null +++ b/crates/aptos/src/move_tool/package_hooks.rs @@ -0,0 +1,54 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{common::types::load_account_arg, move_tool::CachedPackageRegistry}; +use aptos_framework::UPGRADE_POLICY_CUSTOM_FIELD; +use futures::executor::block_on; +use move_package::{ + compilation::package_layout::CompiledPackageLayout, package_hooks::PackageHooks, + source_package::parsed_manifest::CustomDepInfo, +}; +use move_symbol_pool::Symbol; +use reqwest::Url; + +pub fn register_package_hooks() { + move_package::package_hooks::register_package_hooks(Box::new(AptosPackageHooks {})) +} + +struct AptosPackageHooks {} + +impl PackageHooks for AptosPackageHooks { + fn custom_package_info_fields(&self) -> Vec { + vec![UPGRADE_POLICY_CUSTOM_FIELD.to_string()] + } + + fn custom_dependency_key(&self) -> Option { + Some("aptos".to_string()) + } + + fn resolve_custom_dependency( + &self, + _dep_name: Symbol, + info: &CustomDepInfo, + ) -> anyhow::Result<()> { + block_on(maybe_download_package(info)) + } +} + +async fn maybe_download_package(info: &CustomDepInfo) -> anyhow::Result<()> { + if !info + .download_to + .join(CompiledPackageLayout::BuildInfo.path()) + .exists() + { + let registry = CachedPackageRegistry::create( + Url::parse(info.node_url.as_str())?, + load_account_arg(info.package_address.as_str())?, + ) + .await?; + let package = registry.get_package(info.package_name).await?; + package.save_package_to_disk(info.download_to.as_path()) + } else { + Ok(()) + } +} diff --git a/crates/aptos/src/move_tool/show.rs b/crates/aptos/src/move_tool/show.rs index 1a910b322d70f..22e49f0848a3c 100644 --- a/crates/aptos/src/move_tool/show.rs +++ b/crates/aptos/src/move_tool/show.rs @@ -63,10 +63,8 @@ impl CliCommand> for ShowAbi { self.move_options.dev, self.move_options.skip_fetch_latest_git_deps, self.move_options.named_addresses(), - self.move_options.override_std.clone(), self.move_options.bytecode_version, self.move_options.compiler_version, - self.move_options.language_version, self.move_options.skip_attribute_checks, self.move_options.check_test_code, ) diff --git a/crates/aptos/src/move_tool/stored_package.rs b/crates/aptos/src/move_tool/stored_package.rs new file mode 100644 index 0000000000000..5f931d5ada0eb --- /dev/null +++ b/crates/aptos/src/move_tool/stored_package.rs @@ -0,0 +1,215 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::bail; +use aptos_framework::{ + natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy}, + unzip_metadata_str, +}; +use aptos_rest_client::Client; +use aptos_types::account_address::AccountAddress; +use move_package::compilation::package_layout::CompiledPackageLayout; +use reqwest::Url; +use std::{fmt, fs, path::Path}; + +// TODO: this is a first naive implementation of the package registry. Before mainnet +// we need to use tables for the package registry. + +/// Represents the package registry at a given account. +pub struct CachedPackageRegistry { + inner: PackageRegistry, +} + +/// Represents the package metadata found in an registry. +pub struct CachedPackageMetadata<'a> { + metadata: &'a PackageMetadata, +} + +/// Represents the package metadata found in an registry. +pub struct CachedModuleMetadata<'a> { + metadata: &'a ModuleMetadata, +} + +impl fmt::Display for CachedPackageMetadata<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{}", self.metadata)?; + Ok(()) + } +} + +impl CachedPackageRegistry { + /// Creates a new registry. + pub async fn create(url: Url, addr: AccountAddress) -> anyhow::Result { + let client = Client::new(url); + // Need to use a different type to deserialize JSON + let inner = client + .get_account_resource_bcs::(addr, "0x1::code::PackageRegistry") + .await? + .into_inner(); + Ok(Self { inner }) + } + + /// Returns the list of packages in this registry by name. + pub fn package_names(&self) -> Vec<&str> { + self.inner + .packages + .iter() + .map(|p| p.name.as_str()) + .collect() + } + + /// Finds the metadata for the given module in the registry by its unique name. + pub async fn get_module<'a>( + &self, + name: impl AsRef, + ) -> anyhow::Result> { + let name = name.as_ref(); + for package in &self.inner.packages { + for module in &package.modules { + if module.name == name { + return Ok(CachedModuleMetadata { metadata: module }); + } + } + } + bail!("module `{}` not found", name) + } + + /// Finds the metadata for the given package in the registry by its unique name. + pub async fn get_package<'a>( + &self, + name: impl AsRef, + ) -> anyhow::Result> { + let name = name.as_ref(); + for package in &self.inner.packages { + if package.name == name { + return Ok(CachedPackageMetadata { metadata: package }); + } + } + bail!("package `{}` not found", name) + } +} + +impl<'a> CachedPackageMetadata<'a> { + pub fn name(&self) -> &str { + &self.metadata.name + } + + pub fn upgrade_policy(&self) -> UpgradePolicy { + self.metadata.upgrade_policy + } + + pub fn upgrade_number(&self) -> u64 { + self.metadata.upgrade_number + } + + pub fn source_digest(&self) -> &str { + &self.metadata.source_digest + } + + pub fn manifest(&self) -> anyhow::Result { + unzip_metadata_str(&self.metadata.manifest) + } + + pub fn module_names(&self) -> Vec<&str> { + self.metadata + .modules + .iter() + .map(|s| s.name.as_str()) + .collect() + } + + pub fn module(&self, name: impl AsRef) -> anyhow::Result> { + let name = name.as_ref(); + for module in &self.metadata.modules { + if module.name == name { + return Ok(CachedModuleMetadata { metadata: module }); + } + } + bail!("module `{}` not found", name) + } + + pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> { + fs::create_dir_all(path)?; + fs::write( + path.join("Move.toml"), + unzip_metadata_str(&self.metadata.manifest)?, + )?; + let sources_dir = path.join(CompiledPackageLayout::Sources.path()); + fs::create_dir_all(&sources_dir)?; + for module in &self.metadata.modules { + let source = match module.source.is_empty() { + true => { + println!("module without code: {}", module.name); + "".into() + }, + false => unzip_metadata_str(&module.source)?, + }; + fs::write(sources_dir.join(format!("{}.move", module.name)), source)?; + } + Ok(()) + } + + pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> { + let self_metadata = self.metadata; + + if self_metadata.name != package_metadata.name { + bail!( + "Package name doesn't match {} : {}", + package_metadata.name, + self_metadata.name + ) + } else if self_metadata.deps != package_metadata.deps { + bail!( + "Dependencies don't match {:?} : {:?}", + package_metadata.deps, + self_metadata.deps + ) + } else if self_metadata.modules != package_metadata.modules { + bail!( + "Modules don't match {:?} : {:?}", + package_metadata.modules, + self_metadata.modules + ) + } else if self_metadata.manifest != package_metadata.manifest { + bail!( + "Manifest doesn't match {:?} : {:?}", + package_metadata.manifest, + self_metadata.manifest + ) + } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy { + bail!( + "Upgrade policy doesn't match {:?} : {:?}", + package_metadata.upgrade_policy, + self_metadata.upgrade_policy + ) + } else if self_metadata.extension != package_metadata.extension { + bail!( + "Extensions doesn't match {:?} : {:?}", + package_metadata.extension, + self_metadata.extension + ) + } else if self_metadata.source_digest != package_metadata.source_digest { + bail!( + "Source digests doesn't match {:?} : {:?}", + package_metadata.source_digest, + self_metadata.source_digest + ) + } + + Ok(()) + } +} + +impl<'a> CachedModuleMetadata<'a> { + pub fn name(&self) -> &str { + &self.metadata.name + } + + pub fn zipped_source(&self) -> &[u8] { + &self.metadata.source + } + + pub fn zipped_source_map_raw(&self) -> &[u8] { + &self.metadata.source_map + } +} diff --git a/crates/aptos/src/node/analyze/analyze_validators.rs b/crates/aptos/src/node/analyze/analyze_validators.rs index 1f4e6754a6f21..26deff503040c 100644 --- a/crates/aptos/src/node/analyze/analyze_validators.rs +++ b/crates/aptos/src/node/analyze/analyze_validators.rs @@ -11,12 +11,7 @@ use aptos_types::{ account_config::{new_block_event_key, NewBlockEvent}, }; use itertools::Itertools; -use std::{ - cmp::Ordering, - collections::{BTreeMap, HashMap, VecDeque}, - convert::TryFrom, - ops::Add, -}; +use std::{cmp::Ordering, collections::HashMap, convert::TryFrom, ops::Add}; /// Single validator stats #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -105,26 +100,6 @@ impl Add for ValidatorStats { } } -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum TpsInterval { - Blocks(usize), - Seconds(u32), -} - -#[derive(Clone)] -pub struct MaxTpsStats { - /// Max TPS - pub tps: f32, - /// End version of the interval at which Max TPS was achieved - pub end_version: u64, - /// Number of transactions in the interval at which Max TPS was achieved - pub txns: u32, - /// Number of blocks in which Max TPS was achieved - pub blocks: usize, - /// Duration of the interval in which Max TPS was achieved - pub duration: f32, -} - /// Statistics for all validators #[derive(Clone)] pub struct EpochStats { @@ -142,8 +117,6 @@ pub struct EpochStats { pub nil_blocks: u32, /// Total voting power pub total_voting_power: u128, - /// Max TPS per block interval - pub max_tps_per_block_interval: BTreeMap, } impl EpochStats { @@ -209,21 +182,6 @@ impl Add for EpochStats { nil_blocks: self.nil_blocks + other.nil_blocks, total_transactions: self.total_transactions + other.total_transactions, total_voting_power: 0, - max_tps_per_block_interval: self - .max_tps_per_block_interval - .into_iter() - .map(|(k, v)| { - let other_v = other.max_tps_per_block_interval.get(&k).unwrap(); - ( - k, - if v.tps > other_v.tps { - v - } else { - other_v.clone() - }, - ) - }) - .collect(), } } } @@ -309,33 +267,6 @@ impl AnalyzeValidators { let mut votes = HashMap::::new(); let mut transactions = HashMap::::new(); - // because we measure based on block timestamp, we need interval larger than when backpressure - // kicks in, to make sure we are measuring end-to-end throughput, not just ordering throughput. - let mut max_tps_tuples = vec![ - TpsInterval::Blocks(15), - TpsInterval::Blocks(30), - TpsInterval::Blocks(45), - TpsInterval::Blocks(60), - TpsInterval::Seconds(60), - TpsInterval::Seconds(120), - TpsInterval::Seconds(180), - TpsInterval::Seconds(300), - TpsInterval::Seconds(600), - TpsInterval::Seconds(1200), - TpsInterval::Seconds(1800), - TpsInterval::Seconds(3600), - ] - .into_iter() - .map(|v| { - (v, VecDeque::new(), MaxTpsStats { - tps: 0.0, - end_version: 0, - txns: 0, - blocks: 0, - duration: 0.0, - }) - }) - .collect::>(); let mut trimmed_rounds = 0; let mut nil_blocks = 0; let mut previous_round = 0; @@ -382,7 +313,6 @@ impl AnalyzeValidators { let cur_transactions_option = blocks .get(pos + 1) .map(|next| u32::try_from(next.version - block.version - 2).unwrap()); - let cur_next_block_option = blocks.get(pos + 1); if let Some(cur_transactions) = cur_transactions_option { if is_nil { assert_eq!( @@ -395,47 +325,6 @@ impl AnalyzeValidators { } *transactions.entry(event.proposer()).or_insert(0) += cur_transactions; } - - if let (Some(cur_transactions), Some(cur_next_block)) = - (cur_transactions_option, cur_next_block_option) - { - let cur_end_timestamp = cur_next_block.event.proposed_time(); - for (interval, max_tps_deque, max_tps) in &mut max_tps_tuples { - max_tps_deque.push_back((cur_transactions, event.proposed_time())); - - match interval { - TpsInterval::Blocks(num_blocks_for_max_tps) => { - while max_tps_deque.len() > *num_blocks_for_max_tps { - max_tps_deque.pop_front(); - } - }, - TpsInterval::Seconds(num_seconds_for_max_tps) => { - while let Some((_, front_ts)) = max_tps_deque.front() { - let passed = (cur_end_timestamp - front_ts) as f32 / 1000000.0; - if passed > *num_seconds_for_max_tps as f32 { - max_tps_deque.pop_front(); - } else { - break; - } - } - }, - } - - if !max_tps_deque.is_empty() { - let passed = (cur_end_timestamp - max_tps_deque.front().unwrap().1) as f32 - / 1000000.0; - let txns: u32 = max_tps_deque.iter().map(|(txns, _)| *txns).sum(); - let tps = txns as f32 / passed; - if tps > max_tps.tps { - max_tps.tps = tps; - max_tps.duration = passed; - max_tps.end_version = cur_next_block.version - 1; - max_tps.blocks = max_tps_deque.len(); - max_tps.txns = txns; - } - } - } - } } let total_successes: u32 = successes.values().sum(); let total_failures: u32 = failures.values().sum(); @@ -473,10 +362,6 @@ impl AnalyzeValidators { .iter() .map(|validator| validator.voting_power as u128) .sum(), - max_tps_per_block_interval: max_tps_tuples - .into_iter() - .map(|(num_blocks_for_max_tps, _, max_tps)| (num_blocks_for_max_tps, max_tps)) - .collect(), }; } diff --git a/crates/aptos/src/node/analyze/fetch_metadata.rs b/crates/aptos/src/node/analyze/fetch_metadata.rs new file mode 100644 index 0000000000000..e9ab24e8b9c15 --- /dev/null +++ b/crates/aptos/src/node/analyze/fetch_metadata.rs @@ -0,0 +1,337 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{anyhow, Result}; +use aptos_rest_client::{ + aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange}, + Client as RestClient, Transaction, VersionedNewBlockEvent, +}; +use aptos_types::account_address::AccountAddress; +use std::str::FromStr; + +const MAX_FETCH_BATCH_SIZE: u16 = 1000; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct ValidatorInfo { + pub address: AccountAddress, + pub voting_power: u64, + pub validator_index: u16, +} + +pub struct EpochInfo { + pub epoch: u64, + pub blocks: Vec, + pub validators: Vec, + pub partial: bool, +} + +pub struct FetchMetadata {} + +impl FetchMetadata { + fn get_validator_addresses( + data: &MoveResource, + field_name: &str, + ) -> Result> { + fn extract_validator_address(validator: &serde_json::Value) -> Result { + Ok(ValidatorInfo { + address: AccountAddress::from_hex_literal( + validator.get("addr").unwrap().as_str().unwrap(), + ) + .map_err(|e| anyhow!("Cannot parse address {:?}", e))?, + voting_power: validator + .get("voting_power") + .unwrap() + .as_str() + .unwrap() + .parse() + .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?, + validator_index: validator + .get("config") + .unwrap() + .get("validator_index") + .unwrap() + .as_str() + .unwrap() + .parse() + .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?, + }) + } + + let validators_json = data + .data + .0 + .get(&IdentifierWrapper::from_str(field_name).unwrap()) + .unwrap(); + if let serde_json::Value::Array(validators_array) = validators_json { + let mut validators: Vec = vec![]; + for validator in validators_array { + validators.push(extract_validator_address(validator)?); + } + Ok(validators) + } else { + Err(anyhow!("{} validators not in json", field_name)) + } + } + + async fn get_transactions_in_range( + client: &RestClient, + start: u64, + last: u64, + ) -> Result> { + let mut result = Vec::new(); + let mut cursor = start; + while cursor < last { + let limit = std::cmp::min(MAX_FETCH_BATCH_SIZE as u64, last - cursor) as u16; + let mut current = client + .get_transactions(Some(cursor), Some(limit)) + .await? + .into_inner(); + if current.is_empty() { + return Err(anyhow!( + "No transactions returned with start={} and limit={}", + cursor, + limit + )); + } + cursor += current.len() as u64; + result.append(&mut current); + } + Ok(result) + } + + fn get_validators_from_transaction(transaction: &Transaction) -> Result> { + if let Ok(info) = transaction.transaction_info() { + for change in &info.changes { + if let WriteSetChange::WriteResource(resource) = change { + if resource.data.typ.name.0.as_str() == "ValidatorSet" { + // No pending at epoch change + assert_eq!( + Vec::::new(), + FetchMetadata::get_validator_addresses( + &resource.data, + "pending_inactive" + )? + ); + assert_eq!( + Vec::::new(), + FetchMetadata::get_validator_addresses( + &resource.data, + "pending_active" + )? + ); + return FetchMetadata::get_validator_addresses( + &resource.data, + "active_validators", + ); + } + } + } + } + Err(anyhow!("Couldn't find ValidatorSet in the transaction")) + } + + pub async fn fetch_new_block_events( + client: &RestClient, + start_epoch: Option, + end_epoch: Option, + ) -> Result> { + let (last_events, state) = client + .get_new_block_events_bcs(None, Some(1)) + .await? + .into_parts(); + let mut start_seq_num = state.oldest_block_height; + assert_eq!(last_events.len(), 1, "{:?}", last_events); + let last_event = last_events.first().unwrap(); + let last_seq_num = last_event.sequence_number; + + let wanted_start_epoch = { + let mut wanted_start_epoch = start_epoch.unwrap_or(2); + if wanted_start_epoch < 0 { + wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1; + } + + let oldest_event = client + .get_new_block_events_bcs(Some(start_seq_num), Some(1)) + .await? + .into_inner() + .into_iter() + .next() + .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?; + let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2); + if oldest_fetchable_epoch > wanted_start_epoch as u64 { + println!( + "Oldest full epoch that can be retreived is {} ", + oldest_fetchable_epoch + ); + oldest_fetchable_epoch + } else { + wanted_start_epoch as u64 + } + }; + let wanted_end_epoch = { + let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX); + if wanted_end_epoch < 0 { + wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1; + } + std::cmp::min( + last_event.event.epoch() + 1, + std::cmp::max(2, wanted_end_epoch) as u64, + ) + }; + + if wanted_start_epoch > 2 { + let mut search_end = last_seq_num; + + // Stop when search is close enough, and we can then linearly + // proceed from there. + // Since we are ignoring results we are fetching during binary search + // we want to stop when we are close. + while start_seq_num + 20 < search_end { + let mid = (start_seq_num + search_end) / 2; + + let mid_epoch = client + .get_new_block_events_bcs(Some(mid), Some(1)) + .await? + .into_inner() + .first() + .unwrap() + .event + .epoch(); + + if mid_epoch < wanted_start_epoch { + start_seq_num = mid; + } else { + search_end = mid; + } + } + } + + let mut batch_index = 0; + + println!( + "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}", + start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch, + ); + let mut result: Vec = vec![]; + if wanted_start_epoch >= wanted_end_epoch { + return Ok(result); + } + + let mut validators: Vec = vec![]; + let mut current: Vec = vec![]; + let mut epoch = 0; + + let mut cursor = start_seq_num; + loop { + let response = client + .get_new_block_events_bcs(Some(cursor), Some(MAX_FETCH_BATCH_SIZE)) + .await; + + if response.is_err() { + println!( + "Failed to read new_block_events beyond {}, stopping. {:?}", + cursor, + response.unwrap_err() + ); + assert!(!validators.is_empty()); + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: true, + }); + return Ok(result); + } + let events = response.unwrap().into_inner(); + + if events.is_empty() { + return Err(anyhow!( + "No transactions returned with start={} and limit={}", + cursor, + MAX_FETCH_BATCH_SIZE + )); + } + + cursor += events.len() as u64; + batch_index += 1; + + for event in events { + if event.event.epoch() > epoch { + if epoch == 0 { + epoch = event.event.epoch(); + current = vec![]; + } else { + let last = current.last().cloned(); + if let Some(last) = last { + let transactions = FetchMetadata::get_transactions_in_range( + client, + last.version, + event.version, + ) + .await?; + assert_eq!( + transactions.first().unwrap().version().unwrap(), + last.version + ); + for transaction in transactions { + if let Ok(new_validators) = + FetchMetadata::get_validators_from_transaction(&transaction) + { + if epoch >= wanted_start_epoch { + assert!(!validators.is_empty()); + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: false, + }); + } + current = vec![]; + + validators = new_validators; + validators.sort_by_key(|v| v.validator_index); + assert_eq!(epoch + 1, event.event.epoch()); + epoch = event.event.epoch(); + if epoch >= wanted_end_epoch { + return Ok(result); + } + break; + } + } + assert!( + current.is_empty(), + "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}", + last.version, + event.version - last.version, + event.event.epoch(), + ); + } + } + } + current.push(event); + } + + if batch_index % 100 == 0 { + println!( + "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents", + result.len(), + epoch, + current.len(), + cursor + ); + } + + if cursor > last_seq_num { + if !validators.is_empty() { + result.push(EpochInfo { + epoch, + blocks: current, + validators: validators.clone(), + partial: true, + }); + } + return Ok(result); + } + } + } +} diff --git a/crates/aptos/src/node/analyze/mod.rs b/crates/aptos/src/node/analyze/mod.rs new file mode 100644 index 0000000000000..ac8c7016375d3 --- /dev/null +++ b/crates/aptos/src/node/analyze/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod analyze_validators; +pub mod fetch_metadata; diff --git a/crates/aptos/src/node/local_testnet/docker.rs b/crates/aptos/src/node/local_testnet/docker.rs index 82828be1e5ebc..11128b3ac84cd 100644 --- a/crates/aptos/src/node/local_testnet/docker.rs +++ b/crates/aptos/src/node/local_testnet/docker.rs @@ -288,7 +288,7 @@ pub fn setup_docker_logging(test_dir: &Path, dir_name: &str, container_name: &st /// This shutdown step stops a container with the given name. If no container is found /// we continue without error. We choose to stop the container on shutdown rather than /// totally delete it so the user can check the logs if it was an unexpected shutdown. -/// When the localnet is started again, any leftover container will be deleted. +/// When the local testnet is started again, any leftover container will be deleted. #[derive(Clone, Debug)] pub struct StopContainerShutdownStep { container_name: &'static str, diff --git a/crates/aptos/src/node/local_testnet/faucet.rs b/crates/aptos/src/node/local_testnet/faucet.rs index 5a334c729fbf7..065bfb6633288 100644 --- a/crates/aptos/src/node/local_testnet/faucet.rs +++ b/crates/aptos/src/node/local_testnet/faucet.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalnet}; +use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; use anyhow::Result; use aptos_faucet_core::server::{FunderKeyEnum, RunConfig}; use async_trait::async_trait; @@ -10,7 +10,7 @@ use maplit::hashset; use reqwest::Url; use std::{collections::HashSet, net::Ipv4Addr, path::PathBuf}; -/// Args related to running a faucet in the localnet. +/// Args related to running a faucet in the local testnet. #[derive(Debug, Parser)] pub struct FaucetArgs { /// Do not run a faucet alongside the node. @@ -46,7 +46,7 @@ pub struct FaucetManager { impl FaucetManager { pub fn new( - args: &RunLocalnet, + args: &RunLocalTestnet, prerequisite_health_checkers: HashSet, bind_to: Ipv4Addr, test_dir: PathBuf, diff --git a/crates/aptos/src/node/local_testnet/hasura_metadata.json b/crates/aptos/src/node/local_testnet/hasura_metadata.json index 84882e647e183..e1ea9b96b0186 100644 --- a/crates/aptos/src/node/local_testnet/hasura_metadata.json +++ b/crates/aptos/src/node/local_testnet/hasura_metadata.json @@ -1,5 +1,5 @@ { - "resource_version": 319, + "resource_version": 296, "metadata": { "version": 3, "sources": [ @@ -7,54 +7,11 @@ "name": "indexer-v2", "kind": "postgres", "tables": [ - { - "table": { - "name": "parsed_asset_uris", - "schema": "nft_metadata_crawler" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "animation_optimizer_retry_count", - "asset_uri", - "cdn_animation_uri", - "cdn_image_uri", - "cdn_json_uri", - "image_optimizer_retry_count", - "json_parser_retry_count", - "raw_animation_uri", - "raw_image_uri" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, { "table": { "name": "account_transactions", "schema": "public" }, - "object_relationships": [ - { - "name": "user_transaction", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "version" - }, - "insertion_order": null, - "remote_table": { - "name": "user_transactions", - "schema": "public" - } - } - } - } - ], "array_relationships": [ { "name": "coin_activities", @@ -136,7 +93,10 @@ { "role": "anonymous", "permission": { - "columns": ["account_address", "transaction_version"], + "columns": [ + "account_address", + "transaction_version" + ], "filter": {}, "limit": 100, "allow_aggregations": true @@ -252,7 +212,10 @@ { "role": "anonymous", "permission": { - "columns": ["account_address", "transaction_version"], + "columns": [ + "account_address", + "transaction_version" + ], "filter": {}, "limit": 100, "allow_aggregations": true @@ -331,7 +294,10 @@ { "role": "anonymous", "permission": { - "columns": ["address", "transaction_version"], + "columns": [ + "address", + "transaction_version" + ], "filter": {}, "limit": 100, "allow_aggregations": true @@ -630,19 +596,17 @@ "role": "anonymous", "permission": { "columns": [ - "domain", - "domain_expiration_timestamp", - "domain_with_suffix", - "expiration_timestamp", + "last_transaction_version", "is_active", "is_primary", - "last_transaction_version", + "domain", "owner_address", "registered_address", "subdomain", - "subdomain_expiration_policy", "token_name", - "token_standard" + "token_standard", + "domain_with_suffix", + "expiration_timestamp" ], "filter": {}, "limit": 100, @@ -770,23 +734,7 @@ "name": "current_collections_v2", "schema": "public" }, - "object_relationships": [ - { - "name": "cdn_asset_uris", - "using": { - "manual_configuration": { - "column_mapping": { - "uri": "asset_uri" - }, - "insertion_order": null, - "remote_table": { - "name": "parsed_asset_uris", - "schema": "nft_metadata_crawler" - } - } - } - } - ], + "object_relationships": [], "select_permissions": [ { "role": "anonymous", @@ -1124,21 +1072,6 @@ } } }, - { - "name": "cdn_asset_uris", - "using": { - "manual_configuration": { - "column_mapping": { - "token_uri": "asset_uri" - }, - "insertion_order": null, - "remote_table": { - "name": "parsed_asset_uris", - "schema": "nft_metadata_crawler" - } - } - } - }, { "name": "current_collection", "using": { @@ -1153,11 +1086,9 @@ } } } - } - ], - "array_relationships": [ + }, { - "name": "current_token_ownerships", + "name": "current_token_ownership", "using": { "manual_configuration": { "column_mapping": { @@ -1178,9 +1109,7 @@ "permission": { "columns": [ "collection_id", - "decimals", "description", - "is_deleted_v2", "is_fungible_v2", "largest_property_version_v1", "last_transaction_timestamp", @@ -1325,7 +1254,6 @@ "is_soulbound_v2", "last_transaction_timestamp", "last_transaction_version", - "non_transferrable_by_owner", "owner_address", "property_version_v1", "storage_id", @@ -1474,32 +1402,6 @@ } ] }, - { - "table": { - "name": "delegated_staking_pool_balances", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "active_table_handle", - "inactive_table_handle", - "operator_commission_percentage", - "staking_pool_address", - "total_coins", - "total_shares", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - }, - "comment": "" - } - ] - }, { "table": { "name": "delegated_staking_pools", @@ -1577,7 +1479,10 @@ { "role": "anonymous", "permission": { - "columns": ["delegator_address", "pool_address"], + "columns": [ + "delegator_address", + "pool_address" + ], "filter": {}, "limit": 100, "allow_aggregations": true @@ -1694,12 +1599,10 @@ "icon_uri", "last_transaction_timestamp", "last_transaction_version", - "maximum_v2", "name", "project_uri", "supply_aggregator_table_handle_v1", "supply_aggregator_table_key_v1", - "supply_v2", "symbol", "token_standard" ], @@ -1718,7 +1621,10 @@ { "role": "anonymous", "permission": { - "columns": ["db", "is_indexer_up"], + "columns": [ + "db", + "is_indexer_up" + ], "filter": {}, "limit": 100 } @@ -1734,7 +1640,9 @@ { "role": "anonymous", "permission": { - "columns": ["chain_id"], + "columns": [ + "chain_id" + ], "filter": {} } } @@ -1749,7 +1657,10 @@ { "role": "anonymous", "permission": { - "columns": ["address", "transaction_version"], + "columns": [ + "address", + "transaction_version" + ], "filter": {}, "limit": 100, "allow_aggregations": true @@ -1766,7 +1677,10 @@ { "role": "anonymous", "permission": { - "columns": ["num_active_delegator", "pool_address"], + "columns": [ + "num_active_delegator", + "pool_address" + ], "filter": {}, "limit": 100 } @@ -1784,7 +1698,6 @@ "permission": { "columns": [ "last_success_version", - "last_transaction_timestamp", "last_updated", "processor" ], @@ -1819,35 +1732,6 @@ } ] }, - { - "table": { - "name": "signatures", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "is_sender_primary", - "multi_agent_index", - "multi_sig_index", - "public_key", - "public_key_indices", - "signature", - "signer", - "threshold", - "transaction_block_height", - "transaction_version", - "type" - ], - "filter": {}, - "limit": 100 - }, - "comment": "" - } - ] - }, { "table": { "name": "table_items", @@ -1880,7 +1764,11 @@ { "role": "anonymous", "permission": { - "columns": ["handle", "key_type", "value_type"], + "columns": [ + "handle", + "key_type", + "value_type" + ], "filter": {}, "limit": 100 } @@ -2182,6 +2070,13 @@ "from_env": "INDEXER_V2_POSTGRES_URL" }, "isolation_level": "read-committed", + "pool_settings": { + "connection_lifetime": 600, + "idle_timeout": 180, + "max_connections": 100, + "total_max_connections": 1500, + "pool_timeout": 120 + }, "use_prepared_statements": false } } @@ -2194,7 +2089,7 @@ "queries": [ { "name": "Latest Processor Status", - "query": "query LatestProcessorStatus {\n processor_status {\n processor\n last_updated\n last_success_version\n last_transaction_timestamp\n }\n}" + "query": "query LatestProcessorStatus {\n processor_status {\n processor\n last_updated\n last_success_version\n }\n}" } ] } @@ -2217,9 +2112,11 @@ "query_name": "Latest Processor Status" } }, - "methods": ["GET"], + "methods": [ + "GET" + ], "name": "Latest Processor Status", - "url": "get_latest_processor_status" + "url": "get_lastest_processor_status" } ], "api_limits": { diff --git a/crates/aptos/src/node/local_testnet/health_checker.rs b/crates/aptos/src/node/local_testnet/health_checker.rs new file mode 100644 index 0000000000000..ec22495b3c07e --- /dev/null +++ b/crates/aptos/src/node/local_testnet/health_checker.rs @@ -0,0 +1,216 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::indexer_api::confirm_metadata_applied; +use anyhow::{anyhow, Context, Result}; +use aptos_protos::indexer::v1::GetTransactionsRequest; +use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; +use diesel_async::{pg::AsyncPgConnection, AsyncConnection, RunQueryDsl}; +use futures::StreamExt; +use processor::schema::processor_status; +use reqwest::Url; +use serde::Serialize; +use std::time::Duration; +use tokio::time::Instant; +use tracing::info; + +const MAX_WAIT_S: u64 = 60; +const WAIT_INTERVAL_MS: u64 = 200; + +/// This provides a single place to define a variety of different healthchecks. In +/// cases where the name of the service being checked isn't obvious, the enum will take +/// a string arg that names it. +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] +pub enum HealthChecker { + /// Check that a HTTP API is up. The second param is the name of the HTTP service. + Http(Url, String), + /// Check that the node API is up. This is just a specific case of Http for extra + /// guarantees around liveliness. + NodeApi(Url), + /// Check that a data service GRPC stream is up. + DataServiceGrpc(Url), + /// Check that a postgres instance is up. + Postgres(String), + /// Check that a processor is successfully processing txns. The first value is the + /// postgres connection string. The second is the name of the processor. We check + /// the that last_success_version in the processor_status table is present and > 0. + Processor(String, String), + /// Check that the indexer API is up and the metadata has been applied. We only use + /// this one in the ready server. + IndexerApiMetadata(Url), +} + +impl HealthChecker { + pub async fn check(&self) -> Result<()> { + match self { + HealthChecker::Http(url, _) => { + reqwest::get(Url::clone(url)) + .await + .with_context(|| format!("Failed to GET {}", url))?; + Ok(()) + }, + HealthChecker::NodeApi(url) => { + aptos_rest_client::Client::new(Url::clone(url)) + .get_index() + .await?; + Ok(()) + }, + HealthChecker::DataServiceGrpc(url) => { + let mut client = aptos_indexer_grpc_utils::create_data_service_grpc_client( + url.clone(), + Some(Duration::from_secs(5)), + ) + .await?; + let request = tonic::Request::new(GetTransactionsRequest { + starting_version: Some(0), + ..Default::default() + }); + // Make sure we can stream the first message from the stream. + client + .get_transactions(request) + .await + .context("GRPC connection error")? + .into_inner() + .next() + .await + .context("Did not receive init signal from data service GRPC stream")? + .context("Error processing first message from GRPC stream")?; + Ok(()) + }, + HealthChecker::Postgres(connection_string) => { + AsyncPgConnection::establish(connection_string) + .await + .context("Failed to connect to postgres to check DB liveness")?; + Ok(()) + }, + HealthChecker::Processor(connection_string, processor_name) => { + let mut connection = AsyncPgConnection::establish(connection_string) + .await + .context("Failed to connect to postgres to check processor status")?; + let result = processor_status::table + .select((processor_status::last_success_version,)) + .filter(processor_status::processor.eq(processor_name)) + .first::<(i64,)>(&mut connection) + .await + .optional() + .context("Failed to look up processor status")?; + match result { + Some(result) => { + // This is last_success_version. + if result.0 > 0 { + info!( + "Processor {} started processing successfully (currently at version {})", + processor_name, result.0 + ); + Ok(()) + } else { + Err(anyhow!( + "Processor {} found in DB but last_success_version is zero", + processor_name + )) + } + }, + None => Err(anyhow!( + "Processor {} has not processed any transactions", + processor_name + )), + } + }, + HealthChecker::IndexerApiMetadata(url) => { + confirm_metadata_applied(url.clone()).await?; + Ok(()) + }, + } + } + + /// Wait up to MAX_WAIT_S seconds for a service to start up. + pub async fn wait( + &self, + // The service, if any, waiting for this service to start up. + waiting_service: Option<&str>, + ) -> Result<()> { + let prefix = self.to_string(); + wait_for_startup(|| self.check(), match waiting_service { + Some(waiting_service) => { + format!( + "{} at {} did not start up before {}", + prefix, + self.address_str(), + waiting_service, + ) + }, + None => format!("{} at {} did not start up", prefix, self.address_str()), + }) + .await + } + + /// This is only ever used for display purposes. If possible, this should be the + /// endpoint of the service that this HealthChecker is checking. + pub fn address_str(&self) -> &str { + match self { + HealthChecker::Http(url, _) => url.as_str(), + HealthChecker::NodeApi(url) => url.as_str(), + HealthChecker::DataServiceGrpc(url) => url.as_str(), + HealthChecker::Postgres(url) => url.as_str(), + HealthChecker::Processor(_, processor_name) => processor_name.as_str(), + HealthChecker::IndexerApiMetadata(url) => url.as_str(), + } + } + + /// Given a port, make an instance of HealthChecker::Http targeting 127.0.0.1. + pub fn http_checker_from_port(port: u16, name: String) -> Self { + Self::Http( + Url::parse(&format!("http://127.0.0.1:{}", port,)).unwrap(), + name, + ) + } +} + +impl std::fmt::Display for HealthChecker { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + HealthChecker::Http(_, name) => write!(f, "{}", name), + HealthChecker::NodeApi(_) => write!(f, "Node API"), + HealthChecker::DataServiceGrpc(_) => write!(f, "Transaction stream"), + HealthChecker::Postgres(_) => write!(f, "Postgres"), + HealthChecker::Processor(_, processor_name) => write!(f, "{}", processor_name), + HealthChecker::IndexerApiMetadata(_) => write!(f, "Indexer API with metadata applied"), + } + } +} + +async fn wait_for_startup(check_fn: F, error_message: String) -> Result<()> +where + F: Fn() -> Fut, + Fut: futures::Future>, +{ + let max_wait = Duration::from_secs(MAX_WAIT_S); + let wait_interval = Duration::from_millis(WAIT_INTERVAL_MS); + + let start = Instant::now(); + let mut started_successfully = false; + + let mut last_error_message = None; + while start.elapsed() < max_wait { + match check_fn().await { + Ok(_) => { + started_successfully = true; + break; + }, + Err(err) => { + last_error_message = Some(format!("{:#}", err)); + }, + } + tokio::time::sleep(wait_interval).await + } + + if !started_successfully { + let error_message = match last_error_message { + Some(last_error_message) => format!("{}: {}", error_message, last_error_message), + None => error_message, + }; + return Err(anyhow!(error_message)); + } + + Ok(()) +} diff --git a/crates/aptos/src/node/local_testnet/indexer_api.rs b/crates/aptos/src/node/local_testnet/indexer_api.rs index b723a7fc8f638..3440003aa53ae 100644 --- a/crates/aptos/src/node/local_testnet/indexer_api.rs +++ b/crates/aptos/src/node/local_testnet/indexer_api.rs @@ -8,7 +8,7 @@ use super::{ }, health_checker::HealthChecker, traits::{PostHealthyStep, ServiceManager, ShutdownStep}, - RunLocalnet, + RunLocalTestnet, }; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; @@ -34,17 +34,17 @@ const HASURA_IMAGE: &str = "hasura/graphql-engine:v2.36.1"; /// any references to tables that aren't created by the Rust processor migrations. /// /// To arrive at the final edited file I normally start with the new metadata file, -/// try to start the localnet, and check .aptos/testnet/main/tracing.log to +/// try to start the local testnet, and check .aptos/testnet/main/tracing.log to /// see what error Hasura returned. Remove the culprit from the metadata, which is /// generally a few tables and relations to those tables, and try again. Repeat until /// it accepts the metadata. /// -/// This works fine today since all the key processors you'd need in a localnet +/// This works fine today since all the key processors you'd need in a local testnet /// are in the set of processors written in Rust. If this changes, we can explore /// alternatives, e.g. running processors in other languages using containers. const HASURA_METADATA: &str = include_str!("hasura_metadata.json"); -/// Args related to running an indexer API for the localnet. +/// Args related to running an indexer API for the local testnet. #[derive(Debug, Parser)] pub struct IndexerApiArgs { /// If set, we will run a postgres DB using Docker (unless @@ -70,7 +70,7 @@ pub struct IndexerApiManager { impl IndexerApiManager { pub fn new( - args: &RunLocalnet, + args: &RunLocalTestnet, prerequisite_health_checkers: HashSet, test_dir: PathBuf, postgres_connection_string: String, diff --git a/crates/aptos/src/node/local_testnet/logging.rs b/crates/aptos/src/node/local_testnet/logging.rs new file mode 100644 index 0000000000000..3752582070d94 --- /dev/null +++ b/crates/aptos/src/node/local_testnet/logging.rs @@ -0,0 +1,102 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use dashmap::DashMap; +use std::{ + fs::{create_dir_all, File}, + path::PathBuf, + sync::{Arc, Mutex}, +}; +use tracing_subscriber::fmt::MakeWriter; + +/// This struct impls MakeWriter, a trait that returns writers for using by the +/// tracing_subscriber library. It returns a custom logger that logs to different +/// files based on the name of the worker (thread). +/// +/// To be as efficient as possible and only create + open each file once, we keep +/// track of file handles in a DashMap with this FileLock struct as the values. +/// Learn more about FileLock in the doc comment there. +pub struct ThreadNameMakeWriter { + /// The base directory to output logs to. + base_dir: PathBuf, + /// We keep open file handles here to avoid making them every time. They key is the + /// name of the file (thread name without the number). + file_handles: DashMap, +} + +impl ThreadNameMakeWriter { + pub fn new(base_dir: PathBuf) -> Self { + Self { + base_dir, + file_handles: DashMap::new(), + } + } +} + +impl<'a> MakeWriter<'a> for ThreadNameMakeWriter { + type Writer = Box; + + fn make_writer(&'a self) -> Self::Writer { + let base_dir = self.base_dir.clone(); + let thread_name = std::thread::current() + .name() + .unwrap_or("no-thread-name") + .to_string(); + let thread_name_no_number = truncate_last_segment(&thread_name, '-'); + let log_file = self + .file_handles + .entry(thread_name_no_number.clone()) + .or_insert_with(|| FileLock::new(create_file(base_dir, thread_name_no_number))) + .value() + .clone(); + Box::new(log_file) + } +} + +fn create_file(base_dir: PathBuf, thread_name_no_number: String) -> File { + let dir_path = base_dir.join(thread_name_no_number); + create_dir_all(&dir_path).expect("Failed to create log directory"); + let log_path = dir_path.join("tracing.log"); + std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(log_path) + .unwrap() +} + +fn truncate_last_segment(s: &str, delimiter: char) -> String { + s.rsplit_once(delimiter) + .map(|x| x.0) + .unwrap_or(s) + .to_string() +} + +/// This struct protects access to an open file handle. Using a Mutex is necessary +/// because we cannot allow concurrent access to the file. The Arc ensures that +/// every time we hand out a reference to the handle, it is the same handle. +#[derive(Clone)] +pub struct FileLock { + file: Arc>, +} + +impl FileLock { + pub fn new(file: File) -> Self { + Self { + file: Arc::new(Mutex::new(file)), + } + } +} + +impl std::io::Write for FileLock { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.file.lock().unwrap().write(buf) + } + + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.file.lock().unwrap().write_all(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.file.lock().unwrap().flush() + } +} diff --git a/crates/aptos/src/node/local_testnet/mod.rs b/crates/aptos/src/node/local_testnet/mod.rs index cf1a308137035..fc2f8e9bcb038 100644 --- a/crates/aptos/src/node/local_testnet/mod.rs +++ b/crates/aptos/src/node/local_testnet/mod.rs @@ -36,6 +36,7 @@ use crate::{ }, }; use anyhow::{Context, Result}; +#[cfg(feature = "aptos")] use aptos_indexer_grpc_server_framework::setup_logging; use async_trait::async_trait; use clap::Parser; @@ -52,13 +53,13 @@ use tracing_subscriber::fmt::MakeWriter; const TESTNET_FOLDER: &str = "testnet"; -/// Run a localnet +/// Run a local testnet /// -/// This localnet will run it's own genesis and run as a single node network +/// This local testnet will run it's own genesis and run as a single node network /// locally. A faucet and grpc transaction stream will run alongside the node unless /// you specify otherwise with --no-faucet and --no-txn-stream respectively. #[derive(Parser)] -pub struct RunLocalnet { +pub struct RunLocalTestnet { /// The directory to save all files for the node /// /// Defaults to .aptos/testnet @@ -105,7 +106,7 @@ pub struct RunLocalnet { log_to_stdout: bool, } -impl RunLocalnet { +impl RunLocalTestnet { /// Wait for many services to start up. This prints a message like "X is starting, /// please wait..." for each service and then "X is ready. Endpoint: " /// when it's ready. @@ -176,9 +177,9 @@ impl RunLocalnet { } #[async_trait] -impl CliCommand<()> for RunLocalnet { +impl CliCommand<()> for RunLocalTestnet { fn command_name(&self) -> &'static str { - "RunLocalnet" + "RunLocalTestnet" } fn jsonify_error_output(&self) -> bool { @@ -187,6 +188,7 @@ impl CliCommand<()> for RunLocalnet { async fn execute(mut self) -> CliTypedResult<()> { if self.log_to_stdout { + #[cfg(feature = "aptos")] setup_logging(None); } @@ -201,7 +203,7 @@ impl CliCommand<()> for RunLocalnet { // If asked, remove the current test directory and start with a new node. if self.force_restart && test_dir.exists() { prompt_yes_with_override( - "Are you sure you want to delete the existing localnet data?", + "Are you sure you want to delete the existing local testnet data?", self.prompt_options, )?; remove_dir_all(test_dir.as_path()).map_err(|err| { @@ -226,6 +228,7 @@ impl CliCommand<()> for RunLocalnet { let make_writer = move || { ThreadNameMakeWriter::new(td.clone()).make_writer() as Box }; + #[cfg(feature = "aptos")] setup_logging(Some(Box::new(make_writer))); } @@ -391,7 +394,7 @@ impl CliCommand<()> for RunLocalnet { .context("Failed to run post startup step")?; } - eprintln!("\nSetup is complete, you can now use the localnet!"); + eprintln!("\nSetup is complete, you can now use the local testnet!"); // Create a task that listens for ctrl-c. We want to intercept it so we can run // the shutdown steps before properly exiting. This is of course best effort, diff --git a/crates/aptos/src/node/local_testnet/node.rs b/crates/aptos/src/node/local_testnet/node.rs index 62696da50e10b..f90181c3d3d4a 100644 --- a/crates/aptos/src/node/local_testnet/node.rs +++ b/crates/aptos/src/node/local_testnet/node.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalnet}; +use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; use crate::node::local_testnet::utils::socket_addr_to_url; use anyhow::{anyhow, Context, Result}; use aptos_config::config::{NodeConfig, DEFAULT_GRPC_STREAM_PORT}; @@ -20,12 +20,12 @@ use std::{ }; /// Args specific to running a node (and its components, e.g. the txn stream) in the -/// localnet. +/// local testnet. #[derive(Debug, Parser)] pub struct NodeArgs { /// An overridable config template for the test node /// - /// If provided, the config will be used, and any needed configuration for the localnet + /// If provided, the config will be used, and any needed configuration for the local testnet /// will override the config's values #[clap(long, value_parser)] pub config_path: Option, @@ -37,12 +37,6 @@ pub struct NodeArgs { #[clap(long, value_parser, conflicts_with("config_path"))] pub test_config_override: Option, - /// Optimize the node for higher performance. - /// - /// Note: This is only useful for e2e performance testing, and should not be used in production. - #[clap(long)] - pub performance: bool, - /// Random seed for key generation in test mode /// /// This allows you to have deterministic keys for testing @@ -63,7 +57,7 @@ pub struct NodeArgs { /// If set we won't run the node at all. // - // Note: I decided that since running multiple partial localnets is a rare + // Note: I decided that since running multiple partial local testnets is a rare // case that only core devs would ever really want, it wasn't worth making the code // much more complex to support that case "first class". Instead, we have this flag // that does everything else to set up running the node, but never actually runs @@ -71,7 +65,7 @@ pub struct NodeArgs { // and invoke it again to run processors + indexer API. You might want to do this // for compatibility testing. If you use this flag and there _isn't_ a node already // running at the expected port, the processors will fail to connect to the txn - // stream (since there isn't one) and the localnet will crash. + // stream (since there isn't one) and the local testnet will crash. // // If we do change our minds on this one day, the correct way to do this would be // to let the user instead pass in a bunch of flags that declare where an existing @@ -91,7 +85,7 @@ pub struct NodeManager { } impl NodeManager { - pub fn new(args: &RunLocalnet, bind_to: Ipv4Addr, test_dir: PathBuf) -> Result { + pub fn new(args: &RunLocalTestnet, bind_to: Ipv4Addr, test_dir: PathBuf) -> Result { let rng = args .node_args .seed @@ -107,7 +101,6 @@ impl NodeManager { &test_dir, false, false, - args.node_args.performance, aptos_cached_packages::head_release_bundle(), rng, ) diff --git a/crates/aptos/src/node/local_testnet/postgres.rs b/crates/aptos/src/node/local_testnet/postgres.rs index 3cb29ffb79833..a1fc5ad0489b4 100644 --- a/crates/aptos/src/node/local_testnet/postgres.rs +++ b/crates/aptos/src/node/local_testnet/postgres.rs @@ -8,7 +8,7 @@ use super::{ }, health_checker::HealthChecker, traits::{ServiceManager, ShutdownStep}, - RunLocalnet, + RunLocalTestnet, }; use anyhow::{bail, Context, Result}; use async_trait::async_trait; @@ -25,11 +25,11 @@ use tracing::{info, warn}; pub const POSTGRES_CONTAINER_NAME: &str = "local-testnet-postgres"; const POSTGRES_VOLUME_NAME: &str = "local-testnet-postgres-data"; -const POSTGRES_IMAGE: &str = "postgres:14.11"; +const POSTGRES_IMAGE: &str = "postgres:14.9"; const DATA_PATH_IN_CONTAINER: &str = "/var/lib/mydata"; const POSTGRES_DEFAULT_PORT: u16 = 5432; -/// Args related to running postgres in the localnet. +/// Args related to running postgres in the local testnet. #[derive(Clone, Debug, Parser)] pub struct PostgresArgs { /// This is the database to connect to, both when --use-host-postgres is set @@ -83,7 +83,7 @@ impl PostgresArgs { /// we will use that rather than `self.postgres_database`. If `external` is true, /// it will give you the string for connecting from the host. If it is false, it /// will give you the string for connecting from another container in the network - /// we create for all containers in the localnet. + /// we create for all containers in the local testnet. pub fn get_connection_string(&self, database: Option<&str>, external: bool) -> String { let password = match self.use_host_postgres { true => match &self.host_postgres_password { @@ -119,7 +119,7 @@ pub struct PostgresManager { } impl PostgresManager { - pub fn new(args: &RunLocalnet, test_dir: PathBuf) -> Result { + pub fn new(args: &RunLocalTestnet, test_dir: PathBuf) -> Result { if args.postgres_args.use_host_postgres && args.postgres_args.postgres_database == "postgres" { @@ -271,23 +271,6 @@ impl ServiceManager for PostgresManager { // directory inside the container that is mounted from the host system. format!("PGDATA={}", DATA_PATH_IN_CONTAINER), ]), - cmd: Some( - vec![ - "postgres", - "-c", - // The default is 100 as of Postgres 14.11. Given the localnet - // can be composed of many different processors all with their own - // connection pools, 100 is insufficient. - "max_connections=200", - "-c", - // The default is 128MB as of Postgres 14.11. We 2x that value to - // match the fact that we 2x'd max_connections. - "shared_buffers=256MB", - ] - .into_iter() - .map(|s| s.to_string()) - .collect(), - ), ..Default::default() }; diff --git a/crates/aptos/src/node/local_testnet/processors.rs b/crates/aptos/src/node/local_testnet/processors.rs index 40f5552e179d7..7359514de1f88 100644 --- a/crates/aptos/src/node/local_testnet/processors.rs +++ b/crates/aptos/src/node/local_testnet/processors.rs @@ -1,19 +1,14 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalnet}; +use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; use anyhow::{bail, Context, Result}; use async_trait::async_trait; use clap::Parser; -use diesel::Connection; -use diesel_async::{async_connection_wrapper::AsyncConnectionWrapper, pg::AsyncPgConnection}; +use diesel_async::{pg::AsyncPgConnection, AsyncConnection}; use maplit::hashset; use processor::{ - processors::{ - objects_processor::ObjectsProcessorConfig, stake_processor::StakeProcessorConfig, - token_processor::TokenProcessorConfig, token_v2_processor::TokenV2ProcessorConfig, - ProcessorConfig, ProcessorName, - }, + processors::{token_processor::TokenProcessorConfig, ProcessorConfig, ProcessorName}, utils::database::run_pending_migrations, IndexerGrpcProcessorConfig, }; @@ -30,7 +25,7 @@ static RUN_MIGRATIONS_ONCE: OnceCell = OnceCell::const_new(); pub struct ProcessorArgs { /// The value of this flag determines which processors we will run if /// --with-indexer-api is set. Note that some processors are not supported in the - /// localnet (e.g. ANS). If you try to set those an error will be thrown + /// local testnet (e.g. ANS). If you try to set those an error will be thrown /// immediately. #[clap( long, @@ -41,11 +36,9 @@ pub struct ProcessorArgs { ProcessorName::DefaultProcessor, ProcessorName::EventsProcessor, ProcessorName::FungibleAssetProcessor, - ProcessorName::ObjectsProcessor, ProcessorName::StakeProcessor, ProcessorName::TokenProcessor, ProcessorName::TokenV2Processor, - ProcessorName::TransactionMetadataProcessor, ProcessorName::UserTransactionProcessor, ], requires = "with_indexer_api" @@ -71,47 +64,23 @@ impl ProcessorManager { ProcessorConfig::AccountTransactionsProcessor }, ProcessorName::AnsProcessor => { - bail!("ANS processor is not supported in the localnet") + bail!("ANS processor is not supported in the local testnet") }, ProcessorName::CoinProcessor => ProcessorConfig::CoinProcessor, ProcessorName::DefaultProcessor => ProcessorConfig::DefaultProcessor, ProcessorName::EventsProcessor => ProcessorConfig::EventsProcessor, ProcessorName::FungibleAssetProcessor => ProcessorConfig::FungibleAssetProcessor, - ProcessorName::MonitoringProcessor => { - bail!("Monitoring processor is not supported in the localnet") - }, ProcessorName::NftMetadataProcessor => { - bail!("NFT Metadata processor is not supported in the localnet") - }, - ProcessorName::ObjectsProcessor => { - ProcessorConfig::ObjectsProcessor(ObjectsProcessorConfig { - query_retries: Default::default(), - query_retry_delay_ms: Default::default(), - }) - }, - ProcessorName::StakeProcessor => { - ProcessorConfig::StakeProcessor(StakeProcessorConfig { - query_retries: Default::default(), - query_retry_delay_ms: Default::default(), - }) + bail!("NFT Metadata processor is not supported in the local testnet") }, + ProcessorName::StakeProcessor => ProcessorConfig::StakeProcessor, ProcessorName::TokenProcessor => { ProcessorConfig::TokenProcessor(TokenProcessorConfig { - // This NFT points contract doesn't exist on localnets. + // This NFT points contract doesn't exist on local testnets. nft_points_contract: None, - query_retries: Default::default(), - query_retry_delay_ms: Default::default(), - }) - }, - ProcessorName::TokenV2Processor => { - ProcessorConfig::TokenV2Processor(TokenV2ProcessorConfig { - query_retries: Default::default(), - query_retry_delay_ms: Default::default(), }) }, - ProcessorName::TransactionMetadataProcessor => { - ProcessorConfig::TransactionMetadataProcessor - }, + ProcessorName::TokenV2Processor => ProcessorConfig::TokenV2Processor, ProcessorName::UserTransactionProcessor => ProcessorConfig::UserTransactionProcessor, }; let config = IndexerGrpcProcessorConfig { @@ -124,14 +93,6 @@ impl ProcessorManager { ending_version: None, number_concurrent_processing_tasks: None, enable_verbose_logging: None, - // The default at the time of writing is 30 but we don't need that - // many in a localnet environment. - db_pool_size: Some(8), - gap_detection_batch_size: 50, - pb_channel_txn_chunk_size: 100_000, - per_table_chunk_sizes: Default::default(), - transaction_filter: Default::default(), - grpc_response_item_timeout_in_secs: 10, }; let manager = Self { config, @@ -142,7 +103,7 @@ impl ProcessorManager { /// This function returns many new ProcessorManagers, one for each processor. pub fn many_new( - args: &RunLocalnet, + args: &RunLocalTestnet, prerequisite_health_checkers: HashSet, data_service_url: Url, postgres_connection_string: String, @@ -164,18 +125,11 @@ impl ProcessorManager { /// Create the necessary tables in the DB for the processors to work. async fn run_migrations(&self) -> Result<()> { - let connection_string = self.config.postgres_connection_string.clone(); - tokio::task::spawn_blocking(move || { - // This lets us use the connection like a normal diesel connection. See more: - // https://docs.rs/diesel-async/latest/diesel_async/async_connection_wrapper/type.AsyncConnectionWrapper.html - let mut conn: AsyncConnectionWrapper = - AsyncConnectionWrapper::establish(&connection_string).with_context(|| { - format!("Failed to connect to postgres at {}", connection_string) - })?; - run_pending_migrations(&mut conn); - anyhow::Ok(()) - }) - .await??; + let connection_string = &self.config.postgres_connection_string; + let mut connection = AsyncPgConnection::establish(connection_string) + .await + .with_context(|| format!("Failed to connect to postgres at {}", connection_string))?; + run_pending_migrations(&mut connection).await; Ok(()) } } diff --git a/crates/aptos/src/node/local_testnet/ready_server.rs b/crates/aptos/src/node/local_testnet/ready_server.rs index 632e4404725c2..3acdc0a656920 100644 --- a/crates/aptos/src/node/local_testnet/ready_server.rs +++ b/crates/aptos/src/node/local_testnet/ready_server.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalnet}; +use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; use anyhow::Result; use async_trait::async_trait; use clap::Parser; @@ -22,13 +22,13 @@ use std::{ }; use tokio::time::timeout; -/// Args related to running a ready server in the localnet. The ready server lets -/// users / clients check that if all the services in the localnet are ready +/// Args related to running a ready server in the local testnet. The ready server lets +/// users / clients check that if all the services in the local testnet are ready /// without having to ping each service individually. #[derive(Debug, Clone, Parser)] pub struct ReadyServerArgs { /// The port to run the ready server. This exposes an endpoint at `/` that you can - /// use to check if the entire localnet is ready. + /// use to check if the entire local testnet is ready. #[clap(long, default_value_t = 8070)] pub ready_server_listen_port: u16, } @@ -42,7 +42,7 @@ pub struct ReadyServerManager { impl ReadyServerManager { pub fn new( - args: &RunLocalnet, + args: &RunLocalTestnet, bind_to: Ipv4Addr, health_checkers: HashSet, ) -> Result { diff --git a/crates/aptos/src/node/local_testnet/traits.rs b/crates/aptos/src/node/local_testnet/traits.rs new file mode 100644 index 0000000000000..45b552b6cdad3 --- /dev/null +++ b/crates/aptos/src/node/local_testnet/traits.rs @@ -0,0 +1,106 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::health_checker::HealthChecker; +use anyhow::{Context, Result}; +use async_trait::async_trait; +use std::{collections::HashSet, fmt::Debug}; +use tracing::warn; + +#[async_trait] +pub trait ServiceManager: Debug + Send + Sync + 'static { + /// Pretty name that we will show to the user for updates about this service. + fn get_name(&self) -> String; + + /// This is called before the service is run. This is a good place to do any + /// setup that needs to be done before the service is run. + async fn pre_run(&self) -> Result<()> { + Ok(()) + } + + /// All services should expose some way to check if they are healthy. This function + /// returns HealthCheckers, a struct that serves this purpose, that later services + /// can use to make sure prerequisite services have started. These are also used + /// by the "ready server", a server that exposes a unified endpoint for checking + /// if all services are ready. + fn get_health_checkers(&self) -> HashSet; + + /// Whereas get_health_checkers returns healthchecks that other downstream services + /// can use, this should return health checkers for services that this service is + /// waiting to start. + // + // Note: If we were using an object oriented language, we'd just make the + // constructor of the superclass require a vec of health checkers. Unfortunately + // we can't do that here, hence this runaround where the trait implementer must + // individually handle accepting health checkers and exposing them here. Similarly, + // if we could make this function private we would, but we can't since right now + // all functions in a trait must be pub. + fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker>; + + /// This is the function we use from the outside to start the service. It makes + /// sure all the prerequisite services have started and then calls the inner + /// function to run the service. The user should never need to override this + /// implementation. + async fn run(self: Box) -> Result<()> { + // We make a new function here so that each task waits for its prereqs within + // its own run function. This way we can start each service in any order. + let name = self.get_name(); + let name_clone = name.to_string(); + for health_checker in self.get_prerequisite_health_checkers() { + health_checker + .wait(Some(&self.get_name())) + .await + .context("Prerequisite service did not start up successfully")?; + } + self.run_service() + .await + .context("Service ended with an error")?; + warn!( + "Service {} ended unexpectedly without any error", + name_clone + ); + Ok(()) + } + + /// The ServiceManager may return PostHealthySteps. The tool will run these after + /// the service is started. See `PostHealthyStep` for more information. + // + // You might ask, why not just have a `post_healthy` function? The problem is we + // want `run` to take `self` so the implementer doesn't have to worry about making + // their config Clone, so after that point the ServiceManager won't exist. Hence + // this model. + fn get_post_healthy_steps(&self) -> Vec> { + vec![] + } + + /// The ServiceManager may return ShutdownSteps. The tool will run these on shutdown. + /// This is best effort, there is nothing we can do if part of the code aborts or + /// the process receives something like SIGKILL. + /// + /// See `ShutdownStep` for more information. + fn get_shutdown_steps(&self) -> Vec> { + vec![] + } + + /// This function is responsible for running the service. It should return an error + /// if the service ends unexpectedly. It gets called by `run`. + async fn run_service(self: Box) -> Result<()>; +} + +/// If a service wants to do something after it is healthy, it can define a struct, +/// implement this trait for it, and return an instance of it. +/// +/// For more information see `get_post_healthy_steps` in `ServiceManager`. +#[async_trait] +pub trait PostHealthyStep: Debug + Send + Sync + 'static { + async fn run(self: Box) -> Result<()>; +} + +/// If a service wants to do something on shutdown, it can define a struct, +/// implement this trait for it, and return an instance of it. +/// +/// For more information see `get_shutdown_steps` in `ServiceManager`. +#[async_trait] +pub trait ShutdownStep: Debug + Send + Sync + 'static { + async fn run(self: Box) -> Result<()>; +} diff --git a/crates/aptos/src/node/local_testnet/utils.rs b/crates/aptos/src/node/local_testnet/utils.rs new file mode 100644 index 0000000000000..58e0a7808c24e --- /dev/null +++ b/crates/aptos/src/node/local_testnet/utils.rs @@ -0,0 +1,15 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use reqwest::Url; +use std::net::SocketAddr; + +pub fn socket_addr_to_url(socket_addr: &SocketAddr, scheme: &str) -> Result { + let host = match socket_addr { + SocketAddr::V4(v4) => format!("{}", v4.ip()), + SocketAddr::V6(v6) => format!("[{}]", v6.ip()), + }; + let full_url = format!("{}://{}:{}", scheme, host, socket_addr.port()); + Ok(Url::parse(&full_url)?) +} diff --git a/crates/aptos/src/node/mod.rs b/crates/aptos/src/node/mod.rs index bfec773776ddb..83d32e501d010 100644 --- a/crates/aptos/src/node/mod.rs +++ b/crates/aptos/src/node/mod.rs @@ -4,7 +4,7 @@ pub mod analyze; pub mod local_testnet; -use self::local_testnet::RunLocalnet; +use self::local_testnet::RunLocalTestnet; use crate::{ common::{ types::{ @@ -76,8 +76,7 @@ pub enum NodeTool { ShowValidatorConfig(ShowValidatorConfig), ShowValidatorSet(ShowValidatorSet), ShowValidatorStake(ShowValidatorStake), - #[clap(aliases = &["run-local-testnet"])] - RunLocalnet(RunLocalnet), + RunLocalTestnet(RunLocalTestnet), UpdateConsensusKey(UpdateConsensusKey), UpdateValidatorNetworkAddresses(UpdateValidatorNetworkAddresses), } @@ -101,7 +100,7 @@ impl NodeTool { ShowValidatorSet(tool) => tool.execute_serialized().await, ShowValidatorStake(tool) => tool.execute_serialized().await, ShowValidatorConfig(tool) => tool.execute_serialized().await, - RunLocalnet(tool) => tool + RunLocalTestnet(tool) => tool .execute_serialized_without_logger() .await .map(|_| "".to_string()), @@ -1193,8 +1192,6 @@ pub enum AnalyzeMode { /// For each epoch summarize how many validators were in /// each of the reliability buckets. NetworkHealthOverTime, - /// Max TPS - MaxTps, } #[async_trait] @@ -1213,8 +1210,6 @@ impl CliCommand<()> for AnalyzeValidatorPerformance { let print_detailed = self.analyze_mode == AnalyzeMode::DetailedEpochTable || self.analyze_mode == AnalyzeMode::All; - let print_max_tps = - self.analyze_mode == AnalyzeMode::MaxTps || self.analyze_mode == AnalyzeMode::All; for epoch_info in epochs { let mut epoch_stats = AnalyzeValidators::analyze(&epoch_info.blocks, &epoch_info.validators); @@ -1247,21 +1242,6 @@ impl CliCommand<()> for AnalyzeValidatorPerformance { true, ); } - if print_max_tps { - for (num_blocks_for_max_tps, max_tps) in &epoch_stats.max_tps_per_block_interval { - println!( - "In {}epoch {}: during consecutive {:?}, found peak of {} TPS, ending on version: {}, {} txns over {}s and {} blocks", - if epoch_info.partial { "partial " } else { "" }, - epoch_info.epoch, - num_blocks_for_max_tps, - max_tps.tps, - max_tps.end_version, - max_tps.txns, - max_tps.duration, - max_tps.blocks, - ); - } - } if !epoch_info.partial { stats.insert(epoch_info.epoch, epoch_stats); } @@ -1280,19 +1260,6 @@ impl CliCommand<()> for AnalyzeValidatorPerformance { ); AnalyzeValidators::print_detailed_epoch_table(&total_stats, None, true); } - if print_max_tps { - for (num_blocks_for_max_tps, max_tps) in &total_stats.max_tps_per_block_interval { - println!( - "Across all epochs: during consecutive {:?}, found peak of {} TPS, ending on version: {}, {} txns over {}s and {} blocks", - num_blocks_for_max_tps, - max_tps.tps, - max_tps.end_version, - max_tps.txns, - max_tps.duration, - max_tps.blocks, - ); - } - } let all_validators: Vec<_> = total_stats.validator_stats.keys().cloned().collect(); if self.analyze_mode == AnalyzeMode::ValidatorHealthOverTime || self.analyze_mode == AnalyzeMode::All diff --git a/crates/aptos/src/op/key.rs b/crates/aptos/src/op/key.rs new file mode 100644 index 0000000000000..07a65a21db2da --- /dev/null +++ b/crates/aptos/src/op/key.rs @@ -0,0 +1,398 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + account_address_from_public_key, CliError, CliTypedResult, EncodingOptions, KeyType, + RngArgs, SaveFile, + }, + utils::{ + append_file_extension, check_if_file_exists, generate_vanity_account_ed25519, + write_to_file, + }, + }, + CliCommand, CliResult, +}; +use aptos_config::config::{Peer, PeerRole}; +use aptos_crypto::{ + bls12381, ed25519, encoding_type::EncodingType, x25519, PrivateKey, ValidCryptoMaterial, +}; +use aptos_genesis::config::HostAndPort; +use aptos_types::account_address::{ + create_multisig_account_address, from_identity_public_key, AccountAddress, +}; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, +}; + +pub const PUBLIC_KEY_EXTENSION: &str = "pub"; + +/// Tool for generating, inspecting, and interacting with keys +/// +/// This tool allows users to generate and extract related information +/// with all key types used on the Aptos blockchain. +#[derive(Debug, Subcommand)] +pub enum KeyTool { + Generate(GenerateKey), + ExtractPeer(ExtractPeer), +} + +impl KeyTool { + pub async fn execute(self) -> CliResult { + match self { + KeyTool::Generate(tool) => tool.execute_serialized().await, + KeyTool::ExtractPeer(tool) => tool.execute_serialized().await, + } + } +} + +/// Extract full peer information for an upstream peer +/// +/// This command builds a YAML blob that can be copied into a user's network configuration. +/// A host is required to build the network address used for the connection, and the +/// network key is required to identify the peer. +/// +/// A `private-network-key` or `public-network-key` can be given encoded on the command line, or +/// a `private-network-key-file` or a `public-network-key-file` can be given to read from. +/// The `output-file` will be a YAML serialized peer information for use in network config. +#[derive(Debug, Parser)] +pub struct ExtractPeer { + /// Host and port of the full node + /// + /// e.g. 127.0.0.1:6180 or my-awesome-dns.com:6180 + #[clap(long)] + pub(crate) host: HostAndPort, + + #[clap(flatten)] + pub(crate) network_key_input_options: NetworkKeyInputOptions, + #[clap(flatten)] + pub(crate) output_file_options: SaveFile, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, +} + +#[async_trait] +impl CliCommand> for ExtractPeer { + fn command_name(&self) -> &'static str { + "ExtractPeer" + } + + async fn execute(self) -> CliTypedResult> { + // Load key based on public or private + let public_key = self + .network_key_input_options + .extract_public_network_key(self.encoding_options.encoding)?; + + // Check output file exists + self.output_file_options.check_file()?; + + // Build peer info + let peer_id = from_identity_public_key(public_key); + let mut public_keys = HashSet::new(); + public_keys.insert(public_key); + + let address = self.host.as_network_address(public_key).map_err(|err| { + CliError::UnexpectedError(format!("Failed to build network address: {}", err)) + })?; + + let peer = Peer::new(vec![address], public_keys, PeerRole::Upstream); + + let mut map = HashMap::new(); + map.insert(peer_id, peer); + + // Save to file + let yaml = serde_yaml::to_string(&map) + .map_err(|err| CliError::UnexpectedError(err.to_string()))?; + self.output_file_options + .save_to_file("Extracted peer", yaml.as_bytes())?; + Ok(map) + } +} + +#[derive(Debug, Default, Parser)] +pub struct NetworkKeyInputOptions { + /// x25519 Private key input file name + #[clap(long, group = "network_key_input", value_parser)] + private_network_key_file: Option, + + /// x25519 Private key encoded in a type as shown in `encoding` + #[clap(long, group = "network_key_input")] + private_network_key: Option, + + /// x25519 Public key input file name + #[clap(long, group = "network_key_input", value_parser)] + public_network_key_file: Option, + + /// x25519 Public key encoded in a type as shown in `encoding` + #[clap(long, group = "network_key_input")] + public_network_key: Option, +} + +impl NetworkKeyInputOptions { + pub fn from_private_key_file(file: PathBuf) -> Self { + Self { + private_network_key_file: Some(file), + private_network_key: None, + public_network_key_file: None, + public_network_key: None, + } + } + + pub fn extract_public_network_key( + self, + encoding: EncodingType, + ) -> CliTypedResult { + // The grouping above prevents there from being more than one, but just in case + match (self.public_network_key, self.public_network_key_file, self.private_network_key, self.private_network_key_file){ + (Some(public_network_key), None, None, None) => Ok(encoding.decode_key("--public-network-key", public_network_key.as_bytes().to_vec())?), + (None, Some(public_network_key_file),None, None) => Ok(encoding.load_key("--public-network-key-file", public_network_key_file.as_path())?), + (None, None, Some(private_network_key), None) => { + let private_network_key: x25519::PrivateKey = encoding.decode_key("--private-network-key", private_network_key.as_bytes().to_vec())?; + Ok(private_network_key.public_key()) + }, + (None, None, None, Some(private_network_key_file)) => { + let private_network_key: x25519::PrivateKey = encoding.load_key("--private-network-key-file", private_network_key_file.as_path())?; + Ok(private_network_key.public_key()) + }, + _ => Err(CliError::CommandArgumentError("Must provide exactly one of [--public-network-key, --public-network-key-file, --private-network-key, --private-network-key-file]".to_string())) + } + } +} + +/// Generates a `x25519` or `ed25519` key. +/// +/// This can be used for generating an identity. Two files will be created +/// `output_file` and `output_file.pub`. `output_file` will contain the private +/// key encoded with the `encoding` and `output_file.pub` will contain the public +/// key encoded with the `encoding`. +#[derive(Debug, Parser)] +pub struct GenerateKey { + /// Key type to generate. Must be one of [x25519, ed25519, bls12381] + #[clap(long, default_value_t = KeyType::Ed25519)] + pub(crate) key_type: KeyType, + /// Vanity prefix that resultant account address should start with, e.g. 0xaceface or d00d. Each + /// additional character multiplies by a factor of 16 the computational difficulty associated + /// with generating an address, so try out shorter prefixes first and be prepared to wait for + /// longer ones + #[clap(long)] + pub vanity_prefix: Option, + /// Use this flag when vanity prefix is for a multisig account. This mines a private key for + /// a single signer account that can, as its first transaction, create a multisig account with + /// the given vanity prefix + #[clap(long)] + pub vanity_multisig: bool, + #[clap(flatten)] + pub rng_args: RngArgs, + #[clap(flatten)] + pub(crate) save_params: SaveKey, +} + +#[async_trait] +impl CliCommand> for GenerateKey { + fn command_name(&self) -> &'static str { + "GenerateKey" + } + + async fn execute(self) -> CliTypedResult> { + if self.vanity_prefix.is_some() && !matches!(self.key_type, KeyType::Ed25519) { + return Err(CliError::CommandArgumentError(format!( + "Vanity prefixes are only accepted for {} keys", + KeyType::Ed25519 + ))); + } + if self.vanity_multisig && self.vanity_prefix.is_none() { + return Err(CliError::CommandArgumentError( + "No vanity prefix provided".to_string(), + )); + } + self.save_params.check_key_file()?; + let mut keygen = self.rng_args.key_generator()?; + match self.key_type { + KeyType::X25519 => { + let private_key = keygen.generate_x25519_private_key().map_err(|err| { + CliError::UnexpectedError(format!( + "Failed to convert ed25519 to x25519 {:?}", + err + )) + })?; + self.save_params.save_key(&private_key, "x25519") + }, + KeyType::Ed25519 => { + // If no vanity prefix specified, generate a standard Ed25519 private key. + let private_key = if self.vanity_prefix.is_none() { + keygen.generate_ed25519_private_key() + } else { + // If a vanity prefix is specified, generate vanity Ed25519 account from it. + generate_vanity_account_ed25519( + self.vanity_prefix.clone().unwrap().as_str(), + self.vanity_multisig, + )? + }; + // Store CLI result from key save operation, to append vanity address(es) if needed. + let mut result_map = self.save_params.save_key(&private_key, "ed25519").unwrap(); + if self.vanity_prefix.is_some() { + let account_address = account_address_from_public_key( + &ed25519::Ed25519PublicKey::from(&private_key), + ); + // Store account address in a PathBuf so it can be displayed in CLI result. + result_map.insert( + "Account Address:", + PathBuf::from(account_address.to_hex_literal()), + ); + if self.vanity_multisig { + let multisig_account_address = + create_multisig_account_address(account_address, 0); + result_map.insert( + "Multisig Account Address:", + PathBuf::from(multisig_account_address.to_hex_literal()), + ); + } + } + return Ok(result_map); + }, + KeyType::Bls12381 => { + let private_key = keygen.generate_bls12381_private_key(); + self.save_params.save_bls_key(&private_key, "bls12381") + }, + } + } +} + +impl GenerateKey { + /// A test friendly typed key generation for x25519 keys. + pub async fn generate_x25519( + encoding: EncodingType, + key_file: &Path, + ) -> CliTypedResult<(x25519::PrivateKey, x25519::PublicKey)> { + let args = format!( + "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", + key_type = KeyType::X25519, + key_file = key_file.display(), + encoding = encoding, + ); + let command = GenerateKey::parse_from(args.split_whitespace()); + command.execute().await?; + Ok(( + encoding.load_key("private_key", key_file)?, + encoding.load_key( + "public_key", + &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, + )?, + )) + } + + /// A test friendly typed key generation for e25519 keys. + pub async fn generate_ed25519( + encoding: EncodingType, + key_file: &Path, + ) -> CliTypedResult<(ed25519::Ed25519PrivateKey, ed25519::Ed25519PublicKey)> { + let args = format!( + "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", + key_type = KeyType::Ed25519, + key_file = key_file.display(), + encoding = encoding, + ); + let command = GenerateKey::parse_from(args.split_whitespace()); + command.execute().await?; + Ok(( + encoding.load_key("private_key", key_file)?, + encoding.load_key( + "public_key", + &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, + )?, + )) + } +} + +#[derive(Debug, Parser)] +pub struct SaveKey { + #[clap(flatten)] + pub(crate) file_options: SaveFile, + #[clap(flatten)] + pub(crate) encoding_options: EncodingOptions, +} + +impl SaveKey { + /// Public key file name + fn public_key_file(&self) -> CliTypedResult { + append_file_extension( + self.file_options.output_file.as_path(), + PUBLIC_KEY_EXTENSION, + ) + } + + /// Public key file name + fn proof_of_possession_file(&self) -> CliTypedResult { + append_file_extension(self.file_options.output_file.as_path(), "pop") + } + + /// Check if the key file exists already + pub fn check_key_file(&self) -> CliTypedResult<()> { + // Check if file already exists + self.file_options.check_file()?; + check_if_file_exists(&self.public_key_file()?, self.file_options.prompt_options) + } + + /// Saves a key to a file encoded in a string + pub fn save_key( + self, + key: &Key, + key_name: &'static str, + ) -> CliTypedResult> { + let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; + let encoded_public_key = self + .encoding_options + .encoding + .encode_key(key_name, &key.public_key())?; + + // Write private and public keys to files + let public_key_file = self.public_key_file()?; + self.file_options + .save_to_file_confidential(key_name, &encoded_private_key)?; + write_to_file(&public_key_file, key_name, &encoded_public_key)?; + + let mut map = HashMap::new(); + map.insert("PrivateKey Path", self.file_options.output_file); + map.insert("PublicKey Path", public_key_file); + Ok(map) + } + + /// Saves a key to a file encoded in a string + pub fn save_bls_key( + self, + key: &bls12381::PrivateKey, + key_name: &'static str, + ) -> CliTypedResult> { + let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; + let encoded_public_key = self + .encoding_options + .encoding + .encode_key(key_name, &key.public_key())?; + let encoded_proof_of_posession = self + .encoding_options + .encoding + .encode_key(key_name, &bls12381::ProofOfPossession::create(key))?; + + // Write private and public keys to files + let public_key_file = self.public_key_file()?; + let proof_of_possession_file = self.proof_of_possession_file()?; + self.file_options + .save_to_file_confidential(key_name, &encoded_private_key)?; + write_to_file(&public_key_file, key_name, &encoded_public_key)?; + write_to_file( + &proof_of_possession_file, + key_name, + &encoded_proof_of_posession, + )?; + + let mut map = HashMap::new(); + map.insert("PrivateKey Path", self.file_options.output_file); + map.insert("PublicKey Path", public_key_file); + map.insert("Proof of possession Path", proof_of_possession_file); + Ok(map) + } +} diff --git a/crates/aptos/src/op/mod.rs b/crates/aptos/src/op/mod.rs new file mode 100644 index 0000000000000..989a2bb8e32fb --- /dev/null +++ b/crates/aptos/src/op/mod.rs @@ -0,0 +1,4 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod key; diff --git a/crates/aptos/src/stake/mod.rs b/crates/aptos/src/stake/mod.rs new file mode 100644 index 0000000000000..c2fcd8a37957a --- /dev/null +++ b/crates/aptos/src/stake/mod.rs @@ -0,0 +1,668 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{ + CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions, TransactionSummary, + }, + utils::prompt_yes_with_override, + }, + node::{get_stake_pools, StakePoolType}, +}; +use aptos_cached_packages::aptos_stdlib; +use aptos_types::{ + account_address::{ + create_vesting_contract_address, default_stake_pool_address, AccountAddress, + }, + vesting::VestingAdminStore, +}; +use async_trait::async_trait; +use clap::Parser; + +/// Tool for manipulating stake and stake pools +/// +#[derive(Parser)] +pub enum StakeTool { + AddStake(AddStake), + CreateStakingContract(CreateStakingContract), + DistributeVestedCoins(DistributeVestedCoins), + IncreaseLockup(IncreaseLockup), + InitializeStakeOwner(InitializeStakeOwner), + RequestCommission(RequestCommission), + SetDelegatedVoter(SetDelegatedVoter), + SetOperator(SetOperator), + UnlockStake(UnlockStake), + UnlockVestedCoins(UnlockVestedCoins), + WithdrawStake(WithdrawStake), +} + +impl StakeTool { + pub async fn execute(self) -> CliResult { + use StakeTool::*; + match self { + AddStake(tool) => tool.execute_serialized().await, + CreateStakingContract(tool) => tool.execute_serialized().await, + DistributeVestedCoins(tool) => tool.execute_serialized().await, + IncreaseLockup(tool) => tool.execute_serialized().await, + InitializeStakeOwner(tool) => tool.execute_serialized().await, + RequestCommission(tool) => tool.execute_serialized().await, + SetDelegatedVoter(tool) => tool.execute_serialized().await, + SetOperator(tool) => tool.execute_serialized().await, + UnlockStake(tool) => tool.execute_serialized().await, + UnlockVestedCoins(tool) => tool.execute_serialized().await, + WithdrawStake(tool) => tool.execute_serialized().await, + } + } +} + +/// Add APT to a stake pool +/// +/// This command allows stake pool owners to add APT to their stake. +#[derive(Parser)] +pub struct AddStake { + /// Amount of Octas (10^-8 APT) to add to stake + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for AddStake { + fn command_name(&self) -> &'static str { + "AddStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let amount = self.amount; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_add_stake(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_add_stake( + stake_pool.operator_address, + amount, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Adding stake is not supported for vesting contracts".into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Unlock staked APT in a stake pool +/// +/// APT coins can only be unlocked if they no longer have an applied lockup period +#[derive(Parser)] +pub struct UnlockStake { + /// Amount of Octas (10^-8 APT) to unlock + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for UnlockStake { + fn command_name(&self) -> &'static str { + "UnlockStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let amount = self.amount; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_unlock(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_unlock_stake( + stake_pool.operator_address, + amount, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Unlocking stake is not supported for vesting contracts".into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Withdraw unlocked staked APT from a stake pool +/// +/// This allows users to withdraw stake back into their CoinStore. +/// Before calling `WithdrawStake`, `UnlockStake` must be called first. +#[derive(Parser)] +pub struct WithdrawStake { + /// Amount of Octas (10^-8 APT) to withdraw. + /// This only applies to stake pools owned directly by the owner account, instead of via + /// a staking contract. In the latter case, when withdrawal is issued, all coins are distributed + #[clap(long)] + pub amount: u64, + + #[clap(flatten)] + pub(crate) node_op_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for WithdrawStake { + fn command_name(&self) -> &'static str { + "WithdrawStake" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .node_op_options + .rest_options + .client(&self.node_op_options.profile_options)?; + let amount = self.amount; + let owner_address = self.node_op_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.node_op_options + .submit_transaction(aptos_stdlib::stake_withdraw(amount)) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.node_op_options + .submit_transaction(aptos_stdlib::staking_contract_distribute( + owner_address, + stake_pool.operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + return Err(CliError::UnexpectedError( + "Stake withdrawal from vesting contract should use distribute-vested-coins" + .into(), + )) + }, + } + } + Ok(transaction_summaries) + } +} + +/// Increase lockup of all staked APT in a stake pool +/// +/// Lockup may need to be increased in order to vote on a proposal. +#[derive(Parser)] +pub struct IncreaseLockup { + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for IncreaseLockup { + fn command_name(&self) -> &'static str { + "IncreaseLockup" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_increase_lockup()) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_reset_lockup( + stake_pool.operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::vesting_reset_lockup( + stake_pool.vesting_contract.unwrap(), + )) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Initialize a stake pool owner +/// +/// Initializing stake owner adds the capability to delegate the +/// stake pool to an operator, or delegate voting to a different account. +#[derive(Parser)] +pub struct InitializeStakeOwner { + /// Initial amount of Octas (10^-8 APT) to be staked + #[clap(long)] + pub initial_stake_amount: u64, + + /// Account Address of delegated operator + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub operator_address: Option, + + /// Account address of delegated voter + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub voter_address: Option, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for InitializeStakeOwner { + fn command_name(&self) -> &'static str { + "InitializeStakeOwner" + } + + async fn execute(mut self) -> CliTypedResult { + let owner_address = self.txn_options.sender_address()?; + self.txn_options + .submit_transaction(aptos_stdlib::stake_initialize_stake_owner( + self.initial_stake_amount, + self.operator_address.unwrap_or(owner_address), + self.voter_address.unwrap_or(owner_address), + )) + .await + .map(|inner| inner.into()) + } +} + +/// Delegate operator capability to another account +/// +/// This changes teh operator capability from its current operator to a different operator. +/// By default, the operator of a stake pool is the owner of the stake pool +#[derive(Parser)] +pub struct SetOperator { + /// Account Address of delegated operator + /// + /// If not specified, it will be the same as the owner + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub operator_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for SetOperator { + fn command_name(&self) -> &'static str { + "SetOperator" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let new_operator_address = self.operator_address; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_set_operator( + new_operator_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction( + aptos_stdlib::staking_contract_switch_operator_with_same_commission( + stake_pool.operator_address, + new_operator_address, + ), + ) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction( + aptos_stdlib::vesting_update_operator_with_same_commission( + stake_pool.vesting_contract.unwrap(), + new_operator_address, + ), + ) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Delegate voting capability to another account +/// +/// Delegates voting capability from its current voter to a different voter. +/// By default, the voter of a stake pool is the owner of the stake pool +#[derive(Parser)] +pub struct SetDelegatedVoter { + /// Account Address of delegated voter + /// + /// If not specified, it will be the same as the owner + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub voter_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand> for SetDelegatedVoter { + fn command_name(&self) -> &'static str { + "SetDelegatedVoter" + } + + async fn execute(mut self) -> CliTypedResult> { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + let owner_address = self.txn_options.sender_address()?; + let new_voter_address = self.voter_address; + let mut transaction_summaries: Vec = vec![]; + + let stake_pool_results = get_stake_pools(&client, owner_address).await?; + for stake_pool in stake_pool_results { + match stake_pool.pool_type { + StakePoolType::Direct => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::stake_set_delegated_voter( + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::StakingContract => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_update_voter( + stake_pool.operator_address, + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + StakePoolType::Vesting => { + transaction_summaries.push( + self.txn_options + .submit_transaction(aptos_stdlib::vesting_update_voter( + stake_pool.vesting_contract.unwrap(), + new_voter_address, + )) + .await + .map(|inner| inner.into())?, + ); + }, + } + } + Ok(transaction_summaries) + } +} + +/// Create a staking contract stake pool +/// +/// +#[derive(Parser)] +pub struct CreateStakingContract { + /// Account Address of operator + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub operator: AccountAddress, + + /// Account Address of delegated voter + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub voter: AccountAddress, + + /// Amount to create the staking contract with + #[clap(long)] + pub amount: u64, + + /// Percentage of accumulated rewards to pay the operator as commission + #[clap(long)] + pub commission_percentage: u64, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for CreateStakingContract { + fn command_name(&self) -> &'static str { + "CreateStakingContract" + } + + async fn execute(mut self) -> CliTypedResult { + let pool_address = default_stake_pool_address( + self.txn_options.profile_options.account_address()?, + self.operator, + ); + prompt_yes_with_override( + &format!( + "Creating a new staking contract with pool address 0x{}. Confirm?", + pool_address + ), + self.txn_options.prompt_options, + )?; + + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract( + self.operator, + self.voter, + self.amount, + self.commission_percentage, + vec![], + )) + .await + .map(|inner| inner.into()) + } +} + +/// Distribute fully unlocked coins from vesting +/// +/// Distribute fully unlocked coins (rewards and/or vested coins) from the vesting contract +/// to shareholders. +#[derive(Parser)] +pub struct DistributeVestedCoins { + /// Address of the vesting contract's admin. + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub admin_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for DistributeVestedCoins { + fn command_name(&self) -> &'static str { + "DistributeVestedCoins" + } + + async fn execute(mut self) -> CliTypedResult { + let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); + self.txn_options + .submit_transaction(aptos_stdlib::vesting_distribute(vesting_contract_address)) + .await + .map(|inner| inner.into()) + } +} + +/// Unlock vested coins +/// +/// Unlock vested coins according to the vesting contract's schedule. +/// This also unlocks any accumulated staking rewards and pays commission to the operator of the +/// vesting contract's stake pool first. +/// +/// The unlocked vested tokens and staking rewards are still subject to the staking lockup and +/// cannot be withdrawn until after the lockup expires. +#[derive(Parser)] +pub struct UnlockVestedCoins { + /// Address of the vesting contract's admin. + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub admin_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for UnlockVestedCoins { + fn command_name(&self) -> &'static str { + "UnlockVestedCoins" + } + + async fn execute(mut self) -> CliTypedResult { + let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); + self.txn_options + .submit_transaction(aptos_stdlib::vesting_vest(vesting_contract_address)) + .await + .map(|inner| inner.into()) + } +} + +/// Request commission from running a stake pool +/// +/// Allows operators or owners to request commission from running a stake pool (only if there's a +/// staking contract set up with the staker). The commission will be withdrawable at the end of the +/// stake pool's current lockup period. +#[derive(Parser)] +pub struct RequestCommission { + /// Address of the owner of the stake pool + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub owner_address: AccountAddress, + + /// Address of the operator of the stake pool + #[clap(long, value_parser = crate::common::types::load_account_arg)] + pub operator_address: AccountAddress, + + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, +} + +#[async_trait] +impl CliCommand for RequestCommission { + fn command_name(&self) -> &'static str { + "RequestCommission" + } + + async fn execute(mut self) -> CliTypedResult { + let client = self + .txn_options + .rest_options + .client(&self.txn_options.profile_options)?; + + // If this is a vesting stake pool, retrieve the associated vesting contract + let vesting_admin_store = client + .get_account_resource_bcs::( + self.owner_address, + "0x1::vesting::AdminStore", + ) + .await; + + // Note: this only works if the vesting contract has exactly one staking contract + // associated + let staker_address = if let Ok(vesting_admin_store) = vesting_admin_store { + vesting_admin_store.into_inner().vesting_contracts[0] + } else { + self.owner_address + }; + self.txn_options + .submit_transaction(aptos_stdlib::staking_contract_request_commission( + staker_address, + self.operator_address, + )) + .await + .map(|inner| inner.into()) + } +} diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs index e1ac8e8adea69..fb55714bee907 100644 --- a/crates/aptos/src/test/mod.rs +++ b/crates/aptos/src/test/mod.rs @@ -15,9 +15,9 @@ use crate::{ account_address_from_public_key, AccountAddressWrapper, ArgWithTypeVec, AuthenticationKeyInputOptions, CliError, CliTypedResult, EncodingOptions, EntryFunctionArguments, FaucetOptions, GasOptions, KeyType, MoveManifestAccountWrapper, - MovePackageDir, OptionalPoolAddressArgs, OverrideSizeCheckOption, PoolAddressArgs, - PrivateKeyInputOptions, PromptOptions, PublicKeyInputOptions, RestOptions, RngArgs, - SaveFile, ScriptFunctionArguments, TransactionOptions, TransactionSummary, TypeArgVec, + MovePackageDir, OptionalPoolAddressArgs, PoolAddressArgs, PrivateKeyInputOptions, + PromptOptions, PublicKeyInputOptions, RestOptions, RngArgs, SaveFile, + ScriptFunctionArguments, TransactionOptions, TransactionSummary, TypeArgVec, }, utils::write_to_file, }, @@ -72,7 +72,6 @@ use std::{ time::Duration, }; use tempfile::TempDir; -use thiserror::__private::AsDisplay; use tokio::time::{sleep, Instant}; #[cfg(test)] @@ -471,16 +470,6 @@ impl CliTestFramework { .await } - pub fn add_file_in_package(&self, rel_path: &str, content: String) { - let source_path = self.move_dir().join(rel_path); - write_to_file( - source_path.as_path(), - &source_path.as_display().to_string(), - content.as_bytes(), - ) - .unwrap(); - } - pub async fn update_validator_network_addresses( &self, operator_index: usize, @@ -876,9 +865,7 @@ impl CliTestFramework { PublishPackage { move_options: self.move_options(account_strs), txn_options: self.transaction_options(index, gas_options), - override_size_check_option: OverrideSizeCheckOption { - override_size_check: false, - }, + override_size_check: false, included_artifacts_args: IncludedArtifactsArgs { included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), }, @@ -900,7 +887,6 @@ impl CliTestFramework { package, output_dir: Some(output_dir), print_metadata: false, - bytecode: true, } .execute() .await @@ -959,25 +945,6 @@ impl CliTestFramework { .await } - pub async fn run_script_with_gas_options( - &self, - index: usize, - script_contents: &str, - gas_options: Option, - ) -> CliTypedResult { - self.run_script_with_framework_package_and_gas_options( - index, - script_contents, - FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - gas_options, - ) - .await - } - /// Runs the given script contents using the aptos_framework from aptos-core git repository. pub async fn run_script_with_default_framework( &self, @@ -998,22 +965,6 @@ impl CliTestFramework { index: usize, script_contents: &str, framework_package_args: FrameworkPackageArgs, - ) -> CliTypedResult { - self.run_script_with_framework_package_and_gas_options( - index, - script_contents, - framework_package_args, - None, - ) - .await - } - - pub async fn run_script_with_framework_package_and_gas_options( - &self, - index: usize, - script_contents: &str, - framework_package_args: FrameworkPackageArgs, - gas_options: Option, ) -> CliTypedResult { // Make a temporary directory for compilation let temp_dir = TempDir::new().map_err(|err| { @@ -1029,7 +980,7 @@ impl CliTestFramework { .unwrap(); RunScript { - txn_options: self.transaction_options(index, gas_options), + txn_options: self.transaction_options(index, None), compile_proposal_args: CompileScriptFunction { script_path: Some(source_path), compiled_script_path: None, @@ -1075,7 +1026,7 @@ impl CliTestFramework { .await } - pub fn aptos_framework_dir() -> PathBuf { + fn aptos_framework_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") @@ -1090,11 +1041,9 @@ impl CliTestFramework { package_dir: Some(self.move_dir()), output_dir: None, named_addresses: Self::named_addresses(account_strs), - override_std: None, skip_fetch_latest_git_deps: true, bytecode_version: None, compiler_version: None, - language_version: None, skip_attribute_checks: false, check_test_code: false, } diff --git a/crates/aptos/src/test/tests.rs b/crates/aptos/src/test/tests.rs new file mode 100644 index 0000000000000..1a02323a2161a --- /dev/null +++ b/crates/aptos/src/test/tests.rs @@ -0,0 +1,146 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + move_tool::{ArgWithType, FunctionArgType}, + CliResult, Tool, +}; +use clap::Parser; +use std::str::FromStr; + +/// In order to ensure that there aren't duplicate input arguments for untested CLI commands, +/// we call help on every command to ensure it at least runs +#[tokio::test] +async fn ensure_every_command_args_work() { + assert_cmd_not_panic(&["aptos"]).await; + + assert_cmd_not_panic(&["aptos", "account"]).await; + assert_cmd_not_panic(&["aptos", "account", "create", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "create-resource-account", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "fund-with-faucet", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "list", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "lookup-address", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "rotate-key", "--help"]).await; + assert_cmd_not_panic(&["aptos", "account", "transfer", "--help"]).await; + + assert_cmd_not_panic(&["aptos", "config"]).await; + assert_cmd_not_panic(&["aptos", "config", "generate-shell-completions", "--help"]).await; + assert_cmd_not_panic(&["aptos", "config", "init", "--help"]).await; + assert_cmd_not_panic(&["aptos", "config", "set-global-config", "--help"]).await; + assert_cmd_not_panic(&["aptos", "config", "show-global-config"]).await; + assert_cmd_not_panic(&["aptos", "config", "show-profiles"]).await; + + assert_cmd_not_panic(&["aptos", "genesis"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "generate-genesis", "--help"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "generate-keys", "--help"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "generate-layout-template", "--help"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "set-validator-configuration", "--help"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "setup-git", "--help"]).await; + assert_cmd_not_panic(&["aptos", "genesis", "generate-admin-write-set", "--help"]).await; + + assert_cmd_not_panic(&["aptos", "governance"]).await; + assert_cmd_not_panic(&["aptos", "governance", "execute-proposal", "--help"]).await; + assert_cmd_not_panic(&["aptos", "governance", "generate-upgrade-proposal", "--help"]).await; + assert_cmd_not_panic(&["aptos", "governance", "propose", "--help"]).await; + assert_cmd_not_panic(&["aptos", "governance", "vote", "--help"]).await; + assert_cmd_not_panic(&["aptos", "governance", "delegation_pool", "--help"]).await; + assert_cmd_not_panic(&["aptos", "governance", "delegation_pool", "vote", "--help"]).await; + assert_cmd_not_panic(&[ + "aptos", + "governance", + "delegation_pool", + "propose", + "--help", + ]) + .await; + + assert_cmd_not_panic(&["aptos", "info"]).await; + + assert_cmd_not_panic(&["aptos", "init", "--help"]).await; + + assert_cmd_not_panic(&["aptos", "key"]).await; + assert_cmd_not_panic(&["aptos", "key", "generate", "--help"]).await; + assert_cmd_not_panic(&["aptos", "key", "extract-peer", "--help"]).await; + + assert_cmd_not_panic(&["aptos", "move"]).await; + assert_cmd_not_panic(&["aptos", "move", "clean", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "compile", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "compile-script", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "download", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "init", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "list", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "prove", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "publish", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "run", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "run-script", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "test", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "transactional-test", "--help"]).await; + assert_cmd_not_panic(&["aptos", "move", "view", "--help"]).await; + + assert_cmd_not_panic(&["aptos", "node"]).await; + assert_cmd_not_panic(&["aptos", "node", "check-network-connectivity", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "get-stake-pool", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "analyze-validator-performance", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "bootstrap-db-from-backup", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "initialize-validator", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "join-validator-set", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "leave-validator-set", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "run-local-testnet", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "show-validator-config", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "show-validator-set", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "show-validator-stake", "--help"]).await; + assert_cmd_not_panic(&["aptos", "node", "update-consensus-key", "--help"]).await; + assert_cmd_not_panic(&[ + "aptos", + "node", + "update-validator-network-addresses", + "--help", + ]) + .await; + + assert_cmd_not_panic(&["aptos", "stake"]).await; + assert_cmd_not_panic(&["aptos", "stake", "add-stake", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "increase-lockup", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "initialize-stake-owner", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "set-delegated-voter", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "set-operator", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "unlock-stake", "--help"]).await; + assert_cmd_not_panic(&["aptos", "stake", "withdraw-stake", "--help"]).await; +} + +/// Ensure we can parse URLs for args +#[tokio::test] +async fn ensure_can_parse_args_with_urls() { + let result = ArgWithType::from_str("string:https://aptoslabs.com").unwrap(); + matches!(result._ty, FunctionArgType::String); + assert_eq!( + result.arg, + bcs::to_bytes(&"https://aptoslabs.com".to_string()).unwrap() + ); +} + +async fn assert_cmd_not_panic(args: &[&str]) { + // When a command fails, it will have a panic in it due to an improperly setup command + // thread 'main' panicked at 'Command propose: Argument names must be unique, but 'assume-yes' is + // in use by more than one argument or group', ... + + match run_cmd(args).await { + Ok(inner) => assert!( + !inner.contains("panic"), + "Failed to not panic cmd {}: {}", + args.join(" "), + inner + ), + Err(inner) => assert!( + !inner.contains("panic"), + "Failed to not panic cmd {}: {}", + args.join(" "), + inner + ), + } +} + +async fn run_cmd(args: &[&str]) -> CliResult { + let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; + tool.execute().await +} diff --git a/crates/aptos/src/update/helpers.rs b/crates/aptos/src/update/helpers.rs new file mode 100644 index 0000000000000..5b2f7addc329a --- /dev/null +++ b/crates/aptos/src/update/helpers.rs @@ -0,0 +1,77 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{anyhow, Context, Result}; +use self_update::{backends::github::ReleaseList, cargo_crate_version, version::bump_is_greater}; + +#[derive(Debug)] +pub struct UpdateRequiredInfo { + pub update_required: bool, + pub current_version: String, + pub latest_version: String, + pub latest_version_tag: String, +} + +/// Return information about whether an update is required. +pub fn check_if_update_required(repo_owner: &str, repo_name: &str) -> Result { + // Build a configuration for determining the latest release. + let config = ReleaseList::configure() + .repo_owner(repo_owner) + .repo_name(repo_name) + .build() + .map_err(|e| anyhow!("Failed to build configuration to fetch releases: {:#}", e))?; + + // Get the most recent releases. + let releases = config + .fetch() + .map_err(|e| anyhow!("Failed to fetch releases: {:#}", e))?; + + // Find the latest release of the CLI, in which we filter for the CLI tag. + // If the release isn't in the last 30 items (the default API page size) + // this will fail. See https://github.com/aptos-labs/aptos-core/issues/6411. + let mut releases = releases.into_iter(); + let latest_release = loop { + let release = match releases.next() { + Some(release) => release, + None => return Err(anyhow!("Failed to find latest CLI release")), + }; + if release.version.starts_with("aptos-cli-") { + break release; + } + }; + let latest_version_tag = latest_release.version; + let latest_version = latest_version_tag.split("-v").last().unwrap(); + + // Return early if we're up to date already. + let current_version = cargo_crate_version!(); + let update_required = bump_is_greater(current_version, latest_version) + .context("Failed to compare current and latest CLI versions")?; + + Ok(UpdateRequiredInfo { + update_required, + current_version: current_version.to_string(), + latest_version: latest_version.to_string(), + latest_version_tag, + }) +} + +pub enum InstallationMethod { + Source, + Homebrew, + Other, +} + +impl InstallationMethod { + pub fn from_env() -> Result { + // Determine update instructions based on what we detect about the installation. + let exe_path = std::env::current_exe()?; + let installation_method = if exe_path.to_string_lossy().contains("brew") { + InstallationMethod::Homebrew + } else if exe_path.to_string_lossy().contains("target") { + InstallationMethod::Source + } else { + InstallationMethod::Other + }; + Ok(installation_method) + } +} diff --git a/crates/aptos/src/update/mod.rs b/crates/aptos/src/update/mod.rs index fe784c91efdda..17dab6d135811 100644 --- a/crates/aptos/src/update/mod.rs +++ b/crates/aptos/src/update/mod.rs @@ -1,114 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -// Note: We make use of the self_update crate, but as you can see in the case of -// Revela, this can also be used to install / update other binaries. - -mod aptos; mod helpers; -mod revela; mod tool; -use crate::common::types::CliTypedResult; -use anyhow::{anyhow, Context, Result}; -pub use helpers::get_additional_binaries_dir; -pub use revela::get_revela_path; -use self_update::{update::ReleaseUpdate, version::bump_is_greater, Status}; +use helpers::check_if_update_required; pub use tool::UpdateTool; - -/// Things that implement this trait are able to update a binary. -trait BinaryUpdater { - /// For checking the version but not updating - fn check(&self) -> bool; - - /// Only used for messages we print to the user. - fn pretty_name(&self) -> &'static str; - - /// Return information about whether an update is required. - fn get_update_info(&self) -> Result; - - /// Build the updater from the self_update crate. - fn build_updater(&self, info: &UpdateRequiredInfo) -> Result>; - - /// Update the binary. Install if not present, in the case of additional binaries - /// such as Revela. - fn update(&self) -> CliTypedResult { - // Confirm that we need to update. - let info = self - .get_update_info() - .context("Failed to check if we need to update")?; - if !info.update_required()? { - return Ok(format!("Already up to date (v{})", info.target_version)); - } - - // Build the updater. - let updater = self.build_updater(&info)?; - - // Update the binary. - let result = updater - .update() - .map_err(|e| anyhow!("Failed to update {}: {:#}", self.pretty_name(), e))?; - - let message = match result { - Status::UpToDate(_) => unreachable!("We should have caught this already"), - Status::Updated(_) => match info.current_version { - Some(current_version) => format!( - "Successfully updated {} from v{} to v{}", - self.pretty_name(), - current_version, - info.target_version - ), - None => { - format!( - "Successfully installed {} v{}", - self.pretty_name(), - info.target_version - ) - }, - }, - }; - - Ok(message) - } -} - -/// Information used to determine if an update is required. The versions given to this -/// struct should not have any prefix, it should just be the version. e.g. 2.5.0 rather -/// than aptos-cli-v2.5.0. -#[derive(Debug)] -pub struct UpdateRequiredInfo { - pub current_version: Option, - pub target_version: String, -} - -impl UpdateRequiredInfo { - pub fn update_required(&self) -> Result { - match self.current_version { - Some(ref current_version) => bump_is_greater(current_version, &self.target_version) - .context( - "Failed to compare current and latest CLI versions, please update manually", - ), - None => Ok(true), - } - } -} - -async fn update_binary( - updater: Updater, -) -> CliTypedResult { - let name = updater.pretty_name(); - if updater.check() { - let info = tokio::task::spawn_blocking(move || updater.get_update_info()) - .await - .context(format!("Failed to check {} version", name))??; - if info.current_version.unwrap_or_default() != info.target_version { - return Ok(format!("Update is available ({})", info.target_version)); - } - - return Ok(format!("Already up to date ({})", info.target_version)); - } - - tokio::task::spawn_blocking(move || updater.update()) - .await - .context(format!("Failed to install or update {}", name))? -} diff --git a/crates/aptos/src/update/tool.rs b/crates/aptos/src/update/tool.rs new file mode 100644 index 0000000000000..36565e75d0ed1 --- /dev/null +++ b/crates/aptos/src/update/tool.rs @@ -0,0 +1,145 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{check_if_update_required, helpers::InstallationMethod}; +use crate::common::{ + types::{CliCommand, CliTypedResult}, + utils::cli_build_information, +}; +use anyhow::{anyhow, Context}; +use aptos_build_info::BUILD_OS; +use async_trait::async_trait; +use clap::Parser; +use self_update::{backends::github::Update, cargo_crate_version, Status}; +use std::process::Command; + +/// Update the CLI itself +/// +/// This can be used to update the CLI to the latest version. This is useful if you +/// installed the CLI via the install script / by downloading the binary directly. +#[derive(Debug, Parser)] +pub struct UpdateTool { + /// The owner of the repo to download the binary from. + #[clap(long, default_value = "movementlabsxyz")] + repo_owner: String, + + /// The name of the repo to download the binary from. + #[clap(long, default_value = "aptos-core")] + repo_name: String, +} + +impl UpdateTool { + // Out of the box this crate assumes that you have releases named a specific way + // with the crate name, version, and target triple in a specific format. We don't + // do this with our releases, we have other GitHub releases beyond just the CLI, + // and we don't build for all major target triples, so we have to do some of the + // work ourselves first to figure out what the latest version of the CLI is and + // which binary to download based on the current OS. Then we can plug that into + // the library which takes care of the rest. + fn update(&self) -> CliTypedResult { + let installation_method = + InstallationMethod::from_env().context("Failed to determine installation method")?; + match installation_method { + InstallationMethod::Source => { + return Err( + anyhow!("Detected this CLI was built from source, refusing to update").into(), + ); + }, + InstallationMethod::Homebrew => { + return Err(anyhow!( + "Detected this CLI comes from homebrew, use `brew upgrade movement` instead" + ) + .into()); + }, + InstallationMethod::Other => {}, + } + + let info = check_if_update_required(&self.repo_owner, &self.repo_name)?; + if !info.update_required { + return Ok(format!("CLI already up to date (v{})", info.latest_version)); + } + + // Determine the target we should download. This is necessary because we don't + // name our binary releases using the target triples nor do we build specifically + // for all major triples, so we have to generalize to one of the binaries we do + // happen to build. We figure this out based on what system the CLI was built on. + let build_info = cli_build_information(); + let target = match build_info.get(BUILD_OS).context("Failed to determine build info of current CLI")?.as_str() { + "linux-x86_64" => { + // In the case of Linux, which build to use depends on the OpenSSL + // library on the host machine. So we try to determine that here. + // This code below parses the output of the `openssl version` command, + // where the version string is the 1th (0-indexing) item in the string + // when split by whitespace. + let output = Command::new("openssl") + .args(["version"]) + .output(); + let version = match output { + Ok(output) => { + let stdout = String::from_utf8(output.stdout).unwrap(); + stdout.split_whitespace().collect::>()[1].to_string() + }, + Err(e) => { + println!("Failed to determine OpenSSL version, assuming an older version: {:#}", e); + "1.0.0".to_string() + } + }; + // On Ubuntu < 22.04 the bundled OpenSSL is version 1.x.x, whereas on + // 22.04+ it is 3.x.x. Unfortunately if you build the CLI on a system + // with one major version of OpenSSL, you cannot use it on a system + // with a different version. Accordingly, if the current system uses + // OpenSSL 3.x.x, we use the version of the CLI built on a system with + // OpenSSL 3.x.x, meaning Ubuntu 22.04. Otherwise we use the one built + // on 20.04. + if version.starts_with('3') { + "Ubuntu-22.04-x86_64" + } else { + "Ubuntu-x86_64" + } + }, + "macos-x86_64" => "MacOSX-x86_64", + "windows-x86_64" => "Windows-x86_64", + wildcard => return Err(anyhow!("Self-updating is not supported on your OS right now, please download the binary manually: {}", wildcard).into()), + }; + + // Build a new configuration that will direct the library to download the + // binary with the target version tag and target that we determined above. + let config = Update::configure() + .repo_owner(&self.repo_owner) + .repo_name(&self.repo_name) + .bin_name("movement") + .current_version(cargo_crate_version!()) + .target_version_tag(&info.latest_version_tag) + .target(target) + .build() + .map_err(|e| anyhow!("Failed to build self-update configuration: {:#}", e))?; + + // Update the binary. + let result = config + .update() + .map_err(|e| anyhow!("Failed to update Movement CLI: {:#}", e))?; + + let message = match result { + Status::UpToDate(_) => panic!("We should have caught this already"), + Status::Updated(_) => format!( + "Successfully updated from v{} to v{}", + info.current_version, info.latest_version + ), + }; + + Ok(message) + } +} + +#[async_trait] +impl CliCommand for UpdateTool { + fn command_name(&self) -> &'static str { + "Update" + } + + async fn execute(self) -> CliTypedResult { + tokio::task::spawn_blocking(move || self.update()) + .await + .context("Failed to self-update Movement CLI")? + } +} diff --git a/testsuite/forge/Cargo.toml b/testsuite/forge/Cargo.toml index caa3c1f34ba78..1cd6b8bac8205 100644 --- a/testsuite/forge/Cargo.toml +++ b/testsuite/forge/Cargo.toml @@ -15,7 +15,7 @@ rust-version = { workspace = true } [dependencies] again = { workspace = true } anyhow = { workspace = true, features = ["backtrace"] } -aptos = { workspace = true } +movement = { workspace = true } aptos-cached-packages = { workspace = true } aptos-cli-common = { workspace = true } aptos-config = { workspace = true } diff --git a/testsuite/smoke-test/Cargo.toml b/testsuite/smoke-test/Cargo.toml index 31d1d2339702a..dde851dec0fe0 100644 --- a/testsuite/smoke-test/Cargo.toml +++ b/testsuite/smoke-test/Cargo.toml @@ -14,7 +14,7 @@ rust-version = { workspace = true } [dependencies] anyhow = { workspace = true } -aptos = { workspace = true, features = ["fuzzing"] } +movement = { workspace = true, features = ["fuzzing"] } aptos-bitvec = { path = "../../crates/aptos-bitvec" } aptos-cached-packages = { workspace = true } aptos-config = { workspace = true } diff --git a/testsuite/testcases/Cargo.toml b/testsuite/testcases/Cargo.toml index c7443b02da9c2..6f92a72aacf1b 100644 --- a/testsuite/testcases/Cargo.toml +++ b/testsuite/testcases/Cargo.toml @@ -14,7 +14,7 @@ rust-version = { workspace = true } [dependencies] anyhow = { workspace = true } -aptos = { workspace = true, features = ["fuzzing"] } +movement = { workspace = true, features = ["fuzzing"] } aptos-config = { workspace = true } aptos-forge = { workspace = true } aptos-global-constants = { workspace = true } From d4f16c84804db05e94c30eabde1fd75a1c8b0025 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 27 Jun 2024 10:59:01 +0300 Subject: [PATCH 114/147] aptos-db: batching fixes in revert_commit Use batches correctly: one per schema, commit all batches. --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index e52966d7d5936..f27988541d134 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -246,7 +246,6 @@ impl DbWriter for AptosDB { self.ledger_db .transaction_info_db() .delete_transaction_info(target_version + 1, &batch)?; - let batch = SchemaBatch::new(); self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events @@ -260,7 +259,6 @@ impl DbWriter for AptosDB { // Revert the transaction auxiliary data let batch = SchemaBatch::new(); TransactionAuxiliaryDataDb::prune(target_version, latest_version, &batch)?; - let batch = SchemaBatch::new(); self.ledger_db .transaction_auxiliary_data_db() .write_schemas(batch)?; @@ -268,11 +266,16 @@ impl DbWriter for AptosDB { // Revert the write set let batch = SchemaBatch::new(); WriteSetDb::prune(target_version, latest_version, &batch)?; + self.ledger_db.write_set_db().write_schemas(batch)?; + + // Remove the transactions + let batch = SchemaBatch::new(); self.ledger_db.transaction_db().prune_transactions( target_version, latest_version, &batch, )?; + self.ledger_db.transaction_db().write_schemas(batch)?; // Revert the state kv and ledger metadata self.state_store From 68a7b0e920352560e08a138ef22d9bee139cd7d6 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 28 Jun 2024 12:44:00 +0300 Subject: [PATCH 115/147] aptos-db: reorder LedgerInfo commit on revert Commit (and validate) the LedgerInfoWithSignatures and update the DbMetadataKey::OverallCommitProgress value first thing when reverting commits. --- .../aptosdb/src/db/include/aptosdb_writer.rs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index f27988541d134..9063cffa1a070 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -212,6 +212,14 @@ impl DbWriter for AptosDB { let latest_version = self.get_synced_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); + // Update the provided ledger info and the overall commit progress + let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); + self.commit_ledger_info( + target_version, + new_root_hash, + Some(&ledger_info_with_sigs), + )?; + let ledger_batch = SchemaBatch::new(); // Revert the ledger commit progress @@ -220,12 +228,6 @@ impl DbWriter for AptosDB { &DbMetadataValue::Version(target_version), )?; - // Revert the overall commit progress - ledger_batch.put::( - &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(target_version), - )?; - // Write ledger metadata db changes self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -282,14 +284,6 @@ impl DbWriter for AptosDB { .state_kv_db .revert_state_kv_and_ledger_metadata(target_version)?; - // Update the provided ledger info - let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); - self.commit_ledger_info( - target_version, - new_root_hash, - Some(&ledger_info_with_sigs), - )?; - Ok(()) } } From 03ab5ec81fb5b03c9d6f7165efbeaeec8723ad4c Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 1 Jul 2024 13:27:20 +0300 Subject: [PATCH 116/147] aptos-db: remove more on revert_commit Remove block information from ledger metadata db, update the LedgerCommitProgress marker in the same atomic batch. In StateKvDb, reuse the commit method to update version markers. Stub out reversal of store indices from StateStore, add TODO comments on what will need to be cleaned up. Consistently reuse the prune functionality available for the relevant tables to remove the ranges of versions. Remove methods added earlier that Fix off-by-one errors with version ranges to prune. --- .../aptosdb/src/db/include/aptosdb_writer.rs | 109 +++++++++++------- .../src/ledger_db/ledger_metadata_db.rs | 17 +++ .../ledger_db/transaction_accumulator_db.rs | 21 ---- .../src/ledger_db/transaction_info_db.rs | 8 -- storage/aptosdb/src/state_kv_db.rs | 26 ----- storage/aptosdb/src/state_store/mod.rs | 18 +++ 6 files changed, 105 insertions(+), 94 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 9063cffa1a070..523f17f03261b 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 +use crate::ledger_db::transaction_accumulator_db::TransactionAccumulatorDb; use crate::ledger_db::write_set_db::WriteSetDb; -use aptos_types::proof::position::Position; impl DbWriter for AptosDB { /// `first_version` is the version of the first transaction in `txns_to_commit`. @@ -201,10 +201,10 @@ impl DbWriter for AptosDB { } /// Revert a commit. - fn revert_commit( - &self, - ledger_info_with_sigs: &LedgerInfoWithSignatures, - ) -> Result<()> { + fn revert_commit(&self, ledger_info_with_sigs: &LedgerInfoWithSignatures) -> Result<()> { + // TODO: check if the pruners' progress needs to be set back + // to prevent them from pruning useful states. + let _timer = OTHER_TIMERS_SECONDS .with_label_values(&["revert_commit"]) .start_timer(); @@ -214,75 +214,50 @@ impl DbWriter for AptosDB { // Update the provided ledger info and the overall commit progress let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); - self.commit_ledger_info( - target_version, - new_root_hash, - Some(&ledger_info_with_sigs), - )?; - - let ledger_batch = SchemaBatch::new(); - - // Revert the ledger commit progress - ledger_batch.put::( - &DbMetadataKey::LedgerCommitProgress, - &DbMetadataValue::Version(target_version), - )?; - - // Write ledger metadata db changes - self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - - let temp_position = Position::from_postorder_index(latest_version)?; + self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; // Revert the transaction accumulator let batch = SchemaBatch::new(); - self.ledger_db - .transaction_accumulator_db() - .revert_transaction_accumulator(target_version, &batch, temp_position)?; + TransactionAccumulatorDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db .transaction_accumulator_db() .write_schemas(batch)?; // Revert the transaction info - // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); - self.ledger_db - .transaction_info_db() - .delete_transaction_info(target_version + 1, &batch)?; + TransactionInfoDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events - // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); self.ledger_db .event_db() - .delete_events(target_version + 1, &batch)?; + .prune_events(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.event_db().write_schemas(batch)?; // Revert the transaction auxiliary data let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(target_version, latest_version, &batch)?; + TransactionAuxiliaryDataDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db .transaction_auxiliary_data_db() .write_schemas(batch)?; // Revert the write set let batch = SchemaBatch::new(); - WriteSetDb::prune(target_version, latest_version, &batch)?; + WriteSetDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.write_set_db().write_schemas(batch)?; // Remove the transactions let batch = SchemaBatch::new(); self.ledger_db.transaction_db().prune_transactions( - target_version, - latest_version, + target_version + 1, + latest_version + 1, &batch, )?; self.ledger_db.transaction_db().write_schemas(batch)?; // Revert the state kv and ledger metadata - self.state_store - .state_kv_db - .revert_state_kv_and_ledger_metadata(target_version)?; + self.revert_state_kv_and_ledger_metadata(target_version, latest_version)?; Ok(()) } @@ -503,6 +478,62 @@ impl AptosDB { Ok(()) } + fn revert_state_kv_and_ledger_metadata( + &self, + target_version: Version, + latest_version: Version, + ) -> Result<()> { + let ledger_metadata_batch = SchemaBatch::new(); + let sharded_state_kv_batches = new_sharded_kv_schema_batch(); + let state_kv_metadata_batch = SchemaBatch::new(); + + self.state_store.revert_value_sets( + target_version, + latest_version, + &ledger_metadata_batch, + &sharded_state_kv_batches, + &state_kv_metadata_batch, + self.state_store.state_kv_db.enabled_sharding(), + )?; + + // Revert block index if event index is skipped. + if self.skip_index_and_usage { + self.ledger_db + .metadata_db() + .truncate_block_info(target_version + 1, &ledger_metadata_batch)?; + } + + ledger_metadata_batch + .put::( + &DbMetadataKey::LedgerCommitProgress, + &DbMetadataValue::Version(target_version), + ) + .unwrap(); + + let _timer = OTHER_TIMERS_SECONDS + .with_label_values(&["revert_state_kv_and_ledger_metadata___commit"]) + .start_timer(); + rayon::scope(|s| { + s.spawn(|_| { + self.ledger_db + .metadata_db() + .write_schemas(ledger_metadata_batch) + .unwrap(); + }); + s.spawn(|_| { + self.state_kv_db + .commit( + target_version, + state_kv_metadata_batch, + sharded_state_kv_batches, + ) + .unwrap(); + }); + }); + + Ok(()) + } + fn commit_events( &self, txns_to_commit: &[TransactionToCommit], diff --git a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs index c5d2857718f6f..bac201bde2afe 100644 --- a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs +++ b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs @@ -305,6 +305,23 @@ impl LedgerMetadataDb { Ok(()) } + + /// Delete committed block info indices starting from the specified version. + pub(crate) fn truncate_block_info( + &self, + from_version: Version, + batch: &SchemaBatch, + ) -> Result<()> { + let mut iter = self + .db + .iter::(ReadOptions::default())?; + iter.seek(&from_version)?; + while let Some((block_version, block_height)) = iter.next().transpose()? { + batch.delete::(&block_height)?; + batch.delete::(&block_version)?; + } + Ok(()) + } } /// Usage APIs. diff --git a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs index 3f19bf9cd9f29..2fa1367670d5e 100644 --- a/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs +++ b/storage/aptosdb/src/ledger_db/transaction_accumulator_db.rs @@ -53,27 +53,6 @@ impl TransactionAccumulatorDb { pub(crate) fn write_schemas(&self, batch: SchemaBatch) -> Result<()> { self.db.write_schemas(batch) } - - pub(crate) fn revert_transaction_accumulator( - &self, - version: Version, - batch: &SchemaBatch, - position: Position, - ) -> Result<()> { - // Delete the transaction accumulator entry for the given version - batch.delete::(&position)?; - - // Update the transaction accumulator pruner progress to the previous version - let prev_version = version - .checked_sub(1) - .ok_or_else(|| anyhow::anyhow!("Cannot revert transaction accumulator at version 0"))?; - batch.put::( - &DbMetadataKey::TransactionAccumulatorPrunerProgress, - &DbMetadataValue::Version(prev_version), - )?; - - Ok(()) - } } impl TransactionAccumulatorDb { diff --git a/storage/aptosdb/src/ledger_db/transaction_info_db.rs b/storage/aptosdb/src/ledger_db/transaction_info_db.rs index 5adfdff7ff13b..45d826f16cd1c 100644 --- a/storage/aptosdb/src/ledger_db/transaction_info_db.rs +++ b/storage/aptosdb/src/ledger_db/transaction_info_db.rs @@ -98,12 +98,4 @@ impl TransactionInfoDb { } Ok(()) } - - pub(crate) fn delete_transaction_info( - &self, - version: Version, - batch: &SchemaBatch, - ) -> Result<()> { - batch.delete::(&version) - } } diff --git a/storage/aptosdb/src/state_kv_db.rs b/storage/aptosdb/src/state_kv_db.rs index 343726e015f4b..3842a9b2fe18a 100644 --- a/storage/aptosdb/src/state_kv_db.rs +++ b/storage/aptosdb/src/state_kv_db.rs @@ -235,32 +235,6 @@ impl StateKvDb { self.state_kv_db_shards[shard_id as usize].write_schemas(batch) } - pub(crate) fn revert_state_kv_and_ledger_metadata(&self, version: Version) -> Result<()> { - // Revert the state KV metadata DB to the given version - let metadata_batch = SchemaBatch::new(); - metadata_batch.put::( - &DbMetadataKey::StateKvCommitProgress, - &DbMetadataValue::Version(version), - )?; - metadata_batch.put::( - &DbMetadataKey::StateKvPrunerProgress, - &DbMetadataValue::Version(version), - )?; - self.state_kv_metadata_db.write_schemas(metadata_batch)?; - - // Revert each state KV DB shard to the corresponding version - for shard_id in 0..NUM_STATE_SHARDS { - let shard_batch = SchemaBatch::new(); - shard_batch.put::( - &DbMetadataKey::StateKvShardCommitProgress(shard_id as usize), - &DbMetadataValue::Version(version), - )?; - self.state_kv_db_shards[shard_id as usize].write_schemas(shard_batch)?; - } - - Ok(()) - } - fn open_shard>( db_root_path: P, shard_id: u8, diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index 9eb9c5edbdbaf..d2989dd0804fc 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -683,6 +683,24 @@ impl StateStore { ) } + pub fn revert_value_sets( + &self, + _first_version: Version, + _last_version: Version, + _ledger_batch: &SchemaBatch, + _sharded_state_kv_batches: &ShardedStateKvSchemaBatch, + _state_kv_metadata_batch: &SchemaBatch, + _put_state_value_indices: bool, + ) -> Result<()> { + // TODO: add tombstone entries to StaleStateValueIndexSchema + // TODO: revert usage statistics + // TODO: remove matching KV pairs from StateValueSchema + // TODO: remove matching state value indices from StateValueIndexSchema + // while the upstream uses them. + + Ok(()) + } + pub fn put_state_values( &self, value_state_sets: Vec<&ShardedStateUpdates>, From c22113d16c9d3a4ba84ede48ed69ff96a02d42b1 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 1 Jul 2024 14:08:09 +0300 Subject: [PATCH 117/147] aptos-db: remove unused imports in tests --- storage/aptosdb/src/db/aptosdb_test.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 1ee0c9cfa3550..b0b2acf614433 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -23,33 +23,26 @@ use aptos_crypto::{ hash::CryptoHash, HashValue, PrivateKey, Uniform, }; -use aptos_executor_types::StateComputeResult; use aptos_proptest_helpers::ValueGenerator; -use aptos_storage_interface::{DbReader, DbReaderWriter, DbWriter, ExecutedTrees, Order}; +use aptos_storage_interface::{DbReader, DbWriter, ExecutedTrees, Order}; use aptos_temppath::TempPath; use aptos_types::{ chain_id::ChainId, - ledger_info::{generate_ledger_info_with_sig, LedgerInfoWithSignatures}, - on_chain_config::ValidatorSet, - proof::{position::Position, SparseMerkleLeafNode}, - proptest_types::{ - AccountInfoUniverse, BlockInfoGen, LedgerInfoGen, LedgerInfoWithSignaturesGen, - ValidatorSetGen, - }, + ledger_info::LedgerInfoWithSignatures, + proof::SparseMerkleLeafNode, state_store::{ state_key::StateKey, state_storage_usage::StateStorageUsage, state_value::StateValue, }, transaction::{ - ExecutionStatus, RawTransaction, Script, SignedTransaction, Transaction, - TransactionAuxiliaryData, TransactionAuxiliaryDataV1, TransactionInfo, TransactionPayload, - TransactionToCommit, VMErrorDetail, Version, + ExecutionStatus, RawTransaction, Script, SignedTransaction, TransactionAuxiliaryData, + TransactionAuxiliaryDataV1, TransactionInfo, TransactionPayload, TransactionToCommit, + VMErrorDetail, Version, }, vm_status::StatusCode, - write_set::WriteSet, }; -use move_core_types::{account_address::AccountAddress, vm_status::StatusType::Execution}; -use proptest::{prelude::*, std_facade::HashMap, test_runner::TestRunner}; -use std::{collections::HashSet, default, ops::DerefMut, sync::Arc}; +use move_core_types::account_address::AccountAddress; +use proptest::{prelude::*, std_facade::HashMap}; +use std::{collections::HashSet, sync::Arc}; use test_helper::{test_save_blocks_impl, test_sync_transactions_impl}; proptest! { From 07aca16086ec8f73dd4dfc5a79b5651de50dd6e9 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 2 Jul 2024 11:05:33 +0300 Subject: [PATCH 118/147] aptos-db: set in-memory ledger info on revert --- .../aptosdb/src/db/include/aptosdb_writer.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 523f17f03261b..24ca3d481463f 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -212,6 +212,10 @@ impl DbWriter for AptosDB { let latest_version = self.get_synced_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); + // Update in-memory state first, as this is what + // concurrent readers would use for the latest ledger info. + self.pre_revert(latest_version, &ledger_info_with_sigs); + // Update the provided ledger info and the overall commit progress let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; @@ -737,4 +741,42 @@ impl AptosDB { Ok(()) } + + // Update in-memory state of the database and the metrics before reverting. + // Note that any failures in persisting the revert should be treated as + // non-recoverable. + fn pre_revert( + &self, + latest_version: Version, + ledger_info_with_sigs: &LedgerInfoWithSignatures, + ) { + let target_version = ledger_info_with_sigs.ledger_info().version(); + let num_txns = latest_version - target_version + 1; + if num_txns > 0 { + // TODO: also update the COMMITTED_TXNS, but currently it can only go up + LATEST_TXN_VERSION.set(target_version as i64); + + // Set back the ledger pruner and state kv pruner. + // Note the state merkle pruner is activated when state snapshots are persisted + // in their async thread. + self.ledger_pruner + .maybe_set_pruner_target_db_version(target_version); + self.state_store + .state_kv_pruner + .maybe_set_pruner_target_db_version(target_version); + } + + if let Some(_indexer) = &self.indexer { + // TODO: prune the reverted write sets from the indexer + } + + // Update the metrics + LEDGER_VERSION.set(target_version as i64); + NEXT_BLOCK_EPOCH.set(ledger_info_with_sigs.ledger_info().next_block_epoch() as i64); + + // Update the latest in-memory ledger info. + self.ledger_db + .metadata_db() + .set_latest_ledger_info(ledger_info_with_sigs.clone()); + } } From d2146bfe8d7503145200547af2c0c562b538696f Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 2 Jul 2024 12:45:10 +0300 Subject: [PATCH 119/147] fix: cherry-pick. --- storage/aptosdb/src/db/aptosdb_test.rs | 19 ++- .../aptosdb/src/db/include/aptosdb_writer.rs | 110 ++---------------- storage/aptosdb/src/ledger_db/event_db.rs | 23 ---- .../src/ledger_db/ledger_metadata_db.rs | 3 +- storage/aptosdb/src/state_store/mod.rs | 18 --- 5 files changed, 30 insertions(+), 143 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index b0b2acf614433..c618c43fba130 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -265,8 +265,8 @@ fn test_revert_single_commit() { } // Check expected before revert commit - let expected_version = cur_ver - 1; - assert_eq!(db.get_latest_version().unwrap(), expected_version); + let pre_revert_version = cur_ver - 1; + assert_eq!(db.get_latest_version().unwrap(), pre_revert_version); // Get the latest ledger info before revert let latest_ledger_info_before_revert = blocks[1].1.clone(); @@ -276,6 +276,21 @@ fn test_revert_single_commit() { db.revert_commit(&latest_ledger_info_before_revert).unwrap(); assert_eq!(db.get_latest_version().unwrap(), version_to_revert_to); + let ledger_info = db.get_latest_ledger_info().unwrap(); + assert_eq!(ledger_info, latest_ledger_info_before_revert); + let tx_acc_db = db.ledger_db.transaction_accumulator_db(); + for i in version_to_revert_to + 1..=pre_revert_version { + let _ = tx_acc_db + .get_root_hash(i) + .expect_err(&format!("expected no state for {i}")); + } + let root_hash = tx_acc_db.get_root_hash(version_to_revert_to).unwrap(); + assert_eq!( + root_hash, + latest_ledger_info_before_revert + .commit_info() + .executed_state_id() + ); } #[test] diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 24ca3d481463f..e6ff5a48461ec 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,7 +1,6 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::ledger_db::transaction_accumulator_db::TransactionAccumulatorDb; -use crate::ledger_db::write_set_db::WriteSetDb; +use crate::utils::truncation_helper::{truncate_ledger_db, truncate_state_kv_db_shards}; impl DbWriter for AptosDB { /// `first_version` is the version of the first transaction in `txns_to_commit`. @@ -220,48 +219,17 @@ impl DbWriter for AptosDB { let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; - // Revert the transaction accumulator - let batch = SchemaBatch::new(); - TransactionAccumulatorDb::prune(target_version + 1, latest_version + 1, &batch)?; - self.ledger_db - .transaction_accumulator_db() - .write_schemas(batch)?; - - // Revert the transaction info - let batch = SchemaBatch::new(); - TransactionInfoDb::prune(target_version + 1, latest_version + 1, &batch)?; - self.ledger_db.transaction_info_db().write_schemas(batch)?; - - // Revert the events - let batch = SchemaBatch::new(); - self.ledger_db - .event_db() - .prune_events(target_version + 1, latest_version + 1, &batch)?; - self.ledger_db.event_db().write_schemas(batch)?; + truncate_ledger_db(self.ledger_db.clone(), target_version)?; + truncate_state_kv_db_shards(&self.state_kv_db, target_version, Some(latest_version))?; - // Revert the transaction auxiliary data - let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(target_version + 1, latest_version + 1, &batch)?; - self.ledger_db - .transaction_auxiliary_data_db() - .write_schemas(batch)?; - - // Revert the write set - let batch = SchemaBatch::new(); - WriteSetDb::prune(target_version + 1, latest_version + 1, &batch)?; - self.ledger_db.write_set_db().write_schemas(batch)?; - - // Remove the transactions - let batch = SchemaBatch::new(); - self.ledger_db.transaction_db().prune_transactions( - target_version + 1, - latest_version + 1, - &batch, - )?; - self.ledger_db.transaction_db().write_schemas(batch)?; - - // Revert the state kv and ledger metadata - self.revert_state_kv_and_ledger_metadata(target_version, latest_version)?; + // Revert block index if event index is skipped. + if self.skip_index_and_usage { + let batch = SchemaBatch::new(); + self.ledger_db + .metadata_db() + .truncate_block_info(target_version, &batch)?; + self.ledger_db.metadata_db().write_schemas(batch)?; + } Ok(()) } @@ -482,62 +450,6 @@ impl AptosDB { Ok(()) } - fn revert_state_kv_and_ledger_metadata( - &self, - target_version: Version, - latest_version: Version, - ) -> Result<()> { - let ledger_metadata_batch = SchemaBatch::new(); - let sharded_state_kv_batches = new_sharded_kv_schema_batch(); - let state_kv_metadata_batch = SchemaBatch::new(); - - self.state_store.revert_value_sets( - target_version, - latest_version, - &ledger_metadata_batch, - &sharded_state_kv_batches, - &state_kv_metadata_batch, - self.state_store.state_kv_db.enabled_sharding(), - )?; - - // Revert block index if event index is skipped. - if self.skip_index_and_usage { - self.ledger_db - .metadata_db() - .truncate_block_info(target_version + 1, &ledger_metadata_batch)?; - } - - ledger_metadata_batch - .put::( - &DbMetadataKey::LedgerCommitProgress, - &DbMetadataValue::Version(target_version), - ) - .unwrap(); - - let _timer = OTHER_TIMERS_SECONDS - .with_label_values(&["revert_state_kv_and_ledger_metadata___commit"]) - .start_timer(); - rayon::scope(|s| { - s.spawn(|_| { - self.ledger_db - .metadata_db() - .write_schemas(ledger_metadata_batch) - .unwrap(); - }); - s.spawn(|_| { - self.state_kv_db - .commit( - target_version, - state_kv_metadata_batch, - sharded_state_kv_batches, - ) - .unwrap(); - }); - }); - - Ok(()) - } - fn commit_events( &self, txns_to_commit: &[TransactionToCommit], diff --git a/storage/aptosdb/src/ledger_db/event_db.rs b/storage/aptosdb/src/ledger_db/event_db.rs index d5fb1c026d4c8..d475062f9d8ac 100644 --- a/storage/aptosdb/src/ledger_db/event_db.rs +++ b/storage/aptosdb/src/ledger_db/event_db.rs @@ -210,27 +210,4 @@ impl EventDb { .prune_event_accumulator(start, end, db_batch)?; Ok(()) } - - pub(crate) fn delete_events( - &self, - version: Version, - db_batch: &SchemaBatch, - ) -> anyhow::Result<()> { - for events in self.get_events_by_version_iter(version, 1)? { - for (idx, event) in (events?).into_iter().enumerate() { - if let ContractEvent::V1(v1) = event { - db_batch.delete::(&( - *v1.key(), - version, - v1.sequence_number(), - ))?; - db_batch.delete::(&(*v1.key(), v1.sequence_number()))?; - } - db_batch.delete::(&(version, idx as u64))?; - } - } - self.event_store - .delete_event_accumulator(version, db_batch)?; - Ok(()) - } } diff --git a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs index bac201bde2afe..6c1f37c8dca57 100644 --- a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs +++ b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs @@ -309,9 +309,10 @@ impl LedgerMetadataDb { /// Delete committed block info indices starting from the specified version. pub(crate) fn truncate_block_info( &self, - from_version: Version, + target_version: Version, batch: &SchemaBatch, ) -> Result<()> { + let from_version = target_version + 1; let mut iter = self .db .iter::(ReadOptions::default())?; diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index d2989dd0804fc..9eb9c5edbdbaf 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -683,24 +683,6 @@ impl StateStore { ) } - pub fn revert_value_sets( - &self, - _first_version: Version, - _last_version: Version, - _ledger_batch: &SchemaBatch, - _sharded_state_kv_batches: &ShardedStateKvSchemaBatch, - _state_kv_metadata_batch: &SchemaBatch, - _put_state_value_indices: bool, - ) -> Result<()> { - // TODO: add tombstone entries to StaleStateValueIndexSchema - // TODO: revert usage statistics - // TODO: remove matching KV pairs from StateValueSchema - // TODO: remove matching state value indices from StateValueIndexSchema - // while the upstream uses them. - - Ok(()) - } - pub fn put_state_values( &self, value_state_sets: Vec<&ShardedStateUpdates>, From e94a25aff3917bd845def9588e6b3be7fccdd33a Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 2 Jul 2024 18:26:58 +0300 Subject: [PATCH 120/147] types: remove setter methods These were added to make LedgerInfo and BlockInfo work when reverting, but are no longer needed after rewrites. --- types/src/block_info.rs | 8 -------- types/src/ledger_info.rs | 4 ---- 2 files changed, 12 deletions(-) diff --git a/types/src/block_info.rs b/types/src/block_info.rs index 69fd8940d9383..97c13be3893f1 100644 --- a/types/src/block_info.rs +++ b/types/src/block_info.rs @@ -159,10 +159,6 @@ impl BlockInfo { self.epoch } - pub fn set_epoch(&mut self, epoch: u64) { - self.epoch = epoch; - } - pub fn executed_state_id(&self) -> HashValue { self.executed_state_id } @@ -191,10 +187,6 @@ impl BlockInfo { self.version } - pub fn set_version(&mut self, version: Version) { - self.version = version; - } - /// This function checks if the current BlockInfo has /// exactly the same values in those fields that will not change /// after execution, compared to a given BlockInfo diff --git a/types/src/ledger_info.rs b/types/src/ledger_info.rs index 8f95dd5b81283..98761caf913e4 100644 --- a/types/src/ledger_info.rs +++ b/types/src/ledger_info.rs @@ -126,10 +126,6 @@ impl LedgerInfo { self.commit_info.version() } - pub fn set_version(&mut self, version: Version) { - self.commit_info.set_version(version) - } - pub fn timestamp_usecs(&self) -> u64 { self.commit_info.timestamp_usecs() } From 769574897029abd42878a743528053e9806c2727 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 3 Jul 2024 10:48:09 +0300 Subject: [PATCH 121/147] aptos-db: truncate state merkle DB on revert --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index e6ff5a48461ec..79a9dbfc91969 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,6 +1,8 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::utils::truncation_helper::{truncate_ledger_db, truncate_state_kv_db_shards}; +use crate::utils::truncation_helper::{ + truncate_ledger_db, truncate_state_kv_db_shards, truncate_state_merkle_db, +}; impl DbWriter for AptosDB { /// `first_version` is the version of the first transaction in `txns_to_commit`. @@ -219,8 +221,17 @@ impl DbWriter for AptosDB { let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; + // Truncate the ledger and state dbs truncate_ledger_db(self.ledger_db.clone(), target_version)?; - truncate_state_kv_db_shards(&self.state_kv_db, target_version, Some(latest_version))?; + truncate_state_kv_db_shards( + &self.state_store.state_kv_db, + target_version, + Some(latest_version), + )?; + truncate_state_merkle_db(&self.state_store.state_merkle_db, target_version)?; + + // Reset buffered state after truncation + self.state_store.reset(); // Revert block index if event index is skipped. if self.skip_index_and_usage { From 9bf884bf3b534aa1ecfa7c7785cb60897cdcdd8b Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 3 Jul 2024 12:04:41 +0300 Subject: [PATCH 122/147] aptos-db: remove an unused test helper --- storage/aptosdb/src/db/aptosdb_test.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index c618c43fba130..e5c0eb07db555 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -217,27 +217,6 @@ fn test_get_latest_executed_trees() { ); } -fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { - let private_key = Ed25519PrivateKey::generate_for_testing(); - let public_key = private_key.public_key(); - - let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); - let raw_transaction = RawTransaction::new( - AccountAddress::random(), - 0, - transaction_payload, - 0, - gas_unit_price, - 0, - ChainId::new(10), // This is the value used in aptos testing code. - ); - SignedTransaction::new( - raw_transaction, - public_key, - Ed25519Signature::dummy_signature(), - ) -} - #[test] fn test_revert_single_commit() { aptos_logger::Logger::new().init(); From ffd03b4abe921a6eaa0364a7689b620ba0b26c06 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 5 Jul 2024 13:32:31 +0300 Subject: [PATCH 123/147] aptos-db: ResetLock Add a ResetLock to expose scoped locking used in `reset`, to implement atomic updates of buffered state in revert_commit. --- storage/aptosdb/src/state_store/mod.rs | 16 ++++--- storage/aptosdb/src/state_store/reset_lock.rs | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 storage/aptosdb/src/state_store/reset_lock.rs diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index 9eb9c5edbdbaf..cbb2c9f2cf811 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -25,6 +25,7 @@ use crate::{ StateSnapshotProgress, StateSnapshotRestore, StateSnapshotRestoreMode, StateValueWriter, }, state_store::buffered_state::BufferedState, + state_store::reset_lock::ResetLock, utils::{ iterators::PrefixedStateValueIterator, new_sharded_kv_schema_batch, @@ -71,6 +72,7 @@ use rayon::prelude::*; use std::{collections::HashSet, ops::Deref, sync::Arc}; pub(crate) mod buffered_state; +pub(crate) mod reset_lock; mod state_merkle_batch_committer; mod state_snapshot_committer; @@ -549,15 +551,17 @@ impl StateStore { } pub fn reset(&self) { - let (buffered_state, smt_ancestors) = Self::create_buffered_state_from_latest_snapshot( + let lock = self.reset_lock(); + lock.reset(); + } + + pub fn reset_lock(&self) -> ResetLock<'_> { + ResetLock::new( + &self.buffered_state, + &self.smt_ancestors, &self.state_db, self.buffered_state_target_items, - false, - true, ) - .expect("buffered state creation failed."); - *self.buffered_state.lock() = buffered_state; - *self.smt_ancestors.lock() = smt_ancestors; } pub fn buffered_state(&self) -> &Mutex { diff --git a/storage/aptosdb/src/state_store/reset_lock.rs b/storage/aptosdb/src/state_store/reset_lock.rs new file mode 100644 index 0000000000000..211065548f4d4 --- /dev/null +++ b/storage/aptosdb/src/state_store/reset_lock.rs @@ -0,0 +1,45 @@ +use super::buffered_state::BufferedState; +use super::{StateDb, StateStore}; +use aptos_infallible::{Mutex, MutexGuard}; +use aptos_scratchpad::SmtAncestors; +use aptos_types::state_store::state_value::StateValue; + +use std::sync::Arc; + +/// Locks the buffered state. This can be used to implement atomic +/// reset together with a ledger database commit. +pub struct ResetLock<'a> { + buffered_state: MutexGuard<'a, BufferedState>, + smt_ancestors: MutexGuard<'a, SmtAncestors>, + state_db: &'a Arc, + buffered_state_target_items: usize, +} + +impl<'a> ResetLock<'a> { + pub(super) fn new( + buffered_state: &'a Mutex, + smt_ancestors: &'a Mutex>, + state_db: &'a Arc, + buffered_state_target_items: usize, + ) -> Self { + Self { + buffered_state: buffered_state.lock(), + smt_ancestors: smt_ancestors.lock(), + state_db, + buffered_state_target_items, + } + } + + pub fn reset(mut self) { + let (buffered_state, smt_ancestors) = + StateStore::create_buffered_state_from_latest_snapshot( + self.state_db, + self.buffered_state_target_items, + false, + true, + ) + .expect("buffered state creation failed."); + *self.buffered_state = buffered_state; + *self.smt_ancestors = smt_ancestors; + } +} From 7602bf7cf08a915b9b3662be995fd2fc306d8d2c Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 5 Jul 2024 14:01:49 +0300 Subject: [PATCH 124/147] aptos-db: Use the state store lock on revert --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 79a9dbfc91969..f60ba1e455aca 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -217,6 +217,9 @@ impl DbWriter for AptosDB { // concurrent readers would use for the latest ledger info. self.pre_revert(latest_version, &ledger_info_with_sigs); + // Lock buffered state in the state store + let state_lock = self.state_store.reset_lock(); + // Update the provided ledger info and the overall commit progress let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; @@ -231,7 +234,7 @@ impl DbWriter for AptosDB { truncate_state_merkle_db(&self.state_store.state_merkle_db, target_version)?; // Reset buffered state after truncation - self.state_store.reset(); + state_lock.reset(); // Revert block index if event index is skipped. if self.skip_index_and_usage { From 2c0afa31fad282cbdb491246018b759bbbc2385e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 4 Jul 2024 17:44:39 +0300 Subject: [PATCH 125/147] fix: cherry-pick. --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index f60ba1e455aca..bc3ec6d706f4a 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -22,9 +22,9 @@ impl DbWriter for AptosDB { sharded_state_cache: Option<&ShardedStateCache>, ) -> Result<()> { gauged_api("save_transactions", || { - // Executing and committing from more than one threads not allowed -- consensus and - // state sync must hand over to each other after all pending execution and committing - // complete. + // Executing, committing, or reverting from more than one threads not allowed -- + // consensus and state sync must hand over to each other after all pending execution + // and committing complete. let _lock = self .ledger_commit_lock .try_lock() @@ -210,7 +210,15 @@ impl DbWriter for AptosDB { .with_label_values(&["revert_commit"]) .start_timer(); - let latest_version = self.get_synced_version()?; + // Executing, committing, or reverting from more than one threads not allowed -- + // consensus and state sync must hand over to each other after all pending execution + // and committing complete. + let _lock = self + .ledger_commit_lock + .try_lock() + .expect("Concurrent committing detected."); + + let latest_version = self.get_latest_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); // Update in-memory state first, as this is what From c0cb1826ad1d167b7bfe15945852a45f06bbca1e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 27 Jun 2024 10:59:01 +0300 Subject: [PATCH 126/147] fix: cherry-pick. --- .../aptosdb/src/db/include/aptosdb_writer.rs | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index bc3ec6d706f4a..2f0f9660e06cf 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -228,7 +228,69 @@ impl DbWriter for AptosDB { // Lock buffered state in the state store let state_lock = self.state_store.reset_lock(); - // Update the provided ledger info and the overall commit progress + // Revert the overall commit progress + ledger_batch.put::( + &DbMetadataKey::OverallCommitProgress, + &DbMetadataValue::Version(target_version), + )?; + + // Write ledger metadata db changes + self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + + let temp_position = Position::from_postorder_index(latest_version)?; + + // Revert the transaction accumulator + let batch = SchemaBatch::new(); + self.ledger_db + .transaction_accumulator_db() + .revert_transaction_accumulator(target_version, &batch, temp_position)?; + self.ledger_db + .transaction_accumulator_db() + .write_schemas(batch)?; + + // Revert the transaction info + // FIXME: need to delete the range to current latest? + let batch = SchemaBatch::new(); + self.ledger_db + .transaction_info_db() + .delete_transaction_info(target_version + 1, &batch)?; + self.ledger_db.transaction_info_db().write_schemas(batch)?; + + // Revert the events + // FIXME: need to delete the range to current latest? + let batch = SchemaBatch::new(); + self.ledger_db + .event_db() + .delete_events(target_version + 1, &batch)?; + self.ledger_db.event_db().write_schemas(batch)?; + + // Revert the transaction auxiliary data + let batch = SchemaBatch::new(); + TransactionAuxiliaryDataDb::prune(target_version, latest_version, &batch)?; + self.ledger_db + .transaction_auxiliary_data_db() + .write_schemas(batch)?; + + // Revert the write set + let batch = SchemaBatch::new(); + WriteSetDb::prune(target_version, latest_version, &batch)?; + self.ledger_db.write_set_db().write_schemas(batch)?; + + // Remove the transactions + let batch = SchemaBatch::new(); + self.ledger_db.transaction_db().prune_transactions( + target_version, + latest_version, + &batch, + )?; + self.ledger_db.transaction_db().write_schemas(batch)?; + + // Revert the state kv and ledger metadata + self.state_store + .state_kv_db + .revert_state_kv_and_ledger_metadata(target_version)?; + + // Update the provided ledger info let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; From 9e03a25c5b4ec473ba561827c44f441078c924e5 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 28 Jun 2024 12:44:00 +0300 Subject: [PATCH 127/147] fix: cherry-pick. --- .../aptosdb/src/db/include/aptosdb_writer.rs | 43 ++++--------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 2f0f9660e06cf..72b74ca24fe56 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -221,19 +221,19 @@ impl DbWriter for AptosDB { let latest_version = self.get_latest_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); - // Update in-memory state first, as this is what - // concurrent readers would use for the latest ledger info. - self.pre_revert(latest_version, &ledger_info_with_sigs); + // Update the provided ledger info and the overall commit progress + let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); + self.commit_ledger_info( + target_version, + new_root_hash, + Some(&ledger_info_with_sigs), + )?; + + let ledger_batch = SchemaBatch::new(); // Lock buffered state in the state store let state_lock = self.state_store.reset_lock(); - // Revert the overall commit progress - ledger_batch.put::( - &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(target_version), - )?; - // Write ledger metadata db changes self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -290,31 +290,6 @@ impl DbWriter for AptosDB { .state_kv_db .revert_state_kv_and_ledger_metadata(target_version)?; - // Update the provided ledger info - let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); - self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; - - // Truncate the ledger and state dbs - truncate_ledger_db(self.ledger_db.clone(), target_version)?; - truncate_state_kv_db_shards( - &self.state_store.state_kv_db, - target_version, - Some(latest_version), - )?; - truncate_state_merkle_db(&self.state_store.state_merkle_db, target_version)?; - - // Reset buffered state after truncation - state_lock.reset(); - - // Revert block index if event index is skipped. - if self.skip_index_and_usage { - let batch = SchemaBatch::new(); - self.ledger_db - .metadata_db() - .truncate_block_info(target_version, &batch)?; - self.ledger_db.metadata_db().write_schemas(batch)?; - } - Ok(()) } } From cf8cc6f7394364d08eb0a4e8aff5f1ca4a470728 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 1 Jul 2024 13:27:20 +0300 Subject: [PATCH 128/147] fix: cherry-pick --- .../aptosdb/src/db/include/aptosdb_writer.rs | 101 ++++++++++++------ .../src/ledger_db/ledger_metadata_db.rs | 6 ++ storage/aptosdb/src/state_store/mod.rs | 18 ++++ 3 files changed, 91 insertions(+), 34 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 72b74ca24fe56..c649e8dabd47d 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,8 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::utils::truncation_helper::{ - truncate_ledger_db, truncate_state_kv_db_shards, truncate_state_merkle_db, -}; +use crate::ledger_db::transaction_accumulator_db::TransactionAccumulatorDb; +use crate::ledger_db::write_set_db::WriteSetDb; impl DbWriter for AptosDB { /// `first_version` is the version of the first transaction in `txns_to_commit`. @@ -223,72 +222,50 @@ impl DbWriter for AptosDB { // Update the provided ledger info and the overall commit progress let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); - self.commit_ledger_info( - target_version, - new_root_hash, - Some(&ledger_info_with_sigs), - )?; - - let ledger_batch = SchemaBatch::new(); - - // Lock buffered state in the state store - let state_lock = self.state_store.reset_lock(); - - // Write ledger metadata db changes - self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - - let temp_position = Position::from_postorder_index(latest_version)?; + self.commit_ledger_info(target_version, new_root_hash, Some(&ledger_info_with_sigs))?; // Revert the transaction accumulator let batch = SchemaBatch::new(); - self.ledger_db - .transaction_accumulator_db() - .revert_transaction_accumulator(target_version, &batch, temp_position)?; + TransactionAccumulatorDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db .transaction_accumulator_db() .write_schemas(batch)?; // Revert the transaction info - // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); - self.ledger_db - .transaction_info_db() - .delete_transaction_info(target_version + 1, &batch)?; + TransactionInfoDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events - // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); self.ledger_db .event_db() - .delete_events(target_version + 1, &batch)?; + .prune_events(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.event_db().write_schemas(batch)?; // Revert the transaction auxiliary data let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(target_version, latest_version, &batch)?; + TransactionAuxiliaryDataDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db .transaction_auxiliary_data_db() .write_schemas(batch)?; // Revert the write set let batch = SchemaBatch::new(); - WriteSetDb::prune(target_version, latest_version, &batch)?; + WriteSetDb::prune(target_version + 1, latest_version + 1, &batch)?; self.ledger_db.write_set_db().write_schemas(batch)?; // Remove the transactions let batch = SchemaBatch::new(); self.ledger_db.transaction_db().prune_transactions( - target_version, - latest_version, + target_version + 1, + latest_version + 1, &batch, )?; self.ledger_db.transaction_db().write_schemas(batch)?; // Revert the state kv and ledger metadata - self.state_store - .state_kv_db - .revert_state_kv_and_ledger_metadata(target_version)?; + self.revert_state_kv_and_ledger_metadata(target_version, latest_version)?; Ok(()) } @@ -509,6 +486,62 @@ impl AptosDB { Ok(()) } + fn revert_state_kv_and_ledger_metadata( + &self, + target_version: Version, + latest_version: Version, + ) -> Result<()> { + let ledger_metadata_batch = SchemaBatch::new(); + let sharded_state_kv_batches = new_sharded_kv_schema_batch(); + let state_kv_metadata_batch = SchemaBatch::new(); + + self.state_store.revert_value_sets( + target_version, + latest_version, + &ledger_metadata_batch, + &sharded_state_kv_batches, + &state_kv_metadata_batch, + self.state_store.state_kv_db.enabled_sharding(), + )?; + + // Revert block index if event index is skipped. + if self.skip_index_and_usage { + self.ledger_db + .metadata_db() + .truncate_block_info(target_version + 1, &ledger_metadata_batch)?; + } + + ledger_metadata_batch + .put::( + &DbMetadataKey::LedgerCommitProgress, + &DbMetadataValue::Version(target_version), + ) + .unwrap(); + + let _timer = OTHER_TIMERS_SECONDS + .with_label_values(&["revert_state_kv_and_ledger_metadata___commit"]) + .start_timer(); + rayon::scope(|s| { + s.spawn(|_| { + self.ledger_db + .metadata_db() + .write_schemas(ledger_metadata_batch) + .unwrap(); + }); + s.spawn(|_| { + self.state_kv_db + .commit( + target_version, + state_kv_metadata_batch, + sharded_state_kv_batches, + ) + .unwrap(); + }); + }); + + Ok(()) + } + fn commit_events( &self, txns_to_commit: &[TransactionToCommit], diff --git a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs index 6c1f37c8dca57..0072d4ed9d42a 100644 --- a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs +++ b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs @@ -309,10 +309,16 @@ impl LedgerMetadataDb { /// Delete committed block info indices starting from the specified version. pub(crate) fn truncate_block_info( &self, +<<<<<<< HEAD target_version: Version, batch: &SchemaBatch, ) -> Result<()> { let from_version = target_version + 1; +======= + from_version: Version, + batch: &SchemaBatch, + ) -> Result<()> { +>>>>>>> 8c9d76d94d (aptos-db: remove more on revert_commit) let mut iter = self .db .iter::(ReadOptions::default())?; diff --git a/storage/aptosdb/src/state_store/mod.rs b/storage/aptosdb/src/state_store/mod.rs index cbb2c9f2cf811..5b49ff58679b5 100644 --- a/storage/aptosdb/src/state_store/mod.rs +++ b/storage/aptosdb/src/state_store/mod.rs @@ -687,6 +687,24 @@ impl StateStore { ) } + pub fn revert_value_sets( + &self, + _first_version: Version, + _last_version: Version, + _ledger_batch: &SchemaBatch, + _sharded_state_kv_batches: &ShardedStateKvSchemaBatch, + _state_kv_metadata_batch: &SchemaBatch, + _put_state_value_indices: bool, + ) -> Result<()> { + // TODO: add tombstone entries to StaleStateValueIndexSchema + // TODO: revert usage statistics + // TODO: remove matching KV pairs from StateValueSchema + // TODO: remove matching state value indices from StateValueIndexSchema + // while the upstream uses them. + + Ok(()) + } + pub fn put_state_values( &self, value_state_sets: Vec<&ShardedStateUpdates>, From d29fa616e07040fc01447bc2d30455b80973bb8f Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 1 Jul 2024 14:08:09 +0300 Subject: [PATCH 129/147] fix: merge. --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 2 +- storage/aptosdb/src/ledger_db/ledger_metadata_db.rs | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index c649e8dabd47d..039ba42142e01 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -217,7 +217,7 @@ impl DbWriter for AptosDB { .try_lock() .expect("Concurrent committing detected."); - let latest_version = self.get_latest_version()?; + let latest_version = self.get_latest_ledger_info_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); // Update the provided ledger info and the overall commit progress diff --git a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs index 0072d4ed9d42a..596b3b276d457 100644 --- a/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs +++ b/storage/aptosdb/src/ledger_db/ledger_metadata_db.rs @@ -309,19 +309,12 @@ impl LedgerMetadataDb { /// Delete committed block info indices starting from the specified version. pub(crate) fn truncate_block_info( &self, -<<<<<<< HEAD - target_version: Version, - batch: &SchemaBatch, - ) -> Result<()> { - let from_version = target_version + 1; -======= from_version: Version, batch: &SchemaBatch, ) -> Result<()> { ->>>>>>> 8c9d76d94d (aptos-db: remove more on revert_commit) let mut iter = self .db - .iter::(ReadOptions::default())?; + .iter::()?; iter.seek(&from_version)?; while let Some((block_version, block_height)) = iter.next().transpose()? { batch.delete::(&block_height)?; From f7896c1d8326fa45a9806e04af48cc55fda55474 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 13:02:56 +0300 Subject: [PATCH 130/147] movement: revert mod genesis from upstream Replace todo! in struct value initializers with sensible values. Apply some typo fixes. --- crates/aptos/src/genesis/mod.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/aptos/src/genesis/mod.rs b/crates/aptos/src/genesis/mod.rs index e4b4c0bb281b5..7dabbc9c609e5 100644 --- a/crates/aptos/src/genesis/mod.rs +++ b/crates/aptos/src/genesis/mod.rs @@ -257,9 +257,9 @@ pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult CliTypedResult Date: Tue, 9 Jul 2024 13:20:43 +0300 Subject: [PATCH 131/147] Remove debug printouts not present upstream --- config/src/config/identity_config.rs | 10 +--------- crates/indexer/src/models/move_tables.rs | 5 ----- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/config/src/config/identity_config.rs b/config/src/config/identity_config.rs index 68688d6429b0d..9fcfd3aec0422 100644 --- a/config/src/config/identity_config.rs +++ b/config/src/config/identity_config.rs @@ -38,15 +38,7 @@ pub struct IdentityBlob { impl IdentityBlob { pub fn from_file(path: &Path) -> anyhow::Result { - let content = fs::read_to_string(path)?; - match serde_yaml::from_str(&content) { - Ok(identity_blob) => Ok(identity_blob), - Err(e) => { - eprintln!("Deserialization error: {:?}", e); - eprintln!("YAML content:\n{}", content); - Err(e.into()) - }, - } + Ok(serde_yaml::from_str(&fs::read_to_string(path)?)?) } pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { diff --git a/crates/indexer/src/models/move_tables.rs b/crates/indexer/src/models/move_tables.rs index 12732d31dee1c..73a4629018268 100644 --- a/crates/indexer/src/models/move_tables.rs +++ b/crates/indexer/src/models/move_tables.rs @@ -7,7 +7,6 @@ use crate::{ util::{hash_str, standardize_address}, }; use aptos_api_types::{DeleteTableItem, WriteTableItem}; -use aptos_logger::debug; use field_count::FieldCount; use serde::{Deserialize, Serialize}; @@ -57,10 +56,6 @@ impl TableItem { transaction_version: i64, transaction_block_height: i64, ) -> (Self, CurrentTableItem) { - debug!( - "Decoding WriteTableItem: {:?}", - write_table_item - ); ( Self { transaction_version, From a3ddc83666de2f383dc657c0b322a08f909fdcff Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 13:54:55 +0300 Subject: [PATCH 132/147] Remove nft-metadata-crawler-parser again --- Cargo.lock | 129 ------ Cargo.toml | 1 - .../nft-metadata-crawler-parser/Cargo.toml | 45 -- .../nft-metadata-crawler-parser/src/config.rs | 282 ------------ .../nft-metadata-crawler-parser/src/lib.rs | 37 -- .../nft-metadata-crawler-parser/src/main.rs | 11 - .../src/models/mod.rs | 6 - .../src/models/nft_metadata_crawler_uris.rs | 191 -------- .../models/nft_metadata_crawler_uris_query.rs | 128 ------ .../nft-metadata-crawler-parser/src/schema.rs | 31 -- .../src/utils/constants.rs | 29 -- .../src/utils/counters.rs | 235 ---------- .../src/utils/database.rs | 107 ----- .../src/utils/gcs.rs | 133 ------ .../src/utils/image_optimizer.rs | 165 ------- .../src/utils/json_parser.rs | 98 ---- .../src/utils/mod.rs | 10 - .../src/utils/uri_parser.rs | 141 ------ .../nft-metadata-crawler-parser/src/worker.rs | 434 ------------------ 19 files changed, 2213 deletions(-) delete mode 100644 ecosystem/nft-metadata-crawler-parser/Cargo.toml delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/config.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/lib.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/main.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/mod.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/schema.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/database.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/worker.rs diff --git a/Cargo.lock b/Cargo.lock index dd9078fbbc623..1d0f3ea3b6c47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2771,36 +2771,6 @@ dependencies = [ "url", ] -[[package]] -name = "aptos-nft-metadata-crawler-parser" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-indexer-grpc-server-framework", - "aptos-metrics-core", - "async-trait", - "backoff", - "bytes", - "chrono", - "clap 4.5.8", - "diesel", - "diesel_migrations", - "field_count", - "futures", - "google-cloud-storage", - "image", - "once_cell", - "regex", - "reqwest", - "serde", - "serde_json", - "sha256", - "tokio", - "tracing", - "url", - "warp", -] - [[package]] name = "aptos-node" version = "0.0.0-main" @@ -5130,12 +5100,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -7417,22 +7381,6 @@ dependencies = [ "sha3 0.8.2", ] -[[package]] -name = "exr" -version = "1.72.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" -dependencies = [ - "bit_field", - "flume", - "half 2.4.1", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - [[package]] name = "extract-ethereum-abi" version = "0.1.0" @@ -7656,15 +7604,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin 0.9.8", -] - [[package]] name = "fnv" version = "1.0.7" @@ -8052,16 +7991,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.29.0" @@ -8890,13 +8819,8 @@ dependencies = [ "bytemuck", "byteorder", "color_quant", - "exr", - "gif", - "jpeg-decoder", "num-traits", "png", - "qoi", - "tiff", ] [[package]] @@ -9236,15 +9160,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -9496,12 +9411,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "ledger-apdu" version = "0.10.0" @@ -12880,15 +12789,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "qstring" version = "0.7.2" @@ -14657,9 +14557,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -15192,17 +15089,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - [[package]] name = "time" version = "0.3.36" @@ -16530,12 +16416,6 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "which" version = "4.4.2" @@ -17016,12 +16896,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index 945de926e2129..540c6cab4b19e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,6 @@ members = [ "ecosystem/indexer-grpc/indexer-grpc-utils", "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/transaction-filter", - "ecosystem/nft-metadata-crawler-parser", "ecosystem/node-checker", "ecosystem/node-checker/fn-check-client", "execution/block-partitioner", diff --git a/ecosystem/nft-metadata-crawler-parser/Cargo.toml b/ecosystem/nft-metadata-crawler-parser/Cargo.toml deleted file mode 100644 index e04313d4c92ec..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "aptos-nft-metadata-crawler-parser" -description = "NFT Metadata Crawler Parser service." -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-metrics-core = { workspace = true } -async-trait = { workspace = true } -backoff = { workspace = true } -bytes = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true } -diesel = { workspace = true, features = [ - "chrono", - "postgres", - "r2d2", - "numeric", - "serde_json", -] } -diesel_migrations = { workspace = true } -field_count = { workspace = true } -futures = { workspace = true } -google-cloud-storage = { workspace = true } -image = { workspace = true } -once_cell = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -sha256 = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } -warp = { workspace = true } diff --git a/ecosystem/nft-metadata-crawler-parser/src/config.rs b/ecosystem/nft-metadata-crawler-parser/src/config.rs deleted file mode 100644 index f1409c6b26fca..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/config.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - utils::{ - constants::{ - DEFAULT_IMAGE_QUALITY, DEFAULT_MAX_FILE_SIZE_BYTES, DEFAULT_MAX_IMAGE_DIMENSIONS, - DEFAULT_MAX_NUM_PARSE_RETRIES, - }, - counters::{ - GOT_CONNECTION_COUNT, PARSER_FAIL_COUNT, PARSER_INVOCATIONS_COUNT, - PUBSUB_ACK_SUCCESS_COUNT, SKIP_URI_COUNT, UNABLE_TO_GET_CONNECTION_COUNT, - }, - database::{check_or_update_chain_id, establish_connection_pool, run_migrations}, - }, - worker::Worker, -}; -use aptos_indexer_grpc_server_framework::RunnableConfig; -use bytes::Bytes; -use diesel::{ - r2d2::{ConnectionManager, Pool}, - PgConnection, -}; -use google_cloud_storage::client::{Client as GCSClient, ClientConfig as GCSClientConfig}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; -use tracing::{error, info, warn}; -use warp::Filter; - -/// Structs to hold config from YAML -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ParserConfig { - pub google_application_credentials: Option, - pub bucket: String, - pub database_url: String, - pub cdn_prefix: String, - pub ipfs_prefix: String, - pub ipfs_auth_key: Option, - #[serde(default = "ParserConfig::default_max_file_size_bytes")] - pub max_file_size_bytes: u32, - #[serde(default = "ParserConfig::default_image_quality")] - pub image_quality: u8, // Quality up to 100 - #[serde(default = "ParserConfig::default_max_image_dimensions")] - pub max_image_dimensions: u32, - #[serde(default = "ParserConfig::default_max_num_parse_retries")] - pub max_num_parse_retries: i32, - #[serde(default)] - pub ack_parsed_uris: bool, - #[serde(default)] - pub uri_blacklist: Vec, - pub server_port: u16, -} - -impl ParserConfig { - pub const fn default_max_file_size_bytes() -> u32 { - DEFAULT_MAX_FILE_SIZE_BYTES - } - - pub const fn default_image_quality() -> u8 { - DEFAULT_IMAGE_QUALITY - } - - pub const fn default_max_image_dimensions() -> u32 { - DEFAULT_MAX_IMAGE_DIMENSIONS - } - - pub const fn default_max_num_parse_retries() -> i32 { - DEFAULT_MAX_NUM_PARSE_RETRIES - } -} - -#[async_trait::async_trait] -impl RunnableConfig for ParserConfig { - /// Main driver function that establishes a connection to Pubsub and parses the Pubsub entries in parallel - async fn run(&self) -> anyhow::Result<()> { - info!( - "[NFT Metadata Crawler] Starting parser with config: {:?}", - self - ); - - info!("[NFT Metadata Crawler] Connecting to database"); - let pool = establish_connection_pool(&self.database_url); - info!("[NFT Metadata Crawler] Database connection successful"); - - info!("[NFT Metadata Crawler] Running migrations"); - run_migrations(&pool); - info!("[NFT Metadata Crawler] Finished migrations"); - - if let Some(google_application_credentials) = &self.google_application_credentials { - info!( - "[NFT Metadata Crawler] Google Application Credentials path found, setting env var" - ); - std::env::set_var( - "GOOGLE_APPLICATION_CREDENTIALS", - google_application_credentials, - ); - } - - // Establish GCS client - let gcs_config = GCSClientConfig::default() - .with_auth() - .await - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to create gRPC client config" - ); - panic!(); - }); - - // Create request context - let context = Arc::new(ServerContext { - parser_config: Arc::new(self.clone()), - pool, - gcs_client: Arc::new(GCSClient::new(gcs_config)), - }); - - // Create web server - let route = warp::post() - .and(warp::path::end()) - .and(warp::body::bytes()) - .and(warp::any().map(move || context.clone())) - .and_then(handle_root); - warp::serve(route) - .run(([0, 0, 0, 0], self.server_port)) - .await; - Ok(()) - } - - fn get_server_name(&self) -> String { - "parser".to_string() - } -} - -/// Struct to hold context required for parsing -#[derive(Clone)] -pub struct ServerContext { - pub parser_config: Arc, - pub pool: Pool>, - pub gcs_client: Arc, -} - -/// Repeatedly pulls workers from Channel and perform parsing operations -async fn spawn_parser( - parser_config: Arc, - msg_base64: Bytes, - pool: Pool>, - gcs_client: Arc, -) { - PARSER_INVOCATIONS_COUNT.inc(); - let pubsub_message = String::from_utf8(msg_base64.to_vec()) - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse PubSub message" - ); - panic!(); - }) - .replace('\u{0000}', "") - .replace("\\u0000", ""); - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Received message from PubSub" - ); - - // Skips message if it does not have 5 commas (likely malformed URI) - if pubsub_message.matches(',').count() != 5 { - // Sends ack to PubSub only if ack_parsed_uris flag is true - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Number of commans != 5, skipping message" - ); - SKIP_URI_COUNT.with_label_values(&["invalid"]).inc(); - return; - } - - // Parse PubSub message - let parts: Vec<&str> = pubsub_message.split(',').collect(); - - // Perform chain id check - // If chain id is not set, set it - let mut conn = pool.get().unwrap_or_else(|e| { - error!( - pubsub_message = pubsub_message, - error = ?e, - "[NFT Metadata Crawler] Failed to get DB connection from pool"); - UNABLE_TO_GET_CONNECTION_COUNT.inc(); - panic!(); - }); - GOT_CONNECTION_COUNT.inc(); - - let grpc_chain_id = parts[4].parse::().unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse chain id from PubSub message" - ); - panic!(); - }); - - // Panic if chain id of PubSub message does not match chain id in DB - check_or_update_chain_id(&mut conn, grpc_chain_id as i64).expect("Chain id should match"); - - // Spawn worker - let last_transaction_version = parts[2].to_string().parse().unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse last transaction version from PubSub message" - ); - panic!(); - }); - let last_transaction_timestamp = - chrono::NaiveDateTime::parse_from_str(parts[3], "%Y-%m-%d %H:%M:%S %Z").unwrap_or( - chrono::NaiveDateTime::parse_from_str(parts[3], "%Y-%m-%d %H:%M:%S%.f %Z") - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse timestamp from PubSub message" - ); - panic!(); - }), - ); - let mut worker = Worker::new( - parser_config.clone(), - conn, - gcs_client.clone(), - &pubsub_message, - parts[0], - parts[1], - last_transaction_version, - last_transaction_timestamp, - parts[5].parse::().unwrap_or(false), - ); - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Starting worker" - ); - - if let Err(e) = worker.parse().await { - warn!( - pubsub_message = pubsub_message, - error = ?e, - "[NFT Metadata Crawler] Parsing failed" - ); - PARSER_FAIL_COUNT.inc(); - } - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Worker finished" - ); -} - -/// Handles calling parser for the root endpoint -async fn handle_root( - msg: Bytes, - context: Arc, -) -> Result { - // Use spawn_blocking to run the function on a separate thread. - let _ = tokio::spawn(spawn_parser( - context.parser_config.clone(), - msg, - context.pool.clone(), - context.gcs_client.clone(), - )) - .await; - - if !context.parser_config.ack_parsed_uris { - return Ok(warp::reply::with_status( - warp::reply(), - warp::http::StatusCode::BAD_REQUEST, - )); - } - - PUBSUB_ACK_SUCCESS_COUNT.inc(); - Ok(warp::reply::with_status( - warp::reply(), - warp::http::StatusCode::OK, - )) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/lib.rs b/ecosystem/nft-metadata-crawler-parser/src/lib.rs deleted file mode 100644 index 4beffe0b9d5b5..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod config; -pub mod models; -pub mod schema; -pub mod utils; -pub mod worker; - -use anyhow::Context; -use reqwest::{header, Client}; -use std::time::Duration; -use utils::constants::MAX_HEAD_REQUEST_RETRY_SECONDS; - -/// HEAD request to get MIME type and size of content -pub async fn get_uri_metadata(url: &str) -> anyhow::Result<(String, u32)> { - let client = Client::builder() - .timeout(Duration::from_secs(MAX_HEAD_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - let request = client.head(url.trim()); - let response = request.send().await?; - let headers = response.headers(); - - let mime_type = headers - .get(header::CONTENT_TYPE) - .map(|value| value.to_str().unwrap_or("text/plain")) - .unwrap_or("text/plain") - .to_string(); - let size = headers - .get(header::CONTENT_LENGTH) - .and_then(|value| value.to_str().ok()) - .and_then(|s| s.parse::().ok()) - .unwrap_or(0); - - Ok((mime_type, size)) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/main.rs b/ecosystem/nft-metadata-crawler-parser/src/main.rs deleted file mode 100644 index 13dfb560742a1..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/main.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_indexer_grpc_server_framework::ServerArgs; -use aptos_nft_metadata_crawler_parser::config::ParserConfig; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = ::parse(); - args.run::().await -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs b/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs deleted file mode 100644 index a034b1a23f72d..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod ledger_info; -pub mod nft_metadata_crawler_uris; -pub mod nft_metadata_crawler_uris_query; diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs b/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs deleted file mode 100644 index c991362feea80..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - models::nft_metadata_crawler_uris_query::NFTMetadataCrawlerURIsQuery, - schema::nft_metadata_crawler::parsed_asset_uris, -}; -use diesel::prelude::*; -use field_count::FieldCount; -use serde::{Deserialize, Serialize}; -use tracing::warn; - -#[derive(Clone, Debug, Deserialize, FieldCount, Identifiable, Insertable, Serialize)] -#[diesel(primary_key(asset_uri))] -#[diesel(table_name = parsed_asset_uris)] -pub struct NFTMetadataCrawlerURIs { - asset_uri: String, - raw_image_uri: Option, - raw_animation_uri: Option, - cdn_json_uri: Option, - cdn_image_uri: Option, - cdn_animation_uri: Option, - json_parser_retry_count: i32, - image_optimizer_retry_count: i32, - animation_optimizer_retry_count: i32, - do_not_parse: bool, - last_transaction_version: i64, -} - -impl NFTMetadataCrawlerURIs { - pub fn new(asset_uri: &str) -> Self { - Self { - asset_uri: asset_uri.to_string(), - raw_image_uri: None, - raw_animation_uri: None, - cdn_json_uri: None, - cdn_image_uri: None, - cdn_animation_uri: None, - json_parser_retry_count: 0, - image_optimizer_retry_count: 0, - animation_optimizer_retry_count: 0, - do_not_parse: false, - last_transaction_version: 0, - } - } - - pub fn get_asset_uri(&self) -> String { - self.asset_uri.clone() - } - - pub fn set_asset_uri(&mut self, asset_uri: String) { - self.asset_uri = asset_uri; - } - - pub fn get_raw_image_uri(&self) -> Option { - let uri = self.raw_image_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] raw_image_uri is None" - ); - } - uri - } - - pub fn set_raw_image_uri(&mut self, raw_image_uri: Option) { - self.raw_image_uri = raw_image_uri; - } - - pub fn get_raw_animation_uri(&self) -> Option { - let uri = self.raw_animation_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] raw_animation_uri is None" - ); - } - uri - } - - pub fn set_raw_animation_uri(&mut self, raw_animation_uri: Option) { - self.raw_animation_uri = raw_animation_uri; - } - - pub fn get_cdn_json_uri(&self) -> Option { - let uri = self.cdn_json_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_json_uri is None" - ); - } - uri - } - - pub fn set_cdn_json_uri(&mut self, cdn_json_uri: Option) { - self.cdn_json_uri = cdn_json_uri; - } - - pub fn get_cdn_image_uri(&self) -> Option { - let uri = self.cdn_image_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_image_uri is None" - ); - } - uri - } - - pub fn set_cdn_image_uri(&mut self, cdn_image_uri: Option) { - self.cdn_image_uri = cdn_image_uri; - } - - pub fn get_cdn_animation_uri(&self) -> Option { - let uri = self.cdn_animation_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_animation_uri is None" - ); - } - uri - } - - pub fn set_cdn_animation_uri(&mut self, cdn_animation_uri: Option) { - self.cdn_animation_uri = cdn_animation_uri; - } - - pub fn get_json_parser_retry_count(&self) -> i32 { - self.json_parser_retry_count - } - - pub fn increment_json_parser_retry_count(&mut self) { - self.json_parser_retry_count += 1; - } - - pub fn reset_json_parser_retry_count(&mut self) { - self.json_parser_retry_count = 0; - } - - pub fn get_image_optimizer_retry_count(&self) -> i32 { - self.image_optimizer_retry_count - } - - pub fn increment_image_optimizer_retry_count(&mut self) { - self.image_optimizer_retry_count += 1; - } - - pub fn get_animation_optimizer_retry_count(&self) -> i32 { - self.animation_optimizer_retry_count - } - - pub fn increment_animation_optimizer_retry_count(&mut self) { - self.animation_optimizer_retry_count += 1; - } - - pub fn get_do_not_parse(&self) -> bool { - self.do_not_parse - } - - pub fn set_do_not_parse(&mut self, do_not_parse: bool) { - self.do_not_parse = do_not_parse; - } - - pub fn get_last_transaction_version(&self) -> i64 { - self.last_transaction_version - } - - pub fn set_last_transaction_version(&mut self, last_transaction_version: i64) { - self.last_transaction_version = last_transaction_version; - } -} - -impl From for NFTMetadataCrawlerURIs { - fn from(query: NFTMetadataCrawlerURIsQuery) -> Self { - Self { - asset_uri: query.asset_uri, - raw_image_uri: query.raw_image_uri, - raw_animation_uri: query.raw_animation_uri, - cdn_json_uri: query.cdn_json_uri, - cdn_image_uri: query.cdn_image_uri, - cdn_animation_uri: query.cdn_animation_uri, - json_parser_retry_count: query.json_parser_retry_count, - image_optimizer_retry_count: query.image_optimizer_retry_count, - animation_optimizer_retry_count: query.animation_optimizer_retry_count, - do_not_parse: query.do_not_parse, - last_transaction_version: query.last_transaction_version, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs b/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs deleted file mode 100644 index 65ee71ebe7ad3..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - schema::nft_metadata_crawler::parsed_asset_uris, utils::constants::MAX_RETRY_TIME_SECONDS, -}; -use backoff::{retry, ExponentialBackoff}; -use diesel::{ - prelude::*, - r2d2::{ConnectionManager, PooledConnection}, -}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tracing::error; - -#[derive(Debug, Deserialize, Identifiable, Queryable, Serialize)] -#[diesel(primary_key(asset_uri))] -#[diesel(table_name = parsed_asset_uris)] -pub struct NFTMetadataCrawlerURIsQuery { - pub asset_uri: String, - pub raw_image_uri: Option, - pub raw_animation_uri: Option, - pub cdn_json_uri: Option, - pub cdn_image_uri: Option, - pub cdn_animation_uri: Option, - pub json_parser_retry_count: i32, - pub image_optimizer_retry_count: i32, - pub animation_optimizer_retry_count: i32, - pub inserted_at: chrono::NaiveDateTime, - pub do_not_parse: bool, - pub last_transaction_version: i64, -} - -impl NFTMetadataCrawlerURIsQuery { - pub fn get_by_asset_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .find(asset_uri) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_asset_uri"); - None - }) - } - - pub fn get_by_raw_image_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - raw_image_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .filter(parsed_asset_uris::raw_image_uri.eq(raw_image_uri)) - .filter(parsed_asset_uris::asset_uri.ne(asset_uri)) - .filter(parsed_asset_uris::cdn_image_uri.is_not_null()) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_raw_image_uri"); - None - }) - } - - pub fn get_by_raw_animation_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - raw_animation_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .filter(parsed_asset_uris::raw_animation_uri.eq(raw_animation_uri)) - .filter(parsed_asset_uris::asset_uri.ne(asset_uri)) - .filter(parsed_asset_uris::cdn_animation_uri.is_not_null()) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_raw_animation_uri"); - None - }) - } -} - -impl Default for NFTMetadataCrawlerURIsQuery { - fn default() -> Self { - Self { - asset_uri: "".to_string(), - raw_image_uri: None, - raw_animation_uri: None, - cdn_json_uri: None, - cdn_image_uri: None, - cdn_animation_uri: None, - json_parser_retry_count: 0, - image_optimizer_retry_count: 0, - animation_optimizer_retry_count: 0, - inserted_at: chrono::NaiveDateTime::default(), - do_not_parse: false, - last_transaction_version: 0, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/schema.rs b/ecosystem/nft-metadata-crawler-parser/src/schema.rs deleted file mode 100644 index ed7f8278bce59..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/schema.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -// @generated automatically by Diesel CLI. - -pub mod nft_metadata_crawler { - diesel::table! { - nft_metadata_crawler.ledger_infos (chain_id) { - chain_id -> Int8, - } - } - - diesel::table! { - nft_metadata_crawler.parsed_asset_uris (asset_uri) { - asset_uri -> Varchar, - raw_image_uri -> Nullable, - raw_animation_uri -> Nullable, - cdn_json_uri -> Nullable, - cdn_image_uri -> Nullable, - cdn_animation_uri -> Nullable, - json_parser_retry_count -> Int4, - image_optimizer_retry_count -> Int4, - animation_optimizer_retry_count -> Int4, - inserted_at -> Timestamp, - do_not_parse -> Bool, - last_transaction_version -> Int8, - } - } - - diesel::allow_tables_to_appear_in_same_query!(ledger_infos, parsed_asset_uris,); -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs deleted file mode 100644 index 4a7078b6283c3..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -/// Maximum retry time for exponential backoff (2 sec = 3-4 retries) -pub const MAX_RETRY_TIME_SECONDS: u64 = 2; - -/// Allocate 15 seconds for a HEAD request -pub const MAX_HEAD_REQUEST_RETRY_SECONDS: u64 = 15; - -/// Allocate 30 seconds for downloading large JSON files -pub const MAX_JSON_REQUEST_RETRY_SECONDS: u64 = 30; - -/// Allocate 90 seconds for downloading large image files -pub const MAX_IMAGE_REQUEST_RETRY_SECONDS: u64 = 90; - -/// Max number of retries for a given asset_uri -pub const DEFAULT_MAX_NUM_PARSE_RETRIES: i32 = 3; - -/// Default 15 MB maximum file size for files to be downloaded -pub const DEFAULT_MAX_FILE_SIZE_BYTES: u32 = 15_000_000; - -/// Default 100% image quality for image optimization -pub const DEFAULT_IMAGE_QUALITY: u8 = 100; - -/// Default 4096 maximum image dimensions for image optimization -pub const DEFAULT_MAX_IMAGE_DIMENSIONS: u32 = 4_096; - -/// Default IPFS gateway auth param key -pub const IPFS_AUTH_KEY: &str = "pinataGatewayToken"; diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs deleted file mode 100644 index efe9f53dfa229..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - register_int_counter, register_int_counter_vec, IntCounter, IntCounterVec, -}; -use once_cell::sync::Lazy; - -// OVERALL METRICS - -/// Number of times the NFT Metadata Crawler Parser has been invoked -pub static PARSER_INVOCATIONS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_invocation_count", - "Number of times the parser has been invoked", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has completed successfully -pub static PARSER_SUCCESSES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_success_count", - "Number of times the parser has completed successfully", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed -pub static PARSER_FAIL_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_fail_count", - "Number of times the parser has failed", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has received a URI marked as not to parse -pub static DO_NOT_PARSE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_do_not_parse_count", - "Number of times the parser received a URI marked as not to parse", - ) - .unwrap() -}); - -// PUBSUB METRICS - -/// Number of times a PubSub message has successfully been ACK'd -pub static PUBSUB_ACK_SUCCESS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_pubsub_ack_success_count", - "Number of times a PubSub message has successfully been ACK'd", - ) - .unwrap() -}); - -// POSTGRES METRICS - -/// Number of times the connection pool has timed out when trying to get a connection -pub static UNABLE_TO_GET_CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_connection_pool_err", - "Number of times the connection pool has timed out when trying to get a connection" - ) - .unwrap() -}); - -/// Number of times the connection pool got a connection -pub static GOT_CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_connection_pool_ok", - "Number of times the connection pool got a connection" - ) - .unwrap() -}); - -// DEDUPLICATION METRICS - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate asset URI -pub static DUPLICATE_ASSET_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_asset_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate asset URI" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate raw image URI -pub static DUPLICATE_RAW_IMAGE_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_raw_image_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate raw image URI" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate raw animation URI -pub static DUPLICATE_RAW_ANIMATION_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_raw_animation_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate raw animation URI" - ) - .unwrap() -}); - -// URI PARSER METRICS - -/// Number of URIs skipped because of matches on the URI skip list -pub static SKIP_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_skip_uri_count", - "Number of URIs skipped because of matches on the URI skip list", - &["reason"] - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has invocated the URI Parser -pub static PARSE_URI_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_parse_uri_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the URI Parser" - ) - .unwrap() -}); - -/// Number of times a given URI type has been parsed -pub static PARSE_URI_TYPE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_parse_uri_type_count", - "Number of times a given URI type has been parsed", - &["uri_type"] - ) - .unwrap() -}); - -// JSON PARSER METRICS - -/// Number of times the NFT Metadata Crawler has invocated the JSON Parser -pub static PARSE_JSON_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_parse_json_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the JSON Parser" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has been able to parse a JSON -pub static SUCCESSFULLY_PARSED_JSON_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_parsed_json_count", - "Number of times the NFT Metadata Crawler Parser has been able to parse a JSON" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed to parse a JSON and the error type -pub static FAILED_TO_PARSE_JSON_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_failed_to_parse_json_count", - "Number of times the NFT Metadata Crawler Parser has failed to parse a JSON", - &["error_type"] - ) - .unwrap() -}); - -// IMAGE OPTIMIZER METRICS - -/// Number of times the NFT Metadata Crawler Parser has invocated the Image Optimizer for an image -pub static OPTIMIZE_IMAGE_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_optimize_image_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the Image Optimizer for an image" - ) - .unwrap() -}); - -/// Number of times a given image type has been optimized -pub static OPTIMIZE_IMAGE_TYPE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_optimize_image_type_count", - "Number of times a given image type has been optimized", - &["image_type"] - ) - .unwrap() -}); - -/// Number of times the Image Optimizer has been able to optimize an image -pub static SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_optimized_image_count", - "Number of times the Image Optimizer has been able to optimize an image" - ) - .unwrap() -}); - -/// Number of times the Image Optimizer has failed to optimize an image and the error type -pub static FAILED_TO_OPTIMIZE_IMAGE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_failed_to_optimize_image_count", - "Number of times the NFT Metadata Crawler Parser has failed to optimize an image and the error type", - &["error_type"] - ) - .unwrap() -}); - -/// GCS METRICS - -/// Number of times the NFT Metadata Crawler Parser has attempted to upload to GCS -pub static GCS_UPLOAD_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_gcs_upload_invocation_count", - "Number of times the NFT Metadata Crawler Parser has attempted to upload to GCS" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has successfully uploaded to GCS -pub static SUCCESSFULLY_UPLOADED_TO_GCS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_uploaded_to_gcs_count", - "Number of times the NFT Metadata Crawler Parser has successfully uploaded to GCS" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed to upload to GCS -pub static FAILED_TO_UPLOAD_TO_GCS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_failed_to_upload_to_gcs_count", - "Number of times the NFT Metadata Crawler Parser has failed to upload to GCS" - ) - .unwrap() -}); diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs deleted file mode 100644 index 89202f8ffd783..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - models::{ledger_info::LedgerInfo, nft_metadata_crawler_uris::NFTMetadataCrawlerURIs}, - schema, -}; -use anyhow::Context; -use diesel::{ - r2d2::{ConnectionManager, Pool, PooledConnection}, - upsert::excluded, - ExpressionMethods, PgConnection, RunQueryDsl, -}; -use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; -use tracing::{debug, info}; - -pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); - -/// Establishes a connection pool to Postgres -pub fn establish_connection_pool(database_url: &str) -> Pool> { - let manager = ConnectionManager::::new(database_url); - Pool::builder() - .build(manager) - .expect("Failed to create pool.") -} - -/// Runs database migrations -pub fn run_migrations(pool: &Pool>) { - pool.get() - .expect("[NFT Metadata Crawler] Could not get connection for migrations") - .run_pending_migrations(MIGRATIONS) - .expect("[NFT Metadata Crawler] migrations failed!"); -} - -/// Upserts URIs into database -pub fn upsert_uris( - conn: &mut PooledConnection>, - entry: &NFTMetadataCrawlerURIs, - ltv: i64, -) -> anyhow::Result { - use schema::nft_metadata_crawler::parsed_asset_uris::dsl::*; - - let query = diesel::insert_into(schema::nft_metadata_crawler::parsed_asset_uris::table) - .values(entry) - .on_conflict(asset_uri) - .do_update() - .set(( - raw_image_uri.eq(excluded(raw_image_uri)), - raw_animation_uri.eq(excluded(raw_animation_uri)), - cdn_json_uri.eq(excluded(cdn_json_uri)), - cdn_image_uri.eq(excluded(cdn_image_uri)), - cdn_animation_uri.eq(excluded(cdn_animation_uri)), - image_optimizer_retry_count.eq(excluded(image_optimizer_retry_count)), - json_parser_retry_count.eq(excluded(json_parser_retry_count)), - animation_optimizer_retry_count.eq(excluded(animation_optimizer_retry_count)), - inserted_at.eq(excluded(inserted_at)), - do_not_parse.eq(excluded(do_not_parse)), - last_transaction_version.eq(ltv), - )); - - let debug_query = diesel::debug_query::(&query).to_string(); - debug!("Executing Query: {}", debug_query); - query.execute(conn).context(debug_query) -} - -/// Verify the chain id from PubSub against the database. -pub fn check_or_update_chain_id( - conn: &mut PooledConnection>, - pubsub_chain_id: i64, -) -> anyhow::Result { - info!("[NFT Metadata Crawler] Checking if chain id is correct"); - - let maybe_existing_chain_id = LedgerInfo::get(conn)?.map(|li| li.chain_id); - - match maybe_existing_chain_id { - Some(chain_id) => { - anyhow::ensure!(chain_id == pubsub_chain_id, "[NFT Metadata Crawler] Wrong chain detected! Trying to index chain {} now but existing data is for chain {}", pubsub_chain_id, chain_id); - info!( - chain_id = chain_id, - "[NFT Metadata Crawler] Chain id matches! Continue to index...", - ); - Ok(chain_id as u64) - }, - None => { - info!( - chain_id = pubsub_chain_id, - "[NFT Metadata Crawler] Adding chain id to db, continue to index.." - ); - insert_chain_id(conn, pubsub_chain_id).map(|_| pubsub_chain_id as u64) - }, - } -} - -/// Updates chain id in database -fn insert_chain_id( - conn: &mut PooledConnection>, - grpc_chain_id: i64, -) -> anyhow::Result { - let query = - diesel::insert_into(schema::nft_metadata_crawler::ledger_infos::table).values(LedgerInfo { - chain_id: grpc_chain_id, - }); - - let debug_query = diesel::debug_query::(&query).to_string(); - debug!("Executing Query: {}", debug_query); - query.execute(conn).context(debug_query) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs deleted file mode 100644 index 85bf42d9a4303..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils::{ - constants::MAX_RETRY_TIME_SECONDS, - counters::{ - FAILED_TO_UPLOAD_TO_GCS_COUNT, GCS_UPLOAD_INVOCATION_COUNT, - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use google_cloud_storage::{ - client::Client, - http::objects::upload::{Media, UploadObjectRequest, UploadType}, -}; -use image::ImageFormat; -use serde_json::Value; -use std::time::Duration; - -/// Writes JSON Value to GCS -pub async fn write_json_to_gcs( - bucket: &str, - uri: &str, - json: &Value, - client: &Client, -) -> anyhow::Result { - GCS_UPLOAD_INVOCATION_COUNT.inc(); - let hashed_uri = sha256::digest(uri); - let filename = format!("cdn/{}.json", hashed_uri); - let json_string = json.to_string(); - let json_bytes = json_string.into_bytes(); - - let upload_type = UploadType::Simple(Media { - name: filename.clone().into(), - content_type: "application/json".into(), - content_length: Some(json_bytes.len() as u64), - }); - - let op = || { - async { - Ok(client - .upload_object( - &UploadObjectRequest { - bucket: bucket.to_string(), - ..Default::default() - }, - json_bytes.clone(), - &upload_type, - ) - .await - .context("Error uploading JSON to GCS")?) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(_) => { - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT.inc(); - Ok(filename) - }, - Err(e) => { - FAILED_TO_UPLOAD_TO_GCS_COUNT.inc(); - Err(e) - }, - } -} - -/// Infers file type and writes image to GCS -pub async fn write_image_to_gcs( - img_format: ImageFormat, - bucket: &str, - uri: &str, - buffer: Vec, - client: &Client, -) -> anyhow::Result { - GCS_UPLOAD_INVOCATION_COUNT.inc(); - let hashed_uri = sha256::digest(uri); - let extension = match img_format { - ImageFormat::Gif | ImageFormat::Avif | ImageFormat::Png => img_format - .extensions_str() - .last() - .expect("ImageFormat should have at least one extension") - .to_string(), - _ => "jpeg".to_string(), - }; - - let filename = format!("cdn/{}.{}", hashed_uri, extension); - let upload_type = UploadType::Simple(Media { - name: filename.clone().into(), - content_type: format!("image/{}", extension).into(), - content_length: Some(buffer.len() as u64), - }); - - let op = || { - async { - Ok(client - .upload_object( - &UploadObjectRequest { - bucket: bucket.to_string(), - ..Default::default() - }, - buffer.clone(), - &upload_type, - ) - .await - .context("Error uploading image to GCS")?) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(_) => { - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT.inc(); - Ok(filename) - }, - Err(e) => { - FAILED_TO_UPLOAD_TO_GCS_COUNT.inc(); - Err(e) - }, - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs deleted file mode 100644 index 79354d58a8d90..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - get_uri_metadata, - utils::{ - constants::{MAX_IMAGE_REQUEST_RETRY_SECONDS, MAX_RETRY_TIME_SECONDS}, - counters::{ - FAILED_TO_OPTIMIZE_IMAGE_COUNT, OPTIMIZE_IMAGE_INVOCATION_COUNT, - SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT, - }, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use image::{ - imageops::{resize, FilterType}, - DynamicImage, GenericImageView, ImageBuffer, ImageFormat, ImageOutputFormat, -}; -use reqwest::Client; -use std::{ - cmp::{max, min}, - io::Cursor, - time::Duration, -}; -use tracing::{info, warn}; - -pub struct ImageOptimizer; - -impl ImageOptimizer { - /// Resizes and optimizes image from input URI. - /// Returns new image as a byte array and its format. - pub async fn optimize( - uri: &str, - max_file_size_bytes: u32, - image_quality: u8, - max_image_dimensions: u32, - ) -> anyhow::Result<(Vec, ImageFormat)> { - OPTIMIZE_IMAGE_INVOCATION_COUNT.inc(); - let (_, size) = get_uri_metadata(uri).await?; - if size > max_file_size_bytes { - FAILED_TO_OPTIMIZE_IMAGE_COUNT - .with_label_values(&["Image file too large"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "Image optimizer received file too large: {} bytes, skipping", - size - ))); - } - - let op = || { - async { - info!(image_uri = uri, "Sending request for image"); - - let client = Client::builder() - .timeout(Duration::from_secs(MAX_IMAGE_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - - let response = client - .get(uri.trim()) - .send() - .await - .context("Failed to get image")?; - - let img_bytes = response - .bytes() - .await - .context("Failed to load image bytes")?; - - let format = - image::guess_format(&img_bytes).context("Failed to guess image format")?; - - match format { - ImageFormat::Gif | ImageFormat::Avif => Ok((img_bytes.to_vec(), format)), - _ => { - let img = image::load_from_memory(&img_bytes) - .context(format!("Failed to load image from memory: {} bytes", size))?; - let (nwidth, nheight) = Self::calculate_dimensions_with_ration( - min(max(img.width(), img.height()), max_image_dimensions), - img.width(), - img.height(), - ); - let resized_image = - resize(&img.to_rgba8(), nwidth, nheight, FilterType::Gaussian); - Ok(Self::to_image_bytes(resized_image, image_quality)?) - }, - } - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(result) => { - SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT.inc(); - Ok(result) - }, - Err(e) => { - FAILED_TO_OPTIMIZE_IMAGE_COUNT - .with_label_values(&["other"]) - .inc(); - Err(e) - }, - } - } - - /// Calculate new dimensions given a goal size while maintaining original aspect ratio - fn calculate_dimensions_with_ration(goal: u32, width: u32, height: u32) -> (u32, u32) { - if width == 0 || height == 0 { - return (0, 0); - } - - if width > height { - let new_width = goal; - let new_height = (goal as f64 * (height as f64 / width as f64)).round() as u32; - (new_width, new_height) - } else { - let new_height = goal; - let new_width = (goal as f64 * (width as f64 / height as f64)).round() as u32; - (new_width, new_height) - } - } - - /// Checks if an image has any transparent pixels - fn has_transparent_pixels(img: &DynamicImage) -> bool { - let (width, height) = img.dimensions(); - for x in 0..width { - for y in 0..height { - if img.get_pixel(x, y)[3] < 255 { - return true; - } - } - } - false - } - - /// Converts image to image bytes vector - fn to_image_bytes( - image_buffer: ImageBuffer, Vec>, - image_quality: u8, - ) -> anyhow::Result<(Vec, ImageFormat)> { - let dynamic_image = DynamicImage::ImageRgba8(image_buffer); - let mut byte_store = Cursor::new(Vec::new()); - let mut encode_format = ImageOutputFormat::Jpeg(image_quality); - let mut output_format = ImageFormat::Jpeg; - if Self::has_transparent_pixels(&dynamic_image) { - encode_format = ImageOutputFormat::Png; - output_format = ImageFormat::Png; - } - - match dynamic_image.write_to(&mut byte_store, encode_format) { - Ok(_) => Ok((byte_store.into_inner(), output_format)), - Err(e) => { - warn!(error = ?e, "[NFT Metadata Crawler] Error converting image to bytes: {} bytes", dynamic_image.as_bytes().len()); - Err(anyhow::anyhow!(e)) - }, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs deleted file mode 100644 index 8700f9849da8b..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - get_uri_metadata, - utils::{ - constants::{MAX_JSON_REQUEST_RETRY_SECONDS, MAX_RETRY_TIME_SECONDS}, - counters::{ - FAILED_TO_PARSE_JSON_COUNT, PARSE_JSON_INVOCATION_COUNT, SUCCESSFULLY_PARSED_JSON_COUNT, - }, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use image::ImageFormat; -use reqwest::Client; -use serde_json::Value; -use std::time::Duration; -use tracing::info; - -pub struct JSONParser; - -impl JSONParser { - /// Parses JSON from input URI. - /// Returns the underlying raw image URI, raw animation URI, and JSON. - pub async fn parse( - uri: String, - max_file_size_bytes: u32, - ) -> anyhow::Result<(Option, Option, Value)> { - PARSE_JSON_INVOCATION_COUNT.inc(); - let (mime, size) = get_uri_metadata(&uri).await?; - if ImageFormat::from_mime_type(&mime).is_some() { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["found image instead"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "JSON parser received image file: {}, skipping", - mime - ))); - } else if size > max_file_size_bytes { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["json file too large"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "JSON parser received file too large: {} bytes, skipping", - size - ))); - } - - let op = || { - async { - info!(asset_uri = uri, "Sending request for asset_uri"); - - let client = Client::builder() - .timeout(Duration::from_secs(MAX_JSON_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - - let response = client - .get(uri.trim()) - .send() - .await - .context("Failed to get JSON")?; - - let parsed_json = response - .json::() - .await - .context("Failed to parse JSON")?; - - let raw_image_uri = parsed_json["image"].as_str().map(|s| s.to_string()); - let raw_animation_uri = - parsed_json["animation_url"].as_str().map(|s| s.to_string()); - - Ok((raw_image_uri, raw_animation_uri, parsed_json)) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(result) => { - SUCCESSFULLY_PARSED_JSON_COUNT.inc(); - Ok(result) - }, - Err(e) => { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["other"]) - .inc(); - Err(e) - }, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs deleted file mode 100644 index 1f91b7e7c32b8..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod constants; -pub mod counters; -pub mod database; -pub mod gcs; -pub mod image_optimizer; -pub mod json_parser; -pub mod uri_parser; diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs deleted file mode 100644 index ba16fa6f7367e..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils::{ - constants::IPFS_AUTH_KEY, - counters::{PARSE_URI_INVOCATION_COUNT, PARSE_URI_TYPE_COUNT}, -}; -use regex::{Captures, Regex}; -use url::Url; - -pub struct URIParser; - -impl URIParser { - /// Attempts to parse IPFS URI to use dedicated gateway. - /// Returns the original URI if parsing fails. - pub fn parse( - ipfs_prefix: &str, - uri: &str, - ipfs_auth_key: Option<&str>, - ) -> anyhow::Result { - PARSE_URI_INVOCATION_COUNT.inc(); - if uri.contains("arweave.net") { - PARSE_URI_TYPE_COUNT.with_label_values(&["arweave"]).inc(); - return Ok(uri.to_string()); - } - - let modified_uri = if uri.starts_with("ipfs://") { - uri.replace("ipfs://", "https://ipfs.com/ipfs/") - } else { - uri.to_string() - }; - - let ipfs_auth_param = if ipfs_auth_key.is_some() { - Some(format!("?{}={}", IPFS_AUTH_KEY, ipfs_auth_key.unwrap())) - } else { - None - }; - - // Expects the following format for provided URIs `ipfs/{CID}/{path}` - let re = Regex::new(r"^(ipfs/)(?P[a-zA-Z0-9]+)(?P/.*)?$")?; - - // Expects the following format for provided URIs `https://{CID}.ipfs.com/{path}` - let redir_re = Regex::new(r"https:\/\/(?P[^\.]+)\.ipfs\.[^\/]+(?P\/.+)?")?; - - let path = Url::parse(&modified_uri)? - .path_segments() - .map(|segments| segments.collect::>().join("/")); - - if let Some(captures) = re - .captures(&path.unwrap_or_default()) - .or_else(|| redir_re.captures(&modified_uri)) - { - return Self::format_capture(captures, ipfs_prefix, ipfs_auth_param); - } - Err(anyhow::anyhow!("Invalid IPFS URI")) - } - - /// Formats a capture group into a URI. - fn format_capture( - captures: Captures<'_>, - ipfs_prefix: &str, - ipfs_auth_param: Option, - ) -> anyhow::Result { - let cid = captures["cid"].to_string(); - let path = captures.name("path").map(|m| m.as_str().to_string()); - - PARSE_URI_TYPE_COUNT.with_label_values(&["ipfs"]).inc(); - Ok(format!( - "{}{}{}{}", - ipfs_prefix, - cid, - path.unwrap_or_default(), - ipfs_auth_param.unwrap_or_default() - )) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const IPFS_PREFIX: &str = "https://testipfsprefix.com/ipfs/"; - const IPFS_AUTH: &str = "token"; - const CID: &str = "testcid"; - const PATH: &str = "testpath"; - - #[test] - fn test_parse_ipfs_uri() { - let test_ipfs_uri = format!("ipfs://{}/{}", CID, PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri, Some(IPFS_AUTH)).unwrap(); - assert_eq!( - parsed_uri, - format!("{IPFS_PREFIX}{CID}/{PATH}?{IPFS_AUTH_KEY}={IPFS_AUTH}") - ); - - // Path is optional for IPFS URIs - let test_ipfs_uri_no_path = format!("ipfs://{}/{}", CID, ""); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri_no_path, None).unwrap(); - assert_eq!(parsed_uri, format!("{}{}/{}", IPFS_PREFIX, CID, "")); - - // IPFS URIs must contain a CID, expect error here - let test_ipfs_uri_no_cid = format!("ipfs://{}/{}", "", PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri_no_cid, None); - assert!(parsed_uri.is_err()); - } - - #[test] - fn test_parse_public_gateway_uri() { - let test_public_gateway_uri = format!("https://ipfs.io/ipfs/{}/{}", CID, PATH); - let parsed_uri = - URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri, Some(IPFS_AUTH)).unwrap(); - assert_eq!( - parsed_uri, - format!("{IPFS_PREFIX}{CID}/{PATH}?{IPFS_AUTH_KEY}={IPFS_AUTH}") - ); - - // Path is optional for public gateway URIs - let test_public_gateway_uri_no_path = format!("https://ipfs.io/ipfs/{}/{}", CID, ""); - let parsed_uri = - URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri_no_path, None).unwrap(); - assert_eq!(parsed_uri, format!("{}{}/{}", IPFS_PREFIX, CID, "")); - - // Some submitted URIs are in the redirected format - let test_ipfs_redirect = format!("https://{}.ipfs.re.dir.io/{}", CID, PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_redirect, None).unwrap(); - assert_eq!(parsed_uri, format!("{IPFS_PREFIX}{CID}/{PATH}")); - - // Public gateway URIs must contain a CID, expect error here - let test_public_gateway_uri_no_cid = format!("https://ipfs.io/ipfs/{}/{}", "", PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri_no_cid, None); - assert!(parsed_uri.is_err()); - } - - #[test] - fn test_parse_non_ipfs_uri_fail() { - // Expects an error if parsing a non-IPFS URI - let test_non_ipfs_uri = "https://tesetnotipfsuri.com/notipfspath.json".to_string(); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_non_ipfs_uri, None); - assert!(parsed_uri.is_err()); - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/worker.rs b/ecosystem/nft-metadata-crawler-parser/src/worker.rs deleted file mode 100644 index 885a40ec55787..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/worker.rs +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - config::ParserConfig, - models::{ - nft_metadata_crawler_uris::NFTMetadataCrawlerURIs, - nft_metadata_crawler_uris_query::NFTMetadataCrawlerURIsQuery, - }, - utils::{ - counters::{ - DUPLICATE_ASSET_URI_COUNT, DUPLICATE_RAW_ANIMATION_URI_COUNT, - DUPLICATE_RAW_IMAGE_URI_COUNT, OPTIMIZE_IMAGE_TYPE_COUNT, PARSER_SUCCESSES_COUNT, - PARSE_URI_TYPE_COUNT, SKIP_URI_COUNT, - }, - database::upsert_uris, - gcs::{write_image_to_gcs, write_json_to_gcs}, - image_optimizer::ImageOptimizer, - json_parser::JSONParser, - uri_parser::URIParser, - }, -}; -use diesel::{ - r2d2::{ConnectionManager, PooledConnection}, - PgConnection, -}; -use google_cloud_storage::client::Client as GCSClient; -use image::ImageFormat; -use serde_json::Value; -use std::sync::Arc; -use tracing::{error, info, warn}; -use url::Url; - -/// Stuct that represents a parser for a single entry from queue -pub struct Worker { - config: Arc, - conn: PooledConnection>, - gcs_client: Arc, - pubsub_message: String, - model: NFTMetadataCrawlerURIs, - asset_data_id: String, - asset_uri: String, - last_transaction_version: i32, - last_transaction_timestamp: chrono::NaiveDateTime, - force: bool, -} - -impl Worker { - pub fn new( - config: Arc, - conn: PooledConnection>, - gcs_client: Arc, - pubsub_message: &str, - asset_data_id: &str, - asset_uri: &str, - last_transaction_version: i32, - last_transaction_timestamp: chrono::NaiveDateTime, - force: bool, - ) -> Self { - let model = NFTMetadataCrawlerURIs::new(asset_uri); - let worker = Self { - config, - conn, - gcs_client, - pubsub_message: pubsub_message.to_string(), - model, - asset_data_id: asset_data_id.to_string(), - asset_uri: asset_uri.to_string(), - last_transaction_version, - last_transaction_timestamp, - force, - }; - worker.log_info("Created worker"); - worker - } - - /// Main parsing flow - pub async fn parse(&mut self) -> anyhow::Result<()> { - // Deduplicate asset_uri - // Exit if not force or if asset_uri has already been parsed - let prev_model = - NFTMetadataCrawlerURIsQuery::get_by_asset_uri(&mut self.conn, &self.asset_uri); - if let Some(pm) = prev_model { - DUPLICATE_ASSET_URI_COUNT.inc(); - self.model = pm.into(); - if !self.force && self.model.get_do_not_parse() { - self.log_info("asset_uri has been marked as do_not_parse, skipping parse"); - SKIP_URI_COUNT.with_label_values(&["do_not_parse"]).inc(); - self.upsert(); - return Ok(()); - } - } - - // Check asset_uri against the URI blacklist - if self.is_blacklisted_uri(&self.asset_uri.clone()) { - self.log_info("Found match in URI blacklist, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - SKIP_URI_COUNT.with_label_values(&["blacklist"]).inc(); - return Ok(()); - } - - // Skip if asset_uri is not a valid URI, do not write invalid URI to Postgres - if Url::parse(&self.asset_uri).is_err() { - self.log_info("URI is invalid, skipping parse, marking as do_not_parse"); - self.model.set_do_not_parse(true); - SKIP_URI_COUNT.with_label_values(&["invalid"]).inc(); - return Ok(()); - } - - if self.force || self.model.get_cdn_json_uri().is_none() { - // Parse asset_uri - self.log_info("Parsing asset_uri"); - let json_uri = URIParser::parse( - &self.config.ipfs_prefix, - &self.model.get_asset_uri(), - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse asset_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - self.model.get_asset_uri() - }); - - // Parse JSON for raw_image_uri and raw_animation_uri - self.log_info("Starting JSON parsing"); - let (raw_image_uri, raw_animation_uri, json) = - JSONParser::parse(json_uri, self.config.max_file_size_bytes) - .await - .unwrap_or_else(|e| { - // Increment retry count if JSON parsing fails - self.log_warn("JSON parsing failed", Some(&e)); - self.model.increment_json_parser_retry_count(); - (None, None, Value::Null) - }); - - self.model.set_raw_image_uri(raw_image_uri); - self.model.set_raw_animation_uri(raw_animation_uri); - - // Save parsed JSON to GCS - if json != Value::Null { - self.log_info("Writing JSON to GCS"); - let cdn_json_uri_result = write_json_to_gcs( - &self.config.bucket, - &self.asset_uri, - &json, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_json_uri_result.as_ref() { - self.log_warn( - "Failed to write JSON to GCS, maybe upload timed out?", - Some(e), - ); - } - - let cdn_json_uri = cdn_json_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_json_uri(cdn_json_uri); - } - - // Commit model to Postgres - self.log_info("Committing JSON to Postgres"); - self.upsert(); - } - - // Should I optimize image? - // if force: true - // else if cdn_image_uri already exists: false - // else: perform lookup - // if found: set cdn_image_uri, false - // else: true - let should_optimize_image = if self.force { - true - } else if self.model.get_cdn_image_uri().is_some() { - false - } else { - self.model.get_raw_image_uri().map_or(true, |uri| { - match NFTMetadataCrawlerURIsQuery::get_by_raw_image_uri( - &mut self.conn, - &self.asset_uri, - &uri, - ) { - Some(uris) => { - self.log_info("Duplicate raw_image_uri found"); - DUPLICATE_RAW_IMAGE_URI_COUNT.inc(); - self.model.set_cdn_image_uri(uris.cdn_image_uri); - self.upsert(); - false - }, - None => true, - } - }) - }; - - if should_optimize_image { - // Parse raw_image_uri, use asset_uri if parsing fails - self.log_info("Parsing raw_image_uri"); - let raw_image_uri = self - .model - .get_raw_image_uri() - .unwrap_or(self.model.get_asset_uri()); - - // Check raw_image_uri against the URI blacklist - if self.is_blacklisted_uri(&raw_image_uri) { - self.log_info("Found match in URI blacklist, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - SKIP_URI_COUNT.with_label_values(&["blacklist"]).inc(); - return Ok(()); - } - - let img_uri = URIParser::parse( - &self.config.ipfs_prefix, - &raw_image_uri, - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse raw_image_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - raw_image_uri.clone() - }); - - // Resize and optimize image - self.log_info("Starting image optimization"); - OPTIMIZE_IMAGE_TYPE_COUNT - .with_label_values(&["image"]) - .inc(); - let (image, format) = ImageOptimizer::optimize( - &img_uri, - self.config.max_file_size_bytes, - self.config.image_quality, - self.config.max_image_dimensions, - ) - .await - .unwrap_or_else(|e| { - // Increment retry count if image is None - self.log_warn("Image optimization failed", Some(&e)); - self.model.increment_image_optimizer_retry_count(); - (vec![], ImageFormat::Png) - }); - - // Save resized and optimized image to GCS - if !image.is_empty() { - self.log_info("Writing image to GCS"); - let cdn_image_uri_result = write_image_to_gcs( - format, - &self.config.bucket, - &raw_image_uri, - image, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_image_uri_result.as_ref() { - self.log_warn( - "Failed to write image to GCS, maybe upload timed out?", - Some(e), - ); - } - - let cdn_image_uri = cdn_image_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_image_uri(cdn_image_uri); - self.model.reset_json_parser_retry_count(); - } - - // Commit model to Postgres - self.log_info("Committing image to Postgres"); - self.upsert(); - } - - // Should I optimize animation? - // if force: true - // else if cdn_animation_uri already exists: false - // else: perform lookup - // if found: set cdn_animation_uri, false - // else: true - let raw_animation_uri_option = if self.force { - self.model.get_raw_animation_uri() - } else if self.model.get_cdn_animation_uri().is_some() { - None - } else { - self.model.get_raw_animation_uri().and_then(|uri| { - match NFTMetadataCrawlerURIsQuery::get_by_raw_animation_uri( - &mut self.conn, - &self.asset_uri, - &uri, - ) { - Some(uris) => { - self.log_info("Duplicate raw_image_uri found"); - DUPLICATE_RAW_ANIMATION_URI_COUNT.inc(); - self.model.set_cdn_animation_uri(uris.cdn_animation_uri); - self.upsert(); - None - }, - None => Some(uri), - } - }) - }; - - // If raw_animation_uri_option is None, skip - if let Some(raw_animation_uri) = raw_animation_uri_option { - self.log_info("Parsing raw_animation_uri"); - let animation_uri = URIParser::parse( - &self.config.ipfs_prefix, - &raw_animation_uri, - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse raw_animation_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - raw_animation_uri.clone() - }); - - // Resize and optimize animation - self.log_info("Starting animation optimization"); - OPTIMIZE_IMAGE_TYPE_COUNT - .with_label_values(&["animation"]) - .inc(); - let (animation, format) = ImageOptimizer::optimize( - &animation_uri, - self.config.max_file_size_bytes, - self.config.image_quality, - self.config.max_image_dimensions, - ) - .await - .unwrap_or_else(|e| { - // Increment retry count if animation is None - self.log_warn("Animation optimization failed", Some(&e)); - self.model.increment_animation_optimizer_retry_count(); - (vec![], ImageFormat::Png) - }); - - // Save resized and optimized animation to GCS - if !animation.is_empty() { - self.log_info("Writing animation to GCS"); - let cdn_animation_uri_result = write_image_to_gcs( - format, - &self.config.bucket, - &raw_animation_uri, - animation, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_animation_uri_result.as_ref() { - self.log_error("Failed to write animation to GCS", e); - } - - let cdn_animation_uri = cdn_animation_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_animation_uri(cdn_animation_uri); - } - - // Commit model to Postgres - self.log_info("Committing animation to Postgres"); - self.upsert(); - } - - if self.model.get_json_parser_retry_count() >= self.config.max_num_parse_retries - || self.model.get_image_optimizer_retry_count() >= self.config.max_num_parse_retries - || self.model.get_animation_optimizer_retry_count() >= self.config.max_num_parse_retries - { - self.log_info("Retry count exceeded, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - } - - PARSER_SUCCESSES_COUNT.inc(); - Ok(()) - } - - fn upsert(&mut self) { - upsert_uris( - &mut self.conn, - &self.model, - self.last_transaction_version as i64, - ) - .unwrap_or_else(|e| { - self.log_error("Commit to Postgres failed", &e); - panic!(); - }); - } - - fn is_blacklisted_uri(&mut self, uri: &str) -> bool { - self.config - .uri_blacklist - .iter() - .any(|blacklist_uri| uri.contains(blacklist_uri)) - } - - fn log_info(&self, message: &str) { - info!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - "[NFT Metadata Crawler] {}", - message - ); - } - - fn log_warn(&self, message: &str, e: Option<&anyhow::Error>) { - warn!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - error = ?e, - "[NFT Metadata Crawler] {}", - message - ); - } - - fn log_error(&self, message: &str, e: &anyhow::Error) { - error!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - error = ?e, - "[NFT Metadata Crawler] {}", - message - ); - } -} From db4d9e178e93fec736afc2b553620f6e84bced20 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 14:43:53 +0300 Subject: [PATCH 133/147] aptos-storage-interface: recover tests --- Cargo.lock | 1 + storage/storage-interface/Cargo.toml | 4 +- .../storage-interface/src/finality_view.rs | 8 +-- storage/storage-interface/src/mock.rs | 49 +++++++++++++++++-- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d0f3ea3b6c47..b0ee6efcf6b7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3503,6 +3503,7 @@ dependencies = [ "bcs 0.1.4", "crossbeam-channel", "dashmap", + "move-core-types", "once_cell", "parking_lot 0.12.3", "proptest", diff --git a/storage/storage-interface/Cargo.toml b/storage/storage-interface/Cargo.toml index 6b2cb638242b1..09c12a6efdda3 100644 --- a/storage/storage-interface/Cargo.toml +++ b/storage/storage-interface/Cargo.toml @@ -25,6 +25,7 @@ aptos-vm = { workspace = true } bcs = { workspace = true } crossbeam-channel = { workspace = true } dashmap = { workspace = true } +move-core-types = { workspace = true, optional = true } once_cell = { workspace = true } parking_lot = { workspace = true } proptest = { workspace = true } @@ -37,10 +38,11 @@ threadpool = { workspace = true } [dev-dependencies] aptos-types = { workspace = true, features = ["fuzzing"] } assert_unordered = { workspace = true } +move-core-types = { workspace = true } [features] default = [] -fuzzing = ["aptos-types/fuzzing"] +fuzzing = ["aptos-types/fuzzing", "move-core-types"] [package.metadata.cargo-machete] ignored = ["proptest"] diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 6ae4184bf6aa0..7ed218fd0b1b9 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -134,13 +134,13 @@ mod tests { } #[test] - fn test_get_latest_version() -> anyhow::Result<()> { + fn test_get_synced_version() -> anyhow::Result<()> { let view = FinalityView::new(MockDbReaderWriter); - let res = view.get_latest_version(); + let res = view.get_synced_version(); assert!(res.is_err()); let blockheight = 1; view.set_finalized_block_height(blockheight)?; - let version = view.get_latest_version()?; + let version = view.get_synced_version()?; assert_eq!(version, 1); Ok(()) } @@ -163,7 +163,7 @@ mod tests { view.set_finalized_block_height(1)?; let latest_state_view = reader.latest_state_checkpoint_view()?; // TODO: modify mock so we get different states for different versions - let key = StateKey::raw(vec![1]); + let key = StateKey::raw(&[1]); let value = latest_state_view.get_state_value(&key)?.unwrap(); assert_eq!(value.bytes(), &vec![1]); Ok(()) diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs index 5d3d367cc35b4..cb6b926d0dd8e 100644 --- a/storage/storage-interface/src/mock.rs +++ b/storage/storage-interface/src/mock.rs @@ -5,14 +5,16 @@ //! This module provides mock dbreader for tests. use crate::{errors::AptosDbError, DbReader, DbWriter, Result}; +use aptos_crypto::HashValue; use aptos_types::{ + account_address::AccountAddress, + account_config::AccountResource, + event::EventHandle, proof::SparseMerkleProofExt, - state_store::{ - state_key::{inner::StateKeyInner, StateKey}, - state_value::StateValue, - }, + state_store::{state_key::inner::StateKeyInner, state_key::StateKey, state_value::StateValue}, transaction::Version, }; +use move_core_types::move_resource::MoveResource; /// This is a mock of the DbReaderWriter in tests. pub struct MockDbReaderWriter; @@ -38,7 +40,20 @@ impl DbReader for MockDbReaderWriter { _: Version, ) -> Result> { match state_key.inner() { - StateKeyInner::AccessPath(..) => Ok(None), + StateKeyInner::AccessPath(access_path) => { + if access_path.path == AccountResource::resource_path() { + let account_resource = AccountResource::new( + 0, + vec![], + EventHandle::random(0), + EventHandle::random(0), + ); + let value = bcs::to_bytes(&account_resource).unwrap(); + Ok(Some(value.into())) + } else { + Ok(None) + } + }, StateKeyInner::Raw(raw_key) => Ok(Some(StateValue::from(raw_key.to_owned()))), _ => Err(AptosDbError::Other(format!( "Not supported state key type {:?}", @@ -56,6 +71,30 @@ impl DbReader for MockDbReaderWriter { .get_state_value_by_version(state_key, version)? .map(|value| (version, value))) } + + fn get_block_info_by_height( + &self, + height: u64, + ) -> Result<(Version, Version, aptos_types::account_config::NewBlockEvent)> { + Ok(( + 0, + 1, + aptos_types::account_config::NewBlockEvent::new( + AccountAddress::ONE, + 0, + 0, + height, + vec![], + AccountAddress::TWO, + vec![], + 0, + ), + )) + } + + fn get_accumulator_root_hash(&self, _version: Version) -> Result { + Ok(HashValue::zero()) + } } impl DbWriter for MockDbReaderWriter {} From 986a639935c1a13965bf3214e6339c6674f141b3 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 17:43:41 +0300 Subject: [PATCH 134/147] Restore crates from upstream When syncing with upstream, merge conflicts of the "changed by them/ removed by us" kind have been resolved by updating the conflicting files to the latest upstream revision. This only partially recovered the source for crates that have been removed for Monza. Restore the crates fully. Some of the changes made for Monza are overwritten from upstream to reduce the delta and allow resurrecting some crates erased in the work for Monza: - ring dependency removal from aptos-crypto, moving the noise implementation to aptos-network; - removal of tokio synchronization slots from aptos-types. This means this branch no longer supports Monza, and those changes would need to be re-applied if the Monza direction is taken again. --- Cargo.lock | 4 +- consensus/README.md | 59 + consensus/consensus-types/Cargo.toml | 1 + consensus/consensus-types/src/common.rs | 51 +- consensus/consensus-types/src/lib.rs | 1 + consensus/src/consensusdb/consensusdb_test.rs | 118 ++ consensus/src/consensusdb/schema/block/mod.rs | 47 + .../src/consensusdb/schema/block/test.rs | 14 + consensus/src/consensusdb/schema/mod.rs | 41 + .../schema/quorum_certificate/mod.rs | 47 + .../schema/quorum_certificate/test.rs | 15 + .../consensusdb/schema/single_entry/mod.rs | 71 ++ .../consensusdb/schema/single_entry/test.rs | 14 + .../leader_reputation_adapter.rs | 145 +++ consensus/src/dag/anchor_election/mod.rs | 21 + .../src/dag/anchor_election/round_robin.rs | 33 + consensus/src/dag/dag_fetcher.rs | 438 +++++++ consensus/src/dag/dag_store.rs | 548 +++++++++ consensus/src/dag/mod.rs | 32 + consensus/src/dag/observability/counters.rs | 69 ++ consensus/src/dag/observability/logging.rs | 44 + consensus/src/dag/observability/mod.rs | 6 + consensus/src/dag/observability/tracing.rs | 40 + consensus/src/dag/order_rule.rs | 261 +++++ consensus/src/dag/rb_handler.rs | 263 +++++ consensus/src/dag/round_state.rs | 205 ++++ consensus/src/dag/storage.rs | 72 ++ consensus/src/dag/tests/dag_test.rs | 317 ++++++ consensus/src/dag/tests/mod.rs | 13 + consensus/src/dag/tests/rb_handler_tests.rs | 258 +++++ consensus/src/dag/types.rs | 1003 +++++++++++++++++ consensus/src/error.rs | 108 ++ consensus/src/execution_pipeline.rs | 272 +++++ .../src/liveness/cached_proposer_election.rs | 69 ++ .../liveness/cached_proposer_election_test.rs | 69 ++ consensus/src/liveness/mod.rs | 25 + consensus/src/liveness/proposal_generator.rs | 2 +- consensus/src/liveness/proposer_election.rs | 85 ++ .../liveness/rotating_proposer_election.rs | 41 + .../src/liveness/rotating_proposer_test.rs | 59 + .../src/liveness/round_proposer_election.rs | 34 + consensus/src/liveness/round_proposer_test.rs | 41 + .../liveness/unequivocal_proposer_election.rs | 88 ++ .../unequivocal_proposer_election_test.rs | 106 ++ consensus/src/pending_votes.rs | 579 ++++++++++ consensus/src/pipeline/buffer.rs | 256 +++++ consensus/src/pipeline/errors.rs | 20 + consensus/src/pipeline/execution_phase.rs | 92 ++ .../src/pipeline/execution_schedule_phase.rs | 110 ++ .../src/pipeline/execution_wait_phase.rs | 55 + consensus/src/pipeline/hashable.rs | 9 + consensus/src/pipeline/linkedlist.rs | 322 ++++++ consensus/src/pipeline/mod.rs | 40 + consensus/src/pipeline/persisting_phase.rs | 75 ++ consensus/src/pipeline/pipeline_phase.rs | 99 ++ consensus/src/pipeline/signing_phase.rs | 84 ++ .../pipeline/tests/execution_phase_tests.rs | 171 +++ .../src/pipeline/tests/integration_tests.rs | 5 + consensus/src/pipeline/tests/mod.rs | 11 + .../tests/ordering_state_computer_tests.rs | 8 + consensus/src/pipeline/tests/phase_tester.rs | 103 ++ .../src/pipeline/tests/signing_phase_tests.rs | 146 +++ consensus/src/qc_aggregator.rs | 181 +++ consensus/src/quorum_store/batch_requester.rs | 202 ++++ consensus/src/quorum_store/batch_store.rs | 459 ++++++++ consensus/src/quorum_store/mod.rs | 23 + .../src/quorum_store/network_listener.rs | 92 ++ .../quorum_store/quorum_store_coordinator.rs | 158 +++ consensus/src/quorum_store/schema.rs | 74 ++ .../tests/batch_requester_test.rs | 258 +++++ .../quorum_store/tests/batch_store_test.rs | 283 +++++ consensus/src/quorum_store/tests/mod.rs | 12 + .../tests/quorum_store_db_test.rs | 86 ++ .../src/quorum_store/tests/types_test.rs | 42 + consensus/src/quorum_store/utils.rs | 3 + consensus/src/rand/dkg/mod.rs | 2 + consensus/src/rand/mod.rs | 7 + consensus/src/rand/rand_gen/mod.rs | 15 + .../src/rand/rand_gen/storage/in_memory.rs | 78 ++ .../src/rand/rand_gen/storage/interface.rs | 23 + consensus/src/rand/rand_gen/storage/mod.rs | 6 + consensus/src/rand/rand_gen/storage/schema.rs | 96 ++ consensus/src/state_replication.rs | 84 ++ .../test_utils/mock_quorum_store_sender.rs | 85 ++ .../src/test_utils/mock_state_computer.rs | 161 +++ consensus/src/transaction_deduper.rs | 33 + .../fairness/conflict_key/entry_fun.rs | 45 + .../fairness/conflict_key/entry_fun_module.rs | 39 + .../fairness/conflict_key/test_utils.rs | 190 ++++ .../fairness/conflict_key/txn_sender.rs | 19 + .../fairness/conflict_zone.rs | 69 ++ .../src/transaction_shuffler/fairness/mod.rs | 220 ++++ .../fairness/pending_zone.rs | 70 ++ .../fairness/selection_tracker.rs | 43 + .../fairness/tests/manual.rs | 169 +++ .../fairness/tests/mod.rs | 5 + .../fairness/tests/proptests.rs | 104 ++ consensus/src/transaction_shuffler/mod.rs | 69 ++ .../src/transaction_shuffler/sender_aware.rs | 532 +++++++++ consensus/src/twins/basic_twins_test.rs | 315 ++++++ consensus/src/twins/mod.rs | 6 + consensus/src/util/db_tool.rs | 107 ++ consensus/src/util/mock_time_service.rs | 138 +++ consensus/src/util/mod.rs | 24 + consensus/src/util/time_service.rs | 148 +++ crates/aptos-crypto/Cargo.toml | 5 + crates/aptos-crypto/README.md | 3 + crates/aptos-crypto/benches/noise.rs | 132 +++ crates/aptos-crypto/src/lib.rs | 1 + .../aptos-crypto/src/noise.rs | 10 +- crates/aptos-crypto/src/unit_tests/mod.rs | 1 + .../aptos-crypto/src/unit_tests/noise_test.rs | 532 +++++++++ crates/aptos-debugger/src/lib.rs | 35 + crates/aptos-telemetry-service/src/auth.rs | 205 ++++ .../src/clients/humio.rs | 55 + .../aptos-telemetry-service/src/constants.rs | 19 + .../src/custom_event.rs | 222 ++++ crates/aptos-telemetry-service/src/errors.rs | 189 ++++ .../aptos-telemetry-service/src/gcp_logger.rs | 66 ++ crates/aptos-telemetry-service/src/index.rs | 111 ++ .../aptos-telemetry-service/src/jwt_auth.rs | 193 ++++ .../aptos-telemetry-service/src/log_ingest.rs | 138 +++ crates/aptos-telemetry-service/src/main.rs | 13 + .../src/remote_config.rs | 82 ++ .../src/tests/auth_test.rs | 319 ++++++ .../src/tests/custom_event.rs | 59 + .../aptos-telemetry-service/src/tests/mod.rs | 6 + .../aptos-telemetry-service/src/types/auth.rs | 65 ++ .../aptos-telemetry-service/src/types/mod.rs | 121 ++ .../src/types/telemetry.rs | 31 + .../src/validator_cache.rs | 315 ++++++ network/framework/Cargo.toml | 2 - network/framework/src/noise/error.rs | 2 +- network/framework/src/noise/fuzzing.rs | 7 +- network/framework/src/noise/handshake.rs | 23 +- network/framework/src/noise/mod.rs | 1 - network/framework/src/noise/stream.rs | 33 +- 137 files changed, 15152 insertions(+), 54 deletions(-) create mode 100644 consensus/README.md create mode 100644 consensus/src/consensusdb/consensusdb_test.rs create mode 100644 consensus/src/consensusdb/schema/block/mod.rs create mode 100644 consensus/src/consensusdb/schema/block/test.rs create mode 100644 consensus/src/consensusdb/schema/mod.rs create mode 100644 consensus/src/consensusdb/schema/quorum_certificate/mod.rs create mode 100644 consensus/src/consensusdb/schema/quorum_certificate/test.rs create mode 100644 consensus/src/consensusdb/schema/single_entry/mod.rs create mode 100644 consensus/src/consensusdb/schema/single_entry/test.rs create mode 100644 consensus/src/dag/anchor_election/leader_reputation_adapter.rs create mode 100644 consensus/src/dag/anchor_election/mod.rs create mode 100644 consensus/src/dag/anchor_election/round_robin.rs create mode 100644 consensus/src/dag/dag_fetcher.rs create mode 100644 consensus/src/dag/dag_store.rs create mode 100644 consensus/src/dag/mod.rs create mode 100644 consensus/src/dag/observability/counters.rs create mode 100644 consensus/src/dag/observability/logging.rs create mode 100644 consensus/src/dag/observability/mod.rs create mode 100644 consensus/src/dag/observability/tracing.rs create mode 100644 consensus/src/dag/order_rule.rs create mode 100644 consensus/src/dag/rb_handler.rs create mode 100644 consensus/src/dag/round_state.rs create mode 100644 consensus/src/dag/storage.rs create mode 100644 consensus/src/dag/tests/dag_test.rs create mode 100644 consensus/src/dag/tests/mod.rs create mode 100644 consensus/src/dag/tests/rb_handler_tests.rs create mode 100644 consensus/src/dag/types.rs create mode 100644 consensus/src/error.rs create mode 100644 consensus/src/execution_pipeline.rs create mode 100644 consensus/src/liveness/cached_proposer_election.rs create mode 100644 consensus/src/liveness/cached_proposer_election_test.rs create mode 100644 consensus/src/liveness/mod.rs create mode 100644 consensus/src/liveness/proposer_election.rs create mode 100644 consensus/src/liveness/rotating_proposer_election.rs create mode 100644 consensus/src/liveness/rotating_proposer_test.rs create mode 100644 consensus/src/liveness/round_proposer_election.rs create mode 100644 consensus/src/liveness/round_proposer_test.rs create mode 100644 consensus/src/liveness/unequivocal_proposer_election.rs create mode 100644 consensus/src/liveness/unequivocal_proposer_election_test.rs create mode 100644 consensus/src/pending_votes.rs create mode 100644 consensus/src/pipeline/buffer.rs create mode 100644 consensus/src/pipeline/errors.rs create mode 100644 consensus/src/pipeline/execution_phase.rs create mode 100644 consensus/src/pipeline/execution_schedule_phase.rs create mode 100644 consensus/src/pipeline/execution_wait_phase.rs create mode 100644 consensus/src/pipeline/hashable.rs create mode 100644 consensus/src/pipeline/linkedlist.rs create mode 100644 consensus/src/pipeline/mod.rs create mode 100644 consensus/src/pipeline/persisting_phase.rs create mode 100644 consensus/src/pipeline/pipeline_phase.rs create mode 100644 consensus/src/pipeline/signing_phase.rs create mode 100644 consensus/src/pipeline/tests/execution_phase_tests.rs create mode 100644 consensus/src/pipeline/tests/integration_tests.rs create mode 100644 consensus/src/pipeline/tests/mod.rs create mode 100644 consensus/src/pipeline/tests/ordering_state_computer_tests.rs create mode 100644 consensus/src/pipeline/tests/phase_tester.rs create mode 100644 consensus/src/pipeline/tests/signing_phase_tests.rs create mode 100644 consensus/src/qc_aggregator.rs create mode 100644 consensus/src/quorum_store/batch_requester.rs create mode 100644 consensus/src/quorum_store/batch_store.rs create mode 100644 consensus/src/quorum_store/mod.rs create mode 100644 consensus/src/quorum_store/network_listener.rs create mode 100644 consensus/src/quorum_store/quorum_store_coordinator.rs create mode 100644 consensus/src/quorum_store/schema.rs create mode 100644 consensus/src/quorum_store/tests/batch_requester_test.rs create mode 100644 consensus/src/quorum_store/tests/batch_store_test.rs create mode 100644 consensus/src/quorum_store/tests/mod.rs create mode 100644 consensus/src/quorum_store/tests/quorum_store_db_test.rs create mode 100644 consensus/src/quorum_store/tests/types_test.rs create mode 100644 consensus/src/rand/dkg/mod.rs create mode 100644 consensus/src/rand/mod.rs create mode 100644 consensus/src/rand/rand_gen/mod.rs create mode 100644 consensus/src/rand/rand_gen/storage/in_memory.rs create mode 100644 consensus/src/rand/rand_gen/storage/interface.rs create mode 100644 consensus/src/rand/rand_gen/storage/mod.rs create mode 100644 consensus/src/rand/rand_gen/storage/schema.rs create mode 100644 consensus/src/state_replication.rs create mode 100644 consensus/src/test_utils/mock_quorum_store_sender.rs create mode 100644 consensus/src/test_utils/mock_state_computer.rs create mode 100644 consensus/src/transaction_deduper.rs create mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs create mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs create mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs create mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs create mode 100644 consensus/src/transaction_shuffler/fairness/conflict_zone.rs create mode 100644 consensus/src/transaction_shuffler/fairness/mod.rs create mode 100644 consensus/src/transaction_shuffler/fairness/pending_zone.rs create mode 100644 consensus/src/transaction_shuffler/fairness/selection_tracker.rs create mode 100644 consensus/src/transaction_shuffler/fairness/tests/manual.rs create mode 100644 consensus/src/transaction_shuffler/fairness/tests/mod.rs create mode 100644 consensus/src/transaction_shuffler/fairness/tests/proptests.rs create mode 100644 consensus/src/transaction_shuffler/mod.rs create mode 100644 consensus/src/transaction_shuffler/sender_aware.rs create mode 100644 consensus/src/twins/basic_twins_test.rs create mode 100644 consensus/src/twins/mod.rs create mode 100644 consensus/src/util/db_tool.rs create mode 100644 consensus/src/util/mock_time_service.rs create mode 100644 consensus/src/util/mod.rs create mode 100644 consensus/src/util/time_service.rs create mode 100644 crates/aptos-crypto/benches/noise.rs rename network/framework/src/noise/crypto.rs => crates/aptos-crypto/src/noise.rs (98%) create mode 100644 crates/aptos-crypto/src/unit_tests/noise_test.rs create mode 100644 crates/aptos-debugger/src/lib.rs create mode 100644 crates/aptos-telemetry-service/src/auth.rs create mode 100644 crates/aptos-telemetry-service/src/clients/humio.rs create mode 100644 crates/aptos-telemetry-service/src/constants.rs create mode 100644 crates/aptos-telemetry-service/src/custom_event.rs create mode 100644 crates/aptos-telemetry-service/src/errors.rs create mode 100644 crates/aptos-telemetry-service/src/gcp_logger.rs create mode 100644 crates/aptos-telemetry-service/src/index.rs create mode 100644 crates/aptos-telemetry-service/src/jwt_auth.rs create mode 100644 crates/aptos-telemetry-service/src/log_ingest.rs create mode 100644 crates/aptos-telemetry-service/src/main.rs create mode 100644 crates/aptos-telemetry-service/src/remote_config.rs create mode 100644 crates/aptos-telemetry-service/src/tests/auth_test.rs create mode 100644 crates/aptos-telemetry-service/src/tests/custom_event.rs create mode 100644 crates/aptos-telemetry-service/src/tests/mod.rs create mode 100644 crates/aptos-telemetry-service/src/types/auth.rs create mode 100644 crates/aptos-telemetry-service/src/types/mod.rs create mode 100644 crates/aptos-telemetry-service/src/types/telemetry.rs create mode 100644 crates/aptos-telemetry-service/src/validator_cache.rs diff --git a/Cargo.lock b/Cargo.lock index b0ee6efcf6b7e..4ba8901d576fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -831,6 +831,7 @@ dependencies = [ "rayon", "serde", "serde_json", + "tokio", ] [[package]] @@ -888,6 +889,7 @@ dependencies = [ "proptest-derive", "rand 0.7.3", "rand_core 0.5.1", + "ring 0.16.20", "serde", "serde-name", "serde_bytes", @@ -2675,11 +2677,9 @@ dependencies = [ "rand 0.7.3", "rand 0.8.5", "rand_core 0.5.1", - "ring 0.16.20", "serde", "serde_bytes", "serde_json", - "sha2 0.9.9", "thiserror", "tokio", "tokio-retry", diff --git a/consensus/README.md b/consensus/README.md new file mode 100644 index 0000000000000..8d2b91569ede0 --- /dev/null +++ b/consensus/README.md @@ -0,0 +1,59 @@ +--- +id: consensus +title: Consensus +custom_edit_url: https://github.com/aptos-labs/aptos-core/edit/main/consensus/README.md +--- + + +The consensus component supports state machine replication using the AptosBFT consensus protocol. + +## Overview + +A consensus protocol allows a set of validators to create the logical appearance of a single database. The consensus protocol replicates submitted transactions among the validators, executes potential transactions against the current database, and then agrees on a binding commitment to the ordering of transactions and resulting execution. As a result, all validators can maintain an identical database for a given version number following the [state machine replication paradigm](https://dl.acm.org/citation.cfm?id=98167). The Aptos protocol uses a variant of the [Jolteon consensus protocol](https://arxiv.org/pdf/2106.10362.pdf), a recent Byzantine fault-tolerant ([BFT](https://en.wikipedia.org/wiki/Byzantine_fault)) consensus protocol, called AptosBFT. It provides safety (all honest validators agree on commits and execution) and liveness (commits are continually produced) in the partial synchrony model defined in the paper "Consensus in the Presence of Partial Synchrony" by Dwork, Lynch, and Stockmeyer ([DLS](https://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf)) and mentioned in the paper ["Practical Byzantine Fault Tolerance" (PBFT)](http://pmg.csail.mit.edu/papers/osdi99.pdf) by Castro and Liskov, as well as newer protocols such as [Tendermint](https://arxiv.org/abs/1807.04938). In this document, we present a high-level description of the AptosBFT protocol and discuss how the code is organized. For details on the specifications and proofs of AptosBFT, read the full [technical report](../developer-docs-site/static/papers/aptos-consensus-state-machine-replication-in-the-aptos-blockchain/). + +Agreement on the database state must be reached between validators, even if +there are Byzantine faults. The Byzantine failures model allows some validators +to arbitrarily deviate from the protocol without constraint, with the exception +of being computationally bound (and thus not able to break cryptographic assumptions). Byzantine faults are worst-case errors where validators collude and behave maliciously to try to sabotage system behavior. A consensus protocol that tolerates Byzantine faults caused by malicious or hacked validators can also mitigate arbitrary hardware and software failures. + +AptosBFT assumes that a set of 3f + 1 votes is distributed among a set of validators that may be honest or Byzantine. AptosBFT remains safe, preventing attacks such as double spends and forks when at most f votes are controlled by Byzantine validators — also implying that at least 2f+1 votes are honest. AptosBFT remains live, committing transactions from clients, as long as there exists a global stabilization time (GST), after which all messages between honest validators are delivered to other honest validators within a maximal network delay $\Delta$ (this is the partial synchrony model introduced in [DLS](https://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf)). In addition to traditional guarantees, AptosBFT maintains safety when validators crash and restart — even if all validators restart at the same time. + +### AptosBFT Overview + +In AptosBFT, validators receive transactions from clients and share them with each other through a shared mempool protocol. The AptosBFT protocol then proceeds in a sequence of rounds. In each round, a validator takes the role of leader and proposes a block of transactions to extend a certified sequence of blocks (see quorum certificates below) that contain the full previous transaction history. A validator receives the proposed block and checks their voting rules to determine if it should vote for certifying this block. These simple rules ensure the safety of AptosBFT — and their implementation can be cleanly separated and audited. If the validator intends to vote for this block, it executes the block’s transactions speculatively and without external effect. This results in the computation of an authenticator for the database that results from the execution of the block. The validator then sends a signed vote for the block and the database authenticator to the leader. The leader gathers these votes to form a quorum certificate that provides evidence of $\ge$ 2f + 1 votes for this block and broadcasts the quorum certificate to all validators. + +A block is committed when a contiguous 3-chain commit rule is met. A block at round k is committed if it has a quorum certificate and is confirmed by two more blocks and quorum certificates at rounds k + 1 and k + 2. The commit rule eventually allows honest validators to commit a block. AptosBFT guarantees that all honest validators will eventually commit the block (and proceeding sequence of blocks linked from it). Once a sequence of blocks has committed, the state resulting from executing their transactions can be persisted and forms a replicated database. + +### Advantages of Jolteon + +We evaluated several BFT-based protocols against the dimensions of performance, reliability, security, ease of robust implementation, and operational overhead for validators. Our goal was to choose a protocol that would initially support at least 100 validators and would be able to evolve over time to support 500–1,000 validators. The initial AptosBFT protocol was based on HotStuff for the following reasons: (i) simplicity and modularity; (ii) ability to easily integrate consensus with execution; and (iii) promising performance in early experiments. Later we switched to Jolteon as it reduces latecny by 33% without sacrificing throughput. + +The AptosBFT protocol decomposes into modules for safety (voting and commit rules) and liveness (round_state). This decoupling provides the ability to develop and experiment independently and on different modules in parallel. Due to the simple voting and commit rules, protocol safety is easy to implement and verify. It is straightforward to integrate execution as a part of consensus to avoid forking issues that arise from non-deterministic execution in a leader-based protocol. We did not consider proof-of-work based protocols, such as [Bitcoin](https://bitcoin.org/bitcoin.pdf), due to their poor performance and high energy (and environmental) costs. + +### Extensions and Modifications + +We reformulate the safety conditions and provide extended proofs of safety, liveness, and optimistic responsiveness. We also implement a number of additional features. First, we make the protocol more resistant to non-determinism bugs, by having validators collectively sign the resulting state of a block rather than just the sequence of transactions. This also allows clients to use quorum certificates to authenticate reads from the database. Second, we design a round_state that emits explicit timeouts, and validators rely on a quorum of those to move to the next round — without requiring synchronized clocks. Third, we intend to design an unpredictable leader election mechanism in which the leader of a round is determined by the proposer of the latest committed block using a verifiable random function [VRF](https://people.csail.mit.edu/silvio/Selected%20Scientific%20Papers/Pseudo%20Randomness/Verifiable_Random_Functions.pdf). This mechanism limits the window of time in which an adversary can launch an effective denial-of-service attack against a leader. Fourth, we use aggregate signatures that preserve the identity of validators who sign quorum certificates. This allows us to provide incentives to validators that contribute to quorum certificates. Aggregate signatures also do not require a complex [threshold key setup](https://www.cypherpunks.ca/~iang/pubs/DKG.pdf). + +## Implementation Details + +The consensus component is mostly implemented in the [Actor](https://en.wikipedia.org/wiki/Actor_model) programming model — i.e., it uses message-passing to communicate between different subcomponents with the [tokio](https://tokio.rs/) framework used as the task runtime. The primary exception to the actor model (as it is accessed in parallel by several subcomponents) is the consensus data structure *BlockStore* which manages the blocks, execution, quorum certificates, and other shared data structures. The major subcomponents in the consensus component are: + +* **PayloadClient** is the interface to the mempool component and supports the pulling of transactions as well as removing committed transactions. A proposer uses on-demand pull transactions from mempool to form a proposal block. +* **StateComputer** is the interface for accessing the execution component. It can execute blocks, commit blocks, and can synchronize state. +* **BlockStore** maintains the tree of proposal blocks, block execution, votes, quorum certificates, and persistent storage. It is responsible for maintaining the consistency of the combination of these data structures and can be concurrently accessed by other subcomponents. +* **RoundManager** is responsible for processing the individual events (e.g., process_new_round, process_proposal, process_vote). It exposes the async processing functions for each event type and drives the protocol. +* **RoundState** is responsible for the liveness of the consensus protocol. It changes rounds due to timeout certificates or quorum certificates and proposes blocks when it is the proposer for the current round. +* **SafetyRules** is responsible for the safety of the consensus protocol. It processes quorum certificates and LedgerInfo to learn about new commits and guarantees that the two voting rules are followed — even in the case of restart (since all safety data is persisted to local storage). + +All consensus messages are signed by their creators and verified by their receivers. Message verification occurs closest to the network layer to avoid invalid or unnecessary data from entering the consensus protocol. + +## How is this module organized? + + consensus + ├── src + │   ├── block_storage # In-memory storage of blocks and related data structures + │   ├── consensusdb # Database interaction to persist consensus data for safety and liveness + │   ├── liveness # RoundState, proposer, and other liveness related code + │   └── test_utils # Mock implementations that are used for testing only + ├── consensus-types # Consensus data types (i.e. quorum certificates) + └── safety-rules # Safety (voting) rules diff --git a/consensus/consensus-types/Cargo.toml b/consensus/consensus-types/Cargo.toml index 7b5ecff5c3461..097da10ad0369 100644 --- a/consensus/consensus-types/Cargo.toml +++ b/consensus/consensus-types/Cargo.toml @@ -33,6 +33,7 @@ proptest = { workspace = true, optional = true } rand = { workspace = true } rayon = { workspace = true } serde = { workspace = true } +tokio = { workspace = true } [dev-dependencies] aptos-types = { workspace = true, features = ["fuzzing"] } diff --git a/consensus/consensus-types/src/common.rs b/consensus/consensus-types/src/common.rs index db4e6dfbe0cd2..4d0be0f31de79 100644 --- a/consensus/consensus-types/src/common.rs +++ b/consensus/consensus-types/src/common.rs @@ -8,6 +8,8 @@ use aptos_crypto::{ HashValue, }; use aptos_crypto_derive::CryptoHasher; +use aptos_executor_types::ExecutorResult; +use aptos_infallible::Mutex; use aptos_logger::prelude::*; use aptos_types::{ account_address::AccountAddress, transaction::SignedTransaction, @@ -16,7 +18,8 @@ use aptos_types::{ use once_cell::sync::OnceCell; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, fmt, fmt::Write}; +use std::{collections::HashSet, fmt, fmt::Write, sync::Arc}; +use tokio::sync::oneshot; /// The round of a block is a consensus-internal counter, which starts with 0 and increases /// monotonically. It is used for the protocol safety and liveness (please see the detailed @@ -85,24 +88,62 @@ pub struct RejectedTransactionSummary { pub reason: DiscardedVMStatus, } -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +#[derive(Debug)] +pub enum DataStatus { + Cached(Vec), + Requested( + Vec<( + HashValue, + oneshot::Receiver>>, + )>, + ), +} + +impl DataStatus { + pub fn extend(&mut self, other: DataStatus) { + match (self, other) { + (DataStatus::Requested(v1), DataStatus::Requested(v2)) => v1.extend(v2), + (_, _) => unreachable!(), + } + } + + pub fn take(&mut self) -> DataStatus { + std::mem::replace(self, DataStatus::Requested(vec![])) + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct ProofWithData { pub proofs: Vec, #[serde(skip)] - pub status: Vec, + pub status: Arc>>, +} + +impl PartialEq for ProofWithData { + fn eq(&self, other: &Self) -> bool { + self.proofs == other.proofs && Arc::as_ptr(&self.status) == Arc::as_ptr(&other.status) + } } +impl Eq for ProofWithData {} + impl ProofWithData { pub fn new(proofs: Vec) -> Self { Self { proofs, - status: vec![], + status: Arc::new(Mutex::new(None)), } } pub fn extend(&mut self, other: ProofWithData) { + let other_data_status = other.status.lock().as_mut().unwrap().take(); self.proofs.extend(other.proofs); - self.status.extend(other.status); + let mut status = self.status.lock(); + if status.is_none() { + *status = Some(other_data_status); + } else { + status.as_mut().unwrap().extend(other_data_status); + } } pub fn len(&self) -> usize { diff --git a/consensus/consensus-types/src/lib.rs b/consensus/consensus-types/src/lib.rs index cd1de54f5a247..e398e34ecc15d 100644 --- a/consensus/consensus-types/src/lib.rs +++ b/consensus/consensus-types/src/lib.rs @@ -20,6 +20,7 @@ pub mod proposal_ext; pub mod proposal_msg; pub mod quorum_cert; pub mod randomness; +pub mod request_response; pub mod safety_data; pub mod sync_info; pub mod timeout_2chain; diff --git a/consensus/src/consensusdb/consensusdb_test.rs b/consensus/src/consensusdb/consensusdb_test.rs new file mode 100644 index 0000000000000..13b8fd0711590 --- /dev/null +++ b/consensus/src/consensusdb/consensusdb_test.rs @@ -0,0 +1,118 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use self::schema::dag::NodeSchema; +use super::*; +use crate::dag::{CertifiedNode, Extensions, Node, Vote}; +use aptos_consensus_types::{ + block::block_test_utils::certificate_for_genesis, + common::{Author, Payload}, +}; +use aptos_crypto::bls12381::Signature; +use aptos_temppath::TempPath; +use aptos_types::aggregate_signature::AggregateSignature; +use std::{collections::HashMap, hash::Hash}; + +#[test] +fn test_put_get() { + let tmp_dir = TempPath::new(); + let db = ConsensusDB::new(&tmp_dir); + + let block = Block::make_genesis_block(); + let blocks = vec![block]; + + assert_eq!(db.get_all::().unwrap().len(), 0); + assert_eq!(db.get_all::().unwrap().len(), 0); + + let qcs = vec![certificate_for_genesis()]; + db.save_blocks_and_quorum_certificates(blocks.clone(), qcs.clone()) + .unwrap(); + + assert_eq!(db.get_all::().unwrap().len(), 1); + assert_eq!(db.get_all::().unwrap().len(), 1); + + let tc = vec![0u8, 1, 2]; + db.save_highest_2chain_timeout_certificate(tc.clone()) + .unwrap(); + + let vote = vec![2u8, 1, 0]; + db.save_vote(vote.clone()).unwrap(); + + let (vote_1, tc_1, blocks_1, qc_1) = db.get_data().unwrap(); + assert_eq!(blocks, blocks_1); + assert_eq!(qcs, qc_1); + assert_eq!(Some(tc), tc_1); + assert_eq!(Some(vote), vote_1); + + db.delete_highest_2chain_timeout_certificate().unwrap(); + db.delete_last_vote_msg().unwrap(); + assert!(db + .get_highest_2chain_timeout_certificate() + .unwrap() + .is_none()); + assert!(db.get_last_vote().unwrap().is_none()); +} + +#[test] +fn test_delete_block_and_qc() { + let tmp_dir = TempPath::new(); + let db = ConsensusDB::new(&tmp_dir); + + assert_eq!(db.get_all::().unwrap().len(), 0); + assert_eq!(db.get_all::().unwrap().len(), 0); + + let blocks = vec![Block::make_genesis_block()]; + let block_id = blocks[0].id(); + + let qcs = vec![certificate_for_genesis()]; + let qc_id = qcs[0].certified_block().id(); + + db.save_blocks_and_quorum_certificates(blocks, qcs).unwrap(); + assert_eq!(db.get_all::().unwrap().len(), 1); + assert_eq!(db.get_all::().unwrap().len(), 1); + + // Start to delete + db.delete_blocks_and_quorum_certificates(vec![block_id, qc_id]) + .unwrap(); + assert_eq!(db.get_all::().unwrap().len(), 0); + assert_eq!(db.get_all::().unwrap().len(), 0); +} + +fn test_dag_type, K: Eq + Hash>(key: S::Key, value: S::Value, db: &ConsensusDB) { + db.put::(&key, &value).unwrap(); + let mut from_db: HashMap = db.get_all::().unwrap().into_iter().collect(); + assert_eq!(from_db.len(), 1); + let value_from_db = from_db.remove(&key).unwrap(); + assert_eq!(value, value_from_db); + db.delete::(vec![key]).unwrap(); + assert_eq!(db.get_all::().unwrap().len(), 0); +} + +#[test] +fn test_dag() { + let tmp_dir = TempPath::new(); + let db = ConsensusDB::new(&tmp_dir); + + let node = Node::new( + 1, + 1, + Author::random(), + 123, + vec![], + Payload::empty(false, true), + vec![], + Extensions::empty(), + ); + test_dag_type::::Key>((), node.clone(), &db); + + let certified_node = CertifiedNode::new(node.clone(), AggregateSignature::empty()); + test_dag_type::::Key>( + certified_node.digest(), + certified_node, + &db, + ); + + let vote = Vote::new(node.metadata().clone(), Signature::dummy_signature()); + test_dag_type::::Key>(node.id(), vote, &db); +} diff --git a/consensus/src/consensusdb/schema/block/mod.rs b/consensus/src/consensusdb/schema/block/mod.rs new file mode 100644 index 0000000000000..5ac486a9ebdde --- /dev/null +++ b/consensus/src/consensusdb/schema/block/mod.rs @@ -0,0 +1,47 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module defines physical storage schema for consensus block. +//! +//! Serialized block bytes identified by block_hash. +//! ```text +//! |<---key---->|<---value--->| +//! | block_hash | block | +//! ``` + +use crate::define_schema; +use anyhow::Result; +use aptos_consensus_types::block::Block; +use aptos_crypto::HashValue; +use aptos_schemadb::{ + schema::{KeyCodec, ValueCodec}, + ColumnFamilyName, +}; + +pub const BLOCK_CF_NAME: ColumnFamilyName = "block"; + +define_schema!(BlockSchema, HashValue, Block, BLOCK_CF_NAME); + +impl KeyCodec for HashValue { + fn encode_key(&self) -> Result> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Ok(HashValue::from_slice(data)?) + } +} + +impl ValueCodec for Block { + fn encode_value(&self) -> Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_value(data: &[u8]) -> Result { + Ok(bcs::from_bytes(data)?) + } +} + +#[cfg(test)] +mod test; diff --git a/consensus/src/consensusdb/schema/block/test.rs b/consensus/src/consensusdb/schema/block/test.rs new file mode 100644 index 0000000000000..e2c03259a5a0e --- /dev/null +++ b/consensus/src/consensusdb/schema/block/test.rs @@ -0,0 +1,14 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; + +#[test] +fn test_encode_decode() { + let block = Block::make_genesis_block(); + assert_encode_decode::(&block.id(), &block); +} + +test_no_panic_decoding!(BlockSchema); diff --git a/consensus/src/consensusdb/schema/mod.rs b/consensus/src/consensusdb/schema/mod.rs new file mode 100644 index 0000000000000..3b2b511de9ab8 --- /dev/null +++ b/consensus/src/consensusdb/schema/mod.rs @@ -0,0 +1,41 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) mod block; +pub(crate) mod dag; +pub(crate) mod quorum_certificate; +pub(crate) mod single_entry; + +use anyhow::{ensure, Result}; + +pub(crate) fn ensure_slice_len_eq(data: &[u8], len: usize) -> Result<()> { + ensure!( + data.len() == len, + "Unexpected data len {}, expected {}.", + data.len(), + len, + ); + Ok(()) +} + +/// Copied from aptos-schemdadb to define pub struct instead of pub(crate) +#[macro_export] +macro_rules! define_schema { + ($schema_type:ident, $key_type:ty, $value_type:ty, $cf_name:expr) => { + #[derive(Debug)] + pub struct $schema_type; + + impl aptos_schemadb::schema::Schema for $schema_type { + type Key = $key_type; + type Value = $value_type; + + const COLUMN_FAMILY_NAME: ColumnFamilyName = $cf_name; + } + }; +} + +pub use block::BLOCK_CF_NAME; +pub use dag::{CERTIFIED_NODE_CF_NAME, DAG_VOTE_CF_NAME, NODE_CF_NAME}; +pub use quorum_certificate::QC_CF_NAME; +pub use single_entry::SINGLE_ENTRY_CF_NAME; diff --git a/consensus/src/consensusdb/schema/quorum_certificate/mod.rs b/consensus/src/consensusdb/schema/quorum_certificate/mod.rs new file mode 100644 index 0000000000000..63416c43daf6d --- /dev/null +++ b/consensus/src/consensusdb/schema/quorum_certificate/mod.rs @@ -0,0 +1,47 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module defines physical storage schema for consensus quorum certificate (of a block). +//! +//! Serialized quorum certificate bytes identified by block_hash. +//! ```text +//! |<---key---->|<----value--->| +//! | block_hash | QuorumCert | +//! ``` + +use crate::define_schema; +use anyhow::Result; +use aptos_consensus_types::quorum_cert::QuorumCert; +use aptos_crypto::HashValue; +use aptos_schemadb::{ + schema::{KeyCodec, ValueCodec}, + ColumnFamilyName, +}; + +pub const QC_CF_NAME: ColumnFamilyName = "quorum_certificate"; + +define_schema!(QCSchema, HashValue, QuorumCert, QC_CF_NAME); + +impl KeyCodec for HashValue { + fn encode_key(&self) -> Result> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Ok(HashValue::from_slice(data)?) + } +} + +impl ValueCodec for QuorumCert { + fn encode_value(&self) -> Result> { + Ok(bcs::to_bytes(self)?) + } + + fn decode_value(data: &[u8]) -> Result { + Ok(bcs::from_bytes(data)?) + } +} + +#[cfg(test)] +mod test; diff --git a/consensus/src/consensusdb/schema/quorum_certificate/test.rs b/consensus/src/consensusdb/schema/quorum_certificate/test.rs new file mode 100644 index 0000000000000..4b2cf5423dcb4 --- /dev/null +++ b/consensus/src/consensusdb/schema/quorum_certificate/test.rs @@ -0,0 +1,15 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use aptos_consensus_types::block::block_test_utils::certificate_for_genesis; +use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; + +#[test] +fn test_encode_decode() { + let qc = certificate_for_genesis(); + assert_encode_decode::(&qc.certified_block().id(), &qc); +} + +test_no_panic_decoding!(QCSchema); diff --git a/consensus/src/consensusdb/schema/single_entry/mod.rs b/consensus/src/consensusdb/schema/single_entry/mod.rs new file mode 100644 index 0000000000000..6a66609cf9fa4 --- /dev/null +++ b/consensus/src/consensusdb/schema/single_entry/mod.rs @@ -0,0 +1,71 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module defines physical storage schema for any single-entry data. +//! +//! There will be only one row in this column family for each type of data. +//! The key will be a serialized enum type designating the data type and should not have any meaning +//! and be used. +//! +//! ```text +//! |<-------key------->|<-----value----->| +//! | single entry key | raw value bytes | +//! ``` + +use super::ensure_slice_len_eq; +use crate::define_schema; +use anyhow::{format_err, Result}; +use aptos_schemadb::{ + schema::{KeyCodec, ValueCodec}, + ColumnFamilyName, +}; +use byteorder::ReadBytesExt; +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; +use std::mem::size_of; + +pub const SINGLE_ENTRY_CF_NAME: ColumnFamilyName = "single_entry"; + +define_schema!( + SingleEntrySchema, + SingleEntryKey, + Vec, + SINGLE_ENTRY_CF_NAME +); + +#[derive(Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)] +#[repr(u8)] +pub enum SingleEntryKey { + // Used to store the last vote + LastVote = 0, + // Two chain timeout cert + Highest2ChainTimeoutCert = 1, +} + +impl KeyCodec for SingleEntryKey { + fn encode_key(&self) -> Result> { + Ok(vec![self + .to_u8() + .ok_or_else(|| format_err!("ToPrimitive failed."))?]) + } + + fn decode_key(mut data: &[u8]) -> Result { + ensure_slice_len_eq(data, size_of::())?; + let key = data.read_u8()?; + SingleEntryKey::from_u8(key).ok_or_else(|| format_err!("FromPrimitive failed.")) + } +} + +impl ValueCodec for Vec { + fn encode_value(&self) -> Result> { + Ok(self.clone()) + } + + fn decode_value(data: &[u8]) -> Result { + Ok(data.to_vec()) + } +} + +#[cfg(test)] +mod test; diff --git a/consensus/src/consensusdb/schema/single_entry/test.rs b/consensus/src/consensusdb/schema/single_entry/test.rs new file mode 100644 index 0000000000000..0fdc37a571b76 --- /dev/null +++ b/consensus/src/consensusdb/schema/single_entry/test.rs @@ -0,0 +1,14 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; + +// Tests that the DB can encode / decode data +#[test] +fn test_single_entry_schema() { + assert_encode_decode::(&SingleEntryKey::LastVote, &vec![1u8, 2u8, 3u8]); +} + +test_no_panic_decoding!(SingleEntrySchema); diff --git a/consensus/src/dag/anchor_election/leader_reputation_adapter.rs b/consensus/src/dag/anchor_election/leader_reputation_adapter.rs new file mode 100644 index 0000000000000..3d58d5cd31b47 --- /dev/null +++ b/consensus/src/dag/anchor_election/leader_reputation_adapter.rs @@ -0,0 +1,145 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + dag::{ + anchor_election::{AnchorElection, CommitHistory}, + storage::CommitEvent, + }, + liveness::{ + leader_reputation::{ + LeaderReputation, MetadataBackend, ReputationHeuristic, VotingPowerRatio, + }, + proposer_election::ProposerElection, + }, +}; +use aptos_bitvec::BitVec; +use aptos_collections::BoundedVecDeque; +use aptos_consensus_types::common::{Author, Round}; +use aptos_crypto::HashValue; +use aptos_infallible::Mutex; +use aptos_types::account_config::NewBlockEvent; +use move_core_types::account_address::AccountAddress; +use std::{collections::HashMap, sync::Arc}; + +pub struct MetadataBackendAdapter { + epoch_to_validators: HashMap>, + window_size: usize, + sliding_window: Mutex>, +} + +impl MetadataBackendAdapter { + pub fn new( + window_size: usize, + epoch_to_validators: HashMap>, + ) -> Self { + Self { + epoch_to_validators, + window_size, + sliding_window: Mutex::new(BoundedVecDeque::new(window_size)), + } + } + + pub fn push(&self, event: CommitEvent) { + if !self.epoch_to_validators.contains_key(&event.epoch()) { + return; + } + self.sliding_window.lock().push_front(event); + } + + // TODO: we should change NewBlockEvent on LeaderReputation to take a trait + fn convert(&self, event: CommitEvent) -> NewBlockEvent { + let validators = self.epoch_to_validators.get(&event.epoch()).unwrap(); + let mut bitvec = BitVec::with_num_bits(validators.len() as u16); + for author in event.parents() { + bitvec.set(*validators.get(author).unwrap() as u16); + } + let mut failed_authors = vec![]; + for author in event.failed_authors() { + failed_authors.push(*validators.get(author).unwrap() as u64); + } + NewBlockEvent::new( + AccountAddress::ZERO, + event.epoch(), + event.round(), + 0, + bitvec.into(), + *event.author(), + failed_authors, + 0, + ) + } +} + +impl MetadataBackend for MetadataBackendAdapter { + fn get_block_metadata( + &self, + _target_epoch: u64, + _target_round: Round, + ) -> (Vec, HashValue) { + let events: Vec<_> = self + .sliding_window + .lock() + .clone() + .into_iter() + .map(|event| self.convert(event)) + .collect(); + ( + events, + // TODO: fill in the hash value + HashValue::zero(), + ) + } +} + +pub struct LeaderReputationAdapter { + reputation: LeaderReputation, + data_source: Arc, +} + +impl LeaderReputationAdapter { + pub fn new( + epoch: u64, + epoch_to_proposers: HashMap>, + voting_powers: Vec, + backend: Arc, + heuristic: Box, + window_for_chain_health: usize, + ) -> Self { + Self { + reputation: LeaderReputation::new( + epoch, + epoch_to_proposers, + voting_powers, + backend.clone(), + heuristic, + 0, + true, + window_for_chain_health, + ), + data_source: backend, + } + } +} + +impl AnchorElection for LeaderReputationAdapter { + fn get_anchor(&self, round: Round) -> Author { + self.reputation.get_valid_proposer(round) + } + + fn update_reputation(&self, commit_event: CommitEvent) { + self.data_source.push(commit_event) + } +} + +impl CommitHistory for LeaderReputationAdapter { + fn get_voting_power_participation_ratio(&self, round: Round) -> VotingPowerRatio { + let mut voting_power_ratio = self.reputation.get_voting_power_participation_ratio(round); + // TODO: fix this once leader reputation is fixed + if voting_power_ratio < 0.67 { + voting_power_ratio = 1.0; + } + + voting_power_ratio + } +} diff --git a/consensus/src/dag/anchor_election/mod.rs b/consensus/src/dag/anchor_election/mod.rs new file mode 100644 index 0000000000000..53db6db738250 --- /dev/null +++ b/consensus/src/dag/anchor_election/mod.rs @@ -0,0 +1,21 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{dag::storage::CommitEvent, liveness::leader_reputation::VotingPowerRatio}; +use aptos_consensus_types::common::{Author, Round}; + +pub trait AnchorElection: Send + Sync { + fn get_anchor(&self, round: Round) -> Author; + + fn update_reputation(&self, commit_event: CommitEvent); +} + +pub trait CommitHistory: Send + Sync { + fn get_voting_power_participation_ratio(&self, round: Round) -> VotingPowerRatio; +} + +mod leader_reputation_adapter; +mod round_robin; + +pub use leader_reputation_adapter::{LeaderReputationAdapter, MetadataBackendAdapter}; +pub use round_robin::RoundRobinAnchorElection; diff --git a/consensus/src/dag/anchor_election/round_robin.rs b/consensus/src/dag/anchor_election/round_robin.rs new file mode 100644 index 0000000000000..ee1b6e5f53442 --- /dev/null +++ b/consensus/src/dag/anchor_election/round_robin.rs @@ -0,0 +1,33 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::CommitHistory; +use crate::{ + dag::{anchor_election::AnchorElection, storage::CommitEvent}, + liveness::leader_reputation::VotingPowerRatio, +}; +use aptos_consensus_types::common::{Author, Round}; + +pub struct RoundRobinAnchorElection { + validators: Vec, +} + +impl RoundRobinAnchorElection { + pub fn new(validators: Vec) -> Self { + Self { validators } + } +} + +impl AnchorElection for RoundRobinAnchorElection { + fn get_anchor(&self, round: Round) -> Author { + self.validators[(round / 2) as usize % self.validators.len()] + } + + fn update_reputation(&self, _event: CommitEvent) {} +} + +impl CommitHistory for RoundRobinAnchorElection { + fn get_voting_power_participation_ratio(&self, _round: Round) -> VotingPowerRatio { + 1.0 + } +} diff --git a/consensus/src/dag/dag_fetcher.rs b/consensus/src/dag/dag_fetcher.rs new file mode 100644 index 0000000000000..43f7b456e16f0 --- /dev/null +++ b/consensus/src/dag/dag_fetcher.rs @@ -0,0 +1,438 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{dag_store::DagStore, errors::DagFetchError, DAGRpcResult}; +use crate::dag::{ + dag_network::{RpcResultWithResponder, TDAGNetworkSender}, + errors::FetchRequestHandleError, + observability::logging::{LogEvent, LogSchema}, + types::{CertifiedNode, FetchResponse, Node, NodeMetadata, RemoteFetchRequest}, + RpcHandler, RpcWithFallback, +}; +use anyhow::{bail, ensure}; +use aptos_bitvec::BitVec; +use aptos_config::config::DagFetcherConfig; +use aptos_consensus_types::common::{Author, Round}; +use aptos_logger::{debug, error, info}; +use aptos_time_service::TimeService; +use aptos_types::epoch_state::EpochState; +use async_trait::async_trait; +use futures::{future::Shared, stream::FuturesUnordered, Future, FutureExt, Stream, StreamExt}; +use std::{ + collections::HashMap, + pin::Pin, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; +use tokio::{ + select, + sync::{ + mpsc::{Receiver, Sender}, + oneshot, + }, +}; + +pub struct FetchWaiter { + rx: Receiver>, + futures: Pin>>>, +} + +impl FetchWaiter { + fn new(rx: Receiver>) -> Self { + Self { + rx, + futures: Box::pin(FuturesUnordered::new()), + } + } +} + +impl Stream for FetchWaiter { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Poll::Ready(Some(rx)) = self.rx.poll_recv(cx) { + self.futures.push(rx); + } + + self.futures.as_mut().poll_next(cx) + } +} + +pub trait TFetchRequester: Send + Sync { + fn request_for_node(&self, node: Node) -> anyhow::Result<()>; + fn request_for_certified_node(&self, node: CertifiedNode) -> anyhow::Result<()>; +} + +pub struct FetchRequester { + request_tx: Sender, + node_waiter_tx: Sender>, + certified_node_waiter_tx: Sender>, +} + +impl TFetchRequester for FetchRequester { + fn request_for_node(&self, node: Node) -> anyhow::Result<()> { + let (res_tx, res_rx) = oneshot::channel(); + let fetch_req = LocalFetchRequest::Node(node, res_tx); + self.request_tx + .try_send(fetch_req) + .map_err(|e| anyhow::anyhow!("unable to send node fetch request to channel: {}", e))?; + self.node_waiter_tx.try_send(res_rx)?; + Ok(()) + } + + fn request_for_certified_node(&self, node: CertifiedNode) -> anyhow::Result<()> { + let (res_tx, res_rx) = oneshot::channel(); + let fetch_req = LocalFetchRequest::CertifiedNode(node, res_tx); + self.request_tx.try_send(fetch_req).map_err(|e| { + anyhow::anyhow!( + "unable to send certified node fetch request to channel: {}", + e + ) + })?; + self.certified_node_waiter_tx.try_send(res_rx)?; + Ok(()) + } +} + +#[derive(Debug)] +pub enum LocalFetchRequest { + Node(Node, oneshot::Sender), + CertifiedNode(CertifiedNode, oneshot::Sender), +} + +impl LocalFetchRequest { + pub fn responders(&self, validators: &[Author]) -> Vec { + match self { + LocalFetchRequest::Node(node, _) => vec![*node.author()], + LocalFetchRequest::CertifiedNode(node, _) => { + node.signatures().get_signers_addresses(validators) + }, + } + } + + pub fn notify(self) { + if match self { + LocalFetchRequest::Node(node, sender) => sender.send(node).map_err(|_| ()), + LocalFetchRequest::CertifiedNode(node, sender) => sender.send(node).map_err(|_| ()), + } + .is_err() + { + error!("Failed to send node back"); + } + } + + pub fn node(&self) -> &Node { + match self { + LocalFetchRequest::Node(node, _) => node, + LocalFetchRequest::CertifiedNode(node, _) => node, + } + } +} + +pub struct DagFetcherService { + inner: Arc, + dag: Arc, + request_rx: Receiver, + ordered_authors: Vec, + inflight_requests: HashMap< + (Round, BitVec), + Shared> + Send>>>, + >, + futures: + FuturesUnordered> + Send>>>, + max_concurrent_fetches: usize, +} + +impl DagFetcherService { + pub fn new( + epoch_state: Arc, + network: Arc, + dag: Arc, + time_service: TimeService, + config: DagFetcherConfig, + ) -> ( + Self, + FetchRequester, + FetchWaiter, + FetchWaiter, + ) { + let (request_tx, request_rx) = tokio::sync::mpsc::channel(16); + let (node_tx, node_rx) = tokio::sync::mpsc::channel(100); + let (certified_node_tx, certified_node_rx) = tokio::sync::mpsc::channel(100); + let ordered_authors = epoch_state.verifier.get_ordered_account_addresses(); + ( + Self { + max_concurrent_fetches: config.max_concurrent_fetches, + inner: Arc::new(DagFetcher::new(epoch_state, network, time_service, config)), + dag, + request_rx, + ordered_authors, + inflight_requests: HashMap::new(), + futures: FuturesUnordered::new(), + }, + FetchRequester { + request_tx, + node_waiter_tx: node_tx, + certified_node_waiter_tx: certified_node_tx, + }, + FetchWaiter::new(node_rx), + FetchWaiter::new(certified_node_rx), + ) + } + + pub async fn start(mut self) { + loop { + select! { + Some(result) = self.futures.next() => { + match result { + Ok(local_request) => local_request.notify(), + Err(err) => error!("unable to complete fetch successfully: {}", err), + } + }, + // TODO: Configure concurrency + Some(local_request) = self.request_rx.recv(), if self.futures.len() < self.max_concurrent_fetches => { + match self.fetch(local_request.node(), local_request.responders(&self.ordered_authors)) { + Ok(fut) => { + self.futures.push(async move { + fut.await?; + Ok(local_request) + }.boxed()) + }, + Err(err) => error!("unable to initiate fetch successfully: {}", err), + } + }, + else => { + info!("Dag Fetch Service exiting."); + return; + } + } + } + } + + pub(super) fn fetch( + &mut self, + node: &Node, + responders: Vec, + ) -> anyhow::Result>>> { + let remote_request = { + let dag_reader = self.dag.read(); + ensure!( + node.round() >= dag_reader.lowest_incomplete_round(), + "Already synced beyond requested round {}, lowest incomplete round {}", + node.round(), + dag_reader.lowest_incomplete_round() + ); + + let missing_parents: Vec = dag_reader + .filter_missing(node.parents_metadata()) + .cloned() + .collect(); + + if missing_parents.is_empty() { + return Ok(async { Ok(()) }.boxed().shared()); + } + + RemoteFetchRequest::new( + node.metadata().epoch(), + missing_parents, + dag_reader.bitmask(node.round().saturating_sub(1)), + ) + }; + + let target_round = remote_request.target_round(); + let Some(bitmap) = remote_request.exists_bitmask().bitvec(target_round) else { + bail!( + "cannot get bitmap for target_round {} in {:?}", + target_round, + remote_request.exists_bitmask() + ); + }; + + let future = self + .inflight_requests + .entry((target_round, bitmap)) + .or_insert_with(|| { + let fetcher = self.inner.clone(); + let dag_clone = self.dag.clone(); + async move { fetcher.fetch(remote_request, responders, dag_clone).await } + .boxed() + .shared() + }) + .clone(); + + Ok(future) + } +} + +#[async_trait] +pub trait TDagFetcher: Send { + async fn fetch( + &self, + remote_request: RemoteFetchRequest, + responders: Vec, + dag: Arc, + ) -> Result<(), DagFetchError>; +} + +pub(crate) struct DagFetcher { + network: Arc, + time_service: TimeService, + epoch_state: Arc, + config: DagFetcherConfig, +} + +impl DagFetcher { + pub(crate) fn new( + epoch_state: Arc, + network: Arc, + time_service: TimeService, + config: DagFetcherConfig, + ) -> Self { + Self { + network, + time_service, + epoch_state, + config, + } + } +} + +#[async_trait] +impl TDagFetcher for DagFetcher { + async fn fetch( + &self, + remote_request: RemoteFetchRequest, + responders: Vec, + dag: Arc, + ) -> Result<(), DagFetchError> { + debug!( + LogSchema::new(LogEvent::FetchNodes), + start_round = remote_request.start_round(), + target_round = remote_request.target_round(), + lens = remote_request.exists_bitmask().len(), + missing_nodes = remote_request.exists_bitmask().num_missing(), + ); + let mut rpc = RpcWithFallback::new( + responders, + remote_request.clone().into(), + Duration::from_millis(self.config.retry_interval_ms), + Duration::from_millis(self.config.rpc_timeout_ms), + self.network.clone(), + self.time_service.clone(), + self.config.min_concurrent_responders, + self.config.max_concurrent_responders, + ); + + while let Some(RpcResultWithResponder { responder, result }) = rpc.next().await { + match result { + Ok(DAGRpcResult(Ok(response))) => { + match FetchResponse::try_from(response).and_then(|response| { + response.verify(&remote_request, &self.epoch_state.verifier) + }) { + Ok(fetch_response) => { + let certified_nodes = fetch_response.certified_nodes(); + // TODO: support chunk response or fallback to state sync + { + for node in certified_nodes.into_iter().rev() { + if let Err(e) = dag.add_node(node) { + error!(error = ?e, "failed to add node"); + } + } + } + + if dag.read().all_exists(remote_request.targets()) { + return Ok(()); + } + }, + Err(err) => { + info!(error = ?err, "failure parsing/verifying fetch response from {}", responder); + }, + }; + }, + Ok(DAGRpcResult(Err(dag_rpc_error))) => { + info!(error = ?dag_rpc_error, responder = responder, "fetch failure: target {} returned error", responder); + }, + Err(err) => { + info!(error = ?err, responder = responder, "rpc failed to {}", responder); + }, + } + } + Err(DagFetchError::Failed) + } +} + +pub struct FetchRequestHandler { + dag: Arc, + author_to_index: HashMap, +} + +impl FetchRequestHandler { + pub fn new(dag: Arc, epoch_state: Arc) -> Self { + Self { + dag, + author_to_index: epoch_state.verifier.address_to_validator_index().clone(), + } + } +} + +#[async_trait] +impl RpcHandler for FetchRequestHandler { + type Request = RemoteFetchRequest; + type Response = FetchResponse; + + async fn process(&self, message: Self::Request) -> anyhow::Result { + let dag_reader = self.dag.read(); + + // `Certified Node`: In the good case, there should exist at least one honest validator that + // signed the Certified Node that has the all the parents to fulfil this + // request. + // `Node`: In the good case, the sender of the Node should have the parents in its local DAG + // to satisfy this request. + debug!( + LogSchema::new(LogEvent::ReceiveFetchNodes).round(dag_reader.highest_round()), + start_round = message.start_round(), + target_round = message.target_round(), + ); + ensure!( + dag_reader.lowest_round() <= message.start_round(), + FetchRequestHandleError::GarbageCollected( + message.start_round(), + dag_reader.lowest_round() + ), + ); + + let missing_targets: BitVec = message + .targets() + .map(|node| !dag_reader.exists(node)) + .collect(); + ensure!( + missing_targets.all_zeros(), + FetchRequestHandleError::TargetsMissing(missing_targets) + ); + + let certified_nodes: Vec<_> = dag_reader + .reachable( + message.targets(), + Some(message.exists_bitmask().first_round()), + |_| true, + ) + .filter_map(|node_status| { + let arc_node = node_status.as_node(); + self.author_to_index + .get(arc_node.author()) + .and_then(|author_idx| { + if !message.exists_bitmask().has(arc_node.round(), *author_idx) { + Some(arc_node.as_ref().clone()) + } else { + None + } + }) + }) + .collect(); + + // TODO: decide if the response is too big and act accordingly. + + Ok(FetchResponse::new(message.epoch(), certified_nodes)) + } +} diff --git a/consensus/src/dag/dag_store.rs b/consensus/src/dag/dag_store.rs new file mode 100644 index 0000000000000..e22b23f762ae8 --- /dev/null +++ b/consensus/src/dag/dag_store.rs @@ -0,0 +1,548 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{ + types::{DagSnapshotBitmask, NodeMetadata}, + Node, +}; +use crate::{ + dag::{ + storage::DAGStorage, + types::{CertifiedNode, NodeCertificate}, + }, + payload_manager::TPayloadManager, +}; +use anyhow::{anyhow, ensure}; +use aptos_consensus_types::common::{Author, Round}; +use aptos_crypto::HashValue; +use aptos_infallible::RwLock; +use aptos_logger::{debug, error, warn}; +use aptos_types::{epoch_state::EpochState, validator_verifier::ValidatorVerifier}; +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + ops::Deref, + sync::Arc, +}; + +#[derive(Clone)] +pub enum NodeStatus { + Unordered { + node: Arc, + aggregated_weak_voting_power: u128, + aggregated_strong_voting_power: u128, + }, + Ordered(Arc), +} + +impl NodeStatus { + pub fn as_node(&self) -> &Arc { + match self { + NodeStatus::Unordered { node, .. } | NodeStatus::Ordered(node) => node, + } + } + + pub fn mark_as_ordered(&mut self) { + assert!(matches!(self, NodeStatus::Unordered { .. })); + *self = NodeStatus::Ordered(self.as_node().clone()); + } +} +/// Data structure that stores the in-memory DAG representation, it maintains round based index. +#[derive(Clone)] +pub struct InMemDag { + nodes_by_round: BTreeMap>>, + /// Map between peer id to vector index + author_to_index: HashMap, + start_round: Round, + epoch_state: Arc, + /// The window we maintain between highest committed round and initial round + window_size: u64, +} + +impl InMemDag { + pub fn new_empty(epoch_state: Arc, start_round: Round, window_size: u64) -> Self { + let author_to_index = epoch_state.verifier.address_to_validator_index().clone(); + let nodes_by_round = BTreeMap::new(); + Self { + nodes_by_round, + author_to_index, + start_round, + epoch_state, + window_size, + } + } + + pub(crate) fn lowest_round(&self) -> Round { + self.start_round + } + + pub fn highest_round(&self) -> Round { + // If stale nodes exist on the BTreeMap, ignore their rounds when calculating + // the highest round. + *self + .nodes_by_round + .last_key_value() + .map(|(round, _)| round) + .unwrap_or(&self.start_round) + .max(&self.start_round) + } + + /// The highest strong links round is either the highest round or the highest round - 1 + /// because we ensure all parents (strong links) exist for any nodes in the store + pub fn highest_strong_links_round(&self, validator_verifier: &ValidatorVerifier) -> Round { + let highest_round = self.highest_round(); + self.get_strong_links_for_round(highest_round, validator_verifier) + .map_or_else(|| highest_round.saturating_sub(1), |_| highest_round) + } + + #[cfg(test)] + pub fn add_node_for_test(&mut self, node: CertifiedNode) -> anyhow::Result<()> { + self.validate_new_node(&node)?; + self.add_validated_node(node) + } + + fn add_validated_node(&mut self, node: CertifiedNode) -> anyhow::Result<()> { + let round = node.round(); + ensure!( + round >= self.lowest_round(), + "dag was pruned. given round: {}, lowest round: {}", + round, + self.lowest_round() + ); + + let node = Arc::new(node); + let round_ref = self + .get_node_ref_mut(node.round(), node.author()) + .expect("must be present"); + ensure!(round_ref.is_none(), "race during insertion"); + *round_ref = Some(NodeStatus::Unordered { + node: node.clone(), + aggregated_weak_voting_power: 0, + aggregated_strong_voting_power: 0, + }); + self.update_votes(&node, true); + Ok(()) + } + + fn validate_new_node(&mut self, node: &CertifiedNode) -> anyhow::Result<()> { + ensure!( + node.epoch() == self.epoch_state.epoch, + "different epoch {}, current {}", + node.epoch(), + self.epoch_state.epoch + ); + let author = node.metadata().author(); + let index = *self + .author_to_index + .get(author) + .ok_or_else(|| anyhow!("unknown author"))?; + let round = node.metadata().round(); + ensure!( + round >= self.lowest_round(), + "round too low {}, lowest in dag {}", + round, + self.lowest_round() + ); + ensure!( + round <= self.highest_round() + 1, + "round too high {}, highest in dag {}", + round, + self.highest_round() + ); + if round > self.lowest_round() { + for parent in node.parents() { + ensure!(self.exists(parent.metadata()), "parent not exist"); + } + } + let round_ref = self + .nodes_by_round + .entry(round) + .or_insert_with(|| vec![None; self.author_to_index.len()]); + ensure!(round_ref[index].is_none(), "duplicate node"); + Ok(()) + } + + pub fn update_votes(&mut self, node: &Node, update_link_power: bool) { + if node.round() <= self.lowest_round() { + return; + } + + let voting_power = self + .epoch_state + .verifier + .get_voting_power(node.author()) + .expect("must exist"); + + for parent in node.parents_metadata() { + let node_status = self + .get_node_ref_mut(parent.round(), parent.author()) + .expect("must exist"); + match node_status { + Some(NodeStatus::Unordered { + aggregated_weak_voting_power, + aggregated_strong_voting_power, + .. + }) => { + if update_link_power { + *aggregated_strong_voting_power += voting_power as u128; + } else { + *aggregated_weak_voting_power += voting_power as u128; + } + }, + Some(NodeStatus::Ordered(_)) => {}, + None => unreachable!("parents must exist before voting for a node"), + } + } + } + + pub fn exists(&self, metadata: &NodeMetadata) -> bool { + self.get_node_ref_by_metadata(metadata).is_some() + } + + pub fn all_exists<'a>(&self, nodes: impl Iterator) -> bool { + self.filter_missing(nodes).next().is_none() + } + + pub fn all_exists_by_round_author<'a>( + &self, + mut nodes: impl Iterator, + ) -> bool { + nodes.all(|(round, author)| self.get_node_ref(*round, author).is_some()) + } + + pub fn filter_missing<'a, 'b>( + &'b self, + nodes: impl Iterator + 'b, + ) -> impl Iterator + 'b { + nodes.filter(|node_metadata| !self.exists(node_metadata)) + } + + fn get_node_ref_by_metadata(&self, metadata: &NodeMetadata) -> Option<&NodeStatus> { + self.get_node_ref(metadata.round(), metadata.author()) + } + + pub fn get_node_ref(&self, round: Round, author: &Author) -> Option<&NodeStatus> { + let index = self.author_to_index.get(author)?; + let round_ref = self.nodes_by_round.get(&round)?; + round_ref[*index].as_ref() + } + + fn get_node_ref_mut( + &mut self, + round: Round, + author: &Author, + ) -> Option<&mut Option> { + let index = self.author_to_index.get(author)?; + let round_ref = self.nodes_by_round.get_mut(&round)?; + Some(&mut round_ref[*index]) + } + + fn get_round_iter(&self, round: Round) -> Option> { + self.nodes_by_round + .get(&round) + .map(|round_ref| round_ref.iter().flatten()) + } + + pub fn get_node(&self, metadata: &NodeMetadata) -> Option> { + self.get_node_ref_by_metadata(metadata) + .map(|node_status| node_status.as_node().clone()) + } + + pub fn get_node_by_round_author( + &self, + round: Round, + author: &Author, + ) -> Option<&Arc> { + self.get_node_ref(round, author) + .map(|node_status| node_status.as_node()) + } + + pub fn check_votes_for_node( + &self, + metadata: &NodeMetadata, + validator_verifier: &ValidatorVerifier, + ) -> bool { + self.get_node_ref_by_metadata(metadata) + .map(|node_status| match node_status { + NodeStatus::Unordered { + aggregated_weak_voting_power, + aggregated_strong_voting_power, + .. + } => { + validator_verifier + .check_aggregated_voting_power(*aggregated_weak_voting_power, true) + .is_ok() + || validator_verifier + .check_aggregated_voting_power(*aggregated_strong_voting_power, false) + .is_ok() + }, + NodeStatus::Ordered(_) => { + error!("checking voting power for Ordered node"); + true + }, + }) + .unwrap_or(false) + } + + fn reachable_filter(start: Vec) -> impl FnMut(&Arc) -> bool { + let mut reachable: HashSet = HashSet::from_iter(start); + move |node| { + if reachable.contains(&node.digest()) { + for parent in node.parents() { + reachable.insert(*parent.metadata().digest()); + } + true + } else { + false + } + } + } + + pub fn reachable_mut( + &mut self, + from: &Arc, + until: Option, + ) -> impl Iterator { + let until = until.unwrap_or(self.lowest_round()); + let mut reachable_filter = Self::reachable_filter(vec![from.digest()]); + self.nodes_by_round + .range_mut(until..=from.round()) + .rev() + .flat_map(|(_, round_ref)| round_ref.iter_mut()) + .flatten() + .filter(move |node_status| { + matches!(node_status, NodeStatus::Unordered { .. }) + && reachable_filter(node_status.as_node()) + }) + } + + pub fn reachable<'a>( + &self, + targets: impl Iterator + Clone, + until: Option, + // TODO: replace filter with bool to filter unordered + filter: impl Fn(&NodeStatus) -> bool, + ) -> impl Iterator { + let until = until.unwrap_or(self.lowest_round()); + let initial_round = targets.clone().map(|t| t.round()).max().unwrap(); + let initial = targets.map(|t| *t.digest()).collect(); + + let mut reachable_filter = Self::reachable_filter(initial); + self.nodes_by_round + .range(until..=initial_round) + .rev() + .flat_map(|(_, round_ref)| round_ref.iter()) + .flatten() + .filter(move |node_status| { + filter(node_status) && reachable_filter(node_status.as_node()) + }) + } + + pub fn get_strong_links_for_round( + &self, + round: Round, + validator_verifier: &ValidatorVerifier, + ) -> Option> { + if validator_verifier + .check_voting_power( + self.get_round_iter(round)? + .map(|node_status| node_status.as_node().metadata().author()), + true, + ) + .is_ok() + { + Some( + self.get_round_iter(round)? + .map(|node_status| node_status.as_node().certificate()) + .collect(), + ) + } else { + None + } + } + + pub fn lowest_incomplete_round(&self) -> Round { + if self.nodes_by_round.is_empty() { + return self.lowest_round(); + } + + for (round, round_nodes) in &self.nodes_by_round { + if round_nodes.iter().any(|node| node.is_none()) { + return *round; + } + } + + self.highest_round() + 1 + } + + pub fn bitmask(&self, target_round: Round) -> DagSnapshotBitmask { + let from_round = if self.is_empty() { + self.lowest_round() + } else { + target_round + .saturating_sub(self.window_size) + .max(self.lowest_incomplete_round()) + .max(self.lowest_round()) + }; + let mut bitmask: Vec<_> = self + .nodes_by_round + .range(from_round..=target_round) + .map(|(_, round_nodes)| round_nodes.iter().map(|node| node.is_some()).collect()) + .collect(); + + bitmask.resize( + (target_round - from_round + 1) as usize, + vec![false; self.author_to_index.len()], + ); + + DagSnapshotBitmask::new(from_round, bitmask) + } + + pub(super) fn prune(&mut self) -> BTreeMap>> { + let to_keep = self.nodes_by_round.split_off(&self.start_round); + let to_prune = std::mem::replace(&mut self.nodes_by_round, to_keep); + debug!( + "pruning dag. start round {}. pruning from {}", + self.start_round, + to_prune.first_key_value().map(|v| v.0).unwrap() + ); + to_prune + } + + fn commit_callback( + &mut self, + commit_round: Round, + ) -> Option>>> { + let new_start_round = commit_round.saturating_sub(3 * self.window_size); + if new_start_round > self.start_round { + self.start_round = new_start_round; + return Some(self.prune()); + } + None + } + + pub(super) fn highest_ordered_anchor_round(&self) -> Option { + for (round, round_nodes) in self.nodes_by_round.iter().rev() { + for maybe_node_status in round_nodes { + if matches!(maybe_node_status, Some(NodeStatus::Ordered(_))) { + return Some(*round); + } + } + } + None + } + + pub fn is_empty(&self) -> bool { + self.nodes_by_round.is_empty() && self.start_round > 1 + } +} + +pub struct DagStore { + dag: RwLock, + storage: Arc, + payload_manager: Arc, +} + +impl DagStore { + pub fn new( + epoch_state: Arc, + storage: Arc, + payload_manager: Arc, + start_round: Round, + window_size: u64, + ) -> Self { + let mut all_nodes = storage.get_certified_nodes().unwrap_or_default(); + all_nodes.sort_unstable_by_key(|(_, node)| node.round()); + let mut to_prune = vec![]; + // Reconstruct the continuous dag starting from start_round and gc unrelated nodes + let dag = Self::new_empty( + epoch_state, + storage.clone(), + payload_manager, + start_round, + window_size, + ); + for (digest, certified_node) in all_nodes { + // TODO: save the storage call in this case + if let Err(e) = dag.add_node(certified_node) { + debug!("Delete node after bootstrap due to {}", e); + to_prune.push(digest); + } + } + if let Err(e) = storage.delete_certified_nodes(to_prune) { + error!("Error deleting expired nodes: {:?}", e); + } + if dag.read().is_empty() { + warn!( + "[DAG] Start with empty DAG store at {}, need state sync", + start_round + ); + } + dag + } + + pub fn new_empty( + epoch_state: Arc, + storage: Arc, + payload_manager: Arc, + start_round: Round, + window_size: u64, + ) -> Self { + let dag = InMemDag::new_empty(epoch_state, start_round, window_size); + Self { + dag: RwLock::new(dag), + storage, + payload_manager, + } + } + + pub fn new_for_test( + dag: InMemDag, + storage: Arc, + payload_manager: Arc, + ) -> Self { + Self { + dag: RwLock::new(dag), + storage, + payload_manager, + } + } + + pub fn add_node(&self, node: CertifiedNode) -> anyhow::Result<()> { + self.dag.write().validate_new_node(&node)?; + + // Note on concurrency: it is possible that a prune operation kicks in here and + // moves the window forward making the `node` stale. Any stale node inserted + // due to this race will be cleaned up with the next prune operation. + + // mutate after all checks pass + self.storage.save_certified_node(&node)?; + + debug!("Added node {}", node.id()); + self.payload_manager + .prefetch_payload_data(node.payload(), node.metadata().timestamp()); + + self.dag.write().add_validated_node(node) + } + + pub fn commit_callback(&self, commit_round: Round) { + let to_prune = self.dag.write().commit_callback(commit_round); + if let Some(to_prune) = to_prune { + let digests = to_prune + .iter() + .flat_map(|(_, round_ref)| round_ref.iter().flatten()) + .map(|node_status| *node_status.as_node().metadata().digest()) + .collect(); + if let Err(e) = self.storage.delete_certified_nodes(digests) { + error!("Error deleting expired nodes: {:?}", e); + } + } + } +} + +impl Deref for DagStore { + type Target = RwLock; + + fn deref(&self) -> &Self::Target { + &self.dag + } +} diff --git a/consensus/src/dag/mod.rs b/consensus/src/dag/mod.rs new file mode 100644 index 0000000000000..4b5611d84e9ca --- /dev/null +++ b/consensus/src/dag/mod.rs @@ -0,0 +1,32 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +#![allow(dead_code)] + +mod adapter; +mod anchor_election; +mod bootstrap; +mod commit_signer; +mod dag_driver; +mod dag_fetcher; +mod dag_handler; +mod dag_network; +mod dag_state_sync; +mod dag_store; +mod errors; +mod health; +mod observability; +mod order_rule; +mod rb_handler; +mod round_state; +mod storage; +#[cfg(test)] +mod tests; +mod types; + +pub use adapter::{ProofNotifier, StorageAdapter}; +pub use bootstrap::DagBootstrapper; +pub use commit_signer::DagCommitSigner; +pub use dag_network::{RpcHandler, RpcWithFallback, TDAGNetworkSender}; +#[cfg(test)] +pub use types::Extensions; +pub use types::{CertifiedNode, DAGMessage, DAGNetworkMessage, DAGRpcResult, Node, NodeId, Vote}; diff --git a/consensus/src/dag/observability/counters.rs b/consensus/src/dag/observability/counters.rs new file mode 100644 index 0000000000000..2abdc9ec41a15 --- /dev/null +++ b/consensus/src/dag/observability/counters.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_metrics_core::{ + register_histogram, register_histogram_vec, register_int_gauge, Histogram, HistogramVec, + IntGauge, +}; +use once_cell::sync::Lazy; + +/// Traces node latency movement throughout the DAG +pub static NODE_TRACING: Lazy = Lazy::new(|| { + register_histogram_vec!( + "aptos_consensus_dag_node_tracing", + "Histogram for different stages of a node", + &["stage"] + ) + .unwrap() +}); + +/// Traces round latency movement throughout the DAG +pub static ROUND_TRACING: Lazy = Lazy::new(|| { + register_histogram_vec!( + "aptos_consensus_dag_round_tracing", + "Histogram for different stages of a round", + &["stage"] + ) + .unwrap() +}); + +/// This counter is set to the last round reported by the local round_state. +pub static CURRENT_ROUND: Lazy = Lazy::new(|| { + register_int_gauge!( + "aptos_consensus_dag_current_round", + "This counter is set to the last round reported by the dag driver." + ) + .unwrap() +}); + +pub static NUM_TXNS_PER_NODE: Lazy = Lazy::new(|| { + register_histogram!( + "aptos_consensus_dag_num_txns_per_node", + "Histogram counting the number of transactions per node", + ) + .unwrap() +}); + +pub static NODE_PAYLOAD_SIZE: Lazy = Lazy::new(|| { + register_histogram!( + "aptos_consensus_dag_node_payload_size", + "Histogram counting the size of the node payload", + ) + .unwrap() +}); + +pub static NUM_NODES_PER_BLOCK: Lazy = Lazy::new(|| { + register_histogram!( + "aptos_consensus_dag_num_nodes_per_block", + "Histogram counting the number of nodes per block", + ) + .unwrap() +}); + +pub static NUM_ROUNDS_PER_BLOCK: Lazy = Lazy::new(|| { + register_histogram!( + "aptos_consensus_dag_num_rounds_per_block", + "Histogram counting the number of rounds per block", + ) + .unwrap() +}); diff --git a/consensus/src/dag/observability/logging.rs b/consensus/src/dag/observability/logging.rs new file mode 100644 index 0000000000000..51ee8d47b0086 --- /dev/null +++ b/consensus/src/dag/observability/logging.rs @@ -0,0 +1,44 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_consensus_types::common::{Author, Round}; +use aptos_logger::Schema; +use serde::Serialize; + +#[derive(Schema)] +pub struct LogSchema { + event: LogEvent, + remote_peer: Option, + round: Option, +} + +#[derive(Serialize)] +pub enum LogEvent { + EpochStart, + ModeTransition, + BroadcastNode, + ReceiveNode, + Vote, + ReceiveVote, + BroadcastCertifiedNode, + ReceiveCertifiedNode, + ReceiveAck, + OrderedAnchor, + NewRound, + FetchNodes, + ReceiveFetchNodes, + ActiveMode, + SyncMode, + SyncOutcome, + Shutdown, +} + +impl LogSchema { + pub fn new(event: LogEvent) -> Self { + Self { + event, + remote_peer: None, + round: None, + } + } +} diff --git a/consensus/src/dag/observability/mod.rs b/consensus/src/dag/observability/mod.rs new file mode 100644 index 0000000000000..899b950ea22ad --- /dev/null +++ b/consensus/src/dag/observability/mod.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod counters; +pub mod logging; +pub mod tracing; diff --git a/consensus/src/dag/observability/tracing.rs b/consensus/src/dag/observability/tracing.rs new file mode 100644 index 0000000000000..839ad37109789 --- /dev/null +++ b/consensus/src/dag/observability/tracing.rs @@ -0,0 +1,40 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::dag::observability::counters; +use aptos_infallible::duration_since_epoch; +use aptos_metrics_core::HistogramVec; +use std::time::Duration; + +#[derive(strum_macros::AsRefStr)] +pub enum NodeStage { + NodeReceived, + CertAggregated, + CertifiedNodeReceived, + AnchorOrdered, + NodeOrdered, +} + +#[derive(strum_macros::AsRefStr)] +pub enum RoundStage { + NodeBroadcasted, + CertifiedNodeBroadcasted, + StrongLinkReceived, + Finished, +} + +fn observe(counter: &HistogramVec, timestamp: u64, name: &str) { + if let Some(t) = duration_since_epoch().checked_sub(Duration::from_micros(timestamp)) { + counter.with_label_values(&[name]).observe(t.as_secs_f64()); + } +} + +/// Record the time during each stage of a node. +pub fn observe_node(timestamp: u64, stage: NodeStage) { + observe(&counters::NODE_TRACING, timestamp, stage.as_ref()); +} + +/// Record the time during each stage of a round. +pub fn observe_round(timestamp: u64, stage: RoundStage) { + observe(&counters::ROUND_TRACING, timestamp, stage.as_ref()); +} diff --git a/consensus/src/dag/order_rule.rs b/consensus/src/dag/order_rule.rs new file mode 100644 index 0000000000000..4cbbedec80529 --- /dev/null +++ b/consensus/src/dag/order_rule.rs @@ -0,0 +1,261 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::dag_store::DagStore; +use crate::dag::{ + adapter::OrderedNotifier, + anchor_election::AnchorElection, + dag_store::NodeStatus, + observability::{ + logging::{LogEvent, LogSchema}, + tracing::{observe_node, NodeStage}, + }, + storage::CommitEvent, + types::NodeMetadata, + CertifiedNode, +}; +use aptos_consensus_types::common::Round; +use aptos_infallible::Mutex; +use aptos_logger::debug; +use aptos_types::epoch_state::EpochState; +use std::sync::Arc; + +pub trait TOrderRule: Send + Sync { + fn process_new_node(&self, node_metadata: &NodeMetadata); + + fn process_all(&self); +} + +pub struct OrderRule { + epoch_state: Arc, + lowest_unordered_anchor_round: Round, + dag: Arc, + anchor_election: Arc, + notifier: Arc, + dag_window_size_config: Round, +} + +impl OrderRule { + pub fn new( + epoch_state: Arc, + lowest_unordered_anchor_round: Round, + dag: Arc, + anchor_election: Arc, + notifier: Arc, + dag_window_size_config: Round, + commit_events: Option>, + ) -> Self { + if let Some(commit_events) = commit_events { + // make sure it's sorted + assert!(commit_events + .windows(2) + .all(|w| (w[0].epoch(), w[0].round()) < (w[1].epoch(), w[1].round()))); + for event in commit_events { + if event.epoch() == epoch_state.epoch { + let maybe_anchor = dag + .read() + .get_node_by_round_author(event.round(), event.author()) + .cloned(); + if let Some(anchor) = maybe_anchor { + dag.write() + .reachable_mut(&anchor, None) + .for_each(|node_status| node_status.mark_as_ordered()); + } + } + anchor_election.update_reputation(event); + } + } + let mut order_rule = Self { + epoch_state, + lowest_unordered_anchor_round, + dag, + anchor_election, + notifier, + dag_window_size_config, + }; + // re-check if anything can be ordered to recover pending anchors + order_rule.process_all(); + order_rule + } + + /// Check if two rounds have the same parity + fn check_parity(r1: Round, r2: Round) -> bool { + (r1 ^ r2) & 1 == 0 + } + + /// Find if there's anchors that can be ordered start from `start_round` until `round`, + /// if so find next one until nothing can be ordered. + fn check_ordering_between(&mut self, mut start_round: Round, round: Round) { + while start_round <= round { + if let Some(direct_anchor) = + self.find_first_anchor_with_enough_votes(start_round, round) + { + let ordered_anchor = self.find_first_anchor_to_order(direct_anchor); + self.finalize_order(ordered_anchor); + // if there's any anchor being ordered, the loop continues to check if new anchor can be ordered as well. + start_round = self.lowest_unordered_anchor_round; + } else { + break; + } + } + } + + /// From the start round until the target_round, try to find if there's any anchor has enough votes to trigger ordering + fn find_first_anchor_with_enough_votes( + &self, + mut start_round: Round, + target_round: Round, + ) -> Option> { + let dag_reader = self.dag.read(); + while start_round < target_round { + let anchor_author = self.anchor_election.get_anchor(start_round); + // I "think" it's impossible to get ordered/committed node here but to double check + if let Some(anchor_node) = + dag_reader.get_node_by_round_author(start_round, &anchor_author) + { + // f+1 or 2f+1? + if dag_reader + .check_votes_for_node(anchor_node.metadata(), &self.epoch_state.verifier) + { + return Some(anchor_node.clone()); + } + } else { + debug!( + anchor = anchor_author, + "Anchor not found for round {}", start_round + ); + } + start_round += 2; + } + None + } + + /// Follow an anchor with enough votes to find the first anchor that's recursively reachable by its suffix anchor + fn find_first_anchor_to_order( + &self, + mut current_anchor: Arc, + ) -> Arc { + let dag_reader = self.dag.read(); + let anchor_round = current_anchor.round(); + let is_anchor = |metadata: &NodeMetadata| -> bool { + Self::check_parity(metadata.round(), anchor_round) + && *metadata.author() == self.anchor_election.get_anchor(metadata.round()) + }; + while let Some(prev_anchor) = dag_reader + .reachable( + Some(current_anchor.metadata().clone()).iter(), + Some(self.lowest_unordered_anchor_round), + |node_status| matches!(node_status, NodeStatus::Unordered { .. }), + ) + // skip the current anchor itself + .skip(1) + .map(|node_status| node_status.as_node()) + .find(|node| is_anchor(node.metadata())) + { + current_anchor = prev_anchor.clone(); + } + current_anchor + } + + /// Finalize the ordering with the given anchor node, update anchor election and construct blocks for execution. + fn finalize_order(&mut self, anchor: Arc) { + // Check we're in the expected instance + assert!(Self::check_parity( + self.lowest_unordered_anchor_round, + anchor.round(), + )); + let lowest_round_to_reach = anchor.round().saturating_sub(self.dag_window_size_config); + + // Ceil it to the closest unordered anchor round + let lowest_anchor_round = std::cmp::max( + self.lowest_unordered_anchor_round, + lowest_round_to_reach + + !Self::check_parity(lowest_round_to_reach, anchor.round()) as u64, + ); + assert!(Self::check_parity(lowest_anchor_round, anchor.round())); + + let failed_authors_and_rounds: Vec<_> = (lowest_anchor_round..anchor.round()) + .step_by(2) + .map(|failed_round| (failed_round, self.anchor_election.get_anchor(failed_round))) + .collect(); + let parents = anchor + .parents() + .iter() + .map(|cert| *cert.metadata().author()) + .collect(); + let event = CommitEvent::new( + anchor.id(), + parents, + failed_authors_and_rounds + .iter() + .map(|(_, author)| *author) + .collect(), + ); + self.anchor_election.update_reputation(event); + + let mut dag_writer = self.dag.write(); + let mut ordered_nodes: Vec<_> = dag_writer + .reachable_mut(&anchor, Some(lowest_round_to_reach)) + .map(|node_status| { + node_status.mark_as_ordered(); + node_status.as_node().clone() + }) + .collect(); + + observe_node(anchor.timestamp(), NodeStage::AnchorOrdered); + for node in ordered_nodes.iter().skip(1) { + observe_node(node.timestamp(), NodeStage::NodeOrdered); + } + ordered_nodes.reverse(); + + debug!( + LogSchema::new(LogEvent::OrderedAnchor), + id = anchor.id(), + lowest_unordered_anchor_round = self.lowest_unordered_anchor_round, + "Reached round {} with {} nodes", + lowest_anchor_round, + ordered_nodes.len() + ); + + self.lowest_unordered_anchor_round = anchor.round() + 1; + self.notifier + .send_ordered_nodes(ordered_nodes, failed_authors_and_rounds); + } + + /// Check if this node can trigger anchors to be ordered + pub fn process_new_node(&mut self, node_metadata: &NodeMetadata) { + let round = node_metadata.round(); + + debug!( + lowest_unordered_round = self.lowest_unordered_anchor_round, + node_round = round, + "Trigger Ordering" + ); + // If the node comes from the proposal round in the current instance, it can't trigger any ordering + if round <= self.lowest_unordered_anchor_round + || Self::check_parity(round, self.lowest_unordered_anchor_round) + { + return; + } + // This node's votes can trigger an anchor from previous round to be ordered. + let start_round = round - 1; + self.check_ordering_between(start_round, round) + } + + /// Check the whole dag to see if anything can be ordered. + pub fn process_all(&mut self) { + let start_round = self.lowest_unordered_anchor_round; + let round = self.dag.read().highest_round(); + self.check_ordering_between(start_round, round); + } +} + +impl TOrderRule for Mutex { + fn process_new_node(&self, node_metadata: &NodeMetadata) { + self.lock().process_new_node(node_metadata) + } + + fn process_all(&self) { + self.lock().process_all() + } +} diff --git a/consensus/src/dag/rb_handler.rs b/consensus/src/dag/rb_handler.rs new file mode 100644 index 0000000000000..b6948adbfbd93 --- /dev/null +++ b/consensus/src/dag/rb_handler.rs @@ -0,0 +1,263 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{dag_store::DagStore, health::HealthBackoff, order_rule::TOrderRule}; +use crate::{ + dag::{ + dag_fetcher::TFetchRequester, + dag_network::RpcHandler, + errors::NodeBroadcastHandleError, + observability::{ + logging::{LogEvent, LogSchema}, + tracing::{observe_node, NodeStage}, + }, + storage::DAGStorage, + types::{Node, NodeCertificate, Vote}, + NodeId, + }, + util::is_vtxn_expected, +}; +use anyhow::{bail, ensure}; +use aptos_config::config::DagPayloadConfig; +use aptos_consensus_types::common::{Author, Round}; +use aptos_infallible::Mutex; +use aptos_logger::{debug, error}; +use aptos_types::{ + epoch_state::EpochState, + on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorTxnConfig}, + validator_signer::ValidatorSigner, + validator_txn::ValidatorTransaction, +}; +use async_trait::async_trait; +use claims::assert_some; +use dashmap::DashSet; +use std::{collections::BTreeMap, mem, sync::Arc}; + +pub(crate) struct NodeBroadcastHandler { + dag: Arc, + order_rule: Arc, + /// Note: The mutex around BTreeMap is to work around Rust Sync semantics. + /// Fine grained concurrency is implemented by the DashSet below. + votes_by_round_peer: Mutex>>, + votes_fine_grained_lock: DashSet<(Round, Author)>, + signer: Arc, + epoch_state: Arc, + storage: Arc, + fetch_requester: Arc, + payload_config: DagPayloadConfig, + vtxn_config: ValidatorTxnConfig, + randomness_config: OnChainRandomnessConfig, + jwk_consensus_config: OnChainJWKConsensusConfig, + health_backoff: HealthBackoff, +} + +impl NodeBroadcastHandler { + pub fn new( + dag: Arc, + order_rule: Arc, + signer: Arc, + epoch_state: Arc, + storage: Arc, + fetch_requester: Arc, + payload_config: DagPayloadConfig, + vtxn_config: ValidatorTxnConfig, + randomness_config: OnChainRandomnessConfig, + jwk_consensus_config: OnChainJWKConsensusConfig, + health_backoff: HealthBackoff, + ) -> Self { + let epoch = epoch_state.epoch; + let votes_by_round_peer = read_votes_from_storage(&storage, epoch); + + Self { + dag, + order_rule, + votes_by_round_peer: Mutex::new(votes_by_round_peer), + votes_fine_grained_lock: DashSet::with_capacity(epoch_state.verifier.len() * 10), + signer, + epoch_state, + storage, + fetch_requester, + payload_config, + vtxn_config, + randomness_config, + jwk_consensus_config, + health_backoff, + } + } + + pub fn gc(&self) { + let lowest_round = self.dag.read().lowest_round(); + if let Err(e) = self.gc_before_round(lowest_round) { + error!("Error deleting votes: {}", e); + } + } + + pub fn gc_before_round(&self, min_round: Round) -> anyhow::Result<()> { + let mut votes_by_round_peer_guard = self.votes_by_round_peer.lock(); + let to_retain = votes_by_round_peer_guard.split_off(&min_round); + let to_delete = mem::replace(&mut *votes_by_round_peer_guard, to_retain); + drop(votes_by_round_peer_guard); + + let to_delete = to_delete + .iter() + .flat_map(|(r, peer_and_digest)| { + peer_and_digest + .iter() + .map(|(author, _)| NodeId::new(self.epoch_state.epoch, *r, *author)) + }) + .collect(); + self.storage.delete_votes(to_delete) + } + + fn validate(&self, node: Node) -> anyhow::Result { + ensure!( + node.epoch() == self.epoch_state.epoch, + "different epoch {}, current {}", + node.epoch(), + self.epoch_state.epoch + ); + + let num_vtxns = node.validator_txns().len() as u64; + ensure!(num_vtxns <= self.vtxn_config.per_block_limit_txn_count()); + for vtxn in node.validator_txns() { + ensure!( + is_vtxn_expected(&self.randomness_config, &self.jwk_consensus_config, vtxn), + "unexpected validator transaction: {:?}", + vtxn.topic() + ); + } + let vtxn_total_bytes = node + .validator_txns() + .iter() + .map(ValidatorTransaction::size_in_bytes) + .sum::() as u64; + ensure!(vtxn_total_bytes <= self.vtxn_config.per_block_limit_total_bytes()); + + let num_txns = num_vtxns + node.payload().len() as u64; + let txn_bytes = vtxn_total_bytes + node.payload().size() as u64; + ensure!(num_txns <= self.payload_config.max_receiving_txns_per_round); + ensure!(txn_bytes <= self.payload_config.max_receiving_size_per_round_bytes); + + let current_round = node.metadata().round(); + + let dag_reader = self.dag.read(); + let lowest_round = dag_reader.lowest_round(); + + ensure!( + current_round >= lowest_round, + NodeBroadcastHandleError::StaleRound(current_round) + ); + + // check which parents are missing in the DAG + let missing_parents: Vec = node + .parents() + .iter() + .filter(|parent| !dag_reader.exists(parent.metadata())) + .cloned() + .collect(); + drop(dag_reader); // Drop the DAG store early as it is no longer required + + if !missing_parents.is_empty() { + // For each missing parent, verify their signatures and voting power. + // Otherwise, a malicious node can send bad nodes with fake parents + // and cause this peer to issue unnecessary fetch requests. + ensure!( + missing_parents + .iter() + .all(|parent| { parent.verify(&self.epoch_state.verifier).is_ok() }), + NodeBroadcastHandleError::InvalidParent + ); + + // Don't issue fetch requests for parents of the lowest round in the DAG + // because they are already GC'ed + if current_round > lowest_round { + if let Err(err) = self.fetch_requester.request_for_node(node) { + error!("request to fetch failed: {}", err); + } + bail!(NodeBroadcastHandleError::MissingParents); + } + } + + Ok(node) + } +} + +fn read_votes_from_storage( + storage: &Arc, + epoch: u64, +) -> BTreeMap> { + let mut votes_by_round_peer = BTreeMap::new(); + + let all_votes = storage.get_votes().unwrap_or_default(); + let mut to_delete = vec![]; + for (node_id, vote) in all_votes { + if node_id.epoch() == epoch { + votes_by_round_peer + .entry(node_id.round()) + .or_insert_with(BTreeMap::new) + .insert(*node_id.author(), vote); + } else { + to_delete.push(node_id); + } + } + if let Err(err) = storage.delete_votes(to_delete) { + error!("unable to clear old signatures: {}", err); + } + + votes_by_round_peer +} + +#[async_trait] +impl RpcHandler for NodeBroadcastHandler { + type Request = Node; + type Response = Vote; + + async fn process(&self, node: Self::Request) -> anyhow::Result { + ensure!( + !self.health_backoff.stop_voting(), + NodeBroadcastHandleError::VoteRefused + ); + + let key = (node.round(), *node.author()); + ensure!( + self.votes_fine_grained_lock.insert(key), + "concurrent insertion" + ); + defer!({ + assert_some!(self.votes_fine_grained_lock.remove(&key)); + }); + + let node = self.validate(node)?; + observe_node(node.timestamp(), NodeStage::NodeReceived); + debug!(LogSchema::new(LogEvent::ReceiveNode) + .remote_peer(*node.author()) + .round(node.round())); + + if let Some(ack) = self + .votes_by_round_peer + .lock() + .entry(node.round()) + .or_default() + .get(node.author()) + { + return Ok(ack.clone()); + } + + let signature = node.sign_vote(&self.signer)?; + let vote = Vote::new(node.metadata().clone(), signature); + self.storage.save_vote(&node.id(), &vote)?; + self.votes_by_round_peer + .lock() + .get_mut(&node.round()) + .expect("must exist") + .insert(*node.author(), vote.clone()); + + self.dag.write().update_votes(&node, false); + self.order_rule.process_new_node(node.metadata()); + + debug!(LogSchema::new(LogEvent::Vote) + .remote_peer(*node.author()) + .round(node.round())); + Ok(vote) + } +} diff --git a/consensus/src/dag/round_state.rs b/consensus/src/dag/round_state.rs new file mode 100644 index 0000000000000..77a4afd9ca3e4 --- /dev/null +++ b/consensus/src/dag/round_state.rs @@ -0,0 +1,205 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::dag::{ + observability::tracing::{observe_round, RoundStage}, + types::NodeCertificate, +}; +use anyhow::ensure; +use aptos_consensus_types::common::Round; +use aptos_infallible::{duration_since_epoch, Mutex}; +use aptos_types::epoch_state::EpochState; +use std::{cmp::Ordering, sync::Arc, time::Duration}; +use tokio::task::JoinHandle; + +pub struct RoundState { + current_round: Mutex, + event_sender: tokio::sync::mpsc::UnboundedSender, + responsive_check: Box, +} + +impl RoundState { + pub fn new( + event_sender: tokio::sync::mpsc::UnboundedSender, + responsive_check: Box, + ) -> Self { + Self { + current_round: Mutex::new(0), + event_sender, + responsive_check, + } + } + + pub fn check_for_new_round( + &self, + highest_strong_links_round: Round, + strong_links: Vec, + minimum_delay: Duration, + ) { + let current_round = *self.current_round.lock(); + match current_round.cmp(&highest_strong_links_round) { + // we're behind, move forward immediately + Ordering::Less => { + // the receiver can be dropped if we move to a new epoch + let _ = self.event_sender.send(highest_strong_links_round + 1); + }, + Ordering::Equal => self.responsive_check.check_for_new_round( + highest_strong_links_round, + strong_links, + minimum_delay, + ), + Ordering::Greater => (), + } + } + + pub fn current_round(&self) -> Round { + *self.current_round.lock() + } + + pub fn set_current_round(&self, new_round: Round) -> anyhow::Result<()> { + let mut current_round = self.current_round.lock(); + ensure!( + *current_round < new_round, + "current round {} is newer than new round {}", + current_round, + new_round + ); + *current_round = new_round; + self.responsive_check.reset(); + Ok(()) + } +} + +/// Interface to decide if we should move forward to a new round +pub trait ResponsiveCheck: Send + Sync { + fn check_for_new_round( + &self, + highest_strong_links_round: Round, + strong_links: Vec, + health_backoff_delay: Duration, + ); + + fn reset(&self); +} + +/// Move as fast as 2f+1 +pub struct OptimisticResponsive { + event_sender: tokio::sync::mpsc::UnboundedSender, +} + +impl OptimisticResponsive { + pub fn new(event_sender: tokio::sync::mpsc::UnboundedSender) -> Self { + Self { event_sender } + } +} + +impl ResponsiveCheck for OptimisticResponsive { + fn check_for_new_round( + &self, + highest_strong_links_round: Round, + _strong_links: Vec, + _health_backoff_delay: Duration, + ) { + let new_round = highest_strong_links_round + 1; + let _ = self.event_sender.send(new_round); + } + + fn reset(&self) {} +} + +enum State { + Initial, + Scheduled(JoinHandle<()>), + Sent, +} + +struct AdaptiveResponsiveInner { + start_time: Duration, + state: State, +} + +/// More sophisticated strategy to move round forward given 2f+1 strong links +/// Delay if backpressure is triggered. +/// Move as soon as 3f+1 is ready. (TODO: make it configurable) +/// Move if minimal wait time is reached. +pub struct AdaptiveResponsive { + inner: Mutex, + epoch_state: Arc, + minimal_wait_time: Duration, + event_sender: tokio::sync::mpsc::UnboundedSender, +} + +impl AdaptiveResponsive { + pub fn new( + event_sender: tokio::sync::mpsc::UnboundedSender, + epoch_state: Arc, + minimal_wait_time: Duration, + ) -> Self { + Self { + inner: Mutex::new(AdaptiveResponsiveInner { + start_time: duration_since_epoch(), + state: State::Initial, + }), + epoch_state, + minimal_wait_time, + event_sender, + } + } +} + +impl ResponsiveCheck for AdaptiveResponsive { + fn check_for_new_round( + &self, + highest_strong_links_round: Round, + strong_links: Vec, + health_backoff_delay: Duration, + ) { + let mut inner = self.inner.lock(); + if matches!(inner.state, State::Sent) { + return; + } + let new_round = highest_strong_links_round + 1; + observe_round( + inner.start_time.as_micros() as u64, + RoundStage::StrongLinkReceived, + ); + let voting_power = self + .epoch_state + .verifier + .sum_voting_power(strong_links.iter().map(|cert| cert.metadata().author())) + .expect("Unable to sum voting power from strong links"); + + let (wait_time, is_health_backoff) = if self.minimal_wait_time < health_backoff_delay { + (health_backoff_delay, true) + } else { + (self.minimal_wait_time, false) + }; + + // voting power == 3f+1 and pass wait time if health backoff + let duration_since_start = duration_since_epoch().saturating_sub(inner.start_time); + if voting_power == self.epoch_state.verifier.total_voting_power() + && (duration_since_start >= wait_time || !is_health_backoff) + { + let _ = self.event_sender.send(new_round); + if let State::Scheduled(handle) = std::mem::replace(&mut inner.state, State::Sent) { + handle.abort(); + } + } else if matches!(inner.state, State::Initial) { + // wait until minimal time reaches before sending + let sender = self.event_sender.clone(); + let wait_time = wait_time.saturating_sub(duration_since_start); + let handle = tokio::spawn(async move { + tokio::time::sleep(wait_time).await; + let _ = sender.send(new_round); + }); + inner.state = State::Scheduled(handle); + } + } + + fn reset(&self) { + let mut inner = self.inner.lock(); + + inner.start_time = duration_since_epoch(); + inner.state = State::Initial; + } +} diff --git a/consensus/src/dag/storage.rs b/consensus/src/dag/storage.rs new file mode 100644 index 0000000000000..e090b9d9f17b9 --- /dev/null +++ b/consensus/src/dag/storage.rs @@ -0,0 +1,72 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::{types::Vote, NodeId}; +use crate::dag::{CertifiedNode, Node}; +use aptos_consensus_types::common::{Author, Round}; +use aptos_crypto::HashValue; +use aptos_types::ledger_info::LedgerInfoWithSignatures; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct CommitEvent { + node_id: NodeId, + parents: Vec, + failed_authors: Vec, +} + +impl CommitEvent { + pub fn new(node_id: NodeId, parents: Vec, failed_authors: Vec) -> Self { + CommitEvent { + node_id, + parents, + failed_authors, + } + } + + pub fn epoch(&self) -> u64 { + self.node_id.epoch() + } + + pub fn round(&self) -> Round { + self.node_id.round() + } + + pub fn author(&self) -> &Author { + self.node_id.author() + } + + pub fn parents(&self) -> &[Author] { + &self.parents + } + + pub fn failed_authors(&self) -> &[Author] { + &self.failed_authors + } +} + +pub trait DAGStorage: Send + Sync { + fn save_pending_node(&self, node: &Node) -> anyhow::Result<()>; + + fn get_pending_node(&self) -> anyhow::Result>; + + fn delete_pending_node(&self) -> anyhow::Result<()>; + + fn save_vote(&self, node_id: &NodeId, vote: &Vote) -> anyhow::Result<()>; + + fn get_votes(&self) -> anyhow::Result>; + + fn delete_votes(&self, node_ids: Vec) -> anyhow::Result<()>; + + fn save_certified_node(&self, node: &CertifiedNode) -> anyhow::Result<()>; + + fn get_certified_nodes(&self) -> anyhow::Result>; + + fn delete_certified_nodes(&self, digests: Vec) -> anyhow::Result<()>; + + fn get_latest_k_committed_events(&self, k: u64) -> anyhow::Result>; + + fn get_latest_ledger_info(&self) -> anyhow::Result; + + fn get_epoch_to_proposers(&self) -> HashMap>; +} diff --git a/consensus/src/dag/tests/dag_test.rs b/consensus/src/dag/tests/dag_test.rs new file mode 100644 index 0000000000000..c050b81c55cbf --- /dev/null +++ b/consensus/src/dag/tests/dag_test.rs @@ -0,0 +1,317 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::helpers::MockPayloadManager; +use crate::dag::{ + dag_store::DagStore, + storage::{CommitEvent, DAGStorage}, + tests::helpers::{new_certified_node, TEST_DAG_WINDOW}, + types::{CertifiedNode, DagSnapshotBitmask, Node}, + NodeId, Vote, +}; +use aptos_consensus_types::common::Author; +use aptos_crypto::HashValue; +use aptos_infallible::Mutex; +use aptos_types::{ + epoch_state::EpochState, ledger_info::LedgerInfoWithSignatures, + validator_signer::ValidatorSigner, validator_verifier::random_validator_verifier, +}; +use std::{collections::HashMap, sync::Arc}; + +pub struct MockStorage { + node_data: Mutex>, + vote_data: Mutex>, + certified_node_data: Mutex>, + latest_ledger_info: Option, + epoch_state: Option>, +} + +impl MockStorage { + pub fn new() -> Self { + Self { + node_data: Mutex::new(None), + vote_data: Mutex::new(HashMap::new()), + certified_node_data: Mutex::new(HashMap::new()), + latest_ledger_info: None, + epoch_state: None, + } + } + + pub fn new_with_ledger_info( + ledger_info: LedgerInfoWithSignatures, + epoch_state: Arc, + ) -> Self { + Self { + node_data: Mutex::new(None), + vote_data: Mutex::new(HashMap::new()), + certified_node_data: Mutex::new(HashMap::new()), + latest_ledger_info: Some(ledger_info), + epoch_state: Some(epoch_state), + } + } +} + +impl DAGStorage for MockStorage { + fn save_pending_node(&self, node: &Node) -> anyhow::Result<()> { + self.node_data.lock().replace(node.clone()); + Ok(()) + } + + fn get_pending_node(&self) -> anyhow::Result> { + Ok(self.node_data.lock().clone()) + } + + fn delete_pending_node(&self) -> anyhow::Result<()> { + self.node_data.lock().take(); + Ok(()) + } + + fn save_vote(&self, node_id: &NodeId, vote: &Vote) -> anyhow::Result<()> { + self.vote_data.lock().insert(node_id.clone(), vote.clone()); + Ok(()) + } + + fn get_votes(&self) -> anyhow::Result> { + Ok(self.vote_data.lock().clone().into_iter().collect()) + } + + fn delete_votes(&self, node_ids: Vec) -> anyhow::Result<()> { + for node_id in node_ids { + self.vote_data.lock().remove(&node_id); + } + Ok(()) + } + + fn save_certified_node(&self, node: &CertifiedNode) -> anyhow::Result<()> { + self.certified_node_data + .lock() + .insert(node.digest(), node.clone()); + Ok(()) + } + + fn get_certified_nodes(&self) -> anyhow::Result> { + Ok(self + .certified_node_data + .lock() + .clone() + .into_iter() + .collect()) + } + + fn delete_certified_nodes(&self, digests: Vec) -> anyhow::Result<()> { + for digest in digests { + self.certified_node_data.lock().remove(&digest); + } + Ok(()) + } + + fn get_latest_k_committed_events(&self, _k: u64) -> anyhow::Result> { + Ok(vec![]) + } + + fn get_latest_ledger_info(&self) -> anyhow::Result { + self.latest_ledger_info + .clone() + .ok_or_else(|| anyhow::anyhow!("ledger info not set")) + } + + fn get_epoch_to_proposers(&self) -> HashMap> { + self.epoch_state + .as_ref() + .map(|epoch_state| { + [( + epoch_state.epoch, + epoch_state.verifier.get_ordered_account_addresses(), + )] + .into() + }) + .unwrap_or_default() + } +} + +fn setup() -> ( + Vec, + Arc, + DagStore, + Arc, +) { + let (signers, validator_verifier) = random_validator_verifier(4, None, false); + let epoch_state = Arc::new(EpochState { + epoch: 1, + verifier: validator_verifier, + }); + let storage = Arc::new(MockStorage::new()); + let payload_manager = Arc::new(MockPayloadManager {}); + let dag = DagStore::new( + epoch_state.clone(), + storage.clone(), + payload_manager, + 1, + TEST_DAG_WINDOW, + ); + (signers, epoch_state, dag, storage) +} + +#[test] +fn test_dag_insertion_succeed() { + let (signers, epoch_state, dag, _) = setup(); + + // Round 1 - nodes 0, 1, 2 links to vec![] + for signer in &signers[0..3] { + let node = new_certified_node(1, signer.author(), vec![]); + assert!(dag.write().add_node_for_test(node).is_ok()); + } + let parents = dag + .read() + .get_strong_links_for_round(1, &epoch_state.verifier) + .unwrap(); + + // Round 2 nodes 0, 1, 2 links to 0, 1, 2 + for signer in &signers[0..3] { + let node = new_certified_node(2, signer.author(), parents.clone()); + assert!(dag.write().add_node_for_test(node).is_ok()); + } + + // Round 3 nodes 1, 2 links to 0, 1, 2 + let parents = dag + .read() + .get_strong_links_for_round(2, &epoch_state.verifier) + .unwrap(); + + for signer in &signers[1..3] { + let node = new_certified_node(3, signer.author(), parents.clone()); + assert!(dag.write().add_node_for_test(node).is_ok()); + } + + // not enough strong links + assert!(dag + .read() + .get_strong_links_for_round(3, &epoch_state.verifier) + .is_none()); +} + +#[test] +fn test_dag_insertion_failure() { + let (signers, epoch_state, dag, _) = setup(); + + // Round 1 - nodes 0, 1, 2 links to vec![] + for signer in &signers[0..3] { + let node = new_certified_node(1, signer.author(), vec![]); + assert!(dag.write().add_node_for_test(node.clone()).is_ok()); + // duplicate node + assert!(dag.write().add_node_for_test(node).is_err()); + } + + let missing_node = new_certified_node(1, signers[3].author(), vec![]); + let mut parents = dag + .read() + .get_strong_links_for_round(1, &epoch_state.verifier) + .unwrap(); + parents.push(missing_node.certificate()); + + let node = new_certified_node(2, signers[0].author(), parents.clone()); + // parents not exist + assert!(dag.write().add_node_for_test(node).is_err()); + + let node = new_certified_node(3, signers[0].author(), vec![]); + // round too high + assert!(dag.write().add_node_for_test(node).is_err()); + + let node = new_certified_node(2, signers[0].author(), parents[0..3].to_vec()); + assert!(dag.write().add_node_for_test(node).is_ok()); + let node = new_certified_node(2, signers[0].author(), vec![]); + // equivocation node + assert!(dag.write().add_node_for_test(node).is_err()); +} + +#[test] +fn test_dag_recover_from_storage() { + let (signers, epoch_state, dag, storage) = setup(); + + let mut metadatas = vec![]; + + for round in 1..10 { + let parents = dag + .read() + .get_strong_links_for_round(round, &epoch_state.verifier) + .unwrap_or_default(); + for signer in &signers[0..3] { + let node = new_certified_node(round, signer.author(), parents.clone()); + metadatas.push(node.metadata().clone()); + assert!(dag.add_node(node).is_ok()); + } + } + let new_dag = DagStore::new( + epoch_state.clone(), + storage.clone(), + Arc::new(MockPayloadManager {}), + 0, + TEST_DAG_WINDOW, + ); + + for metadata in &metadatas { + assert!(new_dag.read().exists(metadata)); + } + + let new_epoch_state = Arc::new(EpochState { + epoch: 2, + verifier: epoch_state.verifier.clone(), + }); + + let _new_epoch_dag = DagStore::new( + new_epoch_state, + storage.clone(), + Arc::new(MockPayloadManager {}), + 0, + TEST_DAG_WINDOW, + ); + assert!(storage.certified_node_data.lock().is_empty()); +} + +#[test] +fn test_dag_bitmask() { + let (signers, epoch_state, dag, _) = setup(); + + assert_eq!( + dag.read().bitmask(TEST_DAG_WINDOW), + DagSnapshotBitmask::new(1, vec![vec![false; 4]; TEST_DAG_WINDOW as usize]) + ); + + for round in 1..5 { + let parents = dag + .read() + .get_strong_links_for_round(round - 1, &epoch_state.verifier) + .unwrap_or_default(); + if round > 1 { + assert!(!parents.is_empty()); + } + for signer in &signers[0..3] { + let node = new_certified_node(round, signer.author(), parents.clone()); + assert!(dag.write().add_node_for_test(node).is_ok()); + } + } + let mut bitmask = vec![vec![true, true, true, false]; 2]; + bitmask.resize(TEST_DAG_WINDOW as usize + 1, vec![false; 4]); + assert_eq!(dag.read().bitmask(8), DagSnapshotBitmask::new(3, bitmask)); + + // Populate the fourth author for all rounds + for round in 1..5 { + let parents = dag + .read() + .get_strong_links_for_round(round - 1, &epoch_state.verifier) + .unwrap_or_default(); + if round > 1 { + assert!(!parents.is_empty()); + } + let node = new_certified_node(round, signers[3].author(), parents.clone()); + assert!(dag.write().add_node_for_test(node).is_ok()); + } + assert_eq!( + dag.read().bitmask(10), + DagSnapshotBitmask::new(5, vec![vec![false; 4]; 6]) + ); + assert_eq!( + dag.read().bitmask(6), + DagSnapshotBitmask::new(5, vec![vec![false; 4]; 2]) + ); +} diff --git a/consensus/src/dag/tests/mod.rs b/consensus/src/dag/tests/mod.rs new file mode 100644 index 0000000000000..eee0c463dede8 --- /dev/null +++ b/consensus/src/dag/tests/mod.rs @@ -0,0 +1,13 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod dag_driver_tests; +mod dag_network_test; +mod dag_state_sync_tests; +mod dag_test; +mod fetcher_test; +mod helpers; +mod integration_tests; +mod order_rule_tests; +mod rb_handler_tests; +mod types_test; diff --git a/consensus/src/dag/tests/rb_handler_tests.rs b/consensus/src/dag/tests/rb_handler_tests.rs new file mode 100644 index 0000000000000..87729b7078316 --- /dev/null +++ b/consensus/src/dag/tests/rb_handler_tests.rs @@ -0,0 +1,258 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::dag::{ + dag_fetcher::TFetchRequester, + dag_store::DagStore, + errors::NodeBroadcastHandleError, + health::{HealthBackoff, NoChainHealth, NoPipelineBackpressure}, + rb_handler::NodeBroadcastHandler, + storage::DAGStorage, + tests::{ + dag_test::MockStorage, + helpers::{new_node, MockOrderRule, MockPayloadManager, TEST_DAG_WINDOW}, + }, + types::NodeCertificate, + NodeId, RpcHandler, Vote, +}; +use aptos_config::config::DagPayloadConfig; +use aptos_types::{ + aggregate_signature::PartialSignatures, + epoch_state::EpochState, + on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorTxnConfig}, + validator_verifier::random_validator_verifier, +}; +use claims::{assert_ok, assert_ok_eq}; +use futures::executor::block_on; +use std::{collections::BTreeMap, sync::Arc}; + +struct MockFetchRequester {} + +impl TFetchRequester for MockFetchRequester { + fn request_for_node(&self, _node: crate::dag::Node) -> anyhow::Result<()> { + Ok(()) + } + + fn request_for_certified_node(&self, _node: crate::dag::CertifiedNode) -> anyhow::Result<()> { + Ok(()) + } +} + +#[tokio::test] +async fn test_node_broadcast_receiver_succeed() { + let (signers, validator_verifier) = random_validator_verifier(4, None, false); + let epoch_state = Arc::new(EpochState { + epoch: 1, + verifier: validator_verifier.clone(), + }); + let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); + + // Scenario: Start DAG from beginning + let storage = Arc::new(MockStorage::new()); + let dag = Arc::new(DagStore::new( + epoch_state.clone(), + storage.clone(), + Arc::new(MockPayloadManager {}), + 0, + TEST_DAG_WINDOW, + )); + let order_rule = Arc::new(MockOrderRule {}); + + let health_backoff = HealthBackoff::new( + epoch_state.clone(), + NoChainHealth::new(), + NoPipelineBackpressure::new(), + ); + + let wellformed_node = new_node(1, 10, signers[0].author(), vec![]); + let equivocating_node = new_node(1, 20, signers[0].author(), vec![]); + + assert_ne!(wellformed_node.digest(), equivocating_node.digest()); + + let rb_receiver = NodeBroadcastHandler::new( + dag, + order_rule, + signers[3].clone(), + epoch_state.clone(), + storage.clone(), + Arc::new(MockFetchRequester {}), + DagPayloadConfig::default(), + ValidatorTxnConfig::default_disabled(), + OnChainRandomnessConfig::default_disabled(), + OnChainJWKConsensusConfig::default_disabled(), + health_backoff, + ); + + let expected_result = Vote::new( + wellformed_node.metadata().clone(), + wellformed_node.sign_vote(&signers[3]).unwrap(), + ); + // expect an ack for a valid message + assert_ok_eq!(rb_receiver.process(wellformed_node).await, expected_result); + // expect the original ack for any future message from same author + assert_ok_eq!( + rb_receiver.process(equivocating_node).await, + expected_result + ); +} + +// TODO: Unit test node broad receiver with a pruned DAG store. Possibly need a validator verifier trait. + +#[tokio::test] +async fn test_node_broadcast_receiver_failure() { + let (signers, validator_verifier) = random_validator_verifier(4, None, false); + let epoch_state = Arc::new(EpochState { + epoch: 1, + verifier: validator_verifier.clone(), + }); + let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); + + let mut rb_receivers: Vec<_> = signers + .iter() + .map(|signer| { + let storage = Arc::new(MockStorage::new()); + let dag = Arc::new(DagStore::new( + epoch_state.clone(), + storage.clone(), + Arc::new(MockPayloadManager {}), + 0, + TEST_DAG_WINDOW, + )); + let order_rule = Arc::new(MockOrderRule {}); + + NodeBroadcastHandler::new( + dag, + order_rule, + signer.clone(), + epoch_state.clone(), + storage, + Arc::new(MockFetchRequester {}), + DagPayloadConfig::default(), + ValidatorTxnConfig::default_disabled(), + OnChainRandomnessConfig::default_disabled(), + OnChainJWKConsensusConfig::default_disabled(), + HealthBackoff::new( + epoch_state.clone(), + NoChainHealth::new(), + NoPipelineBackpressure::new(), + ), + ) + }) + .collect(); + + // Round 1 + let node = new_node(1, 10, signers[0].author(), vec![]); + let vote = rb_receivers[1].process(node.clone()).await.unwrap(); + + // Round 2 with invalid parent + let partial_sigs = PartialSignatures::new(BTreeMap::from([( + signers[1].author(), + vote.signature().clone(), + )])); + let node_cert = NodeCertificate::new( + node.metadata().clone(), + validator_verifier + .aggregate_signatures(&partial_sigs) + .unwrap(), + ); + let node = new_node(2, 20, signers[0].author(), vec![node_cert]); + assert_eq!( + rb_receivers[1].process(node).await.unwrap_err().to_string(), + NodeBroadcastHandleError::InvalidParent.to_string(), + ); + + // Round 1 - add all nodes + let node_certificates: Vec<_> = signers + .iter() + .map(|signer| { + let node = new_node(1, 10, signer.author(), vec![]); + let mut partial_sigs = PartialSignatures::empty(); + rb_receivers + .iter_mut() + .zip(&signers) + .for_each(|(rb_receiver, signer)| { + let sig = block_on(rb_receiver.process(node.clone())).unwrap(); + partial_sigs.add_signature(signer.author(), sig.signature().clone()) + }); + NodeCertificate::new( + node.metadata().clone(), + validator_verifier + .aggregate_signatures(&partial_sigs) + .unwrap(), + ) + }) + .collect(); + + // Add Round 2 node with proper certificates + let node = new_node(2, 20, signers[0].author(), node_certificates); + assert_eq!( + rb_receivers[0].process(node).await.unwrap_err().to_string(), + NodeBroadcastHandleError::MissingParents.to_string() + ); +} + +#[tokio::test] +async fn test_node_broadcast_receiver_storage() { + let (signers, validator_verifier) = random_validator_verifier(4, None, false); + let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); + let epoch_state = Arc::new(EpochState { + epoch: 1, + verifier: validator_verifier, + }); + + let storage = Arc::new(MockStorage::new()); + let dag = Arc::new(DagStore::new( + epoch_state.clone(), + storage.clone(), + Arc::new(MockPayloadManager {}), + 0, + TEST_DAG_WINDOW, + )); + let order_rule = Arc::new(MockOrderRule {}); + + let node = new_node(1, 10, signers[0].author(), vec![]); + + let rb_receiver = NodeBroadcastHandler::new( + dag.clone(), + order_rule.clone(), + signers[3].clone(), + epoch_state.clone(), + storage.clone(), + Arc::new(MockFetchRequester {}), + DagPayloadConfig::default(), + ValidatorTxnConfig::default_disabled(), + OnChainRandomnessConfig::default_disabled(), + OnChainJWKConsensusConfig::default_disabled(), + HealthBackoff::new( + epoch_state.clone(), + NoChainHealth::new(), + NoPipelineBackpressure::new(), + ), + ); + let sig = rb_receiver.process(node).await.expect("must succeed"); + + assert_ok_eq!(storage.get_votes(), vec![( + NodeId::new(1, 1, signers[0].author()), + sig + )],); + + let rb_receiver = NodeBroadcastHandler::new( + dag, + order_rule.clone(), + signers[3].clone(), + epoch_state.clone(), + storage.clone(), + Arc::new(MockFetchRequester {}), + DagPayloadConfig::default(), + ValidatorTxnConfig::default_disabled(), + OnChainRandomnessConfig::default_disabled(), + OnChainJWKConsensusConfig::default_disabled(), + HealthBackoff::new( + epoch_state, + NoChainHealth::new(), + NoPipelineBackpressure::new(), + ), + ); + assert_ok!(rb_receiver.gc_before_round(2)); + assert_eq!(storage.get_votes().unwrap().len(), 0); +} diff --git a/consensus/src/dag/types.rs b/consensus/src/dag/types.rs new file mode 100644 index 0000000000000..154af7d4f56c5 --- /dev/null +++ b/consensus/src/dag/types.rs @@ -0,0 +1,1003 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::errors::DAGRpcError; +use crate::{ + dag::observability::{ + logging::{LogEvent, LogSchema}, + tracing::{observe_node, NodeStage}, + }, + network::TConsensusMsg, + network_interface::ConsensusMsg, +}; +use anyhow::{bail, ensure}; +use aptos_bitvec::BitVec; +use aptos_consensus_types::common::{Author, Payload, Round}; +use aptos_crypto::{ + bls12381::Signature, + hash::{CryptoHash, CryptoHasher}, + CryptoMaterialError, HashValue, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use aptos_enum_conversion_derive::EnumConversion; +use aptos_infallible::Mutex; +use aptos_logger::debug; +use aptos_reliable_broadcast::{BroadcastStatus, RBMessage}; +use aptos_types::{ + aggregate_signature::{AggregateSignature, PartialSignatures}, + epoch_state::EpochState, + ledger_info::LedgerInfoWithSignatures, + validator_signer::ValidatorSigner, + validator_txn::ValidatorTransaction, + validator_verifier::ValidatorVerifier, +}; +use futures_channel::oneshot; +use serde::{Deserialize, Serialize}; +use std::{ + cmp::min, + collections::HashSet, + fmt::{Display, Formatter}, + ops::{Deref, DerefMut}, + sync::Arc, +}; + +#[derive(Clone, Serialize, Deserialize, CryptoHasher, Debug, PartialEq)] +pub enum Extensions { + Empty, + // Reserved for future extensions such as randomness shares +} + +impl Extensions { + pub fn empty() -> Self { + Self::Empty + } +} + +#[derive(Serialize)] +struct NodeWithoutDigest<'a> { + epoch: u64, + round: Round, + author: Author, + timestamp: u64, + validator_txns: &'a Vec, + payload: &'a Payload, + parents: &'a Vec, + extensions: &'a Extensions, +} + +impl<'a> CryptoHash for NodeWithoutDigest<'a> { + type Hasher = NodeHasher; + + fn hash(&self) -> HashValue { + let mut state = Self::Hasher::new(); + let bytes = bcs::to_bytes(&self).expect("Unable to serialize node"); + state.update(&bytes); + state.finish() + } +} + +impl<'a> From<&'a Node> for NodeWithoutDigest<'a> { + fn from(node: &'a Node) -> Self { + Self { + epoch: node.metadata.epoch, + round: node.metadata.round, + author: node.metadata.author, + timestamp: node.metadata.timestamp, + validator_txns: &node.validator_txns, + payload: &node.payload, + parents: &node.parents, + extensions: &node.extensions, + } + } +} + +/// Represents the metadata about the node, without payload and parents from Node +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, CryptoHasher, BCSCryptoHash)] +pub struct NodeMetadata { + node_id: NodeId, + timestamp: u64, + digest: HashValue, +} + +impl NodeMetadata { + #[cfg(test)] + pub fn new_for_test( + epoch: u64, + round: Round, + author: Author, + timestamp: u64, + digest: HashValue, + ) -> Self { + Self { + node_id: NodeId { + epoch, + round, + author, + }, + timestamp, + digest, + } + } + + pub fn digest(&self) -> &HashValue { + &self.digest + } + + pub fn round(&self) -> Round { + self.round + } + + pub fn author(&self) -> &Author { + &self.author + } + + pub fn epoch(&self) -> u64 { + self.epoch + } + + pub fn timestamp(&self) -> u64 { + self.timestamp + } +} + +impl Deref for NodeMetadata { + type Target = NodeId; + + fn deref(&self) -> &Self::Target { + &self.node_id + } +} + +/// Node representation in the DAG, parents contain 2f+1 strong links (links to previous round) +#[derive(Clone, Serialize, Deserialize, CryptoHasher, Debug, PartialEq)] +pub struct Node { + metadata: NodeMetadata, + validator_txns: Vec, + payload: Payload, + parents: Vec, + extensions: Extensions, +} + +impl Node { + pub fn new( + epoch: u64, + round: Round, + author: Author, + timestamp: u64, + validator_txns: Vec, + payload: Payload, + parents: Vec, + extensions: Extensions, + ) -> Self { + let digest = Self::calculate_digest_internal( + epoch, + round, + author, + timestamp, + &validator_txns, + &payload, + &parents, + &extensions, + ); + + Self { + metadata: NodeMetadata { + node_id: NodeId { + epoch, + round, + author, + }, + timestamp, + digest, + }, + validator_txns, + payload, + parents, + extensions, + } + } + + #[cfg(test)] + pub fn new_for_test( + metadata: NodeMetadata, + payload: Payload, + parents: Vec, + extensions: Extensions, + ) -> Self { + Self { + metadata, + validator_txns: vec![], + payload, + parents, + extensions, + } + } + + /// Calculate the node digest based on all fields in the node + fn calculate_digest_internal( + epoch: u64, + round: Round, + author: Author, + timestamp: u64, + validator_txns: &Vec, + payload: &Payload, + parents: &Vec, + extensions: &Extensions, + ) -> HashValue { + let node_with_out_digest = NodeWithoutDigest { + epoch, + round, + author, + timestamp, + validator_txns, + payload, + parents, + extensions, + }; + node_with_out_digest.hash() + } + + fn calculate_digest(&self) -> HashValue { + Self::calculate_digest_internal( + self.metadata.epoch, + self.metadata.round, + self.metadata.author, + self.metadata.timestamp, + &self.validator_txns, + &self.payload, + &self.parents, + &self.extensions, + ) + } + + pub fn digest(&self) -> HashValue { + self.metadata.digest + } + + pub fn metadata(&self) -> &NodeMetadata { + &self.metadata + } + + pub fn parents(&self) -> &[NodeCertificate] { + &self.parents + } + + pub fn timestamp(&self) -> u64 { + self.metadata.timestamp + } + + pub fn parents_metadata(&self) -> impl Iterator { + self.parents().iter().map(|cert| &cert.metadata) + } + + pub fn author(&self) -> &Author { + self.metadata.author() + } + + pub fn epoch(&self) -> u64 { + self.metadata.epoch + } + + pub fn id(&self) -> NodeId { + NodeId::new(self.epoch(), self.round(), *self.author()) + } + + pub fn sign_vote(&self, signer: &ValidatorSigner) -> Result { + signer.sign(self.metadata()) + } + + pub fn round(&self) -> Round { + self.metadata.round + } + + pub fn payload(&self) -> &Payload { + &self.payload + } + + pub fn validator_txns(&self) -> &Vec { + &self.validator_txns + } + + pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + ensure!( + sender == *self.author(), + "Author {} doesn't match sender {}", + self.author(), + sender + ); + // TODO: move this check to rpc process logic to delay it as much as possible for performance + ensure!(self.digest() == self.calculate_digest(), "invalid digest"); + + let node_round = self.metadata().round(); + + ensure!(node_round > 0, "current round cannot be zero"); + + if node_round == 1 { + ensure!(self.parents().is_empty(), "invalid parents for round 1"); + return Ok(()); + } + + let prev_round = node_round - 1; + // check if the parents' round is the node's round - 1 + ensure!( + self.parents() + .iter() + .all(|parent| parent.metadata().round() == prev_round), + "invalid parent round" + ); + + // Verification of the certificate is delayed until we need to fetch it + ensure!( + verifier + .check_voting_power( + self.parents() + .iter() + .map(|parent| parent.metadata().author()), + true, + ) + .is_ok(), + "not enough parents to satisfy voting power" + ); + + // TODO: validate timestamp + + Ok(()) + } +} + +#[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Hash, Clone, PartialOrd, Ord)] +pub struct NodeId { + epoch: u64, + round: Round, + author: Author, +} + +impl NodeId { + pub fn new(epoch: u64, round: Round, author: Author) -> Self { + Self { + epoch, + round, + author, + } + } + + pub fn epoch(&self) -> u64 { + self.epoch + } + + pub fn round(&self) -> Round { + self.round + } + + pub fn author(&self) -> &Author { + &self.author + } +} + +impl Display for NodeId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "NodeId: [epoch: {}, round: {}, author: {}]", + self.epoch, self.round, self.author + ) + } +} + +/// Quorum signatures over the node digest +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +pub struct NodeCertificate { + metadata: NodeMetadata, + signatures: AggregateSignature, +} + +impl NodeCertificate { + pub fn new(metadata: NodeMetadata, signatures: AggregateSignature) -> Self { + Self { + metadata, + signatures, + } + } + + pub fn metadata(&self) -> &NodeMetadata { + &self.metadata + } + + pub fn signers(&self, validators: &[Author]) -> Vec { + self.signatures.get_signers_addresses(validators) + } + + pub fn signatures(&self) -> &AggregateSignature { + &self.signatures + } + + pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + Ok(verifier.verify_multi_signatures(self.metadata(), self.signatures())?) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct CertifiedNode { + node: Node, + signatures: AggregateSignature, +} + +impl CertifiedNode { + pub fn new(node: Node, signatures: AggregateSignature) -> Self { + Self { node, signatures } + } + + pub fn signatures(&self) -> &AggregateSignature { + &self.signatures + } + + pub fn certificate(&self) -> NodeCertificate { + NodeCertificate::new(self.node.metadata.clone(), self.signatures.clone()) + } + + pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + ensure!(self.digest() == self.calculate_digest(), "invalid digest"); + + Ok(verifier.verify_multi_signatures(self.metadata(), self.signatures())?) + } +} + +impl Deref for CertifiedNode { + type Target = Node; + + fn deref(&self) -> &Self::Target { + &self.node + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct CertifiedNodeMessage { + certified_node: CertifiedNode, + ledger_info: LedgerInfoWithSignatures, +} + +impl CertifiedNodeMessage { + pub fn new(certified_node: CertifiedNode, ledger_info: LedgerInfoWithSignatures) -> Self { + Self { + certified_node, + ledger_info, + } + } + + pub fn certified_node(self) -> CertifiedNode { + self.certified_node + } + + pub fn ledger_info(&self) -> &LedgerInfoWithSignatures { + &self.ledger_info + } + + pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + ensure!( + *self.certified_node.author() == sender, + "Author {} doesn't match sender {}", + self.certified_node.author(), + sender + ); + ensure!( + self.certified_node.epoch() == self.ledger_info.commit_info().epoch(), + "Epoch {} from node doesn't match epoch {} from ledger info", + self.certified_node.epoch(), + self.ledger_info().commit_info().epoch() + ); + self.certified_node.verify(verifier) + } +} + +impl Deref for CertifiedNodeMessage { + type Target = CertifiedNode; + + fn deref(&self) -> &Self::Target { + &self.certified_node + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct Vote { + metadata: NodeMetadata, + signature: Signature, +} + +impl Vote { + pub(crate) fn new(metadata: NodeMetadata, signature: Signature) -> Self { + Self { + metadata, + signature, + } + } + + pub fn signature(&self) -> &Signature { + &self.signature + } + + pub fn verify(&self, author: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + Ok(verifier.verify(author, &self.metadata, self.signature())?) + } +} + +impl From for DAGRpcResult { + fn from(vote: Vote) -> Self { + DAGRpcResult(Ok(DAGMessage::VoteMsg(vote))) + } +} + +impl TryFrom for Vote { + type Error = anyhow::Error; + + fn try_from(result: DAGRpcResult) -> Result { + result.0?.try_into() + } +} + +pub struct SignatureBuilder { + metadata: NodeMetadata, + inner: Mutex<(PartialSignatures, Option>)>, + epoch_state: Arc, +} + +impl SignatureBuilder { + pub fn new( + metadata: NodeMetadata, + epoch_state: Arc, + tx: oneshot::Sender, + ) -> Arc { + Arc::new(Self { + metadata, + inner: Mutex::new((PartialSignatures::empty(), Some(tx))), + epoch_state, + }) + } +} + +impl BroadcastStatus for Arc { + type Aggregated = (); + type Message = Node; + type Response = Vote; + + /// Processes the [Vote]s received for a given [Node]. Once a supermajority voting power + /// is reached, this method sends [NodeCertificate] into a channel. It will only return + /// successfully when [Vote]s are received from all the peers. + fn add(&self, peer: Author, ack: Self::Response) -> anyhow::Result> { + ensure!(self.metadata == ack.metadata, "Digest mismatch"); + ack.verify(peer, &self.epoch_state.verifier)?; + debug!(LogSchema::new(LogEvent::ReceiveVote) + .remote_peer(peer) + .round(self.metadata.round())); + let mut guard = self.inner.lock(); + let (partial_signatures, tx) = guard.deref_mut(); + partial_signatures.add_signature(peer, ack.signature); + + if tx.is_some() + && self + .epoch_state + .verifier + .check_voting_power(partial_signatures.signatures().keys(), true) + .is_ok() + { + let aggregated_signature = self + .epoch_state + .verifier + .aggregate_signatures(partial_signatures) + .expect("Signature aggregation should succeed"); + observe_node(self.metadata.timestamp(), NodeStage::CertAggregated); + let certificate = NodeCertificate::new(self.metadata.clone(), aggregated_signature); + + _ = tx.take().expect("must exist").send(certificate); + } + + if partial_signatures.signatures().len() == self.epoch_state.verifier.len() { + Ok(Some(())) + } else { + Ok(None) + } + } +} + +pub struct CertificateAckState { + num_validators: usize, + received: Mutex>, +} + +impl CertificateAckState { + pub fn new(num_validators: usize) -> Arc { + Arc::new(Self { + num_validators, + received: Mutex::new(HashSet::new()), + }) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct CertifiedAck { + epoch: u64, +} + +impl CertifiedAck { + pub fn new(epoch: u64) -> Self { + Self { epoch } + } +} + +impl From for DAGRpcResult { + fn from(ack: CertifiedAck) -> Self { + DAGRpcResult(Ok(DAGMessage::CertifiedAckMsg(ack))) + } +} + +impl TryFrom for CertifiedAck { + type Error = anyhow::Error; + + fn try_from(result: DAGRpcResult) -> Result { + result.0?.try_into() + } +} + +impl BroadcastStatus for Arc { + type Aggregated = (); + type Message = CertifiedNodeMessage; + type Response = CertifiedAck; + + fn add(&self, peer: Author, _ack: Self::Response) -> anyhow::Result> { + debug!(LogSchema::new(LogEvent::ReceiveAck).remote_peer(peer)); + let mut received = self.received.lock(); + received.insert(peer); + if received.len() == self.num_validators { + Ok(Some(())) + } else { + Ok(None) + } + } +} + +/// Represents a request to fetch missing dependencies for `target`, `start_round` represents +/// the first round we care about in the DAG, `exists_bitmask` is a two dimensional bitmask represents +/// if a node exist at [start_round + index][validator_index]. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct RemoteFetchRequest { + epoch: u64, + targets: Vec, + exists_bitmask: DagSnapshotBitmask, +} + +impl RemoteFetchRequest { + pub fn new(epoch: u64, targets: Vec, exists_bitmask: DagSnapshotBitmask) -> Self { + Self { + epoch, + targets, + exists_bitmask, + } + } + + pub fn epoch(&self) -> u64 { + self.epoch + } + + pub fn targets(&self) -> impl Iterator + Clone { + self.targets.iter() + } + + pub fn exists_bitmask(&self) -> &DagSnapshotBitmask { + &self.exists_bitmask + } + + pub fn start_round(&self) -> Round { + self.exists_bitmask.first_round() + } + + pub fn target_round(&self) -> Round { + self.targets[0].round + } + + pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + ensure!( + self.exists_bitmask + .bitmask + .iter() + .all(|round| round.len() == verifier.len()), + "invalid bitmask: each round length is not equal to validator count" + ); + ensure!(!self.targets.is_empty(), "Targets is empty"); + let target_round = self.targets[0].round(); + ensure!( + self.targets().all(|node| node.round() == target_round), + "Target round is not consistent" + ); + ensure!( + self.exists_bitmask.first_round() + self.exists_bitmask.bitmask.len() as u64 - 1 + == target_round, + "Bitmask length doesn't match, first_round {}, length {}, target {}", + self.exists_bitmask.first_round(), + self.exists_bitmask.bitmask.len(), + target_round + ); + + Ok(()) + } +} + +/// Represents a response to FetchRequest, `certified_nodes` are indexed by [round][validator_index] +/// It should fill in gaps from the `exists_bitmask` according to the parents from the `target_digest` node. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct FetchResponse { + epoch: u64, + certified_nodes: Vec, +} + +impl FetchResponse { + pub fn new(epoch: u64, certified_nodes: Vec) -> Self { + Self { + epoch, + certified_nodes, + } + } + + pub fn certified_nodes(self) -> Vec { + self.certified_nodes + } + + pub fn verify( + self, + request: &RemoteFetchRequest, + validator_verifier: &ValidatorVerifier, + ) -> anyhow::Result { + ensure!( + self.certified_nodes.iter().all(|node| { + let round = node.round(); + let author = node.author(); + if let Some(author_idx) = + validator_verifier.address_to_validator_index().get(author) + { + !request.exists_bitmask.has(round, *author_idx) + } else { + false + } + }), + "nodes don't match requested bitmask" + ); + ensure!( + self.certified_nodes + .iter() + .all(|node| node.verify(validator_verifier).is_ok()), + "unable to verify certified nodes" + ); + + Ok(self) + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct DAGNetworkMessage { + epoch: u64, + #[serde(with = "serde_bytes")] + data: Vec, +} + +impl DAGNetworkMessage { + pub fn new(epoch: u64, data: Vec) -> Self { + Self { epoch, data } + } + + pub fn data(&self) -> &[u8] { + &self.data + } + + pub fn epoch(&self) -> u64 { + self.epoch + } +} + +impl core::fmt::Debug for DAGNetworkMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DAGNetworkMessage") + .field("epoch", &self.epoch) + .field("data", &hex::encode(&self.data[..min(20, self.data.len())])) + .finish() + } +} + +#[derive(Clone, Serialize, Deserialize, Debug, EnumConversion)] +pub enum DAGMessage { + NodeMsg(Node), + VoteMsg(Vote), + CertifiedNodeMsg(CertifiedNodeMessage), + CertifiedAckMsg(CertifiedAck), + FetchRequest(RemoteFetchRequest), + FetchResponse(FetchResponse), + + #[cfg(test)] + TestMessage(TestMessage), + #[cfg(test)] + TestAck(TestAck), +} + +impl DAGMessage { + pub fn name(&self) -> &str { + match self { + DAGMessage::NodeMsg(_) => "NodeMsg", + DAGMessage::VoteMsg(_) => "VoteMsg", + DAGMessage::CertifiedNodeMsg(_) => "CertifiedNodeMsg", + DAGMessage::CertifiedAckMsg(_) => "CertifiedAckMsg", + DAGMessage::FetchRequest(_) => "FetchRequest", + DAGMessage::FetchResponse(_) => "FetchResponse", + #[cfg(test)] + DAGMessage::TestMessage(_) => "TestMessage", + #[cfg(test)] + DAGMessage::TestAck(_) => "TestAck", + } + } + + pub fn author(&self) -> anyhow::Result { + match self { + DAGMessage::NodeMsg(node) => Ok(node.metadata.author), + DAGMessage::CertifiedNodeMsg(node) => Ok(node.metadata.author), + _ => bail!("message does not support author field"), + } + } + + pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { + match self { + DAGMessage::NodeMsg(node) => node.verify(sender, verifier), + DAGMessage::CertifiedNodeMsg(certified_node) => certified_node.verify(sender, verifier), + DAGMessage::FetchRequest(fetch_request) => fetch_request.verify(verifier), + DAGMessage::VoteMsg(_) + | DAGMessage::CertifiedAckMsg(_) + | DAGMessage::FetchResponse(_) => { + bail!("Unexpected to verify {} in rpc handler", self.name()) + }, + #[cfg(test)] + DAGMessage::TestMessage(_) | DAGMessage::TestAck(_) => { + bail!("Unexpected to verify {}", self.name()) + }, + } + } +} + +impl RBMessage for DAGMessage {} + +impl TConsensusMsg for DAGMessage { + fn epoch(&self) -> u64 { + match self { + DAGMessage::NodeMsg(node) => node.metadata.epoch, + DAGMessage::VoteMsg(vote) => vote.metadata.epoch, + DAGMessage::CertifiedNodeMsg(node) => node.metadata.epoch, + DAGMessage::CertifiedAckMsg(ack) => ack.epoch, + DAGMessage::FetchRequest(req) => req.epoch, + DAGMessage::FetchResponse(res) => res.epoch, + #[cfg(test)] + DAGMessage::TestMessage(_) => 1, + #[cfg(test)] + DAGMessage::TestAck(_) => 1, + } + } + + fn from_network_message(msg: ConsensusMsg) -> anyhow::Result { + match msg { + ConsensusMsg::DAGMessage(msg) => Ok(bcs::from_bytes(&msg.data)?), + _ => bail!("unexpected consensus message type {:?}", msg), + } + } + + fn into_network_message(self) -> ConsensusMsg { + ConsensusMsg::DAGMessage(DAGNetworkMessage { + epoch: self.epoch(), + data: bcs::to_bytes(&self).unwrap(), + }) + } +} + +impl TryFrom for DAGMessage { + type Error = anyhow::Error; + + fn try_from(msg: DAGNetworkMessage) -> Result { + Ok(bcs::from_bytes(&msg.data)?) + } +} + +impl TryFrom for DAGMessage { + type Error = anyhow::Error; + + fn try_from(msg: ConsensusMsg) -> Result { + TConsensusMsg::from_network_message(msg) + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DAGRpcResult(pub Result); + +impl TConsensusMsg for DAGRpcResult { + fn epoch(&self) -> u64 { + match &self.0 { + Ok(dag_message) => dag_message.epoch(), + Err(error) => error.epoch(), + } + } + + fn from_network_message(msg: ConsensusMsg) -> anyhow::Result { + match msg { + ConsensusMsg::DAGMessage(msg) => Ok(bcs::from_bytes(&msg.data)?), + _ => bail!("unexpected consensus message type {:?}", msg), + } + } + + fn into_network_message(self) -> ConsensusMsg { + ConsensusMsg::DAGMessage(DAGNetworkMessage { + epoch: self.epoch(), + data: bcs::to_bytes(&self).unwrap(), + }) + } +} + +impl RBMessage for DAGRpcResult {} + +impl Deref for DAGRpcResult { + type Target = Result; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for DAGRpcResult { + fn from(result: Result) -> Self { + Self(result) + } +} + +#[cfg(test)] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TestMessage(pub Vec); + +#[cfg(test)] +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct TestAck(pub Vec); + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct DagSnapshotBitmask { + bitmask: Vec>, + first_round: Round, +} + +impl DagSnapshotBitmask { + pub fn new(first_round: Round, bitmask: Vec>) -> Self { + Self { + bitmask, + first_round, + } + } + + pub fn has(&self, round: Round, author_idx: usize) -> bool { + let round_idx = match round.checked_sub(self.first_round) { + Some(idx) => idx as usize, + None => return false, + }; + self.bitmask + .get(round_idx) + .and_then(|round| round.get(author_idx).cloned()) + .unwrap_or(false) + } + + pub fn num_missing(&self) -> usize { + self.bitmask + .iter() + .map(|round| round.iter().map(|exist| !*exist as usize).sum::()) + .sum::() + } + + pub fn first_round(&self) -> Round { + self.first_round + } + + pub fn len(&self) -> usize { + self.bitmask.len() + } + + pub fn bitvec(&self, round: Round) -> Option { + let round_idx = round.checked_sub(self.first_round)? as usize; + self.bitmask.get(round_idx).map(|bitvec| bitvec.into()) + } +} diff --git a/consensus/src/error.rs b/consensus/src/error.rs new file mode 100644 index 0000000000000..d77acce745694 --- /dev/null +++ b/consensus/src/error.rs @@ -0,0 +1,108 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::pipeline; +use thiserror::Error; + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct DbError { + #[from] + inner: anyhow::Error, +} + +impl From for DbError { + fn from(e: aptos_storage_interface::AptosDbError) -> Self { + DbError { inner: e.into() } + } +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct StateSyncError { + #[from] + inner: anyhow::Error, +} + +impl From for StateSyncError { + fn from(e: pipeline::errors::Error) -> Self { + StateSyncError { inner: e.into() } + } +} + +impl From for StateSyncError { + fn from(e: aptos_executor_types::ExecutorError) -> Self { + StateSyncError { inner: e.into() } + } +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct MempoolError { + #[from] + inner: anyhow::Error, +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct QuorumStoreError { + #[from] + inner: anyhow::Error, +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct VerifyError { + #[from] + inner: anyhow::Error, +} + +pub fn error_kind(e: &anyhow::Error) -> &'static str { + if e.downcast_ref::() + .is_some() + { + return "Execution"; + } + if let Some(e) = e.downcast_ref::() { + if e.inner + .downcast_ref::() + .is_some() + { + return "Execution"; + } + return "StateSync"; + } + if e.downcast_ref::().is_some() { + return "Mempool"; + } + if e.downcast_ref::().is_some() { + return "QuorumStore"; + } + if e.downcast_ref::().is_some() { + return "ConsensusDb"; + } + if e.downcast_ref::().is_some() { + return "SafetyRules"; + } + if e.downcast_ref::().is_some() { + return "VerifyError"; + } + "InternalError" +} + +#[cfg(test)] +mod tests { + use crate::error::{error_kind, StateSyncError}; + use anyhow::Context; + + #[test] + fn conversion_and_downcast() { + let error = aptos_executor_types::ExecutorError::InternalError { + error: "lalala".to_string(), + }; + let typed_error: StateSyncError = error.into(); + let upper: anyhow::Result<()> = Err(typed_error).context("Context!"); + assert_eq!(error_kind(&upper.unwrap_err()), "Execution"); + } +} diff --git a/consensus/src/execution_pipeline.rs b/consensus/src/execution_pipeline.rs new file mode 100644 index 0000000000000..53a50f01a4659 --- /dev/null +++ b/consensus/src/execution_pipeline.rs @@ -0,0 +1,272 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use crate::{ + block_preparer::BlockPreparer, + monitor, + state_computer::{PipelineExecutionResult, StateComputeResultFut}, +}; +use aptos_consensus_types::block::Block; +use aptos_crypto::HashValue; +use aptos_executor_types::{ + state_checkpoint_output::StateCheckpointOutput, BlockExecutorTrait, ExecutorError, + ExecutorResult, +}; +use aptos_experimental_runtimes::thread_manager::optimal_min_len; +use aptos_logger::{debug, error}; +use aptos_types::{ + block_executor::{config::BlockExecutorConfigFromOnchain, partitioner::ExecutableBlock}, + block_metadata_ext::BlockMetadataExt, + transaction::{ + signature_verified_transaction::SignatureVerifiedTransaction, SignedTransaction, + }, +}; +use fail::fail_point; +use once_cell::sync::Lazy; +use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; +use std::sync::Arc; +use tokio::sync::{mpsc, oneshot}; + +pub static SIG_VERIFY_POOL: Lazy> = Lazy::new(|| { + Arc::new( + rayon::ThreadPoolBuilder::new() + .num_threads(8) // More than 8 threads doesn't seem to help much + .thread_name(|index| format!("signature-checker-{}", index)) + .build() + .unwrap(), + ) +}); + +pub struct ExecutionPipeline { + prepare_block_tx: mpsc::UnboundedSender, +} + +impl ExecutionPipeline { + pub fn spawn(executor: Arc, runtime: &tokio::runtime::Handle) -> Self { + let (prepare_block_tx, prepare_block_rx) = mpsc::unbounded_channel(); + let (execute_block_tx, execute_block_rx) = mpsc::unbounded_channel(); + let (ledger_apply_tx, ledger_apply_rx) = mpsc::unbounded_channel(); + runtime.spawn(Self::prepare_block_stage( + prepare_block_rx, + execute_block_tx, + )); + runtime.spawn(Self::execute_stage( + execute_block_rx, + ledger_apply_tx, + executor.clone(), + )); + runtime.spawn(Self::ledger_apply_stage(ledger_apply_rx, executor)); + Self { prepare_block_tx } + } + + pub async fn queue( + &self, + block: Block, + metadata: BlockMetadataExt, + parent_block_id: HashValue, + txn_generator: BlockPreparer, + block_executor_onchain_config: BlockExecutorConfigFromOnchain, + ) -> StateComputeResultFut { + let (result_tx, result_rx) = oneshot::channel(); + let block_id = block.id(); + self.prepare_block_tx + .send(PrepareBlockCommand { + block, + metadata, + block_executor_onchain_config, + parent_block_id, + block_preparer: txn_generator, + result_tx, + }) + .expect("Failed to send block to execution pipeline."); + + Box::pin(async move { + result_rx + .await + .map_err(|err| ExecutorError::InternalError { + error: format!( + "Failed to receive execution result for block {}: {:?}.", + block_id, err + ), + })? + }) + } + + async fn prepare_block( + execute_block_tx: mpsc::UnboundedSender, + command: PrepareBlockCommand, + ) { + let PrepareBlockCommand { + block, + metadata, + block_executor_onchain_config, + parent_block_id, + block_preparer, + result_tx, + } = command; + + debug!("prepare_block received block {}.", block.id()); + let input_txns = block_preparer.prepare_block(&block).await; + if let Err(e) = input_txns { + result_tx.send(Err(e)).unwrap_or_else(|err| { + error!( + block_id = block.id(), + "Failed to send back execution result for block {}: {:?}.", + block.id(), + err, + ); + }); + return; + } + let validator_txns = block.validator_txns().cloned().unwrap_or_default(); + let input_txns = input_txns.unwrap(); + tokio::task::spawn_blocking(move || { + let txns_to_execute = + Block::combine_to_input_transactions(validator_txns, input_txns.clone(), metadata); + let sig_verified_txns: Vec = + SIG_VERIFY_POOL.install(|| { + let num_txns = txns_to_execute.len(); + txns_to_execute + .into_par_iter() + .with_min_len(optimal_min_len(num_txns, 32)) + .map(|t| t.into()) + .collect::>() + }); + execute_block_tx + .send(ExecuteBlockCommand { + input_txns, + block: (block.id(), sig_verified_txns).into(), + parent_block_id, + block_executor_onchain_config, + result_tx, + }) + .expect("Failed to send block to execution pipeline."); + }) + .await + .expect("Failed to spawn_blocking."); + } + + async fn prepare_block_stage( + mut prepare_block_rx: mpsc::UnboundedReceiver, + execute_block_tx: mpsc::UnboundedSender, + ) { + while let Some(command) = prepare_block_rx.recv().await { + monitor!( + "prepare_block", + Self::prepare_block(execute_block_tx.clone(), command).await + ); + } + debug!("prepare_block_stage quitting."); + } + + async fn execute_stage( + mut block_rx: mpsc::UnboundedReceiver, + ledger_apply_tx: mpsc::UnboundedSender, + executor: Arc, + ) { + while let Some(ExecuteBlockCommand { + input_txns, + block, + parent_block_id, + block_executor_onchain_config, + result_tx, + }) = block_rx.recv().await + { + let block_id = block.block_id; + debug!("execute_stage received block {}.", block_id); + let executor = executor.clone(); + let state_checkpoint_output = monitor!( + "execute_block", + tokio::task::spawn_blocking(move || { + fail_point!("consensus::compute", |_| { + Err(ExecutorError::InternalError { + error: "Injected error in compute".into(), + }) + }); + executor.execute_and_state_checkpoint( + block, + parent_block_id, + block_executor_onchain_config, + ) + }) + .await + ) + .expect("Failed to spawn_blocking."); + + ledger_apply_tx + .send(LedgerApplyCommand { + input_txns, + block_id, + parent_block_id, + state_checkpoint_output, + result_tx, + }) + .expect("Failed to send block to ledger_apply stage."); + } + debug!("execute_stage quitting."); + } + + async fn ledger_apply_stage( + mut block_rx: mpsc::UnboundedReceiver, + executor: Arc, + ) { + while let Some(LedgerApplyCommand { + input_txns, + block_id, + parent_block_id, + state_checkpoint_output, + result_tx, + }) = block_rx.recv().await + { + debug!("ledger_apply stage received block {}.", block_id); + let res = async { + let executor = executor.clone(); + monitor!( + "ledger_apply", + tokio::task::spawn_blocking(move || { + executor.ledger_update(block_id, parent_block_id, state_checkpoint_output?) + }) + ) + .await + .expect("Failed to spawn_blocking().") + } + .await; + let pipe_line_res = res.map(|output| PipelineExecutionResult::new(input_txns, output)); + result_tx.send(pipe_line_res).unwrap_or_else(|err| { + error!( + block_id = block_id, + "Failed to send back execution result for block {}: {:?}", block_id, err, + ); + }); + } + debug!("ledger_apply stage quitting."); + } +} + +struct PrepareBlockCommand { + block: Block, + metadata: BlockMetadataExt, + block_executor_onchain_config: BlockExecutorConfigFromOnchain, + // The parent block id. + parent_block_id: HashValue, + block_preparer: BlockPreparer, + result_tx: oneshot::Sender>, +} + +struct ExecuteBlockCommand { + input_txns: Vec, + block: ExecutableBlock, + parent_block_id: HashValue, + block_executor_onchain_config: BlockExecutorConfigFromOnchain, + result_tx: oneshot::Sender>, +} + +struct LedgerApplyCommand { + input_txns: Vec, + block_id: HashValue, + parent_block_id: HashValue, + state_checkpoint_output: ExecutorResult, + result_tx: oneshot::Sender>, +} diff --git a/consensus/src/liveness/cached_proposer_election.rs b/consensus/src/liveness/cached_proposer_election.rs new file mode 100644 index 0000000000000..bb53e196d157a --- /dev/null +++ b/consensus/src/liveness/cached_proposer_election.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::proposer_election::ProposerElection; +use crate::counters::PROPOSER_ELECTION_DURATION; +use aptos_consensus_types::common::{Author, Round}; +use aptos_infallible::Mutex; +use aptos_logger::prelude::info; +use std::collections::BTreeMap; + +// Wrapper around ProposerElection. +// +// Function get_valid_proposer can be expensive, and we want to make sure +// it is computed only once for a given round. +pub struct CachedProposerElection { + epoch: u64, + proposer_election: Box, + // We use BTreeMap since we want a fixed window of cached elements + // to look back (and caller knows how big of a window it needs). + // LRU cache wouldn't work as well, as access order of the elements + // would define eviction, and could lead to evicting still needed elements. + recent_elections: Mutex>, + window: usize, +} + +impl CachedProposerElection { + pub fn new( + epoch: u64, + proposer_election: Box, + window: usize, + ) -> Self { + Self { + epoch, + proposer_election, + recent_elections: Mutex::new(BTreeMap::new()), + window, + } + } + + pub fn get_or_compute_entry(&self, round: Round) -> (Author, f64) { + let mut recent_elections = self.recent_elections.lock(); + + if round > self.window as u64 { + *recent_elections = recent_elections.split_off(&(round - self.window as u64)); + } + + *recent_elections.entry(round).or_insert_with(|| { + let _timer = PROPOSER_ELECTION_DURATION.start_timer(); + let result = self + .proposer_election + .get_valid_proposer_and_voting_power_participation_ratio(round); + info!( + "ProposerElection for epoch {} and round {}: {:?}", + self.epoch, round, result + ); + result + }) + } +} + +impl ProposerElection for CachedProposerElection { + fn get_valid_proposer(&self, round: Round) -> Author { + self.get_or_compute_entry(round).0 + } + + fn get_voting_power_participation_ratio(&self, round: Round) -> f64 { + self.get_or_compute_entry(round).1 + } +} diff --git a/consensus/src/liveness/cached_proposer_election_test.rs b/consensus/src/liveness/cached_proposer_election_test.rs new file mode 100644 index 0000000000000..0cc059f1da5d3 --- /dev/null +++ b/consensus/src/liveness/cached_proposer_election_test.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::proposer_election::ProposerElection; +use crate::liveness::cached_proposer_election::CachedProposerElection; +use aptos_consensus_types::common::{Author, Round}; +use aptos_infallible::Mutex; +use std::{cell::Cell, sync::Arc}; + +struct MockProposerElection { + proposers: Vec, + asked: Arc>>, +} + +impl MockProposerElection { + pub fn new(proposers: Vec, asked: Arc>>) -> Self { + Self { proposers, asked } + } +} + +impl ProposerElection for MockProposerElection { + fn get_valid_proposer(&self, round: Round) -> Author { + let round_uszie = round as usize; + let asked = self.asked.lock(); + asked.replace(asked.get() + 1); + self.proposers[round_uszie % self.proposers.len()] + } +} + +#[test] +fn test_get_valid_proposer_caching() { + let asked = Arc::new(Mutex::new(Cell::new(0))); + let authors: Vec = (0..4).map(|_| Author::random()).collect(); + let cpe = CachedProposerElection::new( + 1, + Box::new(MockProposerElection::new(authors.clone(), asked.clone())), + 10, + ); + + assert_eq!(asked.lock().get(), 0); + + assert_eq!(cpe.get_valid_proposer(0), authors[0]); + assert_eq!(asked.lock().get(), 1); + assert!(cpe.is_valid_proposer(authors[0], 0)); + assert!(!cpe.is_valid_proposer(authors[1], 0)); + assert_eq!(asked.lock().get(), 1); + + assert_eq!(cpe.get_valid_proposer(1), authors[1]); + assert_eq!(asked.lock().get(), 2); + assert!(cpe.is_valid_proposer(authors[1], 1)); + assert!(!cpe.is_valid_proposer(authors[0], 1)); + assert_eq!(asked.lock().get(), 2); + + assert_eq!(cpe.get_valid_proposer(0), authors[0]); + assert_eq!(asked.lock().get(), 2); + + assert_eq!(cpe.get_valid_proposer(11), authors[3]); + assert_eq!(asked.lock().get(), 3); + assert!(cpe.is_valid_proposer(authors[3], 11)); + assert!(!cpe.is_valid_proposer(authors[0], 11)); + assert_eq!(asked.lock().get(), 3); + + // round=0 is outside the caching window, and round=1 is still inside + assert_eq!(cpe.get_valid_proposer(0), authors[0]); + assert_eq!(asked.lock().get(), 4); + + assert_eq!(cpe.get_valid_proposer(1), authors[1]); + assert_eq!(asked.lock().get(), 4); +} diff --git a/consensus/src/liveness/mod.rs b/consensus/src/liveness/mod.rs new file mode 100644 index 0000000000000..f7e8f11bceb05 --- /dev/null +++ b/consensus/src/liveness/mod.rs @@ -0,0 +1,25 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) mod cached_proposer_election; +pub(crate) mod leader_reputation; +pub(crate) mod proposal_generator; +pub(crate) mod proposer_election; +pub(crate) mod rotating_proposer_election; +pub(crate) mod round_proposer_election; +pub(crate) mod round_state; +pub(crate) mod unequivocal_proposer_election; + +#[cfg(test)] +mod cached_proposer_election_test; +#[cfg(test)] +mod leader_reputation_test; +#[cfg(test)] +mod rotating_proposer_test; +#[cfg(test)] +mod round_proposer_test; +#[cfg(test)] +mod round_state_test; +#[cfg(test)] +mod unequivocal_proposer_election_test; diff --git a/consensus/src/liveness/proposal_generator.rs b/consensus/src/liveness/proposal_generator.rs index c412278506712..2ff6b058a99de 100644 --- a/consensus/src/liveness/proposal_generator.rs +++ b/consensus/src/liveness/proposal_generator.rs @@ -421,7 +421,7 @@ impl ProposalGenerator { timestamp: Duration, round: Round, ) -> (u64, u64, Option, Duration) { - let mut values_max_block_txns = vec![self.max_block_txns]; + let mut values_max_block_txns = vec![self.max_block_unique_txns]; let mut values_max_block_bytes = vec![self.max_block_bytes]; let mut values_proposal_delay = vec![Duration::ZERO]; let mut values_max_txns_from_block_to_execute = vec![]; diff --git a/consensus/src/liveness/proposer_election.rs b/consensus/src/liveness/proposer_election.rs new file mode 100644 index 0000000000000..d9935bd731bac --- /dev/null +++ b/consensus/src/liveness/proposer_election.rs @@ -0,0 +1,85 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use aptos_consensus_types::common::{Author, Round}; +use aptos_fallible::copy_from_slice::copy_slice_to_vec; +use num_traits::CheckedAdd; +use std::cmp::Ordering; + +/// ProposerElection incorporates the logic of choosing a leader among multiple candidates. +pub trait ProposerElection { + /// If a given author is a valid candidate for being a proposer, generate the info, + /// otherwise return None. + /// Note that this function is synchronous. + fn is_valid_proposer(&self, author: Author, round: Round) -> bool { + self.get_valid_proposer(round) == author + } + + /// Return the valid proposer for a given round (this information can be + /// used by e.g., voters for choosing the destinations for sending their votes to). + fn get_valid_proposer(&self, round: Round) -> Author; + + /// Return the chain health: a ratio of voting power participating in the consensus. + fn get_voting_power_participation_ratio(&self, _round: Round) -> f64 { + 1.0 + } + + fn get_valid_proposer_and_voting_power_participation_ratio( + &self, + round: Round, + ) -> (Author, f64) { + ( + self.get_valid_proposer(round), + self.get_voting_power_participation_ratio(round), + ) + } +} + +// next consumes seed and returns random deterministic u64 value in [0, max) range +fn next_in_range(state: Vec, max: u128) -> u128 { + // hash = SHA-3-256(state) + let hash = aptos_crypto::HashValue::sha3_256_of(&state).to_vec(); + let mut temp = [0u8; 16]; + copy_slice_to_vec(&hash[..16], &mut temp).expect("next failed"); + // return hash[0..16] + u128::from_le_bytes(temp) % max +} + +// chose index randomly, with given weight distribution +pub(crate) fn choose_index(mut weights: Vec, state: Vec) -> usize { + let mut total_weight = 0; + // Create cumulative weights vector + // Since we own the vector, we can safely modify it in place + for w in &mut weights { + total_weight = total_weight + .checked_add(w) + .expect("Total stake shouldn't exceed u128::MAX"); + *w = total_weight; + } + let chosen_weight = next_in_range(state, total_weight); + weights + .binary_search_by(|w| { + if *w <= chosen_weight { + Ordering::Less + } else { + Ordering::Greater + } + }) + .unwrap_err() +} + +#[test] +fn test_bounds() { + // check that bounds are correct, and both first and last weight can be selected. + let mut selected = [0, 0]; + let weights = [u64::MAX as u128 * 1000, u64::MAX as u128 * 1000].to_vec(); + // 10 is enough to get one of each. + for i in 0i32..10 { + let state = i.to_le_bytes().to_vec(); + selected[choose_index(weights.clone(), state)] += 1; + } + + assert!(selected[0] >= 1); + assert!(selected[1] >= 1); +} diff --git a/consensus/src/liveness/rotating_proposer_election.rs b/consensus/src/liveness/rotating_proposer_election.rs new file mode 100644 index 0000000000000..db2b2c4dfbe2b --- /dev/null +++ b/consensus/src/liveness/rotating_proposer_election.rs @@ -0,0 +1,41 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::liveness::proposer_election::ProposerElection; +use aptos_consensus_types::common::{Author, Round}; + +/// The rotating proposer maps a round to an author according to a round-robin rotation. +/// A fixed proposer strategy loses liveness when the fixed proposer is down. Rotating proposers +/// won't gather quorum certificates to machine loss/byzantine behavior on f/n rounds. +pub struct RotatingProposer { + // Ordering of proposers to rotate through (all honest replicas must agree on this) + proposers: Vec, + // Number of contiguous rounds (i.e. round numbers increase by 1) a proposer is active + // in a row + contiguous_rounds: u32, +} + +/// Choose a proposer that is going to be the single leader (relevant for a mock fixed proposer +/// election only). +pub fn choose_leader(peers: Vec) -> Author { + // As it is just a tmp hack function, pick the min PeerId to be a proposer. + peers.into_iter().min().expect("No trusted peers found!") +} + +impl RotatingProposer { + /// With only one proposer in the vector, it behaves the same as a fixed proposer strategy. + pub fn new(proposers: Vec, contiguous_rounds: u32) -> Self { + Self { + proposers, + contiguous_rounds, + } + } +} + +impl ProposerElection for RotatingProposer { + fn get_valid_proposer(&self, round: Round) -> Author { + self.proposers + [((round / u64::from(self.contiguous_rounds)) % self.proposers.len() as u64) as usize] + } +} diff --git a/consensus/src/liveness/rotating_proposer_test.rs b/consensus/src/liveness/rotating_proposer_test.rs new file mode 100644 index 0000000000000..f2dfc774d7f41 --- /dev/null +++ b/consensus/src/liveness/rotating_proposer_test.rs @@ -0,0 +1,59 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::liveness::{ + proposer_election::ProposerElection, rotating_proposer_election::RotatingProposer, +}; +use aptos_types::account_address::AccountAddress; + +#[test] +fn test_rotating_proposer() { + let chosen_author = AccountAddress::random(); + let another_author = AccountAddress::random(); + let proposers = vec![chosen_author, another_author]; + let pe = RotatingProposer::new(proposers, 1); + + // Send a proposal from both chosen author and another author, the only winning proposals + // follow the round-robin rotation. + + assert!(!pe.is_valid_proposer(chosen_author, 1)); + assert!(pe.is_valid_proposer(another_author, 1),); + assert!(pe.is_valid_proposer(chosen_author, 2)); + assert!(!pe.is_valid_proposer(another_author, 2)); + assert_eq!(pe.get_valid_proposer(1), another_author); + assert_eq!(pe.get_valid_proposer(2), chosen_author); +} + +#[test] +fn test_rotating_proposer_with_three_contiguous_rounds() { + let chosen_author = AccountAddress::random(); + let another_author = AccountAddress::random(); + let proposers = vec![chosen_author, another_author]; + let pe = RotatingProposer::new(proposers, 3); + + // Send a proposal from both chosen author and another author, the only winning proposals + // follow the round-robin rotation with 3 contiguous rounds. + + assert!(!pe.is_valid_proposer(another_author, 1)); + assert!(pe.is_valid_proposer(chosen_author, 1)); + assert!(pe.is_valid_proposer(chosen_author, 2)); + assert!(!pe.is_valid_proposer(another_author, 2)); + assert_eq!(pe.get_valid_proposer(1), chosen_author); + assert_eq!(pe.get_valid_proposer(2), chosen_author); +} + +#[test] +fn test_fixed_proposer() { + let chosen_author = AccountAddress::random(); + let another_author = AccountAddress::random(); + let pe = RotatingProposer::new(vec![chosen_author], 1); + + // Send a proposal from both chosen author and another author, the only winning proposal is + // from the chosen author. + + assert!(pe.is_valid_proposer(chosen_author, 1)); + assert!(!pe.is_valid_proposer(another_author, 1)); + assert_eq!(pe.get_valid_proposer(1), chosen_author); + assert!(pe.is_valid_proposer(chosen_author, 2)); +} diff --git a/consensus/src/liveness/round_proposer_election.rs b/consensus/src/liveness/round_proposer_election.rs new file mode 100644 index 0000000000000..653852b91278b --- /dev/null +++ b/consensus/src/liveness/round_proposer_election.rs @@ -0,0 +1,34 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::liveness::proposer_election::ProposerElection; +use aptos_consensus_types::common::{Author, Round}; +use std::collections::HashMap; + +/// The round proposer maps a round to author +pub struct RoundProposer { + // A pre-defined map specifying proposers per round + proposers: HashMap, + // Default proposer to use if proposer for a round is unspecified. + // We hardcode this to the first proposer + default_proposer: Author, +} + +impl RoundProposer { + pub fn new(proposers: HashMap, default_proposer: Author) -> Self { + Self { + proposers, + default_proposer, + } + } +} + +impl ProposerElection for RoundProposer { + fn get_valid_proposer(&self, round: Round) -> Author { + match self.proposers.get(&round) { + None => self.default_proposer, + Some(round_proposer) => *round_proposer, + } + } +} diff --git a/consensus/src/liveness/round_proposer_test.rs b/consensus/src/liveness/round_proposer_test.rs new file mode 100644 index 0000000000000..27b603b799dd0 --- /dev/null +++ b/consensus/src/liveness/round_proposer_test.rs @@ -0,0 +1,41 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::liveness::{ + proposer_election::ProposerElection, round_proposer_election::RoundProposer, +}; +use aptos_consensus_types::common::{Author, Round}; +use aptos_types::account_address::AccountAddress; +use std::collections::HashMap; + +#[test] +fn test_round_proposer() { + let chosen_author_round1 = AccountAddress::random(); + let chosen_author_round2 = AccountAddress::random(); + let another_author = AccountAddress::random(); + + // A map that specifies the proposer per round + let mut round_proposers: HashMap = HashMap::new(); + round_proposers.insert(1, chosen_author_round1); + round_proposers.insert(2, chosen_author_round2); + + let pe = RoundProposer::new(round_proposers, chosen_author_round1); + + // Send a proposal from both chosen author and another author, the only winning proposals + // follow the round-proposers mapping + + // In round 3, send a proposal from chosen_author_round1 (which is also the default proposer). + // The proposal should win because the map doesn't specify proposer for round 3 hence + // falling back on the default proposer + + assert!(pe.is_valid_proposer(chosen_author_round1, 1),); + assert!(!pe.is_valid_proposer(another_author, 1)); + assert!(pe.is_valid_proposer(chosen_author_round2, 2)); + assert!(!pe.is_valid_proposer(another_author, 2)); + assert!(pe.is_valid_proposer(chosen_author_round1, 3)); + assert!(!pe.is_valid_proposer(another_author, 3)); + assert_eq!(pe.get_valid_proposer(1), chosen_author_round1); + assert_eq!(pe.get_valid_proposer(2), chosen_author_round2); + assert_eq!(pe.get_valid_proposer(3), chosen_author_round1); +} diff --git a/consensus/src/liveness/unequivocal_proposer_election.rs b/consensus/src/liveness/unequivocal_proposer_election.rs new file mode 100644 index 0000000000000..5e4be2bfb372e --- /dev/null +++ b/consensus/src/liveness/unequivocal_proposer_election.rs @@ -0,0 +1,88 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::proposer_election::ProposerElection; +use aptos_consensus_types::{ + block::Block, + common::{Author, Round}, +}; +use aptos_crypto::HashValue; +use aptos_infallible::Mutex; +use aptos_logger::{error, warn, SecurityEvent}; +use std::{cmp::Ordering, sync::Arc}; + +// Wrapper around ProposerElection. +// +// Provides is_valid_proposal that remembers, and rejects if +// the same leader proposes multiple blocks. +pub struct UnequivocalProposerElection { + proposer_election: Arc, + already_proposed: Mutex<(Round, HashValue)>, +} + +impl ProposerElection for UnequivocalProposerElection { + fn get_valid_proposer(&self, round: Round) -> Author { + self.proposer_election.get_valid_proposer(round) + } + + fn get_voting_power_participation_ratio(&self, round: Round) -> f64 { + self.proposer_election + .get_voting_power_participation_ratio(round) + } +} + +impl UnequivocalProposerElection { + pub fn new(proposer_election: Arc) -> Self { + Self { + proposer_election, + already_proposed: Mutex::new((0, HashValue::zero())), + } + } + + // Return if a given proposed block is valid: + // - if a given author is a valid candidate for being a proposer + // - if this is the first block proposer has submitted in this round + // - if it is not old proposal + pub fn is_valid_proposal(&self, block: &Block) -> bool { + block.author().map_or(false, |author| { + let valid_author = self.is_valid_proposer(author, block.round()); + if !valid_author { + warn!( + SecurityEvent::InvalidConsensusProposal, + "Proposal is not from valid author {}, expected {} for round {} and id {}", + author, + self.get_valid_proposer(block.round()), + block.round(), + block.id() + ); + + return false; + } + let mut already_proposed = self.already_proposed.lock(); + // detect if the leader proposes more than once in this round + match block.round().cmp(&already_proposed.0) { + Ordering::Greater => { + already_proposed.0 = block.round(); + already_proposed.1 = block.id(); + true + }, + Ordering::Equal => { + if already_proposed.1 != block.id() { + error!( + SecurityEvent::InvalidConsensusProposal, + "Multiple proposals from {} for round {}: {} and {}", + author, + block.round(), + already_proposed.1, + block.id() + ); + false + } else { + true + } + }, + Ordering::Less => false, + } + }) + } +} diff --git a/consensus/src/liveness/unequivocal_proposer_election_test.rs b/consensus/src/liveness/unequivocal_proposer_election_test.rs new file mode 100644 index 0000000000000..41aa3f1a458b7 --- /dev/null +++ b/consensus/src/liveness/unequivocal_proposer_election_test.rs @@ -0,0 +1,106 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::proposer_election::ProposerElection; +use crate::liveness::unequivocal_proposer_election::UnequivocalProposerElection; +use aptos_consensus_types::{ + block::{block_test_utils::certificate_for_genesis, Block}, + common::{Author, Payload, Round}, +}; +use aptos_types::validator_signer::ValidatorSigner; +use std::{collections::HashMap, sync::Arc}; + +struct MockProposerElection { + proposers: HashMap, +} + +impl MockProposerElection { + pub fn new(proposers: HashMap) -> Self { + Self { proposers } + } +} + +impl ProposerElection for MockProposerElection { + fn get_valid_proposer(&self, round: Round) -> Author { + *self.proposers.get(&round).unwrap() + } +} + +#[test] +fn test_is_valid_proposal() { + let chosen_validator_signer = ValidatorSigner::random([0u8; 32]); + let chosen_author = chosen_validator_signer.author(); + let another_validator_signer = ValidatorSigner::random([1u8; 32]); + // let another_author = another_validator_signer.author(); + + // Test genesis and the next block + let quorum_cert = certificate_for_genesis(); + + let good_proposal = Block::new_proposal( + Payload::empty(false, true), + 1, + 1, + quorum_cert.clone(), + &chosen_validator_signer, + Vec::new(), + ) + .unwrap(); + let bad_author_proposal = Block::new_proposal( + Payload::empty(false, true), + 1, + 1, + quorum_cert.clone(), + &another_validator_signer, + Vec::new(), + ) + .unwrap(); + let bad_duplicate_proposal = Block::new_proposal( + Payload::empty(false, true), + 1, + 2, + quorum_cert.clone(), + &chosen_validator_signer, + Vec::new(), + ) + .unwrap(); + let next_good_proposal = Block::new_proposal( + Payload::empty(false, true), + 2, + 3, + quorum_cert.clone(), + &chosen_validator_signer, + Vec::new(), + ) + .unwrap(); + let next_bad_duplicate_proposal = Block::new_proposal( + Payload::empty(false, true), + 2, + 4, + quorum_cert, + &chosen_validator_signer, + Vec::new(), + ) + .unwrap(); + + let pe = + UnequivocalProposerElection::new(Arc::new(MockProposerElection::new(HashMap::from([ + (1, chosen_author), + (2, chosen_author), + ])))); + + assert!(pe.is_valid_proposer(chosen_author, 1)); + assert!(pe.is_valid_proposal(&good_proposal)); + assert!(!pe.is_valid_proposal(&bad_author_proposal)); + + // another proposal from the valid proposer should fail + assert!(!pe.is_valid_proposal(&bad_duplicate_proposal)); + // good proposal still passes + assert!(pe.is_valid_proposal(&good_proposal)); + + // going to the next round: + assert!(pe.is_valid_proposal(&next_good_proposal)); + assert!(!pe.is_valid_proposal(&next_bad_duplicate_proposal)); + + // Proposal from previous round is not valid any more: + assert!(!pe.is_valid_proposal(&good_proposal)); +} diff --git a/consensus/src/pending_votes.rs b/consensus/src/pending_votes.rs new file mode 100644 index 0000000000000..56af5416a3a20 --- /dev/null +++ b/consensus/src/pending_votes.rs @@ -0,0 +1,579 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! PendingVotes store pending votes observed for a fixed epoch and round. +//! It is meant to be used inside of a RoundState. +//! The module takes care of creating a QC or a TC +//! when enough votes (or timeout votes) have been observed. +//! Votes are automatically dropped when the structure goes out of scope. + +use crate::{ + counters, + qc_aggregator::{create_qc_aggregator, QcAggregator}, + util::time_service::TimeService, +}; +use aptos_config::config::QcAggregatorType; +use aptos_consensus_types::{ + common::Author, + delayed_qc_msg::DelayedQcMsg, + quorum_cert::QuorumCert, + timeout_2chain::{TwoChainTimeoutCertificate, TwoChainTimeoutWithPartialSignatures}, + vote::Vote, + vote_data::VoteData, +}; +use aptos_crypto::{hash::CryptoHash, HashValue}; +use aptos_logger::prelude::*; +use aptos_types::{ + aggregate_signature::PartialSignatures, + ledger_info::LedgerInfoWithPartialSignatures, + validator_verifier::{ValidatorVerifier, VerifyError}, +}; +use futures_channel::mpsc::UnboundedSender; +use std::{ + collections::{BTreeMap, HashMap}, + fmt, + sync::Arc, +}; + +/// Result of the vote processing. The failure case (Verification error) is returned +/// as the Error part of the result. +#[derive(Debug, PartialEq, Eq)] +pub enum VoteReceptionResult { + /// The vote has been added but QC has not been formed yet. Return the amount of voting power + /// QC currently has. + VoteAdded(u128), + /// The vote has been added and we have gather enough voting power to form the QC but we have + /// delayed the QC to aggregate as many signatures as possible. + VoteAddedQCDelayed(u128), + /// The very same vote message has been processed in past. + DuplicateVote, + /// The very same author has already voted for another proposal in this round (equivocation). + EquivocateVote, + /// This block has just been certified after adding the vote. + NewQuorumCertificate(Arc), + /// The vote completes a new TwoChainTimeoutCertificate + New2ChainTimeoutCertificate(Arc), + /// There might be some issues adding a vote + ErrorAddingVote(VerifyError), + /// Error happens when aggregating signature + ErrorAggregatingSignature(VerifyError), + /// Error happens when aggregating timeout certificated + ErrorAggregatingTimeoutCertificate(VerifyError), + /// The vote is not for the current round. + UnexpectedRound(u64, u64), + /// Receive f+1 timeout to trigger a local timeout, return the amount of voting power TC currently has. + EchoTimeout(u128), +} + +/// A PendingVotes structure keep track of votes +pub struct PendingVotes { + /// Maps LedgerInfo digest to associated signatures (contained in a partial LedgerInfoWithSignatures). + /// This might keep multiple LedgerInfos for the current round: either due to different proposals (byzantine behavior) + /// or due to different NIL proposals (clients can have a different view of what block to extend). + li_digest_to_votes: + HashMap, + /// Tracks all the signatures of the 2-chain timeout for the given round. + maybe_partial_2chain_tc: Option, + /// Map of Author to (vote, li_digest). This is useful to discard multiple votes. + author_to_vote: HashMap, + /// Whether we have echoed timeout for this round. + echo_timeout: bool, + + qc_aggregator: Box, +} + +impl PendingVotes { + /// Creates an empty PendingVotes structure for a specific epoch and round + pub fn new( + time_service: Arc, + delayed_qc_tx: UnboundedSender, + qc_aggregator_type: QcAggregatorType, + ) -> Self { + PendingVotes { + li_digest_to_votes: HashMap::new(), + maybe_partial_2chain_tc: None, + author_to_vote: HashMap::new(), + echo_timeout: false, + qc_aggregator: create_qc_aggregator(qc_aggregator_type, time_service, delayed_qc_tx), + } + } + + /// Insert a vote and if the vote is valid, return a QuorumCertificate preferentially over a + /// TimeoutCertificate if either can can be formed + pub fn insert_vote( + &mut self, + vote: &Vote, + validator_verifier: &ValidatorVerifier, + ) -> VoteReceptionResult { + // derive data from vote + let li_digest = vote.ledger_info().hash(); + + // + // 1. Has the author already voted for this round? + // + + if let Some((previously_seen_vote, previous_li_digest)) = + self.author_to_vote.get(&vote.author()) + { + // is it the same vote? + if &li_digest == previous_li_digest { + // we've already seen an equivalent vote before + let new_timeout_vote = vote.is_timeout() && !previously_seen_vote.is_timeout(); + if !new_timeout_vote { + // it's not a new timeout vote + return VoteReceptionResult::DuplicateVote; + } + } else { + // we have seen a different vote for the same round + error!( + SecurityEvent::ConsensusEquivocatingVote, + remote_peer = vote.author(), + vote = vote, + previous_vote = previously_seen_vote + ); + + return VoteReceptionResult::EquivocateVote; + } + } + + // + // 2. Store new vote (or update, in case it's a new timeout vote) + // + + self.author_to_vote + .insert(vote.author(), (vote.clone(), li_digest)); + + // + // 3. Let's check if we can create a QC + // + + let len = self.li_digest_to_votes.len() + 1; + // obtain the ledger info with signatures associated to the vote's ledger info + let (hash_index, li_with_sig) = + self.li_digest_to_votes.entry(li_digest).or_insert_with(|| { + // if the ledger info with signatures doesn't exist yet, create it + ( + len, + LedgerInfoWithPartialSignatures::new( + vote.ledger_info().clone(), + PartialSignatures::empty(), + ), + ) + }); + + let validator_voting_power = validator_verifier + .get_voting_power(&vote.author()) + .unwrap_or(0); + if validator_voting_power == 0 { + warn!("Received vote with no voting power, from {}", vote.author()); + } + let cur_epoch = vote.vote_data().proposed().epoch() as i64; + let cur_round = vote.vote_data().proposed().round() as i64; + counters::CONSENSUS_CURRENT_ROUND_QUORUM_VOTING_POWER + .set(validator_verifier.quorum_voting_power() as f64); + + if !vote.is_timeout() { + counters::CONSENSUS_CURRENT_ROUND_VOTED_POWER + .with_label_values(&[&vote.author().to_string(), &hash_index_to_str(*hash_index)]) + .set(validator_voting_power as f64); + counters::CONSENSUS_LAST_VOTE_EPOCH + .with_label_values(&[&vote.author().to_string()]) + .set(cur_epoch); + counters::CONSENSUS_LAST_VOTE_ROUND + .with_label_values(&[&vote.author().to_string()]) + .set(cur_round); + } + + // add this vote to the ledger info with signatures + li_with_sig.add_signature(vote.author(), vote.signature().clone()); + + // check if we have enough signatures to create a QC + let voting_power = + match validator_verifier.check_voting_power(li_with_sig.signatures().keys(), true) { + // a quorum of signature was reached, a new QC is formed + Ok(aggregated_voting_power) => { + return self.qc_aggregator.handle_aggregated_qc( + validator_verifier, + aggregated_voting_power, + vote, + li_with_sig, + ); + }, + + // not enough votes + Err(VerifyError::TooLittleVotingPower { voting_power, .. }) => voting_power, + + // error + Err(error) => { + error!( + "MUST_FIX: vote received could not be added: {}, vote: {}", + error, vote + ); + return VoteReceptionResult::ErrorAddingVote(error); + }, + }; + + // + // 4. We couldn't form a QC, let's check if we can create a TC + // + + if let Some((timeout, signature)) = vote.two_chain_timeout() { + counters::CONSENSUS_CURRENT_ROUND_TIMEOUT_VOTED_POWER + .with_label_values(&[&vote.author().to_string()]) + .set(validator_voting_power as f64); + counters::CONSENSUS_LAST_TIMEOUT_VOTE_EPOCH + .with_label_values(&[&vote.author().to_string()]) + .set(cur_epoch); + counters::CONSENSUS_LAST_TIMEOUT_VOTE_ROUND + .with_label_values(&[&vote.author().to_string()]) + .set(cur_round); + + let partial_tc = self + .maybe_partial_2chain_tc + .get_or_insert_with(|| TwoChainTimeoutWithPartialSignatures::new(timeout.clone())); + partial_tc.add(vote.author(), timeout.clone(), signature.clone()); + let tc_voting_power = + match validator_verifier.check_voting_power(partial_tc.signers(), true) { + Ok(_) => { + return match partial_tc.aggregate_signatures(validator_verifier) { + Ok(tc_with_sig) => VoteReceptionResult::New2ChainTimeoutCertificate( + Arc::new(tc_with_sig), + ), + Err(e) => VoteReceptionResult::ErrorAggregatingTimeoutCertificate(e), + }; + }, + Err(VerifyError::TooLittleVotingPower { voting_power, .. }) => voting_power, + Err(error) => { + error!( + "MUST_FIX: 2-chain timeout vote received could not be added: {}, vote: {}", + error, vote + ); + return VoteReceptionResult::ErrorAddingVote(error); + }, + }; + + // Echo timeout if receive f+1 timeout message. + if !self.echo_timeout { + let f_plus_one = validator_verifier.total_voting_power() + - validator_verifier.quorum_voting_power() + + 1; + if tc_voting_power >= f_plus_one { + self.echo_timeout = true; + return VoteReceptionResult::EchoTimeout(tc_voting_power); + } + } + } + + // + // 5. No QC (or TC) could be formed, return the QC's voting power + // + + VoteReceptionResult::VoteAdded(voting_power) + } + + pub fn aggregate_qc_now( + validator_verifier: &ValidatorVerifier, + li_with_sig: &LedgerInfoWithPartialSignatures, + vote_data: &VoteData, + ) -> VoteReceptionResult { + match li_with_sig.aggregate_signatures(validator_verifier) { + Ok(ledger_info_with_sig) => VoteReceptionResult::NewQuorumCertificate(Arc::new( + QuorumCert::new(vote_data.clone(), ledger_info_with_sig), + )), + Err(e) => VoteReceptionResult::ErrorAggregatingSignature(e), + } + } + + pub fn process_delayed_qc( + &mut self, + validator_verifier: &ValidatorVerifier, + vote: Vote, + ) -> VoteReceptionResult { + let li_digest = vote.ledger_info().hash(); + let (_, li_with_sig) = self.li_digest_to_votes.get_mut(&li_digest).unwrap(); + match validator_verifier.check_voting_power(li_with_sig.signatures().keys(), true) { + // a quorum of signature was reached, a new QC is formed + Ok(_) => Self::aggregate_qc_now(validator_verifier, li_with_sig, vote.vote_data()), + + // not enough votes + Err(VerifyError::TooLittleVotingPower { .. }) => { + panic!("Delayed QC aggregation should not be triggered if we don't have enough votes to form a QC"); + }, + + // error + Err(error) => { + error!( + "MUST_FIX: vote received could not be added: {}, vote: {}", + error, vote + ); + VoteReceptionResult::ErrorAddingVote(error) + }, + } + } + + pub fn drain_votes( + &mut self, + ) -> ( + Vec<(HashValue, LedgerInfoWithPartialSignatures)>, + Option, + ) { + for (hash_index, _) in self.li_digest_to_votes.values() { + let hash_index_str = hash_index_to_str(*hash_index); + for author in self.author_to_vote.keys() { + counters::CONSENSUS_CURRENT_ROUND_VOTED_POWER + .with_label_values(&[&author.to_string(), &hash_index_str]) + .set(0_f64); + } + } + if let Some(partial_tc) = &self.maybe_partial_2chain_tc { + for author in partial_tc.signers() { + counters::CONSENSUS_CURRENT_ROUND_TIMEOUT_VOTED_POWER + .with_label_values(&[&author.to_string()]) + .set(0_f64); + } + } + + ( + self.li_digest_to_votes + .drain() + .map(|(key, (_, li))| (key, li)) + .collect(), + self.maybe_partial_2chain_tc.take(), + ) + } +} + +fn hash_index_to_str(hash_index: usize) -> String { + if hash_index <= 2 { + hash_index.to_string() + } else { + "other".to_string() + } +} + +// +// Helpful trait implementation +// + +impl fmt::Display for PendingVotes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // collect votes per ledger info + let votes = self + .li_digest_to_votes + .iter() + .map(|(li_digest, (_, li))| (li_digest, li.signatures().keys().collect::>())) + .collect::>(); + + // collect timeout votes + let timeout_votes = self + .maybe_partial_2chain_tc + .as_ref() + .map(|partial_tc| partial_tc.signers().collect::>()); + + // write + write!(f, "PendingVotes: [")?; + + for (hash, authors) in votes { + write!(f, "LI {} has {} votes {:?} ", hash, authors.len(), authors)?; + } + + if let Some(authors) = timeout_votes { + write!(f, "{} timeout {:?}", authors.len(), authors)?; + } + + write!(f, "]") + } +} + +// +// Tests +// + +#[cfg(test)] +mod tests { + use super::{PendingVotes, VoteReceptionResult}; + use crate::util::mock_time_service::SimulatedTimeService; + use aptos_config::config::QcAggregatorType; + use aptos_consensus_types::{ + block::block_test_utils::certificate_for_genesis, vote::Vote, vote_data::VoteData, + }; + use aptos_crypto::HashValue; + use aptos_types::{ + block_info::BlockInfo, ledger_info::LedgerInfo, + validator_verifier::random_validator_verifier, + }; + use futures_channel::mpsc::unbounded; + use itertools::Itertools; + use std::sync::Arc; + + /// Creates a random ledger info for epoch 1 and round 1. + fn random_ledger_info() -> LedgerInfo { + LedgerInfo::new( + BlockInfo::new(1, 0, HashValue::random(), HashValue::random(), 0, 0, None), + HashValue::random(), + ) + } + + /// Creates a random VoteData for epoch 1 and round 1, + /// extending a random block at epoch1 and round 0. + fn random_vote_data() -> VoteData { + VoteData::new(BlockInfo::random(1), BlockInfo::random(0)) + } + + #[test] + /// Verify that votes are properly aggregated to QC based on their LedgerInfo digest + fn test_qc_aggregation() { + ::aptos_logger::Logger::init_for_testing(); + + // set up 4 validators + let (signers, validator) = random_validator_verifier(4, Some(2), false); + let (delayed_qc_tx, _) = unbounded(); + let mut pending_votes = PendingVotes::new( + Arc::new(SimulatedTimeService::new()), + delayed_qc_tx, + QcAggregatorType::NoDelay, + ); + + // create random vote from validator[0] + let li1 = random_ledger_info(); + let vote_data_1 = random_vote_data(); + let vote_data_1_author_0 = + Vote::new(vote_data_1, signers[0].author(), li1, &signers[0]).unwrap(); + + // first time a new vote is added -> VoteAdded + assert_eq!( + pending_votes.insert_vote(&vote_data_1_author_0, &validator), + VoteReceptionResult::VoteAdded(1) + ); + + // same author voting for the same thing -> DuplicateVote + assert_eq!( + pending_votes.insert_vote(&vote_data_1_author_0, &validator), + VoteReceptionResult::DuplicateVote + ); + + // same author voting for a different result -> EquivocateVote + let li2 = random_ledger_info(); + let vote_data_2 = random_vote_data(); + let vote_data_2_author_0 = Vote::new( + vote_data_2.clone(), + signers[0].author(), + li2.clone(), + &signers[0], + ) + .unwrap(); + assert_eq!( + pending_votes.insert_vote(&vote_data_2_author_0, &validator), + VoteReceptionResult::EquivocateVote + ); + + // a different author voting for a different result -> VoteAdded + let vote_data_2_author_1 = Vote::new( + vote_data_2.clone(), + signers[1].author(), + li2.clone(), + &signers[1], + ) + .unwrap(); + assert_eq!( + pending_votes.insert_vote(&vote_data_2_author_1, &validator), + VoteReceptionResult::VoteAdded(1) + ); + + // two votes for the ledger info -> NewQuorumCertificate + let vote_data_2_author_2 = + Vote::new(vote_data_2, signers[2].author(), li2, &signers[2]).unwrap(); + match pending_votes.insert_vote(&vote_data_2_author_2, &validator) { + VoteReceptionResult::NewQuorumCertificate(qc) => { + assert!(qc.ledger_info().check_voting_power(&validator).is_ok()); + }, + _ => { + panic!("No QC formed."); + }, + }; + } + + #[test] + fn test_2chain_tc_aggregation() { + ::aptos_logger::Logger::init_for_testing(); + + // set up 4 validators + let (signers, validator) = random_validator_verifier(4, None, false); + let (delayed_qc_tx, _) = unbounded(); + let mut pending_votes = PendingVotes::new( + Arc::new(SimulatedTimeService::new()), + delayed_qc_tx, + QcAggregatorType::NoDelay, + ); + + // submit a new vote from validator[0] -> VoteAdded + let li0 = random_ledger_info(); + let vote0 = random_vote_data(); + let mut vote0_author_0 = Vote::new(vote0, signers[0].author(), li0, &signers[0]).unwrap(); + + assert_eq!( + pending_votes.insert_vote(&vote0_author_0, &validator), + VoteReceptionResult::VoteAdded(1) + ); + + // submit the same vote but enhanced with a timeout -> VoteAdded + let timeout = vote0_author_0.generate_2chain_timeout(certificate_for_genesis()); + let signature = timeout.sign(&signers[0]).unwrap(); + vote0_author_0.add_2chain_timeout(timeout, signature); + + assert_eq!( + pending_votes.insert_vote(&vote0_author_0, &validator), + VoteReceptionResult::VoteAdded(1) + ); + + // another vote for a different block cannot form a TC if it doesn't have a timeout signature + let li1 = random_ledger_info(); + let vote1 = random_vote_data(); + let mut vote1_author_1 = Vote::new(vote1, signers[1].author(), li1, &signers[1]).unwrap(); + assert_eq!( + pending_votes.insert_vote(&vote1_author_1, &validator), + VoteReceptionResult::VoteAdded(1) + ); + + // if that vote is now enhanced with a timeout signature -> EchoTimeout. + let timeout = vote1_author_1.generate_2chain_timeout(certificate_for_genesis()); + let signature = timeout.sign(&signers[1]).unwrap(); + vote1_author_1.add_2chain_timeout(timeout, signature); + match pending_votes.insert_vote(&vote1_author_1, &validator) { + VoteReceptionResult::EchoTimeout(voting_power) => { + assert_eq!(voting_power, 2); + }, + _ => { + panic!("Should echo timeout"); + }, + }; + + let li2 = random_ledger_info(); + let vote2 = random_vote_data(); + let mut vote2_author_2 = Vote::new(vote2, signers[2].author(), li2, &signers[2]).unwrap(); + + // if that vote is now enhanced with a timeout signature -> NewTimeoutCertificate. + let timeout = vote2_author_2.generate_2chain_timeout(certificate_for_genesis()); + let signature = timeout.sign(&signers[2]).unwrap(); + vote2_author_2.add_2chain_timeout(timeout, signature); + + match pending_votes.insert_vote(&vote2_author_2, &validator) { + VoteReceptionResult::New2ChainTimeoutCertificate(tc) => { + assert!(validator + .check_voting_power( + tc.signatures_with_rounds() + .get_voters( + &validator.get_ordered_account_addresses_iter().collect_vec() + ) + .iter(), + true + ) + .is_ok()); + }, + _ => { + panic!("Should form TC"); + }, + }; + } +} diff --git a/consensus/src/pipeline/buffer.rs b/consensus/src/pipeline/buffer.rs new file mode 100644 index 0000000000000..29ead7aba94d8 --- /dev/null +++ b/consensus/src/pipeline/buffer.rs @@ -0,0 +1,256 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::pipeline::hashable::Hashable; +use aptos_crypto::HashValue; +use std::collections::HashMap; + +pub struct LinkedItem { + // use option so we don't need T to be cloneable + elem: Option, + // index is for find_element_by_key to have a starting position (similar to find_element) + index: u64, + next: Option, +} + +pub type Cursor = Option; + +/// Buffer implementes an ordered dictionary +/// It supports push_back, pop_front, and lookup by HashValue +pub struct Buffer { + map: HashMap>, + count: u64, + head: Cursor, + tail: Cursor, +} + +impl Buffer { + pub fn new() -> Self { + Self { + map: HashMap::new(), + count: 0, + head: None, + tail: None, + } + } + + pub fn len(&self) -> usize { + self.map.len() + } + + pub fn head_cursor(&self) -> &Cursor { + &self.head + } + + #[cfg(test)] + pub fn tail_cursor(&self) -> &Cursor { + &self.tail + } + + pub fn push_back(&mut self, elem: T) { + self.count = self.count.checked_add(1).unwrap(); + let t_hash = elem.hash(); + self.map.insert(t_hash, LinkedItem { + elem: Some(elem), + index: self.count, + next: None, + }); + if let Some(tail) = self.tail { + self.map.get_mut(&tail).unwrap().next = Some(t_hash); + } + self.tail = Some(t_hash); + self.head.get_or_insert(t_hash); + } + + pub fn pop_front(&mut self) -> Option { + self.head.take().map(|head| { + let mut item = self.map.remove(&head).unwrap(); + let elem = item.elem.take(); + self.head = item.next; + if self.head.is_none() { + // empty + self.tail = None; + } + elem.unwrap() + }) + } + + // utils - assuming item is not None + pub fn get_next(&self, cursor: &Cursor) -> Cursor { + self.map.get(cursor.as_ref().unwrap()).unwrap().next + } + + pub fn get(&self, cursor: &Cursor) -> &T { + self.map + .get(cursor.as_ref().unwrap()) + .unwrap() + .elem + .as_ref() + .unwrap() + } + + pub fn set(&mut self, cursor: &Cursor, new_val: T) { + self.map + .get_mut(cursor.as_ref().unwrap()) + .unwrap() + .elem + .replace(new_val); + } + + pub fn take(&mut self, cursor: &Cursor) -> T { + self.map + .get_mut(cursor.as_ref().unwrap()) + .unwrap() + .elem + .take() + .unwrap() + } + + pub fn exist(&self, cursor: &Cursor) -> bool { + cursor.map_or(false, |key| self.map.contains_key(&key)) + } + + /// find_elem returns the first item non-prior to `cursor` that compare(item) is true + /// if no such item exists, the function returns None + pub fn find_elem_from bool>(&self, cursor: Cursor, compare: F) -> Cursor { + let mut current = cursor; + if !self.exist(&cursor) { + return None; + } + while current.is_some() { + if compare(self.get(¤t)) { + return current; + } + current = self.get_next(¤t); + } + None + } + + /// we make sure that the element found by the key is after `cursor` + /// if `cursor` is None, this function returns None (same as find_elem) + pub fn find_elem_by_key(&self, cursor: Cursor, key: HashValue) -> Cursor { + let cursor_order = self.map.get(cursor.as_ref()?).unwrap().index; + let item = self.map.get(&key)?; + if item.index >= cursor_order { + Some(key) + } else { + None + } + } +} + +// tests +#[cfg(test)] +mod test { + use super::Buffer; + use crate::pipeline::hashable::Hashable; + use aptos_crypto::HashValue; + use std::fmt::{Debug, Formatter}; + + #[derive(PartialEq, Eq)] + pub struct HashWrapper { + inner: HashValue, + } + + impl Debug for HashWrapper { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "HashWrapper: [{}]", self.inner) + } + } + + impl From for HashWrapper { + fn from(val: u64) -> Self { + Self { + inner: HashValue::from_u64(val), + } + } + } + + impl Hashable for HashWrapper { + fn hash(&self) -> HashValue { + self.inner + } + } + + #[test] + fn basics() { + let mut buffer = Buffer::::new(); + + // Check empty list behaves right + assert_eq!(buffer.pop_front(), None); + + // Populate list + buffer.push_back(HashWrapper::from(1)); + buffer.push_back(HashWrapper::from(2)); + buffer.push_back(HashWrapper::from(3)); + + // Check normal removal + assert_eq!(buffer.pop_front(), Some(HashWrapper::from(1))); + assert_eq!(buffer.pop_front(), Some(HashWrapper::from(2))); + + // Push some more just to make sure nothing's corrupted + buffer.push_back(HashWrapper::from(4)); + buffer.push_back(HashWrapper::from(5)); + + // Check normal removal + assert_eq!(buffer.pop_front(), Some(HashWrapper::from(3))); + assert_eq!(buffer.pop_front(), Some(HashWrapper::from(4))); + + // Check exhaustion + assert_eq!(buffer.pop_front(), Some(HashWrapper::from(5))); + assert_eq!(buffer.pop_front(), None); + } + + #[test] + fn find() { + let mut buffer = Buffer::::new(); + buffer.push_back(HashWrapper::from(1)); + buffer.push_back(HashWrapper::from(2)); + buffer.push_back(HashWrapper::from(3)); + + // look for 1 (succeed) + let res_1 = buffer.find_elem_by_key(*buffer.head_cursor(), HashValue::from_u64(1)); + assert_eq!(buffer.get(&res_1), &HashWrapper::from(1)); + // look for 4 (fail) + let res_no_4 = buffer.find_elem_by_key(*buffer.head_cursor(), HashValue::from_u64(4)); + assert!(res_no_4.is_none()); + // look for 1 after (or on) the tail (fail) + let res_no_1 = buffer.find_elem_by_key(*buffer.tail_cursor(), HashValue::from_u64(1)); + assert!(res_no_1.is_none()); + + // look for 2 (succeed) + let res_2 = + buffer.find_elem_from(*buffer.head_cursor(), |item| item == &HashWrapper::from(2)); + assert_eq!(buffer.get(&res_2), &HashWrapper::from(2)); + // look for 5 (fail) + let res_no_5 = + buffer.find_elem_from(*buffer.head_cursor(), |item| item == &HashWrapper::from(5)); + assert!(res_no_5.is_none()); + // look for 2 after (or on) the tail (fail) + let res_no_2 = + buffer.find_elem_from(*buffer.tail_cursor(), |item| item == &HashWrapper::from(2)); + assert!(res_no_2.is_none()); + } + + #[test] + fn get_set_take() { + let mut buffer = Buffer::::new(); + buffer.push_back(HashWrapper::from(1)); + buffer.push_back(HashWrapper::from(2)); + buffer.push_back(HashWrapper::from(3)); + + // test get + assert_eq!(buffer.get(buffer.head_cursor()), &HashWrapper::from(1)); + assert_eq!(buffer.get(buffer.tail_cursor()), &HashWrapper::from(3)); + + // test set + let tail = *buffer.tail_cursor(); + buffer.set(&tail, HashWrapper::from(5)); + assert_eq!(buffer.get(buffer.tail_cursor()), &HashWrapper::from(5)); + + // test take + let head = *buffer.head_cursor(); + assert_eq!(buffer.take(&head), HashWrapper::from(1)); + } +} diff --git a/consensus/src/pipeline/errors.rs b/consensus/src/pipeline/errors.rs new file mode 100644 index 0000000000000..746228438eceb --- /dev/null +++ b/consensus/src/pipeline/errors.rs @@ -0,0 +1,20 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use aptos_types::block_info::BlockInfo; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Clone, Debug, Deserialize, Error, PartialEq, Eq, Serialize)] +/// Different reasons of errors in commit phase +pub enum Error { + #[error("The block in the message, {0}, does not match expected block, {1}")] + InconsistentBlockInfo(BlockInfo, BlockInfo), + #[error("Verification Error")] + VerificationError, + #[error("Reset host dropped")] + ResetDropped, + #[error("Rand Reset host dropped")] + RandResetDropped, +} diff --git a/consensus/src/pipeline/execution_phase.rs b/consensus/src/pipeline/execution_phase.rs new file mode 100644 index 0000000000000..3f52473d51af8 --- /dev/null +++ b/consensus/src/pipeline/execution_phase.rs @@ -0,0 +1,92 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{experimental::pipeline_phase::StatelessPipeline, state_replication::StateComputer}; +use aptos_consensus_types::executed_block::ExecutedBlock; +use aptos_crypto::HashValue; +use aptos_executor_types::{ExecutorError, ExecutorResult}; +use async_trait::async_trait; +use std::{ + fmt::{Debug, Display, Formatter}, + sync::Arc, +}; + +/// [ This class is used when consensus.decoupled = true ] +/// ExecutionPhase is a singleton that receives ordered blocks from +/// the buffer manager and execute them. After the execution is done, +/// ExecutionPhase sends the ordered blocks back to the buffer manager. +/// + +pub struct ExecutionRequest { + pub ordered_blocks: Vec, +} + +impl Debug for ExecutionRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for ExecutionRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "ExecutionRequest({:?})", self.ordered_blocks) + } +} + +pub struct ExecutionResponse { + pub block_id: HashValue, + pub inner: ExecutorResult>, +} + +pub struct ExecutionPhase { + execution_proxy: Arc, +} + +impl ExecutionPhase { + pub fn new(execution_proxy: Arc) -> Self { + Self { execution_proxy } + } +} + +#[async_trait] +impl StatelessPipeline for ExecutionPhase { + type Request = ExecutionRequest; + type Response = ExecutionResponse; + + const NAME: &'static str = "execution"; + + async fn process(&self, req: ExecutionRequest) -> ExecutionResponse { + let ExecutionRequest { ordered_blocks } = req; + + if ordered_blocks.is_empty() { + // return err when the blocks are empty + return ExecutionResponse { + block_id: HashValue::zero(), + inner: Err(ExecutorError::EmptyBlocks), + }; + } + + let block_id = ordered_blocks.last().unwrap().id(); + let mut result = vec![]; + + for b in ordered_blocks { + match self.execution_proxy.compute(b.block(), b.parent_id()).await { + Ok(compute_result) => { + result.push(ExecutedBlock::new(b.block().clone(), compute_result)); + }, + Err(e) => { + return ExecutionResponse { + block_id, + inner: Err(e), + } + }, + } + } + + ExecutionResponse { + block_id, + inner: Ok(result), + } + } +} diff --git a/consensus/src/pipeline/execution_schedule_phase.rs b/consensus/src/pipeline/execution_schedule_phase.rs new file mode 100644 index 0000000000000..96586273649c1 --- /dev/null +++ b/consensus/src/pipeline/execution_schedule_phase.rs @@ -0,0 +1,110 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pipeline::{ + execution_wait_phase::ExecutionWaitRequest, + pipeline_phase::{CountedRequest, StatelessPipeline}, + }, + state_computer::PipelineExecutionResult, + state_replication::StateComputer, +}; +use aptos_consensus_types::pipelined_block::PipelinedBlock; +use aptos_crypto::HashValue; +use aptos_executor_types::ExecutorError; +use aptos_logger::debug; +use async_trait::async_trait; +use futures::TryFutureExt; +use std::{ + fmt::{Debug, Display, Formatter}, + sync::Arc, +}; + +/// [ This class is used when consensus.decoupled = true ] +/// ExecutionSchedulePhase is a singleton that receives ordered blocks from +/// the buffer manager and send them to the ExecutionPipeline. + +pub struct ExecutionRequest { + pub ordered_blocks: Vec, + // Hold a CountedRequest to guarantee the executor doesn't get reset with pending tasks + // stuck in the ExecutinoPipeline. + pub lifetime_guard: CountedRequest<()>, +} + +impl Debug for ExecutionRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for ExecutionRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "ExecutionScheduleRequest({:?})", self.ordered_blocks) + } +} + +pub struct ExecutionSchedulePhase { + execution_proxy: Arc, +} + +impl ExecutionSchedulePhase { + pub fn new(execution_proxy: Arc) -> Self { + Self { execution_proxy } + } +} + +#[async_trait] +impl StatelessPipeline for ExecutionSchedulePhase { + type Request = ExecutionRequest; + type Response = ExecutionWaitRequest; + + const NAME: &'static str = "execution_schedule"; + + async fn process(&self, req: ExecutionRequest) -> ExecutionWaitRequest { + let ExecutionRequest { + ordered_blocks, + lifetime_guard, + } = req; + + if ordered_blocks.is_empty() { + return ExecutionWaitRequest { + block_id: HashValue::zero(), + fut: Box::pin(async { Err(aptos_executor_types::ExecutorError::EmptyBlocks) }), + }; + } + + let block_id = ordered_blocks.last().unwrap().id(); + + // Call schedule_compute() for each block here (not in the fut being returned) to + // make sure they are scheduled in order. + let mut futs = vec![]; + for b in &ordered_blocks { + let fut = self + .execution_proxy + .schedule_compute(b.block(), b.parent_id(), b.randomness().cloned()) + .await; + futs.push(fut) + } + + // In the future being returned, wait for the compute results in order. + // n.b. Must `spawn()` here to make sure lifetime_guard will be released even if + // ExecutionWait phase is never kicked off. + let fut = tokio::task::spawn(async move { + let mut results = vec![]; + for (block, fut) in itertools::zip_eq(ordered_blocks, futs) { + debug!("try to receive compute result for block {}", block.id()); + let PipelineExecutionResult { input_txns, result } = fut.await?; + results.push(block.set_execution_result(input_txns, result)); + } + drop(lifetime_guard); + Ok(results) + }) + .map_err(ExecutorError::internal_err) + .and_then(|res| async { res }); + + ExecutionWaitRequest { + block_id, + fut: Box::pin(fut), + } + } +} diff --git a/consensus/src/pipeline/execution_wait_phase.rs b/consensus/src/pipeline/execution_wait_phase.rs new file mode 100644 index 0000000000000..88b616ca7d142 --- /dev/null +++ b/consensus/src/pipeline/execution_wait_phase.rs @@ -0,0 +1,55 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::pipeline::{buffer_item::ExecutionFut, pipeline_phase::StatelessPipeline}; +use aptos_consensus_types::pipelined_block::PipelinedBlock; +use aptos_crypto::HashValue; +use aptos_executor_types::ExecutorResult; +use async_trait::async_trait; +use std::fmt::{Debug, Display, Formatter}; + +/// [ This class is used when consensus.decoupled = true ] +/// ExecutionWaitPhase is a singleton that receives scheduled execution futures +/// from ExecutionSchedulePhase and waits for the results from the ExecutionPipeline. + +pub struct ExecutionWaitRequest { + pub block_id: HashValue, + pub fut: ExecutionFut, +} + +impl Debug for ExecutionWaitRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for ExecutionWaitRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "ExecutionRequest({:?})", self.block_id) + } +} + +pub struct ExecutionResponse { + pub block_id: HashValue, + pub inner: ExecutorResult>, +} + +pub struct ExecutionWaitPhase; + +#[async_trait] +impl StatelessPipeline for ExecutionWaitPhase { + type Request = ExecutionWaitRequest; + type Response = ExecutionResponse; + + const NAME: &'static str = "execution"; + + async fn process(&self, req: ExecutionWaitRequest) -> ExecutionResponse { + let ExecutionWaitRequest { block_id, fut } = req; + + ExecutionResponse { + block_id, + inner: fut.await, + } + } +} diff --git a/consensus/src/pipeline/hashable.rs b/consensus/src/pipeline/hashable.rs new file mode 100644 index 0000000000000..800ee9063c466 --- /dev/null +++ b/consensus/src/pipeline/hashable.rs @@ -0,0 +1,9 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::HashValue; + +pub trait Hashable { + fn hash(&self) -> HashValue; +} diff --git a/consensus/src/pipeline/linkedlist.rs b/consensus/src/pipeline/linkedlist.rs new file mode 100644 index 0000000000000..3353bbab7d33a --- /dev/null +++ b/consensus/src/pipeline/linkedlist.rs @@ -0,0 +1,322 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// modified from https://rust-unofficial.github.io/too-many-lists/fourth-final.html (MIT License) + +// maybe later we can move this to /common +use aptos_infallible::{Mutex, MutexGuard}; +use std::{ + cell::{Ref, RefCell, RefMut}, + rc::Rc, +}; + +pub struct List { + pub head: Link, + pub tail: Link, +} + +pub type Link = Option>>>; + +pub struct Node { + pub elem: Option, + pub next: Link, + pub prev: Link, +} + +impl Node { + pub fn new(elem: T) -> Rc> { + Rc::new(RefCell::new(Node { + elem: Some(elem), + prev: None, + next: None, + })) + } + + pub fn next(&self) -> Link { + self.next.as_ref().cloned() + } + + pub fn prev(&self) -> Link { + self.prev.as_ref().cloned() + } + + pub fn elem(&self) -> &T { + self.elem.as_ref().unwrap() + } + + pub fn elem_mut(&mut self) -> &mut T { + self.elem.as_mut().unwrap() + } +} + +impl List { + pub fn new() -> Self { + List { + head: None, + tail: None, + } + } + + pub fn push_front(&mut self, elem: T) { + let new_head = Node::new(elem); + match self.head.take() { + Some(old_head) => { + (*old_head).borrow_mut().prev = Some(new_head.clone()); + (*new_head).borrow_mut().next = Some(old_head); + self.head = Some(new_head); + } + None => { + self.tail = Some(new_head.clone()); + self.head = Some(new_head); + } + } + } + + pub fn push_back(&mut self, elem: T) { + let new_tail = Node::new(elem); + match self.tail.take() { + Some(old_tail) => { + (*old_tail).borrow_mut().next = Some(new_tail.clone()); + (*new_tail).borrow_mut().prev = Some(old_tail); + self.tail = Some(new_tail); + } + None => { + self.head = Some(new_tail.clone()); + self.tail = Some(new_tail); + } + } + } + + pub fn pop_back(&mut self) -> Option { + self.tail.take().map(|old_tail| { + match (*old_tail).borrow_mut().prev.take() { + Some(new_tail) => { + (*new_tail).borrow_mut().next.take(); + self.tail = Some(new_tail); + } + None => { + self.head.take(); + } + } + Rc::try_unwrap(old_tail) + .ok() + .unwrap() + .into_inner() + .elem + .unwrap() + }) + } + + pub fn pop_front(&mut self) -> Option { + self.head.take().map(|old_head| { + match (*old_head).borrow_mut().next.take() { + Some(new_head) => { + (*new_head).borrow_mut().prev.take(); + self.head = Some(new_head); + } + None => { + self.tail.take(); + } + } + Rc::try_unwrap(old_head) + .ok() + .unwrap() + .into_inner() + .elem + .unwrap() + }) + } + + pub fn peek_front(&self) -> Option> { + self.head + .as_ref() + .map(|node| Ref::map(node.borrow(), |node| node.elem.as_ref().unwrap())) + } + + pub fn peek_back(&self) -> Option> { + self.tail + .as_ref() + .map(|node| Ref::map(node.borrow(), |node| node.elem.as_ref().unwrap())) + } + + pub fn peek_back_mut(&mut self) -> Option> { + self.tail + .as_ref() + .map(|node| RefMut::map((**node).borrow_mut(), |node| node.elem.as_mut().unwrap())) + } + + pub fn peek_front_mut(&mut self) -> Option> { + self.head + .as_ref() + .map(|node| RefMut::map((**node).borrow_mut(), |node| node.elem.as_mut().unwrap())) + } + + pub fn into_iter(self) -> IntoIter { + IntoIter(self) + } +} + +impl Drop for List { + fn drop(&mut self) { + while self.pop_front().is_some() {} + } +} + +pub struct IntoIter(List); + +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.0.pop_front() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.0.pop_back() + } +} + +// utils - assuming link is not None + +pub fn get_next(link: &Link) -> Link { + (**link.as_ref().unwrap()).borrow().next() +} + +// TODO: maybe we need to make the following to macros to better enforce isolation +// e.g. (**link.as_ref().unwrap()).borrow().elem() + +pub fn get_elem(link: &Link) -> Ref { + Ref::map((**link.as_ref().unwrap()).borrow(), |borrow| borrow.elem()) +} + +pub fn take_elem(link: &Link) -> T { + let mut node = (**link.as_ref().unwrap()).borrow_mut(); + node.elem.take().unwrap() +} + +// same for this function +pub fn get_elem_mut(link: &Link) -> RefMut { + RefMut::map((**link.as_ref().unwrap()).borrow_mut(), |borrow_mut| { + borrow_mut.elem_mut() + }) +} + +pub fn set_elem(link: &Link, new_val: T) { + let mut node = (**link.as_ref().unwrap()).borrow_mut(); + node.elem.replace(new_val); +} + +pub fn find_elem bool, T>(link: Link, compare: F) -> Link { + let mut current = link; + while current.is_some() { + if compare(&get_elem(¤t)) { + return current; + } + current = get_next(¤t); + } + None +} + +pub fn link_eq(link_a: &Link, link_b: &Link) -> bool { + link_a.is_some() + && link_b.is_some() + && Rc::ptr_eq(link_a.as_ref().unwrap(), link_b.as_ref().unwrap()) +} + +// tests + +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn basics() { + let mut list = List::new(); + + // Check empty list behaves right + assert_eq!(list.pop_front(), None); + + // Populate list + list.push_front(1); + list.push_front(2); + list.push_front(3); + + // Check normal removal + assert_eq!(list.pop_front(), Some(3)); + assert_eq!(list.pop_front(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push_front(4); + list.push_front(5); + + // Check normal removal + assert_eq!(list.pop_front(), Some(5)); + assert_eq!(list.pop_front(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop_front(), Some(1)); + assert_eq!(list.pop_front(), None); + + // ---- back ----- + + // Check empty list behaves right + assert_eq!(list.pop_back(), None); + + // Populate list + list.push_back(1); + list.push_back(2); + list.push_back(3); + + // Check normal removal + assert_eq!(list.pop_back(), Some(3)); + assert_eq!(list.pop_back(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push_back(4); + list.push_back(5); + + // Check normal removal + assert_eq!(list.pop_back(), Some(5)); + assert_eq!(list.pop_back(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop_back(), Some(1)); + assert_eq!(list.pop_back(), None); + } + + #[test] + fn peek() { + let mut list = List::new(); + assert!(list.peek_front().is_none()); + assert!(list.peek_back().is_none()); + assert!(list.peek_front_mut().is_none()); + assert!(list.peek_back_mut().is_none()); + + list.push_front(1); + list.push_front(2); + list.push_front(3); + + assert_eq!(&*list.peek_front().unwrap(), &3); + assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); + assert_eq!(&*list.peek_back().unwrap(), &1); + assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); + } + + #[test] + fn into_iter() { + let mut list = List::new(); + list.push_front(1); + list.push_front(2); + list.push_front(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.next(), None); + } +} diff --git a/consensus/src/pipeline/mod.rs b/consensus/src/pipeline/mod.rs new file mode 100644 index 0000000000000..11269562e9cfa --- /dev/null +++ b/consensus/src/pipeline/mod.rs @@ -0,0 +1,40 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/* + * ┌──────────────────┐ + * │ 2. Signing Phase │ + * └──────────────▲─┬─┘ + * │ │ + * ┌────────────────────┐ │ │ ┌─────────────────────┐ + * │ 1. Execution Phase │ │ │ │ 4. Persisting Phase │ + * └─────────────────▲─┬┘ │ │ └┬─▲──────────────────┘ + * │ │ │ │ │ │ + * 0. Ordered ┌─┴─▼──┴─▼──▼─┴────┐ 3. Commit Vote ┌─────────┐ + * Blocks │ ├─────────────────► │ + * ┌─────────► Buffer Manager │ │ Network │ + * │ │ ◄─────────────────┤ │ + * ┌────┴─────┐ └─────────▲────────┘ Commit Vote └─────────┘ + * │ Ordering │ │ + * │ State │ Sync Req │ + * │ Computer ├─────────────┘ + * └──────────┘ + */ + +pub mod buffer; +pub mod buffer_item; +pub mod buffer_manager; +pub mod commit_reliable_broadcast; +pub mod decoupled_execution_utils; +pub mod errors; +pub mod execution_schedule_phase; +pub mod execution_wait_phase; +pub mod hashable; +pub mod persisting_phase; +pub mod pipeline_phase; +pub mod signing_phase; + +pub mod execution_client; +#[cfg(test)] +mod tests; diff --git a/consensus/src/pipeline/persisting_phase.rs b/consensus/src/pipeline/persisting_phase.rs new file mode 100644 index 0000000000000..8a60e4589c131 --- /dev/null +++ b/consensus/src/pipeline/persisting_phase.rs @@ -0,0 +1,75 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pipeline::pipeline_phase::StatelessPipeline, + state_replication::{StateComputer, StateComputerCommitCallBackType}, +}; +use aptos_consensus_types::pipelined_block::PipelinedBlock; +use aptos_executor_types::ExecutorResult; +use aptos_types::ledger_info::LedgerInfoWithSignatures; +use async_trait::async_trait; +use std::{ + fmt::{Debug, Display, Formatter}, + sync::Arc, +}; + +/// [ This class is used when consensus.decoupled = true ] +/// PersistingPhase is a singleton that receives aggregated blocks from +/// the buffer manager and persists them. Upon success, it returns +/// a response. + +pub struct PersistingRequest { + pub blocks: Vec>, + pub commit_ledger_info: LedgerInfoWithSignatures, + pub callback: StateComputerCommitCallBackType, +} + +impl Debug for PersistingRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for PersistingRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "PersistingRequest({:?}, {})", + self.blocks, self.commit_ledger_info, + ) + } +} + +pub type PersistingResponse = ExecutorResult<()>; + +pub struct PersistingPhase { + persisting_handle: Arc, +} + +impl PersistingPhase { + pub fn new(persisting_handle: Arc) -> Self { + Self { persisting_handle } + } +} + +#[async_trait] +impl StatelessPipeline for PersistingPhase { + type Request = PersistingRequest; + type Response = PersistingResponse; + + const NAME: &'static str = "persisting"; + + async fn process(&self, req: PersistingRequest) -> PersistingResponse { + let PersistingRequest { + blocks, + commit_ledger_info, + callback, + } = req; + + self.persisting_handle + .commit(&blocks, commit_ledger_info, callback) + .await + } +} diff --git a/consensus/src/pipeline/pipeline_phase.rs b/consensus/src/pipeline/pipeline_phase.rs new file mode 100644 index 0000000000000..89ddbda65ed5c --- /dev/null +++ b/consensus/src/pipeline/pipeline_phase.rs @@ -0,0 +1,99 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + counters::BUFFER_MANAGER_PHASE_PROCESS_SECONDS, + pipeline::buffer_manager::{Receiver, Sender}, +}; +use aptos_logger::debug; +use async_trait::async_trait; +use futures::{SinkExt, StreamExt}; +use std::sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, +}; + +#[async_trait] +pub trait StatelessPipeline: Send + Sync { + type Request; + type Response; + + const NAME: &'static str; + + async fn process(&self, req: Self::Request) -> Self::Response; +} + +struct TaskGuard { + counter: Arc, +} + +impl TaskGuard { + fn new(counter: Arc) -> Self { + counter.fetch_add(1, Ordering::SeqCst); + Self { counter } + } +} + +impl Drop for TaskGuard { + fn drop(&mut self) { + self.counter.fetch_sub(1, Ordering::SeqCst); + } +} + +pub struct CountedRequest { + req: Request, + guard: TaskGuard, +} + +impl CountedRequest { + pub fn new(req: Request, counter: Arc) -> Self { + let guard = TaskGuard::new(counter); + Self { req, guard } + } +} + +pub struct PipelinePhase { + rx: Receiver>, + maybe_tx: Option>, + processor: Box, + reset_flag: Arc, +} + +impl PipelinePhase { + pub fn new( + rx: Receiver>, + maybe_tx: Option>, + processor: Box, + reset_flag: Arc, + ) -> Self { + Self { + rx, + maybe_tx, + processor, + reset_flag, + } + } + + pub async fn start(mut self) { + // main loop + while let Some(counted_req) = self.rx.next().await { + let CountedRequest { req, guard: _guard } = counted_req; + if self.reset_flag.load(Ordering::SeqCst) { + continue; + } + let response = { + let _timer = BUFFER_MANAGER_PHASE_PROCESS_SECONDS + .with_label_values(&[T::NAME]) + .start_timer(); + self.processor.process(req).await + }; + if let Some(tx) = &mut self.maybe_tx { + if tx.send(response).await.is_err() { + debug!("Failed to send response, buffer manager probably dropped"); + break; + } + } + } + } +} diff --git a/consensus/src/pipeline/signing_phase.rs b/consensus/src/pipeline/signing_phase.rs new file mode 100644 index 0000000000000..8482b2c37fa72 --- /dev/null +++ b/consensus/src/pipeline/signing_phase.rs @@ -0,0 +1,84 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::pipeline::pipeline_phase::StatelessPipeline; +use aptos_crypto::bls12381; +use aptos_safety_rules::Error; +use aptos_types::ledger_info::{LedgerInfo, LedgerInfoWithSignatures}; +use async_trait::async_trait; +use std::{ + fmt::{Debug, Display, Formatter}, + sync::Arc, +}; + +/// [ This class is used when consensus.decoupled = true ] +/// SigningPhase is a singleton that receives executed blocks from +/// the buffer manager and sign them. After getting the signature from +/// the safety rule, SigningPhase sends the signature and error (if any) back. + +pub struct SigningRequest { + pub ordered_ledger_info: LedgerInfoWithSignatures, + pub commit_ledger_info: LedgerInfo, +} + +impl Debug for SigningRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for SigningRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "SigningRequest({}, {})", + self.ordered_ledger_info, self.commit_ledger_info + ) + } +} + +pub trait CommitSignerProvider: Send + Sync { + fn sign_commit_vote( + &self, + ledger_info: LedgerInfoWithSignatures, + new_ledger_info: LedgerInfo, + ) -> Result; +} + +pub struct SigningResponse { + pub signature_result: Result, + pub commit_ledger_info: LedgerInfo, +} + +pub struct SigningPhase { + safety_rule_handle: Arc, +} + +impl SigningPhase { + pub fn new(safety_rule_handle: Arc) -> Self { + Self { safety_rule_handle } + } +} + +#[async_trait] +impl StatelessPipeline for SigningPhase { + type Request = SigningRequest; + type Response = SigningResponse; + + const NAME: &'static str = "signing"; + + async fn process(&self, req: SigningRequest) -> SigningResponse { + let SigningRequest { + ordered_ledger_info, + commit_ledger_info, + } = req; + + SigningResponse { + signature_result: self + .safety_rule_handle + .sign_commit_vote(ordered_ledger_info, commit_ledger_info.clone()), + commit_ledger_info, + } + } +} diff --git a/consensus/src/pipeline/tests/execution_phase_tests.rs b/consensus/src/pipeline/tests/execution_phase_tests.rs new file mode 100644 index 0000000000000..bd582b44b2673 --- /dev/null +++ b/consensus/src/pipeline/tests/execution_phase_tests.rs @@ -0,0 +1,171 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pipeline::{ + buffer_manager::create_channel, + execution_schedule_phase::{ExecutionRequest, ExecutionSchedulePhase}, + execution_wait_phase::{ExecutionResponse, ExecutionWaitPhase}, + pipeline_phase::{CountedRequest, PipelinePhase, StatelessPipeline}, + tests::phase_tester::PhaseTester, + }, + state_replication::StateComputer, + test_utils::{consensus_runtime, RandomComputeResultStateComputer}, +}; +use aptos_consensus_types::{ + block::{block_test_utils::certificate_for_genesis, Block}, + common::Payload, + pipelined_block::PipelinedBlock, + quorum_cert::QuorumCert, +}; +use aptos_crypto::HashValue; +use aptos_executor_types::{ExecutorError, StateComputeResult}; +use aptos_types::{ledger_info::LedgerInfo, validator_verifier::random_validator_verifier}; +use async_trait::async_trait; +use std::sync::{ + atomic::{AtomicBool, AtomicU64}, + Arc, +}; + +// ExecutionSchedulePhase and ExecutionWaitPhase chained together. +// In BufferManager they are chained through the main loop. +pub struct ExecutionPhaseForTest { + schedule_phase: ExecutionSchedulePhase, + wait_phase: ExecutionWaitPhase, +} + +impl ExecutionPhaseForTest { + pub fn new(execution_proxy: Arc) -> Self { + let schedule_phase = ExecutionSchedulePhase::new(execution_proxy); + let wait_phase = ExecutionWaitPhase; + Self { + schedule_phase, + wait_phase, + } + } +} + +#[async_trait] +impl StatelessPipeline for ExecutionPhaseForTest { + type Request = ExecutionRequest; + type Response = ExecutionResponse; + + const NAME: &'static str = "execution"; + + async fn process(&self, req: ExecutionRequest) -> ExecutionResponse { + let wait_req = self.schedule_phase.process(req).await; + self.wait_phase.process(wait_req).await + } +} + +pub fn prepare_execution_phase() -> (HashValue, ExecutionPhaseForTest) { + let execution_proxy = Arc::new(RandomComputeResultStateComputer::new()); + let random_hash_value = execution_proxy.get_root_hash(); + let execution_phase = ExecutionPhaseForTest::new(execution_proxy); + + (random_hash_value, execution_phase) +} + +fn dummy_guard() -> CountedRequest<()> { + CountedRequest::new((), Arc::new(AtomicU64::new(0))) +} + +fn add_execution_phase_test_cases( + phase_tester: &mut PhaseTester, + random_hash_value: HashValue, +) { + let genesis_qc = certificate_for_genesis(); + let (signers, _validators) = random_validator_verifier(1, None, false); + let block = Block::new_proposal( + Payload::empty(false, true), + 1, + 1, + genesis_qc, + &signers[0], + Vec::new(), + ) + .unwrap(); + + // happy path + phase_tester.add_test_case( + ExecutionRequest { + ordered_blocks: vec![PipelinedBlock::new( + block, + vec![], + StateComputeResult::new_dummy(), + )], + lifetime_guard: dummy_guard(), + }, + Box::new(move |resp| { + assert_eq!( + resp.inner.unwrap()[0].compute_result().root_hash(), + random_hash_value + ); + }), + ); + + // empty block + phase_tester.add_test_case( + ExecutionRequest { + ordered_blocks: vec![], + lifetime_guard: dummy_guard(), + }, + Box::new(move |resp| assert!(matches!(resp.inner, Err(ExecutorError::EmptyBlocks)))), + ); + + // bad parent id + let bad_qc = QuorumCert::certificate_for_genesis_from_ledger_info( + &LedgerInfo::mock_genesis(None), + random_hash_value, + ); + let bad_block = Block::new_proposal( + Payload::empty(false, true), + 1, + 1, + bad_qc, + &signers[0], + Vec::new(), + ) + .unwrap(); + phase_tester.add_test_case( + ExecutionRequest { + ordered_blocks: vec![PipelinedBlock::new( + bad_block, + vec![], + StateComputeResult::new_dummy(), + )], + lifetime_guard: dummy_guard(), + }, + Box::new(move |resp| assert!(matches!(resp.inner, Err(ExecutorError::BlockNotFound(_))))), + ); +} + +#[test] +fn execution_phase_tests() { + let runtime = consensus_runtime(); + + // unit tests + let (random_hash_value, execution_phase) = prepare_execution_phase(); + let mut unit_phase_tester = PhaseTester::::new(); + add_execution_phase_test_cases(&mut unit_phase_tester, random_hash_value); + unit_phase_tester.unit_test(&execution_phase); + + // e2e tests + let (in_channel_tx, in_channel_rx) = create_channel::>(); + let (out_channel_tx, out_channel_rx) = create_channel::(); + let reset_flag = Arc::new(AtomicBool::new(false)); + + let execution_phase_pipeline = PipelinePhase::new( + in_channel_rx, + Some(out_channel_tx), + Box::new(execution_phase), + reset_flag, + ); + + runtime.spawn(execution_phase_pipeline.start()); + + let mut e2e_phase_tester = PhaseTester::::new(); + add_execution_phase_test_cases(&mut e2e_phase_tester, random_hash_value); + e2e_phase_tester.e2e_test(in_channel_tx, out_channel_rx); +} diff --git a/consensus/src/pipeline/tests/integration_tests.rs b/consensus/src/pipeline/tests/integration_tests.rs new file mode 100644 index 0000000000000..99feb270a5197 --- /dev/null +++ b/consensus/src/pipeline/tests/integration_tests.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// TODO: integration tests diff --git a/consensus/src/pipeline/tests/mod.rs b/consensus/src/pipeline/tests/mod.rs new file mode 100644 index 0000000000000..2d68165141586 --- /dev/null +++ b/consensus/src/pipeline/tests/mod.rs @@ -0,0 +1,11 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod buffer_manager_tests; +mod execution_phase_tests; +mod integration_tests; +mod ordering_state_computer_tests; +mod phase_tester; +mod signing_phase_tests; +mod test_utils; diff --git a/consensus/src/pipeline/tests/ordering_state_computer_tests.rs b/consensus/src/pipeline/tests/ordering_state_computer_tests.rs new file mode 100644 index 0000000000000..3bdf54f06eee2 --- /dev/null +++ b/consensus/src/pipeline/tests/ordering_state_computer_tests.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test] +fn test_ordering_state_computer() { + // TODO: after changing the ordering state computer +} diff --git a/consensus/src/pipeline/tests/phase_tester.rs b/consensus/src/pipeline/tests/phase_tester.rs new file mode 100644 index 0000000000000..2ae67bec53f4c --- /dev/null +++ b/consensus/src/pipeline/tests/phase_tester.rs @@ -0,0 +1,103 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pipeline::{ + buffer_manager::{Receiver, Sender}, + pipeline_phase::{CountedRequest, StatelessPipeline}, + }, + test_utils::{consensus_runtime, timed_block_on}, +}; +use futures::{SinkExt, StreamExt}; +use std::sync::{atomic::AtomicU64, Arc}; + +pub struct PhaseTestCase { + index: usize, + input: T::Request, + judge: Box, + prompt: Option, +} + +pub struct PhaseTester { + pub cases: Vec>, +} + +impl PhaseTester { + pub fn new() -> Self { + Self { cases: vec![] } + } + + pub fn add_test_case(&mut self, input: T::Request, judge: Box) { + self.add_test_case_with_prompt(input, judge, None) + } + + pub fn add_test_case_with_prompt( + &mut self, + input: T::Request, + judge: Box, + prompt: Option, + ) { + self.cases.push(PhaseTestCase { + index: self.cases.len(), + input, + judge, + prompt, + }); + } + + // unit tests are for phase processors only, + // this function consumes the tester + pub fn unit_test(self, processor: &T) { + let runtime = consensus_runtime(); + + timed_block_on(&runtime, async move { + for PhaseTestCase { + index, + input, + judge, + prompt, + } in self.cases + { + eprint!( + "Unit Test - {}:", + prompt.unwrap_or(format!("Test {}", index)) + ); + let resp = processor.process(input).await; + judge(resp); + eprintln!(" OK",); + } + }) + } + + // e2e tests are for the pipeline phases + // this function consumes the tester + pub fn e2e_test( + self, + mut tx: Sender>, + mut rx: Receiver, + ) { + let runtime = consensus_runtime(); + + timed_block_on(&runtime, async move { + for PhaseTestCase { + index, + input, + judge, + prompt, + } in self.cases + { + eprint!( + "E2E Test - {}:", + prompt.unwrap_or(format!("Test {}", index)) + ); + tx.send(CountedRequest::new(input, Arc::new(AtomicU64::new(0)))) + .await + .ok(); + let resp = rx.next().await.unwrap(); + judge(resp); + eprintln!(" OK",); + } + }) + } +} diff --git a/consensus/src/pipeline/tests/signing_phase_tests.rs b/consensus/src/pipeline/tests/signing_phase_tests.rs new file mode 100644 index 0000000000000..e5c5b534b6f0f --- /dev/null +++ b/consensus/src/pipeline/tests/signing_phase_tests.rs @@ -0,0 +1,146 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pipeline::{ + buffer_manager::{create_channel, Receiver, Sender}, + pipeline_phase::{CountedRequest, PipelinePhase}, + signing_phase::{SigningPhase, SigningRequest, SigningResponse}, + tests::{ + phase_tester::PhaseTester, + test_utils::{ + prepare_executed_blocks_with_executed_ledger_info, + prepare_executed_blocks_with_ordered_ledger_info, prepare_safety_rules, + }, + }, + }, + test_utils::consensus_runtime, +}; +use aptos_crypto::HashValue; +use aptos_safety_rules::Error; +use aptos_types::{ + aggregate_signature::AggregateSignature, + block_info::BlockInfo, + ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, + validator_signer::ValidatorSigner, +}; +use std::sync::{atomic::AtomicBool, Arc}; + +pub fn prepare_signing_pipeline( + signing_phase: SigningPhase, +) -> ( + Sender>, + Receiver, + PipelinePhase, +) { + // e2e tests + let (in_channel_tx, in_channel_rx) = create_channel::>(); + let (out_channel_tx, out_channel_rx) = create_channel::(); + let reset_flag = Arc::new(AtomicBool::new(false)); + + let signing_phase_pipeline = PipelinePhase::new( + in_channel_rx, + Some(out_channel_tx), + Box::new(signing_phase), + reset_flag, + ); + + (in_channel_tx, out_channel_rx, signing_phase_pipeline) +} + +fn add_signing_phase_test_cases( + phase_tester: &mut PhaseTester, + signers: &[ValidatorSigner], +) { + let (vecblocks, ordered_ledger_info) = + prepare_executed_blocks_with_ordered_ledger_info(&signers[0]); + let commit_ledger_info = LedgerInfo::new( + vecblocks.last().unwrap().block_info(), + HashValue::from_u64(0xBEEF), + ); + + // happy path + phase_tester.add_test_case( + SigningRequest { + ordered_ledger_info: ordered_ledger_info.clone(), + commit_ledger_info: commit_ledger_info.clone(), + }, + Box::new(move |resp| { + assert!(resp.signature_result.is_ok()); + assert_eq!(resp.commit_ledger_info, commit_ledger_info); + }), + ); + + let (_, executed_ledger_info) = prepare_executed_blocks_with_executed_ledger_info(&signers[0]); + let inconsistent_commit_ledger_info = + LedgerInfo::new(BlockInfo::random(1), HashValue::from_u64(0xBEEF)); + + // inconsistent + phase_tester.add_test_case( + SigningRequest { + ordered_ledger_info: ordered_ledger_info.clone(), + commit_ledger_info: inconsistent_commit_ledger_info, + }, + Box::new(move |resp| { + assert!(matches!( + resp.signature_result, + Err(Error::InconsistentExecutionResult(_, _)) + )); + }), + ); + + // not ordered-only + phase_tester.add_test_case( + SigningRequest { + ordered_ledger_info: executed_ledger_info.clone(), + commit_ledger_info: executed_ledger_info.ledger_info().clone(), + }, + Box::new(move |resp| { + assert!(matches!( + resp.signature_result, + Err(Error::InvalidOrderedLedgerInfo(_)) + )); + }), + ); + + // invalid quorum + phase_tester.add_test_case( + SigningRequest { + ordered_ledger_info: LedgerInfoWithSignatures::new( + ordered_ledger_info.ledger_info().clone(), + AggregateSignature::empty(), + ), + commit_ledger_info: executed_ledger_info.ledger_info().clone(), + }, + Box::new(move |resp| { + assert!(matches!( + resp.signature_result, + Err(Error::InvalidQuorumCertificate(_)) + )); + }), + ); +} + +#[test] +fn signing_phase_tests() { + let runtime = consensus_runtime(); + + let (safety_rule_handle, signers) = prepare_safety_rules(); + + let signing_phase = SigningPhase::new(safety_rule_handle); + + // unit tests + let mut unit_phase_tester = PhaseTester::::new(); + add_signing_phase_test_cases(&mut unit_phase_tester, &signers); + unit_phase_tester.unit_test(&signing_phase); + + let (in_channel_tx, out_channel_rx, signing_phase_pipeline) = + prepare_signing_pipeline(signing_phase); + + runtime.spawn(signing_phase_pipeline.start()); + + let mut e2e_phase_tester = PhaseTester::::new(); + add_signing_phase_test_cases(&mut e2e_phase_tester, &signers); + e2e_phase_tester.e2e_test(in_channel_tx, out_channel_rx); +} diff --git a/consensus/src/qc_aggregator.rs b/consensus/src/qc_aggregator.rs new file mode 100644 index 0000000000000..2f695c651927b --- /dev/null +++ b/consensus/src/qc_aggregator.rs @@ -0,0 +1,181 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + pending_votes::{PendingVotes, VoteReceptionResult}, + util::time_service::TimeService, +}; +use aptos_config::config::{DelayedQcAggregatorConfig, QcAggregatorType}; +use aptos_consensus_types::{delayed_qc_msg::DelayedQcMsg, vote::Vote}; +use aptos_logger::{error, info}; +use aptos_types::{ + ledger_info::LedgerInfoWithPartialSignatures, validator_verifier::ValidatorVerifier, +}; +use futures::SinkExt; +use futures_channel::mpsc::UnboundedSender; +use std::{sync::Arc, time::Duration}; +use tokio::time::sleep; + +pub trait QcAggregator: Send + Sync { + fn handle_aggregated_qc( + &mut self, + validator_verifier: &ValidatorVerifier, + aggregated_voting_power: u128, + vote: &Vote, + li_with_sig: &LedgerInfoWithPartialSignatures, + ) -> VoteReceptionResult; +} + +struct NoDelayQcAggregator {} + +pub fn create_qc_aggregator( + qc_aggregator_type: QcAggregatorType, + time_service: Arc, + delayed_qc_tx: UnboundedSender, +) -> Box { + match qc_aggregator_type { + QcAggregatorType::NoDelay => Box::new(NoDelayQcAggregator {}), + QcAggregatorType::Delayed(delay_config) => { + let DelayedQcAggregatorConfig { + max_delay_after_round_start_ms, + aggregated_voting_power_pct_to_wait, + pct_delay_after_qc_aggregated, + } = delay_config; + Box::new(DelayedQcAggregator::new( + Duration::from_millis(max_delay_after_round_start_ms), + aggregated_voting_power_pct_to_wait, + pct_delay_after_qc_aggregated, + time_service, + delayed_qc_tx, + )) + }, + } +} + +impl QcAggregator for NoDelayQcAggregator { + fn handle_aggregated_qc( + &mut self, + validator_verifier: &ValidatorVerifier, + aggregated_voting_power: u128, + vote: &Vote, + li_with_sig: &LedgerInfoWithPartialSignatures, + ) -> VoteReceptionResult { + assert!( + aggregated_voting_power >= validator_verifier.quorum_voting_power(), + "QC aggregation should not be triggered if we don't have enough votes to form a QC" + ); + PendingVotes::aggregate_qc_now(validator_verifier, li_with_sig, vote.vote_data()) + } +} + +struct DelayedQcAggregator { + round_start_time: Duration, + max_delay_after_round_start: Duration, + aggregated_voting_power_pct_to_wait: usize, + pct_delay_after_qc_aggregated: usize, + time_service: Arc, + // True, if we already have enough vote to aggregate a QC, but we have trigged a delayed QC + // aggregation event to collect as many votes as possible. + qc_aggregation_delayed: bool, + // To send delayed QC aggregation events to the round manager. + delayed_qc_tx: UnboundedSender, +} + +impl DelayedQcAggregator { + pub fn new( + max_delay_after_round_start: Duration, + aggregated_voting_power_pct_to_wait: usize, + pct_delay_after_qc_aggregated: usize, + time_service: Arc, + delayed_qc_tx: UnboundedSender, + ) -> Self { + let round_start_time = time_service.get_current_timestamp(); + Self { + round_start_time, + max_delay_after_round_start, + aggregated_voting_power_pct_to_wait, + pct_delay_after_qc_aggregated, + time_service, + qc_aggregation_delayed: false, + delayed_qc_tx, + } + } +} + +impl QcAggregator for DelayedQcAggregator { + fn handle_aggregated_qc( + &mut self, + validator_verifier: &ValidatorVerifier, + aggregated_voting_power: u128, + vote: &Vote, + li_with_sig: &LedgerInfoWithPartialSignatures, + ) -> VoteReceptionResult { + assert!( + aggregated_voting_power >= validator_verifier.quorum_voting_power(), + "QC aggregation should not be triggered if we don't have enough votes to form a QC" + ); + let current_time = self.time_service.get_current_timestamp(); + + // If we have reached the aggregated voting power threshold, we should aggregate the QC now. + if aggregated_voting_power + >= self.aggregated_voting_power_pct_to_wait as u128 + * validator_verifier.total_voting_power() + / 100 + { + // Voting power is u128 so there is no overflow here. + info!( + "QC aggregation triggered by aggregated voting power: {}", + aggregated_voting_power + ); + return PendingVotes::aggregate_qc_now( + validator_verifier, + li_with_sig, + vote.vote_data(), + ); + } + + // If we have not reached the aggregated voting power threshold and have + // already triggered a delayed QC aggregation event, we should not trigger another + // one. + if self.qc_aggregation_delayed { + return VoteReceptionResult::VoteAddedQCDelayed(aggregated_voting_power); + } + + let time_since_round_start = current_time - self.round_start_time; + if time_since_round_start >= self.max_delay_after_round_start { + info!( + "QC aggregation triggered by time: {} ms", + time_since_round_start.as_millis() + ); + return PendingVotes::aggregate_qc_now( + validator_verifier, + li_with_sig, + vote.vote_data(), + ); + } + + let wait_time = (self.max_delay_after_round_start - time_since_round_start) + .min(time_since_round_start * self.pct_delay_after_qc_aggregated as u32 / 100); + + let delayed_qc_event = DelayedQcMsg::new(vote.clone()); + self.qc_aggregation_delayed = true; + + let mut delayed_qc_sender = self.delayed_qc_tx.clone(); + + info!( + "QC aggregation delayed by {} ms, wait time: {} ms", + time_since_round_start.as_millis(), + wait_time.as_millis() + ); + + tokio::spawn(async move { + sleep(wait_time).await; + if let Err(e) = delayed_qc_sender.send(delayed_qc_event).await { + error!("Failed to send event to round manager {:?}", e); + } + }); + + VoteReceptionResult::VoteAddedQCDelayed(aggregated_voting_power) + } +} diff --git a/consensus/src/quorum_store/batch_requester.rs b/consensus/src/quorum_store/batch_requester.rs new file mode 100644 index 0000000000000..3e136c6f1efb1 --- /dev/null +++ b/consensus/src/quorum_store/batch_requester.rs @@ -0,0 +1,202 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + monitor, + network::QuorumStoreSender, + quorum_store::{ + counters, + types::{BatchRequest, BatchResponse}, + }, +}; +use aptos_consensus_types::proof_of_store::{BatchInfo, ProofOfStore}; +use aptos_crypto::HashValue; +use aptos_executor_types::*; +use aptos_logger::prelude::*; +use aptos_types::{transaction::SignedTransaction, validator_verifier::ValidatorVerifier, PeerId}; +use futures::{stream::FuturesUnordered, StreamExt}; +use rand::Rng; +use std::{sync::Arc, time::Duration}; +use tokio::{sync::oneshot, time}; + +struct BatchRequesterState { + signers: Vec, + next_index: usize, + ret_tx: oneshot::Sender>>, + num_retries: usize, + retry_limit: usize, +} + +impl BatchRequesterState { + fn new( + signers: Vec, + ret_tx: oneshot::Sender>>, + retry_limit: usize, + ) -> Self { + Self { + signers, + next_index: 0, + ret_tx, + num_retries: 0, + retry_limit, + } + } + + fn next_request_peers(&mut self, num_peers: usize) -> Option> { + if self.num_retries == 0 { + let mut rng = rand::thread_rng(); + // make sure nodes request from the different set of nodes + self.next_index = rng.gen::() % self.signers.len(); + counters::SENT_BATCH_REQUEST_COUNT.inc_by(num_peers as u64); + } else { + counters::SENT_BATCH_REQUEST_RETRY_COUNT.inc_by(num_peers as u64); + } + if self.num_retries < self.retry_limit { + self.num_retries += 1; + let ret = self + .signers + .iter() + .cycle() + .skip(self.next_index) + .take(num_peers) + .cloned() + .collect(); + self.next_index = (self.next_index + num_peers) % self.signers.len(); + Some(ret) + } else { + None + } + } + + fn serve_request(self, digest: HashValue, maybe_payload: Option>) { + if let Some(payload) = maybe_payload { + trace!( + "QS: batch to oneshot, digest {}, tx {:?}", + digest, + self.ret_tx + ); + if self.ret_tx.send(Ok(payload)).is_err() { + debug!( + "Receiver of requested batch not available for digest {}", + digest + ) + }; + } else if self + .ret_tx + .send(Err(ExecutorError::CouldNotGetData)) + .is_err() + { + debug!( + "Receiver of requested batch not available for unavailable digest {}", + digest + ); + } + } +} + +pub(crate) struct BatchRequester { + epoch: u64, + my_peer_id: PeerId, + request_num_peers: usize, + retry_limit: usize, + retry_interval_ms: usize, + rpc_timeout_ms: usize, + network_sender: T, + validator_verifier: Arc, +} + +impl BatchRequester { + pub(crate) fn new( + epoch: u64, + my_peer_id: PeerId, + request_num_peers: usize, + retry_limit: usize, + retry_interval_ms: usize, + rpc_timeout_ms: usize, + network_sender: T, + validator_verifier: ValidatorVerifier, + ) -> Self { + Self { + epoch, + my_peer_id, + request_num_peers, + retry_limit, + retry_interval_ms, + rpc_timeout_ms, + network_sender, + validator_verifier: Arc::new(validator_verifier), + } + } + + pub(crate) async fn request_batch( + &self, + proof: ProofOfStore, + ret_tx: oneshot::Sender>>, + ) -> Option<(BatchInfo, Vec)> { + let digest = *proof.digest(); + let expiration = proof.expiration(); + let signers = proof.shuffled_signers(&self.validator_verifier); + let validator_verifier = self.validator_verifier.clone(); + let mut request_state = BatchRequesterState::new(signers, ret_tx, self.retry_limit); + let network_sender = self.network_sender.clone(); + let request_num_peers = self.request_num_peers; + let my_peer_id = self.my_peer_id; + let epoch = self.epoch; + let retry_interval = Duration::from_millis(self.retry_interval_ms as u64); + let rpc_timeout = Duration::from_millis(self.rpc_timeout_ms as u64); + + monitor!("batch_request", { + let mut interval = time::interval(retry_interval); + let mut futures = FuturesUnordered::new(); + let request = BatchRequest::new(my_peer_id, epoch, digest); + loop { + tokio::select! { + _ = interval.tick() => { + // send batch request to a set of peers of size request_num_peers + if let Some(request_peers) = request_state.next_request_peers(request_num_peers) { + for peer in request_peers { + futures.push(network_sender.request_batch(request.clone(), peer, rpc_timeout)); + } + } else if futures.is_empty() { + // end the loop when the futures are drained + break; + } + }, + Some(response) = futures.next() => { + match response { + Ok(BatchResponse::Batch(batch)) => { + counters::RECEIVED_BATCH_RESPONSE_COUNT.inc(); + let digest = *batch.digest(); + let batch_info = batch.batch_info().clone(); + let payload = batch.into_transactions(); + request_state.serve_request(digest, Some(payload.clone())); + return Some((batch_info, payload)); + } + // Short-circuit if the chain has moved beyond expiration + Ok(BatchResponse::NotFound(ledger_info)) => { + counters::RECEIVED_BATCH_NOT_FOUND_COUNT.inc(); + if ledger_info.commit_info().epoch() == epoch + && ledger_info.commit_info().timestamp_usecs() > expiration + && ledger_info.verify_signatures(&validator_verifier).is_ok() + { + counters::RECEIVED_BATCH_EXPIRED_COUNT.inc(); + debug!("QS: batch request expired, digest:{}", digest); + request_state.serve_request(digest, None); + return None; + } + } + Err(e) => { + counters::RECEIVED_BATCH_RESPONSE_ERROR_COUNT.inc(); + debug!("QS: batch request error, digest:{}, error:{:?}", digest, e); + } + } + }, + } + } + counters::RECEIVED_BATCH_REQUEST_TIMEOUT_COUNT.inc(); + debug!("QS: batch request timed out, digest:{}", digest); + request_state.serve_request(digest, None); + None + }) + } +} diff --git a/consensus/src/quorum_store/batch_store.rs b/consensus/src/quorum_store/batch_store.rs new file mode 100644 index 0000000000000..e6a38aa2314b0 --- /dev/null +++ b/consensus/src/quorum_store/batch_store.rs @@ -0,0 +1,459 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + network::QuorumStoreSender, + quorum_store::{ + batch_requester::BatchRequester, + counters, + quorum_store_db::QuorumStoreStorage, + types::{PersistedValue, StorageMode}, + utils::TimeExpirations, + }, +}; +use anyhow::bail; +use aptos_consensus_types::proof_of_store::{ProofOfStore, SignedBatchInfo}; +use aptos_crypto::HashValue; +use aptos_executor_types::{ExecutorError, ExecutorResult}; +use aptos_logger::prelude::*; +use aptos_types::{transaction::SignedTransaction, validator_signer::ValidatorSigner, PeerId}; +use dashmap::{ + mapref::entry::Entry::{Occupied, Vacant}, + DashMap, +}; +use fail::fail_point; +use once_cell::sync::OnceCell; +use std::{ + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, + }, + time::Duration, +}; +use tokio::sync::oneshot; + +// Pub(crate) for testing only. +pub(crate) struct QuotaManager { + memory_balance: usize, + db_balance: usize, + batch_balance: usize, + // Recording the provided quotas for asserts. + memory_quota: usize, + db_quota: usize, + batch_quota: usize, +} + +impl QuotaManager { + pub(crate) fn new(db_quota: usize, memory_quota: usize, batch_quota: usize) -> Self { + assert!(db_quota >= memory_quota); + Self { + memory_balance: memory_quota, + db_balance: db_quota, + batch_balance: batch_quota, + memory_quota, + db_quota, + batch_quota, + } + } + + pub(crate) fn update_quota(&mut self, num_bytes: usize) -> anyhow::Result { + if self.batch_balance == 0 { + counters::EXCEEDED_BATCH_QUOTA_COUNT.inc(); + bail!("Batch quota exceeded "); + } + + if self.db_balance >= num_bytes { + self.batch_balance -= 1; + self.db_balance -= num_bytes; + + if self.memory_balance >= num_bytes { + self.memory_balance -= num_bytes; + Ok(StorageMode::MemoryAndPersisted) + } else { + Ok(StorageMode::PersistedOnly) + } + } else { + counters::EXCEEDED_STORAGE_QUOTA_COUNT.inc(); + bail!("Storage quota exceeded "); + } + } + + fn assert_quota(balance: usize, to_free: usize, quota: usize, kind: &str) { + assert!( + balance + to_free <= quota, + "Balance {} + to_free {} more than {} quota {}", + balance, + to_free, + kind, + quota, + ); + } + + pub(crate) fn free_quota(&mut self, num_bytes: usize, storage_mode: StorageMode) { + Self::assert_quota(self.batch_balance, 1, self.batch_quota, "Batch"); + self.batch_balance += 1; + + Self::assert_quota(self.db_balance, num_bytes, self.db_quota, "DB"); + self.db_balance += num_bytes; + + if matches!(storage_mode, StorageMode::MemoryAndPersisted) { + Self::assert_quota(self.memory_balance, num_bytes, self.memory_quota, "Memory"); + self.memory_balance += num_bytes; + } + } +} + +/// Provides in memory representation of stored batches (strong cache), and allows +/// efficient concurrent readers. +pub struct BatchStore { + epoch: OnceCell, + last_certified_time: AtomicU64, + db_cache: DashMap, + peer_quota: DashMap, + expirations: Mutex>, + db: Arc, + memory_quota: usize, + db_quota: usize, + batch_quota: usize, + validator_signer: ValidatorSigner, +} + +impl BatchStore { + pub(crate) fn new( + epoch: u64, + last_certified_time: u64, + db: Arc, + memory_quota: usize, + db_quota: usize, + batch_quota: usize, + validator_signer: ValidatorSigner, + ) -> Self { + let db_clone = db.clone(); + let batch_store = Self { + epoch: OnceCell::with_value(epoch), + last_certified_time: AtomicU64::new(last_certified_time), + db_cache: DashMap::new(), + peer_quota: DashMap::new(), + expirations: Mutex::new(TimeExpirations::new()), + db, + memory_quota, + db_quota, + batch_quota, + validator_signer, + }; + let db_content = db_clone + .get_all_batches() + .expect("failed to read data from db"); + let mut expired_keys = Vec::new(); + trace!( + "QS: Batchreader {} {} {}", + db_content.len(), + epoch, + last_certified_time + ); + for (digest, value) in db_content { + let expiration = value.expiration(); + + trace!( + "QS: Batchreader recovery content exp {:?}, digest {}", + expiration, + digest + ); + + if last_certified_time >= expiration { + expired_keys.push(digest); + } else { + batch_store + .insert_to_cache(&value) + .expect("Storage limit exceeded upon BatchReader construction"); + } + } + trace!( + "QS: Batchreader recovery expired keys len {}", + expired_keys.len() + ); + db_clone.delete_batches(expired_keys).unwrap(); + + batch_store + } + + fn epoch(&self) -> u64 { + *self.epoch.get().unwrap() + } + + fn free_quota(&self, value: PersistedValue) { + let mut quota_manager = self + .peer_quota + .get_mut(&value.author()) + .expect("No QuotaManager for batch author"); + quota_manager.free_quota(value.num_bytes() as usize, value.payload_storage_mode()); + } + + // Inserts a PersistedValue into the in-memory db_cache. If an entry with a higher + // value is already in the db_cache, Ok(false) is returned. If there was no entry + // Ok(true) is returned after the successful insertion. Finally, the method returns + // an error if storage quota is exceeded (if in-memory quota is exceeded, + // only the metadata is stored in the db-cache). + // Note: holds db_cache entry lock (due to DashMap), while accessing peer_quota + // DashMap. Hence, peer_quota reference should never be held while accessing the + // db_cache to avoid the deadlock (if needed, order is db_cache, then peer_quota). + pub(crate) fn insert_to_cache(&self, value: &PersistedValue) -> anyhow::Result { + let digest = *value.digest(); + let author = value.author(); + let expiration_time = value.expiration(); + + { + // Acquire dashmap internal lock on the entry corresponding to the digest. + let cache_entry = self.db_cache.entry(digest); + + if let Occupied(entry) = &cache_entry { + if entry.get().expiration() >= expiration_time { + debug!( + "QS: already have the digest with higher expiration {}", + digest + ); + return Ok(false); + } + }; + let value_to_be_stored = if self + .peer_quota + .entry(author) + .or_insert(QuotaManager::new( + self.db_quota, + self.memory_quota, + self.batch_quota, + )) + .update_quota(value.num_bytes() as usize)? + == StorageMode::PersistedOnly + { + PersistedValue::new(value.batch_info().clone(), None) + } else { + value.clone() + }; + + match cache_entry { + Occupied(entry) => { + let (k, prev_value) = entry.replace_entry(value_to_be_stored); + debug_assert!(k == digest); + self.free_quota(prev_value); + }, + Vacant(slot) => { + slot.insert(value_to_be_stored); + }, + } + } + + // Add expiration for the inserted entry, no need to be atomic w. insertion. + self.expirations + .lock() + .unwrap() + .add_item(digest, expiration_time); + Ok(true) + } + + pub(crate) fn save(&self, value: &PersistedValue) -> anyhow::Result { + let last_certified_time = self.last_certified_time(); + if value.expiration() > last_certified_time { + fail_point!("quorum_store::save", |_| { + // Skip caching and storing value to the db + Ok(false) + }); + counters::GAP_BETWEEN_BATCH_EXPIRATION_AND_CURRENT_TIME_WHEN_SAVE.observe( + Duration::from_micros(value.expiration() - last_certified_time).as_secs_f64(), + ); + + return self.insert_to_cache(value); + } + counters::NUM_BATCH_EXPIRED_WHEN_SAVE.inc(); + bail!( + "Incorrect expiration {} in epoch {}, last committed timestamp {}", + value.expiration(), + self.epoch(), + last_certified_time, + ); + } + + // pub(crate) for testing + pub(crate) fn clear_expired_payload(&self, certified_time: u64) -> Vec { + let expired_digests = self.expirations.lock().unwrap().expire(certified_time); + let mut ret = Vec::new(); + for h in expired_digests { + let removed_value = match self.db_cache.entry(h) { + Occupied(entry) => { + // We need to check up-to-date expiration again because receiving the same + // digest with a higher expiration would update the persisted value and + // effectively extend the expiration. + if entry.get().expiration() <= certified_time { + Some(entry.remove()) + } else { + None + } + }, + Vacant(_) => unreachable!("Expired entry not in cache"), + }; + // No longer holding the lock on db_cache entry. + if let Some(value) = removed_value { + self.free_quota(value); + ret.push(h); + } + } + ret + } + + fn persist_inner(&self, persist_request: PersistedValue) -> Option { + match self.save(&persist_request) { + Ok(needs_db) => { + let batch_info = persist_request.batch_info().clone(); + trace!("QS: sign digest {}", persist_request.digest()); + if needs_db { + self.db + .save_batch(persist_request) + .expect("Could not write to DB"); + } + SignedBatchInfo::new(batch_info, &self.validator_signer).ok() + }, + + Err(e) => { + debug!("QS: failed to store to cache {:?}", e); + None + }, + } + } + + pub fn update_certified_timestamp(&self, certified_time: u64) { + trace!("QS: batch reader updating time {:?}", certified_time); + let prev_time = self + .last_certified_time + .fetch_max(certified_time, Ordering::SeqCst); + // Note: prev_time may be equal to certified_time due to state-sync + // at the epoch boundary. + assert!( + prev_time <= certified_time, + "Decreasing executed block timestamp reported to BatchReader {} {}", + prev_time, + certified_time, + ); + + let expired_keys = self.clear_expired_payload(certified_time); + if let Err(e) = self.db.delete_batches(expired_keys) { + debug!("Error deleting batches: {:?}", e) + } + } + + fn last_certified_time(&self) -> u64 { + self.last_certified_time.load(Ordering::Relaxed) + } + + fn get_batch_from_db(&self, digest: &HashValue) -> ExecutorResult { + counters::GET_BATCH_FROM_DB_COUNT.inc(); + + match self.db.get_batch(digest) { + Ok(Some(value)) => Ok(value), + Ok(None) | Err(_) => { + warn!("Could not get batch from db"); + Err(ExecutorError::CouldNotGetData) + }, + } + } + + pub(crate) fn get_batch_from_local( + &self, + digest: &HashValue, + ) -> ExecutorResult { + if let Some(value) = self.db_cache.get(digest) { + if value.payload_storage_mode() == StorageMode::PersistedOnly { + self.get_batch_from_db(digest) + } else { + // Available in memory. + Ok(value.clone()) + } + } else { + Err(ExecutorError::CouldNotGetData) + } + } +} + +impl BatchWriter for BatchStore { + fn persist(&self, persist_requests: Vec) -> Vec { + let mut signed_infos = vec![]; + for persist_request in persist_requests.into_iter() { + if let Some(signed_info) = self.persist_inner(persist_request) { + signed_infos.push(signed_info); + } + } + signed_infos + } +} + +pub trait BatchReader: Send + Sync { + /// Check if the batch corresponding to the digest exists, return the batch author if true + fn exists(&self, digest: &HashValue) -> Option; + + fn get_batch( + &self, + proof: ProofOfStore, + ) -> oneshot::Receiver>>; + + fn update_certified_timestamp(&self, certified_time: u64); +} + +pub struct BatchReaderImpl { + batch_store: Arc, + batch_requester: Arc>, +} + +impl BatchReaderImpl { + pub(crate) fn new(batch_store: Arc, batch_requester: BatchRequester) -> Self { + Self { + batch_store, + batch_requester: Arc::new(batch_requester), + } + } +} + +impl BatchReader for BatchReaderImpl { + fn exists(&self, digest: &HashValue) -> Option { + self.batch_store + .get_batch_from_local(digest) + .map(|v| v.author()) + .ok() + } + + fn get_batch( + &self, + proof: ProofOfStore, + ) -> oneshot::Receiver>> { + let (tx, rx) = oneshot::channel(); + let batch_store = self.batch_store.clone(); + let batch_requester = self.batch_requester.clone(); + tokio::spawn(async move { + if let Ok(mut value) = batch_store.get_batch_from_local(proof.digest()) { + if tx + .send(Ok(value.take_payload().expect("Must have payload"))) + .is_err() + { + debug!( + "Receiver of local batch not available for digest {}", + proof.digest() + ) + }; + } else { + // Quorum store metrics + counters::MISSED_BATCHES_COUNT.inc(); + if let Some((batch_info, payload)) = batch_requester.request_batch(proof, tx).await + { + batch_store.persist(vec![PersistedValue::new(batch_info, Some(payload))]); + } + } + }); + rx + } + + fn update_certified_timestamp(&self, certified_time: u64) { + self.batch_store.update_certified_timestamp(certified_time); + } +} + +pub trait BatchWriter: Send + Sync { + fn persist(&self, persist_requests: Vec) -> Vec; +} diff --git a/consensus/src/quorum_store/mod.rs b/consensus/src/quorum_store/mod.rs new file mode 100644 index 0000000000000..888b62b0122c2 --- /dev/null +++ b/consensus/src/quorum_store/mod.rs @@ -0,0 +1,23 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod counters; +/// Equivalent to directly fetching blocks from mempool without a quorum store. +pub mod direct_mempool_quorum_store; + +pub(crate) mod batch_coordinator; +pub(crate) mod batch_generator; +pub(crate) mod batch_requester; +pub(crate) mod batch_store; +pub(crate) mod network_listener; +pub(crate) mod proof_coordinator; +pub(crate) mod proof_manager; +pub(crate) mod quorum_store_builder; +pub(crate) mod quorum_store_coordinator; +pub mod quorum_store_db; +pub mod types; +pub(crate) mod utils; + +mod schema; +#[cfg(test)] +mod tests; diff --git a/consensus/src/quorum_store/network_listener.rs b/consensus/src/quorum_store/network_listener.rs new file mode 100644 index 0000000000000..1d31ea808b1a0 --- /dev/null +++ b/consensus/src/quorum_store/network_listener.rs @@ -0,0 +1,92 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + monitor, + quorum_store::{ + batch_coordinator::BatchCoordinatorCommand, counters, + proof_coordinator::ProofCoordinatorCommand, proof_manager::ProofManagerCommand, + }, + round_manager::VerifiedEvent, +}; +use aptos_channels::aptos_channel; +use aptos_logger::prelude::*; +use aptos_types::PeerId; +use futures::StreamExt; +use tokio::sync::mpsc::Sender; + +pub(crate) struct NetworkListener { + network_msg_rx: aptos_channel::Receiver, + proof_coordinator_tx: Sender, + remote_batch_coordinator_tx: Vec>, + proof_manager_tx: Sender, +} + +impl NetworkListener { + pub(crate) fn new( + network_msg_rx: aptos_channel::Receiver, + proof_coordinator_tx: Sender, + remote_batch_coordinator_tx: Vec>, + proof_manager_tx: Sender, + ) -> Self { + Self { + network_msg_rx, + proof_coordinator_tx, + remote_batch_coordinator_tx, + proof_manager_tx, + } + } + + pub async fn start(mut self) { + info!("QS: starting networking"); + while let Some(msg) = self.network_msg_rx.next().await { + monitor!("qs_network_listener_main_loop", { + match msg { + // TODO: does the assumption have to be that network listener is shutdown first? + VerifiedEvent::Shutdown(ack_tx) => { + info!("QS: shutdown network listener received"); + ack_tx + .send(()) + .expect("Failed to send shutdown ack to QuorumStore"); + break; + }, + VerifiedEvent::SignedBatchInfo(signed_batch_infos) => { + let cmd = ProofCoordinatorCommand::AppendSignature(*signed_batch_infos); + self.proof_coordinator_tx + .send(cmd) + .await + .expect("Could not send signed_batch_info to proof_coordinator"); + }, + VerifiedEvent::BatchMsg(batch_msg) => { + let author = batch_msg.author(); + let batches = batch_msg.take(); + counters::RECEIVED_BATCH_MSG_COUNT.inc(); + + let idx = + author.to_vec()[0] as usize % self.remote_batch_coordinator_tx.len(); + trace!( + "QS: peer_id {:?}, # network_worker {}, hashed to idx {}", + author, + self.remote_batch_coordinator_tx.len(), + idx + ); + self.remote_batch_coordinator_tx[idx] + .send(BatchCoordinatorCommand::NewBatches(author, batches)) + .await + .expect("Could not send remote batch"); + }, + VerifiedEvent::ProofOfStoreMsg(proofs) => { + let cmd = ProofManagerCommand::ReceiveProofs(*proofs); + self.proof_manager_tx + .send(cmd) + .await + .expect("could not push Proof proof_of_store"); + }, + _ => { + unreachable!() + }, + }; + }); + } + } +} diff --git a/consensus/src/quorum_store/quorum_store_coordinator.rs b/consensus/src/quorum_store/quorum_store_coordinator.rs new file mode 100644 index 0000000000000..089e74c499be8 --- /dev/null +++ b/consensus/src/quorum_store/quorum_store_coordinator.rs @@ -0,0 +1,158 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + monitor, + quorum_store::{ + batch_coordinator::BatchCoordinatorCommand, batch_generator::BatchGeneratorCommand, + proof_coordinator::ProofCoordinatorCommand, proof_manager::ProofManagerCommand, + }, + round_manager::VerifiedEvent, +}; +use aptos_channels::aptos_channel; +use aptos_consensus_types::proof_of_store::BatchInfo; +use aptos_logger::prelude::*; +use aptos_types::{account_address::AccountAddress, PeerId}; +use futures::StreamExt; +use tokio::sync::{mpsc, oneshot}; + +pub enum CoordinatorCommand { + CommitNotification(u64, Vec), + Shutdown(futures_channel::oneshot::Sender<()>), +} + +pub struct QuorumStoreCoordinator { + my_peer_id: PeerId, + batch_generator_cmd_tx: mpsc::Sender, + remote_batch_coordinator_cmd_tx: Vec>, + proof_coordinator_cmd_tx: mpsc::Sender, + proof_manager_cmd_tx: mpsc::Sender, + quorum_store_msg_tx: aptos_channel::Sender, +} + +impl QuorumStoreCoordinator { + pub(crate) fn new( + my_peer_id: PeerId, + batch_generator_cmd_tx: mpsc::Sender, + remote_batch_coordinator_cmd_tx: Vec>, + proof_coordinator_cmd_tx: mpsc::Sender, + proof_manager_cmd_tx: mpsc::Sender, + quorum_store_msg_tx: aptos_channel::Sender, + ) -> Self { + Self { + my_peer_id, + batch_generator_cmd_tx, + remote_batch_coordinator_cmd_tx, + proof_coordinator_cmd_tx, + proof_manager_cmd_tx, + quorum_store_msg_tx, + } + } + + pub async fn start(self, mut rx: futures_channel::mpsc::Receiver) { + while let Some(cmd) = rx.next().await { + monitor!("quorum_store_coordinator_loop", { + match cmd { + CoordinatorCommand::CommitNotification(block_timestamp, batches) => { + // TODO: need a callback or not? + self.proof_coordinator_cmd_tx + .send(ProofCoordinatorCommand::CommitNotification(batches.clone())) + .await + .expect("Failed to send to ProofCoordinator"); + + self.proof_manager_cmd_tx + .send(ProofManagerCommand::CommitNotification( + block_timestamp, + batches.clone(), + )) + .await + .expect("Failed to send to ProofManager"); + + self.batch_generator_cmd_tx + .send(BatchGeneratorCommand::CommitNotification( + block_timestamp, + batches, + )) + .await + .expect("Failed to send to BatchGenerator"); + }, + CoordinatorCommand::Shutdown(ack_tx) => { + // Note: Shutdown is done from the back of the quorum store pipeline to the + // front, so senders are always shutdown before receivers. This avoids sending + // messages through closed channels during shutdown. + // Oneshots that send data in the reverse order of the pipeline must assume that + // the receiver could be unavailable during shutdown, and resolve this without + // panicking. + + let (network_listener_shutdown_tx, network_listener_shutdown_rx) = + oneshot::channel(); + match self.quorum_store_msg_tx.push( + self.my_peer_id, + VerifiedEvent::Shutdown(network_listener_shutdown_tx), + ) { + Ok(()) => info!("QS: shutdown network listener sent"), + Err(err) => panic!("Failed to send to NetworkListener, Err {:?}", err), + }; + network_listener_shutdown_rx + .await + .expect("Failed to stop NetworkListener"); + + let (batch_generator_shutdown_tx, batch_generator_shutdown_rx) = + oneshot::channel(); + self.batch_generator_cmd_tx + .send(BatchGeneratorCommand::Shutdown(batch_generator_shutdown_tx)) + .await + .expect("Failed to send to BatchGenerator"); + batch_generator_shutdown_rx + .await + .expect("Failed to stop BatchGenerator"); + + for remote_batch_coordinator_cmd_tx in self.remote_batch_coordinator_cmd_tx + { + let ( + remote_batch_coordinator_shutdown_tx, + remote_batch_coordinator_shutdown_rx, + ) = oneshot::channel(); + remote_batch_coordinator_cmd_tx + .send(BatchCoordinatorCommand::Shutdown( + remote_batch_coordinator_shutdown_tx, + )) + .await + .expect("Failed to send to Remote BatchCoordinator"); + remote_batch_coordinator_shutdown_rx + .await + .expect("Failed to stop Remote BatchCoordinator"); + } + + let (proof_coordinator_shutdown_tx, proof_coordinator_shutdown_rx) = + oneshot::channel(); + self.proof_coordinator_cmd_tx + .send(ProofCoordinatorCommand::Shutdown( + proof_coordinator_shutdown_tx, + )) + .await + .expect("Failed to send to ProofCoordinator"); + proof_coordinator_shutdown_rx + .await + .expect("Failed to stop ProofCoordinator"); + + let (proof_manager_shutdown_tx, proof_manager_shutdown_rx) = + oneshot::channel(); + self.proof_manager_cmd_tx + .send(ProofManagerCommand::Shutdown(proof_manager_shutdown_tx)) + .await + .expect("Failed to send to ProofManager"); + proof_manager_shutdown_rx + .await + .expect("Failed to stop ProofManager"); + + ack_tx + .send(()) + .expect("Failed to send shutdown ack from QuorumStore"); + break; + }, + } + }) + } + } +} diff --git a/consensus/src/quorum_store/schema.rs b/consensus/src/quorum_store/schema.rs new file mode 100644 index 0000000000000..f6213c463c9c0 --- /dev/null +++ b/consensus/src/quorum_store/schema.rs @@ -0,0 +1,74 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::quorum_store::types::PersistedValue; +use anyhow::Result; +use aptos_consensus_types::proof_of_store::BatchId; +use aptos_crypto::HashValue; +use aptos_schemadb::{ + schema::{KeyCodec, Schema, ValueCodec}, + ColumnFamilyName, +}; + +pub(crate) const BATCH_CF_NAME: ColumnFamilyName = "batch"; +pub(crate) const BATCH_ID_CF_NAME: ColumnFamilyName = "batch_ID"; + +#[derive(Debug)] +pub(crate) struct BatchSchema; + +impl Schema for BatchSchema { + type Key = HashValue; + type Value = PersistedValue; + + const COLUMN_FAMILY_NAME: aptos_schemadb::ColumnFamilyName = BATCH_CF_NAME; +} + +impl KeyCodec for HashValue { + fn encode_key(&self) -> Result> { + Ok(self.to_vec()) + } + + fn decode_key(data: &[u8]) -> Result { + Ok(HashValue::from_slice(data)?) + } +} + +impl ValueCodec for PersistedValue { + fn encode_value(&self) -> Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_value(data: &[u8]) -> Result { + Ok(bcs::from_bytes(data)?) + } +} + +#[derive(Debug)] +pub(crate) struct BatchIdSchema; + +impl Schema for BatchIdSchema { + type Key = u64; + type Value = BatchId; + + const COLUMN_FAMILY_NAME: aptos_schemadb::ColumnFamilyName = BATCH_ID_CF_NAME; +} + +impl KeyCodec for u64 { + fn encode_key(&self) -> Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_key(data: &[u8]) -> Result { + Ok(bcs::from_bytes(data)?) + } +} + +impl ValueCodec for BatchId { + fn encode_value(&self) -> Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_value(data: &[u8]) -> Result { + Ok(bcs::from_bytes(data)?) + } +} diff --git a/consensus/src/quorum_store/tests/batch_requester_test.rs b/consensus/src/quorum_store/tests/batch_requester_test.rs new file mode 100644 index 0000000000000..aaf5ff6bf4528 --- /dev/null +++ b/consensus/src/quorum_store/tests/batch_requester_test.rs @@ -0,0 +1,258 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + network::QuorumStoreSender, + quorum_store::{ + batch_requester::BatchRequester, + types::{Batch, BatchRequest, BatchResponse}, + }, +}; +use aptos_consensus_types::{ + common::Author, + proof_of_store::{BatchId, ProofOfStore, SignedBatchInfo}, +}; +use aptos_crypto::HashValue; +use aptos_types::{ + aggregate_signature::{AggregateSignature, PartialSignatures}, + block_info::BlockInfo, + ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, + validator_signer::ValidatorSigner, + validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier}, +}; +use move_core_types::account_address::AccountAddress; +use std::time::{Duration, Instant}; + +#[derive(Clone)] +struct MockBatchRequester { + return_value: BatchResponse, +} + +impl MockBatchRequester { + fn new(return_value: BatchResponse) -> Self { + Self { return_value } + } +} + +#[async_trait::async_trait] +impl QuorumStoreSender for MockBatchRequester { + async fn send_batch_request(&self, _request: BatchRequest, _recipients: Vec) { + unimplemented!() + } + + async fn request_batch( + &self, + _request: BatchRequest, + _recipient: Author, + _timeout: Duration, + ) -> anyhow::Result { + Ok(self.return_value.clone()) + } + + async fn send_batch(&self, _batch: Batch, _recipients: Vec) { + unimplemented!() + } + + async fn send_signed_batch_info_msg( + &self, + _signed_batch_infos: Vec, + _recipients: Vec, + ) { + unimplemented!() + } + + async fn broadcast_batch_msg(&mut self, _batches: Vec) { + unimplemented!() + } + + async fn broadcast_proof_of_store_msg(&mut self, _proof_of_stores: Vec) { + unimplemented!() + } + + async fn send_proof_of_store_msg_to_self(&mut self, _proof_of_stores: Vec) { + unimplemented!() + } +} + +#[tokio::test] +async fn test_batch_request_exists() { + let batch = Batch::new( + BatchId::new_for_test(1), + vec![], + 1, + 1, + AccountAddress::random(), + 0, + ); + let batch_response = BatchResponse::Batch(batch.clone()); + + let validator_signer = ValidatorSigner::random(None); + let (tx, mut rx) = tokio::sync::oneshot::channel(); + let batch_requester = BatchRequester::new( + 1, + AccountAddress::random(), + 1, + 2, + 1_000, + 1_000, + MockBatchRequester::new(batch_response), + ValidatorVerifier::new_single(validator_signer.author(), validator_signer.public_key()), + ); + + let result = batch_requester + .request_batch( + ProofOfStore::new( + batch.batch_info().clone(), + AggregateSignature::new(vec![u8::MAX].into(), None), + ), + tx, + ) + .await; + assert!(result.is_some()); + if let Some((batch_info, _payload)) = result { + assert_eq!(batch_info, *batch.batch_info()); + } + assert!(rx.try_recv().is_ok()); +} + +fn create_ledger_info_with_timestamp( + timestamp: u64, +) -> (LedgerInfoWithSignatures, ValidatorVerifier) { + const NUM_SIGNERS: u8 = 1; + // Generate NUM_SIGNERS random signers. + let validator_signers: Vec = (0..NUM_SIGNERS) + .map(|i| ValidatorSigner::random([i; 32])) + .collect(); + let block_info = BlockInfo::new( + 1, + 1, + HashValue::random(), + HashValue::random(), + 0, + timestamp, + None, + ); + let ledger_info = LedgerInfo::new(block_info, HashValue::random()); + + // Create a map from authors to public keys with equal voting power. + let mut validator_infos = vec![]; + for validator in validator_signers.iter() { + validator_infos.push(ValidatorConsensusInfo::new( + validator.author(), + validator.public_key(), + 1, + )); + } + + // Create a map from author to signatures. + let mut partial_signature = PartialSignatures::empty(); + for validator in validator_signers.iter() { + partial_signature.add_signature(validator.author(), validator.sign(&ledger_info).unwrap()); + } + + // Let's assume our verifier needs to satisfy all NUM_SIGNERS + let validator_verifier = + ValidatorVerifier::new_with_quorum_voting_power(validator_infos, NUM_SIGNERS as u128) + .expect("Incorrect quorum size."); + let aggregated_signature = validator_verifier + .aggregate_signatures(&partial_signature) + .unwrap(); + let ledger_info_with_signatures = + LedgerInfoWithSignatures::new(ledger_info, aggregated_signature); + + (ledger_info_with_signatures, validator_verifier) +} + +#[tokio::test] +async fn test_batch_request_not_exists_not_expired() { + let retry_interval_ms = 1_000; + let expiration = 10_000; + + // Batch has not expired yet + let (ledger_info_with_signatures, validator_verifier) = + create_ledger_info_with_timestamp(expiration - 1); + + let batch = Batch::new( + BatchId::new_for_test(1), + vec![], + 1, + expiration, + AccountAddress::random(), + 0, + ); + let (tx, mut rx) = tokio::sync::oneshot::channel(); + let batch_response = BatchResponse::NotFound(ledger_info_with_signatures); + let batch_requester = BatchRequester::new( + 1, + AccountAddress::random(), + 1, + 2, + retry_interval_ms, + 1_000, + MockBatchRequester::new(batch_response), + validator_verifier, + ); + + let request_start = Instant::now(); + let result = batch_requester + .request_batch( + ProofOfStore::new( + batch.batch_info().clone(), + AggregateSignature::new(vec![u8::MAX].into(), None), + ), + tx, + ) + .await; + let request_duration = request_start.elapsed(); + assert!(result.is_none()); + assert!(rx.try_recv().is_ok()); + // Retried at least once + assert!(request_duration > Duration::from_millis(retry_interval_ms as u64)); +} + +#[tokio::test] +async fn test_batch_request_not_exists_expired() { + let retry_interval_ms = 1_000; + let expiration = 10_000; + + // Batch has expired according to the ledger info that will be returned + let (ledger_info_with_signatures, validator_verifier) = + create_ledger_info_with_timestamp(expiration + 1); + + let batch = Batch::new( + BatchId::new_for_test(1), + vec![], + 1, + expiration, + AccountAddress::random(), + 0, + ); + let (tx, mut rx) = tokio::sync::oneshot::channel(); + let batch_response = BatchResponse::NotFound(ledger_info_with_signatures); + let batch_requester = BatchRequester::new( + 1, + AccountAddress::random(), + 1, + 2, + retry_interval_ms, + 1_000, + MockBatchRequester::new(batch_response), + validator_verifier, + ); + + let request_start = Instant::now(); + let result = batch_requester + .request_batch( + ProofOfStore::new( + batch.batch_info().clone(), + AggregateSignature::new(vec![u8::MAX].into(), None), + ), + tx, + ) + .await; + let request_duration = request_start.elapsed(); + assert!(result.is_none()); + assert!(rx.try_recv().is_ok()); + // No retry because of short-circuiting of expired batch + assert!(request_duration < Duration::from_millis(retry_interval_ms as u64)); +} diff --git a/consensus/src/quorum_store/tests/batch_store_test.rs b/consensus/src/quorum_store/tests/batch_store_test.rs new file mode 100644 index 0000000000000..fed469c4df93f --- /dev/null +++ b/consensus/src/quorum_store/tests/batch_store_test.rs @@ -0,0 +1,283 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::quorum_store::{ + batch_store::{BatchStore, BatchWriter, QuotaManager}, + quorum_store_db::QuorumStoreDB, + types::{PersistedValue, StorageMode}, +}; +use aptos_consensus_types::proof_of_store::{BatchId, BatchInfo}; +use aptos_crypto::HashValue; +use aptos_temppath::TempPath; +use aptos_types::{ + account_address::AccountAddress, transaction::SignedTransaction, + validator_verifier::random_validator_verifier, +}; +use claims::{assert_err, assert_ok, assert_ok_eq}; +use once_cell::sync::Lazy; +use std::sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, +}; +use tokio::task::spawn_blocking; + +static TEST_REQUEST_ACCOUNT: Lazy = Lazy::new(AccountAddress::random); + +pub fn batch_store_for_test(memory_quota: usize) -> Arc { + let tmp_dir = TempPath::new(); + let db = Arc::new(QuorumStoreDB::new(&tmp_dir)); + let (signers, _validator_verifier) = random_validator_verifier(4, None, false); + + Arc::new(BatchStore::new( + 10, // epoch + 10, // last committed round + db, + memory_quota, // memory_quota + 2001, // db quota + 2001, // batch quota + signers[0].clone(), + )) +} + +fn request_for_test( + digest: &HashValue, + round: u64, + num_bytes: u64, + maybe_payload: Option>, +) -> PersistedValue { + PersistedValue::new( + BatchInfo::new( + *TEST_REQUEST_ACCOUNT, // make sure all request come from the same account + BatchId::new_for_test(1), + 10, + round, + *digest, + 10, + num_bytes, + 0, + ), + maybe_payload, + ) +} + +#[test] +fn test_insert_expire() { + let batch_store = batch_store_for_test(30); + + let digest = HashValue::random(); + + assert_ok_eq!( + batch_store.insert_to_cache(&request_for_test(&digest, 15, 10, None)), + true + ); + assert_ok_eq!( + batch_store.insert_to_cache(&request_for_test(&digest, 30, 10, None)), + true + ); + assert_ok_eq!( + batch_store.insert_to_cache(&request_for_test(&digest, 25, 10, None)), + false + ); + let expired = batch_store.clear_expired_payload(27); + assert!(expired.is_empty()); + let expired = batch_store.clear_expired_payload(29); + assert!(expired.is_empty()); + assert_eq!(batch_store.clear_expired_payload(30), vec![digest]); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_extend_expiration_vs_save() { + let num_experiments = 2000; + let batch_store = batch_store_for_test(2001); + + let batch_store_clone1 = batch_store.clone(); + let batch_store_clone2 = batch_store.clone(); + + let digests: Vec = (0..num_experiments).map(|_| HashValue::random()).collect(); + let later_exp_values: Vec = (0..num_experiments) + .map(|i| { + // Pre-insert some of them. + if i % 2 == 0 { + assert_ok!(batch_store.save(&request_for_test( + &digests[i], + i as u64 + 30, + 1, + None + ))); + } + + request_for_test(&digests[i], i as u64 + 40, 1, None) + }) + .collect(); + + // Marshal threads to start at the same time. + let start_flag = Arc::new(AtomicUsize::new(0)); + let start_clone1 = start_flag.clone(); + let start_clone2 = start_flag.clone(); + + let save_error = Arc::new(AtomicBool::new(false)); + let save_error_clone1 = save_error.clone(); + let save_error_clone2 = save_error.clone(); + + // Thread that extends expiration by saving. + spawn_blocking(move || { + for (i, later_exp_value) in later_exp_values.into_iter().enumerate() { + // Wait until both threads are ready for next experiment. + loop { + let flag_val = start_clone1.load(Ordering::Acquire); + if flag_val == 3 * i + 1 || flag_val == 3 * i + 2 { + break; + } + } + + if batch_store_clone1.save(&later_exp_value).is_err() { + // Save in a separate flag and break so test doesn't hang. + save_error_clone1.store(true, Ordering::Release); + break; + } + start_clone1.fetch_add(1, Ordering::Relaxed); + } + }); + + // Thread that expires. + spawn_blocking(move || { + for i in 0..num_experiments { + // Wait until both threads are ready for next experiment. + loop { + let flag_val = start_clone2.load(Ordering::Acquire); + if flag_val == 3 * i + 1 + || flag_val == 3 * i + 2 + || save_error_clone2.load(Ordering::Acquire) + { + break; + } + } + + batch_store_clone2.update_certified_timestamp(i as u64 + 30); + start_clone2.fetch_add(1, Ordering::Relaxed); + } + }); + + for (i, &digest) in digests.iter().enumerate().take(num_experiments) { + // Set the conditions for experiment (both threads waiting). + while start_flag.load(Ordering::Acquire) % 3 != 0 { + assert!(!save_error.load(Ordering::Acquire)); + } + + if i % 2 == 1 { + assert_ok!(batch_store.save(&request_for_test(&digest, i as u64 + 30, 1, None))); + } + + // Unleash the threads. + start_flag.fetch_add(1, Ordering::Relaxed); + } + // Finish the experiment + while start_flag.load(Ordering::Acquire) % 3 != 0 {} + + // Expire everything, call for higher times as well. + for i in 35..50 { + batch_store.update_certified_timestamp((i + num_experiments) as u64); + } +} + +#[test] +fn test_quota_manager() { + let mut qm = QuotaManager::new(20, 10, 7); + assert_ok_eq!(qm.update_quota(5), StorageMode::MemoryAndPersisted); + assert_ok_eq!(qm.update_quota(3), StorageMode::MemoryAndPersisted); + assert_ok_eq!(qm.update_quota(2), StorageMode::MemoryAndPersisted); + assert_ok_eq!(qm.update_quota(1), StorageMode::PersistedOnly); + assert_ok_eq!(qm.update_quota(2), StorageMode::PersistedOnly); + assert_ok_eq!(qm.update_quota(7), StorageMode::PersistedOnly); + // 6 batches, fully used quotas + + // exceed storage quota. + assert_err!(qm.update_quota(2)); + + qm.free_quota(5, StorageMode::MemoryAndPersisted); + // 5 batches, available memory and db quota: 5 + + // exceed storage quota + assert_err!(qm.update_quota(6)); + assert_ok_eq!(qm.update_quota(3), StorageMode::MemoryAndPersisted); + + // exceed storage quota + assert_err!(qm.update_quota(3)); + assert_ok_eq!(qm.update_quota(1), StorageMode::MemoryAndPersisted); + // 7 batches, available memory and DB quota: 1 + + // Exceed batch quota + assert_err!(qm.update_quota(1)); + + qm.free_quota(1, StorageMode::PersistedOnly); + // 6 batches, available memory quota: 1, available DB quota: 2 + + // exceed storage quota + assert_err!(qm.update_quota(3)); + assert_ok_eq!(qm.update_quota(2), StorageMode::PersistedOnly); + // 7 batches, available memory quota: 1, available DB quota: 0 + + qm.free_quota(2, StorageMode::MemoryAndPersisted); + // 6 batches, available memory quota: 3, available DB quota: 2 + + // while there is available memory quota, DB quota isn't enough. + assert_err!(qm.update_quota(3)); + assert_ok_eq!(qm.update_quota(2), StorageMode::MemoryAndPersisted); +} + +#[test] +fn test_get_local_batch() { + let store = batch_store_for_test(30); + + let digest_1 = HashValue::random(); + let request_1 = request_for_test(&digest_1, 50, 20, Some(vec![])); + // Should be stored in memory and DB. + assert!(!store.persist(vec![request_1]).is_empty()); + + store.update_certified_timestamp(40); + + let digest_2 = HashValue::random(); + assert!(digest_2 != digest_1); + // Expiration is before 40. + let request_2_expired = request_for_test(&digest_2, 30, 20, Some(vec![])); + assert!(store.persist(vec![request_2_expired]).is_empty()); + // Proper (in the future) expiration. + let request_2 = request_for_test(&digest_2, 55, 20, Some(vec![])); + // Should be stored in DB only + assert!(!store.persist(vec![request_2]).is_empty()); + + let digest_3 = HashValue::random(); + assert!(digest_3 != digest_1); + assert!(digest_3 != digest_2); + let request_3 = request_for_test(&digest_3, 56, 1970, Some(vec![])); + // Out of quota - should not be stored + assert!(store.persist(vec![request_3.clone()]).is_empty()); + + assert_ok!(store.get_batch_from_local(&digest_1)); + assert_ok!(store.get_batch_from_local(&digest_2)); + store.update_certified_timestamp(51); + // Expired value w. digest_1. + assert_err!(store.get_batch_from_local(&digest_1)); + assert_ok!(store.get_batch_from_local(&digest_2)); + + // Value w. digest_3 was never persisted + assert_err!(store.get_batch_from_local(&digest_3)); + // Since payload is cleared, we can now persist value w. digest_3 + assert!(!store.persist(vec![request_3]).is_empty()); + assert_ok!(store.get_batch_from_local(&digest_3)); + + store.update_certified_timestamp(52); + assert_ok!(store.get_batch_from_local(&digest_2)); + assert_ok!(store.get_batch_from_local(&digest_3)); + + store.update_certified_timestamp(55); + // Expired value w. digest_2 + assert_err!(store.get_batch_from_local(&digest_2)); + assert_ok!(store.get_batch_from_local(&digest_3)); + + store.update_certified_timestamp(56); + // Expired value w. digest_3 + assert_err!(store.get_batch_from_local(&digest_1)); + assert_err!(store.get_batch_from_local(&digest_2)); + assert_err!(store.get_batch_from_local(&digest_3)); +} diff --git a/consensus/src/quorum_store/tests/mod.rs b/consensus/src/quorum_store/tests/mod.rs new file mode 100644 index 0000000000000..9dfc3e2930c59 --- /dev/null +++ b/consensus/src/quorum_store/tests/mod.rs @@ -0,0 +1,12 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod batch_generator_test; +mod batch_requester_test; +mod batch_store_test; +mod direct_mempool_quorum_store_test; +mod proof_coordinator_test; +mod proof_manager_test; +mod quorum_store_db_test; +mod types_test; +mod utils; diff --git a/consensus/src/quorum_store/tests/quorum_store_db_test.rs b/consensus/src/quorum_store/tests/quorum_store_db_test.rs new file mode 100644 index 0000000000000..2bdc060abc4a8 --- /dev/null +++ b/consensus/src/quorum_store/tests/quorum_store_db_test.rs @@ -0,0 +1,86 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + quorum_store::{ + quorum_store_db::{QuorumStoreDB, QuorumStoreStorage}, + types::{Batch, PersistedValue}, + }, + test_utils::create_vec_signed_transactions, +}; +use aptos_consensus_types::proof_of_store::BatchId; +use aptos_temppath::TempPath; +use aptos_types::account_address::AccountAddress; +use claims::assert_ok; + +#[test] +fn test_db_for_data() { + let tmp_dir = TempPath::new(); + let db = QuorumStoreDB::new(&tmp_dir); + + let source = AccountAddress::random(); + let signed_txns = create_vec_signed_transactions(100); + let persist_request_1: PersistedValue = + Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); + let clone_1 = persist_request_1.clone(); + assert!(db.save_batch(clone_1).is_ok()); + + assert_eq!( + db.get_batch(persist_request_1.digest()) + .expect("could not read from db") + .unwrap(), + persist_request_1 + ); + + let signed_txns = create_vec_signed_transactions(200); + let persist_request_2: PersistedValue = + Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); + let clone_2 = persist_request_2.clone(); + assert_ok!(db.save_batch(clone_2)); + + let signed_txns = create_vec_signed_transactions(300); + let persist_request_3: PersistedValue = + Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); + let clone_3 = persist_request_3.clone(); + assert_ok!(db.save_batch(clone_3)); + + let batches = vec![*persist_request_3.digest()]; + assert_ok!(db.delete_batches(batches)); + assert_eq!( + db.get_batch(persist_request_3.digest()) + .expect("could not read from db"), + None + ); + + let all_batches = db.get_all_batches().expect("could not read from db"); + assert_eq!(all_batches.len(), 2); + assert!(all_batches.contains_key(persist_request_1.digest())); + assert!(all_batches.contains_key(persist_request_2.digest())); +} + +#[test] +fn test_db_for_batch_id() { + let tmp_dir = TempPath::new(); + let db = QuorumStoreDB::new(&tmp_dir); + + assert!(db + .clean_and_get_batch_id(0) + .expect("could not read from db") + .is_none()); + assert_ok!(db.save_batch_id(0, BatchId::new_for_test(0))); + assert_ok!(db.save_batch_id(0, BatchId::new_for_test(4))); + assert_eq!( + db.clean_and_get_batch_id(0) + .expect("could not read from db") + .unwrap(), + BatchId::new_for_test(4) + ); + assert_ok!(db.save_batch_id(1, BatchId::new_for_test(1))); + assert_ok!(db.save_batch_id(2, BatchId::new_for_test(2))); + assert_eq!( + db.clean_and_get_batch_id(2) + .expect("could not read from db") + .unwrap(), + BatchId::new_for_test(2) + ); +} diff --git a/consensus/src/quorum_store/tests/types_test.rs b/consensus/src/quorum_store/tests/types_test.rs new file mode 100644 index 0000000000000..81bafe86959fd --- /dev/null +++ b/consensus/src/quorum_store/tests/types_test.rs @@ -0,0 +1,42 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + quorum_store::types::{Batch, BatchRequest}, + test_utils::create_vec_signed_transactions, +}; +use aptos_consensus_types::{common::BatchPayload, proof_of_store::BatchId}; +use aptos_crypto::{hash::CryptoHash, HashValue}; +use aptos_types::account_address::AccountAddress; +use claims::{assert_err, assert_ok}; + +#[test] +fn test_batch() { + let epoch = 0; + let source = AccountAddress::random(); + let signed_txns = create_vec_signed_transactions(500); + + let payload = BatchPayload::new(source, signed_txns.clone()); + let digest = payload.hash(); + + let batch_request = BatchRequest::new(source, epoch, digest); + + assert_eq!(epoch, batch_request.epoch()); + assert!(batch_request.verify(source).is_ok()); + + let batch = Batch::new( + BatchId::new_for_test(1), + signed_txns.clone(), + epoch, + 1, + source, + 0, + ); + + assert_ok!(batch.verify()); + assert_ok!(batch.verify_with_digest(digest)); + // verify should fail if the digest does not match. + assert_err!(batch.verify_with_digest(HashValue::random())); + + assert_eq!(batch.into_transactions(), signed_txns); +} diff --git a/consensus/src/quorum_store/utils.rs b/consensus/src/quorum_store/utils.rs index 433dd9846d89f..23a48a7c78729 100644 --- a/consensus/src/quorum_store/utils.rs +++ b/consensus/src/quorum_store/utils.rs @@ -460,6 +460,9 @@ impl ProofQueue { byte_size = cur_bytes, block_total_txns = cur_all_txns, block_unique_txns = cur_unique_txns, + max_txns = max_txns, + max_unique_txns = max_unique_txns, + max_bytes = max_bytes, batch_count = ret.len(), full = full, return_non_full = return_non_full, diff --git a/consensus/src/rand/dkg/mod.rs b/consensus/src/rand/dkg/mod.rs new file mode 100644 index 0000000000000..7f1658608ae14 --- /dev/null +++ b/consensus/src/rand/dkg/mod.rs @@ -0,0 +1,2 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 diff --git a/consensus/src/rand/mod.rs b/consensus/src/rand/mod.rs new file mode 100644 index 0000000000000..6762a2f2b3a7a --- /dev/null +++ b/consensus/src/rand/mod.rs @@ -0,0 +1,7 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![allow(dead_code)] + +pub mod dkg; +pub mod rand_gen; diff --git a/consensus/src/rand/rand_gen/mod.rs b/consensus/src/rand/rand_gen/mod.rs new file mode 100644 index 0000000000000..0127c320dc217 --- /dev/null +++ b/consensus/src/rand/rand_gen/mod.rs @@ -0,0 +1,15 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(test)] +mod test_utils; + +pub mod block_queue; +pub mod network_messages; +pub mod rand_store; +pub mod types; + +pub mod aug_data_store; +pub mod rand_manager; +pub mod reliable_broadcast_state; +pub mod storage; diff --git a/consensus/src/rand/rand_gen/storage/in_memory.rs b/consensus/src/rand/rand_gen/storage/in_memory.rs new file mode 100644 index 0000000000000..cf5046f5d1a53 --- /dev/null +++ b/consensus/src/rand/rand_gen/storage/in_memory.rs @@ -0,0 +1,78 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::rand::rand_gen::{ + storage::interface::RandStorage, + types::{AugData, AugDataId, CertifiedAugData, TAugmentedData}, +}; +use aptos_infallible::RwLock; +use std::collections::HashMap; + +pub struct InMemRandDb { + key_pair: RwLock)>>, + aug_data: RwLock>>, + certified_aug_data: RwLock>>, +} + +impl InMemRandDb { + pub fn new() -> Self { + Self { + key_pair: RwLock::new(None), + aug_data: RwLock::new(HashMap::new()), + certified_aug_data: RwLock::new(HashMap::new()), + } + } +} + +impl RandStorage for InMemRandDb { + fn save_key_pair_bytes(&self, epoch: u64, key_pair: Vec) -> anyhow::Result<()> { + self.key_pair.write().replace((epoch, key_pair)); + Ok(()) + } + + fn save_aug_data(&self, aug_data: &AugData) -> anyhow::Result<()> { + self.aug_data + .write() + .insert(aug_data.id(), aug_data.clone()); + Ok(()) + } + + fn save_certified_aug_data( + &self, + certified_aug_data: &CertifiedAugData, + ) -> anyhow::Result<()> { + self.certified_aug_data + .write() + .insert(certified_aug_data.id(), certified_aug_data.clone()); + Ok(()) + } + + fn get_key_pair_bytes(&self) -> anyhow::Result)>> { + Ok(self.key_pair.read().clone()) + } + + fn get_all_aug_data(&self) -> anyhow::Result)>> { + Ok(self.aug_data.read().clone().into_iter().collect()) + } + + fn get_all_certified_aug_data(&self) -> anyhow::Result)>> { + Ok(self.certified_aug_data.read().clone().into_iter().collect()) + } + + fn remove_aug_data(&self, aug_data: Vec>) -> anyhow::Result<()> { + for data in aug_data { + self.aug_data.write().remove(&data.id()); + } + Ok(()) + } + + fn remove_certified_aug_data( + &self, + certified_aug_data: Vec>, + ) -> anyhow::Result<()> { + for data in certified_aug_data { + self.certified_aug_data.write().remove(&data.id()); + } + Ok(()) + } +} diff --git a/consensus/src/rand/rand_gen/storage/interface.rs b/consensus/src/rand/rand_gen/storage/interface.rs new file mode 100644 index 0000000000000..80a391f78285e --- /dev/null +++ b/consensus/src/rand/rand_gen/storage/interface.rs @@ -0,0 +1,23 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::rand::rand_gen::types::{AugData, AugDataId, CertifiedAugData}; + +pub trait RandStorage: Send + Sync + 'static { + fn save_key_pair_bytes(&self, epoch: u64, key_pair: Vec) -> anyhow::Result<()>; + fn save_aug_data(&self, aug_data: &AugData) -> anyhow::Result<()>; + fn save_certified_aug_data( + &self, + certified_aug_data: &CertifiedAugData, + ) -> anyhow::Result<()>; + + fn get_key_pair_bytes(&self) -> anyhow::Result)>>; + fn get_all_aug_data(&self) -> anyhow::Result)>>; + fn get_all_certified_aug_data(&self) -> anyhow::Result)>>; + + fn remove_aug_data(&self, aug_data: Vec>) -> anyhow::Result<()>; + fn remove_certified_aug_data( + &self, + certified_aug_data: Vec>, + ) -> anyhow::Result<()>; +} diff --git a/consensus/src/rand/rand_gen/storage/mod.rs b/consensus/src/rand/rand_gen/storage/mod.rs new file mode 100644 index 0000000000000..856d4ab816045 --- /dev/null +++ b/consensus/src/rand/rand_gen/storage/mod.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +pub mod db; +pub mod in_memory; +pub mod interface; +mod schema; diff --git a/consensus/src/rand/rand_gen/storage/schema.rs b/consensus/src/rand/rand_gen/storage/schema.rs new file mode 100644 index 0000000000000..37b7c5b2a7e0e --- /dev/null +++ b/consensus/src/rand/rand_gen/storage/schema.rs @@ -0,0 +1,96 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::rand::rand_gen::types::{AugData, AugDataId, CertifiedAugData, TAugmentedData}; +use aptos_schemadb::{ + define_schema, + schema::{KeyCodec, Schema, ValueCodec}, + ColumnFamilyName, +}; +use std::marker::PhantomData; + +pub(crate) const KEY_PAIR_CF_NAME: ColumnFamilyName = "key_pair"; + +define_schema!(KeyPairSchema, (), (u64, Vec), KEY_PAIR_CF_NAME); + +impl KeyCodec for () { + fn encode_key(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(self)?) + } + + fn decode_key(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} + +impl ValueCodec for (u64, Vec) { + fn encode_value(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(self)?) + } + + fn decode_value(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} + +pub(crate) const AUG_DATA_CF_NAME: ColumnFamilyName = "aug_data"; +#[derive(Debug)] +pub struct AugDataSchema(PhantomData); + +impl Schema for AugDataSchema { + type Key = AugDataId; + type Value = AugData; + + const COLUMN_FAMILY_NAME: ColumnFamilyName = AUG_DATA_CF_NAME; +} + +impl KeyCodec> for AugDataId { + fn encode_key(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(self)?) + } + + fn decode_key(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} + +impl ValueCodec> for AugData { + fn encode_value(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_value(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} + +pub(crate) const CERTIFIED_AUG_DATA_CF_NAME: ColumnFamilyName = "certified_aug_data"; +#[derive(Debug)] +pub struct CertifiedAugDataSchema(PhantomData); + +impl Schema for CertifiedAugDataSchema { + type Key = AugDataId; + type Value = CertifiedAugData; + + const COLUMN_FAMILY_NAME: ColumnFamilyName = CERTIFIED_AUG_DATA_CF_NAME; +} + +impl KeyCodec> for AugDataId { + fn encode_key(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(self)?) + } + + fn decode_key(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} + +impl ValueCodec> for CertifiedAugData { + fn encode_value(&self) -> anyhow::Result> { + Ok(bcs::to_bytes(&self)?) + } + + fn decode_value(data: &[u8]) -> anyhow::Result { + Ok(bcs::from_bytes(data)?) + } +} diff --git a/consensus/src/state_replication.rs b/consensus/src/state_replication.rs new file mode 100644 index 0000000000000..26da5fa80d163 --- /dev/null +++ b/consensus/src/state_replication.rs @@ -0,0 +1,84 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + error::StateSyncError, + payload_manager::PayloadManager, + state_computer::{PipelineExecutionResult, StateComputeResultFut}, + transaction_deduper::TransactionDeduper, + transaction_shuffler::TransactionShuffler, +}; +use anyhow::Result; +use aptos_consensus_types::{block::Block, pipelined_block::PipelinedBlock}; +use aptos_crypto::HashValue; +use aptos_executor_types::ExecutorResult; +use aptos_types::{ + block_executor::config::BlockExecutorConfigFromOnchain, epoch_state::EpochState, + ledger_info::LedgerInfoWithSignatures, randomness::Randomness, +}; +use std::sync::Arc; + +pub type StateComputerCommitCallBackType = + Box], LedgerInfoWithSignatures) + Send + Sync>; + +/// While Consensus is managing proposed blocks, `StateComputer` is managing the results of the +/// (speculative) execution of their payload. +/// StateComputer is using proposed block ids for identifying the transactions. +#[async_trait::async_trait] +pub trait StateComputer: Send + Sync { + /// How to execute a sequence of transactions and obtain the next state. While some of the + /// transactions succeed, some of them can fail. + /// In case all the transactions are failed, new_state_id is equal to the previous state id. + async fn compute( + &self, + // The block that will be computed. + block: &Block, + // The parent block root hash. + parent_block_id: HashValue, + randomness: Option, + ) -> ExecutorResult { + self.schedule_compute(block, parent_block_id, randomness) + .await + .await + } + + async fn schedule_compute( + &self, + // The block that will be computed. + _block: &Block, + // The parent block root hash. + _parent_block_id: HashValue, + _randomness: Option, + ) -> StateComputeResultFut { + unimplemented!("This state computer does not support scheduling"); + } + + /// Send a successful commit. A future is fulfilled when the state is finalized. + async fn commit( + &self, + blocks: &[Arc], + finality_proof: LedgerInfoWithSignatures, + callback: StateComputerCommitCallBackType, + ) -> ExecutorResult<()>; + + /// Best effort state synchronization to the given target LedgerInfo. + /// In case of success (`Result::Ok`) the LI of storage is at the given target. + /// In case of failure (`Result::Error`) the LI of storage remains unchanged, and the validator + /// can assume there were no modifications to the storage made. + async fn sync_to(&self, target: LedgerInfoWithSignatures) -> Result<(), StateSyncError>; + + // Reconfigure to execute transactions for a new epoch. + fn new_epoch( + &self, + epoch_state: &EpochState, + payload_manager: Arc, + transaction_shuffler: Arc, + block_executor_onchain_config: BlockExecutorConfigFromOnchain, + transaction_deduper: Arc, + randomness_enabled: bool, + ); + + // Reconfigure to clear epoch state at end of epoch. + fn end_epoch(&self); +} diff --git a/consensus/src/test_utils/mock_quorum_store_sender.rs b/consensus/src/test_utils/mock_quorum_store_sender.rs new file mode 100644 index 0000000000000..affff1103b49e --- /dev/null +++ b/consensus/src/test_utils/mock_quorum_store_sender.rs @@ -0,0 +1,85 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + network::QuorumStoreSender, + network_interface::ConsensusMsg, + quorum_store::types::{Batch, BatchRequest, BatchResponse}, +}; +use aptos_consensus_types::{ + common::Author, + proof_of_store::{ProofOfStore, ProofOfStoreMsg, SignedBatchInfo, SignedBatchInfoMsg}, +}; +use std::time::Duration; +use tokio::sync::mpsc::Sender; + +#[derive(Clone)] +pub struct MockQuorumStoreSender { + tx: Sender<(ConsensusMsg, Vec)>, +} + +impl MockQuorumStoreSender { + pub fn new(tx: Sender<(ConsensusMsg, Vec)>) -> Self { + Self { tx } + } +} + +#[async_trait::async_trait] +impl QuorumStoreSender for MockQuorumStoreSender { + async fn send_batch_request(&self, request: BatchRequest, recipients: Vec) { + self.tx + .send((ConsensusMsg::BatchRequestMsg(Box::new(request)), recipients)) + .await + .expect("could not send"); + } + + async fn request_batch( + &self, + _request: BatchRequest, + _recipient: Author, + _timeout: Duration, + ) -> anyhow::Result { + unimplemented!(); + } + + async fn send_batch(&self, batch: Batch, recipients: Vec) { + self.tx + .send((ConsensusMsg::BatchResponse(Box::new(batch)), recipients)) + .await + .expect("could not send"); + } + + async fn send_signed_batch_info_msg( + &self, + signed_batch_infos: Vec, + recipients: Vec, + ) { + self.tx + .send(( + ConsensusMsg::SignedBatchInfo(Box::new(SignedBatchInfoMsg::new( + signed_batch_infos, + ))), + recipients, + )) + .await + .expect("could not send"); + } + + async fn broadcast_batch_msg(&mut self, _batches: Vec) { + unimplemented!() + } + + async fn broadcast_proof_of_store_msg(&mut self, proof_of_stores: Vec) { + self.tx + .send(( + ConsensusMsg::ProofOfStoreMsg(Box::new(ProofOfStoreMsg::new(proof_of_stores))), + vec![], + )) + .await + .unwrap(); + } + + async fn send_proof_of_store_msg_to_self(&mut self, _proof_of_stores: Vec) { + unimplemented!() + } +} diff --git a/consensus/src/test_utils/mock_state_computer.rs b/consensus/src/test_utils/mock_state_computer.rs new file mode 100644 index 0000000000000..aeef2a0ef6e97 --- /dev/null +++ b/consensus/src/test_utils/mock_state_computer.rs @@ -0,0 +1,161 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + error::StateSyncError, + payload_manager::PayloadManager, + pipeline::buffer_manager::OrderedBlocks, + state_computer::{PipelineExecutionResult, StateComputeResultFut}, + state_replication::{StateComputer, StateComputerCommitCallBackType}, + transaction_deduper::TransactionDeduper, + transaction_shuffler::TransactionShuffler, +}; +use anyhow::Result; +use aptos_consensus_types::{block::Block, pipelined_block::PipelinedBlock}; +use aptos_crypto::HashValue; +use aptos_executor_types::{ExecutorError, ExecutorResult, StateComputeResult}; +use aptos_logger::debug; +use aptos_types::{ + block_executor::config::BlockExecutorConfigFromOnchain, epoch_state::EpochState, + ledger_info::LedgerInfoWithSignatures, randomness::Randomness, +}; +use futures::SinkExt; +use futures_channel::mpsc::UnboundedSender; +use std::sync::Arc; + +pub struct EmptyStateComputer { + executor_channel: UnboundedSender, +} + +impl EmptyStateComputer { + pub fn new(executor_channel: UnboundedSender) -> Self { + Self { executor_channel } + } +} + +#[async_trait::async_trait] +impl StateComputer for EmptyStateComputer { + async fn compute( + &self, + _block: &Block, + _parent_block_id: HashValue, + _randomness: Option, + ) -> ExecutorResult { + Ok(PipelineExecutionResult::new( + vec![], + StateComputeResult::new_dummy(), + )) + } + + async fn commit( + &self, + blocks: &[Arc], + commit: LedgerInfoWithSignatures, + call_back: StateComputerCommitCallBackType, + ) -> ExecutorResult<()> { + assert!(!blocks.is_empty()); + + if self + .executor_channel + .clone() + .send(OrderedBlocks { + ordered_blocks: blocks + .iter() + .map(|b| (**b).clone()) + .collect::>(), + ordered_proof: commit, + callback: call_back, + }) + .await + .is_err() + { + debug!("Failed to send to buffer manager, maybe epoch ends"); + } + + Ok(()) + } + + async fn sync_to(&self, _commit: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { + Ok(()) + } + + fn new_epoch( + &self, + _: &EpochState, + _: Arc, + _: Arc, + _: BlockExecutorConfigFromOnchain, + _: Arc, + _: bool, + ) { + } + + fn end_epoch(&self) {} +} + +/// Random Compute Result State Computer +/// When compute(), if parent id is random_compute_result_root_hash, it returns Err(Error::BlockNotFound(parent_block_id)) +/// Otherwise, it returns a dummy StateComputeResult with root hash as random_compute_result_root_hash. +pub struct RandomComputeResultStateComputer { + random_compute_result_root_hash: HashValue, +} + +impl RandomComputeResultStateComputer { + pub fn new() -> Self { + Self { + random_compute_result_root_hash: HashValue::random(), + } + } + + pub fn get_root_hash(&self) -> HashValue { + self.random_compute_result_root_hash + } +} + +#[async_trait::async_trait] +impl StateComputer for RandomComputeResultStateComputer { + async fn schedule_compute( + &self, + _block: &Block, + parent_block_id: HashValue, + _randomness: Option, + ) -> StateComputeResultFut { + // trapdoor for Execution Error + let res = if parent_block_id == self.random_compute_result_root_hash { + Err(ExecutorError::BlockNotFound(parent_block_id)) + } else { + Ok(StateComputeResult::new_dummy_with_root_hash( + self.random_compute_result_root_hash, + )) + }; + let pipeline_execution_res = res.map(|res| PipelineExecutionResult::new(vec![], res)); + Box::pin(async move { pipeline_execution_res }) + } + + async fn commit( + &self, + _blocks: &[Arc], + _commit: LedgerInfoWithSignatures, + _call_back: StateComputerCommitCallBackType, + ) -> ExecutorResult<()> { + Ok(()) + } + + async fn sync_to(&self, _commit: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { + Ok(()) + } + + fn new_epoch( + &self, + _: &EpochState, + _: Arc, + _: Arc, + _: BlockExecutorConfigFromOnchain, + _: Arc, + _: bool, + ) { + } + + fn end_epoch(&self) {} +} diff --git a/consensus/src/transaction_deduper.rs b/consensus/src/transaction_deduper.rs new file mode 100644 index 0000000000000..dc1cff4c6376e --- /dev/null +++ b/consensus/src/transaction_deduper.rs @@ -0,0 +1,33 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::txn_hash_and_authenticator_deduper::TxnHashAndAuthenticatorDeduper; +use aptos_logger::info; +use aptos_types::{on_chain_config::TransactionDeduperType, transaction::SignedTransaction}; +use std::sync::Arc; + +/// Interface to dedup transactions. The dedup filters duplicate transactions within a block. +pub trait TransactionDeduper: Send + Sync { + fn dedup(&self, txns: Vec) -> Vec; +} + +/// No Op Deduper to maintain backward compatibility +pub struct NoOpDeduper {} + +impl TransactionDeduper for NoOpDeduper { + fn dedup(&self, txns: Vec) -> Vec { + txns + } +} + +pub fn create_transaction_deduper( + deduper_type: TransactionDeduperType, +) -> Arc { + match deduper_type { + TransactionDeduperType::NoDedup => Arc::new(NoOpDeduper {}), + TransactionDeduperType::TxnHashAndAuthenticatorV1 => { + info!("Using simple hash set transaction deduper"); + Arc::new(TxnHashAndAuthenticatorDeduper::new()) + }, + } +} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs new file mode 100644 index 0000000000000..140191b1d523f --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs @@ -0,0 +1,45 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; +use aptos_types::transaction::{SignedTransaction, TransactionPayload}; +use move_core_types::{identifier::Identifier, language_storage::ModuleId}; + +#[derive(Eq, Hash, PartialEq)] +pub enum EntryFunKey { + EntryFun { + module: ModuleId, + function: Identifier, + }, + Exempt, +} + +impl ConflictKey for EntryFunKey { + fn extract_from(txn: &SignedTransaction) -> Self { + match txn.payload() { + TransactionPayload::EntryFunction(entry_fun) => { + let module_id = entry_fun.module(); + if module_id.address().is_special() { + // Exempt framework modules + Self::Exempt + } else { + // n.b. Generics ignored. + Self::EntryFun { + module: module_id.clone(), + function: entry_fun.function().to_owned(), + } + } + }, + TransactionPayload::Multisig(_) + | TransactionPayload::Script(_) + | TransactionPayload::ModuleBundle(_) => Self::Exempt, + } + } + + fn conflict_exempt(&self) -> bool { + match self { + Self::Exempt => true, + Self::EntryFun { .. } => false, + } + } +} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs new file mode 100644 index 0000000000000..56979f98d6d29 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs @@ -0,0 +1,39 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; +use aptos_types::transaction::{SignedTransaction, TransactionPayload}; +use move_core_types::language_storage::ModuleId; + +#[derive(Eq, Hash, PartialEq)] +pub enum EntryFunModuleKey { + Module(ModuleId), + AnyScriptOrMultiSig, + Exempt, +} + +impl ConflictKey for EntryFunModuleKey { + fn extract_from(txn: &SignedTransaction) -> Self { + match txn.payload() { + TransactionPayload::EntryFunction(entry_fun) => { + let module_id = entry_fun.module(); + + if module_id.address().is_special() { + Self::Exempt + } else { + Self::Module(module_id.clone()) + } + }, + TransactionPayload::Multisig(..) + | TransactionPayload::Script(_) + | TransactionPayload::ModuleBundle(_) => Self::AnyScriptOrMultiSig, + } + } + + fn conflict_exempt(&self) -> bool { + match self { + Self::Exempt => true, + Self::Module(..) | Self::AnyScriptOrMultiSig => false, + } + } +} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs new file mode 100644 index 0000000000000..f9b9e9e72fd22 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs @@ -0,0 +1,190 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::conflict_key::{ + ConflictKey, ConflictKeyId, ConflictKeyRegistry, +}; +use proptest::prelude::*; +use std::hash::Hash; + +impl ConflictKeyId { + pub fn new_for_test(idx: usize) -> Self { + Self(idx) + } +} + +impl ConflictKeyRegistry { + pub fn all_exempt(num_txns: usize) -> Self { + ConflictKeyRegistry { + id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], + is_exempt_by_id: vec![true], + } + } + + pub fn non_conflict(num_txns: usize) -> Self { + ConflictKeyRegistry { + id_by_txn: (0..num_txns).map(ConflictKeyId::new_for_test).collect(), + is_exempt_by_id: vec![false; num_txns], + } + } + + pub fn full_conflict(num_txns: usize) -> Self { + ConflictKeyRegistry { + id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], + is_exempt_by_id: vec![false], + } + } + + pub fn nums_per_key(nums_per_key: [usize; NUM_KEYS]) -> Self { + Self::nums_per_round_per_key([nums_per_key]) + } + + pub fn nums_per_round_per_key( + nums_per_round_per_key: [[usize; NUM_KEYS]; NUM_ROUNDS], + ) -> Self { + let mut seq = (0..NUM_ROUNDS).flat_map(|_| 0..NUM_KEYS); + let nums_per_key = nums_per_round_per_key.into_iter().flatten(); + + ConflictKeyRegistry { + id_by_txn: nums_per_key + .flat_map(|num| { + let s = seq.next().unwrap(); + vec![ConflictKeyId::new_for_test(s); num] + }) + .collect(), + is_exempt_by_id: vec![false; NUM_KEYS], + } + } +} + +#[derive(Debug)] +struct FakeAccount { + id: usize, +} + +impl Arbitrary for FakeAccount { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (0..10usize).prop_map(|id| FakeAccount { id }).boxed() + } +} + +#[derive(Debug)] +struct FakeModule { + id: usize, +} + +impl FakeModule { + pub fn exempt(&self) -> bool { + self.id % 3 == 0 + } +} + +impl Arbitrary for FakeModule { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (0..10usize).prop_map(|id| FakeModule { id }).boxed() + } +} + +#[derive(Debug)] +struct FakeEntryFun { + module: FakeModule, + id: usize, +} + +impl Arbitrary for FakeEntryFun { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (any::(), 0..3usize) + .prop_map(|(module, id)| FakeEntryFun { module, id }) + .boxed() + } +} + +#[derive(Debug)] +pub struct FakeTxn { + sender: FakeAccount, + entry_fun: FakeEntryFun, +} + +impl Arbitrary for FakeTxn { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (any::(), any::()) + .prop_map(|(sender, entry_fun)| FakeTxn { sender, entry_fun }) + .boxed() + } +} + +#[derive(Eq, Hash, PartialEq)] +pub(crate) struct FakeSenderKey { + id: usize, +} + +impl ConflictKey for FakeSenderKey { + fn extract_from(txn: &FakeTxn) -> Self { + Self { id: txn.sender.id } + } + + fn conflict_exempt(&self) -> bool { + false + } +} + +#[derive(Eq, Hash, PartialEq)] +pub(crate) enum FakeEntryFunModuleKey { + Module(usize), + Exempt, +} + +impl ConflictKey for FakeEntryFunModuleKey { + fn extract_from(txn: &FakeTxn) -> Self { + if txn.entry_fun.module.exempt() { + Self::Exempt + } else { + Self::Module(txn.entry_fun.module.id) + } + } + + fn conflict_exempt(&self) -> bool { + match self { + Self::Exempt => true, + Self::Module(..) => false, + } + } +} + +#[derive(Eq, Hash, PartialEq)] +pub(crate) enum FakeEntryFunKey { + EntryFun { module: usize, function: usize }, + Exempt, +} + +impl ConflictKey for FakeEntryFunKey { + fn extract_from(txn: &FakeTxn) -> Self { + if txn.entry_fun.module.exempt() { + Self::Exempt + } else { + Self::EntryFun { + module: txn.entry_fun.module.id, + function: txn.entry_fun.id, + } + } + } + + fn conflict_exempt(&self) -> bool { + match self { + Self::Exempt => true, + Self::EntryFun { .. } => false, + } + } +} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs new file mode 100644 index 0000000000000..3ec1905d869cb --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs @@ -0,0 +1,19 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; +use aptos_types::transaction::SignedTransaction; +use move_core_types::account_address::AccountAddress; + +#[derive(Eq, Hash, PartialEq)] +pub struct TxnSenderKey(AccountAddress); + +impl ConflictKey for TxnSenderKey { + fn extract_from(txn: &SignedTransaction) -> Self { + TxnSenderKey(txn.sender()) + } + + fn conflict_exempt(&self) -> bool { + false + } +} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_zone.rs b/consensus/src/transaction_shuffler/fairness/conflict_zone.rs new file mode 100644 index 0000000000000..983baaadedfb9 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/conflict_zone.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::{ + conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, + TxnIdx, +}; +use std::collections::VecDeque; + +/// A sliding window of transactions (TxnIds), represented by `ConflictKeyId`s extracted from a +/// specific `ConflictKey`, managed by a specific `ConflictKeyRegistry`. +#[derive(Debug)] +pub(crate) struct ConflictZone<'a> { + sliding_window_size: usize, + sliding_window: VecDeque, + /// Number of transactions in the sliding window for each key_id. `ConflictZone::is_conflict(key)` + /// returns true is the count for `key` is greater than 0, unless the key is exempt from conflict. + counts_by_id: MapByKeyId, + key_registry: &'a ConflictKeyRegistry, +} + +impl<'a> ConflictZone<'a> { + pub fn build_zones( + key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], + window_sizes: [usize; NUM_CONFLICT_ZONES], + ) -> [Self; NUM_CONFLICT_ZONES] { + itertools::zip_eq(key_registries.iter(), window_sizes) + .map(|(registry, window_size)| Self::new(registry, window_size)) + .collect::>() + .try_into() + .expect("key_registries and window_sizes must have the same length.") + } + + fn new(key_registry: &'a ConflictKeyRegistry, sliding_window_size: usize) -> Self { + Self { + sliding_window_size, + sliding_window: VecDeque::with_capacity(sliding_window_size + 1), + counts_by_id: key_registry.new_map_by_id(), + key_registry, + } + } + + pub fn is_conflict(&self, txn_idx: TxnIdx) -> bool { + let key_id = self.key_registry.key_id_for_txn(txn_idx); + if self.key_registry.is_conflict_exempt(key_id) { + false + } else { + *self.counts_by_id.get(key_id) > 0 + } + } + + /// Append a new transaction to the sliding window and + /// return the key_id that's no longer in conflict as a result if there is one. + pub fn add(&mut self, txn_idx: TxnIdx) -> Option { + let key_id = self.key_registry.key_id_for_txn(txn_idx); + + *self.counts_by_id.get_mut(key_id) += 1; + self.sliding_window.push_back(key_id); + if self.sliding_window.len() > self.sliding_window_size { + let removed_key_id = self.sliding_window.pop_front().unwrap(); + let count = self.counts_by_id.get_mut(removed_key_id); + *count -= 1; + if *count == 0 && !self.key_registry.is_conflict_exempt(removed_key_id) { + return Some(removed_key_id); + } + } + None + } +} diff --git a/consensus/src/transaction_shuffler/fairness/mod.rs b/consensus/src/transaction_shuffler/fairness/mod.rs new file mode 100644 index 0000000000000..fef6a42a67306 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/mod.rs @@ -0,0 +1,220 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::{ + fairness::{ + conflict_key::{ + entry_fun::EntryFunKey, entry_fun_module::EntryFunModuleKey, txn_sender::TxnSenderKey, + ConflictKeyRegistry, + }, + conflict_zone::ConflictZone, + pending_zone::PendingZone, + }, + TransactionShuffler, +}; +use aptos_types::transaction::SignedTransaction; +use itertools::zip_eq; +use selection_tracker::SelectionTracker; +use std::collections::BTreeSet; + +pub(crate) mod conflict_key; +mod conflict_zone; +mod pending_zone; +mod selection_tracker; + +#[cfg(test)] +mod tests; + +type TxnIdx = usize; + +#[derive(Debug)] +pub struct FairnessShuffler { + pub sender_conflict_window_size: usize, + pub module_conflict_window_size: usize, + pub entry_fun_conflict_window_size: usize, +} + +impl FairnessShuffler { + fn conflict_key_registries(txns: &[SignedTransaction]) -> [ConflictKeyRegistry; 3] { + [ + ConflictKeyRegistry::build::(txns), + ConflictKeyRegistry::build::(txns), + ConflictKeyRegistry::build::(txns), + ] + } + + fn window_sizes(&self) -> [usize; 3] { + [ + self.sender_conflict_window_size, + self.module_conflict_window_size, + self.entry_fun_conflict_window_size, + ] + } +} + +impl TransactionShuffler for FairnessShuffler { + fn shuffle(&self, txns: Vec) -> Vec { + let conflict_key_registries = Self::conflict_key_registries(&txns); + let order = + FairnessShufflerImpl::new(&conflict_key_registries, self.window_sizes()).shuffle(); + reorder(txns, &order) + } +} + +fn reorder(txns: Vec, order: &[TxnIdx]) -> Vec { + assert_eq!(txns.len(), order.len()); + order.iter().map(|idx| txns[*idx].clone()).collect() +} + +struct FairnessShufflerImpl<'a, const NUM_CONFLICT_ZONES: usize> { + conflict_zones: [ConflictZone<'a>; NUM_CONFLICT_ZONES], + pending_zones: [PendingZone<'a>; NUM_CONFLICT_ZONES], + selected_order: Vec, + selection_tracker: SelectionTracker, +} + +impl<'a, const NUM_CONFLICT_ZONES: usize> FairnessShufflerImpl<'a, NUM_CONFLICT_ZONES> { + pub fn new( + conflict_key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], + window_sizes: [usize; NUM_CONFLICT_ZONES], + ) -> Self { + let num_txns = conflict_key_registries[0].num_txns(); + assert!(conflict_key_registries + .iter() + .skip(1) + .all(|r| r.num_txns() == num_txns)); + + Self { + selected_order: Vec::with_capacity(num_txns), + selection_tracker: SelectionTracker::new(num_txns), + conflict_zones: ConflictZone::build_zones(conflict_key_registries, window_sizes), + pending_zones: PendingZone::build_zones(conflict_key_registries), + } + } + + /// Spread (delay) transactions that have conflicts with adjacent previous transactions + /// according to multiple dimensions (`ConflictKey`s). Invariant is held that for each conflict + /// key, i.e. the transaction sender, the module, and the entry function, etc., the order of + /// transactions with the same key is preserved -- unless the key is exempt from conflict. + /// + /// For example, all transactions from a single sender will preserve their order; all transactions + /// from the same module will preserve their order, unless they are of the aptos framework + /// module -- p2p transfers of APT can violate this invariant. + /// + /// Each transaction comes at most once out of `self.selection_tracker.next_unselected()` for + /// both passes, that's O(2n). And each transaction comes out of each conflict zones at most + /// once, that's O(3n). In either case, the transaction is examined by going through all 3 + /// conflict zones and all 3 pending zones. So the time complexity is O(9n) = O(n). Or if we + /// consider `NUM_CONFLICT_ZONES = m`, the time complexity is O(m*m*n). + pub fn shuffle(mut self) -> Vec { + // First pass, only select transactions with no conflicts in all conflict zones + while let Some(txn_idx) = self.selection_tracker.next_unselected() { + if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { + self.select_and_select_unconflicted(txn_idx, false /* is_pending */) + } else { + self.add_pending(txn_idx); + } + } + + // Second pass, select previously pending txns in order, + // with newly un-conflicted txns jumping the line + self.selection_tracker.new_pass(); + while let Some(txn_idx) = self.selection_tracker.next_unselected() { + self.select_and_select_unconflicted(txn_idx, true /* is_pending */); + } + + self.selected_order + } + + fn select_and_select_unconflicted(&mut self, txn_idx: TxnIdx, is_pending: bool) { + let mut maybe_unconflicted = self.select(txn_idx, is_pending); + while let Some(txn_idx) = maybe_unconflicted.pop_first() { + if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { + maybe_unconflicted.extend(self.select(txn_idx, true /* is_pending */)) + } + } + } + + /// Select a transaction and return potentially un-conflicted transactions + fn select(&mut self, txn_idx: TxnIdx, is_pending: bool) -> BTreeSet { + self.selection_tracker.mark_selected(txn_idx); + self.selected_order.push(txn_idx); + if is_pending { + self.pop_pending(txn_idx); + } + + let mut maybe_unconflicted = BTreeSet::new(); + for (conflict_zone, pending_zone) in + zip_eq(&mut self.conflict_zones, &mut self.pending_zones) + { + if let Some(key_id) = conflict_zone.add(txn_idx) { + if let Some(pending) = pending_zone.first_pending_on_key(key_id) { + maybe_unconflicted.insert(pending); + } + } + } + + maybe_unconflicted + } + + fn is_conflict(&self, txn_idx: TxnIdx) -> bool { + self.conflict_zones.iter().any(|z| z.is_conflict(txn_idx)) + } + + fn is_head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { + self.pending_zones + .iter() + .any(|z| z.head_of_line_blocked(txn_idx)) + } + + fn add_pending(&mut self, txn_idx: TxnIdx) { + self.pending_zones.iter_mut().for_each(|z| z.add(txn_idx)); + } + + fn pop_pending(&mut self, txn_idx: TxnIdx) { + self.pending_zones.iter_mut().for_each(|z| z.pop(txn_idx)); + } +} + +#[cfg(test)] +mod test_utils { + use crate::transaction_shuffler::fairness::FairnessShuffler; + use proptest::prelude::*; + + impl FairnessShuffler { + pub fn new_for_test( + sender_conflict_window_size: usize, + module_conflict_window_size: usize, + entry_fun_conflict_window_size: usize, + ) -> Self { + Self { + sender_conflict_window_size, + module_conflict_window_size, + entry_fun_conflict_window_size, + } + } + } + + impl Arbitrary for FairnessShuffler { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (0..10usize, 0..10usize, 0..10usize) + .prop_map( + |( + sender_conflict_window_size, + module_conflict_window_size, + entry_fun_conflict_window_size, + )| { + FairnessShuffler { + sender_conflict_window_size, + module_conflict_window_size, + entry_fun_conflict_window_size, + } + }, + ) + .boxed() + } + } +} diff --git a/consensus/src/transaction_shuffler/fairness/pending_zone.rs b/consensus/src/transaction_shuffler/fairness/pending_zone.rs new file mode 100644 index 0000000000000..8de932b65e601 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/pending_zone.rs @@ -0,0 +1,70 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::{ + conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, + TxnIdx, +}; +use std::collections::VecDeque; + +/// A queue for each confclit Key, represented by `ConflictKeyId`s managed by `ConflictKeyRegistry`. +#[derive(Debug)] +pub(crate) struct PendingZone<'a> { + key_registry: &'a ConflictKeyRegistry, + pending_by_key: MapByKeyId>, +} + +impl<'a> PendingZone<'a> { + pub fn build_zones( + key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], + ) -> [Self; NUM_CONFLICT_ZONES] { + key_registries + .iter() + .map(Self::new) + .collect::>() + .try_into() + .expect("key_registries and the return type must have the same length.") + } + + fn new(key_registry: &'a ConflictKeyRegistry) -> Self { + Self { + key_registry, + pending_by_key: key_registry.new_map_by_id(), + } + } + + pub fn add(&mut self, txn_idx: TxnIdx) { + let key_id = self.key_registry.key_id_for_txn(txn_idx); + if !self.key_registry.is_conflict_exempt(key_id) { + self.pending_by_key.get_mut(key_id).push_back(txn_idx); + } + } + + pub fn pop(&mut self, txn_idx: TxnIdx) { + let key_id = self.key_registry.key_id_for_txn(txn_idx); + if !self.key_registry.is_conflict_exempt(key_id) { + let popped = self + .pending_by_key + .get_mut(key_id) + .pop_front() + .expect("Must exist"); + assert_eq!(popped, txn_idx); + } + } + + pub fn head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { + let key_id = self.key_registry.key_id_for_txn(txn_idx); + if self.key_registry.is_conflict_exempt(key_id) { + false + } else { + match self.pending_by_key.get(key_id).front() { + Some(front) => *front < txn_idx, + None => false, + } + } + } + + pub fn first_pending_on_key(&self, key_id: ConflictKeyId) -> Option { + self.pending_by_key.get(key_id).front().cloned() + } +} diff --git a/consensus/src/transaction_shuffler/fairness/selection_tracker.rs b/consensus/src/transaction_shuffler/fairness/selection_tracker.rs new file mode 100644 index 0000000000000..571f817656ab8 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/selection_tracker.rs @@ -0,0 +1,43 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::TxnIdx; + +pub struct SelectionTracker { + selected_markers: Vec, + cur_idx: usize, +} + +impl SelectionTracker { + pub fn new(num_txns: usize) -> Self { + Self { + selected_markers: vec![false; num_txns], + cur_idx: 0, + } + } + + pub fn next_unselected(&mut self) -> Option { + while self.cur_idx < self.selected_markers.len() { + let idx = self.cur_idx; + self.cur_idx += 1; + + if !self.is_selected(idx) { + return Some(idx); + } + } + None + } + + pub fn new_pass(&mut self) { + self.cur_idx = 0 + } + + pub fn mark_selected(&mut self, idx: TxnIdx) { + assert!(!self.selected_markers[idx]); + self.selected_markers[idx] = true; + } + + fn is_selected(&self, idx: TxnIdx) -> bool { + self.selected_markers[idx] + } +} diff --git a/consensus/src/transaction_shuffler/fairness/tests/manual.rs b/consensus/src/transaction_shuffler/fairness/tests/manual.rs new file mode 100644 index 0000000000000..69cd94c141216 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/tests/manual.rs @@ -0,0 +1,169 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::{ + conflict_key::ConflictKeyRegistry, FairnessShuffler, FairnessShufflerImpl, +}; + +struct TestCase { + shuffler: FairnessShuffler, + conflict_key_registries: [ConflictKeyRegistry; 3], + expected_order: Vec, +} + +impl TestCase { + fn run(self) { + let Self { + shuffler, + conflict_key_registries, + expected_order, + } = self; + + let order = + FairnessShufflerImpl::new(&conflict_key_registries, shuffler.window_sizes()).shuffle(); + assert_eq!(order, expected_order); + } +} + +#[test] +fn test_all_exempt() { + TestCase { + shuffler: FairnessShuffler::new_for_test(2, 2, 2), + conflict_key_registries: [ + ConflictKeyRegistry::all_exempt(9), + ConflictKeyRegistry::all_exempt(9), + ConflictKeyRegistry::all_exempt(9), + ], + expected_order: (0..9).collect(), + } + .run() +} + +#[test] +fn test_non_conflict() { + TestCase { + shuffler: FairnessShuffler::new_for_test(2, 2, 2), + conflict_key_registries: [ + ConflictKeyRegistry::non_conflict(9), + ConflictKeyRegistry::non_conflict(9), + ConflictKeyRegistry::non_conflict(9), + ], + expected_order: (0..9).collect(), + } + .run() +} + +#[test] +fn test_full_conflict() { + TestCase { + shuffler: FairnessShuffler::new_for_test(2, 2, 2), + conflict_key_registries: [ + ConflictKeyRegistry::full_conflict(9), + ConflictKeyRegistry::full_conflict(9), + ConflictKeyRegistry::full_conflict(9), + ], + expected_order: (0..9).collect(), + } + .run() +} + +#[test] +fn test_modules_ignored_by_window_size() { + TestCase { + shuffler: FairnessShuffler::new_for_test(2, 0, 2), + conflict_key_registries: [ + // [A0, A1, A2, ...] + ConflictKeyRegistry::non_conflict(8), + // [M0, M0, M0, M0, M1, M1, M2, M2] + ConflictKeyRegistry::nums_per_key([4, 2, 2]), + // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M2::E0, M2::E0] + ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0], [1, 1, 4]]), + ], + // [M0::E0, M0::E1, M1::E0, M0::E0, M0::E1, M1::E0, M2::E0, M2::E0] + expected_order: vec![0, 1, 4, 2, 3, 5, 6, 7], + } + .run() +} + +#[test] +fn test_modules_and_entry_funs_ignored_by_window_size() { + TestCase { + shuffler: FairnessShuffler::new_for_test(2, 0, 0), + conflict_key_registries: [ + // [A0, A1, A2, ...] + ConflictKeyRegistry::non_conflict(8), + // [M0, M0, M0, M0, M1, M1, M1, M1] + ConflictKeyRegistry::nums_per_key([4, 4]), + // [M0::E0, M0::E0, M0::E1, M0::E1, M1::E0, M1::E0, M1::E1, M1::E1] + ConflictKeyRegistry::nums_per_key([2, 2, 2, 2]), + ], + expected_order: (0..8).collect(), + } + .run() +} + +#[test] +fn test_exempted_modules() { + // think "full block of p2p txns" + TestCase { + shuffler: FairnessShuffler::new_for_test(3, 2, 2), + conflict_key_registries: [ + // [0:A0, 1:A0, 2:A0, 3:A0, 4:A1, 5:A1, 6:A1, 7:A2, 8:A2, 9:A3] + ConflictKeyRegistry::nums_per_key([4, 3, 2, 1]), + ConflictKeyRegistry::all_exempt(10), + ConflictKeyRegistry::all_exempt(10), + ], + // [A0, A1, A2, A3, A0, A1, A2, A0, A1] + expected_order: vec![0, 4, 7, 9, 1, 5, 8, 2, 3, 6], + } + .run() +} + +#[test] +fn test_dominating_module() { + TestCase { + shuffler: FairnessShuffler::new_for_test(4, 1, 1), + conflict_key_registries: [ + ConflictKeyRegistry::non_conflict(7), + // [M0, M0, M0, M1, M2, M3, M4] + ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), + ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), + ], + // [M0, M1, M0, M2, M0, M3, M4] + expected_order: vec![0, 3, 1, 4, 2, 5, 6], + } + .run() +} + +#[test] +fn test_dominating_module2() { + TestCase { + shuffler: FairnessShuffler::new_for_test(4, 1, 1), + conflict_key_registries: [ + ConflictKeyRegistry::non_conflict(8), + // [M0, M0, M0, M1, M2, M3, M4, M0] + ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), + ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), + ], + // [M0, M1, M0, M2, M0, M3, M4, M0] + expected_order: vec![0, 3, 1, 4, 2, 5, 6, 7], + } + .run() +} + +#[test] +fn test_multiple_entry_funs() { + TestCase { + shuffler: FairnessShuffler::new_for_test(4, 1, 2), + conflict_key_registries: [ + ConflictKeyRegistry::non_conflict(10), + // [M0, M0, M0, M0, M1, M1, M1, M1, M2, M2] + ConflictKeyRegistry::nums_per_key([4, 4, 2]), + // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M1::E0, M1::E0, M2::E0, M2::E0] + ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0, 0], [1, 1, 4, 2]]), + ], + // [M0::E0, M1::E0, M0::E1, M2::E0, M0::E0, M1::E0, M0:E1, M2::E0, M1::E0, M1::E0] + expected_order: vec![0, 4, 1, 8, 2, 5, 3, 9, 6, 7], + } + .run() +} diff --git a/consensus/src/transaction_shuffler/fairness/tests/mod.rs b/consensus/src/transaction_shuffler/fairness/tests/mod.rs new file mode 100644 index 0000000000000..c3550a41f7ccc --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/tests/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod manual; +mod proptests; diff --git a/consensus/src/transaction_shuffler/fairness/tests/proptests.rs b/consensus/src/transaction_shuffler/fairness/tests/proptests.rs new file mode 100644 index 0000000000000..0195997fbf768 --- /dev/null +++ b/consensus/src/transaction_shuffler/fairness/tests/proptests.rs @@ -0,0 +1,104 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::transaction_shuffler::fairness::{ + conflict_key::{ + test_utils::{FakeEntryFunKey, FakeEntryFunModuleKey, FakeSenderKey, FakeTxn}, + ConflictKeyRegistry, MapByKeyId, + }, + reorder, FairnessShuffler, FairnessShufflerImpl, TxnIdx, +}; +use proptest::{collection::vec, prelude::*}; +use std::collections::BTreeSet; + +fn arb_order(num_txns: usize) -> impl Strategy> { + Just((0..num_txns).collect::>()).prop_shuffle() +} + +#[derive(Debug, Default, Eq, PartialEq)] +enum OrderOrSet { + #[default] + Empty, + Order(Vec), + Set(BTreeSet), +} + +impl OrderOrSet { + fn add(&mut self, idx: TxnIdx, is_conflict_exempt: bool) { + if self.is_empty() { + *self = if is_conflict_exempt { + Self::Set(BTreeSet::new()) + } else { + Self::Order(Vec::new()) + }; + } + + match self { + Self::Order(order) => order.push(idx), + Self::Set(set) => { + set.insert(idx); + }, + Self::Empty => unreachable!(), + } + } + + fn is_empty(&self) -> bool { + matches!(self, Self::Empty) + } +} + +fn sort_by_key( + order: impl IntoIterator, + registry: &ConflictKeyRegistry, +) -> MapByKeyId { + let mut map: MapByKeyId = registry.new_map_by_id(); + + for txn_idx in order { + let key_id = registry.key_id_for_txn(txn_idx); + let is_exempt = registry.is_conflict_exempt(key_id); + + map.get_mut(key_id).add(txn_idx, is_exempt); + } + + map +} + +fn assert_invariants(txns: &[FakeTxn], order: Vec, registry: &ConflictKeyRegistry) { + let num_txns = txns.len(); + let original_sorted = sort_by_key(0..num_txns, registry); + let result_sorted = sort_by_key(order, registry); + + assert_eq!(result_sorted, original_sorted); +} + +fn registries(txns: &[FakeTxn]) -> [ConflictKeyRegistry; 3] { + [ + ConflictKeyRegistry::build::(txns), + ConflictKeyRegistry::build::(txns), + ConflictKeyRegistry::build::(txns), + ] +} + +proptest! { + #[test] + fn test_reorder( order in (0..1000usize).prop_flat_map(arb_order) ) { + let num_txns = order.len(); + let txns = (0..num_txns).collect::>(); + + let reordered = reorder(txns, &order); + prop_assert_eq!(reordered, order); + } + + #[test] + fn test_fairness_shuffler( + txns in vec(any::(), 0..1000), + shuffler in any::(), + ) { + let registries = registries(&txns); + let order = FairnessShufflerImpl::new(®istries, shuffler.window_sizes()).shuffle(); + + for registry in ®istries { + assert_invariants(&txns, order.clone(), registry); + } + } +} diff --git a/consensus/src/transaction_shuffler/mod.rs b/consensus/src/transaction_shuffler/mod.rs new file mode 100644 index 0000000000000..5cc43f2a2e8da --- /dev/null +++ b/consensus/src/transaction_shuffler/mod.rs @@ -0,0 +1,69 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_logger::info; +use aptos_types::{ + on_chain_config::{ + TransactionShufflerType, + TransactionShufflerType::{DeprecatedSenderAwareV1, NoShuffling, SenderAwareV2}, + }, + transaction::SignedTransaction, +}; +use sender_aware::SenderAwareShuffler; +use std::sync::Arc; + +mod fairness; +mod sender_aware; + +/// Interface to shuffle transactions +pub trait TransactionShuffler: Send + Sync { + fn shuffle(&self, txns: Vec) -> Vec; +} + +/// No Op Shuffler to maintain backward compatibility +pub struct NoOpShuffler {} + +impl TransactionShuffler for NoOpShuffler { + fn shuffle(&self, txns: Vec) -> Vec { + txns + } +} + +pub fn create_transaction_shuffler( + shuffler_type: TransactionShufflerType, +) -> Arc { + match shuffler_type { + NoShuffling => { + info!("Using no-op transaction shuffling"); + Arc::new(NoOpShuffler {}) + }, + DeprecatedSenderAwareV1(_) => { + info!("Using no-op sender aware shuffling v1"); + Arc::new(NoOpShuffler {}) + }, + SenderAwareV2(conflict_window_size) => { + info!( + "Using sender aware transaction shuffling with conflict window size {}", + conflict_window_size + ); + Arc::new(SenderAwareShuffler::new(conflict_window_size as usize)) + }, + TransactionShufflerType::Fairness { + sender_conflict_window_size, + module_conflict_window_size, + entry_fun_conflict_window_size, + } => { + info!( + "Using fairness transaction shuffling with conflict window sizes: sender {}, module {}, entry fun {}", + sender_conflict_window_size, + module_conflict_window_size, + entry_fun_conflict_window_size + ); + Arc::new(fairness::FairnessShuffler { + sender_conflict_window_size: sender_conflict_window_size as usize, + module_conflict_window_size: module_conflict_window_size as usize, + entry_fun_conflict_window_size: entry_fun_conflict_window_size as usize, + }) + }, + } +} diff --git a/consensus/src/transaction_shuffler/sender_aware.rs b/consensus/src/transaction_shuffler/sender_aware.rs new file mode 100644 index 0000000000000..936a9f7e9d0a5 --- /dev/null +++ b/consensus/src/transaction_shuffler/sender_aware.rs @@ -0,0 +1,532 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{counters::NUM_SENDERS_IN_BLOCK, transaction_shuffler::TransactionShuffler}; +use aptos_types::transaction::SignedTransaction; +use move_core_types::account_address::AccountAddress; +use std::collections::{HashMap, VecDeque}; + +/// An implementation of transaction shuffler, which tries to spread transactions from same senders +/// in a block in order to reduce conflict. On a high level, it works as follows - It defines a +/// `conflict_window_size`, which maintains a set of senders added to the block in last `conflict_window_size` +/// transactions. When trying to select a new transaction to the block, the shuffler tries to find +/// a transaction which are not part of the conflicting senders in the window. If it does, it adds +/// the first non-conflicting transaction it finds to the block, if it doesn't then it preserves the +/// order and adds the first transaction in the remaining block. It always maintains the following +/// invariant in terms of ordering +/// 1. Relative ordering of all transactions from the same before and after shuffling is same +/// 2. Relative ordering of all transactions across different senders will also be maintained if they are +/// non-conflicting. In other words, if the input block has only one transaction per sender, the output +/// ordering will remain unchanged. +/// +/// The shuffling algorithm is O(n) and following is the pseudo code for it. +/// loop: +/// if a sender fell out of the sliding window in previous iteration, +/// then: we add the first pending transaction from that sender to the block +/// else while we have transactions to process in the original transaction order +/// take a new one, +/// if it conflicts, add to the pending set +/// else we add it to the block +/// else +/// take the first transaction from the pending transactions and add it to the block + +pub struct SenderAwareShuffler { + conflict_window_size: usize, +} + +impl TransactionShuffler for SenderAwareShuffler { + fn shuffle(&self, txns: Vec) -> Vec { + // Early return for performance reason if there are no transactions to shuffle + if txns.is_empty() { + return txns; + } + + // handle the corner case of conflict window being 0, in which case we don't do any shuffling + if self.conflict_window_size == 0 { + return txns; + } + + // maintains the intermediate state of the shuffled transactions + let mut sliding_window = SlidingWindowState::new(self.conflict_window_size, txns.len()); + let mut pending_txns = PendingTransactions::new(); + let num_transactions = txns.len(); + let mut orig_txns = VecDeque::from(txns); + let mut next_to_add = |sliding_window: &mut SlidingWindowState| -> SignedTransaction { + // First check if we have a sender dropped off of conflict window in previous step, if so, + // we try to find pending transaction from the corresponding sender and add it to the block. + if let Some(sender) = sliding_window.last_dropped_sender() { + if let Some(txn) = pending_txns.remove_pending_from_sender(sender) { + return txn; + } + } + // If we can't find any transaction from a sender dropped off of conflict window, then + // iterate through the original transactions and try to find the next candidate + while let Some(txn) = orig_txns.pop_front() { + if !sliding_window.has_conflict(&txn.sender()) { + return txn; + } + pending_txns.add_transaction(txn); + } + + // If we can't find any candidate in above steps, then lastly + // add pending transactions in the order if we can't find any other candidate + pending_txns.remove_first_pending().unwrap() + }; + while sliding_window.num_txns() < num_transactions { + let txn = next_to_add(&mut sliding_window); + sliding_window.add_transaction(txn) + } + sliding_window.finalize() + } +} + +impl SenderAwareShuffler { + pub fn new(conflict_window_size: usize) -> Self { + Self { + conflict_window_size, + } + } +} + +/// A structure to maintain a set of transactions that are pending to be added to the block indexed by +/// the sender. For a particular sender, relative ordering of transactions are maintained, +/// so that the final block preserves the ordering of transactions by sender. It also maintains a vector +/// to preserve the original order of the transactions. This is needed in case we can't find +/// any non-conflicting transactions and we need to add the first pending transaction to the block. +struct PendingTransactions { + txns_by_senders: HashMap>, + // Transactions are kept in the original order. This is not kept in sync with pending transactions, + // so this can contain a bunch of transactions that are already added to the block. + ordered_txns: VecDeque, +} + +impl PendingTransactions { + pub fn new() -> Self { + Self { + txns_by_senders: HashMap::new(), + ordered_txns: VecDeque::new(), + } + } + + pub fn add_transaction(&mut self, txn: SignedTransaction) { + self.ordered_txns.push_back(txn.clone()); + self.txns_by_senders + .entry(txn.sender()) + .or_default() + .push_back(txn); + } + + /// Removes the first pending transaction from the sender. Please note that the transaction is not + /// removed from the `ordered_txns`, so the `ordered_txns` will contain a set of transactions that + /// are removed from pending transactions already. + pub fn remove_pending_from_sender( + &mut self, + sender: AccountAddress, + ) -> Option { + self.txns_by_senders + .get_mut(&sender) + .and_then(|txns| txns.pop_front()) + } + + pub fn remove_first_pending(&mut self) -> Option { + while let Some(txn) = self.ordered_txns.pop_front() { + let sender = txn.sender(); + // We don't remove the txns from ordered_txns when remove_pending_from_sender is called. + // So it is possible that the ordered_txns has some transactions that are not pending + // anymore. + if Some(txn).as_ref() == self.txns_by_senders.get(&sender).unwrap().front() { + return self.remove_pending_from_sender(sender); + } + } + None + } +} + +/// A stateful data structure maintained by the transaction shuffler during shuffling. On a +/// high level, it maintains a sliding window of the conflicting transactions, which helps the payload +/// generator include a set of transactions which are non-conflicting with each other within a particular +/// window size. +struct SlidingWindowState { + // Please note that the start index can be negative in case the window size is larger than the + // end_index. + start_index: i64, + // Hashmap of senders to the number of transactions included in the window for the corresponding + // sender. + senders_in_window: HashMap, + // Partially ordered transactions, needs to be updated every time add_transactions is called. + txns: Vec, +} + +impl SlidingWindowState { + pub fn new(window_size: usize, num_txns: usize) -> Self { + Self { + start_index: -(window_size as i64), + senders_in_window: HashMap::new(), + txns: Vec::with_capacity(num_txns), + } + } + + /// Slides the current window. Essentially, it increments the start_index and + /// updates the senders_in_window map if start_index is greater than 0 + pub fn add_transaction(&mut self, txn: SignedTransaction) { + if self.start_index >= 0 { + // if the start_index is negative, then no sender falls out of the window. + let sender = self + .txns + .get(self.start_index as usize) + .expect("Transaction expected") + .sender(); + self.senders_in_window + .entry(sender) + .and_modify(|count| *count -= 1); + } + let count = self + .senders_in_window + .entry(txn.sender()) + .or_insert_with(|| 0); + *count += 1; + self.txns.push(txn); + self.start_index += 1; + } + + pub fn has_conflict(&self, addr: &AccountAddress) -> bool { + self.senders_in_window + .get(addr) + .map_or(false, |count| *count != 0) + } + + /// Returns the sender which was dropped off of the conflict window in previous iteration. + pub fn last_dropped_sender(&self) -> Option { + let prev_start_index = self.start_index - 1; + if prev_start_index >= 0 { + let last_sender = self.txns.get(prev_start_index as usize).unwrap().sender(); + if *self.senders_in_window.get(&last_sender).unwrap() == 0 { + return Some(last_sender); + } + } + None + } + + pub fn num_txns(&self) -> usize { + self.txns.len() + } + + pub fn finalize(self) -> Vec { + NUM_SENDERS_IN_BLOCK.set(self.senders_in_window.len() as f64); + self.txns + } +} + +#[cfg(test)] +mod tests { + use crate::transaction_shuffler::{sender_aware::SenderAwareShuffler, TransactionShuffler}; + use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, SigningKey, Uniform}; + use aptos_types::{ + chain_id::ChainId, + transaction::{RawTransaction, Script, SignedTransaction, TransactionPayload}, + }; + use move_core_types::account_address::AccountAddress; + use rand::{rngs::OsRng, Rng}; + use std::{ + collections::{HashMap, HashSet}, + time::Instant, + }; + + fn create_signed_transaction(num_transactions: usize) -> Vec { + let private_key = Ed25519PrivateKey::generate_for_testing(); + let public_key = private_key.public_key(); + let sender = AccountAddress::random(); + + let mut transactions = Vec::new(); + + for i in 0..num_transactions { + let transaction_payload = + TransactionPayload::Script(Script::new(vec![], vec![], vec![])); + let raw_transaction = RawTransaction::new( + sender, + i as u64, + transaction_payload, + 0, + 0, + 0, + ChainId::new(10), + ); + let signed_transaction = SignedTransaction::new( + raw_transaction.clone(), + public_key.clone(), + private_key.sign(&raw_transaction).unwrap(), + ); + transactions.push(signed_transaction) + } + transactions + } + + #[test] + fn test_single_user_txns() { + for num_txns in [1, 5, 50, 500] { + let txns = create_signed_transaction(num_txns); + let txn_shuffer = SenderAwareShuffler::new(10); + let optimized_txns = txn_shuffer.shuffle(txns.clone()); + assert_eq!(txns.len(), optimized_txns.len()); + // Assert that ordering is unchanged in case of single sender block + assert_eq!(txns, optimized_txns) + } + } + + #[test] + fn test_unique_sender_txns() { + for num_senders in [1, 5, 50, 500] { + let mut txns = Vec::new(); + let mut senders = Vec::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(1); + senders.push(sender_txns.first().unwrap().sender()); + txns.append(&mut sender_txns); + } + let txn_shuffer = SenderAwareShuffler::new(10); + let optimized_txns = txn_shuffer.shuffle(txns.clone()); + assert_eq!(txns.len(), optimized_txns.len()); + // Assert that the ordering is unchanged in case of unique senders txns. + assert_eq!(txns, optimized_txns) + } + } + + #[test] + fn test_perfect_shuffling() { + let num_senders = 50; + let mut txns = Vec::new(); + let mut senders = Vec::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(10); + senders.push(sender_txns.first().unwrap().sender()); + txns.append(&mut sender_txns); + } + + let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); + let optimized_txns = txn_shuffler.shuffle(txns.clone()); + assert_eq!(txns.len(), optimized_txns.len()); + let mut sender_index = 0; + for txn in optimized_txns { + assert_eq!(&txn.sender(), senders.get(sender_index).unwrap()); + sender_index = (sender_index + 1) % senders.len() + } + } + + #[test] + fn test_shuffling_benchmark() { + let num_senders = 200; + let mut txns = Vec::new(); + let mut senders = Vec::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(10); + senders.push(sender_txns.first().unwrap().sender()); + txns.append(&mut sender_txns); + } + + let now = Instant::now(); + let txn_shuffler = SenderAwareShuffler::new(32); + let optimized_txns = txn_shuffler.shuffle(txns.clone()); + println!("elapsed time is {}", now.elapsed().as_millis()); + assert_eq!(txns.len(), optimized_txns.len()); + } + + #[test] + fn test_same_sender_relative_order() { + let mut rng = OsRng; + let max_txn_per_sender = 100; + let num_senders = 100; + let mut orig_txns = Vec::new(); + let mut orig_txns_by_sender = HashMap::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); + orig_txns_by_sender.insert(sender_txns.first().unwrap().sender(), sender_txns.clone()); + orig_txns.append(&mut sender_txns); + } + let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); + let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); + let mut optimized_txns_by_sender = HashMap::new(); + for txn in optimized_txns { + optimized_txns_by_sender + .entry(txn.sender()) + .or_insert_with(Vec::new) + .push(txn); + } + + for (sender, orig_txns) in orig_txns_by_sender { + assert_eq!(optimized_txns_by_sender.get(&sender).unwrap(), &orig_txns) + } + } + + #[test] + // S1_1, S2_1, S3_1, S3_2 + // with conflict_window_size=3, should return (keep the order, fairness to early transactions): + // S1_1, S2_1, S3_1, S3_2 + fn test_3_sender_shuffling() { + let mut orig_txns = Vec::new(); + let sender1_txns = create_signed_transaction(1); + let sender2_txns = create_signed_transaction(1); + let sender3_txns = create_signed_transaction(2); + orig_txns.extend(sender1_txns.clone()); + orig_txns.extend(sender2_txns.clone()); + orig_txns.extend(sender3_txns.clone()); + let txn_shuffler = SenderAwareShuffler::new(3); + let optimized_txns = txn_shuffler.shuffle(orig_txns); + assert_eq!( + optimized_txns.first().unwrap(), + sender1_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(1).unwrap(), + sender2_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(2).unwrap(), + sender3_txns.first().unwrap() + ); + assert_eq!(optimized_txns.get(3).unwrap(), sender3_txns.get(1).unwrap()); + } + + #[test] + // S1_1, S2_1, S1_2, S3_1, S4_1, S5_1 + // with conflict_window_size=3, should return + // (we separate transactions from same sender, even if they are not consecutive): + // S1_1, S2_1, S3_1, S4_1, S1_2, S5_1 + fn test_5_sender_shuffling() { + let mut orig_txns = Vec::new(); + let sender1_txns = create_signed_transaction(2); + let sender2_txns = create_signed_transaction(1); + let sender3_txns = create_signed_transaction(1); + let sender4_txns = create_signed_transaction(1); + let sender5_txns = create_signed_transaction(1); + orig_txns.extend(sender1_txns.clone()); + orig_txns.extend(sender2_txns.clone()); + orig_txns.extend(sender3_txns.clone()); + orig_txns.extend(sender4_txns.clone()); + orig_txns.extend(sender5_txns.clone()); + let txn_shuffler = SenderAwareShuffler::new(3); + let optimized_txns = txn_shuffler.shuffle(orig_txns); + assert_eq!( + optimized_txns.first().unwrap(), + sender1_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(1).unwrap(), + sender2_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(2).unwrap(), + sender3_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(3).unwrap(), + sender4_txns.first().unwrap() + ); + assert_eq!(optimized_txns.get(4).unwrap(), sender1_txns.get(1).unwrap()); + assert_eq!( + optimized_txns.get(5).unwrap(), + sender5_txns.first().unwrap() + ); + } + + #[test] + // S1_1, S1_2, S2_1, S3_1, S3_2, S4_1, S5_1, S6_1 + // with conflict_window_size=3, should return (each batches are separated from the point they appear on): + // S1_1, S2_1, S3_1, S4_1, S1_2, S5_1, S3_2, S6_1 + fn test_6_sender_shuffling() { + let mut orig_txns = Vec::new(); + let sender1_txns = create_signed_transaction(2); + let sender2_txns = create_signed_transaction(1); + let sender3_txns = create_signed_transaction(2); + let sender4_txns = create_signed_transaction(1); + let sender5_txns = create_signed_transaction(1); + let sender6_txns = create_signed_transaction(1); + orig_txns.extend(sender1_txns.clone()); + orig_txns.extend(sender2_txns.clone()); + orig_txns.extend(sender3_txns.clone()); + orig_txns.extend(sender4_txns.clone()); + orig_txns.extend(sender5_txns.clone()); + orig_txns.extend(sender6_txns.clone()); + let txn_shuffler = SenderAwareShuffler::new(3); + let optimized_txns = txn_shuffler.shuffle(orig_txns); + assert_eq!( + optimized_txns.first().unwrap(), + sender1_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(1).unwrap(), + sender2_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(2).unwrap(), + sender3_txns.first().unwrap() + ); + assert_eq!( + optimized_txns.get(3).unwrap(), + sender4_txns.first().unwrap() + ); + assert_eq!(optimized_txns.get(4).unwrap(), sender1_txns.get(1).unwrap()); + assert_eq!( + optimized_txns.get(5).unwrap(), + sender5_txns.first().unwrap() + ); + assert_eq!(optimized_txns.get(6).unwrap(), sender3_txns.get(1).unwrap()); + assert_eq!( + optimized_txns.get(7).unwrap(), + sender6_txns.first().unwrap() + ); + } + + #[test] + fn test_random_shuffling() { + let mut rng = OsRng; + let max_senders = 50; + let max_txn_per_sender = 100; + let num_senders = rng.gen_range(1, max_senders); + let mut orig_txns = Vec::new(); + let mut senders = Vec::new(); + let mut orig_txn_set = HashSet::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); + senders.push(sender_txns.first().unwrap().sender()); + orig_txns.append(&mut sender_txns); + } + for txn in orig_txns.clone() { + orig_txn_set.insert(txn.into_raw_transaction()); + } + + let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); + let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); + let mut optimized_txn_set = HashSet::new(); + assert_eq!(orig_txns.len(), optimized_txns.len()); + + for optimized_txn in optimized_txns { + assert!(orig_txn_set.contains(&optimized_txn.clone().into_raw_transaction())); + optimized_txn_set.insert(optimized_txn.into_raw_transaction()); + } + + for orig_txn in orig_txns { + assert!(optimized_txn_set.contains(&orig_txn.into_raw_transaction())); + } + } + + #[test] + fn test_shuffling_zero_conflict_window() { + let mut rng = OsRng; + let max_senders = 50; + let max_txn_per_sender = 100; + let num_senders = rng.gen_range(1, max_senders); + let mut orig_txns = Vec::new(); + let mut senders = Vec::new(); + for _ in 0..num_senders { + let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); + senders.push(sender_txns.first().unwrap().sender()); + orig_txns.append(&mut sender_txns); + } + + let txn_shuffler = SenderAwareShuffler::new(0); + let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); + assert_eq!(orig_txns.len(), optimized_txns.len()); + // Assert that the ordering is unchanged in case of unique senders txns. + assert_eq!(orig_txns, optimized_txns); + } +} diff --git a/consensus/src/twins/basic_twins_test.rs b/consensus/src/twins/basic_twins_test.rs new file mode 100644 index 0000000000000..9893efebd5cae --- /dev/null +++ b/consensus/src/twins/basic_twins_test.rs @@ -0,0 +1,315 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + network_interface::ConsensusMsg, + network_tests::{NetworkPlayground, TwinId}, + test_utils::{consensus_runtime, timed_block_on}, + twins::twins_node::SMRNode, +}; +use aptos_consensus_types::{block::Block, common::Round}; +use aptos_types::on_chain_config::ProposerElectionType::{ + FixedProposer, RotatingProposer, RoundProposer, +}; +use futures::StreamExt; +use std::collections::HashMap; + +#[test] +/// This test checks that the first proposal has its parent and +/// QC pointing to the genesis block. +/// +/// Setup: +/// +/// 4 honest nodes, and 0 twins +/// +/// Run the test: +/// cargo xtest -p consensus basic_start_test -- --nocapture +fn basic_start_test() { + let runtime = consensus_runtime(); + let mut playground = NetworkPlayground::new(runtime.handle().clone()); + let num_nodes = 4; + let num_twins = 0; + let nodes = SMRNode::start_num_nodes_with_twins( + num_nodes, + num_twins, + &mut playground, + RotatingProposer(2), + None, + ); + let genesis = Block::make_genesis_block_from_ledger_info(&nodes[0].storage.get_ledger_info()); + timed_block_on(&runtime, async { + let msg = playground + .wait_for_messages(1, NetworkPlayground::proposals_only) + .await; + let first_proposal = match &msg[0].1 { + ConsensusMsg::ProposalMsg(proposal) => proposal, + _ => panic!("Unexpected message found"), + }; + assert_eq!(first_proposal.proposal().parent_id(), genesis.id()); + assert_eq!( + first_proposal + .proposal() + .quorum_cert() + .certified_block() + .id(), + genesis.id() + ); + }); +} + +#[test] +/// This test checks that the split_network function works +/// as expected, that is: nodes in a partition with less nodes +/// than required for quorum do not commit anything. +/// +/// Setup: +/// +/// 4 honest nodes (n0, n1, n2, n3), and 0 twins. +/// Create two partitions p1=[n2], and p2=[n0, n1, n3] with +/// a proposer (n0) in p2. +/// +/// Test: +/// +/// Run consensus for enough rounds to potentially form a commit. +/// Check that n1 has no commits, and n0 has commits. +/// +/// Run the test: +/// cargo xtest -p consensus drop_config_test -- --nocapture +#[ignore] // TODO: https://github.com/aptos-labs/aptos-core/issues/8767 +fn drop_config_test() { + let runtime = consensus_runtime(); + let mut playground = NetworkPlayground::new(runtime.handle().clone()); + let num_nodes = 4; + let num_twins = 0; + let mut nodes = SMRNode::start_num_nodes_with_twins( + num_nodes, + num_twins, + &mut playground, + FixedProposer(2), + None, + ); + + // 4 honest nodes + let n0_twin_id = nodes[0].id; + let n1_twin_id = nodes[1].id; + let n2_twin_id = nodes[2].id; + let n3_twin_id = nodes[3].id; + + assert!(playground.split_network(vec![n2_twin_id], vec![n0_twin_id, n1_twin_id, n3_twin_id])); + runtime.spawn(playground.start()); + + timed_block_on(&runtime, async { + // Check that the commit log for n0 is not empty + let node0_commit = nodes[0].commit_cb_receiver.next().await; + assert!(node0_commit.is_some()); + + // Check that the commit log for n2 is empty + let node2_commit = match nodes[2].commit_cb_receiver.try_next() { + Ok(Some(node_commit)) => Some(node_commit), + _ => None, + }; + assert!(node2_commit.is_none()); + }); +} + +#[test] +/// This test checks that the vote of a node and its twin +/// should be counted as duplicate vote (because they have +/// the same public keys) +/// +/// Setup: +/// +/// 4 honest nodes (n0, n1, n2, n3), and 1 twin (twin0) +/// Create 2 partitions, p1=[n1, n3], p2=[n0, twin0, n2] +/// +/// Test: +/// +/// Extract enough votes to potentially form commits. Check +/// that no node commits any block. This is because we need +/// 3 nodes to form a quorum and no partition has enough votes +/// (note there are 3 nodes in p2, but one of them is a twin, +/// and its vote will be counted as duplicate of n0). +/// +/// Run the test: +/// cargo xtest -p consensus twins_vote_dedup_test -- --nocapture +fn twins_vote_dedup_test() { + let runtime = consensus_runtime(); + let mut playground = NetworkPlayground::new(runtime.handle().clone()); + let num_nodes = 4; + let num_twins = 1; + let mut nodes = SMRNode::start_num_nodes_with_twins( + num_nodes, + num_twins, + &mut playground, + RotatingProposer(2), + None, + ); + + // 4 honest nodes + let n0_twin_id = nodes[0].id; + // twin of n0 has same author as node[0] + let twin0_twin_id = nodes[4].id; + assert_eq!(n0_twin_id.author, twin0_twin_id.author); + let n1_twin_id = nodes[1].id; + let n2_twin_id = nodes[2].id; + let n3_twin_id = nodes[3].id; + + assert!(playground.split_network(vec![n1_twin_id, n3_twin_id], vec![ + twin0_twin_id, + n0_twin_id, + n2_twin_id + ],)); + runtime.spawn(playground.start()); + + timed_block_on(&runtime, async { + // No node should be able to commit because of the way partitions + // have been created + let mut commit_seen = false; + for node in &mut nodes { + if let Ok(Some(_node_commit)) = node.commit_cb_receiver.try_next() { + commit_seen = true; + } + } + assert!(!commit_seen); + }); +} + +#[test] +/// This test checks that when a node becomes a proposer, its +/// twin becomes one too. +/// +/// Setup: +/// +/// 4 honest nodes (n0, n1, n2, n3), and 2 twins (twin0, twin1) +/// Create 2 partitions, p1=[n0, n1, n2], p2=[n3, twin0, twin1] +/// Let n0 (and implicitly twin0) be proposers +/// +/// Test: +/// +/// Extract enough votes so nodes in both partitions form commits. +/// The commits should be on two different blocks +/// +/// Run the test: +/// cargo xtest -p consensus twins_proposer_test -- --nocapture +#[ignore] +fn twins_proposer_test() { + let runtime = consensus_runtime(); + let mut playground = NetworkPlayground::new(runtime.handle().clone()); + let num_nodes = 4; + let num_twins = 2; + + // Specify round leaders + // Will default to the first node, if no leader specified for given round + let mut round_proposers: HashMap = HashMap::new(); + // Leaders are n0 (and implicitly twin0) for round 1..10 + for i in 1..10 { + round_proposers.insert(i, 0); + } + + let mut nodes = SMRNode::start_num_nodes_with_twins( + num_nodes, + num_twins, + &mut playground, + RoundProposer(HashMap::new()), + Some(round_proposers), + ); + + // 4 honest nodes + let n0_twin_id = nodes[0].id; + // twin of n0 has same author as node_authors[0] + let twin0_twin_id = nodes[4].id; + assert_eq!(n0_twin_id.author, twin0_twin_id.author); + let n1_twin_id = nodes[1].id; + // twin of n1 has same author as node_authors[1] + let twin1_twin_id = nodes[5].id; + assert_eq!(n1_twin_id.author, twin1_twin_id.author); + let n2_twin_id = nodes[2].id; + let n3_twin_id = nodes[3].id; + + // Create per round partitions + let mut round_partitions: HashMap>> = HashMap::new(); + // Round 1 to 10 partitions: [node0, node1, node2], [node3, twin0, twin1] + for i in 1..10 { + round_partitions.insert(i, vec![vec![n0_twin_id, n1_twin_id, n2_twin_id], vec![ + n3_twin_id, + twin0_twin_id, + twin1_twin_id, + ]]); + } + assert!(playground.split_network_round(&round_partitions)); + runtime.spawn(playground.start()); + + timed_block_on(&runtime, async { + let node0_commit = nodes[0].commit_cb_receiver.next().await; + let twin0_commit = nodes[4].commit_cb_receiver.next().await; + + match (node0_commit, twin0_commit) { + (Some(node0_commit_inner), Some(twin0_commit_inner)) => { + let node0_commit_id = node0_commit_inner.ledger_info().commit_info().id(); + let twin0_commit_id = twin0_commit_inner.ledger_info().commit_info().id(); + // Proposal from both node0 and twin_node0 are going to + // get committed in their respective partitions + assert_ne!(node0_commit_id, twin0_commit_id); + }, + _ => panic!("[TwinsTest] Test failed due to no commit(s)"), + } + }); +} + +#[test] +#[ignore] // TODO: https://github.com/aptos-labs/aptos-core/issues/6615 +/// This test checks that when a node and its twin are both leaders +/// for a round, only one of the two proposals gets committed +/// +/// Setup: +/// +/// Network of 4 nodes (n0, n1, n2, n3), and 1 twin (twin0) +/// +/// Test: +/// +/// Let n0 (and implicitly twin0) be proposers +/// Pull out enough votes so a commit can be formed +/// Check that the commit of n0 and twin0 matches +/// +/// Run the test: +/// cargo xtest -p consensus twins_commit_test -- --nocapture +fn twins_commit_test() { + let runtime = consensus_runtime(); + let mut playground = NetworkPlayground::new(runtime.handle().clone()); + let num_nodes = 4; + let num_twins = 1; + + // Specify round leaders + // Will default to the first node, if no leader specified for given round + let mut round_proposers: HashMap = HashMap::new(); + // Leaders are n0 and twin0 for round 1..10 + for i in 1..10 { + round_proposers.insert(i, 0); + } + + let mut nodes = SMRNode::start_num_nodes_with_twins( + num_nodes, + num_twins, + &mut playground, + RoundProposer(HashMap::new()), + Some(round_proposers), + ); + runtime.spawn(playground.start()); + + timed_block_on(&runtime, async { + let node0_commit = nodes[0].commit_cb_receiver.next().await; + let twin0_commit = nodes[4].commit_cb_receiver.next().await; + + match (node0_commit, twin0_commit) { + (Some(node0_commit_inner), Some(twin0_commit_inner)) => { + let node0_commit_id = node0_commit_inner.ledger_info().commit_info().id(); + let twin0_commit_id = twin0_commit_inner.ledger_info().commit_info().id(); + // Proposals from both node0 and twin_node0 are going to race, + // but only one of them will form a commit + assert_eq!(node0_commit_id, twin0_commit_id); + }, + _ => panic!("[TwinsTest] Test failed due to no commit(s)"), + } + }); +} diff --git a/consensus/src/twins/mod.rs b/consensus/src/twins/mod.rs new file mode 100644 index 0000000000000..f35a1c4ce2848 --- /dev/null +++ b/consensus/src/twins/mod.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod basic_twins_test; +mod twins_node; diff --git a/consensus/src/util/db_tool.rs b/consensus/src/util/db_tool.rs new file mode 100644 index 0000000000000..5809747c4bcb7 --- /dev/null +++ b/consensus/src/util/db_tool.rs @@ -0,0 +1,107 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + consensusdb::ConsensusDB, + quorum_store::{ + quorum_store_db::{QuorumStoreDB, QuorumStoreStorage}, + types::PersistedValue, + }, +}; +use anyhow::{bail, Result}; +use aptos_consensus_types::{block::Block, common::Payload, proof_of_store::ProofOfStore}; +use aptos_crypto::HashValue; +use aptos_types::transaction::{SignedTransaction, Transaction}; +use clap::Parser; +use std::{collections::HashMap, path::PathBuf}; + +#[derive(Parser)] +#[clap(about = "Dump txns from consensus db.")] +pub struct Command { + #[clap(long, value_parser)] + pub db_dir: PathBuf, + + // If None, will dump all blocks. + #[clap(long)] + pub block_id: Option, +} + +impl Command { + pub async fn run(self) -> Result<()> { + let txns = self.dump_pending_txns()?; + println!("{txns:?}"); + + Ok(()) + } + + pub fn dump_pending_txns(&self) -> Result> { + let quorum_store_db = QuorumStoreDB::new(self.db_dir.clone()); + let all_batches = quorum_store_db.get_all_batches().unwrap(); + + let consensus_db = ConsensusDB::new(self.db_dir.clone()); + let (_, _, blocks, _) = consensus_db.get_data()?; + + let mut txns = Vec::new(); + for block in blocks { + let id = block.id(); + if self.block_id.is_none() || id == self.block_id.unwrap() { + txns.extend( + extract_txns_from_block(&block, &all_batches)? + .into_iter() + .cloned() + .map(Transaction::UserTransaction), + ); + } + } + + Ok(txns) + } +} + +pub fn extract_txns_from_block<'a>( + block: &'a Block, + all_batches: &'a HashMap, +) -> anyhow::Result> { + match block.payload().as_ref() { + Some(payload) => { + let mut block_txns = Vec::new(); + + let extract_txns_from_proof_stores = move |proofs: &Vec| { + for proof in proofs { + let digest = proof.digest(); + if let Some(batch) = all_batches.get(digest) { + if let Some(txns) = batch.payload() { + block_txns.extend(txns); + } else { + bail!("Payload is not found for batch ({digest})."); + } + } else { + bail!("Batch ({digest}) is not found."); + } + } + Ok(block_txns) + }; + + match payload { + Payload::DirectMempool(_) => { + bail!("DirectMempool is not supported."); + }, + Payload::InQuorumStore(proof_with_data) => { + extract_txns_from_proof_stores(&proof_with_data.proofs) + }, + Payload::InQuorumStoreWithLimit(proof_with_data) => { + extract_txns_from_proof_stores(&proof_with_data.proof_with_data.proofs) + }, + Payload::QuorumStoreInlineHybrid(inline_batches, proof_with_data, _) => { + let mut all_txns = + extract_txns_from_proof_stores(&proof_with_data.proofs).unwrap(); + for (_, txns) in inline_batches { + all_txns.extend(txns); + } + Ok(all_txns) + }, + } + }, + None => Ok(vec![]), + } +} diff --git a/consensus/src/util/mock_time_service.rs b/consensus/src/util/mock_time_service.rs new file mode 100644 index 0000000000000..9f9a665cac48b --- /dev/null +++ b/consensus/src/util/mock_time_service.rs @@ -0,0 +1,138 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::util::time_service::{ScheduledTask, TimeService}; +use aptos_infallible::Mutex; +use aptos_logger::prelude::*; +use async_trait::async_trait; +use futures::future::AbortHandle; +use std::{sync::Arc, time::Duration}; + +/// SimulatedTimeService implements TimeService, however it does not depend on actual time +/// There are multiple ways to use it: +/// SimulatedTimeService::new will create time service that simply 'stuck' on time 0 +/// SimulatedTimeService::update_auto_advance_limit can then be used to allow time to advance up to +/// certain limit. SimulatedTimeService::auto_advance_until will create time service that will 'run' +/// until certain time limit Note that SimulatedTimeService does not actually wait for any timeouts, +/// notion of time in it is abstract. Tasks run asap as long as they are scheduled before configured +/// time limit +pub struct SimulatedTimeService { + inner: Arc>, +} + +struct SimulatedTimeServiceInner { + now: Duration, + pending: Vec<(Duration, Box)>, + time_limit: Duration, + /// Maximum duration self.now is allowed to advance to + max: Duration, +} + +#[async_trait] +impl TimeService for SimulatedTimeService { + fn run_after(&self, timeout: Duration, mut t: Box) -> AbortHandle { + let mut inner = self.inner.lock(); + let now = inner.now; + let deadline = now + timeout; + if deadline > inner.time_limit { + debug!( + "sched for deadline: {}, now: {}, limit: {}", + deadline.as_millis(), + now.as_millis(), + inner.time_limit.as_millis() + ); + inner.pending.push((deadline, t)); + } else { + debug!( + "exec deadline: {}, now: {}", + deadline.as_millis(), + now.as_millis() + ); + inner.now = deadline; + if inner.now > inner.max { + inner.now = inner.max; + } + // Perhaps this could be done better, but I think its good enough for tests... + futures::executor::block_on(t.run()); + } + let (handle, _) = AbortHandle::new_pair(); + handle + } + + fn get_current_timestamp(&self) -> Duration { + self.inner.lock().now + } + + async fn sleep(&self, t: Duration) { + let inner = self.inner.clone(); + let mut inner = inner.lock(); + inner.now += t; + if inner.now > inner.max { + inner.now = inner.max; + } + } +} + +impl Default for SimulatedTimeService { + fn default() -> Self { + Self::new() + } +} + +impl SimulatedTimeService { + /// Creates new SimulatedTimeService in disabled state (time not running) + pub fn new() -> SimulatedTimeService { + SimulatedTimeService { + inner: Arc::new(Mutex::new(SimulatedTimeServiceInner { + now: Duration::from_secs(0), + pending: vec![], + time_limit: Duration::from_secs(0), + max: Duration::from_secs(std::u64::MAX), + })), + } + } + + /// Creates new SimulatedTimeService that automatically advance time up to time_limit + pub fn auto_advance_until(time_limit: Duration) -> SimulatedTimeService { + SimulatedTimeService { + inner: Arc::new(Mutex::new(SimulatedTimeServiceInner { + now: Duration::from_secs(0), + pending: vec![], + time_limit, + max: Duration::from_secs(std::u64::MAX), + })), + } + } + + /// Update time_limit of this SimulatedTimeService instance and run pending tasks that has + /// deadline lower then new time_limit + #[allow(dead_code)] + pub fn update_auto_advance_limit(&mut self, time: Duration) { + let mut inner = self.inner.lock(); + inner.time_limit += time; + let time_limit = inner.time_limit; + let mut i = 0; + let mut drain = vec![]; + while i != inner.pending.len() { + let deadline = inner.pending[i].0; + if deadline <= time_limit { + drain.push(inner.pending.remove(i)); + } else { + i += 1; + } + } + for (_, mut t) in drain { + // probably could be done better then that, but for now I feel its good enough for tests + futures::executor::block_on(t.run()); + } + } +} + +impl Clone for SimulatedTimeService { + fn clone(&self) -> SimulatedTimeService { + SimulatedTimeService { + inner: self.inner.clone(), + } + } +} diff --git a/consensus/src/util/mod.rs b/consensus/src/util/mod.rs new file mode 100644 index 0000000000000..a469f121916b2 --- /dev/null +++ b/consensus/src/util/mod.rs @@ -0,0 +1,24 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use aptos_types::{ + on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig}, + validator_txn::ValidatorTransaction, +}; + +pub mod db_tool; +#[cfg(any(test, feature = "fuzzing"))] +pub mod mock_time_service; +pub mod time_service; + +pub fn is_vtxn_expected( + randomness_config: &OnChainRandomnessConfig, + jwk_consensus_config: &OnChainJWKConsensusConfig, + vtxn: &ValidatorTransaction, +) -> bool { + match vtxn { + ValidatorTransaction::DKGResult(_) => randomness_config.randomness_enabled(), + ValidatorTransaction::ObservedJWKUpdate(_) => jwk_consensus_config.jwk_consensus_enabled(), + } +} diff --git a/consensus/src/util/time_service.rs b/consensus/src/util/time_service.rs new file mode 100644 index 0000000000000..d90fd6a54c008 --- /dev/null +++ b/consensus/src/util/time_service.rs @@ -0,0 +1,148 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::counters; +use aptos_logger::prelude::*; +use async_trait::async_trait; +use futures::{ + future::{AbortHandle, Abortable}, + Future, FutureExt, SinkExt, +}; +use std::{pin::Pin, time::Duration}; +use tokio::{runtime::Handle, time::sleep}; + +/// Time service is an abstraction for operations that depend on time +/// It supports implementations that can simulated time or depend on actual time +/// We can use simulated time in tests so tests can run faster and be more stable. +/// see SimulatedTime for implementation that tests should use +/// Time service also supports opportunities for future optimizations +/// For example instead of scheduling O(N) tasks in TaskExecutor we could have more optimal code +/// that only keeps single task in TaskExecutor +#[async_trait] +pub trait TimeService: Send + Sync { + /// Sends message to given sender after timeout, returns a handle that could use to cancel the task. + fn run_after(&self, timeout: Duration, task: Box) -> AbortHandle; + + /// Retrieve the current time stamp as a Duration (assuming it is on or after the UNIX_EPOCH) + fn get_current_timestamp(&self) -> Duration; + + /// Makes a future that will sleep for given Duration + /// This function guarantees that get_current_timestamp will increase at least by + /// given duration, e.g. + /// X = time_service::get_current_timestamp(); + /// time_service::sleep(Y).await; + /// Z = time_service::get_current_timestamp(); + /// assert(Z >= X + Y) + async fn sleep(&self, t: Duration); + + /// Wait until the Duration t since UNIX_EPOCH pass at least 1ms. + async fn wait_until(&self, t: Duration) { + while let Some(mut wait_duration) = t.checked_sub(self.get_current_timestamp()) { + wait_duration += Duration::from_millis(1); + counters::WAIT_DURATION_S.observe_duration(wait_duration); + self.sleep(wait_duration).await; + } + } +} + +/// This trait represents abstract task that can be submitted to TimeService::run_after +pub trait ScheduledTask: Send { + /// TimeService::run_after will run this method when time expires + /// It is expected that this function is lightweight and does not take long time to complete + fn run(&mut self) -> Pin + Send>>; +} + +/// This tasks send message to given Sender +pub struct SendTask +where + T: Send + 'static, +{ + sender: Option>, + message: Option, +} + +impl SendTask +where + T: Send + 'static, +{ + /// Makes new SendTask for given sender and message and wraps it to Box + pub fn make(sender: aptos_channels::Sender, message: T) -> Box { + Box::new(SendTask { + sender: Some(sender), + message: Some(message), + }) + } +} + +impl ScheduledTask for SendTask +where + T: Send + 'static, +{ + fn run(&mut self) -> Pin + Send>> { + let mut sender = self.sender.take().unwrap(); + let message = self.message.take().unwrap(); + let r = async move { + if let Err(e) = sender.send(message).await { + error!("Error on send: {:?}", e); + }; + }; + r.boxed() + } +} + +/// TimeService implementation that uses actual clock to schedule tasks +pub struct ClockTimeService { + executor: Handle, +} + +impl ClockTimeService { + /// Creates new TimeService that runs tasks based on actual clock + /// It needs executor to schedule internal tasks that facilitates it's work + pub fn new(executor: Handle) -> ClockTimeService { + ClockTimeService { executor } + } +} + +#[async_trait] +impl TimeService for ClockTimeService { + fn run_after(&self, timeout: Duration, mut t: Box) -> AbortHandle { + let (abort_handle, abort_registration) = AbortHandle::new_pair(); + let task = Abortable::new( + async move { + sleep(timeout).await; + t.run().await; + }, + abort_registration, + ); + self.executor.spawn(task); + abort_handle + } + + fn get_current_timestamp(&self) -> Duration { + aptos_infallible::duration_since_epoch() + } + + async fn sleep(&self, t: Duration) { + sleep(t).await + } +} + +#[tokio::test] +async fn test_time_service_abort() { + use futures::StreamExt; + + let time_service = ClockTimeService::new(tokio::runtime::Handle::current()); + let (tx, mut rx) = aptos_channels::new_test(10); + let task1 = SendTask::make(tx.clone(), 1); + let task2 = SendTask::make(tx.clone(), 2); + let handle1 = time_service.run_after(Duration::from_millis(100), task1); + let handle2 = time_service.run_after(Duration::from_millis(200), task2); + handle1.abort(); + // task 1 is aborted + assert_eq!(rx.next().await, Some(2)); + drop(tx); + assert_eq!(rx.next().await, None); + // abort an already finished task is no-op + handle2.abort(); +} diff --git a/crates/aptos-crypto/Cargo.toml b/crates/aptos-crypto/Cargo.toml index d7462f071780e..eacada812fa56 100644 --- a/crates/aptos-crypto/Cargo.toml +++ b/crates/aptos-crypto/Cargo.toml @@ -46,6 +46,7 @@ proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } rand = { workspace = true } rand_core = { workspace = true } +ring = { workspace = true } serde = { workspace = true } serde-name = { workspace = true } serde_bytes = { workspace = true } @@ -105,6 +106,10 @@ harness = false name = "hash" harness = false +[[bench]] +name = "noise" +harness = false + [[bench]] name = "ristretto255" harness = false diff --git a/crates/aptos-crypto/README.md b/crates/aptos-crypto/README.md index b470897ac7526..57e5aa1d73315 100644 --- a/crates/aptos-crypto/README.md +++ b/crates/aptos-crypto/README.md @@ -23,6 +23,8 @@ Aptos makes use of several cryptographic algorithms: - **Boneh-Shacham-Lynn (BLS) multisignatures and aggregate signatures** + Based on the [blst](https://docs.rs/blst/) crate + Implemented on top of Barreto-Lynn-Scott BLS12-381 elliptic curves +- The **[Noise Protocol Framework](http://www.noiseprotocol.org/)** + - Used to create authenticated and encrypted communications channels between validators - **X25519** key exchange + Based on the [x25519-dalek](https://docs.rs/x25519-dalek) crate + Used in our implementation of the [Noise Protocol Framework](http://www.noiseprotocol.org/) @@ -41,6 +43,7 @@ Before implementing a cryptographic primitive, be sure to read [`traits.rs`](src ├── hash.rs # Hash function (SHA-3) ├── hkdf.rs # HKDF implementation ├── multi_ed25519.rs # MultiEd25519 implementation of the signing/verification API in traits.rs + ├── noise.rs # Noise Protocol Framework implementation ├── test_utils.rs ├── traits.rs # Traits for safer implementations of signature schemes ├── validatable.rs # Traits for deferring validation of group elements (e.g., public keys, signatures) diff --git a/crates/aptos-crypto/benches/noise.rs b/crates/aptos-crypto/benches/noise.rs new file mode 100644 index 0000000000000..e4f212aa0ef09 --- /dev/null +++ b/crates/aptos-crypto/benches/noise.rs @@ -0,0 +1,132 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Don't forget to run this benchmark with AES-NI enable. +//! You can do this by building with the following flags: +//! `RUSTFLAGS="-Ctarget-cpu=skylake -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"`. +//! + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{ + noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, AES_GCM_TAGLEN}, + test_utils::TEST_SEED, + x25519, Uniform as _, ValidCryptoMaterial as _, +}; +use criterion::{Criterion, Throughput}; +use rand::SeedableRng; +use std::convert::TryFrom as _; + +const MSG_SIZE: usize = 4096; + +fn benchmarks(c: &mut Criterion) { + // bench the handshake + let mut group = c.benchmark_group("noise-handshake"); + group.throughput(Throughput::Elements(1)); + group.bench_function("connect", |b| { + // setup keys first + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let initiator_static = initiator_static.to_bytes(); + let responder_static = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_static.public_key(); + let responder_static = responder_static.to_bytes(); + + let mut first_message = [0u8; handshake_init_msg_len(0)]; + let mut second_message = [0u8; handshake_resp_msg_len(0)]; + + b.iter(|| { + let initiator_static = + x25519::PrivateKey::try_from(initiator_static.clone().as_slice()).unwrap(); + let responder_static = + x25519::PrivateKey::try_from(responder_static.clone().as_slice()).unwrap(); + + let initiator = NoiseConfig::new(initiator_static); + let responder = NoiseConfig::new(responder_static); + + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"prologue", + responder_public, + None, + &mut first_message, + ) + .unwrap(); + + let _ = responder + .respond_to_client_and_finalize( + &mut rng, + b"prologue", + &first_message, + None, + &mut second_message, + ) + .unwrap(); + let _ = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + }) + }); + group.finish(); + + let mut transport_group = c.benchmark_group("noise-transport"); + transport_group.throughput(Throughput::Bytes(MSG_SIZE as u64 * 2)); + transport_group.bench_function("AES-GCM throughput", |b| { + let mut buffer_msg = [0u8; MSG_SIZE + AES_GCM_TAGLEN]; + + // setup keys first + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let responder_static = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_static.public_key(); + + // handshake first + let initiator = NoiseConfig::new(initiator_static); + let responder = NoiseConfig::new(responder_static); + + let mut first_message = [0u8; handshake_init_msg_len(0)]; + let mut second_message = [0u8; handshake_resp_msg_len(0)]; + + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"prologue", + responder_public, + None, + &mut first_message, + ) + .unwrap(); + let (_, mut responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + b"prologue", + &first_message, + None, + &mut second_message, + ) + .unwrap(); + let (_, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + + // bench throughput post-handshake + b.iter(move || { + let auth_tag = initiator_session + .write_message_in_place(&mut buffer_msg[..MSG_SIZE]) + .expect("session should not be closed"); + + buffer_msg[MSG_SIZE..MSG_SIZE + AES_GCM_TAGLEN].copy_from_slice(&auth_tag); + + let _plaintext = responder_session + .read_message_in_place(&mut buffer_msg[..MSG_SIZE + AES_GCM_TAGLEN]) + .expect("session should not be closed"); + }) + }); + transport_group.finish(); +} + +criterion_group!(benches, benchmarks); +criterion_main!(benches); diff --git a/crates/aptos-crypto/src/lib.rs b/crates/aptos-crypto/src/lib.rs index c86f6a1ecdb8c..2141f191d4a14 100644 --- a/crates/aptos-crypto/src/lib.rs +++ b/crates/aptos-crypto/src/lib.rs @@ -17,6 +17,7 @@ pub mod error; pub mod hash; pub mod hkdf; pub mod multi_ed25519; +pub mod noise; pub mod secp256k1_ecdsa; pub mod secp256r1_ecdsa; pub mod test_utils; diff --git a/network/framework/src/noise/crypto.rs b/crates/aptos-crypto/src/noise.rs similarity index 98% rename from network/framework/src/noise/crypto.rs rename to crates/aptos-crypto/src/noise.rs index aeafef6597401..13a08e493e7b3 100644 --- a/network/framework/src/noise/crypto.rs +++ b/crates/aptos-crypto/src/noise.rs @@ -19,11 +19,10 @@ //! Usage example: //! //! ``` -//! use aptos_network::noise; -//! use aptos_crypto::{x25519, traits::*}; +//! use aptos_crypto::{noise, x25519, traits::*}; //! use rand::prelude::*; //! -//! # fn main() -> Result<(), noise::NoiseError> { +//! # fn main() -> Result<(), aptos_crypto::noise::NoiseError> { //! let mut rng = rand::thread_rng(); //! let initiator_static = x25519::PrivateKey::generate(&mut rng); //! let responder_static = x25519::PrivateKey::generate(&mut rng); @@ -64,9 +63,7 @@ //! #![allow(clippy::arithmetic_side_effects)] -use aptos_crypto::{ - hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519, ValidCryptoMaterial, -}; +use crate::{hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519, ValidCryptoMaterial}; use ring::aead::{self, Aad, LessSafeKey, UnboundKey}; use sha2::Digest; use std::{ @@ -558,7 +555,6 @@ impl NoiseConfig { /// This function is a one-call that replaces calling the two functions parse_client_init_message /// and respond_to_client consecutively - #[allow(dead_code)] pub fn respond_to_client_and_finalize( &self, rng: &mut (impl rand::RngCore + rand::CryptoRng), diff --git a/crates/aptos-crypto/src/unit_tests/mod.rs b/crates/aptos-crypto/src/unit_tests/mod.rs index 87423cb6540f1..34cf3348c431f 100644 --- a/crates/aptos-crypto/src/unit_tests/mod.rs +++ b/crates/aptos-crypto/src/unit_tests/mod.rs @@ -11,5 +11,6 @@ mod ed25519_test; mod hash_test; mod hkdf_test; mod multi_ed25519_test; +mod noise_test; mod secp256k1_ecdsa_test; mod secp256r1_ecdsa_test; diff --git a/crates/aptos-crypto/src/unit_tests/noise_test.rs b/crates/aptos-crypto/src/unit_tests/noise_test.rs new file mode 100644 index 0000000000000..c5245eeb54972 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/noise_test.rs @@ -0,0 +1,532 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, MAX_SIZE_NOISE_MSG}, + test_utils::TEST_SEED, + x25519, Uniform as _, +}; +use rand::SeedableRng; +use serde::*; +use std::{fs::File, io::BufReader, path::PathBuf}; + +#[test] +fn simple_handshake() { + // setup peers + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_private = x25519::PrivateKey::generate(&mut rng); + let initiator_public = initiator_private.public_key(); + let responder_private = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_private.public_key(); + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // initiator sends first message + let prologue = b"prologue"; + let payload1 = b"payload1"; + let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + prologue, + responder_public, + Some(payload1), + &mut first_message, + ) + .unwrap(); + + let payload2 = b"payload2"; + let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; + + // responder parses the first message and responds + let mut responder_session = if i == 0 { + let (received_payload, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + prologue, + &first_message, + Some(payload2), + &mut second_message, + ) + .unwrap(); + let remote_static = responder_session.get_remote_static(); + assert_eq!(remote_static, initiator_public); + assert_eq!(received_payload, b"payload1"); + responder_session + } else { + let payload2 = b"payload2"; + let (remote_static, handshake_state, received_payload) = responder + .parse_client_init_message(prologue, &first_message) + .unwrap(); + assert_eq!(remote_static, initiator_public); + assert_eq!(received_payload, b"payload1"); + + responder + .respond_to_client( + &mut rng, + handshake_state, + Some(payload2), + &mut second_message, + ) + .unwrap() + }; + + // initiator parses the response + let (received_payload, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + assert_eq!(received_payload, b"payload2"); + + // session usage + let mut message_sent = b"payload".to_vec(); + for i in 0..10 { + message_sent.push(i); + let mut message = message_sent.clone(); + let received_message = if i % 2 == 0 { + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + responder_session + .read_message_in_place(&mut message) + .expect("session should not be closed") + } else { + let auth_tag = responder_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + initiator_session + .read_message_in_place(&mut message) + .expect("session should not be closed") + }; + assert_eq!(received_message, message_sent.as_slice()); + } + } +} + +#[test] +fn test_vectors() { + // structures needed to deserialize test vectors + #[derive(Serialize, Deserialize)] + struct TestVectors { + vectors: Vec, + } + #[derive(Serialize, Deserialize, Debug)] + struct TestVector { + protocol_name: String, + init_prologue: String, + init_static: Option, + init_ephemeral: String, + init_remote_static: Option, + resp_static: Option, + resp_ephemeral: Option, + handshake_hash: String, + messages: Vec, + } + #[derive(Serialize, Deserialize, Debug)] + struct TestMessage { + payload: String, + ciphertext: String, + } + + // EphemeralRng is used to get deterministic ephemeral keys based on test vectors + struct EphemeralRng { + ephemeral: Vec, + } + impl rand::RngCore for EphemeralRng { + fn next_u32(&mut self) -> u32 { + unreachable!() + } + + fn next_u64(&mut self) -> u64 { + unreachable!() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.copy_from_slice(&self.ephemeral); + } + + fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), rand::Error> { + unreachable!() + } + } + impl rand::CryptoRng for EphemeralRng {} + + // test vectors are taken from the cacophony library + let mut test_vectors_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + test_vectors_path.push("test_vectors"); + test_vectors_path.push("noise_cacophony.txt"); + let test_vectors_path = test_vectors_path.to_str().unwrap(); + let test_vectors_file = File::open(test_vectors_path).expect("missing noise test vectors"); + let test_vectors: TestVectors = + serde_json::from_reader(BufReader::new(test_vectors_file)).unwrap(); + + // only go through Noise_IK_25519_AESGCM_SHA256 test vectors (don't exist for SHA-3) + let test_vector = test_vectors + .vectors + .iter() + .find(|vector| vector.protocol_name == "Noise_IK_25519_AESGCM_SHA256") + .expect("test vector for Noise_IK_25519_AESGCM_SHA256 should be in cacophony test vectors"); + + // initiate peers with test vector + use crate::traits::ValidCryptoMaterialStringExt; + let initiator_private = + x25519::PrivateKey::from_encoded_string(test_vector.init_static.as_ref().unwrap()).unwrap(); + let initiator_public = initiator_private.public_key(); + let responder_private = + x25519::PrivateKey::from_encoded_string(test_vector.resp_static.as_ref().unwrap()).unwrap(); + let responder_public = responder_private.public_key(); + + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // assert public keys + let init_remote_static = + hex::decode(test_vector.init_remote_static.as_ref().unwrap()).unwrap(); + assert_eq!(responder_public.as_slice(), init_remote_static.as_slice()); + + // go through handshake test messages + let prologue = hex::decode(&test_vector.init_prologue).unwrap(); + let mut messages = test_vector.messages.iter(); + + // first handshake message + let message = messages.next().unwrap(); + let payload1 = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + let init_ephemeral = hex::decode(&test_vector.init_ephemeral).unwrap(); + let mut rng = EphemeralRng { + ephemeral: init_ephemeral, + }; + let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + &prologue, + responder_public, + Some(&payload1), + &mut first_message, + ) + .unwrap(); + assert_eq!(first_message, expected_ciphertext); + + // second handshake message + let message = messages.next().unwrap(); + let payload2 = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + + // responder part + let resp_ephemeral = hex::decode(test_vector.resp_ephemeral.as_ref().unwrap()).unwrap(); + let mut rng = EphemeralRng { + ephemeral: resp_ephemeral, + }; + let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; + + let mut responder_session = if i == 0 { + let (received_payload, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + &prologue, + &first_message, + Some(&payload2), + &mut second_message, + ) + .unwrap(); + assert_eq!(payload1, received_payload); + responder_session + } else { + let (remote_static, handshake_state, received_payload) = responder + .parse_client_init_message(&prologue, &first_message) + .unwrap(); + assert_eq!(remote_static, initiator_public); + assert_eq!(payload1, received_payload); + + responder + .respond_to_client( + &mut rng, + handshake_state, + Some(&payload2), + &mut second_message, + ) + .unwrap() + }; + + let remote_static = responder_session.get_remote_static(); + assert_eq!(second_message, expected_ciphertext); + assert_eq!(remote_static, initiator_public); + + // initiator part + let (received_payload, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + assert_eq!(payload2, received_payload); + + // post-handshake messages + let mut client_turn = true; + for message in messages { + // decode + let payload = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + + // initiator and responder takes turn to send messages + let mut message = payload.clone(); + if client_turn { + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + assert_eq!(message, expected_ciphertext); + + let received_payload = responder_session + .read_message_in_place(&mut message) + .expect("session should not be closed"); + assert_eq!(payload, received_payload); + } else { + let auth_tag = responder_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + assert_eq!(message, expected_ciphertext); + + let received_payload = initiator_session + .read_message_in_place(&mut message) + .expect("session should not be closed"); + assert_eq!(payload, received_payload); + } + + // swap sender + client_turn = !client_turn; + } + } +} + +// Negative tests +// -------------- +// +// things that should fail during the handshake: +// - buffer to write is too small (should fail) +// - message received is too small (should fail) +// - message received is too big (should fail) +// - message received is larger than max noise size (should fail) +// - payload to write is larger than max noise size (should fail) +// +// things that should work during the handshake: +// - buffer to write is too big +// + +#[test] +fn wrong_buffer_sizes() { + // setup peers + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_private = x25519::PrivateKey::generate(&mut rng); + let responder_private = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_private.public_key(); + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // initiator sends first message with buffer too small (should fail) + let payload = b"payload"; + let mut first_message_bad = vec![0u8; handshake_init_msg_len(payload.len()) - 1]; + let res = initiator.initiate_connection( + &mut rng, + b"", + responder_public, + Some(payload), + &mut first_message_bad, + ); + + assert!(res.is_err()); + + // try again with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_init_msg_len(0) + 1]; + let res = initiator.initiate_connection( + &mut rng, + b"", + responder_public, + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // try again with buffer too large (should work) + let mut first_message_good = vec![0u8; handshake_init_msg_len(payload.len()) + 1]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"", + responder_public, + Some(payload), + &mut first_message_good, + ) + .unwrap(); + + // responder parses the first message and responds + let mut second_message_small = vec![0u8; handshake_resp_msg_len(payload.len()) - 1]; + let mut second_message_large = vec![0u8; handshake_resp_msg_len(payload.len()) + 1]; + + let (mut responder_session, second_message_large) = if i == 0 { + // with buffer too small (shouldn't work) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_small, + ); + + assert!(res.is_err()); + + // with first message too large (shouldn't work) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good, + Some(payload), + &mut second_message_large, + ); + + assert!(res.is_err()); + + // with incorrect prologue (should fail) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"incorrect prologue", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_large, + ); + + assert!(res.is_err()); + + // with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // with correct first message and buffer too large (should work) + let (_, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_large, + ) + .unwrap(); + + (responder_session, second_message_large) + } else { + // with first message too large + let res = responder.parse_client_init_message(b"", &first_message_good); + + assert!(res.is_err()); + + // with first message too small + let res = responder.parse_client_init_message( + b"", + &first_message_good[..first_message_good.len() - 2], + ); + + assert!(res.is_err()); + + // with wrong prologue + let res = responder.parse_client_init_message( + b"incorrect prologue", + &first_message_good[..first_message_good.len() - 1], + ); + + assert!(res.is_err()); + + // with first message of correct length + let (_, handshake_state, _) = responder + .parse_client_init_message(b"", &first_message_good[..first_message_good.len() - 1]) + .unwrap(); + + // write to buffer to small (should fail) + let res = responder.respond_to_client( + &mut rng, + handshake_state.clone(), + Some(payload), + &mut second_message_small, + ); + + assert!(res.is_err()); + + // with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; + let res = responder.respond_to_client( + &mut rng, + handshake_state.clone(), + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // write to buffer too big (should work) + let responder_session = responder + .respond_to_client( + &mut rng, + handshake_state, + Some(payload), + &mut second_message_large, + ) + .unwrap(); + + (responder_session, second_message_large) + }; + + // initiator parses the response too large (should fail) + let res = initiator.finalize_connection(initiator_state.clone(), &second_message_large); + + assert!(res.is_err()); + + // initiator parses the response too small (should fail) + let res = initiator.finalize_connection( + initiator_state.clone(), + &second_message_large[..second_message_large.len() - 2], + ); + + assert!(res.is_err()); + + // initiator parses response of correct size + let (_, mut initiator_session) = initiator + .finalize_connection( + initiator_state.clone(), + &second_message_large[..second_message_large.len() - 1], + ) + .unwrap(); + + // session usage + let mut message = b"".to_vec(); + + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("should work"); + + // message too short to have auth tag + let res = responder_session.read_message_in_place(&mut message); + assert!(res.is_err()); + + // session should be unusable now + message.extend_from_slice(&auth_tag); + let res = responder_session.read_message_in_place(&mut message); + assert!(res.is_err()); + } +} diff --git a/crates/aptos-debugger/src/lib.rs b/crates/aptos-debugger/src/lib.rs new file mode 100644 index 0000000000000..ecfc35d8ce81e --- /dev/null +++ b/crates/aptos-debugger/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use clap::Parser; + +#[derive(Parser)] +pub enum Cmd { + #[clap(subcommand)] + AptosDb(aptos_db_tool::DBTool), + + Decode(aptos_move_debugger::bcs_txn_decoder::Command), + + DumpPendingTxns(aptos_consensus::util::db_tool::Command), + + #[clap(subcommand)] + Move(aptos_move_debugger::common::Command), +} + +impl Cmd { + pub async fn run(self) -> Result<()> { + match self { + Cmd::AptosDb(cmd) => cmd.run().await, + Cmd::Decode(cmd) => cmd.run().await, + Cmd::DumpPendingTxns(cmd) => cmd.run().await, + Cmd::Move(cmd) => cmd.run().await, + } + } +} + +#[test] +fn verify_tool() { + use clap::CommandFactory; + Cmd::command().debug_assert() +} diff --git a/crates/aptos-telemetry-service/src/auth.rs b/crates/aptos-telemetry-service/src/auth.rs new file mode 100644 index 0000000000000..1a16a4f0328ba --- /dev/null +++ b/crates/aptos-telemetry-service/src/auth.rs @@ -0,0 +1,205 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + context::Context, + debug, error, + errors::{AuthError, ServiceError, ServiceErrorCode}, + jwt_auth::{authorize_jwt, create_jwt_token, jwt_from_header}, + types::{ + auth::{AuthRequest, AuthResponse, Claims}, + common::NodeType, + }, + warn, +}; +use anyhow::Result; +use aptos_config::config::{PeerRole, RoleType}; +use aptos_crypto::{noise, x25519}; +use aptos_types::{chain_id::ChainId, PeerId}; +use reqwest::header::AUTHORIZATION; +use warp::{filters::BoxedFilter, reject, reply, Filter, Rejection, Reply}; + +pub fn auth(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path!("auth") + .and(warp::post()) + .and(context.filter()) + .and(warp::body::json()) + .and_then(handle_auth) + .boxed() +} + +pub async fn handle_auth(context: Context, body: AuthRequest) -> Result { + debug!("received auth request: {:?}", body); + + let client_init_message = &body.handshake_msg; + + // Check whether the client (validator) is using the correct server's public key, which the + // client includes in its request body. + // This is useful for returning a refined error response to the client if it is using an + // invalid server public key. + if body.server_public_key != context.noise_config().public_key() { + return Err(reject::custom(ServiceError::bad_request( + ServiceErrorCode::AuthError(AuthError::InvalidServerPublicKey, body.chain_id), + ))); + } + + // build the prologue (chain_id | peer_id | public_key) + const CHAIN_ID_LENGTH: usize = 1; + const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; + const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; + let mut prologue = [0; PROLOGUE_SIZE]; + prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[body.chain_id.id()]); + prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(body.peer_id.as_ref()); + prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(body.server_public_key.as_slice()); + + let (remote_public_key, handshake_state, _payload) = context + .noise_config() + .parse_client_init_message(&prologue, client_init_message) + .map_err(|e| { + debug!("error performing noise handshake: {}", e); + reject::custom(ServiceError::bad_request(ServiceErrorCode::AuthError( + AuthError::NoiseHandshakeError(e), + body.chain_id, + ))) + })?; + + let cache = if body.role_type == RoleType::Validator { + context.peers().validators() + } else { + context.peers().validator_fullnodes() + }; + + let (epoch, peer_role) = match cache.read().get(&body.chain_id) { + Some((epoch, peer_set)) => { + match peer_set.get(&body.peer_id) { + Some(peer) => { + let remote_public_key = &remote_public_key; + if !peer.keys.contains(remote_public_key) { + warn!("peer found in peer set but public_key is not found. request body: {}, role_type: {}, peer_id: {}, received public_key: {}", body.chain_id, body.role_type, body.peer_id, remote_public_key); + return Err(reject::custom(ServiceError::forbidden( + ServiceErrorCode::AuthError( + AuthError::PeerPublicKeyNotFound, + body.chain_id, + ), + ))); + } + Ok((*epoch, peer.role)) + }, + None => { + // if not, verify that their peerid is constructed correctly from their public key + let derived_remote_peer_id = + aptos_types::account_address::from_identity_public_key(remote_public_key); + if derived_remote_peer_id != body.peer_id { + return Err(reject::custom(ServiceError::forbidden( + ServiceErrorCode::AuthError( + AuthError::PublicKeyMismatch, + body.chain_id, + ), + ))); + } else { + Ok((*epoch, PeerRole::Unknown)) + } + }, + } + }, + None => { + warn!( + "Validator set unavailable for Chain ID {}. Rejecting request.", + body.chain_id + ); + Err(reject::custom(ServiceError::unauthorized( + ServiceErrorCode::AuthError(AuthError::ValidatorSetUnavailable, body.chain_id), + ))) + }, + }?; + + let node_type = match peer_role { + PeerRole::Validator => NodeType::Validator, + PeerRole::ValidatorFullNode => NodeType::ValidatorFullNode, + PeerRole::Unknown => match body.role_type { + RoleType::Validator => NodeType::UnknownValidator, + RoleType::FullNode => context + .peers() + .public_fullnodes() + .get(&body.chain_id) + .and_then(|peer_set| { + if peer_set.contains_key(&body.peer_id) { + Some(NodeType::PublicFullNode) + } else { + None + } + }) + .unwrap_or(NodeType::UnknownFullNode), + }, + _ => NodeType::Unknown, + }; + + let token = create_jwt_token( + context.jwt_service(), + body.chain_id, + body.peer_id, + node_type, + epoch, + body.run_uuid, + ) + .map_err(|e| { + error!("unable to create jwt token: {}", e); + reject::custom(ServiceError::internal(ServiceErrorCode::AuthError( + AuthError::from(e), + body.chain_id, + ))) + })?; + + let mut rng = rand::rngs::OsRng; + let response_payload = token.as_bytes(); + let mut server_response = vec![0u8; noise::handshake_resp_msg_len(response_payload.len())]; + context + .noise_config() + .respond_to_client( + &mut rng, + handshake_state, + Some(response_payload), + &mut server_response, + ) + .map_err(|e| { + error!("unable to complete handshake {}", e); + ServiceError::internal(ServiceErrorCode::AuthError( + AuthError::NoiseHandshakeError(e), + body.chain_id, + )) + })?; + + Ok(reply::json(&AuthResponse { + handshake_msg: server_response, + })) +} + +pub fn with_auth( + context: Context, + roles: Vec, +) -> impl Filter + Clone { + warp::header::optional(AUTHORIZATION.as_str()) + .and_then(jwt_from_header) + .and( + warp::any() + .map(move || (context.clone(), roles.clone())) + .untuple_one(), + ) + .and_then(authorize_jwt) +} + +pub fn check_chain_access(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path!("chain-access" / ChainId) + .and(warp::get()) + .and(context.filter()) + .and_then(handle_check_chain_access) + .boxed() +} + +async fn handle_check_chain_access( + chain_id: ChainId, + context: Context, +) -> Result { + let present = context.chain_set().contains(&chain_id); + Ok(reply::json(&present)) +} diff --git a/crates/aptos-telemetry-service/src/clients/humio.rs b/crates/aptos-telemetry-service/src/clients/humio.rs new file mode 100644 index 0000000000000..935f20796fac3 --- /dev/null +++ b/crates/aptos-telemetry-service/src/clients/humio.rs @@ -0,0 +1,55 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::humio::UnstructuredLog; +use anyhow::anyhow; +use flate2::{write::GzEncoder, Compression}; +use reqwest::{Client as ReqwestClient, Url}; +use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; +use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; + +pub const PEER_ID_FIELD_NAME: &str = "peer_id"; +pub const EPOCH_FIELD_NAME: &str = "epoch"; +pub const PEER_ROLE_TAG_NAME: &str = "peer_role"; +pub const CHAIN_ID_TAG_NAME: &str = "chain_id"; +pub const RUN_UUID_TAG_NAME: &str = "run_uuid"; + +#[derive(Clone)] +pub struct IngestClient { + inner: ClientWithMiddleware, + base_url: Url, + auth_token: String, +} + +impl IngestClient { + pub fn new(base_url: Url, auth_token: String) -> Self { + let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3); + let inner = ClientBuilder::new(ReqwestClient::new()) + .with(RetryTransientMiddleware::new_with_policy(retry_policy)) + .build(); + Self { + inner, + base_url, + auth_token, + } + } + + pub async fn ingest_unstructured_log( + &self, + unstructured_log: UnstructuredLog, + ) -> Result { + let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); + serde_json::to_writer(&mut gzip_encoder, &vec![unstructured_log]) + .map_err(|e| anyhow!("unable to serialize json: {}", e))?; + let compressed_bytes = gzip_encoder.finish()?; + + self.inner + .post(self.base_url.join("api/v1/ingest/humio-unstructured")?) + .bearer_auth(self.auth_token.clone()) + .header("Content-Encoding", "gzip") + .body(compressed_bytes) + .send() + .await + .map_err(|e| anyhow!("failed to post metrics: {}", e)) + } +} diff --git a/crates/aptos-telemetry-service/src/constants.rs b/crates/aptos-telemetry-service/src/constants.rs new file mode 100644 index 0000000000000..eff0370e77348 --- /dev/null +++ b/crates/aptos-telemetry-service/src/constants.rs @@ -0,0 +1,19 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +/// The maximum content length to accept in the http body. +pub const MAX_CONTENT_LENGTH: u64 = 1024 * 1024; + +/// GCP Header field for the current request's trace ID. +pub const GCP_CLOUD_TRACE_CONTEXT_HEADER: &str = "X-Cloud-Trace-Context"; + +/// GCP Cloud Run env variable for the current deployment revision +pub const GCP_CLOUD_RUN_REVISION_ENV: &str = "K_REVISION"; +/// GCP Cloud Run env variable for service name +pub const GCP_CLOUD_RUN_SERVICE_ENV: &str = "K_SERVICE"; +/// GCP Project within which this service is running. +/// This variable must be set by calling the metadata server +pub const GCP_SERVICE_PROJECT_ID_ENV: &str = "GCP_METADATA_PROJECT_ID"; +/// Environment variable with the container identifier for this cloud run revision +/// This variable must be set by calling the metadata server +pub const GCP_CLOUD_RUN_INSTANCE_ID_ENV: &str = "GCP_CLOUD_RUN_INSTANCE_ID"; diff --git a/crates/aptos-telemetry-service/src/custom_event.rs b/crates/aptos-telemetry-service/src/custom_event.rs new file mode 100644 index 0000000000000..e4ffba5f79796 --- /dev/null +++ b/crates/aptos-telemetry-service/src/custom_event.rs @@ -0,0 +1,222 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + auth::with_auth, + context::Context, + debug, error, + errors::{CustomEventIngestError, ServiceError}, + metrics::BIG_QUERY_BACKEND_REQUEST_DURATION, + types::{ + auth::Claims, + common::{EventIdentity, NodeType}, + telemetry::{BigQueryRow, TelemetryDump}, + }, +}; +use anyhow::anyhow; +use aptos_types::PeerId; +use gcp_bigquery_client::model::table_data_insert_all_request::TableDataInsertAllRequest; +use serde_json::json; +use std::{str::FromStr, time::Duration}; +use tokio::time::Instant; +use warp::{filters::BoxedFilter, hyper::StatusCode, reject, reply, Filter, Rejection, Reply}; + +pub fn custom_event_ingest(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path!("ingest" / "custom-event") + .and(warp::post()) + .and(context.clone().filter()) + .and(with_auth(context, vec![ + NodeType::Validator, + NodeType::ValidatorFullNode, + NodeType::PublicFullNode, + NodeType::Unknown, + NodeType::UnknownValidator, + NodeType::UnknownFullNode, + ])) + .and(warp::body::json()) + .and_then(handle_custom_event) + .boxed() +} + +fn validate_custom_event_body( + claims: &Claims, + body: &TelemetryDump, +) -> anyhow::Result<(), Rejection> { + let body_peer_id = PeerId::from_str(&body.user_id).map_err(|_| { + reject::custom(ServiceError::bad_request( + CustomEventIngestError::InvalidEvent(body.user_id.clone(), claims.peer_id).into(), + )) + })?; + if body_peer_id != claims.peer_id { + return Err(reject::custom(ServiceError::bad_request( + CustomEventIngestError::InvalidEvent(body.user_id.clone(), claims.peer_id).into(), + ))); + } + + if body.events.is_empty() { + return Err(reject::custom(ServiceError::bad_request( + CustomEventIngestError::EmptyPayload.into(), + ))); + } + + Ok(()) +} + +pub(crate) async fn handle_custom_event( + context: Context, + claims: Claims, + body: TelemetryDump, +) -> anyhow::Result { + validate_custom_event_body(&claims, &body)?; + + let mut insert_request = TableDataInsertAllRequest::new(); + + let telemetry_event = &body.events[0]; + let event_params: Vec = telemetry_event + .params + .iter() + .map(|(k, v)| { + json!({ + "key": k, + "value": v + }) + }) + .collect(); + + let duration = + Duration::from_micros(body.timestamp_micros.as_str().parse::().map_err(|_| { + ServiceError::bad_request( + CustomEventIngestError::InvalidTimestamp(body.timestamp_micros).into(), + ) + })?); + + let row = BigQueryRow { + event_identity: EventIdentity::from(claims), + event_name: telemetry_event.name.clone(), + event_timestamp: duration.as_secs(), + event_params, + }; + + insert_request.add_row(None, &row).map_err(|e| { + error!("unable to create row: {}", e); + ServiceError::internal(CustomEventIngestError::from(e).into()) + })?; + + let start_timer = Instant::now(); + + context + .bigquery_client() + .ok_or_else(|| { + error!("big query client is not configured"); + ServiceError::internal( + CustomEventIngestError::from(anyhow!("BQ client is not configured")).into(), + ) + })? + .insert_all(insert_request) + .await + .map_err(|e| { + BIG_QUERY_BACKEND_REQUEST_DURATION + .with_label_values(&["request_error"]) + .observe(start_timer.elapsed().as_millis() as f64); + error!("unable to insert row into bigquery: {}", e); + ServiceError::internal(CustomEventIngestError::from(e).into()) + }) + .and_then(|result| { + if let Some(err) = result.insert_errors { + BIG_QUERY_BACKEND_REQUEST_DURATION + .with_label_values(&["insert_error"]) + .observe(start_timer.elapsed().as_secs_f64()); + Err(ServiceError::bad_request( + CustomEventIngestError::from(err[0].clone()).into(), + )) + } else { + BIG_QUERY_BACKEND_REQUEST_DURATION + .with_label_values(&["success"]) + .observe(start_timer.elapsed().as_secs_f64()); + Ok(result) + } + })?; + + debug!("row inserted succeefully: {:?}", &row); + + Ok(reply::with_status(reply::reply(), StatusCode::CREATED)) +} + +#[cfg(test)] +mod test { + use super::validate_custom_event_body; + use crate::types::{ + auth::Claims, + common::NodeType, + telemetry::{TelemetryDump, TelemetryEvent}, + }; + use aptos_types::{chain_id::ChainId, PeerId}; + use claims::assert_ok; + use std::collections::BTreeMap; + use uuid::Uuid; + + #[test] + fn test_validate_custom_event_body() { + let claims = Claims { + chain_id: ChainId::test(), + peer_id: PeerId::from_hex_literal("0x1234").unwrap(), + node_type: NodeType::Validator, + epoch: 1, + exp: 100, + iat: 200, + run_uuid: Uuid::new_v4(), + }; + + let body = TelemetryDump { + client_id: String::new(), + user_id: String::from("0x1"), + timestamp_micros: String::new(), + events: Vec::new(), + }; + assert_eq!(format!("{:?}", validate_custom_event_body(&claims, &body)), "Err(Rejection(ServiceError { http_code: 400, error_code: CustomEventIngestError(InvalidEvent(\"0x1\", 0000000000000000000000000000000000000000000000000000000000001234)) }))"); + + let body = TelemetryDump { + client_id: String::new(), + user_id: String::from("0x1234"), + timestamp_micros: String::new(), + events: vec![TelemetryEvent { + name: "test".into(), + params: BTreeMap::new(), + }], + }; + assert_ok!(validate_custom_event_body(&claims, &body)); + + let body = TelemetryDump { + client_id: String::new(), + user_id: String::from("1234"), + timestamp_micros: String::new(), + events: vec![TelemetryEvent { + name: "test".into(), + params: BTreeMap::new(), + }], + }; + assert_ok!(validate_custom_event_body(&claims, &body)); + + let body = TelemetryDump { + client_id: String::new(), + user_id: String::from("0x00001234"), + timestamp_micros: String::new(), + events: vec![TelemetryEvent { + name: "test".into(), + params: BTreeMap::new(), + }], + }; + assert_ok!(validate_custom_event_body(&claims, &body)); + + let body = TelemetryDump { + client_id: String::new(), + user_id: String::from("00001234"), + timestamp_micros: String::new(), + events: vec![TelemetryEvent { + name: "test".into(), + params: BTreeMap::new(), + }], + }; + assert_ok!(validate_custom_event_body(&claims, &body)); + } +} diff --git a/crates/aptos-telemetry-service/src/errors.rs b/crates/aptos-telemetry-service/src/errors.rs new file mode 100644 index 0000000000000..1237740576008 --- /dev/null +++ b/crates/aptos-telemetry-service/src/errors.rs @@ -0,0 +1,189 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::noise::NoiseError; +use aptos_rest_client::error::RestError; +use aptos_types::{chain_id::ChainId, PeerId}; +use debug_ignore::DebugIgnore; +use gcp_bigquery_client::{ + error::BQError, + model::table_data_insert_all_response_insert_errors::TableDataInsertAllResponseInsertErrors, +}; +use std::fmt::{self, Debug}; +use thiserror::Error as ThisError; +use warp::{http::StatusCode, reject::Reject}; + +#[derive(Debug, ThisError)] +pub(crate) enum AuthError { + #[error("invalid public key")] + InvalidServerPublicKey, + #[error("error performing noise handshake")] + NoiseHandshakeError(NoiseError), + #[error("public key not found in peer keys")] + PeerPublicKeyNotFound, + #[error("public key does not match identity")] + PublicKeyMismatch, + #[error("validator set unavailable for chain")] + ValidatorSetUnavailable, + #[error("unable to authenticate")] + CreateJwtError(DebugIgnore), +} + +impl From for AuthError { + fn from(val: jsonwebtoken::errors::Error) -> Self { + Self::CreateJwtError(DebugIgnore(val)) + } +} + +#[derive(Debug, ThisError)] +pub(crate) enum JwtAuthError { + #[error("invalid authorization token")] + InvalidAuthToken, + #[error("expired authorization token")] + ExpiredAuthToken, + #[error("access denied to this resource")] + AccessDenied, + #[error("invalid authorization header: {0}")] + InvalidAuthHeader(DebugIgnore), +} + +impl From for JwtAuthError { + fn from(val: String) -> Self { + Self::InvalidAuthHeader(DebugIgnore(val)) + } +} + +#[derive(Debug, ThisError)] +pub(crate) enum CustomEventIngestError { + #[error("user_id {0} in event does not match peer_id {1}")] + InvalidEvent(String, PeerId), + #[error("no events in payload")] + EmptyPayload, + #[error("invalid payload timestamp: {0}")] + InvalidTimestamp(String), + #[error("unable to insert row into big query")] + BigQueryClientError(DebugIgnore), + #[error("invalid payload schema: {0}")] + BigQueryInsertError(DebugIgnore), + #[error("{0}")] + Other(DebugIgnore), +} + +impl From for CustomEventIngestError { + fn from(err: BQError) -> Self { + Self::BigQueryClientError(DebugIgnore(err)) + } +} + +impl From for CustomEventIngestError { + fn from(err: TableDataInsertAllResponseInsertErrors) -> Self { + Self::BigQueryInsertError(DebugIgnore(err)) + } +} + +impl From for CustomEventIngestError { + fn from(err: anyhow::Error) -> Self { + Self::Other(DebugIgnore(err)) + } +} + +#[derive(Debug, ThisError)] +pub(crate) enum LogIngestError { + #[error( + "unexpected payload body. Payload should be an array of strings possibly in gzip format" + )] + UnexpectedPayloadBody, + #[error("unexpected content encoding. Supported encodings are: gzip")] + UnexpectedContentEncoding, + #[error("unable to ingest logs")] + IngestionError, + #[error("peer id forbidden from posting logs")] + Forbidden(PeerId), +} + +#[derive(Debug, ThisError)] +pub(crate) enum MetricsIngestError { + #[error("unable to ingest metrics")] + IngestionError, +} + +#[derive(Debug, ThisError)] +pub(crate) enum ValidatorCacheUpdateError { + #[error("invalid url")] + InvalidUrl, + #[error("request error")] + RestError(#[source] RestError), + #[error("both peer set empty")] + BothPeerSetEmpty, + #[error("validator set empty")] + ValidatorSetEmpty, + #[error("vfn set empty")] + VfnSetEmpty, +} + +#[derive(Debug, ThisError)] +pub(crate) enum ServiceErrorCode { + #[error("authentication error: {0}")] + AuthError(AuthError, ChainId), + #[error("custom event ingest error: {0}")] + CustomEventIngestError(#[from] CustomEventIngestError), + #[error("authorization error: {0}")] + JwtAuthError(#[from] JwtAuthError), + #[error("log ingest error: {0}")] + LogIngestError(#[from] LogIngestError), + #[error("metrics ingest error: {0}")] + MetricsIngestError(#[from] MetricsIngestError), +} + +#[derive(Debug)] +pub(crate) struct ServiceError { + http_code: StatusCode, + error_code: ServiceErrorCode, +} + +impl ServiceError { + pub(crate) fn new(http_code: StatusCode, error_code: ServiceErrorCode) -> Self { + Self { + http_code, + error_code, + } + } + + pub(crate) fn bad_request(error_code: ServiceErrorCode) -> Self { + Self::new(StatusCode::BAD_REQUEST, error_code) + } + + pub(crate) fn unauthorized(error_code: ServiceErrorCode) -> Self { + Self::new(StatusCode::UNAUTHORIZED, error_code) + } + + pub(crate) fn forbidden(error_code: ServiceErrorCode) -> Self { + Self::new(StatusCode::FORBIDDEN, error_code) + } + + pub(crate) fn internal(error_code: ServiceErrorCode) -> Self { + Self::new(StatusCode::INTERNAL_SERVER_ERROR, error_code) + } + + pub(crate) fn http_status_code(&self) -> StatusCode { + self.http_code + } + + #[cfg(test)] + pub(crate) fn error_as_string(&self) -> String { + self.error_code.to_string() + } + + pub(crate) fn error_code(&self) -> &ServiceErrorCode { + &self.error_code + } +} + +impl Reject for ServiceError {} + +impl fmt::Display for ServiceError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.http_code, self.error_code)?; + Ok(()) + } +} diff --git a/crates/aptos-telemetry-service/src/gcp_logger.rs b/crates/aptos-telemetry-service/src/gcp_logger.rs new file mode 100644 index 0000000000000..bde9f1cf1e226 --- /dev/null +++ b/crates/aptos-telemetry-service/src/gcp_logger.rs @@ -0,0 +1,66 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::constants::GCP_SERVICE_PROJECT_ID_ENV; +use std::env; + +pub fn gcp_trace_id() -> Option { + let current_span = tracing::Span::current(); + current_span + .field("trace_id") + .zip(env::var(GCP_SERVICE_PROJECT_ID_ENV).ok()) + .map(|(trace_id, project_id)| format!("projects/{}/traces/{}", project_id, trace_id)) +} + +/// Log at the `trace` level +#[macro_export] +macro_rules! trace { + ($($arg:tt)+) => { + $crate::log!(aptos_logger::Level::Trace, $($arg)+) + }; +} + +/// Log at the `debug` level +#[macro_export] +macro_rules! debug { + ($($arg:tt)+) => { + $crate::log!(aptos_logger::Level::Debug, $($arg)+) + }; +} + +/// Log at the `info` level +#[macro_export] +macro_rules! info { + ($($arg:tt)+) => { + $crate::log!(aptos_logger::Level::Info, $($arg)+) + }; +} + +/// Log at the `warn` level +#[macro_export] +macro_rules! warn { + ($($arg:tt)+) => { + $crate::log!(aptos_logger::Level::Warn, $($arg)+) + }; +} + +/// Log at the `error` level +#[macro_export] +macro_rules! error { + ($($arg:tt)+) => { + $crate::log!(aptos_logger::Level::Error, $($arg)+) + }; +} + +/// Log at the given level, it's recommended to use a specific level macro instead +#[macro_export] +macro_rules! log { + // Entry, Log Level + stuff + ($level:expr, $($args:tt)+) => {{ + if let Some(trace_id) = $crate::gcp_logger::gcp_trace_id() { + aptos_logger::log!($level, "logging.googleapis.com/trace"=%trace_id, $($args)+) + } else { + aptos_logger::log!($level, $($args)+) + } + }}; +} diff --git a/crates/aptos-telemetry-service/src/index.rs b/crates/aptos-telemetry-service/src/index.rs new file mode 100644 index 0000000000000..5b273b9ed75fb --- /dev/null +++ b/crates/aptos-telemetry-service/src/index.rs @@ -0,0 +1,111 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + auth, + constants::GCP_CLOUD_TRACE_CONTEXT_HEADER, + context::Context, + custom_event, debug, + errors::ServiceError, + log_ingest, + metrics::SERVICE_ERROR_COUNTS, + prometheus_push_metrics, remote_config, + types::response::{ErrorResponse, IndexResponse}, +}; +use std::convert::Infallible; +use warp::{ + body::BodyDeserializeError, + filters::BoxedFilter, + http::StatusCode, + reject::{ + InvalidHeader, LengthRequired, MethodNotAllowed, PayloadTooLarge, UnsupportedMediaType, + }, + reply, Filter, Rejection, Reply, +}; + +pub fn routes( + context: Context, +) -> impl Filter + Clone { + let v1_api_prefix = warp::path!("api" / "v1" / ..); + + let v1_api = v1_api_prefix.and( + index(context.clone()) + .or(auth::check_chain_access(context.clone())) + .or(auth::auth(context.clone())) + .or(custom_event::custom_event_ingest(context.clone())) + .or(prometheus_push_metrics::metrics_ingest(context.clone())) + .or(log_ingest::log_ingest(context.clone())) + .or(remote_config::telemetry_log_env(context)), + ); + + v1_api + .recover(handle_rejection) + .with(warp::trace::trace(|info| { + let trace_id = info.request_headers() + .get(GCP_CLOUD_TRACE_CONTEXT_HEADER) + .and_then(|header_value| header_value.to_str().ok().and_then(|trace_value| trace_value.split_once('/').map(|parts| parts.0))) + .unwrap_or_default(); + let span = tracing::debug_span!("request", method=%info.method(), path=%info.path(), trace_id=trace_id); + span + })) +} + +fn index(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path::end() + .and(warp::get()) + .and(context.filter()) + .and_then(handle_index) + .boxed() +} + +async fn handle_index(context: Context) -> anyhow::Result { + let resp_payload = IndexResponse { + public_key: context.noise_config().public_key(), + }; + Ok(reply::json(&resp_payload)) +} + +pub async fn handle_rejection(err: Rejection) -> std::result::Result { + let code; + let body; + + if let Some(error) = err.find::() { + code = error.http_status_code(); + body = reply::json(&ErrorResponse::from(error)); + + SERVICE_ERROR_COUNTS + .with_label_values(&[&format!("{:?}", error.error_code())]) + .inc(); + } else if err.is_not_found() { + code = StatusCode::NOT_FOUND; + body = reply::json(&ErrorResponse::new(code, "Not Found".to_owned())); + } else if let Some(cause) = err.find::() { + code = StatusCode::BAD_REQUEST; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else if let Some(cause) = err.find::() { + code = StatusCode::BAD_REQUEST; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else if let Some(cause) = err.find::() { + code = StatusCode::LENGTH_REQUIRED; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else if let Some(cause) = err.find::() { + code = StatusCode::PAYLOAD_TOO_LARGE; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else if let Some(cause) = err.find::() { + code = StatusCode::UNSUPPORTED_MEDIA_TYPE; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else if let Some(cause) = err.find::() { + code = StatusCode::METHOD_NOT_ALLOWED; + body = reply::json(&ErrorResponse::new(code, cause.to_string())); + } else { + code = StatusCode::INTERNAL_SERVER_ERROR; + body = reply::json(&ErrorResponse::new( + code, + format!("unexpected error: {:?}", err), + )); + } + + debug!("returning an error with status code {}: {:?}", code, err); + + Ok(reply::with_status(body, code).into_response()) +} diff --git a/crates/aptos-telemetry-service/src/jwt_auth.rs b/crates/aptos-telemetry-service/src/jwt_auth.rs new file mode 100644 index 0000000000000..ea445cd936843 --- /dev/null +++ b/crates/aptos-telemetry-service/src/jwt_auth.rs @@ -0,0 +1,193 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + context::{Context, JsonWebTokenService}, + error, + errors::{JwtAuthError, ServiceError}, + types::{auth::Claims, common::NodeType}, +}; +use aptos_types::{chain_id::ChainId, PeerId}; +use chrono::Utc; +use jsonwebtoken::{errors::Error, TokenData}; +use uuid::Uuid; +use warp::{reject, Rejection}; + +const BEARER: &str = "BEARER "; + +pub fn create_jwt_token( + jwt_service: &JsonWebTokenService, + chain_id: ChainId, + peer_id: PeerId, + node_type: NodeType, + epoch: u64, + uuid: Uuid, +) -> Result { + let issued = Utc::now().timestamp(); + let expiration = Utc::now() + .checked_add_signed(chrono::Duration::minutes(60)) + .expect("valid timestamp") + .timestamp(); + + let claims = Claims { + chain_id, + peer_id, + node_type, + epoch, + exp: expiration as usize, + iat: issued as usize, + run_uuid: uuid, + }; + jwt_service.encode(claims) +} + +pub async fn authorize_jwt( + token: String, + context: Context, + allow_roles: Vec, +) -> anyhow::Result { + let decoded: TokenData = context.jwt_service().decode(&token).map_err(|e| { + error!("unable to authorize jwt token: {}", e); + reject::custom(ServiceError::unauthorized( + JwtAuthError::InvalidAuthToken.into(), + )) + })?; + let claims = decoded.claims; + + let current_epoch = match context.peers().validators().read().get(&claims.chain_id) { + Some(info) => info.0, + None => { + return Err(reject::custom(ServiceError::unauthorized( + JwtAuthError::ExpiredAuthToken.into(), + ))); + }, + }; + + if !allow_roles.contains(&claims.node_type) { + return Err(reject::custom(ServiceError::forbidden( + JwtAuthError::AccessDenied.into(), + ))); + } + + if claims.epoch == current_epoch && claims.exp > Utc::now().timestamp() as usize { + Ok(claims) + } else { + Err(reject::custom(ServiceError::unauthorized( + JwtAuthError::ExpiredAuthToken.into(), + ))) + } +} + +pub async fn jwt_from_header(auth_header: Option) -> anyhow::Result { + let auth_header = match auth_header { + Some(v) => v, + None => { + return Err(reject::custom(ServiceError::unauthorized( + JwtAuthError::from("bearer token missing".to_owned()).into(), + ))) + }, + }; + let auth_header = auth_header.split(',').next().unwrap_or_default(); + if !auth_header + .get(..BEARER.len()) + .unwrap_or_default() + .eq_ignore_ascii_case(BEARER) + { + return Err(reject::custom(ServiceError::unauthorized( + JwtAuthError::from("malformed bearer token".to_owned()).into(), + ))); + } + Ok(auth_header + .get(BEARER.len()..) + .unwrap_or_default() + .to_owned()) +} + +#[cfg(test)] +mod tests { + + use super::{super::tests::test_context, *}; + use std::collections::HashMap; + use warp::hyper::StatusCode; + + #[tokio::test] + async fn jwt_from_header_valid_bearer() { + assert_eq!( + jwt_from_header(Some("Bearer token".into())).await.unwrap(), + "token" + ); + + assert_eq!( + jwt_from_header(Some("bearer token".into())).await.unwrap(), + "token" + ); + + assert_eq!( + jwt_from_header(Some("BEARER token".into())).await.unwrap(), + "token" + ); + } + + #[tokio::test] + async fn jwt_from_header_invalid_bearer() { + let jwt = jwt_from_header(None).await; + assert!(jwt.is_err()); + + let jwt = jwt_from_header(Some("Bear token".into())).await; + assert!(jwt.is_err()); + + let jwt = jwt_from_header(Some("".into())).await; + assert!(jwt.is_err()); + + let jwt = jwt_from_header(Some("Bear".into())).await; + assert!(jwt.is_err()); + + let jwt = jwt_from_header(Some("BEARER: token".into())).await; + assert!(jwt.is_err()); + } + + #[tokio::test] + async fn test_authoize_jwt() { + let test_context = test_context::new_test_context().await; + { + test_context + .inner + .peers() + .validators() + .write() + .insert(ChainId::new(25), (10, HashMap::new())); + } + let token = create_jwt_token( + test_context.inner.jwt_service(), + ChainId::new(25), + PeerId::random(), + NodeType::Validator, + 10, + Uuid::default(), + ) + .unwrap(); + let result = + authorize_jwt(token, test_context.inner.clone(), vec![NodeType::Validator]).await; + assert!(result.is_ok()); + + let token = create_jwt_token( + test_context.inner.jwt_service(), + ChainId::new(25), + PeerId::random(), + NodeType::ValidatorFullNode, + 10, + Uuid::default(), + ) + .unwrap(); + let result = authorize_jwt(token, test_context.inner, vec![NodeType::Validator]).await; + assert!(result.is_err()); + + let rejection = result.err().unwrap(); + let service_error = rejection.find::().unwrap(); + assert_eq!(service_error.http_status_code(), StatusCode::FORBIDDEN); + assert_eq!( + service_error.error_as_string(), + "authorization error: access denied to this resource" + ); + } +} diff --git a/crates/aptos-telemetry-service/src/log_ingest.rs b/crates/aptos-telemetry-service/src/log_ingest.rs new file mode 100644 index 0000000000000..a0faaafdb753b --- /dev/null +++ b/crates/aptos-telemetry-service/src/log_ingest.rs @@ -0,0 +1,138 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + auth::with_auth, + clients::humio::{ + CHAIN_ID_TAG_NAME, EPOCH_FIELD_NAME, PEER_ID_FIELD_NAME, PEER_ROLE_TAG_NAME, + RUN_UUID_TAG_NAME, + }, + constants::MAX_CONTENT_LENGTH, + context::Context, + debug, error, + errors::{LogIngestError, ServiceError}, + metrics::LOG_INGEST_BACKEND_REQUEST_DURATION, + types::{auth::Claims, common::NodeType, humio::UnstructuredLog}, +}; +use flate2::bufread::GzDecoder; +use reqwest::{header::CONTENT_ENCODING, StatusCode}; +use std::collections::HashMap; +use tokio::time::Instant; +use warp::{filters::BoxedFilter, reject, reply, Buf, Filter, Rejection, Reply}; + +pub fn log_ingest(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path!("ingest" / "logs") + .and(warp::post()) + .and(context.clone().filter()) + .and(with_auth(context, vec![ + NodeType::Validator, + NodeType::ValidatorFullNode, + NodeType::PublicFullNode, + NodeType::UnknownFullNode, + NodeType::UnknownValidator, + ])) + .and(warp::header::optional(CONTENT_ENCODING.as_str())) + .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH)) + .and(warp::body::aggregate()) + .and_then(handle_log_ingest) + .boxed() +} + +pub async fn handle_log_ingest( + context: Context, + claims: Claims, + encoding: Option, + body: impl Buf, +) -> anyhow::Result { + debug!("handling log ingest"); + + if let Some(blacklist) = &context.log_ingest_clients().blacklist { + if blacklist.contains(&claims.peer_id) { + return Err(reject::custom(ServiceError::forbidden( + LogIngestError::Forbidden(claims.peer_id).into(), + ))); + } + } + + let client = match claims.node_type { + NodeType::Unknown | NodeType::UnknownValidator | NodeType::UnknownFullNode => { + &context.log_ingest_clients().unknown_logs_ingest_client + }, + _ => &context.log_ingest_clients().known_logs_ingest_client, + }; + + let log_messages: Vec = if let Some(encoding) = encoding { + if encoding.eq_ignore_ascii_case("gzip") { + let decoder = GzDecoder::new(body.reader()); + serde_json::from_reader(decoder).map_err(|e| { + debug!("unable to decode and deserialize body: {}", e); + ServiceError::bad_request(LogIngestError::UnexpectedPayloadBody.into()) + })? + } else { + return Err(reject::custom(ServiceError::bad_request( + LogIngestError::UnexpectedContentEncoding.into(), + ))); + } + } else { + serde_json::from_reader(body.reader()).map_err(|e| { + error!("unable to deserialize body: {}", e); + ServiceError::bad_request(LogIngestError::UnexpectedPayloadBody.into()) + })? + }; + + let mut fields = HashMap::new(); + fields.insert(PEER_ID_FIELD_NAME.into(), claims.peer_id.to_string()); + fields.insert(EPOCH_FIELD_NAME.into(), claims.epoch.to_string()); + + let mut tags = HashMap::new(); + let chain_name = if claims.chain_id.id() == 3 { + format!("{}", claims.chain_id.id()) + } else { + format!("{}", claims.chain_id) + }; + tags.insert(CHAIN_ID_TAG_NAME.into(), chain_name); + tags.insert(PEER_ROLE_TAG_NAME.into(), claims.node_type.to_string()); + tags.insert(RUN_UUID_TAG_NAME.into(), claims.run_uuid.to_string()); + + let unstructured_log = UnstructuredLog { + fields, + tags, + messages: log_messages, + }; + + debug!("ingesting to humio: {:?}", unstructured_log); + + let start_timer = Instant::now(); + + let res = client.ingest_unstructured_log(unstructured_log).await; + + match res { + Ok(res) => { + LOG_INGEST_BACKEND_REQUEST_DURATION + .with_label_values(&[res.status().as_str()]) + .observe(start_timer.elapsed().as_secs_f64()); + if res.status().is_success() { + debug!("log ingested into humio succeessfully"); + } else { + error!( + "humio log ingestion failed: {}", + res.error_for_status().err().unwrap() + ); + return Err(reject::custom(ServiceError::bad_request( + LogIngestError::IngestionError.into(), + ))); + } + }, + Err(err) => { + LOG_INGEST_BACKEND_REQUEST_DURATION + .with_label_values(&["Unknown"]) + .observe(start_timer.elapsed().as_secs_f64()); + error!("error sending log ingest request: {}", err); + return Err(reject::custom(ServiceError::bad_request( + LogIngestError::IngestionError.into(), + ))); + }, + } + + Ok(reply::with_status(reply::reply(), StatusCode::CREATED)) +} diff --git a/crates/aptos-telemetry-service/src/main.rs b/crates/aptos-telemetry-service/src/main.rs new file mode 100644 index 0000000000000..34e9a77099d4e --- /dev/null +++ b/crates/aptos-telemetry-service/src/main.rs @@ -0,0 +1,13 @@ +#![forbid(unsafe_code)] + +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_telemetry_service::AptosTelemetryServiceArgs; +use clap::Parser; + +#[tokio::main] +async fn main() { + aptos_logger::Logger::new().init(); + AptosTelemetryServiceArgs::parse().run().await; +} diff --git a/crates/aptos-telemetry-service/src/remote_config.rs b/crates/aptos-telemetry-service/src/remote_config.rs new file mode 100644 index 0000000000000..e2f256b9517e0 --- /dev/null +++ b/crates/aptos-telemetry-service/src/remote_config.rs @@ -0,0 +1,82 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + auth::with_auth, + context::Context, + types::{auth::Claims, common::NodeType}, +}; +use warp::{filters::BoxedFilter, reply, Filter, Rejection, Reply}; + +pub fn telemetry_log_env(context: Context) -> BoxedFilter<(impl Reply,)> { + warp::path!("config" / "env" / "telemetry-log") + .and(warp::get()) + .and(with_auth(context.clone(), vec![ + NodeType::Validator, + NodeType::ValidatorFullNode, + NodeType::PublicFullNode, + ])) + .and(context.filter()) + .and_then(handle_telemetry_log_env) + .boxed() +} + +async fn handle_telemetry_log_env( + claims: Claims, + context: Context, +) -> Result { + let env: Option = context + .log_env_map() + .get(&claims.chain_id) + .and_then(|inner| inner.get(&claims.peer_id)) + .cloned(); + Ok(reply::json(&env)) +} + +#[cfg(test)] +mod tests { + use crate::{jwt_auth::create_jwt_token, tests::test_context, types::common::NodeType}; + use aptos_config::config::PeerSet; + use aptos_types::{chain_id::ChainId, PeerId}; + use std::collections::HashMap; + use uuid::Uuid; + + #[tokio::test] + async fn test_handle_telemetry_log_env() { + let log_level: String = String::from("debug,hyper=off"); + let peer_id = PeerId::random(); + let chain_id = ChainId::default(); + let epoch = 10; + let node_type = NodeType::Validator; + + let mut test_context = test_context::new_test_context().await; + test_context + .inner + .log_env_map_mut() + .insert(chain_id, HashMap::from([(peer_id, log_level.clone())])); + + test_context + .inner + .peers() + .validators() + .write() + .insert(chain_id, (epoch, PeerSet::default())); + + let jwt_token = create_jwt_token( + test_context.inner.jwt_service(), + chain_id, + peer_id, + node_type, + epoch, + Uuid::default(), + ) + .unwrap(); + + let value = test_context + .with_bearer_auth(jwt_token) + .get("/api/v1/config/env/telemetry-log") + .await; + + assert_eq!(value, log_level) + } +} diff --git a/crates/aptos-telemetry-service/src/tests/auth_test.rs b/crates/aptos-telemetry-service/src/tests/auth_test.rs new file mode 100644 index 0000000000000..42b3284ce3953 --- /dev/null +++ b/crates/aptos-telemetry-service/src/tests/auth_test.rs @@ -0,0 +1,319 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + context::JsonWebTokenService, + tests::test_context::new_test_context, + types::{ + auth::{AuthResponse, Claims}, + common::NodeType, + }, +}; +use aptos_config::config::{Peer, PeerRole, PeerSet, RoleType}; +use aptos_crypto::{ + noise, + noise::{InitiatorHandshakeState, NoiseConfig}, + x25519, Uniform, +}; +use aptos_types::{ + account_address, + chain_id::ChainId, + network_address::{ + DnsName, NetworkAddress, + Protocol::{Dns, Handshake, NoiseIK, Tcp}, + }, + PeerId, +}; +use serde_json::json; +use uuid::Uuid; + +fn init( + peer_role: PeerRole, +) -> ( + rand::rngs::ThreadRng, + NoiseConfig, + ChainId, + PeerId, + std::collections::HashMap, + Uuid, +) { + let mut rng = rand::thread_rng(); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let initiator_public_key = initiator_static.public_key(); + let initiator = noise::NoiseConfig::new(initiator_static); + let chain_id = ChainId::new(21); + let peer_id = account_address::from_identity_public_key(initiator_public_key); + let protocols = vec![ + Dns(DnsName::try_from("example.com".to_string()).unwrap()), + Tcp(1234), + NoiseIK(initiator_public_key), + Handshake(0), + ]; + let addr = NetworkAddress::from_protocols(protocols).unwrap(); + let peer = Peer::from_addrs(peer_role, vec![addr]); + let mut peer_set = PeerSet::new(); + peer_set.insert(peer_id, peer); + + let uuid = Uuid::new_v4(); + + (rng, initiator, chain_id, peer_id, peer_set, uuid) +} + +fn init_handshake( + rng: &mut (impl rand::RngCore + rand::CryptoRng), + chain_id: ChainId, + peer_id: PeerId, + server_public_key: x25519::PublicKey, + initiator: &NoiseConfig, +) -> (noise::InitiatorHandshakeState, Vec) { + // buffer to first noise handshake message + let mut client_noise_msg = vec![0; noise::handshake_init_msg_len(0)]; + + // build the prologue (chain_id | peer_id | public_key) + const CHAIN_ID_LENGTH: usize = 1; + const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; + const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; + let mut prologue = [0; PROLOGUE_SIZE]; + prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[chain_id.id()]); + prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(peer_id.as_ref()); + prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(server_public_key.as_slice()); + + // craft first handshake message (-> e, es, s, ss) + let initiator_state = initiator + .initiate_connection( + rng, + &prologue, + server_public_key, + None, + &mut client_noise_msg, + ) + .unwrap(); + + (initiator_state, client_noise_msg) +} + +fn finish_handshake( + jwt_service: &JsonWebTokenService, + initiator: &NoiseConfig, + initiator_state: InitiatorHandshakeState, + resp: serde_json::Value, +) -> jsonwebtoken::TokenData { + let resp: AuthResponse = serde_json::from_value(resp).unwrap(); + + let (response_payload, _) = initiator + .finalize_connection(initiator_state, resp.handshake_msg.as_slice()) + .unwrap(); + + let jwt = String::from_utf8(response_payload).unwrap(); + + jwt_service.decode(&jwt).unwrap() +} + +#[tokio::test] +async fn test_auth_validator_backwards_compat_uuid() { + let context = new_test_context().await; + let server_public_key = context.inner.noise_config().public_key(); + + let (mut rng, initiator, chain_id, peer_id, peer_set, _) = init(PeerRole::Validator); + + context + .inner + .peers() + .validators() + .write() + .insert(chain_id, (1, peer_set)); + + let (initiator_state, client_noise_msg) = + init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); + + let req = json!({ + "chain_id": chain_id, + "peer_id": peer_id, + "role_type": RoleType::Validator, + "server_public_key": server_public_key, + "handshake_msg": &client_noise_msg, + }); + let resp = context.post("/api/v1/auth", req).await; + + let decoded = finish_handshake( + context.inner.jwt_service(), + &initiator, + initiator_state, + resp, + ); + + assert_eq!(decoded.claims, Claims { + chain_id, + peer_id, + node_type: NodeType::Validator, + epoch: 1, + exp: decoded.claims.exp, + iat: decoded.claims.iat, + run_uuid: Uuid::default(), + },) +} + +#[tokio::test] +async fn test_auth_validator() { + let context = new_test_context().await; + let server_public_key = context.inner.noise_config().public_key(); + + let (mut rng, initiator, chain_id, peer_id, peer_set, run_uuid) = init(PeerRole::Validator); + + context + .inner + .peers() + .validators() + .write() + .insert(chain_id, (1, peer_set)); + + let (initiator_state, client_noise_msg) = + init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); + + let req = json!({ + "chain_id": chain_id, + "peer_id": peer_id, + "role_type": RoleType::Validator, + "server_public_key": server_public_key, + "handshake_msg": &client_noise_msg, + "run_uuid": run_uuid, + }); + let resp = context.post("/api/v1/auth", req).await; + + let decoded = finish_handshake( + context.inner.jwt_service(), + &initiator, + initiator_state, + resp, + ); + + assert_eq!(decoded.claims, Claims { + chain_id, + peer_id, + node_type: NodeType::Validator, + epoch: 1, + exp: decoded.claims.exp, + iat: decoded.claims.iat, + run_uuid, + },) +} + +#[tokio::test] +async fn test_auth_validatorfullnode() { + let context = new_test_context().await; + let server_public_key = context.inner.noise_config().public_key(); + + let (mut rng, initiator, chain_id, peer_id, peer_set, run_uuid) = + init(PeerRole::ValidatorFullNode); + + context + .inner + .peers() + .validator_fullnodes() + .write() + .insert(chain_id, (1, peer_set)); + + let (initiator_state, client_noise_msg) = + init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); + + let req = json!({ + "chain_id": chain_id, + "peer_id": peer_id, + "role_type": RoleType::FullNode, + "server_public_key": server_public_key, + "handshake_msg": &client_noise_msg, + "run_uuid": run_uuid, + }); + let resp = context.post("/api/v1/auth", req).await; + + let decoded = finish_handshake( + context.inner.jwt_service(), + &initiator, + initiator_state, + resp, + ); + + assert_eq!(decoded.claims, Claims { + chain_id, + peer_id, + node_type: NodeType::ValidatorFullNode, + epoch: 1, + exp: decoded.claims.exp, + iat: decoded.claims.iat, + run_uuid + },) +} + +#[tokio::test] +#[should_panic] +async fn test_auth_wrong_key() { + let context = new_test_context().await; + let server_public_key = context.inner.noise_config().public_key(); + + let mut rng = rand::thread_rng(); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let initiator_static2 = x25519::PrivateKey::generate(&mut rng); + let initiator_public_key = initiator_static.public_key(); + let initiator = noise::NoiseConfig::new(initiator_static); + let chain_id = ChainId::new(21); + let peer_id: PeerId = account_address::from_identity_public_key(initiator_public_key); + let protocols = vec![ + Dns(DnsName::try_from("example.com".to_string()).unwrap()), + Tcp(1234), + NoiseIK(initiator_static2.public_key()), + Handshake(0), + ]; + let addr = NetworkAddress::from_protocols(protocols).unwrap(); + let peer = Peer::from_addrs(PeerRole::Validator, vec![addr]); + let mut peer_set = PeerSet::new(); + peer_set.insert(peer_id, peer); + + context + .inner + .peers() + .validators() + .write() + .insert(chain_id, (1, peer_set)); + + let (initiator_state, client_noise_msg) = + init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); + + let req = json!({ + "chain_id": chain_id, + "peer_id": peer_id, + "role_type": RoleType::Validator, + "server_public_key": server_public_key, + "handshake_msg": client_noise_msg, + }); + let resp = context.post("/api/v1/auth", req).await; + + finish_handshake( + context.inner.jwt_service(), + &initiator, + initiator_state, + resp, + ); +} + +#[tokio::test] +async fn test_chain_access() { + let context = new_test_context().await; + let present_chain_id = ChainId::new(24); + let missing_chain_id = ChainId::new(32); + context + .inner + .peers() + .validators() + .write() + .insert(present_chain_id, (1, PeerSet::new())); + + let resp = context + .get(&format!("/api/v1/chain-access/{}", present_chain_id)) + .await; + assert!(resp.as_bool().unwrap()); + + let resp = context + .get(&format!("/api/v1/chain-access/{}", missing_chain_id)) + .await; + assert!(!resp.as_bool().unwrap()); +} diff --git a/crates/aptos-telemetry-service/src/tests/custom_event.rs b/crates/aptos-telemetry-service/src/tests/custom_event.rs new file mode 100644 index 0000000000000..61484abd82093 --- /dev/null +++ b/crates/aptos-telemetry-service/src/tests/custom_event.rs @@ -0,0 +1,59 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::test_context::new_test_context; +use crate::{ + jwt_auth::create_jwt_token, + types::{ + common::NodeType, + telemetry::{TelemetryDump, TelemetryEvent}, + }, +}; +use aptos_config::config::PeerSet; +use aptos_types::{chain_id::ChainId, PeerId}; +use chrono::Utc; +use serde_json::json; +use std::collections::BTreeMap; +use uuid::Uuid; + +#[tokio::test] +async fn test_custom_event() { + let test_context = new_test_context().await; + let chain_id = ChainId::new(28); + let peer_id = PeerId::random(); + let node_type = NodeType::Validator; + let uuid = Uuid::new_v4(); + let epoch = 10; + + test_context + .inner + .peers() + .validators() + .write() + .insert(chain_id, (epoch, PeerSet::default())); + + let jwt_token = create_jwt_token( + test_context.inner.jwt_service(), + chain_id, + peer_id, + node_type, + epoch, + uuid, + ) + .unwrap(); + + let body = TelemetryDump { + client_id: "test-client".into(), + user_id: peer_id.to_string(), + timestamp_micros: Utc::now().timestamp_micros().to_string(), + events: vec![TelemetryEvent { + name: "sample-event".into(), + params: BTreeMap::new(), + }], + }; + test_context + .with_bearer_auth(jwt_token) + .expect_status_code(500) + .post("/api/v1/ingest/custom-event", json!(body)) + .await; +} diff --git a/crates/aptos-telemetry-service/src/tests/mod.rs b/crates/aptos-telemetry-service/src/tests/mod.rs new file mode 100644 index 0000000000000..cf88484edcf28 --- /dev/null +++ b/crates/aptos-telemetry-service/src/tests/mod.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod auth_test; +mod custom_event; +pub(crate) mod test_context; diff --git a/crates/aptos-telemetry-service/src/types/auth.rs b/crates/aptos-telemetry-service/src/types/auth.rs new file mode 100644 index 0000000000000..abb8d53604ac7 --- /dev/null +++ b/crates/aptos-telemetry-service/src/types/auth.rs @@ -0,0 +1,65 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use super::common::NodeType; +use aptos_config::config::RoleType; +use aptos_crypto::x25519; +use aptos_types::{chain_id::ChainId, PeerId}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize, Debug)] +pub struct AuthRequest { + pub chain_id: ChainId, + pub peer_id: PeerId, + #[serde(default = "default_role_type")] + pub role_type: RoleType, + pub server_public_key: x25519::PublicKey, + pub handshake_msg: Vec, + #[serde(default = "default_uuid")] + pub run_uuid: Uuid, +} + +#[derive(Serialize, Deserialize)] +pub struct AuthResponse { + pub handshake_msg: Vec, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Claims { + pub chain_id: ChainId, + pub peer_id: PeerId, + pub node_type: NodeType, + pub epoch: u64, + pub exp: usize, + pub iat: usize, + pub run_uuid: Uuid, +} + +fn default_role_type() -> RoleType { + RoleType::Validator +} + +fn default_uuid() -> Uuid { + Uuid::default() +} + +impl Claims { + #[cfg(test)] + pub(crate) fn test() -> Self { + use chrono::{Duration, Utc}; + + Self { + chain_id: ChainId::test(), + peer_id: PeerId::random(), + node_type: NodeType::Validator, + epoch: 10, + exp: Utc::now().timestamp() as usize, + iat: Utc::now() + .checked_add_signed(Duration::seconds(3600)) + .unwrap() + .timestamp() as usize, + run_uuid: Uuid::default(), + } + } +} diff --git a/crates/aptos-telemetry-service/src/types/mod.rs b/crates/aptos-telemetry-service/src/types/mod.rs new file mode 100644 index 0000000000000..c8a9eab1d23a1 --- /dev/null +++ b/crates/aptos-telemetry-service/src/types/mod.rs @@ -0,0 +1,121 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod auth; +pub mod telemetry; + +pub mod common { + + use crate::types::auth::Claims; + use aptos_config::config::PeerSet; + use aptos_types::{chain_id::ChainId, PeerId}; + use serde::{Deserialize, Serialize}; + use std::{collections::HashMap, fmt}; + use uuid::Uuid; + + pub type EpochNum = u64; + pub type EpochedPeerStore = HashMap; + pub type PeerStore = HashMap; + pub type ChainCommonName = String; + + #[derive(Debug, Serialize, Deserialize, Clone)] + pub struct EventIdentity { + pub peer_id: PeerId, + pub chain_id: ChainId, + pub role_type: NodeType, + pub epoch: u64, + pub uuid: Uuid, + } + + impl From for EventIdentity { + fn from(claims: Claims) -> Self { + Self { + peer_id: claims.peer_id, + chain_id: claims.chain_id, + role_type: claims.node_type, + epoch: claims.epoch, + uuid: claims.run_uuid, + } + } + } + + #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] + pub enum NodeType { + Validator, + ValidatorFullNode, + PublicFullNode, + Unknown, + UnknownValidator, + UnknownFullNode, + } + + impl NodeType { + pub fn as_str(self) -> &'static str { + match self { + NodeType::Validator => "validator", + NodeType::ValidatorFullNode => "validator_fullnode", + NodeType::PublicFullNode => "public_fullnode", + NodeType::Unknown => "unknown_peer", + NodeType::UnknownValidator => "unknown_validator", + NodeType::UnknownFullNode => "unknown_fullnode", + } + } + } + + impl fmt::Debug for NodeType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } + } + + impl fmt::Display for NodeType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } + } +} + +pub mod response { + use crate::errors::ServiceError; + use aptos_crypto::x25519; + use reqwest::StatusCode; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + pub struct IndexResponse { + pub public_key: x25519::PublicKey, + } + + #[derive(Serialize, Deserialize)] + pub struct ErrorResponse { + code: u16, + message: String, + } + + impl ErrorResponse { + pub fn new(code: StatusCode, message: String) -> Self { + Self { + code: code.as_u16(), + message, + } + } + } + + impl From<&ServiceError> for ErrorResponse { + fn from(err: &ServiceError) -> Self { + Self::new(err.http_status_code(), err.to_string()) + } + } +} + +pub mod humio { + use serde::{Deserialize, Serialize}; + use std::collections::HashMap; + + #[derive(Deserialize, Serialize, Clone, Debug)] + pub struct UnstructuredLog { + pub fields: HashMap, + pub tags: HashMap, + pub messages: Vec, + } +} diff --git a/crates/aptos-telemetry-service/src/types/telemetry.rs b/crates/aptos-telemetry-service/src/types/telemetry.rs new file mode 100644 index 0000000000000..adc8d1dfb667f --- /dev/null +++ b/crates/aptos-telemetry-service/src/types/telemetry.rs @@ -0,0 +1,31 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::common::EventIdentity; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// A useful struct for serialization a telemetry event +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct TelemetryEvent { + pub name: String, + pub params: BTreeMap, +} + +/// A useful struct for serializing a telemetry dump +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct TelemetryDump { + pub client_id: String, + pub user_id: String, + pub timestamp_micros: String, + pub events: Vec, +} + +#[derive(Debug, Serialize, Clone)] +pub(crate) struct BigQueryRow { + #[serde(flatten)] + pub event_identity: EventIdentity, + pub event_name: String, + pub event_timestamp: u64, + pub event_params: Vec, +} diff --git a/crates/aptos-telemetry-service/src/validator_cache.rs b/crates/aptos-telemetry-service/src/validator_cache.rs new file mode 100644 index 0000000000000..554e533d3b2d8 --- /dev/null +++ b/crates/aptos-telemetry-service/src/validator_cache.rs @@ -0,0 +1,315 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + debug, error, + errors::ValidatorCacheUpdateError, + metrics::{VALIDATOR_SET_UPDATE_FAILED_COUNT, VALIDATOR_SET_UPDATE_SUCCESS_COUNT}, + types::common::{ChainCommonName, EpochedPeerStore}, +}; +use aptos_config::config::{Peer, PeerRole, PeerSet}; +use aptos_infallible::RwLock; +use aptos_rest_client::Response; +use aptos_types::{ + account_config::CORE_CODE_ADDRESS, chain_id::ChainId, on_chain_config::ValidatorSet, PeerId, +}; +use std::{collections::HashMap, sync::Arc, time::Duration}; +use tokio::time; +use url::Url; + +#[derive(Clone)] +pub struct PeerSetCacheUpdater { + validators: Arc>, + validator_fullnodes: Arc>, + + query_addresses: Arc>, + update_interval: time::Duration, +} + +impl PeerSetCacheUpdater { + pub fn new( + validators: Arc>, + validator_fullnodes: Arc>, + trusted_full_node_addresses: HashMap, + update_interval: Duration, + ) -> Self { + Self { + validators, + validator_fullnodes, + query_addresses: Arc::new(trusted_full_node_addresses), + update_interval, + } + } + + pub fn run(self) { + let mut interval = time::interval(self.update_interval); + tokio::spawn(async move { + loop { + self.update().await; + interval.tick().await; + } + }); + } + + async fn update(&self) { + for (chain_name, url) in self.query_addresses.iter() { + match self.update_for_chain(chain_name, url).await { + Ok(_) => { + VALIDATOR_SET_UPDATE_SUCCESS_COUNT + .with_label_values(&[&chain_name.to_string()]) + .inc(); + debug!( + "validator set update successful for chain name {}", + chain_name + ); + }, + Err(err) => { + VALIDATOR_SET_UPDATE_FAILED_COUNT + .with_label_values(&[&chain_name.to_string(), &err.to_string()]) + .inc(); + error!( + "validator set update error for chain name {}: {:?}", + chain_name, err + ); + }, + } + } + } + + async fn update_for_chain( + &self, + chain_name: &ChainCommonName, + url: &str, + ) -> Result<(), ValidatorCacheUpdateError> { + let client = aptos_rest_client::Client::new(Url::parse(url).map_err(|e| { + error!("invalid url for chain_id {}: {}", chain_name, e); + ValidatorCacheUpdateError::InvalidUrl + })?); + let response: Response = client + .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") + .await + .map_err(ValidatorCacheUpdateError::RestError)?; + + let (peer_addrs, state) = response.into_parts(); + + let chain_id = ChainId::new(state.chain_id); + + let mut validator_cache = self.validators.write(); + let mut vfn_cache = self.validator_fullnodes.write(); + + let validator_peers: PeerSet = peer_addrs + .clone() + .into_iter() + .filter_map(|validator_info| -> Option<(PeerId, Peer)> { + validator_info + .config() + .validator_network_addresses() + .map(|addresses| { + ( + *validator_info.account_address(), + Peer::from_addrs(PeerRole::Validator, addresses), + ) + }) + .map_err(|err| { + error!( + "unable to parse validator network address for validator info {} for chain name {}: {}", + validator_info, chain_name, err + ) + }) + .ok() + }) + .collect(); + + let vfn_peers: PeerSet = peer_addrs + .into_iter() + .filter_map(|validator_info| -> Option<(PeerId, Peer)> { + validator_info + .config() + .fullnode_network_addresses() + .map(|addresses| { + ( + *validator_info.account_address(), + Peer::from_addrs(PeerRole::ValidatorFullNode, addresses), + ) + }) + .map_err(|err| { + error!( + "unable to parse fullnode network address for validator info {} in chain name {}: {}", + validator_info, chain_name, err + ); + }) + .ok() + }) + .collect(); + + debug!( + "Validator peers for chain name {} (chain id {}) at epoch {}: {:?}", + chain_name, chain_id, state.epoch, validator_peers + ); + + let result = if validator_peers.is_empty() && vfn_peers.is_empty() { + Err(ValidatorCacheUpdateError::BothPeerSetEmpty) + } else if validator_peers.is_empty() { + Err(ValidatorCacheUpdateError::ValidatorSetEmpty) + } else if vfn_peers.is_empty() { + Err(ValidatorCacheUpdateError::VfnSetEmpty) + } else { + Ok(()) + }; + + if !validator_peers.is_empty() { + validator_cache.insert(chain_id, (state.epoch, validator_peers)); + } + + debug!( + "Validator fullnode peers for chain name {} (chain id {}) at epoch {}: {:?}", + chain_name, chain_id, state.epoch, vfn_peers + ); + + if !vfn_peers.is_empty() { + vfn_cache.insert(chain_id, (state.epoch, vfn_peers)); + } + + result + } +} + +#[cfg(test)] +mod tests { + use super::PeerSetCacheUpdater; + use aptos_crypto::{ + bls12381::{PrivateKey, PublicKey}, + test_utils::KeyPair, + Uniform, + }; + use aptos_infallible::RwLock; + use aptos_rest_client::aptos_api_types::*; + use aptos_types::{ + chain_id::ChainId, network_address::NetworkAddress, on_chain_config::ValidatorSet, + validator_config::ValidatorConfig, validator_info::ValidatorInfo, PeerId, + }; + use httpmock::MockServer; + use rand_core::OsRng; + use std::{collections::HashMap, str::FromStr, sync::Arc, time::Duration}; + + #[tokio::test] + async fn test_validator_cache_updater_with_invalid_address() { + let mut rng = OsRng; + let keypair = KeyPair::::generate(&mut rng); + let validator_info = ValidatorInfo::new( + PeerId::random(), + 10, + ValidatorConfig::new(keypair.public_key, vec![0, 0], vec![0, 0], 2), + ); + let validator_set = ValidatorSet::new(vec![validator_info]); + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("GET") + .path("/v1/accounts/0000000000000000000000000000000000000000000000000000000000000001/resource/0x1::stake::ValidatorSet"); + then.status(200) + .body(bcs::to_bytes(&validator_set).unwrap()) + .header(X_APTOS_CHAIN_ID, "25") + .header(X_APTOS_EPOCH, "10") + .header(X_APTOS_LEDGER_VERSION, "10") + .header(X_APTOS_LEDGER_OLDEST_VERSION, "2") + .header(X_APTOS_BLOCK_HEIGHT, "25") + .header(X_APTOS_OLDEST_BLOCK_HEIGHT, "10") + .header(X_APTOS_LEDGER_TIMESTAMP, "10"); + }); + + let mut fullnodes = HashMap::new(); + fullnodes.insert("testing".into(), server.base_url()); + + let updater = PeerSetCacheUpdater::new( + Arc::new(RwLock::new(HashMap::new())), + Arc::new(RwLock::new(HashMap::new())), + fullnodes, + Duration::from_secs(10), + ); + + updater.update().await; + + mock.assert(); + assert!(updater.validators.read().is_empty()); + assert!(updater.validator_fullnodes.read().is_empty()); + } + + #[tokio::test] + async fn test_validator_cache_updater_with_valid_address() { + let mut rng = OsRng; + let keypair = KeyPair::::generate(&mut rng); + let validator_info = ValidatorInfo::new( + PeerId::random(), + 10, + ValidatorConfig::new( + keypair.public_key, + bcs::to_bytes(&vec![NetworkAddress::from_str("/dns/a5f3d921730874389bb2f66275f163a5-8f14ad5b5e992c1c.elb.ap-southeast-1.amazonaws.com/tcp/6180/noise-ik/0xc5edf62233096df793b554e1013b07c83d01b3cf50c14ac83a0a7e0cfe340426/handshake/0").unwrap()]).unwrap(), + bcs::to_bytes(&vec![NetworkAddress::from_str("/dns/fullnode0.testnet.aptoslabs.com/tcp/6182/noise-ik/0xea19ab47ed9191865f15d85d751ed0663205c0b2f0f465714b1947c023715973/handshake/0").unwrap()]).unwrap(), + 2, + ), + ); + let validator_set = ValidatorSet::new(vec![validator_info.clone()]); + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("GET") + .path("/v1/accounts/0000000000000000000000000000000000000000000000000000000000000001/resource/0x1::stake::ValidatorSet"); + then.status(200) + .body(bcs::to_bytes(&validator_set).unwrap()) + .header(X_APTOS_CHAIN_ID, "25") + .header(X_APTOS_EPOCH, "10") + .header(X_APTOS_LEDGER_VERSION, "10") + .header(X_APTOS_LEDGER_OLDEST_VERSION, "2") + .header(X_APTOS_BLOCK_HEIGHT, "25") + .header(X_APTOS_OLDEST_BLOCK_HEIGHT, "10") + .header(X_APTOS_LEDGER_TIMESTAMP, "10"); + }); + + let mut fullnodes = HashMap::new(); + fullnodes.insert("testing".into(), server.base_url()); + + let updater = PeerSetCacheUpdater::new( + Arc::new(RwLock::new(HashMap::new())), + Arc::new(RwLock::new(HashMap::new())), + fullnodes, + Duration::from_secs(10), + ); + + updater.update().await; + + mock.assert(); + assert_eq!(updater.validators.read().len(), 1); + assert_eq!(updater.validator_fullnodes.read().len(), 1); + assert_eq!( + updater + .validators + .read() + .get(&ChainId::new(25)) + .unwrap() + .1 + .get(validator_info.account_address()) + .unwrap() + .addresses, + validator_info + .config() + .validator_network_addresses() + .unwrap() + ); + assert_eq!( + updater + .validator_fullnodes + .read() + .get(&ChainId::new(25)) + .unwrap() + .1 + .get(validator_info.account_address()) + .unwrap() + .addresses, + validator_info + .config() + .fullnode_network_addresses() + .unwrap() + ); + } +} diff --git a/network/framework/Cargo.toml b/network/framework/Cargo.toml index 8fafd90ef1edf..fc9ede7291c34 100644 --- a/network/framework/Cargo.toml +++ b/network/framework/Cargo.toml @@ -50,8 +50,6 @@ rand = { workspace = true, features = ["small_rng"] } # https://github.com/aptos-labs/aptos-core/blob/main/state-sync/aptos-data-client/Cargo.toml#L41. # See also https://github.com/aptos-labs/aptos-core/issues/13031 rand_latest = { package = "rand", version = "0.8.5" } -ring = { workspace = true } -sha2 = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } serde_json = { workspace = true } diff --git a/network/framework/src/noise/error.rs b/network/framework/src/noise/error.rs index 801b0ae3709e3..67f2d657a6397 100644 --- a/network/framework/src/noise/error.rs +++ b/network/framework/src/noise/error.rs @@ -2,8 +2,8 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 -pub use super::crypto::NoiseError; use crate::application; +use aptos_crypto::noise::NoiseError; use aptos_short_hex_str::ShortHexStr; use aptos_types::PeerId; use std::io; diff --git a/network/framework/src/noise/fuzzing.rs b/network/framework/src/noise/fuzzing.rs index c574d45624295..fed9ee70d4f9e 100644 --- a/network/framework/src/noise/fuzzing.rs +++ b/network/framework/src/noise/fuzzing.rs @@ -10,14 +10,11 @@ // use crate::{ - noise::{ - crypto::NoiseSession, stream::NoiseStream, AntiReplayTimestamps, HandshakeAuthMode, - NoiseUpgrader, - }, + noise::{stream::NoiseStream, AntiReplayTimestamps, HandshakeAuthMode, NoiseUpgrader}, testutils::fake_socket::{ReadOnlyTestSocket, ReadWriteTestSocket}, }; use aptos_config::network_id::NetworkContext; -use aptos_crypto::{test_utils::TEST_SEED, x25519, Uniform as _}; +use aptos_crypto::{noise::NoiseSession, test_utils::TEST_SEED, x25519, Uniform as _}; use futures::{executor::block_on, future::join}; use futures_util::io::AsyncReadExt; use once_cell::sync::Lazy; diff --git a/network/framework/src/noise/handshake.rs b/network/framework/src/noise/handshake.rs index f8bc7c83d34ad..1e7c2c51d358a 100644 --- a/network/framework/src/noise/handshake.rs +++ b/network/framework/src/noise/handshake.rs @@ -14,13 +14,13 @@ use crate::{ application::storage::PeersAndMetadata, logging::NetworkSchema, - noise::{crypto, error::NoiseHandshakeError, stream::NoiseStream}, + noise::{error::NoiseHandshakeError, stream::NoiseStream}, }; use aptos_config::{ config::{Peer, PeerRole}, network_id::{NetworkContext, NetworkId}, }; -use aptos_crypto::x25519; +use aptos_crypto::{noise, x25519}; use aptos_infallible::{duration_since_epoch, RwLock}; use aptos_logger::{error, trace}; use aptos_short_hex_str::{AsShortHexStr, ShortHexStr}; @@ -147,7 +147,7 @@ pub struct NoiseUpgrader { /// The validator's network context pub network_context: NetworkContext, /// Config for executing Noise handshakes. Includes our static private key. - noise_config: crypto::NoiseConfig, + noise_config: noise::NoiseConfig, /// Handshake authentication can be either mutual or server-only authentication. auth_mode: HandshakeAuthMode, } @@ -155,11 +155,11 @@ pub struct NoiseUpgrader { impl NoiseUpgrader { /// The client message consist of the prologue + a noise message with a timestamp as payload. const CLIENT_MESSAGE_SIZE: usize = - Self::PROLOGUE_SIZE + crypto::handshake_init_msg_len(AntiReplayTimestamps::TIMESTAMP_SIZE); + Self::PROLOGUE_SIZE + noise::handshake_init_msg_len(AntiReplayTimestamps::TIMESTAMP_SIZE); /// The prologue is the client's peer_id and the remote's expected public key. const PROLOGUE_SIZE: usize = PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; /// The server's message contains no payload. - const SERVER_MESSAGE_SIZE: usize = crypto::handshake_resp_msg_len(0); + const SERVER_MESSAGE_SIZE: usize = noise::handshake_resp_msg_len(0); /// Create a new NoiseConfig with the provided keypair and authentication mode. pub fn new( @@ -169,7 +169,7 @@ impl NoiseUpgrader { ) -> Self { Self { network_context, - noise_config: crypto::NoiseConfig::new(key), + noise_config: noise::NoiseConfig::new(key), auth_mode, } } @@ -743,7 +743,7 @@ mod test { let ((mut client, _), (server, server_public_key)) = build_peers(true, None); // swap in a different keypair, so the connection will be unauthenticated - client.noise_config = crypto::NoiseConfig::new(client_private_key); + client.noise_config = noise::NoiseConfig::new(client_private_key); let (client_res, server_res) = perform_handshake(&client, &server, server_public_key); client_res.unwrap_err(); @@ -1019,11 +1019,10 @@ mod test { PeerRole::ValidatorFullNode, ), ); - insert_new_trusted_peers( - &peers_and_metadata, - NetworkId::Public, - vec![client_peer, server_peer], - ); + insert_new_trusted_peers(&peers_and_metadata, NetworkId::Public, vec![ + client_peer, + server_peer, + ]); // Create an in-memory socket for testing let (dialer_socket, listener_socket) = MemorySocket::new_pair(); diff --git a/network/framework/src/noise/mod.rs b/network/framework/src/noise/mod.rs index d72dfdfaacc3d..fd3617078f53d 100644 --- a/network/framework/src/noise/mod.rs +++ b/network/framework/src/noise/mod.rs @@ -102,7 +102,6 @@ //! [ik]: https://noiseexplorer.com/patterns/IK //! [crypto]: ../aptos_crypto/noise/index.html -mod crypto; pub mod error; pub mod handshake; pub mod stream; diff --git a/network/framework/src/noise/stream.rs b/network/framework/src/noise/stream.rs index 496667c2cdd2f..5d61a340605ec 100644 --- a/network/framework/src/noise/stream.rs +++ b/network/framework/src/noise/stream.rs @@ -9,8 +9,7 @@ //! //! [handshake]: crate::noise::handshake -use crate::noise::crypto; -use aptos_crypto::x25519; +use aptos_crypto::{noise, x25519}; use aptos_logger::prelude::*; use futures::{ io::{AsyncRead, AsyncWrite}, @@ -37,7 +36,7 @@ pub struct NoiseStream { /// the socket we write to and read from socket: TSocket, /// the noise session used to encrypt and decrypt messages - session: crypto::NoiseSession, + session: noise::NoiseSession, /// handy buffers to write/read buffers: Box, /// an enum used for progressively reading a noise payload @@ -48,7 +47,7 @@ pub struct NoiseStream { impl NoiseStream { /// Create a NoiseStream from a socket and a noise post-handshake session - pub fn new(socket: TSocket, session: crypto::NoiseSession) -> Self { + pub fn new(socket: TSocket, session: noise::NoiseSession) -> Self { Self { socket, session, @@ -83,7 +82,7 @@ enum ReadState { /// End of file reached, result indicated if EOF was expected or not Eof(Result<(), ()>), /// Decryption Error - DecryptionError(crypto::NoiseError), + DecryptionError(noise::NoiseError), } impl NoiseStream @@ -220,7 +219,7 @@ enum WriteState { /// End of file reached Eof, /// Encryption Error - EncryptionError(crypto::NoiseError), + EncryptionError(noise::NoiseError), } impl NoiseStream @@ -270,11 +269,10 @@ where { Ok(authentication_tag) => { // append the authentication tag - self.buffers.write_buffer - [*offset..*offset + crypto::AES_GCM_TAGLEN] + self.buffers.write_buffer[*offset..*offset + noise::AES_GCM_TAGLEN] .copy_from_slice(&authentication_tag); // calculate frame length - let frame_len = crypto::encrypted_len(*offset); + let frame_len = noise::encrypted_len(*offset); let frame_len = frame_len .try_into() .expect("offset should be able to fit in u16"); @@ -404,22 +402,22 @@ where // // encrypted messages include a tag along with the payload. -const MAX_WRITE_BUFFER_LENGTH: usize = crypto::decrypted_len(crypto::MAX_SIZE_NOISE_MSG); +const MAX_WRITE_BUFFER_LENGTH: usize = noise::decrypted_len(noise::MAX_SIZE_NOISE_MSG); /// Collection of buffers used for buffering data during the various read/write states of a /// [`NoiseStream`] struct NoiseBuffers { /// A read buffer, used for both a received ciphertext and then for its decrypted content. - read_buffer: [u8; crypto::MAX_SIZE_NOISE_MSG], + read_buffer: [u8; noise::MAX_SIZE_NOISE_MSG], /// A write buffer, used for both a plaintext to send, and then its encrypted version. - write_buffer: [u8; crypto::MAX_SIZE_NOISE_MSG], + write_buffer: [u8; noise::MAX_SIZE_NOISE_MSG], } impl NoiseBuffers { fn new() -> Self { Self { - read_buffer: [0; crypto::MAX_SIZE_NOISE_MSG], - write_buffer: [0; crypto::MAX_SIZE_NOISE_MSG], + read_buffer: [0; noise::MAX_SIZE_NOISE_MSG], + write_buffer: [0; noise::MAX_SIZE_NOISE_MSG], } } } @@ -525,7 +523,6 @@ where mod test { use super::*; use crate::{ - noise::crypto::{NoiseSession, MAX_SIZE_NOISE_MSG}, noise::{AntiReplayTimestamps, HandshakeAuthMode, NoiseUpgrader}, testutils::fake_socket::{ReadOnlyTestSocket, ReadWriteTestSocket}, }; @@ -628,7 +625,7 @@ mod test { fake_socket.set_trailing(); // setup a NoiseStream with a dummy state - let noise_session = NoiseSession::new_for_testing(); + let noise_session = noise::NoiseSession::new_for_testing(); let mut peer = NoiseStream::new(fake_socket, noise_session); // make sure we error and we don't continuously read @@ -671,11 +668,11 @@ mod test { let ((client, _client_public), (server, server_public)) = build_peers(); let (mut client, mut server) = perform_handshake(client, server_public, server); - let buf_send = [1; MAX_SIZE_NOISE_MSG]; + let buf_send = [1; noise::MAX_SIZE_NOISE_MSG]; block_on(client.write_all(&buf_send)).unwrap(); block_on(client.flush()).unwrap(); - let mut buf_receive = [0; MAX_SIZE_NOISE_MSG]; + let mut buf_receive = [0; noise::MAX_SIZE_NOISE_MSG]; block_on(server.read_exact(&mut buf_receive)).unwrap(); assert_eq!(&buf_receive[..], &buf_send[..]); } From cccee62362d3fe5d75a45ebdf29bc46bb0a8d4b1 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 17:51:26 +0300 Subject: [PATCH 135/147] aptos-node: recover mod indexer --- aptos-node/src/indexer.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 aptos-node/src/indexer.rs diff --git a/aptos-node/src/indexer.rs b/aptos-node/src/indexer.rs new file mode 100644 index 0000000000000..bede7d1a8ec1d --- /dev/null +++ b/aptos-node/src/indexer.rs @@ -0,0 +1,34 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_config::config::NodeConfig; +use aptos_mempool::MempoolClientSender; +use aptos_storage_interface::DbReader; +use aptos_types::chain_id::ChainId; +use std::sync::Arc; +use tokio::runtime::Runtime; + +#[cfg(feature = "indexer")] +pub fn bootstrap_indexer( + node_config: &NodeConfig, + chain_id: ChainId, + aptos_db: Arc, + mp_client_sender: MempoolClientSender, +) -> Result, anyhow::Error> { + use aptos_indexer::runtime::bootstrap as bootstrap_indexer_stream; + + match bootstrap_indexer_stream(&node_config, chain_id, aptos_db, mp_client_sender) { + None => Ok(None), + Some(res) => res.map(Some), + } +} + +#[cfg(not(feature = "indexer"))] +pub fn bootstrap_indexer( + _node_config: &NodeConfig, + _chain_id: ChainId, + _aptos_db: Arc, + _mp_client_sender: MempoolClientSender, +) -> Result, anyhow::Error> { + Ok(None) +} From 94a520e68e5d2b5f17625be7a04dd0255aec0bee Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 17:53:55 +0300 Subject: [PATCH 136/147] aptos-db: update tests The storage interface used by tests added by Movement needed to be updated to the upstream changes. --- storage/aptosdb/src/db/aptosdb_test.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index e5c0eb07db555..94f2219fdaf47 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -18,29 +18,22 @@ use aptos_config::config::{ StateMerklePrunerConfig, StorageDirPaths, BUFFERED_STATE_TARGET_ITEMS, DEFAULT_MAX_NUM_NODES_PER_LRU_CACHE_SHARD, }; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519Signature}, - hash::CryptoHash, - HashValue, PrivateKey, Uniform, -}; +use aptos_crypto::{hash::CryptoHash, HashValue}; use aptos_proptest_helpers::ValueGenerator; use aptos_storage_interface::{DbReader, DbWriter, ExecutedTrees, Order}; use aptos_temppath::TempPath; use aptos_types::{ - chain_id::ChainId, ledger_info::LedgerInfoWithSignatures, proof::SparseMerkleLeafNode, state_store::{ state_key::StateKey, state_storage_usage::StateStorageUsage, state_value::StateValue, }, transaction::{ - ExecutionStatus, RawTransaction, Script, SignedTransaction, TransactionAuxiliaryData, - TransactionAuxiliaryDataV1, TransactionInfo, TransactionPayload, TransactionToCommit, - VMErrorDetail, Version, + ExecutionStatus, TransactionAuxiliaryData, TransactionAuxiliaryDataV1, TransactionInfo, + TransactionToCommit, VMErrorDetail, Version, }, vm_status::StatusCode, }; -use move_core_types::account_address::AccountAddress; use proptest::{prelude::*, std_facade::HashMap}; use std::{collections::HashSet, sync::Arc}; use test_helper::{test_save_blocks_impl, test_sync_transactions_impl}; @@ -245,7 +238,7 @@ fn test_revert_single_commit() { // Check expected before revert commit let pre_revert_version = cur_ver - 1; - assert_eq!(db.get_latest_version().unwrap(), pre_revert_version); + assert_eq!(db.get_synced_version().unwrap(), pre_revert_version); // Get the latest ledger info before revert let latest_ledger_info_before_revert = blocks[1].1.clone(); @@ -254,7 +247,7 @@ fn test_revert_single_commit() { // Revert the last commit db.revert_commit(&latest_ledger_info_before_revert).unwrap(); - assert_eq!(db.get_latest_version().unwrap(), version_to_revert_to); + assert_eq!(db.get_synced_version().unwrap(), version_to_revert_to); let ledger_info = db.get_latest_ledger_info().unwrap(); assert_eq!(ledger_info, latest_ledger_info_before_revert); let tx_acc_db = db.ledger_db.transaction_accumulator_db(); @@ -323,7 +316,7 @@ fn test_revert_nth_commit() { // Check expected before revert commit let expected_version = cur_ver - 1; - assert_eq!(db.get_latest_version().unwrap(), expected_version); + assert_eq!(db.get_synced_version().unwrap(), expected_version); // Get the 3rd block back from the latest block let revert_block_num = blockheight - 3; @@ -335,7 +328,7 @@ fn test_revert_nth_commit() { db.revert_commit(&pre_revert_ledger_info).unwrap(); - assert_eq!(db.get_latest_version().unwrap(), version_to_revert - 1); + assert_eq!(db.get_synced_version().unwrap(), version_to_revert - 1); } pub fn test_state_merkle_pruning_impl( From b75af9dfd4a2347677ab2980c04b90c06b305207 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 18:33:35 +0300 Subject: [PATCH 137/147] fix(indexer-grpc-server-framework): fixup Pull the README from upstream and remove the duplicate workspace entry. --- Cargo.toml | 1 - .../indexer-grpc-server-framework/README.md | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md diff --git a/Cargo.toml b/Cargo.toml index 540c6cab4b19e..073c88aa45ad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,6 @@ members = [ "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/indexer-grpc-table-info", "ecosystem/indexer-grpc/indexer-grpc-utils", - "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/transaction-filter", "ecosystem/node-checker", "ecosystem/node-checker/fn-check-client", diff --git a/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md b/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md new file mode 100644 index 0000000000000..762fe0c22f455 --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md @@ -0,0 +1,29 @@ +# Indexer GRPC server framework + +A boilerplate server runtime for indexer grpc infra. + +## Usage + +```rust +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ExampleConfig { + pub name: String, +} + +impl RunnableConfig for ExampleConfig { + fn run(&self) -> Result<()> { + println!("Hello, {}!", self.name); + Ok(()) + } + fn get_server_name(&self) -> String { + "srv_exp".to_string() + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let args = ServerArgs::parse(); + args.run::().await +} +``` \ No newline at end of file From 436c7b03f990126c3879609879ef6717442140d6 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 9 Jul 2024 19:59:15 +0300 Subject: [PATCH 138/147] Sync movement CLI from aptos-cli-v3.5.0 --- Cargo.lock | 255 +++-- crates/aptos/CHANGELOG.md | 74 +- crates/aptos/Cargo.toml | 31 +- crates/aptos/e2e/cases/account.py | 10 +- crates/aptos/e2e/cases/init.py | 4 +- crates/aptos/e2e/cases/stake.py | 61 +- crates/aptos/e2e/local_testnet.py | 11 +- crates/aptos/e2e/main.py | 52 +- crates/aptos/e2e/poetry.lock | 956 +++++++++++++----- crates/aptos/e2e/pyproject.toml | 4 +- crates/aptos/e2e/test_helpers.py | 4 +- crates/aptos/src/account/mod.rs | 3 + crates/aptos/src/account/multisig_account.rs | 34 +- crates/aptos/src/common/init.rs | 36 +- crates/aptos/src/common/mod.rs | 1 + crates/aptos/src/common/types.rs | 257 +++-- crates/aptos/src/common/utils.rs | 67 +- crates/aptos/src/config/mod.rs | 5 +- .../aptos/src/governance/delegation_pool.rs | 38 +- crates/aptos/src/governance/mod.rs | 44 +- crates/aptos/src/lib.rs | 3 +- crates/aptos/src/main.rs | 2 +- crates/aptos/src/move_tool/bytecode.rs | 283 ++++++ crates/aptos/src/move_tool/disassembler.rs | 148 --- crates/aptos/src/move_tool/mod.rs | 552 +++++++++- crates/aptos/src/move_tool/package_hooks.rs | 1 + crates/aptos/src/move_tool/show.rs | 2 + crates/aptos/src/move_tool/stored_package.rs | 48 +- .../src/node/analyze/analyze_validators.rs | 117 ++- crates/aptos/src/node/local_testnet/docker.rs | 2 +- crates/aptos/src/node/local_testnet/faucet.rs | 6 +- .../node/local_testnet/hasura_metadata.json | 217 ++-- .../src/node/local_testnet/indexer_api.rs | 10 +- crates/aptos/src/node/local_testnet/mod.rs | 19 +- crates/aptos/src/node/local_testnet/node.rs | 19 +- .../aptos/src/node/local_testnet/postgres.rs | 27 +- .../src/node/local_testnet/processors.rs | 76 +- .../src/node/local_testnet/ready_server.rs | 10 +- crates/aptos/src/node/mod.rs | 39 +- crates/aptos/src/test/mod.rs | 64 +- crates/aptos/src/test/tests.rs | 2 + crates/aptos/src/update/helpers.rs | 86 +- crates/aptos/src/update/mod.rs | 108 +- crates/aptos/src/update/tool.rs | 149 +-- 44 files changed, 2822 insertions(+), 1115 deletions(-) create mode 100644 crates/aptos/src/move_tool/bytecode.rs delete mode 100644 crates/aptos/src/move_tool/disassembler.rs diff --git a/Cargo.lock b/Cargo.lock index 4ba8901d576fa..96a8f3b578db8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.15", "once_cell", + "serde", "version_check", "zerocopy", ] @@ -264,7 +265,7 @@ dependencies = [ "aptos-logger", "aptos-runtimes", "aptos-storage-interface", - "aptos-system-utils", + "aptos-system-utils 0.1.0", "aptos-types", "bcs 0.1.4", "http 0.2.12", @@ -869,10 +870,10 @@ dependencies = [ "byteorder", "bytes", "criterion", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "curve25519-dalek-ng", "digest 0.9.0", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "ff", "hex", "hkdf 0.10.0", @@ -1298,7 +1299,7 @@ dependencies = [ "aptos-logger", "aptos-metrics-core", "aptos-node-resource-metrics", - "aptos-profiler", + "aptos-profiler 0.1.0", "aptos-push-metrics", "aptos-sdk", "aptos-storage-interface", @@ -1838,7 +1839,7 @@ dependencies = [ "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-metrics-core", - "aptos-moving-average", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "aptos-protos 1.3.0", "async-trait", "clap 4.5.8", @@ -1865,7 +1866,7 @@ dependencies = [ "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-metrics-core", - "aptos-moving-average", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "aptos-protos 1.3.0", "aptos-transaction-filter", "async-trait", @@ -1893,7 +1894,7 @@ dependencies = [ "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-metrics-core", - "aptos-moving-average", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "async-trait", "clap 4.5.8", "futures", @@ -1927,7 +1928,7 @@ dependencies = [ "aptos-mempool", "aptos-mempool-notifications", "aptos-metrics-core", - "aptos-moving-average", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "aptos-proptest-helpers", "aptos-protos 1.3.0", "aptos-runtimes", @@ -2006,7 +2007,7 @@ version = "1.0.0" dependencies = [ "anyhow", "aptos-metrics-core", - "aptos-system-utils", + "aptos-system-utils 0.1.0", "async-trait", "backtrace", "clap 4.5.8", @@ -2261,7 +2262,7 @@ dependencies = [ "bcs 0.1.4", "blstrs", "derivation-path", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "hex", "hmac 0.12.1", "jsonwebtoken 8.3.0", @@ -2582,6 +2583,14 @@ dependencies = [ "chrono", ] +[[package]] +name = "aptos-moving-average" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" +dependencies = [ + "chrono", +] + [[package]] name = "aptos-mvhashmap" version = "0.1.0" @@ -3028,6 +3037,19 @@ dependencies = [ "regex", ] +[[package]] +name = "aptos-profiler" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93" +dependencies = [ + "anyhow", + "backtrace", + "jemalloc-sys", + "jemallocator", + "pprof", + "regex", +] + [[package]] name = "aptos-proptest-helpers" version = "0.1.0" @@ -3039,20 +3061,19 @@ dependencies = [ [[package]] name = "aptos-protos" -version = "1.1.2" -source = "git+https://github.com/aptos-labs/aptos-core.git?rev=af0dcea7144225a709e4f595e58f8026b99e901c#af0dcea7144225a709e4f595e58f8026b99e901c" +version = "1.3.0" dependencies = [ "futures-core", "pbjson", "prost 0.12.6", - "prost-types 0.12.6", "serde", - "tonic 0.10.2", + "tonic 0.11.0", ] [[package]] name = "aptos-protos" version = "1.3.0" +source = "git+https://github.com/aptos-labs/aptos-core.git?tag=aptos-node-v1.12.1#4b9a2593facaee92b28df2e99b2773a7e4f930f5" dependencies = [ "futures-core", "pbjson", @@ -3596,7 +3617,27 @@ name = "aptos-system-utils" version = "0.1.0" dependencies = [ "anyhow", - "aptos-profiler", + "aptos-profiler 0.1.0", + "async-mutex", + "http 0.2.12", + "hyper", + "lazy_static", + "mime", + "pprof", + "regex", + "rstack-self", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "aptos-system-utils" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93" +dependencies = [ + "anyhow", + "aptos-profiler 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)", "async-mutex", "http 0.2.12", "hyper", @@ -6386,6 +6427,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "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.68", +] + [[package]] name = "curve25519-dalek-ng" version = "4.1.1" @@ -6739,7 +6807,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" dependencies = [ "async-trait", - "bb8", "diesel", "futures-util", "scoped-futures", @@ -7023,14 +7090,24 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", "rand 0.7.3", "serde", "serde_bytes", @@ -7038,6 +7115,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "serde", + "sha2 0.10.8", + "signature 2.2.0", + "subtle", + "zeroize", +] + [[package]] name = "ed25519-dalek-bip32" version = "0.2.0" @@ -7045,7 +7137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" dependencies = [ "derivation-path", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "hmac 0.12.1", "sha2 0.10.8", ] @@ -7473,6 +7565,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "field_count" version = "0.1.1" @@ -9249,6 +9347,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kanal" +version = "0.1.0-pre8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05d55519627edaf7fd0f29981f6dc03fb52df3f5b257130eb8d0bf2801ea1d7" +dependencies = [ + "futures-core", + "lock_api", +] + [[package]] name = "keccak" version = "0.1.5" @@ -10859,7 +10967,7 @@ dependencies = [ [[package]] name = "movement" -version = "2.4.0" +version = "3.5.0" dependencies = [ "anyhow", "aptos-api-types", @@ -10877,20 +10985,25 @@ dependencies = [ "aptos-genesis", "aptos-github-client", "aptos-global-constants", + "aptos-indexer-grpc-server-framework", "aptos-indexer-grpc-utils", "aptos-keygen", "aptos-ledger", "aptos-logger", + "aptos-move-debugger", "aptos-network-checker", "aptos-node", "aptos-protos 1.3.0", "aptos-rest-client", "aptos-sdk", "aptos-storage-interface", + "aptos-telemetry", "aptos-temppath", "aptos-types", "aptos-vm", "aptos-vm-genesis", + "aptos-vm-logging", + "aptos-vm-types", "async-trait", "base64 0.13.1", "bcs 0.1.4", @@ -10898,7 +11011,6 @@ dependencies = [ "chrono", "clap 4.5.8", "clap_complete", - "codespan-reporting", "dashmap", "diesel", "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)", @@ -10917,15 +11029,15 @@ dependencies = [ "move-coverage", "move-disassembler", "move-ir-types", + "move-model", "move-package", "move-symbol-pool", "move-unit-test", "move-vm-runtime", - "once_cell", + "pathsearch", "poem", "processor", "rand 0.7.3", - "regex", "reqwest", "self_update", "serde", @@ -10934,15 +11046,14 @@ dependencies = [ "server-framework", "shadow-rs", "tempfile", - "termcolor", "thiserror", "tokio", "toml 0.7.8", "tonic 0.11.0", "tracing", "tracing-subscriber 0.3.18", + "url", "version-compare", - "walkdir", ] [[package]] @@ -11749,6 +11860,16 @@ dependencies = [ "camino", ] +[[package]] +name = "pathsearch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da983bc5e582ab17179c190b4b66c7d76c5943a69c6d34df2a2b6bf8a2977b05" +dependencies = [ + "anyhow", + "libc", +] + [[package]] name = "pbjson" version = "0.5.1" @@ -12258,6 +12379,19 @@ dependencies = [ "ark-std", ] +[[package]] +name = "postgres-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" +dependencies = [ + "futures", + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres", +] + [[package]] name = "postgres-protocol" version = "0.6.6" @@ -12517,11 +12651,12 @@ dependencies = [ [[package]] name = "processor" version = "1.0.0" -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf#4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" +source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" dependencies = [ + "ahash 0.8.11", "anyhow", - "aptos-moving-average", - "aptos-protos 1.1.2", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d)", + "aptos-protos 1.3.0 (git+https://github.com/aptos-labs/aptos-core.git?tag=aptos-node-v1.12.1)", "async-trait", "base64 0.13.1", "bcs 0.1.4", @@ -12529,7 +12664,7 @@ dependencies = [ "chrono", "clap 4.5.8", "diesel", - "diesel-async 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)", "diesel_async_migrations", "diesel_migrations", "enum_dispatch", @@ -12540,7 +12675,13 @@ dependencies = [ "google-cloud-googleapis", "google-cloud-pubsub", "hex", + "itertools 0.12.1", + "jemallocator", + "kanal", + "native-tls", + "num_cpus", "once_cell", + "postgres-native-tls", "prometheus", "prost 0.12.6", "prost-types 0.12.6", @@ -12552,7 +12693,8 @@ dependencies = [ "sha3 0.9.1", "strum 0.24.1", "tokio", - "tonic 0.10.2", + "tokio-postgres", + "tonic 0.11.0", "tracing", "unescape", "url", @@ -13827,9 +13969,8 @@ dependencies = [ [[package]] name = "self_update" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b3c585a1ced6b97ac13bd5e56f66559e5a75f477da5913f70df98e114518446" +version = "0.39.0" +source = "git+https://github.com/banool/self_update.git?rev=8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b#8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b" dependencies = [ "hyper", "indicatif 0.17.8", @@ -13843,6 +13984,7 @@ dependencies = [ "tempfile", "urlencoding", "zip", + "zipsign-api", ] [[package]] @@ -14103,9 +14245,10 @@ dependencies = [ [[package]] name = "server-framework" version = "1.0.0" -source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf#4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" +source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" dependencies = [ "anyhow", + "aptos-system-utils 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)", "async-trait", "backtrace", "clap 4.5.8", @@ -15484,38 +15627,6 @@ dependencies = [ "webpki-roots 0.23.1", ] -[[package]] -name = "tonic" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.6.20", - "base64 0.21.7", - "bytes", - "flate2", - "h2", - "http 0.2.12", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project 1.1.5", - "prost 0.12.6", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", - "rustls-pemfile 1.0.4", - "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tonic" version = "0.11.0" @@ -16742,7 +16853,7 @@ name = "x25519-dalek" version = "1.2.0" source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "zeroize", ] @@ -16870,6 +16981,16 @@ dependencies = [ "time", ] +[[package]] +name = "zipsign-api" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413a546ada9dbcd0b9a3e0b0880581279e35047bce9797e523b3408e1df607c" +dependencies = [ + "ed25519-dalek 2.1.1", + "thiserror", +] + [[package]] name = "zstd" version = "0.13.1" diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md index 1c5857e78aaea..14aba5280b8d8 100644 --- a/crates/aptos/CHANGELOG.md +++ b/crates/aptos/CHANGELOG.md @@ -2,43 +2,91 @@ All notable changes to the Movement CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -Note, all changes listed before version 2.4.0 were for Aptos CLI, from which Movement CLI was derived. +Note, all changes listed up to version 3.5.0 were for Aptos CLI, from which Movement CLI was derived. ## Unreleased + +## [3.5.0] - 2024/07/06 +- Add balance command to easily get account balances for APT currently +- Add network to config file +- Add explorer links to initialized accounts, and transaction submissions +- Alias some move commands as common misnomers (e.g. build -> compile, deploy -> publish) +- Add "hello_blockchain" template to move init command + +## [3.4.1] - 2024/05/31 +- Upgraded indexer processors for localnet from ca60e51b53c3be6f9517de7c73d4711e9c1f7236 to 5244b84fa5ed872e5280dc8df032d744d62ad29d. Upgraded Hasura metadata accordingly. + +## [3.4.0] - 2024/05/30 +- Adds a check for safe usage of randomness features. Public functions are not allowed to call randomness features unless explicitly allowed via attribute `#[lint::allow_unsafe_randomness]`. +- The Move syntax now supports structured attribute names, as in `#[attribute_area::attribute_name]`. +- Upgraded indexer processors for localnet from a11f0b6532349aa6b9a80c9a1d77524f02d8a013 to ca60e51b53c3be6f9517de7c73d4711e9c1f7236. Upgraded Hasura metadata accordingly. + +## [3.3.1] - 2024/05/21 +- Fixed incompatibility bug that broken local simulation and gas profiling. + +## [3.3.0] - 2024/05/03 +- **Breaking Change** Update View functions to use BCS for submission. Allows for all arguments to be supported in view functions. Note some input arguments that were previously inputted as strings may be handled differently. +- [Early beta release of the Move compiler v2](https://aptos.dev/move/compiler_v2/) is now accessible through the CLI. We now allow specifying the Move compiler version and the Move language version via the CLI. + +## [3.2.0] - 2024/03/29 +- Renamed `run-local-testnet` to `run-localnet`. `run-local-testnet` is still supported for backwards compatibility. +- Updated localnet node to use latest code changes including long pull + +## [3.1.0] - 2024/03/21 +- Update `self_update` dependency to support situations where relevant directories (e.g. `/tmp`) exist on different filesystems. +- [bugfix] Rename `--value` back to `--override-size-check` for publishing packages +- Upgraded indexer processors for localnet from cc764f83e26aed1d83ccad0cee3ab579792a0538. This adds support for the `TransactionMetadataProcessor` among other improvements. + +## [3.0.2] - 2024/03/12 +- Increased `max_connections` for postgres container created as part of localnet to address occasional startup failures due to overloaded DB. + +## [3.0.1] - 2024/03/05 +- Fix bug in `aptos update revela` if default install directory doesn't exist. + +## [3.0.0] - 2024/03/05 +- **Breaking Change**: `aptos update` is now `aptos update aptos`. +- Added `aptos update revela`. This installs / updates the `revela` binary, which is needed for the new `aptos move decompile` subcommand. +- Extended `aptos move download` with an option `--bytecode` to also download the bytecode of a module +- Integrated the Revela decompiler which is now available via `aptos move decompile` +- Extended `aptos move disassemble` and the new `aptos move decompile` to also work on entire packages instead of only single files + +## [2.5.0] - 2024/02/27 - Updated CLI source compilation to use rust toolchain version 1.75.0 (from 1.74.1). +- Upgraded indexer processors for localnet from 9936ec73cef251fb01fd2c47412e064cad3975c2 to d44b2d209f57872ac593299c34751a5531b51352. Upgraded Hasura metadata accordingly. +- Added support for objects processor in localnet and enabled it by default. -## [2.4.0] - 2023/01/05 +## [2.4.0] - 2024/01/05 - Hide the V2 compiler from input options until the V2 compiler is ready for release - Updated CLI source compilation to use rust toolchain version 1.74.1 (from 1.72.1). -- Added `for` loop. +- Added `for` loop. - Syntax: `for (iter in lower_bound..upper_bound) { loop_body }` with integer bounds. - Documentation: https://aptos.dev/move/book/loops -- Upgraded indexer processors for local testnet from 2d5cb211a89a8705674e9e1e741c841dd899c558 to 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. Upgraded Hasura metadata accordingly. +- Upgraded indexer processors for localnet from 2d5cb211a89a8705674e9e1e741c841dd899c558 to 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. Upgraded Hasura metadata accordingly. - Upgraded Hasura GraphQL engine image from 2.35.0 to 2.36.1. ## [2.3.2] - 2023/11/28 -- Services in the local testnet now bind to 127.0.0.1 by default (unless the CLI is running inside a container, which most users should not do) rather than 0.0.0.0. You can override this behavior with the `--bind-to` flag. This fixes an issue preventing the local testnet from working on Windows. +- Services in the localnet now bind to 127.0.0.1 by default (unless the CLI is running inside a container, which most users should not do) rather than 0.0.0.0. You can override this behavior with the `--bind-to` flag. This fixes an issue preventing the localnet from working on Windows. ## [2.3.1] - 2023/11/07 ### Updated -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to 2d5cb211a89a8705674e9e1e741c841dd899c558. -- Improved reliability of inter-container networking with local testnet. +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to 2d5cb211a89a8705674e9e1e741c841dd899c558. +- Improved reliability of inter-container networking with localnet. ## [2.3.0] - 2023/10/25 ### Added - Added `--node-api-key`. This lets you set an API key for the purpose of not being ratelimited. ### Updated -- Made the local testnet exit more quickly if a service fails to start. -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to bcba94c26c8a6372056d2b69ce411c5719f98965. +- Made the localnet exit more quickly if a service fails to start. +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to bcba94c26c8a6372056d2b69ce411c5719f98965. ### Fixed -- Fixed an infrequent bug that caused startup failures for the local testnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage. +- Fixed an infrequent bug that caused startup failures for the localnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage. - Fixed an issue where the CLI could not find the Docker socket with some Docker Desktop configurations. ## [2.2.2] - 2023/10/16 ### Updated -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to d6f55d4baba32960ea7be60878552e73ffbe8b7e. +- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the localnet to d6f55d4baba32960ea7be60878552e73ffbe8b7e. ## [2.2.1] - 2023/10/13 ### Fixed @@ -46,7 +94,7 @@ Note, all changes listed before version 2.4.0 were for Aptos CLI, from which Mov ## [2.2.0] - 2023/10/11 ### Added -- Added `--with-indexer-api` to `aptos node run-local-testnet`. With this flag you can run a full processor + indexer API stack as part of your local testnet. You must have Docker installed to use this feature. For more information, see https://aptos.dev/nodes/local-testnet/local-testnet-index. +- Added `--with-indexer-api` to `aptos node run-local-testnet`. With this flag you can run a full processor + indexer API stack as part of your localnet. You must have Docker installed to use this feature. For more information, see https://aptos.dev/nodes/local-testnet/local-testnet-index. ### Updated - Updated CLI source compilation to use rust toolchain version 1.72.1 (from 1.71.1). @@ -92,7 +140,7 @@ Note, all changes listed before version 2.4.0 were for Aptos CLI, from which Mov ## [2.0.1] - 2023/06/05 ### Fixed -- Updated txn expiration configuration for the faucet built into the CLI to make local testnet startup more reliable. +- Updated txn expiration configuration for the faucet built into the CLI to make localnet startup more reliable. ## [2.0.0] - 2023/06/01 ### Added diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml index 592cf46f0802a..e9f4c42c3e76e 100644 --- a/crates/aptos/Cargo.toml +++ b/crates/aptos/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "movement" description = "Movement tool for management of nodes and interacting with Movement" -version = "2.4.0" +version = "3.5.0" # Workspace inherited keys authors = { workspace = true } @@ -29,22 +29,25 @@ aptos-gas-schedule = { workspace = true } aptos-genesis = { workspace = true } aptos-github-client = { workspace = true } aptos-global-constants = { workspace = true } +aptos-indexer-grpc-server-framework = { workspace = true } aptos-indexer-grpc-utils = { workspace = true } aptos-keygen = { workspace = true } aptos-ledger = { workspace = true } aptos-logger = { workspace = true } -# aptos-move-debugger = { workspace = true } +aptos-move-debugger = { workspace = true } aptos-network-checker = { workspace = true } aptos-node = { workspace = true } aptos-protos = { workspace = true } aptos-rest-client = { workspace = true } aptos-sdk = { workspace = true } aptos-storage-interface = { workspace = true } -# aptos-telemetry = { workspace = true } +aptos-telemetry = { workspace = true } aptos-temppath = { workspace = true } aptos-types = { workspace = true } aptos-vm = { workspace = true, features = ["testing"] } aptos-vm-genesis = { workspace = true } +aptos-vm-logging = { workspace = true } +aptos-vm-types = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } bcs = { workspace = true } @@ -52,7 +55,6 @@ bollard = { workspace = true } chrono = { workspace = true } clap = { workspace = true, features = ["env", "unstable-styles"] } clap_complete = { workspace = true } -codespan-reporting = { workspace = true } dashmap = { workspace = true } diesel = { workspace = true, features = [ "postgres_backend", @@ -72,31 +74,32 @@ move-core-types = { workspace = true } move-coverage = { workspace = true } move-disassembler = { workspace = true } move-ir-types = { workspace = true } +move-model = { workspace = true } move-package = { workspace = true } move-symbol-pool = { workspace = true } -move-unit-test = { workspace = true, features = [ "debugging" ] } -move-vm-runtime = { workspace = true, features = [ "testing" ] } -once_cell = { workspace = true } +move-unit-test = { workspace = true, features = ["debugging"] } +move-vm-runtime = { workspace = true, features = ["testing"] } +pathsearch = { workspace = true } poem = { workspace = true } -processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" } +# We set default-features to false so we don't onboard the libpq dep. See more here: +# https://github.com/aptos-labs/aptos-core/pull/12568 +processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "5244b84fa5ed872e5280dc8df032d744d62ad29d", default-features = false } rand = { workspace = true } -regex = { workspace = true } reqwest = { workspace = true } -self_update = { version = "0.38.0", features = ["archive-zip", "compression-zip-deflate"] } +self_update = { git = "https://github.com/banool/self_update.git", rev = "8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b", features = ["archive-zip", "compression-zip-deflate"] } serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } -server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf" } +server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "5244b84fa5ed872e5280dc8df032d744d62ad29d" } tempfile = { workspace = true } -termcolor = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } toml = { workspace = true } tonic = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } +url = { workspace = true } version-compare = { workspace = true } -walkdir = { workspace = true } [target.'cfg(unix)'.dependencies] jemallocator = { workspace = true } @@ -105,7 +108,7 @@ jemallocator = { workspace = true } default = [] fuzzing = [] no-upload-proposal = [] -# indexer = ["aptos-node/indexer"] +indexer = ["aptos-node/indexer"] cli-framework-test-move = [] [build-dependencies] diff --git a/crates/aptos/e2e/cases/account.py b/crates/aptos/e2e/cases/account.py index 34dba7d71a334..f4b4e8c02c469 100644 --- a/crates/aptos/e2e/cases/account.py +++ b/crates/aptos/e2e/cases/account.py @@ -10,7 +10,7 @@ @test_case -def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): +async def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): amount_in_octa = 100000000000 # Fund the account. @@ -29,7 +29,7 @@ def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): # Assert it has the requested balance. balance = int( - run_helper.api_client.account_balance( + await run_helper.api_client.account_balance( run_helper.get_account_info().account_address ) ) @@ -40,7 +40,7 @@ def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): @test_case -def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): +async def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): # Create the new account. run_helper.run_command( test_name, @@ -56,7 +56,7 @@ def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): # Assert it exists and has zero balance. balance = int( - run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) + await run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) ) if balance != 0: raise TestError( @@ -80,7 +80,7 @@ def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): ) balance = int( - run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) + await run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) ) if balance != transfer_amount: diff --git a/crates/aptos/e2e/cases/init.py b/crates/aptos/e2e/cases/init.py index 096e628ada6ad..682478328f7a2 100644 --- a/crates/aptos/e2e/cases/init.py +++ b/crates/aptos/e2e/cases/init.py @@ -35,12 +35,12 @@ def test_init(run_helper: RunHelper, test_name=None): if not account_info: raise TestError("Failed to read account info from newly created config file") - # Confirm with the local testnet that it was created. + # Confirm with the localnet that it was created. try: run_helper.api_client.account(account_info.account_address) except Exception as e: raise TestError( - f"Failed to query local testnet for account {account_info.account_address}" + f"Failed to query localnet for account {account_info.account_address}" ) from e diff --git a/crates/aptos/e2e/cases/stake.py b/crates/aptos/e2e/cases/stake.py index 2de1febab1088..80693404e7fb4 100644 --- a/crates/aptos/e2e/cases/stake.py +++ b/crates/aptos/e2e/cases/stake.py @@ -1,13 +1,19 @@ # Copyright © Aptos Foundation # SPDX-License-Identifier: Apache-2.0 +import asyncio import json +import logging +from aptos_sdk.account_address import AccountAddress from common import TestError from test_helpers import RunHelper from test_results import test_case +LOG = logging.getLogger(__name__) + + @test_case def test_stake_initialize_stake_owner(run_helper: RunHelper, test_name=None): # run the initialize-stake-owner command @@ -27,7 +33,7 @@ def test_stake_initialize_stake_owner(run_helper: RunHelper, test_name=None): if result.get("success") != True: raise TestError("Did not initialize stake owner successfully") - # make sure the the stake pool initialized on chain + # make sure the stake pool initialized on chain response = run_helper.run_command( test_name, [ @@ -200,7 +206,10 @@ def test_stake_set_voter(run_helper: RunHelper, test_name=None): @test_case -def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): +async def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): + # First wait for reconfiguration to finish. + await wait_for_reconfiguration(run_helper) + # run the set-operator command # Note: This command has to run after set-operator and set-voter # because it needs to know the operator and voter addresses @@ -227,32 +236,6 @@ def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): raise TestError("Did not set create staking contract successfully") -@test_case -def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): - # run the set-operator command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "create-staking-contract", - "--operator", - "operator", - "--voter", - "voter", - "--amount", - "1000000", - "--commission-percentage", - "1", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") != True: - raise TestError("Did not set create staking contract successfully") - - @test_case def test_stake_increase_lockup(run_helper: RunHelper, test_name=None): # run the set-operator command @@ -285,7 +268,9 @@ def test_stake_unlock_stake(run_helper: RunHelper, test_name=None): @test_case -def test_stake_withdraw_stake_after_unlock(run_helper: RunHelper, test_name=None): +async def test_stake_withdraw_stake_after_unlock(run_helper: RunHelper, test_name=None): + await wait_for_reconfiguration(run_helper) + # get the current stake amount response = run_helper.run_command( test_name, @@ -416,3 +401,21 @@ def test_stake_request_commission(run_helper: RunHelper, test_name=None): result = json.loads(response.stdout)["Result"] if result.get("success") != True: raise TestError("Did not execute [request-commission] successfully") + + +async def wait_for_reconfiguration(run_helper: RunHelper): + # First wait for reconfiguration to finish. + LOG.info("Waiting for reconfiguration to finish...") + attempts = 0 + while True: + response = await run_helper.api_client.account_resource( + AccountAddress.from_str("0x1"), + "0x1::reconfiguration_state::State", + ) + if response["data"]["variant"]["data"] == "0x00": + break + attempts += 1 + if attempts > 20: + raise TestError("Reconfiguration did not finish in time") + await asyncio.sleep(0.5) + LOG.info("Reconfiguration finished") diff --git a/crates/aptos/e2e/local_testnet.py b/crates/aptos/e2e/local_testnet.py index 941bef3da23fb..11ff78dafdb00 100644 --- a/crates/aptos/e2e/local_testnet.py +++ b/crates/aptos/e2e/local_testnet.py @@ -1,7 +1,7 @@ # Copyright © Aptos Foundation # SPDX-License-Identifier: Apache-2.0 -# This file contains functions for running the local testnet. +# This file contains functions for running the localnet. import logging import subprocess @@ -12,12 +12,12 @@ LOG = logging.getLogger(__name__) -# Run a local testnet in a docker container. We choose to detach here and we'll +# Run a localnet in a docker container. We choose to detach here and we'll # stop running it later using the container name. def run_node(network: Network, image_repo_with_project: str, pull=True): image_name = build_image_name(image_repo_with_project, network) container_name = f"aptos-tools-{network}" - LOG.info(f"Trying to run aptos CLI local testnet from image: {image_name}") + LOG.info(f"Trying to run movement CLI localnet from image: {image_name}") # Confirm that the Docker daemon is running. try: @@ -69,12 +69,13 @@ def run_node(network: Network, image_repo_with_project: str, pull=True): ] # Run the container. + LOG.debug("Running command: %s", " ".join(args)) subprocess.run( args, **kwargs, ) - LOG.info(f"Running aptos CLI local testnet from image: {image_name}") + LOG.info(f"Running movement CLI localnet from image: {image_name}. Container name: {container_name}") return container_name @@ -95,7 +96,7 @@ def wait_for_startup(container_name: str, timeout: int): try: api_response = requests.get(f"http://127.0.0.1:{NODE_PORT}/v1") # Try to query the legacy faucet health endpoint first. TODO: Remove this - # once all local testnet images we use have the new faucet in them. + # once all localnet images we use have the new faucet in them. faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/health") if faucet_response.status_code == 404: # If that fails, try the new faucet health endpoint. diff --git a/crates/aptos/e2e/main.py b/crates/aptos/e2e/main.py index 90dbb882fcd1c..46bf3078e2b4e 100644 --- a/crates/aptos/e2e/main.py +++ b/crates/aptos/e2e/main.py @@ -4,9 +4,9 @@ # SPDX-License-Identifier: Apache-2.0 """ -This script is how we orchestrate running a local testnet and then running CLI tests against it. There are two different CLIs used for this: +This script is how we orchestrate running a localnet and then running CLI tests against it. There are two different CLIs used for this: -1. Base: For running the local testnet. This is what the --base-network flag and all other flags starting with --base are for. +1. Base: For running the localnet. This is what the --base-network flag and all other flags starting with --base are for. 2. Test: The CLI that we're testing. This is what the --test-cli-tag / --test-cli-path and all other flags starting with --test are for. Example (testing CLI in image): @@ -15,7 +15,7 @@ Example (testing locally built CLI binary): python3 main.py --base-network devnet --test-cli-path ~/aptos-core/target/release/aptos -This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a local testnet built from the testnet branch of aptos-core. +This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a localnet built from the testnet branch of aptos-core. Example (using a different image repo): See ~/.github/workflows/cli-e2e-tests.yaml @@ -24,15 +24,18 @@ """ import argparse +import asyncio import logging +import os import pathlib +import platform import shutil import sys from cases.account import ( test_account_create_and_transfer, - test_account_list, test_account_fund_with_faucet, + test_account_list, test_account_lookup_address, test_account_resource_account, test_account_rotate_key, @@ -51,6 +54,7 @@ test_node_update_consensus_key, test_node_update_validator_network_address, ) +""" from cases.stake import ( test_stake_add_stake, test_stake_create_staking_contract, @@ -63,6 +67,7 @@ test_stake_withdraw_stake_after_unlock, test_stake_withdraw_stake_before_unlock, ) +""" from common import Network from local_testnet import run_node, stop_node, wait_for_startup from test_helpers import RunHelper @@ -89,7 +94,7 @@ def parse_args(): "--image-repo-with-project", default="aptoslabs", help=( - "What docker image repo (+ project) to use for the local testnet. " + "What docker image repo (+ project) to use for the localnet. " "By default we use Docker Hub: %(default)s (so, just aptoslabs for the " "project since Docker Hub is the implied default repo). If you want to " "specify a different repo, it might look like this: " @@ -101,7 +106,7 @@ def parse_args(): required=True, type=Network, choices=list(Network), - help="What branch the Aptos CLI used for the local testnet should be built from", + help="What branch the Aptos CLI used for the localnet should be built from", ) parser.add_argument( "--base-startup-timeout", @@ -126,13 +131,13 @@ def parse_args(): parser.add_argument( "--no-pull-always", action="store_true", - help='If set, do not set "--pull always" when running the local testnet. Necessary for using local images.', + help='If set, do not set "--pull always" when running the localnet. Necessary for using local images.', ) args = parser.parse_args() return args -def run_tests(run_helper): +async def run_tests(run_helper): # Make sure the metrics port is accessible. test_metrics_accessible(run_helper) @@ -143,8 +148,8 @@ def run_tests(run_helper): test_config_show_profiles(run_helper) # Run account tests. - test_account_fund_with_faucet(run_helper) - test_account_create_and_transfer(run_helper) + await test_account_fund_with_faucet(run_helper) + await test_account_create_and_transfer(run_helper) test_account_list(run_helper) test_account_lookup_address(run_helper) test_account_resource_account(run_helper) @@ -160,16 +165,18 @@ def run_tests(run_helper): test_move_view(run_helper) # Run stake subcommand group tests. + """ test_stake_initialize_stake_owner(run_helper) test_stake_add_stake(run_helper) test_stake_withdraw_stake_before_unlock(run_helper) test_stake_unlock_stake(run_helper) - test_stake_withdraw_stake_after_unlock(run_helper) + await test_stake_withdraw_stake_after_unlock(run_helper) test_stake_increase_lockup(run_helper) test_stake_set_operator(run_helper) test_stake_set_voter(run_helper) - test_stake_create_staking_contract(run_helper) + await test_stake_create_staking_contract(run_helper) test_stake_request_commission(run_helper) + """ # Run node subcommand group tests. test_node_show_validator_set(run_helper) @@ -180,7 +187,7 @@ def run_tests(run_helper): test_account_rotate_key(run_helper) -def main(): +async def main(): args = parse_args() if args.debug: @@ -193,13 +200,26 @@ def main(): shutil.rmtree(args.working_directory, ignore_errors=True) pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True) + # If we're on Mac and DOCKER_DEFAULT_PLATFORM is not already set, set it to + # linux/amd64 since we only publish images for that platform. + if ( + platform.system().lower() == "darwin" + and platform.processor().lower().startswith("arm") + ): + if not os.environ.get("DOCKER_DEFAULT_PLATFORM"): + os.environ["DOCKER_DEFAULT_PLATFORM"] = "linux/amd64" + LOG.info( + "Detected ARM Mac and DOCKER_DEFAULT_PLATFORM was not set, setting it " + "to linux/amd64" + ) + # Run a node + faucet and wait for them to start up. container_name = run_node( args.base_network, args.image_repo_with_project, not args.no_pull_always ) # We run these in a try finally so that if something goes wrong, such as the - # local testnet not starting up correctly or some unexpected error in the + # localnet not starting up correctly or some unexpected error in the # test framework, we still stop the node + faucet. try: wait_for_startup(container_name, args.base_startup_timeout) @@ -217,7 +237,7 @@ def main(): run_helper.prepare() # Run tests. - run_tests(run_helper) + await run_tests(run_helper) finally: # Stop the node + faucet. stop_node(container_name) @@ -245,7 +265,7 @@ def main(): if __name__ == "__main__": - if main(): + if asyncio.run(main()): sys.exit(0) else: sys.exit(1) diff --git a/crates/aptos/e2e/poetry.lock b/crates/aptos/e2e/poetry.lock index bd8def62ff7ce..fdcae8345acce 100644 --- a/crates/aptos/e2e/poetry.lock +++ b/crates/aptos/e2e/poetry.lock @@ -1,41 +1,185 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "anyio" -version = "3.7.1" +version = "4.3.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "aptos-sdk" -version = "0.5.1" -description = "" +version = "0.8.6" +description = "Aptos SDK" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8.1" files = [ - {file = "aptos_sdk-0.5.1.tar.gz", hash = "sha256:3711ad2bf1120fff463cd5f494162c4658f03dd6bfbf1f523ee9aea01a4cb0f0"}, + {file = "aptos_sdk-0.8.6-py3-none-any.whl", hash = "sha256:94818c54ac18b5eb631e744edfb5dcf721b8fdf28aa8b471814bcab3c6f6f7a5"}, + {file = "aptos_sdk-0.8.6.tar.gz", hash = "sha256:57fe71d966903b096933b905c94f0c835e57ca0781c098e38b637bf861c4470c"}, ] [package.dependencies] -httpx = ">=0.23.0,<0.24.0" -mypy = ">=0.982,<0.983" +ecdsa = "0.18.0" +httpx = ">=0.27.0,<0.28.0" PyNaCl = ">=1.5.0,<2.0.0" +python-graphql-client = ">=0.4.3,<0.5.0" +tomli = ">=2.0.1,<3.0.0" +typing-extensions = ">=4.4.0,<5.0.0" + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "black" @@ -64,7 +208,6 @@ mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] @@ -75,86 +218,74 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] @@ -272,7 +403,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -285,20 +415,124 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "ecdsa" +version = "0.18.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, + {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -310,145 +544,175 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - [[package]] name = "httpcore" -version = "0.16.3" +version = "1.0.5" description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, - {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, ] [package.dependencies] -anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = "==1.*" [package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.23.3" +version = "0.27.0" description = "The next generation HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, - {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] +anyio = "*" certifi = "*" -httpcore = ">=0.15.0,<0.17.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +httpcore = "==1.*" +idna = "*" sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] -[[package]] -name = "importlib-metadata" -version = "6.7.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "isort" -version = "5.11.5" +version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] -name = "mypy" -version = "0.982" -description = "Optional static typing for Python" +name = "multidict" +version = "6.0.5" +description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ - {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, - {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, - {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, - {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, - {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, - {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, - {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, - {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, - {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, - {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, - {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, - {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, - {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, - {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, - {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, - {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, - {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, - {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, - {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, - {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -462,42 +726,40 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" -version = "4.0.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] [[package]] @@ -526,15 +788,34 @@ cffi = ">=1.4.1" docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] +[[package]] +name = "python-graphql-client" +version = "0.4.3" +description = "Python GraphQL Client" +optional = false +python-versions = "*" +files = [ + {file = "python_graphql_client-0.4.3-py3-none-any.whl", hash = "sha256:c5eb996702acf46110b352f61819c46065ea4f4f106158535cd471e66490b25e"}, + {file = "python_graphql_client-0.4.3.tar.gz", hash = "sha256:fdbd03115dde8776db02e60414b83b018d7d95e5752d6d5fabf21c99265f5b9d"}, +] + +[package.dependencies] +aiohttp = ">=3.0,<4.0" +requests = ">=2.0,<3.0" +websockets = ">=5.0" + +[package.extras] +dev = ["black", "flake8", "flake8-black", "flake8-docstrings", "flake8-isort", "gitchangelog", "pre-commit", "pystache"] + [[package]] name = "requests" -version = "2.31.0" +version = "2.32.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, + {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, ] [package.dependencies] @@ -548,31 +829,25 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] @@ -586,100 +861,219 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] - [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] name = "urllib3" -version = "2.0.7" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, ] -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = ">=3.7 <=4" -content-hash = "a521ce7dc5692587334cf5116fa19b834a12ab556c1ab32cd8f6793dd5d5568c" +python-versions = ">=3.8.1,<=4" +content-hash = "494e13a2e2742cb22d504ba34cf18391f5727928283ad845214ac0b84f9bd3ec" diff --git a/crates/aptos/e2e/pyproject.toml b/crates/aptos/e2e/pyproject.toml index 23b3274f6d7f0..8368895122074 100644 --- a/crates/aptos/e2e/pyproject.toml +++ b/crates/aptos/e2e/pyproject.toml @@ -6,8 +6,8 @@ authors = ["Aptos Labs "] license = "Apache-2.0" [tool.poetry.dependencies] -python = ">=3.7 <=4" -aptos-sdk = "^0.5.1" +python = ">=3.8.1,<=4" +aptos-sdk = "^0.8.6" requests = "^2.31.0" [tool.poetry.dev-dependencies] diff --git a/crates/aptos/e2e/test_helpers.py b/crates/aptos/e2e/test_helpers.py index dbca11238b8f0..67d2655674f3f 100644 --- a/crates/aptos/e2e/test_helpers.py +++ b/crates/aptos/e2e/test_helpers.py @@ -10,7 +10,7 @@ import traceback from dataclasses import dataclass -from aptos_sdk.client import RestClient +from aptos_sdk.async_client import RestClient from common import METRICS_PORT, NODE_PORT, AccountInfo, Network, build_image_name LOG = logging.getLogger(__name__) @@ -30,7 +30,7 @@ class RunHelper: test_count: int - # This can be used by the tests to query the local testnet node. + # This can be used by the tests to query the localnet node. api_client: RestClient def __init__( diff --git a/crates/aptos/src/account/mod.rs b/crates/aptos/src/account/mod.rs index 2421a74327360..18e769ecad85b 100644 --- a/crates/aptos/src/account/mod.rs +++ b/crates/aptos/src/account/mod.rs @@ -4,6 +4,7 @@ use crate::common::types::{CliCommand, CliResult}; use clap::Subcommand; +pub mod balance; pub mod create; pub mod create_resource_account; pub mod derive_resource_account; @@ -23,6 +24,7 @@ pub enum AccountTool { CreateResourceAccount(create_resource_account::CreateResourceAccount), DeriveResourceAccountAddress(derive_resource_account::DeriveResourceAccount), FundWithFaucet(fund::FundWithFaucet), + Balance(balance::Balance), List(list::ListAccount), LookupAddress(key_rotation::LookupAddress), RotateKey(key_rotation::RotateKey), @@ -36,6 +38,7 @@ impl AccountTool { AccountTool::CreateResourceAccount(tool) => tool.execute_serialized().await, AccountTool::DeriveResourceAccountAddress(tool) => tool.execute_serialized().await, AccountTool::FundWithFaucet(tool) => tool.execute_serialized().await, + AccountTool::Balance(tool) => tool.execute_serialized().await, AccountTool::List(tool) => tool.execute_serialized().await, AccountTool::LookupAddress(tool) => tool.execute_serialized().await, AccountTool::RotateKey(tool) => tool.execute_serialized().await, diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index e37664e9992a7..a65a351cec56f 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -8,12 +8,11 @@ use crate::common::{ }, utils::view_json_option_str, }; +use aptos_api_types::ViewFunction; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_rest_client::{ - aptos_api_types::{ - EntryFunctionId, HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange, - }, + aptos_api_types::{HexEncodedBytes, WriteResource, WriteSetChange}, Transaction, }; use aptos_types::{ @@ -23,13 +22,10 @@ use aptos_types::{ use async_trait::async_trait; use bcs::to_bytes; use clap::Parser; -use once_cell::sync::Lazy; +use move_core_types::{ident_str, language_storage::ModuleId}; use serde::Serialize; use serde_json::json; -static GET_TRANSACTION_ENTRY_FUNCTION: Lazy = - Lazy::new(|| "0x1::multisig_account::get_transaction".parse().unwrap()); - /// Create a new multisig account (v2) on-chain. /// /// This will create a new multisig account and make the sender one of the owners. @@ -171,21 +167,23 @@ impl CliCommand for VerifyProposal { // Get multisig transaction via view function. let multisig_transaction = &self .txn_options - .view(ViewRequest { - function: GET_TRANSACTION_ENTRY_FUNCTION.clone(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(String::from( + .view(ViewFunction { + module: ModuleId::new( + AccountAddress::ONE, + ident_str!("multisig_account").to_owned(), + ), + function: ident_str!("get_transaction").to_owned(), + ty_args: vec![], + args: vec![ + bcs::to_bytes( &self .multisig_account_with_sequence_number .multisig_account .multisig_address, - )), - serde_json::Value::String( - self.multisig_account_with_sequence_number - .sequence_number - .to_string(), - ), + ) + .unwrap(), + bcs::to_bytes(&self.multisig_account_with_sequence_number.sequence_number) + .unwrap(), ], }) .await?[0]; diff --git a/crates/aptos/src/common/init.rs b/crates/aptos/src/common/init.rs index 45bb0be2c66be..a8647b218e0b7 100644 --- a/crates/aptos/src/common/init.rs +++ b/crates/aptos/src/common/init.rs @@ -9,7 +9,7 @@ use crate::{ ConfigSearchMode, EncodingOptions, HardwareWalletOptions, PrivateKeyInputOptions, ProfileConfig, ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE, }, - utils::{fund_account, prompt_yes_with_override, read_line}, + utils::{explorer_account_link, fund_account, prompt_yes_with_override, read_line}, }, }; use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt}; @@ -22,7 +22,11 @@ use async_trait::async_trait; use clap::Parser; use reqwest::Url; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, str::FromStr}; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, + str::FromStr, +}; /// 1 APT (might not actually get that much, depending on the faucet) const NUM_DEFAULT_OCTAS: u64 = 100000000; @@ -122,6 +126,9 @@ impl CliCommand<()> for InitTool { } }; + // Ensure the config contains the network used + profile_config.network = Some(network); + // Ensure that there is at least a REST URL set for the network match network { Network::Mainnet => { @@ -183,7 +190,7 @@ impl CliCommand<()> for InitTool { }; // Set the derivation_path to the one user chose - profile_config.derivation_path = derivation_path.clone(); + profile_config.derivation_path.clone_from(&derivation_path); // Private key let private_key = if self.is_hardware_wallet() { @@ -337,7 +344,16 @@ impl CliCommand<()> for InitTool { .expect("Must have profiles, as created above") .insert(profile_name.to_string(), profile_config); config.save()?; - eprintln!("\n---\nAptos CLI is now set up for account {} as profile {}! Run `aptos --help` for more information about commands", address, self.profile_options.profile_name().unwrap_or(DEFAULT_PROFILE)); + let profile_name = self + .profile_options + .profile_name() + .unwrap_or(DEFAULT_PROFILE); + eprintln!( + "\n---\nMovement CLI is now set up for account {} as profile {}!\n See the account here: {}\n Run `movement --help` for more information about commands", + address, + profile_name, + explorer_account_link(address, Some(network)) + ); Ok(()) } } @@ -431,6 +447,18 @@ pub enum Network { Custom, } +impl Display for Network { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + Network::Mainnet => "mainnet", + Network::Testnet => "testnet", + Network::Devnet => "devnet", + Network::Local => "local", + Network::Custom => "custom", + }) + } +} + impl FromStr for Network { type Err = CliError; diff --git a/crates/aptos/src/common/mod.rs b/crates/aptos/src/common/mod.rs index 1be94da1d100b..016e3e18ca6f4 100644 --- a/crates/aptos/src/common/mod.rs +++ b/crates/aptos/src/common/mod.rs @@ -2,5 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 pub mod init; +pub mod local_simulation; pub mod types; pub mod utils; diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index dabe07a42cb12..9db5e99f90ff2 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -1,10 +1,11 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use super::utils::fund_account; +use super::utils::{explorer_transaction_link, fund_account}; use crate::{ common::{ init::Network, + local_simulation, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, get_account_with_state, get_auth_key, get_sequence_number, parse_json_file, @@ -18,12 +19,12 @@ use crate::{ move_tool::{ArgWithType, FunctionArgType, MemberId}, }; use anyhow::Context; +use aptos_api_types::ViewFunction; use aptos_crypto::{ ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, encoding_type::{EncodingError, EncodingType}, x25519, PrivateKey, ValidCryptoMaterialStringExt, }; -use aptos_gas_profiling::FrameName; use aptos_global_constants::adjust_gas_headroom; use aptos_keygen::KeyGen; use aptos_logger::Level; @@ -44,11 +45,15 @@ use aptos_types::{ SignedTransaction, TransactionArgument, TransactionPayload, TransactionStatus, }, }; +use aptos_vm_types::output::VMOutput; use async_trait::async_trait; use clap::{Parser, ValueEnum}; use hex::FromHexError; -use move_core_types::{account_address::AccountAddress, language_storage::TypeTag}; -use move_package::CompilerVersion; +use move_core_types::{ + account_address::AccountAddress, language_storage::TypeTag, vm_status::VMStatus, +}; +use move_model::metadata::{CompilerVersion, LanguageVersion}; +use move_package::source_package::std_lib::StdVersion; use serde::{Deserialize, Serialize}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; @@ -57,7 +62,7 @@ use std::{ convert::TryFrom, fmt::{Debug, Display, Formatter}, fs::OpenOptions, - path::{Path, PathBuf}, + path::PathBuf, str::FromStr, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; @@ -227,6 +232,7 @@ pub const CONFIG_FOLDER: &str = ".aptos"; /// An individual profile #[derive(Debug, Default, Serialize, Deserialize)] pub struct ProfileConfig { + /// Name of network being used, if setup from aptos init #[serde(skip_serializing_if = "Option::is_none")] pub network: Option, /// Private key for commands. @@ -679,8 +685,8 @@ pub struct HardwareWalletOptions { /// Index of your account in hardware wallet /// - /// This is the simpler version of derivation path e.g format - [0] - /// we will translate this index into [m/44'/637'/0'/0'/0] + /// This is the simpler version of derivation path e.g `format - [0]` + /// we will translate this index into `[m/44'/637'/0'/0'/0]` #[clap(long)] pub derivation_index: Option, } @@ -1044,6 +1050,10 @@ pub struct MovePackageDir { #[clap(long, value_parser = crate::common::utils::parse_map::, default_value = "")] pub(crate) named_addresses: BTreeMap, + /// Override the standard library version by mainnet/testnet/devnet + #[clap(long, value_parser)] + pub override_std: Option, + /// Skip pulling the latest git dependencies /// /// If you don't have a network connection, the compiler may fail due @@ -1057,18 +1067,22 @@ pub struct MovePackageDir { pub bytecode_version: Option, /// Specify the version of the compiler. - /// - /// Currently hidden until the official launch of Compiler V2 - #[clap(long, hide = true)] + /// Currently, default to `v1` + #[clap(long, value_parser = clap::value_parser!(CompilerVersion))] pub compiler_version: Option, + /// Specify the language version to be supported. + /// Currently, default to `v1` + #[clap(long, value_parser = clap::value_parser!(LanguageVersion))] + pub language_version: Option, + /// Do not complain about unknown attributes in Move code. #[clap(long)] pub skip_attribute_checks: bool, /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code. /// NOTE: this behavior will become the default in the future. - /// See https://github.com/aptos-labs/aptos-core/issues/10335 + /// See #[clap(long, env = "APTOS_CHECK_TEST_CODE")] pub check_test_code: bool, } @@ -1080,9 +1094,11 @@ impl MovePackageDir { package_dir: Some(package_dir), output_dir: None, named_addresses: Default::default(), + override_std: None, skip_fetch_latest_git_deps: true, bytecode_version: None, compiler_version: None, + language_version: None, skip_attribute_checks: false, check_test_code: false, } @@ -1337,17 +1353,29 @@ impl From<&Transaction> for TransactionSummary { pending: None, sequence_number: None, }, - Transaction::ValidatorTransaction(txn) => TransactionSummary { + Transaction::BlockEpilogueTransaction(txn) => TransactionSummary { transaction_hash: txn.info.hash, + success: Some(txn.info.success), + version: Some(txn.info.version.0), + vm_status: Some(txn.info.vm_status.clone()), + timestamp_us: Some(txn.timestamp.0), + sender: None, + gas_used: None, + gas_unit_price: None, + pending: None, + sequence_number: None, + }, + Transaction::ValidatorTransaction(txn) => TransactionSummary { + transaction_hash: txn.transaction_info().hash, gas_used: None, gas_unit_price: None, pending: None, sender: None, sequence_number: None, - success: Some(txn.info.success), - timestamp_us: Some(txn.timestamp.0), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), + success: Some(txn.transaction_info().success), + timestamp_us: Some(txn.timestamp().0), + version: Some(txn.transaction_info().version.0), + vm_status: Some(txn.transaction_info().vm_status.clone()), }, } } @@ -1501,6 +1529,14 @@ pub struct TransactionOptions { #[clap(flatten)] pub(crate) prompt_options: PromptOptions, + /// If this option is set, simulate the transaction locally. + #[clap(long)] + pub(crate) local: bool, + + /// If this option is set, benchmark the transaction locally. + #[clap(long)] + pub(crate) benchmark: bool, + /// If this option is set, simulate the transaction locally using the debugger and generate /// flamegraphs that reflect the gas usage. #[clap(long)] @@ -1577,9 +1613,12 @@ impl TransactionOptions { get_sequence_number(&client, sender_address).await } - pub async fn view(&self, payload: ViewRequest) -> CliTypedResult> { + pub async fn view(&self, payload: ViewFunction) -> CliTypedResult> { let client = self.rest_client()?; - Ok(client.view(&payload, None).await?.into_inner()) + Ok(client + .view_bcs_with_json_response(&payload, None) + .await? + .into_inner()) } /// Submit a transaction @@ -1678,25 +1717,19 @@ impl TransactionOptions { adjusted_max_gas }; - // Sign and submit transaction + // Build a transaction let transaction_factory = TransactionFactory::new(chain_id) .with_gas_unit_price(gas_unit_price) .with_max_gas_amount(max_gas) .with_transaction_expiration_time(self.gas_options.expiration_secs); - match self.get_transaction_account_type() { + // Sign it with the appropriate signer + let transaction = match self.get_transaction_account_type() { Ok(AccountType::Local) => { let (private_key, _) = self.get_key_and_address()?; let sender_account = &mut LocalAccount::new(sender_address, private_key, sequence_number); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload)); - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) + sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)) }, Ok(AccountType::HardwareWallet) => { let sender_account = &mut HardwareWalletAccount::new( @@ -1709,27 +1742,49 @@ impl TransactionOptions { HardwareWalletType::Ledger, sequence_number, ); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload))?; - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) + sender_account + .sign_with_transaction_builder(transaction_factory.payload(payload))? }, - Err(err) => Err(err), - } + Err(err) => return Err(err), + }; + + // Submit the transaction, printing out a useful transaction link + client + .submit_bcs(&transaction) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + let transaction_hash = transaction.clone().committed_hash(); + let network = self + .profile_options + .profile() + .ok() + .and_then(|profile| profile.network); + eprintln!( + "Transaction submitted: {}", + explorer_transaction_link(transaction_hash, network) + ); + let response = client + .wait_for_signed_transaction(&transaction) + .await + .map_err(|err| CliError::ApiError(err.to_string()))?; + + Ok(response.into_inner()) } - /// Simulate the transaction locally using the debugger, with the gas profiler enabled. - pub async fn profile_gas( + /// Simulates a transaction locally, using the debugger to fetch required data from remote. + async fn simulate_using_debugger( &self, payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Simulating transaction locally with the gas profiler..."); - + execute: F, + ) -> CliTypedResult + where + F: FnOnce( + &AptosDebugger, + u64, + SignedTransaction, + aptos_crypto::HashValue, + ) -> CliTypedResult<(VMStatus, VMOutput)>, + { let client = self.rest_client()?; // Fetch the chain states required for the simulation @@ -1761,7 +1816,6 @@ impl TransactionOptions { } }); - // Create and sign the transaction let transaction_factory = TransactionFactory::new(chain_id) .with_gas_unit_price(gas_unit_price) .with_max_gas_amount(max_gas) @@ -1769,49 +1823,19 @@ impl TransactionOptions { let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); let transaction = sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); - let hash = transaction.clone().committed_hash(); + let hash = transaction.committed_hash(); - // Execute the transaction using the debugger let debugger = AptosDebugger::rest_client(client).unwrap(); - let res = debugger.execute_transaction_at_version_with_gas_profiler(version, transaction); - let (vm_status, output, gas_log) = res.map_err(|err| { - CliError::UnexpectedError(format!("failed to simulate txn with gas profiler: {}", err)) - })?; - - // Generate a humen-readable name for the report - let entry_point = gas_log.entry_point(); - - let human_readable_name = match entry_point { - FrameName::Script => "script".to_string(), - FrameName::Function { - module_id, name, .. - } => { - let addr_short = module_id.address().short_str_lossless(); - let addr_truncated = if addr_short.len() > 4 { - &addr_short[..4] - } else { - addr_short.as_str() - }; - format!("0x{}-{}-{}", addr_truncated, module_id.name(), name) - }, - }; - let raw_file_name = format!("txn-{}-{}", hash, human_readable_name); - - // Generate the report - let path = Path::new("gas-profiling").join(raw_file_name); - gas_log.generate_html_report(path, format!("Gas Report - {}", human_readable_name))?; + let (vm_status, vm_output) = execute(&debugger, version, transaction, hash)?; - // Generate the transaction summary - - // TODO(Gas): double check if this is correct. - let success = match output.status() { + let success = match vm_output.status() { TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), TransactionStatus::Discard(_) | TransactionStatus::Retry => None, }; - Ok(TransactionSummary { + let summary = TransactionSummary { transaction_hash: hash.into(), - gas_used: Some(output.gas_used()), + gas_used: Some(vm_output.gas_used()), gas_unit_price: Some(gas_unit_price), pending: None, sender: Some(sender_address), @@ -1820,7 +1844,53 @@ impl TransactionOptions { timestamp_us: None, version: Some(version), // The transaction is not comitted so there is no new version. vm_status: Some(vm_status.to_string()), - }) + }; + + Ok(summary) + } + + /// Simulates a transaction locally. + pub async fn simulate_locally( + &self, + payload: TransactionPayload, + ) -> CliTypedResult { + println!(); + println!("Simulating transaction locally..."); + + self.simulate_using_debugger(payload, local_simulation::run_transaction_using_debugger) + .await + } + + /// Benchmarks the transaction payload locally. + /// The transaction is executed multiple times, and the median value is calculated to improve + /// the accuracy of the measurement results. + pub async fn benchmark_locally( + &self, + payload: TransactionPayload, + ) -> CliTypedResult { + println!(); + println!("Benchmarking transaction locally..."); + + self.simulate_using_debugger( + payload, + local_simulation::benchmark_transaction_using_debugger, + ) + .await + } + + /// Simulates the transaction locally with the gas profiler enabled. + pub async fn profile_gas( + &self, + payload: TransactionPayload, + ) -> CliTypedResult { + println!(); + println!("Simulating transaction locally using the gas profiler..."); + + self.simulate_using_debugger( + payload, + local_simulation::profile_transaction_using_debugger, + ) + .await } pub async fn estimate_gas_price(&self) -> CliTypedResult { @@ -2023,6 +2093,21 @@ impl TryInto for EntryFunctionArguments { } } +impl TryInto for EntryFunctionArguments { + type Error = CliError; + + fn try_into(self) -> Result { + let view_function_args = self.check_input_style()?; + let function_id: MemberId = (&view_function_args).try_into()?; + Ok(ViewFunction { + module: function_id.module_id, + function: function_id.member_id, + ty_args: view_function_args.type_arg_vec.try_into()?, + args: view_function_args.arg_vec.try_into()?, + }) + } +} + impl TryInto for EntryFunctionArguments { type Error = CliError; @@ -2140,3 +2225,13 @@ impl TryInto for ScriptFunctionArgumentsJSON { }) } } + +#[derive(Parser)] +pub struct OverrideSizeCheckOption { + /// Whether to override the check for maximal size of published data + /// + /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it + /// will still be blocked from publishing. + #[clap(long)] + pub(crate) override_size_check: bool, +} diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index a1a053e5c3a97..fcdaf59d4267f 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -2,9 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - common::types::{ - account_address_from_public_key, CliError, CliTypedResult, PromptOptions, - TransactionOptions, TransactionSummary, + common::{ + init::Network, + types::{ + account_address_from_public_key, CliError, CliTypedResult, PromptOptions, + TransactionOptions, TransactionSummary, + }, }, config::GlobalConfig, CliResult, @@ -14,7 +17,6 @@ use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; use aptos_keygen::KeyGen; use aptos_logger::{debug, Level}; use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, FaucetClient, State}; -#[cfg(feature = "aptos")] use aptos_telemetry::service::telemetry_is_disabled; use aptos_types::{ account_address::create_multisig_account_address, @@ -86,7 +88,6 @@ pub async fn to_common_result( ) -> CliResult { let latency = start_time.elapsed(); - #[cfg(feature = "aptos")] if !telemetry_is_disabled() { let error = if let Err(ref error) = result { // Only print the error type @@ -131,7 +132,6 @@ async fn send_telemetry_event(command: &str, latency: Duration, error: Option<&s // Collect the build information let build_information = cli_build_information(); - #[cfg(feature = "aptos")] // Send the event aptos_telemetry::cli_metrics::send_cli_telemetry_event( build_information, @@ -431,6 +431,22 @@ pub fn read_line(input_name: &'static str) -> CliTypedResult { Ok(input_buf) } +/// Lists the content of a directory +pub fn read_dir_files( + path: &Path, + predicate: impl Fn(&Path) -> bool, +) -> CliTypedResult> { + let to_cli_err = |err| CliError::IO(path.display().to_string(), err); + let mut result = vec![]; + for entry in std::fs::read_dir(path).map_err(to_cli_err)? { + let path = entry.map_err(to_cli_err)?.path(); + if predicate(path.as_path()) { + result.push(path) + } + } + Ok(result) +} + /// Fund account (and possibly create it) from a faucet. This function waits for the /// transaction on behalf of the caller. pub async fn fund_account( @@ -475,7 +491,6 @@ pub async fn wait_for_transactions( pub fn start_logger(level: Level) { let mut logger = aptos_logger::Logger::new(); - #[cfg(feature = "aptos")] logger.channel_size(1000).is_async(false).level(level); logger.build(); } @@ -485,9 +500,19 @@ pub async fn profile_or_submit( payload: TransactionPayload, txn_options_ref: &TransactionOptions, ) -> CliTypedResult { + if txn_options_ref.profile_gas && txn_options_ref.benchmark { + return Err(CliError::UnexpectedError( + "Cannot perform benchmarking and gas profiling at the same time.".to_string(), + )); + } + // Profile gas if needed. if txn_options_ref.profile_gas { txn_options_ref.profile_gas(payload).await + } else if txn_options_ref.benchmark { + txn_options_ref.benchmark_locally(payload).await + } else if txn_options_ref.local { + txn_options_ref.simulate_locally(payload).await } else { // Otherwise submit the transaction. txn_options_ref @@ -541,3 +566,31 @@ pub fn view_json_option_str(option_ref: &serde_json::Value) -> CliTypedResult) -> String { + // For now, default to what the browser is already on, though the link could be wrong + if let Some(network) = network { + format!( + "https://explorer.aptoslabs.com/account/{}?network={}", + hash, network + ) + } else { + format!("https://explorer.aptoslabs.com/account/{}", hash) + } +} + +pub fn explorer_transaction_link( + hash: aptos_crypto::HashValue, + network: Option, +) -> String { + // For now, default to what the browser is already on, though the link could be wrong + if let Some(network) = network { + format!( + "https://explorer.aptoslabs.com/txn/{}?network={}", + hash.to_hex_literal(), + network + ) + } else { + format!("https://explorer.aptoslabs.com/txn/{}", hash) + } +} diff --git a/crates/aptos/src/config/mod.rs b/crates/aptos/src/config/mod.rs index dae3b77695f39..17a1f6c18964d 100644 --- a/crates/aptos/src/config/mod.rs +++ b/crates/aptos/src/config/mod.rs @@ -197,7 +197,10 @@ impl GlobalConfig { from_yaml(&String::from_utf8(read_from_file(path.as_path())?)?) } else { // If we don't have a config, let's load the default - Ok(GlobalConfig::default()) + // Let's create the file if it doesn't exist + let config = GlobalConfig::default(); + config.save()?; + Ok(config) } } diff --git a/crates/aptos/src/governance/delegation_pool.rs b/crates/aptos/src/governance/delegation_pool.rs index a2263a941043e..5d836241af694 100644 --- a/crates/aptos/src/governance/delegation_pool.rs +++ b/crates/aptos/src/governance/delegation_pool.rs @@ -209,13 +209,15 @@ async fn is_partial_governance_voting_enabled_for_delegation_pool( pool_address: AccountAddress, ) -> CliTypedResult { let response = client - .view( - &ViewRequest { - function: "0x1::delegation_pool::partial_governance_voting_enabled" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![serde_json::Value::String(pool_address.to_string())], + .view_bcs_with_json_response( + &ViewFunction { + module: ModuleId::new( + AccountAddress::ONE, + ident_str!("delegation_pool").to_owned(), + ), + function: ident_str!("partial_governance_voting_enabled").to_owned(), + ty_args: vec![], + args: vec![bcs::to_bytes(&pool_address).unwrap()], }, None, ) @@ -236,16 +238,18 @@ async fn get_remaining_voting_power( proposal_id: u64, ) -> CliTypedResult { let response = client - .view( - &ViewRequest { - function: "0x1::delegation_pool::calculate_and_update_remaining_voting_power" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(pool_address.to_string()), - serde_json::Value::String(voter_address.to_string()), - serde_json::Value::String(proposal_id.to_string()), + .view_bcs_with_json_response( + &ViewFunction { + module: ModuleId::new( + AccountAddress::ONE, + ident_str!("delegation_pool").to_owned(), + ), + function: ident_str!("calculate_and_update_remaining_voting_power").to_owned(), + ty_args: vec![], + args: vec![ + bcs::to_bytes(&pool_address).unwrap(), + bcs::to_bytes(&voter_address).unwrap(), + bcs::to_bytes(&proposal_id).unwrap(), ], }, None, diff --git a/crates/aptos/src/governance/mod.rs b/crates/aptos/src/governance/mod.rs index a781f12dbee32..883e06fa9f2c1 100644 --- a/crates/aptos/src/governance/mod.rs +++ b/crates/aptos/src/governance/mod.rs @@ -18,7 +18,7 @@ use crate::{ move_tool::{FrameworkPackageArgs, IncludedArtifacts}, CliCommand, CliResult, }; -use aptos_api_types::ViewRequest; +use aptos_api_types::ViewFunction; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; @@ -38,7 +38,10 @@ use aptos_types::{ }; use async_trait::async_trait; use clap::Parser; -use move_core_types::transaction_argument::TransactionArgument; +use move_core_types::{ + ident_str, language_storage::ModuleId, parser::parse_type_tag, + transaction_argument::TransactionArgument, +}; use reqwest::Url; use serde::{Deserialize, Serialize}; use std::{ @@ -605,14 +608,15 @@ impl SubmitVote { let is_proposal_closed = self .args .txn_options - .view(ViewRequest { - function: "0x1::voting::is_voting_closed".parse().unwrap(), - type_arguments: vec!["0x1::governance_proposal::GovernanceProposal" - .parse() - .unwrap()], - arguments: vec![ - serde_json::Value::String("0x1".to_string()), - serde_json::Value::String(proposal_id.to_string()), + .view(ViewFunction { + module: ModuleId::new(AccountAddress::ONE, ident_str!("voting").to_owned()), + function: ident_str!("is_voting_closed").to_owned(), + ty_args: vec![ + parse_type_tag("0x1::governance_proposal::GovernanceProposal").unwrap(), + ], + args: vec![ + bcs::to_bytes(&AccountAddress::ONE).unwrap(), + bcs::to_bytes(&proposal_id).unwrap(), ], }) .await?[0] @@ -630,14 +634,16 @@ impl SubmitVote { let remaining_voting_power = self .args .txn_options - .view(ViewRequest { - function: "0x1::aptos_governance::get_remaining_voting_power" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(pool_address.to_string()), - serde_json::Value::String(proposal_id.to_string()), + .view(ViewFunction { + module: ModuleId::new( + AccountAddress::ONE, + ident_str!("aptos_governance").to_owned(), + ), + function: ident_str!("get_remaining_voting_power").to_owned(), + ty_args: vec![], + args: vec![ + bcs::to_bytes(&pool_address).unwrap(), + bcs::to_bytes(&proposal_id).unwrap(), ], }) .await?[0] @@ -995,8 +1001,10 @@ impl CliCommand<()> for GenerateUpgradeProposal { move_options.dev, move_options.skip_fetch_latest_git_deps, move_options.named_addresses(), + move_options.override_std, move_options.bytecode_version, move_options.compiler_version, + move_options.language_version, move_options.skip_attribute_checks, move_options.check_test_code, ); diff --git a/crates/aptos/src/lib.rs b/crates/aptos/src/lib.rs index 9f2863a8a0e58..03dce8730aedc 100644 --- a/crates/aptos/src/lib.rs +++ b/crates/aptos/src/lib.rs @@ -49,6 +49,7 @@ pub enum Tool { Node(node::NodeTool), #[clap(subcommand)] Stake(stake::StakeTool), + #[clap(subcommand)] Update(update::UpdateTool), } @@ -68,7 +69,7 @@ impl Tool { Multisig(tool) => tool.execute().await, Node(tool) => tool.execute().await, Stake(tool) => tool.execute().await, - Update(tool) => tool.execute_serialized().await, + Update(tool) => tool.execute().await, } } } diff --git a/crates/aptos/src/main.rs b/crates/aptos/src/main.rs index 657c2d22488b4..f85244f6a2d2c 100644 --- a/crates/aptos/src/main.rs +++ b/crates/aptos/src/main.rs @@ -28,7 +28,7 @@ fn main() { // Shutdown the runtime with a timeout. We do this to make sure that we don't sit // here waiting forever waiting for tasks that sometimes don't want to exit on - // their own (e.g. telemetry, containers spawned by the local testnet, etc). + // their own (e.g. telemetry, containers spawned by the localnet, etc). runtime.shutdown_timeout(Duration::from_millis(50)); match result { diff --git a/crates/aptos/src/move_tool/bytecode.rs b/crates/aptos/src/move_tool/bytecode.rs new file mode 100644 index 0000000000000..c883783f02a51 --- /dev/null +++ b/crates/aptos/src/move_tool/bytecode.rs @@ -0,0 +1,283 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + types::{CliCommand, CliError, CliTypedResult, PromptOptions}, + utils::{ + check_if_file_exists, create_dir_if_not_exist, read_dir_files, read_from_file, + write_to_user_only_file, + }, + }, + update::get_revela_path, +}; +use anyhow::Context; +use async_trait::async_trait; +use clap::{Args, Parser}; +use itertools::Itertools; +use move_binary_format::{ + binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, +}; +use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; +use move_command_line_common::files::{ + MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, +}; +use move_coverage::coverage_map::CoverageMap; +use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; +use move_ir_types::location::Spanned; +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; + +const DISASSEMBLER_EXTENSION: &str = "mv.asm"; +const DECOMPILER_EXTENSION: &str = "mv.move"; + +/// Disassemble the Move bytecode pointed to in the textual representation +/// of Move bytecode. +/// +/// For example, if you want to disassemble an on-chain package `PackName` at account `0x42`: +/// 1. Download the package with `aptos move download --account 0x42 --package PackName --bytecode` +/// 2. Disassemble the package bytecode with `aptos disassemble --package-path PackName/bytecode_modules` +#[derive(Debug, Parser)] +pub struct Disassemble { + #[clap(flatten)] + pub command: BytecodeCommand, +} + +/// Decompile the Move bytecode pointed to into Move source code. +/// +/// For example, if you want to decompile an on-chain package `PackName` at account `0x42`: +/// 1. Download the package with `aptos move download --account 0x42 --package PackName --bytecode` +/// 2. Decompile the package bytecode with `aptos decompile --package-path PackName/bytecode_modules` +#[derive(Debug, Parser)] +pub struct Decompile { + #[clap(flatten)] + pub command: BytecodeCommand, +} + +#[derive(Debug, Args)] +pub struct BytecodeCommand { + /// Treat input file as a script (default is to treat file as a module) + #[clap(long)] + pub is_script: bool, + + #[clap(flatten)] + input: BytecodeCommandInput, + + /// (Optional) Currently only for disassemble: path to a coverage file for the VM in order + /// to print trace information in the disassembled output. + #[clap(long)] + pub code_coverage_path: Option, + + /// Output directory for the generated file. Defaults to the directory of the + /// `path/module.mv` file if not provided. The disassembled output is stored + /// at `output_dir/module.mv.asm`, and decompiled output at + /// `output_dir/module.mv.move`. + #[clap(long, value_parser)] + pub(crate) output_dir: Option, + + #[clap(flatten)] + pub(crate) prompt_options: PromptOptions, +} + +/// Allows to ensure that either one of both is selected (via the `group` attribute). +#[derive(Debug, Args)] +#[group(required = true, multiple = false)] +pub struct BytecodeCommandInput { + /// The path to a directory containing Move bytecode files with the extension `.mv`. + /// The tool will process all files find in this directory + /// + /// If present, a source map at the same location ending in `.mvsm` and the source + /// file itself ending in`.move` will be processed by the tool. + #[clap(long)] + pub package_path: Option, + + /// Alternatively to a package path, path to a single bytecode file which should be processed. + #[clap(long)] + pub bytecode_path: Option, +} + +#[derive(Debug, Clone, Copy)] +enum BytecodeCommandType { + Disassemble, + Decompile, +} + +#[async_trait] +impl CliCommand for Disassemble { + fn command_name(&self) -> &'static str { + "Disassemble" + } + + async fn execute(mut self) -> CliTypedResult { + self.command.execute(BytecodeCommandType::Disassemble).await + } +} + +#[async_trait] +impl CliCommand for Decompile { + fn command_name(&self) -> &'static str { + "Decompile" + } + + async fn execute(mut self) -> CliTypedResult { + self.command.execute(BytecodeCommandType::Decompile).await + } +} + +impl BytecodeCommand { + async fn execute(self, command_type: BytecodeCommandType) -> CliTypedResult { + let inputs = if let Some(path) = self.input.bytecode_path.clone() { + vec![path] + } else if let Some(path) = self.input.package_path.clone() { + read_dir_files(path.as_path(), |p| { + p.extension() + .map(|s| s == MOVE_COMPILED_EXTENSION) + .unwrap_or_default() + })? + } else { + unreachable!("arguments required by clap") + }; + + let mut report = vec![]; + let mut last_out_dir = String::new(); + for bytecode_path in inputs { + let bytecode_path = bytecode_path.as_path(); + let extension = bytecode_path + .extension() + .context("Missing file extension for bytecode file")?; + if extension != MOVE_COMPILED_EXTENSION { + return Err(CliError::UnexpectedError(format!( + "Bad source file extension {:?}; expected {}", + extension, MOVE_COMPILED_EXTENSION + ))); + } + + let (output, extension) = match command_type { + BytecodeCommandType::Disassemble => { + (self.disassemble(bytecode_path)?, DISASSEMBLER_EXTENSION) + }, + BytecodeCommandType::Decompile => { + (self.decompile(bytecode_path)?, DECOMPILER_EXTENSION) + }, + }; + + let output_dir = if let Some(dir) = self.output_dir.clone() { + dir + } else { + bytecode_path.parent().expect("has parent dir").to_owned() + }; + last_out_dir = output_dir.display().to_string(); + + let output_file = output_dir + .join(bytecode_path.file_name().expect("file name")) + .with_extension(extension); + check_if_file_exists(output_file.as_path(), self.prompt_options)?; + + // Create the directory if it doesn't exist + create_dir_if_not_exist(output_dir.as_path())?; + + // write to file + write_to_user_only_file( + output_file.as_path(), + &output_file.display().to_string(), + output.as_bytes(), + )?; + report.push( + output_file + .file_name() + .expect("file name") + .to_string_lossy() + .to_string(), + ); + } + + Ok(match report.len() { + 0 => "no bytecode modules found".to_owned(), + 1 => format!("{}/{}", last_out_dir, report[0]), + _ => format!("{}/{{{}}}", last_out_dir, report.into_iter().join(",")), + }) + } + + fn disassemble(&self, bytecode_path: &Path) -> Result { + let bytecode_bytes = read_from_file(bytecode_path)?; + let move_path = bytecode_path.with_extension(MOVE_EXTENSION); + let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); + + let source = fs::read_to_string(move_path).ok(); + let source_map = source_map_from_file(&source_map_path).ok(); + + let disassembler_options = DisassemblerOptions { + print_code: true, + only_externally_visible: false, + print_basic_blocks: true, + print_locals: true, + }; + let no_loc = Spanned::unsafe_no_loc(()).loc; + let module: CompiledModule; + let script: CompiledScript; + let bytecode = if self.is_script { + script = CompiledScript::deserialize(&bytecode_bytes) + .context("Script blob can't be deserialized")?; + BinaryIndexedView::Script(&script) + } else { + module = CompiledModule::deserialize(&bytecode_bytes) + .context("Module blob can't be deserialized")?; + BinaryIndexedView::Module(&module) + }; + + let mut source_mapping = if let Some(s) = source_map { + SourceMapping::new(s, bytecode) + } else { + SourceMapping::new_from_view(bytecode, no_loc) + .context("Unable to build dummy source mapping")? + }; + + if let Some(source_code) = source { + source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); + } + + let mut disassembler = Disassembler::new(source_mapping, disassembler_options); + + if let Some(file_path) = &self.code_coverage_path { + disassembler.add_coverage_map( + CoverageMap::from_binary_file(file_path) + .map_err(|_err| { + CliError::UnexpectedError("Unable to read from file_path".to_string()) + })? + .to_unified_exec_map(), + ); + } + + disassembler + .disassemble() + .map_err(|err| CliError::UnexpectedError(format!("Unable to disassemble: {}", err))) + } + + fn decompile(&self, bytecode_path: &Path) -> Result { + let exe = get_revela_path()?; + let to_cli_error = |e| CliError::IO(exe.display().to_string(), e); + let mut cmd = Command::new(exe.as_path()); + cmd.arg(format!("--bytecode={}", bytecode_path.display())); + if self.is_script { + cmd.arg("--script"); + } + let out = cmd.output().map_err(to_cli_error)?; + if out.status.success() { + String::from_utf8(out.stdout).map_err(|err| { + CliError::UnexpectedError(format!( + "output generated by decompiler is not valid utf8: {}", + err + )) + }) + } else { + Err(CliError::UnexpectedError(format!( + "decompiler exited with status {}: {}", + out.status, + String::from_utf8(out.stderr).unwrap_or_default() + ))) + } + } +} diff --git a/crates/aptos/src/move_tool/disassembler.rs b/crates/aptos/src/move_tool/disassembler.rs deleted file mode 100644 index 709bdd460c590..0000000000000 --- a/crates/aptos/src/move_tool/disassembler.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{CliCommand, CliError, CliTypedResult, PromptOptions}, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, read_from_file, - write_to_user_only_file, - }, -}; -use anyhow::Context; -use async_trait::async_trait; -use clap::Parser; -use move_binary_format::{ - binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, -}; -use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; -use move_command_line_common::files::{ - MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, -}; -use move_coverage::coverage_map::CoverageMap; -use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; -use move_ir_types::location::Spanned; -use std::{fs, path::PathBuf}; - -const DISASSEMBLED_CODE_FILE: &str = "disassembled-code.move"; - -/// Disassemble the Move bytecode pointed to -/// -/// For example, if you want to disassemble on chain module: -/// 1. Download the package - aptos move download -/// 2. Compile the package - aptos move compile -/// 3. Cd to package and disassemble - aptos move disassemble --bytecode-path ./test.mv -#[derive(Debug, Parser)] -pub struct Disassemble { - /// Treat input file as a script (default is to treat file as a module) - #[clap(long)] - pub is_script: bool, - - /// The path to the bytecode file to disassemble; - /// - /// let's call it file.mv. We assume that two other files reside under the same directory: - /// a source map file.mvsm (possibly) and the Move source code file.move. - #[clap(long)] - pub bytecode_path: PathBuf, - - /// (Optional) Path to a coverage file for the VM in order to print trace information in the - /// disassembled output. - #[clap(long)] - pub code_coverage_path: Option, - - /// Output directory for the key files - #[clap(long, value_parser)] - pub(crate) output_dir: Option, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand for Disassemble { - fn command_name(&self) -> &'static str { - "Disassemble" - } - - async fn execute(self) -> CliTypedResult { - let bytecode_path = self.bytecode_path.as_path(); - let extension = bytecode_path - .extension() - .context("Missing file extension for bytecode file")?; - if extension != MOVE_COMPILED_EXTENSION { - return Err(CliError::UnexpectedError(format!( - "Bad source file extension {:?}; expected {}", - extension, MOVE_COMPILED_EXTENSION - ))); - } - - let bytecode_bytes = read_from_file(bytecode_path)?; - let move_path = bytecode_path.with_extension(MOVE_EXTENSION); - let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); - - let source = fs::read_to_string(move_path).ok(); - let source_map = source_map_from_file(&source_map_path); - - let disassembler_options = DisassemblerOptions { - print_code: true, - only_externally_visible: false, - print_basic_blocks: true, - print_locals: true, - }; - - let no_loc = Spanned::unsafe_no_loc(()).loc; - let module: CompiledModule; - let script: CompiledScript; - let bytecode = if self.is_script { - script = CompiledScript::deserialize(&bytecode_bytes) - .context("Script blob can't be deserialized")?; - BinaryIndexedView::Script(&script) - } else { - module = CompiledModule::deserialize(&bytecode_bytes) - .context("Module blob can't be deserialized")?; - BinaryIndexedView::Module(&module) - }; - - let mut source_mapping = if let Ok(s) = source_map { - SourceMapping::new(s, bytecode) - } else { - SourceMapping::new_from_view(bytecode, no_loc) - .context("Unable to build dummy source mapping")? - }; - - if let Some(source_code) = source { - source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); - } - - let mut disassembler = Disassembler::new(source_mapping, disassembler_options); - - if let Some(file_path) = &self.code_coverage_path { - disassembler.add_coverage_map( - CoverageMap::from_binary_file(file_path) - .map_err(|_err| { - CliError::UnexpectedError("Unable to read from file_path".to_string()) - })? - .to_unified_exec_map(), - ); - } - - let disassemble_string = disassembler - .disassemble() - .map_err(|_err| CliError::UnexpectedError("Unable to disassemble".to_string()))?; - - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let disassemble_file = output_dir.join(DISASSEMBLED_CODE_FILE); - check_if_file_exists(disassemble_file.as_path(), self.prompt_options)?; - - // Create the directory if it doesn't exist - create_dir_if_not_exist(output_dir.as_path())?; - - // write to file - write_to_user_only_file( - disassemble_file.as_path(), - DISASSEMBLED_CODE_FILE, - disassemble_string.as_bytes(), - )?; - - Ok(disassemble_file.as_path().display().to_string()) - } -} diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs index e8ff54c7b7fb9..bec9ca7f809bc 100644 --- a/crates/aptos/src/move_tool/mod.rs +++ b/crates/aptos/src/move_tool/mod.rs @@ -1,22 +1,16 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -mod aptos_debug_natives; -pub mod coverage; -mod disassembler; -mod manifest; -pub mod package_hooks; -mod show; -pub mod stored_package; - use crate::{ account::derive_resource_account::ResourceAccountSeed, common::{ + local_simulation, types::{ load_account_arg, ArgWithTypeJSON, CliConfig, CliError, CliTypedResult, ConfigSearchMode, EntryFunctionArguments, EntryFunctionArgumentsJSON, - MoveManifestAccountWrapper, MovePackageDir, ProfileOptions, PromptOptions, RestOptions, - SaveFile, ScriptFunctionArguments, TransactionOptions, TransactionSummary, + MoveManifestAccountWrapper, MovePackageDir, OverrideSizeCheckOption, ProfileOptions, + PromptOptions, RestOptions, SaveFile, ScriptFunctionArguments, TransactionOptions, + TransactionSummary, }, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, @@ -25,8 +19,8 @@ use crate::{ }, governance::CompileScriptFunction, move_tool::{ + bytecode::{Decompile, Disassemble}, coverage::SummaryCoverage, - disassembler::Disassemble, manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo}, }, CliCommand, CliResult, @@ -37,21 +31,28 @@ use aptos_framework::{ BuildOptions, BuiltPackage, }; use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters}; -use aptos_rest_client::aptos_api_types::{ - EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, +use aptos_move_debugger::aptos_debugger::AptosDebugger; +use aptos_rest_client::{ + aptos_api_types::{EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId}, + Client, }; use aptos_types::{ account_address::{create_resource_address, AccountAddress}, - transaction::{TransactionArgument, TransactionPayload}, + object_address::create_object_code_deployment_address, + on_chain_config::aptos_test_feature_flags_genesis, + transaction::{Transaction, TransactionArgument, TransactionPayload, TransactionStatus}, }; +use aptos_vm::data_cache::AsMoveResolver; use async_trait::async_trait; use clap::{Parser, Subcommand, ValueEnum}; use itertools::Itertools; use move_cli::{self, base::test::UnitTestResult}; use move_command_line_common::env::MOVE_HOME; use move_core_types::{identifier::Identifier, language_storage::ModuleId, u256::U256}; +use move_model::metadata::{CompilerVersion, LanguageVersion}; use move_package::{ - source_package::layout::SourcePackageLayout, BuildConfig, CompilerConfig, CompilerVersion, + source_package::{layout::SourcePackageLayout, std_lib::StdVersion}, + BuildConfig, CompilerConfig, }; use move_unit_test::UnitTestingConfig; pub use package_hooks::*; @@ -65,8 +66,21 @@ use std::{ }; pub use stored_package::*; use tokio::task; +use url::Url; + +mod aptos_debug_natives; +mod bytecode; +pub mod coverage; +mod manifest; +pub mod package_hooks; +mod show; +pub mod stored_package; + +const HELLO_BLOCKCHAIN_EXAMPLE: &str = include_str!( + "../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain.move" +); -/// Tool for Move related operations +/// Tool for Move smart contract related operations /// /// This tool lets you compile, test, and publish Move code, in addition /// to run any other tools that help run, verify, or provide information @@ -75,17 +89,24 @@ use tokio::task; pub enum MoveTool { BuildPublishPayload(BuildPublishPayload), Clean(CleanPackage), + #[clap(alias = "build")] Compile(CompilePackage), + #[clap(alias = "build-script")] CompileScript(CompileScript), #[clap(subcommand)] Coverage(coverage::CoveragePackage), + CreateObjectAndPublishPackage(CreateObjectAndPublishPackage), + UpgradeObjectPackage(UpgradeObjectPackage), CreateResourceAccountAndPublishPackage(CreateResourceAccountAndPublishPackage), Disassemble(Disassemble), + Decompile(Decompile), + #[clap(alias = "doc")] Document(DocumentPackage), Download(DownloadPackage), Init(InitPackage), List(ListPackage), Prove(ProvePackage), + #[clap(alias = "deploy")] Publish(PublishPackage), Run(RunFunction), RunScript(RunScript), @@ -94,6 +115,7 @@ pub enum MoveTool { Test(TestPackage), VerifyPackage(VerifyPackage), View(ViewFunction), + Replay(Replay), } impl MoveTool { @@ -104,10 +126,15 @@ impl MoveTool { MoveTool::Compile(tool) => tool.execute_serialized().await, MoveTool::CompileScript(tool) => tool.execute_serialized().await, MoveTool::Coverage(tool) => tool.execute().await, + MoveTool::CreateObjectAndPublishPackage(tool) => { + tool.execute_serialized_success().await + }, + MoveTool::UpgradeObjectPackage(tool) => tool.execute_serialized_success().await, MoveTool::CreateResourceAccountAndPublishPackage(tool) => { tool.execute_serialized_success().await }, MoveTool::Disassemble(tool) => tool.execute_serialized().await, + MoveTool::Decompile(tool) => tool.execute_serialized().await, MoveTool::Document(tool) => tool.execute_serialized().await, MoveTool::Download(tool) => tool.execute_serialized().await, MoveTool::Init(tool) => tool.execute_serialized_success().await, @@ -120,6 +147,7 @@ impl MoveTool { MoveTool::Test(tool) => tool.execute_serialized().await, MoveTool::VerifyPackage(tool) => tool.execute_serialized().await, MoveTool::View(tool) => tool.execute_serialized().await, + MoveTool::Replay(tool) => tool.execute_serialized().await, } } } @@ -224,6 +252,11 @@ impl FrameworkPackageArgs { } } +#[derive(ValueEnum, Clone, Copy, Debug)] +pub enum Template { + HelloBlockchain, +} + /// Creates a new Move package at the given location /// /// This will create a directory for a Move package and a corresponding @@ -245,9 +278,17 @@ pub struct InitPackage { /// Example: alice=0x1234,bob=0x5678,greg=_ /// /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap(long, value_parser = crate::common::utils::parse_map::, default_value = "")] + #[clap( + long, + value_parser = crate::common::utils::parse_map::, + default_value = "" + )] pub(crate) named_addresses: BTreeMap, + /// Template name for initialization + #[clap(long)] + pub(crate) template: Option
    c2T%bt4OVGCkz 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̑&YjUZFaSy{>tJ}moyeTNC_ZrM>yWPL&H42jV} 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

    x8Q0Y0$Z&?F&)Q4646Rcf*xa6p;qAR(DK z4)c}KR-A2stpUU^8mxp6WV>W}NO?dmgg#beK97RAM+hCz97oL8C^abl$I9#as3i|P zoJiv#wWAIrLs2fJQ76Ju>kX4$jQ4o(;-i6wFM9ZC04@wQZGdZZzO-D5(Hb$lkq458bnq<1P{sxh>`JRr4z5P!*z(c*~t^ zf?yS-LPPmnBlrVJJ9;Adlo0;NAI%qy6*Uta6D=WBFU6zzl7R}1Y_j5vNPgcDc#c*= zE(VqdyJj97snM`K=6ea5V;l*JRz-d0L2w@g#Zqw4p+KXhgqSpfrxL^YfSLf$Jy_rn z8w=9U(||q7a{$Ki91)-s3H6+j741R-h$}Lm-R!_G>kif_&%=S>(RPk99_b^K@%RcS z0m1e1g?O!%LGm`OhPI{*(<2S^bC zv1_n8vrwo3C;&KAERa$-auH$X(y*IkGEqn)3TPBU(?A3Y3I#B>kPrSlIBlhwFA<7# z#PmY+q7@}ZEn*5FJ`mu6UPKL~VebXDt(d4FP89sK2on}3V(yS-nkDAEf+BRg*-*rW z#fhyq+iW9&5P{IH;qN$A+4 z{4brwJ5)ZO$IVf|HgPKEV1l8D#(8|if-2z$l|le29_=$MN2dljAOT`>m^-)vhA|M= z!bO${Q4xe9h~b;*4$48Dflv$d8;KAD2{%*3;Fhiu>`+nBRC)WJ_9AQ#`nls!+e3Zs zV3HDXsBD7-xCMX`ao4=ckIE$^&Bp;2Bt@3!BqX1Lo}f(BF_?}*c0eVh#=dT8aHB>{ z*BdorVh}+!OPI?%jZ1V*zi_1fibApa_~RYhGPb=wk~vk!$IIxni#tkyGvZ`?0K^HX z7R0R5DJFOP?GbCOm6)Co1;=OXk#no~g!r@?=y0d}AlZt~Xmj|Sp9euC9Ixzc(7{m) zCe0Ngu~w~cs>z*tYjJ;~@nI%DW{gaWZlQ;R=5#ndM7cGR*@??^1rFq~l)`yBMBKqi zDLT+AFjz8kxgquXBF41#$t*(uDhQFlSH;H-xH1;kwG!uMt&i(*y}~qbBIH)i3>Xo8 zqU|YLP0U$zDpvyrCjcMQ+p@{#td2(#;C;bL8dSCx3|@#@0ZAaLgCk8N6tSiwrFKBZ z88~B*X9i3zxM8Dq!dW_Shx#(68Lf5!w~G?H@W68liz82<#*PlyOT!9^>^$U808

  • x9<81m#O57~zG=?1U?rW-?^0_B0> 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 diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/test-data-mainnet-10m-15-execution-result.txt b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/test-data-mainnet-10m-15-execution-result.txt deleted file mode 100644 index 85abe792ac8ec..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/test-data-mainnet-10m-15-execution-result.txt +++ /dev/null @@ -1,20 +0,0 @@ -version:10000003 -write set:V0(WriteSetV0(WriteSetMut { write_set: {StateKey { inner: AccessPath { address: 0x5a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948, path: "Resource(0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::dao_storage::Storage<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC, 0x1::aptos_coin::AptosCoin, 0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated>)" }, hash: OnceCell(Uninit) }: Modification(30167a9600000000b8e9b60e07000000), StateKey { inner: AccessPath { address: 0x5a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948, path: "Resource(0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::dao_storage::EventsStore<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC, 0x1::aptos_coin::AptosCoin, 0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated>)" }, hash: OnceCell(Uninit) }: Modification(01000000000000004f0000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9487524010000000000500000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9480000000000000000510000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948), StateKey { inner: AccessPath { address: 0x5a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948, path: "Resource(0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::liquidity_pool::EventsStore<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC, 0x1::aptos_coin::AptosCoin, 0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated>)" }, hash: OnceCell(Uninit) }: Modification(0100000000000000520000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948042b000000000000530000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948820e000000000000540000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9483f24010000000000550000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9483600000000000000560000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9488929010000000000570000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9480000000000000000580000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c9480000000000000000590000000000000005a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948), StateKey { inner: AccessPath { address: 0x5a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948, path: "Resource(0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::liquidity_pool::LiquidityPool<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC, 0x1::aptos_coin::AptosCoin, 0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated>)" }, hash: OnceCell(Uninit) }: Modification(c22c8a848200000036883bed3905000085cb55630000000088581db90c1163babce7570000000000a06a28988cc287b1f5b5c80000000000000000000000000000000000000000000000001e000000000000002100000000000000), StateKey { inner: AccessPath { address: 0x63a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(88553a6500000000000100000000000000020000000000000063a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d0200000000000000030000000000000063a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d), StateKey { inner: AccessPath { address: 0x63a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d, path: "Resource(0x1::coin::CoinStore<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC>)" }, hash: OnceCell(Uninit) }: Modification(28aa370100000000000100000000000000060000000000000063a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d0000000000000000070000000000000063a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d)} })) - events:[ContractEvent { key: EventKey { creation_number: 3, account_address: 63a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d }, index: 1, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "8058840c00000000" }, ContractEvent { key: EventKey { creation_number: 80, account_address: 05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948 }, index: 74868, type: Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("dao_storage"), name: Identifier("CoinDepositedEvent"), type_params: [Struct(StructTag { address: f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa, module: Identifier("asset"), name: Identifier("USDC"), type_params: [] }), Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("aptos_coin"), name: Identifier("AptosCoin"), type_params: [] }), Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("curves"), name: Identifier("Uncorrelated"), type_params: [] })] }), event_data: "00000000000000005034030000000000" }, ContractEvent { key: EventKey { creation_number: 87, account_address: 05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948 }, index: 76168, type: Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("liquidity_pool"), name: Identifier("OracleUpdatedEvent"), type_params: [Struct(StructTag { address: f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa, module: Identifier("asset"), name: Identifier("USDC"), type_params: [] }), Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("aptos_coin"), name: Identifier("AptosCoin"), type_params: [] }), Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("curves"), name: Identifier("Uncorrelated"), type_params: [] })] }), event_data: "88581db90c1163babce7570000000000a06a28988cc287b1f5b5c80000000000" }, ContractEvent { key: EventKey { creation_number: 85, account_address: 05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948 }, index: 74814, type: Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("liquidity_pool"), name: Identifier("SwapEvent"), type_params: [Struct(StructTag { address: f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa, module: Identifier("asset"), name: Identifier("USDC"), type_params: [] }), Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("aptos_coin"), name: Identifier("AptosCoin"), type_params: [] }), Struct(StructTag { address: 190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12, module: Identifier("curves"), name: Identifier("Uncorrelated"), type_params: [] })] }), event_data: "000000000000000028aa3701000000008058840c000000000000000000000000" }, ContractEvent { key: EventKey { creation_number: 6, account_address: 63a87c4a0e717a999f96f8307aa540f30108f30dbae89ec84830874a48c4261d }, index: 0, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "28aa370100000000" }] - -version:10000006 -write set:V0(WriteSetV0(WriteSetMut { write_set: {StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(6ef5a5ac6800000000aa2501000000000002000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2040000000000000003000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::events::Events)" }, hash: OnceCell(Uninit) }: Modification(580700000000000004000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2b60000000000000005000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a20f0500000000000006000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2901600000000000007000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2db0e00000000000008000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2051000000000000009000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a244e10100000000000a000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a26e880000000000000b000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2ee140100000000000c000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a295160000000000000d000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a225150000000000000e000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x49f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(9c95eb0f00000000000d00000000000000020000000000000049f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f9232400000000000000030000000000000049f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923), StateKey { inner: AccessPath { address: 0x49f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::token_coin_swap::TokenListings<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(bbbaa5ca4e719b398a46267d3cff56ae85098a611a8360c043a188a4b77745d80900000000000000080000000000000049f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f9230600000000000000090000000000000049f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923), StateKey { inner: AccessPath { address: 0xc6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(a02577650100000000ef0c0000000000000200000000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a1a000000000000000300000000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a), StateKey { inner: AccessPath { address: 0xf1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(1e963f50000000000009000000000000000200000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c889361e000000000000000300000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936), StateKey { inner: AccessPath { address: 0xf1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936, path: "Resource(0x3::token::TokenStore)" }, hash: OnceCell(Uninit) }: Modification(605e1ce5490b6a0a65d3b1aa6d696a24807e5ab8c4404764054870f3cdba551e000a000000000000000400000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c8893605000000000000000500000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c8893600000000000000000600000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c8893600000000000000000700000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936), StateKey { inner: TableItem { handle: 605e1ce5490b6a0a65d3b1aa6d696a24807e5ab8c4404764054870f3cdba551e, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000 }, hash: OnceCell(Uninit) }: Creation(c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000010000000000000000), StateKey { inner: TableItem { handle: bbbaa5ca4e719b398a46267d3cff56ae85098a611a8360c043a188a4b77745d8, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: d2d137b816a2e903591cf9a9f564dfa52fbb97ef4eadec12bb6ad765a637b6a9, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: e32a79149395a3cb3611fd30f748be07c49adb10c36e4ff7cda52708e7ac025a, key: 2de1010000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: ef0b3bbc34b67651a375d55742210a087164028a83305c128077b48c13cd9f5d, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000 }, hash: OnceCell(Uninit) }: Deletion} })) - events:[ContractEvent { key: EventKey { creation_number: 4, account_address: f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936 }, index: 9, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000003, module: Identifier("token"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f72202335313200000000000000000100000000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936 }, index: 27, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "506b620000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a }, index: 3310, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "506b620000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936 }, index: 28, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "a835310000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 75177, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "a835310000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936 }, index: 29, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "48c11c0700000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 49f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923 }, index: 12, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "48c11c0700000000" }, ContractEvent { key: EventKey { creation_number: 9, account_address: 49f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923 }, index: 5, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("token_coin_swap"), name: Identifier("TokenSwapEvent"), type_params: [] }), event_data: "c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233531320000000000000000f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c8893601000000000000004062b0070000000000000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e" }, ContractEvent { key: EventKey { creation_number: 12, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 70893, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("events"), name: Identifier("BuyEvent"), type_params: [] }), event_data: "c8a0c0e0bbeb05002de1010000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f72202335313200000000000000004062b00700000000010000000000000049f7a9ef702c13b57e983ca6e2149bbe37f4e6c4c689734b26d0628b6714f923f1231ef24d1140dfda6d33bbc5f455846b29c29925abf8db9f531d1394c88936" }] - -version:10000007 -write set:V0(WriteSetV0(WriteSetMut { write_set: {StateKey { inner: AccessPath { address: 0x715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de, path: "Resource(0x1::account::Account)" }, hash: OnceCell(Uninit) }: Modification(20715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de0200000000000000110000000000000001000000000000000000000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000100000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de0000), StateKey { inner: AccessPath { address: 0x715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de, path: "Resource(0x3::token::TokenStore)" }, hash: OnceCell(Uninit) }: Creation(55903940dcb2c33615770ec72e3fc8aebdb984585276948509282e4883114fad0100000000000000000400000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000500000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000600000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000700000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de), StateKey { inner: AccessPath { address: 0x715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de, path: "Resource(0x3::token_event_store::TokenEventStoreV1)" }, hash: OnceCell(Uninit) }: Creation(00000000000000000800000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000900000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000a00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de01000000000000000b00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000c00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000d00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000e00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000000f00000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00000000000000001000000000000000715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de00)} })) - events:[ContractEvent { key: EventKey { creation_number: 11, account_address: 715a4789232f9d79c1a03cb9f0d890a96607c8aa68729b2c8ba29abc8d3bb9de }, index: 0, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000003, module: Identifier("token_event_store"), name: Identifier("OptInTransferEvent"), type_params: [] }), event_data: "01" }] - -version:10000010 -write set:V0(WriteSetV0(WriteSetMut { write_set: {StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(0675d9ac6800000000ab2501000000000002000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2040000000000000003000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::events::Events)" }, hash: OnceCell(Uninit) }: Modification(580700000000000004000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2b60000000000000005000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a20f0500000000000006000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2901600000000000007000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2db0e00000000000008000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2051000000000000009000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a244e10100000000000a000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a26e880000000000000b000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2ef140100000000000c000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a295160000000000000d000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a225150000000000000e000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x7731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(e69ac3070000000000060000000000000002000000000000007731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24180000000000000003000000000000007731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24), StateKey { inner: AccessPath { address: 0x7731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::token_coin_swap::TokenListings<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(74952190dc36c88c349ef4805b71952244352a1d805a224b18945901ab2ec950050000000000000008000000000000007731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24030000000000000009000000000000007731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24), StateKey { inner: AccessPath { address: 0xc6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(d024de650100000000f00c0000000000000200000000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a1a000000000000000300000000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a), StateKey { inner: AccessPath { address: 0xdd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(4062b007000000000001000000000000000200000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c03000000000000000300000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c), StateKey { inner: AccessPath { address: 0xdd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c, path: "Resource(0x1::account::Account)" }, hash: OnceCell(Uninit) }: Modification(20dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c0000000000000000080000000000000001000000000000000000000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c00000000000000000100000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c0000), StateKey { inner: AccessPath { address: 0xdd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c, path: "Resource(0x3::token::TokenStore)" }, hash: OnceCell(Uninit) }: Creation(55903940dcb2c33615770ec72e3fc8aebdb984585276948509282e4883114fad0001000000000000000400000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c00000000000000000500000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c00000000000000000600000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c00000000000000000700000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c), StateKey { inner: TableItem { handle: 1ae6ad6af049cbfa981d8a326cb1d2707f325f6162a3589d526db6f4dca708f1, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: 55903940dcb2c33615770ec72e3fc8aebdb984585276948509282e4883114fad, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000 }, hash: OnceCell(Uninit) }: Creation(c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000010000000000000000), StateKey { inner: TableItem { handle: 74952190dc36c88c349ef4805b71952244352a1d805a224b18945901ab2ec950, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: d2d137b816a2e903591cf9a9f564dfa52fbb97ef4eadec12bb6ad765a637b6a9, key: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: e32a79149395a3cb3611fd30f748be07c49adb10c36e4ff7cda52708e7ac025a, key: 18e1010000000000 }, hash: OnceCell(Uninit) }: Deletion} })) - events:[ContractEvent { key: EventKey { creation_number: 4, account_address: dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c }, index: 0, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000003, module: Identifier("token"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f72202336313300000000000000000100000000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c }, index: 0, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "30ff660000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a }, index: 3311, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "30ff660000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c }, index: 1, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "987f330000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 75178, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "987f330000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c }, index: 2, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "f870710700000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 7731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24 }, index: 5, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "f870710700000000" }, ContractEvent { key: EventKey { creation_number: 9, account_address: 7731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24 }, index: 2, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("token_coin_swap"), name: Identifier("TokenSwapEvent"), type_params: [] }), event_data: "c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c0100000000000000c0ef0b080000000000000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e" }, ContractEvent { key: EventKey { creation_number: 12, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 70894, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("events"), name: Identifier("BuyEvent"), type_params: [] }), event_data: "36edc4e0bbeb050018e1010000000000c6dc71caab87ad0108eb647c126630b4fef94b66d4fb9c9b4a258cee2fafe89a0f4170746f7320416c6c696761746f72144170746f7320416c6c696761746f7220233631330000000000000000c0ef0b080000000001000000000000007731e9598b7dbdcf64da0444c21d6ddcb4fce3264610aa1feb3a4389a1d94b24dd7cf62c6b829d67b3abbadcb28e20c2236879e022cf1af4cd8572b69464d35c" }] - -version:10000011 -write set:V0(WriteSetV0(WriteSetMut { write_set: {StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(561548ad6800000000ac2501000000000002000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2040000000000000003000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::events::Events)" }, hash: OnceCell(Uninit) }: Modification(580700000000000004000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2b60000000000000005000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a20f0500000000000006000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2901600000000000007000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2db0e00000000000008000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2051000000000000009000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a244e10100000000000a000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a26e880000000000000b000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2f0140100000000000c000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a295160000000000000d000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a225150000000000000e000000000000002c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2), StateKey { inner: AccessPath { address: 0x3447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(3e1bce8700000000006c0000000000000002000000000000003447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa950100000000000003000000000000003447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa), StateKey { inner: AccessPath { address: 0x3447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa, path: "Resource(0x2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2::token_coin_swap::TokenListings<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(ee329e7815febce236234dc1dc9ebcba4143fde35f9b5b8b79de903895f148bda80000000000000018000000000000003447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa500000000000000019000000000000003447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa), StateKey { inner: AccessPath { address: 0x98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(1c59d8cf00000000000400000000000000020000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf3690481500000000000000030000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048), StateKey { inner: AccessPath { address: 0x98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048, path: "Resource(0x3::token::TokenStore)" }, hash: OnceCell(Uninit) }: Modification(2cfab8a74460d344aabb7773dc5f424b5d68dace9acb17a089729e2e7a48eccd000700000000000000040000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf3690480300000000000000050000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf3690480000000000000000060000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf3690480000000000000000070000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048), StateKey { inner: AccessPath { address: 0xe21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de, path: "Resource(0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>)" }, hash: OnceCell(Uninit) }: Modification(3b805d0e00000000009b000000000000000200000000000000e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de03000000000000000300000000000000e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de), StateKey { inner: TableItem { handle: 2cfab8a74460d344aabb7773dc5f424b5d68dace9acb17a089729e2e7a48eccd, key: e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000 }, hash: OnceCell(Uninit) }: Creation(e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000010000000000000000), StateKey { inner: TableItem { handle: b43f6fd288d992eaffd946148fd2f9c8f34c3b86ff0b4f4ad241740f0dd16c5d, key: e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: d2d137b816a2e903591cf9a9f564dfa52fbb97ef4eadec12bb6ad765a637b6a9, key: e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: e32a79149395a3cb3611fd30f748be07c49adb10c36e4ff7cda52708e7ac025a, key: f5d4010000000000 }, hash: OnceCell(Uninit) }: Deletion, StateKey { inner: TableItem { handle: ee329e7815febce236234dc1dc9ebcba4143fde35f9b5b8b79de903895f148bd, key: e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000 }, hash: OnceCell(Uninit) }: Deletion} })) - events:[ContractEvent { key: EventKey { creation_number: 4, account_address: 98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048 }, index: 6, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000003, module: Identifier("token"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f73204372656570732023323500000000000000000100000000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: 98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048 }, index: 18, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "b060f30000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de }, index: 154, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "b060f30000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: 98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048 }, index: 19, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "50a06e0000000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 75179, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "50a06e0000000000" }, ContractEvent { key: EventKey { creation_number: 3, account_address: 98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048 }, index: 20, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("WithdrawEvent"), type_params: [] }), event_data: "800be70f00000000" }, ContractEvent { key: EventKey { creation_number: 2, account_address: 3447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa }, index: 107, type: Struct(StructTag { address: 0000000000000000000000000000000000000000000000000000000000000001, module: Identifier("coin"), name: Identifier("DepositEvent"), type_params: [] }), event_data: "800be70f00000000" }, ContractEvent { key: EventKey { creation_number: 25, account_address: 3447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa }, index: 79, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("token_coin_swap"), name: Identifier("TokenSwapEvent"), type_params: [] }), event_data: "e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f732043726565707320233235000000000000000098d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf3690480100000000000000800c49110000000000000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e" }, ContractEvent { key: EventKey { creation_number: 12, account_address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2 }, index: 70895, type: Struct(StructTag { address: 2c7bccf7b31baf770fdbcc768d9e9cb3d87805e255355df5db32ac9a669010a2, module: Identifier("events"), name: Identifier("BuyEvent"), type_params: [] }), event_data: "36edc4e0bbeb0500f5d4010000000000e21bc674fd9f76df06823efc8c6aea4c7a20fc40aa4aaf923e4c04a7546797de0c4170746f7320437265657073104170746f7320437265657073202332350000000000000000800c49110000000001000000000000003447d5813fcf570f5c313cbf484592072b8188584974a398cc513064065758fa98d3afcb9fc5b38e4306a471775d466a0e1c890e9828aba276a85b4cbf369048" }] - diff --git a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/version_index.txt b/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/version_index.txt deleted file mode 100644 index 3efd029956b68..0000000000000 --- a/aptos-move/aptos-e2e-comparison-testing/test-data-mainnet-10m-15/version_index.txt +++ /dev/null @@ -1,5 +0,0 @@ -10000003 -10000006 -10000007 -10000010 -10000011 diff --git a/aptos-move/aptos-gas-calibration/Cargo.toml b/aptos-move/aptos-gas-calibration/Cargo.toml deleted file mode 100644 index 6a9ced7550a47..0000000000000 --- a/aptos-move/aptos-gas-calibration/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "aptos-gas-calibration" -version = "0.1.0" -edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true } -aptos-abstract-gas-usage = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas-algebra = { workspace = true } -aptos-gas-meter = { workspace = true } -aptos-gas-schedule = { workspace = true } -aptos-language-e2e-tests = { workspace = true } -aptos-move-stdlib = { workspace = true } -aptos-native-interface = { workspace = true } -aptos-types = { workspace = true } -aptos-vm-types = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -float-cmp = { workspace = true } -move-binary-format = { workspace = true } -move-bytecode-source-map = { workspace = true } -move-core-types = { workspace = true } -move-ir-compiler = { workspace = true } -move-vm-runtime = { workspace = true } -move-vm-test-utils = { workspace = true } -nalgebra = { workspace = true } -walkdir = { workspace = true } diff --git a/aptos-move/aptos-gas-calibration/README.md b/aptos-move/aptos-gas-calibration/README.md deleted file mode 100644 index e1d34e932bd50..0000000000000 --- a/aptos-move/aptos-gas-calibration/README.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -id: aptos-gas-calibration -title: Aptos Automated Gas Calibration ---- - -## Aptos Automated Gas Calibration - -The Aptos Automated Gas Calibration is a tool that lets anyone write Move Samples (or also Move IR) to calibrate the gas parameters for Native Functions (and also Move bytecode instructions). - -### Terminology: - -- User: Anyone writing Move Native Functions. -- Move Sample: A Move package in the `/samples` directory. -- Abstract Gas Usage: Records the number of times a gas parameter has been called. -- Calibration Function: A function used to track the running time and Abstract Gas Usage. - -### How the system works at a high-level: - -1. User implements the Native function and respective gas formula. -2. Write a Move Sample and some Calibration Functions. -3. Determine the Abstract Gas Usage of the Calibration Function to create a gas formula. -4. Determine the running time of the Calibration Function and formulate a linear equation. -5. Repeat 3-4 for all Move Samples in `/samples` -6. Solve the system of linear equations using Linear Algebra -7. Output a result to the User (the gas parameter costs, or any outliers, or gas parameters that couldn’t be solved) - -## Creating a Move Sample - -Create a Move project under `/samples` by running: - -```bash -mkdir MY-PROJECT -cd MY-PROJECT -cargo run -p aptos -- move init --name MY-PROJECT -``` - -## Writing Your First Calibration Function - -Calibration Functions need to be marked with `entry` and have a prefix of `calibrate_`. For example, the following functions would work: - -```Move -//// VALID -public entry fun calibrate() {} - -public entry fun calibrate_another_txn() {} - -public entry fun calibrate123() {} - -//// INVALID -public fun calibrate() {} - -public fun test_my_txn() {} - -public entry fun calibrate_addition(_x: u64, _y: u64) {} -``` - -If the Calibration Function is expected to error, please denote it with the postfix `_should_error`. - -``` -//// VALID -public entry fun calibrate_my_test_should_error() {} - -public entry fun calibrate_should_error() {} - -//// INVALID -public entry fun should_error_calibrate() {} - -public entry fun calibrate_test_error() {} - -public entry fun calibrate_test_should_error_() {} -``` -Note: These are still valid Calibration Functions that will still run, but would not error. - -## Usage - -```bash -cargo run --release -- --help -Automated Gas Calibration to calibrate Move bytecode and Native Functions - -Usage: aptos-gas-calibration [OPTIONS] - -Options: - -p, --pattern Specific tests to run that match a pattern [default: ""] - -i, --iterations Number of iterations to run each Calibration Function [default: 20] - -m, --max_execution_time Maximum execution time in milliseconds [default: 300] - -h, --help Print help -``` - -## Examples - -There are examples of how to write Calibration Functions under `/samples_ir` and `/samples`. There will be more examples in the future as more Users write Move Samples and add it to the calibration set. - -Here is an example written in the Move source language: -```Move -public fun calibrate_blake2b_256_impl(num_iterations: u64) { - let i = 0; - let msg = b"abcdefghijkl" - while i < num_iterations { - // This is what I want to calibrate: - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - aptos_hash::blake2b_256(msg); - i += 1; - } -} - -public entry fun calibrate_blake2b_256_x500() { - calibrate_blake2b_256_impl(50); -} - -public entry fun calibrate_blake2b_256_x1000() { - calibrate_blake2b_256_impl(100); -} - -public entry fun calibrate_blake2b_256_x5000() { - calibrate_blake2b_256_impl(500); -} -``` -As you can see in this example, we want the thing that we are trying to calibrate to run for a long enough time, and to also be called enough times. This is why we have the Native Function being called 10 times. Furthermore, we want to sample different lengths (discussed in more detail below), which is why we have data points at 500, 1000, and 5000. This allows the system to record an accurate and appropriate gas usage of the instructions being called, while finding the best line of fit. - -## FAQ - -### How many Calibration Functions to provide? - -In order for the system to find deterministic values for the gas parameters, the number of Calibration Functions needs to be at least the number of linearly independent samples for each gas parameter that we’re solving. For example, if a User was calling a function, it would use the gas parameters: `CALL_BASE`, `CALL_PER_ARG`, `CALL_PER_LOCAL`, which would require at least three Calibration Functions. - -### How do I write "good" Calibration Functions? - -A good Calibration Function should run for many iterations (i.e., see `/samples_ir/ld/ldu8.mvir`). This allows the system to record a good representation of the gas usage. Furthermore, the idea is that we are sampling different data points at different "lengths" to approximate a line of best fit. In the `ldu8.mvir` example, we have the data points at 100, 500, and 1000 iterations. - -### How are the gas parameters calculated? - -For every Calibration Function, the Abstract Gas Usage and running time are determined. This forms a linear equation. For all the Calibration Functions, we can create a system of lienar equations. To solve this system of linear equations, we compute the Least Squares Solution. - -If the matrix that represents this system is not invertible, then we report the undetermined gas parameters, or the linearly dependent combinations of gas parameters. The exact math can be found under `/src/math.rs`. - -Otherwise, the User can expect to see all the values, along with the running times and any outliers. - -### I see "linearly dependent variables" instead of the gas costs, what do I do? - -If you happen to see something like: - -``` -linearly dependent variables are: - -- gas parameter: HASH_BLAKE2B_256_BASE -- gas parameter: HASH_BLAKE2B_256_PER_BYTE -``` - -There are a few reasons as to why this would happen. The first reason would be that you may have an insufficient number of Calibration Functions for the gas parameters you are trying to calculate. In this example, there should be at least two Calibration Functions, since there are two gas parameters. Another reason would be that too many of the Calibration Functions are linearly dependent. That is, try writing them using different input sizes and varying number of iterations. - diff --git a/aptos-move/aptos-gas-calibration/samples/empty/Move.toml b/aptos-move/aptos-gas-calibration/samples/empty/Move.toml deleted file mode 100644 index 0ada5b49e2931..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples/empty/Move.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "empty" -version = "1.0.0" -authors = [] - -[addresses] -empty = "0xcafe" - -[dev-addresses] - -[dependencies.AptosFramework] -git = "https://github.com/aptos-labs/aptos-core.git" -rev = "mainnet" -subdir = "aptos-move/framework/aptos-framework" - -[dev-dependencies] diff --git a/aptos-move/aptos-gas-calibration/samples/empty/sources/Empty.move b/aptos-move/aptos-gas-calibration/samples/empty/sources/Empty.move deleted file mode 100644 index c5d9220a09e1c..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples/empty/sources/Empty.move +++ /dev/null @@ -1,5 +0,0 @@ -module empty::empty { - public entry fun calibrate_empty_test() { - return - } -} diff --git a/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core-loop.mvir b/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core-loop.mvir deleted file mode 100644 index 54f3eb0ee4c93..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core-loop.mvir +++ /dev/null @@ -1,4567 +0,0 @@ -module 0xcafe.BootstrapCoreLoop { - // A minimal set of samples that allow us to calibrate all the instructions required - // to form a basic for loop. - // - st_loc - // - ld_const - // - lt - // - branch - // - add - // - ld_false - // - br_false - // - copy_loc (base + per_abs_val_unit) - // - move_loc - - public entry calibrate_move_loc_x10() { - let a: u64; - let b: u64; - label b0: - a = 5; - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - return; - } - - public entry calibrate_move_loc_x50() { - let a: u64; - let b: u64; - label b0: - a = 5; - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - return; - } - - public entry calibrate_move_loc_x100() { - let a: u64; - let b: u64; - label b0: - a = 5; - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - return; - } - - public entry calibrate_move_loc_x300() { - let a: u64; - let b: u64; - label b0: - a = 5; - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - return; - } - - public entry calibrate_move_loc_x600() { - let a: u64; - let b: u64; - label b0: - a = 5; - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); b = move(a); a = move(b); - - return; - } - - - - public entry calibrate_br_false_x10() { - label b0: jump_if_false (false) b1; - label b1: jump_if_false (false) b2; - label b2: jump_if_false (false) b3; - label b3: jump_if_false (false) b4; - label b4: jump_if_false (false) b5; - label b5: jump_if_false (false) b6; - label b6: jump_if_false (false) b7; - label b7: jump_if_false (false) b8; - label b8: jump_if_false (false) b9; - label b9: jump_if_false (false) b10; - label b10: - return; - } - - public entry calibrate_br_false_x50() { - label b0: jump_if_false (false) b1; - label b1: jump_if_false (false) b2; - label b2: jump_if_false (false) b3; - label b3: jump_if_false (false) b4; - label b4: jump_if_false (false) b5; - label b5: jump_if_false (false) b6; - label b6: jump_if_false (false) b7; - label b7: jump_if_false (false) b8; - label b8: jump_if_false (false) b9; - label b9: jump_if_false (false) b10; - label b10: jump_if_false (false) b11; - label b11: jump_if_false (false) b12; - label b12: jump_if_false (false) b13; - label b13: jump_if_false (false) b14; - label b14: jump_if_false (false) b15; - label b15: jump_if_false (false) b16; - label b16: jump_if_false (false) b17; - label b17: jump_if_false (false) b18; - label b18: jump_if_false (false) b19; - label b19: jump_if_false (false) b20; - label b20: jump_if_false (false) b21; - label b21: jump_if_false (false) b22; - label b22: jump_if_false (false) b23; - label b23: jump_if_false (false) b24; - label b24: jump_if_false (false) b25; - label b25: jump_if_false (false) b26; - label b26: jump_if_false (false) b27; - label b27: jump_if_false (false) b28; - label b28: jump_if_false (false) b29; - label b29: jump_if_false (false) b30; - label b30: jump_if_false (false) b31; - label b31: jump_if_false (false) b32; - label b32: jump_if_false (false) b33; - label b33: jump_if_false (false) b34; - label b34: jump_if_false (false) b35; - label b35: jump_if_false (false) b36; - label b36: jump_if_false (false) b37; - label b37: jump_if_false (false) b38; - label b38: jump_if_false (false) b39; - label b39: jump_if_false (false) b40; - label b40: jump_if_false (false) b41; - label b41: jump_if_false (false) b42; - label b42: jump_if_false (false) b43; - label b43: jump_if_false (false) b44; - label b44: jump_if_false (false) b45; - label b45: jump_if_false (false) b46; - label b46: jump_if_false (false) b47; - label b47: jump_if_false (false) b48; - label b48: jump_if_false (false) b49; - label b49: jump_if_false (false) b50; - label b50: - return; - } - - public entry calibrate_br_false_x500() { - label b0: jump_if_false (false) b1; - label b1: jump_if_false (false) b2; - label b2: jump_if_false (false) b3; - label b3: jump_if_false (false) b4; - label b4: jump_if_false (false) b5; - label b5: jump_if_false (false) b6; - label b6: jump_if_false (false) b7; - label b7: jump_if_false (false) b8; - label b8: jump_if_false (false) b9; - label b9: jump_if_false (false) b10; - label b10: jump_if_false (false) b11; - label b11: jump_if_false (false) b12; - label b12: jump_if_false (false) b13; - label b13: jump_if_false (false) b14; - label b14: jump_if_false (false) b15; - label b15: jump_if_false (false) b16; - label b16: jump_if_false (false) b17; - label b17: jump_if_false (false) b18; - label b18: jump_if_false (false) b19; - label b19: jump_if_false (false) b20; - label b20: jump_if_false (false) b21; - label b21: jump_if_false (false) b22; - label b22: jump_if_false (false) b23; - label b23: jump_if_false (false) b24; - label b24: jump_if_false (false) b25; - label b25: jump_if_false (false) b26; - label b26: jump_if_false (false) b27; - label b27: jump_if_false (false) b28; - label b28: jump_if_false (false) b29; - label b29: jump_if_false (false) b30; - label b30: jump_if_false (false) b31; - label b31: jump_if_false (false) b32; - label b32: jump_if_false (false) b33; - label b33: jump_if_false (false) b34; - label b34: jump_if_false (false) b35; - label b35: jump_if_false (false) b36; - label b36: jump_if_false (false) b37; - label b37: jump_if_false (false) b38; - label b38: jump_if_false (false) b39; - label b39: jump_if_false (false) b40; - label b40: jump_if_false (false) b41; - label b41: jump_if_false (false) b42; - label b42: jump_if_false (false) b43; - label b43: jump_if_false (false) b44; - label b44: jump_if_false (false) b45; - label b45: jump_if_false (false) b46; - label b46: jump_if_false (false) b47; - label b47: jump_if_false (false) b48; - label b48: jump_if_false (false) b49; - label b49: jump_if_false (false) b50; - label b50: jump_if_false (false) b51; - label b51: jump_if_false (false) b52; - label b52: jump_if_false (false) b53; - label b53: jump_if_false (false) b54; - label b54: jump_if_false (false) b55; - label b55: jump_if_false (false) b56; - label b56: jump_if_false (false) b57; - label b57: jump_if_false (false) b58; - label b58: jump_if_false (false) b59; - label b59: jump_if_false (false) b60; - label b60: jump_if_false (false) b61; - label b61: jump_if_false (false) b62; - label b62: jump_if_false (false) b63; - label b63: jump_if_false (false) b64; - label b64: jump_if_false (false) b65; - label b65: jump_if_false (false) b66; - label b66: jump_if_false (false) b67; - label b67: jump_if_false (false) b68; - label b68: jump_if_false (false) b69; - label b69: jump_if_false (false) b70; - label b70: jump_if_false (false) b71; - label b71: jump_if_false (false) b72; - label b72: jump_if_false (false) b73; - label b73: jump_if_false (false) b74; - label b74: jump_if_false (false) b75; - label b75: jump_if_false (false) b76; - label b76: jump_if_false (false) b77; - label b77: jump_if_false (false) b78; - label b78: jump_if_false (false) b79; - label b79: jump_if_false (false) b80; - label b80: jump_if_false (false) b81; - label b81: jump_if_false (false) b82; - label b82: jump_if_false (false) b83; - label b83: jump_if_false (false) b84; - label b84: jump_if_false (false) b85; - label b85: jump_if_false (false) b86; - label b86: jump_if_false (false) b87; - label b87: jump_if_false (false) b88; - label b88: jump_if_false (false) b89; - label b89: jump_if_false (false) b90; - label b90: jump_if_false (false) b91; - label b91: jump_if_false (false) b92; - label b92: jump_if_false (false) b93; - label b93: jump_if_false (false) b94; - label b94: jump_if_false (false) b95; - label b95: jump_if_false (false) b96; - label b96: jump_if_false (false) b97; - label b97: jump_if_false (false) b98; - label b98: jump_if_false (false) b99; - label b99: jump_if_false (false) b100; - label b100: jump_if_false (false) b101; - label b101: jump_if_false (false) b102; - label b102: jump_if_false (false) b103; - label b103: jump_if_false (false) b104; - label b104: jump_if_false (false) b105; - label b105: jump_if_false (false) b106; - label b106: jump_if_false (false) b107; - label b107: jump_if_false (false) b108; - label b108: jump_if_false (false) b109; - label b109: jump_if_false (false) b110; - label b110: jump_if_false (false) b111; - label b111: jump_if_false (false) b112; - label b112: jump_if_false (false) b113; - label b113: jump_if_false (false) b114; - label b114: jump_if_false (false) b115; - label b115: jump_if_false (false) b116; - label b116: jump_if_false (false) b117; - label b117: jump_if_false (false) b118; - label b118: jump_if_false (false) b119; - label b119: jump_if_false (false) b120; - label b120: jump_if_false (false) b121; - label b121: jump_if_false (false) b122; - label b122: jump_if_false (false) b123; - label b123: jump_if_false (false) b124; - label b124: jump_if_false (false) b125; - label b125: jump_if_false (false) b126; - label b126: jump_if_false (false) b127; - label b127: jump_if_false (false) b128; - label b128: jump_if_false (false) b129; - label b129: jump_if_false (false) b130; - label b130: jump_if_false (false) b131; - label b131: jump_if_false (false) b132; - label b132: jump_if_false (false) b133; - label b133: jump_if_false (false) b134; - label b134: jump_if_false (false) b135; - label b135: jump_if_false (false) b136; - label b136: jump_if_false (false) b137; - label b137: jump_if_false (false) b138; - label b138: jump_if_false (false) b139; - label b139: jump_if_false (false) b140; - label b140: jump_if_false (false) b141; - label b141: jump_if_false (false) b142; - label b142: jump_if_false (false) b143; - label b143: jump_if_false (false) b144; - label b144: jump_if_false (false) b145; - label b145: jump_if_false (false) b146; - label b146: jump_if_false (false) b147; - label b147: jump_if_false (false) b148; - label b148: jump_if_false (false) b149; - label b149: jump_if_false (false) b150; - label b150: jump_if_false (false) b151; - label b151: jump_if_false (false) b152; - label b152: jump_if_false (false) b153; - label b153: jump_if_false (false) b154; - label b154: jump_if_false (false) b155; - label b155: jump_if_false (false) b156; - label b156: jump_if_false (false) b157; - label b157: jump_if_false (false) b158; - label b158: jump_if_false (false) b159; - label b159: jump_if_false (false) b160; - label b160: jump_if_false (false) b161; - label b161: jump_if_false (false) b162; - label b162: jump_if_false (false) b163; - label b163: jump_if_false (false) b164; - label b164: jump_if_false (false) b165; - label b165: jump_if_false (false) b166; - label b166: jump_if_false (false) b167; - label b167: jump_if_false (false) b168; - label b168: jump_if_false (false) b169; - label b169: jump_if_false (false) b170; - label b170: jump_if_false (false) b171; - label b171: jump_if_false (false) b172; - label b172: jump_if_false (false) b173; - label b173: jump_if_false (false) b174; - label b174: jump_if_false (false) b175; - label b175: jump_if_false (false) b176; - label b176: jump_if_false (false) b177; - label b177: jump_if_false (false) b178; - label b178: jump_if_false (false) b179; - label b179: jump_if_false (false) b180; - label b180: jump_if_false (false) b181; - label b181: jump_if_false (false) b182; - label b182: jump_if_false (false) b183; - label b183: jump_if_false (false) b184; - label b184: jump_if_false (false) b185; - label b185: jump_if_false (false) b186; - label b186: jump_if_false (false) b187; - label b187: jump_if_false (false) b188; - label b188: jump_if_false (false) b189; - label b189: jump_if_false (false) b190; - label b190: jump_if_false (false) b191; - label b191: jump_if_false (false) b192; - label b192: jump_if_false (false) b193; - label b193: jump_if_false (false) b194; - label b194: jump_if_false (false) b195; - label b195: jump_if_false (false) b196; - label b196: jump_if_false (false) b197; - label b197: jump_if_false (false) b198; - label b198: jump_if_false (false) b199; - label b199: jump_if_false (false) b200; - label b200: jump_if_false (false) b201; - label b201: jump_if_false (false) b202; - label b202: jump_if_false (false) b203; - label b203: jump_if_false (false) b204; - label b204: jump_if_false (false) b205; - label b205: jump_if_false (false) b206; - label b206: jump_if_false (false) b207; - label b207: jump_if_false (false) b208; - label b208: jump_if_false (false) b209; - label b209: jump_if_false (false) b210; - label b210: jump_if_false (false) b211; - label b211: jump_if_false (false) b212; - label b212: jump_if_false (false) b213; - label b213: jump_if_false (false) b214; - label b214: jump_if_false (false) b215; - label b215: jump_if_false (false) b216; - label b216: jump_if_false (false) b217; - label b217: jump_if_false (false) b218; - label b218: jump_if_false (false) b219; - label b219: jump_if_false (false) b220; - label b220: jump_if_false (false) b221; - label b221: jump_if_false (false) b222; - label b222: jump_if_false (false) b223; - label b223: jump_if_false (false) b224; - label b224: jump_if_false (false) b225; - label b225: jump_if_false (false) b226; - label b226: jump_if_false (false) b227; - label b227: jump_if_false (false) b228; - label b228: jump_if_false (false) b229; - label b229: jump_if_false (false) b230; - label b230: jump_if_false (false) b231; - label b231: jump_if_false (false) b232; - label b232: jump_if_false (false) b233; - label b233: jump_if_false (false) b234; - label b234: jump_if_false (false) b235; - label b235: jump_if_false (false) b236; - label b236: jump_if_false (false) b237; - label b237: jump_if_false (false) b238; - label b238: jump_if_false (false) b239; - label b239: jump_if_false (false) b240; - label b240: jump_if_false (false) b241; - label b241: jump_if_false (false) b242; - label b242: jump_if_false (false) b243; - label b243: jump_if_false (false) b244; - label b244: jump_if_false (false) b245; - label b245: jump_if_false (false) b246; - label b246: jump_if_false (false) b247; - label b247: jump_if_false (false) b248; - label b248: jump_if_false (false) b249; - label b249: jump_if_false (false) b250; - label b250: jump_if_false (false) b251; - label b251: jump_if_false (false) b252; - label b252: jump_if_false (false) b253; - label b253: jump_if_false (false) b254; - label b254: jump_if_false (false) b255; - label b255: jump_if_false (false) b256; - label b256: jump_if_false (false) b257; - label b257: jump_if_false (false) b258; - label b258: jump_if_false (false) b259; - label b259: jump_if_false (false) b260; - label b260: jump_if_false (false) b261; - label b261: jump_if_false (false) b262; - label b262: jump_if_false (false) b263; - label b263: jump_if_false (false) b264; - label b264: jump_if_false (false) b265; - label b265: jump_if_false (false) b266; - label b266: jump_if_false (false) b267; - label b267: jump_if_false (false) b268; - label b268: jump_if_false (false) b269; - label b269: jump_if_false (false) b270; - label b270: jump_if_false (false) b271; - label b271: jump_if_false (false) b272; - label b272: jump_if_false (false) b273; - label b273: jump_if_false (false) b274; - label b274: jump_if_false (false) b275; - label b275: jump_if_false (false) b276; - label b276: jump_if_false (false) b277; - label b277: jump_if_false (false) b278; - label b278: jump_if_false (false) b279; - label b279: jump_if_false (false) b280; - label b280: jump_if_false (false) b281; - label b281: jump_if_false (false) b282; - label b282: jump_if_false (false) b283; - label b283: jump_if_false (false) b284; - label b284: jump_if_false (false) b285; - label b285: jump_if_false (false) b286; - label b286: jump_if_false (false) b287; - label b287: jump_if_false (false) b288; - label b288: jump_if_false (false) b289; - label b289: jump_if_false (false) b290; - label b290: jump_if_false (false) b291; - label b291: jump_if_false (false) b292; - label b292: jump_if_false (false) b293; - label b293: jump_if_false (false) b294; - label b294: jump_if_false (false) b295; - label b295: jump_if_false (false) b296; - label b296: jump_if_false (false) b297; - label b297: jump_if_false (false) b298; - label b298: jump_if_false (false) b299; - label b299: jump_if_false (false) b300; - label b300: jump_if_false (false) b301; - label b301: jump_if_false (false) b302; - label b302: jump_if_false (false) b303; - label b303: jump_if_false (false) b304; - label b304: jump_if_false (false) b305; - label b305: jump_if_false (false) b306; - label b306: jump_if_false (false) b307; - label b307: jump_if_false (false) b308; - label b308: jump_if_false (false) b309; - label b309: jump_if_false (false) b310; - label b310: jump_if_false (false) b311; - label b311: jump_if_false (false) b312; - label b312: jump_if_false (false) b313; - label b313: jump_if_false (false) b314; - label b314: jump_if_false (false) b315; - label b315: jump_if_false (false) b316; - label b316: jump_if_false (false) b317; - label b317: jump_if_false (false) b318; - label b318: jump_if_false (false) b319; - label b319: jump_if_false (false) b320; - label b320: jump_if_false (false) b321; - label b321: jump_if_false (false) b322; - label b322: jump_if_false (false) b323; - label b323: jump_if_false (false) b324; - label b324: jump_if_false (false) b325; - label b325: jump_if_false (false) b326; - label b326: jump_if_false (false) b327; - label b327: jump_if_false (false) b328; - label b328: jump_if_false (false) b329; - label b329: jump_if_false (false) b330; - label b330: jump_if_false (false) b331; - label b331: jump_if_false (false) b332; - label b332: jump_if_false (false) b333; - label b333: jump_if_false (false) b334; - label b334: jump_if_false (false) b335; - label b335: jump_if_false (false) b336; - label b336: jump_if_false (false) b337; - label b337: jump_if_false (false) b338; - label b338: jump_if_false (false) b339; - label b339: jump_if_false (false) b340; - label b340: jump_if_false (false) b341; - label b341: jump_if_false (false) b342; - label b342: jump_if_false (false) b343; - label b343: jump_if_false (false) b344; - label b344: jump_if_false (false) b345; - label b345: jump_if_false (false) b346; - label b346: jump_if_false (false) b347; - label b347: jump_if_false (false) b348; - label b348: jump_if_false (false) b349; - label b349: jump_if_false (false) b350; - label b350: jump_if_false (false) b351; - label b351: jump_if_false (false) b352; - label b352: jump_if_false (false) b353; - label b353: jump_if_false (false) b354; - label b354: jump_if_false (false) b355; - label b355: jump_if_false (false) b356; - label b356: jump_if_false (false) b357; - label b357: jump_if_false (false) b358; - label b358: jump_if_false (false) b359; - label b359: jump_if_false (false) b360; - label b360: jump_if_false (false) b361; - label b361: jump_if_false (false) b362; - label b362: jump_if_false (false) b363; - label b363: jump_if_false (false) b364; - label b364: jump_if_false (false) b365; - label b365: jump_if_false (false) b366; - label b366: jump_if_false (false) b367; - label b367: jump_if_false (false) b368; - label b368: jump_if_false (false) b369; - label b369: jump_if_false (false) b370; - label b370: jump_if_false (false) b371; - label b371: jump_if_false (false) b372; - label b372: jump_if_false (false) b373; - label b373: jump_if_false (false) b374; - label b374: jump_if_false (false) b375; - label b375: jump_if_false (false) b376; - label b376: jump_if_false (false) b377; - label b377: jump_if_false (false) b378; - label b378: jump_if_false (false) b379; - label b379: jump_if_false (false) b380; - label b380: jump_if_false (false) b381; - label b381: jump_if_false (false) b382; - label b382: jump_if_false (false) b383; - label b383: jump_if_false (false) b384; - label b384: jump_if_false (false) b385; - label b385: jump_if_false (false) b386; - label b386: jump_if_false (false) b387; - label b387: jump_if_false (false) b388; - label b388: jump_if_false (false) b389; - label b389: jump_if_false (false) b390; - label b390: jump_if_false (false) b391; - label b391: jump_if_false (false) b392; - label b392: jump_if_false (false) b393; - label b393: jump_if_false (false) b394; - label b394: jump_if_false (false) b395; - label b395: jump_if_false (false) b396; - label b396: jump_if_false (false) b397; - label b397: jump_if_false (false) b398; - label b398: jump_if_false (false) b399; - label b399: jump_if_false (false) b400; - label b400: jump_if_false (false) b401; - label b401: jump_if_false (false) b402; - label b402: jump_if_false (false) b403; - label b403: jump_if_false (false) b404; - label b404: jump_if_false (false) b405; - label b405: jump_if_false (false) b406; - label b406: jump_if_false (false) b407; - label b407: jump_if_false (false) b408; - label b408: jump_if_false (false) b409; - label b409: jump_if_false (false) b410; - label b410: jump_if_false (false) b411; - label b411: jump_if_false (false) b412; - label b412: jump_if_false (false) b413; - label b413: jump_if_false (false) b414; - label b414: jump_if_false (false) b415; - label b415: jump_if_false (false) b416; - label b416: jump_if_false (false) b417; - label b417: jump_if_false (false) b418; - label b418: jump_if_false (false) b419; - label b419: jump_if_false (false) b420; - label b420: jump_if_false (false) b421; - label b421: jump_if_false (false) b422; - label b422: jump_if_false (false) b423; - label b423: jump_if_false (false) b424; - label b424: jump_if_false (false) b425; - label b425: jump_if_false (false) b426; - label b426: jump_if_false (false) b427; - label b427: jump_if_false (false) b428; - label b428: jump_if_false (false) b429; - label b429: jump_if_false (false) b430; - label b430: jump_if_false (false) b431; - label b431: jump_if_false (false) b432; - label b432: jump_if_false (false) b433; - label b433: jump_if_false (false) b434; - label b434: jump_if_false (false) b435; - label b435: jump_if_false (false) b436; - label b436: jump_if_false (false) b437; - label b437: jump_if_false (false) b438; - label b438: jump_if_false (false) b439; - label b439: jump_if_false (false) b440; - label b440: jump_if_false (false) b441; - label b441: jump_if_false (false) b442; - label b442: jump_if_false (false) b443; - label b443: jump_if_false (false) b444; - label b444: jump_if_false (false) b445; - label b445: jump_if_false (false) b446; - label b446: jump_if_false (false) b447; - label b447: jump_if_false (false) b448; - label b448: jump_if_false (false) b449; - label b449: jump_if_false (false) b450; - label b450: jump_if_false (false) b451; - label b451: jump_if_false (false) b452; - label b452: jump_if_false (false) b453; - label b453: jump_if_false (false) b454; - label b454: jump_if_false (false) b455; - label b455: jump_if_false (false) b456; - label b456: jump_if_false (false) b457; - label b457: jump_if_false (false) b458; - label b458: jump_if_false (false) b459; - label b459: jump_if_false (false) b460; - label b460: jump_if_false (false) b461; - label b461: jump_if_false (false) b462; - label b462: jump_if_false (false) b463; - label b463: jump_if_false (false) b464; - label b464: jump_if_false (false) b465; - label b465: jump_if_false (false) b466; - label b466: jump_if_false (false) b467; - label b467: jump_if_false (false) b468; - label b468: jump_if_false (false) b469; - label b469: jump_if_false (false) b470; - label b470: jump_if_false (false) b471; - label b471: jump_if_false (false) b472; - label b472: jump_if_false (false) b473; - label b473: jump_if_false (false) b474; - label b474: jump_if_false (false) b475; - label b475: jump_if_false (false) b476; - label b476: jump_if_false (false) b477; - label b477: jump_if_false (false) b478; - label b478: jump_if_false (false) b479; - label b479: jump_if_false (false) b480; - label b480: jump_if_false (false) b481; - label b481: jump_if_false (false) b482; - label b482: jump_if_false (false) b483; - label b483: jump_if_false (false) b484; - label b484: jump_if_false (false) b485; - label b485: jump_if_false (false) b486; - label b486: jump_if_false (false) b487; - label b487: jump_if_false (false) b488; - label b488: jump_if_false (false) b489; - label b489: jump_if_false (false) b490; - label b490: jump_if_false (false) b491; - label b491: jump_if_false (false) b492; - label b492: jump_if_false (false) b493; - label b493: jump_if_false (false) b494; - label b494: jump_if_false (false) b495; - label b495: jump_if_false (false) b496; - label b496: jump_if_false (false) b497; - label b497: jump_if_false (false) b498; - label b498: jump_if_false (false) b499; - label b499: jump_if_false (false) b500; - label b500: - return; - } - - public entry calibrate_br_false_x1000() { - label b0: jump_if_false (false) b1; - label b1: jump_if_false (false) b2; - label b2: jump_if_false (false) b3; - label b3: jump_if_false (false) b4; - label b4: jump_if_false (false) b5; - label b5: jump_if_false (false) b6; - label b6: jump_if_false (false) b7; - label b7: jump_if_false (false) b8; - label b8: jump_if_false (false) b9; - label b9: jump_if_false (false) b10; - label b10: jump_if_false (false) b11; - label b11: jump_if_false (false) b12; - label b12: jump_if_false (false) b13; - label b13: jump_if_false (false) b14; - label b14: jump_if_false (false) b15; - label b15: jump_if_false (false) b16; - label b16: jump_if_false (false) b17; - label b17: jump_if_false (false) b18; - label b18: jump_if_false (false) b19; - label b19: jump_if_false (false) b20; - label b20: jump_if_false (false) b21; - label b21: jump_if_false (false) b22; - label b22: jump_if_false (false) b23; - label b23: jump_if_false (false) b24; - label b24: jump_if_false (false) b25; - label b25: jump_if_false (false) b26; - label b26: jump_if_false (false) b27; - label b27: jump_if_false (false) b28; - label b28: jump_if_false (false) b29; - label b29: jump_if_false (false) b30; - label b30: jump_if_false (false) b31; - label b31: jump_if_false (false) b32; - label b32: jump_if_false (false) b33; - label b33: jump_if_false (false) b34; - label b34: jump_if_false (false) b35; - label b35: jump_if_false (false) b36; - label b36: jump_if_false (false) b37; - label b37: jump_if_false (false) b38; - label b38: jump_if_false (false) b39; - label b39: jump_if_false (false) b40; - label b40: jump_if_false (false) b41; - label b41: jump_if_false (false) b42; - label b42: jump_if_false (false) b43; - label b43: jump_if_false (false) b44; - label b44: jump_if_false (false) b45; - label b45: jump_if_false (false) b46; - label b46: jump_if_false (false) b47; - label b47: jump_if_false (false) b48; - label b48: jump_if_false (false) b49; - label b49: jump_if_false (false) b50; - label b50: jump_if_false (false) b51; - label b51: jump_if_false (false) b52; - label b52: jump_if_false (false) b53; - label b53: jump_if_false (false) b54; - label b54: jump_if_false (false) b55; - label b55: jump_if_false (false) b56; - label b56: jump_if_false (false) b57; - label b57: jump_if_false (false) b58; - label b58: jump_if_false (false) b59; - label b59: jump_if_false (false) b60; - label b60: jump_if_false (false) b61; - label b61: jump_if_false (false) b62; - label b62: jump_if_false (false) b63; - label b63: jump_if_false (false) b64; - label b64: jump_if_false (false) b65; - label b65: jump_if_false (false) b66; - label b66: jump_if_false (false) b67; - label b67: jump_if_false (false) b68; - label b68: jump_if_false (false) b69; - label b69: jump_if_false (false) b70; - label b70: jump_if_false (false) b71; - label b71: jump_if_false (false) b72; - label b72: jump_if_false (false) b73; - label b73: jump_if_false (false) b74; - label b74: jump_if_false (false) b75; - label b75: jump_if_false (false) b76; - label b76: jump_if_false (false) b77; - label b77: jump_if_false (false) b78; - label b78: jump_if_false (false) b79; - label b79: jump_if_false (false) b80; - label b80: jump_if_false (false) b81; - label b81: jump_if_false (false) b82; - label b82: jump_if_false (false) b83; - label b83: jump_if_false (false) b84; - label b84: jump_if_false (false) b85; - label b85: jump_if_false (false) b86; - label b86: jump_if_false (false) b87; - label b87: jump_if_false (false) b88; - label b88: jump_if_false (false) b89; - label b89: jump_if_false (false) b90; - label b90: jump_if_false (false) b91; - label b91: jump_if_false (false) b92; - label b92: jump_if_false (false) b93; - label b93: jump_if_false (false) b94; - label b94: jump_if_false (false) b95; - label b95: jump_if_false (false) b96; - label b96: jump_if_false (false) b97; - label b97: jump_if_false (false) b98; - label b98: jump_if_false (false) b99; - label b99: jump_if_false (false) b100; - label b100: jump_if_false (false) b101; - label b101: jump_if_false (false) b102; - label b102: jump_if_false (false) b103; - label b103: jump_if_false (false) b104; - label b104: jump_if_false (false) b105; - label b105: jump_if_false (false) b106; - label b106: jump_if_false (false) b107; - label b107: jump_if_false (false) b108; - label b108: jump_if_false (false) b109; - label b109: jump_if_false (false) b110; - label b110: jump_if_false (false) b111; - label b111: jump_if_false (false) b112; - label b112: jump_if_false (false) b113; - label b113: jump_if_false (false) b114; - label b114: jump_if_false (false) b115; - label b115: jump_if_false (false) b116; - label b116: jump_if_false (false) b117; - label b117: jump_if_false (false) b118; - label b118: jump_if_false (false) b119; - label b119: jump_if_false (false) b120; - label b120: jump_if_false (false) b121; - label b121: jump_if_false (false) b122; - label b122: jump_if_false (false) b123; - label b123: jump_if_false (false) b124; - label b124: jump_if_false (false) b125; - label b125: jump_if_false (false) b126; - label b126: jump_if_false (false) b127; - label b127: jump_if_false (false) b128; - label b128: jump_if_false (false) b129; - label b129: jump_if_false (false) b130; - label b130: jump_if_false (false) b131; - label b131: jump_if_false (false) b132; - label b132: jump_if_false (false) b133; - label b133: jump_if_false (false) b134; - label b134: jump_if_false (false) b135; - label b135: jump_if_false (false) b136; - label b136: jump_if_false (false) b137; - label b137: jump_if_false (false) b138; - label b138: jump_if_false (false) b139; - label b139: jump_if_false (false) b140; - label b140: jump_if_false (false) b141; - label b141: jump_if_false (false) b142; - label b142: jump_if_false (false) b143; - label b143: jump_if_false (false) b144; - label b144: jump_if_false (false) b145; - label b145: jump_if_false (false) b146; - label b146: jump_if_false (false) b147; - label b147: jump_if_false (false) b148; - label b148: jump_if_false (false) b149; - label b149: jump_if_false (false) b150; - label b150: jump_if_false (false) b151; - label b151: jump_if_false (false) b152; - label b152: jump_if_false (false) b153; - label b153: jump_if_false (false) b154; - label b154: jump_if_false (false) b155; - label b155: jump_if_false (false) b156; - label b156: jump_if_false (false) b157; - label b157: jump_if_false (false) b158; - label b158: jump_if_false (false) b159; - label b159: jump_if_false (false) b160; - label b160: jump_if_false (false) b161; - label b161: jump_if_false (false) b162; - label b162: jump_if_false (false) b163; - label b163: jump_if_false (false) b164; - label b164: jump_if_false (false) b165; - label b165: jump_if_false (false) b166; - label b166: jump_if_false (false) b167; - label b167: jump_if_false (false) b168; - label b168: jump_if_false (false) b169; - label b169: jump_if_false (false) b170; - label b170: jump_if_false (false) b171; - label b171: jump_if_false (false) b172; - label b172: jump_if_false (false) b173; - label b173: jump_if_false (false) b174; - label b174: jump_if_false (false) b175; - label b175: jump_if_false (false) b176; - label b176: jump_if_false (false) b177; - label b177: jump_if_false (false) b178; - label b178: jump_if_false (false) b179; - label b179: jump_if_false (false) b180; - label b180: jump_if_false (false) b181; - label b181: jump_if_false (false) b182; - label b182: jump_if_false (false) b183; - label b183: jump_if_false (false) b184; - label b184: jump_if_false (false) b185; - label b185: jump_if_false (false) b186; - label b186: jump_if_false (false) b187; - label b187: jump_if_false (false) b188; - label b188: jump_if_false (false) b189; - label b189: jump_if_false (false) b190; - label b190: jump_if_false (false) b191; - label b191: jump_if_false (false) b192; - label b192: jump_if_false (false) b193; - label b193: jump_if_false (false) b194; - label b194: jump_if_false (false) b195; - label b195: jump_if_false (false) b196; - label b196: jump_if_false (false) b197; - label b197: jump_if_false (false) b198; - label b198: jump_if_false (false) b199; - label b199: jump_if_false (false) b200; - label b200: jump_if_false (false) b201; - label b201: jump_if_false (false) b202; - label b202: jump_if_false (false) b203; - label b203: jump_if_false (false) b204; - label b204: jump_if_false (false) b205; - label b205: jump_if_false (false) b206; - label b206: jump_if_false (false) b207; - label b207: jump_if_false (false) b208; - label b208: jump_if_false (false) b209; - label b209: jump_if_false (false) b210; - label b210: jump_if_false (false) b211; - label b211: jump_if_false (false) b212; - label b212: jump_if_false (false) b213; - label b213: jump_if_false (false) b214; - label b214: jump_if_false (false) b215; - label b215: jump_if_false (false) b216; - label b216: jump_if_false (false) b217; - label b217: jump_if_false (false) b218; - label b218: jump_if_false (false) b219; - label b219: jump_if_false (false) b220; - label b220: jump_if_false (false) b221; - label b221: jump_if_false (false) b222; - label b222: jump_if_false (false) b223; - label b223: jump_if_false (false) b224; - label b224: jump_if_false (false) b225; - label b225: jump_if_false (false) b226; - label b226: jump_if_false (false) b227; - label b227: jump_if_false (false) b228; - label b228: jump_if_false (false) b229; - label b229: jump_if_false (false) b230; - label b230: jump_if_false (false) b231; - label b231: jump_if_false (false) b232; - label b232: jump_if_false (false) b233; - label b233: jump_if_false (false) b234; - label b234: jump_if_false (false) b235; - label b235: jump_if_false (false) b236; - label b236: jump_if_false (false) b237; - label b237: jump_if_false (false) b238; - label b238: jump_if_false (false) b239; - label b239: jump_if_false (false) b240; - label b240: jump_if_false (false) b241; - label b241: jump_if_false (false) b242; - label b242: jump_if_false (false) b243; - label b243: jump_if_false (false) b244; - label b244: jump_if_false (false) b245; - label b245: jump_if_false (false) b246; - label b246: jump_if_false (false) b247; - label b247: jump_if_false (false) b248; - label b248: jump_if_false (false) b249; - label b249: jump_if_false (false) b250; - label b250: jump_if_false (false) b251; - label b251: jump_if_false (false) b252; - label b252: jump_if_false (false) b253; - label b253: jump_if_false (false) b254; - label b254: jump_if_false (false) b255; - label b255: jump_if_false (false) b256; - label b256: jump_if_false (false) b257; - label b257: jump_if_false (false) b258; - label b258: jump_if_false (false) b259; - label b259: jump_if_false (false) b260; - label b260: jump_if_false (false) b261; - label b261: jump_if_false (false) b262; - label b262: jump_if_false (false) b263; - label b263: jump_if_false (false) b264; - label b264: jump_if_false (false) b265; - label b265: jump_if_false (false) b266; - label b266: jump_if_false (false) b267; - label b267: jump_if_false (false) b268; - label b268: jump_if_false (false) b269; - label b269: jump_if_false (false) b270; - label b270: jump_if_false (false) b271; - label b271: jump_if_false (false) b272; - label b272: jump_if_false (false) b273; - label b273: jump_if_false (false) b274; - label b274: jump_if_false (false) b275; - label b275: jump_if_false (false) b276; - label b276: jump_if_false (false) b277; - label b277: jump_if_false (false) b278; - label b278: jump_if_false (false) b279; - label b279: jump_if_false (false) b280; - label b280: jump_if_false (false) b281; - label b281: jump_if_false (false) b282; - label b282: jump_if_false (false) b283; - label b283: jump_if_false (false) b284; - label b284: jump_if_false (false) b285; - label b285: jump_if_false (false) b286; - label b286: jump_if_false (false) b287; - label b287: jump_if_false (false) b288; - label b288: jump_if_false (false) b289; - label b289: jump_if_false (false) b290; - label b290: jump_if_false (false) b291; - label b291: jump_if_false (false) b292; - label b292: jump_if_false (false) b293; - label b293: jump_if_false (false) b294; - label b294: jump_if_false (false) b295; - label b295: jump_if_false (false) b296; - label b296: jump_if_false (false) b297; - label b297: jump_if_false (false) b298; - label b298: jump_if_false (false) b299; - label b299: jump_if_false (false) b300; - label b300: jump_if_false (false) b301; - label b301: jump_if_false (false) b302; - label b302: jump_if_false (false) b303; - label b303: jump_if_false (false) b304; - label b304: jump_if_false (false) b305; - label b305: jump_if_false (false) b306; - label b306: jump_if_false (false) b307; - label b307: jump_if_false (false) b308; - label b308: jump_if_false (false) b309; - label b309: jump_if_false (false) b310; - label b310: jump_if_false (false) b311; - label b311: jump_if_false (false) b312; - label b312: jump_if_false (false) b313; - label b313: jump_if_false (false) b314; - label b314: jump_if_false (false) b315; - label b315: jump_if_false (false) b316; - label b316: jump_if_false (false) b317; - label b317: jump_if_false (false) b318; - label b318: jump_if_false (false) b319; - label b319: jump_if_false (false) b320; - label b320: jump_if_false (false) b321; - label b321: jump_if_false (false) b322; - label b322: jump_if_false (false) b323; - label b323: jump_if_false (false) b324; - label b324: jump_if_false (false) b325; - label b325: jump_if_false (false) b326; - label b326: jump_if_false (false) b327; - label b327: jump_if_false (false) b328; - label b328: jump_if_false (false) b329; - label b329: jump_if_false (false) b330; - label b330: jump_if_false (false) b331; - label b331: jump_if_false (false) b332; - label b332: jump_if_false (false) b333; - label b333: jump_if_false (false) b334; - label b334: jump_if_false (false) b335; - label b335: jump_if_false (false) b336; - label b336: jump_if_false (false) b337; - label b337: jump_if_false (false) b338; - label b338: jump_if_false (false) b339; - label b339: jump_if_false (false) b340; - label b340: jump_if_false (false) b341; - label b341: jump_if_false (false) b342; - label b342: jump_if_false (false) b343; - label b343: jump_if_false (false) b344; - label b344: jump_if_false (false) b345; - label b345: jump_if_false (false) b346; - label b346: jump_if_false (false) b347; - label b347: jump_if_false (false) b348; - label b348: jump_if_false (false) b349; - label b349: jump_if_false (false) b350; - label b350: jump_if_false (false) b351; - label b351: jump_if_false (false) b352; - label b352: jump_if_false (false) b353; - label b353: jump_if_false (false) b354; - label b354: jump_if_false (false) b355; - label b355: jump_if_false (false) b356; - label b356: jump_if_false (false) b357; - label b357: jump_if_false (false) b358; - label b358: jump_if_false (false) b359; - label b359: jump_if_false (false) b360; - label b360: jump_if_false (false) b361; - label b361: jump_if_false (false) b362; - label b362: jump_if_false (false) b363; - label b363: jump_if_false (false) b364; - label b364: jump_if_false (false) b365; - label b365: jump_if_false (false) b366; - label b366: jump_if_false (false) b367; - label b367: jump_if_false (false) b368; - label b368: jump_if_false (false) b369; - label b369: jump_if_false (false) b370; - label b370: jump_if_false (false) b371; - label b371: jump_if_false (false) b372; - label b372: jump_if_false (false) b373; - label b373: jump_if_false (false) b374; - label b374: jump_if_false (false) b375; - label b375: jump_if_false (false) b376; - label b376: jump_if_false (false) b377; - label b377: jump_if_false (false) b378; - label b378: jump_if_false (false) b379; - label b379: jump_if_false (false) b380; - label b380: jump_if_false (false) b381; - label b381: jump_if_false (false) b382; - label b382: jump_if_false (false) b383; - label b383: jump_if_false (false) b384; - label b384: jump_if_false (false) b385; - label b385: jump_if_false (false) b386; - label b386: jump_if_false (false) b387; - label b387: jump_if_false (false) b388; - label b388: jump_if_false (false) b389; - label b389: jump_if_false (false) b390; - label b390: jump_if_false (false) b391; - label b391: jump_if_false (false) b392; - label b392: jump_if_false (false) b393; - label b393: jump_if_false (false) b394; - label b394: jump_if_false (false) b395; - label b395: jump_if_false (false) b396; - label b396: jump_if_false (false) b397; - label b397: jump_if_false (false) b398; - label b398: jump_if_false (false) b399; - label b399: jump_if_false (false) b400; - label b400: jump_if_false (false) b401; - label b401: jump_if_false (false) b402; - label b402: jump_if_false (false) b403; - label b403: jump_if_false (false) b404; - label b404: jump_if_false (false) b405; - label b405: jump_if_false (false) b406; - label b406: jump_if_false (false) b407; - label b407: jump_if_false (false) b408; - label b408: jump_if_false (false) b409; - label b409: jump_if_false (false) b410; - label b410: jump_if_false (false) b411; - label b411: jump_if_false (false) b412; - label b412: jump_if_false (false) b413; - label b413: jump_if_false (false) b414; - label b414: jump_if_false (false) b415; - label b415: jump_if_false (false) b416; - label b416: jump_if_false (false) b417; - label b417: jump_if_false (false) b418; - label b418: jump_if_false (false) b419; - label b419: jump_if_false (false) b420; - label b420: jump_if_false (false) b421; - label b421: jump_if_false (false) b422; - label b422: jump_if_false (false) b423; - label b423: jump_if_false (false) b424; - label b424: jump_if_false (false) b425; - label b425: jump_if_false (false) b426; - label b426: jump_if_false (false) b427; - label b427: jump_if_false (false) b428; - label b428: jump_if_false (false) b429; - label b429: jump_if_false (false) b430; - label b430: jump_if_false (false) b431; - label b431: jump_if_false (false) b432; - label b432: jump_if_false (false) b433; - label b433: jump_if_false (false) b434; - label b434: jump_if_false (false) b435; - label b435: jump_if_false (false) b436; - label b436: jump_if_false (false) b437; - label b437: jump_if_false (false) b438; - label b438: jump_if_false (false) b439; - label b439: jump_if_false (false) b440; - label b440: jump_if_false (false) b441; - label b441: jump_if_false (false) b442; - label b442: jump_if_false (false) b443; - label b443: jump_if_false (false) b444; - label b444: jump_if_false (false) b445; - label b445: jump_if_false (false) b446; - label b446: jump_if_false (false) b447; - label b447: jump_if_false (false) b448; - label b448: jump_if_false (false) b449; - label b449: jump_if_false (false) b450; - label b450: jump_if_false (false) b451; - label b451: jump_if_false (false) b452; - label b452: jump_if_false (false) b453; - label b453: jump_if_false (false) b454; - label b454: jump_if_false (false) b455; - label b455: jump_if_false (false) b456; - label b456: jump_if_false (false) b457; - label b457: jump_if_false (false) b458; - label b458: jump_if_false (false) b459; - label b459: jump_if_false (false) b460; - label b460: jump_if_false (false) b461; - label b461: jump_if_false (false) b462; - label b462: jump_if_false (false) b463; - label b463: jump_if_false (false) b464; - label b464: jump_if_false (false) b465; - label b465: jump_if_false (false) b466; - label b466: jump_if_false (false) b467; - label b467: jump_if_false (false) b468; - label b468: jump_if_false (false) b469; - label b469: jump_if_false (false) b470; - label b470: jump_if_false (false) b471; - label b471: jump_if_false (false) b472; - label b472: jump_if_false (false) b473; - label b473: jump_if_false (false) b474; - label b474: jump_if_false (false) b475; - label b475: jump_if_false (false) b476; - label b476: jump_if_false (false) b477; - label b477: jump_if_false (false) b478; - label b478: jump_if_false (false) b479; - label b479: jump_if_false (false) b480; - label b480: jump_if_false (false) b481; - label b481: jump_if_false (false) b482; - label b482: jump_if_false (false) b483; - label b483: jump_if_false (false) b484; - label b484: jump_if_false (false) b485; - label b485: jump_if_false (false) b486; - label b486: jump_if_false (false) b487; - label b487: jump_if_false (false) b488; - label b488: jump_if_false (false) b489; - label b489: jump_if_false (false) b490; - label b490: jump_if_false (false) b491; - label b491: jump_if_false (false) b492; - label b492: jump_if_false (false) b493; - label b493: jump_if_false (false) b494; - label b494: jump_if_false (false) b495; - label b495: jump_if_false (false) b496; - label b496: jump_if_false (false) b497; - label b497: jump_if_false (false) b498; - label b498: jump_if_false (false) b499; - label b499: jump_if_false (false) b500; - label b500: jump_if_false (false) b501; - label b501: jump_if_false (false) b502; - label b502: jump_if_false (false) b503; - label b503: jump_if_false (false) b504; - label b504: jump_if_false (false) b505; - label b505: jump_if_false (false) b506; - label b506: jump_if_false (false) b507; - label b507: jump_if_false (false) b508; - label b508: jump_if_false (false) b509; - label b509: jump_if_false (false) b510; - label b510: jump_if_false (false) b511; - label b511: jump_if_false (false) b512; - label b512: jump_if_false (false) b513; - label b513: jump_if_false (false) b514; - label b514: jump_if_false (false) b515; - label b515: jump_if_false (false) b516; - label b516: jump_if_false (false) b517; - label b517: jump_if_false (false) b518; - label b518: jump_if_false (false) b519; - label b519: jump_if_false (false) b520; - label b520: jump_if_false (false) b521; - label b521: jump_if_false (false) b522; - label b522: jump_if_false (false) b523; - label b523: jump_if_false (false) b524; - label b524: jump_if_false (false) b525; - label b525: jump_if_false (false) b526; - label b526: jump_if_false (false) b527; - label b527: jump_if_false (false) b528; - label b528: jump_if_false (false) b529; - label b529: jump_if_false (false) b530; - label b530: jump_if_false (false) b531; - label b531: jump_if_false (false) b532; - label b532: jump_if_false (false) b533; - label b533: jump_if_false (false) b534; - label b534: jump_if_false (false) b535; - label b535: jump_if_false (false) b536; - label b536: jump_if_false (false) b537; - label b537: jump_if_false (false) b538; - label b538: jump_if_false (false) b539; - label b539: jump_if_false (false) b540; - label b540: jump_if_false (false) b541; - label b541: jump_if_false (false) b542; - label b542: jump_if_false (false) b543; - label b543: jump_if_false (false) b544; - label b544: jump_if_false (false) b545; - label b545: jump_if_false (false) b546; - label b546: jump_if_false (false) b547; - label b547: jump_if_false (false) b548; - label b548: jump_if_false (false) b549; - label b549: jump_if_false (false) b550; - label b550: jump_if_false (false) b551; - label b551: jump_if_false (false) b552; - label b552: jump_if_false (false) b553; - label b553: jump_if_false (false) b554; - label b554: jump_if_false (false) b555; - label b555: jump_if_false (false) b556; - label b556: jump_if_false (false) b557; - label b557: jump_if_false (false) b558; - label b558: jump_if_false (false) b559; - label b559: jump_if_false (false) b560; - label b560: jump_if_false (false) b561; - label b561: jump_if_false (false) b562; - label b562: jump_if_false (false) b563; - label b563: jump_if_false (false) b564; - label b564: jump_if_false (false) b565; - label b565: jump_if_false (false) b566; - label b566: jump_if_false (false) b567; - label b567: jump_if_false (false) b568; - label b568: jump_if_false (false) b569; - label b569: jump_if_false (false) b570; - label b570: jump_if_false (false) b571; - label b571: jump_if_false (false) b572; - label b572: jump_if_false (false) b573; - label b573: jump_if_false (false) b574; - label b574: jump_if_false (false) b575; - label b575: jump_if_false (false) b576; - label b576: jump_if_false (false) b577; - label b577: jump_if_false (false) b578; - label b578: jump_if_false (false) b579; - label b579: jump_if_false (false) b580; - label b580: jump_if_false (false) b581; - label b581: jump_if_false (false) b582; - label b582: jump_if_false (false) b583; - label b583: jump_if_false (false) b584; - label b584: jump_if_false (false) b585; - label b585: jump_if_false (false) b586; - label b586: jump_if_false (false) b587; - label b587: jump_if_false (false) b588; - label b588: jump_if_false (false) b589; - label b589: jump_if_false (false) b590; - label b590: jump_if_false (false) b591; - label b591: jump_if_false (false) b592; - label b592: jump_if_false (false) b593; - label b593: jump_if_false (false) b594; - label b594: jump_if_false (false) b595; - label b595: jump_if_false (false) b596; - label b596: jump_if_false (false) b597; - label b597: jump_if_false (false) b598; - label b598: jump_if_false (false) b599; - label b599: jump_if_false (false) b600; - label b600: jump_if_false (false) b601; - label b601: jump_if_false (false) b602; - label b602: jump_if_false (false) b603; - label b603: jump_if_false (false) b604; - label b604: jump_if_false (false) b605; - label b605: jump_if_false (false) b606; - label b606: jump_if_false (false) b607; - label b607: jump_if_false (false) b608; - label b608: jump_if_false (false) b609; - label b609: jump_if_false (false) b610; - label b610: jump_if_false (false) b611; - label b611: jump_if_false (false) b612; - label b612: jump_if_false (false) b613; - label b613: jump_if_false (false) b614; - label b614: jump_if_false (false) b615; - label b615: jump_if_false (false) b616; - label b616: jump_if_false (false) b617; - label b617: jump_if_false (false) b618; - label b618: jump_if_false (false) b619; - label b619: jump_if_false (false) b620; - label b620: jump_if_false (false) b621; - label b621: jump_if_false (false) b622; - label b622: jump_if_false (false) b623; - label b623: jump_if_false (false) b624; - label b624: jump_if_false (false) b625; - label b625: jump_if_false (false) b626; - label b626: jump_if_false (false) b627; - label b627: jump_if_false (false) b628; - label b628: jump_if_false (false) b629; - label b629: jump_if_false (false) b630; - label b630: jump_if_false (false) b631; - label b631: jump_if_false (false) b632; - label b632: jump_if_false (false) b633; - label b633: jump_if_false (false) b634; - label b634: jump_if_false (false) b635; - label b635: jump_if_false (false) b636; - label b636: jump_if_false (false) b637; - label b637: jump_if_false (false) b638; - label b638: jump_if_false (false) b639; - label b639: jump_if_false (false) b640; - label b640: jump_if_false (false) b641; - label b641: jump_if_false (false) b642; - label b642: jump_if_false (false) b643; - label b643: jump_if_false (false) b644; - label b644: jump_if_false (false) b645; - label b645: jump_if_false (false) b646; - label b646: jump_if_false (false) b647; - label b647: jump_if_false (false) b648; - label b648: jump_if_false (false) b649; - label b649: jump_if_false (false) b650; - label b650: jump_if_false (false) b651; - label b651: jump_if_false (false) b652; - label b652: jump_if_false (false) b653; - label b653: jump_if_false (false) b654; - label b654: jump_if_false (false) b655; - label b655: jump_if_false (false) b656; - label b656: jump_if_false (false) b657; - label b657: jump_if_false (false) b658; - label b658: jump_if_false (false) b659; - label b659: jump_if_false (false) b660; - label b660: jump_if_false (false) b661; - label b661: jump_if_false (false) b662; - label b662: jump_if_false (false) b663; - label b663: jump_if_false (false) b664; - label b664: jump_if_false (false) b665; - label b665: jump_if_false (false) b666; - label b666: jump_if_false (false) b667; - label b667: jump_if_false (false) b668; - label b668: jump_if_false (false) b669; - label b669: jump_if_false (false) b670; - label b670: jump_if_false (false) b671; - label b671: jump_if_false (false) b672; - label b672: jump_if_false (false) b673; - label b673: jump_if_false (false) b674; - label b674: jump_if_false (false) b675; - label b675: jump_if_false (false) b676; - label b676: jump_if_false (false) b677; - label b677: jump_if_false (false) b678; - label b678: jump_if_false (false) b679; - label b679: jump_if_false (false) b680; - label b680: jump_if_false (false) b681; - label b681: jump_if_false (false) b682; - label b682: jump_if_false (false) b683; - label b683: jump_if_false (false) b684; - label b684: jump_if_false (false) b685; - label b685: jump_if_false (false) b686; - label b686: jump_if_false (false) b687; - label b687: jump_if_false (false) b688; - label b688: jump_if_false (false) b689; - label b689: jump_if_false (false) b690; - label b690: jump_if_false (false) b691; - label b691: jump_if_false (false) b692; - label b692: jump_if_false (false) b693; - label b693: jump_if_false (false) b694; - label b694: jump_if_false (false) b695; - label b695: jump_if_false (false) b696; - label b696: jump_if_false (false) b697; - label b697: jump_if_false (false) b698; - label b698: jump_if_false (false) b699; - label b699: jump_if_false (false) b700; - label b700: jump_if_false (false) b701; - label b701: jump_if_false (false) b702; - label b702: jump_if_false (false) b703; - label b703: jump_if_false (false) b704; - label b704: jump_if_false (false) b705; - label b705: jump_if_false (false) b706; - label b706: jump_if_false (false) b707; - label b707: jump_if_false (false) b708; - label b708: jump_if_false (false) b709; - label b709: jump_if_false (false) b710; - label b710: jump_if_false (false) b711; - label b711: jump_if_false (false) b712; - label b712: jump_if_false (false) b713; - label b713: jump_if_false (false) b714; - label b714: jump_if_false (false) b715; - label b715: jump_if_false (false) b716; - label b716: jump_if_false (false) b717; - label b717: jump_if_false (false) b718; - label b718: jump_if_false (false) b719; - label b719: jump_if_false (false) b720; - label b720: jump_if_false (false) b721; - label b721: jump_if_false (false) b722; - label b722: jump_if_false (false) b723; - label b723: jump_if_false (false) b724; - label b724: jump_if_false (false) b725; - label b725: jump_if_false (false) b726; - label b726: jump_if_false (false) b727; - label b727: jump_if_false (false) b728; - label b728: jump_if_false (false) b729; - label b729: jump_if_false (false) b730; - label b730: jump_if_false (false) b731; - label b731: jump_if_false (false) b732; - label b732: jump_if_false (false) b733; - label b733: jump_if_false (false) b734; - label b734: jump_if_false (false) b735; - label b735: jump_if_false (false) b736; - label b736: jump_if_false (false) b737; - label b737: jump_if_false (false) b738; - label b738: jump_if_false (false) b739; - label b739: jump_if_false (false) b740; - label b740: jump_if_false (false) b741; - label b741: jump_if_false (false) b742; - label b742: jump_if_false (false) b743; - label b743: jump_if_false (false) b744; - label b744: jump_if_false (false) b745; - label b745: jump_if_false (false) b746; - label b746: jump_if_false (false) b747; - label b747: jump_if_false (false) b748; - label b748: jump_if_false (false) b749; - label b749: jump_if_false (false) b750; - label b750: jump_if_false (false) b751; - label b751: jump_if_false (false) b752; - label b752: jump_if_false (false) b753; - label b753: jump_if_false (false) b754; - label b754: jump_if_false (false) b755; - label b755: jump_if_false (false) b756; - label b756: jump_if_false (false) b757; - label b757: jump_if_false (false) b758; - label b758: jump_if_false (false) b759; - label b759: jump_if_false (false) b760; - label b760: jump_if_false (false) b761; - label b761: jump_if_false (false) b762; - label b762: jump_if_false (false) b763; - label b763: jump_if_false (false) b764; - label b764: jump_if_false (false) b765; - label b765: jump_if_false (false) b766; - label b766: jump_if_false (false) b767; - label b767: jump_if_false (false) b768; - label b768: jump_if_false (false) b769; - label b769: jump_if_false (false) b770; - label b770: jump_if_false (false) b771; - label b771: jump_if_false (false) b772; - label b772: jump_if_false (false) b773; - label b773: jump_if_false (false) b774; - label b774: jump_if_false (false) b775; - label b775: jump_if_false (false) b776; - label b776: jump_if_false (false) b777; - label b777: jump_if_false (false) b778; - label b778: jump_if_false (false) b779; - label b779: jump_if_false (false) b780; - label b780: jump_if_false (false) b781; - label b781: jump_if_false (false) b782; - label b782: jump_if_false (false) b783; - label b783: jump_if_false (false) b784; - label b784: jump_if_false (false) b785; - label b785: jump_if_false (false) b786; - label b786: jump_if_false (false) b787; - label b787: jump_if_false (false) b788; - label b788: jump_if_false (false) b789; - label b789: jump_if_false (false) b790; - label b790: jump_if_false (false) b791; - label b791: jump_if_false (false) b792; - label b792: jump_if_false (false) b793; - label b793: jump_if_false (false) b794; - label b794: jump_if_false (false) b795; - label b795: jump_if_false (false) b796; - label b796: jump_if_false (false) b797; - label b797: jump_if_false (false) b798; - label b798: jump_if_false (false) b799; - label b799: jump_if_false (false) b800; - label b800: jump_if_false (false) b801; - label b801: jump_if_false (false) b802; - label b802: jump_if_false (false) b803; - label b803: jump_if_false (false) b804; - label b804: jump_if_false (false) b805; - label b805: jump_if_false (false) b806; - label b806: jump_if_false (false) b807; - label b807: jump_if_false (false) b808; - label b808: jump_if_false (false) b809; - label b809: jump_if_false (false) b810; - label b810: jump_if_false (false) b811; - label b811: jump_if_false (false) b812; - label b812: jump_if_false (false) b813; - label b813: jump_if_false (false) b814; - label b814: jump_if_false (false) b815; - label b815: jump_if_false (false) b816; - label b816: jump_if_false (false) b817; - label b817: jump_if_false (false) b818; - label b818: jump_if_false (false) b819; - label b819: jump_if_false (false) b820; - label b820: jump_if_false (false) b821; - label b821: jump_if_false (false) b822; - label b822: jump_if_false (false) b823; - label b823: jump_if_false (false) b824; - label b824: jump_if_false (false) b825; - label b825: jump_if_false (false) b826; - label b826: jump_if_false (false) b827; - label b827: jump_if_false (false) b828; - label b828: jump_if_false (false) b829; - label b829: jump_if_false (false) b830; - label b830: jump_if_false (false) b831; - label b831: jump_if_false (false) b832; - label b832: jump_if_false (false) b833; - label b833: jump_if_false (false) b834; - label b834: jump_if_false (false) b835; - label b835: jump_if_false (false) b836; - label b836: jump_if_false (false) b837; - label b837: jump_if_false (false) b838; - label b838: jump_if_false (false) b839; - label b839: jump_if_false (false) b840; - label b840: jump_if_false (false) b841; - label b841: jump_if_false (false) b842; - label b842: jump_if_false (false) b843; - label b843: jump_if_false (false) b844; - label b844: jump_if_false (false) b845; - label b845: jump_if_false (false) b846; - label b846: jump_if_false (false) b847; - label b847: jump_if_false (false) b848; - label b848: jump_if_false (false) b849; - label b849: jump_if_false (false) b850; - label b850: jump_if_false (false) b851; - label b851: jump_if_false (false) b852; - label b852: jump_if_false (false) b853; - label b853: jump_if_false (false) b854; - label b854: jump_if_false (false) b855; - label b855: jump_if_false (false) b856; - label b856: jump_if_false (false) b857; - label b857: jump_if_false (false) b858; - label b858: jump_if_false (false) b859; - label b859: jump_if_false (false) b860; - label b860: jump_if_false (false) b861; - label b861: jump_if_false (false) b862; - label b862: jump_if_false (false) b863; - label b863: jump_if_false (false) b864; - label b864: jump_if_false (false) b865; - label b865: jump_if_false (false) b866; - label b866: jump_if_false (false) b867; - label b867: jump_if_false (false) b868; - label b868: jump_if_false (false) b869; - label b869: jump_if_false (false) b870; - label b870: jump_if_false (false) b871; - label b871: jump_if_false (false) b872; - label b872: jump_if_false (false) b873; - label b873: jump_if_false (false) b874; - label b874: jump_if_false (false) b875; - label b875: jump_if_false (false) b876; - label b876: jump_if_false (false) b877; - label b877: jump_if_false (false) b878; - label b878: jump_if_false (false) b879; - label b879: jump_if_false (false) b880; - label b880: jump_if_false (false) b881; - label b881: jump_if_false (false) b882; - label b882: jump_if_false (false) b883; - label b883: jump_if_false (false) b884; - label b884: jump_if_false (false) b885; - label b885: jump_if_false (false) b886; - label b886: jump_if_false (false) b887; - label b887: jump_if_false (false) b888; - label b888: jump_if_false (false) b889; - label b889: jump_if_false (false) b890; - label b890: jump_if_false (false) b891; - label b891: jump_if_false (false) b892; - label b892: jump_if_false (false) b893; - label b893: jump_if_false (false) b894; - label b894: jump_if_false (false) b895; - label b895: jump_if_false (false) b896; - label b896: jump_if_false (false) b897; - label b897: jump_if_false (false) b898; - label b898: jump_if_false (false) b899; - label b899: jump_if_false (false) b900; - label b900: jump_if_false (false) b901; - label b901: jump_if_false (false) b902; - label b902: jump_if_false (false) b903; - label b903: jump_if_false (false) b904; - label b904: jump_if_false (false) b905; - label b905: jump_if_false (false) b906; - label b906: jump_if_false (false) b907; - label b907: jump_if_false (false) b908; - label b908: jump_if_false (false) b909; - label b909: jump_if_false (false) b910; - label b910: jump_if_false (false) b911; - label b911: jump_if_false (false) b912; - label b912: jump_if_false (false) b913; - label b913: jump_if_false (false) b914; - label b914: jump_if_false (false) b915; - label b915: jump_if_false (false) b916; - label b916: jump_if_false (false) b917; - label b917: jump_if_false (false) b918; - label b918: jump_if_false (false) b919; - label b919: jump_if_false (false) b920; - label b920: jump_if_false (false) b921; - label b921: jump_if_false (false) b922; - label b922: jump_if_false (false) b923; - label b923: jump_if_false (false) b924; - label b924: jump_if_false (false) b925; - label b925: jump_if_false (false) b926; - label b926: jump_if_false (false) b927; - label b927: jump_if_false (false) b928; - label b928: jump_if_false (false) b929; - label b929: jump_if_false (false) b930; - label b930: jump_if_false (false) b931; - label b931: jump_if_false (false) b932; - label b932: jump_if_false (false) b933; - label b933: jump_if_false (false) b934; - label b934: jump_if_false (false) b935; - label b935: jump_if_false (false) b936; - label b936: jump_if_false (false) b937; - label b937: jump_if_false (false) b938; - label b938: jump_if_false (false) b939; - label b939: jump_if_false (false) b940; - label b940: jump_if_false (false) b941; - label b941: jump_if_false (false) b942; - label b942: jump_if_false (false) b943; - label b943: jump_if_false (false) b944; - label b944: jump_if_false (false) b945; - label b945: jump_if_false (false) b946; - label b946: jump_if_false (false) b947; - label b947: jump_if_false (false) b948; - label b948: jump_if_false (false) b949; - label b949: jump_if_false (false) b950; - label b950: jump_if_false (false) b951; - label b951: jump_if_false (false) b952; - label b952: jump_if_false (false) b953; - label b953: jump_if_false (false) b954; - label b954: jump_if_false (false) b955; - label b955: jump_if_false (false) b956; - label b956: jump_if_false (false) b957; - label b957: jump_if_false (false) b958; - label b958: jump_if_false (false) b959; - label b959: jump_if_false (false) b960; - label b960: jump_if_false (false) b961; - label b961: jump_if_false (false) b962; - label b962: jump_if_false (false) b963; - label b963: jump_if_false (false) b964; - label b964: jump_if_false (false) b965; - label b965: jump_if_false (false) b966; - label b966: jump_if_false (false) b967; - label b967: jump_if_false (false) b968; - label b968: jump_if_false (false) b969; - label b969: jump_if_false (false) b970; - label b970: jump_if_false (false) b971; - label b971: jump_if_false (false) b972; - label b972: jump_if_false (false) b973; - label b973: jump_if_false (false) b974; - label b974: jump_if_false (false) b975; - label b975: jump_if_false (false) b976; - label b976: jump_if_false (false) b977; - label b977: jump_if_false (false) b978; - label b978: jump_if_false (false) b979; - label b979: jump_if_false (false) b980; - label b980: jump_if_false (false) b981; - label b981: jump_if_false (false) b982; - label b982: jump_if_false (false) b983; - label b983: jump_if_false (false) b984; - label b984: jump_if_false (false) b985; - label b985: jump_if_false (false) b986; - label b986: jump_if_false (false) b987; - label b987: jump_if_false (false) b988; - label b988: jump_if_false (false) b989; - label b989: jump_if_false (false) b990; - label b990: jump_if_false (false) b991; - label b991: jump_if_false (false) b992; - label b992: jump_if_false (false) b993; - label b993: jump_if_false (false) b994; - label b994: jump_if_false (false) b995; - label b995: jump_if_false (false) b996; - label b996: jump_if_false (false) b997; - label b997: jump_if_false (false) b998; - label b998: jump_if_false (false) b999; - label b999: jump_if_false (false) b1000; - label b1000: - return; - } - - public entry calibrate_ld_false_x10() { - let a: bool; - label b0: - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - return; - } - - public entry calibrate_ld_false_x50() { - let a: bool; - label b0: - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - return; - } - - public entry calibrate_ld_false_x100() { - let a: bool; - label b0: - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - return; - } - - public entry calibrate_ld_false_x300() { - let a: bool; - label b0: - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - return; - } - - public entry calibrate_ld_false_x600() { - let a: bool; - label b0: - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - a, a, a, a, a, a, a, a, a, a = (false, false, false, false, false, false, false, false, false, false); - return; - } - - public entry calibrate_add_x10() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - return; - } - - public entry calibrate_add_x50() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - return; - } - - public entry calibrate_add_x100() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - return; - } - - public entry calibrate_add_x300() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - return; - } - - public entry calibrate_add_x600() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - a, a, a, a, a, a, a, a, a, a = (1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1, 1+1); - return; - } - - public entry calibrate_st_loc_x10() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - return; - } - - public entry calibrate_st_loc_x50() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - return; - } - - public entry calibrate_st_loc_x100() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - return; - } - - public entry calibrate_st_loc_x300() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - return; - } - - public entry calibrate_st_loc_x600() { - let a: u64; - label b0: - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - a, a, a, a, a, a, a, a, a, a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - return; - } - - public entry calibrate_ld_const_0_x10() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - return; - } - - public entry calibrate_ld_const_0_x100() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - _, _, _, _, _, _, _, _, _, _ = (h"", h"", h"", h"", h"", h"", h"", h"", h"", h""); - return; - } - - - public entry calibrate_ld_const_32_x10() { - label b0: - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - return; - } - - public entry calibrate_ld_const_32_x100() { - label b0: - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - return; - } - - public entry calibrate_ld_const_64_x10() { - label b0: - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - return; - } - - public entry calibrate_ld_const_64_x100() { - label b0: - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - return; - } - - public entry calibrate_ld_const_128_x100() { - label b0: - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - _ = h"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - return; - } - - public entry calibrate_branch_x10() { - label b0: jump b1; - label b1: jump b2; - label b2: jump b3; - label b3: jump b4; - label b4: jump b5; - label b5: jump b6; - label b6: jump b7; - label b7: jump b8; - label b8: jump b9; - label b9: jump b10; - label b10: - return; - } - - public entry calibrate_branch_x50() { - label b0: jump b1; - label b1: jump b2; - label b2: jump b3; - label b3: jump b4; - label b4: jump b5; - label b5: jump b6; - label b6: jump b7; - label b7: jump b8; - label b8: jump b9; - label b9: jump b10; - label b10: jump b11; - label b11: jump b12; - label b12: jump b13; - label b13: jump b14; - label b14: jump b15; - label b15: jump b16; - label b16: jump b17; - label b17: jump b18; - label b18: jump b19; - label b19: jump b20; - label b20: jump b21; - label b21: jump b22; - label b22: jump b23; - label b23: jump b24; - label b24: jump b25; - label b25: jump b26; - label b26: jump b27; - label b27: jump b28; - label b28: jump b29; - label b29: jump b30; - label b30: jump b31; - label b31: jump b32; - label b32: jump b33; - label b33: jump b34; - label b34: jump b35; - label b35: jump b36; - label b36: jump b37; - label b37: jump b38; - label b38: jump b39; - label b39: jump b40; - label b40: jump b41; - label b41: jump b42; - label b42: jump b43; - label b43: jump b44; - label b44: jump b45; - label b45: jump b46; - label b46: jump b47; - label b47: jump b48; - label b48: jump b49; - label b49: jump b50; - label b50: - return; - } - - public entry calibrate_branch_x500() { - label b0: jump b1; - label b1: jump b2; - label b2: jump b3; - label b3: jump b4; - label b4: jump b5; - label b5: jump b6; - label b6: jump b7; - label b7: jump b8; - label b8: jump b9; - label b9: jump b10; - label b10: jump b11; - label b11: jump b12; - label b12: jump b13; - label b13: jump b14; - label b14: jump b15; - label b15: jump b16; - label b16: jump b17; - label b17: jump b18; - label b18: jump b19; - label b19: jump b20; - label b20: jump b21; - label b21: jump b22; - label b22: jump b23; - label b23: jump b24; - label b24: jump b25; - label b25: jump b26; - label b26: jump b27; - label b27: jump b28; - label b28: jump b29; - label b29: jump b30; - label b30: jump b31; - label b31: jump b32; - label b32: jump b33; - label b33: jump b34; - label b34: jump b35; - label b35: jump b36; - label b36: jump b37; - label b37: jump b38; - label b38: jump b39; - label b39: jump b40; - label b40: jump b41; - label b41: jump b42; - label b42: jump b43; - label b43: jump b44; - label b44: jump b45; - label b45: jump b46; - label b46: jump b47; - label b47: jump b48; - label b48: jump b49; - label b49: jump b50; - label b50: jump b51; - label b51: jump b52; - label b52: jump b53; - label b53: jump b54; - label b54: jump b55; - label b55: jump b56; - label b56: jump b57; - label b57: jump b58; - label b58: jump b59; - label b59: jump b60; - label b60: jump b61; - label b61: jump b62; - label b62: jump b63; - label b63: jump b64; - label b64: jump b65; - label b65: jump b66; - label b66: jump b67; - label b67: jump b68; - label b68: jump b69; - label b69: jump b70; - label b70: jump b71; - label b71: jump b72; - label b72: jump b73; - label b73: jump b74; - label b74: jump b75; - label b75: jump b76; - label b76: jump b77; - label b77: jump b78; - label b78: jump b79; - label b79: jump b80; - label b80: jump b81; - label b81: jump b82; - label b82: jump b83; - label b83: jump b84; - label b84: jump b85; - label b85: jump b86; - label b86: jump b87; - label b87: jump b88; - label b88: jump b89; - label b89: jump b90; - label b90: jump b91; - label b91: jump b92; - label b92: jump b93; - label b93: jump b94; - label b94: jump b95; - label b95: jump b96; - label b96: jump b97; - label b97: jump b98; - label b98: jump b99; - label b99: jump b100; - label b100: jump b101; - label b101: jump b102; - label b102: jump b103; - label b103: jump b104; - label b104: jump b105; - label b105: jump b106; - label b106: jump b107; - label b107: jump b108; - label b108: jump b109; - label b109: jump b110; - label b110: jump b111; - label b111: jump b112; - label b112: jump b113; - label b113: jump b114; - label b114: jump b115; - label b115: jump b116; - label b116: jump b117; - label b117: jump b118; - label b118: jump b119; - label b119: jump b120; - label b120: jump b121; - label b121: jump b122; - label b122: jump b123; - label b123: jump b124; - label b124: jump b125; - label b125: jump b126; - label b126: jump b127; - label b127: jump b128; - label b128: jump b129; - label b129: jump b130; - label b130: jump b131; - label b131: jump b132; - label b132: jump b133; - label b133: jump b134; - label b134: jump b135; - label b135: jump b136; - label b136: jump b137; - label b137: jump b138; - label b138: jump b139; - label b139: jump b140; - label b140: jump b141; - label b141: jump b142; - label b142: jump b143; - label b143: jump b144; - label b144: jump b145; - label b145: jump b146; - label b146: jump b147; - label b147: jump b148; - label b148: jump b149; - label b149: jump b150; - label b150: jump b151; - label b151: jump b152; - label b152: jump b153; - label b153: jump b154; - label b154: jump b155; - label b155: jump b156; - label b156: jump b157; - label b157: jump b158; - label b158: jump b159; - label b159: jump b160; - label b160: jump b161; - label b161: jump b162; - label b162: jump b163; - label b163: jump b164; - label b164: jump b165; - label b165: jump b166; - label b166: jump b167; - label b167: jump b168; - label b168: jump b169; - label b169: jump b170; - label b170: jump b171; - label b171: jump b172; - label b172: jump b173; - label b173: jump b174; - label b174: jump b175; - label b175: jump b176; - label b176: jump b177; - label b177: jump b178; - label b178: jump b179; - label b179: jump b180; - label b180: jump b181; - label b181: jump b182; - label b182: jump b183; - label b183: jump b184; - label b184: jump b185; - label b185: jump b186; - label b186: jump b187; - label b187: jump b188; - label b188: jump b189; - label b189: jump b190; - label b190: jump b191; - label b191: jump b192; - label b192: jump b193; - label b193: jump b194; - label b194: jump b195; - label b195: jump b196; - label b196: jump b197; - label b197: jump b198; - label b198: jump b199; - label b199: jump b200; - label b200: jump b201; - label b201: jump b202; - label b202: jump b203; - label b203: jump b204; - label b204: jump b205; - label b205: jump b206; - label b206: jump b207; - label b207: jump b208; - label b208: jump b209; - label b209: jump b210; - label b210: jump b211; - label b211: jump b212; - label b212: jump b213; - label b213: jump b214; - label b214: jump b215; - label b215: jump b216; - label b216: jump b217; - label b217: jump b218; - label b218: jump b219; - label b219: jump b220; - label b220: jump b221; - label b221: jump b222; - label b222: jump b223; - label b223: jump b224; - label b224: jump b225; - label b225: jump b226; - label b226: jump b227; - label b227: jump b228; - label b228: jump b229; - label b229: jump b230; - label b230: jump b231; - label b231: jump b232; - label b232: jump b233; - label b233: jump b234; - label b234: jump b235; - label b235: jump b236; - label b236: jump b237; - label b237: jump b238; - label b238: jump b239; - label b239: jump b240; - label b240: jump b241; - label b241: jump b242; - label b242: jump b243; - label b243: jump b244; - label b244: jump b245; - label b245: jump b246; - label b246: jump b247; - label b247: jump b248; - label b248: jump b249; - label b249: jump b250; - label b250: jump b251; - label b251: jump b252; - label b252: jump b253; - label b253: jump b254; - label b254: jump b255; - label b255: jump b256; - label b256: jump b257; - label b257: jump b258; - label b258: jump b259; - label b259: jump b260; - label b260: jump b261; - label b261: jump b262; - label b262: jump b263; - label b263: jump b264; - label b264: jump b265; - label b265: jump b266; - label b266: jump b267; - label b267: jump b268; - label b268: jump b269; - label b269: jump b270; - label b270: jump b271; - label b271: jump b272; - label b272: jump b273; - label b273: jump b274; - label b274: jump b275; - label b275: jump b276; - label b276: jump b277; - label b277: jump b278; - label b278: jump b279; - label b279: jump b280; - label b280: jump b281; - label b281: jump b282; - label b282: jump b283; - label b283: jump b284; - label b284: jump b285; - label b285: jump b286; - label b286: jump b287; - label b287: jump b288; - label b288: jump b289; - label b289: jump b290; - label b290: jump b291; - label b291: jump b292; - label b292: jump b293; - label b293: jump b294; - label b294: jump b295; - label b295: jump b296; - label b296: jump b297; - label b297: jump b298; - label b298: jump b299; - label b299: jump b300; - label b300: jump b301; - label b301: jump b302; - label b302: jump b303; - label b303: jump b304; - label b304: jump b305; - label b305: jump b306; - label b306: jump b307; - label b307: jump b308; - label b308: jump b309; - label b309: jump b310; - label b310: jump b311; - label b311: jump b312; - label b312: jump b313; - label b313: jump b314; - label b314: jump b315; - label b315: jump b316; - label b316: jump b317; - label b317: jump b318; - label b318: jump b319; - label b319: jump b320; - label b320: jump b321; - label b321: jump b322; - label b322: jump b323; - label b323: jump b324; - label b324: jump b325; - label b325: jump b326; - label b326: jump b327; - label b327: jump b328; - label b328: jump b329; - label b329: jump b330; - label b330: jump b331; - label b331: jump b332; - label b332: jump b333; - label b333: jump b334; - label b334: jump b335; - label b335: jump b336; - label b336: jump b337; - label b337: jump b338; - label b338: jump b339; - label b339: jump b340; - label b340: jump b341; - label b341: jump b342; - label b342: jump b343; - label b343: jump b344; - label b344: jump b345; - label b345: jump b346; - label b346: jump b347; - label b347: jump b348; - label b348: jump b349; - label b349: jump b350; - label b350: jump b351; - label b351: jump b352; - label b352: jump b353; - label b353: jump b354; - label b354: jump b355; - label b355: jump b356; - label b356: jump b357; - label b357: jump b358; - label b358: jump b359; - label b359: jump b360; - label b360: jump b361; - label b361: jump b362; - label b362: jump b363; - label b363: jump b364; - label b364: jump b365; - label b365: jump b366; - label b366: jump b367; - label b367: jump b368; - label b368: jump b369; - label b369: jump b370; - label b370: jump b371; - label b371: jump b372; - label b372: jump b373; - label b373: jump b374; - label b374: jump b375; - label b375: jump b376; - label b376: jump b377; - label b377: jump b378; - label b378: jump b379; - label b379: jump b380; - label b380: jump b381; - label b381: jump b382; - label b382: jump b383; - label b383: jump b384; - label b384: jump b385; - label b385: jump b386; - label b386: jump b387; - label b387: jump b388; - label b388: jump b389; - label b389: jump b390; - label b390: jump b391; - label b391: jump b392; - label b392: jump b393; - label b393: jump b394; - label b394: jump b395; - label b395: jump b396; - label b396: jump b397; - label b397: jump b398; - label b398: jump b399; - label b399: jump b400; - label b400: jump b401; - label b401: jump b402; - label b402: jump b403; - label b403: jump b404; - label b404: jump b405; - label b405: jump b406; - label b406: jump b407; - label b407: jump b408; - label b408: jump b409; - label b409: jump b410; - label b410: jump b411; - label b411: jump b412; - label b412: jump b413; - label b413: jump b414; - label b414: jump b415; - label b415: jump b416; - label b416: jump b417; - label b417: jump b418; - label b418: jump b419; - label b419: jump b420; - label b420: jump b421; - label b421: jump b422; - label b422: jump b423; - label b423: jump b424; - label b424: jump b425; - label b425: jump b426; - label b426: jump b427; - label b427: jump b428; - label b428: jump b429; - label b429: jump b430; - label b430: jump b431; - label b431: jump b432; - label b432: jump b433; - label b433: jump b434; - label b434: jump b435; - label b435: jump b436; - label b436: jump b437; - label b437: jump b438; - label b438: jump b439; - label b439: jump b440; - label b440: jump b441; - label b441: jump b442; - label b442: jump b443; - label b443: jump b444; - label b444: jump b445; - label b445: jump b446; - label b446: jump b447; - label b447: jump b448; - label b448: jump b449; - label b449: jump b450; - label b450: jump b451; - label b451: jump b452; - label b452: jump b453; - label b453: jump b454; - label b454: jump b455; - label b455: jump b456; - label b456: jump b457; - label b457: jump b458; - label b458: jump b459; - label b459: jump b460; - label b460: jump b461; - label b461: jump b462; - label b462: jump b463; - label b463: jump b464; - label b464: jump b465; - label b465: jump b466; - label b466: jump b467; - label b467: jump b468; - label b468: jump b469; - label b469: jump b470; - label b470: jump b471; - label b471: jump b472; - label b472: jump b473; - label b473: jump b474; - label b474: jump b475; - label b475: jump b476; - label b476: jump b477; - label b477: jump b478; - label b478: jump b479; - label b479: jump b480; - label b480: jump b481; - label b481: jump b482; - label b482: jump b483; - label b483: jump b484; - label b484: jump b485; - label b485: jump b486; - label b486: jump b487; - label b487: jump b488; - label b488: jump b489; - label b489: jump b490; - label b490: jump b491; - label b491: jump b492; - label b492: jump b493; - label b493: jump b494; - label b494: jump b495; - label b495: jump b496; - label b496: jump b497; - label b497: jump b498; - label b498: jump b499; - label b499: jump b500; - label b500: - return; - } - - public entry calibrate_branch_x1000() { - label b0: jump b1; - label b1: jump b2; - label b2: jump b3; - label b3: jump b4; - label b4: jump b5; - label b5: jump b6; - label b6: jump b7; - label b7: jump b8; - label b8: jump b9; - label b9: jump b10; - label b10: jump b11; - label b11: jump b12; - label b12: jump b13; - label b13: jump b14; - label b14: jump b15; - label b15: jump b16; - label b16: jump b17; - label b17: jump b18; - label b18: jump b19; - label b19: jump b20; - label b20: jump b21; - label b21: jump b22; - label b22: jump b23; - label b23: jump b24; - label b24: jump b25; - label b25: jump b26; - label b26: jump b27; - label b27: jump b28; - label b28: jump b29; - label b29: jump b30; - label b30: jump b31; - label b31: jump b32; - label b32: jump b33; - label b33: jump b34; - label b34: jump b35; - label b35: jump b36; - label b36: jump b37; - label b37: jump b38; - label b38: jump b39; - label b39: jump b40; - label b40: jump b41; - label b41: jump b42; - label b42: jump b43; - label b43: jump b44; - label b44: jump b45; - label b45: jump b46; - label b46: jump b47; - label b47: jump b48; - label b48: jump b49; - label b49: jump b50; - label b50: jump b51; - label b51: jump b52; - label b52: jump b53; - label b53: jump b54; - label b54: jump b55; - label b55: jump b56; - label b56: jump b57; - label b57: jump b58; - label b58: jump b59; - label b59: jump b60; - label b60: jump b61; - label b61: jump b62; - label b62: jump b63; - label b63: jump b64; - label b64: jump b65; - label b65: jump b66; - label b66: jump b67; - label b67: jump b68; - label b68: jump b69; - label b69: jump b70; - label b70: jump b71; - label b71: jump b72; - label b72: jump b73; - label b73: jump b74; - label b74: jump b75; - label b75: jump b76; - label b76: jump b77; - label b77: jump b78; - label b78: jump b79; - label b79: jump b80; - label b80: jump b81; - label b81: jump b82; - label b82: jump b83; - label b83: jump b84; - label b84: jump b85; - label b85: jump b86; - label b86: jump b87; - label b87: jump b88; - label b88: jump b89; - label b89: jump b90; - label b90: jump b91; - label b91: jump b92; - label b92: jump b93; - label b93: jump b94; - label b94: jump b95; - label b95: jump b96; - label b96: jump b97; - label b97: jump b98; - label b98: jump b99; - label b99: jump b100; - label b100: jump b101; - label b101: jump b102; - label b102: jump b103; - label b103: jump b104; - label b104: jump b105; - label b105: jump b106; - label b106: jump b107; - label b107: jump b108; - label b108: jump b109; - label b109: jump b110; - label b110: jump b111; - label b111: jump b112; - label b112: jump b113; - label b113: jump b114; - label b114: jump b115; - label b115: jump b116; - label b116: jump b117; - label b117: jump b118; - label b118: jump b119; - label b119: jump b120; - label b120: jump b121; - label b121: jump b122; - label b122: jump b123; - label b123: jump b124; - label b124: jump b125; - label b125: jump b126; - label b126: jump b127; - label b127: jump b128; - label b128: jump b129; - label b129: jump b130; - label b130: jump b131; - label b131: jump b132; - label b132: jump b133; - label b133: jump b134; - label b134: jump b135; - label b135: jump b136; - label b136: jump b137; - label b137: jump b138; - label b138: jump b139; - label b139: jump b140; - label b140: jump b141; - label b141: jump b142; - label b142: jump b143; - label b143: jump b144; - label b144: jump b145; - label b145: jump b146; - label b146: jump b147; - label b147: jump b148; - label b148: jump b149; - label b149: jump b150; - label b150: jump b151; - label b151: jump b152; - label b152: jump b153; - label b153: jump b154; - label b154: jump b155; - label b155: jump b156; - label b156: jump b157; - label b157: jump b158; - label b158: jump b159; - label b159: jump b160; - label b160: jump b161; - label b161: jump b162; - label b162: jump b163; - label b163: jump b164; - label b164: jump b165; - label b165: jump b166; - label b166: jump b167; - label b167: jump b168; - label b168: jump b169; - label b169: jump b170; - label b170: jump b171; - label b171: jump b172; - label b172: jump b173; - label b173: jump b174; - label b174: jump b175; - label b175: jump b176; - label b176: jump b177; - label b177: jump b178; - label b178: jump b179; - label b179: jump b180; - label b180: jump b181; - label b181: jump b182; - label b182: jump b183; - label b183: jump b184; - label b184: jump b185; - label b185: jump b186; - label b186: jump b187; - label b187: jump b188; - label b188: jump b189; - label b189: jump b190; - label b190: jump b191; - label b191: jump b192; - label b192: jump b193; - label b193: jump b194; - label b194: jump b195; - label b195: jump b196; - label b196: jump b197; - label b197: jump b198; - label b198: jump b199; - label b199: jump b200; - label b200: jump b201; - label b201: jump b202; - label b202: jump b203; - label b203: jump b204; - label b204: jump b205; - label b205: jump b206; - label b206: jump b207; - label b207: jump b208; - label b208: jump b209; - label b209: jump b210; - label b210: jump b211; - label b211: jump b212; - label b212: jump b213; - label b213: jump b214; - label b214: jump b215; - label b215: jump b216; - label b216: jump b217; - label b217: jump b218; - label b218: jump b219; - label b219: jump b220; - label b220: jump b221; - label b221: jump b222; - label b222: jump b223; - label b223: jump b224; - label b224: jump b225; - label b225: jump b226; - label b226: jump b227; - label b227: jump b228; - label b228: jump b229; - label b229: jump b230; - label b230: jump b231; - label b231: jump b232; - label b232: jump b233; - label b233: jump b234; - label b234: jump b235; - label b235: jump b236; - label b236: jump b237; - label b237: jump b238; - label b238: jump b239; - label b239: jump b240; - label b240: jump b241; - label b241: jump b242; - label b242: jump b243; - label b243: jump b244; - label b244: jump b245; - label b245: jump b246; - label b246: jump b247; - label b247: jump b248; - label b248: jump b249; - label b249: jump b250; - label b250: jump b251; - label b251: jump b252; - label b252: jump b253; - label b253: jump b254; - label b254: jump b255; - label b255: jump b256; - label b256: jump b257; - label b257: jump b258; - label b258: jump b259; - label b259: jump b260; - label b260: jump b261; - label b261: jump b262; - label b262: jump b263; - label b263: jump b264; - label b264: jump b265; - label b265: jump b266; - label b266: jump b267; - label b267: jump b268; - label b268: jump b269; - label b269: jump b270; - label b270: jump b271; - label b271: jump b272; - label b272: jump b273; - label b273: jump b274; - label b274: jump b275; - label b275: jump b276; - label b276: jump b277; - label b277: jump b278; - label b278: jump b279; - label b279: jump b280; - label b280: jump b281; - label b281: jump b282; - label b282: jump b283; - label b283: jump b284; - label b284: jump b285; - label b285: jump b286; - label b286: jump b287; - label b287: jump b288; - label b288: jump b289; - label b289: jump b290; - label b290: jump b291; - label b291: jump b292; - label b292: jump b293; - label b293: jump b294; - label b294: jump b295; - label b295: jump b296; - label b296: jump b297; - label b297: jump b298; - label b298: jump b299; - label b299: jump b300; - label b300: jump b301; - label b301: jump b302; - label b302: jump b303; - label b303: jump b304; - label b304: jump b305; - label b305: jump b306; - label b306: jump b307; - label b307: jump b308; - label b308: jump b309; - label b309: jump b310; - label b310: jump b311; - label b311: jump b312; - label b312: jump b313; - label b313: jump b314; - label b314: jump b315; - label b315: jump b316; - label b316: jump b317; - label b317: jump b318; - label b318: jump b319; - label b319: jump b320; - label b320: jump b321; - label b321: jump b322; - label b322: jump b323; - label b323: jump b324; - label b324: jump b325; - label b325: jump b326; - label b326: jump b327; - label b327: jump b328; - label b328: jump b329; - label b329: jump b330; - label b330: jump b331; - label b331: jump b332; - label b332: jump b333; - label b333: jump b334; - label b334: jump b335; - label b335: jump b336; - label b336: jump b337; - label b337: jump b338; - label b338: jump b339; - label b339: jump b340; - label b340: jump b341; - label b341: jump b342; - label b342: jump b343; - label b343: jump b344; - label b344: jump b345; - label b345: jump b346; - label b346: jump b347; - label b347: jump b348; - label b348: jump b349; - label b349: jump b350; - label b350: jump b351; - label b351: jump b352; - label b352: jump b353; - label b353: jump b354; - label b354: jump b355; - label b355: jump b356; - label b356: jump b357; - label b357: jump b358; - label b358: jump b359; - label b359: jump b360; - label b360: jump b361; - label b361: jump b362; - label b362: jump b363; - label b363: jump b364; - label b364: jump b365; - label b365: jump b366; - label b366: jump b367; - label b367: jump b368; - label b368: jump b369; - label b369: jump b370; - label b370: jump b371; - label b371: jump b372; - label b372: jump b373; - label b373: jump b374; - label b374: jump b375; - label b375: jump b376; - label b376: jump b377; - label b377: jump b378; - label b378: jump b379; - label b379: jump b380; - label b380: jump b381; - label b381: jump b382; - label b382: jump b383; - label b383: jump b384; - label b384: jump b385; - label b385: jump b386; - label b386: jump b387; - label b387: jump b388; - label b388: jump b389; - label b389: jump b390; - label b390: jump b391; - label b391: jump b392; - label b392: jump b393; - label b393: jump b394; - label b394: jump b395; - label b395: jump b396; - label b396: jump b397; - label b397: jump b398; - label b398: jump b399; - label b399: jump b400; - label b400: jump b401; - label b401: jump b402; - label b402: jump b403; - label b403: jump b404; - label b404: jump b405; - label b405: jump b406; - label b406: jump b407; - label b407: jump b408; - label b408: jump b409; - label b409: jump b410; - label b410: jump b411; - label b411: jump b412; - label b412: jump b413; - label b413: jump b414; - label b414: jump b415; - label b415: jump b416; - label b416: jump b417; - label b417: jump b418; - label b418: jump b419; - label b419: jump b420; - label b420: jump b421; - label b421: jump b422; - label b422: jump b423; - label b423: jump b424; - label b424: jump b425; - label b425: jump b426; - label b426: jump b427; - label b427: jump b428; - label b428: jump b429; - label b429: jump b430; - label b430: jump b431; - label b431: jump b432; - label b432: jump b433; - label b433: jump b434; - label b434: jump b435; - label b435: jump b436; - label b436: jump b437; - label b437: jump b438; - label b438: jump b439; - label b439: jump b440; - label b440: jump b441; - label b441: jump b442; - label b442: jump b443; - label b443: jump b444; - label b444: jump b445; - label b445: jump b446; - label b446: jump b447; - label b447: jump b448; - label b448: jump b449; - label b449: jump b450; - label b450: jump b451; - label b451: jump b452; - label b452: jump b453; - label b453: jump b454; - label b454: jump b455; - label b455: jump b456; - label b456: jump b457; - label b457: jump b458; - label b458: jump b459; - label b459: jump b460; - label b460: jump b461; - label b461: jump b462; - label b462: jump b463; - label b463: jump b464; - label b464: jump b465; - label b465: jump b466; - label b466: jump b467; - label b467: jump b468; - label b468: jump b469; - label b469: jump b470; - label b470: jump b471; - label b471: jump b472; - label b472: jump b473; - label b473: jump b474; - label b474: jump b475; - label b475: jump b476; - label b476: jump b477; - label b477: jump b478; - label b478: jump b479; - label b479: jump b480; - label b480: jump b481; - label b481: jump b482; - label b482: jump b483; - label b483: jump b484; - label b484: jump b485; - label b485: jump b486; - label b486: jump b487; - label b487: jump b488; - label b488: jump b489; - label b489: jump b490; - label b490: jump b491; - label b491: jump b492; - label b492: jump b493; - label b493: jump b494; - label b494: jump b495; - label b495: jump b496; - label b496: jump b497; - label b497: jump b498; - label b498: jump b499; - label b499: jump b500; - label b500: jump b501; - label b501: jump b502; - label b502: jump b503; - label b503: jump b504; - label b504: jump b505; - label b505: jump b506; - label b506: jump b507; - label b507: jump b508; - label b508: jump b509; - label b509: jump b510; - label b510: jump b511; - label b511: jump b512; - label b512: jump b513; - label b513: jump b514; - label b514: jump b515; - label b515: jump b516; - label b516: jump b517; - label b517: jump b518; - label b518: jump b519; - label b519: jump b520; - label b520: jump b521; - label b521: jump b522; - label b522: jump b523; - label b523: jump b524; - label b524: jump b525; - label b525: jump b526; - label b526: jump b527; - label b527: jump b528; - label b528: jump b529; - label b529: jump b530; - label b530: jump b531; - label b531: jump b532; - label b532: jump b533; - label b533: jump b534; - label b534: jump b535; - label b535: jump b536; - label b536: jump b537; - label b537: jump b538; - label b538: jump b539; - label b539: jump b540; - label b540: jump b541; - label b541: jump b542; - label b542: jump b543; - label b543: jump b544; - label b544: jump b545; - label b545: jump b546; - label b546: jump b547; - label b547: jump b548; - label b548: jump b549; - label b549: jump b550; - label b550: jump b551; - label b551: jump b552; - label b552: jump b553; - label b553: jump b554; - label b554: jump b555; - label b555: jump b556; - label b556: jump b557; - label b557: jump b558; - label b558: jump b559; - label b559: jump b560; - label b560: jump b561; - label b561: jump b562; - label b562: jump b563; - label b563: jump b564; - label b564: jump b565; - label b565: jump b566; - label b566: jump b567; - label b567: jump b568; - label b568: jump b569; - label b569: jump b570; - label b570: jump b571; - label b571: jump b572; - label b572: jump b573; - label b573: jump b574; - label b574: jump b575; - label b575: jump b576; - label b576: jump b577; - label b577: jump b578; - label b578: jump b579; - label b579: jump b580; - label b580: jump b581; - label b581: jump b582; - label b582: jump b583; - label b583: jump b584; - label b584: jump b585; - label b585: jump b586; - label b586: jump b587; - label b587: jump b588; - label b588: jump b589; - label b589: jump b590; - label b590: jump b591; - label b591: jump b592; - label b592: jump b593; - label b593: jump b594; - label b594: jump b595; - label b595: jump b596; - label b596: jump b597; - label b597: jump b598; - label b598: jump b599; - label b599: jump b600; - label b600: jump b601; - label b601: jump b602; - label b602: jump b603; - label b603: jump b604; - label b604: jump b605; - label b605: jump b606; - label b606: jump b607; - label b607: jump b608; - label b608: jump b609; - label b609: jump b610; - label b610: jump b611; - label b611: jump b612; - label b612: jump b613; - label b613: jump b614; - label b614: jump b615; - label b615: jump b616; - label b616: jump b617; - label b617: jump b618; - label b618: jump b619; - label b619: jump b620; - label b620: jump b621; - label b621: jump b622; - label b622: jump b623; - label b623: jump b624; - label b624: jump b625; - label b625: jump b626; - label b626: jump b627; - label b627: jump b628; - label b628: jump b629; - label b629: jump b630; - label b630: jump b631; - label b631: jump b632; - label b632: jump b633; - label b633: jump b634; - label b634: jump b635; - label b635: jump b636; - label b636: jump b637; - label b637: jump b638; - label b638: jump b639; - label b639: jump b640; - label b640: jump b641; - label b641: jump b642; - label b642: jump b643; - label b643: jump b644; - label b644: jump b645; - label b645: jump b646; - label b646: jump b647; - label b647: jump b648; - label b648: jump b649; - label b649: jump b650; - label b650: jump b651; - label b651: jump b652; - label b652: jump b653; - label b653: jump b654; - label b654: jump b655; - label b655: jump b656; - label b656: jump b657; - label b657: jump b658; - label b658: jump b659; - label b659: jump b660; - label b660: jump b661; - label b661: jump b662; - label b662: jump b663; - label b663: jump b664; - label b664: jump b665; - label b665: jump b666; - label b666: jump b667; - label b667: jump b668; - label b668: jump b669; - label b669: jump b670; - label b670: jump b671; - label b671: jump b672; - label b672: jump b673; - label b673: jump b674; - label b674: jump b675; - label b675: jump b676; - label b676: jump b677; - label b677: jump b678; - label b678: jump b679; - label b679: jump b680; - label b680: jump b681; - label b681: jump b682; - label b682: jump b683; - label b683: jump b684; - label b684: jump b685; - label b685: jump b686; - label b686: jump b687; - label b687: jump b688; - label b688: jump b689; - label b689: jump b690; - label b690: jump b691; - label b691: jump b692; - label b692: jump b693; - label b693: jump b694; - label b694: jump b695; - label b695: jump b696; - label b696: jump b697; - label b697: jump b698; - label b698: jump b699; - label b699: jump b700; - label b700: jump b701; - label b701: jump b702; - label b702: jump b703; - label b703: jump b704; - label b704: jump b705; - label b705: jump b706; - label b706: jump b707; - label b707: jump b708; - label b708: jump b709; - label b709: jump b710; - label b710: jump b711; - label b711: jump b712; - label b712: jump b713; - label b713: jump b714; - label b714: jump b715; - label b715: jump b716; - label b716: jump b717; - label b717: jump b718; - label b718: jump b719; - label b719: jump b720; - label b720: jump b721; - label b721: jump b722; - label b722: jump b723; - label b723: jump b724; - label b724: jump b725; - label b725: jump b726; - label b726: jump b727; - label b727: jump b728; - label b728: jump b729; - label b729: jump b730; - label b730: jump b731; - label b731: jump b732; - label b732: jump b733; - label b733: jump b734; - label b734: jump b735; - label b735: jump b736; - label b736: jump b737; - label b737: jump b738; - label b738: jump b739; - label b739: jump b740; - label b740: jump b741; - label b741: jump b742; - label b742: jump b743; - label b743: jump b744; - label b744: jump b745; - label b745: jump b746; - label b746: jump b747; - label b747: jump b748; - label b748: jump b749; - label b749: jump b750; - label b750: jump b751; - label b751: jump b752; - label b752: jump b753; - label b753: jump b754; - label b754: jump b755; - label b755: jump b756; - label b756: jump b757; - label b757: jump b758; - label b758: jump b759; - label b759: jump b760; - label b760: jump b761; - label b761: jump b762; - label b762: jump b763; - label b763: jump b764; - label b764: jump b765; - label b765: jump b766; - label b766: jump b767; - label b767: jump b768; - label b768: jump b769; - label b769: jump b770; - label b770: jump b771; - label b771: jump b772; - label b772: jump b773; - label b773: jump b774; - label b774: jump b775; - label b775: jump b776; - label b776: jump b777; - label b777: jump b778; - label b778: jump b779; - label b779: jump b780; - label b780: jump b781; - label b781: jump b782; - label b782: jump b783; - label b783: jump b784; - label b784: jump b785; - label b785: jump b786; - label b786: jump b787; - label b787: jump b788; - label b788: jump b789; - label b789: jump b790; - label b790: jump b791; - label b791: jump b792; - label b792: jump b793; - label b793: jump b794; - label b794: jump b795; - label b795: jump b796; - label b796: jump b797; - label b797: jump b798; - label b798: jump b799; - label b799: jump b800; - label b800: jump b801; - label b801: jump b802; - label b802: jump b803; - label b803: jump b804; - label b804: jump b805; - label b805: jump b806; - label b806: jump b807; - label b807: jump b808; - label b808: jump b809; - label b809: jump b810; - label b810: jump b811; - label b811: jump b812; - label b812: jump b813; - label b813: jump b814; - label b814: jump b815; - label b815: jump b816; - label b816: jump b817; - label b817: jump b818; - label b818: jump b819; - label b819: jump b820; - label b820: jump b821; - label b821: jump b822; - label b822: jump b823; - label b823: jump b824; - label b824: jump b825; - label b825: jump b826; - label b826: jump b827; - label b827: jump b828; - label b828: jump b829; - label b829: jump b830; - label b830: jump b831; - label b831: jump b832; - label b832: jump b833; - label b833: jump b834; - label b834: jump b835; - label b835: jump b836; - label b836: jump b837; - label b837: jump b838; - label b838: jump b839; - label b839: jump b840; - label b840: jump b841; - label b841: jump b842; - label b842: jump b843; - label b843: jump b844; - label b844: jump b845; - label b845: jump b846; - label b846: jump b847; - label b847: jump b848; - label b848: jump b849; - label b849: jump b850; - label b850: jump b851; - label b851: jump b852; - label b852: jump b853; - label b853: jump b854; - label b854: jump b855; - label b855: jump b856; - label b856: jump b857; - label b857: jump b858; - label b858: jump b859; - label b859: jump b860; - label b860: jump b861; - label b861: jump b862; - label b862: jump b863; - label b863: jump b864; - label b864: jump b865; - label b865: jump b866; - label b866: jump b867; - label b867: jump b868; - label b868: jump b869; - label b869: jump b870; - label b870: jump b871; - label b871: jump b872; - label b872: jump b873; - label b873: jump b874; - label b874: jump b875; - label b875: jump b876; - label b876: jump b877; - label b877: jump b878; - label b878: jump b879; - label b879: jump b880; - label b880: jump b881; - label b881: jump b882; - label b882: jump b883; - label b883: jump b884; - label b884: jump b885; - label b885: jump b886; - label b886: jump b887; - label b887: jump b888; - label b888: jump b889; - label b889: jump b890; - label b890: jump b891; - label b891: jump b892; - label b892: jump b893; - label b893: jump b894; - label b894: jump b895; - label b895: jump b896; - label b896: jump b897; - label b897: jump b898; - label b898: jump b899; - label b899: jump b900; - label b900: jump b901; - label b901: jump b902; - label b902: jump b903; - label b903: jump b904; - label b904: jump b905; - label b905: jump b906; - label b906: jump b907; - label b907: jump b908; - label b908: jump b909; - label b909: jump b910; - label b910: jump b911; - label b911: jump b912; - label b912: jump b913; - label b913: jump b914; - label b914: jump b915; - label b915: jump b916; - label b916: jump b917; - label b917: jump b918; - label b918: jump b919; - label b919: jump b920; - label b920: jump b921; - label b921: jump b922; - label b922: jump b923; - label b923: jump b924; - label b924: jump b925; - label b925: jump b926; - label b926: jump b927; - label b927: jump b928; - label b928: jump b929; - label b929: jump b930; - label b930: jump b931; - label b931: jump b932; - label b932: jump b933; - label b933: jump b934; - label b934: jump b935; - label b935: jump b936; - label b936: jump b937; - label b937: jump b938; - label b938: jump b939; - label b939: jump b940; - label b940: jump b941; - label b941: jump b942; - label b942: jump b943; - label b943: jump b944; - label b944: jump b945; - label b945: jump b946; - label b946: jump b947; - label b947: jump b948; - label b948: jump b949; - label b949: jump b950; - label b950: jump b951; - label b951: jump b952; - label b952: jump b953; - label b953: jump b954; - label b954: jump b955; - label b955: jump b956; - label b956: jump b957; - label b957: jump b958; - label b958: jump b959; - label b959: jump b960; - label b960: jump b961; - label b961: jump b962; - label b962: jump b963; - label b963: jump b964; - label b964: jump b965; - label b965: jump b966; - label b966: jump b967; - label b967: jump b968; - label b968: jump b969; - label b969: jump b970; - label b970: jump b971; - label b971: jump b972; - label b972: jump b973; - label b973: jump b974; - label b974: jump b975; - label b975: jump b976; - label b976: jump b977; - label b977: jump b978; - label b978: jump b979; - label b979: jump b980; - label b980: jump b981; - label b981: jump b982; - label b982: jump b983; - label b983: jump b984; - label b984: jump b985; - label b985: jump b986; - label b986: jump b987; - label b987: jump b988; - label b988: jump b989; - label b989: jump b990; - label b990: jump b991; - label b991: jump b992; - label b992: jump b993; - label b993: jump b994; - label b994: jump b995; - label b995: jump b996; - label b996: jump b997; - label b997: jump b998; - label b998: jump b999; - label b999: jump b1000; - label b1000: - return; - } - - public entry calibrate_lt_x10() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - return; - } - - public entry calibrate_lt_x50() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - return; - } - - public entry calibrate_lt_x100() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - return; - } - - public entry calibrate_lt_x300() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - return; - } - - public entry calibrate_lt_x600() { - label b0: - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - _, _, _, _, _, _, _, _, _, _ = (0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10, 0 < 10); - return; - } - - public entry calibrate_copy_loc_u64_x100() { - let v: u64; - label b0: - v = 42; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_u64_x500() { - let v: u64; - label b0: - v = 42; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_u64_x1000() { - let v: u64; - label b0: - v = 42; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_v16_x100() { - let v: vector; - label b0: - v = h"0011223344556677889900aabbccddeeff"; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_v64_x100() { - let v: vector; - label b0: - v = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_v256_x100() { - let v: vector; - label b0: - v = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_v1024_x100() { - let v: vector; - label b0: - v = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } - - public entry calibrate_copy_loc_v4096_x100() { - let v: vector; - label b0: - v = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - _, _, _, _, _, _, _, _, _, _ = (copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v), copy(v)); - - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core.mvir b/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core.mvir deleted file mode 100644 index aa8172f4edb68..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/bootstrap/core.mvir +++ /dev/null @@ -1,612 +0,0 @@ -module 0xcafe.BootstrapCore { - // A minimal set of samples that allow us to calibrate these instructions: ld_u64, pop & sub. - // Among these instructions, pop is the most important one, since it is in many cases required - // to maintain stack balance. - // Calibrating pop will allow many other instructions to be calibrated as well. - - public entry calibrate_empty_fun() { - label b0: - return; - } - - public entry calibrate_ldu64_and_pop_x10() { - label b0: - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - return; - } - - public entry_ldu64_and_pop_x50() { - label b0: - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - return; - } - - public entry calibrate_ldu64_and_pop_x100() { - label b0: - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - return; - } - - public entry calibrate_ldu64_and_pop_x300() { - label b0: - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - return; - } - - public entry calibrate_ldu64_and_pop_x600() { - label b0: - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - _,_,_,_,_,_,_,_,_,_ = (1,2,3,4,5,6,7,8,9,10); - return; - } - - public entry calibrate_sub_and_ldu64_10() { - label b0: - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - return; - } - - public entry calibrate_sub_and_ldu64_50() { - label b0: - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - return; - } - - public entry calibrate_sub_and_ldu64_100() { - label b0: - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - return; - } - - public entry calibrate_sub_and_ldu64_300() { - label b0: - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - return; - } - - public entry calibrate_sub_and_ldu64_600() { - label b0: - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - _ = (10 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1); - return; - } - - public entry calibrate_ldu64_10_should_error() { - label b0: - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - _ = (0 - 1); - - _, _, _, _, _, _, _, _, _, _ = (); - - return; - } - - public entry calibrate_ldu64_50_should_error() { - label b0: - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - _ = (0 - 1); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - return; - } - - public entry calibrate_ldu64_100_should_error() { - label b0: - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - _ = (0 - 1); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - return; - } - - public entry calibrate_ldu64_300_should_error() { - label b0: - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - _ = (0 - 1); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - return; - } - - public entry calibrate_ldu64_600_should_error() { - label b0: - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - - _ = (0 - 1); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - _, _, _, _, _, _, _, _, _, _ = (); - return; - } - - public entry calibrate_tuning_many_of_all_instr() { - label b0: - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - (1,2,3,4,5,6,7,8,9,10); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _ = (10000-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - _,_,_,_,_,_,_,_,_,_ = (); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/borrow/freeze_ref.mvir b/aptos-move/aptos-gas-calibration/samples_ir/borrow/freeze_ref.mvir deleted file mode 100644 index 4022edebe29a5..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/borrow/freeze_ref.mvir +++ /dev/null @@ -1,46 +0,0 @@ -module 0xcafe.FreezeRef { - - public calibrate_freeze_ref_impl(n: u64) { - let i: u64; - let a: u64; - label entry: - i = 0; - a = 5; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - _ = freeze(&mut a); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_freeze_ref_x100() { - label b0: - Self.calibrate_freeze_ref_impl(10); - return; - } - - public entry calibrate_freeze_ref_x500() { - label b0: - Self.calibrate_freeze_ref_impl(50); - return; - } - - public entry calibrate_freeze_ref_x1000() { - label b0: - Self.calibrate_freeze_ref_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/borrow/imm-borrow-loc.mvir b/aptos-move/aptos-gas-calibration/samples_ir/borrow/imm-borrow-loc.mvir deleted file mode 100644 index acecdec32320c..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/borrow/imm-borrow-loc.mvir +++ /dev/null @@ -1,37 +0,0 @@ -module 0xcafe.ImmBorrowLoc { - - public calibrate_imm_borrow_loc_impl(n: u64) { - let i: u64; - let a: u64; - label entry: - i = 0; - a = 5; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (&a, &a, &a, &a, &a, &a, &a, &a, &a, &a); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_loc_x100() { - label b0: - Self.calibrate_imm_borrow_loc_impl(10); - return; - } - - public entry calibrate_imm_borrow_loc_x500() { - label b0: - Self.calibrate_imm_borrow_loc_impl(50); - return; - } - - public entry calibrate_imm_borrow_loc_x1000() { - label b0: - Self.calibrate_imm_borrow_loc_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/borrow/mut-borrow-loc.mvir b/aptos-move/aptos-gas-calibration/samples_ir/borrow/mut-borrow-loc.mvir deleted file mode 100644 index abc70f665c986..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/borrow/mut-borrow-loc.mvir +++ /dev/null @@ -1,37 +0,0 @@ -module 0xcafe.MutBorrowLoc { - - public calibrate_mut_borrow_loc_impl(n: u64) { - let i: u64; - let a: u64; - label entry: - i = 0; - a = 5; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (&mut a, &mut a, &mut a, &mut a, &mut a, &mut a, &mut a, &mut a, &mut a, &mut a); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_loc_x100() { - label b0: - Self.calibrate_mut_borrow_loc_impl(10); - return; - } - - public entry calibrate_mut_borrow_loc_x500() { - label b0: - Self.calibrate_mut_borrow_loc_impl(50); - return; - } - - public entry calibrate_mut_borrow_loc_x1000() { - label b0: - Self.calibrate_mut_borrow_loc_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/borrow/read_ref.mvir b/aptos-move/aptos-gas-calibration/samples_ir/borrow/read_ref.mvir deleted file mode 100644 index 28e8e352a9053..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/borrow/read_ref.mvir +++ /dev/null @@ -1,178 +0,0 @@ -module 0xcafe.ReadRef { - - public calibrate_read_ref_u64_impl(n: u64) { - let i: u64; - let a: u64; - label entry: - i = 0; - a = 5; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_read_ref_u64_x100() { - label b0: - Self.calibrate_read_ref_u64_impl(10); - return; - } - - public entry calibrate_read_ref_u64_x500() { - label b0: - Self.calibrate_read_ref_u64_impl(50); - return; - } - - public entry calibrate_read_ref_u64_x1000() { - label b0: - Self.calibrate_read_ref_u64_impl(100); - return; - } - - public calibrate_read_ref_v256_impl(n: u64) { - let i: u64; - let a: vector; - label entry: - i = 0; - a = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_read_ref_v256_x100() { - label b0: - Self.calibrate_read_ref_v256_impl(10); - return; - } - - public entry calibrate_read_ref_v256_x500() { - label b0: - Self.calibrate_read_ref_v256_impl(50); - return; - } - - public entry calibrate_read_ref_v256_x1000() { - label b0: - Self.calibrate_read_ref_v256_impl(100); - return; - } - - public calibrate_read_ref_v1024_impl(n: u64) { - let i: u64; - let a: vector; - label entry: - i = 0; - a = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_read_ref_v1024_x100() { - label b0: - Self.calibrate_read_ref_v1024_impl(10); - return; - } - - public entry calibrate_read_ref_v1024_x500() { - label b0: - Self.calibrate_read_ref_v1024_impl(50); - return; - } - - public entry calibrate_read_ref_v1024_x1000() { - label b0: - Self.calibrate_read_ref_v1024_impl(100); - return; - } - - public calibrate_read_ref_v4096_impl(n: u64) { - let i: u64; - let a: vector; - label entry: - i = 0; - a = h"0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - _ = *&a; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_read_ref_v4096_x100() { - label b0: - Self.calibrate_read_ref_v4096_impl(10); - return; - } - - public entry calibrate_read_ref_v4096_x500() { - label b0: - Self.calibrate_read_ref_v4096_impl(50); - return; - } - - public entry calibrate_read_ref_v4096_x1000() { - label b0: - Self.calibrate_read_ref_v4096_impl(100); - return; - } -} diff --git a/aptos-move/aptos-gas-calibration/samples_ir/borrow/write_ref.mvir b/aptos-move/aptos-gas-calibration/samples_ir/borrow/write_ref.mvir deleted file mode 100644 index fd5c220ac1eeb..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/borrow/write_ref.mvir +++ /dev/null @@ -1,49 +0,0 @@ -module 0xcafe.WriteRef { - - public calibrate_write_ref_impl(n: u64) { - let i: u64; - let a: u64; - label entry: - i = 0; - a = 5; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - *(&mut a) = 42; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_write_ref_x100() { - label b0: - Self.calibrate_write_ref_impl(10); - return; - } - - public entry calibrate_write_ref_x500() { - label b0: - Self.calibrate_write_ref_impl(50); - return; - } - - public entry calibrate_write_ref_x1000() { - label b0: - Self.calibrate_write_ref_impl(100); - return; - } - - // TODO(Gas): add more test cases for vector & struct values, but ideally - // those should not make a difference. -} diff --git a/aptos-move/aptos-gas-calibration/samples_ir/branch/br-false.mvir b/aptos-move/aptos-gas-calibration/samples_ir/branch/br-false.mvir deleted file mode 100644 index 7b533b7bd954f..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/branch/br-false.mvir +++ /dev/null @@ -1,109 +0,0 @@ -module 0xcafe.BrFalse { - // more BrFalse cases - - public entry calibrate_br_false_x100() { - label b0: jump_if_false (false) b1; - label b1: jump_if_false (false) b2; - label b2: jump_if_false (false) b3; - label b3: jump_if_false (false) b4; - label b4: jump_if_false (false) b5; - label b5: jump_if_false (false) b6; - label b6: jump_if_false (false) b7; - label b7: jump_if_false (false) b8; - label b8: jump_if_false (false) b9; - label b9: jump_if_false (false) b10; - label b10: jump_if_false (false) b11; - label b11: jump_if_false (false) b12; - label b12: jump_if_false (false) b13; - label b13: jump_if_false (false) b14; - label b14: jump_if_false (false) b15; - label b15: jump_if_false (false) b16; - label b16: jump_if_false (false) b17; - label b17: jump_if_false (false) b18; - label b18: jump_if_false (false) b19; - label b19: jump_if_false (false) b20; - label b20: jump_if_false (false) b21; - label b21: jump_if_false (false) b22; - label b22: jump_if_false (false) b23; - label b23: jump_if_false (false) b24; - label b24: jump_if_false (false) b25; - label b25: jump_if_false (false) b26; - label b26: jump_if_false (false) b27; - label b27: jump_if_false (false) b28; - label b28: jump_if_false (false) b29; - label b29: jump_if_false (false) b30; - label b30: jump_if_false (false) b31; - label b31: jump_if_false (false) b32; - label b32: jump_if_false (false) b33; - label b33: jump_if_false (false) b34; - label b34: jump_if_false (false) b35; - label b35: jump_if_false (false) b36; - label b36: jump_if_false (false) b37; - label b37: jump_if_false (false) b38; - label b38: jump_if_false (false) b39; - label b39: jump_if_false (false) b40; - label b40: jump_if_false (false) b41; - label b41: jump_if_false (false) b42; - label b42: jump_if_false (false) b43; - label b43: jump_if_false (false) b44; - label b44: jump_if_false (false) b45; - label b45: jump_if_false (false) b46; - label b46: jump_if_false (false) b47; - label b47: jump_if_false (false) b48; - label b48: jump_if_false (false) b49; - label b49: jump_if_false (false) b50; - label b50: jump_if_false (false) b51; - label b51: jump_if_false (false) b52; - label b52: jump_if_false (false) b53; - label b53: jump_if_false (false) b54; - label b54: jump_if_false (false) b55; - label b55: jump_if_false (false) b56; - label b56: jump_if_false (false) b57; - label b57: jump_if_false (false) b58; - label b58: jump_if_false (false) b59; - label b59: jump_if_false (false) b60; - label b60: jump_if_false (false) b61; - label b61: jump_if_false (false) b62; - label b62: jump_if_false (false) b63; - label b63: jump_if_false (false) b64; - label b64: jump_if_false (false) b65; - label b65: jump_if_false (false) b66; - label b66: jump_if_false (false) b67; - label b67: jump_if_false (false) b68; - label b68: jump_if_false (false) b69; - label b69: jump_if_false (false) b70; - label b70: jump_if_false (false) b71; - label b71: jump_if_false (false) b72; - label b72: jump_if_false (false) b73; - label b73: jump_if_false (false) b74; - label b74: jump_if_false (false) b75; - label b75: jump_if_false (false) b76; - label b76: jump_if_false (false) b77; - label b77: jump_if_false (false) b78; - label b78: jump_if_false (false) b79; - label b79: jump_if_false (false) b80; - label b80: jump_if_false (false) b81; - label b81: jump_if_false (false) b82; - label b82: jump_if_false (false) b83; - label b83: jump_if_false (false) b84; - label b84: jump_if_false (false) b85; - label b85: jump_if_false (false) b86; - label b86: jump_if_false (false) b87; - label b87: jump_if_false (false) b88; - label b88: jump_if_false (false) b89; - label b89: jump_if_false (false) b90; - label b90: jump_if_false (false) b91; - label b91: jump_if_false (false) b92; - label b92: jump_if_false (false) b93; - label b93: jump_if_false (false) b94; - label b94: jump_if_false (false) b95; - label b95: jump_if_false (false) b96; - label b96: jump_if_false (false) b97; - label b97: jump_if_false (false) b98; - label b98: jump_if_false (false) b99; - label b99: jump_if_false (false) b100; - label b100: - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/branch/br-true.mvir b/aptos-move/aptos-gas-calibration/samples_ir/branch/br-true.mvir deleted file mode 100644 index a22348f1ee979..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/branch/br-true.mvir +++ /dev/null @@ -1,1687 +0,0 @@ -module 0xcafe.BrTrue { - public entry calibrate_br_true_x10() { - label b0: jump_if (true) b1; - label b1: jump_if (true) b2; - label b2: jump_if (true) b3; - label b3: jump_if (true) b4; - label b4: jump_if (true) b5; - label b5: jump_if (true) b6; - label b6: jump_if (true) b7; - label b7: jump_if (true) b8; - label b8: jump_if (true) b9; - label b9: jump_if (true) b10; - label b10: - return; - } - - public entry calibrate_br_true_x50() { - label b0: jump_if (true) b1; - label b1: jump_if (true) b2; - label b2: jump_if (true) b3; - label b3: jump_if (true) b4; - label b4: jump_if (true) b5; - label b5: jump_if (true) b6; - label b6: jump_if (true) b7; - label b7: jump_if (true) b8; - label b8: jump_if (true) b9; - label b9: jump_if (true) b10; - label b10: jump_if (true) b11; - label b11: jump_if (true) b12; - label b12: jump_if (true) b13; - label b13: jump_if (true) b14; - label b14: jump_if (true) b15; - label b15: jump_if (true) b16; - label b16: jump_if (true) b17; - label b17: jump_if (true) b18; - label b18: jump_if (true) b19; - label b19: jump_if (true) b20; - label b20: jump_if (true) b21; - label b21: jump_if (true) b22; - label b22: jump_if (true) b23; - label b23: jump_if (true) b24; - label b24: jump_if (true) b25; - label b25: jump_if (true) b26; - label b26: jump_if (true) b27; - label b27: jump_if (true) b28; - label b28: jump_if (true) b29; - label b29: jump_if (true) b30; - label b30: jump_if (true) b31; - label b31: jump_if (true) b32; - label b32: jump_if (true) b33; - label b33: jump_if (true) b34; - label b34: jump_if (true) b35; - label b35: jump_if (true) b36; - label b36: jump_if (true) b37; - label b37: jump_if (true) b38; - label b38: jump_if (true) b39; - label b39: jump_if (true) b40; - label b40: jump_if (true) b41; - label b41: jump_if (true) b42; - label b42: jump_if (true) b43; - label b43: jump_if (true) b44; - label b44: jump_if (true) b45; - label b45: jump_if (true) b46; - label b46: jump_if (true) b47; - label b47: jump_if (true) b48; - label b48: jump_if (true) b49; - label b49: jump_if (true) b50; - label b50: - return; - } - - public entry calibrate_br_true_x100() { - label b0: jump_if (true) b1; - label b1: jump_if (true) b2; - label b2: jump_if (true) b3; - label b3: jump_if (true) b4; - label b4: jump_if (true) b5; - label b5: jump_if (true) b6; - label b6: jump_if (true) b7; - label b7: jump_if (true) b8; - label b8: jump_if (true) b9; - label b9: jump_if (true) b10; - label b10: jump_if (true) b11; - label b11: jump_if (true) b12; - label b12: jump_if (true) b13; - label b13: jump_if (true) b14; - label b14: jump_if (true) b15; - label b15: jump_if (true) b16; - label b16: jump_if (true) b17; - label b17: jump_if (true) b18; - label b18: jump_if (true) b19; - label b19: jump_if (true) b20; - label b20: jump_if (true) b21; - label b21: jump_if (true) b22; - label b22: jump_if (true) b23; - label b23: jump_if (true) b24; - label b24: jump_if (true) b25; - label b25: jump_if (true) b26; - label b26: jump_if (true) b27; - label b27: jump_if (true) b28; - label b28: jump_if (true) b29; - label b29: jump_if (true) b30; - label b30: jump_if (true) b31; - label b31: jump_if (true) b32; - label b32: jump_if (true) b33; - label b33: jump_if (true) b34; - label b34: jump_if (true) b35; - label b35: jump_if (true) b36; - label b36: jump_if (true) b37; - label b37: jump_if (true) b38; - label b38: jump_if (true) b39; - label b39: jump_if (true) b40; - label b40: jump_if (true) b41; - label b41: jump_if (true) b42; - label b42: jump_if (true) b43; - label b43: jump_if (true) b44; - label b44: jump_if (true) b45; - label b45: jump_if (true) b46; - label b46: jump_if (true) b47; - label b47: jump_if (true) b48; - label b48: jump_if (true) b49; - label b49: jump_if (true) b50; - label b50: jump_if (true) b51; - label b51: jump_if (true) b52; - label b52: jump_if (true) b53; - label b53: jump_if (true) b54; - label b54: jump_if (true) b55; - label b55: jump_if (true) b56; - label b56: jump_if (true) b57; - label b57: jump_if (true) b58; - label b58: jump_if (true) b59; - label b59: jump_if (true) b60; - label b60: jump_if (true) b61; - label b61: jump_if (true) b62; - label b62: jump_if (true) b63; - label b63: jump_if (true) b64; - label b64: jump_if (true) b65; - label b65: jump_if (true) b66; - label b66: jump_if (true) b67; - label b67: jump_if (true) b68; - label b68: jump_if (true) b69; - label b69: jump_if (true) b70; - label b70: jump_if (true) b71; - label b71: jump_if (true) b72; - label b72: jump_if (true) b73; - label b73: jump_if (true) b74; - label b74: jump_if (true) b75; - label b75: jump_if (true) b76; - label b76: jump_if (true) b77; - label b77: jump_if (true) b78; - label b78: jump_if (true) b79; - label b79: jump_if (true) b80; - label b80: jump_if (true) b81; - label b81: jump_if (true) b82; - label b82: jump_if (true) b83; - label b83: jump_if (true) b84; - label b84: jump_if (true) b85; - label b85: jump_if (true) b86; - label b86: jump_if (true) b87; - label b87: jump_if (true) b88; - label b88: jump_if (true) b89; - label b89: jump_if (true) b90; - label b90: jump_if (true) b91; - label b91: jump_if (true) b92; - label b92: jump_if (true) b93; - label b93: jump_if (true) b94; - label b94: jump_if (true) b95; - label b95: jump_if (true) b96; - label b96: jump_if (true) b97; - label b97: jump_if (true) b98; - label b98: jump_if (true) b99; - label b99: jump_if (true) b100; - label b100: - return; - } - - public entry calibrate_br_true_x500() { - label b0: jump_if (true) b1; - label b1: jump_if (true) b2; - label b2: jump_if (true) b3; - label b3: jump_if (true) b4; - label b4: jump_if (true) b5; - label b5: jump_if (true) b6; - label b6: jump_if (true) b7; - label b7: jump_if (true) b8; - label b8: jump_if (true) b9; - label b9: jump_if (true) b10; - label b10: jump_if (true) b11; - label b11: jump_if (true) b12; - label b12: jump_if (true) b13; - label b13: jump_if (true) b14; - label b14: jump_if (true) b15; - label b15: jump_if (true) b16; - label b16: jump_if (true) b17; - label b17: jump_if (true) b18; - label b18: jump_if (true) b19; - label b19: jump_if (true) b20; - label b20: jump_if (true) b21; - label b21: jump_if (true) b22; - label b22: jump_if (true) b23; - label b23: jump_if (true) b24; - label b24: jump_if (true) b25; - label b25: jump_if (true) b26; - label b26: jump_if (true) b27; - label b27: jump_if (true) b28; - label b28: jump_if (true) b29; - label b29: jump_if (true) b30; - label b30: jump_if (true) b31; - label b31: jump_if (true) b32; - label b32: jump_if (true) b33; - label b33: jump_if (true) b34; - label b34: jump_if (true) b35; - label b35: jump_if (true) b36; - label b36: jump_if (true) b37; - label b37: jump_if (true) b38; - label b38: jump_if (true) b39; - label b39: jump_if (true) b40; - label b40: jump_if (true) b41; - label b41: jump_if (true) b42; - label b42: jump_if (true) b43; - label b43: jump_if (true) b44; - label b44: jump_if (true) b45; - label b45: jump_if (true) b46; - label b46: jump_if (true) b47; - label b47: jump_if (true) b48; - label b48: jump_if (true) b49; - label b49: jump_if (true) b50; - label b50: jump_if (true) b51; - label b51: jump_if (true) b52; - label b52: jump_if (true) b53; - label b53: jump_if (true) b54; - label b54: jump_if (true) b55; - label b55: jump_if (true) b56; - label b56: jump_if (true) b57; - label b57: jump_if (true) b58; - label b58: jump_if (true) b59; - label b59: jump_if (true) b60; - label b60: jump_if (true) b61; - label b61: jump_if (true) b62; - label b62: jump_if (true) b63; - label b63: jump_if (true) b64; - label b64: jump_if (true) b65; - label b65: jump_if (true) b66; - label b66: jump_if (true) b67; - label b67: jump_if (true) b68; - label b68: jump_if (true) b69; - label b69: jump_if (true) b70; - label b70: jump_if (true) b71; - label b71: jump_if (true) b72; - label b72: jump_if (true) b73; - label b73: jump_if (true) b74; - label b74: jump_if (true) b75; - label b75: jump_if (true) b76; - label b76: jump_if (true) b77; - label b77: jump_if (true) b78; - label b78: jump_if (true) b79; - label b79: jump_if (true) b80; - label b80: jump_if (true) b81; - label b81: jump_if (true) b82; - label b82: jump_if (true) b83; - label b83: jump_if (true) b84; - label b84: jump_if (true) b85; - label b85: jump_if (true) b86; - label b86: jump_if (true) b87; - label b87: jump_if (true) b88; - label b88: jump_if (true) b89; - label b89: jump_if (true) b90; - label b90: jump_if (true) b91; - label b91: jump_if (true) b92; - label b92: jump_if (true) b93; - label b93: jump_if (true) b94; - label b94: jump_if (true) b95; - label b95: jump_if (true) b96; - label b96: jump_if (true) b97; - label b97: jump_if (true) b98; - label b98: jump_if (true) b99; - label b99: jump_if (true) b100; - label b100: jump_if (true) b101; - label b101: jump_if (true) b102; - label b102: jump_if (true) b103; - label b103: jump_if (true) b104; - label b104: jump_if (true) b105; - label b105: jump_if (true) b106; - label b106: jump_if (true) b107; - label b107: jump_if (true) b108; - label b108: jump_if (true) b109; - label b109: jump_if (true) b110; - label b110: jump_if (true) b111; - label b111: jump_if (true) b112; - label b112: jump_if (true) b113; - label b113: jump_if (true) b114; - label b114: jump_if (true) b115; - label b115: jump_if (true) b116; - label b116: jump_if (true) b117; - label b117: jump_if (true) b118; - label b118: jump_if (true) b119; - label b119: jump_if (true) b120; - label b120: jump_if (true) b121; - label b121: jump_if (true) b122; - label b122: jump_if (true) b123; - label b123: jump_if (true) b124; - label b124: jump_if (true) b125; - label b125: jump_if (true) b126; - label b126: jump_if (true) b127; - label b127: jump_if (true) b128; - label b128: jump_if (true) b129; - label b129: jump_if (true) b130; - label b130: jump_if (true) b131; - label b131: jump_if (true) b132; - label b132: jump_if (true) b133; - label b133: jump_if (true) b134; - label b134: jump_if (true) b135; - label b135: jump_if (true) b136; - label b136: jump_if (true) b137; - label b137: jump_if (true) b138; - label b138: jump_if (true) b139; - label b139: jump_if (true) b140; - label b140: jump_if (true) b141; - label b141: jump_if (true) b142; - label b142: jump_if (true) b143; - label b143: jump_if (true) b144; - label b144: jump_if (true) b145; - label b145: jump_if (true) b146; - label b146: jump_if (true) b147; - label b147: jump_if (true) b148; - label b148: jump_if (true) b149; - label b149: jump_if (true) b150; - label b150: jump_if (true) b151; - label b151: jump_if (true) b152; - label b152: jump_if (true) b153; - label b153: jump_if (true) b154; - label b154: jump_if (true) b155; - label b155: jump_if (true) b156; - label b156: jump_if (true) b157; - label b157: jump_if (true) b158; - label b158: jump_if (true) b159; - label b159: jump_if (true) b160; - label b160: jump_if (true) b161; - label b161: jump_if (true) b162; - label b162: jump_if (true) b163; - label b163: jump_if (true) b164; - label b164: jump_if (true) b165; - label b165: jump_if (true) b166; - label b166: jump_if (true) b167; - label b167: jump_if (true) b168; - label b168: jump_if (true) b169; - label b169: jump_if (true) b170; - label b170: jump_if (true) b171; - label b171: jump_if (true) b172; - label b172: jump_if (true) b173; - label b173: jump_if (true) b174; - label b174: jump_if (true) b175; - label b175: jump_if (true) b176; - label b176: jump_if (true) b177; - label b177: jump_if (true) b178; - label b178: jump_if (true) b179; - label b179: jump_if (true) b180; - label b180: jump_if (true) b181; - label b181: jump_if (true) b182; - label b182: jump_if (true) b183; - label b183: jump_if (true) b184; - label b184: jump_if (true) b185; - label b185: jump_if (true) b186; - label b186: jump_if (true) b187; - label b187: jump_if (true) b188; - label b188: jump_if (true) b189; - label b189: jump_if (true) b190; - label b190: jump_if (true) b191; - label b191: jump_if (true) b192; - label b192: jump_if (true) b193; - label b193: jump_if (true) b194; - label b194: jump_if (true) b195; - label b195: jump_if (true) b196; - label b196: jump_if (true) b197; - label b197: jump_if (true) b198; - label b198: jump_if (true) b199; - label b199: jump_if (true) b200; - label b200: jump_if (true) b201; - label b201: jump_if (true) b202; - label b202: jump_if (true) b203; - label b203: jump_if (true) b204; - label b204: jump_if (true) b205; - label b205: jump_if (true) b206; - label b206: jump_if (true) b207; - label b207: jump_if (true) b208; - label b208: jump_if (true) b209; - label b209: jump_if (true) b210; - label b210: jump_if (true) b211; - label b211: jump_if (true) b212; - label b212: jump_if (true) b213; - label b213: jump_if (true) b214; - label b214: jump_if (true) b215; - label b215: jump_if (true) b216; - label b216: jump_if (true) b217; - label b217: jump_if (true) b218; - label b218: jump_if (true) b219; - label b219: jump_if (true) b220; - label b220: jump_if (true) b221; - label b221: jump_if (true) b222; - label b222: jump_if (true) b223; - label b223: jump_if (true) b224; - label b224: jump_if (true) b225; - label b225: jump_if (true) b226; - label b226: jump_if (true) b227; - label b227: jump_if (true) b228; - label b228: jump_if (true) b229; - label b229: jump_if (true) b230; - label b230: jump_if (true) b231; - label b231: jump_if (true) b232; - label b232: jump_if (true) b233; - label b233: jump_if (true) b234; - label b234: jump_if (true) b235; - label b235: jump_if (true) b236; - label b236: jump_if (true) b237; - label b237: jump_if (true) b238; - label b238: jump_if (true) b239; - label b239: jump_if (true) b240; - label b240: jump_if (true) b241; - label b241: jump_if (true) b242; - label b242: jump_if (true) b243; - label b243: jump_if (true) b244; - label b244: jump_if (true) b245; - label b245: jump_if (true) b246; - label b246: jump_if (true) b247; - label b247: jump_if (true) b248; - label b248: jump_if (true) b249; - label b249: jump_if (true) b250; - label b250: jump_if (true) b251; - label b251: jump_if (true) b252; - label b252: jump_if (true) b253; - label b253: jump_if (true) b254; - label b254: jump_if (true) b255; - label b255: jump_if (true) b256; - label b256: jump_if (true) b257; - label b257: jump_if (true) b258; - label b258: jump_if (true) b259; - label b259: jump_if (true) b260; - label b260: jump_if (true) b261; - label b261: jump_if (true) b262; - label b262: jump_if (true) b263; - label b263: jump_if (true) b264; - label b264: jump_if (true) b265; - label b265: jump_if (true) b266; - label b266: jump_if (true) b267; - label b267: jump_if (true) b268; - label b268: jump_if (true) b269; - label b269: jump_if (true) b270; - label b270: jump_if (true) b271; - label b271: jump_if (true) b272; - label b272: jump_if (true) b273; - label b273: jump_if (true) b274; - label b274: jump_if (true) b275; - label b275: jump_if (true) b276; - label b276: jump_if (true) b277; - label b277: jump_if (true) b278; - label b278: jump_if (true) b279; - label b279: jump_if (true) b280; - label b280: jump_if (true) b281; - label b281: jump_if (true) b282; - label b282: jump_if (true) b283; - label b283: jump_if (true) b284; - label b284: jump_if (true) b285; - label b285: jump_if (true) b286; - label b286: jump_if (true) b287; - label b287: jump_if (true) b288; - label b288: jump_if (true) b289; - label b289: jump_if (true) b290; - label b290: jump_if (true) b291; - label b291: jump_if (true) b292; - label b292: jump_if (true) b293; - label b293: jump_if (true) b294; - label b294: jump_if (true) b295; - label b295: jump_if (true) b296; - label b296: jump_if (true) b297; - label b297: jump_if (true) b298; - label b298: jump_if (true) b299; - label b299: jump_if (true) b300; - label b300: jump_if (true) b301; - label b301: jump_if (true) b302; - label b302: jump_if (true) b303; - label b303: jump_if (true) b304; - label b304: jump_if (true) b305; - label b305: jump_if (true) b306; - label b306: jump_if (true) b307; - label b307: jump_if (true) b308; - label b308: jump_if (true) b309; - label b309: jump_if (true) b310; - label b310: jump_if (true) b311; - label b311: jump_if (true) b312; - label b312: jump_if (true) b313; - label b313: jump_if (true) b314; - label b314: jump_if (true) b315; - label b315: jump_if (true) b316; - label b316: jump_if (true) b317; - label b317: jump_if (true) b318; - label b318: jump_if (true) b319; - label b319: jump_if (true) b320; - label b320: jump_if (true) b321; - label b321: jump_if (true) b322; - label b322: jump_if (true) b323; - label b323: jump_if (true) b324; - label b324: jump_if (true) b325; - label b325: jump_if (true) b326; - label b326: jump_if (true) b327; - label b327: jump_if (true) b328; - label b328: jump_if (true) b329; - label b329: jump_if (true) b330; - label b330: jump_if (true) b331; - label b331: jump_if (true) b332; - label b332: jump_if (true) b333; - label b333: jump_if (true) b334; - label b334: jump_if (true) b335; - label b335: jump_if (true) b336; - label b336: jump_if (true) b337; - label b337: jump_if (true) b338; - label b338: jump_if (true) b339; - label b339: jump_if (true) b340; - label b340: jump_if (true) b341; - label b341: jump_if (true) b342; - label b342: jump_if (true) b343; - label b343: jump_if (true) b344; - label b344: jump_if (true) b345; - label b345: jump_if (true) b346; - label b346: jump_if (true) b347; - label b347: jump_if (true) b348; - label b348: jump_if (true) b349; - label b349: jump_if (true) b350; - label b350: jump_if (true) b351; - label b351: jump_if (true) b352; - label b352: jump_if (true) b353; - label b353: jump_if (true) b354; - label b354: jump_if (true) b355; - label b355: jump_if (true) b356; - label b356: jump_if (true) b357; - label b357: jump_if (true) b358; - label b358: jump_if (true) b359; - label b359: jump_if (true) b360; - label b360: jump_if (true) b361; - label b361: jump_if (true) b362; - label b362: jump_if (true) b363; - label b363: jump_if (true) b364; - label b364: jump_if (true) b365; - label b365: jump_if (true) b366; - label b366: jump_if (true) b367; - label b367: jump_if (true) b368; - label b368: jump_if (true) b369; - label b369: jump_if (true) b370; - label b370: jump_if (true) b371; - label b371: jump_if (true) b372; - label b372: jump_if (true) b373; - label b373: jump_if (true) b374; - label b374: jump_if (true) b375; - label b375: jump_if (true) b376; - label b376: jump_if (true) b377; - label b377: jump_if (true) b378; - label b378: jump_if (true) b379; - label b379: jump_if (true) b380; - label b380: jump_if (true) b381; - label b381: jump_if (true) b382; - label b382: jump_if (true) b383; - label b383: jump_if (true) b384; - label b384: jump_if (true) b385; - label b385: jump_if (true) b386; - label b386: jump_if (true) b387; - label b387: jump_if (true) b388; - label b388: jump_if (true) b389; - label b389: jump_if (true) b390; - label b390: jump_if (true) b391; - label b391: jump_if (true) b392; - label b392: jump_if (true) b393; - label b393: jump_if (true) b394; - label b394: jump_if (true) b395; - label b395: jump_if (true) b396; - label b396: jump_if (true) b397; - label b397: jump_if (true) b398; - label b398: jump_if (true) b399; - label b399: jump_if (true) b400; - label b400: jump_if (true) b401; - label b401: jump_if (true) b402; - label b402: jump_if (true) b403; - label b403: jump_if (true) b404; - label b404: jump_if (true) b405; - label b405: jump_if (true) b406; - label b406: jump_if (true) b407; - label b407: jump_if (true) b408; - label b408: jump_if (true) b409; - label b409: jump_if (true) b410; - label b410: jump_if (true) b411; - label b411: jump_if (true) b412; - label b412: jump_if (true) b413; - label b413: jump_if (true) b414; - label b414: jump_if (true) b415; - label b415: jump_if (true) b416; - label b416: jump_if (true) b417; - label b417: jump_if (true) b418; - label b418: jump_if (true) b419; - label b419: jump_if (true) b420; - label b420: jump_if (true) b421; - label b421: jump_if (true) b422; - label b422: jump_if (true) b423; - label b423: jump_if (true) b424; - label b424: jump_if (true) b425; - label b425: jump_if (true) b426; - label b426: jump_if (true) b427; - label b427: jump_if (true) b428; - label b428: jump_if (true) b429; - label b429: jump_if (true) b430; - label b430: jump_if (true) b431; - label b431: jump_if (true) b432; - label b432: jump_if (true) b433; - label b433: jump_if (true) b434; - label b434: jump_if (true) b435; - label b435: jump_if (true) b436; - label b436: jump_if (true) b437; - label b437: jump_if (true) b438; - label b438: jump_if (true) b439; - label b439: jump_if (true) b440; - label b440: jump_if (true) b441; - label b441: jump_if (true) b442; - label b442: jump_if (true) b443; - label b443: jump_if (true) b444; - label b444: jump_if (true) b445; - label b445: jump_if (true) b446; - label b446: jump_if (true) b447; - label b447: jump_if (true) b448; - label b448: jump_if (true) b449; - label b449: jump_if (true) b450; - label b450: jump_if (true) b451; - label b451: jump_if (true) b452; - label b452: jump_if (true) b453; - label b453: jump_if (true) b454; - label b454: jump_if (true) b455; - label b455: jump_if (true) b456; - label b456: jump_if (true) b457; - label b457: jump_if (true) b458; - label b458: jump_if (true) b459; - label b459: jump_if (true) b460; - label b460: jump_if (true) b461; - label b461: jump_if (true) b462; - label b462: jump_if (true) b463; - label b463: jump_if (true) b464; - label b464: jump_if (true) b465; - label b465: jump_if (true) b466; - label b466: jump_if (true) b467; - label b467: jump_if (true) b468; - label b468: jump_if (true) b469; - label b469: jump_if (true) b470; - label b470: jump_if (true) b471; - label b471: jump_if (true) b472; - label b472: jump_if (true) b473; - label b473: jump_if (true) b474; - label b474: jump_if (true) b475; - label b475: jump_if (true) b476; - label b476: jump_if (true) b477; - label b477: jump_if (true) b478; - label b478: jump_if (true) b479; - label b479: jump_if (true) b480; - label b480: jump_if (true) b481; - label b481: jump_if (true) b482; - label b482: jump_if (true) b483; - label b483: jump_if (true) b484; - label b484: jump_if (true) b485; - label b485: jump_if (true) b486; - label b486: jump_if (true) b487; - label b487: jump_if (true) b488; - label b488: jump_if (true) b489; - label b489: jump_if (true) b490; - label b490: jump_if (true) b491; - label b491: jump_if (true) b492; - label b492: jump_if (true) b493; - label b493: jump_if (true) b494; - label b494: jump_if (true) b495; - label b495: jump_if (true) b496; - label b496: jump_if (true) b497; - label b497: jump_if (true) b498; - label b498: jump_if (true) b499; - label b499: jump_if (true) b500; - label b500: - return; - } - - public entry calibrate_br_true_x1000() { - label b0: jump_if (true) b1; - label b1: jump_if (true) b2; - label b2: jump_if (true) b3; - label b3: jump_if (true) b4; - label b4: jump_if (true) b5; - label b5: jump_if (true) b6; - label b6: jump_if (true) b7; - label b7: jump_if (true) b8; - label b8: jump_if (true) b9; - label b9: jump_if (true) b10; - label b10: jump_if (true) b11; - label b11: jump_if (true) b12; - label b12: jump_if (true) b13; - label b13: jump_if (true) b14; - label b14: jump_if (true) b15; - label b15: jump_if (true) b16; - label b16: jump_if (true) b17; - label b17: jump_if (true) b18; - label b18: jump_if (true) b19; - label b19: jump_if (true) b20; - label b20: jump_if (true) b21; - label b21: jump_if (true) b22; - label b22: jump_if (true) b23; - label b23: jump_if (true) b24; - label b24: jump_if (true) b25; - label b25: jump_if (true) b26; - label b26: jump_if (true) b27; - label b27: jump_if (true) b28; - label b28: jump_if (true) b29; - label b29: jump_if (true) b30; - label b30: jump_if (true) b31; - label b31: jump_if (true) b32; - label b32: jump_if (true) b33; - label b33: jump_if (true) b34; - label b34: jump_if (true) b35; - label b35: jump_if (true) b36; - label b36: jump_if (true) b37; - label b37: jump_if (true) b38; - label b38: jump_if (true) b39; - label b39: jump_if (true) b40; - label b40: jump_if (true) b41; - label b41: jump_if (true) b42; - label b42: jump_if (true) b43; - label b43: jump_if (true) b44; - label b44: jump_if (true) b45; - label b45: jump_if (true) b46; - label b46: jump_if (true) b47; - label b47: jump_if (true) b48; - label b48: jump_if (true) b49; - label b49: jump_if (true) b50; - label b50: jump_if (true) b51; - label b51: jump_if (true) b52; - label b52: jump_if (true) b53; - label b53: jump_if (true) b54; - label b54: jump_if (true) b55; - label b55: jump_if (true) b56; - label b56: jump_if (true) b57; - label b57: jump_if (true) b58; - label b58: jump_if (true) b59; - label b59: jump_if (true) b60; - label b60: jump_if (true) b61; - label b61: jump_if (true) b62; - label b62: jump_if (true) b63; - label b63: jump_if (true) b64; - label b64: jump_if (true) b65; - label b65: jump_if (true) b66; - label b66: jump_if (true) b67; - label b67: jump_if (true) b68; - label b68: jump_if (true) b69; - label b69: jump_if (true) b70; - label b70: jump_if (true) b71; - label b71: jump_if (true) b72; - label b72: jump_if (true) b73; - label b73: jump_if (true) b74; - label b74: jump_if (true) b75; - label b75: jump_if (true) b76; - label b76: jump_if (true) b77; - label b77: jump_if (true) b78; - label b78: jump_if (true) b79; - label b79: jump_if (true) b80; - label b80: jump_if (true) b81; - label b81: jump_if (true) b82; - label b82: jump_if (true) b83; - label b83: jump_if (true) b84; - label b84: jump_if (true) b85; - label b85: jump_if (true) b86; - label b86: jump_if (true) b87; - label b87: jump_if (true) b88; - label b88: jump_if (true) b89; - label b89: jump_if (true) b90; - label b90: jump_if (true) b91; - label b91: jump_if (true) b92; - label b92: jump_if (true) b93; - label b93: jump_if (true) b94; - label b94: jump_if (true) b95; - label b95: jump_if (true) b96; - label b96: jump_if (true) b97; - label b97: jump_if (true) b98; - label b98: jump_if (true) b99; - label b99: jump_if (true) b100; - label b100: jump_if (true) b101; - label b101: jump_if (true) b102; - label b102: jump_if (true) b103; - label b103: jump_if (true) b104; - label b104: jump_if (true) b105; - label b105: jump_if (true) b106; - label b106: jump_if (true) b107; - label b107: jump_if (true) b108; - label b108: jump_if (true) b109; - label b109: jump_if (true) b110; - label b110: jump_if (true) b111; - label b111: jump_if (true) b112; - label b112: jump_if (true) b113; - label b113: jump_if (true) b114; - label b114: jump_if (true) b115; - label b115: jump_if (true) b116; - label b116: jump_if (true) b117; - label b117: jump_if (true) b118; - label b118: jump_if (true) b119; - label b119: jump_if (true) b120; - label b120: jump_if (true) b121; - label b121: jump_if (true) b122; - label b122: jump_if (true) b123; - label b123: jump_if (true) b124; - label b124: jump_if (true) b125; - label b125: jump_if (true) b126; - label b126: jump_if (true) b127; - label b127: jump_if (true) b128; - label b128: jump_if (true) b129; - label b129: jump_if (true) b130; - label b130: jump_if (true) b131; - label b131: jump_if (true) b132; - label b132: jump_if (true) b133; - label b133: jump_if (true) b134; - label b134: jump_if (true) b135; - label b135: jump_if (true) b136; - label b136: jump_if (true) b137; - label b137: jump_if (true) b138; - label b138: jump_if (true) b139; - label b139: jump_if (true) b140; - label b140: jump_if (true) b141; - label b141: jump_if (true) b142; - label b142: jump_if (true) b143; - label b143: jump_if (true) b144; - label b144: jump_if (true) b145; - label b145: jump_if (true) b146; - label b146: jump_if (true) b147; - label b147: jump_if (true) b148; - label b148: jump_if (true) b149; - label b149: jump_if (true) b150; - label b150: jump_if (true) b151; - label b151: jump_if (true) b152; - label b152: jump_if (true) b153; - label b153: jump_if (true) b154; - label b154: jump_if (true) b155; - label b155: jump_if (true) b156; - label b156: jump_if (true) b157; - label b157: jump_if (true) b158; - label b158: jump_if (true) b159; - label b159: jump_if (true) b160; - label b160: jump_if (true) b161; - label b161: jump_if (true) b162; - label b162: jump_if (true) b163; - label b163: jump_if (true) b164; - label b164: jump_if (true) b165; - label b165: jump_if (true) b166; - label b166: jump_if (true) b167; - label b167: jump_if (true) b168; - label b168: jump_if (true) b169; - label b169: jump_if (true) b170; - label b170: jump_if (true) b171; - label b171: jump_if (true) b172; - label b172: jump_if (true) b173; - label b173: jump_if (true) b174; - label b174: jump_if (true) b175; - label b175: jump_if (true) b176; - label b176: jump_if (true) b177; - label b177: jump_if (true) b178; - label b178: jump_if (true) b179; - label b179: jump_if (true) b180; - label b180: jump_if (true) b181; - label b181: jump_if (true) b182; - label b182: jump_if (true) b183; - label b183: jump_if (true) b184; - label b184: jump_if (true) b185; - label b185: jump_if (true) b186; - label b186: jump_if (true) b187; - label b187: jump_if (true) b188; - label b188: jump_if (true) b189; - label b189: jump_if (true) b190; - label b190: jump_if (true) b191; - label b191: jump_if (true) b192; - label b192: jump_if (true) b193; - label b193: jump_if (true) b194; - label b194: jump_if (true) b195; - label b195: jump_if (true) b196; - label b196: jump_if (true) b197; - label b197: jump_if (true) b198; - label b198: jump_if (true) b199; - label b199: jump_if (true) b200; - label b200: jump_if (true) b201; - label b201: jump_if (true) b202; - label b202: jump_if (true) b203; - label b203: jump_if (true) b204; - label b204: jump_if (true) b205; - label b205: jump_if (true) b206; - label b206: jump_if (true) b207; - label b207: jump_if (true) b208; - label b208: jump_if (true) b209; - label b209: jump_if (true) b210; - label b210: jump_if (true) b211; - label b211: jump_if (true) b212; - label b212: jump_if (true) b213; - label b213: jump_if (true) b214; - label b214: jump_if (true) b215; - label b215: jump_if (true) b216; - label b216: jump_if (true) b217; - label b217: jump_if (true) b218; - label b218: jump_if (true) b219; - label b219: jump_if (true) b220; - label b220: jump_if (true) b221; - label b221: jump_if (true) b222; - label b222: jump_if (true) b223; - label b223: jump_if (true) b224; - label b224: jump_if (true) b225; - label b225: jump_if (true) b226; - label b226: jump_if (true) b227; - label b227: jump_if (true) b228; - label b228: jump_if (true) b229; - label b229: jump_if (true) b230; - label b230: jump_if (true) b231; - label b231: jump_if (true) b232; - label b232: jump_if (true) b233; - label b233: jump_if (true) b234; - label b234: jump_if (true) b235; - label b235: jump_if (true) b236; - label b236: jump_if (true) b237; - label b237: jump_if (true) b238; - label b238: jump_if (true) b239; - label b239: jump_if (true) b240; - label b240: jump_if (true) b241; - label b241: jump_if (true) b242; - label b242: jump_if (true) b243; - label b243: jump_if (true) b244; - label b244: jump_if (true) b245; - label b245: jump_if (true) b246; - label b246: jump_if (true) b247; - label b247: jump_if (true) b248; - label b248: jump_if (true) b249; - label b249: jump_if (true) b250; - label b250: jump_if (true) b251; - label b251: jump_if (true) b252; - label b252: jump_if (true) b253; - label b253: jump_if (true) b254; - label b254: jump_if (true) b255; - label b255: jump_if (true) b256; - label b256: jump_if (true) b257; - label b257: jump_if (true) b258; - label b258: jump_if (true) b259; - label b259: jump_if (true) b260; - label b260: jump_if (true) b261; - label b261: jump_if (true) b262; - label b262: jump_if (true) b263; - label b263: jump_if (true) b264; - label b264: jump_if (true) b265; - label b265: jump_if (true) b266; - label b266: jump_if (true) b267; - label b267: jump_if (true) b268; - label b268: jump_if (true) b269; - label b269: jump_if (true) b270; - label b270: jump_if (true) b271; - label b271: jump_if (true) b272; - label b272: jump_if (true) b273; - label b273: jump_if (true) b274; - label b274: jump_if (true) b275; - label b275: jump_if (true) b276; - label b276: jump_if (true) b277; - label b277: jump_if (true) b278; - label b278: jump_if (true) b279; - label b279: jump_if (true) b280; - label b280: jump_if (true) b281; - label b281: jump_if (true) b282; - label b282: jump_if (true) b283; - label b283: jump_if (true) b284; - label b284: jump_if (true) b285; - label b285: jump_if (true) b286; - label b286: jump_if (true) b287; - label b287: jump_if (true) b288; - label b288: jump_if (true) b289; - label b289: jump_if (true) b290; - label b290: jump_if (true) b291; - label b291: jump_if (true) b292; - label b292: jump_if (true) b293; - label b293: jump_if (true) b294; - label b294: jump_if (true) b295; - label b295: jump_if (true) b296; - label b296: jump_if (true) b297; - label b297: jump_if (true) b298; - label b298: jump_if (true) b299; - label b299: jump_if (true) b300; - label b300: jump_if (true) b301; - label b301: jump_if (true) b302; - label b302: jump_if (true) b303; - label b303: jump_if (true) b304; - label b304: jump_if (true) b305; - label b305: jump_if (true) b306; - label b306: jump_if (true) b307; - label b307: jump_if (true) b308; - label b308: jump_if (true) b309; - label b309: jump_if (true) b310; - label b310: jump_if (true) b311; - label b311: jump_if (true) b312; - label b312: jump_if (true) b313; - label b313: jump_if (true) b314; - label b314: jump_if (true) b315; - label b315: jump_if (true) b316; - label b316: jump_if (true) b317; - label b317: jump_if (true) b318; - label b318: jump_if (true) b319; - label b319: jump_if (true) b320; - label b320: jump_if (true) b321; - label b321: jump_if (true) b322; - label b322: jump_if (true) b323; - label b323: jump_if (true) b324; - label b324: jump_if (true) b325; - label b325: jump_if (true) b326; - label b326: jump_if (true) b327; - label b327: jump_if (true) b328; - label b328: jump_if (true) b329; - label b329: jump_if (true) b330; - label b330: jump_if (true) b331; - label b331: jump_if (true) b332; - label b332: jump_if (true) b333; - label b333: jump_if (true) b334; - label b334: jump_if (true) b335; - label b335: jump_if (true) b336; - label b336: jump_if (true) b337; - label b337: jump_if (true) b338; - label b338: jump_if (true) b339; - label b339: jump_if (true) b340; - label b340: jump_if (true) b341; - label b341: jump_if (true) b342; - label b342: jump_if (true) b343; - label b343: jump_if (true) b344; - label b344: jump_if (true) b345; - label b345: jump_if (true) b346; - label b346: jump_if (true) b347; - label b347: jump_if (true) b348; - label b348: jump_if (true) b349; - label b349: jump_if (true) b350; - label b350: jump_if (true) b351; - label b351: jump_if (true) b352; - label b352: jump_if (true) b353; - label b353: jump_if (true) b354; - label b354: jump_if (true) b355; - label b355: jump_if (true) b356; - label b356: jump_if (true) b357; - label b357: jump_if (true) b358; - label b358: jump_if (true) b359; - label b359: jump_if (true) b360; - label b360: jump_if (true) b361; - label b361: jump_if (true) b362; - label b362: jump_if (true) b363; - label b363: jump_if (true) b364; - label b364: jump_if (true) b365; - label b365: jump_if (true) b366; - label b366: jump_if (true) b367; - label b367: jump_if (true) b368; - label b368: jump_if (true) b369; - label b369: jump_if (true) b370; - label b370: jump_if (true) b371; - label b371: jump_if (true) b372; - label b372: jump_if (true) b373; - label b373: jump_if (true) b374; - label b374: jump_if (true) b375; - label b375: jump_if (true) b376; - label b376: jump_if (true) b377; - label b377: jump_if (true) b378; - label b378: jump_if (true) b379; - label b379: jump_if (true) b380; - label b380: jump_if (true) b381; - label b381: jump_if (true) b382; - label b382: jump_if (true) b383; - label b383: jump_if (true) b384; - label b384: jump_if (true) b385; - label b385: jump_if (true) b386; - label b386: jump_if (true) b387; - label b387: jump_if (true) b388; - label b388: jump_if (true) b389; - label b389: jump_if (true) b390; - label b390: jump_if (true) b391; - label b391: jump_if (true) b392; - label b392: jump_if (true) b393; - label b393: jump_if (true) b394; - label b394: jump_if (true) b395; - label b395: jump_if (true) b396; - label b396: jump_if (true) b397; - label b397: jump_if (true) b398; - label b398: jump_if (true) b399; - label b399: jump_if (true) b400; - label b400: jump_if (true) b401; - label b401: jump_if (true) b402; - label b402: jump_if (true) b403; - label b403: jump_if (true) b404; - label b404: jump_if (true) b405; - label b405: jump_if (true) b406; - label b406: jump_if (true) b407; - label b407: jump_if (true) b408; - label b408: jump_if (true) b409; - label b409: jump_if (true) b410; - label b410: jump_if (true) b411; - label b411: jump_if (true) b412; - label b412: jump_if (true) b413; - label b413: jump_if (true) b414; - label b414: jump_if (true) b415; - label b415: jump_if (true) b416; - label b416: jump_if (true) b417; - label b417: jump_if (true) b418; - label b418: jump_if (true) b419; - label b419: jump_if (true) b420; - label b420: jump_if (true) b421; - label b421: jump_if (true) b422; - label b422: jump_if (true) b423; - label b423: jump_if (true) b424; - label b424: jump_if (true) b425; - label b425: jump_if (true) b426; - label b426: jump_if (true) b427; - label b427: jump_if (true) b428; - label b428: jump_if (true) b429; - label b429: jump_if (true) b430; - label b430: jump_if (true) b431; - label b431: jump_if (true) b432; - label b432: jump_if (true) b433; - label b433: jump_if (true) b434; - label b434: jump_if (true) b435; - label b435: jump_if (true) b436; - label b436: jump_if (true) b437; - label b437: jump_if (true) b438; - label b438: jump_if (true) b439; - label b439: jump_if (true) b440; - label b440: jump_if (true) b441; - label b441: jump_if (true) b442; - label b442: jump_if (true) b443; - label b443: jump_if (true) b444; - label b444: jump_if (true) b445; - label b445: jump_if (true) b446; - label b446: jump_if (true) b447; - label b447: jump_if (true) b448; - label b448: jump_if (true) b449; - label b449: jump_if (true) b450; - label b450: jump_if (true) b451; - label b451: jump_if (true) b452; - label b452: jump_if (true) b453; - label b453: jump_if (true) b454; - label b454: jump_if (true) b455; - label b455: jump_if (true) b456; - label b456: jump_if (true) b457; - label b457: jump_if (true) b458; - label b458: jump_if (true) b459; - label b459: jump_if (true) b460; - label b460: jump_if (true) b461; - label b461: jump_if (true) b462; - label b462: jump_if (true) b463; - label b463: jump_if (true) b464; - label b464: jump_if (true) b465; - label b465: jump_if (true) b466; - label b466: jump_if (true) b467; - label b467: jump_if (true) b468; - label b468: jump_if (true) b469; - label b469: jump_if (true) b470; - label b470: jump_if (true) b471; - label b471: jump_if (true) b472; - label b472: jump_if (true) b473; - label b473: jump_if (true) b474; - label b474: jump_if (true) b475; - label b475: jump_if (true) b476; - label b476: jump_if (true) b477; - label b477: jump_if (true) b478; - label b478: jump_if (true) b479; - label b479: jump_if (true) b480; - label b480: jump_if (true) b481; - label b481: jump_if (true) b482; - label b482: jump_if (true) b483; - label b483: jump_if (true) b484; - label b484: jump_if (true) b485; - label b485: jump_if (true) b486; - label b486: jump_if (true) b487; - label b487: jump_if (true) b488; - label b488: jump_if (true) b489; - label b489: jump_if (true) b490; - label b490: jump_if (true) b491; - label b491: jump_if (true) b492; - label b492: jump_if (true) b493; - label b493: jump_if (true) b494; - label b494: jump_if (true) b495; - label b495: jump_if (true) b496; - label b496: jump_if (true) b497; - label b497: jump_if (true) b498; - label b498: jump_if (true) b499; - label b499: jump_if (true) b500; - label b500: jump_if (true) b501; - label b501: jump_if (true) b502; - label b502: jump_if (true) b503; - label b503: jump_if (true) b504; - label b504: jump_if (true) b505; - label b505: jump_if (true) b506; - label b506: jump_if (true) b507; - label b507: jump_if (true) b508; - label b508: jump_if (true) b509; - label b509: jump_if (true) b510; - label b510: jump_if (true) b511; - label b511: jump_if (true) b512; - label b512: jump_if (true) b513; - label b513: jump_if (true) b514; - label b514: jump_if (true) b515; - label b515: jump_if (true) b516; - label b516: jump_if (true) b517; - label b517: jump_if (true) b518; - label b518: jump_if (true) b519; - label b519: jump_if (true) b520; - label b520: jump_if (true) b521; - label b521: jump_if (true) b522; - label b522: jump_if (true) b523; - label b523: jump_if (true) b524; - label b524: jump_if (true) b525; - label b525: jump_if (true) b526; - label b526: jump_if (true) b527; - label b527: jump_if (true) b528; - label b528: jump_if (true) b529; - label b529: jump_if (true) b530; - label b530: jump_if (true) b531; - label b531: jump_if (true) b532; - label b532: jump_if (true) b533; - label b533: jump_if (true) b534; - label b534: jump_if (true) b535; - label b535: jump_if (true) b536; - label b536: jump_if (true) b537; - label b537: jump_if (true) b538; - label b538: jump_if (true) b539; - label b539: jump_if (true) b540; - label b540: jump_if (true) b541; - label b541: jump_if (true) b542; - label b542: jump_if (true) b543; - label b543: jump_if (true) b544; - label b544: jump_if (true) b545; - label b545: jump_if (true) b546; - label b546: jump_if (true) b547; - label b547: jump_if (true) b548; - label b548: jump_if (true) b549; - label b549: jump_if (true) b550; - label b550: jump_if (true) b551; - label b551: jump_if (true) b552; - label b552: jump_if (true) b553; - label b553: jump_if (true) b554; - label b554: jump_if (true) b555; - label b555: jump_if (true) b556; - label b556: jump_if (true) b557; - label b557: jump_if (true) b558; - label b558: jump_if (true) b559; - label b559: jump_if (true) b560; - label b560: jump_if (true) b561; - label b561: jump_if (true) b562; - label b562: jump_if (true) b563; - label b563: jump_if (true) b564; - label b564: jump_if (true) b565; - label b565: jump_if (true) b566; - label b566: jump_if (true) b567; - label b567: jump_if (true) b568; - label b568: jump_if (true) b569; - label b569: jump_if (true) b570; - label b570: jump_if (true) b571; - label b571: jump_if (true) b572; - label b572: jump_if (true) b573; - label b573: jump_if (true) b574; - label b574: jump_if (true) b575; - label b575: jump_if (true) b576; - label b576: jump_if (true) b577; - label b577: jump_if (true) b578; - label b578: jump_if (true) b579; - label b579: jump_if (true) b580; - label b580: jump_if (true) b581; - label b581: jump_if (true) b582; - label b582: jump_if (true) b583; - label b583: jump_if (true) b584; - label b584: jump_if (true) b585; - label b585: jump_if (true) b586; - label b586: jump_if (true) b587; - label b587: jump_if (true) b588; - label b588: jump_if (true) b589; - label b589: jump_if (true) b590; - label b590: jump_if (true) b591; - label b591: jump_if (true) b592; - label b592: jump_if (true) b593; - label b593: jump_if (true) b594; - label b594: jump_if (true) b595; - label b595: jump_if (true) b596; - label b596: jump_if (true) b597; - label b597: jump_if (true) b598; - label b598: jump_if (true) b599; - label b599: jump_if (true) b600; - label b600: jump_if (true) b601; - label b601: jump_if (true) b602; - label b602: jump_if (true) b603; - label b603: jump_if (true) b604; - label b604: jump_if (true) b605; - label b605: jump_if (true) b606; - label b606: jump_if (true) b607; - label b607: jump_if (true) b608; - label b608: jump_if (true) b609; - label b609: jump_if (true) b610; - label b610: jump_if (true) b611; - label b611: jump_if (true) b612; - label b612: jump_if (true) b613; - label b613: jump_if (true) b614; - label b614: jump_if (true) b615; - label b615: jump_if (true) b616; - label b616: jump_if (true) b617; - label b617: jump_if (true) b618; - label b618: jump_if (true) b619; - label b619: jump_if (true) b620; - label b620: jump_if (true) b621; - label b621: jump_if (true) b622; - label b622: jump_if (true) b623; - label b623: jump_if (true) b624; - label b624: jump_if (true) b625; - label b625: jump_if (true) b626; - label b626: jump_if (true) b627; - label b627: jump_if (true) b628; - label b628: jump_if (true) b629; - label b629: jump_if (true) b630; - label b630: jump_if (true) b631; - label b631: jump_if (true) b632; - label b632: jump_if (true) b633; - label b633: jump_if (true) b634; - label b634: jump_if (true) b635; - label b635: jump_if (true) b636; - label b636: jump_if (true) b637; - label b637: jump_if (true) b638; - label b638: jump_if (true) b639; - label b639: jump_if (true) b640; - label b640: jump_if (true) b641; - label b641: jump_if (true) b642; - label b642: jump_if (true) b643; - label b643: jump_if (true) b644; - label b644: jump_if (true) b645; - label b645: jump_if (true) b646; - label b646: jump_if (true) b647; - label b647: jump_if (true) b648; - label b648: jump_if (true) b649; - label b649: jump_if (true) b650; - label b650: jump_if (true) b651; - label b651: jump_if (true) b652; - label b652: jump_if (true) b653; - label b653: jump_if (true) b654; - label b654: jump_if (true) b655; - label b655: jump_if (true) b656; - label b656: jump_if (true) b657; - label b657: jump_if (true) b658; - label b658: jump_if (true) b659; - label b659: jump_if (true) b660; - label b660: jump_if (true) b661; - label b661: jump_if (true) b662; - label b662: jump_if (true) b663; - label b663: jump_if (true) b664; - label b664: jump_if (true) b665; - label b665: jump_if (true) b666; - label b666: jump_if (true) b667; - label b667: jump_if (true) b668; - label b668: jump_if (true) b669; - label b669: jump_if (true) b670; - label b670: jump_if (true) b671; - label b671: jump_if (true) b672; - label b672: jump_if (true) b673; - label b673: jump_if (true) b674; - label b674: jump_if (true) b675; - label b675: jump_if (true) b676; - label b676: jump_if (true) b677; - label b677: jump_if (true) b678; - label b678: jump_if (true) b679; - label b679: jump_if (true) b680; - label b680: jump_if (true) b681; - label b681: jump_if (true) b682; - label b682: jump_if (true) b683; - label b683: jump_if (true) b684; - label b684: jump_if (true) b685; - label b685: jump_if (true) b686; - label b686: jump_if (true) b687; - label b687: jump_if (true) b688; - label b688: jump_if (true) b689; - label b689: jump_if (true) b690; - label b690: jump_if (true) b691; - label b691: jump_if (true) b692; - label b692: jump_if (true) b693; - label b693: jump_if (true) b694; - label b694: jump_if (true) b695; - label b695: jump_if (true) b696; - label b696: jump_if (true) b697; - label b697: jump_if (true) b698; - label b698: jump_if (true) b699; - label b699: jump_if (true) b700; - label b700: jump_if (true) b701; - label b701: jump_if (true) b702; - label b702: jump_if (true) b703; - label b703: jump_if (true) b704; - label b704: jump_if (true) b705; - label b705: jump_if (true) b706; - label b706: jump_if (true) b707; - label b707: jump_if (true) b708; - label b708: jump_if (true) b709; - label b709: jump_if (true) b710; - label b710: jump_if (true) b711; - label b711: jump_if (true) b712; - label b712: jump_if (true) b713; - label b713: jump_if (true) b714; - label b714: jump_if (true) b715; - label b715: jump_if (true) b716; - label b716: jump_if (true) b717; - label b717: jump_if (true) b718; - label b718: jump_if (true) b719; - label b719: jump_if (true) b720; - label b720: jump_if (true) b721; - label b721: jump_if (true) b722; - label b722: jump_if (true) b723; - label b723: jump_if (true) b724; - label b724: jump_if (true) b725; - label b725: jump_if (true) b726; - label b726: jump_if (true) b727; - label b727: jump_if (true) b728; - label b728: jump_if (true) b729; - label b729: jump_if (true) b730; - label b730: jump_if (true) b731; - label b731: jump_if (true) b732; - label b732: jump_if (true) b733; - label b733: jump_if (true) b734; - label b734: jump_if (true) b735; - label b735: jump_if (true) b736; - label b736: jump_if (true) b737; - label b737: jump_if (true) b738; - label b738: jump_if (true) b739; - label b739: jump_if (true) b740; - label b740: jump_if (true) b741; - label b741: jump_if (true) b742; - label b742: jump_if (true) b743; - label b743: jump_if (true) b744; - label b744: jump_if (true) b745; - label b745: jump_if (true) b746; - label b746: jump_if (true) b747; - label b747: jump_if (true) b748; - label b748: jump_if (true) b749; - label b749: jump_if (true) b750; - label b750: jump_if (true) b751; - label b751: jump_if (true) b752; - label b752: jump_if (true) b753; - label b753: jump_if (true) b754; - label b754: jump_if (true) b755; - label b755: jump_if (true) b756; - label b756: jump_if (true) b757; - label b757: jump_if (true) b758; - label b758: jump_if (true) b759; - label b759: jump_if (true) b760; - label b760: jump_if (true) b761; - label b761: jump_if (true) b762; - label b762: jump_if (true) b763; - label b763: jump_if (true) b764; - label b764: jump_if (true) b765; - label b765: jump_if (true) b766; - label b766: jump_if (true) b767; - label b767: jump_if (true) b768; - label b768: jump_if (true) b769; - label b769: jump_if (true) b770; - label b770: jump_if (true) b771; - label b771: jump_if (true) b772; - label b772: jump_if (true) b773; - label b773: jump_if (true) b774; - label b774: jump_if (true) b775; - label b775: jump_if (true) b776; - label b776: jump_if (true) b777; - label b777: jump_if (true) b778; - label b778: jump_if (true) b779; - label b779: jump_if (true) b780; - label b780: jump_if (true) b781; - label b781: jump_if (true) b782; - label b782: jump_if (true) b783; - label b783: jump_if (true) b784; - label b784: jump_if (true) b785; - label b785: jump_if (true) b786; - label b786: jump_if (true) b787; - label b787: jump_if (true) b788; - label b788: jump_if (true) b789; - label b789: jump_if (true) b790; - label b790: jump_if (true) b791; - label b791: jump_if (true) b792; - label b792: jump_if (true) b793; - label b793: jump_if (true) b794; - label b794: jump_if (true) b795; - label b795: jump_if (true) b796; - label b796: jump_if (true) b797; - label b797: jump_if (true) b798; - label b798: jump_if (true) b799; - label b799: jump_if (true) b800; - label b800: jump_if (true) b801; - label b801: jump_if (true) b802; - label b802: jump_if (true) b803; - label b803: jump_if (true) b804; - label b804: jump_if (true) b805; - label b805: jump_if (true) b806; - label b806: jump_if (true) b807; - label b807: jump_if (true) b808; - label b808: jump_if (true) b809; - label b809: jump_if (true) b810; - label b810: jump_if (true) b811; - label b811: jump_if (true) b812; - label b812: jump_if (true) b813; - label b813: jump_if (true) b814; - label b814: jump_if (true) b815; - label b815: jump_if (true) b816; - label b816: jump_if (true) b817; - label b817: jump_if (true) b818; - label b818: jump_if (true) b819; - label b819: jump_if (true) b820; - label b820: jump_if (true) b821; - label b821: jump_if (true) b822; - label b822: jump_if (true) b823; - label b823: jump_if (true) b824; - label b824: jump_if (true) b825; - label b825: jump_if (true) b826; - label b826: jump_if (true) b827; - label b827: jump_if (true) b828; - label b828: jump_if (true) b829; - label b829: jump_if (true) b830; - label b830: jump_if (true) b831; - label b831: jump_if (true) b832; - label b832: jump_if (true) b833; - label b833: jump_if (true) b834; - label b834: jump_if (true) b835; - label b835: jump_if (true) b836; - label b836: jump_if (true) b837; - label b837: jump_if (true) b838; - label b838: jump_if (true) b839; - label b839: jump_if (true) b840; - label b840: jump_if (true) b841; - label b841: jump_if (true) b842; - label b842: jump_if (true) b843; - label b843: jump_if (true) b844; - label b844: jump_if (true) b845; - label b845: jump_if (true) b846; - label b846: jump_if (true) b847; - label b847: jump_if (true) b848; - label b848: jump_if (true) b849; - label b849: jump_if (true) b850; - label b850: jump_if (true) b851; - label b851: jump_if (true) b852; - label b852: jump_if (true) b853; - label b853: jump_if (true) b854; - label b854: jump_if (true) b855; - label b855: jump_if (true) b856; - label b856: jump_if (true) b857; - label b857: jump_if (true) b858; - label b858: jump_if (true) b859; - label b859: jump_if (true) b860; - label b860: jump_if (true) b861; - label b861: jump_if (true) b862; - label b862: jump_if (true) b863; - label b863: jump_if (true) b864; - label b864: jump_if (true) b865; - label b865: jump_if (true) b866; - label b866: jump_if (true) b867; - label b867: jump_if (true) b868; - label b868: jump_if (true) b869; - label b869: jump_if (true) b870; - label b870: jump_if (true) b871; - label b871: jump_if (true) b872; - label b872: jump_if (true) b873; - label b873: jump_if (true) b874; - label b874: jump_if (true) b875; - label b875: jump_if (true) b876; - label b876: jump_if (true) b877; - label b877: jump_if (true) b878; - label b878: jump_if (true) b879; - label b879: jump_if (true) b880; - label b880: jump_if (true) b881; - label b881: jump_if (true) b882; - label b882: jump_if (true) b883; - label b883: jump_if (true) b884; - label b884: jump_if (true) b885; - label b885: jump_if (true) b886; - label b886: jump_if (true) b887; - label b887: jump_if (true) b888; - label b888: jump_if (true) b889; - label b889: jump_if (true) b890; - label b890: jump_if (true) b891; - label b891: jump_if (true) b892; - label b892: jump_if (true) b893; - label b893: jump_if (true) b894; - label b894: jump_if (true) b895; - label b895: jump_if (true) b896; - label b896: jump_if (true) b897; - label b897: jump_if (true) b898; - label b898: jump_if (true) b899; - label b899: jump_if (true) b900; - label b900: jump_if (true) b901; - label b901: jump_if (true) b902; - label b902: jump_if (true) b903; - label b903: jump_if (true) b904; - label b904: jump_if (true) b905; - label b905: jump_if (true) b906; - label b906: jump_if (true) b907; - label b907: jump_if (true) b908; - label b908: jump_if (true) b909; - label b909: jump_if (true) b910; - label b910: jump_if (true) b911; - label b911: jump_if (true) b912; - label b912: jump_if (true) b913; - label b913: jump_if (true) b914; - label b914: jump_if (true) b915; - label b915: jump_if (true) b916; - label b916: jump_if (true) b917; - label b917: jump_if (true) b918; - label b918: jump_if (true) b919; - label b919: jump_if (true) b920; - label b920: jump_if (true) b921; - label b921: jump_if (true) b922; - label b922: jump_if (true) b923; - label b923: jump_if (true) b924; - label b924: jump_if (true) b925; - label b925: jump_if (true) b926; - label b926: jump_if (true) b927; - label b927: jump_if (true) b928; - label b928: jump_if (true) b929; - label b929: jump_if (true) b930; - label b930: jump_if (true) b931; - label b931: jump_if (true) b932; - label b932: jump_if (true) b933; - label b933: jump_if (true) b934; - label b934: jump_if (true) b935; - label b935: jump_if (true) b936; - label b936: jump_if (true) b937; - label b937: jump_if (true) b938; - label b938: jump_if (true) b939; - label b939: jump_if (true) b940; - label b940: jump_if (true) b941; - label b941: jump_if (true) b942; - label b942: jump_if (true) b943; - label b943: jump_if (true) b944; - label b944: jump_if (true) b945; - label b945: jump_if (true) b946; - label b946: jump_if (true) b947; - label b947: jump_if (true) b948; - label b948: jump_if (true) b949; - label b949: jump_if (true) b950; - label b950: jump_if (true) b951; - label b951: jump_if (true) b952; - label b952: jump_if (true) b953; - label b953: jump_if (true) b954; - label b954: jump_if (true) b955; - label b955: jump_if (true) b956; - label b956: jump_if (true) b957; - label b957: jump_if (true) b958; - label b958: jump_if (true) b959; - label b959: jump_if (true) b960; - label b960: jump_if (true) b961; - label b961: jump_if (true) b962; - label b962: jump_if (true) b963; - label b963: jump_if (true) b964; - label b964: jump_if (true) b965; - label b965: jump_if (true) b966; - label b966: jump_if (true) b967; - label b967: jump_if (true) b968; - label b968: jump_if (true) b969; - label b969: jump_if (true) b970; - label b970: jump_if (true) b971; - label b971: jump_if (true) b972; - label b972: jump_if (true) b973; - label b973: jump_if (true) b974; - label b974: jump_if (true) b975; - label b975: jump_if (true) b976; - label b976: jump_if (true) b977; - label b977: jump_if (true) b978; - label b978: jump_if (true) b979; - label b979: jump_if (true) b980; - label b980: jump_if (true) b981; - label b981: jump_if (true) b982; - label b982: jump_if (true) b983; - label b983: jump_if (true) b984; - label b984: jump_if (true) b985; - label b985: jump_if (true) b986; - label b986: jump_if (true) b987; - label b987: jump_if (true) b988; - label b988: jump_if (true) b989; - label b989: jump_if (true) b990; - label b990: jump_if (true) b991; - label b991: jump_if (true) b992; - label b992: jump_if (true) b993; - label b993: jump_if (true) b994; - label b994: jump_if (true) b995; - label b995: jump_if (true) b996; - label b996: jump_if (true) b997; - label b997: jump_if (true) b998; - label b998: jump_if (true) b999; - label b999: jump_if (true) b1000; - label b1000: - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu128.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu128.mvir deleted file mode 100644 index d7b8038da19f5..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu128.mvir +++ /dev/null @@ -1,45 +0,0 @@ -module 0xcafe.CastU128 { - - public calibrate_castu128_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u128(0u8); - _ = to_u128(0u64); - _ = to_u128(0u128); - - _ = to_u128(21u8); - _ = to_u128(21u64); - _ = to_u128(21u128); - - _ = to_u128(255u8); - _ = to_u128(18446744073709551615u64); - _ = to_u128(340282366920938463463374607431768211455u128); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu128_x100() { - label b0: - Self.calibrate_castu128_impl(10); - return; - } - - public entry calibrate_castu128_x500() { - label b0: - Self.calibrate_castu128_impl(50); - return; - } - - public entry calibrate_castu128_x1000() { - label b0: - Self.calibrate_castu128_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu16.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu16.mvir deleted file mode 100644 index 0cec61b037635..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu16.mvir +++ /dev/null @@ -1,54 +0,0 @@ -module 0xcafe.CastU16 { - - public calibrate_castu16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u16(0u8); - _ = to_u16(0u64); - _ = to_u16(0u128); - _ = to_u16(0u16); - _ = to_u16(0u32); - _ = to_u16(0u256); - - _ = to_u16(21u8); - _ = to_u16(21u64); - _ = to_u16(21u128); - _ = to_u16(21u16); - _ = to_u16(21u32); - _ = to_u16(21u256); - - _ = to_u16(255u8); - _ = to_u16(65535u64); - _ = to_u16(65535u128); - _ = to_u16(65535u16); - _ = to_u16(65535u32); - _ = to_u16(65535u256); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu16_x100() { - label b0: - Self.calibrate_castu16_impl(10); - return; - } - - public entry calibrate_castu16_x500() { - label b0: - Self.calibrate_castu16_impl(50); - return; - } - - public entry calibrate_castu16_x1000() { - label b0: - Self.calibrate_castu16_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu256.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu256.mvir deleted file mode 100644 index bc7cea67a74ab..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu256.mvir +++ /dev/null @@ -1,54 +0,0 @@ -module 0xcafe.CastU256 { - - public calibrate_castu256_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u256(0u8); - _ = to_u256(0u64); - _ = to_u256(0u128); - _ = to_u256(0u16); - _ = to_u256(0u32); - _ = to_u256(0u256); - - _ = to_u256(21u8); - _ = to_u256(21u64); - _ = to_u256(21u128); - _ = to_u256(21u16); - _ = to_u256(21u32); - _ = to_u256(21u256); - - _ = to_u256(255u8); - _ = to_u256(18446744073709551615u64); - _ = to_u256(340282366920938463463374607431768211455u128); - _ = to_u256(65535u16); - _ = to_u256(4294967295u32); - _ = to_u256(115792089237316195423570985008687907853269984665640564039457584007913129639935u256); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu256_x100() { - label b0: - Self.calibrate_castu256_impl(10); - return; - } - - public entry calibrate_castu256_x500() { - label b0: - Self.calibrate_castu256_impl(50); - return; - } - - public entry calibrate_castu256_x1000() { - label b0: - Self.calibrate_castu256_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu32.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu32.mvir deleted file mode 100644 index f476162107cda..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu32.mvir +++ /dev/null @@ -1,56 +0,0 @@ -module 0xcafe.CastU32 { - - public calibrate_castu32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u32(0u8); - _ = to_u32(0u64); - _ = to_u32(0u128); - _ = to_u32(0u16); - _ = to_u32(0u32); - _ = to_u32(0u256); - - // Random small number unchanged. - _ = to_u32(21u8); - _ = to_u32(21u64); - _ = to_u32(21u128); - _ = to_u32(21u16); - _ = to_u32(21u32); - _ = to_u32(21u256); - - // Max representable values remain unchanged. - _ = to_u32(255u8); - _ = to_u32(4294967295u64); - _ = to_u32(4294967295u128); - _ = to_u32(65535u16); - _ = to_u32(4294967295u32); - _ = to_u32(4294967295u256); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu32_x100() { - label b0: - Self.calibrate_castu32_impl(10); - return; - } - - public entry calibrate_castu32_x500() { - label b0: - Self.calibrate_castu32_impl(50); - return; - } - - public entry calibrate_castu32_x1000() { - label b0: - Self.calibrate_castu32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu64.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu64.mvir deleted file mode 100644 index 46a26489f3a11..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu64.mvir +++ /dev/null @@ -1,45 +0,0 @@ -module 0xcafe.CastU64 { - - public calibrate_castu64_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u64(0u8); - _ = to_u64(0u64); - _ = to_u64(0u128); - - _ = to_u64(21u8); - _ = to_u64(21u64); - _ = to_u64(21u128); - - _ = to_u64(255u8); - _ = to_u64(18446744073709551615u64); - _ = to_u64(18446744073709551615u128); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu64_x100() { - label b0: - Self.calibrate_castu64_impl(10); - return; - } - - public entry calibrate_castu64_x500() { - label b0: - Self.calibrate_castu64_impl(50); - return; - } - - public entry calibrate_castu64_x1000() { - label b0: - Self.calibrate_castu64_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu8.mvir b/aptos-move/aptos-gas-calibration/samples_ir/cast/castu8.mvir deleted file mode 100644 index cab5ce72bec8c..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/cast/castu8.mvir +++ /dev/null @@ -1,54 +0,0 @@ -module 0xcafe.CastU8 { - - public calibrate_castu8_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u8(0u8); - _ = to_u8(0u64); - _ = to_u8(0u128); - _ = to_u8(0u16); - _ = to_u8(0u32); - _ = to_u8(0u256); - - _ = to_u8(21u8); - _ = to_u8(21u64); - _ = to_u8(21u128); - _ = to_u8(21u16); - _ = to_u8(21u32); - _ = to_u8(21u256); - - _ = to_u8(255u8); - _ = to_u8(255u64); - _ = to_u8(255u128); - _ = to_u8(255u16); - _ = to_u8(255u32); - _ = to_u8(255u256); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_castu8_x100() { - label b0: - Self.calibrate_castu8_impl(10); - return; - } - - public entry calibrate_castu8_x500() { - label b0: - Self.calibrate_castu8_impl(50); - return; - } - - public entry calibrate_castu8_x1000() { - label b0: - Self.calibrate_castu8_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/function/call.mvir b/aptos-move/aptos-gas-calibration/samples_ir/function/call.mvir deleted file mode 100644 index 9ab1c6ffda186..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/function/call.mvir +++ /dev/null @@ -1,363 +0,0 @@ -// !!! GENERATED FILE -- DO NOT EDIT MANUALLY !!! -module 0xcafe.Call { - // Calling function with 0 args & 0 locals. - a0_l0() { - - label b0: - return; - } - - public calibrate_call_a0_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a0_l0(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a0_l0_x100() { - label b0: - Self.calibrate_call_a0_l0_impl(10); - return; - } - - public entry calibrate_a0_l0_x500() { - label b0: - Self.calibrate_call_a0_l0_impl(50); - return; - } - - public entry calibrate_a0_l0_x1000() { - label b0: - Self.calibrate_call_a0_l0_impl(100); - return; - } - - // Calling function with 4 args & 0 locals. - a4_l0(a0: u64, a1: u64, a2: u64, a3: u64) { - - label b0: - return; - } - - public calibrate_call_a4_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a4_l0(0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a4_l0_x100() { - label b0: - Self.calibrate_call_a4_l0_impl(10); - return; - } - - public entry calibrate_a4_l0_x500() { - label b0: - Self.calibrate_call_a4_l0_impl(50); - return; - } - - public entry calibrate_a4_l0_x1000() { - label b0: - Self.calibrate_call_a4_l0_impl(100); - return; - } - - // Calling function with 16 args & 0 locals. - a16_l0(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, a8: u64, a9: u64, a10: u64, a11: u64, a12: u64, a13: u64, a14: u64, a15: u64) { - - label b0: - return; - } - - public calibrate_call_a16_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a16_l0(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a16_l0_x100() { - label b0: - Self.calibrate_call_a16_l0_impl(10); - return; - } - - public entry calibrate_a16_l0_x500() { - label b0: - Self.calibrate_call_a16_l0_impl(50); - return; - } - - public entry calibrate_a16_l0_x1000() { - label b0: - Self.calibrate_call_a16_l0_impl(100); - return; - } - - // Calling function with 64 args & 0 locals. - a64_l0(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, a8: u64, a9: u64, a10: u64, a11: u64, a12: u64, a13: u64, a14: u64, a15: u64, a16: u64, a17: u64, a18: u64, a19: u64, a20: u64, a21: u64, a22: u64, a23: u64, a24: u64, a25: u64, a26: u64, a27: u64, a28: u64, a29: u64, a30: u64, a31: u64, a32: u64, a33: u64, a34: u64, a35: u64, a36: u64, a37: u64, a38: u64, a39: u64, a40: u64, a41: u64, a42: u64, a43: u64, a44: u64, a45: u64, a46: u64, a47: u64, a48: u64, a49: u64, a50: u64, a51: u64, a52: u64, a53: u64, a54: u64, a55: u64, a56: u64, a57: u64, a58: u64, a59: u64, a60: u64, a61: u64, a62: u64, a63: u64) { - - label b0: - return; - } - - public calibrate_call_a64_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a64_l0(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a64_l0_x100() { - label b0: - Self.calibrate_call_a64_l0_impl(10); - return; - } - - public entry calibrate_a64_l0_x500() { - label b0: - Self.calibrate_call_a64_l0_impl(50); - return; - } - - public entry calibrate_a64_l0_x1000() { - label b0: - Self.calibrate_call_a64_l0_impl(100); - return; - } - - // Calling function with 0 args & 4 locals. - a0_l4() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - label b0: - return; - } - - public calibrate_call_a0_l4_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a0_l4(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a0_l4_x100() { - label b0: - Self.calibrate_call_a0_l4_impl(10); - return; - } - - public entry calibrate_a0_l4_x500() { - label b0: - Self.calibrate_call_a0_l4_impl(50); - return; - } - - public entry calibrate_a0_l4_x1000() { - label b0: - Self.calibrate_call_a0_l4_impl(100); - return; - } - - // Calling function with 0 args & 16 locals. - a0_l16() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - let l4: u64; - let l5: u64; - let l6: u64; - let l7: u64; - let l8: u64; - let l9: u64; - let l10: u64; - let l11: u64; - let l12: u64; - let l13: u64; - let l14: u64; - let l15: u64; - label b0: - return; - } - - public calibrate_call_a0_l16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a0_l16(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a0_l16_x100() { - label b0: - Self.calibrate_call_a0_l16_impl(10); - return; - } - - public entry calibrate_a0_l16_x500() { - label b0: - Self.calibrate_call_a0_l16_impl(50); - return; - } - - public entry calibrate_a0_l16_x1000() { - label b0: - Self.calibrate_call_a0_l16_impl(100); - return; - } - - // Calling function with 0 args & 64 locals. - a0_l64() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - let l4: u64; - let l5: u64; - let l6: u64; - let l7: u64; - let l8: u64; - let l9: u64; - let l10: u64; - let l11: u64; - let l12: u64; - let l13: u64; - let l14: u64; - let l15: u64; - let l16: u64; - let l17: u64; - let l18: u64; - let l19: u64; - let l20: u64; - let l21: u64; - let l22: u64; - let l23: u64; - let l24: u64; - let l25: u64; - let l26: u64; - let l27: u64; - let l28: u64; - let l29: u64; - let l30: u64; - let l31: u64; - let l32: u64; - let l33: u64; - let l34: u64; - let l35: u64; - let l36: u64; - let l37: u64; - let l38: u64; - let l39: u64; - let l40: u64; - let l41: u64; - let l42: u64; - let l43: u64; - let l44: u64; - let l45: u64; - let l46: u64; - let l47: u64; - let l48: u64; - let l49: u64; - let l50: u64; - let l51: u64; - let l52: u64; - let l53: u64; - let l54: u64; - let l55: u64; - let l56: u64; - let l57: u64; - let l58: u64; - let l59: u64; - let l60: u64; - let l61: u64; - let l62: u64; - let l63: u64; - label b0: - return; - } - - public calibrate_call_a0_l64_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.a0_l64(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_a0_l64_x100() { - label b0: - Self.calibrate_call_a0_l64_impl(10); - return; - } - - public entry calibrate_a0_l64_x500() { - label b0: - Self.calibrate_call_a0_l64_impl(50); - return; - } - - public entry calibrate_a0_l64_x1000() { - label b0: - Self.calibrate_call_a0_l64_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/function/call_recursive.mvir b/aptos-move/aptos-gas-calibration/samples_ir/function/call_recursive.mvir deleted file mode 100644 index 57eea1b6c3062..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/function/call_recursive.mvir +++ /dev/null @@ -1,91 +0,0 @@ -module 0xcafe.CallRecrusive { - ///////////////////////////////////////////////// - // INSTRUCTIONS: - // * `CALL_BASE` - // * `CALL_PER_ARG` - // * `CALL_PER_LOCAL` - // * `UNPACK_BASE` - // * `UNPACK_PER_FIELD` - - f1(n: u64) { - label b0: - jump_if_false (0 < copy(n)) end; - Self.f1(move(n) - 1); - label end: - return; - } - - public entry calibrate_f1() { - label b0: - Self.f1(100); - return; - } - - f2(n: u64, a: u64) { - label b0: - jump_if_false (0 < copy(n)) end; - Self.f2(move(n) - 1, move(a)); - label end: - return; - } - - public entry calibrate_f2() { - label b0: - Self.f2(1000, 50); - return; - } - - f3(n: u64) { - let l1: u64; - let l2: u64; - let l3: u64; - label b0: - jump_if_false (0 < copy(n)) end; - Self.f3(move(n) - 1); - label end: - return; - } - - public entry calibrate_f3() { - label b0: - Self.f3(200); - return; - } - - f4() { - label b0: - return; - } - - public entry calibrate_f4_loop() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 1000) loop_end; - i = move(i) + 1; - - Self.f4(); - - jump loop_start; - label loop_end: - return; - } - - f5(n: u64) { - label b0: - jump_if_false (0 < copy(n)) end; - Self.f5(move(n) - 1); - jump true_end; - label end: - _ = (1 - 2); - label true_end: - return; - } - - public entry calibrate_f5_should_error() { - label b0: - Self.f5(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/function/gen.py b/aptos-move/aptos-gas-calibration/samples_ir/function/gen.py deleted file mode 100644 index 94a0d8b62156e..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/function/gen.py +++ /dev/null @@ -1,65 +0,0 @@ -import os - -template = """ - // Calling function with {num_args} args & {num_locals} locals. - {name}({params}) {{ - {ls} - label b0: - return; - }} - - public calibrate_call_{name}_impl(n: u64) {{ - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.{name}({args}); - - jump loop_start; - label loop_end: - return; - }} - - public entry calibrate_call_{name}_x100() {{ - label b0: - Self.calibrate_call_{name}_impl(10); - return; - }} - - public entry calibrate_{name}_x500() {{ - label b0: - Self.calibrate_call_{name}_impl(50); - return; - }} - - public entry calibrate_{name}_x1000() {{ - label b0: - Self.calibrate_call_{name}_impl(100); - return; - }} -""" - -def gen_calibration_sample(num_args, num_locals): - name = "a{}_l{}".format(num_args, num_locals) - ls = '\n '.join(['let l{}: u64;'.format(i) for i in range(num_locals)]) - params = ', '.join(['a{}: u64'.format(i) for i in range(num_args)]) - args = ', '.join(['0'] * num_args) - return template.format(num_args = num_args, num_locals = num_locals, name = name, params = params, args = args, ls = ls) - -with open(os.path.dirname(__file__) + "/call.mvir", "w") as f: - f.write("// !!! GENERATED FILE -- DO NOT EDIT MANUALLY !!!\n") - f.write("module 0xcafe.Call {") - - f.write(gen_calibration_sample(0, 0)) - - f.write(gen_calibration_sample(4, 0)) - f.write(gen_calibration_sample(16, 0)) - f.write(gen_calibration_sample(64, 0)) - - f.write(gen_calibration_sample(0, 4)) - f.write(gen_calibration_sample(0, 16)) - f.write(gen_calibration_sample(0, 64)) - f.write("}") diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/call_generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/generics/call_generic.mvir deleted file mode 100644 index 31fcc32e34a70..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/call_generic.mvir +++ /dev/null @@ -1,483 +0,0 @@ -// !!! GENERATED FILE -- DO NOT EDIT MANUALLY !!! -module 0xcafe.CallGeneric { - // Calling function with 1 type args, 0 args & 0 locals. - t1_a0_l0() { - - label b0: - return; - } - - public calibrate_call_generic_t1_a0_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a0_l0(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a0_l0_x100() { - label b0: - Self.calibrate_call_generic_t1_a0_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a0_l0_x500() { - label b0: - Self.calibrate_call_generic_t1_a0_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a0_l0_x1000() { - label b0: - Self.calibrate_call_generic_t1_a0_l0_impl(100); - return; - } - - // Calling function with 1 type args, 4 args & 0 locals. - t1_a4_l0(a0: u64, a1: u64, a2: u64, a3: u64) { - - label b0: - return; - } - - public calibrate_call_generic_t1_a4_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a4_l0(0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a4_l0_x100() { - label b0: - Self.calibrate_call_generic_t1_a4_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a4_l0_x500() { - label b0: - Self.calibrate_call_generic_t1_a4_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a4_l0_x1000() { - label b0: - Self.calibrate_call_generic_t1_a4_l0_impl(100); - return; - } - - // Calling function with 1 type args, 16 args & 0 locals. - t1_a16_l0(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, a8: u64, a9: u64, a10: u64, a11: u64, a12: u64, a13: u64, a14: u64, a15: u64) { - - label b0: - return; - } - - public calibrate_call_generic_t1_a16_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a16_l0(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a16_l0_x100() { - label b0: - Self.calibrate_call_generic_t1_a16_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a16_l0_x500() { - label b0: - Self.calibrate_call_generic_t1_a16_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a16_l0_x1000() { - label b0: - Self.calibrate_call_generic_t1_a16_l0_impl(100); - return; - } - - // Calling function with 1 type args, 64 args & 0 locals. - t1_a64_l0(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, a8: u64, a9: u64, a10: u64, a11: u64, a12: u64, a13: u64, a14: u64, a15: u64, a16: u64, a17: u64, a18: u64, a19: u64, a20: u64, a21: u64, a22: u64, a23: u64, a24: u64, a25: u64, a26: u64, a27: u64, a28: u64, a29: u64, a30: u64, a31: u64, a32: u64, a33: u64, a34: u64, a35: u64, a36: u64, a37: u64, a38: u64, a39: u64, a40: u64, a41: u64, a42: u64, a43: u64, a44: u64, a45: u64, a46: u64, a47: u64, a48: u64, a49: u64, a50: u64, a51: u64, a52: u64, a53: u64, a54: u64, a55: u64, a56: u64, a57: u64, a58: u64, a59: u64, a60: u64, a61: u64, a62: u64, a63: u64) { - - label b0: - return; - } - - public calibrate_call_generic_t1_a64_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a64_l0(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a64_l0_x100() { - label b0: - Self.calibrate_call_generic_t1_a64_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a64_l0_x500() { - label b0: - Self.calibrate_call_generic_t1_a64_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a64_l0_x1000() { - label b0: - Self.calibrate_call_generic_t1_a64_l0_impl(100); - return; - } - - // Calling function with 1 type args, 0 args & 4 locals. - t1_a0_l4() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - label b0: - return; - } - - public calibrate_call_generic_t1_a0_l4_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a0_l4(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a0_l4_x100() { - label b0: - Self.calibrate_call_generic_t1_a0_l4_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a0_l4_x500() { - label b0: - Self.calibrate_call_generic_t1_a0_l4_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a0_l4_x1000() { - label b0: - Self.calibrate_call_generic_t1_a0_l4_impl(100); - return; - } - - // Calling function with 1 type args, 0 args & 16 locals. - t1_a0_l16() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - let l4: u64; - let l5: u64; - let l6: u64; - let l7: u64; - let l8: u64; - let l9: u64; - let l10: u64; - let l11: u64; - let l12: u64; - let l13: u64; - let l14: u64; - let l15: u64; - label b0: - return; - } - - public calibrate_call_generic_t1_a0_l16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a0_l16(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a0_l16_x100() { - label b0: - Self.calibrate_call_generic_t1_a0_l16_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a0_l16_x500() { - label b0: - Self.calibrate_call_generic_t1_a0_l16_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a0_l16_x1000() { - label b0: - Self.calibrate_call_generic_t1_a0_l16_impl(100); - return; - } - - // Calling function with 1 type args, 0 args & 64 locals. - t1_a0_l64() { - let l0: u64; - let l1: u64; - let l2: u64; - let l3: u64; - let l4: u64; - let l5: u64; - let l6: u64; - let l7: u64; - let l8: u64; - let l9: u64; - let l10: u64; - let l11: u64; - let l12: u64; - let l13: u64; - let l14: u64; - let l15: u64; - let l16: u64; - let l17: u64; - let l18: u64; - let l19: u64; - let l20: u64; - let l21: u64; - let l22: u64; - let l23: u64; - let l24: u64; - let l25: u64; - let l26: u64; - let l27: u64; - let l28: u64; - let l29: u64; - let l30: u64; - let l31: u64; - let l32: u64; - let l33: u64; - let l34: u64; - let l35: u64; - let l36: u64; - let l37: u64; - let l38: u64; - let l39: u64; - let l40: u64; - let l41: u64; - let l42: u64; - let l43: u64; - let l44: u64; - let l45: u64; - let l46: u64; - let l47: u64; - let l48: u64; - let l49: u64; - let l50: u64; - let l51: u64; - let l52: u64; - let l53: u64; - let l54: u64; - let l55: u64; - let l56: u64; - let l57: u64; - let l58: u64; - let l59: u64; - let l60: u64; - let l61: u64; - let l62: u64; - let l63: u64; - label b0: - return; - } - - public calibrate_call_generic_t1_a0_l64_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t1_a0_l64(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t1_a0_l64_x100() { - label b0: - Self.calibrate_call_generic_t1_a0_l64_impl(10); - return; - } - - public entry calibrate_call_generic_t1_a0_l64_x500() { - label b0: - Self.calibrate_call_generic_t1_a0_l64_impl(50); - return; - } - - public entry calibrate_call_generic_t1_a0_l64_x1000() { - label b0: - Self.calibrate_call_generic_t1_a0_l64_impl(100); - return; - } - - // Calling function with 4 type args, 0 args & 0 locals. - t4_a0_l0() { - - label b0: - return; - } - - public calibrate_call_generic_t4_a0_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t4_a0_l0(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t4_a0_l0_x100() { - label b0: - Self.calibrate_call_generic_t4_a0_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t4_a0_l0_x500() { - label b0: - Self.calibrate_call_generic_t4_a0_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t4_a0_l0_x1000() { - label b0: - Self.calibrate_call_generic_t4_a0_l0_impl(100); - return; - } - - // Calling function with 16 type args, 0 args & 0 locals. - t16_a0_l0() { - - label b0: - return; - } - - public calibrate_call_generic_t16_a0_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t16_a0_l0(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t16_a0_l0_x100() { - label b0: - Self.calibrate_call_generic_t16_a0_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t16_a0_l0_x500() { - label b0: - Self.calibrate_call_generic_t16_a0_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t16_a0_l0_x1000() { - label b0: - Self.calibrate_call_generic_t16_a0_l0_impl(100); - return; - } - - // Calling function with 32 type args, 0 args & 0 locals. - t32_a0_l0() { - - label b0: - return; - } - - public calibrate_call_generic_t32_a0_l0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.t32_a0_l0(); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_call_generic_t32_a0_l0_x100() { - label b0: - Self.calibrate_call_generic_t32_a0_l0_impl(10); - return; - } - - public entry calibrate_call_generic_t32_a0_l0_x500() { - label b0: - Self.calibrate_call_generic_t32_a0_l0_impl(50); - return; - } - - public entry calibrate_call_generic_t32_a0_l0_x1000() { - label b0: - Self.calibrate_call_generic_t32_a0_l0_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/gen.py b/aptos-move/aptos-gas-calibration/samples_ir/generics/gen.py deleted file mode 100644 index ae34dddbf814e..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/gen.py +++ /dev/null @@ -1,73 +0,0 @@ -import os - -template = """ - // Calling function with {num_types} type args, {num_args} args & {num_locals} locals. - {name}<{ty_params}>({params}) {{ - {ls} - label b0: - return; - }} - - public calibrate_call_generic_{name}_impl(n: u64) {{ - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - Self.{name}<{ty_args}>({args}); - - jump loop_start; - label loop_end: - return; - }} - - public entry calibrate_call_generic_{name}_x100() {{ - label b0: - Self.calibrate_call_generic_{name}_impl(10); - return; - }} - - public entry calibrate_call_generic_{name}_x500() {{ - label b0: - Self.calibrate_call_generic_{name}_impl(50); - return; - }} - - public entry calibrate_call_generic_{name}_x1000() {{ - label b0: - Self.calibrate_call_generic_{name}_impl(100); - return; - }} -""" - -def gen_calibration_sample(num_types, num_args, num_locals): - name = "t{}_a{}_l{}".format(num_types, num_args, num_locals) - ls = '\n '.join(['let l{}: u64;'.format(i) for i in range(num_locals)]) - params = ', '.join(['a{}: u64'.format(i) for i in range(num_args)]) - args = ', '.join(['0'] * num_args) - ty_params = ','.join('T{}'.format(i) for i in range(num_types)) - ty_args = ', '.join(['u64'] * num_types) - return template.format(num_types=num_types, num_args = num_args, num_locals = num_locals, name = name, params = params, args = args, ls = ls, ty_params=ty_params, ty_args=ty_args) - -with open(os.path.dirname(__file__) + "/call_generic.mvir", "w") as f: - f.write("// !!! GENERATED FILE -- DO NOT EDIT MANUALLY !!!\n") - f.write("module 0xcafe.CallGeneric {") - - # The number of type parameters must be at least 1 here. - # Otherwise, the IR compiler will emit regular calls, which are unwanted. - f.write(gen_calibration_sample(1, 0, 0)) - - f.write(gen_calibration_sample(1, 4, 0)) - f.write(gen_calibration_sample(1, 16, 0)) - f.write(gen_calibration_sample(1, 64, 0)) - - f.write(gen_calibration_sample(1, 0, 4)) - f.write(gen_calibration_sample(1, 0, 16)) - f.write(gen_calibration_sample(1, 0, 64)) - - f.write(gen_calibration_sample(4, 0, 0)) - f.write(gen_calibration_sample(16, 0, 0)) - f.write(gen_calibration_sample(32, 0, 0)) - f.write("}") diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/imm-borrow-field-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/generics/imm-borrow-field-generic.mvir deleted file mode 100644 index 0d069ad04006b..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/imm-borrow-field-generic.mvir +++ /dev/null @@ -1,42 +0,0 @@ -module 0xcafe.ImmBorrowFieldGeneric { - - struct Foo has drop { u: T } - - public calibrate_imm_borrow_field_generic_foo_impl(n: u64) { - let i: u64; - let a: Self.Foo; - let b: &Self.Foo; - label entry: - i = 0; - a = Foo { u: 0 }; - b = &a; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_field_generic_foo_x100() { - label b0: - Self.calibrate_imm_borrow_field_generic_foo_impl(10); - return; - } - - public entry calibrate_imm_borrow_field_generic_foo_x500() { - label b0: - Self.calibrate_imm_borrow_field_generic_foo_impl(50); - return; - } - - public entry calibrate_imm_borrow_field_generic_foo_x1000() { - label b0: - Self.calibrate_imm_borrow_field_generic_foo_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/mut-borrow-field-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/generics/mut-borrow-field-generic.mvir deleted file mode 100644 index 7a4a1cf1ba98a..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/mut-borrow-field-generic.mvir +++ /dev/null @@ -1,42 +0,0 @@ -module 0xcafe.MutBorrowFieldGeneric { - - struct Foo has drop { u: T } - - public calibrate_mut_borrow_field_generic_foo_impl(n: u64) { - let i: u64; - let a: Self.Foo; - let b: &mut Self.Foo; - label entry: - i = 0; - a = Foo { u: 0 }; - b = &mut a; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_field_generic_foo_x100() { - label b0: - Self.calibrate_mut_borrow_field_generic_foo_impl(10); - return; - } - - public entry calibrate_mut_borrow_field_generic_foo_x500() { - label b0: - Self.calibrate_mut_borrow_field_generic_foo_impl(50); - return; - } - - public entry calibrate_mut_borrow_field_generic_foo_x1000() { - label b0: - Self.calibrate_mut_borrow_field_generic_foo_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/pack-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/generics/pack-generic.mvir deleted file mode 100644 index 842bf1a0bdf43..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/pack-generic.mvir +++ /dev/null @@ -1,138 +0,0 @@ -module 0xcafe.PackGeneric { - struct S1 has drop { x1: T } - struct S2 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T } - struct S3 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T} - struct S4 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T, x33: T, x34: T, x35: T, x36: T, x37: T, x38: T, x39: T, x40: T, x41: T, x42: T, x43: T, x44: T, x45: T, x46: T, x47: T, x48: T, x49: T, x50: T, x51: T, x52: T, x53: T, x54: T, x55: T, x56: T, x57: T, x58: T, x59: T, x60: T, x61: T, x62: T, x63: T, x64: T } - - public calibrate_pack_s1_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}, S1{x1: 0}); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_s1_x100() { - label b0: - Self.calibrate_pack_s1_impl(10); - return; - } - - public entry calibrate_pack_s1_x500() { - label b0: - Self.calibrate_pack_s1_impl(50); - return; - } - - public entry calibrate_pack_s1_x1000() { - label b0: - Self.calibrate_pack_s1_impl(100); - return; - } - - public calibrate_pack_s2_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}, S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_s2_x100() { - label b0: - Self.calibrate_pack_s2_impl(10); - return; - } - - public entry calibrate_pack_s2_x500() { - label b0: - Self.calibrate_pack_s2_impl(50); - return; - } - - public entry calibrate_pack_s2_x1000() { - label b0: - Self.calibrate_pack_s2_impl(100); - return; - } - - public calibrate_pack_s3_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}, S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_s3_x100() { - label b0: - Self.calibrate_pack_s3_impl(10); - return; - } - - public entry calibrate_pack_s3_x500() { - label b0: - Self.calibrate_pack_s3_impl(50); - return; - } - - public entry calibrate_pack_s3_x1000() { - label b0: - Self.calibrate_pack_s3_impl(100); - return; - } - - public calibrate_pack_s4_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}, ); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_s4_x100() { - label b0: - Self.calibrate_pack_s4_impl(10); - return; - } - - public entry calibrate_pack_s4_x500() { - label b0: - Self.calibrate_pack_s4_impl(50); - return; - } - - public entry calibrate_pack_s4_x1000() { - label b0: - Self.calibrate_pack_s4_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/generics/unpack-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/generics/unpack-generic.mvir deleted file mode 100644 index 7d037e7d93d97..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/generics/unpack-generic.mvir +++ /dev/null @@ -1,360 +0,0 @@ -module 0xcafe.UnpackGeneric { - struct S1 has drop { x1: T } - struct S2 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T } - struct S3 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T} - struct S4 has drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T, x33: T, x34: T, x35: T, x36: T, x37: T, x38: T, x39: T, x40: T, x41: T, x42: T, x43: T, x44: T, x45: T, x46: T, x47: T, x48: T, x49: T, x50: T, x51: T, x52: T, x53: T, x54: T, x55: T, x56: T, x57: T, x58: T, x59: T, x60: T, x61: T, x62: T, x63: T, x64: T } - - public calibrate_unpack_s1_impl(n: u64) { - let i: u64; - let t: Self.S1; - let x1: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - t = S1{x1: 0}; - S1{x1} = move(t); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_s1_x100() { - label b0: - Self.calibrate_unpack_s1_impl(10); - return; - } - - public entry calibrate_unpack_s1_x500() { - label b0: - Self.calibrate_unpack_s1_impl(50); - return; - } - - public entry calibrate_unpack_s1_x1000() { - label b0: - Self.calibrate_unpack_s1_impl(100); - return; - } - - public calibrate_unpack_s2_impl(n: u64) { - let i: u64; - let t: Self.S2; - let x1: u64; - let x2: u64; - let x3: u64; - let x4: u64; - let x5: u64; - let x6: u64; - let x7: u64; - let x8: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - t = S2{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0}; - S2{x1,x2,x3,x4,x5,x6,x7,x8} = move(t); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_s2_x100() { - label b0: - Self.calibrate_unpack_s2_impl(10); - return; - } - - public entry calibrate_unpack_s2_x500() { - label b0: - Self.calibrate_unpack_s2_impl(50); - return; - } - - public entry calibrate_unpack_s2_x1000() { - label b0: - Self.calibrate_unpack_s2_impl(100); - return; - } - - public calibrate_unpack_s3_impl(n: u64) { - let i: u64; - let t: Self.S3; - let x1: u64; - let x2: u64; - let x3: u64; - let x4: u64; - let x5: u64; - let x6: u64; - let x7: u64; - let x8: u64; - let x9: u64; - let x10: u64; - let x11: u64; - let x12: u64; - let x13: u64; - let x14: u64; - let x15: u64; - let x16: u64; - let x17: u64; - let x18: u64; - let x19: u64; - let x20: u64; - let x21: u64; - let x22: u64; - let x23: u64; - let x24: u64; - let x25: u64; - let x26: u64; - let x27: u64; - let x28: u64; - let x29: u64; - let x30: u64; - let x31: u64; - let x32: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - t = S3{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0}; - S3{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32} = move(t); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_s3_x100() { - label b0: - Self.calibrate_unpack_s3_impl(10); - return; - } - - public entry calibrate_unpack_s3_x500() { - label b0: - Self.calibrate_unpack_s3_impl(50); - return; - } - - public entry calibrate_unpack_s3_x1000() { - label b0: - Self.calibrate_unpack_s3_impl(100); - return; - } - - public calibrate_unpack_s4_impl(n: u64) { - let i: u64; - let t: Self.S4; - let x1: u64; - let x2: u64; - let x3: u64; - let x4: u64; - let x5: u64; - let x6: u64; - let x7: u64; - let x8: u64; - let x9: u64; - let x10: u64; - let x11: u64; - let x12: u64; - let x13: u64; - let x14: u64; - let x15: u64; - let x16: u64; - let x17: u64; - let x18: u64; - let x19: u64; - let x20: u64; - let x21: u64; - let x22: u64; - let x23: u64; - let x24: u64; - let x25: u64; - let x26: u64; - let x27: u64; - let x28: u64; - let x29: u64; - let x30: u64; - let x31: u64; - let x32: u64; - let x33: u64; - let x34: u64; - let x35: u64; - let x36: u64; - let x37: u64; - let x38: u64; - let x39: u64; - let x40: u64; - let x41: u64; - let x42: u64; - let x43: u64; - let x44: u64; - let x45: u64; - let x46: u64; - let x47: u64; - let x48: u64; - let x49: u64; - let x50: u64; - let x51: u64; - let x52: u64; - let x53: u64; - let x54: u64; - let x55: u64; - let x56: u64; - let x57: u64; - let x58: u64; - let x59: u64; - let x60: u64; - let x61: u64; - let x62: u64; - let x63: u64; - let x64: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - t = S4{x1: 0, x2: 0, x3: 0, x4: 0, x5: 0, x6: 0, x7: 0, x8: 0, x9: 0, x10: 0, x11: 0, x12: 0, x13: 0, x14: 0, x15: 0, x16: 0, x17: 0, x18: 0, x19: 0, x20: 0, x21: 0, x22: 0, x23: 0, x24: 0, x25: 0, x26: 0, x27: 0, x28: 0, x29: 0, x30: 0, x31: 0, x32: 0, x33: 0, x34: 0, x35: 0, x36: 0, x37: 0, x38: 0, x39: 0, x40: 0, x41: 0, x42: 0, x43: 0, x44: 0, x45: 0, x46: 0, x47: 0, x48: 0, x49: 0, x50: 0, x51: 0, x52: 0, x53: 0, x54: 0, x55: 0, x56: 0, x57: 0, x58: 0, x59: 0, x60: 0, x61: 0, x62: 0, x63: 0, x64: 0}; - S4{x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32,x33,x34,x35,x36,x37,x38,x39,x40,x41,x42,x43,x44,x45,x46,x47,x48,x49,x50,x51,x52,x53,x54,x55,x56,x57,x58,x59,x60,x61,x62,x63,x64} = move(t); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_s4_x100() { - label b0: - Self.calibrate_unpack_s4_impl(10); - return; - } - - public entry calibrate_unpack_s4_x500() { - label b0: - Self.calibrate_unpack_s4_impl(50); - return; - } - - public entry calibrate_unpack_s4_x1000() { - label b0: - Self.calibrate_unpack_s4_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/exists-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/exists-generic.mvir deleted file mode 100644 index ea7a9fc7d61d2..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/exists-generic.mvir +++ /dev/null @@ -1,138 +0,0 @@ -module 0xcafe.ExistsGeneric { - struct S1 has key, drop { x1: T } - struct S2 has key, drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T } - struct S3 has key, drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T} - struct S4 has key, drop { x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T, x17: T, x18: T, x19: T, x20: T, x21: T, x22: T, x23: T, x24: T, x25: T, x26: T, x27: T, x28: T, x29: T, x30: T, x31: T, x32: T, x33: T, x34: T, x35: T, x36: T, x37: T, x38: T, x39: T, x40: T, x41: T, x42: T, x43: T, x44: T, x45: T, x46: T, x47: T, x48: T, x49: T, x50: T, x51: T, x52: T, x53: T, x54: T, x55: T, x56: T, x57: T, x58: T, x59: T, x60: T, x61: T, x62: T, x63: T, x64: T } - - public calibrate_exists_generic_s1_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_generic_s1_x100() { - label b0: - Self.calibrate_exists_generic_s1_impl(10); - return; - } - - public entry calibrate_exists_generic_s1_x1000() { - label b0: - Self.calibrate_exists_generic_s1_impl(100); - return; - } - - public entry calibrate_exists_generic_s1_x5000() { - label b0: - Self.calibrate_exists_generic_s1_impl(500); - return; - } - - public calibrate_exists_generic_s2_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_generic_s2_x100() { - label b0: - Self.calibrate_exists_generic_s2_impl(10); - return; - } - - public entry calibrate_exists_generic_s2_x1000() { - label b0: - Self.calibrate_exists_generic_s2_impl(100); - return; - } - - public entry calibrate_exists_generic_s2_x5000() { - label b0: - Self.calibrate_exists_generic_s2_impl(500); - return; - } - - public calibrate_exists_generic_s3_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_generic_s3_x100() { - label b0: - Self.calibrate_exists_generic_s3_impl(10); - return; - } - - public entry calibrate_exists_generic_s3_x1000() { - label b0: - Self.calibrate_exists_generic_s3_impl(100); - return; - } - - public entry calibrate_exists_generic_s3_x5000() { - label b0: - Self.calibrate_exists_generic_s3_impl(500); - return; - } - - public calibrate_exists_generic_s4_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe), exists>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_generic_s4_x100() { - label b0: - Self.calibrate_exists_generic_s4_impl(10); - return; - } - - public entry calibrate_exists_generic_s4_x1000() { - label b0: - Self.calibrate_exists_generic_s4_impl(100); - return; - } - - public entry calibrate_exists_generic_s4_x5000() { - label b0: - Self.calibrate_exists_generic_s4_impl(500); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/imm-borrow-global-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/imm-borrow-global-generic.mvir deleted file mode 100644 index 10fbdd31d3364..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/imm-borrow-global-generic.mvir +++ /dev/null @@ -1,53 +0,0 @@ -module 0xcafe.ImmBorrowGlobalGeneric { - struct S1 has key, drop { x: T } - struct S2 has key, drop { x: T } - struct S3 has key, drop { x: T } - - public entry calibrate_imm_borrow_global_generic_x100(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S1{x:0}); - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_global_generic_x500(s: signer) acquires S2 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S2{x:0}); - label loop_start: - jump_if_false (copy(i) < 500) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_global_generic_x1000(s: signer) acquires S3 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S3{x:0}); - label loop_start: - jump_if_false (copy(i) < 1000) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe), borrow_global>(0xcafe)); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-from-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-from-generic.mvir deleted file mode 100644 index 50e691ffa3bd1..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-from-generic.mvir +++ /dev/null @@ -1,132 +0,0 @@ -module 0xcafe.MoveFromGeneric { - struct S1 has key, drop { x: T } - - public entry calibrate_move_from_generic_x100(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_move_from_generic_x1000(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 1000) loop_end; - i = move(i) + 1; - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_move_from_generic_x5000(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 5000) loop_end; - i = move(i) + 1; - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - move_to>(&s, S1{ x: 0 }); - _ = move_from>(0xcafe); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-to-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-to-generic.mvir deleted file mode 100644 index 2b5feedb9611d..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/move-to-generic.mvir +++ /dev/null @@ -1,3217 +0,0 @@ -module 0xcafe.MoveToGeneric { - struct S1 has key, drop { x: T } - struct S2 has key, drop { x: T } - struct S3 has key, drop { x: T } - struct S4 has key, drop { x: T } - struct S5 has key, drop { x: T } - struct S6 has key, drop { x: T } - struct S7 has key, drop { x: T } - struct S8 has key, drop { x: T } - struct S9 has key, drop { x: T } - struct S10 has key, drop { x: T } - struct S11 has key, drop { x: T } - struct S12 has key, drop { x: T } - struct S13 has key, drop { x: T } - struct S14 has key, drop { x: T } - struct S15 has key, drop { x: T } - struct S16 has key, drop { x: T } - struct S17 has key, drop { x: T } - struct S18 has key, drop { x: T } - struct S19 has key, drop { x: T } - struct S20 has key, drop { x: T } - struct S21 has key, drop { x: T } - struct S22 has key, drop { x: T } - struct S23 has key, drop { x: T } - struct S24 has key, drop { x: T } - struct S25 has key, drop { x: T } - struct S26 has key, drop { x: T } - struct S27 has key, drop { x: T } - struct S28 has key, drop { x: T } - struct S29 has key, drop { x: T } - struct S30 has key, drop { x: T } - struct S31 has key, drop { x: T } - struct S32 has key, drop { x: T } - struct S33 has key, drop { x: T } - struct S34 has key, drop { x: T } - struct S35 has key, drop { x: T } - struct S36 has key, drop { x: T } - struct S37 has key, drop { x: T } - struct S38 has key, drop { x: T } - struct S39 has key, drop { x: T } - struct S40 has key, drop { x: T } - struct S41 has key, drop { x: T } - struct S42 has key, drop { x: T } - struct S43 has key, drop { x: T } - struct S44 has key, drop { x: T } - struct S45 has key, drop { x: T } - struct S46 has key, drop { x: T } - struct S47 has key, drop { x: T } - struct S48 has key, drop { x: T } - struct S49 has key, drop { x: T } - struct S50 has key, drop { x: T } - struct S51 has key, drop { x: T } - struct S52 has key, drop { x: T } - struct S53 has key, drop { x: T } - struct S54 has key, drop { x: T } - struct S55 has key, drop { x: T } - struct S56 has key, drop { x: T } - struct S57 has key, drop { x: T } - struct S58 has key, drop { x: T } - struct S59 has key, drop { x: T } - struct S60 has key, drop { x: T } - struct S61 has key, drop { x: T } - struct S62 has key, drop { x: T } - struct S63 has key, drop { x: T } - struct S64 has key, drop { x: T } - struct S65 has key, drop { x: T } - struct S66 has key, drop { x: T } - struct S67 has key, drop { x: T } - struct S68 has key, drop { x: T } - struct S69 has key, drop { x: T } - struct S70 has key, drop { x: T } - struct S71 has key, drop { x: T } - struct S72 has key, drop { x: T } - struct S73 has key, drop { x: T } - struct S74 has key, drop { x: T } - struct S75 has key, drop { x: T } - struct S76 has key, drop { x: T } - struct S77 has key, drop { x: T } - struct S78 has key, drop { x: T } - struct S79 has key, drop { x: T } - struct S80 has key, drop { x: T } - struct S81 has key, drop { x: T } - struct S82 has key, drop { x: T } - struct S83 has key, drop { x: T } - struct S84 has key, drop { x: T } - struct S85 has key, drop { x: T } - struct S86 has key, drop { x: T } - struct S87 has key, drop { x: T } - struct S88 has key, drop { x: T } - struct S89 has key, drop { x: T } - struct S90 has key, drop { x: T } - struct S91 has key, drop { x: T } - struct S92 has key, drop { x: T } - struct S93 has key, drop { x: T } - struct S94 has key, drop { x: T } - struct S95 has key, drop { x: T } - struct S96 has key, drop { x: T } - struct S97 has key, drop { x: T } - struct S98 has key, drop { x: T } - struct S99 has key, drop { x: T } - struct S100 has key, drop { x: T } - struct S101 has key, drop { x: T } - struct S102 has key, drop { x: T } - struct S103 has key, drop { x: T } - struct S104 has key, drop { x: T } - struct S105 has key, drop { x: T } - struct S106 has key, drop { x: T } - struct S107 has key, drop { x: T } - struct S108 has key, drop { x: T } - struct S109 has key, drop { x: T } - struct S110 has key, drop { x: T } - struct S111 has key, drop { x: T } - struct S112 has key, drop { x: T } - struct S113 has key, drop { x: T } - struct S114 has key, drop { x: T } - struct S115 has key, drop { x: T } - struct S116 has key, drop { x: T } - struct S117 has key, drop { x: T } - struct S118 has key, drop { x: T } - struct S119 has key, drop { x: T } - struct S120 has key, drop { x: T } - struct S121 has key, drop { x: T } - struct S122 has key, drop { x: T } - struct S123 has key, drop { x: T } - struct S124 has key, drop { x: T } - struct S125 has key, drop { x: T } - struct S126 has key, drop { x: T } - struct S127 has key, drop { x: T } - struct S128 has key, drop { x: T } - struct S129 has key, drop { x: T } - struct S130 has key, drop { x: T } - struct S131 has key, drop { x: T } - struct S132 has key, drop { x: T } - struct S133 has key, drop { x: T } - struct S134 has key, drop { x: T } - struct S135 has key, drop { x: T } - struct S136 has key, drop { x: T } - struct S137 has key, drop { x: T } - struct S138 has key, drop { x: T } - struct S139 has key, drop { x: T } - struct S140 has key, drop { x: T } - struct S141 has key, drop { x: T } - struct S142 has key, drop { x: T } - struct S143 has key, drop { x: T } - struct S144 has key, drop { x: T } - struct S145 has key, drop { x: T } - struct S146 has key, drop { x: T } - struct S147 has key, drop { x: T } - struct S148 has key, drop { x: T } - struct S149 has key, drop { x: T } - struct S150 has key, drop { x: T } - struct S151 has key, drop { x: T } - struct S152 has key, drop { x: T } - struct S153 has key, drop { x: T } - struct S154 has key, drop { x: T } - struct S155 has key, drop { x: T } - struct S156 has key, drop { x: T } - struct S157 has key, drop { x: T } - struct S158 has key, drop { x: T } - struct S159 has key, drop { x: T } - struct S160 has key, drop { x: T } - struct S161 has key, drop { x: T } - struct S162 has key, drop { x: T } - struct S163 has key, drop { x: T } - struct S164 has key, drop { x: T } - struct S165 has key, drop { x: T } - struct S166 has key, drop { x: T } - struct S167 has key, drop { x: T } - struct S168 has key, drop { x: T } - struct S169 has key, drop { x: T } - struct S170 has key, drop { x: T } - struct S171 has key, drop { x: T } - struct S172 has key, drop { x: T } - struct S173 has key, drop { x: T } - struct S174 has key, drop { x: T } - struct S175 has key, drop { x: T } - struct S176 has key, drop { x: T } - struct S177 has key, drop { x: T } - struct S178 has key, drop { x: T } - struct S179 has key, drop { x: T } - struct S180 has key, drop { x: T } - struct S181 has key, drop { x: T } - struct S182 has key, drop { x: T } - struct S183 has key, drop { x: T } - struct S184 has key, drop { x: T } - struct S185 has key, drop { x: T } - struct S186 has key, drop { x: T } - struct S187 has key, drop { x: T } - struct S188 has key, drop { x: T } - struct S189 has key, drop { x: T } - struct S190 has key, drop { x: T } - struct S191 has key, drop { x: T } - struct S192 has key, drop { x: T } - struct S193 has key, drop { x: T } - struct S194 has key, drop { x: T } - struct S195 has key, drop { x: T } - struct S196 has key, drop { x: T } - struct S197 has key, drop { x: T } - struct S198 has key, drop { x: T } - struct S199 has key, drop { x: T } - struct S200 has key, drop { x: T } - struct S201 has key, drop { x: T } - struct S202 has key, drop { x: T } - struct S203 has key, drop { x: T } - struct S204 has key, drop { x: T } - struct S205 has key, drop { x: T } - struct S206 has key, drop { x: T } - struct S207 has key, drop { x: T } - struct S208 has key, drop { x: T } - struct S209 has key, drop { x: T } - struct S210 has key, drop { x: T } - struct S211 has key, drop { x: T } - struct S212 has key, drop { x: T } - struct S213 has key, drop { x: T } - struct S214 has key, drop { x: T } - struct S215 has key, drop { x: T } - struct S216 has key, drop { x: T } - struct S217 has key, drop { x: T } - struct S218 has key, drop { x: T } - struct S219 has key, drop { x: T } - struct S220 has key, drop { x: T } - struct S221 has key, drop { x: T } - struct S222 has key, drop { x: T } - struct S223 has key, drop { x: T } - struct S224 has key, drop { x: T } - struct S225 has key, drop { x: T } - struct S226 has key, drop { x: T } - struct S227 has key, drop { x: T } - struct S228 has key, drop { x: T } - struct S229 has key, drop { x: T } - struct S230 has key, drop { x: T } - struct S231 has key, drop { x: T } - struct S232 has key, drop { x: T } - struct S233 has key, drop { x: T } - struct S234 has key, drop { x: T } - struct S235 has key, drop { x: T } - struct S236 has key, drop { x: T } - struct S237 has key, drop { x: T } - struct S238 has key, drop { x: T } - struct S239 has key, drop { x: T } - struct S240 has key, drop { x: T } - struct S241 has key, drop { x: T } - struct S242 has key, drop { x: T } - struct S243 has key, drop { x: T } - struct S244 has key, drop { x: T } - struct S245 has key, drop { x: T } - struct S246 has key, drop { x: T } - struct S247 has key, drop { x: T } - struct S248 has key, drop { x: T } - struct S249 has key, drop { x: T } - struct S250 has key, drop { x: T } - struct S251 has key, drop { x: T } - struct S252 has key, drop { x: T } - struct S253 has key, drop { x: T } - struct S254 has key, drop { x: T } - struct S255 has key, drop { x: T } - struct S256 has key, drop { x: T } - struct S257 has key, drop { x: T } - struct S258 has key, drop { x: T } - struct S259 has key, drop { x: T } - struct S260 has key, drop { x: T } - struct S261 has key, drop { x: T } - struct S262 has key, drop { x: T } - struct S263 has key, drop { x: T } - struct S264 has key, drop { x: T } - struct S265 has key, drop { x: T } - struct S266 has key, drop { x: T } - struct S267 has key, drop { x: T } - struct S268 has key, drop { x: T } - struct S269 has key, drop { x: T } - struct S270 has key, drop { x: T } - struct S271 has key, drop { x: T } - struct S272 has key, drop { x: T } - struct S273 has key, drop { x: T } - struct S274 has key, drop { x: T } - struct S275 has key, drop { x: T } - struct S276 has key, drop { x: T } - struct S277 has key, drop { x: T } - struct S278 has key, drop { x: T } - struct S279 has key, drop { x: T } - struct S280 has key, drop { x: T } - struct S281 has key, drop { x: T } - struct S282 has key, drop { x: T } - struct S283 has key, drop { x: T } - struct S284 has key, drop { x: T } - struct S285 has key, drop { x: T } - struct S286 has key, drop { x: T } - struct S287 has key, drop { x: T } - struct S288 has key, drop { x: T } - struct S289 has key, drop { x: T } - struct S290 has key, drop { x: T } - struct S291 has key, drop { x: T } - struct S292 has key, drop { x: T } - struct S293 has key, drop { x: T } - struct S294 has key, drop { x: T } - struct S295 has key, drop { x: T } - struct S296 has key, drop { x: T } - struct S297 has key, drop { x: T } - struct S298 has key, drop { x: T } - struct S299 has key, drop { x: T } - struct S300 has key, drop { x: T } - struct S301 has key, drop { x: T } - struct S302 has key, drop { x: T } - struct S303 has key, drop { x: T } - struct S304 has key, drop { x: T } - struct S305 has key, drop { x: T } - struct S306 has key, drop { x: T } - struct S307 has key, drop { x: T } - struct S308 has key, drop { x: T } - struct S309 has key, drop { x: T } - struct S310 has key, drop { x: T } - struct S311 has key, drop { x: T } - struct S312 has key, drop { x: T } - struct S313 has key, drop { x: T } - struct S314 has key, drop { x: T } - struct S315 has key, drop { x: T } - struct S316 has key, drop { x: T } - struct S317 has key, drop { x: T } - struct S318 has key, drop { x: T } - struct S319 has key, drop { x: T } - struct S320 has key, drop { x: T } - struct S321 has key, drop { x: T } - struct S322 has key, drop { x: T } - struct S323 has key, drop { x: T } - struct S324 has key, drop { x: T } - struct S325 has key, drop { x: T } - struct S326 has key, drop { x: T } - struct S327 has key, drop { x: T } - struct S328 has key, drop { x: T } - struct S329 has key, drop { x: T } - struct S330 has key, drop { x: T } - struct S331 has key, drop { x: T } - struct S332 has key, drop { x: T } - struct S333 has key, drop { x: T } - struct S334 has key, drop { x: T } - struct S335 has key, drop { x: T } - struct S336 has key, drop { x: T } - struct S337 has key, drop { x: T } - struct S338 has key, drop { x: T } - struct S339 has key, drop { x: T } - struct S340 has key, drop { x: T } - struct S341 has key, drop { x: T } - struct S342 has key, drop { x: T } - struct S343 has key, drop { x: T } - struct S344 has key, drop { x: T } - struct S345 has key, drop { x: T } - struct S346 has key, drop { x: T } - struct S347 has key, drop { x: T } - struct S348 has key, drop { x: T } - struct S349 has key, drop { x: T } - struct S350 has key, drop { x: T } - struct S351 has key, drop { x: T } - struct S352 has key, drop { x: T } - struct S353 has key, drop { x: T } - struct S354 has key, drop { x: T } - struct S355 has key, drop { x: T } - struct S356 has key, drop { x: T } - struct S357 has key, drop { x: T } - struct S358 has key, drop { x: T } - struct S359 has key, drop { x: T } - struct S360 has key, drop { x: T } - struct S361 has key, drop { x: T } - struct S362 has key, drop { x: T } - struct S363 has key, drop { x: T } - struct S364 has key, drop { x: T } - struct S365 has key, drop { x: T } - struct S366 has key, drop { x: T } - struct S367 has key, drop { x: T } - struct S368 has key, drop { x: T } - struct S369 has key, drop { x: T } - struct S370 has key, drop { x: T } - struct S371 has key, drop { x: T } - struct S372 has key, drop { x: T } - struct S373 has key, drop { x: T } - struct S374 has key, drop { x: T } - struct S375 has key, drop { x: T } - struct S376 has key, drop { x: T } - struct S377 has key, drop { x: T } - struct S378 has key, drop { x: T } - struct S379 has key, drop { x: T } - struct S380 has key, drop { x: T } - struct S381 has key, drop { x: T } - struct S382 has key, drop { x: T } - struct S383 has key, drop { x: T } - struct S384 has key, drop { x: T } - struct S385 has key, drop { x: T } - struct S386 has key, drop { x: T } - struct S387 has key, drop { x: T } - struct S388 has key, drop { x: T } - struct S389 has key, drop { x: T } - struct S390 has key, drop { x: T } - struct S391 has key, drop { x: T } - struct S392 has key, drop { x: T } - struct S393 has key, drop { x: T } - struct S394 has key, drop { x: T } - struct S395 has key, drop { x: T } - struct S396 has key, drop { x: T } - struct S397 has key, drop { x: T } - struct S398 has key, drop { x: T } - struct S399 has key, drop { x: T } - struct S400 has key, drop { x: T } - struct S401 has key, drop { x: T } - struct S402 has key, drop { x: T } - struct S403 has key, drop { x: T } - struct S404 has key, drop { x: T } - struct S405 has key, drop { x: T } - struct S406 has key, drop { x: T } - struct S407 has key, drop { x: T } - struct S408 has key, drop { x: T } - struct S409 has key, drop { x: T } - struct S410 has key, drop { x: T } - struct S411 has key, drop { x: T } - struct S412 has key, drop { x: T } - struct S413 has key, drop { x: T } - struct S414 has key, drop { x: T } - struct S415 has key, drop { x: T } - struct S416 has key, drop { x: T } - struct S417 has key, drop { x: T } - struct S418 has key, drop { x: T } - struct S419 has key, drop { x: T } - struct S420 has key, drop { x: T } - struct S421 has key, drop { x: T } - struct S422 has key, drop { x: T } - struct S423 has key, drop { x: T } - struct S424 has key, drop { x: T } - struct S425 has key, drop { x: T } - struct S426 has key, drop { x: T } - struct S427 has key, drop { x: T } - struct S428 has key, drop { x: T } - struct S429 has key, drop { x: T } - struct S430 has key, drop { x: T } - struct S431 has key, drop { x: T } - struct S432 has key, drop { x: T } - struct S433 has key, drop { x: T } - struct S434 has key, drop { x: T } - struct S435 has key, drop { x: T } - struct S436 has key, drop { x: T } - struct S437 has key, drop { x: T } - struct S438 has key, drop { x: T } - struct S439 has key, drop { x: T } - struct S440 has key, drop { x: T } - struct S441 has key, drop { x: T } - struct S442 has key, drop { x: T } - struct S443 has key, drop { x: T } - struct S444 has key, drop { x: T } - struct S445 has key, drop { x: T } - struct S446 has key, drop { x: T } - struct S447 has key, drop { x: T } - struct S448 has key, drop { x: T } - struct S449 has key, drop { x: T } - struct S450 has key, drop { x: T } - struct S451 has key, drop { x: T } - struct S452 has key, drop { x: T } - struct S453 has key, drop { x: T } - struct S454 has key, drop { x: T } - struct S455 has key, drop { x: T } - struct S456 has key, drop { x: T } - struct S457 has key, drop { x: T } - struct S458 has key, drop { x: T } - struct S459 has key, drop { x: T } - struct S460 has key, drop { x: T } - struct S461 has key, drop { x: T } - struct S462 has key, drop { x: T } - struct S463 has key, drop { x: T } - struct S464 has key, drop { x: T } - struct S465 has key, drop { x: T } - struct S466 has key, drop { x: T } - struct S467 has key, drop { x: T } - struct S468 has key, drop { x: T } - struct S469 has key, drop { x: T } - struct S470 has key, drop { x: T } - struct S471 has key, drop { x: T } - struct S472 has key, drop { x: T } - struct S473 has key, drop { x: T } - struct S474 has key, drop { x: T } - struct S475 has key, drop { x: T } - struct S476 has key, drop { x: T } - struct S477 has key, drop { x: T } - struct S478 has key, drop { x: T } - struct S479 has key, drop { x: T } - struct S480 has key, drop { x: T } - struct S481 has key, drop { x: T } - struct S482 has key, drop { x: T } - struct S483 has key, drop { x: T } - struct S484 has key, drop { x: T } - struct S485 has key, drop { x: T } - struct S486 has key, drop { x: T } - struct S487 has key, drop { x: T } - struct S488 has key, drop { x: T } - struct S489 has key, drop { x: T } - struct S490 has key, drop { x: T } - struct S491 has key, drop { x: T } - struct S492 has key, drop { x: T } - struct S493 has key, drop { x: T } - struct S494 has key, drop { x: T } - struct S495 has key, drop { x: T } - struct S496 has key, drop { x: T } - struct S497 has key, drop { x: T } - struct S498 has key, drop { x: T } - struct S499 has key, drop { x: T } - struct S500 has key, drop { x: T } - struct S501 has key, drop { x: T } - struct S502 has key, drop { x: T } - struct S503 has key, drop { x: T } - struct S504 has key, drop { x: T } - struct S505 has key, drop { x: T } - struct S506 has key, drop { x: T } - struct S507 has key, drop { x: T } - struct S508 has key, drop { x: T } - struct S509 has key, drop { x: T } - struct S510 has key, drop { x: T } - struct S511 has key, drop { x: T } - struct S512 has key, drop { x: T } - struct S513 has key, drop { x: T } - struct S514 has key, drop { x: T } - struct S515 has key, drop { x: T } - struct S516 has key, drop { x: T } - struct S517 has key, drop { x: T } - struct S518 has key, drop { x: T } - struct S519 has key, drop { x: T } - struct S520 has key, drop { x: T } - struct S521 has key, drop { x: T } - struct S522 has key, drop { x: T } - struct S523 has key, drop { x: T } - struct S524 has key, drop { x: T } - struct S525 has key, drop { x: T } - struct S526 has key, drop { x: T } - struct S527 has key, drop { x: T } - struct S528 has key, drop { x: T } - struct S529 has key, drop { x: T } - struct S530 has key, drop { x: T } - struct S531 has key, drop { x: T } - struct S532 has key, drop { x: T } - struct S533 has key, drop { x: T } - struct S534 has key, drop { x: T } - struct S535 has key, drop { x: T } - struct S536 has key, drop { x: T } - struct S537 has key, drop { x: T } - struct S538 has key, drop { x: T } - struct S539 has key, drop { x: T } - struct S540 has key, drop { x: T } - struct S541 has key, drop { x: T } - struct S542 has key, drop { x: T } - struct S543 has key, drop { x: T } - struct S544 has key, drop { x: T } - struct S545 has key, drop { x: T } - struct S546 has key, drop { x: T } - struct S547 has key, drop { x: T } - struct S548 has key, drop { x: T } - struct S549 has key, drop { x: T } - struct S550 has key, drop { x: T } - struct S551 has key, drop { x: T } - struct S552 has key, drop { x: T } - struct S553 has key, drop { x: T } - struct S554 has key, drop { x: T } - struct S555 has key, drop { x: T } - struct S556 has key, drop { x: T } - struct S557 has key, drop { x: T } - struct S558 has key, drop { x: T } - struct S559 has key, drop { x: T } - struct S560 has key, drop { x: T } - struct S561 has key, drop { x: T } - struct S562 has key, drop { x: T } - struct S563 has key, drop { x: T } - struct S564 has key, drop { x: T } - struct S565 has key, drop { x: T } - struct S566 has key, drop { x: T } - struct S567 has key, drop { x: T } - struct S568 has key, drop { x: T } - struct S569 has key, drop { x: T } - struct S570 has key, drop { x: T } - struct S571 has key, drop { x: T } - struct S572 has key, drop { x: T } - struct S573 has key, drop { x: T } - struct S574 has key, drop { x: T } - struct S575 has key, drop { x: T } - struct S576 has key, drop { x: T } - struct S577 has key, drop { x: T } - struct S578 has key, drop { x: T } - struct S579 has key, drop { x: T } - struct S580 has key, drop { x: T } - struct S581 has key, drop { x: T } - struct S582 has key, drop { x: T } - struct S583 has key, drop { x: T } - struct S584 has key, drop { x: T } - struct S585 has key, drop { x: T } - struct S586 has key, drop { x: T } - struct S587 has key, drop { x: T } - struct S588 has key, drop { x: T } - struct S589 has key, drop { x: T } - struct S590 has key, drop { x: T } - struct S591 has key, drop { x: T } - struct S592 has key, drop { x: T } - struct S593 has key, drop { x: T } - struct S594 has key, drop { x: T } - struct S595 has key, drop { x: T } - struct S596 has key, drop { x: T } - struct S597 has key, drop { x: T } - struct S598 has key, drop { x: T } - struct S599 has key, drop { x: T } - struct S600 has key, drop { x: T } - struct S601 has key, drop { x: T } - struct S602 has key, drop { x: T } - struct S603 has key, drop { x: T } - struct S604 has key, drop { x: T } - struct S605 has key, drop { x: T } - struct S606 has key, drop { x: T } - struct S607 has key, drop { x: T } - struct S608 has key, drop { x: T } - struct S609 has key, drop { x: T } - struct S610 has key, drop { x: T } - struct S611 has key, drop { x: T } - struct S612 has key, drop { x: T } - struct S613 has key, drop { x: T } - struct S614 has key, drop { x: T } - struct S615 has key, drop { x: T } - struct S616 has key, drop { x: T } - struct S617 has key, drop { x: T } - struct S618 has key, drop { x: T } - struct S619 has key, drop { x: T } - struct S620 has key, drop { x: T } - struct S621 has key, drop { x: T } - struct S622 has key, drop { x: T } - struct S623 has key, drop { x: T } - struct S624 has key, drop { x: T } - struct S625 has key, drop { x: T } - struct S626 has key, drop { x: T } - struct S627 has key, drop { x: T } - struct S628 has key, drop { x: T } - struct S629 has key, drop { x: T } - struct S630 has key, drop { x: T } - struct S631 has key, drop { x: T } - struct S632 has key, drop { x: T } - struct S633 has key, drop { x: T } - struct S634 has key, drop { x: T } - struct S635 has key, drop { x: T } - struct S636 has key, drop { x: T } - struct S637 has key, drop { x: T } - struct S638 has key, drop { x: T } - struct S639 has key, drop { x: T } - struct S640 has key, drop { x: T } - struct S641 has key, drop { x: T } - struct S642 has key, drop { x: T } - struct S643 has key, drop { x: T } - struct S644 has key, drop { x: T } - struct S645 has key, drop { x: T } - struct S646 has key, drop { x: T } - struct S647 has key, drop { x: T } - struct S648 has key, drop { x: T } - struct S649 has key, drop { x: T } - struct S650 has key, drop { x: T } - struct S651 has key, drop { x: T } - struct S652 has key, drop { x: T } - struct S653 has key, drop { x: T } - struct S654 has key, drop { x: T } - struct S655 has key, drop { x: T } - struct S656 has key, drop { x: T } - struct S657 has key, drop { x: T } - struct S658 has key, drop { x: T } - struct S659 has key, drop { x: T } - struct S660 has key, drop { x: T } - struct S661 has key, drop { x: T } - struct S662 has key, drop { x: T } - struct S663 has key, drop { x: T } - struct S664 has key, drop { x: T } - struct S665 has key, drop { x: T } - struct S666 has key, drop { x: T } - struct S667 has key, drop { x: T } - struct S668 has key, drop { x: T } - struct S669 has key, drop { x: T } - struct S670 has key, drop { x: T } - struct S671 has key, drop { x: T } - struct S672 has key, drop { x: T } - struct S673 has key, drop { x: T } - struct S674 has key, drop { x: T } - struct S675 has key, drop { x: T } - struct S676 has key, drop { x: T } - struct S677 has key, drop { x: T } - struct S678 has key, drop { x: T } - struct S679 has key, drop { x: T } - struct S680 has key, drop { x: T } - struct S681 has key, drop { x: T } - struct S682 has key, drop { x: T } - struct S683 has key, drop { x: T } - struct S684 has key, drop { x: T } - struct S685 has key, drop { x: T } - struct S686 has key, drop { x: T } - struct S687 has key, drop { x: T } - struct S688 has key, drop { x: T } - struct S689 has key, drop { x: T } - struct S690 has key, drop { x: T } - struct S691 has key, drop { x: T } - struct S692 has key, drop { x: T } - struct S693 has key, drop { x: T } - struct S694 has key, drop { x: T } - struct S695 has key, drop { x: T } - struct S696 has key, drop { x: T } - struct S697 has key, drop { x: T } - struct S698 has key, drop { x: T } - struct S699 has key, drop { x: T } - struct S700 has key, drop { x: T } - struct S701 has key, drop { x: T } - struct S702 has key, drop { x: T } - struct S703 has key, drop { x: T } - struct S704 has key, drop { x: T } - struct S705 has key, drop { x: T } - struct S706 has key, drop { x: T } - struct S707 has key, drop { x: T } - struct S708 has key, drop { x: T } - struct S709 has key, drop { x: T } - struct S710 has key, drop { x: T } - struct S711 has key, drop { x: T } - struct S712 has key, drop { x: T } - struct S713 has key, drop { x: T } - struct S714 has key, drop { x: T } - struct S715 has key, drop { x: T } - struct S716 has key, drop { x: T } - struct S717 has key, drop { x: T } - struct S718 has key, drop { x: T } - struct S719 has key, drop { x: T } - struct S720 has key, drop { x: T } - struct S721 has key, drop { x: T } - struct S722 has key, drop { x: T } - struct S723 has key, drop { x: T } - struct S724 has key, drop { x: T } - struct S725 has key, drop { x: T } - struct S726 has key, drop { x: T } - struct S727 has key, drop { x: T } - struct S728 has key, drop { x: T } - struct S729 has key, drop { x: T } - struct S730 has key, drop { x: T } - struct S731 has key, drop { x: T } - struct S732 has key, drop { x: T } - struct S733 has key, drop { x: T } - struct S734 has key, drop { x: T } - struct S735 has key, drop { x: T } - struct S736 has key, drop { x: T } - struct S737 has key, drop { x: T } - struct S738 has key, drop { x: T } - struct S739 has key, drop { x: T } - struct S740 has key, drop { x: T } - struct S741 has key, drop { x: T } - struct S742 has key, drop { x: T } - struct S743 has key, drop { x: T } - struct S744 has key, drop { x: T } - struct S745 has key, drop { x: T } - struct S746 has key, drop { x: T } - struct S747 has key, drop { x: T } - struct S748 has key, drop { x: T } - struct S749 has key, drop { x: T } - struct S750 has key, drop { x: T } - struct S751 has key, drop { x: T } - struct S752 has key, drop { x: T } - struct S753 has key, drop { x: T } - struct S754 has key, drop { x: T } - struct S755 has key, drop { x: T } - struct S756 has key, drop { x: T } - struct S757 has key, drop { x: T } - struct S758 has key, drop { x: T } - struct S759 has key, drop { x: T } - struct S760 has key, drop { x: T } - struct S761 has key, drop { x: T } - struct S762 has key, drop { x: T } - struct S763 has key, drop { x: T } - struct S764 has key, drop { x: T } - struct S765 has key, drop { x: T } - struct S766 has key, drop { x: T } - struct S767 has key, drop { x: T } - struct S768 has key, drop { x: T } - struct S769 has key, drop { x: T } - struct S770 has key, drop { x: T } - struct S771 has key, drop { x: T } - struct S772 has key, drop { x: T } - struct S773 has key, drop { x: T } - struct S774 has key, drop { x: T } - struct S775 has key, drop { x: T } - struct S776 has key, drop { x: T } - struct S777 has key, drop { x: T } - struct S778 has key, drop { x: T } - struct S779 has key, drop { x: T } - struct S780 has key, drop { x: T } - struct S781 has key, drop { x: T } - struct S782 has key, drop { x: T } - struct S783 has key, drop { x: T } - struct S784 has key, drop { x: T } - struct S785 has key, drop { x: T } - struct S786 has key, drop { x: T } - struct S787 has key, drop { x: T } - struct S788 has key, drop { x: T } - struct S789 has key, drop { x: T } - struct S790 has key, drop { x: T } - struct S791 has key, drop { x: T } - struct S792 has key, drop { x: T } - struct S793 has key, drop { x: T } - struct S794 has key, drop { x: T } - struct S795 has key, drop { x: T } - struct S796 has key, drop { x: T } - struct S797 has key, drop { x: T } - struct S798 has key, drop { x: T } - struct S799 has key, drop { x: T } - struct S800 has key, drop { x: T } - struct S801 has key, drop { x: T } - struct S802 has key, drop { x: T } - struct S803 has key, drop { x: T } - struct S804 has key, drop { x: T } - struct S805 has key, drop { x: T } - struct S806 has key, drop { x: T } - struct S807 has key, drop { x: T } - struct S808 has key, drop { x: T } - struct S809 has key, drop { x: T } - struct S810 has key, drop { x: T } - struct S811 has key, drop { x: T } - struct S812 has key, drop { x: T } - struct S813 has key, drop { x: T } - struct S814 has key, drop { x: T } - struct S815 has key, drop { x: T } - struct S816 has key, drop { x: T } - struct S817 has key, drop { x: T } - struct S818 has key, drop { x: T } - struct S819 has key, drop { x: T } - struct S820 has key, drop { x: T } - struct S821 has key, drop { x: T } - struct S822 has key, drop { x: T } - struct S823 has key, drop { x: T } - struct S824 has key, drop { x: T } - struct S825 has key, drop { x: T } - struct S826 has key, drop { x: T } - struct S827 has key, drop { x: T } - struct S828 has key, drop { x: T } - struct S829 has key, drop { x: T } - struct S830 has key, drop { x: T } - struct S831 has key, drop { x: T } - struct S832 has key, drop { x: T } - struct S833 has key, drop { x: T } - struct S834 has key, drop { x: T } - struct S835 has key, drop { x: T } - struct S836 has key, drop { x: T } - struct S837 has key, drop { x: T } - struct S838 has key, drop { x: T } - struct S839 has key, drop { x: T } - struct S840 has key, drop { x: T } - struct S841 has key, drop { x: T } - struct S842 has key, drop { x: T } - struct S843 has key, drop { x: T } - struct S844 has key, drop { x: T } - struct S845 has key, drop { x: T } - struct S846 has key, drop { x: T } - struct S847 has key, drop { x: T } - struct S848 has key, drop { x: T } - struct S849 has key, drop { x: T } - struct S850 has key, drop { x: T } - struct S851 has key, drop { x: T } - struct S852 has key, drop { x: T } - struct S853 has key, drop { x: T } - struct S854 has key, drop { x: T } - struct S855 has key, drop { x: T } - struct S856 has key, drop { x: T } - struct S857 has key, drop { x: T } - struct S858 has key, drop { x: T } - struct S859 has key, drop { x: T } - struct S860 has key, drop { x: T } - struct S861 has key, drop { x: T } - struct S862 has key, drop { x: T } - struct S863 has key, drop { x: T } - struct S864 has key, drop { x: T } - struct S865 has key, drop { x: T } - struct S866 has key, drop { x: T } - struct S867 has key, drop { x: T } - struct S868 has key, drop { x: T } - struct S869 has key, drop { x: T } - struct S870 has key, drop { x: T } - struct S871 has key, drop { x: T } - struct S872 has key, drop { x: T } - struct S873 has key, drop { x: T } - struct S874 has key, drop { x: T } - struct S875 has key, drop { x: T } - struct S876 has key, drop { x: T } - struct S877 has key, drop { x: T } - struct S878 has key, drop { x: T } - struct S879 has key, drop { x: T } - struct S880 has key, drop { x: T } - struct S881 has key, drop { x: T } - struct S882 has key, drop { x: T } - struct S883 has key, drop { x: T } - struct S884 has key, drop { x: T } - struct S885 has key, drop { x: T } - struct S886 has key, drop { x: T } - struct S887 has key, drop { x: T } - struct S888 has key, drop { x: T } - struct S889 has key, drop { x: T } - struct S890 has key, drop { x: T } - struct S891 has key, drop { x: T } - struct S892 has key, drop { x: T } - struct S893 has key, drop { x: T } - struct S894 has key, drop { x: T } - struct S895 has key, drop { x: T } - struct S896 has key, drop { x: T } - struct S897 has key, drop { x: T } - struct S898 has key, drop { x: T } - struct S899 has key, drop { x: T } - struct S900 has key, drop { x: T } - struct S901 has key, drop { x: T } - struct S902 has key, drop { x: T } - struct S903 has key, drop { x: T } - struct S904 has key, drop { x: T } - struct S905 has key, drop { x: T } - struct S906 has key, drop { x: T } - struct S907 has key, drop { x: T } - struct S908 has key, drop { x: T } - struct S909 has key, drop { x: T } - struct S910 has key, drop { x: T } - struct S911 has key, drop { x: T } - struct S912 has key, drop { x: T } - struct S913 has key, drop { x: T } - struct S914 has key, drop { x: T } - struct S915 has key, drop { x: T } - struct S916 has key, drop { x: T } - struct S917 has key, drop { x: T } - struct S918 has key, drop { x: T } - struct S919 has key, drop { x: T } - struct S920 has key, drop { x: T } - struct S921 has key, drop { x: T } - struct S922 has key, drop { x: T } - struct S923 has key, drop { x: T } - struct S924 has key, drop { x: T } - struct S925 has key, drop { x: T } - struct S926 has key, drop { x: T } - struct S927 has key, drop { x: T } - struct S928 has key, drop { x: T } - struct S929 has key, drop { x: T } - struct S930 has key, drop { x: T } - struct S931 has key, drop { x: T } - struct S932 has key, drop { x: T } - struct S933 has key, drop { x: T } - struct S934 has key, drop { x: T } - struct S935 has key, drop { x: T } - struct S936 has key, drop { x: T } - struct S937 has key, drop { x: T } - struct S938 has key, drop { x: T } - struct S939 has key, drop { x: T } - struct S940 has key, drop { x: T } - struct S941 has key, drop { x: T } - struct S942 has key, drop { x: T } - struct S943 has key, drop { x: T } - struct S944 has key, drop { x: T } - struct S945 has key, drop { x: T } - struct S946 has key, drop { x: T } - struct S947 has key, drop { x: T } - struct S948 has key, drop { x: T } - struct S949 has key, drop { x: T } - struct S950 has key, drop { x: T } - struct S951 has key, drop { x: T } - struct S952 has key, drop { x: T } - struct S953 has key, drop { x: T } - struct S954 has key, drop { x: T } - struct S955 has key, drop { x: T } - struct S956 has key, drop { x: T } - struct S957 has key, drop { x: T } - struct S958 has key, drop { x: T } - struct S959 has key, drop { x: T } - struct S960 has key, drop { x: T } - struct S961 has key, drop { x: T } - struct S962 has key, drop { x: T } - struct S963 has key, drop { x: T } - struct S964 has key, drop { x: T } - struct S965 has key, drop { x: T } - struct S966 has key, drop { x: T } - struct S967 has key, drop { x: T } - struct S968 has key, drop { x: T } - struct S969 has key, drop { x: T } - struct S970 has key, drop { x: T } - struct S971 has key, drop { x: T } - struct S972 has key, drop { x: T } - struct S973 has key, drop { x: T } - struct S974 has key, drop { x: T } - struct S975 has key, drop { x: T } - struct S976 has key, drop { x: T } - struct S977 has key, drop { x: T } - struct S978 has key, drop { x: T } - struct S979 has key, drop { x: T } - struct S980 has key, drop { x: T } - struct S981 has key, drop { x: T } - struct S982 has key, drop { x: T } - struct S983 has key, drop { x: T } - struct S984 has key, drop { x: T } - struct S985 has key, drop { x: T } - struct S986 has key, drop { x: T } - struct S987 has key, drop { x: T } - struct S988 has key, drop { x: T } - struct S989 has key, drop { x: T } - struct S990 has key, drop { x: T } - struct S991 has key, drop { x: T } - struct S992 has key, drop { x: T } - struct S993 has key, drop { x: T } - struct S994 has key, drop { x: T } - struct S995 has key, drop { x: T } - struct S996 has key, drop { x: T } - struct S997 has key, drop { x: T } - struct S998 has key, drop { x: T } - struct S999 has key, drop { x: T } - struct S1000 has key, drop { x: T } - struct S1001 has key, drop { x: T } - struct S1002 has key, drop { x: T } - struct S1003 has key, drop { x: T } - struct S1004 has key, drop { x: T } - struct S1005 has key, drop { x: T } - struct S1006 has key, drop { x: T } - struct S1007 has key, drop { x: T } - struct S1008 has key, drop { x: T } - struct S1009 has key, drop { x: T } - struct S1010 has key, drop { x: T } - struct S1011 has key, drop { x: T } - struct S1012 has key, drop { x: T } - struct S1013 has key, drop { x: T } - struct S1014 has key, drop { x: T } - struct S1015 has key, drop { x: T } - struct S1016 has key, drop { x: T } - struct S1017 has key, drop { x: T } - struct S1018 has key, drop { x: T } - struct S1019 has key, drop { x: T } - struct S1020 has key, drop { x: T } - struct S1021 has key, drop { x: T } - struct S1022 has key, drop { x: T } - struct S1023 has key, drop { x: T } - struct S1024 has key, drop { x: T } - struct S1025 has key, drop { x: T } - struct S1026 has key, drop { x: T } - struct S1027 has key, drop { x: T } - struct S1028 has key, drop { x: T } - struct S1029 has key, drop { x: T } - struct S1030 has key, drop { x: T } - struct S1031 has key, drop { x: T } - struct S1032 has key, drop { x: T } - struct S1033 has key, drop { x: T } - struct S1034 has key, drop { x: T } - struct S1035 has key, drop { x: T } - struct S1036 has key, drop { x: T } - struct S1037 has key, drop { x: T } - struct S1038 has key, drop { x: T } - struct S1039 has key, drop { x: T } - struct S1040 has key, drop { x: T } - struct S1041 has key, drop { x: T } - struct S1042 has key, drop { x: T } - struct S1043 has key, drop { x: T } - struct S1044 has key, drop { x: T } - struct S1045 has key, drop { x: T } - struct S1046 has key, drop { x: T } - struct S1047 has key, drop { x: T } - struct S1048 has key, drop { x: T } - struct S1049 has key, drop { x: T } - struct S1050 has key, drop { x: T } - struct S1051 has key, drop { x: T } - struct S1052 has key, drop { x: T } - struct S1053 has key, drop { x: T } - struct S1054 has key, drop { x: T } - struct S1055 has key, drop { x: T } - struct S1056 has key, drop { x: T } - struct S1057 has key, drop { x: T } - struct S1058 has key, drop { x: T } - struct S1059 has key, drop { x: T } - struct S1060 has key, drop { x: T } - struct S1061 has key, drop { x: T } - struct S1062 has key, drop { x: T } - struct S1063 has key, drop { x: T } - struct S1064 has key, drop { x: T } - struct S1065 has key, drop { x: T } - struct S1066 has key, drop { x: T } - struct S1067 has key, drop { x: T } - struct S1068 has key, drop { x: T } - struct S1069 has key, drop { x: T } - struct S1070 has key, drop { x: T } - struct S1071 has key, drop { x: T } - struct S1072 has key, drop { x: T } - struct S1073 has key, drop { x: T } - struct S1074 has key, drop { x: T } - struct S1075 has key, drop { x: T } - struct S1076 has key, drop { x: T } - struct S1077 has key, drop { x: T } - struct S1078 has key, drop { x: T } - struct S1079 has key, drop { x: T } - struct S1080 has key, drop { x: T } - struct S1081 has key, drop { x: T } - struct S1082 has key, drop { x: T } - struct S1083 has key, drop { x: T } - struct S1084 has key, drop { x: T } - struct S1085 has key, drop { x: T } - struct S1086 has key, drop { x: T } - struct S1087 has key, drop { x: T } - struct S1088 has key, drop { x: T } - struct S1089 has key, drop { x: T } - struct S1090 has key, drop { x: T } - struct S1091 has key, drop { x: T } - struct S1092 has key, drop { x: T } - struct S1093 has key, drop { x: T } - struct S1094 has key, drop { x: T } - struct S1095 has key, drop { x: T } - struct S1096 has key, drop { x: T } - struct S1097 has key, drop { x: T } - struct S1098 has key, drop { x: T } - struct S1099 has key, drop { x: T } - struct S1100 has key, drop { x: T } - struct S1101 has key, drop { x: T } - struct S1102 has key, drop { x: T } - struct S1103 has key, drop { x: T } - struct S1104 has key, drop { x: T } - struct S1105 has key, drop { x: T } - struct S1106 has key, drop { x: T } - struct S1107 has key, drop { x: T } - struct S1108 has key, drop { x: T } - struct S1109 has key, drop { x: T } - struct S1110 has key, drop { x: T } - struct S1111 has key, drop { x: T } - struct S1112 has key, drop { x: T } - struct S1113 has key, drop { x: T } - struct S1114 has key, drop { x: T } - struct S1115 has key, drop { x: T } - struct S1116 has key, drop { x: T } - struct S1117 has key, drop { x: T } - struct S1118 has key, drop { x: T } - struct S1119 has key, drop { x: T } - struct S1120 has key, drop { x: T } - struct S1121 has key, drop { x: T } - struct S1122 has key, drop { x: T } - struct S1123 has key, drop { x: T } - struct S1124 has key, drop { x: T } - struct S1125 has key, drop { x: T } - struct S1126 has key, drop { x: T } - struct S1127 has key, drop { x: T } - struct S1128 has key, drop { x: T } - struct S1129 has key, drop { x: T } - struct S1130 has key, drop { x: T } - struct S1131 has key, drop { x: T } - struct S1132 has key, drop { x: T } - struct S1133 has key, drop { x: T } - struct S1134 has key, drop { x: T } - struct S1135 has key, drop { x: T } - struct S1136 has key, drop { x: T } - struct S1137 has key, drop { x: T } - struct S1138 has key, drop { x: T } - struct S1139 has key, drop { x: T } - struct S1140 has key, drop { x: T } - struct S1141 has key, drop { x: T } - struct S1142 has key, drop { x: T } - struct S1143 has key, drop { x: T } - struct S1144 has key, drop { x: T } - struct S1145 has key, drop { x: T } - struct S1146 has key, drop { x: T } - struct S1147 has key, drop { x: T } - struct S1148 has key, drop { x: T } - struct S1149 has key, drop { x: T } - struct S1150 has key, drop { x: T } - struct S1151 has key, drop { x: T } - struct S1152 has key, drop { x: T } - struct S1153 has key, drop { x: T } - struct S1154 has key, drop { x: T } - struct S1155 has key, drop { x: T } - struct S1156 has key, drop { x: T } - struct S1157 has key, drop { x: T } - struct S1158 has key, drop { x: T } - struct S1159 has key, drop { x: T } - struct S1160 has key, drop { x: T } - struct S1161 has key, drop { x: T } - struct S1162 has key, drop { x: T } - struct S1163 has key, drop { x: T } - struct S1164 has key, drop { x: T } - struct S1165 has key, drop { x: T } - struct S1166 has key, drop { x: T } - struct S1167 has key, drop { x: T } - struct S1168 has key, drop { x: T } - struct S1169 has key, drop { x: T } - struct S1170 has key, drop { x: T } - struct S1171 has key, drop { x: T } - struct S1172 has key, drop { x: T } - struct S1173 has key, drop { x: T } - struct S1174 has key, drop { x: T } - struct S1175 has key, drop { x: T } - struct S1176 has key, drop { x: T } - struct S1177 has key, drop { x: T } - struct S1178 has key, drop { x: T } - struct S1179 has key, drop { x: T } - struct S1180 has key, drop { x: T } - struct S1181 has key, drop { x: T } - struct S1182 has key, drop { x: T } - struct S1183 has key, drop { x: T } - struct S1184 has key, drop { x: T } - struct S1185 has key, drop { x: T } - struct S1186 has key, drop { x: T } - struct S1187 has key, drop { x: T } - struct S1188 has key, drop { x: T } - struct S1189 has key, drop { x: T } - struct S1190 has key, drop { x: T } - struct S1191 has key, drop { x: T } - struct S1192 has key, drop { x: T } - struct S1193 has key, drop { x: T } - struct S1194 has key, drop { x: T } - struct S1195 has key, drop { x: T } - struct S1196 has key, drop { x: T } - struct S1197 has key, drop { x: T } - struct S1198 has key, drop { x: T } - struct S1199 has key, drop { x: T } - struct S1200 has key, drop { x: T } - struct S1201 has key, drop { x: T } - struct S1202 has key, drop { x: T } - struct S1203 has key, drop { x: T } - struct S1204 has key, drop { x: T } - struct S1205 has key, drop { x: T } - struct S1206 has key, drop { x: T } - struct S1207 has key, drop { x: T } - struct S1208 has key, drop { x: T } - struct S1209 has key, drop { x: T } - struct S1210 has key, drop { x: T } - struct S1211 has key, drop { x: T } - struct S1212 has key, drop { x: T } - struct S1213 has key, drop { x: T } - struct S1214 has key, drop { x: T } - struct S1215 has key, drop { x: T } - struct S1216 has key, drop { x: T } - struct S1217 has key, drop { x: T } - struct S1218 has key, drop { x: T } - struct S1219 has key, drop { x: T } - struct S1220 has key, drop { x: T } - struct S1221 has key, drop { x: T } - struct S1222 has key, drop { x: T } - struct S1223 has key, drop { x: T } - struct S1224 has key, drop { x: T } - struct S1225 has key, drop { x: T } - struct S1226 has key, drop { x: T } - struct S1227 has key, drop { x: T } - struct S1228 has key, drop { x: T } - struct S1229 has key, drop { x: T } - struct S1230 has key, drop { x: T } - struct S1231 has key, drop { x: T } - struct S1232 has key, drop { x: T } - struct S1233 has key, drop { x: T } - struct S1234 has key, drop { x: T } - struct S1235 has key, drop { x: T } - struct S1236 has key, drop { x: T } - struct S1237 has key, drop { x: T } - struct S1238 has key, drop { x: T } - struct S1239 has key, drop { x: T } - struct S1240 has key, drop { x: T } - struct S1241 has key, drop { x: T } - struct S1242 has key, drop { x: T } - struct S1243 has key, drop { x: T } - struct S1244 has key, drop { x: T } - struct S1245 has key, drop { x: T } - struct S1246 has key, drop { x: T } - struct S1247 has key, drop { x: T } - struct S1248 has key, drop { x: T } - struct S1249 has key, drop { x: T } - struct S1250 has key, drop { x: T } - struct S1251 has key, drop { x: T } - struct S1252 has key, drop { x: T } - struct S1253 has key, drop { x: T } - struct S1254 has key, drop { x: T } - struct S1255 has key, drop { x: T } - struct S1256 has key, drop { x: T } - struct S1257 has key, drop { x: T } - struct S1258 has key, drop { x: T } - struct S1259 has key, drop { x: T } - struct S1260 has key, drop { x: T } - struct S1261 has key, drop { x: T } - struct S1262 has key, drop { x: T } - struct S1263 has key, drop { x: T } - struct S1264 has key, drop { x: T } - struct S1265 has key, drop { x: T } - struct S1266 has key, drop { x: T } - struct S1267 has key, drop { x: T } - struct S1268 has key, drop { x: T } - struct S1269 has key, drop { x: T } - struct S1270 has key, drop { x: T } - struct S1271 has key, drop { x: T } - struct S1272 has key, drop { x: T } - struct S1273 has key, drop { x: T } - struct S1274 has key, drop { x: T } - struct S1275 has key, drop { x: T } - struct S1276 has key, drop { x: T } - struct S1277 has key, drop { x: T } - struct S1278 has key, drop { x: T } - struct S1279 has key, drop { x: T } - struct S1280 has key, drop { x: T } - struct S1281 has key, drop { x: T } - struct S1282 has key, drop { x: T } - struct S1283 has key, drop { x: T } - struct S1284 has key, drop { x: T } - struct S1285 has key, drop { x: T } - struct S1286 has key, drop { x: T } - struct S1287 has key, drop { x: T } - struct S1288 has key, drop { x: T } - struct S1289 has key, drop { x: T } - struct S1290 has key, drop { x: T } - struct S1291 has key, drop { x: T } - struct S1292 has key, drop { x: T } - struct S1293 has key, drop { x: T } - struct S1294 has key, drop { x: T } - struct S1295 has key, drop { x: T } - struct S1296 has key, drop { x: T } - struct S1297 has key, drop { x: T } - struct S1298 has key, drop { x: T } - struct S1299 has key, drop { x: T } - struct S1300 has key, drop { x: T } - struct S1301 has key, drop { x: T } - struct S1302 has key, drop { x: T } - struct S1303 has key, drop { x: T } - struct S1304 has key, drop { x: T } - struct S1305 has key, drop { x: T } - struct S1306 has key, drop { x: T } - struct S1307 has key, drop { x: T } - struct S1308 has key, drop { x: T } - struct S1309 has key, drop { x: T } - struct S1310 has key, drop { x: T } - struct S1311 has key, drop { x: T } - struct S1312 has key, drop { x: T } - struct S1313 has key, drop { x: T } - struct S1314 has key, drop { x: T } - struct S1315 has key, drop { x: T } - struct S1316 has key, drop { x: T } - struct S1317 has key, drop { x: T } - struct S1318 has key, drop { x: T } - struct S1319 has key, drop { x: T } - struct S1320 has key, drop { x: T } - struct S1321 has key, drop { x: T } - struct S1322 has key, drop { x: T } - struct S1323 has key, drop { x: T } - struct S1324 has key, drop { x: T } - struct S1325 has key, drop { x: T } - struct S1326 has key, drop { x: T } - struct S1327 has key, drop { x: T } - struct S1328 has key, drop { x: T } - struct S1329 has key, drop { x: T } - struct S1330 has key, drop { x: T } - struct S1331 has key, drop { x: T } - struct S1332 has key, drop { x: T } - struct S1333 has key, drop { x: T } - struct S1334 has key, drop { x: T } - struct S1335 has key, drop { x: T } - struct S1336 has key, drop { x: T } - struct S1337 has key, drop { x: T } - struct S1338 has key, drop { x: T } - struct S1339 has key, drop { x: T } - struct S1340 has key, drop { x: T } - struct S1341 has key, drop { x: T } - struct S1342 has key, drop { x: T } - struct S1343 has key, drop { x: T } - struct S1344 has key, drop { x: T } - struct S1345 has key, drop { x: T } - struct S1346 has key, drop { x: T } - struct S1347 has key, drop { x: T } - struct S1348 has key, drop { x: T } - struct S1349 has key, drop { x: T } - struct S1350 has key, drop { x: T } - struct S1351 has key, drop { x: T } - struct S1352 has key, drop { x: T } - struct S1353 has key, drop { x: T } - struct S1354 has key, drop { x: T } - struct S1355 has key, drop { x: T } - struct S1356 has key, drop { x: T } - struct S1357 has key, drop { x: T } - struct S1358 has key, drop { x: T } - struct S1359 has key, drop { x: T } - struct S1360 has key, drop { x: T } - struct S1361 has key, drop { x: T } - struct S1362 has key, drop { x: T } - struct S1363 has key, drop { x: T } - struct S1364 has key, drop { x: T } - struct S1365 has key, drop { x: T } - struct S1366 has key, drop { x: T } - struct S1367 has key, drop { x: T } - struct S1368 has key, drop { x: T } - struct S1369 has key, drop { x: T } - struct S1370 has key, drop { x: T } - struct S1371 has key, drop { x: T } - struct S1372 has key, drop { x: T } - struct S1373 has key, drop { x: T } - struct S1374 has key, drop { x: T } - struct S1375 has key, drop { x: T } - struct S1376 has key, drop { x: T } - struct S1377 has key, drop { x: T } - struct S1378 has key, drop { x: T } - struct S1379 has key, drop { x: T } - struct S1380 has key, drop { x: T } - struct S1381 has key, drop { x: T } - struct S1382 has key, drop { x: T } - struct S1383 has key, drop { x: T } - struct S1384 has key, drop { x: T } - struct S1385 has key, drop { x: T } - struct S1386 has key, drop { x: T } - struct S1387 has key, drop { x: T } - struct S1388 has key, drop { x: T } - struct S1389 has key, drop { x: T } - struct S1390 has key, drop { x: T } - struct S1391 has key, drop { x: T } - struct S1392 has key, drop { x: T } - struct S1393 has key, drop { x: T } - struct S1394 has key, drop { x: T } - struct S1395 has key, drop { x: T } - struct S1396 has key, drop { x: T } - struct S1397 has key, drop { x: T } - struct S1398 has key, drop { x: T } - struct S1399 has key, drop { x: T } - struct S1400 has key, drop { x: T } - struct S1401 has key, drop { x: T } - struct S1402 has key, drop { x: T } - struct S1403 has key, drop { x: T } - struct S1404 has key, drop { x: T } - struct S1405 has key, drop { x: T } - struct S1406 has key, drop { x: T } - struct S1407 has key, drop { x: T } - struct S1408 has key, drop { x: T } - struct S1409 has key, drop { x: T } - struct S1410 has key, drop { x: T } - struct S1411 has key, drop { x: T } - struct S1412 has key, drop { x: T } - struct S1413 has key, drop { x: T } - struct S1414 has key, drop { x: T } - struct S1415 has key, drop { x: T } - struct S1416 has key, drop { x: T } - struct S1417 has key, drop { x: T } - struct S1418 has key, drop { x: T } - struct S1419 has key, drop { x: T } - struct S1420 has key, drop { x: T } - struct S1421 has key, drop { x: T } - struct S1422 has key, drop { x: T } - struct S1423 has key, drop { x: T } - struct S1424 has key, drop { x: T } - struct S1425 has key, drop { x: T } - struct S1426 has key, drop { x: T } - struct S1427 has key, drop { x: T } - struct S1428 has key, drop { x: T } - struct S1429 has key, drop { x: T } - struct S1430 has key, drop { x: T } - struct S1431 has key, drop { x: T } - struct S1432 has key, drop { x: T } - struct S1433 has key, drop { x: T } - struct S1434 has key, drop { x: T } - struct S1435 has key, drop { x: T } - struct S1436 has key, drop { x: T } - struct S1437 has key, drop { x: T } - struct S1438 has key, drop { x: T } - struct S1439 has key, drop { x: T } - struct S1440 has key, drop { x: T } - struct S1441 has key, drop { x: T } - struct S1442 has key, drop { x: T } - struct S1443 has key, drop { x: T } - struct S1444 has key, drop { x: T } - struct S1445 has key, drop { x: T } - struct S1446 has key, drop { x: T } - struct S1447 has key, drop { x: T } - struct S1448 has key, drop { x: T } - struct S1449 has key, drop { x: T } - struct S1450 has key, drop { x: T } - struct S1451 has key, drop { x: T } - struct S1452 has key, drop { x: T } - struct S1453 has key, drop { x: T } - struct S1454 has key, drop { x: T } - struct S1455 has key, drop { x: T } - struct S1456 has key, drop { x: T } - struct S1457 has key, drop { x: T } - struct S1458 has key, drop { x: T } - struct S1459 has key, drop { x: T } - struct S1460 has key, drop { x: T } - struct S1461 has key, drop { x: T } - struct S1462 has key, drop { x: T } - struct S1463 has key, drop { x: T } - struct S1464 has key, drop { x: T } - struct S1465 has key, drop { x: T } - struct S1466 has key, drop { x: T } - struct S1467 has key, drop { x: T } - struct S1468 has key, drop { x: T } - struct S1469 has key, drop { x: T } - struct S1470 has key, drop { x: T } - struct S1471 has key, drop { x: T } - struct S1472 has key, drop { x: T } - struct S1473 has key, drop { x: T } - struct S1474 has key, drop { x: T } - struct S1475 has key, drop { x: T } - struct S1476 has key, drop { x: T } - struct S1477 has key, drop { x: T } - struct S1478 has key, drop { x: T } - struct S1479 has key, drop { x: T } - struct S1480 has key, drop { x: T } - struct S1481 has key, drop { x: T } - struct S1482 has key, drop { x: T } - struct S1483 has key, drop { x: T } - struct S1484 has key, drop { x: T } - struct S1485 has key, drop { x: T } - struct S1486 has key, drop { x: T } - struct S1487 has key, drop { x: T } - struct S1488 has key, drop { x: T } - struct S1489 has key, drop { x: T } - struct S1490 has key, drop { x: T } - struct S1491 has key, drop { x: T } - struct S1492 has key, drop { x: T } - struct S1493 has key, drop { x: T } - struct S1494 has key, drop { x: T } - struct S1495 has key, drop { x: T } - struct S1496 has key, drop { x: T } - struct S1497 has key, drop { x: T } - struct S1498 has key, drop { x: T } - struct S1499 has key, drop { x: T } - struct S1500 has key, drop { x: T } - struct S1501 has key, drop { x: T } - struct S1502 has key, drop { x: T } - struct S1503 has key, drop { x: T } - struct S1504 has key, drop { x: T } - struct S1505 has key, drop { x: T } - struct S1506 has key, drop { x: T } - struct S1507 has key, drop { x: T } - struct S1508 has key, drop { x: T } - struct S1509 has key, drop { x: T } - struct S1510 has key, drop { x: T } - struct S1511 has key, drop { x: T } - struct S1512 has key, drop { x: T } - struct S1513 has key, drop { x: T } - struct S1514 has key, drop { x: T } - struct S1515 has key, drop { x: T } - struct S1516 has key, drop { x: T } - struct S1517 has key, drop { x: T } - struct S1518 has key, drop { x: T } - struct S1519 has key, drop { x: T } - struct S1520 has key, drop { x: T } - struct S1521 has key, drop { x: T } - struct S1522 has key, drop { x: T } - struct S1523 has key, drop { x: T } - struct S1524 has key, drop { x: T } - struct S1525 has key, drop { x: T } - struct S1526 has key, drop { x: T } - struct S1527 has key, drop { x: T } - struct S1528 has key, drop { x: T } - struct S1529 has key, drop { x: T } - struct S1530 has key, drop { x: T } - struct S1531 has key, drop { x: T } - struct S1532 has key, drop { x: T } - struct S1533 has key, drop { x: T } - struct S1534 has key, drop { x: T } - struct S1535 has key, drop { x: T } - struct S1536 has key, drop { x: T } - struct S1537 has key, drop { x: T } - struct S1538 has key, drop { x: T } - struct S1539 has key, drop { x: T } - struct S1540 has key, drop { x: T } - struct S1541 has key, drop { x: T } - struct S1542 has key, drop { x: T } - struct S1543 has key, drop { x: T } - struct S1544 has key, drop { x: T } - struct S1545 has key, drop { x: T } - struct S1546 has key, drop { x: T } - struct S1547 has key, drop { x: T } - struct S1548 has key, drop { x: T } - struct S1549 has key, drop { x: T } - struct S1550 has key, drop { x: T } - struct S1551 has key, drop { x: T } - struct S1552 has key, drop { x: T } - struct S1553 has key, drop { x: T } - struct S1554 has key, drop { x: T } - struct S1555 has key, drop { x: T } - struct S1556 has key, drop { x: T } - struct S1557 has key, drop { x: T } - struct S1558 has key, drop { x: T } - struct S1559 has key, drop { x: T } - struct S1560 has key, drop { x: T } - struct S1561 has key, drop { x: T } - struct S1562 has key, drop { x: T } - struct S1563 has key, drop { x: T } - struct S1564 has key, drop { x: T } - struct S1565 has key, drop { x: T } - struct S1566 has key, drop { x: T } - struct S1567 has key, drop { x: T } - struct S1568 has key, drop { x: T } - struct S1569 has key, drop { x: T } - struct S1570 has key, drop { x: T } - struct S1571 has key, drop { x: T } - struct S1572 has key, drop { x: T } - struct S1573 has key, drop { x: T } - struct S1574 has key, drop { x: T } - struct S1575 has key, drop { x: T } - struct S1576 has key, drop { x: T } - struct S1577 has key, drop { x: T } - struct S1578 has key, drop { x: T } - struct S1579 has key, drop { x: T } - struct S1580 has key, drop { x: T } - struct S1581 has key, drop { x: T } - struct S1582 has key, drop { x: T } - struct S1583 has key, drop { x: T } - struct S1584 has key, drop { x: T } - struct S1585 has key, drop { x: T } - struct S1586 has key, drop { x: T } - struct S1587 has key, drop { x: T } - struct S1588 has key, drop { x: T } - struct S1589 has key, drop { x: T } - struct S1590 has key, drop { x: T } - struct S1591 has key, drop { x: T } - struct S1592 has key, drop { x: T } - struct S1593 has key, drop { x: T } - struct S1594 has key, drop { x: T } - struct S1595 has key, drop { x: T } - struct S1596 has key, drop { x: T } - struct S1597 has key, drop { x: T } - struct S1598 has key, drop { x: T } - struct S1599 has key, drop { x: T } - struct S1600 has key, drop { x: T } - - public entry calibrate_move_to_generic_x100(s: signer) { - label b0: - move_to>(&s, S1{ x: 0 }); - move_to>(&s, S2{ x: 0 }); - move_to>(&s, S3{ x: 0 }); - move_to>(&s, S4{ x: 0 }); - move_to>(&s, S5{ x: 0 }); - move_to>(&s, S6{ x: 0 }); - move_to>(&s, S7{ x: 0 }); - move_to>(&s, S8{ x: 0 }); - move_to>(&s, S9{ x: 0 }); - move_to>(&s, S10{ x: 0 }); - move_to>(&s, S11{ x: 0 }); - move_to>(&s, S12{ x: 0 }); - move_to>(&s, S13{ x: 0 }); - move_to>(&s, S14{ x: 0 }); - move_to>(&s, S15{ x: 0 }); - move_to>(&s, S16{ x: 0 }); - move_to>(&s, S17{ x: 0 }); - move_to>(&s, S18{ x: 0 }); - move_to>(&s, S19{ x: 0 }); - move_to>(&s, S20{ x: 0 }); - move_to>(&s, S21{ x: 0 }); - move_to>(&s, S22{ x: 0 }); - move_to>(&s, S23{ x: 0 }); - move_to>(&s, S24{ x: 0 }); - move_to>(&s, S25{ x: 0 }); - move_to>(&s, S26{ x: 0 }); - move_to>(&s, S27{ x: 0 }); - move_to>(&s, S28{ x: 0 }); - move_to>(&s, S29{ x: 0 }); - move_to>(&s, S30{ x: 0 }); - move_to>(&s, S31{ x: 0 }); - move_to>(&s, S32{ x: 0 }); - move_to>(&s, S33{ x: 0 }); - move_to>(&s, S34{ x: 0 }); - move_to>(&s, S35{ x: 0 }); - move_to>(&s, S36{ x: 0 }); - move_to>(&s, S37{ x: 0 }); - move_to>(&s, S38{ x: 0 }); - move_to>(&s, S39{ x: 0 }); - move_to>(&s, S40{ x: 0 }); - move_to>(&s, S41{ x: 0 }); - move_to>(&s, S42{ x: 0 }); - move_to>(&s, S43{ x: 0 }); - move_to>(&s, S44{ x: 0 }); - move_to>(&s, S45{ x: 0 }); - move_to>(&s, S46{ x: 0 }); - move_to>(&s, S47{ x: 0 }); - move_to>(&s, S48{ x: 0 }); - move_to>(&s, S49{ x: 0 }); - move_to>(&s, S50{ x: 0 }); - move_to>(&s, S51{ x: 0 }); - move_to>(&s, S52{ x: 0 }); - move_to>(&s, S53{ x: 0 }); - move_to>(&s, S54{ x: 0 }); - move_to>(&s, S55{ x: 0 }); - move_to>(&s, S56{ x: 0 }); - move_to>(&s, S57{ x: 0 }); - move_to>(&s, S58{ x: 0 }); - move_to>(&s, S59{ x: 0 }); - move_to>(&s, S60{ x: 0 }); - move_to>(&s, S61{ x: 0 }); - move_to>(&s, S62{ x: 0 }); - move_to>(&s, S63{ x: 0 }); - move_to>(&s, S64{ x: 0 }); - move_to>(&s, S65{ x: 0 }); - move_to>(&s, S66{ x: 0 }); - move_to>(&s, S67{ x: 0 }); - move_to>(&s, S68{ x: 0 }); - move_to>(&s, S69{ x: 0 }); - move_to>(&s, S70{ x: 0 }); - move_to>(&s, S71{ x: 0 }); - move_to>(&s, S72{ x: 0 }); - move_to>(&s, S73{ x: 0 }); - move_to>(&s, S74{ x: 0 }); - move_to>(&s, S75{ x: 0 }); - move_to>(&s, S76{ x: 0 }); - move_to>(&s, S77{ x: 0 }); - move_to>(&s, S78{ x: 0 }); - move_to>(&s, S79{ x: 0 }); - move_to>(&s, S80{ x: 0 }); - move_to>(&s, S81{ x: 0 }); - move_to>(&s, S82{ x: 0 }); - move_to>(&s, S83{ x: 0 }); - move_to>(&s, S84{ x: 0 }); - move_to>(&s, S85{ x: 0 }); - move_to>(&s, S86{ x: 0 }); - move_to>(&s, S87{ x: 0 }); - move_to>(&s, S88{ x: 0 }); - move_to>(&s, S89{ x: 0 }); - move_to>(&s, S90{ x: 0 }); - move_to>(&s, S91{ x: 0 }); - move_to>(&s, S92{ x: 0 }); - move_to>(&s, S93{ x: 0 }); - move_to>(&s, S94{ x: 0 }); - move_to>(&s, S95{ x: 0 }); - move_to>(&s, S96{ x: 0 }); - move_to>(&s, S97{ x: 0 }); - move_to>(&s, S98{ x: 0 }); - move_to>(&s, S99{ x: 0 }); - move_to>(&s, S100{ x: 0 }); - return; - } - - public entry calibrate_move_to_generic_x500(s: signer) { - label b0: - move_to>(&s, S101{ x: 0 }); - move_to>(&s, S102{ x: 0 }); - move_to>(&s, S103{ x: 0 }); - move_to>(&s, S104{ x: 0 }); - move_to>(&s, S105{ x: 0 }); - move_to>(&s, S106{ x: 0 }); - move_to>(&s, S107{ x: 0 }); - move_to>(&s, S108{ x: 0 }); - move_to>(&s, S109{ x: 0 }); - move_to>(&s, S110{ x: 0 }); - move_to>(&s, S111{ x: 0 }); - move_to>(&s, S112{ x: 0 }); - move_to>(&s, S113{ x: 0 }); - move_to>(&s, S114{ x: 0 }); - move_to>(&s, S115{ x: 0 }); - move_to>(&s, S116{ x: 0 }); - move_to>(&s, S117{ x: 0 }); - move_to>(&s, S118{ x: 0 }); - move_to>(&s, S119{ x: 0 }); - move_to>(&s, S120{ x: 0 }); - move_to>(&s, S121{ x: 0 }); - move_to>(&s, S122{ x: 0 }); - move_to>(&s, S123{ x: 0 }); - move_to>(&s, S124{ x: 0 }); - move_to>(&s, S125{ x: 0 }); - move_to>(&s, S126{ x: 0 }); - move_to>(&s, S127{ x: 0 }); - move_to>(&s, S128{ x: 0 }); - move_to>(&s, S129{ x: 0 }); - move_to>(&s, S130{ x: 0 }); - move_to>(&s, S131{ x: 0 }); - move_to>(&s, S132{ x: 0 }); - move_to>(&s, S133{ x: 0 }); - move_to>(&s, S134{ x: 0 }); - move_to>(&s, S135{ x: 0 }); - move_to>(&s, S136{ x: 0 }); - move_to>(&s, S137{ x: 0 }); - move_to>(&s, S138{ x: 0 }); - move_to>(&s, S139{ x: 0 }); - move_to>(&s, S140{ x: 0 }); - move_to>(&s, S141{ x: 0 }); - move_to>(&s, S142{ x: 0 }); - move_to>(&s, S143{ x: 0 }); - move_to>(&s, S144{ x: 0 }); - move_to>(&s, S145{ x: 0 }); - move_to>(&s, S146{ x: 0 }); - move_to>(&s, S147{ x: 0 }); - move_to>(&s, S148{ x: 0 }); - move_to>(&s, S149{ x: 0 }); - move_to>(&s, S150{ x: 0 }); - move_to>(&s, S151{ x: 0 }); - move_to>(&s, S152{ x: 0 }); - move_to>(&s, S153{ x: 0 }); - move_to>(&s, S154{ x: 0 }); - move_to>(&s, S155{ x: 0 }); - move_to>(&s, S156{ x: 0 }); - move_to>(&s, S157{ x: 0 }); - move_to>(&s, S158{ x: 0 }); - move_to>(&s, S159{ x: 0 }); - move_to>(&s, S160{ x: 0 }); - move_to>(&s, S161{ x: 0 }); - move_to>(&s, S162{ x: 0 }); - move_to>(&s, S163{ x: 0 }); - move_to>(&s, S164{ x: 0 }); - move_to>(&s, S165{ x: 0 }); - move_to>(&s, S166{ x: 0 }); - move_to>(&s, S167{ x: 0 }); - move_to>(&s, S168{ x: 0 }); - move_to>(&s, S169{ x: 0 }); - move_to>(&s, S170{ x: 0 }); - move_to>(&s, S171{ x: 0 }); - move_to>(&s, S172{ x: 0 }); - move_to>(&s, S173{ x: 0 }); - move_to>(&s, S174{ x: 0 }); - move_to>(&s, S175{ x: 0 }); - move_to>(&s, S176{ x: 0 }); - move_to>(&s, S177{ x: 0 }); - move_to>(&s, S178{ x: 0 }); - move_to>(&s, S179{ x: 0 }); - move_to>(&s, S180{ x: 0 }); - move_to>(&s, S181{ x: 0 }); - move_to>(&s, S182{ x: 0 }); - move_to>(&s, S183{ x: 0 }); - move_to>(&s, S184{ x: 0 }); - move_to>(&s, S185{ x: 0 }); - move_to>(&s, S186{ x: 0 }); - move_to>(&s, S187{ x: 0 }); - move_to>(&s, S188{ x: 0 }); - move_to>(&s, S189{ x: 0 }); - move_to>(&s, S190{ x: 0 }); - move_to>(&s, S191{ x: 0 }); - move_to>(&s, S192{ x: 0 }); - move_to>(&s, S193{ x: 0 }); - move_to>(&s, S194{ x: 0 }); - move_to>(&s, S195{ x: 0 }); - move_to>(&s, S196{ x: 0 }); - move_to>(&s, S197{ x: 0 }); - move_to>(&s, S198{ x: 0 }); - move_to>(&s, S199{ x: 0 }); - move_to>(&s, S200{ x: 0 }); - move_to>(&s, S201{ x: 0 }); - move_to>(&s, S202{ x: 0 }); - move_to>(&s, S203{ x: 0 }); - move_to>(&s, S204{ x: 0 }); - move_to>(&s, S205{ x: 0 }); - move_to>(&s, S206{ x: 0 }); - move_to>(&s, S207{ x: 0 }); - move_to>(&s, S208{ x: 0 }); - move_to>(&s, S209{ x: 0 }); - move_to>(&s, S210{ x: 0 }); - move_to>(&s, S211{ x: 0 }); - move_to>(&s, S212{ x: 0 }); - move_to>(&s, S213{ x: 0 }); - move_to>(&s, S214{ x: 0 }); - move_to>(&s, S215{ x: 0 }); - move_to>(&s, S216{ x: 0 }); - move_to>(&s, S217{ x: 0 }); - move_to>(&s, S218{ x: 0 }); - move_to>(&s, S219{ x: 0 }); - move_to>(&s, S220{ x: 0 }); - move_to>(&s, S221{ x: 0 }); - move_to>(&s, S222{ x: 0 }); - move_to>(&s, S223{ x: 0 }); - move_to>(&s, S224{ x: 0 }); - move_to>(&s, S225{ x: 0 }); - move_to>(&s, S226{ x: 0 }); - move_to>(&s, S227{ x: 0 }); - move_to>(&s, S228{ x: 0 }); - move_to>(&s, S229{ x: 0 }); - move_to>(&s, S230{ x: 0 }); - move_to>(&s, S231{ x: 0 }); - move_to>(&s, S232{ x: 0 }); - move_to>(&s, S233{ x: 0 }); - move_to>(&s, S234{ x: 0 }); - move_to>(&s, S235{ x: 0 }); - move_to>(&s, S236{ x: 0 }); - move_to>(&s, S237{ x: 0 }); - move_to>(&s, S238{ x: 0 }); - move_to>(&s, S239{ x: 0 }); - move_to>(&s, S240{ x: 0 }); - move_to>(&s, S241{ x: 0 }); - move_to>(&s, S242{ x: 0 }); - move_to>(&s, S243{ x: 0 }); - move_to>(&s, S244{ x: 0 }); - move_to>(&s, S245{ x: 0 }); - move_to>(&s, S246{ x: 0 }); - move_to>(&s, S247{ x: 0 }); - move_to>(&s, S248{ x: 0 }); - move_to>(&s, S249{ x: 0 }); - move_to>(&s, S250{ x: 0 }); - move_to>(&s, S251{ x: 0 }); - move_to>(&s, S252{ x: 0 }); - move_to>(&s, S253{ x: 0 }); - move_to>(&s, S254{ x: 0 }); - move_to>(&s, S255{ x: 0 }); - move_to>(&s, S256{ x: 0 }); - move_to>(&s, S257{ x: 0 }); - move_to>(&s, S258{ x: 0 }); - move_to>(&s, S259{ x: 0 }); - move_to>(&s, S260{ x: 0 }); - move_to>(&s, S261{ x: 0 }); - move_to>(&s, S262{ x: 0 }); - move_to>(&s, S263{ x: 0 }); - move_to>(&s, S264{ x: 0 }); - move_to>(&s, S265{ x: 0 }); - move_to>(&s, S266{ x: 0 }); - move_to>(&s, S267{ x: 0 }); - move_to>(&s, S268{ x: 0 }); - move_to>(&s, S269{ x: 0 }); - move_to>(&s, S270{ x: 0 }); - move_to>(&s, S271{ x: 0 }); - move_to>(&s, S272{ x: 0 }); - move_to>(&s, S273{ x: 0 }); - move_to>(&s, S274{ x: 0 }); - move_to>(&s, S275{ x: 0 }); - move_to>(&s, S276{ x: 0 }); - move_to>(&s, S277{ x: 0 }); - move_to>(&s, S278{ x: 0 }); - move_to>(&s, S279{ x: 0 }); - move_to>(&s, S280{ x: 0 }); - move_to>(&s, S281{ x: 0 }); - move_to>(&s, S282{ x: 0 }); - move_to>(&s, S283{ x: 0 }); - move_to>(&s, S284{ x: 0 }); - move_to>(&s, S285{ x: 0 }); - move_to>(&s, S286{ x: 0 }); - move_to>(&s, S287{ x: 0 }); - move_to>(&s, S288{ x: 0 }); - move_to>(&s, S289{ x: 0 }); - move_to>(&s, S290{ x: 0 }); - move_to>(&s, S291{ x: 0 }); - move_to>(&s, S292{ x: 0 }); - move_to>(&s, S293{ x: 0 }); - move_to>(&s, S294{ x: 0 }); - move_to>(&s, S295{ x: 0 }); - move_to>(&s, S296{ x: 0 }); - move_to>(&s, S297{ x: 0 }); - move_to>(&s, S298{ x: 0 }); - move_to>(&s, S299{ x: 0 }); - move_to>(&s, S300{ x: 0 }); - move_to>(&s, S301{ x: 0 }); - move_to>(&s, S302{ x: 0 }); - move_to>(&s, S303{ x: 0 }); - move_to>(&s, S304{ x: 0 }); - move_to>(&s, S305{ x: 0 }); - move_to>(&s, S306{ x: 0 }); - move_to>(&s, S307{ x: 0 }); - move_to>(&s, S308{ x: 0 }); - move_to>(&s, S309{ x: 0 }); - move_to>(&s, S310{ x: 0 }); - move_to>(&s, S311{ x: 0 }); - move_to>(&s, S312{ x: 0 }); - move_to>(&s, S313{ x: 0 }); - move_to>(&s, S314{ x: 0 }); - move_to>(&s, S315{ x: 0 }); - move_to>(&s, S316{ x: 0 }); - move_to>(&s, S317{ x: 0 }); - move_to>(&s, S318{ x: 0 }); - move_to>(&s, S319{ x: 0 }); - move_to>(&s, S320{ x: 0 }); - move_to>(&s, S321{ x: 0 }); - move_to>(&s, S322{ x: 0 }); - move_to>(&s, S323{ x: 0 }); - move_to>(&s, S324{ x: 0 }); - move_to>(&s, S325{ x: 0 }); - move_to>(&s, S326{ x: 0 }); - move_to>(&s, S327{ x: 0 }); - move_to>(&s, S328{ x: 0 }); - move_to>(&s, S329{ x: 0 }); - move_to>(&s, S330{ x: 0 }); - move_to>(&s, S331{ x: 0 }); - move_to>(&s, S332{ x: 0 }); - move_to>(&s, S333{ x: 0 }); - move_to>(&s, S334{ x: 0 }); - move_to>(&s, S335{ x: 0 }); - move_to>(&s, S336{ x: 0 }); - move_to>(&s, S337{ x: 0 }); - move_to>(&s, S338{ x: 0 }); - move_to>(&s, S339{ x: 0 }); - move_to>(&s, S340{ x: 0 }); - move_to>(&s, S341{ x: 0 }); - move_to>(&s, S342{ x: 0 }); - move_to>(&s, S343{ x: 0 }); - move_to>(&s, S344{ x: 0 }); - move_to>(&s, S345{ x: 0 }); - move_to>(&s, S346{ x: 0 }); - move_to>(&s, S347{ x: 0 }); - move_to>(&s, S348{ x: 0 }); - move_to>(&s, S349{ x: 0 }); - move_to>(&s, S350{ x: 0 }); - move_to>(&s, S351{ x: 0 }); - move_to>(&s, S352{ x: 0 }); - move_to>(&s, S353{ x: 0 }); - move_to>(&s, S354{ x: 0 }); - move_to>(&s, S355{ x: 0 }); - move_to>(&s, S356{ x: 0 }); - move_to>(&s, S357{ x: 0 }); - move_to>(&s, S358{ x: 0 }); - move_to>(&s, S359{ x: 0 }); - move_to>(&s, S360{ x: 0 }); - move_to>(&s, S361{ x: 0 }); - move_to>(&s, S362{ x: 0 }); - move_to>(&s, S363{ x: 0 }); - move_to>(&s, S364{ x: 0 }); - move_to>(&s, S365{ x: 0 }); - move_to>(&s, S366{ x: 0 }); - move_to>(&s, S367{ x: 0 }); - move_to>(&s, S368{ x: 0 }); - move_to>(&s, S369{ x: 0 }); - move_to>(&s, S370{ x: 0 }); - move_to>(&s, S371{ x: 0 }); - move_to>(&s, S372{ x: 0 }); - move_to>(&s, S373{ x: 0 }); - move_to>(&s, S374{ x: 0 }); - move_to>(&s, S375{ x: 0 }); - move_to>(&s, S376{ x: 0 }); - move_to>(&s, S377{ x: 0 }); - move_to>(&s, S378{ x: 0 }); - move_to>(&s, S379{ x: 0 }); - move_to>(&s, S380{ x: 0 }); - move_to>(&s, S381{ x: 0 }); - move_to>(&s, S382{ x: 0 }); - move_to>(&s, S383{ x: 0 }); - move_to>(&s, S384{ x: 0 }); - move_to>(&s, S385{ x: 0 }); - move_to>(&s, S386{ x: 0 }); - move_to>(&s, S387{ x: 0 }); - move_to>(&s, S388{ x: 0 }); - move_to>(&s, S389{ x: 0 }); - move_to>(&s, S390{ x: 0 }); - move_to>(&s, S391{ x: 0 }); - move_to>(&s, S392{ x: 0 }); - move_to>(&s, S393{ x: 0 }); - move_to>(&s, S394{ x: 0 }); - move_to>(&s, S395{ x: 0 }); - move_to>(&s, S396{ x: 0 }); - move_to>(&s, S397{ x: 0 }); - move_to>(&s, S398{ x: 0 }); - move_to>(&s, S399{ x: 0 }); - move_to>(&s, S400{ x: 0 }); - move_to>(&s, S401{ x: 0 }); - move_to>(&s, S402{ x: 0 }); - move_to>(&s, S403{ x: 0 }); - move_to>(&s, S404{ x: 0 }); - move_to>(&s, S405{ x: 0 }); - move_to>(&s, S406{ x: 0 }); - move_to>(&s, S407{ x: 0 }); - move_to>(&s, S408{ x: 0 }); - move_to>(&s, S409{ x: 0 }); - move_to>(&s, S410{ x: 0 }); - move_to>(&s, S411{ x: 0 }); - move_to>(&s, S412{ x: 0 }); - move_to>(&s, S413{ x: 0 }); - move_to>(&s, S414{ x: 0 }); - move_to>(&s, S415{ x: 0 }); - move_to>(&s, S416{ x: 0 }); - move_to>(&s, S417{ x: 0 }); - move_to>(&s, S418{ x: 0 }); - move_to>(&s, S419{ x: 0 }); - move_to>(&s, S420{ x: 0 }); - move_to>(&s, S421{ x: 0 }); - move_to>(&s, S422{ x: 0 }); - move_to>(&s, S423{ x: 0 }); - move_to>(&s, S424{ x: 0 }); - move_to>(&s, S425{ x: 0 }); - move_to>(&s, S426{ x: 0 }); - move_to>(&s, S427{ x: 0 }); - move_to>(&s, S428{ x: 0 }); - move_to>(&s, S429{ x: 0 }); - move_to>(&s, S430{ x: 0 }); - move_to>(&s, S431{ x: 0 }); - move_to>(&s, S432{ x: 0 }); - move_to>(&s, S433{ x: 0 }); - move_to>(&s, S434{ x: 0 }); - move_to>(&s, S435{ x: 0 }); - move_to>(&s, S436{ x: 0 }); - move_to>(&s, S437{ x: 0 }); - move_to>(&s, S438{ x: 0 }); - move_to>(&s, S439{ x: 0 }); - move_to>(&s, S440{ x: 0 }); - move_to>(&s, S441{ x: 0 }); - move_to>(&s, S442{ x: 0 }); - move_to>(&s, S443{ x: 0 }); - move_to>(&s, S444{ x: 0 }); - move_to>(&s, S445{ x: 0 }); - move_to>(&s, S446{ x: 0 }); - move_to>(&s, S447{ x: 0 }); - move_to>(&s, S448{ x: 0 }); - move_to>(&s, S449{ x: 0 }); - move_to>(&s, S450{ x: 0 }); - move_to>(&s, S451{ x: 0 }); - move_to>(&s, S452{ x: 0 }); - move_to>(&s, S453{ x: 0 }); - move_to>(&s, S454{ x: 0 }); - move_to>(&s, S455{ x: 0 }); - move_to>(&s, S456{ x: 0 }); - move_to>(&s, S457{ x: 0 }); - move_to>(&s, S458{ x: 0 }); - move_to>(&s, S459{ x: 0 }); - move_to>(&s, S460{ x: 0 }); - move_to>(&s, S461{ x: 0 }); - move_to>(&s, S462{ x: 0 }); - move_to>(&s, S463{ x: 0 }); - move_to>(&s, S464{ x: 0 }); - move_to>(&s, S465{ x: 0 }); - move_to>(&s, S466{ x: 0 }); - move_to>(&s, S467{ x: 0 }); - move_to>(&s, S468{ x: 0 }); - move_to>(&s, S469{ x: 0 }); - move_to>(&s, S470{ x: 0 }); - move_to>(&s, S471{ x: 0 }); - move_to>(&s, S472{ x: 0 }); - move_to>(&s, S473{ x: 0 }); - move_to>(&s, S474{ x: 0 }); - move_to>(&s, S475{ x: 0 }); - move_to>(&s, S476{ x: 0 }); - move_to>(&s, S477{ x: 0 }); - move_to>(&s, S478{ x: 0 }); - move_to>(&s, S479{ x: 0 }); - move_to>(&s, S480{ x: 0 }); - move_to>(&s, S481{ x: 0 }); - move_to>(&s, S482{ x: 0 }); - move_to>(&s, S483{ x: 0 }); - move_to>(&s, S484{ x: 0 }); - move_to>(&s, S485{ x: 0 }); - move_to>(&s, S486{ x: 0 }); - move_to>(&s, S487{ x: 0 }); - move_to>(&s, S488{ x: 0 }); - move_to>(&s, S489{ x: 0 }); - move_to>(&s, S490{ x: 0 }); - move_to>(&s, S491{ x: 0 }); - move_to>(&s, S492{ x: 0 }); - move_to>(&s, S493{ x: 0 }); - move_to>(&s, S494{ x: 0 }); - move_to>(&s, S495{ x: 0 }); - move_to>(&s, S496{ x: 0 }); - move_to>(&s, S497{ x: 0 }); - move_to>(&s, S498{ x: 0 }); - move_to>(&s, S499{ x: 0 }); - move_to>(&s, S500{ x: 0 }); - move_to>(&s, S501{ x: 0 }); - move_to>(&s, S502{ x: 0 }); - move_to>(&s, S503{ x: 0 }); - move_to>(&s, S504{ x: 0 }); - move_to>(&s, S505{ x: 0 }); - move_to>(&s, S506{ x: 0 }); - move_to>(&s, S507{ x: 0 }); - move_to>(&s, S508{ x: 0 }); - move_to>(&s, S509{ x: 0 }); - move_to>(&s, S510{ x: 0 }); - move_to>(&s, S511{ x: 0 }); - move_to>(&s, S512{ x: 0 }); - move_to>(&s, S513{ x: 0 }); - move_to>(&s, S514{ x: 0 }); - move_to>(&s, S515{ x: 0 }); - move_to>(&s, S516{ x: 0 }); - move_to>(&s, S517{ x: 0 }); - move_to>(&s, S518{ x: 0 }); - move_to>(&s, S519{ x: 0 }); - move_to>(&s, S520{ x: 0 }); - move_to>(&s, S521{ x: 0 }); - move_to>(&s, S522{ x: 0 }); - move_to>(&s, S523{ x: 0 }); - move_to>(&s, S524{ x: 0 }); - move_to>(&s, S525{ x: 0 }); - move_to>(&s, S526{ x: 0 }); - move_to>(&s, S527{ x: 0 }); - move_to>(&s, S528{ x: 0 }); - move_to>(&s, S529{ x: 0 }); - move_to>(&s, S530{ x: 0 }); - move_to>(&s, S531{ x: 0 }); - move_to>(&s, S532{ x: 0 }); - move_to>(&s, S533{ x: 0 }); - move_to>(&s, S534{ x: 0 }); - move_to>(&s, S535{ x: 0 }); - move_to>(&s, S536{ x: 0 }); - move_to>(&s, S537{ x: 0 }); - move_to>(&s, S538{ x: 0 }); - move_to>(&s, S539{ x: 0 }); - move_to>(&s, S540{ x: 0 }); - move_to>(&s, S541{ x: 0 }); - move_to>(&s, S542{ x: 0 }); - move_to>(&s, S543{ x: 0 }); - move_to>(&s, S544{ x: 0 }); - move_to>(&s, S545{ x: 0 }); - move_to>(&s, S546{ x: 0 }); - move_to>(&s, S547{ x: 0 }); - move_to>(&s, S548{ x: 0 }); - move_to>(&s, S549{ x: 0 }); - move_to>(&s, S550{ x: 0 }); - move_to>(&s, S551{ x: 0 }); - move_to>(&s, S552{ x: 0 }); - move_to>(&s, S553{ x: 0 }); - move_to>(&s, S554{ x: 0 }); - move_to>(&s, S555{ x: 0 }); - move_to>(&s, S556{ x: 0 }); - move_to>(&s, S557{ x: 0 }); - move_to>(&s, S558{ x: 0 }); - move_to>(&s, S559{ x: 0 }); - move_to>(&s, S560{ x: 0 }); - move_to>(&s, S561{ x: 0 }); - move_to>(&s, S562{ x: 0 }); - move_to>(&s, S563{ x: 0 }); - move_to>(&s, S564{ x: 0 }); - move_to>(&s, S565{ x: 0 }); - move_to>(&s, S566{ x: 0 }); - move_to>(&s, S567{ x: 0 }); - move_to>(&s, S568{ x: 0 }); - move_to>(&s, S569{ x: 0 }); - move_to>(&s, S570{ x: 0 }); - move_to>(&s, S571{ x: 0 }); - move_to>(&s, S572{ x: 0 }); - move_to>(&s, S573{ x: 0 }); - move_to>(&s, S574{ x: 0 }); - move_to>(&s, S575{ x: 0 }); - move_to>(&s, S576{ x: 0 }); - move_to>(&s, S577{ x: 0 }); - move_to>(&s, S578{ x: 0 }); - move_to>(&s, S579{ x: 0 }); - move_to>(&s, S580{ x: 0 }); - move_to>(&s, S581{ x: 0 }); - move_to>(&s, S582{ x: 0 }); - move_to>(&s, S583{ x: 0 }); - move_to>(&s, S584{ x: 0 }); - move_to>(&s, S585{ x: 0 }); - move_to>(&s, S586{ x: 0 }); - move_to>(&s, S587{ x: 0 }); - move_to>(&s, S588{ x: 0 }); - move_to>(&s, S589{ x: 0 }); - move_to>(&s, S590{ x: 0 }); - move_to>(&s, S591{ x: 0 }); - move_to>(&s, S592{ x: 0 }); - move_to>(&s, S593{ x: 0 }); - move_to>(&s, S594{ x: 0 }); - move_to>(&s, S595{ x: 0 }); - move_to>(&s, S596{ x: 0 }); - move_to>(&s, S597{ x: 0 }); - move_to>(&s, S598{ x: 0 }); - move_to>(&s, S599{ x: 0 }); - move_to>(&s, S600{ x: 0 }); - return; - } - - public entry calibrate_move_to_generic_x1000(s: signer) { - label b0: - move_to>(&s, S601{ x: 0 }); - move_to>(&s, S602{ x: 0 }); - move_to>(&s, S603{ x: 0 }); - move_to>(&s, S604{ x: 0 }); - move_to>(&s, S605{ x: 0 }); - move_to>(&s, S606{ x: 0 }); - move_to>(&s, S607{ x: 0 }); - move_to>(&s, S608{ x: 0 }); - move_to>(&s, S609{ x: 0 }); - move_to>(&s, S610{ x: 0 }); - move_to>(&s, S611{ x: 0 }); - move_to>(&s, S612{ x: 0 }); - move_to>(&s, S613{ x: 0 }); - move_to>(&s, S614{ x: 0 }); - move_to>(&s, S615{ x: 0 }); - move_to>(&s, S616{ x: 0 }); - move_to>(&s, S617{ x: 0 }); - move_to>(&s, S618{ x: 0 }); - move_to>(&s, S619{ x: 0 }); - move_to>(&s, S620{ x: 0 }); - move_to>(&s, S621{ x: 0 }); - move_to>(&s, S622{ x: 0 }); - move_to>(&s, S623{ x: 0 }); - move_to>(&s, S624{ x: 0 }); - move_to>(&s, S625{ x: 0 }); - move_to>(&s, S626{ x: 0 }); - move_to>(&s, S627{ x: 0 }); - move_to>(&s, S628{ x: 0 }); - move_to>(&s, S629{ x: 0 }); - move_to>(&s, S630{ x: 0 }); - move_to>(&s, S631{ x: 0 }); - move_to>(&s, S632{ x: 0 }); - move_to>(&s, S633{ x: 0 }); - move_to>(&s, S634{ x: 0 }); - move_to>(&s, S635{ x: 0 }); - move_to>(&s, S636{ x: 0 }); - move_to>(&s, S637{ x: 0 }); - move_to>(&s, S638{ x: 0 }); - move_to>(&s, S639{ x: 0 }); - move_to>(&s, S640{ x: 0 }); - move_to>(&s, S641{ x: 0 }); - move_to>(&s, S642{ x: 0 }); - move_to>(&s, S643{ x: 0 }); - move_to>(&s, S644{ x: 0 }); - move_to>(&s, S645{ x: 0 }); - move_to>(&s, S646{ x: 0 }); - move_to>(&s, S647{ x: 0 }); - move_to>(&s, S648{ x: 0 }); - move_to>(&s, S649{ x: 0 }); - move_to>(&s, S650{ x: 0 }); - move_to>(&s, S651{ x: 0 }); - move_to>(&s, S652{ x: 0 }); - move_to>(&s, S653{ x: 0 }); - move_to>(&s, S654{ x: 0 }); - move_to>(&s, S655{ x: 0 }); - move_to>(&s, S656{ x: 0 }); - move_to>(&s, S657{ x: 0 }); - move_to>(&s, S658{ x: 0 }); - move_to>(&s, S659{ x: 0 }); - move_to>(&s, S660{ x: 0 }); - move_to>(&s, S661{ x: 0 }); - move_to>(&s, S662{ x: 0 }); - move_to>(&s, S663{ x: 0 }); - move_to>(&s, S664{ x: 0 }); - move_to>(&s, S665{ x: 0 }); - move_to>(&s, S666{ x: 0 }); - move_to>(&s, S667{ x: 0 }); - move_to>(&s, S668{ x: 0 }); - move_to>(&s, S669{ x: 0 }); - move_to>(&s, S670{ x: 0 }); - move_to>(&s, S671{ x: 0 }); - move_to>(&s, S672{ x: 0 }); - move_to>(&s, S673{ x: 0 }); - move_to>(&s, S674{ x: 0 }); - move_to>(&s, S675{ x: 0 }); - move_to>(&s, S676{ x: 0 }); - move_to>(&s, S677{ x: 0 }); - move_to>(&s, S678{ x: 0 }); - move_to>(&s, S679{ x: 0 }); - move_to>(&s, S680{ x: 0 }); - move_to>(&s, S681{ x: 0 }); - move_to>(&s, S682{ x: 0 }); - move_to>(&s, S683{ x: 0 }); - move_to>(&s, S684{ x: 0 }); - move_to>(&s, S685{ x: 0 }); - move_to>(&s, S686{ x: 0 }); - move_to>(&s, S687{ x: 0 }); - move_to>(&s, S688{ x: 0 }); - move_to>(&s, S689{ x: 0 }); - move_to>(&s, S690{ x: 0 }); - move_to>(&s, S691{ x: 0 }); - move_to>(&s, S692{ x: 0 }); - move_to>(&s, S693{ x: 0 }); - move_to>(&s, S694{ x: 0 }); - move_to>(&s, S695{ x: 0 }); - move_to>(&s, S696{ x: 0 }); - move_to>(&s, S697{ x: 0 }); - move_to>(&s, S698{ x: 0 }); - move_to>(&s, S699{ x: 0 }); - move_to>(&s, S700{ x: 0 }); - move_to>(&s, S701{ x: 0 }); - move_to>(&s, S702{ x: 0 }); - move_to>(&s, S703{ x: 0 }); - move_to>(&s, S704{ x: 0 }); - move_to>(&s, S705{ x: 0 }); - move_to>(&s, S706{ x: 0 }); - move_to>(&s, S707{ x: 0 }); - move_to>(&s, S708{ x: 0 }); - move_to>(&s, S709{ x: 0 }); - move_to>(&s, S710{ x: 0 }); - move_to>(&s, S711{ x: 0 }); - move_to>(&s, S712{ x: 0 }); - move_to>(&s, S713{ x: 0 }); - move_to>(&s, S714{ x: 0 }); - move_to>(&s, S715{ x: 0 }); - move_to>(&s, S716{ x: 0 }); - move_to>(&s, S717{ x: 0 }); - move_to>(&s, S718{ x: 0 }); - move_to>(&s, S719{ x: 0 }); - move_to>(&s, S720{ x: 0 }); - move_to>(&s, S721{ x: 0 }); - move_to>(&s, S722{ x: 0 }); - move_to>(&s, S723{ x: 0 }); - move_to>(&s, S724{ x: 0 }); - move_to>(&s, S725{ x: 0 }); - move_to>(&s, S726{ x: 0 }); - move_to>(&s, S727{ x: 0 }); - move_to>(&s, S728{ x: 0 }); - move_to>(&s, S729{ x: 0 }); - move_to>(&s, S730{ x: 0 }); - move_to>(&s, S731{ x: 0 }); - move_to>(&s, S732{ x: 0 }); - move_to>(&s, S733{ x: 0 }); - move_to>(&s, S734{ x: 0 }); - move_to>(&s, S735{ x: 0 }); - move_to>(&s, S736{ x: 0 }); - move_to>(&s, S737{ x: 0 }); - move_to>(&s, S738{ x: 0 }); - move_to>(&s, S739{ x: 0 }); - move_to>(&s, S740{ x: 0 }); - move_to>(&s, S741{ x: 0 }); - move_to>(&s, S742{ x: 0 }); - move_to>(&s, S743{ x: 0 }); - move_to>(&s, S744{ x: 0 }); - move_to>(&s, S745{ x: 0 }); - move_to>(&s, S746{ x: 0 }); - move_to>(&s, S747{ x: 0 }); - move_to>(&s, S748{ x: 0 }); - move_to>(&s, S749{ x: 0 }); - move_to>(&s, S750{ x: 0 }); - move_to>(&s, S751{ x: 0 }); - move_to>(&s, S752{ x: 0 }); - move_to>(&s, S753{ x: 0 }); - move_to>(&s, S754{ x: 0 }); - move_to>(&s, S755{ x: 0 }); - move_to>(&s, S756{ x: 0 }); - move_to>(&s, S757{ x: 0 }); - move_to>(&s, S758{ x: 0 }); - move_to>(&s, S759{ x: 0 }); - move_to>(&s, S760{ x: 0 }); - move_to>(&s, S761{ x: 0 }); - move_to>(&s, S762{ x: 0 }); - move_to>(&s, S763{ x: 0 }); - move_to>(&s, S764{ x: 0 }); - move_to>(&s, S765{ x: 0 }); - move_to>(&s, S766{ x: 0 }); - move_to>(&s, S767{ x: 0 }); - move_to>(&s, S768{ x: 0 }); - move_to>(&s, S769{ x: 0 }); - move_to>(&s, S770{ x: 0 }); - move_to>(&s, S771{ x: 0 }); - move_to>(&s, S772{ x: 0 }); - move_to>(&s, S773{ x: 0 }); - move_to>(&s, S774{ x: 0 }); - move_to>(&s, S775{ x: 0 }); - move_to>(&s, S776{ x: 0 }); - move_to>(&s, S777{ x: 0 }); - move_to>(&s, S778{ x: 0 }); - move_to>(&s, S779{ x: 0 }); - move_to>(&s, S780{ x: 0 }); - move_to>(&s, S781{ x: 0 }); - move_to>(&s, S782{ x: 0 }); - move_to>(&s, S783{ x: 0 }); - move_to>(&s, S784{ x: 0 }); - move_to>(&s, S785{ x: 0 }); - move_to>(&s, S786{ x: 0 }); - move_to>(&s, S787{ x: 0 }); - move_to>(&s, S788{ x: 0 }); - move_to>(&s, S789{ x: 0 }); - move_to>(&s, S790{ x: 0 }); - move_to>(&s, S791{ x: 0 }); - move_to>(&s, S792{ x: 0 }); - move_to>(&s, S793{ x: 0 }); - move_to>(&s, S794{ x: 0 }); - move_to>(&s, S795{ x: 0 }); - move_to>(&s, S796{ x: 0 }); - move_to>(&s, S797{ x: 0 }); - move_to>(&s, S798{ x: 0 }); - move_to>(&s, S799{ x: 0 }); - move_to>(&s, S800{ x: 0 }); - move_to>(&s, S801{ x: 0 }); - move_to>(&s, S802{ x: 0 }); - move_to>(&s, S803{ x: 0 }); - move_to>(&s, S804{ x: 0 }); - move_to>(&s, S805{ x: 0 }); - move_to>(&s, S806{ x: 0 }); - move_to>(&s, S807{ x: 0 }); - move_to>(&s, S808{ x: 0 }); - move_to>(&s, S809{ x: 0 }); - move_to>(&s, S810{ x: 0 }); - move_to>(&s, S811{ x: 0 }); - move_to>(&s, S812{ x: 0 }); - move_to>(&s, S813{ x: 0 }); - move_to>(&s, S814{ x: 0 }); - move_to>(&s, S815{ x: 0 }); - move_to>(&s, S816{ x: 0 }); - move_to>(&s, S817{ x: 0 }); - move_to>(&s, S818{ x: 0 }); - move_to>(&s, S819{ x: 0 }); - move_to>(&s, S820{ x: 0 }); - move_to>(&s, S821{ x: 0 }); - move_to>(&s, S822{ x: 0 }); - move_to>(&s, S823{ x: 0 }); - move_to>(&s, S824{ x: 0 }); - move_to>(&s, S825{ x: 0 }); - move_to>(&s, S826{ x: 0 }); - move_to>(&s, S827{ x: 0 }); - move_to>(&s, S828{ x: 0 }); - move_to>(&s, S829{ x: 0 }); - move_to>(&s, S830{ x: 0 }); - move_to>(&s, S831{ x: 0 }); - move_to>(&s, S832{ x: 0 }); - move_to>(&s, S833{ x: 0 }); - move_to>(&s, S834{ x: 0 }); - move_to>(&s, S835{ x: 0 }); - move_to>(&s, S836{ x: 0 }); - move_to>(&s, S837{ x: 0 }); - move_to>(&s, S838{ x: 0 }); - move_to>(&s, S839{ x: 0 }); - move_to>(&s, S840{ x: 0 }); - move_to>(&s, S841{ x: 0 }); - move_to>(&s, S842{ x: 0 }); - move_to>(&s, S843{ x: 0 }); - move_to>(&s, S844{ x: 0 }); - move_to>(&s, S845{ x: 0 }); - move_to>(&s, S846{ x: 0 }); - move_to>(&s, S847{ x: 0 }); - move_to>(&s, S848{ x: 0 }); - move_to>(&s, S849{ x: 0 }); - move_to>(&s, S850{ x: 0 }); - move_to>(&s, S851{ x: 0 }); - move_to>(&s, S852{ x: 0 }); - move_to>(&s, S853{ x: 0 }); - move_to>(&s, S854{ x: 0 }); - move_to>(&s, S855{ x: 0 }); - move_to>(&s, S856{ x: 0 }); - move_to>(&s, S857{ x: 0 }); - move_to>(&s, S858{ x: 0 }); - move_to>(&s, S859{ x: 0 }); - move_to>(&s, S860{ x: 0 }); - move_to>(&s, S861{ x: 0 }); - move_to>(&s, S862{ x: 0 }); - move_to>(&s, S863{ x: 0 }); - move_to>(&s, S864{ x: 0 }); - move_to>(&s, S865{ x: 0 }); - move_to>(&s, S866{ x: 0 }); - move_to>(&s, S867{ x: 0 }); - move_to>(&s, S868{ x: 0 }); - move_to>(&s, S869{ x: 0 }); - move_to>(&s, S870{ x: 0 }); - move_to>(&s, S871{ x: 0 }); - move_to>(&s, S872{ x: 0 }); - move_to>(&s, S873{ x: 0 }); - move_to>(&s, S874{ x: 0 }); - move_to>(&s, S875{ x: 0 }); - move_to>(&s, S876{ x: 0 }); - move_to>(&s, S877{ x: 0 }); - move_to>(&s, S878{ x: 0 }); - move_to>(&s, S879{ x: 0 }); - move_to>(&s, S880{ x: 0 }); - move_to>(&s, S881{ x: 0 }); - move_to>(&s, S882{ x: 0 }); - move_to>(&s, S883{ x: 0 }); - move_to>(&s, S884{ x: 0 }); - move_to>(&s, S885{ x: 0 }); - move_to>(&s, S886{ x: 0 }); - move_to>(&s, S887{ x: 0 }); - move_to>(&s, S888{ x: 0 }); - move_to>(&s, S889{ x: 0 }); - move_to>(&s, S890{ x: 0 }); - move_to>(&s, S891{ x: 0 }); - move_to>(&s, S892{ x: 0 }); - move_to>(&s, S893{ x: 0 }); - move_to>(&s, S894{ x: 0 }); - move_to>(&s, S895{ x: 0 }); - move_to>(&s, S896{ x: 0 }); - move_to>(&s, S897{ x: 0 }); - move_to>(&s, S898{ x: 0 }); - move_to>(&s, S899{ x: 0 }); - move_to>(&s, S900{ x: 0 }); - move_to>(&s, S901{ x: 0 }); - move_to>(&s, S902{ x: 0 }); - move_to>(&s, S903{ x: 0 }); - move_to>(&s, S904{ x: 0 }); - move_to>(&s, S905{ x: 0 }); - move_to>(&s, S906{ x: 0 }); - move_to>(&s, S907{ x: 0 }); - move_to>(&s, S908{ x: 0 }); - move_to>(&s, S909{ x: 0 }); - move_to>(&s, S910{ x: 0 }); - move_to>(&s, S911{ x: 0 }); - move_to>(&s, S912{ x: 0 }); - move_to>(&s, S913{ x: 0 }); - move_to>(&s, S914{ x: 0 }); - move_to>(&s, S915{ x: 0 }); - move_to>(&s, S916{ x: 0 }); - move_to>(&s, S917{ x: 0 }); - move_to>(&s, S918{ x: 0 }); - move_to>(&s, S919{ x: 0 }); - move_to>(&s, S920{ x: 0 }); - move_to>(&s, S921{ x: 0 }); - move_to>(&s, S922{ x: 0 }); - move_to>(&s, S923{ x: 0 }); - move_to>(&s, S924{ x: 0 }); - move_to>(&s, S925{ x: 0 }); - move_to>(&s, S926{ x: 0 }); - move_to>(&s, S927{ x: 0 }); - move_to>(&s, S928{ x: 0 }); - move_to>(&s, S929{ x: 0 }); - move_to>(&s, S930{ x: 0 }); - move_to>(&s, S931{ x: 0 }); - move_to>(&s, S932{ x: 0 }); - move_to>(&s, S933{ x: 0 }); - move_to>(&s, S934{ x: 0 }); - move_to>(&s, S935{ x: 0 }); - move_to>(&s, S936{ x: 0 }); - move_to>(&s, S937{ x: 0 }); - move_to>(&s, S938{ x: 0 }); - move_to>(&s, S939{ x: 0 }); - move_to>(&s, S940{ x: 0 }); - move_to>(&s, S941{ x: 0 }); - move_to>(&s, S942{ x: 0 }); - move_to>(&s, S943{ x: 0 }); - move_to>(&s, S944{ x: 0 }); - move_to>(&s, S945{ x: 0 }); - move_to>(&s, S946{ x: 0 }); - move_to>(&s, S947{ x: 0 }); - move_to>(&s, S948{ x: 0 }); - move_to>(&s, S949{ x: 0 }); - move_to>(&s, S950{ x: 0 }); - move_to>(&s, S951{ x: 0 }); - move_to>(&s, S952{ x: 0 }); - move_to>(&s, S953{ x: 0 }); - move_to>(&s, S954{ x: 0 }); - move_to>(&s, S955{ x: 0 }); - move_to>(&s, S956{ x: 0 }); - move_to>(&s, S957{ x: 0 }); - move_to>(&s, S958{ x: 0 }); - move_to>(&s, S959{ x: 0 }); - move_to>(&s, S960{ x: 0 }); - move_to>(&s, S961{ x: 0 }); - move_to>(&s, S962{ x: 0 }); - move_to>(&s, S963{ x: 0 }); - move_to>(&s, S964{ x: 0 }); - move_to>(&s, S965{ x: 0 }); - move_to>(&s, S966{ x: 0 }); - move_to>(&s, S967{ x: 0 }); - move_to>(&s, S968{ x: 0 }); - move_to>(&s, S969{ x: 0 }); - move_to>(&s, S970{ x: 0 }); - move_to>(&s, S971{ x: 0 }); - move_to>(&s, S972{ x: 0 }); - move_to>(&s, S973{ x: 0 }); - move_to>(&s, S974{ x: 0 }); - move_to>(&s, S975{ x: 0 }); - move_to>(&s, S976{ x: 0 }); - move_to>(&s, S977{ x: 0 }); - move_to>(&s, S978{ x: 0 }); - move_to>(&s, S979{ x: 0 }); - move_to>(&s, S980{ x: 0 }); - move_to>(&s, S981{ x: 0 }); - move_to>(&s, S982{ x: 0 }); - move_to>(&s, S983{ x: 0 }); - move_to>(&s, S984{ x: 0 }); - move_to>(&s, S985{ x: 0 }); - move_to>(&s, S986{ x: 0 }); - move_to>(&s, S987{ x: 0 }); - move_to>(&s, S988{ x: 0 }); - move_to>(&s, S989{ x: 0 }); - move_to>(&s, S990{ x: 0 }); - move_to>(&s, S991{ x: 0 }); - move_to>(&s, S992{ x: 0 }); - move_to>(&s, S993{ x: 0 }); - move_to>(&s, S994{ x: 0 }); - move_to>(&s, S995{ x: 0 }); - move_to>(&s, S996{ x: 0 }); - move_to>(&s, S997{ x: 0 }); - move_to>(&s, S998{ x: 0 }); - move_to>(&s, S999{ x: 0 }); - move_to>(&s, S1000{ x: 0 }); - move_to>(&s, S1001{ x: 0 }); - move_to>(&s, S1002{ x: 0 }); - move_to>(&s, S1003{ x: 0 }); - move_to>(&s, S1004{ x: 0 }); - move_to>(&s, S1005{ x: 0 }); - move_to>(&s, S1006{ x: 0 }); - move_to>(&s, S1007{ x: 0 }); - move_to>(&s, S1008{ x: 0 }); - move_to>(&s, S1009{ x: 0 }); - move_to>(&s, S1010{ x: 0 }); - move_to>(&s, S1011{ x: 0 }); - move_to>(&s, S1012{ x: 0 }); - move_to>(&s, S1013{ x: 0 }); - move_to>(&s, S1014{ x: 0 }); - move_to>(&s, S1015{ x: 0 }); - move_to>(&s, S1016{ x: 0 }); - move_to>(&s, S1017{ x: 0 }); - move_to>(&s, S1018{ x: 0 }); - move_to>(&s, S1019{ x: 0 }); - move_to>(&s, S1020{ x: 0 }); - move_to>(&s, S1021{ x: 0 }); - move_to>(&s, S1022{ x: 0 }); - move_to>(&s, S1023{ x: 0 }); - move_to>(&s, S1024{ x: 0 }); - move_to>(&s, S1025{ x: 0 }); - move_to>(&s, S1026{ x: 0 }); - move_to>(&s, S1027{ x: 0 }); - move_to>(&s, S1028{ x: 0 }); - move_to>(&s, S1029{ x: 0 }); - move_to>(&s, S1030{ x: 0 }); - move_to>(&s, S1031{ x: 0 }); - move_to>(&s, S1032{ x: 0 }); - move_to>(&s, S1033{ x: 0 }); - move_to>(&s, S1034{ x: 0 }); - move_to>(&s, S1035{ x: 0 }); - move_to>(&s, S1036{ x: 0 }); - move_to>(&s, S1037{ x: 0 }); - move_to>(&s, S1038{ x: 0 }); - move_to>(&s, S1039{ x: 0 }); - move_to>(&s, S1040{ x: 0 }); - move_to>(&s, S1041{ x: 0 }); - move_to>(&s, S1042{ x: 0 }); - move_to>(&s, S1043{ x: 0 }); - move_to>(&s, S1044{ x: 0 }); - move_to>(&s, S1045{ x: 0 }); - move_to>(&s, S1046{ x: 0 }); - move_to>(&s, S1047{ x: 0 }); - move_to>(&s, S1048{ x: 0 }); - move_to>(&s, S1049{ x: 0 }); - move_to>(&s, S1050{ x: 0 }); - move_to>(&s, S1051{ x: 0 }); - move_to>(&s, S1052{ x: 0 }); - move_to>(&s, S1053{ x: 0 }); - move_to>(&s, S1054{ x: 0 }); - move_to>(&s, S1055{ x: 0 }); - move_to>(&s, S1056{ x: 0 }); - move_to>(&s, S1057{ x: 0 }); - move_to>(&s, S1058{ x: 0 }); - move_to>(&s, S1059{ x: 0 }); - move_to>(&s, S1060{ x: 0 }); - move_to>(&s, S1061{ x: 0 }); - move_to>(&s, S1062{ x: 0 }); - move_to>(&s, S1063{ x: 0 }); - move_to>(&s, S1064{ x: 0 }); - move_to>(&s, S1065{ x: 0 }); - move_to>(&s, S1066{ x: 0 }); - move_to>(&s, S1067{ x: 0 }); - move_to>(&s, S1068{ x: 0 }); - move_to>(&s, S1069{ x: 0 }); - move_to>(&s, S1070{ x: 0 }); - move_to>(&s, S1071{ x: 0 }); - move_to>(&s, S1072{ x: 0 }); - move_to>(&s, S1073{ x: 0 }); - move_to>(&s, S1074{ x: 0 }); - move_to>(&s, S1075{ x: 0 }); - move_to>(&s, S1076{ x: 0 }); - move_to>(&s, S1077{ x: 0 }); - move_to>(&s, S1078{ x: 0 }); - move_to>(&s, S1079{ x: 0 }); - move_to>(&s, S1080{ x: 0 }); - move_to>(&s, S1081{ x: 0 }); - move_to>(&s, S1082{ x: 0 }); - move_to>(&s, S1083{ x: 0 }); - move_to>(&s, S1084{ x: 0 }); - move_to>(&s, S1085{ x: 0 }); - move_to>(&s, S1086{ x: 0 }); - move_to>(&s, S1087{ x: 0 }); - move_to>(&s, S1088{ x: 0 }); - move_to>(&s, S1089{ x: 0 }); - move_to>(&s, S1090{ x: 0 }); - move_to>(&s, S1091{ x: 0 }); - move_to>(&s, S1092{ x: 0 }); - move_to>(&s, S1093{ x: 0 }); - move_to>(&s, S1094{ x: 0 }); - move_to>(&s, S1095{ x: 0 }); - move_to>(&s, S1096{ x: 0 }); - move_to>(&s, S1097{ x: 0 }); - move_to>(&s, S1098{ x: 0 }); - move_to>(&s, S1099{ x: 0 }); - move_to>(&s, S1100{ x: 0 }); - move_to>(&s, S1101{ x: 0 }); - move_to>(&s, S1102{ x: 0 }); - move_to>(&s, S1103{ x: 0 }); - move_to>(&s, S1104{ x: 0 }); - move_to>(&s, S1105{ x: 0 }); - move_to>(&s, S1106{ x: 0 }); - move_to>(&s, S1107{ x: 0 }); - move_to>(&s, S1108{ x: 0 }); - move_to>(&s, S1109{ x: 0 }); - move_to>(&s, S1110{ x: 0 }); - move_to>(&s, S1111{ x: 0 }); - move_to>(&s, S1112{ x: 0 }); - move_to>(&s, S1113{ x: 0 }); - move_to>(&s, S1114{ x: 0 }); - move_to>(&s, S1115{ x: 0 }); - move_to>(&s, S1116{ x: 0 }); - move_to>(&s, S1117{ x: 0 }); - move_to>(&s, S1118{ x: 0 }); - move_to>(&s, S1119{ x: 0 }); - move_to>(&s, S1120{ x: 0 }); - move_to>(&s, S1121{ x: 0 }); - move_to>(&s, S1122{ x: 0 }); - move_to>(&s, S1123{ x: 0 }); - move_to>(&s, S1124{ x: 0 }); - move_to>(&s, S1125{ x: 0 }); - move_to>(&s, S1126{ x: 0 }); - move_to>(&s, S1127{ x: 0 }); - move_to>(&s, S1128{ x: 0 }); - move_to>(&s, S1129{ x: 0 }); - move_to>(&s, S1130{ x: 0 }); - move_to>(&s, S1131{ x: 0 }); - move_to>(&s, S1132{ x: 0 }); - move_to>(&s, S1133{ x: 0 }); - move_to>(&s, S1134{ x: 0 }); - move_to>(&s, S1135{ x: 0 }); - move_to>(&s, S1136{ x: 0 }); - move_to>(&s, S1137{ x: 0 }); - move_to>(&s, S1138{ x: 0 }); - move_to>(&s, S1139{ x: 0 }); - move_to>(&s, S1140{ x: 0 }); - move_to>(&s, S1141{ x: 0 }); - move_to>(&s, S1142{ x: 0 }); - move_to>(&s, S1143{ x: 0 }); - move_to>(&s, S1144{ x: 0 }); - move_to>(&s, S1145{ x: 0 }); - move_to>(&s, S1146{ x: 0 }); - move_to>(&s, S1147{ x: 0 }); - move_to>(&s, S1148{ x: 0 }); - move_to>(&s, S1149{ x: 0 }); - move_to>(&s, S1150{ x: 0 }); - move_to>(&s, S1151{ x: 0 }); - move_to>(&s, S1152{ x: 0 }); - move_to>(&s, S1153{ x: 0 }); - move_to>(&s, S1154{ x: 0 }); - move_to>(&s, S1155{ x: 0 }); - move_to>(&s, S1156{ x: 0 }); - move_to>(&s, S1157{ x: 0 }); - move_to>(&s, S1158{ x: 0 }); - move_to>(&s, S1159{ x: 0 }); - move_to>(&s, S1160{ x: 0 }); - move_to>(&s, S1161{ x: 0 }); - move_to>(&s, S1162{ x: 0 }); - move_to>(&s, S1163{ x: 0 }); - move_to>(&s, S1164{ x: 0 }); - move_to>(&s, S1165{ x: 0 }); - move_to>(&s, S1166{ x: 0 }); - move_to>(&s, S1167{ x: 0 }); - move_to>(&s, S1168{ x: 0 }); - move_to>(&s, S1169{ x: 0 }); - move_to>(&s, S1170{ x: 0 }); - move_to>(&s, S1171{ x: 0 }); - move_to>(&s, S1172{ x: 0 }); - move_to>(&s, S1173{ x: 0 }); - move_to>(&s, S1174{ x: 0 }); - move_to>(&s, S1175{ x: 0 }); - move_to>(&s, S1176{ x: 0 }); - move_to>(&s, S1177{ x: 0 }); - move_to>(&s, S1178{ x: 0 }); - move_to>(&s, S1179{ x: 0 }); - move_to>(&s, S1180{ x: 0 }); - move_to>(&s, S1181{ x: 0 }); - move_to>(&s, S1182{ x: 0 }); - move_to>(&s, S1183{ x: 0 }); - move_to>(&s, S1184{ x: 0 }); - move_to>(&s, S1185{ x: 0 }); - move_to>(&s, S1186{ x: 0 }); - move_to>(&s, S1187{ x: 0 }); - move_to>(&s, S1188{ x: 0 }); - move_to>(&s, S1189{ x: 0 }); - move_to>(&s, S1190{ x: 0 }); - move_to>(&s, S1191{ x: 0 }); - move_to>(&s, S1192{ x: 0 }); - move_to>(&s, S1193{ x: 0 }); - move_to>(&s, S1194{ x: 0 }); - move_to>(&s, S1195{ x: 0 }); - move_to>(&s, S1196{ x: 0 }); - move_to>(&s, S1197{ x: 0 }); - move_to>(&s, S1198{ x: 0 }); - move_to>(&s, S1199{ x: 0 }); - move_to>(&s, S1200{ x: 0 }); - move_to>(&s, S1201{ x: 0 }); - move_to>(&s, S1202{ x: 0 }); - move_to>(&s, S1203{ x: 0 }); - move_to>(&s, S1204{ x: 0 }); - move_to>(&s, S1205{ x: 0 }); - move_to>(&s, S1206{ x: 0 }); - move_to>(&s, S1207{ x: 0 }); - move_to>(&s, S1208{ x: 0 }); - move_to>(&s, S1209{ x: 0 }); - move_to>(&s, S1210{ x: 0 }); - move_to>(&s, S1211{ x: 0 }); - move_to>(&s, S1212{ x: 0 }); - move_to>(&s, S1213{ x: 0 }); - move_to>(&s, S1214{ x: 0 }); - move_to>(&s, S1215{ x: 0 }); - move_to>(&s, S1216{ x: 0 }); - move_to>(&s, S1217{ x: 0 }); - move_to>(&s, S1218{ x: 0 }); - move_to>(&s, S1219{ x: 0 }); - move_to>(&s, S1220{ x: 0 }); - move_to>(&s, S1221{ x: 0 }); - move_to>(&s, S1222{ x: 0 }); - move_to>(&s, S1223{ x: 0 }); - move_to>(&s, S1224{ x: 0 }); - move_to>(&s, S1225{ x: 0 }); - move_to>(&s, S1226{ x: 0 }); - move_to>(&s, S1227{ x: 0 }); - move_to>(&s, S1228{ x: 0 }); - move_to>(&s, S1229{ x: 0 }); - move_to>(&s, S1230{ x: 0 }); - move_to>(&s, S1231{ x: 0 }); - move_to>(&s, S1232{ x: 0 }); - move_to>(&s, S1233{ x: 0 }); - move_to>(&s, S1234{ x: 0 }); - move_to>(&s, S1235{ x: 0 }); - move_to>(&s, S1236{ x: 0 }); - move_to>(&s, S1237{ x: 0 }); - move_to>(&s, S1238{ x: 0 }); - move_to>(&s, S1239{ x: 0 }); - move_to>(&s, S1240{ x: 0 }); - move_to>(&s, S1241{ x: 0 }); - move_to>(&s, S1242{ x: 0 }); - move_to>(&s, S1243{ x: 0 }); - move_to>(&s, S1244{ x: 0 }); - move_to>(&s, S1245{ x: 0 }); - move_to>(&s, S1246{ x: 0 }); - move_to>(&s, S1247{ x: 0 }); - move_to>(&s, S1248{ x: 0 }); - move_to>(&s, S1249{ x: 0 }); - move_to>(&s, S1250{ x: 0 }); - move_to>(&s, S1251{ x: 0 }); - move_to>(&s, S1252{ x: 0 }); - move_to>(&s, S1253{ x: 0 }); - move_to>(&s, S1254{ x: 0 }); - move_to>(&s, S1255{ x: 0 }); - move_to>(&s, S1256{ x: 0 }); - move_to>(&s, S1257{ x: 0 }); - move_to>(&s, S1258{ x: 0 }); - move_to>(&s, S1259{ x: 0 }); - move_to>(&s, S1260{ x: 0 }); - move_to>(&s, S1261{ x: 0 }); - move_to>(&s, S1262{ x: 0 }); - move_to>(&s, S1263{ x: 0 }); - move_to>(&s, S1264{ x: 0 }); - move_to>(&s, S1265{ x: 0 }); - move_to>(&s, S1266{ x: 0 }); - move_to>(&s, S1267{ x: 0 }); - move_to>(&s, S1268{ x: 0 }); - move_to>(&s, S1269{ x: 0 }); - move_to>(&s, S1270{ x: 0 }); - move_to>(&s, S1271{ x: 0 }); - move_to>(&s, S1272{ x: 0 }); - move_to>(&s, S1273{ x: 0 }); - move_to>(&s, S1274{ x: 0 }); - move_to>(&s, S1275{ x: 0 }); - move_to>(&s, S1276{ x: 0 }); - move_to>(&s, S1277{ x: 0 }); - move_to>(&s, S1278{ x: 0 }); - move_to>(&s, S1279{ x: 0 }); - move_to>(&s, S1280{ x: 0 }); - move_to>(&s, S1281{ x: 0 }); - move_to>(&s, S1282{ x: 0 }); - move_to>(&s, S1283{ x: 0 }); - move_to>(&s, S1284{ x: 0 }); - move_to>(&s, S1285{ x: 0 }); - move_to>(&s, S1286{ x: 0 }); - move_to>(&s, S1287{ x: 0 }); - move_to>(&s, S1288{ x: 0 }); - move_to>(&s, S1289{ x: 0 }); - move_to>(&s, S1290{ x: 0 }); - move_to>(&s, S1291{ x: 0 }); - move_to>(&s, S1292{ x: 0 }); - move_to>(&s, S1293{ x: 0 }); - move_to>(&s, S1294{ x: 0 }); - move_to>(&s, S1295{ x: 0 }); - move_to>(&s, S1296{ x: 0 }); - move_to>(&s, S1297{ x: 0 }); - move_to>(&s, S1298{ x: 0 }); - move_to>(&s, S1299{ x: 0 }); - move_to>(&s, S1300{ x: 0 }); - move_to>(&s, S1301{ x: 0 }); - move_to>(&s, S1302{ x: 0 }); - move_to>(&s, S1303{ x: 0 }); - move_to>(&s, S1304{ x: 0 }); - move_to>(&s, S1305{ x: 0 }); - move_to>(&s, S1306{ x: 0 }); - move_to>(&s, S1307{ x: 0 }); - move_to>(&s, S1308{ x: 0 }); - move_to>(&s, S1309{ x: 0 }); - move_to>(&s, S1310{ x: 0 }); - move_to>(&s, S1311{ x: 0 }); - move_to>(&s, S1312{ x: 0 }); - move_to>(&s, S1313{ x: 0 }); - move_to>(&s, S1314{ x: 0 }); - move_to>(&s, S1315{ x: 0 }); - move_to>(&s, S1316{ x: 0 }); - move_to>(&s, S1317{ x: 0 }); - move_to>(&s, S1318{ x: 0 }); - move_to>(&s, S1319{ x: 0 }); - move_to>(&s, S1320{ x: 0 }); - move_to>(&s, S1321{ x: 0 }); - move_to>(&s, S1322{ x: 0 }); - move_to>(&s, S1323{ x: 0 }); - move_to>(&s, S1324{ x: 0 }); - move_to>(&s, S1325{ x: 0 }); - move_to>(&s, S1326{ x: 0 }); - move_to>(&s, S1327{ x: 0 }); - move_to>(&s, S1328{ x: 0 }); - move_to>(&s, S1329{ x: 0 }); - move_to>(&s, S1330{ x: 0 }); - move_to>(&s, S1331{ x: 0 }); - move_to>(&s, S1332{ x: 0 }); - move_to>(&s, S1333{ x: 0 }); - move_to>(&s, S1334{ x: 0 }); - move_to>(&s, S1335{ x: 0 }); - move_to>(&s, S1336{ x: 0 }); - move_to>(&s, S1337{ x: 0 }); - move_to>(&s, S1338{ x: 0 }); - move_to>(&s, S1339{ x: 0 }); - move_to>(&s, S1340{ x: 0 }); - move_to>(&s, S1341{ x: 0 }); - move_to>(&s, S1342{ x: 0 }); - move_to>(&s, S1343{ x: 0 }); - move_to>(&s, S1344{ x: 0 }); - move_to>(&s, S1345{ x: 0 }); - move_to>(&s, S1346{ x: 0 }); - move_to>(&s, S1347{ x: 0 }); - move_to>(&s, S1348{ x: 0 }); - move_to>(&s, S1349{ x: 0 }); - move_to>(&s, S1350{ x: 0 }); - move_to>(&s, S1351{ x: 0 }); - move_to>(&s, S1352{ x: 0 }); - move_to>(&s, S1353{ x: 0 }); - move_to>(&s, S1354{ x: 0 }); - move_to>(&s, S1355{ x: 0 }); - move_to>(&s, S1356{ x: 0 }); - move_to>(&s, S1357{ x: 0 }); - move_to>(&s, S1358{ x: 0 }); - move_to>(&s, S1359{ x: 0 }); - move_to>(&s, S1360{ x: 0 }); - move_to>(&s, S1361{ x: 0 }); - move_to>(&s, S1362{ x: 0 }); - move_to>(&s, S1363{ x: 0 }); - move_to>(&s, S1364{ x: 0 }); - move_to>(&s, S1365{ x: 0 }); - move_to>(&s, S1366{ x: 0 }); - move_to>(&s, S1367{ x: 0 }); - move_to>(&s, S1368{ x: 0 }); - move_to>(&s, S1369{ x: 0 }); - move_to>(&s, S1370{ x: 0 }); - move_to>(&s, S1371{ x: 0 }); - move_to>(&s, S1372{ x: 0 }); - move_to>(&s, S1373{ x: 0 }); - move_to>(&s, S1374{ x: 0 }); - move_to>(&s, S1375{ x: 0 }); - move_to>(&s, S1376{ x: 0 }); - move_to>(&s, S1377{ x: 0 }); - move_to>(&s, S1378{ x: 0 }); - move_to>(&s, S1379{ x: 0 }); - move_to>(&s, S1380{ x: 0 }); - move_to>(&s, S1381{ x: 0 }); - move_to>(&s, S1382{ x: 0 }); - move_to>(&s, S1383{ x: 0 }); - move_to>(&s, S1384{ x: 0 }); - move_to>(&s, S1385{ x: 0 }); - move_to>(&s, S1386{ x: 0 }); - move_to>(&s, S1387{ x: 0 }); - move_to>(&s, S1388{ x: 0 }); - move_to>(&s, S1389{ x: 0 }); - move_to>(&s, S1390{ x: 0 }); - move_to>(&s, S1391{ x: 0 }); - move_to>(&s, S1392{ x: 0 }); - move_to>(&s, S1393{ x: 0 }); - move_to>(&s, S1394{ x: 0 }); - move_to>(&s, S1395{ x: 0 }); - move_to>(&s, S1396{ x: 0 }); - move_to>(&s, S1397{ x: 0 }); - move_to>(&s, S1398{ x: 0 }); - move_to>(&s, S1399{ x: 0 }); - move_to>(&s, S1400{ x: 0 }); - move_to>(&s, S1401{ x: 0 }); - move_to>(&s, S1402{ x: 0 }); - move_to>(&s, S1403{ x: 0 }); - move_to>(&s, S1404{ x: 0 }); - move_to>(&s, S1405{ x: 0 }); - move_to>(&s, S1406{ x: 0 }); - move_to>(&s, S1407{ x: 0 }); - move_to>(&s, S1408{ x: 0 }); - move_to>(&s, S1409{ x: 0 }); - move_to>(&s, S1410{ x: 0 }); - move_to>(&s, S1411{ x: 0 }); - move_to>(&s, S1412{ x: 0 }); - move_to>(&s, S1413{ x: 0 }); - move_to>(&s, S1414{ x: 0 }); - move_to>(&s, S1415{ x: 0 }); - move_to>(&s, S1416{ x: 0 }); - move_to>(&s, S1417{ x: 0 }); - move_to>(&s, S1418{ x: 0 }); - move_to>(&s, S1419{ x: 0 }); - move_to>(&s, S1420{ x: 0 }); - move_to>(&s, S1421{ x: 0 }); - move_to>(&s, S1422{ x: 0 }); - move_to>(&s, S1423{ x: 0 }); - move_to>(&s, S1424{ x: 0 }); - move_to>(&s, S1425{ x: 0 }); - move_to>(&s, S1426{ x: 0 }); - move_to>(&s, S1427{ x: 0 }); - move_to>(&s, S1428{ x: 0 }); - move_to>(&s, S1429{ x: 0 }); - move_to>(&s, S1430{ x: 0 }); - move_to>(&s, S1431{ x: 0 }); - move_to>(&s, S1432{ x: 0 }); - move_to>(&s, S1433{ x: 0 }); - move_to>(&s, S1434{ x: 0 }); - move_to>(&s, S1435{ x: 0 }); - move_to>(&s, S1436{ x: 0 }); - move_to>(&s, S1437{ x: 0 }); - move_to>(&s, S1438{ x: 0 }); - move_to>(&s, S1439{ x: 0 }); - move_to>(&s, S1440{ x: 0 }); - move_to>(&s, S1441{ x: 0 }); - move_to>(&s, S1442{ x: 0 }); - move_to>(&s, S1443{ x: 0 }); - move_to>(&s, S1444{ x: 0 }); - move_to>(&s, S1445{ x: 0 }); - move_to>(&s, S1446{ x: 0 }); - move_to>(&s, S1447{ x: 0 }); - move_to>(&s, S1448{ x: 0 }); - move_to>(&s, S1449{ x: 0 }); - move_to>(&s, S1450{ x: 0 }); - move_to>(&s, S1451{ x: 0 }); - move_to>(&s, S1452{ x: 0 }); - move_to>(&s, S1453{ x: 0 }); - move_to>(&s, S1454{ x: 0 }); - move_to>(&s, S1455{ x: 0 }); - move_to>(&s, S1456{ x: 0 }); - move_to>(&s, S1457{ x: 0 }); - move_to>(&s, S1458{ x: 0 }); - move_to>(&s, S1459{ x: 0 }); - move_to>(&s, S1460{ x: 0 }); - move_to>(&s, S1461{ x: 0 }); - move_to>(&s, S1462{ x: 0 }); - move_to>(&s, S1463{ x: 0 }); - move_to>(&s, S1464{ x: 0 }); - move_to>(&s, S1465{ x: 0 }); - move_to>(&s, S1466{ x: 0 }); - move_to>(&s, S1467{ x: 0 }); - move_to>(&s, S1468{ x: 0 }); - move_to>(&s, S1469{ x: 0 }); - move_to>(&s, S1470{ x: 0 }); - move_to>(&s, S1471{ x: 0 }); - move_to>(&s, S1472{ x: 0 }); - move_to>(&s, S1473{ x: 0 }); - move_to>(&s, S1474{ x: 0 }); - move_to>(&s, S1475{ x: 0 }); - move_to>(&s, S1476{ x: 0 }); - move_to>(&s, S1477{ x: 0 }); - move_to>(&s, S1478{ x: 0 }); - move_to>(&s, S1479{ x: 0 }); - move_to>(&s, S1480{ x: 0 }); - move_to>(&s, S1481{ x: 0 }); - move_to>(&s, S1482{ x: 0 }); - move_to>(&s, S1483{ x: 0 }); - move_to>(&s, S1484{ x: 0 }); - move_to>(&s, S1485{ x: 0 }); - move_to>(&s, S1486{ x: 0 }); - move_to>(&s, S1487{ x: 0 }); - move_to>(&s, S1488{ x: 0 }); - move_to>(&s, S1489{ x: 0 }); - move_to>(&s, S1490{ x: 0 }); - move_to>(&s, S1491{ x: 0 }); - move_to>(&s, S1492{ x: 0 }); - move_to>(&s, S1493{ x: 0 }); - move_to>(&s, S1494{ x: 0 }); - move_to>(&s, S1495{ x: 0 }); - move_to>(&s, S1496{ x: 0 }); - move_to>(&s, S1497{ x: 0 }); - move_to>(&s, S1498{ x: 0 }); - move_to>(&s, S1499{ x: 0 }); - move_to>(&s, S1500{ x: 0 }); - move_to>(&s, S1501{ x: 0 }); - move_to>(&s, S1502{ x: 0 }); - move_to>(&s, S1503{ x: 0 }); - move_to>(&s, S1504{ x: 0 }); - move_to>(&s, S1505{ x: 0 }); - move_to>(&s, S1506{ x: 0 }); - move_to>(&s, S1507{ x: 0 }); - move_to>(&s, S1508{ x: 0 }); - move_to>(&s, S1509{ x: 0 }); - move_to>(&s, S1510{ x: 0 }); - move_to>(&s, S1511{ x: 0 }); - move_to>(&s, S1512{ x: 0 }); - move_to>(&s, S1513{ x: 0 }); - move_to>(&s, S1514{ x: 0 }); - move_to>(&s, S1515{ x: 0 }); - move_to>(&s, S1516{ x: 0 }); - move_to>(&s, S1517{ x: 0 }); - move_to>(&s, S1518{ x: 0 }); - move_to>(&s, S1519{ x: 0 }); - move_to>(&s, S1520{ x: 0 }); - move_to>(&s, S1521{ x: 0 }); - move_to>(&s, S1522{ x: 0 }); - move_to>(&s, S1523{ x: 0 }); - move_to>(&s, S1524{ x: 0 }); - move_to>(&s, S1525{ x: 0 }); - move_to>(&s, S1526{ x: 0 }); - move_to>(&s, S1527{ x: 0 }); - move_to>(&s, S1528{ x: 0 }); - move_to>(&s, S1529{ x: 0 }); - move_to>(&s, S1530{ x: 0 }); - move_to>(&s, S1531{ x: 0 }); - move_to>(&s, S1532{ x: 0 }); - move_to>(&s, S1533{ x: 0 }); - move_to>(&s, S1534{ x: 0 }); - move_to>(&s, S1535{ x: 0 }); - move_to>(&s, S1536{ x: 0 }); - move_to>(&s, S1537{ x: 0 }); - move_to>(&s, S1538{ x: 0 }); - move_to>(&s, S1539{ x: 0 }); - move_to>(&s, S1540{ x: 0 }); - move_to>(&s, S1541{ x: 0 }); - move_to>(&s, S1542{ x: 0 }); - move_to>(&s, S1543{ x: 0 }); - move_to>(&s, S1544{ x: 0 }); - move_to>(&s, S1545{ x: 0 }); - move_to>(&s, S1546{ x: 0 }); - move_to>(&s, S1547{ x: 0 }); - move_to>(&s, S1548{ x: 0 }); - move_to>(&s, S1549{ x: 0 }); - move_to>(&s, S1550{ x: 0 }); - move_to>(&s, S1551{ x: 0 }); - move_to>(&s, S1552{ x: 0 }); - move_to>(&s, S1553{ x: 0 }); - move_to>(&s, S1554{ x: 0 }); - move_to>(&s, S1555{ x: 0 }); - move_to>(&s, S1556{ x: 0 }); - move_to>(&s, S1557{ x: 0 }); - move_to>(&s, S1558{ x: 0 }); - move_to>(&s, S1559{ x: 0 }); - move_to>(&s, S1560{ x: 0 }); - move_to>(&s, S1561{ x: 0 }); - move_to>(&s, S1562{ x: 0 }); - move_to>(&s, S1563{ x: 0 }); - move_to>(&s, S1564{ x: 0 }); - move_to>(&s, S1565{ x: 0 }); - move_to>(&s, S1566{ x: 0 }); - move_to>(&s, S1567{ x: 0 }); - move_to>(&s, S1568{ x: 0 }); - move_to>(&s, S1569{ x: 0 }); - move_to>(&s, S1570{ x: 0 }); - move_to>(&s, S1571{ x: 0 }); - move_to>(&s, S1572{ x: 0 }); - move_to>(&s, S1573{ x: 0 }); - move_to>(&s, S1574{ x: 0 }); - move_to>(&s, S1575{ x: 0 }); - move_to>(&s, S1576{ x: 0 }); - move_to>(&s, S1577{ x: 0 }); - move_to>(&s, S1578{ x: 0 }); - move_to>(&s, S1579{ x: 0 }); - move_to>(&s, S1580{ x: 0 }); - move_to>(&s, S1581{ x: 0 }); - move_to>(&s, S1582{ x: 0 }); - move_to>(&s, S1583{ x: 0 }); - move_to>(&s, S1584{ x: 0 }); - move_to>(&s, S1585{ x: 0 }); - move_to>(&s, S1586{ x: 0 }); - move_to>(&s, S1587{ x: 0 }); - move_to>(&s, S1588{ x: 0 }); - move_to>(&s, S1589{ x: 0 }); - move_to>(&s, S1590{ x: 0 }); - move_to>(&s, S1591{ x: 0 }); - move_to>(&s, S1592{ x: 0 }); - move_to>(&s, S1593{ x: 0 }); - move_to>(&s, S1594{ x: 0 }); - move_to>(&s, S1595{ x: 0 }); - move_to>(&s, S1596{ x: 0 }); - move_to>(&s, S1597{ x: 0 }); - move_to>(&s, S1598{ x: 0 }); - move_to>(&s, S1599{ x: 0 }); - move_to>(&s, S1600{ x: 0 }); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/mut-borrow-global-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/mut-borrow-global-generic.mvir deleted file mode 100644 index 9c81ad70524b1..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations-generic/mut-borrow-global-generic.mvir +++ /dev/null @@ -1,134 +0,0 @@ -module 0xcafe.MutBorrowGlobalGeneric { - struct S1 has key, drop { x: T } - struct S2 has key, drop { x: T } - struct S3 has key, drop { x: T } - struct S4 has key, drop { x: T } - struct S5 has key, drop { x: T } - struct S6 has key, drop { x: T } - struct S7 has key, drop { x: T } - struct S8 has key, drop { x: T } - struct S9 has key, drop { x: T } - struct S10 has key, drop { x: T } - struct S11 has key, drop { x: T } - struct S12 has key, drop { x: T } - struct S13 has key, drop { x: T } - struct S14 has key, drop { x: T } - struct S15 has key, drop { x: T } - struct S16 has key, drop { x: T } - struct S17 has key, drop { x: T } - struct S18 has key, drop { x: T } - struct S19 has key, drop { x: T } - struct S20 has key, drop { x: T } - struct S21 has key, drop { x: T } - struct S22 has key, drop { x: T } - struct S23 has key, drop { x: T } - struct S24 has key, drop { x: T } - struct S25 has key, drop { x: T } - struct S26 has key, drop { x: T } - struct S27 has key, drop { x: T } - struct S28 has key, drop { x: T } - struct S29 has key, drop { x: T } - struct S30 has key, drop { x: T } - - public entry calibrate_mut_borrow_global_generic_x100(s: signer) acquires S1, S2, S3, S4, S5, S6, S7, S8, S9, S10 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S1{ x: 0 }); - move_to>(&s, S2{ x: 0 }); - move_to>(&s, S3{ x: 0 }); - move_to>(&s, S4{ x: 0 }); - move_to>(&s, S5{ x: 0 }); - move_to>(&s, S6{ x: 0 }); - move_to>(&s, S7{ x: 0 }); - move_to>(&s, S8{ x: 0 }); - move_to>(&s, S9{ x: 0 }); - move_to>(&s, S10{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_global_generic_x500(s: signer) acquires S11, S12, S13, S14, S15, S16, S17, S18, S19, S20 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S11{ x: 0 }); - move_to>(&s, S12{ x: 0 }); - move_to>(&s, S13{ x: 0 }); - move_to>(&s, S14{ x: 0 }); - move_to>(&s, S15{ x: 0 }); - move_to>(&s, S16{ x: 0 }); - move_to>(&s, S17{ x: 0 }); - move_to>(&s, S18{ x: 0 }); - move_to>(&s, S19{ x: 0 }); - move_to>(&s, S20{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 500) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_global_generic_x1500(s: signer) acquires S21, S22, S23, S24, S25, S26, S27, S28, S29, S30 { - let i: u64; - label entry: - i = 0; - move_to>(&s, S21{ x: 0 }); - move_to>(&s, S22{ x: 0 }); - move_to>(&s, S23{ x: 0 }); - move_to>(&s, S24{ x: 0 }); - move_to>(&s, S25{ x: 0 }); - move_to>(&s, S26{ x: 0 }); - move_to>(&s, S27{ x: 0 }); - move_to>(&s, S28{ x: 0 }); - move_to>(&s, S29{ x: 0 }); - move_to>(&s, S30{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 1500) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - _ = borrow_global_mut>(0xcafe); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/exists.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations/exists.mvir deleted file mode 100644 index 6a9df4d904bd5..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/exists.mvir +++ /dev/null @@ -1,138 +0,0 @@ -module 0xcafe.Exists { - struct S1 has key, drop { x1: u64 } - struct S2 has key, drop { x1: u64, x2: u64, x3: u64, x4: u64, x5: u64, x6: u64, x7: u64, x8: u64 } - struct S3 has key, drop { x1: u64, x2: u64, x3: u64, x4: u64, x5: u64, x6: u64, x7: u64, x8: u64, x9: u64, x10: u64, x11: u64, x12: u64, x13: u64, x14: u64, x15: u64, x16: u64, x17: u64, x18: u64, x19: u64, x20: u64, x21: u64, x22: u64, x23: u64, x24: u64, x25: u64, x26: u64, x27: u64, x28: u64, x29: u64, x30: u64, x31: u64, x32: u64} - struct S4 has key, drop { x1: u64, x2: u64, x3: u64, x4: u64, x5: u64, x6: u64, x7: u64, x8: u64, x9: u64, x10: u64, x11: u64, x12: u64, x13: u64, x14: u64, x15: u64, x16: u64, x17: u64, x18: u64, x19: u64, x20: u64, x21: u64, x22: u64, x23: u64, x24: u64, x25: u64, x26: u64, x27: u64, x28: u64, x29: u64, x30: u64, x31: u64, x32: u64, x33: u64, x34: u64, x35: u64, x36: u64, x37: u64, x38: u64, x39: u64, x40: u64, x41: u64, x42: u64, x43: u64, x44: u64, x45: u64, x46: u64, x47: u64, x48: u64, x49: u64, x50: u64, x51: u64, x52: u64, x53: u64, x54: u64, x55: u64, x56: u64, x57: u64, x58: u64, x59: u64, x60: u64, x61: u64, x62: u64, x63: u64, x64: u64 } - - public calibrate_exists_s1_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_s1_x100() { - label b0: - Self.calibrate_exists_s1_impl(10); - return; - } - - public entry calibrate_exists_s1_x1000() { - label b0: - Self.calibrate_exists_s1_impl(100); - return; - } - - public entry calibrate_exists_s1_x5000() { - label b0: - Self.calibrate_exists_s1_impl(500); - return; - } - - public calibrate_exists_s2_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_s2_x100() { - label b0: - Self.calibrate_exists_s2_impl(10); - return; - } - - public entry calibrate_exists_s2_x1000() { - label b0: - Self.calibrate_exists_s2_impl(100); - return; - } - - public entry calibrate_exists_s2_x5000() { - label b0: - Self.calibrate_exists_s2_impl(500); - return; - } - - public calibrate_exists_s3_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_s3_x100() { - label b0: - Self.calibrate_exists_s3_impl(10); - return; - } - - public entry calibrate_exists_s3_x1000() { - label b0: - Self.calibrate_exists_s3_impl(100); - return; - } - - public entry calibrate_exists_s3_x5000() { - label b0: - Self.calibrate_exists_s3_impl(500); - return; - } - - public calibrate_exists_s4_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe), exists(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_exists_s4_x100() { - label b0: - Self.calibrate_exists_s4_impl(10); - return; - } - - public entry calibrate_exists_s4_x1000() { - label b0: - Self.calibrate_exists_s4_impl(100); - return; - } - - public entry calibrate_exists_s4_x5000() { - label b0: - Self.calibrate_exists_s4_impl(500); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/imm-borrow-global.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations/imm-borrow-global.mvir deleted file mode 100644 index 2c13060cbc2a5..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/imm-borrow-global.mvir +++ /dev/null @@ -1,53 +0,0 @@ -module 0xcafe.ImmBorrowGlobal { - struct S1 has key, drop { x: u64 } - struct S2 has key, drop { x: u64 } - struct S3 has key, drop { x: u64 } - - public entry calibrate_imm_borrow_global_x100(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - move_to(&s, S1{x:0}); - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_global_x500(s: signer) acquires S2 { - let i: u64; - label entry: - i = 0; - move_to(&s, S2{x:0}); - label loop_start: - jump_if_false (copy(i) < 500) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_global_x1000(s: signer) acquires S3 { - let i: u64; - label entry: - i = 0; - move_to(&s, S3{x:0}); - label loop_start: - jump_if_false (copy(i) < 1000) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe), borrow_global(0xcafe)); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-from.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-from.mvir deleted file mode 100644 index 77ac6e6b8c7fb..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-from.mvir +++ /dev/null @@ -1,132 +0,0 @@ -module 0xcafe.MoveFrom { - struct S1 has key, drop { x: u64 } - - public entry calibrate_move_from_x100(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_move_from_x1000(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 1000) loop_end; - i = move(i) + 1; - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_move_from_x5000(s: signer) acquires S1 { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 5000) loop_end; - i = move(i) + 1; - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - move_to(&s, S1{ x: 0 }); - _ = move_from(0xcafe); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-to.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-to.mvir deleted file mode 100644 index 3cb0f87f0629b..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/move-to.mvir +++ /dev/null @@ -1,3217 +0,0 @@ -module 0xcafe.MoveTo { - struct S1 has key, drop { x: u64 } - struct S2 has key, drop { x: u64 } - struct S3 has key, drop { x: u64 } - struct S4 has key, drop { x: u64 } - struct S5 has key, drop { x: u64 } - struct S6 has key, drop { x: u64 } - struct S7 has key, drop { x: u64 } - struct S8 has key, drop { x: u64 } - struct S9 has key, drop { x: u64 } - struct S10 has key, drop { x: u64 } - struct S11 has key, drop { x: u64 } - struct S12 has key, drop { x: u64 } - struct S13 has key, drop { x: u64 } - struct S14 has key, drop { x: u64 } - struct S15 has key, drop { x: u64 } - struct S16 has key, drop { x: u64 } - struct S17 has key, drop { x: u64 } - struct S18 has key, drop { x: u64 } - struct S19 has key, drop { x: u64 } - struct S20 has key, drop { x: u64 } - struct S21 has key, drop { x: u64 } - struct S22 has key, drop { x: u64 } - struct S23 has key, drop { x: u64 } - struct S24 has key, drop { x: u64 } - struct S25 has key, drop { x: u64 } - struct S26 has key, drop { x: u64 } - struct S27 has key, drop { x: u64 } - struct S28 has key, drop { x: u64 } - struct S29 has key, drop { x: u64 } - struct S30 has key, drop { x: u64 } - struct S31 has key, drop { x: u64 } - struct S32 has key, drop { x: u64 } - struct S33 has key, drop { x: u64 } - struct S34 has key, drop { x: u64 } - struct S35 has key, drop { x: u64 } - struct S36 has key, drop { x: u64 } - struct S37 has key, drop { x: u64 } - struct S38 has key, drop { x: u64 } - struct S39 has key, drop { x: u64 } - struct S40 has key, drop { x: u64 } - struct S41 has key, drop { x: u64 } - struct S42 has key, drop { x: u64 } - struct S43 has key, drop { x: u64 } - struct S44 has key, drop { x: u64 } - struct S45 has key, drop { x: u64 } - struct S46 has key, drop { x: u64 } - struct S47 has key, drop { x: u64 } - struct S48 has key, drop { x: u64 } - struct S49 has key, drop { x: u64 } - struct S50 has key, drop { x: u64 } - struct S51 has key, drop { x: u64 } - struct S52 has key, drop { x: u64 } - struct S53 has key, drop { x: u64 } - struct S54 has key, drop { x: u64 } - struct S55 has key, drop { x: u64 } - struct S56 has key, drop { x: u64 } - struct S57 has key, drop { x: u64 } - struct S58 has key, drop { x: u64 } - struct S59 has key, drop { x: u64 } - struct S60 has key, drop { x: u64 } - struct S61 has key, drop { x: u64 } - struct S62 has key, drop { x: u64 } - struct S63 has key, drop { x: u64 } - struct S64 has key, drop { x: u64 } - struct S65 has key, drop { x: u64 } - struct S66 has key, drop { x: u64 } - struct S67 has key, drop { x: u64 } - struct S68 has key, drop { x: u64 } - struct S69 has key, drop { x: u64 } - struct S70 has key, drop { x: u64 } - struct S71 has key, drop { x: u64 } - struct S72 has key, drop { x: u64 } - struct S73 has key, drop { x: u64 } - struct S74 has key, drop { x: u64 } - struct S75 has key, drop { x: u64 } - struct S76 has key, drop { x: u64 } - struct S77 has key, drop { x: u64 } - struct S78 has key, drop { x: u64 } - struct S79 has key, drop { x: u64 } - struct S80 has key, drop { x: u64 } - struct S81 has key, drop { x: u64 } - struct S82 has key, drop { x: u64 } - struct S83 has key, drop { x: u64 } - struct S84 has key, drop { x: u64 } - struct S85 has key, drop { x: u64 } - struct S86 has key, drop { x: u64 } - struct S87 has key, drop { x: u64 } - struct S88 has key, drop { x: u64 } - struct S89 has key, drop { x: u64 } - struct S90 has key, drop { x: u64 } - struct S91 has key, drop { x: u64 } - struct S92 has key, drop { x: u64 } - struct S93 has key, drop { x: u64 } - struct S94 has key, drop { x: u64 } - struct S95 has key, drop { x: u64 } - struct S96 has key, drop { x: u64 } - struct S97 has key, drop { x: u64 } - struct S98 has key, drop { x: u64 } - struct S99 has key, drop { x: u64 } - struct S100 has key, drop { x: u64 } - struct S101 has key, drop { x: u64 } - struct S102 has key, drop { x: u64 } - struct S103 has key, drop { x: u64 } - struct S104 has key, drop { x: u64 } - struct S105 has key, drop { x: u64 } - struct S106 has key, drop { x: u64 } - struct S107 has key, drop { x: u64 } - struct S108 has key, drop { x: u64 } - struct S109 has key, drop { x: u64 } - struct S110 has key, drop { x: u64 } - struct S111 has key, drop { x: u64 } - struct S112 has key, drop { x: u64 } - struct S113 has key, drop { x: u64 } - struct S114 has key, drop { x: u64 } - struct S115 has key, drop { x: u64 } - struct S116 has key, drop { x: u64 } - struct S117 has key, drop { x: u64 } - struct S118 has key, drop { x: u64 } - struct S119 has key, drop { x: u64 } - struct S120 has key, drop { x: u64 } - struct S121 has key, drop { x: u64 } - struct S122 has key, drop { x: u64 } - struct S123 has key, drop { x: u64 } - struct S124 has key, drop { x: u64 } - struct S125 has key, drop { x: u64 } - struct S126 has key, drop { x: u64 } - struct S127 has key, drop { x: u64 } - struct S128 has key, drop { x: u64 } - struct S129 has key, drop { x: u64 } - struct S130 has key, drop { x: u64 } - struct S131 has key, drop { x: u64 } - struct S132 has key, drop { x: u64 } - struct S133 has key, drop { x: u64 } - struct S134 has key, drop { x: u64 } - struct S135 has key, drop { x: u64 } - struct S136 has key, drop { x: u64 } - struct S137 has key, drop { x: u64 } - struct S138 has key, drop { x: u64 } - struct S139 has key, drop { x: u64 } - struct S140 has key, drop { x: u64 } - struct S141 has key, drop { x: u64 } - struct S142 has key, drop { x: u64 } - struct S143 has key, drop { x: u64 } - struct S144 has key, drop { x: u64 } - struct S145 has key, drop { x: u64 } - struct S146 has key, drop { x: u64 } - struct S147 has key, drop { x: u64 } - struct S148 has key, drop { x: u64 } - struct S149 has key, drop { x: u64 } - struct S150 has key, drop { x: u64 } - struct S151 has key, drop { x: u64 } - struct S152 has key, drop { x: u64 } - struct S153 has key, drop { x: u64 } - struct S154 has key, drop { x: u64 } - struct S155 has key, drop { x: u64 } - struct S156 has key, drop { x: u64 } - struct S157 has key, drop { x: u64 } - struct S158 has key, drop { x: u64 } - struct S159 has key, drop { x: u64 } - struct S160 has key, drop { x: u64 } - struct S161 has key, drop { x: u64 } - struct S162 has key, drop { x: u64 } - struct S163 has key, drop { x: u64 } - struct S164 has key, drop { x: u64 } - struct S165 has key, drop { x: u64 } - struct S166 has key, drop { x: u64 } - struct S167 has key, drop { x: u64 } - struct S168 has key, drop { x: u64 } - struct S169 has key, drop { x: u64 } - struct S170 has key, drop { x: u64 } - struct S171 has key, drop { x: u64 } - struct S172 has key, drop { x: u64 } - struct S173 has key, drop { x: u64 } - struct S174 has key, drop { x: u64 } - struct S175 has key, drop { x: u64 } - struct S176 has key, drop { x: u64 } - struct S177 has key, drop { x: u64 } - struct S178 has key, drop { x: u64 } - struct S179 has key, drop { x: u64 } - struct S180 has key, drop { x: u64 } - struct S181 has key, drop { x: u64 } - struct S182 has key, drop { x: u64 } - struct S183 has key, drop { x: u64 } - struct S184 has key, drop { x: u64 } - struct S185 has key, drop { x: u64 } - struct S186 has key, drop { x: u64 } - struct S187 has key, drop { x: u64 } - struct S188 has key, drop { x: u64 } - struct S189 has key, drop { x: u64 } - struct S190 has key, drop { x: u64 } - struct S191 has key, drop { x: u64 } - struct S192 has key, drop { x: u64 } - struct S193 has key, drop { x: u64 } - struct S194 has key, drop { x: u64 } - struct S195 has key, drop { x: u64 } - struct S196 has key, drop { x: u64 } - struct S197 has key, drop { x: u64 } - struct S198 has key, drop { x: u64 } - struct S199 has key, drop { x: u64 } - struct S200 has key, drop { x: u64 } - struct S201 has key, drop { x: u64 } - struct S202 has key, drop { x: u64 } - struct S203 has key, drop { x: u64 } - struct S204 has key, drop { x: u64 } - struct S205 has key, drop { x: u64 } - struct S206 has key, drop { x: u64 } - struct S207 has key, drop { x: u64 } - struct S208 has key, drop { x: u64 } - struct S209 has key, drop { x: u64 } - struct S210 has key, drop { x: u64 } - struct S211 has key, drop { x: u64 } - struct S212 has key, drop { x: u64 } - struct S213 has key, drop { x: u64 } - struct S214 has key, drop { x: u64 } - struct S215 has key, drop { x: u64 } - struct S216 has key, drop { x: u64 } - struct S217 has key, drop { x: u64 } - struct S218 has key, drop { x: u64 } - struct S219 has key, drop { x: u64 } - struct S220 has key, drop { x: u64 } - struct S221 has key, drop { x: u64 } - struct S222 has key, drop { x: u64 } - struct S223 has key, drop { x: u64 } - struct S224 has key, drop { x: u64 } - struct S225 has key, drop { x: u64 } - struct S226 has key, drop { x: u64 } - struct S227 has key, drop { x: u64 } - struct S228 has key, drop { x: u64 } - struct S229 has key, drop { x: u64 } - struct S230 has key, drop { x: u64 } - struct S231 has key, drop { x: u64 } - struct S232 has key, drop { x: u64 } - struct S233 has key, drop { x: u64 } - struct S234 has key, drop { x: u64 } - struct S235 has key, drop { x: u64 } - struct S236 has key, drop { x: u64 } - struct S237 has key, drop { x: u64 } - struct S238 has key, drop { x: u64 } - struct S239 has key, drop { x: u64 } - struct S240 has key, drop { x: u64 } - struct S241 has key, drop { x: u64 } - struct S242 has key, drop { x: u64 } - struct S243 has key, drop { x: u64 } - struct S244 has key, drop { x: u64 } - struct S245 has key, drop { x: u64 } - struct S246 has key, drop { x: u64 } - struct S247 has key, drop { x: u64 } - struct S248 has key, drop { x: u64 } - struct S249 has key, drop { x: u64 } - struct S250 has key, drop { x: u64 } - struct S251 has key, drop { x: u64 } - struct S252 has key, drop { x: u64 } - struct S253 has key, drop { x: u64 } - struct S254 has key, drop { x: u64 } - struct S255 has key, drop { x: u64 } - struct S256 has key, drop { x: u64 } - struct S257 has key, drop { x: u64 } - struct S258 has key, drop { x: u64 } - struct S259 has key, drop { x: u64 } - struct S260 has key, drop { x: u64 } - struct S261 has key, drop { x: u64 } - struct S262 has key, drop { x: u64 } - struct S263 has key, drop { x: u64 } - struct S264 has key, drop { x: u64 } - struct S265 has key, drop { x: u64 } - struct S266 has key, drop { x: u64 } - struct S267 has key, drop { x: u64 } - struct S268 has key, drop { x: u64 } - struct S269 has key, drop { x: u64 } - struct S270 has key, drop { x: u64 } - struct S271 has key, drop { x: u64 } - struct S272 has key, drop { x: u64 } - struct S273 has key, drop { x: u64 } - struct S274 has key, drop { x: u64 } - struct S275 has key, drop { x: u64 } - struct S276 has key, drop { x: u64 } - struct S277 has key, drop { x: u64 } - struct S278 has key, drop { x: u64 } - struct S279 has key, drop { x: u64 } - struct S280 has key, drop { x: u64 } - struct S281 has key, drop { x: u64 } - struct S282 has key, drop { x: u64 } - struct S283 has key, drop { x: u64 } - struct S284 has key, drop { x: u64 } - struct S285 has key, drop { x: u64 } - struct S286 has key, drop { x: u64 } - struct S287 has key, drop { x: u64 } - struct S288 has key, drop { x: u64 } - struct S289 has key, drop { x: u64 } - struct S290 has key, drop { x: u64 } - struct S291 has key, drop { x: u64 } - struct S292 has key, drop { x: u64 } - struct S293 has key, drop { x: u64 } - struct S294 has key, drop { x: u64 } - struct S295 has key, drop { x: u64 } - struct S296 has key, drop { x: u64 } - struct S297 has key, drop { x: u64 } - struct S298 has key, drop { x: u64 } - struct S299 has key, drop { x: u64 } - struct S300 has key, drop { x: u64 } - struct S301 has key, drop { x: u64 } - struct S302 has key, drop { x: u64 } - struct S303 has key, drop { x: u64 } - struct S304 has key, drop { x: u64 } - struct S305 has key, drop { x: u64 } - struct S306 has key, drop { x: u64 } - struct S307 has key, drop { x: u64 } - struct S308 has key, drop { x: u64 } - struct S309 has key, drop { x: u64 } - struct S310 has key, drop { x: u64 } - struct S311 has key, drop { x: u64 } - struct S312 has key, drop { x: u64 } - struct S313 has key, drop { x: u64 } - struct S314 has key, drop { x: u64 } - struct S315 has key, drop { x: u64 } - struct S316 has key, drop { x: u64 } - struct S317 has key, drop { x: u64 } - struct S318 has key, drop { x: u64 } - struct S319 has key, drop { x: u64 } - struct S320 has key, drop { x: u64 } - struct S321 has key, drop { x: u64 } - struct S322 has key, drop { x: u64 } - struct S323 has key, drop { x: u64 } - struct S324 has key, drop { x: u64 } - struct S325 has key, drop { x: u64 } - struct S326 has key, drop { x: u64 } - struct S327 has key, drop { x: u64 } - struct S328 has key, drop { x: u64 } - struct S329 has key, drop { x: u64 } - struct S330 has key, drop { x: u64 } - struct S331 has key, drop { x: u64 } - struct S332 has key, drop { x: u64 } - struct S333 has key, drop { x: u64 } - struct S334 has key, drop { x: u64 } - struct S335 has key, drop { x: u64 } - struct S336 has key, drop { x: u64 } - struct S337 has key, drop { x: u64 } - struct S338 has key, drop { x: u64 } - struct S339 has key, drop { x: u64 } - struct S340 has key, drop { x: u64 } - struct S341 has key, drop { x: u64 } - struct S342 has key, drop { x: u64 } - struct S343 has key, drop { x: u64 } - struct S344 has key, drop { x: u64 } - struct S345 has key, drop { x: u64 } - struct S346 has key, drop { x: u64 } - struct S347 has key, drop { x: u64 } - struct S348 has key, drop { x: u64 } - struct S349 has key, drop { x: u64 } - struct S350 has key, drop { x: u64 } - struct S351 has key, drop { x: u64 } - struct S352 has key, drop { x: u64 } - struct S353 has key, drop { x: u64 } - struct S354 has key, drop { x: u64 } - struct S355 has key, drop { x: u64 } - struct S356 has key, drop { x: u64 } - struct S357 has key, drop { x: u64 } - struct S358 has key, drop { x: u64 } - struct S359 has key, drop { x: u64 } - struct S360 has key, drop { x: u64 } - struct S361 has key, drop { x: u64 } - struct S362 has key, drop { x: u64 } - struct S363 has key, drop { x: u64 } - struct S364 has key, drop { x: u64 } - struct S365 has key, drop { x: u64 } - struct S366 has key, drop { x: u64 } - struct S367 has key, drop { x: u64 } - struct S368 has key, drop { x: u64 } - struct S369 has key, drop { x: u64 } - struct S370 has key, drop { x: u64 } - struct S371 has key, drop { x: u64 } - struct S372 has key, drop { x: u64 } - struct S373 has key, drop { x: u64 } - struct S374 has key, drop { x: u64 } - struct S375 has key, drop { x: u64 } - struct S376 has key, drop { x: u64 } - struct S377 has key, drop { x: u64 } - struct S378 has key, drop { x: u64 } - struct S379 has key, drop { x: u64 } - struct S380 has key, drop { x: u64 } - struct S381 has key, drop { x: u64 } - struct S382 has key, drop { x: u64 } - struct S383 has key, drop { x: u64 } - struct S384 has key, drop { x: u64 } - struct S385 has key, drop { x: u64 } - struct S386 has key, drop { x: u64 } - struct S387 has key, drop { x: u64 } - struct S388 has key, drop { x: u64 } - struct S389 has key, drop { x: u64 } - struct S390 has key, drop { x: u64 } - struct S391 has key, drop { x: u64 } - struct S392 has key, drop { x: u64 } - struct S393 has key, drop { x: u64 } - struct S394 has key, drop { x: u64 } - struct S395 has key, drop { x: u64 } - struct S396 has key, drop { x: u64 } - struct S397 has key, drop { x: u64 } - struct S398 has key, drop { x: u64 } - struct S399 has key, drop { x: u64 } - struct S400 has key, drop { x: u64 } - struct S401 has key, drop { x: u64 } - struct S402 has key, drop { x: u64 } - struct S403 has key, drop { x: u64 } - struct S404 has key, drop { x: u64 } - struct S405 has key, drop { x: u64 } - struct S406 has key, drop { x: u64 } - struct S407 has key, drop { x: u64 } - struct S408 has key, drop { x: u64 } - struct S409 has key, drop { x: u64 } - struct S410 has key, drop { x: u64 } - struct S411 has key, drop { x: u64 } - struct S412 has key, drop { x: u64 } - struct S413 has key, drop { x: u64 } - struct S414 has key, drop { x: u64 } - struct S415 has key, drop { x: u64 } - struct S416 has key, drop { x: u64 } - struct S417 has key, drop { x: u64 } - struct S418 has key, drop { x: u64 } - struct S419 has key, drop { x: u64 } - struct S420 has key, drop { x: u64 } - struct S421 has key, drop { x: u64 } - struct S422 has key, drop { x: u64 } - struct S423 has key, drop { x: u64 } - struct S424 has key, drop { x: u64 } - struct S425 has key, drop { x: u64 } - struct S426 has key, drop { x: u64 } - struct S427 has key, drop { x: u64 } - struct S428 has key, drop { x: u64 } - struct S429 has key, drop { x: u64 } - struct S430 has key, drop { x: u64 } - struct S431 has key, drop { x: u64 } - struct S432 has key, drop { x: u64 } - struct S433 has key, drop { x: u64 } - struct S434 has key, drop { x: u64 } - struct S435 has key, drop { x: u64 } - struct S436 has key, drop { x: u64 } - struct S437 has key, drop { x: u64 } - struct S438 has key, drop { x: u64 } - struct S439 has key, drop { x: u64 } - struct S440 has key, drop { x: u64 } - struct S441 has key, drop { x: u64 } - struct S442 has key, drop { x: u64 } - struct S443 has key, drop { x: u64 } - struct S444 has key, drop { x: u64 } - struct S445 has key, drop { x: u64 } - struct S446 has key, drop { x: u64 } - struct S447 has key, drop { x: u64 } - struct S448 has key, drop { x: u64 } - struct S449 has key, drop { x: u64 } - struct S450 has key, drop { x: u64 } - struct S451 has key, drop { x: u64 } - struct S452 has key, drop { x: u64 } - struct S453 has key, drop { x: u64 } - struct S454 has key, drop { x: u64 } - struct S455 has key, drop { x: u64 } - struct S456 has key, drop { x: u64 } - struct S457 has key, drop { x: u64 } - struct S458 has key, drop { x: u64 } - struct S459 has key, drop { x: u64 } - struct S460 has key, drop { x: u64 } - struct S461 has key, drop { x: u64 } - struct S462 has key, drop { x: u64 } - struct S463 has key, drop { x: u64 } - struct S464 has key, drop { x: u64 } - struct S465 has key, drop { x: u64 } - struct S466 has key, drop { x: u64 } - struct S467 has key, drop { x: u64 } - struct S468 has key, drop { x: u64 } - struct S469 has key, drop { x: u64 } - struct S470 has key, drop { x: u64 } - struct S471 has key, drop { x: u64 } - struct S472 has key, drop { x: u64 } - struct S473 has key, drop { x: u64 } - struct S474 has key, drop { x: u64 } - struct S475 has key, drop { x: u64 } - struct S476 has key, drop { x: u64 } - struct S477 has key, drop { x: u64 } - struct S478 has key, drop { x: u64 } - struct S479 has key, drop { x: u64 } - struct S480 has key, drop { x: u64 } - struct S481 has key, drop { x: u64 } - struct S482 has key, drop { x: u64 } - struct S483 has key, drop { x: u64 } - struct S484 has key, drop { x: u64 } - struct S485 has key, drop { x: u64 } - struct S486 has key, drop { x: u64 } - struct S487 has key, drop { x: u64 } - struct S488 has key, drop { x: u64 } - struct S489 has key, drop { x: u64 } - struct S490 has key, drop { x: u64 } - struct S491 has key, drop { x: u64 } - struct S492 has key, drop { x: u64 } - struct S493 has key, drop { x: u64 } - struct S494 has key, drop { x: u64 } - struct S495 has key, drop { x: u64 } - struct S496 has key, drop { x: u64 } - struct S497 has key, drop { x: u64 } - struct S498 has key, drop { x: u64 } - struct S499 has key, drop { x: u64 } - struct S500 has key, drop { x: u64 } - struct S501 has key, drop { x: u64 } - struct S502 has key, drop { x: u64 } - struct S503 has key, drop { x: u64 } - struct S504 has key, drop { x: u64 } - struct S505 has key, drop { x: u64 } - struct S506 has key, drop { x: u64 } - struct S507 has key, drop { x: u64 } - struct S508 has key, drop { x: u64 } - struct S509 has key, drop { x: u64 } - struct S510 has key, drop { x: u64 } - struct S511 has key, drop { x: u64 } - struct S512 has key, drop { x: u64 } - struct S513 has key, drop { x: u64 } - struct S514 has key, drop { x: u64 } - struct S515 has key, drop { x: u64 } - struct S516 has key, drop { x: u64 } - struct S517 has key, drop { x: u64 } - struct S518 has key, drop { x: u64 } - struct S519 has key, drop { x: u64 } - struct S520 has key, drop { x: u64 } - struct S521 has key, drop { x: u64 } - struct S522 has key, drop { x: u64 } - struct S523 has key, drop { x: u64 } - struct S524 has key, drop { x: u64 } - struct S525 has key, drop { x: u64 } - struct S526 has key, drop { x: u64 } - struct S527 has key, drop { x: u64 } - struct S528 has key, drop { x: u64 } - struct S529 has key, drop { x: u64 } - struct S530 has key, drop { x: u64 } - struct S531 has key, drop { x: u64 } - struct S532 has key, drop { x: u64 } - struct S533 has key, drop { x: u64 } - struct S534 has key, drop { x: u64 } - struct S535 has key, drop { x: u64 } - struct S536 has key, drop { x: u64 } - struct S537 has key, drop { x: u64 } - struct S538 has key, drop { x: u64 } - struct S539 has key, drop { x: u64 } - struct S540 has key, drop { x: u64 } - struct S541 has key, drop { x: u64 } - struct S542 has key, drop { x: u64 } - struct S543 has key, drop { x: u64 } - struct S544 has key, drop { x: u64 } - struct S545 has key, drop { x: u64 } - struct S546 has key, drop { x: u64 } - struct S547 has key, drop { x: u64 } - struct S548 has key, drop { x: u64 } - struct S549 has key, drop { x: u64 } - struct S550 has key, drop { x: u64 } - struct S551 has key, drop { x: u64 } - struct S552 has key, drop { x: u64 } - struct S553 has key, drop { x: u64 } - struct S554 has key, drop { x: u64 } - struct S555 has key, drop { x: u64 } - struct S556 has key, drop { x: u64 } - struct S557 has key, drop { x: u64 } - struct S558 has key, drop { x: u64 } - struct S559 has key, drop { x: u64 } - struct S560 has key, drop { x: u64 } - struct S561 has key, drop { x: u64 } - struct S562 has key, drop { x: u64 } - struct S563 has key, drop { x: u64 } - struct S564 has key, drop { x: u64 } - struct S565 has key, drop { x: u64 } - struct S566 has key, drop { x: u64 } - struct S567 has key, drop { x: u64 } - struct S568 has key, drop { x: u64 } - struct S569 has key, drop { x: u64 } - struct S570 has key, drop { x: u64 } - struct S571 has key, drop { x: u64 } - struct S572 has key, drop { x: u64 } - struct S573 has key, drop { x: u64 } - struct S574 has key, drop { x: u64 } - struct S575 has key, drop { x: u64 } - struct S576 has key, drop { x: u64 } - struct S577 has key, drop { x: u64 } - struct S578 has key, drop { x: u64 } - struct S579 has key, drop { x: u64 } - struct S580 has key, drop { x: u64 } - struct S581 has key, drop { x: u64 } - struct S582 has key, drop { x: u64 } - struct S583 has key, drop { x: u64 } - struct S584 has key, drop { x: u64 } - struct S585 has key, drop { x: u64 } - struct S586 has key, drop { x: u64 } - struct S587 has key, drop { x: u64 } - struct S588 has key, drop { x: u64 } - struct S589 has key, drop { x: u64 } - struct S590 has key, drop { x: u64 } - struct S591 has key, drop { x: u64 } - struct S592 has key, drop { x: u64 } - struct S593 has key, drop { x: u64 } - struct S594 has key, drop { x: u64 } - struct S595 has key, drop { x: u64 } - struct S596 has key, drop { x: u64 } - struct S597 has key, drop { x: u64 } - struct S598 has key, drop { x: u64 } - struct S599 has key, drop { x: u64 } - struct S600 has key, drop { x: u64 } - struct S601 has key, drop { x: u64 } - struct S602 has key, drop { x: u64 } - struct S603 has key, drop { x: u64 } - struct S604 has key, drop { x: u64 } - struct S605 has key, drop { x: u64 } - struct S606 has key, drop { x: u64 } - struct S607 has key, drop { x: u64 } - struct S608 has key, drop { x: u64 } - struct S609 has key, drop { x: u64 } - struct S610 has key, drop { x: u64 } - struct S611 has key, drop { x: u64 } - struct S612 has key, drop { x: u64 } - struct S613 has key, drop { x: u64 } - struct S614 has key, drop { x: u64 } - struct S615 has key, drop { x: u64 } - struct S616 has key, drop { x: u64 } - struct S617 has key, drop { x: u64 } - struct S618 has key, drop { x: u64 } - struct S619 has key, drop { x: u64 } - struct S620 has key, drop { x: u64 } - struct S621 has key, drop { x: u64 } - struct S622 has key, drop { x: u64 } - struct S623 has key, drop { x: u64 } - struct S624 has key, drop { x: u64 } - struct S625 has key, drop { x: u64 } - struct S626 has key, drop { x: u64 } - struct S627 has key, drop { x: u64 } - struct S628 has key, drop { x: u64 } - struct S629 has key, drop { x: u64 } - struct S630 has key, drop { x: u64 } - struct S631 has key, drop { x: u64 } - struct S632 has key, drop { x: u64 } - struct S633 has key, drop { x: u64 } - struct S634 has key, drop { x: u64 } - struct S635 has key, drop { x: u64 } - struct S636 has key, drop { x: u64 } - struct S637 has key, drop { x: u64 } - struct S638 has key, drop { x: u64 } - struct S639 has key, drop { x: u64 } - struct S640 has key, drop { x: u64 } - struct S641 has key, drop { x: u64 } - struct S642 has key, drop { x: u64 } - struct S643 has key, drop { x: u64 } - struct S644 has key, drop { x: u64 } - struct S645 has key, drop { x: u64 } - struct S646 has key, drop { x: u64 } - struct S647 has key, drop { x: u64 } - struct S648 has key, drop { x: u64 } - struct S649 has key, drop { x: u64 } - struct S650 has key, drop { x: u64 } - struct S651 has key, drop { x: u64 } - struct S652 has key, drop { x: u64 } - struct S653 has key, drop { x: u64 } - struct S654 has key, drop { x: u64 } - struct S655 has key, drop { x: u64 } - struct S656 has key, drop { x: u64 } - struct S657 has key, drop { x: u64 } - struct S658 has key, drop { x: u64 } - struct S659 has key, drop { x: u64 } - struct S660 has key, drop { x: u64 } - struct S661 has key, drop { x: u64 } - struct S662 has key, drop { x: u64 } - struct S663 has key, drop { x: u64 } - struct S664 has key, drop { x: u64 } - struct S665 has key, drop { x: u64 } - struct S666 has key, drop { x: u64 } - struct S667 has key, drop { x: u64 } - struct S668 has key, drop { x: u64 } - struct S669 has key, drop { x: u64 } - struct S670 has key, drop { x: u64 } - struct S671 has key, drop { x: u64 } - struct S672 has key, drop { x: u64 } - struct S673 has key, drop { x: u64 } - struct S674 has key, drop { x: u64 } - struct S675 has key, drop { x: u64 } - struct S676 has key, drop { x: u64 } - struct S677 has key, drop { x: u64 } - struct S678 has key, drop { x: u64 } - struct S679 has key, drop { x: u64 } - struct S680 has key, drop { x: u64 } - struct S681 has key, drop { x: u64 } - struct S682 has key, drop { x: u64 } - struct S683 has key, drop { x: u64 } - struct S684 has key, drop { x: u64 } - struct S685 has key, drop { x: u64 } - struct S686 has key, drop { x: u64 } - struct S687 has key, drop { x: u64 } - struct S688 has key, drop { x: u64 } - struct S689 has key, drop { x: u64 } - struct S690 has key, drop { x: u64 } - struct S691 has key, drop { x: u64 } - struct S692 has key, drop { x: u64 } - struct S693 has key, drop { x: u64 } - struct S694 has key, drop { x: u64 } - struct S695 has key, drop { x: u64 } - struct S696 has key, drop { x: u64 } - struct S697 has key, drop { x: u64 } - struct S698 has key, drop { x: u64 } - struct S699 has key, drop { x: u64 } - struct S700 has key, drop { x: u64 } - struct S701 has key, drop { x: u64 } - struct S702 has key, drop { x: u64 } - struct S703 has key, drop { x: u64 } - struct S704 has key, drop { x: u64 } - struct S705 has key, drop { x: u64 } - struct S706 has key, drop { x: u64 } - struct S707 has key, drop { x: u64 } - struct S708 has key, drop { x: u64 } - struct S709 has key, drop { x: u64 } - struct S710 has key, drop { x: u64 } - struct S711 has key, drop { x: u64 } - struct S712 has key, drop { x: u64 } - struct S713 has key, drop { x: u64 } - struct S714 has key, drop { x: u64 } - struct S715 has key, drop { x: u64 } - struct S716 has key, drop { x: u64 } - struct S717 has key, drop { x: u64 } - struct S718 has key, drop { x: u64 } - struct S719 has key, drop { x: u64 } - struct S720 has key, drop { x: u64 } - struct S721 has key, drop { x: u64 } - struct S722 has key, drop { x: u64 } - struct S723 has key, drop { x: u64 } - struct S724 has key, drop { x: u64 } - struct S725 has key, drop { x: u64 } - struct S726 has key, drop { x: u64 } - struct S727 has key, drop { x: u64 } - struct S728 has key, drop { x: u64 } - struct S729 has key, drop { x: u64 } - struct S730 has key, drop { x: u64 } - struct S731 has key, drop { x: u64 } - struct S732 has key, drop { x: u64 } - struct S733 has key, drop { x: u64 } - struct S734 has key, drop { x: u64 } - struct S735 has key, drop { x: u64 } - struct S736 has key, drop { x: u64 } - struct S737 has key, drop { x: u64 } - struct S738 has key, drop { x: u64 } - struct S739 has key, drop { x: u64 } - struct S740 has key, drop { x: u64 } - struct S741 has key, drop { x: u64 } - struct S742 has key, drop { x: u64 } - struct S743 has key, drop { x: u64 } - struct S744 has key, drop { x: u64 } - struct S745 has key, drop { x: u64 } - struct S746 has key, drop { x: u64 } - struct S747 has key, drop { x: u64 } - struct S748 has key, drop { x: u64 } - struct S749 has key, drop { x: u64 } - struct S750 has key, drop { x: u64 } - struct S751 has key, drop { x: u64 } - struct S752 has key, drop { x: u64 } - struct S753 has key, drop { x: u64 } - struct S754 has key, drop { x: u64 } - struct S755 has key, drop { x: u64 } - struct S756 has key, drop { x: u64 } - struct S757 has key, drop { x: u64 } - struct S758 has key, drop { x: u64 } - struct S759 has key, drop { x: u64 } - struct S760 has key, drop { x: u64 } - struct S761 has key, drop { x: u64 } - struct S762 has key, drop { x: u64 } - struct S763 has key, drop { x: u64 } - struct S764 has key, drop { x: u64 } - struct S765 has key, drop { x: u64 } - struct S766 has key, drop { x: u64 } - struct S767 has key, drop { x: u64 } - struct S768 has key, drop { x: u64 } - struct S769 has key, drop { x: u64 } - struct S770 has key, drop { x: u64 } - struct S771 has key, drop { x: u64 } - struct S772 has key, drop { x: u64 } - struct S773 has key, drop { x: u64 } - struct S774 has key, drop { x: u64 } - struct S775 has key, drop { x: u64 } - struct S776 has key, drop { x: u64 } - struct S777 has key, drop { x: u64 } - struct S778 has key, drop { x: u64 } - struct S779 has key, drop { x: u64 } - struct S780 has key, drop { x: u64 } - struct S781 has key, drop { x: u64 } - struct S782 has key, drop { x: u64 } - struct S783 has key, drop { x: u64 } - struct S784 has key, drop { x: u64 } - struct S785 has key, drop { x: u64 } - struct S786 has key, drop { x: u64 } - struct S787 has key, drop { x: u64 } - struct S788 has key, drop { x: u64 } - struct S789 has key, drop { x: u64 } - struct S790 has key, drop { x: u64 } - struct S791 has key, drop { x: u64 } - struct S792 has key, drop { x: u64 } - struct S793 has key, drop { x: u64 } - struct S794 has key, drop { x: u64 } - struct S795 has key, drop { x: u64 } - struct S796 has key, drop { x: u64 } - struct S797 has key, drop { x: u64 } - struct S798 has key, drop { x: u64 } - struct S799 has key, drop { x: u64 } - struct S800 has key, drop { x: u64 } - struct S801 has key, drop { x: u64 } - struct S802 has key, drop { x: u64 } - struct S803 has key, drop { x: u64 } - struct S804 has key, drop { x: u64 } - struct S805 has key, drop { x: u64 } - struct S806 has key, drop { x: u64 } - struct S807 has key, drop { x: u64 } - struct S808 has key, drop { x: u64 } - struct S809 has key, drop { x: u64 } - struct S810 has key, drop { x: u64 } - struct S811 has key, drop { x: u64 } - struct S812 has key, drop { x: u64 } - struct S813 has key, drop { x: u64 } - struct S814 has key, drop { x: u64 } - struct S815 has key, drop { x: u64 } - struct S816 has key, drop { x: u64 } - struct S817 has key, drop { x: u64 } - struct S818 has key, drop { x: u64 } - struct S819 has key, drop { x: u64 } - struct S820 has key, drop { x: u64 } - struct S821 has key, drop { x: u64 } - struct S822 has key, drop { x: u64 } - struct S823 has key, drop { x: u64 } - struct S824 has key, drop { x: u64 } - struct S825 has key, drop { x: u64 } - struct S826 has key, drop { x: u64 } - struct S827 has key, drop { x: u64 } - struct S828 has key, drop { x: u64 } - struct S829 has key, drop { x: u64 } - struct S830 has key, drop { x: u64 } - struct S831 has key, drop { x: u64 } - struct S832 has key, drop { x: u64 } - struct S833 has key, drop { x: u64 } - struct S834 has key, drop { x: u64 } - struct S835 has key, drop { x: u64 } - struct S836 has key, drop { x: u64 } - struct S837 has key, drop { x: u64 } - struct S838 has key, drop { x: u64 } - struct S839 has key, drop { x: u64 } - struct S840 has key, drop { x: u64 } - struct S841 has key, drop { x: u64 } - struct S842 has key, drop { x: u64 } - struct S843 has key, drop { x: u64 } - struct S844 has key, drop { x: u64 } - struct S845 has key, drop { x: u64 } - struct S846 has key, drop { x: u64 } - struct S847 has key, drop { x: u64 } - struct S848 has key, drop { x: u64 } - struct S849 has key, drop { x: u64 } - struct S850 has key, drop { x: u64 } - struct S851 has key, drop { x: u64 } - struct S852 has key, drop { x: u64 } - struct S853 has key, drop { x: u64 } - struct S854 has key, drop { x: u64 } - struct S855 has key, drop { x: u64 } - struct S856 has key, drop { x: u64 } - struct S857 has key, drop { x: u64 } - struct S858 has key, drop { x: u64 } - struct S859 has key, drop { x: u64 } - struct S860 has key, drop { x: u64 } - struct S861 has key, drop { x: u64 } - struct S862 has key, drop { x: u64 } - struct S863 has key, drop { x: u64 } - struct S864 has key, drop { x: u64 } - struct S865 has key, drop { x: u64 } - struct S866 has key, drop { x: u64 } - struct S867 has key, drop { x: u64 } - struct S868 has key, drop { x: u64 } - struct S869 has key, drop { x: u64 } - struct S870 has key, drop { x: u64 } - struct S871 has key, drop { x: u64 } - struct S872 has key, drop { x: u64 } - struct S873 has key, drop { x: u64 } - struct S874 has key, drop { x: u64 } - struct S875 has key, drop { x: u64 } - struct S876 has key, drop { x: u64 } - struct S877 has key, drop { x: u64 } - struct S878 has key, drop { x: u64 } - struct S879 has key, drop { x: u64 } - struct S880 has key, drop { x: u64 } - struct S881 has key, drop { x: u64 } - struct S882 has key, drop { x: u64 } - struct S883 has key, drop { x: u64 } - struct S884 has key, drop { x: u64 } - struct S885 has key, drop { x: u64 } - struct S886 has key, drop { x: u64 } - struct S887 has key, drop { x: u64 } - struct S888 has key, drop { x: u64 } - struct S889 has key, drop { x: u64 } - struct S890 has key, drop { x: u64 } - struct S891 has key, drop { x: u64 } - struct S892 has key, drop { x: u64 } - struct S893 has key, drop { x: u64 } - struct S894 has key, drop { x: u64 } - struct S895 has key, drop { x: u64 } - struct S896 has key, drop { x: u64 } - struct S897 has key, drop { x: u64 } - struct S898 has key, drop { x: u64 } - struct S899 has key, drop { x: u64 } - struct S900 has key, drop { x: u64 } - struct S901 has key, drop { x: u64 } - struct S902 has key, drop { x: u64 } - struct S903 has key, drop { x: u64 } - struct S904 has key, drop { x: u64 } - struct S905 has key, drop { x: u64 } - struct S906 has key, drop { x: u64 } - struct S907 has key, drop { x: u64 } - struct S908 has key, drop { x: u64 } - struct S909 has key, drop { x: u64 } - struct S910 has key, drop { x: u64 } - struct S911 has key, drop { x: u64 } - struct S912 has key, drop { x: u64 } - struct S913 has key, drop { x: u64 } - struct S914 has key, drop { x: u64 } - struct S915 has key, drop { x: u64 } - struct S916 has key, drop { x: u64 } - struct S917 has key, drop { x: u64 } - struct S918 has key, drop { x: u64 } - struct S919 has key, drop { x: u64 } - struct S920 has key, drop { x: u64 } - struct S921 has key, drop { x: u64 } - struct S922 has key, drop { x: u64 } - struct S923 has key, drop { x: u64 } - struct S924 has key, drop { x: u64 } - struct S925 has key, drop { x: u64 } - struct S926 has key, drop { x: u64 } - struct S927 has key, drop { x: u64 } - struct S928 has key, drop { x: u64 } - struct S929 has key, drop { x: u64 } - struct S930 has key, drop { x: u64 } - struct S931 has key, drop { x: u64 } - struct S932 has key, drop { x: u64 } - struct S933 has key, drop { x: u64 } - struct S934 has key, drop { x: u64 } - struct S935 has key, drop { x: u64 } - struct S936 has key, drop { x: u64 } - struct S937 has key, drop { x: u64 } - struct S938 has key, drop { x: u64 } - struct S939 has key, drop { x: u64 } - struct S940 has key, drop { x: u64 } - struct S941 has key, drop { x: u64 } - struct S942 has key, drop { x: u64 } - struct S943 has key, drop { x: u64 } - struct S944 has key, drop { x: u64 } - struct S945 has key, drop { x: u64 } - struct S946 has key, drop { x: u64 } - struct S947 has key, drop { x: u64 } - struct S948 has key, drop { x: u64 } - struct S949 has key, drop { x: u64 } - struct S950 has key, drop { x: u64 } - struct S951 has key, drop { x: u64 } - struct S952 has key, drop { x: u64 } - struct S953 has key, drop { x: u64 } - struct S954 has key, drop { x: u64 } - struct S955 has key, drop { x: u64 } - struct S956 has key, drop { x: u64 } - struct S957 has key, drop { x: u64 } - struct S958 has key, drop { x: u64 } - struct S959 has key, drop { x: u64 } - struct S960 has key, drop { x: u64 } - struct S961 has key, drop { x: u64 } - struct S962 has key, drop { x: u64 } - struct S963 has key, drop { x: u64 } - struct S964 has key, drop { x: u64 } - struct S965 has key, drop { x: u64 } - struct S966 has key, drop { x: u64 } - struct S967 has key, drop { x: u64 } - struct S968 has key, drop { x: u64 } - struct S969 has key, drop { x: u64 } - struct S970 has key, drop { x: u64 } - struct S971 has key, drop { x: u64 } - struct S972 has key, drop { x: u64 } - struct S973 has key, drop { x: u64 } - struct S974 has key, drop { x: u64 } - struct S975 has key, drop { x: u64 } - struct S976 has key, drop { x: u64 } - struct S977 has key, drop { x: u64 } - struct S978 has key, drop { x: u64 } - struct S979 has key, drop { x: u64 } - struct S980 has key, drop { x: u64 } - struct S981 has key, drop { x: u64 } - struct S982 has key, drop { x: u64 } - struct S983 has key, drop { x: u64 } - struct S984 has key, drop { x: u64 } - struct S985 has key, drop { x: u64 } - struct S986 has key, drop { x: u64 } - struct S987 has key, drop { x: u64 } - struct S988 has key, drop { x: u64 } - struct S989 has key, drop { x: u64 } - struct S990 has key, drop { x: u64 } - struct S991 has key, drop { x: u64 } - struct S992 has key, drop { x: u64 } - struct S993 has key, drop { x: u64 } - struct S994 has key, drop { x: u64 } - struct S995 has key, drop { x: u64 } - struct S996 has key, drop { x: u64 } - struct S997 has key, drop { x: u64 } - struct S998 has key, drop { x: u64 } - struct S999 has key, drop { x: u64 } - struct S1000 has key, drop { x: u64 } - struct S1001 has key, drop { x: u64 } - struct S1002 has key, drop { x: u64 } - struct S1003 has key, drop { x: u64 } - struct S1004 has key, drop { x: u64 } - struct S1005 has key, drop { x: u64 } - struct S1006 has key, drop { x: u64 } - struct S1007 has key, drop { x: u64 } - struct S1008 has key, drop { x: u64 } - struct S1009 has key, drop { x: u64 } - struct S1010 has key, drop { x: u64 } - struct S1011 has key, drop { x: u64 } - struct S1012 has key, drop { x: u64 } - struct S1013 has key, drop { x: u64 } - struct S1014 has key, drop { x: u64 } - struct S1015 has key, drop { x: u64 } - struct S1016 has key, drop { x: u64 } - struct S1017 has key, drop { x: u64 } - struct S1018 has key, drop { x: u64 } - struct S1019 has key, drop { x: u64 } - struct S1020 has key, drop { x: u64 } - struct S1021 has key, drop { x: u64 } - struct S1022 has key, drop { x: u64 } - struct S1023 has key, drop { x: u64 } - struct S1024 has key, drop { x: u64 } - struct S1025 has key, drop { x: u64 } - struct S1026 has key, drop { x: u64 } - struct S1027 has key, drop { x: u64 } - struct S1028 has key, drop { x: u64 } - struct S1029 has key, drop { x: u64 } - struct S1030 has key, drop { x: u64 } - struct S1031 has key, drop { x: u64 } - struct S1032 has key, drop { x: u64 } - struct S1033 has key, drop { x: u64 } - struct S1034 has key, drop { x: u64 } - struct S1035 has key, drop { x: u64 } - struct S1036 has key, drop { x: u64 } - struct S1037 has key, drop { x: u64 } - struct S1038 has key, drop { x: u64 } - struct S1039 has key, drop { x: u64 } - struct S1040 has key, drop { x: u64 } - struct S1041 has key, drop { x: u64 } - struct S1042 has key, drop { x: u64 } - struct S1043 has key, drop { x: u64 } - struct S1044 has key, drop { x: u64 } - struct S1045 has key, drop { x: u64 } - struct S1046 has key, drop { x: u64 } - struct S1047 has key, drop { x: u64 } - struct S1048 has key, drop { x: u64 } - struct S1049 has key, drop { x: u64 } - struct S1050 has key, drop { x: u64 } - struct S1051 has key, drop { x: u64 } - struct S1052 has key, drop { x: u64 } - struct S1053 has key, drop { x: u64 } - struct S1054 has key, drop { x: u64 } - struct S1055 has key, drop { x: u64 } - struct S1056 has key, drop { x: u64 } - struct S1057 has key, drop { x: u64 } - struct S1058 has key, drop { x: u64 } - struct S1059 has key, drop { x: u64 } - struct S1060 has key, drop { x: u64 } - struct S1061 has key, drop { x: u64 } - struct S1062 has key, drop { x: u64 } - struct S1063 has key, drop { x: u64 } - struct S1064 has key, drop { x: u64 } - struct S1065 has key, drop { x: u64 } - struct S1066 has key, drop { x: u64 } - struct S1067 has key, drop { x: u64 } - struct S1068 has key, drop { x: u64 } - struct S1069 has key, drop { x: u64 } - struct S1070 has key, drop { x: u64 } - struct S1071 has key, drop { x: u64 } - struct S1072 has key, drop { x: u64 } - struct S1073 has key, drop { x: u64 } - struct S1074 has key, drop { x: u64 } - struct S1075 has key, drop { x: u64 } - struct S1076 has key, drop { x: u64 } - struct S1077 has key, drop { x: u64 } - struct S1078 has key, drop { x: u64 } - struct S1079 has key, drop { x: u64 } - struct S1080 has key, drop { x: u64 } - struct S1081 has key, drop { x: u64 } - struct S1082 has key, drop { x: u64 } - struct S1083 has key, drop { x: u64 } - struct S1084 has key, drop { x: u64 } - struct S1085 has key, drop { x: u64 } - struct S1086 has key, drop { x: u64 } - struct S1087 has key, drop { x: u64 } - struct S1088 has key, drop { x: u64 } - struct S1089 has key, drop { x: u64 } - struct S1090 has key, drop { x: u64 } - struct S1091 has key, drop { x: u64 } - struct S1092 has key, drop { x: u64 } - struct S1093 has key, drop { x: u64 } - struct S1094 has key, drop { x: u64 } - struct S1095 has key, drop { x: u64 } - struct S1096 has key, drop { x: u64 } - struct S1097 has key, drop { x: u64 } - struct S1098 has key, drop { x: u64 } - struct S1099 has key, drop { x: u64 } - struct S1100 has key, drop { x: u64 } - struct S1101 has key, drop { x: u64 } - struct S1102 has key, drop { x: u64 } - struct S1103 has key, drop { x: u64 } - struct S1104 has key, drop { x: u64 } - struct S1105 has key, drop { x: u64 } - struct S1106 has key, drop { x: u64 } - struct S1107 has key, drop { x: u64 } - struct S1108 has key, drop { x: u64 } - struct S1109 has key, drop { x: u64 } - struct S1110 has key, drop { x: u64 } - struct S1111 has key, drop { x: u64 } - struct S1112 has key, drop { x: u64 } - struct S1113 has key, drop { x: u64 } - struct S1114 has key, drop { x: u64 } - struct S1115 has key, drop { x: u64 } - struct S1116 has key, drop { x: u64 } - struct S1117 has key, drop { x: u64 } - struct S1118 has key, drop { x: u64 } - struct S1119 has key, drop { x: u64 } - struct S1120 has key, drop { x: u64 } - struct S1121 has key, drop { x: u64 } - struct S1122 has key, drop { x: u64 } - struct S1123 has key, drop { x: u64 } - struct S1124 has key, drop { x: u64 } - struct S1125 has key, drop { x: u64 } - struct S1126 has key, drop { x: u64 } - struct S1127 has key, drop { x: u64 } - struct S1128 has key, drop { x: u64 } - struct S1129 has key, drop { x: u64 } - struct S1130 has key, drop { x: u64 } - struct S1131 has key, drop { x: u64 } - struct S1132 has key, drop { x: u64 } - struct S1133 has key, drop { x: u64 } - struct S1134 has key, drop { x: u64 } - struct S1135 has key, drop { x: u64 } - struct S1136 has key, drop { x: u64 } - struct S1137 has key, drop { x: u64 } - struct S1138 has key, drop { x: u64 } - struct S1139 has key, drop { x: u64 } - struct S1140 has key, drop { x: u64 } - struct S1141 has key, drop { x: u64 } - struct S1142 has key, drop { x: u64 } - struct S1143 has key, drop { x: u64 } - struct S1144 has key, drop { x: u64 } - struct S1145 has key, drop { x: u64 } - struct S1146 has key, drop { x: u64 } - struct S1147 has key, drop { x: u64 } - struct S1148 has key, drop { x: u64 } - struct S1149 has key, drop { x: u64 } - struct S1150 has key, drop { x: u64 } - struct S1151 has key, drop { x: u64 } - struct S1152 has key, drop { x: u64 } - struct S1153 has key, drop { x: u64 } - struct S1154 has key, drop { x: u64 } - struct S1155 has key, drop { x: u64 } - struct S1156 has key, drop { x: u64 } - struct S1157 has key, drop { x: u64 } - struct S1158 has key, drop { x: u64 } - struct S1159 has key, drop { x: u64 } - struct S1160 has key, drop { x: u64 } - struct S1161 has key, drop { x: u64 } - struct S1162 has key, drop { x: u64 } - struct S1163 has key, drop { x: u64 } - struct S1164 has key, drop { x: u64 } - struct S1165 has key, drop { x: u64 } - struct S1166 has key, drop { x: u64 } - struct S1167 has key, drop { x: u64 } - struct S1168 has key, drop { x: u64 } - struct S1169 has key, drop { x: u64 } - struct S1170 has key, drop { x: u64 } - struct S1171 has key, drop { x: u64 } - struct S1172 has key, drop { x: u64 } - struct S1173 has key, drop { x: u64 } - struct S1174 has key, drop { x: u64 } - struct S1175 has key, drop { x: u64 } - struct S1176 has key, drop { x: u64 } - struct S1177 has key, drop { x: u64 } - struct S1178 has key, drop { x: u64 } - struct S1179 has key, drop { x: u64 } - struct S1180 has key, drop { x: u64 } - struct S1181 has key, drop { x: u64 } - struct S1182 has key, drop { x: u64 } - struct S1183 has key, drop { x: u64 } - struct S1184 has key, drop { x: u64 } - struct S1185 has key, drop { x: u64 } - struct S1186 has key, drop { x: u64 } - struct S1187 has key, drop { x: u64 } - struct S1188 has key, drop { x: u64 } - struct S1189 has key, drop { x: u64 } - struct S1190 has key, drop { x: u64 } - struct S1191 has key, drop { x: u64 } - struct S1192 has key, drop { x: u64 } - struct S1193 has key, drop { x: u64 } - struct S1194 has key, drop { x: u64 } - struct S1195 has key, drop { x: u64 } - struct S1196 has key, drop { x: u64 } - struct S1197 has key, drop { x: u64 } - struct S1198 has key, drop { x: u64 } - struct S1199 has key, drop { x: u64 } - struct S1200 has key, drop { x: u64 } - struct S1201 has key, drop { x: u64 } - struct S1202 has key, drop { x: u64 } - struct S1203 has key, drop { x: u64 } - struct S1204 has key, drop { x: u64 } - struct S1205 has key, drop { x: u64 } - struct S1206 has key, drop { x: u64 } - struct S1207 has key, drop { x: u64 } - struct S1208 has key, drop { x: u64 } - struct S1209 has key, drop { x: u64 } - struct S1210 has key, drop { x: u64 } - struct S1211 has key, drop { x: u64 } - struct S1212 has key, drop { x: u64 } - struct S1213 has key, drop { x: u64 } - struct S1214 has key, drop { x: u64 } - struct S1215 has key, drop { x: u64 } - struct S1216 has key, drop { x: u64 } - struct S1217 has key, drop { x: u64 } - struct S1218 has key, drop { x: u64 } - struct S1219 has key, drop { x: u64 } - struct S1220 has key, drop { x: u64 } - struct S1221 has key, drop { x: u64 } - struct S1222 has key, drop { x: u64 } - struct S1223 has key, drop { x: u64 } - struct S1224 has key, drop { x: u64 } - struct S1225 has key, drop { x: u64 } - struct S1226 has key, drop { x: u64 } - struct S1227 has key, drop { x: u64 } - struct S1228 has key, drop { x: u64 } - struct S1229 has key, drop { x: u64 } - struct S1230 has key, drop { x: u64 } - struct S1231 has key, drop { x: u64 } - struct S1232 has key, drop { x: u64 } - struct S1233 has key, drop { x: u64 } - struct S1234 has key, drop { x: u64 } - struct S1235 has key, drop { x: u64 } - struct S1236 has key, drop { x: u64 } - struct S1237 has key, drop { x: u64 } - struct S1238 has key, drop { x: u64 } - struct S1239 has key, drop { x: u64 } - struct S1240 has key, drop { x: u64 } - struct S1241 has key, drop { x: u64 } - struct S1242 has key, drop { x: u64 } - struct S1243 has key, drop { x: u64 } - struct S1244 has key, drop { x: u64 } - struct S1245 has key, drop { x: u64 } - struct S1246 has key, drop { x: u64 } - struct S1247 has key, drop { x: u64 } - struct S1248 has key, drop { x: u64 } - struct S1249 has key, drop { x: u64 } - struct S1250 has key, drop { x: u64 } - struct S1251 has key, drop { x: u64 } - struct S1252 has key, drop { x: u64 } - struct S1253 has key, drop { x: u64 } - struct S1254 has key, drop { x: u64 } - struct S1255 has key, drop { x: u64 } - struct S1256 has key, drop { x: u64 } - struct S1257 has key, drop { x: u64 } - struct S1258 has key, drop { x: u64 } - struct S1259 has key, drop { x: u64 } - struct S1260 has key, drop { x: u64 } - struct S1261 has key, drop { x: u64 } - struct S1262 has key, drop { x: u64 } - struct S1263 has key, drop { x: u64 } - struct S1264 has key, drop { x: u64 } - struct S1265 has key, drop { x: u64 } - struct S1266 has key, drop { x: u64 } - struct S1267 has key, drop { x: u64 } - struct S1268 has key, drop { x: u64 } - struct S1269 has key, drop { x: u64 } - struct S1270 has key, drop { x: u64 } - struct S1271 has key, drop { x: u64 } - struct S1272 has key, drop { x: u64 } - struct S1273 has key, drop { x: u64 } - struct S1274 has key, drop { x: u64 } - struct S1275 has key, drop { x: u64 } - struct S1276 has key, drop { x: u64 } - struct S1277 has key, drop { x: u64 } - struct S1278 has key, drop { x: u64 } - struct S1279 has key, drop { x: u64 } - struct S1280 has key, drop { x: u64 } - struct S1281 has key, drop { x: u64 } - struct S1282 has key, drop { x: u64 } - struct S1283 has key, drop { x: u64 } - struct S1284 has key, drop { x: u64 } - struct S1285 has key, drop { x: u64 } - struct S1286 has key, drop { x: u64 } - struct S1287 has key, drop { x: u64 } - struct S1288 has key, drop { x: u64 } - struct S1289 has key, drop { x: u64 } - struct S1290 has key, drop { x: u64 } - struct S1291 has key, drop { x: u64 } - struct S1292 has key, drop { x: u64 } - struct S1293 has key, drop { x: u64 } - struct S1294 has key, drop { x: u64 } - struct S1295 has key, drop { x: u64 } - struct S1296 has key, drop { x: u64 } - struct S1297 has key, drop { x: u64 } - struct S1298 has key, drop { x: u64 } - struct S1299 has key, drop { x: u64 } - struct S1300 has key, drop { x: u64 } - struct S1301 has key, drop { x: u64 } - struct S1302 has key, drop { x: u64 } - struct S1303 has key, drop { x: u64 } - struct S1304 has key, drop { x: u64 } - struct S1305 has key, drop { x: u64 } - struct S1306 has key, drop { x: u64 } - struct S1307 has key, drop { x: u64 } - struct S1308 has key, drop { x: u64 } - struct S1309 has key, drop { x: u64 } - struct S1310 has key, drop { x: u64 } - struct S1311 has key, drop { x: u64 } - struct S1312 has key, drop { x: u64 } - struct S1313 has key, drop { x: u64 } - struct S1314 has key, drop { x: u64 } - struct S1315 has key, drop { x: u64 } - struct S1316 has key, drop { x: u64 } - struct S1317 has key, drop { x: u64 } - struct S1318 has key, drop { x: u64 } - struct S1319 has key, drop { x: u64 } - struct S1320 has key, drop { x: u64 } - struct S1321 has key, drop { x: u64 } - struct S1322 has key, drop { x: u64 } - struct S1323 has key, drop { x: u64 } - struct S1324 has key, drop { x: u64 } - struct S1325 has key, drop { x: u64 } - struct S1326 has key, drop { x: u64 } - struct S1327 has key, drop { x: u64 } - struct S1328 has key, drop { x: u64 } - struct S1329 has key, drop { x: u64 } - struct S1330 has key, drop { x: u64 } - struct S1331 has key, drop { x: u64 } - struct S1332 has key, drop { x: u64 } - struct S1333 has key, drop { x: u64 } - struct S1334 has key, drop { x: u64 } - struct S1335 has key, drop { x: u64 } - struct S1336 has key, drop { x: u64 } - struct S1337 has key, drop { x: u64 } - struct S1338 has key, drop { x: u64 } - struct S1339 has key, drop { x: u64 } - struct S1340 has key, drop { x: u64 } - struct S1341 has key, drop { x: u64 } - struct S1342 has key, drop { x: u64 } - struct S1343 has key, drop { x: u64 } - struct S1344 has key, drop { x: u64 } - struct S1345 has key, drop { x: u64 } - struct S1346 has key, drop { x: u64 } - struct S1347 has key, drop { x: u64 } - struct S1348 has key, drop { x: u64 } - struct S1349 has key, drop { x: u64 } - struct S1350 has key, drop { x: u64 } - struct S1351 has key, drop { x: u64 } - struct S1352 has key, drop { x: u64 } - struct S1353 has key, drop { x: u64 } - struct S1354 has key, drop { x: u64 } - struct S1355 has key, drop { x: u64 } - struct S1356 has key, drop { x: u64 } - struct S1357 has key, drop { x: u64 } - struct S1358 has key, drop { x: u64 } - struct S1359 has key, drop { x: u64 } - struct S1360 has key, drop { x: u64 } - struct S1361 has key, drop { x: u64 } - struct S1362 has key, drop { x: u64 } - struct S1363 has key, drop { x: u64 } - struct S1364 has key, drop { x: u64 } - struct S1365 has key, drop { x: u64 } - struct S1366 has key, drop { x: u64 } - struct S1367 has key, drop { x: u64 } - struct S1368 has key, drop { x: u64 } - struct S1369 has key, drop { x: u64 } - struct S1370 has key, drop { x: u64 } - struct S1371 has key, drop { x: u64 } - struct S1372 has key, drop { x: u64 } - struct S1373 has key, drop { x: u64 } - struct S1374 has key, drop { x: u64 } - struct S1375 has key, drop { x: u64 } - struct S1376 has key, drop { x: u64 } - struct S1377 has key, drop { x: u64 } - struct S1378 has key, drop { x: u64 } - struct S1379 has key, drop { x: u64 } - struct S1380 has key, drop { x: u64 } - struct S1381 has key, drop { x: u64 } - struct S1382 has key, drop { x: u64 } - struct S1383 has key, drop { x: u64 } - struct S1384 has key, drop { x: u64 } - struct S1385 has key, drop { x: u64 } - struct S1386 has key, drop { x: u64 } - struct S1387 has key, drop { x: u64 } - struct S1388 has key, drop { x: u64 } - struct S1389 has key, drop { x: u64 } - struct S1390 has key, drop { x: u64 } - struct S1391 has key, drop { x: u64 } - struct S1392 has key, drop { x: u64 } - struct S1393 has key, drop { x: u64 } - struct S1394 has key, drop { x: u64 } - struct S1395 has key, drop { x: u64 } - struct S1396 has key, drop { x: u64 } - struct S1397 has key, drop { x: u64 } - struct S1398 has key, drop { x: u64 } - struct S1399 has key, drop { x: u64 } - struct S1400 has key, drop { x: u64 } - struct S1401 has key, drop { x: u64 } - struct S1402 has key, drop { x: u64 } - struct S1403 has key, drop { x: u64 } - struct S1404 has key, drop { x: u64 } - struct S1405 has key, drop { x: u64 } - struct S1406 has key, drop { x: u64 } - struct S1407 has key, drop { x: u64 } - struct S1408 has key, drop { x: u64 } - struct S1409 has key, drop { x: u64 } - struct S1410 has key, drop { x: u64 } - struct S1411 has key, drop { x: u64 } - struct S1412 has key, drop { x: u64 } - struct S1413 has key, drop { x: u64 } - struct S1414 has key, drop { x: u64 } - struct S1415 has key, drop { x: u64 } - struct S1416 has key, drop { x: u64 } - struct S1417 has key, drop { x: u64 } - struct S1418 has key, drop { x: u64 } - struct S1419 has key, drop { x: u64 } - struct S1420 has key, drop { x: u64 } - struct S1421 has key, drop { x: u64 } - struct S1422 has key, drop { x: u64 } - struct S1423 has key, drop { x: u64 } - struct S1424 has key, drop { x: u64 } - struct S1425 has key, drop { x: u64 } - struct S1426 has key, drop { x: u64 } - struct S1427 has key, drop { x: u64 } - struct S1428 has key, drop { x: u64 } - struct S1429 has key, drop { x: u64 } - struct S1430 has key, drop { x: u64 } - struct S1431 has key, drop { x: u64 } - struct S1432 has key, drop { x: u64 } - struct S1433 has key, drop { x: u64 } - struct S1434 has key, drop { x: u64 } - struct S1435 has key, drop { x: u64 } - struct S1436 has key, drop { x: u64 } - struct S1437 has key, drop { x: u64 } - struct S1438 has key, drop { x: u64 } - struct S1439 has key, drop { x: u64 } - struct S1440 has key, drop { x: u64 } - struct S1441 has key, drop { x: u64 } - struct S1442 has key, drop { x: u64 } - struct S1443 has key, drop { x: u64 } - struct S1444 has key, drop { x: u64 } - struct S1445 has key, drop { x: u64 } - struct S1446 has key, drop { x: u64 } - struct S1447 has key, drop { x: u64 } - struct S1448 has key, drop { x: u64 } - struct S1449 has key, drop { x: u64 } - struct S1450 has key, drop { x: u64 } - struct S1451 has key, drop { x: u64 } - struct S1452 has key, drop { x: u64 } - struct S1453 has key, drop { x: u64 } - struct S1454 has key, drop { x: u64 } - struct S1455 has key, drop { x: u64 } - struct S1456 has key, drop { x: u64 } - struct S1457 has key, drop { x: u64 } - struct S1458 has key, drop { x: u64 } - struct S1459 has key, drop { x: u64 } - struct S1460 has key, drop { x: u64 } - struct S1461 has key, drop { x: u64 } - struct S1462 has key, drop { x: u64 } - struct S1463 has key, drop { x: u64 } - struct S1464 has key, drop { x: u64 } - struct S1465 has key, drop { x: u64 } - struct S1466 has key, drop { x: u64 } - struct S1467 has key, drop { x: u64 } - struct S1468 has key, drop { x: u64 } - struct S1469 has key, drop { x: u64 } - struct S1470 has key, drop { x: u64 } - struct S1471 has key, drop { x: u64 } - struct S1472 has key, drop { x: u64 } - struct S1473 has key, drop { x: u64 } - struct S1474 has key, drop { x: u64 } - struct S1475 has key, drop { x: u64 } - struct S1476 has key, drop { x: u64 } - struct S1477 has key, drop { x: u64 } - struct S1478 has key, drop { x: u64 } - struct S1479 has key, drop { x: u64 } - struct S1480 has key, drop { x: u64 } - struct S1481 has key, drop { x: u64 } - struct S1482 has key, drop { x: u64 } - struct S1483 has key, drop { x: u64 } - struct S1484 has key, drop { x: u64 } - struct S1485 has key, drop { x: u64 } - struct S1486 has key, drop { x: u64 } - struct S1487 has key, drop { x: u64 } - struct S1488 has key, drop { x: u64 } - struct S1489 has key, drop { x: u64 } - struct S1490 has key, drop { x: u64 } - struct S1491 has key, drop { x: u64 } - struct S1492 has key, drop { x: u64 } - struct S1493 has key, drop { x: u64 } - struct S1494 has key, drop { x: u64 } - struct S1495 has key, drop { x: u64 } - struct S1496 has key, drop { x: u64 } - struct S1497 has key, drop { x: u64 } - struct S1498 has key, drop { x: u64 } - struct S1499 has key, drop { x: u64 } - struct S1500 has key, drop { x: u64 } - struct S1501 has key, drop { x: u64 } - struct S1502 has key, drop { x: u64 } - struct S1503 has key, drop { x: u64 } - struct S1504 has key, drop { x: u64 } - struct S1505 has key, drop { x: u64 } - struct S1506 has key, drop { x: u64 } - struct S1507 has key, drop { x: u64 } - struct S1508 has key, drop { x: u64 } - struct S1509 has key, drop { x: u64 } - struct S1510 has key, drop { x: u64 } - struct S1511 has key, drop { x: u64 } - struct S1512 has key, drop { x: u64 } - struct S1513 has key, drop { x: u64 } - struct S1514 has key, drop { x: u64 } - struct S1515 has key, drop { x: u64 } - struct S1516 has key, drop { x: u64 } - struct S1517 has key, drop { x: u64 } - struct S1518 has key, drop { x: u64 } - struct S1519 has key, drop { x: u64 } - struct S1520 has key, drop { x: u64 } - struct S1521 has key, drop { x: u64 } - struct S1522 has key, drop { x: u64 } - struct S1523 has key, drop { x: u64 } - struct S1524 has key, drop { x: u64 } - struct S1525 has key, drop { x: u64 } - struct S1526 has key, drop { x: u64 } - struct S1527 has key, drop { x: u64 } - struct S1528 has key, drop { x: u64 } - struct S1529 has key, drop { x: u64 } - struct S1530 has key, drop { x: u64 } - struct S1531 has key, drop { x: u64 } - struct S1532 has key, drop { x: u64 } - struct S1533 has key, drop { x: u64 } - struct S1534 has key, drop { x: u64 } - struct S1535 has key, drop { x: u64 } - struct S1536 has key, drop { x: u64 } - struct S1537 has key, drop { x: u64 } - struct S1538 has key, drop { x: u64 } - struct S1539 has key, drop { x: u64 } - struct S1540 has key, drop { x: u64 } - struct S1541 has key, drop { x: u64 } - struct S1542 has key, drop { x: u64 } - struct S1543 has key, drop { x: u64 } - struct S1544 has key, drop { x: u64 } - struct S1545 has key, drop { x: u64 } - struct S1546 has key, drop { x: u64 } - struct S1547 has key, drop { x: u64 } - struct S1548 has key, drop { x: u64 } - struct S1549 has key, drop { x: u64 } - struct S1550 has key, drop { x: u64 } - struct S1551 has key, drop { x: u64 } - struct S1552 has key, drop { x: u64 } - struct S1553 has key, drop { x: u64 } - struct S1554 has key, drop { x: u64 } - struct S1555 has key, drop { x: u64 } - struct S1556 has key, drop { x: u64 } - struct S1557 has key, drop { x: u64 } - struct S1558 has key, drop { x: u64 } - struct S1559 has key, drop { x: u64 } - struct S1560 has key, drop { x: u64 } - struct S1561 has key, drop { x: u64 } - struct S1562 has key, drop { x: u64 } - struct S1563 has key, drop { x: u64 } - struct S1564 has key, drop { x: u64 } - struct S1565 has key, drop { x: u64 } - struct S1566 has key, drop { x: u64 } - struct S1567 has key, drop { x: u64 } - struct S1568 has key, drop { x: u64 } - struct S1569 has key, drop { x: u64 } - struct S1570 has key, drop { x: u64 } - struct S1571 has key, drop { x: u64 } - struct S1572 has key, drop { x: u64 } - struct S1573 has key, drop { x: u64 } - struct S1574 has key, drop { x: u64 } - struct S1575 has key, drop { x: u64 } - struct S1576 has key, drop { x: u64 } - struct S1577 has key, drop { x: u64 } - struct S1578 has key, drop { x: u64 } - struct S1579 has key, drop { x: u64 } - struct S1580 has key, drop { x: u64 } - struct S1581 has key, drop { x: u64 } - struct S1582 has key, drop { x: u64 } - struct S1583 has key, drop { x: u64 } - struct S1584 has key, drop { x: u64 } - struct S1585 has key, drop { x: u64 } - struct S1586 has key, drop { x: u64 } - struct S1587 has key, drop { x: u64 } - struct S1588 has key, drop { x: u64 } - struct S1589 has key, drop { x: u64 } - struct S1590 has key, drop { x: u64 } - struct S1591 has key, drop { x: u64 } - struct S1592 has key, drop { x: u64 } - struct S1593 has key, drop { x: u64 } - struct S1594 has key, drop { x: u64 } - struct S1595 has key, drop { x: u64 } - struct S1596 has key, drop { x: u64 } - struct S1597 has key, drop { x: u64 } - struct S1598 has key, drop { x: u64 } - struct S1599 has key, drop { x: u64 } - struct S1600 has key, drop { x: u64 } - - public entry calibrate_move_to_x100(s: signer) { - label b0: - move_to(&s, S1{ x: 0 }); - move_to(&s, S2{ x: 0 }); - move_to(&s, S3{ x: 0 }); - move_to(&s, S4{ x: 0 }); - move_to(&s, S5{ x: 0 }); - move_to(&s, S6{ x: 0 }); - move_to(&s, S7{ x: 0 }); - move_to(&s, S8{ x: 0 }); - move_to(&s, S9{ x: 0 }); - move_to(&s, S10{ x: 0 }); - move_to(&s, S11{ x: 0 }); - move_to(&s, S12{ x: 0 }); - move_to(&s, S13{ x: 0 }); - move_to(&s, S14{ x: 0 }); - move_to(&s, S15{ x: 0 }); - move_to(&s, S16{ x: 0 }); - move_to(&s, S17{ x: 0 }); - move_to(&s, S18{ x: 0 }); - move_to(&s, S19{ x: 0 }); - move_to(&s, S20{ x: 0 }); - move_to(&s, S21{ x: 0 }); - move_to(&s, S22{ x: 0 }); - move_to(&s, S23{ x: 0 }); - move_to(&s, S24{ x: 0 }); - move_to(&s, S25{ x: 0 }); - move_to(&s, S26{ x: 0 }); - move_to(&s, S27{ x: 0 }); - move_to(&s, S28{ x: 0 }); - move_to(&s, S29{ x: 0 }); - move_to(&s, S30{ x: 0 }); - move_to(&s, S31{ x: 0 }); - move_to(&s, S32{ x: 0 }); - move_to(&s, S33{ x: 0 }); - move_to(&s, S34{ x: 0 }); - move_to(&s, S35{ x: 0 }); - move_to(&s, S36{ x: 0 }); - move_to(&s, S37{ x: 0 }); - move_to(&s, S38{ x: 0 }); - move_to(&s, S39{ x: 0 }); - move_to(&s, S40{ x: 0 }); - move_to(&s, S41{ x: 0 }); - move_to(&s, S42{ x: 0 }); - move_to(&s, S43{ x: 0 }); - move_to(&s, S44{ x: 0 }); - move_to(&s, S45{ x: 0 }); - move_to(&s, S46{ x: 0 }); - move_to(&s, S47{ x: 0 }); - move_to(&s, S48{ x: 0 }); - move_to(&s, S49{ x: 0 }); - move_to(&s, S50{ x: 0 }); - move_to(&s, S51{ x: 0 }); - move_to(&s, S52{ x: 0 }); - move_to(&s, S53{ x: 0 }); - move_to(&s, S54{ x: 0 }); - move_to(&s, S55{ x: 0 }); - move_to(&s, S56{ x: 0 }); - move_to(&s, S57{ x: 0 }); - move_to(&s, S58{ x: 0 }); - move_to(&s, S59{ x: 0 }); - move_to(&s, S60{ x: 0 }); - move_to(&s, S61{ x: 0 }); - move_to(&s, S62{ x: 0 }); - move_to(&s, S63{ x: 0 }); - move_to(&s, S64{ x: 0 }); - move_to(&s, S65{ x: 0 }); - move_to(&s, S66{ x: 0 }); - move_to(&s, S67{ x: 0 }); - move_to(&s, S68{ x: 0 }); - move_to(&s, S69{ x: 0 }); - move_to(&s, S70{ x: 0 }); - move_to(&s, S71{ x: 0 }); - move_to(&s, S72{ x: 0 }); - move_to(&s, S73{ x: 0 }); - move_to(&s, S74{ x: 0 }); - move_to(&s, S75{ x: 0 }); - move_to(&s, S76{ x: 0 }); - move_to(&s, S77{ x: 0 }); - move_to(&s, S78{ x: 0 }); - move_to(&s, S79{ x: 0 }); - move_to(&s, S80{ x: 0 }); - move_to(&s, S81{ x: 0 }); - move_to(&s, S82{ x: 0 }); - move_to(&s, S83{ x: 0 }); - move_to(&s, S84{ x: 0 }); - move_to(&s, S85{ x: 0 }); - move_to(&s, S86{ x: 0 }); - move_to(&s, S87{ x: 0 }); - move_to(&s, S88{ x: 0 }); - move_to(&s, S89{ x: 0 }); - move_to(&s, S90{ x: 0 }); - move_to(&s, S91{ x: 0 }); - move_to(&s, S92{ x: 0 }); - move_to(&s, S93{ x: 0 }); - move_to(&s, S94{ x: 0 }); - move_to(&s, S95{ x: 0 }); - move_to(&s, S96{ x: 0 }); - move_to(&s, S97{ x: 0 }); - move_to(&s, S98{ x: 0 }); - move_to(&s, S99{ x: 0 }); - move_to(&s, S100{ x: 0 }); - return; - } - - public entry calibrate_move_to_x500(s: signer) { - label b0: - move_to(&s, S101{ x: 0 }); - move_to(&s, S102{ x: 0 }); - move_to(&s, S103{ x: 0 }); - move_to(&s, S104{ x: 0 }); - move_to(&s, S105{ x: 0 }); - move_to(&s, S106{ x: 0 }); - move_to(&s, S107{ x: 0 }); - move_to(&s, S108{ x: 0 }); - move_to(&s, S109{ x: 0 }); - move_to(&s, S110{ x: 0 }); - move_to(&s, S111{ x: 0 }); - move_to(&s, S112{ x: 0 }); - move_to(&s, S113{ x: 0 }); - move_to(&s, S114{ x: 0 }); - move_to(&s, S115{ x: 0 }); - move_to(&s, S116{ x: 0 }); - move_to(&s, S117{ x: 0 }); - move_to(&s, S118{ x: 0 }); - move_to(&s, S119{ x: 0 }); - move_to(&s, S120{ x: 0 }); - move_to(&s, S121{ x: 0 }); - move_to(&s, S122{ x: 0 }); - move_to(&s, S123{ x: 0 }); - move_to(&s, S124{ x: 0 }); - move_to(&s, S125{ x: 0 }); - move_to(&s, S126{ x: 0 }); - move_to(&s, S127{ x: 0 }); - move_to(&s, S128{ x: 0 }); - move_to(&s, S129{ x: 0 }); - move_to(&s, S130{ x: 0 }); - move_to(&s, S131{ x: 0 }); - move_to(&s, S132{ x: 0 }); - move_to(&s, S133{ x: 0 }); - move_to(&s, S134{ x: 0 }); - move_to(&s, S135{ x: 0 }); - move_to(&s, S136{ x: 0 }); - move_to(&s, S137{ x: 0 }); - move_to(&s, S138{ x: 0 }); - move_to(&s, S139{ x: 0 }); - move_to(&s, S140{ x: 0 }); - move_to(&s, S141{ x: 0 }); - move_to(&s, S142{ x: 0 }); - move_to(&s, S143{ x: 0 }); - move_to(&s, S144{ x: 0 }); - move_to(&s, S145{ x: 0 }); - move_to(&s, S146{ x: 0 }); - move_to(&s, S147{ x: 0 }); - move_to(&s, S148{ x: 0 }); - move_to(&s, S149{ x: 0 }); - move_to(&s, S150{ x: 0 }); - move_to(&s, S151{ x: 0 }); - move_to(&s, S152{ x: 0 }); - move_to(&s, S153{ x: 0 }); - move_to(&s, S154{ x: 0 }); - move_to(&s, S155{ x: 0 }); - move_to(&s, S156{ x: 0 }); - move_to(&s, S157{ x: 0 }); - move_to(&s, S158{ x: 0 }); - move_to(&s, S159{ x: 0 }); - move_to(&s, S160{ x: 0 }); - move_to(&s, S161{ x: 0 }); - move_to(&s, S162{ x: 0 }); - move_to(&s, S163{ x: 0 }); - move_to(&s, S164{ x: 0 }); - move_to(&s, S165{ x: 0 }); - move_to(&s, S166{ x: 0 }); - move_to(&s, S167{ x: 0 }); - move_to(&s, S168{ x: 0 }); - move_to(&s, S169{ x: 0 }); - move_to(&s, S170{ x: 0 }); - move_to(&s, S171{ x: 0 }); - move_to(&s, S172{ x: 0 }); - move_to(&s, S173{ x: 0 }); - move_to(&s, S174{ x: 0 }); - move_to(&s, S175{ x: 0 }); - move_to(&s, S176{ x: 0 }); - move_to(&s, S177{ x: 0 }); - move_to(&s, S178{ x: 0 }); - move_to(&s, S179{ x: 0 }); - move_to(&s, S180{ x: 0 }); - move_to(&s, S181{ x: 0 }); - move_to(&s, S182{ x: 0 }); - move_to(&s, S183{ x: 0 }); - move_to(&s, S184{ x: 0 }); - move_to(&s, S185{ x: 0 }); - move_to(&s, S186{ x: 0 }); - move_to(&s, S187{ x: 0 }); - move_to(&s, S188{ x: 0 }); - move_to(&s, S189{ x: 0 }); - move_to(&s, S190{ x: 0 }); - move_to(&s, S191{ x: 0 }); - move_to(&s, S192{ x: 0 }); - move_to(&s, S193{ x: 0 }); - move_to(&s, S194{ x: 0 }); - move_to(&s, S195{ x: 0 }); - move_to(&s, S196{ x: 0 }); - move_to(&s, S197{ x: 0 }); - move_to(&s, S198{ x: 0 }); - move_to(&s, S199{ x: 0 }); - move_to(&s, S200{ x: 0 }); - move_to(&s, S201{ x: 0 }); - move_to(&s, S202{ x: 0 }); - move_to(&s, S203{ x: 0 }); - move_to(&s, S204{ x: 0 }); - move_to(&s, S205{ x: 0 }); - move_to(&s, S206{ x: 0 }); - move_to(&s, S207{ x: 0 }); - move_to(&s, S208{ x: 0 }); - move_to(&s, S209{ x: 0 }); - move_to(&s, S210{ x: 0 }); - move_to(&s, S211{ x: 0 }); - move_to(&s, S212{ x: 0 }); - move_to(&s, S213{ x: 0 }); - move_to(&s, S214{ x: 0 }); - move_to(&s, S215{ x: 0 }); - move_to(&s, S216{ x: 0 }); - move_to(&s, S217{ x: 0 }); - move_to(&s, S218{ x: 0 }); - move_to(&s, S219{ x: 0 }); - move_to(&s, S220{ x: 0 }); - move_to(&s, S221{ x: 0 }); - move_to(&s, S222{ x: 0 }); - move_to(&s, S223{ x: 0 }); - move_to(&s, S224{ x: 0 }); - move_to(&s, S225{ x: 0 }); - move_to(&s, S226{ x: 0 }); - move_to(&s, S227{ x: 0 }); - move_to(&s, S228{ x: 0 }); - move_to(&s, S229{ x: 0 }); - move_to(&s, S230{ x: 0 }); - move_to(&s, S231{ x: 0 }); - move_to(&s, S232{ x: 0 }); - move_to(&s, S233{ x: 0 }); - move_to(&s, S234{ x: 0 }); - move_to(&s, S235{ x: 0 }); - move_to(&s, S236{ x: 0 }); - move_to(&s, S237{ x: 0 }); - move_to(&s, S238{ x: 0 }); - move_to(&s, S239{ x: 0 }); - move_to(&s, S240{ x: 0 }); - move_to(&s, S241{ x: 0 }); - move_to(&s, S242{ x: 0 }); - move_to(&s, S243{ x: 0 }); - move_to(&s, S244{ x: 0 }); - move_to(&s, S245{ x: 0 }); - move_to(&s, S246{ x: 0 }); - move_to(&s, S247{ x: 0 }); - move_to(&s, S248{ x: 0 }); - move_to(&s, S249{ x: 0 }); - move_to(&s, S250{ x: 0 }); - move_to(&s, S251{ x: 0 }); - move_to(&s, S252{ x: 0 }); - move_to(&s, S253{ x: 0 }); - move_to(&s, S254{ x: 0 }); - move_to(&s, S255{ x: 0 }); - move_to(&s, S256{ x: 0 }); - move_to(&s, S257{ x: 0 }); - move_to(&s, S258{ x: 0 }); - move_to(&s, S259{ x: 0 }); - move_to(&s, S260{ x: 0 }); - move_to(&s, S261{ x: 0 }); - move_to(&s, S262{ x: 0 }); - move_to(&s, S263{ x: 0 }); - move_to(&s, S264{ x: 0 }); - move_to(&s, S265{ x: 0 }); - move_to(&s, S266{ x: 0 }); - move_to(&s, S267{ x: 0 }); - move_to(&s, S268{ x: 0 }); - move_to(&s, S269{ x: 0 }); - move_to(&s, S270{ x: 0 }); - move_to(&s, S271{ x: 0 }); - move_to(&s, S272{ x: 0 }); - move_to(&s, S273{ x: 0 }); - move_to(&s, S274{ x: 0 }); - move_to(&s, S275{ x: 0 }); - move_to(&s, S276{ x: 0 }); - move_to(&s, S277{ x: 0 }); - move_to(&s, S278{ x: 0 }); - move_to(&s, S279{ x: 0 }); - move_to(&s, S280{ x: 0 }); - move_to(&s, S281{ x: 0 }); - move_to(&s, S282{ x: 0 }); - move_to(&s, S283{ x: 0 }); - move_to(&s, S284{ x: 0 }); - move_to(&s, S285{ x: 0 }); - move_to(&s, S286{ x: 0 }); - move_to(&s, S287{ x: 0 }); - move_to(&s, S288{ x: 0 }); - move_to(&s, S289{ x: 0 }); - move_to(&s, S290{ x: 0 }); - move_to(&s, S291{ x: 0 }); - move_to(&s, S292{ x: 0 }); - move_to(&s, S293{ x: 0 }); - move_to(&s, S294{ x: 0 }); - move_to(&s, S295{ x: 0 }); - move_to(&s, S296{ x: 0 }); - move_to(&s, S297{ x: 0 }); - move_to(&s, S298{ x: 0 }); - move_to(&s, S299{ x: 0 }); - move_to(&s, S300{ x: 0 }); - move_to(&s, S301{ x: 0 }); - move_to(&s, S302{ x: 0 }); - move_to(&s, S303{ x: 0 }); - move_to(&s, S304{ x: 0 }); - move_to(&s, S305{ x: 0 }); - move_to(&s, S306{ x: 0 }); - move_to(&s, S307{ x: 0 }); - move_to(&s, S308{ x: 0 }); - move_to(&s, S309{ x: 0 }); - move_to(&s, S310{ x: 0 }); - move_to(&s, S311{ x: 0 }); - move_to(&s, S312{ x: 0 }); - move_to(&s, S313{ x: 0 }); - move_to(&s, S314{ x: 0 }); - move_to(&s, S315{ x: 0 }); - move_to(&s, S316{ x: 0 }); - move_to(&s, S317{ x: 0 }); - move_to(&s, S318{ x: 0 }); - move_to(&s, S319{ x: 0 }); - move_to(&s, S320{ x: 0 }); - move_to(&s, S321{ x: 0 }); - move_to(&s, S322{ x: 0 }); - move_to(&s, S323{ x: 0 }); - move_to(&s, S324{ x: 0 }); - move_to(&s, S325{ x: 0 }); - move_to(&s, S326{ x: 0 }); - move_to(&s, S327{ x: 0 }); - move_to(&s, S328{ x: 0 }); - move_to(&s, S329{ x: 0 }); - move_to(&s, S330{ x: 0 }); - move_to(&s, S331{ x: 0 }); - move_to(&s, S332{ x: 0 }); - move_to(&s, S333{ x: 0 }); - move_to(&s, S334{ x: 0 }); - move_to(&s, S335{ x: 0 }); - move_to(&s, S336{ x: 0 }); - move_to(&s, S337{ x: 0 }); - move_to(&s, S338{ x: 0 }); - move_to(&s, S339{ x: 0 }); - move_to(&s, S340{ x: 0 }); - move_to(&s, S341{ x: 0 }); - move_to(&s, S342{ x: 0 }); - move_to(&s, S343{ x: 0 }); - move_to(&s, S344{ x: 0 }); - move_to(&s, S345{ x: 0 }); - move_to(&s, S346{ x: 0 }); - move_to(&s, S347{ x: 0 }); - move_to(&s, S348{ x: 0 }); - move_to(&s, S349{ x: 0 }); - move_to(&s, S350{ x: 0 }); - move_to(&s, S351{ x: 0 }); - move_to(&s, S352{ x: 0 }); - move_to(&s, S353{ x: 0 }); - move_to(&s, S354{ x: 0 }); - move_to(&s, S355{ x: 0 }); - move_to(&s, S356{ x: 0 }); - move_to(&s, S357{ x: 0 }); - move_to(&s, S358{ x: 0 }); - move_to(&s, S359{ x: 0 }); - move_to(&s, S360{ x: 0 }); - move_to(&s, S361{ x: 0 }); - move_to(&s, S362{ x: 0 }); - move_to(&s, S363{ x: 0 }); - move_to(&s, S364{ x: 0 }); - move_to(&s, S365{ x: 0 }); - move_to(&s, S366{ x: 0 }); - move_to(&s, S367{ x: 0 }); - move_to(&s, S368{ x: 0 }); - move_to(&s, S369{ x: 0 }); - move_to(&s, S370{ x: 0 }); - move_to(&s, S371{ x: 0 }); - move_to(&s, S372{ x: 0 }); - move_to(&s, S373{ x: 0 }); - move_to(&s, S374{ x: 0 }); - move_to(&s, S375{ x: 0 }); - move_to(&s, S376{ x: 0 }); - move_to(&s, S377{ x: 0 }); - move_to(&s, S378{ x: 0 }); - move_to(&s, S379{ x: 0 }); - move_to(&s, S380{ x: 0 }); - move_to(&s, S381{ x: 0 }); - move_to(&s, S382{ x: 0 }); - move_to(&s, S383{ x: 0 }); - move_to(&s, S384{ x: 0 }); - move_to(&s, S385{ x: 0 }); - move_to(&s, S386{ x: 0 }); - move_to(&s, S387{ x: 0 }); - move_to(&s, S388{ x: 0 }); - move_to(&s, S389{ x: 0 }); - move_to(&s, S390{ x: 0 }); - move_to(&s, S391{ x: 0 }); - move_to(&s, S392{ x: 0 }); - move_to(&s, S393{ x: 0 }); - move_to(&s, S394{ x: 0 }); - move_to(&s, S395{ x: 0 }); - move_to(&s, S396{ x: 0 }); - move_to(&s, S397{ x: 0 }); - move_to(&s, S398{ x: 0 }); - move_to(&s, S399{ x: 0 }); - move_to(&s, S400{ x: 0 }); - move_to(&s, S401{ x: 0 }); - move_to(&s, S402{ x: 0 }); - move_to(&s, S403{ x: 0 }); - move_to(&s, S404{ x: 0 }); - move_to(&s, S405{ x: 0 }); - move_to(&s, S406{ x: 0 }); - move_to(&s, S407{ x: 0 }); - move_to(&s, S408{ x: 0 }); - move_to(&s, S409{ x: 0 }); - move_to(&s, S410{ x: 0 }); - move_to(&s, S411{ x: 0 }); - move_to(&s, S412{ x: 0 }); - move_to(&s, S413{ x: 0 }); - move_to(&s, S414{ x: 0 }); - move_to(&s, S415{ x: 0 }); - move_to(&s, S416{ x: 0 }); - move_to(&s, S417{ x: 0 }); - move_to(&s, S418{ x: 0 }); - move_to(&s, S419{ x: 0 }); - move_to(&s, S420{ x: 0 }); - move_to(&s, S421{ x: 0 }); - move_to(&s, S422{ x: 0 }); - move_to(&s, S423{ x: 0 }); - move_to(&s, S424{ x: 0 }); - move_to(&s, S425{ x: 0 }); - move_to(&s, S426{ x: 0 }); - move_to(&s, S427{ x: 0 }); - move_to(&s, S428{ x: 0 }); - move_to(&s, S429{ x: 0 }); - move_to(&s, S430{ x: 0 }); - move_to(&s, S431{ x: 0 }); - move_to(&s, S432{ x: 0 }); - move_to(&s, S433{ x: 0 }); - move_to(&s, S434{ x: 0 }); - move_to(&s, S435{ x: 0 }); - move_to(&s, S436{ x: 0 }); - move_to(&s, S437{ x: 0 }); - move_to(&s, S438{ x: 0 }); - move_to(&s, S439{ x: 0 }); - move_to(&s, S440{ x: 0 }); - move_to(&s, S441{ x: 0 }); - move_to(&s, S442{ x: 0 }); - move_to(&s, S443{ x: 0 }); - move_to(&s, S444{ x: 0 }); - move_to(&s, S445{ x: 0 }); - move_to(&s, S446{ x: 0 }); - move_to(&s, S447{ x: 0 }); - move_to(&s, S448{ x: 0 }); - move_to(&s, S449{ x: 0 }); - move_to(&s, S450{ x: 0 }); - move_to(&s, S451{ x: 0 }); - move_to(&s, S452{ x: 0 }); - move_to(&s, S453{ x: 0 }); - move_to(&s, S454{ x: 0 }); - move_to(&s, S455{ x: 0 }); - move_to(&s, S456{ x: 0 }); - move_to(&s, S457{ x: 0 }); - move_to(&s, S458{ x: 0 }); - move_to(&s, S459{ x: 0 }); - move_to(&s, S460{ x: 0 }); - move_to(&s, S461{ x: 0 }); - move_to(&s, S462{ x: 0 }); - move_to(&s, S463{ x: 0 }); - move_to(&s, S464{ x: 0 }); - move_to(&s, S465{ x: 0 }); - move_to(&s, S466{ x: 0 }); - move_to(&s, S467{ x: 0 }); - move_to(&s, S468{ x: 0 }); - move_to(&s, S469{ x: 0 }); - move_to(&s, S470{ x: 0 }); - move_to(&s, S471{ x: 0 }); - move_to(&s, S472{ x: 0 }); - move_to(&s, S473{ x: 0 }); - move_to(&s, S474{ x: 0 }); - move_to(&s, S475{ x: 0 }); - move_to(&s, S476{ x: 0 }); - move_to(&s, S477{ x: 0 }); - move_to(&s, S478{ x: 0 }); - move_to(&s, S479{ x: 0 }); - move_to(&s, S480{ x: 0 }); - move_to(&s, S481{ x: 0 }); - move_to(&s, S482{ x: 0 }); - move_to(&s, S483{ x: 0 }); - move_to(&s, S484{ x: 0 }); - move_to(&s, S485{ x: 0 }); - move_to(&s, S486{ x: 0 }); - move_to(&s, S487{ x: 0 }); - move_to(&s, S488{ x: 0 }); - move_to(&s, S489{ x: 0 }); - move_to(&s, S490{ x: 0 }); - move_to(&s, S491{ x: 0 }); - move_to(&s, S492{ x: 0 }); - move_to(&s, S493{ x: 0 }); - move_to(&s, S494{ x: 0 }); - move_to(&s, S495{ x: 0 }); - move_to(&s, S496{ x: 0 }); - move_to(&s, S497{ x: 0 }); - move_to(&s, S498{ x: 0 }); - move_to(&s, S499{ x: 0 }); - move_to(&s, S500{ x: 0 }); - move_to(&s, S501{ x: 0 }); - move_to(&s, S502{ x: 0 }); - move_to(&s, S503{ x: 0 }); - move_to(&s, S504{ x: 0 }); - move_to(&s, S505{ x: 0 }); - move_to(&s, S506{ x: 0 }); - move_to(&s, S507{ x: 0 }); - move_to(&s, S508{ x: 0 }); - move_to(&s, S509{ x: 0 }); - move_to(&s, S510{ x: 0 }); - move_to(&s, S511{ x: 0 }); - move_to(&s, S512{ x: 0 }); - move_to(&s, S513{ x: 0 }); - move_to(&s, S514{ x: 0 }); - move_to(&s, S515{ x: 0 }); - move_to(&s, S516{ x: 0 }); - move_to(&s, S517{ x: 0 }); - move_to(&s, S518{ x: 0 }); - move_to(&s, S519{ x: 0 }); - move_to(&s, S520{ x: 0 }); - move_to(&s, S521{ x: 0 }); - move_to(&s, S522{ x: 0 }); - move_to(&s, S523{ x: 0 }); - move_to(&s, S524{ x: 0 }); - move_to(&s, S525{ x: 0 }); - move_to(&s, S526{ x: 0 }); - move_to(&s, S527{ x: 0 }); - move_to(&s, S528{ x: 0 }); - move_to(&s, S529{ x: 0 }); - move_to(&s, S530{ x: 0 }); - move_to(&s, S531{ x: 0 }); - move_to(&s, S532{ x: 0 }); - move_to(&s, S533{ x: 0 }); - move_to(&s, S534{ x: 0 }); - move_to(&s, S535{ x: 0 }); - move_to(&s, S536{ x: 0 }); - move_to(&s, S537{ x: 0 }); - move_to(&s, S538{ x: 0 }); - move_to(&s, S539{ x: 0 }); - move_to(&s, S540{ x: 0 }); - move_to(&s, S541{ x: 0 }); - move_to(&s, S542{ x: 0 }); - move_to(&s, S543{ x: 0 }); - move_to(&s, S544{ x: 0 }); - move_to(&s, S545{ x: 0 }); - move_to(&s, S546{ x: 0 }); - move_to(&s, S547{ x: 0 }); - move_to(&s, S548{ x: 0 }); - move_to(&s, S549{ x: 0 }); - move_to(&s, S550{ x: 0 }); - move_to(&s, S551{ x: 0 }); - move_to(&s, S552{ x: 0 }); - move_to(&s, S553{ x: 0 }); - move_to(&s, S554{ x: 0 }); - move_to(&s, S555{ x: 0 }); - move_to(&s, S556{ x: 0 }); - move_to(&s, S557{ x: 0 }); - move_to(&s, S558{ x: 0 }); - move_to(&s, S559{ x: 0 }); - move_to(&s, S560{ x: 0 }); - move_to(&s, S561{ x: 0 }); - move_to(&s, S562{ x: 0 }); - move_to(&s, S563{ x: 0 }); - move_to(&s, S564{ x: 0 }); - move_to(&s, S565{ x: 0 }); - move_to(&s, S566{ x: 0 }); - move_to(&s, S567{ x: 0 }); - move_to(&s, S568{ x: 0 }); - move_to(&s, S569{ x: 0 }); - move_to(&s, S570{ x: 0 }); - move_to(&s, S571{ x: 0 }); - move_to(&s, S572{ x: 0 }); - move_to(&s, S573{ x: 0 }); - move_to(&s, S574{ x: 0 }); - move_to(&s, S575{ x: 0 }); - move_to(&s, S576{ x: 0 }); - move_to(&s, S577{ x: 0 }); - move_to(&s, S578{ x: 0 }); - move_to(&s, S579{ x: 0 }); - move_to(&s, S580{ x: 0 }); - move_to(&s, S581{ x: 0 }); - move_to(&s, S582{ x: 0 }); - move_to(&s, S583{ x: 0 }); - move_to(&s, S584{ x: 0 }); - move_to(&s, S585{ x: 0 }); - move_to(&s, S586{ x: 0 }); - move_to(&s, S587{ x: 0 }); - move_to(&s, S588{ x: 0 }); - move_to(&s, S589{ x: 0 }); - move_to(&s, S590{ x: 0 }); - move_to(&s, S591{ x: 0 }); - move_to(&s, S592{ x: 0 }); - move_to(&s, S593{ x: 0 }); - move_to(&s, S594{ x: 0 }); - move_to(&s, S595{ x: 0 }); - move_to(&s, S596{ x: 0 }); - move_to(&s, S597{ x: 0 }); - move_to(&s, S598{ x: 0 }); - move_to(&s, S599{ x: 0 }); - move_to(&s, S600{ x: 0 }); - return; - } - - public entry calibrate_move_to_x1000(s: signer) { - label b0: - move_to(&s, S601{ x: 0 }); - move_to(&s, S602{ x: 0 }); - move_to(&s, S603{ x: 0 }); - move_to(&s, S604{ x: 0 }); - move_to(&s, S605{ x: 0 }); - move_to(&s, S606{ x: 0 }); - move_to(&s, S607{ x: 0 }); - move_to(&s, S608{ x: 0 }); - move_to(&s, S609{ x: 0 }); - move_to(&s, S610{ x: 0 }); - move_to(&s, S611{ x: 0 }); - move_to(&s, S612{ x: 0 }); - move_to(&s, S613{ x: 0 }); - move_to(&s, S614{ x: 0 }); - move_to(&s, S615{ x: 0 }); - move_to(&s, S616{ x: 0 }); - move_to(&s, S617{ x: 0 }); - move_to(&s, S618{ x: 0 }); - move_to(&s, S619{ x: 0 }); - move_to(&s, S620{ x: 0 }); - move_to(&s, S621{ x: 0 }); - move_to(&s, S622{ x: 0 }); - move_to(&s, S623{ x: 0 }); - move_to(&s, S624{ x: 0 }); - move_to(&s, S625{ x: 0 }); - move_to(&s, S626{ x: 0 }); - move_to(&s, S627{ x: 0 }); - move_to(&s, S628{ x: 0 }); - move_to(&s, S629{ x: 0 }); - move_to(&s, S630{ x: 0 }); - move_to(&s, S631{ x: 0 }); - move_to(&s, S632{ x: 0 }); - move_to(&s, S633{ x: 0 }); - move_to(&s, S634{ x: 0 }); - move_to(&s, S635{ x: 0 }); - move_to(&s, S636{ x: 0 }); - move_to(&s, S637{ x: 0 }); - move_to(&s, S638{ x: 0 }); - move_to(&s, S639{ x: 0 }); - move_to(&s, S640{ x: 0 }); - move_to(&s, S641{ x: 0 }); - move_to(&s, S642{ x: 0 }); - move_to(&s, S643{ x: 0 }); - move_to(&s, S644{ x: 0 }); - move_to(&s, S645{ x: 0 }); - move_to(&s, S646{ x: 0 }); - move_to(&s, S647{ x: 0 }); - move_to(&s, S648{ x: 0 }); - move_to(&s, S649{ x: 0 }); - move_to(&s, S650{ x: 0 }); - move_to(&s, S651{ x: 0 }); - move_to(&s, S652{ x: 0 }); - move_to(&s, S653{ x: 0 }); - move_to(&s, S654{ x: 0 }); - move_to(&s, S655{ x: 0 }); - move_to(&s, S656{ x: 0 }); - move_to(&s, S657{ x: 0 }); - move_to(&s, S658{ x: 0 }); - move_to(&s, S659{ x: 0 }); - move_to(&s, S660{ x: 0 }); - move_to(&s, S661{ x: 0 }); - move_to(&s, S662{ x: 0 }); - move_to(&s, S663{ x: 0 }); - move_to(&s, S664{ x: 0 }); - move_to(&s, S665{ x: 0 }); - move_to(&s, S666{ x: 0 }); - move_to(&s, S667{ x: 0 }); - move_to(&s, S668{ x: 0 }); - move_to(&s, S669{ x: 0 }); - move_to(&s, S670{ x: 0 }); - move_to(&s, S671{ x: 0 }); - move_to(&s, S672{ x: 0 }); - move_to(&s, S673{ x: 0 }); - move_to(&s, S674{ x: 0 }); - move_to(&s, S675{ x: 0 }); - move_to(&s, S676{ x: 0 }); - move_to(&s, S677{ x: 0 }); - move_to(&s, S678{ x: 0 }); - move_to(&s, S679{ x: 0 }); - move_to(&s, S680{ x: 0 }); - move_to(&s, S681{ x: 0 }); - move_to(&s, S682{ x: 0 }); - move_to(&s, S683{ x: 0 }); - move_to(&s, S684{ x: 0 }); - move_to(&s, S685{ x: 0 }); - move_to(&s, S686{ x: 0 }); - move_to(&s, S687{ x: 0 }); - move_to(&s, S688{ x: 0 }); - move_to(&s, S689{ x: 0 }); - move_to(&s, S690{ x: 0 }); - move_to(&s, S691{ x: 0 }); - move_to(&s, S692{ x: 0 }); - move_to(&s, S693{ x: 0 }); - move_to(&s, S694{ x: 0 }); - move_to(&s, S695{ x: 0 }); - move_to(&s, S696{ x: 0 }); - move_to(&s, S697{ x: 0 }); - move_to(&s, S698{ x: 0 }); - move_to(&s, S699{ x: 0 }); - move_to(&s, S700{ x: 0 }); - move_to(&s, S701{ x: 0 }); - move_to(&s, S702{ x: 0 }); - move_to(&s, S703{ x: 0 }); - move_to(&s, S704{ x: 0 }); - move_to(&s, S705{ x: 0 }); - move_to(&s, S706{ x: 0 }); - move_to(&s, S707{ x: 0 }); - move_to(&s, S708{ x: 0 }); - move_to(&s, S709{ x: 0 }); - move_to(&s, S710{ x: 0 }); - move_to(&s, S711{ x: 0 }); - move_to(&s, S712{ x: 0 }); - move_to(&s, S713{ x: 0 }); - move_to(&s, S714{ x: 0 }); - move_to(&s, S715{ x: 0 }); - move_to(&s, S716{ x: 0 }); - move_to(&s, S717{ x: 0 }); - move_to(&s, S718{ x: 0 }); - move_to(&s, S719{ x: 0 }); - move_to(&s, S720{ x: 0 }); - move_to(&s, S721{ x: 0 }); - move_to(&s, S722{ x: 0 }); - move_to(&s, S723{ x: 0 }); - move_to(&s, S724{ x: 0 }); - move_to(&s, S725{ x: 0 }); - move_to(&s, S726{ x: 0 }); - move_to(&s, S727{ x: 0 }); - move_to(&s, S728{ x: 0 }); - move_to(&s, S729{ x: 0 }); - move_to(&s, S730{ x: 0 }); - move_to(&s, S731{ x: 0 }); - move_to(&s, S732{ x: 0 }); - move_to(&s, S733{ x: 0 }); - move_to(&s, S734{ x: 0 }); - move_to(&s, S735{ x: 0 }); - move_to(&s, S736{ x: 0 }); - move_to(&s, S737{ x: 0 }); - move_to(&s, S738{ x: 0 }); - move_to(&s, S739{ x: 0 }); - move_to(&s, S740{ x: 0 }); - move_to(&s, S741{ x: 0 }); - move_to(&s, S742{ x: 0 }); - move_to(&s, S743{ x: 0 }); - move_to(&s, S744{ x: 0 }); - move_to(&s, S745{ x: 0 }); - move_to(&s, S746{ x: 0 }); - move_to(&s, S747{ x: 0 }); - move_to(&s, S748{ x: 0 }); - move_to(&s, S749{ x: 0 }); - move_to(&s, S750{ x: 0 }); - move_to(&s, S751{ x: 0 }); - move_to(&s, S752{ x: 0 }); - move_to(&s, S753{ x: 0 }); - move_to(&s, S754{ x: 0 }); - move_to(&s, S755{ x: 0 }); - move_to(&s, S756{ x: 0 }); - move_to(&s, S757{ x: 0 }); - move_to(&s, S758{ x: 0 }); - move_to(&s, S759{ x: 0 }); - move_to(&s, S760{ x: 0 }); - move_to(&s, S761{ x: 0 }); - move_to(&s, S762{ x: 0 }); - move_to(&s, S763{ x: 0 }); - move_to(&s, S764{ x: 0 }); - move_to(&s, S765{ x: 0 }); - move_to(&s, S766{ x: 0 }); - move_to(&s, S767{ x: 0 }); - move_to(&s, S768{ x: 0 }); - move_to(&s, S769{ x: 0 }); - move_to(&s, S770{ x: 0 }); - move_to(&s, S771{ x: 0 }); - move_to(&s, S772{ x: 0 }); - move_to(&s, S773{ x: 0 }); - move_to(&s, S774{ x: 0 }); - move_to(&s, S775{ x: 0 }); - move_to(&s, S776{ x: 0 }); - move_to(&s, S777{ x: 0 }); - move_to(&s, S778{ x: 0 }); - move_to(&s, S779{ x: 0 }); - move_to(&s, S780{ x: 0 }); - move_to(&s, S781{ x: 0 }); - move_to(&s, S782{ x: 0 }); - move_to(&s, S783{ x: 0 }); - move_to(&s, S784{ x: 0 }); - move_to(&s, S785{ x: 0 }); - move_to(&s, S786{ x: 0 }); - move_to(&s, S787{ x: 0 }); - move_to(&s, S788{ x: 0 }); - move_to(&s, S789{ x: 0 }); - move_to(&s, S790{ x: 0 }); - move_to(&s, S791{ x: 0 }); - move_to(&s, S792{ x: 0 }); - move_to(&s, S793{ x: 0 }); - move_to(&s, S794{ x: 0 }); - move_to(&s, S795{ x: 0 }); - move_to(&s, S796{ x: 0 }); - move_to(&s, S797{ x: 0 }); - move_to(&s, S798{ x: 0 }); - move_to(&s, S799{ x: 0 }); - move_to(&s, S800{ x: 0 }); - move_to(&s, S801{ x: 0 }); - move_to(&s, S802{ x: 0 }); - move_to(&s, S803{ x: 0 }); - move_to(&s, S804{ x: 0 }); - move_to(&s, S805{ x: 0 }); - move_to(&s, S806{ x: 0 }); - move_to(&s, S807{ x: 0 }); - move_to(&s, S808{ x: 0 }); - move_to(&s, S809{ x: 0 }); - move_to(&s, S810{ x: 0 }); - move_to(&s, S811{ x: 0 }); - move_to(&s, S812{ x: 0 }); - move_to(&s, S813{ x: 0 }); - move_to(&s, S814{ x: 0 }); - move_to(&s, S815{ x: 0 }); - move_to(&s, S816{ x: 0 }); - move_to(&s, S817{ x: 0 }); - move_to(&s, S818{ x: 0 }); - move_to(&s, S819{ x: 0 }); - move_to(&s, S820{ x: 0 }); - move_to(&s, S821{ x: 0 }); - move_to(&s, S822{ x: 0 }); - move_to(&s, S823{ x: 0 }); - move_to(&s, S824{ x: 0 }); - move_to(&s, S825{ x: 0 }); - move_to(&s, S826{ x: 0 }); - move_to(&s, S827{ x: 0 }); - move_to(&s, S828{ x: 0 }); - move_to(&s, S829{ x: 0 }); - move_to(&s, S830{ x: 0 }); - move_to(&s, S831{ x: 0 }); - move_to(&s, S832{ x: 0 }); - move_to(&s, S833{ x: 0 }); - move_to(&s, S834{ x: 0 }); - move_to(&s, S835{ x: 0 }); - move_to(&s, S836{ x: 0 }); - move_to(&s, S837{ x: 0 }); - move_to(&s, S838{ x: 0 }); - move_to(&s, S839{ x: 0 }); - move_to(&s, S840{ x: 0 }); - move_to(&s, S841{ x: 0 }); - move_to(&s, S842{ x: 0 }); - move_to(&s, S843{ x: 0 }); - move_to(&s, S844{ x: 0 }); - move_to(&s, S845{ x: 0 }); - move_to(&s, S846{ x: 0 }); - move_to(&s, S847{ x: 0 }); - move_to(&s, S848{ x: 0 }); - move_to(&s, S849{ x: 0 }); - move_to(&s, S850{ x: 0 }); - move_to(&s, S851{ x: 0 }); - move_to(&s, S852{ x: 0 }); - move_to(&s, S853{ x: 0 }); - move_to(&s, S854{ x: 0 }); - move_to(&s, S855{ x: 0 }); - move_to(&s, S856{ x: 0 }); - move_to(&s, S857{ x: 0 }); - move_to(&s, S858{ x: 0 }); - move_to(&s, S859{ x: 0 }); - move_to(&s, S860{ x: 0 }); - move_to(&s, S861{ x: 0 }); - move_to(&s, S862{ x: 0 }); - move_to(&s, S863{ x: 0 }); - move_to(&s, S864{ x: 0 }); - move_to(&s, S865{ x: 0 }); - move_to(&s, S866{ x: 0 }); - move_to(&s, S867{ x: 0 }); - move_to(&s, S868{ x: 0 }); - move_to(&s, S869{ x: 0 }); - move_to(&s, S870{ x: 0 }); - move_to(&s, S871{ x: 0 }); - move_to(&s, S872{ x: 0 }); - move_to(&s, S873{ x: 0 }); - move_to(&s, S874{ x: 0 }); - move_to(&s, S875{ x: 0 }); - move_to(&s, S876{ x: 0 }); - move_to(&s, S877{ x: 0 }); - move_to(&s, S878{ x: 0 }); - move_to(&s, S879{ x: 0 }); - move_to(&s, S880{ x: 0 }); - move_to(&s, S881{ x: 0 }); - move_to(&s, S882{ x: 0 }); - move_to(&s, S883{ x: 0 }); - move_to(&s, S884{ x: 0 }); - move_to(&s, S885{ x: 0 }); - move_to(&s, S886{ x: 0 }); - move_to(&s, S887{ x: 0 }); - move_to(&s, S888{ x: 0 }); - move_to(&s, S889{ x: 0 }); - move_to(&s, S890{ x: 0 }); - move_to(&s, S891{ x: 0 }); - move_to(&s, S892{ x: 0 }); - move_to(&s, S893{ x: 0 }); - move_to(&s, S894{ x: 0 }); - move_to(&s, S895{ x: 0 }); - move_to(&s, S896{ x: 0 }); - move_to(&s, S897{ x: 0 }); - move_to(&s, S898{ x: 0 }); - move_to(&s, S899{ x: 0 }); - move_to(&s, S900{ x: 0 }); - move_to(&s, S901{ x: 0 }); - move_to(&s, S902{ x: 0 }); - move_to(&s, S903{ x: 0 }); - move_to(&s, S904{ x: 0 }); - move_to(&s, S905{ x: 0 }); - move_to(&s, S906{ x: 0 }); - move_to(&s, S907{ x: 0 }); - move_to(&s, S908{ x: 0 }); - move_to(&s, S909{ x: 0 }); - move_to(&s, S910{ x: 0 }); - move_to(&s, S911{ x: 0 }); - move_to(&s, S912{ x: 0 }); - move_to(&s, S913{ x: 0 }); - move_to(&s, S914{ x: 0 }); - move_to(&s, S915{ x: 0 }); - move_to(&s, S916{ x: 0 }); - move_to(&s, S917{ x: 0 }); - move_to(&s, S918{ x: 0 }); - move_to(&s, S919{ x: 0 }); - move_to(&s, S920{ x: 0 }); - move_to(&s, S921{ x: 0 }); - move_to(&s, S922{ x: 0 }); - move_to(&s, S923{ x: 0 }); - move_to(&s, S924{ x: 0 }); - move_to(&s, S925{ x: 0 }); - move_to(&s, S926{ x: 0 }); - move_to(&s, S927{ x: 0 }); - move_to(&s, S928{ x: 0 }); - move_to(&s, S929{ x: 0 }); - move_to(&s, S930{ x: 0 }); - move_to(&s, S931{ x: 0 }); - move_to(&s, S932{ x: 0 }); - move_to(&s, S933{ x: 0 }); - move_to(&s, S934{ x: 0 }); - move_to(&s, S935{ x: 0 }); - move_to(&s, S936{ x: 0 }); - move_to(&s, S937{ x: 0 }); - move_to(&s, S938{ x: 0 }); - move_to(&s, S939{ x: 0 }); - move_to(&s, S940{ x: 0 }); - move_to(&s, S941{ x: 0 }); - move_to(&s, S942{ x: 0 }); - move_to(&s, S943{ x: 0 }); - move_to(&s, S944{ x: 0 }); - move_to(&s, S945{ x: 0 }); - move_to(&s, S946{ x: 0 }); - move_to(&s, S947{ x: 0 }); - move_to(&s, S948{ x: 0 }); - move_to(&s, S949{ x: 0 }); - move_to(&s, S950{ x: 0 }); - move_to(&s, S951{ x: 0 }); - move_to(&s, S952{ x: 0 }); - move_to(&s, S953{ x: 0 }); - move_to(&s, S954{ x: 0 }); - move_to(&s, S955{ x: 0 }); - move_to(&s, S956{ x: 0 }); - move_to(&s, S957{ x: 0 }); - move_to(&s, S958{ x: 0 }); - move_to(&s, S959{ x: 0 }); - move_to(&s, S960{ x: 0 }); - move_to(&s, S961{ x: 0 }); - move_to(&s, S962{ x: 0 }); - move_to(&s, S963{ x: 0 }); - move_to(&s, S964{ x: 0 }); - move_to(&s, S965{ x: 0 }); - move_to(&s, S966{ x: 0 }); - move_to(&s, S967{ x: 0 }); - move_to(&s, S968{ x: 0 }); - move_to(&s, S969{ x: 0 }); - move_to(&s, S970{ x: 0 }); - move_to(&s, S971{ x: 0 }); - move_to(&s, S972{ x: 0 }); - move_to(&s, S973{ x: 0 }); - move_to(&s, S974{ x: 0 }); - move_to(&s, S975{ x: 0 }); - move_to(&s, S976{ x: 0 }); - move_to(&s, S977{ x: 0 }); - move_to(&s, S978{ x: 0 }); - move_to(&s, S979{ x: 0 }); - move_to(&s, S980{ x: 0 }); - move_to(&s, S981{ x: 0 }); - move_to(&s, S982{ x: 0 }); - move_to(&s, S983{ x: 0 }); - move_to(&s, S984{ x: 0 }); - move_to(&s, S985{ x: 0 }); - move_to(&s, S986{ x: 0 }); - move_to(&s, S987{ x: 0 }); - move_to(&s, S988{ x: 0 }); - move_to(&s, S989{ x: 0 }); - move_to(&s, S990{ x: 0 }); - move_to(&s, S991{ x: 0 }); - move_to(&s, S992{ x: 0 }); - move_to(&s, S993{ x: 0 }); - move_to(&s, S994{ x: 0 }); - move_to(&s, S995{ x: 0 }); - move_to(&s, S996{ x: 0 }); - move_to(&s, S997{ x: 0 }); - move_to(&s, S998{ x: 0 }); - move_to(&s, S999{ x: 0 }); - move_to(&s, S1000{ x: 0 }); - move_to(&s, S1001{ x: 0 }); - move_to(&s, S1002{ x: 0 }); - move_to(&s, S1003{ x: 0 }); - move_to(&s, S1004{ x: 0 }); - move_to(&s, S1005{ x: 0 }); - move_to(&s, S1006{ x: 0 }); - move_to(&s, S1007{ x: 0 }); - move_to(&s, S1008{ x: 0 }); - move_to(&s, S1009{ x: 0 }); - move_to(&s, S1010{ x: 0 }); - move_to(&s, S1011{ x: 0 }); - move_to(&s, S1012{ x: 0 }); - move_to(&s, S1013{ x: 0 }); - move_to(&s, S1014{ x: 0 }); - move_to(&s, S1015{ x: 0 }); - move_to(&s, S1016{ x: 0 }); - move_to(&s, S1017{ x: 0 }); - move_to(&s, S1018{ x: 0 }); - move_to(&s, S1019{ x: 0 }); - move_to(&s, S1020{ x: 0 }); - move_to(&s, S1021{ x: 0 }); - move_to(&s, S1022{ x: 0 }); - move_to(&s, S1023{ x: 0 }); - move_to(&s, S1024{ x: 0 }); - move_to(&s, S1025{ x: 0 }); - move_to(&s, S1026{ x: 0 }); - move_to(&s, S1027{ x: 0 }); - move_to(&s, S1028{ x: 0 }); - move_to(&s, S1029{ x: 0 }); - move_to(&s, S1030{ x: 0 }); - move_to(&s, S1031{ x: 0 }); - move_to(&s, S1032{ x: 0 }); - move_to(&s, S1033{ x: 0 }); - move_to(&s, S1034{ x: 0 }); - move_to(&s, S1035{ x: 0 }); - move_to(&s, S1036{ x: 0 }); - move_to(&s, S1037{ x: 0 }); - move_to(&s, S1038{ x: 0 }); - move_to(&s, S1039{ x: 0 }); - move_to(&s, S1040{ x: 0 }); - move_to(&s, S1041{ x: 0 }); - move_to(&s, S1042{ x: 0 }); - move_to(&s, S1043{ x: 0 }); - move_to(&s, S1044{ x: 0 }); - move_to(&s, S1045{ x: 0 }); - move_to(&s, S1046{ x: 0 }); - move_to(&s, S1047{ x: 0 }); - move_to(&s, S1048{ x: 0 }); - move_to(&s, S1049{ x: 0 }); - move_to(&s, S1050{ x: 0 }); - move_to(&s, S1051{ x: 0 }); - move_to(&s, S1052{ x: 0 }); - move_to(&s, S1053{ x: 0 }); - move_to(&s, S1054{ x: 0 }); - move_to(&s, S1055{ x: 0 }); - move_to(&s, S1056{ x: 0 }); - move_to(&s, S1057{ x: 0 }); - move_to(&s, S1058{ x: 0 }); - move_to(&s, S1059{ x: 0 }); - move_to(&s, S1060{ x: 0 }); - move_to(&s, S1061{ x: 0 }); - move_to(&s, S1062{ x: 0 }); - move_to(&s, S1063{ x: 0 }); - move_to(&s, S1064{ x: 0 }); - move_to(&s, S1065{ x: 0 }); - move_to(&s, S1066{ x: 0 }); - move_to(&s, S1067{ x: 0 }); - move_to(&s, S1068{ x: 0 }); - move_to(&s, S1069{ x: 0 }); - move_to(&s, S1070{ x: 0 }); - move_to(&s, S1071{ x: 0 }); - move_to(&s, S1072{ x: 0 }); - move_to(&s, S1073{ x: 0 }); - move_to(&s, S1074{ x: 0 }); - move_to(&s, S1075{ x: 0 }); - move_to(&s, S1076{ x: 0 }); - move_to(&s, S1077{ x: 0 }); - move_to(&s, S1078{ x: 0 }); - move_to(&s, S1079{ x: 0 }); - move_to(&s, S1080{ x: 0 }); - move_to(&s, S1081{ x: 0 }); - move_to(&s, S1082{ x: 0 }); - move_to(&s, S1083{ x: 0 }); - move_to(&s, S1084{ x: 0 }); - move_to(&s, S1085{ x: 0 }); - move_to(&s, S1086{ x: 0 }); - move_to(&s, S1087{ x: 0 }); - move_to(&s, S1088{ x: 0 }); - move_to(&s, S1089{ x: 0 }); - move_to(&s, S1090{ x: 0 }); - move_to(&s, S1091{ x: 0 }); - move_to(&s, S1092{ x: 0 }); - move_to(&s, S1093{ x: 0 }); - move_to(&s, S1094{ x: 0 }); - move_to(&s, S1095{ x: 0 }); - move_to(&s, S1096{ x: 0 }); - move_to(&s, S1097{ x: 0 }); - move_to(&s, S1098{ x: 0 }); - move_to(&s, S1099{ x: 0 }); - move_to(&s, S1100{ x: 0 }); - move_to(&s, S1101{ x: 0 }); - move_to(&s, S1102{ x: 0 }); - move_to(&s, S1103{ x: 0 }); - move_to(&s, S1104{ x: 0 }); - move_to(&s, S1105{ x: 0 }); - move_to(&s, S1106{ x: 0 }); - move_to(&s, S1107{ x: 0 }); - move_to(&s, S1108{ x: 0 }); - move_to(&s, S1109{ x: 0 }); - move_to(&s, S1110{ x: 0 }); - move_to(&s, S1111{ x: 0 }); - move_to(&s, S1112{ x: 0 }); - move_to(&s, S1113{ x: 0 }); - move_to(&s, S1114{ x: 0 }); - move_to(&s, S1115{ x: 0 }); - move_to(&s, S1116{ x: 0 }); - move_to(&s, S1117{ x: 0 }); - move_to(&s, S1118{ x: 0 }); - move_to(&s, S1119{ x: 0 }); - move_to(&s, S1120{ x: 0 }); - move_to(&s, S1121{ x: 0 }); - move_to(&s, S1122{ x: 0 }); - move_to(&s, S1123{ x: 0 }); - move_to(&s, S1124{ x: 0 }); - move_to(&s, S1125{ x: 0 }); - move_to(&s, S1126{ x: 0 }); - move_to(&s, S1127{ x: 0 }); - move_to(&s, S1128{ x: 0 }); - move_to(&s, S1129{ x: 0 }); - move_to(&s, S1130{ x: 0 }); - move_to(&s, S1131{ x: 0 }); - move_to(&s, S1132{ x: 0 }); - move_to(&s, S1133{ x: 0 }); - move_to(&s, S1134{ x: 0 }); - move_to(&s, S1135{ x: 0 }); - move_to(&s, S1136{ x: 0 }); - move_to(&s, S1137{ x: 0 }); - move_to(&s, S1138{ x: 0 }); - move_to(&s, S1139{ x: 0 }); - move_to(&s, S1140{ x: 0 }); - move_to(&s, S1141{ x: 0 }); - move_to(&s, S1142{ x: 0 }); - move_to(&s, S1143{ x: 0 }); - move_to(&s, S1144{ x: 0 }); - move_to(&s, S1145{ x: 0 }); - move_to(&s, S1146{ x: 0 }); - move_to(&s, S1147{ x: 0 }); - move_to(&s, S1148{ x: 0 }); - move_to(&s, S1149{ x: 0 }); - move_to(&s, S1150{ x: 0 }); - move_to(&s, S1151{ x: 0 }); - move_to(&s, S1152{ x: 0 }); - move_to(&s, S1153{ x: 0 }); - move_to(&s, S1154{ x: 0 }); - move_to(&s, S1155{ x: 0 }); - move_to(&s, S1156{ x: 0 }); - move_to(&s, S1157{ x: 0 }); - move_to(&s, S1158{ x: 0 }); - move_to(&s, S1159{ x: 0 }); - move_to(&s, S1160{ x: 0 }); - move_to(&s, S1161{ x: 0 }); - move_to(&s, S1162{ x: 0 }); - move_to(&s, S1163{ x: 0 }); - move_to(&s, S1164{ x: 0 }); - move_to(&s, S1165{ x: 0 }); - move_to(&s, S1166{ x: 0 }); - move_to(&s, S1167{ x: 0 }); - move_to(&s, S1168{ x: 0 }); - move_to(&s, S1169{ x: 0 }); - move_to(&s, S1170{ x: 0 }); - move_to(&s, S1171{ x: 0 }); - move_to(&s, S1172{ x: 0 }); - move_to(&s, S1173{ x: 0 }); - move_to(&s, S1174{ x: 0 }); - move_to(&s, S1175{ x: 0 }); - move_to(&s, S1176{ x: 0 }); - move_to(&s, S1177{ x: 0 }); - move_to(&s, S1178{ x: 0 }); - move_to(&s, S1179{ x: 0 }); - move_to(&s, S1180{ x: 0 }); - move_to(&s, S1181{ x: 0 }); - move_to(&s, S1182{ x: 0 }); - move_to(&s, S1183{ x: 0 }); - move_to(&s, S1184{ x: 0 }); - move_to(&s, S1185{ x: 0 }); - move_to(&s, S1186{ x: 0 }); - move_to(&s, S1187{ x: 0 }); - move_to(&s, S1188{ x: 0 }); - move_to(&s, S1189{ x: 0 }); - move_to(&s, S1190{ x: 0 }); - move_to(&s, S1191{ x: 0 }); - move_to(&s, S1192{ x: 0 }); - move_to(&s, S1193{ x: 0 }); - move_to(&s, S1194{ x: 0 }); - move_to(&s, S1195{ x: 0 }); - move_to(&s, S1196{ x: 0 }); - move_to(&s, S1197{ x: 0 }); - move_to(&s, S1198{ x: 0 }); - move_to(&s, S1199{ x: 0 }); - move_to(&s, S1200{ x: 0 }); - move_to(&s, S1201{ x: 0 }); - move_to(&s, S1202{ x: 0 }); - move_to(&s, S1203{ x: 0 }); - move_to(&s, S1204{ x: 0 }); - move_to(&s, S1205{ x: 0 }); - move_to(&s, S1206{ x: 0 }); - move_to(&s, S1207{ x: 0 }); - move_to(&s, S1208{ x: 0 }); - move_to(&s, S1209{ x: 0 }); - move_to(&s, S1210{ x: 0 }); - move_to(&s, S1211{ x: 0 }); - move_to(&s, S1212{ x: 0 }); - move_to(&s, S1213{ x: 0 }); - move_to(&s, S1214{ x: 0 }); - move_to(&s, S1215{ x: 0 }); - move_to(&s, S1216{ x: 0 }); - move_to(&s, S1217{ x: 0 }); - move_to(&s, S1218{ x: 0 }); - move_to(&s, S1219{ x: 0 }); - move_to(&s, S1220{ x: 0 }); - move_to(&s, S1221{ x: 0 }); - move_to(&s, S1222{ x: 0 }); - move_to(&s, S1223{ x: 0 }); - move_to(&s, S1224{ x: 0 }); - move_to(&s, S1225{ x: 0 }); - move_to(&s, S1226{ x: 0 }); - move_to(&s, S1227{ x: 0 }); - move_to(&s, S1228{ x: 0 }); - move_to(&s, S1229{ x: 0 }); - move_to(&s, S1230{ x: 0 }); - move_to(&s, S1231{ x: 0 }); - move_to(&s, S1232{ x: 0 }); - move_to(&s, S1233{ x: 0 }); - move_to(&s, S1234{ x: 0 }); - move_to(&s, S1235{ x: 0 }); - move_to(&s, S1236{ x: 0 }); - move_to(&s, S1237{ x: 0 }); - move_to(&s, S1238{ x: 0 }); - move_to(&s, S1239{ x: 0 }); - move_to(&s, S1240{ x: 0 }); - move_to(&s, S1241{ x: 0 }); - move_to(&s, S1242{ x: 0 }); - move_to(&s, S1243{ x: 0 }); - move_to(&s, S1244{ x: 0 }); - move_to(&s, S1245{ x: 0 }); - move_to(&s, S1246{ x: 0 }); - move_to(&s, S1247{ x: 0 }); - move_to(&s, S1248{ x: 0 }); - move_to(&s, S1249{ x: 0 }); - move_to(&s, S1250{ x: 0 }); - move_to(&s, S1251{ x: 0 }); - move_to(&s, S1252{ x: 0 }); - move_to(&s, S1253{ x: 0 }); - move_to(&s, S1254{ x: 0 }); - move_to(&s, S1255{ x: 0 }); - move_to(&s, S1256{ x: 0 }); - move_to(&s, S1257{ x: 0 }); - move_to(&s, S1258{ x: 0 }); - move_to(&s, S1259{ x: 0 }); - move_to(&s, S1260{ x: 0 }); - move_to(&s, S1261{ x: 0 }); - move_to(&s, S1262{ x: 0 }); - move_to(&s, S1263{ x: 0 }); - move_to(&s, S1264{ x: 0 }); - move_to(&s, S1265{ x: 0 }); - move_to(&s, S1266{ x: 0 }); - move_to(&s, S1267{ x: 0 }); - move_to(&s, S1268{ x: 0 }); - move_to(&s, S1269{ x: 0 }); - move_to(&s, S1270{ x: 0 }); - move_to(&s, S1271{ x: 0 }); - move_to(&s, S1272{ x: 0 }); - move_to(&s, S1273{ x: 0 }); - move_to(&s, S1274{ x: 0 }); - move_to(&s, S1275{ x: 0 }); - move_to(&s, S1276{ x: 0 }); - move_to(&s, S1277{ x: 0 }); - move_to(&s, S1278{ x: 0 }); - move_to(&s, S1279{ x: 0 }); - move_to(&s, S1280{ x: 0 }); - move_to(&s, S1281{ x: 0 }); - move_to(&s, S1282{ x: 0 }); - move_to(&s, S1283{ x: 0 }); - move_to(&s, S1284{ x: 0 }); - move_to(&s, S1285{ x: 0 }); - move_to(&s, S1286{ x: 0 }); - move_to(&s, S1287{ x: 0 }); - move_to(&s, S1288{ x: 0 }); - move_to(&s, S1289{ x: 0 }); - move_to(&s, S1290{ x: 0 }); - move_to(&s, S1291{ x: 0 }); - move_to(&s, S1292{ x: 0 }); - move_to(&s, S1293{ x: 0 }); - move_to(&s, S1294{ x: 0 }); - move_to(&s, S1295{ x: 0 }); - move_to(&s, S1296{ x: 0 }); - move_to(&s, S1297{ x: 0 }); - move_to(&s, S1298{ x: 0 }); - move_to(&s, S1299{ x: 0 }); - move_to(&s, S1300{ x: 0 }); - move_to(&s, S1301{ x: 0 }); - move_to(&s, S1302{ x: 0 }); - move_to(&s, S1303{ x: 0 }); - move_to(&s, S1304{ x: 0 }); - move_to(&s, S1305{ x: 0 }); - move_to(&s, S1306{ x: 0 }); - move_to(&s, S1307{ x: 0 }); - move_to(&s, S1308{ x: 0 }); - move_to(&s, S1309{ x: 0 }); - move_to(&s, S1310{ x: 0 }); - move_to(&s, S1311{ x: 0 }); - move_to(&s, S1312{ x: 0 }); - move_to(&s, S1313{ x: 0 }); - move_to(&s, S1314{ x: 0 }); - move_to(&s, S1315{ x: 0 }); - move_to(&s, S1316{ x: 0 }); - move_to(&s, S1317{ x: 0 }); - move_to(&s, S1318{ x: 0 }); - move_to(&s, S1319{ x: 0 }); - move_to(&s, S1320{ x: 0 }); - move_to(&s, S1321{ x: 0 }); - move_to(&s, S1322{ x: 0 }); - move_to(&s, S1323{ x: 0 }); - move_to(&s, S1324{ x: 0 }); - move_to(&s, S1325{ x: 0 }); - move_to(&s, S1326{ x: 0 }); - move_to(&s, S1327{ x: 0 }); - move_to(&s, S1328{ x: 0 }); - move_to(&s, S1329{ x: 0 }); - move_to(&s, S1330{ x: 0 }); - move_to(&s, S1331{ x: 0 }); - move_to(&s, S1332{ x: 0 }); - move_to(&s, S1333{ x: 0 }); - move_to(&s, S1334{ x: 0 }); - move_to(&s, S1335{ x: 0 }); - move_to(&s, S1336{ x: 0 }); - move_to(&s, S1337{ x: 0 }); - move_to(&s, S1338{ x: 0 }); - move_to(&s, S1339{ x: 0 }); - move_to(&s, S1340{ x: 0 }); - move_to(&s, S1341{ x: 0 }); - move_to(&s, S1342{ x: 0 }); - move_to(&s, S1343{ x: 0 }); - move_to(&s, S1344{ x: 0 }); - move_to(&s, S1345{ x: 0 }); - move_to(&s, S1346{ x: 0 }); - move_to(&s, S1347{ x: 0 }); - move_to(&s, S1348{ x: 0 }); - move_to(&s, S1349{ x: 0 }); - move_to(&s, S1350{ x: 0 }); - move_to(&s, S1351{ x: 0 }); - move_to(&s, S1352{ x: 0 }); - move_to(&s, S1353{ x: 0 }); - move_to(&s, S1354{ x: 0 }); - move_to(&s, S1355{ x: 0 }); - move_to(&s, S1356{ x: 0 }); - move_to(&s, S1357{ x: 0 }); - move_to(&s, S1358{ x: 0 }); - move_to(&s, S1359{ x: 0 }); - move_to(&s, S1360{ x: 0 }); - move_to(&s, S1361{ x: 0 }); - move_to(&s, S1362{ x: 0 }); - move_to(&s, S1363{ x: 0 }); - move_to(&s, S1364{ x: 0 }); - move_to(&s, S1365{ x: 0 }); - move_to(&s, S1366{ x: 0 }); - move_to(&s, S1367{ x: 0 }); - move_to(&s, S1368{ x: 0 }); - move_to(&s, S1369{ x: 0 }); - move_to(&s, S1370{ x: 0 }); - move_to(&s, S1371{ x: 0 }); - move_to(&s, S1372{ x: 0 }); - move_to(&s, S1373{ x: 0 }); - move_to(&s, S1374{ x: 0 }); - move_to(&s, S1375{ x: 0 }); - move_to(&s, S1376{ x: 0 }); - move_to(&s, S1377{ x: 0 }); - move_to(&s, S1378{ x: 0 }); - move_to(&s, S1379{ x: 0 }); - move_to(&s, S1380{ x: 0 }); - move_to(&s, S1381{ x: 0 }); - move_to(&s, S1382{ x: 0 }); - move_to(&s, S1383{ x: 0 }); - move_to(&s, S1384{ x: 0 }); - move_to(&s, S1385{ x: 0 }); - move_to(&s, S1386{ x: 0 }); - move_to(&s, S1387{ x: 0 }); - move_to(&s, S1388{ x: 0 }); - move_to(&s, S1389{ x: 0 }); - move_to(&s, S1390{ x: 0 }); - move_to(&s, S1391{ x: 0 }); - move_to(&s, S1392{ x: 0 }); - move_to(&s, S1393{ x: 0 }); - move_to(&s, S1394{ x: 0 }); - move_to(&s, S1395{ x: 0 }); - move_to(&s, S1396{ x: 0 }); - move_to(&s, S1397{ x: 0 }); - move_to(&s, S1398{ x: 0 }); - move_to(&s, S1399{ x: 0 }); - move_to(&s, S1400{ x: 0 }); - move_to(&s, S1401{ x: 0 }); - move_to(&s, S1402{ x: 0 }); - move_to(&s, S1403{ x: 0 }); - move_to(&s, S1404{ x: 0 }); - move_to(&s, S1405{ x: 0 }); - move_to(&s, S1406{ x: 0 }); - move_to(&s, S1407{ x: 0 }); - move_to(&s, S1408{ x: 0 }); - move_to(&s, S1409{ x: 0 }); - move_to(&s, S1410{ x: 0 }); - move_to(&s, S1411{ x: 0 }); - move_to(&s, S1412{ x: 0 }); - move_to(&s, S1413{ x: 0 }); - move_to(&s, S1414{ x: 0 }); - move_to(&s, S1415{ x: 0 }); - move_to(&s, S1416{ x: 0 }); - move_to(&s, S1417{ x: 0 }); - move_to(&s, S1418{ x: 0 }); - move_to(&s, S1419{ x: 0 }); - move_to(&s, S1420{ x: 0 }); - move_to(&s, S1421{ x: 0 }); - move_to(&s, S1422{ x: 0 }); - move_to(&s, S1423{ x: 0 }); - move_to(&s, S1424{ x: 0 }); - move_to(&s, S1425{ x: 0 }); - move_to(&s, S1426{ x: 0 }); - move_to(&s, S1427{ x: 0 }); - move_to(&s, S1428{ x: 0 }); - move_to(&s, S1429{ x: 0 }); - move_to(&s, S1430{ x: 0 }); - move_to(&s, S1431{ x: 0 }); - move_to(&s, S1432{ x: 0 }); - move_to(&s, S1433{ x: 0 }); - move_to(&s, S1434{ x: 0 }); - move_to(&s, S1435{ x: 0 }); - move_to(&s, S1436{ x: 0 }); - move_to(&s, S1437{ x: 0 }); - move_to(&s, S1438{ x: 0 }); - move_to(&s, S1439{ x: 0 }); - move_to(&s, S1440{ x: 0 }); - move_to(&s, S1441{ x: 0 }); - move_to(&s, S1442{ x: 0 }); - move_to(&s, S1443{ x: 0 }); - move_to(&s, S1444{ x: 0 }); - move_to(&s, S1445{ x: 0 }); - move_to(&s, S1446{ x: 0 }); - move_to(&s, S1447{ x: 0 }); - move_to(&s, S1448{ x: 0 }); - move_to(&s, S1449{ x: 0 }); - move_to(&s, S1450{ x: 0 }); - move_to(&s, S1451{ x: 0 }); - move_to(&s, S1452{ x: 0 }); - move_to(&s, S1453{ x: 0 }); - move_to(&s, S1454{ x: 0 }); - move_to(&s, S1455{ x: 0 }); - move_to(&s, S1456{ x: 0 }); - move_to(&s, S1457{ x: 0 }); - move_to(&s, S1458{ x: 0 }); - move_to(&s, S1459{ x: 0 }); - move_to(&s, S1460{ x: 0 }); - move_to(&s, S1461{ x: 0 }); - move_to(&s, S1462{ x: 0 }); - move_to(&s, S1463{ x: 0 }); - move_to(&s, S1464{ x: 0 }); - move_to(&s, S1465{ x: 0 }); - move_to(&s, S1466{ x: 0 }); - move_to(&s, S1467{ x: 0 }); - move_to(&s, S1468{ x: 0 }); - move_to(&s, S1469{ x: 0 }); - move_to(&s, S1470{ x: 0 }); - move_to(&s, S1471{ x: 0 }); - move_to(&s, S1472{ x: 0 }); - move_to(&s, S1473{ x: 0 }); - move_to(&s, S1474{ x: 0 }); - move_to(&s, S1475{ x: 0 }); - move_to(&s, S1476{ x: 0 }); - move_to(&s, S1477{ x: 0 }); - move_to(&s, S1478{ x: 0 }); - move_to(&s, S1479{ x: 0 }); - move_to(&s, S1480{ x: 0 }); - move_to(&s, S1481{ x: 0 }); - move_to(&s, S1482{ x: 0 }); - move_to(&s, S1483{ x: 0 }); - move_to(&s, S1484{ x: 0 }); - move_to(&s, S1485{ x: 0 }); - move_to(&s, S1486{ x: 0 }); - move_to(&s, S1487{ x: 0 }); - move_to(&s, S1488{ x: 0 }); - move_to(&s, S1489{ x: 0 }); - move_to(&s, S1490{ x: 0 }); - move_to(&s, S1491{ x: 0 }); - move_to(&s, S1492{ x: 0 }); - move_to(&s, S1493{ x: 0 }); - move_to(&s, S1494{ x: 0 }); - move_to(&s, S1495{ x: 0 }); - move_to(&s, S1496{ x: 0 }); - move_to(&s, S1497{ x: 0 }); - move_to(&s, S1498{ x: 0 }); - move_to(&s, S1499{ x: 0 }); - move_to(&s, S1500{ x: 0 }); - move_to(&s, S1501{ x: 0 }); - move_to(&s, S1502{ x: 0 }); - move_to(&s, S1503{ x: 0 }); - move_to(&s, S1504{ x: 0 }); - move_to(&s, S1505{ x: 0 }); - move_to(&s, S1506{ x: 0 }); - move_to(&s, S1507{ x: 0 }); - move_to(&s, S1508{ x: 0 }); - move_to(&s, S1509{ x: 0 }); - move_to(&s, S1510{ x: 0 }); - move_to(&s, S1511{ x: 0 }); - move_to(&s, S1512{ x: 0 }); - move_to(&s, S1513{ x: 0 }); - move_to(&s, S1514{ x: 0 }); - move_to(&s, S1515{ x: 0 }); - move_to(&s, S1516{ x: 0 }); - move_to(&s, S1517{ x: 0 }); - move_to(&s, S1518{ x: 0 }); - move_to(&s, S1519{ x: 0 }); - move_to(&s, S1520{ x: 0 }); - move_to(&s, S1521{ x: 0 }); - move_to(&s, S1522{ x: 0 }); - move_to(&s, S1523{ x: 0 }); - move_to(&s, S1524{ x: 0 }); - move_to(&s, S1525{ x: 0 }); - move_to(&s, S1526{ x: 0 }); - move_to(&s, S1527{ x: 0 }); - move_to(&s, S1528{ x: 0 }); - move_to(&s, S1529{ x: 0 }); - move_to(&s, S1530{ x: 0 }); - move_to(&s, S1531{ x: 0 }); - move_to(&s, S1532{ x: 0 }); - move_to(&s, S1533{ x: 0 }); - move_to(&s, S1534{ x: 0 }); - move_to(&s, S1535{ x: 0 }); - move_to(&s, S1536{ x: 0 }); - move_to(&s, S1537{ x: 0 }); - move_to(&s, S1538{ x: 0 }); - move_to(&s, S1539{ x: 0 }); - move_to(&s, S1540{ x: 0 }); - move_to(&s, S1541{ x: 0 }); - move_to(&s, S1542{ x: 0 }); - move_to(&s, S1543{ x: 0 }); - move_to(&s, S1544{ x: 0 }); - move_to(&s, S1545{ x: 0 }); - move_to(&s, S1546{ x: 0 }); - move_to(&s, S1547{ x: 0 }); - move_to(&s, S1548{ x: 0 }); - move_to(&s, S1549{ x: 0 }); - move_to(&s, S1550{ x: 0 }); - move_to(&s, S1551{ x: 0 }); - move_to(&s, S1552{ x: 0 }); - move_to(&s, S1553{ x: 0 }); - move_to(&s, S1554{ x: 0 }); - move_to(&s, S1555{ x: 0 }); - move_to(&s, S1556{ x: 0 }); - move_to(&s, S1557{ x: 0 }); - move_to(&s, S1558{ x: 0 }); - move_to(&s, S1559{ x: 0 }); - move_to(&s, S1560{ x: 0 }); - move_to(&s, S1561{ x: 0 }); - move_to(&s, S1562{ x: 0 }); - move_to(&s, S1563{ x: 0 }); - move_to(&s, S1564{ x: 0 }); - move_to(&s, S1565{ x: 0 }); - move_to(&s, S1566{ x: 0 }); - move_to(&s, S1567{ x: 0 }); - move_to(&s, S1568{ x: 0 }); - move_to(&s, S1569{ x: 0 }); - move_to(&s, S1570{ x: 0 }); - move_to(&s, S1571{ x: 0 }); - move_to(&s, S1572{ x: 0 }); - move_to(&s, S1573{ x: 0 }); - move_to(&s, S1574{ x: 0 }); - move_to(&s, S1575{ x: 0 }); - move_to(&s, S1576{ x: 0 }); - move_to(&s, S1577{ x: 0 }); - move_to(&s, S1578{ x: 0 }); - move_to(&s, S1579{ x: 0 }); - move_to(&s, S1580{ x: 0 }); - move_to(&s, S1581{ x: 0 }); - move_to(&s, S1582{ x: 0 }); - move_to(&s, S1583{ x: 0 }); - move_to(&s, S1584{ x: 0 }); - move_to(&s, S1585{ x: 0 }); - move_to(&s, S1586{ x: 0 }); - move_to(&s, S1587{ x: 0 }); - move_to(&s, S1588{ x: 0 }); - move_to(&s, S1589{ x: 0 }); - move_to(&s, S1590{ x: 0 }); - move_to(&s, S1591{ x: 0 }); - move_to(&s, S1592{ x: 0 }); - move_to(&s, S1593{ x: 0 }); - move_to(&s, S1594{ x: 0 }); - move_to(&s, S1595{ x: 0 }); - move_to(&s, S1596{ x: 0 }); - move_to(&s, S1597{ x: 0 }); - move_to(&s, S1598{ x: 0 }); - move_to(&s, S1599{ x: 0 }); - move_to(&s, S1600{ x: 0 }); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/mut-borrow-global.mvir b/aptos-move/aptos-gas-calibration/samples_ir/global-operations/mut-borrow-global.mvir deleted file mode 100644 index c91520cb1a64c..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/global-operations/mut-borrow-global.mvir +++ /dev/null @@ -1,134 +0,0 @@ -module 0xcafe.MutBorrowGlobal { - struct S1 has key, drop { x: u64 } - struct S2 has key, drop { x: u64 } - struct S3 has key, drop { x: u64 } - struct S4 has key, drop { x: u64 } - struct S5 has key, drop { x: u64 } - struct S6 has key, drop { x: u64 } - struct S7 has key, drop { x: u64 } - struct S8 has key, drop { x: u64 } - struct S9 has key, drop { x: u64 } - struct S10 has key, drop { x: u64 } - struct S11 has key, drop { x: u64 } - struct S12 has key, drop { x: u64 } - struct S13 has key, drop { x: u64 } - struct S14 has key, drop { x: u64 } - struct S15 has key, drop { x: u64 } - struct S16 has key, drop { x: u64 } - struct S17 has key, drop { x: u64 } - struct S18 has key, drop { x: u64 } - struct S19 has key, drop { x: u64 } - struct S20 has key, drop { x: u64 } - struct S21 has key, drop { x: u64 } - struct S22 has key, drop { x: u64 } - struct S23 has key, drop { x: u64 } - struct S24 has key, drop { x: u64 } - struct S25 has key, drop { x: u64 } - struct S26 has key, drop { x: u64 } - struct S27 has key, drop { x: u64 } - struct S28 has key, drop { x: u64 } - struct S29 has key, drop { x: u64 } - struct S30 has key, drop { x: u64 } - - public entry calibrate_mut_borrow_global_x100(s: signer) acquires S1, S2, S3, S4, S5, S6, S7, S8, S9, S10 { - let i: u64; - label entry: - i = 0; - move_to(&s, S1{ x: 0 }); - move_to(&s, S2{ x: 0 }); - move_to(&s, S3{ x: 0 }); - move_to(&s, S4{ x: 0 }); - move_to(&s, S5{ x: 0 }); - move_to(&s, S6{ x: 0 }); - move_to(&s, S7{ x: 0 }); - move_to(&s, S8{ x: 0 }); - move_to(&s, S9{ x: 0 }); - move_to(&s, S10{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_global_x500(s: signer) acquires S11, S12, S13, S14, S15, S16, S17, S18, S19, S20 { - let i: u64; - label entry: - i = 0; - move_to(&s, S11{ x: 0 }); - move_to(&s, S12{ x: 0 }); - move_to(&s, S13{ x: 0 }); - move_to(&s, S14{ x: 0 }); - move_to(&s, S15{ x: 0 }); - move_to(&s, S16{ x: 0 }); - move_to(&s, S17{ x: 0 }); - move_to(&s, S18{ x: 0 }); - move_to(&s, S19{ x: 0 }); - move_to(&s, S20{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 500) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_global_x1500(s: signer) acquires S21, S22, S23, S24, S25, S26, S27, S28, S29, S30 { - let i: u64; - label entry: - i = 0; - move_to(&s, S21{ x: 0 }); - move_to(&s, S22{ x: 0 }); - move_to(&s, S23{ x: 0 }); - move_to(&s, S24{ x: 0 }); - move_to(&s, S25{ x: 0 }); - move_to(&s, S26{ x: 0 }); - move_to(&s, S27{ x: 0 }); - move_to(&s, S28{ x: 0 }); - move_to(&s, S29{ x: 0 }); - move_to(&s, S30{ x: 0 }); - label loop_start: - jump_if_false (copy(i) < 1500) loop_end; - i = move(i) + 1; - - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - _ = borrow_global_mut(0xcafe); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ld-true.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ld-true.mvir deleted file mode 100644 index 64c33415071aa..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ld-true.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdTrue { - - public calibrate_ld_true_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (true, true, true, true, true, true, true, true, true, true); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ld_true_x100() { - label b0: - Self.calibrate_ld_true_impl(10); - return; - } - - public entry calibrate_ld_true_x500() { - label b0: - Self.calibrate_ld_true_impl(50); - return; - } - - public entry calibrate_ld_true_x1000() { - label b0: - Self.calibrate_ld_true_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu128.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu128.mvir deleted file mode 100644 index b37fbe4e73782..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu128.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdU128 { - - public calibrate_ldu128_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (1u128,2u128,3u128,4u128,5u128,6u128,7u128,8u128,9u128,10u128); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ldu128_x100() { - label b0: - Self.calibrate_ldu128_impl(10); - return; - } - - public entry calibrate_ldu128_x500() { - label b0: - Self.calibrate_ldu128_impl(50); - return; - } - - public entry calibrate_ldu128_x1000() { - label b0: - Self.calibrate_ldu128_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu16.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu16.mvir deleted file mode 100644 index a79131dbec1e1..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu16.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdU16 { - - public calibrate_ldu16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (1u16,2u16,3u16,4u16,5u16,6u16,7u16,8u16,9u16,10u16); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ldu16_x100() { - label b0: - Self.calibrate_ldu16_impl(10); - return; - } - - public entry calibrate_ldu16_x500() { - label b0: - Self.calibrate_ldu16_impl(50); - return; - } - - public entry calibrate_ldu16_x1000() { - label b0: - Self.calibrate_ldu16_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu256.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu256.mvir deleted file mode 100644 index ebd8065139b81..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu256.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdU256 { - - public calibrate_ldu256_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (1u256,2u256,3u256,4u256,5u256,6u256,7u256,8u256,9u256,10u256); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ldu256_x100() { - label b0: - Self.calibrate_ldu256_impl(10); - return; - } - - public entry calibrate_ldu256_x500() { - label b0: - Self.calibrate_ldu256_impl(50); - return; - } - - public entry calibrate_ldu256_x1000() { - label b0: - Self.calibrate_ldu256_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu32.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu32.mvir deleted file mode 100644 index c95815abb8131..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu32.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdU32 { - - public calibrate_ldu32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (1u32,2u32,3u32,4u32,5u32,6u32,7u32,8u32,9u32,10u32); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ldu32_x100() { - label b0: - Self.calibrate_ldu32_impl(10); - return; - } - - public entry calibrate_ldu32_x500() { - label b0: - Self.calibrate_ldu32_impl(50); - return; - } - - public entry calibrate_ldu32_x1000() { - label b0: - Self.calibrate_ldu32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu8.mvir b/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu8.mvir deleted file mode 100644 index 9c80047ec4463..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/ld/ldu8.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.LdU8 { - - public calibrate_ldu8_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (1u8,2u8,3u8,4u8,5u8,6u8,7u8,8u8,9u8,10u8); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ldu8_x100() { - label b0: - Self.calibrate_ldu8_impl(10); - return; - } - - public entry calibrate_ldu8_x500() { - label b0: - Self.calibrate_ldu8_impl(50); - return; - } - - public entry calibrate_ldu8_x1000() { - label b0: - Self.calibrate_ldu8_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/and.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/and.mvir deleted file mode 100644 index 63510ddb915c3..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/and.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.ConditionsAnd { - - public calibrate_and_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = true && true && true && true && true && true && true && true && true && false; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_and_x100() { - label b0: - Self.calibrate_and_impl(10); - return; - } - - public entry calibrate_and_x500() { - label b0: - Self.calibrate_and_impl(50); - return; - } - - public entry calibrate_and_x1000() { - label b0: - Self.calibrate_and_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/eq.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/eq.mvir deleted file mode 100644 index 069b2101285d3..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/eq.mvir +++ /dev/null @@ -1,311 +0,0 @@ -module 0xcafe.Eq { - - public eq_u16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u16(21u8) == 21u16; - _ = to_u16(21u64) == 21u16; - _ = to_u16(21u128) == 21u16; - _ = to_u16(21u16) == 21u16; - _ = to_u16(21u32) == 21u16; - _ = to_u16(21u256) == 21u16; - _ = to_u16(255u8) == 255u16; - _ = to_u16(65535u64) == 65535u16; - _ = to_u16(65535u128) == 65535u16; - _ = to_u16(65535u16) == 65535u16; - _ = to_u16(65535u32) == 65535u16; - _ = to_u16(65535u256) == 65535u16; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_with_u16_x120() { - label b0: - Self.eq_u16_impl(10); - return; - } - - public entry calibrate_eq_with_u16_x600() { - label b0: - Self.eq_u16_impl(50); - return; - } - - public entry calibrate_eq_with_u16_x1200() { - label b0: - Self.eq_u16_impl(100); - return; - } - - public eq_u32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u32(21u8) == 21u32; - _ = to_u32(21u64) == 21u32; - _ = to_u32(21u128) == 21u32; - _ = to_u32(21u16) == 21u32; - _ = to_u32(21u32) == 21u32; - _ = to_u32(21u256) == 21u32; - _ = to_u32(255u8) == 255u32; - _ = to_u32(4294967295u64) == 4294967295u32; - _ = to_u32(4294967295u128) == 4294967295u32; - _ = to_u32(65535u16) == 65535u32; - _ = to_u32(4294967295u32) == 4294967295u32; - _ = to_u32(4294967295u256) == 4294967295u32; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_with_u32_x120() { - label b0: - Self.eq_u32_impl(10); - return; - } - - public entry calibrate_eq_with_u32_x600() { - label b0: - Self.eq_u32_impl(50); - return; - } - - public entry calibrate_eq_with_u32_x1200() { - label b0: - Self.eq_u32_impl(100); - return; - } - - public eq_u64_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u64(0u8) == 0u64; - _ = to_u64(0u64) == 0u64; - _ = to_u64(0u128) == 0u64; - _ = to_u64(21u8) == 21u64; - _ = to_u64(21u64) == 21u64; - _ = to_u64(21u128) == 21u64; - _ = to_u64(255u8) == 255u64; - _ = to_u64(18446744073709551615u64) == 18446744073709551615u64; - _ = to_u64(18446744073709551615u128) == 18446744073709551615u64; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_with_u64_x90() { - label b0: - Self.eq_u64_impl(10); - return; - } - - public entry calibrate_eq_with_u64_x450() { - label b0: - Self.eq_u64_impl(50); - return; - } - - public entry calibrate_eq_with_u64_x900() { - label b0: - Self.eq_u64_impl(100); - return; - } - - public eq_u128_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u128(0u8) == 0u128; - _ = to_u128(0u64) == 0u128; - _ = to_u128(0u128) == 0u128; - _ = to_u128(21u8) == 21u128; - _ = to_u128(21u64) == 21u128; - _ = to_u128(21u128) == 21u128; - _ = to_u128(255u8) == 255u128; - _ = to_u128(18446744073709551615u64) == 18446744073709551615u128; - _ = to_u128(340282366920938463463374607431768211455u128) == 340282366920938463463374607431768211455u128; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_with_u128_x90() { - label b0: - Self.eq_u128_impl(10); - return; - } - - public entry calibrate_eq_with_u128_x450() { - label b0: - Self.eq_u128_impl(50); - return; - } - - public entry calibrate_eq_with_u128_x900() { - label b0: - Self.eq_u128_impl(100); - return; - } - - public eq_u256_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u256(0u8) == 0u256; - _ = to_u256(0u64) == 0u256; - _ = to_u256(0u128) == 0u256; - _ = to_u256(0u16) == 0u256; - _ = to_u256(0u32) == 0u256; - _ = to_u256(0u256) == 0u256; - _ = to_u256(21u8) == 21u256; - _ = to_u256(21u64) == 21u256; - _ = to_u256(21u128) == 21u256; - _ = to_u256(21u16) == 21u256; - _ = to_u256(21u32) == 21u256; - _ = to_u256(21u256) == 21u256; - _ = to_u256(255u8) == 255u256; - _ = to_u256(18446744073709551615u64) == 18446744073709551615u256; - _ = to_u256(340282366920938463463374607431768211455u128) == 340282366920938463463374607431768211455u256; - _ = to_u256(65535u16) == 65535u256; - _ = to_u256(4294967295u32) == 4294967295u256; - _ = to_u256(115792089237316195423570985008687907853269984665640564039457584007913129639935u256) == 115792089237316195423570985008687907853269984665640564039457584007913129639935u256; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_with_u256_x180() { - label b0: - Self.eq_u256_impl(10); - return; - } - - public entry calibrate_eq_with_u256_x900() { - label b0: - Self.eq_u256_impl(50); - return; - } - - public entry calibrate_eq_with_u256_x1800() { - label b0: - Self.eq_u256_impl(100); - return; - } - - public eq_vec_same_len_impl(n: u64) { - let i: u64; - let a: vector; - let b: vector; - label entry: - i = 0; - a = h"000102030405060712346536356345012392385678567856785678567856785678123412341234123412341234"; - b = h"000102030405060712346536356345012392385678567856785678567856785678123412341234123412341234"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&a == &b,&a == &b,&a == &b,&a == &b,&a == &b,&a == &b,&a == &b,&a == &b,&a == &b,&a == &b); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_vec_same_len_x100() { - label b0: - Self.eq_vec_same_len_impl(10); - return; - } - - public entry calibrate_eq_vec_same_len_x500() { - label b0: - Self.eq_vec_same_len_impl(50); - return; - } - - public entry calibrate_eq_vec_same_len_x1000() { - label b0: - Self.eq_vec_same_len_impl(100); - return; - } - - public eq_vec_mix_len_impl(n: u64) { - let i: u64; - let a: vector; - let b: vector; - let c: vector; - let d: vector; - let e: vector; - let f: vector; - let g: vector; - let h: vector; - let k: vector; - label entry: - i = 0; - a = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - b = h""; - c = h"000102030405060712346536356345012392385678567856785678567856785678"; - d = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - e = h"1234"; - f = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - g = h""; - h = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - k = h"000102030405060712346536356345012392385678567856785678567856785678"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&a == &b,&c == &d,&e == &f,&g == &h,&h == &k,&a == &b,&c == &d,&e == &f,&g == &h,&h == &k); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_eq_vec_mix_len_x100() { - label b0: - Self.eq_vec_mix_len_impl(10); - return; - } - - public entry calibrate_eq_vec_mix_len_x500() { - label b0: - Self.eq_vec_mix_len_impl(50); - return; - } - - public entry calibrate_eq_vec_mix_len_x1000() { - label b0: - Self.eq_vec_mix_len_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/ge.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/ge.mvir deleted file mode 100644 index 9aefc28b2a57f..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/ge.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBoolGe { - - public calibrate_ge_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10, 0 >= 10); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_ge_x100() { - label b0: - Self.calibrate_ge_impl(10); - return; - } - - public entry calibrate_ge_x500() { - label b0: - Self.calibrate_ge_impl(50); - return; - } - - public entry calibrate_ge_x1000() { - label b0: - Self.calibrate_ge_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/gt.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/gt.mvir deleted file mode 100644 index 27a58ce239ed1..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/gt.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBoolGt { - - public calibrate_gt_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10, 0 > 10); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_gt_x100() { - label b0: - Self.calibrate_gt_impl(10); - return; - } - - public entry calibrate_gt_x500() { - label b0: - Self.calibrate_gt_impl(50); - return; - } - - public entry calibrate_gt_x1000() { - label b0: - Self.calibrate_gt_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/le.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/le.mvir deleted file mode 100644 index 7d8956b4df8c3..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/le.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBoolLe { - - public calibrate_le_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10, 0 <= 10); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_le_x100() { - label b0: - Self.calibrate_le_impl(10); - return; - } - - public entry calibrate_le_x500() { - label b0: - Self.calibrate_le_impl(50); - return; - } - - public entry calibrate_le_x1000() { - label b0: - Self.calibrate_le_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/neq.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/neq.mvir deleted file mode 100644 index 8adc2c077dc09..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/neq.mvir +++ /dev/null @@ -1,311 +0,0 @@ -module 0xcafe.Neq { - - public neq_u16_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u16(21u8) != 21u16; - _ = to_u16(21u64) != 21u16; - _ = to_u16(21u128) != 21u16; - _ = to_u16(21u16) != 21u16; - _ = to_u16(21u32) != 21u16; - _ = to_u16(21u256) != 21u16; - _ = to_u16(255u8) != 255u16; - _ = to_u16(65535u64) != 65535u16; - _ = to_u16(65535u128) != 65535u16; - _ = to_u16(65535u16) != 65535u16; - _ = to_u16(65535u32) != 65535u16; - _ = to_u16(65535u256) != 65535u16; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_with_u16_x120() { - label b0: - Self.neq_u16_impl(10); - return; - } - - public entry calibrate_neq_with_u16_x600() { - label b0: - Self.neq_u16_impl(50); - return; - } - - public entry calibrate_neq_with_u16_x1200() { - label b0: - Self.neq_u16_impl(100); - return; - } - - public neq_u32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u32(21u8) != 21u32; - _ = to_u32(21u64) != 21u32; - _ = to_u32(21u128) != 21u32; - _ = to_u32(21u16) != 21u32; - _ = to_u32(21u32) != 21u32; - _ = to_u32(21u256) != 21u32; - _ = to_u32(255u8) != 255u32; - _ = to_u32(4294967295u64) != 4294967295u32; - _ = to_u32(4294967295u128) != 4294967295u32; - _ = to_u32(65535u16) != 65535u32; - _ = to_u32(4294967295u32) != 4294967295u32; - _ = to_u32(4294967295u256) != 4294967295u32; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_with_u32_x120() { - label b0: - Self.neq_u32_impl(10); - return; - } - - public entry calibrate_neq_with_u32_x600() { - label b0: - Self.neq_u32_impl(50); - return; - } - - public entry calibrate_neq_with_u32_x1200() { - label b0: - Self.neq_u32_impl(100); - return; - } - - public neq_u64_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u64(0u8) != 0u64; - _ = to_u64(0u64) != 0u64; - _ = to_u64(0u128) != 0u64; - _ = to_u64(21u8) != 21u64; - _ = to_u64(21u64) != 21u64; - _ = to_u64(21u128) != 21u64; - _ = to_u64(255u8) != 255u64; - _ = to_u64(18446744073709551615u64) != 18446744073709551615u64; - _ = to_u64(18446744073709551615u128) != 18446744073709551615u64; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_with_u64_x90() { - label b0: - Self.neq_u64_impl(10); - return; - } - - public entry calibrate_neq_with_u64_x450() { - label b0: - Self.neq_u64_impl(50); - return; - } - - public entry calibrate_neq_with_u64_x900() { - label b0: - Self.neq_u64_impl(100); - return; - } - - public neq_u128_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u128(0u8) != 0u128; - _ = to_u128(0u64) != 0u128; - _ = to_u128(0u128) != 0u128; - _ = to_u128(21u8) != 21u128; - _ = to_u128(21u64) != 21u128; - _ = to_u128(21u128) != 21u128; - _ = to_u128(255u8) != 255u128; - _ = to_u128(18446744073709551615u64) != 18446744073709551615u128; - _ = to_u128(340282366920938463463374607431768211455u128) != 340282366920938463463374607431768211455u128; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_with_u128_x90() { - label b0: - Self.neq_u128_impl(10); - return; - } - - public entry calibrate_neq_with_u128_x450() { - label b0: - Self.neq_u128_impl(50); - return; - } - - public entry calibrate_neq_with_u128_x900() { - label b0: - Self.neq_u128_impl(100); - return; - } - - public neq_u256_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = to_u256(0u8) != 0u256; - _ = to_u256(0u64) != 0u256; - _ = to_u256(0u128) != 0u256; - _ = to_u256(0u16) != 0u256; - _ = to_u256(0u32) != 0u256; - _ = to_u256(0u256) != 0u256; - _ = to_u256(21u8) != 21u256; - _ = to_u256(21u64) != 21u256; - _ = to_u256(21u128) != 21u256; - _ = to_u256(21u16) != 21u256; - _ = to_u256(21u32) != 21u256; - _ = to_u256(21u256) != 21u256; - _ = to_u256(255u8) != 255u256; - _ = to_u256(18446744073709551615u64) != 18446744073709551615u256; - _ = to_u256(340282366920938463463374607431768211455u128) != 340282366920938463463374607431768211455u256; - _ = to_u256(65535u16) != 65535u256; - _ = to_u256(4294967295u32) != 4294967295u256; - _ = to_u256(115792089237316195423570985008687907853269984665640564039457584007913129639935u256) != 115792089237316195423570985008687907853269984665640564039457584007913129639935u256; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_with_u256_x180() { - label b0: - Self.neq_u256_impl(10); - return; - } - - public entry calibrate_neq_with_u256_x900() { - label b0: - Self.neq_u256_impl(50); - return; - } - - public entry calibrate_neq_with_u256_x1800() { - label b0: - Self.neq_u256_impl(100); - return; - } - - public neq_vec_same_len_impl(n: u64) { - let i: u64; - let a: vector; - let b: vector; - label entry: - i = 0; - a = h"000102030405060712346536356345012392385678567856785678567856785678123412341234123412341234"; - b = h"000102030405060712346536356345012392385678567856785678567856785678123412341234123412341234"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&a != &b,&a != &b,&a != &b,&a != &b,&a != &b,&a != &b,&a != &b,&a != &b,&a != &b,&a != &b); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_vec_same_len_x100() { - label b0: - Self.neq_vec_same_len_impl(10); - return; - } - - public entry calibrate_neq_vec_same_len_x500() { - label b0: - Self.neq_vec_same_len_impl(50); - return; - } - - public entry calibrate_neq_vec_same_len_x1000() { - label b0: - Self.neq_vec_same_len_impl(100); - return; - } - - public neq_vec_mix_len_impl(n: u64) { - let i: u64; - let a: vector; - let b: vector; - let c: vector; - let d: vector; - let e: vector; - let f: vector; - let g: vector; - let h: vector; - let k: vector; - label entry: - i = 0; - a = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - b = h""; - c = h"000102030405060712346536356345012392385678567856785678567856785678"; - d = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - e = h"1234"; - f = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - g = h""; - h = h"000102030405060712346536356345012392385678567856785678567856785678000102030405060712346536356345012392385678567856785678567856785678"; - k = h"000102030405060712346536356345012392385678567856785678567856785678"; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&a != &b,&c != &d,&e != &f,&g != &h,&h != &k,&a != &b,&c != &d,&e != &f,&g != &h,&h != &k); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_neq_vec_mix_len_x100() { - label b0: - Self.neq_vec_mix_len_impl(10); - return; - } - - public entry calibrate_neq_vec_mix_len_x500() { - label b0: - Self.neq_vec_mix_len_impl(50); - return; - } - - public entry calibrate_neq_vec_mix_len_x1000() { - label b0: - Self.neq_vec_mix_len_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/not.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/not.mvir deleted file mode 100644 index ef88b481148c8..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/not.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBoolNot { - - public calibrate_not_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = !!!!!!!!!!true; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_not_x100() { - label b0: - Self.calibrate_not_impl(10); - return; - } - - public entry calibrate_not_x500() { - label b0: - Self.calibrate_not_impl(50); - return; - } - - public entry calibrate_not_x1000() { - label b0: - Self.calibrate_not_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/or.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/or.mvir deleted file mode 100644 index fb3a0645bbe43..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations-bool/or.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBoolOr { - - public calibrate_or_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = false || false || false || false || false || false || false || false || false || false; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_or_x100() { - label b0: - Self.calibrate_or_impl(10); - return; - } - - public entry calibrate_or_x500() { - label b0: - Self.calibrate_or_impl(50); - return; - } - - public entry calibrate_or_x1000() { - label b0: - Self.calibrate_or_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/bitand.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/bitand.mvir deleted file mode 100644 index 5ebf4f0ead150..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/bitand.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBitAnd { - - public calibrate_bitand_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_bitand_x100() { - label b0: - Self.calibrate_bitand_impl(10); - return; - } - - public entry calibrate_bitand_x500() { - label b0: - Self.calibrate_bitand_impl(50); - return; - } - - public entry calibrate_bitand_x1000() { - label b0: - Self.calibrate_bitand_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/bitor.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/bitor.mvir deleted file mode 100644 index 8a5d78b417b08..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/bitor.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsBitOr { - - public calibrate_bitor_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_bitor_x100() { - label b0: - Self.calibrate_bitor_impl(10); - return; - } - - public entry calibrate_bitor_x500() { - label b0: - Self.calibrate_bitor_impl(50); - return; - } - - public entry calibrate_bitor_x1000() { - label b0: - Self.calibrate_bitor_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/div.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/div.mvir deleted file mode 100644 index 1c80c8df9aee2..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/div.mvir +++ /dev/null @@ -1,47 +0,0 @@ -module 0xcafe.OperationDiv { - - public entry calibrate_div_x100() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 10) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_div_x500() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 50) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_div_x1000() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1, 100/1); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/mod.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/mod.mvir deleted file mode 100644 index 796764bbf3d7d..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/mod.mvir +++ /dev/null @@ -1,47 +0,0 @@ -module 0xcafe.OperationMod { - - public entry calibrate_mod_x100() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 10) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mod_x500() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 50) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mod_x1000() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 100) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1, 100%1); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/mul.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/mul.mvir deleted file mode 100644 index 97716e67e43a6..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/mul.mvir +++ /dev/null @@ -1,78 +0,0 @@ -module 0xcafe.OperationMul { - // - mul - - public entry calibrate_mul_x10() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 1) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mul_x50() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 5) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mul_x100() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 10) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mul_x300() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 30) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mul_x600() { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < 60) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1, 100*1); - - jump loop_start; - label loop_end: - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/shl.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/shl.mvir deleted file mode 100644 index 4814ebb0479fd..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/shl.mvir +++ /dev/null @@ -1,44 +0,0 @@ -module 0xcafe.OperationsShl { - - public calibrate_shl_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = (1u8 << 1u8); - _ = (7u64 << 1u8); - _ = (1000u128 << 1u8); - _ = (3u16 << 1u8); - _ = (7u32 << 1u8); - _ = (1000u256 << 1u8); - _ = (123453u256 << 13u8); - _ = (123453678909u256 << 76u8); - _ = (1234536789093546757803u256 << 168u8); - _ = (1234536789093546757803786604381691994985672142341299639418u256 << 202u8); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_shl_x100() { - label b0: - Self.calibrate_shl_impl(10); - return; - } - - public entry calibrate_shl_x500() { - label b0: - Self.calibrate_shl_impl(50); - return; - } - - public entry calibrate_shl_x1000() { - label b0: - Self.calibrate_shl_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/shr.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/shr.mvir deleted file mode 100644 index f3c30adcbae46..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/shr.mvir +++ /dev/null @@ -1,44 +0,0 @@ -module 0xcafe.OperationsShr { - - public calibrate_shr_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = (1u8 >> 1u8); - _ = (7u64 >> 1u8); - _ = (1000u128 >> 1u8); - _ = (3u16 >> 1u8); - _ = (7u32 >> 1u8); - _ = (1000u256 >> 1u8); - _ = (123453u256 >> 13u8); - _ = (123453678909u256 >> 76u8); - _ = (1234536789093546757803u256 >> 168u8); - _ = (1234536789093546757803786604381691994985672142341299639418u256 >> 202u8); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_shr_x100() { - label b0: - Self.calibrate_shr_impl(10); - return; - } - - public entry calibrate_shr_x500() { - label b0: - Self.calibrate_shr_impl(50); - return; - } - - public entry calibrate_shr_x1000() { - label b0: - Self.calibrate_shr_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/operations/xor.mvir b/aptos-move/aptos-gas-calibration/samples_ir/operations/xor.mvir deleted file mode 100644 index 9cbfe67d6044c..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/operations/xor.mvir +++ /dev/null @@ -1,35 +0,0 @@ -module 0xcafe.OperationsXor { - - public calibrate_xor_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = 1 ^ 1 ^ 1 ^ 1 ^ 1 ^ 1 ^ 1 ^ 1 ^ 1 ^ 1; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_xor_x100() { - label b0: - Self.calibrate_xor_impl(10); - return; - } - - public entry calibrate_xor_x500() { - label b0: - Self.calibrate_xor_impl(50); - return; - } - - public entry calibrate_xor_x1000() { - label b0: - Self.calibrate_xor_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/struct/imm-borrow-field.mvir b/aptos-move/aptos-gas-calibration/samples_ir/struct/imm-borrow-field.mvir deleted file mode 100644 index b4a11e0860a0b..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/struct/imm-borrow-field.mvir +++ /dev/null @@ -1,42 +0,0 @@ -module 0xcafe.ImmBorrowField { - - struct Foo has drop { u: u64 } - - public calibrate_imm_borrow_field_foo_impl(n: u64) { - let i: u64; - let a: Self.Foo; - let b: &Self.Foo; - label entry: - i = 0; - a = Foo { u: 0 }; - b = &a; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u,©(b).Foo::u); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_imm_borrow_field_foo_x100() { - label b0: - Self.calibrate_imm_borrow_field_foo_impl(10); - return; - } - - public entry calibrate_imm_borrow_field_foo_x500() { - label b0: - Self.calibrate_imm_borrow_field_foo_impl(50); - return; - } - - public entry calibrate_imm_borrow_field_foo_x1000() { - label b0: - Self.calibrate_imm_borrow_field_foo_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/struct/mut-borrow-field.mvir b/aptos-move/aptos-gas-calibration/samples_ir/struct/mut-borrow-field.mvir deleted file mode 100644 index a0fd0d6e94974..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/struct/mut-borrow-field.mvir +++ /dev/null @@ -1,42 +0,0 @@ -module 0xcafe.MutBorrowField { - - struct Foo has drop { u: u64 } - - public calibrate_mut_borrow_field_foo_impl(n: u64) { - let i: u64; - let a: Self.Foo; - let b: &mut Self.Foo; - label entry: - i = 0; - a = Foo { u: 0 }; - b = &mut a; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _,_,_,_,_,_,_,_,_,_ = (&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u,&mut copy(b).Foo::u); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_mut_borrow_field_foo_x100() { - label b0: - Self.calibrate_mut_borrow_field_foo_impl(10); - return; - } - - public entry calibrate_mut_borrow_field_foo_x500() { - label b0: - Self.calibrate_mut_borrow_field_foo_impl(50); - return; - } - - public entry calibrate_mut_borrow_field_foo_x1000() { - label b0: - Self.calibrate_mut_borrow_field_foo_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/struct/pack.mvir b/aptos-move/aptos-gas-calibration/samples_ir/struct/pack.mvir deleted file mode 100644 index 8579ffa4c3cd1..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/struct/pack.mvir +++ /dev/null @@ -1,186 +0,0 @@ -module 0xcafe.Pack { - struct Foo has drop { x: u64, y: u64 } - struct Bar has drop { x: u64, y: u64, z: Self.Foo } - - struct T1 has drop { x: u64 } - struct T2 has drop { x: Self.T1 } - struct T3 has drop { x: Self.T2 } - struct T4 has drop { x: Self.T3 } - struct T5 has drop { x: Self.T4 } - struct T6 has drop { x: Self.T5 } - struct T7 has drop { x: Self.T6 } - struct T8 has drop { x: Self.T7 } - struct T9 has drop { x: Self.T8 } - struct T10 has drop { x: Self.T9 } - struct T11 has drop { x: Self.T10 } - struct T12 has drop { x: Self.T11 } - struct T13 has drop { x: Self.T12 } - struct T14 has drop { x: Self.T13 } - struct T15 has drop { x: Self.T14 } - struct T16 has drop { x: Self.T15 } - struct T17 has drop { x: Self.T16 } - struct T18 has drop { x: Self.T17 } - struct T19 has drop { x: Self.T18 } - struct T20 has drop { x: Self.T19 } - struct T21 has drop { x: Self.T20 } - struct T22 has drop { x: Self.T21 } - struct T23 has drop { x: Self.T22 } - struct T24 has drop { x: Self.T23 } - struct T25 has drop { x: Self.T24 } - struct T26 has drop { x: Self.T25 } - struct T27 has drop { x: Self.T26 } - struct T28 has drop { x: Self.T27 } - struct T29 has drop { x: Self.T28 } - struct T30 has drop { x: Self.T29 } - struct T31 has drop { x: Self.T30 } - - public calibrate_pack_foo_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}, Foo{x:0, y:0}); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_foo_x100() { - label b0: - Self.calibrate_pack_foo_impl(10); - return; - } - - public entry calibrate_pack_foo_x500() { - label b0: - Self.calibrate_pack_foo_impl(50); - return; - } - - public entry calibrate_pack_foo_x1000() { - label b0: - Self.calibrate_pack_foo_impl(100); - return; - } - - public calibrate_pack_bar_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}, Bar{x:0, y:0, z:Foo{x:0, y:0}}); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_bar_x100() { - label b0: - Self.calibrate_pack_bar_impl(10); - return; - } - - public entry calibrate_pack_bar_x500() { - label b0: - Self.calibrate_pack_bar_impl(50); - return; - } - - public entry calibrate_pack_bar_x1000() { - label b0: - Self.calibrate_pack_bar_impl(100); - return; - } - - public calibrate_pack_t8_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - _ = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_t8_x100() { - label b0: - Self.calibrate_pack_t8_impl(10); - return; - } - - public entry calibrate_pack_t8_x500() { - label b0: - Self.calibrate_pack_t8_impl(50); - return; - } - - public entry calibrate_pack_t8_x1000() { - label b0: - Self.calibrate_pack_t8_impl(100); - return; - } - - public calibrate_pack_t32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - _ = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_pack_t32_x100() { - label b0: - Self.calibrate_pack_t32_impl(10); - return; - } - - public entry calibrate_pack_t32_x500() { - label b0: - Self.calibrate_pack_t32_impl(50); - return; - } - - public entry calibrate_pack_t32_x1000() { - label b0: - Self.calibrate_pack_t32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/struct/unpack.mvir b/aptos-move/aptos-gas-calibration/samples_ir/struct/unpack.mvir deleted file mode 100644 index d410d7d093468..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/struct/unpack.mvir +++ /dev/null @@ -1,260 +0,0 @@ -module 0xcafe.Unpack { - struct Foo has drop { x: u64, y: u64 } - struct Bar has drop { x: u64, y: u64, z: Self.Foo } - - struct T1 has drop { x: u64 } - struct T2 has drop { x: Self.T1 } - struct T3 has drop { x: Self.T2 } - struct T4 has drop { x: Self.T3 } - struct T5 has drop { x: Self.T4 } - struct T6 has drop { x: Self.T5 } - struct T7 has drop { x: Self.T6 } - struct T8 has drop { x: Self.T7 } - struct T9 has drop { x: Self.T8 } - struct T10 has drop { x: Self.T9 } - struct T11 has drop { x: Self.T10 } - struct T12 has drop { x: Self.T11 } - struct T13 has drop { x: Self.T12 } - struct T14 has drop { x: Self.T13 } - struct T15 has drop { x: Self.T14 } - struct T16 has drop { x: Self.T15 } - struct T17 has drop { x: Self.T16 } - struct T18 has drop { x: Self.T17 } - struct T19 has drop { x: Self.T18 } - struct T20 has drop { x: Self.T19 } - struct T21 has drop { x: Self.T20 } - struct T22 has drop { x: Self.T21 } - struct T23 has drop { x: Self.T22 } - struct T24 has drop { x: Self.T23 } - struct T25 has drop { x: Self.T24 } - struct T26 has drop { x: Self.T25 } - struct T27 has drop { x: Self.T26 } - struct T28 has drop { x: Self.T27 } - struct T29 has drop { x: Self.T28 } - struct T30 has drop { x: Self.T29 } - struct T31 has drop { x: Self.T30 } - - public calibrate_unpack_foo_impl(n: u64) { - let i: u64; - let x: u64; - let y: u64; - let t: Self.Foo; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = Foo{x:0,y:0}; - Foo{x,y} = move(t); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_foo_x100() { - label b0: - Self.calibrate_unpack_foo_impl(10); - return; - } - - public entry calibrate_unpack_foo_x500() { - label b0: - Self.calibrate_unpack_foo_impl(50); - return; - } - - public entry calibrate_unpack_foo_x1000() { - label b0: - Self.calibrate_unpack_foo_impl(100); - return; - } - - public calibrate_unpack_bar_impl(n: u64) { - let i: u64; - let x: u64; - let y: u64; - let s: u64; - let w: u64; - let z: Self.Foo; - let t: Self.Bar; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t = Bar{x:0,y:0,z:Foo{x:0,y:0}}; - Bar{x,y,z} = move(t); - Foo{s,w} = move(z); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_bar_x100() { - label b0: - Self.calibrate_unpack_bar_impl(10); - return; - } - - public entry calibrate_unpack_bar_x500() { - label b0: - Self.calibrate_unpack_bar_impl(50); - return; - } - - public entry calibrate_unpack_bar_x1000() { - label b0: - Self.calibrate_unpack_bar_impl(100); - return; - } - - public calibrate_unpack_t8_impl(n: u64) { - let i: u64; - let t8: Self.T8; - let x7: Self.T7; - let x6: Self.T6; - let x5: Self.T5; - let x4: Self.T4; - let x3: Self.T3; - let x2: Self.T2; - let x1: Self.T1; - let x: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t8 = T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}; - T8{x7} = move(t8); - T7{x6} = move(x7); - T6{x5} = move(x6); - T5{x4} = move(x5); - T4{x3} = move(x4); - T3{x2} = move(x3); - T2{x1} = move(x2); - T1{x} = move(x1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_t8_x100() { - label b0: - Self.calibrate_unpack_t8_impl(10); - return; - } - - public entry calibrate_unpack_t8_x500() { - label b0: - Self.calibrate_unpack_t8_impl(50); - return; - } - - public entry calibrate_unpack_t8_x1000() { - label b0: - Self.calibrate_unpack_t8_impl(100); - return; - } - - public calibrate_unpack_t32_impl(n: u64) { - let i: u64; - let t31: Self.T31; - let x30: Self.T30; - let x29: Self.T29; - let x28: Self.T28; - let x27: Self.T27; - let x26: Self.T26; - let x25: Self.T25; - let x24: Self.T24; - let x23: Self.T23; - let x22: Self.T22; - let x21: Self.T21; - let x20: Self.T20; - let x19: Self.T19; - let x18: Self.T18; - let x17: Self.T17; - let x16: Self.T16; - let x15: Self.T15; - let x14: Self.T14; - let x13: Self.T13; - let x12: Self.T12; - let x11: Self.T11; - let x10: Self.T10; - let x9: Self.T9; - let x8: Self.T8; - let x7: Self.T7; - let x6: Self.T6; - let x5: Self.T5; - let x4: Self.T4; - let x3: Self.T3; - let x2: Self.T2; - let x1: Self.T1; - let x0: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - t31 = T31{x:T30{x:T29{x:T28{x:T27{x:T26{x:T25{x:T24{x:T23{x:T22{x:T21{x:T20{x:T19{x:T18{x:T17{x:T16{x:T15{x:T14{x:T13{x:T12{x:T11{x:T10{x:T9{x:T8{x:T7{x:T6{x:T5{x:T4{x:T3{x:T2{x:T1{x:0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}; - T31{x30} = move(t31); - T30{x29} = move(x30); - T29{x28} = move(x29); - T28{x27} = move(x28); - T27{x26} = move(x27); - T26{x25} = move(x26); - T25{x24} = move(x25); - T24{x23} = move(x24); - T23{x22} = move(x23); - T22{x21} = move(x22); - T21{x20} = move(x21); - T20{x19} = move(x20); - T19{x18} = move(x19); - T18{x17} = move(x18); - T17{x16} = move(x17); - T16{x15} = move(x16); - T15{x14} = move(x15); - T14{x13} = move(x14); - T13{x12} = move(x13); - T12{x11} = move(x12); - T11{x10} = move(x11); - T10{x9} = move(x10); - T9{x8} = move(x9); - T8{x7} = move(x8); - T7{x6} = move(x7); - T6{x5} = move(x6); - T5{x4} = move(x5); - T4{x3} = move(x4); - T3{x2} = move(x3); - T2{x1} = move(x2); - T1{x0} = move(x1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_unpack_t32_x100() { - label b0: - Self.calibrate_unpack_t32_impl(10); - return; - } - - public entry calibrate_unpack_t32_x500() { - label b0: - Self.calibrate_unpack_t32_impl(50); - return; - } - - public entry calibrate_unpack_t32_x1000() { - label b0: - Self.calibrate_unpack_t32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-generic.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-generic.mvir deleted file mode 100644 index 2af4918258c20..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-generic.mvir +++ /dev/null @@ -1,230 +0,0 @@ -module 0xcafe.VecGeneric { - struct D has copy, drop { x: u64 } - struct C has copy, drop { x: T, y: u64 } - struct E has copy, drop { x: T, y: u64 } - struct F has copy, drop { x: T, y: u64 } - struct G has copy, drop { x: T, y: u64 } - - public make(): Self.C { - let d: Self.D; - label b0: - d = D { x: 0 }; - return C { x: move(d), y: 0 }; - } - - public calibrate_vec_len_generic_impl(n: u64) { - let i: u64; - let c: Self.C; - let v: vector>; - label entry: - i = 0; - c = Self.make(); - v = vec_pack_1>(move(c)); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - Self.vec_len(&v); - jump loop_start; - label loop_end: - return; - } - - public calibrate_vec_len_generic_inlined_impl(n: u64) { - let i: u64; - let c: Self.C; - let v: vector>; - label entry: - i = 0; - c = Self.make(); - v = vec_pack_1>(move(c)); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - _ = vec_len>(&v); - jump loop_start; - label loop_end: - return; - } - - public vec_len(v: &vector>) { - label entry: - _ = vec_len>(move(v)); - return; - } - - public entry calibrate_vec_len_generic_1_x100() { - label b0: - Self.calibrate_vec_len_generic_impl(10); - return; - } - - public entry calibrate_vec_len_generic_1_x500() { - label b0: - Self.calibrate_vec_len_generic_impl(50); - return; - } - - public entry calibrate_vec_len_generic_1_x1000() { - label b0: - Self.calibrate_vec_len_generic_impl(100); - return; - } - - public entry calibrate_vec_len_generic_inlined_1_x100() { - label b0: - Self.calibrate_vec_len_generic_inlined_impl(10); - return; - } - - public entry calibrate_vec_len_generic_inlined_1_x500() { - label b0: - Self.calibrate_vec_len_generic_inlined_impl(50); - return; - } - - public entry calibrate_vec_len_generic_inlined_1_x1000() { - label b0: - Self.calibrate_vec_len_generic_inlined_impl(100); - return; - } - - public make_gen(v: T): Self.C { - label b0: - return C { x: move(v), y: 0 }; - } - - public calibrate_vec_len_generic_2_impl(n: u64, val: T) { - let i: u64; - let c: Self.C; - let v: vector>; - label entry: - i = 0; - c = Self.make_gen(move(val)); - v = vec_pack_1>(move(c)); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - Self.vec_len_gen(&v); - jump loop_start; - label loop_end: - return; - } - - public calibrate_vec_len_generic_inlined_2_impl(n: u64, val: T) { - let i: u64; - let c: Self.C; - let v: vector>; - label entry: - i = 0; - c = Self.make_gen(move(val)); - v = vec_pack_1>(move(c)); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - _ = vec_len>(&v); - jump loop_start; - label loop_end: - return; - } - - public vec_len_gen(v: &vector>) { - label entry: - _ = vec_len>(move(v)); - return; - } - - public entry calibrate_vec_len_generic_2_x100() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_2_impl(10, move(d)); - return; - } - - public entry calibrate_vec_len_generic_2_x500() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_2_impl(50, move(d)); - return; - } - - public entry calibrate_vec_len_generic_2_x1000() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_2_impl(100, move(d)); - return; - } - - public entry calibrate_vec_len_generic_inlined_2_x100() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_2_impl(10, move(d)); - return; - } - - public entry calibrate_vec_len_generic_inlined_2_x500() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_2_impl(50, move(d)); - return; - } - - public entry calibrate_vec_len_generic_inlined_2_x1000() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_2_impl(100, move(d)); - return; - } - - public calibrate_vec_len_generic_inlined_exterme_impl(n: u64, val: T) { - let i: u64; - let v: vector>>>>; - let g: Self.G; - let f: Self.F>; - let e: Self.E>>; - let c: Self.C>>>; - label entry: - i = 0; - g = G { x: move(val), y: 0}; - f = F> { x: move(g), y: 0 }; - e = E>> { x: move(f), y: 0 }; - c = C>>> { x: move(e), y: 0 }; - v = vec_pack_1>>>>(move(c)); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - _ = vec_len>>>>(&v); - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_len_generic_inlined_exterme_x100() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_exterme_impl(10, move(d)); - return; - } - - public entry calibrate_vec_len_generic_inlined_exterme_x500() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_exterme_impl(50, move(d)); - return; - } - - public entry calibrate_vec_len_generic_inlined_exterme_x1000() { - let d: Self.D; - label b0: - d = D { x: 0 }; - Self.calibrate_vec_len_generic_inlined_exterme_impl(100, move(d)); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-imm-borrow.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-imm-borrow.mvir deleted file mode 100644 index d1a0d4e0aad49..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-imm-borrow.mvir +++ /dev/null @@ -1,73 +0,0 @@ -module 0xcafe.VecImmBorrow { - - public calibrate_vec_imm_borrow_1_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_1(42); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_imm_borrow_1_x100() { - label b0: - Self.calibrate_vec_imm_borrow_1_impl(10); - return; - } - - public entry calibrate_vec_imm_borrow_1_x500() { - label b0: - Self.calibrate_vec_imm_borrow_1_impl(50); - return; - } - - public entry calibrate_vec_imm_borrow_1_x1000() { - label b0: - Self.calibrate_vec_imm_borrow_1_impl(100); - return; - } - - public calibrate_vec_imm_borrow_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_2(42, 43); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0), vec_imm_borrow(&v, 0)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_imm_borrow_2_x100() { - label b0: - Self.calibrate_vec_imm_borrow_2_impl(10); - return; - } - - public entry calibrate_vec_imm_borrow_2_x500() { - label b0: - Self.calibrate_vec_imm_borrow_2_impl(50); - return; - } - - public entry calibrate_vec_imm_borrow_2_x1000() { - label b0: - Self.calibrate_vec_imm_borrow_2_impl(100); - return; - } - -} diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-len.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-len.mvir deleted file mode 100644 index f1d0494d6ed0d..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-len.mvir +++ /dev/null @@ -1,178 +0,0 @@ -module 0xcafe.VecLen { - - public calibrate_veclen_0_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_0(); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_veclen_0_x100() { - label b0: - Self.calibrate_veclen_0_impl(10); - return; - } - - public entry calibrate_veclen_0_x500() { - label b0: - Self.calibrate_veclen_0_impl(50); - return; - } - - public entry calibrate_veclen_0_x1000() { - label b0: - Self.calibrate_veclen_0_impl(100); - return; - } - - public calibrate_veclen_1_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_1(42); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_veclen_1_x100() { - label b0: - Self.calibrate_veclen_1_impl(10); - return; - } - - public entry calibrate_veclen_1_x500() { - label b0: - Self.calibrate_veclen_1_impl(50); - return; - } - - public entry calibrate_veclen_1_x1000() { - label b0: - Self.calibrate_veclen_1_impl(100); - return; - } - - public calibrate_veclen_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_2(42, 43); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_veclen_2_x100() { - label b0: - Self.calibrate_veclen_2_impl(10); - return; - } - - public entry calibrate_veclen_2_x500() { - label b0: - Self.calibrate_veclen_2_impl(50); - return; - } - - public entry calibrate_veclen_2_x1000() { - label b0: - Self.calibrate_veclen_2_impl(100); - return; - } - - public calibrate_veclen_8_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_veclen_8_x100() { - label b0: - Self.calibrate_veclen_8_impl(10); - return; - } - - public entry calibrate_veclen_8_x500() { - label b0: - Self.calibrate_veclen_8_impl(50); - return; - } - - public entry calibrate_veclen_8_x1000() { - label b0: - Self.calibrate_veclen_8_impl(100); - return; - } - - public calibrate_veclen_32_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v), vec_len(&v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_veclen_32_x100() { - label b0: - Self.calibrate_veclen_32_impl(10); - return; - } - - public entry calibrate_veclen_32_x500() { - label b0: - Self.calibrate_veclen_32_impl(50); - return; - } - - public entry calibrate_veclen_32_x1000() { - label b0: - Self.calibrate_veclen_32_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-mut-borrow.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-mut-borrow.mvir deleted file mode 100644 index 662e5d7c235fa..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-mut-borrow.mvir +++ /dev/null @@ -1,135 +0,0 @@ -module 0xcafe.VecMutBorrow { - - public calibrate_vec_mut_borrow_1_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_1(42); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_mut_borrow_1_x100() { - label b0: - Self.calibrate_vec_mut_borrow_1_impl(10); - return; - } - - public entry calibrate_vec_mut_borrow_1_x500() { - label b0: - Self.calibrate_vec_mut_borrow_1_impl(50); - return; - } - - public entry calibrate_vec_mut_borrow_1_x1000() { - label b0: - Self.calibrate_vec_mut_borrow_1_impl(100); - return; - } - - public calibrate_vec_mut_borrow_10_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_10(40, 41, 42, 43, 44, 45, 46, 47, 48, 49); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 1); - _ = vec_mut_borrow(&mut v, 2); - _ = vec_mut_borrow(&mut v, 3); - _ = vec_mut_borrow(&mut v, 4); - _ = vec_mut_borrow(&mut v, 5); - _ = vec_mut_borrow(&mut v, 6); - _ = vec_mut_borrow(&mut v, 7); - _ = vec_mut_borrow(&mut v, 8); - _ = vec_mut_borrow(&mut v, 9); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_mut_borrow_10_x100() { - label b0: - Self.calibrate_vec_mut_borrow_10_impl(10); - return; - } - - public entry calibrate_vec_mut_borrow_10_x500() { - label b0: - Self.calibrate_vec_mut_borrow_10_impl(50); - return; - } - - public entry calibrate_vec_mut_borrow_10_x1000() { - label b0: - Self.calibrate_vec_mut_borrow_10_impl(100); - return; - } - - public calibrate_vec_mut_borrow_32_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _ = vec_mut_borrow(&mut v, 0); - _ = vec_mut_borrow(&mut v, 1); - _ = vec_mut_borrow(&mut v, 2); - _ = vec_mut_borrow(&mut v, 3); - _ = vec_mut_borrow(&mut v, 4); - _ = vec_mut_borrow(&mut v, 5); - _ = vec_mut_borrow(&mut v, 6); - _ = vec_mut_borrow(&mut v, 7); - _ = vec_mut_borrow(&mut v, 8); - _ = vec_mut_borrow(&mut v, 9); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_mut_borrow_32_x100() { - label b0: - Self.calibrate_vec_mut_borrow_32_impl(10); - return; - } - - public entry calibrate_vec_mut_borrow_32_x500() { - label b0: - Self.calibrate_vec_mut_borrow_32_impl(50); - return; - } - - public entry calibrate_vec_mut_borrow_32_x1000() { - label b0: - Self.calibrate_vec_mut_borrow_32_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pack.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pack.mvir deleted file mode 100644 index be37a2cca7e8e..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pack.mvir +++ /dev/null @@ -1,168 +0,0 @@ -module 0xcafe.VecPack { - - public calibrate_vecpack_0_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0(), vec_pack_0()); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpack_0_x100() { - label b0: - Self.calibrate_vecpack_0_impl(10); - return; - } - - public entry calibrate_vecpack_0_x500() { - label b0: - Self.calibrate_vecpack_0_impl(50); - return; - } - - public entry calibrate_vecpack_0_x1000() { - label b0: - Self.calibrate_vecpack_0_impl(100); - return; - } - - public calibrate_vecpack_1_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42), vec_pack_1(42)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpack_1_x100() { - label b0: - Self.calibrate_vecpack_1_impl(10); - return; - } - - public entry calibrate_vecpack_1_x500() { - label b0: - Self.calibrate_vecpack_1_impl(50); - return; - } - - public entry calibrate_vecpack_1_x1000() { - label b0: - Self.calibrate_vecpack_1_impl(100); - return; - } - - public calibrate_vecpack_2_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43), vec_pack_2(42, 43)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpack_2_x100() { - label b0: - Self.calibrate_vecpack_2_impl(10); - return; - } - - public entry calibrate_vecpack_2_x500() { - label b0: - Self.calibrate_vecpack_2_impl(50); - return; - } - - public entry calibrate_vecpack_2_x1000() { - label b0: - Self.calibrate_vecpack_2_impl(100); - return; - } - - public calibrate_vecpack_8_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47), vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpack_8_x100() { - label b0: - Self.calibrate_vecpack_8_impl(10); - return; - } - - public entry calibrate_vecpack_8_x500() { - label b0: - Self.calibrate_vecpack_8_impl(50); - return; - } - - public entry calibrate_vecpack_8_x1000() { - label b0: - Self.calibrate_vecpack_8_impl(100); - return; - } - - public calibrate_vecpack_32_impl(n: u64) { - let i: u64; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - _, _, _, _, _, _, _, _, _, _ = (vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31), vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpack_32_x100() { - label b0: - Self.calibrate_vecpack_32_impl(10); - return; - } - - public entry calibrate_vecpack_32_x500() { - label b0: - Self.calibrate_vecpack_32_impl(50); - return; - } - - public entry calibrate_vecpack_32_x1000() { - label b0: - Self.calibrate_vecpack_32_impl(100); - return; - } - -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pop-back.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pop-back.mvir deleted file mode 100644 index 52c52b935723f..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-pop-back.mvir +++ /dev/null @@ -1,146 +0,0 @@ -module 0xcafe.VecPop { - - public calibrate_vec_pop_back_1_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_1(42); - _ = vec_pop_back(&mut v); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_pop_back_1_x100() { - label b0: - Self.calibrate_vec_pop_back_1_impl(10); - return; - } - - public entry calibrate_vec_pop_back_1_x500() { - label b0: - Self.calibrate_vec_pop_back_1_impl(50); - return; - } - - public entry calibrate_vec_pop_back_1_x1000() { - label b0: - Self.calibrate_vec_pop_back_1_impl(100); - return; - } - - public calibrate_vec_pop_back_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_2(42, 43); - _ = vec_pop_back(&mut v); - _ = vec_pop_back(&mut v); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_pop_back_2_x100() { - label b0: - Self.calibrate_vec_pop_back_2_impl(10); - return; - } - - public entry calibrate_vec_pop_back_2_x500() { - label b0: - Self.calibrate_vec_pop_back_2_impl(50); - return; - } - - public entry calibrate_vec_pop_back_2_x1000() { - label b0: - Self.calibrate_vec_pop_back_2_impl(100); - return; - } - - public calibrate_vec_pop_back_8_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47); - _, _, _, _, _, _, _, _ = (vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_pop_back_8_x100() { - label b0: - Self.calibrate_vec_pop_back_8_impl(10); - return; - } - - public entry calibrate_vec_pop_back_8_x500() { - label b0: - Self.calibrate_vec_pop_back_8_impl(50); - return; - } - - public entry calibrate_vec_pop_back_8_x1000() { - label b0: - Self.calibrate_vec_pop_back_8_impl(100); - return; - } - - public calibrate_vec_pop_back_32_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); - _, _, _, _, _, _, _, _ = (vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v)); - _, _, _, _, _, _, _, _ = (vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v)); - _, _, _, _, _, _, _, _ = (vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v)); - _, _, _, _, _, _, _, _ = (vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v), vec_pop_back(&mut v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_pop_back_32_x100() { - label b0: - Self.calibrate_vec_pop_back_32_impl(10); - return; - } - - public entry calibrate_vec_pop_back_32_x500() { - label b0: - Self.calibrate_vec_pop_back_32_impl(50); - return; - } - - public entry calibrate_vec_pop_back_32_x1000() { - label b0: - Self.calibrate_vec_pop_back_32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-push-back.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-push-back.mvir deleted file mode 100644 index f7136ae3caed6..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-push-back.mvir +++ /dev/null @@ -1,134 +0,0 @@ -module 0xcafe.VecPushBack { - - public calibrate_vecpushback_0_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_0(); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpushback_0_x100() { - label b0: - Self.calibrate_vecpushback_0_impl(10); - return; - } - - public entry calibrate_vecpushback_0_x500() { - label b0: - Self.calibrate_vecpushback_0_impl(50); - return; - } - - public entry calibrate_vecpushback_0_x1000() { - label b0: - Self.calibrate_vecpushback_0_impl(100); - return; - } - - public calibrate_vecpushback_1_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_1(42); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpushback_1_x100() { - label b0: - Self.calibrate_vecpushback_1_impl(10); - return; - } - - public entry calibrate_vecpushback_1_x500() { - label b0: - Self.calibrate_vecpushback_1_impl(50); - return; - } - - public entry calibrate_vecpushback_1_x1000() { - label b0: - Self.calibrate_vecpushback_1_impl(100); - return; - } - - public calibrate_vecpushback_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_2(42, 43); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - vec_push_back(&mut v, 0); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vecpushback_2_x100() { - label b0: - Self.calibrate_vecpushback_2_impl(10); - return; - } - - public entry calibrate_vecpushback_2_x500() { - label b0: - Self.calibrate_vecpushback_2_impl(50); - return; - } - - public entry calibrate_vecpushback_2_x1000() { - label b0: - Self.calibrate_vecpushback_2_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-swap.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-swap.mvir deleted file mode 100644 index 7a8f9fbc151cf..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-swap.mvir +++ /dev/null @@ -1,133 +0,0 @@ -module 0xcafe.VecSwap { - public calibrate_vec_swap_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_2(42, 43); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_swap_2_x100() { - label b0: - Self.calibrate_vec_swap_2_impl(10); - return; - } - - public entry calibrate_vec_swap_2_x500() { - label b0: - Self.calibrate_vec_swap_2_impl(50); - return; - } - - public entry calibrate_vec_swap_2_x1000() { - label b0: - Self.calibrate_vec_swap_2_impl(100); - return; - } - - public calibrate_vec_swap_10_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_10(40, 41, 42, 43, 44, 45, 46, 47, 48, 49); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 3, 4); - vec_swap(&mut v, 3, 4); - vec_swap(&mut v, 3, 4); - vec_swap(&mut v, 8, 9); - vec_swap(&mut v, 8, 9); - vec_swap(&mut v, 8, 9); - vec_swap(&mut v, 8, 9); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_swap_10_x100() { - label b0: - Self.calibrate_vec_swap_10_impl(10); - return; - } - - public entry calibrate_vec_swap_10_x500() { - label b0: - Self.calibrate_vec_swap_10_impl(50); - return; - } - - public entry calibrate_vec_swap_10_x1000() { - label b0: - Self.calibrate_vec_swap_10_impl(100); - return; - } - - public calibrate_vec_swap_32_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - v = vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 0, 1); - vec_swap(&mut v, 10, 11); - vec_swap(&mut v, 10, 11); - vec_swap(&mut v, 20, 21); - vec_swap(&mut v, 20, 21); - vec_swap(&mut v, 28, 29); - vec_swap(&mut v, 28, 29); - vec_swap(&mut v, 28, 29); - vec_swap(&mut v, 28, 29); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_swap_32_x100() { - label b0: - Self.calibrate_vec_swap_32_impl(10); - return; - } - - public entry calibrate_vec_swap_32_x500() { - label b0: - Self.calibrate_vec_swap_32_impl(50); - return; - } - - public entry calibrate_vec_swap_32_x1000() { - label b0: - Self.calibrate_vec_swap_32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-unpack.mvir b/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-unpack.mvir deleted file mode 100644 index fe81426dc5d3d..0000000000000 --- a/aptos-move/aptos-gas-calibration/samples_ir/vector/vec-unpack.mvir +++ /dev/null @@ -1,141 +0,0 @@ -module 0xcafe.VecUnpack { - public calibrate_vec_unpack_0_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_0(); - vec_unpack_0(move(v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_unpack_0_x100() { - label b0: - Self.calibrate_vec_unpack_0_impl(10); - return; - } - - public entry calibrate_vec_unpack_0_x500() { - label b0: - Self.calibrate_vec_unpack_0_impl(50); - return; - } - - public entry calibrate_vec_unpack_0_x1000() { - label b0: - Self.calibrate_vec_unpack_0_impl(100); - return; - } - - public calibrate_vec_unpack_2_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_2(42, 43); - _, _ = vec_unpack_2(move(v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_unpack_2_x100() { - label b0: - Self.calibrate_vec_unpack_2_impl(10); - return; - } - - public entry calibrate_vec_unpack_2_x500() { - label b0: - Self.calibrate_vec_unpack_2_impl(50); - return; - } - - public entry calibrate_vec_unpack_2_x1000() { - label b0: - Self.calibrate_vec_unpack_2_impl(100); - return; - } - - public calibrate_vec_unpack_8_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_8(40, 41, 42, 43, 44, 45, 46, 47); - _, _, _, _, _, _, _, _ = vec_unpack_8(move(v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_unpack_8_x100() { - label b0: - Self.calibrate_vec_unpack_8_impl(10); - return; - } - - public entry calibrate_vec_unpack_8_x500() { - label b0: - Self.calibrate_vec_unpack_8_impl(50); - return; - } - - public entry calibrate_vec_unpack_8_x1000() { - label b0: - Self.calibrate_vec_unpack_8_impl(100); - return; - } - - public calibrate_vec_unpack_32_impl(n: u64) { - let i: u64; - let v: vector; - label entry: - i = 0; - label loop_start: - jump_if_false (copy(i) < copy(n)) loop_end; - i = move(i) + 1; - - v = vec_pack_32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ = vec_unpack_32(move(v)); - - jump loop_start; - label loop_end: - return; - } - - public entry calibrate_vec_unpack_32_x100() { - label b0: - Self.calibrate_vec_unpack_32_impl(10); - return; - } - - public entry calibrate_vec_unpack_32_x500() { - label b0: - Self.calibrate_vec_unpack_32_impl(50); - return; - } - - public entry calibrate_vec_unpack_32_x1000() { - label b0: - Self.calibrate_vec_unpack_32_impl(100); - return; - } -} \ No newline at end of file diff --git a/aptos-move/aptos-gas-calibration/src/main.rs b/aptos-move/aptos-gas-calibration/src/main.rs deleted file mode 100644 index bef5bb950d553..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod math; -mod math_interface; -mod measurements; -mod measurements_helpers; -mod solve; -use aptos_abstract_gas_usage::{aggregate_terms, expand_terms}; -use aptos_gas_algebra::DynamicExpression; -use clap::Parser; -use math_interface::{convert_to_matrix_format, total_num_of_cols, total_num_rows}; -use measurements::compile_and_run; -use solve::{build_coefficient_matrix, build_constant_matrix, least_squares}; -use std::collections::BTreeMap; - -/// Automated Gas Calibration to calibrate Move bytecode and Native Functions -#[derive(Parser, Debug)] -struct Args { - /// Specific Calibration Function tests to run that match a given pattern - #[clap(short, long, default_value = "")] - pattern: String, - - /// Number of iterations to run each Calibration Function - #[clap(short, long, default_value_t = 20)] - iterations: u64, - - /// Maximum execution time in milliseconds - #[clap(short, long, default_value_t = 300)] - max_execution_time: u64, -} - -fn main() { - // Implement CLI - let args = Args::parse(); - let pattern = &args.pattern; - let iterations = args.iterations; - let max_execution_time = args.max_execution_time; - - println!( - "Running each Calibration Function for {} iterations\n", - iterations - ); - - println!("Calibrating Gas Parameters ...\n"); - - let measurements = compile_and_run(iterations, pattern); - - let mut system_of_equations: Vec> = Vec::new(); - for formula in measurements.abstract_meter { - let mut terms: Vec = Vec::new(); - for term in formula { - let normal = expand_terms(term); - terms.extend(normal); - } - system_of_equations.push(terms); - } - - // Collect like terms - let mut mappings: Vec> = Vec::new(); - for equation in system_of_equations { - let map = aggregate_terms(equation) - .expect("Failed: Should not have concrete quantities in gas formulae."); - mappings.push(map); - } - - // Convert simplified map to a math friendly interface - let vec_format: Vec> = convert_to_matrix_format(mappings.clone()); - - // Build the system of linear equations using the math library - let nrows = total_num_rows(mappings.clone()); - let ncols = total_num_of_cols(mappings.clone()); - let vec_col: usize = 1; - - let mut coeff_matrix = build_coefficient_matrix(vec_format, nrows, ncols); - let mut const_matrix = build_constant_matrix(measurements.regular_meter, nrows, vec_col); - - // Solve the system of linear equations - least_squares( - mappings, - &mut coeff_matrix, - &mut const_matrix, - measurements.equation_names, - max_execution_time, - ); -} diff --git a/aptos-move/aptos-gas-calibration/src/math.rs b/aptos-move/aptos-gas-calibration/src/math.rs deleted file mode 100644 index bc336d6b71a24..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/math.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use float_cmp::approx_eq; -use nalgebra::{self, DMatrix}; -use std::ops::{Div, Mul}; - -const MARGIN_OF_ERROR: f64 = 0.2; - -/// Add a gas formula to the coefficient matrix -/// -/// ### Arguments -/// -/// * `idx` - Keeps track of which row to edit -/// * `ncols` - Max number of columns -/// * `formula` - The gas formula to add -/// * `coefficient_matrix` - The Matrix we want to edit -pub fn add_gas_formula_to_coefficient_matrix( - idx: usize, - ncols: usize, - formula: &[f64], - coefficient_matrix: &mut DMatrix, -) { - for j in 0..ncols { - coefficient_matrix[(idx, j)] = formula[j]; - } -} - -/// Add the running time corresponding to the gas formula to -/// constant matrix. -/// -/// ### Arguments -/// -/// * `idx` - Keeps track of which row to edit -/// * `running_time` - The running time w.r.t the gas formula -/// * `constant_matrix` - The Matrix we want to edit -pub fn add_running_time_to_constant_matrix( - idx: usize, - running_time: f64, - constant_matrix: &mut DMatrix, -) { - constant_matrix[(idx, 0)] = running_time; -} - -/// Join coefficient and constant matrix to make augmented -/// -/// ### Arguments -/// -/// * `augmented_matrix` - Matrix to join coefficient and constant -/// * `coefficient_matrix` - Matrix for gas formula -/// * `constant_matrix` - Matrix for running time w.r.t gas formula -fn create_augmented_matrix( - augmented_matrix: &mut DMatrix, - coefficient_matrix: &mut DMatrix, - constant_matrix: &mut DMatrix, -) { - let nrows = augmented_matrix.nrows(); - let ncols = coefficient_matrix.ncols(); - for i in 0..nrows { - for j in 0..ncols { - augmented_matrix[(i, j)] = coefficient_matrix[(i, j)]; - } - } - - for i in 0..nrows { - augmented_matrix[(i, ncols)] = constant_matrix[(i, 0)]; - } -} - -/// Compute least squares -/// -/// ### Arguments -/// -/// * `A` - Coefficient matrix -/// * `b` - Constant matrix -#[allow(non_snake_case)] -pub fn compute_least_square_solutions( - A: &mut DMatrix, - b: &mut DMatrix, -) -> Result, String> { - let A_T = A.transpose(); - let A_TA = A_T.clone().mul(A.clone()); - let A_Tb = A_T.clone().mul(b.clone()); - - if !A_TA.is_invertible() { - return Err("cannot invert A_TA matrix".to_string()); - } - - let inverse = A_TA.try_inverse().expect("inverse should work"); - let x_hat = inverse.mul(&A_Tb); - Ok(x_hat) -} - -/// Find all free variables / linear dependent combinations -/// -/// ### Arguments -/// -/// * `A` - Coefficient matrix -/// * `b` - Constant matrix -#[allow(non_snake_case)] -pub fn find_linearly_dependent_variables( - A: &mut DMatrix, - b: &mut DMatrix, - gas_params: Vec, -) -> Result, Vec> { - let mut aug_matrix = DMatrix::::zeros(A.nrows(), A.ncols() + 1); - create_augmented_matrix(&mut aug_matrix, A, b); - rref(&mut aug_matrix); - - let linearly_independent = find_linear_independent_variables(&mut aug_matrix); - let mut linearly_dependent = Vec::new(); - for (idx, gas_param) in gas_params.into_iter().enumerate() { - if !linearly_independent.contains(&idx) { - linearly_dependent.push(gas_param) - } - } - Ok(linearly_dependent) -} - -/// We use the Least Squares solution to input into the LHS to get what we -/// call as the Computed Time. We compare this against the LHS (the Actual Time) and -/// check if it it varies by a certain amount. -/// -/// ### Arguments -/// -/// * `x_hat` - Solutions to the Least Squares -/// * `coefficient_matrix` - Matrix of linear equations -/// * `constant_matrix` - Matrix of Actual Time -pub fn get_computed_time_and_outliers( - x_hat: &mut DMatrix, - coefficient_matrix: &mut DMatrix, - constant_matrix: &mut DMatrix, -) -> Result, String> { - // get computed running time - let mut computed_running_time: Vec = Vec::new(); - let coeff_row = coefficient_matrix.nrows(); - let coeff_col = coefficient_matrix.ncols(); - for i in 0..coeff_row { - let mut total_time: f64 = 0.0; - for j in 0..coeff_col { - let a_ij = coefficient_matrix[(i, j)]; - total_time += a_ij * x_hat[(j, 0)]; - } - computed_running_time.push(total_time); - } - - // compare w/ margin of error - let mut outliers: Vec<(usize, f64, f64, f64, bool)> = Vec::new(); - let const_row = constant_matrix.nrows(); - for i in 0..const_row { - let a_ij = constant_matrix[(i, 0)]; - - let numerator = (a_ij - computed_running_time[i]).abs(); - let denominator = computed_running_time[i]; - if approx_eq!(f64, denominator, 0.0, ulps = 2) { - return Err(String::from("Division by zero")); - } else { - let diff = numerator.div(denominator); - if diff > MARGIN_OF_ERROR { - // append equation that is an outlier - outliers.push((i, computed_running_time[i], a_ij, diff, true)); - } else { - // not outlier, but still display to the user - // the running time of that Calibration Function - outliers.push((i, computed_running_time[i], a_ij, diff, false)); - } - } - } - - Ok(outliers) -} - -/// Reduced row echelon form (RREF) with partial pivoting -/// -/// ### Arguments -/// -/// * `matrix` - A matrix to perform RREF -fn rref(matrix: &mut DMatrix) { - let (nrows, ncols) = matrix.shape(); - let mut lead = 0; - - for r in 0..nrows { - if ncols <= lead { - break; - } - - let mut i = r; - - while matrix[(i, lead)] == 0.0 { - i += 1; - - if nrows == i { - i = r; - lead += 1; - - if ncols == lead { - return; - } - } - } - - if i != r { - matrix.swap_rows(i, r); - } - - let pivot = matrix[(r, lead)]; - - for j in 0..ncols { - matrix[(r, j)] /= pivot; - } - - for i in 0..nrows { - if i != r { - let factor = matrix[(i, lead)]; - for j in 0..ncols { - matrix[(i, j)] -= factor * matrix[(r, j)]; - } - } - } - - lead += 1; - } -} - -/// If the matrix is not invertible, and there exists a row that has more than one -/// approximate value of 1, then we should report all linear combinations that exist -/// to the user by scanning each row and only reporting those rows. -/// -/// Source: https://stackoverflow.com/questions/43619121/how-to-find-partial-solutions-in-a-underdetermined-system-of-linear-equations -/// -/// ### Arguments -/// -/// * `matrix` - Augmented matrix that has been RREF'd -fn find_linear_independent_variables(matrix: &mut DMatrix) -> Vec { - let mut linear_combos = Vec::new(); - - for i in 0..matrix.nrows() { - let mut max_val: f64 = 0.0; - for k in 0..(matrix.ncols() - 1) { - let a_ik = matrix[(i, k)]; - if a_ik > max_val { - max_val = a_ik; - } - } - - if approx_eq!(f64, max_val, 0.0, ulps = 2) { - // ignore this row - continue; - } - - let mut independent_vars = Vec::new(); - for j in 0..(matrix.ncols() - 1) { - let a_ij = matrix[(i, j)]; - let ratio = a_ij / max_val; - // if each element / max_val is approximately not 0 - if !approx_eq!(f64, ratio, 0.0, ulps = 2) { - independent_vars.push((i, j)); - } - } - - if independent_vars.len() == 1 { - // mark all linear combinations as undetermined - for (_, gas_param) in independent_vars { - linear_combos.push(gas_param); - } - } - } - - linear_combos -} diff --git a/aptos-move/aptos-gas-calibration/src/math_interface.rs b/aptos-move/aptos-gas-calibration/src/math_interface.rs deleted file mode 100644 index a33e2989585fe..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/math_interface.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::BTreeMap; - -const DEFAULT_VALUE: f64 = 0.0; - -/// Getter function for number of rows -/// -/// ### Arguments -/// -/// * `input` - All the equations -pub fn total_num_rows(input: Vec>) -> usize { - input.len() -} - -/// Taking the union finds the total distinct gas parameters -/// -/// ### Arguments -/// -/// * `input` - All the equations in the simplified mapping version -pub fn total_num_of_cols(input: Vec>) -> usize { - let mut union_btreemap: BTreeMap = BTreeMap::new(); - for map in input { - union_btreemap.extend(map); - } - union_btreemap.len() -} - -/// Creates a generic template for BTreeMaps so we can easily -/// translate the entries into indices of our vector representation -/// -/// ### Arguments -/// -/// * `input` - All the equations in the simplified mapping version -pub fn generic_map(input: Vec>) -> BTreeMap { - let mut union_btreemap: BTreeMap = BTreeMap::new(); - for map in input { - union_btreemap.extend(map); - } - - // sort BTree lexicographically - let sorted_map: BTreeMap = union_btreemap.into_iter().collect(); - - let keys: Vec = sorted_map.keys().map(|key| key.to_string()).collect(); - let generic: BTreeMap = keys.into_iter().map(|key| (key, DEFAULT_VALUE)).collect(); - generic -} - -/// Standardize all maps into a generic map -/// -/// ### Arguments -/// -/// * `input` - All the equations in the simplified mapping version -pub fn convert_to_generic_map(input: Vec>) -> Vec> { - let mut generic_maps: Vec> = Vec::new(); - for map in &input { - let mut generic = generic_map(input.clone()); - for (key, value) in map.iter() { - generic - .entry(key.to_string()) - .and_modify(|v| *v = *value as f64); - } - generic_maps.push(generic); - } - generic_maps -} - -/// Transform into standardized mapping before converting to a vector -/// -/// ### Arguments -/// -/// * `input` - All the equations in the simplified mapping version -pub fn convert_to_matrix_format(input: Vec>) -> Vec> { - let ncols = total_num_of_cols(input.clone()); - let mut result: Vec> = Vec::new(); - let generic_maps = convert_to_generic_map(input); - //println!("KEYS {:?}\n", generic_maps); - for eq in generic_maps { - let vec_format: Vec = eq.values().cloned().collect(); - assert_eq!(vec_format.len(), ncols); - result.push(vec_format); - } - - result -} diff --git a/aptos-move/aptos-gas-calibration/src/measurements.rs b/aptos-move/aptos-gas-calibration/src/measurements.rs deleted file mode 100644 index fa0257ce29a0c..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/measurements.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 -use crate::measurements_helpers::{get_dir_paths, list_entrypoints, record_gas_usage}; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_gas_algebra::DynamicExpression; -use aptos_language_e2e_tests::executor::{ExecFuncTimerDynamicArgs, FakeExecutor, GasMeterType}; -use move_binary_format::CompiledModule; -use move_ir_compiler::Compiler; -use std::{ - fs::{read_dir, read_to_string}, - path::PathBuf, -}; -use walkdir::WalkDir; - -pub struct GasMeasurements { - pub regular_meter: Vec, - pub abstract_meter: Vec>, - pub equation_names: Vec, -} - -/// Compile and run both samples and samples_ir directories -pub fn compile_and_run(iterations: u64, pattern: &String) -> GasMeasurements { - let executor = FakeExecutor::from_head_genesis(); - let mut executor = executor.set_not_parallel(); - - let mut gas_measurement = GasMeasurements { - regular_meter: Vec::new(), - abstract_meter: Vec::new(), - equation_names: Vec::new(), - }; - - compile_and_run_samples(iterations, pattern, &mut gas_measurement, &mut executor); - compile_and_run_samples_ir(iterations, pattern, &mut gas_measurement, &mut executor); - - gas_measurement -} - -/// Compile every Move sample and run each sample with two different measuring methods. -/// The first is with the Regular Gas Meter (used in production) to record the running time. -/// The second is with the Abstract Algebra Gas Meter to record abstract gas usage. -fn compile_and_run_samples( - iterations: u64, - pattern: &String, - gas_measurement: &mut GasMeasurements, - executor: &mut FakeExecutor, -) { - // Discover all top-level packages in samples directory - let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("samples"); - let dirs = read_dir(path.as_path()).unwrap(); - let dir_paths = get_dir_paths(dirs); - - // Go over all Move projects - for dir_path in dir_paths { - // configure and build Move package - let build_options = BuildOptions { - with_srcs: true, - with_abis: true, - with_source_maps: true, - with_error_map: true, - ..BuildOptions::default() - }; - let package = - BuiltPackage::build(dir_path, build_options).expect("Failed to build package"); - - // iterate over all Move package code - let codes = package.extract_code(); - for code in codes { - let compiled_module = CompiledModule::deserialize(&code).unwrap(); - let module_id = compiled_module.self_id(); - let identifier = &module_id.name().to_string(); - - // get module address - let address = module_id.address(); - - // get all benchmark tagged functions - let func_identifiers = list_entrypoints(&compiled_module, pattern.to_string()).expect( - "Failed: entry function probably has >1 parameter that's not a Signer type", - ); - - let measurement_results = record_gas_usage( - &package, - executor, - func_identifiers, - *address, - identifier, - iterations, - ); - - // record the equation names - gas_measurement - .equation_names - .extend(measurement_results.equation_names); - - // record with regular gas meter - gas_measurement - .regular_meter - .extend(measurement_results.regular_meter); - - // record with abstract gas meter - gas_measurement - .abstract_meter - .extend(measurement_results.abstract_meter); - } - } -} - -/// Compile every MVIR and run each sample with two different measuring methods. -/// The first is with the Regular Gas Meter (used in production) to record the running time. -/// The second is with the Abstract Algebra Gas Meter to record abstract gas usage. -fn compile_and_run_samples_ir( - iterations: u64, - pattern: &String, - gas_measurement: &mut GasMeasurements, - executor: &mut FakeExecutor, -) { - let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("samples_ir"); - - // Walk through all subdirectories and files in the root directory - for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { - let path_entry = entry.path(); - - // Check if the entry is a file - if path_entry.is_file() { - // ignore all non .mvir files - if let Some(file_ext) = path_entry.extension() { - if let Some(ext) = file_ext.to_str() { - if ext != "mvir" { - continue; - } - } - } - - if let Some(file_name) = path_entry.file_name() { - // Convert the file_name to a string slice - if let Some(_file_name_str) = file_name.to_str() { - // compile module - let code = read_to_string(path_entry).expect("Failed to read file contents"); - let module = Compiler::new(vec![]) - .into_compiled_module(&code) - .expect("should compile mvir"); - - // get relevant module metadata - let module_id = module.self_id(); - let identifier = &module_id.name().to_string(); - let func_identifiers = list_entrypoints(&module, pattern.to_string()).expect( - "Failed: entry function probably has >1 parameter that's not a Signer type", - ); - - // build .mv of module - let mut module_blob: Vec = vec![]; - module - .serialize(&mut module_blob) - .expect("Failed to serialize module"); - - // publish module - executor.add_module(&module_id, module_blob); - - for func_identifier in func_identifiers { - println!("Benchmarking {}::{}\n", &identifier, func_identifier.0); - - gas_measurement - .equation_names - .push(format!("{}::{}", &identifier, func_identifier.0)); - - let elapsed = executor.exec_func_record_running_time( - &module_id, - &func_identifier.0, - vec![], - func_identifier.1.clone(), - iterations, - ExecFuncTimerDynamicArgs::NoArgs, - GasMeterType::UnmeteredGasMeter, - ); - gas_measurement.regular_meter.push(elapsed); - - // record with abstract gas meter - let gas_formula = executor.exec_abstract_usage( - &module_id, - &func_identifier.0, - vec![], - func_identifier.1, - ); - gas_measurement.abstract_meter.push(gas_formula); - } - } - } - } - } -} diff --git a/aptos-move/aptos-gas-calibration/src/measurements_helpers.rs b/aptos-move/aptos-gas-calibration/src/measurements_helpers.rs deleted file mode 100644 index 3f242b4457d4e..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/measurements_helpers.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::measurements::GasMeasurements; -use anyhow::{anyhow, Result}; -use aptos_cached_packages::aptos_stdlib; -use aptos_framework::BuiltPackage; -use aptos_language_e2e_tests::{ - account::Account, - executor::{ExecFuncTimerDynamicArgs, FakeExecutor, GasMeterType}, -}; -use aptos_types::transaction::TransactionPayload; -use move_binary_format::{file_format::SignatureToken, CompiledModule}; -use move_core_types::{ - account_address::AccountAddress, - identifier::Identifier, - language_storage::ModuleId, - value::{serialize_values, MoveValue}, -}; -use std::{fs::ReadDir, path::PathBuf, string::String, time::Instant}; - -// CONSTANTS -const PREFIX: &str = "calibrate_"; - -/// Generate a TransactionPayload for modules -/// -/// ### Arguments -/// -/// * `package` - Built Move package -pub fn generate_module_payload(package: &BuiltPackage) -> TransactionPayload { - // extract package data - let code = package.extract_code(); - let metadata = package - .extract_metadata() - .expect("extracting package metadata must succeed"); - - // publish package similar to create_publish_package in harness.rs - aptos_stdlib::code_publish_package_txn( - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - code, - ) -} - -/// Sign transaction to create a Module and return transaction status -/// -/// ### Arguments -/// -/// * `executor` - Runs transactions -/// * `account` - Account to publish module under -/// * `payload` - Info relating to the module -/// * `sequence_number` - Nonce -pub fn execute_module_txn( - executor: &mut FakeExecutor, - account: &Account, - payload: TransactionPayload, - sequence_number: u64, -) { - // build and sign transaction - let sign_tx = account - .transaction() - .sequence_number(sequence_number) - .max_gas_amount(2_000_000) - .gas_unit_price(200) - .payload(payload) - .sign(); - - // Restart timer and sequence counter for each new package - // only count running time of entry function - let start = Instant::now(); - let txn_output = executor.execute_transaction(sign_tx); - - // apply write set to avoid LINKER_ERROR - executor.apply_write_set(txn_output.write_set()); - let elapsed = start.elapsed(); - println!("running time (microseconds): {}", elapsed.as_micros()); - - // validate successful transaction - let txn_status = txn_output.status().to_owned(); - assert!(txn_output.status().status().unwrap().is_success()); - println!("txn status: {:?}", txn_status); -} - -/// execute user transaction and the time only records the body of the transaction -/// -/// ### Arguments -/// -/// * `executor` - Runs transactions -/// * `module_name` - Name of module -/// * `function_name` - Name of function in the module -pub fn execute_user_txn( - executor: &mut FakeExecutor, - module_name: &ModuleId, - function_name: &str, - iterations: u64, - args: Vec>, -) -> u128 { - let elapsed = executor.exec_func_record_running_time( - module_name, - function_name, - vec![], - args, - iterations, - ExecFuncTimerDynamicArgs::NoArgs, - GasMeterType::UnmeteredGasMeter, - ); - println!("running time (microseconds): {}", elapsed); - elapsed -} - -/// Publish module under user, sign and run user transaction. -/// This runs for both Gas Meters (regular and abstract). -/// -/// ### Arguments -/// -/// * `package` - Built Move package -/// * `executor` - Runs transactions -/// * `func_identifiers` - All function names -/// * `address` - Address associated to an account -/// * `identifier` - Name of module -pub fn record_gas_usage( - package: &BuiltPackage, - executor: &mut FakeExecutor, - func_identifiers: Vec<(String, Vec>)>, - address: AccountAddress, - identifier: &String, - iterations: u64, -) -> GasMeasurements { - // publish test-package under module address - let creator = executor.new_account_at(address); - - let mut gas_measurement = GasMeasurements { - regular_meter: Vec::new(), - abstract_meter: Vec::new(), - equation_names: Vec::new(), - }; - - // iterate over all the functions that satisfied the requirements above - for (sequence_num_counter, func_identifier) in func_identifiers.into_iter().enumerate() { - println!( - "Executing {}::{}::{}", - address, - identifier, - func_identifier.0.clone(), - ); - gas_measurement - .equation_names - .push(format!("{}::{}", &identifier, &func_identifier.0)); - - // publish package similar to create_publish_package in harness.rs - println!("Signing txn for module... "); - let module_payload = generate_module_payload(package); - let counter = sequence_num_counter.try_into().unwrap(); - execute_module_txn(executor, &creator, module_payload, counter); - - // send a txn that invokes the entry function 0x{address}::{name}::benchmark - println!("Signing and running user txn for Regular Meter... "); - let module_name = get_module_name(address, identifier, &func_identifier.0); - let duration = execute_user_txn( - executor, - &module_name, - &func_identifier.0, - iterations, - func_identifier.1.clone(), - ); - gas_measurement.regular_meter.push(duration); - - println!("Signing and running user txn for Abstract Meter... "); - let gas_formula = executor.exec_abstract_usage( - &module_name, - &func_identifier.0, - vec![], - func_identifier.1, - ); - gas_measurement.abstract_meter.push(gas_formula); - } - - gas_measurement -} - -/* - * - * GETTER FUNCTIONS - * - */ -/// get module name -/// -/// ### Arguments -/// -/// * `address` - Address associated to an account -/// * `identifier` - Name of module -/// * `func_identifier` - Name of function in module -pub fn get_module_name( - address: AccountAddress, - identifier: &str, - _func_identifier: &str, -) -> ModuleId { - ModuleId::new(address, Identifier::new(identifier).unwrap()) -} - -/// get all directories of Move projects -/// -/// ### Arguments -/// -/// * `dirs` - directory -pub fn get_dir_paths(dirs: ReadDir) -> Vec { - let mut dir_paths = Vec::new(); - for dir in dirs { - // validate path is directory - let entry = dir.unwrap(); - if !entry.path().is_dir() { - continue; - } - dir_paths.push(entry.path()); - } - dir_paths -} - -/// get functional identifiers -/// -/// ### Arguments -/// -/// * `cm` - Compiled module -/// * `identifier` - Name of module -/// * `address` - Account address for the module -/// * `pattern` - Certain functions to run based on pattern -pub fn list_entrypoints( - cm: &CompiledModule, - pattern: String, -) -> Result>)>> { - // find non-entry functions and ignore them - // keep entry function names in func_identifiers vector - let funcs = &cm.function_defs; - let func_handles = &cm.function_handles; - let func_identifier_pool = &cm.identifiers; - - let module_id = cm.self_id(); - let identifier = module_id.name().to_string(); - let address = module_id.address(); - - // find # of params in each func if it is entry function - let signature_pool = &cm.signatures; - - let mut func_identifiers: Vec<(String, Vec>)> = Vec::new(); - for func in funcs { - // check if function is marked as entry, if not skip it - let is_entry = func.is_entry; - if !is_entry { - continue; - } - - // extract some info from the function - let func_idx: usize = func.function.0.into(); - let handle = &func_handles[func_idx]; - let func_identifier_idx: usize = handle.name.0.into(); - let func_identifier = &func_identifier_pool[func_identifier_idx]; - - // check if it doesn't start with "benchmark", if not skip it - let func_name = func_identifier.to_string(); - if !func_name.starts_with(PREFIX) { - continue; - } - - // check if it doesn't match pattern, if not skip it - let fully_qualified_path = format!("{}::{}::{}", address, identifier, func_name); - if !fully_qualified_path.contains(&pattern) && !pattern.is_empty() { - continue; - } - - // if it does, ensure no params in benchmark function - let signature_idx: usize = handle.parameters.0.into(); - let func_params = &signature_pool[signature_idx]; - if !func_params.is_empty() { - let first_arg_signature_token = &func_params.0.to_vec()[0]; - match first_arg_signature_token { - SignatureToken::Signer => { - let args = serialize_values(&vec![MoveValue::Signer(*address)]); - func_identifiers.push((func_name, args)); - }, - _ => { - return Err(anyhow!( - "Failed: only supports 1 parameter that is a signer type at the moment." - )); - }, - } - } else { - func_identifiers.push((func_name, vec![])); - } - } - - Ok(func_identifiers) -} diff --git a/aptos-move/aptos-gas-calibration/src/solve.rs b/aptos-move/aptos-gas-calibration/src/solve.rs deleted file mode 100644 index 0cdc85ff63ab3..0000000000000 --- a/aptos-move/aptos-gas-calibration/src/solve.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - math::{ - add_gas_formula_to_coefficient_matrix, add_running_time_to_constant_matrix, - compute_least_square_solutions, find_linearly_dependent_variables, - get_computed_time_and_outliers, - }, - math_interface::generic_map, -}; -use aptos_gas_schedule::{InitialGasSchedule, TransactionGasParameters}; -use nalgebra::DMatrix; -use std::collections::BTreeMap; - -const MILLISECONDS_TO_MICROSECONDS: u64 = 1000; - -/// wrapper function to build a coefficient matrix -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `nrows` - Number of rows -/// * `ncols` - Number of cols -pub fn build_coefficient_matrix(input: Vec>, nrows: usize, ncols: usize) -> DMatrix { - let mut coeff_matrix = DMatrix::::zeros(nrows, ncols); - for (idx, eq) in input.iter().enumerate() { - add_gas_formula_to_coefficient_matrix(idx, ncols, eq, &mut coeff_matrix); - } - coeff_matrix -} - -/// wrapper function to build a constant matrix -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `nrows` - Number of rows -/// * `ncols` - Number of cols -pub fn build_constant_matrix(input: Vec, nrows: usize, ncols: usize) -> DMatrix { - let mut const_matrix = DMatrix::::zeros(nrows, ncols); - for (idx, run_time) in input.iter().enumerate() { - add_running_time_to_constant_matrix(idx, *run_time as f64, &mut const_matrix); - } - const_matrix -} - -/// compute the least squares solution -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `coeff_matrix` - Coefficient Matrix -/// * `const_matrix` - Constant Matrix -/// * `max_execution_time` - Configurable flag for max execution time of txn -pub fn least_squares( - input: Vec>, - coeff_matrix: &mut DMatrix, - const_matrix: &mut DMatrix, - equation_names: Vec, - max_execution_time: u64, -) { - let lss = compute_least_square_solutions(coeff_matrix, const_matrix); - if let Ok(answer) = lss { - let mut x_hat = answer; - - let map = generic_map(input.clone()); - let keys: Vec = map.keys().map(|key| key.to_string()).collect(); - - // TODO: error handling with division zero that bubbles up - let computed_time_and_outliers = - get_computed_time_and_outliers(&mut x_hat, coeff_matrix, const_matrix) - .expect("Failed: should unwrap, possibly division by zero"); - - report_computed_times(&equation_names, &computed_time_and_outliers); - - report_outliers(&equation_names, &computed_time_and_outliers); - - convert_to_internal_gas_cost(&mut x_hat, max_execution_time, keys); - } else { - report_undetermined_gas_params(input, coeff_matrix, const_matrix); - } -} - -/// display the computed running times to the user after computing least squares -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `x_hat` - Least squares solution -/// * `coeff_matrix` - Coefficient Matrix -/// * `const_matrix` - Constant Matrix -fn report_computed_times( - equation_names: &[String], - actual_times: &Vec<(usize, f64, f64, f64, bool)>, -) { - println!("\nComputed running times are:\n"); - for (idx, cr, ar, err, is_outlier) in actual_times { - if *is_outlier { - continue; - }; - println!( - "- {} | Computed {}µs vs. Actual {}µs | Error {}\n", - equation_names[*idx], - cr, - format_args!("{:.3}", ar), - format_args!("{:.3}", err) - ); - } -} - -/// determine the outliers after computing least squares -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `x_hat` - Least squares solution -/// * `coeff_matrix` - Coefficient Matrix -/// * `const_matrix` - Constant Matrix -fn report_outliers(equation_names: &[String], outliers: &Vec<(usize, f64, f64, f64, bool)>) { - println!("\nOutliers are:\n"); - for (idx, cr, ar, err, is_outlier) in outliers { - if !is_outlier { - continue; - }; - println!( - "- {} | Computed {}µs vs. Actual {}µs | Error {}\n", - equation_names[*idx], - cr, - format_args!("{:.3}", ar), - format_args!("{:.3}", err) - ); - } -} - -/// find the gas params that could not be determined if the system -/// was not solvable. -/// -/// ### Arguments -/// -/// * `input` - Collection of like-terms -/// * `coeff_matrix` - Coefficient Matrix -/// * `const_matrix` - Constant Matrix -fn report_undetermined_gas_params( - input: Vec>, - coeff_matrix: &mut DMatrix, - const_matrix: &mut DMatrix, -) { - let map = generic_map(input); - let keys: Vec = map.keys().map(|key| key.to_string()).collect(); - - let result = find_linearly_dependent_variables(coeff_matrix, const_matrix, keys.clone()); - match result { - Ok(linear_combos) => { - println!("linearly dependent variables are:\n"); - for gas_param in linear_combos { - println!("- gas parameter: {}\n", gas_param); - } - }, - Err(pivot_columns) => { - println!("free variables are:\n"); - for col in pivot_columns { - let gas_param = &keys[col]; - println!("- gas parameter: {}\n", gas_param); - } - }, - } -} - -/// convert gas usage per instruction to gas cost (InternalGas) -/// -/// ### Arguments -/// -/// * `x_hat` - Least Squares Solution -/// * `max_execution_time` - Configurable flag for max execution time of txn -/// * `gas_params` - A vector representing all gas parameter names in the system -fn convert_to_internal_gas_cost( - x_hat: &mut DMatrix, - max_execution_time: u64, - gas_params: Vec, -) { - let max_execution_gas = u64::from(TransactionGasParameters::initial().max_execution_gas); - let one_microsec_per_internal_gas = - (max_execution_gas / max_execution_time) / MILLISECONDS_TO_MICROSECONDS; - - println!( - "\ninternal gas cost ({} InternalGas per 1µ):\n", - one_microsec_per_internal_gas - ); - - let nrows = x_hat.nrows(); - let ncols = x_hat.ncols(); - for i in 0..nrows { - for j in 0..ncols { - let internal_gas_cost = x_hat[(i, j)] * one_microsec_per_internal_gas as f64; - println!("{} = {}", gas_params[i], internal_gas_cost); - } - } -} diff --git a/aptos-move/aptos-release-builder/Cargo.toml b/aptos-move/aptos-release-builder/Cargo.toml deleted file mode 100644 index 85643b5af10d3..0000000000000 --- a/aptos-move/aptos-release-builder/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "aptos-release-builder" -description = "Tooling for building the governance proposal script used for the network" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true, features = [ "no-upload-proposal" ] } -aptos-api-types = { workspace = true } -aptos-build-info = { workspace = true } -aptos-crypto = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas-schedule-updator = { workspace = true } -aptos-genesis = { workspace = true } -aptos-infallible = { workspace = true } -aptos-keygen = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-temppath = { workspace = true } -aptos-types = { workspace = true } -aptos-vm-genesis = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -git2 = { workspace = true } -handlebars = { workspace = true } -hex = { workspace = true } -move-binary-format = { workspace = true } -move-core-types = { workspace = true } -move-model = { workspace = true } -once_cell = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -strum = { workspace = true } -strum_macros = { workspace = true } -tempfile = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } -walkdir = { workspace = true } - -[[bin]] -name = "aptos-release-builder" -path = "src/main.rs" diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/0-features.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/0-features.move deleted file mode 100644 index 60316fcbd6aa1..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/0-features.move +++ /dev/null @@ -1,26 +0,0 @@ -// Script hash: a9f09ee9 -// Modifying on-chain feature flags: -// Enabled Features: [Bls12381Structures] -// Disabled Features: [Bn254Structures] -// -script { - use aptos_framework::aptos_governance; - use std::features; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - let enabled_blob: vector = vector[ - 13, - ]; - - let disabled_blob: vector = vector[ - 43, - ]; - - features::change_feature_flags_for_next_epoch(framework_signer, enabled_blob, disabled_blob); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/1-consensus-config.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/1-consensus-config.move deleted file mode 100644 index aeee0f28e75c0..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/1-consensus-config.move +++ /dev/null @@ -1,54 +0,0 @@ -// Script hash: 5d14ba49 -// Consensus config upgrade proposal - -// config: V3 { -// alg: Jolteon { -// main: ConsensusConfigV1 { -// decoupled_execution: true, -// back_pressure_limit: 10, -// exclude_round: 40, -// proposer_election_type: LeaderReputation( -// ProposerAndVoterV2( -// ProposerAndVoterConfig { -// active_weight: 1000, -// inactive_weight: 10, -// failed_weight: 1, -// failure_threshold_percent: 10, -// proposer_window_num_validators_multiplier: 10, -// voter_window_num_validators_multiplier: 1, -// weight_by_voting_power: true, -// use_history_from_previous_epoch_max_count: 5, -// }, -// ), -// ), -// max_failed_authors_to_store: 10, -// }, -// quorum_store_enabled: true, -// }, -// vtxn: V1 { -// per_block_limit_txn_count: 3, -// per_block_limit_total_bytes: 2097152, -// }, -// } - -script { - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - let consensus_blob: vector = vector[ - 2, 0, 1, 10, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 2, - 1, 232, 3, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, 5, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, - ]; - - consensus_config::set_for_next_epoch(framework_signer, consensus_blob); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/2-execution-config.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/2-execution-config.move deleted file mode 100644 index b3d8252f0a532..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/2-execution-config.move +++ /dev/null @@ -1,46 +0,0 @@ -// Script hash: e281bacb -// Execution config upgrade proposal - -// config: V4( -// ExecutionConfigV4 { -// transaction_shuffler_type: Fairness { -// sender_conflict_window_size: 256, -// module_conflict_window_size: 2, -// entry_fun_conflict_window_size: 3, -// }, -// block_gas_limit_type: ComplexLimitV1 { -// effective_block_gas_limit: 80001, -// execution_gas_effective_multiplier: 1, -// io_gas_effective_multiplier: 1, -// conflict_penalty_window: 6, -// use_granular_resource_group_conflicts: false, -// use_module_publishing_block_conflict: true, -// block_output_limit: Some( -// 12582912, -// ), -// include_user_txn_size_in_block_output: true, -// add_block_limit_outcome_onchain: false, -// }, -// transaction_deduper_type: TxnHashAndAuthenticatorV1, -// }, -// ) - -script { - use aptos_framework::aptos_governance; - use aptos_framework::execution_config; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - let execution_blob: vector = vector[ - 4, 3, 0, 1, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 2, 129, 56, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 6, - 0, 0, 0, 0, 1, 1, 0, 0, 192, 0, 0, 0, 0, 0, 1, 0, 1, - ]; - - execution_config::set_for_next_epoch(framework_signer, execution_blob); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/3-version.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/3-version.move deleted file mode 100644 index 41fbc42309f21..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/3-version.move +++ /dev/null @@ -1,14 +0,0 @@ -// Script hash: c2035ec4 -script { - use aptos_framework::aptos_governance; - use aptos_framework::version; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - version::set_for_next_epoch(framework_signer, 999); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/4-oidc-provider-ops.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/4-oidc-provider-ops.move deleted file mode 100644 index 852e5a323e6e4..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/4-oidc-provider-ops.move +++ /dev/null @@ -1,15 +0,0 @@ -// Script hash: 268052ee -script { - use aptos_framework::aptos_governance; - use aptos_framework::jwks; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - jwks::upsert_oidc_provider_for_next_epoch(framework_signer, b"https://accounts.google.com", b"https://accounts.google.com/.well-known/openid-configuration"); - jwks::remove_oidc_provider_for_next_epoch(framework_signer, b"https://www.facebook.com"); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/5-gas-schedule.move b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/5-gas-schedule.move deleted file mode 100644 index cc614fdd764f1..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/output/5-gas-schedule.move +++ /dev/null @@ -1,1358 +0,0 @@ -// Script hash: 6688e655 -// source commit hash: d3d3b74193101d949f6c709b24e3a70ddb474197 - -// Gas schedule upgrade proposal - -// Feature version: 15 -// -// Entries: -// instr.nop : 36 -// instr.ret : 220 -// instr.abort : 220 -// instr.br_true : 441 -// instr.br_false : 441 -// instr.branch : 294 -// instr.pop : 147 -// instr.ld_u8 : 220 -// instr.ld_u16 : 220 -// instr.ld_u32 : 220 -// instr.ld_u64 : 220 -// instr.ld_u128 : 294 -// instr.ld_u256 : 294 -// instr.ld_true : 220 -// instr.ld_false : 220 -// instr.ld_const.base : 2389 -// instr.ld_const.per_byte : 128 -// instr.imm_borrow_loc : 220 -// instr.mut_borrow_loc : 220 -// instr.imm_borrow_field : 735 -// instr.mut_borrow_field : 735 -// instr.imm_borrow_field_generic : 735 -// instr.mut_borrow_field_generic : 735 -// instr.copy_loc.base : 294 -// instr.copy_loc.per_abs_val_unit : 14 -// instr.move_loc.base : 441 -// instr.st_loc.base : 441 -// instr.call.base : 3676 -// instr.call.per_arg : 367 -// instr.call.per_local : 367 -// instr.call_generic.base : 3676 -// instr.call_generic.per_ty_arg : 367 -// instr.call_generic.per_arg : 367 -// instr.call_generic.per_local : 367 -// instr.pack.base : 808 -// instr.pack.per_field : 147 -// instr.pack_generic.base : 808 -// instr.pack_generic.per_field : 147 -// instr.unpack.base : 808 -// instr.unpack.per_field : 147 -// instr.unpack_generic.base : 808 -// instr.unpack_generic.per_field : 147 -// instr.read_ref.base : 735 -// instr.read_ref.per_abs_val_unit : 14 -// instr.write_ref.base : 735 -// instr.freeze_ref : 36 -// instr.cast_u8 : 441 -// instr.cast_u16 : 441 -// instr.cast_u32 : 441 -// instr.cast_u64 : 441 -// instr.cast_u128 : 441 -// instr.cast_u256 : 441 -// instr.add : 588 -// instr.sub : 588 -// instr.mul : 588 -// instr.mod : 588 -// instr.div : 588 -// instr.bit_or : 588 -// instr.bit_and : 588 -// instr.bit_xor : 588 -// instr.bit_shl : 588 -// instr.bit_shr : 588 -// instr.or : 588 -// instr.and : 588 -// instr.not : 588 -// instr.lt : 588 -// instr.gt : 588 -// instr.le : 588 -// instr.ge : 588 -// instr.eq.base : 367 -// instr.eq.per_abs_val_unit : 14 -// instr.neq.base : 367 -// instr.neq.per_abs_val_unit : 14 -// instr.imm_borrow_global.base : 1838 -// instr.imm_borrow_global_generic.base : 1838 -// instr.mut_borrow_global.base : 1838 -// instr.mut_borrow_global_generic.base : 1838 -// instr.exists.base : 919 -// instr.exists_generic.base : 919 -// instr.move_from.base : 1286 -// instr.move_from_generic.base : 1286 -// instr.move_to.base : 1838 -// instr.move_to_generic.base : 1838 -// instr.vec_len.base : 808 -// instr.vec_imm_borrow.base : 1213 -// instr.vec_mut_borrow.base : 1213 -// instr.vec_push_back.base : 1396 -// instr.vec_pop_back.base : 955 -// instr.vec_swap.base : 1102 -// instr.vec_pack.base : 2205 -// instr.vec_pack.per_elem : 147 -// instr.vec_unpack.base : 1838 -// instr.vec_unpack.per_expected_elem : 147 -// instr.subst_ty_per_node : 400 -// txn.min_transaction_gas_units : 2760000 -// txn.large_transaction_cutoff : 600 -// txn.intrinsic_gas_per_byte : 1158 -// txn.maximum_number_of_gas_units : 2000000 -// txn.min_price_per_gas_unit : 100 -// txn.max_price_per_gas_unit : 10000000000 -// txn.max_transaction_size_in_bytes : 10485760 -// txn.gas_unit_scaling_factor : 1000000 -// txn.storage_io_per_state_slot_read : 302385 -// txn.storage_io_per_state_byte_read : 151 -// txn.load_data.failure : 0 -// txn.storage_io_per_state_slot_write : 89568 -// txn.storage_io_per_state_byte_write : 89 -// txn.memory_quota : 10000000 -// txn.free_write_bytes_quota : 1024 -// txn.legacy_free_event_bytes_quota : 1024 -// txn.max_bytes_per_write_op : 1048576 -// txn.max_bytes_all_write_ops_per_transaction : 10485760 -// txn.max_bytes_per_event : 1048576 -// txn.max_bytes_all_events_per_transaction : 10485760 -// txn.max_write_ops_per_transaction : 8192 -// txn.legacy_storage_fee_per_state_slot_create : 50000 -// txn.storage_fee_per_state_slot : 40000 -// txn.legacy_storage_fee_per_excess_state_byte : 50 -// txn.storage_fee_per_state_byte : 40 -// txn.legacy_storage_fee_per_event_byte : 20 -// txn.legacy_storage_fee_per_transaction_byte : 20 -// txn.max_execution_gas : 9999999999 -// txn.max_io_gas : 1000000000 -// txn.max_storage_fee : 200000000 -// misc.abs_val.u8 : 40 -// misc.abs_val.u16 : 40 -// misc.abs_val.u32 : 40 -// misc.abs_val.u64 : 40 -// misc.abs_val.u128 : 40 -// misc.abs_val.u256 : 40 -// misc.abs_val.bool : 40 -// misc.abs_val.address : 40 -// misc.abs_val.struct : 40 -// misc.abs_val.vector : 40 -// misc.abs_val.reference : 40 -// misc.abs_val.per_u8_packed : 1 -// misc.abs_val.per_u16_packed : 2 -// misc.abs_val.per_u32_packed : 4 -// misc.abs_val.per_u64_packed : 8 -// misc.abs_val.per_u128_packed : 16 -// misc.abs_val.per_u256_packed : 32 -// misc.abs_val.per_bool_packed : 1 -// misc.abs_val.per_address_packed : 32 -// move_stdlib.bcs.to_bytes.per_byte_serialized : 36 -// move_stdlib.bcs.to_bytes.failure : 3676 -// move_stdlib.hash.sha2_256.base : 11028 -// move_stdlib.hash.sha2_256.per_byte : 183 -// move_stdlib.hash.sha3_256.base : 14704 -// move_stdlib.hash.sha3_256.per_byte : 165 -// move_stdlib.signer.borrow_address.base : 735 -// move_stdlib.string.check_utf8.base : 1102 -// move_stdlib.string.check_utf8.per_byte : 29 -// move_stdlib.string.is_char_boundary.base : 1102 -// move_stdlib.string.sub_string.base : 1470 -// move_stdlib.string.sub_string.per_byte : 11 -// move_stdlib.string.index_of.base : 1470 -// move_stdlib.string.index_of.per_byte_pattern : 73 -// move_stdlib.string.index_of.per_byte_searched : 36 -// table.common.load.base : 302385 -// table.common.load.base_new : 302385 -// table.common.load.per_byte : 151 -// table.common.load.failure : 0 -// table.new_table_handle.base : 3676 -// table.add_box.base : 4411 -// table.add_box.per_byte_serialized : 36 -// table.borrow_box.base : 4411 -// table.borrow_box.per_byte_serialized : 36 -// table.contains_box.base : 4411 -// table.contains_box.per_byte_serialized : 36 -// table.remove_box.base : 4411 -// table.remove_box.per_byte_serialized : 36 -// table.destroy_empty_box.base : 4411 -// table.drop_unchecked_box.base : 367 -// aptos_framework.account.create_address.base : 1102 -// aptos_framework.account.create_signer.base : 1102 -// aptos_framework.algebra.ark_bn254_fq12_add : 809 -// aptos_framework.algebra.ark_bn254_fq12_clone : 807 -// aptos_framework.algebra.ark_bn254_fq12_deser : 23721 -// aptos_framework.algebra.ark_bn254_fq12_div : 517140 -// aptos_framework.algebra.ark_bn254_fq12_eq : 2231 -// aptos_framework.algebra.ark_bn254_fq12_from_u64 : 2658 -// aptos_framework.algebra.ark_bn254_fq12_inv : 398555 -// aptos_framework.algebra.ark_bn254_fq12_mul : 118351 -// aptos_framework.algebra.ark_bn254_fq12_neg : 2446 -// aptos_framework.algebra.ark_bn254_fq12_one : 38 -// aptos_framework.algebra.ark_bn254_fq12_pow_u256 : 35449826 -// aptos_framework.algebra.ark_bn254_fq12_serialize : 21566 -// aptos_framework.algebra.ark_bn254_fq12_square : 86193 -// aptos_framework.algebra.ark_bn254_fq12_sub : 5605 -// aptos_framework.algebra.ark_bn254_fq12_zero : 38 -// aptos_framework.algebra.ark_bn254_fq_add : 803 -// aptos_framework.algebra.ark_bn254_fq_clone : 792 -// aptos_framework.algebra.ark_bn254_fq_deser : 3232 -// aptos_framework.algebra.ark_bn254_fq_div : 209631 -// aptos_framework.algebra.ark_bn254_fq_eq : 803 -// aptos_framework.algebra.ark_bn254_fq_from_u64 : 2598 -// aptos_framework.algebra.ark_bn254_fq_inv : 208902 -// aptos_framework.algebra.ark_bn254_fq_mul : 1847 -// aptos_framework.algebra.ark_bn254_fq_neg : 792 -// aptos_framework.algebra.ark_bn254_fq_one : 38 -// aptos_framework.algebra.ark_bn254_fq_pow_u256 : 382570 -// aptos_framework.algebra.ark_bn254_fq_serialize : 4767 -// aptos_framework.algebra.ark_bn254_fq_square : 792 -// aptos_framework.algebra.ark_bn254_fq_sub : 1130 -// aptos_framework.algebra.ark_bn254_fq_zero : 38 -// aptos_framework.algebra.ark_bn254_fr_add : 804 -// aptos_framework.algebra.ark_bn254_fr_deser : 3073 -// aptos_framework.algebra.ark_bn254_fr_div : 223857 -// aptos_framework.algebra.ark_bn254_fr_eq : 807 -// aptos_framework.algebra.ark_bn254_fr_from_u64 : 2478 -// aptos_framework.algebra.ark_bn254_fr_inv : 222216 -// aptos_framework.algebra.ark_bn254_fr_mul : 1813 -// aptos_framework.algebra.ark_bn254_fr_neg : 792 -// aptos_framework.algebra.ark_bn254_fr_one : 0 -// aptos_framework.algebra.ark_bn254_fr_serialize : 4732 -// aptos_framework.algebra.ark_bn254_fr_square : 792 -// aptos_framework.algebra.ark_bn254_fr_sub : 1906 -// aptos_framework.algebra.ark_bn254_fr_zero : 38 -// aptos_framework.algebra.ark_bn254_g1_affine_deser_comp : 4318809 -// aptos_framework.algebra.ark_bn254_g1_affine_deser_uncomp : 3956976 -// aptos_framework.algebra.ark_bn254_g1_affine_serialize_comp : 8257 -// aptos_framework.algebra.ark_bn254_g1_affine_serialize_uncomp : 10811 -// aptos_framework.algebra.ark_bn254_g1_proj_add : 19574 -// aptos_framework.algebra.ark_bn254_g1_proj_double : 11704 -// aptos_framework.algebra.ark_bn254_g1_proj_eq : 9745 -// aptos_framework.algebra.ark_bn254_g1_proj_generator : 38 -// aptos_framework.algebra.ark_bn254_g1_proj_infinity : 38 -// aptos_framework.algebra.ark_bn254_g1_proj_neg : 38 -// aptos_framework.algebra.ark_bn254_g1_proj_scalar_mul : 4862683 -// aptos_framework.algebra.ark_bn254_g1_proj_sub : 19648 -// aptos_framework.algebra.ark_bn254_g1_proj_to_affine : 1165 -// aptos_framework.algebra.ark_bn254_g2_affine_deser_comp : 12445138 -// aptos_framework.algebra.ark_bn254_g2_affine_deser_uncomp : 11152541 -// aptos_framework.algebra.ark_bn254_g2_affine_serialize_comp : 12721 -// aptos_framework.algebra.ark_bn254_g2_affine_serialize_uncomp : 18105 -// aptos_framework.algebra.ark_bn254_g2_proj_add : 58491 -// aptos_framework.algebra.ark_bn254_g2_proj_double : 29201 -// aptos_framework.algebra.ark_bn254_g2_proj_eq : 25981 -// aptos_framework.algebra.ark_bn254_g2_proj_generator : 38 -// aptos_framework.algebra.ark_bn254_g2_proj_infinity : 38 -// aptos_framework.algebra.ark_bn254_g2_proj_neg : 38 -// aptos_framework.algebra.ark_bn254_g2_proj_scalar_mul : 14041548 -// aptos_framework.algebra.ark_bn254_g2_proj_sub : 59133 -// aptos_framework.algebra.ark_bn254_g2_proj_to_affine : 230100 -// aptos_framework.algebra.ark_bn254_multi_pairing_base : 23488646 -// aptos_framework.algebra.ark_bn254_multi_pairing_per_pair : 12429399 -// aptos_framework.algebra.ark_bn254_pairing : 38543565 -// aptos_framework.algebra.ark_bls12_381_fq12_add : 6686 -// aptos_framework.algebra.ark_bls12_381_fq12_clone : 775 -// aptos_framework.algebra.ark_bls12_381_fq12_deser : 41097 -// aptos_framework.algebra.ark_bls12_381_fq12_div : 921988 -// aptos_framework.algebra.ark_bls12_381_fq12_eq : 2668 -// aptos_framework.algebra.ark_bls12_381_fq12_from_u64 : 3312 -// aptos_framework.algebra.ark_bls12_381_fq12_inv : 737122 -// aptos_framework.algebra.ark_bls12_381_fq12_mul : 183380 -// aptos_framework.algebra.ark_bls12_381_fq12_neg : 4341 -// aptos_framework.algebra.ark_bls12_381_fq12_one : 40 -// aptos_framework.algebra.ark_bls12_381_fq12_pow_u256 : 53905624 -// aptos_framework.algebra.ark_bls12_381_fq12_serialize : 29694 -// aptos_framework.algebra.ark_bls12_381_fq12_square : 129193 -// aptos_framework.algebra.ark_bls12_381_fq12_sub : 6462 -// aptos_framework.algebra.ark_bls12_381_fq12_zero : 775 -// aptos_framework.algebra.ark_bls12_381_fr_add : 775 -// aptos_framework.algebra.ark_bls12_381_fr_deser : 2764 -// aptos_framework.algebra.ark_bls12_381_fr_div : 218501 -// aptos_framework.algebra.ark_bls12_381_fr_eq : 779 -// aptos_framework.algebra.ark_bls12_381_fr_from_u64 : 1815 -// aptos_framework.algebra.ark_bls12_381_fr_inv : 215450 -// aptos_framework.algebra.ark_bls12_381_fr_mul : 1845 -// aptos_framework.algebra.ark_bls12_381_fr_neg : 782 -// aptos_framework.algebra.ark_bls12_381_fr_one : 775 -// aptos_framework.algebra.ark_bls12_381_fr_serialize : 4054 -// aptos_framework.algebra.ark_bls12_381_fr_square : 1746 -// aptos_framework.algebra.ark_bls12_381_fr_sub : 1066 -// aptos_framework.algebra.ark_bls12_381_fr_zero : 775 -// aptos_framework.algebra.ark_bls12_381_g1_affine_deser_comp : 3784805 -// aptos_framework.algebra.ark_bls12_381_g1_affine_deser_uncomp : 2649065 -// aptos_framework.algebra.ark_bls12_381_g1_affine_serialize_comp : 7403 -// aptos_framework.algebra.ark_bls12_381_g1_affine_serialize_uncomp : 8943 -// aptos_framework.algebra.ark_bls12_381_g1_proj_add : 39722 -// aptos_framework.algebra.ark_bls12_381_g1_proj_double : 19350 -// aptos_framework.algebra.ark_bls12_381_g1_proj_eq : 18508 -// aptos_framework.algebra.ark_bls12_381_g1_proj_generator : 40 -// aptos_framework.algebra.ark_bls12_381_g1_proj_infinity : 40 -// aptos_framework.algebra.ark_bls12_381_g1_proj_neg : 40 -// aptos_framework.algebra.ark_bls12_381_g1_proj_scalar_mul : 9276463 -// aptos_framework.algebra.ark_bls12_381_g1_proj_sub : 40976 -// aptos_framework.algebra.ark_bls12_381_g1_proj_to_affine : 444924 -// aptos_framework.algebra.ark_bls12_381_g2_affine_deser_comp : 7572809 -// aptos_framework.algebra.ark_bls12_381_g2_affine_deser_uncomp : 3742090 -// aptos_framework.algebra.ark_bls12_381_g2_affine_serialize_comp : 12417 -// aptos_framework.algebra.ark_bls12_381_g2_affine_serialize_uncomp : 15501 -// aptos_framework.algebra.ark_bls12_381_g2_proj_add : 119106 -// aptos_framework.algebra.ark_bls12_381_g2_proj_double : 54548 -// aptos_framework.algebra.ark_bls12_381_g2_proj_eq : 55709 -// aptos_framework.algebra.ark_bls12_381_g2_proj_generator : 40 -// aptos_framework.algebra.ark_bls12_381_g2_proj_infinity : 40 -// aptos_framework.algebra.ark_bls12_381_g2_proj_neg : 40 -// aptos_framework.algebra.ark_bls12_381_g2_proj_scalar_mul : 27667443 -// aptos_framework.algebra.ark_bls12_381_g2_proj_sub : 120826 -// aptos_framework.algebra.ark_bls12_381_g2_proj_to_affine : 473678 -// aptos_framework.algebra.ark_bls12_381_multi_pairing_base : 33079033 -// aptos_framework.algebra.ark_bls12_381_multi_pairing_per_pair : 16919311 -// aptos_framework.algebra.ark_bls12_381_pairing : 54523240 -// aptos_framework.algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_base : 11954142 -// aptos_framework.algebra.ark_h2c_bls12381g1_xmd_sha256_sswu_per_msg_byte : 176 -// aptos_framework.algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_base : 24897555 -// aptos_framework.algebra.ark_h2c_bls12381g2_xmd_sha256_sswu_per_msg_byte : 176 -// aptos_framework.bls12381.base : 551 -// aptos_framework.bls12381.per_pubkey_deserialize : 400684 -// aptos_framework.bls12381.per_pubkey_aggregate : 15439 -// aptos_framework.bls12381.per_pubkey_subgroup_check : 1360120 -// aptos_framework.bls12381.per_sig_deserialize : 816072 -// aptos_framework.bls12381.per_sig_aggregate : 42825 -// aptos_framework.bls12381.per_sig_subgroup_check : 1692798 -// aptos_framework.bls12381.per_sig_verify : 31190860 -// aptos_framework.bls12381.per_pop_verify : 37862800 -// aptos_framework.bls12381.per_pairing : 14751788 -// aptos_framework.bls12381.per_msg_hashing : 5661040 -// aptos_framework.bls12381.per_byte_hashing : 183 -// aptos_framework.signature.base : 551 -// aptos_framework.signature.per_pubkey_deserialize : 139688 -// aptos_framework.signature.per_pubkey_small_order_check : 23342 -// aptos_framework.signature.per_sig_deserialize : 1378 -// aptos_framework.signature.per_sig_strict_verify : 981492 -// aptos_framework.signature.per_msg_hashing_base : 11910 -// aptos_framework.signature.per_msg_byte_hashing : 220 -// aptos_framework.secp256k1.base : 551 -// aptos_framework.secp256k1.ecdsa_recover : 5918360 -// aptos_framework.ristretto255.basepoint_mul : 470528 -// aptos_framework.ristretto255.basepoint_double_mul : 1617440 -// aptos_framework.ristretto255.point_add : 7848 -// aptos_framework.ristretto255.point_clone : 551 -// aptos_framework.ristretto255.point_compress : 147040 -// aptos_framework.ristretto255.point_decompress : 148878 -// aptos_framework.ristretto255.point_equals : 8454 -// aptos_framework.ristretto255.point_from_64_uniform_bytes : 299594 -// aptos_framework.ristretto255.point_identity : 551 -// aptos_framework.ristretto255.point_mul : 1731396 -// aptos_framework.ristretto255.point_double_mul : 1869907 -// aptos_framework.ristretto255.point_neg : 1323 -// aptos_framework.ristretto255.point_sub : 7829 -// aptos_framework.ristretto255.point_parse_arg : 551 -// aptos_framework.ristretto255.scalar_sha512_per_byte : 220 -// aptos_framework.ristretto255.scalar_sha512_per_hash : 11910 -// aptos_framework.ristretto255.scalar_add : 2830 -// aptos_framework.ristretto255.scalar_reduced_from_32_bytes : 2609 -// aptos_framework.ristretto255.scalar_uniform_from_64_bytes : 4576 -// aptos_framework.ristretto255.scalar_from_u128 : 643 -// aptos_framework.ristretto255.scalar_from_u64 : 643 -// aptos_framework.ristretto255.scalar_invert : 404360 -// aptos_framework.ristretto255.scalar_is_canonical : 4227 -// aptos_framework.ristretto255.scalar_mul : 3914 -// aptos_framework.ristretto255.scalar_neg : 2665 -// aptos_framework.ristretto255.scalar_sub : 3896 -// aptos_framework.ristretto255.scalar_parse_arg : 551 -// aptos_framework.hash.sip_hash.base : 3676 -// aptos_framework.hash.sip_hash.per_byte : 73 -// aptos_framework.hash.keccak256.base : 14704 -// aptos_framework.hash.keccak256.per_byte : 165 -// aptos_framework.bulletproofs.base : 11794651 -// aptos_framework.bulletproofs.per_bit_rangeproof_verify : 1004253 -// aptos_framework.bulletproofs.per_byte_rangeproof_deserialize : 121 -// aptos_framework.type_info.type_of.base : 1102 -// aptos_framework.type_info.type_of.per_abstract_memory_unit : 18 -// aptos_framework.type_info.type_name.base : 1102 -// aptos_framework.type_info.type_name.per_abstract_memory_unit : 18 -// aptos_framework.type_info.chain_id.base : 551 -// aptos_framework.hash.sha2_512.base : 11910 -// aptos_framework.hash.sha2_512.per_byte : 220 -// aptos_framework.hash.sha3_512.base : 16542 -// aptos_framework.hash.sha3_512.per_byte : 183 -// aptos_framework.hash.ripemd160.base : 11028 -// aptos_framework.hash.ripemd160.per_byte : 183 -// aptos_framework.hash.blake2b_256.base : 6433 -// aptos_framework.hash.blake2b_256.per_byte : 55 -// aptos_framework.util.from_bytes.base : 1102 -// aptos_framework.util.from_bytes.per_byte : 18 -// aptos_framework.transaction_context.get_txn_hash.base : 735 -// aptos_framework.transaction_context.get_script_hash.base : 735 -// aptos_framework.transaction_context.generate_unique_address.base : 14704 -// aptos_framework.code.request_publish.base : 1838 -// aptos_framework.code.request_publish.per_byte : 7 -// aptos_framework.event.write_to_event_store.base : 20006 -// aptos_framework.event.write_to_event_store.per_abstract_memory_unit : 61 -// aptos_framework.state_storage.get_usage.base : 1838 -// aptos_framework.aggregator.add.base : 1102 -// aptos_framework.aggregator.read.base : 1102 -// aptos_framework.aggregator.sub.base : 1102 -// aptos_framework.aggregator.destroy.base : 1838 -// aptos_framework.aggregator_factory.new_aggregator.base : 1838 -// aptos_framework.aggregator_v2.create_aggregator.base : 1838 -// aptos_framework.aggregator_v2.try_add.base : 1102 -// aptos_framework.aggregator_v2.try_sub.base : 1102 -// aptos_framework.aggregator_v2.read.base : 2205 -// aptos_framework.aggregator_v2.snapshot.base : 1102 -// aptos_framework.aggregator_v2.create_snapshot.base : 1102 -// aptos_framework.aggregator_v2.create_snapshot.per_byte : 3 -// aptos_framework.aggregator_v2.copy_snapshot.base : 1102 -// aptos_framework.aggregator_v2.read_snapshot.base : 2205 -// aptos_framework.aggregator_v2.string_concat.base : 1102 -// aptos_framework.aggregator_v2.string_concat.per_byte : 3 -// aptos_framework.object.exists_at.base : 919 -// aptos_framework.object.exists_at.per_byte_loaded : 183 -// aptos_framework.object.exists_at.per_item_loaded : 1470 -// aptos_framework.string_utils.format.base : 1102 -// aptos_framework.string_utils.format.per_byte : 3 - -script { - use aptos_framework::aptos_governance; - use aptos_framework::gas_schedule; - - fun main(core_resources: &signer) { - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - - let framework_signer = &core_signer; - - let gas_schedule_blob: vector = vector[ - 15, 0, 0, 0, 0, 0, 0, 0, 151, 3, 9, 105, 110, 115, 116, 114, 46, 110, 111, 112, - 36, 0, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 114, 101, 116, 220, 0, - 0, 0, 0, 0, 0, 0, 11, 105, 110, 115, 116, 114, 46, 97, 98, 111, 114, 116, 220, 0, - 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 114, 95, 116, 114, 117, 101, - 185, 1, 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 98, 114, 95, 102, 97, - 108, 115, 101, 185, 1, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 98, 114, - 97, 110, 99, 104, 38, 1, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 112, - 111, 112, 147, 0, 0, 0, 0, 0, 0, 0, 11, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 117, 56, 220, 0, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 117, 49, 54, 220, 0, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, 100, - 95, 117, 51, 50, 220, 0, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, - 100, 95, 117, 54, 52, 220, 0, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, - 108, 100, 95, 117, 49, 50, 56, 38, 1, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, - 114, 46, 108, 100, 95, 117, 50, 53, 54, 38, 1, 0, 0, 0, 0, 0, 0, 13, 105, 110, - 115, 116, 114, 46, 108, 100, 95, 116, 114, 117, 101, 220, 0, 0, 0, 0, 0, 0, 0, 14, - 105, 110, 115, 116, 114, 46, 108, 100, 95, 102, 97, 108, 115, 101, 220, 0, 0, 0, 0, 0, - 0, 0, 19, 105, 110, 115, 116, 114, 46, 108, 100, 95, 99, 111, 110, 115, 116, 46, 98, 97, - 115, 101, 85, 9, 0, 0, 0, 0, 0, 0, 23, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 99, 111, 110, 115, 116, 46, 112, 101, 114, 95, 98, 121, 116, 101, 128, 0, 0, 0, 0, 0, - 0, 0, 20, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, - 108, 111, 99, 220, 0, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 109, 117, - 116, 95, 98, 111, 114, 114, 111, 119, 95, 108, 111, 99, 220, 0, 0, 0, 0, 0, 0, 0, - 22, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, 102, 105, - 101, 108, 100, 223, 2, 0, 0, 0, 0, 0, 0, 22, 105, 110, 115, 116, 114, 46, 109, 117, - 116, 95, 98, 111, 114, 114, 111, 119, 95, 102, 105, 101, 108, 100, 223, 2, 0, 0, 0, 0, - 0, 0, 30, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, - 102, 105, 101, 108, 100, 95, 103, 101, 110, 101, 114, 105, 99, 223, 2, 0, 0, 0, 0, 0, - 0, 30, 105, 110, 115, 116, 114, 46, 109, 117, 116, 95, 98, 111, 114, 114, 111, 119, 95, 102, - 105, 101, 108, 100, 95, 103, 101, 110, 101, 114, 105, 99, 223, 2, 0, 0, 0, 0, 0, 0, - 19, 105, 110, 115, 116, 114, 46, 99, 111, 112, 121, 95, 108, 111, 99, 46, 98, 97, 115, 101, - 38, 1, 0, 0, 0, 0, 0, 0, 31, 105, 110, 115, 116, 114, 46, 99, 111, 112, 121, 95, - 108, 111, 99, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, 110, 105, 116, - 14, 0, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, - 108, 111, 99, 46, 98, 97, 115, 101, 185, 1, 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, - 116, 114, 46, 115, 116, 95, 108, 111, 99, 46, 98, 97, 115, 101, 185, 1, 0, 0, 0, 0, - 0, 0, 15, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 46, 98, 97, 115, 101, 92, 14, - 0, 0, 0, 0, 0, 0, 18, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 46, 112, 101, - 114, 95, 97, 114, 103, 111, 1, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, - 99, 97, 108, 108, 46, 112, 101, 114, 95, 108, 111, 99, 97, 108, 111, 1, 0, 0, 0, 0, - 0, 0, 23, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, - 99, 46, 98, 97, 115, 101, 92, 14, 0, 0, 0, 0, 0, 0, 29, 105, 110, 115, 116, 114, - 46, 99, 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 116, 121, - 95, 97, 114, 103, 111, 1, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, 116, 114, 46, 99, - 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 97, 114, 103, 111, - 1, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 95, 103, - 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 108, 111, 99, 97, 108, 111, 1, 0, 0, - 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, 46, 98, 97, 115, 101, - 40, 3, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, 46, - 112, 101, 114, 95, 102, 105, 101, 108, 100, 147, 0, 0, 0, 0, 0, 0, 0, 23, 105, 110, - 115, 116, 114, 46, 112, 97, 99, 107, 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, - 101, 40, 3, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, - 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 102, 105, 101, 108, 100, 147, 0, - 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 46, - 98, 97, 115, 101, 40, 3, 0, 0, 0, 0, 0, 0, 22, 105, 110, 115, 116, 114, 46, 117, - 110, 112, 97, 99, 107, 46, 112, 101, 114, 95, 102, 105, 101, 108, 100, 147, 0, 0, 0, 0, - 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 95, 103, 101, 110, - 101, 114, 105, 99, 46, 98, 97, 115, 101, 40, 3, 0, 0, 0, 0, 0, 0, 30, 105, 110, - 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, - 101, 114, 95, 102, 105, 101, 108, 100, 147, 0, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, - 116, 114, 46, 114, 101, 97, 100, 95, 114, 101, 102, 46, 98, 97, 115, 101, 223, 2, 0, 0, - 0, 0, 0, 0, 31, 105, 110, 115, 116, 114, 46, 114, 101, 97, 100, 95, 114, 101, 102, 46, - 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, 110, 105, 116, 14, 0, 0, 0, - 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 119, 114, 105, 116, 101, 95, 114, 101, 102, - 46, 98, 97, 115, 101, 223, 2, 0, 0, 0, 0, 0, 0, 16, 105, 110, 115, 116, 114, 46, - 102, 114, 101, 101, 122, 101, 95, 114, 101, 102, 36, 0, 0, 0, 0, 0, 0, 0, 13, 105, - 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 56, 185, 1, 0, 0, 0, 0, 0, 0, - 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 49, 54, 185, 1, 0, 0, 0, - 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 51, 50, 185, 1, - 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 54, - 52, 185, 1, 0, 0, 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, - 95, 117, 49, 50, 56, 185, 1, 0, 0, 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, - 99, 97, 115, 116, 95, 117, 50, 53, 54, 185, 1, 0, 0, 0, 0, 0, 0, 9, 105, 110, - 115, 116, 114, 46, 97, 100, 100, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, - 114, 46, 115, 117, 98, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, - 109, 117, 108, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 109, 111, - 100, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 100, 105, 118, 76, - 2, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 98, 105, 116, 95, 111, 114, - 76, 2, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 105, 116, 95, 97, - 110, 100, 76, 2, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 105, 116, - 95, 120, 111, 114, 76, 2, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, - 105, 116, 95, 115, 104, 108, 76, 2, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, - 46, 98, 105, 116, 95, 115, 104, 114, 76, 2, 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, - 116, 114, 46, 111, 114, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, - 97, 110, 100, 76, 2, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 110, 111, - 116, 76, 2, 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 108, 116, 76, 2, - 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 103, 116, 76, 2, 0, 0, 0, - 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 108, 101, 76, 2, 0, 0, 0, 0, 0, 0, - 8, 105, 110, 115, 116, 114, 46, 103, 101, 76, 2, 0, 0, 0, 0, 0, 0, 13, 105, 110, - 115, 116, 114, 46, 101, 113, 46, 98, 97, 115, 101, 111, 1, 0, 0, 0, 0, 0, 0, 25, - 105, 110, 115, 116, 114, 46, 101, 113, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, - 95, 117, 110, 105, 116, 14, 0, 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, - 110, 101, 113, 46, 98, 97, 115, 101, 111, 1, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, - 116, 114, 46, 110, 101, 113, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, - 110, 105, 116, 14, 0, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 105, 109, - 109, 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 46, 98, 97, 115, 101, - 46, 7, 0, 0, 0, 0, 0, 0, 36, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, - 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 95, 103, 101, 110, 101, 114, 105, 99, - 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, - 109, 117, 116, 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 46, 98, 97, - 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 36, 105, 110, 115, 116, 114, 46, 109, 117, 116, - 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 95, 103, 101, 110, 101, 114, - 105, 99, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, 116, - 114, 46, 101, 120, 105, 115, 116, 115, 46, 98, 97, 115, 101, 151, 3, 0, 0, 0, 0, 0, - 0, 25, 105, 110, 115, 116, 114, 46, 101, 120, 105, 115, 116, 115, 95, 103, 101, 110, 101, 114, - 105, 99, 46, 98, 97, 115, 101, 151, 3, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, - 114, 46, 109, 111, 118, 101, 95, 102, 114, 111, 109, 46, 98, 97, 115, 101, 6, 5, 0, 0, - 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, 102, 114, 111, 109, - 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, 101, 6, 5, 0, 0, 0, 0, 0, - 0, 18, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, 116, 111, 46, 98, 97, 115, 101, - 46, 7, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, - 116, 111, 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, - 0, 0, 0, 18, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 108, 101, 110, 46, 98, 97, - 115, 101, 40, 3, 0, 0, 0, 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 118, 101, 99, - 95, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 46, 98, 97, 115, 101, 189, 4, 0, 0, - 0, 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 109, 117, 116, 95, 98, - 111, 114, 114, 111, 119, 46, 98, 97, 115, 101, 189, 4, 0, 0, 0, 0, 0, 0, 24, 105, - 110, 115, 116, 114, 46, 118, 101, 99, 95, 112, 117, 115, 104, 95, 98, 97, 99, 107, 46, 98, - 97, 115, 101, 116, 5, 0, 0, 0, 0, 0, 0, 23, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 112, 111, 112, 95, 98, 97, 99, 107, 46, 98, 97, 115, 101, 187, 3, 0, 0, 0, - 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 115, 119, 97, 112, 46, 98, - 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 112, 97, 99, 107, 46, 98, 97, 115, 101, 157, 8, 0, 0, 0, 0, 0, 0, 23, - 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 112, 97, 99, 107, 46, 112, 101, 114, 95, 101, - 108, 101, 109, 147, 0, 0, 0, 0, 0, 0, 0, 21, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 117, 110, 112, 97, 99, 107, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, - 0, 34, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 117, 110, 112, 97, 99, 107, 46, 112, - 101, 114, 95, 101, 120, 112, 101, 99, 116, 101, 100, 95, 101, 108, 101, 109, 147, 0, 0, 0, - 0, 0, 0, 0, 23, 105, 110, 115, 116, 114, 46, 115, 117, 98, 115, 116, 95, 116, 121, 95, - 112, 101, 114, 95, 110, 111, 100, 101, 144, 1, 0, 0, 0, 0, 0, 0, 29, 116, 120, 110, - 46, 109, 105, 110, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 103, 97, 115, - 95, 117, 110, 105, 116, 115, 64, 29, 42, 0, 0, 0, 0, 0, 28, 116, 120, 110, 46, 108, - 97, 114, 103, 101, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 99, 117, 116, - 111, 102, 102, 88, 2, 0, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 105, 110, 116, 114, - 105, 110, 115, 105, 99, 95, 103, 97, 115, 95, 112, 101, 114, 95, 98, 121, 116, 101, 134, 4, - 0, 0, 0, 0, 0, 0, 31, 116, 120, 110, 46, 109, 97, 120, 105, 109, 117, 109, 95, 110, - 117, 109, 98, 101, 114, 95, 111, 102, 95, 103, 97, 115, 95, 117, 110, 105, 116, 115, 128, 132, - 30, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 109, 105, 110, 95, 112, 114, 105, 99, 101, - 95, 112, 101, 114, 95, 103, 97, 115, 95, 117, 110, 105, 116, 100, 0, 0, 0, 0, 0, 0, - 0, 26, 116, 120, 110, 46, 109, 97, 120, 95, 112, 114, 105, 99, 101, 95, 112, 101, 114, 95, - 103, 97, 115, 95, 117, 110, 105, 116, 0, 228, 11, 84, 2, 0, 0, 0, 33, 116, 120, 110, - 46, 109, 97, 120, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 115, 105, 122, - 101, 95, 105, 110, 95, 98, 121, 116, 101, 115, 0, 0, 160, 0, 0, 0, 0, 0, 27, 116, - 120, 110, 46, 103, 97, 115, 95, 117, 110, 105, 116, 95, 115, 99, 97, 108, 105, 110, 103, 95, - 102, 97, 99, 116, 111, 114, 64, 66, 15, 0, 0, 0, 0, 0, 34, 116, 120, 110, 46, 115, - 116, 111, 114, 97, 103, 101, 95, 105, 111, 95, 112, 101, 114, 95, 115, 116, 97, 116, 101, 95, - 115, 108, 111, 116, 95, 114, 101, 97, 100, 49, 157, 4, 0, 0, 0, 0, 0, 34, 116, 120, - 110, 46, 115, 116, 111, 114, 97, 103, 101, 95, 105, 111, 95, 112, 101, 114, 95, 115, 116, 97, - 116, 101, 95, 98, 121, 116, 101, 95, 114, 101, 97, 100, 151, 0, 0, 0, 0, 0, 0, 0, - 21, 116, 120, 110, 46, 108, 111, 97, 100, 95, 100, 97, 116, 97, 46, 102, 97, 105, 108, 117, - 114, 101, 0, 0, 0, 0, 0, 0, 0, 0, 35, 116, 120, 110, 46, 115, 116, 111, 114, 97, - 103, 101, 95, 105, 111, 95, 112, 101, 114, 95, 115, 116, 97, 116, 101, 95, 115, 108, 111, 116, - 95, 119, 114, 105, 116, 101, 224, 93, 1, 0, 0, 0, 0, 0, 35, 116, 120, 110, 46, 115, - 116, 111, 114, 97, 103, 101, 95, 105, 111, 95, 112, 101, 114, 95, 115, 116, 97, 116, 101, 95, - 98, 121, 116, 101, 95, 119, 114, 105, 116, 101, 89, 0, 0, 0, 0, 0, 0, 0, 16, 116, - 120, 110, 46, 109, 101, 109, 111, 114, 121, 95, 113, 117, 111, 116, 97, 128, 150, 152, 0, 0, - 0, 0, 0, 26, 116, 120, 110, 46, 102, 114, 101, 101, 95, 119, 114, 105, 116, 101, 95, 98, - 121, 116, 101, 115, 95, 113, 117, 111, 116, 97, 0, 4, 0, 0, 0, 0, 0, 0, 33, 116, - 120, 110, 46, 108, 101, 103, 97, 99, 121, 95, 102, 114, 101, 101, 95, 101, 118, 101, 110, 116, - 95, 98, 121, 116, 101, 115, 95, 113, 117, 111, 116, 97, 0, 4, 0, 0, 0, 0, 0, 0, - 26, 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, 112, 101, 114, 95, 119, - 114, 105, 116, 101, 95, 111, 112, 0, 0, 16, 0, 0, 0, 0, 0, 43, 116, 120, 110, 46, - 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, 97, 108, 108, 95, 119, 114, 105, 116, 101, 95, - 111, 112, 115, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 0, - 0, 160, 0, 0, 0, 0, 0, 23, 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, - 115, 95, 112, 101, 114, 95, 101, 118, 101, 110, 116, 0, 0, 16, 0, 0, 0, 0, 0, 40, - 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, 97, 108, 108, 95, 101, 118, - 101, 110, 116, 115, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, - 0, 0, 160, 0, 0, 0, 0, 0, 33, 116, 120, 110, 46, 109, 97, 120, 95, 119, 114, 105, - 116, 101, 95, 111, 112, 115, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, - 111, 110, 0, 32, 0, 0, 0, 0, 0, 0, 44, 116, 120, 110, 46, 108, 101, 103, 97, 99, - 121, 95, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, 112, 101, 114, 95, 115, 116, - 97, 116, 101, 95, 115, 108, 111, 116, 95, 99, 114, 101, 97, 116, 101, 80, 195, 0, 0, 0, - 0, 0, 0, 30, 116, 120, 110, 46, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, - 112, 101, 114, 95, 115, 116, 97, 116, 101, 95, 115, 108, 111, 116, 64, 156, 0, 0, 0, 0, - 0, 0, 44, 116, 120, 110, 46, 108, 101, 103, 97, 99, 121, 95, 115, 116, 111, 114, 97, 103, - 101, 95, 102, 101, 101, 95, 112, 101, 114, 95, 101, 120, 99, 101, 115, 115, 95, 115, 116, 97, - 116, 101, 95, 98, 121, 116, 101, 50, 0, 0, 0, 0, 0, 0, 0, 30, 116, 120, 110, 46, - 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, 112, 101, 114, 95, 115, 116, 97, 116, - 101, 95, 98, 121, 116, 101, 40, 0, 0, 0, 0, 0, 0, 0, 37, 116, 120, 110, 46, 108, - 101, 103, 97, 99, 121, 95, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, 112, 101, - 114, 95, 101, 118, 101, 110, 116, 95, 98, 121, 116, 101, 20, 0, 0, 0, 0, 0, 0, 0, - 43, 116, 120, 110, 46, 108, 101, 103, 97, 99, 121, 95, 115, 116, 111, 114, 97, 103, 101, 95, - 102, 101, 101, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, - 98, 121, 116, 101, 20, 0, 0, 0, 0, 0, 0, 0, 21, 116, 120, 110, 46, 109, 97, 120, - 95, 101, 120, 101, 99, 117, 116, 105, 111, 110, 95, 103, 97, 115, 254, 227, 11, 84, 2, 0, - 0, 0, 14, 116, 120, 110, 46, 109, 97, 120, 95, 105, 111, 95, 103, 97, 115, 0, 202, 154, - 59, 0, 0, 0, 0, 19, 116, 120, 110, 46, 109, 97, 120, 95, 115, 116, 111, 114, 97, 103, - 101, 95, 102, 101, 101, 0, 194, 235, 11, 0, 0, 0, 0, 15, 109, 105, 115, 99, 46, 97, - 98, 115, 95, 118, 97, 108, 46, 117, 56, 40, 0, 0, 0, 0, 0, 0, 0, 16, 109, 105, - 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 49, 54, 40, 0, 0, 0, 0, 0, - 0, 0, 16, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 51, 50, 40, - 0, 0, 0, 0, 0, 0, 0, 16, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, - 46, 117, 54, 52, 40, 0, 0, 0, 0, 0, 0, 0, 17, 109, 105, 115, 99, 46, 97, 98, - 115, 95, 118, 97, 108, 46, 117, 49, 50, 56, 40, 0, 0, 0, 0, 0, 0, 0, 17, 109, - 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 50, 53, 54, 40, 0, 0, 0, - 0, 0, 0, 0, 17, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 98, 111, - 111, 108, 40, 0, 0, 0, 0, 0, 0, 0, 20, 109, 105, 115, 99, 46, 97, 98, 115, 95, - 118, 97, 108, 46, 97, 100, 100, 114, 101, 115, 115, 40, 0, 0, 0, 0, 0, 0, 0, 19, - 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 115, 116, 114, 117, 99, 116, 40, - 0, 0, 0, 0, 0, 0, 0, 19, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, - 46, 118, 101, 99, 116, 111, 114, 40, 0, 0, 0, 0, 0, 0, 0, 22, 109, 105, 115, 99, - 46, 97, 98, 115, 95, 118, 97, 108, 46, 114, 101, 102, 101, 114, 101, 110, 99, 101, 40, 0, - 0, 0, 0, 0, 0, 0, 26, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, - 112, 101, 114, 95, 117, 56, 95, 112, 97, 99, 107, 101, 100, 1, 0, 0, 0, 0, 0, 0, - 0, 27, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 117, - 49, 54, 95, 112, 97, 99, 107, 101, 100, 2, 0, 0, 0, 0, 0, 0, 0, 27, 109, 105, - 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 117, 51, 50, 95, 112, - 97, 99, 107, 101, 100, 4, 0, 0, 0, 0, 0, 0, 0, 27, 109, 105, 115, 99, 46, 97, - 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 117, 54, 52, 95, 112, 97, 99, 107, 101, - 100, 8, 0, 0, 0, 0, 0, 0, 0, 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, - 97, 108, 46, 112, 101, 114, 95, 117, 49, 50, 56, 95, 112, 97, 99, 107, 101, 100, 16, 0, - 0, 0, 0, 0, 0, 0, 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, - 112, 101, 114, 95, 117, 50, 53, 54, 95, 112, 97, 99, 107, 101, 100, 32, 0, 0, 0, 0, - 0, 0, 0, 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, - 95, 98, 111, 111, 108, 95, 112, 97, 99, 107, 101, 100, 1, 0, 0, 0, 0, 0, 0, 0, - 31, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 97, 100, - 100, 114, 101, 115, 115, 95, 112, 97, 99, 107, 101, 100, 32, 0, 0, 0, 0, 0, 0, 0, - 44, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 98, 99, 115, 46, 116, 111, 95, - 98, 121, 116, 101, 115, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, 114, 105, 97, - 108, 105, 122, 101, 100, 36, 0, 0, 0, 0, 0, 0, 0, 32, 109, 111, 118, 101, 95, 115, - 116, 100, 108, 105, 98, 46, 98, 99, 115, 46, 116, 111, 95, 98, 121, 116, 101, 115, 46, 102, - 97, 105, 108, 117, 114, 101, 92, 14, 0, 0, 0, 0, 0, 0, 30, 109, 111, 118, 101, 95, - 115, 116, 100, 108, 105, 98, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 50, 53, 54, - 46, 98, 97, 115, 101, 20, 43, 0, 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, 95, 115, - 116, 100, 108, 105, 98, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 50, 53, 54, 46, - 112, 101, 114, 95, 98, 121, 116, 101, 183, 0, 0, 0, 0, 0, 0, 0, 30, 109, 111, 118, - 101, 95, 115, 116, 100, 108, 105, 98, 46, 104, 97, 115, 104, 46, 115, 104, 97, 51, 95, 50, - 53, 54, 46, 98, 97, 115, 101, 112, 57, 0, 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, - 95, 115, 116, 100, 108, 105, 98, 46, 104, 97, 115, 104, 46, 115, 104, 97, 51, 95, 50, 53, - 54, 46, 112, 101, 114, 95, 98, 121, 116, 101, 165, 0, 0, 0, 0, 0, 0, 0, 38, 109, - 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 105, 103, 110, 101, 114, 46, 98, 111, - 114, 114, 111, 119, 95, 97, 100, 100, 114, 101, 115, 115, 46, 98, 97, 115, 101, 223, 2, 0, - 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, - 114, 105, 110, 103, 46, 99, 104, 101, 99, 107, 95, 117, 116, 102, 56, 46, 98, 97, 115, 101, - 78, 4, 0, 0, 0, 0, 0, 0, 38, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, - 46, 115, 116, 114, 105, 110, 103, 46, 99, 104, 101, 99, 107, 95, 117, 116, 102, 56, 46, 112, - 101, 114, 95, 98, 121, 116, 101, 29, 0, 0, 0, 0, 0, 0, 0, 40, 109, 111, 118, 101, - 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 105, 115, 95, 99, 104, - 97, 114, 95, 98, 111, 117, 110, 100, 97, 114, 121, 46, 98, 97, 115, 101, 78, 4, 0, 0, - 0, 0, 0, 0, 34, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, - 105, 110, 103, 46, 115, 117, 98, 95, 115, 116, 114, 105, 110, 103, 46, 98, 97, 115, 101, 190, - 5, 0, 0, 0, 0, 0, 0, 38, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, - 115, 116, 114, 105, 110, 103, 46, 115, 117, 98, 95, 115, 116, 114, 105, 110, 103, 46, 112, 101, - 114, 95, 98, 121, 116, 101, 11, 0, 0, 0, 0, 0, 0, 0, 32, 109, 111, 118, 101, 95, - 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 105, 110, 100, 101, 120, 95, - 111, 102, 46, 98, 97, 115, 101, 190, 5, 0, 0, 0, 0, 0, 0, 44, 109, 111, 118, 101, - 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 105, 110, 100, 101, 120, - 95, 111, 102, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 112, 97, 116, 116, 101, 114, 110, - 73, 0, 0, 0, 0, 0, 0, 0, 45, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, - 46, 115, 116, 114, 105, 110, 103, 46, 105, 110, 100, 101, 120, 95, 111, 102, 46, 112, 101, 114, - 95, 98, 121, 116, 101, 95, 115, 101, 97, 114, 99, 104, 101, 100, 36, 0, 0, 0, 0, 0, - 0, 0, 22, 116, 97, 98, 108, 101, 46, 99, 111, 109, 109, 111, 110, 46, 108, 111, 97, 100, - 46, 98, 97, 115, 101, 49, 157, 4, 0, 0, 0, 0, 0, 26, 116, 97, 98, 108, 101, 46, - 99, 111, 109, 109, 111, 110, 46, 108, 111, 97, 100, 46, 98, 97, 115, 101, 95, 110, 101, 119, - 49, 157, 4, 0, 0, 0, 0, 0, 26, 116, 97, 98, 108, 101, 46, 99, 111, 109, 109, 111, - 110, 46, 108, 111, 97, 100, 46, 112, 101, 114, 95, 98, 121, 116, 101, 151, 0, 0, 0, 0, - 0, 0, 0, 25, 116, 97, 98, 108, 101, 46, 99, 111, 109, 109, 111, 110, 46, 108, 111, 97, - 100, 46, 102, 97, 105, 108, 117, 114, 101, 0, 0, 0, 0, 0, 0, 0, 0, 27, 116, 97, - 98, 108, 101, 46, 110, 101, 119, 95, 116, 97, 98, 108, 101, 95, 104, 97, 110, 100, 108, 101, - 46, 98, 97, 115, 101, 92, 14, 0, 0, 0, 0, 0, 0, 18, 116, 97, 98, 108, 101, 46, - 97, 100, 100, 95, 98, 111, 120, 46, 98, 97, 115, 101, 59, 17, 0, 0, 0, 0, 0, 0, - 33, 116, 97, 98, 108, 101, 46, 97, 100, 100, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, - 121, 116, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 36, 0, 0, 0, 0, 0, - 0, 0, 21, 116, 97, 98, 108, 101, 46, 98, 111, 114, 114, 111, 119, 95, 98, 111, 120, 46, - 98, 97, 115, 101, 59, 17, 0, 0, 0, 0, 0, 0, 36, 116, 97, 98, 108, 101, 46, 98, - 111, 114, 114, 111, 119, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, - 101, 114, 105, 97, 108, 105, 122, 101, 100, 36, 0, 0, 0, 0, 0, 0, 0, 23, 116, 97, - 98, 108, 101, 46, 99, 111, 110, 116, 97, 105, 110, 115, 95, 98, 111, 120, 46, 98, 97, 115, - 101, 59, 17, 0, 0, 0, 0, 0, 0, 38, 116, 97, 98, 108, 101, 46, 99, 111, 110, 116, - 97, 105, 110, 115, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, - 114, 105, 97, 108, 105, 122, 101, 100, 36, 0, 0, 0, 0, 0, 0, 0, 21, 116, 97, 98, - 108, 101, 46, 114, 101, 109, 111, 118, 101, 95, 98, 111, 120, 46, 98, 97, 115, 101, 59, 17, - 0, 0, 0, 0, 0, 0, 36, 116, 97, 98, 108, 101, 46, 114, 101, 109, 111, 118, 101, 95, - 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, 114, 105, 97, 108, 105, - 122, 101, 100, 36, 0, 0, 0, 0, 0, 0, 0, 28, 116, 97, 98, 108, 101, 46, 100, 101, - 115, 116, 114, 111, 121, 95, 101, 109, 112, 116, 121, 95, 98, 111, 120, 46, 98, 97, 115, 101, - 59, 17, 0, 0, 0, 0, 0, 0, 29, 116, 97, 98, 108, 101, 46, 100, 114, 111, 112, 95, - 117, 110, 99, 104, 101, 99, 107, 101, 100, 95, 98, 111, 120, 46, 98, 97, 115, 101, 111, 1, - 0, 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 99, 99, 111, 117, 110, 116, 46, 99, 114, 101, 97, 116, 101, 95, 97, 100, - 100, 114, 101, 115, 115, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 42, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 99, 99, 111, 117, - 110, 116, 46, 99, 114, 101, 97, 116, 101, 95, 115, 105, 103, 110, 101, 114, 46, 98, 97, 115, - 101, 78, 4, 0, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, - 50, 53, 52, 95, 102, 113, 49, 50, 95, 97, 100, 100, 41, 3, 0, 0, 0, 0, 0, 0, - 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, - 99, 108, 111, 110, 101, 39, 3, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, 100, 101, 115, 101, 114, 169, 92, - 0, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 102, 113, 49, 50, 95, 100, 105, 118, 20, 228, 7, 0, 0, 0, 0, 0, 41, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, 101, 113, 183, - 8, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, - 52, 95, 102, 113, 49, 50, 95, 102, 114, 111, 109, 95, 117, 54, 52, 98, 10, 0, 0, 0, - 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, - 49, 50, 95, 105, 110, 118, 219, 20, 6, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, 109, 117, 108, 79, 206, 1, - 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, - 102, 113, 49, 50, 95, 110, 101, 103, 142, 9, 0, 0, 0, 0, 0, 0, 42, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, 111, 110, 101, 38, - 0, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, - 52, 95, 102, 113, 49, 50, 95, 112, 111, 119, 95, 117, 50, 53, 54, 226, 235, 28, 2, 0, - 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, - 49, 50, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 62, 84, 0, 0, 0, 0, 0, 0, - 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, - 115, 113, 117, 97, 114, 101, 177, 80, 1, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 49, 50, 95, 115, 117, 98, 229, 21, 0, - 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, - 102, 113, 49, 50, 95, 122, 101, 114, 111, 38, 0, 0, 0, 0, 0, 0, 0, 40, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 97, 100, 100, 35, 3, - 0, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 102, 113, 95, 99, 108, 111, 110, 101, 24, 3, 0, 0, 0, 0, 0, 0, 42, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 100, 101, 115, 101, 114, - 160, 12, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, - 53, 52, 95, 102, 113, 95, 100, 105, 118, 223, 50, 3, 0, 0, 0, 0, 0, 39, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 101, 113, 35, 3, 0, - 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, - 102, 113, 95, 102, 114, 111, 109, 95, 117, 54, 52, 38, 10, 0, 0, 0, 0, 0, 0, 40, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, - 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 105, 110, 118, - 6, 48, 3, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, - 53, 52, 95, 102, 113, 95, 109, 117, 108, 55, 7, 0, 0, 0, 0, 0, 0, 40, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 110, 101, 103, 24, 3, - 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 102, 113, 95, 111, 110, 101, 38, 0, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 112, 111, 119, 95, 117, 50, 53, - 54, 106, 214, 5, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, - 50, 53, 52, 95, 102, 113, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 159, 18, 0, 0, - 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, - 113, 95, 115, 113, 117, 97, 114, 101, 24, 3, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 113, 95, 115, 117, 98, 106, 4, 0, - 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, - 102, 113, 95, 122, 101, 114, 111, 38, 0, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 97, 100, 100, 36, 3, 0, 0, - 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, - 114, 95, 100, 101, 115, 101, 114, 1, 12, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 100, 105, 118, 113, 106, 3, 0, - 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, - 114, 95, 101, 113, 39, 3, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 102, 114, 111, 109, 95, 117, 54, 52, 174, 9, - 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 102, 114, 95, 105, 110, 118, 8, 100, 3, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 109, 117, 108, 21, 7, 0, 0, - 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, - 114, 95, 110, 101, 103, 24, 3, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 111, 110, 101, 0, 0, 0, 0, 0, 0, - 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, - 115, 101, 114, 105, 97, 108, 105, 122, 101, 124, 18, 0, 0, 0, 0, 0, 0, 43, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 115, 113, 117, 97, 114, - 101, 24, 3, 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, - 50, 53, 52, 95, 102, 114, 95, 115, 117, 98, 114, 7, 0, 0, 0, 0, 0, 0, 41, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, - 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 102, 114, 95, 122, 101, 114, 111, - 38, 0, 0, 0, 0, 0, 0, 0, 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, - 53, 52, 95, 103, 49, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 99, - 111, 109, 112, 89, 230, 65, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, - 98, 110, 50, 53, 52, 95, 103, 49, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, - 114, 95, 117, 110, 99, 111, 109, 112, 240, 96, 60, 0, 0, 0, 0, 0, 58, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, 95, 97, 102, 102, 105, 110, 101, - 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 95, 99, 111, 109, 112, 65, 32, 0, 0, 0, - 0, 0, 0, 60, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, - 95, 97, 102, 102, 105, 110, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 95, 117, 110, - 99, 111, 109, 112, 59, 42, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 110, 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 97, 100, 100, 118, 76, - 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 103, 49, 95, 112, 114, 111, 106, 95, 100, 111, 117, 98, 108, 101, 184, 45, 0, 0, 0, - 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, - 95, 112, 114, 111, 106, 95, 101, 113, 17, 38, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 103, - 101, 110, 101, 114, 97, 116, 111, 114, 38, 0, 0, 0, 0, 0, 0, 0, 50, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 105, - 110, 102, 105, 110, 105, 116, 121, 38, 0, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 110, 101, - 103, 38, 0, 0, 0, 0, 0, 0, 0, 52, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, - 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 115, 99, 97, 108, 97, 114, 95, 109, - 117, 108, 219, 50, 74, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, - 110, 50, 53, 52, 95, 103, 49, 95, 112, 114, 111, 106, 95, 115, 117, 98, 192, 76, 0, 0, - 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, - 49, 95, 112, 114, 111, 106, 95, 116, 111, 95, 97, 102, 102, 105, 110, 101, 141, 4, 0, 0, - 0, 0, 0, 0, 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, - 50, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 99, 111, 109, 112, 210, - 229, 189, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, - 52, 95, 103, 50, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 117, 110, - 99, 111, 109, 112, 157, 44, 170, 0, 0, 0, 0, 0, 58, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 97, 102, 102, 105, 110, 101, 95, 115, 101, 114, - 105, 97, 108, 105, 122, 101, 95, 99, 111, 109, 112, 177, 49, 0, 0, 0, 0, 0, 0, 60, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, - 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 97, 102, 102, - 105, 110, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 95, 117, 110, 99, 111, 109, 112, - 185, 70, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, - 53, 52, 95, 103, 50, 95, 112, 114, 111, 106, 95, 97, 100, 100, 123, 228, 0, 0, 0, 0, - 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, - 112, 114, 111, 106, 95, 100, 111, 117, 98, 108, 101, 17, 114, 0, 0, 0, 0, 0, 0, 44, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, - 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 112, 114, 111, - 106, 95, 101, 113, 125, 101, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 112, 114, 111, 106, 95, 103, 101, 110, 101, 114, - 97, 116, 111, 114, 38, 0, 0, 0, 0, 0, 0, 0, 50, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 112, 114, 111, 106, 95, 105, 110, 102, 105, 110, - 105, 116, 121, 38, 0, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, - 98, 110, 50, 53, 52, 95, 103, 50, 95, 112, 114, 111, 106, 95, 110, 101, 103, 38, 0, 0, - 0, 0, 0, 0, 0, 52, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, - 103, 50, 95, 112, 114, 111, 106, 95, 115, 99, 97, 108, 97, 114, 95, 109, 117, 108, 204, 65, - 214, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 103, 50, 95, 112, 114, 111, 106, 95, 115, 117, 98, 253, 230, 0, 0, 0, 0, 0, 0, - 51, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 103, 50, 95, 112, 114, - 111, 106, 95, 116, 111, 95, 97, 102, 102, 105, 110, 101, 212, 130, 3, 0, 0, 0, 0, 0, - 52, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 109, 117, 108, 116, 105, - 95, 112, 97, 105, 114, 105, 110, 103, 95, 98, 97, 115, 101, 134, 104, 102, 1, 0, 0, 0, - 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, - 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, 95, 109, 117, 108, 116, - 105, 95, 112, 97, 105, 114, 105, 110, 103, 95, 112, 101, 114, 95, 112, 97, 105, 114, 87, 168, - 189, 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 110, 50, 53, 52, - 95, 112, 97, 105, 114, 105, 110, 103, 205, 32, 76, 2, 0, 0, 0, 0, 46, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, - 97, 100, 100, 30, 26, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, - 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 99, 108, 111, 110, 101, - 7, 3, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, - 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 100, 101, 115, 101, 114, 137, 160, 0, - 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, - 51, 56, 49, 95, 102, 113, 49, 50, 95, 100, 105, 118, 132, 17, 14, 0, 0, 0, 0, 0, - 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, - 113, 49, 50, 95, 101, 113, 108, 10, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 102, 114, - 111, 109, 95, 117, 54, 52, 240, 12, 0, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 105, 110, - 118, 98, 63, 11, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, - 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 109, 117, 108, 84, 204, 2, 0, - 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, - 56, 49, 95, 102, 113, 49, 50, 95, 110, 101, 103, 245, 16, 0, 0, 0, 0, 0, 0, 46, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, - 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, - 49, 50, 95, 111, 110, 101, 40, 0, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 112, 111, - 119, 95, 117, 50, 53, 54, 216, 136, 54, 3, 0, 0, 0, 0, 52, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 115, 101, - 114, 105, 97, 108, 105, 122, 101, 254, 115, 0, 0, 0, 0, 0, 0, 49, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 115, - 113, 117, 97, 114, 101, 169, 248, 1, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 115, 117, 98, - 62, 25, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, - 49, 50, 95, 51, 56, 49, 95, 102, 113, 49, 50, 95, 122, 101, 114, 111, 7, 3, 0, 0, - 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, - 56, 49, 95, 102, 114, 95, 97, 100, 100, 7, 3, 0, 0, 0, 0, 0, 0, 46, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 100, - 101, 115, 101, 114, 204, 10, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 100, 105, 118, 133, 85, 3, - 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, - 51, 56, 49, 95, 102, 114, 95, 101, 113, 11, 3, 0, 0, 0, 0, 0, 0, 49, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 102, - 114, 111, 109, 95, 117, 54, 52, 23, 7, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 105, 110, 118, - 154, 73, 3, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, - 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 109, 117, 108, 53, 7, 0, 0, 0, 0, 0, - 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, - 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, - 102, 114, 95, 110, 101, 103, 14, 3, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, - 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 111, 110, 101, 7, - 3, 0, 0, 0, 0, 0, 0, 50, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, - 50, 95, 51, 56, 49, 95, 102, 114, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 214, 15, - 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, - 95, 51, 56, 49, 95, 102, 114, 95, 115, 113, 117, 97, 114, 101, 210, 6, 0, 0, 0, 0, - 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, - 95, 102, 114, 95, 115, 117, 98, 42, 4, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 102, 114, 95, 122, 101, 114, - 111, 7, 3, 0, 0, 0, 0, 0, 0, 58, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, - 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, - 115, 101, 114, 95, 99, 111, 109, 112, 101, 192, 57, 0, 0, 0, 0, 0, 60, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 97, 102, - 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 117, 110, 99, 111, 109, 112, 233, 107, 40, - 0, 0, 0, 0, 0, 62, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, - 51, 56, 49, 95, 103, 49, 95, 97, 102, 102, 105, 110, 101, 95, 115, 101, 114, 105, 97, 108, - 105, 122, 101, 95, 99, 111, 109, 112, 235, 28, 0, 0, 0, 0, 0, 0, 64, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 97, 102, - 102, 105, 110, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 95, 117, 110, 99, 111, 109, - 112, 239, 34, 0, 0, 0, 0, 0, 0, 49, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, - 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 97, 100, 100, 42, - 155, 0, 0, 0, 0, 0, 0, 52, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, - 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 100, 111, 117, 98, 108, 101, - 150, 75, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, - 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 101, 113, 76, 72, 0, - 0, 0, 0, 0, 0, 55, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, - 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 103, 101, 110, 101, 114, 97, 116, 111, - 114, 40, 0, 0, 0, 0, 0, 0, 0, 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, - 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 105, 110, 102, 105, - 110, 105, 116, 121, 40, 0, 0, 0, 0, 0, 0, 0, 49, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, - 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 110, - 101, 103, 40, 0, 0, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, - 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, 115, 99, 97, - 108, 97, 114, 95, 109, 117, 108, 47, 140, 141, 0, 0, 0, 0, 0, 49, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, - 106, 95, 115, 117, 98, 16, 160, 0, 0, 0, 0, 0, 0, 55, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 49, 95, 112, 114, 111, 106, 95, - 116, 111, 95, 97, 102, 102, 105, 110, 101, 252, 201, 6, 0, 0, 0, 0, 0, 58, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 97, - 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 99, 111, 109, 112, 73, 141, 115, 0, - 0, 0, 0, 0, 60, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, - 56, 49, 95, 103, 50, 95, 97, 102, 102, 105, 110, 101, 95, 100, 101, 115, 101, 114, 95, 117, - 110, 99, 111, 109, 112, 138, 25, 57, 0, 0, 0, 0, 0, 62, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 97, 102, 102, 105, 110, - 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 95, 99, 111, 109, 112, 129, 48, 0, 0, - 0, 0, 0, 0, 64, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, - 56, 49, 95, 103, 50, 95, 97, 102, 102, 105, 110, 101, 95, 115, 101, 114, 105, 97, 108, 105, - 122, 101, 95, 117, 110, 99, 111, 109, 112, 141, 60, 0, 0, 0, 0, 0, 0, 49, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 112, - 114, 111, 106, 95, 97, 100, 100, 66, 209, 1, 0, 0, 0, 0, 0, 52, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, - 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 112, 114, 111, - 106, 95, 100, 111, 117, 98, 108, 101, 20, 213, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, - 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 112, 114, - 111, 106, 95, 101, 113, 157, 217, 0, 0, 0, 0, 0, 0, 55, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 112, 114, 111, 106, 95, - 103, 101, 110, 101, 114, 97, 116, 111, 114, 40, 0, 0, 0, 0, 0, 0, 0, 54, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, 112, - 114, 111, 106, 95, 105, 110, 102, 105, 110, 105, 116, 121, 40, 0, 0, 0, 0, 0, 0, 0, - 49, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, - 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, - 50, 95, 112, 114, 111, 106, 95, 110, 101, 103, 40, 0, 0, 0, 0, 0, 0, 0, 56, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, - 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 103, 50, 95, - 112, 114, 111, 106, 95, 115, 99, 97, 108, 97, 114, 95, 109, 117, 108, 243, 43, 166, 1, 0, - 0, 0, 0, 49, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, - 49, 95, 103, 50, 95, 112, 114, 111, 106, 95, 115, 117, 98, 250, 215, 1, 0, 0, 0, 0, - 0, 55, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, - 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, - 103, 50, 95, 112, 114, 111, 106, 95, 116, 111, 95, 97, 102, 102, 105, 110, 101, 78, 58, 7, - 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, - 51, 56, 49, 95, 109, 117, 108, 116, 105, 95, 112, 97, 105, 114, 105, 110, 103, 95, 98, 97, - 115, 101, 249, 190, 248, 1, 0, 0, 0, 0, 60, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 98, - 108, 115, 49, 50, 95, 51, 56, 49, 95, 109, 117, 108, 116, 105, 95, 112, 97, 105, 114, 105, - 110, 103, 95, 112, 101, 114, 95, 112, 97, 105, 114, 15, 43, 2, 1, 0, 0, 0, 0, 45, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, - 98, 114, 97, 46, 97, 114, 107, 95, 98, 108, 115, 49, 50, 95, 51, 56, 49, 95, 112, 97, - 105, 114, 105, 110, 103, 104, 245, 63, 3, 0, 0, 0, 0, 63, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, - 107, 95, 104, 50, 99, 95, 98, 108, 115, 49, 50, 51, 56, 49, 103, 49, 95, 120, 109, 100, - 95, 115, 104, 97, 50, 53, 54, 95, 115, 115, 119, 117, 95, 98, 97, 115, 101, 222, 103, 182, - 0, 0, 0, 0, 0, 71, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 104, 50, 99, 95, 98, 108, - 115, 49, 50, 51, 56, 49, 103, 49, 95, 120, 109, 100, 95, 115, 104, 97, 50, 53, 54, 95, - 115, 115, 119, 117, 95, 112, 101, 114, 95, 109, 115, 103, 95, 98, 121, 116, 101, 176, 0, 0, - 0, 0, 0, 0, 0, 63, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 108, 103, 101, 98, 114, 97, 46, 97, 114, 107, 95, 104, 50, 99, 95, 98, 108, - 115, 49, 50, 51, 56, 49, 103, 50, 95, 120, 109, 100, 95, 115, 104, 97, 50, 53, 54, 95, - 115, 115, 119, 117, 95, 98, 97, 115, 101, 19, 232, 123, 1, 0, 0, 0, 0, 71, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 108, 103, 101, 98, 114, - 97, 46, 97, 114, 107, 95, 104, 50, 99, 95, 98, 108, 115, 49, 50, 51, 56, 49, 103, 50, - 95, 120, 109, 100, 95, 115, 104, 97, 50, 53, 54, 95, 115, 115, 119, 117, 95, 112, 101, 114, - 95, 109, 115, 103, 95, 98, 121, 116, 101, 176, 0, 0, 0, 0, 0, 0, 0, 29, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, - 56, 49, 46, 98, 97, 115, 101, 39, 2, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, - 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, 100, 101, 115, 101, 114, 105, 97, 108, - 105, 122, 101, 44, 29, 6, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, - 95, 112, 117, 98, 107, 101, 121, 95, 97, 103, 103, 114, 101, 103, 97, 116, 101, 79, 60, 0, - 0, 0, 0, 0, 0, 50, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, - 121, 95, 115, 117, 98, 103, 114, 111, 117, 112, 95, 99, 104, 101, 99, 107, 248, 192, 20, 0, - 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, 100, 101, - 115, 101, 114, 105, 97, 108, 105, 122, 101, 200, 115, 12, 0, 0, 0, 0, 0, 42, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, - 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, 97, 103, 103, 114, 101, 103, 97, 116, 101, - 73, 167, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, - 103, 95, 115, 117, 98, 103, 114, 111, 117, 112, 95, 99, 104, 101, 99, 107, 126, 212, 25, 0, - 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, 118, 101, - 114, 105, 102, 121, 76, 239, 219, 1, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, - 114, 95, 112, 111, 112, 95, 118, 101, 114, 105, 102, 121, 144, 189, 65, 2, 0, 0, 0, 0, - 36, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, - 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 112, 97, 105, 114, 105, 110, 103, 44, 24, 225, - 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 109, 115, 103, 95, 104, - 97, 115, 104, 105, 110, 103, 112, 97, 86, 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, - 112, 101, 114, 95, 98, 121, 116, 101, 95, 104, 97, 115, 104, 105, 110, 103, 183, 0, 0, 0, - 0, 0, 0, 0, 30, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 98, 97, 115, 101, 39, 2, 0, 0, 0, - 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, - 95, 100, 101, 115, 101, 114, 105, 97, 108, 105, 122, 101, 168, 33, 2, 0, 0, 0, 0, 0, - 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, - 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, 115, 109, - 97, 108, 108, 95, 111, 114, 100, 101, 114, 95, 99, 104, 101, 99, 107, 46, 91, 0, 0, 0, - 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 115, 105, 103, 95, 100, 101, - 115, 101, 114, 105, 97, 108, 105, 122, 101, 98, 5, 0, 0, 0, 0, 0, 0, 47, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, 116, - 117, 114, 101, 46, 112, 101, 114, 95, 115, 105, 103, 95, 115, 116, 114, 105, 99, 116, 95, 118, - 101, 114, 105, 102, 121, 244, 249, 14, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, - 112, 101, 114, 95, 109, 115, 103, 95, 104, 97, 115, 104, 105, 110, 103, 95, 98, 97, 115, 101, - 134, 46, 0, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 109, - 115, 103, 95, 98, 121, 116, 101, 95, 104, 97, 115, 104, 105, 110, 103, 220, 0, 0, 0, 0, - 0, 0, 0, 30, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 115, 101, 99, 112, 50, 53, 54, 107, 49, 46, 98, 97, 115, 101, 39, 2, 0, 0, 0, 0, - 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, - 101, 99, 112, 50, 53, 54, 107, 49, 46, 101, 99, 100, 115, 97, 95, 114, 101, 99, 111, 118, - 101, 114, 152, 78, 90, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, - 98, 97, 115, 101, 112, 111, 105, 110, 116, 95, 109, 117, 108, 0, 46, 7, 0, 0, 0, 0, - 0, 49, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, - 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 98, 97, 115, 101, 112, 111, 105, 110, 116, - 95, 100, 111, 117, 98, 108, 101, 95, 109, 117, 108, 32, 174, 24, 0, 0, 0, 0, 0, 38, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, - 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 97, 100, 100, 168, 30, - 0, 0, 0, 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, - 116, 95, 99, 108, 111, 110, 101, 39, 2, 0, 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, - 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 99, 111, 109, 112, 114, 101, 115, 115, 96, - 62, 2, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, - 110, 116, 95, 100, 101, 99, 111, 109, 112, 114, 101, 115, 115, 142, 69, 2, 0, 0, 0, 0, - 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, - 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 101, 113, 117, - 97, 108, 115, 6, 33, 0, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 112, 111, 105, 110, 116, 95, 102, 114, 111, 109, 95, 54, 52, 95, 117, 110, 105, 102, 111, - 114, 109, 95, 98, 121, 116, 101, 115, 74, 146, 4, 0, 0, 0, 0, 0, 43, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 105, 100, 101, 110, 116, 105, 116, 121, - 39, 2, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, - 105, 110, 116, 95, 109, 117, 108, 68, 107, 26, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, - 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 100, 111, 117, 98, 108, 101, 95, 109, 117, - 108, 83, 136, 28, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, - 111, 105, 110, 116, 95, 110, 101, 103, 43, 5, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 115, 117, 98, 149, 30, 0, 0, 0, - 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 112, - 97, 114, 115, 101, 95, 97, 114, 103, 39, 2, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 115, 104, 97, 53, 49, 50, 95, - 112, 101, 114, 95, 98, 121, 116, 101, 220, 0, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 115, 104, 97, 53, 49, 50, 95, - 112, 101, 114, 95, 104, 97, 115, 104, 134, 46, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 97, 100, 100, 14, 11, 0, 0, - 0, 0, 0, 0, 57, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, - 95, 114, 101, 100, 117, 99, 101, 100, 95, 102, 114, 111, 109, 95, 51, 50, 95, 98, 121, 116, - 101, 115, 49, 10, 0, 0, 0, 0, 0, 0, 57, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, - 115, 99, 97, 108, 97, 114, 95, 117, 110, 105, 102, 111, 114, 109, 95, 102, 114, 111, 109, 95, - 54, 52, 95, 98, 121, 116, 101, 115, 224, 17, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 102, 114, 111, 109, 95, 117, 49, - 50, 56, 131, 2, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, - 115, 99, 97, 108, 97, 114, 95, 102, 114, 111, 109, 95, 117, 54, 52, 131, 2, 0, 0, 0, - 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, - 105, 110, 118, 101, 114, 116, 136, 43, 6, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, - 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 105, 115, 95, 99, 97, 110, 111, 110, 105, - 99, 97, 108, 131, 16, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 115, 99, 97, 108, 97, 114, 95, 109, 117, 108, 74, 15, 0, 0, 0, 0, 0, 0, 39, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, - 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 110, 101, 103, 105, - 10, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, - 108, 97, 114, 95, 115, 117, 98, 56, 15, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, - 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 112, 97, 114, 115, 101, 95, 97, 114, - 103, 39, 2, 0, 0, 0, 0, 0, 0, 34, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 105, 112, 95, 104, 97, 115, 104, 46, - 98, 97, 115, 101, 92, 14, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 105, 112, 95, 104, 97, - 115, 104, 46, 112, 101, 114, 95, 98, 121, 116, 101, 73, 0, 0, 0, 0, 0, 0, 0, 35, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, - 46, 107, 101, 99, 99, 97, 107, 50, 53, 54, 46, 98, 97, 115, 101, 112, 57, 0, 0, 0, - 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 104, 97, 115, 104, 46, 107, 101, 99, 99, 97, 107, 50, 53, 54, 46, 112, 101, 114, 95, 98, - 121, 116, 101, 165, 0, 0, 0, 0, 0, 0, 0, 33, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 98, 117, 108, 108, 101, 116, 112, 114, 111, 111, 102, 115, - 46, 98, 97, 115, 101, 219, 248, 179, 0, 0, 0, 0, 0, 54, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 117, 108, 108, 101, 116, 112, 114, 111, 111, - 102, 115, 46, 112, 101, 114, 95, 98, 105, 116, 95, 114, 97, 110, 103, 101, 112, 114, 111, 111, - 102, 95, 118, 101, 114, 105, 102, 121, 221, 82, 15, 0, 0, 0, 0, 0, 60, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 117, 108, 108, 101, 116, 112, - 114, 111, 111, 102, 115, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 114, 97, 110, 103, 101, - 112, 114, 111, 111, 102, 95, 100, 101, 115, 101, 114, 105, 97, 108, 105, 122, 101, 121, 0, 0, - 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, 95, 111, 102, 46, - 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 58, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, 116, - 121, 112, 101, 95, 111, 102, 46, 112, 101, 114, 95, 97, 98, 115, 116, 114, 97, 99, 116, 95, - 109, 101, 109, 111, 114, 121, 95, 117, 110, 105, 116, 18, 0, 0, 0, 0, 0, 0, 0, 40, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, - 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, 95, 110, 97, 109, 101, 46, 98, 97, 115, 101, - 78, 4, 0, 0, 0, 0, 0, 0, 60, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, 95, - 110, 97, 109, 101, 46, 112, 101, 114, 95, 97, 98, 115, 116, 114, 97, 99, 116, 95, 109, 101, - 109, 111, 114, 121, 95, 117, 110, 105, 116, 18, 0, 0, 0, 0, 0, 0, 0, 39, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, 105, - 110, 102, 111, 46, 99, 104, 97, 105, 110, 95, 105, 100, 46, 98, 97, 115, 101, 39, 2, 0, - 0, 0, 0, 0, 0, 34, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 53, 49, 50, 46, 98, 97, 115, 101, - 134, 46, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 53, 49, 50, 46, 112, - 101, 114, 95, 98, 121, 116, 101, 220, 0, 0, 0, 0, 0, 0, 0, 34, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 104, 97, - 51, 95, 53, 49, 50, 46, 98, 97, 115, 101, 158, 64, 0, 0, 0, 0, 0, 0, 38, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, - 115, 104, 97, 51, 95, 53, 49, 50, 46, 112, 101, 114, 95, 98, 121, 116, 101, 183, 0, 0, - 0, 0, 0, 0, 0, 35, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 104, 97, 115, 104, 46, 114, 105, 112, 101, 109, 100, 49, 54, 48, 46, 98, 97, 115, - 101, 20, 43, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 114, 105, 112, 101, 109, 100, 49, 54, 48, - 46, 112, 101, 114, 95, 98, 121, 116, 101, 183, 0, 0, 0, 0, 0, 0, 0, 37, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 98, - 108, 97, 107, 101, 50, 98, 95, 50, 53, 54, 46, 98, 97, 115, 101, 33, 25, 0, 0, 0, - 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 104, 97, 115, 104, 46, 98, 108, 97, 107, 101, 50, 98, 95, 50, 53, 54, 46, 112, 101, 114, - 95, 98, 121, 116, 101, 55, 0, 0, 0, 0, 0, 0, 0, 36, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 117, 116, 105, 108, 46, 102, 114, 111, 109, 95, - 98, 121, 116, 101, 115, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 40, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 117, 116, 105, 108, 46, - 102, 114, 111, 109, 95, 98, 121, 116, 101, 115, 46, 112, 101, 114, 95, 98, 121, 116, 101, 18, - 0, 0, 0, 0, 0, 0, 0, 53, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 99, 111, 110, 116, - 101, 120, 116, 46, 103, 101, 116, 95, 116, 120, 110, 95, 104, 97, 115, 104, 46, 98, 97, 115, - 101, 223, 2, 0, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 99, 111, - 110, 116, 101, 120, 116, 46, 103, 101, 116, 95, 115, 99, 114, 105, 112, 116, 95, 104, 97, 115, - 104, 46, 98, 97, 115, 101, 223, 2, 0, 0, 0, 0, 0, 0, 64, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 114, 97, 110, 115, 97, 99, 116, 105, - 111, 110, 95, 99, 111, 110, 116, 101, 120, 116, 46, 103, 101, 110, 101, 114, 97, 116, 101, 95, - 117, 110, 105, 113, 117, 101, 95, 97, 100, 100, 114, 101, 115, 115, 46, 98, 97, 115, 101, 112, - 57, 0, 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 99, 111, 100, 101, 46, 114, 101, 113, 117, 101, 115, 116, 95, 112, 117, 98, - 108, 105, 115, 104, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 45, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 99, 111, 100, 101, 46, 114, - 101, 113, 117, 101, 115, 116, 95, 112, 117, 98, 108, 105, 115, 104, 46, 112, 101, 114, 95, 98, - 121, 116, 101, 7, 0, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 101, 118, 101, 110, 116, 46, 119, 114, 105, 116, 101, 95, - 116, 111, 95, 101, 118, 101, 110, 116, 95, 115, 116, 111, 114, 101, 46, 98, 97, 115, 101, 38, - 78, 0, 0, 0, 0, 0, 0, 67, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 101, 118, 101, 110, 116, 46, 119, 114, 105, 116, 101, 95, 116, 111, 95, 101, - 118, 101, 110, 116, 95, 115, 116, 111, 114, 101, 46, 112, 101, 114, 95, 97, 98, 115, 116, 114, - 97, 99, 116, 95, 109, 101, 109, 111, 114, 121, 95, 117, 110, 105, 116, 61, 0, 0, 0, 0, - 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 115, 116, 97, 116, 101, 95, 115, 116, 111, 114, 97, 103, 101, 46, 103, 101, 116, 95, 117, 115, - 97, 103, 101, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 35, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, - 116, 111, 114, 46, 97, 100, 100, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, - 36, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, - 114, 101, 103, 97, 116, 111, 114, 46, 114, 101, 97, 100, 46, 98, 97, 115, 101, 78, 4, 0, - 0, 0, 0, 0, 0, 35, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 115, 117, 98, 46, 98, 97, 115, - 101, 78, 4, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 100, 101, 115, - 116, 114, 111, 121, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, 54, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, - 97, 116, 111, 114, 95, 102, 97, 99, 116, 111, 114, 121, 46, 110, 101, 119, 95, 97, 103, 103, - 114, 101, 103, 97, 116, 111, 114, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, 0, - 52, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, - 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 99, 114, 101, 97, 116, 101, 95, 97, 103, - 103, 114, 101, 103, 97, 116, 111, 114, 46, 98, 97, 115, 101, 46, 7, 0, 0, 0, 0, 0, - 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, - 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 116, 114, 121, 95, 97, 100, 100, 46, - 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, - 118, 50, 46, 116, 114, 121, 95, 115, 117, 98, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, - 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 114, 101, 97, 100, 46, 98, - 97, 115, 101, 157, 8, 0, 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, - 50, 46, 115, 110, 97, 112, 115, 104, 111, 116, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, - 0, 0, 0, 50, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 99, 114, 101, 97, 116, 101, - 95, 115, 110, 97, 112, 115, 104, 111, 116, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, - 0, 0, 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 99, 114, 101, 97, 116, 101, 95, - 115, 110, 97, 112, 115, 104, 111, 116, 46, 112, 101, 114, 95, 98, 121, 116, 101, 3, 0, 0, - 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 99, 111, 112, 121, - 95, 115, 110, 97, 112, 115, 104, 111, 116, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, - 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 114, 101, 97, 100, 95, 115, 110, - 97, 112, 115, 104, 111, 116, 46, 98, 97, 115, 101, 157, 8, 0, 0, 0, 0, 0, 0, 48, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, - 101, 103, 97, 116, 111, 114, 95, 118, 50, 46, 115, 116, 114, 105, 110, 103, 95, 99, 111, 110, - 99, 97, 116, 46, 98, 97, 115, 101, 78, 4, 0, 0, 0, 0, 0, 0, 52, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, - 116, 111, 114, 95, 118, 50, 46, 115, 116, 114, 105, 110, 103, 95, 99, 111, 110, 99, 97, 116, - 46, 112, 101, 114, 95, 98, 121, 116, 101, 3, 0, 0, 0, 0, 0, 0, 0, 37, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 111, 98, 106, 101, 99, 116, - 46, 101, 120, 105, 115, 116, 115, 95, 97, 116, 46, 98, 97, 115, 101, 151, 3, 0, 0, 0, - 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 111, 98, 106, 101, 99, 116, 46, 101, 120, 105, 115, 116, 115, 95, 97, 116, 46, 112, 101, 114, - 95, 98, 121, 116, 101, 95, 108, 111, 97, 100, 101, 100, 183, 0, 0, 0, 0, 0, 0, 0, - 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 111, 98, 106, - 101, 99, 116, 46, 101, 120, 105, 115, 116, 115, 95, 97, 116, 46, 112, 101, 114, 95, 105, 116, - 101, 109, 95, 108, 111, 97, 100, 101, 100, 190, 5, 0, 0, 0, 0, 0, 0, 40, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 116, 114, 105, 110, 103, - 95, 117, 116, 105, 108, 115, 46, 102, 111, 114, 109, 97, 116, 46, 98, 97, 115, 101, 78, 4, - 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 115, 116, 114, 105, 110, 103, 95, 117, 116, 105, 108, 115, 46, 102, 111, 114, 109, - 97, 116, 46, 112, 101, 114, 95, 98, 121, 116, 101, 3, 0, 0, 0, 0, 0, 0, 0, - ]; - - gas_schedule::set_for_next_epoch(framework_signer, gas_schedule_blob); - aptos_governance::reconfigure(framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/release.yaml b/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/release.yaml deleted file mode 100644 index 59a4fdfbd3316..0000000000000 --- a/aptos-move/aptos-release-builder/data/example-release-with-randomness-framework/release.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -remote_endpoint: ~ -name: example-after-randomness-enabled -proposals: - - name: all_configs - metadata: - title: "" - description: "" - source_code_url: "" - discussion_url: "" - execution_mode: RootSigner - update_sequence: - - FeatureFlag: - enabled: - - bls12381_structures - disabled: - - bn254_structures - - Consensus: - V3: - alg: - Jolteon: - main: - decoupled_execution: true - back_pressure_limit: 10 - exclude_round: 40 - proposer_election_type: - leader_reputation: - proposer_and_voter_v2: - active_weight: 1000 - inactive_weight: 10 - failed_weight: 1 - failure_threshold_percent: 10 - proposer_window_num_validators_multiplier: 10 - voter_window_num_validators_multiplier: 1 - weight_by_voting_power: true - use_history_from_previous_epoch_max_count: 5 - max_failed_authors_to_store: 10 - quorum_store_enabled: true - vtxn: - V1: - per_block_limit_txn_count: 3 - per_block_limit_total_bytes: 2097152 - - Execution: - V4: - transaction_shuffler_type: - fairness: - sender_conflict_window_size: 256 - module_conflict_window_size: 2 - entry_fun_conflict_window_size: 3 - block_gas_limit_type: - complex_limit_v1: - effective_block_gas_limit: 80001 - execution_gas_effective_multiplier: 1 - io_gas_effective_multiplier: 1 - conflict_penalty_window: 6 - use_granular_resource_group_conflicts: false - use_module_publishing_block_conflict: true - block_output_limit: 12582912 - include_user_txn_size_in_block_output: true - add_block_limit_outcome_onchain: false - transaction_deduper_type: txn_hash_and_authenticator_v1 - - Version: - major: 999 - - OidcProviderOps: - - Upsert: - issuer: "https://accounts.google.com" - config_url: "https://accounts.google.com/.well-known/openid-configuration" - - Remove: - issuer: "https://www.facebook.com" - keep_observed_jwks: true - - DefaultGasWithOverride: - - name: "txn.max_execution_gas" - value: 9999999998 diff --git a/aptos-move/aptos-release-builder/data/example.yaml b/aptos-move/aptos-release-builder/data/example.yaml deleted file mode 100644 index 8d50c14abcffc..0000000000000 --- a/aptos-move/aptos-release-builder/data/example.yaml +++ /dev/null @@ -1,69 +0,0 @@ ---- -remote_endpoint: ~ -proposals: - - name: framework - metadata: - title: "" - description: "" - source_code_url: "" - discussion_url: "" - execution_mode: MultiStep - update_sequence: - - Framework: - bytecode_version: 6 - git_hash: ~ - - name: gas - metadata: - title: "" - description: "" - source_code_url: "" - discussion_url: "" - execution_mode: MultiStep - update_sequence: - - DefaultGas - - name: feature_flags - metadata: - title: "" - description: "" - source_code_url: "" - discussion_url: "" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - code_dependency_check - - treat_friend_as_private - - sha512_and_ripe_md160_natives - - aptos_std_chain_id_natives - - v_m_binary_format_v6 - - multi_ed25519_pk_validate_v2_natives - - blake2b256_native - - resource_groups - - multisig_accounts - - delegation_pools - - ed25519_pubkey_validate_return_false_wrong_length - - struct_constructors - - cryptography_algebra_natives - - bls12381_structures - disabled: [] - - Consensus: - V1: - decoupled_execution: true - back_pressure_limit: 10 - exclude_round: 40 - proposer_election_type: - leader_reputation: - proposer_and_voter_v2: - active_weight: 1000 - inactive_weight: 10 - failed_weight: 1 - failure_threshold_percent: 10 - proposer_window_num_validators_multiplier: 10 - voter_window_num_validators_multiplier: 1 - weight_by_voting_power: true - use_history_from_previous_epoch_max_count: 5 - max_failed_authors_to_store: 10 - - Execution: - V1: - transaction_shuffler_type: no_shuffling - - RawScript: data/example_proposals/empty_multi_step.move diff --git a/aptos-move/aptos-release-builder/data/example_output/0-move-stdlib.move b/aptos-move/aptos-release-builder/data/example_output/0-move-stdlib.move deleted file mode 100644 index 4aad6c0290a4c..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/0-move-stdlib.move +++ /dev/null @@ -1,1423 +0,0 @@ -// Script hash: 70505204 -// Framework commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Builder commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Upgrade proposal for package `MoveStdlib` - -// source digest: 01E7CA7EB9D1303980FBC85153550F926CAF61E9FCAEB6E791CDE9B3ECFD72BA -script { - use std::vector; - use aptos_framework::aptos_governance; - use aptos_framework::code; - - fun main(core_resources: &signer){ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let code = vector::empty(); - let chunk0 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,2u8,3u8,2u8,6u8,5u8,8u8,7u8,7u8, - 15u8,13u8,8u8,28u8,32u8,12u8,60u8,4u8,0u8,0u8,0u8,1u8,0u8,1u8,1u8,0u8,1u8,6u8,9u8,0u8, - 1u8,10u8,2u8,3u8,98u8,99u8,115u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,1u8,2u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk0); - let chunk1 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,2u8,2u8,2u8,4u8,3u8,6u8,60u8,5u8, - 66u8,31u8,7u8,97u8,163u8,1u8,8u8,132u8,2u8,32u8,6u8,164u8,2u8,68u8,16u8,232u8,2u8,156u8,3u8,10u8, - 132u8,6u8,5u8,12u8,137u8,6u8,234u8,3u8,13u8,243u8,9u8,2u8,0u8,0u8,0u8,1u8,7u8,0u8,0u8,2u8, - 0u8,1u8,0u8,0u8,3u8,2u8,0u8,0u8,0u8,4u8,1u8,0u8,0u8,0u8,5u8,1u8,0u8,0u8,0u8,6u8, - 3u8,1u8,0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,9u8,0u8,4u8,0u8,0u8,10u8, - 5u8,0u8,0u8,0u8,11u8,5u8,0u8,0u8,0u8,12u8,3u8,1u8,0u8,0u8,13u8,0u8,1u8,0u8,1u8,8u8, - 0u8,1u8,3u8,2u8,3u8,3u8,2u8,3u8,8u8,0u8,1u8,1u8,2u8,8u8,0u8,8u8,0u8,0u8,4u8,1u8, - 4u8,4u8,4u8,1u8,4u8,3u8,3u8,3u8,3u8,13u8,102u8,105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8, - 116u8,51u8,50u8,12u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,51u8,50u8,4u8,99u8,101u8,105u8, - 108u8,20u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8,97u8,116u8,105u8,111u8,110u8, - 97u8,108u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8,97u8,119u8,95u8,118u8, - 97u8,108u8,117u8,101u8,15u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,117u8,54u8,52u8, - 10u8,100u8,105u8,118u8,105u8,100u8,101u8,95u8,117u8,54u8,52u8,5u8,102u8,108u8,111u8,111u8,114u8,13u8,103u8,101u8, - 116u8,95u8,114u8,97u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,7u8,105u8,115u8,95u8,122u8,101u8,114u8,111u8,3u8, - 109u8,97u8,120u8,3u8,109u8,105u8,110u8,12u8,109u8,117u8,108u8,116u8,105u8,112u8,108u8,121u8,95u8,117u8,54u8,52u8, - 5u8,114u8,111u8,117u8,110u8,100u8,5u8,118u8,97u8,108u8,117u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,2u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,2u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,4u8,16u8,255u8,255u8,255u8,255u8, - 255u8,255u8,255u8,255u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,135u8,3u8,5u8,1u8,0u8,1u8,0u8,0u8,0u8, - 0u8,0u8,12u8,69u8,68u8,69u8,78u8,79u8,77u8,73u8,78u8,65u8,84u8,79u8,82u8,33u8,84u8,104u8,101u8,32u8, - 100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8, - 32u8,119u8,97u8,115u8,32u8,122u8,101u8,114u8,111u8,2u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,9u8,69u8,68u8, - 73u8,86u8,73u8,83u8,73u8,79u8,78u8,59u8,84u8,104u8,101u8,32u8,113u8,117u8,111u8,116u8,105u8,101u8,110u8,116u8, - 32u8,118u8,97u8,108u8,117u8,101u8,32u8,119u8,111u8,117u8,108u8,100u8,32u8,98u8,101u8,32u8,116u8,111u8,111u8,32u8, - 108u8,97u8,114u8,103u8,101u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,104u8,101u8,108u8,100u8,32u8,105u8,110u8,32u8, - 97u8,32u8,96u8,117u8,54u8,52u8,96u8,3u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,77u8,85u8,76u8, - 84u8,73u8,80u8,76u8,73u8,67u8,65u8,84u8,73u8,79u8,78u8,61u8,84u8,104u8,101u8,32u8,109u8,117u8,108u8,116u8, - 105u8,112u8,108u8,105u8,101u8,100u8,32u8,118u8,97u8,108u8,117u8,101u8,32u8,119u8,111u8,117u8,108u8,100u8,32u8,98u8, - 101u8,32u8,116u8,111u8,111u8,32u8,108u8,97u8,114u8,103u8,101u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,104u8,101u8, - 108u8,100u8,32u8,105u8,110u8,32u8,97u8,32u8,96u8,117u8,54u8,52u8,96u8,4u8,0u8,1u8,0u8,0u8,0u8,0u8, - 0u8,17u8,69u8,68u8,73u8,86u8,73u8,83u8,73u8,79u8,78u8,95u8,66u8,89u8,95u8,90u8,69u8,82u8,79u8,34u8, - 65u8,32u8,100u8,105u8,118u8,105u8,115u8,105u8,111u8,110u8,32u8,98u8,121u8,32u8,122u8,101u8,114u8,111u8,32u8,119u8, - 97u8,115u8,32u8,101u8,110u8,99u8,111u8,117u8,110u8,116u8,101u8,114u8,101u8,100u8,5u8,0u8,2u8,0u8,0u8,0u8, - 0u8,0u8,19u8,69u8,82u8,65u8,84u8,73u8,79u8,95u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,82u8,65u8,78u8, - 71u8,69u8,79u8,84u8,104u8,101u8,32u8,99u8,111u8,109u8,112u8,117u8,116u8,101u8,100u8,32u8,114u8,97u8,116u8,105u8, - 111u8,32u8,119u8,104u8,101u8,110u8,32u8,99u8,111u8,110u8,118u8,101u8,114u8,116u8,105u8,110u8,103u8,32u8,116u8,111u8, - 32u8,97u8,32u8,96u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,51u8,50u8,96u8,32u8,119u8,111u8, - 117u8,108u8,100u8,32u8,98u8,101u8,32u8,117u8,110u8,114u8,101u8,112u8,114u8,101u8,115u8,101u8,110u8,116u8,97u8,98u8, - 108u8,101u8,0u8,0u8,0u8,2u8,1u8,14u8,3u8,0u8,1u8,0u8,0u8,1u8,23u8,10u8,0u8,17u8,5u8,49u8, - 32u8,47u8,12u8,1u8,14u8,0u8,16u8,0u8,20u8,10u8,1u8,33u8,4u8,15u8,11u8,1u8,49u8,32u8,48u8,2u8, - 11u8,1u8,53u8,50u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,49u8,32u8,48u8,52u8,2u8,1u8,1u8,0u8,0u8,7u8,48u8,10u8,0u8,53u8,49u8,64u8,47u8,12u8,5u8, - 11u8,1u8,53u8,49u8,32u8,47u8,12u8,4u8,10u8,4u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,15u8,5u8,17u8,7u8,0u8,39u8,11u8,5u8,11u8,4u8,26u8, - 12u8,3u8,10u8,3u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,34u8,4u8,28u8,8u8,12u8,2u8,5u8,32u8,11u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 33u8,12u8,2u8,11u8,2u8,4u8,35u8,5u8,37u8,7u8,4u8,39u8,10u8,3u8,7u8,5u8,37u8,4u8,42u8,5u8, - 44u8,7u8,4u8,39u8,11u8,3u8,52u8,18u8,0u8,2u8,2u8,1u8,0u8,0u8,6u8,3u8,11u8,0u8,18u8,0u8, - 2u8,3u8,1u8,0u8,0u8,8u8,16u8,11u8,0u8,53u8,49u8,32u8,47u8,12u8,1u8,10u8,1u8,7u8,5u8,37u8, - 4u8,10u8,5u8,12u8,7u8,4u8,39u8,11u8,1u8,52u8,18u8,0u8,2u8,4u8,1u8,0u8,0u8,8u8,29u8,14u8, - 1u8,16u8,0u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,7u8,5u8,9u8,7u8,2u8, - 39u8,11u8,0u8,53u8,49u8,32u8,47u8,14u8,1u8,16u8,0u8,20u8,53u8,26u8,12u8,2u8,10u8,2u8,7u8,5u8, - 37u8,4u8,24u8,5u8,26u8,7u8,1u8,39u8,11u8,2u8,52u8,2u8,5u8,1u8,0u8,0u8,6u8,6u8,14u8,0u8, - 16u8,0u8,20u8,49u8,32u8,48u8,2u8,6u8,1u8,0u8,0u8,6u8,4u8,14u8,0u8,16u8,0u8,20u8,2u8,7u8, - 1u8,0u8,0u8,6u8,6u8,14u8,0u8,16u8,0u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8, - 2u8,8u8,1u8,0u8,0u8,0u8,15u8,14u8,0u8,16u8,0u8,20u8,14u8,1u8,16u8,0u8,20u8,36u8,4u8,11u8, - 11u8,0u8,12u8,2u8,5u8,13u8,11u8,1u8,12u8,2u8,11u8,2u8,2u8,9u8,1u8,0u8,0u8,0u8,15u8,14u8, - 0u8,16u8,0u8,20u8,14u8,1u8,16u8,0u8,20u8,35u8,4u8,11u8,11u8,0u8,12u8,2u8,5u8,13u8,11u8,1u8, - 12u8,2u8,11u8,2u8,2u8,10u8,1u8,0u8,0u8,8u8,20u8,11u8,0u8,53u8,14u8,1u8,16u8,0u8,20u8,53u8, - 24u8,49u8,32u8,48u8,12u8,2u8,10u8,2u8,7u8,5u8,37u8,4u8,15u8,5u8,17u8,7u8,3u8,39u8,11u8,2u8, - 52u8,2u8,11u8,1u8,0u8,0u8,9u8,25u8,10u8,0u8,17u8,5u8,49u8,32u8,47u8,12u8,3u8,10u8,3u8,6u8, - 0u8,0u8,0u8,128u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,14u8,0u8,16u8,0u8,20u8,11u8,2u8,35u8,4u8, - 20u8,11u8,3u8,49u8,32u8,48u8,12u8,1u8,5u8,23u8,11u8,0u8,17u8,0u8,12u8,1u8,11u8,1u8,2u8,0u8, - 0u8,0u8, - ]; - vector::push_back(&mut code, chunk1); - let chunk2 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,2u8,3u8,2u8,10u8,5u8,12u8,3u8,7u8, - 15u8,23u8,8u8,38u8,32u8,12u8,70u8,8u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8,0u8, - 1u8,10u8,2u8,4u8,104u8,97u8,115u8,104u8,8u8,115u8,104u8,97u8,50u8,95u8,50u8,53u8,54u8,8u8,115u8,104u8, - 97u8,51u8,95u8,50u8,53u8,54u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,1u8, - 2u8,0u8,1u8,1u8,2u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk2); - let chunk3 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,2u8,3u8,2u8,138u8,1u8,4u8,140u8,1u8, - 12u8,5u8,152u8,1u8,106u8,7u8,130u8,2u8,221u8,1u8,8u8,223u8,3u8,32u8,6u8,255u8,3u8,20u8,16u8,147u8, - 4u8,162u8,1u8,12u8,181u8,5u8,191u8,6u8,0u8,0u8,0u8,1u8,0u8,1u8,1u8,0u8,0u8,2u8,2u8,3u8, - 1u8,0u8,0u8,3u8,4u8,5u8,1u8,0u8,0u8,4u8,6u8,7u8,1u8,0u8,0u8,5u8,8u8,1u8,1u8,0u8, - 0u8,6u8,1u8,8u8,1u8,0u8,0u8,7u8,6u8,9u8,1u8,0u8,0u8,8u8,10u8,1u8,1u8,0u8,0u8,9u8, - 11u8,7u8,1u8,0u8,0u8,10u8,11u8,12u8,1u8,0u8,0u8,11u8,13u8,14u8,1u8,0u8,0u8,12u8,15u8,1u8, - 1u8,0u8,0u8,13u8,4u8,14u8,1u8,0u8,0u8,14u8,13u8,1u8,1u8,0u8,0u8,15u8,0u8,1u8,1u8,0u8, - 0u8,16u8,16u8,1u8,1u8,0u8,0u8,17u8,4u8,12u8,1u8,0u8,0u8,18u8,17u8,12u8,1u8,0u8,0u8,19u8, - 14u8,8u8,1u8,0u8,0u8,20u8,16u8,1u8,1u8,0u8,0u8,21u8,4u8,14u8,1u8,0u8,0u8,22u8,4u8,8u8, - 1u8,0u8,0u8,23u8,4u8,8u8,1u8,0u8,13u8,14u8,14u8,14u8,15u8,14u8,17u8,14u8,8u8,14u8,22u8,14u8, - 2u8,7u8,10u8,9u8,0u8,10u8,9u8,0u8,0u8,2u8,6u8,10u8,9u8,0u8,3u8,1u8,6u8,9u8,0u8,2u8, - 7u8,10u8,9u8,0u8,3u8,1u8,7u8,9u8,0u8,2u8,6u8,10u8,9u8,0u8,6u8,9u8,0u8,1u8,1u8,1u8, - 10u8,9u8,0u8,2u8,1u8,3u8,3u8,7u8,10u8,9u8,0u8,3u8,9u8,0u8,1u8,6u8,10u8,9u8,0u8,1u8, - 3u8,1u8,7u8,10u8,9u8,0u8,1u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8,9u8,0u8,3u8,7u8,10u8,9u8, - 0u8,3u8,3u8,4u8,7u8,10u8,9u8,0u8,3u8,3u8,3u8,2u8,3u8,3u8,3u8,3u8,7u8,10u8,9u8,0u8, - 3u8,2u8,3u8,10u8,9u8,0u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8, - 6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,8u8,99u8, - 111u8,110u8,116u8,97u8,105u8,110u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8, - 121u8,5u8,101u8,109u8,112u8,116u8,121u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,6u8,105u8,110u8,115u8, - 101u8,114u8,116u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,8u8, - 112u8,111u8,112u8,95u8,98u8,97u8,99u8,107u8,9u8,112u8,117u8,115u8,104u8,95u8,98u8,97u8,99u8,107u8,6u8,114u8, - 101u8,109u8,111u8,118u8,101u8,7u8,114u8,101u8,118u8,101u8,114u8,115u8,101u8,14u8,114u8,101u8,118u8,101u8,114u8,115u8, - 101u8,95u8,97u8,112u8,112u8,101u8,110u8,100u8,13u8,114u8,101u8,118u8,101u8,114u8,115u8,101u8,95u8,115u8,108u8,105u8, - 99u8,101u8,6u8,114u8,111u8,116u8,97u8,116u8,101u8,12u8,114u8,111u8,116u8,97u8,116u8,101u8,95u8,115u8,108u8,105u8, - 99u8,101u8,9u8,115u8,105u8,110u8,103u8,108u8,101u8,116u8,111u8,110u8,4u8,115u8,119u8,97u8,112u8,11u8,115u8,119u8, - 97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,4u8,116u8,114u8,105u8,109u8,12u8,116u8,114u8,105u8,109u8,95u8, - 114u8,101u8,118u8,101u8,114u8,115u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8, - 8u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,141u8,1u8, - 2u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,73u8,78u8,68u8,69u8,88u8,95u8,79u8,85u8,84u8, - 95u8,79u8,70u8,95u8,66u8,79u8,85u8,78u8,68u8,83u8,42u8,84u8,104u8,101u8,32u8,105u8,110u8,100u8,101u8,120u8, - 32u8,105u8,110u8,116u8,111u8,32u8,116u8,104u8,101u8,32u8,118u8,101u8,99u8,116u8,111u8,114u8,32u8,105u8,115u8,32u8, - 111u8,117u8,116u8,32u8,111u8,102u8,32u8,98u8,111u8,117u8,110u8,100u8,115u8,1u8,0u8,2u8,0u8,0u8,0u8,0u8, - 0u8,14u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,82u8,65u8,78u8,71u8,69u8,42u8,84u8,104u8,101u8, - 32u8,105u8,110u8,100u8,101u8,120u8,32u8,105u8,110u8,116u8,111u8,32u8,116u8,104u8,101u8,32u8,118u8,101u8,99u8,116u8, - 111u8,114u8,32u8,105u8,115u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8,32u8,98u8,111u8,117u8,110u8,100u8,115u8,0u8, - 0u8,0u8,1u8,0u8,0u8,1u8,6u8,13u8,1u8,56u8,0u8,11u8,0u8,11u8,1u8,56u8,1u8,2u8,1u8,1u8, - 2u8,0u8,2u8,1u8,2u8,0u8,3u8,1u8,0u8,0u8,18u8,33u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,2u8,10u8,0u8,65u8,14u8,12u8,3u8,10u8,2u8,10u8,3u8,35u8,4u8,27u8,5u8,10u8,10u8,0u8, - 10u8,2u8,66u8,14u8,10u8,1u8,33u8,4u8,22u8,11u8,0u8,1u8,11u8,1u8,1u8,8u8,2u8,11u8,2u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,5u8,11u8,0u8,1u8,11u8,1u8,1u8,9u8, - 2u8,4u8,1u8,2u8,0u8,5u8,1u8,2u8,0u8,6u8,1u8,0u8,0u8,18u8,35u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,2u8,10u8,0u8,65u8,14u8,12u8,3u8,10u8,2u8,10u8,3u8,35u8,4u8,28u8,5u8, - 10u8,10u8,0u8,10u8,2u8,66u8,14u8,10u8,1u8,33u8,4u8,23u8,11u8,0u8,1u8,11u8,1u8,1u8,8u8,11u8, - 2u8,2u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,5u8,11u8,0u8, - 1u8,11u8,1u8,1u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,7u8,1u8,0u8,0u8,12u8, - 33u8,10u8,0u8,46u8,65u8,14u8,12u8,3u8,10u8,1u8,10u8,3u8,37u8,4u8,9u8,5u8,13u8,11u8,0u8,1u8, - 7u8,0u8,39u8,10u8,0u8,11u8,2u8,68u8,14u8,10u8,1u8,10u8,3u8,35u8,4u8,30u8,5u8,21u8,10u8,0u8, - 10u8,1u8,10u8,3u8,71u8,14u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8, - 5u8,16u8,11u8,0u8,1u8,2u8,8u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,65u8,14u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,33u8,2u8,9u8,1u8,2u8,0u8,10u8,1u8,2u8,0u8,11u8,1u8,2u8,0u8,12u8, - 1u8,0u8,0u8,19u8,37u8,10u8,0u8,46u8,65u8,14u8,12u8,4u8,10u8,1u8,10u8,4u8,38u8,4u8,12u8,11u8, - 0u8,1u8,7u8,0u8,39u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,4u8,10u8, - 1u8,10u8,4u8,35u8,4u8,34u8,5u8,21u8,10u8,0u8,12u8,3u8,10u8,1u8,12u8,2u8,11u8,1u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,11u8,3u8,11u8,2u8,10u8,1u8,71u8,14u8,5u8,16u8, - 11u8,0u8,69u8,14u8,2u8,13u8,1u8,0u8,0u8,12u8,9u8,10u8,0u8,46u8,65u8,14u8,12u8,1u8,11u8,0u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,1u8,56u8,2u8,2u8,14u8,1u8,0u8,0u8,12u8,22u8, - 14u8,1u8,65u8,14u8,12u8,2u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,17u8, - 5u8,8u8,10u8,0u8,13u8,1u8,69u8,14u8,68u8,14u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,23u8,12u8,2u8,5u8,3u8,11u8,0u8,1u8,11u8,1u8,70u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,2u8,15u8,1u8,0u8,0u8,1u8,41u8,10u8,1u8,10u8,2u8,37u8,4u8,5u8,5u8,9u8,11u8,0u8,1u8, - 7u8,1u8,39u8,10u8,1u8,10u8,2u8,33u8,4u8,16u8,11u8,0u8,1u8,2u8,11u8,2u8,6u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,23u8,12u8,2u8,10u8,1u8,10u8,2u8,35u8,4u8,38u8,5u8,25u8,10u8,0u8,10u8, - 1u8,10u8,2u8,71u8,14u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,11u8, - 2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,2u8,5u8,20u8,11u8,0u8,1u8,2u8,16u8, - 1u8,0u8,0u8,12u8,10u8,10u8,0u8,46u8,65u8,14u8,12u8,2u8,11u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,11u8,1u8,11u8,2u8,56u8,3u8,2u8,17u8,1u8,0u8,0u8,1u8,18u8,10u8,0u8,10u8,1u8, - 10u8,2u8,56u8,2u8,10u8,0u8,10u8,2u8,10u8,3u8,56u8,2u8,11u8,0u8,10u8,1u8,10u8,3u8,56u8,2u8, - 11u8,1u8,11u8,3u8,11u8,2u8,23u8,22u8,2u8,18u8,1u8,0u8,0u8,8u8,7u8,64u8,14u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,12u8,1u8,13u8,1u8,11u8,0u8,68u8,14u8,11u8,1u8,2u8,19u8,1u8,2u8,0u8, - 20u8,1u8,0u8,0u8,12u8,23u8,10u8,0u8,46u8,56u8,4u8,32u8,4u8,6u8,5u8,10u8,11u8,0u8,1u8,7u8, - 0u8,39u8,10u8,0u8,46u8,65u8,14u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,2u8,10u8, - 0u8,11u8,1u8,11u8,2u8,71u8,14u8,11u8,0u8,69u8,14u8,2u8,21u8,1u8,0u8,0u8,8u8,8u8,11u8,0u8, - 11u8,1u8,56u8,5u8,12u8,2u8,13u8,2u8,56u8,0u8,11u8,2u8,2u8,22u8,1u8,0u8,0u8,20u8,33u8,10u8, - 0u8,46u8,65u8,14u8,12u8,2u8,10u8,1u8,10u8,2u8,37u8,4u8,9u8,5u8,13u8,11u8,0u8,1u8,7u8,0u8, - 39u8,64u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,1u8,10u8,2u8,35u8,4u8,29u8, - 5u8,20u8,13u8,3u8,10u8,0u8,69u8,14u8,68u8,14u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,23u8,12u8,2u8,5u8,15u8,11u8,0u8,1u8,11u8,3u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk3); - let chunk4 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,7u8,1u8,0u8,2u8,3u8,2u8,65u8,5u8,67u8,6u8,7u8, - 73u8,183u8,1u8,8u8,128u8,2u8,32u8,6u8,160u8,2u8,130u8,1u8,12u8,162u8,3u8,171u8,1u8,0u8,0u8,0u8, - 1u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,3u8,1u8,0u8,0u8,0u8,4u8,0u8,0u8,0u8,0u8, - 5u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,7u8,0u8,0u8,0u8,0u8,8u8,0u8,0u8,0u8,0u8, - 9u8,0u8,0u8,0u8,0u8,10u8,0u8,0u8,0u8,0u8,11u8,0u8,0u8,0u8,0u8,12u8,0u8,0u8,0u8,0u8, - 13u8,0u8,0u8,0u8,1u8,3u8,2u8,3u8,3u8,0u8,5u8,101u8,114u8,114u8,111u8,114u8,7u8,97u8,98u8,111u8, - 114u8,116u8,101u8,100u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,9u8, - 99u8,97u8,110u8,111u8,110u8,105u8,99u8,97u8,108u8,8u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,16u8,105u8, - 110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,13u8,105u8,110u8,118u8,97u8, - 108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,15u8, - 110u8,111u8,116u8,95u8,105u8,109u8,112u8,108u8,101u8,109u8,101u8,110u8,116u8,101u8,100u8,12u8,111u8,117u8,116u8,95u8, - 111u8,102u8,95u8,114u8,97u8,110u8,103u8,101u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8, - 100u8,101u8,110u8,105u8,101u8,100u8,18u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,101u8,120u8,104u8,97u8, - 117u8,115u8,116u8,101u8,100u8,15u8,117u8,110u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,101u8, - 100u8,11u8,117u8,110u8,97u8,118u8,97u8,105u8,108u8,97u8,98u8,108u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,2u8, - 4u8,7u8,0u8,11u8,0u8,17u8,2u8,2u8,1u8,1u8,0u8,0u8,2u8,4u8,7u8,1u8,11u8,0u8,17u8,2u8, - 2u8,2u8,1u8,0u8,0u8,2u8,6u8,11u8,0u8,49u8,16u8,47u8,11u8,1u8,22u8,2u8,3u8,1u8,0u8,0u8, - 2u8,4u8,7u8,3u8,11u8,0u8,17u8,2u8,2u8,4u8,1u8,0u8,0u8,2u8,4u8,7u8,4u8,11u8,0u8,17u8, - 2u8,2u8,5u8,1u8,0u8,0u8,2u8,4u8,7u8,5u8,11u8,0u8,17u8,2u8,2u8,6u8,1u8,0u8,0u8,2u8, - 4u8,7u8,6u8,11u8,0u8,17u8,2u8,2u8,7u8,1u8,0u8,0u8,2u8,4u8,7u8,7u8,11u8,0u8,17u8,2u8, - 2u8,8u8,1u8,0u8,0u8,2u8,4u8,7u8,8u8,11u8,0u8,17u8,2u8,2u8,9u8,1u8,0u8,0u8,2u8,4u8, - 7u8,9u8,11u8,0u8,17u8,2u8,2u8,10u8,1u8,0u8,0u8,2u8,4u8,7u8,10u8,11u8,0u8,17u8,2u8,2u8, - 11u8,1u8,0u8,0u8,2u8,4u8,7u8,11u8,11u8,0u8,17u8,2u8,2u8,12u8,1u8,0u8,0u8,2u8,4u8,7u8, - 12u8,11u8,0u8,17u8,2u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk4); - let chunk5 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,48u8,4u8, - 58u8,6u8,5u8,64u8,47u8,7u8,111u8,94u8,8u8,205u8,1u8,32u8,6u8,237u8,1u8,20u8,16u8,129u8,2u8,137u8, - 1u8,10u8,138u8,3u8,6u8,12u8,144u8,3u8,140u8,1u8,13u8,156u8,4u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8, - 0u8,3u8,7u8,0u8,0u8,4u8,0u8,1u8,0u8,0u8,5u8,2u8,1u8,0u8,0u8,6u8,2u8,3u8,0u8,0u8, - 7u8,1u8,4u8,0u8,0u8,8u8,0u8,1u8,0u8,2u8,6u8,7u8,3u8,1u8,0u8,1u8,10u8,8u8,8u8,0u8, - 2u8,11u8,7u8,10u8,1u8,0u8,2u8,8u8,11u8,12u8,1u8,0u8,5u8,6u8,7u8,6u8,8u8,6u8,2u8,7u8, - 8u8,0u8,5u8,0u8,2u8,6u8,8u8,0u8,5u8,1u8,1u8,1u8,8u8,0u8,1u8,6u8,5u8,1u8,5u8,2u8, - 6u8,10u8,9u8,0u8,6u8,9u8,0u8,1u8,3u8,2u8,6u8,5u8,3u8,2u8,1u8,3u8,2u8,7u8,10u8,9u8, - 0u8,3u8,1u8,9u8,0u8,3u8,97u8,99u8,108u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,118u8,101u8,99u8,116u8, - 111u8,114u8,3u8,65u8,67u8,76u8,3u8,97u8,100u8,100u8,15u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,99u8,111u8, - 110u8,116u8,97u8,105u8,110u8,115u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,5u8,101u8,109u8,112u8,116u8, - 121u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,4u8,108u8,105u8,115u8,116u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8, - 100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8, - 101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,117u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 8u8,69u8,67u8,79u8,78u8,84u8,65u8,73u8,78u8,37u8,84u8,104u8,101u8,32u8,65u8,67u8,76u8,32u8,97u8,108u8, - 114u8,101u8,97u8,100u8,121u8,32u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,32u8,116u8,104u8,101u8,32u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,46u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,69u8,78u8,79u8,84u8, - 95u8,67u8,79u8,78u8,84u8,65u8,73u8,78u8,37u8,84u8,104u8,101u8,32u8,65u8,67u8,76u8,32u8,100u8,111u8,101u8, - 115u8,32u8,110u8,111u8,116u8,32u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,46u8,0u8,0u8,0u8,2u8,1u8,9u8,10u8,5u8,0u8,1u8,0u8,0u8,5u8,20u8, - 10u8,0u8,15u8,0u8,14u8,1u8,12u8,2u8,46u8,11u8,2u8,56u8,0u8,32u8,4u8,10u8,5u8,15u8,11u8,0u8, - 1u8,7u8,0u8,17u8,6u8,39u8,11u8,0u8,15u8,0u8,11u8,1u8,68u8,6u8,2u8,1u8,1u8,0u8,0u8,1u8, - 9u8,11u8,0u8,11u8,1u8,17u8,2u8,4u8,5u8,5u8,8u8,7u8,1u8,17u8,6u8,39u8,2u8,2u8,1u8,0u8, - 0u8,1u8,5u8,11u8,0u8,16u8,0u8,14u8,1u8,56u8,0u8,2u8,3u8,1u8,0u8,0u8,1u8,3u8,64u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,0u8,2u8,4u8,1u8,0u8,0u8,9u8,21u8,10u8,0u8,15u8, - 0u8,14u8,1u8,12u8,2u8,46u8,11u8,2u8,56u8,1u8,12u8,3u8,4u8,10u8,5u8,15u8,11u8,0u8,1u8,7u8, - 1u8,17u8,6u8,39u8,11u8,0u8,15u8,0u8,11u8,3u8,56u8,2u8,1u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk5); - let chunk6 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,2u8,2u8,2u8,4u8,3u8,6u8,35u8,5u8, - 41u8,36u8,7u8,77u8,109u8,8u8,186u8,1u8,32u8,6u8,218u8,1u8,40u8,16u8,130u8,2u8,131u8,1u8,10u8,133u8, - 3u8,8u8,12u8,141u8,3u8,239u8,3u8,13u8,252u8,6u8,4u8,0u8,0u8,0u8,1u8,7u8,0u8,0u8,2u8,0u8, - 1u8,0u8,0u8,3u8,2u8,3u8,0u8,0u8,4u8,0u8,3u8,0u8,0u8,5u8,3u8,4u8,0u8,0u8,6u8,5u8, - 6u8,0u8,0u8,7u8,5u8,6u8,0u8,0u8,8u8,5u8,6u8,0u8,2u8,6u8,8u8,0u8,3u8,1u8,1u8,1u8, - 6u8,8u8,0u8,1u8,3u8,1u8,8u8,0u8,2u8,7u8,8u8,0u8,3u8,0u8,2u8,10u8,1u8,3u8,1u8,7u8, - 1u8,5u8,3u8,7u8,1u8,3u8,3u8,3u8,10u8,98u8,105u8,116u8,95u8,118u8,101u8,99u8,116u8,111u8,114u8,9u8, - 66u8,105u8,116u8,86u8,101u8,99u8,116u8,111u8,114u8,12u8,105u8,115u8,95u8,105u8,110u8,100u8,101u8,120u8,95u8,115u8, - 101u8,116u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,32u8,108u8,111u8,110u8,103u8,101u8,115u8,116u8,95u8,115u8,101u8, - 116u8,95u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,95u8,115u8,116u8,97u8,114u8,116u8,105u8,110u8,103u8,95u8, - 97u8,116u8,3u8,110u8,101u8,119u8,3u8,115u8,101u8,116u8,10u8,115u8,104u8,105u8,102u8,116u8,95u8,108u8,101u8,102u8, - 116u8,5u8,117u8,110u8,115u8,101u8,116u8,9u8,98u8,105u8,116u8,95u8,102u8,105u8,101u8,108u8,100u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,1u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8, - 97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,111u8,2u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,6u8,69u8, - 73u8,78u8,68u8,69u8,88u8,35u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,105u8, - 110u8,100u8,101u8,120u8,32u8,105u8,115u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8,32u8,98u8,111u8,117u8,110u8,100u8, - 115u8,1u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,7u8,69u8,76u8,69u8,78u8,71u8,84u8,72u8,40u8,65u8,110u8, - 32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,108u8,101u8,110u8,103u8,116u8,104u8,32u8,111u8,102u8,32u8,98u8, - 105u8,116u8,118u8,101u8,99u8,116u8,111u8,114u8,32u8,119u8,97u8,115u8,32u8,103u8,105u8,118u8,101u8,110u8,0u8,0u8, - 0u8,2u8,2u8,3u8,3u8,9u8,10u8,1u8,0u8,1u8,0u8,0u8,6u8,17u8,10u8,1u8,10u8,0u8,16u8,0u8, - 65u8,1u8,35u8,4u8,7u8,5u8,11u8,11u8,0u8,1u8,7u8,0u8,39u8,11u8,0u8,16u8,0u8,11u8,1u8,66u8, - 1u8,20u8,2u8,1u8,1u8,0u8,0u8,6u8,4u8,11u8,0u8,16u8,0u8,65u8,1u8,2u8,2u8,1u8,0u8,0u8, - 3u8,37u8,10u8,1u8,10u8,0u8,16u8,1u8,20u8,35u8,4u8,7u8,5u8,11u8,11u8,0u8,1u8,7u8,0u8,39u8, - 10u8,1u8,12u8,2u8,10u8,2u8,10u8,0u8,16u8,1u8,20u8,35u8,4u8,33u8,5u8,20u8,10u8,0u8,10u8,2u8, - 17u8,0u8,32u8,4u8,28u8,11u8,0u8,1u8,5u8,33u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,22u8,12u8,2u8,5u8,13u8,11u8,2u8,11u8,1u8,23u8,2u8,3u8,1u8,0u8,0u8,7u8,37u8,10u8,0u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,7u8,7u8,1u8,39u8,10u8,0u8,7u8, - 2u8,35u8,4u8,12u8,5u8,14u8,7u8,1u8,39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8, - 64u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,40u8,10u8,2u8,10u8,0u8,35u8,4u8,32u8, - 5u8,24u8,13u8,1u8,9u8,68u8,1u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8, - 2u8,5u8,18u8,40u8,11u8,0u8,11u8,1u8,18u8,0u8,2u8,4u8,1u8,0u8,0u8,8u8,20u8,10u8,1u8,10u8, - 0u8,16u8,0u8,65u8,1u8,35u8,4u8,7u8,5u8,11u8,11u8,0u8,1u8,7u8,0u8,39u8,11u8,0u8,15u8,0u8, - 11u8,1u8,67u8,1u8,12u8,2u8,8u8,11u8,2u8,21u8,2u8,5u8,1u8,0u8,0u8,9u8,89u8,10u8,1u8,10u8, - 0u8,16u8,1u8,20u8,38u8,4u8,33u8,10u8,0u8,16u8,0u8,65u8,1u8,12u8,6u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,4u8,10u8,4u8,10u8,6u8,35u8,4u8,30u8,5u8,17u8,10u8,0u8,15u8,0u8,10u8, - 4u8,67u8,1u8,12u8,3u8,9u8,11u8,3u8,21u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,12u8,4u8,5u8,12u8,11u8,0u8,1u8,5u8,88u8,10u8,1u8,12u8,5u8,10u8,5u8,10u8,0u8,16u8,1u8, - 20u8,35u8,4u8,65u8,5u8,42u8,10u8,0u8,10u8,5u8,12u8,2u8,46u8,11u8,2u8,17u8,0u8,4u8,55u8,10u8, - 0u8,10u8,5u8,10u8,1u8,23u8,17u8,4u8,5u8,60u8,10u8,0u8,10u8,5u8,10u8,1u8,23u8,17u8,6u8,11u8, - 5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,35u8,10u8,0u8,16u8,1u8,20u8, - 11u8,1u8,23u8,12u8,5u8,10u8,5u8,10u8,0u8,16u8,1u8,20u8,35u8,4u8,86u8,5u8,78u8,10u8,0u8,10u8, - 5u8,17u8,6u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,71u8,11u8, - 0u8,1u8,2u8,6u8,1u8,0u8,0u8,8u8,20u8,10u8,1u8,10u8,0u8,16u8,0u8,65u8,1u8,35u8,4u8,7u8, - 5u8,11u8,11u8,0u8,1u8,7u8,0u8,39u8,11u8,0u8,15u8,0u8,11u8,1u8,67u8,1u8,12u8,2u8,9u8,11u8, - 2u8,21u8,2u8,0u8,1u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk6); - let chunk7 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,2u8,3u8,2u8,10u8,5u8,12u8,9u8,7u8, - 21u8,33u8,8u8,54u8,32u8,12u8,86u8,16u8,0u8,0u8,0u8,1u8,0u8,1u8,0u8,0u8,2u8,0u8,2u8,0u8, - 1u8,6u8,12u8,1u8,5u8,1u8,6u8,5u8,0u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,10u8,97u8,100u8,100u8, - 114u8,101u8,115u8,115u8,95u8,111u8,102u8,14u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,1u8,0u8,0u8,3u8,4u8, - 11u8,0u8,17u8,1u8,20u8,2u8,1u8,1u8,2u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk7); - let chunk8 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,150u8,1u8, - 5u8,160u8,1u8,47u8,7u8,207u8,1u8,186u8,6u8,8u8,137u8,8u8,32u8,6u8,169u8,8u8,178u8,1u8,16u8,219u8, - 9u8,146u8,2u8,10u8,237u8,11u8,6u8,12u8,243u8,11u8,237u8,4u8,13u8,224u8,16u8,2u8,0u8,0u8,0u8,1u8, - 0u8,2u8,0u8,3u8,8u8,0u8,0u8,4u8,0u8,1u8,0u8,0u8,5u8,0u8,1u8,0u8,0u8,6u8,0u8,1u8, - 0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,2u8,0u8,0u8,0u8,9u8,0u8,1u8,0u8,0u8,10u8,0u8,1u8, - 0u8,0u8,11u8,3u8,1u8,0u8,0u8,12u8,0u8,1u8,0u8,0u8,13u8,0u8,1u8,0u8,0u8,14u8,0u8,4u8, - 0u8,0u8,15u8,0u8,4u8,0u8,0u8,16u8,0u8,4u8,0u8,0u8,17u8,0u8,4u8,0u8,0u8,18u8,0u8,4u8, - 0u8,0u8,19u8,0u8,4u8,0u8,0u8,20u8,0u8,4u8,0u8,0u8,21u8,0u8,4u8,0u8,0u8,22u8,0u8,4u8, - 0u8,0u8,23u8,0u8,4u8,0u8,0u8,24u8,4u8,1u8,0u8,0u8,25u8,0u8,1u8,0u8,0u8,26u8,0u8,4u8, - 0u8,0u8,27u8,0u8,1u8,0u8,0u8,28u8,0u8,1u8,0u8,0u8,29u8,5u8,0u8,0u8,0u8,30u8,0u8,1u8, - 0u8,0u8,31u8,0u8,1u8,0u8,2u8,32u8,7u8,8u8,0u8,1u8,33u8,4u8,4u8,0u8,0u8,1u8,1u8,3u8, - 6u8,12u8,10u8,3u8,10u8,3u8,2u8,6u8,10u8,2u8,3u8,1u8,3u8,3u8,7u8,10u8,2u8,3u8,1u8,5u8, - 7u8,10u8,2u8,3u8,3u8,3u8,3u8,1u8,6u8,12u8,1u8,5u8,3u8,1u8,2u8,3u8,1u8,2u8,3u8,2u8, - 3u8,7u8,2u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,115u8, - 105u8,103u8,110u8,101u8,114u8,8u8,70u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,25u8,97u8,108u8,108u8,111u8,119u8, - 95u8,118u8,109u8,95u8,98u8,105u8,110u8,97u8,114u8,121u8,95u8,102u8,111u8,114u8,109u8,97u8,116u8,95u8,118u8,54u8, - 29u8,97u8,112u8,116u8,111u8,115u8,95u8,115u8,116u8,100u8,108u8,105u8,98u8,95u8,99u8,104u8,97u8,105u8,110u8,95u8, - 105u8,100u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,19u8,98u8,108u8,97u8,107u8,101u8,50u8,98u8,95u8,50u8, - 53u8,54u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,28u8,98u8,108u8,115u8,49u8,50u8,95u8,51u8,56u8,49u8, - 95u8,115u8,116u8,114u8,117u8,99u8,116u8,117u8,114u8,101u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,20u8, - 99u8,104u8,97u8,110u8,103u8,101u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,95u8,102u8,108u8,97u8,103u8,115u8, - 29u8,99u8,111u8,100u8,101u8,95u8,100u8,101u8,112u8,101u8,110u8,100u8,101u8,110u8,99u8,121u8,95u8,99u8,104u8,101u8, - 99u8,107u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,31u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,97u8, - 110u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,103u8,97u8,115u8,95u8,102u8,101u8, - 101u8,115u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,28u8,99u8,114u8,121u8,112u8,116u8,111u8,103u8,114u8, - 97u8,112u8,104u8,121u8,95u8,97u8,108u8,103u8,101u8,98u8,114u8,97u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8, - 24u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,115u8,95u8,101u8,110u8, - 97u8,98u8,108u8,101u8,100u8,33u8,103u8,101u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,115u8,116u8,100u8,108u8, - 105u8,98u8,95u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,23u8, - 103u8,101u8,116u8,95u8,98u8,108u8,97u8,107u8,101u8,50u8,98u8,95u8,50u8,53u8,54u8,95u8,102u8,101u8,97u8,116u8, - 117u8,114u8,101u8,31u8,103u8,101u8,116u8,95u8,98u8,108u8,115u8,49u8,50u8,95u8,51u8,56u8,49u8,95u8,115u8,116u8, - 114u8,117u8,116u8,117u8,114u8,101u8,115u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,43u8,103u8,101u8,116u8,95u8, - 99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8, - 116u8,101u8,95u8,103u8,97u8,115u8,95u8,102u8,101u8,101u8,115u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,40u8, - 103u8,101u8,116u8,95u8,99u8,114u8,121u8,112u8,116u8,111u8,103u8,114u8,97u8,112u8,104u8,121u8,95u8,97u8,108u8,103u8, - 101u8,98u8,114u8,97u8,95u8,110u8,97u8,116u8,105u8,118u8,101u8,115u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8, - 28u8,103u8,101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8, - 115u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,29u8,103u8,101u8,116u8,95u8,109u8,117u8,108u8,116u8,105u8,115u8, - 105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,115u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,27u8, - 103u8,101u8,116u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,103u8,114u8,111u8,117u8,112u8,115u8,95u8, - 102u8,101u8,97u8,116u8,117u8,114u8,101u8,34u8,103u8,101u8,116u8,95u8,115u8,104u8,97u8,95u8,53u8,49u8,50u8,95u8, - 97u8,110u8,100u8,95u8,114u8,105u8,112u8,101u8,109u8,100u8,95u8,49u8,54u8,48u8,95u8,102u8,101u8,97u8,116u8,117u8, - 114u8,101u8,23u8,103u8,101u8,116u8,95u8,118u8,109u8,95u8,98u8,105u8,110u8,97u8,114u8,121u8,95u8,102u8,111u8,114u8, - 109u8,97u8,116u8,95u8,118u8,54u8,10u8,105u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,36u8,109u8,117u8, - 108u8,116u8,105u8,95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,95u8,112u8,107u8,95u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,101u8,95u8,118u8,50u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,36u8,109u8,117u8,108u8,116u8,105u8, - 95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,95u8,112u8,107u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8, - 95u8,118u8,50u8,95u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,25u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8, - 95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,23u8,114u8,101u8, - 115u8,111u8,117u8,114u8,99u8,101u8,95u8,103u8,114u8,111u8,117u8,112u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8, - 100u8,3u8,115u8,101u8,116u8,30u8,115u8,104u8,97u8,95u8,53u8,49u8,50u8,95u8,97u8,110u8,100u8,95u8,114u8,105u8, - 112u8,101u8,109u8,100u8,95u8,49u8,54u8,48u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,23u8,116u8,114u8,101u8, - 97u8,116u8,95u8,102u8,114u8,105u8,101u8,110u8,100u8,95u8,97u8,115u8,95u8,112u8,114u8,105u8,118u8,97u8,116u8,101u8, - 10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8, - 111u8,110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8,1u8,0u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,253u8,1u8, - 2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,70u8,82u8,65u8,77u8,69u8,87u8,79u8,82u8,75u8, - 95u8,83u8,73u8,71u8,78u8,69u8,82u8,95u8,78u8,69u8,69u8,68u8,69u8,68u8,48u8,84u8,104u8,101u8,32u8,112u8, - 114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,115u8,105u8,103u8,110u8,101u8,114u8,32u8,104u8,97u8,115u8,32u8,110u8, - 111u8,116u8,32u8,97u8,32u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,32u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,46u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,49u8,69u8,68u8,50u8,53u8,53u8,49u8,57u8,95u8, - 80u8,85u8,66u8,75u8,69u8,89u8,95u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,69u8,95u8,82u8,69u8,84u8,85u8, - 82u8,78u8,95u8,70u8,65u8,76u8,83u8,69u8,95u8,87u8,82u8,79u8,78u8,71u8,95u8,76u8,69u8,78u8,71u8,84u8, - 72u8,109u8,87u8,104u8,101u8,116u8,104u8,101u8,114u8,32u8,110u8,97u8,116u8,105u8,118u8,101u8,95u8,112u8,117u8,98u8, - 108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,32u8,97u8,98u8,111u8, - 114u8,116u8,115u8,32u8,119u8,104u8,101u8,110u8,32u8,97u8,32u8,112u8,117u8,98u8,108u8,105u8,99u8,32u8,107u8,101u8, - 121u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,119u8,114u8,111u8,110u8,103u8,32u8,108u8,101u8,110u8,103u8,116u8, - 104u8,32u8,105u8,115u8,32u8,103u8,105u8,118u8,101u8,110u8,10u8,32u8,76u8,105u8,102u8,101u8,116u8,105u8,109u8,101u8, - 58u8,32u8,101u8,112u8,104u8,101u8,109u8,101u8,114u8,97u8,108u8,0u8,0u8,0u8,2u8,1u8,0u8,10u8,2u8,0u8, - 1u8,0u8,1u8,0u8,0u8,3u8,7u8,13u8,17u8,20u8,2u8,1u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,0u8, - 17u8,20u8,2u8,2u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,1u8,17u8,20u8,2u8,3u8,1u8,0u8,1u8,0u8, - 0u8,3u8,7u8,2u8,17u8,20u8,2u8,4u8,1u8,0u8,1u8,0u8,6u8,73u8,10u8,0u8,17u8,28u8,7u8,14u8, - 33u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,3u8,17u8,29u8,39u8,7u8,14u8,41u8,0u8,32u8,4u8,20u8, - 11u8,0u8,7u8,15u8,18u8,0u8,45u8,0u8,5u8,22u8,11u8,0u8,1u8,7u8,14u8,42u8,0u8,15u8,0u8,12u8, - 3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,14u8,1u8,65u8,4u8,12u8,6u8,10u8,4u8, - 10u8,6u8,35u8,4u8,48u8,5u8,36u8,10u8,3u8,14u8,1u8,10u8,4u8,66u8,4u8,20u8,8u8,17u8,25u8,11u8, - 4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,31u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,5u8,14u8,2u8,65u8,4u8,12u8,7u8,10u8,5u8,10u8,7u8,35u8,4u8,70u8,5u8, - 58u8,10u8,3u8,14u8,2u8,10u8,5u8,66u8,4u8,20u8,9u8,17u8,25u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,53u8,11u8,3u8,1u8,2u8,5u8,1u8,0u8,1u8,0u8,0u8,3u8, - 7u8,3u8,17u8,20u8,2u8,6u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,4u8,17u8,20u8,2u8,7u8,0u8,0u8, - 0u8,9u8,32u8,10u8,1u8,6u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,4u8,49u8,1u8,11u8, - 1u8,6u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,51u8,47u8,12u8,3u8,10u8,4u8,10u8,0u8,65u8, - 10u8,35u8,4u8,26u8,11u8,0u8,11u8,4u8,66u8,10u8,20u8,11u8,3u8,28u8,49u8,0u8,34u8,12u8,2u8,5u8, - 30u8,11u8,0u8,1u8,9u8,12u8,2u8,11u8,2u8,2u8,8u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,5u8,17u8, - 20u8,2u8,9u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,6u8,17u8,20u8,2u8,10u8,1u8,0u8,0u8,0u8,2u8, - 7u8,0u8,2u8,11u8,1u8,0u8,0u8,0u8,2u8,7u8,1u8,2u8,12u8,1u8,0u8,0u8,0u8,2u8,7u8,2u8, - 2u8,13u8,1u8,0u8,0u8,0u8,2u8,7u8,4u8,2u8,14u8,1u8,0u8,0u8,0u8,2u8,7u8,5u8,2u8,15u8, - 1u8,0u8,0u8,0u8,2u8,7u8,6u8,2u8,16u8,1u8,0u8,0u8,0u8,2u8,7u8,8u8,2u8,17u8,1u8,0u8, - 0u8,0u8,2u8,7u8,10u8,2u8,18u8,1u8,0u8,0u8,0u8,2u8,7u8,11u8,2u8,19u8,1u8,0u8,0u8,0u8, - 2u8,7u8,13u8,2u8,20u8,0u8,0u8,1u8,0u8,1u8,14u8,7u8,14u8,41u8,0u8,4u8,10u8,7u8,14u8,43u8, - 0u8,16u8,0u8,11u8,0u8,17u8,7u8,12u8,1u8,5u8,12u8,9u8,12u8,1u8,11u8,1u8,2u8,21u8,1u8,0u8, - 1u8,0u8,0u8,3u8,7u8,9u8,17u8,20u8,2u8,22u8,1u8,0u8,0u8,0u8,2u8,7u8,9u8,2u8,23u8,1u8, - 0u8,1u8,0u8,0u8,3u8,7u8,8u8,17u8,20u8,2u8,24u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,10u8,17u8, - 20u8,2u8,25u8,0u8,0u8,0u8,11u8,44u8,10u8,1u8,6u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8, - 12u8,4u8,49u8,1u8,11u8,1u8,6u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,51u8,47u8,12u8,3u8, - 10u8,0u8,46u8,65u8,10u8,10u8,4u8,37u8,4u8,22u8,5u8,18u8,10u8,0u8,49u8,0u8,68u8,10u8,5u8,11u8, - 11u8,0u8,11u8,4u8,67u8,10u8,12u8,5u8,11u8,2u8,4u8,35u8,10u8,5u8,20u8,11u8,3u8,27u8,11u8,5u8, - 21u8,5u8,43u8,10u8,5u8,20u8,49u8,255u8,11u8,3u8,29u8,28u8,11u8,5u8,21u8,2u8,26u8,1u8,0u8,1u8, - 0u8,0u8,3u8,7u8,11u8,17u8,20u8,2u8,27u8,1u8,0u8,1u8,0u8,0u8,3u8,7u8,12u8,17u8,20u8,2u8, - 0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk8); - let chunk9 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,4u8,2u8,4u8,6u8,3u8,10u8,120u8,4u8, - 130u8,1u8,14u8,5u8,144u8,1u8,135u8,1u8,7u8,151u8,2u8,219u8,1u8,8u8,242u8,3u8,32u8,6u8,146u8,4u8, - 20u8,16u8,166u8,4u8,175u8,2u8,10u8,213u8,6u8,7u8,11u8,220u8,6u8,2u8,12u8,222u8,6u8,128u8,4u8,13u8, - 222u8,10u8,2u8,14u8,224u8,10u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,7u8,1u8,0u8,0u8,0u8,3u8,0u8, - 1u8,1u8,0u8,0u8,4u8,2u8,3u8,1u8,0u8,0u8,5u8,4u8,1u8,1u8,0u8,0u8,6u8,4u8,5u8,1u8, - 0u8,0u8,7u8,6u8,7u8,1u8,0u8,0u8,8u8,6u8,8u8,1u8,0u8,0u8,9u8,9u8,8u8,1u8,2u8,0u8, - 10u8,2u8,8u8,1u8,0u8,0u8,11u8,10u8,7u8,1u8,0u8,0u8,12u8,11u8,8u8,1u8,3u8,0u8,13u8,0u8, - 5u8,1u8,0u8,0u8,14u8,0u8,5u8,1u8,0u8,0u8,15u8,7u8,6u8,1u8,0u8,0u8,16u8,8u8,6u8,1u8, - 0u8,0u8,17u8,10u8,8u8,1u8,0u8,0u8,18u8,10u8,6u8,1u8,0u8,0u8,19u8,6u8,12u8,1u8,0u8,1u8, - 21u8,14u8,5u8,1u8,0u8,1u8,6u8,15u8,5u8,1u8,0u8,1u8,22u8,8u8,12u8,1u8,0u8,11u8,8u8,17u8, - 8u8,18u8,8u8,10u8,8u8,19u8,8u8,12u8,8u8,13u8,8u8,1u8,6u8,11u8,0u8,1u8,9u8,0u8,1u8,6u8, - 9u8,0u8,1u8,7u8,11u8,0u8,1u8,9u8,0u8,1u8,7u8,9u8,0u8,2u8,6u8,11u8,0u8,1u8,9u8,0u8, - 6u8,9u8,0u8,1u8,1u8,1u8,11u8,0u8,1u8,9u8,0u8,0u8,1u8,9u8,0u8,2u8,11u8,0u8,1u8,9u8, - 0u8,9u8,0u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,9u8,0u8,2u8,6u8,11u8,0u8,1u8,9u8,0u8,9u8, - 0u8,1u8,10u8,9u8,0u8,2u8,6u8,9u8,0u8,6u8,10u8,9u8,0u8,1u8,6u8,10u8,9u8,0u8,2u8,6u8, - 10u8,9u8,0u8,6u8,9u8,0u8,2u8,9u8,0u8,10u8,9u8,0u8,1u8,7u8,10u8,9u8,0u8,2u8,9u8,0u8, - 6u8,10u8,9u8,0u8,2u8,9u8,0u8,7u8,10u8,9u8,0u8,3u8,11u8,0u8,1u8,9u8,0u8,11u8,0u8,1u8, - 9u8,0u8,7u8,10u8,9u8,0u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8, - 6u8,79u8,112u8,116u8,105u8,111u8,110u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8, - 119u8,95u8,109u8,117u8,116u8,19u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8,101u8, - 102u8,97u8,117u8,108u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,12u8,100u8,101u8,115u8,116u8,114u8, - 111u8,121u8,95u8,110u8,111u8,110u8,101u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,115u8,111u8,109u8,101u8, - 20u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8, - 116u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,4u8,102u8,105u8,108u8,108u8,16u8,103u8,101u8,116u8,95u8,119u8, - 105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,7u8,105u8,115u8,95u8,110u8,111u8,110u8,101u8,7u8, - 105u8,115u8,95u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,4u8,115u8,111u8,109u8,101u8,4u8,115u8,119u8, - 97u8,112u8,12u8,115u8,119u8,97u8,112u8,95u8,111u8,114u8,95u8,102u8,105u8,108u8,108u8,6u8,116u8,111u8,95u8,118u8, - 101u8,99u8,3u8,118u8,101u8,99u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,9u8,115u8,105u8,110u8,103u8, - 108u8,101u8,116u8,111u8,110u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,0u8, - 0u8,4u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,4u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8, - 116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,154u8,2u8,2u8,0u8, - 0u8,4u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,79u8,80u8,84u8,73u8,79u8,78u8,95u8,73u8,83u8,95u8,83u8, - 69u8,84u8,115u8,84u8,104u8,101u8,32u8,96u8,79u8,112u8,116u8,105u8,111u8,110u8,96u8,32u8,105u8,115u8,32u8,105u8, - 110u8,32u8,97u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,115u8,116u8,97u8,116u8,101u8,32u8,102u8, - 111u8,114u8,32u8,116u8,104u8,101u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,97u8,116u8,116u8, - 101u8,109u8,112u8,116u8,101u8,100u8,46u8,10u8,32u8,84u8,104u8,101u8,32u8,96u8,79u8,112u8,116u8,105u8,111u8,110u8, - 96u8,32u8,105u8,115u8,32u8,96u8,83u8,111u8,109u8,101u8,96u8,32u8,119u8,104u8,105u8,108u8,101u8,32u8,105u8,116u8, - 32u8,115u8,104u8,111u8,117u8,108u8,100u8,32u8,98u8,101u8,32u8,96u8,78u8,111u8,110u8,101u8,96u8,46u8,1u8,0u8, - 4u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,79u8,80u8,84u8,73u8,79u8,78u8,95u8,78u8,79u8,84u8,95u8,83u8, - 69u8,84u8,115u8,84u8,104u8,101u8,32u8,96u8,79u8,112u8,116u8,105u8,111u8,110u8,96u8,32u8,105u8,115u8,32u8,105u8, - 110u8,32u8,97u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,115u8,116u8,97u8,116u8,101u8,32u8,102u8, - 111u8,114u8,32u8,116u8,104u8,101u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,97u8,116u8,116u8, - 101u8,109u8,112u8,116u8,101u8,100u8,46u8,10u8,32u8,84u8,104u8,101u8,32u8,96u8,79u8,112u8,116u8,105u8,111u8,110u8, - 96u8,32u8,105u8,115u8,32u8,96u8,78u8,111u8,110u8,101u8,96u8,32u8,119u8,104u8,105u8,108u8,101u8,32u8,105u8,116u8, - 32u8,115u8,104u8,111u8,117u8,108u8,100u8,32u8,98u8,101u8,32u8,96u8,83u8,111u8,109u8,101u8,96u8,46u8,0u8,0u8, - 0u8,2u8,1u8,20u8,10u8,9u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,7u8,13u8,10u8,0u8,56u8,0u8,4u8, - 4u8,5u8,8u8,11u8,0u8,1u8,7u8,1u8,39u8,11u8,0u8,55u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,66u8,8u8,2u8,1u8,1u8,0u8,0u8,7u8,14u8,10u8,0u8,46u8,56u8,0u8,4u8,5u8,5u8,9u8, - 11u8,0u8,1u8,7u8,1u8,39u8,11u8,0u8,54u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,67u8, - 8u8,2u8,2u8,1u8,0u8,0u8,13u8,19u8,11u8,0u8,55u8,0u8,12u8,3u8,10u8,3u8,56u8,1u8,4u8,11u8, - 11u8,3u8,1u8,11u8,1u8,12u8,2u8,5u8,17u8,11u8,1u8,1u8,11u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,66u8,8u8,12u8,2u8,11u8,2u8,2u8,3u8,1u8,0u8,0u8,7u8,5u8,11u8,0u8,55u8,0u8, - 11u8,1u8,56u8,2u8,2u8,4u8,1u8,0u8,0u8,7u8,10u8,14u8,0u8,56u8,3u8,4u8,4u8,5u8,6u8,7u8, - 0u8,39u8,11u8,0u8,58u8,0u8,70u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,5u8,1u8,0u8, - 0u8,16u8,16u8,14u8,0u8,56u8,0u8,4u8,4u8,5u8,6u8,7u8,1u8,39u8,11u8,0u8,58u8,0u8,12u8,2u8, - 13u8,2u8,69u8,8u8,12u8,1u8,11u8,2u8,70u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,1u8, - 2u8,6u8,1u8,0u8,0u8,16u8,15u8,11u8,0u8,58u8,0u8,12u8,3u8,13u8,3u8,46u8,56u8,1u8,4u8,10u8, - 11u8,1u8,12u8,2u8,5u8,13u8,13u8,3u8,69u8,8u8,12u8,2u8,11u8,2u8,2u8,7u8,1u8,0u8,0u8,7u8, - 13u8,10u8,0u8,46u8,56u8,0u8,4u8,5u8,5u8,9u8,11u8,0u8,1u8,7u8,1u8,39u8,11u8,0u8,54u8,0u8, - 69u8,8u8,2u8,8u8,1u8,0u8,0u8,17u8,16u8,11u8,0u8,54u8,0u8,12u8,2u8,10u8,2u8,46u8,56u8,1u8, - 4u8,8u8,5u8,12u8,11u8,2u8,1u8,7u8,0u8,39u8,11u8,2u8,11u8,1u8,68u8,8u8,2u8,9u8,1u8,0u8, - 0u8,18u8,18u8,11u8,0u8,55u8,0u8,12u8,3u8,10u8,3u8,56u8,1u8,4u8,11u8,11u8,3u8,1u8,11u8,1u8, - 12u8,2u8,5u8,16u8,11u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,66u8,8u8,20u8,12u8,2u8, - 11u8,2u8,2u8,10u8,1u8,0u8,0u8,7u8,4u8,11u8,0u8,55u8,0u8,56u8,1u8,2u8,11u8,1u8,0u8,0u8, - 7u8,5u8,11u8,0u8,55u8,0u8,56u8,1u8,32u8,2u8,12u8,1u8,0u8,0u8,7u8,3u8,64u8,8u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,57u8,0u8,2u8,13u8,1u8,0u8,0u8,7u8,4u8,11u8,0u8,56u8,4u8,57u8, - 0u8,2u8,14u8,1u8,0u8,0u8,19u8,20u8,10u8,0u8,46u8,56u8,0u8,4u8,5u8,5u8,9u8,11u8,0u8,1u8, - 7u8,1u8,39u8,11u8,0u8,54u8,0u8,12u8,3u8,10u8,3u8,69u8,8u8,12u8,2u8,11u8,3u8,11u8,1u8,68u8, - 8u8,11u8,2u8,2u8,15u8,1u8,0u8,0u8,20u8,21u8,11u8,0u8,54u8,0u8,12u8,4u8,10u8,4u8,46u8,56u8, - 1u8,4u8,10u8,56u8,5u8,12u8,2u8,5u8,14u8,10u8,4u8,69u8,8u8,56u8,6u8,12u8,2u8,11u8,2u8,12u8, - 3u8,11u8,4u8,11u8,1u8,68u8,8u8,11u8,3u8,2u8,16u8,1u8,0u8,0u8,7u8,3u8,11u8,0u8,58u8,0u8, - 2u8,0u8,0u8,0u8,8u8,0u8, - ]; - vector::push_back(&mut code, chunk9); - let chunk10 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,10u8,3u8,16u8,94u8,4u8, - 110u8,8u8,5u8,118u8,120u8,7u8,238u8,1u8,211u8,1u8,8u8,193u8,3u8,32u8,6u8,225u8,3u8,20u8,16u8,245u8, - 3u8,114u8,10u8,231u8,4u8,6u8,12u8,237u8,4u8,235u8,2u8,13u8,216u8,7u8,2u8,0u8,0u8,0u8,1u8,0u8, - 2u8,0u8,3u8,7u8,0u8,1u8,16u8,7u8,1u8,0u8,0u8,0u8,4u8,0u8,1u8,0u8,0u8,5u8,2u8,1u8, - 0u8,0u8,6u8,3u8,4u8,0u8,0u8,7u8,5u8,6u8,0u8,0u8,8u8,7u8,1u8,0u8,0u8,9u8,4u8,8u8, - 0u8,0u8,10u8,9u8,6u8,0u8,0u8,11u8,10u8,8u8,0u8,0u8,12u8,11u8,12u8,0u8,0u8,13u8,3u8,8u8, - 0u8,0u8,14u8,3u8,6u8,0u8,0u8,15u8,13u8,14u8,0u8,0u8,17u8,12u8,15u8,0u8,0u8,18u8,12u8,14u8, - 0u8,2u8,4u8,17u8,1u8,1u8,0u8,2u8,13u8,19u8,8u8,1u8,0u8,1u8,19u8,21u8,22u8,1u8,0u8,1u8, - 20u8,1u8,22u8,1u8,0u8,14u8,16u8,15u8,16u8,16u8,14u8,17u8,14u8,2u8,7u8,8u8,0u8,8u8,0u8,0u8, - 2u8,7u8,8u8,0u8,10u8,2u8,1u8,6u8,8u8,0u8,1u8,6u8,10u8,2u8,2u8,6u8,8u8,0u8,6u8,8u8, - 0u8,1u8,3u8,3u8,7u8,8u8,0u8,3u8,8u8,0u8,1u8,1u8,2u8,6u8,10u8,2u8,6u8,10u8,2u8,2u8, - 6u8,10u8,2u8,3u8,3u8,6u8,10u8,2u8,3u8,3u8,1u8,10u8,2u8,3u8,6u8,8u8,0u8,3u8,3u8,1u8, - 8u8,0u8,1u8,11u8,1u8,1u8,8u8,0u8,1u8,2u8,2u8,7u8,10u8,9u8,0u8,10u8,9u8,0u8,8u8,1u8, - 3u8,3u8,3u8,6u8,10u8,2u8,8u8,0u8,8u8,0u8,3u8,1u8,6u8,10u8,9u8,0u8,5u8,1u8,1u8,1u8, - 6u8,10u8,2u8,3u8,1u8,9u8,0u8,1u8,11u8,1u8,1u8,9u8,0u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8, - 6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,6u8,83u8,116u8,114u8,105u8,110u8, - 103u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8,11u8,97u8,112u8,112u8,101u8,110u8,100u8,95u8,117u8,116u8,102u8,56u8, - 5u8,98u8,121u8,116u8,101u8,115u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,6u8,105u8,110u8,115u8,101u8, - 114u8,116u8,19u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,95u8,99u8,104u8,101u8,99u8,107u8,95u8,117u8,116u8, - 102u8,56u8,17u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,95u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8, - 25u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,95u8,105u8,115u8,95u8,99u8,104u8,97u8,114u8,95u8,98u8,111u8, - 117u8,110u8,100u8,97u8,114u8,121u8,19u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,95u8,115u8,117u8,98u8,95u8, - 115u8,116u8,114u8,105u8,110u8,103u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8, - 116u8,104u8,10u8,115u8,117u8,98u8,95u8,115u8,116u8,114u8,105u8,110u8,103u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8, - 8u8,116u8,114u8,121u8,95u8,117u8,116u8,102u8,56u8,4u8,117u8,116u8,102u8,56u8,4u8,115u8,111u8,109u8,101u8,4u8, - 110u8,111u8,110u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8, - 111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,94u8,2u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,13u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,85u8,84u8,70u8,56u8,25u8, - 65u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,85u8,84u8,70u8,56u8,32u8,101u8,110u8,99u8,111u8, - 100u8,105u8,110u8,103u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,73u8,78u8,86u8,65u8,76u8, - 73u8,68u8,95u8,73u8,78u8,68u8,69u8,88u8,19u8,73u8,110u8,100u8,101u8,120u8,32u8,111u8,117u8,116u8,32u8,111u8, - 102u8,32u8,114u8,97u8,110u8,103u8,101u8,46u8,0u8,0u8,0u8,2u8,1u8,6u8,10u8,2u8,0u8,1u8,0u8,0u8, - 1u8,7u8,11u8,0u8,15u8,0u8,14u8,1u8,16u8,0u8,20u8,56u8,0u8,2u8,1u8,1u8,0u8,0u8,1u8,5u8, - 11u8,0u8,11u8,1u8,17u8,13u8,17u8,0u8,2u8,2u8,1u8,0u8,0u8,1u8,3u8,11u8,0u8,16u8,0u8,2u8, - 3u8,1u8,0u8,0u8,1u8,6u8,11u8,0u8,16u8,0u8,11u8,1u8,16u8,0u8,17u8,6u8,2u8,4u8,1u8,0u8, - 0u8,18u8,56u8,10u8,0u8,16u8,0u8,12u8,7u8,10u8,1u8,10u8,7u8,65u8,16u8,37u8,4u8,13u8,11u8,7u8, - 10u8,1u8,17u8,7u8,12u8,3u8,5u8,17u8,11u8,7u8,1u8,9u8,12u8,3u8,11u8,3u8,4u8,20u8,5u8,24u8, - 11u8,0u8,1u8,7u8,0u8,39u8,10u8,0u8,46u8,17u8,10u8,12u8,10u8,10u8,0u8,10u8,1u8,12u8,4u8,46u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,4u8,17u8,11u8,12u8,9u8,10u8,0u8,11u8,1u8,11u8, - 10u8,12u8,6u8,12u8,5u8,46u8,11u8,5u8,11u8,6u8,17u8,11u8,12u8,8u8,13u8,9u8,11u8,2u8,17u8,0u8, - 13u8,9u8,11u8,8u8,17u8,0u8,11u8,9u8,11u8,0u8,21u8,2u8,5u8,1u8,2u8,0u8,6u8,0u8,2u8,0u8, - 7u8,0u8,2u8,0u8,8u8,0u8,2u8,0u8,9u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,0u8,56u8,1u8, - 2u8,10u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,0u8,65u8,16u8,2u8,11u8,1u8,0u8,0u8,20u8,48u8, - 11u8,0u8,16u8,0u8,12u8,6u8,10u8,6u8,65u8,16u8,12u8,7u8,10u8,2u8,11u8,7u8,37u8,4u8,15u8,10u8, - 1u8,10u8,2u8,37u8,12u8,3u8,5u8,17u8,9u8,12u8,3u8,11u8,3u8,4u8,24u8,10u8,6u8,10u8,1u8,17u8, - 7u8,12u8,4u8,5u8,26u8,9u8,12u8,4u8,11u8,4u8,4u8,33u8,10u8,6u8,10u8,2u8,17u8,7u8,12u8,5u8, - 5u8,35u8,9u8,12u8,5u8,11u8,5u8,4u8,38u8,5u8,42u8,11u8,6u8,1u8,7u8,0u8,39u8,11u8,6u8,11u8, - 1u8,11u8,2u8,17u8,8u8,18u8,0u8,2u8,12u8,1u8,0u8,0u8,15u8,12u8,14u8,0u8,17u8,5u8,4u8,8u8, - 11u8,0u8,18u8,0u8,56u8,2u8,12u8,1u8,5u8,10u8,56u8,3u8,12u8,1u8,11u8,1u8,2u8,13u8,1u8,0u8, - 0u8,1u8,9u8,14u8,0u8,17u8,5u8,4u8,4u8,5u8,6u8,7u8,1u8,39u8,11u8,0u8,18u8,0u8,2u8,0u8, - 0u8,0u8, - ]; - vector::push_back(&mut code, chunk10); - let chunk1 = vector[ - 10u8,77u8,111u8,118u8,101u8,83u8,116u8,100u8,108u8,105u8,98u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,64u8,48u8,49u8,69u8,55u8,67u8,65u8,55u8,69u8,66u8,57u8,68u8,49u8,51u8,48u8,51u8,57u8,56u8,48u8, - 70u8,66u8,67u8,56u8,53u8,49u8,53u8,51u8,53u8,53u8,48u8,70u8,57u8,50u8,54u8,67u8,65u8,70u8,54u8,49u8, - 69u8,57u8,70u8,67u8,65u8,69u8,66u8,54u8,69u8,55u8,57u8,49u8,67u8,68u8,69u8,57u8,66u8,51u8,69u8,67u8, - 70u8,68u8,55u8,50u8,66u8,65u8,96u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,1u8,73u8,0u8, - 182u8,255u8,91u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,93u8,10u8,110u8,97u8,109u8,101u8,32u8,61u8,32u8,34u8, - 77u8,111u8,118u8,101u8,83u8,116u8,100u8,108u8,105u8,98u8,34u8,10u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,32u8, - 61u8,32u8,34u8,49u8,46u8,53u8,46u8,48u8,34u8,10u8,10u8,91u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8, - 115u8,93u8,10u8,115u8,116u8,100u8,32u8,61u8,32u8,34u8,48u8,120u8,49u8,34u8,10u8,209u8,25u8,56u8,252u8,73u8, - 0u8,0u8,0u8,11u8,3u8,98u8,99u8,115u8,152u8,3u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8, - 141u8,82u8,189u8,78u8,228u8,48u8,16u8,238u8,243u8,20u8,35u8,157u8,116u8,7u8,69u8,146u8,246u8,148u8,227u8,40u8, - 160u8,134u8,130u8,21u8,180u8,224u8,56u8,19u8,50u8,146u8,99u8,71u8,246u8,56u8,176u8,160u8,125u8,119u8,198u8,78u8, - 178u8,66u8,32u8,16u8,174u8,146u8,241u8,231u8,239u8,207u8,174u8,235u8,26u8,110u8,153u8,12u8,241u8,30u8,122u8,231u8, - 65u8,59u8,59u8,163u8,103u8,178u8,143u8,160u8,224u8,202u8,205u8,8u8,179u8,50u8,17u8,129u8,29u8,16u8,7u8,104u8, - 201u8,42u8,191u8,7u8,143u8,147u8,199u8,128u8,150u8,21u8,147u8,179u8,64u8,22u8,46u8,46u8,119u8,112u8,114u8,177u8, - 236u8,94u8,42u8,235u8,44u8,105u8,101u8,138u8,90u8,216u8,119u8,232u8,73u8,25u8,122u8,201u8,208u8,211u8,42u8,35u8, - 41u8,0u8,15u8,184u8,177u8,161u8,213u8,174u8,75u8,138u8,201u8,64u8,214u8,20u8,114u8,23u8,189u8,198u8,0u8,202u8, - 118u8,224u8,4u8,235u8,65u8,56u8,203u8,209u8,117u8,209u8,172u8,150u8,66u8,102u8,159u8,98u8,107u8,40u8,12u8,40u8, - 40u8,91u8,234u8,65u8,145u8,173u8,68u8,17u8,97u8,96u8,158u8,66u8,83u8,215u8,143u8,196u8,67u8,108u8,43u8,237u8, - 198u8,90u8,77u8,236u8,66u8,105u8,84u8,27u8,234u8,86u8,135u8,95u8,139u8,120u8,169u8,55u8,179u8,101u8,120u8,111u8, - 180u8,20u8,72u8,246u8,51u8,58u8,143u8,89u8,169u8,67u8,86u8,100u8,130u8,232u8,164u8,8u8,85u8,177u8,122u8,9u8, - 220u8,53u8,77u8,2u8,191u8,22u8,32u8,43u8,33u8,111u8,144u8,163u8,183u8,239u8,19u8,126u8,232u8,203u8,245u8,240u8, - 48u8,63u8,124u8,89u8,219u8,135u8,202u8,146u8,141u8,81u8,113u8,166u8,183u8,50u8,146u8,126u8,114u8,106u8,13u8,125u8, - 20u8,21u8,119u8,223u8,238u8,25u8,195u8,89u8,42u8,238u8,46u8,21u8,115u8,126u8,50u8,55u8,240u8,251u8,248u8,123u8, - 218u8,192u8,140u8,154u8,157u8,63u8,139u8,127u8,207u8,255u8,21u8,171u8,75u8,248u8,255u8,237u8,218u8,80u8,87u8,75u8, - 200u8,221u8,132u8,154u8,122u8,241u8,150u8,12u8,229u8,189u8,32u8,19u8,88u8,43u8,120u8,61u8,36u8,104u8,120u8,34u8, - 214u8,67u8,122u8,40u8,235u8,184u8,115u8,58u8,142u8,199u8,204u8,242u8,176u8,24u8,159u8,185u8,248u8,124u8,58u8,79u8, - 182u8,238u8,174u8,151u8,128u8,146u8,76u8,231u8,99u8,79u8,3u8,9u8,169u8,188u8,151u8,14u8,123u8,178u8,114u8,205u8, - 180u8,20u8,59u8,121u8,201u8,231u8,255u8,4u8,249u8,64u8,19u8,59u8,172u8,142u8,44u8,246u8,200u8,0u8,219u8,141u8, - 226u8,79u8,203u8,73u8,231u8,15u8,197u8,161u8,120u8,3u8,225u8,252u8,251u8,248u8,22u8,3u8,0u8,0u8,0u8,0u8, - 13u8,102u8,105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8,51u8,50u8,180u8,21u8,31u8,139u8,8u8,0u8, - 0u8,0u8,0u8,0u8,2u8,255u8,213u8,90u8,91u8,83u8,27u8,57u8,22u8,126u8,231u8,87u8,40u8,15u8,59u8,133u8, - 179u8,142u8,193u8,64u8,72u8,6u8,66u8,170u8,216u8,36u8,108u8,81u8,53u8,129u8,20u8,73u8,246u8,246u8,48u8,142u8, - 220u8,45u8,227u8,222u8,180u8,91u8,78u8,95u8,48u8,30u8,134u8,255u8,190u8,223u8,145u8,212u8,45u8,169u8,47u8,182u8, - 195u8,146u8,169u8,154u8,84u8,30u8,140u8,91u8,58u8,55u8,125u8,231u8,59u8,71u8,167u8,189u8,179u8,179u8,195u8,222u8, - 138u8,73u8,148u8,136u8,140u8,113u8,54u8,137u8,110u8,69u8,248u8,108u8,46u8,163u8,36u8,103u8,73u8,49u8,19u8,105u8, - 20u8,176u8,124u8,57u8,23u8,108u8,17u8,229u8,83u8,60u8,223u8,223u8,123u8,54u8,142u8,114u8,134u8,199u8,226u8,90u8, - 164u8,108u8,206u8,211u8,156u8,241u8,36u8,220u8,218u8,129u8,148u8,234u8,233u8,36u8,229u8,65u8,30u8,201u8,132u8,199u8, - 106u8,193u8,96u8,107u8,107u8,38u8,195u8,34u8,22u8,44u8,203u8,195u8,163u8,35u8,165u8,98u8,164u8,84u8,236u8,239u8, - 177u8,187u8,173u8,45u8,134u8,127u8,59u8,149u8,21u8,107u8,141u8,192u8,38u8,71u8,1u8,244u8,101u8,131u8,74u8,196u8, - 167u8,105u8,148u8,49u8,252u8,255u8,111u8,145u8,193u8,46u8,86u8,28u8,30u8,84u8,166u8,142u8,11u8,152u8,157u8,211u8, - 195u8,69u8,202u8,231u8,115u8,17u8,226u8,9u8,150u8,100u8,121u8,90u8,4u8,57u8,203u8,101u8,37u8,99u8,198u8,191u8, - 146u8,17u8,69u8,18u8,125u8,43u8,132u8,210u8,59u8,168u8,228u8,114u8,232u8,75u8,120u8,186u8,100u8,169u8,152u8,167u8, - 34u8,19u8,73u8,206u8,201u8,142u8,62u8,203u8,36u8,11u8,69u8,16u8,205u8,120u8,92u8,201u8,185u8,225u8,113u8,129u8, - 152u8,206u8,248u8,146u8,37u8,50u8,103u8,99u8,193u8,196u8,45u8,204u8,142u8,221u8,205u8,227u8,88u8,244u8,75u8,203u8, - 230u8,169u8,188u8,137u8,66u8,218u8,33u8,83u8,81u8,73u8,201u8,167u8,60u8,97u8,63u8,151u8,194u8,89u8,24u8,93u8, - 195u8,97u8,38u8,39u8,88u8,142u8,175u8,50u8,40u8,103u8,99u8,137u8,168u8,140u8,197u8,4u8,219u8,232u8,44u8,24u8, - 159u8,228u8,240u8,55u8,159u8,90u8,33u8,229u8,110u8,29u8,210u8,237u8,225u8,203u8,82u8,78u8,46u8,115u8,30u8,247u8, - 6u8,236u8,76u8,166u8,44u8,144u8,51u8,156u8,86u8,148u8,145u8,59u8,161u8,44u8,96u8,154u8,213u8,81u8,73u8,154u8, - 196u8,18u8,30u8,39u8,215u8,230u8,116u8,166u8,60u8,99u8,177u8,200u8,50u8,109u8,230u8,240u8,112u8,149u8,157u8,20u8, - 164u8,74u8,14u8,162u8,17u8,240u8,84u8,76u8,138u8,152u8,241u8,177u8,68u8,0u8,138u8,12u8,82u8,235u8,226u8,115u8, - 9u8,171u8,146u8,27u8,1u8,144u8,193u8,157u8,76u8,148u8,49u8,117u8,78u8,203u8,40u8,212u8,16u8,48u8,135u8,121u8, - 70u8,248u8,249u8,96u8,0u8,70u8,38u8,6u8,114u8,190u8,132u8,83u8,169u8,156u8,195u8,136u8,156u8,226u8,116u8,167u8, - 69u8,29u8,41u8,136u8,220u8,107u8,12u8,66u8,21u8,80u8,243u8,254u8,244u8,95u8,163u8,207u8,135u8,7u8,120u8,50u8, - 220u8,123u8,201u8,78u8,216u8,240u8,229u8,193u8,193u8,225u8,139u8,131u8,131u8,221u8,23u8,251u8,47u8,118u8,127u8,126u8, - 254u8,124u8,120u8,56u8,124u8,126u8,188u8,229u8,32u8,78u8,192u8,134u8,68u8,206u8,128u8,11u8,72u8,46u8,79u8,49u8, - 100u8,11u8,232u8,253u8,77u8,164u8,210u8,17u8,253u8,238u8,237u8,187u8,139u8,203u8,247u8,231u8,23u8,167u8,159u8,46u8, - 175u8,180u8,230u8,19u8,182u8,123u8,59u8,220u8,221u8,221u8,29u8,30u8,123u8,2u8,191u8,21u8,50u8,143u8,128u8,15u8, - 109u8,36u8,91u8,200u8,34u8,14u8,41u8,100u8,185u8,148u8,44u8,230u8,233u8,53u8,125u8,162u8,191u8,167u8,34u8,54u8, - 48u8,254u8,2u8,105u8,95u8,60u8,85u8,231u8,255u8,56u8,255u8,120u8,126u8,121u8,97u8,245u8,236u8,65u8,207u8,158u8, - 175u8,103u8,86u8,196u8,121u8,52u8,143u8,35u8,152u8,251u8,112u8,77u8,239u8,63u8,255u8,242u8,233u8,252u8,195u8,47u8, - 231u8,111u8,78u8,63u8,53u8,244u8,237u8,91u8,125u8,167u8,192u8,196u8,141u8,1u8,236u8,82u8,69u8,70u8,133u8,72u8, - 36u8,129u8,44u8,144u8,162u8,169u8,8u8,219u8,204u8,31u8,253u8,237u8,223u8,163u8,255u8,188u8,187u8,186u8,244u8,195u8, - 117u8,224u8,187u8,65u8,200u8,45u8,114u8,56u8,145u8,82u8,54u8,178u8,197u8,84u8,36u8,37u8,110u8,8u8,84u8,240u8, - 0u8,102u8,187u8,160u8,248u8,98u8,29u8,45u8,18u8,47u8,27u8,93u8,27u8,174u8,200u8,161u8,209u8,229u8,231u8,79u8, - 163u8,203u8,179u8,209u8,213u8,233u8,197u8,223u8,223u8,249u8,206u8,185u8,48u8,120u8,175u8,35u8,185u8,172u8,147u8,206u8, - 178u8,73u8,103u8,99u8,145u8,246u8,25u8,128u8,154u8,4u8,10u8,234u8,72u8,217u8,165u8,77u8,47u8,159u8,57u8,41u8, - 127u8,0u8,124u8,2u8,85u8,8u8,96u8,27u8,30u8,90u8,68u8,177u8,202u8,26u8,60u8,143u8,188u8,231u8,149u8,24u8, - 9u8,215u8,145u8,74u8,11u8,195u8,140u8,115u8,36u8,51u8,40u8,116u8,82u8,36u8,229u8,137u8,47u8,71u8,176u8,114u8, - 27u8,103u8,174u8,60u8,234u8,91u8,32u8,164u8,71u8,94u8,246u8,244u8,180u8,199u8,119u8,74u8,140u8,150u8,174u8,34u8, - 110u8,20u8,42u8,251u8,22u8,146u8,97u8,9u8,241u8,190u8,201u8,78u8,202u8,56u8,202u8,29u8,162u8,102u8,197u8,140u8, - 115u8,88u8,35u8,211u8,89u8,69u8,73u8,70u8,80u8,169u8,52u8,80u8,28u8,170u8,201u8,93u8,37u8,29u8,113u8,110u8, - 166u8,168u8,236u8,171u8,16u8,115u8,229u8,33u8,136u8,34u8,46u8,133u8,122u8,238u8,26u8,89u8,116u8,198u8,55u8,50u8, - 10u8,89u8,44u8,21u8,145u8,240u8,32u8,40u8,16u8,203u8,229u8,160u8,90u8,20u8,11u8,144u8,76u8,146u8,5u8,60u8, - 166u8,234u8,99u8,236u8,63u8,97u8,20u8,4u8,6u8,131u8,73u8,113u8,143u8,61u8,101u8,219u8,54u8,20u8,3u8,157u8, - 19u8,229u8,195u8,227u8,122u8,16u8,74u8,105u8,85u8,52u8,200u8,241u8,102u8,117u8,98u8,219u8,147u8,84u8,42u8,231u8, - 157u8,48u8,247u8,92u8,97u8,8u8,17u8,16u8,72u8,178u8,168u8,14u8,0u8,51u8,217u8,52u8,154u8,104u8,108u8,44u8, - 80u8,61u8,104u8,39u8,14u8,211u8,41u8,117u8,165u8,63u8,214u8,141u8,134u8,103u8,175u8,95u8,195u8,20u8,207u8,228u8, - 55u8,83u8,17u8,124u8,165u8,228u8,128u8,60u8,85u8,30u8,76u8,206u8,71u8,153u8,205u8,118u8,43u8,158u8,103u8,25u8, - 210u8,231u8,201u8,118u8,41u8,239u8,213u8,73u8,201u8,140u8,253u8,122u8,202u8,59u8,145u8,169u8,150u8,83u8,208u8,14u8, - 15u8,180u8,151u8,247u8,154u8,155u8,231u8,34u8,240u8,224u8,231u8,160u8,106u8,158u8,242u8,235u8,25u8,103u8,114u8,206u8, - 81u8,114u8,173u8,180u8,40u8,9u8,226u8,34u8,20u8,85u8,114u8,157u8,18u8,230u8,179u8,243u8,137u8,93u8,33u8,146u8, - 172u8,64u8,232u8,40u8,124u8,88u8,195u8,78u8,78u8,148u8,154u8,81u8,29u8,229u8,46u8,192u8,141u8,181u8,142u8,81u8, - 89u8,48u8,21u8,208u8,94u8,215u8,226u8,216u8,167u8,18u8,5u8,169u8,107u8,21u8,119u8,37u8,140u8,93u8,161u8,18u8, - 52u8,27u8,33u8,67u8,215u8,155u8,196u8,94u8,151u8,193u8,213u8,89u8,80u8,11u8,112u8,195u8,98u8,202u8,228u8,86u8, - 169u8,202u8,202u8,149u8,233u8,140u8,231u8,142u8,99u8,10u8,253u8,79u8,89u8,29u8,243u8,61u8,13u8,31u8,163u8,214u8, - 54u8,104u8,17u8,149u8,182u8,31u8,67u8,113u8,101u8,201u8,235u8,228u8,56u8,85u8,61u8,100u8,90u8,201u8,137u8,116u8, - 121u8,101u8,168u8,185u8,145u8,47u8,98u8,5u8,253u8,133u8,202u8,131u8,26u8,249u8,25u8,201u8,235u8,153u8,79u8,103u8, - 16u8,168u8,172u8,81u8,202u8,154u8,121u8,99u8,132u8,26u8,14u8,121u8,130u8,154u8,209u8,111u8,86u8,53u8,159u8,83u8, - 206u8,162u8,20u8,101u8,167u8,106u8,120u8,100u8,69u8,162u8,138u8,10u8,115u8,170u8,106u8,138u8,24u8,144u8,250u8,147u8, - 170u8,99u8,53u8,123u8,121u8,24u8,214u8,168u8,71u8,5u8,103u8,172u8,155u8,188u8,42u8,128u8,232u8,84u8,66u8,159u8, - 66u8,12u8,109u8,104u8,51u8,235u8,116u8,248u8,234u8,149u8,71u8,34u8,180u8,190u8,10u8,242u8,137u8,191u8,117u8,135u8, - 213u8,92u8,110u8,165u8,205u8,135u8,113u8,80u8,165u8,211u8,35u8,161u8,50u8,152u8,190u8,6u8,43u8,147u8,26u8,111u8, - 183u8,147u8,233u8,67u8,111u8,20u8,76u8,53u8,180u8,2u8,94u8,160u8,157u8,164u8,197u8,1u8,207u8,84u8,176u8,39u8, - 60u8,138u8,93u8,65u8,250u8,194u8,131u8,142u8,39u8,197u8,135u8,153u8,200u8,129u8,32u8,145u8,166u8,112u8,207u8,38u8, - 79u8,101u8,87u8,7u8,219u8,89u8,180u8,109u8,206u8,117u8,58u8,199u8,54u8,102u8,58u8,31u8,208u8,21u8,150u8,59u8, - 73u8,206u8,23u8,191u8,146u8,226u8,90u8,211u8,162u8,141u8,223u8,252u8,115u8,135u8,101u8,187u8,134u8,197u8,234u8,112u8, - 239u8,36u8,199u8,46u8,47u8,26u8,188u8,88u8,74u8,236u8,102u8,196u8,122u8,134u8,43u8,62u8,236u8,202u8,240u8,22u8, - 50u8,84u8,144u8,239u8,1u8,206u8,158u8,87u8,117u8,38u8,124u8,147u8,10u8,158u8,215u8,175u8,170u8,218u8,127u8,85u8, - 236u8,185u8,110u8,69u8,41u8,15u8,53u8,29u8,42u8,227u8,162u8,9u8,117u8,218u8,160u8,12u8,100u8,101u8,37u8,74u8, - 93u8,112u8,213u8,213u8,129u8,178u8,220u8,185u8,74u8,12u8,216u8,27u8,30u8,199u8,170u8,129u8,37u8,70u8,132u8,135u8, - 42u8,181u8,193u8,1u8,101u8,239u8,138u8,190u8,117u8,2u8,76u8,154u8,206u8,89u8,145u8,43u8,196u8,232u8,155u8,212u8, - 151u8,143u8,34u8,158u8,28u8,29u8,5u8,202u8,208u8,17u8,25u8,53u8,74u8,249u8,66u8,39u8,235u8,23u8,147u8,9u8, - 116u8,161u8,141u8,51u8,234u8,156u8,128u8,125u8,234u8,126u8,107u8,23u8,233u8,22u8,18u8,118u8,110u8,58u8,134u8,128u8, - 7u8,236u8,60u8,55u8,43u8,99u8,231u8,130u8,231u8,109u8,179u8,62u8,98u8,83u8,34u8,19u8,197u8,77u8,134u8,212u8, - 76u8,211u8,174u8,30u8,208u8,96u8,193u8,124u8,151u8,92u8,219u8,235u8,235u8,222u8,175u8,207u8,192u8,109u8,131u8,1u8, - 62u8,96u8,194u8,48u8,28u8,176u8,127u8,42u8,46u8,84u8,17u8,93u8,146u8,179u8,229u8,173u8,179u8,164u8,63u8,180u8, - 157u8,245u8,11u8,102u8,37u8,44u8,197u8,117u8,35u8,164u8,77u8,42u8,155u8,179u8,35u8,178u8,113u8,41u8,11u8,253u8, - 61u8,17u8,65u8,24u8,101u8,243u8,24u8,220u8,113u8,81u8,94u8,98u8,171u8,251u8,116u8,227u8,138u8,175u8,206u8,189u8, - 175u8,182u8,7u8,160u8,10u8,162u8,20u8,238u8,197u8,8u8,197u8,109u8,184u8,251u8,235u8,133u8,109u8,79u8,53u8,26u8, - 50u8,162u8,192u8,84u8,120u8,119u8,116u8,144u8,62u8,26u8,63u8,72u8,143u8,89u8,52u8,179u8,151u8,123u8,19u8,144u8, - 214u8,177u8,3u8,106u8,5u8,33u8,33u8,152u8,82u8,180u8,60u8,97u8,165u8,147u8,125u8,38u8,6u8,215u8,131u8,62u8, - 219u8,29u8,236u8,14u8,247u8,158u8,235u8,99u8,210u8,142u8,134u8,114u8,145u8,144u8,89u8,234u8,9u8,180u8,100u8,185u8, - 224u8,33u8,217u8,91u8,204u8,203u8,175u8,247u8,27u8,229u8,211u8,71u8,147u8,6u8,248u8,118u8,117u8,184u8,101u8,57u8, - 181u8,254u8,171u8,111u8,122u8,126u8,218u8,249u8,37u8,245u8,188u8,19u8,87u8,125u8,13u8,123u8,139u8,193u8,129u8,187u8, - 239u8,163u8,234u8,150u8,125u8,108u8,193u8,236u8,41u8,191u8,17u8,116u8,27u8,169u8,183u8,224u8,37u8,214u8,28u8,61u8, - 181u8,219u8,131u8,218u8,217u8,108u8,222u8,213u8,253u8,5u8,195u8,141u8,220u8,239u8,48u8,148u8,85u8,102u8,139u8,43u8, - 168u8,117u8,48u8,85u8,43u8,181u8,214u8,96u8,148u8,91u8,39u8,251u8,157u8,162u8,123u8,120u8,112u8,220u8,182u8,211u8, - 141u8,18u8,246u8,186u8,127u8,118u8,149u8,236u8,178u8,132u8,182u8,72u8,40u8,123u8,18u8,59u8,147u8,232u8,173u8,43u8, - 245u8,214u8,218u8,157u8,22u8,155u8,142u8,187u8,11u8,55u8,233u8,98u8,191u8,255u8,238u8,28u8,214u8,137u8,214u8,222u8, - 188u8,99u8,251u8,229u8,252u8,74u8,228u8,69u8,154u8,248u8,193u8,231u8,89u8,107u8,199u8,9u8,90u8,0u8,251u8,170u8, - 30u8,42u8,17u8,66u8,165u8,114u8,208u8,104u8,55u8,168u8,218u8,187u8,242u8,41u8,109u8,179u8,34u8,8u8,176u8,97u8, - 211u8,190u8,99u8,165u8,201u8,62u8,210u8,203u8,33u8,83u8,163u8,83u8,48u8,165u8,203u8,41u8,96u8,109u8,169u8,181u8, - 121u8,215u8,160u8,235u8,209u8,25u8,54u8,95u8,153u8,189u8,27u8,119u8,16u8,171u8,115u8,218u8,75u8,231u8,206u8,174u8, - 162u8,91u8,189u8,227u8,130u8,207u8,19u8,78u8,159u8,81u8,227u8,139u8,227u8,7u8,36u8,205u8,163u8,230u8,204u8,255u8, - 137u8,125u8,219u8,218u8,180u8,88u8,224u8,52u8,71u8,54u8,239u8,218u8,54u8,91u8,3u8,104u8,203u8,79u8,63u8,53u8, - 205u8,120u8,98u8,101u8,53u8,33u8,185u8,82u8,100u8,189u8,171u8,234u8,218u8,222u8,214u8,95u8,173u8,43u8,1u8,186u8, - 223u8,114u8,143u8,20u8,223u8,116u8,151u8,0u8,247u8,235u8,187u8,50u8,91u8,172u8,143u8,234u8,92u8,123u8,234u8,114u8, - 225u8,68u8,81u8,247u8,104u8,247u8,171u8,155u8,178u8,214u8,158u8,108u8,161u8,191u8,88u8,83u8,216u8,76u8,155u8,180u8, - 109u8,71u8,196u8,155u8,57u8,80u8,38u8,252u8,186u8,236u8,54u8,242u8,215u8,167u8,183u8,61u8,187u8,9u8,154u8,43u8, - 209u8,149u8,203u8,182u8,243u8,86u8,31u8,142u8,235u8,129u8,57u8,5u8,191u8,101u8,232u8,101u8,85u8,123u8,168u8,219u8, - 171u8,133u8,186u8,221u8,234u8,96u8,176u8,75u8,69u8,143u8,106u8,142u8,143u8,81u8,234u8,12u8,141u8,133u8,156u8,11u8, - 125u8,184u8,84u8,0u8,11u8,180u8,138u8,220u8,54u8,172u8,184u8,106u8,82u8,251u8,4u8,81u8,89u8,49u8,206u8,85u8, - 193u8,195u8,159u8,94u8,28u8,244u8,32u8,176u8,175u8,216u8,21u8,141u8,88u8,40u8,241u8,90u8,71u8,183u8,165u8,70u8, - 123u8,253u8,37u8,73u8,24u8,161u8,225u8,193u8,171u8,145u8,198u8,193u8,92u8,139u8,220u8,57u8,16u8,0u8,99u8,205u8, - 45u8,29u8,43u8,218u8,59u8,118u8,93u8,72u8,50u8,154u8,74u8,136u8,178u8,45u8,173u8,58u8,79u8,123u8,133u8,119u8, - 84u8,71u8,217u8,136u8,190u8,111u8,85u8,58u8,150u8,50u8,110u8,211u8,170u8,178u8,181u8,83u8,53u8,84u8,170u8,6u8, - 79u8,164u8,229u8,212u8,131u8,134u8,167u8,94u8,228u8,76u8,127u8,216u8,156u8,219u8,70u8,9u8,25u8,50u8,244u8,45u8, - 233u8,211u8,250u8,189u8,134u8,117u8,29u8,80u8,133u8,215u8,74u8,134u8,49u8,245u8,149u8,218u8,92u8,14u8,124u8,236u8, - 50u8,227u8,208u8,176u8,250u8,226u8,158u8,9u8,128u8,175u8,185u8,194u8,118u8,62u8,13u8,188u8,195u8,220u8,71u8,67u8, - 183u8,157u8,234u8,153u8,24u8,104u8,175u8,123u8,43u8,166u8,98u8,127u8,138u8,96u8,181u8,226u8,67u8,77u8,42u8,30u8, - 0u8,15u8,126u8,251u8,216u8,30u8,191u8,254u8,145u8,240u8,224u8,183u8,63u8,0u8,30u8,38u8,6u8,235u8,225u8,241u8, - 167u8,8u8,214u8,230u8,101u8,205u8,50u8,249u8,170u8,178u8,230u8,206u8,60u8,187u8,253u8,162u8,222u8,103u8,163u8,89u8, - 96u8,217u8,35u8,155u8,244u8,120u8,96u8,131u8,92u8,86u8,124u8,59u8,50u8,132u8,113u8,43u8,171u8,231u8,119u8,13u8, - 211u8,108u8,95u8,10u8,203u8,30u8,212u8,17u8,155u8,168u8,109u8,208u8,249u8,58u8,26u8,86u8,142u8,213u8,190u8,119u8, - 232u8,218u8,232u8,39u8,75u8,196u8,153u8,128u8,111u8,214u8,170u8,185u8,243u8,176u8,77u8,155u8,25u8,115u8,56u8,213u8, - 84u8,108u8,61u8,113u8,101u8,246u8,151u8,33u8,246u8,87u8,1u8,104u8,20u8,196u8,183u8,2u8,66u8,212u8,123u8,216u8, - 235u8,232u8,6u8,179u8,27u8,115u8,99u8,171u8,227u8,21u8,67u8,123u8,153u8,126u8,79u8,149u8,247u8,222u8,83u8,216u8, - 8u8,144u8,152u8,199u8,39u8,152u8,202u8,186u8,21u8,220u8,162u8,215u8,168u8,72u8,175u8,244u8,128u8,80u8,224u8,12u8, - 11u8,84u8,231u8,102u8,92u8,250u8,11u8,219u8,30u8,154u8,22u8,247u8,216u8,227u8,27u8,119u8,61u8,218u8,140u8,58u8, - 207u8,88u8,9u8,54u8,40u8,157u8,148u8,179u8,109u8,87u8,63u8,115u8,12u8,233u8,213u8,247u8,54u8,142u8,156u8,134u8, - 71u8,153u8,26u8,18u8,225u8,208u8,245u8,97u8,122u8,168u8,49u8,47u8,27u8,18u8,113u8,155u8,215u8,49u8,209u8,164u8, - 39u8,17u8,197u8,27u8,156u8,182u8,138u8,21u8,197u8,85u8,95u8,127u8,16u8,44u8,123u8,18u8,245u8,108u8,49u8,188u8, - 108u8,187u8,49u8,103u8,99u8,61u8,96u8,169u8,158u8,45u8,184u8,162u8,107u8,222u8,31u8,215u8,121u8,145u8,146u8,117u8, - 219u8,221u8,80u8,101u8,237u8,95u8,237u8,169u8,185u8,239u8,67u8,105u8,143u8,18u8,218u8,245u8,142u8,128u8,98u8,224u8, - 141u8,196u8,48u8,123u8,189u8,124u8,123u8,121u8,196u8,22u8,50u8,253u8,170u8,127u8,164u8,164u8,222u8,240u8,211u8,91u8, - 10u8,250u8,161u8,80u8,150u8,227u8,165u8,153u8,158u8,110u8,228u8,209u8,76u8,48u8,250u8,237u8,12u8,58u8,235u8,128u8, - 50u8,16u8,75u8,126u8,219u8,199u8,217u8,132u8,72u8,251u8,58u8,248u8,49u8,94u8,196u8,176u8,148u8,98u8,231u8,99u8, - 253u8,145u8,114u8,163u8,60u8,202u8,21u8,169u8,161u8,150u8,60u8,126u8,102u8,208u8,122u8,186u8,95u8,156u8,176u8,97u8, - 27u8,24u8,254u8,128u8,164u8,193u8,217u8,195u8,128u8,245u8,169u8,227u8,176u8,165u8,22u8,130u8,46u8,143u8,119u8,100u8, - 15u8,70u8,215u8,43u8,18u8,71u8,141u8,112u8,31u8,57u8,115u8,104u8,237u8,152u8,228u8,210u8,156u8,217u8,203u8,27u8, - 194u8,118u8,21u8,120u8,220u8,197u8,235u8,188u8,100u8,243u8,237u8,85u8,37u8,161u8,30u8,231u8,21u8,73u8,214u8,22u8, - 233u8,10u8,78u8,221u8,61u8,164u8,30u8,99u8,63u8,58u8,201u8,87u8,161u8,93u8,129u8,100u8,189u8,230u8,199u8,64u8, - 217u8,57u8,2u8,47u8,230u8,15u8,1u8,124u8,247u8,113u8,108u8,74u8,255u8,143u8,157u8,5u8,236u8,105u8,237u8,31u8, - 251u8,248u8,225u8,221u8,155u8,243u8,51u8,243u8,67u8,135u8,143u8,141u8,231u8,91u8,206u8,165u8,65u8,255u8,134u8,244u8, - 238u8,94u8,253u8,100u8,6u8,147u8,43u8,140u8,37u8,66u8,25u8,96u8,86u8,100u8,95u8,135u8,32u8,95u8,110u8,213u8, - 219u8,92u8,179u8,56u8,22u8,55u8,34u8,110u8,145u8,81u8,71u8,77u8,133u8,146u8,17u8,110u8,253u8,89u8,158u8,70u8, - 65u8,94u8,30u8,255u8,253u8,214u8,255u8,0u8,101u8,51u8,37u8,65u8,34u8,43u8,0u8,0u8,0u8,0u8,4u8,104u8, - 97u8,115u8,104u8,204u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,141u8,78u8,205u8,106u8,2u8, - 49u8,16u8,190u8,239u8,83u8,124u8,183u8,182u8,23u8,3u8,150u8,150u8,178u8,45u8,5u8,111u8,94u8,60u8,233u8,93u8, - 178u8,201u8,44u8,9u8,196u8,68u8,50u8,147u8,21u8,17u8,223u8,221u8,172u8,235u8,65u8,15u8,130u8,3u8,51u8,204u8, - 223u8,247u8,163u8,148u8,194u8,42u8,217u8,18u8,8u8,7u8,231u8,141u8,131u8,165u8,222u8,71u8,98u8,172u8,151u8,11u8, - 56u8,205u8,174u8,182u8,125u8,202u8,232u8,142u8,66u8,24u8,200u8,72u8,202u8,60u8,107u8,148u8,82u8,99u8,98u8,227u8, - 8u8,125u8,137u8,70u8,124u8,138u8,12u8,31u8,33u8,206u8,51u8,118u8,19u8,159u8,206u8,132u8,168u8,197u8,15u8,20u8, - 142u8,149u8,214u8,132u8,186u8,176u8,232u8,146u8,184u8,233u8,147u8,170u8,240u8,64u8,200u8,37u8,138u8,223u8,209u8,149u8, - 79u8,243u8,195u8,105u8,159u8,107u8,205u8,111u8,92u8,27u8,10u8,197u8,210u8,172u8,185u8,81u8,179u8,216u8,182u8,29u8, - 221u8,225u8,212u8,160u8,198u8,36u8,131u8,125u8,233u8,130u8,55u8,163u8,35u8,176u8,211u8,243u8,237u8,252u8,235u8,251u8, - 221u8,106u8,209u8,237u8,205u8,249u8,95u8,249u8,249u8,255u8,184u8,31u8,126u8,159u8,163u8,63u8,95u8,65u8,159u8,155u8, - 11u8,24u8,21u8,215u8,138u8,64u8,1u8,0u8,0u8,0u8,0u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,149u8,26u8, - 31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,189u8,91u8,107u8,111u8,212u8,60u8,22u8,254u8,206u8,175u8, - 48u8,122u8,165u8,238u8,140u8,54u8,45u8,109u8,217u8,221u8,15u8,189u8,32u8,149u8,23u8,216u8,69u8,90u8,90u8,244u8, - 2u8,239u8,174u8,84u8,85u8,153u8,76u8,226u8,116u8,12u8,153u8,36u8,196u8,201u8,76u8,187u8,208u8,255u8,190u8,231u8, - 28u8,219u8,137u8,115u8,157u8,76u8,233u8,5u8,1u8,211u8,196u8,151u8,227u8,231u8,57u8,62u8,55u8,123u8,94u8,188u8, - 120u8,193u8,206u8,216u8,202u8,203u8,132u8,55u8,143u8,248u8,174u8,20u8,255u8,227u8,1u8,243u8,147u8,56u8,247u8,68u8, - 204u8,51u8,150u8,47u8,188u8,156u8,249u8,94u8,204u8,22u8,73u8,20u8,48u8,47u8,190u8,101u8,249u8,109u8,202u8,247u8, - 216u8,251u8,56u8,224u8,55u8,34u8,190u8,102u8,66u8,178u8,253u8,221u8,185u8,39u8,121u8,224u8,192u8,235u8,224u8,217u8, - 11u8,24u8,111u8,197u8,253u8,60u8,201u8,36u8,243u8,50u8,206u8,174u8,179u8,100u8,141u8,35u8,239u8,177u8,207u8,11u8, - 104u8,187u8,76u8,130u8,34u8,226u8,108u8,225u8,193u8,71u8,28u8,45u8,246u8,114u8,177u8,226u8,44u8,44u8,98u8,63u8, - 23u8,73u8,44u8,247u8,104u8,128u8,63u8,121u8,38u8,66u8,225u8,123u8,248u8,136u8,37u8,161u8,238u8,37u8,149u8,52u8, - 133u8,228u8,240u8,1u8,198u8,74u8,98u8,142u8,191u8,208u8,160u8,60u8,170u8,198u8,80u8,205u8,112u8,114u8,177u8,76u8, - 35u8,190u8,228u8,113u8,206u8,149u8,96u8,129u8,200u8,64u8,180u8,232u8,150u8,137u8,152u8,189u8,78u8,146u8,107u8,65u8, - 98u8,113u8,38u8,83u8,238u8,87u8,19u8,70u8,94u8,124u8,93u8,120u8,215u8,74u8,202u8,121u8,33u8,162u8,124u8,23u8, - 218u8,87u8,195u8,39u8,41u8,207u8,60u8,245u8,81u8,22u8,254u8,130u8,70u8,134u8,150u8,51u8,9u8,120u8,68u8,60u8, - 79u8,98u8,87u8,1u8,48u8,163u8,193u8,65u8,12u8,20u8,69u8,38u8,75u8,24u8,144u8,71u8,208u8,215u8,26u8,42u8, - 224u8,33u8,192u8,28u8,48u8,106u8,22u8,38u8,89u8,93u8,18u8,137u8,114u8,38u8,57u8,188u8,164u8,57u8,12u8,10u8, - 48u8,215u8,154u8,71u8,17u8,65u8,69u8,47u8,94u8,157u8,39u8,57u8,63u8,98u8,255u8,225u8,176u8,192u8,128u8,197u8, - 73u8,14u8,12u8,0u8,128u8,183u8,208u8,67u8,230u8,136u8,31u8,12u8,65u8,13u8,63u8,36u8,54u8,214u8,106u8,214u8, - 57u8,247u8,61u8,132u8,148u8,232u8,88u8,120u8,208u8,32u8,74u8,146u8,84u8,58u8,44u8,227u8,223u8,11u8,145u8,33u8, - 199u8,248u8,0u8,68u8,33u8,37u8,137u8,115u8,192u8,55u8,97u8,105u8,6u8,67u8,85u8,140u8,195u8,4u8,208u8,62u8, - 47u8,50u8,144u8,55u8,198u8,166u8,92u8,230u8,136u8,59u8,10u8,20u8,255u8,37u8,103u8,146u8,243u8,37u8,91u8,39u8, - 89u8,190u8,96u8,34u8,167u8,133u8,66u8,15u8,152u8,84u8,18u8,67u8,54u8,253u8,90u8,65u8,100u8,30u8,28u8,29u8, - 41u8,24u8,217u8,143u8,103u8,12u8,126u8,112u8,26u8,228u8,74u8,160u8,230u8,193u8,191u8,32u8,4u8,206u8,170u8,219u8, - 160u8,58u8,20u8,180u8,214u8,121u8,82u8,196u8,129u8,164u8,46u8,160u8,198u8,176u8,254u8,183u8,239u8,207u8,223u8,188u8, - 253u8,175u8,123u8,241u8,229u8,179u8,123u8,241u8,206u8,125u8,125u8,241u8,229u8,252u8,205u8,167u8,35u8,86u8,252u8,227u8, - 111u8,236u8,148u8,237u8,223u8,28u8,238u8,195u8,207u8,241u8,179u8,95u8,158u8,226u8,207u8,179u8,127u8,191u8,127u8,227u8, - 254u8,113u8,118u8,254u8,207u8,183u8,245u8,193u8,15u8,244u8,224u8,191u8,93u8,206u8,111u8,115u8,238u8,131u8,170u8,186u8, - 2u8,250u8,100u8,5u8,173u8,248u8,170u8,156u8,248u8,247u8,140u8,123u8,57u8,232u8,74u8,204u8,248u8,50u8,205u8,111u8, - 245u8,172u8,123u8,244u8,94u8,239u8,146u8,180u8,152u8,71u8,194u8,71u8,180u8,84u8,155u8,147u8,183u8,74u8,187u8,95u8, - 77u8,166u8,71u8,186u8,125u8,249u8,104u8,228u8,172u8,127u8,40u8,210u8,112u8,153u8,17u8,143u8,175u8,129u8,31u8,165u8, - 46u8,27u8,166u8,87u8,109u8,171u8,249u8,87u8,71u8,108u8,167u8,33u8,192u8,148u8,96u8,24u8,41u8,198u8,153u8,143u8, - 186u8,70u8,171u8,23u8,203u8,101u8,145u8,163u8,197u8,0u8,133u8,10u8,65u8,65u8,99u8,159u8,51u8,77u8,196u8,76u8, - 204u8,64u8,64u8,174u8,38u8,168u8,11u8,202u8,102u8,171u8,217u8,94u8,53u8,220u8,28u8,84u8,13u8,118u8,79u8,136u8, - 93u8,90u8,196u8,245u8,45u8,10u8,58u8,129u8,177u8,26u8,92u8,148u8,195u8,4u8,45u8,11u8,22u8,183u8,163u8,159u8, - 141u8,93u8,97u8,16u8,148u8,146u8,207u8,248u8,204u8,44u8,137u8,199u8,65u8,223u8,66u8,218u8,2u8,166u8,133u8,92u8, - 184u8,115u8,207u8,255u8,86u8,151u8,17u8,240u8,98u8,45u8,57u8,193u8,30u8,232u8,95u8,166u8,219u8,169u8,130u8,199u8, - 70u8,226u8,47u8,226u8,71u8,193u8,223u8,133u8,233u8,71u8,172u8,175u8,226u8,1u8,95u8,111u8,199u8,197u8,71u8,48u8, - 103u8,184u8,207u8,244u8,66u8,194u8,44u8,89u8,218u8,100u8,108u8,88u8,209u8,138u8,86u8,68u8,27u8,176u8,151u8,168u8, - 36u8,29u8,199u8,211u8,244u8,104u8,75u8,209u8,223u8,128u8,97u8,205u8,146u8,219u8,49u8,200u8,43u8,57u8,209u8,27u8, - 12u8,202u8,26u8,168u8,17u8,221u8,134u8,69u8,89u8,181u8,44u8,202u8,88u8,61u8,250u8,180u8,246u8,82u8,169u8,240u8, - 84u8,61u8,193u8,99u8,229u8,150u8,254u8,128u8,207u8,96u8,179u8,175u8,248u8,9u8,236u8,172u8,240u8,185u8,28u8,175u8, - 73u8,248u8,246u8,235u8,120u8,133u8,146u8,32u8,201u8,104u8,85u8,114u8,216u8,87u8,165u8,82u8,150u8,51u8,48u8,91u8, - 34u8,54u8,178u8,193u8,148u8,24u8,33u8,81u8,232u8,161u8,131u8,36u8,116u8,144u8,214u8,198u8,86u8,178u8,216u8,66u8, - 152u8,168u8,160u8,146u8,196u8,218u8,155u8,45u8,148u8,181u8,183u8,195u8,31u8,232u8,197u8,86u8,224u8,71u8,136u8,153u8, - 9u8,200u8,101u8,94u8,148u8,118u8,96u8,162u8,22u8,4u8,187u8,221u8,122u8,187u8,162u8,79u8,119u8,244u8,47u8,134u8, - 19u8,149u8,4u8,214u8,216u8,30u8,225u8,234u8,2u8,174u8,161u8,23u8,73u8,94u8,245u8,230u8,177u8,44u8,50u8,224u8, - 4u8,254u8,22u8,81u8,206u8,78u8,79u8,81u8,192u8,137u8,25u8,255u8,206u8,198u8,6u8,130u8,12u8,201u8,21u8,211u8, - 73u8,22u8,240u8,204u8,24u8,178u8,146u8,246u8,22u8,175u8,248u8,36u8,141u8,60u8,159u8,183u8,80u8,202u8,212u8,104u8, - 155u8,55u8,76u8,3u8,31u8,112u8,68u8,128u8,144u8,114u8,71u8,147u8,149u8,133u8,130u8,30u8,208u8,149u8,48u8,3u8, - 159u8,0u8,68u8,251u8,14u8,54u8,171u8,175u8,131u8,224u8,209u8,45u8,173u8,129u8,211u8,204u8,187u8,94u8,122u8,24u8, - 0u8,64u8,248u8,35u8,65u8,194u8,83u8,6u8,106u8,206u8,239u8,3u8,193u8,101u8,196u8,195u8,28u8,98u8,41u8,113u8, - 189u8,200u8,167u8,247u8,0u8,68u8,201u8,63u8,66u8,137u8,113u8,30u8,173u8,199u8,52u8,153u8,210u8,101u8,155u8,113u8, - 41u8,121u8,150u8,63u8,159u8,96u8,67u8,118u8,114u8,170u8,90u8,57u8,141u8,208u8,197u8,2u8,16u8,84u8,67u8,181u8, - 61u8,61u8,53u8,226u8,171u8,8u8,207u8,194u8,24u8,31u8,51u8,253u8,154u8,237u8,178u8,131u8,234u8,213u8,122u8,33u8, - 192u8,131u8,232u8,201u8,76u8,255u8,74u8,24u8,2u8,31u8,118u8,39u8,18u8,99u8,35u8,116u8,92u8,107u8,161u8,166u8, - 87u8,255u8,253u8,213u8,30u8,125u8,195u8,228u8,119u8,77u8,162u8,62u8,194u8,134u8,193u8,192u8,57u8,138u8,90u8,4u8, - 233u8,223u8,103u8,20u8,102u8,207u8,202u8,88u8,207u8,196u8,126u8,179u8,104u8,33u8,103u8,181u8,96u8,200u8,226u8,200u8, - 75u8,83u8,112u8,25u8,21u8,57u8,208u8,182u8,143u8,30u8,26u8,254u8,104u8,72u8,155u8,53u8,225u8,106u8,75u8,83u8, - 243u8,14u8,109u8,86u8,51u8,226u8,68u8,78u8,173u8,141u8,181u8,219u8,85u8,147u8,241u8,218u8,92u8,118u8,20u8,82u8, - 249u8,128u8,123u8,108u8,132u8,71u8,194u8,183u8,190u8,234u8,7u8,194u8,185u8,101u8,53u8,118u8,154u8,96u8,151u8,186u8, - 27u8,179u8,87u8,108u8,191u8,169u8,182u8,149u8,241u8,37u8,22u8,140u8,171u8,183u8,121u8,107u8,169u8,177u8,158u8,173u8, - 161u8,165u8,213u8,199u8,154u8,15u8,158u8,212u8,137u8,173u8,50u8,147u8,76u8,44u8,33u8,58u8,211u8,8u8,2u8,126u8, - 30u8,147u8,75u8,128u8,28u8,172u8,14u8,250u8,36u8,71u8,111u8,79u8,244u8,71u8,196u8,192u8,74u8,248u8,144u8,247u8, - 214u8,172u8,49u8,217u8,168u8,38u8,202u8,192u8,237u8,114u8,132u8,129u8,137u8,249u8,218u8,133u8,53u8,152u8,176u8,107u8, - 216u8,109u8,161u8,7u8,57u8,165u8,145u8,93u8,163u8,215u8,171u8,114u8,136u8,182u8,94u8,43u8,240u8,160u8,83u8,237u8, - 149u8,124u8,4u8,4u8,140u8,177u8,239u8,69u8,194u8,29u8,235u8,137u8,182u8,68u8,164u8,215u8,81u8,25u8,187u8,172u8, - 71u8,67u8,211u8,12u8,255u8,57u8,157u8,105u8,171u8,213u8,77u8,163u8,76u8,62u8,186u8,29u8,33u8,104u8,21u8,46u8, - 199u8,36u8,215u8,215u8,171u8,200u8,6u8,125u8,24u8,204u8,82u8,232u8,213u8,246u8,122u8,172u8,198u8,48u8,180u8,53u8, - 195u8,168u8,25u8,218u8,142u8,25u8,58u8,150u8,134u8,19u8,196u8,58u8,75u8,156u8,88u8,225u8,34u8,70u8,136u8,20u8, - 151u8,204u8,212u8,142u8,90u8,11u8,217u8,246u8,144u8,198u8,94u8,109u8,202u8,66u8,231u8,73u8,18u8,213u8,200u8,208u8, - 20u8,160u8,99u8,219u8,111u8,187u8,116u8,149u8,15u8,131u8,164u8,20u8,116u8,114u8,138u8,54u8,123u8,2u8,84u8,75u8, - 24u8,29u8,12u8,202u8,13u8,217u8,35u8,175u8,242u8,198u8,46u8,201u8,32u8,169u8,194u8,186u8,193u8,241u8,56u8,205u8, - 209u8,36u8,139u8,110u8,122u8,209u8,127u8,171u8,140u8,10u8,183u8,158u8,160u8,229u8,114u8,227u8,195u8,45u8,51u8,94u8, - 182u8,135u8,73u8,68u8,221u8,205u8,90u8,204u8,18u8,27u8,77u8,143u8,97u8,22u8,125u8,175u8,208u8,73u8,169u8,196u8, - 4u8,223u8,162u8,120u8,179u8,33u8,184u8,49u8,129u8,80u8,69u8,25u8,200u8,2u8,170u8,228u8,224u8,194u8,232u8,134u8, - 217u8,249u8,80u8,132u8,155u8,144u8,164u8,16u8,238u8,77u8,219u8,36u8,209u8,16u8,110u8,18u8,110u8,69u8,210u8,4u8, - 89u8,114u8,154u8,209u8,212u8,83u8,114u8,85u8,98u8,180u8,13u8,97u8,21u8,14u8,45u8,63u8,175u8,81u8,184u8,7u8, - 107u8,239u8,99u8,180u8,85u8,96u8,118u8,193u8,176u8,148u8,73u8,15u8,112u8,147u8,38u8,82u8,80u8,233u8,116u8,31u8, - 205u8,151u8,208u8,54u8,12u8,48u8,0u8,220u8,48u8,251u8,96u8,23u8,19u8,93u8,93u8,218u8,197u8,213u8,229u8,98u8, - 201u8,187u8,82u8,188u8,142u8,164u8,174u8,198u8,30u8,78u8,190u8,69u8,62u8,103u8,165u8,89u8,219u8,26u8,99u8,49u8, - 214u8,12u8,87u8,70u8,180u8,145u8,133u8,13u8,19u8,110u8,98u8,95u8,97u8,39u8,37u8,155u8,137u8,173u8,109u8,160u8, - 37u8,22u8,115u8,71u8,148u8,197u8,28u8,38u8,23u8,34u8,204u8,145u8,6u8,12u8,208u8,100u8,49u8,151u8,80u8,217u8, - 197u8,166u8,198u8,224u8,238u8,89u8,213u8,79u8,220u8,126u8,18u8,248u8,2u8,145u8,209u8,12u8,167u8,96u8,210u8,121u8, - 6u8,229u8,92u8,229u8,49u8,113u8,16u8,152u8,160u8,59u8,195u8,219u8,174u8,254u8,83u8,139u8,243u8,112u8,45u8,219u8, - 20u8,125u8,244u8,163u8,177u8,180u8,130u8,72u8,162u8,46u8,195u8,177u8,202u8,128u8,107u8,217u8,142u8,96u8,175u8,78u8, - 21u8,89u8,244u8,174u8,147u8,122u8,93u8,28u8,24u8,112u8,135u8,77u8,226u8,45u8,170u8,127u8,88u8,212u8,194u8,127u8, - 119u8,182u8,30u8,85u8,94u8,183u8,185u8,95u8,21u8,58u8,247u8,216u8,173u8,88u8,136u8,25u8,163u8,32u8,108u8,45u8, - 224u8,53u8,149u8,128u8,61u8,153u8,87u8,251u8,26u8,232u8,135u8,135u8,84u8,205u8,234u8,100u8,185u8,82u8,150u8,131u8, - 169u8,3u8,7u8,37u8,80u8,237u8,79u8,184u8,170u8,56u8,25u8,181u8,121u8,76u8,173u8,65u8,96u8,221u8,135u8,80u8, - 29u8,179u8,233u8,159u8,155u8,136u8,2u8,72u8,24u8,21u8,127u8,33u8,92u8,174u8,8u8,110u8,108u8,165u8,171u8,43u8, - 131u8,189u8,207u8,117u8,227u8,113u8,164u8,91u8,139u8,187u8,7u8,243u8,103u8,105u8,26u8,169u8,26u8,161u8,57u8,89u8, - 193u8,104u8,153u8,123u8,126u8,79u8,245u8,214u8,161u8,115u8,140u8,98u8,73u8,7u8,122u8,121u8,13u8,105u8,17u8,71u8, - 112u8,66u8,69u8,128u8,195u8,177u8,141u8,139u8,99u8,12u8,21u8,9u8,29u8,22u8,30u8,177u8,159u8,250u8,183u8,159u8, - 189u8,249u8,45u8,108u8,78u8,220u8,148u8,112u8,98u8,21u8,115u8,136u8,208u8,65u8,56u8,19u8,152u8,91u8,170u8,9u8, - 79u8,149u8,88u8,28u8,15u8,141u8,120u8,8u8,199u8,98u8,2u8,70u8,141u8,110u8,171u8,168u8,68u8,75u8,100u8,39u8, - 25u8,63u8,249u8,79u8,22u8,66u8,193u8,106u8,250u8,196u8,168u8,116u8,38u8,14u8,163u8,209u8,105u8,103u8,165u8,171u8, - 209u8,25u8,105u8,56u8,169u8,39u8,161u8,171u8,95u8,77u8,64u8,75u8,117u8,220u8,0u8,157u8,103u8,29u8,17u8,224u8, - 254u8,238u8,69u8,114u8,4u8,118u8,155u8,130u8,50u8,4u8,110u8,167u8,15u8,185u8,135u8,139u8,196u8,194u8,90u8,28u8, - 214u8,231u8,152u8,251u8,43u8,77u8,125u8,64u8,117u8,158u8,169u8,252u8,2u8,96u8,227u8,14u8,73u8,8u8,52u8,235u8, - 124u8,228u8,9u8,128u8,67u8,201u8,238u8,11u8,222u8,59u8,188u8,99u8,80u8,195u8,14u8,204u8,95u8,86u8,171u8,39u8, - 237u8,65u8,163u8,140u8,241u8,27u8,15u8,207u8,141u8,29u8,200u8,18u8,161u8,203u8,68u8,173u8,251u8,242u8,192u8,57u8, - 116u8,94u8,94u8,81u8,165u8,55u8,156u8,162u8,87u8,131u8,104u8,135u8,223u8,112u8,191u8,200u8,121u8,57u8,197u8,44u8, - 156u8,224u8,31u8,104u8,130u8,62u8,235u8,16u8,254u8,190u8,156u8,206u8,122u8,193u8,142u8,130u8,147u8,51u8,223u8,47u8, - 150u8,69u8,228u8,145u8,61u8,40u8,17u8,175u8,138u8,237u8,237u8,61u8,94u8,197u8,20u8,177u8,128u8,50u8,172u8,61u8, - 64u8,101u8,184u8,128u8,24u8,251u8,133u8,225u8,199u8,122u8,70u8,109u8,167u8,181u8,254u8,13u8,242u8,60u8,120u8,131u8, - 160u8,194u8,52u8,199u8,45u8,147u8,168u8,76u8,33u8,12u8,251u8,211u8,180u8,11u8,39u8,248u8,193u8,33u8,40u8,109u8, - 114u8,240u8,105u8,39u8,19u8,170u8,178u8,26u8,137u8,111u8,156u8,160u8,192u8,168u8,8u8,188u8,17u8,250u8,121u8,56u8, - 201u8,255u8,134u8,86u8,81u8,181u8,0u8,77u8,198u8,18u8,237u8,3u8,80u8,115u8,0u8,175u8,39u8,135u8,248u8,207u8, - 75u8,204u8,92u8,166u8,131u8,212u8,100u8,143u8,200u8,141u8,238u8,224u8,216u8,45u8,30u8,144u8,156u8,186u8,191u8,170u8, - 147u8,132u8,191u8,58u8,244u8,235u8,38u8,146u8,62u8,232u8,240u8,174u8,189u8,91u8,74u8,43u8,35u8,251u8,234u8,177u8, - 198u8,195u8,165u8,89u8,18u8,20u8,62u8,229u8,6u8,148u8,212u8,105u8,215u8,139u8,17u8,33u8,70u8,95u8,203u8,36u8, - 16u8,225u8,173u8,46u8,168u8,149u8,19u8,39u8,192u8,187u8,136u8,189u8,136u8,45u8,189u8,180u8,207u8,82u8,45u8,41u8, - 126u8,41u8,173u8,186u8,195u8,206u8,249u8,186u8,147u8,162u8,157u8,94u8,142u8,106u8,38u8,191u8,234u8,110u8,176u8,215u8, - 253u8,172u8,113u8,219u8,69u8,72u8,85u8,30u8,107u8,183u8,188u8,188u8,234u8,36u8,37u8,180u8,8u8,233u8,41u8,143u8, - 41u8,130u8,166u8,211u8,105u8,95u8,213u8,107u8,51u8,59u8,219u8,113u8,49u8,0u8,240u8,102u8,112u8,7u8,177u8,125u8, - 58u8,104u8,31u8,16u8,214u8,119u8,34u8,202u8,53u8,142u8,90u8,87u8,85u8,133u8,1u8,31u8,96u8,165u8,134u8,123u8, - 213u8,85u8,45u8,71u8,229u8,78u8,38u8,243u8,45u8,113u8,199u8,251u8,71u8,224u8,205u8,192u8,249u8,206u8,82u8,136u8, - 20u8,203u8,115u8,114u8,140u8,165u8,123u8,61u8,47u8,205u8,107u8,144u8,60u8,10u8,178u8,36u8,29u8,9u8,117u8,106u8, - 171u8,49u8,138u8,216u8,64u8,121u8,36u8,196u8,35u8,241u8,109u8,23u8,150u8,210u8,201u8,142u8,194u8,182u8,15u8,123u8, - 122u8,107u8,133u8,133u8,27u8,57u8,248u8,232u8,101u8,185u8,80u8,0u8,75u8,74u8,217u8,122u8,208u8,133u8,44u8,48u8, - 64u8,108u8,169u8,154u8,170u8,15u8,124u8,194u8,12u8,138u8,134u8,85u8,206u8,247u8,177u8,172u8,47u8,40u8,187u8,21u8, - 169u8,67u8,252u8,238u8,195u8,212u8,238u8,145u8,157u8,114u8,176u8,215u8,95u8,62u8,179u8,243u8,139u8,207u8,230u8,126u8, - 217u8,80u8,71u8,170u8,142u8,245u8,113u8,157u8,154u8,245u8,157u8,116u8,91u8,172u8,174u8,88u8,203u8,202u8,209u8,120u8, - 208u8,199u8,56u8,222u8,11u8,123u8,204u8,26u8,226u8,115u8,156u8,188u8,30u8,193u8,78u8,217u8,60u8,227u8,222u8,183u8, - 109u8,74u8,135u8,40u8,68u8,138u8,45u8,172u8,195u8,224u8,118u8,135u8,205u8,210u8,116u8,9u8,83u8,111u8,101u8,167u8, - 200u8,105u8,187u8,194u8,73u8,112u8,194u8,204u8,105u8,251u8,244u8,247u8,110u8,155u8,5u8,165u8,77u8,5u8,206u8,146u8, - 220u8,203u8,117u8,42u8,10u8,177u8,9u8,131u8,144u8,3u8,2u8,14u8,40u8,25u8,254u8,253u8,10u8,131u8,66u8,182u8, - 251u8,138u8,93u8,234u8,7u8,16u8,40u8,194u8,163u8,171u8,242u8,160u8,190u8,42u8,53u8,231u8,116u8,113u8,52u8,18u8, - 88u8,254u8,20u8,218u8,118u8,226u8,224u8,120u8,169u8,244u8,165u8,9u8,231u8,117u8,56u8,164u8,98u8,167u8,86u8,221u8, - 139u8,196u8,184u8,143u8,146u8,65u8,79u8,210u8,166u8,126u8,197u8,234u8,191u8,24u8,65u8,147u8,214u8,238u8,69u8,192u8, - 35u8,85u8,135u8,108u8,85u8,144u8,60u8,200u8,189u8,225u8,112u8,166u8,138u8,252u8,18u8,188u8,32u8,6u8,37u8,196u8, - 93u8,234u8,143u8,59u8,20u8,236u8,173u8,151u8,101u8,222u8,109u8,227u8,170u8,3u8,213u8,148u8,202u8,187u8,6u8,73u8, - 117u8,229u8,160u8,34u8,161u8,194u8,177u8,27u8,152u8,230u8,221u8,135u8,241u8,240u8,84u8,151u8,33u8,90u8,136u8,57u8, - 245u8,219u8,11u8,3u8,40u8,182u8,110u8,144u8,232u8,245u8,37u8,249u8,208u8,53u8,19u8,194u8,178u8,121u8,153u8,161u8, - 111u8,168u8,70u8,51u8,125u8,209u8,97u8,98u8,174u8,52u8,224u8,84u8,189u8,182u8,151u8,212u8,75u8,33u8,79u8,215u8, - 175u8,21u8,53u8,184u8,231u8,240u8,246u8,48u8,199u8,253u8,68u8,183u8,164u8,51u8,136u8,227u8,208u8,176u8,193u8,71u8, - 169u8,114u8,80u8,85u8,216u8,43u8,100u8,57u8,102u8,186u8,141u8,17u8,214u8,138u8,157u8,175u8,147u8,202u8,78u8,202u8, - 62u8,75u8,170u8,102u8,116u8,127u8,205u8,160u8,142u8,183u8,166u8,189u8,74u8,143u8,47u8,59u8,15u8,74u8,241u8,69u8, - 56u8,112u8,130u8,218u8,83u8,114u8,193u8,110u8,28u8,45u8,83u8,85u8,193u8,59u8,238u8,116u8,188u8,157u8,38u8,175u8, - 225u8,135u8,243u8,250u8,201u8,1u8,17u8,14u8,120u8,215u8,174u8,36u8,245u8,116u8,13u8,59u8,186u8,110u8,91u8,251u8, - 33u8,115u8,159u8,72u8,171u8,244u8,148u8,247u8,223u8,60u8,1u8,221u8,29u8,126u8,27u8,214u8,234u8,155u8,114u8,195u8, - 249u8,42u8,222u8,57u8,239u8,190u8,79u8,42u8,65u8,7u8,101u8,40u8,180u8,78u8,150u8,106u8,221u8,167u8,104u8,48u8, - 208u8,201u8,150u8,233u8,69u8,175u8,94u8,117u8,28u8,203u8,150u8,193u8,88u8,227u8,182u8,92u8,135u8,11u8,175u8,251u8, - 69u8,165u8,136u8,77u8,37u8,40u8,135u8,75u8,7u8,203u8,77u8,160u8,67u8,170u8,101u8,151u8,18u8,145u8,91u8,31u8, - 229u8,19u8,55u8,159u8,212u8,247u8,177u8,99u8,135u8,117u8,93u8,244u8,220u8,142u8,36u8,39u8,138u8,158u8,132u8,156u8, - 250u8,161u8,246u8,163u8,115u8,243u8,252u8,105u8,201u8,49u8,87u8,127u8,189u8,50u8,97u8,252u8,90u8,72u8,60u8,140u8, - 93u8,103u8,184u8,253u8,50u8,112u8,6u8,120u8,80u8,210u8,42u8,50u8,40u8,87u8,236u8,97u8,141u8,215u8,207u8,68u8, - 74u8,166u8,61u8,6u8,199u8,94u8,14u8,188u8,198u8,67u8,158u8,2u8,93u8,136u8,230u8,24u8,207u8,245u8,249u8,13u8, - 157u8,189u8,232u8,194u8,176u8,74u8,74u8,135u8,19u8,82u8,221u8,244u8,100u8,187u8,68u8,52u8,176u8,18u8,81u8,197u8, - 177u8,5u8,103u8,95u8,125u8,63u8,192u8,250u8,126u8,29u8,31u8,56u8,50u8,255u8,197u8,31u8,51u8,208u8,7u8,245u8, - 13u8,148u8,79u8,246u8,55u8,113u8,172u8,235u8,162u8,250u8,11u8,42u8,63u8,238u8,24u8,29u8,177u8,137u8,28u8,178u8, - 10u8,200u8,109u8,244u8,227u8,32u8,129u8,130u8,16u8,172u8,69u8,125u8,147u8,72u8,3u8,89u8,113u8,248u8,27u8,251u8, - 151u8,250u8,10u8,208u8,59u8,243u8,141u8,151u8,142u8,145u8,173u8,99u8,203u8,23u8,236u8,247u8,5u8,247u8,191u8,169u8, - 251u8,220u8,7u8,234u8,226u8,249u8,247u8,2u8,42u8,47u8,58u8,155u8,210u8,202u8,138u8,97u8,88u8,16u8,32u8,73u8, - 120u8,131u8,66u8,223u8,182u8,214u8,183u8,217u8,103u8,171u8,195u8,89u8,5u8,40u8,126u8,117u8,228u8,187u8,219u8,245u8, - 69u8,130u8,131u8,142u8,211u8,138u8,213u8,225u8,209u8,224u8,55u8,11u8,90u8,219u8,81u8,123u8,28u8,24u8,141u8,238u8, - 48u8,208u8,71u8,136u8,167u8,65u8,207u8,217u8,206u8,78u8,173u8,213u8,234u8,224u8,82u8,55u8,220u8,61u8,184u8,162u8, - 251u8,14u8,29u8,45u8,246u8,247u8,246u8,234u8,141u8,86u8,135u8,229u8,179u8,195u8,233u8,149u8,85u8,73u8,238u8,195u8, - 108u8,8u8,50u8,96u8,7u8,173u8,22u8,222u8,27u8,71u8,228u8,16u8,95u8,186u8,121u8,212u8,1u8,88u8,243u8,6u8, - 98u8,215u8,209u8,206u8,88u8,4u8,7u8,80u8,171u8,64u8,59u8,64u8,208u8,12u8,124u8,77u8,96u8,44u8,92u8,20u8, - 42u8,109u8,116u8,13u8,184u8,186u8,161u8,110u8,119u8,56u8,140u8,216u8,6u8,21u8,43u8,139u8,41u8,148u8,199u8,139u8, - 204u8,58u8,43u8,238u8,211u8,51u8,8u8,143u8,40u8,225u8,223u8,94u8,207u8,134u8,117u8,11u8,21u8,202u8,210u8,175u8, - 150u8,230u8,104u8,93u8,57u8,24u8,171u8,43u8,244u8,141u8,193u8,109u8,150u8,111u8,93u8,126u8,41u8,47u8,38u8,169u8, - 47u8,146u8,32u8,14u8,123u8,77u8,32u8,212u8,73u8,174u8,139u8,189u8,92u8,15u8,142u8,129u8,177u8,75u8,133u8,137u8, - 185u8,176u8,242u8,52u8,216u8,160u8,246u8,8u8,107u8,55u8,193u8,231u8,118u8,27u8,209u8,208u8,176u8,195u8,75u8,242u8, - 86u8,91u8,238u8,189u8,242u8,130u8,88u8,249u8,197u8,8u8,131u8,9u8,26u8,60u8,183u8,243u8,210u8,220u8,214u8,246u8, - 134u8,223u8,8u8,9u8,241u8,9u8,126u8,97u8,15u8,61u8,205u8,13u8,25u8,147u8,246u8,33u8,211u8,221u8,179u8,255u8, - 3u8,143u8,194u8,59u8,98u8,235u8,58u8,0u8,0u8,0u8,0u8,5u8,101u8,114u8,114u8,111u8,114u8,160u8,12u8,31u8, - 139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,141u8,86u8,219u8,114u8,219u8,54u8,16u8,125u8,247u8,87u8,108u8, - 167u8,15u8,141u8,83u8,87u8,146u8,99u8,59u8,109u8,236u8,228u8,129u8,145u8,21u8,91u8,51u8,138u8,148u8,209u8,37u8, - 211u8,78u8,38u8,67u8,65u8,36u8,40u8,97u8,66u8,1u8,12u8,0u8,250u8,50u8,153u8,252u8,123u8,23u8,11u8,94u8, - 32u8,71u8,81u8,251u8,224u8,177u8,72u8,96u8,207u8,222u8,206u8,158u8,101u8,183u8,219u8,133u8,249u8,70u8,24u8,216u8, - 170u8,180u8,204u8,57u8,164u8,60u8,19u8,146u8,27u8,96u8,96u8,184u8,5u8,149u8,65u8,194u8,164u8,146u8,34u8,97u8, - 57u8,112u8,173u8,149u8,134u8,68u8,165u8,120u8,124u8,191u8,17u8,201u8,6u8,152u8,230u8,160u8,10u8,43u8,148u8,196u8, - 99u8,171u8,160u8,52u8,28u8,86u8,143u8,192u8,138u8,34u8,71u8,11u8,247u8,222u8,64u8,134u8,54u8,118u8,195u8,143u8, - 186u8,232u8,104u8,201u8,86u8,74u8,219u8,37u8,48u8,153u8,226u8,111u8,99u8,184u8,182u8,191u8,44u8,33u8,227u8,204u8, - 150u8,154u8,155u8,142u8,187u8,67u8,247u8,250u8,123u8,125u8,58u8,116u8,68u8,130u8,51u8,200u8,213u8,61u8,55u8,22u8, - 93u8,89u8,124u8,141u8,49u8,186u8,183u8,229u8,203u8,115u8,32u8,120u8,186u8,13u8,154u8,201u8,53u8,135u8,103u8,116u8, - 82u8,20u8,92u8,195u8,69u8,117u8,221u8,197u8,156u8,105u8,206u8,41u8,48u8,133u8,231u8,218u8,1u8,31u8,147u8,115u8, - 152u8,56u8,44u8,101u8,248u8,9u8,65u8,110u8,196u8,122u8,83u8,251u8,1u8,205u8,11u8,140u8,145u8,75u8,107u8,232u8, - 236u8,121u8,21u8,25u8,179u8,124u8,173u8,244u8,227u8,115u8,202u8,201u8,29u8,184u8,216u8,48u8,227u8,123u8,85u8,249u8, - 11u8,46u8,107u8,206u8,140u8,146u8,207u8,189u8,171u8,27u8,113u8,199u8,37u8,90u8,193u8,46u8,16u8,44u8,123u8,15u8, - 167u8,190u8,66u8,172u8,50u8,112u8,175u8,206u8,150u8,39u8,248u8,220u8,246u8,34u8,200u8,52u8,87u8,234u8,11u8,166u8, - 101u8,200u8,178u8,215u8,235u8,157u8,45u8,219u8,66u8,70u8,117u8,91u8,209u8,146u8,234u8,23u8,98u8,144u8,245u8,189u8, - 176u8,27u8,247u8,22u8,91u8,101u8,153u8,180u8,216u8,255u8,36u8,103u8,154u8,122u8,87u8,87u8,54u8,83u8,57u8,38u8, - 37u8,228u8,218u8,149u8,108u8,123u8,217u8,128u8,47u8,151u8,75u8,250u8,239u8,254u8,32u8,146u8,32u8,228u8,29u8,203u8, - 69u8,10u8,209u8,172u8,63u8,28u8,66u8,178u8,65u8,152u8,196u8,98u8,45u8,238u8,49u8,54u8,46u8,19u8,85u8,74u8, - 124u8,226u8,41u8,114u8,7u8,19u8,79u8,48u8,55u8,235u8,48u8,145u8,104u8,86u8,227u8,15u8,95u8,21u8,138u8,3u8, - 6u8,195u8,241u8,199u8,104u8,52u8,188u8,142u8,251u8,183u8,209u8,52u8,234u8,207u8,7u8,211u8,75u8,234u8,239u8,27u8, - 232u8,61u8,244u8,40u8,199u8,171u8,48u8,2u8,250u8,77u8,36u8,166u8,148u8,240u8,255u8,10u8,27u8,11u8,62u8,26u8, - 33u8,41u8,137u8,123u8,165u8,243u8,212u8,84u8,233u8,98u8,113u8,221u8,15u8,85u8,218u8,167u8,244u8,54u8,29u8,24u8, - 210u8,75u8,88u8,113u8,87u8,176u8,212u8,21u8,150u8,65u8,145u8,51u8,196u8,169u8,106u8,153u8,43u8,188u8,78u8,62u8, - 195u8,182u8,66u8,41u8,83u8,174u8,93u8,17u8,83u8,55u8,3u8,206u8,39u8,127u8,16u8,134u8,114u8,244u8,247u8,182u8, - 172u8,192u8,33u8,81u8,57u8,190u8,57u8,1u8,124u8,38u8,228u8,221u8,118u8,116u8,130u8,116u8,176u8,89u8,137u8,45u8, - 221u8,65u8,123u8,197u8,147u8,68u8,32u8,173u8,54u8,236u8,14u8,103u8,141u8,59u8,10u8,165u8,56u8,130u8,24u8,104u8, - 166u8,213u8,22u8,110u8,148u8,90u8,231u8,252u8,55u8,179u8,127u8,108u8,79u8,170u8,185u8,117u8,53u8,41u8,181u8,164u8, - 89u8,192u8,160u8,145u8,137u8,41u8,57u8,37u8,136u8,133u8,20u8,15u8,59u8,115u8,247u8,201u8,224u8,180u8,224u8,156u8, - 240u8,207u8,207u8,54u8,214u8,22u8,230u8,178u8,219u8,77u8,114u8,85u8,166u8,157u8,53u8,57u8,235u8,36u8,106u8,219u8, - 101u8,133u8,48u8,93u8,188u8,44u8,214u8,178u8,235u8,11u8,249u8,235u8,6u8,43u8,225u8,114u8,141u8,253u8,243u8,113u8, - 7u8,6u8,12u8,125u8,83u8,139u8,54u8,46u8,119u8,73u8,62u8,81u8,6u8,84u8,34u8,152u8,203u8,224u8,118u8,62u8, - 255u8,16u8,120u8,174u8,130u8,13u8,155u8,129u8,129u8,79u8,7u8,179u8,57u8,56u8,119u8,29u8,170u8,17u8,22u8,181u8, - 32u8,106u8,186u8,208u8,3u8,91u8,84u8,35u8,23u8,108u8,195u8,8u8,169u8,44u8,156u8,94u8,158u8,94u8,5u8,125u8, - 243u8,217u8,185u8,196u8,168u8,16u8,12u8,86u8,194u8,130u8,70u8,151u8,110u8,126u8,49u8,122u8,31u8,15u8,221u8,234u8, - 28u8,85u8,189u8,55u8,54u8,189u8,188u8,244u8,230u8,223u8,142u8,142u8,0u8,188u8,90u8,229u8,57u8,154u8,152u8,130u8, - 39u8,34u8,19u8,142u8,50u8,237u8,44u8,48u8,189u8,46u8,183u8,40u8,26u8,64u8,165u8,187u8,132u8,243u8,94u8,239u8, - 24u8,205u8,60u8,205u8,107u8,150u8,71u8,211u8,155u8,197u8,251u8,193u8,120u8,222u8,146u8,28u8,227u8,172u8,192u8,105u8, - 174u8,10u8,36u8,42u8,17u8,205u8,148u8,57u8,137u8,178u8,27u8,216u8,45u8,190u8,245u8,115u8,138u8,233u8,57u8,42u8, - 227u8,251u8,74u8,245u8,246u8,248u8,154u8,44u8,230u8,241u8,228u8,93u8,60u8,141u8,198u8,55u8,131u8,214u8,207u8,139u8, - 198u8,143u8,171u8,165u8,121u8,52u8,150u8,111u8,235u8,106u8,97u8,173u8,221u8,108u8,98u8,103u8,220u8,196u8,106u8,175u8, - 189u8,10u8,165u8,212u8,59u8,173u8,218u8,130u8,207u8,78u8,21u8,48u8,235u8,67u8,25u8,206u8,230u8,209u8,60u8,112u8, - 123u8,214u8,184u8,157u8,242u8,175u8,165u8,147u8,88u8,231u8,144u8,149u8,232u8,65u8,90u8,183u8,57u8,16u8,46u8,45u8, - 169u8,137u8,91u8,97u8,12u8,13u8,76u8,85u8,81u8,154u8,28u8,254u8,80u8,8u8,167u8,36u8,206u8,2u8,47u8,125u8, - 193u8,41u8,104u8,188u8,159u8,182u8,222u8,23u8,227u8,104u8,49u8,191u8,197u8,194u8,14u8,251u8,232u8,254u8,186u8,245u8, - 127u8,222u8,248u8,79u8,114u8,225u8,186u8,147u8,42u8,238u8,147u8,166u8,177u8,50u8,101u8,150u8,137u8,132u8,14u8,48u8, - 61u8,138u8,64u8,5u8,30u8,206u8,90u8,15u8,31u8,6u8,211u8,247u8,195u8,217u8,108u8,56u8,25u8,199u8,215u8,131u8, - 241u8,48u8,244u8,113u8,209u8,182u8,48u8,160u8,6u8,182u8,80u8,149u8,58u8,105u8,40u8,153u8,161u8,42u8,6u8,165u8, - 59u8,111u8,161u8,199u8,147u8,121u8,252u8,110u8,178u8,24u8,7u8,144u8,47u8,27u8,200u8,190u8,146u8,73u8,169u8,53u8, - 170u8,234u8,163u8,187u8,158u8,225u8,186u8,181u8,39u8,24u8,183u8,219u8,201u8,198u8,9u8,82u8,250u8,7u8,18u8,86u8, - 100u8,143u8,127u8,220u8,107u8,129u8,13u8,172u8,239u8,180u8,158u8,94u8,181u8,158u8,162u8,183u8,147u8,233u8,78u8,121u8, - 254u8,220u8,97u8,69u8,19u8,50u8,206u8,132u8,117u8,204u8,243u8,37u8,67u8,209u8,198u8,124u8,176u8,67u8,164u8,228u8, - 56u8,62u8,185u8,115u8,251u8,232u8,69u8,207u8,236u8,119u8,52u8,154u8,14u8,162u8,235u8,127u8,226u8,193u8,223u8,195u8, - 217u8,124u8,214u8,250u8,251u8,171u8,241u8,55u8,241u8,52u8,94u8,99u8,18u8,205u8,118u8,118u8,244u8,162u8,45u8,255u8, - 181u8,84u8,150u8,53u8,192u8,47u8,2u8,96u8,212u8,132u8,201u8,98u8,218u8,31u8,32u8,242u8,109u8,180u8,152u8,237u8, - 36u8,243u8,234u8,7u8,174u8,33u8,115u8,19u8,142u8,35u8,219u8,232u8,116u8,149u8,81u8,141u8,252u8,42u8,64u8,238u8, - 71u8,227u8,254u8,96u8,52u8,10u8,1u8,163u8,6u8,112u8,232u8,22u8,154u8,108u8,36u8,182u8,178u8,191u8,216u8,29u8, - 0u8,92u8,94u8,227u8,104u8,212u8,154u8,191u8,109u8,204u8,223u8,249u8,239u8,30u8,63u8,108u8,219u8,34u8,231u8,78u8, - 40u8,218u8,65u8,186u8,8u8,169u8,236u8,216u8,48u8,124u8,255u8,97u8,52u8,112u8,58u8,17u8,70u8,211u8,223u8,157u8, - 96u8,174u8,239u8,132u8,103u8,151u8,231u8,135u8,205u8,31u8,113u8,49u8,177u8,59u8,38u8,114u8,182u8,66u8,173u8,198u8, - 152u8,83u8,26u8,48u8,83u8,183u8,83u8,115u8,171u8,29u8,137u8,202u8,60u8,5u8,163u8,242u8,59u8,63u8,231u8,200u8, - 249u8,146u8,183u8,145u8,156u8,237u8,12u8,213u8,199u8,104u8,56u8,138u8,222u8,142u8,130u8,129u8,190u8,14u8,153u8,137u8, - 251u8,188u8,76u8,236u8,206u8,110u8,11u8,132u8,153u8,132u8,154u8,181u8,223u8,59u8,225u8,151u8,78u8,7u8,65u8,138u8, - 114u8,133u8,76u8,133u8,172u8,148u8,173u8,253u8,179u8,250u8,54u8,121u8,60u8,169u8,110u8,211u8,195u8,177u8,15u8,226u8, - 27u8,90u8,2u8,52u8,247u8,224u8,245u8,107u8,56u8,125u8,121u8,12u8,191u8,87u8,87u8,241u8,244u8,59u8,254u8,185u8, - 57u8,12u8,162u8,242u8,70u8,133u8,102u8,235u8,45u8,67u8,101u8,99u8,72u8,15u8,204u8,6u8,163u8,231u8,87u8,116u8, - 146u8,227u8,23u8,176u8,217u8,228u8,49u8,78u8,0u8,190u8,223u8,197u8,246u8,55u8,184u8,52u8,238u8,203u8,21u8,62u8, - 97u8,109u8,112u8,14u8,44u8,255u8,92u8,139u8,244u8,155u8,55u8,141u8,105u8,29u8,132u8,55u8,161u8,47u8,55u8,19u8, - 139u8,12u8,62u8,177u8,21u8,150u8,10u8,151u8,252u8,103u8,200u8,88u8,110u8,248u8,19u8,200u8,246u8,180u8,133u8,172u8, - 99u8,184u8,162u8,132u8,106u8,30u8,149u8,50u8,241u8,223u8,218u8,110u8,26u8,255u8,171u8,254u8,213u8,71u8,221u8,154u8, - 62u8,63u8,107u8,188u8,39u8,133u8,175u8,180u8,54u8,174u8,183u8,215u8,51u8,189u8,83u8,107u8,8u8,58u8,243u8,116u8, - 135u8,97u8,115u8,142u8,169u8,214u8,1u8,28u8,46u8,167u8,88u8,101u8,49u8,45u8,167u8,3u8,80u8,225u8,138u8,218u8, - 7u8,83u8,71u8,69u8,59u8,233u8,127u8,132u8,68u8,75u8,103u8,31u8,16u8,14u8,68u8,184u8,106u8,158u8,66u8,181u8, - 72u8,79u8,22u8,200u8,62u8,172u8,118u8,65u8,196u8,41u8,151u8,226u8,16u8,218u8,15u8,203u8,98u8,31u8,30u8,138u8, - 65u8,76u8,75u8,225u8,231u8,56u8,205u8,102u8,216u8,103u8,79u8,244u8,58u8,20u8,69u8,165u8,246u8,123u8,109u8,189u8, - 132u8,199u8,94u8,194u8,15u8,64u8,236u8,232u8,248u8,62u8,164u8,122u8,103u8,32u8,212u8,134u8,149u8,102u8,79u8,64u8, - 1u8,220u8,143u8,234u8,189u8,191u8,251u8,94u8,108u8,15u8,54u8,222u8,139u8,237u8,207u8,234u8,26u8,136u8,236u8,1u8, - 148u8,39u8,82u8,251u8,19u8,2u8,213u8,138u8,122u8,144u8,60u8,181u8,80u8,86u8,24u8,223u8,143u8,254u8,5u8,20u8, - 191u8,245u8,229u8,110u8,15u8,0u8,0u8,0u8,0u8,3u8,97u8,99u8,108u8,222u8,4u8,31u8,139u8,8u8,0u8,0u8, - 0u8,0u8,0u8,2u8,255u8,149u8,83u8,77u8,143u8,211u8,48u8,16u8,189u8,231u8,87u8,204u8,238u8,97u8,73u8,164u8, - 168u8,5u8,9u8,113u8,72u8,0u8,169u8,90u8,113u8,64u8,66u8,93u8,9u8,245u8,222u8,117u8,108u8,167u8,49u8,36u8, - 118u8,228u8,143u8,66u8,181u8,234u8,127u8,103u8,108u8,39u8,221u8,108u8,218u8,46u8,224u8,155u8,61u8,51u8,111u8,222u8, - 60u8,191u8,89u8,46u8,151u8,176u8,162u8,148u8,27u8,3u8,84u8,73u8,171u8,85u8,11u8,173u8,48u8,22u8,82u8,66u8, - 219u8,12u8,58u8,197u8,92u8,203u8,23u8,176u8,146u8,128u8,119u8,16u8,6u8,72u8,12u8,171u8,26u8,31u8,168u8,114u8, - 210u8,2u8,97u8,76u8,99u8,57u8,55u8,240u8,171u8,81u8,201u8,18u8,241u8,26u8,178u8,231u8,96u8,27u8,238u8,83u8, - 60u8,112u8,207u8,117u8,39u8,140u8,17u8,74u8,130u8,85u8,136u8,64u8,185u8,182u8,68u8,72u8,80u8,213u8,15u8,78u8, - 237u8,34u8,212u8,108u8,26u8,4u8,143u8,237u8,192u8,121u8,48u8,2u8,143u8,123u8,140u8,42u8,253u8,232u8,139u8,52u8, - 239u8,177u8,9u8,199u8,118u8,30u8,215u8,83u8,200u8,161u8,114u8,22u8,40u8,145u8,80u8,113u8,12u8,215u8,196u8,231u8, - 114u8,134u8,201u8,1u8,15u8,49u8,16u8,226u8,214u8,112u8,123u8,11u8,66u8,26u8,203u8,9u8,67u8,126u8,92u8,130u8, - 176u8,111u8,16u8,124u8,79u8,68u8,75u8,42u8,236u8,133u8,52u8,2u8,34u8,145u8,59u8,71u8,118u8,167u8,123u8,237u8, - 172u8,211u8,124u8,145u8,36u8,3u8,37u8,99u8,89u8,81u8,120u8,9u8,158u8,18u8,192u8,227u8,209u8,195u8,83u8,164u8, - 88u8,190u8,124u8,228u8,90u8,251u8,183u8,240u8,24u8,103u8,227u8,176u8,186u8,255u8,6u8,164u8,213u8,72u8,227u8,16u8, - 132u8,198u8,249u8,77u8,212u8,40u8,202u8,183u8,8u8,217u8,24u8,65u8,113u8,191u8,220u8,63u8,172u8,55u8,171u8,175u8, - 235u8,2u8,220u8,135u8,247u8,240u8,9u8,222u8,150u8,103u8,80u8,76u8,161u8,68u8,82u8,217u8,17u8,235u8,42u8,212u8, - 250u8,97u8,179u8,157u8,193u8,189u8,27u8,168u8,25u8,171u8,29u8,181u8,1u8,174u8,33u8,6u8,175u8,168u8,95u8,14u8, - 76u8,171u8,62u8,199u8,234u8,254u8,48u8,204u8,234u8,143u8,23u8,188u8,128u8,56u8,236u8,199u8,161u8,205u8,231u8,16u8, - 61u8,62u8,143u8,249u8,157u8,163u8,102u8,104u8,20u8,9u8,188u8,235u8,237u8,193u8,3u8,71u8,42u8,189u8,171u8,90u8, - 65u8,81u8,211u8,33u8,146u8,102u8,69u8,232u8,250u8,140u8,143u8,183u8,167u8,23u8,77u8,80u8,68u8,159u8,121u8,234u8, - 149u8,102u8,216u8,105u8,214u8,111u8,197u8,216u8,116u8,108u8,111u8,19u8,27u8,229u8,57u8,107u8,139u8,41u8,222u8,208u8, - 5u8,220u8,117u8,46u8,76u8,156u8,135u8,170u8,98u8,172u8,205u8,38u8,92u8,8u8,122u8,89u8,219u8,155u8,244u8,102u8, - 100u8,50u8,126u8,87u8,26u8,138u8,17u8,102u8,17u8,253u8,119u8,231u8,139u8,179u8,28u8,194u8,119u8,23u8,133u8,144u8, - 123u8,210u8,10u8,182u8,37u8,122u8,231u8,58u8,116u8,106u8,58u8,126u8,99u8,150u8,149u8,39u8,240u8,17u8,179u8,119u8, - 166u8,217u8,86u8,132u8,254u8,156u8,131u8,6u8,204u8,242u8,92u8,218u8,78u8,141u8,59u8,53u8,76u8,91u8,107u8,213u8, - 93u8,157u8,87u8,135u8,252u8,127u8,30u8,185u8,229u8,184u8,240u8,53u8,110u8,51u8,203u8,113u8,1u8,24u8,255u8,157u8, - 161u8,77u8,70u8,170u8,225u8,97u8,171u8,234u8,203u8,227u8,151u8,103u8,186u8,13u8,56u8,87u8,85u8,153u8,56u8,242u8, - 146u8,50u8,3u8,245u8,89u8,179u8,200u8,170u8,188u8,98u8,57u8,52u8,50u8,110u8,110u8,93u8,143u8,122u8,188u8,178u8, - 97u8,19u8,145u8,78u8,255u8,26u8,101u8,186u8,32u8,81u8,1u8,149u8,82u8,237u8,68u8,168u8,115u8,75u8,204u8,245u8, - 152u8,51u8,28u8,84u8,65u8,34u8,196u8,158u8,232u8,249u8,117u8,123u8,141u8,89u8,44u8,218u8,254u8,157u8,224u8,5u8, - 219u8,78u8,139u8,6u8,59u8,253u8,223u8,103u8,28u8,147u8,99u8,242u8,7u8,12u8,234u8,17u8,186u8,22u8,6u8,0u8, - 0u8,0u8,0u8,10u8,98u8,105u8,116u8,95u8,118u8,101u8,99u8,116u8,111u8,114u8,181u8,10u8,31u8,139u8,8u8,0u8, - 0u8,0u8,0u8,0u8,2u8,255u8,221u8,88u8,109u8,111u8,219u8,54u8,16u8,254u8,158u8,95u8,113u8,233u8,128u8,78u8, - 234u8,220u8,196u8,41u8,138u8,125u8,176u8,227u8,0u8,25u8,154u8,109u8,6u8,182u8,12u8,104u8,186u8,173u8,88u8,81u8, - 72u8,178u8,68u8,219u8,68u8,101u8,201u8,19u8,201u8,56u8,105u8,225u8,255u8,190u8,227u8,139u8,40u8,138u8,146u8,188u8, - 164u8,67u8,177u8,97u8,2u8,146u8,56u8,226u8,241u8,222u8,248u8,220u8,115u8,103u8,110u8,202u8,76u8,228u8,4u8,24u8, - 207u8,38u8,147u8,5u8,229u8,209u8,45u8,73u8,121u8,89u8,193u8,167u8,35u8,192u8,71u8,48u8,179u8,162u8,223u8,78u8, - 143u8,212u8,219u8,211u8,211u8,83u8,120u8,179u8,38u8,176u8,173u8,202u8,91u8,154u8,145u8,12u8,104u8,145u8,145u8,59u8, - 160u8,12u8,74u8,193u8,161u8,92u8,194u8,162u8,20u8,69u8,198u8,148u8,108u8,90u8,22u8,140u8,195u8,213u8,252u8,250u8, - 213u8,213u8,219u8,9u8,136u8,111u8,95u8,194u8,12u8,198u8,119u8,47u8,198u8,248u8,76u8,173u8,178u8,203u8,2u8,85u8, - 220u8,38u8,57u8,205u8,32u8,39u8,197u8,138u8,175u8,149u8,18u8,202u8,141u8,47u8,187u8,132u8,193u8,138u8,222u8,146u8, - 194u8,213u8,248u8,211u8,213u8,245u8,15u8,111u8,126u8,108u8,171u8,60u8,51u8,14u8,106u8,145u8,223u8,127u8,121u8,253u8, - 42u8,186u8,153u8,255u8,113u8,85u8,11u8,157u8,77u8,91u8,238u8,111u8,146u8,59u8,186u8,17u8,27u8,72u8,242u8,188u8, - 220u8,97u8,20u8,141u8,65u8,70u8,63u8,18u8,71u8,209u8,207u8,151u8,111u8,219u8,122u8,198u8,47u8,94u8,26u8,75u8, - 140u8,87u8,34u8,229u8,240u8,29u8,229u8,191u8,233u8,173u8,107u8,244u8,53u8,45u8,183u8,247u8,35u8,200u8,170u8,114u8, - 59u8,66u8,129u8,178u8,34u8,38u8,155u8,242u8,209u8,241u8,41u8,77u8,35u8,251u8,82u8,230u8,125u8,73u8,73u8,158u8, - 77u8,64u8,123u8,112u8,190u8,40u8,203u8,252u8,66u8,11u8,236u8,181u8,165u8,173u8,88u8,228u8,52u8,133u8,165u8,40u8, - 160u8,32u8,187u8,192u8,209u8,19u8,78u8,28u8,251u8,141u8,165u8,132u8,49u8,82u8,241u8,99u8,35u8,9u8,23u8,48u8, - 30u8,213u8,89u8,11u8,167u8,67u8,82u8,231u8,54u8,216u8,62u8,225u8,156u8,112u8,12u8,78u8,20u8,156u8,84u8,50u8, - 231u8,237u8,5u8,27u8,4u8,46u8,233u8,40u8,38u8,19u8,178u8,217u8,242u8,251u8,192u8,209u8,176u8,91u8,83u8,4u8, - 91u8,240u8,137u8,109u8,73u8,234u8,248u8,42u8,31u8,137u8,128u8,138u8,38u8,69u8,99u8,226u8,124u8,102u8,210u8,53u8, - 29u8,16u8,196u8,213u8,192u8,154u8,13u8,97u8,54u8,171u8,183u8,54u8,27u8,246u8,237u8,189u8,129u8,213u8,109u8,84u8, - 135u8,251u8,208u8,243u8,163u8,246u8,125u8,43u8,216u8,58u8,90u8,36u8,233u8,135u8,224u8,233u8,70u8,56u8,225u8,141u8, - 96u8,153u8,228u8,140u8,132u8,109u8,197u8,77u8,90u8,234u8,79u8,223u8,212u8,120u8,243u8,220u8,232u8,9u8,93u8,31u8, - 66u8,163u8,163u8,63u8,110u8,35u8,213u8,13u8,218u8,23u8,222u8,27u8,112u8,202u8,167u8,15u8,25u8,13u8,14u8,71u8, - 173u8,119u8,77u8,136u8,141u8,170u8,163u8,230u8,183u8,114u8,28u8,177u8,231u8,104u8,162u8,69u8,154u8,139u8,140u8,192u8, - 53u8,217u8,93u8,46u8,202u8,138u8,179u8,249u8,178u8,113u8,131u8,20u8,76u8,84u8,132u8,1u8,254u8,136u8,156u8,159u8, - 24u8,132u8,245u8,248u8,91u8,11u8,202u8,200u8,140u8,240u8,112u8,128u8,142u8,47u8,44u8,93u8,147u8,77u8,226u8,26u8, - 239u8,47u8,52u8,7u8,237u8,74u8,46u8,162u8,203u8,154u8,103u8,16u8,98u8,99u8,216u8,81u8,252u8,100u8,208u8,126u8, - 64u8,246u8,98u8,102u8,139u8,163u8,103u8,203u8,190u8,225u8,198u8,27u8,172u8,6u8,142u8,4u8,131u8,65u8,64u8,194u8, - 33u8,150u8,193u8,40u8,138u8,140u8,49u8,97u8,106u8,37u8,182u8,92u8,19u8,99u8,122u8,86u8,73u8,149u8,229u8,132u8, - 49u8,201u8,122u8,148u8,51u8,228u8,85u8,114u8,75u8,75u8,193u8,144u8,60u8,18u8,78u8,78u8,252u8,242u8,103u8,132u8, - 7u8,118u8,255u8,4u8,20u8,58u8,237u8,49u8,143u8,192u8,90u8,211u8,228u8,208u8,195u8,8u8,86u8,2u8,171u8,160u8, - 70u8,187u8,142u8,49u8,120u8,106u8,21u8,59u8,71u8,48u8,50u8,252u8,237u8,49u8,193u8,157u8,83u8,232u8,152u8,171u8, - 170u8,220u8,69u8,232u8,138u8,173u8,22u8,95u8,141u8,227u8,153u8,163u8,232u8,153u8,212u8,130u8,36u8,74u8,186u8,167u8, - 139u8,38u8,186u8,72u8,195u8,220u8,14u8,35u8,173u8,199u8,234u8,59u8,107u8,244u8,253u8,16u8,126u8,28u8,149u8,142u8, - 65u8,39u8,195u8,54u8,185u8,211u8,22u8,99u8,55u8,73u8,238u8,3u8,77u8,147u8,228u8,139u8,26u8,192u8,205u8,169u8, - 133u8,6u8,64u8,42u8,173u8,29u8,252u8,252u8,90u8,176u8,47u8,139u8,32u8,81u8,252u8,255u8,48u8,164u8,72u8,185u8, - 115u8,196u8,42u8,210u8,30u8,24u8,169u8,20u8,15u8,3u8,233u8,248u8,243u8,144u8,212u8,210u8,250u8,223u8,192u8,210u8, - 205u8,154u8,46u8,121u8,7u8,49u8,57u8,193u8,151u8,139u8,123u8,136u8,147u8,141u8,236u8,57u8,241u8,9u8,160u8,191u8, - 245u8,63u8,114u8,144u8,91u8,85u8,36u8,145u8,173u8,136u8,175u8,19u8,133u8,55u8,171u8,207u8,42u8,249u8,154u8,213u8, - 188u8,104u8,128u8,90u8,15u8,107u8,52u8,207u8,97u8,65u8,224u8,35u8,169u8,74u8,28u8,170u8,112u8,34u8,236u8,18u8, - 152u8,244u8,41u8,146u8,62u8,28u8,194u8,160u8,246u8,166u8,3u8,64u8,76u8,71u8,160u8,151u8,100u8,50u8,154u8,115u8, - 50u8,237u8,188u8,221u8,230u8,36u8,196u8,112u8,193u8,1u8,217u8,33u8,140u8,78u8,253u8,173u8,180u8,61u8,229u8,52u8, - 243u8,11u8,213u8,243u8,131u8,63u8,58u8,152u8,109u8,36u8,39u8,155u8,199u8,226u8,154u8,122u8,243u8,132u8,132u8,181u8, - 209u8,227u8,32u8,219u8,157u8,130u8,112u8,133u8,182u8,135u8,140u8,246u8,156u8,177u8,71u8,63u8,24u8,233u8,180u8,125u8, - 29u8,149u8,206u8,160u8,51u8,39u8,120u8,193u8,253u8,77u8,94u8,237u8,73u8,80u8,166u8,129u8,25u8,181u8,8u8,69u8, - 134u8,19u8,130u8,255u8,10u8,158u8,27u8,179u8,97u8,71u8,149u8,242u8,212u8,99u8,165u8,214u8,142u8,105u8,215u8,122u8, - 111u8,252u8,173u8,217u8,167u8,22u8,243u8,131u8,177u8,106u8,255u8,89u8,252u8,93u8,127u8,31u8,229u8,166u8,63u8,98u8, - 217u8,18u8,123u8,77u8,184u8,168u8,52u8,203u8,227u8,119u8,34u8,65u8,36u8,169u8,63u8,184u8,25u8,156u8,64u8,44u8, - 187u8,105u8,108u8,181u8,85u8,4u8,155u8,1u8,35u8,5u8,118u8,133u8,39u8,103u8,79u8,32u8,41u8,50u8,136u8,21u8, - 162u8,98u8,119u8,41u8,129u8,177u8,95u8,165u8,253u8,71u8,139u8,117u8,58u8,220u8,39u8,38u8,32u8,191u8,183u8,124u8, - 153u8,110u8,241u8,172u8,93u8,78u8,189u8,251u8,220u8,246u8,224u8,83u8,180u8,27u8,78u8,79u8,67u8,152u8,179u8,185u8, - 92u8,61u8,56u8,94u8,232u8,217u8,84u8,14u8,164u8,159u8,215u8,30u8,186u8,54u8,254u8,213u8,30u8,97u8,221u8,83u8, - 156u8,140u8,31u8,162u8,161u8,35u8,127u8,196u8,137u8,75u8,82u8,56u8,236u8,138u8,95u8,74u8,10u8,140u8,135u8,89u8, - 235u8,112u8,186u8,31u8,84u8,73u8,166u8,242u8,131u8,66u8,108u8,22u8,216u8,215u8,176u8,164u8,4u8,75u8,22u8,185u8, - 170u8,42u8,22u8,234u8,18u8,163u8,206u8,4u8,233u8,87u8,131u8,31u8,134u8,91u8,7u8,161u8,190u8,24u8,104u8,124u8, - 126u8,8u8,196u8,251u8,125u8,101u8,174u8,179u8,166u8,238u8,243u8,178u8,88u8,17u8,188u8,134u8,96u8,228u8,79u8,65u8, - 138u8,84u8,241u8,1u8,211u8,95u8,184u8,213u8,128u8,87u8,113u8,90u8,172u8,36u8,51u8,4u8,88u8,220u8,86u8,159u8, - 6u8,54u8,174u8,132u8,16u8,43u8,161u8,67u8,156u8,49u8,87u8,150u8,240u8,162u8,2u8,83u8,80u8,148u8,192u8,68u8, - 186u8,182u8,138u8,106u8,179u8,35u8,41u8,82u8,64u8,60u8,86u8,3u8,66u8,165u8,220u8,37u8,89u8,167u8,185u8,27u8, - 111u8,37u8,132u8,162u8,122u8,107u8,84u8,123u8,25u8,37u8,131u8,84u8,226u8,248u8,88u8,67u8,171u8,157u8,212u8,154u8, - 75u8,28u8,193u8,30u8,162u8,30u8,152u8,51u8,181u8,248u8,204u8,181u8,226u8,144u8,63u8,70u8,249u8,61u8,190u8,83u8, - 121u8,209u8,147u8,15u8,171u8,183u8,152u8,108u8,213u8,23u8,67u8,152u8,24u8,57u8,19u8,113u8,121u8,105u8,36u8,37u8, - 104u8,170u8,190u8,183u8,50u8,166u8,39u8,37u8,201u8,208u8,152u8,69u8,140u8,253u8,196u8,191u8,242u8,24u8,114u8,216u8, - 47u8,7u8,89u8,64u8,199u8,131u8,109u8,85u8,81u8,91u8,8u8,11u8,116u8,242u8,131u8,127u8,43u8,162u8,35u8,212u8, - 127u8,253u8,203u8,135u8,163u8,182u8,216u8,115u8,55u8,17u8,46u8,18u8,191u8,122u8,39u8,99u8,143u8,202u8,34u8,191u8, - 127u8,239u8,159u8,236u8,174u8,172u8,178u8,72u8,94u8,141u8,5u8,157u8,179u8,177u8,87u8,109u8,70u8,215u8,254u8,232u8, - 47u8,230u8,23u8,234u8,111u8,87u8,20u8,0u8,0u8,0u8,0u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,204u8,2u8, - 31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,181u8,82u8,205u8,78u8,3u8,33u8,16u8,190u8,247u8,41u8, - 230u8,100u8,90u8,99u8,218u8,251u8,246u8,166u8,111u8,96u8,188u8,47u8,179u8,11u8,219u8,78u8,196u8,129u8,240u8,211u8, - 218u8,52u8,125u8,119u8,89u8,22u8,86u8,107u8,52u8,61u8,149u8,19u8,48u8,243u8,205u8,247u8,3u8,31u8,70u8,70u8, - 173u8,192u8,7u8,217u8,52u8,158u8,118u8,172u8,28u8,156u8,23u8,144u8,214u8,102u8,3u8,207u8,198u8,57u8,115u8,244u8, - 16u8,246u8,10u8,80u8,74u8,167u8,188u8,7u8,51u8,228u8,227u8,212u8,90u8,27u8,95u8,12u8,247u8,202u8,134u8,136u8, - 90u8,159u8,158u8,224u8,100u8,34u8,244u8,200u8,169u8,143u8,248u8,189u8,2u8,196u8,132u8,16u8,128u8,30u8,58u8,69u8, - 188u8,3u8,76u8,164u8,46u8,246u8,1u8,142u8,14u8,173u8,77u8,188u8,152u8,216u8,34u8,75u8,64u8,174u8,115u8,11u8, - 105u8,61u8,10u8,33u8,234u8,182u8,64u8,139u8,226u8,125u8,26u8,42u8,157u8,177u8,112u8,206u8,144u8,102u8,86u8,123u8, - 249u8,3u8,42u8,186u8,108u8,171u8,45u8,61u8,2u8,186u8,217u8,38u8,121u8,32u8,30u8,7u8,14u8,164u8,180u8,204u8, - 0u8,198u8,64u8,7u8,5u8,54u8,118u8,154u8,122u8,24u8,34u8,195u8,53u8,124u8,233u8,27u8,120u8,152u8,100u8,172u8, - 210u8,174u8,220u8,110u8,23u8,223u8,209u8,88u8,82u8,183u8,34u8,252u8,49u8,189u8,52u8,181u8,102u8,184u8,158u8,92u8, - 193u8,211u8,227u8,140u8,235u8,241u8,183u8,144u8,85u8,46u8,93u8,42u8,245u8,6u8,94u8,85u8,136u8,46u8,189u8,131u8, - 139u8,10u8,12u8,235u8,19u8,208u8,144u8,222u8,65u8,64u8,178u8,137u8,233u8,22u8,217u8,99u8,31u8,200u8,112u8,145u8, - 178u8,134u8,183u8,156u8,192u8,88u8,245u8,86u8,101u8,61u8,83u8,61u8,131u8,241u8,128u8,164u8,177u8,75u8,159u8,133u8, - 56u8,215u8,215u8,153u8,40u8,119u8,150u8,148u8,70u8,3u8,228u8,219u8,240u8,201u8,237u8,52u8,114u8,244u8,48u8,91u8, - 232u8,140u8,209u8,219u8,27u8,234u8,240u8,95u8,117u8,53u8,129u8,59u8,168u8,204u8,17u8,46u8,113u8,14u8,121u8,214u8, - 122u8,89u8,124u8,1u8,168u8,44u8,20u8,94u8,31u8,3u8,0u8,0u8,0u8,0u8,8u8,102u8,101u8,97u8,116u8,117u8, - 114u8,101u8,115u8,254u8,21u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,197u8,89u8,109u8,83u8,219u8, - 58u8,22u8,254u8,222u8,95u8,33u8,102u8,103u8,153u8,164u8,27u8,160u8,9u8,133u8,182u8,41u8,237u8,108u8,72u8,12u8, - 100u8,26u8,2u8,19u8,2u8,157u8,206u8,157u8,174u8,80u8,108u8,37u8,209u8,226u8,216u8,94u8,217u8,14u8,100u8,186u8, - 253u8,239u8,123u8,36u8,89u8,126u8,119u8,8u8,105u8,239u8,222u8,204u8,116u8,106u8,108u8,233u8,188u8,60u8,231u8,57u8, - 71u8,71u8,210u8,193u8,193u8,1u8,234u8,209u8,41u8,115u8,168u8,143u8,166u8,148u8,4u8,33u8,167u8,104u8,106u8,147u8, - 25u8,252u8,229u8,114u8,212u8,241u8,2u8,215u8,223u8,71u8,227u8,185u8,235u8,83u8,68u8,224u8,83u8,232u8,83u8,11u8, - 49u8,71u8,125u8,64u8,190u8,71u8,77u8,54u8,101u8,38u8,98u8,11u8,207u8,166u8,11u8,234u8,4u8,36u8,96u8,174u8, - 227u8,35u8,119u8,170u8,133u8,249u8,48u8,250u8,213u8,1u8,40u8,9u8,230u8,20u8,93u8,186u8,75u8,138u8,252u8,192u8, - 178u8,217u8,164u8,33u8,95u8,68u8,98u8,162u8,55u8,196u8,177u8,82u8,111u8,167u8,156u8,44u8,232u8,163u8,203u8,31u8, - 246u8,95u8,45u8,92u8,43u8,180u8,229u8,204u8,118u8,59u8,150u8,251u8,227u8,21u8,130u8,31u8,24u8,164u8,222u8,83u8, - 206u8,93u8,254u8,49u8,251u8,206u8,103u8,51u8,135u8,230u8,95u8,46u8,169u8,25u8,136u8,145u8,242u8,45u8,24u8,246u8, - 233u8,79u8,252u8,105u8,29u8,103u8,17u8,178u8,103u8,128u8,172u8,130u8,155u8,73u8,160u8,98u8,35u8,12u8,98u8,206u8, - 51u8,248u8,35u8,127u8,238u8,134u8,182u8,133u8,76u8,119u8,65u8,209u8,35u8,11u8,230u8,200u8,114u8,205u8,48u8,134u8, - 24u8,61u8,206u8,25u8,204u8,248u8,119u8,232u8,7u8,16u8,0u8,64u8,67u8,224u8,230u8,80u8,8u8,14u8,64u8,47u8, - 158u8,133u8,140u8,125u8,45u8,189u8,239u8,4u8,28u8,32u8,52u8,229u8,76u8,24u8,64u8,96u8,232u8,99u8,86u8,29u8, - 167u8,255u8,9u8,153u8,128u8,149u8,120u8,30u8,119u8,151u8,196u8,150u8,49u8,212u8,33u8,64u8,238u8,35u8,0u8,9u8, - 76u8,56u8,133u8,225u8,60u8,156u8,193u8,231u8,199u8,57u8,117u8,180u8,124u8,22u8,201u8,103u8,206u8,172u8,32u8,217u8, - 135u8,184u8,130u8,121u8,174u8,139u8,22u8,196u8,89u8,33u8,147u8,56u8,240u8,240u8,64u8,17u8,11u8,208u8,156u8,112u8, - 8u8,184u8,139u8,66u8,199u8,2u8,225u8,129u8,14u8,191u8,233u8,90u8,84u8,155u8,254u8,2u8,124u8,72u8,76u8,71u8, - 146u8,248u8,105u8,179u8,41u8,13u8,216u8,130u8,182u8,115u8,242u8,246u8,224u8,219u8,235u8,128u8,19u8,199u8,103u8,0u8, - 233u8,235u8,172u8,104u8,6u8,4u8,118u8,236u8,149u8,196u8,19u8,16u8,13u8,157u8,128u8,217u8,48u8,158u8,83u8,155u8, - 4u8,212u8,146u8,246u8,33u8,238u8,218u8,182u8,27u8,10u8,31u8,124u8,248u8,231u8,121u8,212u8,161u8,150u8,72u8,20u8, - 230u8,107u8,21u8,72u8,200u8,9u8,86u8,30u8,216u8,99u8,131u8,48u8,226u8,251u8,174u8,201u8,164u8,0u8,105u8,174u8, - 240u8,148u8,229u8,226u8,34u8,176u8,115u8,192u8,250u8,101u8,148u8,42u8,211u8,208u8,145u8,223u8,124u8,149u8,27u8,218u8, - 46u8,145u8,132u8,137u8,150u8,41u8,119u8,23u8,106u8,184u8,4u8,14u8,108u8,160u8,42u8,92u8,138u8,10u8,44u8,201u8, - 107u8,49u8,127u8,98u8,179u8,25u8,149u8,168u8,115u8,186u8,16u8,147u8,32u8,14u8,174u8,99u8,82u8,53u8,82u8,68u8, - 103u8,66u8,129u8,106u8,78u8,33u8,2u8,128u8,152u8,131u8,94u8,123u8,148u8,67u8,24u8,75u8,33u8,139u8,24u8,36u8, - 101u8,67u8,48u8,193u8,99u8,238u8,66u8,100u8,69u8,29u8,161u8,75u8,202u8,193u8,48u8,141u8,133u8,200u8,125u8,81u8, - 80u8,84u8,157u8,153u8,133u8,64u8,131u8,196u8,157u8,9u8,157u8,147u8,37u8,131u8,218u8,3u8,85u8,38u8,194u8,66u8, - 248u8,149u8,84u8,135u8,120u8,128u8,132u8,49u8,250u8,95u8,132u8,66u8,210u8,62u8,50u8,74u8,166u8,2u8,24u8,2u8, - 206u8,120u8,192u8,106u8,202u8,151u8,25u8,200u8,96u8,150u8,103u8,147u8,213u8,132u8,152u8,15u8,121u8,63u8,135u8,110u8, - 32u8,192u8,32u8,129u8,2u8,72u8,80u8,154u8,228u8,152u8,7u8,116u8,176u8,147u8,132u8,145u8,106u8,163u8,64u8,69u8, - 105u8,25u8,80u8,63u8,80u8,5u8,52u8,101u8,147u8,86u8,80u8,179u8,25u8,208u8,255u8,94u8,56u8,133u8,45u8,10u8, - 196u8,177u8,168u8,99u8,174u8,176u8,57u8,167u8,230u8,3u8,166u8,14u8,153u8,216u8,212u8,186u8,7u8,195u8,109u8,247u8, - 177u8,94u8,130u8,165u8,96u8,188u8,7u8,184u8,76u8,152u8,205u8,130u8,21u8,152u8,65u8,124u8,69u8,16u8,168u8,180u8, - 65u8,156u8,144u8,144u8,199u8,200u8,11u8,33u8,216u8,102u8,108u8,219u8,62u8,186u8,112u8,31u8,69u8,36u8,26u8,58u8, - 224u8,180u8,16u8,67u8,139u8,249u8,82u8,127u8,28u8,33u8,77u8,64u8,201u8,12u8,19u8,30u8,32u8,75u8,3u8,123u8, - 165u8,245u8,112u8,10u8,211u8,29u8,20u8,240u8,16u8,248u8,18u8,23u8,179u8,189u8,63u8,241u8,167u8,117u8,116u8,69u8, - 18u8,94u8,11u8,15u8,253u8,57u8,196u8,72u8,235u8,62u8,64u8,95u8,231u8,20u8,60u8,227u8,8u8,74u8,24u8,179u8, - 226u8,74u8,224u8,65u8,164u8,201u8,12u8,152u8,173u8,1u8,23u8,53u8,19u8,28u8,142u8,240u8,78u8,216u8,165u8,147u8, - 60u8,162u8,94u8,28u8,216u8,56u8,175u8,15u8,16u8,89u8,18u8,102u8,139u8,105u8,42u8,223u8,133u8,152u8,168u8,82u8, - 76u8,168u8,73u8,196u8,58u8,3u8,10u8,89u8,105u8,201u8,205u8,73u8,221u8,143u8,101u8,14u8,116u8,157u8,66u8,113u8, - 81u8,146u8,223u8,36u8,228u8,168u8,123u8,213u8,51u8,112u8,207u8,184u8,54u8,134u8,61u8,99u8,216u8,253u8,134u8,187u8, - 23u8,70u8,247u8,75u8,27u8,133u8,199u8,111u8,209u8,39u8,212u8,84u8,235u8,91u8,18u8,109u8,180u8,150u8,90u8,181u8, - 122u8,27u8,77u8,92u8,23u8,10u8,154u8,25u8,17u8,248u8,44u8,187u8,162u8,34u8,89u8,185u8,226u8,225u8,165u8,186u8, - 235u8,114u8,228u8,207u8,34u8,234u8,86u8,200u8,69u8,198u8,132u8,222u8,140u8,19u8,139u8,230u8,184u8,42u8,237u8,128u8, - 207u8,13u8,168u8,89u8,224u8,161u8,149u8,162u8,87u8,84u8,205u8,33u8,93u8,3u8,96u8,180u8,8u8,128u8,207u8,22u8, - 128u8,50u8,71u8,34u8,91u8,98u8,53u8,30u8,103u8,75u8,248u8,154u8,76u8,44u8,67u8,48u8,174u8,81u8,41u8,4u8, - 199u8,35u8,163u8,51u8,198u8,103u8,163u8,62u8,184u8,129u8,59u8,55u8,248u8,122u8,212u8,191u8,235u8,140u8,13u8,141u8, - 97u8,171u8,128u8,161u8,52u8,3u8,43u8,59u8,49u8,241u8,113u8,164u8,249u8,165u8,232u8,85u8,232u8,173u8,196u8,79u8, - 45u8,227u8,143u8,232u8,230u8,162u8,211u8,218u8,59u8,106u8,182u8,26u8,226u8,233u8,80u8,60u8,73u8,134u8,142u8,250u8, - 215u8,198u8,101u8,111u8,175u8,121u8,252u8,70u8,44u8,60u8,243u8,132u8,158u8,138u8,88u8,190u8,236u8,209u8,34u8,221u8, - 9u8,52u8,213u8,44u8,125u8,102u8,17u8,90u8,11u8,115u8,25u8,81u8,193u8,88u8,12u8,182u8,226u8,14u8,120u8,171u8, - 108u8,197u8,96u8,43u8,30u8,118u8,198u8,253u8,59u8,227u8,70u8,163u8,125u8,24u8,53u8,95u8,41u8,184u8,103u8,52u8, - 192u8,254u8,156u8,200u8,201u8,224u8,40u8,230u8,204u8,163u8,11u8,75u8,78u8,142u8,106u8,148u8,64u8,94u8,76u8,255u8, - 241u8,140u8,18u8,141u8,106u8,74u8,120u8,133u8,224u8,45u8,19u8,98u8,189u8,250u8,103u8,35u8,123u8,79u8,68u8,119u8, - 139u8,85u8,207u8,219u8,110u8,67u8,131u8,64u8,49u8,115u8,166u8,110u8,187u8,109u8,206u8,9u8,115u8,48u8,3u8,131u8, - 238u8,227u8,0u8,64u8,189u8,7u8,188u8,77u8,81u8,226u8,84u8,111u8,36u8,198u8,160u8,126u8,47u8,85u8,187u8,182u8, - 141u8,243u8,47u8,85u8,164u8,206u8,245u8,248u8,234u8,6u8,223u8,140u8,123u8,80u8,15u8,58u8,253u8,33u8,238u8,247u8, - 242u8,65u8,126u8,91u8,30u8,228u8,180u8,247u8,88u8,123u8,92u8,140u8,113u8,181u8,252u8,146u8,248u8,150u8,203u8,220u8, - 50u8,188u8,213u8,154u8,171u8,67u8,235u8,34u8,104u8,107u8,220u8,71u8,9u8,115u8,132u8,248u8,132u8,57u8,132u8,175u8, - 68u8,0u8,23u8,208u8,74u8,192u8,170u8,235u8,11u8,216u8,151u8,199u8,155u8,2u8,124u8,119u8,137u8,79u8,251u8,195u8, - 206u8,232u8,27u8,62u8,187u8,26u8,93u8,66u8,13u8,185u8,59u8,214u8,200u8,30u8,149u8,35u8,187u8,92u8,96u8,165u8, - 20u8,43u8,165u8,120u8,121u8,156u8,224u8,89u8,34u8,174u8,12u8,72u8,225u8,69u8,149u8,160u8,151u8,128u8,88u8,162u8, - 174u8,18u8,189u8,25u8,17u8,29u8,106u8,84u8,187u8,76u8,104u8,171u8,97u8,127u8,6u8,236u8,21u8,229u8,14u8,218u8, - 145u8,128u8,179u8,73u8,24u8,168u8,102u8,78u8,118u8,127u8,182u8,107u8,62u8,192u8,58u8,224u8,122u8,208u8,159u8,112u8, - 127u8,243u8,21u8,116u8,48u8,48u8,186u8,99u8,153u8,180u8,189u8,254u8,205u8,120u8,212u8,63u8,189u8,29u8,27u8,248u8, - 28u8,74u8,242u8,153u8,145u8,144u8,246u8,184u8,28u8,218u8,200u8,42u8,89u8,64u8,18u8,155u8,48u8,88u8,142u8,133u8, - 229u8,69u8,250u8,62u8,163u8,174u8,4u8,250u8,103u8,84u8,188u8,124u8,213u8,94u8,107u8,192u8,75u8,171u8,212u8,34u8, - 180u8,3u8,134u8,169u8,213u8,58u8,58u8,106u8,126u8,104u8,183u8,149u8,221u8,248u8,129u8,174u8,112u8,212u8,102u8,137u8, - 26u8,22u8,80u8,238u8,16u8,27u8,47u8,91u8,169u8,2u8,246u8,23u8,151u8,169u8,203u8,219u8,193u8,184u8,143u8,141u8, - 158u8,180u8,27u8,95u8,127u8,193u8,119u8,157u8,65u8,191u8,7u8,11u8,48u8,190u8,107u8,229u8,11u8,214u8,187u8,98u8, - 236u8,51u8,110u8,99u8,239u8,33u8,241u8,118u8,217u8,42u8,6u8,125u8,19u8,101u8,37u8,145u8,95u8,171u8,100u8,203u8, - 34u8,182u8,137u8,41u8,207u8,114u8,224u8,116u8,208u8,249u8,98u8,180u8,78u8,247u8,90u8,71u8,199u8,165u8,221u8,198u8, - 111u8,8u8,110u8,73u8,104u8,107u8,126u8,125u8,211u8,232u8,70u8,6u8,98u8,48u8,48u8,114u8,74u8,199u8,242u8,125u8, - 121u8,30u8,79u8,108u8,56u8,121u8,104u8,77u8,228u8,132u8,66u8,248u8,138u8,210u8,74u8,130u8,149u8,150u8,176u8,101u8, - 108u8,138u8,122u8,42u8,35u8,1u8,50u8,220u8,144u8,195u8,94u8,109u8,6u8,219u8,63u8,239u8,197u8,221u8,157u8,64u8, - 151u8,4u8,81u8,41u8,81u8,251u8,81u8,40u8,44u8,128u8,190u8,47u8,171u8,43u8,17u8,221u8,132u8,51u8,19u8,81u8, - 129u8,247u8,46u8,23u8,251u8,35u8,78u8,229u8,118u8,57u8,58u8,101u8,218u8,79u8,129u8,61u8,50u8,110u8,174u8,110u8, - 71u8,93u8,168u8,33u8,163u8,171u8,219u8,235u8,56u8,107u8,62u8,148u8,35u8,173u8,45u8,199u8,202u8,242u8,34u8,218u8, - 57u8,113u8,37u8,80u8,231u8,69u8,108u8,9u8,119u8,78u8,81u8,37u8,214u8,50u8,13u8,225u8,192u8,16u8,228u8,154u8, - 176u8,215u8,6u8,140u8,106u8,22u8,155u8,78u8,41u8,7u8,48u8,212u8,25u8,75u8,252u8,65u8,30u8,60u8,200u8,241u8, - 123u8,81u8,218u8,34u8,18u8,194u8,43u8,168u8,135u8,126u8,189u8,24u8,164u8,84u8,41u8,186u8,233u8,159u8,227u8,78u8, - 183u8,123u8,117u8,59u8,28u8,199u8,8u8,54u8,223u8,148u8,67u8,168u8,13u8,194u8,90u8,111u8,69u8,197u8,73u8,203u8, - 172u8,42u8,47u8,25u8,49u8,191u8,82u8,83u8,210u8,202u8,170u8,55u8,129u8,212u8,166u8,51u8,181u8,245u8,246u8,64u8, - 65u8,5u8,111u8,215u8,231u8,118u8,207u8,24u8,24u8,231u8,144u8,29u8,87u8,67u8,124u8,125u8,117u8,53u8,72u8,208u8, - 106u8,150u8,163u8,149u8,232u8,196u8,82u8,103u8,17u8,172u8,188u8,196u8,18u8,172u8,10u8,66u8,182u8,132u8,42u8,175u8, - 170u8,186u8,247u8,129u8,99u8,67u8,14u8,202u8,137u8,61u8,163u8,19u8,78u8,208u8,132u8,248u8,240u8,151u8,11u8,59u8, - 88u8,5u8,159u8,31u8,122u8,158u8,203u8,3u8,145u8,161u8,247u8,209u8,152u8,125u8,113u8,106u8,119u8,95u8,10u8,233u8, - 166u8,189u8,208u8,232u8,27u8,244u8,184u8,231u8,163u8,206u8,245u8,197u8,55u8,220u8,25u8,156u8,27u8,167u8,163u8,78u8, - 126u8,49u8,108u8,22u8,119u8,196u8,178u8,17u8,226u8,43u8,232u8,11u8,96u8,111u8,239u8,205u8,87u8,56u8,50u8,8u8, - 71u8,123u8,207u8,146u8,46u8,104u8,141u8,34u8,64u8,35u8,223u8,1u8,149u8,201u8,222u8,246u8,208u8,98u8,141u8,230u8, - 181u8,235u8,94u8,62u8,32u8,217u8,139u8,13u8,89u8,66u8,79u8,7u8,55u8,205u8,214u8,225u8,251u8,102u8,18u8,38u8, - 255u8,87u8,162u8,33u8,197u8,97u8,144u8,7u8,123u8,142u8,209u8,109u8,119u8,124u8,59u8,74u8,69u8,225u8,240u8,99u8, - 249u8,50u8,230u8,71u8,83u8,68u8,57u8,151u8,48u8,148u8,45u8,103u8,69u8,185u8,69u8,208u8,179u8,162u8,76u8,37u8, - 107u8,235u8,133u8,173u8,168u8,177u8,18u8,108u8,69u8,26u8,92u8,210u8,73u8,34u8,50u8,1u8,202u8,251u8,242u8,130u8, - 33u8,57u8,197u8,132u8,17u8,186u8,137u8,120u8,228u8,46u8,236u8,134u8,109u8,234u8,204u8,160u8,230u8,194u8,194u8,55u8, - 3u8,65u8,78u8,9u8,218u8,212u8,155u8,67u8,232u8,56u8,177u8,83u8,104u8,199u8,45u8,209u8,237u8,233u8,23u8,227u8, - 91u8,210u8,22u8,141u8,12u8,48u8,119u8,136u8,207u8,58u8,131u8,27u8,3u8,127u8,29u8,93u8,13u8,207u8,241u8,192u8, - 24u8,158u8,143u8,47u8,226u8,80u8,188u8,253u8,11u8,111u8,140u8,250u8,25u8,22u8,190u8,74u8,45u8,253u8,226u8,120u8, - 27u8,14u8,169u8,45u8,121u8,96u8,38u8,46u8,187u8,228u8,133u8,132u8,227u8,6u8,226u8,208u8,58u8,190u8,189u8,33u8, - 150u8,5u8,81u8,243u8,211u8,203u8,145u8,113u8,54u8,234u8,92u8,26u8,95u8,175u8,70u8,95u8,48u8,212u8,244u8,161u8, - 49u8,194u8,67u8,195u8,232u8,25u8,189u8,212u8,169u8,98u8,70u8,77u8,20u8,228u8,248u8,90u8,175u8,145u8,180u8,10u8, - 162u8,233u8,128u8,19u8,106u8,216u8,242u8,6u8,62u8,13u8,100u8,43u8,33u8,238u8,161u8,28u8,117u8,88u8,161u8,148u8, - 42u8,118u8,37u8,4u8,18u8,86u8,138u8,144u8,38u8,68u8,210u8,130u8,219u8,72u8,93u8,208u8,157u8,132u8,239u8,63u8, - 55u8,242u8,236u8,57u8,211u8,29u8,40u8,236u8,6u8,149u8,69u8,122u8,159u8,40u8,159u8,181u8,140u8,125u8,212u8,133u8, - 227u8,106u8,121u8,87u8,2u8,39u8,138u8,226u8,206u8,65u8,219u8,24u8,129u8,4u8,76u8,250u8,39u8,236u8,109u8,246u8, - 11u8,53u8,72u8,54u8,68u8,58u8,153u8,176u8,188u8,160u8,168u8,197u8,56u8,182u8,209u8,174u8,154u8,223u8,136u8,148u8, - 39u8,182u8,30u8,191u8,253u8,220u8,208u8,102u8,100u8,222u8,170u8,4u8,88u8,151u8,65u8,112u8,49u8,68u8,121u8,176u8, - 83u8,83u8,162u8,219u8,237u8,40u8,88u8,216u8,157u8,38u8,154u8,235u8,192u8,57u8,105u8,49u8,104u8,22u8,247u8,156u8, - 176u8,5u8,131u8,67u8,78u8,230u8,139u8,67u8,5u8,88u8,250u8,28u8,6u8,233u8,87u8,21u8,209u8,122u8,253u8,99u8, - 146u8,173u8,83u8,84u8,219u8,161u8,79u8,176u8,191u8,244u8,79u8,180u8,41u8,159u8,107u8,66u8,112u8,189u8,158u8,50u8, - 73u8,252u8,196u8,50u8,131u8,3u8,55u8,53u8,44u8,182u8,166u8,17u8,187u8,241u8,35u8,31u8,181u8,63u8,190u8,255u8, - 172u8,199u8,98u8,126u8,38u8,138u8,109u8,32u8,70u8,124u8,115u8,251u8,9u8,237u8,46u8,224u8,190u8,6u8,210u8,156u8, - 195u8,113u8,195u8,204u8,118u8,39u8,176u8,109u8,132u8,55u8,121u8,147u8,246u8,245u8,140u8,172u8,28u8,6u8,2u8,222u8, - 100u8,95u8,57u8,240u8,74u8,25u8,208u8,110u8,171u8,194u8,80u8,219u8,85u8,33u8,74u8,57u8,15u8,119u8,51u8,192u8, - 145u8,26u8,67u8,39u8,200u8,201u8,123u8,11u8,196u8,173u8,37u8,212u8,126u8,173u8,101u8,41u8,19u8,181u8,172u8,6u8, - 98u8,245u8,134u8,188u8,236u8,72u8,73u8,149u8,176u8,130u8,122u8,134u8,254u8,129u8,154u8,85u8,174u8,111u8,104u8,114u8, - 68u8,160u8,223u8,99u8,115u8,36u8,76u8,25u8,61u8,37u8,182u8,191u8,177u8,213u8,169u8,132u8,235u8,138u8,35u8,123u8, - 81u8,138u8,227u8,21u8,50u8,117u8,153u8,152u8,89u8,241u8,68u8,250u8,164u8,22u8,131u8,104u8,152u8,44u8,39u8,155u8, - 172u8,35u8,229u8,164u8,68u8,187u8,187u8,241u8,8u8,168u8,91u8,1u8,212u8,19u8,191u8,182u8,155u8,33u8,78u8,37u8, - 105u8,26u8,218u8,212u8,194u8,26u8,116u8,65u8,109u8,79u8,29u8,219u8,49u8,199u8,180u8,67u8,184u8,167u8,128u8,101u8, - 157u8,62u8,169u8,199u8,236u8,101u8,95u8,226u8,91u8,26u8,237u8,182u8,34u8,112u8,170u8,80u8,161u8,180u8,191u8,13u8, - 45u8,87u8,249u8,157u8,142u8,155u8,136u8,252u8,100u8,37u8,143u8,75u8,44u8,250u8,4u8,1u8,208u8,202u8,14u8,196u8, - 158u8,53u8,51u8,138u8,193u8,22u8,128u8,248u8,15u8,162u8,22u8,163u8,147u8,19u8,84u8,211u8,218u8,209u8,223u8,209u8, - 251u8,186u8,184u8,238u8,11u8,223u8,23u8,137u8,146u8,99u8,148u8,54u8,184u8,142u8,78u8,62u8,165u8,244u8,230u8,153u8, - 164u8,103u8,121u8,161u8,63u8,199u8,226u8,82u8,52u8,197u8,171u8,55u8,149u8,249u8,12u8,165u8,31u8,78u8,56u8,19u8, - 22u8,71u8,97u8,1u8,100u8,82u8,211u8,83u8,74u8,179u8,101u8,40u8,130u8,168u8,158u8,49u8,228u8,181u8,150u8,25u8, - 61u8,252u8,55u8,134u8,33u8,33u8,10u8,16u8,121u8,237u8,156u8,93u8,84u8,123u8,243u8,52u8,157u8,162u8,127u8,197u8, - 115u8,215u8,16u8,192u8,204u8,240u8,155u8,20u8,46u8,68u8,11u8,20u8,143u8,89u8,152u8,226u8,66u8,21u8,15u8,52u8, - 239u8,255u8,15u8,225u8,79u8,9u8,61u8,65u8,149u8,36u8,216u8,5u8,108u8,242u8,117u8,162u8,52u8,84u8,0u8,98u8, - 12u8,30u8,218u8,129u8,218u8,149u8,70u8,240u8,111u8,127u8,136u8,251u8,237u8,239u8,49u8,36u8,226u8,175u8,120u8,201u8, - 132u8,52u8,129u8,195u8,202u8,156u8,203u8,169u8,202u8,175u8,151u8,137u8,196u8,116u8,145u8,88u8,50u8,157u8,18u8,75u8, - 154u8,133u8,42u8,91u8,50u8,232u8,104u8,147u8,65u8,205u8,119u8,155u8,140u8,106u8,29u8,22u8,70u8,233u8,85u8,57u8, - 169u8,58u8,41u8,161u8,117u8,145u8,22u8,155u8,141u8,61u8,170u8,139u8,241u8,27u8,202u8,125u8,7u8,131u8,91u8,27u8, - 14u8,110u8,29u8,194u8,224u8,195u8,103u8,33u8,202u8,23u8,254u8,10u8,140u8,242u8,195u8,182u8,113u8,127u8,231u8,37u8, - 254u8,239u8,252u8,94u8,0u8,50u8,212u8,172u8,77u8,69u8,106u8,201u8,213u8,32u8,199u8,210u8,92u8,127u8,23u8,60u8, - 57u8,48u8,182u8,29u8,117u8,133u8,245u8,181u8,11u8,84u8,105u8,107u8,184u8,59u8,125u8,106u8,104u8,74u8,3u8,105u8, - 63u8,8u8,171u8,190u8,199u8,111u8,190u8,151u8,120u8,146u8,90u8,34u8,155u8,21u8,208u8,164u8,134u8,124u8,168u8,192u8, - 35u8,53u8,164u8,192u8,131u8,231u8,13u8,125u8,151u8,152u8,248u8,97u8,75u8,27u8,119u8,94u8,102u8,164u8,12u8,236u8, - 225u8,6u8,142u8,188u8,141u8,227u8,249u8,243u8,213u8,255u8,0u8,128u8,189u8,145u8,221u8,155u8,40u8,0u8,0u8,0u8, - 0u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,177u8,17u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8, - 205u8,90u8,91u8,111u8,219u8,54u8,20u8,126u8,207u8,175u8,96u8,59u8,32u8,179u8,55u8,35u8,23u8,160u8,216u8,131u8, - 211u8,20u8,40u8,214u8,22u8,203u8,67u8,155u8,161u8,201u8,158u8,134u8,193u8,98u8,36u8,58u8,38u8,38u8,139u8,170u8, - 72u8,37u8,117u8,155u8,252u8,247u8,157u8,67u8,82u8,18u8,37u8,145u8,150u8,29u8,187u8,93u8,253u8,208u8,198u8,18u8, - 111u8,231u8,251u8,206u8,157u8,62u8,62u8,62u8,38u8,215u8,11u8,46u8,201u8,82u8,36u8,101u8,202u8,72u8,194u8,230u8, - 60u8,99u8,146u8,168u8,5u8,35u8,151u8,185u8,226u8,34u8,35u8,106u8,149u8,51u8,66u8,179u8,132u8,112u8,5u8,195u8, - 152u8,90u8,136u8,4u8,222u8,11u8,82u8,176u8,188u8,96u8,146u8,101u8,74u8,191u8,92u8,192u8,63u8,41u8,142u8,35u8, - 66u8,79u8,163u8,41u8,185u8,163u8,105u8,201u8,142u8,14u8,236u8,202u8,82u8,37u8,211u8,169u8,121u8,71u8,190u8,30u8, - 16u8,248u8,148u8,210u8,62u8,189u8,99u8,177u8,18u8,197u8,217u8,129u8,126u8,122u8,12u8,39u8,122u8,125u8,35u8,85u8, - 65u8,99u8,61u8,86u8,204u8,9u8,53u8,75u8,193u8,161u8,168u8,34u8,75u8,186u8,34u8,162u8,208u8,255u8,101u8,66u8, - 145u8,27u8,70u8,236u8,49u8,142u8,200u8,197u8,50u8,79u8,217u8,18u8,254u8,100u8,9u8,185u8,231u8,106u8,129u8,19u8, - 245u8,210u8,184u8,136u8,228u8,95u8,88u8,189u8,193u8,23u8,86u8,8u8,92u8,69u8,100u8,12u8,86u8,136u8,41u8,30u8, - 229u8,189u8,184u8,131u8,47u8,43u8,197u8,98u8,145u8,0u8,16u8,2u8,80u8,192u8,13u8,22u8,20u8,30u8,191u8,126u8, - 115u8,45u8,143u8,244u8,108u8,56u8,88u8,25u8,43u8,139u8,205u8,203u8,183u8,102u8,191u8,87u8,48u8,74u8,146u8,88u8, - 228u8,171u8,9u8,73u8,10u8,145u8,79u8,96u8,152u8,40u8,152u8,149u8,19u8,63u8,112u8,142u8,169u8,61u8,76u8,61u8, - 73u8,191u8,124u8,52u8,171u8,230u8,44u8,174u8,240u8,110u8,38u8,29u8,107u8,114u8,152u8,62u8,58u8,138u8,96u8,133u8, - 1u8,182u8,104u8,122u8,79u8,87u8,146u8,164u8,76u8,34u8,85u8,0u8,58u8,251u8,84u8,2u8,226u8,192u8,202u8,105u8, - 107u8,118u8,37u8,27u8,87u8,63u8,75u8,114u8,66u8,230u8,48u8,251u8,121u8,6u8,66u8,63u8,71u8,225u8,79u8,205u8, - 119u8,41u8,150u8,236u8,249u8,81u8,61u8,141u8,103u8,119u8,180u8,224u8,20u8,104u8,77u8,89u8,54u8,130u8,45u8,199u8, - 228u8,229u8,57u8,57u8,61u8,179u8,167u8,61u8,112u8,15u8,22u8,153u8,35u8,71u8,120u8,36u8,158u8,33u8,249u8,56u8, - 59u8,229u8,9u8,200u8,79u8,21u8,211u8,235u8,163u8,38u8,137u8,156u8,21u8,84u8,11u8,71u8,149u8,98u8,203u8,28u8, - 8u8,58u8,10u8,46u8,20u8,93u8,193u8,129u8,34u8,114u8,191u8,224u8,41u8,30u8,156u8,200u8,133u8,40u8,211u8,4u8, - 137u8,142u8,62u8,192u8,209u8,35u8,51u8,51u8,22u8,153u8,84u8,228u8,237u8,229u8,159u8,215u8,23u8,151u8,31u8,102u8, - 23u8,87u8,179u8,171u8,183u8,215u8,83u8,82u8,254u8,246u8,130u8,156u8,147u8,147u8,207u8,47u8,78u8,224u8,115u8,246u8, - 29u8,206u8,170u8,79u8,228u8,61u8,171u8,150u8,194u8,119u8,214u8,15u8,151u8,215u8,253u8,195u8,158u8,58u8,218u8,255u8, - 145u8,169u8,178u8,208u8,39u8,196u8,237u8,87u8,245u8,134u8,122u8,64u8,94u8,222u8,164u8,60u8,38u8,243u8,50u8,35u8, - 72u8,100u8,173u8,76u8,163u8,241u8,180u8,167u8,148u8,141u8,38u8,85u8,170u8,229u8,234u8,225u8,116u8,170u8,151u8,31u8, - 141u8,173u8,14u8,58u8,154u8,136u8,43u8,59u8,179u8,243u8,130u8,222u8,46u8,41u8,32u8,67u8,63u8,149u8,236u8,172u8, - 126u8,76u8,111u8,68u8,161u8,228u8,140u8,207u8,201u8,156u8,166u8,210u8,121u8,193u8,50u8,89u8,130u8,77u8,130u8,135u8, - 144u8,101u8,170u8,200u8,249u8,185u8,94u8,117u8,214u8,57u8,239u8,89u8,119u8,87u8,148u8,201u8,55u8,112u8,88u8,176u8, - 70u8,46u8,71u8,26u8,15u8,158u8,53u8,117u8,192u8,136u8,162u8,60u8,227u8,217u8,45u8,137u8,88u8,15u8,88u8,180u8, - 136u8,102u8,127u8,54u8,37u8,246u8,239u8,39u8,64u8,44u8,97u8,139u8,148u8,41u8,145u8,141u8,152u8,7u8,103u8,220u8, - 104u8,255u8,56u8,227u8,170u8,176u8,93u8,24u8,223u8,29u8,228u8,115u8,144u8,102u8,97u8,168u8,193u8,71u8,130u8,57u8, - 204u8,73u8,164u8,34u8,199u8,145u8,10u8,176u8,12u8,235u8,202u8,187u8,136u8,115u8,217u8,33u8,93u8,77u8,201u8,97u8, - 231u8,44u8,112u8,186u8,27u8,33u8,210u8,182u8,83u8,213u8,32u8,195u8,108u8,163u8,202u8,135u8,234u8,8u8,29u8,86u8, - 87u8,112u8,187u8,250u8,254u8,145u8,182u8,11u8,143u8,212u8,26u8,176u8,125u8,162u8,109u8,47u8,89u8,75u8,176u8,117u8, - 112u8,35u8,202u8,114u8,13u8,204u8,109u8,238u8,55u8,130u8,249u8,217u8,22u8,56u8,127u8,27u8,141u8,182u8,11u8,15u8, - 225u8,220u8,147u8,237u8,9u8,162u8,109u8,4u8,52u8,198u8,9u8,147u8,146u8,64u8,36u8,65u8,212u8,33u8,20u8,212u8, - 17u8,56u8,98u8,179u8,130u8,205u8,163u8,38u8,153u8,49u8,177u8,186u8,208u8,75u8,64u8,200u8,208u8,34u8,71u8,91u8, - 153u8,135u8,117u8,87u8,114u8,45u8,113u8,19u8,162u8,55u8,134u8,23u8,141u8,57u8,7u8,212u8,170u8,90u8,207u8,18u8, - 105u8,103u8,246u8,248u8,172u8,134u8,237u8,159u8,208u8,250u8,0u8,170u8,218u8,59u8,76u8,172u8,87u8,248u8,190u8,236u8, - 174u8,23u8,235u8,136u8,221u8,168u8,15u8,57u8,60u8,132u8,151u8,69u8,33u8,238u8,241u8,11u8,156u8,134u8,133u8,131u8, - 5u8,95u8,46u8,75u8,69u8,111u8,32u8,176u8,195u8,249u8,88u8,193u8,178u8,152u8,33u8,189u8,46u8,247u8,146u8,67u8, - 166u8,8u8,44u8,58u8,137u8,43u8,226u8,176u8,21u8,183u8,230u8,56u8,67u8,38u8,89u8,113u8,234u8,72u8,69u8,165u8, - 100u8,133u8,122u8,54u8,106u8,164u8,155u8,116u8,115u8,140u8,241u8,89u8,143u8,122u8,43u8,125u8,69u8,252u8,73u8,143u8, - 116u8,51u8,96u8,152u8,114u8,158u8,197u8,105u8,9u8,226u8,27u8,145u8,47u8,230u8,31u8,92u8,39u8,55u8,168u8,0u8, - 53u8,9u8,97u8,222u8,61u8,192u8,244u8,113u8,233u8,195u8,162u8,5u8,251u8,251u8,228u8,159u8,16u8,177u8,195u8,108u8, - 34u8,125u8,92u8,89u8,79u8,10u8,82u8,117u8,151u8,136u8,160u8,72u8,162u8,32u8,140u8,54u8,243u8,39u8,112u8,61u8, - 195u8,218u8,100u8,102u8,23u8,25u8,48u8,105u8,103u8,171u8,150u8,97u8,123u8,244u8,1u8,210u8,12u8,100u8,25u8,135u8, - 66u8,90u8,105u8,8u8,118u8,232u8,154u8,147u8,81u8,207u8,217u8,217u8,225u8,227u8,177u8,187u8,79u8,67u8,29u8,88u8, - 114u8,87u8,111u8,236u8,140u8,176u8,226u8,180u8,132u8,219u8,171u8,227u8,24u8,161u8,8u8,157u8,112u8,48u8,238u8,104u8, - 147u8,57u8,179u8,35u8,204u8,248u8,44u8,228u8,202u8,119u8,32u8,126u8,59u8,210u8,111u8,153u8,242u8,50u8,62u8,213u8, - 5u8,35u8,249u8,85u8,87u8,140u8,175u8,70u8,141u8,2u8,123u8,244u8,160u8,126u8,105u8,215u8,168u8,21u8,223u8,188u8, - 241u8,26u8,194u8,62u8,244u8,161u8,173u8,11u8,191u8,108u8,174u8,12u8,93u8,161u8,255u8,95u8,77u8,232u8,107u8,193u8, - 239u8,34u8,187u8,3u8,239u8,169u8,213u8,64u8,231u8,134u8,182u8,51u8,129u8,172u8,130u8,91u8,160u8,38u8,53u8,183u8, - 15u8,111u8,86u8,132u8,38u8,137u8,45u8,22u8,142u8,2u8,206u8,158u8,166u8,5u8,163u8,201u8,106u8,125u8,2u8,54u8, - 231u8,105u8,218u8,182u8,120u8,136u8,49u8,107u8,131u8,89u8,152u8,80u8,156u8,185u8,29u8,169u8,213u8,171u8,188u8,148u8, - 139u8,217u8,13u8,141u8,255u8,109u8,24u8,100u8,227u8,54u8,213u8,154u8,141u8,78u8,121u8,221u8,243u8,212u8,32u8,204u8, - 54u8,188u8,118u8,24u8,51u8,45u8,154u8,246u8,22u8,125u8,214u8,189u8,249u8,95u8,111u8,64u8,59u8,166u8,7u8,217u8, - 166u8,36u8,146u8,186u8,185u8,96u8,137u8,213u8,76u8,71u8,153u8,174u8,225u8,129u8,228u8,130u8,45u8,197u8,29u8,210u8, - 140u8,13u8,45u8,147u8,175u8,225u8,183u8,198u8,85u8,232u8,174u8,78u8,178u8,167u8,192u8,207u8,62u8,235u8,30u8,215u8, - 160u8,58u8,120u8,141u8,251u8,105u8,193u8,63u8,23u8,185u8,225u8,189u8,81u8,158u8,158u8,229u8,218u8,99u8,125u8,167u8, - 4u8,0u8,240u8,65u8,227u8,13u8,243u8,222u8,169u8,175u8,250u8,225u8,252u8,59u8,38u8,105u8,51u8,216u8,107u8,19u8, - 186u8,244u8,227u8,253u8,38u8,108u8,184u8,181u8,195u8,218u8,154u8,248u8,139u8,99u8,190u8,126u8,155u8,44u8,205u8,29u8, - 169u8,7u8,25u8,246u8,122u8,220u8,92u8,221u8,211u8,220u8,244u8,210u8,0u8,209u8,94u8,144u8,213u8,102u8,15u8,142u8, - 212u8,177u8,178u8,246u8,224u8,93u8,136u8,146u8,176u8,245u8,118u8,14u8,118u8,119u8,235u8,26u8,246u8,203u8,56u8,2u8, - 14u8,61u8,51u8,88u8,156u8,247u8,237u8,177u8,242u8,209u8,30u8,147u8,245u8,249u8,234u8,102u8,88u8,189u8,106u8,175u8, - 181u8,132u8,28u8,252u8,32u8,54u8,188u8,147u8,239u8,222u8,81u8,153u8,154u8,38u8,48u8,118u8,206u8,117u8,233u8,14u8, - 141u8,121u8,142u8,154u8,212u8,12u8,154u8,152u8,88u8,6u8,185u8,95u8,181u8,94u8,19u8,229u8,223u8,240u8,185u8,118u8, - 44u8,138u8,204u8,11u8,177u8,212u8,184u8,142u8,64u8,29u8,240u8,255u8,153u8,40u8,102u8,56u8,17u8,154u8,142u8,52u8, - 77u8,197u8,189u8,212u8,77u8,100u8,60u8,86u8,165u8,166u8,58u8,152u8,84u8,23u8,33u8,30u8,85u8,173u8,86u8,216u8, - 86u8,101u8,195u8,109u8,186u8,237u8,149u8,113u8,32u8,105u8,208u8,14u8,184u8,201u8,14u8,234u8,12u8,65u8,147u8,26u8, - 84u8,228u8,61u8,107u8,114u8,133u8,211u8,94u8,211u8,72u8,215u8,123u8,109u8,171u8,149u8,111u8,24u8,92u8,4u8,9u8, - 104u8,235u8,68u8,234u8,40u8,34u8,23u8,158u8,222u8,219u8,164u8,82u8,71u8,14u8,55u8,83u8,31u8,171u8,230u8,79u8, - 93u8,65u8,8u8,84u8,196u8,123u8,46u8,123u8,30u8,44u8,49u8,43u8,7u8,74u8,6u8,83u8,43u8,248u8,26u8,32u8, - 221u8,202u8,32u8,88u8,19u8,184u8,141u8,106u8,242u8,8u8,10u8,160u8,6u8,18u8,72u8,173u8,68u8,152u8,42u8,4u8, - 202u8,2u8,127u8,118u8,225u8,203u8,45u8,124u8,178u8,253u8,104u8,149u8,193u8,95u8,89u8,14u8,50u8,152u8,148u8,190u8, - 241u8,41u8,120u8,247u8,137u8,141u8,40u8,128u8,83u8,238u8,18u8,159u8,42u8,4u8,54u8,104u8,85u8,14u8,135u8,165u8, - 195u8,225u8,184u8,180u8,142u8,108u8,124u8,207u8,96u8,19u8,95u8,64u8,170u8,41u8,236u8,219u8,113u8,37u8,66u8,237u8, - 40u8,92u8,19u8,130u8,229u8,66u8,164u8,111u8,214u8,26u8,222u8,115u8,91u8,201u8,67u8,107u8,128u8,190u8,181u8,117u8, - 91u8,37u8,195u8,96u8,39u8,223u8,207u8,150u8,246u8,161u8,45u8,182u8,76u8,185u8,179u8,5u8,89u8,97u8,244u8,67u8, - 120u8,111u8,123u8,229u8,177u8,113u8,121u8,230u8,41u8,172u8,116u8,243u8,36u8,211u8,229u8,84u8,115u8,227u8,14u8,247u8, - 199u8,183u8,176u8,196u8,169u8,109u8,171u8,212u8,87u8,187u8,147u8,122u8,58u8,26u8,88u8,125u8,205u8,89u8,77u8,12u8, - 57u8,70u8,37u8,102u8,48u8,100u8,192u8,104u8,58u8,87u8,236u8,91u8,184u8,62u8,120u8,212u8,69u8,210u8,236u8,184u8, - 87u8,7u8,229u8,196u8,226u8,199u8,70u8,17u8,243u8,60u8,93u8,233u8,180u8,5u8,228u8,140u8,171u8,202u8,212u8,92u8, - 70u8,219u8,223u8,82u8,48u8,219u8,238u8,209u8,119u8,201u8,229u8,18u8,243u8,10u8,140u8,43u8,111u8,172u8,207u8,89u8, - 232u8,7u8,115u8,76u8,106u8,76u8,88u8,175u8,126u8,19u8,225u8,130u8,200u8,179u8,148u8,103u8,204u8,244u8,33u8,32u8, - 158u8,50u8,26u8,47u8,26u8,52u8,133u8,39u8,164u8,64u8,203u8,241u8,193u8,126u8,123u8,112u8,53u8,27u8,157u8,108u8, - 237u8,131u8,196u8,216u8,125u8,133u8,159u8,249u8,200u8,53u8,249u8,17u8,12u8,168u8,95u8,63u8,26u8,231u8,219u8,30u8, - 239u8,42u8,44u8,140u8,110u8,6u8,119u8,181u8,109u8,115u8,156u8,154u8,106u8,112u8,119u8,140u8,48u8,85u8,105u8,225u8, - 116u8,232u8,5u8,234u8,112u8,8u8,41u8,31u8,80u8,85u8,6u8,61u8,254u8,241u8,164u8,110u8,213u8,186u8,34u8,148u8, - 149u8,106u8,201u8,157u8,114u8,247u8,9u8,210u8,235u8,210u8,118u8,45u8,2u8,239u8,180u8,107u8,110u8,33u8,32u8,192u8, - 237u8,120u8,49u8,8u8,203u8,149u8,38u8,47u8,95u8,199u8,113u8,185u8,44u8,83u8,10u8,46u8,98u8,66u8,106u8,225u8, - 154u8,20u8,180u8,111u8,3u8,78u8,92u8,226u8,224u8,112u8,220u8,5u8,234u8,87u8,136u8,129u8,251u8,162u8,130u8,194u8, - 121u8,86u8,117u8,105u8,157u8,71u8,219u8,216u8,19u8,110u8,62u8,33u8,59u8,89u8,213u8,89u8,235u8,37u8,46u8,24u8, - 198u8,251u8,61u8,205u8,13u8,220u8,54u8,227u8,209u8,191u8,193u8,170u8,126u8,216u8,21u8,2u8,120u8,217u8,20u8,222u8, - 19u8,114u8,137u8,78u8,124u8,99u8,223u8,226u8,142u8,110u8,138u8,155u8,214u8,26u8,155u8,163u8,165u8,159u8,247u8,93u8, - 208u8,46u8,104u8,117u8,74u8,160u8,237u8,240u8,210u8,113u8,84u8,128u8,133u8,216u8,45u8,170u8,142u8,162u8,40u8,248u8, - 45u8,71u8,197u8,29u8,132u8,213u8,117u8,64u8,93u8,104u8,93u8,221u8,29u8,240u8,75u8,79u8,71u8,57u8,8u8,114u8, - 227u8,190u8,6u8,224u8,29u8,66u8,240u8,29u8,79u8,21u8,43u8,194u8,32u8,134u8,140u8,90u8,79u8,171u8,171u8,35u8, - 83u8,28u8,133u8,180u8,173u8,6u8,2u8,111u8,135u8,215u8,214u8,208u8,29u8,13u8,195u8,75u8,227u8,90u8,86u8,212u8, - 184u8,46u8,26u8,98u8,55u8,225u8,171u8,226u8,208u8,253u8,117u8,129u8,213u8,157u8,250u8,6u8,30u8,179u8,36u8,235u8, - 228u8,239u8,23u8,60u8,94u8,16u8,73u8,21u8,151u8,115u8,14u8,78u8,30u8,28u8,122u8,194u8,99u8,170u8,88u8,72u8, - 133u8,104u8,182u8,26u8,136u8,95u8,185u8,7u8,158u8,192u8,29u8,186u8,129u8,35u8,239u8,70u8,46u8,55u8,211u8,86u8, - 60u8,229u8,144u8,207u8,185u8,209u8,202u8,234u8,190u8,99u8,21u8,250u8,183u8,156u8,220u8,20u8,76u8,200u8,91u8,142u8, - 77u8,220u8,144u8,4u8,118u8,250u8,250u8,108u8,37u8,9u8,100u8,43u8,182u8,35u8,130u8,217u8,158u8,206u8,231u8,90u8, - 230u8,221u8,73u8,171u8,71u8,186u8,183u8,250u8,192u8,30u8,72u8,50u8,98u8,227u8,118u8,21u8,161u8,83u8,66u8,251u8, - 99u8,214u8,175u8,143u8,32u8,42u8,116u8,38u8,184u8,2u8,42u8,18u8,1u8,78u8,29u8,118u8,165u8,53u8,101u8,208u8, - 67u8,39u8,88u8,70u8,161u8,232u8,118u8,70u8,202u8,238u8,88u8,234u8,89u8,168u8,155u8,91u8,214u8,185u8,164u8,78u8, - 197u8,85u8,193u8,99u8,213u8,203u8,186u8,127u8,34u8,127u8,176u8,52u8,135u8,0u8,120u8,21u8,47u8,216u8,146u8,58u8, - 171u8,74u8,253u8,192u8,95u8,60u8,185u8,119u8,228u8,61u8,248u8,214u8,21u8,4u8,182u8,5u8,223u8,46u8,8u8,108u8, - 185u8,89u8,157u8,237u8,241u8,224u8,63u8,253u8,109u8,57u8,232u8,74u8,44u8,0u8,0u8,0u8,0u8,6u8,115u8,116u8, - 114u8,105u8,110u8,103u8,191u8,8u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,141u8,86u8,219u8,110u8, - 227u8,54u8,16u8,125u8,207u8,87u8,76u8,95u8,12u8,187u8,80u8,147u8,182u8,40u8,138u8,64u8,78u8,22u8,48u8,118u8, - 183u8,128u8,129u8,34u8,45u8,186u8,105u8,209u8,55u8,155u8,150u8,40u8,91u8,174u8,76u8,170u8,188u8,100u8,215u8,8u8, - 252u8,239u8,59u8,67u8,82u8,50u8,117u8,177u8,55u8,126u8,72u8,108u8,113u8,46u8,103u8,206u8,28u8,206u8,232u8,238u8, - 238u8,14u8,158u8,119u8,28u8,214u8,218u8,168u8,82u8,108u8,215u8,112u8,144u8,185u8,173u8,56u8,228u8,188u8,40u8,5u8, - 215u8,96u8,232u8,236u8,83u8,56u8,51u8,199u8,154u8,195u8,231u8,93u8,153u8,237u8,64u8,241u8,90u8,113u8,205u8,133u8, - 209u8,240u8,247u8,243u8,111u8,247u8,192u8,69u8,38u8,115u8,158u8,131u8,15u8,163u8,111u8,111u8,66u8,28u8,109u8,242u8, - 52u8,245u8,15u8,225u8,245u8,6u8,240u8,99u8,117u8,120u8,250u8,194u8,51u8,35u8,213u8,188u8,251u8,80u8,214u8,166u8, - 148u8,34u8,77u8,95u8,63u8,241u8,170u8,72u8,224u8,15u8,247u8,243u8,52u8,191u8,113u8,86u8,119u8,8u8,118u8,33u8, - 160u8,20u8,47u8,172u8,42u8,243u8,40u8,49u8,70u8,191u8,117u8,22u8,153u8,20u8,218u8,192u8,199u8,229u8,211u8,63u8, - 139u8,223u8,151u8,31u8,86u8,100u8,145u8,130u8,253u8,245u8,23u8,120u8,132u8,159u8,162u8,32u8,75u8,145u8,243u8,47u8, - 32u8,173u8,1u8,89u8,128u8,98u8,98u8,203u8,71u8,221u8,151u8,79u8,31u8,62u8,254u8,219u8,248u8,255u8,28u8,131u8, - 56u8,115u8,178u8,147u8,85u8,174u8,129u8,129u8,230u8,255u8,91u8,196u8,194u8,41u8,226u8,230u8,104u8,144u8,58u8,79u8, - 84u8,169u8,97u8,107u8,25u8,230u8,48u8,28u8,233u8,49u8,18u8,54u8,28u8,11u8,0u8,107u8,138u8,123u8,40u8,164u8, - 58u8,48u8,227u8,83u8,35u8,71u8,54u8,51u8,224u8,195u8,194u8,142u8,105u8,4u8,83u8,31u8,19u8,200u8,149u8,172u8, - 19u8,60u8,149u8,138u8,7u8,2u8,233u8,227u8,50u8,164u8,224u8,41u8,124u8,176u8,247u8,239u8,18u8,119u8,116u8,58u8, - 67u8,124u8,175u8,56u8,35u8,20u8,12u8,4u8,255u8,28u8,218u8,2u8,133u8,146u8,135u8,49u8,172u8,183u8,176u8,216u8, - 72u8,133u8,189u8,44u8,11u8,215u8,113u8,95u8,64u8,46u8,65u8,72u8,115u8,110u8,53u8,120u8,218u8,9u8,186u8,199u8, - 92u8,219u8,77u8,85u8,102u8,80u8,88u8,95u8,207u8,116u8,128u8,106u8,150u8,54u8,5u8,157u8,161u8,51u8,173u8,185u8, - 50u8,223u8,77u8,75u8,100u8,68u8,9u8,86u8,173u8,178u8,29u8,207u8,254u8,91u8,185u8,0u8,19u8,23u8,97u8,150u8, - 116u8,59u8,56u8,155u8,183u8,206u8,62u8,218u8,171u8,51u8,59u8,245u8,75u8,126u8,86u8,37u8,41u8,86u8,66u8,230u8, - 106u8,127u8,99u8,233u8,253u8,74u8,140u8,58u8,174u8,46u8,86u8,227u8,245u8,248u8,224u8,97u8,188u8,139u8,170u8,66u8, - 226u8,174u8,85u8,52u8,139u8,76u8,233u8,211u8,200u8,92u8,203u8,3u8,159u8,118u8,138u8,154u8,181u8,118u8,39u8,224u8, - 149u8,230u8,23u8,28u8,133u8,20u8,124u8,26u8,217u8,246u8,185u8,248u8,139u8,27u8,171u8,4u8,181u8,95u8,241u8,130u8, - 43u8,87u8,50u8,50u8,67u8,205u8,181u8,40u8,126u8,85u8,29u8,137u8,20u8,74u8,25u8,10u8,28u8,240u8,224u8,224u8, - 76u8,145u8,128u8,137u8,199u8,135u8,213u8,79u8,206u8,92u8,68u8,176u8,38u8,250u8,214u8,217u8,14u8,20u8,72u8,44u8, - 208u8,53u8,224u8,152u8,85u8,97u8,106u8,188u8,10u8,161u8,25u8,248u8,141u8,31u8,106u8,115u8,28u8,36u8,45u8,245u8, - 202u8,29u8,116u8,243u8,110u8,164u8,172u8,162u8,132u8,30u8,69u8,154u8,182u8,198u8,13u8,130u8,217u8,37u8,22u8,168u8, - 236u8,138u8,139u8,173u8,217u8,81u8,223u8,35u8,36u8,9u8,221u8,197u8,113u8,29u8,120u8,251u8,46u8,16u8,154u8,4u8, - 67u8,28u8,193u8,242u8,34u8,138u8,69u8,93u8,115u8,225u8,135u8,132u8,11u8,52u8,72u8,197u8,156u8,129u8,75u8,117u8, - 176u8,205u8,12u8,72u8,64u8,53u8,183u8,103u8,54u8,146u8,52u8,248u8,56u8,135u8,144u8,24u8,61u8,190u8,129u8,32u8, - 30u8,76u8,7u8,139u8,163u8,206u8,15u8,163u8,243u8,189u8,238u8,140u8,164u8,1u8,66u8,47u8,233u8,62u8,204u8,225u8, - 61u8,137u8,111u8,123u8,40u8,45u8,137u8,38u8,196u8,108u8,128u8,111u8,41u8,104u8,38u8,184u8,54u8,73u8,39u8,150u8, - 160u8,19u8,102u8,218u8,113u8,132u8,56u8,105u8,102u8,35u8,218u8,109u8,249u8,194u8,69u8,67u8,165u8,91u8,94u8,254u8, - 168u8,41u8,8u8,157u8,88u8,92u8,82u8,182u8,99u8,170u8,205u8,180u8,145u8,40u8,127u8,166u8,70u8,148u8,231u8,32u8, - 12u8,138u8,99u8,198u8,117u8,61u8,1u8,57u8,214u8,140u8,138u8,155u8,192u8,233u8,99u8,123u8,13u8,230u8,131u8,73u8, - 135u8,128u8,30u8,30u8,251u8,106u8,241u8,76u8,192u8,100u8,2u8,237u8,216u8,64u8,61u8,19u8,214u8,85u8,131u8,113u8, - 26u8,186u8,202u8,76u8,60u8,17u8,221u8,82u8,138u8,70u8,34u8,97u8,168u8,48u8,127u8,163u8,215u8,222u8,17u8,206u8, - 61u8,156u8,220u8,143u8,160u8,237u8,102u8,229u8,57u8,163u8,102u8,252u8,232u8,162u8,118u8,45u8,177u8,79u8,3u8,59u8, - 102u8,18u8,168u8,34u8,187u8,88u8,117u8,46u8,50u8,18u8,115u8,253u8,24u8,127u8,71u8,6u8,223u8,19u8,83u8,238u8, - 100u8,126u8,121u8,98u8,33u8,132u8,31u8,130u8,0u8,172u8,166u8,191u8,164u8,1u8,223u8,245u8,70u8,9u8,101u8,70u8, - 196u8,224u8,104u8,193u8,173u8,184u8,46u8,215u8,52u8,80u8,200u8,168u8,40u8,149u8,246u8,29u8,129u8,90u8,234u8,146u8, - 70u8,37u8,48u8,44u8,107u8,189u8,111u8,77u8,180u8,97u8,202u8,180u8,73u8,101u8,209u8,247u8,163u8,141u8,87u8,138u8, - 172u8,178u8,244u8,54u8,51u8,149u8,106u8,48u8,59u8,120u8,144u8,222u8,172u8,213u8,30u8,129u8,137u8,213u8,215u8,211u8, - 94u8,163u8,57u8,92u8,78u8,73u8,155u8,184u8,125u8,39u8,240u8,245u8,5u8,161u8,227u8,158u8,181u8,149u8,33u8,168u8, - 87u8,86u8,109u8,220u8,161u8,118u8,56u8,225u8,32u8,11u8,66u8,221u8,187u8,255u8,99u8,155u8,247u8,186u8,90u8,27u8, - 29u8,141u8,234u8,116u8,40u8,234u8,206u8,86u8,218u8,147u8,192u8,43u8,39u8,102u8,250u8,182u8,127u8,147u8,172u8,203u8, - 183u8,169u8,127u8,63u8,75u8,58u8,185u8,186u8,23u8,161u8,61u8,186u8,240u8,146u8,144u8,158u8,19u8,68u8,188u8,53u8, - 8u8,40u8,250u8,96u8,117u8,190u8,151u8,135u8,218u8,154u8,240u8,238u8,235u8,71u8,75u8,71u8,38u8,50u8,203u8,172u8, - 82u8,205u8,75u8,68u8,59u8,212u8,91u8,1u8,175u8,219u8,155u8,184u8,166u8,215u8,2u8,33u8,99u8,143u8,130u8,170u8, - 27u8,25u8,62u8,152u8,101u8,37u8,139u8,78u8,67u8,213u8,229u8,205u8,115u8,38u8,173u8,113u8,156u8,180u8,59u8,96u8, - 210u8,95u8,2u8,161u8,48u8,120u8,98u8,6u8,239u8,16u8,44u8,254u8,92u8,198u8,233u8,133u8,127u8,234u8,81u8,12u8, - 223u8,95u8,94u8,58u8,91u8,63u8,236u8,98u8,79u8,245u8,152u8,231u8,160u8,137u8,93u8,255u8,70u8,165u8,223u8,140u8, - 19u8,245u8,106u8,60u8,66u8,164u8,243u8,243u8,225u8,21u8,92u8,13u8,79u8,253u8,104u8,170u8,95u8,31u8,198u8,156u8, - 223u8,156u8,110u8,190u8,2u8,23u8,65u8,172u8,53u8,24u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8, - ]; - code::publish_package_txn(&framework_signer, chunk1, code) - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/1-aptos-stdlib.move b/aptos-move/aptos-release-builder/data/example_output/1-aptos-stdlib.move deleted file mode 100644 index 85385cf71264a..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/1-aptos-stdlib.move +++ /dev/null @@ -1,5983 +0,0 @@ -// Script hash: 034204de -// Framework commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Builder commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Upgrade proposal for package `AptosStdlib` - -// source digest: 1D4ED5929F42A72E62C7E3DF754A7EBB336DC734C24C5DFCC72E00B54CE886A0 -script { - use std::vector; - use aptos_framework::aptos_governance; - use aptos_framework::code; - - fun main(core_resources: &signer){ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let code = vector::empty(); - let chunk0 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,5u8,1u8,0u8,2u8,2u8,2u8,8u8,7u8,10u8,44u8,8u8, - 54u8,32u8,10u8,86u8,10u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,2u8,0u8,0u8,16u8,97u8,108u8,103u8,101u8, - 98u8,114u8,97u8,95u8,98u8,108u8,115u8,49u8,50u8,51u8,56u8,49u8,2u8,70u8,114u8,11u8,70u8,114u8,70u8,111u8, - 114u8,109u8,97u8,116u8,76u8,115u8,98u8,11u8,100u8,117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,1u8,3u8,1u8,1u8,2u8,1u8,3u8, - 1u8,0u8, - ]; - vector::push_back(&mut code, chunk0); - let chunk1 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,2u8,3u8,2u8,11u8,5u8,13u8,5u8,7u8, - 18u8,30u8,8u8,48u8,32u8,12u8,80u8,8u8,0u8,0u8,0u8,1u8,0u8,1u8,1u8,0u8,0u8,2u8,1u8,1u8, - 0u8,1u8,6u8,9u8,0u8,0u8,5u8,100u8,101u8,98u8,117u8,103u8,5u8,112u8,114u8,105u8,110u8,116u8,17u8,112u8, - 114u8,105u8,110u8,116u8,95u8,115u8,116u8,97u8,99u8,107u8,95u8,116u8,114u8,97u8,99u8,101u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,1u8,2u8,0u8,1u8,1u8,2u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk1); - let chunk2 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,2u8,2u8,2u8,4u8,3u8,6u8,60u8,5u8, - 66u8,30u8,7u8,96u8,166u8,1u8,8u8,134u8,2u8,32u8,6u8,166u8,2u8,84u8,16u8,250u8,2u8,158u8,3u8,10u8, - 152u8,6u8,5u8,12u8,157u8,6u8,163u8,4u8,13u8,192u8,10u8,2u8,0u8,0u8,0u8,1u8,7u8,0u8,0u8,2u8, - 0u8,1u8,0u8,0u8,3u8,2u8,0u8,0u8,0u8,4u8,1u8,0u8,0u8,0u8,5u8,1u8,0u8,0u8,0u8,6u8, - 3u8,1u8,0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,9u8,0u8,4u8,0u8,0u8,10u8, - 5u8,0u8,0u8,0u8,11u8,5u8,0u8,0u8,0u8,12u8,3u8,1u8,0u8,0u8,13u8,0u8,1u8,0u8,1u8,8u8, - 0u8,1u8,4u8,2u8,4u8,4u8,2u8,4u8,8u8,0u8,1u8,1u8,2u8,8u8,0u8,8u8,0u8,0u8,3u8,1u8, - 15u8,15u8,1u8,15u8,3u8,4u8,4u8,4u8,13u8,102u8,105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8, - 54u8,52u8,12u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,54u8,52u8,4u8,99u8,101u8,105u8,108u8, - 20u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8,97u8,116u8,105u8,111u8,110u8,97u8, - 108u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8,97u8,119u8,95u8,118u8,97u8, - 108u8,117u8,101u8,16u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,117u8,49u8,50u8,56u8, - 11u8,100u8,105u8,118u8,105u8,100u8,101u8,95u8,117u8,49u8,50u8,56u8,5u8,102u8,108u8,111u8,111u8,114u8,13u8,103u8, - 101u8,116u8,95u8,114u8,97u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,7u8,105u8,115u8,95u8,122u8,101u8,114u8,111u8, - 3u8,109u8,97u8,120u8,3u8,109u8,105u8,110u8,13u8,109u8,117u8,108u8,116u8,105u8,112u8,108u8,121u8,95u8,117u8,49u8, - 50u8,56u8,5u8,114u8,111u8,117u8,110u8,100u8,5u8,118u8,97u8,108u8,117u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8, - 2u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8, - 2u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,15u8,32u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8, - 116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,137u8,3u8,5u8,1u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8, - 12u8,69u8,68u8,69u8,78u8,79u8,77u8,73u8,78u8,65u8,84u8,79u8,82u8,33u8,84u8,104u8,101u8,32u8,100u8,101u8, - 110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,119u8, - 97u8,115u8,32u8,122u8,101u8,114u8,111u8,2u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,9u8,69u8,68u8,73u8,86u8, - 73u8,83u8,73u8,79u8,78u8,60u8,84u8,104u8,101u8,32u8,113u8,117u8,111u8,116u8,105u8,101u8,110u8,116u8,32u8,118u8, - 97u8,108u8,117u8,101u8,32u8,119u8,111u8,117u8,108u8,100u8,32u8,98u8,101u8,32u8,116u8,111u8,111u8,32u8,108u8,97u8, - 114u8,103u8,101u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,104u8,101u8,108u8,100u8,32u8,105u8,110u8,32u8,97u8,32u8, - 96u8,117u8,49u8,50u8,56u8,96u8,3u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,77u8,85u8,76u8,84u8, - 73u8,80u8,76u8,73u8,67u8,65u8,84u8,73u8,79u8,78u8,62u8,84u8,104u8,101u8,32u8,109u8,117u8,108u8,116u8,105u8, - 112u8,108u8,105u8,101u8,100u8,32u8,118u8,97u8,108u8,117u8,101u8,32u8,119u8,111u8,117u8,108u8,100u8,32u8,98u8,101u8, - 32u8,116u8,111u8,111u8,32u8,108u8,97u8,114u8,103u8,101u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,104u8,101u8,108u8, - 100u8,32u8,105u8,110u8,32u8,97u8,32u8,96u8,117u8,49u8,50u8,56u8,96u8,4u8,0u8,1u8,0u8,0u8,0u8,0u8, - 0u8,17u8,69u8,68u8,73u8,86u8,73u8,83u8,73u8,79u8,78u8,95u8,66u8,89u8,95u8,90u8,69u8,82u8,79u8,34u8, - 65u8,32u8,100u8,105u8,118u8,105u8,115u8,105u8,111u8,110u8,32u8,98u8,121u8,32u8,122u8,101u8,114u8,111u8,32u8,119u8, - 97u8,115u8,32u8,101u8,110u8,99u8,111u8,117u8,110u8,116u8,101u8,114u8,101u8,100u8,5u8,0u8,2u8,0u8,0u8,0u8, - 0u8,0u8,19u8,69u8,82u8,65u8,84u8,73u8,79u8,95u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,82u8,65u8,78u8, - 71u8,69u8,79u8,84u8,104u8,101u8,32u8,99u8,111u8,109u8,112u8,117u8,116u8,101u8,100u8,32u8,114u8,97u8,116u8,105u8, - 111u8,32u8,119u8,104u8,101u8,110u8,32u8,99u8,111u8,110u8,118u8,101u8,114u8,116u8,105u8,110u8,103u8,32u8,116u8,111u8, - 32u8,97u8,32u8,96u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,54u8,52u8,96u8,32u8,119u8,111u8, - 117u8,108u8,100u8,32u8,98u8,101u8,32u8,117u8,110u8,114u8,101u8,112u8,114u8,101u8,115u8,101u8,110u8,116u8,97u8,98u8, - 108u8,101u8,0u8,0u8,0u8,2u8,1u8,14u8,4u8,0u8,1u8,0u8,0u8,1u8,23u8,10u8,0u8,17u8,5u8,49u8, - 64u8,47u8,12u8,1u8,14u8,0u8,16u8,0u8,20u8,10u8,1u8,33u8,4u8,15u8,11u8,1u8,49u8,64u8,48u8,2u8, - 11u8,1u8,77u8,74u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,49u8,64u8,48u8, - 53u8,2u8,1u8,1u8,0u8,0u8,7u8,44u8,10u8,0u8,77u8,49u8,64u8,47u8,12u8,4u8,10u8,1u8,50u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,10u8,5u8,12u8, - 7u8,0u8,39u8,11u8,4u8,11u8,1u8,77u8,26u8,12u8,3u8,10u8,3u8,74u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,24u8,8u8,12u8,2u8,5u8,28u8,11u8,0u8,50u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8,2u8,11u8,2u8,4u8,31u8, - 5u8,33u8,7u8,4u8,39u8,10u8,3u8,7u8,5u8,37u8,4u8,38u8,5u8,40u8,7u8,4u8,39u8,11u8,3u8,53u8, - 18u8,0u8,2u8,2u8,1u8,0u8,0u8,6u8,3u8,11u8,0u8,18u8,0u8,2u8,3u8,1u8,0u8,0u8,8u8,16u8, - 11u8,0u8,77u8,49u8,64u8,47u8,12u8,1u8,10u8,1u8,7u8,5u8,37u8,4u8,10u8,5u8,12u8,7u8,4u8,39u8, - 11u8,1u8,53u8,18u8,0u8,2u8,4u8,1u8,0u8,0u8,8u8,29u8,14u8,1u8,16u8,0u8,20u8,50u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,7u8,5u8,9u8,7u8, - 2u8,39u8,11u8,0u8,77u8,49u8,64u8,47u8,14u8,1u8,16u8,0u8,20u8,77u8,26u8,12u8,2u8,10u8,2u8,7u8, - 5u8,37u8,4u8,24u8,5u8,26u8,7u8,1u8,39u8,11u8,2u8,53u8,2u8,5u8,1u8,0u8,0u8,6u8,6u8,14u8, - 0u8,16u8,0u8,20u8,49u8,64u8,48u8,2u8,6u8,1u8,0u8,0u8,6u8,4u8,14u8,0u8,16u8,0u8,20u8,2u8, - 7u8,1u8,0u8,0u8,6u8,6u8,14u8,0u8,16u8,0u8,20u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,2u8,8u8,1u8,0u8,0u8,0u8,15u8,14u8,0u8,16u8,0u8, - 20u8,14u8,1u8,16u8,0u8,20u8,36u8,4u8,11u8,11u8,0u8,12u8,2u8,5u8,13u8,11u8,1u8,12u8,2u8,11u8, - 2u8,2u8,9u8,1u8,0u8,0u8,0u8,15u8,14u8,0u8,16u8,0u8,20u8,14u8,1u8,16u8,0u8,20u8,35u8,4u8, - 11u8,11u8,0u8,12u8,2u8,5u8,13u8,11u8,1u8,12u8,2u8,11u8,2u8,2u8,10u8,1u8,0u8,0u8,8u8,20u8, - 11u8,0u8,77u8,14u8,1u8,16u8,0u8,20u8,77u8,24u8,49u8,64u8,48u8,12u8,2u8,10u8,2u8,7u8,5u8,37u8, - 4u8,15u8,5u8,17u8,7u8,3u8,39u8,11u8,2u8,53u8,2u8,11u8,1u8,0u8,0u8,9u8,25u8,10u8,0u8,17u8, - 5u8,49u8,64u8,47u8,12u8,3u8,10u8,3u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,128u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,14u8,0u8,16u8,0u8,20u8,11u8,2u8,35u8,4u8,20u8,11u8,3u8, - 49u8,64u8,48u8,12u8,1u8,5u8,23u8,11u8,0u8,17u8,0u8,12u8,1u8,11u8,1u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk2); - let chunk3 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,8u8,2u8,8u8,12u8,3u8,20u8,67u8,4u8, - 87u8,10u8,5u8,97u8,77u8,7u8,174u8,1u8,236u8,1u8,8u8,154u8,3u8,32u8,10u8,186u8,3u8,5u8,11u8,191u8, - 3u8,2u8,12u8,193u8,3u8,107u8,13u8,172u8,4u8,2u8,14u8,174u8,4u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8, - 0u8,3u8,0u8,4u8,3u8,1u8,0u8,1u8,3u8,8u8,7u8,1u8,0u8,0u8,0u8,5u8,0u8,0u8,0u8,0u8, - 6u8,1u8,2u8,1u8,0u8,0u8,7u8,3u8,4u8,1u8,0u8,0u8,9u8,5u8,6u8,2u8,0u8,0u8,0u8,10u8, - 5u8,7u8,2u8,0u8,0u8,0u8,11u8,8u8,9u8,2u8,0u8,0u8,0u8,12u8,4u8,9u8,2u8,0u8,0u8,2u8, - 14u8,0u8,10u8,0u8,1u8,15u8,4u8,4u8,0u8,3u8,16u8,11u8,14u8,1u8,0u8,3u8,17u8,0u8,14u8,1u8, - 0u8,2u8,11u8,4u8,13u8,9u8,2u8,10u8,2u8,6u8,13u8,0u8,2u8,6u8,11u8,0u8,1u8,9u8,0u8,6u8, - 11u8,0u8,1u8,9u8,0u8,1u8,11u8,0u8,1u8,9u8,0u8,2u8,3u8,3u8,1u8,3u8,1u8,6u8,10u8,2u8, - 1u8,11u8,1u8,1u8,11u8,0u8,1u8,9u8,0u8,2u8,1u8,3u8,1u8,6u8,11u8,0u8,1u8,9u8,0u8,1u8, - 10u8,2u8,1u8,1u8,1u8,9u8,0u8,2u8,11u8,1u8,1u8,11u8,0u8,1u8,9u8,0u8,3u8,2u8,9u8,0u8, - 9u8,1u8,1u8,11u8,1u8,1u8,9u8,0u8,7u8,97u8,108u8,103u8,101u8,98u8,114u8,97u8,5u8,101u8,114u8,114u8, - 111u8,114u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,7u8,69u8, - 108u8,101u8,109u8,101u8,110u8,116u8,49u8,97u8,98u8,111u8,114u8,116u8,95u8,117u8,110u8,108u8,101u8,115u8,115u8,95u8, - 99u8,114u8,121u8,112u8,116u8,111u8,103u8,114u8,97u8,112u8,104u8,121u8,95u8,97u8,108u8,103u8,101u8,98u8,114u8,97u8, - 95u8,110u8,97u8,116u8,105u8,118u8,101u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,3u8,97u8,100u8,100u8, - 12u8,97u8,100u8,100u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8, - 11u8,100u8,101u8,115u8,101u8,114u8,105u8,97u8,108u8,105u8,122u8,101u8,20u8,100u8,101u8,115u8,101u8,114u8,105u8,97u8, - 108u8,105u8,122u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,9u8,115u8,101u8,114u8,105u8,97u8,108u8, - 105u8,122u8,101u8,18u8,115u8,101u8,114u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8, - 97u8,108u8,6u8,104u8,97u8,110u8,100u8,108u8,101u8,28u8,99u8,114u8,121u8,112u8,116u8,111u8,103u8,114u8,97u8,112u8, - 104u8,121u8,95u8,97u8,108u8,103u8,101u8,98u8,114u8,97u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,15u8,110u8, - 111u8,116u8,95u8,105u8,109u8,112u8,108u8,101u8,109u8,101u8,110u8,116u8,101u8,100u8,4u8,115u8,111u8,109u8,101u8,4u8, - 110u8,111u8,110u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,1u8,13u8, - 3u8,0u8,11u8,0u8,0u8,0u8,0u8,0u8,6u8,17u8,7u8,4u8,3u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,17u8,8u8,39u8,1u8,1u8,0u8,0u8,0u8,10u8,17u8,0u8,11u8,0u8,55u8,0u8,20u8,11u8, - 1u8,55u8,0u8,20u8,56u8,0u8,57u8,0u8,2u8,2u8,0u8,2u8,0u8,3u8,1u8,0u8,0u8,12u8,14u8,17u8, - 0u8,11u8,0u8,56u8,1u8,12u8,2u8,4u8,10u8,11u8,2u8,57u8,0u8,56u8,2u8,12u8,1u8,5u8,12u8,56u8, - 3u8,12u8,1u8,11u8,1u8,2u8,4u8,0u8,2u8,0u8,5u8,1u8,0u8,0u8,0u8,6u8,17u8,0u8,11u8,0u8, - 55u8,0u8,20u8,56u8,4u8,2u8,6u8,0u8,2u8,0u8,0u8,0u8,0u8,11u8,0u8, - ]; - vector::push_back(&mut code, chunk3); - let chunk4 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,10u8,2u8,10u8,8u8,3u8,18u8,59u8,4u8, - 77u8,2u8,5u8,79u8,29u8,7u8,108u8,202u8,1u8,8u8,182u8,2u8,32u8,6u8,214u8,2u8,10u8,16u8,224u8,2u8, - 59u8,10u8,155u8,3u8,11u8,12u8,166u8,3u8,84u8,13u8,250u8,3u8,6u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8, - 3u8,0u8,4u8,0u8,5u8,7u8,0u8,4u8,12u8,7u8,0u8,0u8,6u8,0u8,1u8,0u8,0u8,7u8,2u8,3u8, - 0u8,0u8,8u8,2u8,3u8,0u8,0u8,9u8,0u8,4u8,0u8,0u8,10u8,5u8,6u8,1u8,0u8,0u8,11u8,0u8, - 4u8,0u8,0u8,13u8,2u8,7u8,1u8,0u8,0u8,14u8,2u8,8u8,1u8,0u8,3u8,15u8,2u8,9u8,0u8,2u8, - 16u8,6u8,6u8,0u8,1u8,17u8,5u8,4u8,1u8,0u8,10u8,10u8,1u8,6u8,8u8,0u8,1u8,5u8,0u8,1u8, - 2u8,1u8,10u8,2u8,1u8,6u8,9u8,0u8,1u8,3u8,1u8,8u8,1u8,1u8,8u8,0u8,1u8,1u8,1u8,9u8, - 0u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,3u8,98u8,99u8,115u8,5u8,101u8,114u8,114u8,111u8, - 114u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,8u8,84u8,121u8, - 112u8,101u8,73u8,110u8,102u8,111u8,15u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,8u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8,17u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8, - 95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,11u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,110u8,97u8,109u8, - 101u8,11u8,115u8,105u8,122u8,101u8,95u8,111u8,102u8,95u8,118u8,97u8,108u8,11u8,115u8,116u8,114u8,117u8,99u8,116u8, - 95u8,110u8,97u8,109u8,101u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,9u8,116u8,121u8,112u8,101u8,95u8,110u8,97u8, - 109u8,101u8,7u8,116u8,121u8,112u8,101u8,95u8,111u8,102u8,29u8,97u8,112u8,116u8,111u8,115u8,95u8,115u8,116u8,100u8, - 108u8,105u8,98u8,95u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8, - 13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,8u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,39u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,95u8,78u8,65u8,84u8, - 73u8,86u8,69u8,95u8,70u8,85u8,78u8,95u8,78u8,79u8,84u8,95u8,65u8,86u8,65u8,73u8,76u8,65u8,66u8,76u8, - 69u8,0u8,0u8,0u8,0u8,2u8,3u8,6u8,5u8,9u8,10u8,2u8,11u8,10u8,2u8,0u8,1u8,0u8,0u8,2u8, - 4u8,11u8,0u8,16u8,0u8,20u8,2u8,1u8,1u8,0u8,0u8,2u8,8u8,17u8,8u8,32u8,4u8,6u8,7u8,0u8, - 17u8,9u8,39u8,17u8,2u8,2u8,2u8,0u8,2u8,0u8,3u8,1u8,0u8,0u8,2u8,4u8,11u8,0u8,16u8,1u8, - 20u8,2u8,4u8,1u8,0u8,0u8,4u8,6u8,11u8,0u8,56u8,0u8,12u8,1u8,14u8,1u8,65u8,3u8,2u8,5u8, - 1u8,0u8,0u8,2u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,6u8,1u8,2u8,0u8,7u8,1u8,2u8,0u8,0u8, - 0u8,0u8,1u8,0u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk4); - let chunk5 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,4u8,2u8,4u8,4u8,3u8,8u8,66u8,4u8, - 74u8,20u8,5u8,94u8,34u8,7u8,128u8,1u8,158u8,1u8,8u8,158u8,2u8,32u8,6u8,190u8,2u8,10u8,16u8,200u8, - 2u8,98u8,12u8,170u8,3u8,131u8,1u8,15u8,173u8,4u8,4u8,0u8,2u8,0u8,3u8,1u8,8u8,7u8,0u8,0u8, - 4u8,0u8,1u8,1u8,0u8,0u8,5u8,0u8,2u8,0u8,0u8,6u8,0u8,3u8,0u8,0u8,7u8,0u8,0u8,0u8, - 0u8,9u8,0u8,4u8,0u8,0u8,10u8,0u8,5u8,0u8,0u8,11u8,0u8,6u8,0u8,0u8,12u8,0u8,7u8,0u8, - 0u8,13u8,0u8,8u8,0u8,0u8,14u8,0u8,9u8,0u8,0u8,15u8,0u8,10u8,0u8,1u8,16u8,12u8,13u8,0u8, - 1u8,17u8,13u8,3u8,0u8,0u8,2u8,0u8,3u8,0u8,0u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8, - 8u8,0u8,9u8,0u8,10u8,1u8,10u8,2u8,1u8,9u8,0u8,1u8,5u8,1u8,1u8,1u8,8u8,0u8,1u8,4u8, - 1u8,13u8,1u8,15u8,1u8,14u8,1u8,3u8,1u8,2u8,0u8,1u8,6u8,8u8,0u8,1u8,6u8,10u8,2u8,3u8, - 97u8,110u8,121u8,12u8,99u8,111u8,112u8,121u8,97u8,98u8,108u8,101u8,95u8,97u8,110u8,121u8,8u8,102u8,114u8,111u8, - 109u8,95u8,98u8,99u8,115u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,10u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8, - 116u8,101u8,115u8,10u8,116u8,111u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,7u8,116u8,111u8,95u8,98u8,111u8, - 111u8,108u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,9u8,116u8, - 111u8,95u8,115u8,116u8,114u8,105u8,110u8,103u8,7u8,116u8,111u8,95u8,117u8,49u8,50u8,56u8,6u8,116u8,111u8,95u8, - 117u8,49u8,54u8,7u8,116u8,111u8,95u8,117u8,50u8,53u8,54u8,6u8,116u8,111u8,95u8,117u8,51u8,50u8,6u8,116u8, - 111u8,95u8,117u8,54u8,52u8,5u8,116u8,111u8,95u8,117u8,56u8,5u8,98u8,121u8,116u8,101u8,115u8,19u8,105u8,110u8, - 116u8,101u8,114u8,110u8,97u8,108u8,95u8,99u8,104u8,101u8,99u8,107u8,95u8,117u8,116u8,102u8,56u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,78u8,1u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,85u8,84u8, - 70u8,56u8,52u8,85u8,84u8,70u8,56u8,32u8,99u8,104u8,101u8,99u8,107u8,32u8,102u8,97u8,105u8,108u8,101u8,100u8, - 32u8,105u8,110u8,32u8,99u8,111u8,110u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,32u8,102u8,114u8,111u8,109u8,32u8, - 98u8,121u8,116u8,101u8,115u8,32u8,116u8,111u8,32u8,115u8,116u8,114u8,105u8,110u8,103u8,0u8,0u8,0u8,3u8,2u8, - 0u8,1u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8,56u8,0u8,2u8,2u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8, - 56u8,1u8,2u8,3u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8,56u8,2u8,2u8,4u8,1u8,0u8,0u8,4u8,12u8, - 11u8,0u8,56u8,3u8,12u8,1u8,14u8,1u8,17u8,11u8,17u8,12u8,4u8,8u8,5u8,10u8,7u8,0u8,39u8,11u8, - 1u8,2u8,5u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8,56u8,4u8,2u8,6u8,1u8,0u8,0u8,11u8,3u8,11u8, - 0u8,56u8,5u8,2u8,7u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8,56u8,6u8,2u8,8u8,1u8,0u8,0u8,11u8, - 3u8,11u8,0u8,56u8,7u8,2u8,9u8,1u8,0u8,0u8,11u8,3u8,11u8,0u8,56u8,8u8,2u8,10u8,1u8,0u8, - 0u8,11u8,3u8,11u8,0u8,56u8,9u8,2u8,0u8,0u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk5); - let chunk6 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,12u8,2u8,12u8,8u8,3u8,20u8,40u8,4u8, - 60u8,6u8,5u8,66u8,27u8,7u8,93u8,128u8,1u8,8u8,221u8,1u8,32u8,6u8,253u8,1u8,10u8,16u8,135u8,2u8, - 118u8,10u8,253u8,2u8,9u8,12u8,134u8,3u8,57u8,13u8,191u8,3u8,4u8,15u8,195u8,3u8,2u8,0u8,1u8,0u8, - 2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,6u8,0u8,4u8,9u8,7u8,0u8,0u8,8u8,0u8, - 1u8,1u8,6u8,0u8,10u8,2u8,3u8,0u8,0u8,11u8,1u8,0u8,1u8,0u8,5u8,10u8,4u8,5u8,1u8,0u8, - 1u8,13u8,6u8,7u8,1u8,0u8,2u8,14u8,8u8,8u8,0u8,3u8,15u8,7u8,0u8,1u8,0u8,3u8,0u8,4u8, - 0u8,6u8,0u8,1u8,9u8,0u8,1u8,8u8,0u8,1u8,6u8,8u8,0u8,1u8,6u8,8u8,1u8,0u8,1u8,8u8, - 1u8,1u8,6u8,9u8,0u8,1u8,10u8,2u8,1u8,3u8,12u8,99u8,111u8,112u8,121u8,97u8,98u8,108u8,101u8,95u8, - 97u8,110u8,121u8,3u8,97u8,110u8,121u8,3u8,98u8,99u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,114u8, - 111u8,109u8,95u8,98u8,99u8,115u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8, - 110u8,102u8,111u8,3u8,65u8,110u8,121u8,4u8,112u8,97u8,99u8,107u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,9u8, - 116u8,121u8,112u8,101u8,95u8,110u8,97u8,109u8,101u8,6u8,117u8,110u8,112u8,97u8,99u8,107u8,4u8,100u8,97u8,116u8, - 97u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8, - 114u8,103u8,117u8,109u8,101u8,110u8,116u8,10u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,98u8, - 1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,84u8,89u8,80u8,69u8,95u8,77u8,73u8,83u8,77u8, - 65u8,84u8,67u8,72u8,71u8,84u8,104u8,101u8,32u8,116u8,121u8,112u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8, - 101u8,100u8,32u8,102u8,111u8,114u8,32u8,96u8,117u8,110u8,112u8,97u8,99u8,107u8,96u8,32u8,105u8,115u8,32u8,110u8, - 111u8,116u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8,109u8,101u8,32u8,97u8,115u8,32u8,119u8,97u8,115u8,32u8,103u8, - 105u8,118u8,101u8,110u8,32u8,102u8,111u8,114u8,32u8,96u8,112u8,97u8,99u8,107u8,96u8,46u8,0u8,0u8,0u8,2u8, - 2u8,10u8,8u8,1u8,12u8,10u8,2u8,0u8,1u8,0u8,0u8,4u8,5u8,56u8,0u8,14u8,0u8,56u8,1u8,18u8, - 0u8,2u8,1u8,1u8,0u8,0u8,4u8,3u8,11u8,0u8,16u8,0u8,2u8,2u8,1u8,0u8,0u8,4u8,15u8,56u8, - 0u8,14u8,0u8,16u8,0u8,20u8,33u8,4u8,7u8,5u8,10u8,7u8,0u8,17u8,5u8,39u8,14u8,0u8,16u8,1u8, - 20u8,56u8,2u8,2u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk6); - let chunk7 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,8u8,3u8,8u8,77u8,4u8,85u8,2u8,5u8, - 87u8,15u8,7u8,102u8,131u8,2u8,8u8,233u8,2u8,32u8,6u8,137u8,3u8,10u8,16u8,147u8,3u8,108u8,12u8,255u8, - 3u8,121u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,0u8,0u8,0u8,5u8,0u8,0u8,0u8, - 0u8,6u8,0u8,0u8,0u8,0u8,7u8,0u8,0u8,0u8,0u8,8u8,0u8,0u8,0u8,0u8,9u8,0u8,0u8,0u8, - 0u8,10u8,0u8,0u8,0u8,0u8,11u8,0u8,0u8,0u8,0u8,12u8,0u8,0u8,0u8,0u8,13u8,0u8,1u8,0u8, - 0u8,14u8,2u8,1u8,1u8,0u8,3u8,15u8,3u8,4u8,0u8,2u8,16u8,1u8,1u8,0u8,3u8,17u8,3u8,4u8, - 0u8,1u8,18u8,2u8,0u8,1u8,0u8,14u8,5u8,1u8,10u8,2u8,1u8,3u8,1u8,6u8,9u8,0u8,0u8,1u8, - 1u8,1u8,9u8,0u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,104u8,97u8,115u8,104u8,3u8,98u8,99u8,115u8,5u8, - 101u8,114u8,114u8,111u8,114u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,11u8,98u8,108u8,97u8,107u8,101u8, - 50u8,98u8,95u8,50u8,53u8,54u8,20u8,98u8,108u8,97u8,107u8,101u8,50u8,98u8,95u8,50u8,53u8,54u8,95u8,105u8, - 110u8,116u8,101u8,114u8,110u8,97u8,108u8,9u8,107u8,101u8,99u8,99u8,97u8,107u8,50u8,53u8,54u8,9u8,114u8,105u8, - 112u8,101u8,109u8,100u8,49u8,54u8,48u8,18u8,114u8,105u8,112u8,101u8,109u8,100u8,49u8,54u8,48u8,95u8,105u8,110u8, - 116u8,101u8,114u8,110u8,97u8,108u8,8u8,115u8,104u8,97u8,50u8,95u8,53u8,49u8,50u8,17u8,115u8,104u8,97u8,50u8, - 95u8,53u8,49u8,50u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,8u8,115u8,104u8,97u8,51u8,95u8,53u8, - 49u8,50u8,17u8,115u8,104u8,97u8,51u8,95u8,53u8,49u8,50u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8, - 8u8,115u8,105u8,112u8,95u8,104u8,97u8,115u8,104u8,19u8,115u8,105u8,112u8,95u8,104u8,97u8,115u8,104u8,95u8,102u8, - 114u8,111u8,109u8,95u8,118u8,97u8,108u8,117u8,101u8,19u8,98u8,108u8,97u8,107u8,101u8,50u8,98u8,95u8,50u8,53u8, - 54u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8, - 97u8,116u8,101u8,30u8,115u8,104u8,97u8,95u8,53u8,49u8,50u8,95u8,97u8,110u8,100u8,95u8,114u8,105u8,112u8,101u8, - 109u8,100u8,95u8,49u8,54u8,48u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,8u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,88u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,95u8,78u8,65u8,84u8, - 73u8,86u8,69u8,95u8,70u8,85u8,78u8,95u8,78u8,79u8,84u8,95u8,65u8,86u8,65u8,73u8,76u8,65u8,66u8,76u8, - 69u8,49u8,65u8,32u8,110u8,101u8,119u8,108u8,121u8,45u8,97u8,100u8,100u8,101u8,100u8,32u8,110u8,97u8,116u8,105u8, - 118u8,101u8,32u8,102u8,117u8,110u8,99u8,116u8,105u8,111u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,121u8, - 101u8,116u8,32u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,46u8,0u8,0u8,0u8,1u8,0u8,0u8,3u8,9u8,17u8, - 11u8,32u8,4u8,6u8,7u8,0u8,17u8,12u8,39u8,11u8,0u8,17u8,1u8,2u8,1u8,0u8,2u8,0u8,2u8,1u8, - 2u8,0u8,3u8,1u8,0u8,0u8,3u8,9u8,17u8,13u8,32u8,4u8,6u8,7u8,0u8,17u8,12u8,39u8,11u8,0u8, - 17u8,4u8,2u8,4u8,0u8,2u8,0u8,5u8,1u8,0u8,0u8,3u8,9u8,17u8,13u8,32u8,4u8,6u8,7u8,0u8, - 17u8,12u8,39u8,11u8,0u8,17u8,6u8,2u8,6u8,0u8,2u8,0u8,7u8,1u8,0u8,0u8,3u8,9u8,17u8,13u8, - 32u8,4u8,6u8,7u8,0u8,17u8,12u8,39u8,11u8,0u8,17u8,8u8,2u8,8u8,0u8,2u8,0u8,9u8,1u8,2u8, - 0u8,10u8,1u8,0u8,0u8,3u8,4u8,11u8,0u8,56u8,0u8,17u8,9u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk7); - let chunk8 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,2u8,2u8,2u8,14u8,3u8,16u8,133u8,1u8, - 4u8,149u8,1u8,24u8,5u8,173u8,1u8,142u8,1u8,7u8,187u8,2u8,130u8,2u8,8u8,189u8,4u8,32u8,10u8,221u8, - 4u8,11u8,11u8,232u8,4u8,4u8,12u8,236u8,4u8,250u8,1u8,13u8,230u8,6u8,2u8,14u8,232u8,6u8,2u8,15u8, - 234u8,6u8,2u8,0u8,1u8,0u8,2u8,14u8,1u8,0u8,0u8,0u8,3u8,4u8,2u8,3u8,1u8,0u8,1u8,0u8, - 4u8,0u8,1u8,2u8,3u8,0u8,0u8,5u8,2u8,1u8,3u8,3u8,0u8,0u8,0u8,6u8,3u8,4u8,2u8,3u8, - 0u8,0u8,7u8,3u8,5u8,3u8,3u8,0u8,0u8,0u8,8u8,6u8,7u8,3u8,3u8,0u8,0u8,0u8,9u8,6u8, - 8u8,2u8,3u8,0u8,0u8,10u8,0u8,8u8,2u8,3u8,2u8,0u8,11u8,9u8,4u8,2u8,3u8,0u8,0u8,12u8, - 3u8,10u8,2u8,3u8,0u8,0u8,13u8,3u8,10u8,3u8,3u8,0u8,0u8,0u8,14u8,11u8,1u8,2u8,3u8,0u8, - 0u8,15u8,12u8,1u8,3u8,3u8,0u8,0u8,0u8,16u8,11u8,1u8,3u8,3u8,0u8,0u8,0u8,17u8,1u8,11u8, - 2u8,3u8,4u8,0u8,18u8,1u8,13u8,2u8,0u8,0u8,0u8,19u8,6u8,14u8,2u8,3u8,0u8,0u8,20u8,6u8, - 15u8,3u8,3u8,0u8,0u8,0u8,21u8,0u8,1u8,2u8,3u8,2u8,1u8,16u8,3u8,16u8,4u8,16u8,8u8,18u8, - 0u8,18u8,5u8,18u8,2u8,18u8,9u8,16u8,11u8,16u8,12u8,16u8,14u8,18u8,16u8,16u8,3u8,7u8,11u8,1u8, - 2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,0u8,3u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8, - 0u8,11u8,0u8,1u8,9u8,1u8,2u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,6u8,9u8, - 1u8,1u8,6u8,11u8,0u8,1u8,9u8,1u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8, - 7u8,11u8,0u8,1u8,9u8,1u8,1u8,7u8,9u8,1u8,3u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8, - 0u8,6u8,9u8,1u8,1u8,1u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,1u8,6u8,11u8,1u8,2u8,9u8, - 0u8,9u8,1u8,1u8,5u8,1u8,9u8,1u8,1u8,11u8,0u8,1u8,9u8,1u8,3u8,9u8,0u8,9u8,1u8,11u8, - 0u8,1u8,9u8,1u8,1u8,9u8,0u8,2u8,9u8,0u8,9u8,1u8,2u8,9u8,0u8,7u8,9u8,1u8,17u8,116u8, - 97u8,98u8,108u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,108u8,101u8,110u8,103u8,116u8,104u8,5u8,116u8,97u8,98u8, - 108u8,101u8,3u8,66u8,111u8,120u8,5u8,84u8,97u8,98u8,108u8,101u8,3u8,97u8,100u8,100u8,7u8,97u8,100u8,100u8, - 95u8,98u8,111u8,120u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,98u8, - 111u8,120u8,14u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,98u8,111u8,120u8,95u8,109u8,117u8,116u8,10u8,98u8,111u8, - 114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,23u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,95u8, - 119u8,105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,19u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8, - 119u8,105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8, - 115u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,98u8,111u8,120u8,7u8,100u8,101u8,115u8,116u8,114u8, - 111u8,121u8,17u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8,121u8,95u8,98u8,111u8,120u8, - 18u8,100u8,114u8,111u8,112u8,95u8,117u8,110u8,99u8,104u8,101u8,99u8,107u8,101u8,100u8,95u8,98u8,111u8,120u8,3u8, - 110u8,101u8,119u8,16u8,110u8,101u8,119u8,95u8,116u8,97u8,98u8,108u8,101u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8, - 6u8,114u8,101u8,109u8,111u8,118u8,101u8,10u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8,98u8,111u8,120u8,6u8,117u8, - 112u8,115u8,101u8,114u8,116u8,3u8,118u8,97u8,108u8,6u8,104u8,97u8,110u8,100u8,108u8,101u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,1u8,22u8,9u8,0u8,1u8,2u8,1u8,23u8,5u8,0u8, - 14u8,1u8,18u8,0u8,1u8,0u8,0u8,1u8,6u8,11u8,0u8,11u8,1u8,11u8,2u8,57u8,0u8,56u8,0u8,2u8, - 1u8,0u8,2u8,0u8,2u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,11u8,1u8,56u8,1u8,55u8,0u8,2u8,3u8, - 0u8,2u8,0u8,4u8,0u8,2u8,0u8,5u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,11u8,1u8,56u8,2u8,54u8, - 0u8,2u8,6u8,1u8,0u8,0u8,17u8,16u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8,3u8,32u8, - 4u8,12u8,10u8,0u8,10u8,1u8,11u8,2u8,56u8,4u8,11u8,0u8,11u8,1u8,56u8,5u8,2u8,7u8,1u8,0u8, - 0u8,4u8,18u8,10u8,0u8,10u8,1u8,56u8,3u8,32u8,4u8,10u8,11u8,0u8,1u8,11u8,2u8,12u8,3u8,5u8, - 16u8,11u8,2u8,1u8,11u8,0u8,10u8,1u8,56u8,6u8,12u8,3u8,11u8,3u8,2u8,8u8,1u8,0u8,0u8,1u8, - 4u8,11u8,0u8,11u8,1u8,56u8,7u8,2u8,9u8,0u8,2u8,0u8,10u8,3u8,0u8,0u8,1u8,5u8,14u8,0u8, - 56u8,8u8,11u8,0u8,56u8,9u8,2u8,11u8,0u8,2u8,0u8,12u8,0u8,2u8,0u8,13u8,1u8,0u8,0u8,1u8, - 3u8,56u8,10u8,57u8,1u8,2u8,14u8,0u8,2u8,0u8,15u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,11u8,1u8, - 56u8,11u8,58u8,0u8,2u8,16u8,0u8,2u8,0u8,17u8,1u8,0u8,0u8,19u8,21u8,10u8,0u8,10u8,1u8,12u8, - 3u8,46u8,11u8,3u8,56u8,3u8,32u8,4u8,13u8,11u8,0u8,10u8,1u8,11u8,2u8,56u8,4u8,5u8,20u8,11u8, - 0u8,11u8,1u8,56u8,5u8,12u8,4u8,11u8,2u8,11u8,4u8,21u8,2u8,0u8,0u8,0u8,14u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk8); - let chunk9 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,6u8,2u8,6u8,16u8,3u8,22u8,131u8,1u8, - 4u8,153u8,1u8,16u8,5u8,169u8,1u8,116u8,7u8,157u8,2u8,180u8,1u8,8u8,209u8,3u8,32u8,6u8,241u8,3u8, - 30u8,16u8,143u8,4u8,88u8,10u8,231u8,4u8,13u8,11u8,244u8,4u8,2u8,12u8,246u8,4u8,199u8,2u8,13u8,189u8, - 7u8,4u8,14u8,193u8,7u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,4u8,2u8,3u8,1u8,0u8,1u8, - 2u8,16u8,4u8,2u8,3u8,1u8,0u8,1u8,0u8,4u8,0u8,1u8,2u8,3u8,0u8,0u8,5u8,2u8,3u8,2u8, - 3u8,0u8,0u8,6u8,4u8,5u8,2u8,3u8,0u8,0u8,7u8,0u8,5u8,2u8,3u8,2u8,0u8,8u8,2u8,6u8, - 2u8,3u8,0u8,0u8,9u8,7u8,1u8,2u8,3u8,0u8,0u8,10u8,8u8,6u8,2u8,3u8,0u8,0u8,11u8,8u8, - 9u8,2u8,3u8,0u8,0u8,12u8,1u8,7u8,2u8,3u8,4u8,0u8,13u8,4u8,10u8,2u8,3u8,0u8,0u8,14u8, - 0u8,1u8,2u8,3u8,2u8,2u8,4u8,12u8,1u8,2u8,3u8,0u8,2u8,5u8,13u8,3u8,2u8,3u8,0u8,2u8, - 6u8,14u8,5u8,2u8,3u8,0u8,2u8,8u8,13u8,6u8,2u8,3u8,0u8,1u8,17u8,9u8,9u8,0u8,2u8,18u8, - 15u8,1u8,2u8,3u8,0u8,2u8,12u8,1u8,15u8,2u8,3u8,4u8,2u8,13u8,14u8,10u8,2u8,3u8,0u8,11u8, - 11u8,12u8,11u8,13u8,11u8,14u8,11u8,16u8,11u8,17u8,11u8,18u8,11u8,0u8,11u8,3u8,7u8,11u8,0u8,2u8, - 9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,0u8,2u8,6u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8, - 1u8,6u8,9u8,1u8,2u8,7u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8,1u8,1u8, - 1u8,1u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,1u8,6u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,1u8,3u8, - 1u8,9u8,1u8,2u8,9u8,0u8,9u8,1u8,3u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8, - 1u8,2u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8, - 1u8,9u8,0u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,17u8,116u8,97u8,98u8,108u8,101u8,95u8,119u8,105u8, - 116u8,104u8,95u8,108u8,101u8,110u8,103u8,116u8,104u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,116u8,97u8,98u8,108u8, - 101u8,15u8,84u8,97u8,98u8,108u8,101u8,87u8,105u8,116u8,104u8,76u8,101u8,110u8,103u8,116u8,104u8,3u8,97u8,100u8, - 100u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,23u8, - 98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8, - 117u8,108u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8, - 95u8,101u8,109u8,112u8,116u8,121u8,5u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,3u8, - 110u8,101u8,119u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,6u8,117u8,112u8,115u8,101u8,114u8,116u8,5u8,105u8,110u8, - 110u8,101u8,114u8,5u8,84u8,97u8,98u8,108u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8, - 97u8,116u8,101u8,7u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,1u8,3u8,8u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,102u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8, - 58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,68u8,3u8,100u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,15u8,69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,0u8,101u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,0u8,102u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8,84u8,89u8,0u8,0u8, - 0u8,0u8,2u8,2u8,15u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,11u8,3u8,0u8,11u8,0u8,1u8,0u8,0u8, - 1u8,14u8,10u8,0u8,54u8,0u8,11u8,1u8,11u8,2u8,56u8,0u8,10u8,0u8,55u8,1u8,20u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,0u8,54u8,1u8,21u8,2u8,1u8,1u8,0u8,0u8,1u8,5u8,11u8, - 0u8,55u8,0u8,11u8,1u8,56u8,1u8,2u8,2u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,54u8,0u8,11u8,1u8, - 56u8,2u8,2u8,3u8,1u8,0u8,0u8,5u8,31u8,10u8,0u8,55u8,0u8,10u8,1u8,56u8,3u8,4u8,11u8,11u8, - 0u8,54u8,0u8,11u8,1u8,56u8,2u8,12u8,3u8,5u8,29u8,10u8,0u8,54u8,0u8,10u8,1u8,11u8,2u8,56u8, - 0u8,10u8,0u8,55u8,1u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,0u8,54u8,1u8, - 21u8,11u8,0u8,54u8,0u8,11u8,1u8,56u8,2u8,12u8,3u8,11u8,3u8,2u8,4u8,1u8,0u8,0u8,1u8,5u8, - 11u8,0u8,55u8,0u8,11u8,1u8,56u8,3u8,2u8,5u8,1u8,0u8,0u8,1u8,15u8,14u8,0u8,55u8,1u8,20u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,5u8,10u8,7u8,1u8,17u8,15u8,39u8,11u8, - 0u8,58u8,0u8,1u8,56u8,4u8,2u8,6u8,1u8,0u8,0u8,1u8,6u8,11u8,0u8,55u8,1u8,20u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,2u8,7u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,55u8,1u8,20u8, - 2u8,8u8,1u8,0u8,0u8,1u8,4u8,56u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,0u8, - 2u8,9u8,1u8,0u8,0u8,10u8,15u8,10u8,0u8,54u8,0u8,11u8,1u8,56u8,6u8,12u8,2u8,10u8,0u8,55u8, - 1u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,11u8,0u8,54u8,1u8,21u8,11u8,2u8,2u8, - 10u8,1u8,0u8,0u8,5u8,20u8,10u8,0u8,55u8,0u8,10u8,1u8,56u8,3u8,32u8,4u8,11u8,11u8,0u8,10u8, - 1u8,11u8,2u8,56u8,7u8,5u8,19u8,11u8,0u8,54u8,0u8,11u8,1u8,56u8,2u8,12u8,3u8,11u8,2u8,11u8, - 3u8,21u8,2u8,0u8,0u8,0u8,1u8,0u8,11u8,1u8,11u8,0u8, - ]; - vector::push_back(&mut code, chunk9); - let chunk10 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,15u8,1u8,0u8,8u8,2u8,8u8,14u8,3u8,22u8,185u8,1u8, - 4u8,207u8,1u8,40u8,5u8,247u8,1u8,143u8,2u8,7u8,134u8,4u8,162u8,2u8,8u8,168u8,6u8,32u8,6u8,200u8, - 6u8,40u8,16u8,240u8,6u8,252u8,1u8,10u8,236u8,8u8,15u8,11u8,251u8,8u8,2u8,12u8,253u8,8u8,208u8,12u8, - 13u8,205u8,21u8,6u8,14u8,211u8,21u8,6u8,15u8,217u8,21u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8, - 0u8,5u8,4u8,1u8,0u8,0u8,2u8,23u8,4u8,2u8,3u8,1u8,0u8,1u8,0u8,6u8,0u8,1u8,1u8,4u8, - 0u8,7u8,2u8,3u8,1u8,0u8,0u8,8u8,4u8,5u8,1u8,0u8,0u8,9u8,6u8,7u8,1u8,0u8,0u8,10u8, - 8u8,1u8,1u8,0u8,0u8,11u8,9u8,8u8,1u8,4u8,0u8,12u8,6u8,10u8,1u8,0u8,0u8,13u8,11u8,7u8, - 1u8,0u8,0u8,14u8,11u8,9u8,1u8,0u8,0u8,15u8,12u8,13u8,1u8,0u8,0u8,16u8,14u8,1u8,1u8,4u8, - 0u8,17u8,4u8,13u8,1u8,0u8,0u8,18u8,12u8,1u8,1u8,0u8,0u8,19u8,15u8,8u8,1u8,4u8,0u8,20u8, - 16u8,1u8,1u8,0u8,0u8,21u8,4u8,13u8,1u8,0u8,1u8,26u8,9u8,9u8,0u8,2u8,7u8,19u8,20u8,2u8, - 3u8,0u8,2u8,8u8,21u8,22u8,2u8,3u8,0u8,2u8,10u8,23u8,1u8,2u8,3u8,0u8,2u8,27u8,1u8,23u8, - 2u8,3u8,4u8,2u8,14u8,24u8,9u8,2u8,3u8,0u8,3u8,12u8,25u8,10u8,1u8,0u8,1u8,28u8,9u8,9u8, - 0u8,3u8,13u8,27u8,7u8,1u8,0u8,2u8,17u8,21u8,28u8,2u8,3u8,0u8,2u8,29u8,29u8,1u8,2u8,3u8, - 0u8,3u8,17u8,31u8,13u8,1u8,0u8,3u8,18u8,34u8,1u8,1u8,0u8,3u8,21u8,31u8,13u8,1u8,0u8,8u8, - 13u8,15u8,13u8,10u8,13u8,9u8,13u8,4u8,13u8,17u8,18u8,18u8,18u8,7u8,13u8,6u8,13u8,19u8,18u8,20u8, - 18u8,21u8,18u8,22u8,13u8,24u8,13u8,25u8,18u8,26u8,18u8,27u8,13u8,28u8,33u8,5u8,13u8,29u8,13u8,2u8, - 7u8,11u8,0u8,1u8,9u8,0u8,11u8,0u8,1u8,9u8,0u8,0u8,2u8,6u8,11u8,0u8,1u8,9u8,0u8,3u8, - 1u8,6u8,9u8,0u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,3u8,1u8,7u8,9u8,0u8,2u8,6u8,11u8,0u8, - 1u8,9u8,0u8,6u8,9u8,0u8,1u8,1u8,1u8,11u8,0u8,1u8,9u8,0u8,1u8,3u8,2u8,1u8,3u8,1u8, - 6u8,11u8,0u8,1u8,9u8,0u8,1u8,7u8,11u8,0u8,1u8,9u8,0u8,1u8,9u8,0u8,2u8,7u8,11u8,0u8, - 1u8,9u8,0u8,9u8,0u8,2u8,9u8,0u8,3u8,3u8,7u8,11u8,0u8,1u8,9u8,0u8,3u8,3u8,3u8,3u8, - 3u8,3u8,2u8,3u8,10u8,9u8,0u8,2u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,6u8, - 9u8,1u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8,1u8,1u8,11u8,1u8, - 2u8,9u8,0u8,9u8,1u8,1u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,2u8,6u8,10u8,9u8,0u8,6u8, - 9u8,0u8,3u8,7u8,10u8,9u8,0u8,3u8,9u8,0u8,1u8,6u8,10u8,9u8,0u8,1u8,9u8,1u8,3u8,7u8, - 11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,8u8,7u8,10u8,9u8,0u8,7u8,10u8,9u8,0u8, - 3u8,7u8,10u8,9u8,0u8,3u8,3u8,9u8,0u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8,3u8,8u8,3u8,3u8, - 10u8,10u8,9u8,0u8,3u8,3u8,10u8,9u8,0u8,3u8,10u8,9u8,0u8,1u8,10u8,9u8,0u8,1u8,7u8,10u8, - 9u8,0u8,11u8,1u8,10u8,9u8,0u8,10u8,9u8,0u8,9u8,0u8,9u8,0u8,3u8,3u8,3u8,3u8,3u8,3u8, - 4u8,7u8,10u8,9u8,0u8,3u8,9u8,0u8,9u8,0u8,12u8,115u8,109u8,97u8,114u8,116u8,95u8,118u8,101u8,99u8, - 116u8,111u8,114u8,10u8,98u8,105u8,103u8,95u8,118u8,101u8,99u8,116u8,111u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8, - 17u8,116u8,97u8,98u8,108u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,108u8,101u8,110u8,103u8,116u8,104u8,6u8,118u8, - 101u8,99u8,116u8,111u8,114u8,9u8,66u8,105u8,103u8,86u8,101u8,99u8,116u8,111u8,114u8,6u8,97u8,112u8,112u8,101u8, - 110u8,100u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8, - 8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8, - 112u8,116u8,121u8,5u8,101u8,109u8,112u8,116u8,121u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,8u8,105u8, - 115u8,95u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,8u8,112u8,111u8,112u8,95u8,98u8, - 97u8,99u8,107u8,9u8,112u8,117u8,115u8,104u8,95u8,98u8,97u8,99u8,107u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8, - 7u8,114u8,101u8,118u8,101u8,114u8,115u8,101u8,9u8,115u8,105u8,110u8,103u8,108u8,101u8,116u8,111u8,110u8,4u8,115u8, - 119u8,97u8,112u8,11u8,115u8,119u8,97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,7u8,98u8,117u8,99u8,107u8, - 101u8,116u8,115u8,15u8,84u8,97u8,98u8,108u8,101u8,87u8,105u8,116u8,104u8,76u8,101u8,110u8,103u8,116u8,104u8,9u8, - 101u8,110u8,100u8,95u8,105u8,110u8,100u8,101u8,120u8,11u8,98u8,117u8,99u8,107u8,101u8,116u8,95u8,115u8,105u8,122u8, - 101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,3u8,110u8, - 101u8,119u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,3u8,97u8,100u8,100u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,231u8,1u8,4u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,20u8,69u8,73u8,78u8,68u8,69u8,88u8,95u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,66u8,79u8,85u8, - 78u8,68u8,83u8,29u8,86u8,101u8,99u8,116u8,111u8,114u8,32u8,105u8,110u8,100u8,101u8,120u8,32u8,105u8,115u8,32u8, - 111u8,117u8,116u8,32u8,111u8,102u8,32u8,98u8,111u8,117u8,110u8,100u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,17u8,69u8,86u8,69u8,67u8,84u8,79u8,82u8,95u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8,84u8,89u8,33u8, - 67u8,97u8,110u8,110u8,111u8,116u8,32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,97u8,32u8,110u8,111u8,110u8, - 45u8,101u8,109u8,112u8,116u8,121u8,32u8,118u8,101u8,99u8,116u8,111u8,114u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,13u8,69u8,86u8,69u8,67u8,84u8,79u8,82u8,95u8,69u8,77u8,80u8,84u8,89u8,36u8,67u8,97u8,110u8,110u8, - 111u8,116u8,32u8,112u8,111u8,112u8,32u8,98u8,97u8,99u8,107u8,32u8,102u8,114u8,111u8,109u8,32u8,97u8,110u8,32u8, - 101u8,109u8,112u8,116u8,121u8,32u8,118u8,101u8,99u8,116u8,111u8,114u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 17u8,69u8,90u8,69u8,82u8,79u8,95u8,66u8,85u8,67u8,75u8,69u8,84u8,95u8,83u8,73u8,90u8,69u8,23u8,98u8, - 117u8,99u8,107u8,101u8,116u8,95u8,115u8,105u8,122u8,101u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8, - 32u8,48u8,0u8,0u8,0u8,2u8,3u8,22u8,11u8,1u8,2u8,3u8,10u8,9u8,0u8,24u8,3u8,25u8,3u8,0u8, - 13u8,0u8,1u8,0u8,0u8,17u8,43u8,14u8,1u8,56u8,0u8,12u8,4u8,10u8,4u8,6u8,2u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,26u8,12u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,3u8, - 10u8,2u8,35u8,4u8,24u8,5u8,14u8,10u8,0u8,13u8,1u8,10u8,3u8,56u8,1u8,56u8,2u8,11u8,3u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,9u8,10u8,3u8,10u8,4u8,35u8,4u8,38u8, - 5u8,29u8,10u8,0u8,13u8,1u8,56u8,3u8,56u8,2u8,11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,22u8,12u8,3u8,5u8,24u8,11u8,0u8,1u8,11u8,1u8,56u8,4u8,2u8,1u8,1u8,0u8,0u8,1u8,26u8, - 10u8,1u8,10u8,0u8,56u8,0u8,35u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,0u8,17u8,16u8,39u8,10u8, - 0u8,55u8,0u8,10u8,1u8,10u8,0u8,55u8,1u8,20u8,26u8,56u8,5u8,11u8,1u8,11u8,0u8,55u8,1u8,20u8, - 25u8,66u8,13u8,2u8,2u8,1u8,0u8,0u8,1u8,27u8,10u8,1u8,10u8,0u8,46u8,56u8,0u8,35u8,4u8,7u8, - 5u8,12u8,11u8,0u8,1u8,7u8,0u8,17u8,16u8,39u8,10u8,0u8,54u8,0u8,10u8,1u8,10u8,0u8,55u8,1u8, - 20u8,26u8,56u8,6u8,11u8,1u8,11u8,0u8,55u8,1u8,20u8,25u8,67u8,13u8,2u8,3u8,1u8,0u8,0u8,1u8, - 14u8,10u8,0u8,56u8,7u8,4u8,9u8,11u8,1u8,1u8,11u8,0u8,1u8,9u8,2u8,11u8,0u8,11u8,1u8,56u8, - 8u8,1u8,2u8,4u8,1u8,0u8,0u8,1u8,13u8,14u8,0u8,56u8,7u8,4u8,4u8,5u8,7u8,7u8,2u8,17u8, - 16u8,39u8,11u8,0u8,58u8,0u8,1u8,1u8,56u8,9u8,2u8,5u8,3u8,0u8,0u8,1u8,13u8,10u8,0u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,8u8,7u8,3u8,17u8,16u8,39u8,56u8,10u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,0u8,57u8,0u8,2u8,6u8,1u8,0u8,0u8,17u8,42u8, - 10u8,0u8,55u8,0u8,56u8,11u8,12u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8, - 2u8,10u8,4u8,35u8,4u8,35u8,5u8,11u8,10u8,0u8,55u8,0u8,10u8,2u8,56u8,5u8,10u8,1u8,56u8,12u8, - 12u8,3u8,4u8,30u8,11u8,1u8,1u8,8u8,11u8,2u8,11u8,0u8,55u8,1u8,20u8,24u8,11u8,3u8,22u8,2u8, - 11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,6u8,11u8,1u8,1u8,11u8, - 0u8,1u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,7u8,1u8,0u8,0u8,1u8,5u8,11u8, - 0u8,56u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,2u8,8u8,1u8,0u8,0u8,1u8,4u8, - 11u8,0u8,55u8,2u8,20u8,2u8,9u8,1u8,0u8,0u8,26u8,51u8,10u8,0u8,46u8,56u8,7u8,32u8,4u8,6u8, - 5u8,11u8,11u8,0u8,1u8,7u8,1u8,17u8,23u8,39u8,10u8,0u8,55u8,0u8,56u8,11u8,12u8,2u8,10u8,0u8, - 54u8,0u8,10u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,6u8,12u8,1u8,10u8,1u8, - 69u8,13u8,12u8,3u8,10u8,1u8,46u8,56u8,13u8,4u8,39u8,11u8,1u8,1u8,10u8,0u8,54u8,0u8,11u8,2u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,14u8,70u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,5u8,41u8,11u8,1u8,1u8,10u8,0u8,55u8,2u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,23u8,11u8,0u8,54u8,2u8,21u8,11u8,3u8,2u8,10u8,1u8,0u8,0u8,9u8,43u8,10u8,0u8,55u8,0u8, - 56u8,11u8,12u8,2u8,10u8,0u8,55u8,2u8,20u8,10u8,2u8,10u8,0u8,55u8,1u8,20u8,24u8,33u8,4u8,26u8, - 10u8,0u8,54u8,0u8,10u8,2u8,64u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,56u8,15u8,10u8,0u8, - 54u8,0u8,11u8,2u8,56u8,6u8,11u8,1u8,68u8,13u8,5u8,34u8,10u8,0u8,54u8,0u8,11u8,2u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,6u8,11u8,1u8,68u8,13u8,10u8,0u8,55u8,2u8,20u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,0u8,54u8,2u8,21u8,2u8,11u8,1u8,0u8,0u8,30u8, - 105u8,10u8,0u8,46u8,56u8,0u8,12u8,6u8,10u8,1u8,11u8,6u8,35u8,4u8,9u8,5u8,14u8,11u8,0u8,1u8, - 7u8,0u8,17u8,16u8,39u8,10u8,0u8,55u8,0u8,56u8,11u8,12u8,7u8,10u8,1u8,10u8,0u8,55u8,1u8,20u8, - 26u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,10u8,0u8,54u8,0u8,10u8,4u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,6u8,12u8,2u8,10u8,2u8,11u8,1u8,10u8,0u8,55u8, - 1u8,20u8,25u8,56u8,16u8,12u8,8u8,11u8,2u8,1u8,10u8,4u8,10u8,7u8,35u8,4u8,72u8,5u8,48u8,10u8, - 0u8,54u8,0u8,10u8,4u8,56u8,6u8,12u8,3u8,10u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 56u8,16u8,12u8,9u8,11u8,3u8,1u8,10u8,0u8,54u8,0u8,10u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,56u8,6u8,11u8,9u8,68u8,13u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,12u8,4u8,5u8,43u8,10u8,0u8,54u8,0u8,10u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 23u8,56u8,6u8,12u8,5u8,10u8,5u8,46u8,56u8,13u8,4u8,93u8,11u8,5u8,1u8,10u8,0u8,54u8,0u8,11u8, - 7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,14u8,70u8,13u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,5u8,95u8,11u8,5u8,1u8,10u8,0u8,55u8,2u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,11u8,0u8,54u8,2u8,21u8,11u8,8u8,2u8,12u8,1u8,0u8,0u8,32u8,107u8,64u8,33u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,64u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8, - 8u8,10u8,0u8,55u8,0u8,56u8,11u8,12u8,4u8,10u8,4u8,12u8,5u8,10u8,5u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,36u8,4u8,60u8,5u8,15u8,10u8,0u8,54u8,0u8,10u8,5u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,23u8,56u8,14u8,12u8,6u8,14u8,6u8,65u8,13u8,12u8,7u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,1u8,10u8,1u8,10u8,7u8,35u8,4u8,53u8,5u8,32u8,13u8,8u8,13u8,6u8,69u8, - 13u8,68u8,13u8,14u8,8u8,65u8,13u8,10u8,0u8,55u8,1u8,20u8,33u8,4u8,48u8,13u8,3u8,11u8,8u8,68u8, - 33u8,64u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,8u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,27u8,11u8,6u8,70u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,5u8,5u8,10u8,14u8,8u8,65u8, - 13u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,69u8,13u8,3u8,11u8,8u8,68u8,33u8,5u8, - 71u8,11u8,8u8,70u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,3u8,56u8,17u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8,0u8,55u8,0u8,56u8,11u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,33u8,4u8,82u8,5u8,86u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 39u8,10u8,2u8,10u8,4u8,35u8,4u8,102u8,5u8,91u8,10u8,0u8,54u8,0u8,10u8,2u8,13u8,3u8,69u8,33u8, - 56u8,15u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,86u8,11u8,0u8, - 1u8,11u8,3u8,70u8,33u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,13u8,3u8,0u8,0u8,8u8,8u8, - 11u8,1u8,56u8,18u8,12u8,2u8,13u8,2u8,11u8,0u8,56u8,2u8,11u8,2u8,2u8,14u8,1u8,0u8,0u8,35u8, - 112u8,10u8,1u8,10u8,0u8,46u8,56u8,0u8,35u8,4u8,13u8,10u8,2u8,10u8,0u8,46u8,56u8,0u8,35u8,12u8, - 3u8,5u8,15u8,9u8,12u8,3u8,11u8,3u8,4u8,18u8,5u8,23u8,11u8,0u8,1u8,7u8,0u8,17u8,16u8,39u8, - 10u8,1u8,10u8,0u8,55u8,1u8,20u8,26u8,12u8,8u8,10u8,2u8,10u8,0u8,55u8,1u8,20u8,26u8,12u8,10u8, - 11u8,1u8,10u8,0u8,55u8,1u8,20u8,25u8,12u8,9u8,11u8,2u8,10u8,0u8,55u8,1u8,20u8,25u8,12u8,11u8, - 10u8,8u8,10u8,10u8,33u8,4u8,59u8,11u8,0u8,54u8,0u8,11u8,8u8,56u8,6u8,11u8,9u8,11u8,11u8,71u8, - 13u8,2u8,10u8,0u8,54u8,0u8,10u8,8u8,56u8,14u8,12u8,4u8,10u8,0u8,54u8,0u8,10u8,10u8,56u8,14u8, - 12u8,5u8,13u8,4u8,10u8,9u8,56u8,19u8,12u8,6u8,13u8,5u8,10u8,11u8,56u8,19u8,12u8,7u8,13u8,4u8, - 11u8,7u8,68u8,13u8,13u8,5u8,11u8,6u8,68u8,13u8,14u8,4u8,65u8,13u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,23u8,12u8,12u8,14u8,5u8,65u8,13u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8, - 12u8,13u8,13u8,4u8,11u8,9u8,11u8,12u8,71u8,13u8,13u8,5u8,11u8,11u8,11u8,13u8,71u8,13u8,10u8,0u8, - 54u8,0u8,11u8,8u8,11u8,4u8,56u8,15u8,11u8,0u8,54u8,0u8,11u8,10u8,11u8,5u8,56u8,15u8,2u8,15u8, - 1u8,0u8,0u8,36u8,61u8,10u8,1u8,10u8,0u8,46u8,56u8,0u8,35u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8, - 7u8,0u8,17u8,16u8,39u8,10u8,0u8,56u8,3u8,12u8,4u8,10u8,0u8,55u8,2u8,20u8,10u8,1u8,33u8,4u8, - 25u8,11u8,0u8,1u8,11u8,4u8,2u8,10u8,0u8,54u8,0u8,10u8,1u8,10u8,0u8,55u8,1u8,20u8,26u8,56u8, - 6u8,12u8,2u8,10u8,2u8,46u8,65u8,13u8,12u8,3u8,10u8,2u8,10u8,1u8,10u8,0u8,55u8,1u8,20u8,25u8, - 56u8,19u8,12u8,5u8,10u8,2u8,11u8,4u8,68u8,13u8,11u8,2u8,11u8,1u8,11u8,0u8,55u8,1u8,20u8,25u8, - 11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,71u8,13u8,11u8,5u8,2u8,0u8,0u8,0u8, - 2u8,0u8,1u8,0u8,13u8,1u8,13u8,2u8,13u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk10); - let chunk11 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,30u8,3u8,36u8,162u8,1u8, - 4u8,198u8,1u8,12u8,5u8,210u8,1u8,164u8,1u8,7u8,246u8,2u8,201u8,7u8,8u8,191u8,10u8,32u8,6u8,223u8, - 10u8,202u8,1u8,16u8,169u8,12u8,158u8,2u8,10u8,199u8,14u8,36u8,12u8,235u8,14u8,152u8,3u8,13u8,131u8,18u8, - 12u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,7u8,0u8,0u8,4u8,7u8,0u8,0u8,5u8,7u8,0u8,0u8, - 6u8,7u8,0u8,0u8,7u8,7u8,0u8,0u8,8u8,7u8,0u8,2u8,15u8,7u8,1u8,0u8,0u8,0u8,9u8,0u8, - 1u8,0u8,0u8,10u8,2u8,3u8,0u8,0u8,11u8,2u8,0u8,0u8,0u8,12u8,4u8,0u8,0u8,0u8,13u8,5u8, - 6u8,0u8,0u8,14u8,5u8,7u8,0u8,0u8,16u8,8u8,9u8,0u8,0u8,17u8,8u8,7u8,0u8,0u8,18u8,0u8, - 10u8,0u8,0u8,19u8,11u8,0u8,0u8,0u8,20u8,0u8,12u8,0u8,0u8,21u8,13u8,14u8,0u8,0u8,22u8,15u8, - 0u8,0u8,0u8,23u8,16u8,0u8,0u8,0u8,24u8,16u8,17u8,0u8,0u8,25u8,0u8,18u8,0u8,0u8,26u8,19u8, - 3u8,0u8,0u8,27u8,0u8,3u8,0u8,0u8,28u8,19u8,0u8,0u8,0u8,29u8,0u8,3u8,0u8,0u8,30u8,20u8, - 3u8,0u8,0u8,31u8,21u8,3u8,0u8,0u8,32u8,22u8,3u8,0u8,0u8,33u8,23u8,3u8,0u8,0u8,34u8,24u8, - 3u8,0u8,0u8,35u8,23u8,3u8,0u8,0u8,36u8,25u8,3u8,0u8,0u8,37u8,26u8,3u8,0u8,0u8,38u8,23u8, - 3u8,0u8,1u8,40u8,29u8,29u8,0u8,2u8,41u8,31u8,32u8,1u8,0u8,2u8,42u8,27u8,32u8,1u8,0u8,30u8, - 1u8,31u8,1u8,30u8,17u8,31u8,17u8,30u8,33u8,31u8,33u8,1u8,10u8,2u8,1u8,8u8,0u8,1u8,6u8,8u8, - 0u8,1u8,1u8,1u8,6u8,8u8,1u8,1u8,10u8,8u8,4u8,1u8,8u8,1u8,2u8,10u8,2u8,1u8,1u8,10u8, - 8u8,5u8,1u8,11u8,6u8,1u8,8u8,0u8,1u8,8u8,2u8,1u8,6u8,8u8,2u8,1u8,11u8,6u8,1u8,8u8, - 3u8,2u8,10u8,2u8,6u8,8u8,2u8,1u8,11u8,6u8,1u8,8u8,4u8,1u8,6u8,8u8,3u8,1u8,6u8,8u8, - 4u8,1u8,8u8,3u8,1u8,8u8,5u8,1u8,6u8,8u8,5u8,3u8,6u8,8u8,0u8,10u8,8u8,4u8,10u8,10u8, - 2u8,3u8,10u8,2u8,10u8,8u8,4u8,10u8,10u8,2u8,3u8,6u8,8u8,0u8,6u8,8u8,1u8,10u8,2u8,3u8, - 10u8,2u8,10u8,2u8,10u8,2u8,3u8,6u8,8u8,5u8,6u8,8u8,3u8,10u8,2u8,2u8,10u8,2u8,10u8,2u8, - 3u8,6u8,8u8,5u8,6u8,8u8,4u8,10u8,2u8,0u8,1u8,2u8,1u8,3u8,3u8,11u8,6u8,1u8,8u8,0u8, - 10u8,2u8,1u8,1u8,9u8,0u8,1u8,11u8,6u8,1u8,9u8,0u8,1u8,8u8,4u8,8u8,98u8,108u8,115u8,49u8, - 50u8,51u8,56u8,49u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,20u8,65u8,103u8, - 103u8,114u8,79u8,114u8,77u8,117u8,108u8,116u8,105u8,83u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,21u8,65u8, - 103u8,103u8,114u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,115u8,87u8,105u8,116u8,104u8,80u8,111u8,80u8, - 17u8,80u8,114u8,111u8,111u8,102u8,79u8,102u8,80u8,111u8,115u8,115u8,101u8,115u8,115u8,105u8,111u8,110u8,9u8,80u8, - 117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,16u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,87u8,105u8, - 116u8,104u8,80u8,111u8,80u8,9u8,83u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,34u8,97u8,103u8,103u8,114u8, - 95u8,111u8,114u8,95u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8, - 102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,38u8,97u8,103u8,103u8,114u8,95u8,111u8,114u8,95u8,109u8, - 117u8,108u8,116u8,105u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,115u8,117u8,98u8,103u8,114u8, - 111u8,117u8,112u8,95u8,99u8,104u8,101u8,99u8,107u8,32u8,97u8,103u8,103u8,114u8,95u8,111u8,114u8,95u8,109u8,117u8, - 108u8,116u8,105u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8, - 101u8,115u8,25u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,112u8,117u8,98u8,107u8,101u8,121u8,95u8, - 116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,17u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,112u8, - 117u8,98u8,107u8,101u8,121u8,115u8,26u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,112u8,117u8,98u8, - 107u8,101u8,121u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8, - 20u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8, - 115u8,29u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8, - 101u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,30u8,112u8,114u8,111u8,111u8,102u8,95u8,111u8,102u8, - 95u8,112u8,111u8,115u8,115u8,101u8,115u8,115u8,105u8,111u8,110u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8, - 101u8,115u8,28u8,112u8,114u8,111u8,111u8,102u8,95u8,111u8,102u8,95u8,112u8,111u8,115u8,115u8,101u8,115u8,115u8,105u8, - 111u8,110u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,21u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8, - 101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,30u8,112u8,117u8,98u8,108u8,105u8,99u8, - 95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,95u8,119u8,105u8,116u8,104u8, - 95u8,112u8,111u8,112u8,19u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8,98u8, - 121u8,116u8,101u8,115u8,28u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,119u8,105u8,116u8,104u8, - 95u8,112u8,111u8,112u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,29u8,112u8,117u8,98u8,108u8,105u8,99u8, - 95u8,107u8,101u8,121u8,95u8,119u8,105u8,116u8,104u8,95u8,112u8,111u8,112u8,95u8,116u8,111u8,95u8,110u8,111u8,114u8, - 109u8,97u8,108u8,20u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8, - 121u8,116u8,101u8,115u8,24u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,115u8,117u8,98u8,103u8,114u8, - 111u8,117u8,112u8,95u8,99u8,104u8,101u8,99u8,107u8,33u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8, - 115u8,117u8,98u8,103u8,114u8,111u8,117u8,112u8,95u8,99u8,104u8,101u8,99u8,107u8,95u8,105u8,110u8,116u8,101u8,114u8, - 110u8,97u8,108u8,18u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8, - 101u8,115u8,24u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,112u8,117u8,98u8,107u8,101u8,121u8,95u8,105u8, - 110u8,116u8,101u8,114u8,110u8,97u8,108u8,26u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,97u8,103u8,103u8,114u8,101u8, - 103u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,35u8,118u8,101u8,114u8,105u8,102u8, - 121u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8, - 101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,21u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,109u8,117u8, - 108u8,116u8,105u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,30u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8, - 109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8, - 110u8,97u8,108u8,23u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,110u8,111u8,114u8,109u8,97u8,108u8,95u8,115u8,105u8, - 103u8,110u8,97u8,116u8,117u8,114u8,101u8,32u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,110u8,111u8,114u8,109u8,97u8, - 108u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8, - 35u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,112u8,114u8,111u8,111u8,102u8,95u8,111u8,102u8,95u8,112u8,111u8,115u8, - 115u8,101u8,115u8,115u8,105u8,111u8,110u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,22u8,118u8,101u8,114u8, - 105u8,102u8,121u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,31u8, - 118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,115u8,104u8,97u8, - 114u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,5u8,98u8,121u8,116u8,101u8,115u8,16u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,4u8,115u8,111u8,109u8,101u8,4u8, - 110u8,111u8,110u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,48u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,49u8,48u8, - 138u8,83u8,231u8,174u8,82u8,112u8,227u8,231u8,101u8,205u8,138u8,64u8,50u8,194u8,231u8,124u8,111u8,126u8,135u8,164u8, - 78u8,187u8,133u8,191u8,40u8,164u8,215u8,134u8,85u8,101u8,105u8,143u8,151u8,83u8,70u8,113u8,66u8,98u8,249u8,228u8, - 124u8,111u8,62u8,13u8,93u8,149u8,22u8,96u8,10u8,2u8,97u8,96u8,160u8,26u8,101u8,133u8,79u8,152u8,125u8,52u8, - 52u8,20u8,155u8,127u8,8u8,247u8,7u8,48u8,227u8,11u8,36u8,25u8,132u8,232u8,113u8,43u8,194u8,172u8,168u8,133u8, - 214u8,50u8,170u8,252u8,237u8,76u8,63u8,102u8,18u8,9u8,222u8,187u8,107u8,28u8,134u8,1u8,50u8,102u8,35u8,204u8, - 22u8,202u8,47u8,108u8,158u8,220u8,83u8,183u8,184u8,139u8,116u8,53u8,251u8,107u8,5u8,221u8,236u8,228u8,24u8,210u8, - 195u8,77u8,198u8,172u8,162u8,245u8,161u8,26u8,121u8,230u8,119u8,116u8,88u8,44u8,20u8,8u8,74u8,1u8,220u8,183u8, - 130u8,14u8,76u8,180u8,186u8,208u8,234u8,141u8,3u8,8u8,96u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8, - 112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,137u8,2u8,3u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,90u8,69u8,82u8,79u8,95u8,80u8,85u8,66u8,75u8,69u8, - 89u8,83u8,57u8,84u8,104u8,101u8,32u8,99u8,97u8,108u8,108u8,101u8,114u8,32u8,119u8,97u8,115u8,32u8,115u8,117u8, - 112u8,112u8,111u8,115u8,101u8,100u8,32u8,116u8,111u8,32u8,105u8,110u8,112u8,117u8,116u8,32u8,111u8,110u8,101u8,32u8, - 111u8,114u8,32u8,109u8,111u8,114u8,101u8,32u8,112u8,117u8,98u8,108u8,105u8,99u8,32u8,107u8,101u8,121u8,115u8,46u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,69u8,87u8,82u8,79u8,78u8,71u8,95u8,83u8,73u8,90u8,69u8, - 44u8,79u8,110u8,101u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,103u8,105u8,118u8,101u8,110u8,32u8,105u8,110u8, - 112u8,117u8,116u8,115u8,32u8,104u8,97u8,115u8,32u8,116u8,104u8,101u8,32u8,119u8,114u8,111u8,110u8,103u8,32u8,115u8, - 105u8,122u8,101u8,46u8,115u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,95u8,78u8,85u8,77u8,95u8, - 83u8,73u8,71u8,78u8,69u8,82u8,83u8,95u8,77u8,85u8,83u8,84u8,95u8,69u8,81u8,95u8,78u8,85u8,77u8,95u8, - 77u8,69u8,83u8,83u8,65u8,71u8,69u8,83u8,73u8,84u8,104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8, - 111u8,102u8,32u8,115u8,105u8,103u8,110u8,101u8,114u8,115u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8, - 109u8,97u8,116u8,99u8,104u8,32u8,116u8,104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8, - 109u8,101u8,115u8,115u8,97u8,103u8,101u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,115u8,105u8,103u8,110u8,101u8, - 100u8,46u8,0u8,0u8,0u8,2u8,1u8,39u8,10u8,2u8,1u8,2u8,1u8,39u8,10u8,2u8,2u8,2u8,1u8,39u8, - 10u8,2u8,3u8,2u8,1u8,39u8,10u8,2u8,4u8,2u8,1u8,39u8,10u8,2u8,5u8,2u8,1u8,39u8,10u8,2u8, - 0u8,1u8,0u8,0u8,27u8,12u8,14u8,0u8,65u8,28u8,7u8,6u8,33u8,4u8,6u8,5u8,9u8,7u8,0u8,17u8, - 29u8,39u8,11u8,0u8,18u8,0u8,2u8,1u8,1u8,0u8,0u8,27u8,5u8,11u8,0u8,16u8,0u8,20u8,17u8,17u8, - 2u8,2u8,1u8,0u8,0u8,27u8,4u8,11u8,0u8,16u8,0u8,20u8,2u8,3u8,1u8,0u8,0u8,27u8,4u8,11u8, - 0u8,16u8,1u8,20u8,2u8,4u8,1u8,0u8,0u8,7u8,13u8,11u8,0u8,17u8,5u8,12u8,2u8,12u8,1u8,11u8, - 2u8,4u8,7u8,5u8,10u8,7u8,1u8,17u8,29u8,39u8,11u8,1u8,18u8,1u8,2u8,5u8,0u8,2u8,0u8,6u8, - 1u8,0u8,0u8,30u8,15u8,11u8,0u8,17u8,7u8,12u8,3u8,12u8,2u8,11u8,3u8,4u8,11u8,11u8,2u8,18u8, - 0u8,56u8,0u8,12u8,1u8,5u8,13u8,56u8,1u8,12u8,1u8,11u8,1u8,2u8,7u8,0u8,2u8,0u8,8u8,1u8, - 0u8,0u8,27u8,3u8,11u8,0u8,18u8,2u8,2u8,9u8,1u8,0u8,0u8,27u8,4u8,11u8,0u8,16u8,2u8,20u8, - 2u8,10u8,1u8,0u8,0u8,12u8,12u8,10u8,0u8,17u8,19u8,4u8,8u8,11u8,0u8,18u8,3u8,56u8,2u8,12u8, - 1u8,5u8,10u8,56u8,3u8,12u8,1u8,11u8,1u8,2u8,11u8,1u8,0u8,0u8,14u8,15u8,10u8,0u8,11u8,1u8, - 16u8,2u8,20u8,17u8,26u8,4u8,11u8,11u8,0u8,18u8,4u8,56u8,4u8,12u8,2u8,5u8,13u8,56u8,5u8,12u8, - 2u8,11u8,2u8,2u8,12u8,1u8,0u8,0u8,27u8,4u8,11u8,0u8,16u8,3u8,20u8,2u8,13u8,1u8,0u8,0u8, - 27u8,4u8,11u8,0u8,16u8,4u8,20u8,2u8,14u8,1u8,0u8,0u8,27u8,5u8,11u8,0u8,16u8,4u8,20u8,18u8, - 3u8,2u8,15u8,1u8,0u8,0u8,27u8,3u8,11u8,0u8,18u8,5u8,2u8,16u8,1u8,0u8,0u8,27u8,5u8,11u8, - 0u8,16u8,5u8,20u8,17u8,17u8,2u8,17u8,0u8,2u8,0u8,18u8,1u8,0u8,0u8,27u8,4u8,11u8,0u8,16u8, - 5u8,20u8,2u8,19u8,0u8,2u8,0u8,20u8,1u8,0u8,0u8,27u8,7u8,11u8,0u8,16u8,0u8,20u8,11u8,1u8, - 11u8,2u8,17u8,21u8,2u8,21u8,0u8,2u8,0u8,22u8,1u8,0u8,0u8,27u8,9u8,11u8,0u8,16u8,0u8,20u8, - 11u8,1u8,16u8,1u8,20u8,11u8,2u8,17u8,23u8,2u8,23u8,0u8,2u8,0u8,24u8,1u8,0u8,0u8,27u8,9u8, - 11u8,0u8,16u8,5u8,20u8,11u8,1u8,16u8,3u8,20u8,11u8,2u8,17u8,25u8,2u8,25u8,0u8,2u8,0u8,26u8, - 0u8,2u8,0u8,27u8,1u8,0u8,0u8,27u8,9u8,11u8,0u8,16u8,5u8,20u8,11u8,1u8,16u8,4u8,20u8,11u8, - 2u8,17u8,28u8,2u8,28u8,0u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8, - 0u8, - ]; - vector::push_back(&mut code, chunk11); - let chunk12 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,8u8,2u8,8u8,24u8,3u8,32u8,98u8,4u8, - 130u8,1u8,14u8,5u8,144u8,1u8,115u8,7u8,131u8,2u8,150u8,2u8,8u8,153u8,4u8,32u8,6u8,185u8,4u8,30u8, - 16u8,215u8,4u8,241u8,1u8,10u8,200u8,6u8,21u8,11u8,221u8,6u8,8u8,12u8,229u8,6u8,206u8,2u8,13u8,179u8, - 9u8,8u8,14u8,187u8,9u8,8u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,3u8,1u8,0u8,1u8, - 0u8,5u8,8u8,1u8,0u8,1u8,0u8,6u8,8u8,1u8,0u8,1u8,0u8,7u8,2u8,1u8,0u8,1u8,0u8,8u8, - 0u8,1u8,1u8,0u8,0u8,9u8,0u8,2u8,1u8,0u8,0u8,10u8,3u8,4u8,1u8,2u8,0u8,11u8,0u8,4u8, - 1u8,0u8,0u8,12u8,5u8,4u8,1u8,0u8,0u8,13u8,6u8,7u8,1u8,0u8,0u8,14u8,8u8,4u8,1u8,2u8, - 0u8,15u8,9u8,4u8,1u8,0u8,0u8,16u8,10u8,7u8,1u8,0u8,0u8,17u8,11u8,7u8,1u8,0u8,3u8,20u8, - 14u8,15u8,1u8,0u8,2u8,21u8,11u8,7u8,0u8,1u8,22u8,16u8,16u8,0u8,3u8,23u8,14u8,18u8,1u8,0u8, - 3u8,24u8,19u8,12u8,1u8,0u8,1u8,25u8,16u8,16u8,0u8,1u8,26u8,16u8,16u8,0u8,9u8,12u8,10u8,12u8, - 2u8,7u8,13u8,12u8,14u8,12u8,6u8,7u8,10u8,7u8,2u8,6u8,12u8,6u8,9u8,0u8,1u8,11u8,0u8,1u8, - 9u8,0u8,1u8,11u8,3u8,1u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8,9u8,0u8,0u8,3u8,11u8,0u8,1u8, - 9u8,0u8,6u8,9u8,0u8,6u8,12u8,2u8,11u8,3u8,1u8,9u8,0u8,6u8,9u8,0u8,1u8,5u8,2u8,7u8, - 10u8,9u8,0u8,6u8,9u8,0u8,3u8,11u8,0u8,1u8,9u8,0u8,6u8,9u8,0u8,5u8,2u8,11u8,0u8,1u8, - 9u8,0u8,6u8,9u8,0u8,1u8,6u8,12u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,2u8,6u8,10u8,9u8,0u8, - 6u8,9u8,0u8,1u8,1u8,1u8,3u8,2u8,6u8,9u8,0u8,3u8,2u8,1u8,3u8,2u8,7u8,10u8,9u8,0u8, - 3u8,3u8,5u8,5u8,5u8,10u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,5u8,101u8,114u8,114u8, - 111u8,114u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,3u8,67u8,97u8,112u8, - 16u8,67u8,97u8,112u8,68u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,83u8,116u8,97u8,116u8,101u8,8u8,67u8,97u8, - 112u8,83u8,116u8,97u8,116u8,101u8,9u8,76u8,105u8,110u8,101u8,97u8,114u8,67u8,97u8,112u8,7u8,97u8,99u8,113u8, - 117u8,105u8,114u8,101u8,14u8,97u8,99u8,113u8,117u8,105u8,114u8,101u8,95u8,108u8,105u8,110u8,101u8,97u8,114u8,11u8, - 97u8,100u8,100u8,95u8,101u8,108u8,101u8,109u8,101u8,110u8,116u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,8u8,100u8, - 101u8,108u8,101u8,103u8,97u8,116u8,101u8,16u8,108u8,105u8,110u8,101u8,97u8,114u8,95u8,114u8,111u8,111u8,116u8,95u8, - 97u8,100u8,100u8,114u8,14u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8,101u8,108u8,101u8,109u8,101u8,110u8,116u8,6u8, - 114u8,101u8,118u8,111u8,107u8,101u8,9u8,114u8,111u8,111u8,116u8,95u8,97u8,100u8,100u8,114u8,16u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,101u8,95u8,97u8,99u8,113u8,117u8,105u8,114u8,101u8,4u8,114u8,111u8,111u8,116u8,9u8,100u8, - 101u8,108u8,101u8,103u8,97u8,116u8,101u8,115u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,10u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8, - 115u8,116u8,115u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,13u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8, - 117u8,110u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,220u8,1u8,3u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,67u8,65u8,80u8, - 65u8,66u8,73u8,76u8,73u8,84u8,89u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8, - 84u8,83u8,59u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8, - 99u8,101u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,32u8,111u8,110u8, - 32u8,116u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,97u8,99u8,99u8,111u8,117u8, - 110u8,116u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8, - 84u8,89u8,95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,29u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8,110u8,111u8,116u8,32u8,102u8,111u8,117u8, - 110u8,100u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,9u8,69u8,68u8,69u8,76u8,69u8,71u8,65u8,84u8,69u8, - 43u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8,97u8, - 118u8,101u8,32u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,32u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8, - 105u8,111u8,110u8,115u8,0u8,0u8,0u8,2u8,1u8,18u8,5u8,1u8,2u8,1u8,18u8,5u8,2u8,2u8,1u8,19u8, - 10u8,5u8,3u8,2u8,1u8,18u8,5u8,0u8,12u8,3u8,12u8,2u8,12u8,1u8,12u8,0u8,1u8,0u8,2u8,1u8, - 2u8,4u8,4u8,11u8,0u8,56u8,0u8,57u8,0u8,2u8,1u8,1u8,0u8,2u8,1u8,2u8,4u8,4u8,11u8,0u8, - 56u8,0u8,57u8,1u8,2u8,2u8,0u8,0u8,0u8,13u8,15u8,10u8,0u8,14u8,1u8,12u8,2u8,46u8,11u8,2u8, - 56u8,1u8,32u8,4u8,12u8,11u8,0u8,11u8,1u8,68u8,12u8,5u8,14u8,11u8,0u8,1u8,2u8,3u8,1u8,0u8, - 0u8,4u8,16u8,10u8,0u8,17u8,11u8,59u8,2u8,32u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,0u8,17u8, - 12u8,39u8,11u8,0u8,64u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,2u8,63u8,2u8,2u8,4u8, - 1u8,0u8,1u8,2u8,7u8,23u8,10u8,2u8,17u8,11u8,12u8,3u8,10u8,3u8,59u8,3u8,4u8,9u8,11u8,2u8, - 1u8,2u8,11u8,2u8,14u8,0u8,55u8,0u8,20u8,57u8,3u8,63u8,3u8,14u8,0u8,55u8,0u8,20u8,60u8,2u8, - 54u8,1u8,11u8,3u8,56u8,2u8,2u8,5u8,1u8,0u8,0u8,4u8,4u8,14u8,0u8,55u8,2u8,20u8,2u8,6u8, - 0u8,0u8,0u8,17u8,16u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8,56u8,3u8,12u8,3u8,4u8,13u8, - 11u8,0u8,11u8,3u8,56u8,4u8,1u8,5u8,15u8,11u8,0u8,1u8,2u8,7u8,1u8,0u8,2u8,1u8,2u8,4u8, - 17u8,10u8,2u8,59u8,3u8,32u8,4u8,5u8,2u8,10u8,2u8,62u8,3u8,58u8,3u8,1u8,14u8,0u8,55u8,0u8, - 20u8,60u8,2u8,54u8,1u8,14u8,2u8,56u8,5u8,2u8,8u8,1u8,0u8,0u8,4u8,4u8,14u8,0u8,55u8,0u8, - 20u8,2u8,9u8,0u8,0u8,2u8,1u8,2u8,20u8,42u8,11u8,0u8,17u8,11u8,12u8,2u8,10u8,2u8,59u8,3u8, - 4u8,31u8,10u8,2u8,61u8,3u8,55u8,3u8,20u8,12u8,3u8,10u8,3u8,59u8,2u8,4u8,15u8,5u8,18u8,7u8, - 2u8,17u8,15u8,39u8,10u8,3u8,61u8,2u8,55u8,1u8,14u8,2u8,56u8,6u8,4u8,25u8,5u8,28u8,7u8,2u8, - 17u8,15u8,39u8,11u8,3u8,12u8,1u8,5u8,40u8,10u8,2u8,59u8,2u8,4u8,35u8,5u8,38u8,7u8,1u8,17u8, - 16u8,39u8,11u8,2u8,12u8,1u8,11u8,1u8,2u8,0u8,0u8,2u8,0u8,3u8,0u8,1u8,0u8,0u8,12u8,1u8, - 12u8,2u8,12u8,3u8,12u8,0u8, - ]; - vector::push_back(&mut code, chunk12); - let chunk13 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,4u8,2u8,4u8,4u8,3u8,8u8,32u8,4u8, - 40u8,2u8,5u8,42u8,45u8,7u8,87u8,104u8,8u8,191u8,1u8,32u8,6u8,223u8,1u8,9u8,16u8,232u8,1u8,38u8, - 10u8,142u8,2u8,5u8,12u8,147u8,2u8,225u8,1u8,13u8,244u8,3u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,2u8, - 0u8,0u8,3u8,0u8,1u8,1u8,0u8,0u8,4u8,2u8,1u8,0u8,0u8,5u8,3u8,4u8,0u8,0u8,6u8,3u8, - 4u8,0u8,0u8,7u8,3u8,4u8,0u8,1u8,9u8,6u8,7u8,1u8,0u8,5u8,5u8,2u8,6u8,9u8,0u8,6u8, - 9u8,0u8,1u8,8u8,0u8,2u8,10u8,2u8,10u8,2u8,1u8,6u8,8u8,0u8,1u8,1u8,1u8,9u8,0u8,1u8, - 6u8,9u8,0u8,1u8,10u8,2u8,0u8,8u8,1u8,8u8,0u8,8u8,0u8,3u8,2u8,3u8,2u8,3u8,1u8,2u8, - 10u8,99u8,111u8,109u8,112u8,97u8,114u8,97u8,116u8,111u8,114u8,3u8,98u8,99u8,115u8,6u8,82u8,101u8,115u8,117u8, - 108u8,116u8,7u8,99u8,111u8,109u8,112u8,97u8,114u8,101u8,17u8,99u8,111u8,109u8,112u8,97u8,114u8,101u8,95u8,117u8, - 56u8,95u8,118u8,101u8,99u8,116u8,111u8,114u8,8u8,105u8,115u8,95u8,101u8,113u8,117u8,97u8,108u8,15u8,105u8,115u8, - 95u8,103u8,114u8,101u8,97u8,116u8,101u8,114u8,95u8,116u8,104u8,97u8,110u8,15u8,105u8,115u8,95u8,115u8,109u8,97u8, - 108u8,108u8,101u8,114u8,95u8,116u8,104u8,97u8,110u8,5u8,105u8,110u8,110u8,101u8,114u8,8u8,116u8,111u8,95u8,98u8, - 121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,2u8,1u8,0u8,2u8, - 1u8,2u8,2u8,1u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,18u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,69u8,81u8,85u8,65u8,76u8, - 0u8,0u8,0u8,0u8,2u8,1u8,8u8,2u8,0u8,1u8,0u8,0u8,2u8,10u8,11u8,0u8,56u8,0u8,12u8,2u8, - 11u8,1u8,56u8,0u8,12u8,3u8,11u8,2u8,11u8,3u8,17u8,1u8,2u8,1u8,1u8,0u8,0u8,9u8,74u8,14u8, - 0u8,65u8,10u8,12u8,7u8,14u8,1u8,65u8,10u8,12u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 12u8,5u8,10u8,5u8,10u8,7u8,35u8,4u8,18u8,5u8,13u8,10u8,5u8,10u8,9u8,35u8,12u8,2u8,5u8,20u8, - 9u8,12u8,2u8,11u8,2u8,4u8,51u8,14u8,0u8,10u8,5u8,66u8,10u8,20u8,12u8,6u8,14u8,1u8,10u8,5u8, - 66u8,10u8,20u8,12u8,8u8,10u8,6u8,10u8,8u8,35u8,4u8,39u8,7u8,2u8,18u8,0u8,2u8,11u8,6u8,11u8, - 8u8,36u8,4u8,46u8,7u8,1u8,18u8,0u8,2u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,12u8,5u8,5u8,8u8,10u8,7u8,10u8,9u8,35u8,4u8,59u8,7u8,2u8,18u8,0u8,12u8,4u8,5u8,72u8, - 11u8,7u8,11u8,9u8,36u8,4u8,67u8,7u8,1u8,18u8,0u8,12u8,3u8,5u8,70u8,7u8,0u8,18u8,0u8,12u8, - 3u8,11u8,3u8,12u8,4u8,11u8,4u8,2u8,2u8,1u8,0u8,0u8,8u8,6u8,11u8,0u8,16u8,0u8,20u8,7u8, - 0u8,33u8,2u8,3u8,1u8,0u8,0u8,8u8,6u8,11u8,0u8,16u8,0u8,20u8,7u8,1u8,33u8,2u8,4u8,1u8, - 0u8,0u8,8u8,6u8,11u8,0u8,16u8,0u8,20u8,7u8,2u8,33u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk13); - let chunk14 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,12u8,2u8,12u8,8u8,3u8,20u8,40u8,4u8, - 60u8,6u8,5u8,66u8,27u8,7u8,93u8,124u8,8u8,217u8,1u8,32u8,6u8,249u8,1u8,10u8,16u8,131u8,2u8,118u8, - 10u8,249u8,2u8,9u8,12u8,130u8,3u8,57u8,13u8,187u8,3u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8, - 0u8,4u8,0u8,5u8,0u8,6u8,7u8,0u8,4u8,8u8,7u8,0u8,0u8,7u8,0u8,1u8,1u8,7u8,0u8,9u8, - 2u8,3u8,0u8,0u8,10u8,1u8,0u8,1u8,0u8,5u8,9u8,4u8,5u8,1u8,0u8,1u8,12u8,6u8,7u8,1u8, - 0u8,2u8,13u8,8u8,8u8,0u8,3u8,14u8,7u8,0u8,1u8,0u8,3u8,0u8,4u8,0u8,6u8,0u8,1u8,9u8, - 0u8,1u8,8u8,0u8,1u8,6u8,8u8,0u8,1u8,6u8,8u8,1u8,0u8,1u8,8u8,1u8,1u8,6u8,9u8,0u8, - 1u8,10u8,2u8,1u8,3u8,12u8,99u8,111u8,112u8,121u8,97u8,98u8,108u8,101u8,95u8,97u8,110u8,121u8,3u8,98u8, - 99u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,114u8,111u8,109u8,95u8,98u8,99u8,115u8,6u8,115u8,116u8, - 114u8,105u8,110u8,103u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,3u8,65u8,110u8,121u8,4u8,112u8, - 97u8,99u8,107u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,9u8,116u8,121u8,112u8,101u8,95u8,110u8,97u8,109u8,101u8, - 6u8,117u8,110u8,112u8,97u8,99u8,107u8,4u8,100u8,97u8,116u8,97u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8, - 115u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,10u8,102u8, - 114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8, - 101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,98u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 14u8,69u8,84u8,89u8,80u8,69u8,95u8,77u8,73u8,83u8,77u8,65u8,84u8,67u8,72u8,71u8,84u8,104u8,101u8,32u8, - 116u8,121u8,112u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,102u8,111u8,114u8,32u8,96u8,117u8, - 110u8,112u8,97u8,99u8,107u8,96u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8, - 109u8,101u8,32u8,97u8,115u8,32u8,119u8,97u8,115u8,32u8,103u8,105u8,118u8,101u8,110u8,32u8,102u8,111u8,114u8,32u8, - 96u8,112u8,97u8,99u8,107u8,96u8,46u8,0u8,0u8,0u8,2u8,2u8,9u8,8u8,1u8,11u8,10u8,2u8,0u8,1u8, - 0u8,0u8,4u8,5u8,56u8,0u8,14u8,0u8,56u8,1u8,18u8,0u8,2u8,1u8,1u8,0u8,0u8,4u8,3u8,11u8, - 0u8,16u8,0u8,2u8,2u8,1u8,0u8,0u8,4u8,15u8,56u8,0u8,14u8,0u8,16u8,0u8,20u8,33u8,4u8,7u8, - 5u8,10u8,7u8,0u8,17u8,5u8,39u8,14u8,0u8,16u8,1u8,20u8,56u8,2u8,2u8,0u8,0u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk14); - let chunk15 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,12u8,2u8,12u8,28u8,3u8,40u8,121u8,4u8, - 161u8,1u8,8u8,5u8,169u8,1u8,84u8,7u8,253u8,1u8,185u8,5u8,8u8,182u8,7u8,32u8,6u8,214u8,7u8,43u8, - 16u8,129u8,8u8,250u8,1u8,10u8,251u8,9u8,27u8,11u8,150u8,10u8,2u8,12u8,152u8,10u8,151u8,2u8,13u8,175u8, - 12u8,6u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,7u8,0u8,0u8,7u8, - 2u8,1u8,0u8,0u8,0u8,8u8,7u8,0u8,0u8,9u8,7u8,0u8,4u8,13u8,7u8,1u8,0u8,0u8,5u8,29u8, - 7u8,0u8,0u8,10u8,0u8,1u8,0u8,0u8,11u8,2u8,3u8,1u8,2u8,0u8,12u8,0u8,4u8,0u8,0u8,14u8, - 0u8,5u8,0u8,0u8,15u8,0u8,0u8,0u8,0u8,16u8,6u8,4u8,0u8,0u8,17u8,7u8,4u8,0u8,0u8,18u8, - 8u8,5u8,0u8,0u8,19u8,0u8,9u8,0u8,0u8,20u8,10u8,0u8,0u8,0u8,21u8,11u8,9u8,0u8,0u8,22u8, - 12u8,9u8,0u8,0u8,23u8,13u8,9u8,1u8,2u8,0u8,24u8,8u8,0u8,0u8,0u8,25u8,8u8,0u8,0u8,0u8, - 26u8,7u8,0u8,0u8,0u8,27u8,7u8,0u8,0u8,2u8,31u8,16u8,16u8,0u8,5u8,32u8,14u8,17u8,1u8,0u8, - 4u8,33u8,2u8,18u8,1u8,0u8,4u8,34u8,14u8,18u8,1u8,0u8,3u8,35u8,0u8,0u8,0u8,1u8,36u8,19u8, - 0u8,1u8,0u8,18u8,2u8,19u8,6u8,20u8,6u8,22u8,3u8,1u8,10u8,2u8,1u8,8u8,0u8,1u8,9u8,0u8, - 1u8,11u8,1u8,1u8,9u8,0u8,1u8,8u8,2u8,1u8,11u8,4u8,1u8,8u8,3u8,1u8,8u8,3u8,1u8,6u8, - 8u8,3u8,1u8,6u8,8u8,2u8,1u8,1u8,1u8,6u8,8u8,0u8,3u8,6u8,8u8,0u8,6u8,8u8,2u8,10u8, - 2u8,3u8,10u8,2u8,10u8,2u8,10u8,2u8,3u8,6u8,8u8,0u8,6u8,8u8,2u8,9u8,0u8,0u8,1u8,2u8, - 1u8,3u8,1u8,8u8,5u8,1u8,11u8,4u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,7u8,101u8,100u8,50u8,53u8, - 53u8,49u8,57u8,3u8,98u8,99u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,4u8,104u8,97u8,115u8,104u8,6u8,111u8, - 112u8,116u8,105u8,111u8,110u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,9u8,83u8,105u8,103u8,110u8, - 97u8,116u8,117u8,114u8,101u8,13u8,83u8,105u8,103u8,110u8,101u8,100u8,77u8,101u8,115u8,115u8,97u8,103u8,101u8,20u8, - 85u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8, - 18u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,24u8, - 110u8,101u8,119u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8, - 121u8,116u8,101u8,115u8,18u8,110u8,101u8,119u8,95u8,115u8,105u8,103u8,110u8,101u8,100u8,95u8,109u8,101u8,115u8,115u8, - 97u8,103u8,101u8,37u8,110u8,101u8,119u8,95u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8, - 112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8, - 115u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,35u8,110u8,101u8,119u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8, - 101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8, - 121u8,116u8,101u8,115u8,38u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,98u8,121u8,116u8,101u8, - 115u8,95u8,116u8,111u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8, - 107u8,101u8,121u8,27u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,105u8,110u8,116u8,111u8,95u8, - 117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,25u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8, - 101u8,121u8,95u8,116u8,111u8,95u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,19u8,112u8,117u8, - 98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,28u8,112u8,117u8, - 98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,105u8,110u8, - 116u8,101u8,114u8,110u8,97u8,108u8,18u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,116u8,111u8,95u8, - 98u8,121u8,116u8,101u8,115u8,23u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8,114u8,105u8, - 102u8,121u8,95u8,115u8,116u8,114u8,105u8,99u8,116u8,32u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8, - 118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8,116u8,114u8,105u8,99u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8, - 97u8,108u8,25u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8, - 115u8,116u8,114u8,105u8,99u8,116u8,95u8,116u8,44u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8, - 95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8,97u8,117u8,116u8,104u8,101u8, - 110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,31u8,117u8,110u8,118u8,97u8,108u8,105u8, - 100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8, - 98u8,121u8,116u8,101u8,115u8,42u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8, - 105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8, - 116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,29u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8, - 117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,5u8,98u8, - 121u8,116u8,101u8,115u8,8u8,84u8,121u8,112u8,101u8,73u8,110u8,102u8,111u8,5u8,105u8,110u8,110u8,101u8,114u8,16u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,7u8,116u8,121u8,112u8, - 101u8,95u8,111u8,102u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,8u8,115u8,104u8,97u8,51u8,95u8, - 50u8,53u8,54u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,64u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,2u8,1u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8, - 97u8,116u8,97u8,95u8,118u8,49u8,229u8,1u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8,95u8, - 87u8,82u8,79u8,78u8,71u8,95u8,80u8,85u8,66u8,75u8,69u8,89u8,95u8,83u8,73u8,90u8,69u8,83u8,87u8,114u8, - 111u8,110u8,103u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8, - 119u8,101u8,114u8,101u8,32u8,103u8,105u8,118u8,101u8,110u8,32u8,97u8,115u8,32u8,105u8,110u8,112u8,117u8,116u8,32u8, - 119u8,104u8,101u8,110u8,32u8,100u8,101u8,115u8,101u8,114u8,105u8,97u8,108u8,105u8,122u8,105u8,110u8,103u8,32u8,97u8, - 110u8,32u8,69u8,100u8,50u8,53u8,53u8,49u8,57u8,32u8,112u8,117u8,98u8,108u8,105u8,99u8,32u8,107u8,101u8,121u8, - 46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,95u8,87u8,82u8,79u8,78u8,71u8,95u8,83u8,73u8, - 71u8,78u8,65u8,84u8,85u8,82u8,69u8,95u8,83u8,73u8,90u8,69u8,82u8,87u8,114u8,111u8,110u8,103u8,32u8,110u8, - 117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8,119u8,101u8,114u8,101u8,32u8, - 103u8,105u8,118u8,101u8,110u8,32u8,97u8,115u8,32u8,105u8,110u8,112u8,117u8,116u8,32u8,119u8,104u8,101u8,110u8,32u8, - 100u8,101u8,115u8,101u8,114u8,105u8,97u8,108u8,105u8,122u8,105u8,110u8,103u8,32u8,97u8,110u8,32u8,69u8,100u8,50u8, - 53u8,53u8,49u8,57u8,32u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,46u8,0u8,0u8,0u8,2u8,1u8, - 28u8,10u8,2u8,1u8,2u8,2u8,5u8,8u8,5u8,30u8,9u8,0u8,2u8,2u8,1u8,28u8,10u8,2u8,3u8,2u8, - 1u8,28u8,10u8,2u8,1u8,2u8,0u8,1u8,0u8,0u8,14u8,12u8,14u8,0u8,65u8,15u8,7u8,3u8,33u8,4u8, - 6u8,5u8,9u8,7u8,1u8,17u8,17u8,39u8,11u8,0u8,18u8,0u8,2u8,1u8,1u8,0u8,0u8,14u8,4u8,56u8, - 0u8,11u8,0u8,57u8,0u8,2u8,2u8,1u8,0u8,0u8,14u8,12u8,14u8,0u8,65u8,15u8,7u8,2u8,33u8,4u8, - 6u8,5u8,9u8,7u8,0u8,17u8,17u8,39u8,11u8,0u8,18u8,2u8,2u8,3u8,1u8,0u8,0u8,5u8,12u8,10u8, - 0u8,17u8,8u8,4u8,8u8,11u8,0u8,18u8,3u8,56u8,1u8,12u8,1u8,5u8,10u8,56u8,2u8,12u8,1u8,11u8, - 1u8,2u8,4u8,0u8,0u8,0u8,14u8,6u8,13u8,0u8,7u8,4u8,68u8,15u8,11u8,0u8,17u8,21u8,2u8,5u8, - 1u8,0u8,0u8,14u8,5u8,14u8,0u8,16u8,0u8,20u8,18u8,2u8,2u8,6u8,1u8,0u8,0u8,14u8,5u8,11u8, - 0u8,16u8,0u8,20u8,18u8,2u8,2u8,7u8,1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8,1u8,20u8,17u8,3u8, - 2u8,8u8,0u8,2u8,0u8,9u8,1u8,0u8,0u8,14u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,10u8,1u8,0u8, - 0u8,14u8,9u8,11u8,0u8,16u8,2u8,20u8,11u8,1u8,16u8,1u8,20u8,11u8,2u8,17u8,11u8,2u8,11u8,0u8, - 2u8,0u8,12u8,1u8,0u8,0u8,3u8,14u8,56u8,0u8,11u8,2u8,57u8,0u8,12u8,3u8,11u8,0u8,16u8,2u8, - 20u8,11u8,1u8,16u8,1u8,20u8,14u8,3u8,56u8,3u8,17u8,11u8,2u8,13u8,1u8,0u8,0u8,14u8,5u8,11u8, - 0u8,16u8,1u8,20u8,17u8,4u8,2u8,14u8,1u8,0u8,0u8,14u8,4u8,11u8,0u8,16u8,1u8,20u8,2u8,15u8, - 1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8,0u8,20u8,17u8,4u8,2u8,16u8,1u8,0u8,0u8,14u8,4u8,11u8, - 0u8,16u8,0u8,20u8,2u8,3u8,0u8,2u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk15); - let chunk16 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,55u8,5u8, - 65u8,24u8,7u8,89u8,150u8,1u8,8u8,239u8,1u8,32u8,6u8,143u8,2u8,20u8,16u8,163u8,2u8,132u8,1u8,12u8, - 167u8,3u8,172u8,6u8,0u8,0u8,0u8,1u8,0u8,2u8,2u8,7u8,7u8,0u8,0u8,3u8,0u8,1u8,0u8,0u8, - 4u8,2u8,3u8,0u8,0u8,5u8,0u8,3u8,0u8,0u8,6u8,3u8,4u8,0u8,0u8,8u8,3u8,5u8,0u8,0u8, - 9u8,2u8,3u8,0u8,0u8,10u8,2u8,3u8,0u8,0u8,11u8,2u8,3u8,0u8,0u8,12u8,3u8,3u8,0u8,1u8, - 13u8,7u8,7u8,0u8,2u8,14u8,7u8,5u8,0u8,3u8,4u8,4u8,4u8,0u8,2u8,4u8,4u8,1u8,4u8,1u8, - 2u8,1u8,8u8,0u8,2u8,2u8,2u8,1u8,3u8,3u8,3u8,3u8,2u8,7u8,109u8,97u8,116u8,104u8,49u8,50u8, - 56u8,5u8,101u8,114u8,114u8,111u8,114u8,13u8,102u8,105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8,51u8, - 50u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,112u8,114u8,111u8,120u8,95u8,116u8,104u8,101u8,95u8, - 115u8,97u8,109u8,101u8,7u8,97u8,118u8,101u8,114u8,97u8,103u8,101u8,5u8,99u8,108u8,97u8,109u8,112u8,10u8,102u8, - 108u8,111u8,111u8,114u8,95u8,108u8,111u8,103u8,50u8,12u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8, - 51u8,50u8,4u8,108u8,111u8,103u8,50u8,3u8,109u8,97u8,120u8,3u8,109u8,105u8,110u8,3u8,112u8,111u8,119u8,4u8, - 115u8,113u8,114u8,116u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8, - 116u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8,97u8,119u8,95u8,118u8,97u8, - 108u8,117u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8, - 115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,112u8,2u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,23u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,65u8,82u8,71u8,95u8,70u8,76u8, - 79u8,79u8,82u8,95u8,76u8,79u8,71u8,50u8,49u8,65u8,98u8,111u8,114u8,116u8,32u8,118u8,97u8,108u8,117u8,101u8, - 32u8,119u8,104u8,101u8,110u8,32u8,97u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,97u8,114u8,103u8, - 117u8,109u8,101u8,110u8,116u8,32u8,105u8,115u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,46u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8,68u8,73u8,86u8,73u8,83u8,73u8,79u8,78u8,95u8,66u8,89u8,95u8, - 90u8,69u8,82u8,79u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,26u8,10u8,0u8,10u8,1u8,35u8,4u8,10u8, - 11u8,0u8,12u8,4u8,11u8,1u8,12u8,0u8,11u8,4u8,12u8,1u8,50u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,2u8,17u8,7u8,12u8,3u8,10u8,0u8,11u8,1u8,23u8, - 11u8,3u8,24u8,11u8,0u8,35u8,4u8,23u8,5u8,25u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,39u8, - 2u8,1u8,1u8,0u8,0u8,3u8,23u8,10u8,0u8,10u8,1u8,35u8,4u8,13u8,10u8,0u8,11u8,1u8,11u8,0u8, - 23u8,50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,22u8, - 12u8,2u8,5u8,21u8,10u8,1u8,11u8,0u8,11u8,1u8,23u8,50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,22u8,12u8,2u8,11u8,2u8,2u8,2u8,1u8,0u8,0u8,1u8, - 6u8,11u8,2u8,11u8,1u8,11u8,0u8,17u8,5u8,17u8,6u8,2u8,3u8,1u8,0u8,0u8,6u8,38u8,49u8,0u8, - 12u8,2u8,10u8,0u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,34u8,4u8,7u8,5u8,10u8,7u8,1u8,17u8,9u8,39u8,49u8,64u8,12u8,1u8,10u8,1u8,49u8,0u8,36u8, - 4u8,36u8,5u8,17u8,10u8,0u8,50u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,10u8,1u8,47u8,38u8,4u8,31u8,11u8,0u8,10u8,1u8,48u8,12u8,0u8,11u8,2u8,10u8,1u8, - 22u8,12u8,2u8,11u8,1u8,49u8,1u8,48u8,12u8,1u8,5u8,12u8,11u8,2u8,2u8,4u8,1u8,0u8,0u8,8u8, - 60u8,10u8,0u8,17u8,3u8,12u8,3u8,10u8,0u8,50u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,38u8,4u8,14u8,11u8,0u8,10u8,3u8,49u8,32u8,23u8,48u8,12u8,0u8,5u8, - 20u8,11u8,0u8,49u8,32u8,10u8,3u8,23u8,47u8,12u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 12u8,2u8,6u8,0u8,0u8,0u8,128u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,34u8,4u8,52u8,5u8,29u8,10u8,0u8,11u8,0u8,24u8,49u8,32u8,48u8,12u8,0u8,10u8, - 0u8,50u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,38u8,4u8, - 47u8,11u8,2u8,10u8,1u8,22u8,12u8,2u8,11u8,0u8,49u8,1u8,48u8,12u8,0u8,11u8,1u8,49u8,1u8,48u8, - 12u8,1u8,5u8,24u8,11u8,3u8,52u8,49u8,32u8,47u8,11u8,2u8,22u8,17u8,10u8,2u8,5u8,1u8,0u8,0u8, - 3u8,11u8,10u8,0u8,10u8,1u8,38u8,4u8,7u8,11u8,0u8,12u8,2u8,5u8,9u8,11u8,1u8,12u8,2u8,11u8, - 2u8,2u8,6u8,1u8,0u8,0u8,3u8,11u8,10u8,0u8,10u8,1u8,35u8,4u8,7u8,11u8,0u8,12u8,2u8,5u8, - 9u8,11u8,1u8,12u8,2u8,11u8,2u8,2u8,7u8,1u8,0u8,0u8,2u8,39u8,10u8,1u8,50u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,50u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,5u8,37u8,50u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,1u8,50u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,33u8,5u8,14u8, - 10u8,1u8,50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8, - 50u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,24u8, - 11u8,3u8,10u8,0u8,24u8,12u8,3u8,11u8,1u8,50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,1u8,10u8,0u8,11u8,0u8,24u8,12u8,0u8,5u8,9u8,11u8,3u8, - 11u8,0u8,24u8,12u8,2u8,11u8,2u8,2u8,8u8,1u8,0u8,0u8,3u8,61u8,10u8,0u8,50u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,6u8,50u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,50u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,17u8,3u8,49u8,1u8,22u8,49u8,1u8, - 48u8,47u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,10u8, - 0u8,11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8, - 48u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,10u8,0u8, - 11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,11u8,0u8,11u8,1u8,26u8,17u8,6u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk16); - let chunk17 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,55u8,5u8, - 65u8,31u8,7u8,96u8,149u8,1u8,8u8,245u8,1u8,32u8,6u8,149u8,2u8,10u8,16u8,159u8,2u8,105u8,12u8,136u8, - 3u8,165u8,5u8,0u8,0u8,0u8,1u8,0u8,2u8,2u8,7u8,7u8,0u8,0u8,3u8,0u8,1u8,0u8,0u8,4u8, - 2u8,3u8,0u8,0u8,5u8,4u8,3u8,0u8,0u8,6u8,3u8,5u8,0u8,0u8,8u8,3u8,6u8,0u8,0u8,9u8, - 2u8,3u8,0u8,0u8,10u8,2u8,3u8,0u8,0u8,11u8,2u8,3u8,0u8,0u8,12u8,3u8,3u8,0u8,1u8,13u8, - 3u8,3u8,0u8,2u8,14u8,3u8,6u8,0u8,3u8,4u8,4u8,3u8,0u8,2u8,3u8,3u8,1u8,3u8,3u8,3u8, - 3u8,3u8,1u8,2u8,1u8,8u8,0u8,2u8,4u8,4u8,2u8,2u8,2u8,5u8,3u8,3u8,3u8,2u8,4u8,6u8, - 109u8,97u8,116u8,104u8,54u8,52u8,5u8,101u8,114u8,114u8,111u8,114u8,13u8,102u8,105u8,120u8,101u8,100u8,95u8,112u8, - 111u8,105u8,110u8,116u8,51u8,50u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,112u8,114u8,111u8,120u8, - 95u8,116u8,104u8,101u8,95u8,115u8,97u8,109u8,101u8,7u8,97u8,118u8,101u8,114u8,97u8,103u8,101u8,5u8,99u8,108u8, - 97u8,109u8,112u8,10u8,102u8,108u8,111u8,111u8,114u8,95u8,108u8,111u8,103u8,50u8,12u8,70u8,105u8,120u8,101u8,100u8, - 80u8,111u8,105u8,110u8,116u8,51u8,50u8,4u8,108u8,111u8,103u8,50u8,3u8,109u8,97u8,120u8,3u8,109u8,105u8,110u8, - 3u8,112u8,111u8,119u8,4u8,115u8,113u8,114u8,116u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8, - 103u8,117u8,109u8,101u8,110u8,116u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,114u8, - 97u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8, - 3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8, - 116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,85u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8, - 69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,65u8,82u8,71u8,95u8,70u8,76u8,79u8,79u8,82u8,95u8,76u8, - 79u8,71u8,50u8,49u8,65u8,98u8,111u8,114u8,116u8,32u8,118u8,97u8,108u8,117u8,101u8,32u8,119u8,104u8,101u8,110u8, - 32u8,97u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8, - 32u8,105u8,115u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,46u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8, - 27u8,10u8,0u8,10u8,1u8,35u8,4u8,10u8,11u8,0u8,12u8,4u8,11u8,1u8,12u8,0u8,11u8,4u8,12u8,1u8, - 6u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,2u8,17u8,7u8,53u8,12u8,3u8,10u8,0u8,11u8,1u8, - 23u8,11u8,3u8,24u8,11u8,0u8,35u8,4u8,24u8,5u8,26u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 39u8,2u8,1u8,1u8,0u8,0u8,3u8,23u8,10u8,0u8,10u8,1u8,35u8,4u8,13u8,10u8,0u8,11u8,1u8,11u8, - 0u8,23u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,22u8,12u8,2u8,5u8,21u8,10u8,1u8,11u8, - 0u8,11u8,1u8,23u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,22u8,12u8,2u8,11u8,2u8,2u8, - 2u8,1u8,0u8,0u8,1u8,6u8,11u8,2u8,11u8,1u8,11u8,0u8,17u8,5u8,17u8,6u8,2u8,3u8,1u8,0u8, - 0u8,8u8,38u8,49u8,0u8,12u8,2u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8, - 7u8,5u8,10u8,7u8,0u8,17u8,9u8,39u8,49u8,32u8,12u8,1u8,10u8,1u8,49u8,0u8,36u8,4u8,36u8,5u8, - 17u8,10u8,0u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,1u8,47u8,38u8,4u8,31u8,11u8,0u8, - 10u8,1u8,48u8,12u8,0u8,11u8,2u8,10u8,1u8,22u8,12u8,2u8,11u8,1u8,49u8,1u8,48u8,12u8,1u8,5u8, - 12u8,11u8,2u8,2u8,4u8,1u8,0u8,0u8,9u8,63u8,10u8,0u8,17u8,3u8,12u8,4u8,10u8,0u8,6u8,0u8, - 0u8,0u8,0u8,1u8,0u8,0u8,0u8,38u8,4u8,14u8,11u8,0u8,10u8,4u8,49u8,32u8,23u8,48u8,12u8,1u8, - 5u8,20u8,11u8,0u8,49u8,32u8,10u8,4u8,23u8,47u8,12u8,1u8,11u8,1u8,53u8,12u8,5u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,6u8,0u8,0u8,0u8,128u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8, - 2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,55u8,5u8,32u8,10u8,5u8,11u8,5u8,24u8, - 49u8,32u8,48u8,12u8,5u8,10u8,5u8,50u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,38u8,4u8,50u8,11u8,3u8,10u8,2u8,22u8,12u8,3u8,11u8,5u8,49u8,1u8,48u8,12u8, - 5u8,11u8,2u8,49u8,1u8,48u8,12u8,2u8,5u8,27u8,11u8,4u8,52u8,49u8,32u8,47u8,11u8,3u8,22u8,17u8, - 10u8,2u8,5u8,1u8,0u8,0u8,3u8,11u8,10u8,0u8,10u8,1u8,38u8,4u8,7u8,11u8,0u8,12u8,2u8,5u8, - 9u8,11u8,1u8,12u8,2u8,11u8,2u8,2u8,6u8,1u8,0u8,0u8,3u8,11u8,10u8,0u8,10u8,1u8,35u8,4u8, - 7u8,11u8,0u8,12u8,2u8,5u8,9u8,11u8,1u8,12u8,2u8,11u8,2u8,2u8,7u8,1u8,0u8,0u8,2u8,39u8, - 10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,12u8,2u8,5u8,37u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,1u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,33u8,5u8,14u8,10u8,1u8,6u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,25u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,24u8,11u8,3u8, - 10u8,0u8,24u8,12u8,3u8,11u8,1u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,1u8,10u8, - 0u8,11u8,0u8,24u8,12u8,0u8,5u8,9u8,11u8,3u8,11u8,0u8,24u8,12u8,2u8,11u8,2u8,2u8,8u8,1u8, - 0u8,0u8,3u8,53u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,6u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,17u8, - 3u8,49u8,1u8,22u8,49u8,1u8,48u8,47u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8, - 48u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,10u8,0u8, - 11u8,1u8,26u8,22u8,49u8,1u8,48u8,12u8,1u8,10u8,1u8,10u8,0u8,11u8,1u8,26u8,22u8,49u8,1u8,48u8, - 12u8,1u8,10u8,1u8,11u8,0u8,11u8,1u8,26u8,17u8,6u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk17); - let chunk18 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,8u8,2u8,8u8,4u8,3u8,12u8,75u8,5u8, - 87u8,45u8,7u8,132u8,1u8,194u8,1u8,8u8,198u8,2u8,32u8,6u8,230u8,2u8,38u8,16u8,140u8,3u8,68u8,12u8, - 208u8,3u8,211u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,2u8,5u8,7u8,0u8,0u8,4u8,0u8,1u8, - 0u8,0u8,6u8,2u8,2u8,0u8,0u8,7u8,3u8,3u8,0u8,0u8,8u8,2u8,2u8,0u8,0u8,9u8,2u8,2u8, - 0u8,0u8,10u8,4u8,2u8,0u8,0u8,11u8,5u8,2u8,0u8,0u8,12u8,6u8,3u8,0u8,0u8,13u8,2u8,2u8, - 0u8,3u8,11u8,6u8,3u8,0u8,2u8,14u8,2u8,7u8,0u8,2u8,15u8,7u8,2u8,0u8,1u8,16u8,7u8,7u8, - 0u8,3u8,17u8,3u8,2u8,0u8,3u8,13u8,3u8,3u8,0u8,3u8,4u8,4u8,4u8,0u8,1u8,8u8,0u8,1u8, - 4u8,3u8,8u8,0u8,8u8,0u8,8u8,0u8,2u8,8u8,0u8,3u8,2u8,4u8,4u8,1u8,3u8,10u8,4u8,4u8, - 4u8,4u8,2u8,4u8,4u8,4u8,4u8,4u8,5u8,3u8,3u8,3u8,3u8,3u8,1u8,15u8,10u8,109u8,97u8,116u8, - 104u8,95u8,102u8,105u8,120u8,101u8,100u8,5u8,101u8,114u8,114u8,111u8,114u8,13u8,102u8,105u8,120u8,101u8,100u8,95u8, - 112u8,111u8,105u8,110u8,116u8,51u8,50u8,7u8,109u8,97u8,116u8,104u8,49u8,50u8,56u8,22u8,97u8,115u8,115u8,101u8, - 114u8,116u8,95u8,97u8,112u8,112u8,114u8,111u8,120u8,95u8,116u8,104u8,101u8,95u8,115u8,97u8,109u8,101u8,12u8,70u8, - 105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,51u8,50u8,3u8,101u8,120u8,112u8,7u8,101u8,120u8,112u8,95u8, - 114u8,97u8,119u8,13u8,108u8,110u8,95u8,112u8,108u8,117u8,115u8,95u8,51u8,50u8,108u8,110u8,50u8,12u8,108u8,111u8, - 103u8,50u8,95u8,112u8,108u8,117u8,115u8,95u8,51u8,50u8,7u8,109u8,117u8,108u8,95u8,100u8,105u8,118u8,3u8,112u8, - 111u8,119u8,7u8,112u8,111u8,119u8,95u8,114u8,97u8,119u8,4u8,115u8,113u8,114u8,116u8,13u8,103u8,101u8,116u8,95u8, - 114u8,97u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,111u8, - 109u8,95u8,114u8,97u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8, - 115u8,116u8,97u8,116u8,101u8,4u8,108u8,111u8,103u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,16u8,248u8,23u8,114u8,177u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,255u8,66u8,46u8,22u8,0u8,0u8,0u8, - 18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,48u8, - 1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,95u8, - 69u8,88u8,80u8,22u8,65u8,98u8,111u8,114u8,116u8,32u8,99u8,111u8,100u8,101u8,32u8,111u8,110u8,32u8,111u8,118u8, - 101u8,114u8,102u8,108u8,111u8,119u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,26u8,10u8,0u8,10u8,1u8,35u8,4u8, - 10u8,11u8,0u8,12u8,4u8,11u8,1u8,12u8,0u8,11u8,4u8,12u8,1u8,50u8,10u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,2u8,17u8,9u8,12u8,3u8,10u8,0u8,11u8,1u8, - 23u8,11u8,3u8,24u8,11u8,0u8,35u8,4u8,23u8,5u8,25u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 39u8,2u8,1u8,1u8,0u8,0u8,1u8,7u8,11u8,0u8,17u8,10u8,53u8,17u8,2u8,52u8,17u8,11u8,2u8,2u8, - 0u8,0u8,0u8,8u8,77u8,10u8,0u8,7u8,1u8,26u8,12u8,6u8,10u8,6u8,50u8,31u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,4u8,9u8,5u8,12u8,7u8,0u8,17u8,12u8, - 39u8,11u8,6u8,51u8,12u8,5u8,11u8,0u8,7u8,1u8,25u8,12u8,4u8,50u8,72u8,22u8,9u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8,4u8,10u8,1u8,26u8,12u8,2u8,11u8, - 4u8,11u8,1u8,25u8,12u8,10u8,50u8,113u8,22u8,9u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,10u8,2u8,17u8,7u8,12u8,3u8,10u8,3u8,11u8,3u8,50u8,139u8,76u8,248u8,73u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,11u8,2u8,24u8,49u8,64u8,48u8,22u8,12u8,3u8, - 10u8,3u8,10u8,10u8,24u8,49u8,32u8,10u8,5u8,23u8,48u8,12u8,7u8,10u8,7u8,10u8,10u8,24u8,49u8,32u8, - 48u8,12u8,8u8,10u8,8u8,11u8,10u8,24u8,49u8,32u8,48u8,12u8,9u8,11u8,3u8,11u8,5u8,47u8,11u8,7u8, - 22u8,11u8,8u8,50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 26u8,22u8,11u8,9u8,50u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,26u8,22u8,2u8,3u8,1u8,0u8,0u8,1u8,13u8,11u8,0u8,17u8,10u8,53u8,17u8,13u8,17u8,10u8,53u8, - 7u8,1u8,24u8,49u8,32u8,48u8,52u8,17u8,11u8,2u8,4u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,17u8,10u8, - 53u8,17u8,13u8,2u8,5u8,1u8,0u8,0u8,9u8,24u8,11u8,0u8,17u8,10u8,12u8,3u8,11u8,1u8,17u8,10u8, - 12u8,4u8,11u8,2u8,17u8,10u8,12u8,6u8,11u8,3u8,11u8,4u8,11u8,6u8,12u8,7u8,12u8,5u8,53u8,11u8, - 5u8,53u8,24u8,11u8,7u8,53u8,26u8,52u8,17u8,11u8,2u8,6u8,1u8,0u8,0u8,1u8,9u8,11u8,0u8,17u8, - 10u8,53u8,11u8,1u8,53u8,17u8,7u8,52u8,17u8,11u8,2u8,7u8,0u8,0u8,0u8,10u8,43u8,74u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,11u8,0u8,49u8,32u8,47u8,12u8,0u8,10u8, - 1u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8, - 38u8,5u8,11u8,10u8,1u8,50u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,28u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 34u8,4u8,24u8,11u8,2u8,10u8,0u8,77u8,24u8,49u8,64u8,48u8,12u8,2u8,11u8,1u8,49u8,1u8,48u8,12u8, - 1u8,10u8,0u8,77u8,11u8,0u8,77u8,24u8,49u8,64u8,48u8,53u8,12u8,0u8,5u8,6u8,11u8,2u8,49u8,32u8, - 48u8,53u8,2u8,8u8,1u8,0u8,0u8,1u8,9u8,11u8,0u8,17u8,10u8,53u8,49u8,32u8,47u8,17u8,14u8,52u8, - 17u8,11u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk18); - let chunk19 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,14u8,2u8,14u8,24u8,3u8,38u8,165u8,1u8, - 4u8,203u8,1u8,12u8,5u8,215u8,1u8,101u8,7u8,188u8,2u8,220u8,7u8,8u8,152u8,10u8,32u8,6u8,184u8,10u8, - 63u8,16u8,247u8,10u8,214u8,3u8,10u8,205u8,14u8,18u8,12u8,223u8,14u8,217u8,4u8,13u8,184u8,19u8,6u8,0u8, - 0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,7u8,0u8,0u8,8u8,7u8, - 0u8,0u8,9u8,7u8,0u8,6u8,10u8,7u8,1u8,0u8,0u8,2u8,42u8,2u8,1u8,0u8,0u8,0u8,11u8,0u8, - 1u8,0u8,0u8,12u8,0u8,2u8,0u8,0u8,13u8,0u8,3u8,0u8,0u8,14u8,0u8,4u8,0u8,0u8,15u8,0u8, - 4u8,0u8,0u8,16u8,0u8,0u8,0u8,0u8,17u8,5u8,3u8,0u8,0u8,18u8,6u8,3u8,0u8,0u8,19u8,7u8, - 4u8,0u8,0u8,20u8,0u8,8u8,0u8,0u8,21u8,7u8,4u8,0u8,0u8,22u8,0u8,8u8,0u8,0u8,23u8,9u8, - 0u8,0u8,0u8,24u8,10u8,8u8,0u8,0u8,25u8,11u8,8u8,0u8,0u8,26u8,12u8,8u8,1u8,2u8,0u8,27u8, - 7u8,13u8,0u8,0u8,28u8,7u8,1u8,0u8,0u8,29u8,7u8,0u8,0u8,0u8,30u8,7u8,0u8,0u8,0u8,31u8, - 6u8,13u8,0u8,0u8,32u8,6u8,13u8,0u8,0u8,33u8,6u8,0u8,0u8,0u8,34u8,6u8,0u8,0u8,6u8,36u8, - 15u8,16u8,1u8,0u8,6u8,37u8,17u8,16u8,1u8,0u8,3u8,38u8,18u8,18u8,0u8,4u8,39u8,15u8,8u8,0u8, - 3u8,40u8,18u8,18u8,0u8,5u8,41u8,0u8,0u8,0u8,2u8,43u8,17u8,20u8,1u8,2u8,1u8,44u8,21u8,0u8, - 1u8,0u8,24u8,13u8,25u8,13u8,25u8,5u8,24u8,5u8,30u8,17u8,31u8,20u8,1u8,10u8,2u8,1u8,11u8,3u8, - 1u8,2u8,1u8,8u8,0u8,1u8,8u8,1u8,1u8,11u8,3u8,1u8,8u8,2u8,1u8,8u8,2u8,1u8,6u8,8u8, - 2u8,1u8,6u8,8u8,1u8,1u8,1u8,1u8,6u8,8u8,0u8,3u8,6u8,8u8,0u8,6u8,8u8,1u8,10u8,2u8, - 3u8,10u8,2u8,10u8,2u8,10u8,2u8,3u8,6u8,8u8,0u8,6u8,8u8,1u8,9u8,0u8,1u8,2u8,7u8,1u8, - 1u8,1u8,3u8,3u8,2u8,3u8,0u8,1u8,11u8,3u8,1u8,9u8,0u8,1u8,9u8,0u8,1u8,3u8,2u8,1u8, - 11u8,3u8,1u8,8u8,2u8,1u8,11u8,4u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,13u8,109u8,117u8,108u8,116u8, - 105u8,95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,3u8,98u8,99u8,115u8,7u8,101u8,100u8,50u8,53u8,53u8,49u8, - 57u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,4u8,104u8,97u8,115u8, - 104u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,9u8,83u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,20u8,85u8, - 110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,18u8, - 86u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,6u8,79u8, - 112u8,116u8,105u8,111u8,110u8,23u8,99u8,104u8,101u8,99u8,107u8,95u8,97u8,110u8,100u8,95u8,103u8,101u8,116u8,95u8, - 116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,24u8,110u8,101u8,119u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8, - 117u8,114u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,37u8,110u8,101u8,119u8,95u8,117u8, - 110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8, - 121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,35u8,110u8,101u8,119u8,95u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8, - 111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,38u8,110u8,101u8,119u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8, - 101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8, - 121u8,116u8,101u8,115u8,95u8,118u8,50u8,38u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,98u8, - 121u8,116u8,101u8,115u8,95u8,116u8,111u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8, - 111u8,110u8,95u8,107u8,101u8,121u8,27u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,105u8,110u8, - 116u8,111u8,95u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,25u8,112u8,117u8,98u8,108u8,105u8, - 99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8, - 19u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8, - 28u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8, - 95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,22u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8, - 95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,118u8,50u8,31u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8, - 107u8,101u8,121u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,118u8,50u8,95u8,105u8,110u8,116u8,101u8, - 114u8,110u8,97u8,108u8,18u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,23u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8,114u8,105u8,102u8,121u8, - 95u8,115u8,116u8,114u8,105u8,99u8,116u8,32u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8, - 114u8,105u8,102u8,121u8,95u8,115u8,116u8,114u8,105u8,99u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8, - 25u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8,116u8, - 114u8,105u8,99u8,116u8,95u8,116u8,34u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8, - 117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,110u8,117u8,109u8,95u8,115u8,117u8,98u8,95u8,112u8,107u8, - 115u8,32u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8, - 95u8,107u8,101u8,121u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,44u8,117u8,110u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8, - 95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,31u8, - 117u8,110u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8, - 101u8,121u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8, - 100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,110u8,117u8,109u8,95u8,115u8,117u8,98u8, - 95u8,112u8,107u8,115u8,30u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8, - 99u8,95u8,107u8,101u8,121u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,42u8,118u8,97u8,108u8,105u8, - 100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8,95u8, - 97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,29u8,118u8, - 97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8, - 116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,5u8,98u8,121u8,116u8,101u8,115u8,4u8,110u8,111u8,110u8,101u8,4u8, - 115u8,111u8,109u8,101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8, - 116u8,36u8,109u8,117u8,108u8,116u8,105u8,95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,95u8,112u8,107u8,95u8,118u8, - 97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,118u8,50u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,13u8,105u8, - 110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,8u8,115u8,104u8,97u8,51u8,95u8,50u8,53u8, - 54u8,13u8,83u8,105u8,103u8,110u8,101u8,100u8,77u8,101u8,115u8,115u8,97u8,103u8,101u8,18u8,110u8,101u8,119u8,95u8, - 115u8,105u8,103u8,110u8,101u8,100u8,95u8,109u8,101u8,115u8,115u8,97u8,103u8,101u8,8u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,32u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,64u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,1u8,1u8,18u8,97u8, - 112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,193u8,3u8,4u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8,95u8,87u8,82u8,79u8,78u8,71u8,95u8,80u8,85u8,66u8, - 75u8,69u8,89u8,95u8,83u8,73u8,90u8,69u8,83u8,87u8,114u8,111u8,110u8,103u8,32u8,110u8,117u8,109u8,98u8,101u8, - 114u8,32u8,111u8,102u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8,119u8,101u8,114u8,101u8,32u8,103u8,105u8,118u8,101u8, - 110u8,32u8,97u8,115u8,32u8,105u8,110u8,112u8,117u8,116u8,32u8,119u8,104u8,101u8,110u8,32u8,100u8,101u8,115u8,101u8, - 114u8,105u8,97u8,108u8,105u8,122u8,105u8,110u8,103u8,32u8,97u8,110u8,32u8,69u8,100u8,50u8,53u8,53u8,49u8,57u8, - 32u8,112u8,117u8,98u8,108u8,105u8,99u8,32u8,107u8,101u8,121u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,69u8,95u8,87u8,82u8,79u8,78u8,71u8,95u8,83u8,73u8,71u8,78u8,65u8,84u8,85u8,82u8,69u8,95u8,83u8, - 73u8,90u8,69u8,82u8,87u8,114u8,111u8,110u8,103u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8, - 98u8,121u8,116u8,101u8,115u8,32u8,119u8,101u8,114u8,101u8,32u8,103u8,105u8,118u8,101u8,110u8,32u8,97u8,115u8,32u8, - 105u8,110u8,112u8,117u8,116u8,32u8,119u8,104u8,101u8,110u8,32u8,100u8,101u8,115u8,101u8,114u8,105u8,97u8,108u8,105u8, - 122u8,105u8,110u8,103u8,32u8,97u8,110u8,32u8,69u8,100u8,50u8,53u8,53u8,49u8,57u8,32u8,115u8,105u8,103u8,110u8, - 97u8,116u8,117u8,114u8,101u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,40u8,69u8,95u8,73u8,78u8,86u8, - 65u8,76u8,73u8,68u8,95u8,84u8,72u8,82u8,69u8,83u8,72u8,79u8,76u8,68u8,95u8,79u8,82u8,95u8,78u8,85u8, - 77u8,66u8,69u8,82u8,95u8,79u8,70u8,95u8,83u8,73u8,71u8,78u8,69u8,82u8,83u8,84u8,84u8,104u8,101u8,32u8, - 116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,105u8,110u8, - 32u8,116u8,104u8,101u8,32u8,114u8,97u8,110u8,103u8,101u8,32u8,96u8,91u8,49u8,44u8,32u8,110u8,93u8,96u8,44u8, - 32u8,119u8,104u8,101u8,114u8,101u8,32u8,110u8,32u8,105u8,115u8,32u8,116u8,104u8,101u8,32u8,116u8,111u8,116u8,97u8, - 108u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,115u8,105u8,103u8,110u8,101u8,114u8,115u8,46u8, - 4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,95u8,78u8,65u8,84u8,73u8,86u8,69u8,95u8,70u8,85u8, - 78u8,95u8,78u8,79u8,84u8,95u8,65u8,86u8,65u8,73u8,76u8,65u8,66u8,76u8,69u8,50u8,84u8,104u8,101u8,32u8, - 110u8,97u8,116u8,105u8,118u8,101u8,32u8,102u8,117u8,110u8,99u8,116u8,105u8,111u8,110u8,115u8,32u8,104u8,97u8,118u8, - 101u8,32u8,110u8,111u8,116u8,32u8,98u8,101u8,101u8,110u8,32u8,114u8,111u8,108u8,108u8,101u8,100u8,32u8,111u8,117u8, - 116u8,32u8,121u8,101u8,116u8,46u8,0u8,0u8,0u8,2u8,1u8,35u8,10u8,2u8,1u8,2u8,1u8,35u8,10u8,2u8, - 2u8,2u8,1u8,35u8,10u8,2u8,0u8,1u8,0u8,0u8,14u8,67u8,14u8,0u8,65u8,13u8,12u8,4u8,10u8,4u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,56u8,0u8,2u8,10u8,4u8,7u8,4u8,25u8, - 12u8,7u8,10u8,4u8,7u8,4u8,26u8,12u8,5u8,14u8,0u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,66u8,13u8,20u8,12u8,6u8,10u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8, - 4u8,31u8,8u8,12u8,1u8,5u8,35u8,10u8,5u8,7u8,4u8,36u8,12u8,1u8,11u8,1u8,4u8,40u8,8u8,12u8, - 2u8,5u8,44u8,11u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,12u8,2u8,11u8,2u8,4u8, - 48u8,56u8,0u8,2u8,10u8,6u8,49u8,0u8,33u8,4u8,55u8,8u8,12u8,3u8,5u8,60u8,10u8,6u8,11u8,5u8, - 51u8,36u8,12u8,3u8,11u8,3u8,4u8,64u8,56u8,0u8,2u8,11u8,6u8,56u8,1u8,2u8,1u8,1u8,0u8,0u8, - 15u8,14u8,14u8,0u8,65u8,13u8,7u8,5u8,25u8,7u8,0u8,33u8,4u8,8u8,5u8,11u8,7u8,3u8,17u8,26u8, - 39u8,11u8,0u8,18u8,0u8,2u8,2u8,1u8,0u8,0u8,18u8,26u8,14u8,0u8,65u8,13u8,12u8,1u8,10u8,1u8, - 7u8,4u8,26u8,7u8,4u8,37u8,4u8,10u8,5u8,13u8,7u8,2u8,17u8,26u8,39u8,11u8,1u8,7u8,4u8,25u8, - 7u8,2u8,33u8,4u8,20u8,5u8,23u8,7u8,2u8,17u8,26u8,39u8,11u8,0u8,18u8,1u8,2u8,3u8,1u8,0u8, - 0u8,19u8,24u8,14u8,0u8,65u8,13u8,7u8,4u8,25u8,7u8,2u8,33u8,4u8,11u8,10u8,0u8,17u8,9u8,12u8, - 1u8,5u8,13u8,9u8,12u8,1u8,11u8,1u8,4u8,20u8,11u8,0u8,18u8,2u8,56u8,2u8,12u8,2u8,5u8,22u8, - 56u8,3u8,12u8,2u8,11u8,2u8,2u8,4u8,1u8,0u8,0u8,4u8,18u8,17u8,27u8,32u8,4u8,6u8,7u8,0u8, - 17u8,28u8,39u8,10u8,0u8,17u8,11u8,4u8,14u8,11u8,0u8,18u8,2u8,56u8,2u8,12u8,1u8,5u8,16u8,56u8, - 3u8,12u8,1u8,11u8,1u8,2u8,5u8,0u8,0u8,0u8,15u8,6u8,13u8,0u8,7u8,6u8,68u8,13u8,11u8,0u8, - 17u8,29u8,2u8,6u8,1u8,0u8,0u8,15u8,5u8,14u8,0u8,16u8,0u8,20u8,18u8,1u8,2u8,7u8,1u8,0u8, - 0u8,15u8,5u8,11u8,0u8,16u8,0u8,20u8,18u8,1u8,2u8,8u8,1u8,0u8,0u8,15u8,5u8,11u8,0u8,16u8, - 1u8,20u8,17u8,3u8,2u8,9u8,0u8,2u8,0u8,10u8,1u8,0u8,0u8,15u8,5u8,11u8,0u8,16u8,1u8,20u8, - 17u8,4u8,2u8,11u8,0u8,2u8,0u8,12u8,1u8,0u8,0u8,15u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,13u8, - 1u8,0u8,0u8,15u8,9u8,11u8,0u8,16u8,2u8,20u8,11u8,1u8,16u8,1u8,20u8,11u8,2u8,17u8,14u8,2u8, - 14u8,0u8,2u8,0u8,15u8,1u8,0u8,0u8,20u8,13u8,11u8,2u8,56u8,4u8,12u8,3u8,11u8,0u8,16u8,2u8, - 20u8,11u8,1u8,16u8,1u8,20u8,14u8,3u8,56u8,5u8,17u8,14u8,2u8,16u8,1u8,0u8,0u8,15u8,7u8,11u8, - 0u8,16u8,1u8,65u8,13u8,7u8,4u8,26u8,51u8,2u8,17u8,1u8,0u8,0u8,15u8,5u8,11u8,0u8,16u8,1u8, - 20u8,17u8,0u8,2u8,18u8,1u8,0u8,0u8,15u8,5u8,11u8,0u8,16u8,1u8,20u8,17u8,5u8,2u8,19u8,1u8, - 0u8,0u8,15u8,4u8,11u8,0u8,16u8,1u8,20u8,2u8,20u8,1u8,0u8,0u8,15u8,7u8,11u8,0u8,16u8,0u8, - 65u8,13u8,7u8,4u8,26u8,51u8,2u8,21u8,1u8,0u8,0u8,18u8,12u8,10u8,0u8,16u8,0u8,65u8,13u8,12u8, - 1u8,11u8,0u8,16u8,0u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,66u8,13u8,20u8, - 2u8,22u8,1u8,0u8,0u8,15u8,5u8,11u8,0u8,16u8,0u8,20u8,17u8,5u8,2u8,23u8,1u8,0u8,0u8,15u8, - 4u8,11u8,0u8,16u8,0u8,20u8,2u8,2u8,0u8,1u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk19); - let chunk20 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,8u8,2u8,8u8,22u8,3u8,30u8,124u8,4u8, - 154u8,1u8,24u8,5u8,178u8,1u8,218u8,1u8,7u8,140u8,3u8,236u8,1u8,8u8,248u8,4u8,32u8,6u8,152u8,5u8, - 20u8,16u8,172u8,5u8,118u8,10u8,162u8,6u8,21u8,11u8,183u8,6u8,4u8,12u8,187u8,6u8,222u8,4u8,13u8,153u8, - 11u8,6u8,14u8,159u8,11u8,6u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,7u8,2u8,0u8,0u8, - 0u8,0u8,0u8,5u8,7u8,2u8,0u8,0u8,0u8,0u8,2u8,12u8,7u8,1u8,0u8,0u8,0u8,6u8,0u8,1u8, - 2u8,4u8,4u8,0u8,7u8,2u8,3u8,2u8,4u8,4u8,0u8,8u8,4u8,5u8,2u8,4u8,4u8,0u8,9u8,2u8, - 6u8,2u8,4u8,4u8,0u8,10u8,1u8,7u8,2u8,4u8,4u8,0u8,11u8,7u8,1u8,2u8,4u8,4u8,0u8,13u8, - 2u8,8u8,2u8,4u8,4u8,0u8,14u8,9u8,10u8,2u8,4u8,4u8,0u8,15u8,4u8,11u8,2u8,4u8,4u8,0u8, - 16u8,7u8,12u8,2u8,4u8,4u8,0u8,17u8,0u8,13u8,2u8,4u8,4u8,2u8,21u8,15u8,6u8,1u8,0u8,1u8, - 22u8,10u8,10u8,0u8,2u8,23u8,15u8,6u8,1u8,0u8,2u8,24u8,18u8,19u8,1u8,0u8,2u8,25u8,19u8,22u8, - 1u8,0u8,2u8,26u8,1u8,22u8,1u8,0u8,3u8,27u8,24u8,19u8,1u8,0u8,3u8,28u8,27u8,1u8,1u8,0u8, - 6u8,11u8,11u8,10u8,13u8,10u8,14u8,10u8,15u8,10u8,16u8,10u8,17u8,16u8,18u8,16u8,15u8,19u8,15u8,26u8, - 16u8,19u8,16u8,26u8,3u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,0u8,2u8,6u8, - 11u8,1u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,6u8,9u8,1u8,2u8,7u8,11u8,1u8,2u8,9u8, - 0u8,9u8,1u8,6u8,9u8,0u8,1u8,7u8,9u8,1u8,1u8,1u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8, - 1u8,11u8,2u8,1u8,3u8,1u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,1u8,3u8,2u8,9u8,0u8,9u8, - 1u8,2u8,10u8,9u8,0u8,10u8,9u8,1u8,2u8,11u8,2u8,1u8,9u8,0u8,11u8,2u8,1u8,9u8,1u8,2u8, - 6u8,9u8,0u8,11u8,2u8,1u8,3u8,1u8,6u8,11u8,2u8,1u8,9u8,0u8,1u8,11u8,0u8,2u8,9u8,0u8, - 9u8,1u8,2u8,3u8,11u8,2u8,1u8,3u8,1u8,7u8,11u8,2u8,1u8,9u8,0u8,1u8,9u8,0u8,3u8,6u8, - 9u8,0u8,3u8,11u8,2u8,1u8,3u8,2u8,3u8,3u8,1u8,11u8,2u8,1u8,9u8,0u8,3u8,6u8,9u8,0u8, - 11u8,2u8,1u8,3u8,3u8,2u8,7u8,10u8,9u8,0u8,3u8,7u8,9u8,0u8,10u8,9u8,0u8,3u8,10u8,11u8, - 0u8,2u8,9u8,0u8,9u8,1u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,9u8,1u8,10u8,9u8,1u8,1u8, - 9u8,1u8,1u8,7u8,10u8,9u8,0u8,5u8,3u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8, - 9u8,1u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8, - 111u8,112u8,116u8,105u8,111u8,110u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,7u8,69u8,108u8,101u8,109u8,101u8,110u8, - 116u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,3u8,97u8,100u8,100u8,6u8,98u8,111u8,114u8,114u8, - 111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8, - 110u8,115u8,95u8,107u8,101u8,121u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8, - 121u8,95u8,101u8,109u8,112u8,116u8,121u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,4u8,102u8,105u8,110u8,100u8,6u8, - 108u8,101u8,110u8,103u8,116u8,104u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,11u8,116u8,111u8,95u8,118u8,101u8,99u8, - 95u8,112u8,97u8,105u8,114u8,6u8,117u8,112u8,115u8,101u8,114u8,116u8,3u8,107u8,101u8,121u8,5u8,118u8,97u8,108u8, - 117u8,101u8,4u8,100u8,97u8,116u8,97u8,7u8,105u8,115u8,95u8,110u8,111u8,110u8,101u8,16u8,105u8,110u8,118u8,97u8, - 108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8, - 7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,11u8,115u8, - 119u8,97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,7u8,114u8,101u8,118u8,101u8,114u8,115u8,101u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8, - 116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,98u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8, - 69u8,75u8,69u8,89u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,22u8, - 77u8,97u8,112u8,32u8,107u8,101u8,121u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8,115u8, - 116u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,75u8,69u8,89u8,95u8,78u8,79u8,84u8,95u8, - 70u8,79u8,85u8,78u8,68u8,20u8,77u8,97u8,112u8,32u8,107u8,101u8,121u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8, - 32u8,102u8,111u8,117u8,110u8,100u8,0u8,0u8,0u8,2u8,2u8,18u8,9u8,0u8,19u8,9u8,1u8,1u8,2u8,1u8, - 20u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,0u8,11u8,1u8,11u8,0u8,1u8,0u8,0u8,14u8,23u8,10u8, - 0u8,14u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8,0u8,12u8,4u8,14u8,4u8,56u8,1u8,4u8,11u8,5u8,16u8, - 11u8,0u8,1u8,7u8,0u8,17u8,12u8,39u8,11u8,0u8,54u8,0u8,11u8,1u8,11u8,2u8,57u8,0u8,68u8,16u8, - 2u8,1u8,1u8,0u8,0u8,17u8,22u8,10u8,0u8,11u8,1u8,56u8,0u8,12u8,3u8,14u8,3u8,56u8,2u8,4u8, - 8u8,5u8,13u8,11u8,0u8,1u8,7u8,1u8,17u8,12u8,39u8,13u8,3u8,56u8,3u8,12u8,2u8,11u8,0u8,55u8, - 0u8,11u8,2u8,66u8,16u8,55u8,1u8,2u8,2u8,1u8,0u8,0u8,20u8,25u8,10u8,0u8,11u8,1u8,12u8,2u8, - 46u8,11u8,2u8,56u8,0u8,12u8,4u8,14u8,4u8,56u8,2u8,4u8,11u8,5u8,16u8,11u8,0u8,1u8,7u8,1u8, - 17u8,12u8,39u8,13u8,4u8,56u8,3u8,12u8,3u8,11u8,0u8,54u8,0u8,11u8,3u8,67u8,16u8,54u8,1u8,2u8, - 3u8,1u8,0u8,0u8,8u8,7u8,11u8,0u8,11u8,1u8,56u8,0u8,12u8,2u8,14u8,2u8,56u8,2u8,2u8,4u8, - 1u8,0u8,0u8,1u8,3u8,64u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,1u8,2u8,5u8,1u8, - 0u8,0u8,1u8,4u8,11u8,0u8,58u8,1u8,70u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,6u8, - 0u8,0u8,0u8,21u8,37u8,10u8,0u8,55u8,0u8,65u8,16u8,12u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,12u8,2u8,10u8,2u8,10u8,3u8,35u8,4u8,31u8,5u8,11u8,10u8,0u8,55u8,0u8,10u8,2u8,66u8, - 16u8,55u8,2u8,10u8,1u8,33u8,4u8,26u8,11u8,0u8,1u8,11u8,1u8,1u8,11u8,2u8,56u8,4u8,2u8,11u8, - 2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,6u8,11u8,0u8,1u8,11u8,1u8, - 1u8,56u8,5u8,2u8,7u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,55u8,0u8,65u8,16u8,2u8,8u8,1u8,0u8, - 0u8,23u8,25u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8,56u8,0u8,12u8,3u8,14u8,3u8,56u8,2u8, - 4u8,11u8,5u8,16u8,11u8,0u8,1u8,7u8,1u8,17u8,12u8,39u8,13u8,3u8,56u8,3u8,12u8,4u8,11u8,0u8, - 54u8,0u8,11u8,4u8,56u8,6u8,58u8,0u8,2u8,9u8,1u8,0u8,0u8,25u8,40u8,64u8,19u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,12u8,2u8,64u8,26u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,11u8, - 0u8,58u8,1u8,12u8,4u8,13u8,4u8,56u8,7u8,11u8,4u8,12u8,5u8,14u8,5u8,65u8,16u8,12u8,3u8,10u8, - 3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,35u8,5u8,19u8,13u8,5u8,69u8,16u8,58u8, - 0u8,12u8,6u8,12u8,1u8,13u8,2u8,11u8,1u8,68u8,19u8,13u8,7u8,11u8,6u8,68u8,26u8,11u8,3u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,3u8,5u8,14u8,11u8,5u8,70u8,16u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,11u8,2u8,11u8,7u8,2u8,10u8,1u8,0u8,0u8,28u8,59u8,10u8,0u8,54u8,0u8, - 12u8,4u8,10u8,4u8,46u8,65u8,16u8,12u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8, - 10u8,5u8,10u8,6u8,35u8,4u8,48u8,5u8,14u8,10u8,4u8,10u8,5u8,12u8,3u8,46u8,11u8,3u8,66u8,16u8, - 55u8,2u8,14u8,1u8,33u8,4u8,43u8,11u8,0u8,1u8,10u8,4u8,11u8,1u8,11u8,2u8,57u8,0u8,68u8,16u8, - 10u8,4u8,11u8,5u8,11u8,6u8,71u8,16u8,11u8,4u8,69u8,16u8,58u8,0u8,12u8,7u8,56u8,8u8,11u8,7u8, - 56u8,9u8,2u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,9u8,11u8, - 4u8,1u8,11u8,0u8,54u8,0u8,11u8,1u8,11u8,2u8,57u8,0u8,68u8,16u8,56u8,10u8,56u8,11u8,2u8,1u8, - 0u8,0u8,1u8,0u8,0u8,0u8,11u8,1u8,11u8,2u8,11u8,0u8, - ]; - vector::push_back(&mut code, chunk20); - let chunk21 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,8u8,2u8,8u8,12u8,3u8,20u8,174u8,1u8, - 4u8,194u8,1u8,16u8,5u8,210u8,1u8,166u8,1u8,7u8,248u8,2u8,249u8,3u8,8u8,241u8,6u8,32u8,6u8,145u8, - 7u8,80u8,16u8,225u8,7u8,174u8,4u8,10u8,143u8,12u8,20u8,12u8,163u8,12u8,212u8,8u8,13u8,247u8,20u8,12u8, - 0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,4u8,0u8,2u8,28u8,7u8,2u8,0u8,0u8,0u8,0u8, - 0u8,5u8,0u8,1u8,0u8,0u8,6u8,2u8,1u8,0u8,0u8,7u8,3u8,1u8,0u8,0u8,8u8,4u8,1u8,0u8, - 0u8,9u8,0u8,1u8,0u8,0u8,10u8,4u8,5u8,0u8,0u8,11u8,1u8,6u8,0u8,0u8,12u8,7u8,6u8,0u8, - 0u8,13u8,0u8,1u8,0u8,0u8,14u8,6u8,8u8,0u8,0u8,15u8,9u8,1u8,0u8,0u8,16u8,0u8,1u8,0u8, - 0u8,17u8,10u8,11u8,0u8,0u8,18u8,10u8,1u8,0u8,0u8,19u8,4u8,1u8,0u8,0u8,20u8,2u8,1u8,0u8, - 0u8,21u8,3u8,1u8,0u8,0u8,22u8,1u8,12u8,0u8,0u8,23u8,10u8,1u8,0u8,0u8,24u8,10u8,1u8,0u8, - 0u8,25u8,13u8,8u8,0u8,0u8,26u8,14u8,8u8,0u8,2u8,30u8,17u8,18u8,2u8,4u8,4u8,1u8,31u8,1u8, - 1u8,0u8,1u8,32u8,1u8,1u8,0u8,2u8,33u8,20u8,8u8,2u8,4u8,4u8,2u8,34u8,22u8,5u8,2u8,4u8, - 4u8,2u8,11u8,8u8,23u8,2u8,4u8,4u8,3u8,35u8,25u8,21u8,1u8,0u8,3u8,36u8,26u8,27u8,1u8,0u8, - 2u8,36u8,17u8,28u8,2u8,4u8,4u8,2u8,37u8,22u8,30u8,2u8,4u8,4u8,22u8,16u8,25u8,16u8,26u8,16u8, - 27u8,16u8,28u8,19u8,29u8,19u8,30u8,16u8,31u8,16u8,3u8,7u8,8u8,0u8,5u8,3u8,1u8,3u8,2u8,6u8, - 8u8,0u8,3u8,3u8,6u8,8u8,0u8,3u8,3u8,2u8,6u8,8u8,0u8,5u8,1u8,1u8,1u8,8u8,0u8,2u8, - 3u8,3u8,0u8,4u8,6u8,8u8,0u8,3u8,3u8,3u8,1u8,6u8,8u8,0u8,1u8,10u8,5u8,1u8,4u8,4u8, - 7u8,8u8,0u8,5u8,5u8,3u8,2u8,7u8,8u8,0u8,3u8,5u8,5u8,3u8,3u8,3u8,7u8,3u8,2u8,5u8, - 3u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,7u8,9u8,1u8,1u8,5u8,3u8, - 7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,2u8,1u8,3u8,2u8,6u8,11u8,1u8,2u8, - 9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,5u8,5u8,5u8,7u8,3u8, - 3u8,3u8,2u8,6u8,10u8,9u8,0u8,6u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8,3u8,1u8,9u8,0u8,2u8, - 9u8,0u8,9u8,1u8,4u8,5u8,5u8,3u8,3u8,1u8,6u8,9u8,1u8,2u8,5u8,5u8,8u8,112u8,111u8,111u8, - 108u8,95u8,117u8,54u8,52u8,5u8,101u8,114u8,114u8,111u8,114u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8, - 97u8,112u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,4u8,80u8,111u8,111u8,108u8,10u8,97u8,100u8,100u8,95u8,115u8, - 104u8,97u8,114u8,101u8,115u8,16u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,116u8,111u8,95u8,115u8,104u8,97u8,114u8, - 101u8,115u8,33u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,116u8,111u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8, - 119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,7u8,98u8,97u8,108u8, - 97u8,110u8,99u8,101u8,6u8,98u8,117u8,121u8,95u8,105u8,110u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8, - 6u8,99u8,114u8,101u8,97u8,116u8,101u8,26u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8, - 115u8,99u8,97u8,108u8,105u8,110u8,103u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,13u8,100u8,101u8,100u8,117u8,99u8, - 116u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8, - 116u8,121u8,20u8,109u8,117u8,108u8,116u8,105u8,112u8,108u8,121u8,95u8,116u8,104u8,101u8,110u8,95u8,100u8,105u8,118u8, - 105u8,100u8,101u8,13u8,114u8,101u8,100u8,101u8,101u8,109u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,12u8,115u8,104u8, - 97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,18u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8, - 101u8,114u8,115u8,95u8,99u8,111u8,117u8,110u8,116u8,6u8,115u8,104u8,97u8,114u8,101u8,115u8,16u8,115u8,104u8,97u8, - 114u8,101u8,115u8,95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,33u8,115u8,104u8,97u8,114u8,101u8,115u8, - 95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8, - 108u8,95u8,99u8,111u8,105u8,110u8,115u8,7u8,116u8,111u8,95u8,117u8,49u8,50u8,56u8,11u8,116u8,111u8,116u8,97u8, - 108u8,95u8,99u8,111u8,105u8,110u8,115u8,12u8,116u8,111u8,116u8,97u8,108u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8, - 15u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,18u8,117u8,112u8,100u8, - 97u8,116u8,101u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,18u8,115u8,104u8,97u8,114u8, - 101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,95u8,108u8,105u8,109u8,105u8,116u8,9u8,83u8,105u8,109u8,112u8,108u8, - 101u8,77u8,97u8,112u8,14u8,115u8,99u8,97u8,108u8,105u8,110u8,103u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,10u8, - 98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8, - 114u8,103u8,117u8,109u8,101u8,110u8,116u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8, - 101u8,3u8,97u8,100u8,100u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,8u8,105u8, - 110u8,100u8,101u8,120u8,95u8,111u8,102u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8, - 119u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8, - 58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,153u8,4u8,7u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,69u8,83u8,72u8,65u8,82u8,69u8,72u8,79u8,76u8,68u8,69u8,82u8,95u8,78u8,79u8,84u8, - 95u8,70u8,79u8,85u8,78u8,68u8,32u8,83u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,32u8,110u8, - 111u8,116u8,32u8,112u8,114u8,101u8,115u8,101u8,110u8,116u8,32u8,105u8,110u8,32u8,112u8,111u8,111u8,108u8,46u8,2u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,84u8,79u8,79u8,95u8,77u8,65u8,78u8,89u8,95u8,83u8,72u8, - 65u8,82u8,69u8,72u8,79u8,76u8,68u8,69u8,82u8,83u8,44u8,84u8,104u8,101u8,114u8,101u8,32u8,97u8,114u8,101u8, - 32u8,116u8,111u8,111u8,32u8,109u8,97u8,110u8,121u8,32u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8, - 114u8,115u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,112u8,111u8,111u8,108u8,46u8,3u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,18u8,69u8,80u8,79u8,79u8,76u8,95u8,73u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8, - 84u8,89u8,30u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,110u8,111u8, - 110u8,45u8,101u8,109u8,112u8,116u8,121u8,32u8,112u8,111u8,111u8,108u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,20u8,69u8,73u8,78u8,83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,83u8,72u8,65u8,82u8, - 69u8,83u8,63u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,114u8,101u8,100u8,101u8,101u8,109u8,32u8,109u8,111u8,114u8, - 101u8,32u8,115u8,104u8,97u8,114u8,101u8,115u8,32u8,116u8,104u8,97u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,104u8, - 97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,32u8,104u8,97u8,115u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8, - 32u8,112u8,111u8,111u8,108u8,46u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,83u8,72u8,65u8,82u8, - 69u8,72u8,79u8,76u8,68u8,69u8,82u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,79u8,86u8,69u8,82u8,70u8, - 76u8,79u8,87u8,49u8,83u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,32u8,99u8,97u8,110u8,110u8, - 111u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8,117u8,54u8, - 52u8,46u8,109u8,97u8,120u8,32u8,115u8,104u8,97u8,114u8,101u8,115u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,26u8,69u8,80u8,79u8,79u8,76u8,95u8,84u8,79u8,84u8,65u8,76u8,95u8,67u8,79u8,73u8,78u8,83u8,95u8, - 79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,41u8,80u8,111u8,111u8,108u8,39u8,115u8,32u8,116u8,111u8,116u8,97u8, - 108u8,32u8,99u8,111u8,105u8,110u8,115u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,101u8,120u8,99u8,101u8,101u8, - 100u8,32u8,117u8,54u8,52u8,46u8,109u8,97u8,120u8,46u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8, - 80u8,79u8,79u8,76u8,95u8,84u8,79u8,84u8,65u8,76u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,79u8,86u8, - 69u8,82u8,70u8,76u8,79u8,87u8,42u8,80u8,111u8,111u8,108u8,39u8,115u8,32u8,116u8,111u8,116u8,97u8,108u8,32u8, - 115u8,104u8,97u8,114u8,101u8,115u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8, - 32u8,117u8,54u8,52u8,46u8,109u8,97u8,120u8,46u8,0u8,0u8,0u8,2u8,6u8,27u8,3u8,23u8,3u8,24u8,3u8, - 19u8,11u8,1u8,2u8,5u8,3u8,17u8,10u8,5u8,29u8,3u8,0u8,0u8,0u8,0u8,15u8,74u8,10u8,0u8,10u8, - 1u8,12u8,3u8,46u8,11u8,3u8,17u8,5u8,4u8,36u8,11u8,0u8,15u8,0u8,14u8,1u8,56u8,0u8,12u8,7u8, - 10u8,7u8,20u8,12u8,6u8,7u8,7u8,10u8,6u8,23u8,10u8,2u8,38u8,4u8,22u8,5u8,27u8,11u8,7u8,1u8, - 7u8,5u8,17u8,23u8,39u8,11u8,6u8,11u8,2u8,22u8,10u8,7u8,21u8,11u8,7u8,20u8,12u8,5u8,5u8,72u8, - 10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,66u8,10u8,0u8,16u8,1u8,65u8,19u8, - 10u8,0u8,16u8,2u8,20u8,35u8,4u8,49u8,5u8,54u8,11u8,0u8,1u8,7u8,6u8,17u8,24u8,39u8,10u8,0u8, - 15u8,1u8,10u8,1u8,68u8,19u8,11u8,0u8,15u8,0u8,11u8,1u8,10u8,2u8,56u8,1u8,11u8,2u8,12u8,4u8, - 5u8,70u8,11u8,0u8,1u8,11u8,2u8,12u8,4u8,11u8,4u8,12u8,5u8,11u8,5u8,2u8,1u8,1u8,0u8,0u8, - 8u8,7u8,10u8,0u8,11u8,1u8,11u8,0u8,16u8,3u8,20u8,17u8,2u8,2u8,2u8,1u8,0u8,0u8,21u8,34u8, - 10u8,0u8,16u8,3u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,8u8,12u8,3u8, - 5u8,15u8,10u8,0u8,16u8,4u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8,3u8,11u8, - 3u8,4u8,24u8,11u8,1u8,11u8,0u8,16u8,5u8,20u8,24u8,12u8,4u8,5u8,32u8,10u8,0u8,11u8,1u8,11u8, - 0u8,16u8,4u8,20u8,11u8,2u8,17u8,10u8,12u8,4u8,11u8,4u8,2u8,3u8,1u8,0u8,0u8,1u8,8u8,10u8, - 0u8,11u8,1u8,17u8,14u8,12u8,2u8,11u8,0u8,11u8,2u8,17u8,15u8,2u8,4u8,1u8,0u8,0u8,7u8,66u8, - 10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,8u8,11u8,0u8,1u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8,0u8,10u8,2u8,12u8,3u8,46u8,11u8,3u8,17u8,1u8,12u8,4u8, - 7u8,7u8,10u8,0u8,16u8,3u8,20u8,23u8,10u8,2u8,38u8,4u8,24u8,5u8,29u8,11u8,0u8,1u8,7u8,2u8, - 17u8,23u8,39u8,7u8,7u8,10u8,0u8,16u8,4u8,20u8,23u8,10u8,4u8,38u8,4u8,38u8,5u8,43u8,11u8,0u8, - 1u8,7u8,2u8,17u8,23u8,39u8,10u8,0u8,16u8,3u8,20u8,11u8,2u8,22u8,10u8,0u8,15u8,3u8,21u8,10u8, - 0u8,16u8,4u8,20u8,10u8,4u8,22u8,10u8,0u8,15u8,4u8,21u8,11u8,0u8,11u8,1u8,10u8,4u8,17u8,0u8, - 1u8,11u8,4u8,2u8,5u8,1u8,0u8,0u8,8u8,5u8,11u8,0u8,16u8,0u8,14u8,1u8,56u8,2u8,2u8,6u8, - 1u8,0u8,0u8,8u8,4u8,11u8,0u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,7u8,2u8,7u8, - 1u8,0u8,0u8,8u8,8u8,11u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,56u8,3u8,64u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,1u8,18u8, - 0u8,2u8,8u8,0u8,0u8,0u8,24u8,68u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,17u8,5u8,4u8, - 8u8,5u8,13u8,11u8,0u8,1u8,7u8,4u8,17u8,23u8,39u8,10u8,0u8,10u8,1u8,12u8,4u8,46u8,11u8,4u8, - 17u8,14u8,10u8,2u8,38u8,4u8,23u8,5u8,28u8,11u8,0u8,1u8,7u8,0u8,17u8,23u8,39u8,10u8,0u8,15u8, - 0u8,14u8,1u8,56u8,0u8,12u8,5u8,10u8,5u8,20u8,11u8,2u8,23u8,10u8,5u8,21u8,11u8,5u8,20u8,12u8, - 6u8,10u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,64u8,10u8,0u8,16u8,1u8,14u8, - 1u8,56u8,4u8,12u8,7u8,1u8,10u8,0u8,15u8,1u8,11u8,7u8,56u8,5u8,1u8,11u8,0u8,15u8,0u8,14u8, - 1u8,56u8,6u8,1u8,1u8,5u8,66u8,11u8,0u8,1u8,11u8,6u8,2u8,9u8,1u8,0u8,0u8,8u8,19u8,14u8, - 0u8,16u8,3u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,5u8,10u8,7u8,1u8, - 17u8,24u8,39u8,11u8,0u8,19u8,0u8,1u8,1u8,1u8,1u8,1u8,1u8,2u8,10u8,1u8,0u8,0u8,8u8,10u8, - 11u8,1u8,17u8,17u8,11u8,2u8,17u8,17u8,24u8,11u8,3u8,17u8,17u8,26u8,52u8,2u8,11u8,1u8,0u8,0u8, - 29u8,66u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,17u8,5u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8, - 7u8,4u8,17u8,23u8,39u8,10u8,0u8,10u8,1u8,12u8,4u8,46u8,11u8,4u8,17u8,14u8,10u8,2u8,38u8,4u8, - 23u8,5u8,28u8,11u8,0u8,1u8,7u8,0u8,17u8,23u8,39u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,36u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8,0u8, - 10u8,2u8,12u8,5u8,46u8,11u8,5u8,17u8,15u8,12u8,6u8,10u8,0u8,16u8,3u8,20u8,10u8,6u8,23u8,10u8, - 0u8,15u8,3u8,21u8,10u8,0u8,16u8,4u8,20u8,10u8,2u8,23u8,10u8,0u8,15u8,4u8,21u8,11u8,0u8,11u8, - 1u8,11u8,2u8,17u8,8u8,1u8,11u8,6u8,2u8,12u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,1u8,20u8, - 2u8,13u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,1u8,65u8,19u8,2u8,14u8,1u8,0u8,0u8,1u8,17u8, - 10u8,0u8,10u8,1u8,17u8,5u8,4u8,11u8,11u8,0u8,16u8,0u8,14u8,1u8,56u8,7u8,20u8,12u8,2u8,5u8, - 15u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,11u8,2u8,2u8,15u8,1u8, - 0u8,0u8,8u8,7u8,10u8,0u8,11u8,1u8,11u8,0u8,16u8,3u8,20u8,17u8,16u8,2u8,16u8,1u8,0u8,0u8, - 21u8,32u8,10u8,0u8,16u8,3u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,8u8, - 12u8,3u8,5u8,15u8,10u8,0u8,16u8,4u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8, - 3u8,11u8,3u8,4u8,22u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,5u8, - 30u8,10u8,0u8,11u8,1u8,11u8,2u8,11u8,0u8,16u8,4u8,20u8,17u8,10u8,12u8,4u8,11u8,4u8,2u8,17u8, - 0u8,0u8,0u8,8u8,3u8,11u8,0u8,53u8,2u8,18u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,3u8,20u8, - 2u8,19u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,4u8,20u8,2u8,20u8,1u8,0u8,0u8,31u8,46u8,10u8, - 0u8,10u8,1u8,12u8,4u8,46u8,11u8,4u8,17u8,5u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,4u8,17u8, - 23u8,39u8,10u8,0u8,10u8,1u8,12u8,5u8,46u8,11u8,5u8,17u8,14u8,10u8,3u8,38u8,4u8,23u8,5u8,28u8, - 11u8,0u8,1u8,7u8,0u8,17u8,23u8,39u8,10u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8, - 4u8,35u8,11u8,0u8,1u8,2u8,10u8,0u8,11u8,1u8,10u8,3u8,17u8,8u8,1u8,11u8,0u8,11u8,2u8,11u8, - 3u8,17u8,0u8,1u8,2u8,21u8,1u8,0u8,0u8,8u8,5u8,11u8,1u8,11u8,0u8,15u8,3u8,21u8,2u8,0u8, - 3u8,0u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,5u8,0u8, - ]; - vector::push_back(&mut code, chunk21); - let chunk22 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,12u8,3u8,18u8,181u8,1u8, - 4u8,199u8,1u8,16u8,5u8,215u8,1u8,183u8,1u8,7u8,142u8,3u8,134u8,4u8,8u8,148u8,7u8,32u8,6u8,180u8, - 7u8,98u8,16u8,150u8,8u8,174u8,4u8,10u8,196u8,12u8,15u8,12u8,211u8,12u8,189u8,9u8,13u8,144u8,22u8,8u8, - 0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,4u8,0u8,2u8,27u8,4u8,2u8,3u8,1u8,0u8,1u8,0u8,4u8, - 0u8,1u8,0u8,0u8,5u8,2u8,1u8,0u8,0u8,6u8,3u8,1u8,0u8,0u8,7u8,4u8,5u8,0u8,0u8,8u8, - 6u8,1u8,0u8,0u8,9u8,4u8,7u8,0u8,0u8,10u8,8u8,9u8,0u8,0u8,11u8,5u8,9u8,0u8,0u8,12u8, - 0u8,1u8,0u8,0u8,13u8,9u8,8u8,0u8,0u8,14u8,10u8,1u8,0u8,0u8,15u8,0u8,5u8,0u8,0u8,16u8, - 11u8,5u8,0u8,0u8,17u8,4u8,1u8,0u8,0u8,18u8,12u8,5u8,0u8,0u8,19u8,13u8,5u8,0u8,0u8,20u8, - 14u8,5u8,0u8,0u8,21u8,5u8,1u8,0u8,0u8,22u8,1u8,15u8,0u8,0u8,23u8,11u8,5u8,0u8,0u8,24u8, - 11u8,1u8,0u8,0u8,25u8,16u8,8u8,0u8,0u8,26u8,17u8,8u8,0u8,2u8,29u8,20u8,21u8,2u8,3u8,0u8, - 1u8,30u8,5u8,5u8,0u8,2u8,31u8,22u8,8u8,2u8,3u8,0u8,2u8,9u8,25u8,7u8,2u8,3u8,0u8,2u8, - 32u8,8u8,26u8,2u8,3u8,4u8,2u8,33u8,20u8,28u8,2u8,3u8,0u8,1u8,34u8,5u8,5u8,0u8,2u8,13u8, - 26u8,8u8,2u8,3u8,0u8,2u8,35u8,31u8,5u8,2u8,3u8,0u8,2u8,36u8,25u8,32u8,2u8,3u8,0u8,23u8, - 19u8,25u8,19u8,26u8,19u8,27u8,19u8,28u8,19u8,30u8,19u8,31u8,19u8,32u8,19u8,3u8,7u8,8u8,0u8,5u8, - 4u8,1u8,4u8,2u8,6u8,8u8,0u8,3u8,3u8,6u8,8u8,0u8,3u8,3u8,2u8,6u8,8u8,0u8,5u8,1u8, - 3u8,3u8,7u8,8u8,0u8,5u8,3u8,1u8,1u8,0u8,1u8,8u8,0u8,4u8,6u8,8u8,0u8,4u8,4u8,4u8, - 1u8,6u8,8u8,0u8,2u8,6u8,8u8,0u8,4u8,3u8,6u8,8u8,0u8,4u8,3u8,4u8,6u8,8u8,0u8,4u8, - 3u8,4u8,1u8,15u8,4u8,7u8,8u8,0u8,5u8,5u8,4u8,2u8,7u8,8u8,0u8,3u8,5u8,5u8,4u8,4u8, - 4u8,7u8,4u8,2u8,5u8,4u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8, - 1u8,3u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,2u8,1u8,4u8,2u8,3u8,4u8, - 2u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,4u8, - 5u8,5u8,7u8,4u8,4u8,1u8,9u8,1u8,1u8,11u8,1u8,2u8,5u8,4u8,4u8,5u8,5u8,4u8,3u8,1u8, - 6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,1u8,6u8,9u8,1u8,2u8,1u8,3u8,2u8,5u8,5u8,16u8,112u8, - 111u8,111u8,108u8,95u8,117u8,54u8,52u8,95u8,117u8,110u8,98u8,111u8,117u8,110u8,100u8,5u8,101u8,114u8,114u8,111u8, - 114u8,17u8,116u8,97u8,98u8,108u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,108u8,101u8,110u8,103u8,116u8,104u8,4u8, - 80u8,111u8,111u8,108u8,10u8,97u8,100u8,100u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,16u8,97u8,109u8,111u8,117u8, - 110u8,116u8,95u8,116u8,111u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,33u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8, - 116u8,111u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8,108u8, - 95u8,99u8,111u8,105u8,110u8,115u8,7u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,6u8,98u8,117u8,121u8,95u8,105u8, - 110u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,26u8,99u8,114u8, - 101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,115u8,99u8,97u8,108u8,105u8,110u8,103u8,95u8,102u8,97u8, - 99u8,116u8,111u8,114u8,13u8,100u8,101u8,100u8,117u8,99u8,116u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,13u8,100u8, - 101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8,121u8,20u8,109u8,117u8,108u8,116u8,105u8,112u8,108u8, - 121u8,95u8,116u8,104u8,101u8,110u8,95u8,100u8,105u8,118u8,105u8,100u8,101u8,13u8,114u8,101u8,100u8,101u8,101u8,109u8, - 95u8,115u8,104u8,97u8,114u8,101u8,115u8,18u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8, - 95u8,99u8,111u8,117u8,110u8,116u8,6u8,115u8,104u8,97u8,114u8,101u8,115u8,16u8,115u8,104u8,97u8,114u8,101u8,115u8, - 95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,33u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,116u8,111u8, - 95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8, - 111u8,105u8,110u8,115u8,33u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8, - 116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,115u8,116u8,97u8,116u8,115u8,7u8,116u8, - 111u8,95u8,117u8,49u8,50u8,56u8,7u8,116u8,111u8,95u8,117u8,50u8,53u8,54u8,11u8,116u8,111u8,116u8,97u8,108u8, - 95u8,99u8,111u8,105u8,110u8,115u8,12u8,116u8,111u8,116u8,97u8,108u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,15u8, - 116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,18u8,117u8,112u8,100u8,97u8, - 116u8,101u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,15u8,84u8,97u8,98u8,108u8,101u8, - 87u8,105u8,116u8,104u8,76u8,101u8,110u8,103u8,116u8,104u8,14u8,115u8,99u8,97u8,108u8,105u8,110u8,103u8,95u8,102u8, - 97u8,99u8,116u8,111u8,114u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,16u8,105u8,110u8,118u8, - 97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,3u8,97u8,100u8,100u8,3u8,110u8,101u8, - 119u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8, - 116u8,101u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,16u8, - 255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,3u8,8u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,255u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8, - 116u8,97u8,95u8,118u8,49u8,153u8,4u8,7u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,83u8,72u8, - 65u8,82u8,69u8,72u8,79u8,76u8,68u8,69u8,82u8,95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,32u8, - 83u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,32u8,110u8,111u8,116u8,32u8,112u8,114u8,101u8,115u8, - 101u8,110u8,116u8,32u8,105u8,110u8,32u8,112u8,111u8,111u8,108u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,69u8,84u8,79u8,79u8,95u8,77u8,65u8,78u8,89u8,95u8,83u8,72u8,65u8,82u8,69u8,72u8,79u8,76u8,68u8, - 69u8,82u8,83u8,44u8,84u8,104u8,101u8,114u8,101u8,32u8,97u8,114u8,101u8,32u8,116u8,111u8,111u8,32u8,109u8,97u8, - 110u8,121u8,32u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,32u8,105u8,110u8,32u8,116u8, - 104u8,101u8,32u8,112u8,111u8,111u8,108u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,80u8,79u8, - 79u8,76u8,95u8,73u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8,84u8,89u8,30u8,67u8,97u8,110u8,110u8, - 111u8,116u8,32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,110u8,111u8,110u8,45u8,101u8,109u8,112u8,116u8,121u8, - 32u8,112u8,111u8,111u8,108u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,73u8,78u8,83u8,85u8, - 70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,63u8,67u8,97u8,110u8,110u8, - 111u8,116u8,32u8,114u8,101u8,100u8,101u8,101u8,109u8,32u8,109u8,111u8,114u8,101u8,32u8,115u8,104u8,97u8,114u8,101u8, - 115u8,32u8,116u8,104u8,97u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8, - 101u8,114u8,32u8,104u8,97u8,115u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,112u8,111u8,111u8,108u8,46u8,5u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,83u8,72u8,65u8,82u8,69u8,72u8,79u8,76u8,68u8,69u8,82u8, - 95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,49u8,83u8,104u8,97u8, - 114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,104u8,97u8,118u8,101u8, - 32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8,117u8,54u8,52u8,46u8,109u8,97u8,120u8,32u8,115u8, - 104u8,97u8,114u8,101u8,115u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,80u8,79u8,79u8,76u8, - 95u8,84u8,79u8,84u8,65u8,76u8,95u8,67u8,79u8,73u8,78u8,83u8,95u8,79u8,86u8,69u8,82u8,70u8,76u8,79u8, - 87u8,41u8,80u8,111u8,111u8,108u8,39u8,115u8,32u8,116u8,111u8,116u8,97u8,108u8,32u8,99u8,111u8,105u8,110u8,115u8, - 32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,32u8,117u8,54u8,52u8,46u8,109u8, - 97u8,120u8,46u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,80u8,79u8,79u8,76u8,95u8,84u8,79u8, - 84u8,65u8,76u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,42u8, - 80u8,111u8,111u8,108u8,39u8,115u8,32u8,116u8,111u8,116u8,97u8,108u8,32u8,115u8,104u8,97u8,114u8,101u8,115u8,32u8, - 99u8,97u8,110u8,110u8,111u8,116u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,32u8,117u8,54u8,52u8,46u8,109u8,97u8, - 120u8,46u8,0u8,0u8,0u8,2u8,4u8,23u8,3u8,24u8,4u8,17u8,11u8,1u8,2u8,5u8,4u8,28u8,3u8,0u8, - 0u8,0u8,0u8,18u8,56u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,17u8,5u8,4u8,36u8,11u8,0u8, - 15u8,0u8,11u8,1u8,56u8,0u8,12u8,7u8,10u8,7u8,20u8,12u8,6u8,7u8,7u8,10u8,6u8,23u8,10u8,2u8, - 38u8,4u8,22u8,5u8,27u8,11u8,7u8,1u8,7u8,5u8,17u8,24u8,39u8,11u8,6u8,11u8,2u8,22u8,10u8,7u8, - 21u8,11u8,7u8,20u8,12u8,5u8,5u8,54u8,10u8,2u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,48u8,11u8,0u8,15u8,0u8,11u8,1u8,10u8,2u8,56u8,1u8, - 11u8,2u8,12u8,4u8,5u8,52u8,11u8,0u8,1u8,11u8,2u8,12u8,4u8,11u8,4u8,12u8,5u8,11u8,5u8,2u8, - 1u8,1u8,0u8,0u8,8u8,7u8,10u8,0u8,11u8,1u8,11u8,0u8,16u8,1u8,20u8,17u8,2u8,2u8,2u8,1u8, - 0u8,0u8,23u8,38u8,10u8,0u8,16u8,1u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8, - 9u8,8u8,12u8,3u8,5u8,15u8,10u8,0u8,16u8,2u8,20u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8,3u8,11u8,3u8,4u8,26u8,11u8,1u8,17u8,17u8,11u8, - 0u8,16u8,3u8,20u8,17u8,17u8,24u8,12u8,4u8,5u8,36u8,10u8,0u8,11u8,1u8,17u8,17u8,11u8,0u8,16u8, - 2u8,20u8,11u8,2u8,17u8,17u8,17u8,10u8,12u8,4u8,11u8,4u8,2u8,3u8,1u8,0u8,0u8,1u8,8u8,10u8, - 0u8,11u8,1u8,17u8,13u8,12u8,2u8,11u8,0u8,11u8,2u8,17u8,14u8,2u8,4u8,1u8,0u8,0u8,24u8,66u8, - 10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,8u8,11u8,0u8,1u8,50u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8,0u8,10u8,2u8,12u8, - 3u8,46u8,11u8,3u8,17u8,1u8,12u8,4u8,7u8,8u8,10u8,0u8,16u8,1u8,20u8,23u8,10u8,2u8,38u8,4u8, - 24u8,5u8,29u8,11u8,0u8,1u8,7u8,2u8,17u8,24u8,39u8,7u8,7u8,10u8,0u8,16u8,2u8,20u8,23u8,10u8, - 4u8,38u8,4u8,38u8,5u8,43u8,11u8,0u8,1u8,7u8,3u8,17u8,24u8,39u8,10u8,0u8,16u8,1u8,20u8,11u8, - 2u8,22u8,10u8,0u8,15u8,1u8,21u8,10u8,0u8,16u8,2u8,20u8,10u8,4u8,22u8,10u8,0u8,15u8,2u8,21u8, - 11u8,0u8,11u8,1u8,10u8,4u8,17u8,0u8,1u8,11u8,4u8,2u8,5u8,1u8,0u8,0u8,8u8,5u8,11u8,0u8, - 16u8,0u8,11u8,1u8,56u8,2u8,2u8,6u8,1u8,0u8,0u8,8u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,17u8,7u8,2u8,7u8,1u8,0u8,0u8,8u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,56u8,3u8,11u8, - 0u8,18u8,0u8,2u8,8u8,0u8,0u8,0u8,27u8,56u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,17u8, - 5u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,4u8,17u8,24u8,39u8,10u8,0u8,10u8,1u8,12u8,4u8,46u8, - 11u8,4u8,17u8,13u8,10u8,2u8,38u8,4u8,23u8,5u8,28u8,11u8,0u8,1u8,7u8,0u8,17u8,24u8,39u8,10u8, - 0u8,15u8,0u8,10u8,1u8,56u8,0u8,12u8,5u8,10u8,5u8,20u8,11u8,2u8,23u8,10u8,5u8,21u8,11u8,5u8, - 20u8,12u8,6u8,10u8,6u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,52u8,11u8,0u8,15u8,0u8,11u8,1u8,56u8,4u8,1u8,5u8,54u8,11u8,0u8,1u8,11u8, - 6u8,2u8,9u8,1u8,0u8,0u8,29u8,19u8,14u8,0u8,16u8,1u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,7u8,5u8,10u8,7u8,1u8,17u8,29u8,39u8,11u8,0u8,19u8,0u8,1u8,12u8,1u8,1u8, - 1u8,11u8,1u8,56u8,5u8,2u8,10u8,1u8,0u8,0u8,8u8,10u8,11u8,1u8,17u8,18u8,11u8,2u8,17u8,18u8, - 24u8,11u8,3u8,17u8,18u8,26u8,53u8,2u8,11u8,1u8,0u8,0u8,30u8,66u8,10u8,0u8,10u8,1u8,12u8,3u8, - 46u8,11u8,3u8,17u8,5u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,4u8,17u8,24u8,39u8,10u8,0u8,10u8, - 1u8,12u8,4u8,46u8,11u8,4u8,17u8,13u8,10u8,2u8,38u8,4u8,23u8,5u8,28u8,11u8,0u8,1u8,7u8,0u8, - 17u8,24u8,39u8,10u8,2u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,36u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8,0u8, - 10u8,2u8,12u8,5u8,46u8,11u8,5u8,17u8,14u8,12u8,6u8,10u8,0u8,16u8,1u8,20u8,10u8,6u8,23u8,10u8, - 0u8,15u8,1u8,21u8,10u8,0u8,16u8,2u8,20u8,10u8,2u8,23u8,10u8,0u8,15u8,2u8,21u8,11u8,0u8,11u8, - 1u8,11u8,2u8,17u8,8u8,1u8,11u8,6u8,2u8,12u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,0u8,56u8, - 6u8,2u8,13u8,1u8,0u8,0u8,1u8,17u8,10u8,0u8,10u8,1u8,17u8,5u8,4u8,11u8,11u8,0u8,16u8,0u8, - 11u8,1u8,56u8,7u8,20u8,12u8,2u8,5u8,15u8,11u8,0u8,1u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,11u8,2u8,2u8,14u8,1u8,0u8,0u8,8u8,7u8, - 10u8,0u8,11u8,1u8,11u8,0u8,16u8,1u8,20u8,17u8,15u8,2u8,15u8,1u8,0u8,0u8,33u8,34u8,10u8,0u8, - 16u8,1u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,8u8,12u8,3u8,5u8,15u8, - 10u8,0u8,16u8,2u8,20u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,12u8,3u8,11u8,3u8,4u8,22u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,4u8,5u8,32u8,10u8,0u8,11u8,1u8,11u8,2u8,17u8,17u8,11u8,0u8,16u8,2u8,20u8,17u8,10u8, - 52u8,12u8,4u8,11u8,4u8,2u8,16u8,1u8,0u8,0u8,33u8,30u8,10u8,0u8,16u8,1u8,20u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,8u8,12u8,4u8,5u8,13u8,10u8,3u8,50u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8,4u8,11u8,4u8,4u8,20u8, - 11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,5u8,28u8,11u8,0u8,11u8,1u8, - 11u8,2u8,17u8,17u8,11u8,3u8,17u8,10u8,52u8,12u8,5u8,11u8,5u8,2u8,17u8,0u8,0u8,0u8,8u8,3u8, - 11u8,0u8,53u8,2u8,18u8,0u8,0u8,0u8,8u8,3u8,11u8,0u8,77u8,2u8,19u8,1u8,0u8,0u8,8u8,4u8, - 11u8,0u8,16u8,1u8,20u8,2u8,20u8,1u8,0u8,0u8,8u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,21u8,1u8, - 0u8,0u8,34u8,46u8,10u8,0u8,10u8,1u8,12u8,4u8,46u8,11u8,4u8,17u8,5u8,4u8,8u8,5u8,13u8,11u8, - 0u8,1u8,7u8,4u8,17u8,24u8,39u8,10u8,0u8,10u8,1u8,12u8,5u8,46u8,11u8,5u8,17u8,13u8,10u8,3u8, - 38u8,4u8,23u8,5u8,28u8,11u8,0u8,1u8,7u8,0u8,17u8,24u8,39u8,10u8,3u8,50u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,35u8,11u8,0u8,1u8,2u8,10u8, - 0u8,11u8,1u8,10u8,3u8,17u8,8u8,1u8,11u8,0u8,11u8,2u8,11u8,3u8,17u8,0u8,1u8,2u8,22u8,1u8, - 0u8,0u8,8u8,5u8,11u8,1u8,11u8,0u8,15u8,1u8,21u8,2u8,0u8,2u8,0u8,0u8,0u8,1u8,0u8,3u8, - 0u8, - ]; - vector::push_back(&mut code, chunk22); - let chunk23 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,8u8,2u8,8u8,18u8,3u8,26u8,242u8,2u8, - 4u8,140u8,3u8,18u8,5u8,158u8,3u8,203u8,1u8,7u8,233u8,4u8,248u8,11u8,8u8,225u8,16u8,32u8,6u8,129u8, - 17u8,238u8,6u8,16u8,239u8,23u8,254u8,1u8,10u8,237u8,25u8,17u8,12u8,254u8,25u8,184u8,8u8,13u8,182u8,34u8, - 4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,7u8,0u8,0u8,5u8,2u8,0u8,0u8,6u8,7u8, - 0u8,2u8,15u8,7u8,1u8,0u8,0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,0u8,2u8,0u8,0u8,9u8,3u8, - 1u8,0u8,0u8,10u8,4u8,5u8,0u8,0u8,11u8,6u8,1u8,0u8,0u8,12u8,7u8,5u8,0u8,0u8,13u8,8u8, - 1u8,0u8,0u8,14u8,9u8,5u8,2u8,0u8,0u8,0u8,16u8,7u8,10u8,0u8,0u8,17u8,7u8,11u8,0u8,0u8, - 18u8,7u8,5u8,0u8,0u8,19u8,7u8,11u8,0u8,0u8,20u8,7u8,1u8,0u8,0u8,21u8,7u8,5u8,0u8,0u8, - 22u8,7u8,12u8,0u8,0u8,23u8,7u8,13u8,0u8,0u8,24u8,14u8,13u8,0u8,0u8,25u8,5u8,13u8,0u8,0u8, - 26u8,15u8,13u8,0u8,0u8,27u8,7u8,12u8,0u8,0u8,28u8,7u8,12u8,0u8,0u8,29u8,16u8,1u8,0u8,0u8, - 30u8,17u8,18u8,0u8,0u8,31u8,19u8,5u8,0u8,0u8,32u8,20u8,2u8,0u8,0u8,33u8,20u8,7u8,0u8,0u8, - 34u8,21u8,1u8,0u8,0u8,35u8,7u8,22u8,0u8,0u8,36u8,16u8,23u8,0u8,0u8,37u8,0u8,1u8,0u8,0u8, - 38u8,0u8,2u8,0u8,0u8,39u8,0u8,5u8,0u8,0u8,40u8,7u8,23u8,0u8,0u8,41u8,24u8,1u8,0u8,0u8, - 42u8,25u8,18u8,0u8,0u8,43u8,26u8,5u8,0u8,0u8,44u8,20u8,1u8,0u8,0u8,45u8,18u8,18u8,0u8,0u8, - 46u8,27u8,5u8,0u8,0u8,47u8,16u8,1u8,0u8,0u8,48u8,17u8,18u8,0u8,0u8,49u8,19u8,5u8,0u8,0u8, - 50u8,21u8,7u8,0u8,0u8,51u8,28u8,13u8,0u8,0u8,52u8,29u8,30u8,0u8,0u8,53u8,31u8,7u8,0u8,0u8, - 54u8,28u8,23u8,0u8,0u8,55u8,7u8,7u8,0u8,0u8,56u8,14u8,7u8,0u8,0u8,57u8,5u8,7u8,0u8,0u8, - 58u8,6u8,12u8,0u8,0u8,59u8,7u8,7u8,0u8,0u8,60u8,7u8,23u8,0u8,0u8,61u8,6u8,23u8,0u8,0u8, - 62u8,6u8,23u8,0u8,0u8,63u8,28u8,13u8,0u8,0u8,64u8,29u8,30u8,0u8,0u8,65u8,31u8,7u8,0u8,0u8, - 66u8,6u8,13u8,0u8,0u8,67u8,30u8,30u8,0u8,0u8,68u8,7u8,7u8,0u8,0u8,69u8,0u8,13u8,0u8,0u8, - 70u8,7u8,7u8,0u8,0u8,71u8,28u8,13u8,0u8,0u8,72u8,29u8,30u8,0u8,0u8,73u8,31u8,7u8,0u8,0u8, - 74u8,6u8,7u8,0u8,0u8,75u8,7u8,7u8,0u8,0u8,76u8,0u8,13u8,0u8,3u8,79u8,32u8,23u8,1u8,0u8, - 1u8,80u8,5u8,5u8,0u8,2u8,81u8,34u8,35u8,1u8,0u8,2u8,82u8,0u8,35u8,1u8,0u8,69u8,1u8,69u8, - 13u8,7u8,33u8,71u8,2u8,72u8,2u8,71u8,1u8,72u8,1u8,71u8,13u8,72u8,13u8,0u8,1u8,8u8,1u8,1u8, - 8u8,0u8,3u8,6u8,8u8,2u8,6u8,8u8,1u8,6u8,8u8,2u8,3u8,10u8,2u8,6u8,8u8,1u8,10u8,2u8, - 1u8,3u8,1u8,6u8,8u8,2u8,1u8,10u8,2u8,2u8,6u8,10u8,8u8,1u8,6u8,10u8,8u8,2u8,2u8,6u8, - 10u8,9u8,0u8,6u8,10u8,9u8,1u8,1u8,11u8,3u8,1u8,8u8,0u8,1u8,11u8,3u8,1u8,8u8,1u8,1u8, - 11u8,3u8,1u8,8u8,2u8,1u8,8u8,2u8,1u8,4u8,1u8,2u8,2u8,6u8,8u8,1u8,6u8,8u8,1u8,2u8, - 7u8,8u8,1u8,6u8,8u8,1u8,1u8,7u8,8u8,1u8,3u8,6u8,8u8,1u8,6u8,8u8,1u8,1u8,1u8,6u8, - 8u8,1u8,1u8,6u8,8u8,0u8,2u8,3u8,1u8,1u8,1u8,2u8,6u8,8u8,1u8,6u8,8u8,2u8,2u8,7u8, - 8u8,1u8,6u8,8u8,2u8,3u8,6u8,8u8,1u8,10u8,2u8,1u8,2u8,6u8,8u8,1u8,1u8,2u8,6u8,8u8, - 2u8,6u8,8u8,2u8,2u8,7u8,8u8,2u8,6u8,8u8,2u8,1u8,7u8,8u8,2u8,2u8,10u8,2u8,10u8,2u8, - 1u8,6u8,10u8,9u8,0u8,2u8,8u8,1u8,8u8,2u8,1u8,9u8,0u8,1u8,11u8,3u8,1u8,9u8,0u8,3u8, - 11u8,3u8,1u8,8u8,1u8,3u8,1u8,2u8,7u8,2u8,8u8,2u8,2u8,6u8,8u8,2u8,8u8,2u8,12u8,114u8, - 105u8,115u8,116u8,114u8,101u8,116u8,116u8,111u8,50u8,53u8,53u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,111u8,112u8, - 116u8,105u8,111u8,110u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,19u8,67u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8, - 101u8,100u8,82u8,105u8,115u8,116u8,114u8,101u8,116u8,116u8,111u8,14u8,82u8,105u8,115u8,116u8,114u8,101u8,116u8,116u8, - 111u8,80u8,111u8,105u8,110u8,116u8,6u8,83u8,99u8,97u8,108u8,97u8,114u8,9u8,98u8,97u8,115u8,101u8,112u8,111u8, - 105u8,110u8,116u8,20u8,98u8,97u8,115u8,101u8,112u8,111u8,105u8,110u8,116u8,95u8,99u8,111u8,109u8,112u8,114u8,101u8, - 115u8,115u8,101u8,100u8,20u8,98u8,97u8,115u8,101u8,112u8,111u8,105u8,110u8,116u8,95u8,100u8,111u8,117u8,98u8,108u8, - 101u8,95u8,109u8,117u8,108u8,29u8,98u8,97u8,115u8,101u8,112u8,111u8,105u8,110u8,116u8,95u8,100u8,111u8,117u8,98u8, - 108u8,101u8,95u8,109u8,117u8,108u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,13u8,98u8,97u8,115u8,101u8, - 112u8,111u8,105u8,110u8,116u8,95u8,109u8,117u8,108u8,22u8,98u8,97u8,115u8,101u8,112u8,111u8,105u8,110u8,116u8,95u8, - 109u8,117u8,108u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,16u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8, - 99u8,97u8,108u8,97u8,114u8,95u8,109u8,117u8,108u8,25u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,99u8,97u8,108u8, - 97u8,114u8,95u8,109u8,117u8,108u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,6u8,79u8,112u8,116u8,105u8, - 111u8,110u8,31u8,110u8,101u8,119u8,95u8,99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,101u8,100u8,95u8,112u8,111u8, - 105u8,110u8,116u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,31u8,110u8,101u8,119u8,95u8,112u8, - 111u8,105u8,110u8,116u8,95u8,102u8,114u8,111u8,109u8,95u8,54u8,52u8,95u8,117u8,110u8,105u8,102u8,111u8,114u8,109u8, - 95u8,98u8,121u8,116u8,101u8,115u8,40u8,110u8,101u8,119u8,95u8,112u8,111u8,105u8,110u8,116u8,95u8,102u8,114u8,111u8, - 109u8,95u8,54u8,52u8,95u8,117u8,110u8,105u8,102u8,111u8,114u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,95u8,105u8, - 110u8,116u8,101u8,114u8,110u8,97u8,108u8,20u8,110u8,101u8,119u8,95u8,112u8,111u8,105u8,110u8,116u8,95u8,102u8,114u8, - 111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,21u8,110u8,101u8,119u8,95u8,112u8,111u8,105u8,110u8,116u8,95u8,102u8, - 114u8,111u8,109u8,95u8,115u8,104u8,97u8,53u8,49u8,50u8,30u8,110u8,101u8,119u8,95u8,112u8,111u8,105u8,110u8,116u8, - 95u8,102u8,114u8,111u8,109u8,95u8,115u8,104u8,97u8,53u8,49u8,50u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8, - 108u8,21u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8, - 116u8,101u8,115u8,22u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8,114u8,111u8,109u8,95u8, - 115u8,104u8,97u8,53u8,49u8,50u8,20u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8,114u8, - 111u8,109u8,95u8,117u8,49u8,50u8,56u8,19u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8, - 114u8,111u8,109u8,95u8,117u8,54u8,52u8,18u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8, - 114u8,111u8,109u8,95u8,117u8,56u8,32u8,110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,114u8,101u8, - 100u8,117u8,99u8,101u8,100u8,95u8,102u8,114u8,111u8,109u8,95u8,51u8,50u8,95u8,98u8,121u8,116u8,101u8,115u8,32u8, - 110u8,101u8,119u8,95u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,117u8,110u8,105u8,102u8,111u8,114u8,109u8,95u8,102u8, - 114u8,111u8,109u8,95u8,54u8,52u8,95u8,98u8,121u8,116u8,101u8,115u8,9u8,112u8,111u8,105u8,110u8,116u8,95u8,97u8, - 100u8,100u8,16u8,112u8,111u8,105u8,110u8,116u8,95u8,97u8,100u8,100u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,18u8, - 112u8,111u8,105u8,110u8,116u8,95u8,97u8,100u8,100u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,14u8,112u8, - 111u8,105u8,110u8,116u8,95u8,99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,23u8,112u8,111u8,105u8,110u8,116u8,95u8, - 99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,16u8,112u8,111u8, - 105u8,110u8,116u8,95u8,100u8,101u8,99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,25u8,112u8,111u8,105u8,110u8,116u8, - 95u8,100u8,101u8,99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8, - 12u8,112u8,111u8,105u8,110u8,116u8,95u8,101u8,113u8,117u8,97u8,108u8,115u8,14u8,112u8,111u8,105u8,110u8,116u8,95u8, - 105u8,100u8,101u8,110u8,116u8,105u8,116u8,121u8,25u8,112u8,111u8,105u8,110u8,116u8,95u8,105u8,100u8,101u8,110u8,116u8, - 105u8,116u8,121u8,95u8,99u8,111u8,109u8,112u8,114u8,101u8,115u8,115u8,101u8,100u8,23u8,112u8,111u8,105u8,110u8,116u8, - 95u8,105u8,100u8,101u8,110u8,116u8,105u8,116u8,121u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,27u8,112u8, - 111u8,105u8,110u8,116u8,95u8,105u8,115u8,95u8,99u8,97u8,110u8,111u8,110u8,105u8,99u8,97u8,108u8,95u8,105u8,110u8, - 116u8,101u8,114u8,110u8,97u8,108u8,9u8,112u8,111u8,105u8,110u8,116u8,95u8,109u8,117u8,108u8,16u8,112u8,111u8,105u8, - 110u8,116u8,95u8,109u8,117u8,108u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,18u8,112u8,111u8,105u8,110u8,116u8,95u8, - 109u8,117u8,108u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,9u8,112u8,111u8,105u8,110u8,116u8,95u8,110u8, - 101u8,103u8,16u8,112u8,111u8,105u8,110u8,116u8,95u8,110u8,101u8,103u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,18u8, - 112u8,111u8,105u8,110u8,116u8,95u8,110u8,101u8,103u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,9u8,112u8, - 111u8,105u8,110u8,116u8,95u8,115u8,117u8,98u8,16u8,112u8,111u8,105u8,110u8,116u8,95u8,115u8,117u8,98u8,95u8,97u8, - 115u8,115u8,105u8,103u8,110u8,18u8,112u8,111u8,105u8,110u8,116u8,95u8,115u8,117u8,98u8,95u8,105u8,110u8,116u8,101u8, - 114u8,110u8,97u8,108u8,14u8,112u8,111u8,105u8,110u8,116u8,95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,10u8, - 115u8,99u8,97u8,108u8,97u8,114u8,95u8,97u8,100u8,100u8,17u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,97u8,100u8, - 100u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,19u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,97u8,100u8,100u8,95u8, - 105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,13u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,101u8,113u8,117u8,97u8, - 108u8,115u8,27u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8,114u8,111u8,109u8,95u8,115u8,104u8,97u8,53u8,49u8, - 50u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,25u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,102u8,114u8, - 111u8,109u8,95u8,117u8,49u8,50u8,56u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,24u8,115u8,99u8,97u8, - 108u8,97u8,114u8,95u8,102u8,114u8,111u8,109u8,95u8,117u8,54u8,52u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8, - 108u8,13u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,105u8,110u8,118u8,101u8,114u8,116u8,22u8,115u8,99u8,97u8,108u8, - 97u8,114u8,95u8,105u8,110u8,118u8,101u8,114u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,28u8,115u8, - 99u8,97u8,108u8,97u8,114u8,95u8,105u8,115u8,95u8,99u8,97u8,110u8,111u8,110u8,105u8,99u8,97u8,108u8,95u8,105u8, - 110u8,116u8,101u8,114u8,110u8,97u8,108u8,13u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,105u8,115u8,95u8,111u8,110u8, - 101u8,14u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,105u8,115u8,95u8,122u8,101u8,114u8,111u8,10u8,115u8,99u8,97u8, - 108u8,97u8,114u8,95u8,109u8,117u8,108u8,17u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,109u8,117u8,108u8,95u8,97u8, - 115u8,115u8,105u8,103u8,110u8,19u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,109u8,117u8,108u8,95u8,105u8,110u8,116u8, - 101u8,114u8,110u8,97u8,108u8,10u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,110u8,101u8,103u8,17u8,115u8,99u8,97u8, - 108u8,97u8,114u8,95u8,110u8,101u8,103u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,19u8,115u8,99u8,97u8,108u8,97u8, - 114u8,95u8,110u8,101u8,103u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,10u8,115u8,99u8,97u8,108u8,97u8, - 114u8,95u8,111u8,110u8,101u8,37u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,114u8,101u8,100u8,117u8,99u8,101u8,100u8, - 95u8,102u8,114u8,111u8,109u8,95u8,51u8,50u8,95u8,98u8,121u8,116u8,101u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8, - 110u8,97u8,108u8,10u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,115u8,117u8,98u8,17u8,115u8,99u8,97u8,108u8,97u8, - 114u8,95u8,115u8,117u8,98u8,95u8,97u8,115u8,115u8,105u8,103u8,110u8,19u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8, - 115u8,117u8,98u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,15u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8, - 116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,37u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,117u8,110u8,105u8,102u8, - 111u8,114u8,109u8,95u8,102u8,114u8,111u8,109u8,95u8,54u8,52u8,95u8,98u8,121u8,116u8,101u8,115u8,95u8,105u8,110u8, - 116u8,101u8,114u8,110u8,97u8,108u8,11u8,115u8,99u8,97u8,108u8,97u8,114u8,95u8,122u8,101u8,114u8,111u8,4u8,100u8, - 97u8,116u8,97u8,6u8,104u8,97u8,110u8,100u8,108u8,101u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,16u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,4u8,115u8,111u8,109u8, - 101u8,4u8,110u8,111u8,110u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8, - 33u8,32u8,112u8,207u8,55u8,83u8,71u8,91u8,159u8,243u8,62u8,47u8,132u8,65u8,62u8,214u8,181u8,5u8,32u8,115u8, - 188u8,204u8,10u8,10u8,129u8,120u8,157u8,62u8,86u8,117u8,220u8,37u8,128u8,86u8,10u8,2u8,33u8,32u8,8u8,56u8, - 57u8,221u8,73u8,30u8,87u8,197u8,116u8,55u8,16u8,195u8,154u8,145u8,214u8,229u8,2u8,202u8,179u8,207u8,14u8,39u8, - 154u8,228u8,23u8,217u8,31u8,242u8,203u8,99u8,62u8,7u8,10u8,2u8,33u8,32u8,232u8,127u8,237u8,161u8,153u8,215u8, - 43u8,131u8,222u8,79u8,91u8,45u8,69u8,211u8,72u8,5u8,197u8,112u8,25u8,198u8,197u8,156u8,66u8,203u8,112u8,238u8, - 61u8,25u8,170u8,153u8,111u8,117u8,10u8,2u8,33u8,32u8,26u8,14u8,151u8,138u8,144u8,246u8,98u8,45u8,55u8,71u8, - 2u8,63u8,138u8,216u8,38u8,77u8,167u8,88u8,170u8,27u8,136u8,224u8,64u8,209u8,88u8,158u8,123u8,127u8,35u8,118u8, - 239u8,9u8,10u8,2u8,33u8,32u8,150u8,213u8,45u8,146u8,98u8,238u8,30u8,26u8,174u8,121u8,251u8,174u8,232u8,193u8, - 217u8,6u8,139u8,13u8,1u8,191u8,154u8,69u8,121u8,230u8,24u8,9u8,12u8,61u8,16u8,136u8,174u8,16u8,10u8,2u8, - 33u8,32u8,42u8,181u8,14u8,56u8,61u8,124u8,33u8,15u8,116u8,213u8,56u8,115u8,48u8,115u8,95u8,24u8,49u8,81u8, - 18u8,209u8,13u8,251u8,152u8,252u8,206u8,30u8,38u8,32u8,192u8,192u8,20u8,2u8,10u8,2u8,33u8,32u8,226u8,242u8, - 174u8,10u8,106u8,188u8,78u8,113u8,168u8,132u8,169u8,97u8,197u8,0u8,81u8,95u8,88u8,227u8,11u8,106u8,165u8,130u8, - 221u8,141u8,182u8,166u8,89u8,69u8,224u8,141u8,45u8,118u8,10u8,2u8,33u8,32u8,250u8,11u8,54u8,36u8,176u8,129u8, - 198u8,47u8,54u8,77u8,11u8,40u8,57u8,220u8,199u8,109u8,124u8,58u8,176u8,226u8,126u8,49u8,190u8,178u8,185u8,237u8, - 118u8,101u8,117u8,242u8,142u8,118u8,10u8,2u8,33u8,32u8,219u8,253u8,151u8,175u8,211u8,138u8,6u8,240u8,19u8,141u8, - 5u8,39u8,239u8,178u8,142u8,173u8,91u8,113u8,9u8,180u8,134u8,70u8,89u8,19u8,191u8,58u8,164u8,114u8,168u8,237u8, - 78u8,13u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,33u8,32u8,236u8,211u8,245u8,92u8, - 26u8,99u8,18u8,88u8,214u8,156u8,247u8,162u8,222u8,249u8,222u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,10u8,2u8,33u8,32u8,238u8,211u8,245u8,92u8,26u8,99u8,18u8,88u8, - 214u8,156u8,247u8,162u8,222u8,249u8,222u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,16u8,10u8,2u8,33u8,32u8,239u8,211u8,245u8,92u8,26u8,99u8,18u8,88u8,214u8,156u8,247u8,162u8, - 222u8,249u8,222u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8, - 3u8,8u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 10u8,2u8,33u8,32u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,10u8,2u8,33u8,32u8, - 237u8,211u8,245u8,92u8,26u8,99u8,18u8,88u8,214u8,156u8,247u8,162u8,222u8,249u8,222u8,20u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,10u8,2u8,33u8,32u8,28u8,149u8,152u8,141u8, - 116u8,49u8,236u8,214u8,112u8,207u8,125u8,115u8,244u8,91u8,239u8,198u8,254u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,255u8,255u8,15u8,10u8,2u8,33u8,32u8,216u8,154u8,179u8,139u8,210u8,121u8,2u8,71u8, - 69u8,99u8,158u8,216u8,23u8,173u8,63u8,100u8,204u8,0u8,91u8,50u8,219u8,153u8,57u8,249u8,28u8,82u8,31u8,197u8, - 100u8,165u8,192u8,8u8,10u8,2u8,33u8,32u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 10u8,2u8,33u8,32u8,28u8,220u8,23u8,252u8,224u8,233u8,165u8,187u8,217u8,36u8,126u8,86u8,187u8,1u8,99u8,71u8, - 187u8,186u8,49u8,237u8,213u8,169u8,187u8,150u8,213u8,11u8,205u8,122u8,63u8,150u8,42u8,15u8,10u8,2u8,33u8,32u8, - 78u8,90u8,180u8,52u8,93u8,71u8,8u8,132u8,89u8,19u8,180u8,100u8,27u8,194u8,125u8,82u8,82u8,165u8,133u8,16u8, - 27u8,204u8,66u8,68u8,212u8,73u8,244u8,168u8,121u8,217u8,242u8,4u8,10u8,2u8,33u8,32u8,108u8,51u8,116u8,161u8, - 137u8,79u8,98u8,33u8,10u8,170u8,47u8,225u8,134u8,166u8,249u8,44u8,224u8,170u8,117u8,194u8,119u8,149u8,129u8,194u8, - 149u8,252u8,8u8,23u8,154u8,115u8,148u8,12u8,10u8,2u8,33u8,32u8,144u8,118u8,51u8,254u8,28u8,75u8,102u8,164u8, - 162u8,141u8,45u8,215u8,103u8,131u8,134u8,195u8,83u8,208u8,222u8,84u8,85u8,212u8,252u8,157u8,232u8,239u8,122u8,195u8, - 31u8,53u8,187u8,5u8,10u8,2u8,33u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 10u8,2u8,33u8,32u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8, - 111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,233u8,1u8,3u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,95u8,68u8,73u8,70u8,70u8,69u8,82u8,69u8,78u8,84u8,95u8,78u8, - 85u8,77u8,95u8,80u8,79u8,73u8,78u8,84u8,83u8,95u8,65u8,78u8,68u8,95u8,83u8,67u8,65u8,76u8,65u8,82u8, - 83u8,58u8,84u8,104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,115u8,99u8,97u8,108u8, - 97u8,114u8,115u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,32u8,116u8, - 104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,112u8,111u8,105u8,110u8,116u8,115u8,46u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,95u8,90u8,69u8,82u8,79u8,95u8,80u8,79u8,73u8,78u8, - 84u8,83u8,40u8,69u8,120u8,112u8,101u8,99u8,116u8,101u8,100u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8, - 110u8,32u8,122u8,101u8,114u8,111u8,32u8,112u8,111u8,105u8,110u8,116u8,115u8,32u8,97u8,115u8,32u8,105u8,110u8,112u8, - 117u8,116u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,95u8,90u8,69u8,82u8,79u8,95u8,83u8, - 67u8,65u8,76u8,65u8,82u8,83u8,41u8,69u8,120u8,112u8,101u8,99u8,116u8,101u8,100u8,32u8,109u8,111u8,114u8,101u8, - 32u8,116u8,104u8,97u8,110u8,32u8,122u8,101u8,114u8,111u8,32u8,115u8,99u8,97u8,108u8,97u8,114u8,115u8,32u8,97u8, - 115u8,32u8,105u8,110u8,112u8,117u8,116u8,46u8,0u8,0u8,0u8,2u8,1u8,77u8,10u8,2u8,1u8,2u8,1u8,78u8, - 3u8,2u8,2u8,1u8,77u8,10u8,2u8,0u8,1u8,0u8,0u8,0u8,5u8,7u8,6u8,17u8,27u8,1u8,18u8,1u8, - 2u8,1u8,1u8,0u8,0u8,0u8,3u8,7u8,6u8,18u8,0u8,2u8,2u8,1u8,0u8,0u8,0u8,10u8,11u8,0u8, - 16u8,0u8,20u8,11u8,1u8,11u8,2u8,16u8,0u8,20u8,17u8,3u8,18u8,1u8,2u8,3u8,0u8,2u8,0u8,4u8, - 1u8,0u8,0u8,0u8,6u8,11u8,0u8,16u8,0u8,20u8,17u8,5u8,18u8,1u8,2u8,5u8,0u8,2u8,0u8,6u8, - 1u8,0u8,0u8,0u8,43u8,10u8,0u8,56u8,0u8,32u8,4u8,5u8,5u8,12u8,11u8,1u8,1u8,11u8,0u8,1u8, - 7u8,10u8,17u8,70u8,39u8,10u8,1u8,56u8,1u8,32u8,4u8,17u8,5u8,24u8,11u8,1u8,1u8,11u8,0u8,1u8, - 7u8,11u8,17u8,70u8,39u8,10u8,0u8,65u8,1u8,10u8,1u8,65u8,13u8,33u8,4u8,31u8,5u8,38u8,11u8,1u8, - 1u8,11u8,0u8,1u8,7u8,9u8,17u8,70u8,39u8,11u8,0u8,11u8,1u8,56u8,2u8,18u8,1u8,2u8,7u8,0u8, - 2u8,0u8,8u8,1u8,0u8,0u8,10u8,12u8,10u8,0u8,17u8,32u8,4u8,8u8,11u8,0u8,18u8,0u8,56u8,3u8, - 12u8,1u8,5u8,10u8,56u8,4u8,12u8,1u8,11u8,1u8,2u8,9u8,1u8,0u8,0u8,11u8,15u8,14u8,0u8,65u8, - 15u8,6u8,64u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,11u8,11u8,0u8,17u8,10u8,18u8,1u8,56u8, - 5u8,12u8,1u8,5u8,13u8,56u8,6u8,12u8,1u8,11u8,1u8,2u8,10u8,0u8,2u8,0u8,11u8,1u8,0u8,0u8, - 36u8,15u8,11u8,0u8,17u8,27u8,12u8,3u8,12u8,2u8,11u8,3u8,4u8,11u8,11u8,2u8,18u8,1u8,56u8,5u8, - 12u8,1u8,5u8,13u8,56u8,6u8,12u8,1u8,11u8,1u8,2u8,12u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,17u8, - 13u8,18u8,1u8,2u8,13u8,0u8,2u8,0u8,14u8,1u8,0u8,0u8,12u8,12u8,10u8,0u8,17u8,52u8,4u8,8u8, - 11u8,0u8,18u8,2u8,56u8,7u8,12u8,1u8,5u8,10u8,56u8,8u8,12u8,1u8,11u8,1u8,2u8,15u8,1u8,0u8, - 0u8,0u8,4u8,11u8,0u8,17u8,47u8,18u8,2u8,2u8,16u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,17u8,48u8, - 18u8,2u8,2u8,17u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,17u8,49u8,18u8,2u8,2u8,18u8,1u8,0u8,0u8, - 37u8,12u8,17u8,68u8,12u8,2u8,13u8,2u8,15u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,67u8, - 15u8,12u8,1u8,11u8,0u8,11u8,1u8,21u8,11u8,2u8,2u8,19u8,1u8,0u8,0u8,12u8,15u8,14u8,0u8,65u8, - 15u8,6u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,11u8,11u8,0u8,17u8,62u8,18u8,2u8,56u8, - 7u8,12u8,1u8,5u8,13u8,56u8,8u8,12u8,1u8,11u8,1u8,2u8,20u8,1u8,0u8,0u8,12u8,15u8,14u8,0u8, - 65u8,15u8,6u8,64u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,11u8,11u8,0u8,17u8,67u8,18u8,2u8, - 56u8,7u8,12u8,1u8,5u8,13u8,56u8,8u8,12u8,1u8,11u8,1u8,2u8,21u8,1u8,0u8,0u8,0u8,6u8,11u8, - 0u8,11u8,1u8,9u8,17u8,23u8,18u8,1u8,2u8,22u8,1u8,0u8,0u8,20u8,10u8,10u8,0u8,11u8,1u8,12u8, - 2u8,46u8,11u8,2u8,8u8,17u8,23u8,1u8,11u8,0u8,2u8,23u8,0u8,2u8,0u8,24u8,1u8,0u8,0u8,0u8, - 4u8,11u8,0u8,17u8,25u8,18u8,0u8,2u8,25u8,0u8,2u8,0u8,26u8,1u8,0u8,0u8,0u8,7u8,11u8,0u8, - 16u8,1u8,20u8,17u8,27u8,1u8,18u8,1u8,2u8,27u8,0u8,2u8,0u8,28u8,1u8,2u8,0u8,29u8,1u8,0u8, - 0u8,0u8,3u8,17u8,31u8,18u8,1u8,2u8,30u8,1u8,0u8,0u8,0u8,3u8,7u8,26u8,18u8,0u8,2u8,31u8, - 0u8,2u8,0u8,32u8,0u8,2u8,0u8,33u8,1u8,0u8,0u8,0u8,8u8,11u8,0u8,11u8,1u8,16u8,0u8,20u8, - 9u8,17u8,35u8,18u8,1u8,2u8,34u8,1u8,0u8,0u8,7u8,12u8,10u8,0u8,11u8,1u8,16u8,0u8,20u8,12u8, - 2u8,46u8,11u8,2u8,8u8,17u8,35u8,1u8,11u8,0u8,2u8,35u8,0u8,2u8,0u8,36u8,1u8,0u8,0u8,0u8, - 5u8,11u8,0u8,9u8,17u8,38u8,18u8,1u8,2u8,37u8,1u8,0u8,0u8,0u8,7u8,10u8,0u8,46u8,8u8,17u8, - 38u8,1u8,11u8,0u8,2u8,38u8,0u8,2u8,0u8,39u8,1u8,0u8,0u8,0u8,6u8,11u8,0u8,11u8,1u8,9u8, - 17u8,41u8,18u8,1u8,2u8,40u8,1u8,0u8,0u8,20u8,10u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8, - 8u8,17u8,41u8,1u8,11u8,0u8,2u8,41u8,0u8,2u8,0u8,42u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,16u8, - 1u8,20u8,2u8,43u8,1u8,0u8,0u8,0u8,9u8,11u8,0u8,16u8,0u8,20u8,11u8,1u8,16u8,0u8,20u8,17u8, - 45u8,18u8,2u8,2u8,44u8,1u8,0u8,0u8,38u8,15u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8,17u8, - 43u8,12u8,3u8,14u8,3u8,16u8,0u8,20u8,10u8,0u8,15u8,0u8,21u8,11u8,0u8,2u8,45u8,0u8,2u8,0u8, - 46u8,1u8,0u8,0u8,0u8,8u8,11u8,0u8,16u8,0u8,20u8,11u8,1u8,16u8,0u8,20u8,33u8,2u8,47u8,0u8, - 2u8,0u8,48u8,0u8,2u8,0u8,49u8,0u8,2u8,0u8,50u8,1u8,0u8,0u8,12u8,17u8,10u8,0u8,17u8,54u8, - 4u8,8u8,11u8,0u8,1u8,56u8,8u8,12u8,1u8,5u8,15u8,11u8,0u8,16u8,0u8,20u8,17u8,51u8,18u8,2u8, - 56u8,7u8,12u8,1u8,11u8,1u8,2u8,51u8,0u8,2u8,0u8,52u8,0u8,2u8,0u8,53u8,1u8,0u8,0u8,0u8, - 6u8,11u8,0u8,16u8,0u8,20u8,7u8,27u8,33u8,2u8,54u8,1u8,0u8,0u8,0u8,6u8,11u8,0u8,16u8,0u8, - 20u8,7u8,26u8,33u8,2u8,55u8,1u8,0u8,0u8,0u8,9u8,11u8,0u8,16u8,0u8,20u8,11u8,1u8,16u8,0u8, - 20u8,17u8,57u8,18u8,2u8,2u8,56u8,1u8,0u8,0u8,38u8,15u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8, - 2u8,17u8,55u8,12u8,3u8,14u8,3u8,16u8,0u8,20u8,10u8,0u8,15u8,0u8,21u8,11u8,0u8,2u8,57u8,0u8, - 2u8,0u8,58u8,1u8,0u8,0u8,0u8,6u8,11u8,0u8,16u8,0u8,20u8,17u8,60u8,18u8,2u8,2u8,59u8,1u8, - 0u8,0u8,13u8,12u8,10u8,0u8,46u8,17u8,58u8,12u8,1u8,14u8,1u8,16u8,0u8,20u8,10u8,0u8,15u8,0u8, - 21u8,11u8,0u8,2u8,60u8,0u8,2u8,0u8,61u8,1u8,0u8,0u8,0u8,3u8,7u8,27u8,18u8,2u8,2u8,62u8, - 0u8,2u8,0u8,63u8,1u8,0u8,0u8,0u8,9u8,11u8,0u8,16u8,0u8,20u8,11u8,1u8,16u8,0u8,20u8,17u8, - 65u8,18u8,2u8,2u8,64u8,1u8,0u8,0u8,38u8,15u8,10u8,0u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8,17u8, - 63u8,12u8,3u8,14u8,3u8,16u8,0u8,20u8,10u8,0u8,15u8,0u8,21u8,11u8,0u8,2u8,65u8,0u8,2u8,0u8, - 66u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,16u8,0u8,20u8,2u8,67u8,0u8,2u8,0u8,68u8,1u8,0u8,0u8, - 0u8,3u8,7u8,26u8,18u8,2u8,2u8,2u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk23); - let chunk24 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,14u8,3u8,20u8,47u8,4u8, - 67u8,4u8,5u8,71u8,63u8,7u8,134u8,1u8,250u8,1u8,8u8,128u8,3u8,32u8,6u8,160u8,3u8,20u8,16u8,180u8, - 3u8,121u8,10u8,173u8,4u8,12u8,12u8,185u8,4u8,124u8,13u8,181u8,5u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8, - 0u8,3u8,7u8,0u8,0u8,4u8,7u8,0u8,2u8,7u8,7u8,1u8,0u8,0u8,0u8,5u8,0u8,1u8,0u8,0u8, - 6u8,2u8,0u8,0u8,0u8,8u8,3u8,4u8,0u8,0u8,9u8,5u8,6u8,0u8,0u8,10u8,0u8,7u8,0u8,0u8, - 11u8,8u8,0u8,0u8,1u8,13u8,11u8,11u8,0u8,2u8,14u8,13u8,14u8,1u8,0u8,2u8,15u8,9u8,14u8,1u8, - 0u8,7u8,1u8,8u8,1u8,1u8,10u8,2u8,1u8,8u8,0u8,1u8,6u8,8u8,0u8,3u8,10u8,2u8,2u8,6u8, - 8u8,1u8,1u8,11u8,2u8,1u8,8u8,0u8,3u8,10u8,2u8,2u8,10u8,2u8,2u8,10u8,2u8,1u8,1u8,8u8, - 1u8,1u8,6u8,8u8,1u8,0u8,1u8,2u8,1u8,3u8,3u8,11u8,2u8,1u8,8u8,0u8,10u8,2u8,1u8,1u8, - 9u8,0u8,1u8,11u8,2u8,1u8,9u8,0u8,9u8,115u8,101u8,99u8,112u8,50u8,53u8,54u8,107u8,49u8,5u8,101u8, - 114u8,114u8,111u8,114u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,17u8,69u8,67u8,68u8,83u8,65u8,82u8,97u8,119u8, - 80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,14u8,69u8,67u8,68u8,83u8,65u8,83u8,105u8,103u8,110u8,97u8, - 116u8,117u8,114u8,101u8,34u8,101u8,99u8,100u8,115u8,97u8,95u8,114u8,97u8,119u8,95u8,112u8,117u8,98u8,108u8,105u8, - 99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8,95u8,54u8,52u8,95u8,98u8,121u8,116u8,101u8,115u8,29u8, - 101u8,99u8,100u8,115u8,97u8,95u8,114u8,97u8,119u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8, - 95u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,13u8,101u8,99u8,100u8, - 115u8,97u8,95u8,114u8,101u8,99u8,111u8,118u8,101u8,114u8,22u8,101u8,99u8,100u8,115u8,97u8,95u8,114u8,101u8,99u8, - 111u8,118u8,101u8,114u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,26u8,101u8,99u8,100u8,115u8,97u8,95u8, - 115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8, - 24u8,101u8,99u8,100u8,115u8,97u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,116u8,111u8,95u8, - 98u8,121u8,116u8,101u8,115u8,5u8,98u8,121u8,116u8,101u8,115u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8, - 97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,64u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8, - 116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,101u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8, - 69u8,95u8,68u8,69u8,83u8,69u8,82u8,73u8,65u8,76u8,73u8,90u8,69u8,75u8,65u8,110u8,32u8,101u8,114u8,114u8, - 111u8,114u8,32u8,111u8,99u8,99u8,117u8,114u8,114u8,101u8,100u8,32u8,119u8,104u8,105u8,108u8,101u8,32u8,100u8,101u8, - 115u8,101u8,114u8,105u8,97u8,108u8,105u8,122u8,105u8,110u8,103u8,44u8,32u8,102u8,111u8,114u8,32u8,101u8,120u8,97u8, - 109u8,112u8,108u8,101u8,32u8,100u8,117u8,101u8,32u8,116u8,111u8,32u8,119u8,114u8,111u8,110u8,103u8,32u8,105u8,110u8, - 112u8,117u8,116u8,32u8,115u8,105u8,122u8,101u8,46u8,0u8,0u8,0u8,2u8,1u8,12u8,10u8,2u8,1u8,2u8,1u8, - 12u8,10u8,2u8,0u8,1u8,0u8,0u8,9u8,12u8,14u8,0u8,65u8,10u8,7u8,1u8,33u8,4u8,6u8,5u8,9u8, - 7u8,0u8,17u8,6u8,39u8,11u8,0u8,18u8,0u8,2u8,1u8,1u8,0u8,0u8,9u8,4u8,11u8,0u8,16u8,0u8, - 20u8,2u8,2u8,1u8,0u8,0u8,12u8,19u8,11u8,0u8,11u8,1u8,11u8,2u8,16u8,1u8,20u8,17u8,3u8,12u8, - 5u8,12u8,4u8,11u8,5u8,4u8,15u8,11u8,4u8,17u8,0u8,56u8,0u8,12u8,3u8,5u8,17u8,56u8,1u8,12u8, - 3u8,11u8,3u8,2u8,3u8,0u8,2u8,0u8,4u8,1u8,0u8,0u8,9u8,12u8,14u8,0u8,65u8,10u8,7u8,1u8, - 33u8,4u8,6u8,5u8,9u8,7u8,0u8,17u8,6u8,39u8,11u8,0u8,18u8,1u8,2u8,5u8,1u8,0u8,0u8,9u8, - 4u8,11u8,0u8,16u8,1u8,20u8,2u8,0u8,0u8,1u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk24); - let chunk25 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,14u8,2u8,14u8,24u8,3u8,38u8,205u8,1u8, - 4u8,243u8,1u8,34u8,5u8,149u8,2u8,151u8,3u8,7u8,172u8,5u8,136u8,4u8,8u8,180u8,9u8,32u8,6u8,212u8, - 9u8,70u8,16u8,154u8,10u8,193u8,3u8,10u8,219u8,13u8,37u8,11u8,128u8,14u8,4u8,12u8,132u8,14u8,213u8,13u8, - 13u8,217u8,27u8,18u8,14u8,235u8,27u8,18u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8, - 0u8,6u8,0u8,7u8,7u8,2u8,0u8,0u8,0u8,0u8,0u8,8u8,4u8,2u8,0u8,0u8,0u8,0u8,4u8,31u8, - 4u8,2u8,3u8,1u8,0u8,1u8,0u8,9u8,0u8,1u8,2u8,0u8,0u8,0u8,10u8,2u8,3u8,2u8,2u8,0u8, - 0u8,11u8,4u8,5u8,2u8,2u8,0u8,0u8,12u8,0u8,5u8,2u8,3u8,2u8,0u8,13u8,6u8,3u8,2u8,3u8, - 0u8,0u8,14u8,7u8,8u8,0u8,0u8,15u8,2u8,9u8,2u8,2u8,0u8,0u8,16u8,10u8,1u8,2u8,2u8,2u8, - 0u8,17u8,10u8,1u8,2u8,0u8,0u8,0u8,18u8,11u8,8u8,2u8,0u8,0u8,0u8,19u8,11u8,8u8,2u8,0u8, - 0u8,0u8,20u8,1u8,10u8,2u8,7u8,4u8,0u8,21u8,12u8,10u8,2u8,7u8,4u8,0u8,22u8,4u8,13u8,2u8, - 3u8,0u8,0u8,23u8,14u8,1u8,2u8,0u8,0u8,0u8,24u8,15u8,1u8,2u8,0u8,0u8,0u8,25u8,16u8,1u8, - 2u8,0u8,0u8,0u8,26u8,0u8,1u8,2u8,3u8,2u8,1u8,37u8,19u8,8u8,1u8,0u8,4u8,11u8,22u8,5u8, - 2u8,3u8,0u8,2u8,38u8,8u8,8u8,0u8,2u8,39u8,8u8,8u8,0u8,5u8,40u8,19u8,8u8,1u8,0u8,3u8, - 41u8,24u8,8u8,0u8,4u8,10u8,26u8,3u8,2u8,3u8,0u8,4u8,22u8,22u8,13u8,2u8,3u8,0u8,4u8,17u8, - 29u8,1u8,2u8,3u8,0u8,4u8,20u8,1u8,29u8,2u8,3u8,4u8,4u8,9u8,31u8,1u8,2u8,3u8,0u8,6u8, - 42u8,33u8,18u8,1u8,0u8,6u8,43u8,33u8,35u8,1u8,0u8,18u8,18u8,19u8,21u8,22u8,23u8,10u8,20u8,14u8, - 20u8,24u8,21u8,6u8,20u8,0u8,20u8,2u8,20u8,1u8,20u8,25u8,21u8,26u8,21u8,12u8,20u8,27u8,21u8,28u8, - 21u8,29u8,23u8,30u8,23u8,3u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,0u8,2u8, - 6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,6u8,9u8,1u8,2u8,7u8,11u8,1u8,2u8,9u8, - 0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8,1u8,3u8,6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8, - 6u8,9u8,1u8,3u8,2u8,3u8,3u8,1u8,3u8,1u8,1u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,1u8, - 6u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8,2u8,3u8,1u8,9u8,1u8,1u8,7u8,11u8,1u8,2u8, - 9u8,0u8,9u8,1u8,2u8,7u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,2u8,2u8,7u8,11u8,1u8,2u8,9u8, - 0u8,9u8,1u8,3u8,8u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,11u8,0u8,2u8,9u8,0u8,9u8, - 1u8,3u8,3u8,3u8,3u8,1u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,1u8,9u8,0u8,1u8,6u8, - 9u8,0u8,2u8,9u8,0u8,9u8,1u8,2u8,3u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,2u8,7u8,11u8, - 2u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,2u8,3u8,3u8,5u8, - 6u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,6u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8,3u8, - 2u8,6u8,11u8,2u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,5u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8, - 1u8,7u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8,3u8,7u8,1u8,6u8,11u8,0u8,2u8,9u8,0u8, - 9u8,1u8,3u8,3u8,3u8,1u8,6u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,1u8,11u8,2u8,2u8,9u8, - 0u8,9u8,1u8,4u8,2u8,11u8,2u8,2u8,3u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,11u8,2u8,2u8, - 3u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,11u8,1u8,2u8,9u8,0u8,9u8,1u8,3u8,7u8,11u8,2u8, - 2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,6u8,3u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8, - 3u8,3u8,3u8,9u8,1u8,2u8,7u8,10u8,9u8,0u8,3u8,13u8,3u8,3u8,6u8,11u8,0u8,2u8,9u8,0u8, - 9u8,1u8,6u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8, - 3u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,3u8,3u8,3u8,7u8,10u8,11u8,0u8,2u8,9u8,0u8, - 9u8,1u8,1u8,10u8,9u8,0u8,2u8,9u8,0u8,7u8,9u8,1u8,11u8,115u8,109u8,97u8,114u8,116u8,95u8,116u8, - 97u8,98u8,108u8,101u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,104u8,97u8,115u8,104u8,5u8,101u8,114u8,114u8,111u8, - 114u8,6u8,109u8,97u8,116u8,104u8,54u8,52u8,17u8,116u8,97u8,98u8,108u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8, - 108u8,101u8,110u8,103u8,116u8,104u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,6u8,118u8,101u8,99u8, - 116u8,111u8,114u8,5u8,69u8,110u8,116u8,114u8,121u8,10u8,83u8,109u8,97u8,114u8,116u8,84u8,97u8,98u8,108u8,101u8, - 3u8,97u8,100u8,100u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8, - 117u8,116u8,23u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8, - 101u8,102u8,97u8,117u8,108u8,116u8,19u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8, - 101u8,102u8,97u8,117u8,108u8,116u8,12u8,98u8,117u8,99u8,107u8,101u8,116u8,95u8,105u8,110u8,100u8,101u8,120u8,8u8, - 99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,7u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,13u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,11u8,108u8,111u8, - 97u8,100u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,3u8,110u8,101u8,119u8,15u8,110u8,101u8,119u8,95u8,119u8,105u8, - 116u8,104u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,16u8,115u8,112u8,108u8, - 105u8,116u8,95u8,111u8,110u8,101u8,95u8,98u8,117u8,99u8,107u8,101u8,116u8,27u8,117u8,112u8,100u8,97u8,116u8,101u8, - 95u8,115u8,112u8,108u8,105u8,116u8,95u8,108u8,111u8,97u8,100u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8, - 100u8,25u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,116u8,97u8,114u8,103u8,101u8,116u8,95u8,98u8,117u8,99u8,107u8, - 101u8,116u8,95u8,115u8,105u8,122u8,101u8,6u8,117u8,112u8,115u8,101u8,114u8,116u8,4u8,104u8,97u8,115u8,104u8,3u8, - 107u8,101u8,121u8,5u8,118u8,97u8,108u8,117u8,101u8,7u8,98u8,117u8,99u8,107u8,101u8,116u8,115u8,15u8,84u8,97u8, - 98u8,108u8,101u8,87u8,105u8,116u8,104u8,76u8,101u8,110u8,103u8,116u8,104u8,11u8,110u8,117u8,109u8,95u8,98u8,117u8, - 99u8,107u8,101u8,116u8,115u8,5u8,108u8,101u8,118u8,101u8,108u8,4u8,115u8,105u8,122u8,101u8,20u8,115u8,112u8,108u8, - 105u8,116u8,95u8,108u8,111u8,97u8,100u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,18u8,116u8,97u8, - 114u8,103u8,101u8,116u8,95u8,98u8,117u8,99u8,107u8,101u8,116u8,95u8,115u8,105u8,122u8,101u8,19u8,115u8,105u8,112u8, - 95u8,104u8,97u8,115u8,104u8,95u8,102u8,114u8,111u8,109u8,95u8,118u8,97u8,108u8,117u8,101u8,17u8,112u8,101u8,114u8, - 109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,16u8,105u8,110u8,118u8,97u8,108u8, - 105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,11u8,115u8,105u8,122u8,101u8,95u8,111u8,102u8,95u8, - 118u8,97u8,108u8,3u8,109u8,97u8,120u8,11u8,115u8,119u8,97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,12u8, - 116u8,114u8,105u8,109u8,95u8,114u8,101u8,118u8,101u8,114u8,115u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8, - 58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,172u8,3u8,7u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,32u8,75u8,101u8,121u8,32u8, - 110u8,111u8,116u8,32u8,102u8,111u8,117u8,110u8,100u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,109u8,97u8, - 114u8,116u8,32u8,116u8,97u8,98u8,108u8,101u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,90u8,69u8, - 82u8,79u8,95u8,67u8,65u8,80u8,65u8,67u8,73u8,84u8,89u8,42u8,83u8,109u8,97u8,114u8,116u8,32u8,116u8,97u8, - 98u8,108u8,101u8,32u8,99u8,97u8,112u8,97u8,99u8,105u8,116u8,121u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8, - 32u8,108u8,97u8,114u8,103u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8,32u8,48u8,3u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8,84u8,89u8,32u8,67u8,97u8,110u8,110u8,111u8,116u8, - 32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,110u8,111u8,110u8,45u8,101u8,109u8,112u8,116u8,121u8,32u8,104u8, - 97u8,115u8,104u8,109u8,97u8,112u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,65u8,76u8,82u8,69u8, - 65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,18u8,75u8,101u8,121u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8, - 121u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,31u8,69u8,73u8,78u8, - 86u8,65u8,76u8,73u8,68u8,95u8,76u8,79u8,65u8,68u8,95u8,84u8,72u8,82u8,69u8,83u8,72u8,79u8,76u8,68u8, - 95u8,80u8,69u8,82u8,67u8,69u8,78u8,84u8,48u8,73u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,108u8,111u8,97u8, - 100u8,32u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,32u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,32u8, - 116u8,111u8,32u8,116u8,114u8,105u8,103u8,103u8,101u8,114u8,32u8,115u8,112u8,108u8,105u8,116u8,46u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,27u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,84u8,65u8,82u8,71u8,69u8, - 84u8,95u8,66u8,85u8,67u8,75u8,69u8,84u8,95u8,83u8,73u8,90u8,69u8,27u8,73u8,110u8,118u8,97u8,108u8,105u8, - 100u8,32u8,116u8,97u8,114u8,103u8,101u8,116u8,32u8,98u8,117u8,99u8,107u8,101u8,116u8,32u8,115u8,105u8,122u8,101u8, - 46u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,69u8,88u8,67u8,69u8,69u8,68u8,95u8,77u8,65u8, - 88u8,95u8,66u8,85u8,67u8,75u8,69u8,84u8,95u8,83u8,73u8,90u8,69u8,27u8,73u8,110u8,118u8,97u8,108u8,105u8, - 100u8,32u8,116u8,97u8,114u8,103u8,101u8,116u8,32u8,98u8,117u8,99u8,107u8,101u8,116u8,32u8,115u8,105u8,122u8,101u8, - 46u8,0u8,0u8,0u8,2u8,3u8,27u8,3u8,28u8,9u8,0u8,29u8,9u8,1u8,1u8,2u8,6u8,30u8,11u8,2u8, - 2u8,3u8,10u8,11u8,0u8,2u8,9u8,0u8,9u8,1u8,32u8,3u8,33u8,2u8,34u8,3u8,35u8,2u8,36u8,3u8, - 0u8,20u8,1u8,20u8,0u8,1u8,0u8,0u8,17u8,121u8,14u8,1u8,56u8,0u8,12u8,6u8,10u8,0u8,55u8,0u8, - 20u8,10u8,0u8,55u8,1u8,20u8,10u8,6u8,17u8,5u8,12u8,8u8,10u8,0u8,54u8,2u8,11u8,8u8,56u8,1u8, - 12u8,3u8,10u8,3u8,46u8,65u8,23u8,6u8,16u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,4u8,24u8,5u8, - 31u8,11u8,0u8,1u8,11u8,3u8,1u8,7u8,1u8,17u8,20u8,39u8,10u8,3u8,12u8,10u8,8u8,12u8,9u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,10u8,7u8,10u8,10u8,65u8,23u8,35u8,4u8,61u8,5u8, - 43u8,10u8,10u8,10u8,7u8,66u8,23u8,55u8,3u8,14u8,1u8,34u8,12u8,9u8,10u8,9u8,32u8,4u8,56u8,11u8, - 10u8,1u8,5u8,61u8,11u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,7u8,5u8,37u8, - 11u8,9u8,4u8,64u8,5u8,71u8,11u8,0u8,1u8,11u8,3u8,1u8,7u8,0u8,17u8,21u8,39u8,11u8,6u8,11u8, - 1u8,11u8,2u8,57u8,0u8,12u8,4u8,10u8,0u8,55u8,4u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,33u8,4u8,95u8,14u8,4u8,56u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,23u8,12u8, - 5u8,6u8,0u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,5u8,26u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,17u8,23u8,10u8,0u8,54u8,4u8,21u8,11u8,3u8,11u8,4u8,68u8,23u8,10u8,0u8,55u8,5u8,20u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,0u8,54u8,5u8,21u8,10u8,0u8,46u8,56u8,3u8, - 10u8,0u8,55u8,6u8,20u8,52u8,38u8,4u8,118u8,11u8,0u8,56u8,4u8,5u8,120u8,11u8,0u8,1u8,2u8,1u8, - 1u8,0u8,0u8,25u8,51u8,10u8,0u8,55u8,0u8,20u8,10u8,0u8,55u8,1u8,20u8,14u8,1u8,56u8,0u8,17u8, - 5u8,12u8,5u8,11u8,0u8,55u8,2u8,11u8,5u8,56u8,5u8,12u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,12u8,4u8,10u8,2u8,65u8,23u8,12u8,6u8,10u8,4u8,10u8,6u8,35u8,4u8,46u8,5u8,25u8,10u8, - 2u8,10u8,4u8,66u8,23u8,12u8,3u8,10u8,3u8,55u8,3u8,14u8,1u8,33u8,4u8,39u8,11u8,2u8,1u8,11u8, - 3u8,55u8,7u8,2u8,11u8,3u8,1u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8, - 4u8,5u8,20u8,11u8,2u8,1u8,7u8,5u8,17u8,21u8,39u8,2u8,1u8,0u8,0u8,27u8,52u8,10u8,0u8,55u8, - 0u8,20u8,10u8,0u8,55u8,1u8,20u8,14u8,1u8,56u8,0u8,17u8,5u8,12u8,5u8,11u8,0u8,54u8,2u8,11u8, - 5u8,56u8,1u8,12u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,2u8,46u8,65u8, - 23u8,12u8,6u8,10u8,4u8,10u8,6u8,35u8,4u8,47u8,5u8,26u8,10u8,2u8,10u8,4u8,67u8,23u8,12u8,3u8, - 10u8,3u8,55u8,3u8,14u8,1u8,33u8,4u8,40u8,11u8,2u8,1u8,11u8,3u8,54u8,7u8,2u8,11u8,3u8,1u8, - 11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,21u8,11u8,2u8,1u8,7u8, - 5u8,17u8,21u8,39u8,3u8,1u8,0u8,0u8,18u8,16u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8, - 6u8,32u8,4u8,12u8,10u8,0u8,10u8,1u8,11u8,2u8,56u8,7u8,11u8,0u8,11u8,1u8,56u8,8u8,2u8,4u8, - 1u8,0u8,0u8,3u8,18u8,10u8,0u8,10u8,1u8,56u8,6u8,32u8,4u8,10u8,11u8,0u8,1u8,11u8,2u8,12u8, - 3u8,5u8,16u8,11u8,2u8,1u8,11u8,0u8,10u8,1u8,56u8,9u8,12u8,3u8,11u8,3u8,2u8,5u8,0u8,0u8, - 0u8,24u8,23u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,49u8,1u8,22u8,47u8, - 25u8,12u8,4u8,10u8,4u8,11u8,1u8,35u8,4u8,15u8,11u8,4u8,12u8,3u8,5u8,21u8,11u8,4u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,0u8,47u8,25u8,12u8,3u8,11u8,3u8,2u8,6u8,1u8,0u8,0u8, - 28u8,61u8,14u8,1u8,56u8,0u8,12u8,4u8,10u8,0u8,55u8,0u8,20u8,10u8,0u8,55u8,1u8,20u8,10u8,4u8, - 17u8,5u8,12u8,6u8,11u8,0u8,55u8,2u8,11u8,6u8,56u8,5u8,12u8,8u8,9u8,12u8,7u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,5u8,10u8,8u8,65u8,23u8,35u8,4u8,59u8,5u8,27u8,10u8, - 8u8,10u8,5u8,66u8,23u8,12u8,3u8,10u8,3u8,55u8,8u8,20u8,10u8,4u8,33u8,4u8,43u8,11u8,3u8,55u8, - 3u8,14u8,1u8,33u8,12u8,2u8,5u8,47u8,11u8,3u8,1u8,9u8,12u8,2u8,11u8,2u8,12u8,7u8,10u8,7u8, - 4u8,54u8,11u8,8u8,1u8,5u8,59u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8, - 5u8,5u8,21u8,11u8,7u8,2u8,7u8,1u8,0u8,0u8,8u8,28u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,1u8,10u8,1u8,14u8,0u8,55u8,1u8,20u8,35u8,4u8,19u8,5u8,9u8,13u8,0u8,54u8,2u8,10u8, - 1u8,56u8,10u8,1u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,2u8, - 11u8,0u8,58u8,1u8,1u8,1u8,1u8,1u8,1u8,56u8,11u8,2u8,8u8,1u8,0u8,0u8,8u8,38u8,14u8,0u8, - 55u8,5u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,5u8,10u8,7u8,4u8,17u8, - 21u8,39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8,1u8,14u8,0u8,55u8,1u8,20u8, - 35u8,4u8,29u8,5u8,19u8,13u8,0u8,54u8,2u8,10u8,1u8,56u8,10u8,70u8,23u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,12u8,11u8, - 0u8,58u8,1u8,1u8,1u8,1u8,1u8,1u8,56u8,11u8,2u8,9u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,55u8, - 5u8,20u8,2u8,10u8,1u8,0u8,0u8,1u8,14u8,10u8,0u8,55u8,5u8,20u8,6u8,100u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,24u8,10u8,0u8,55u8,1u8,20u8,26u8,11u8,0u8,55u8,4u8,20u8,26u8,2u8,11u8,1u8,0u8, - 0u8,1u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,49u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,56u8,12u8,2u8,12u8,1u8,0u8,0u8,30u8,53u8,10u8,1u8,49u8,100u8,37u8,4u8,5u8,5u8, - 8u8,7u8,2u8,17u8,21u8,39u8,56u8,13u8,12u8,5u8,13u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,64u8,23u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,56u8,14u8,11u8,5u8,12u8,4u8,10u8,1u8,49u8, - 0u8,33u8,4u8,23u8,49u8,75u8,12u8,3u8,5u8,25u8,11u8,1u8,12u8,3u8,11u8,4u8,6u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,49u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,3u8,11u8,2u8, - 57u8,1u8,12u8,6u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,39u8,6u8,2u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,0u8,10u8,0u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 36u8,4u8,51u8,5u8,44u8,11u8,0u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,0u8,13u8, - 6u8,56u8,4u8,5u8,39u8,11u8,6u8,2u8,13u8,1u8,0u8,0u8,32u8,65u8,10u8,0u8,55u8,0u8,20u8,10u8, - 0u8,55u8,1u8,20u8,14u8,1u8,56u8,0u8,17u8,5u8,12u8,5u8,10u8,0u8,54u8,2u8,11u8,5u8,56u8,1u8, - 12u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,3u8,46u8,65u8,23u8,12u8,6u8, - 10u8,4u8,10u8,6u8,35u8,4u8,58u8,5u8,26u8,10u8,3u8,10u8,4u8,12u8,2u8,46u8,11u8,2u8,66u8,23u8, - 55u8,3u8,14u8,1u8,33u8,4u8,53u8,11u8,3u8,11u8,4u8,56u8,15u8,58u8,0u8,12u8,7u8,1u8,1u8,10u8, - 0u8,55u8,5u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,11u8,0u8,54u8,5u8,21u8,11u8, - 7u8,2u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,21u8,11u8,0u8, - 1u8,11u8,3u8,1u8,7u8,5u8,17u8,21u8,39u8,14u8,0u8,0u8,0u8,34u8,139u8,1u8,10u8,0u8,55u8,1u8, - 20u8,12u8,8u8,10u8,8u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,55u8,0u8,20u8,47u8, - 29u8,12u8,12u8,10u8,8u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,0u8,54u8,1u8,21u8, - 10u8,12u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,10u8,0u8,55u8,0u8,20u8,47u8,33u8,4u8,36u8,10u8,0u8,55u8,0u8,20u8,49u8,1u8,22u8,10u8,0u8, - 54u8,0u8,21u8,10u8,0u8,54u8,2u8,11u8,12u8,56u8,1u8,12u8,9u8,10u8,9u8,12u8,13u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,13u8,46u8,65u8,23u8,12u8,6u8,10u8,5u8,10u8,6u8,35u8, - 4u8,81u8,5u8,54u8,10u8,13u8,10u8,5u8,12u8,1u8,46u8,11u8,1u8,66u8,23u8,12u8,3u8,10u8,0u8,55u8, - 0u8,20u8,10u8,0u8,55u8,1u8,20u8,11u8,3u8,55u8,8u8,20u8,17u8,5u8,10u8,8u8,34u8,32u8,4u8,76u8, - 5u8,81u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,49u8,10u8,5u8, - 12u8,11u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,10u8,5u8,10u8,6u8, - 35u8,4u8,125u8,5u8,92u8,10u8,13u8,10u8,5u8,12u8,2u8,46u8,11u8,2u8,66u8,23u8,12u8,4u8,10u8,0u8, - 55u8,0u8,20u8,10u8,0u8,55u8,1u8,20u8,11u8,4u8,55u8,8u8,20u8,17u8,5u8,10u8,8u8,34u8,4u8,120u8, - 10u8,13u8,10u8,11u8,10u8,5u8,71u8,23u8,11u8,11u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,11u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,87u8,11u8,13u8, - 1u8,11u8,11u8,12u8,10u8,11u8,9u8,11u8,10u8,56u8,16u8,12u8,7u8,11u8,0u8,54u8,2u8,11u8,8u8,11u8, - 7u8,56u8,14u8,2u8,15u8,1u8,0u8,0u8,9u8,24u8,10u8,1u8,49u8,100u8,37u8,4u8,9u8,10u8,1u8,49u8, - 0u8,36u8,12u8,2u8,5u8,11u8,9u8,12u8,2u8,11u8,2u8,4u8,14u8,5u8,19u8,11u8,0u8,1u8,7u8,2u8, - 17u8,21u8,39u8,11u8,1u8,11u8,0u8,54u8,6u8,21u8,2u8,16u8,1u8,0u8,0u8,1u8,15u8,10u8,1u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,3u8,17u8,21u8, - 39u8,11u8,1u8,11u8,0u8,54u8,4u8,21u8,2u8,17u8,1u8,0u8,0u8,36u8,21u8,10u8,0u8,10u8,1u8,12u8, - 3u8,46u8,11u8,3u8,56u8,6u8,32u8,4u8,13u8,11u8,0u8,10u8,1u8,11u8,2u8,56u8,7u8,5u8,20u8,11u8, - 0u8,11u8,1u8,56u8,8u8,12u8,4u8,11u8,2u8,11u8,4u8,21u8,2u8,1u8,2u8,1u8,1u8,1u8,0u8,0u8, - 1u8,1u8,5u8,1u8,3u8,1u8,4u8,0u8,2u8,0u8,0u8,0u8,20u8,1u8,20u8,2u8,20u8,3u8,20u8,4u8, - 20u8,5u8,20u8,6u8,20u8,7u8,20u8,8u8,20u8,0u8, - ]; - vector::push_back(&mut code, chunk25); - let chunk26 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,14u8,2u8,14u8,18u8,3u8,32u8,157u8,2u8, - 4u8,189u8,2u8,82u8,5u8,143u8,3u8,225u8,2u8,7u8,240u8,5u8,138u8,3u8,8u8,250u8,8u8,32u8,6u8,154u8, - 9u8,40u8,16u8,194u8,9u8,252u8,1u8,10u8,190u8,11u8,26u8,11u8,216u8,11u8,2u8,12u8,218u8,11u8,253u8,11u8, - 13u8,215u8,23u8,8u8,14u8,223u8,23u8,8u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8, - 0u8,6u8,0u8,7u8,4u8,1u8,0u8,0u8,4u8,27u8,7u8,1u8,0u8,0u8,1u8,28u8,4u8,1u8,0u8,0u8, - 0u8,8u8,0u8,1u8,1u8,4u8,0u8,9u8,2u8,3u8,1u8,0u8,0u8,10u8,4u8,5u8,1u8,0u8,0u8,11u8, - 6u8,7u8,1u8,0u8,0u8,12u8,8u8,1u8,1u8,0u8,0u8,13u8,1u8,8u8,1u8,4u8,0u8,14u8,9u8,8u8, - 1u8,4u8,0u8,15u8,6u8,10u8,1u8,0u8,0u8,16u8,11u8,7u8,1u8,0u8,0u8,17u8,11u8,12u8,1u8,0u8, - 0u8,18u8,13u8,14u8,1u8,0u8,0u8,19u8,15u8,1u8,1u8,4u8,0u8,20u8,4u8,14u8,1u8,0u8,0u8,21u8, - 13u8,1u8,1u8,4u8,0u8,22u8,14u8,8u8,1u8,4u8,0u8,23u8,16u8,1u8,1u8,4u8,0u8,24u8,4u8,14u8, - 1u8,0u8,2u8,31u8,12u8,12u8,0u8,4u8,9u8,20u8,3u8,1u8,0u8,1u8,9u8,21u8,3u8,1u8,0u8,4u8, - 10u8,23u8,5u8,1u8,0u8,1u8,10u8,24u8,5u8,1u8,0u8,4u8,32u8,26u8,1u8,1u8,0u8,4u8,33u8,1u8, - 26u8,1u8,0u8,4u8,34u8,14u8,26u8,1u8,0u8,6u8,15u8,28u8,10u8,1u8,0u8,4u8,35u8,20u8,7u8,1u8, - 0u8,1u8,15u8,29u8,10u8,1u8,0u8,4u8,36u8,20u8,7u8,1u8,0u8,1u8,17u8,30u8,12u8,1u8,0u8,2u8, - 37u8,12u8,12u8,0u8,4u8,38u8,23u8,14u8,1u8,0u8,1u8,18u8,32u8,14u8,1u8,0u8,1u8,16u8,30u8,7u8, - 1u8,0u8,1u8,12u8,19u8,1u8,1u8,0u8,4u8,39u8,33u8,1u8,1u8,0u8,5u8,40u8,3u8,12u8,1u8,0u8, - 3u8,41u8,9u8,12u8,0u8,1u8,13u8,12u8,19u8,1u8,4u8,1u8,19u8,36u8,1u8,1u8,4u8,6u8,20u8,38u8, - 14u8,1u8,0u8,1u8,20u8,24u8,14u8,1u8,0u8,6u8,21u8,40u8,1u8,1u8,0u8,1u8,21u8,32u8,1u8,1u8, - 0u8,6u8,16u8,41u8,7u8,1u8,0u8,1u8,23u8,43u8,1u8,1u8,0u8,6u8,24u8,38u8,14u8,1u8,0u8,1u8, - 24u8,24u8,14u8,1u8,0u8,9u8,14u8,16u8,14u8,11u8,14u8,10u8,14u8,4u8,14u8,18u8,19u8,19u8,14u8,20u8, - 19u8,21u8,14u8,8u8,14u8,7u8,14u8,22u8,19u8,23u8,19u8,23u8,12u8,24u8,12u8,25u8,14u8,26u8,19u8,27u8, - 14u8,28u8,19u8,29u8,14u8,31u8,19u8,32u8,14u8,33u8,14u8,34u8,14u8,35u8,19u8,26u8,12u8,18u8,12u8,36u8, - 14u8,36u8,35u8,38u8,14u8,39u8,14u8,40u8,14u8,41u8,14u8,42u8,14u8,43u8,14u8,44u8,14u8,5u8,14u8,15u8, - 14u8,45u8,14u8,46u8,14u8,47u8,14u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,11u8,0u8,1u8,9u8,0u8,0u8, - 2u8,6u8,11u8,0u8,1u8,9u8,0u8,3u8,1u8,6u8,9u8,0u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,3u8, - 1u8,7u8,9u8,0u8,2u8,6u8,11u8,0u8,1u8,9u8,0u8,6u8,9u8,0u8,1u8,1u8,1u8,11u8,0u8,1u8, - 9u8,0u8,2u8,3u8,3u8,2u8,1u8,3u8,1u8,6u8,11u8,0u8,1u8,9u8,0u8,1u8,3u8,1u8,7u8,11u8, - 0u8,1u8,9u8,0u8,1u8,9u8,0u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,9u8,0u8,3u8,7u8,11u8,0u8, - 1u8,9u8,0u8,3u8,3u8,3u8,3u8,3u8,3u8,2u8,6u8,9u8,0u8,3u8,1u8,11u8,2u8,1u8,9u8,0u8, - 1u8,6u8,11u8,1u8,1u8,9u8,0u8,2u8,6u8,11u8,2u8,1u8,9u8,0u8,3u8,2u8,7u8,9u8,0u8,3u8, - 1u8,7u8,11u8,1u8,1u8,9u8,0u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8,3u8,1u8,11u8,1u8,1u8,11u8, - 2u8,1u8,9u8,0u8,1u8,11u8,1u8,1u8,9u8,0u8,5u8,1u8,3u8,1u8,3u8,3u8,2u8,6u8,10u8,9u8, - 0u8,6u8,9u8,0u8,2u8,6u8,11u8,2u8,1u8,9u8,0u8,6u8,9u8,0u8,1u8,6u8,11u8,2u8,1u8,9u8, - 0u8,4u8,9u8,0u8,11u8,2u8,1u8,9u8,0u8,7u8,11u8,1u8,1u8,11u8,2u8,1u8,9u8,0u8,9u8,0u8, - 1u8,7u8,11u8,2u8,1u8,9u8,0u8,2u8,7u8,11u8,1u8,1u8,9u8,0u8,9u8,0u8,6u8,3u8,3u8,3u8, - 3u8,3u8,3u8,1u8,10u8,9u8,0u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8,9u8,0u8,6u8,9u8,0u8,11u8, - 2u8,1u8,9u8,0u8,7u8,11u8,1u8,1u8,11u8,2u8,1u8,9u8,0u8,3u8,3u8,9u8,0u8,2u8,7u8,10u8, - 9u8,0u8,3u8,4u8,3u8,3u8,10u8,9u8,0u8,10u8,9u8,0u8,1u8,7u8,10u8,9u8,0u8,1u8,6u8,10u8, - 9u8,0u8,6u8,7u8,11u8,2u8,1u8,9u8,0u8,9u8,0u8,9u8,0u8,3u8,7u8,10u8,9u8,0u8,3u8,3u8, - 7u8,11u8,2u8,1u8,9u8,0u8,3u8,3u8,9u8,9u8,0u8,11u8,2u8,1u8,9u8,0u8,11u8,2u8,1u8,9u8, - 0u8,7u8,11u8,1u8,1u8,11u8,2u8,1u8,9u8,0u8,3u8,7u8,10u8,9u8,0u8,9u8,0u8,3u8,9u8,0u8, - 12u8,115u8,109u8,97u8,114u8,116u8,95u8,118u8,101u8,99u8,116u8,111u8,114u8,10u8,98u8,105u8,103u8,95u8,118u8,101u8, - 99u8,116u8,111u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,109u8,97u8,116u8,104u8,54u8,52u8,6u8,111u8,112u8, - 116u8,105u8,111u8,110u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,6u8,118u8,101u8,99u8,116u8,111u8, - 114u8,11u8,83u8,109u8,97u8,114u8,116u8,86u8,101u8,99u8,116u8,111u8,114u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8, - 6u8,98u8,111u8,114u8,114u8,111u8,119u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,8u8,99u8, - 111u8,110u8,116u8,97u8,105u8,110u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8, - 121u8,5u8,101u8,109u8,112u8,116u8,121u8,17u8,101u8,109u8,112u8,116u8,121u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8, - 111u8,110u8,102u8,105u8,103u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,8u8,105u8,115u8,95u8,101u8,109u8, - 112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,8u8,112u8,111u8,112u8,95u8,98u8,97u8,99u8,107u8,9u8, - 112u8,117u8,115u8,104u8,95u8,98u8,97u8,99u8,107u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,7u8,114u8,101u8,118u8, - 101u8,114u8,115u8,101u8,9u8,115u8,105u8,110u8,103u8,108u8,101u8,116u8,111u8,110u8,4u8,115u8,119u8,97u8,112u8,11u8, - 115u8,119u8,97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,10u8,105u8,110u8,108u8,105u8,110u8,101u8,95u8,118u8, - 101u8,99u8,7u8,98u8,105u8,103u8,95u8,118u8,101u8,99u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,9u8,66u8,105u8, - 103u8,86u8,101u8,99u8,116u8,111u8,114u8,15u8,105u8,110u8,108u8,105u8,110u8,101u8,95u8,99u8,97u8,112u8,97u8,99u8, - 105u8,116u8,121u8,11u8,98u8,117u8,99u8,107u8,101u8,116u8,95u8,115u8,105u8,122u8,101u8,16u8,105u8,110u8,118u8,97u8, - 108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8, - 95u8,110u8,111u8,110u8,101u8,4u8,110u8,111u8,110u8,101u8,4u8,115u8,111u8,109u8,101u8,7u8,105u8,115u8,95u8,115u8, - 111u8,109u8,101u8,7u8,105u8,115u8,95u8,110u8,111u8,110u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8, - 115u8,116u8,97u8,116u8,101u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,4u8,102u8,105u8,108u8,108u8,11u8,115u8, - 105u8,122u8,101u8,95u8,111u8,102u8,95u8,118u8,97u8,108u8,3u8,109u8,97u8,120u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8, - 116u8,97u8,95u8,118u8,49u8,231u8,1u8,4u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,73u8,78u8, - 68u8,69u8,88u8,95u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,66u8,79u8,85u8,78u8,68u8,83u8,29u8,86u8,101u8, - 99u8,116u8,111u8,114u8,32u8,105u8,110u8,100u8,101u8,120u8,32u8,105u8,115u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8, - 32u8,98u8,111u8,117u8,110u8,100u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8,86u8,69u8,67u8, - 84u8,79u8,82u8,95u8,78u8,79u8,84u8,95u8,69u8,77u8,80u8,84u8,89u8,33u8,67u8,97u8,110u8,110u8,111u8,116u8, - 32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,97u8,32u8,110u8,111u8,110u8,45u8,101u8,109u8,112u8,116u8,121u8, - 32u8,118u8,101u8,99u8,116u8,111u8,114u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,86u8,69u8,67u8, - 84u8,79u8,82u8,95u8,69u8,77u8,80u8,84u8,89u8,36u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,112u8,111u8,112u8, - 32u8,98u8,97u8,99u8,107u8,32u8,102u8,114u8,111u8,109u8,32u8,97u8,110u8,32u8,101u8,109u8,112u8,116u8,121u8,32u8, - 118u8,101u8,99u8,116u8,111u8,114u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8,90u8,69u8,82u8,79u8, - 95u8,66u8,85u8,67u8,75u8,69u8,84u8,95u8,83u8,73u8,90u8,69u8,23u8,98u8,117u8,99u8,107u8,101u8,116u8,95u8, - 115u8,105u8,122u8,101u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,48u8,0u8,0u8,0u8,2u8, - 4u8,25u8,10u8,9u8,0u8,26u8,11u8,1u8,1u8,11u8,2u8,1u8,9u8,0u8,29u8,11u8,1u8,1u8,3u8,30u8, - 11u8,1u8,1u8,3u8,0u8,14u8,0u8,1u8,0u8,0u8,17u8,43u8,14u8,1u8,56u8,0u8,12u8,4u8,10u8,4u8, - 6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,3u8,10u8,3u8,10u8,2u8,35u8,4u8,24u8,5u8,14u8,10u8,0u8,13u8,1u8,10u8,3u8,56u8,1u8, - 56u8,2u8,11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,9u8,10u8,3u8, - 10u8,4u8,35u8,4u8,38u8,5u8,29u8,10u8,0u8,13u8,1u8,56u8,3u8,56u8,2u8,11u8,3u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,24u8,11u8,0u8,1u8,11u8,1u8,56u8,4u8,2u8,1u8, - 1u8,0u8,0u8,18u8,35u8,10u8,1u8,10u8,0u8,56u8,0u8,35u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8, - 0u8,17u8,17u8,39u8,10u8,0u8,55u8,0u8,65u8,14u8,12u8,3u8,10u8,1u8,10u8,3u8,35u8,4u8,25u8,11u8, - 0u8,55u8,0u8,11u8,1u8,66u8,14u8,12u8,2u8,5u8,33u8,11u8,0u8,55u8,1u8,56u8,5u8,11u8,1u8,11u8, - 3u8,23u8,56u8,6u8,12u8,2u8,11u8,2u8,2u8,2u8,1u8,0u8,0u8,22u8,36u8,10u8,1u8,10u8,0u8,46u8, - 56u8,0u8,35u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8,0u8,17u8,17u8,39u8,10u8,0u8,55u8,0u8,65u8, - 14u8,12u8,3u8,10u8,1u8,10u8,3u8,35u8,4u8,26u8,11u8,0u8,54u8,0u8,11u8,1u8,67u8,14u8,12u8,2u8, - 5u8,34u8,11u8,0u8,54u8,1u8,56u8,7u8,11u8,1u8,11u8,3u8,23u8,56u8,8u8,12u8,2u8,11u8,2u8,2u8, - 3u8,1u8,0u8,0u8,1u8,14u8,10u8,0u8,56u8,9u8,4u8,9u8,11u8,1u8,1u8,11u8,0u8,1u8,9u8,2u8, - 11u8,0u8,11u8,1u8,56u8,10u8,1u8,2u8,4u8,1u8,0u8,0u8,25u8,16u8,14u8,0u8,56u8,9u8,4u8,4u8, - 5u8,7u8,7u8,2u8,17u8,17u8,39u8,11u8,0u8,58u8,0u8,1u8,1u8,12u8,1u8,70u8,14u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,11u8,1u8,56u8,11u8,2u8,5u8,1u8,0u8,0u8,1u8,6u8,64u8,14u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,56u8,12u8,56u8,13u8,56u8,13u8,57u8,0u8,2u8,6u8,1u8,0u8,0u8,1u8, - 16u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,8u8,7u8,3u8,17u8, - 17u8,39u8,64u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,56u8,12u8,11u8,0u8,56u8,14u8,11u8,1u8, - 56u8,14u8,57u8,0u8,2u8,7u8,1u8,0u8,0u8,27u8,46u8,10u8,0u8,55u8,0u8,10u8,1u8,56u8,15u8,12u8, - 6u8,4u8,15u8,11u8,1u8,1u8,11u8,0u8,1u8,8u8,11u8,6u8,12u8,5u8,12u8,4u8,5u8,43u8,10u8,0u8, - 55u8,1u8,56u8,16u8,4u8,31u8,10u8,0u8,55u8,1u8,56u8,5u8,11u8,1u8,56u8,17u8,11u8,0u8,55u8,0u8, - 65u8,14u8,22u8,12u8,3u8,12u8,2u8,5u8,39u8,11u8,1u8,1u8,11u8,0u8,1u8,9u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,12u8,3u8,12u8,2u8,11u8,2u8,11u8,3u8,12u8,5u8,12u8,4u8,11u8,4u8,11u8, - 5u8,2u8,8u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,56u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,33u8,2u8,9u8,1u8,0u8,0u8,12u8,18u8,10u8,0u8,55u8,1u8,56u8,18u8,4u8,7u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,5u8,12u8,10u8,0u8,55u8,1u8,56u8,5u8,56u8,19u8,12u8,1u8, - 11u8,0u8,55u8,0u8,65u8,14u8,11u8,1u8,22u8,2u8,10u8,1u8,0u8,0u8,31u8,48u8,10u8,0u8,46u8,56u8, - 9u8,32u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,1u8,17u8,30u8,39u8,10u8,0u8,54u8,1u8,12u8,3u8, - 10u8,3u8,46u8,56u8,16u8,4u8,40u8,11u8,0u8,1u8,10u8,3u8,56u8,20u8,12u8,2u8,13u8,2u8,56u8,21u8, - 12u8,4u8,14u8,2u8,56u8,22u8,4u8,34u8,11u8,3u8,1u8,11u8,2u8,56u8,23u8,5u8,37u8,11u8,3u8,11u8, - 2u8,56u8,24u8,11u8,4u8,12u8,1u8,5u8,46u8,11u8,3u8,1u8,11u8,0u8,54u8,0u8,69u8,14u8,12u8,1u8, - 11u8,1u8,2u8,11u8,1u8,0u8,0u8,34u8,81u8,10u8,0u8,46u8,56u8,0u8,12u8,6u8,10u8,0u8,55u8,0u8, - 65u8,14u8,12u8,5u8,10u8,6u8,10u8,5u8,33u8,4u8,75u8,10u8,0u8,55u8,2u8,56u8,25u8,4u8,34u8,11u8, - 6u8,10u8,0u8,55u8,2u8,56u8,26u8,20u8,35u8,4u8,28u8,11u8,0u8,54u8,0u8,11u8,1u8,68u8,14u8,2u8, - 10u8,0u8,55u8,3u8,56u8,26u8,20u8,12u8,2u8,5u8,68u8,14u8,1u8,56u8,27u8,12u8,7u8,10u8,7u8,10u8, - 5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,24u8,6u8,150u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,35u8,4u8,50u8,11u8,0u8,54u8,0u8,11u8,1u8,68u8,14u8,2u8,10u8,0u8,55u8,0u8,56u8,28u8,11u8, - 7u8,22u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,26u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,17u8,37u8,12u8,4u8,6u8,0u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,4u8,26u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,37u8,12u8,2u8,11u8,2u8,12u8,3u8,10u8,0u8,54u8, - 1u8,11u8,3u8,56u8,29u8,56u8,24u8,11u8,0u8,54u8,1u8,56u8,7u8,11u8,1u8,56u8,30u8,2u8,12u8,1u8, - 0u8,0u8,37u8,55u8,10u8,0u8,46u8,56u8,0u8,12u8,6u8,10u8,1u8,11u8,6u8,35u8,4u8,9u8,5u8,14u8, - 11u8,0u8,1u8,7u8,0u8,17u8,17u8,39u8,10u8,0u8,55u8,0u8,65u8,14u8,12u8,5u8,10u8,1u8,10u8,5u8, - 35u8,4u8,28u8,11u8,0u8,54u8,0u8,11u8,1u8,56u8,31u8,12u8,2u8,5u8,53u8,11u8,0u8,54u8,1u8,12u8, - 4u8,10u8,4u8,56u8,20u8,12u8,3u8,13u8,3u8,11u8,1u8,11u8,5u8,23u8,56u8,32u8,12u8,7u8,14u8,3u8, - 56u8,22u8,4u8,48u8,11u8,4u8,1u8,11u8,3u8,56u8,23u8,5u8,51u8,11u8,4u8,11u8,3u8,56u8,24u8,11u8, - 7u8,12u8,2u8,11u8,2u8,2u8,13u8,1u8,0u8,0u8,39u8,79u8,10u8,0u8,55u8,0u8,65u8,14u8,12u8,2u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,64u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,3u8,10u8,1u8,10u8,2u8,35u8,4u8,22u8,5u8,13u8,13u8,3u8,10u8,0u8,56u8,3u8,68u8,14u8, - 11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,8u8,13u8,3u8,56u8,33u8, - 10u8,0u8,55u8,1u8,56u8,16u8,4u8,32u8,10u8,0u8,54u8,1u8,56u8,7u8,56u8,34u8,64u8,14u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,0u8,54u8,0u8,46u8,56u8,35u8,32u8,4u8,47u8,5u8,41u8, - 13u8,4u8,10u8,0u8,54u8,0u8,69u8,14u8,68u8,14u8,5u8,34u8,13u8,4u8,56u8,33u8,13u8,3u8,46u8,56u8, - 35u8,32u8,4u8,61u8,5u8,55u8,10u8,0u8,54u8,0u8,13u8,3u8,69u8,14u8,68u8,14u8,5u8,49u8,11u8,3u8, - 70u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,4u8,46u8,56u8,35u8,32u8,4u8,74u8,5u8,69u8, - 10u8,0u8,13u8,4u8,69u8,14u8,56u8,2u8,5u8,63u8,11u8,0u8,1u8,11u8,4u8,70u8,14u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,2u8,14u8,1u8,0u8,0u8,8u8,7u8,56u8,36u8,12u8,1u8,13u8,1u8,11u8,0u8, - 56u8,2u8,11u8,1u8,2u8,15u8,1u8,0u8,0u8,42u8,92u8,10u8,1u8,10u8,2u8,36u8,4u8,9u8,11u8,0u8, - 11u8,2u8,11u8,1u8,56u8,37u8,2u8,10u8,0u8,46u8,56u8,0u8,12u8,8u8,10u8,2u8,10u8,8u8,35u8,4u8, - 18u8,5u8,23u8,11u8,0u8,1u8,7u8,0u8,17u8,17u8,39u8,10u8,0u8,55u8,0u8,65u8,14u8,12u8,6u8,10u8, - 1u8,10u8,6u8,38u8,4u8,42u8,11u8,0u8,54u8,1u8,56u8,7u8,11u8,1u8,10u8,6u8,23u8,11u8,2u8,11u8, - 6u8,23u8,56u8,38u8,5u8,91u8,10u8,2u8,10u8,6u8,35u8,4u8,52u8,11u8,0u8,54u8,0u8,11u8,1u8,11u8, - 2u8,71u8,14u8,5u8,91u8,10u8,0u8,54u8,1u8,56u8,7u8,12u8,3u8,11u8,0u8,54u8,0u8,12u8,7u8,10u8, - 7u8,10u8,1u8,56u8,39u8,12u8,4u8,10u8,3u8,10u8,2u8,10u8,6u8,23u8,56u8,40u8,12u8,5u8,10u8,7u8, - 11u8,5u8,68u8,14u8,11u8,7u8,11u8,1u8,10u8,6u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8, - 71u8,14u8,10u8,3u8,11u8,4u8,56u8,30u8,11u8,3u8,11u8,2u8,10u8,6u8,23u8,11u8,8u8,11u8,6u8,23u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,56u8,38u8,2u8,16u8,1u8,0u8,0u8,44u8,86u8,10u8, - 0u8,46u8,56u8,0u8,12u8,9u8,10u8,1u8,10u8,9u8,35u8,4u8,9u8,5u8,14u8,11u8,0u8,1u8,7u8,0u8, - 17u8,17u8,39u8,10u8,0u8,55u8,0u8,65u8,14u8,12u8,6u8,10u8,0u8,54u8,1u8,12u8,5u8,11u8,0u8,54u8, - 0u8,12u8,7u8,10u8,1u8,10u8,6u8,38u8,4u8,53u8,11u8,7u8,1u8,10u8,5u8,56u8,20u8,12u8,3u8,13u8, - 3u8,11u8,1u8,11u8,6u8,23u8,56u8,40u8,12u8,10u8,14u8,3u8,56u8,22u8,4u8,47u8,11u8,5u8,1u8,11u8, - 3u8,56u8,23u8,5u8,50u8,11u8,5u8,11u8,3u8,56u8,24u8,11u8,10u8,12u8,2u8,5u8,84u8,11u8,6u8,11u8, - 9u8,35u8,4u8,78u8,10u8,5u8,56u8,20u8,12u8,4u8,13u8,4u8,56u8,21u8,12u8,8u8,14u8,4u8,56u8,22u8, - 4u8,71u8,11u8,5u8,1u8,11u8,4u8,56u8,23u8,5u8,74u8,11u8,5u8,11u8,4u8,56u8,24u8,10u8,7u8,11u8, - 8u8,68u8,14u8,5u8,80u8,11u8,5u8,1u8,11u8,7u8,11u8,1u8,56u8,39u8,12u8,2u8,11u8,2u8,2u8,0u8, - 0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,14u8,1u8,14u8,2u8,14u8,3u8,14u8,0u8, - ]; - vector::push_back(&mut code, chunk26); - let chunk1 = vector[ - 11u8,65u8,112u8,116u8,111u8,115u8,83u8,116u8,100u8,108u8,105u8,98u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,64u8,49u8,68u8,52u8,69u8,68u8,53u8,57u8,50u8,57u8,70u8,52u8,50u8,65u8,55u8,50u8,69u8,54u8, - 50u8,67u8,55u8,69u8,51u8,68u8,70u8,55u8,53u8,52u8,65u8,55u8,69u8,66u8,66u8,51u8,51u8,54u8,68u8,67u8, - 55u8,51u8,52u8,67u8,50u8,52u8,67u8,53u8,68u8,70u8,67u8,67u8,55u8,50u8,69u8,48u8,48u8,66u8,53u8,52u8, - 67u8,69u8,56u8,56u8,54u8,65u8,48u8,188u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,101u8, - 143u8,65u8,10u8,194u8,48u8,16u8,69u8,247u8,57u8,197u8,16u8,215u8,198u8,122u8,0u8,23u8,46u8,116u8,39u8,8u8, - 46u8,75u8,177u8,177u8,25u8,37u8,52u8,77u8,66u8,38u8,106u8,161u8,244u8,238u8,78u8,40u8,186u8,113u8,251u8,62u8, - 255u8,205u8,159u8,58u8,234u8,174u8,215u8,15u8,108u8,132u8,215u8,3u8,194u8,14u8,228u8,62u8,230u8,64u8,151u8,108u8, - 156u8,189u8,73u8,241u8,194u8,68u8,54u8,248u8,194u8,183u8,170u8,82u8,149u8,20u8,162u8,214u8,198u8,36u8,36u8,66u8, - 106u8,4u8,101u8,83u8,162u8,106u8,220u8,74u8,161u8,75u8,239u8,250u8,79u8,238u8,137u8,197u8,239u8,144u8,250u8,31u8, - 63u8,140u8,25u8,125u8,209u8,210u8,23u8,193u8,10u8,142u8,33u8,193u8,57u8,5u8,62u8,8u8,57u8,128u8,245u8,148u8, - 181u8,207u8,86u8,103u8,132u8,118u8,154u8,184u8,49u8,207u8,45u8,83u8,136u8,9u8,221u8,211u8,160u8,226u8,29u8,6u8, - 35u8,122u8,131u8,190u8,179u8,101u8,202u8,137u8,171u8,203u8,106u8,150u8,78u8,224u8,66u8,167u8,93u8,209u8,43u8,181u8, - 25u8,56u8,90u8,211u8,242u8,17u8,204u8,226u8,3u8,73u8,201u8,156u8,147u8,243u8,0u8,0u8,0u8,27u8,16u8,97u8, - 108u8,103u8,101u8,98u8,114u8,97u8,95u8,98u8,108u8,115u8,49u8,50u8,51u8,56u8,49u8,245u8,5u8,31u8,139u8,8u8, - 0u8,0u8,0u8,0u8,0u8,2u8,255u8,181u8,84u8,109u8,79u8,219u8,48u8,16u8,254u8,206u8,175u8,184u8,177u8,170u8, - 106u8,164u8,144u8,54u8,48u8,105u8,40u8,188u8,104u8,101u8,163u8,19u8,82u8,247u8,34u8,64u8,251u8,48u8,132u8,82u8, - 39u8,113u8,82u8,139u8,196u8,142u8,206u8,78u8,105u8,65u8,252u8,247u8,157u8,147u8,166u8,84u8,140u8,173u8,67u8,104u8, - 254u8,80u8,55u8,177u8,239u8,158u8,231u8,158u8,123u8,46u8,253u8,126u8,31u8,46u8,167u8,66u8,67u8,161u8,146u8,42u8, - 231u8,144u8,240u8,84u8,72u8,78u8,143u8,12u8,111u8,56u8,130u8,89u8,148u8,92u8,187u8,16u8,43u8,169u8,13u8,147u8, - 70u8,3u8,147u8,9u8,24u8,174u8,13u8,196u8,76u8,211u8,181u8,84u8,33u8,220u8,42u8,188u8,17u8,50u8,131u8,91u8, - 97u8,166u8,112u8,50u8,190u8,240u8,119u8,119u8,246u8,246u8,125u8,136u8,43u8,156u8,113u8,189u8,213u8,39u8,128u8,74u8, - 219u8,243u8,140u8,75u8,142u8,34u8,134u8,225u8,247u8,179u8,37u8,76u8,2u8,66u8,194u8,132u8,229u8,25u8,143u8,144u8, - 121u8,133u8,154u8,241u8,137u8,103u8,3u8,234u8,160u8,143u8,21u8,34u8,151u8,38u8,95u8,128u8,174u8,202u8,82u8,161u8, - 161u8,235u8,143u8,217u8,181u8,193u8,42u8,54u8,21u8,18u8,7u8,33u8,227u8,188u8,74u8,56u8,164u8,130u8,231u8,9u8, - 76u8,70u8,72u8,73u8,150u8,213u8,176u8,210u8,40u8,29u8,106u8,147u8,4u8,193u8,18u8,37u8,140u8,114u8,237u8,239u8, - 218u8,12u8,247u8,91u8,64u8,139u8,192u8,154u8,13u8,190u8,172u8,85u8,220u8,148u8,57u8,229u8,2u8,65u8,19u8,103u8, - 150u8,139u8,59u8,102u8,132u8,146u8,160u8,227u8,41u8,47u8,232u8,60u8,226u8,153u8,144u8,94u8,155u8,97u8,185u8,91u8, - 37u8,45u8,13u8,41u8,76u8,203u8,166u8,51u8,10u8,177u8,67u8,153u8,152u8,21u8,76u8,82u8,24u8,105u8,65u8,117u8, - 48u8,109u8,179u8,83u8,54u8,150u8,51u8,108u8,174u8,234u8,85u8,18u8,171u8,169u8,61u8,205u8,80u8,85u8,165u8,134u8, - 206u8,231u8,208u8,239u8,184u8,118u8,219u8,109u8,54u8,211u8,177u8,178u8,173u8,164u8,216u8,137u8,152u8,77u8,89u8,50u8, - 129u8,164u8,114u8,195u8,169u8,81u8,7u8,70u8,8u8,247u8,15u8,143u8,236u8,134u8,79u8,138u8,33u8,160u8,130u8,136u8, - 89u8,60u8,171u8,26u8,240u8,156u8,106u8,163u8,38u8,187u8,171u8,136u8,219u8,41u8,71u8,82u8,81u8,182u8,71u8,64u8, - 70u8,65u8,94u8,146u8,234u8,244u8,64u8,160u8,209u8,2u8,24u8,253u8,80u8,185u8,12u8,145u8,45u8,96u8,18u8,93u8, - 93u8,79u8,64u8,165u8,160u8,197u8,29u8,135u8,189u8,221u8,198u8,19u8,182u8,152u8,156u8,51u8,50u8,141u8,22u8,153u8, - 20u8,169u8,32u8,41u8,76u8,19u8,22u8,171u8,194u8,90u8,35u8,21u8,168u8,77u8,43u8,103u8,127u8,5u8,255u8,245u8, - 219u8,229u8,105u8,208u8,40u8,197u8,10u8,190u8,20u8,223u8,114u8,96u8,185u8,86u8,141u8,146u8,164u8,132u8,162u8,11u8, - 8u8,162u8,40u8,27u8,142u8,117u8,109u8,26u8,122u8,220u8,203u8,60u8,151u8,120u8,221u8,236u8,212u8,45u8,175u8,165u8, - 26u8,120u8,239u8,188u8,129u8,11u8,244u8,194u8,208u8,255u8,61u8,239u8,189u8,243u8,68u8,175u8,81u8,45u8,201u8,88u8, - 71u8,107u8,194u8,181u8,6u8,233u8,189u8,200u8,33u8,156u8,206u8,173u8,122u8,78u8,91u8,22u8,92u8,210u8,216u8,60u8, - 235u8,156u8,183u8,87u8,118u8,162u8,66u8,37u8,243u8,197u8,117u8,253u8,130u8,42u8,123u8,198u8,186u8,65u8,112u8,159u8, - 240u8,22u8,140u8,187u8,176u8,246u8,151u8,37u8,137u8,75u8,136u8,44u8,202u8,121u8,24u8,227u8,130u8,66u8,51u8,100u8, - 229u8,116u8,17u8,182u8,174u8,151u8,68u8,142u8,166u8,241u8,225u8,224u8,15u8,136u8,245u8,128u8,55u8,158u8,10u8,73u8, - 168u8,112u8,116u8,30u8,254u8,24u8,142u8,195u8,65u8,120u8,113u8,122u8,126u8,54u8,28u8,159u8,253u8,60u8,253u8,20u8, - 142u8,47u8,78u8,2u8,152u8,241u8,216u8,40u8,60u8,172u8,246u8,143u8,225u8,8u8,230u8,219u8,131u8,87u8,174u8,237u8, - 131u8,23u8,241u8,241u8,55u8,242u8,241u8,95u8,205u8,103u8,141u8,80u8,47u8,157u8,83u8,214u8,15u8,212u8,4u8,167u8, - 161u8,149u8,86u8,178u8,254u8,250u8,133u8,41u8,210u8,89u8,80u8,251u8,153u8,163u8,179u8,252u8,150u8,216u8,245u8,15u8, - 77u8,232u8,117u8,211u8,185u8,115u8,176u8,138u8,200u8,185u8,129u8,25u8,203u8,195u8,1u8,65u8,213u8,237u8,86u8,165u8, - 53u8,82u8,16u8,240u8,185u8,65u8,22u8,155u8,94u8,183u8,168u8,12u8,172u8,117u8,254u8,112u8,132u8,238u8,186u8,85u8, - 143u8,123u8,171u8,92u8,118u8,117u8,55u8,118u8,209u8,121u8,6u8,221u8,255u8,111u8,232u8,254u8,6u8,116u8,93u8,21u8, - 132u8,77u8,14u8,238u8,117u8,107u8,25u8,92u8,168u8,119u8,127u8,237u8,26u8,211u8,4u8,111u8,222u8,244u8,54u8,230u8, - 134u8,163u8,35u8,248u8,11u8,211u8,46u8,97u8,57u8,46u8,180u8,169u8,127u8,159u8,240u8,102u8,68u8,219u8,209u8,245u8, - 156u8,246u8,252u8,97u8,235u8,23u8,204u8,47u8,98u8,213u8,40u8,7u8,0u8,0u8,0u8,0u8,5u8,100u8,101u8,98u8, - 117u8,103u8,131u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,125u8,204u8,49u8,14u8,131u8,48u8, - 12u8,133u8,225u8,61u8,167u8,240u8,84u8,209u8,165u8,217u8,211u8,170u8,55u8,232u8,198u8,142u8,76u8,146u8,34u8,171u8, - 169u8,131u8,192u8,65u8,32u8,196u8,221u8,49u8,98u8,231u8,205u8,223u8,251u8,173u8,181u8,240u8,201u8,161u8,164u8,8u8, - 253u8,144u8,39u8,10u8,196u8,29u8,132u8,216u8,150u8,14u8,190u8,133u8,189u8,80u8,102u8,76u8,36u8,203u8,195u8,252u8, - 79u8,133u8,189u8,228u8,177u8,25u8,37u8,56u8,119u8,178u8,213u8,128u8,142u8,81u8,104u8,210u8,70u8,105u8,19u8,249u8, - 227u8,170u8,57u8,98u8,121u8,213u8,239u8,106u8,118u8,112u8,171u8,239u8,79u8,115u8,229u8,52u8,136u8,254u8,215u8,200u8, - 128u8,62u8,86u8,106u8,55u8,179u8,3u8,252u8,22u8,253u8,101u8,153u8,0u8,0u8,0u8,0u8,0u8,13u8,102u8,105u8, - 120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8,54u8,52u8,224u8,21u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8, - 0u8,2u8,255u8,213u8,90u8,91u8,115u8,211u8,72u8,22u8,126u8,207u8,175u8,232u8,121u8,216u8,169u8,132u8,53u8,142u8, - 237u8,132u8,0u8,9u8,161u8,42u8,11u8,100u8,43u8,85u8,67u8,66u8,5u8,216u8,219u8,195u8,24u8,89u8,106u8,199u8, - 90u8,100u8,181u8,209u8,37u8,142u8,135u8,201u8,127u8,223u8,239u8,116u8,183u8,250u8,162u8,139u8,109u8,216u8,48u8,85u8, - 67u8,241u8,0u8,82u8,247u8,185u8,245u8,249u8,190u8,115u8,250u8,200u8,251u8,251u8,251u8,236u8,53u8,159u8,198u8,41u8, - 207u8,89u8,192u8,166u8,241u8,29u8,143u8,30u8,47u8,68u8,156u8,22u8,44u8,45u8,231u8,60u8,139u8,67u8,86u8,172u8, - 22u8,156u8,45u8,227u8,98u8,134u8,247u8,71u8,135u8,143u8,39u8,113u8,193u8,240u8,154u8,223u8,240u8,140u8,45u8,130u8, - 172u8,96u8,65u8,26u8,237u8,236u8,67u8,138u8,121u8,59u8,205u8,130u8,176u8,136u8,69u8,26u8,36u8,114u8,65u8,127u8, - 103u8,103u8,46u8,162u8,50u8,225u8,44u8,88u8,20u8,34u8,31u8,231u8,69u8,116u8,124u8,44u8,21u8,141u8,165u8,162u8, - 163u8,67u8,246u8,117u8,103u8,135u8,225u8,207u8,190u8,177u8,101u8,163u8,41u8,216u8,228u8,168u8,129u8,214u8,188u8,111u8, - 68u8,124u8,152u8,197u8,57u8,195u8,223u8,255u8,150u8,57u8,172u8,99u8,229u8,112u8,244u8,204u8,88u8,60u8,41u8,97u8, - 125u8,65u8,111u8,151u8,89u8,176u8,88u8,240u8,8u8,111u8,176u8,38u8,47u8,178u8,50u8,44u8,88u8,33u8,140u8,144u8, - 121u8,240u8,153u8,172u8,40u8,211u8,248u8,75u8,201u8,165u8,226u8,190u8,17u8,28u8,64u8,97u8,26u8,100u8,43u8,150u8, - 241u8,69u8,198u8,115u8,158u8,22u8,1u8,25u8,210u8,99u8,185u8,96u8,17u8,15u8,227u8,121u8,144u8,24u8,57u8,183u8, - 65u8,82u8,34u8,180u8,243u8,96u8,197u8,82u8,81u8,176u8,9u8,103u8,252u8,14u8,118u8,39u8,238u8,230u8,73u8,194u8, - 123u8,149u8,101u8,139u8,76u8,220u8,198u8,17u8,237u8,16u8,25u8,55u8,82u8,138u8,89u8,144u8,178u8,231u8,149u8,112u8, - 22u8,197u8,55u8,240u8,152u8,137u8,41u8,150u8,227u8,81u8,14u8,229u8,108u8,34u8,16u8,150u8,9u8,159u8,98u8,27u8, - 29u8,9u8,11u8,166u8,5u8,252u8,45u8,102u8,86u8,72u8,181u8,91u8,197u8,116u8,119u8,248u8,172u8,146u8,83u8,136u8, - 34u8,72u8,246u8,250u8,236u8,92u8,100u8,44u8,20u8,115u8,28u8,90u8,156u8,147u8,59u8,145u8,40u8,97u8,154u8,213u8, - 97u8,36u8,77u8,19u8,1u8,143u8,211u8,27u8,125u8,60u8,179u8,32u8,103u8,9u8,207u8,115u8,101u8,230u8,240u8,104u8, - 157u8,157u8,20u8,36u8,35u8,7u8,209u8,8u8,131u8,140u8,79u8,203u8,132u8,5u8,19u8,129u8,0u8,148u8,57u8,164u8, - 214u8,197u8,23u8,2u8,86u8,165u8,183u8,28u8,185u8,6u8,119u8,114u8,94u8,197u8,212u8,57u8,45u8,173u8,80u8,229u8, - 128u8,62u8,204u8,115u8,74u8,160u8,119u8,58u8,195u8,200u8,196u8,80u8,44u8,86u8,112u8,42u8,19u8,11u8,24u8,81u8, - 80u8,156u8,190u8,42u8,81u8,199u8,42u8,71u8,238u8,85u8,22u8,66u8,23u8,242u8,230u8,237u8,217u8,191u8,198u8,31u8, - 241u8,20u8,239u8,70u8,79u8,142u8,216u8,41u8,59u8,56u8,28u8,140u8,158u8,141u8,14u8,142u8,142u8,158u8,143u8,6u8, - 207u8,15u8,158u8,29u8,30u8,29u8,224u8,239u8,193u8,211u8,195u8,163u8,193u8,211u8,195u8,131u8,225u8,211u8,163u8,103u8, - 163u8,225u8,240u8,240u8,201u8,147u8,147u8,29u8,39u8,15u8,57u8,12u8,75u8,197u8,28u8,201u8,2u8,117u8,213u8,209u8, - 70u8,108u8,9u8,99u8,126u8,227u8,153u8,112u8,212u8,189u8,121u8,253u8,230u8,242u8,234u8,237u8,197u8,229u8,217u8,135u8, - 171u8,107u8,168u8,132u8,197u8,167u8,108u8,112u8,55u8,28u8,12u8,6u8,195u8,19u8,79u8,224u8,151u8,82u8,20u8,49u8, - 146u8,70u8,89u8,206u8,150u8,162u8,76u8,34u8,138u8,99u8,33u8,4u8,75u8,130u8,236u8,134u8,254u8,69u8,255u8,159u8, - 241u8,68u8,231u8,246u8,39u8,114u8,238u8,147u8,167u8,235u8,226u8,31u8,23u8,239u8,47u8,174u8,46u8,173u8,162u8,17u8, - 20u8,141u8,124u8,69u8,243u8,50u8,41u8,226u8,69u8,18u8,195u8,222u8,255u8,67u8,213u8,219u8,143u8,191u8,124u8,184u8, - 120u8,247u8,203u8,197u8,171u8,179u8,15u8,13u8,133u8,7u8,86u8,225u8,25u8,82u8,229u8,86u8,231u8,241u8,74u8,198u8, - 70u8,6u8,137u8,167u8,161u8,40u8,129u8,220u8,140u8,71u8,109u8,246u8,143u8,255u8,246u8,239u8,241u8,127u8,222u8,92u8, - 95u8,249u8,1u8,59u8,244u8,253u8,160u8,132u8,46u8,11u8,120u8,145u8,17u8,72u8,217u8,114u8,198u8,211u8,42u8,157u8, - 40u8,215u8,224u8,2u8,236u8,118u8,115u8,229u8,147u8,245u8,180u8,76u8,61u8,144u8,186u8,54u8,92u8,147u8,67u8,227u8, - 171u8,143u8,31u8,198u8,87u8,231u8,227u8,235u8,179u8,203u8,191u8,191u8,241u8,157u8,115u8,19u8,225u8,173u8,10u8,229u8, - 170u8,65u8,70u8,171u8,38u8,207u8,77u8,120u8,214u8,99u8,72u8,224u8,52u8,148u8,16u8,0u8,148u8,87u8,22u8,118u8, - 62u8,177u8,18u8,174u8,0u8,8u8,202u8,171u8,8u8,9u8,175u8,249u8,105u8,25u8,39u8,18u8,77u8,120u8,31u8,123u8, - 239u8,141u8,24u8,1u8,223u8,1u8,177u8,165u8,166u8,204u8,5u8,64u8,14u8,110u8,157u8,150u8,105u8,117u8,230u8,171u8, - 49u8,153u8,185u8,139u8,99u8,87u8,200u8,232u8,217u8,100u8,200u8,142u8,61u8,92u8,237u8,105u8,232u8,124u8,149u8,146u8, - 148u8,2u8,25u8,117u8,173u8,83u8,154u8,184u8,20u8,140u8,214u8,80u8,105u8,208u8,200u8,37u8,52u8,18u8,170u8,136u8, - 183u8,37u8,107u8,46u8,96u8,145u8,200u8,230u8,134u8,174u8,180u8,164u8,74u8,109u8,40u8,249u8,85u8,49u8,191u8,132u8, - 35u8,241u8,113u8,46u8,105u8,238u8,51u8,231u8,11u8,233u8,37u8,72u8,36u8,169u8,132u8,122u8,46u8,107u8,89u8,116u8, - 208u8,183u8,34u8,142u8,88u8,34u8,36u8,201u8,4u8,97u8,88u8,34u8,158u8,171u8,190u8,89u8,148u8,112u8,16u8,80u8, - 154u8,135u8,65u8,66u8,165u8,73u8,59u8,112u8,202u8,40u8,14u8,12u8,6u8,147u8,226u8,61u8,246u8,136u8,237u8,218u8, - 96u8,244u8,21u8,50u8,170u8,151u8,39u8,245u8,40u8,84u8,210u8,76u8,56u8,200u8,241u8,102u8,233u8,98u8,187u8,211u8, - 76u8,72u8,231u8,157u8,64u8,239u8,185u8,194u8,16u8,34u8,164u8,33u8,201u8,162u8,26u8,129u8,188u8,201u8,103u8,241u8, - 84u8,229u8,199u8,18u8,149u8,133u8,118u8,226u8,64u8,157u8,58u8,88u8,249u8,99u8,221u8,104u8,120u8,246u8,242u8,37u8, - 76u8,241u8,76u8,126u8,53u8,227u8,225u8,103u8,66u8,8u8,228u8,201u8,210u8,161u8,145u8,31u8,231u8,22u8,243u8,86u8, - 124u8,144u8,231u8,192u8,208u8,79u8,187u8,149u8,188u8,23u8,167u8,134u8,52u8,123u8,117u8,224u8,59u8,161u8,49u8,235u8, - 41u8,106u8,88u8,171u8,252u8,188u8,87u8,204u8,189u8,224u8,161u8,159u8,132u8,78u8,102u8,45u8,178u8,224u8,102u8,30u8, - 48u8,177u8,8u8,80u8,145u8,173u8,188u8,56u8,13u8,147u8,50u8,226u8,6u8,100u8,103u8,148u8,250u8,249u8,197u8,212u8, - 174u8,224u8,105u8,94u8,34u8,122u8,20u8,65u8,172u8,97u8,167u8,167u8,82u8,207u8,184u8,145u8,236u8,110u8,154u8,107u8, - 131u8,29u8,179u8,242u8,112u8,198u8,161u8,190u8,174u8,198u8,49u8,80u8,226u8,5u8,16u8,182u8,154u8,187u8,96u8,99u8, - 87u8,72u8,160u8,230u8,99u8,32u8,117u8,11u8,155u8,216u8,75u8,19u8,97u8,133u8,133u8,90u8,148u8,27u8,54u8,19u8, - 166u8,219u8,229u8,74u8,67u8,215u8,226u8,26u8,239u8,29u8,223u8,36u8,8u8,30u8,177u8,122u8,234u8,239u8,169u8,44u8, - 210u8,122u8,109u8,19u8,23u8,83u8,161u8,251u8,65u8,116u8,87u8,85u8,192u8,78u8,190u8,147u8,165u8,68u8,100u8,70u8, - 78u8,172u8,170u8,45u8,67u8,9u8,142u8,125u8,17u8,107u8,168u8,48u8,146u8,46u8,52u8,136u8,80u8,203u8,222u8,130u8, - 5u8,21u8,152u8,192u8,106u8,141u8,210u8,214u8,132u8,144u8,150u8,170u8,233u8,228u8,39u8,212u8,144u8,94u8,179u8,202u8, - 249u8,244u8,114u8,30u8,103u8,40u8,67u8,166u8,47u8,18u8,134u8,79u8,37u8,43u8,22u8,84u8,229u8,36u8,71u8,128u8, - 5u8,166u8,166u8,177u8,213u8,123u8,131u8,40u8,170u8,177u8,144u8,140u8,207u8,68u8,245u8,130u8,38u8,134u8,232u8,93u8, - 34u8,159u8,77u8,52u8,131u8,40u8,51u8,235u8,204u8,248u8,226u8,133u8,199u8,39u8,180u8,222u8,196u8,249u8,212u8,223u8, - 186u8,207u8,106u8,46u8,183u8,50u8,232u8,247u8,209u8,145u8,209u8,233u8,243u8,81u8,21u8,77u8,95u8,133u8,21u8,74u8, - 13u8,186u8,219u8,219u8,244u8,160u8,56u8,14u8,103u8,42u8,189u8,194u8,160u8,68u8,219u8,73u8,139u8,195u8,32u8,151u8, - 209u8,158u8,6u8,113u8,226u8,10u8,82u8,247u8,35u8,244u8,64u8,25u8,254u8,49u8,231u8,5u8,178u8,136u8,103u8,25u8, - 252u8,179u8,8u8,50u8,134u8,117u8,17u8,159u8,147u8,114u8,219u8,211u8,158u8,130u8,218u8,214u8,164u8,87u8,75u8,107u8, - 147u8,208u8,157u8,124u8,231u8,203u8,95u8,203u8,118u8,173u8,216u8,104u8,163u8,58u8,255u8,236u8,97u8,218u8,64u8,211u8, - 89u8,61u8,229u8,59u8,121u8,178u8,211u8,141u8,38u8,69u8,86u8,50u8,187u8,201u8,177u8,1u8,117u8,73u8,141u8,93u8, - 72u8,111u8,225u8,69u8,153u8,249u8,123u8,200u8,106u8,207u8,177u8,58u8,41u8,190u8,202u8,120u8,80u8,212u8,111u8,182u8, - 42u8,4u8,178u8,252u8,7u8,170u8,67u8,37u8,56u8,42u8,98u8,148u8,214u8,197u8,83u8,234u8,192u8,193u8,28u8,0u8, - 167u8,17u8,37u8,239u8,195u8,242u8,78u8,65u8,96u8,119u8,238u8,24u8,125u8,246u8,42u8,72u8,18u8,217u8,215u8,18u8, - 55u8,194u8,69u8,137u8,112u8,80u8,65u8,213u8,210u8,162u8,157u8,157u8,34u8,51u8,117u8,67u8,45u8,105u8,22u8,98u8, - 212u8,189u8,235u8,211u8,123u8,158u8,76u8,143u8,143u8,67u8,105u8,232u8,152u8,140u8,26u8,103u8,193u8,82u8,97u8,246u8, - 147u8,198u8,3u8,93u8,127u8,147u8,156u8,122u8,41u8,32u8,128u8,154u8,226u8,218u8,189u8,187u8,133u8,142u8,157u8,43u8, - 144u8,166u8,226u8,62u8,187u8,40u8,244u8,202u8,196u8,185u8,14u8,122u8,219u8,172u8,143u8,216u8,148u8,138u8,84u8,82u8, - 148u8,230u8,54u8,221u8,203u8,203u8,23u8,52u8,141u8,208u8,207u8,210u8,27u8,123u8,217u8,29u8,253u8,250u8,24u8,20u8, - 215u8,239u8,227u8,31u8,24u8,75u8,12u8,251u8,236u8,159u8,146u8,18u8,101u8,68u8,87u8,228u8,108u8,117u8,71u8,173u8, - 88u8,16u8,141u8,104u8,253u8,58u8,106u8,132u8,101u8,184u8,133u8,68u8,180u8,73u8,98u8,58u8,63u8,38u8,27u8,87u8, - 162u8,84u8,207u8,137u8,14u8,162u8,56u8,95u8,36u8,96u8,144u8,203u8,234u8,202u8,107u8,110u8,223u8,141u8,129u8,128u8, - 60u8,247u8,158u8,220u8,30u8,130u8,48u8,136u8,88u8,2u8,47u8,70u8,40u8,115u8,195u8,193u8,175u8,151u8,182u8,97u8, - 85u8,217u8,144u8,19u8,19u8,102u8,220u8,187u8,209u8,131u8,251u8,209u8,10u8,66u8,122u8,194u8,226u8,185u8,29u8,5u8, - 232u8,128u8,180u8,14u8,41u8,80u8,50u8,40u8,19u8,194u8,25u8,69u8,203u8,19u8,86u8,57u8,217u8,99u8,188u8,127u8, - 211u8,239u8,177u8,65u8,127u8,48u8,28u8,61u8,81u8,199u8,164u8,28u8,141u8,196u8,50u8,37u8,179u8,228u8,27u8,104u8, - 201u8,11u8,30u8,68u8,100u8,111u8,185u8,168u8,30u8,31u8,52u8,10u8,169u8,159u8,77u8,42u8,193u8,119u8,205u8,225u8, - 154u8,186u8,106u8,3u8,160u8,30u8,237u8,249u8,192u8,243u8,107u8,235u8,69u8,103u8,102u8,245u8,84u8,226u8,219u8,44u8, - 236u8,187u8,251u8,222u8,203u8,14u8,218u8,207u8,46u8,24u8,62u8,11u8,110u8,121u8,75u8,91u8,46u8,111u8,38u8,24u8, - 105u8,20u8,126u8,207u8,32u8,101u8,235u8,45u8,174u8,240u8,214u8,121u8,84u8,173u8,114u8,90u8,181u8,168u8,158u8,14u8, - 138u8,59u8,106u8,168u8,233u8,15u8,28u8,71u8,171u8,238u8,192u8,206u8,11u8,246u8,54u8,21u8,93u8,171u8,136u8,10u8, - 175u8,35u8,171u8,89u8,118u8,27u8,85u8,148u8,212u8,177u8,223u8,127u8,119u8,2u8,118u8,170u8,12u8,104u8,94u8,128u8, - 253u8,210u8,122u8,205u8,139u8,50u8,75u8,253u8,208u8,5u8,121u8,107u8,7u8,8u8,112u8,130u8,3u8,101u8,67u8,147u8, - 114u8,46u8,1u8,21u8,54u8,106u8,63u8,85u8,94u8,87u8,62u8,129u8,39u8,47u8,195u8,16u8,27u8,182u8,110u8,2u8, - 214u8,218u8,236u8,167u8,91u8,53u8,25u8,106u8,150u8,109u8,93u8,67u8,156u8,74u8,210u8,150u8,226u8,155u8,43u8,184u8, - 126u8,92u8,196u8,115u8,78u8,163u8,175u8,83u8,118u8,52u8,24u8,156u8,200u8,44u8,197u8,33u8,202u8,30u8,36u8,51u8, - 47u8,13u8,23u8,208u8,12u8,9u8,207u8,169u8,253u8,40u8,51u8,222u8,232u8,6u8,84u8,141u8,57u8,135u8,33u8,215u8, - 218u8,142u8,173u8,59u8,131u8,245u8,56u8,245u8,16u8,218u8,217u8,44u8,116u8,171u8,119u8,194u8,81u8,195u8,190u8,211u8, - 63u8,212u8,57u8,224u8,228u8,59u8,32u8,212u8,210u8,133u8,234u8,141u8,110u8,226u8,159u8,110u8,194u8,193u8,102u8,32u8, - 53u8,165u8,182u8,118u8,43u8,45u8,186u8,157u8,126u8,199u8,130u8,184u8,109u8,179u8,53u8,128u8,182u8,252u8,252u8,115u8, - 211u8,140u8,159u8,172u8,172u8,102u8,110u8,175u8,21u8,217u8,232u8,147u8,186u8,246u8,183u8,117u8,76u8,155u8,56u8,93u8, - 53u8,80u8,238u8,113u8,226u8,73u8,55u8,163u8,187u8,143u8,191u8,86u8,184u8,179u8,78u8,226u8,72u8,37u8,238u8,106u8, - 228u8,165u8,186u8,174u8,251u8,245u8,109u8,86u8,107u8,151u8,181u8,84u8,15u8,54u8,148u8,42u8,221u8,248u8,236u8,58u8, - 35u8,226u8,237u8,92u8,168u8,200u8,99u8,19u8,81u8,104u8,5u8,155u8,153u8,194u8,30u8,223u8,20u8,253u8,18u8,239u8, - 130u8,178u8,237u8,167u8,229u8,63u8,78u8,234u8,145u8,57u8,3u8,89u8,230u8,104u8,79u8,101u8,199u8,167u8,58u8,166u8, - 165u8,186u8,184u8,170u8,112u8,176u8,43u8,73u8,182u8,114u8,146u8,143u8,169u8,233u8,28u8,205u8,130u8,88u8,112u8,117u8, - 190u8,84u8,12u8,75u8,180u8,127u8,129u8,109u8,66u8,113u8,139u8,164u8,150u8,8u8,178u8,242u8,114u8,82u8,200u8,226u8, - 135u8,255u8,122u8,129u8,80u8,227u8,190u8,158u8,228u8,106u8,52u8,87u8,145u8,192u8,151u8,29u8,213u8,106u8,106u8,245u8, - 245u8,207u8,36u8,81u8,140u8,38u8,6u8,31u8,71u8,26u8,71u8,115u8,195u8,11u8,231u8,72u8,144u8,27u8,155u8,110u8, - 224u8,88u8,210u8,222u8,134u8,171u8,186u8,148u8,211u8,208u8,129u8,87u8,189u8,166u8,105u8,39u8,237u8,245u8,220u8,209u8, - 29u8,231u8,99u8,122u8,222u8,170u8,117u8,34u8,68u8,210u8,166u8,85u8,66u8,182u8,83u8,53u8,84u8,202u8,174u8,141u8, - 103u8,213u8,80u8,131u8,134u8,164u8,94u8,232u8,116u8,211u8,215u8,28u8,209u8,198u8,41u8,25u8,50u8,244u8,45u8,233u8, - 209u8,250u8,81u8,195u8,186u8,142u8,100u8,133u8,215u8,82u8,134u8,54u8,245u8,133u8,220u8,92u8,13u8,116u8,236u8,50u8, - 237u8,208u8,208u8,60u8,184u8,103u8,28u8,233u8,215u8,92u8,49u8,178u8,43u8,26u8,195u8,60u8,244u8,162u8,15u8,149u8, - 223u8,118u8,114u8,167u8,99u8,160u8,188u8,222u8,91u8,51u8,246u8,250u8,83u8,4u8,171u8,53u8,63u8,116u8,3u8,240u8, - 205u8,233u8,17u8,220u8,61u8,180u8,199u8,47u8,127u8,100u8,122u8,4u8,119u8,63u8,32u8,61u8,116u8,12u8,54u8,167u8, - 199u8,159u8,34u8,88u8,219u8,87u8,54u8,135u8,203u8,215u8,149u8,54u8,127u8,166u8,217u8,237u8,26u8,245u8,64u8,91u8, - 141u8,250u8,170u8,174u8,91u8,35u8,228u8,123u8,91u8,238u8,170u8,242u8,219u8,145u8,32u8,153u8,183u8,182u8,136u8,126u8, - 219u8,172u8,204u8,182u8,167u8,31u8,143u8,14u8,191u8,171u8,49u8,174u8,66u8,183u8,69u8,7u8,236u8,168u8,88u8,59u8, - 53u8,251u8,214u8,185u8,106u8,163u8,183u8,172u8,18u8,175u8,10u8,250u8,118u8,109u8,155u8,55u8,237u8,218u8,182u8,175u8, - 209u8,39u8,100u8,102u8,94u8,155u8,25u8,44u8,183u8,63u8,22u8,177u8,191u8,16u8,64u8,203u8,192u8,191u8,148u8,16u8, - 34u8,63u8,190u8,222u8,196u8,183u8,152u8,204u8,232u8,155u8,96u8,61u8,111u8,49u8,156u8,23u8,217u8,55u8,213u8,123u8, - 239u8,139u8,132u8,141u8,1u8,201u8,121u8,120u8,170u8,49u8,230u8,173u8,97u8,25u8,181u8,70u8,134u8,122u8,189u8,11u8, - 148u8,9u8,206u8,16u8,65u8,182u8,113u8,218u8,167u8,191u8,176u8,221u8,161u8,110u8,120u8,79u8,60u8,234u8,113u8,215u8, - 163u8,227u8,168u8,83u8,142u8,149u8,96u8,163u8,210u8,201u8,62u8,187u8,118u8,245u8,99u8,199u8,144u8,189u8,250u8,222u8, - 198u8,161u8,211u8,112u8,40u8,151u8,67u8,32u8,28u8,187u8,58u8,78u8,47u8,111u8,244u8,55u8,133u8,148u8,223u8,21u8, - 245u8,172u8,104u8,18u8,21u8,143u8,147u8,109u8,206u8,91u8,6u8,139u8,34u8,171u8,238u8,67u8,136u8,150u8,61u8,139u8, - 58u8,100u8,52u8,71u8,219u8,206u8,204u8,217u8,88u8,143u8,88u8,166u8,198u8,22u8,174u8,232u8,154u8,251u8,39u8,117u8, - 130u8,36u8,196u8,238u8,186u8,27u8,12u8,116u8,255u8,106u8,143u8,205u8,253u8,2u8,74u8,123u8,164u8,208u8,206u8,79u8, - 1u8,20u8,5u8,111u8,228u8,133u8,233u8,234u8,213u8,235u8,171u8,99u8,182u8,20u8,217u8,103u8,245u8,163u8,37u8,249u8, - 101u8,159u8,190u8,70u8,208u8,15u8,135u8,242u8,2u8,31u8,200u8,212u8,228u8,132u8,70u8,6u8,140u8,102u8,6u8,232u8, - 179u8,67u8,66u8,33u8,150u8,252u8,118u8,128u8,211u8,137u8,128u8,253u8,122u8,254u8,99u8,150u8,128u8,113u8,40u8,5u8, - 207u8,79u8,247u8,7u8,130u8,71u8,117u8,152u8,107u8,208u8,33u8,151u8,252u8,0u8,112u8,208u8,122u8,186u8,110u8,156u8, - 178u8,97u8,91u8,58u8,252u8,1u8,184u8,193u8,233u8,195u8,128u8,205u8,232u8,113u8,40u8,83u8,9u8,65u8,207u8,23u8, - 116u8,0u8,8u8,211u8,233u8,53u8,216u8,145u8,83u8,218u8,135u8,6u8,15u8,173u8,157u8,144u8,96u8,154u8,37u8,123u8, - 208u8,161u8,244u8,54u8,145u8,199u8,237u8,124u8,180u8,215u8,5u8,185u8,23u8,70u8,66u8,61u8,208u8,107u8,112u8,214u8, - 22u8,106u8,147u8,80u8,221u8,45u8,165u8,26u8,85u8,63u8,56u8,211u8,155u8,216u8,174u8,201u8,101u8,181u8,230u8,7u8, - 37u8,179u8,115u8,6u8,94u8,208u8,191u8,39u8,229u8,187u8,207u8,99u8,219u8,26u8,240u8,208u8,56u8,96u8,143u8,106u8, - 127u8,216u8,251u8,119u8,111u8,94u8,93u8,156u8,235u8,95u8,54u8,188u8,111u8,188u8,223u8,113u8,46u8,17u8,234u8,215u8, - 165u8,95u8,239u8,229u8,47u8,101u8,48u8,205u8,194u8,156u8,34u8,18u8,33u8,230u8,71u8,246u8,155u8,7u8,16u8,115u8, - 39u8,199u8,168u8,122u8,113u8,194u8,111u8,121u8,210u8,34u8,163u8,158u8,54u8,38u8,77u8,198u8,49u8,253u8,114u8,53u8, - 139u8,195u8,162u8,58u8,255u8,251u8,157u8,255u8,1u8,16u8,123u8,102u8,8u8,60u8,43u8,0u8,0u8,0u8,0u8,7u8, - 97u8,108u8,103u8,101u8,98u8,114u8,97u8,219u8,6u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,173u8, - 86u8,75u8,143u8,219u8,54u8,16u8,190u8,251u8,87u8,12u8,80u8,160u8,144u8,90u8,65u8,110u8,139u8,162u8,40u8,100u8, - 119u8,47u8,65u8,211u8,91u8,27u8,212u8,189u8,5u8,11u8,153u8,146u8,70u8,18u8,1u8,153u8,20u8,72u8,202u8,141u8, - 18u8,248u8,191u8,103u8,72u8,61u8,76u8,89u8,94u8,103u8,3u8,196u8,135u8,93u8,83u8,226u8,60u8,190u8,199u8,144u8, - 222u8,110u8,183u8,240u8,95u8,205u8,53u8,156u8,100u8,209u8,53u8,8u8,173u8,146u8,103u8,94u8,160u8,134u8,10u8,5u8, - 42u8,158u8,131u8,54u8,170u8,203u8,141u8,222u8,150u8,157u8,200u8,13u8,151u8,66u8,67u8,41u8,21u8,200u8,22u8,21u8, - 27u8,150u8,178u8,4u8,214u8,84u8,152u8,41u8,54u8,239u8,238u8,20u8,37u8,8u8,48u8,174u8,98u8,40u8,57u8,54u8, - 133u8,6u8,38u8,10u8,168u8,148u8,236u8,90u8,29u8,70u8,155u8,45u8,213u8,252u8,191u8,230u8,121u8,13u8,57u8,19u8, - 144u8,33u8,116u8,26u8,11u8,48u8,18u8,178u8,142u8,55u8,197u8,92u8,57u8,87u8,125u8,107u8,100u8,165u8,88u8,91u8, - 219u8,204u8,121u8,141u8,39u8,74u8,203u8,140u8,108u8,99u8,151u8,226u8,128u8,8u8,199u8,177u8,116u8,250u8,67u8,124u8, - 146u8,103u8,60u8,186u8,230u8,242u8,78u8,41u8,20u8,166u8,233u8,129u8,159u8,218u8,134u8,162u8,132u8,161u8,252u8,247u8, - 154u8,116u8,137u8,92u8,178u8,55u8,115u8,140u8,238u8,218u8,86u8,42u8,27u8,225u8,129u8,228u8,34u8,111u8,186u8,2u8, - 1u8,135u8,116u8,160u8,169u8,67u8,214u8,240u8,143u8,238u8,245u8,150u8,248u8,242u8,215u8,14u8,45u8,43u8,10u8,110u8, - 23u8,241u8,102u8,100u8,150u8,17u8,24u8,157u8,106u8,83u8,36u8,201u8,216u8,10u8,124u8,218u8,0u8,125u8,8u8,61u8, - 184u8,199u8,178u8,181u8,1u8,73u8,242u8,233u8,31u8,247u8,37u8,2u8,45u8,79u8,24u8,129u8,144u8,2u8,47u8,187u8, - 229u8,214u8,18u8,153u8,3u8,144u8,36u8,30u8,75u8,125u8,58u8,177u8,129u8,130u8,101u8,13u8,22u8,187u8,141u8,139u8, - 218u8,78u8,26u8,15u8,192u8,65u8,97u8,75u8,161u8,4u8,195u8,234u8,50u8,67u8,178u8,58u8,138u8,123u8,44u8,193u8, - 241u8,112u8,140u8,93u8,162u8,49u8,254u8,207u8,33u8,98u8,223u8,214u8,76u8,24u8,121u8,130u8,195u8,19u8,212u8,76u8, - 67u8,46u8,219u8,62u8,130u8,66u8,201u8,118u8,4u8,102u8,63u8,180u8,165u8,104u8,48u8,129u8,238u8,183u8,95u8,221u8, - 179u8,203u8,212u8,210u8,248u8,15u8,222u8,117u8,89u8,67u8,213u8,174u8,246u8,202u8,176u8,226u8,34u8,158u8,118u8,205u8, - 0u8,222u8,200u8,83u8,219u8,25u8,106u8,230u8,3u8,252u8,8u8,253u8,32u8,244u8,216u8,186u8,166u8,135u8,71u8,71u8, - 250u8,145u8,94u8,188u8,2u8,71u8,59u8,215u8,180u8,50u8,237u8,15u8,79u8,193u8,135u8,4u8,190u8,159u8,96u8,29u8, - 158u8,34u8,232u8,23u8,235u8,48u8,129u8,235u8,194u8,3u8,199u8,50u8,50u8,74u8,218u8,137u8,6u8,181u8,78u8,239u8, - 10u8,33u8,200u8,16u8,103u8,212u8,147u8,32u8,65u8,184u8,155u8,131u8,239u8,102u8,244u8,41u8,163u8,214u8,82u8,78u8, - 214u8,85u8,130u8,53u8,174u8,199u8,120u8,120u8,67u8,221u8,141u8,223u8,194u8,57u8,238u8,178u8,36u8,151u8,244u8,86u8, - 61u8,92u8,109u8,201u8,69u8,5u8,12u8,178u8,158u8,248u8,99u8,74u8,177u8,222u8,14u8,219u8,107u8,165u8,39u8,211u8, - 13u8,225u8,21u8,33u8,17u8,75u8,231u8,91u8,21u8,78u8,204u8,192u8,241u8,237u8,49u8,158u8,75u8,255u8,139u8,20u8, - 42u8,156u8,109u8,129u8,151u8,96u8,106u8,132u8,219u8,1u8,41u8,25u8,39u8,54u8,86u8,98u8,92u8,183u8,225u8,254u8, - 16u8,193u8,219u8,167u8,192u8,182u8,172u8,73u8,139u8,51u8,230u8,70u8,170u8,125u8,247u8,187u8,213u8,98u8,24u8,143u8, - 253u8,149u8,192u8,111u8,166u8,73u8,131u8,6u8,2u8,221u8,229u8,57u8,98u8,129u8,69u8,52u8,74u8,17u8,194u8,31u8, - 126u8,107u8,158u8,40u8,215u8,30u8,189u8,36u8,132u8,249u8,154u8,35u8,188u8,17u8,215u8,142u8,116u8,224u8,75u8,63u8, - 214u8,128u8,139u8,39u8,38u8,9u8,67u8,131u8,190u8,12u8,180u8,116u8,6u8,15u8,4u8,63u8,76u8,237u8,189u8,94u8, - 88u8,107u8,2u8,223u8,20u8,95u8,35u8,180u8,167u8,218u8,173u8,102u8,99u8,241u8,219u8,9u8,186u8,74u8,248u8,173u8, - 212u8,122u8,81u8,145u8,177u8,131u8,197u8,148u8,172u8,14u8,158u8,96u8,117u8,242u8,32u8,157u8,32u8,53u8,42u8,140u8, - 195u8,105u8,207u8,223u8,174u8,248u8,227u8,195u8,73u8,204u8,123u8,238u8,155u8,228u8,175u8,151u8,141u8,28u8,100u8,82u8, - 54u8,145u8,61u8,27u8,71u8,84u8,94u8,174u8,197u8,244u8,83u8,130u8,1u8,75u8,250u8,179u8,59u8,74u8,39u8,103u8, - 166u8,191u8,184u8,101u8,232u8,254u8,174u8,82u8,188u8,216u8,140u8,119u8,40u8,47u8,132u8,217u8,173u8,56u8,90u8,17u8, - 176u8,230u8,232u8,157u8,226u8,103u8,102u8,190u8,64u8,146u8,67u8,244u8,245u8,90u8,123u8,78u8,177u8,83u8,245u8,232u8, - 174u8,11u8,194u8,144u8,46u8,54u8,123u8,242u8,236u8,150u8,238u8,10u8,220u8,133u8,137u8,74u8,73u8,149u8,36u8,66u8, - 154u8,212u8,251u8,81u8,16u8,252u8,20u8,46u8,220u8,241u8,221u8,123u8,82u8,201u8,164u8,82u8,52u8,253u8,243u8,173u8, - 205u8,135u8,58u8,15u8,27u8,15u8,74u8,123u8,143u8,104u8,94u8,209u8,15u8,24u8,191u8,247u8,219u8,43u8,155u8,248u8, - 175u8,48u8,29u8,31u8,164u8,101u8,195u8,42u8,27u8,26u8,141u8,74u8,188u8,191u8,217u8,94u8,161u8,121u8,76u8,215u8, - 184u8,53u8,8u8,159u8,231u8,20u8,207u8,163u8,161u8,238u8,184u8,126u8,37u8,215u8,74u8,210u8,205u8,101u8,243u8,25u8, - 202u8,138u8,72u8,100u8,23u8,10u8,0u8,0u8,0u8,0u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8, - 175u8,25u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,26u8,107u8,111u8,219u8,56u8,242u8,251u8, - 254u8,10u8,182u8,7u8,4u8,118u8,207u8,117u8,252u8,138u8,237u8,56u8,77u8,111u8,211u8,221u8,236u8,93u8,128u8,189u8, - 222u8,162u8,205u8,246u8,14u8,40u8,90u8,69u8,150u8,232u8,88u8,168u8,44u8,25u8,18u8,21u8,199u8,87u8,228u8,191u8, - 223u8,204u8,144u8,162u8,72u8,89u8,146u8,221u8,199u8,222u8,118u8,215u8,64u8,155u8,152u8,228u8,188u8,159u8,28u8,102u8, - 21u8,251u8,89u8,200u8,153u8,187u8,22u8,113u8,234u8,164u8,194u8,159u8,205u8,196u8,118u8,205u8,157u8,32u8,90u8,196u8, - 236u8,227u8,119u8,12u8,62u8,89u8,202u8,25u8,109u8,204u8,189u8,244u8,204u8,94u8,73u8,69u8,18u8,68u8,183u8,165u8, - 197u8,5u8,119u8,69u8,150u8,240u8,242u8,217u8,59u8,238u8,137u8,56u8,57u8,251u8,142u8,86u8,143u8,143u8,213u8,15u8, - 118u8,153u8,36u8,113u8,194u8,188u8,216u8,231u8,105u8,190u8,67u8,63u8,189u8,56u8,74u8,5u8,187u8,116u8,94u8,94u8, - 92u8,95u8,189u8,185u8,116u8,126u8,250u8,245u8,165u8,243u8,242u8,95u8,215u8,206u8,197u8,155u8,139u8,171u8,159u8,47u8, - 94u8,252u8,124u8,57u8,99u8,217u8,120u8,196u8,206u8,89u8,127u8,7u8,227u8,107u8,145u8,100u8,158u8,176u8,177u8,165u8, - 180u8,198u8,174u8,65u8,180u8,43u8,148u8,108u8,233u8,166u8,64u8,97u8,189u8,237u8,48u8,63u8,137u8,215u8,29u8,216u8, - 143u8,19u8,174u8,228u8,197u8,143u8,235u8,121u8,113u8,22u8,9u8,199u8,245u8,125u8,144u8,36u8,157u8,49u8,245u8,75u8, - 71u8,159u8,88u8,145u8,222u8,156u8,200u8,93u8,241u8,25u8,147u8,178u8,61u8,203u8,166u8,207u8,139u8,3u8,146u8,100u8, - 205u8,129u8,135u8,50u8,211u8,191u8,100u8,243u8,48u8,240u8,216u8,34u8,139u8,60u8,17u8,128u8,232u8,22u8,247u8,107u8, - 189u8,89u8,102u8,172u8,165u8,141u8,53u8,99u8,71u8,185u8,116u8,109u8,205u8,174u8,33u8,145u8,62u8,216u8,45u8,161u8, - 48u8,249u8,49u8,8u8,25u8,242u8,213u8,17u8,41u8,132u8,170u8,164u8,99u8,96u8,168u8,161u8,97u8,168u8,232u8,51u8, - 105u8,24u8,24u8,108u8,189u8,30u8,179u8,87u8,28u8,188u8,48u8,74u8,153u8,88u8,114u8,230u8,101u8,73u8,194u8,35u8, - 193u8,188u8,165u8,27u8,68u8,236u8,234u8,199u8,14u8,91u8,5u8,232u8,119u8,224u8,188u8,108u8,179u8,116u8,5u8,187u8, - 145u8,254u8,191u8,72u8,0u8,209u8,38u8,78u8,62u8,204u8,102u8,116u8,212u8,9u8,192u8,113u8,111u8,185u8,104u8,181u8, - 111u8,216u8,38u8,206u8,66u8,159u8,37u8,132u8,181u8,195u8,248u8,189u8,199u8,215u8,130u8,1u8,182u8,155u8,191u8,188u8, - 21u8,60u8,21u8,239u8,110u8,52u8,101u8,109u8,196u8,14u8,32u8,231u8,224u8,87u8,98u8,25u8,164u8,108u8,19u8,132u8, - 33u8,115u8,195u8,141u8,187u8,77u8,21u8,22u8,118u8,51u8,202u8,166u8,55u8,204u8,85u8,60u8,106u8,222u8,8u8,8u8, - 86u8,15u8,97u8,10u8,144u8,106u8,186u8,10u8,235u8,102u8,25u8,120u8,75u8,126u8,199u8,19u8,64u8,198u8,54u8,128u8, - 103u8,237u8,166u8,41u8,247u8,153u8,136u8,27u8,49u8,6u8,81u8,32u8,2u8,55u8,12u8,254u8,203u8,157u8,69u8,156u8, - 56u8,40u8,19u8,80u8,232u8,150u8,109u8,150u8,3u8,180u8,192u8,56u8,217u8,212u8,48u8,74u8,176u8,96u8,173u8,71u8, - 121u8,236u8,207u8,102u8,58u8,161u8,132u8,193u8,220u8,201u8,129u8,28u8,30u8,185u8,243u8,144u8,3u8,112u8,219u8,128u8, - 164u8,128u8,155u8,199u8,137u8,104u8,81u8,162u8,224u8,104u8,25u8,228u8,231u8,14u8,184u8,241u8,1u8,133u8,43u8,120u8, - 171u8,62u8,17u8,180u8,219u8,26u8,209u8,131u8,202u8,5u8,148u8,63u8,114u8,146u8,65u8,36u8,120u8,18u8,185u8,97u8, - 171u8,93u8,225u8,133u8,145u8,43u8,130u8,59u8,78u8,130u8,145u8,83u8,197u8,139u8,103u8,215u8,207u8,81u8,180u8,220u8, - 5u8,207u8,26u8,143u8,163u8,223u8,41u8,0u8,153u8,10u8,103u8,179u8,215u8,42u8,37u8,18u8,152u8,113u8,190u8,130u8, - 31u8,84u8,224u8,89u8,217u8,97u8,201u8,23u8,94u8,252u8,240u8,154u8,165u8,96u8,137u8,14u8,58u8,216u8,124u8,11u8, - 182u8,232u8,176u8,120u8,193u8,64u8,33u8,25u8,164u8,106u8,112u8,87u8,248u8,205u8,73u8,248u8,66u8,153u8,231u8,88u8, - 231u8,17u8,200u8,126u8,156u8,19u8,138u8,183u8,132u8,99u8,205u8,189u8,119u8,173u8,165u8,16u8,235u8,116u8,118u8,124u8, - 124u8,27u8,136u8,101u8,54u8,239u8,122u8,241u8,234u8,216u8,15u8,248u8,234u8,24u8,242u8,121u8,187u8,18u8,254u8,6u8, - 141u8,239u8,32u8,3u8,160u8,16u8,231u8,14u8,121u8,189u8,97u8,224u8,20u8,204u8,133u8,4u8,4u8,172u8,111u8,83u8, - 240u8,103u8,224u8,7u8,48u8,173u8,98u8,169u8,138u8,20u8,54u8,124u8,141u8,36u8,2u8,120u8,140u8,173u8,181u8,43u8, - 80u8,88u8,224u8,30u8,220u8,112u8,195u8,49u8,0u8,210u8,93u8,236u8,142u8,12u8,242u8,180u8,160u8,162u8,17u8,153u8, - 212u8,228u8,41u8,210u8,11u8,243u8,183u8,160u8,250u8,32u8,93u8,237u8,184u8,167u8,129u8,22u8,13u8,163u8,244u8,132u8, - 57u8,165u8,45u8,43u8,70u8,225u8,118u8,133u8,202u8,21u8,230u8,144u8,71u8,183u8,98u8,89u8,144u8,2u8,84u8,62u8, - 153u8,34u8,225u8,107u8,240u8,105u8,72u8,32u8,46u8,134u8,117u8,87u8,99u8,144u8,199u8,102u8,51u8,9u8,216u8,58u8, - 2u8,133u8,66u8,249u8,140u8,29u8,178u8,88u8,78u8,187u8,109u8,249u8,157u8,74u8,22u8,244u8,59u8,185u8,17u8,133u8, - 153u8,193u8,85u8,200u8,69u8,145u8,223u8,160u8,190u8,105u8,183u8,84u8,14u8,9u8,206u8,118u8,86u8,148u8,41u8,8u8, - 236u8,68u8,60u8,106u8,149u8,171u8,194u8,145u8,198u8,208u8,102u8,231u8,231u8,236u8,123u8,29u8,140u8,29u8,214u8,171u8, - 0u8,55u8,115u8,125u8,9u8,116u8,254u8,88u8,127u8,127u8,220u8,97u8,253u8,10u8,96u8,51u8,137u8,239u8,0u8,231u8, - 92u8,3u8,236u8,64u8,193u8,90u8,138u8,104u8,45u8,238u8,65u8,198u8,239u8,129u8,179u8,182u8,173u8,19u8,157u8,50u8, - 224u8,4u8,4u8,87u8,112u8,27u8,241u8,164u8,109u8,27u8,239u8,223u8,28u8,252u8,76u8,230u8,53u8,153u8,86u8,200u8, - 239u8,85u8,6u8,194u8,192u8,137u8,19u8,31u8,146u8,32u8,122u8,20u8,110u8,168u8,88u8,244u8,92u8,240u8,66u8,0u8, - 153u8,67u8,32u8,133u8,97u8,188u8,225u8,126u8,97u8,207u8,34u8,123u8,1u8,245u8,232u8,22u8,18u8,161u8,92u8,112u8, - 22u8,161u8,123u8,11u8,90u8,93u8,220u8,119u8,148u8,205u8,223u8,22u8,71u8,33u8,19u8,59u8,213u8,201u8,78u8,157u8, - 105u8,181u8,223u8,105u8,176u8,119u8,109u8,35u8,69u8,129u8,12u8,215u8,192u8,152u8,80u8,193u8,194u8,163u8,187u8,32u8, - 137u8,163u8,149u8,89u8,168u8,24u8,184u8,62u8,20u8,137u8,238u8,142u8,214u8,139u8,52u8,140u8,138u8,134u8,35u8,133u8, - 113u8,26u8,60u8,205u8,209u8,89u8,203u8,242u8,57u8,236u8,209u8,204u8,6u8,16u8,149u8,57u8,155u8,93u8,227u8,15u8, - 131u8,221u8,156u8,116u8,145u8,249u8,230u8,113u8,28u8,62u8,151u8,28u8,228u8,233u8,47u8,19u8,139u8,105u8,107u8,254u8, - 24u8,119u8,30u8,183u8,171u8,189u8,173u8,128u8,135u8,170u8,94u8,9u8,157u8,77u8,17u8,182u8,223u8,12u8,59u8,30u8, - 213u8,0u8,143u8,71u8,8u8,61u8,104u8,134u8,238u8,15u8,234u8,104u8,195u8,14u8,194u8,15u8,27u8,225u8,85u8,160u8, - 85u8,163u8,80u8,155u8,136u8,101u8,212u8,136u8,69u8,186u8,117u8,53u8,18u8,185u8,135u8,56u8,78u8,74u8,62u8,35u8, - 93u8,169u8,1u8,109u8,209u8,51u8,85u8,163u8,46u8,246u8,17u8,253u8,184u8,145u8,69u8,117u8,214u8,64u8,217u8,136u8, - 211u8,56u8,135u8,184u8,39u8,159u8,128u8,91u8,39u8,184u8,131u8,40u8,244u8,238u8,251u8,198u8,77u8,5u8,156u8,85u8, - 67u8,35u8,221u8,41u8,170u8,204u8,212u8,153u8,76u8,82u8,13u8,188u8,24u8,233u8,181u8,130u8,118u8,29u8,53u8,164u8, - 117u8,218u8,40u8,163u8,213u8,228u8,80u8,68u8,217u8,75u8,180u8,172u8,176u8,117u8,118u8,119u8,8u8,0u8,131u8,123u8, - 71u8,73u8,214u8,217u8,226u8,91u8,19u8,255u8,70u8,88u8,215u8,234u8,15u8,66u8,118u8,231u8,172u8,65u8,191u8,94u8, - 237u8,164u8,247u8,126u8,175u8,156u8,131u8,160u8,13u8,13u8,22u8,91u8,39u8,142u8,194u8,109u8,145u8,138u8,212u8,162u8, - 42u8,109u8,159u8,95u8,254u8,240u8,112u8,169u8,244u8,1u8,72u8,67u8,49u8,180u8,65u8,141u8,178u8,7u8,96u8,53u8, - 69u8,208u8,6u8,49u8,138u8,29u8,59u8,103u8,53u8,165u8,175u8,0u8,193u8,254u8,171u8,220u8,232u8,146u8,139u8,236u8, - 178u8,109u8,86u8,233u8,179u8,42u8,8u8,139u8,91u8,187u8,48u8,87u8,158u8,183u8,88u8,181u8,106u8,241u8,153u8,217u8, - 48u8,127u8,170u8,181u8,156u8,91u8,14u8,121u8,41u8,240u8,168u8,237u8,61u8,196u8,112u8,127u8,98u8,139u8,153u8,50u8, - 150u8,175u8,213u8,135u8,216u8,208u8,130u8,55u8,246u8,14u8,177u8,167u8,5u8,107u8,236u8,85u8,24u8,87u8,203u8,85u8, - 109u8,73u8,115u8,236u8,129u8,183u8,176u8,212u8,129u8,171u8,220u8,35u8,4u8,112u8,2u8,116u8,71u8,68u8,109u8,152u8, - 209u8,106u8,47u8,12u8,87u8,81u8,35u8,150u8,31u8,50u8,152u8,165u8,172u8,208u8,215u8,104u8,200u8,130u8,227u8,21u8, - 246u8,113u8,15u8,204u8,235u8,96u8,181u8,14u8,185u8,156u8,219u8,148u8,70u8,51u8,6u8,115u8,139u8,128u8,135u8,62u8, - 222u8,153u8,14u8,98u8,35u8,70u8,148u8,247u8,175u8,115u8,238u8,247u8,160u8,117u8,250u8,51u8,134u8,61u8,75u8,167u8, - 180u8,60u8,64u8,122u8,229u8,197u8,33u8,221u8,36u8,202u8,171u8,35u8,88u8,133u8,222u8,161u8,188u8,124u8,50u8,179u8, - 196u8,43u8,111u8,143u8,225u8,170u8,121u8,136u8,60u8,215u8,155u8,248u8,5u8,240u8,151u8,26u8,74u8,213u8,152u8,144u8, - 241u8,93u8,1u8,104u8,117u8,32u8,87u8,27u8,41u8,232u8,9u8,93u8,188u8,198u8,235u8,205u8,153u8,213u8,159u8,43u8, - 175u8,198u8,38u8,189u8,119u8,223u8,83u8,77u8,58u8,222u8,204u8,46u8,163u8,20u8,219u8,108u8,186u8,163u8,171u8,201u8, - 3u8,220u8,1u8,189u8,36u8,134u8,208u8,80u8,173u8,182u8,190u8,23u8,234u8,251u8,96u8,10u8,131u8,137u8,149u8,155u8, - 118u8,237u8,166u8,212u8,188u8,104u8,150u8,39u8,112u8,112u8,115u8,147u8,157u8,16u8,109u8,152u8,9u8,71u8,223u8,63u8, - 12u8,232u8,163u8,133u8,27u8,166u8,156u8,42u8,96u8,159u8,154u8,79u8,172u8,253u8,168u8,55u8,38u8,220u8,15u8,192u8, - 75u8,159u8,46u8,211u8,221u8,38u8,28u8,212u8,148u8,30u8,245u8,74u8,56u8,96u8,210u8,241u8,9u8,24u8,176u8,53u8, - 85u8,40u8,166u8,5u8,10u8,184u8,124u8,74u8,28u8,83u8,121u8,165u8,223u8,131u8,132u8,58u8,212u8,156u8,145u8,113u8, - 129u8,6u8,214u8,115u8,94u8,198u8,101u8,68u8,176u8,127u8,161u8,210u8,19u8,92u8,28u8,92u8,150u8,13u8,78u8,198u8, - 221u8,102u8,141u8,145u8,89u8,145u8,198u8,112u8,80u8,221u8,174u8,155u8,167u8,149u8,81u8,204u8,243u8,52u8,113u8,37u8, - 11u8,17u8,201u8,40u8,31u8,61u8,218u8,76u8,201u8,20u8,230u8,81u8,110u8,32u8,191u8,128u8,201u8,149u8,88u8,198u8, - 153u8,144u8,97u8,32u8,221u8,154u8,110u8,246u8,253u8,61u8,252u8,22u8,249u8,229u8,227u8,131u8,97u8,36u8,147u8,218u8, - 143u8,220u8,11u8,93u8,240u8,77u8,23u8,48u8,98u8,228u8,229u8,49u8,132u8,36u8,97u8,177u8,255u8,20u8,181u8,38u8, - 9u8,119u8,237u8,90u8,65u8,199u8,85u8,202u8,3u8,151u8,55u8,3u8,247u8,163u8,74u8,63u8,189u8,135u8,179u8,10u8, - 201u8,136u8,117u8,144u8,63u8,136u8,252u8,192u8,131u8,121u8,149u8,143u8,131u8,142u8,3u8,124u8,165u8,117u8,100u8,209u8, - 172u8,18u8,8u8,25u8,243u8,100u8,54u8,43u8,56u8,179u8,211u8,27u8,249u8,138u8,93u8,180u8,116u8,86u8,163u8,136u8, - 232u8,84u8,236u8,65u8,106u8,232u8,85u8,173u8,15u8,107u8,214u8,71u8,53u8,235u8,39u8,51u8,91u8,111u8,85u8,103u8, - 32u8,203u8,245u8,140u8,186u8,100u8,27u8,73u8,9u8,103u8,91u8,41u8,138u8,225u8,206u8,158u8,80u8,226u8,0u8,101u8, - 6u8,81u8,26u8,248u8,188u8,219u8,160u8,120u8,212u8,244u8,38u8,192u8,76u8,150u8,173u8,212u8,104u8,106u8,29u8,71u8, - 112u8,149u8,78u8,247u8,232u8,223u8,86u8,45u8,25u8,160u8,213u8,103u8,127u8,101u8,248u8,111u8,138u8,63u8,199u8,234u8, - 75u8,127u8,220u8,110u8,242u8,52u8,131u8,121u8,177u8,137u8,41u8,235u8,114u8,136u8,6u8,26u8,217u8,165u8,182u8,155u8, - 193u8,190u8,51u8,167u8,100u8,126u8,174u8,243u8,250u8,199u8,60u8,137u8,75u8,123u8,233u8,236u8,77u8,95u8,27u8,93u8, - 142u8,168u8,237u8,205u8,39u8,208u8,237u8,228u8,84u8,73u8,198u8,65u8,189u8,44u8,17u8,227u8,171u8,181u8,216u8,230u8, - 83u8,49u8,80u8,38u8,15u8,57u8,77u8,37u8,40u8,112u8,33u8,143u8,217u8,226u8,208u8,105u8,53u8,195u8,115u8,228u8, - 163u8,76u8,62u8,24u8,163u8,45u8,117u8,85u8,255u8,60u8,98u8,224u8,217u8,77u8,212u8,48u8,25u8,238u8,144u8,147u8, - 119u8,251u8,38u8,157u8,201u8,200u8,132u8,178u8,117u8,235u8,38u8,126u8,136u8,233u8,18u8,8u8,103u8,17u8,140u8,139u8, - 194u8,45u8,141u8,98u8,12u8,30u8,246u8,168u8,181u8,44u8,125u8,77u8,78u8,250u8,127u8,49u8,0u8,178u8,239u8,205u8, - 138u8,232u8,5u8,56u8,31u8,115u8,149u8,226u8,108u8,5u8,147u8,231u8,41u8,115u8,20u8,170u8,77u8,129u8,43u8,216u8, - 141u8,163u8,150u8,44u8,177u8,22u8,234u8,95u8,178u8,116u8,201u8,230u8,174u8,247u8,65u8,199u8,45u8,34u8,217u8,157u8, - 147u8,174u8,225u8,156u8,131u8,231u8,90u8,71u8,171u8,204u8,162u8,212u8,97u8,21u8,104u8,75u8,58u8,27u8,74u8,55u8, - 199u8,240u8,92u8,3u8,9u8,165u8,34u8,184u8,116u8,210u8,136u8,111u8,238u8,66u8,240u8,75u8,100u8,237u8,61u8,26u8, - 51u8,232u8,202u8,74u8,182u8,163u8,171u8,191u8,227u8,53u8,133u8,165u8,49u8,180u8,218u8,178u8,41u8,234u8,200u8,87u8, - 21u8,74u8,241u8,152u8,47u8,144u8,178u8,76u8,242u8,213u8,74u8,4u8,71u8,112u8,36u8,36u8,232u8,80u8,254u8,2u8, - 58u8,4u8,124u8,173u8,102u8,191u8,56u8,205u8,101u8,156u8,126u8,161u8,140u8,5u8,3u8,36u8,226u8,169u8,45u8,98u8, - 206u8,17u8,191u8,23u8,137u8,235u8,9u8,105u8,14u8,3u8,228u8,76u8,14u8,199u8,87u8,241u8,157u8,28u8,170u8,202u8, - 135u8,135u8,61u8,89u8,56u8,225u8,126u8,230u8,1u8,239u8,48u8,92u8,61u8,168u8,242u8,149u8,88u8,52u8,60u8,182u8, - 98u8,130u8,137u8,45u8,231u8,27u8,186u8,190u8,232u8,102u8,179u8,164u8,17u8,201u8,4u8,60u8,142u8,248u8,193u8,98u8, - 193u8,233u8,185u8,79u8,206u8,229u8,241u8,25u8,14u8,210u8,178u8,70u8,83u8,236u8,27u8,1u8,183u8,8u8,238u8,185u8, - 255u8,148u8,80u8,40u8,165u8,167u8,187u8,111u8,43u8,63u8,225u8,227u8,132u8,145u8,170u8,212u8,131u8,65u8,132u8,143u8, - 213u8,2u8,230u8,177u8,53u8,136u8,58u8,168u8,67u8,141u8,132u8,182u8,0u8,152u8,244u8,42u8,81u8,97u8,253u8,130u8, - 10u8,19u8,177u8,39u8,44u8,205u8,159u8,123u8,228u8,203u8,33u8,245u8,113u8,120u8,50u8,135u8,50u8,222u8,70u8,242u8, - 28u8,81u8,60u8,17u8,97u8,171u8,61u8,151u8,157u8,223u8,99u8,210u8,12u8,66u8,61u8,214u8,7u8,0u8,17u8,168u8, - 231u8,206u8,77u8,2u8,253u8,214u8,126u8,44u8,43u8,86u8,180u8,43u8,43u8,78u8,166u8,53u8,142u8,188u8,181u8,75u8, - 68u8,176u8,0u8,127u8,49u8,94u8,99u8,240u8,121u8,132u8,71u8,240u8,124u8,15u8,178u8,119u8,24u8,94u8,64u8,195u8, - 109u8,199u8,70u8,62u8,7u8,61u8,185u8,184u8,111u8,114u8,225u8,171u8,108u8,180u8,140u8,55u8,108u8,229u8,70u8,91u8, - 173u8,44u8,230u8,202u8,225u8,189u8,169u8,50u8,21u8,97u8,236u8,210u8,133u8,16u8,76u8,51u8,248u8,207u8,196u8,72u8, - 189u8,163u8,203u8,240u8,133u8,38u8,185u8,131u8,160u8,68u8,99u8,36u8,144u8,225u8,230u8,129u8,96u8,45u8,222u8,189u8, - 213u8,178u8,73u8,93u8,42u8,133u8,134u8,110u8,42u8,44u8,52u8,127u8,131u8,242u8,78u8,184u8,137u8,191u8,9u8,130u8, - 167u8,20u8,132u8,234u8,144u8,198u8,162u8,50u8,247u8,10u8,39u8,244u8,249u8,227u8,2u8,40u8,37u8,115u8,67u8,109u8, - 18u8,234u8,146u8,187u8,236u8,31u8,160u8,25u8,46u8,159u8,180u8,152u8,76u8,160u8,26u8,137u8,73u8,153u8,252u8,3u8, - 180u8,112u8,31u8,172u8,160u8,121u8,177u8,112u8,72u8,102u8,60u8,208u8,61u8,188u8,87u8,248u8,60u8,245u8,146u8,96u8, - 142u8,253u8,80u8,97u8,191u8,214u8,128u8,189u8,135u8,57u8,43u8,123u8,138u8,1u8,132u8,14u8,64u8,97u8,97u8,171u8, - 188u8,129u8,128u8,198u8,83u8,71u8,72u8,19u8,120u8,15u8,88u8,129u8,200u8,108u8,199u8,83u8,158u8,192u8,6u8,133u8, - 111u8,221u8,39u8,98u8,207u8,24u8,212u8,165u8,18u8,192u8,160u8,2u8,0u8,203u8,249u8,179u8,115u8,9u8,48u8,30u8, - 78u8,71u8,37u8,144u8,225u8,14u8,8u8,157u8,202u8,65u8,6u8,189u8,211u8,73u8,255u8,100u8,96u8,0u8,117u8,187u8, - 93u8,227u8,91u8,241u8,41u8,100u8,106u8,145u8,80u8,237u8,28u8,133u8,33u8,108u8,45u8,26u8,228u8,228u8,148u8,56u8, - 153u8,12u8,122u8,39u8,147u8,147u8,211u8,81u8,111u8,56u8,57u8,29u8,76u8,78u8,135u8,227u8,28u8,205u8,233u8,96u8, - 48u8,28u8,194u8,238u8,112u8,60u8,61u8,25u8,77u8,38u8,39u8,211u8,94u8,89u8,250u8,126u8,175u8,83u8,117u8,74u8, - 75u8,63u8,29u8,141u8,198u8,147u8,209u8,168u8,55u8,25u8,78u8,122u8,167u8,39u8,39u8,253u8,113u8,127u8,188u8,163u8, - 248u8,151u8,177u8,224u8,210u8,110u8,104u8,222u8,108u8,189u8,166u8,194u8,11u8,73u8,141u8,65u8,217u8,137u8,148u8,35u8, - 32u8,41u8,52u8,35u8,10u8,6u8,67u8,11u8,150u8,184u8,84u8,160u8,1u8,44u8,218u8,241u8,161u8,247u8,120u8,150u8, - 28u8,105u8,206u8,61u8,23u8,231u8,2u8,244u8,162u8,22u8,39u8,43u8,14u8,21u8,26u8,31u8,203u8,224u8,57u8,45u8, - 184u8,165u8,231u8,181u8,212u8,138u8,208u8,220u8,183u8,162u8,108u8,53u8,231u8,102u8,27u8,151u8,90u8,142u8,69u8,127u8, - 102u8,227u8,155u8,189u8,71u8,57u8,212u8,23u8,65u8,2u8,113u8,185u8,14u8,93u8,15u8,194u8,2u8,131u8,151u8,253u8, - 10u8,101u8,226u8,159u8,23u8,255u8,217u8,205u8,80u8,87u8,17u8,91u8,99u8,245u8,10u8,32u8,198u8,230u8,236u8,57u8, - 72u8,8u8,76u8,101u8,81u8,24u8,124u8,128u8,28u8,164u8,158u8,248u8,48u8,53u8,101u8,248u8,224u8,158u8,63u8,243u8, - 85u8,13u8,29u8,138u8,247u8,103u8,251u8,129u8,49u8,239u8,152u8,20u8,167u8,58u8,27u8,150u8,218u8,249u8,22u8,110u8, - 72u8,124u8,168u8,57u8,253u8,101u8,96u8,126u8,25u8,66u8,141u8,131u8,206u8,165u8,131u8,110u8,63u8,44u8,181u8,0u8, - 70u8,158u8,5u8,197u8,245u8,225u8,41u8,110u8,29u8,66u8,138u8,38u8,3u8,246u8,7u8,19u8,70u8,79u8,211u8,208u8, - 34u8,218u8,133u8,41u8,39u8,31u8,57u8,184u8,158u8,58u8,94u8,38u8,226u8,197u8,194u8,233u8,163u8,197u8,7u8,19u8, - 170u8,224u8,134u8,93u8,209u8,172u8,221u8,6u8,170u8,131u8,18u8,85u8,240u8,68u8,116u8,196u8,115u8,21u8,97u8,7u8, - 145u8,30u8,48u8,121u8,124u8,88u8,34u8,62u8,176u8,137u8,35u8,228u8,254u8,219u8,130u8,105u8,0u8,243u8,138u8,80u8, - 226u8,32u8,11u8,67u8,39u8,79u8,104u8,231u8,172u8,87u8,186u8,90u8,226u8,118u8,206u8,123u8,183u8,220u8,224u8,229u8, - 96u8,168u8,130u8,210u8,29u8,67u8,238u8,144u8,229u8,112u8,104u8,108u8,118u8,45u8,38u8,69u8,219u8,142u8,111u8,140u8, - 54u8,164u8,104u8,241u8,161u8,142u8,71u8,121u8,163u8,0u8,171u8,189u8,61u8,77u8,81u8,233u8,34u8,97u8,185u8,86u8, - 121u8,42u8,16u8,236u8,72u8,28u8,198u8,48u8,24u8,84u8,30u8,95u8,16u8,130u8,214u8,21u8,134u8,32u8,173u8,0u8, - 77u8,90u8,114u8,22u8,240u8,120u8,132u8,191u8,130u8,243u8,48u8,181u8,128u8,232u8,17u8,65u8,168u8,98u8,80u8,30u8, - 153u8,89u8,183u8,121u8,57u8,95u8,162u8,91u8,91u8,89u8,169u8,13u8,125u8,126u8,33u8,83u8,135u8,213u8,168u8,15u8, - 63u8,40u8,78u8,128u8,23u8,109u8,18u8,233u8,42u8,242u8,18u8,163u8,78u8,153u8,242u8,60u8,84u8,169u8,221u8,234u8, - 85u8,96u8,218u8,8u8,134u8,63u8,168u8,11u8,53u8,21u8,254u8,212u8,182u8,252u8,19u8,100u8,169u8,193u8,6u8,90u8, - 27u8,242u8,206u8,163u8,155u8,117u8,252u8,219u8,48u8,78u8,213u8,87u8,254u8,237u8,25u8,234u8,113u8,239u8,101u8,232u8, - 0u8,37u8,29u8,172u8,160u8,26u8,173u8,68u8,208u8,107u8,13u8,14u8,154u8,21u8,124u8,138u8,82u8,74u8,147u8,132u8, - 122u8,103u8,27u8,84u8,58u8,91u8,202u8,161u8,95u8,243u8,255u8,4u8,222u8,246u8,155u8,107u8,246u8,143u8,232u8,110u8, - 195u8,175u8,174u8,148u8,221u8,203u8,250u8,43u8,190u8,230u8,208u8,98u8,96u8,221u8,82u8,3u8,102u8,57u8,140u8,171u8, - 172u8,56u8,249u8,176u8,177u8,92u8,117u8,236u8,129u8,106u8,254u8,231u8,29u8,181u8,99u8,171u8,154u8,202u8,82u8,81u8, - 146u8,44u8,196u8,191u8,227u8,140u8,182u8,122u8,150u8,93u8,51u8,172u8,197u8,71u8,129u8,170u8,0u8,56u8,188u8,48u8, - 54u8,23u8,217u8,175u8,92u8,45u8,149u8,81u8,155u8,43u8,102u8,81u8,45u8,95u8,241u8,167u8,197u8,31u8,187u8,126u8, - 219u8,53u8,83u8,73u8,214u8,161u8,167u8,203u8,175u8,157u8,206u8,114u8,149u8,126u8,90u8,21u8,197u8,79u8,163u8,17u8, - 190u8,129u8,42u8,170u8,213u8,246u8,165u8,169u8,109u8,159u8,138u8,42u8,75u8,234u8,23u8,107u8,232u8,27u8,45u8,169u8, - 223u8,128u8,51u8,254u8,230u8,186u8,254u8,195u8,123u8,227u8,240u8,171u8,107u8,104u8,104u8,13u8,102u8,31u8,190u8,251u8, - 31u8,41u8,207u8,29u8,219u8,50u8,52u8,0u8,0u8,0u8,0u8,8u8,102u8,114u8,111u8,109u8,95u8,98u8,99u8,115u8, - 239u8,7u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,165u8,86u8,93u8,107u8,227u8,70u8,20u8,125u8, - 207u8,175u8,184u8,205u8,66u8,177u8,192u8,235u8,196u8,78u8,106u8,140u8,188u8,14u8,93u8,232u8,22u8,2u8,101u8,41u8, - 172u8,219u8,151u8,165u8,72u8,35u8,233u8,42u8,30u8,34u8,207u8,136u8,153u8,145u8,98u8,119u8,201u8,127u8,239u8,157u8, - 25u8,73u8,150u8,109u8,57u8,121u8,168u8,48u8,216u8,154u8,59u8,115u8,206u8,253u8,154u8,123u8,124u8,115u8,115u8,3u8, - 235u8,13u8,215u8,176u8,149u8,89u8,85u8,32u8,148u8,74u8,214u8,60u8,67u8,13u8,12u8,68u8,181u8,77u8,80u8,129u8, - 204u8,33u8,175u8,68u8,106u8,184u8,20u8,26u8,140u8,132u8,84u8,138u8,26u8,149u8,129u8,168u8,84u8,124u8,203u8,13u8, - 175u8,49u8,2u8,179u8,47u8,233u8,72u8,174u8,228u8,22u8,204u8,6u8,185u8,2u8,133u8,165u8,66u8,141u8,194u8,48u8, - 123u8,14u8,184u8,128u8,88u8,155u8,44u8,12u8,147u8,84u8,199u8,87u8,55u8,196u8,73u8,64u8,53u8,43u8,42u8,212u8, - 19u8,79u8,79u8,31u8,58u8,9u8,178u8,44u8,165u8,230u8,6u8,45u8,107u8,76u8,155u8,195u8,208u8,200u8,40u8,217u8, - 27u8,212u8,241u8,4u8,190u8,74u8,50u8,152u8,13u8,51u8,192u8,141u8,61u8,32u8,164u8,1u8,205u8,114u8,180u8,96u8, - 25u8,230u8,92u8,32u8,121u8,253u8,132u8,2u8,21u8,79u8,161u8,172u8,146u8,130u8,190u8,98u8,235u8,84u8,131u8,224u8, - 136u8,219u8,88u8,32u8,193u8,148u8,85u8,218u8,2u8,18u8,84u8,202u8,4u8,212u8,92u8,22u8,140u8,24u8,248u8,182u8, - 164u8,147u8,68u8,161u8,141u8,170u8,82u8,98u8,18u8,53u8,83u8,156u8,9u8,163u8,199u8,214u8,71u8,133u8,185u8,84u8, - 228u8,160u8,40u8,246u8,208u8,165u8,160u8,201u8,0u8,179u8,134u8,60u8,71u8,165u8,48u8,155u8,192u8,99u8,238u8,24u8, - 27u8,159u8,88u8,209u8,164u8,78u8,59u8,118u8,150u8,62u8,127u8,100u8,34u8,251u8,72u8,96u8,41u8,186u8,104u8,16u8, - 51u8,204u8,198u8,118u8,147u8,166u8,10u8,40u8,151u8,143u8,152u8,149u8,70u8,234u8,200u8,229u8,238u8,179u8,216u8,199u8, - 142u8,7u8,94u8,54u8,60u8,221u8,128u8,203u8,176u8,170u8,137u8,246u8,224u8,225u8,196u8,50u8,58u8,214u8,47u8,59u8, - 70u8,113u8,96u8,232u8,94u8,226u8,216u8,7u8,111u8,227u8,109u8,235u8,176u8,236u8,86u8,122u8,28u8,62u8,91u8,141u8, - 209u8,123u8,175u8,137u8,196u8,252u8,52u8,106u8,45u8,174u8,34u8,44u8,203u8,136u8,92u8,143u8,142u8,42u8,52u8,250u8, - 249u8,215u8,219u8,29u8,75u8,82u8,42u8,69u8,16u8,192u8,106u8,5u8,221u8,235u8,24u8,110u8,131u8,101u8,231u8,72u8, - 211u8,105u8,3u8,172u8,240u8,227u8,10u8,232u8,233u8,188u8,164u8,2u8,112u8,241u8,20u8,134u8,63u8,190u8,97u8,65u8, - 32u8,223u8,220u8,235u8,235u8,242u8,202u8,237u8,178u8,112u8,127u8,173u8,127u8,95u8,64u8,186u8,193u8,244u8,25u8,114u8, - 198u8,11u8,204u8,108u8,167u8,245u8,178u8,236u8,58u8,210u8,57u8,103u8,123u8,196u8,195u8,185u8,195u8,54u8,201u8,6u8, - 190u8,60u8,126u8,253u8,251u8,243u8,31u8,143u8,191u8,69u8,22u8,39u8,132u8,106u8,126u8,15u8,43u8,184u8,221u8,77u8, - 27u8,134u8,166u8,133u8,168u8,99u8,192u8,198u8,40u8,101u8,49u8,170u8,67u8,168u8,49u8,53u8,82u8,125u8,170u8,22u8, - 15u8,65u8,8u8,118u8,177u8,241u8,218u8,62u8,135u8,102u8,251u8,100u8,45u8,15u8,163u8,58u8,112u8,182u8,215u8,33u8, - 192u8,106u8,113u8,6u8,87u8,45u8,134u8,193u8,200u8,250u8,54u8,212u8,116u8,126u8,142u8,53u8,157u8,95u8,0u8,155u8, - 206u8,223u8,65u8,187u8,155u8,157u8,163u8,221u8,205u8,46u8,160u8,221u8,205u8,222u8,65u8,155u8,223u8,159u8,163u8,81u8, - 166u8,135u8,209u8,230u8,247u8,239u8,69u8,58u8,27u8,72u8,27u8,45u8,94u8,138u8,117u8,246u8,94u8,234u8,102u8,191u8, - 12u8,228u8,142u8,22u8,47u8,0u8,146u8,229u8,109u8,192u8,246u8,110u8,156u8,98u8,54u8,235u8,195u8,176u8,141u8,241u8, - 109u8,100u8,127u8,203u8,78u8,113u8,15u8,47u8,195u8,208u8,7u8,251u8,219u8,232u8,254u8,118u8,156u8,193u8,251u8,75u8, - 215u8,131u8,182u8,130u8,33u8,97u8,203u8,158u8,155u8,209u8,105u8,39u8,240u8,24u8,94u8,208u8,13u8,48u8,123u8,205u8, - 208u8,14u8,118u8,230u8,70u8,53u8,66u8,101u8,242u8,197u8,97u8,62u8,77u8,58u8,148u8,2u8,105u8,190u8,210u8,109u8, - 235u8,57u8,234u8,153u8,172u8,147u8,203u8,110u8,91u8,59u8,125u8,218u8,73u8,192u8,133u8,65u8,37u8,88u8,17u8,185u8, - 75u8,31u8,89u8,244u8,206u8,214u8,12u8,33u8,29u8,4u8,227u8,227u8,155u8,221u8,195u8,211u8,253u8,248u8,237u8,4u8, - 249u8,147u8,230u8,48u8,123u8,178u8,186u8,199u8,107u8,235u8,179u8,96u8,110u8,156u8,119u8,42u8,225u8,164u8,133u8,124u8, - 224u8,172u8,224u8,255u8,90u8,125u8,113u8,3u8,120u8,61u8,105u8,1u8,58u8,160u8,131u8,56u8,185u8,172u8,116u8,0u8, - 153u8,68u8,175u8,84u8,101u8,101u8,128u8,137u8,189u8,31u8,61u8,138u8,81u8,36u8,164u8,33u8,16u8,175u8,99u8,43u8, - 20u8,180u8,154u8,161u8,29u8,124u8,250u8,228u8,184u8,145u8,29u8,195u8,177u8,31u8,5u8,201u8,29u8,83u8,94u8,68u8, - 199u8,36u8,136u8,186u8,83u8,94u8,93u8,90u8,5u8,73u8,120u8,193u8,205u8,190u8,245u8,8u8,33u8,99u8,134u8,217u8, - 31u8,251u8,35u8,28u8,174u8,59u8,120u8,249u8,34u8,72u8,180u8,122u8,141u8,65u8,35u8,159u8,163u8,200u8,130u8,94u8, - 78u8,250u8,229u8,90u8,63u8,140u8,220u8,143u8,147u8,126u8,89u8,251u8,100u8,251u8,179u8,253u8,33u8,79u8,177u8,95u8, - 50u8,165u8,178u8,220u8,179u8,164u8,192u8,200u8,237u8,241u8,197u8,249u8,240u8,157u8,176u8,77u8,100u8,101u8,246u8,159u8, - 99u8,85u8,112u8,242u8,212u8,219u8,227u8,205u8,174u8,139u8,237u8,137u8,246u8,254u8,5u8,189u8,158u8,181u8,221u8,102u8, - 215u8,193u8,137u8,210u8,237u8,116u8,121u8,102u8,137u8,40u8,8u8,178u8,238u8,174u8,111u8,255u8,223u8,51u8,189u8,30u8, - 192u8,150u8,84u8,250u8,85u8,127u8,54u8,180u8,140u8,193u8,176u8,35u8,205u8,129u8,19u8,117u8,109u8,161u8,6u8,174u8, - 135u8,143u8,109u8,213u8,177u8,121u8,193u8,29u8,218u8,229u8,227u8,92u8,29u8,81u8,141u8,97u8,218u8,236u8,126u8,61u8, - 207u8,235u8,135u8,239u8,184u8,43u8,169u8,192u8,152u8,69u8,86u8,98u8,43u8,133u8,35u8,150u8,72u8,101u8,34u8,215u8, - 177u8,78u8,46u8,109u8,216u8,99u8,40u8,100u8,234u8,255u8,236u8,173u8,192u8,202u8,117u8,48u8,92u8,22u8,7u8,114u8, - 86u8,155u8,132u8,101u8,77u8,1u8,146u8,235u8,163u8,20u8,246u8,255u8,107u8,248u8,61u8,157u8,167u8,175u8,87u8,255u8, - 1u8,188u8,18u8,122u8,119u8,188u8,10u8,0u8,0u8,0u8,0u8,3u8,97u8,110u8,121u8,136u8,6u8,31u8,139u8,8u8, - 0u8,0u8,0u8,0u8,0u8,2u8,255u8,117u8,84u8,109u8,107u8,219u8,48u8,16u8,254u8,222u8,95u8,113u8,99u8,144u8, - 37u8,204u8,184u8,52u8,140u8,49u8,220u8,52u8,208u8,141u8,142u8,237u8,67u8,89u8,105u8,188u8,15u8,99u8,140u8,88u8, - 177u8,229u8,68u8,212u8,145u8,60u8,189u8,36u8,14u8,37u8,255u8,125u8,167u8,147u8,227u8,56u8,89u8,27u8,8u8,81u8, - 116u8,111u8,207u8,61u8,247u8,156u8,214u8,170u8,112u8,21u8,7u8,86u8,91u8,101u8,230u8,198u8,22u8,73u8,194u8,228u8, - 14u8,158u8,47u8,0u8,63u8,206u8,156u8,24u8,236u8,174u8,230u8,115u8,33u8,75u8,117u8,253u8,146u8,181u8,212u8,106u8, - 61u8,95u8,228u8,230u8,112u8,218u8,89u8,110u8,142u8,142u8,228u8,66u8,86u8,171u8,94u8,180u8,113u8,173u8,149u8,62u8, - 187u8,51u8,86u8,11u8,185u8,76u8,146u8,25u8,253u8,94u8,95u8,144u8,181u8,212u8,130u8,203u8,162u8,95u8,57u8,87u8, - 245u8,142u8,45u8,42u8,62u8,71u8,228u8,173u8,211u8,229u8,229u8,37u8,164u8,43u8,14u8,30u8,50u8,212u8,90u8,109u8, - 68u8,193u8,11u8,40u8,149u8,134u8,204u8,201u8,154u8,229u8,79u8,25u8,8u8,3u8,82u8,89u8,176u8,232u8,101u8,216u8, - 26u8,91u8,49u8,176u8,197u8,239u8,82u8,108u8,184u8,12u8,158u8,228u8,23u8,83u8,190u8,92u8,73u8,99u8,225u8,46u8, - 253u8,245u8,112u8,55u8,191u8,255u8,62u8,187u8,191u8,77u8,191u8,124u8,75u8,192u8,125u8,252u8,0u8,55u8,112u8,213u8, - 171u8,121u8,27u8,42u8,110u8,87u8,34u8,95u8,65u8,206u8,36u8,104u8,94u8,107u8,110u8,184u8,180u8,192u8,96u8,195u8, - 42u8,199u8,65u8,149u8,224u8,25u8,246u8,126u8,49u8,98u8,68u8,24u8,172u8,170u8,212u8,214u8,80u8,205u8,206u8,157u8, - 89u8,161u8,164u8,247u8,125u8,231u8,228u8,147u8,84u8,91u8,249u8,14u8,74u8,103u8,157u8,230u8,93u8,41u8,202u8,102u8, - 98u8,248u8,138u8,97u8,188u8,97u8,235u8,186u8,226u8,17u8,88u8,5u8,5u8,47u8,133u8,196u8,118u8,48u8,151u8,81u8, - 78u8,231u8,216u8,158u8,67u8,48u8,118u8,197u8,44u8,8u8,75u8,160u8,22u8,28u8,42u8,102u8,185u8,246u8,7u8,222u8, - 88u8,164u8,19u8,201u8,217u8,10u8,187u8,82u8,206u8,194u8,66u8,115u8,246u8,132u8,116u8,119u8,133u8,242u8,21u8,147u8, - 75u8,110u8,64u8,97u8,86u8,31u8,93u8,168u8,131u8,169u8,115u8,201u8,178u8,108u8,173u8,54u8,71u8,108u8,128u8,243u8, - 211u8,46u8,183u8,240u8,120u8,192u8,240u8,220u8,179u8,249u8,33u8,10u8,94u8,21u8,9u8,164u8,72u8,66u8,116u8,106u8, - 137u8,227u8,248u8,244u8,130u8,0u8,26u8,100u8,35u8,129u8,31u8,181u8,103u8,101u8,114u8,43u8,119u8,211u8,158u8,207u8, - 190u8,15u8,131u8,206u8,109u8,113u8,244u8,131u8,21u8,142u8,180u8,208u8,170u8,142u8,240u8,82u8,233u8,3u8,14u8,255u8, - 33u8,53u8,75u8,156u8,124u8,2u8,65u8,95u8,81u8,103u8,42u8,152u8,101u8,9u8,108u8,120u8,142u8,33u8,19u8,247u8, - 41u8,20u8,219u8,31u8,167u8,252u8,128u8,194u8,232u8,230u8,41u8,36u8,146u8,238u8,85u8,148u8,97u8,189u8,236u8,108u8, - 130u8,49u8,124u8,230u8,57u8,243u8,130u8,246u8,96u8,90u8,238u8,9u8,9u8,42u8,24u8,85u8,236u8,161u8,213u8,188u8, - 192u8,193u8,121u8,33u8,8u8,211u8,149u8,96u8,149u8,81u8,152u8,235u8,175u8,19u8,222u8,213u8,111u8,20u8,100u8,105u8, - 43u8,198u8,218u8,45u8,42u8,145u8,163u8,30u8,36u8,120u8,137u8,78u8,210u8,132u8,242u8,192u8,251u8,144u8,121u8,58u8, - 108u8,144u8,217u8,81u8,66u8,37u8,143u8,253u8,158u8,254u8,59u8,99u8,160u8,91u8,237u8,118u8,203u8,253u8,237u8,36u8, - 157u8,14u8,71u8,209u8,73u8,68u8,32u8,230u8,176u8,193u8,195u8,65u8,51u8,234u8,204u8,251u8,115u8,146u8,126u8,210u8, - 158u8,117u8,52u8,81u8,11u8,175u8,211u8,20u8,22u8,97u8,161u8,180u8,69u8,26u8,74u8,114u8,12u8,113u8,126u8,128u8, - 135u8,61u8,229u8,77u8,141u8,51u8,65u8,62u8,104u8,201u8,94u8,226u8,35u8,44u8,183u8,71u8,222u8,80u8,255u8,200u8, - 66u8,218u8,235u8,154u8,25u8,195u8,181u8,125u8,51u8,124u8,181u8,93u8,184u8,185u8,129u8,38u8,238u8,238u8,34u8,160u8, - 103u8,41u8,73u8,132u8,68u8,52u8,162u8,152u8,51u8,189u8,116u8,107u8,68u8,61u8,60u8,125u8,10u8,70u8,163u8,235u8, - 174u8,198u8,241u8,245u8,35u8,24u8,177u8,231u8,108u8,116u8,78u8,206u8,35u8,199u8,93u8,150u8,134u8,186u8,162u8,102u8, - 124u8,53u8,191u8,238u8,164u8,3u8,4u8,126u8,222u8,88u8,7u8,201u8,55u8,54u8,8u8,157u8,13u8,130u8,110u8,123u8, - 253u8,13u8,122u8,216u8,251u8,37u8,223u8,254u8,70u8,56u8,118u8,174u8,100u8,181u8,251u8,211u8,223u8,144u8,25u8,209u8, - 75u8,170u8,137u8,130u8,134u8,158u8,161u8,9u8,239u8,218u8,73u8,100u8,8u8,34u8,28u8,62u8,15u8,190u8,95u8,72u8, - 213u8,255u8,180u8,182u8,236u8,99u8,248u8,116u8,232u8,79u8,195u8,241u8,120u8,68u8,148u8,142u8,199u8,17u8,92u8,245u8, - 56u8,58u8,11u8,152u8,181u8,238u8,179u8,80u8,126u8,60u8,134u8,125u8,136u8,235u8,93u8,68u8,48u8,110u8,19u8,236u8, - 47u8,246u8,23u8,255u8,0u8,7u8,214u8,151u8,176u8,182u8,6u8,0u8,0u8,0u8,0u8,10u8,97u8,112u8,116u8,111u8, - 115u8,95u8,104u8,97u8,115u8,104u8,171u8,18u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,89u8, - 235u8,110u8,219u8,56u8,22u8,254u8,223u8,167u8,96u8,51u8,64u8,107u8,239u8,38u8,182u8,72u8,234u8,66u8,185u8,237u8, - 96u8,211u8,78u8,103u8,167u8,51u8,157u8,76u8,209u8,118u8,187u8,11u8,20u8,93u8,133u8,151u8,67u8,91u8,136u8,44u8, - 25u8,146u8,236u8,196u8,179u8,232u8,187u8,239u8,161u8,228u8,139u8,156u8,196u8,105u8,82u8,96u8,23u8,237u8,116u8,140u8, - 54u8,182u8,40u8,157u8,43u8,249u8,125u8,231u8,144u8,26u8,14u8,135u8,228u8,89u8,185u8,156u8,213u8,197u8,184u8,148u8, - 179u8,73u8,170u8,201u8,68u8,86u8,19u8,168u8,70u8,247u8,134u8,120u8,231u8,136u8,252u8,2u8,90u8,203u8,179u8,35u8, - 22u8,132u8,35u8,82u8,1u8,144u8,73u8,93u8,207u8,170u8,209u8,112u8,120u8,214u8,140u8,15u8,106u8,144u8,211u8,245u8, - 239u8,73u8,61u8,205u8,156u8,84u8,35u8,249u8,34u8,39u8,210u8,152u8,180u8,78u8,139u8,252u8,144u8,188u8,249u8,233u8, - 152u8,57u8,21u8,68u8,230u8,198u8,93u8,240u8,246u8,162u8,4u8,34u8,23u8,50u8,205u8,164u8,202u8,128u8,164u8,57u8, - 57u8,173u8,106u8,51u8,26u8,57u8,251u8,167u8,3u8,114u8,82u8,212u8,64u8,234u8,137u8,172u8,183u8,2u8,105u8,69u8, - 36u8,89u8,200u8,50u8,149u8,121u8,77u8,10u8,187u8,242u8,109u8,68u8,210u8,26u8,111u8,53u8,86u8,79u8,126u8,123u8, - 139u8,50u8,64u8,42u8,57u8,69u8,221u8,85u8,199u8,251u8,193u8,198u8,179u8,147u8,34u8,63u8,210u8,235u8,136u8,175u8, - 6u8,252u8,38u8,157u8,253u8,132u8,3u8,35u8,244u8,213u8,69u8,112u8,84u8,22u8,181u8,172u8,225u8,232u8,162u8,40u8, - 73u8,239u8,248u8,245u8,191u8,250u8,68u8,201u8,10u8,12u8,177u8,114u8,154u8,102u8,75u8,231u8,197u8,172u8,130u8,185u8, - 41u8,74u8,12u8,173u8,152u8,18u8,59u8,207u8,181u8,139u8,185u8,34u8,186u8,4u8,148u8,51u8,68u8,45u8,201u8,207u8, - 32u8,243u8,163u8,87u8,147u8,52u8,75u8,103u8,51u8,32u8,199u8,243u8,169u8,172u8,170u8,34u8,111u8,146u8,241u8,131u8, - 204u8,83u8,200u8,200u8,207u8,3u8,242u8,20u8,202u8,188u8,170u8,1u8,211u8,128u8,255u8,152u8,71u8,217u8,189u8,105u8, - 97u8,230u8,152u8,24u8,137u8,158u8,86u8,73u8,147u8,154u8,246u8,167u8,243u8,151u8,252u8,231u8,30u8,193u8,207u8,188u8, - 194u8,88u8,221u8,29u8,165u8,171u8,71u8,187u8,35u8,22u8,205u8,207u8,75u8,192u8,225u8,102u8,28u8,131u8,111u8,191u8, - 200u8,51u8,244u8,174u8,198u8,12u8,86u8,235u8,241u8,213u8,247u8,144u8,28u8,147u8,28u8,206u8,179u8,229u8,17u8,134u8, - 141u8,142u8,231u8,178u8,78u8,23u8,176u8,137u8,200u8,101u8,63u8,47u8,106u8,178u8,132u8,154u8,64u8,238u8,166u8,204u8, - 12u8,26u8,73u8,237u8,244u8,145u8,231u8,201u8,201u8,241u8,219u8,23u8,239u8,158u8,39u8,63u8,254u8,227u8,36u8,193u8, - 105u8,72u8,142u8,223u8,29u8,191u8,120u8,121u8,252u8,244u8,229u8,243u8,17u8,153u8,135u8,62u8,121u8,66u8,232u8,21u8, - 79u8,126u8,92u8,39u8,235u8,138u8,39u8,175u8,1u8,125u8,199u8,36u8,186u8,185u8,236u8,229u8,221u8,9u8,115u8,75u8, - 180u8,191u8,158u8,36u8,151u8,254u8,83u8,181u8,172u8,161u8,194u8,5u8,243u8,166u8,179u8,58u8,33u8,31u8,156u8,167u8, - 103u8,233u8,12u8,76u8,42u8,7u8,69u8,57u8,30u8,186u8,171u8,225u8,74u8,170u8,177u8,178u8,138u8,110u8,54u8,87u8, - 25u8,46u8,1u8,12u8,146u8,84u8,233u8,172u8,73u8,109u8,175u8,81u8,56u8,34u8,11u8,208u8,117u8,81u8,62u8,158u8, - 139u8,239u8,251u8,77u8,8u8,143u8,62u8,203u8,61u8,247u8,200u8,211u8,103u8,111u8,16u8,59u8,184u8,108u8,179u8,244u8, - 119u8,217u8,228u8,210u8,249u8,189u8,184u8,179u8,207u8,215u8,56u8,155u8,216u8,178u8,152u8,38u8,11u8,153u8,205u8,225u8, - 241u8,175u8,197u8,2u8,222u8,185u8,95u8,223u8,247u8,22u8,35u8,242u8,96u8,115u8,217u8,186u8,191u8,90u8,49u8,238u8, - 147u8,225u8,20u8,54u8,65u8,226u8,172u8,224u8,194u8,25u8,141u8,234u8,34u8,105u8,174u8,123u8,139u8,254u8,42u8,74u8, - 247u8,217u8,77u8,72u8,191u8,25u8,255u8,120u8,125u8,18u8,182u8,56u8,107u8,208u8,212u8,157u8,151u8,61u8,217u8,110u8, - 57u8,3u8,5u8,174u8,77u8,247u8,246u8,98u8,79u8,214u8,27u8,70u8,9u8,40u8,187u8,222u8,92u8,55u8,81u8,19u8, - 201u8,18u8,124u8,240u8,19u8,102u8,58u8,217u8,73u8,109u8,239u8,254u8,26u8,60u8,163u8,17u8,202u8,59u8,241u8,4u8, - 193u8,154u8,148u8,56u8,57u8,83u8,147u8,208u8,208u8,75u8,86u8,8u8,232u8,245u8,251u8,29u8,73u8,247u8,145u8,170u8, - 40u8,235u8,94u8,131u8,64u8,40u8,203u8,162u8,28u8,141u8,210u8,28u8,167u8,39u8,53u8,136u8,96u8,228u8,131u8,222u8, - 126u8,156u8,244u8,251u8,27u8,69u8,31u8,187u8,179u8,176u8,10u8,32u8,73u8,243u8,26u8,41u8,66u8,102u8,183u8,152u8, - 142u8,134u8,49u8,111u8,153u8,29u8,254u8,181u8,103u8,135u8,223u8,148u8,157u8,107u8,211u8,243u8,250u8,197u8,171u8,231u8, - 191u8,254u8,112u8,132u8,158u8,94u8,159u8,160u8,225u8,134u8,162u8,134u8,228u8,159u8,199u8,175u8,79u8,94u8,156u8,252u8, - 125u8,68u8,126u8,203u8,145u8,238u8,133u8,119u8,164u8,176u8,218u8,84u8,160u8,231u8,101u8,90u8,47u8,29u8,39u8,206u8, - 202u8,98u8,145u8,154u8,150u8,232u8,235u8,9u8,14u8,172u8,9u8,115u8,64u8,222u8,186u8,203u8,41u8,146u8,127u8,213u8, - 22u8,147u8,5u8,148u8,149u8,44u8,151u8,228u8,124u8,82u8,16u8,141u8,35u8,186u8,152u8,206u8,230u8,88u8,229u8,202u8, - 98u8,62u8,158u8,160u8,118u8,246u8,111u8,225u8,109u8,12u8,183u8,85u8,137u8,156u8,167u8,89u8,118u8,136u8,127u8,235u8, - 9u8,153u8,164u8,227u8,137u8,51u8,167u8,164u8,194u8,82u8,82u8,47u8,15u8,137u8,77u8,177u8,132u8,72u8,212u8,147u8, - 101u8,105u8,229u8,120u8,229u8,34u8,161u8,228u8,254u8,19u8,252u8,98u8,164u8,154u8,235u8,73u8,91u8,63u8,183u8,225u8, - 246u8,240u8,126u8,31u8,65u8,191u8,51u8,194u8,250u8,87u8,150u8,69u8,59u8,149u8,238u8,246u8,215u8,186u8,46u8,54u8, - 17u8,220u8,5u8,54u8,79u8,95u8,30u8,255u8,242u8,156u8,61u8,221u8,79u8,99u8,157u8,20u8,169u8,76u8,158u8,1u8, - 83u8,201u8,167u8,25u8,108u8,111u8,146u8,58u8,42u8,254u8,143u8,153u8,233u8,90u8,189u8,49u8,55u8,235u8,42u8,253u8, - 170u8,76u8,23u8,104u8,233u8,114u8,63u8,112u8,115u8,209u8,190u8,153u8,159u8,183u8,170u8,246u8,209u8,219u8,77u8,245u8, - 224u8,51u8,40u8,111u8,215u8,32u8,191u8,163u8,193u8,63u8,57u8,228u8,182u8,28u8,210u8,201u8,243u8,62u8,4u8,222u8, - 53u8,209u8,159u8,4u8,101u8,199u8,230u8,254u8,181u8,253u8,105u8,171u8,235u8,229u8,254u8,22u8,170u8,58u8,205u8,199u8, - 59u8,171u8,251u8,187u8,247u8,168u8,162u8,254u8,208u8,252u8,222u8,105u8,94u8,18u8,55u8,222u8,235u8,95u8,234u8,173u8, - 210u8,28u8,167u8,196u8,53u8,87u8,173u8,153u8,247u8,59u8,128u8,86u8,7u8,117u8,107u8,225u8,224u8,240u8,210u8,120u8, - 103u8,224u8,67u8,7u8,176u8,78u8,99u8,49u8,175u8,111u8,80u8,121u8,113u8,16u8,88u8,26u8,90u8,95u8,71u8,150u8, - 250u8,177u8,212u8,190u8,141u8,3u8,234u8,153u8,88u8,91u8,161u8,45u8,23u8,190u8,199u8,133u8,52u8,220u8,23u8,138u8, - 43u8,109u8,180u8,71u8,99u8,26u8,224u8,19u8,6u8,40u8,51u8,54u8,54u8,84u8,121u8,236u8,146u8,47u8,23u8,7u8, - 58u8,48u8,204u8,15u8,61u8,42u8,66u8,27u8,49u8,206u8,117u8,204u8,34u8,136u8,140u8,98u8,70u8,235u8,200u8,227u8, - 218u8,131u8,192u8,243u8,84u8,24u8,112u8,45u8,5u8,99u8,17u8,87u8,145u8,178u8,210u8,8u8,207u8,15u8,140u8,8u8, - 164u8,31u8,121u8,55u8,196u8,146u8,98u8,20u8,222u8,163u8,205u8,208u8,57u8,238u8,139u8,176u8,139u8,78u8,201u8,227u8, - 118u8,223u8,210u8,6u8,56u8,26u8,101u8,144u8,143u8,235u8,73u8,239u8,65u8,155u8,204u8,43u8,188u8,184u8,201u8,51u8, - 106u8,251u8,203u8,142u8,32u8,18u8,102u8,89u8,156u8,175u8,5u8,15u8,73u8,218u8,127u8,116u8,69u8,178u8,105u8,157u8, - 225u8,98u8,134u8,34u8,136u8,186u8,125u8,26u8,86u8,89u8,223u8,175u8,2u8,37u8,183u8,125u8,108u8,99u8,175u8,219u8, - 63u8,55u8,244u8,93u8,97u8,227u8,95u8,223u8,239u8,93u8,50u8,248u8,164u8,17u8,63u8,36u8,244u8,242u8,243u8,46u8, - 57u8,41u8,249u8,171u8,219u8,44u8,117u8,200u8,187u8,195u8,203u8,237u8,82u8,236u8,217u8,11u8,124u8,240u8,111u8,155u8, - 173u8,97u8,127u8,187u8,54u8,55u8,132u8,186u8,122u8,14u8,183u8,237u8,233u8,56u8,135u8,178u8,155u8,64u8,199u8,73u8, - 72u8,232u8,128u8,174u8,212u8,197u8,106u8,55u8,215u8,128u8,109u8,85u8,156u8,220u8,22u8,180u8,40u8,13u8,148u8,196u8, - 226u8,182u8,215u8,221u8,88u8,129u8,76u8,203u8,44u8,115u8,34u8,10u8,183u8,165u8,89u8,86u8,156u8,175u8,247u8,128u8, - 141u8,241u8,77u8,97u8,211u8,19u8,153u8,143u8,33u8,89u8,13u8,36u8,54u8,147u8,227u8,170u8,247u8,192u8,94u8,28u8, - 174u8,87u8,239u8,246u8,209u8,49u8,212u8,201u8,158u8,102u8,97u8,245u8,80u8,175u8,255u8,97u8,35u8,247u8,161u8,127u8, - 121u8,45u8,237u8,67u8,218u8,117u8,40u8,219u8,139u8,48u8,183u8,21u8,197u8,109u8,212u8,118u8,39u8,54u8,205u8,105u8, - 36u8,6u8,99u8,36u8,204u8,185u8,26u8,164u8,197u8,176u8,200u8,179u8,52u8,135u8,163u8,186u8,40u8,178u8,106u8,136u8, - 238u8,162u8,183u8,237u8,105u8,199u8,237u8,48u8,138u8,248u8,100u8,84u8,197u8,90u8,131u8,69u8,0u8,82u8,223u8,208u8, - 40u8,134u8,72u8,82u8,165u8,68u8,20u8,69u8,1u8,19u8,145u8,39u8,67u8,19u8,50u8,47u8,230u8,66u8,49u8,33u8, - 195u8,80u8,82u8,47u8,2u8,169u8,67u8,8u8,133u8,23u8,168u8,216u8,120u8,177u8,136u8,173u8,31u8,168u8,32u8,226u8, - 94u8,224u8,33u8,206u8,168u8,148u8,1u8,68u8,212u8,19u8,126u8,100u8,124u8,30u8,131u8,140u8,124u8,109u8,56u8,101u8, - 58u8,230u8,1u8,146u8,129u8,101u8,70u8,130u8,39u8,140u8,239u8,129u8,79u8,77u8,208u8,133u8,35u8,98u8,219u8,10u8, - 14u8,148u8,7u8,17u8,160u8,63u8,66u8,25u8,75u8,3u8,159u8,137u8,192u8,51u8,97u8,136u8,16u8,246u8,34u8,231u8, - 9u8,248u8,104u8,7u8,205u8,209u8,192u8,104u8,193u8,173u8,47u8,99u8,70u8,13u8,15u8,53u8,196u8,26u8,208u8,164u8, - 103u8,40u8,215u8,14u8,235u8,150u8,41u8,207u8,162u8,62u8,42u8,12u8,134u8,129u8,26u8,53u8,179u8,33u8,87u8,49u8, - 167u8,202u8,248u8,145u8,79u8,35u8,41u8,168u8,12u8,184u8,224u8,44u8,146u8,22u8,73u8,196u8,72u8,14u8,223u8,12u8, - 55u8,108u8,246u8,158u8,95u8,14u8,53u8,240u8,63u8,50u8,53u8,124u8,1u8,204u8,208u8,228u8,247u8,110u8,220u8,32u8, - 4u8,213u8,136u8,57u8,37u8,99u8,17u8,70u8,2u8,9u8,34u8,14u8,129u8,5u8,220u8,19u8,161u8,246u8,61u8,95u8, - 104u8,14u8,146u8,226u8,101u8,216u8,128u8,206u8,90u8,95u8,112u8,159u8,234u8,16u8,1u8,11u8,72u8,0u8,212u8,99u8, - 88u8,110u8,85u8,136u8,245u8,31u8,28u8,106u8,77u8,24u8,104u8,79u8,115u8,19u8,70u8,145u8,66u8,242u8,136u8,141u8, - 53u8,72u8,45u8,156u8,133u8,97u8,28u8,132u8,94u8,172u8,149u8,52u8,86u8,90u8,73u8,17u8,231u8,16u8,131u8,138u8, - 172u8,166u8,59u8,220u8,32u8,195u8,216u8,70u8,28u8,75u8,26u8,227u8,18u8,59u8,137u8,64u8,11u8,21u8,132u8,145u8, - 209u8,20u8,203u8,122u8,20u8,132u8,16u8,71u8,58u8,22u8,140u8,134u8,190u8,69u8,31u8,69u8,16u8,3u8,58u8,133u8, - 61u8,1u8,245u8,35u8,124u8,20u8,89u8,140u8,6u8,138u8,81u8,148u8,69u8,10u8,194u8,30u8,195u8,215u8,148u8,2u8, - 135u8,216u8,247u8,152u8,230u8,168u8,45u8,16u8,22u8,251u8,6u8,26u8,199u8,38u8,14u8,20u8,186u8,5u8,220u8,163u8, - 17u8,170u8,17u8,24u8,14u8,170u8,49u8,44u8,252u8,150u8,184u8,129u8,127u8,73u8,220u8,176u8,109u8,215u8,255u8,36u8, - 135u8,207u8,36u8,135u8,243u8,243u8,243u8,129u8,194u8,5u8,128u8,83u8,135u8,164u8,48u8,30u8,224u8,214u8,108u8,216u8, - 18u8,195u8,38u8,185u8,71u8,147u8,245u8,89u8,238u8,109u8,152u8,65u8,137u8,88u8,33u8,246u8,67u8,229u8,123u8,10u8, - 12u8,139u8,101u8,16u8,135u8,65u8,40u8,124u8,21u8,97u8,115u8,238u8,107u8,108u8,245u8,185u8,140u8,194u8,88u8,237u8, - 32u8,56u8,70u8,204u8,33u8,88u8,3u8,29u8,64u8,108u8,117u8,224u8,135u8,148u8,9u8,79u8,196u8,174u8,54u8,35u8, - 250u8,112u8,19u8,192u8,16u8,184u8,216u8,42u8,124u8,51u8,72u8,219u8,30u8,102u8,125u8,1u8,80u8,251u8,238u8,253u8, - 218u8,68u8,98u8,241u8,205u8,151u8,91u8,167u8,205u8,9u8,79u8,162u8,11u8,3u8,238u8,37u8,73u8,28u8,34u8,77u8, - 31u8,146u8,172u8,208u8,237u8,155u8,130u8,39u8,248u8,142u8,32u8,179u8,29u8,160u8,118u8,247u8,184u8,141u8,104u8,117u8, - 35u8,88u8,77u8,90u8,93u8,65u8,41u8,2u8,113u8,138u8,74u8,112u8,123u8,95u8,182u8,55u8,78u8,59u8,74u8,79u8, - 91u8,176u8,182u8,170u8,63u8,7u8,164u8,31u8,246u8,192u8,181u8,235u8,248u8,22u8,163u8,253u8,235u8,143u8,166u8,122u8, - 234u8,160u8,57u8,249u8,112u8,71u8,23u8,173u8,47u8,7u8,253u8,187u8,17u8,90u8,215u8,220u8,87u8,67u8,105u8,215u8, - 231u8,232u8,115u8,120u8,108u8,135u8,192u8,174u8,35u8,181u8,213u8,24u8,145u8,99u8,153u8,230u8,7u8,135u8,46u8,17u8, - 48u8,157u8,225u8,121u8,212u8,20u8,170u8,74u8,142u8,113u8,229u8,20u8,80u8,229u8,15u8,241u8,133u8,31u8,190u8,160u8, - 52u8,238u8,240u8,169u8,5u8,27u8,193u8,69u8,233u8,210u8,208u8,118u8,62u8,100u8,12u8,152u8,80u8,137u8,198u8,111u8, - 77u8,145u8,142u8,18u8,207u8,210u8,90u8,201u8,101u8,203u8,144u8,103u8,170u8,97u8,201u8,97u8,123u8,208u8,211u8,196u8, - 222u8,145u8,223u8,81u8,133u8,51u8,208u8,158u8,119u8,57u8,199u8,157u8,19u8,235u8,3u8,33u8,244u8,174u8,117u8,190u8, - 170u8,75u8,188u8,135u8,135u8,93u8,208u8,188u8,9u8,109u8,38u8,178u8,112u8,211u8,227u8,36u8,94u8,45u8,235u8,73u8, - 145u8,115u8,82u8,105u8,228u8,132u8,122u8,212u8,85u8,125u8,122u8,122u8,218u8,189u8,196u8,245u8,117u8,127u8,56u8,175u8, - 202u8,161u8,74u8,243u8,225u8,172u8,21u8,219u8,227u8,20u8,50u8,194u8,116u8,134u8,235u8,179u8,241u8,37u8,75u8,213u8, - 222u8,199u8,102u8,232u8,89u8,221u8,91u8,61u8,53u8,88u8,205u8,116u8,79u8,61u8,124u8,120u8,136u8,32u8,29u8,227u8, - 108u8,36u8,85u8,250u8,59u8,60u8,225u8,120u8,150u8,54u8,129u8,139u8,118u8,8u8,207u8,131u8,31u8,237u8,115u8,243u8, - 83u8,213u8,3u8,207u8,104u8,162u8,128u8,106u8,143u8,133u8,16u8,248u8,92u8,49u8,16u8,18u8,255u8,40u8,15u8,249u8, - 37u8,54u8,82u8,82u8,67u8,33u8,48u8,214u8,199u8,29u8,168u8,176u8,248u8,63u8,178u8,82u8,42u8,63u8,208u8,184u8, - 29u8,100u8,22u8,184u8,20u8,187u8,85u8,37u8,230u8,113u8,100u8,45u8,103u8,18u8,220u8,65u8,146u8,112u8,103u8,63u8, - 161u8,9u8,52u8,195u8,150u8,148u8,251u8,220u8,68u8,88u8,108u8,12u8,160u8,86u8,15u8,205u8,184u8,222u8,209u8,198u8, - 30u8,154u8,163u8,146u8,134u8,216u8,223u8,133u8,193u8,142u8,50u8,138u8,79u8,42u8,60u8,38u8,194u8,254u8,211u8,23u8, - 212u8,119u8,167u8,72u8,177u8,98u8,49u8,80u8,27u8,105u8,95u8,129u8,80u8,210u8,247u8,13u8,248u8,86u8,2u8,72u8, - 139u8,254u8,224u8,249u8,18u8,167u8,30u8,246u8,174u8,160u8,173u8,22u8,190u8,254u8,102u8,74u8,87u8,151u8,16u8,255u8, - 231u8,197u8,235u8,227u8,189u8,255u8,2u8,141u8,2u8,134u8,173u8,12u8,34u8,0u8,0u8,0u8,0u8,5u8,116u8,97u8, - 98u8,108u8,101u8,159u8,12u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,197u8,88u8,95u8,111u8,219u8, - 54u8,16u8,127u8,247u8,167u8,184u8,162u8,128u8,97u8,119u8,170u8,93u8,167u8,109u8,26u8,216u8,109u8,176u8,110u8,47u8, - 11u8,58u8,12u8,69u8,17u8,100u8,15u8,195u8,96u8,83u8,210u8,217u8,38u8,34u8,137u8,42u8,73u8,37u8,241u8,138u8, - 124u8,247u8,221u8,145u8,146u8,44u8,203u8,127u8,154u8,184u8,237u8,150u8,39u8,71u8,188u8,251u8,221u8,221u8,239u8,142u8, - 71u8,242u8,134u8,195u8,33u8,92u8,174u8,114u8,4u8,53u8,135u8,68u8,232u8,5u8,62u8,55u8,145u8,72u8,16u8,140u8, - 85u8,90u8,44u8,16u8,172u8,8u8,19u8,52u8,131u8,206u8,144u8,228u8,140u8,42u8,116u8,132u8,99u8,88u8,90u8,155u8, - 155u8,241u8,112u8,184u8,144u8,118u8,89u8,132u8,131u8,72u8,165u8,195u8,84u8,221u8,224u8,243u8,68u8,100u8,139u8,130u8, - 148u8,220u8,127u8,195u8,48u8,81u8,225u8,112u8,20u8,158u8,134u8,111u8,94u8,143u8,94u8,198u8,81u8,52u8,18u8,175u8, - 163u8,179u8,211u8,211u8,249u8,232u8,205u8,89u8,68u8,63u8,71u8,248u8,230u8,85u8,136u8,225u8,73u8,132u8,163u8,179u8, - 17u8,14u8,107u8,93u8,188u8,179u8,152u8,25u8,169u8,50u8,227u8,65u8,157u8,3u8,207u8,235u8,175u8,67u8,239u8,132u8, - 25u8,94u8,242u8,247u8,1u8,139u8,60u8,253u8,125u8,196u8,238u8,57u8,23u8,47u8,44u8,200u8,52u8,79u8,48u8,197u8, - 204u8,26u8,176u8,75u8,4u8,39u8,7u8,150u8,67u8,188u8,93u8,202u8,104u8,9u8,166u8,200u8,115u8,165u8,105u8,85u8, - 102u8,177u8,188u8,145u8,113u8,33u8,18u8,31u8,37u8,72u8,139u8,41u8,41u8,41u8,8u8,17u8,52u8,230u8,26u8,13u8, - 161u8,96u8,12u8,225u8,202u8,135u8,143u8,185u8,208u8,194u8,34u8,44u8,40u8,50u8,82u8,50u8,150u8,255u8,113u8,74u8, - 3u8,184u8,36u8,83u8,89u8,145u8,134u8,168u8,153u8,73u8,143u8,36u8,178u8,24u8,4u8,20u8,153u8,252u8,92u8,32u8, - 44u8,233u8,63u8,178u8,33u8,52u8,57u8,163u8,69u8,116u8,77u8,184u8,42u8,115u8,30u8,58u8,227u8,222u8,130u8,213u8, - 69u8,68u8,17u8,88u8,131u8,201u8,60u8,96u8,127u8,217u8,117u8,18u8,81u8,57u8,146u8,101u8,38u8,197u8,1u8,212u8, - 33u8,18u8,136u8,48u8,144u8,209u8,210u8,13u8,194u8,188u8,200u8,34u8,39u8,51u8,128u8,63u8,20u8,27u8,185u8,65u8, - 109u8,200u8,79u8,105u8,32u8,215u8,138u8,34u8,197u8,120u8,208u8,233u8,164u8,42u8,46u8,216u8,143u8,220u8,42u8,51u8, - 53u8,54u8,30u8,143u8,125u8,240u8,95u8,58u8,64u8,127u8,115u8,45u8,145u8,189u8,110u8,173u8,78u8,111u8,41u8,213u8, - 211u8,4u8,179u8,133u8,93u8,78u8,58u8,78u8,114u8,216u8,40u8,28u8,95u8,36u8,238u8,115u8,25u8,130u8,99u8,253u8, - 109u8,78u8,65u8,91u8,149u8,194u8,135u8,49u8,68u8,42u8,95u8,193u8,79u8,16u8,107u8,149u8,7u8,80u8,125u8,191u8, - 58u8,39u8,90u8,140u8,171u8,181u8,202u8,1u8,254u8,243u8,84u8,141u8,65u8,196u8,49u8,229u8,192u8,4u8,110u8,225u8, - 126u8,109u8,246u8,87u8,141u8,204u8,188u8,128u8,12u8,111u8,189u8,165u8,129u8,91u8,203u8,139u8,48u8,145u8,17u8,243u8, - 192u8,43u8,111u8,219u8,86u8,175u8,198u8,222u8,210u8,121u8,175u8,63u8,46u8,29u8,252u8,16u8,176u8,15u8,107u8,203u8, - 151u8,13u8,42u8,218u8,222u8,16u8,228u8,212u8,147u8,225u8,191u8,120u8,229u8,94u8,63u8,168u8,165u8,239u8,219u8,142u8, - 190u8,143u8,227u8,210u8,75u8,202u8,149u8,94u8,113u8,105u8,213u8,9u8,31u8,192u8,251u8,208u8,151u8,225u8,156u8,10u8, - 165u8,20u8,152u8,43u8,77u8,18u8,210u8,212u8,8u8,215u8,184u8,2u8,145u8,80u8,188u8,241u8,10u8,240u8,78u8,26u8, - 91u8,150u8,154u8,23u8,247u8,133u8,194u8,9u8,206u8,148u8,245u8,193u8,197u8,84u8,214u8,108u8,164u8,70u8,112u8,198u8, - 2u8,87u8,139u8,145u8,200u8,88u8,142u8,170u8,59u8,150u8,38u8,162u8,157u8,195u8,226u8,115u8,77u8,137u8,144u8,118u8, - 139u8,65u8,34u8,127u8,155u8,193u8,243u8,158u8,131u8,27u8,67u8,55u8,45u8,108u8,147u8,195u8,128u8,61u8,29u8,3u8, - 253u8,190u8,17u8,201u8,24u8,174u8,250u8,13u8,18u8,9u8,105u8,26u8,170u8,59u8,39u8,24u8,192u8,47u8,244u8,235u8, - 234u8,188u8,4u8,114u8,106u8,238u8,27u8,124u8,97u8,77u8,184u8,239u8,111u8,113u8,24u8,125u8,46u8,36u8,149u8,7u8, - 113u8,36u8,83u8,178u8,234u8,50u8,164u8,113u8,78u8,206u8,103u8,17u8,86u8,148u8,146u8,110u8,81u8,109u8,240u8,25u8, - 97u8,206u8,32u8,21u8,57u8,239u8,229u8,193u8,26u8,168u8,166u8,155u8,20u8,120u8,19u8,49u8,107u8,13u8,222u8,157u8, - 218u8,22u8,13u8,164u8,164u8,213u8,237u8,33u8,38u8,118u8,176u8,64u8,245u8,213u8,189u8,106u8,48u8,208u8,245u8,40u8, - 7u8,89u8,232u8,15u8,40u8,134u8,31u8,22u8,252u8,39u8,180u8,133u8,166u8,238u8,97u8,114u8,140u8,228u8,92u8,82u8, - 222u8,99u8,156u8,139u8,34u8,177u8,165u8,242u8,81u8,172u8,248u8,174u8,80u8,2u8,61u8,146u8,162u8,160u8,114u8,128u8, - 169u8,106u8,243u8,69u8,222u8,244u8,158u8,68u8,42u8,179u8,66u8,102u8,166u8,226u8,200u8,129u8,51u8,81u8,253u8,214u8, - 6u8,45u8,129u8,214u8,219u8,16u8,48u8,49u8,237u8,93u8,236u8,61u8,222u8,194u8,58u8,176u8,119u8,43u8,234u8,225u8, - 255u8,42u8,186u8,41u8,25u8,62u8,98u8,11u8,246u8,203u8,165u8,141u8,2u8,228u8,15u8,235u8,34u8,244u8,200u8,71u8, - 21u8,226u8,55u8,177u8,113u8,145u8,25u8,212u8,214u8,73u8,231u8,66u8,106u8,232u8,57u8,185u8,0u8,102u8,101u8,10u8, - 103u8,125u8,152u8,75u8,109u8,236u8,241u8,124u8,125u8,165u8,36u8,199u8,238u8,199u8,3u8,186u8,88u8,93u8,156u8,87u8, - 187u8,232u8,124u8,92u8,125u8,82u8,255u8,107u8,203u8,212u8,6u8,26u8,5u8,56u8,233u8,108u8,214u8,42u8,135u8,211u8, - 76u8,75u8,59u8,37u8,123u8,201u8,116u8,137u8,32u8,42u8,31u8,66u8,34u8,3u8,21u8,121u8,204u8,199u8,233u8,58u8, - 135u8,202u8,41u8,182u8,21u8,56u8,207u8,37u8,54u8,40u8,6u8,190u8,149u8,6u8,219u8,153u8,40u8,114u8,118u8,234u8, - 27u8,136u8,119u8,6u8,90u8,7u8,200u8,119u8,160u8,219u8,193u8,246u8,15u8,183u8,136u8,4u8,45u8,87u8,53u8,188u8, - 219u8,147u8,128u8,201u8,134u8,244u8,51u8,47u8,234u8,128u8,39u8,237u8,44u8,54u8,210u8,244u8,9u8,249u8,142u8,234u8, - 143u8,217u8,153u8,67u8,155u8,185u8,195u8,88u8,187u8,142u8,252u8,99u8,251u8,136u8,118u8,182u8,143u8,235u8,33u8,205u8, - 122u8,103u8,102u8,26u8,103u8,52u8,133u8,237u8,145u8,15u8,31u8,104u8,107u8,86u8,118u8,52u8,148u8,234u8,64u8,162u8, - 235u8,34u8,159u8,62u8,243u8,154u8,155u8,42u8,207u8,155u8,151u8,162u8,221u8,241u8,85u8,178u8,143u8,63u8,158u8,67u8, - 165u8,146u8,70u8,128u8,21u8,208u8,225u8,128u8,154u8,49u8,60u8,253u8,203u8,162u8,177u8,83u8,149u8,37u8,171u8,191u8, - 215u8,247u8,97u8,250u8,36u8,179u8,5u8,240u8,87u8,186u8,190u8,38u8,137u8,186u8,117u8,239u8,9u8,118u8,138u8,90u8, - 167u8,111u8,156u8,120u8,131u8,25u8,39u8,82u8,218u8,234u8,226u8,134u8,105u8,110u8,87u8,91u8,193u8,177u8,210u8,148u8, - 174u8,244u8,75u8,228u8,199u8,194u8,129u8,16u8,27u8,17u8,54u8,247u8,194u8,166u8,254u8,190u8,200u8,54u8,130u8,242u8, - 214u8,123u8,254u8,17u8,208u8,247u8,94u8,80u8,72u8,90u8,173u8,142u8,48u8,239u8,21u8,167u8,46u8,184u8,29u8,214u8, - 187u8,222u8,252u8,228u8,91u8,252u8,221u8,74u8,66u8,243u8,245u8,241u8,155u8,74u8,98u8,212u8,95u8,127u8,131u8,148u8, - 173u8,201u8,189u8,68u8,248u8,186u8,189u8,14u8,193u8,110u8,4u8,183u8,109u8,184u8,39u8,162u8,72u8,21u8,153u8,165u8, - 253u8,240u8,243u8,139u8,187u8,81u8,223u8,251u8,192u8,172u8,57u8,183u8,124u8,47u8,172u8,132u8,232u8,241u8,33u8,23u8, - 25u8,234u8,126u8,107u8,95u8,177u8,54u8,191u8,86u8,138u8,211u8,87u8,1u8,20u8,103u8,244u8,160u8,152u8,108u8,172u8, - 187u8,146u8,165u8,69u8,18u8,27u8,141u8,70u8,155u8,107u8,72u8,109u8,74u8,79u8,35u8,21u8,99u8,45u8,178u8,22u8, - 16u8,134u8,205u8,63u8,105u8,116u8,206u8,174u8,245u8,117u8,28u8,52u8,20u8,27u8,214u8,74u8,127u8,93u8,79u8,176u8, - 229u8,205u8,124u8,116u8,210u8,223u8,70u8,124u8,86u8,94u8,166u8,42u8,60u8,120u8,71u8,134u8,79u8,30u8,129u8,122u8, - 242u8,242u8,129u8,168u8,39u8,47u8,91u8,168u8,181u8,150u8,107u8,63u8,86u8,245u8,186u8,37u8,189u8,65u8,51u8,233u8, - 212u8,168u8,44u8,61u8,37u8,38u8,143u8,78u8,217u8,142u8,123u8,237u8,247u8,204u8,223u8,139u8,23u8,63u8,50u8,127u8, - 45u8,34u8,55u8,163u8,232u8,86u8,220u8,119u8,41u8,165u8,7u8,19u8,198u8,231u8,231u8,102u8,13u8,28u8,111u8,227u8, - 168u8,236u8,237u8,72u8,30u8,245u8,214u8,119u8,255u8,201u8,95u8,101u8,237u8,130u8,102u8,43u8,58u8,163u8,195u8,238u8, - 253u8,199u8,139u8,245u8,161u8,245u8,167u8,22u8,57u8,77u8,98u8,220u8,137u8,228u8,14u8,109u8,122u8,141u8,127u8,66u8, - 119u8,51u8,142u8,221u8,199u8,84u8,92u8,115u8,255u8,247u8,107u8,192u8,194u8,66u8,243u8,132u8,134u8,198u8,24u8,126u8, - 108u8,85u8,190u8,206u8,215u8,19u8,28u8,55u8,213u8,25u8,52u8,155u8,151u8,111u8,119u8,85u8,59u8,10u8,202u8,118u8, - 213u8,158u8,145u8,248u8,103u8,118u8,139u8,163u8,143u8,90u8,166u8,146u8,103u8,65u8,166u8,188u8,77u8,88u8,113u8,141u8, - 108u8,95u8,184u8,247u8,188u8,100u8,91u8,60u8,237u8,226u8,177u8,13u8,207u8,178u8,82u8,164u8,40u8,97u8,230u8,45u8, - 210u8,213u8,209u8,168u8,29u8,206u8,85u8,224u8,52u8,61u8,128u8,194u8,160u8,155u8,79u8,184u8,67u8,141u8,149u8,83u8, - 153u8,209u8,172u8,16u8,181u8,20u8,137u8,252u8,199u8,137u8,211u8,20u8,113u8,165u8,138u8,114u8,158u8,176u8,158u8,76u8, - 237u8,29u8,159u8,212u8,99u8,158u8,178u8,60u8,26u8,42u8,245u8,224u8,160u8,125u8,246u8,208u8,153u8,240u8,208u8,73u8, - 132u8,143u8,173u8,191u8,13u8,222u8,124u8,146u8,31u8,196u8,223u8,243u8,196u8,242u8,192u8,135u8,112u8,119u8,191u8,223u8, - 30u8,226u8,123u8,245u8,230u8,216u8,103u8,99u8,243u8,186u8,114u8,132u8,247u8,124u8,253u8,217u8,134u8,109u8,94u8,234u8, - 142u8,116u8,123u8,159u8,199u8,59u8,110u8,3u8,15u8,117u8,123u8,71u8,238u8,118u8,93u8,21u8,14u8,193u8,109u8,162u8, - 221u8,119u8,254u8,5u8,128u8,66u8,73u8,9u8,245u8,22u8,0u8,0u8,0u8,0u8,17u8,116u8,97u8,98u8,108u8,101u8, - 95u8,119u8,105u8,116u8,104u8,95u8,108u8,101u8,110u8,103u8,116u8,104u8,236u8,11u8,31u8,139u8,8u8,0u8,0u8,0u8, - 0u8,0u8,2u8,255u8,197u8,88u8,109u8,111u8,219u8,54u8,16u8,254u8,158u8,95u8,113u8,197u8,128u8,206u8,106u8,213u8, - 196u8,78u8,135u8,173u8,144u8,155u8,2u8,193u8,154u8,1u8,69u8,187u8,118u8,104u8,210u8,108u8,197u8,48u8,56u8,178u8, - 68u8,197u8,68u8,101u8,81u8,35u8,169u8,164u8,70u8,224u8,255u8,190u8,187u8,35u8,45u8,201u8,146u8,237u8,52u8,77u8, - 139u8,229u8,147u8,35u8,221u8,235u8,115u8,15u8,143u8,119u8,58u8,56u8,56u8,128u8,147u8,207u8,86u8,20u8,169u8,129u8, - 179u8,120u8,154u8,11u8,136u8,139u8,20u8,74u8,173u8,174u8,100u8,42u8,12u8,100u8,85u8,145u8,88u8,169u8,10u8,3u8, - 166u8,74u8,102u8,16u8,27u8,200u8,69u8,113u8,105u8,103u8,44u8,101u8,103u8,40u8,61u8,149u8,185u8,180u8,11u8,176u8, - 10u8,166u8,2u8,80u8,197u8,106u8,181u8,16u8,233u8,222u8,222u8,92u8,165u8,21u8,89u8,43u8,173u8,50u8,19u8,99u8, - 211u8,40u8,178u8,100u8,126u8,114u8,45u8,237u8,108u8,226u8,141u8,220u8,236u8,1u8,254u8,85u8,70u8,0u8,11u8,8u8, - 173u8,149u8,30u8,215u8,207u8,186u8,170u8,81u8,116u8,115u8,42u8,242u8,44u8,116u8,113u8,46u8,199u8,123u8,44u8,137u8, - 241u8,23u8,177u8,149u8,87u8,2u8,18u8,149u8,10u8,208u8,177u8,52u8,24u8,183u8,157u8,73u8,3u8,228u8,10u8,216u8, - 104u8,20u8,201u8,226u8,42u8,206u8,101u8,58u8,137u8,245u8,101u8,53u8,23u8,133u8,53u8,131u8,128u8,213u8,19u8,204u8, - 205u8,194u8,201u8,241u8,155u8,247u8,39u8,199u8,47u8,63u8,78u8,78u8,254u8,122u8,117u8,122u8,118u8,26u8,65u8,245u8, - 243u8,79u8,112u8,4u8,163u8,225u8,112u8,252u8,13u8,157u8,188u8,125u8,119u8,54u8,249u8,237u8,221u8,135u8,183u8,47u8, - 27u8,251u8,163u8,113u8,87u8,224u8,228u8,247u8,63u8,206u8,62u8,54u8,2u8,135u8,117u8,154u8,7u8,112u8,182u8,40u8, - 5u8,168u8,12u8,24u8,13u8,195u8,143u8,17u8,238u8,42u8,177u8,14u8,145u8,63u8,49u8,146u8,55u8,12u8,236u8,243u8, - 114u8,22u8,23u8,86u8,205u8,225u8,117u8,132u8,166u8,203u8,5u8,60u8,134u8,84u8,171u8,50u8,132u8,213u8,243u8,243u8, - 23u8,48u8,195u8,90u8,26u8,171u8,180u8,240u8,85u8,160u8,63u8,89u8,20u8,66u8,71u8,206u8,218u8,243u8,215u8,33u8, - 138u8,133u8,245u8,59u8,87u8,50u8,14u8,204u8,61u8,92u8,54u8,129u8,253u8,170u8,69u8,108u8,177u8,96u8,80u8,136u8, - 107u8,167u8,189u8,207u8,239u8,202u8,106u8,154u8,203u8,132u8,72u8,68u8,111u8,158u8,119u8,163u8,57u8,143u8,92u8,4u8, - 47u8,6u8,65u8,212u8,75u8,129u8,221u8,183u8,98u8,235u8,188u8,111u8,189u8,105u8,69u8,238u8,121u8,194u8,206u8,72u8, - 127u8,16u8,132u8,107u8,98u8,171u8,36u8,134u8,205u8,227u8,101u8,55u8,153u8,151u8,142u8,195u8,152u8,13u8,91u8,219u8, - 135u8,51u8,164u8,57u8,255u8,132u8,121u8,133u8,85u8,66u8,154u8,139u8,121u8,233u8,40u8,143u8,135u8,34u8,17u8,34u8, - 237u8,101u8,235u8,207u8,193u8,132u8,5u8,251u8,121u8,191u8,24u8,184u8,64u8,55u8,39u8,29u8,180u8,114u8,139u8,141u8, - 17u8,218u8,62u8,112u8,242u8,251u8,254u8,216u8,28u8,29u8,97u8,2u8,93u8,206u8,25u8,139u8,37u8,24u8,52u8,20u8, - 10u8,130u8,113u8,171u8,118u8,182u8,143u8,159u8,195u8,44u8,172u8,49u8,153u8,192u8,18u8,25u8,199u8,142u8,26u8,77u8, - 15u8,168u8,207u8,103u8,192u8,42u8,65u8,23u8,177u8,227u8,52u8,245u8,181u8,71u8,206u8,107u8,6u8,198u8,174u8,48u8, - 219u8,135u8,227u8,169u8,210u8,214u8,128u8,204u8,176u8,103u8,120u8,129u8,76u8,105u8,62u8,60u8,181u8,133u8,79u8,2u8, - 241u8,206u8,145u8,69u8,233u8,2u8,196u8,103u8,105u8,172u8,113u8,176u8,59u8,113u8,105u8,13u8,158u8,123u8,192u8,195u8, - 86u8,40u8,235u8,40u8,147u8,98u8,244u8,228u8,164u8,182u8,192u8,206u8,66u8,110u8,75u8,73u8,92u8,144u8,28u8,181u8, - 35u8,105u8,18u8,117u8,37u8,72u8,60u8,211u8,72u8,123u8,105u8,123u8,149u8,138u8,211u8,116u8,71u8,125u8,30u8,206u8, - 43u8,187u8,185u8,72u8,33u8,197u8,28u8,1u8,254u8,70u8,248u8,35u8,56u8,111u8,23u8,205u8,131u8,134u8,166u8,7u8, - 108u8,192u8,1u8,225u8,225u8,70u8,53u8,214u8,9u8,58u8,32u8,215u8,197u8,93u8,255u8,247u8,49u8,248u8,22u8,209u8, - 198u8,59u8,249u8,183u8,146u8,154u8,58u8,53u8,200u8,57u8,58u8,96u8,110u8,106u8,145u8,97u8,162u8,69u8,34u8,86u8, - 240u8,163u8,143u8,74u8,192u8,245u8,76u8,98u8,223u8,190u8,64u8,175u8,23u8,48u8,143u8,75u8,108u8,90u8,106u8,191u8, - 49u8,84u8,151u8,6u8,21u8,208u8,32u8,35u8,220u8,170u8,17u8,171u8,245u8,32u8,67u8,37u8,173u8,174u8,119u8,161u8, - 182u8,19u8,49u8,60u8,235u8,15u8,207u8,251u8,104u8,57u8,171u8,131u8,135u8,93u8,176u8,130u8,173u8,217u8,195u8,255u8, - 149u8,250u8,4u8,29u8,223u8,139u8,52u8,129u8,23u8,218u8,10u8,3u8,121u8,216u8,204u8,157u8,30u8,28u8,239u8,133u8, - 173u8,116u8,97u8,56u8,111u8,207u8,25u8,149u8,53u8,231u8,47u8,4u8,185u8,143u8,135u8,144u8,254u8,47u8,170u8,249u8, - 84u8,104u8,122u8,75u8,105u8,74u8,97u8,122u8,233u8,229u8,62u8,214u8,59u8,86u8,54u8,112u8,215u8,213u8,205u8,70u8, - 66u8,111u8,141u8,87u8,99u8,137u8,24u8,126u8,132u8,222u8,213u8,17u8,127u8,112u8,195u8,236u8,197u8,117u8,91u8,27u8, - 221u8,26u8,214u8,84u8,169u8,124u8,75u8,92u8,220u8,69u8,191u8,15u8,183u8,94u8,21u8,212u8,180u8,89u8,186u8,140u8, - 165u8,134u8,1u8,203u8,133u8,112u8,145u8,138u8,44u8,174u8,114u8,123u8,17u8,64u8,38u8,53u8,94u8,37u8,95u8,205u8, - 62u8,55u8,69u8,121u8,115u8,155u8,238u8,85u8,250u8,113u8,167u8,46u8,230u8,109u8,81u8,39u8,219u8,64u8,78u8,12u8, - 212u8,27u8,139u8,112u8,84u8,177u8,177u8,44u8,204u8,134u8,147u8,26u8,116u8,46u8,229u8,59u8,81u8,154u8,43u8,1u8, - 34u8,55u8,98u8,179u8,149u8,29u8,221u8,212u8,199u8,222u8,234u8,168u8,95u8,220u8,85u8,191u8,62u8,212u8,46u8,117u8, - 182u8,22u8,157u8,9u8,131u8,37u8,255u8,146u8,98u8,147u8,161u8,170u8,76u8,105u8,152u8,106u8,184u8,230u8,143u8,115u8, - 71u8,129u8,248u8,232u8,109u8,131u8,34u8,195u8,215u8,56u8,147u8,118u8,25u8,83u8,149u8,20u8,212u8,55u8,33u8,8u8, - 187u8,234u8,92u8,116u8,68u8,139u8,7u8,119u8,231u8,5u8,149u8,210u8,55u8,39u8,14u8,107u8,117u8,37u8,86u8,226u8, - 22u8,42u8,208u8,28u8,131u8,7u8,114u8,85u8,205u8,47u8,168u8,215u8,122u8,149u8,31u8,57u8,101u8,118u8,213u8,188u8, - 89u8,142u8,251u8,61u8,106u8,142u8,99u8,131u8,155u8,25u8,46u8,216u8,230u8,5u8,79u8,22u8,154u8,123u8,215u8,247u8, - 189u8,98u8,52u8,251u8,190u8,239u8,245u8,210u8,62u8,188u8,132u8,26u8,134u8,219u8,160u8,230u8,92u8,220u8,138u8,216u8, - 174u8,3u8,244u8,164u8,125u8,128u8,208u8,248u8,45u8,93u8,62u8,171u8,97u8,92u8,209u8,100u8,125u8,24u8,220u8,12u8, - 197u8,74u8,246u8,62u8,163u8,198u8,166u8,27u8,96u8,39u8,89u8,219u8,153u8,252u8,240u8,183u8,197u8,193u8,119u8,162u8, - 138u8,124u8,241u8,79u8,179u8,31u8,96u8,8u8,254u8,194u8,18u8,87u8,162u8,160u8,250u8,210u8,184u8,201u8,119u8,84u8, - 8u8,36u8,139u8,180u8,192u8,231u8,164u8,42u8,139u8,203u8,254u8,110u8,128u8,250u8,19u8,92u8,171u8,103u8,34u8,249u8, - 36u8,210u8,251u8,44u8,7u8,24u8,204u8,135u8,162u8,140u8,147u8,79u8,62u8,28u8,94u8,69u8,93u8,129u8,66u8,182u8, - 86u8,162u8,255u8,213u8,80u8,144u8,168u8,170u8,192u8,161u8,24u8,75u8,142u8,209u8,182u8,77u8,112u8,238u8,190u8,186u8, - 187u8,54u8,134u8,155u8,238u8,190u8,176u8,99u8,93u8,88u8,75u8,209u8,111u8,13u8,99u8,88u8,129u8,215u8,243u8,184u8, - 6u8,119u8,131u8,244u8,185u8,208u8,50u8,91u8,48u8,144u8,79u8,24u8,87u8,178u8,91u8,127u8,146u8,136u8,233u8,187u8, - 131u8,211u8,39u8,88u8,185u8,82u8,29u8,199u8,65u8,231u8,20u8,56u8,152u8,142u8,120u8,25u8,37u8,98u8,132u8,76u8, - 15u8,220u8,18u8,93u8,108u8,34u8,201u8,99u8,60u8,167u8,180u8,200u8,116u8,224u8,88u8,191u8,124u8,66u8,166u8,117u8, - 8u8,89u8,140u8,45u8,202u8,233u8,210u8,10u8,228u8,57u8,65u8,164u8,110u8,52u8,59u8,17u8,177u8,76u8,11u8,138u8, - 221u8,32u8,212u8,121u8,185u8,62u8,222u8,207u8,199u8,231u8,82u8,61u8,11u8,161u8,122u8,70u8,121u8,180u8,11u8,235u8, - 62u8,231u8,152u8,153u8,170u8,242u8,148u8,41u8,58u8,139u8,177u8,169u8,209u8,170u8,53u8,132u8,133u8,176u8,189u8,29u8, - 243u8,65u8,235u8,84u8,132u8,48u8,12u8,66u8,24u8,117u8,12u8,210u8,168u8,230u8,237u8,73u8,119u8,221u8,177u8,181u8, - 208u8,49u8,207u8,181u8,196u8,209u8,208u8,109u8,97u8,158u8,118u8,94u8,28u8,151u8,177u8,81u8,109u8,202u8,103u8,227u8, - 0u8,13u8,73u8,127u8,52u8,92u8,247u8,116u8,82u8,152u8,74u8,183u8,239u8,64u8,116u8,156u8,96u8,175u8,23u8,137u8, - 69u8,26u8,24u8,74u8,93u8,161u8,82u8,47u8,133u8,71u8,245u8,22u8,65u8,9u8,208u8,136u8,71u8,225u8,140u8,182u8, - 26u8,247u8,65u8,118u8,173u8,247u8,236u8,58u8,57u8,52u8,235u8,108u8,246u8,76u8,190u8,17u8,216u8,232u8,93u8,90u8, - 173u8,160u8,233u8,3u8,129u8,154u8,11u8,156u8,112u8,241u8,24u8,210u8,117u8,230u8,144u8,185u8,114u8,180u8,150u8,246u8, - 199u8,218u8,237u8,14u8,104u8,14u8,159u8,182u8,92u8,109u8,207u8,243u8,240u8,105u8,47u8,168u8,83u8,73u8,99u8,171u8, - 171u8,247u8,218u8,114u8,45u8,210u8,176u8,157u8,125u8,151u8,33u8,9u8,126u8,36u8,186u8,196u8,79u8,120u8,119u8,5u8, - 225u8,85u8,6u8,215u8,98u8,133u8,2u8,126u8,15u8,80u8,197u8,19u8,118u8,71u8,217u8,243u8,21u8,223u8,247u8,137u8, - 17u8,98u8,92u8,126u8,110u8,217u8,144u8,63u8,58u8,249u8,101u8,67u8,250u8,235u8,145u8,28u8,186u8,72u8,106u8,169u8, - 181u8,235u8,141u8,240u8,25u8,111u8,123u8,213u8,78u8,96u8,237u8,147u8,205u8,96u8,53u8,76u8,46u8,247u8,150u8,123u8, - 255u8,1u8,188u8,206u8,123u8,17u8,35u8,21u8,0u8,0u8,0u8,0u8,10u8,98u8,105u8,103u8,95u8,118u8,101u8,99u8, - 116u8,111u8,114u8,223u8,26u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,91u8,123u8,111u8,27u8, - 185u8,17u8,255u8,63u8,159u8,130u8,65u8,113u8,62u8,41u8,93u8,59u8,150u8,243u8,186u8,56u8,177u8,129u8,60u8,220u8, - 34u8,104u8,27u8,7u8,137u8,115u8,215u8,94u8,16u8,172u8,214u8,18u8,101u8,81u8,145u8,118u8,149u8,125u8,41u8,110u8, - 224u8,239u8,222u8,25u8,190u8,150u8,175u8,93u8,73u8,142u8,125u8,40u8,206u8,8u8,2u8,89u8,36u8,135u8,156u8,153u8, - 223u8,60u8,73u8,47u8,178u8,113u8,53u8,167u8,36u8,89u8,150u8,89u8,17u8,23u8,229u8,248u8,240u8,240u8,156u8,93u8, - 196u8,53u8,29u8,149u8,89u8,78u8,190u8,223u8,33u8,240u8,83u8,21u8,148u8,240u8,17u8,154u8,231u8,89u8,254u8,204u8, - 254u8,78u8,204u8,108u8,190u8,52u8,8u8,149u8,201u8,249u8,156u8,198u8,43u8,86u8,78u8,227u8,57u8,77u8,47u8,202u8, - 233u8,225u8,225u8,247u8,15u8,116u8,62u8,137u8,200u8,25u8,142u8,252u8,6u8,3u8,255u8,228u8,223u8,95u8,137u8,229u8, - 147u8,156u8,209u8,116u8,108u8,82u8,40u8,22u8,73u8,94u8,198u8,106u8,11u8,62u8,233u8,254u8,253u8,251u8,228u8,87u8, - 113u8,58u8,150u8,142u8,233u8,55u8,194u8,10u8,146u8,85u8,37u8,201u8,38u8,228u8,60u8,171u8,210u8,113u8,193u8,103u8, - 141u8,178u8,180u8,40u8,201u8,201u8,155u8,183u8,175u8,79u8,254u8,29u8,159u8,126u8,60u8,139u8,79u8,255u8,22u8,191u8, - 60u8,253u8,248u8,246u8,245u8,135u8,67u8,82u8,61u8,126u8,72u8,142u8,200u8,224u8,153u8,38u8,247u8,42u8,73u8,211u8, - 172u8,36u8,99u8,90u8,148u8,121u8,118u8,73u8,18u8,146u8,102u8,233u8,46u8,93u8,44u8,203u8,75u8,34u8,182u8,54u8, - 73u8,254u8,122u8,242u8,234u8,236u8,244u8,125u8,252u8,246u8,244u8,44u8,62u8,249u8,215u8,187u8,179u8,255u8,40u8,122u8, - 7u8,30u8,189u8,101u8,182u8,36u8,231u8,201u8,232u8,11u8,240u8,149u8,45u8,72u8,146u8,146u8,53u8,36u8,45u8,114u8, - 15u8,26u8,114u8,231u8,213u8,232u8,11u8,45u8,227u8,130u8,253u8,151u8,146u8,145u8,32u8,125u8,78u8,201u8,190u8,73u8, - 225u8,247u8,147u8,247u8,167u8,241u8,203u8,143u8,175u8,254u8,113u8,114u8,22u8,127u8,120u8,243u8,251u8,137u8,162u8,242u8, - 208u8,16u8,218u8,11u8,82u8,140u8,146u8,57u8,10u8,94u8,30u8,129u8,176u8,197u8,114u8,78u8,23u8,52u8,45u8,147u8, - 146u8,101u8,41u8,156u8,181u8,160u8,99u8,2u8,31u8,184u8,222u8,10u8,178u8,154u8,210u8,156u8,18u8,42u8,166u8,20u8, - 36u8,129u8,95u8,46u8,242u8,172u8,90u8,194u8,36u8,150u8,150u8,153u8,60u8,87u8,177u8,167u8,119u8,56u8,73u8,70u8, - 83u8,249u8,45u8,153u8,38u8,176u8,2u8,142u8,187u8,76u8,70u8,12u8,152u8,6u8,237u8,12u8,13u8,62u8,134u8,154u8, - 172u8,88u8,13u8,130u8,175u8,70u8,37u8,121u8,201u8,46u8,132u8,106u8,159u8,159u8,29u8,115u8,10u8,5u8,124u8,166u8, - 18u8,133u8,248u8,35u8,183u8,60u8,116u8,1u8,244u8,28u8,216u8,141u8,36u8,91u8,176u8,246u8,56u8,210u8,43u8,0u8, - 80u8,49u8,199u8,9u8,23u8,73u8,228u8,80u8,226u8,135u8,225u8,35u8,124u8,224u8,170u8,145u8,214u8,123u8,122u8,81u8, - 205u8,147u8,92u8,65u8,237u8,197u8,187u8,55u8,205u8,216u8,171u8,156u8,38u8,37u8,117u8,53u8,42u8,56u8,89u8,86u8, - 231u8,115u8,54u8,234u8,9u8,40u8,247u8,201u8,164u8,146u8,147u8,158u8,159u8,29u8,10u8,110u8,142u8,123u8,238u8,214u8, - 253u8,67u8,155u8,241u8,134u8,221u8,164u8,40u8,104u8,94u8,222u8,53u8,87u8,144u8,99u8,178u8,31u8,17u8,110u8,136u8, - 135u8,135u8,44u8,173u8,147u8,57u8,27u8,199u8,73u8,126u8,81u8,161u8,52u8,123u8,30u8,16u8,250u8,253u8,103u8,154u8, - 154u8,222u8,196u8,216u8,193u8,18u8,106u8,192u8,94u8,83u8,186u8,234u8,245u8,35u8,107u8,186u8,33u8,209u8,253u8,40u8, - 64u8,136u8,31u8,179u8,25u8,184u8,114u8,37u8,171u8,164u8,167u8,80u8,8u8,208u8,16u8,187u8,145u8,1u8,194u8,185u8, - 76u8,88u8,202u8,210u8,11u8,82u8,78u8,41u8,89u8,162u8,0u8,16u8,109u8,10u8,46u8,173u8,50u8,46u8,96u8,201u8, - 156u8,150u8,89u8,106u8,200u8,89u8,174u8,1u8,176u8,68u8,100u8,115u8,153u8,3u8,21u8,82u8,131u8,233u8,112u8,173u8, - 153u8,146u8,55u8,36u8,185u8,172u8,138u8,105u8,140u8,166u8,221u8,219u8,89u8,128u8,235u8,169u8,35u8,117u8,62u8,99u8, - 78u8,237u8,242u8,253u8,90u8,186u8,23u8,100u8,76u8,178u8,62u8,172u8,135u8,141u8,245u8,188u8,56u8,207u8,114u8,48u8, - 51u8,54u8,193u8,175u8,209u8,171u8,161u8,161u8,243u8,83u8,152u8,92u8,115u8,110u8,165u8,167u8,138u8,37u8,178u8,142u8, - 123u8,181u8,205u8,78u8,63u8,128u8,33u8,86u8,136u8,233u8,189u8,157u8,186u8,223u8,1u8,32u8,215u8,189u8,153u8,0u8, - 66u8,209u8,24u8,32u8,82u8,192u8,137u8,76u8,72u8,196u8,145u8,99u8,91u8,49u8,185u8,2u8,105u8,214u8,13u8,149u8, - 0u8,202u8,44u8,126u8,164u8,204u8,11u8,185u8,177u8,33u8,193u8,23u8,163u8,175u8,21u8,203u8,185u8,225u8,177u8,5u8, - 8u8,158u8,251u8,178u8,156u8,78u8,192u8,77u8,165u8,35u8,74u8,192u8,37u8,161u8,108u8,135u8,108u8,8u8,88u8,146u8, - 10u8,65u8,116u8,109u8,32u8,112u8,54u8,244u8,194u8,136u8,39u8,116u8,152u8,157u8,103u8,43u8,41u8,237u8,29u8,83u8, - 220u8,17u8,97u8,10u8,84u8,59u8,103u8,33u8,209u8,147u8,231u8,18u8,226u8,189u8,78u8,217u8,7u8,162u8,149u8,41u8, - 126u8,193u8,3u8,196u8,103u8,126u8,144u8,94u8,64u8,142u8,114u8,100u8,167u8,222u8,211u8,170u8,97u8,228u8,62u8,81u8, - 191u8,10u8,24u8,227u8,119u8,63u8,57u8,223u8,249u8,222u8,175u8,172u8,242u8,20u8,108u8,116u8,67u8,41u8,131u8,133u8, - 222u8,168u8,148u8,99u8,216u8,87u8,73u8,26u8,45u8,172u8,77u8,218u8,56u8,246u8,135u8,73u8,28u8,15u8,213u8,46u8, - 117u8,62u8,42u8,220u8,193u8,143u8,74u8,255u8,132u8,71u8,149u8,4u8,82u8,161u8,177u8,225u8,51u8,50u8,248u8,47u8, - 151u8,71u8,138u8,248u8,40u8,186u8,33u8,66u8,49u8,232u8,74u8,148u8,235u8,88u8,45u8,245u8,97u8,46u8,129u8,192u8, - 46u8,85u8,55u8,159u8,22u8,58u8,3u8,224u8,243u8,244u8,198u8,69u8,178u8,128u8,69u8,249u8,24u8,22u8,65u8,240u8, - 133u8,17u8,8u8,220u8,163u8,81u8,149u8,231u8,194u8,9u8,115u8,114u8,141u8,102u8,95u8,179u8,98u8,52u8,79u8,216u8, - 130u8,230u8,224u8,98u8,167u8,160u8,89u8,208u8,223u8,136u8,103u8,18u8,240u8,121u8,148u8,21u8,229u8,252u8,114u8,143u8, - 124u8,132u8,148u8,144u8,149u8,36u8,41u8,201u8,101u8,86u8,193u8,33u8,86u8,224u8,189u8,96u8,93u8,78u8,113u8,162u8, - 167u8,253u8,100u8,185u8,4u8,95u8,98u8,248u8,112u8,56u8,107u8,16u8,2u8,252u8,40u8,237u8,78u8,15u8,61u8,21u8, - 159u8,130u8,74u8,2u8,247u8,35u8,177u8,176u8,195u8,191u8,115u8,60u8,218u8,52u8,153u8,79u8,98u8,115u8,114u8,243u8, - 249u8,190u8,202u8,234u8,212u8,92u8,6u8,195u8,251u8,205u8,87u8,171u8,41u8,3u8,219u8,224u8,104u8,179u8,169u8,244u8, - 157u8,16u8,219u8,4u8,12u8,96u8,40u8,34u8,197u8,42u8,89u8,198u8,57u8,93u8,100u8,53u8,21u8,144u8,225u8,11u8, - 1u8,26u8,38u8,248u8,240u8,7u8,247u8,99u8,228u8,175u8,42u8,89u8,229u8,56u8,9u8,110u8,191u8,233u8,206u8,144u8, - 147u8,26u8,129u8,75u8,136u8,99u8,171u8,61u8,109u8,87u8,109u8,202u8,211u8,116u8,212u8,227u8,177u8,118u8,13u8,67u8, - 176u8,185u8,161u8,242u8,26u8,152u8,222u8,251u8,30u8,153u8,188u8,41u8,49u8,179u8,92u8,113u8,200u8,169u8,168u8,130u8, - 233u8,167u8,64u8,49u8,7u8,32u8,80u8,82u8,3u8,152u8,136u8,78u8,170u8,249u8,188u8,65u8,34u8,71u8,95u8,182u8, - 164u8,185u8,72u8,100u8,87u8,108u8,62u8,231u8,0u8,36u8,11u8,76u8,31u8,47u8,18u8,73u8,12u8,113u8,56u8,30u8, - 67u8,96u8,165u8,43u8,73u8,204u8,3u8,160u8,22u8,151u8,129u8,193u8,176u8,19u8,2u8,182u8,0u8,246u8,46u8,232u8, - 210u8,106u8,17u8,171u8,115u8,30u8,133u8,66u8,157u8,66u8,162u8,118u8,18u8,134u8,244u8,193u8,71u8,246u8,234u8,61u8, - 29u8,76u8,201u8,209u8,145u8,69u8,239u8,158u8,227u8,54u8,28u8,77u8,7u8,54u8,3u8,110u8,61u8,151u8,100u8,80u8, - 140u8,180u8,131u8,19u8,218u8,116u8,145u8,160u8,70u8,27u8,12u8,109u8,231u8,253u8,140u8,173u8,250u8,92u8,96u8,6u8, - 253u8,43u8,0u8,72u8,65u8,29u8,22u8,110u8,112u8,67u8,178u8,75u8,6u8,254u8,166u8,134u8,107u8,55u8,229u8,108u8, - 253u8,166u8,145u8,111u8,0u8,250u8,29u8,148u8,114u8,137u8,206u8,66u8,69u8,65u8,103u8,224u8,217u8,193u8,242u8,56u8, - 163u8,69u8,250u8,115u8,73u8,138u8,105u8,206u8,210u8,47u8,22u8,168u8,105u8,141u8,56u8,228u8,22u8,112u8,249u8,51u8, - 86u8,87u8,77u8,126u8,39u8,42u8,71u8,64u8,238u8,80u8,172u8,139u8,203u8,44u8,158u8,176u8,18u8,74u8,165u8,111u8, - 75u8,128u8,39u8,86u8,81u8,96u8,69u8,99u8,10u8,83u8,178u8,17u8,102u8,208u8,162u8,248u8,240u8,170u8,48u8,47u8, - 143u8,12u8,231u8,144u8,202u8,19u8,180u8,133u8,89u8,8u8,175u8,161u8,200u8,122u8,87u8,231u8,145u8,129u8,192u8,90u8, - 64u8,37u8,73u8,123u8,86u8,61u8,235u8,230u8,143u8,63u8,100u8,32u8,72u8,96u8,158u8,20u8,165u8,164u8,16u8,38u8, - 176u8,5u8,62u8,108u8,202u8,192u8,3u8,66u8,65u8,97u8,80u8,121u8,74u8,99u8,67u8,99u8,1u8,8u8,250u8,67u8, - 163u8,94u8,145u8,36u8,9u8,189u8,242u8,19u8,234u8,0u8,107u8,137u8,95u8,27u8,185u8,220u8,67u8,203u8,210u8,220u8, - 195u8,181u8,107u8,140u8,18u8,38u8,215u8,97u8,27u8,181u8,253u8,114u8,64u8,44u8,102u8,184u8,233u8,16u8,201u8,182u8, - 214u8,178u8,107u8,198u8,9u8,144u8,160u8,159u8,73u8,242u8,243u8,27u8,217u8,9u8,230u8,2u8,178u8,115u8,227u8,228u8, - 141u8,53u8,207u8,106u8,114u8,145u8,123u8,242u8,252u8,101u8,149u8,66u8,230u8,1u8,84u8,43u8,164u8,0u8,235u8,86u8, - 224u8,202u8,151u8,57u8,173u8,89u8,86u8,21u8,243u8,75u8,225u8,160u8,199u8,156u8,32u8,146u8,170u8,13u8,35u8,0u8, - 59u8,210u8,217u8,144u8,200u8,97u8,176u8,168u8,20u8,91u8,179u8,17u8,116u8,55u8,46u8,120u8,25u8,154u8,35u8,93u8, - 176u8,72u8,17u8,51u8,160u8,173u8,82u8,76u8,217u8,164u8,4u8,154u8,99u8,204u8,84u8,206u8,47u8,201u8,96u8,143u8, - 252u8,134u8,67u8,9u8,90u8,21u8,42u8,143u8,181u8,228u8,176u8,183u8,148u8,17u8,9u8,181u8,109u8,144u8,11u8,159u8, - 57u8,145u8,200u8,74u8,124u8,106u8,67u8,173u8,78u8,142u8,124u8,253u8,236u8,248u8,70u8,204u8,25u8,52u8,35u8,9u8, - 104u8,120u8,121u8,73u8,179u8,157u8,140u8,216u8,171u8,182u8,246u8,1u8,222u8,134u8,190u8,35u8,200u8,105u8,97u8,56u8, - 2u8,105u8,56u8,205u8,186u8,80u8,10u8,223u8,16u8,224u8,112u8,111u8,38u8,123u8,217u8,154u8,183u8,255u8,115u8,43u8, - 78u8,58u8,198u8,15u8,160u8,18u8,251u8,67u8,250u8,78u8,253u8,248u8,3u8,14u8,55u8,231u8,72u8,84u8,57u8,146u8, - 209u8,92u8,188u8,37u8,121u8,57u8,57u8,2u8,82u8,47u8,187u8,165u8,181u8,239u8,44u8,105u8,149u8,144u8,100u8,88u8, - 148u8,54u8,165u8,52u8,18u8,39u8,109u8,228u8,206u8,21u8,100u8,225u8,29u8,2u8,221u8,194u8,45u8,97u8,34u8,156u8, - 154u8,24u8,27u8,70u8,164u8,116u8,166u8,7u8,96u8,237u8,125u8,229u8,166u8,216u8,63u8,30u8,91u8,110u8,47u8,62u8, - 254u8,25u8,163u8,150u8,158u8,7u8,246u8,238u8,134u8,173u8,15u8,80u8,161u8,109u8,210u8,77u8,34u8,120u8,218u8,70u8, - 51u8,58u8,196u8,1u8,96u8,75u8,44u8,57u8,240u8,14u8,160u8,89u8,225u8,84u8,44u8,240u8,239u8,180u8,135u8,89u8, - 234u8,121u8,37u8,146u8,70u8,222u8,242u8,3u8,92u8,129u8,131u8,174u8,101u8,33u8,142u8,81u8,11u8,118u8,117u8,43u8, - 123u8,151u8,222u8,150u8,93u8,22u8,163u8,0u8,221u8,50u8,180u8,220u8,112u8,135u8,69u8,35u8,86u8,36u8,95u8,58u8, - 233u8,170u8,237u8,84u8,75u8,162u8,63u8,167u8,95u8,43u8,64u8,140u8,206u8,9u8,88u8,97u8,249u8,132u8,72u8,229u8, - 14u8,172u8,236u8,170u8,169u8,152u8,139u8,85u8,185u8,74u8,157u8,35u8,4u8,37u8,188u8,134u8,161u8,163u8,4u8,47u8, - 184u8,196u8,142u8,112u8,173u8,3u8,226u8,93u8,208u8,5u8,92u8,82u8,129u8,48u8,35u8,194u8,47u8,75u8,86u8,148u8, - 139u8,86u8,249u8,236u8,208u8,153u8,181u8,239u8,22u8,248u8,189u8,227u8,56u8,62u8,209u8,1u8,209u8,108u8,113u8,185u8, - 200u8,226u8,89u8,26u8,53u8,199u8,85u8,161u8,240u8,201u8,121u8,151u8,35u8,56u8,23u8,165u8,103u8,73u8,247u8,154u8, - 174u8,192u8,239u8,92u8,61u8,11u8,144u8,149u8,29u8,19u8,101u8,206u8,18u8,16u8,94u8,178u8,236u8,103u8,215u8,102u8, - 7u8,100u8,147u8,168u8,234u8,187u8,95u8,181u8,74u8,73u8,41u8,48u8,25u8,55u8,105u8,167u8,30u8,153u8,60u8,216u8, - 222u8,46u8,144u8,199u8,106u8,135u8,208u8,220u8,135u8,137u8,216u8,196u8,126u8,6u8,7u8,128u8,154u8,155u8,225u8,7u8, - 149u8,93u8,58u8,137u8,173u8,155u8,68u8,82u8,198u8,59u8,115u8,25u8,166u8,147u8,48u8,62u8,227u8,61u8,13u8,255u8, - 22u8,19u8,55u8,158u8,240u8,245u8,33u8,3u8,94u8,103u8,185u8,17u8,153u8,9u8,11u8,94u8,103u8,190u8,100u8,103u8, - 7u8,142u8,112u8,99u8,246u8,204u8,214u8,38u8,116u8,246u8,252u8,153u8,59u8,127u8,214u8,61u8,159u8,201u8,11u8,97u8, - 131u8,254u8,79u8,221u8,244u8,157u8,249u8,179u8,246u8,249u8,232u8,46u8,220u8,243u8,31u8,57u8,39u8,236u8,183u8,116u8, - 45u8,56u8,220u8,182u8,180u8,50u8,155u8,112u8,228u8,240u8,22u8,57u8,103u8,119u8,146u8,12u8,225u8,183u8,90u8,220u8, - 213u8,27u8,196u8,22u8,199u8,37u8,7u8,23u8,195u8,42u8,99u8,194u8,155u8,248u8,101u8,115u8,113u8,83u8,38u8,95u8, - 168u8,213u8,159u8,64u8,12u8,78u8,88u8,14u8,62u8,5u8,113u8,71u8,147u8,226u8,146u8,95u8,1u8,52u8,165u8,137u8, - 99u8,255u8,44u8,236u8,88u8,90u8,194u8,54u8,107u8,203u8,36u8,13u8,138u8,179u8,173u8,40u8,206u8,218u8,40u8,130u8, - 4u8,254u8,78u8,75u8,219u8,98u8,185u8,235u8,85u8,156u8,66u8,109u8,7u8,215u8,225u8,115u8,140u8,175u8,67u8,195u8, - 27u8,13u8,109u8,54u8,229u8,90u8,206u8,103u8,200u8,119u8,241u8,243u8,168u8,35u8,184u8,234u8,115u8,24u8,84u8,180u8, - 102u8,27u8,208u8,154u8,117u8,168u8,62u8,236u8,141u8,212u8,125u8,0u8,127u8,113u8,32u8,35u8,134u8,232u8,255u8,155u8, - 173u8,206u8,176u8,47u8,117u8,184u8,208u8,7u8,237u8,116u8,192u8,206u8,113u8,181u8,164u8,66u8,209u8,157u8,179u8,0u8, - 255u8,199u8,6u8,108u8,156u8,160u8,177u8,163u8,134u8,250u8,118u8,79u8,161u8,149u8,198u8,172u8,157u8,198u8,204u8,161u8, - 193u8,251u8,16u8,187u8,203u8,172u8,96u8,188u8,24u8,231u8,149u8,211u8,10u8,35u8,237u8,184u8,17u8,160u8,20u8,89u8, - 206u8,46u8,166u8,178u8,63u8,177u8,23u8,14u8,40u8,157u8,42u8,143u8,90u8,248u8,109u8,139u8,78u8,157u8,58u8,111u8, - 33u8,54u8,179u8,177u8,128u8,205u8,118u8,161u8,244u8,198u8,142u8,247u8,238u8,108u8,217u8,21u8,182u8,45u8,83u8,135u8, - 70u8,243u8,220u8,27u8,82u8,154u8,133u8,41u8,205u8,252u8,91u8,130u8,247u8,208u8,12u8,205u8,101u8,66u8,37u8,46u8, - 157u8,90u8,174u8,177u8,116u8,123u8,136u8,165u8,187u8,75u8,72u8,189u8,232u8,45u8,119u8,92u8,248u8,177u8,90u8,251u8, - 162u8,110u8,199u8,159u8,174u8,140u8,14u8,136u8,56u8,234u8,167u8,207u8,54u8,126u8,133u8,209u8,168u8,68u8,44u8,60u8, - 231u8,135u8,59u8,41u8,6u8,1u8,88u8,51u8,193u8,157u8,140u8,175u8,140u8,114u8,71u8,54u8,34u8,188u8,249u8,199u8, - 80u8,169u8,59u8,161u8,141u8,31u8,30u8,115u8,242u8,142u8,36u8,114u8,125u8,137u8,38u8,200u8,251u8,53u8,181u8,77u8, - 93u8,18u8,12u8,88u8,117u8,51u8,39u8,64u8,192u8,185u8,146u8,211u8,12u8,242u8,20u8,199u8,163u8,222u8,183u8,249u8, - 235u8,240u8,108u8,134u8,206u8,34u8,191u8,41u8,44u8,166u8,52u8,231u8,114u8,14u8,230u8,150u8,204u8,154u8,147u8,134u8, - 104u8,31u8,19u8,139u8,206u8,11u8,157u8,53u8,7u8,52u8,128u8,23u8,153u8,167u8,13u8,28u8,69u8,32u8,188u8,27u8, - 131u8,129u8,44u8,66u8,115u8,226u8,223u8,8u8,6u8,102u8,134u8,107u8,250u8,86u8,221u8,117u8,195u8,85u8,67u8,38u8, - 220u8,33u8,97u8,147u8,110u8,209u8,6u8,160u8,124u8,125u8,41u8,118u8,94u8,85u8,57u8,220u8,134u8,9u8,24u8,39u8, - 111u8,250u8,100u8,220u8,203u8,120u8,135u8,232u8,119u8,94u8,56u8,171u8,244u8,125u8,35u8,247u8,128u8,0u8,219u8,183u8, - 219u8,111u8,141u8,101u8,116u8,244u8,28u8,55u8,13u8,25u8,109u8,102u8,97u8,114u8,179u8,213u8,221u8,114u8,88u8,166u8, - 190u8,112u8,252u8,135u8,42u8,188u8,16u8,227u8,217u8,186u8,12u8,33u8,34u8,135u8,149u8,143u8,23u8,240u8,217u8,10u8, - 124u8,111u8,92u8,224u8,225u8,101u8,129u8,184u8,81u8,192u8,38u8,218u8,215u8,74u8,20u8,217u8,116u8,79u8,210u8,43u8, - 72u8,175u8,204u8,43u8,168u8,16u8,101u8,190u8,15u8,166u8,92u8,84u8,35u8,44u8,243u8,244u8,198u8,138u8,18u8,222u8, - 72u8,76u8,176u8,98u8,19u8,79u8,50u8,122u8,147u8,4u8,176u8,130u8,66u8,23u8,201u8,215u8,138u8,21u8,183u8,27u8, - 173u8,68u8,114u8,144u8,77u8,194u8,239u8,146u8,248u8,37u8,245u8,206u8,25u8,244u8,112u8,122u8,231u8,89u8,54u8,143u8, - 220u8,114u8,240u8,70u8,2u8,143u8,83u8,189u8,249u8,239u8,35u8,54u8,111u8,119u8,203u8,94u8,117u8,87u8,183u8,194u8, - 122u8,221u8,180u8,166u8,49u8,221u8,147u8,154u8,129u8,156u8,178u8,137u8,42u8,74u8,98u8,216u8,163u8,118u8,47u8,136u8, - 149u8,223u8,230u8,235u8,66u8,254u8,88u8,182u8,138u8,36u8,60u8,204u8,237u8,239u8,185u8,87u8,22u8,172u8,223u8,229u8, - 45u8,29u8,161u8,117u8,54u8,134u8,213u8,199u8,6u8,92u8,45u8,102u8,192u8,44u8,144u8,55u8,184u8,134u8,43u8,100u8, - 86u8,248u8,249u8,212u8,173u8,2u8,83u8,62u8,229u8,44u8,214u8,1u8,19u8,113u8,105u8,8u8,154u8,23u8,224u8,205u8, - 21u8,115u8,95u8,73u8,156u8,179u8,110u8,227u8,174u8,199u8,185u8,138u8,72u8,140u8,218u8,213u8,90u8,173u8,93u8,157u8, - 242u8,89u8,29u8,126u8,67u8,166u8,29u8,86u8,107u8,215u8,227u8,70u8,76u8,10u8,241u8,210u8,23u8,111u8,173u8,191u8, - 135u8,186u8,206u8,45u8,187u8,14u8,17u8,61u8,67u8,213u8,212u8,52u8,154u8,201u8,248u8,228u8,57u8,205u8,236u8,162u8, - 110u8,200u8,57u8,31u8,186u8,238u8,196u8,116u8,1u8,133u8,245u8,16u8,212u8,61u8,157u8,35u8,224u8,166u8,235u8,131u8, - 225u8,193u8,60u8,225u8,95u8,62u8,149u8,224u8,120u8,227u8,44u8,157u8,95u8,126u8,22u8,239u8,241u8,155u8,167u8,166u8, - 248u8,28u8,102u8,156u8,103u8,203u8,206u8,167u8,166u8,210u8,220u8,155u8,39u8,2u8,34u8,100u8,120u8,119u8,3u8,118u8, - 184u8,168u8,251u8,27u8,60u8,52u8,170u8,251u8,254u8,73u8,155u8,67u8,54u8,127u8,177u8,16u8,227u8,64u8,175u8,223u8, - 250u8,158u8,247u8,81u8,127u8,211u8,7u8,93u8,131u8,253u8,253u8,246u8,183u8,84u8,234u8,217u8,47u8,219u8,42u8,190u8, - 241u8,102u8,84u8,112u8,195u8,89u8,120u8,195u8,166u8,111u8,170u8,125u8,31u8,20u8,88u8,206u8,158u8,42u8,39u8,184u8, - 199u8,103u8,66u8,179u8,202u8,191u8,113u8,155u8,241u8,166u8,215u8,218u8,103u8,100u8,129u8,172u8,73u8,48u8,180u8,235u8, - 166u8,126u8,166u8,249u8,201u8,24u8,105u8,152u8,32u8,158u8,114u8,135u8,5u8,252u8,241u8,204u8,108u8,241u8,123u8,202u8, - 55u8,153u8,145u8,180u8,247u8,91u8,134u8,155u8,94u8,126u8,251u8,156u8,89u8,96u8,252u8,234u8,143u8,84u8,117u8,83u8, - 188u8,27u8,47u8,16u8,107u8,167u8,53u8,161u8,78u8,235u8,53u8,130u8,106u8,179u8,250u8,231u8,198u8,250u8,244u8,169u8, - 205u8,76u8,215u8,210u8,253u8,214u8,236u8,143u8,244u8,140u8,163u8,4u8,52u8,14u8,110u8,202u8,240u8,74u8,224u8,254u8, - 147u8,249u8,42u8,185u8,44u8,200u8,167u8,183u8,17u8,25u8,68u8,228u8,32u8,34u8,123u8,123u8,123u8,228u8,237u8,238u8, - 224u8,179u8,184u8,251u8,202u8,233u8,18u8,2u8,64u8,201u8,106u8,74u8,194u8,7u8,241u8,80u8,64u8,191u8,45u8,129u8, - 56u8,244u8,94u8,76u8,169u8,60u8,107u8,1u8,126u8,152u8,102u8,88u8,227u8,210u8,0u8,20u8,253u8,86u8,213u8,187u8, - 174u8,229u8,217u8,134u8,190u8,69u8,220u8,205u8,196u8,116u8,124u8,65u8,227u8,81u8,82u8,208u8,22u8,103u8,51u8,104u8, - 245u8,54u8,245u8,1u8,114u8,164u8,254u8,84u8,161u8,55u8,224u8,77u8,250u8,39u8,238u8,164u8,7u8,122u8,253u8,99u8, - 119u8,232u8,161u8,30u8,250u8,197u8,4u8,2u8,63u8,151u8,20u8,207u8,3u8,136u8,127u8,15u8,3u8,40u8,209u8,162u8, - 126u8,16u8,130u8,134u8,69u8,2u8,116u8,12u8,179u8,58u8,72u8,28u8,112u8,18u8,131u8,14u8,18u8,48u8,6u8,179u8, - 58u8,72u8,12u8,66u8,36u8,164u8,98u8,122u8,245u8,96u8,91u8,157u8,92u8,75u8,19u8,98u8,232u8,201u8,198u8,33u8, - 225u8,201u8,58u8,47u8,49u8,216u8,214u8,77u8,24u8,196u8,15u8,30u8,173u8,163u8,126u8,176u8,45u8,245u8,235u8,104u8, - 228u8,224u8,145u8,173u8,146u8,118u8,105u8,248u8,7u8,214u8,145u8,72u8,71u8,171u8,129u8,200u8,189u8,131u8,126u8,122u8, - 163u8,119u8,198u8,219u8,64u8,65u8,120u8,137u8,24u8,146u8,166u8,88u8,86u8,215u8,235u8,178u8,129u8,193u8,96u8,139u8, - 116u8,96u8,112u8,211u8,49u8,194u8,241u8,106u8,24u8,133u8,90u8,7u8,159u8,118u8,140u8,253u8,210u8,49u8,246u8,164u8, - 99u8,236u8,113u8,199u8,216u8,163u8,142u8,177u8,135u8,29u8,99u8,15u8,58u8,198u8,14u8,58u8,198u8,6u8,29u8,99u8, - 251u8,93u8,168u8,21u8,145u8,81u8,122u8,51u8,91u8,151u8,126u8,61u8,186u8,149u8,122u8,121u8,37u8,2u8,55u8,133u8, - 131u8,125u8,114u8,247u8,200u8,143u8,147u8,97u8,192u8,55u8,233u8,80u8,16u8,243u8,156u8,174u8,186u8,181u8,244u8,107u8, - 61u8,7u8,32u8,91u8,88u8,201u8,198u8,70u8,194u8,227u8,233u8,255u8,181u8,89u8,180u8,111u8,246u8,200u8,219u8,171u8, - 185u8,67u8,169u8,121u8,67u8,10u8,108u8,8u8,210u8,171u8,155u8,218u8,47u8,192u8,92u8,80u8,227u8,34u8,148u8,137u8, - 173u8,175u8,237u8,231u8,54u8,214u8,160u8,74u8,179u8,111u8,84u8,139u8,215u8,72u8,128u8,173u8,6u8,203u8,216u8,169u8, - 0u8,228u8,26u8,175u8,10u8,80u8,242u8,227u8,235u8,240u8,221u8,1u8,172u8,252u8,177u8,248u8,176u8,177u8,220u8,184u8, - 84u8,98u8,213u8,150u8,104u8,23u8,28u8,254u8,45u8,241u8,49u8,233u8,13u8,66u8,110u8,231u8,174u8,238u8,106u8,224u8, - 36u8,81u8,232u8,244u8,6u8,248u8,199u8,82u8,216u8,88u8,235u8,7u8,179u8,25u8,47u8,205u8,188u8,186u8,243u8,63u8, - 39u8,36u8,130u8,175u8,116u8,63u8,0u8,0u8,0u8,0u8,8u8,98u8,108u8,115u8,49u8,50u8,51u8,56u8,49u8,199u8, - 94u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,125u8,233u8,118u8,28u8,199u8,145u8,238u8,127u8, - 61u8,69u8,153u8,227u8,195u8,1u8,52u8,0u8,152u8,75u8,109u8,73u8,89u8,62u8,71u8,182u8,53u8,26u8,31u8,89u8, - 18u8,175u8,40u8,95u8,31u8,143u8,172u8,219u8,200u8,21u8,104u8,19u8,232u8,198u8,116u8,55u8,68u8,209u8,178u8,222u8, - 253u8,126u8,81u8,85u8,93u8,123u8,245u8,2u8,130u8,146u8,61u8,22u8,77u8,139u8,64u8,119u8,86u8,46u8,177u8,101u8, - 196u8,23u8,145u8,89u8,207u8,158u8,61u8,139u8,126u8,187u8,92u8,108u8,244u8,124u8,177u8,142u8,194u8,253u8,194u8,110u8, - 230u8,75u8,250u8,105u8,185u8,122u8,254u8,222u8,179u8,103u8,207u8,232u8,255u8,17u8,253u8,249u8,234u8,218u8,71u8,183u8, - 243u8,197u8,252u8,246u8,254u8,246u8,252u8,238u8,222u8,188u8,242u8,111u8,206u8,215u8,243u8,191u8,249u8,232u8,91u8,189u8, - 154u8,235u8,197u8,38u8,90u8,134u8,232u8,235u8,223u8,44u8,23u8,254u8,250u8,252u8,15u8,111u8,22u8,139u8,243u8,151u8, - 215u8,218u8,94u8,235u8,219u8,232u8,228u8,55u8,127u8,120u8,121u8,26u8,173u8,231u8,87u8,11u8,189u8,185u8,95u8,249u8, - 245u8,55u8,39u8,215u8,155u8,205u8,221u8,250u8,249u8,179u8,103u8,126u8,113u8,241u8,122u8,254u8,106u8,126u8,231u8,221u8, - 92u8,95u8,44u8,87u8,87u8,207u8,232u8,183u8,103u8,104u8,62u8,115u8,243u8,171u8,249u8,70u8,223u8,204u8,234u8,167u8, - 78u8,207u8,234u8,105u8,188u8,190u8,246u8,43u8,31u8,97u8,2u8,55u8,115u8,27u8,97u8,18u8,235u8,72u8,227u8,119u8, - 60u8,197u8,197u8,185u8,204u8,121u8,228u8,111u8,110u8,230u8,119u8,155u8,185u8,61u8,183u8,247u8,171u8,111u8,209u8,112u8, - 57u8,95u8,108u8,214u8,209u8,124u8,17u8,253u8,242u8,47u8,183u8,122u8,115u8,109u8,204u8,247u8,159u8,252u8,48u8,227u8, - 191u8,140u8,244u8,194u8,181u8,102u8,85u8,116u8,210u8,107u8,36u8,126u8,217u8,140u8,170u8,215u8,209u8,157u8,95u8,69u8, - 27u8,208u8,224u8,235u8,223u8,127u8,252u8,213u8,127u8,210u8,128u8,145u8,91u8,233u8,176u8,137u8,214u8,27u8,116u8,165u8, - 87u8,174u8,89u8,152u8,211u8,27u8,189u8,89u8,105u8,251u8,202u8,175u8,46u8,230u8,126u8,19u8,138u8,197u8,185u8,165u8, - 125u8,118u8,189u8,185u8,189u8,121u8,86u8,60u8,117u8,62u8,95u8,109u8,194u8,185u8,13u8,171u8,171u8,115u8,115u8,179u8, - 62u8,175u8,39u8,242u8,111u8,107u8,95u8,208u8,255u8,92u8,92u8,240u8,211u8,139u8,247u8,222u8,187u8,93u8,186u8,251u8, - 27u8,31u8,233u8,187u8,205u8,114u8,61u8,91u8,111u8,220u8,243u8,231u8,104u8,206u8,5u8,45u8,244u8,251u8,247u8,104u8, - 98u8,247u8,107u8,31u8,21u8,159u8,47u8,239u8,232u8,185u8,231u8,207u8,191u8,127u8,233u8,111u8,194u8,89u8,244u8,69u8, - 241u8,235u8,15u8,31u8,20u8,141u8,254u8,237u8,235u8,141u8,95u8,111u8,102u8,203u8,197u8,205u8,155u8,111u8,186u8,79u8, - 249u8,213u8,10u8,124u8,126u8,62u8,95u8,124u8,171u8,111u8,230u8,110u8,166u8,87u8,87u8,247u8,183u8,126u8,177u8,249u8, - 224u8,189u8,162u8,25u8,173u8,158u8,216u8,94u8,79u8,47u8,34u8,142u8,159u8,17u8,161u8,204u8,27u8,116u8,89u8,52u8, - 178u8,144u8,150u8,77u8,244u8,242u8,247u8,159u8,124u8,254u8,209u8,87u8,127u8,252u8,242u8,227u8,217u8,203u8,223u8,255u8, - 247u8,199u8,207u8,163u8,251u8,52u8,142u8,62u8,140u8,84u8,218u8,235u8,169u8,225u8,219u8,116u8,87u8,47u8,254u8,248u8, - 155u8,63u8,252u8,254u8,183u8,179u8,79u8,63u8,254u8,243u8,236u8,243u8,63u8,126u8,54u8,251u8,205u8,159u8,191u8,250u8, - 248u8,229u8,182u8,195u8,56u8,239u8,117u8,104u8,245u8,205u8,13u8,24u8,243u8,26u8,12u8,90u8,223u8,223u8,221u8,45u8, - 215u8,222u8,69u8,155u8,37u8,186u8,189u8,187u8,135u8,72u8,46u8,124u8,180u8,92u8,69u8,183u8,203u8,174u8,196u8,92u8, - 180u8,6u8,251u8,248u8,191u8,63u8,254u8,242u8,139u8,25u8,134u8,196u8,112u8,245u8,40u8,188u8,53u8,200u8,23u8,212u8, - 71u8,40u8,56u8,127u8,53u8,255u8,214u8,47u8,202u8,174u8,215u8,209u8,53u8,70u8,164u8,15u8,95u8,175u8,150u8,139u8, - 171u8,98u8,49u8,23u8,237u8,69u8,124u8,252u8,167u8,47u8,191u8,248u8,252u8,147u8,14u8,49u8,68u8,111u8,234u8,139u8, - 251u8,91u8,131u8,169u8,163u8,115u8,162u8,175u8,95u8,173u8,35u8,183u8,132u8,36u8,46u8,150u8,155u8,8u8,66u8,104u8, - 175u8,139u8,238u8,155u8,70u8,183u8,126u8,189u8,214u8,87u8,104u8,128u8,229u8,153u8,146u8,39u8,222u8,117u8,150u8,82u8, - 144u8,139u8,24u8,241u8,241u8,151u8,47u8,103u8,159u8,253u8,241u8,229u8,87u8,179u8,143u8,255u8,79u8,241u8,217u8,103u8, - 31u8,191u8,124u8,249u8,209u8,39u8,13u8,21u8,101u8,61u8,147u8,232u8,171u8,47u8,126u8,247u8,197u8,243u8,232u8,133u8, - 95u8,65u8,223u8,111u8,245u8,194u8,98u8,65u8,203u8,251u8,27u8,135u8,85u8,218u8,149u8,215u8,16u8,147u8,57u8,166u8, - 183u8,89u8,221u8,219u8,82u8,137u8,54u8,215u8,243u8,117u8,180u8,21u8,74u8,82u8,153u8,219u8,187u8,27u8,79u8,18u8, - 3u8,178u8,67u8,68u8,64u8,160u8,155u8,55u8,209u8,183u8,115u8,13u8,242u8,44u8,220u8,13u8,38u8,123u8,178u8,158u8, - 223u8,206u8,111u8,244u8,138u8,102u8,253u8,149u8,54u8,244u8,216u8,194u8,109u8,7u8,255u8,114u8,142u8,174u8,253u8,102u8, - 179u8,124u8,65u8,90u8,122u8,122u8,1u8,178u8,160u8,251u8,215u8,243u8,155u8,155u8,72u8,127u8,187u8,156u8,187u8,232u8, - 126u8,177u8,240u8,150u8,214u8,189u8,122u8,19u8,157u8,56u8,127u8,186u8,246u8,176u8,53u8,55u8,243u8,191u8,105u8,18u8, - 238u8,139u8,232u8,79u8,219u8,217u8,46u8,124u8,201u8,118u8,8u8,195u8,242u8,53u8,230u8,187u8,92u8,129u8,82u8,21u8, - 219u8,10u8,81u8,47u8,23u8,176u8,89u8,46u8,47u8,222u8,27u8,215u8,136u8,178u8,73u8,244u8,210u8,99u8,217u8,155u8, - 79u8,33u8,158u8,196u8,95u8,187u8,188u8,123u8,115u8,6u8,53u8,95u8,222u8,85u8,218u8,70u8,127u8,10u8,113u8,125u8, - 30u8,125u8,11u8,69u8,93u8,174u8,126u8,117u8,159u8,255u8,250u8,172u8,248u8,234u8,135u8,134u8,185u8,31u8,69u8,239u8, - 23u8,234u8,164u8,65u8,149u8,247u8,219u8,34u8,191u8,185u8,214u8,155u8,231u8,117u8,179u8,40u8,58u8,225u8,167u8,17u8, - 22u8,172u8,75u8,43u8,85u8,210u8,23u8,146u8,186u8,154u8,223u8,250u8,243u8,229u8,202u8,129u8,239u8,235u8,123u8,115u8, - 181u8,90u8,222u8,223u8,109u8,69u8,112u8,104u8,231u8,162u8,194u8,206u8,157u8,181u8,200u8,90u8,244u8,44u8,138u8,158u8, - 73u8,150u8,232u8,177u8,185u8,3u8,139u8,230u8,155u8,55u8,229u8,64u8,219u8,118u8,45u8,105u8,68u8,219u8,138u8,2u8, - 86u8,47u8,72u8,188u8,238u8,43u8,69u8,250u8,22u8,20u8,15u8,111u8,48u8,197u8,5u8,201u8,199u8,77u8,116u8,178u8, - 128u8,113u8,210u8,87u8,87u8,43u8,127u8,69u8,203u8,107u8,89u8,246u8,139u8,67u8,186u8,181u8,203u8,91u8,51u8,135u8, - 220u8,130u8,201u8,155u8,107u8,244u8,250u8,98u8,181u8,92u8,134u8,47u8,194u8,139u8,229u8,122u8,13u8,54u8,131u8,169u8, - 219u8,246u8,160u8,69u8,73u8,1u8,204u8,129u8,36u8,113u8,227u8,169u8,117u8,65u8,74u8,112u8,231u8,79u8,120u8,250u8, - 5u8,184u8,82u8,54u8,62u8,195u8,166u8,48u8,183u8,215u8,245u8,184u8,83u8,75u8,184u8,189u8,191u8,217u8,204u8,123u8, - 243u8,173u8,134u8,171u8,123u8,238u8,241u8,253u8,172u8,16u8,38u8,191u8,139u8,253u8,67u8,238u8,223u8,209u8,162u8,206u8, - 241u8,247u8,174u8,89u8,214u8,201u8,139u8,229u8,139u8,211u8,154u8,68u8,209u8,39u8,133u8,41u8,89u8,223u8,91u8,34u8, - 66u8,53u8,7u8,218u8,145u8,90u8,107u8,172u8,23u8,71u8,86u8,140u8,150u8,84u8,104u8,121u8,217u8,178u8,79u8,137u8, - 23u8,208u8,51u8,239u8,177u8,104u8,136u8,255u8,105u8,119u8,97u8,3u8,2u8,63u8,194u8,2u8,119u8,136u8,55u8,186u8, - 167u8,85u8,96u8,101u8,164u8,183u8,225u8,254u8,230u8,230u8,205u8,121u8,193u8,128u8,57u8,152u8,177u8,159u8,48u8,173u8, - 65u8,202u8,9u8,12u8,149u8,184u8,226u8,174u8,199u8,194u8,253u8,170u8,175u8,77u8,125u8,158u8,47u8,162u8,90u8,86u8, - 27u8,73u8,237u8,233u8,73u8,35u8,205u8,165u8,84u8,46u8,169u8,227u8,33u8,133u8,27u8,43u8,72u8,134u8,102u8,17u8, - 125u8,132u8,199u8,234u8,70u8,235u8,170u8,85u8,37u8,138u8,133u8,42u8,223u8,175u8,22u8,109u8,81u8,108u8,141u8,74u8, - 127u8,166u8,5u8,115u8,84u8,46u8,183u8,179u8,120u8,123u8,238u8,53u8,36u8,33u8,242u8,131u8,188u8,109u8,95u8,170u8, - 32u8,64u8,205u8,47u8,12u8,184u8,222u8,174u8,104u8,92u8,169u8,186u8,51u8,95u8,119u8,68u8,111u8,148u8,64u8,143u8, - 33u8,126u8,228u8,123u8,53u8,106u8,92u8,154u8,153u8,142u8,84u8,68u8,186u8,47u8,23u8,123u8,173u8,87u8,79u8,38u8, - 90u8,30u8,207u8,53u8,237u8,115u8,39u8,216u8,30u8,7u8,140u8,34u8,223u8,98u8,68u8,188u8,78u8,59u8,27u8,74u8, - 221u8,248u8,29u8,112u8,174u8,67u8,136u8,245u8,1u8,148u8,136u8,59u8,210u8,222u8,44u8,228u8,100u8,126u8,225u8,47u8, - 206u8,218u8,234u8,82u8,141u8,208u8,242u8,144u8,215u8,179u8,249u8,89u8,228u8,225u8,212u8,195u8,32u8,17u8,41u8,74u8, - 119u8,36u8,186u8,157u8,205u8,79u8,219u8,67u8,200u8,211u8,62u8,149u8,142u8,238u8,156u8,118u8,172u8,181u8,190u8,245u8, - 205u8,24u8,167u8,3u8,251u8,128u8,173u8,223u8,193u8,123u8,152u8,47u8,174u8,238u8,231u8,235u8,107u8,44u8,120u8,243u8, - 218u8,123u8,154u8,87u8,67u8,240u8,205u8,155u8,59u8,95u8,25u8,85u8,18u8,197u8,47u8,86u8,159u8,209u8,188u8,122u8, - 223u8,67u8,148u8,239u8,86u8,30u8,198u8,120u8,19u8,57u8,252u8,115u8,179u8,188u8,35u8,71u8,44u8,172u8,150u8,183u8, - 164u8,232u8,126u8,133u8,24u8,102u8,113u8,229u8,225u8,184u8,188u8,105u8,109u8,47u8,216u8,122u8,225u8,243u8,93u8,150u8, - 58u8,48u8,235u8,46u8,247u8,178u8,24u8,114u8,251u8,93u8,253u8,241u8,172u8,144u8,163u8,203u8,150u8,234u8,24u8,216u8, - 153u8,190u8,254u8,116u8,163u8,146u8,82u8,246u8,214u8,103u8,245u8,200u8,149u8,38u8,22u8,46u8,79u8,181u8,41u8,194u8, - 164u8,194u8,169u8,186u8,37u8,223u8,241u8,117u8,73u8,57u8,204u8,205u8,15u8,244u8,112u8,176u8,248u8,183u8,150u8,198u8, - 223u8,22u8,227u8,147u8,243u8,178u8,240u8,175u8,219u8,187u8,64u8,65u8,59u8,236u8,0u8,254u8,127u8,238u8,61u8,249u8, - 147u8,96u8,116u8,209u8,89u8,57u8,167u8,170u8,33u8,226u8,204u8,234u8,199u8,25u8,158u8,153u8,209u8,51u8,179u8,162u8, - 213u8,201u8,96u8,224u8,211u8,231u8,85u8,48u8,243u8,171u8,218u8,152u8,252u8,186u8,53u8,81u8,248u8,167u8,39u8,219u8, - 237u8,104u8,86u8,198u8,165u8,179u8,130u8,115u8,11u8,125u8,83u8,118u8,118u8,122u8,218u8,106u8,78u8,127u8,182u8,161u8, - 210u8,122u8,121u8,235u8,79u8,154u8,253u8,182u8,219u8,168u8,38u8,66u8,231u8,211u8,31u8,78u8,235u8,95u8,127u8,128u8, - 11u8,182u8,246u8,19u8,93u8,195u8,206u8,248u8,214u8,124u8,79u8,90u8,143u8,245u8,9u8,249u8,178u8,242u8,105u8,11u8, - 90u8,182u8,232u8,88u8,236u8,51u8,113u8,190u8,159u8,120u8,155u8,101u8,69u8,186u8,187u8,87u8,207u8,163u8,167u8,245u8, - 160u8,167u8,109u8,34u8,182u8,166u8,121u8,247u8,234u8,162u8,89u8,215u8,52u8,67u8,167u8,182u8,234u8,238u8,118u8,112u8, - 131u8,71u8,86u8,245u8,166u8,48u8,229u8,168u8,109u8,55u8,207u8,179u8,193u8,42u8,104u8,148u8,25u8,254u8,54u8,163u8, - 236u8,147u8,133u8,161u8,67u8,211u8,44u8,109u8,215u8,119u8,67u8,126u8,238u8,226u8,196u8,166u8,19u8,245u8,22u8,172u8, - 80u8,233u8,20u8,43u8,70u8,22u8,209u8,240u8,100u8,121u8,71u8,76u8,233u8,79u8,108u8,146u8,57u8,203u8,187u8,125u8, - 220u8,1u8,57u8,255u8,221u8,141u8,40u8,92u8,181u8,197u8,181u8,190u8,40u8,109u8,159u8,93u8,174u8,96u8,68u8,238u8, - 150u8,11u8,71u8,70u8,107u8,132u8,175u8,135u8,41u8,230u8,140u8,92u8,3u8,44u8,241u8,14u8,98u8,54u8,27u8,198u8, - 64u8,209u8,244u8,66u8,251u8,218u8,91u8,201u8,196u8,64u8,137u8,75u8,139u8,57u8,70u8,205u8,90u8,159u8,183u8,67u8, - 159u8,53u8,132u8,58u8,80u8,187u8,183u8,130u8,56u8,161u8,228u8,207u8,163u8,109u8,215u8,143u8,160u8,238u8,219u8,5u8, - 238u8,210u8,250u8,150u8,182u8,13u8,248u8,86u8,49u8,180u8,207u8,232u8,93u8,124u8,218u8,50u8,135u8,4u8,175u8,236u8, - 16u8,180u8,170u8,88u8,210u8,155u8,22u8,233u8,208u8,136u8,205u8,155u8,178u8,131u8,53u8,121u8,186u8,162u8,185u8,223u8, - 148u8,13u8,228u8,244u8,96u8,131u8,214u8,94u8,204u8,152u8,101u8,107u8,22u8,242u8,22u8,6u8,174u8,209u8,238u8,201u8, - 13u8,43u8,250u8,221u8,22u8,143u8,177u8,215u8,222u8,190u8,234u8,25u8,5u8,242u8,8u8,199u8,66u8,245u8,122u8,188u8, - 91u8,79u8,208u8,205u8,250u8,122u8,142u8,56u8,117u8,78u8,29u8,23u8,241u8,209u8,156u8,182u8,234u8,69u8,137u8,156u8, - 204u8,237u8,124u8,3u8,188u8,196u8,221u8,175u8,72u8,53u8,75u8,183u8,219u8,150u8,216u8,70u8,159u8,60u8,141u8,35u8, - 177u8,199u8,62u8,54u8,91u8,124u8,67u8,145u8,177u8,207u8,222u8,157u8,61u8,108u8,166u8,90u8,243u8,15u8,31u8,129u8, - 129u8,245u8,52u8,166u8,56u8,135u8,102u8,19u8,172u8,35u8,250u8,175u8,75u8,2u8,22u8,72u8,92u8,129u8,137u8,248u8, - 18u8,122u8,42u8,63u8,118u8,62u8,192u8,241u8,33u8,238u8,182u8,230u8,185u8,222u8,133u8,169u8,92u8,116u8,129u8,138u8, - 146u8,199u8,244u8,72u8,195u8,153u8,187u8,18u8,23u8,163u8,200u8,240u8,26u8,222u8,101u8,105u8,162u8,136u8,87u8,122u8, - 241u8,166u8,53u8,14u8,1u8,94u8,45u8,100u8,236u8,44u8,50u8,0u8,31u8,95u8,195u8,7u8,255u8,142u8,0u8,201u8, - 242u8,171u8,45u8,178u8,14u8,130u8,110u8,26u8,95u8,114u8,78u8,33u8,34u8,161u8,108u8,192u8,252u8,230u8,87u8,215u8, - 155u8,42u8,196u8,66u8,220u8,92u8,200u8,151u8,190u8,163u8,169u8,232u8,18u8,145u8,7u8,205u8,129u8,200u8,205u8,73u8, - 96u8,230u8,235u8,219u8,249u8,154u8,22u8,87u8,120u8,62u8,109u8,71u8,218u8,235u8,21u8,26u8,236u8,20u8,160u8,237u8, - 250u8,103u8,197u8,138u8,79u8,234u8,47u8,122u8,28u8,50u8,203u8,229u8,77u8,151u8,55u8,163u8,207u8,55u8,198u8,185u8, - 9u8,201u8,74u8,195u8,220u8,103u8,98u8,137u8,121u8,232u8,86u8,96u8,191u8,47u8,242u8,172u8,208u8,162u8,66u8,252u8, - 110u8,235u8,192u8,251u8,253u8,38u8,132u8,233u8,192u8,15u8,131u8,56u8,181u8,30u8,121u8,50u8,94u8,69u8,179u8,125u8, - 158u8,252u8,72u8,128u8,215u8,127u8,172u8,110u8,210u8,184u8,251u8,151u8,141u8,112u8,125u8,100u8,150u8,43u8,130u8,13u8, - 2u8,44u8,201u8,32u8,111u8,81u8,66u8,202u8,122u8,93u8,162u8,202u8,3u8,166u8,53u8,61u8,151u8,158u8,45u8,44u8, - 97u8,109u8,38u8,27u8,197u8,31u8,108u8,60u8,224u8,222u8,120u8,240u8,221u8,176u8,243u8,198u8,111u8,162u8,147u8,106u8, - 47u8,173u8,192u8,154u8,83u8,32u8,195u8,131u8,1u8,91u8,123u8,111u8,51u8,242u8,233u8,7u8,117u8,63u8,26u8,155u8, - 244u8,106u8,243u8,139u8,147u8,170u8,143u8,179u8,157u8,105u8,133u8,147u8,14u8,218u8,126u8,122u8,90u8,193u8,208u8,244u8, - 103u8,223u8,124u8,143u8,51u8,87u8,29u8,220u8,231u8,224u8,45u8,168u8,191u8,248u8,198u8,126u8,233u8,98u8,3u8,26u8, - 157u8,227u8,148u8,45u8,211u8,83u8,219u8,208u8,71u8,219u8,81u8,74u8,171u8,90u8,102u8,42u8,90u8,178u8,181u8,21u8, - 243u8,122u8,54u8,48u8,91u8,231u8,133u8,100u8,158u8,183u8,34u8,195u8,194u8,159u8,198u8,79u8,103u8,99u8,254u8,120u8, - 173u8,70u8,48u8,76u8,245u8,184u8,187u8,164u8,149u8,144u8,140u8,9u8,37u8,184u8,136u8,190u8,244u8,4u8,101u8,173u8, - 163u8,203u8,207u8,177u8,111u8,93u8,146u8,32u8,255u8,205u8,175u8,150u8,253u8,228u8,89u8,87u8,146u8,235u8,81u8,209u8, - 49u8,101u8,16u8,224u8,139u8,109u8,33u8,236u8,222u8,115u8,180u8,189u8,150u8,86u8,172u8,99u8,212u8,119u8,113u8,167u8, - 233u8,161u8,177u8,57u8,141u8,54u8,212u8,54u8,172u8,21u8,57u8,142u8,133u8,192u8,191u8,62u8,70u8,27u8,154u8,113u8, - 70u8,236u8,93u8,91u8,31u8,200u8,147u8,173u8,123u8,216u8,225u8,152u8,14u8,220u8,208u8,209u8,40u8,125u8,232u8,173u8, - 142u8,135u8,165u8,141u8,62u8,108u8,255u8,28u8,227u8,184u8,142u8,82u8,231u8,208u8,144u8,117u8,143u8,160u8,238u8,118u8, - 21u8,232u8,209u8,217u8,114u8,85u8,202u8,220u8,108u8,210u8,113u8,24u8,155u8,224u8,209u8,62u8,196u8,239u8,252u8,250u8, - 240u8,105u8,23u8,62u8,225u8,209u8,211u8,222u8,227u8,154u8,237u8,97u8,113u8,109u8,74u8,201u8,130u8,150u8,15u8,62u8, - 127u8,126u8,227u8,23u8,87u8,155u8,235u8,147u8,167u8,229u8,150u8,26u8,125u8,248u8,97u8,47u8,211u8,186u8,207u8,224u8, - 54u8,105u8,200u8,129u8,185u8,221u8,35u8,109u8,19u8,214u8,246u8,65u8,62u8,217u8,62u8,33u8,57u8,192u8,79u8,59u8, - 128u8,252u8,59u8,28u8,155u8,9u8,9u8,122u8,116u8,31u8,231u8,255u8,150u8,150u8,119u8,61u8,145u8,121u8,24u8,67u8, - 65u8,111u8,59u8,190u8,36u8,204u8,44u8,76u8,204u8,101u8,27u8,105u8,117u8,243u8,16u8,252u8,138u8,136u8,186u8,197u8, - 67u8,47u8,1u8,186u8,94u8,14u8,200u8,50u8,109u8,223u8,27u8,107u8,83u8,144u8,110u8,90u8,169u8,206u8,154u8,32u8, - 234u8,16u8,63u8,163u8,105u8,190u8,77u8,78u8,215u8,109u8,27u8,193u8,175u8,90u8,13u8,169u8,61u8,61u8,223u8,134u8, - 222u8,219u8,249u8,94u8,108u8,195u8,254u8,102u8,90u8,103u8,245u8,160u8,59u8,184u8,208u8,115u8,248u8,158u8,239u8,167u8, - 255u8,14u8,24u8,250u8,242u8,118u8,146u8,232u8,221u8,97u8,26u8,122u8,111u8,63u8,223u8,75u8,239u8,98u8,161u8,205u8, - 234u8,166u8,188u8,141u8,1u8,197u8,7u8,16u8,237u8,36u8,157u8,187u8,83u8,108u8,72u8,188u8,253u8,124u8,75u8,226u8, - 222u8,76u8,182u8,31u8,87u8,227u8,237u8,162u8,117u8,9u8,60u8,156u8,69u8,221u8,36u8,75u8,63u8,67u8,60u8,36u8, - 94u8,249u8,220u8,152u8,184u8,142u8,198u8,38u8,99u8,66u8,218u8,134u8,7u8,222u8,130u8,70u8,253u8,153u8,76u8,42u8, - 126u8,91u8,18u8,143u8,160u8,80u8,63u8,173u8,84u8,89u8,189u8,94u8,194u8,164u8,252u8,142u8,194u8,191u8,81u8,35u8, - 82u8,126u8,63u8,69u8,204u8,94u8,198u8,225u8,100u8,204u8,184u8,209u8,23u8,199u8,80u8,244u8,237u8,133u8,175u8,55u8, - 248u8,8u8,93u8,203u8,47u8,142u8,163u8,238u8,160u8,138u8,163u8,8u8,51u8,253u8,194u8,175u8,42u8,160u8,135u8,18u8, - 100u8,84u8,168u8,118u8,167u8,231u8,171u8,231u8,5u8,198u8,67u8,229u8,29u8,101u8,180u8,72u8,241u8,230u8,156u8,242u8, - 201u8,93u8,124u8,116u8,26u8,110u8,187u8,170u8,58u8,46u8,172u8,207u8,9u8,150u8,122u8,82u8,87u8,139u8,156u8,13u8, - 16u8,239u8,211u8,190u8,119u8,185u8,110u8,160u8,203u8,234u8,39u8,242u8,48u8,59u8,125u8,54u8,84u8,105u8,57u8,148u8, - 244u8,240u8,250u8,21u8,154u8,54u8,165u8,41u8,163u8,0u8,221u8,186u8,143u8,95u8,254u8,208u8,237u8,163u8,0u8,240u8, - 208u8,205u8,30u8,68u8,116u8,10u8,13u8,109u8,245u8,134u8,165u8,156u8,149u8,221u8,29u8,207u8,140u8,46u8,124u8,214u8, - 100u8,17u8,171u8,114u8,16u8,250u8,154u8,184u8,48u8,70u8,126u8,250u8,14u8,142u8,141u8,153u8,111u8,86u8,122u8,245u8, - 166u8,241u8,15u8,233u8,129u8,74u8,96u8,91u8,252u8,24u8,145u8,208u8,135u8,128u8,99u8,207u8,203u8,97u8,59u8,226u8, - 90u8,141u8,55u8,42u8,152u8,3u8,119u8,121u8,47u8,85u8,6u8,158u8,231u8,144u8,46u8,69u8,147u8,187u8,27u8,223u8, - 38u8,207u8,208u8,41u8,109u8,188u8,161u8,93u8,84u8,194u8,138u8,158u8,110u8,3u8,165u8,45u8,181u8,126u8,61u8,69u8, - 174u8,61u8,94u8,34u8,137u8,213u8,2u8,34u8,53u8,230u8,176u8,182u8,135u8,236u8,75u8,243u8,252u8,106u8,141u8,167u8, - 202u8,7u8,190u8,254u8,166u8,251u8,229u8,124u8,91u8,163u8,198u8,154u8,207u8,17u8,225u8,222u8,80u8,26u8,57u8,250u8, - 85u8,180u8,232u8,7u8,85u8,85u8,135u8,52u8,139u8,209u8,149u8,183u8,167u8,6u8,32u8,102u8,181u8,124u8,221u8,153u8, - 218u8,89u8,52u8,63u8,109u8,88u8,248u8,65u8,167u8,235u8,206u8,179u8,119u8,247u8,235u8,235u8,153u8,209u8,112u8,43u8, - 159u8,222u8,150u8,241u8,58u8,133u8,138u8,243u8,171u8,222u8,51u8,115u8,76u8,100u8,30u8,253u8,7u8,85u8,16u8,78u8, - 232u8,225u8,214u8,214u8,79u8,132u8,151u8,52u8,187u8,54u8,193u8,182u8,129u8,154u8,255u8,142u8,42u8,74u8,55u8,229u8, - 232u8,219u8,62u8,142u8,210u8,191u8,197u8,120u8,234u8,127u8,249u8,45u8,85u8,9u8,220u8,220u8,52u8,245u8,133u8,216u8, - 148u8,182u8,63u8,159u8,85u8,229u8,182u8,109u8,146u8,125u8,61u8,255u8,166u8,248u8,125u8,93u8,183u8,194u8,39u8,227u8, - 62u8,250u8,219u8,9u8,99u8,235u8,219u8,150u8,55u8,121u8,152u8,88u8,182u8,6u8,153u8,33u8,95u8,14u8,223u8,249u8, - 104u8,49u8,173u8,102u8,177u8,243u8,241u8,218u8,3u8,29u8,1u8,196u8,134u8,51u8,248u8,176u8,219u8,231u8,89u8,52u8, - 12u8,215u8,246u8,150u8,112u8,158u8,62u8,162u8,50u8,13u8,166u8,248u8,78u8,148u8,235u8,253u8,177u8,102u8,141u8,128u8, - 205u8,79u8,127u8,20u8,181u8,219u8,202u8,227u8,219u8,168u8,221u8,182u8,143u8,253u8,106u8,183u8,133u8,204u8,96u8,200u8, - 53u8,18u8,2u8,174u8,40u8,181u8,160u8,96u8,67u8,23u8,219u8,10u8,128u8,175u8,149u8,30u8,238u8,112u8,212u8,182u8, - 11u8,31u8,60u8,237u8,152u8,227u8,81u8,188u8,163u8,48u8,194u8,254u8,117u8,249u8,28u8,86u8,247u8,126u8,241u8,67u8, - 119u8,241u8,97u8,190u8,194u8,20u8,139u8,145u8,123u8,114u8,92u8,178u8,3u8,177u8,65u8,181u8,196u8,186u8,167u8,179u8, - 136u8,181u8,200u8,241u8,126u8,167u8,135u8,246u8,111u8,255u8,47u8,98u8,223u8,133u8,208u8,180u8,172u8,59u8,120u8,27u8, - 26u8,85u8,41u8,209u8,233u8,192u8,161u8,160u8,84u8,19u8,47u8,12u8,211u8,75u8,15u8,216u8,232u8,91u8,212u8,127u8, - 58u8,142u8,68u8,157u8,30u8,179u8,199u8,79u8,47u8,111u8,202u8,16u8,175u8,134u8,222u8,192u8,248u8,194u8,39u8,240u8, - 143u8,221u8,96u8,217u8,30u8,163u8,121u8,40u8,32u8,212u8,165u8,211u8,33u8,248u8,221u8,35u8,81u8,109u8,152u8,39u8, - 31u8,39u8,78u8,19u8,57u8,12u8,235u8,97u8,30u8,144u8,247u8,110u8,175u8,118u8,188u8,230u8,230u8,177u8,214u8,183u8, - 55u8,221u8,223u8,91u8,94u8,187u8,32u8,99u8,79u8,154u8,127u8,232u8,234u8,31u8,22u8,5u8,76u8,44u8,126u8,44u8, - 63u8,255u8,46u8,116u8,99u8,31u8,45u8,154u8,166u8,3u8,174u8,79u8,165u8,106u8,246u8,165u8,153u8,14u8,78u8,67u8, - 13u8,245u8,96u8,60u8,115u8,244u8,120u8,242u8,113u8,72u8,229u8,78u8,41u8,34u8,195u8,10u8,154u8,29u8,101u8,72u8, - 111u8,93u8,79u8,213u8,19u8,147u8,61u8,197u8,80u8,99u8,212u8,56u8,44u8,80u8,154u8,174u8,72u8,163u8,144u8,105u8, - 211u8,61u8,92u8,4u8,47u8,108u8,105u8,231u8,77u8,77u8,119u8,129u8,229u8,53u8,193u8,255u8,229u8,250u8,213u8,229u8, - 116u8,124u8,63u8,70u8,192u8,245u8,171u8,118u8,124u8,249u8,56u8,116u8,219u8,53u8,96u8,43u8,228u8,124u8,213u8,198u8, - 154u8,71u8,242u8,49u8,219u8,211u8,52u8,159u8,23u8,231u8,111u8,154u8,35u8,124u8,219u8,175u8,27u8,196u8,254u8,203u8, - 63u8,191u8,248u8,234u8,139u8,79u8,190u8,252u8,232u8,197u8,127u8,253u8,57u8,250u8,211u8,71u8,95u8,126u8,254u8,251u8, - 207u8,63u8,121u8,94u8,22u8,63u8,108u8,31u8,34u8,194u8,193u8,21u8,109u8,193u8,250u8,213u8,1u8,171u8,58u8,165u8, - 72u8,161u8,66u8,59u8,139u8,125u8,173u8,191u8,165u8,138u8,191u8,194u8,145u8,173u8,71u8,234u8,241u8,170u8,97u8,85u8, - 55u8,113u8,62u8,178u8,238u8,203u8,225u8,81u8,128u8,97u8,221u8,64u8,157u8,200u8,105u8,219u8,136u8,201u8,122u8,129u8, - 81u8,139u8,114u8,22u8,173u8,10u8,85u8,43u8,166u8,179u8,77u8,253u8,33u8,169u8,234u8,79u8,47u8,219u8,245u8,182u8, - 20u8,252u8,92u8,22u8,223u8,94u8,86u8,37u8,178u8,165u8,36u8,141u8,140u8,255u8,22u8,25u8,255u8,69u8,205u8,185u8, - 3u8,19u8,240u8,59u8,83u8,255u8,39u8,237u8,178u8,60u8,130u8,227u8,40u8,241u8,115u8,148u8,16u8,84u8,201u8,228u8, - 181u8,14u8,116u8,156u8,171u8,144u8,128u8,82u8,143u8,150u8,112u8,17u8,155u8,34u8,152u8,90u8,70u8,6u8,57u8,108u8, - 90u8,232u8,54u8,145u8,219u8,164u8,130u8,195u8,17u8,199u8,155u8,138u8,83u8,77u8,71u8,202u8,66u8,27u8,196u8,223u8, - 35u8,10u8,237u8,148u8,204u8,227u8,8u8,194u8,240u8,28u8,212u8,88u8,249u8,216u8,128u8,82u8,139u8,202u8,118u8,33u8, - 81u8,6u8,231u8,102u8,204u8,49u8,92u8,79u8,86u8,154u8,181u8,42u8,204u8,46u8,6u8,59u8,200u8,229u8,201u8,236u8, - 44u8,10u8,26u8,169u8,223u8,211u8,203u8,74u8,20u8,119u8,102u8,236u8,71u8,166u8,173u8,73u8,142u8,119u8,72u8,232u8, - 238u8,164u8,248u8,120u8,66u8,126u8,84u8,56u8,187u8,115u8,143u8,46u8,137u8,252u8,197u8,164u8,137u8,50u8,101u8,96u8, - 3u8,80u8,226u8,178u8,81u8,128u8,203u8,98u8,1u8,149u8,213u8,105u8,73u8,77u8,163u8,108u8,173u8,99u8,15u8,197u8, - 41u8,187u8,162u8,240u8,238u8,243u8,47u8,190u8,26u8,57u8,14u8,215u8,59u8,59u8,87u8,28u8,157u8,219u8,148u8,231u8, - 242u8,166u8,142u8,219u8,141u8,63u8,40u8,155u8,7u8,219u8,236u8,234u8,156u8,185u8,171u8,215u8,88u8,176u8,230u8,178u8, - 60u8,108u8,244u8,122u8,190u8,246u8,7u8,211u8,127u8,178u8,216u8,189u8,141u8,221u8,119u8,226u8,70u8,34u8,243u8,62u8, - 42u8,143u8,173u8,175u8,45u8,222u8,68u8,255u8,166u8,40u8,164u8,79u8,221u8,99u8,72u8,251u8,48u8,186u8,190u8,99u8, - 162u8,30u8,145u8,249u8,221u8,77u8,220u8,113u8,243u8,250u8,159u8,20u8,48u8,159u8,117u8,11u8,1u8,235u8,173u8,118u8, - 215u8,142u8,58u8,185u8,147u8,246u8,138u8,23u8,171u8,51u8,44u8,235u8,242u8,84u8,201u8,26u8,129u8,210u8,205u8,121u8, - 109u8,38u8,244u8,102u8,163u8,41u8,85u8,79u8,229u8,108u8,171u8,229u8,213u8,189u8,63u8,47u8,92u8,164u8,242u8,211u8, - 11u8,74u8,44u8,32u8,249u8,209u8,159u8,221u8,216u8,30u8,208u8,216u8,195u8,233u8,189u8,96u8,44u8,97u8,181u8,59u8, - 191u8,127u8,176u8,241u8,175u8,77u8,91u8,71u8,114u8,199u8,6u8,188u8,196u8,135u8,248u8,237u8,146u8,210u8,184u8,151u8, - 91u8,172u8,233u8,50u8,186u8,95u8,208u8,184u8,45u8,59u8,130u8,207u8,190u8,221u8,102u8,231u8,78u8,42u8,35u8,223u8, - 66u8,53u8,47u8,235u8,161u8,215u8,215u8,197u8,161u8,155u8,250u8,220u8,53u8,172u8,82u8,167u8,31u8,106u8,124u8,186u8, - 99u8,198u8,149u8,96u8,98u8,202u8,253u8,115u8,139u8,231u8,125u8,39u8,129u8,14u8,172u8,111u8,161u8,216u8,161u8,153u8, - 62u8,107u8,63u8,56u8,60u8,27u8,94u8,159u8,32u8,247u8,255u8,115u8,143u8,253u8,164u8,105u8,209u8,26u8,161u8,213u8, - 67u8,77u8,167u8,74u8,139u8,199u8,52u8,24u8,243u8,169u8,244u8,150u8,116u8,124u8,191u8,238u8,82u8,123u8,217u8,180u8, - 111u8,157u8,13u8,155u8,214u8,223u8,125u8,38u8,239u8,128u8,74u8,129u8,118u8,38u8,189u8,0u8,66u8,250u8,7u8,180u8, - 223u8,101u8,85u8,195u8,7u8,15u8,115u8,178u8,187u8,7u8,99u8,215u8,99u8,14u8,243u8,48u8,182u8,89u8,151u8,199u8, - 109u8,230u8,139u8,230u8,64u8,218u8,242u8,22u8,146u8,81u8,106u8,161u8,31u8,45u8,124u8,188u8,232u8,89u8,137u8,163u8, - 12u8,196u8,132u8,84u8,247u8,212u8,144u8,178u8,123u8,151u8,253u8,2u8,218u8,150u8,250u8,181u8,52u8,77u8,95u8,209u8, - 93u8,35u8,155u8,250u8,177u8,209u8,82u8,77u8,18u8,205u8,86u8,88u8,127u8,121u8,49u8,169u8,84u8,15u8,216u8,66u8, - 119u8,87u8,68u8,12u8,170u8,55u8,134u8,134u8,191u8,83u8,184u8,49u8,27u8,223u8,121u8,15u8,78u8,151u8,31u8,41u8, - 61u8,127u8,250u8,253u8,31u8,254u8,80u8,187u8,148u8,149u8,213u8,109u8,215u8,184u8,142u8,110u8,149u8,103u8,157u8,19u8, - 245u8,149u8,32u8,212u8,195u8,222u8,204u8,13u8,225u8,250u8,36u8,89u8,219u8,211u8,143u8,183u8,243u8,117u8,25u8,172u8, - 149u8,103u8,10u8,203u8,239u8,97u8,245u8,32u8,144u8,87u8,126u8,83u8,74u8,219u8,178u8,118u8,70u8,58u8,242u8,105u8, - 124u8,88u8,150u8,134u8,171u8,122u8,254u8,150u8,204u8,215u8,54u8,201u8,65u8,133u8,236u8,77u8,101u8,118u8,123u8,101u8, - 135u8,90u8,252u8,203u8,189u8,18u8,214u8,49u8,244u8,209u8,59u8,18u8,159u8,233u8,98u8,145u8,177u8,250u8,149u8,221u8, - 230u8,232u8,109u8,229u8,230u8,129u8,158u8,115u8,125u8,153u8,76u8,75u8,126u8,78u8,170u8,187u8,110u8,46u8,123u8,142u8, - 230u8,101u8,115u8,204u8,247u8,125u8,24u8,138u8,247u8,75u8,222u8,181u8,30u8,164u8,35u8,165u8,135u8,187u8,45u8,111u8, - 239u8,7u8,31u8,112u8,94u8,236u8,96u8,74u8,247u8,122u8,161u8,220u8,207u8,67u8,149u8,245u8,163u8,26u8,65u8,241u8, - 71u8,83u8,167u8,103u8,166u8,107u8,91u8,92u8,15u8,185u8,181u8,212u8,101u8,157u8,55u8,158u8,171u8,206u8,14u8,53u8, - 174u8,79u8,171u8,186u8,237u8,120u8,125u8,218u8,158u8,82u8,126u8,68u8,173u8,162u8,116u8,93u8,125u8,3u8,204u8,84u8, - 141u8,249u8,145u8,60u8,159u8,172u8,38u8,154u8,46u8,118u8,122u8,119u8,234u8,55u8,0u8,46u8,91u8,243u8,157u8,42u8, - 239u8,233u8,197u8,193u8,173u8,128u8,226u8,128u8,78u8,123u8,21u8,41u8,175u8,186u8,231u8,48u8,111u8,215u8,87u8,189u8, - 8u8,165u8,249u8,229u8,152u8,25u8,239u8,193u8,36u8,247u8,141u8,209u8,192u8,146u8,191u8,165u8,107u8,75u8,52u8,137u8, - 51u8,185u8,23u8,215u8,254u8,166u8,60u8,97u8,79u8,126u8,13u8,102u8,49u8,68u8,41u8,191u8,68u8,51u8,236u8,63u8, - 141u8,68u8,111u8,167u8,84u8,56u8,221u8,171u8,251u8,69u8,9u8,213u8,88u8,236u8,39u8,203u8,162u8,139u8,232u8,252u8, - 188u8,54u8,100u8,179u8,181u8,166u8,27u8,137u8,26u8,9u8,193u8,151u8,139u8,165u8,213u8,119u8,213u8,207u8,243u8,133u8, - 189u8,185u8,119u8,254u8,28u8,95u8,99u8,139u8,114u8,151u8,133u8,117u8,180u8,5u8,196u8,252u8,172u8,184u8,100u8,235u8, - 220u8,174u8,222u8,224u8,223u8,203u8,139u8,206u8,85u8,77u8,219u8,114u8,156u8,202u8,249u8,135u8,142u8,62u8,249u8,47u8, - 120u8,191u8,203u8,232u8,35u8,122u8,230u8,23u8,79u8,138u8,165u8,21u8,206u8,87u8,131u8,59u8,191u8,252u8,148u8,218u8, - 177u8,44u8,230u8,105u8,170u8,164u8,73u8,141u8,20u8,54u8,143u8,181u8,241u8,113u8,146u8,100u8,121u8,38u8,114u8,47u8, - 100u8,166u8,66u8,34u8,146u8,76u8,40u8,159u8,24u8,21u8,103u8,169u8,136u8,101u8,162u8,37u8,247u8,105u8,226u8,173u8, - 69u8,19u8,167u8,219u8,23u8,60u8,125u8,249u8,209u8,231u8,191u8,251u8,162u8,44u8,15u8,40u8,10u8,192u8,59u8,25u8, - 225u8,15u8,163u8,239u8,158u8,104u8,198u8,117u8,154u8,228u8,73u8,28u8,84u8,158u8,57u8,25u8,203u8,152u8,199u8,202u8, - 100u8,129u8,229u8,33u8,99u8,153u8,100u8,94u8,50u8,35u8,98u8,174u8,242u8,216u8,231u8,25u8,23u8,198u8,10u8,109u8, - 117u8,158u8,39u8,46u8,149u8,66u8,235u8,96u8,189u8,139u8,173u8,12u8,105u8,202u8,5u8,83u8,206u8,27u8,204u8,153u8, - 219u8,60u8,101u8,92u8,138u8,52u8,21u8,210u8,90u8,158u8,90u8,45u8,66u8,106u8,149u8,119u8,54u8,145u8,38u8,51u8, - 121u8,110u8,50u8,76u8,57u8,160u8,37u8,75u8,156u8,243u8,214u8,199u8,60u8,119u8,194u8,202u8,216u8,217u8,84u8,83u8, - 227u8,68u8,115u8,174u8,51u8,229u8,211u8,44u8,203u8,226u8,36u8,23u8,150u8,199u8,12u8,84u8,96u8,220u8,89u8,147u8, - 229u8,130u8,249u8,216u8,154u8,216u8,104u8,199u8,188u8,206u8,221u8,147u8,15u8,254u8,177u8,197u8,224u8,199u8,224u8,238u8, - 139u8,79u8,7u8,108u8,205u8,117u8,34u8,125u8,166u8,125u8,34u8,50u8,176u8,208u8,103u8,105u8,98u8,93u8,174u8,99u8, - 134u8,33u8,133u8,207u8,50u8,155u8,134u8,12u8,252u8,212u8,113u8,12u8,150u8,229u8,137u8,9u8,2u8,95u8,186u8,44u8, - 79u8,147u8,36u8,77u8,82u8,149u8,7u8,149u8,37u8,50u8,78u8,51u8,30u8,139u8,84u8,4u8,229u8,99u8,122u8,66u8, - 122u8,230u8,18u8,167u8,18u8,76u8,159u8,61u8,25u8,232u8,236u8,87u8,3u8,205u8,28u8,216u8,140u8,210u8,88u8,108u8, - 102u8,171u8,130u8,79u8,179u8,50u8,234u8,58u8,57u8,226u8,148u8,195u8,94u8,196u8,165u8,47u8,238u8,167u8,103u8,17u8, - 223u8,127u8,134u8,97u8,155u8,92u8,233u8,63u8,189u8,39u8,249u8,214u8,91u8,206u8,221u8,171u8,38u8,189u8,186u8,59u8, - 147u8,186u8,93u8,207u8,36u8,44u8,87u8,243u8,181u8,63u8,255u8,195u8,114u8,176u8,245u8,227u8,187u8,178u8,136u8,205u8, - 26u8,138u8,101u8,85u8,83u8,168u8,166u8,68u8,233u8,171u8,118u8,101u8,15u8,185u8,221u8,164u8,47u8,116u8,85u8,90u8, - 43u8,66u8,0u8,2u8,2u8,45u8,40u8,3u8,66u8,178u8,206u8,152u8,240u8,96u8,145u8,219u8,178u8,152u8,249u8,122u8, - 70u8,39u8,135u8,58u8,89u8,225u8,214u8,225u8,23u8,88u8,34u8,47u8,173u8,83u8,16u8,81u8,147u8,42u8,43u8,152u8, - 102u8,14u8,122u8,17u8,28u8,140u8,137u8,87u8,89u8,22u8,124u8,234u8,61u8,228u8,145u8,105u8,216u8,38u8,197u8,131u8, - 72u8,201u8,204u8,4u8,97u8,181u8,181u8,14u8,146u8,169u8,51u8,157u8,201u8,52u8,201u8,84u8,174u8,45u8,103u8,46u8, - 100u8,89u8,154u8,102u8,137u8,145u8,58u8,205u8,156u8,201u8,131u8,214u8,236u8,201u8,105u8,69u8,205u8,7u8,207u8,80u8, - 65u8,255u8,226u8,60u8,21u8,208u8,163u8,88u8,42u8,244u8,28u8,92u8,150u8,106u8,101u8,179u8,4u8,115u8,20u8,33u8, - 240u8,68u8,65u8,183u8,152u8,179u8,33u8,205u8,243u8,16u8,50u8,35u8,156u8,227u8,52u8,39u8,145u8,196u8,176u8,138u8, - 57u8,22u8,128u8,89u8,11u8,111u8,178u8,204u8,185u8,212u8,163u8,49u8,12u8,128u8,178u8,38u8,112u8,157u8,164u8,58u8, - 205u8,15u8,153u8,97u8,113u8,18u8,108u8,114u8,134u8,70u8,250u8,88u8,9u8,46u8,178u8,76u8,8u8,238u8,25u8,247u8, - 14u8,54u8,59u8,143u8,141u8,79u8,96u8,199u8,227u8,68u8,40u8,97u8,64u8,184u8,44u8,196u8,105u8,162u8,211u8,96u8, - 157u8,33u8,51u8,1u8,237u8,246u8,206u8,73u8,229u8,45u8,84u8,155u8,57u8,45u8,141u8,16u8,10u8,212u8,164u8,117u8, - 101u8,156u8,9u8,24u8,102u8,35u8,83u8,231u8,65u8,246u8,206u8,12u8,71u8,196u8,233u8,223u8,190u8,246u8,223u8,221u8, - 193u8,18u8,33u8,189u8,30u8,244u8,252u8,134u8,42u8,76u8,10u8,175u8,108u8,102u8,151u8,142u8,202u8,128u8,96u8,95u8, - 100u8,118u8,6u8,49u8,42u8,15u8,40u8,23u8,37u8,202u8,55u8,225u8,180u8,39u8,139u8,254u8,246u8,110u8,243u8,102u8, - 43u8,145u8,45u8,231u8,180u8,47u8,146u8,21u8,68u8,122u8,171u8,95u8,97u8,155u8,45u8,64u8,195u8,34u8,103u8,81u8, - 221u8,182u8,88u8,3u8,97u8,103u8,101u8,182u8,100u8,27u8,11u8,175u8,42u8,183u8,147u8,142u8,38u8,182u8,123u8,219u8, - 18u8,187u8,149u8,78u8,91u8,222u8,205u8,182u8,120u8,75u8,125u8,154u8,118u8,91u8,6u8,85u8,76u8,242u8,228u8,180u8, - 56u8,95u8,213u8,62u8,26u8,119u8,210u8,231u8,225u8,224u8,60u8,110u8,167u8,154u8,106u8,219u8,205u8,7u8,135u8,106u8, - 232u8,14u8,122u8,212u8,168u8,44u8,194u8,238u8,226u8,248u8,36u8,61u8,119u8,78u8,135u8,197u8,215u8,227u8,91u8,97u8, - 65u8,151u8,37u8,93u8,128u8,72u8,191u8,1u8,19u8,186u8,37u8,167u8,100u8,106u8,99u8,123u8,222u8,30u8,43u8,250u8, - 101u8,212u8,221u8,71u8,171u8,237u8,179u8,181u8,220u8,87u8,51u8,244u8,86u8,251u8,237u8,187u8,183u8,211u8,94u8,221u8, - 121u8,171u8,62u8,177u8,99u8,224u8,134u8,246u8,111u8,107u8,243u8,72u8,59u8,189u8,128u8,51u8,147u8,179u8,84u8,7u8, - 17u8,167u8,33u8,103u8,9u8,228u8,156u8,5u8,3u8,191u8,34u8,88u8,197u8,72u8,23u8,53u8,92u8,13u8,56u8,25u8, - 214u8,10u8,153u8,122u8,56u8,49u8,18u8,27u8,47u8,100u8,220u8,165u8,240u8,96u8,168u8,133u8,135u8,218u8,192u8,17u8, - 225u8,42u8,81u8,169u8,77u8,114u8,235u8,21u8,249u8,57u8,9u8,28u8,32u8,166u8,159u8,68u8,63u8,156u8,29u8,60u8, - 25u8,109u8,148u8,195u8,12u8,56u8,134u8,86u8,169u8,118u8,158u8,91u8,22u8,43u8,102u8,149u8,204u8,45u8,220u8,172u8, - 196u8,96u8,6u8,216u8,155u8,173u8,53u8,121u8,150u8,230u8,158u8,155u8,196u8,113u8,56u8,89u8,153u8,77u8,124u8,110u8, - 2u8,172u8,129u8,200u8,114u8,105u8,20u8,166u8,198u8,77u8,198u8,96u8,70u8,116u8,12u8,159u8,136u8,107u8,5u8,191u8, - 12u8,59u8,118u8,56u8,110u8,50u8,48u8,168u8,57u8,134u8,22u8,22u8,46u8,157u8,202u8,156u8,82u8,150u8,103u8,134u8, - 12u8,144u8,85u8,206u8,198u8,222u8,27u8,157u8,199u8,24u8,208u8,166u8,130u8,103u8,140u8,233u8,28u8,19u8,247u8,112u8, - 54u8,224u8,202u8,57u8,13u8,95u8,204u8,75u8,109u8,96u8,137u8,177u8,130u8,196u8,196u8,48u8,25u8,206u8,7u8,227u8, - 125u8,96u8,137u8,133u8,231u8,38u8,98u8,21u8,31u8,53u8,25u8,37u8,227u8,12u8,44u8,202u8,105u8,229u8,46u8,206u8, - 180u8,82u8,248u8,71u8,100u8,158u8,39u8,176u8,76u8,68u8,19u8,149u8,8u8,7u8,111u8,135u8,229u8,9u8,172u8,173u8, - 178u8,14u8,30u8,8u8,6u8,204u8,224u8,165u8,164u8,194u8,128u8,70u8,194u8,49u8,144u8,135u8,25u8,50u8,249u8,32u8, - 12u8,199u8,127u8,44u8,220u8,83u8,41u8,181u8,227u8,248u8,231u8,56u8,54u8,197u8,216u8,60u8,48u8,158u8,214u8,78u8, - 106u8,150u8,38u8,220u8,114u8,165u8,114u8,23u8,140u8,166u8,253u8,36u8,206u8,61u8,131u8,107u8,26u8,195u8,153u8,130u8, - 95u8,156u8,37u8,113u8,108u8,2u8,44u8,101u8,2u8,78u8,168u8,12u8,214u8,53u8,133u8,187u8,203u8,192u8,16u8,101u8, - 114u8,248u8,112u8,104u8,31u8,123u8,174u8,153u8,100u8,214u8,194u8,132u8,102u8,14u8,221u8,118u8,38u8,243u8,77u8,107u8, - 35u8,39u8,123u8,67u8,40u8,224u8,171u8,162u8,54u8,250u8,195u8,232u8,47u8,0u8,0u8,102u8,223u8,255u8,53u8,250u8, - 213u8,135u8,209u8,252u8,7u8,186u8,143u8,228u8,235u8,191u8,126u8,211u8,47u8,70u8,157u8,77u8,235u8,197u8,68u8,13u8, - 210u8,79u8,168u8,28u8,123u8,103u8,100u8,50u8,165u8,137u8,255u8,198u8,196u8,49u8,119u8,216u8,129u8,180u8,194u8,190u8, - 195u8,116u8,42u8,82u8,23u8,132u8,143u8,177u8,145u8,113u8,4u8,32u8,185u8,77u8,176u8,151u8,103u8,240u8,133u8,3u8, - 195u8,240u8,82u8,229u8,222u8,234u8,160u8,61u8,3u8,177u8,45u8,131u8,159u8,224u8,18u8,240u8,15u8,243u8,202u8,193u8, - 33u8,175u8,180u8,9u8,22u8,142u8,170u8,213u8,252u8,1u8,51u8,74u8,66u8,226u8,77u8,202u8,19u8,146u8,119u8,153u8, - 231u8,16u8,173u8,44u8,69u8,68u8,132u8,61u8,20u8,179u8,209u8,2u8,251u8,186u8,16u8,177u8,77u8,28u8,195u8,238u8, - 234u8,184u8,7u8,227u8,153u8,77u8,141u8,245u8,8u8,69u8,18u8,155u8,100u8,50u8,132u8,224u8,85u8,158u8,130u8,174u8, - 198u8,197u8,140u8,177u8,20u8,66u8,129u8,29u8,207u8,192u8,165u8,55u8,22u8,62u8,243u8,241u8,51u8,82u8,160u8,8u8, - 75u8,61u8,34u8,32u8,203u8,115u8,240u8,67u8,32u8,244u8,74u8,17u8,112u8,33u8,20u8,74u8,65u8,123u8,103u8,109u8, - 8u8,48u8,10u8,10u8,190u8,16u8,12u8,68u8,166u8,192u8,88u8,3u8,99u8,162u8,149u8,212u8,34u8,78u8,224u8,3u8, - 193u8,193u8,128u8,103u8,146u8,64u8,91u8,181u8,144u8,76u8,38u8,44u8,181u8,177u8,177u8,90u8,233u8,216u8,51u8,19u8, - 30u8,66u8,35u8,233u8,2u8,183u8,193u8,123u8,193u8,211u8,60u8,192u8,225u8,33u8,247u8,6u8,142u8,25u8,122u8,131u8, - 215u8,0u8,7u8,201u8,50u8,236u8,241u8,136u8,87u8,48u8,71u8,132u8,31u8,22u8,145u8,71u8,106u8,193u8,85u8,72u8, - 25u8,23u8,48u8,119u8,34u8,24u8,195u8,224u8,68u8,73u8,50u8,133u8,10u8,189u8,65u8,248u8,224u8,254u8,32u8,222u8, - 179u8,150u8,5u8,57u8,169u8,48u8,69u8,109u8,122u8,183u8,44u8,189u8,80u8,11u8,107u8,239u8,201u8,245u8,238u8,23u8, - 41u8,23u8,219u8,234u8,216u8,245u8,74u8,227u8,85u8,237u8,99u8,199u8,167u8,161u8,109u8,131u8,139u8,162u8,118u8,21u8, - 154u8,111u8,167u8,50u8,81u8,190u8,78u8,253u8,85u8,165u8,235u8,131u8,130u8,121u8,93u8,44u8,96u8,232u8,41u8,108u8, - 187u8,236u8,63u8,3u8,235u8,241u8,89u8,237u8,244u8,188u8,248u8,116u8,29u8,189u8,246u8,45u8,24u8,175u8,40u8,1u8, - 92u8,173u8,48u8,124u8,117u8,3u8,97u8,223u8,157u8,44u8,134u8,251u8,112u8,98u8,154u8,149u8,161u8,41u8,11u8,241u8, - 121u8,175u8,104u8,126u8,111u8,228u8,82u8,95u8,97u8,209u8,143u8,92u8,232u8,207u8,116u8,201u8,253u8,81u8,145u8,201u8, - 172u8,74u8,6u8,205u8,182u8,103u8,33u8,202u8,195u8,117u8,253u8,211u8,115u8,179u8,242u8,204u8,217u8,224u8,200u8,92u8, - 255u8,164u8,220u8,246u8,220u8,26u8,218u8,237u8,44u8,88u8,125u8,90u8,240u8,225u8,129u8,174u8,121u8,125u8,128u8,239u8, - 237u8,98u8,144u8,78u8,217u8,100u8,221u8,229u8,110u8,159u8,188u8,38u8,100u8,19u8,62u8,239u8,247u8,169u8,11u8,175u8, - 174u8,80u8,162u8,54u8,58u8,188u8,127u8,218u8,219u8,51u8,15u8,131u8,195u8,18u8,219u8,211u8,38u8,167u8,125u8,193u8, - 248u8,135u8,243u8,91u8,139u8,227u8,49u8,187u8,157u8,213u8,206u8,228u8,155u8,10u8,41u8,228u8,204u8,139u8,131u8,224u8, - 229u8,221u8,236u8,209u8,124u8,199u8,169u8,155u8,174u8,85u8,25u8,187u8,13u8,2u8,206u8,73u8,146u8,104u8,43u8,92u8, - 26u8,27u8,88u8,127u8,242u8,15u8,13u8,79u8,28u8,121u8,77u8,210u8,39u8,49u8,156u8,29u8,239u8,53u8,203u8,16u8, - 189u8,33u8,176u8,204u8,69u8,158u8,89u8,235u8,51u8,235u8,176u8,249u8,33u8,142u8,75u8,56u8,128u8,51u8,236u8,98u8, - 153u8,74u8,52u8,124u8,38u8,108u8,11u8,150u8,115u8,145u8,40u8,6u8,80u8,7u8,113u8,169u8,205u8,44u8,57u8,48u8, - 44u8,65u8,196u8,157u8,24u8,207u8,130u8,50u8,46u8,139u8,13u8,118u8,13u8,116u8,227u8,226u8,24u8,15u8,99u8,35u8, - 115u8,120u8,194u8,165u8,46u8,177u8,169u8,74u8,128u8,181u8,37u8,30u8,91u8,115u8,154u8,26u8,111u8,100u8,28u8,11u8, - 129u8,93u8,47u8,83u8,92u8,112u8,116u8,109u8,88u8,142u8,153u8,105u8,0u8,121u8,26u8,219u8,31u8,98u8,95u8,199u8, - 164u8,215u8,225u8,201u8,233u8,217u8,33u8,203u8,84u8,216u8,29u8,16u8,77u8,99u8,84u8,197u8,185u8,226u8,54u8,78u8, - 129u8,130u8,193u8,35u8,37u8,4u8,209u8,10u8,37u8,29u8,252u8,207u8,56u8,133u8,199u8,69u8,219u8,176u8,118u8,2u8, - 174u8,26u8,54u8,65u8,33u8,37u8,60u8,49u8,43u8,224u8,129u8,229u8,38u8,141u8,101u8,22u8,180u8,196u8,222u8,18u8, - 148u8,19u8,60u8,55u8,130u8,97u8,147u8,130u8,155u8,156u8,169u8,140u8,193u8,71u8,197u8,94u8,137u8,105u8,195u8,237u8, - 133u8,71u8,39u8,188u8,138u8,129u8,99u8,210u8,143u8,112u8,121u8,8u8,249u8,194u8,190u8,173u8,19u8,110u8,8u8,50u8, - 195u8,86u8,229u8,19u8,44u8,154u8,27u8,184u8,231u8,0u8,231u8,8u8,181u8,140u8,85u8,72u8,139u8,47u8,2u8,203u8, - 172u8,81u8,42u8,128u8,44u8,214u8,32u8,198u8,103u8,240u8,169u8,132u8,49u8,135u8,46u8,19u8,80u8,156u8,203u8,57u8, - 28u8,234u8,4u8,187u8,159u8,15u8,18u8,113u8,54u8,98u8,3u8,227u8,53u8,72u8,12u8,250u8,33u8,42u8,96u8,169u8, - 164u8,157u8,59u8,5u8,178u8,137u8,240u8,33u8,55u8,113u8,136u8,141u8,161u8,120u8,92u8,114u8,0u8,113u8,222u8,130u8, - 45u8,169u8,53u8,9u8,87u8,78u8,11u8,11u8,232u8,129u8,49u8,90u8,74u8,46u8,29u8,39u8,52u8,20u8,147u8,134u8, - 155u8,138u8,5u8,203u8,128u8,14u8,99u8,161u8,224u8,173u8,3u8,77u8,129u8,135u8,13u8,247u8,63u8,75u8,21u8,112u8, - 11u8,207u8,115u8,248u8,255u8,0u8,18u8,173u8,118u8,65u8,1u8,176u8,101u8,41u8,144u8,17u8,108u8,229u8,210u8,198u8, - 193u8,7u8,184u8,187u8,224u8,99u8,48u8,18u8,14u8,20u8,103u8,74u8,195u8,227u8,210u8,134u8,17u8,188u8,155u8,75u8, - 121u8,224u8,50u8,225u8,42u8,195u8,99u8,143u8,67u8,98u8,17u8,2u8,165u8,73u8,108u8,56u8,220u8,98u8,129u8,216u8, - 3u8,144u8,3u8,179u8,49u8,240u8,7u8,184u8,125u8,129u8,28u8,121u8,227u8,24u8,163u8,229u8,133u8,152u8,160u8,71u8, - 56u8,17u8,62u8,118u8,1u8,126u8,147u8,230u8,1u8,1u8,17u8,3u8,186u8,34u8,84u8,14u8,58u8,3u8,183u8,244u8, - 113u8,112u8,0u8,78u8,64u8,160u8,20u8,126u8,76u8,30u8,231u8,240u8,175u8,224u8,203u8,58u8,69u8,200u8,53u8,64u8, - 104u8,11u8,145u8,183u8,169u8,13u8,58u8,69u8,0u8,67u8,130u8,1u8,201u8,148u8,86u8,105u8,60u8,12u8,88u8,91u8, - 103u8,240u8,120u8,1u8,119u8,130u8,201u8,224u8,109u8,140u8,181u8,39u8,57u8,240u8,13u8,104u8,86u8,154u8,66u8,250u8, - 193u8,84u8,242u8,96u8,189u8,177u8,112u8,181u8,242u8,3u8,151u8,137u8,176u8,0u8,42u8,151u8,26u8,0u8,77u8,112u8, - 118u8,32u8,146u8,169u8,119u8,10u8,40u8,58u8,144u8,239u8,60u8,164u8,2u8,222u8,49u8,124u8,91u8,4u8,5u8,138u8, - 197u8,8u8,116u8,44u8,203u8,56u8,144u8,161u8,88u8,167u8,30u8,44u8,133u8,24u8,105u8,43u8,65u8,19u8,180u8,130u8, - 135u8,7u8,124u8,61u8,120u8,65u8,159u8,194u8,107u8,5u8,101u8,4u8,28u8,52u8,4u8,55u8,146u8,43u8,120u8,184u8, - 25u8,96u8,87u8,225u8,29u8,154u8,73u8,5u8,52u8,7u8,48u8,185u8,66u8,140u8,225u8,116u8,174u8,184u8,2u8,191u8, - 116u8,150u8,192u8,243u8,244u8,240u8,138u8,51u8,233u8,36u8,4u8,25u8,26u8,28u8,98u8,132u8,47u8,2u8,198u8,66u8, - 164u8,8u8,103u8,28u8,158u8,133u8,111u8,159u8,231u8,80u8,149u8,152u8,1u8,121u8,242u8,18u8,68u8,224u8,237u8,101u8, - 246u8,226u8,146u8,109u8,192u8,94u8,68u8,38u8,243u8,238u8,21u8,4u8,128u8,79u8,202u8,44u8,198u8,89u8,145u8,198u8, - 0u8,56u8,178u8,246u8,72u8,101u8,20u8,217u8,255u8,237u8,27u8,41u8,56u8,204u8,51u8,16u8,193u8,171u8,107u8,236u8, - 245u8,39u8,133u8,229u8,92u8,3u8,133u8,57u8,29u8,61u8,209u8,186u8,43u8,158u8,153u8,198u8,105u8,255u8,23u8,152u8, - 198u8,49u8,199u8,123u8,215u8,130u8,243u8,0u8,113u8,80u8,8u8,81u8,141u8,74u8,172u8,180u8,8u8,133u8,115u8,204u8, - 34u8,79u8,49u8,70u8,174u8,224u8,214u8,91u8,6u8,217u8,64u8,98u8,5u8,240u8,93u8,194u8,97u8,88u8,16u8,161u8, - 38u8,192u8,60u8,53u8,65u8,159u8,20u8,65u8,104u8,196u8,208u8,88u8,85u8,172u8,51u8,3u8,157u8,83u8,6u8,128u8, - 30u8,254u8,151u8,105u8,1u8,83u8,11u8,177u8,162u8,124u8,141u8,15u8,14u8,105u8,150u8,20u8,193u8,10u8,243u8,160u8, - 29u8,67u8,124u8,227u8,114u8,4u8,104u8,192u8,48u8,96u8,65u8,140u8,193u8,252u8,97u8,125u8,33u8,135u8,0u8,47u8, - 96u8,27u8,18u8,216u8,27u8,216u8,103u8,13u8,115u8,37u8,17u8,10u8,3u8,225u8,32u8,204u8,49u8,78u8,227u8,140u8, - 7u8,238u8,97u8,149u8,56u8,44u8,55u8,236u8,77u8,64u8,103u8,198u8,30u8,189u8,96u8,112u8,8u8,225u8,33u8,204u8, - 107u8,30u8,176u8,188u8,144u8,235u8,212u8,196u8,218u8,195u8,124u8,169u8,56u8,213u8,72u8,28u8,25u8,159u8,147u8,174u8, - 8u8,7u8,43u8,153u8,90u8,153u8,249u8,16u8,82u8,64u8,174u8,88u8,146u8,7u8,180u8,129u8,32u8,13u8,224u8,175u8, - 227u8,26u8,10u8,106u8,21u8,32u8,7u8,136u8,11u8,2u8,71u8,153u8,27u8,218u8,104u8,96u8,35u8,25u8,19u8,64u8, - 102u8,16u8,53u8,101u8,89u8,34u8,48u8,119u8,152u8,117u8,90u8,35u8,200u8,32u8,99u8,22u8,96u8,136u8,177u8,29u8, - 40u8,140u8,134u8,164u8,12u8,84u8,209u8,229u8,2u8,154u8,38u8,65u8,94u8,131u8,189u8,192u8,67u8,212u8,66u8,194u8, - 193u8,83u8,137u8,177u8,148u8,150u8,8u8,144u8,181u8,53u8,30u8,224u8,116u8,6u8,98u8,35u8,192u8,66u8,194u8,229u8, - 248u8,5u8,3u8,161u8,197u8,227u8,8u8,37u8,17u8,121u8,27u8,18u8,112u8,32u8,52u8,22u8,128u8,77u8,76u8,128u8, - 11u8,0u8,18u8,76u8,130u8,35u8,92u8,199u8,26u8,145u8,60u8,75u8,96u8,158u8,13u8,114u8,57u8,204u8,228u8,140u8, - 118u8,22u8,204u8,91u8,193u8,110u8,192u8,204u8,193u8,134u8,56u8,40u8,59u8,88u8,174u8,82u8,153u8,196u8,4u8,133u8, - 199u8,49u8,32u8,29u8,0u8,185u8,120u8,8u8,18u8,14u8,156u8,7u8,150u8,5u8,38u8,55u8,9u8,146u8,233u8,24u8, - 166u8,31u8,224u8,142u8,192u8,6u8,136u8,132u8,20u8,30u8,53u8,73u8,206u8,40u8,238u8,4u8,128u8,13u8,100u8,8u8, - 106u8,161u8,16u8,89u8,43u8,145u8,105u8,109u8,125u8,32u8,172u8,157u8,129u8,80u8,100u8,198u8,32u8,27u8,28u8,54u8, - 152u8,201u8,84u8,101u8,136u8,8u8,143u8,23u8,105u8,24u8,114u8,132u8,249u8,212u8,63u8,44u8,61u8,140u8,112u8,194u8, - 4u8,54u8,27u8,47u8,92u8,174u8,96u8,232u8,9u8,4u8,64u8,0u8,9u8,88u8,7u8,40u8,154u8,161u8,44u8,162u8, - 141u8,93u8,12u8,37u8,75u8,60u8,1u8,98u8,130u8,108u8,58u8,226u8,207u8,20u8,27u8,134u8,70u8,242u8,209u8,25u8, - 129u8,13u8,30u8,155u8,181u8,87u8,18u8,246u8,0u8,74u8,2u8,168u8,203u8,75u8,135u8,196u8,102u8,160u8,244u8,129u8, - 67u8,159u8,153u8,1u8,240u8,70u8,219u8,4u8,129u8,241u8,200u8,100u8,185u8,28u8,36u8,213u8,32u8,46u8,212u8,7u8, - 250u8,164u8,148u8,230u8,14u8,15u8,1u8,122u8,35u8,124u8,1u8,95u8,195u8,167u8,112u8,216u8,163u8,96u8,173u8,1u8, - 77u8,97u8,227u8,128u8,203u8,64u8,254u8,1u8,71u8,158u8,52u8,229u8,15u8,11u8,85u8,43u8,163u8,55u8,18u8,172u8, - 182u8,239u8,81u8,59u8,60u8,74u8,45u8,14u8,33u8,31u8,31u8,166u8,150u8,167u8,162u8,199u8,35u8,192u8,242u8,187u8, - 241u8,72u8,181u8,117u8,21u8,193u8,228u8,121u8,231u8,129u8,239u8,223u8,12u8,121u8,186u8,51u8,146u8,45u8,104u8,115u8, - 92u8,40u8,219u8,204u8,103u8,42u8,158u8,173u8,183u8,154u8,221u8,17u8,237u8,254u8,220u8,98u8,247u8,210u8,163u8,199u8, - 139u8,111u8,203u8,108u8,199u8,254u8,224u8,236u8,237u8,99u8,174u8,125u8,51u8,233u8,85u8,218u8,252u8,156u8,94u8,248u8, - 57u8,189u8,240u8,115u8,122u8,225u8,231u8,244u8,194u8,207u8,233u8,133u8,159u8,211u8,11u8,255u8,219u8,211u8,11u8,15u8, - 139u8,123u8,203u8,234u8,214u8,70u8,211u8,30u8,55u8,198u8,117u8,208u8,122u8,120u8,244u8,38u8,4u8,56u8,251u8,32u8, - 132u8,103u8,9u8,124u8,118u8,111u8,24u8,98u8,159u8,140u8,5u8,1u8,13u8,226u8,214u8,179u8,12u8,49u8,46u8,129u8, - 30u8,240u8,199u8,33u8,92u8,12u8,209u8,14u8,156u8,103u8,184u8,196u8,176u8,195u8,112u8,244u8,17u8,235u8,136u8,12u8, - 97u8,63u8,236u8,42u8,228u8,31u8,33u8,18u8,108u8,48u8,112u8,10u8,68u8,183u8,48u8,115u8,9u8,161u8,69u8,4u8, - 240u8,112u8,34u8,42u8,48u8,16u8,132u8,82u8,28u8,14u8,48u8,162u8,59u8,231u8,152u8,243u8,169u8,180u8,8u8,115u8, - 2u8,7u8,170u8,166u8,83u8,208u8,51u8,69u8,84u8,129u8,22u8,14u8,16u8,138u8,7u8,2u8,101u8,33u8,139u8,198u8, - 209u8,158u8,66u8,104u8,71u8,6u8,154u8,35u8,200u8,224u8,136u8,177u8,141u8,71u8,36u8,113u8,116u8,64u8,0u8,95u8, - 94u8,7u8,172u8,3u8,58u8,65u8,17u8,21u8,2u8,43u8,176u8,83u8,18u8,134u8,2u8,77u8,36u8,29u8,137u8,129u8, - 45u8,97u8,116u8,4u8,5u8,100u8,102u8,16u8,162u8,115u8,101u8,61u8,80u8,145u8,24u8,251u8,77u8,142u8,88u8,22u8, - 1u8,26u8,56u8,239u8,0u8,97u8,97u8,197u8,152u8,23u8,124u8,126u8,102u8,131u8,226u8,14u8,82u8,140u8,56u8,22u8, - 81u8,163u8,68u8,200u8,147u8,35u8,52u8,117u8,180u8,101u8,49u8,4u8,246u8,1u8,68u8,73u8,148u8,139u8,115u8,78u8, - 165u8,143u8,193u8,64u8,208u8,17u8,217u8,27u8,172u8,29u8,232u8,16u8,5u8,156u8,1u8,150u8,10u8,64u8,31u8,134u8, - 149u8,60u8,147u8,100u8,52u8,240u8,47u8,70u8,4u8,238u8,130u8,88u8,16u8,194u8,133u8,117u8,99u8,134u8,204u8,197u8, - 199u8,47u8,216u8,36u8,2u8,40u8,32u8,199u8,216u8,0u8,135u8,56u8,2u8,11u8,68u8,236u8,20u8,174u8,234u8,56u8, - 119u8,38u8,67u8,76u8,109u8,1u8,195u8,229u8,9u8,226u8,84u8,240u8,18u8,52u8,7u8,206u8,233u8,51u8,232u8,4u8, - 98u8,37u8,0u8,137u8,16u8,117u8,14u8,30u8,88u8,224u8,71u8,16u8,127u8,132u8,97u8,192u8,34u8,2u8,0u8,65u8, - 153u8,130u8,68u8,152u8,21u8,144u8,135u8,12u8,240u8,41u8,231u8,160u8,37u8,39u8,165u8,4u8,143u8,192u8,162u8,144u8, - 96u8,190u8,182u8,240u8,19u8,172u8,140u8,181u8,226u8,129u8,240u8,9u8,9u8,84u8,8u8,64u8,44u8,194u8,88u8,10u8, - 151u8,16u8,200u8,194u8,171u8,0u8,219u8,57u8,236u8,50u8,186u8,73u8,72u8,233u8,128u8,152u8,193u8,161u8,192u8,52u8, - 36u8,158u8,134u8,50u8,177u8,7u8,196u8,184u8,42u8,78u8,66u8,202u8,83u8,5u8,239u8,5u8,88u8,38u8,8u8,41u8, - 17u8,86u8,195u8,37u8,0u8,128u8,2u8,151u8,129u8,19u8,41u8,98u8,184u8,37u8,216u8,189u8,21u8,166u8,174u8,18u8, - 130u8,188u8,0u8,95u8,97u8,127u8,78u8,114u8,68u8,166u8,208u8,113u8,184u8,39u8,192u8,86u8,50u8,10u8,235u8,93u8, - 10u8,254u8,73u8,192u8,93u8,80u8,148u8,98u8,211u8,70u8,120u8,14u8,56u8,16u8,59u8,15u8,194u8,213u8,144u8,232u8, - 56u8,0u8,32u8,133u8,252u8,2u8,155u8,208u8,54u8,69u8,184u8,14u8,228u8,3u8,32u8,36u8,54u8,82u8,231u8,40u8, - 78u8,228u8,16u8,40u8,88u8,124u8,72u8,1u8,124u8,16u8,3u8,19u8,227u8,16u8,239u8,26u8,155u8,35u8,224u8,139u8, - 9u8,241u8,4u8,94u8,2u8,226u8,129u8,8u8,240u8,152u8,44u8,52u8,200u8,137u8,227u8,57u8,140u8,89u8,33u8,76u8, - 47u8,48u8,76u8,136u8,153u8,133u8,169u8,196u8,206u8,134u8,24u8,20u8,214u8,18u8,68u8,7u8,33u8,192u8,94u8,199u8, - 20u8,166u8,104u8,176u8,171u8,123u8,153u8,3u8,191u8,113u8,0u8,136u8,0u8,39u8,56u8,184u8,82u8,82u8,17u8,232u8, - 140u8,63u8,32u8,19u8,12u8,180u8,84u8,42u8,102u8,22u8,22u8,47u8,133u8,40u8,192u8,184u8,193u8,95u8,1u8,63u8, - 83u8,252u8,223u8,96u8,125u8,1u8,216u8,151u8,226u8,192u8,67u8,29u8,48u8,66u8,163u8,189u8,70u8,104u8,44u8,20u8, - 128u8,113u8,218u8,253u8,0u8,62u8,98u8,249u8,208u8,83u8,75u8,80u8,105u8,154u8,1u8,189u8,50u8,112u8,1u8,177u8, - 168u8,20u8,251u8,176u8,8u8,0u8,136u8,1u8,187u8,3u8,76u8,2u8,102u8,12u8,219u8,2u8,40u8,139u8,112u8,248u8, - 159u8,211u8,177u8,135u8,164u8,99u8,31u8,150u8,87u8,29u8,79u8,172u8,142u8,222u8,13u8,124u8,72u8,96u8,251u8,84u8, - 211u8,90u8,205u8,200u8,54u8,245u8,120u8,161u8,234u8,248u8,33u8,184u8,178u8,254u8,149u8,142u8,124u8,15u8,146u8,176u8, - 37u8,38u8,92u8,223u8,135u8,199u8,123u8,215u8,229u8,233u8,239u8,102u8,189u8,22u8,73u8,175u8,197u8,154u8,144u8,6u8, - 243u8,228u8,186u8,168u8,156u8,127u8,189u8,92u8,221u8,80u8,193u8,119u8,79u8,100u8,58u8,93u8,192u8,195u8,237u8,119u8, - 219u8,151u8,154u8,214u8,37u8,49u8,197u8,49u8,27u8,186u8,233u8,181u8,58u8,178u8,216u8,159u8,251u8,246u8,90u8,186u8, - 209u8,235u8,242u8,234u8,120u8,178u8,57u8,33u8,186u8,179u8,93u8,79u8,95u8,250u8,66u8,191u8,99u8,198u8,117u8,78u8, - 251u8,128u8,148u8,246u8,33u8,186u8,209u8,189u8,113u8,111u8,253u8,234u8,200u8,199u8,59u8,55u8,106u8,223u8,141u8,61u8, - 61u8,34u8,94u8,149u8,136u8,77u8,114u8,98u8,244u8,26u8,179u8,94u8,136u8,178u8,154u8,77u8,104u8,230u8,248u8,139u8, - 70u8,70u8,64u8,171u8,29u8,55u8,158u8,62u8,237u8,82u8,5u8,162u8,55u8,2u8,86u8,81u8,149u8,123u8,251u8,45u8, - 62u8,131u8,247u8,47u8,29u8,166u8,208u8,181u8,242u8,146u8,222u8,150u8,235u8,42u8,71u8,156u8,6u8,168u8,126u8,49u8, - 213u8,211u8,174u8,235u8,221u8,234u8,113u8,78u8,31u8,97u8,160u8,102u8,202u8,59u8,174u8,210u8,218u8,142u8,114u8,250u8, - 8u8,195u8,52u8,19u8,110u8,149u8,61u8,80u8,175u8,99u8,22u8,141u8,46u8,209u8,185u8,89u8,87u8,128u8,209u8,142u8, - 179u8,109u8,125u8,21u8,175u8,114u8,247u8,239u8,86u8,113u8,215u8,175u8,198u8,111u8,81u8,236u8,11u8,221u8,124u8,68u8, - 151u8,246u8,221u8,168u8,249u8,106u8,43u8,171u8,199u8,90u8,128u8,237u8,189u8,55u8,195u8,187u8,49u8,119u8,171u8,240u8, - 152u8,98u8,86u8,130u8,208u8,208u8,253u8,24u8,124u8,184u8,245u8,86u8,148u8,9u8,97u8,57u8,240u8,245u8,13u8,79u8, - 199u8,166u8,178u8,67u8,6u8,247u8,222u8,69u8,56u8,222u8,33u8,109u8,242u8,251u8,31u8,109u8,169u8,222u8,64u8,88u8, - 123u8,187u8,94u8,231u8,215u8,238u8,166u8,124u8,232u8,158u8,188u8,61u8,76u8,51u8,2u8,24u8,31u8,182u8,202u8,61u8, - 47u8,39u8,25u8,156u8,172u8,41u8,168u8,208u8,255u8,116u8,88u8,219u8,51u8,168u8,209u8,111u8,155u8,204u8,78u8,45u8, - 126u8,249u8,190u8,180u8,145u8,250u8,125u8,186u8,30u8,226u8,239u8,112u8,157u8,254u8,30u8,253u8,226u8,195u8,232u8,239u8, - 144u8,245u8,245u8,223u8,223u8,155u8,48u8,179u8,99u8,47u8,178u8,120u8,58u8,114u8,228u8,232u8,172u8,214u8,246u8,230u8, - 167u8,98u8,65u8,197u8,81u8,212u8,222u8,42u8,14u8,24u8,163u8,195u8,220u8,209u8,1u8,59u8,45u8,170u8,33u8,39u8, - 79u8,15u8,69u8,223u8,140u8,182u8,255u8,9u8,166u8,56u8,62u8,145u8,2u8,129u8,126u8,18u8,253u8,19u8,144u8,108u8, - 96u8,214u8,190u8,123u8,98u8,221u8,147u8,51u8,252u8,227u8,195u8,147u8,206u8,151u8,63u8,193u8,98u8,6u8,147u8,155u8, - 90u8,220u8,217u8,59u8,111u8,249u8,238u8,168u8,247u8,83u8,39u8,142u8,250u8,231u8,59u8,7u8,25u8,164u8,146u8,69u8, - 253u8,252u8,209u8,72u8,165u8,223u8,87u8,215u8,245u8,105u8,222u8,206u8,109u8,59u8,237u8,144u8,107u8,29u8,253u8,106u8, - 254u8,235u8,95u8,60u8,217u8,94u8,130u8,142u8,95u8,162u8,191u8,96u8,222u8,223u8,243u8,179u8,232u8,226u8,226u8,226u8, - 44u8,74u8,126u8,232u8,199u8,55u8,83u8,240u8,224u8,119u8,79u8,0u8,102u8,81u8,168u8,15u8,4u8,157u8,18u8,253u8, - 0u8,185u8,88u8,70u8,37u8,42u8,82u8,0u8,130u8,18u8,252u8,201u8,217u8,225u8,205u8,197u8,113u8,205u8,229u8,113u8, - 205u8,227u8,227u8,154u8,39u8,157u8,230u8,61u8,16u8,246u8,69u8,115u8,251u8,65u8,245u8,182u8,247u8,65u8,33u8,229u8, - 195u8,210u8,119u8,70u8,73u8,100u8,31u8,128u8,129u8,8u8,147u8,75u8,228u8,129u8,168u8,128u8,193u8,56u8,42u8,127u8, - 16u8,177u8,215u8,84u8,133u8,5u8,164u8,221u8,1u8,69u8,100u8,192u8,217u8,109u8,206u8,2u8,157u8,127u8,182u8,57u8, - 143u8,145u8,74u8,2u8,190u8,2u8,192u8,16u8,200u8,114u8,76u8,112u8,180u8,5u8,108u8,239u8,129u8,13u8,33u8,155u8, - 193u8,233u8,196u8,97u8,156u8,57u8,192u8,239u8,199u8,101u8,204u8,226u8,36u8,141u8,145u8,225u8,0u8,4u8,164u8,21u8, - 50u8,113u8,0u8,174u8,145u8,31u8,65u8,166u8,202u8,36u8,74u8,1u8,95u8,247u8,65u8,26u8,42u8,217u8,2u8,222u8, - 43u8,131u8,22u8,204u8,2u8,42u8,210u8,54u8,3u8,162u8,37u8,232u8,32u8,53u8,32u8,113u8,109u8,144u8,203u8,64u8, - 226u8,132u8,197u8,72u8,165u8,248u8,24u8,185u8,32u8,3u8,2u8,35u8,125u8,97u8,128u8,129u8,219u8,227u8,38u8,243u8, - 174u8,207u8,12u8,30u8,51u8,153u8,60u8,166u8,210u8,27u8,116u8,97u8,85u8,150u8,1u8,182u8,119u8,198u8,97u8,77u8, - 129u8,42u8,187u8,128u8,206u8,105u8,197u8,40u8,251u8,0u8,228u8,23u8,249u8,66u8,2u8,48u8,115u8,203u8,41u8,233u8, - 96u8,0u8,13u8,102u8,84u8,215u8,164u8,12u8,160u8,64u8,1u8,168u8,60u8,5u8,76u8,6u8,144u8,52u8,102u8,192u8, - 135u8,37u8,3u8,184u8,151u8,5u8,32u8,142u8,66u8,28u8,151u8,190u8,203u8,148u8,151u8,142u8,33u8,17u8,5u8,58u8, - 64u8,52u8,138u8,180u8,27u8,67u8,182u8,3u8,98u8,0u8,8u8,82u8,35u8,201u8,5u8,136u8,46u8,6u8,66u8,108u8, - 227u8,76u8,33u8,195u8,153u8,3u8,121u8,183u8,25u8,211u8,200u8,221u8,169u8,196u8,82u8,69u8,149u8,128u8,116u8,112u8, - 96u8,168u8,49u8,149u8,177u8,112u8,29u8,155u8,212u8,8u8,160u8,171u8,0u8,29u8,101u8,30u8,239u8,73u8,223u8,109u8, - 115u8,17u8,131u8,244u8,93u8,241u8,197u8,95u8,191u8,105u8,189u8,105u8,129u8,126u8,29u8,201u8,89u8,144u8,121u8,161u8, - 111u8,202u8,60u8,5u8,229u8,40u8,134u8,105u8,191u8,183u8,201u8,80u8,8u8,66u8,75u8,193u8,23u8,143u8,212u8,110u8, - 98u8,128u8,49u8,154u8,4u8,25u8,28u8,100u8,153u8,157u8,0u8,148u8,109u8,83u8,16u8,28u8,107u8,214u8,72u8,85u8, - 167u8,57u8,64u8,108u8,11u8,184u8,58u8,80u8,154u8,213u8,48u8,15u8,108u8,27u8,73u8,156u8,12u8,201u8,94u8,229u8, - 125u8,154u8,167u8,160u8,117u8,74u8,100u8,179u8,9u8,161u8,187u8,2u8,56u8,41u8,114u8,83u8,0u8,215u8,189u8,227u8, - 232u8,79u8,33u8,99u8,27u8,28u8,82u8,16u8,128u8,74u8,1u8,129u8,34u8,101u8,11u8,29u8,4u8,72u8,202u8,24u8, - 240u8,112u8,227u8,53u8,132u8,1u8,249u8,129u8,0u8,9u8,228u8,224u8,9u8,128u8,93u8,193u8,9u8,231u8,55u8,65u8, - 65u8,225u8,51u8,129u8,252u8,70u8,106u8,168u8,176u8,84u8,243u8,132u8,248u8,73u8,169u8,73u8,198u8,143u8,47u8,89u8, - 210u8,4u8,216u8,107u8,73u8,9u8,147u8,0u8,145u8,138u8,157u8,74u8,169u8,76u8,202u8,162u8,179u8,12u8,2u8,136u8, - 180u8,24u8,166u8,29u8,75u8,228u8,212u8,12u8,50u8,98u8,192u8,86u8,41u8,131u8,3u8,100u8,222u8,57u8,227u8,169u8, - 12u8,141u8,9u8,100u8,146u8,13u8,48u8,104u8,144u8,12u8,244u8,146u8,72u8,151u8,193u8,4u8,49u8,36u8,65u8,69u8, - 14u8,196u8,22u8,216u8,180u8,82u8,200u8,165u8,249u8,52u8,181u8,0u8,191u8,129u8,220u8,26u8,224u8,249u8,72u8,196u8, - 33u8,241u8,70u8,104u8,183u8,12u8,200u8,139u8,106u8,82u8,43u8,100u8,180u8,145u8,139u8,19u8,6u8,41u8,0u8,167u8, - 120u8,14u8,173u8,160u8,44u8,135u8,146u8,208u8,3u8,145u8,179u8,24u8,233u8,65u8,71u8,212u8,10u8,44u8,0u8,180u8, - 119u8,74u8,128u8,1u8,160u8,137u8,127u8,64u8,21u8,30u8,232u8,207u8,160u8,250u8,49u8,82u8,10u8,200u8,47u8,21u8, - 85u8,141u8,96u8,2u8,200u8,12u8,60u8,91u8,75u8,141u8,156u8,16u8,166u8,157u8,196u8,48u8,164u8,148u8,96u8,144u8, - 214u8,230u8,72u8,35u8,8u8,100u8,67u8,176u8,139u8,164u8,200u8,43u8,102u8,248u8,27u8,12u8,80u8,103u8,164u8,244u8, - 83u8,240u8,21u8,249u8,20u8,48u8,222u8,121u8,100u8,28u8,50u8,41u8,169u8,212u8,14u8,147u8,83u8,65u8,72u8,144u8, - 17u8,214u8,36u8,100u8,176u8,117u8,132u8,106u8,3u8,132u8,71u8,114u8,17u8,10u8,37u8,145u8,166u8,73u8,0u8,141u8, - 167u8,58u8,78u8,56u8,85u8,130u8,83u8,137u8,101u8,78u8,117u8,181u8,89u8,138u8,68u8,149u8,192u8,163u8,208u8,117u8, - 202u8,142u8,88u8,200u8,30u8,53u8,114u8,78u8,89u8,96u8,231u8,89u8,22u8,210u8,227u8,139u8,210u8,100u8,42u8,114u8, - 164u8,94u8,36u8,216u8,1u8,233u8,205u8,98u8,129u8,4u8,141u8,96u8,42u8,64u8,140u8,177u8,65u8,64u8,189u8,21u8, - 6u8,142u8,145u8,80u8,136u8,51u8,36u8,142u8,84u8,130u8,244u8,148u8,3u8,202u8,142u8,169u8,9u8,72u8,63u8,247u8, - 152u8,120u8,64u8,210u8,22u8,57u8,94u8,228u8,16u8,144u8,211u8,129u8,157u8,4u8,151u8,52u8,76u8,0u8,243u8,144u8, - 4u8,134u8,4u8,38u8,12u8,5u8,118u8,196u8,60u8,231u8,42u8,135u8,148u8,96u8,23u8,128u8,121u8,229u8,130u8,178u8, - 185u8,200u8,121u8,67u8,172u8,48u8,148u8,144u8,54u8,32u8,7u8,20u8,167u8,24u8,20u8,191u8,51u8,1u8,44u8,62u8, - 96u8,139u8,96u8,72u8,179u8,89u8,12u8,140u8,221u8,8u8,242u8,132u8,180u8,15u8,82u8,127u8,177u8,15u8,176u8,182u8, - 12u8,230u8,26u8,73u8,146u8,227u8,57u8,28u8,147u8,20u8,103u8,138u8,138u8,10u8,145u8,239u8,163u8,148u8,180u8,245u8, - 84u8,95u8,103u8,160u8,140u8,57u8,82u8,64u8,105u8,230u8,45u8,114u8,95u8,200u8,239u8,59u8,170u8,200u8,7u8,27u8, - 147u8,132u8,83u8,214u8,6u8,202u8,44u8,36u8,114u8,65u8,200u8,235u8,139u8,12u8,251u8,149u8,83u8,193u8,129u8,228u8, - 156u8,182u8,5u8,158u8,229u8,185u8,72u8,145u8,5u8,228u8,84u8,80u8,142u8,172u8,149u8,193u8,86u8,130u8,140u8,165u8, - 71u8,14u8,45u8,131u8,164u8,36u8,30u8,230u8,65u8,105u8,30u8,83u8,125u8,63u8,105u8,54u8,132u8,62u8,64u8,212u8, - 61u8,21u8,164u8,67u8,22u8,82u8,40u8,89u8,158u8,48u8,151u8,202u8,28u8,6u8,157u8,174u8,163u8,208u8,180u8,103u8, - 10u8,26u8,28u8,185u8,27u8,93u8,72u8,157u8,209u8,154u8,31u8,125u8,96u8,12u8,38u8,145u8,80u8,131u8,181u8,223u8, - 140u8,231u8,40u8,90u8,215u8,245u8,141u8,28u8,20u8,218u8,245u8,228u8,163u8,103u8,55u8,26u8,3u8,29u8,237u8,72u8, - 42u8,108u8,51u8,0u8,125u8,132u8,97u8,23u8,126u8,187u8,93u8,200u8,254u8,220u8,200u8,161u8,93u8,54u8,100u8,157u8, - 234u8,147u8,246u8,162u8,177u8,132u8,203u8,33u8,17u8,124u8,185u8,204u8,179u8,246u8,196u8,155u8,1u8,31u8,61u8,189u8, - 49u8,134u8,249u8,188u8,171u8,36u8,199u8,99u8,166u8,48u8,138u8,27u8,133u8,182u8,145u8,201u8,143u8,144u8,207u8,216u8, - 117u8,213u8,229u8,207u8,137u8,144u8,142u8,201u8,233u8,221u8,207u8,83u8,145u8,134u8,179u8,24u8,178u8,203u8,56u8,253u8, - 39u8,223u8,254u8,135u8,227u8,87u8,41u8,206u8,34u8,149u8,209u8,47u8,130u8,254u8,147u8,86u8,31u8,195u8,152u8,150u8, - 223u8,113u8,158u8,85u8,191u8,149u8,79u8,243u8,184u8,252u8,60u8,206u8,255u8,3u8,36u8,212u8,235u8,232u8,62u8,63u8, - 253u8,230u8,3u8,72u8,73u8,149u8,56u8,43u8,194u8,229u8,226u8,166u8,198u8,232u8,251u8,249u8,15u8,79u8,142u8,90u8, - 99u8,243u8,218u8,165u8,113u8,148u8,249u8,240u8,84u8,207u8,103u8,64u8,241u8,91u8,145u8,243u8,162u8,115u8,19u8,255u8, - 80u8,94u8,203u8,183u8,89u8,204u8,70u8,165u8,113u8,242u8,189u8,38u8,227u8,230u8,172u8,205u8,38u8,118u8,122u8,58u8, - 38u8,203u8,229u8,96u8,245u8,236u8,186u8,35u8,77u8,191u8,187u8,234u8,105u8,67u8,158u8,241u8,142u8,231u8,93u8,139u8, - 240u8,0u8,89u8,191u8,155u8,204u8,29u8,116u8,214u8,53u8,159u8,150u8,190u8,169u8,231u8,219u8,111u8,212u8,58u8,78u8, - 240u8,135u8,188u8,129u8,217u8,191u8,123u8,245u8,176u8,94u8,154u8,105u8,188u8,255u8,182u8,34u8,86u8,27u8,197u8,233u8, - 119u8,201u8,246u8,118u8,215u8,85u8,243u8,130u8,175u8,131u8,210u8,130u8,79u8,91u8,239u8,111u8,123u8,172u8,228u8,224u8, - 212u8,158u8,183u8,42u8,55u8,189u8,209u8,247u8,23u8,239u8,79u8,172u8,141u8,246u8,186u8,59u8,91u8,88u8,141u8,121u8, - 250u8,200u8,131u8,214u8,75u8,25u8,19u8,155u8,71u8,235u8,188u8,219u8,107u8,87u8,180u8,254u8,229u8,18u8,134u8,15u8, - 214u8,250u8,3u8,178u8,141u8,239u8,255u8,244u8,233u8,70u8,244u8,1u8,236u8,182u8,184u8,48u8,244u8,71u8,202u8,52u8, - 118u8,147u8,101u8,173u8,241u8,15u8,204u8,6u8,54u8,170u8,245u8,14u8,146u8,129u8,207u8,182u8,247u8,239u8,117u8,179u8, - 107u8,197u8,59u8,160u8,74u8,199u8,181u8,247u8,38u8,93u8,187u8,242u8,133u8,109u8,44u8,175u8,107u8,46u8,132u8,237u8, - 187u8,39u8,79u8,70u8,189u8,225u8,234u8,202u8,98u8,66u8,202u8,155u8,171u8,84u8,203u8,247u8,68u8,247u8,206u8,135u8, - 20u8,230u8,143u8,80u8,253u8,119u8,125u8,239u8,98u8,123u8,208u8,209u8,140u8,64u8,187u8,193u8,135u8,63u8,230u8,159u8, - 78u8,214u8,227u8,211u8,231u8,219u8,223u8,24u8,162u8,246u8,92u8,39u8,0u8,195u8,169u8,230u8,22u8,88u8,25u8,133u8, - 248u8,113u8,66u8,161u8,48u8,226u8,102u8,23u8,164u8,200u8,169u8,66u8,145u8,81u8,16u8,175u8,101u8,14u8,24u8,26u8, - 63u8,6u8,32u8,61u8,116u8,224u8,47u8,201u8,221u8,123u8,99u8,111u8,25u8,45u8,10u8,163u8,58u8,87u8,138u8,142u8, - 102u8,94u8,187u8,230u8,173u8,122u8,75u8,83u8,201u8,201u8,78u8,175u8,203u8,87u8,133u8,73u8,219u8,243u8,118u8,117u8, - 250u8,243u8,116u8,226u8,76u8,180u8,97u8,220u8,122u8,224u8,51u8,116u8,194u8,222u8,229u8,54u8,133u8,151u8,42u8,83u8, - 143u8,80u8,93u8,11u8,237u8,114u8,207u8,18u8,172u8,146u8,137u8,64u8,103u8,9u8,115u8,14u8,200u8,221u8,25u8,128u8, - 23u8,25u8,48u8,60u8,161u8,115u8,11u8,12u8,140u8,14u8,244u8,2u8,43u8,81u8,64u8,86u8,128u8,184u8,42u8,32u8, - 5u8,138u8,89u8,206u8,76u8,74u8,119u8,132u8,18u8,154u8,146u8,121u8,96u8,47u8,9u8,144u8,38u8,0u8,13u8,84u8, - 100u8,78u8,160u8,27u8,160u8,36u8,201u8,184u8,246u8,185u8,4u8,70u8,159u8,10u8,201u8,173u8,0u8,76u8,194u8,8u8, - 121u8,208u8,94u8,164u8,57u8,19u8,70u8,229u8,78u8,177u8,60u8,5u8,106u8,224u8,209u8,39u8,96u8,117u8,153u8,103u8, - 82u8,133u8,12u8,224u8,149u8,75u8,68u8,154u8,40u8,207u8,1u8,59u8,133u8,172u8,127u8,244u8,251u8,233u8,168u8,93u8, - 153u8,188u8,28u8,48u8,6u8,76u8,4u8,36u8,40u8,53u8,25u8,97u8,119u8,25u8,192u8,73u8,66u8,173u8,9u8,162u8, - 163u8,131u8,131u8,94u8,73u8,176u8,95u8,153u8,84u8,153u8,140u8,206u8,3u8,131u8,183u8,60u8,183u8,177u8,3u8,188u8, - 137u8,25u8,7u8,64u8,103u8,206u8,58u8,158u8,210u8,121u8,151u8,140u8,22u8,144u8,34u8,61u8,161u8,98u8,5u8,28u8, - 81u8,8u8,157u8,229u8,73u8,113u8,57u8,96u8,103u8,122u8,149u8,24u8,52u8,31u8,142u8,93u8,43u8,82u8,20u8,36u8, - 210u8,205u8,205u8,61u8,187u8,83u8,59u8,150u8,255u8,104u8,107u8,28u8,64u8,46u8,91u8,215u8,126u8,244u8,61u8,228u8, - 205u8,187u8,199u8,203u8,83u8,121u8,29u8,159u8,176u8,47u8,213u8,147u8,175u8,185u8,255u8,151u8,18u8,234u8,22u8,73u8, - 31u8,81u8,158u8,218u8,89u8,222u8,49u8,131u8,227u8,150u8,197u8,171u8,61u8,70u8,236u8,206u8,142u8,252u8,195u8,56u8, - 96u8,249u8,207u8,206u8,144u8,62u8,66u8,59u8,94u8,56u8,254u8,243u8,42u8,255u8,217u8,86u8,249u8,205u8,7u8,7u8, - 166u8,169u8,127u8,4u8,59u8,218u8,79u8,199u8,163u8,147u8,60u8,225u8,198u8,208u8,253u8,180u8,185u8,96u8,200u8,107u8, - 72u8,100u8,214u8,152u8,23u8,22u8,169u8,5u8,164u8,138u8,164u8,201u8,50u8,77u8,103u8,31u8,1u8,214u8,35u8,141u8, - 138u8,172u8,17u8,29u8,33u8,229u8,185u8,151u8,34u8,83u8,148u8,202u8,207u8,37u8,104u8,137u8,220u8,151u8,10u8,200u8, - 21u8,216u8,20u8,120u8,60u8,93u8,55u8,97u8,145u8,219u8,80u8,200u8,46u8,201u8,193u8,128u8,185u8,64u8,26u8,25u8, - 105u8,89u8,112u8,80u8,50u8,169u8,5u8,72u8,158u8,34u8,161u8,203u8,232u8,254u8,94u8,201u8,148u8,103u8,198u8,37u8, - 42u8,51u8,25u8,67u8,206u8,150u8,78u8,218u8,131u8,247u8,42u8,167u8,187u8,200u8,51u8,131u8,239u8,24u8,242u8,122u8, - 74u8,10u8,164u8,201u8,130u8,215u8,129u8,50u8,187u8,9u8,126u8,66u8,18u8,145u8,209u8,1u8,8u8,100u8,145u8,196u8, - 147u8,73u8,186u8,15u8,33u8,156u8,238u8,21u8,201u8,93u8,151u8,233u8,236u8,152u8,47u8,127u8,51u8,254u8,229u8,158u8, - 172u8,195u8,67u8,161u8,255u8,42u8,10u8,219u8,117u8,66u8,127u8,4u8,187u8,125u8,53u8,153u8,43u8,168u8,64u8,253u8, - 17u8,236u8,107u8,61u8,157u8,96u8,232u8,6u8,139u8,227u8,195u8,29u8,225u8,66u8,208u8,171u8,109u8,71u8,186u8,65u8, - 36u8,117u8,184u8,251u8,89u8,133u8,76u8,195u8,74u8,39u8,172u8,112u8,248u8,33u8,22u8,215u8,253u8,112u8,34u8,238u8, - 171u8,166u8,48u8,94u8,195u8,52u8,49u8,207u8,157u8,14u8,197u8,244u8,52u8,15u8,113u8,99u8,222u8,225u8,66u8,198u8, - 114u8,35u8,135u8,166u8,70u8,6u8,239u8,175u8,89u8,174u8,6u8,111u8,214u8,216u8,145u8,41u8,169u8,96u8,123u8,122u8, - 167u8,239u8,254u8,91u8,249u8,166u8,239u8,227u8,43u8,103u8,65u8,18u8,77u8,61u8,245u8,180u8,111u8,247u8,145u8,145u8, - 61u8,240u8,198u8,211u8,97u8,53u8,117u8,15u8,171u8,27u8,200u8,230u8,211u8,178u8,86u8,125u8,188u8,174u8,190u8,143u8, - 96u8,13u8,159u8,238u8,189u8,150u8,254u8,105u8,85u8,171u8,255u8,208u8,238u8,90u8,245u8,249u8,237u8,162u8,252u8,137u8, - 122u8,252u8,67u8,251u8,59u8,164u8,6u8,191u8,71u8,167u8,190u8,110u8,212u8,61u8,145u8,223u8,121u8,192u8,84u8,6u8, - 207u8,79u8,18u8,234u8,161u8,29u8,142u8,146u8,170u8,201u8,36u8,84u8,210u8,245u8,22u8,93u8,111u8,167u8,54u8,78u8, - 184u8,163u8,161u8,156u8,22u8,144u8,211u8,127u8,247u8,91u8,253u8,58u8,160u8,234u8,141u8,207u8,125u8,181u8,45u8,43u8, - 100u8,223u8,2u8,172u8,169u8,48u8,26u8,10u8,195u8,30u8,248u8,86u8,140u8,159u8,14u8,146u8,57u8,216u8,43u8,203u8, - 89u8,113u8,250u8,93u8,193u8,13u8,164u8,123u8,35u8,84u8,81u8,248u8,71u8,47u8,29u8,8u8,113u8,156u8,229u8,49u8, - 28u8,194u8,216u8,39u8,169u8,131u8,67u8,68u8,85u8,9u8,240u8,179u8,66u8,72u8,184u8,72u8,109u8,194u8,227u8,156u8, - 113u8,186u8,215u8,65u8,58u8,248u8,80u8,158u8,123u8,38u8,99u8,120u8,52u8,142u8,174u8,168u8,147u8,116u8,251u8,158u8, - 164u8,10u8,189u8,108u8,232u8,36u8,229u8,177u8,196u8,95u8,11u8,223u8,143u8,167u8,25u8,221u8,65u8,20u8,11u8,45u8, - 120u8,108u8,139u8,106u8,60u8,186u8,211u8,207u8,4u8,151u8,75u8,167u8,99u8,124u8,107u8,18u8,75u8,71u8,190u8,61u8, - 157u8,148u8,165u8,147u8,249u8,84u8,173u8,103u8,84u8,208u8,65u8,167u8,112u8,8u8,225u8,88u8,49u8,158u8,198u8,140u8, - 139u8,132u8,10u8,77u8,232u8,109u8,42u8,98u8,56u8,160u8,22u8,210u8,36u8,34u8,166u8,246u8,185u8,139u8,83u8,120u8, - 150u8,222u8,231u8,214u8,152u8,144u8,100u8,65u8,122u8,56u8,194u8,204u8,210u8,237u8,25u8,92u8,59u8,101u8,101u8,80u8, - 60u8,193u8,68u8,164u8,140u8,189u8,78u8,19u8,41u8,138u8,202u8,154u8,196u8,50u8,238u8,164u8,147u8,218u8,105u8,186u8, - 186u8,144u8,179u8,224u8,169u8,252u8,72u8,88u8,73u8,247u8,55u8,141u8,12u8,168u8,181u8,150u8,94u8,123u8,14u8,226u8, - 4u8,75u8,151u8,142u8,209u8,25u8,85u8,76u8,154u8,211u8,69u8,101u8,112u8,234u8,49u8,58u8,147u8,32u8,159u8,166u8, - 178u8,45u8,170u8,7u8,138u8,141u8,145u8,70u8,122u8,131u8,110u8,37u8,221u8,55u8,144u8,37u8,169u8,145u8,9u8,203u8, - 233u8,254u8,37u8,239u8,241u8,181u8,7u8,217u8,139u8,119u8,67u8,192u8,29u8,30u8,146u8,52u8,86u8,121u8,98u8,50u8, - 15u8,154u8,59u8,227u8,52u8,252u8,89u8,186u8,84u8,143u8,145u8,31u8,237u8,84u8,154u8,50u8,116u8,174u8,66u8,160u8, - 194u8,73u8,184u8,182u8,74u8,91u8,111u8,121u8,18u8,224u8,232u8,91u8,48u8,12u8,145u8,65u8,208u8,137u8,19u8,46u8, - 168u8,192u8,156u8,37u8,255u8,147u8,42u8,87u8,178u8,52u8,201u8,121u8,10u8,116u8,78u8,219u8,206u8,128u8,125u8,183u8, - 15u8,202u8,49u8,37u8,91u8,99u8,47u8,70u8,234u8,222u8,154u8,104u8,192u8,121u8,204u8,75u8,137u8,132u8,35u8,20u8, - 137u8,77u8,0u8,225u8,133u8,150u8,116u8,33u8,152u8,228u8,6u8,224u8,161u8,9u8,197u8,201u8,124u8,143u8,101u8,112u8, - 120u8,205u8,194u8,161u8,93u8,72u8,45u8,254u8,102u8,73u8,81u8,87u8,231u8,177u8,94u8,73u8,71u8,171u8,53u8,178u8, - 198u8,6u8,209u8,79u8,172u8,25u8,98u8,5u8,80u8,28u8,148u8,231u8,138u8,37u8,138u8,238u8,157u8,132u8,19u8,238u8, - 136u8,12u8,116u8,207u8,132u8,18u8,2u8,225u8,14u8,104u8,16u8,11u8,30u8,20u8,220u8,107u8,56u8,248u8,12u8,236u8, - 129u8,96u8,120u8,25u8,91u8,47u8,40u8,204u8,65u8,240u8,195u8,137u8,205u8,34u8,75u8,242u8,140u8,206u8,47u8,91u8, - 184u8,244u8,46u8,165u8,106u8,63u8,11u8,34u8,209u8,129u8,123u8,153u8,198u8,125u8,148u8,97u8,255u8,130u8,161u8,29u8, - 73u8,64u8,148u8,65u8,71u8,185u8,51u8,102u8,125u8,10u8,205u8,11u8,16u8,66u8,9u8,113u8,97u8,202u8,241u8,12u8, - 241u8,13u8,21u8,153u8,166u8,9u8,213u8,47u8,67u8,208u8,189u8,179u8,204u8,102u8,153u8,8u8,46u8,209u8,137u8,148u8, - 73u8,14u8,137u8,68u8,12u8,66u8,119u8,127u8,169u8,132u8,238u8,226u8,164u8,139u8,247u8,168u8,60u8,55u8,161u8,226u8, - 85u8,4u8,146u8,9u8,99u8,74u8,74u8,196u8,140u8,206u8,9u8,9u8,209u8,50u8,116u8,103u8,39u8,34u8,168u8,132u8, - 165u8,218u8,35u8,252u8,137u8,233u8,108u8,55u8,132u8,195u8,102u8,57u8,4u8,196u8,82u8,36u8,98u8,82u8,99u8,51u8, - 19u8,178u8,216u8,230u8,33u8,73u8,49u8,51u8,147u8,40u8,68u8,34u8,154u8,73u8,186u8,80u8,147u8,176u8,88u8,128u8, - 178u8,154u8,197u8,15u8,88u8,176u8,65u8,88u8,23u8,4u8,196u8,95u8,37u8,94u8,120u8,109u8,160u8,160u8,210u8,210u8, - 253u8,116u8,64u8,128u8,17u8,9u8,241u8,144u8,208u8,189u8,100u8,10u8,228u8,206u8,36u8,247u8,33u8,197u8,104u8,41u8, - 93u8,97u8,169u8,52u8,212u8,45u8,88u8,8u8,134u8,10u8,113u8,224u8,84u8,211u8,24u8,2u8,4u8,66u8,102u8,9u8, - 144u8,98u8,163u8,83u8,186u8,189u8,46u8,6u8,139u8,40u8,118u8,6u8,41u8,50u8,48u8,90u8,165u8,214u8,231u8,57u8, - 20u8,65u8,25u8,168u8,151u8,116u8,206u8,101u8,129u8,202u8,22u8,161u8,200u8,18u8,216u8,50u8,214u8,43u8,25u8,34u8, - 185u8,144u8,115u8,186u8,87u8,51u8,208u8,149u8,113u8,134u8,167u8,210u8,101u8,113u8,98u8,147u8,24u8,171u8,142u8,97u8, - 95u8,18u8,132u8,216u8,208u8,55u8,186u8,151u8,19u8,209u8,24u8,59u8,158u8,195u8,8u8,251u8,184u8,8u8,88u8,94u8, - 174u8,17u8,235u8,185u8,60u8,129u8,170u8,33u8,132u8,5u8,123u8,232u8,117u8,41u8,33u8,208u8,187u8,79u8,68u8,66u8, - 125u8,211u8,229u8,165u8,73u8,108u8,180u8,72u8,89u8,160u8,146u8,108u8,208u8,6u8,145u8,186u8,202u8,57u8,119u8,194u8, - 91u8,167u8,173u8,78u8,232u8,120u8,58u8,221u8,27u8,164u8,44u8,189u8,12u8,198u8,165u8,130u8,241u8,226u8,208u8,62u8, - 204u8,179u8,49u8,113u8,200u8,60u8,93u8,153u8,10u8,162u8,66u8,120u8,115u8,44u8,55u8,201u8,3u8,100u8,214u8,34u8, - 194u8,79u8,98u8,207u8,28u8,221u8,11u8,32u8,83u8,171u8,51u8,46u8,53u8,2u8,93u8,132u8,255u8,42u8,142u8,165u8, - 18u8,26u8,115u8,82u8,194u8,164u8,116u8,35u8,169u8,166u8,155u8,16u8,133u8,145u8,185u8,226u8,130u8,110u8,252u8,57u8, - 122u8,193u8,160u8,184u8,50u8,66u8,197u8,65u8,90u8,6u8,115u8,145u8,22u8,210u8,3u8,185u8,42u8,110u8,178u8,77u8, - 52u8,36u8,207u8,165u8,58u8,64u8,178u8,88u8,144u8,8u8,117u8,51u8,152u8,97u8,64u8,17u8,224u8,135u8,130u8,177u8, - 85u8,84u8,242u8,138u8,125u8,35u8,75u8,209u8,202u8,209u8,37u8,49u8,134u8,174u8,148u8,16u8,96u8,82u8,154u8,36u8, - 144u8,235u8,220u8,240u8,152u8,137u8,4u8,34u8,32u8,242u8,12u8,113u8,188u8,162u8,178u8,58u8,161u8,61u8,24u8,101u8, - 138u8,187u8,234u8,146u8,36u8,165u8,26u8,69u8,150u8,193u8,152u8,131u8,135u8,105u8,78u8,192u8,74u8,156u8,3u8,248u8, - 200u8,125u8,66u8,55u8,169u8,210u8,157u8,168u8,50u8,35u8,29u8,134u8,76u8,216u8,16u8,103u8,128u8,31u8,32u8,252u8, - 116u8,239u8,131u8,192u8,62u8,227u8,166u8,174u8,4u8,173u8,111u8,72u8,155u8,10u8,94u8,41u8,216u8,24u8,255u8,18u8, - 22u8,241u8,116u8,4u8,120u8,126u8,164u8,0u8,25u8,113u8,231u8,172u8,31u8,38u8,52u8,28u8,105u8,60u8,185u8,157u8, - 225u8,240u8,217u8,68u8,105u8,5u8,166u8,62u8,82u8,2u8,55u8,121u8,235u8,119u8,57u8,151u8,195u8,75u8,209u8,134u8, - 47u8,158u8,137u8,2u8,229u8,107u8,162u8,23u8,159u8,254u8,251u8,58u8,162u8,160u8,176u8,126u8,45u8,115u8,233u8,195u8, - 213u8,111u8,192u8,189u8,93u8,186u8,57u8,140u8,33u8,53u8,36u8,50u8,126u8,167u8,89u8,244u8,119u8,252u8,242u8,53u8, - 127u8,222u8,45u8,237u8,62u8,148u8,58u8,253u8,29u8,251u8,199u8,117u8,130u8,158u8,254u8,11u8,108u8,141u8,135u8,92u8, - 247u8,62u8,46u8,63u8,7u8,11u8,199u8,60u8,212u8,114u8,65u8,175u8,98u8,93u8,222u8,149u8,178u8,97u8,72u8,54u8, - 240u8,219u8,99u8,9u8,71u8,254u8,143u8,38u8,28u8,230u8,95u8,79u8,56u8,14u8,68u8,106u8,8u8,42u8,217u8,143u8, - 196u8,188u8,163u8,151u8,35u8,84u8,238u8,112u8,187u8,243u8,17u8,86u8,18u8,218u8,242u8,224u8,55u8,42u8,52u8,50u8, - 186u8,157u8,34u8,69u8,220u8,8u8,216u8,223u8,234u8,5u8,11u8,163u8,189u8,150u8,16u8,193u8,216u8,252u8,139u8,241u8, - 30u8,105u8,192u8,209u8,87u8,59u8,12u8,215u8,244u8,195u8,123u8,63u8,188u8,247u8,255u8,1u8,165u8,252u8,94u8,242u8, - 127u8,193u8,0u8,0u8,0u8,0u8,10u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,238u8,20u8,31u8, - 139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,189u8,89u8,109u8,111u8,27u8,185u8,17u8,254u8,158u8,95u8,193u8, - 224u8,0u8,87u8,10u8,116u8,114u8,123u8,45u8,250u8,65u8,118u8,2u8,168u8,177u8,210u8,26u8,112u8,227u8,32u8,246u8, - 181u8,119u8,40u8,138u8,53u8,181u8,75u8,73u8,172u8,87u8,203u8,189u8,37u8,87u8,178u8,206u8,240u8,127u8,239u8,204u8, - 240u8,101u8,185u8,187u8,146u8,95u8,146u8,220u8,229u8,67u8,18u8,81u8,228u8,188u8,60u8,51u8,243u8,204u8,144u8,58u8, - 62u8,62u8,102u8,83u8,182u8,86u8,89u8,157u8,11u8,182u8,93u8,201u8,116u8,197u8,50u8,177u8,144u8,133u8,208u8,204u8, - 172u8,4u8,155u8,115u8,45u8,83u8,150u8,170u8,34u8,21u8,165u8,97u8,106u8,241u8,234u8,24u8,246u8,255u8,231u8,77u8, - 202u8,75u8,62u8,151u8,185u8,52u8,82u8,232u8,55u8,255u8,29u8,172u8,140u8,41u8,245u8,228u8,248u8,88u8,20u8,227u8, - 173u8,188u8,149u8,165u8,200u8,36u8,31u8,171u8,106u8,121u8,140u8,159u8,142u8,223u8,251u8,205u8,187u8,239u8,65u8,156u8, - 200u8,18u8,45u8,210u8,186u8,130u8,143u8,67u8,182u8,80u8,21u8,91u8,243u8,130u8,47u8,101u8,177u8,100u8,60u8,77u8, - 133u8,214u8,168u8,204u8,84u8,42u8,31u8,163u8,42u8,82u8,55u8,251u8,233u8,211u8,236u8,243u8,249u8,63u8,103u8,31u8, - 175u8,167u8,23u8,97u8,241u8,59u8,118u8,185u8,17u8,213u8,70u8,138u8,109u8,88u8,154u8,178u8,96u8,214u8,142u8,73u8, - 205u8,56u8,171u8,11u8,80u8,176u8,20u8,124u8,14u8,190u8,25u8,117u8,43u8,10u8,231u8,161u8,17u8,218u8,200u8,133u8, - 36u8,31u8,185u8,129u8,141u8,90u8,46u8,11u8,81u8,177u8,21u8,135u8,83u8,181u8,89u8,169u8,74u8,254u8,42u8,50u8, - 88u8,79u8,69u8,101u8,184u8,44u8,152u8,42u8,69u8,197u8,141u8,84u8,5u8,89u8,197u8,174u8,87u8,94u8,30u8,168u8, - 217u8,240u8,92u8,102u8,44u8,3u8,143u8,192u8,7u8,196u8,204u8,84u8,188u8,208u8,60u8,197u8,237u8,160u8,79u8,84u8, - 130u8,73u8,131u8,27u8,213u8,28u8,101u8,137u8,108u8,204u8,174u8,36u8,224u8,105u8,247u8,238u8,74u8,193u8,110u8,26u8, - 187u8,39u8,19u8,64u8,235u8,6u8,13u8,33u8,69u8,133u8,98u8,222u8,33u8,163u8,216u8,92u8,48u8,109u8,84u8,5u8, - 150u8,129u8,73u8,203u8,92u8,205u8,121u8,206u8,214u8,98u8,173u8,170u8,221u8,136u8,197u8,17u8,129u8,15u8,69u8,161u8, - 12u8,203u8,5u8,191u8,101u8,170u8,198u8,184u8,129u8,47u8,145u8,89u8,99u8,246u8,1u8,128u8,23u8,0u8,224u8,142u8, - 45u8,234u8,130u8,214u8,72u8,95u8,202u8,243u8,28u8,196u8,111u8,165u8,89u8,129u8,10u8,222u8,113u8,5u8,161u8,35u8, - 136u8,98u8,160u8,233u8,115u8,201u8,43u8,190u8,22u8,70u8,84u8,35u8,231u8,235u8,178u8,134u8,149u8,194u8,8u8,16u8, - 70u8,16u8,163u8,175u8,205u8,33u8,210u8,133u8,146u8,230u8,2u8,48u8,244u8,184u8,176u8,141u8,228u8,40u8,171u8,66u8, - 180u8,93u8,72u8,108u8,198u8,132u8,160u8,80u8,12u8,0u8,4u8,81u8,194u8,54u8,177u8,145u8,170u8,214u8,249u8,14u8, - 209u8,232u8,224u8,254u8,7u8,205u8,196u8,29u8,100u8,89u8,8u8,153u8,205u8,153u8,239u8,216u8,143u8,154u8,47u8,69u8, - 88u8,56u8,47u8,0u8,47u8,8u8,223u8,175u8,148u8,126u8,5u8,232u8,73u8,127u8,169u8,37u8,5u8,178u8,133u8,39u8, - 56u8,84u8,235u8,26u8,176u8,217u8,49u8,81u8,192u8,55u8,186u8,206u8,185u8,177u8,97u8,224u8,161u8,118u8,0u8,51u8, - 68u8,12u8,2u8,74u8,178u8,13u8,95u8,58u8,200u8,32u8,26u8,76u8,21u8,112u8,22u8,226u8,7u8,201u8,173u8,77u8, - 85u8,167u8,120u8,122u8,14u8,65u8,93u8,129u8,104u8,43u8,160u8,49u8,243u8,230u8,230u8,134u8,254u8,117u8,130u8,63u8, - 221u8,46u8,39u8,147u8,15u8,130u8,155u8,26u8,50u8,233u8,158u8,190u8,97u8,96u8,14u8,230u8,66u8,54u8,153u8,116u8, - 82u8,231u8,36u8,72u8,97u8,204u8,214u8,4u8,101u8,24u8,26u8,83u8,107u8,107u8,49u8,236u8,58u8,117u8,226u8,222u8, - 141u8,217u8,37u8,26u8,22u8,153u8,65u8,214u8,166u8,21u8,124u8,47u8,0u8,17u8,216u8,175u8,13u8,135u8,100u8,29u8, - 69u8,50u8,17u8,40u8,99u8,243u8,90u8,99u8,134u8,150u8,245u8,60u8,7u8,146u8,240u8,137u8,196u8,20u8,126u8,137u8, - 97u8,47u8,216u8,149u8,200u8,23u8,147u8,137u8,69u8,213u8,211u8,75u8,37u8,64u8,117u8,129u8,89u8,3u8,149u8,83u8, - 11u8,76u8,78u8,210u8,143u8,134u8,142u8,35u8,53u8,215u8,184u8,184u8,229u8,144u8,219u8,177u8,121u8,152u8,53u8,139u8, - 58u8,207u8,61u8,77u8,176u8,149u8,218u8,182u8,92u8,162u8,236u8,147u8,27u8,204u8,171u8,218u8,120u8,121u8,22u8,115u8, - 230u8,81u8,68u8,33u8,25u8,164u8,25u8,187u8,127u8,232u8,224u8,21u8,50u8,130u8,184u8,161u8,29u8,28u8,216u8,210u8, - 184u8,10u8,200u8,248u8,157u8,3u8,61u8,97u8,71u8,54u8,93u8,135u8,33u8,64u8,40u8,142u8,189u8,183u8,56u8,54u8, - 49u8,26u8,91u8,175u8,86u8,188u8,44u8,5u8,64u8,128u8,180u8,202u8,160u8,58u8,156u8,111u8,65u8,164u8,77u8,115u8, - 35u8,215u8,65u8,49u8,137u8,187u8,44u8,4u8,43u8,160u8,164u8,52u8,146u8,1u8,148u8,201u8,70u8,102u8,112u8,28u8, - 51u8,175u8,64u8,226u8,68u8,50u8,157u8,11u8,207u8,66u8,106u8,139u8,140u8,6u8,224u8,58u8,167u8,99u8,65u8,174u8, - 96u8,126u8,128u8,64u8,134u8,202u8,109u8,52u8,157u8,158u8,242u8,44u8,147u8,104u8,2u8,39u8,152u8,237u8,255u8,33u8, - 96u8,121u8,174u8,182u8,36u8,94u8,69u8,222u8,91u8,152u8,26u8,23u8,223u8,189u8,11u8,130u8,226u8,220u8,180u8,41u8, - 21u8,162u8,52u8,208u8,35u8,118u8,228u8,62u8,220u8,63u8,12u8,79u8,220u8,153u8,110u8,56u8,166u8,54u8,115u8,116u8, - 135u8,63u8,208u8,130u8,173u8,170u8,110u8,109u8,209u8,145u8,1u8,11u8,43u8,107u8,79u8,160u8,92u8,246u8,197u8,81u8, - 154u8,180u8,51u8,230u8,254u8,69u8,174u8,251u8,108u8,126u8,158u8,223u8,110u8,247u8,75u8,29u8,63u8,83u8,224u8,180u8, - 86u8,16u8,153u8,21u8,170u8,173u8,132u8,37u8,28u8,80u8,143u8,72u8,120u8,103u8,169u8,17u8,17u8,103u8,67u8,27u8, - 173u8,181u8,129u8,104u8,106u8,44u8,172u8,86u8,137u8,247u8,17u8,201u8,84u8,18u8,68u8,15u8,18u8,176u8,182u8,141u8, - 7u8,36u8,49u8,27u8,143u8,199u8,214u8,38u8,247u8,183u8,227u8,36u8,79u8,163u8,103u8,34u8,23u8,75u8,238u8,219u8, - 6u8,173u8,190u8,111u8,53u8,32u8,16u8,239u8,131u8,3u8,169u8,88u8,58u8,68u8,157u8,221u8,152u8,149u8,111u8,178u8, - 32u8,226u8,205u8,152u8,253u8,11u8,152u8,255u8,198u8,146u8,133u8,91u8,23u8,55u8,35u8,228u8,159u8,144u8,197u8,188u8, - 219u8,62u8,144u8,167u8,50u8,129u8,1u8,181u8,84u8,101u8,89u8,199u8,53u8,114u8,219u8,44u8,121u8,174u8,149u8,61u8, - 150u8,147u8,206u8,134u8,221u8,219u8,233u8,52u8,102u8,23u8,242u8,214u8,246u8,99u8,104u8,50u8,48u8,139u8,96u8,240u8, - 49u8,95u8,85u8,101u8,121u8,15u8,191u8,241u8,118u8,197u8,21u8,40u8,180u8,40u8,12u8,195u8,60u8,104u8,244u8,218u8, - 110u8,214u8,77u8,88u8,88u8,33u8,142u8,107u8,90u8,148u8,30u8,71u8,40u8,162u8,55u8,164u8,9u8,172u8,134u8,190u8, - 6u8,163u8,133u8,237u8,134u8,14u8,19u8,187u8,4u8,136u8,84u8,208u8,237u8,55u8,214u8,1u8,28u8,112u8,236u8,192u8, - 4u8,38u8,175u8,12u8,91u8,84u8,106u8,221u8,50u8,180u8,105u8,40u8,255u8,94u8,201u8,92u8,68u8,227u8,92u8,187u8, - 155u8,174u8,69u8,10u8,84u8,45u8,245u8,154u8,8u8,196u8,159u8,166u8,166u8,135u8,71u8,52u8,208u8,3u8,54u8,121u8, - 138u8,102u8,10u8,211u8,71u8,171u8,53u8,34u8,57u8,251u8,126u8,7u8,147u8,22u8,141u8,26u8,77u8,100u8,217u8,154u8, - 76u8,195u8,185u8,165u8,158u8,255u8,79u8,164u8,244u8,53u8,160u8,102u8,42u8,105u8,49u8,112u8,45u8,161u8,16u8,54u8, - 183u8,113u8,99u8,41u8,82u8,28u8,205u8,50u8,106u8,51u8,48u8,161u8,208u8,7u8,199u8,155u8,17u8,74u8,153u8,2u8, - 50u8,68u8,136u8,220u8,28u8,36u8,139u8,13u8,175u8,36u8,140u8,28u8,218u8,179u8,155u8,147u8,148u8,90u8,75u8,114u8, - 94u8,192u8,76u8,178u8,20u8,110u8,244u8,185u8,227u8,235u8,50u8,23u8,35u8,220u8,171u8,170u8,204u8,70u8,14u8,39u8, - 10u8,140u8,103u8,214u8,74u8,110u8,172u8,125u8,248u8,22u8,124u8,195u8,252u8,66u8,136u8,226u8,84u8,28u8,49u8,180u8, - 3u8,115u8,17u8,59u8,50u8,213u8,166u8,242u8,92u8,17u8,76u8,154u8,244u8,90u8,59u8,107u8,190u8,68u8,145u8,168u8, - 130u8,79u8,24u8,112u8,79u8,133u8,1u8,181u8,67u8,99u8,204u8,34u8,232u8,75u8,2u8,93u8,11u8,139u8,181u8,161u8, - 18u8,62u8,156u8,4u8,206u8,105u8,254u8,228u8,162u8,24u8,244u8,206u8,134u8,176u8,198u8,167u8,135u8,236u8,237u8,91u8, - 246u8,199u8,147u8,94u8,129u8,95u8,201u8,181u8,204u8,121u8,149u8,83u8,235u8,221u8,235u8,17u8,176u8,111u8,157u8,103u8, - 48u8,12u8,129u8,241u8,52u8,201u8,114u8,211u8,100u8,14u8,160u8,186u8,0u8,132u8,37u8,140u8,105u8,133u8,25u8,89u8, - 98u8,210u8,128u8,167u8,94u8,236u8,154u8,145u8,154u8,20u8,1u8,226u8,25u8,6u8,72u8,252u8,190u8,8u8,57u8,121u8, - 52u8,13u8,61u8,15u8,169u8,125u8,82u8,208u8,68u8,157u8,208u8,244u8,31u8,14u8,37u8,32u8,58u8,113u8,44u8,55u8, - 200u8,134u8,109u8,104u8,93u8,151u8,231u8,165u8,81u8,58u8,233u8,12u8,111u8,208u8,131u8,88u8,60u8,214u8,137u8,170u8, - 82u8,213u8,73u8,123u8,205u8,114u8,76u8,103u8,113u8,3u8,85u8,133u8,59u8,95u8,49u8,215u8,57u8,154u8,219u8,22u8, - 86u8,154u8,170u8,49u8,62u8,60u8,7u8,54u8,203u8,118u8,54u8,40u8,56u8,118u8,196u8,5u8,130u8,165u8,150u8,166u8, - 170u8,46u8,12u8,201u8,160u8,9u8,149u8,205u8,222u8,79u8,63u8,77u8,255u8,118u8,126u8,113u8,126u8,253u8,115u8,50u8, - 189u8,248u8,60u8,155u8,158u8,253u8,156u8,204u8,126u8,58u8,191u8,186u8,190u8,154u8,176u8,250u8,175u8,127u8,97u8,111u8, - 217u8,159u8,78u8,30u8,85u8,137u8,247u8,143u8,5u8,8u8,205u8,14u8,8u8,253u8,120u8,121u8,157u8,124u8,184u8,252u8, - 241u8,227u8,153u8,151u8,247u8,67u8,35u8,111u8,106u8,205u8,129u8,26u8,23u8,154u8,4u8,173u8,248u8,166u8,225u8,53u8, - 152u8,89u8,68u8,181u8,150u8,90u8,35u8,129u8,196u8,210u8,207u8,102u8,23u8,179u8,191u8,79u8,175u8,103u8,94u8,226u8, - 159u8,35u8,84u8,154u8,27u8,91u8,37u8,28u8,111u8,219u8,153u8,223u8,183u8,243u8,172u8,213u8,15u8,222u8,219u8,11u8, - 84u8,235u8,194u8,229u8,111u8,90u8,115u8,184u8,80u8,165u8,170u8,244u8,20u8,133u8,115u8,36u8,92u8,120u8,129u8,127u8, - 133u8,200u8,119u8,99u8,210u8,232u8,198u8,77u8,108u8,170u8,37u8,112u8,171u8,1u8,102u8,14u8,195u8,6u8,206u8,158u8, - 112u8,28u8,4u8,217u8,9u8,244u8,149u8,79u8,166u8,74u8,41u8,19u8,50u8,156u8,86u8,31u8,26u8,251u8,167u8,44u8, - 135u8,43u8,18u8,175u8,144u8,17u8,53u8,13u8,217u8,237u8,150u8,104u8,157u8,107u8,104u8,18u8,77u8,183u8,3u8,255u8, - 226u8,128u8,147u8,76u8,175u8,168u8,134u8,97u8,163u8,43u8,227u8,44u8,104u8,179u8,84u8,76u8,231u8,233u8,226u8,66u8, - 115u8,42u8,81u8,95u8,209u8,110u8,30u8,45u8,119u8,47u8,200u8,194u8,131u8,78u8,191u8,200u8,93u8,156u8,175u8,97u8, - 40u8,197u8,78u8,156u8,113u8,195u8,157u8,10u8,156u8,29u8,208u8,140u8,118u8,12u8,49u8,254u8,11u8,185u8,172u8,59u8, - 65u8,236u8,68u8,226u8,202u8,224u8,252u8,185u8,215u8,178u8,91u8,177u8,139u8,12u8,11u8,4u8,48u8,97u8,182u8,184u8, - 78u8,157u8,145u8,239u8,190u8,153u8,149u8,77u8,42u8,31u8,182u8,215u8,13u8,7u8,226u8,217u8,118u8,63u8,10u8,168u8, - 189u8,137u8,224u8,108u8,88u8,136u8,109u8,156u8,5u8,105u8,14u8,35u8,227u8,136u8,230u8,44u8,119u8,35u8,21u8,52u8, - 69u8,194u8,39u8,203u8,55u8,99u8,122u8,87u8, - ]; - let chunk2 = vector[ - 233u8,141u8,152u8,254u8,210u8,129u8,212u8,223u8,228u8,205u8,10u8,44u8,2u8,81u8,36u8,229u8,198u8,217u8,122u8, - 99u8,175u8,160u8,209u8,69u8,131u8,181u8,199u8,209u8,238u8,213u8,128u8,102u8,190u8,48u8,172u8,143u8,152u8,39u8,212u8, - 196u8,233u8,156u8,132u8,1u8,122u8,24u8,185u8,159u8,195u8,240u8,129u8,206u8,67u8,225u8,219u8,131u8,48u8,123u8,91u8, - 44u8,18u8,181u8,176u8,50u8,135u8,39u8,97u8,55u8,122u8,88u8,153u8,215u8,131u8,215u8,150u8,18u8,79u8,67u8,130u8, - 120u8,43u8,128u8,245u8,225u8,244u8,112u8,196u8,136u8,137u8,65u8,150u8,165u8,208u8,196u8,238u8,31u8,28u8,230u8,199u8, - 97u8,164u8,5u8,134u8,53u8,145u8,24u8,181u8,79u8,58u8,25u8,52u8,10u8,137u8,9u8,35u8,119u8,47u8,239u8,160u8, - 11u8,172u8,75u8,179u8,27u8,12u8,217u8,131u8,147u8,25u8,231u8,158u8,191u8,25u8,237u8,99u8,1u8,119u8,169u8,143u8, - 46u8,129u8,157u8,113u8,212u8,71u8,189u8,85u8,209u8,81u8,98u8,142u8,130u8,30u8,228u8,18u8,93u8,195u8,160u8,233u8, - 158u8,133u8,236u8,228u8,217u8,188u8,135u8,61u8,157u8,29u8,143u8,101u8,69u8,80u8,115u8,56u8,59u8,122u8,23u8,168u8, - 74u8,252u8,82u8,195u8,8u8,249u8,220u8,20u8,105u8,223u8,109u8,72u8,58u8,247u8,216u8,121u8,244u8,71u8,189u8,130u8, - 139u8,18u8,171u8,125u8,85u8,116u8,101u8,70u8,109u8,31u8,27u8,254u8,97u8,243u8,32u8,108u8,143u8,4u8,205u8,209u8, - 121u8,63u8,118u8,231u8,244u8,126u8,86u8,151u8,254u8,174u8,215u8,122u8,154u8,5u8,20u8,117u8,12u8,161u8,130u8,136u8, - 165u8,240u8,10u8,16u8,52u8,192u8,96u8,100u8,95u8,95u8,20u8,52u8,251u8,82u8,105u8,209u8,168u8,130u8,96u8,23u8, - 170u8,248u8,190u8,167u8,248u8,16u8,226u8,137u8,221u8,249u8,21u8,192u8,55u8,13u8,225u8,43u8,224u8,239u8,11u8,249u8, - 202u8,32u8,252u8,67u8,228u8,165u8,69u8,200u8,159u8,143u8,58u8,228u8,152u8,125u8,118u8,207u8,83u8,136u8,61u8,170u8, - 9u8,51u8,103u8,175u8,138u8,44u8,112u8,136u8,216u8,51u8,12u8,137u8,31u8,31u8,98u8,142u8,126u8,25u8,24u8,143u8, - 147u8,92u8,227u8,117u8,67u8,65u8,208u8,255u8,7u8,13u8,199u8,181u8,155u8,74u8,135u8,235u8,98u8,54u8,245u8,202u8, - 16u8,129u8,196u8,105u8,156u8,43u8,160u8,194u8,109u8,98u8,47u8,92u8,79u8,74u8,27u8,227u8,209u8,147u8,150u8,64u8, - 0u8,63u8,83u8,53u8,94u8,198u8,211u8,149u8,72u8,111u8,45u8,59u8,4u8,163u8,233u8,201u8,30u8,186u8,39u8,61u8, - 179u8,86u8,98u8,137u8,247u8,7u8,108u8,150u8,244u8,192u8,236u8,137u8,169u8,37u8,207u8,147u8,248u8,97u8,14u8,15u8, - 230u8,55u8,68u8,142u8,215u8,10u8,28u8,217u8,53u8,110u8,29u8,132u8,161u8,49u8,166u8,237u8,88u8,182u8,167u8,98u8, - 124u8,110u8,132u8,107u8,139u8,30u8,28u8,245u8,96u8,56u8,172u8,116u8,28u8,221u8,137u8,142u8,172u8,25u8,175u8,186u8, - 119u8,136u8,151u8,153u8,21u8,100u8,135u8,213u8,7u8,38u8,114u8,45u8,58u8,161u8,123u8,26u8,153u8,54u8,40u8,48u8, - 236u8,38u8,52u8,173u8,15u8,246u8,206u8,232u8,61u8,112u8,90u8,6u8,116u8,11u8,236u8,96u8,5u8,129u8,85u8,42u8, - 149u8,52u8,247u8,132u8,183u8,33u8,251u8,84u8,251u8,84u8,11u8,11u8,178u8,93u8,17u8,250u8,135u8,36u8,122u8,4u8, - 82u8,212u8,147u8,122u8,52u8,22u8,160u8,106u8,170u8,177u8,247u8,212u8,245u8,4u8,129u8,121u8,195u8,27u8,112u8,65u8, - 2u8,37u8,246u8,55u8,240u8,249u8,80u8,7u8,232u8,250u8,97u8,247u8,37u8,135u8,220u8,233u8,243u8,227u8,55u8,116u8, - 202u8,22u8,97u8,84u8,128u8,120u8,245u8,160u8,199u8,72u8,234u8,253u8,231u8,54u8,26u8,126u8,161u8,115u8,207u8,116u8, - 15u8,247u8,225u8,119u8,1u8,188u8,206u8,5u8,217u8,248u8,94u8,7u8,115u8,241u8,216u8,45u8,176u8,235u8,203u8,179u8, - 203u8,9u8,246u8,172u8,92u8,209u8,47u8,5u8,174u8,141u8,209u8,219u8,90u8,184u8,168u8,64u8,167u8,91u8,151u8,10u8, - 159u8,21u8,0u8,149u8,91u8,188u8,100u8,110u8,105u8,134u8,129u8,146u8,52u8,93u8,216u8,124u8,233u8,125u8,97u8,244u8, - 193u8,120u8,213u8,112u8,246u8,126u8,170u8,126u8,54u8,49u8,27u8,245u8,165u8,140u8,108u8,127u8,44u8,233u8,77u8,148u8, - 32u8,177u8,223u8,39u8,250u8,13u8,210u8,7u8,54u8,12u8,144u8,174u8,120u8,19u8,56u8,182u8,6u8,20u8,7u8,71u8, - 107u8,184u8,204u8,182u8,8u8,45u8,129u8,149u8,125u8,124u8,225u8,69u8,181u8,56u8,141u8,140u8,60u8,233u8,167u8,13u8, - 190u8,86u8,62u8,150u8,52u8,240u8,163u8,81u8,200u8,153u8,195u8,185u8,242u8,219u8,231u8,136u8,125u8,87u8,253u8,226u8, - 12u8,193u8,39u8,215u8,80u8,79u8,195u8,231u8,182u8,115u8,218u8,119u8,223u8,74u8,135u8,215u8,79u8,231u8,3u8,234u8, - 218u8,147u8,15u8,152u8,119u8,253u8,121u8,193u8,133u8,63u8,177u8,177u8,135u8,156u8,164u8,180u8,65u8,17u8,79u8,170u8, - 104u8,68u8,211u8,91u8,179u8,248u8,86u8,169u8,114u8,20u8,73u8,223u8,59u8,140u8,89u8,117u8,56u8,138u8,57u8,141u8, - 246u8,69u8,155u8,187u8,203u8,80u8,51u8,108u8,181u8,237u8,58u8,133u8,231u8,30u8,124u8,89u8,120u8,55u8,216u8,64u8, - 104u8,208u8,70u8,119u8,105u8,159u8,65u8,240u8,238u8,96u8,105u8,214u8,189u8,36u8,14u8,168u8,209u8,225u8,211u8,111u8, - 38u8,238u8,224u8,9u8,52u8,220u8,182u8,104u8,1u8,203u8,117u8,3u8,39u8,59u8,5u8,75u8,103u8,186u8,3u8,146u8, - 63u8,104u8,45u8,194u8,99u8,86u8,230u8,201u8,225u8,222u8,216u8,248u8,11u8,105u8,19u8,59u8,139u8,43u8,61u8,87u8, - 163u8,82u8,125u8,202u8,207u8,150u8,155u8,148u8,83u8,189u8,201u8,5u8,236u8,59u8,186u8,27u8,30u8,114u8,162u8,172u8, - 245u8,42u8,153u8,243u8,244u8,214u8,186u8,223u8,115u8,225u8,225u8,213u8,255u8,1u8,21u8,209u8,13u8,158u8,43u8,34u8, - 0u8,0u8,0u8,0u8,10u8,99u8,111u8,109u8,112u8,97u8,114u8,97u8,116u8,111u8,114u8,137u8,9u8,31u8,139u8,8u8, - 0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,87u8,95u8,143u8,226u8,54u8,16u8,127u8,231u8,83u8,184u8,119u8,18u8, - 10u8,109u8,116u8,139u8,179u8,176u8,165u8,97u8,23u8,105u8,123u8,218u8,246u8,101u8,43u8,181u8,123u8,219u8,167u8,211u8, - 41u8,50u8,224u8,64u8,116u8,73u8,156u8,198u8,206u8,2u8,173u8,246u8,187u8,119u8,28u8,59u8,193u8,129u8,36u8,92u8, - 224u8,218u8,74u8,229u8,1u8,136u8,231u8,223u8,207u8,51u8,191u8,25u8,59u8,87u8,87u8,87u8,232u8,215u8,148u8,189u8, - 4u8,75u8,202u8,17u8,65u8,126u8,74u8,34u8,186u8,97u8,233u8,103u8,228u8,179u8,20u8,45u8,88u8,148u8,144u8,52u8, - 136u8,87u8,72u8,108u8,24u8,162u8,33u8,141u8,104u8,44u8,120u8,47u8,98u8,203u8,44u8,164u8,136u8,36u8,130u8,113u8, - 143u8,139u8,165u8,235u8,42u8,69u8,34u8,192u8,230u8,175u8,30u8,130u8,79u8,198u8,41u8,202u8,37u8,243u8,5u8,159u8, - 86u8,87u8,94u8,232u8,2u8,244u8,166u8,189u8,124u8,117u8,193u8,98u8,46u8,208u8,195u8,111u8,191u8,223u8,63u8,186u8, - 40u8,155u8,160u8,59u8,52u8,156u8,26u8,130u8,15u8,191u8,220u8,63u8,62u8,62u8,60u8,105u8,17u8,54u8,69u8,63u8, - 63u8,61u8,220u8,63u8,151u8,34u8,71u8,251u8,227u8,34u8,205u8,22u8,2u8,61u8,81u8,158u8,133u8,2u8,173u8,9u8, - 71u8,203u8,148u8,37u8,26u8,148u8,252u8,4u8,113u8,76u8,83u8,105u8,100u8,231u8,75u8,175u8,202u8,44u8,201u8,230u8, - 97u8,176u8,64u8,126u8,22u8,163u8,128u8,123u8,244u8,143u8,140u8,132u8,86u8,154u8,251u8,112u8,81u8,95u8,57u8,27u8, - 184u8,104u8,206u8,88u8,104u8,184u8,82u8,10u8,239u8,114u8,143u8,232u8,238u8,78u8,237u8,162u8,217u8,43u8,143u8,72u8, - 24u8,210u8,212u8,19u8,107u8,18u8,119u8,119u8,174u8,51u8,209u8,236u8,126u8,149u8,82u8,34u8,206u8,118u8,175u8,179u8, - 105u8,186u8,151u8,196u8,160u8,41u8,208u8,32u8,146u8,196u8,208u8,84u8,224u8,44u8,70u8,204u8,207u8,233u8,32u8,118u8, - 137u8,164u8,140u8,15u8,65u8,209u8,143u8,239u8,63u8,32u8,78u8,211u8,128u8,132u8,193u8,159u8,68u8,4u8,44u8,126u8, - 87u8,184u8,144u8,18u8,40u8,61u8,71u8,97u8,32u8,4u8,144u8,134u8,198u8,203u8,128u8,196u8,240u8,179u8,96u8,75u8, - 73u8,44u8,73u8,51u8,200u8,11u8,212u8,69u8,208u8,21u8,120u8,202u8,189u8,218u8,133u8,53u8,103u8,102u8,224u8,57u8, - 21u8,27u8,74u8,99u8,148u8,164u8,65u8,20u8,136u8,224u8,133u8,86u8,173u8,208u8,38u8,0u8,71u8,49u8,19u8,160u8, - 184u8,38u8,32u8,133u8,234u8,211u8,109u8,2u8,116u8,163u8,203u8,18u8,207u8,79u8,16u8,143u8,110u8,73u8,148u8,132u8, - 212u8,70u8,216u8,26u8,110u8,241u8,64u8,217u8,205u8,41u8,10u8,73u8,154u8,59u8,131u8,12u8,34u8,103u8,124u8,35u8, - 133u8,195u8,225u8,160u8,125u8,139u8,70u8,17u8,20u8,82u8,122u8,251u8,60u8,179u8,66u8,234u8,203u8,236u8,63u8,219u8, - 40u8,13u8,86u8,235u8,252u8,47u8,212u8,64u8,179u8,114u8,95u8,133u8,144u8,10u8,36u8,85u8,189u8,249u8,78u8,0u8, - 254u8,59u8,4u8,221u8,226u8,186u8,130u8,169u8,231u8,220u8,203u8,96u8,90u8,209u8,206u8,221u8,53u8,168u8,231u8,178u8, - 129u8,110u8,4u8,213u8,40u8,57u8,32u8,47u8,155u8,120u8,170u8,233u8,172u8,125u8,48u8,219u8,116u8,53u8,232u8,82u8, - 119u8,229u8,234u8,54u8,155u8,204u8,56u8,130u8,100u8,74u8,7u8,122u8,141u8,55u8,100u8,228u8,0u8,128u8,107u8,184u8, - 40u8,19u8,180u8,95u8,106u8,77u8,84u8,72u8,227u8,149u8,88u8,195u8,214u8,149u8,190u8,235u8,170u8,5u8,171u8,223u8, - 152u8,171u8,70u8,139u8,163u8,116u8,73u8,155u8,96u8,185u8,85u8,51u8,168u8,92u8,221u8,172u8,3u8,160u8,173u8,37u8, - 5u8,183u8,21u8,12u8,253u8,62u8,82u8,139u8,102u8,156u8,129u8,1u8,250u8,168u8,194u8,224u8,249u8,219u8,2u8,197u8, - 156u8,165u8,41u8,219u8,40u8,220u8,182u8,116u8,100u8,128u8,63u8,46u8,118u8,157u8,101u8,46u8,45u8,76u8,43u8,182u8, - 129u8,143u8,246u8,149u8,46u8,1u8,202u8,135u8,67u8,120u8,106u8,20u8,136u8,44u8,141u8,203u8,156u8,23u8,19u8,82u8, - 15u8,28u8,96u8,133u8,169u8,252u8,10u8,167u8,0u8,12u8,242u8,106u8,132u8,217u8,121u8,17u8,244u8,204u8,57u8,140u8, - 80u8,205u8,131u8,42u8,136u8,252u8,254u8,174u8,152u8,255u8,90u8,171u8,119u8,180u8,93u8,93u8,152u8,246u8,138u8,156u8, - 222u8,231u8,225u8,30u8,181u8,219u8,89u8,39u8,183u8,199u8,155u8,211u8,110u8,219u8,205u8,242u8,51u8,196u8,52u8,50u8, - 59u8,243u8,237u8,71u8,232u8,85u8,241u8,233u8,176u8,203u8,228u8,34u8,28u8,195u8,242u8,156u8,230u8,150u8,137u8,171u8, - 60u8,114u8,149u8,240u8,128u8,234u8,47u8,36u8,204u8,232u8,16u8,146u8,171u8,164u8,174u8,155u8,9u8,127u8,98u8,205u8, - 223u8,144u8,48u8,89u8,147u8,55u8,7u8,189u8,148u8,43u8,227u8,99u8,101u8,24u8,201u8,245u8,186u8,78u8,189u8,110u8, - 174u8,92u8,106u8,19u8,14u8,19u8,85u8,124u8,99u8,149u8,103u8,110u8,95u8,79u8,12u8,171u8,175u8,208u8,217u8,72u8, - 255u8,25u8,12u8,108u8,52u8,52u8,226u8,156u8,178u8,196u8,133u8,37u8,150u8,150u8,184u8,131u8,165u8,83u8,88u8,58u8, - 210u8,210u8,105u8,128u8,91u8,57u8,109u8,155u8,80u8,231u8,177u8,175u8,235u8,99u8,87u8,110u8,3u8,77u8,224u8,243u8, - 109u8,143u8,26u8,32u8,180u8,121u8,24u8,86u8,54u8,49u8,174u8,135u8,208u8,182u8,7u8,167u8,2u8,225u8,230u8,12u8, - 8u8,184u8,2u8,225u8,251u8,11u8,32u8,228u8,105u8,156u8,104u8,7u8,53u8,173u8,240u8,246u8,99u8,113u8,202u8,123u8, - 62u8,9u8,194u8,44u8,165u8,245u8,45u8,162u8,47u8,11u8,149u8,22u8,129u8,195u8,238u8,43u8,92u8,2u8,170u8,45u8, - 5u8,183u8,75u8,236u8,24u8,247u8,213u8,106u8,15u8,149u8,82u8,112u8,254u8,31u8,181u8,194u8,217u8,108u8,194u8,69u8, - 75u8,116u8,44u8,101u8,149u8,208u8,215u8,205u8,165u8,60u8,44u8,153u8,76u8,85u8,165u8,94u8,53u8,89u8,30u8,183u8, - 102u8,25u8,143u8,157u8,186u8,233u8,180u8,183u8,198u8,120u8,42u8,89u8,0u8,101u8,246u8,253u8,255u8,195u8,100u8,250u8, - 151u8,11u8,121u8,217u8,100u8,186u8,190u8,116u8,50u8,141u8,46u8,158u8,76u8,227u8,75u8,39u8,211u8,77u8,29u8,157u8, - 61u8,22u8,135u8,187u8,79u8,230u8,43u8,233u8,123u8,38u8,223u8,57u8,182u8,117u8,239u8,164u8,5u8,161u8,139u8,59u8, - 48u8,48u8,115u8,102u8,87u8,165u8,120u8,255u8,202u8,90u8,174u8,73u8,18u8,223u8,140u8,236u8,47u8,237u8,165u8,133u8, - 2u8,208u8,208u8,78u8,222u8,208u8,184u8,34u8,211u8,40u8,17u8,59u8,203u8,72u8,76u8,33u8,72u8,50u8,190u8,246u8, - 230u8,100u8,241u8,217u8,234u8,71u8,217u8,222u8,20u8,120u8,60u8,236u8,162u8,253u8,67u8,23u8,229u8,241u8,160u8,246u8, - 226u8,226u8,225u8,243u8,1u8,227u8,78u8,128u8,113u8,23u8,192u8,184u8,202u8,168u8,211u8,56u8,14u8,119u8,55u8,39u8, - 92u8,94u8,247u8,11u8,186u8,84u8,175u8,139u8,37u8,83u8,138u8,236u8,28u8,75u8,129u8,41u8,248u8,186u8,102u8,29u8, - 216u8,50u8,194u8,118u8,237u8,5u8,90u8,134u8,101u8,98u8,13u8,108u8,31u8,126u8,105u8,100u8,252u8,181u8,35u8,227u8, - 147u8,145u8,23u8,44u8,217u8,157u8,218u8,248u8,232u8,220u8,240u8,206u8,63u8,149u8,114u8,167u8,62u8,114u8,203u8,172u8, - 151u8,4u8,128u8,1u8,35u8,127u8,90u8,206u8,150u8,134u8,1u8,167u8,141u8,117u8,53u8,91u8,78u8,152u8,134u8,249u8, - 166u8,13u8,141u8,248u8,206u8,249u8,241u8,113u8,247u8,17u8,175u8,13u8,141u8,248u8,163u8,243u8,227u8,159u8,49u8,223u8, - 181u8,161u8,17u8,127u8,63u8,222u8,95u8,123u8,127u8,3u8,199u8,100u8,98u8,66u8,45u8,21u8,0u8,0u8,0u8,0u8, - 12u8,99u8,111u8,112u8,121u8,97u8,98u8,108u8,101u8,95u8,97u8,110u8,121u8,246u8,4u8,31u8,139u8,8u8,0u8,0u8, - 0u8,0u8,0u8,2u8,255u8,117u8,147u8,219u8,138u8,219u8,48u8,16u8,134u8,239u8,243u8,20u8,83u8,10u8,193u8,166u8, - 38u8,105u8,77u8,41u8,69u8,57u8,192u8,182u8,44u8,180u8,23u8,11u8,75u8,227u8,94u8,148u8,82u8,28u8,217u8,86u8, - 18u8,81u8,71u8,114u8,117u8,200u8,198u8,148u8,188u8,123u8,71u8,82u8,226u8,56u8,238u8,174u8,33u8,216u8,145u8,102u8, - 52u8,255u8,255u8,105u8,102u8,47u8,43u8,91u8,51u8,160u8,141u8,145u8,58u8,215u8,166u8,34u8,164u8,148u8,77u8,75u8, - 139u8,154u8,229u8,84u8,180u8,240u8,119u8,4u8,248u8,88u8,125u8,19u8,97u8,218u8,134u8,229u8,92u8,108u8,228u8,236u8, - 185u8,221u8,141u8,146u8,251u8,188u8,40u8,245u8,229u8,171u8,53u8,76u8,95u8,3u8,125u8,8u8,238u8,14u8,86u8,152u8, - 82u8,82u8,13u8,214u8,180u8,81u8,92u8,108u8,9u8,89u8,249u8,247u8,108u8,228u8,119u8,167u8,211u8,41u8,100u8,59u8, - 6u8,78u8,3u8,52u8,74u8,30u8,120u8,197u8,42u8,216u8,72u8,5u8,107u8,43u8,26u8,90u8,254u8,94u8,3u8,215u8, - 32u8,164u8,1u8,131u8,81u8,154u8,238u8,81u8,155u8,134u8,39u8,252u8,109u8,249u8,129u8,137u8,16u8,233u8,227u8,38u8, - 254u8,188u8,82u8,10u8,109u8,224u8,62u8,251u8,241u8,120u8,159u8,63u8,124u8,93u8,61u8,220u8,101u8,159u8,191u8,16u8, - 176u8,31u8,222u8,195u8,2u8,222u8,14u8,106u8,94u8,78u8,91u8,35u8,25u8,66u8,238u8,68u8,187u8,134u8,194u8,26u8, - 120u8,226u8,102u8,231u8,171u8,57u8,114u8,64u8,11u8,94u8,115u8,211u8,134u8,211u8,209u8,130u8,45u8,13u8,96u8,40u8, - 236u8,48u8,177u8,82u8,178u8,73u8,112u8,81u8,42u8,150u8,132u8,232u8,192u8,215u8,61u8,30u8,170u8,192u8,10u8,4u8, - 130u8,225u8,164u8,219u8,170u8,168u8,161u8,4u8,14u8,172u8,196u8,196u8,185u8,253u8,184u8,244u8,27u8,167u8,171u8,182u8, - 71u8,180u8,3u8,20u8,14u8,180u8,182u8,12u8,184u8,48u8,210u8,171u8,89u8,123u8,129u8,138u8,53u8,138u8,105u8,38u8, - 12u8,53u8,92u8,138u8,9u8,124u8,98u8,37u8,117u8,132u8,157u8,164u8,146u8,10u8,40u8,88u8,208u8,83u8,37u8,94u8, - 93u8,227u8,62u8,168u8,168u8,156u8,58u8,142u8,96u8,205u8,14u8,105u8,114u8,221u8,149u8,162u8,181u8,150u8,120u8,230u8, - 31u8,203u8,149u8,195u8,142u8,23u8,12u8,235u8,236u8,140u8,178u8,177u8,69u8,205u8,75u8,216u8,88u8,1u8,14u8,240u8, - 60u8,35u8,254u8,72u8,120u8,19u8,42u8,224u8,219u8,89u8,94u8,70u8,71u8,2u8,89u8,76u8,188u8,130u8,171u8,253u8, - 219u8,127u8,3u8,32u8,93u8,195u8,157u8,123u8,207u8,173u8,206u8,179u8,101u8,20u8,39u8,55u8,25u8,129u8,147u8,111u8, - 61u8,35u8,67u8,227u8,69u8,227u8,99u8,220u8,197u8,156u8,134u8,224u8,190u8,251u8,142u8,233u8,208u8,121u8,59u8,47u8, - 163u8,203u8,28u8,11u8,90u8,72u8,101u8,16u8,201u8,198u8,7u8,134u8,60u8,119u8,181u8,151u8,142u8,99u8,199u8,6u8, - 239u8,201u8,145u8,115u8,13u8,250u8,28u8,155u8,208u8,166u8,78u8,254u8,209u8,67u8,64u8,20u8,89u8,207u8,58u8,213u8, - 154u8,41u8,243u8,42u8,122u8,209u8,51u8,44u8,22u8,112u8,156u8,116u8,107u8,9u8,248u8,217u8,33u8,132u8,11u8,84u8, - 195u8,171u8,156u8,170u8,173u8,221u8,163u8,234u8,232u8,182u8,169u8,227u8,120u8,214u8,213u8,184u8,14u8,166u8,151u8,49u8, - 113u8,224u8,226u8,33u8,156u8,111u8,204u8,88u8,37u8,180u8,119u8,229u8,205u8,184u8,106u8,32u8,55u8,161u8,39u8,80u8, - 248u8,208u8,88u8,39u8,201u8,25u8,27u8,7u8,103u8,227u8,208u8,203u8,61u8,127u8,227u8,158u8,246u8,126u8,201u8,215u8, - 63u8,81u8,142u8,201u8,165u8,168u8,219u8,95u8,253u8,217u8,89u8,121u8,188u8,231u8,153u8,9u8,3u8,20u8,38u8,7u8, - 142u8,97u8,78u8,111u8,242u8,67u8,170u8,87u8,227u8,78u8,195u8,65u8,69u8,96u8,255u8,195u8,61u8,223u8,1u8,166u8, - 47u8,35u8,247u8,21u8,165u8,105u8,236u8,193u8,166u8,105u8,2u8,239u8,122u8,164u8,6u8,9u8,171u8,115u8,248u8,42u8, - 148u8,79u8,83u8,56u8,133u8,188u8,222u8,66u8,2u8,233u8,249u8,128u8,211u8,232u8,52u8,250u8,7u8,131u8,195u8,231u8, - 51u8,96u8,5u8,0u8,0u8,0u8,0u8,7u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,141u8,19u8,31u8,139u8,8u8, - 0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,89u8,255u8,111u8,219u8,182u8,18u8,255u8,189u8,127u8,5u8,95u8,6u8, - 4u8,118u8,225u8,47u8,139u8,211u8,230u8,117u8,78u8,90u8,32u8,77u8,189u8,46u8,216u8,107u8,90u8,212u8,110u8,134u8, - 189u8,161u8,144u8,105u8,137u8,182u8,249u8,44u8,139u8,122u8,34u8,149u8,44u8,27u8,242u8,191u8,239u8,142u8,212u8,23u8, - 202u8,162u8,108u8,39u8,107u8,55u8,44u8,104u8,145u8,72u8,162u8,142u8,119u8,159u8,59u8,222u8,125u8,238u8,212u8,239u8, - 247u8,201u8,133u8,136u8,20u8,229u8,145u8,36u8,243u8,52u8,242u8,21u8,23u8,248u8,151u8,72u8,134u8,79u8,250u8,253u8, - 62u8,254u8,39u8,228u8,168u8,71u8,126u8,25u8,5u8,131u8,231u8,207u8,143u8,190u8,251u8,220u8,90u8,42u8,21u8,203u8, - 97u8,191u8,207u8,162u8,222u8,45u8,95u8,241u8,152u8,5u8,156u8,246u8,68u8,178u8,232u8,227u8,85u8,127u8,20u8,188u8, - 25u8,159u8,127u8,147u8,45u8,110u8,147u8,128u8,47u8,184u8,162u8,33u8,145u8,124u8,17u8,81u8,149u8,38u8,76u8,14u8, - 9u8,239u8,177u8,94u8,135u8,232u8,133u8,214u8,125u8,34u8,110u8,88u8,2u8,119u8,111u8,105u8,18u8,72u8,253u8,54u8, - 241u8,211u8,228u8,6u8,30u8,220u8,114u8,181u8,36u8,190u8,232u8,206u8,169u8,175u8,68u8,66u8,94u8,104u8,189u8,214u8, - 34u8,72u8,67u8,70u8,104u8,172u8,132u8,244u8,164u8,10u8,134u8,67u8,102u8,246u8,36u8,191u8,63u8,33u8,240u8,147u8, - 74u8,70u8,244u8,237u8,153u8,47u8,79u8,139u8,59u8,214u8,114u8,117u8,23u8,51u8,143u8,71u8,115u8,49u8,28u8,254u8, - 62u8,102u8,225u8,188u8,67u8,38u8,112u8,231u8,18u8,110u8,220u8,159u8,86u8,37u8,136u8,24u8,49u8,41u8,150u8,189u8, - 215u8,151u8,176u8,72u8,175u8,2u8,85u8,204u8,47u8,50u8,74u8,18u8,208u8,206u8,23u8,1u8,147u8,249u8,147u8,236u8, - 119u8,159u8,252u8,148u8,136u8,104u8,65u8,162u8,116u8,61u8,3u8,27u8,197u8,156u8,204u8,238u8,20u8,26u8,198u8,18u8, - 70u8,22u8,252u8,134u8,69u8,132u8,74u8,194u8,163u8,56u8,85u8,228u8,118u8,9u8,87u8,32u8,130u8,37u8,156u8,134u8, - 252u8,55u8,14u8,175u8,209u8,136u8,100u8,120u8,146u8,56u8,157u8,133u8,220u8,39u8,43u8,118u8,215u8,211u8,178u8,125u8, - 240u8,149u8,34u8,35u8,239u8,167u8,143u8,239u8,175u8,222u8,122u8,31u8,62u8,189u8,254u8,113u8,244u8,179u8,55u8,190u8, - 252u8,239u8,104u8,72u8,210u8,147u8,103u8,228u8,37u8,57u8,58u8,253u8,242u8,74u8,20u8,62u8,115u8,233u8,48u8,190u8, - 124u8,123u8,117u8,62u8,249u8,244u8,113u8,84u8,81u8,99u8,80u8,67u8,235u8,2u8,223u8,162u8,145u8,170u8,99u8,53u8, - 89u8,50u8,194u8,3u8,22u8,41u8,62u8,231u8,70u8,81u8,5u8,119u8,106u8,155u8,19u8,233u8,47u8,217u8,154u8,117u8, - 64u8,89u8,238u8,47u8,9u8,151u8,232u8,176u8,32u8,87u8,61u8,225u8,55u8,168u8,245u8,57u8,250u8,155u8,208u8,20u8, - 36u8,128u8,60u8,159u8,162u8,231u8,16u8,61u8,9u8,198u8,147u8,37u8,149u8,75u8,88u8,84u8,108u8,204u8,21u8,81u8, - 98u8,193u8,96u8,109u8,98u8,98u8,110u8,39u8,240u8,150u8,177u8,23u8,63u8,140u8,222u8,141u8,188u8,203u8,55u8,96u8, - 241u8,11u8,48u8,248u8,219u8,211u8,170u8,65u8,146u8,255u8,198u8,208u8,20u8,74u8,114u8,80u8,65u8,215u8,82u8,104u8, - 7u8,128u8,55u8,254u8,176u8,197u8,131u8,63u8,255u8,115u8,121u8,225u8,161u8,79u8,175u8,62u8,189u8,243u8,94u8,255u8, - 60u8,25u8,141u8,115u8,68u8,143u8,7u8,251u8,236u8,80u8,160u8,229u8,222u8,160u8,212u8,191u8,38u8,255u8,228u8,89u8, - 205u8,101u8,99u8,149u8,164u8,254u8,134u8,195u8,190u8,249u8,5u8,68u8,42u8,79u8,68u8,225u8,221u8,103u8,75u8,29u8, - 240u8,134u8,212u8,171u8,201u8,82u8,132u8,129u8,172u8,68u8,15u8,243u8,19u8,166u8,208u8,104u8,240u8,43u8,85u8,196u8, - 135u8,103u8,51u8,102u8,156u8,167u8,4u8,89u8,176u8,136u8,37u8,84u8,57u8,28u8,46u8,73u8,144u8,38u8,232u8,84u8, - 220u8,17u8,126u8,27u8,67u8,178u8,109u8,198u8,90u8,234u8,143u8,76u8,123u8,149u8,4u8,137u8,136u8,179u8,60u8,128u8, - 63u8,218u8,238u8,33u8,185u8,97u8,152u8,63u8,206u8,210u8,23u8,175u8,244u8,147u8,251u8,18u8,191u8,115u8,242u8,250u8, - 98u8,220u8,205u8,113u8,163u8,51u8,72u8,44u8,107u8,38u8,37u8,93u8,20u8,225u8,37u8,34u8,166u8,53u8,133u8,28u8, - 197u8,231u8,119u8,149u8,180u8,5u8,55u8,57u8,37u8,211u8,226u8,150u8,103u8,214u8,64u8,150u8,73u8,184u8,175u8,60u8, - 53u8,173u8,232u8,9u8,171u8,88u8,240u8,206u8,8u8,63u8,203u8,126u8,99u8,226u8,121u8,229u8,82u8,188u8,204u8,81u8, - 69u8,114u8,234u8,20u8,15u8,121u8,4u8,72u8,13u8,137u8,37u8,163u8,83u8,179u8,43u8,34u8,79u8,211u8,232u8,6u8, - 140u8,10u8,0u8,210u8,224u8,169u8,35u8,156u8,135u8,36u8,18u8,138u8,68u8,204u8,71u8,49u8,9u8,15u8,239u8,208u8, - 87u8,44u8,12u8,57u8,100u8,57u8,223u8,100u8,96u8,18u8,11u8,30u8,169u8,14u8,249u8,95u8,10u8,33u8,131u8,225u8, - 245u8,255u8,148u8,69u8,190u8,14u8,182u8,227u8,129u8,1u8,215u8,54u8,241u8,83u8,185u8,225u8,7u8,189u8,77u8,238u8, - 21u8,95u8,196u8,16u8,232u8,104u8,98u8,7u8,214u8,10u8,56u8,194u8,15u8,114u8,209u8,211u8,135u8,218u8,65u8,226u8, - 132u8,175u8,89u8,87u8,36u8,144u8,14u8,114u8,27u8,124u8,145u8,134u8,1u8,198u8,219u8,154u8,255u8,202u8,2u8,243u8, - 172u8,67u8,102u8,152u8,243u8,120u8,24u8,194u8,251u8,88u8,132u8,102u8,172u8,216u8,22u8,204u8,93u8,211u8,48u8,180u8, - 133u8,244u8,242u8,135u8,197u8,162u8,239u8,33u8,239u8,71u8,226u8,182u8,3u8,1u8,93u8,6u8,62u8,252u8,133u8,26u8, - 233u8,184u8,134u8,179u8,71u8,163u8,59u8,19u8,60u8,121u8,22u8,42u8,106u8,173u8,217u8,30u8,146u8,207u8,154u8,47u8, - 150u8,10u8,117u8,131u8,229u8,152u8,242u8,230u8,105u8,153u8,100u8,51u8,161u8,215u8,95u8,5u8,217u8,56u8,77u8,98u8, - 145u8,128u8,84u8,71u8,150u8,181u8,207u8,168u8,209u8,31u8,150u8,109u8,11u8,248u8,41u8,1u8,52u8,182u8,156u8,134u8, - 222u8,230u8,113u8,48u8,251u8,252u8,73u8,51u8,242u8,28u8,245u8,125u8,142u8,106u8,173u8,172u8,124u8,160u8,137u8,132u8, - 227u8,138u8,192u8,154u8,34u8,151u8,199u8,46u8,150u8,61u8,186u8,251u8,160u8,24u8,189u8,179u8,107u8,112u8,30u8,196u8, - 202u8,173u8,103u8,189u8,227u8,153u8,71u8,30u8,44u8,245u8,230u8,137u8,88u8,123u8,90u8,120u8,171u8,166u8,119u8,123u8, - 232u8,62u8,33u8,165u8,169u8,84u8,66u8,38u8,82u8,255u8,106u8,105u8,242u8,97u8,94u8,28u8,14u8,67u8,22u8,45u8, - 212u8,178u8,117u8,168u8,229u8,181u8,201u8,203u8,151u8,206u8,234u8,208u8,49u8,140u8,133u8,33u8,21u8,25u8,14u8,185u8, - 217u8,199u8,163u8,201u8,34u8,93u8,67u8,1u8,108u8,57u8,136u8,66u8,187u8,125u8,90u8,108u8,236u8,214u8,43u8,67u8, - 233u8,126u8,51u8,110u8,182u8,3u8,186u8,227u8,184u8,58u8,209u8,124u8,12u8,150u8,134u8,141u8,157u8,213u8,15u8,198u8, - 43u8,11u8,81u8,62u8,39u8,45u8,75u8,100u8,190u8,15u8,164u8,86u8,197u8,146u8,136u8,134u8,70u8,114u8,187u8,109u8, - 189u8,130u8,63u8,57u8,241u8,147u8,98u8,205u8,90u8,215u8,219u8,92u8,86u8,137u8,210u8,202u8,221u8,251u8,118u8,113u8, - 121u8,15u8,121u8,85u8,178u8,134u8,61u8,34u8,168u8,48u8,46u8,43u8,90u8,214u8,251u8,59u8,61u8,0u8,149u8,219u8, - 242u8,192u8,150u8,99u8,237u8,132u8,191u8,60u8,179u8,59u8,48u8,47u8,79u8,237u8,131u8,131u8,214u8,193u8,56u8,246u8, - 140u8,217u8,42u8,177u8,180u8,195u8,214u8,82u8,167u8,41u8,86u8,129u8,109u8,66u8,30u8,82u8,136u8,138u8,195u8,143u8, - 64u8,59u8,32u8,1u8,184u8,162u8,63u8,47u8,255u8,54u8,73u8,201u8,178u8,179u8,73u8,104u8,213u8,156u8,126u8,254u8, - 225u8,82u8,214u8,160u8,181u8,2u8,79u8,9u8,59u8,99u8,180u8,226u8,213u8,144u8,28u8,214u8,245u8,217u8,157u8,33u8, - 118u8,60u8,182u8,242u8,101u8,188u8,234u8,85u8,99u8,178u8,6u8,205u8,59u8,129u8,61u8,150u8,19u8,23u8,56u8,30u8, - 127u8,21u8,50u8,184u8,85u8,13u8,155u8,191u8,27u8,154u8,113u8,78u8,164u8,101u8,19u8,10u8,121u8,29u8,3u8,156u8, - 142u8,7u8,93u8,139u8,95u8,91u8,86u8,54u8,212u8,8u8,176u8,215u8,156u8,48u8,29u8,5u8,46u8,225u8,109u8,251u8, - 216u8,89u8,54u8,84u8,20u8,111u8,84u8,247u8,250u8,81u8,202u8,238u8,161u8,234u8,245u8,23u8,85u8,212u8,58u8,189u8, - 165u8,122u8,39u8,207u8,26u8,212u8,43u8,83u8,84u8,161u8,19u8,220u8,2u8,165u8,10u8,41u8,77u8,186u8,192u8,50u8, - 183u8,50u8,19u8,186u8,98u8,210u8,48u8,180u8,13u8,14u8,80u8,86u8,43u8,120u8,22u8,16u8,170u8,20u8,91u8,199u8, - 144u8,67u8,64u8,193u8,124u8,21u8,208u8,182u8,130u8,14u8,146u8,143u8,12u8,20u8,128u8,217u8,201u8,116u8,236u8,174u8, - 23u8,237u8,41u8,22u8,34u8,153u8,250u8,200u8,81u8,231u8,105u8,168u8,165u8,78u8,175u8,32u8,241u8,3u8,103u8,194u8, - 174u8,243u8,150u8,75u8,182u8,237u8,144u8,228u8,187u8,110u8,13u8,153u8,189u8,10u8,226u8,62u8,213u8,54u8,247u8,94u8, - 123u8,19u8,177u8,107u8,67u8,3u8,221u8,37u8,166u8,100u8,126u8,83u8,136u8,124u8,36u8,205u8,53u8,92u8,167u8,229u8, - 126u8,83u8,108u8,159u8,116u8,206u8,136u8,153u8,111u8,184u8,229u8,52u8,235u8,188u8,166u8,189u8,106u8,63u8,233u8,3u8, - 13u8,55u8,36u8,189u8,0u8,31u8,95u8,180u8,156u8,4u8,45u8,61u8,140u8,4u8,252u8,21u8,246u8,136u8,92u8,19u8, - 240u8,171u8,247u8,147u8,34u8,41u8,33u8,141u8,7u8,240u8,103u8,139u8,68u8,164u8,241u8,150u8,192u8,170u8,240u8,213u8, - 150u8,29u8,63u8,102u8,129u8,29u8,108u8,101u8,51u8,86u8,154u8,212u8,224u8,153u8,114u8,105u8,102u8,95u8,141u8,203u8, - 130u8,239u8,102u8,66u8,132u8,213u8,152u8,117u8,146u8,232u8,130u8,182u8,148u8,245u8,92u8,123u8,170u8,99u8,169u8,145u8, - 223u8,201u8,118u8,171u8,57u8,81u8,99u8,154u8,247u8,32u8,197u8,244u8,4u8,67u8,219u8,116u8,184u8,212u8,234u8,1u8, - 132u8,105u8,95u8,106u8,61u8,50u8,182u8,167u8,100u8,210u8,43u8,251u8,31u8,131u8,250u8,134u8,36u8,64u8,191u8,216u8, - 53u8,78u8,4u8,116u8,141u8,240u8,15u8,90u8,179u8,27u8,116u8,33u8,250u8,77u8,220u8,66u8,35u8,11u8,163u8,152u8, - 216u8,12u8,111u8,18u8,161u8,168u8,210u8,35u8,167u8,250u8,216u8,102u8,95u8,191u8,121u8,234u8,108u8,50u8,212u8,157u8, - 196u8,171u8,150u8,219u8,115u8,123u8,120u8,140u8,192u8,53u8,133u8,206u8,187u8,238u8,152u8,16u8,6u8,24u8,208u8,2u8, - 195u8,168u8,47u8,128u8,81u8,73u8,165u8,167u8,223u8,168u8,53u8,86u8,7u8,111u8,13u8,28u8,245u8,159u8,98u8,126u8, - 54u8,1u8,122u8,215u8,169u8,172u8,207u8,154u8,122u8,220u8,185u8,124u8,144u8,207u8,26u8,191u8,84u8,76u8,192u8,72u8, - 20u8,116u8,200u8,19u8,232u8,97u8,102u8,73u8,187u8,22u8,35u8,63u8,176u8,48u8,134u8,3u8,188u8,134u8,145u8,152u8, - 208u8,30u8,213u8,83u8,35u8,157u8,164u8,233u8,134u8,213u8,230u8,118u8,35u8,173u8,132u8,36u8,147u8,5u8,98u8,233u8, - 151u8,18u8,221u8,234u8,80u8,100u8,98u8,39u8,170u8,175u8,143u8,237u8,166u8,209u8,111u8,112u8,118u8,152u8,177u8,106u8, - 61u8,62u8,236u8,102u8,185u8,201u8,119u8,4u8,100u8,62u8,157u8,52u8,243u8,211u8,61u8,218u8,156u8,102u8,50u8,80u8, - 21u8,142u8,119u8,31u8,193u8,12u8,74u8,137u8,218u8,183u8,141u8,114u8,27u8,50u8,251u8,215u8,181u8,253u8,161u8,150u8, - 95u8,255u8,147u8,237u8,222u8,40u8,221u8,219u8,213u8,242u8,92u8,93u8,150u8,155u8,198u8,216u8,237u8,85u8,156u8,202u8, - 165u8,55u8,163u8,254u8,170u8,117u8,184u8,134u8,238u8,47u8,151u8,211u8,113u8,205u8,165u8,173u8,126u8,73u8,203u8,192u8, - 225u8,55u8,116u8,182u8,75u8,122u8,236u8,13u8,158u8,159u8,20u8,58u8,84u8,160u8,113u8,14u8,119u8,223u8,102u8,3u8, - 218u8,202u8,80u8,23u8,97u8,137u8,41u8,79u8,106u8,62u8,207u8,199u8,185u8,104u8,168u8,108u8,129u8,85u8,173u8,98u8, - 82u8,219u8,113u8,241u8,251u8,141u8,36u8,219u8,146u8,133u8,77u8,133u8,134u8,144u8,113u8,43u8,82u8,203u8,212u8,103u8, - 217u8,136u8,47u8,203u8,21u8,38u8,231u8,98u8,48u8,236u8,108u8,2u8,114u8,249u8,118u8,174u8,181u8,101u8,196u8,40u8, - 227u8,122u8,239u8,142u8,162u8,89u8,24u8,88u8,210u8,137u8,87u8,143u8,132u8,183u8,44u8,197u8,240u8,93u8,14u8,114u8, - 175u8,9u8,61u8,220u8,139u8,208u8,36u8,161u8,119u8,80u8,114u8,117u8,193u8,204u8,30u8,224u8,114u8,188u8,118u8,29u8, - 66u8,124u8,6u8,205u8,245u8,140u8,171u8,132u8,38u8,119u8,57u8,135u8,198u8,227u8,102u8,57u8,102u8,45u8,23u8,123u8, - 180u8,252u8,174u8,123u8,54u8,180u8,184u8,83u8,89u8,150u8,86u8,5u8,35u8,145u8,139u8,134u8,153u8,198u8,99u8,1u8, - 201u8,172u8,214u8,37u8,8u8,238u8,5u8,58u8,197u8,63u8,20u8,19u8,243u8,58u8,148u8,167u8,140u8,53u8,108u8,64u8, - 82u8,173u8,86u8,155u8,102u8,87u8,9u8,65u8,189u8,234u8,233u8,98u8,231u8,30u8,89u8,60u8,4u8,186u8,173u8,133u8, - 219u8,81u8,205u8,242u8,169u8,232u8,21u8,36u8,155u8,27u8,86u8,142u8,156u8,107u8,195u8,81u8,211u8,186u8,144u8,41u8, - 96u8,192u8,116u8,151u8,130u8,9u8,206u8,204u8,82u8,128u8,63u8,87u8,8u8,123u8,214u8,240u8,199u8,56u8,123u8,10u8, - 204u8,160u8,73u8,39u8,118u8,231u8,76u8,222u8,124u8,234u8,5u8,90u8,24u8,227u8,124u8,72u8,150u8,60u8,16u8,7u8, - 233u8,178u8,43u8,162u8,174u8,249u8,194u8,128u8,157u8,16u8,76u8,203u8,187u8,60u8,234u8,154u8,121u8,123u8,78u8,212u8, - 13u8,163u8,151u8,142u8,38u8,107u8,78u8,97u8,152u8,86u8,107u8,156u8,162u8,194u8,82u8,178u8,123u8,224u8,183u8,17u8, - 227u8,72u8,242u8,78u8,107u8,152u8,32u8,36u8,57u8,34u8,174u8,38u8,7u8,138u8,66u8,209u8,178u8,228u8,19u8,114u8, - 0u8,101u8,129u8,31u8,212u8,85u8,245u8,45u8,11u8,198u8,102u8,131u8,96u8,43u8,198u8,209u8,168u8,97u8,177u8,164u8, - 91u8,221u8,48u8,201u8,37u8,17u8,237u8,16u8,154u8,232u8,79u8,49u8,183u8,250u8,235u8,46u8,126u8,5u8,148u8,246u8, - 139u8,182u8,235u8,2u8,193u8,204u8,71u8,9u8,244u8,70u8,205u8,7u8,154u8,196u8,55u8,187u8,160u8,211u8,160u8,206u8, - 23u8,19u8,170u8,187u8,52u8,187u8,231u8,40u8,97u8,45u8,246u8,48u8,156u8,183u8,230u8,234u8,157u8,164u8,216u8,213u8, - 198u8,149u8,190u8,119u8,183u8,113u8,174u8,231u8,59u8,122u8,183u8,211u8,63u8,87u8,55u8,45u8,139u8,154u8,42u8,28u8, - 20u8,80u8,75u8,47u8,59u8,126u8,79u8,255u8,234u8,162u8,178u8,129u8,191u8,157u8,175u8,42u8,224u8,57u8,170u8,73u8, - 121u8,81u8,251u8,200u8,60u8,1u8,237u8,119u8,124u8,98u8,206u8,90u8,16u8,92u8,153u8,55u8,6u8,213u8,175u8,71u8, - 246u8,23u8,84u8,174u8,194u8,6u8,95u8,67u8,51u8,163u8,128u8,137u8,213u8,31u8,86u8,170u8,209u8,231u8,130u8,205u8, - 105u8,45u8,192u8,47u8,58u8,181u8,231u8,129u8,230u8,139u8,245u8,76u8,180u8,28u8,180u8,5u8,124u8,3u8,229u8,126u8, - 147u8,171u8,108u8,82u8,20u8,77u8,47u8,182u8,141u8,65u8,81u8,136u8,213u8,253u8,225u8,59u8,0u8,231u8,81u8,133u8, - 29u8,190u8,36u8,191u8,30u8,124u8,123u8,52u8,56u8,126u8,246u8,252u8,228u8,223u8,47u8,190u8,163u8,51u8,63u8,96u8, - 243u8,131u8,13u8,42u8,196u8,23u8,71u8,176u8,206u8,89u8,252u8,15u8,81u8,89u8,20u8,106u8,233u8,86u8,76u8,243u8, - 27u8,198u8,34u8,135u8,40u8,176u8,67u8,14u8,227u8,252u8,85u8,247u8,16u8,95u8,42u8,156u8,86u8,29u8,181u8,29u8, - 38u8,12u8,64u8,29u8,219u8,131u8,27u8,173u8,157u8,113u8,219u8,236u8,0u8,71u8,105u8,100u8,130u8,87u8,7u8,213u8, - 102u8,174u8,240u8,222u8,236u8,96u8,130u8,159u8,40u8,97u8,238u8,0u8,179u8,184u8,131u8,78u8,19u8,145u8,3u8,133u8, - 7u8,57u8,2u8,38u8,128u8,140u8,225u8,24u8,53u8,90u8,159u8,253u8,173u8,247u8,140u8,253u8,131u8,204u8,254u8,82u8, - 194u8,54u8,16u8,6u8,249u8,23u8,11u8,136u8,174u8,39u8,247u8,79u8,254u8,0u8,48u8,83u8,131u8,13u8,119u8,37u8, - 0u8,0u8,0u8,0u8,7u8,109u8,97u8,116u8,104u8,49u8,50u8,56u8,215u8,18u8,31u8,139u8,8u8,0u8,0u8,0u8, - 0u8,0u8,2u8,255u8,205u8,25u8,107u8,111u8,219u8,200u8,241u8,187u8,127u8,197u8,4u8,69u8,81u8,50u8,146u8,37u8, - 145u8,146u8,252u8,146u8,29u8,32u8,197u8,37u8,7u8,3u8,105u8,92u8,228u8,218u8,43u8,218u8,32u8,22u8,40u8,113u8, - 37u8,45u8,64u8,237u8,234u8,150u8,164u8,45u8,245u8,224u8,255u8,222u8,153u8,93u8,46u8,223u8,122u8,56u8,189u8,3u8, - 78u8,128u8,45u8,106u8,119u8,222u8,51u8,59u8,143u8,101u8,191u8,223u8,135u8,159u8,146u8,64u8,132u8,129u8,10u8,97u8, - 29u8,36u8,43u8,72u8,19u8,30u8,241u8,132u8,179u8,24u8,214u8,60u8,142u8,185u8,88u8,2u8,23u8,144u8,172u8,24u8, - 252u8,77u8,62u8,49u8,248u8,20u8,136u8,101u8,26u8,44u8,89u8,239u8,108u8,45u8,195u8,52u8,98u8,16u8,108u8,18u8, - 25u8,79u8,227u8,36u8,188u8,185u8,33u8,116u8,207u8,191u8,130u8,95u8,207u8,206u8,0u8,63u8,105u8,204u8,64u8,175u8, - 47u8,248u8,150u8,133u8,211u8,141u8,228u8,34u8,25u8,250u8,55u8,55u8,31u8,233u8,231u8,223u8,205u8,175u8,201u8,1u8, - 200u8,137u8,33u8,211u8,71u8,9u8,223u8,207u8,164u8,74u8,224u8,41u8,136u8,82u8,6u8,207u8,43u8,38u8,32u8,16u8, - 40u8,21u8,254u8,230u8,33u8,4u8,106u8,153u8,174u8,153u8,72u8,128u8,199u8,176u8,81u8,242u8,137u8,135u8,44u8,236u8, - 105u8,204u8,185u8,20u8,113u8,2u8,31u8,238u8,63u8,255u8,252u8,254u8,211u8,253u8,15u8,211u8,247u8,95u8,126u8,156u8, - 126u8,252u8,244u8,240u8,240u8,101u8,250u8,233u8,225u8,71u8,255u8,6u8,210u8,139u8,17u8,220u8,129u8,55u8,41u8,195u8, - 254u8,112u8,255u8,243u8,253u8,79u8,247u8,15u8,159u8,167u8,127u8,253u8,247u8,244u8,63u8,31u8,190u8,60u8,88u8,168u8, - 178u8,48u8,95u8,88u8,146u8,42u8,99u8,146u8,8u8,153u8,51u8,196u8,147u8,11u8,72u8,158u8,37u8,136u8,116u8,61u8, - 99u8,42u8,54u8,220u8,55u8,233u8,44u8,226u8,115u8,88u8,164u8,2u8,237u8,186u8,117u8,2u8,36u8,133u8,198u8,233u8, - 194u8,204u8,60u8,184u8,230u8,11u8,141u8,5u8,217u8,135u8,47u8,192u8,9u8,224u8,221u8,29u8,204u8,92u8,8u8,128u8, - 69u8,104u8,148u8,153u8,222u8,124u8,105u8,229u8,29u8,175u8,131u8,40u8,58u8,133u8,57u8,23u8,39u8,51u8,191u8,61u8, - 145u8,119u8,240u8,196u8,20u8,6u8,65u8,198u8,186u8,193u8,50u8,219u8,126u8,29u8,219u8,98u8,145u8,62u8,1u8,116u8, - 192u8,153u8,193u8,57u8,4u8,46u8,244u8,193u8,207u8,247u8,94u8,140u8,112u8,85u8,224u8,25u8,1u8,7u8,8u8,60u8, - 171u8,1u8,183u8,171u8,16u8,35u8,245u8,183u8,136u8,212u8,135u8,57u8,44u8,37u8,69u8,120u8,178u8,82u8,50u8,93u8, - 174u8,140u8,116u8,137u8,196u8,56u8,98u8,79u8,58u8,164u8,68u8,194u8,212u8,154u8,133u8,60u8,72u8,80u8,89u8,212u8, - 106u8,17u8,201u8,231u8,178u8,182u8,92u8,68u8,92u8,48u8,99u8,231u8,52u8,154u8,134u8,252u8,169u8,161u8,116u8,23u8, - 230u8,251u8,180u8,119u8,28u8,148u8,58u8,136u8,33u8,245u8,199u8,23u8,46u8,138u8,132u8,10u8,231u8,191u8,250u8,224u8, - 204u8,243u8,95u8,174u8,126u8,34u8,26u8,123u8,156u8,178u8,133u8,121u8,20u8,172u8,55u8,44u8,36u8,233u8,201u8,67u8, - 90u8,114u8,60u8,32u8,240u8,21u8,37u8,102u8,170u8,11u8,233u8,102u8,195u8,212u8,183u8,134u8,171u8,52u8,154u8,179u8, - 181u8,162u8,106u8,96u8,251u8,67u8,163u8,236u8,147u8,157u8,194u8,74u8,3u8,116u8,117u8,120u8,103u8,92u8,182u8,174u8, - 123u8,32u8,108u8,204u8,1u8,198u8,160u8,17u8,160u8,2u8,30u8,27u8,105u8,55u8,132u8,9u8,172u8,46u8,24u8,46u8, - 59u8,194u8,74u8,194u8,14u8,197u8,15u8,131u8,187u8,59u8,24u8,212u8,3u8,200u8,59u8,28u8,50u8,17u8,75u8,96u8, - 83u8,28u8,127u8,251u8,121u8,94u8,113u8,204u8,105u8,72u8,242u8,29u8,120u8,117u8,138u8,5u8,191u8,63u8,131u8,79u8, - 60u8,91u8,33u8,180u8,26u8,72u8,120u8,131u8,254u8,20u8,147u8,198u8,246u8,75u8,115u8,9u8,229u8,199u8,191u8,62u8, - 248u8,205u8,45u8,129u8,91u8,162u8,73u8,168u8,70u8,68u8,179u8,58u8,30u8,244u8,24u8,187u8,82u8,161u8,163u8,150u8, - 190u8,99u8,221u8,84u8,178u8,183u8,222u8,157u8,154u8,221u8,194u8,218u8,101u8,91u8,147u8,201u8,20u8,150u8,5u8,52u8, - 118u8,193u8,63u8,136u8,99u8,166u8,146u8,55u8,206u8,22u8,222u8,224u8,122u8,215u8,164u8,113u8,166u8,148u8,84u8,55u8, - 55u8,89u8,134u8,158u8,218u8,12u8,237u8,236u8,73u8,197u8,174u8,91u8,208u8,67u8,121u8,63u8,44u8,22u8,108u8,158u8, - 240u8,39u8,22u8,237u8,116u8,212u8,108u8,100u8,140u8,245u8,72u8,10u8,157u8,109u8,240u8,247u8,90u8,98u8,210u8,139u8, - 249u8,82u8,240u8,5u8,159u8,7u8,120u8,74u8,99u8,20u8,108u8,198u8,147u8,138u8,160u8,100u8,185u8,139u8,81u8,65u8, - 55u8,115u8,172u8,64u8,199u8,54u8,66u8,133u8,156u8,186u8,165u8,196u8,235u8,120u8,112u8,123u8,11u8,194u8,109u8,243u8, - 235u8,22u8,9u8,34u8,208u8,187u8,54u8,167u8,26u8,171u8,208u8,255u8,206u8,17u8,87u8,25u8,135u8,34u8,149u8,82u8, - 220u8,149u8,64u8,144u8,68u8,213u8,123u8,185u8,243u8,50u8,183u8,213u8,189u8,86u8,243u8,87u8,185u8,180u8,214u8,60u8, - 71u8,9u8,97u8,201u8,212u8,116u8,19u8,96u8,37u8,189u8,171u8,184u8,187u8,106u8,254u8,207u8,82u8,97u8,101u8,225u8, - 255u8,101u8,168u8,46u8,30u8,207u8,175u8,94u8,23u8,124u8,151u8,122u8,0u8,93u8,156u8,65u8,23u8,103u8,24u8,250u8, - 189u8,179u8,134u8,249u8,180u8,245u8,134u8,126u8,221u8,122u8,185u8,229u8,156u8,138u8,12u8,231u8,4u8,58u8,57u8,124u8, - 74u8,13u8,42u8,82u8,117u8,80u8,159u8,243u8,138u8,14u8,110u8,171u8,1u8,73u8,211u8,133u8,10u8,230u8,213u8,32u8, - 165u8,213u8,144u8,69u8,73u8,0u8,86u8,72u8,175u8,17u8,25u8,102u8,255u8,77u8,75u8,38u8,65u8,155u8,160u8,157u8, - 180u8,44u8,94u8,223u8,55u8,207u8,143u8,126u8,29u8,100u8,75u8,38u8,50u8,198u8,106u8,168u8,128u8,230u8,121u8,139u8, - 169u8,145u8,108u8,48u8,172u8,157u8,114u8,131u8,25u8,131u8,144u8,207u8,150u8,192u8,200u8,173u8,67u8,160u8,133u8,13u8, - 121u8,159u8,118u8,233u8,16u8,136u8,66u8,36u8,140u8,57u8,122u8,70u8,30u8,253u8,58u8,239u8,34u8,176u8,253u8,204u8, - 53u8,168u8,155u8,181u8,143u8,254u8,234u8,24u8,187u8,76u8,10u8,39u8,121u8,147u8,122u8,204u8,90u8,203u8,153u8,239u8, - 189u8,177u8,91u8,235u8,242u8,230u8,138u8,97u8,209u8,156u8,46u8,148u8,92u8,79u8,85u8,240u8,60u8,53u8,185u8,31u8, - 43u8,94u8,37u8,8u8,168u8,172u8,93u8,160u8,74u8,89u8,224u8,116u8,180u8,84u8,121u8,9u8,105u8,164u8,176u8,248u8, - 151u8,52u8,80u8,12u8,148u8,148u8,186u8,237u8,217u8,118u8,169u8,82u8,207u8,177u8,146u8,96u8,170u8,48u8,233u8,45u8, - 254u8,69u8,37u8,109u8,233u8,205u8,172u8,31u8,42u8,35u8,219u8,172u8,140u8,40u8,83u8,175u8,6u8,181u8,67u8,129u8, - 13u8,128u8,78u8,70u8,81u8,26u8,163u8,205u8,179u8,150u8,152u8,109u8,145u8,63u8,54u8,201u8,82u8,244u8,224u8,83u8, - 150u8,117u8,178u8,147u8,165u8,15u8,22u8,60u8,51u8,88u8,97u8,55u8,100u8,221u8,247u8,40u8,48u8,62u8,30u8,127u8, - 21u8,29u8,239u8,5u8,75u8,186u8,192u8,250u8,183u8,66u8,114u8,186u8,163u8,18u8,49u8,149u8,65u8,46u8,202u8,92u8, - 105u8,99u8,21u8,68u8,139u8,115u8,185u8,97u8,162u8,84u8,210u8,253u8,71u8,71u8,244u8,125u8,87u8,211u8,114u8,144u8, - 152u8,219u8,247u8,95u8,220u8,30u8,124u8,148u8,88u8,71u8,177u8,111u8,65u8,49u8,144u8,47u8,166u8,70u8,120u8,86u8, - 92u8,203u8,141u8,1u8,134u8,150u8,46u8,240u8,180u8,53u8,208u8,222u8,36u8,11u8,225u8,150u8,153u8,46u8,144u8,138u8, - 12u8,67u8,208u8,242u8,58u8,25u8,121u8,183u8,159u8,161u8,16u8,207u8,124u8,177u8,194u8,82u8,247u8,195u8,108u8,145u8, - 0u8,19u8,54u8,75u8,32u8,219u8,204u8,219u8,57u8,85u8,130u8,82u8,124u8,185u8,74u8,202u8,44u8,219u8,48u8,122u8, - 112u8,191u8,208u8,106u8,172u8,164u8,196u8,188u8,128u8,226u8,203u8,20u8,201u8,112u8,133u8,201u8,63u8,216u8,96u8,163u8, - 191u8,229u8,56u8,111u8,80u8,89u8,176u8,237u8,14u8,177u8,41u8,40u8,89u8,203u8,35u8,34u8,246u8,39u8,124u8,157u8, - 174u8,203u8,28u8,21u8,139u8,2u8,42u8,49u8,160u8,75u8,21u8,48u8,142u8,68u8,20u8,56u8,214u8,46u8,231u8,84u8, - 219u8,113u8,29u8,11u8,2u8,62u8,90u8,221u8,93u8,152u8,73u8,28u8,144u8,40u8,2u8,77u8,243u8,173u8,204u8,81u8, - 196u8,172u8,208u8,107u8,169u8,146u8,58u8,211u8,56u8,78u8,37u8,211u8,98u8,132u8,123u8,58u8,15u8,120u8,213u8,164u8, - 251u8,47u8,150u8,13u8,65u8,217u8,36u8,38u8,216u8,115u8,34u8,197u8,185u8,90u8,5u8,155u8,24u8,85u8,68u8,47u8, - 170u8,76u8,89u8,9u8,124u8,77u8,99u8,14u8,203u8,244u8,198u8,210u8,136u8,33u8,81u8,177u8,72u8,175u8,76u8,249u8, - 31u8,43u8,171u8,36u8,181u8,178u8,232u8,44u8,25u8,61u8,49u8,29u8,14u8,250u8,60u8,79u8,121u8,199u8,179u8,103u8, - 123u8,202u8,31u8,125u8,74u8,34u8,224u8,224u8,49u8,11u8,137u8,225u8,156u8,230u8,34u8,108u8,123u8,151u8,76u8,204u8, - 153u8,91u8,161u8,123u8,159u8,128u8,57u8,147u8,50u8,197u8,167u8,85u8,128u8,94u8,89u8,32u8,7u8,24u8,23u8,194u8, - 154u8,200u8,54u8,105u8,3u8,29u8,91u8,216u8,12u8,35u8,211u8,127u8,60u8,199u8,193u8,42u8,63u8,2u8,51u8,134u8, - 13u8,164u8,6u8,79u8,208u8,120u8,43u8,25u8,133u8,189u8,179u8,106u8,117u8,117u8,76u8,121u8,165u8,44u8,135u8,79u8, - 110u8,45u8,15u8,253u8,209u8,96u8,168u8,59u8,198u8,229u8,110u8,190u8,95u8,46u8,235u8,205u8,217u8,97u8,206u8,184u8, - 25u8,30u8,242u8,70u8,124u8,183u8,47u8,85u8,161u8,233u8,11u8,104u8,132u8,115u8,109u8,202u8,113u8,48u8,131u8,117u8, - 96u8,103u8,98u8,183u8,95u8,219u8,40u8,22u8,59u8,165u8,206u8,24u8,105u8,149u8,145u8,230u8,50u8,141u8,66u8,136u8, - 55u8,169u8,226u8,50u8,141u8,49u8,165u8,218u8,137u8,167u8,7u8,177u8,164u8,3u8,69u8,33u8,106u8,198u8,94u8,242u8, - 52u8,110u8,82u8,230u8,107u8,77u8,162u8,181u8,97u8,46u8,107u8,18u8,119u8,89u8,147u8,216u8,24u8,179u8,221u8,106u8, - 197u8,25u8,212u8,154u8,89u8,221u8,37u8,83u8,119u8,80u8,210u8,36u8,87u8,36u8,51u8,234u8,159u8,190u8,38u8,56u8, - 12u8,127u8,43u8,27u8,24u8,91u8,78u8,181u8,211u8,246u8,165u8,173u8,105u8,110u8,182u8,178u8,120u8,86u8,180u8,124u8, - 243u8,186u8,11u8,67u8,151u8,180u8,24u8,118u8,81u8,145u8,201u8,126u8,64u8,111u8,96u8,33u8,71u8,199u8,32u8,189u8, - 147u8,33u8,253u8,147u8,33u8,135u8,22u8,114u8,108u8,32u8,171u8,213u8,170u8,58u8,172u8,182u8,82u8,192u8,143u8,71u8, - 195u8,229u8,237u8,45u8,197u8,25u8,218u8,245u8,186u8,152u8,50u8,187u8,224u8,121u8,198u8,8u8,131u8,235u8,225u8,232u8, - 242u8,98u8,112u8,225u8,95u8,123u8,151u8,23u8,190u8,63u8,188u8,30u8,123u8,190u8,55u8,190u8,186u8,28u8,142u8,6u8, - 254u8,213u8,213u8,213u8,192u8,187u8,198u8,197u8,66u8,214u8,151u8,19u8,125u8,65u8,35u8,163u8,219u8,156u8,45u8,210u8, - 136u8,122u8,83u8,218u8,28u8,154u8,99u8,112u8,161u8,197u8,105u8,218u8,193u8,194u8,98u8,147u8,95u8,83u8,191u8,65u8, - 201u8,27u8,27u8,82u8,158u8,127u8,148u8,150u8,135u8,182u8,244u8,114u8,77u8,78u8,85u8,5u8,143u8,250u8,126u8,85u8, - 112u8,243u8,100u8,85u8,134u8,7u8,85u8,65u8,74u8,175u8,81u8,197u8,255u8,14u8,85u8,236u8,189u8,205u8,94u8,117u8, - 44u8,192u8,201u8,42u8,141u8,14u8,169u8,100u8,169u8,189u8,70u8,173u8,74u8,172u8,157u8,168u8,22u8,93u8,37u8,236u8, - 85u8,137u8,54u8,189u8,65u8,38u8,192u8,213u8,113u8,1u8,6u8,141u8,207u8,33u8,21u8,43u8,212u8,79u8,32u8,126u8, - 42u8,173u8,193u8,113u8,90u8,223u8,97u8,40u8,123u8,133u8,85u8,55u8,86u8,178u8,222u8,100u8,117u8,8u8,233u8,82u8, - 190u8,184u8,108u8,114u8,182u8,184u8,8u8,219u8,205u8,254u8,116u8,6u8,161u8,231u8,170u8,82u8,109u8,24u8,227u8,238u8, - 184u8,10u8,221u8,236u8,189u8,177u8,195u8,144u8,42u8,100u8,138u8,238u8,235u8,164u8,110u8,215u8,158u8,131u8,157u8,110u8, - 24u8,215u8,217u8,16u8,208u8,107u8,176u8,32u8,218u8,88u8,44u8,198u8,116u8,185u8,54u8,166u8,226u8,83u8,161u8,127u8, - 178u8,93u8,74u8,45u8,92u8,221u8,52u8,60u8,220u8,234u8,107u8,146u8,202u8,188u8,153u8,141u8,148u8,184u8,135u8,87u8, - 155u8,58u8,187u8,182u8,215u8,195u8,18u8,93u8,52u8,43u8,130u8,107u8,27u8,224u8,119u8,213u8,6u8,186u8,184u8,134u8, - 52u8,161u8,209u8,255u8,206u8,158u8,249u8,203u8,64u8,120u8,237u8,82u8,220u8,157u8,40u8,70u8,81u8,20u8,180u8,52u8, - 186u8,216u8,218u8,162u8,96u8,133u8,163u8,213u8,215u8,10u8,120u8,178u8,169u8,127u8,59u8,35u8,23u8,141u8,120u8,217u8, - 192u8,147u8,118u8,11u8,84u8,103u8,215u8,37u8,75u8,138u8,153u8,213u8,209u8,237u8,29u8,170u8,174u8,89u8,85u8,6u8, - 214u8,255u8,195u8,77u8,131u8,87u8,248u8,169u8,166u8,201u8,65u8,31u8,53u8,110u8,23u8,140u8,44u8,132u8,104u8,167u8, - 25u8,255u8,209u8,68u8,154u8,221u8,18u8,184u8,113u8,110u8,151u8,251u8,145u8,104u8,92u8,109u8,252u8,19u8,59u8,175u8, - 33u8,14u8,37u8,250u8,248u8,65u8,18u8,236u8,34u8,154u8,38u8,100u8,105u8,232u8,208u8,147u8,48u8,94u8,218u8,225u8, - 253u8,144u8,201u8,64u8,13u8,249u8,243u8,253u8,146u8,29u8,181u8,170u8,218u8,144u8,147u8,6u8,188u8,225u8,66u8,163u8, - 137u8,227u8,228u8,247u8,74u8,125u8,168u8,168u8,94u8,168u8,189u8,15u8,223u8,39u8,124u8,75u8,234u8,173u8,37u8,218u8, - 122u8,17u8,83u8,32u8,13u8,11u8,36u8,255u8,4u8,164u8,146u8,102u8,249u8,227u8,57u8,10u8,106u8,217u8,118u8,114u8, - 89u8,104u8,180u8,234u8,228u8,76u8,250u8,212u8,191u8,229u8,138u8,249u8,215u8,151u8,151u8,131u8,209u8,104u8,116u8,217u8, - 188u8,31u8,194u8,42u8,201u8,23u8,152u8,233u8,18u8,122u8,63u8,55u8,95u8,97u8,32u8,160u8,237u8,175u8,42u8,55u8, - 160u8,33u8,95u8,242u8,36u8,110u8,9u8,237u8,169u8,241u8,209u8,20u8,243u8,229u8,52u8,14u8,214u8,204u8,57u8,33u8, - 212u8,139u8,38u8,208u8,234u8,211u8,133u8,171u8,223u8,231u8,180u8,235u8,193u8,122u8,111u8,105u8,214u8,187u8,131u8,131u8, - 85u8,238u8,96u8,193u8,212u8,248u8,222u8,41u8,85u8,242u8,0u8,62u8,189u8,126u8,57u8,72u8,225u8,226u8,184u8,8u8, - 84u8,50u8,143u8,80u8,185u8,189u8,189u8,24u8,30u8,37u8,100u8,79u8,62u8,133u8,77u8,214u8,180u8,183u8,159u8,253u8, - 38u8,3u8,71u8,119u8,31u8,132u8,72u8,217u8,235u8,252u8,4u8,197u8,11u8,12u8,172u8,247u8,238u8,145u8,174u8,108u8, - 48u8,26u8,94u8,121u8,151u8,87u8,254u8,120u8,56u8,244u8,241u8,203u8,247u8,252u8,87u8,210u8,215u8,170u8,124u8,31u8, - 143u8,70u8,196u8,73u8,17u8,237u8,190u8,229u8,119u8,135u8,116u8,69u8,133u8,1u8,55u8,183u8,55u8,18u8,65u8,82u8, - 73u8,92u8,65u8,246u8,86u8,138u8,39u8,127u8,137u8,105u8,192u8,93u8,164u8,145u8,126u8,135u8,70u8,111u8,86u8,243u8, - 189u8,24u8,223u8,147u8,225u8,45u8,84u8,78u8,51u8,123u8,201u8,166u8,223u8,68u8,204u8,165u8,194u8,246u8,195u8,190u8, - 157u8,78u8,55u8,180u8,25u8,5u8,113u8,118u8,40u8,53u8,138u8,126u8,39u8,218u8,126u8,30u8,235u8,3u8,191u8,189u8, - 209u8,164u8,201u8,58u8,187u8,2u8,104u8,220u8,83u8,222u8,210u8,28u8,223u8,44u8,17u8,212u8,235u8,224u8,37u8,238u8, - 164u8,113u8,253u8,188u8,171u8,46u8,237u8,64u8,247u8,65u8,123u8,239u8,207u8,215u8,229u8,118u8,179u8,44u8,79u8,139u8, - 127u8,244u8,64u8,190u8,163u8,246u8,74u8,99u8,221u8,194u8,182u8,236u8,147u8,151u8,179u8,255u8,1u8,190u8,128u8,177u8, - 156u8,95u8,32u8,0u8,0u8,0u8,0u8,6u8,109u8,97u8,116u8,104u8,54u8,52u8,247u8,18u8,31u8,139u8,8u8,0u8, - 0u8,0u8,0u8,0u8,2u8,255u8,189u8,25u8,105u8,111u8,219u8,200u8,245u8,187u8,127u8,197u8,11u8,138u8,162u8,228u8, - 234u8,36u8,117u8,217u8,150u8,109u8,32u8,197u8,38u8,11u8,3u8,110u8,92u8,100u8,219u8,45u8,218u8,32u8,38u8,40u8, - 113u8,36u8,13u8,64u8,113u8,180u8,67u8,210u8,146u8,186u8,200u8,127u8,239u8,123u8,51u8,28u8,138u8,151u8,14u8,167u8, - 237u8,18u8,176u8,69u8,114u8,222u8,188u8,123u8,222u8,197u8,94u8,175u8,7u8,63u8,39u8,126u8,20u8,248u8,50u8,128u8, - 181u8,159u8,172u8,32u8,77u8,120u8,200u8,19u8,206u8,98u8,88u8,243u8,56u8,230u8,209u8,18u8,120u8,4u8,201u8,138u8, - 193u8,95u8,196u8,43u8,131u8,39u8,63u8,90u8,166u8,254u8,146u8,117u8,175u8,214u8,34u8,72u8,67u8,6u8,254u8,38u8, - 17u8,177u8,23u8,39u8,193u8,237u8,45u8,109u8,31u8,15u8,225u8,183u8,171u8,43u8,192u8,43u8,141u8,25u8,168u8,215u8, - 11u8,190u8,99u8,129u8,183u8,17u8,60u8,74u8,6u8,238u8,237u8,237u8,71u8,122u8,252u8,171u8,126u8,154u8,158u8,128u8, - 156u8,106u8,52u8,61u8,100u8,240u8,253u8,76u8,200u8,4u8,94u8,253u8,48u8,101u8,176u8,93u8,177u8,8u8,252u8,8u8, - 153u8,194u8,103u8,30u8,128u8,47u8,151u8,233u8,154u8,69u8,9u8,240u8,24u8,54u8,82u8,188u8,242u8,128u8,5u8,93u8, - 181u8,115u8,46u8,162u8,56u8,129u8,15u8,143u8,159u8,126u8,121u8,255u8,244u8,248u8,163u8,247u8,254u8,243u8,79u8,222u8, - 199u8,167u8,231u8,231u8,207u8,222u8,211u8,243u8,79u8,238u8,45u8,164u8,200u8,234u8,61u8,56u8,211u8,34u8,236u8,143u8, - 143u8,191u8,60u8,254u8,252u8,248u8,252u8,201u8,251u8,243u8,63u8,189u8,127u8,125u8,248u8,252u8,92u8,128u8,202u8,153u8, - 249u8,204u8,146u8,84u8,106u8,141u8,132u8,72u8,156u8,225u8,62u8,177u8,128u8,100u8,43u8,32u8,74u8,215u8,51u8,38u8, - 99u8,77u8,125u8,147u8,206u8,66u8,62u8,135u8,69u8,26u8,161u8,90u8,119u8,150u8,175u8,80u8,181u8,97u8,166u8,126u8, - 109u8,141u8,248u8,55u8,5u8,73u8,23u8,95u8,128u8,229u8,195u8,195u8,61u8,204u8,108u8,240u8,129u8,133u8,168u8,145u8, - 153u8,90u8,252u8,214u8,72u8,56u8,94u8,251u8,97u8,120u8,9u8,101u8,30u8,93u8,70u8,249u8,238u8,66u8,194u8,254u8, - 43u8,147u8,104u8,253u8,140u8,110u8,141u8,94u8,182u8,252u8,6u8,154u8,135u8,151u8,116u8,249u8,208u8,2u8,107u8,6u8, - 29u8,240u8,109u8,232u8,129u8,155u8,175u8,125u8,211u8,156u8,149u8,129u8,103u8,4u8,236u8,35u8,240u8,172u8,2u8,220u8, - 204u8,127u8,140u8,216u8,127u8,192u8,77u8,61u8,152u8,195u8,82u8,144u8,95u8,39u8,43u8,41u8,210u8,37u8,122u8,188u8, - 227u8,94u8,67u8,34u8,208u8,125u8,216u8,171u8,242u8,164u8,40u8,97u8,114u8,205u8,2u8,238u8,39u8,40u8,41u8,138u8, - 180u8,8u8,197u8,182u8,40u8,42u8,143u8,66u8,30u8,49u8,173u8,225u8,52u8,244u8,2u8,254u8,90u8,145u8,184u8,13u8, - 243u8,102u8,201u8,45u8,11u8,57u8,246u8,99u8,69u8,210u8,70u8,118u8,80u8,216u8,252u8,169u8,7u8,214u8,60u8,127u8, - 178u8,213u8,29u8,98u8,56u8,98u8,140u8,29u8,204u8,67u8,127u8,189u8,97u8,1u8,49u8,78u8,150u8,81u8,76u8,227u8, - 145u8,128u8,47u8,200u8,44u8,147u8,109u8,72u8,55u8,27u8,38u8,191u8,214u8,76u8,164u8,182u8,89u8,187u8,140u8,77u8, - 5u8,155u8,221u8,171u8,13u8,205u8,108u8,147u8,31u8,169u8,229u8,182u8,114u8,230u8,140u8,194u8,206u8,182u8,79u8,184u8, - 138u8,62u8,174u8,232u8,40u8,17u8,72u8,159u8,199u8,154u8,211u8,13u8,237u8,4u8,86u8,101u8,10u8,95u8,91u8,81u8, - 198u8,6u8,59u8,238u8,51u8,12u8,238u8,239u8,161u8,95u8,117u8,26u8,231u8,180u8,155u8,132u8,44u8,129u8,205u8,225u8, - 164u8,155u8,107u8,187u8,226u8,24u8,189u8,16u8,229u8,3u8,56u8,85u8,140u8,7u8,122u8,127u8,4u8,151u8,104u8,54u8, - 66u8,40u8,25u8,16u8,241u8,6u8,237u8,24u8,77u8,107u8,203u8,223u8,234u8,175u8,144u8,127u8,252u8,67u8,95u8,173u8, - 47u8,69u8,184u8,20u8,213u8,17u8,85u8,144u8,40u8,82u8,231u8,29u8,29u8,253u8,85u8,72u8,43u8,92u8,186u8,150u8, - 49u8,81u8,65u8,215u8,106u8,209u8,11u8,5u8,173u8,230u8,170u8,190u8,46u8,200u8,71u8,10u8,147u8,24u8,254u8,81u8, - 213u8,7u8,234u8,126u8,28u8,51u8,153u8,188u8,179u8,118u8,240u8,14u8,223u8,183u8,117u8,188u8,102u8,82u8,10u8,121u8, - 123u8,155u8,133u8,98u8,207u8,132u8,98u8,235u8,72u8,204u8,181u8,237u8,3u8,62u8,228u8,246u8,195u8,98u8,193u8,230u8, - 9u8,127u8,101u8,225u8,94u8,57u8,204u8,70u8,196u8,152u8,119u8,68u8,164u8,130u8,11u8,62u8,175u8,5u8,6u8,184u8, - 152u8,47u8,35u8,190u8,224u8,115u8,31u8,207u8,101u8,140u8,140u8,205u8,120u8,82u8,98u8,148u8,244u8,54u8,40u8,232u8, - 51u8,51u8,107u8,132u8,102u8,173u8,57u8,10u8,153u8,116u8,71u8,65u8,214u8,114u8,224u8,238u8,14u8,34u8,187u8,201u8, - 170u8,59u8,68u8,136u8,64u8,15u8,77u8,38u8,213u8,90u8,161u8,255u8,173u8,51u8,134u8,210u8,230u8,68u8,44u8,5u8, - 175u8,43u8,128u8,32u8,138u8,178u8,237u8,114u8,211u8,105u8,187u8,212u8,140u8,86u8,54u8,87u8,49u8,133u8,86u8,12u8, - 71u8,97u8,96u8,201u8,164u8,183u8,241u8,49u8,99u8,222u8,151u8,140u8,93u8,214u8,254u8,39u8,33u8,49u8,137u8,240u8, - 127u8,51u8,148u8,22u8,15u8,230u8,23u8,167u8,13u8,174u8,77u8,169u8,94u8,37u8,97u8,80u8,73u8,24u8,53u8,219u8, - 45u8,97u8,223u8,35u8,74u8,43u8,87u8,163u8,210u8,226u8,192u8,173u8,106u8,81u8,105u8,207u8,42u8,49u8,210u8,33u8, - 176u8,211u8,199u8,116u8,71u8,200u8,44u8,148u8,167u8,83u8,146u8,161u8,184u8,203u8,196u8,198u8,105u8,137u8,167u8,133u8, - 244u8,231u8,101u8,95u8,165u8,183u8,1u8,11u8,19u8,31u8,12u8,143u8,78u8,205u8,65u8,244u8,250u8,187u8,134u8,112u8, - 130u8,186u8,65u8,125u8,41u8,63u8,112u8,122u8,174u8,190u8,127u8,113u8,171u8,32u8,59u8,82u8,149u8,86u8,90u8,105u8, - 73u8,105u8,104u8,143u8,199u8,116u8,111u8,147u8,26u8,6u8,149u8,163u8,174u8,119u8,198u8,16u8,137u8,173u8,65u8,48u8, - 180u8,171u8,16u8,168u8,96u8,141u8,222u8,165u8,85u8,58u8,11u8,209u8,129u8,37u8,116u8,61u8,186u8,71u8,11u8,244u8, - 170u8,180u8,201u8,48u8,123u8,229u8,223u8,110u8,102u8,25u8,148u8,205u8,232u8,71u8,253u8,180u8,180u8,94u8,166u8,138u8, - 205u8,189u8,246u8,207u8,170u8,235u8,26u8,205u8,233u8,223u8,163u8,46u8,92u8,169u8,234u8,230u8,146u8,97u8,182u8,244u8, - 22u8,82u8,172u8,61u8,233u8,111u8,61u8,29u8,253u8,49u8,221u8,149u8,252u8,32u8,203u8,105u8,198u8,111u8,90u8,138u8, - 43u8,251u8,88u8,24u8,139u8,127u8,77u8,125u8,201u8,64u8,10u8,161u8,10u8,157u8,93u8,155u8,50u8,244u8,28u8,83u8, - 9u8,6u8,12u8,29u8,226u8,226u8,95u8,101u8,210u8,20u8,227u8,244u8,251u8,227u8,137u8,100u8,151u8,37u8,18u8,169u8, - 211u8,85u8,191u8,114u8,50u8,48u8,237u8,171u8,128u8,20u8,166u8,49u8,42u8,60u8,43u8,127u8,217u8,14u8,169u8,99u8, - 65u8,44u8,162u8,46u8,60u8,101u8,145u8,39u8,59u8,94u8,234u8,116u8,193u8,150u8,193u8,10u8,11u8,32u8,99u8,187u8, - 151u8,8u8,157u8,227u8,197u8,138u8,90u8,142u8,253u8,128u8,5u8,43u8,166u8,191u8,21u8,162u8,83u8,69u8,84u8,20u8, - 83u8,22u8,228u8,81u8,145u8,42u8,45u8,172u8,252u8,112u8,209u8,17u8,27u8,22u8,21u8,178u8,57u8,161u8,232u8,185u8, - 182u8,194u8,165u8,144u8,225u8,195u8,67u8,23u8,62u8,10u8,76u8,163u8,88u8,173u8,32u8,27u8,72u8,23u8,195u8,35u8, - 108u8,37u8,87u8,124u8,163u8,119u8,161u8,154u8,15u8,251u8,148u8,46u8,80u8,217u8,217u8,139u8,135u8,34u8,209u8,5u8, - 98u8,17u8,65u8,0u8,138u8,223u8,28u8,125u8,47u8,219u8,82u8,164u8,89u8,38u8,169u8,138u8,95u8,182u8,72u8,128u8, - 69u8,38u8,84u8,32u8,217u8,204u8,212u8,57u8,86u8,130u8,146u8,124u8,185u8,74u8,138u8,36u8,155u8,118u8,116u8,225u8, - 113u8,161u8,196u8,88u8,9u8,129u8,129u8,1u8,217u8,23u8,41u8,162u8,225u8,18u8,19u8,128u8,191u8,193u8,170u8,126u8, - 199u8,177u8,183u8,160u8,212u8,96u8,42u8,29u8,34u8,115u8,192u8,100u8,52u8,143u8,27u8,177u8,60u8,225u8,235u8,116u8, - 93u8,164u8,40u8,89u8,232u8,83u8,154u8,1u8,149u8,174u8,128u8,113u8,68u8,34u8,193u8,50u8,122u8,233u8,80u8,118u8, - 199u8,247u8,152u8,20u8,240u8,214u8,200u8,110u8,195u8,76u8,96u8,51u8,68u8,254u8,167u8,139u8,109u8,169u8,207u8,33u8, - 134u8,132u8,110u8,67u8,166u8,84u8,97u8,198u8,178u8,74u8,225u8,22u8,221u8,219u8,81u8,65u8,192u8,41u8,71u8,222u8, - 127u8,176u8,172u8,227u8,201u8,186u8,174u8,136u8,109u8,19u8,17u8,117u8,228u8,202u8,223u8,196u8,40u8,34u8,90u8,81u8, - 102u8,194u8,10u8,224u8,107u8,234u8,105u8,88u8,38u8,55u8,166u8,71u8,116u8,137u8,146u8,70u8,186u8,69u8,204u8,127u8, - 91u8,25u8,33u8,169u8,128u8,69u8,99u8,137u8,240u8,149u8,41u8,119u8,80u8,135u8,217u8,227u8,45u8,199u8,28u8,108u8, - 143u8,191u8,184u8,20u8,65u8,192u8,194u8,67u8,22u8,16u8,193u8,57u8,53u8,65u8,88u8,236u8,46u8,89u8,52u8,103u8, - 118u8,9u8,239u8,99u8,2u8,250u8,68u8,138u8,20u8,239u8,86u8,62u8,90u8,101u8,129u8,20u8,96u8,120u8,96u8,86u8, - 123u8,182u8,142u8,25u8,104u8,216u8,131u8,206u8,208u8,51u8,221u8,151u8,14u8,198u8,245u8,252u8,8u8,204u8,24u8,214u8, - 143u8,10u8,60u8,65u8,229u8,173u8,68u8,24u8,116u8,175u8,202u8,25u8,214u8,210u8,41u8,150u8,66u8,28u8,222u8,217u8, - 149u8,32u8,244u8,123u8,194u8,80u8,225u8,139u8,175u8,219u8,249u8,122u8,49u8,86u8,213u8,187u8,129u8,57u8,227u8,186u8, - 29u8,48u8,245u8,245u8,190u8,57u8,6u8,161u8,78u8,15u8,160u8,109u8,202u8,21u8,89u8,44u8,177u8,48u8,52u8,181u8, - 48u8,50u8,43u8,167u8,236u8,85u8,22u8,14u8,47u8,91u8,133u8,162u8,23u8,113u8,21u8,55u8,205u8,69u8,26u8,6u8, - 16u8,111u8,82u8,201u8,69u8,26u8,99u8,164u8,52u8,13u8,76u8,23u8,98u8,65u8,39u8,133u8,124u8,79u8,55u8,175u8, - 100u8,66u8,92u8,164u8,144u8,214u8,24u8,29u8,43u8,189u8,89u8,86u8,1u8,238u8,179u8,10u8,176u8,214u8,44u8,219u8, - 229u8,60u8,210u8,175u8,212u8,169u8,170u8,0u8,166u8,188u8,95u8,144u8,36u8,23u8,36u8,211u8,232u8,31u8,190u8,36u8, - 216u8,213u8,126u8,45u8,106u8,23u8,235u8,73u8,185u8,87u8,202u8,165u8,37u8,47u8,87u8,91u8,145u8,61u8,195u8,90u8, - 190u8,120u8,211u8,134u8,129u8,77u8,82u8,12u8,218u8,40u8,200u8,244u8,56u8,160u8,211u8,55u8,144u8,195u8,115u8,144u8, - 206u8,197u8,144u8,238u8,197u8,144u8,3u8,3u8,57u8,210u8,144u8,229u8,52u8,84u8,238u8,61u8,27u8,49u8,224u8,229u8, - 80u8,77u8,116u8,119u8,71u8,105u8,181u8,3u8,55u8,121u8,223u8,216u8,6u8,199u8,81u8,168u8,157u8,241u8,100u8,124u8, - 51u8,25u8,79u8,6u8,131u8,155u8,201u8,96u8,116u8,51u8,26u8,247u8,157u8,3u8,91u8,23u8,235u8,29u8,227u8,170u8, - 55u8,30u8,150u8,180u8,158u8,69u8,191u8,52u8,164u8,66u8,147u8,218u8,194u8,129u8,114u8,249u8,49u8,81u8,175u8,11u8, - 109u8,32u8,239u8,97u8,92u8,145u8,181u8,134u8,199u8,25u8,41u8,68u8,142u8,123u8,14u8,147u8,131u8,106u8,115u8,222u8, - 44u8,9u8,30u8,233u8,227u8,98u8,224u8,226u8,133u8,98u8,12u8,78u8,138u8,129u8,120u8,46u8,23u8,195u8,253u8,14u8, - 49u8,204u8,168u8,229u8,168u8,40u8,6u8,224u8,66u8,113u8,134u8,167u8,196u8,49u8,184u8,46u8,23u8,105u8,240u8,29u8, - 62u8,150u8,81u8,241u8,2u8,193u8,98u8,47u8,18u8,137u8,103u8,252u8,255u8,188u8,144u8,206u8,245u8,112u8,56u8,158u8, - 12u8,135u8,253u8,201u8,96u8,210u8,191u8,25u8,141u8,156u8,177u8,242u8,141u8,134u8,183u8,167u8,217u8,110u8,68u8,243u8, - 102u8,65u8,54u8,167u8,88u8,166u8,69u8,167u8,175u8,245u8,120u8,125u8,86u8,143u8,253u8,218u8,117u8,202u8,78u8,69u8, - 220u8,231u8,81u8,95u8,136u8,169u8,127u8,22u8,211u8,247u8,196u8,147u8,180u8,30u8,198u8,137u8,129u8,100u8,189u8,201u8, - 135u8,174u8,24u8,212u8,6u8,117u8,186u8,102u8,39u8,66u8,182u8,179u8,63u8,21u8,231u8,232u8,190u8,44u8,80u8,211u8, - 142u8,81u8,123u8,84u8,134u8,174u8,23u8,254u8,88u8,222u8,8u8,25u8,48u8,73u8,35u8,66u8,161u8,106u8,197u8,173u8, - 191u8,87u8,213u8,234u8,58u8,235u8,63u8,186u8,53u8,18u8,132u8,27u8,19u8,218u8,136u8,102u8,122u8,35u8,74u8,144u8, - 37u8,252u8,23u8,107u8,229u8,208u8,79u8,84u8,245u8,194u8,131u8,157u8,26u8,211u8,148u8,26u8,221u8,172u8,151u8,197u8, - 53u8,28u8,166u8,82u8,2u8,104u8,206u8,216u8,133u8,170u8,20u8,117u8,138u8,208u8,74u8,3u8,248u8,91u8,214u8,128u8, - 74u8,255u8,1u8,53u8,153u8,244u8,191u8,117u8,164u8,239u8,211u8,16u8,78u8,51u8,19u8,247u8,151u8,113u8,113u8,200u8, - 90u8,138u8,25u8,85u8,13u8,100u8,105u8,203u8,176u8,70u8,47u8,223u8,202u8,222u8,197u8,106u8,86u8,76u8,252u8,47u8, - 52u8,124u8,104u8,0u8,138u8,218u8,157u8,54u8,203u8,95u8,110u8,152u8,151u8,44u8,57u8,52u8,202u8,150u8,42u8,61u8, - 239u8,239u8,53u8,165u8,82u8,151u8,252u8,95u8,216u8,168u8,127u8,185u8,145u8,42u8,130u8,156u8,50u8,80u8,109u8,160u8, - 161u8,57u8,161u8,125u8,166u8,135u8,114u8,95u8,180u8,147u8,153u8,165u8,8u8,23u8,58u8,230u8,117u8,47u8,140u8,106u8, - 211u8,148u8,191u8,99u8,89u8,56u8,192u8,86u8,72u8,157u8,59u8,72u8,252u8,125u8,72u8,61u8,140u8,40u8,180u8,58u8, - 170u8,255u8,198u8,113u8,33u8,142u8,166u8,116u8,224u8,169u8,177u8,159u8,175u8,23u8,180u8,168u8,230u8,234u8,74u8,141u8, - 211u8,26u8,188u8,166u8,66u8,13u8,145u8,101u8,229u8,147u8,172u8,30u8,61u8,164u8,238u8,104u8,172u8,37u8,183u8,27u8, - 102u8,78u8,229u8,253u8,46u8,237u8,55u8,168u8,126u8,48u8,72u8,27u8,103u8,63u8,135u8,77u8,131u8,195u8,38u8,247u8, - 130u8,77u8,5u8,201u8,242u8,219u8,14u8,50u8,106u8,200u8,182u8,114u8,94u8,168u8,161u8,107u8,229u8,68u8,122u8,84u8, - 92u8,230u8,130u8,185u8,55u8,147u8,73u8,127u8,56u8,28u8,78u8,234u8,35u8,41u8,76u8,162u8,124u8,129u8,33u8,46u8, - 161u8,47u8,128u8,243u8,21u8,250u8,1u8,234u8,254u8,186u8,52u8,123u8,13u8,248u8,146u8,39u8,113u8,131u8,99u8,123u8, - 218u8,70u8,30u8,6u8,74u8,47u8,246u8,215u8,204u8,186u8,192u8,209u8,141u8,82u8,219u8,185u8,60u8,109u8,184u8,254u8, - 255u8,28u8,117u8,213u8,206u8,31u8,205u8,197u8,106u8,181u8,127u8,50u8,185u8,157u8,204u8,146u8,106u8,191u8,115u8,73u8, - 114u8,60u8,177u8,31u8,189u8,237u8,52u8,134u8,241u8,121u8,22u8,48u8,85u8,186u8,167u8,145u8,220u8,221u8,13u8,206u8, - 179u8,98u8,206u8,61u8,121u8,141u8,110u8,40u8,26u8,15u8,126u8,29u8,189u8,69u8,229u8,134u8,241u8,181u8,206u8,5u8, - 66u8,231u8,27u8,198u8,131u8,211u8,152u8,7u8,125u8,44u8,197u8,250u8,253u8,225u8,205u8,205u8,155u8,112u8,42u8,214u8, - 223u8,130u8,183u8,230u8,85u8,34u8,10u8,247u8,95u8,243u8,153u8,36u8,13u8,191u8,208u8,169u8,230u8,102u8,214u8,225u8, - 39u8,165u8,224u8,228u8,103u8,159u8,187u8,120u8,242u8,167u8,152u8,58u8,236u8,69u8,26u8,170u8,15u8,115u8,244u8,141u8, - 54u8,95u8,139u8,241u8,227u8,27u8,206u8,183u8,114u8,156u8,217u8,151u8,59u8,245u8,157u8,99u8,46u8,36u8,214u8,22u8, - 230u8,35u8,119u8,186u8,161u8,197u8,208u8,143u8,179u8,131u8,167u8,182u8,168u8,15u8,172u8,205u8,103u8,142u8,50u8,23u8, - 154u8,77u8,207u8,27u8,212u8,141u8,174u8,84u8,168u8,181u8,215u8,19u8,136u8,218u8,0u8,244u8,142u8,230u8,8u8,245u8, - 36u8,64u8,117u8,12u8,126u8,0u8,153u8,86u8,102u8,243u8,56u8,45u8,158u8,214u8,230u8,220u8,8u8,219u8,152u8,126u8, - 8u8,207u8,90u8,219u8,197u8,210u8,117u8,100u8,145u8,159u8,166u8,152u8,154u8,151u8,81u8,148u8,247u8,247u8,84u8,68u8, - 169u8,253u8,119u8,176u8,43u8,26u8,231u8,219u8,213u8,127u8,0u8,137u8,152u8,113u8,123u8,174u8,32u8,0u8,0u8,0u8, - 0u8,10u8,109u8,97u8,116u8,104u8,95u8,102u8,105u8,120u8,101u8,100u8,173u8,13u8,31u8,139u8,8u8,0u8,0u8,0u8, - 0u8,0u8,2u8,255u8,205u8,88u8,219u8,110u8,219u8,70u8,16u8,125u8,247u8,87u8,76u8,81u8,164u8,165u8,108u8,197u8, - 18u8,169u8,139u8,35u8,217u8,10u8,208u8,22u8,14u8,80u8,32u8,77u8,130u8,186u8,104u8,2u8,20u8,49u8,177u8,166u8, - 86u8,50u8,1u8,138u8,100u8,184u8,203u8,152u8,114u8,145u8,127u8,239u8,153u8,229u8,157u8,150u8,124u8,73u8,209u8,160u8, - 122u8,176u8,76u8,238u8,204u8,236u8,236u8,153u8,203u8,153u8,213u8,96u8,48u8,160u8,11u8,45u8,194u8,165u8,72u8,150u8, - 180u8,17u8,250u8,154u8,82u8,237u8,7u8,190u8,246u8,165u8,162u8,141u8,175u8,148u8,31u8,174u8,201u8,15u8,73u8,95u8, - 75u8,250u8,45u8,250u8,44u8,233u8,181u8,8u8,215u8,169u8,88u8,203u8,227u8,131u8,77u8,180u8,76u8,3u8,73u8,34u8, - 214u8,145u8,114u8,149u8,94u8,206u8,231u8,172u8,238u8,174u8,252u8,76u8,46u8,233u8,239u8,3u8,194u8,39u8,85u8,146u8, - 204u8,138u8,121u8,233u8,198u8,145u8,31u8,234u8,145u8,115u8,122u8,207u8,218u8,124u8,254u8,138u8,31u8,223u8,117u8,37u8, - 59u8,187u8,216u8,206u8,139u8,189u8,107u8,211u8,241u8,233u8,129u8,89u8,27u8,224u8,104u8,63u8,93u8,69u8,137u8,38u8, - 47u8,90u8,74u8,138u8,66u8,194u8,1u8,146u8,85u8,16u8,221u8,152u8,101u8,47u8,10u8,149u8,166u8,243u8,183u8,127u8, - 158u8,255u8,254u8,234u8,245u8,219u8,247u8,238u8,249u8,135u8,119u8,115u8,74u8,167u8,99u8,90u8,144u8,221u8,176u8,240u8, - 70u8,232u8,52u8,17u8,1u8,5u8,209u8,154u8,28u8,134u8,98u8,228u8,208u8,149u8,175u8,41u8,63u8,167u8,113u8,187u8, - 97u8,238u8,245u8,27u8,7u8,70u8,224u8,30u8,172u8,56u8,179u8,147u8,147u8,225u8,120u8,60u8,62u8,113u8,78u8,217u8, - 20u8,5u8,161u8,229u8,244u8,216u8,66u8,174u8,10u8,59u8,137u8,140u8,19u8,169u8,100u8,168u8,133u8,246u8,163u8,176u8, - 109u8,198u8,253u8,224u8,142u8,156u8,210u8,33u8,200u8,30u8,62u8,221u8,94u8,117u8,134u8,139u8,79u8,169u8,72u8,36u8, - 37u8,81u8,164u8,41u8,90u8,53u8,93u8,167u8,48u8,221u8,92u8,201u8,196u8,72u8,198u8,233u8,85u8,224u8,123u8,180u8, - 74u8,67u8,82u8,159u8,18u8,109u8,101u8,115u8,106u8,6u8,163u8,215u8,126u8,44u8,66u8,204u8,159u8,64u8,106u8,218u8, - 194u8,75u8,171u8,19u8,202u8,181u8,212u8,110u8,34u8,110u8,220u8,207u8,34u8,72u8,165u8,149u8,245u8,72u8,40u8,131u8, - 77u8,239u8,180u8,82u8,237u8,104u8,120u8,137u8,20u8,90u8,186u8,171u8,36u8,218u8,52u8,52u8,173u8,34u8,228u8,243u8, - 185u8,241u8,107u8,75u8,103u8,103u8,56u8,109u8,110u8,110u8,58u8,238u8,245u8,140u8,181u8,47u8,245u8,113u8,207u8,179u8, - 56u8,10u8,129u8,2u8,31u8,197u8,99u8,36u8,232u8,198u8,71u8,98u8,11u8,2u8,58u8,30u8,231u8,52u8,167u8,194u8, - 138u8,102u8,180u8,244u8,215u8,190u8,86u8,199u8,221u8,211u8,203u8,44u8,126u8,218u8,225u8,43u8,87u8,255u8,75u8,16u8, - 224u8,21u8,63u8,90u8,213u8,171u8,253u8,0u8,252u8,44u8,61u8,193u8,149u8,129u8,156u8,69u8,202u8,42u8,10u8,229u8, - 26u8,25u8,129u8,234u8,93u8,69u8,9u8,25u8,93u8,69u8,103u8,100u8,211u8,141u8,68u8,250u8,40u8,45u8,197u8,18u8, - 169u8,131u8,52u8,15u8,141u8,6u8,59u8,121u8,196u8,233u8,116u8,115u8,237u8,123u8,215u8,149u8,81u8,216u8,137u8,35u8, - 229u8,87u8,118u8,68u8,16u8,148u8,182u8,0u8,102u8,118u8,7u8,69u8,54u8,229u8,198u8,65u8,170u8,144u8,199u8,223u8, - 20u8,206u8,42u8,91u8,204u8,97u8,106u8,184u8,154u8,40u8,53u8,253u8,12u8,75u8,47u8,131u8,240u8,219u8,58u8,202u8, - 38u8,178u8,7u8,85u8,247u8,29u8,231u8,235u8,51u8,41u8,67u8,51u8,65u8,135u8,161u8,151u8,47u8,57u8,202u8,251u8, - 146u8,232u8,215u8,80u8,203u8,181u8,76u8,16u8,244u8,27u8,252u8,69u8,136u8,197u8,35u8,26u8,7u8,132u8,187u8,16u8, - 246u8,41u8,52u8,109u8,236u8,127u8,80u8,66u8,240u8,174u8,93u8,66u8,125u8,178u8,194u8,202u8,218u8,254u8,122u8,186u8, - 136u8,209u8,55u8,68u8,224u8,223u8,226u8,244u8,85u8,79u8,225u8,26u8,96u8,36u8,183u8,52u8,160u8,91u8,16u8,164u8, - 64u8,111u8,221u8,160u8,157u8,160u8,164u8,180u8,76u8,54u8,114u8,233u8,195u8,5u8,82u8,215u8,254u8,74u8,131u8,68u8, - 187u8,64u8,109u8,210u8,192u8,93u8,250u8,159u8,239u8,130u8,181u8,237u8,190u8,184u8,125u8,74u8,70u8,10u8,192u8,247u8, - 0u8,122u8,237u8,252u8,187u8,122u8,72u8,97u8,219u8,81u8,240u8,30u8,82u8,184u8,125u8,98u8,88u8,200u8,202u8,89u8, - 27u8,236u8,93u8,96u8,34u8,250u8,116u8,213u8,39u8,175u8,27u8,7u8,250u8,69u8,4u8,94u8,26u8,48u8,170u8,242u8, - 50u8,67u8,119u8,146u8,224u8,180u8,140u8,48u8,187u8,152u8,241u8,4u8,204u8,151u8,6u8,64u8,0u8,47u8,239u8,230u8, - 169u8,50u8,54u8,138u8,230u8,110u8,114u8,32u8,203u8,121u8,186u8,87u8,208u8,117u8,13u8,35u8,54u8,50u8,4u8,128u8, - 176u8,58u8,151u8,76u8,51u8,96u8,242u8,75u8,243u8,104u8,241u8,51u8,34u8,110u8,136u8,183u8,87u8,44u8,96u8,158u8, - 136u8,146u8,157u8,203u8,71u8,180u8,74u8,132u8,183u8,107u8,169u8,215u8,66u8,212u8,164u8,136u8,27u8,68u8,152u8,180u8, - 22u8,196u8,210u8,168u8,205u8,26u8,66u8,161u8,148u8,76u8,244u8,119u8,86u8,67u8,234u8,12u8,3u8,129u8,221u8,207u8, - 167u8,39u8,153u8,36u8,81u8,50u8,159u8,251u8,33u8,176u8,244u8,151u8,152u8,130u8,0u8,143u8,213u8,26u8,106u8,122u8, - 157u8,0u8,26u8,67u8,92u8,101u8,13u8,139u8,156u8,248u8,221u8,206u8,148u8,200u8,141u8,240u8,195u8,37u8,106u8,159u8, - 189u8,122u8,214u8,246u8,138u8,71u8,43u8,13u8,216u8,13u8,41u8,48u8,204u8,160u8,146u8,27u8,129u8,111u8,29u8,145u8, - 87u8,133u8,9u8,240u8,212u8,86u8,120u8,96u8,1u8,154u8,224u8,110u8,179u8,113u8,211u8,20u8,22u8,176u8,201u8,100u8, - 54u8,153u8,32u8,18u8,135u8,52u8,158u8,205u8,102u8,57u8,249u8,208u8,70u8,138u8,80u8,181u8,19u8,214u8,95u8,175u8, - 132u8,167u8,163u8,164u8,210u8,104u8,187u8,45u8,75u8,242u8,95u8,80u8,115u8,239u8,74u8,237u8,110u8,251u8,173u8,197u8, - 158u8,237u8,18u8,27u8,12u8,118u8,158u8,99u8,193u8,65u8,181u8,236u8,1u8,59u8,219u8,235u8,93u8,86u8,219u8,30u8, - 182u8,179u8,167u8,13u8,41u8,6u8,48u8,125u8,19u8,65u8,119u8,236u8,192u8,245u8,169u8,243u8,98u8,58u8,201u8,103u8, - 185u8,102u8,198u8,182u8,167u8,56u8,238u8,188u8,245u8,70u8,77u8,175u8,254u8,96u8,244u8,175u8,17u8,58u8,129u8,172u8, - 230u8,52u8,96u8,209u8,201u8,112u8,56u8,196u8,222u8,99u8,178u8,135u8,151u8,51u8,108u8,152u8,174u8,175u8,131u8,45u8, - 77u8,139u8,81u8,135u8,37u8,234u8,33u8,168u8,229u8,91u8,222u8,230u8,23u8,84u8,245u8,200u8,220u8,215u8,126u8,5u8, - 104u8,39u8,61u8,100u8,172u8,92u8,47u8,74u8,96u8,204u8,120u8,137u8,161u8,217u8,25u8,219u8,195u8,225u8,204u8,153u8, - 217u8,181u8,96u8,195u8,40u8,190u8,143u8,200u8,180u8,96u8,252u8,119u8,216u8,85u8,63u8,172u8,183u8,97u8,90u8,66u8, - 19u8,110u8,5u8,32u8,227u8,233u8,227u8,110u8,85u8,147u8,218u8,96u8,10u8,193u8,55u8,186u8,111u8,88u8,100u8,195u8, - 192u8,148u8,218u8,25u8,13u8,143u8,129u8,132u8,61u8,38u8,21u8,113u8,110u8,134u8,18u8,170u8,81u8,8u8,40u8,28u8, - 210u8,98u8,203u8,58u8,152u8,123u8,98u8,213u8,220u8,4u8,169u8,139u8,78u8,102u8,122u8,201u8,35u8,240u8,98u8,43u8, - 81u8,98u8,115u8,30u8,148u8,103u8,202u8,140u8,239u8,22u8,182u8,127u8,158u8,231u8,120u8,7u8,178u8,92u8,133u8,83u8, - 221u8,42u8,181u8,75u8,165u8,145u8,179u8,75u8,116u8,84u8,139u8,58u8,59u8,69u8,139u8,173u8,203u8,154u8,226u8,158u8, - 83u8,90u8,62u8,170u8,182u8,67u8,38u8,86u8,79u8,35u8,60u8,77u8,247u8,182u8,213u8,140u8,49u8,224u8,243u8,87u8, - 156u8,31u8,246u8,159u8,216u8,103u8,143u8,171u8,70u8,91u8,38u8,82u8,209u8,104u8,243u8,49u8,96u8,103u8,199u8,205u8, - 187u8,141u8,194u8,123u8,103u8,50u8,229u8,76u8,226u8,3u8,241u8,53u8,174u8,20u8,200u8,76u8,11u8,50u8,83u8,127u8, - 253u8,18u8,13u8,2u8,215u8,79u8,240u8,247u8,119u8,11u8,26u8,246u8,26u8,230u8,248u8,227u8,175u8,120u8,229u8,7u8, - 152u8,218u8,181u8,202u8,31u8,108u8,200u8,232u8,242u8,215u8,33u8,161u8,94u8,185u8,5u8,98u8,251u8,94u8,145u8,127u8, - 167u8,45u8,133u8,47u8,237u8,71u8,78u8,247u8,144u8,229u8,236u8,246u8,123u8,51u8,204u8,89u8,86u8,195u8,218u8,78u8, - 219u8,187u8,6u8,152u8,198u8,14u8,150u8,113u8,202u8,68u8,186u8,150u8,108u8,198u8,236u8,251u8,191u8,180u8,84u8,250u8, - 99u8,115u8,164u8,64u8,225u8,36u8,91u8,3u8,59u8,47u8,185u8,230u8,158u8,212u8,107u8,147u8,218u8,5u8,222u8,113u8, - 25u8,93u8,9u8,101u8,106u8,129u8,90u8,183u8,170u8,34u8,188u8,41u8,110u8,253u8,145u8,50u8,183u8,68u8,142u8,53u8, - 27u8,51u8,191u8,0u8,40u8,90u8,162u8,58u8,249u8,93u8,34u8,143u8,91u8,129u8,203u8,41u8,158u8,141u8,150u8,145u8, - 235u8,230u8,114u8,145u8,48u8,139u8,252u8,86u8,121u8,207u8,72u8,128u8,217u8,203u8,178u8,155u8,116u8,85u8,114u8,223u8, - 189u8,243u8,70u8,110u8,30u8,13u8,121u8,209u8,240u8,165u8,143u8,160u8,23u8,151u8,248u8,175u8,114u8,195u8,185u8,235u8, - 134u8,43u8,226u8,56u8,137u8,50u8,23u8,24u8,184u8,74u8,108u8,48u8,78u8,62u8,202u8,173u8,50u8,128u8,125u8,154u8, - 14u8,79u8,198u8,104u8,73u8,104u8,75u8,125u8,154u8,21u8,230u8,31u8,29u8,81u8,38u8,149u8,94u8,167u8,102u8,158u8, - 0u8,125u8,57u8,245u8,12u8,119u8,160u8,91u8,10u8,61u8,22u8,191u8,210u8,86u8,45u8,221u8,165u8,6u8,118u8,199u8, - 158u8,158u8,140u8,103u8,35u8,123u8,50u8,25u8,231u8,12u8,39u8,239u8,249u8,237u8,228u8,30u8,144u8,243u8,93u8,193u8, - 66u8,57u8,102u8,247u8,123u8,100u8,15u8,81u8,111u8,251u8,221u8,202u8,98u8,8u8,44u8,104u8,54u8,158u8,14u8,65u8, - 191u8,67u8,103u8,52u8,177u8,39u8,39u8,133u8,119u8,151u8,88u8,250u8,119u8,14u8,178u8,245u8,175u8,8u8,44u8,223u8, - 152u8,58u8,149u8,250u8,94u8,154u8,223u8,181u8,184u8,0u8,61u8,14u8,46u8,138u8,17u8,214u8,119u8,31u8,189u8,236u8, - 178u8,245u8,64u8,209u8,55u8,243u8,211u8,195u8,233u8,91u8,58u8,94u8,100u8,206u8,8u8,9u8,186u8,203u8,119u8,38u8, - 205u8,143u8,213u8,77u8,232u8,21u8,166u8,140u8,242u8,6u8,164u8,242u8,75u8,79u8,110u8,215u8,223u8,48u8,129u8,136u8, - 252u8,23u8,1u8,242u8,245u8,143u8,138u8,207u8,176u8,74u8,3u8,67u8,41u8,176u8,83u8,175u8,41u8,242u8,130u8,72u8, - 201u8,202u8,102u8,193u8,57u8,166u8,239u8,20u8,35u8,65u8,33u8,154u8,198u8,188u8,24u8,8u8,44u8,24u8,62u8,174u8, - 184u8,101u8,207u8,145u8,42u8,170u8,217u8,150u8,255u8,212u8,228u8,93u8,144u8,79u8,3u8,105u8,230u8,8u8,176u8,10u8, - 109u8,187u8,244u8,96u8,8u8,120u8,19u8,51u8,233u8,220u8,237u8,239u8,219u8,246u8,43u8,254u8,221u8,11u8,178u8,59u8, - 27u8,57u8,219u8,217u8,228u8,97u8,170u8,122u8,45u8,135u8,155u8,243u8,164u8,118u8,108u8,71u8,69u8,194u8,173u8,231u8, - 236u8,214u8,97u8,174u8,126u8,70u8,89u8,94u8,143u8,121u8,112u8,190u8,28u8,252u8,3u8,2u8,16u8,18u8,113u8,181u8, - 21u8,0u8,0u8,0u8,0u8,13u8,109u8,117u8,108u8,116u8,105u8,95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,208u8, - 37u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,92u8,251u8,115u8,211u8,200u8,29u8,255u8,157u8, - 191u8,98u8,185u8,78u8,51u8,22u8,181u8,13u8,49u8,143u8,94u8,29u8,96u8,26u8,32u8,87u8,50u8,64u8,200u8,144u8, - 144u8,107u8,123u8,147u8,202u8,138u8,188u8,182u8,213u8,200u8,146u8,71u8,143u8,60u8,174u8,229u8,127u8,239u8,231u8,187u8, - 187u8,146u8,118u8,165u8,149u8,173u8,228u8,194u8,113u8,87u8,134u8,1u8,98u8,75u8,251u8,125u8,191u8,191u8,203u8,195u8, - 135u8,15u8,217u8,222u8,213u8,42u8,78u8,178u8,148u8,125u8,200u8,195u8,44u8,216u8,155u8,142u8,158u8,62u8,221u8,254u8, - 11u8,91u8,210u8,15u8,131u8,52u8,152u8,71u8,94u8,150u8,39u8,60u8,101u8,65u8,196u8,62u8,196u8,23u8,124u8,120u8, - 239u8,33u8,94u8,57u8,94u8,4u8,41u8,91u8,198u8,211u8,60u8,228u8,108u8,225u8,165u8,44u8,91u8,112u8,198u8,175u8, - 60u8,63u8,99u8,169u8,183u8,228u8,120u8,54u8,227u8,201u8,204u8,243u8,57u8,83u8,223u8,149u8,167u8,138u8,87u8,134u8, - 247u8,238u8,169u8,119u8,189u8,85u8,22u8,167u8,110u8,154u8,77u8,199u8,99u8,1u8,208u8,229u8,234u8,193u8,255u8,220u8, - 99u8,248u8,149u8,167u8,156u8,137u8,47u8,207u8,252u8,116u8,199u8,252u8,132u8,39u8,73u8,156u8,212u8,62u8,155u8,113u8, - 137u8,107u8,237u8,227u8,120u8,149u8,5u8,113u8,52u8,30u8,255u8,231u8,136u8,135u8,179u8,62u8,251u8,40u8,126u8,252u8, - 82u8,123u8,232u8,130u8,251u8,153u8,126u8,160u8,134u8,153u8,194u8,105u8,231u8,158u8,248u8,242u8,225u8,67u8,245u8,23u8, - 219u8,35u8,20u8,152u8,31u8,79u8,121u8,90u8,124u8,163u8,254u8,126u8,200u8,126u8,76u8,226u8,104u8,206u8,162u8,124u8, - 121u8,198u8,19u8,22u8,207u8,216u8,217u8,117u8,6u8,30u8,94u8,242u8,132u8,179u8,121u8,112u8,193u8,35u8,98u8,76u8, - 16u8,173u8,242u8,140u8,93u8,46u8,240u8,19u8,142u8,224u8,73u8,224u8,133u8,193u8,207u8,1u8,94u8,243u8,162u8,146u8, - 97u8,171u8,252u8,44u8,12u8,124u8,118u8,206u8,175u8,135u8,226u8,108u8,63u8,142u8,210u8,140u8,237u8,185u8,63u8,126u8, - 250u8,120u8,240u8,55u8,247u8,240u8,243u8,171u8,119u8,123u8,255u8,112u8,143u8,246u8,255u8,185u8,55u8,102u8,249u8,179u8, - 39u8,236u8,5u8,219u8,222u8,185u8,123u8,36u8,74u8,45u8,176u8,225u8,112u8,180u8,255u8,183u8,131u8,221u8,227u8,207u8, - 159u8,246u8,12u8,52u8,70u8,26u8,26u8,199u8,80u8,128u8,108u8,1u8,185u8,44u8,226u8,112u8,10u8,197u8,194u8,203u8, - 103u8,164u8,35u8,66u8,51u8,18u8,47u8,154u8,115u8,54u8,249u8,105u8,187u8,207u8,162u8,211u8,73u8,159u8,48u8,1u8, - 122u8,17u8,11u8,164u8,226u8,100u8,113u8,230u8,133u8,26u8,9u8,132u8,10u8,79u8,82u8,19u8,145u8,253u8,131u8,147u8, - 221u8,247u8,251u8,111u8,220u8,227u8,183u8,159u8,246u8,142u8,222u8,126u8,124u8,255u8,198u8,253u8,248u8,201u8,61u8,248u8, - 252u8,225u8,213u8,222u8,39u8,247u8,227u8,15u8,2u8,195u8,189u8,79u8,71u8,5u8,106u8,143u8,107u8,168u8,129u8,52u8, - 48u8,131u8,205u8,242u8,200u8,39u8,197u8,72u8,161u8,208u8,248u8,49u8,138u8,9u8,75u8,112u8,37u8,137u8,195u8,144u8, - 79u8,89u8,12u8,46u8,93u8,243u8,204u8,4u8,12u8,194u8,247u8,79u8,246u8,220u8,31u8,62u8,31u8,184u8,7u8,31u8, - 143u8,221u8,221u8,147u8,221u8,253u8,247u8,187u8,175u8,222u8,151u8,92u8,120u8,210u8,208u8,153u8,215u8,244u8,166u8,23u8, - 101u8,77u8,141u8,33u8,84u8,130u8,41u8,143u8,178u8,96u8,22u8,72u8,90u8,137u8,126u8,195u8,38u8,75u8,57u8,176u8, - 212u8,95u8,240u8,37u8,39u8,110u8,5u8,254u8,130u8,120u8,5u8,149u8,157u8,22u8,82u8,76u8,130u8,11u8,18u8,224u8, - 46u8,105u8,48u8,243u8,114u8,28u8,131u8,67u8,125u8,143u8,136u8,35u8,69u8,74u8,161u8,7u8,100u8,180u8,11u8,60u8, - 84u8,66u8,15u8,50u8,176u8,122u8,206u8,241u8,108u8,194u8,46u8,131u8,108u8,65u8,226u8,55u8,96u8,219u8,21u8,81u8, - 19u8,254u8,235u8,183u8,123u8,31u8,32u8,138u8,55u8,160u8,253u8,251u8,154u8,30u8,18u8,105u8,105u8,240u8,51u8,39u8, - 162u8,112u8,110u8,16u8,77u8,129u8,225u8,52u8,135u8,96u8,155u8,167u8,247u8,73u8,49u8,132u8,162u8,14u8,203u8,247u8, - 123u8,187u8,109u8,184u8,8u8,52u8,130u8,20u8,206u8,139u8,116u8,131u8,95u8,240u8,4u8,135u8,74u8,214u8,165u8,96u8, - 207u8,42u8,204u8,149u8,26u8,21u8,234u8,55u8,116u8,52u8,236u8,247u8,15u8,222u8,236u8,159u8,236u8,191u8,249u8,188u8, - 251u8,158u8,44u8,233u8,253u8,254u8,107u8,151u8,172u8,9u8,138u8,227u8,190u8,250u8,199u8,241u8,94u8,165u8,48u8,163u8, - 27u8,209u8,82u8,74u8,169u8,11u8,41u8,149u8,72u8,59u8,82u8,34u8,13u8,128u8,157u8,5u8,217u8,210u8,91u8,181u8, - 81u8,83u8,137u8,165u8,65u8,204u8,179u8,39u8,186u8,131u8,32u8,133u8,49u8,140u8,190u8,141u8,209u8,125u8,147u8,141u8, - 236u8,28u8,90u8,18u8,134u8,100u8,199u8,60u8,34u8,15u8,56u8,133u8,6u8,210u8,1u8,153u8,8u8,12u8,94u8,116u8, - 173u8,19u8,46u8,241u8,171u8,172u8,147u8,28u8,133u8,137u8,213u8,246u8,13u8,144u8,210u8,248u8,75u8,56u8,73u8,78u8, - 224u8,159u8,94u8,38u8,164u8,1u8,69u8,231u8,58u8,171u8,210u8,77u8,152u8,150u8,128u8,27u8,24u8,191u8,218u8,63u8, - 254u8,176u8,123u8,40u8,120u8,8u8,55u8,98u8,32u8,172u8,115u8,241u8,131u8,119u8,165u8,121u8,40u8,222u8,224u8,29u8, - 140u8,48u8,12u8,227u8,75u8,64u8,134u8,62u8,200u8,184u8,90u8,60u8,68u8,223u8,106u8,0u8,63u8,236u8,254u8,93u8, - 243u8,91u8,149u8,78u8,218u8,84u8,177u8,240u8,40u8,71u8,89u8,146u8,251u8,149u8,63u8,161u8,191u8,254u8,240u8,19u8, - 8u8,201u8,220u8,56u8,10u8,175u8,79u8,197u8,7u8,169u8,120u8,134u8,29u8,113u8,63u8,225u8,217u8,59u8,46u8,204u8, - 159u8,77u8,147u8,120u8,165u8,226u8,44u8,253u8,18u8,212u8,143u8,153u8,140u8,131u8,207u8,243u8,239u8,95u8,246u8,197u8, - 87u8,95u8,42u8,58u8,119u8,35u8,246u8,32u8,143u8,46u8,32u8,150u8,41u8,120u8,60u8,125u8,208u8,135u8,22u8,144u8, - 91u8,4u8,209u8,237u8,206u8,66u8,24u8,204u8,68u8,28u8,62u8,97u8,240u8,108u8,208u8,29u8,208u8,154u8,121u8,1u8, - 28u8,109u8,111u8,219u8,41u8,85u8,221u8,95u8,228u8,209u8,57u8,41u8,127u8,9u8,110u8,162u8,152u8,52u8,30u8,219u8, - 140u8,115u8,34u8,17u8,238u8,51u8,238u8,193u8,3u8,10u8,209u8,74u8,93u8,41u8,112u8,56u8,124u8,215u8,135u8,129u8, - 78u8,89u8,111u8,228u8,224u8,83u8,18u8,57u8,242u8,13u8,122u8,167u8,122u8,184u8,166u8,210u8,149u8,149u8,62u8,248u8, - 172u8,145u8,201u8,150u8,220u8,139u8,132u8,70u8,193u8,74u8,161u8,51u8,81u8,204u8,230u8,185u8,135u8,184u8,149u8,113u8, - 46u8,213u8,142u8,206u8,201u8,35u8,56u8,222u8,240u8,154u8,142u8,61u8,124u8,7u8,137u8,227u8,89u8,113u8,4u8,227u8, - 97u8,24u8,32u8,227u8,240u8,153u8,159u8,39u8,136u8,43u8,171u8,24u8,201u8,145u8,48u8,242u8,40u8,142u8,6u8,233u8, - 18u8,170u8,81u8,130u8,141u8,19u8,156u8,49u8,212u8,37u8,166u8,225u8,113u8,40u8,120u8,90u8,8u8,207u8,143u8,87u8, - 176u8,73u8,18u8,97u8,31u8,207u8,198u8,128u8,182u8,70u8,146u8,13u8,65u8,178u8,7u8,26u8,121u8,93u8,132u8,248u8, - 224u8,164u8,201u8,14u8,16u8,14u8,228u8,109u8,196u8,43u8,131u8,43u8,225u8,217u8,89u8,32u8,79u8,0u8,230u8,8u8, - 157u8,194u8,233u8,17u8,47u8,6u8,146u8,7u8,108u8,63u8,99u8,211u8,152u8,167u8,34u8,12u8,71u8,220u8,231u8,105u8, - 234u8,37u8,65u8,120u8,45u8,128u8,19u8,200u8,235u8,210u8,170u8,87u8,73u8,176u8,228u8,242u8,53u8,117u8,242u8,80u8, - 115u8,214u8,144u8,151u8,98u8,165u8,180u8,126u8,233u8,25u8,180u8,0u8,34u8,178u8,86u8,28u8,12u8,158u8,78u8,108u8, - 220u8,158u8,148u8,167u8,149u8,167u8,254u8,128u8,172u8,47u8,138u8,47u8,251u8,210u8,133u8,168u8,227u8,3u8,137u8,171u8, - 136u8,195u8,48u8,115u8,242u8,128u8,80u8,108u8,196u8,114u8,21u8,120u8,203u8,244u8,162u8,207u8,206u8,192u8,110u8,196u8, - 219u8,101u8,48u8,95u8,232u8,153u8,208u8,44u8,175u8,82u8,44u8,117u8,232u8,201u8,87u8,17u8,254u8,42u8,79u8,40u8, - 225u8,7u8,162u8,235u8,50u8,126u8,41u8,31u8,31u8,236u8,6u8,138u8,146u8,20u8,188u8,113u8,17u8,120u8,108u8,82u8, - 62u8,227u8,138u8,207u8,175u8,145u8,38u8,39u8,129u8,159u8,77u8,160u8,192u8,149u8,245u8,182u8,60u8,228u8,102u8,147u8, - 155u8,251u8,4u8,205u8,23u8,88u8,66u8,219u8,196u8,244u8,224u8,237u8,46u8,65u8,139u8,28u8,149u8,103u8,152u8,88u8, - 188u8,252u8,100u8,32u8,28u8,133u8,10u8,45u8,134u8,191u8,144u8,65u8,165u8,202u8,155u8,68u8,198u8,150u8,5u8,69u8, - 224u8,40u8,60u8,109u8,201u8,197u8,95u8,40u8,175u8,194u8,203u8,255u8,80u8,168u8,143u8,145u8,55u8,54u8,28u8,189u8, - 82u8,108u8,40u8,27u8,155u8,115u8,224u8,9u8,221u8,113u8,41u8,198u8,244u8,74u8,55u8,71u8,233u8,25u8,82u8,109u8, - 250u8,203u8,25u8,179u8,94u8,25u8,18u8,250u8,22u8,101u8,115u8,52u8,60u8,189u8,20u8,33u8,57u8,187u8,223u8,219u8, - 102u8,207u8,95u8,104u8,78u8,115u8,107u8,75u8,251u8,1u8,223u8,68u8,224u8,62u8,149u8,69u8,227u8,113u8,32u8,141u8, - 201u8,245u8,146u8,121u8,190u8,4u8,147u8,122u8,93u8,243u8,116u8,199u8,217u8,41u8,161u8,134u8,60u8,99u8,189u8,244u8, - 220u8,85u8,130u8,93u8,169u8,127u8,57u8,8u8,133u8,6u8,125u8,174u8,168u8,56u8,35u8,47u8,172u8,8u8,5u8,145u8, - 181u8,131u8,210u8,115u8,188u8,86u8,197u8,192u8,138u8,54u8,77u8,14u8,5u8,172u8,242u8,187u8,47u8,230u8,25u8,43u8, - 58u8,195u8,98u8,150u8,214u8,195u8,86u8,107u8,14u8,3u8,85u8,68u8,143u8,163u8,139u8,123u8,157u8,56u8,73u8,239u8, - 192u8,75u8,168u8,100u8,226u8,37u8,215u8,242u8,92u8,156u8,49u8,102u8,91u8,154u8,8u8,151u8,233u8,92u8,87u8,37u8, - 135u8,141u8,53u8,77u8,172u8,80u8,180u8,125u8,166u8,243u8,128u8,64u8,149u8,12u8,77u8,207u8,135u8,138u8,251u8,56u8, - 221u8,169u8,72u8,185u8,17u8,226u8,210u8,44u8,158u8,31u8,143u8,133u8,21u8,188u8,108u8,32u8,14u8,110u8,122u8,99u8, - 118u8,220u8,134u8,48u8,113u8,190u8,200u8,222u8,94u8,176u8,210u8,23u8,68u8,252u8,210u8,21u8,246u8,56u8,117u8,151u8, - 20u8,33u8,230u8,188u8,71u8,7u8,105u8,98u8,191u8,37u8,165u8,104u8,55u8,140u8,199u8,89u8,172u8,184u8,188u8,165u8, - 64u8,59u8,78u8,223u8,78u8,62u8,57u8,130u8,67u8,47u8,73u8,85u8,116u8,145u8,37u8,245u8,227u8,145u8,170u8,183u8, - 225u8,0u8,188u8,90u8,230u8,180u8,190u8,180u8,210u8,99u8,13u8,98u8,35u8,170u8,73u8,17u8,199u8,138u8,8u8,2u8, - 119u8,146u8,195u8,216u8,68u8,149u8,234u8,133u8,105u8,12u8,55u8,201u8,253u8,115u8,112u8,166u8,204u8,69u8,254u8,32u8, - 194u8,105u8,126u8,54u8,160u8,96u8,140u8,55u8,95u8,178u8,71u8,101u8,212u8,153u8,34u8,37u8,65u8,160u8,26u8,178u8, - 183u8,200u8,72u8,225u8,102u8,251u8,148u8,26u8,161u8,81u8,147u8,230u8,254u8,162u8,242u8,106u8,18u8,215u8,50u8,147u8, - 73u8,248u8,191u8,161u8,84u8,0u8,49u8,205u8,19u8,114u8,132u8,85u8,144u8,48u8,98u8,28u8,148u8,154u8,147u8,99u8, - 207u8,231u8,179u8,224u8,202u8,213u8,8u8,118u8,97u8,9u8,179u8,36u8,94u8,186u8,63u8,243u8,36u8,118u8,129u8,217u8, - 234u8,60u8,157u8,56u8,34u8,142u8,151u8,48u8,69u8,60u8,167u8,16u8,234u8,123u8,212u8,127u8,89u8,37u8,241u8,89u8, - 200u8,151u8,105u8,147u8,37u8,63u8,114u8,197u8,1u8,0u8,145u8,33u8,120u8,247u8,112u8,159u8,106u8,90u8,111u8,170u8, - 252u8,61u8,244u8,130u8,197u8,81u8,17u8,193u8,136u8,59u8,233u8,70u8,190u8,224u8,199u8,48u8,56u8,231u8,200u8,4u8, - 8u8,7u8,143u8,205u8,227u8,120u8,74u8,222u8,221u8,43u8,225u8,102u8,49u8,248u8,0u8,180u8,166u8,185u8,207u8,197u8, - 113u8,135u8,239u8,152u8,162u8,144u8,136u8,15u8,227u8,57u8,132u8,88u8,116u8,201u8,8u8,75u8,184u8,33u8,66u8,147u8, - 206u8,19u8,194u8,154u8,18u8,78u8,144u8,87u8,17u8,239u8,131u8,4u8,41u8,255u8,42u8,68u8,155u8,12u8,249u8,14u8, - 210u8,127u8,238u8,193u8,105u8,93u8,114u8,193u8,137u8,18u8,106u8,200u8,233u8,85u8,32u8,232u8,9u8,148u8,41u8,110u8, - 81u8,172u8,12u8,162u8,156u8,154u8,107u8,105u8,190u8,36u8,146u8,237u8,153u8,11u8,139u8,207u8,72u8,108u8,169u8,98u8, - 151u8,200u8,52u8,42u8,185u8,10u8,230u8,8u8,166u8,167u8,222u8,140u8,232u8,214u8,18u8,182u8,110u8,242u8,30u8,214u8, - 77u8,157u8,236u8,209u8,144u8,186u8,248u8,138u8,28u8,180u8,148u8,190u8,52u8,167u8,70u8,196u8,67u8,28u8,178u8,230u8, - 185u8,166u8,253u8,135u8,168u8,10u8,95u8,168u8,183u8,198u8,99u8,252u8,52u8,207u8,22u8,189u8,45u8,25u8,14u8,76u8, - 31u8,141u8,90u8,140u8,116u8,12u8,74u8,151u8,226u8,13u8,122u8,239u8,225u8,166u8,82u8,95u8,85u8,85u8,122u8,160u8, - 211u8,79u8,65u8,96u8,107u8,45u8,205u8,214u8,5u8,188u8,70u8,151u8,78u8,143u8,109u8,5u8,32u8,66u8,240u8,143u8, - 155u8,16u8,100u8,47u8,94u8,88u8,11u8,232u8,91u8,67u8,183u8,115u8,92u8,185u8,172u8,134u8,119u8,123u8,179u8,119u8, - 248u8,105u8,239u8,245u8,238u8,241u8,30u8,218u8,59u8,159u8,97u8,157u8,19u8,18u8,245u8,6u8,65u8,187u8,23u8,163u8, - 9u8,52u8,78u8,232u8,245u8,16u8,113u8,23u8,111u8,105u8,207u8,21u8,47u8,151u8,238u8,119u8,2u8,53u8,93u8,18u8, - 230u8,22u8,147u8,239u8,237u8,195u8,3u8,39u8,9u8,167u8,76u8,221u8,97u8,171u8,186u8,171u8,173u8,252u8,172u8,81u8, - 216u8,172u8,117u8,178u8,53u8,181u8,189u8,141u8,210u8,202u8,70u8,242u8,243u8,102u8,54u8,240u8,82u8,83u8,93u8,242u8, - 225u8,113u8,166u8,124u8,209u8,6u8,6u8,8u8,107u8,20u8,254u8,74u8,61u8,110u8,215u8,246u8,205u8,234u8,188u8,86u8, - 97u8,85u8,69u8,67u8,191u8,130u8,25u8,235u8,181u8,192u8,184u8,181u8,70u8,34u8,37u8,52u8,66u8,237u8,58u8,146u8, - 37u8,91u8,29u8,167u8,22u8,157u8,139u8,134u8,125u8,26u8,47u8,121u8,111u8,99u8,178u8,85u8,198u8,115u8,227u8,211u8, - 47u8,90u8,170u8,130u8,2u8,52u8,229u8,45u8,48u8,80u8,133u8,115u8,155u8,8u8,123u8,206u8,13u8,98u8,253u8,205u8, - 20u8,80u8,133u8,201u8,117u8,140u8,33u8,219u8,113u8,110u8,163u8,168u8,120u8,241u8,246u8,186u8,74u8,234u8,112u8,191u8, - 24u8,162u8,212u8,38u8,50u8,20u8,194u8,75u8,52u8,47u8,70u8,46u8,143u8,60u8,196u8,231u8,105u8,175u8,33u8,60u8, - 239u8,12u8,181u8,101u8,175u8,230u8,149u8,208u8,243u8,206u8,120u8,175u8,189u8,97u8,238u8,56u8,122u8,142u8,108u8,32u8, - 100u8,99u8,19u8,224u8,255u8,31u8,171u8,80u8,183u8,250u8,220u8,170u8,29u8,85u8,209u8,189u8,193u8,119u8,217u8,18u8, - 227u8,34u8,32u8,117u8,241u8,8u8,150u8,50u8,156u8,28u8,130u8,165u8,150u8,222u8,28u8,161u8,204u8,9u8,146u8,99u8, - 79u8,223u8,219u8,34u8,19u8,6u8,42u8,200u8,73u8,50u8,226u8,155u8,69u8,204u8,200u8,217u8,144u8,123u8,219u8,98u8, - 93u8,49u8,59u8,81u8,13u8,142u8,162u8,111u8,35u8,74u8,124u8,209u8,168u8,48u8,83u8,91u8,164u8,152u8,105u8,131u8, - 227u8,154u8,106u8,162u8,88u8,208u8,50u8,159u8,222u8,138u8,234u8,27u8,75u8,65u8,189u8,49u8,211u8,217u8,240u8,181u8, - 81u8,92u8,14u8,107u8,197u8,101u8,157u8,53u8,148u8,136u8,182u8,240u8,5u8,214u8,243u8,107u8,113u8,134u8,64u8,53u8, - 120u8,243u8,173u8,89u8,115u8,164u8,198u8,11u8,60u8,109u8,227u8,66u8,209u8,201u8,1u8,159u8,30u8,143u8,6u8,218u8, - 104u8,64u8,163u8,178u8,37u8,215u8,45u8,11u8,71u8,161u8,5u8,182u8,195u8,29u8,221u8,26u8,53u8,26u8,12u8,196u8, - 91u8,208u8,181u8,137u8,179u8,3u8,174u8,29u8,48u8,61u8,185u8,91u8,60u8,43u8,227u8,173u8,208u8,123u8,246u8,164u8, - 5u8,189u8,202u8,113u8,149u8,56u8,225u8,35u8,106u8,18u8,20u8,95u8,180u8,225u8,130u8,199u8,236u8,200u8,52u8,114u8, - 86u8,123u8,36u8,185u8,171u8,28u8,245u8,216u8,59u8,151u8,91u8,18u8,141u8,98u8,95u8,139u8,252u8,84u8,190u8,121u8, - 89u8,198u8,151u8,43u8,106u8,128u8,199u8,165u8,84u8,80u8,224u8,85u8,93u8,235u8,79u8,28u8,244u8,162u8,21u8,58u8, - 57u8,178u8,7u8,47u8,103u8,66u8,129u8,17u8,5u8,59u8,181u8,197u8,103u8,121u8,40u8,78u8,157u8,28u8,32u8,10u8, - 161u8,228u8,163u8,49u8,197u8,101u8,144u8,242u8,117u8,38u8,89u8,64u8,93u8,171u8,160u8,157u8,50u8,134u8,46u8,169u8, - 115u8,161u8,44u8,78u8,93u8,64u8,191u8,67u8,142u8,81u8,122u8,245u8,107u8,48u8,77u8,194u8,105u8,225u8,219u8,137u8, - 108u8,198u8,175u8,73u8,21u8,38u8,34u8,87u8,40u8,109u8,106u8,34u8,7u8,52u8,77u8,62u8,107u8,202u8,14u8,70u8, - 40u8,31u8,191u8,226u8,190u8,236u8,245u8,79u8,84u8,83u8,109u8,82u8,27u8,168u8,248u8,52u8,246u8,17u8,181u8,74u8, - 41u8,140u8,218u8,80u8,5u8,205u8,24u8,81u8,198u8,80u8,239u8,64u8,246u8,86u8,104u8,198u8,83u8,4u8,17u8,154u8, - 243u8,80u8,27u8,102u8,158u8,196u8,249u8,106u8,141u8,39u8,48u8,230u8,6u8,189u8,146u8,125u8,38u8,109u8,186u8,139u8, - 168u8,218u8,115u8,21u8,93u8,45u8,178u8,170u8,30u8,85u8,68u8,54u8,90u8,240u8,144u8,230u8,89u8,28u8,135u8,166u8, - 167u8,177u8,78u8,52u8,202u8,84u8,212u8,196u8,172u8,104u8,38u8,86u8,184u8,148u8,141u8,84u8,9u8,178u8,105u8,16u8, - 70u8,155u8,175u8,216u8,234u8,32u8,165u8,23u8,240u8,32u8,239u8,250u8,140u8,38u8,150u8,147u8,166u8,87u8,175u8,143u8, - 6u8,197u8,196u8,156u8,210u8,114u8,150u8,93u8,175u8,56u8,59u8,30u8,86u8,163u8,42u8,41u8,133u8,218u8,121u8,144u8, - 70u8,9u8,27u8,141u8,45u8,52u8,199u8,240u8,27u8,243u8,180u8,11u8,18u8,41u8,201u8,49u8,190u8,164u8,1u8,250u8, - 34u8,88u8,201u8,213u8,146u8,36u8,70u8,26u8,47u8,90u8,108u8,205u8,165u8,146u8,174u8,114u8,116u8,181u8,46u8,240u8, - 26u8,73u8,118u8,144u8,96u8,217u8,52u8,110u8,8u8,234u8,198u8,237u8,226u8,187u8,23u8,113u8,91u8,7u8,185u8,17u8, - 164u8,104u8,91u8,71u8,21u8,2u8,98u8,97u8,103u8,160u8,236u8,207u8,183u8,48u8,185u8,88u8,10u8,146u8,203u8,91u8, - 29u8,58u8,27u8,237u8,9u8,138u8,121u8,56u8,125u8,122u8,139u8,108u8,165u8,58u8,81u8,58u8,173u8,182u8,115u8,91u8, - 252u8,88u8,225u8,181u8,137u8,36u8,181u8,63u8,17u8,25u8,29u8,90u8,17u8,25u8,52u8,26u8,88u8,54u8,192u8,168u8, - 122u8,16u8,207u8,6u8,181u8,81u8,245u8,225u8,187u8,202u8,69u8,237u8,207u8,100u8,83u8,184u8,165u8,39u8,122u8,41u8, - 122u8,161u8,43u8,212u8,58u8,122u8,243u8,22u8,160u8,108u8,129u8,127u8,34u8,86u8,76u8,212u8,162u8,154u8,192u8,22u8, - 72u8,232u8,93u8,228u8,18u8,170u8,74u8,150u8,19u8,30u8,146u8,231u8,204u8,87u8,177u8,88u8,174u8,83u8,77u8,43u8, - 107u8,15u8,27u8,6u8,119u8,129u8,254u8,114u8,213u8,193u8,22u8,181u8,31u8,220u8,166u8,183u8,34u8,87u8,29u8,10u8, - 133u8,19u8,155u8,105u8,48u8,88u8,114u8,185u8,162u8,209u8,107u8,67u8,82u8,68u8,51u8,129u8,231u8,196u8,42u8,110u8, - 173u8,147u8,89u8,141u8,74u8,47u8,11u8,186u8,100u8,249u8,57u8,89u8,157u8,79u8,200u8,67u8,156u8,195u8,91u8,68u8, - 4u8,241u8,76u8,237u8,47u8,116u8,213u8,41u8,13u8,200u8,90u8,77u8,202u8,191u8,239u8,212u8,215u8,45u8,149u8,70u8, - 51u8,206u8,94u8,175u8,83u8,47u8,215u8,33u8,78u8,98u8,186u8,217u8,65u8,221u8,178u8,219u8,170u8,27u8,235u8,5u8, - 67u8,62u8,172u8,45u8,69u8,57u8,152u8,127u8,171u8,172u8,162u8,154u8,164u8,204u8,202u8,65u8,119u8,185u8,207u8,80u8, - 182u8,50u8,149u8,147u8,18u8,179u8,23u8,200u8,190u8,208u8,226u8,14u8,6u8,92u8,128u8,236u8,146u8,140u8,152u8,70u8, - 43u8,226u8,178u8,11u8,141u8,113u8,231u8,60u8,51u8,14u8,106u8,177u8,210u8,175u8,235u8,161u8,110u8,234u8,159u8,78u8, - 126u8,83u8,222u8,137u8,221u8,204u8,53u8,29u8,137u8,73u8,155u8,152u8,253u8,196u8,201u8,210u8,203u8,36u8,183u8,96u8, - 117u8,80u8,40u8,218u8,19u8,16u8,251u8,166u8,229u8,145u8,253u8,118u8,183u8,67u8,150u8,90u8,238u8,32u8,77u8,149u8, - 185u8,26u8,206u8,102u8,19u8,139u8,27u8,230u8,122u8,242u8,251u8,51u8,86u8,118u8,123u8,75u8,237u8,168u8,135u8,166u8, - 153u8,221u8,17u8,139u8,244u8,135u8,75u8,8u8,66u8,71u8,241u8,222u8,131u8,226u8,69u8,116u8,81u8,147u8,248u8,178u8, - 122u8,177u8,47u8,14u8,30u8,176u8,109u8,157u8,201u8,230u8,235u8,141u8,214u8,88u8,49u8,249u8,84u8,99u8,225u8,34u8, - 67u8,4u8,191u8,42u8,253u8,243u8,214u8,241u8,174u8,238u8,169u8,82u8,38u8,214u8,63u8,34u8,250u8,3u8,243u8,109u8, - 37u8,12u8,125u8,187u8,21u8,153u8,253u8,228u8,100u8,205u8,40u8,146u8,166u8,201u8,166u8,230u8,138u8,80u8,44u8,172u8, - 64u8,184u8,38u8,181u8,210u8,90u8,215u8,129u8,106u8,201u8,36u8,147u8,24u8,40u8,71u8,99u8,241u8,153u8,109u8,30u8, - 110u8,77u8,91u8,220u8,244u8,28u8,221u8,167u8,141u8,212u8,159u8,22u8,79u8,190u8,96u8,143u8,234u8,29u8,104u8,105u8, - 184u8,102u8,147u8,24u8,112u8,122u8,246u8,78u8,183u8,169u8,12u8,100u8,157u8,241u8,76u8,250u8,45u8,53u8,194u8,252u8, - 227u8,230u8,17u8,102u8,125u8,6u8,138u8,19u8,196u8,246u8,105u8,231u8,25u8,232u8,205u8,52u8,179u8,93u8,45u8,137u8, - 45u8,6u8,2u8,96u8,15u8,251u8,239u8,127u8,13u8,164u8,94u8,182u8,15u8,168u8,232u8,209u8,22u8,94u8,220u8,127u8, - 1u8,72u8,55u8,103u8,180u8,236u8,222u8,19u8,90u8,117u8,178u8,20u8,102u8,181u8,143u8,95u8,154u8,248u8,75u8,23u8, - 117u8,107u8,184u8,107u8,95u8,19u8,3u8,10u8,19u8,252u8,154u8,177u8,193u8,87u8,8u8,198u8,181u8,78u8,199u8,250u8, - 88u8,233u8,218u8,140u8,200u8,26u8,126u8,11u8,109u8,89u8,229u8,233u8,194u8,61u8,243u8,252u8,243u8,222u8,214u8,50u8, - 207u8,202u8,149u8,168u8,190u8,237u8,234u8,128u8,102u8,89u8,226u8,226u8,13u8,93u8,82u8,0u8,135u8,22u8,222u8,99u8, - 119u8,244u8,244u8,89u8,9u8,222u8,177u8,175u8,200u8,29u8,212u8,238u8,113u8,52u8,110u8,88u8,116u8,106u8,8u8,234u8, - 19u8,184u8,170u8,51u8,104u8,86u8,230u8,151u8,158u8,108u8,104u8,84u8,238u8,49u8,88u8,174u8,66u8,78u8,141u8,65u8, - 62u8,29u8,55u8,146u8,125u8,182u8,109u8,238u8,179u8,106u8,19u8,94u8,125u8,43u8,197u8,216u8,24u8,47u8,182u8,83u8, - 100u8,43u8,62u8,4u8,18u8,162u8,251u8,85u8,44u8,112u8,172u8,243u8,177u8,21u8,92u8,154u8,233u8,204u8,105u8,171u8, - 50u8,83u8,231u8,68u8,177u8,182u8,202u8,33u8,59u8,56u8,162u8,204u8,45u8,150u8,128u8,38u8,155u8,234u8,95u8,53u8, - 163u8,150u8,251u8,33u8,50u8,111u8,213u8,118u8,74u8,134u8,6u8,104u8,193u8,51u8,181u8,22u8,235u8,33u8,248u8,248u8, - 121u8,18u8,100u8,96u8,85u8,154u8,230u8,92u8,174u8,221u8,120u8,5u8,7u8,35u8,148u8,227u8,242u8,11u8,213u8,132u8, - 85u8,43u8,68u8,235u8,23u8,136u8,52u8,112u8,35u8,193u8,97u8,127u8,129u8,217u8,18u8,89u8,71u8,28u8,163u8,83u8, - 2u8,220u8,230u8,30u8,116u8,53u8,152u8,105u8,235u8,54u8,50u8,100u8,17u8,90u8,10u8,111u8,209u8,26u8,81u8,131u8, - 119u8,122u8,155u8,66u8,163u8,226u8,138u8,232u8,116u8,8u8,106u8,151u8,96u8,162u8,216u8,131u8,169u8,5u8,188u8,166u8, - 106u8,29u8,141u8,43u8,164u8,6u8,93u8,58u8,125u8,250u8,227u8,22u8,125u8,108u8,64u8,146u8,65u8,145u8,77u8,208u8, - 82u8,231u8,147u8,130u8,54u8,233u8,28u8,205u8,34u8,118u8,82u8,212u8,164u8,98u8,93u8,98u8,42u8,203u8,75u8,41u8, - 170u8,150u8,185u8,52u8,88u8,37u8,82u8,37u8,162u8,185u8,218u8,232u8,174u8,70u8,150u8,72u8,187u8,68u8,176u8,22u8, - 91u8,214u8,131u8,56u8,26u8,200u8,117u8,110u8,170u8,62u8,33u8,227u8,65u8,160u8,214u8,217u8,7u8,69u8,107u8,79u8, - 173u8,94u8,89u8,218u8,180u8,51u8,15u8,126u8,177u8,209u8,122u8,173u8,46u8,98u8,117u8,88u8,26u8,168u8,249u8,32u8, - 234u8,0u8,237u8,220u8,251u8,13u8,112u8,169u8,72u8,80u8,191u8,37u8,167u8,26u8,227u8,241u8,78u8,204u8,34u8,94u8, - 21u8,172u8,90u8,223u8,77u8,134u8,11u8,44u8,123u8,195u8,197u8,106u8,120u8,90u8,186u8,154u8,230u8,251u8,26u8,187u8, - 219u8,73u8,4u8,100u8,30u8,16u8,153u8,149u8,5u8,13u8,196u8,162u8,184u8,74u8,247u8,13u8,161u8,77u8,99u8,233u8, - 86u8,32u8,131u8,138u8,209u8,105u8,197u8,105u8,209u8,246u8,108u8,103u8,116u8,191u8,6u8,194u8,188u8,230u8,218u8,160u8, - 247u8,14u8,161u8,17u8,115u8,214u8,243u8,179u8,12u8,19u8,210u8,9u8,53u8,4u8,190u8,177u8,69u8,217u8,218u8,40u8, - 175u8,95u8,237u8,169u8,55u8,202u8,109u8,223u8,111u8,232u8,142u8,239u8,180u8,236u8,255u8,106u8,248u8,110u8,218u8,211u8, - 174u8,45u8,164u8,107u8,72u8,104u8,58u8,219u8,1u8,78u8,125u8,151u8,215u8,160u8,199u8,70u8,135u8,145u8,185u8,52u8, - 174u8,85u8,29u8,3u8,208u8,134u8,101u8,123u8,53u8,214u8,164u8,39u8,63u8,200u8,227u8,107u8,219u8,254u8,90u8,58u8, - 52u8,139u8,99u8,59u8,131u8,207u8,188u8,68u8,92u8,238u8,234u8,119u8,221u8,167u8,94u8,122u8,121u8,232u8,138u8,96u8, - 86u8,109u8,123u8,168u8,89u8,41u8,37u8,88u8,213u8,188u8,180u8,86u8,209u8,148u8,175u8,20u8,9u8,189u8,153u8,207u8, - 187u8,120u8,89u8,166u8,104u8,229u8,64u8,181u8,143u8,162u8,166u8,202u8,200u8,30u8,52u8,14u8,168u8,127u8,242u8,47u8, - 246u8,232u8,106u8,54u8,219u8,41u8,8u8,209u8,40u8,233u8,205u8,174u8,240u8,252u8,95u8,145u8,212u8,57u8,167u8,101u8, - 210u8,217u8,37u8,198u8,227u8,197u8,177u8,186u8,114u8,161u8,211u8,83u8,237u8,40u8,33u8,104u8,71u8,115u8,238u8,170u8, - 15u8,220u8,89u8,232u8,205u8,209u8,110u8,159u8,93u8,245u8,21u8,121u8,63u8,177u8,142u8,251u8,76u8,234u8,177u8,158u8, - 115u8,90u8,190u8,122u8,90u8,43u8,214u8,139u8,114u8,172u8,18u8,227u8,79u8,219u8,249u8,247u8,167u8,59u8,93u8,23u8, - 104u8,80u8,108u8,108u8,247u8,205u8,74u8,137u8,20u8,45u8,185u8,174u8,223u8,188u8,174u8,185u8,79u8,91u8,174u8,39u8, - 238u8,232u8,62u8,170u8,110u8,182u8,168u8,168u8,67u8,17u8,102u8,91u8,43u8,150u8,9u8,242u8,208u8,128u8,70u8,73u8, - 153u8,236u8,129u8,211u8,114u8,115u8,136u8,220u8,149u8,50u8,89u8,49u8,26u8,165u8,150u8,147u8,218u8,134u8,134u8,95u8, - 75u8,144u8,205u8,226u8,91u8,114u8,71u8,67u8,56u8,235u8,185u8,151u8,76u8,67u8,104u8,121u8,191u8,145u8,245u8,149u8, - 55u8,177u8,212u8,30u8,241u8,52u8,72u8,151u8,200u8,229u8,248u8,84u8,135u8,219u8,97u8,145u8,184u8,96u8,51u8,85u8, - 68u8,224u8,114u8,231u8,173u8,76u8,203u8,74u8,109u8,81u8,92u8,5u8,169u8,75u8,101u8,89u8,111u8,107u8,109u8,79u8, - 192u8,113u8,132u8,84u8,36u8,162u8,228u8,184u8,169u8,157u8,158u8,160u8,252u8,89u8,119u8,174u8,168u8,219u8,182u8,232u8, - 79u8,188u8,61u8,18u8,111u8,183u8,255u8,18u8,43u8,245u8,42u8,215u8,53u8,132u8,191u8,31u8,137u8,109u8,238u8,196u8, - 75u8,51u8,217u8,162u8,186u8,24u8,137u8,89u8,128u8,96u8,233u8,204u8,11u8,66u8,83u8,53u8,250u8,98u8,11u8,60u8, - 83u8,155u8,229u8,38u8,219u8,136u8,208u8,110u8,108u8,43u8,119u8,4u8,187u8,112u8,142u8,254u8,4u8,133u8,143u8,157u8, - 157u8,166u8,131u8,178u8,154u8,181u8,240u8,92u8,134u8,81u8,147u8,174u8,162u8,197u8,228u8,154u8,85u8,238u8,55u8,55u8, - 109u8,243u8,146u8,145u8,75u8,138u8,13u8,151u8,227u8,232u8,19u8,67u8,243u8,14u8,85u8,139u8,59u8,40u8,159u8,94u8, - 191u8,98u8,179u8,37u8,143u8,191u8,137u8,183u8,216u8,208u8,176u8,185u8,83u8,95u8,178u8,221u8,240u8,37u8,228u8,10u8, - 80u8,48u8,154u8,206u8,68u8,85u8,164u8,58u8,208u8,242u8,226u8,70u8,225u8,68u8,110u8,169u8,150u8,223u8,210u8,154u8, - 13u8,93u8,239u8,96u8,205u8,54u8,91u8,62u8,10u8,150u8,65u8,232u8,161u8,28u8,104u8,90u8,178u8,184u8,166u8,211u8, - 52u8,231u8,95u8,217u8,128u8,55u8,17u8,37u8,47u8,19u8,41u8,202u8,110u8,107u8,236u8,2u8,75u8,145u8,6u8,187u8, - 226u8,78u8,174u8,12u8,228u8,191u8,169u8,56u8,142u8,207u8,82u8,106u8,105u8,137u8,52u8,94u8,120u8,39u8,205u8,53u8, - 109u8,155u8,1u8,222u8,104u8,221u8,33u8,122u8,63u8,250u8,42u8,191u8,13u8,40u8,242u8,195u8,237u8,234u8,195u8,83u8, - 203u8,237u8,148u8,66u8,204u8,252u8,10u8,193u8,195u8,87u8,153u8,91u8,155u8,93u8,172u8,39u8,216u8,249u8,10u8,153u8, - 201u8,118u8,167u8,204u8,196u8,12u8,103u8,125u8,29u8,54u8,191u8,242u8,249u8,74u8,141u8,45u8,202u8,150u8,77u8,113u8, - 73u8,92u8,93u8,148u8,87u8,215u8,187u8,171u8,91u8,86u8,191u8,212u8,1u8,109u8,224u8,211u8,206u8,77u8,60u8,200u8, - 55u8,116u8,13u8,119u8,70u8,134u8,53u8,232u8,215u8,140u8,31u8,33u8,82u8,212u8,33u8,170u8,26u8,237u8,213u8,203u8, - 142u8,18u8,114u8,149u8,57u8,211u8,255u8,245u8,131u8,223u8,35u8,241u8,251u8,49u8,254u8,253u8,180u8,127u8,186u8,67u8, - 108u8,50u8,6u8,61u8,200u8,52u8,169u8,171u8,26u8,248u8,65u8,53u8,127u8,42u8,23u8,145u8,181u8,86u8,169u8,121u8, - 61u8,215u8,75u8,178u8,107u8,215u8,135u8,211u8,207u8,76u8,120u8,21u8,48u8,216u8,214u8,227u8,81u8,255u8,180u8,230u8, - 18u8,136u8,20u8,223u8,75u8,185u8,124u8,215u8,54u8,192u8,211u8,78u8,118u8,218u8,94u8,14u8,166u8,228u8,35u8,31u8, - 105u8,194u8,71u8,180u8,12u8,57u8,198u8,14u8,198u8,35u8,207u8,235u8,240u8,234u8,163u8,5u8,131u8,113u8,182u8,217u8, - 139u8,206u8,37u8,227u8,108u8,13u8,181u8,226u8,32u8,209u8,138u8,112u8,197u8,255u8,89u8,99u8,155u8,47u8,106u8,116u8, - 53u8,207u8,106u8,28u8,86u8,92u8,91u8,174u8,95u8,192u8,214u8,239u8,93u8,87u8,0u8,107u8,216u8,148u8,25u8,207u8, - 250u8,97u8,235u8,150u8,56u8,95u8,187u8,101u8,46u8,93u8,83u8,231u8,147u8,244u8,217u8,118u8,113u8,86u8,133u8,147u8, - 180u8,78u8,219u8,97u8,155u8,90u8,105u8,229u8,12u8,87u8,90u8,70u8,131u8,55u8,185u8,184u8,28u8,190u8,126u8,245u8, - 189u8,5u8,180u8,213u8,157u8,111u8,92u8,254u8,216u8,2u8,68u8,167u8,206u8,170u8,39u8,45u8,32u8,58u8,236u8,237u8, - 136u8,243u8,234u8,220u8,122u8,106u8,35u8,117u8,153u8,206u8,41u8,80u8,158u8,125u8,247u8,150u8,135u8,97u8,44u8,71u8, - 81u8,247u8,191u8,107u8,234u8,30u8,60u8,3u8,61u8,103u8,189u8,188u8,190u8,69u8,170u8,68u8,7u8,181u8,32u8,220u8, - 182u8,8u8,186u8,69u8,135u8,246u8,25u8,33u8,171u8,222u8,239u8,179u8,103u8,54u8,36u8,49u8,15u8,25u8,1u8,184u8, - 222u8,159u8,105u8,94u8,251u8,17u8,141u8,153u8,130u8,14u8,186u8,49u8,113u8,255u8,187u8,126u8,243u8,110u8,16u8,53u8, - 105u8,138u8,30u8,141u8,54u8,190u8,181u8,144u8,59u8,42u8,200u8,149u8,221u8,33u8,73u8,37u8,181u8,132u8,4u8,58u8, - 55u8,35u8,213u8,149u8,196u8,142u8,10u8,98u8,171u8,99u8,250u8,236u8,207u8,117u8,138u8,235u8,94u8,200u8,252u8,249u8, - 79u8,244u8,95u8,44u8,89u8,199u8,138u8,86u8,175u8,174u8,141u8,95u8,227u8,204u8,93u8,34u8,155u8,144u8,35u8,31u8, - 40u8,105u8,195u8,195u8,147u8,75u8,176u8,121u8,132u8,39u8,82u8,119u8,110u8,105u8,252u8,79u8,76u8,163u8,191u8,177u8, - 193u8,63u8,53u8,237u8,252u8,151u8,218u8,248u8,45u8,236u8,251u8,174u8,108u8,251u8,137u8,105u8,211u8,183u8,177u8,231u8, - 167u8,53u8,51u8,238u8,98u8,194u8,183u8,49u8,95u8,107u8,111u8,178u8,104u8,42u8,218u8,196u8,121u8,255u8,214u8,70u8, - 222u8,201u8,192u8,55u8,26u8,119u8,195u8,176u8,191u8,52u8,24u8,208u8,217u8,160u8,215u8,18u8,63u8,186u8,1u8,241u8, - 155u8,204u8,94u8,26u8,239u8,151u8,123u8,255u8,3u8,241u8,90u8,44u8,168u8,188u8,83u8,0u8,0u8,0u8,0u8,10u8, - 115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,147u8,13u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,221u8,88u8,123u8,111u8,219u8,54u8,16u8,255u8,63u8,159u8,226u8,138u8,2u8,134u8,180u8,105u8,73u8,108u8, - 167u8,47u8,165u8,9u8,80u8,160u8,41u8,80u8,116u8,107u8,134u8,53u8,27u8,86u8,20u8,131u8,64u8,91u8,76u8,76u8, - 88u8,175u8,73u8,148u8,29u8,163u8,246u8,119u8,223u8,29u8,41u8,91u8,148u8,44u8,217u8,114u8,155u8,12u8,195u8,242u8, - 79u8,96u8,242u8,120u8,143u8,223u8,189u8,117u8,114u8,114u8,2u8,55u8,19u8,145u8,65u8,24u8,251u8,121u8,192u8,33u8, - 73u8,227u8,153u8,240u8,121u8,6u8,12u8,178u8,56u8,200u8,165u8,136u8,35u8,184u8,141u8,83u8,252u8,145u8,74u8,238u8, - 67u8,200u8,146u8,204u8,1u8,57u8,97u8,18u8,240u8,141u8,144u8,48u8,97u8,25u8,254u8,84u8,239u8,18u8,158u8,74u8, - 193u8,51u8,117u8,123u8,116u8,130u8,124u8,251u8,54u8,124u8,224u8,139u8,12u8,146u8,88u8,68u8,18u8,100u8,12u8,127u8, - 176u8,32u8,231u8,153u8,186u8,27u8,216u8,112u8,197u8,198u8,19u8,34u8,128u8,48u8,207u8,36u8,140u8,56u8,228u8,145u8, - 248u8,59u8,231u8,234u8,122u8,104u8,195u8,27u8,117u8,55u8,102u8,17u8,93u8,221u8,198u8,121u8,228u8,195u8,92u8,200u8, - 137u8,136u8,224u8,218u8,250u8,104u8,131u8,20u8,161u8,38u8,61u8,179u8,209u8,0u8,14u8,83u8,146u8,196u8,82u8,226u8, - 162u8,117u8,61u8,86u8,183u8,207u8,144u8,145u8,239u8,227u8,13u8,62u8,79u8,121u8,24u8,207u8,88u8,128u8,26u8,178u8, - 41u8,55u8,184u8,20u8,166u8,179u8,68u8,198u8,153u8,151u8,73u8,223u8,117u8,51u8,17u8,38u8,1u8,247u8,208u8,90u8, - 248u8,122u8,4u8,248u8,151u8,103u8,28u8,212u8,13u8,79u8,211u8,56u8,61u8,175u8,158u8,197u8,9u8,193u8,84u8,59u8, - 156u8,241u8,177u8,36u8,74u8,117u8,74u8,154u8,252u8,130u8,204u8,80u8,73u8,96u8,65u8,202u8,153u8,191u8,0u8,126u8, - 47u8,50u8,153u8,169u8,235u8,113u8,28u8,33u8,2u8,87u8,31u8,174u8,62u8,123u8,111u8,126u8,254u8,237u8,234u8,205u8, - 219u8,207u8,222u8,213u8,159u8,239u8,63u8,221u8,124u8,114u8,33u8,127u8,126u8,6u8,23u8,208u8,63u8,223u8,98u8,131u8, - 240u8,71u8,177u8,212u8,192u8,212u8,153u8,124u8,188u8,190u8,241u8,222u8,93u8,255u8,254u8,241u8,237u8,250u8,253u8,160u8, - 208u8,35u8,147u8,105u8,62u8,150u8,240u8,73u8,153u8,135u8,156u8,94u8,35u8,196u8,142u8,118u8,203u8,165u8,114u8,229u8, - 56u8,78u8,240u8,192u8,71u8,103u8,58u8,72u8,28u8,35u8,156u8,218u8,126u8,250u8,243u8,153u8,100u8,46u8,104u8,187u8, - 94u8,95u8,5u8,60u8,228u8,145u8,52u8,25u8,92u8,58u8,138u8,116u8,85u8,17u8,213u8,64u8,183u8,87u8,16u8,154u8, - 231u8,146u8,243u8,157u8,205u8,201u8,140u8,30u8,186u8,250u8,125u8,69u8,74u8,146u8,143u8,2u8,49u8,134u8,219u8,60u8, - 130u8,128u8,71u8,119u8,114u8,66u8,130u8,92u8,205u8,177u8,144u8,87u8,252u8,186u8,180u8,208u8,155u8,46u8,244u8,26u8, - 109u8,183u8,53u8,80u8,165u8,10u8,218u8,74u8,215u8,213u8,76u8,173u8,30u8,190u8,61u8,38u8,251u8,237u8,22u8,225u8, - 99u8,244u8,169u8,228u8,59u8,132u8,163u8,132u8,102u8,212u8,75u8,153u8,155u8,123u8,227u8,172u8,142u8,59u8,6u8,96u8, - 152u8,200u8,133u8,101u8,151u8,216u8,172u8,90u8,84u8,26u8,197u8,24u8,170u8,243u8,29u8,42u8,109u8,56u8,236u8,0u8, - 198u8,169u8,58u8,165u8,183u8,241u8,10u8,218u8,211u8,83u8,36u8,134u8,178u8,1u8,151u8,200u8,107u8,49u8,226u8,158u8, - 240u8,239u8,49u8,234u8,110u8,69u8,228u8,19u8,234u8,14u8,61u8,182u8,207u8,55u8,116u8,44u8,203u8,176u8,84u8,60u8, - 177u8,116u8,222u8,184u8,174u8,192u8,172u8,139u8,67u8,78u8,40u8,23u8,143u8,109u8,7u8,84u8,162u8,225u8,93u8,132u8, - 190u8,23u8,190u8,199u8,210u8,187u8,156u8,2u8,201u8,170u8,6u8,184u8,109u8,176u8,37u8,241u8,90u8,240u8,154u8,49u8, - 191u8,151u8,41u8,27u8,75u8,100u8,156u8,27u8,154u8,25u8,79u8,122u8,107u8,84u8,53u8,88u8,165u8,159u8,29u8,226u8, - 100u8,31u8,171u8,184u8,219u8,137u8,175u8,135u8,172u8,187u8,99u8,76u8,122u8,28u8,142u8,51u8,189u8,250u8,31u8,96u8, - 77u8,23u8,85u8,188u8,9u8,188u8,245u8,131u8,206u8,184u8,99u8,157u8,147u8,76u8,68u8,153u8,135u8,134u8,62u8,98u8, - 116u8,143u8,226u8,56u8,248u8,6u8,188u8,119u8,224u8,220u8,98u8,15u8,54u8,91u8,153u8,198u8,11u8,79u8,37u8,246u8, - 190u8,242u8,213u8,92u8,189u8,106u8,122u8,26u8,133u8,68u8,21u8,15u8,88u8,161u8,194u8,248u8,252u8,124u8,171u8,188u8, - 85u8,100u8,91u8,170u8,192u8,157u8,183u8,168u8,201u8,124u8,255u8,225u8,227u8,124u8,79u8,145u8,183u8,59u8,57u8,160u8, - 183u8,55u8,226u8,163u8,56u8,58u8,36u8,226u8,171u8,61u8,152u8,194u8,126u8,11u8,184u8,36u8,207u8,38u8,222u8,136u8, - 141u8,167u8,245u8,240u8,45u8,250u8,29u8,34u8,63u8,37u8,171u8,149u8,77u8,176u8,170u8,162u8,74u8,109u8,252u8,125u8, - 68u8,90u8,18u8,209u8,137u8,166u8,73u8,152u8,72u8,1u8,7u8,173u8,60u8,65u8,70u8,56u8,140u8,68u8,122u8,64u8, - 16u8,209u8,157u8,106u8,247u8,56u8,61u8,49u8,136u8,248u8,28u8,202u8,212u8,48u8,156u8,147u8,39u8,196u8,237u8,95u8, - 244u8,207u8,58u8,75u8,44u8,99u8,0u8,114u8,221u8,107u8,245u8,159u8,248u8,93u8,58u8,208u8,116u8,211u8,28u8,175u8, - 42u8,70u8,47u8,160u8,130u8,100u8,181u8,212u8,96u8,27u8,70u8,130u8,90u8,83u8,54u8,194u8,117u8,83u8,146u8,144u8, - 234u8,180u8,60u8,154u8,79u8,4u8,206u8,117u8,150u8,128u8,215u8,196u8,193u8,174u8,245u8,85u8,122u8,192u8,11u8,119u8, - 93u8,212u8,42u8,147u8,85u8,20u8,35u8,131u8,63u8,253u8,137u8,91u8,176u8,122u8,197u8,155u8,99u8,114u8,203u8,197u8, - 133u8,14u8,191u8,26u8,235u8,230u8,80u8,105u8,143u8,144u8,85u8,77u8,142u8,249u8,62u8,155u8,179u8,100u8,173u8,142u8, - 163u8,236u8,216u8,166u8,37u8,83u8,154u8,35u8,207u8,176u8,44u8,137u8,147u8,82u8,143u8,6u8,38u8,41u8,151u8,121u8, - 26u8,213u8,92u8,170u8,138u8,24u8,89u8,88u8,115u8,168u8,58u8,87u8,50u8,108u8,187u8,194u8,105u8,85u8,67u8,12u8, - 21u8,16u8,240u8,227u8,122u8,130u8,173u8,81u8,124u8,95u8,58u8,209u8,95u8,85u8,91u8,149u8,232u8,117u8,85u8,245u8, - 161u8,93u8,207u8,192u8,155u8,148u8,69u8,25u8,174u8,54u8,161u8,218u8,95u8,104u8,212u8,199u8,45u8,37u8,6u8,57u8, - 143u8,11u8,181u8,50u8,181u8,111u8,168u8,91u8,189u8,92u8,224u8,10u8,161u8,20u8,200u8,16u8,170u8,44u8,65u8,26u8, - 49u8,227u8,193u8,98u8,195u8,240u8,215u8,84u8,132u8,44u8,21u8,193u8,130u8,150u8,0u8,159u8,50u8,182u8,40u8,175u8, - 152u8,185u8,200u8,190u8,158u8,179u8,50u8,246u8,80u8,142u8,71u8,121u8,223u8,57u8,113u8,219u8,134u8,87u8,171u8,152u8, - 202u8,117u8,230u8,21u8,63u8,154u8,147u8,141u8,76u8,217u8,76u8,241u8,68u8,111u8,68u8,72u8,49u8,91u8,86u8,115u8, - 74u8,155u8,236u8,86u8,185u8,238u8,123u8,212u8,189u8,5u8,161u8,7u8,60u8,142u8,155u8,96u8,17u8,226u8,75u8,190u8, - 196u8,39u8,59u8,227u8,153u8,159u8,183u8,197u8,13u8,217u8,86u8,52u8,228u8,54u8,18u8,109u8,77u8,193u8,14u8,201u8, - 42u8,177u8,164u8,223u8,107u8,146u8,173u8,120u8,121u8,135u8,197u8,153u8,214u8,95u8,189u8,253u8,226u8,94u8,74u8,235u8, - 23u8,174u8,166u8,180u8,195u8,36u8,228u8,111u8,218u8,162u8,5u8,45u8,206u8,184u8,53u8,7u8,66u8,46u8,204u8,8u8, - 192u8,32u8,10u8,55u8,156u8,242u8,140u8,170u8,122u8,192u8,194u8,145u8,79u8,219u8,115u8,133u8,10u8,131u8,208u8,23u8, - 184u8,129u8,231u8,44u8,168u8,71u8,221u8,177u8,25u8,65u8,34u8,10u8,68u8,196u8,205u8,1u8,226u8,251u8,130u8,168u8, - 44u8,243u8,254u8,212u8,133u8,37u8,222u8,44u8,141u8,163u8,25u8,30u8,41u8,186u8,101u8,99u8,95u8,174u8,2u8,135u8, - 46u8,50u8,98u8,155u8,122u8,180u8,221u8,58u8,121u8,20u8,47u8,151u8,222u8,116u8,137u8,130u8,45u8,111u8,106u8,239u8, - 160u8,93u8,251u8,110u8,233u8,205u8,144u8,122u8,102u8,121u8,51u8,187u8,117u8,92u8,81u8,187u8,62u8,127u8,228u8,201u8, - 220u8,42u8,233u8,236u8,255u8,214u8,108u8,158u8,4u8,108u8,188u8,238u8,108u8,157u8,39u8,244u8,78u8,93u8,132u8,218u8, - 145u8,167u8,209u8,173u8,23u8,235u8,141u8,208u8,90u8,78u8,173u8,179u8,205u8,116u8,21u8,249u8,136u8,112u8,121u8,196u8, - 9u8,190u8,54u8,123u8,224u8,130u8,127u8,249u8,181u8,62u8,85u8,220u8,109u8,143u8,21u8,229u8,174u8,127u8,192u8,108u8, - 113u8,119u8,208u8,112u8,97u8,174u8,153u8,29u8,38u8,12u8,138u8,154u8,175u8,109u8,237u8,186u8,210u8,144u8,197u8,119u8, - 244u8,98u8,179u8,93u8,42u8,176u8,172u8,138u8,195u8,158u8,126u8,145u8,152u8,134u8,127u8,53u8,172u8,5u8,69u8,52u8, - 224u8,215u8,178u8,8u8,251u8,192u8,86u8,30u8,36u8,40u8,181u8,248u8,60u8,130u8,92u8,29u8,208u8,172u8,141u8,153u8, - 122u8,157u8,6u8,6u8,254u8,54u8,153u8,125u8,234u8,192u8,105u8,67u8,182u8,60u8,49u8,23u8,64u8,69u8,141u8,43u8, - 192u8,16u8,51u8,165u8,111u8,18u8,251u8,254u8,38u8,58u8,29u8,24u8,214u8,46u8,91u8,4u8,246u8,29u8,252u8,44u8, - 185u8,77u8,214u8,38u8,111u8,216u8,64u8,107u8,56u8,88u8,81u8,169u8,1u8,17u8,249u8,158u8,25u8,180u8,63u8,52u8, - 44u8,191u8,5u8,49u8,125u8,168u8,235u8,196u8,114u8,224u8,224u8,151u8,205u8,6u8,12u8,27u8,193u8,25u8,160u8,178u8, - 207u8,91u8,193u8,41u8,88u8,237u8,3u8,7u8,201u8,94u8,116u8,4u8,135u8,228u8,189u8,220u8,11u8,206u8,64u8,91u8, - 242u8,204u8,129u8,87u8,251u8,193u8,33u8,98u8,120u8,213u8,145u8,229u8,43u8,116u8,247u8,169u8,137u8,78u8,173u8,92u8, - 41u8,202u8,78u8,225u8,208u8,239u8,119u8,13u8,64u8,178u8,185u8,63u8,176u8,187u8,187u8,175u8,63u8,220u8,173u8,225u8, - 176u8,131u8,134u8,152u8,33u8,253u8,179u8,131u8,82u8,164u8,18u8,52u8,213u8,237u8,191u8,236u8,211u8,13u8,25u8,255u8, - 244u8,11u8,191u8,167u8,137u8,151u8,251u8,222u8,45u8,19u8,65u8,158u8,242u8,198u8,74u8,32u8,231u8,98u8,204u8,15u8, - 168u8,1u8,221u8,210u8,181u8,233u8,178u8,35u8,114u8,15u8,107u8,98u8,81u8,232u8,30u8,197u8,202u8,221u8,118u8,52u8, - 223u8,126u8,139u8,153u8,91u8,95u8,13u8,60u8,186u8,251u8,6u8,107u8,112u8,144u8,165u8,151u8,100u8,21u8,141u8,179u8, - 195u8,117u8,187u8,203u8,138u8,249u8,86u8,115u8,223u8,144u8,23u8,159u8,40u8,74u8,86u8,165u8,45u8,148u8,104u8,96u8, - 112u8,214u8,164u8,213u8,18u8,53u8,216u8,73u8,128u8,88u8,14u8,161u8,75u8,79u8,25u8,54u8,247u8,148u8,166u8,124u8, - 233u8,219u8,205u8,93u8,163u8,45u8,251u8,31u8,182u8,117u8,244u8,27u8,91u8,199u8,206u8,178u8,215u8,82u8,200u8,27u8, - 11u8,208u8,80u8,119u8,4u8,211u8,153u8,227u8,9u8,139u8,238u8,212u8,34u8,156u8,144u8,251u8,250u8,63u8,93u8,246u8, - 105u8,55u8,193u8,255u8,103u8,237u8,192u8,147u8,126u8,93u8,129u8,127u8,113u8,0u8,240u8,47u8,187u8,2u8,116u8,86u8, - 182u8,143u8,213u8,209u8,234u8,232u8,31u8,187u8,217u8,87u8,60u8,207u8,28u8,0u8,0u8,0u8,0u8,8u8,112u8,111u8, - 111u8,108u8,95u8,117u8,54u8,52u8,229u8,31u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,28u8, - 105u8,111u8,212u8,72u8,246u8,59u8,191u8,162u8,86u8,163u8,101u8,210u8,76u8,147u8,180u8,187u8,211u8,9u8,147u8,76u8, - 208u8,102u8,33u8,104u8,144u8,32u8,65u8,36u8,236u8,236u8,104u8,180u8,170u8,56u8,237u8,106u8,218u8,194u8,109u8,183u8, - 124u8,228u8,128u8,225u8,191u8,239u8,123u8,117u8,216u8,85u8,229u8,178u8,219u8,14u8,73u8,24u8,77u8,62u8,0u8,177u8, - 171u8,222u8,125u8,87u8,153u8,173u8,39u8,79u8,30u8,145u8,39u8,228u8,52u8,92u8,174u8,34u8,70u8,150u8,73u8,80u8, - 192u8,95u8,243u8,36u8,37u8,121u8,234u8,207u8,62u8,133u8,241u8,71u8,226u8,199u8,1u8,153u8,249u8,209u8,172u8,136u8, - 252u8,28u8,127u8,207u8,22u8,126u8,202u8,50u8,146u8,204u8,137u8,79u8,86u8,73u8,18u8,225u8,191u8,102u8,73u8,24u8, - 103u8,155u8,228u8,108u8,193u8,212u8,107u8,248u8,131u8,92u8,37u8,105u8,190u8,0u8,152u8,240u8,79u8,63u8,35u8,57u8, - 188u8,204u8,147u8,220u8,143u8,196u8,106u8,18u8,198u8,136u8,24u8,159u8,114u8,40u8,97u8,60u8,75u8,153u8,159u8,49u8, - 0u8,115u8,204u8,174u8,4u8,152u8,69u8,18u8,5u8,44u8,5u8,236u8,49u8,185u8,40u8,110u8,4u8,36u8,133u8,62u8, - 37u8,41u8,11u8,24u8,91u8,34u8,132u8,48u8,37u8,236u8,58u8,204u8,52u8,242u8,54u8,1u8,54u8,130u8,63u8,186u8, - 246u8,57u8,99u8,243u8,40u8,185u8,218u8,195u8,7u8,222u8,38u8,121u8,135u8,232u8,178u8,220u8,79u8,115u8,146u8,20u8, - 121u8,70u8,216u8,114u8,149u8,223u8,224u8,6u8,50u8,222u8,36u8,167u8,26u8,222u8,67u8,196u8,138u8,132u8,146u8,171u8, - 16u8,24u8,241u8,70u8,163u8,145u8,226u8,244u8,16u8,30u8,69u8,17u8,144u8,48u8,99u8,225u8,37u8,19u8,175u8,36u8, - 101u8,176u8,92u8,49u8,37u8,113u8,197u8,201u8,21u8,89u8,128u8,4u8,248u8,42u8,93u8,4u8,32u8,90u8,78u8,83u8, - 245u8,188u8,162u8,158u8,76u8,228u8,110u8,127u8,181u8,2u8,52u8,161u8,159u8,11u8,216u8,151u8,126u8,84u8,0u8,63u8, - 105u8,178u8,4u8,236u8,87u8,126u8,26u8,112u8,48u8,37u8,142u8,177u8,78u8,228u8,143u8,153u8,65u8,25u8,234u8,4u8, - 215u8,9u8,189u8,104u8,43u8,17u8,221u8,182u8,201u8,251u8,191u8,249u8,202u8,70u8,254u8,79u8,65u8,93u8,140u8,92u8, - 176u8,57u8,170u8,4u8,217u8,69u8,253u8,132u8,241u8,144u8,48u8,127u8,182u8,176u8,148u8,65u8,194u8,76u8,161u8,20u8, - 219u8,135u8,0u8,29u8,197u8,135u8,104u8,149u8,4u8,167u8,134u8,0u8,217u8,245u8,108u8,225u8,199u8,31u8,133u8,37u8, - 234u8,120u8,77u8,121u8,106u8,155u8,80u8,6u8,19u8,147u8,163u8,233u8,173u8,4u8,184u8,99u8,2u8,217u8,65u8,93u8, - 11u8,75u8,203u8,52u8,34u8,55u8,201u8,17u8,50u8,106u8,241u8,199u8,247u8,110u8,9u8,186u8,14u8,80u8,162u8,135u8, - 138u8,63u8,83u8,49u8,156u8,44u8,68u8,182u8,93u8,62u8,228u8,52u8,32u8,167u8,136u8,84u8,242u8,20u8,177u8,121u8, - 142u8,68u8,108u8,61u8,146u8,126u8,233u8,175u8,242u8,36u8,163u8,89u8,30u8,236u8,237u8,161u8,121u8,209u8,98u8,103u8, - 155u8,124u8,121u8,68u8,224u8,167u8,200u8,140u8,183u8,25u8,119u8,103u8,186u8,244u8,87u8,123u8,123u8,95u8,78u8,89u8, - 52u8,31u8,74u8,15u8,127u8,235u8,175u8,190u8,238u8,151u8,59u8,248u8,90u8,150u8,166u8,73u8,106u8,61u8,187u8,100u8, - 179u8,28u8,31u8,242u8,167u8,91u8,91u8,91u8,134u8,105u8,196u8,73u8,78u8,64u8,162u8,25u8,139u8,115u8,148u8,39u8, - 55u8,116u8,190u8,112u8,150u8,196u8,89u8,78u8,142u8,78u8,127u8,61u8,124u8,127u8,244u8,235u8,201u8,155u8,151u8,71u8, - 239u8,233u8,241u8,201u8,25u8,125u8,117u8,242u8,225u8,248u8,229u8,30u8,65u8,98u8,15u8,136u8,183u8,95u8,194u8,132u8, - 104u8,129u8,145u8,1u8,13u8,40u8,73u8,200u8,210u8,143u8,111u8,116u8,175u8,55u8,29u8,73u8,135u8,127u8,118u8,114u8, - 66u8,223u8,30u8,30u8,255u8,78u8,53u8,68u8,167u8,10u8,254u8,184u8,130u8,255u8,194u8,143u8,145u8,212u8,128u8,101u8, - 121u8,154u8,220u8,0u8,217u8,241u8,83u8,238u8,234u8,117u8,136u8,239u8,78u8,78u8,222u8,208u8,215u8,167u8,156u8,218u8, - 163u8,183u8,239u8,206u8,126u8,87u8,208u8,38u8,53u8,104u8,50u8,232u8,232u8,161u8,40u8,7u8,83u8,229u8,148u8,234u8, - 49u8,11u8,181u8,219u8,196u8,192u8,235u8,227u8,211u8,15u8,175u8,94u8,189u8,126u8,241u8,250u8,232u8,248u8,76u8,48u8, - 81u8,146u8,191u8,189u8,239u8,20u8,249u8,76u8,32u8,95u8,248u8,151u8,76u8,160u8,230u8,56u8,97u8,203u8,230u8,210u8, - 191u8,174u8,130u8,134u8,91u8,7u8,2u8,3u8,61u8,249u8,207u8,209u8,251u8,87u8,111u8,78u8,126u8,83u8,168u8,166u8, - 21u8,42u8,180u8,71u8,136u8,21u8,122u8,104u8,146u8,24u8,193u8,17u8,25u8,11u8,20u8,166u8,186u8,208u8,206u8,78u8, - 206u8,14u8,223u8,208u8,23u8,39u8,192u8,82u8,13u8,193u8,78u8,3u8,2u8,41u8,182u8,94u8,24u8,26u8,120u8,216u8, - 149u8,38u8,42u8,54u8,188u8,61u8,252u8,47u8,253u8,176u8,179u8,93u8,154u8,218u8,179u8,237u8,237u8,157u8,221u8,237u8, - 237u8,209u8,238u8,100u8,119u8,244u8,243u8,116u8,234u8,237u8,120u8,83u8,185u8,30u8,44u8,162u8,152u8,229u8,149u8,31u8, - 102u8,57u8,10u8,85u8,184u8,18u8,127u8,175u8,217u8,33u8,141u8,194u8,101u8,152u8,115u8,160u8,195u8,114u8,1u8,231u8, - 132u8,114u8,81u8,57u8,223u8,8u8,30u8,173u8,87u8,234u8,97u8,233u8,138u8,191u8,248u8,65u8,0u8,79u8,32u8,32u8, - 194u8,186u8,231u8,67u8,39u8,250u8,61u8,34u8,156u8,81u8,173u8,213u8,150u8,129u8,96u8,95u8,178u8,185u8,95u8,68u8, - 57u8,96u8,197u8,204u8,118u8,182u8,8u8,51u8,145u8,45u8,25u8,250u8,114u8,128u8,143u8,151u8,97u8,12u8,244u8,127u8, - 102u8,36u8,77u8,138u8,56u8,192u8,160u8,204u8,29u8,30u8,66u8,214u8,130u8,197u8,32u8,184u8,229u8,170u8,208u8,211u8, - 58u8,207u8,246u8,34u8,32u8,45u8,97u8,67u8,190u8,169u8,99u8,251u8,53u8,185u8,98u8,151u8,44u8,29u8,34u8,112u8, - 128u8,16u8,51u8,137u8,194u8,255u8,4u8,198u8,95u8,200u8,76u8,160u8,239u8,38u8,65u8,18u8,255u8,8u8,105u8,22u8, - 54u8,97u8,6u8,22u8,56u8,151u8,64u8,112u8,184u8,138u8,66u8,216u8,124u8,113u8,35u8,28u8,7u8,234u8,11u8,164u8, - 97u8,238u8,35u8,167u8,21u8,74u8,249u8,156u8,138u8,231u8,154u8,52u8,191u8,86u8,145u8,233u8,5u8,84u8,13u8,57u8, - 132u8,17u8,160u8,230u8,74u8,115u8,181u8,85u8,113u8,17u8,133u8,51u8,50u8,47u8,128u8,71u8,190u8,98u8,163u8,65u8, - 167u8,131u8,61u8,97u8,6u8,95u8,26u8,196u8,234u8,91u8,212u8,97u8,185u8,227u8,145u8,13u8,54u8,159u8,131u8,90u8, - 32u8,178u8,71u8,24u8,89u8,212u8,154u8,65u8,69u8,187u8,192u8,74u8,49u8,115u8,82u8,147u8,15u8,7u8,37u8,67u8, - 226u8,13u8,214u8,50u8,38u8,178u8,240u8,172u8,0u8,99u8,93u8,146u8,115u8,19u8,230u8,121u8,3u8,215u8,29u8,241u8, - 11u8,209u8,186u8,228u8,93u8,23u8,143u8,245u8,171u8,219u8,93u8,134u8,198u8,123u8,195u8,91u8,70u8,174u8,119u8,202u8, - 53u8,172u8,151u8,234u8,177u8,158u8,207u8,4u8,99u8,166u8,235u8,108u8,12u8,134u8,141u8,4u8,41u8,7u8,130u8,76u8, - 135u8,209u8,191u8,244u8,163u8,218u8,30u8,131u8,249u8,234u8,221u8,87u8,91u8,49u8,47u8,101u8,66u8,1u8,63u8,211u8, - 242u8,137u8,112u8,62u8,94u8,21u8,206u8,253u8,16u8,234u8,217u8,121u8,85u8,219u8,98u8,136u8,193u8,244u8,118u8,225u8, - 71u8,62u8,214u8,77u8,101u8,197u8,108u8,43u8,77u8,230u8,42u8,202u8,225u8,110u8,224u8,94u8,33u8,254u8,129u8,38u8, - 112u8,63u8,3u8,231u8,203u8,255u8,193u8,223u8,110u8,106u8,146u8,37u8,7u8,7u8,32u8,62u8,225u8,221u8,123u8,123u8, - 97u8,12u8,117u8,78u8,24u8,64u8,57u8,128u8,182u8,95u8,207u8,112u8,131u8,193u8,126u8,9u8,49u8,98u8,121u8,55u8, - 173u8,238u8,17u8,218u8,162u8,88u8,218u8,166u8,88u8,234u8,86u8,44u8,109u8,211u8,27u8,109u8,83u8,144u8,241u8,250u8, - 43u8,132u8,123u8,148u8,199u8,190u8,173u8,171u8,247u8,44u8,47u8,210u8,152u8,156u8,227u8,203u8,243u8,50u8,3u8,173u8, - 213u8,131u8,198u8,151u8,212u8,194u8,99u8,174u8,6u8,145u8,90u8,42u8,49u8,217u8,58u8,104u8,64u8,95u8,181u8,62u8, - 113u8,177u8,188u8,96u8,60u8,134u8,168u8,120u8,59u8,75u8,147u8,12u8,254u8,138u8,162u8,90u8,241u8,35u8,136u8,110u8, - 160u8,77u8,108u8,239u8,74u8,156u8,88u8,221u8,68u8,93u8,10u8,245u8,48u8,152u8,235u8,185u8,70u8,192u8,57u8,22u8, - 181u8,205u8,52u8,64u8,190u8,205u8,125u8,75u8,56u8,67u8,157u8,129u8,61u8,34u8,221u8,12u8,168u8,186u8,48u8,77u8, - 203u8,240u8,101u8,9u8,135u8,126u8,98u8,55u8,27u8,143u8,57u8,193u8,130u8,212u8,33u8,121u8,172u8,65u8,27u8,180u8, - 200u8,181u8,38u8,209u8,4u8,57u8,201u8,33u8,37u8,149u8,156u8,52u8,178u8,81u8,23u8,98u8,35u8,19u8,166u8,104u8, - 65u8,90u8,27u8,134u8,12u8,140u8,125u8,131u8,129u8,229u8,73u8,79u8,116u8,158u8,47u8,18u8,240u8,208u8,171u8,181u8, - 220u8,10u8,179u8,102u8,81u8,198u8,44u8,96u8,163u8,230u8,200u8,164u8,137u8,69u8,153u8,120u8,24u8,203u8,132u8,156u8, - 212u8,20u8,44u8,197u8,178u8,121u8,110u8,139u8,69u8,238u8,189u8,133u8,92u8,48u8,146u8,128u8,66u8,164u8,189u8,129u8, - 83u8,106u8,18u8,54u8,69u8,180u8,111u8,85u8,69u8,52u8,79u8,168u8,168u8,25u8,228u8,226u8,10u8,76u8,155u8,246u8, - 35u8,104u8,52u8,75u8,221u8,175u8,247u8,29u8,125u8,153u8,229u8,59u8,86u8,149u8,101u8,251u8,145u8,190u8,179u8,187u8, - 53u8,246u8,163u8,8u8,98u8,136u8,226u8,191u8,201u8,167u8,85u8,42u8,139u8,88u8,252u8,49u8,95u8,232u8,70u8,36u8, - 97u8,212u8,132u8,245u8,97u8,21u8,96u8,25u8,209u8,55u8,2u8,22u8,124u8,27u8,117u8,4u8,194u8,101u8,33u8,178u8, - 197u8,16u8,11u8,19u8,106u8,23u8,194u8,131u8,150u8,232u8,8u8,246u8,96u8,109u8,169u8,197u8,235u8,195u8,8u8,203u8, - 68u8,204u8,172u8,106u8,132u8,0u8,21u8,87u8,108u8,205u8,131u8,176u8,42u8,11u8,2u8,57u8,248u8,17u8,144u8,225u8, - 145u8,54u8,73u8,50u8,231u8,7u8,229u8,246u8,58u8,151u8,48u8,181u8,160u8,97u8,92u8,231u8,204u8,101u8,236u8,67u8, - 129u8,74u8,90u8,169u8,42u8,142u8,92u8,113u8,161u8,90u8,197u8,115u8,242u8,0u8,154u8,70u8,110u8,32u8,35u8,217u8, - 125u8,148u8,158u8,2u8,194u8,40u8,61u8,69u8,108u8,64u8,55u8,48u8,124u8,70u8,135u8,166u8,57u8,141u8,42u8,2u8, - 100u8,203u8,67u8,158u8,214u8,133u8,253u8,252u8,192u8,216u8,92u8,171u8,12u8,252u8,244u8,99u8,177u8,132u8,30u8,126u8, - 163u8,185u8,147u8,27u8,116u8,197u8,40u8,185u8,120u8,126u8,160u8,241u8,116u8,91u8,132u8,109u8,230u8,83u8,123u8,244u8, - 147u8,193u8,227u8,126u8,115u8,238u8,51u8,55u8,203u8,103u8,63u8,105u8,228u8,106u8,156u8,6u8,1u8,109u8,138u8,91u8, - 67u8,109u8,135u8,38u8,156u8,234u8,97u8,205u8,160u8,133u8,161u8,214u8,211u8,84u8,16u8,194u8,104u8,40u8,135u8,238u8, - 1u8,77u8,212u8,29u8,153u8,101u8,180u8,16u8,179u8,18u8,62u8,100u8,42u8,162u8,0u8,54u8,70u8,5u8,56u8,116u8, - 2u8,80u8,83u8,43u8,202u8,84u8,37u8,39u8,56u8,186u8,237u8,226u8,176u8,49u8,192u8,102u8,76u8,120u8,134u8,0u8, - 141u8,62u8,96u8,177u8,219u8,193u8,15u8,42u8,110u8,219u8,188u8,160u8,115u8,118u8,68u8,87u8,80u8,238u8,174u8,101u8, - 142u8,90u8,202u8,164u8,64u8,215u8,6u8,39u8,174u8,57u8,117u8,238u8,215u8,32u8,207u8,138u8,52u8,5u8,163u8,171u8, - 0u8,63u8,177u8,112u8,153u8,91u8,234u8,102u8,110u8,1u8,232u8,106u8,227u8,45u8,19u8,24u8,195u8,202u8,121u8,125u8, - 80u8,103u8,223u8,194u8,234u8,182u8,84u8,215u8,94u8,187u8,120u8,64u8,101u8,104u8,129u8,230u8,57u8,198u8,163u8,47u8, - 78u8,150u8,141u8,135u8,221u8,146u8,13u8,249u8,165u8,158u8,29u8,93u8,13u8,32u8,254u8,184u8,27u8,19u8,231u8,48u8, - 207u8,234u8,204u8,108u8,113u8,41u8,186u8,86u8,69u8,182u8,160u8,23u8,112u8,76u8,97u8,91u8,133u8,164u8,164u8,169u8, - 226u8,176u8,171u8,80u8,48u8,108u8,135u8,93u8,173u8,247u8,122u8,135u8,231u8,55u8,86u8,109u8,174u8,117u8,238u8,228u8, - 103u8,198u8,2u8,72u8,108u8,198u8,57u8,71u8,53u8,35u8,23u8,33u8,130u8,135u8,15u8,119u8,10u8,23u8,251u8,122u8, - 250u8,118u8,85u8,141u8,137u8,237u8,110u8,15u8,87u8,38u8,211u8,226u8,229u8,29u8,93u8,163u8,28u8,16u8,187u8,114u8, - 77u8,99u8,245u8,136u8,94u8,104u8,83u8,218u8,130u8,208u8,49u8,112u8,53u8,124u8,16u8,125u8,196u8,6u8,215u8,158u8, - 186u8,197u8,26u8,22u8,148u8,153u8,169u8,161u8,138u8,181u8,161u8,14u8,246u8,123u8,165u8,183u8,167u8,22u8,162u8,190u8, - 9u8,238u8,105u8,141u8,128u8,10u8,66u8,192u8,2u8,152u8,129u8,182u8,100u8,58u8,7u8,237u8,229u8,102u8,147u8,44u8, - 219u8,144u8,207u8,82u8,63u8,206u8,230u8,172u8,180u8,86u8,126u8,216u8,162u8,219u8,53u8,245u8,184u8,101u8,27u8,143u8, - 198u8,142u8,86u8,87u8,194u8,81u8,68u8,26u8,220u8,235u8,198u8,236u8,154u8,156u8,82u8,175u8,50u8,107u8,231u8,251u8, - 113u8,211u8,123u8,206u8,179u8,194u8,173u8,77u8,29u8,7u8,189u8,60u8,128u8,122u8,247u8,234u8,3u8,0u8,222u8,244u8, - 2u8,69u8,112u8,127u8,63u8,112u8,187u8,129u8,130u8,103u8,56u8,130u8,102u8,2u8,107u8,236u8,135u8,122u8,67u8,7u8, - 113u8,131u8,110u8,101u8,22u8,29u8,183u8,108u8,214u8,71u8,113u8,170u8,136u8,18u8,196u8,152u8,177u8,19u8,106u8,160u8, - 90u8,213u8,85u8,197u8,77u8,28u8,116u8,167u8,174u8,198u8,45u8,101u8,75u8,80u8,168u8,113u8,148u8,44u8,43u8,165u8, - 58u8,199u8,93u8,138u8,165u8,178u8,143u8,253u8,235u8,134u8,210u8,138u8,198u8,111u8,8u8,162u8,247u8,83u8,198u8,57u8, - 74u8,163u8,218u8,163u8,167u8,26u8,3u8,26u8,65u8,188u8,51u8,95u8,194u8,217u8,67u8,237u8,112u8,14u8,79u8,61u8, - 128u8,92u8,156u8,221u8,139u8,154u8,249u8,70u8,28u8,173u8,225u8,32u8,223u8,56u8,122u8,53u8,34u8,190u8,52u8,139u8, - 46u8,149u8,36u8,122u8,82u8,125u8,195u8,65u8,189u8,246u8,66u8,200u8,27u8,212u8,180u8,253u8,48u8,14u8,216u8,245u8, - 0u8,224u8,171u8,58u8,135u8,63u8,160u8,201u8,220u8,81u8,129u8,181u8,150u8,191u8,106u8,127u8,202u8,165u8,208u8,165u8, - 72u8,146u8,184u8,155u8,75u8,37u8,55u8,168u8,102u8,50u8,190u8,26u8,57u8,195u8,20u8,72u8,167u8,49u8,74u8,213u8, - 188u8,147u8,115u8,189u8,213u8,59u8,47u8,111u8,133u8,56u8,186u8,165u8,115u8,181u8,6u8,79u8,168u8,248u8,112u8,224u8, - 34u8,132u8,115u8,175u8,56u8,41u8,62u8,46u8,248u8,244u8,224u8,50u8,9u8,131u8,234u8,64u8,76u8,96u8,171u8,101u8, - 31u8,103u8,71u8,94u8,206u8,195u8,214u8,15u8,3u8,236u8,253u8,226u8,44u8,198u8,158u8,168u8,12u8,173u8,46u8,221u8, - 174u8,1u8,6u8,119u8,45u8,38u8,113u8,140u8,228u8,171u8,131u8,36u8,253u8,208u8,87u8,23u8,196u8,253u8,201u8,209u8, - 45u8,135u8,102u8,193u8,14u8,107u8,167u8,173u8,53u8,81u8,3u8,173u8,199u8,165u8,239u8,222u8,128u8,79u8,101u8,137u8, - 58u8,129u8,44u8,47u8,103u8,240u8,32u8,224u8,47u8,235u8,173u8,248u8,166u8,225u8,182u8,206u8,99u8,21u8,242u8,231u8, - 159u8,174u8,138u8,203u8,225u8,208u8,64u8,201u8,91u8,113u8,190u8,121u8,131u8,167u8,155u8,214u8,217u8,97u8,219u8,121u8, - 108u8,80u8,164u8,248u8,91u8,24u8,231u8,44u8,141u8,81u8,37u8,234u8,206u8,21u8,156u8,109u8,243u8,42u8,95u8,104u8, - 49u8,219u8,146u8,183u8,81u8,54u8,109u8,188u8,229u8,233u8,111u8,121u8,220u8,122u8,1u8,94u8,106u8,81u8,0u8,75u8, - 216u8,245u8,10u8,2u8,131u8,56u8,186u8,133u8,131u8,226u8,217u8,34u8,201u8,240u8,36u8,24u8,88u8,154u8,23u8,17u8, - 144u8,157u8,225u8,44u8,173u8,28u8,55u8,224u8,25u8,189u8,2u8,104u8,162u8,52u8,6u8,93u8,79u8,100u8,56u8,48u8, - 206u8,104u8,218u8,91u8,34u8,117u8,199u8,33u8,35u8,171u8,52u8,132u8,65u8,197u8,129u8,174u8,103u8,178u8,37u8,45u8, - 179u8,126u8,177u8,203u8,4u8,129u8,151u8,197u8,106u8,105u8,94u8,244u8,232u8,146u8,180u8,45u8,85u8,74u8,40u8,52u8, - 218u8,187u8,39u8,181u8,148u8,37u8,241u8,82u8,251u8,248u8,91u8,162u8,251u8,13u8,20u8,199u8,252u8,52u8,229u8,211u8, - 69u8,126u8,220u8,13u8,106u8,226u8,197u8,68u8,144u8,148u8,39u8,219u8,51u8,174u8,53u8,50u8,15u8,211u8,44u8,119u8, - 248u8,137u8,80u8,184u8,9u8,90u8,110u8,189u8,161u8,0u8,51u8,166u8,65u8,120u8,25u8,6u8,108u8,93u8,124u8,80u8, - 177u8,183u8,22u8,45u8,214u8,29u8,14u8,84u8,242u8,18u8,178u8,22u8,101u8,83u8,118u8,174u8,221u8,218u8,115u8,69u8, - 85u8,181u8,234u8,155u8,162u8,129u8,179u8,81u8,50u8,79u8,25u8,26u8,156u8,220u8,222u8,217u8,20u8,79u8,149u8,88u8, - 110u8,23u8,73u8,215u8,75u8,164u8,79u8,0u8,189u8,23u8,145u8,173u8,9u8,160u8,250u8,221u8,147u8,158u8,161u8,83u8, - 205u8,18u8,84u8,12u8,181u8,47u8,115u8,70u8,80u8,210u8,222u8,105u8,184u8,28u8,61u8,68u8,136u8,144u8,32u8,132u8, - 26u8,85u8,203u8,84u8,93u8,179u8,83u8,160u8,203u8,231u8,183u8,193u8,241u8,29u8,226u8,130u8,195u8,253u8,29u8,225u8, - 161u8,33u8,34u8,104u8,230u8,229u8,68u8,65u8,13u8,147u8,186u8,150u8,214u8,116u8,35u8,255u8,254u8,236u8,182u8,37u8, - 81u8,39u8,103u8,120u8,115u8,230u8,128u8,108u8,128u8,189u8,22u8,222u8,248u8,217u8,6u8,212u8,178u8,40u8,83u8,241u8, - 203u8,13u8,140u8,127u8,183u8,202u8,223u8,62u8,107u8,165u8,226u8,134u8,220u8,233u8,103u8,28u8,182u8,78u8,172u8,56u8, - 246u8,22u8,91u8,192u8,71u8,74u8,228u8,240u8,187u8,134u8,29u8,95u8,241u8,221u8,240u8,216u8,216u8,254u8,195u8,31u8, - 57u8,203u8,114u8,154u8,196u8,209u8,205u8,255u8,154u8,110u8,92u8,32u8,187u8,13u8,23u8,46u8,254u8,166u8,215u8,35u8, - 132u8,84u8,12u8,129u8,64u8,159u8,151u8,222u8,8u8,105u8,163u8,192u8,196u8,233u8,24u8,5u8,35u8,150u8,19u8,160u8, - 13u8,91u8,44u8,252u8,188u8,237u8,64u8,221u8,176u8,154u8,14u8,204u8,238u8,235u8,84u8,63u8,156u8,240u8,184u8,51u8, - 140u8,85u8,45u8,202u8,125u8,160u8,50u8,117u8,121u8,16u8,87u8,246u8,21u8,67u8,242u8,47u8,152u8,31u8,224u8,125u8, - 88u8,205u8,66u8,28u8,139u8,96u8,78u8,48u8,54u8,23u8,25u8,13u8,175u8,121u8,176u8,202u8,219u8,167u8,1u8,198u8, - 33u8,216u8,230u8,218u8,163u8,135u8,212u8,106u8,49u8,94u8,43u8,198u8,11u8,90u8,77u8,235u8,101u8,119u8,96u8,111u8, - 24u8,55u8,119u8,225u8,143u8,21u8,139u8,124u8,185u8,199u8,151u8,79u8,214u8,47u8,31u8,11u8,218u8,249u8,242u8,109u8, - 199u8,114u8,117u8,110u8,239u8,2u8,63u8,237u8,176u8,94u8,135u8,191u8,99u8,233u8,242u8,157u8,113u8,71u8,191u8,188u8, - 71u8,93u8,105u8,208u8,113u8,96u8,172u8,41u8,106u8,218u8,162u8,36u8,183u8,60u8,118u8,251u8,201u8,227u8,153u8,117u8, - 159u8,73u8,21u8,185u8,84u8,50u8,73u8,61u8,34u8,32u8,227u8,197u8,112u8,113u8,77u8,27u8,213u8,212u8,85u8,136u8, - 117u8,112u8,67u8,242u8,243u8,58u8,148u8,99u8,34u8,168u8,235u8,139u8,114u8,236u8,70u8,57u8,70u8,135u8,104u8,118u8, - 49u8,50u8,41u8,175u8,235u8,67u8,15u8,33u8,206u8,195u8,17u8,237u8,83u8,84u8,134u8,118u8,129u8,80u8,191u8,78u8, - 95u8,93u8,193u8,230u8,119u8,231u8,27u8,147u8,156u8,193u8,94u8,89u8,99u8,75u8,113u8,78u8,4u8,111u8,83u8,131u8, - 55u8,135u8,175u8,78u8,106u8,14u8,221u8,160u8,215u8,137u8,201u8,191u8,202u8,115u8,158u8,215u8,193u8,134u8,39u8,154u8, - 9u8,121u8,227u8,118u8,35u8,230u8,55u8,169u8,251u8,90u8,242u8,51u8,193u8,194u8,218u8,56u8,39u8,34u8,166u8,41u8, - 66u8,63u8,210u8,166u8,245u8,92u8,108u8,63u8,217u8,108u8,118u8,14u8,50u8,21u8,44u8,96u8,116u8,210u8,100u8,138u8, - 156u8,53u8,186u8,130u8,201u8,14u8,168u8,167u8,194u8,61u8,229u8,138u8,123u8,38u8,20u8,87u8,65u8,170u8,35u8,55u8, - 207u8,144u8,172u8,240u8,12u8,80u8,76u8,77u8,185u8,176u8,1u8,117u8,174u8,88u8,117u8,39u8,128u8,167u8,189u8,0u8, - 171u8,92u8,177u8,22u8,50u8,126u8,13u8,3u8,208u8,237u8,16u8,248u8,178u8,96u8,226u8,66u8,48u8,116u8,195u8,208u8, - 37u8,47u8,241u8,206u8,158u8,89u8,187u8,241u8,187u8,193u8,198u8,64u8,13u8,156u8,18u8,210u8,113u8,225u8,99u8,95u8, - 205u8,63u8,89u8,17u8,70u8,135u8,86u8,101u8,154u8,134u8,62u8,132u8,155u8,40u8,151u8,39u8,235u8,9u8,221u8,65u8, - 207u8,3u8,51u8,242u8,186u8,186u8,134u8,19u8,17u8,112u8,187u8,219u8,57u8,31u8,54u8,65u8,120u8,214u8,55u8,11u8, - 99u8,134u8,183u8,131u8,104u8,53u8,201u8,165u8,147u8,234u8,254u8,88u8,197u8,66u8,31u8,133u8,79u8,134u8,6u8,184u8, - 54u8,226u8,199u8,182u8,79u8,31u8,39u8,48u8,3u8,1u8,189u8,154u8,131u8,224u8,78u8,108u8,97u8,238u8,215u8,67u8, - 85u8,253u8,82u8,239u8,160u8,177u8,16u8,251u8,225u8,143u8,82u8,225u8,120u8,139u8,184u8,72u8,217u8,134u8,15u8,99u8, - 243u8,28u8,144u8,4u8,104u8,14u8,222u8,207u8,59u8,59u8,30u8,200u8,45u8,74u8,100u8,47u8,113u8,64u8,240u8,243u8, - 158u8,65u8,91u8,25u8,103u8,224u8,7u8,105u8,224u8,88u8,135u8,67u8,167u8,225u8,156u8,194u8,120u8,71u8,18u8,214u8, - 94u8,219u8,233u8,12u8,181u8,7u8,72u8,207u8,8u8,241u8,125u8,152u8,239u8,88u8,133u8,210u8,8u8,14u8,37u8,24u8, - 21u8,205u8,114u8,182u8,134u8,238u8,177u8,101u8,95u8,11u8,63u8,154u8,195u8,40u8,251u8,154u8,138u8,111u8,68u8,212u8, - 53u8,143u8,45u8,245u8,225u8,144u8,225u8,143u8,25u8,175u8,26u8,220u8,181u8,169u8,14u8,168u8,49u8,165u8,225u8,254u8, - 3u8,99u8,169u8,89u8,120u8,106u8,152u8,198u8,110u8,76u8,99u8,19u8,19u8,122u8,122u8,51u8,182u8,177u8,141u8,13u8, - 151u8,247u8,42u8,93u8,165u8,60u8,220u8,213u8,171u8,59u8,28u8,148u8,91u8,38u8,125u8,195u8,188u8,146u8,209u8,160u8, - 46u8,164u8,237u8,190u8,145u8,93u8,73u8,96u8,224u8,22u8,193u8,244u8,33u8,77u8,210u8,249u8,209u8,133u8,109u8,166u8, - 230u8,107u8,81u8,77u8,237u8,55u8,27u8,178u8,19u8,230u8,216u8,254u8,100u8,195u8,50u8,45u8,243u8,42u8,162u8,102u8, - 236u8,53u8,92u8,101u8,101u8,224u8,54u8,246u8,53u8,183u8,16u8,27u8,140u8,201u8,26u8,16u8,91u8,31u8,88u8,244u8, - 232u8,192u8,204u8,9u8,168u8,119u8,59u8,59u8,115u8,64u8,26u8,223u8,135u8,85u8,124u8,102u8,105u8,57u8,219u8,236u8, - 30u8,155u8,26u8,27u8,96u8,87u8,205u8,235u8,12u8,19u8,35u8,149u8,124u8,70u8,221u8,53u8,84u8,235u8,191u8,134u8, - 205u8,225u8,165u8,37u8,139u8,187u8,228u8,88u8,14u8,84u8,110u8,41u8,70u8,97u8,239u8,88u8,100u8,81u8,93u8,105u8, - 61u8,68u8,106u8,181u8,71u8,158u8,245u8,53u8,51u8,243u8,118u8,245u8,139u8,83u8,237u8,83u8,8u8,243u8,167u8,25u8, - 201u8,216u8,68u8,98u8,212u8,138u8,246u8,137u8,66u8,187u8,58u8,61u8,37u8,92u8,11u8,155u8,227u8,91u8,205u8,170u8, - 163u8,129u8,19u8,167u8,241u8,232u8,159u8,155u8,22u8,73u8,34u8,229u8,227u8,221u8,207u8,140u8,49u8,254u8,33u8,146u8, - 168u8,73u8,181u8,143u8,4u8,4u8,0u8,156u8,158u8,133u8,57u8,249u8,200u8,242u8,76u8,71u8,201u8,139u8,92u8,134u8, - 147u8,204u8,43u8,171u8,106u8,53u8,175u8,47u8,213u8,157u8,183u8,107u8,205u8,160u8,195u8,145u8,53u8,173u8,201u8,245u8, - 219u8,48u8,46u8,176u8,122u8,14u8,68u8,13u8,110u8,150u8,221u8,155u8,61u8,38u8,33u8,230u8,79u8,89u8,64u8,63u8, - 109u8,202u8,147u8,238u8,222u8,252u8,174u8,109u8,30u8,239u8,194u8,240u8,184u8,33u8,211u8,152u8,44u8,210u8,176u8,56u8, - 43u8,175u8,201u8,8u8,117u8,222u8,101u8,153u8,230u8,10u8,20u8,218u8,173u8,28u8,203u8,13u8,90u8,227u8,203u8,186u8, - 130u8,216u8,187u8,79u8,105u8,137u8,107u8,60u8,25u8,45u8,143u8,242u8,68u8,30u8,166u8,201u8,92u8,177u8,114u8,95u8, - 66u8,107u8,20u8,150u8,215u8,117u8,216u8,213u8,38u8,111u8,83u8,212u8,119u8,34u8,54u8,41u8,49u8,243u8,208u8,243u8, - 158u8,45u8,171u8,155u8,144u8,26u8,87u8,181u8,205u8,81u8,245u8,149u8,98u8,244u8,122u8,127u8,50u8,43u8,111u8,240u8, - 118u8,18u8,215u8,248u8,187u8,136u8,107u8,220u8,83u8,92u8,94u8,139u8,55u8,91u8,144u8,213u8,220u8,252u8,150u8,158u8, - 220u8,169u8,177u8,29u8,245u8,107u8,108u8,53u8,229u8,200u8,128u8,201u8,226u8,121u8,146u8,194u8,168u8,84u8,239u8,242u8, - 249u8,209u8,76u8,15u8,109u8,221u8,169u8,2u8,220u8,171u8,38u8,247u8,144u8,62u8,76u8,65u8,192u8,89u8,231u8,39u8, - 234u8,207u8,115u8,224u8,63u8,197u8,91u8,139u8,150u8,175u8,103u8,244u8,130u8,69u8,201u8,85u8,39u8,217u8,76u8,238u8, - 73u8,54u8,230u8,109u8,202u8,91u8,9u8,113u8,210u8,117u8,150u8,124u8,251u8,176u8,176u8,206u8,110u8,119u8,166u8,211u8, - 201u8,110u8,63u8,179u8,53u8,122u8,21u8,200u8,241u8,177u8,8u8,199u8,229u8,215u8,30u8,189u8,195u8,113u8,55u8,173u8, - 180u8,182u8,207u8,222u8,189u8,73u8,103u8,123u8,244u8,45u8,210u8,129u8,88u8,89u8,204u8,231u8,225u8,44u8,172u8,190u8, - 133u8,121u8,64u8,193u8,220u8,117u8,153u8,167u8,48u8,241u8,174u8,166u8,103u8,145u8,98u8,117u8,54u8,188u8,125u8,177u8, - 255u8,207u8,142u8,111u8,234u8,100u8,26u8,218u8,144u8,182u8,45u8,157u8,186u8,111u8,103u8,166u8,185u8,43u8,81u8,106u8, - 101u8,224u8,183u8,55u8,219u8,125u8,57u8,91u8,95u8,17u8,215u8,142u8,54u8,219u8,134u8,113u8,246u8,212u8,195u8,107u8, - 155u8,194u8,213u8,90u8,121u8,207u8,12u8,135u8,223u8,55u8,196u8,25u8,177u8,253u8,225u8,66u8,92u8,107u8,74u8,249u8, - 235u8,132u8,56u8,83u8,58u8,15u8,17u8,226u8,154u8,5u8,115u8,215u8,33u8,206u8,228u8,77u8,92u8,23u8,55u8,202u8, - 49u8,62u8,215u8,137u8,111u8,225u8,180u8,247u8,86u8,24u8,119u8,188u8,36u8,210u8,42u8,195u8,219u8,76u8,205u8,238u8, - 180u8,39u8,182u8,63u8,149u8,186u8,11u8,185u8,142u8,31u8,68u8,174u8,54u8,229u8,22u8,17u8,182u8,235u8,174u8,139u8, - 175u8,163u8,174u8,247u8,70u8,238u8,188u8,85u8,180u8,175u8,185u8,139u8,243u8,54u8,14u8,175u8,187u8,63u8,139u8,235u8, - 152u8,198u8,127u8,148u8,134u8,233u8,93u8,27u8,247u8,213u8,167u8,93u8,181u8,15u8,21u8,30u8,107u8,157u8,101u8,117u8, - 29u8,162u8,126u8,196u8,105u8,226u8,225u8,183u8,197u8,147u8,37u8,115u8,35u8,187u8,221u8,52u8,169u8,27u8,101u8,94u8, - 19u8,101u8,98u8,34u8,104u8,17u8,102u8,95u8,78u8,105u8,167u8,172u8,99u8,111u8,253u8,45u8,228u8,223u8,105u8,0u8, - 173u8,93u8,246u8,125u8,48u8,51u8,170u8,221u8,204u8,182u8,185u8,253u8,62u8,54u8,212u8,133u8,172u8,191u8,170u8,1u8, - 117u8,161u8,189u8,155u8,245u8,124u8,125u8,244u8,127u8,40u8,110u8,199u8,192u8,170u8,87u8,0u8,0u8,0u8,0u8,16u8, - 112u8,111u8,111u8,108u8,95u8,117u8,54u8,52u8,95u8,117u8,110u8,98u8,111u8,117u8,110u8,100u8,128u8,21u8,31u8,139u8, - 8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,205u8,26u8,217u8,82u8,219u8,200u8,246u8,157u8,175u8,232u8,251u8,50u8, - 99u8,51u8,198u8,27u8,198u8,16u8,115u8,73u8,21u8,55u8,33u8,149u8,84u8,17u8,156u8,10u8,206u8,205u8,204u8,147u8, - 44u8,91u8,109u8,172u8,26u8,89u8,114u8,105u8,1u8,156u8,12u8,255u8,62u8,231u8,244u8,34u8,245u8,38u8,99u8,103u8, - 153u8,26u8,138u8,2u8,187u8,213u8,103u8,223u8,187u8,213u8,57u8,60u8,60u8,32u8,135u8,228u8,54u8,92u8,173u8,35u8, - 74u8,86u8,73u8,80u8,192u8,191u8,69u8,146u8,146u8,60u8,245u8,231u8,127u8,134u8,241u8,29u8,241u8,227u8,128u8,204u8, - 253u8,104u8,94u8,68u8,126u8,142u8,223u8,179u8,165u8,159u8,210u8,140u8,36u8,11u8,226u8,147u8,117u8,146u8,68u8,248u8, - 105u8,158u8,132u8,113u8,214u8,38u8,147u8,37u8,149u8,143u8,225u8,15u8,121u8,72u8,210u8,124u8,9u8,56u8,225u8,163u8, - 159u8,145u8,28u8,30u8,230u8,73u8,238u8,71u8,124u8,55u8,9u8,99u8,36u8,140u8,171u8,12u8,75u8,24u8,207u8,83u8, - 234u8,103u8,20u8,208u8,220u8,208u8,7u8,142u8,102u8,153u8,68u8,1u8,77u8,129u8,122u8,76u8,102u8,197u8,134u8,99u8, - 146u8,228u8,83u8,146u8,210u8,128u8,210u8,21u8,98u8,8u8,83u8,66u8,31u8,195u8,76u8,97u8,175u8,13u8,184u8,17u8, - 253u8,213u8,163u8,207u8,4u8,91u8,68u8,201u8,195u8,8u8,23u8,122u8,109u8,242u8,1u8,201u8,101u8,185u8,159u8,230u8, - 36u8,41u8,242u8,140u8,208u8,213u8,58u8,223u8,32u8,0u8,233u8,183u8,201u8,173u8,66u8,247u8,18u8,169u8,34u8,163u8, - 228u8,33u8,4u8,65u8,122u8,221u8,110u8,87u8,74u8,122u8,9u8,75u8,81u8,4u8,44u8,204u8,105u8,120u8,79u8,249u8, - 35u8,193u8,25u8,108u8,151u8,66u8,9u8,90u8,113u8,242u8,64u8,150u8,160u8,1u8,182u8,75u8,85u8,1u8,168u8,150u8, - 241u8,84u8,173u8,87u8,220u8,147u8,99u8,1u8,237u8,175u8,215u8,64u8,38u8,244u8,115u8,142u8,251u8,222u8,143u8,10u8, - 144u8,39u8,77u8,86u8,64u8,253u8,193u8,79u8,3u8,134u8,166u8,164u8,209u8,87u8,153u8,252u8,53u8,211u8,56u8,67u8, - 155u8,224u8,62u8,110u8,23u8,101u8,39u8,146u8,27u8,232u8,178u8,255u8,143u8,237u8,172u8,149u8,255u8,22u8,204u8,69u8, - 201u8,140u8,46u8,208u8,36u8,40u8,46u8,218u8,39u8,140u8,91u8,132u8,250u8,243u8,165u8,97u8,12u8,18u8,102u8,146u8, - 36u8,7u8,111u8,1u8,118u8,84u8,31u8,146u8,149u8,26u8,60u8,209u8,20u8,72u8,31u8,231u8,75u8,63u8,190u8,227u8, - 158u8,168u8,210u8,213u8,245u8,169u8,0u8,161u8,14u8,142u8,117u8,137u8,78u8,190u8,73u8,129u8,67u8,29u8,201u8,16u8, - 109u8,205u8,61u8,45u8,83u8,152u8,108u8,147u8,43u8,20u8,212u8,144u8,143u8,193u8,118u8,56u8,95u8,23u8,168u8,209u8, - 75u8,41u8,159u8,110u8,24u8,198u8,22u8,18u8,27u8,148u8,139u8,140u8,7u8,148u8,20u8,137u8,10u8,153u8,34u8,186u8, - 200u8,145u8,137u8,206u8,129u8,136u8,75u8,127u8,157u8,39u8,153u8,151u8,229u8,193u8,104u8,132u8,238u8,229u8,21u8,195u8, - 129u8,87u8,196u8,179u8,164u8,0u8,216u8,175u8,7u8,4u8,126u8,138u8,76u8,219u8,149u8,251u8,179u8,136u8,122u8,104u8, - 61u8,47u8,162u8,241u8,93u8,190u8,28u8,141u8,190u8,222u8,210u8,104u8,193u8,226u8,17u8,159u8,181u8,200u8,4u8,255u8, - 125u8,134u8,29u8,215u8,108u8,3u8,62u8,97u8,75u8,79u8,231u8,37u8,66u8,134u8,138u8,166u8,105u8,146u8,158u8,31u8, - 176u8,197u8,78u8,167u8,163u8,57u8,75u8,156u8,228u8,4u8,116u8,156u8,209u8,56u8,71u8,13u8,51u8,215u8,103u8,27u8, - 231u8,73u8,156u8,229u8,228u8,234u8,246u8,237u8,229u8,199u8,171u8,183u8,227u8,235u8,215u8,87u8,31u8,189u8,155u8,241u8, - 196u8,123u8,51u8,254u8,116u8,243u8,122u8,68u8,128u8,125u8,80u8,83u8,239u8,188u8,196u8,9u8,249u8,3u8,115u8,5u8, - 186u8,84u8,146u8,144u8,149u8,31u8,111u8,212u8,60u8,160u8,135u8,150u8,138u8,127u8,50u8,30u8,123u8,239u8,47u8,111u8, - 254u8,240u8,20u8,66u8,183u8,18u8,127u8,191u8,194u8,255u8,202u8,143u8,145u8,213u8,128u8,102u8,121u8,154u8,108u8,128u8, - 237u8,248u8,136u8,5u8,191u8,141u8,241u8,195u8,120u8,124u8,237u8,189u8,187u8,101u8,220u8,94u8,189u8,255u8,48u8,249u8, - 67u8,98u8,59u8,182u8,176u8,137u8,52u8,164u8,38u8,167u8,28u8,156u8,151u8,113u8,170u8,102u8,49u8,180u8,119u8,157u8, - 0u8,239u8,110u8,110u8,63u8,189u8,121u8,243u8,238u8,213u8,187u8,171u8,155u8,9u8,23u8,162u8,100u8,127u8,112u8,238u8, - 84u8,249u8,156u8,19u8,95u8,250u8,247u8,148u8,147u8,102u8,52u8,1u8,164u8,189u8,242u8,31u8,171u8,52u8,226u8,182u8, - 1u8,167u8,224u8,141u8,255u8,127u8,245u8,241u8,205u8,245u8,248u8,179u8,36u8,117u8,82u8,145u8,66u8,15u8,133u8,236u8, - 161u8,38u8,43u8,65u8,17u8,66u8,147u8,210u8,64u8,82u8,178u8,149u8,54u8,25u8,79u8,46u8,175u8,189u8,87u8,99u8, - 16u8,201u8,34u8,48u8,172u8,33u8,32u8,212u8,182u8,23u8,133u8,26u8,25u8,78u8,133u8,139u8,114u8,128u8,247u8,151u8, - 191u8,123u8,159u8,134u8,131u8,210u8,213u8,206u8,6u8,131u8,225u8,233u8,96u8,208u8,61u8,61u8,62u8,237u8,190u8,56u8, - 57u8,233u8,13u8,123u8,39u8,246u8,254u8,94u8,255u8,12u8,0u8,224u8,47u8,154u8,123u8,208u8,237u8,159u8,245u8,143u8, - 135u8,195u8,23u8,253u8,238u8,139u8,227u8,179u8,193u8,240u8,24u8,126u8,143u8,79u8,7u8,195u8,238u8,233u8,224u8,184u8, - 119u8,58u8,60u8,235u8,247u8,122u8,131u8,19u8,137u8,4u8,220u8,170u8,152u8,231u8,85u8,120u8,103u8,57u8,90u8,134u8, - 71u8,38u8,254u8,48u8,105u8,61u8,166u8,78u8,198u8,82u8,203u8,120u8,194u8,245u8,192u8,137u8,87u8,207u8,228u8,42u8, - 11u8,203u8,255u8,250u8,65u8,0u8,223u8,32u8,135u8,226u8,166u8,151u8,213u8,46u8,208u8,233u8,107u8,186u8,240u8,139u8, - 40u8,7u8,100u8,88u8,230u8,38u8,203u8,48u8,227u8,165u8,147u8,98u8,20u8,7u8,184u8,188u8,10u8,227u8,112u8,21u8, - 126u8,161u8,36u8,197u8,156u8,129u8,25u8,154u8,133u8,53u8,228u8,175u8,37u8,141u8,65u8,7u8,171u8,117u8,161u8,214u8, - 120u8,86u8,250u8,121u8,118u8,90u8,1u8,64u8,222u8,86u8,169u8,189u8,77u8,30u8,232u8,61u8,77u8,91u8,136u8,28u8, - 48u8,196u8,84u8,144u8,240u8,255u8,4u8,191u8,47u8,68u8,89u8,80u8,161u8,73u8,144u8,196u8,191u8,66u8,205u8,5u8, - 32u8,44u8,199u8,156u8,230u8,10u8,24u8,14u8,215u8,81u8,8u8,192u8,179u8,13u8,143u8,25u8,104u8,54u8,144u8,135u8, - 133u8,63u8,7u8,245u8,85u8,36u8,197u8,186u8,199u8,215u8,21u8,253u8,61u8,85u8,73u8,233u8,21u8,180u8,16u8,57u8, - 100u8,16u8,224u8,230u8,65u8,137u8,178u8,117u8,49u8,139u8,194u8,57u8,89u8,20u8,32u8,35u8,219u8,209u8,104u8,142u8, - 184u8,149u8,190u8,214u8,232u8,207u8,55u8,216u8,192u8,38u8,167u8,71u8,26u8,116u8,177u8,160u8,243u8,28u8,242u8,121u8, - 132u8,217u8,67u8,238u8,105u8,86u8,76u8,114u8,244u8,60u8,227u8,234u8,12u8,55u8,122u8,205u8,103u8,185u8,229u8,117u8, - 118u8,94u8,128u8,223u8,172u8,200u8,84u8,135u8,159u8,214u8,136u8,226u8,164u8,229u8,208u8,149u8,45u8,177u8,241u8,213u8, - 114u8,208u8,110u8,203u8,241u8,76u8,58u8,163u8,241u8,80u8,46u8,179u8,162u8,50u8,26u8,129u8,64u8,134u8,163u8,54u8, - 154u8,6u8,128u8,198u8,97u8,245u8,236u8,201u8,84u8,210u8,107u8,145u8,172u8,193u8,145u8,149u8,92u8,205u8,189u8,155u8, - 245u8,96u8,11u8,63u8,132u8,238u8,113u8,81u8,117u8,146u8,24u8,121u8,88u8,58u8,102u8,126u8,228u8,99u8,151u8,82u8, - 246u8,167u8,166u8,2u8,69u8,29u8,240u8,24u8,222u8,6u8,194u8,114u8,29u8,53u8,21u8,173u8,248u8,25u8,120u8,119u8, - 254u8,31u8,246u8,180u8,173u8,168u8,135u8,92u8,92u8,128u8,14u8,120u8,248u8,140u8,70u8,97u8,12u8,93u8,69u8,24u8, - 64u8,209u8,69u8,231u8,178u8,171u8,71u8,179u8,121u8,94u8,98u8,140u8,104u8,254u8,172u8,234u8,189u8,109u8,170u8,247u8, - 92u8,170u8,223u8,166u8,93u8,13u8,228u8,9u8,178u8,26u8,10u8,83u8,49u8,36u8,108u8,166u8,105u8,195u8,180u8,30u8, - 39u8,34u8,164u8,80u8,172u8,243u8,145u8,230u8,69u8,26u8,147u8,41u8,98u8,156u8,150u8,249u8,252u8,89u8,205u8,43u8, - 194u8,10u8,189u8,255u8,194u8,20u8,207u8,19u8,117u8,165u8,24u8,83u8,235u8,53u8,228u8,171u8,209u8,34u8,46u8,86u8, - 51u8,202u8,162u8,85u8,166u8,176u8,121u8,154u8,100u8,240u8,47u8,138u8,172u8,86u8,130u8,51u8,93u8,195u8,27u8,7u8, - 55u8,153u8,195u8,162u8,224u8,228u8,142u8,111u8,175u8,99u8,47u8,133u8,134u8,19u8,60u8,116u8,170u8,112u8,48u8,197u8, - 174u8,177u8,158u8,9u8,40u8,71u8,185u8,111u8,104u8,167u8,165u8,74u8,48u8,34u8,194u8,66u8,192u8,214u8,76u8,247u8, - 38u8,97u8,207u8,18u8,197u8,47u8,140u8,79u8,225u8,38u8,42u8,142u8,230u8,22u8,117u8,90u8,138u8,76u8,144u8,255u8, - 28u8,146u8,123u8,201u8,127u8,45u8,243u8,182u8,238u8,106u8,89u8,55u8,52u8,10u8,74u8,106u8,104u8,162u8,235u8,252u8, - 54u8,141u8,152u8,57u8,20u8,162u8,206u8,18u8,8u8,195u8,135u8,231u8,4u8,229u8,222u8,79u8,163u8,140u8,26u8,104u8, - 186u8,245u8,217u8,71u8,209u8,136u8,116u8,234u8,48u8,22u8,85u8,45u8,177u8,44u8,42u8,52u8,210u8,158u8,154u8,26u8, - 17u8,176u8,187u8,169u8,68u8,139u8,0u8,204u8,22u8,96u8,11u8,225u8,96u8,16u8,187u8,138u8,114u8,117u8,25u8,207u8, - 141u8,134u8,193u8,203u8,19u8,143u8,23u8,94u8,177u8,185u8,66u8,179u8,135u8,225u8,159u8,143u8,23u8,117u8,27u8,68u8, - 169u8,164u8,87u8,23u8,210u8,194u8,100u8,124u8,28u8,209u8,76u8,102u8,113u8,245u8,105u8,29u8,96u8,125u8,220u8,55u8, - 185u8,20u8,12u8,204u8,115u8,228u8,152u8,85u8,193u8,83u8,111u8,11u8,43u8,174u8,103u8,118u8,99u8,205u8,45u8,137u8, - 7u8,20u8,111u8,128u8,88u8,169u8,240u8,50u8,194u8,166u8,6u8,203u8,148u8,156u8,126u8,161u8,109u8,136u8,141u8,163u8, - 12u8,108u8,45u8,130u8,64u8,156u8,89u8,112u8,204u8,176u8,164u8,28u8,130u8,232u8,163u8,111u8,9u8,110u8,75u8,9u8, - 3u8,183u8,23u8,198u8,182u8,100u8,46u8,175u8,106u8,113u8,82u8,194u8,29u8,100u8,59u8,224u8,140u8,189u8,106u8,27u8, - 171u8,112u8,77u8,24u8,111u8,152u8,107u8,116u8,69u8,139u8,91u8,250u8,36u8,104u8,163u8,244u8,73u8,14u8,128u8,14u8, - 167u8,121u8,167u8,138u8,77u8,113u8,79u8,89u8,82u8,69u8,115u8,78u8,142u8,108u8,109u8,191u8,188u8,208u8,128u8,173u8, - 58u8,235u8,167u8,119u8,197u8,10u8,166u8,205u8,70u8,253u8,204u8,209u8,172u8,163u8,136u8,82u8,31u8,217u8,185u8,27u8, - 105u8,86u8,66u8,237u8,70u8,209u8,152u8,65u8,144u8,228u8,54u8,15u8,178u8,150u8,126u8,211u8,164u8,60u8,175u8,47u8, - 44u8,58u8,176u8,88u8,251u8,77u8,225u8,87u8,145u8,53u8,8u8,188u8,186u8,28u8,209u8,82u8,32u8,20u8,245u8,84u8, - 139u8,150u8,79u8,115u8,95u8,181u8,171u8,65u8,16u8,194u8,193u8,70u8,14u8,93u8,48u8,122u8,169u8,59u8,11u8,138u8, - 76u8,193u8,231u8,122u8,118u8,68u8,82u8,68u8,1u8,0u8,70u8,5u8,196u8,116u8,2u8,88u8,83u8,35u8,195u8,84u8, - 45u8,28u8,196u8,186u8,25u8,229u8,0u8,24u8,224u8,244u8,192u8,131u8,131u8,163u8,198u8,48u8,48u8,196u8,221u8,33u8, - 20u8,42u8,105u8,121u8,8u8,124u8,119u8,21u8,194u8,112u8,144u8,49u8,95u8,217u8,75u8,43u8,77u8,30u8,48u8,213u8, - 96u8,156u8,213u8,150u8,168u8,115u8,11u8,231u8,188u8,72u8,83u8,240u8,184u8,10u8,229u8,161u8,65u8,69u8,7u8,113u8, - 56u8,185u8,129u8,97u8,87u8,15u8,223u8,114u8,84u8,160u8,185u8,56u8,43u8,193u8,182u8,228u8,6u8,85u8,183u8,155u8, - 186u8,96u8,205u8,42u8,141u8,118u8,80u8,242u8,204u8,75u8,76u8,71u8,70u8,211u8,204u8,149u8,12u8,182u8,221u8,174u8, - 221u8,26u8,199u8,119u8,56u8,127u8,109u8,147u8,224u8,218u8,231u8,46u8,1u8,122u8,56u8,64u8,122u8,215u8,14u8,170u8, - 171u8,67u8,78u8,30u8,37u8,44u8,130u8,220u8,133u8,140u8,195u8,237u8,233u8,222u8,85u8,241u8,231u8,224u8,149u8,147u8, - 107u8,101u8,88u8,122u8,203u8,22u8,63u8,223u8,209u8,67u8,202u8,3u8,61u8,87u8,198u8,173u8,237u8,86u8,208u8,25u8, - 77u8,86u8,183u8,16u8,116u8,28u8,144u8,105u8,174u8,136u8,174u8,98u8,162u8,219u8,94u8,192u8,248u8,30u8,26u8,148u8, - 217u8,185u8,166u8,107u8,50u8,177u8,54u8,207u8,247u8,74u8,241u8,71u8,6u8,161u8,125u8,147u8,252u8,145u8,197u8,64u8, - 133u8,33u8,160u8,1u8,28u8,55u8,109u8,201u8,246u8,14u8,222u8,75u8,96u8,157u8,45u8,211u8,147u8,39u8,169u8,31u8, - 103u8,11u8,90u8,186u8,43u8,59u8,46u8,87u8,29u8,219u8,235u8,49u8,215u8,214u8,150u8,250u8,142u8,97u8,74u8,224u8, - 145u8,76u8,106u8,210u8,171u8,222u8,172u8,55u8,175u8,146u8,70u8,229u8,215u8,206u8,231u8,253u8,186u8,231u8,76u8,102u8, - 73u8,91u8,61u8,79u8,107u8,238u8,21u8,2u8,94u8,239u8,167u8,6u8,1u8,160u8,215u8,195u8,64u8,114u8,188u8,127u8, - 32u8,184u8,227u8,64u8,226u8,211u8,34u8,65u8,241u8,129u8,103u8,28u8,200u8,235u8,181u8,28u8,204u8,53u8,119u8,235u8, - 53u8,188u8,254u8,22u8,96u8,245u8,124u8,71u8,118u8,18u8,156u8,25u8,61u8,123u8,66u8,35u8,96u8,181u8,30u8,85u8, - 230u8,196u8,227u8,201u8,212u8,53u8,185u8,164u8,116u8,5u8,6u8,213u8,110u8,3u8,69u8,187u8,96u8,75u8,188u8,75u8, - 199u8,80u8,14u8,78u8,117u8,29u8,195u8,191u8,33u8,155u8,86u8,92u8,126u8,71u8,30u8,253u8,209u8,189u8,140u8,163u8, - 61u8,176u8,150u8,142u8,20u8,214u8,21u8,86u8,216u8,88u8,186u8,130u8,211u8,98u8,235u8,38u8,5u8,207u8,169u8,129u8, - 81u8,60u8,132u8,229u8,77u8,227u8,134u8,223u8,131u8,224u8,137u8,172u8,118u8,115u8,166u8,165u8,123u8,225u8,18u8,187u8, - 180u8,83u8,24u8,69u8,54u8,192u8,69u8,109u8,255u8,145u8,50u8,62u8,119u8,86u8,202u8,147u8,150u8,134u8,117u8,50u8, - 59u8,77u8,230u8,213u8,88u8,72u8,166u8,234u8,4u8,49u8,45u8,175u8,202u8,29u8,77u8,248u8,84u8,238u8,193u8,147u8, - 122u8,54u8,118u8,206u8,66u8,56u8,255u8,143u8,147u8,226u8,110u8,201u8,230u8,210u8,251u8,36u8,12u8,170u8,139u8,1u8, - 78u8,205u8,74u8,232u8,206u8,81u8,175u8,60u8,210u8,216u8,97u8,204u8,52u8,17u8,240u8,243u8,107u8,115u8,88u8,111u8, - 25u8,243u8,159u8,89u8,87u8,155u8,63u8,90u8,79u8,252u8,232u8,221u8,151u8,135u8,239u8,234u8,197u8,151u8,170u8,137u8, - 159u8,167u8,72u8,183u8,30u8,234u8,53u8,219u8,178u8,110u8,147u8,108u8,93u8,3u8,179u8,55u8,101u8,76u8,108u8,32u8, - 10u8,178u8,68u8,222u8,197u8,148u8,119u8,214u8,44u8,184u8,252u8,149u8,61u8,227u8,181u8,181u8,112u8,112u8,158u8,127u8, - 147u8,191u8,254u8,114u8,181u8,49u8,142u8,64u8,1u8,78u8,222u8,243u8,155u8,158u8,13u8,222u8,243u8,24u8,151u8,43u8, - 219u8,110u8,166u8,130u8,34u8,197u8,111u8,97u8,156u8,211u8,52u8,70u8,155u8,200u8,87u8,81u8,224u8,194u8,142u8,245u8, - 206u8,220u8,140u8,89u8,71u8,92u8,210u8,183u8,77u8,186u8,229u8,61u8,88u8,121u8,241u8,52u8,131u8,48u8,53u8,56u8, - 128u8,45u8,244u8,113u8,13u8,133u8,136u8,95u8,98u8,193u8,149u8,217u8,124u8,153u8,100u8,120u8,39u8,6u8,34u8,45u8, - 138u8,8u8,216u8,206u8,240u8,156u8,166u8,156u8,99u8,241u8,162u8,82u8,34u8,108u8,27u8,135u8,245u8,30u8,154u8,65u8, - 59u8,75u8,105u8,226u8,155u8,46u8,98u8,157u8,39u8,8u8,237u8,128u8,254u8,153u8,243u8,73u8,121u8,245u8,155u8,193u8, - 37u8,123u8,8u8,51u8,241u8,133u8,106u8,122u8,210u8,17u8,206u8,106u8,191u8,1u8,163u8,163u8,192u8,183u8,106u8,172u8, - 98u8,202u8,39u8,66u8,225u8,19u8,29u8,89u8,176u8,37u8,25u8,229u8,217u8,161u8,85u8,22u8,4u8,93u8,207u8,188u8, - 26u8,20u8,228u8,62u8,131u8,41u8,169u8,159u8,166u8,236u8,44u8,139u8,93u8,5u8,130u8,225u8,88u8,201u8,14u8,146u8, - 242u8,214u8,111u8,206u8,236u8,72u8,22u8,97u8,154u8,229u8,142u8,208u8,225u8,46u8,160u8,163u8,22u8,160u8,27u8,15u8, - 112u8,198u8,94u8,16u8,222u8,135u8,1u8,21u8,41u8,195u8,169u8,249u8,150u8,237u8,161u8,213u8,78u8,53u8,163u8,52u8, - 119u8,58u8,5u8,174u8,52u8,200u8,181u8,207u8,219u8,149u8,108u8,170u8,188u8,240u8,228u8,202u8,189u8,114u8,215u8,119u8, - 165u8,12u8,231u8,132u8,162u8,31u8,39u8,103u8,53u8,227u8,158u8,9u8,90u8,151u8,117u8,165u8,130u8,190u8,45u8,223u8, - 62u8,175u8,146u8,125u8,210u8,236u8,79u8,209u8,217u8,51u8,105u8,86u8,187u8,129u8,119u8,102u8,88u8,77u8,173u8,90u8, - 130u8,149u8,115u8,188u8,204u8,180u8,230u8,155u8,112u8,17u8,52u8,147u8,63u8,52u8,169u8,118u8,255u8,137u8,180u8,33u8, - 80u8,112u8,59u8,202u8,97u8,165u8,122u8,71u8,73u8,162u8,46u8,215u8,191u8,133u8,198u8,207u8,202u8,21u8,141u8,45u8, - 201u8,98u8,91u8,42u8,112u8,228u8,140u8,38u8,190u8,24u8,133u8,46u8,240u8,195u8,179u8,132u8,118u8,233u8,111u8,188u8, - 29u8,40u8,213u8,202u8,241u8,101u8,123u8,121u8,55u8,222u8,69u8,219u8,3u8,183u8,99u8,216u8,182u8,94u8,56u8,217u8, - 255u8,53u8,21u8,43u8,48u8,182u8,122u8,247u8,247u8,58u8,246u8,183u8,155u8,117u8,15u8,139u8,42u8,90u8,118u8,146u8, - 243u8,180u8,188u8,241u8,40u8,83u8,198u8,70u8,126u8,248u8,82u8,55u8,43u8,242u8,105u8,36u8,195u8,23u8,77u8,46u8, - 72u8,3u8,249u8,236u8,159u8,12u8,27u8,143u8,178u8,69u8,192u8,47u8,27u8,56u8,99u8,238u8,148u8,223u8,190u8,40u8, - 163u8,67u8,67u8,64u8,34u8,235u8,136u8,93u8,229u8,152u8,95u8,94u8,115u8,177u8,193u8,97u8,106u8,122u8,66u8,124u8, - 180u8,21u8,28u8,73u8,114u8,112u8,193u8,62u8,44u8,184u8,224u8,97u8,89u8,194u8,63u8,29u8,252u8,13u8,195u8,113u8, - 179u8,205u8,71u8,44u8,0u8,0u8,0u8,0u8,12u8,114u8,105u8,115u8,116u8,114u8,101u8,116u8,116u8,111u8,50u8,53u8, - 53u8,215u8,71u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,61u8,107u8,119u8,219u8,184u8,177u8, - 223u8,247u8,87u8,112u8,221u8,173u8,35u8,109u8,100u8,135u8,239u8,135u8,83u8,239u8,61u8,78u8,226u8,180u8,57u8,39u8, - 143u8,61u8,113u8,218u8,166u8,143u8,173u8,2u8,146u8,160u8,196u8,91u8,189u8,174u8,40u8,37u8,118u8,123u8,242u8,223u8, - 239u8,12u8,0u8,146u8,32u8,9u8,82u8,164u8,31u8,233u8,126u8,104u8,118u8,147u8,56u8,18u8,128u8,121u8,98u8,48u8, - 152u8,1u8,6u8,79u8,158u8,60u8,209u8,62u8,204u8,211u8,76u8,91u8,174u8,227u8,253u8,130u8,106u8,209u8,122u8,181u8, - 35u8,233u8,42u8,211u8,146u8,253u8,42u8,218u8,165u8,107u8,252u8,105u8,189u8,213u8,222u8,167u8,217u8,110u8,75u8,119u8, - 187u8,181u8,233u8,56u8,90u8,180u8,223u8,126u8,166u8,26u8,217u8,166u8,187u8,249u8,146u8,238u8,210u8,104u8,162u8,145u8, - 44u8,219u8,47u8,211u8,213u8,76u8,35u8,113u8,156u8,98u8,47u8,248u8,68u8,219u8,205u8,169u8,54u8,219u8,174u8,247u8, - 27u8,109u8,189u8,161u8,91u8,130u8,31u8,159u8,126u8,247u8,228u8,201u8,19u8,252u8,13u8,48u8,169u8,182u8,222u8,198u8, - 116u8,171u8,173u8,19u8,214u8,178u8,2u8,129u8,46u8,22u8,233u8,6u8,70u8,22u8,160u8,248u8,40u8,128u8,228u8,15u8, - 127u8,135u8,111u8,180u8,115u8,205u8,252u8,135u8,233u8,152u8,218u8,99u8,205u8,244u8,60u8,219u8,180u8,12u8,15u8,126u8, - 89u8,158u8,105u8,57u8,248u8,159u8,239u8,24u8,129u8,229u8,121u8,129u8,238u8,251u8,150u8,107u8,251u8,118u8,96u8,253u8, - 48u8,209u8,50u8,178u8,164u8,12u8,174u8,192u8,171u8,2u8,122u8,179u8,77u8,151u8,244u8,132u8,127u8,148u8,237u8,67u8, - 129u8,115u8,162u8,61u8,71u8,224u8,128u8,142u8,17u8,200u8,120u8,151u8,188u8,218u8,108u8,215u8,159u8,211u8,152u8,194u8, - 136u8,95u8,214u8,26u8,96u8,191u8,143u8,118u8,156u8,91u8,116u8,21u8,173u8,99u8,100u8,71u8,65u8,84u8,141u8,34u8, - 232u8,178u8,102u8,160u8,99u8,250u8,153u8,46u8,144u8,61u8,103u8,5u8,4u8,237u8,68u8,123u8,153u8,110u8,179u8,29u8, - 112u8,85u8,179u8,204u8,147u8,240u8,102u8,71u8,79u8,178u8,244u8,95u8,52u8,214u8,158u8,175u8,151u8,155u8,45u8,205u8, - 50u8,26u8,151u8,227u8,114u8,176u8,19u8,237u8,203u8,60u8,141u8,230u8,200u8,162u8,61u8,124u8,141u8,163u8,195u8,152u8, - 25u8,180u8,210u8,54u8,235u8,116u8,5u8,104u8,165u8,43u8,104u8,186u8,222u8,146u8,25u8,61u8,149u8,33u8,93u8,81u8, - 16u8,120u8,140u8,160u8,22u8,100u8,59u8,163u8,219u8,9u8,52u8,60u8,89u8,210u8,229u8,122u8,123u8,51u8,41u8,177u8, - 255u8,25u8,199u8,104u8,130u8,138u8,161u8,51u8,199u8,136u8,132u8,192u8,142u8,100u8,187u8,94u8,194u8,64u8,237u8,88u8, - 158u8,50u8,238u8,49u8,208u8,28u8,154u8,248u8,92u8,139u8,200u8,74u8,11u8,41u8,71u8,29u8,249u8,151u8,16u8,64u8, - 188u8,212u8,175u8,82u8,127u8,50u8,109u8,148u8,107u8,88u8,54u8,209u8,150u8,251u8,197u8,46u8,221u8,44u8,210u8,136u8, - 136u8,15u8,232u8,46u8,58u8,29u8,159u8,50u8,221u8,2u8,12u8,224u8,219u8,44u8,31u8,58u8,35u8,159u8,105u8,204u8, - 32u8,135u8,36u8,250u8,39u8,16u8,201u8,144u8,98u8,252u8,208u8,194u8,27u8,45u8,167u8,163u8,34u8,51u8,153u8,234u8, - 140u8,247u8,131u8,94u8,237u8,228u8,101u8,106u8,45u8,33u8,139u8,108u8,93u8,170u8,10u8,209u8,174u8,34u8,2u8,212u8, - 231u8,164u8,35u8,185u8,66u8,84u8,8u8,60u8,99u8,95u8,202u8,226u8,210u8,200u8,42u8,214u8,226u8,53u8,126u8,217u8, - 96u8,203u8,10u8,117u8,104u8,89u8,130u8,125u8,183u8,162u8,208u8,243u8,51u8,52u8,33u8,128u8,249u8,18u8,230u8,49u8, - 206u8,101u8,96u8,42u8,144u8,184u8,147u8,80u8,74u8,113u8,18u8,16u8,24u8,11u8,38u8,83u8,7u8,61u8,12u8,5u8, - 64u8,0u8,153u8,184u8,94u8,1u8,155u8,23u8,139u8,155u8,19u8,166u8,219u8,48u8,36u8,211u8,171u8,9u8,3u8,203u8, - 21u8,2u8,57u8,77u8,22u8,95u8,200u8,77u8,134u8,12u8,47u8,117u8,3u8,218u8,50u8,118u8,19u8,237u8,51u8,89u8, - 164u8,162u8,163u8,192u8,61u8,55u8,37u8,200u8,23u8,37u8,223u8,79u8,181u8,63u8,174u8,128u8,69u8,187u8,253u8,138u8, - 236u8,232u8,2u8,116u8,50u8,222u8,83u8,144u8,2u8,215u8,161u8,116u8,153u8,238u8,132u8,90u8,0u8,191u8,214u8,251u8, - 173u8,182u8,95u8,193u8,28u8,94u8,220u8,32u8,179u8,162u8,98u8,242u8,158u8,196u8,100u8,65u8,255u8,89u8,183u8,41u8, - 139u8,52u8,220u8,18u8,212u8,114u8,198u8,152u8,18u8,93u8,52u8,94u8,95u8,82u8,96u8,204u8,126u8,181u8,162u8,17u8, - 234u8,246u8,54u8,93u8,220u8,104u8,159u8,233u8,54u8,77u8,110u8,24u8,92u8,196u8,155u8,145u8,146u8,238u8,110u8,10u8, - 19u8,194u8,112u8,70u8,81u8,237u8,230u8,123u8,96u8,220u8,34u8,157u8,205u8,119u8,208u8,13u8,198u8,221u8,82u8,146u8, - 81u8,148u8,49u8,208u8,177u8,36u8,171u8,72u8,154u8,128u8,87u8,64u8,1u8,136u8,27u8,233u8,66u8,81u8,84u8,52u8, - 163u8,155u8,251u8,92u8,79u8,38u8,74u8,214u8,103u8,36u8,1u8,86u8,137u8,73u8,3u8,109u8,129u8,160u8,61u8,106u8, - 54u8,183u8,127u8,170u8,89u8,85u8,98u8,244u8,138u8,139u8,37u8,217u8,239u8,246u8,91u8,10u8,195u8,83u8,109u8,137u8, - 148u8,128u8,89u8,220u8,108u8,64u8,12u8,133u8,125u8,39u8,11u8,45u8,161u8,4u8,27u8,101u8,165u8,229u8,250u8,81u8, - 123u8,9u8,218u8,44u8,84u8,248u8,140u8,155u8,24u8,52u8,50u8,33u8,217u8,69u8,243u8,41u8,40u8,38u8,221u8,238u8, - 70u8,227u8,210u8,252u8,240u8,246u8,220u8,64u8,73u8,205u8,227u8,245u8,30u8,204u8,9u8,111u8,168u8,177u8,95u8,143u8, - 217u8,148u8,142u8,230u8,192u8,2u8,186u8,154u8,149u8,250u8,219u8,144u8,113u8,188u8,134u8,41u8,246u8,246u8,221u8,7u8, - 141u8,94u8,51u8,116u8,249u8,80u8,213u8,21u8,44u8,183u8,136u8,163u8,21u8,90u8,26u8,248u8,125u8,25u8,127u8,33u8, - 219u8,56u8,147u8,16u8,43u8,177u8,152u8,130u8,72u8,167u8,185u8,106u8,76u8,25u8,33u8,163u8,90u8,195u8,36u8,189u8, - 166u8,241u8,73u8,8u8,66u8,158u8,104u8,108u8,234u8,65u8,183u8,147u8,29u8,172u8,40u8,218u8,231u8,148u8,0u8,147u8, - 57u8,187u8,166u8,75u8,108u8,54u8,101u8,38u8,139u8,115u8,8u8,127u8,30u8,141u8,81u8,115u8,255u8,68u8,182u8,216u8, - 254u8,231u8,45u8,83u8,194u8,61u8,200u8,236u8,77u8,217u8,12u8,126u8,172u8,50u8,2u8,136u8,255u8,178u8,222u8,47u8, - 98u8,48u8,113u8,255u8,183u8,79u8,153u8,134u8,8u8,83u8,113u8,146u8,108u8,83u8,186u8,138u8,65u8,246u8,5u8,177u8, - 207u8,0u8,45u8,70u8,239u8,7u8,102u8,162u8,81u8,65u8,65u8,87u8,10u8,27u8,175u8,229u8,182u8,2u8,212u8,56u8, - 221u8,193u8,196u8,90u8,203u8,176u8,132u8,122u8,230u8,107u8,149u8,202u8,82u8,224u8,128u8,45u8,6u8,51u8,93u8,201u8, - 54u8,103u8,184u8,48u8,31u8,101u8,173u8,100u8,64u8,151u8,213u8,26u8,32u8,193u8,156u8,132u8,121u8,248u8,47u8,252u8, - 236u8,187u8,239u8,114u8,115u8,187u8,217u8,173u8,179u8,105u8,182u8,139u8,207u8,206u8,182u8,178u8,75u8,241u8,239u8,239u8, - 16u8,56u8,44u8,49u8,26u8,251u8,142u8,203u8,229u8,236u8,236u8,29u8,251u8,251u8,233u8,119u8,236u8,219u8,223u8,252u8, - 109u8,71u8,179u8,221u8,116u8,189u8,90u8,220u8,252u8,162u8,106u8,46u8,154u8,129u8,244u8,249u8,95u8,192u8,146u8,85u8, - 182u8,3u8,254u8,101u8,249u8,231u8,226u8,239u8,67u8,254u8,13u8,119u8,49u8,144u8,121u8,41u8,78u8,116u8,62u8,239u8, - 147u8,148u8,46u8,98u8,92u8,128u8,193u8,54u8,237u8,118u8,160u8,68u8,32u8,75u8,16u8,206u8,41u8,27u8,51u8,66u8, - 72u8,218u8,187u8,247u8,47u8,46u8,223u8,79u8,47u8,95u8,191u8,62u8,3u8,131u8,20u8,129u8,212u8,127u8,183u8,247u8, - 127u8,2u8,127u8,232u8,250u8,136u8,198u8,177u8,149u8,56u8,78u8,100u8,16u8,215u8,50u8,76u8,199u8,143u8,221u8,32u8, - 74u8,60u8,98u8,198u8,52u8,9u8,98u8,106u8,216u8,122u8,231u8,47u8,67u8,63u8,122u8,90u8,226u8,254u8,169u8,0u8, - 243u8,9u8,244u8,219u8,56u8,211u8,210u8,83u8,122u8,58u8,97u8,84u8,28u8,177u8,133u8,59u8,219u8,29u8,77u8,64u8, - 253u8,226u8,125u8,84u8,152u8,34u8,46u8,110u8,202u8,137u8,144u8,48u8,126u8,61u8,125u8,243u8,234u8,237u8,31u8,175u8, - 166u8,239u8,222u8,94u8,54u8,113u8,142u8,238u8,15u8,103u8,228u8,247u8,146u8,92u8,167u8,203u8,253u8,82u8,67u8,111u8, - 9u8,241u8,65u8,215u8,41u8,67u8,254u8,171u8,109u8,168u8,48u8,183u8,160u8,76u8,150u8,201u8,219u8,202u8,172u8,126u8, - 115u8,241u8,113u8,122u8,245u8,252u8,226u8,245u8,197u8,251u8,233u8,219u8,63u8,190u8,153u8,62u8,251u8,203u8,135u8,203u8, - 171u8,51u8,109u8,239u8,218u8,128u8,186u8,101u8,194u8,223u8,135u8,128u8,163u8,100u8,123u8,192u8,54u8,29u8,151u8,181u8, - 237u8,130u8,253u8,234u8,67u8,1u8,26u8,154u8,247u8,128u8,221u8,77u8,120u8,69u8,21u8,249u8,202u8,213u8,193u8,132u8, - 159u8,223u8,189u8,122u8,251u8,161u8,31u8,15u8,194u8,124u8,154u8,106u8,163u8,25u8,93u8,225u8,234u8,178u8,222u8,142u8, - 219u8,39u8,128u8,4u8,232u8,217u8,197u8,213u8,37u8,135u8,212u8,212u8,18u8,51u8,49u8,9u8,213u8,137u8,75u8,194u8, - 200u8,166u8,158u8,65u8,124u8,223u8,38u8,129u8,107u8,68u8,142u8,174u8,59u8,134u8,147u8,56u8,62u8,181u8,244u8,208u8, - 37u8,196u8,241u8,205u8,56u8,246u8,99u8,248u8,209u8,117u8,2u8,219u8,161u8,186u8,31u8,155u8,177u8,231u8,30u8,53u8, - 102u8,237u8,123u8,88u8,145u8,243u8,141u8,13u8,221u8,110u8,225u8,79u8,228u8,139u8,122u8,14u8,175u8,246u8,203u8,144u8, - 79u8,226u8,220u8,53u8,99u8,107u8,12u8,154u8,159u8,37u8,174u8,6u8,140u8,180u8,178u8,17u8,95u8,91u8,100u8,22u8, - 94u8,78u8,95u8,188u8,122u8,249u8,242u8,242u8,253u8,165u8,96u8,34u8,35u8,242u8,106u8,122u8,241u8,246u8,133u8,16u8, - 113u8,193u8,81u8,227u8,105u8,1u8,249u8,242u8,122u8,3u8,76u8,0u8,81u8,45u8,209u8,5u8,0u8,243u8,184u8,210u8, - 254u8,69u8,183u8,197u8,210u8,69u8,208u8,186u8,194u8,66u8,81u8,133u8,243u8,215u8,203u8,247u8,239u8,196u8,240u8,133u8, - 182u8,28u8,30u8,50u8,39u8,171u8,99u8,204u8,26u8,158u8,86u8,131u8,161u8,66u8,155u8,209u8,158u8,109u8,228u8,53u8, - 64u8,193u8,209u8,52u8,203u8,61u8,223u8,45u8,197u8,213u8,132u8,50u8,122u8,114u8,35u8,194u8,28u8,193u8,138u8,245u8, - 99u8,58u8,89u8,110u8,168u8,80u8,167u8,113u8,253u8,218u8,81u8,220u8,62u8,128u8,170u8,255u8,240u8,119u8,16u8,195u8, - 60u8,12u8,255u8,253u8,215u8,175u8,83u8,220u8,28u8,254u8,80u8,238u8,79u8,10u8,168u8,204u8,145u8,66u8,47u8,84u8, - 251u8,20u8,147u8,29u8,249u8,116u8,170u8,253u8,129u8,162u8,119u8,195u8,54u8,147u8,49u8,5u8,89u8,82u8,197u8,174u8, - 80u8,182u8,202u8,176u8,237u8,96u8,174u8,29u8,149u8,61u8,76u8,213u8,38u8,117u8,204u8,217u8,39u8,72u8,20u8,124u8, - 153u8,3u8,89u8,209u8,122u8,3u8,94u8,30u8,195u8,4u8,156u8,216u8,237u8,122u8,35u8,22u8,36u8,252u8,133u8,72u8, - 201u8,106u8,207u8,190u8,248u8,218u8,135u8,107u8,98u8,1u8,164u8,53u8,175u8,186u8,185u8,91u8,103u8,203u8,74u8,117u8, - 134u8,215u8,71u8,22u8,59u8,165u8,138u8,227u8,206u8,182u8,116u8,249u8,70u8,132u8,123u8,241u8,178u8,247u8,80u8,93u8, - 249u8,39u8,8u8,158u8,51u8,159u8,237u8,84u8,234u8,97u8,130u8,2u8,170u8,128u8,36u8,92u8,98u8,26u8,87u8,120u8, - 166u8,242u8,50u8,30u8,138u8,129u8,74u8,90u8,74u8,163u8,136u8,50u8,23u8,142u8,111u8,166u8,38u8,169u8,224u8,100u8, - 1u8,234u8,2u8,198u8,92u8,98u8,15u8,244u8,170u8,138u8,189u8,216u8,25u8,218u8,18u8,152u8,118u8,108u8,99u8,177u8, - 130u8,61u8,255u8,22u8,25u8,128u8,97u8,131u8,42u8,11u8,31u8,101u8,124u8,230u8,33u8,166u8,192u8,254u8,47u8,0u8, - 135u8,235u8,34u8,132u8,47u8,128u8,11u8,224u8,219u8,209u8,83u8,182u8,203u8,67u8,22u8,206u8,97u8,79u8,91u8,192u8, - 77u8,151u8,203u8,253u8,142u8,57u8,71u8,91u8,154u8,0u8,40u8,216u8,103u8,20u8,65u8,6u8,214u8,187u8,33u8,171u8, - 4u8,64u8,237u8,183u8,25u8,173u8,112u8,191u8,230u8,203u8,33u8,227u8,107u8,188u8,230u8,88u8,48u8,91u8,80u8,101u8, - 115u8,110u8,12u8,94u8,86u8,2u8,71u8,213u8,237u8,42u8,183u8,95u8,13u8,179u8,240u8,158u8,194u8,78u8,98u8,197u8, - 231u8,32u8,236u8,146u8,87u8,59u8,220u8,89u8,9u8,25u8,100u8,234u8,144u8,2u8,199u8,122u8,3u8,62u8,58u8,140u8, - 12u8,177u8,42u8,222u8,124u8,154u8,247u8,158u8,150u8,74u8,60u8,26u8,159u8,41u8,149u8,170u8,36u8,169u8,251u8,219u8, - 82u8,193u8,174u8,143u8,244u8,59u8,254u8,58u8,42u8,70u8,253u8,90u8,215u8,209u8,251u8,231u8,1u8,18u8,94u8,147u8, - 103u8,73u8,85u8,235u8,23u8,178u8,140u8,107u8,60u8,69u8,179u8,187u8,133u8,189u8,12u8,236u8,128u8,122u8,145u8,49u8, - 208u8,29u8,224u8,116u8,74u8,214u8,135u8,245u8,173u8,211u8,88u8,12u8,250u8,32u8,34u8,46u8,253u8,144u8,7u8,33u8, - 177u8,149u8,152u8,78u8,89u8,45u8,40u8,12u8,207u8,101u8,50u8,209u8,166u8,99u8,88u8,130u8,57u8,3u8,74u8,83u8, - 93u8,138u8,166u8,36u8,96u8,44u8,150u8,233u8,158u8,210u8,110u8,167u8,247u8,141u8,8u8,179u8,209u8,219u8,73u8,21u8, - 98u8,79u8,229u8,250u8,190u8,194u8,109u8,107u8,201u8,62u8,30u8,165u8,171u8,173u8,71u8,232u8,172u8,162u8,67u8,186u8, - 220u8,139u8,149u8,4u8,172u8,37u8,243u8,87u8,62u8,113u8,178u8,113u8,223u8,124u8,44u8,113u8,110u8,162u8,29u8,103u8, - 235u8,37u8,157u8,114u8,24u8,227u8,79u8,96u8,90u8,35u8,130u8,59u8,55u8,244u8,199u8,242u8,61u8,53u8,11u8,116u8, - 104u8,204u8,66u8,102u8,167u8,237u8,58u8,133u8,99u8,131u8,26u8,28u8,243u8,149u8,251u8,206u8,19u8,168u8,50u8,112u8, - 41u8,36u8,114u8,138u8,218u8,214u8,49u8,139u8,158u8,67u8,208u8,104u8,199u8,162u8,133u8,43u8,250u8,69u8,169u8,188u8, - 92u8,2u8,34u8,228u8,154u8,65u8,36u8,0u8,45u8,62u8,82u8,92u8,44u8,242u8,218u8,43u8,20u8,201u8,58u8,163u8, - 98u8,59u8,16u8,175u8,153u8,215u8,90u8,172u8,127u8,121u8,64u8,174u8,0u8,202u8,35u8,122u8,133u8,124u8,222u8,174u8, - 87u8,180u8,193u8,41u8,64u8,71u8,154u8,119u8,83u8,78u8,27u8,98u8,49u8,101u8,64u8,70u8,236u8,79u8,121u8,41u8, - 6u8,22u8,242u8,45u8,246u8,239u8,20u8,84u8,252u8,36u8,177u8,45u8,77u8,180u8,145u8,176u8,55u8,217u8,180u8,216u8, - 181u8,148u8,60u8,99u8,35u8,143u8,199u8,53u8,70u8,87u8,246u8,242u8,168u8,5u8,163u8,195u8,51u8,189u8,156u8,237u8, - 108u8,204u8,202u8,119u8,95u8,37u8,161u8,128u8,131u8,7u8,204u8,235u8,128u8,7u8,56u8,82u8,37u8,89u8,163u8,222u8, - 162u8,173u8,105u8,209u8,125u8,200u8,51u8,143u8,204u8,230u8,0u8,15u8,202u8,115u8,136u8,16u8,171u8,248u8,254u8,212u8, - 102u8,164u8,100u8,17u8,118u8,219u8,43u8,46u8,214u8,167u8,21u8,53u8,168u8,246u8,62u8,36u8,240u8,250u8,68u8,20u8, - 147u8,239u8,22u8,162u8,172u8,17u8,167u8,146u8,98u8,193u8,213u8,63u8,144u8,108u8,46u8,12u8,34u8,219u8,54u8,105u8, - 44u8,194u8,189u8,95u8,165u8,232u8,210u8,194u8,94u8,155u8,236u8,78u8,182u8,128u8,7u8,136u8,179u8,134u8,31u8,6u8, - 5u8,175u8,254u8,112u8,225u8,24u8,230u8,33u8,97u8,100u8,115u8,2u8,173u8,70u8,252u8,175u8,154u8,56u8,238u8,102u8, - 150u8,148u8,128u8,74u8,153u8,240u8,127u8,119u8,232u8,240u8,21u8,89u8,110u8,22u8,76u8,135u8,123u8,16u8,60u8,75u8, - 63u8,211u8,85u8,77u8,169u8,97u8,31u8,169u8,234u8,41u8,20u8,157u8,173u8,2u8,121u8,254u8,177u8,190u8,107u8,200u8, - 243u8,91u8,225u8,62u8,133u8,125u8,25u8,250u8,11u8,176u8,13u8,195u8,168u8,253u8,9u8,40u8,86u8,202u8,162u8,113u8, - 232u8,182u8,206u8,139u8,1u8,120u8,116u8,113u8,73u8,54u8,25u8,128u8,101u8,233u8,52u8,109u8,137u8,97u8,253u8,25u8, - 119u8,144u8,235u8,14u8,248u8,33u8,169u8,184u8,246u8,84u8,160u8,126u8,151u8,217u8,130u8,106u8,206u8,20u8,144u8,119u8, - 59u8,59u8,195u8,88u8,232u8,110u8,14u8,235u8,26u8,155u8,15u8,218u8,249u8,57u8,32u8,59u8,92u8,247u8,27u8,118u8, - 174u8,69u8,226u8,117u8,34u8,234u8,243u8,241u8,142u8,54u8,177u8,199u8,68u8,202u8,165u8,250u8,162u8,220u8,110u8,182u8, - 120u8,185u8,170u8,77u8,104u8,93u8,199u8,202u8,52u8,72u8,71u8,26u8,177u8,197u8,93u8,46u8,173u8,18u8,95u8,127u8, - 96u8,245u8,87u8,96u8,209u8,53u8,231u8,128u8,12u8,200u8,57u8,64u8,124u8,243u8,29u8,164u8,160u8,84u8,20u8,148u8, - 57u8,57u8,76u8,148u8,195u8,183u8,128u8,35u8,87u8,75u8,220u8,10u8,222u8,180u8,244u8,41u8,105u8,206u8,42u8,107u8, - 117u8,30u8,197u8,170u8,160u8,115u8,43u8,103u8,145u8,125u8,195u8,125u8,145u8,167u8,173u8,22u8,164u8,176u8,167u8,141u8, - 149u8,76u8,22u8,92u8,173u8,23u8,19u8,146u8,138u8,172u8,138u8,164u8,208u8,110u8,202u8,137u8,105u8,165u8,124u8,26u8, - 210u8,169u8,130u8,186u8,55u8,215u8,191u8,10u8,173u8,198u8,164u8,158u8,59u8,30u8,217u8,198u8,241u8,85u8,186u8,88u8, - 158u8,243u8,116u8,72u8,205u8,133u8,147u8,124u8,223u8,53u8,238u8,253u8,49u8,71u8,214u8,100u8,231u8,163u8,205u8,35u8, - 102u8,243u8,112u8,200u8,9u8,132u8,164u8,182u8,24u8,139u8,16u8,152u8,98u8,226u8,6u8,23u8,149u8,79u8,81u8,33u8, - 235u8,130u8,101u8,199u8,155u8,241u8,167u8,137u8,72u8,75u8,130u8,17u8,102u8,158u8,53u8,98u8,81u8,0u8,45u8,140u8, - 36u8,252u8,255u8,41u8,250u8,212u8,34u8,130u8,221u8,90u8,216u8,186u8,238u8,9u8,34u8,5u8,110u8,75u8,6u8,151u8, - 58u8,214u8,198u8,56u8,2u8,137u8,64u8,137u8,23u8,13u8,232u8,232u8,147u8,171u8,101u8,15u8,164u8,221u8,159u8,175u8, - 174u8,240u8,211u8,133u8,71u8,204u8,221u8,117u8,224u8,59u8,164u8,242u8,105u8,215u8,178u8,72u8,89u8,84u8,238u8,199u8, - 115u8,41u8,126u8,148u8,123u8,95u8,143u8,200u8,163u8,118u8,242u8,166u8,96u8,22u8,210u8,217u8,170u8,160u8,18u8,66u8, - 57u8,90u8,39u8,165u8,138u8,22u8,117u8,150u8,119u8,82u8,2u8,193u8,30u8,42u8,77u8,249u8,114u8,163u8,173u8,16u8, - 207u8,8u8,229u8,195u8,54u8,88u8,156u8,170u8,199u8,90u8,8u8,31u8,224u8,206u8,134u8,127u8,48u8,198u8,136u8,43u8, - 6u8,183u8,202u8,143u8,120u8,78u8,79u8,218u8,13u8,74u8,155u8,198u8,60u8,9u8,129u8,209u8,216u8,114u8,179u8,250u8, - 169u8,99u8,79u8,38u8,50u8,177u8,213u8,173u8,217u8,68u8,66u8,73u8,161u8,23u8,225u8,67u8,236u8,225u8,74u8,68u8, - 234u8,91u8,57u8,25u8,27u8,0u8,126u8,104u8,123u8,87u8,106u8,62u8,48u8,83u8,173u8,21u8,144u8,104u8,103u8,212u8, - 170u8,8u8,107u8,24u8,192u8,251u8,80u8,124u8,0u8,40u8,81u8,5u8,128u8,122u8,235u8,251u8,227u8,115u8,208u8,136u8, - 126u8,186u8,142u8,64u8,132u8,174u8,147u8,22u8,61u8,87u8,18u8,216u8,71u8,223u8,21u8,4u8,212u8,212u8,188u8,195u8, - 2u8,157u8,180u8,201u8,33u8,219u8,135u8,223u8,86u8,14u8,0u8,240u8,150u8,114u8,56u8,233u8,47u8,7u8,4u8,242u8, - 80u8,114u8,80u8,16u8,208u8,87u8,14u8,39u8,68u8,141u8,238u8,138u8,206u8,20u8,66u8,184u8,39u8,134u8,195u8,224u8, - 21u8,124u8,123u8,114u8,251u8,28u8,176u8,157u8,244u8,100u8,55u8,130u8,232u8,100u8,119u8,79u8,214u8,214u8,81u8,237u8, - 203u8,87u8,108u8,135u8,91u8,14u8,180u8,201u8,205u8,164u8,3u8,80u8,195u8,50u8,130u8,34u8,81u8,32u8,114u8,140u8, - 34u8,149u8,84u8,77u8,114u8,113u8,250u8,86u8,16u8,76u8,251u8,76u8,155u8,100u8,130u8,243u8,3u8,188u8,27u8,205u8, - 20u8,211u8,101u8,174u8,146u8,94u8,184u8,94u8,47u8,158u8,86u8,253u8,201u8,61u8,15u8,141u8,176u8,99u8,50u8,39u8, - 34u8,104u8,88u8,61u8,230u8,151u8,7u8,168u8,216u8,105u8,211u8,169u8,161u8,109u8,224u8,247u8,99u8,248u8,201u8,132u8, - 159u8,240u8,72u8,232u8,233u8,233u8,41u8,251u8,55u8,32u8,53u8,93u8,213u8,34u8,139u8,133u8,183u8,163u8,138u8,46u8, - 242u8,56u8,33u8,75u8,236u8,17u8,248u8,138u8,76u8,83u8,24u8,33u8,229u8,71u8,167u8,164u8,208u8,227u8,39u8,126u8, - 136u8,38u8,102u8,233u8,72u8,8u8,111u8,238u8,164u8,195u8,134u8,149u8,182u8,208u8,164u8,185u8,170u8,49u8,90u8,166u8, - 210u8,1u8,32u8,113u8,250u8,73u8,59u8,22u8,206u8,83u8,109u8,235u8,52u8,41u8,142u8,83u8,21u8,45u8,248u8,178u8, - 214u8,25u8,4u8,0u8,93u8,3u8,55u8,242u8,251u8,209u8,247u8,149u8,45u8,38u8,132u8,85u8,232u8,114u8,3u8,121u8, - 1u8,14u8,115u8,60u8,225u8,59u8,55u8,150u8,1u8,135u8,111u8,87u8,108u8,135u8,49u8,37u8,219u8,217u8,126u8,9u8, - 254u8,234u8,168u8,146u8,80u8,30u8,203u8,106u8,214u8,61u8,186u8,64u8,184u8,231u8,240u8,34u8,183u8,172u8,26u8,95u8, - 181u8,63u8,22u8,168u8,227u8,254u8,88u8,245u8,117u8,111u8,216u8,135u8,114u8,242u8,227u8,129u8,209u8,243u8,179u8,134u8, - 100u8,139u8,137u8,250u8,187u8,250u8,92u8,16u8,2u8,20u8,180u8,20u8,18u8,110u8,53u8,57u8,125u8,18u8,107u8,124u8, - 204u8,102u8,102u8,237u8,247u8,138u8,224u8,75u8,30u8,81u8,156u8,104u8,209u8,156u8,70u8,255u8,204u8,132u8,101u8,184u8, - 81u8,156u8,21u8,41u8,143u8,171u8,150u8,134u8,14u8,246u8,27u8,229u8,180u8,122u8,7u8,29u8,183u8,95u8,82u8,60u8, - 251u8,118u8,48u8,206u8,40u8,120u8,211u8,47u8,208u8,40u8,152u8,84u8,15u8,153u8,240u8,33u8,238u8,22u8,33u8,22u8, - 20u8,61u8,100u8,80u8,56u8,23u8,113u8,199u8,50u8,210u8,63u8,128u8,40u8,240u8,45u8,3u8,135u8,135u8,152u8,91u8, - 9u8,28u8,78u8,217u8,232u8,53u8,38u8,55u8,88u8,160u8,228u8,9u8,231u8,71u8,115u8,228u8,122u8,164u8,144u8,131u8, - 232u8,21u8,243u8,22u8,112u8,120u8,156u8,123u8,165u8,237u8,253u8,131u8,154u8,178u8,247u8,153u8,96u8,33u8,225u8,236u8, - 171u8,16u8,199u8,112u8,71u8,6u8,139u8,177u8,232u8,130u8,71u8,90u8,70u8,146u8,45u8,193u8,175u8,177u8,59u8,251u8, - 66u8,171u8,25u8,141u8,112u8,13u8,246u8,225u8,11u8,76u8,214u8,221u8,136u8,173u8,190u8,153u8,240u8,229u8,117u8,105u8, - 128u8,31u8,229u8,222u8,248u8,179u8,100u8,23u8,178u8,254u8,116u8,186u8,246u8,97u8,66u8,93u8,123u8,68u8,241u8,176u8, - 237u8,84u8,76u8,11u8,248u8,224u8,14u8,162u8,130u8,222u8,165u8,156u8,164u8,113u8,111u8,37u8,38u8,195u8,236u8,33u8, - 40u8,104u8,52u8,202u8,210u8,235u8,29u8,165u8,171u8,130u8,4u8,248u8,236u8,46u8,52u8,64u8,119u8,73u8,217u8,228u8, - 177u8,7u8,147u8,145u8,91u8,61u8,204u8,71u8,178u8,163u8,139u8,184u8,84u8,227u8,236u8,171u8,28u8,55u8,42u8,206u8, - 200u8,137u8,35u8,93u8,44u8,106u8,83u8,230u8,92u8,216u8,25u8,211u8,53u8,191u8,134u8,242u8,67u8,23u8,71u8,196u8, - 225u8,72u8,78u8,134u8,101u8,222u8,218u8,222u8,117u8,135u8,136u8,45u8,243u8,206u8,214u8,174u8,11u8,223u8,251u8,141u8, - 10u8,143u8,122u8,37u8,20u8,132u8,207u8,167u8,178u8,131u8,60u8,145u8,80u8,166u8,14u8,234u8,137u8,131u8,226u8,8u8, - 219u8,32u8,17u8,151u8,39u8,104u8,110u8,41u8,234u8,60u8,144u8,158u8,7u8,214u8,31u8,70u8,212u8,125u8,178u8,1u8, - 253u8,68u8,173u8,196u8,247u8,219u8,137u8,58u8,223u8,147u8,232u8,252u8,184u8,7u8,199u8,185u8,193u8,223u8,138u8,57u8, - 31u8,102u8,64u8,190u8,205u8,41u8,29u8,105u8,71u8,197u8,245u8,82u8,96u8,196u8,247u8,64u8,154u8,222u8,70u8,17u8, - 184u8,46u8,140u8,168u8,76u8,14u8,80u8,225u8,70u8,72u8,162u8,133u8,175u8,67u8,40u8,245u8,251u8,162u8,69u8,65u8, - 129u8,209u8,135u8,255u8,76u8,150u8,131u8,217u8,111u8,252u8,199u8,217u8,111u8,116u8,176u8,31u8,105u8,234u8,205u8,125u8, - 227u8,129u8,184u8,95u8,223u8,145u8,23u8,167u8,112u8,97u8,39u8,206u8,104u8,104u8,35u8,64u8,236u8,177u8,23u8,243u8, - 76u8,10u8,133u8,110u8,231u8,93u8,4u8,65u8,219u8,130u8,164u8,173u8,248u8,185u8,243u8,244u8,25u8,38u8,33u8,240u8, - 226u8,195u8,63u8,254u8,125u8,98u8,124u8,69u8,67u8,200u8,79u8,202u8,178u8,243u8,228u8,194u8,62u8,75u8,103u8,72u8, - 223u8,75u8,126u8,63u8,146u8,148u8,225u8,238u8,26u8,117u8,188u8,85u8,6u8,252u8,34u8,80u8,5u8,229u8,158u8,94u8, - 63u8,159u8,58u8,221u8,62u8,126u8,155u8,3u8,126u8,208u8,114u8,13u8,176u8,161u8,156u8,4u8,201u8,53u8,169u8,69u8, - 124u8,235u8,86u8,179u8,139u8,221u8,155u8,45u8,172u8,52u8,209u8,46u8,63u8,65u8,37u8,105u8,67u8,27u8,3u8,235u8, - 145u8,240u8,74u8,176u8,251u8,22u8,190u8,150u8,50u8,160u8,125u8,48u8,136u8,93u8,132u8,107u8,106u8,68u8,64u8,24u8, - 140u8,237u8,22u8,31u8,133u8,252u8,111u8,30u8,248u8,146u8,207u8,123u8,225u8,86u8,167u8,8u8,150u8,201u8,236u8,80u8, - 69u8,208u8,36u8,28u8,107u8,33u8,52u8,37u8,249u8,210u8,55u8,114u8,64u8,68u8,76u8,128u8,10u8,7u8,161u8,231u8, - 152u8,125u8,222u8,39u8,130u8,134u8,209u8,177u8,253u8,114u8,128u8,144u8,242u8,0u8,254u8,125u8,9u8,169u8,26u8,222u8, - 190u8,149u8,144u8,4u8,1u8,189u8,5u8,52u8,68u8,62u8,138u8,200u8,254u8,29u8,228u8,195u8,152u8,55u8,84u8,62u8, - 113u8,154u8,136u8,147u8,207u8,3u8,196u8,148u8,199u8,247u8,239u8,75u8,76u8,213u8,232u8,119u8,95u8,49u8,93u8,237u8, - 195u8,221u8,150u8,224u8,173u8,57u8,20u8,10u8,219u8,175u8,228u8,82u8,186u8,159u8,233u8,163u8,8u8,248u8,223u8,65u8, - 60u8,140u8,105u8,67u8,197u8,3u8,193u8,107u8,126u8,8u8,147u8,171u8,96u8,126u8,171u8,236u8,135u8,145u8,14u8,217u8, - 23u8,50u8,214u8,254u8,158u8,47u8,52u8,63u8,180u8,17u8,145u8,167u8,1u8,238u8,34u8,159u8,106u8,8u8,253u8,112u8, - 162u8,110u8,179u8,32u8,120u8,146u8,30u8,165u8,1u8,187u8,10u8,188u8,211u8,149u8,211u8,81u8,242u8,191u8,143u8,0u8, - 20u8,41u8,128u8,129u8,76u8,103u8,212u8,15u8,226u8,56u8,43u8,29u8,80u8,28u8,64u8,40u8,152u8,95u8,94u8,110u8, - 105u8,67u8,182u8,200u8,247u8,87u8,214u8,104u8,101u8,122u8,95u8,229u8,78u8,228u8,1u8,203u8,119u8,171u8,197u8,13u8, - 63u8,141u8,147u8,115u8,28u8,254u8,141u8,177u8,203u8,20u8,119u8,123u8,75u8,118u8,40u8,98u8,214u8,243u8,90u8,105u8, - 37u8,178u8,41u8,18u8,16u8,173u8,103u8,230u8,234u8,1u8,170u8,218u8,62u8,172u8,188u8,184u8,214u8,62u8,208u8,129u8, - 19u8,82u8,125u8,70u8,60u8,120u8,154u8,181u8,54u8,136u8,148u8,14u8,105u8,142u8,210u8,60u8,131u8,223u8,9u8,85u8, - 117u8,218u8,103u8,73u8,110u8,66u8,58u8,5u8,84u8,36u8,132u8,84u8,120u8,140u8,96u8,220u8,9u8,195u8,102u8,220u8, - 54u8,124u8,203u8,41u8,25u8,85u8,146u8,167u8,28u8,186u8,109u8,176u8,230u8,65u8,5u8,245u8,65u8,143u8,114u8,36u8, - 188u8,205u8,52u8,101u8,51u8,147u8,115u8,173u8,141u8,23u8,109u8,199u8,175u8,251u8,72u8,239u8,64u8,218u8,191u8,138u8, - 206u8,161u8,179u8,8u8,125u8,149u8,165u8,186u8,200u8,247u8,74u8,59u8,247u8,229u8,133u8,42u8,57u8,251u8,0u8,0u8, - 170u8,246u8,117u8,216u8,96u8,249u8,157u8,75u8,118u8,173u8,0u8,44u8,82u8,158u8,175u8,225u8,219u8,162u8,21u8,165u8, - 49u8,63u8,250u8,25u8,83u8,178u8,224u8,119u8,163u8,144u8,239u8,218u8,155u8,53u8,96u8,240u8,167u8,55u8,218u8,134u8, - 70u8,251u8,69u8,138u8,121u8,17u8,188u8,170u8,192u8,15u8,154u8,129u8,150u8,126u8,198u8,179u8,209u8,251u8,140u8,175u8, - 170u8,60u8,228u8,43u8,226u8,66u8,213u8,147u8,239u8,153u8,54u8,42u8,171u8,23u8,224u8,189u8,252u8,44u8,101u8,9u8, - 16u8,145u8,119u8,195u8,248u8,208u8,251u8,125u8,182u8,107u8,94u8,249u8,250u8,243u8,197u8,251u8,183u8,175u8,222u8,254u8, - 254u8,172u8,150u8,99u8,196u8,51u8,110u8,120u8,147u8,28u8,15u8,186u8,97u8,78u8,37u8,191u8,207u8,245u8,51u8,152u8, - 244u8,90u8,70u8,9u8,205u8,221u8,21u8,124u8,44u8,111u8,198u8,37u8,206u8,182u8,167u8,150u8,126u8,134u8,108u8,210u8, - 79u8,141u8,28u8,226u8,207u8,202u8,180u8,225u8,79u8,53u8,94u8,15u8,179u8,210u8,50u8,106u8,10u8,51u8,220u8,153u8, - 152u8,233u8,103u8,232u8,90u8,131u8,215u8,16u8,167u8,203u8,131u8,225u8,93u8,198u8,164u8,61u8,112u8,204u8,7u8,224u8, - 161u8,232u8,30u8,35u8,244u8,137u8,133u8,214u8,40u8,234u8,49u8,106u8,159u8,176u8,219u8,240u8,81u8,235u8,27u8,209u8, - 91u8,14u8,115u8,48u8,185u8,51u8,124u8,200u8,170u8,185u8,108u8,46u8,52u8,96u8,106u8,166u8,183u8,196u8,182u8,106u8, - 37u8,239u8,117u8,232u8,170u8,125u8,188u8,215u8,161u8,171u8,150u8,177u8,223u8,40u8,229u8,44u8,253u8,64u8,89u8,129u8, - 160u8,90u8,130u8,151u8,153u8,75u8,17u8,146u8,49u8,165u8,139u8,219u8,31u8,254u8,156u8,103u8,214u8,27u8,215u8,233u8, - 117u8,243u8,142u8,33u8,173u8,2u8,53u8,140u8,248u8,156u8,20u8,211u8,93u8,160u8,113u8,38u8,93u8,164u8,206u8,163u8, - 70u8,101u8,33u8,176u8,199u8,154u8,81u8,169u8,13u8,241u8,243u8,235u8,182u8,210u8,16u8,244u8,190u8,74u8,67u8,220u8, - 10u8,75u8,179u8,137u8,37u8,176u8,180u8,137u8,101u8,114u8,111u8,88u8,94u8,225u8,74u8,38u8,178u8,25u8,66u8,160u8, - 252u8,102u8,58u8,43u8,222u8,244u8,81u8,194u8,231u8,99u8,155u8,100u8,109u8,234u8,144u8,208u8,182u8,108u8,39u8,182u8, - 61u8,221u8,247u8,109u8,39u8,48u8,172u8,208u8,118u8,109u8,35u8,140u8,76u8,47u8,118u8,76u8,199u8,36u8,88u8,28u8, - 77u8,135u8,127u8,71u8,182u8,105u8,219u8,177u8,109u8,7u8,137u8,77u8,124u8,47u8,136u8,131u8,196u8,212u8,109u8,9u8, - 155u8,143u8,60u8,244u8,119u8,174u8,25u8,79u8,62u8,194u8,159u8,174u8,239u8,4u8,129u8,229u8,153u8,158u8,239u8,91u8, - 186u8,23u8,120u8,102u8,96u8,184u8,46u8,140u8,111u8,26u8,150u8,97u8,152u8,186u8,171u8,187u8,150u8,238u8,187u8,190u8, - 175u8,91u8,174u8,229u8,155u8,158u8,105u8,89u8,158u8,31u8,56u8,134u8,231u8,194u8,39u8,142u8,165u8,91u8,134u8,237u8, - 186u8,70u8,224u8,58u8,30u8,64u8,205u8,65u8,232u8,215u8,198u8,243u8,23u8,207u8,13u8,239u8,229u8,243u8,75u8,253u8, - 50u8,184u8,112u8,158u8,61u8,123u8,17u8,152u8,182u8,119u8,233u8,184u8,207u8,158u8,233u8,134u8,107u8,217u8,222u8,179u8, - 103u8,207u8,46u8,44u8,227u8,242u8,197u8,11u8,231u8,34u8,120u8,246u8,44u8,112u8,95u8,56u8,250u8,179u8,231u8,47u8, - 188u8,11u8,235u8,101u8,224u8,154u8,23u8,250u8,203u8,10u8,79u8,94u8,189u8,253u8,83u8,27u8,95u8,140u8,40u8,142u8, - 12u8,47u8,137u8,168u8,78u8,3u8,226u8,132u8,97u8,140u8,112u8,168u8,227u8,134u8,33u8,135u8,19u8,134u8,33u8,177u8, - 12u8,26u8,199u8,14u8,9u8,194u8,48u8,112u8,99u8,71u8,15u8,163u8,216u8,35u8,86u8,2u8,112u8,136u8,158u8,116u8, - 75u8,233u8,47u8,172u8,40u8,71u8,0u8,52u8,27u8,102u8,96u8,6u8,150u8,1u8,156u8,112u8,61u8,199u8,243u8,116u8, - 211u8,242u8,125u8,199u8,241u8,109u8,215u8,178u8,124u8,215u8,114u8,28u8,71u8,247u8,140u8,192u8,183u8,3u8,199u8,197u8, - 194u8,116u8,129u8,229u8,216u8,158u8,99u8,219u8,134u8,31u8,24u8,158u8,239u8,249u8,186u8,29u8,0u8,99u8,197u8,165u8, - 106u8,78u8,213u8,95u8,218u8,40u8,10u8,116u8,15u8,6u8,77u8,168u8,17u8,217u8,161u8,235u8,18u8,155u8,152u8,88u8, - 234u8,34u8,246u8,92u8,207u8,7u8,80u8,17u8,240u8,60u8,214u8,99u8,234u8,216u8,14u8,168u8,66u8,18u8,129u8,42u8, - 250u8,20u8,212u8,50u8,178u8,140u8,196u8,2u8,250u8,117u8,71u8,150u8,180u8,246u8,35u8,35u8,193u8,113u8,3u8,93u8, - 183u8,29u8,27u8,68u8,232u8,89u8,129u8,109u8,131u8,228u8,76u8,211u8,183u8,108u8,223u8,13u8,2u8,16u8,160u8,9u8, - 99u8,129u8,152u8,225u8,95u8,14u8,106u8,150u8,238u8,122u8,182u8,133u8,56u8,59u8,64u8,152u8,11u8,42u8,1u8,12u8, - 48u8,13u8,95u8,215u8,3u8,208u8,17u8,189u8,34u8,152u8,15u8,175u8,222u8,92u8,94u8,77u8,91u8,73u8,1u8,108u8, - 45u8,207u8,38u8,192u8,5u8,59u8,113u8,97u8,12u8,157u8,16u8,98u8,2u8,101u8,190u8,75u8,220u8,36u8,48u8,65u8, - 104u8,132u8,120u8,14u8,40u8,48u8,192u8,240u8,141u8,200u8,12u8,156u8,36u8,210u8,125u8,195u8,11u8,8u8,162u8,169u8, - 71u8,21u8,82u8,30u8,179u8,194u8,128u8,46u8,144u8,244u8,177u8,12u8,39u8,72u8,216u8,188u8,191u8,124u8,241u8,199u8, - 231u8,151u8,47u8,166u8,31u8,249u8,148u8,54u8,167u8,31u8,222u8,77u8,161u8,189u8,192u8,177u8,117u8,98u8,197u8,126u8, - 64u8,66u8,203u8,15u8,99u8,211u8,11u8,116u8,208u8,29u8,164u8,58u8,160u8,49u8,96u8,65u8,96u8,254u8,187u8,118u8, - 20u8,233u8,186u8,19u8,90u8,102u8,28u8,194u8,44u8,9u8,146u8,192u8,136u8,28u8,211u8,72u8,34u8,199u8,181u8,137u8, - 3u8,95u8,249u8,18u8,142u8,120u8,237u8,232u8,76u8,27u8,94u8,194u8,176u8,218u8,63u8,76u8,103u8,98u8,4u8,23u8, - 11u8,244u8,84u8,191u8,196u8,13u8,255u8,104u8,132u8,77u8,126u8,171u8,45u8,32u8,94u8,144u8,206u8,32u8,106u8,49u8, - 130u8,166u8,227u8,177u8,130u8,21u8,38u8,99u8,0u8,175u8,209u8,99u8,180u8,207u8,32u8,16u8,176u8,239u8,199u8,32u8, - 116u8,131u8,70u8,177u8,235u8,233u8,96u8,230u8,98u8,207u8,74u8,108u8,39u8,164u8,73u8,228u8,38u8,52u8,233u8,248u8, - 85u8,206u8,32u8,14u8,250u8,237u8,187u8,183u8,211u8,231u8,23u8,240u8,231u8,43u8,128u8,54u8,189u8,120u8,253u8,26u8, - 87u8,128u8,171u8,6u8,208u8,151u8,119u8,252u8,85u8,5u8,122u8,209u8,74u8,27u8,1u8,179u8,224u8,249u8,36u8,208u8, - 19u8,23u8,20u8,48u8,6u8,109u8,196u8,169u8,155u8,248u8,36u8,246u8,97u8,58u8,198u8,160u8,125u8,62u8,33u8,70u8, - 232u8,251u8,84u8,183u8,245u8,216u8,112u8,252u8,128u8,122u8,161u8,151u8,152u8,150u8,231u8,210u8,68u8,15u8,36u8,225u8, - 254u8,158u8,95u8,135u8,230u8,135u8,219u8,27u8,181u8,201u8,62u8,167u8,228u8,44u8,111u8,249u8,233u8,211u8,167u8,252u8, - 199u8,252u8,148u8,10u8,198u8,128u8,240u8,222u8,26u8,172u8,72u8,176u8,17u8,224u8,97u8,139u8,51u8,88u8,72u8,8u8, - 56u8,255u8,249u8,137u8,22u8,209u8,156u8,55u8,59u8,221u8,111u8,32u8,244u8,66u8,71u8,225u8,81u8,8u8,90u8,190u8, - 254u8,254u8,168u8,218u8,40u8,63u8,24u8,195u8,29u8,247u8,179u8,51u8,230u8,226u8,97u8,207u8,17u8,239u8,94u8,109u8, - 189u8,217u8,130u8,107u8,178u8,88u8,125u8,63u8,58u8,202u8,23u8,200u8,127u8,159u8,93u8,255u8,207u8,215u8,35u8,216u8, - 72u8,156u8,22u8,209u8,160u8,113u8,217u8,39u8,39u8,64u8,84u8,239u8,105u8,157u8,57u8,97u8,18u8,7u8,30u8,129u8, - 101u8,210u8,39u8,186u8,155u8,232u8,134u8,229u8,199u8,186u8,99u8,122u8,52u8,9u8,77u8,159u8,146u8,216u8,9u8,61u8, - 67u8,15u8,66u8,176u8,50u8,182u8,203u8,150u8,171u8,196u8,34u8,196u8,246u8,76u8,226u8,211u8,216u8,166u8,122u8,92u8, - 151u8,33u8,159u8,168u8,173u8,224u8,76u8,18u8,58u8,58u8,181u8,124u8,43u8,246u8,34u8,48u8,37u8,137u8,103u8,199u8, - 142u8,229u8,123u8,22u8,44u8,90u8,150u8,147u8,24u8,190u8,101u8,56u8,176u8,94u8,197u8,134u8,30u8,39u8,97u8,224u8, - 39u8,81u8,68u8,13u8,106u8,186u8,166u8,30u8,193u8,127u8,176u8,90u8,155u8,117u8,112u8,204u8,74u8,180u8,66u8,211u8, - 193u8,216u8,90u8,65u8,28u8,219u8,129u8,65u8,29u8,47u8,114u8,208u8,36u8,26u8,122u8,100u8,5u8,36u8,48u8,98u8, - 151u8,58u8,186u8,25u8,129u8,217u8,136u8,18u8,157u8,130u8,221u8,32u8,212u8,54u8,188u8,56u8,48u8,146u8,196u8,140u8, - 66u8,48u8,222u8,84u8,247u8,228u8,242u8,85u8,185u8,122u8,130u8,233u8,42u8,47u8,69u8,76u8,180u8,188u8,30u8,28u8, - 250u8,0u8,96u8,207u8,210u8,132u8,21u8,158u8,33u8,26u8,150u8,41u8,83u8,42u8,25u8,222u8,166u8,218u8,70u8,79u8, - 40u8,47u8,105u8,119u8,186u8,101u8,87u8,28u8,227u8,245u8,89u8,89u8,219u8,171u8,208u8,188u8,82u8,77u8,16u8,74u8, - 99u8,83u8,58u8,42u8,184u8,13u8,248u8,48u8,116u8,78u8,165u8,155u8,114u8,227u8,211u8,253u8,234u8,203u8,150u8,108u8, - 64u8,39u8,78u8,203u8,15u8,159u8,86u8,134u8,46u8,117u8,106u8,78u8,175u8,75u8,133u8,194u8,230u8,42u8,157u8,170u8, - 43u8,149u8,12u8,191u8,165u8,52u8,20u8,174u8,213u8,38u8,44u8,231u8,174u8,73u8,65u8,146u8,6u8,33u8,212u8,11u8, - 146u8,144u8,80u8,234u8,71u8,70u8,28u8,192u8,114u8,21u8,234u8,49u8,184u8,59u8,73u8,64u8,108u8,199u8,11u8,168u8, - 11u8,43u8,84u8,0u8,2u8,2u8,5u8,240u8,125u8,66u8,75u8,255u8,171u8,144u8,183u8,186u8,0u8,149u8,239u8,37u8, - 52u8,38u8,70u8,16u8,196u8,158u8,25u8,130u8,114u8,81u8,59u8,113u8,66u8,51u8,6u8,71u8,11u8,214u8,71u8,221u8, - 1u8,201u8,235u8,70u8,16u8,185u8,145u8,19u8,128u8,95u8,21u8,133u8,158u8,78u8,41u8,192u8,8u8,8u8,9u8,2u8, - 55u8,241u8,112u8,181u8,149u8,167u8,138u8,26u8,72u8,66u8,244u8,208u8,114u8,77u8,59u8,132u8,197u8,45u8,114u8,205u8, - 4u8,236u8,126u8,172u8,195u8,36u8,1u8,5u8,139u8,34u8,207u8,5u8,133u8,182u8,72u8,136u8,154u8,68u8,45u8,35u8, - 164u8,161u8,25u8,194u8,2u8,228u8,185u8,224u8,82u8,57u8,9u8,204u8,36u8,86u8,229u8,74u8,161u8,185u8,106u8,88u8, - 104u8,190u8,45u8,207u8,1u8,215u8,199u8,9u8,131u8,36u8,1u8,101u8,52u8,19u8,31u8,60u8,17u8,139u8,198u8,46u8, - 76u8,32u8,199u8,132u8,233u8,2u8,62u8,98u8,164u8,19u8,157u8,192u8,34u8,231u8,7u8,177u8,5u8,238u8,146u8,231u8, - 196u8,17u8,248u8,184u8,186u8,35u8,85u8,212u8,106u8,51u8,234u8,160u8,201u8,191u8,191u8,188u8,250u8,48u8,189u8,124u8, - 193u8,148u8,115u8,218u8,180u8,238u8,137u8,159u8,220u8,225u8,151u8,151u8,28u8,61u8,109u8,160u8,208u8,1u8,190u8,125u8, - 163u8,1u8,252u8,180u8,109u8,15u8,28u8,49u8,112u8,66u8,188u8,4u8,60u8,48u8,11u8,102u8,39u8,241u8,67u8,26u8, - 152u8,160u8,77u8,110u8,220u8,141u8,135u8,62u8,20u8,143u8,246u8,98u8,120u8,32u8,224u8,187u8,34u8,34u8,213u8,48u8, - 228u8,229u8,11u8,113u8,247u8,201u8,42u8,26u8,214u8,131u8,194u8,16u8,159u8,26u8,141u8,107u8,167u8,40u8,165u8,194u8, - 43u8,231u8,7u8,235u8,60u8,92u8,20u8,53u8,70u8,138u8,76u8,132u8,124u8,106u8,59u8,79u8,169u8,67u8,60u8,136u8, - 101u8,213u8,143u8,203u8,177u8,198u8,19u8,205u8,144u8,15u8,87u8,35u8,104u8,126u8,51u8,236u8,92u8,93u8,141u8,96u8, - 40u8,168u8,252u8,82u8,90u8,43u8,148u8,74u8,55u8,122u8,205u8,18u8,111u8,252u8,0u8,40u8,239u8,250u8,180u8,157u8, - 45u8,237u8,93u8,37u8,2u8,171u8,253u8,241u8,78u8,197u8,52u8,7u8,221u8,184u8,113u8,124u8,92u8,233u8,215u8,160u8, - 176u8,114u8,175u8,226u8,88u8,92u8,47u8,59u8,46u8,135u8,20u8,100u8,74u8,217u8,151u8,14u8,241u8,139u8,97u8,234u8, - 114u8,71u8,87u8,82u8,170u8,166u8,162u8,192u8,126u8,214u8,73u8,185u8,82u8,104u8,82u8,25u8,154u8,218u8,136u8,228u8, - 22u8,163u8,53u8,151u8,130u8,241u8,97u8,110u8,205u8,114u8,78u8,205u8,36u8,46u8,201u8,205u8,191u8,111u8,182u8,39u8, - 179u8,65u8,28u8,229u8,21u8,92u8,43u8,23u8,193u8,19u8,138u8,69u8,1u8,103u8,253u8,56u8,92u8,180u8,39u8,85u8, - 22u8,29u8,228u8,80u8,243u8,40u8,125u8,238u8,64u8,140u8,85u8,195u8,211u8,188u8,246u8,31u8,249u8,113u8,246u8,208u8, - 194u8,192u8,74u8,13u8,220u8,107u8,105u8,64u8,219u8,20u8,51u8,128u8,149u8,240u8,97u8,12u8,175u8,117u8,205u8,200u8, - 10u8,178u8,92u8,39u8,236u8,98u8,2u8,139u8,200u8,240u8,235u8,21u8,89u8,67u8,120u8,179u8,83u8,113u8,45u8,253u8, - 252u8,92u8,211u8,213u8,242u8,37u8,114u8,27u8,67u8,221u8,102u8,35u8,53u8,49u8,107u8,54u8,163u8,135u8,158u8,228u8, - 104u8,47u8,201u8,63u8,241u8,120u8,197u8,54u8,63u8,203u8,63u8,131u8,93u8,216u8,146u8,149u8,209u8,223u8,175u8,152u8, - 223u8,70u8,227u8,67u8,115u8,187u8,58u8,106u8,101u8,204u8,245u8,126u8,135u8,131u8,2u8,59u8,241u8,104u8,85u8,4u8, - 41u8,12u8,144u8,102u8,127u8,21u8,205u8,243u8,206u8,189u8,39u8,126u8,7u8,143u8,239u8,85u8,79u8,31u8,72u8,21u8, - 111u8,163u8,7u8,93u8,182u8,160u8,104u8,92u8,61u8,86u8,80u8,84u8,125u8,64u8,212u8,152u8,100u8,254u8,119u8,143u8, - 245u8,38u8,180u8,70u8,33u8,62u8,244u8,193u8,103u8,149u8,190u8,72u8,48u8,108u8,223u8,152u8,101u8,109u8,200u8,137u8, - 209u8,219u8,152u8,28u8,2u8,100u8,186u8,226u8,215u8,227u8,68u8,99u8,113u8,44u8,4u8,115u8,254u8,48u8,189u8,85u8, - 119u8,8u8,75u8,44u8,178u8,57u8,43u8,27u8,141u8,101u8,3u8,235u8,69u8,5u8,241u8,44u8,232u8,163u8,217u8,163u8, - 10u8,176u8,156u8,53u8,12u8,207u8,14u8,77u8,104u8,213u8,105u8,214u8,177u8,110u8,84u8,153u8,214u8,246u8,178u8,221u8, - 3u8,109u8,49u8,30u8,25u8,82u8,218u8,226u8,161u8,182u8,181u8,213u8,1u8,169u8,153u8,42u8,62u8,122u8,88u8,189u8, - 106u8,114u8,139u8,149u8,178u8,107u8,244u8,210u8,112u8,23u8,55u8,197u8,11u8,66u8,32u8,141u8,186u8,207u8,166u8,225u8, - 173u8,232u8,145u8,118u8,7u8,125u8,141u8,183u8,56u8,123u8,116u8,46u8,221u8,79u8,63u8,38u8,32u8,166u8,176u8,135u8, - 201u8,228u8,237u8,106u8,122u8,51u8,196u8,218u8,147u8,195u8,214u8,62u8,60u8,60u8,201u8,115u8,150u8,53u8,109u8,126u8, - 189u8,165u8,168u8,75u8,87u8,182u8,179u8,250u8,173u8,13u8,72u8,40u8,239u8,219u8,189u8,62u8,144u8,30u8,235u8,67u8, - 109u8,232u8,176u8,231u8,208u8,225u8,224u8,165u8,39u8,103u8,75u8,19u8,128u8,114u8,21u8,122u8,28u8,14u8,95u8,133u8, - 202u8,211u8,129u8,83u8,125u8,170u8,87u8,230u8,169u8,186u8,85u8,145u8,40u8,211u8,203u8,235u8,88u8,131u8,192u8,24u8, - 195u8,192u8,24u8,183u8,4u8,163u8,79u8,141u8,129u8,212u8,24u8,183u8,1u8,99u8,77u8,189u8,33u8,96u8,64u8,91u8, - 61u8,21u8,24u8,169u8,178u8,125u8,59u8,172u8,50u8,151u8,76u8,33u8,97u8,77u8,167u8,100u8,58u8,35u8,27u8,150u8, - 128u8,135u8,228u8,39u8,255u8,40u8,44u8,62u8,170u8,89u8,220u8,136u8,93u8,189u8,210u8,152u8,13u8,106u8,220u8,50u8, - 159u8,179u8,162u8,203u8,217u8,154u8,189u8,193u8,179u8,214u8,68u8,241u8,200u8,242u8,220u8,230u8,78u8,99u8,13u8,175u8, - 216u8,179u8,23u8,11u8,114u8,3u8,10u8,151u8,229u8,199u8,48u8,216u8,1u8,66u8,158u8,82u8,197u8,67u8,24u8,229u8, - 217u8,4u8,82u8,212u8,8u8,43u8,54u8,108u8,232u8,214u8,72u8,168u8,151u8,179u8,251u8,203u8,60u8,133u8,217u8,60u8, - 138u8,180u8,159u8,64u8,216u8,181u8,213u8,28u8,123u8,78u8,129u8,124u8,132u8,125u8,199u8,221u8,78u8,101u8,92u8,196u8, - 38u8,194u8,32u8,189u8,122u8,249u8,123u8,240u8,85u8,234u8,87u8,38u8,145u8,240u8,215u8,42u8,145u8,111u8,179u8,176u8, - 63u8,228u8,194u8,125u8,171u8,197u8,84u8,158u8,40u8,61u8,214u8,85u8,244u8,66u8,164u8,30u8,229u8,63u8,195u8,246u8, - 1u8,84u8,171u8,110u8,159u8,113u8,134u8,56u8,21u8,61u8,154u8,230u8,104u8,12u8,112u8,234u8,231u8,183u8,115u8,234u8, - 137u8,236u8,36u8,85u8,156u8,250u8,134u8,191u8,36u8,59u8,245u8,101u8,227u8,251u8,116u8,234u8,73u8,187u8,83u8,223u8, - 173u8,7u8,253u8,252u8,123u8,5u8,103u8,251u8,123u8,249u8,45u8,157u8,15u8,175u8,140u8,120u8,254u8,252u8,191u8,190u8, - 254u8,55u8,243u8,245u8,145u8,221u8,178u8,151u8,248u8,95u8,151u8,191u8,151u8,247u8,172u8,246u8,252u8,115u8,81u8,254u8, - 154u8,54u8,0u8,253u8,60u8,255u8,71u8,224u8,250u8,159u8,132u8,143u8,134u8,123u8,255u8,120u8,115u8,161u8,30u8,124u8, - 186u8,167u8,185u8,137u8,67u8,177u8,171u8,21u8,133u8,178u8,34u8,176u8,106u8,228u8,164u8,161u8,81u8,223u8,159u8,243u8, - 62u8,167u8,121u8,153u8,204u8,158u8,75u8,8u8,235u8,212u8,111u8,197u8,145u8,54u8,201u8,245u8,162u8,252u8,44u8,176u8, - 115u8,96u8,180u8,214u8,193u8,100u8,52u8,26u8,35u8,171u8,146u8,30u8,185u8,62u8,107u8,57u8,181u8,79u8,155u8,188u8, - 155u8,194u8,114u8,34u8,243u8,175u8,186u8,102u8,41u8,208u8,43u8,39u8,73u8,209u8,191u8,147u8,151u8,21u8,114u8,138u8, - 46u8,10u8,30u8,116u8,233u8,82u8,181u8,72u8,187u8,74u8,159u8,242u8,43u8,52u8,226u8,198u8,79u8,145u8,83u8,255u8, - 90u8,171u8,7u8,82u8,212u8,34u8,236u8,72u8,124u8,20u8,214u8,184u8,18u8,178u8,46u8,154u8,139u8,232u8,92u8,183u8, - 220u8,242u8,49u8,208u8,20u8,86u8,144u8,135u8,190u8,131u8,137u8,150u8,170u8,32u8,214u8,105u8,151u8,112u8,237u8,63u8, - 155u8,174u8,143u8,66u8,234u8,196u8,174u8,225u8,196u8,126u8,232u8,39u8,144u8,169u8,134u8,140u8,99u8,20u8,71u8,46u8, - 53u8,252u8,192u8,9u8,253u8,208u8,141u8,45u8,195u8,132u8,212u8,179u8,19u8,27u8,73u8,18u8,234u8,166u8,19u8,248u8, - 144u8,21u8,198u8,3u8,78u8,46u8,177u8,252u8,72u8,119u8,200u8,209u8,88u8,21u8,16u8,238u8,41u8,4u8,82u8,228u8, - 196u8,110u8,101u8,0u8,234u8,11u8,113u8,13u8,234u8,51u8,5u8,212u8,1u8,50u8,146u8,120u8,157u8,59u8,41u8,66u8, - 232u8,225u8,1u8,193u8,253u8,230u8,111u8,249u8,128u8,211u8,132u8,164u8,11u8,176u8,158u8,53u8,129u8,54u8,206u8,242u8, - 147u8,16u8,159u8,151u8,225u8,181u8,179u8,196u8,231u8,213u8,28u8,93u8,163u8,100u8,152u8,56u8,209u8,255u8,55u8,89u8, - 121u8,181u8,95u8,38u8,249u8,73u8,255u8,191u8,253u8,242u8,96u8,232u8,241u8,139u8,6u8,61u8,177u8,147u8,48u8,106u8, - 87u8,10u8,237u8,225u8,144u8,37u8,139u8,197u8,45u8,48u8,125u8,8u8,124u8,138u8,237u8,244u8,20u8,31u8,104u8,187u8, - 163u8,116u8,219u8,121u8,57u8,105u8,159u8,5u8,90u8,23u8,93u8,237u8,248u8,99u8,57u8,59u8,124u8,137u8,179u8,234u8, - 106u8,227u8,81u8,116u8,141u8,127u8,133u8,166u8,167u8,153u8,99u8,199u8,99u8,103u8,2u8,219u8,202u8,46u8,66u8,126u8, - 179u8,163u8,248u8,226u8,151u8,154u8,13u8,201u8,43u8,33u8,168u8,135u8,104u8,39u8,190u8,117u8,192u8,194u8,153u8,109u8, - 178u8,58u8,175u8,192u8,118u8,156u8,223u8,250u8,105u8,93u8,3u8,190u8,69u8,194u8,154u8,35u8,10u8,216u8,228u8,112u8, - 123u8,174u8,17u8,13u8,177u8,229u8,15u8,168u8,42u8,196u8,38u8,30u8,68u8,189u8,187u8,216u8,190u8,157u8,60u8,21u8, - 205u8,158u8,125u8,43u8,177u8,43u8,151u8,132u8,102u8,5u8,137u8,106u8,65u8,177u8,81u8,62u8,46u8,198u8,148u8,171u8, - 116u8,28u8,183u8,242u8,178u8,123u8,28u8,227u8,91u8,170u8,207u8,146u8,172u8,110u8,26u8,62u8,70u8,183u8,24u8,91u8, - 42u8,208u8,133u8,71u8,198u8,81u8,141u8,206u8,214u8,150u8,102u8,239u8,150u8,86u8,239u8,150u8,118u8,239u8,150u8,206u8, - 81u8,135u8,10u8,119u8,206u8,14u8,245u8,163u8,29u8,45u8,148u8,171u8,26u8,154u8,125u8,27u8,90u8,125u8,27u8,218u8, - 125u8,27u8,118u8,82u8,125u8,123u8,235u8,119u8,125u8,20u8,217u8,36u8,240u8,147u8,144u8,186u8,97u8,172u8,39u8,150u8, - 225u8,16u8,61u8,50u8,28u8,221u8,119u8,124u8,66u8,35u8,31u8,126u8,8u8,169u8,21u8,120u8,182u8,109u8,233u8,129u8, - 69u8,3u8,199u8,161u8,73u8,224u8,155u8,212u8,12u8,130u8,200u8,176u8,192u8,253u8,52u8,253u8,163u8,186u8,143u8,55u8, - 108u8,54u8,247u8,247u8,249u8,228u8,109u8,239u8,193u8,153u8,162u8,102u8,99u8,125u8,166u8,44u8,51u8,118u8,176u8,226u8, - 232u8,195u8,26u8,176u8,102u8,151u8,41u8,201u8,6u8,159u8,161u8,74u8,49u8,178u8,78u8,182u8,209u8,60u8,221u8,1u8, - 116u8,246u8,188u8,54u8,4u8,212u8,97u8,155u8,125u8,131u8,175u8,64u8,172u8,216u8,253u8,86u8,12u8,42u8,66u8,80u8, - 102u8,153u8,242u8,144u8,35u8,222u8,79u8,58u8,122u8,122u8,47u8,190u8,62u8,33u8,36u8,48u8,104u8,104u8,91u8,212u8, - 161u8,102u8,98u8,152u8,73u8,18u8,5u8,120u8,249u8,133u8,26u8,118u8,24u8,217u8,142u8,159u8,196u8,161u8,225u8,121u8, - 102u8,104u8,154u8,150u8,25u8,234u8,126u8,224u8,121u8,148u8,186u8,6u8,37u8,65u8,226u8,219u8,212u8,50u8,142u8,198u8, - 131u8,216u8,170u8,230u8,20u8,112u8,102u8,124u8,59u8,86u8,55u8,30u8,93u8,169u8,51u8,157u8,223u8,166u8,100u8,207u8, - 147u8,222u8,157u8,216u8,123u8,58u8,240u8,123u8,71u8,145u8,217u8,196u8,167u8,182u8,25u8,36u8,129u8,238u8,218u8,158u8, - 239u8,58u8,54u8,32u8,27u8,123u8,4u8,246u8,103u8,58u8,137u8,93u8,152u8,76u8,120u8,125u8,40u8,176u8,109u8,18u8, - 185u8,94u8,98u8,249u8,212u8,212u8,99u8,63u8,33u8,94u8,64u8,237u8,208u8,139u8,61u8,131u8,54u8,183u8,103u8,67u8, - 118u8,92u8,234u8,103u8,110u8,224u8,227u8,241u8,144u8,109u8,239u8,144u8,67u8,138u8,98u8,78u8,195u8,10u8,153u8,70u8, - 211u8,207u8,41u8,9u8,211u8,5u8,139u8,106u8,40u8,188u8,24u8,254u8,78u8,6u8,158u8,98u8,229u8,183u8,186u8,247u8, - 126u8,133u8,78u8,44u8,251u8,210u8,216u8,18u8,150u8,87u8,34u8,85u8,155u8,194u8,106u8,117u8,173u8,99u8,69u8,17u8, - 82u8,115u8,239u8,99u8,204u8,5u8,198u8,86u8,68u8,53u8,213u8,88u8,137u8,203u8,92u8,67u8,224u8,184u8,246u8,8u8, - 223u8,85u8,30u8,10u8,201u8,48u8,253u8,193u8,160u8,176u8,92u8,167u8,201u8,110u8,69u8,119u8,3u8,99u8,245u8,89u8, - 140u8,177u8,246u8,91u8,113u8,95u8,242u8,92u8,252u8,157u8,95u8,60u8,234u8,128u8,41u8,149u8,139u8,57u8,174u8,84u8, - 244u8,43u8,172u8,53u8,175u8,48u8,135u8,255u8,174u8,137u8,75u8,122u8,55u8,92u8,251u8,218u8,173u8,65u8,79u8,4u8, - 166u8,153u8,22u8,211u8,252u8,69u8,92u8,126u8,77u8,162u8,82u8,198u8,180u8,82u8,3u8,185u8,188u8,16u8,90u8,150u8, - 251u8,173u8,107u8,34u8,99u8,83u8,173u8,84u8,70u8,67u8,29u8,215u8,107u8,8u8,223u8,127u8,169u8,21u8,13u8,110u8, - 59u8,160u8,204u8,106u8,35u8,30u8,171u8,15u8,198u8,97u8,169u8,191u8,163u8,177u8,66u8,8u8,150u8,201u8,31u8,106u8, - 22u8,53u8,46u8,183u8,180u8,68u8,190u8,231u8,137u8,232u8,118u8,128u8,119u8,188u8,10u8,172u8,66u8,183u8,122u8,221u8, - 54u8,127u8,138u8,113u8,191u8,18u8,183u8,234u8,239u8,200u8,35u8,67u8,191u8,219u8,127u8,74u8,148u8,159u8,55u8,208u8, - 101u8,119u8,130u8,149u8,10u8,62u8,128u8,193u8,146u8,10u8,247u8,103u8,84u8,113u8,99u8,241u8,182u8,60u8,122u8,247u8, - 254u8,197u8,229u8,251u8,233u8,229u8,235u8,215u8,195u8,96u8,62u8,54u8,238u8,4u8,181u8,188u8,202u8,61u8,16u8,172u8, - 121u8,31u8,96u8,193u8,182u8,247u8,7u8,59u8,79u8,103u8,144u8,202u8,75u8,217u8,35u8,53u8,25u8,173u8,62u8,156u8, - 85u8,45u8,142u8,131u8,45u8,177u8,97u8,225u8,248u8,227u8,97u8,164u8,7u8,251u8,31u8,44u8,241u8,47u8,79u8,15u8, - 99u8,51u8,157u8,211u8,107u8,237u8,238u8,149u8,65u8,125u8,253u8,168u8,185u8,10u8,182u8,144u8,127u8,222u8,142u8,137u8, - 58u8,222u8,63u8,68u8,132u8,202u8,161u8,199u8,195u8,124u8,6u8,190u8,168u8,84u8,45u8,179u8,206u8,18u8,135u8,109u8, - 43u8,84u8,94u8,75u8,178u8,186u8,44u8,181u8, - ]; - let chunk3 = vector[ - 100u8,99u8,234u8,125u8,20u8,222u8,129u8,14u8,222u8,129u8,66u8,3u8,117u8,204u8,53u8,25u8,125u8,145u8,224u8, - 107u8,33u8,226u8,205u8,222u8,32u8,105u8,14u8,247u8,115u8,10u8,73u8,83u8,82u8,187u8,128u8,30u8,222u8,176u8,11u8, - 161u8,184u8,226u8,241u8,151u8,242u8,240u8,142u8,250u8,17u8,111u8,115u8,84u8,91u8,152u8,242u8,75u8,161u8,45u8,251u8, - 224u8,235u8,163u8,152u8,146u8,56u8,164u8,52u8,57u8,106u8,120u8,2u8,209u8,92u8,60u8,13u8,48u8,193u8,180u8,237u8, - 86u8,188u8,74u8,7u8,9u8,156u8,69u8,58u8,75u8,49u8,166u8,180u8,217u8,174u8,67u8,225u8,180u8,105u8,35u8,94u8, - 219u8,205u8,120u8,98u8,254u8,3u8,103u8,249u8,152u8,63u8,118u8,199u8,211u8,189u8,217u8,28u8,92u8,225u8,162u8,72u8, - 40u8,56u8,19u8,26u8,208u8,125u8,152u8,69u8,117u8,190u8,180u8,119u8,96u8,202u8,150u8,117u8,241u8,81u8,188u8,104u8, - 124u8,3u8,242u8,97u8,37u8,121u8,234u8,44u8,197u8,122u8,60u8,101u8,250u8,19u8,119u8,105u8,51u8,96u8,28u8,34u8, - 210u8,87u8,148u8,108u8,103u8,218u8,112u8,121u8,250u8,42u8,88u8,101u8,144u8,210u8,87u8,18u8,74u8,58u8,108u8,110u8, - 48u8,165u8,170u8,78u8,13u8,131u8,37u8,204u8,15u8,114u8,176u8,234u8,160u8,245u8,224u8,185u8,98u8,94u8,24u8,234u8, - 121u8,97u8,224u8,188u8,208u8,123u8,98u8,32u8,200u8,254u8,213u8,204u8,11u8,185u8,83u8,186u8,250u8,92u8,86u8,232u8, - 19u8,133u8,109u8,143u8,179u8,135u8,152u8,60u8,153u8,246u8,133u8,136u8,41u8,35u8,222u8,243u8,227u8,207u8,249u8,65u8, - 142u8,225u8,179u8,116u8,90u8,232u8,173u8,114u8,50u8,41u8,189u8,22u8,64u8,87u8,149u8,49u8,22u8,36u8,181u8,134u8, - 124u8,176u8,91u8,203u8,116u8,202u8,68u8,85u8,46u8,94u8,61u8,68u8,61u8,139u8,186u8,39u8,187u8,44u8,247u8,66u8, - 253u8,17u8,98u8,63u8,13u8,148u8,123u8,66u8,175u8,250u8,156u8,235u8,49u8,93u8,218u8,34u8,59u8,249u8,110u8,8u8, - 182u8,17u8,176u8,187u8,77u8,147u8,52u8,202u8,95u8,145u8,229u8,38u8,173u8,56u8,19u8,1u8,108u8,128u8,32u8,14u8, - 138u8,39u8,73u8,233u8,34u8,174u8,170u8,216u8,110u8,91u8,187u8,247u8,153u8,167u8,236u8,74u8,202u8,42u8,129u8,96u8, - 136u8,28u8,209u8,85u8,44u8,94u8,167u8,216u8,97u8,177u8,206u8,251u8,10u8,43u8,157u8,202u8,154u8,124u8,8u8,230u8, - 159u8,217u8,49u8,77u8,81u8,231u8,119u8,182u8,37u8,203u8,172u8,124u8,100u8,21u8,43u8,16u8,189u8,33u8,43u8,80u8, - 209u8,29u8,89u8,105u8,31u8,96u8,126u8,101u8,209u8,54u8,221u8,136u8,162u8,109u8,162u8,174u8,206u8,114u8,157u8,237u8, - 216u8,25u8,84u8,186u8,164u8,67u8,0u8,175u8,8u8,167u8,136u8,61u8,79u8,3u8,115u8,127u8,173u8,109u8,200u8,22u8, - 107u8,169u8,45u8,202u8,97u8,145u8,48u8,220u8,8u8,67u8,168u8,98u8,191u8,32u8,218u8,102u8,177u8,222u8,241u8,242u8, - 62u8,200u8,20u8,186u8,187u8,217u8,32u8,22u8,67u8,128u8,10u8,6u8,241u8,183u8,109u8,180u8,205u8,156u8,172u8,118u8, - 36u8,3u8,146u8,191u8,224u8,196u8,93u8,71u8,17u8,193u8,141u8,55u8,147u8,64u8,254u8,22u8,241u8,126u8,5u8,29u8, - 22u8,233u8,138u8,243u8,40u8,129u8,169u8,194u8,230u8,236u8,16u8,160u8,27u8,186u8,157u8,227u8,19u8,197u8,108u8,74u8, - 87u8,164u8,185u8,37u8,12u8,15u8,246u8,70u8,85u8,72u8,217u8,198u8,54u8,92u8,67u8,199u8,188u8,208u8,28u8,22u8, - 53u8,2u8,214u8,199u8,120u8,225u8,31u8,95u8,222u8,30u8,2u8,149u8,143u8,180u8,64u8,11u8,130u8,179u8,21u8,172u8, - 220u8,110u8,126u8,122u8,84u8,183u8,8u8,29u8,150u8,17u8,198u8,25u8,183u8,196u8,141u8,213u8,154u8,94u8,137u8,75u8, - 155u8,198u8,68u8,243u8,253u8,137u8,102u8,234u8,248u8,135u8,99u8,78u8,52u8,23u8,79u8,93u8,153u8,240u8,131u8,105u8, - 160u8,75u8,12u8,31u8,85u8,58u8,24u8,142u8,61u8,209u8,44u8,31u8,191u8,129u8,38u8,216u8,214u8,197u8,223u8,30u8, - 12u8,195u8,154u8,235u8,181u8,230u8,1u8,142u8,132u8,80u8,60u8,15u8,127u8,128u8,198u8,46u8,126u8,130u8,157u8,77u8, - 24u8,201u8,176u8,253u8,106u8,7u8,211u8,10u8,38u8,154u8,141u8,227u8,123u8,56u8,48u8,142u8,233u8,67u8,99u8,55u8, - 64u8,40u8,248u8,177u8,161u8,12u8,151u8,23u8,214u8,168u8,168u8,179u8,94u8,198u8,192u8,140u8,113u8,95u8,211u8,35u8, - 150u8,143u8,154u8,213u8,129u8,205u8,41u8,62u8,243u8,206u8,191u8,108u8,241u8,65u8,148u8,238u8,118u8,125u8,81u8,82u8, - 249u8,16u8,50u8,160u8,119u8,43u8,250u8,40u8,43u8,138u8,191u8,167u8,89u8,197u8,82u8,47u8,184u8,229u8,86u8,172u8, - 117u8,178u8,151u8,208u8,247u8,138u8,54u8,180u8,86u8,173u8,60u8,2u8,64u8,235u8,202u8,131u8,221u8,14u8,46u8,1u8, - 138u8,177u8,75u8,219u8,45u8,60u8,4u8,30u8,8u8,253u8,88u8,37u8,87u8,62u8,211u8,150u8,99u8,116u8,221u8,140u8, - 35u8,126u8,108u8,57u8,208u8,114u8,173u8,116u8,5u8,174u8,123u8,243u8,228u8,186u8,101u8,57u8,190u8,62u8,180u8,30u8, - 95u8,243u8,5u8,185u8,222u8,103u8,42u8,133u8,157u8,27u8,36u8,148u8,213u8,178u8,148u8,199u8,116u8,107u8,97u8,188u8, - 107u8,190u8,148u8,86u8,70u8,29u8,15u8,209u8,236u8,250u8,1u8,64u8,16u8,200u8,201u8,232u8,228u8,35u8,115u8,233u8, - 62u8,14u8,97u8,120u8,181u8,45u8,142u8,91u8,173u8,143u8,92u8,225u8,119u8,209u8,70u8,213u8,14u8,255u8,26u8,247u8, - 32u8,29u8,233u8,206u8,199u8,24u8,230u8,73u8,72u8,7u8,233u8,234u8,217u8,138u8,33u8,106u8,53u8,141u8,214u8,155u8, - 27u8,220u8,240u8,203u8,15u8,79u8,53u8,32u8,112u8,77u8,80u8,62u8,210u8,167u8,34u8,8u8,199u8,172u8,121u8,84u8, - 3u8,198u8,60u8,52u8,228u8,129u8,14u8,77u8,72u8,173u8,176u8,199u8,42u8,108u8,123u8,240u8,94u8,113u8,49u8,31u8, - 11u8,157u8,25u8,131u8,21u8,174u8,7u8,233u8,178u8,207u8,121u8,93u8,15u8,108u8,183u8,153u8,162u8,234u8,155u8,150u8, - 204u8,77u8,130u8,164u8,69u8,101u8,3u8,83u8,221u8,157u8,220u8,52u8,241u8,252u8,75u8,171u8,202u8,236u8,82u8,240u8, - 77u8,167u8,55u8,42u8,210u8,170u8,229u8,216u8,122u8,144u8,88u8,39u8,239u8,134u8,203u8,68u8,128u8,80u8,208u8,119u8, - 129u8,53u8,140u8,134u8,140u8,218u8,113u8,160u8,233u8,184u8,245u8,224u8,135u8,34u8,101u8,80u8,47u8,15u8,117u8,40u8, - 111u8,160u8,80u8,154u8,123u8,152u8,176u8,15u8,39u8,168u8,250u8,84u8,173u8,95u8,25u8,231u8,210u8,233u8,107u8,211u8, - 170u8,2u8,236u8,201u8,34u8,197u8,37u8,235u8,139u8,56u8,78u8,153u8,14u8,243u8,0u8,127u8,118u8,198u8,226u8,186u8, - 39u8,120u8,85u8,8u8,166u8,27u8,143u8,164u8,203u8,123u8,125u8,230u8,34u8,46u8,22u8,211u8,101u8,186u8,218u8,179u8, - 37u8,187u8,73u8,124u8,37u8,247u8,211u8,59u8,90u8,194u8,206u8,71u8,87u8,70u8,238u8,49u8,25u8,115u8,52u8,205u8, - 123u8,78u8,32u8,202u8,56u8,201u8,56u8,40u8,18u8,95u8,234u8,212u8,219u8,5u8,32u8,246u8,108u8,8u8,148u8,251u8, - 155u8,69u8,149u8,170u8,103u8,67u8,39u8,145u8,226u8,106u8,78u8,254u8,184u8,69u8,69u8,73u8,88u8,82u8,49u8,215u8, - 15u8,57u8,211u8,114u8,111u8,250u8,113u8,155u8,36u8,100u8,5u8,176u8,66u8,42u8,166u8,192u8,218u8,184u8,39u8,117u8, - 169u8,197u8,45u8,24u8,142u8,48u8,90u8,47u8,197u8,61u8,1u8,108u8,206u8,181u8,147u8,255u8,24u8,19u8,115u8,125u8, - 230u8,250u8,171u8,100u8,93u8,79u8,157u8,81u8,63u8,150u8,88u8,211u8,34u8,70u8,227u8,99u8,70u8,179u8,46u8,254u8, - 54u8,181u8,145u8,120u8,165u8,143u8,197u8,201u8,154u8,111u8,130u8,246u8,169u8,77u8,162u8,6u8,46u8,39u8,150u8,158u8, - 222u8,183u8,109u8,104u8,157u8,244u8,239u8,229u8,71u8,11u8,49u8,18u8,96u8,192u8,22u8,5u8,83u8,48u8,162u8,4u8, - 253u8,13u8,70u8,148u8,50u8,237u8,135u8,81u8,81u8,250u8,83u8,126u8,54u8,165u8,122u8,32u8,38u8,157u8,205u8,208u8, - 209u8,184u8,19u8,23u8,212u8,149u8,58u8,199u8,135u8,93u8,66u8,1u8,189u8,105u8,93u8,186u8,202u8,142u8,14u8,53u8, - 53u8,135u8,79u8,6u8,229u8,222u8,86u8,181u8,50u8,172u8,176u8,65u8,101u8,192u8,174u8,182u8,40u8,179u8,11u8,90u8, - 38u8,62u8,130u8,130u8,56u8,242u8,117u8,242u8,186u8,53u8,110u8,119u8,56u8,214u8,210u8,50u8,226u8,164u8,112u8,33u8, - 250u8,4u8,108u8,250u8,12u8,82u8,59u8,49u8,199u8,36u8,218u8,87u8,1u8,212u8,175u8,88u8,182u8,64u8,29u8,183u8, - 159u8,152u8,109u8,145u8,119u8,119u8,197u8,221u8,30u8,147u8,70u8,144u8,83u8,63u8,222u8,218u8,87u8,87u8,202u8,122u8, - 147u8,85u8,245u8,96u8,241u8,116u8,220u8,252u8,183u8,31u8,169u8,232u8,124u8,217u8,64u8,94u8,79u8,248u8,99u8,66u8, - 205u8,89u8,125u8,122u8,122u8,26u8,98u8,220u8,60u8,129u8,8u8,34u8,196u8,101u8,247u8,11u8,45u8,229u8,79u8,62u8, - 100u8,84u8,228u8,196u8,242u8,140u8,52u8,132u8,18u8,33u8,86u8,197u8,83u8,211u8,43u8,57u8,121u8,173u8,74u8,11u8, - 119u8,63u8,122u8,188u8,32u8,25u8,127u8,240u8,247u8,208u8,163u8,199u8,98u8,52u8,241u8,82u8,149u8,37u8,111u8,9u8, - 127u8,148u8,7u8,129u8,244u8,116u8,251u8,238u8,82u8,205u8,29u8,121u8,104u8,213u8,70u8,8u8,159u8,203u8,96u8,149u8, - 76u8,49u8,75u8,144u8,241u8,52u8,65u8,77u8,94u8,90u8,188u8,166u8,200u8,139u8,93u8,254u8,248u8,120u8,203u8,77u8, - 236u8,252u8,97u8,66u8,110u8,47u8,179u8,225u8,252u8,226u8,208u8,206u8,27u8,47u8,32u8,213u8,226u8,102u8,183u8,100u8, - 178u8,120u8,111u8,125u8,32u8,119u8,135u8,50u8,247u8,192u8,20u8,18u8,61u8,26u8,249u8,68u8,217u8,232u8,126u8,253u8, - 238u8,255u8,1u8,147u8,119u8,177u8,87u8,17u8,172u8,0u8,0u8,0u8,0u8,9u8,115u8,101u8,99u8,112u8,50u8,53u8, - 54u8,107u8,49u8,142u8,12u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,87u8,107u8,111u8,211u8, - 72u8,20u8,253u8,222u8,95u8,49u8,116u8,165u8,214u8,150u8,66u8,107u8,199u8,207u8,164u8,176u8,82u8,41u8,217u8,85u8, - 5u8,20u8,212u8,20u8,173u8,88u8,132u8,156u8,241u8,60u8,18u8,43u8,142u8,199u8,242u8,216u8,77u8,195u8,170u8,255u8, - 125u8,239u8,216u8,142u8,237u8,60u8,74u8,65u8,172u8,22u8,136u8,20u8,69u8,206u8,204u8,220u8,123u8,238u8,153u8,115u8, - 31u8,62u8,61u8,61u8,69u8,55u8,179u8,72u8,162u8,133u8,160u8,69u8,204u8,80u8,180u8,72u8,99u8,182u8,96u8,73u8, - 46u8,209u8,232u8,226u8,229u8,248u8,28u8,201u8,104u8,154u8,224u8,188u8,200u8,152u8,68u8,33u8,150u8,140u8,34u8,145u8, - 160u8,124u8,198u8,80u8,154u8,69u8,11u8,246u8,84u8,100u8,148u8,101u8,72u8,50u8,146u8,246u8,29u8,119u8,110u8,34u8, - 22u8,199u8,105u8,30u8,17u8,68u8,138u8,236u8,150u8,33u8,45u8,58u8,97u8,39u8,61u8,68u8,4u8,199u8,36u8,23u8, - 25u8,2u8,55u8,166u8,126u8,114u8,112u8,80u8,251u8,194u8,105u8,46u8,100u8,32u8,115u8,58u8,28u8,182u8,22u8,254u8, - 57u8,64u8,240u8,41u8,36u8,67u8,229u8,130u8,0u8,115u8,34u8,25u8,14u8,223u8,150u8,191u8,103u8,7u8,229u8,234u8, - 41u8,128u8,62u8,79u8,16u8,203u8,50u8,48u8,42u8,8u8,56u8,203u8,0u8,216u8,114u8,22u8,129u8,81u8,202u8,36u8, - 203u8,34u8,28u8,71u8,159u8,163u8,100u8,218u8,67u8,28u8,118u8,176u8,59u8,172u8,162u8,66u8,180u8,96u8,40u8,23u8, - 104u8,153u8,137u8,100u8,138u8,162u8,36u8,45u8,114u8,136u8,238u8,51u8,59u8,41u8,109u8,18u8,145u8,200u8,28u8,141u8, - 130u8,151u8,163u8,241u8,232u8,250u8,242u8,252u8,245u8,229u8,223u8,163u8,33u8,42u8,92u8,27u8,61u8,71u8,230u8,89u8, - 233u8,179u8,226u8,137u8,8u8,202u8,208u8,162u8,128u8,189u8,33u8,43u8,137u8,144u8,120u8,193u8,122u8,40u8,226u8,136u8, - 221u8,2u8,21u8,25u8,3u8,174u8,18u8,64u8,195u8,51u8,177u8,40u8,215u8,129u8,189u8,8u8,184u8,184u8,86u8,71u8, - 26u8,118u8,177u8,138u8,230u8,164u8,141u8,230u8,70u8,25u8,2u8,44u8,72u8,112u8,132u8,91u8,62u8,159u8,86u8,148u8, - 87u8,55u8,145u8,22u8,97u8,12u8,212u8,206u8,217u8,10u8,220u8,37u8,40u8,92u8,229u8,76u8,118u8,177u8,95u8,159u8, - 255u8,21u8,188u8,123u8,255u8,226u8,245u8,229u8,69u8,240u8,106u8,244u8,33u8,184u8,122u8,255u8,38u8,120u8,241u8,225u8, - 102u8,52u8,94u8,135u8,225u8,218u8,103u8,181u8,191u8,106u8,255u8,197u8,219u8,55u8,239u8,174u8,71u8,227u8,241u8,232u8, - 101u8,247u8,216u8,184u8,19u8,184u8,101u8,157u8,125u8,27u8,196u8,70u8,44u8,251u8,17u8,142u8,47u8,255u8,188u8,58u8, - 191u8,121u8,127u8,61u8,218u8,15u8,174u8,189u8,91u8,120u8,124u8,170u8,78u8,239u8,68u8,94u8,89u8,147u8,121u8,86u8, - 144u8,188u8,90u8,188u8,198u8,203u8,119u8,229u8,242u8,43u8,182u8,66u8,51u8,172u8,46u8,40u8,5u8,130u8,104u8,38u8, - 210u8,30u8,108u8,20u8,25u8,171u8,37u8,165u8,62u8,37u8,162u8,33u8,186u8,101u8,74u8,142u8,207u8,10u8,255u8,247u8, - 114u8,229u8,254u8,97u8,207u8,77u8,64u8,187u8,142u8,199u8,235u8,165u8,239u8,247u8,122u8,161u8,216u8,81u8,150u8,37u8, - 194u8,201u8,182u8,245u8,218u8,103u8,163u8,167u8,41u8,168u8,41u8,1u8,152u8,93u8,122u8,107u8,130u8,120u8,1u8,73u8, - 65u8,168u8,196u8,65u8,131u8,59u8,80u8,231u8,130u8,114u8,171u8,182u8,131u8,67u8,31u8,110u8,59u8,107u8,65u8,99u8, - 9u8,121u8,148u8,63u8,209u8,202u8,44u8,172u8,142u8,12u8,135u8,49u8,75u8,166u8,249u8,76u8,59u8,42u8,45u8,233u8, - 232u8,249u8,243u8,125u8,87u8,218u8,171u8,50u8,183u8,204u8,206u8,225u8,48u8,74u8,110u8,33u8,25u8,105u8,128u8,179u8, - 105u8,161u8,148u8,175u8,109u8,164u8,152u8,174u8,159u8,53u8,30u8,183u8,161u8,84u8,1u8,2u8,79u8,143u8,179u8,181u8, - 33u8,130u8,106u8,177u8,87u8,19u8,133u8,155u8,27u8,205u8,240u8,18u8,210u8,51u8,133u8,74u8,214u8,38u8,224u8,94u8, - 242u8,96u8,99u8,80u8,253u8,25u8,128u8,228u8,42u8,6u8,93u8,251u8,49u8,18u8,55u8,48u8,124u8,51u8,143u8,15u8, - 37u8,239u8,119u8,145u8,185u8,9u8,233u8,33u8,62u8,199u8,117u8,189u8,100u8,95u8,228u8,83u8,85u8,206u8,154u8,73u8, - 249u8,117u8,188u8,229u8,162u8,166u8,44u8,157u8,15u8,209u8,209u8,142u8,93u8,189u8,75u8,98u8,135u8,176u8,116u8,126u8, - 82u8,30u8,251u8,10u8,152u8,59u8,73u8,242u8,56u8,198u8,54u8,49u8,26u8,120u8,240u8,215u8,26u8,95u8,99u8,240u8, - 33u8,112u8,176u8,119u8,63u8,186u8,107u8,70u8,4u8,212u8,127u8,89u8,181u8,4u8,48u8,195u8,178u8,99u8,89u8,74u8, - 78u8,171u8,17u8,233u8,157u8,50u8,86u8,101u8,115u8,167u8,138u8,214u8,229u8,102u8,210u8,192u8,155u8,212u8,242u8,85u8, - 230u8,38u8,89u8,101u8,124u8,21u8,68u8,116u8,2u8,193u8,211u8,214u8,9u8,109u8,0u8,76u8,22u8,76u8,74u8,60u8, - 133u8,131u8,154u8,213u8,47u8,175u8,26u8,209u8,104u8,202u8,100u8,174u8,159u8,172u8,247u8,52u8,123u8,175u8,68u8,174u8, - 122u8,23u8,206u8,21u8,147u8,181u8,166u8,186u8,181u8,27u8,122u8,38u8,110u8,159u8,215u8,96u8,105u8,196u8,57u8,203u8, - 64u8,117u8,168u8,118u8,213u8,67u8,203u8,40u8,142u8,33u8,167u8,100u8,17u8,231u8,170u8,224u8,43u8,88u8,107u8,172u8, - 101u8,155u8,72u8,26u8,151u8,81u8,66u8,4u8,116u8,105u8,184u8,163u8,78u8,49u8,175u8,58u8,106u8,115u8,2u8,199u8, - 83u8,145u8,69u8,249u8,108u8,129u8,8u8,224u8,18u8,73u8,188u8,82u8,61u8,182u8,80u8,237u8,5u8,46u8,150u8,204u8, - 24u8,153u8,163u8,18u8,107u8,148u8,175u8,234u8,46u8,212u8,96u8,132u8,254u8,219u8,101u8,190u8,241u8,219u8,225u8,92u8, - 83u8,19u8,8u8,20u8,13u8,40u8,215u8,51u8,93u8,205u8,34u8,243u8,68u8,44u8,161u8,75u8,49u8,152u8,17u8,216u8, - 12u8,104u8,125u8,72u8,214u8,21u8,62u8,173u8,17u8,65u8,29u8,126u8,87u8,34u8,189u8,102u8,177u8,115u8,87u8,208u8, - 219u8,252u8,94u8,87u8,58u8,21u8,214u8,29u8,177u8,85u8,123u8,64u8,114u8,213u8,144u8,243u8,108u8,39u8,85u8,186u8, - 10u8,140u8,89u8,142u8,32u8,163u8,160u8,46u8,20u8,132u8,0u8,16u8,168u8,30u8,155u8,56u8,131u8,40u8,201u8,89u8, - 150u8,224u8,88u8,107u8,174u8,169u8,131u8,169u8,215u8,105u8,105u8,85u8,245u8,105u8,107u8,6u8,112u8,168u8,53u8,86u8, - 91u8,143u8,85u8,227u8,235u8,204u8,98u8,82u8,44u8,152u8,246u8,21u8,165u8,50u8,157u8,235u8,122u8,99u8,229u8,30u8, - 102u8,67u8,201u8,190u8,100u8,54u8,17u8,9u8,219u8,19u8,187u8,214u8,177u8,177u8,153u8,117u8,245u8,15u8,186u8,170u8, - 38u8,44u8,184u8,49u8,162u8,44u8,173u8,47u8,191u8,155u8,156u8,106u8,40u8,147u8,104u8,162u8,181u8,88u8,123u8,8u8, - 74u8,6u8,211u8,39u8,42u8,234u8,110u8,218u8,1u8,79u8,17u8,143u8,160u8,200u8,192u8,156u8,219u8,230u8,84u8,145u8, - 168u8,57u8,183u8,163u8,111u8,208u8,228u8,164u8,53u8,54u8,105u8,92u8,169u8,236u8,204u8,26u8,119u8,31u8,63u8,193u8, - 4u8,138u8,33u8,110u8,240u8,35u8,224u8,116u8,182u8,140u8,100u8,61u8,74u8,36u8,13u8,232u8,135u8,174u8,239u8,191u8, - 211u8,219u8,214u8,228u8,1u8,74u8,211u8,58u8,166u8,80u8,40u8,68u8,172u8,159u8,109u8,179u8,122u8,3u8,181u8,99u8, - 147u8,202u8,223u8,62u8,194u8,173u8,230u8,159u8,218u8,137u8,16u8,158u8,20u8,77u8,24u8,213u8,21u8,100u8,171u8,154u8, - 181u8,217u8,73u8,50u8,134u8,115u8,160u8,172u8,144u8,48u8,148u8,35u8,57u8,7u8,205u8,222u8,29u8,26u8,102u8,223u8, - 178u8,29u8,215u8,243u8,7u8,56u8,36u8,148u8,241u8,111u8,125u8,62u8,44u8,129u8,40u8,2u8,21u8,172u8,96u8,51u8, - 89u8,187u8,2u8,110u8,94u8,38u8,84u8,230u8,215u8,113u8,174u8,83u8,41u8,157u8,111u8,231u8,143u8,182u8,161u8,80u8, - 117u8,4u8,20u8,63u8,195u8,253u8,0u8,66u8,211u8,194u8,67u8,229u8,171u8,122u8,115u8,105u8,227u8,61u8,212u8,123u8, - 27u8,135u8,140u8,205u8,199u8,163u8,253u8,147u8,205u8,16u8,56u8,224u8,30u8,166u8,3u8,203u8,165u8,216u8,176u8,248u8, - 192u8,246u8,137u8,105u8,19u8,199u8,238u8,27u8,125u8,131u8,89u8,196u8,225u8,54u8,51u8,250u8,24u8,99u8,66u8,77u8, - 222u8,55u8,236u8,190u8,71u8,76u8,19u8,99u8,151u8,245u8,121u8,200u8,125u8,207u8,115u8,109u8,245u8,117u8,195u8,16u8, - 27u8,204u8,196u8,150u8,199u8,7u8,204u8,35u8,158u8,231u8,17u8,187u8,111u8,97u8,147u8,246u8,125u8,123u8,16u8,98u8, - 204u8,169u8,199u8,185u8,139u8,7u8,3u8,203u8,240u8,77u8,27u8,219u8,22u8,177u8,184u8,111u8,80u8,103u8,64u8,67u8, - 199u8,229u8,135u8,232u8,190u8,197u8,218u8,41u8,5u8,27u8,83u8,203u8,58u8,65u8,35u8,120u8,89u8,83u8,169u8,127u8, - 4u8,137u8,221u8,131u8,55u8,185u8,71u8,182u8,179u8,187u8,60u8,131u8,247u8,62u8,237u8,104u8,81u8,40u8,158u8,245u8, - 170u8,222u8,168u8,97u8,231u8,238u8,208u8,6u8,224u8,152u8,57u8,134u8,237u8,89u8,166u8,27u8,2u8,96u8,131u8,26u8, - 134u8,239u8,18u8,31u8,19u8,70u8,92u8,223u8,227u8,134u8,17u8,154u8,132u8,14u8,168u8,73u8,137u8,107u8,217u8,220u8, - 37u8,161u8,229u8,192u8,162u8,129u8,7u8,216u8,231u8,240u8,97u8,158u8,23u8,218u8,148u8,26u8,216u8,14u8,121u8,56u8, - 112u8,124u8,199u8,228u8,86u8,232u8,89u8,142u8,67u8,60u8,223u8,164u8,212u8,53u8,184u8,111u8,155u8,62u8,7u8,131u8, - 174u8,67u8,77u8,123u8,96u8,120u8,152u8,115u8,219u8,35u8,3u8,195u8,194u8,142u8,51u8,56u8,172u8,240u8,55u8,1u8, - 128u8,154u8,255u8,136u8,163u8,52u8,5u8,141u8,134u8,208u8,35u8,206u8,80u8,119u8,180u8,192u8,43u8,89u8,41u8,252u8, - 127u8,87u8,142u8,42u8,110u8,111u8,111u8,224u8,125u8,236u8,28u8,29u8,123u8,199u8,104u8,9u8,47u8,26u8,188u8,134u8, - 9u8,29u8,17u8,154u8,228u8,49u8,63u8,70u8,80u8,86u8,216u8,143u8,211u8,27u8,255u8,217u8,245u8,246u8,228u8,23u8, - 211u8,91u8,8u8,98u8,130u8,138u8,191u8,158u8,203u8,126u8,170u8,106u8,197u8,127u8,45u8,245u8,168u8,137u8,98u8,75u8, - 61u8,247u8,7u8,247u8,7u8,255u8,2u8,242u8,122u8,217u8,179u8,244u8,18u8,0u8,0u8,0u8,0u8,11u8,115u8,109u8, - 97u8,114u8,116u8,95u8,116u8,97u8,98u8,108u8,101u8,159u8,28u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8, - 255u8,237u8,91u8,123u8,115u8,219u8,54u8,18u8,255u8,223u8,159u8,2u8,157u8,155u8,250u8,36u8,159u8,34u8,63u8,106u8, - 167u8,87u8,219u8,242u8,140u8,107u8,171u8,215u8,76u8,210u8,164u8,19u8,187u8,110u8,218u8,206u8,29u8,69u8,145u8,144u8, - 132u8,9u8,95u8,37u8,65u8,59u8,106u8,234u8,239u8,126u8,187u8,11u8,144u8,4u8,73u8,80u8,150u8,242u8,104u8,122u8, - 143u8,76u8,166u8,177u8,73u8,96u8,119u8,177u8,248u8,237u8,111u8,23u8,11u8,118u8,119u8,119u8,151u8,157u8,179u8,44u8, - 116u8,83u8,201u8,164u8,59u8,13u8,56u8,19u8,97u8,18u8,240u8,144u8,71u8,210u8,149u8,34u8,142u8,216u8,212u8,205u8, - 184u8,207u8,224u8,135u8,64u8,68u8,220u8,77u8,217u8,194u8,205u8,22u8,34u8,154u8,15u8,89u8,111u8,33u8,101u8,146u8, - 29u8,239u8,238u8,242u8,104u8,120u8,39u8,94u8,139u8,132u8,251u8,194u8,29u8,198u8,233u8,124u8,23u8,127u8,219u8,125u8, - 70u8,195u8,29u8,61u8,188u8,191u8,181u8,11u8,138u8,46u8,226u8,48u8,113u8,83u8,206u8,100u8,204u8,174u8,81u8,215u8, - 128u8,9u8,201u8,242u8,140u8,103u8,44u8,224u8,89u8,198u8,50u8,25u8,167u8,238u8,156u8,179u8,44u8,136u8,101u8,198u8, - 166u8,185u8,68u8,101u8,108u8,33u8,230u8,11u8,158u8,50u8,111u8,225u8,70u8,30u8,103u8,241u8,140u8,121u8,113u8,16u8, - 136u8,12u8,108u8,27u8,48u8,151u8,201u8,212u8,245u8,249u8,163u8,120u8,54u8,99u8,83u8,46u8,239u8,56u8,143u8,88u8, - 150u8,184u8,48u8,206u8,141u8,124u8,38u8,69u8,200u8,135u8,77u8,197u8,177u8,68u8,105u8,254u8,50u8,114u8,67u8,225u8, - 21u8,171u8,105u8,44u8,122u8,208u8,88u8,44u8,200u8,12u8,4u8,152u8,20u8,71u8,28u8,204u8,242u8,94u8,115u8,137u8, - 154u8,65u8,58u8,19u8,81u8,38u8,185u8,235u8,163u8,89u8,126u8,156u8,79u8,3u8,28u8,172u8,70u8,100u8,164u8,248u8, - 110u8,1u8,22u8,241u8,55u8,9u8,152u8,131u8,175u8,64u8,191u8,123u8,27u8,11u8,159u8,229u8,17u8,60u8,228u8,158u8, - 4u8,215u8,206u8,97u8,141u8,94u8,156u8,73u8,101u8,233u8,21u8,238u8,4u8,57u8,71u8,57u8,102u8,230u8,130u8,2u8, - 101u8,8u8,155u8,229u8,145u8,71u8,123u8,114u8,37u8,146u8,111u8,241u8,129u8,161u8,221u8,75u8,151u8,137u8,140u8,231u8, - 169u8,155u8,44u8,132u8,231u8,6u8,193u8,146u8,101u8,220u8,203u8,97u8,201u8,181u8,153u8,224u8,104u8,241u8,26u8,28u8, - 188u8,112u8,191u8,120u8,116u8,112u8,244u8,152u8,101u8,2u8,124u8,74u8,122u8,97u8,35u8,100u8,28u8,240u8,212u8,149u8, - 60u8,171u8,60u8,156u8,13u8,183u8,194u8,216u8,207u8,193u8,24u8,23u8,164u8,103u8,78u8,38u8,253u8,227u8,99u8,2u8, - 139u8,163u8,192u8,242u8,118u8,139u8,193u8,31u8,176u8,148u8,209u8,43u8,158u8,166u8,113u8,122u8,82u8,127u8,118u8,11u8, - 203u8,52u8,31u8,26u8,146u8,212u8,143u8,104u8,33u8,72u8,21u8,9u8,253u8,228u8,204u8,210u8,56u8,116u8,110u8,221u8, - 32u8,231u8,214u8,57u8,164u8,215u8,185u8,19u8,114u8,225u8,4u8,60u8,154u8,75u8,152u8,250u8,246u8,138u8,7u8,179u8, - 129u8,130u8,212u8,143u8,240u8,226u8,25u8,61u8,191u8,183u8,79u8,95u8,38u8,220u8,17u8,209u8,44u8,70u8,141u8,191u8, - 113u8,39u8,158u8,161u8,46u8,235u8,208u8,208u8,149u8,139u8,199u8,135u8,248u8,239u8,155u8,147u8,45u8,26u8,128u8,142u8, - 122u8,202u8,151u8,44u8,138u8,37u8,155u8,197u8,57u8,64u8,76u8,68u8,12u8,240u8,100u8,198u8,15u8,13u8,244u8,192u8, - 117u8,146u8,141u8,159u8,191u8,184u8,118u8,190u8,121u8,241u8,195u8,243u8,203u8,99u8,150u8,63u8,62u8,100u8,35u8,182u8, - 127u8,82u8,202u8,185u8,50u8,66u8,206u8,115u8,1u8,179u8,66u8,46u8,89u8,152u8,195u8,180u8,41u8,103u8,129u8,155u8, - 206u8,97u8,215u8,37u8,96u8,158u8,237u8,153u8,18u8,127u8,30u8,191u8,124u8,225u8,92u8,156u8,127u8,127u8,126u8,241u8, - 228u8,250u8,167u8,66u8,234u8,65u8,37u8,245u8,194u8,141u8,208u8,56u8,159u8,103u8,50u8,141u8,209u8,208u8,232u8,17u8, - 15u8,19u8,144u8,140u8,174u8,13u8,221u8,164u8,105u8,221u8,248u8,187u8,239u8,43u8,57u8,95u8,156u8,212u8,86u8,233u8, - 6u8,41u8,128u8,107u8,9u8,240u8,21u8,25u8,192u8,217u8,152u8,121u8,254u8,236u8,229u8,248u8,252u8,242u8,39u8,103u8, - 252u8,234u8,201u8,213u8,117u8,49u8,251u8,176u8,154u8,253u8,36u8,2u8,159u8,2u8,202u8,131u8,24u8,192u8,41u8,23u8, - 41u8,207u8,22u8,113u8,224u8,179u8,132u8,167u8,30u8,132u8,24u8,70u8,129u8,76u8,197u8,28u8,87u8,72u8,129u8,53u8, - 52u8,101u8,63u8,121u8,126u8,115u8,254u8,236u8,201u8,165u8,243u8,236u8,197u8,249u8,165u8,115u8,253u8,237u8,203u8,241u8, - 213u8,183u8,47u8,158u8,93u8,58u8,223u8,143u8,95u8,94u8,140u8,159u8,151u8,202u8,142u8,218u8,202u8,36u8,250u8,76u8, - 22u8,241u8,137u8,155u8,107u8,149u8,123u8,125u8,254u8,242u8,31u8,227u8,107u8,231u8,235u8,31u8,46u8,158u8,194u8,63u8, - 87u8,79u8,126u8,30u8,23u8,50u8,31u8,111u8,44u8,115u8,252u8,234u8,98u8,60u8,190u8,116u8,190u8,59u8,127u8,101u8, - 147u8,247u8,165u8,129u8,26u8,35u8,172u8,193u8,3u8,233u8,18u8,37u8,72u8,23u8,226u8,151u8,77u8,129u8,144u8,8u8, - 67u8,175u8,209u8,229u8,0u8,41u8,130u8,190u8,82u8,3u8,155u8,152u8,123u8,160u8,7u8,103u8,156u8,62u8,29u8,176u8, - 155u8,51u8,98u8,68u8,47u8,78u8,150u8,3u8,230u8,167u8,113u8,50u8,32u8,206u8,44u8,194u8,16u8,255u8,80u8,48u8, - 161u8,1u8,131u8,242u8,17u8,8u8,62u8,102u8,79u8,171u8,223u8,73u8,193u8,49u8,187u8,81u8,79u8,238u8,183u8,76u8, - 85u8,149u8,157u8,134u8,190u8,166u8,14u8,77u8,112u8,199u8,205u8,136u8,59u8,69u8,181u8,76u8,197u8,251u8,169u8,97u8, - 244u8,217u8,89u8,165u8,60u8,202u8,67u8,167u8,156u8,95u8,51u8,19u8,188u8,4u8,47u8,167u8,0u8,10u8,160u8,179u8, - 41u8,210u8,45u8,224u8,36u8,229u8,9u8,160u8,7u8,65u8,99u8,76u8,44u8,167u8,4u8,252u8,150u8,7u8,32u8,230u8, - 239u8,53u8,41u8,50u8,150u8,110u8,96u8,200u8,18u8,146u8,135u8,213u8,28u8,220u8,196u8,182u8,230u8,43u8,196u8,33u8, - 187u8,19u8,65u8,128u8,225u8,167u8,209u8,9u8,228u8,76u8,252u8,173u8,81u8,208u8,64u8,51u8,132u8,190u8,6u8,52u8, - 230u8,44u8,145u8,129u8,173u8,174u8,183u8,40u8,230u8,184u8,62u8,17u8,190u8,203u8,34u8,126u8,167u8,182u8,124u8,88u8, - 153u8,128u8,202u8,28u8,20u8,231u8,148u8,226u8,90u8,171u8,184u8,6u8,72u8,104u8,197u8,104u8,50u8,46u8,4u8,229u8, - 107u8,231u8,15u8,64u8,139u8,128u8,223u8,64u8,45u8,132u8,49u8,40u8,152u8,197u8,96u8,138u8,207u8,50u8,72u8,112u8, - 183u8,60u8,197u8,25u8,126u8,177u8,79u8,64u8,47u8,145u8,10u8,226u8,202u8,4u8,37u8,89u8,59u8,212u8,105u8,248u8, - 228u8,190u8,194u8,237u8,5u8,44u8,74u8,98u8,54u8,101u8,138u8,70u8,12u8,28u8,35u8,5u8,3u8,209u8,204u8,220u8, - 60u8,144u8,8u8,231u8,153u8,152u8,231u8,41u8,165u8,207u8,76u8,233u8,73u8,48u8,31u8,122u8,152u8,123u8,208u8,9u8, - 167u8,79u8,143u8,9u8,184u8,236u8,111u8,132u8,92u8,248u8,135u8,96u8,5u8,248u8,56u8,86u8,63u8,157u8,245u8,250u8, - 199u8,109u8,252u8,85u8,168u8,3u8,25u8,138u8,246u8,149u8,46u8,245u8,190u8,183u8,55u8,96u8,248u8,183u8,191u8,169u8, - 225u8,30u8,48u8,109u8,28u8,146u8,155u8,108u8,182u8,163u8,136u8,9u8,66u8,78u8,68u8,66u8,10u8,55u8,40u8,160u8, - 55u8,57u8,166u8,141u8,49u8,128u8,170u8,125u8,12u8,89u8,89u8,15u8,21u8,191u8,145u8,160u8,33u8,219u8,99u8,33u8, - 119u8,33u8,194u8,243u8,12u8,145u8,80u8,120u8,202u8,8u8,111u8,82u8,98u8,3u8,131u8,214u8,82u8,240u8,165u8,214u8, - 166u8,118u8,60u8,198u8,42u8,72u8,67u8,109u8,160u8,160u8,212u8,198u8,109u8,135u8,242u8,82u8,107u8,211u8,136u8,54u8, - 28u8,180u8,9u8,26u8,129u8,213u8,122u8,17u8,202u8,2u8,74u8,4u8,176u8,173u8,132u8,162u8,92u8,196u8,249u8,124u8, - 65u8,89u8,113u8,158u8,187u8,169u8,27u8,73u8,110u8,154u8,128u8,207u8,51u8,44u8,153u8,128u8,220u8,208u8,210u8,82u8, - 173u8,46u8,194u8,168u8,90u8,113u8,179u8,108u8,14u8,53u8,23u8,128u8,118u8,73u8,108u8,136u8,252u8,152u8,186u8,30u8, - 34u8,203u8,231u8,54u8,60u8,213u8,177u8,240u8,32u8,182u8,44u8,123u8,169u8,16u8,223u8,29u8,141u8,93u8,65u8,178u8, - 26u8,166u8,176u8,16u8,158u8,202u8,207u8,122u8,54u8,177u8,236u8,20u8,74u8,128u8,61u8,192u8,43u8,213u8,73u8,199u8, - 199u8,66u8,165u8,25u8,7u8,244u8,228u8,88u8,124u8,246u8,30u8,200u8,126u8,253u8,254u8,137u8,193u8,125u8,178u8,4u8, - 223u8,136u8,89u8,170u8,34u8,240u8,81u8,207u8,24u8,111u8,25u8,1u8,28u8,213u8,219u8,14u8,243u8,82u8,14u8,133u8, - 146u8,162u8,111u8,40u8,229u8,48u8,108u8,122u8,77u8,141u8,170u8,94u8,25u8,153u8,225u8,84u8,45u8,221u8,200u8,13u8, - 131u8,218u8,195u8,26u8,233u8,239u8,215u8,223u8,105u8,14u8,223u8,171u8,63u8,85u8,206u8,110u8,60u8,212u8,172u8,88u8, - 68u8,146u8,194u8,126u8,147u8,149u8,51u8,246u8,229u8,209u8,231u8,195u8,186u8,48u8,235u8,14u8,139u8,25u8,179u8,111u8, - 210u8,104u8,4u8,124u8,194u8,222u8,130u8,28u8,118u8,207u8,120u8,0u8,69u8,225u8,91u8,171u8,8u8,118u8,95u8,55u8, - 175u8,13u8,151u8,234u8,253u8,253u8,201u8,86u8,199u8,42u8,140u8,60u8,165u8,224u8,89u8,238u8,42u8,44u8,229u8,160u8, - 90u8,8u8,154u8,107u8,1u8,113u8,97u8,109u8,203u8,223u8,173u8,113u8,69u8,153u8,216u8,48u8,7u8,88u8,5u8,54u8, - 209u8,42u8,250u8,140u8,237u8,175u8,39u8,217u8,246u8,244u8,81u8,81u8,236u8,214u8,119u8,1u8,142u8,80u8,122u8,136u8, - 130u8,30u8,1u8,170u8,111u8,181u8,172u8,42u8,167u8,13u8,94u8,191u8,212u8,149u8,173u8,98u8,117u8,26u8,82u8,241u8, - 216u8,249u8,52u8,78u8,209u8,113u8,152u8,243u8,255u8,170u8,136u8,135u8,198u8,181u8,248u8,67u8,215u8,199u8,14u8,189u8, - 213u8,153u8,132u8,100u8,181u8,67u8,187u8,111u8,137u8,109u8,165u8,150u8,210u8,50u8,186u8,127u8,69u8,60u8,151u8,53u8, - 118u8,51u8,144u8,4u8,184u8,109u8,175u8,181u8,11u8,130u8,157u8,234u8,53u8,25u8,33u8,211u8,220u8,131u8,34u8,66u8, - 107u8,139u8,232u8,89u8,226u8,59u8,229u8,33u8,20u8,3u8,134u8,159u8,135u8,101u8,160u8,11u8,211u8,30u8,130u8,23u8, - 216u8,35u8,128u8,56u8,247u8,173u8,91u8,129u8,22u8,155u8,33u8,95u8,49u8,70u8,45u8,180u8,157u8,65u8,17u8,206u8, - 240u8,147u8,10u8,97u8,167u8,139u8,96u8,29u8,59u8,191u8,58u8,16u8,115u8,154u8,206u8,86u8,114u8,87u8,125u8,241u8, - 133u8,167u8,78u8,186u8,224u8,226u8,22u8,39u8,173u8,24u8,15u8,250u8,146u8,67u8,198u8,161u8,50u8,237u8,134u8,10u8, - 220u8,9u8,102u8,141u8,73u8,23u8,74u8,48u8,187u8,168u8,98u8,251u8,70u8,253u8,176u8,22u8,86u8,222u8,115u8,147u8, - 55u8,221u8,206u8,255u8,137u8,221u8,60u8,247u8,125u8,214u8,131u8,227u8,204u8,64u8,21u8,48u8,125u8,150u8,184u8,34u8, - 45u8,206u8,224u8,212u8,232u8,128u8,35u8,46u8,53u8,149u8,66u8,119u8,201u8,230u8,105u8,124u8,103u8,182u8,108u8,128u8, - 22u8,160u8,35u8,146u8,98u8,69u8,69u8,73u8,99u8,230u8,98u8,28u8,65u8,101u8,236u8,65u8,185u8,146u8,145u8,140u8, - 114u8,69u8,21u8,167u8,60u8,143u8,161u8,140u8,212u8,18u8,169u8,146u8,161u8,172u8,131u8,131u8,97u8,122u8,14u8,116u8, - 141u8,245u8,246u8,44u8,136u8,239u8,202u8,130u8,123u8,8u8,199u8,71u8,234u8,203u8,144u8,37u8,119u8,128u8,47u8,31u8, - 16u8,151u8,134u8,69u8,121u8,51u8,49u8,188u8,60u8,161u8,170u8,104u8,66u8,126u8,158u8,84u8,74u8,191u8,1u8,187u8, - 50u8,9u8,175u8,220u8,212u8,55u8,123u8,81u8,112u8,38u8,159u8,199u8,41u8,120u8,46u8,36u8,217u8,66u8,159u8,205u8, - 124u8,96u8,38u8,128u8,248u8,173u8,155u8,10u8,218u8,81u8,108u8,157u8,213u8,181u8,64u8,207u8,11u8,193u8,159u8,67u8, - 214u8,194u8,246u8,2u8,168u8,195u8,78u8,155u8,223u8,160u8,78u8,116u8,209u8,4u8,252u8,59u8,105u8,156u8,253u8,235u8, - 222u8,192u8,202u8,16u8,52u8,135u8,28u8,234u8,62u8,159u8,252u8,18u8,123u8,158u8,139u8,237u8,34u8,170u8,228u8,176u8, - 151u8,5u8,141u8,12u8,40u8,85u8,67u8,60u8,54u8,98u8,123u8,75u8,157u8,162u8,84u8,113u8,90u8,117u8,200u8,204u8, - 195u8,191u8,17u8,117u8,80u8,149u8,212u8,25u8,153u8,0u8,223u8,12u8,181u8,65u8,113u8,172u8,45u8,143u8,179u8,205u8, - 224u8,35u8,127u8,141u8,152u8,165u8,175u8,212u8,219u8,134u8,185u8,77u8,62u8,142u8,124u8,254u8,6u8,134u8,107u8,248u8, - 210u8,175u8,154u8,231u8,105u8,115u8,6u8,237u8,144u8,29u8,144u8,10u8,107u8,73u8,102u8,175u8,200u8,192u8,197u8,0u8, - 73u8,7u8,150u8,99u8,15u8,98u8,212u8,217u8,175u8,21u8,10u8,63u8,114u8,85u8,57u8,99u8,185u8,253u8,72u8,139u8, - 14u8,68u8,8u8,91u8,79u8,27u8,74u8,39u8,25u8,151u8,229u8,9u8,85u8,227u8,212u8,144u8,234u8,65u8,125u8,185u8, - 7u8,5u8,1u8,180u8,142u8,160u8,182u8,136u8,167u8,49u8,108u8,34u8,32u8,26u8,55u8,62u8,138u8,211u8,144u8,118u8, - 136u8,78u8,15u8,195u8,86u8,86u8,43u8,242u8,138u8,50u8,87u8,71u8,97u8,95u8,215u8,172u8,70u8,213u8,154u8,32u8, - 150u8,51u8,220u8,111u8,199u8,231u8,145u8,224u8,126u8,175u8,163u8,17u8,98u8,230u8,152u8,166u8,18u8,48u8,164u8,87u8, - 156u8,31u8,126u8,215u8,61u8,145u8,223u8,27u8,28u8,136u8,254u8,196u8,253u8,55u8,251u8,31u8,35u8,53u8,182u8,78u8, - 119u8,219u8,124u8,136u8,221u8,147u8,207u8,70u8,12u8,119u8,182u8,162u8,188u8,254u8,138u8,204u8,92u8,235u8,97u8,53u8, - 179u8,51u8,150u8,184u8,164u8,21u8,200u8,17u8,55u8,153u8,208u8,166u8,161u8,102u8,50u8,41u8,22u8,101u8,106u8,23u8, - 219u8,236u8,103u8,173u8,204u8,72u8,120u8,6u8,173u8,99u8,56u8,156u8,250u8,14u8,45u8,69u8,15u8,134u8,64u8,122u8, - 211u8,51u8,90u8,146u8,189u8,109u8,14u8,230u8,239u8,55u8,136u8,189u8,91u8,25u8,205u8,223u8,223u8,59u8,56u8,100u8, - 187u8,59u8,108u8,150u8,114u8,192u8,30u8,16u8,5u8,119u8,126u8,205u8,161u8,29u8,194u8,118u8,118u8,217u8,174u8,85u8, - 113u8,93u8,133u8,177u8,180u8,98u8,163u8,146u8,28u8,98u8,103u8,234u8,122u8,175u8,203u8,237u8,226u8,205u8,83u8,133u8, - 174u8,132u8,204u8,95u8,40u8,245u8,212u8,220u8,68u8,25u8,67u8,81u8,174u8,114u8,89u8,159u8,157u8,141u8,10u8,239u8, - 89u8,203u8,107u8,32u8,14u8,60u8,109u8,53u8,93u8,216u8,170u8,35u8,91u8,37u8,100u8,187u8,8u8,240u8,132u8,207u8, - 171u8,142u8,9u8,146u8,55u8,244u8,223u8,203u8,96u8,133u8,62u8,147u8,98u8,117u8,228u8,98u8,245u8,19u8,254u8,141u8, - 176u8,79u8,121u8,23u8,171u8,40u8,195u8,57u8,92u8,93u8,17u8,100u8,216u8,119u8,71u8,129u8,248u8,76u8,83u8,62u8, - 233u8,66u8,246u8,106u8,90u8,183u8,6u8,149u8,53u8,137u8,11u8,15u8,183u8,38u8,13u8,149u8,158u8,53u8,184u8,167u8, - 198u8,18u8,157u8,235u8,129u8,181u8,86u8,115u8,104u8,29u8,113u8,174u8,114u8,87u8,136u8,36u8,157u8,137u8,121u8,36u8, - 102u8,112u8,0u8,135u8,140u8,56u8,21u8,70u8,119u8,136u8,78u8,122u8,177u8,163u8,132u8,140u8,218u8,6u8,253u8,11u8, - 104u8,134u8,157u8,22u8,85u8,12u8,241u8,99u8,11u8,19u8,134u8,177u8,54u8,17u8,181u8,218u8,4u8,239u8,31u8,102u8, - 100u8,215u8,29u8,236u8,188u8,202u8,79u8,1u8,101u8,55u8,180u8,64u8,210u8,253u8,147u8,7u8,80u8,157u8,230u8,97u8, - 66u8,195u8,104u8,64u8,253u8,108u8,84u8,218u8,11u8,146u8,49u8,244u8,90u8,6u8,218u8,10u8,44u8,245u8,170u8,116u8, - 176u8,250u8,109u8,85u8,217u8,4u8,184u8,116u8,222u8,131u8,226u8,11u8,35u8,235u8,44u8,15u8,87u8,83u8,82u8,208u8, - 197u8,78u8,133u8,168u8,1u8,251u8,101u8,111u8,56u8,76u8,250u8,88u8,6u8,44u8,17u8,113u8,168u8,186u8,122u8,151u8, - 12u8,193u8,216u8,168u8,207u8,230u8,49u8,167u8,62u8,41u8,118u8,25u8,213u8,203u8,154u8,185u8,9u8,88u8,89u8,6u8, - 114u8,161u8,165u8,87u8,45u8,2u8,184u8,151u8,91u8,89u8,23u8,249u8,161u8,205u8,188u8,39u8,104u8,237u8,248u8,13u8, - 172u8,192u8,195u8,26u8,8u8,174u8,82u8,8u8,107u8,208u8,239u8,202u8,102u8,75u8,170u8,173u8,161u8,192u8,77u8,45u8, - 253u8,128u8,53u8,243u8,169u8,234u8,147u8,82u8,86u8,69u8,50u8,111u8,130u8,198u8,32u8,246u8,147u8,142u8,136u8,49u8, - 214u8,11u8,245u8,70u8,232u8,164u8,88u8,233u8,100u8,188u8,182u8,228u8,100u8,205u8,174u8,72u8,99u8,239u8,154u8,214u8, - 152u8,79u8,218u8,165u8,234u8,75u8,46u8,243u8,84u8,109u8,104u8,121u8,211u8,87u8,212u8,161u8,20u8,0u8,224u8,184u8, - 153u8,192u8,75u8,74u8,93u8,186u8,86u8,53u8,214u8,215u8,80u8,75u8,81u8,83u8,172u8,184u8,30u8,101u8,190u8,152u8, - 205u8,56u8,21u8,174u8,120u8,27u8,203u8,38u8,132u8,109u8,85u8,51u8,178u8,219u8,76u8,255u8,222u8,43u8,209u8,219u8, - 159u8,32u8,94u8,232u8,226u8,14u8,218u8,193u8,9u8,79u8,27u8,55u8,185u8,178u8,108u8,236u8,149u8,58u8,107u8,166u8, - 121u8,234u8,182u8,212u8,167u8,139u8,154u8,5u8,215u8,79u8,227u8,89u8,39u8,209u8,84u8,244u8,87u8,219u8,238u8,170u8, - 61u8,223u8,110u8,251u8,87u8,23u8,21u8,125u8,117u8,95u8,242u8,214u8,90u8,142u8,81u8,17u8,247u8,185u8,166u8,27u8, - 99u8,133u8,253u8,122u8,14u8,86u8,195u8,79u8,217u8,138u8,3u8,21u8,114u8,140u8,110u8,131u8,87u8,37u8,104u8,253u8, - 208u8,84u8,199u8,152u8,110u8,253u8,88u8,132u8,228u8,81u8,201u8,75u8,93u8,98u8,74u8,155u8,21u8,247u8,116u8,231u8, - 166u8,115u8,239u8,215u8,92u8,164u8,212u8,168u8,22u8,33u8,160u8,142u8,234u8,247u8,148u8,211u8,118u8,123u8,188u8,216u8, - 1u8,85u8,120u8,168u8,4u8,166u8,10u8,116u8,56u8,232u8,96u8,224u8,219u8,122u8,31u8,146u8,106u8,67u8,228u8,254u8, - 88u8,23u8,86u8,112u8,57u8,160u8,166u8,181u8,138u8,109u8,197u8,87u8,198u8,9u8,183u8,74u8,85u8,93u8,21u8,55u8, - 236u8,215u8,246u8,77u8,199u8,118u8,109u8,20u8,237u8,157u8,149u8,249u8,198u8,37u8,117u8,111u8,251u8,129u8,82u8,218u8, - 114u8,24u8,199u8,71u8,32u8,197u8,32u8,140u8,122u8,221u8,107u8,61u8,182u8,19u8,231u8,118u8,208u8,165u8,33u8,73u8, - 91u8,85u8,16u8,78u8,235u8,116u8,14u8,136u8,221u8,86u8,92u8,135u8,101u8,235u8,72u8,149u8,173u8,77u8,193u8,248u8, - 39u8,85u8,44u8,162u8,7u8,147u8,139u8,106u8,99u8,238u8,55u8,57u8,246u8,187u8,116u8,196u8,91u8,217u8,170u8,162u8, - 203u8,234u8,254u8,71u8,195u8,168u8,34u8,69u8,76u8,235u8,80u8,145u8,205u8,224u8,228u8,80u8,191u8,21u8,121u8,55u8, - 240u8,42u8,64u8,104u8,65u8,141u8,155u8,128u8,181u8,0u8,61u8,40u8,172u8,64u8,96u8,55u8,209u8,141u8,123u8,245u8, - 89u8,113u8,89u8,171u8,68u8,13u8,148u8,6u8,66u8,106u8,99u8,207u8,204u8,123u8,150u8,78u8,18u8,209u8,232u8,104u8, - 202u8,90u8,131u8,35u8,216u8,167u8,34u8,8u8,44u8,104u8,172u8,36u8,177u8,234u8,104u8,222u8,215u8,239u8,255u8,100u8, - 100u8,177u8,246u8,249u8,251u8,147u8,144u8,6u8,89u8,247u8,225u8,136u8,3u8,151u8,249u8,39u8,33u8,143u8,247u8,2u8, - 47u8,116u8,212u8,120u8,170u8,142u8,46u8,212u8,243u8,235u8,209u8,184u8,1u8,116u8,110u8,85u8,196u8,77u8,250u8,80u8, - 77u8,165u8,153u8,124u8,119u8,120u8,63u8,64u8,35u8,141u8,206u8,239u8,3u8,61u8,169u8,146u8,80u8,110u8,108u8,65u8, - 176u8,25u8,167u8,96u8,61u8,218u8,24u8,83u8,42u8,232u8,219u8,182u8,202u8,0u8,146u8,158u8,87u8,210u8,75u8,171u8, - 64u8,5u8,47u8,167u8,68u8,188u8,51u8,188u8,4u8,134u8,193u8,147u8,234u8,203u8,20u8,188u8,59u8,127u8,192u8,125u8, - 197u8,216u8,77u8,11u8,136u8,105u8,28u8,7u8,255u8,33u8,237u8,186u8,7u8,107u8,139u8,178u8,163u8,21u8,45u8,63u8, - 92u8,71u8,139u8,15u8,149u8,63u8,116u8,5u8,188u8,189u8,93u8,244u8,184u8,70u8,173u8,30u8,87u8,123u8,99u8,241u8, - 154u8,128u8,161u8,255u8,202u8,61u8,197u8,198u8,70u8,90u8,157u8,72u8,62u8,90u8,182u8,80u8,87u8,20u8,171u8,146u8, - 240u8,3u8,9u8,227u8,255u8,185u8,226u8,143u8,45u8,48u8,81u8,184u8,217u8,232u8,164u8,155u8,28u8,218u8,13u8,167u8, - 108u8,119u8,26u8,122u8,179u8,59u8,55u8,113u8,244u8,61u8,84u8,151u8,242u8,85u8,109u8,193u8,214u8,165u8,176u8,145u8, - 171u8,62u8,85u8,130u8,234u8,76u8,45u8,100u8,16u8,36u8,150u8,117u8,98u8,0u8,5u8,229u8,137u8,143u8,159u8,28u8, - 85u8,1u8,166u8,79u8,204u8,141u8,9u8,152u8,245u8,180u8,108u8,245u8,49u8,242u8,157u8,200u8,120u8,51u8,144u8,242u8, - 4u8,141u8,122u8,223u8,52u8,100u8,187u8,26u8,249u8,0u8,201u8,71u8,93u8,190u8,173u8,46u8,114u8,17u8,88u8,144u8, - 232u8,49u8,116u8,173u8,233u8,168u8,190u8,181u8,59u8,106u8,168u8,241u8,213u8,175u8,177u8,187u8,182u8,164u8,69u8,61u8, - 64u8,140u8,166u8,194u8,201u8,90u8,180u8,24u8,242u8,161u8,106u8,83u8,52u8,191u8,88u8,106u8,113u8,149u8,154u8,223u8, - 232u8,207u8,182u8,122u8,179u8,205u8,230u8,68u8,5u8,230u8,21u8,29u8,31u8,243u8,122u8,81u8,219u8,135u8,193u8,101u8, - 124u8,173u8,96u8,218u8,81u8,53u8,198u8,223u8,221u8,24u8,182u8,131u8,151u8,52u8,208u8,227u8,111u8,55u8,95u8,119u8, - 59u8,47u8,13u8,154u8,43u8,248u8,65u8,1u8,216u8,254u8,73u8,218u8,176u8,13u8,81u8,28u8,237u8,216u8,6u8,175u8, - 117u8,127u8,215u8,245u8,221u8,85u8,127u8,179u8,143u8,169u8,48u8,55u8,90u8,95u8,159u8,177u8,15u8,243u8,161u8,213u8, - 138u8,27u8,138u8,145u8,85u8,243u8,73u8,151u8,95u8,45u8,95u8,217u8,117u8,121u8,181u8,61u8,116u8,45u8,159u8,118u8, - 125u8,177u8,102u8,253u8,134u8,165u8,117u8,135u8,180u8,158u8,199u8,218u8,31u8,80u8,183u8,189u8,101u8,189u8,160u8,106u8, - 63u8,172u8,121u8,234u8,47u8,191u8,72u8,184u8,160u8,250u8,103u8,117u8,147u8,82u8,253u8,111u8,6u8,14u8,190u8,233u8, - 53u8,239u8,73u8,138u8,111u8,209u8,26u8,31u8,186u8,173u8,254u8,240u8,226u8,96u8,111u8,207u8,198u8,113u8,85u8,142u8, - 7u8,14u8,217u8,244u8,123u8,138u8,194u8,165u8,58u8,191u8,111u8,235u8,235u8,45u8,72u8,187u8,7u8,123u8,244u8,113u8, - 170u8,209u8,172u8,220u8,196u8,176u8,29u8,107u8,17u8,130u8,214u8,145u8,57u8,59u8,230u8,23u8,93u8,166u8,29u8,59u8, - 181u8,202u8,85u8,141u8,215u8,19u8,234u8,214u8,60u8,188u8,180u8,134u8,189u8,245u8,15u8,160u8,76u8,150u8,57u8,99u8, - 71u8,150u8,90u8,172u8,191u8,246u8,30u8,104u8,193u8,101u8,98u8,170u8,108u8,111u8,155u8,92u8,12u8,110u8,125u8,16u8, - 243u8,94u8,75u8,181u8,124u8,78u8,213u8,95u8,27u8,160u8,138u8,7u8,186u8,97u8,90u8,251u8,86u8,136u8,250u8,223u8, - 240u8,159u8,51u8,125u8,49u8,102u8,124u8,201u8,218u8,219u8,31u8,168u8,47u8,68u8,247u8,45u8,136u8,222u8,111u8,22u8, - 151u8,234u8,238u8,202u8,138u8,39u8,192u8,222u8,209u8,227u8,46u8,47u8,91u8,110u8,232u8,70u8,8u8,251u8,46u8,71u8, - 215u8,238u8,202u8,70u8,76u8,87u8,222u8,205u8,32u8,121u8,183u8,56u8,42u8,106u8,18u8,81u8,94u8,218u8,153u8,109u8, - 126u8,107u8,201u8,170u8,150u8,109u8,185u8,172u8,107u8,236u8,232u8,253u8,74u8,23u8,174u8,233u8,171u8,142u8,96u8,218u8, - 20u8,93u8,43u8,93u8,127u8,112u8,244u8,229u8,234u8,224u8,41u8,25u8,198u8,184u8,68u8,55u8,104u8,230u8,171u8,175u8, - 234u8,230u8,172u8,32u8,164u8,163u8,199u8,245u8,161u8,26u8,245u8,155u8,226u8,93u8,167u8,41u8,5u8,218u8,236u8,191u8, - 130u8,154u8,59u8,51u8,111u8,205u8,130u8,125u8,203u8,20u8,91u8,17u8,80u8,155u8,116u8,180u8,103u8,39u8,194u8,195u8, - 63u8,116u8,197u8,135u8,235u8,39u8,163u8,195u8,143u8,206u8,208u8,31u8,146u8,157u8,239u8,183u8,254u8,13u8,71u8,132u8, - 217u8,41u8,198u8,58u8,0u8,0u8,0u8,0u8,12u8,115u8,109u8,97u8,114u8,116u8,95u8,118u8,101u8,99u8,116u8,111u8, - 114u8,139u8,27u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,229u8,91u8,123u8,111u8,219u8,56u8,18u8, - 255u8,191u8,159u8,130u8,197u8,225u8,178u8,118u8,235u8,36u8,118u8,154u8,118u8,175u8,73u8,19u8,160u8,143u8,28u8,80u8, - 28u8,182u8,41u8,154u8,116u8,247u8,110u8,139u8,66u8,150u8,45u8,218u8,166u8,35u8,75u8,94u8,189u8,92u8,95u8,145u8, - 239u8,126u8,51u8,36u8,37u8,145u8,20u8,37u8,203u8,222u8,164u8,93u8,244u8,130u8,125u8,164u8,34u8,57u8,28u8,206u8, - 227u8,55u8,15u8,178u8,139u8,208u8,75u8,125u8,74u8,220u8,101u8,18u8,198u8,78u8,156u8,120u8,39u8,39u8,241u8,194u8, - 141u8,18u8,39u8,163u8,227u8,36u8,140u8,200u8,215u8,7u8,4u8,126u8,210u8,152u8,18u8,62u8,70u8,163u8,40u8,140u8, - 78u8,245u8,111u8,98u8,102u8,249u8,81u8,33u8,53u8,98u8,83u8,73u8,232u8,228u8,228u8,235u8,21u8,245u8,39u8,61u8, - 242u8,138u8,77u8,127u8,229u8,95u8,110u8,173u8,43u8,22u8,110u8,50u8,123u8,118u8,140u8,255u8,255u8,98u8,29u8,79u8, - 214u8,75u8,234u8,176u8,96u8,18u8,2u8,159u8,236u8,191u8,212u8,9u8,39u8,78u8,230u8,250u8,6u8,71u8,225u8,50u8, - 97u8,97u8,80u8,108u8,121u8,201u8,255u8,8u8,251u8,241u8,89u8,135u8,135u8,135u8,68u8,112u8,64u8,88u8,224u8,209u8, - 47u8,132u8,197u8,36u8,76u8,19u8,18u8,78u8,200u8,40u8,76u8,3u8,47u8,230u8,179u8,198u8,97u8,16u8,39u8,228u8, - 226u8,237u8,187u8,55u8,23u8,255u8,118u8,46u8,63u8,94u8,59u8,151u8,255u8,116u8,94u8,93u8,126u8,124u8,247u8,230u8, - 234u8,132u8,164u8,207u8,142u8,201u8,25u8,25u8,156u8,22u8,228u8,94u8,187u8,65u8,16u8,38u8,196u8,163u8,113u8,18u8, - 133u8,107u8,226u8,146u8,32u8,12u8,246u8,233u8,98u8,153u8,172u8,137u8,56u8,189u8,74u8,242u8,215u8,139u8,215u8,215u8, - 151u8,31u8,156u8,119u8,151u8,215u8,206u8,197u8,47u8,239u8,175u8,255u8,147u8,211u8,59u8,170u8,208u8,91u8,134u8,75u8, - 50u8,114u8,199u8,55u8,100u8,18u8,133u8,11u8,226u8,6u8,100u8,3u8,73u8,141u8,220u8,147u8,146u8,220u8,40u8,29u8, - 223u8,208u8,196u8,65u8,121u8,145u8,177u8,32u8,61u8,162u8,164u8,175u8,82u8,248u8,253u8,226u8,195u8,165u8,243u8,234u8, - 227u8,235u8,127u8,93u8,92u8,59u8,87u8,111u8,127u8,191u8,200u8,169u8,28u8,43u8,66u8,123u8,73u8,174u8,198u8,174u8, - 239u8,142u8,192u8,90u8,164u8,113u8,176u8,197u8,210u8,167u8,11u8,26u8,36u8,46u8,10u8,24u8,120u8,141u8,169u8,71u8, - 224u8,151u8,4u8,39u8,197u8,61u8,66u8,197u8,104u8,76u8,220u8,136u8,146u8,105u8,20u8,166u8,75u8,24u8,103u8,65u8, - 18u8,74u8,150u8,98u8,178u8,98u8,201u8,140u8,12u8,21u8,6u8,135u8,7u8,197u8,126u8,215u8,51u8,74u8,132u8,38u8, - 201u8,42u8,114u8,151u8,75u8,22u8,76u8,75u8,235u8,33u8,177u8,155u8,209u8,152u8,196u8,75u8,119u8,76u8,129u8,36u8, - 73u8,96u8,242u8,130u8,38u8,174u8,231u8,38u8,46u8,113u8,227u8,56u8,28u8,51u8,55u8,129u8,221u8,248u8,6u8,229u8, - 170u8,213u8,140u8,6u8,68u8,51u8,112u8,22u8,23u8,27u8,198u8,33u8,14u8,249u8,62u8,16u8,115u8,19u8,32u8,234u8, - 179u8,128u8,226u8,180u8,252u8,184u8,32u8,59u8,50u8,11u8,125u8,143u8,136u8,57u8,148u8,224u8,102u8,130u8,95u8,80u8, - 124u8,58u8,78u8,200u8,21u8,82u8,22u8,91u8,189u8,184u8,62u8,39u8,51u8,23u8,56u8,132u8,223u8,169u8,116u8,35u8, - 252u8,41u8,201u8,158u8,72u8,186u8,48u8,179u8,87u8,12u8,75u8,143u8,57u8,145u8,70u8,251u8,162u8,96u8,29u8,102u8, - 41u8,211u8,36u8,149u8,177u8,11u8,231u8,103u8,201u8,186u8,152u8,14u8,90u8,83u8,137u8,149u8,114u8,181u8,204u8,184u8, - 45u8,85u8,251u8,129u8,78u8,83u8,223u8,141u8,114u8,191u8,120u8,249u8,254u8,109u8,57u8,246u8,58u8,162u8,32u8,73u8, - 211u8,252u8,192u8,217u8,80u8,31u8,30u8,157u8,184u8,169u8,159u8,16u8,63u8,156u8,178u8,49u8,1u8,197u8,130u8,11u8, - 176u8,5u8,206u8,31u8,26u8,44u8,14u8,129u8,130u8,167u8,171u8,186u8,7u8,234u8,96u8,227u8,25u8,89u8,184u8,107u8, - 48u8,200u8,98u8,67u8,22u8,184u8,227u8,113u8,26u8,1u8,17u8,33u8,219u8,101u8,58u8,242u8,129u8,248u8,36u8,149u8, - 28u8,188u8,184u8,62u8,17u8,82u8,61u8,239u8,116u8,79u8,76u8,145u8,151u8,130u8,86u8,6u8,148u8,175u8,118u8,21u8, - 124u8,250u8,220u8,211u8,102u8,20u8,90u8,200u8,145u8,4u8,28u8,154u8,118u8,186u8,61u8,27u8,153u8,82u8,7u8,77u8, - 147u8,53u8,85u8,212u8,77u8,188u8,53u8,245u8,82u8,35u8,123u8,110u8,212u8,227u8,20u8,132u8,176u8,0u8,122u8,30u8, - 58u8,241u8,132u8,77u8,75u8,207u8,249u8,13u8,173u8,220u8,96u8,15u8,156u8,185u8,223u8,211u8,132u8,226u8,209u8,105u8, - 228u8,2u8,102u8,161u8,218u8,92u8,225u8,98u8,52u8,66u8,24u8,44u8,108u8,206u8,46u8,127u8,7u8,183u8,119u8,196u8, - 158u8,138u8,46u8,42u8,226u8,0u8,75u8,235u8,233u8,231u8,134u8,47u8,77u8,26u8,3u8,183u8,165u8,81u8,242u8,176u8, - 163u8,226u8,213u8,57u8,114u8,205u8,163u8,206u8,201u8,9u8,11u8,0u8,234u8,153u8,231u8,184u8,209u8,52u8,69u8,92u8, - 233u8,84u8,96u8,171u8,219u8,61u8,253u8,11u8,232u8,63u8,14u8,23u8,212u8,20u8,71u8,27u8,115u8,224u8,235u8,148u8, - 145u8,54u8,150u8,145u8,155u8,4u8,168u8,205u8,167u8,193u8,20u8,236u8,98u8,128u8,230u8,144u8,184u8,44u8,64u8,255u8, - 68u8,148u8,90u8,162u8,92u8,17u8,119u8,115u8,60u8,174u8,168u8,21u8,93u8,217u8,167u8,9u8,160u8,67u8,169u8,78u8, - 57u8,247u8,132u8,92u8,55u8,169u8,12u8,86u8,145u8,12u8,76u8,139u8,27u8,70u8,71u8,145u8,254u8,50u8,141u8,103u8, - 14u8,6u8,175u8,206u8,222u8,2u8,130u8,107u8,86u8,4u8,3u8,101u8,78u8,102u8,30u8,234u8,141u8,12u8,160u8,200u8, - 181u8,60u8,215u8,48u8,83u8,34u8,194u8,203u8,81u8,24u8,65u8,208u8,96u8,19u8,252u8,140u8,113u8,27u8,67u8,25u8, - 223u8,186u8,114u8,36u8,25u8,139u8,29u8,137u8,24u8,231u8,157u8,204u8,60u8,69u8,215u8,98u8,121u8,44u8,22u8,11u8, - 58u8,123u8,89u8,183u8,193u8,236u8,204u8,16u8,174u8,154u8,29u8,74u8,68u8,51u8,61u8,197u8,220u8,122u8,185u8,97u8, - 245u8,170u8,198u8,227u8,24u8,190u8,226u8,220u8,130u8,88u8,51u8,69u8,88u8,50u8,141u8,210u8,14u8,214u8,41u8,105u8, - 43u8,44u8,228u8,246u8,148u8,79u8,229u8,198u8,43u8,119u8,150u8,211u8,20u8,161u8,191u8,28u8,255u8,145u8,178u8,136u8, - 131u8,12u8,91u8,128u8,174u8,120u8,128u8,143u8,232u8,132u8,70u8,52u8,128u8,216u8,10u8,224u8,128u8,234u8,24u8,178u8, - 33u8,216u8,150u8,212u8,33u8,90u8,91u8,11u8,29u8,177u8,97u8,37u8,183u8,170u8,232u8,9u8,102u8,71u8,225u8,74u8, - 42u8,104u8,79u8,215u8,16u8,72u8,41u8,199u8,140u8,189u8,107u8,155u8,182u8,200u8,11u8,105u8,244u8,157u8,70u8,117u8, - 89u8,146u8,56u8,83u8,99u8,82u8,142u8,64u8,13u8,165u8,46u8,69u8,45u8,105u8,239u8,101u8,7u8,86u8,49u8,195u8, - 17u8,57u8,11u8,229u8,218u8,174u8,1u8,52u8,57u8,33u8,113u8,72u8,157u8,16u8,28u8,174u8,91u8,186u8,55u8,72u8, - 54u8,166u8,198u8,106u8,53u8,121u8,150u8,20u8,114u8,205u8,150u8,4u8,115u8,181u8,2u8,57u8,178u8,175u8,178u8,82u8, - 15u8,29u8,31u8,104u8,146u8,70u8,1u8,64u8,71u8,75u8,101u8,203u8,236u8,234u8,206u8,148u8,237u8,192u8,190u8,185u8, - 194u8,17u8,27u8,234u8,149u8,142u8,163u8,63u8,128u8,226u8,241u8,192u8,18u8,6u8,255u8,164u8,1u8,112u8,74u8,186u8, - 17u8,168u8,196u8,119u8,48u8,134u8,11u8,158u8,87u8,96u8,106u8,230u8,41u8,224u8,27u8,194u8,127u8,34u8,121u8,138u8, - 30u8,31u8,69u8,60u8,39u8,212u8,133u8,108u8,77u8,250u8,126u8,145u8,219u8,75u8,243u8,80u8,151u8,64u8,13u8,32u8, - 45u8,201u8,159u8,197u8,69u8,177u8,192u8,231u8,149u8,201u8,182u8,187u8,128u8,69u8,145u8,7u8,139u8,32u8,75u8,134u8, - 145u8,53u8,9u8,49u8,237u8,139u8,68u8,168u8,226u8,228u8,74u8,67u8,123u8,195u8,226u8,177u8,239u8,178u8,5u8,141u8, - 32u8,32u8,205u8,192u8,208u8,192u8,156u8,198u8,188u8,52u8,16u8,201u8,35u8,196u8,188u8,56u8,241u8,215u8,7u8,228u8, - 35u8,72u8,145u8,37u8,4u8,242u8,247u8,117u8,152u8,2u8,35u8,43u8,8u8,5u8,176u8,54u8,162u8,56u8,185u8,98u8, - 144u8,152u8,242u8,4u8,158u8,18u8,245u8,128u8,223u8,26u8,171u8,228u8,12u8,53u8,197u8,16u8,180u8,38u8,62u8,73u8, - 26u8,83u8,110u8,68u8,252u8,155u8,97u8,117u8,51u8,215u8,159u8,56u8,234u8,228u8,242u8,247u8,195u8,188u8,16u8,44u8, - 44u8,20u8,51u8,183u8,242u8,19u8,164u8,204u8,224u8,178u8,220u8,254u8,116u8,42u8,166u8,13u8,150u8,17u8,24u8,14u8, - 213u8,35u8,241u8,202u8,93u8,58u8,17u8,93u8,132u8,25u8,21u8,230u8,194u8,23u8,162u8,17u8,42u8,188u8,113u8,235u8, - 134u8,253u8,24u8,121u8,156u8,215u8,183u8,220u8,94u8,172u8,219u8,183u8,221u8,25u8,202u8,88u8,37u8,19u8,16u8,226u8, - 216u8,106u8,79u8,61u8,228u8,169u8,242u8,84u8,195u8,152u8,231u8,21u8,136u8,53u8,4u8,32u8,24u8,230u8,96u8,6u8, - 10u8,182u8,196u8,43u8,242u8,54u8,193u8,138u8,116u8,197u8,77u8,175u8,44u8,71u8,49u8,113u8,198u8,15u8,220u8,16u8, - 129u8,82u8,62u8,128u8,5u8,236u8,36u8,245u8,125u8,181u8,58u8,69u8,184u8,131u8,140u8,89u8,212u8,190u8,43u8,6u8, - 53u8,33u8,26u8,33u8,89u8,96u8,189u8,55u8,117u8,37u8,49u8,180u8,69u8,207u8,131u8,76u8,133u8,174u8,36u8,177u8, - 138u8,17u8,22u8,226u8,82u8,236u8,176u8,14u8,27u8,225u8,96u8,152u8,145u8,25u8,102u8,167u8,25u8,92u8,118u8,71u8, - 8u8,199u8,23u8,156u8,53u8,128u8,28u8,18u8,87u8,83u8,245u8,51u8,190u8,44u8,71u8,39u8,72u8,165u8,120u8,62u8, - 91u8,110u8,81u8,164u8,194u8,38u8,33u8,117u8,199u8,23u8,228u8,81u8,53u8,200u8,181u8,33u8,160u8,194u8,174u8,153u8, - 124u8,106u8,168u8,11u8,34u8,52u8,172u8,47u8,255u8,137u8,120u8,96u8,172u8,12u8,221u8,86u8,103u8,91u8,120u8,84u8, - 243u8,118u8,109u8,190u8,21u8,218u8,139u8,220u8,217u8,245u8,115u8,225u8,41u8,237u8,44u8,160u8,103u8,101u8,18u8,133u8, - 84u8,172u8,120u8,68u8,58u8,138u8,106u8,193u8,119u8,186u8,32u8,188u8,193u8,211u8,62u8,57u8,124u8,4u8,160u8,136u8, - 229u8,120u8,144u8,46u8,70u8,0u8,175u8,143u8,14u8,191u8,187u8,188u8,240u8,168u8,121u8,103u8,0u8,2u8,116u8,54u8, - 205u8,15u8,13u8,109u8,190u8,78u8,71u8,63u8,185u8,106u8,145u8,112u8,172u8,252u8,188u8,93u8,64u8,70u8,243u8,192u8, - 61u8,248u8,183u8,186u8,25u8,210u8,28u8,244u8,143u8,142u8,81u8,16u8,147u8,136u8,82u8,103u8,21u8,177u8,132u8,58u8, - 127u8,164u8,33u8,180u8,134u8,30u8,29u8,2u8,157u8,42u8,43u8,72u8,233u8,65u8,195u8,33u8,114u8,125u8,79u8,192u8, - 205u8,141u8,104u8,219u8,211u8,226u8,180u8,192u8,40u8,213u8,24u8,186u8,86u8,76u8,83u8,215u8,148u8,226u8,111u8,19u8, - 218u8,75u8,125u8,40u8,232u8,247u8,30u8,90u8,133u8,110u8,81u8,224u8,137u8,134u8,161u8,2u8,126u8,6u8,240u8,121u8, - 33u8,182u8,206u8,102u8,17u8,11u8,110u8,52u8,248u8,99u8,28u8,38u8,215u8,63u8,1u8,128u8,41u8,85u8,149u8,181u8, - 242u8,178u8,87u8,93u8,57u8,212u8,215u8,167u8,119u8,144u8,214u8,217u8,50u8,186u8,135u8,69u8,229u8,101u8,73u8,232u8, - 98u8,232u8,47u8,210u8,142u8,214u8,229u8,52u8,211u8,56u8,41u8,30u8,39u8,239u8,98u8,156u8,17u8,93u8,110u8,58u8, - 180u8,153u8,24u8,101u8,172u8,238u8,90u8,145u8,78u8,204u8,193u8,88u8,45u8,87u8,211u8,47u8,73u8,228u8,142u8,147u8, - 202u8,234u8,211u8,7u8,22u8,47u8,135u8,117u8,154u8,202u8,181u8,152u8,168u8,215u8,102u8,42u8,167u8,234u8,154u8,178u8, - 56u8,205u8,231u8,219u8,252u8,90u8,93u8,161u8,7u8,206u8,124u8,85u8,59u8,116u8,210u8,44u8,222u8,56u8,98u8,175u8, - 134u8,99u8,195u8,105u8,224u8,216u8,205u8,9u8,174u8,93u8,26u8,26u8,2u8,52u8,149u8,49u8,152u8,203u8,168u8,185u8, - 40u8,225u8,93u8,91u8,222u8,210u8,55u8,138u8,150u8,140u8,231u8,176u8,2u8,175u8,68u8,182u8,186u8,10u8,32u8,207u8, - 4u8,6u8,83u8,42u8,186u8,189u8,43u8,8u8,216u8,203u8,136u8,102u8,44u8,76u8,99u8,127u8,45u8,194u8,176u8,199u8, - 9u8,34u8,169u8,76u8,241u8,4u8,136u8,243u8,69u8,238u8,43u8,50u8,86u8,108u8,180u8,136u8,173u8,217u8,24u8,28u8, - 107u8,202u8,91u8,51u8,17u8,210u8,133u8,248u8,47u8,50u8,3u8,200u8,80u8,227u8,25u8,155u8,96u8,71u8,218u8,195u8, - 156u8,116u8,180u8,38u8,131u8,3u8,242u8,27u8,14u8,185u8,232u8,90u8,168u8,108u8,86u8,83u8,64u8,221u8,99u8,254u8, - 43u8,242u8,193u8,86u8,197u8,216u8,117u8,219u8,156u8,195u8,40u8,210u8,254u8,202u8,229u8,153u8,154u8,14u8,111u8,87u8, - 154u8,109u8,135u8,57u8,247u8,131u8,33u8,42u8,247u8,101u8,95u8,73u8,47u8,252u8,126u8,100u8,68u8,49u8,1u8,225u8, - 10u8,42u8,156u8,54u8,189u8,42u8,209u8,62u8,231u8,213u8,169u8,11u8,249u8,122u8,1u8,30u8,128u8,16u8,9u8,166u8, - 236u8,120u8,237u8,86u8,174u8,48u8,50u8,126u8,248u8,231u8,178u8,131u8,153u8,199u8,40u8,149u8,113u8,148u8,223u8,212u8, - 69u8,20u8,140u8,62u8,147u8,5u8,45u8,226u8,1u8,236u8,106u8,86u8,200u8,38u8,189u8,45u8,155u8,39u8,74u8,1u8, - 247u8,131u8,57u8,236u8,118u8,206u8,164u8,236u8,34u8,124u8,169u8,226u8,190u8,38u8,20u8,156u8,111u8,172u8,98u8,238u8, - 212u8,47u8,43u8,149u8,246u8,255u8,163u8,115u8,218u8,246u8,225u8,218u8,40u8,237u8,131u8,91u8,91u8,183u8,166u8,48u8, - 218u8,85u8,37u8,133u8,181u8,131u8,107u8,59u8,152u8,6u8,59u8,37u8,165u8,109u8,19u8,176u8,221u8,181u8,178u8,131u8, - 102u8,26u8,181u8,179u8,171u8,134u8,106u8,202u8,177u8,106u8,225u8,161u8,198u8,190u8,138u8,232u8,54u8,168u8,221u8,98u8, - 246u8,117u8,161u8,180u8,22u8,178u8,203u8,151u8,2u8,9u8,255u8,51u8,251u8,9u8,32u8,26u8,17u8,121u8,142u8,191u8, - 228u8,153u8,149u8,145u8,212u8,153u8,9u8,20u8,101u8,188u8,7u8,25u8,98u8,42u8,5u8,227u8,115u8,222u8,181u8,169u8, - 62u8,237u8,192u8,141u8,39u8,124u8,189u8,13u8,98u8,91u8,52u8,97u8,152u8,188u8,219u8,156u8,11u8,168u8,85u8,47u8, - 247u8,5u8,222u8,144u8,185u8,105u8,20u8,50u8,245u8,196u8,13u8,58u8,112u8,23u8,54u8,215u8,197u8,114u8,186u8,29u8, - 80u8,207u8,191u8,111u8,102u8,213u8,8u8,167u8,38u8,16u8,238u8,208u8,173u8,6u8,241u8,212u8,225u8,164u8,116u8,16u8, - 228u8,99u8,222u8,42u8,195u8,227u8,44u8,216u8,242u8,59u8,216u8,164u8,74u8,183u8,77u8,100u8,168u8,63u8,71u8,53u8, - 54u8,180u8,13u8,83u8,69u8,139u8,68u8,120u8,129u8,195u8,20u8,149u8,212u8,187u8,85u8,253u8,250u8,121u8,67u8,68u8, - 42u8,130u8,209u8,188u8,41u8,24u8,53u8,35u8,68u8,177u8,79u8,183u8,30u8,10u8,58u8,134u8,184u8,21u8,123u8,219u8, - 175u8,180u8,109u8,236u8,221u8,144u8,130u8,213u8,66u8,46u8,13u8,203u8,248u8,158u8,53u8,135u8,235u8,17u8,177u8,109u8, - 45u8,15u8,150u8,226u8,50u8,163u8,81u8,44u8,170u8,75u8,113u8,73u8,81u8,115u8,237u8,81u8,20u8,152u8,44u8,216u8, - 95u8,250u8,238u8,152u8,126u8,131u8,154u8,141u8,179u8,182u8,17u8,167u8,204u8,38u8,241u8,174u8,153u8,153u8,113u8,255u8, - 128u8,159u8,160u8,153u8,237u8,104u8,182u8,157u8,191u8,162u8,40u8,167u8,97u8,79u8,10u8,175u8,140u8,138u8,52u8,123u8, - 88u8,238u8,63u8,84u8,165u8,200u8,31u8,159u8,36u8,16u8,24u8,181u8,252u8,24u8,127u8,148u8,171u8,134u8,205u8,142u8, - 110u8,180u8,48u8,117u8,14u8,149u8,59u8,136u8,108u8,187u8,171u8,135u8,178u8,214u8,226u8,66u8,183u8,17u8,239u8,106u8, - 135u8,86u8,13u8,167u8,52u8,79u8,144u8,218u8,68u8,132u8,168u8,47u8,44u8,78u8,226u8,131u8,198u8,110u8,148u8,114u8, - 171u8,219u8,132u8,174u8,57u8,75u8,155u8,129u8,201u8,222u8,129u8,4u8,102u8,127u8,161u8,11u8,225u8,56u8,156u8,221u8, - 100u8,21u8,202u8,243u8,42u8,12u8,162u8,186u8,81u8,59u8,181u8,138u8,150u8,90u8,122u8,88u8,77u8,144u8,42u8,189u8, - 156u8,150u8,186u8,203u8,183u8,235u8,181u8,233u8,15u8,117u8,219u8,171u8,45u8,167u8,219u8,109u8,203u8,188u8,161u8,231u8, - 150u8,236u8,27u8,221u8,115u8,235u8,17u8,76u8,202u8,141u8,135u8,208u8,147u8,199u8,38u8,227u8,43u8,60u8,14u8,202u8, - 209u8,68u8,233u8,82u8,69u8,108u8,202u8,2u8,215u8,135u8,190u8,150u8,176u8,195u8,64u8,137u8,76u8,104u8,157u8,252u8, - 145u8,105u8,245u8,186u8,140u8,191u8,147u8,172u8,115u8,203u8,26u8,153u8,21u8,66u8,174u8,191u8,20u8,204u8,234u8,164u8, - 82u8,174u8,221u8,66u8,30u8,134u8,86u8,171u8,239u8,28u8,134u8,157u8,36u8,74u8,41u8,6u8,204u8,161u8,232u8,95u8, - 227u8,221u8,32u8,139u8,171u8,207u8,26u8,202u8,246u8,33u8,212u8,227u8,37u8,140u8,95u8,98u8,82u8,185u8,98u8,49u8, - 144u8,16u8,89u8,92u8,12u8,36u8,39u8,174u8,143u8,31u8,250u8,221u8,225u8,189u8,227u8,61u8,103u8,9u8,174u8,71u8, - 234u8,222u8,200u8,240u8,43u8,193u8,61u8,124u8,165u8,213u8,25u8,133u8,161u8,223u8,51u8,115u8,82u8,244u8,224u8,206u8, - 4u8,211u8,95u8,148u8,129u8,130u8,253u8,57u8,97u8,243u8,93u8,138u8,126u8,225u8,131u8,8u8,197u8,87u8,155u8,10u8, - 45u8,196u8,106u8,203u8,206u8,182u8,192u8,180u8,10u8,127u8,90u8,185u8,149u8,243u8,216u8,244u8,242u8,165u8,122u8,67u8, - 85u8,208u8,195u8,11u8,164u8,166u8,80u8,183u8,161u8,205u8,87u8,234u8,121u8,227u8,115u8,26u8,148u8,70u8,179u8,125u8, - 221u8,187u8,165u8,200u8,119u8,127u8,241u8,102u8,75u8,65u8,67u8,49u8,203u8,150u8,242u8,2u8,166u8,155u8,151u8,43u8, - 252u8,244u8,122u8,232u8,239u8,240u8,8u8,214u8,35u8,14u8,106u8,170u8,208u8,78u8,102u8,42u8,129u8,207u8,170u8,147u8, - 20u8,166u8,4u8,226u8,173u8,162u8,214u8,155u8,171u8,156u8,71u8,76u8,178u8,159u8,166u8,43u8,94u8,168u8,127u8,173u8, - 160u8,131u8,85u8,205u8,96u8,6u8,134u8,85u8,242u8,55u8,113u8,13u8,86u8,217u8,111u8,255u8,52u8,71u8,238u8,88u8, - 111u8,161u8,155u8,141u8,103u8,136u8,214u8,51u8,148u8,247u8,111u8,42u8,36u8,225u8,11u8,242u8,32u8,84u8,202u8,99u8, - 124u8,67u8,205u8,213u8,50u8,20u8,47u8,41u8,16u8,152u8,170u8,144u8,17u8,107u8,239u8,30u8,171u8,162u8,51u8,244u8, - 95u8,212u8,154u8,120u8,225u8,223u8,87u8,121u8,252u8,219u8,167u8,4u8,208u8,214u8,9u8,3u8,127u8,253u8,153u8,127u8, - 80u8,222u8,86u8,98u8,6u8,234u8,69u8,225u8,114u8,195u8,219u8,202u8,60u8,94u8,24u8,137u8,129u8,21u8,7u8,28u8, - 48u8,42u8,35u8,216u8,119u8,91u8,60u8,9u8,201u8,186u8,85u8,158u8,75u8,118u8,213u8,231u8,254u8,14u8,14u8,117u8, - 186u8,109u8,94u8,176u8,54u8,191u8,189u8,25u8,244u8,251u8,245u8,17u8,46u8,127u8,242u8,202u8,182u8,202u8,53u8,113u8, - 195u8,185u8,117u8,195u8,185u8,125u8,67u8,165u8,253u8,152u8,219u8,156u8,94u8,214u8,170u8,77u8,131u8,71u8,124u8,230u8, - 25u8,182u8,30u8,250u8,198u8,20u8,220u8,116u8,190u8,249u8,197u8,207u8,57u8,169u8,112u8,32u8,14u8,180u8,175u8,174u8, - 52u8,129u8,130u8,99u8,132u8,6u8,22u8,200u8,229u8,158u8,173u8,124u8,157u8,55u8,106u8,95u8,61u8,140u8,164u8,221u8, - 175u8,25u8,22u8,81u8,28u8,31u8,175u8,212u8,207u8,153u8,91u8,198u8,111u8,191u8,165u8,170u8,121u8,155u8,77u8,114u8, - 90u8,60u8,22u8,3u8,23u8,212u8,164u8,153u8,115u8,91u8,233u8,41u8,103u8,61u8,133u8,0u8,247u8,219u8,231u8,207u8, - 245u8,195u8,52u8,45u8,237u8,11u8,79u8,215u8,23u8,200u8,35u8,43u8,172u8,88u8,52u8,14u8,152u8,165u8,64u8,20u8, - 132u8,46u8,215u8,95u8,185u8,235u8,152u8,124u8,122u8,215u8,35u8,131u8,30u8,57u8,234u8,145u8,131u8,131u8,3u8,242u8, - 110u8,127u8,240u8,89u8,92u8,179u8,68u8,116u8,9u8,193u8,42u8,97u8,112u8,55u8,98u8,103u8,164u8,218u8,196u8,248u8, - 178u8,4u8,226u8,112u8,81u8,170u8,74u8,165u8,174u8,239u8,110u8,167u8,105u8,215u8,184u8,116u8,128u8,156u8,126u8,173u8, - 234u8,77u8,108u8,57u8,109u8,13u8,46u8,226u8,57u8,161u8,67u8,189u8,41u8,62u8,86u8,138u8,105u8,13u8,218u8,12u8, - 234u8,224u8,38u8,59u8,226u8,239u8,128u8,228u8,99u8,252u8,206u8,0u8,51u8,57u8,99u8,198u8,147u8,218u8,181u8,199u8, - 182u8,17u8,193u8,145u8,20u8,205u8,19u8,136u8,210u8,199u8,22u8,11u8,41u8,196u8,252u8,196u8,102u8,22u8,26u8,9u8, - 208u8,47u8,204u8,106u8,32u8,113u8,196u8,73u8,12u8,26u8,72u8,192u8,24u8,204u8,106u8,32u8,49u8,176u8,145u8,144u8, - 74u8,233u8,100u8,131u8,237u8,245u8,177u8,139u8,22u8,182u8,12u8,7u8,63u8,111u8,66u8,136u8,193u8,182u8,16u8,161u8, - 16u8,63u8,122u8,186u8,137u8,250u8,209u8,182u8,212u8,119u8,209u8,200u8,209u8,83u8,93u8,37u8,245u8,210u8,168u8,50u8, - 92u8,68u8,161u8,34u8,82u8,13u8,68u8,182u8,111u8,197u8,232u8,86u8,207u8,65u8,183u8,51u8,5u8,129u8,17u8,187u8, - 132u8,127u8,112u8,194u8,154u8,176u8,48u8,184u8,143u8,176u8,176u8,75u8,155u8,206u8,0u8,64u8,12u8,88u8,181u8,131u8, - 207u8,27u8,198u8,254u8,209u8,48u8,246u8,115u8,195u8,216u8,179u8,134u8,177u8,167u8,13u8,99u8,199u8,13u8,99u8,79u8, - 26u8,198u8,142u8,108u8,113u8,174u8,185u8,0u8,208u8,94u8,178u8,234u8,22u8,87u8,145u8,222u8,214u8,196u8,65u8,159u8, - 91u8,108u8,176u8,11u8,253u8,163u8,38u8,250u8,21u8,127u8,21u8,249u8,128u8,196u8,113u8,195u8,186u8,68u8,190u8,209u8, - 111u8,227u8,198u8,22u8,11u8,23u8,55u8,64u8,127u8,135u8,33u8,242u8,240u8,172u8,154u8,29u8,216u8,93u8,189u8,76u8, - 2u8,173u8,222u8,206u8,233u8,74u8,190u8,196u8,255u8,31u8,155u8,9u8,229u8,237u8,78u8,248u8,176u8,21u8,60u8,240u8, - 70u8,225u8,221u8,226u8,195u8,93u8,195u8,131u8,214u8,205u8,52u8,159u8,121u8,223u8,88u8,213u8,120u8,99u8,101u8,164u8, - 172u8,9u8,26u8,255u8,130u8,210u8,141u8,176u8,35u8,200u8,72u8,111u8,170u8,90u8,195u8,253u8,110u8,218u8,68u8,175u8, - 123u8,72u8,159u8,235u8,91u8,144u8,141u8,253u8,230u8,6u8,63u8,108u8,35u8,101u8,203u8,69u8,136u8,254u8,55u8,61u8, - 178u8,174u8,69u8,0u8,136u8,13u8,214u8,151u8,14u8,165u8,159u8,108u8,249u8,247u8,188u8,184u8,86u8,182u8,94u8,37u8, - 94u8,136u8,63u8,54u8,243u8,170u8,251u8,113u8,39u8,158u8,150u8,223u8,93u8,169u8,125u8,231u8,129u8,182u8,126u8,179u8, - 167u8,149u8,189u8,148u8,43u8,100u8,126u8,145u8,9u8,246u8,140u8,87u8,137u8,119u8,180u8,159u8,229u8,112u8,86u8,0u8, - 21u8,57u8,177u8,216u8,250u8,91u8,104u8,48u8,175u8,214u8,191u8,111u8,195u8,68u8,235u8,13u8,123u8,70u8,27u8,65u8, - 174u8,169u8,180u8,18u8,114u8,249u8,241u8,117u8,100u8,111u8,15u8,87u8,254u8,185u8,68u8,179u8,144u8,219u8,237u8,131u8, - 255u8,1u8,243u8,142u8,45u8,226u8,47u8,69u8,0u8,0u8,0u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,10u8,77u8,111u8,118u8,101u8,83u8,116u8,100u8,108u8,105u8,98u8,0u8, - ]; - vector::append(&mut chunk1, chunk2); - vector::append(&mut chunk1, chunk3); - code::publish_package_txn(&framework_signer, chunk1, code) - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/2-aptos-framework.move b/aptos-move/aptos-release-builder/data/example_output/2-aptos-framework.move deleted file mode 100644 index cc1c48dfe723e..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/2-aptos-framework.move +++ /dev/null @@ -1,14097 +0,0 @@ -// Script hash: c0d44eec -// Framework commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Builder commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Upgrade proposal for package `AptosFramework` - -// source digest: 4DFFCA3EE6359F91D67870AF6000124A19F85DC4064AC4292E8F24DE133340A4 -script { - use std::vector; - use aptos_framework::aptos_governance; - use aptos_framework::code; - - fun main(proposal_id: u64){ - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000001, - vector[96u8,173u8,219u8,80u8,179u8,22u8,110u8,137u8,164u8,193u8,0u8,81u8,135u8,32u8,145u8,224u8,208u8,103u8,73u8,224u8,74u8,29u8,17u8,93u8,237u8,3u8,152u8,156u8,113u8,255u8,58u8,225u8,], - ); - let code = vector::empty(); - let chunk0 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,8u8,1u8,0u8,6u8,3u8,6u8,70u8,5u8,76u8,20u8,7u8, - 96u8,196u8,2u8,8u8,164u8,3u8,32u8,6u8,196u8,3u8,192u8,3u8,16u8,132u8,7u8,249u8,2u8,12u8,253u8,9u8, - 210u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,1u8,0u8,0u8,4u8,0u8,1u8,0u8,0u8,5u8, - 2u8,1u8,0u8,0u8,6u8,2u8,1u8,0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,9u8, - 2u8,3u8,0u8,0u8,10u8,2u8,3u8,0u8,0u8,11u8,2u8,3u8,0u8,0u8,12u8,2u8,3u8,0u8,0u8,13u8, - 0u8,3u8,0u8,0u8,14u8,2u8,3u8,0u8,2u8,15u8,0u8,2u8,0u8,1u8,16u8,4u8,4u8,0u8,1u8,6u8, - 12u8,0u8,1u8,5u8,1u8,1u8,1u8,3u8,9u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,16u8,115u8, - 121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,5u8,101u8,114u8,114u8,111u8, - 114u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8, - 115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,20u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,99u8, - 111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,28u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8, - 99u8,111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,25u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,95u8,114u8, - 101u8,115u8,101u8,114u8,118u8,101u8,100u8,33u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,102u8,114u8,97u8,109u8,101u8, - 119u8,111u8,114u8,107u8,95u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,9u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,118u8,109u8,26u8,105u8,115u8,95u8,97u8,112u8,116u8,111u8,115u8, - 95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,24u8,105u8, - 115u8,95u8,99u8,111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,29u8,105u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,95u8,114u8,101u8,115u8, - 101u8,114u8,118u8,101u8,100u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,19u8,105u8,115u8,95u8,114u8,101u8,115u8, - 101u8,114u8,118u8,101u8,100u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,5u8,105u8,115u8,95u8,118u8,109u8,13u8, - 105u8,115u8,95u8,118u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,95u8,111u8,102u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8,105u8, - 101u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,85u8, - 12u8,24u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,5u8,32u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,4u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,5u8,32u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,7u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,8u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,9u8,5u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8, - 49u8,228u8,2u8,4u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,78u8,79u8,84u8,95u8,67u8,79u8, - 82u8,69u8,95u8,82u8,69u8,83u8,79u8,85u8,82u8,67u8,69u8,95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,67u8, - 84u8,104u8,101u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,47u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8, - 100u8,105u8,100u8,32u8,110u8,111u8,116u8,32u8,99u8,111u8,114u8,114u8,101u8,115u8,112u8,111u8,110u8,100u8,32u8,116u8, - 111u8,32u8,116u8,104u8,101u8,32u8,99u8,111u8,114u8,101u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8, - 97u8,100u8,100u8,114u8,101u8,115u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,69u8,86u8,77u8,45u8, - 84u8,104u8,101u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,99u8,97u8,110u8,32u8,111u8,110u8, - 108u8,121u8,32u8,98u8,101u8,32u8,112u8,101u8,114u8,102u8,111u8,114u8,109u8,101u8,100u8,32u8,98u8,121u8,32u8,116u8, - 104u8,101u8,32u8,86u8,77u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,78u8,79u8,84u8,95u8,65u8, - 80u8,84u8,79u8,83u8,95u8,70u8,82u8,65u8,77u8,69u8,87u8,79u8,82u8,75u8,95u8,65u8,68u8,68u8,82u8,69u8, - 83u8,83u8,68u8,84u8,104u8,101u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,47u8,97u8,99u8,99u8,111u8,117u8, - 110u8,116u8,32u8,100u8,105u8,100u8,32u8,110u8,111u8,116u8,32u8,99u8,111u8,114u8,114u8,101u8,115u8,112u8,111u8,110u8, - 100u8,32u8,116u8,111u8,32u8,116u8,104u8,101u8,32u8,99u8,111u8,114u8,101u8,32u8,102u8,114u8,97u8,109u8,101u8,119u8, - 111u8,114u8,107u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,31u8, - 69u8,78u8,79u8,84u8,95u8,70u8,82u8,65u8,77u8,69u8,87u8,79u8,82u8,75u8,95u8,82u8,69u8,83u8,69u8,82u8, - 86u8,69u8,68u8,95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,45u8,84u8,104u8,101u8,32u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8, - 32u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,0u8,0u8,0u8, - 1u8,0u8,0u8,1u8,9u8,11u8,0u8,17u8,12u8,17u8,6u8,4u8,5u8,5u8,8u8,7u8,0u8,17u8,13u8,39u8, - 2u8,1u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,17u8,12u8,17u8,2u8,2u8,2u8,1u8,0u8,0u8,1u8,8u8, - 11u8,0u8,17u8,7u8,4u8,4u8,5u8,7u8,7u8,1u8,17u8,13u8,39u8,2u8,3u8,1u8,0u8,0u8,1u8,8u8, - 11u8,0u8,17u8,8u8,4u8,4u8,5u8,7u8,7u8,2u8,17u8,13u8,39u8,2u8,4u8,1u8,0u8,0u8,1u8,4u8, - 11u8,0u8,17u8,12u8,17u8,3u8,2u8,5u8,1u8,0u8,0u8,1u8,8u8,11u8,0u8,17u8,10u8,4u8,4u8,5u8, - 7u8,7u8,3u8,17u8,13u8,39u8,2u8,6u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,7u8,4u8,33u8,2u8,7u8, - 1u8,0u8,0u8,1u8,4u8,11u8,0u8,7u8,5u8,33u8,2u8,8u8,1u8,0u8,0u8,5u8,84u8,10u8,0u8,17u8, - 6u8,4u8,6u8,8u8,12u8,1u8,5u8,10u8,10u8,0u8,7u8,6u8,33u8,12u8,1u8,11u8,1u8,4u8,15u8,8u8, - 12u8,2u8,5u8,19u8,10u8,0u8,7u8,7u8,33u8,12u8,2u8,11u8,2u8,4u8,24u8,8u8,12u8,3u8,5u8,28u8, - 10u8,0u8,7u8,8u8,33u8,12u8,3u8,11u8,3u8,4u8,33u8,8u8,12u8,4u8,5u8,37u8,10u8,0u8,7u8,9u8, - 33u8,12u8,4u8,11u8,4u8,4u8,42u8,8u8,12u8,5u8,5u8,46u8,10u8,0u8,7u8,10u8,33u8,12u8,5u8,11u8, - 5u8,4u8,51u8,8u8,12u8,6u8,5u8,55u8,10u8,0u8,7u8,11u8,33u8,12u8,6u8,11u8,6u8,4u8,60u8,8u8, - 12u8,7u8,5u8,64u8,10u8,0u8,7u8,12u8,33u8,12u8,7u8,11u8,7u8,4u8,69u8,8u8,12u8,8u8,5u8,73u8, - 10u8,0u8,7u8,13u8,33u8,12u8,8u8,11u8,8u8,4u8,78u8,8u8,12u8,9u8,5u8,82u8,11u8,0u8,7u8,14u8, - 33u8,12u8,9u8,11u8,9u8,2u8,9u8,1u8,0u8,0u8,3u8,11u8,10u8,0u8,17u8,6u8,4u8,6u8,8u8,12u8, - 1u8,5u8,9u8,11u8,0u8,17u8,11u8,12u8,1u8,11u8,1u8,2u8,10u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8, - 17u8,12u8,17u8,11u8,2u8,11u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,7u8,15u8,33u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk0); - let chunk1 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,2u8,2u8,2u8,8u8,3u8,10u8,40u8,5u8, - 50u8,35u8,7u8,85u8,123u8,8u8,208u8,1u8,32u8,6u8,240u8,1u8,10u8,16u8,250u8,1u8,154u8,1u8,10u8,148u8, - 3u8,13u8,12u8,161u8,3u8,126u8,13u8,159u8,4u8,6u8,15u8,165u8,4u8,4u8,0u8,2u8,0u8,3u8,6u8,0u8, - 0u8,4u8,7u8,0u8,0u8,5u8,0u8,1u8,0u8,0u8,6u8,2u8,3u8,0u8,0u8,7u8,4u8,5u8,0u8,0u8, - 8u8,4u8,6u8,0u8,0u8,9u8,7u8,8u8,0u8,0u8,10u8,4u8,3u8,0u8,0u8,11u8,9u8,5u8,0u8,0u8, - 12u8,9u8,6u8,0u8,2u8,5u8,7u8,3u8,1u8,8u8,0u8,2u8,5u8,3u8,1u8,8u8,1u8,1u8,6u8,8u8, - 0u8,1u8,3u8,1u8,5u8,2u8,6u8,8u8,0u8,6u8,8u8,1u8,1u8,1u8,1u8,6u8,8u8,1u8,0u8,7u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,6u8,111u8,98u8,106u8,101u8,99u8,116u8,4u8,103u8,117u8,105u8,100u8,4u8, - 71u8,85u8,73u8,68u8,2u8,73u8,68u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,9u8,99u8,114u8,101u8,97u8,116u8, - 101u8,95u8,105u8,100u8,12u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8,15u8,99u8,114u8, - 101u8,97u8,116u8,111u8,114u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,5u8,101u8,113u8,95u8,105u8,100u8,2u8, - 105u8,100u8,15u8,105u8,100u8,95u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8,18u8,105u8, - 100u8,95u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,4u8,97u8,100u8, - 100u8,114u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8, - 95u8,118u8,49u8,133u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,71u8,85u8,73u8,68u8, - 95u8,71u8,69u8,78u8,69u8,82u8,65u8,84u8,79u8,82u8,95u8,78u8,79u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8, - 83u8,72u8,69u8,68u8,91u8,71u8,85u8,73u8,68u8,32u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,111u8,114u8,32u8, - 109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,32u8,97u8,104u8, - 101u8,97u8,100u8,32u8,111u8,102u8,32u8,102u8,105u8,114u8,115u8,116u8,32u8,117u8,115u8,97u8,103u8,101u8,32u8,111u8, - 102u8,32u8,96u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,96u8,32u8,102u8,117u8,110u8,99u8,116u8,105u8,111u8,110u8,46u8,0u8,0u8,0u8,2u8, - 1u8,10u8,8u8,1u8,1u8,2u8,2u8,7u8,3u8,13u8,5u8,0u8,3u8,0u8,0u8,5u8,13u8,10u8,1u8,20u8, - 12u8,2u8,10u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,1u8,21u8,11u8,2u8,11u8, - 0u8,18u8,1u8,18u8,0u8,2u8,1u8,1u8,0u8,0u8,10u8,4u8,11u8,1u8,11u8,0u8,18u8,1u8,2u8,2u8, - 1u8,0u8,0u8,10u8,5u8,11u8,0u8,16u8,0u8,16u8,1u8,20u8,2u8,3u8,1u8,0u8,0u8,10u8,5u8,11u8, - 0u8,16u8,0u8,16u8,2u8,20u8,2u8,4u8,1u8,0u8,0u8,10u8,5u8,11u8,0u8,16u8,0u8,11u8,1u8,33u8, - 2u8,5u8,1u8,0u8,0u8,10u8,4u8,11u8,0u8,16u8,0u8,20u8,2u8,6u8,1u8,0u8,0u8,10u8,4u8,11u8, - 0u8,16u8,1u8,20u8,2u8,7u8,1u8,0u8,0u8,10u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,0u8,0u8,1u8, - 0u8,1u8,1u8,0u8,0u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk1); - let chunk2 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,6u8,2u8,6u8,10u8,3u8,16u8,42u8,4u8, - 58u8,4u8,5u8,62u8,48u8,7u8,110u8,128u8,1u8,8u8,238u8,1u8,32u8,10u8,142u8,2u8,8u8,11u8,150u8,2u8, - 2u8,12u8,152u8,2u8,103u8,13u8,255u8,2u8,4u8,14u8,131u8,3u8,4u8,15u8,135u8,3u8,4u8,0u8,2u8,0u8, - 3u8,0u8,4u8,0u8,5u8,4u8,1u8,6u8,1u8,2u8,9u8,6u8,0u8,0u8,6u8,0u8,1u8,1u8,6u8,0u8, - 7u8,2u8,3u8,1u8,6u8,0u8,8u8,4u8,3u8,1u8,6u8,0u8,4u8,0u8,5u8,1u8,6u8,0u8,10u8,6u8, - 2u8,1u8,6u8,0u8,11u8,7u8,3u8,1u8,6u8,1u8,12u8,9u8,10u8,1u8,0u8,6u8,6u8,5u8,8u8,1u8, - 6u8,11u8,0u8,1u8,9u8,0u8,1u8,3u8,1u8,11u8,0u8,1u8,9u8,0u8,0u8,2u8,7u8,11u8,0u8,1u8, - 9u8,0u8,9u8,0u8,1u8,6u8,8u8,1u8,1u8,8u8,1u8,3u8,10u8,2u8,3u8,9u8,0u8,1u8,9u8,0u8, - 1u8,6u8,9u8,0u8,1u8,10u8,2u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,6u8,111u8,98u8,106u8,101u8, - 99u8,116u8,5u8,101u8,118u8,101u8,110u8,116u8,3u8,98u8,99u8,115u8,4u8,103u8,117u8,105u8,100u8,11u8,69u8,118u8, - 101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,7u8,99u8,111u8,117u8,110u8,116u8,101u8,114u8,14u8,100u8,101u8, - 115u8,116u8,114u8,111u8,121u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8, - 101u8,110u8,116u8,4u8,71u8,85u8,73u8,68u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8, - 97u8,110u8,100u8,108u8,101u8,20u8,119u8,114u8,105u8,116u8,101u8,95u8,116u8,111u8,95u8,101u8,118u8,101u8,110u8,116u8, - 95u8,115u8,116u8,111u8,114u8,101u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,2u8,6u8,3u8,4u8,8u8,1u8,0u8,8u8,0u8,1u8,0u8, - 0u8,3u8,4u8,11u8,0u8,55u8,0u8,20u8,2u8,1u8,1u8,0u8,0u8,3u8,5u8,11u8,0u8,58u8,0u8,1u8, - 1u8,2u8,2u8,1u8,0u8,0u8,3u8,18u8,10u8,0u8,55u8,1u8,56u8,0u8,10u8,0u8,55u8,0u8,20u8,11u8, - 1u8,56u8,1u8,40u8,10u8,0u8,55u8,0u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8, - 0u8,54u8,0u8,21u8,2u8,3u8,1u8,0u8,0u8,3u8,3u8,11u8,0u8,55u8,1u8,2u8,4u8,3u8,0u8,0u8, - 3u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,0u8,57u8,0u8,2u8,5u8,0u8,2u8,0u8, - 0u8,0u8,0u8,1u8,0u8,8u8,1u8,8u8,0u8,0u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk2); - let chunk3 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,7u8,1u8,0u8,2u8,3u8,2u8,5u8,5u8,7u8,4u8,7u8, - 11u8,68u8,8u8,79u8,32u8,12u8,111u8,4u8,15u8,115u8,10u8,0u8,5u8,0u8,5u8,0u8,1u8,0u8,1u8,5u8, - 1u8,12u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,13u8,97u8,112u8,116u8,111u8,115u8,95u8,97u8,99u8,99u8, - 111u8,117u8,110u8,116u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,16u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8, - 103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,6u8,111u8,98u8,106u8,101u8,99u8,116u8,13u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,1u8,0u8,3u8,2u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8, - ]; - vector::push_back(&mut code, chunk3); - let chunk4 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,4u8,2u8,4u8,4u8,3u8,8u8,15u8,5u8, - 23u8,10u8,7u8,33u8,83u8,8u8,116u8,32u8,6u8,148u8,1u8,34u8,16u8,182u8,1u8,30u8,10u8,212u8,1u8,5u8, - 12u8,217u8,1u8,34u8,13u8,251u8,1u8,2u8,15u8,253u8,1u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,8u8,0u8, - 0u8,4u8,0u8,1u8,0u8,0u8,5u8,2u8,0u8,0u8,1u8,7u8,3u8,0u8,0u8,0u8,1u8,2u8,2u8,6u8, - 12u8,2u8,1u8,6u8,12u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,8u8,99u8,104u8,97u8,105u8,110u8,95u8, - 105u8,100u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,7u8, - 67u8,104u8,97u8,105u8,110u8,73u8,100u8,3u8,103u8,101u8,116u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,2u8,105u8,100u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8, - 58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,10u8,0u8,0u8,1u8,3u8,103u8,101u8, - 116u8,1u8,1u8,0u8,0u8,2u8,1u8,6u8,2u8,0u8,1u8,0u8,1u8,0u8,0u8,5u8,7u8,0u8,43u8,0u8, - 16u8,0u8,20u8,2u8,1u8,3u8,0u8,0u8,0u8,7u8,10u8,0u8,17u8,2u8,11u8,0u8,11u8,1u8,18u8,0u8, - 45u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk4); - let chunk5 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,15u8,1u8,0u8,34u8,2u8,34u8,94u8,3u8,128u8,1u8,248u8, - 2u8,4u8,248u8,3u8,54u8,5u8,174u8,4u8,185u8,3u8,7u8,231u8,7u8,170u8,16u8,8u8,145u8,24u8,32u8,6u8, - 177u8,24u8,135u8,5u8,16u8,184u8,29u8,128u8,17u8,10u8,184u8,46u8,122u8,11u8,178u8,47u8,4u8,12u8,182u8,47u8, - 187u8,15u8,13u8,241u8,62u8,20u8,14u8,133u8,63u8,4u8,15u8,137u8,63u8,10u8,0u8,5u8,0u8,6u8,0u8,7u8, - 0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8,17u8, - 0u8,18u8,0u8,19u8,0u8,20u8,0u8,21u8,0u8,22u8,12u8,0u8,0u8,23u8,4u8,1u8,0u8,1u8,0u8,24u8, - 6u8,0u8,0u8,25u8,6u8,0u8,0u8,26u8,8u8,0u8,0u8,27u8,6u8,0u8,0u8,28u8,2u8,0u8,0u8,29u8, - 2u8,0u8,0u8,30u8,3u8,0u8,0u8,31u8,6u8,0u8,0u8,32u8,2u8,0u8,0u8,33u8,3u8,0u8,8u8,39u8, - 6u8,0u8,6u8,53u8,4u8,1u8,6u8,1u8,11u8,75u8,7u8,1u8,0u8,0u8,15u8,76u8,7u8,0u8,14u8,80u8, - 4u8,2u8,3u8,1u8,0u8,1u8,4u8,86u8,7u8,0u8,10u8,86u8,7u8,0u8,4u8,87u8,7u8,0u8,10u8,87u8, - 7u8,0u8,0u8,34u8,0u8,1u8,0u8,0u8,35u8,2u8,3u8,0u8,0u8,36u8,2u8,3u8,0u8,0u8,37u8,4u8, - 3u8,0u8,0u8,38u8,2u8,5u8,0u8,0u8,40u8,6u8,7u8,0u8,0u8,41u8,8u8,5u8,0u8,0u8,42u8,9u8, - 2u8,0u8,0u8,43u8,10u8,3u8,0u8,0u8,44u8,2u8,11u8,0u8,0u8,45u8,2u8,1u8,0u8,0u8,46u8,2u8, - 12u8,0u8,0u8,47u8,2u8,12u8,0u8,0u8,48u8,10u8,2u8,0u8,0u8,49u8,2u8,2u8,0u8,0u8,50u8,2u8, - 13u8,0u8,0u8,51u8,6u8,13u8,0u8,0u8,52u8,2u8,11u8,0u8,0u8,54u8,6u8,14u8,1u8,6u8,0u8,55u8, - 15u8,13u8,0u8,0u8,56u8,15u8,13u8,0u8,0u8,57u8,2u8,13u8,1u8,0u8,0u8,58u8,6u8,13u8,0u8,0u8, - 59u8,6u8,13u8,0u8,0u8,60u8,4u8,13u8,0u8,0u8,61u8,4u8,13u8,0u8,0u8,62u8,16u8,13u8,0u8,0u8, - 63u8,8u8,13u8,0u8,0u8,64u8,17u8,13u8,0u8,0u8,65u8,18u8,13u8,0u8,0u8,66u8,19u8,13u8,1u8,2u8, - 4u8,88u8,1u8,21u8,0u8,4u8,89u8,1u8,22u8,0u8,4u8,90u8,24u8,11u8,1u8,2u8,5u8,91u8,12u8,12u8, - 0u8,4u8,92u8,25u8,1u8,0u8,10u8,88u8,1u8,26u8,0u8,10u8,89u8,1u8,27u8,0u8,10u8,90u8,28u8,11u8, - 1u8,2u8,10u8,92u8,29u8,1u8,0u8,5u8,93u8,12u8,12u8,0u8,3u8,8u8,2u8,3u8,0u8,1u8,94u8,32u8, - 1u8,1u8,0u8,8u8,95u8,34u8,7u8,0u8,6u8,54u8,7u8,14u8,1u8,6u8,11u8,96u8,13u8,37u8,1u8,0u8, - 5u8,97u8,12u8,12u8,0u8,12u8,98u8,6u8,2u8,0u8,11u8,99u8,41u8,11u8,1u8,0u8,5u8,100u8,12u8,12u8, - 0u8,5u8,101u8,12u8,12u8,0u8,11u8,102u8,45u8,11u8,1u8,0u8,5u8,103u8,12u8,12u8,0u8,11u8,104u8,46u8, - 37u8,1u8,0u8,16u8,105u8,47u8,13u8,1u8,0u8,9u8,106u8,1u8,1u8,0u8,7u8,107u8,1u8,2u8,0u8,11u8, - 108u8,45u8,11u8,1u8,0u8,11u8,109u8,45u8,32u8,1u8,0u8,13u8,110u8,6u8,13u8,0u8,14u8,111u8,13u8,51u8, - 2u8,3u8,4u8,2u8,112u8,13u8,33u8,0u8,11u8,113u8,54u8,37u8,1u8,0u8,15u8,114u8,13u8,57u8,1u8,0u8, - 6u8,115u8,58u8,13u8,1u8,6u8,11u8,116u8,59u8,46u8,1u8,0u8,5u8,117u8,12u8,12u8,0u8,14u8,99u8,64u8, - 11u8,2u8,3u8,0u8,14u8,118u8,65u8,66u8,2u8,3u8,0u8,14u8,119u8,67u8,13u8,2u8,3u8,0u8,33u8,23u8, - 38u8,23u8,42u8,2u8,44u8,35u8,44u8,36u8,45u8,2u8,48u8,2u8,51u8,2u8,53u8,2u8,54u8,33u8,57u8,2u8, - 58u8,2u8,60u8,50u8,44u8,46u8,33u8,53u8,38u8,53u8,62u8,2u8,30u8,56u8,63u8,46u8,64u8,35u8,65u8,2u8, - 67u8,50u8,68u8,50u8,69u8,50u8,64u8,36u8,33u8,46u8,38u8,46u8,4u8,2u8,10u8,2u8,10u8,2u8,6u8,8u8, - 8u8,1u8,10u8,2u8,1u8,5u8,1u8,12u8,2u8,6u8,12u8,5u8,2u8,12u8,8u8,9u8,1u8,6u8,12u8,1u8, - 8u8,12u8,2u8,6u8,12u8,10u8,2u8,2u8,6u8,5u8,10u8,2u8,1u8,6u8,8u8,9u8,1u8,1u8,1u8,3u8, - 0u8,1u8,11u8,13u8,1u8,9u8,0u8,5u8,6u8,12u8,10u8,2u8,2u8,10u8,2u8,5u8,7u8,6u8,12u8,2u8, - 10u8,2u8,2u8,10u8,2u8,10u8,2u8,10u8,2u8,5u8,6u8,12u8,5u8,2u8,10u8,2u8,10u8,2u8,3u8,5u8, - 7u8,8u8,0u8,10u8,2u8,5u8,5u8,2u8,10u8,2u8,10u8,2u8,9u8,0u8,5u8,10u8,2u8,8u8,17u8,8u8, - 18u8,8u8,19u8,8u8,20u8,1u8,8u8,17u8,1u8,8u8,19u8,1u8,8u8,8u8,3u8,6u8,8u8,19u8,6u8,8u8, - 17u8,9u8,0u8,1u8,6u8,8u8,17u8,1u8,8u8,18u8,1u8,8u8,20u8,3u8,6u8,8u8,20u8,6u8,8u8,18u8, - 9u8,0u8,1u8,6u8,8u8,18u8,2u8,1u8,1u8,5u8,10u8,2u8,11u8,13u8,1u8,8u8,2u8,3u8,11u8,13u8, - 1u8,8u8,3u8,12u8,1u8,6u8,9u8,0u8,1u8,2u8,2u8,5u8,7u8,3u8,1u8,8u8,2u8,1u8,8u8,3u8, - 1u8,11u8,14u8,1u8,9u8,0u8,1u8,8u8,5u8,1u8,8u8,9u8,2u8,6u8,8u8,0u8,5u8,2u8,6u8,11u8, - 14u8,1u8,9u8,0u8,6u8,9u8,0u8,11u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,1u8,12u8,8u8,9u8, - 3u8,7u8,8u8,0u8,5u8,8u8,12u8,7u8,5u8,12u8,6u8,8u8,0u8,7u8,8u8,0u8,12u8,5u8,8u8,9u8, - 1u8,6u8,11u8,14u8,1u8,9u8,0u8,1u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8,10u8,9u8,0u8,1u8,6u8, - 8u8,0u8,1u8,7u8,3u8,2u8,5u8,5u8,1u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,9u8,7u8,8u8,0u8, - 5u8,10u8,2u8,10u8,2u8,8u8,7u8,8u8,17u8,8u8,18u8,8u8,19u8,8u8,20u8,1u8,8u8,7u8,2u8,7u8, - 11u8,14u8,1u8,9u8,0u8,9u8,0u8,2u8,8u8,11u8,5u8,1u8,8u8,11u8,1u8,8u8,15u8,2u8,7u8,11u8, - 13u8,1u8,9u8,0u8,9u8,0u8,1u8,7u8,11u8,14u8,1u8,9u8,0u8,9u8,7u8,8u8,0u8,5u8,8u8,8u8, - 5u8,10u8,2u8,10u8,2u8,8u8,17u8,8u8,18u8,10u8,2u8,2u8,7u8,8u8,0u8,5u8,6u8,8u8,8u8,5u8, - 5u8,10u8,2u8,6u8,8u8,0u8,7u8,8u8,0u8,4u8,5u8,7u8,11u8,16u8,2u8,5u8,5u8,5u8,5u8,2u8, - 6u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,2u8,7u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,9u8, - 0u8,1u8,9u8,1u8,3u8,7u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,7u8,7u8,8u8, - 0u8,10u8,2u8,10u8,2u8,8u8,17u8,8u8,18u8,8u8,19u8,8u8,20u8,13u8,97u8,112u8,116u8,111u8,115u8,95u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,4u8,99u8,111u8,105u8,110u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8, - 16u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,22u8,116u8,114u8, - 97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8, - 7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,3u8,98u8,99u8,115u8,8u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8, - 100u8,13u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,7u8,101u8,100u8,50u8,53u8, - 53u8,49u8,57u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,114u8,111u8,109u8, - 95u8,98u8,99u8,115u8,4u8,103u8,117u8,105u8,100u8,4u8,104u8,97u8,115u8,104u8,13u8,109u8,117u8,108u8,116u8,105u8, - 95u8,101u8,100u8,50u8,53u8,53u8,49u8,57u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8, - 101u8,114u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,5u8, - 116u8,97u8,98u8,108u8,101u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,6u8,118u8,101u8,99u8,116u8, - 111u8,114u8,7u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,15u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,79u8,102u8,102u8,101u8,114u8,17u8,67u8,111u8,105u8,110u8,82u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,69u8, - 118u8,101u8,110u8,116u8,16u8,75u8,101u8,121u8,82u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8, - 116u8,18u8,79u8,114u8,105u8,103u8,105u8,110u8,97u8,116u8,105u8,110u8,103u8,65u8,100u8,100u8,114u8,101u8,115u8,115u8, - 18u8,82u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,37u8, - 82u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,79u8,102u8, - 102u8,101u8,114u8,80u8,114u8,111u8,111u8,102u8,67u8,104u8,97u8,108u8,108u8,101u8,110u8,103u8,101u8,39u8,82u8,111u8, - 116u8,97u8,116u8,105u8,111u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,79u8,102u8,102u8,101u8, - 114u8,80u8,114u8,111u8,111u8,102u8,67u8,104u8,97u8,108u8,108u8,101u8,110u8,103u8,101u8,86u8,50u8,22u8,82u8,111u8, - 116u8,97u8,116u8,105u8,111u8,110u8,80u8,114u8,111u8,111u8,102u8,67u8,104u8,97u8,108u8,108u8,101u8,110u8,103u8,101u8, - 16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,35u8,83u8,105u8, - 103u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,79u8,102u8,102u8,101u8,114u8,80u8, - 114u8,111u8,111u8,102u8,67u8,104u8,97u8,108u8,108u8,101u8,110u8,103u8,101u8,37u8,83u8,105u8,103u8,110u8,101u8,114u8, - 67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,79u8,102u8,102u8,101u8,114u8,80u8,114u8,111u8,111u8,102u8, - 67u8,104u8,97u8,108u8,108u8,101u8,110u8,103u8,101u8,86u8,50u8,54u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,118u8, - 97u8,108u8,105u8,100u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,114u8,111u8,111u8,102u8,95u8, - 115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,97u8,110u8,100u8,95u8,103u8,101u8,116u8,95u8,97u8,117u8, - 116u8,104u8,95u8,107u8,101u8,121u8,14u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,117u8,110u8,99u8, - 104u8,101u8,99u8,107u8,101u8,100u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,117u8,116u8,104u8,111u8,114u8, - 105u8,122u8,101u8,100u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,33u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,95u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,4u8,71u8,85u8,73u8,68u8,11u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,103u8,117u8, - 105u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8, - 99u8,99u8,111u8,117u8,110u8,116u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8, - 99u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,29u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8, - 103u8,110u8,101u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 9u8,101u8,120u8,105u8,115u8,116u8,115u8,95u8,97u8,116u8,22u8,103u8,101u8,116u8,95u8,97u8,117u8,116u8,104u8,101u8, - 110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,26u8,103u8,101u8,116u8,95u8,103u8,117u8, - 105u8,100u8,95u8,110u8,101u8,120u8,116u8,95u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8, - 19u8,103u8,101u8,116u8,95u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8, - 29u8,103u8,101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8, - 116u8,121u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,31u8,103u8,101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,111u8,102u8,102u8,101u8,114u8,95u8,102u8, - 111u8,114u8,25u8,105u8,110u8,99u8,114u8,101u8,109u8,101u8,110u8,116u8,95u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8, - 101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,28u8, - 105u8,115u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 95u8,111u8,102u8,102u8,101u8,114u8,101u8,100u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8, - 16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,25u8,111u8,102u8, - 102u8,101u8,114u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,23u8,111u8,102u8,102u8,101u8,114u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8, - 97u8,98u8,105u8,108u8,105u8,116u8,121u8,13u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,95u8,99u8,111u8,105u8, - 110u8,30u8,114u8,101u8,118u8,111u8,107u8,101u8,95u8,97u8,110u8,121u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8, - 110u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,28u8,114u8,101u8,118u8,111u8,107u8,101u8,95u8, - 97u8,110u8,121u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,26u8,114u8,101u8,118u8,111u8,107u8,101u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,95u8,99u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,24u8,114u8,101u8,118u8,111u8,107u8,101u8,95u8,115u8,105u8,103u8,110u8, - 101u8,114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,25u8,114u8,111u8,116u8,97u8,116u8,101u8, - 95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,34u8, - 114u8,111u8,116u8,97u8,116u8,101u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8, - 110u8,95u8,107u8,101u8,121u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,50u8,114u8,111u8,116u8,97u8,116u8, - 101u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8, - 95u8,119u8,105u8,116u8,104u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,95u8,99u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,45u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,97u8,117u8,116u8,104u8,95u8,107u8,101u8, - 121u8,95u8,97u8,110u8,100u8,95u8,111u8,114u8,105u8,103u8,105u8,110u8,97u8,116u8,105u8,110u8,103u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,95u8,116u8,97u8,98u8,108u8,101u8,21u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8, - 105u8,103u8,110u8,101u8,100u8,95u8,109u8,101u8,115u8,115u8,97u8,103u8,101u8,18u8,97u8,117u8,116u8,104u8,101u8,110u8, - 116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,15u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8, - 101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,17u8,103u8,117u8,105u8,100u8,95u8,99u8,114u8,101u8,97u8,116u8,105u8, - 111u8,110u8,95u8,110u8,117u8,109u8,20u8,99u8,111u8,105u8,110u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8, - 95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,107u8,101u8,121u8,95u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8, - 95u8,101u8,118u8,101u8,110u8,116u8,115u8,25u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,95u8,99u8,97u8,112u8, - 97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,111u8,102u8,102u8,101u8,114u8,23u8,115u8,105u8,103u8,110u8,101u8,114u8, - 95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,111u8,102u8,102u8,101u8,114u8,3u8,102u8,111u8, - 114u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,8u8,84u8,121u8,112u8,101u8,73u8,110u8,102u8,111u8,22u8,111u8,108u8, - 100u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8, - 22u8,110u8,101u8,119u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8, - 107u8,101u8,121u8,11u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,109u8,97u8,112u8,5u8,84u8,97u8,98u8,108u8, - 101u8,17u8,114u8,101u8,99u8,105u8,112u8,105u8,101u8,110u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,14u8, - 115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,10u8,111u8,114u8,105u8,103u8,105u8, - 110u8,97u8,116u8,111u8,114u8,16u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,97u8,117u8,116u8,104u8,95u8,107u8, - 101u8,121u8,14u8,110u8,101u8,119u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,20u8,85u8,110u8, - 118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,100u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,9u8,83u8, - 105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,37u8,110u8,101u8,119u8,95u8,117u8,110u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,102u8,114u8,111u8,109u8, - 95u8,98u8,121u8,116u8,101u8,115u8,24u8,110u8,101u8,119u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8, - 95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,25u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8, - 101u8,95u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8,116u8,114u8,105u8,99u8,116u8,95u8,116u8,16u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,44u8,117u8,110u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,101u8,100u8,95u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8,116u8,111u8, - 95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,14u8, - 97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,8u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,4u8,110u8,111u8,110u8,101u8,9u8,110u8,111u8,116u8,95u8, - 102u8,111u8,117u8,110u8,100u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,8u8,99u8,111u8,110u8, - 116u8,97u8,105u8,110u8,115u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8, - 105u8,101u8,100u8,12u8,111u8,117u8,116u8,95u8,111u8,102u8,95u8,114u8,97u8,110u8,103u8,101u8,7u8,105u8,115u8,95u8, - 110u8,111u8,110u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,4u8,115u8, - 111u8,109u8,101u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8,8u8,115u8,104u8,97u8,51u8,95u8,50u8,53u8,54u8,10u8, - 116u8,111u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8,6u8,98u8, - 111u8,114u8,114u8,111u8,119u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,3u8,110u8,101u8,119u8,3u8,103u8,101u8,116u8,12u8,115u8,119u8,97u8, - 112u8,95u8,111u8,114u8,95u8,102u8,105u8,108u8,108u8,7u8,116u8,121u8,112u8,101u8,95u8,111u8,102u8,10u8,101u8,109u8, - 105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,15u8,117u8,110u8,97u8, - 117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,101u8,100u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,3u8, - 97u8,100u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,2u8,1u8,255u8,3u8,8u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,1u8, - 0u8,3u8,8u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,18u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,0u8,4u8,16u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 255u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,1u8,1u8,10u8,2u8,33u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,5u8,32u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,2u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,5u8,32u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,6u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,8u8,5u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,9u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,10u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8, - 49u8,235u8,16u8,21u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,68u8,50u8,53u8,53u8,49u8,57u8, - 95u8,83u8,67u8,72u8,69u8,77u8,69u8,100u8,83u8,99u8,104u8,101u8,109u8,101u8,32u8,105u8,100u8,101u8,110u8,116u8, - 105u8,102u8,105u8,101u8,114u8,32u8,102u8,111u8,114u8,32u8,69u8,100u8,50u8,53u8,53u8,49u8,57u8,32u8,115u8,105u8, - 103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,32u8,117u8,115u8,101u8,100u8,32u8,116u8,111u8,32u8,100u8,101u8,114u8, - 105u8,118u8,101u8,32u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,32u8,107u8, - 101u8,121u8,115u8,32u8,102u8,111u8,114u8,32u8,69u8,100u8,50u8,53u8,53u8,49u8,57u8,32u8,112u8,117u8,98u8,108u8, - 105u8,99u8,32u8,107u8,101u8,121u8,115u8,46u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,65u8,67u8, - 67u8,79u8,85u8,78u8,84u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8, - 22u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8, - 115u8,116u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8, - 95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,22u8,65u8,99u8,99u8,111u8, - 117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,83u8,69u8,81u8,85u8,69u8,78u8,67u8,69u8,95u8,78u8,85u8,77u8, - 66u8,69u8,82u8,95u8,84u8,79u8,79u8,95u8,66u8,73u8,71u8,51u8,83u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8, - 32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,115u8,32u8,116u8,104u8,101u8,32u8, - 109u8,97u8,120u8,105u8,109u8,117u8,109u8,32u8,118u8,97u8,108u8,117u8,101u8,32u8,102u8,111u8,114u8,32u8,97u8,32u8, - 117u8,54u8,52u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,77u8,65u8,76u8,70u8,79u8,82u8,77u8, - 69u8,68u8,95u8,65u8,85u8,84u8,72u8,69u8,78u8,84u8,73u8,67u8,65u8,84u8,73u8,79u8,78u8,95u8,75u8,69u8, - 89u8,53u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,97u8,117u8,116u8,104u8,101u8, - 110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,32u8,107u8,101u8,121u8,32u8,104u8,97u8,115u8,32u8,97u8,110u8, - 32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,108u8,101u8,110u8,103u8,116u8,104u8,5u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,24u8,69u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,82u8,69u8,83u8,69u8,82u8,86u8,69u8,68u8, - 95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,49u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,99u8,114u8,101u8,97u8, - 116u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,98u8,101u8,99u8,97u8,117u8,115u8,101u8,32u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,32u8,105u8,115u8,32u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,11u8,69u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,71u8,65u8,83u8,42u8,84u8, - 114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,101u8,100u8,32u8, - 105u8,116u8,115u8,32u8,97u8,108u8,108u8,111u8,99u8,97u8,116u8,101u8,100u8,32u8,109u8,97u8,120u8,32u8,103u8,97u8, - 115u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,87u8,82u8,79u8,78u8,71u8,95u8,67u8,85u8,82u8, - 82u8,69u8,78u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8,67u8,95u8,75u8,69u8,89u8,43u8,83u8,112u8,101u8,99u8, - 105u8,102u8,105u8,101u8,100u8,32u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,112u8,117u8,98u8,108u8,105u8,99u8, - 32u8,107u8,101u8,121u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,99u8,111u8,114u8,114u8,101u8,99u8,116u8,8u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,80u8,82u8,79u8, - 79u8,70u8,95u8,79u8,70u8,95u8,75u8,78u8,79u8,87u8,76u8,69u8,68u8,71u8,69u8,83u8,83u8,112u8,101u8,99u8, - 105u8,102u8,105u8,101u8,100u8,32u8,112u8,114u8,111u8,111u8,102u8,32u8,111u8,102u8,32u8,107u8,110u8,111u8,119u8,108u8, - 101u8,100u8,103u8,101u8,32u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,32u8,116u8,111u8,32u8,112u8,114u8,111u8, - 118u8,101u8,32u8,111u8,119u8,110u8,101u8,114u8,115u8,104u8,105u8,112u8,32u8,111u8,102u8,32u8,97u8,32u8,112u8,117u8, - 98u8,108u8,105u8,99u8,32u8,107u8,101u8,121u8,32u8,105u8,115u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,9u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,78u8,79u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8, - 84u8,89u8,83u8,84u8,104u8,101u8,32u8,99u8,97u8,108u8,108u8,101u8,114u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8, - 111u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,97u8,32u8,100u8,105u8,103u8,105u8,116u8,97u8,108u8,45u8,115u8,105u8, - 103u8,110u8,97u8,116u8,117u8,114u8,101u8,45u8,98u8,97u8,115u8,101u8,100u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8, - 108u8,105u8,116u8,121u8,32u8,116u8,111u8,32u8,99u8,97u8,108u8,108u8,32u8,116u8,104u8,105u8,115u8,32u8,102u8,117u8, - 110u8,99u8,116u8,105u8,111u8,110u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,35u8,69u8,73u8,78u8,86u8,65u8, - 76u8,73u8,68u8,95u8,65u8,67u8,67u8,69u8,80u8,84u8,95u8,82u8,79u8,84u8,65u8,84u8,73u8,79u8,78u8,95u8, - 67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8,89u8,81u8,84u8,104u8,101u8,32u8,99u8,97u8,108u8,108u8,101u8, - 114u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,97u8,32u8,118u8,97u8, - 108u8,105u8,100u8,32u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,32u8,111u8,102u8,102u8,101u8,114u8,32u8,102u8,114u8,111u8,109u8,32u8,116u8,104u8,101u8,32u8,111u8, - 116u8,104u8,101u8,114u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 36u8,69u8,78u8,79u8,95u8,86u8,65u8,76u8,73u8,68u8,95u8,70u8,82u8,65u8,77u8,69u8,87u8,79u8,82u8,75u8, - 95u8,82u8,69u8,83u8,69u8,82u8,86u8,69u8,68u8,95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,69u8,65u8,100u8, - 100u8,114u8,101u8,115u8,115u8,32u8,116u8,111u8,32u8,99u8,114u8,101u8,97u8,116u8,101u8,32u8,105u8,115u8,32u8,110u8, - 111u8,116u8,32u8,97u8,32u8,118u8,97u8,108u8,105u8,100u8,32u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,32u8, - 97u8,100u8,100u8,114u8,101u8,115u8,115u8,32u8,102u8,111u8,114u8,32u8,65u8,112u8,116u8,111u8,115u8,32u8,102u8,114u8, - 97u8,109u8,101u8,119u8,111u8,114u8,107u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,73u8,78u8,86u8, - 65u8,76u8,73u8,68u8,95u8,83u8,67u8,72u8,69u8,77u8,69u8,129u8,1u8,83u8,112u8,101u8,99u8,105u8,102u8,105u8, - 101u8,100u8,32u8,115u8,99u8,104u8,101u8,109u8,101u8,32u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,32u8,116u8, - 111u8,32u8,112u8,114u8,111u8,99u8,101u8,101u8,100u8,32u8,119u8,105u8,116u8,104u8,32u8,116u8,104u8,101u8,32u8,115u8, - 109u8,97u8,114u8,116u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8, - 105u8,111u8,110u8,32u8,45u8,32u8,99u8,97u8,110u8,32u8,111u8,110u8,108u8,121u8,32u8,98u8,101u8,32u8,69u8,68u8, - 50u8,53u8,53u8,49u8,57u8,95u8,83u8,67u8,72u8,69u8,77u8,69u8,40u8,48u8,41u8,32u8,79u8,82u8,32u8,77u8, - 85u8,76u8,84u8,73u8,95u8,69u8,68u8,50u8,53u8,53u8,49u8,57u8,95u8,83u8,67u8,72u8,69u8,77u8,69u8,40u8, - 49u8,41u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8, - 79u8,82u8,73u8,71u8,73u8,78u8,65u8,84u8,73u8,78u8,71u8,95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,107u8, - 65u8,98u8,111u8,114u8,116u8,32u8,116u8,104u8,101u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8, - 110u8,32u8,105u8,102u8,32u8,116u8,104u8,101u8,32u8,101u8,120u8,112u8,101u8,99u8,116u8,101u8,100u8,32u8,111u8,114u8, - 105u8,103u8,105u8,110u8,97u8,116u8,105u8,110u8,103u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,32u8,105u8,115u8, - 32u8,100u8,105u8,102u8,102u8,101u8,114u8,101u8,110u8,116u8,32u8,102u8,114u8,111u8,109u8,32u8,116u8,104u8,101u8,32u8, - 111u8,114u8,105u8,103u8,105u8,110u8,97u8,116u8,105u8,110u8,103u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,32u8,111u8, - 110u8,45u8,99u8,104u8,97u8,105u8,110u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,78u8,79u8,95u8, - 83u8,85u8,67u8,72u8,95u8,83u8,73u8,71u8,78u8,69u8,82u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8, - 84u8,89u8,62u8,84u8,104u8,101u8,32u8,115u8,105u8,103u8,110u8,101u8,114u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8, - 108u8,105u8,116u8,121u8,32u8,111u8,102u8,102u8,101u8,114u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,101u8, - 120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8,101u8,32u8,103u8,105u8,118u8,101u8,110u8,32u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,82u8,69u8,83u8,79u8,85u8, - 82u8,67u8,69u8,95u8,65u8,67u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,60u8, - 65u8,110u8,32u8,97u8,116u8,116u8,101u8,109u8,112u8,116u8,32u8,116u8,111u8,32u8,99u8,114u8,101u8,97u8,116u8,101u8, - 32u8,97u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8, - 111u8,110u8,32u8,97u8,32u8,99u8,108u8,97u8,105u8,109u8,101u8,100u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8, - 16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,65u8,76u8, - 82u8,69u8,65u8,68u8,89u8,95u8,85u8,83u8,69u8,68u8,86u8,65u8,110u8,32u8,97u8,116u8,116u8,101u8,109u8,112u8, - 116u8,32u8,116u8,111u8,32u8,99u8,114u8,101u8,97u8,116u8,101u8,32u8,97u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8, - 99u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,111u8,110u8,32u8,97u8,110u8,32u8,97u8,99u8,99u8, - 111u8,117u8,110u8,116u8,32u8,116u8,104u8,97u8,116u8,32u8,104u8,97u8,115u8,32u8,97u8,32u8,99u8,111u8,109u8,109u8, - 105u8,116u8,116u8,101u8,100u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,17u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,31u8,69u8,79u8,70u8,70u8,69u8,82u8,69u8,82u8,95u8,65u8,68u8,68u8,82u8,69u8, - 83u8,83u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,29u8,79u8,102u8, - 102u8,101u8,114u8,101u8,114u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8, - 116u8,32u8,101u8,120u8,105u8,115u8,116u8,18u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,78u8,79u8,95u8, - 83u8,85u8,67u8,72u8,95u8,82u8,79u8,84u8,65u8,84u8,73u8,79u8,78u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8, - 76u8,73u8,84u8,89u8,95u8,79u8,70u8,70u8,69u8,82u8,86u8,84u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8, - 102u8,105u8,101u8,100u8,32u8,114u8,111u8,116u8,97u8,116u8,105u8,111u8,110u8,32u8,99u8,97u8,112u8,97u8,98u8,108u8, - 105u8,116u8,121u8,32u8,111u8,102u8,102u8,101u8,114u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8, - 120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8, - 100u8,32u8,111u8,102u8,102u8,101u8,114u8,101u8,114u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,19u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,29u8,69u8,78u8,79u8,95u8,83u8,73u8,71u8,78u8,69u8,82u8,95u8,67u8,65u8,80u8, - 65u8,66u8,73u8,76u8,73u8,84u8,89u8,95u8,79u8,70u8,70u8,69u8,82u8,69u8,68u8,0u8,20u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,31u8,69u8,69u8,88u8,67u8,69u8,69u8,68u8,69u8,68u8,95u8,77u8,65u8,88u8,95u8,71u8, - 85u8,73u8,68u8,95u8,67u8,82u8,69u8,65u8,84u8,73u8,79u8,78u8,95u8,78u8,85u8,77u8,0u8,0u8,6u8,9u8, - 101u8,120u8,105u8,115u8,116u8,115u8,95u8,97u8,116u8,1u8,1u8,0u8,19u8,103u8,101u8,116u8,95u8,115u8,101u8,113u8, - 117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,1u8,1u8,0u8,22u8,103u8,101u8,116u8,95u8, - 97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,1u8,1u8, - 0u8,26u8,103u8,101u8,116u8,95u8,103u8,117u8,105u8,100u8,95u8,110u8,101u8,120u8,116u8,95u8,99u8,114u8,101u8,97u8, - 116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8,1u8,1u8,0u8,28u8,105u8,115u8,95u8,115u8,105u8,103u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,111u8,102u8,102u8,101u8,114u8,101u8,100u8, - 1u8,1u8,0u8,31u8,103u8,101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,95u8,111u8,102u8,102u8,101u8,114u8,95u8,102u8,111u8,114u8,1u8,1u8,0u8,0u8,2u8, - 7u8,67u8,10u8,2u8,68u8,3u8,69u8,3u8,70u8,11u8,13u8,1u8,8u8,2u8,71u8,11u8,13u8,1u8,8u8,3u8, - 72u8,11u8,1u8,1u8,8u8,5u8,73u8,11u8,1u8,1u8,8u8,9u8,1u8,2u8,1u8,74u8,11u8,14u8,1u8,5u8, - 2u8,2u8,1u8,20u8,8u8,15u8,3u8,2u8,2u8,77u8,10u8,2u8,78u8,10u8,2u8,4u8,2u8,1u8,79u8,11u8, - 16u8,2u8,5u8,5u8,5u8,2u8,1u8,5u8,5u8,6u8,2u8,2u8,68u8,3u8,81u8,5u8,7u8,2u8,4u8,7u8, - 2u8,68u8,3u8,82u8,5u8,81u8,5u8,8u8,2u8,4u8,68u8,3u8,83u8,5u8,84u8,5u8,85u8,10u8,2u8,9u8, - 2u8,1u8,5u8,5u8,10u8,2u8,2u8,68u8,3u8,81u8,5u8,11u8,2u8,3u8,68u8,3u8,82u8,5u8,81u8,5u8, - 1u8,38u8,1u8,39u8,0u8,0u8,0u8,0u8,20u8,54u8,10u8,0u8,7u8,5u8,33u8,4u8,24u8,11u8,1u8,17u8, - 31u8,12u8,5u8,11u8,2u8,17u8,32u8,12u8,7u8,14u8,7u8,14u8,5u8,11u8,3u8,20u8,56u8,0u8,4u8,17u8, - 5u8,20u8,7u8,9u8,17u8,34u8,39u8,14u8,5u8,17u8,35u8,12u8,4u8,5u8,52u8,11u8,0u8,7u8,24u8,33u8, - 4u8,44u8,11u8,1u8,17u8,36u8,12u8,6u8,11u8,2u8,17u8,37u8,12u8,8u8,14u8,8u8,14u8,6u8,11u8,3u8, - 20u8,56u8,1u8,4u8,41u8,5u8,49u8,7u8,9u8,17u8,34u8,39u8,11u8,3u8,1u8,7u8,10u8,17u8,34u8,39u8, - 14u8,6u8,17u8,39u8,12u8,4u8,11u8,4u8,2u8,1u8,3u8,0u8,0u8,30u8,37u8,10u8,0u8,41u8,0u8,32u8, - 4u8,5u8,5u8,8u8,7u8,1u8,17u8,40u8,39u8,10u8,0u8,7u8,26u8,34u8,4u8,17u8,10u8,0u8,7u8,27u8, - 34u8,12u8,1u8,5u8,19u8,9u8,12u8,1u8,11u8,1u8,4u8,26u8,10u8,0u8,7u8,28u8,34u8,12u8,2u8,5u8, - 28u8,9u8,12u8,2u8,11u8,2u8,4u8,31u8,5u8,34u8,7u8,4u8,17u8,34u8,39u8,11u8,0u8,17u8,2u8,2u8, - 2u8,0u8,0u8,0u8,31u8,41u8,10u8,0u8,17u8,41u8,12u8,5u8,14u8,0u8,56u8,2u8,12u8,1u8,14u8,1u8, - 65u8,33u8,6u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,12u8,5u8,15u8,7u8,11u8,17u8,34u8, - 39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,0u8,13u8,3u8,17u8,43u8,56u8,3u8, - 12u8,2u8,11u8,0u8,13u8,3u8,17u8,43u8,56u8,4u8,12u8,4u8,14u8,5u8,11u8,1u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,11u8,3u8,11u8,2u8,11u8,4u8,56u8,5u8,57u8,0u8,56u8,5u8,57u8,1u8,18u8, - 0u8,45u8,0u8,11u8,5u8,2u8,3u8,1u8,0u8,1u8,0u8,40u8,28u8,10u8,1u8,17u8,9u8,4u8,4u8,5u8, - 9u8,11u8,0u8,1u8,7u8,17u8,17u8,46u8,39u8,10u8,1u8,43u8,0u8,12u8,2u8,11u8,0u8,17u8,47u8,12u8, - 3u8,11u8,2u8,16u8,0u8,55u8,0u8,14u8,3u8,56u8,6u8,4u8,22u8,5u8,25u8,7u8,15u8,17u8,46u8,39u8, - 11u8,1u8,17u8,41u8,2u8,4u8,3u8,0u8,0u8,42u8,98u8,10u8,0u8,7u8,27u8,33u8,4u8,7u8,8u8,12u8, - 1u8,5u8,11u8,10u8,0u8,7u8,29u8,33u8,12u8,1u8,11u8,1u8,4u8,16u8,8u8,12u8,2u8,5u8,20u8,10u8, - 0u8,7u8,28u8,33u8,12u8,2u8,11u8,2u8,4u8,25u8,8u8,12u8,3u8,5u8,29u8,10u8,0u8,7u8,30u8,33u8, - 12u8,3u8,11u8,3u8,4u8,34u8,8u8,12u8,4u8,5u8,38u8,10u8,0u8,7u8,31u8,33u8,12u8,4u8,11u8,4u8, - 4u8,43u8,8u8,12u8,5u8,5u8,47u8,10u8,0u8,7u8,32u8,33u8,12u8,5u8,11u8,5u8,4u8,52u8,8u8,12u8, - 6u8,5u8,56u8,10u8,0u8,7u8,33u8,33u8,12u8,6u8,11u8,6u8,4u8,61u8,8u8,12u8,7u8,5u8,65u8,10u8, - 0u8,7u8,34u8,33u8,12u8,7u8,11u8,7u8,4u8,70u8,8u8,12u8,8u8,5u8,74u8,10u8,0u8,7u8,35u8,33u8, - 12u8,8u8,11u8,8u8,4u8,79u8,8u8,12u8,9u8,5u8,83u8,10u8,0u8,7u8,36u8,33u8,12u8,9u8,11u8,9u8, - 4u8,86u8,5u8,89u8,7u8,16u8,17u8,49u8,39u8,10u8,0u8,17u8,2u8,12u8,10u8,11u8,0u8,18u8,9u8,12u8, - 11u8,11u8,10u8,11u8,11u8,2u8,5u8,1u8,0u8,1u8,0u8,43u8,23u8,11u8,0u8,17u8,47u8,12u8,2u8,10u8, - 2u8,42u8,0u8,12u8,1u8,11u8,2u8,10u8,1u8,15u8,2u8,17u8,43u8,12u8,3u8,11u8,1u8,16u8,2u8,20u8, - 7u8,22u8,35u8,4u8,18u8,5u8,21u8,7u8,6u8,17u8,50u8,39u8,11u8,3u8,2u8,6u8,1u8,0u8,1u8,0u8, - 44u8,61u8,11u8,0u8,17u8,47u8,12u8,2u8,14u8,2u8,11u8,1u8,17u8,7u8,12u8,7u8,10u8,7u8,17u8,9u8, - 4u8,38u8,10u8,7u8,43u8,0u8,12u8,4u8,10u8,4u8,16u8,0u8,55u8,0u8,56u8,7u8,4u8,19u8,5u8,24u8, - 11u8,4u8,1u8,7u8,19u8,17u8,40u8,39u8,11u8,4u8,16u8,3u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,31u8,5u8,34u8,7u8,2u8,17u8,52u8,39u8,10u8,7u8,17u8,41u8,12u8,3u8,5u8,41u8, - 10u8,7u8,17u8,2u8,12u8,3u8,11u8,3u8,12u8,6u8,14u8,6u8,7u8,25u8,17u8,27u8,10u8,7u8,42u8,0u8, - 12u8,5u8,10u8,7u8,56u8,8u8,11u8,5u8,15u8,0u8,54u8,0u8,21u8,11u8,7u8,18u8,9u8,12u8,8u8,11u8, - 6u8,11u8,8u8,2u8,7u8,1u8,0u8,0u8,1u8,13u8,11u8,0u8,56u8,2u8,12u8,2u8,13u8,2u8,11u8,1u8, - 56u8,9u8,13u8,2u8,7u8,0u8,68u8,33u8,11u8,2u8,17u8,55u8,17u8,56u8,2u8,8u8,1u8,0u8,0u8,13u8, - 5u8,11u8,0u8,16u8,4u8,20u8,17u8,41u8,2u8,9u8,1u8,0u8,0u8,13u8,3u8,11u8,0u8,41u8,0u8,2u8, - 10u8,1u8,0u8,1u8,0u8,13u8,5u8,11u8,0u8,43u8,0u8,16u8,5u8,20u8,2u8,11u8,1u8,0u8,1u8,0u8, - 13u8,5u8,11u8,0u8,43u8,0u8,16u8,2u8,20u8,2u8,12u8,1u8,0u8,1u8,0u8,13u8,5u8,11u8,0u8,43u8, - 0u8,16u8,3u8,20u8,2u8,13u8,1u8,0u8,0u8,13u8,4u8,11u8,0u8,16u8,4u8,20u8,2u8,14u8,1u8,0u8, - 1u8,0u8,48u8,20u8,11u8,0u8,43u8,0u8,12u8,1u8,10u8,1u8,16u8,0u8,55u8,0u8,56u8,10u8,4u8,9u8, - 5u8,14u8,11u8,1u8,1u8,7u8,13u8,17u8,46u8,39u8,11u8,1u8,16u8,0u8,55u8,0u8,56u8,11u8,20u8,2u8, - 15u8,3u8,0u8,1u8,0u8,49u8,23u8,11u8,0u8,42u8,0u8,15u8,3u8,12u8,1u8,10u8,1u8,20u8,53u8,7u8, - 23u8,35u8,4u8,11u8,5u8,16u8,11u8,1u8,1u8,7u8,20u8,17u8,50u8,39u8,10u8,1u8,20u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,1u8,21u8,2u8,16u8,3u8,0u8,0u8,13u8,7u8,10u8,0u8,17u8, - 59u8,11u8,0u8,56u8,12u8,18u8,4u8,45u8,4u8,2u8,17u8,1u8,0u8,1u8,0u8,13u8,6u8,11u8,0u8,43u8, - 0u8,16u8,0u8,55u8,0u8,56u8,10u8,2u8,18u8,1u8,0u8,1u8,0u8,13u8,4u8,11u8,0u8,17u8,5u8,56u8, - 13u8,2u8,19u8,1u8,4u8,1u8,0u8,52u8,106u8,11u8,0u8,17u8,47u8,12u8,6u8,10u8,4u8,17u8,9u8,4u8, - 7u8,5u8,10u8,7u8,3u8,17u8,46u8,39u8,10u8,6u8,42u8,0u8,12u8,5u8,17u8,61u8,10u8,5u8,16u8,3u8, - 20u8,11u8,6u8,10u8,4u8,18u8,7u8,12u8,9u8,10u8,2u8,7u8,5u8,33u8,4u8,58u8,11u8,3u8,17u8,31u8, - 12u8,10u8,14u8,10u8,17u8,35u8,12u8,7u8,10u8,5u8,16u8,5u8,20u8,11u8,7u8,33u8,4u8,38u8,5u8,43u8, - 11u8,5u8,1u8,7u8,21u8,17u8,34u8,39u8,11u8,1u8,17u8,32u8,12u8,12u8,14u8,12u8,14u8,10u8,11u8,9u8, - 56u8,14u8,4u8,52u8,5u8,57u8,11u8,5u8,1u8,7u8,9u8,17u8,34u8,39u8,5u8,99u8,11u8,2u8,7u8,24u8, - 33u8,4u8,94u8,11u8,3u8,17u8,36u8,12u8,11u8,14u8,11u8,17u8,39u8,12u8,8u8,10u8,5u8,16u8,5u8,20u8, - 11u8,8u8,33u8,4u8,75u8,5u8,80u8,11u8,5u8,1u8,7u8,21u8,17u8,34u8,39u8,11u8,1u8,17u8,37u8,12u8, - 13u8,14u8,13u8,14u8,11u8,11u8,9u8,56u8,15u8,4u8,89u8,5u8,99u8,11u8,5u8,1u8,7u8,9u8,17u8,34u8, - 39u8,11u8,5u8,1u8,7u8,10u8,17u8,34u8,39u8,11u8,5u8,15u8,6u8,54u8,1u8,11u8,4u8,56u8,16u8,1u8, - 2u8,20u8,1u8,4u8,1u8,0u8,55u8,30u8,11u8,0u8,17u8,47u8,12u8,6u8,10u8,4u8,17u8,9u8,4u8,7u8, - 5u8,10u8,7u8,3u8,17u8,46u8,39u8,10u8,6u8,17u8,12u8,10u8,6u8,10u8,4u8,18u8,11u8,12u8,5u8,10u8, - 6u8,11u8,2u8,11u8,3u8,11u8,1u8,11u8,5u8,56u8,17u8,11u8,6u8,42u8,0u8,15u8,0u8,54u8,0u8,11u8, - 4u8,56u8,16u8,1u8,2u8,21u8,3u8,0u8,1u8,0u8,13u8,7u8,11u8,0u8,42u8,0u8,15u8,7u8,56u8,18u8, - 18u8,2u8,56u8,19u8,2u8,22u8,1u8,4u8,1u8,0u8,13u8,8u8,11u8,0u8,17u8,47u8,42u8,0u8,15u8,6u8, - 54u8,1u8,56u8,20u8,1u8,2u8,23u8,1u8,4u8,1u8,0u8,13u8,8u8,11u8,0u8,17u8,47u8,42u8,0u8,15u8, - 0u8,54u8,0u8,56u8,20u8,1u8,2u8,24u8,1u8,4u8,1u8,0u8,13u8,26u8,10u8,1u8,17u8,9u8,4u8,4u8, - 5u8,9u8,11u8,0u8,1u8,7u8,3u8,17u8,46u8,39u8,10u8,0u8,17u8,47u8,42u8,0u8,16u8,6u8,55u8,1u8, - 14u8,1u8,56u8,6u8,4u8,18u8,5u8,23u8,11u8,0u8,1u8,7u8,14u8,17u8,46u8,39u8,11u8,0u8,17u8,22u8, - 2u8,25u8,1u8,4u8,1u8,0u8,13u8,26u8,10u8,1u8,17u8,9u8,4u8,4u8,5u8,9u8,11u8,0u8,1u8,7u8, - 3u8,17u8,46u8,39u8,10u8,0u8,17u8,47u8,42u8,0u8,16u8,0u8,55u8,0u8,14u8,1u8,56u8,6u8,4u8,18u8, - 5u8,23u8,11u8,0u8,1u8,7u8,15u8,17u8,46u8,39u8,11u8,0u8,17u8,23u8,2u8,26u8,1u8,4u8,2u8,0u8, - 4u8,60u8,93u8,11u8,0u8,17u8,47u8,12u8,8u8,10u8,8u8,17u8,9u8,4u8,7u8,5u8,10u8,7u8,3u8,17u8, - 46u8,39u8,10u8,8u8,42u8,0u8,12u8,7u8,10u8,1u8,7u8,5u8,33u8,4u8,36u8,10u8,2u8,17u8,31u8,12u8, - 13u8,14u8,13u8,17u8,35u8,12u8,11u8,10u8,7u8,16u8,5u8,20u8,11u8,11u8,33u8,4u8,30u8,5u8,35u8,11u8, - 7u8,1u8,7u8,21u8,17u8,66u8,39u8,5u8,63u8,10u8,1u8,7u8,24u8,33u8,4u8,58u8,10u8,2u8,17u8,36u8, - 12u8,14u8,14u8,14u8,17u8,39u8,12u8,12u8,10u8,7u8,16u8,5u8,20u8,11u8,12u8,33u8,4u8,53u8,5u8,63u8, - 11u8,7u8,1u8,7u8,21u8,17u8,66u8,39u8,11u8,7u8,1u8,7u8,10u8,17u8,34u8,39u8,10u8,7u8,16u8,5u8, - 20u8,17u8,56u8,12u8,10u8,10u8,7u8,16u8,3u8,20u8,10u8,8u8,11u8,10u8,10u8,4u8,18u8,8u8,12u8,9u8, - 11u8,1u8,11u8,2u8,11u8,5u8,14u8,9u8,17u8,0u8,1u8,11u8,3u8,11u8,4u8,11u8,6u8,14u8,9u8,17u8, - 0u8,12u8,15u8,11u8,8u8,11u8,7u8,11u8,15u8,17u8,29u8,2u8,27u8,3u8,0u8,1u8,0u8,61u8,27u8,11u8, - 0u8,17u8,47u8,12u8,3u8,10u8,3u8,17u8,9u8,4u8,7u8,5u8,10u8,7u8,3u8,17u8,46u8,39u8,14u8,1u8, - 65u8,33u8,6u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,16u8,5u8,19u8,7u8,11u8,17u8,34u8, - 39u8,11u8,3u8,42u8,0u8,12u8,2u8,11u8,1u8,11u8,2u8,15u8,5u8,21u8,2u8,28u8,1u8,4u8,2u8,0u8, - 4u8,62u8,53u8,10u8,1u8,17u8,9u8,4u8,4u8,5u8,9u8,11u8,0u8,1u8,7u8,17u8,17u8,46u8,39u8,11u8, - 0u8,17u8,47u8,12u8,7u8,10u8,1u8,43u8,0u8,12u8,9u8,10u8,9u8,16u8,6u8,55u8,1u8,14u8,7u8,56u8, - 6u8,4u8,22u8,5u8,27u8,11u8,9u8,1u8,7u8,14u8,17u8,46u8,39u8,11u8,9u8,16u8,5u8,20u8,17u8,56u8, - 12u8,6u8,11u8,7u8,17u8,12u8,10u8,1u8,11u8,6u8,10u8,3u8,18u8,8u8,12u8,5u8,11u8,2u8,11u8,3u8, - 11u8,4u8,14u8,5u8,17u8,0u8,12u8,8u8,10u8,1u8,42u8,0u8,12u8,10u8,11u8,1u8,11u8,10u8,11u8,8u8, - 17u8,29u8,2u8,29u8,0u8,0u8,1u8,4u8,63u8,50u8,7u8,27u8,42u8,4u8,15u8,8u8,12u8,4u8,10u8,1u8, - 16u8,5u8,20u8,17u8,56u8,12u8,5u8,10u8,4u8,10u8,5u8,12u8,3u8,46u8,11u8,3u8,56u8,21u8,4u8,30u8, - 10u8,0u8,10u8,4u8,11u8,5u8,56u8,22u8,33u8,4u8,23u8,5u8,30u8,11u8,4u8,1u8,11u8,1u8,1u8,7u8, - 8u8,17u8,46u8,39u8,10u8,2u8,17u8,56u8,12u8,6u8,11u8,4u8,11u8,6u8,11u8,0u8,56u8,23u8,10u8,1u8, - 15u8,9u8,10u8,1u8,16u8,5u8,20u8,10u8,2u8,18u8,3u8,56u8,24u8,11u8,2u8,11u8,1u8,15u8,5u8,21u8, - 2u8,30u8,1u8,0u8,1u8,0u8,68u8,74u8,11u8,0u8,42u8,0u8,12u8,5u8,10u8,1u8,7u8,5u8,33u8,4u8, - 36u8,11u8,2u8,17u8,31u8,12u8,8u8,14u8,8u8,17u8,35u8,12u8,6u8,11u8,5u8,16u8,5u8,20u8,11u8,6u8, - 33u8,4u8,20u8,5u8,23u8,7u8,21u8,17u8,34u8,39u8,11u8,3u8,17u8,32u8,12u8,10u8,14u8,10u8,14u8,8u8, - 11u8,4u8,56u8,25u8,4u8,32u8,5u8,35u8,7u8,9u8,17u8,34u8,39u8,5u8,73u8,11u8,1u8,7u8,24u8,33u8, - 4u8,68u8,11u8,2u8,17u8,36u8,12u8,9u8,14u8,9u8,17u8,39u8,12u8,7u8,11u8,5u8,16u8,5u8,20u8,11u8, - 7u8,33u8,4u8,53u8,5u8,56u8,7u8,21u8,17u8,34u8,39u8,11u8,3u8,17u8,37u8,12u8,11u8,14u8,11u8,14u8, - 9u8,11u8,4u8,56u8,26u8,4u8,65u8,5u8,73u8,7u8,9u8,17u8,34u8,39u8,11u8,5u8,1u8,7u8,10u8,17u8, - 34u8,39u8,2u8,0u8,6u8,1u8,0u8,0u8,2u8,0u8,1u8,9u8,0u8,0u8,0u8,0u8,5u8,0u8,3u8,4u8, - 0u8,0u8,4u8,1u8,39u8,1u8,38u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8, - ]; - vector::push_back(&mut code, chunk5); - let chunk6 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,2u8,2u8,2u8,4u8,3u8,6u8,25u8,5u8, - 31u8,15u8,7u8,46u8,60u8,8u8,106u8,32u8,6u8,138u8,1u8,30u8,16u8,168u8,1u8,173u8,2u8,10u8,213u8,3u8, - 9u8,12u8,222u8,3u8,28u8,13u8,250u8,3u8,2u8,0u8,0u8,0u8,1u8,4u8,0u8,0u8,2u8,0u8,1u8,0u8, - 0u8,3u8,2u8,1u8,0u8,0u8,4u8,3u8,4u8,0u8,0u8,5u8,3u8,4u8,0u8,0u8,6u8,0u8,1u8,0u8, - 2u8,7u8,8u8,0u8,4u8,0u8,1u8,8u8,0u8,1u8,6u8,8u8,0u8,1u8,4u8,10u8,97u8,103u8,103u8,114u8, - 101u8,103u8,97u8,116u8,111u8,114u8,10u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,3u8,97u8,100u8, - 100u8,7u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,5u8,108u8,105u8,109u8,105u8,116u8,4u8,114u8,101u8,97u8,100u8, - 3u8,115u8,117u8,98u8,6u8,104u8,97u8,110u8,100u8,108u8,101u8,3u8,107u8,101u8,121u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8, - 116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,152u8,2u8,3u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,65u8,71u8,71u8,82u8,69u8,71u8,65u8,84u8,79u8,82u8,95u8, - 79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,57u8,84u8,104u8,101u8,32u8,118u8,97u8,108u8,117u8,101u8,32u8,111u8, - 102u8,32u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,32u8,111u8,118u8,101u8,114u8,102u8,108u8,111u8, - 119u8,115u8,46u8,32u8,82u8,97u8,105u8,115u8,101u8,100u8,32u8,98u8,121u8,32u8,110u8,97u8,116u8,105u8,118u8,101u8, - 32u8,99u8,111u8,100u8,101u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,65u8,71u8,71u8,82u8, - 69u8,71u8,65u8,84u8,79u8,82u8,95u8,85u8,78u8,68u8,69u8,82u8,70u8,76u8,79u8,87u8,76u8,84u8,104u8,101u8, - 32u8,118u8,97u8,108u8,117u8,101u8,32u8,111u8,102u8,32u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8, - 32u8,117u8,110u8,100u8,101u8,114u8,102u8,108u8,111u8,119u8,115u8,32u8,40u8,103u8,111u8,101u8,115u8,32u8,98u8,101u8, - 108u8,111u8,119u8,32u8,122u8,101u8,114u8,111u8,41u8,46u8,32u8,82u8,97u8,105u8,115u8,101u8,100u8,32u8,98u8,121u8, - 32u8,110u8,97u8,116u8,105u8,118u8,101u8,32u8,99u8,111u8,100u8,101u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,14u8,69u8,78u8,79u8,84u8,95u8,83u8,85u8,80u8,80u8,79u8,82u8,84u8,69u8,68u8,59u8,65u8,103u8,103u8, - 114u8,101u8,103u8,97u8,116u8,111u8,114u8,32u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,32u8,105u8,115u8,32u8,110u8, - 111u8,116u8,32u8,115u8,117u8,112u8,112u8,111u8,114u8,116u8,101u8,100u8,46u8,32u8,82u8,97u8,105u8,115u8,101u8,100u8, - 32u8,98u8,121u8,32u8,110u8,97u8,116u8,105u8,118u8,101u8,32u8,99u8,111u8,100u8,101u8,46u8,0u8,0u8,0u8,2u8, - 3u8,7u8,5u8,8u8,5u8,4u8,4u8,0u8,1u8,2u8,0u8,1u8,1u8,2u8,0u8,2u8,1u8,0u8,0u8,1u8, - 4u8,11u8,0u8,16u8,0u8,20u8,2u8,3u8,1u8,2u8,0u8,4u8,1u8,2u8,0u8,0u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk6); - let chunk7 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,10u8,2u8,10u8,16u8,3u8,26u8,37u8,4u8, - 63u8,2u8,5u8,65u8,34u8,7u8,99u8,135u8,2u8,8u8,234u8,2u8,32u8,6u8,138u8,3u8,44u8,16u8,182u8,3u8, - 102u8,10u8,156u8,4u8,9u8,12u8,165u8,4u8,72u8,15u8,237u8,4u8,4u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8, - 5u8,0u8,6u8,0u8,7u8,8u8,0u8,1u8,8u8,4u8,0u8,4u8,14u8,4u8,2u8,3u8,1u8,0u8,1u8,0u8, - 9u8,0u8,1u8,0u8,0u8,10u8,2u8,1u8,0u8,0u8,11u8,3u8,4u8,0u8,0u8,12u8,5u8,1u8,0u8,3u8, - 15u8,3u8,4u8,0u8,2u8,16u8,6u8,6u8,0u8,4u8,17u8,4u8,9u8,2u8,3u8,4u8,6u8,8u8,2u8,6u8, - 12u8,4u8,1u8,8u8,1u8,1u8,4u8,1u8,6u8,12u8,0u8,2u8,7u8,8u8,0u8,4u8,1u8,3u8,1u8,8u8, - 0u8,2u8,5u8,4u8,1u8,11u8,2u8,2u8,9u8,0u8,9u8,1u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8, - 19u8,111u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8, - 18u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,121u8,10u8, - 97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,16u8,115u8,121u8,115u8, - 116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,5u8,116u8,97u8,98u8,108u8,101u8,17u8, - 65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,70u8,97u8,99u8,116u8,111u8,114u8,121u8,10u8,65u8,103u8, - 103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,17u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,103u8,103u8,114u8, - 101u8,103u8,97u8,116u8,111u8,114u8,26u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8, - 97u8,116u8,111u8,114u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,29u8,105u8,110u8,105u8,116u8,105u8,97u8, - 108u8,105u8,122u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,102u8,97u8,99u8,116u8, - 111u8,114u8,121u8,14u8,110u8,101u8,119u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,13u8,112u8, - 104u8,97u8,110u8,116u8,111u8,109u8,95u8,116u8,97u8,98u8,108u8,101u8,5u8,84u8,97u8,98u8,108u8,101u8,22u8,97u8, - 115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8, - 107u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,3u8,110u8,101u8,119u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8, - 101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,82u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 29u8,69u8,65u8,71u8,71u8,82u8,69u8,71u8,65u8,84u8,79u8,82u8,95u8,70u8,65u8,67u8,84u8,79u8,82u8,89u8, - 95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,40u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8, - 114u8,32u8,102u8,97u8,99u8,116u8,111u8,114u8,121u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,112u8,117u8,98u8, - 108u8,105u8,115u8,104u8,101u8,100u8,32u8,121u8,101u8,116u8,46u8,0u8,0u8,0u8,2u8,1u8,13u8,11u8,2u8,2u8, - 5u8,4u8,0u8,1u8,0u8,1u8,0u8,4u8,5u8,11u8,0u8,17u8,4u8,11u8,1u8,17u8,1u8,2u8,1u8,3u8, - 0u8,1u8,0u8,4u8,12u8,7u8,1u8,41u8,0u8,4u8,4u8,5u8,7u8,7u8,0u8,17u8,5u8,39u8,7u8,1u8, - 42u8,0u8,11u8,0u8,17u8,3u8,2u8,2u8,3u8,0u8,0u8,7u8,9u8,10u8,0u8,17u8,4u8,56u8,0u8,18u8, - 0u8,12u8,1u8,11u8,0u8,11u8,1u8,45u8,0u8,2u8,3u8,0u8,2u8,0u8,0u8,0u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk7); - let chunk8 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,10u8,2u8,10u8,18u8,3u8,28u8,179u8,1u8, - 4u8,207u8,1u8,34u8,5u8,241u8,1u8,108u8,7u8,221u8,2u8,245u8,3u8,8u8,210u8,6u8,32u8,6u8,242u8,6u8, - 20u8,16u8,134u8,7u8,220u8,1u8,10u8,226u8,8u8,22u8,12u8,248u8,8u8,159u8,4u8,13u8,151u8,13u8,8u8,15u8, - 159u8,13u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,4u8,0u8,0u8,7u8,4u8, - 0u8,4u8,27u8,7u8,1u8,0u8,0u8,1u8,28u8,4u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,9u8,2u8,1u8, - 0u8,0u8,10u8,3u8,1u8,0u8,0u8,11u8,4u8,1u8,0u8,0u8,12u8,3u8,5u8,0u8,0u8,13u8,3u8,5u8, - 0u8,0u8,14u8,6u8,7u8,0u8,0u8,15u8,8u8,5u8,0u8,0u8,16u8,9u8,3u8,0u8,0u8,17u8,5u8,4u8, - 0u8,0u8,18u8,6u8,5u8,0u8,0u8,19u8,8u8,5u8,0u8,0u8,20u8,0u8,1u8,0u8,0u8,21u8,2u8,1u8, - 0u8,0u8,22u8,10u8,1u8,0u8,0u8,23u8,10u8,1u8,0u8,0u8,24u8,10u8,5u8,0u8,0u8,25u8,10u8,5u8, - 0u8,4u8,30u8,12u8,7u8,1u8,0u8,4u8,31u8,13u8,14u8,1u8,0u8,1u8,8u8,15u8,1u8,0u8,3u8,32u8, - 16u8,16u8,0u8,4u8,33u8,12u8,18u8,1u8,0u8,1u8,15u8,19u8,5u8,0u8,4u8,34u8,20u8,21u8,1u8,0u8, - 1u8,10u8,11u8,1u8,0u8,4u8,35u8,20u8,1u8,1u8,0u8,2u8,36u8,5u8,11u8,0u8,4u8,37u8,21u8,20u8, - 1u8,0u8,4u8,38u8,1u8,20u8,1u8,0u8,1u8,18u8,19u8,5u8,0u8,1u8,20u8,15u8,1u8,0u8,4u8,39u8, - 13u8,21u8,1u8,0u8,4u8,40u8,23u8,1u8,1u8,0u8,18u8,11u8,19u8,11u8,19u8,4u8,22u8,11u8,24u8,11u8, - 26u8,4u8,22u8,4u8,24u8,4u8,26u8,11u8,28u8,11u8,29u8,4u8,29u8,11u8,28u8,4u8,32u8,4u8,33u8,11u8, - 32u8,11u8,33u8,4u8,2u8,7u8,8u8,1u8,4u8,0u8,2u8,7u8,8u8,0u8,4u8,1u8,8u8,1u8,1u8,8u8, - 0u8,1u8,4u8,1u8,6u8,8u8,1u8,1u8,1u8,1u8,6u8,8u8,0u8,2u8,4u8,1u8,1u8,7u8,8u8,1u8, - 1u8,8u8,3u8,1u8,6u8,11u8,2u8,1u8,9u8,0u8,1u8,7u8,11u8,2u8,1u8,9u8,0u8,1u8,7u8,9u8, - 0u8,2u8,7u8,8u8,3u8,4u8,1u8,3u8,3u8,11u8,2u8,1u8,8u8,3u8,11u8,2u8,1u8,8u8,0u8,4u8, - 1u8,6u8,9u8,0u8,1u8,6u8,8u8,3u8,1u8,11u8,2u8,1u8,9u8,0u8,1u8,9u8,0u8,3u8,8u8,3u8, - 8u8,0u8,4u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8,9u8,0u8,4u8,99u8,111u8,105u8,110u8,19u8,111u8,112u8, - 116u8,105u8,111u8,110u8,97u8,108u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,10u8,97u8,103u8, - 103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,18u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8, - 102u8,97u8,99u8,116u8,111u8,114u8,121u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8, - 7u8,73u8,110u8,116u8,101u8,103u8,101u8,114u8,18u8,79u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,65u8,103u8,103u8, - 114u8,101u8,103u8,97u8,116u8,111u8,114u8,3u8,97u8,100u8,100u8,11u8,97u8,100u8,100u8,95u8,105u8,110u8,116u8,101u8, - 103u8,101u8,114u8,7u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,15u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8, - 105u8,110u8,116u8,101u8,103u8,101u8,114u8,27u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,111u8,112u8,116u8,105u8, - 111u8,110u8,97u8,108u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,24u8,100u8,101u8,115u8,116u8, - 114u8,111u8,121u8,95u8,111u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,95u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8, - 17u8,105u8,115u8,95u8,112u8,97u8,114u8,97u8,108u8,108u8,101u8,108u8,105u8,122u8,97u8,98u8,108u8,101u8,5u8,108u8, - 105u8,109u8,105u8,116u8,3u8,110u8,101u8,119u8,11u8,110u8,101u8,119u8,95u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8, - 4u8,114u8,101u8,97u8,100u8,12u8,114u8,101u8,97u8,100u8,95u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8,3u8,115u8, - 117u8,98u8,11u8,115u8,117u8,98u8,95u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8,6u8,115u8,119u8,105u8,116u8,99u8, - 104u8,19u8,115u8,119u8,105u8,116u8,99u8,104u8,95u8,97u8,110u8,100u8,95u8,122u8,101u8,114u8,111u8,95u8,111u8,117u8, - 116u8,33u8,115u8,119u8,105u8,116u8,99u8,104u8,95u8,116u8,111u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8, - 111u8,114u8,95u8,97u8,110u8,100u8,95u8,122u8,101u8,114u8,111u8,95u8,111u8,117u8,116u8,30u8,115u8,119u8,105u8,116u8, - 99u8,104u8,95u8,116u8,111u8,95u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8,95u8,97u8,110u8,100u8,95u8,122u8,101u8, - 114u8,111u8,95u8,111u8,117u8,116u8,5u8,118u8,97u8,108u8,117u8,101u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,10u8, - 65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,7u8,105u8,110u8,116u8,101u8,103u8,101u8,114u8,7u8,105u8, - 115u8,95u8,115u8,111u8,109u8,101u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,12u8,111u8,117u8, - 116u8,95u8,111u8,102u8,95u8,114u8,97u8,110u8,103u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,12u8,100u8,101u8, - 115u8,116u8,114u8,111u8,121u8,95u8,115u8,111u8,109u8,101u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,110u8, - 111u8,110u8,101u8,26u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8, - 114u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8, - 7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,4u8,102u8,105u8,108u8,108u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,199u8,1u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,65u8,71u8,71u8, - 82u8,69u8,71u8,65u8,84u8,79u8,82u8,95u8,79u8,86u8,69u8,82u8,70u8,76u8,79u8,87u8,76u8,84u8,104u8,101u8, - 32u8,118u8,97u8,108u8,117u8,101u8,32u8,111u8,102u8,32u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8, - 32u8,117u8,110u8,100u8,101u8,114u8,102u8,108u8,111u8,119u8,115u8,32u8,40u8,103u8,111u8,101u8,115u8,32u8,98u8,101u8, - 108u8,111u8,119u8,32u8,122u8,101u8,114u8,111u8,41u8,46u8,32u8,82u8,97u8,105u8,115u8,101u8,100u8,32u8,98u8,121u8, - 32u8,110u8,97u8,116u8,105u8,118u8,101u8,32u8,99u8,111u8,100u8,101u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,21u8,69u8,65u8,71u8,71u8,82u8,69u8,71u8,65u8,84u8,79u8,82u8,95u8,85u8,78u8,68u8,69u8,82u8,70u8, - 76u8,79u8,87u8,59u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,32u8,102u8,101u8,97u8,116u8,117u8, - 114u8,101u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,115u8,117u8,112u8,112u8,111u8,114u8,116u8,101u8,100u8,46u8, - 32u8,82u8,97u8,105u8,115u8,101u8,100u8,32u8,98u8,121u8,32u8,110u8,97u8,116u8,105u8,118u8,101u8,32u8,99u8,111u8, - 100u8,101u8,46u8,0u8,0u8,0u8,2u8,2u8,26u8,4u8,15u8,4u8,1u8,2u8,2u8,2u8,11u8,2u8,1u8,8u8, - 3u8,29u8,11u8,2u8,1u8,8u8,0u8,0u8,1u8,0u8,0u8,1u8,16u8,10u8,0u8,16u8,0u8,56u8,0u8,4u8, - 10u8,11u8,0u8,15u8,0u8,56u8,1u8,11u8,1u8,17u8,20u8,5u8,15u8,11u8,0u8,15u8,1u8,56u8,2u8,11u8, - 1u8,17u8,1u8,2u8,1u8,0u8,0u8,0u8,1u8,25u8,10u8,1u8,10u8,0u8,16u8,2u8,20u8,10u8,0u8,16u8, - 3u8,20u8,23u8,37u8,4u8,11u8,5u8,16u8,11u8,0u8,1u8,7u8,0u8,17u8,21u8,39u8,10u8,0u8,16u8,3u8, - 20u8,11u8,1u8,22u8,11u8,0u8,15u8,3u8,21u8,2u8,2u8,1u8,0u8,0u8,1u8,11u8,14u8,0u8,17u8,6u8, - 4u8,7u8,11u8,0u8,17u8,4u8,1u8,5u8,10u8,11u8,0u8,17u8,5u8,1u8,2u8,3u8,0u8,0u8,0u8,1u8, - 5u8,11u8,0u8,19u8,0u8,1u8,1u8,2u8,4u8,0u8,0u8,0u8,17u8,15u8,11u8,0u8,19u8,1u8,12u8,2u8, - 12u8,1u8,14u8,1u8,56u8,3u8,17u8,23u8,12u8,3u8,11u8,1u8,56u8,4u8,17u8,25u8,11u8,2u8,56u8,5u8, - 11u8,3u8,2u8,5u8,0u8,0u8,0u8,17u8,15u8,11u8,0u8,19u8,1u8,12u8,2u8,12u8,1u8,14u8,2u8,56u8, - 6u8,17u8,7u8,12u8,3u8,11u8,2u8,56u8,7u8,17u8,3u8,11u8,1u8,56u8,8u8,11u8,3u8,2u8,6u8,1u8, - 0u8,0u8,1u8,4u8,11u8,0u8,16u8,0u8,56u8,0u8,2u8,7u8,0u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8, - 2u8,20u8,2u8,8u8,3u8,0u8,0u8,3u8,17u8,11u8,1u8,4u8,9u8,11u8,0u8,17u8,27u8,56u8,9u8,56u8, - 10u8,18u8,1u8,12u8,2u8,5u8,15u8,56u8,11u8,11u8,0u8,17u8,9u8,56u8,12u8,18u8,1u8,12u8,2u8,11u8, - 2u8,2u8,9u8,0u8,0u8,0u8,1u8,4u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,11u8,0u8,18u8,0u8,2u8,10u8,1u8,0u8,0u8,5u8,17u8,10u8,0u8,16u8,0u8, - 56u8,0u8,4u8,10u8,11u8,0u8,16u8,0u8,56u8,3u8,17u8,30u8,12u8,1u8,5u8,15u8,11u8,0u8,16u8,1u8, - 56u8,6u8,17u8,11u8,12u8,1u8,11u8,1u8,2u8,11u8,0u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,3u8,20u8, - 2u8,12u8,1u8,0u8,0u8,1u8,16u8,10u8,0u8,16u8,0u8,56u8,0u8,4u8,10u8,11u8,0u8,15u8,0u8,56u8, - 1u8,11u8,1u8,17u8,31u8,5u8,15u8,11u8,0u8,15u8,1u8,56u8,2u8,11u8,1u8,17u8,13u8,2u8,13u8,0u8, - 0u8,0u8,1u8,21u8,10u8,1u8,10u8,0u8,16u8,3u8,20u8,37u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8, - 1u8,17u8,21u8,39u8,10u8,0u8,16u8,3u8,20u8,11u8,1u8,23u8,11u8,0u8,15u8,3u8,21u8,2u8,14u8,1u8, - 0u8,0u8,5u8,10u8,10u8,0u8,46u8,17u8,10u8,12u8,1u8,10u8,0u8,17u8,15u8,11u8,0u8,11u8,1u8,17u8, - 0u8,2u8,15u8,0u8,0u8,0u8,1u8,12u8,10u8,0u8,46u8,17u8,6u8,4u8,8u8,11u8,0u8,17u8,17u8,1u8, - 5u8,11u8,11u8,0u8,17u8,16u8,1u8,2u8,16u8,0u8,0u8,0u8,22u8,18u8,10u8,0u8,15u8,1u8,56u8,13u8, - 12u8,2u8,14u8,2u8,17u8,7u8,12u8,3u8,11u8,2u8,17u8,3u8,10u8,3u8,17u8,27u8,12u8,1u8,11u8,0u8, - 15u8,0u8,11u8,1u8,56u8,14u8,11u8,3u8,2u8,17u8,0u8,0u8,0u8,22u8,18u8,10u8,0u8,15u8,0u8,56u8, - 15u8,12u8,1u8,14u8,1u8,17u8,23u8,12u8,3u8,11u8,1u8,17u8,25u8,10u8,3u8,17u8,9u8,12u8,2u8,11u8, - 0u8,15u8,1u8,11u8,2u8,56u8,16u8,11u8,3u8,2u8,1u8,0u8,1u8,1u8,0u8,1u8,0u8,0u8,0u8,0u8, - 0u8, - ]; - vector::push_back(&mut code, chunk8); - let chunk9 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,15u8,1u8,0u8,24u8,2u8,24u8,82u8,3u8,106u8,241u8,2u8, - 4u8,219u8,3u8,46u8,5u8,137u8,4u8,134u8,3u8,7u8,143u8,7u8,227u8,9u8,8u8,242u8,16u8,32u8,6u8,146u8, - 17u8,210u8,1u8,16u8,228u8,18u8,226u8,9u8,10u8,198u8,28u8,81u8,11u8,151u8,29u8,14u8,12u8,165u8,29u8,224u8, - 9u8,13u8,133u8,39u8,22u8,14u8,155u8,39u8,20u8,15u8,175u8,39u8,6u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8, - 6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,4u8, - 1u8,0u8,1u8,0u8,16u8,5u8,1u8,0u8,1u8,0u8,17u8,4u8,1u8,0u8,1u8,0u8,18u8,8u8,1u8,0u8, - 1u8,0u8,19u8,8u8,1u8,0u8,1u8,0u8,20u8,6u8,0u8,0u8,21u8,5u8,1u8,0u8,1u8,0u8,22u8,5u8, - 1u8,0u8,1u8,0u8,23u8,8u8,0u8,0u8,24u8,6u8,0u8,9u8,41u8,7u8,0u8,6u8,55u8,7u8,1u8,0u8, - 0u8,2u8,64u8,4u8,0u8,7u8,66u8,4u8,0u8,5u8,69u8,4u8,1u8,6u8,1u8,11u8,79u8,7u8,0u8,0u8, - 25u8,0u8,1u8,0u8,0u8,26u8,2u8,3u8,1u8,0u8,0u8,27u8,4u8,1u8,1u8,0u8,0u8,28u8,5u8,1u8, - 1u8,0u8,0u8,29u8,1u8,2u8,1u8,0u8,0u8,30u8,6u8,1u8,1u8,0u8,0u8,31u8,1u8,7u8,1u8,0u8, - 0u8,32u8,8u8,1u8,1u8,0u8,0u8,33u8,9u8,1u8,1u8,0u8,0u8,34u8,10u8,1u8,1u8,0u8,0u8,35u8, - 11u8,1u8,1u8,0u8,0u8,36u8,12u8,1u8,1u8,0u8,0u8,37u8,13u8,12u8,1u8,0u8,0u8,38u8,14u8,12u8, - 1u8,0u8,0u8,39u8,15u8,12u8,1u8,0u8,0u8,40u8,16u8,1u8,1u8,0u8,0u8,42u8,17u8,18u8,1u8,0u8, - 0u8,43u8,19u8,20u8,1u8,0u8,0u8,44u8,21u8,18u8,1u8,0u8,0u8,45u8,19u8,1u8,0u8,0u8,46u8,17u8, - 18u8,1u8,0u8,0u8,47u8,2u8,22u8,1u8,0u8,0u8,48u8,23u8,22u8,1u8,0u8,0u8,49u8,1u8,22u8,1u8, - 0u8,0u8,50u8,24u8,1u8,1u8,0u8,0u8,51u8,25u8,1u8,1u8,0u8,0u8,52u8,26u8,12u8,1u8,0u8,0u8, - 53u8,1u8,27u8,1u8,0u8,0u8,54u8,19u8,1u8,1u8,0u8,0u8,56u8,1u8,28u8,1u8,0u8,0u8,57u8,1u8, - 27u8,1u8,0u8,0u8,58u8,29u8,1u8,1u8,0u8,0u8,59u8,16u8,1u8,1u8,0u8,0u8,60u8,19u8,1u8,1u8, - 0u8,0u8,61u8,30u8,3u8,1u8,0u8,0u8,62u8,31u8,12u8,1u8,0u8,0u8,63u8,1u8,12u8,1u8,0u8,10u8, - 73u8,19u8,1u8,0u8,4u8,74u8,3u8,3u8,0u8,4u8,75u8,3u8,3u8,0u8,6u8,76u8,36u8,22u8,1u8,0u8, - 6u8,77u8,37u8,38u8,1u8,0u8,7u8,78u8,39u8,1u8,0u8,11u8,80u8,1u8,40u8,1u8,0u8,11u8,81u8,41u8, - 2u8,0u8,4u8,82u8,3u8,3u8,0u8,5u8,83u8,44u8,1u8,1u8,6u8,2u8,84u8,46u8,45u8,0u8,4u8,85u8, - 3u8,3u8,0u8,2u8,78u8,47u8,1u8,0u8,3u8,86u8,48u8,49u8,0u8,8u8,87u8,19u8,2u8,0u8,4u8,88u8, - 3u8,3u8,0u8,9u8,89u8,51u8,3u8,0u8,7u8,90u8,52u8,35u8,0u8,6u8,91u8,33u8,53u8,1u8,0u8,6u8, - 92u8,1u8,53u8,1u8,0u8,2u8,93u8,47u8,1u8,0u8,7u8,93u8,39u8,1u8,0u8,1u8,94u8,2u8,1u8,1u8, - 0u8,1u8,95u8,19u8,56u8,1u8,6u8,6u8,96u8,36u8,59u8,1u8,0u8,7u8,84u8,60u8,45u8,0u8,7u8,97u8, - 60u8,22u8,0u8,7u8,98u8,62u8,1u8,0u8,21u8,33u8,4u8,33u8,40u8,35u8,41u8,35u8,13u8,33u8,2u8,33u8, - 43u8,33u8,25u8,33u8,46u8,43u8,24u8,33u8,18u8,33u8,55u8,35u8,56u8,35u8,36u8,33u8,59u8,33u8,60u8,43u8, - 60u8,57u8,61u8,35u8,55u8,45u8,56u8,45u8,35u8,33u8,7u8,33u8,46u8,57u8,2u8,6u8,12u8,1u8,0u8,1u8, - 5u8,1u8,3u8,2u8,11u8,2u8,1u8,9u8,0u8,6u8,11u8,1u8,1u8,9u8,0u8,3u8,5u8,3u8,6u8,11u8, - 1u8,1u8,9u8,0u8,3u8,5u8,3u8,7u8,11u8,0u8,1u8,9u8,0u8,1u8,2u8,2u8,5u8,11u8,2u8,1u8, - 9u8,0u8,1u8,11u8,1u8,1u8,9u8,0u8,1u8,11u8,6u8,1u8,9u8,0u8,1u8,11u8,7u8,1u8,9u8,0u8, - 1u8,11u8,2u8,1u8,9u8,0u8,1u8,7u8,11u8,0u8,1u8,9u8,0u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8, - 3u8,1u8,7u8,11u8,2u8,1u8,9u8,0u8,2u8,5u8,6u8,11u8,6u8,1u8,9u8,0u8,5u8,6u8,12u8,8u8, - 10u8,8u8,10u8,2u8,1u8,3u8,11u8,1u8,1u8,9u8,0u8,11u8,6u8,1u8,9u8,0u8,11u8,7u8,1u8,9u8, - 0u8,1u8,6u8,12u8,1u8,11u8,0u8,1u8,9u8,0u8,6u8,6u8,12u8,8u8,10u8,8u8,10u8,2u8,1u8,1u8, - 1u8,1u8,1u8,6u8,11u8,0u8,1u8,9u8,0u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8,11u8,2u8,1u8,9u8, - 0u8,2u8,7u8,11u8,0u8,1u8,9u8,0u8,11u8,2u8,1u8,9u8,0u8,2u8,3u8,6u8,11u8,7u8,1u8,9u8, - 0u8,1u8,8u8,10u8,1u8,11u8,11u8,1u8,4u8,3u8,6u8,12u8,5u8,3u8,1u8,6u8,11u8,2u8,1u8,9u8, - 0u8,2u8,6u8,12u8,3u8,1u8,7u8,1u8,1u8,9u8,0u8,2u8,3u8,7u8,11u8,11u8,1u8,8u8,13u8,1u8, - 8u8,13u8,1u8,6u8,11u8,11u8,1u8,9u8,0u8,1u8,7u8,11u8,11u8,1u8,9u8,0u8,1u8,7u8,9u8,0u8, - 2u8,7u8,8u8,13u8,4u8,1u8,8u8,15u8,1u8,6u8,8u8,15u8,1u8,7u8,11u8,4u8,1u8,9u8,0u8,1u8, - 8u8,5u8,2u8,7u8,11u8,14u8,1u8,9u8,0u8,9u8,0u8,1u8,4u8,1u8,6u8,8u8,12u8,2u8,7u8,8u8, - 12u8,4u8,2u8,6u8,12u8,4u8,1u8,8u8,12u8,6u8,11u8,11u8,1u8,8u8,13u8,2u8,8u8,10u8,8u8,10u8, - 5u8,11u8,3u8,1u8,9u8,0u8,1u8,6u8,8u8,10u8,2u8,4u8,1u8,1u8,11u8,11u8,1u8,9u8,0u8,1u8, - 7u8,11u8,11u8,1u8,8u8,13u8,2u8,5u8,11u8,4u8,1u8,9u8,0u8,1u8,11u8,14u8,1u8,9u8,0u8,1u8, - 8u8,9u8,2u8,11u8,11u8,1u8,4u8,6u8,11u8,11u8,1u8,8u8,13u8,1u8,6u8,9u8,0u8,1u8,6u8,8u8, - 13u8,3u8,5u8,7u8,11u8,11u8,1u8,8u8,13u8,7u8,8u8,13u8,1u8,7u8,8u8,13u8,2u8,5u8,7u8,11u8, - 4u8,1u8,9u8,0u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,7u8,103u8,101u8,110u8,101u8, - 115u8,105u8,115u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,4u8, - 99u8,111u8,105u8,110u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8, - 116u8,111u8,114u8,18u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,102u8,97u8,99u8,116u8,111u8, - 114u8,121u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,6u8,111u8,112u8,116u8,105u8,111u8, - 110u8,19u8,111u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8, - 114u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,16u8,115u8,121u8,115u8,116u8, - 101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8, - 102u8,111u8,16u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8,101u8,67u8,111u8,105u8,110u8,14u8, - 66u8,117u8,114u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,4u8,67u8,111u8,105u8,110u8,8u8, - 67u8,111u8,105u8,110u8,73u8,110u8,102u8,111u8,9u8,67u8,111u8,105u8,110u8,83u8,116u8,111u8,114u8,101u8,12u8,68u8, - 101u8,112u8,111u8,115u8,105u8,116u8,69u8,118u8,101u8,110u8,116u8,16u8,70u8,114u8,101u8,101u8,122u8,101u8,67u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,14u8,77u8,105u8,110u8,116u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,12u8,83u8,117u8,112u8,112u8,108u8,121u8,67u8,111u8,110u8,102u8,105u8,103u8,13u8,87u8,105u8,116u8, - 104u8,100u8,114u8,97u8,119u8,69u8,118u8,101u8,110u8,116u8,21u8,97u8,108u8,108u8,111u8,119u8,95u8,115u8,117u8,112u8, - 112u8,108u8,121u8,95u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,115u8,7u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8, - 4u8,98u8,117u8,114u8,110u8,9u8,98u8,117u8,114u8,110u8,95u8,102u8,114u8,111u8,109u8,12u8,99u8,111u8,105u8,110u8, - 95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,30u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,105u8,110u8,116u8, - 111u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,8u8, - 100u8,101u8,99u8,105u8,109u8,97u8,108u8,115u8,7u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,16u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,98u8,117u8,114u8,110u8,95u8,99u8,97u8,112u8,18u8,100u8,101u8,115u8,116u8,114u8,111u8, - 121u8,95u8,102u8,114u8,101u8,101u8,122u8,101u8,95u8,99u8,97u8,112u8,16u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8, - 95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,122u8,101u8, - 114u8,111u8,23u8,100u8,114u8,97u8,105u8,110u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8, - 101u8,95u8,99u8,111u8,105u8,110u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,11u8,101u8,120u8,116u8,114u8,97u8, - 99u8,116u8,95u8,97u8,108u8,108u8,17u8,102u8,114u8,101u8,101u8,122u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8,115u8, - 116u8,111u8,114u8,101u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,28u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8, - 97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,19u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,24u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,37u8,105u8,110u8,105u8, - 116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,112u8,97u8,114u8,97u8,108u8,108u8,101u8, - 108u8,105u8,122u8,97u8,98u8,108u8,101u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,21u8,105u8,115u8,95u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8,25u8,105u8,115u8,95u8, - 97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8,122u8,101u8, - 114u8,111u8,19u8,105u8,115u8,95u8,99u8,111u8,105u8,110u8,95u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8, - 101u8,100u8,5u8,109u8,101u8,114u8,103u8,101u8,23u8,109u8,101u8,114u8,103u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8, - 103u8,97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,4u8,109u8,105u8,110u8,116u8,4u8,110u8,97u8, - 109u8,101u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8, - 117u8,112u8,112u8,108u8,121u8,6u8,115u8,121u8,109u8,98u8,111u8,108u8,8u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8, - 114u8,19u8,117u8,110u8,102u8,114u8,101u8,101u8,122u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8,115u8,116u8,111u8,114u8, - 101u8,14u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,5u8,118u8,97u8,108u8, - 117u8,101u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,4u8,122u8,101u8,114u8,111u8,10u8,65u8,103u8,103u8, - 114u8,101u8,103u8,97u8,116u8,111u8,114u8,11u8,100u8,117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8,18u8, - 79u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,6u8,102u8, - 114u8,111u8,122u8,101u8,110u8,14u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8, - 11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,15u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8, - 119u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,14u8,97u8,108u8,108u8,111u8, - 119u8,95u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,115u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8, - 116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8, - 117u8,110u8,100u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8, - 7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,3u8, - 115u8,117u8,98u8,8u8,84u8,121u8,112u8,101u8,73u8,110u8,102u8,111u8,7u8,116u8,121u8,112u8,101u8,95u8,111u8,102u8, - 15u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,17u8,112u8,101u8,114u8, - 109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,10u8,101u8,109u8,105u8,116u8,95u8, - 101u8,118u8,101u8,110u8,116u8,4u8,114u8,101u8,97u8,100u8,12u8,111u8,117u8,116u8,95u8,111u8,102u8,95u8,114u8,97u8, - 110u8,103u8,101u8,17u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8, - 114u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8, - 95u8,101u8,120u8,105u8,115u8,116u8,115u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,3u8,110u8,101u8,119u8,4u8,115u8, - 111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,3u8,97u8,100u8,100u8,13u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8, - 114u8,95u8,99u8,111u8,105u8,110u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8, - 100u8,108u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,17u8,105u8,115u8,95u8,112u8,97u8,114u8,97u8,108u8,108u8, - 101u8,108u8,105u8,122u8,97u8,98u8,108u8,101u8,6u8,115u8,119u8,105u8,116u8,99u8,104u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,11u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,32u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,16u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,4u8,16u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8, - 116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,205u8,9u8,13u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,67u8,79u8,73u8,78u8,95u8,73u8,78u8,70u8,79u8,95u8,65u8, - 68u8,68u8,82u8,69u8,83u8,83u8,95u8,77u8,73u8,83u8,77u8,65u8,84u8,67u8,72u8,101u8,65u8,100u8,100u8,114u8, - 101u8,115u8,115u8,32u8,111u8,102u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,119u8,104u8,105u8,99u8,104u8, - 32u8,105u8,115u8,32u8,117u8,115u8,101u8,100u8,32u8,116u8,111u8,32u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,32u8,97u8,32u8,99u8,111u8,105u8,110u8,32u8,96u8,67u8,111u8,105u8,110u8,84u8,121u8,112u8,101u8,96u8, - 32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,32u8,116u8,104u8,101u8,32u8,100u8, - 101u8,112u8,108u8,111u8,121u8,101u8,114u8,32u8,111u8,102u8,32u8,109u8,111u8,100u8,117u8,108u8,101u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,28u8,69u8,67u8,79u8,73u8,78u8,95u8,73u8,78u8,70u8,79u8,95u8,65u8,76u8,82u8, - 69u8,65u8,68u8,89u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,43u8,96u8,67u8,111u8,105u8,110u8, - 84u8,121u8,112u8,101u8,96u8,32u8,105u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,105u8,110u8,105u8, - 116u8,105u8,97u8,108u8,105u8,122u8,101u8,100u8,32u8,97u8,115u8,32u8,97u8,32u8,99u8,111u8,105u8,110u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,67u8,79u8,73u8,78u8,95u8,73u8,78u8,70u8,79u8,95u8,78u8,79u8, - 84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,44u8,96u8,67u8,111u8,105u8,110u8,84u8,121u8,112u8, - 101u8,96u8,32u8,104u8,97u8,115u8,110u8,39u8,116u8,32u8,98u8,101u8,101u8,110u8,32u8,105u8,110u8,105u8,116u8,105u8, - 97u8,108u8,105u8,122u8,101u8,100u8,32u8,97u8,115u8,32u8,97u8,32u8,99u8,111u8,105u8,110u8,4u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,29u8,69u8,67u8,79u8,73u8,78u8,95u8,83u8,84u8,79u8,82u8,69u8,95u8,65u8,76u8,82u8, - 69u8,65u8,68u8,89u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,69u8,68u8,101u8,112u8,114u8,101u8, - 99u8,97u8,116u8,101u8,100u8,46u8,32u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,97u8,108u8,114u8,101u8,97u8, - 100u8,121u8,32u8,104u8,97u8,115u8,32u8,96u8,67u8,111u8,105u8,110u8,83u8,116u8,111u8,114u8,101u8,96u8,32u8,114u8, - 101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8,32u8,102u8,111u8,114u8,32u8,96u8,67u8,111u8,105u8,110u8,84u8, - 121u8,112u8,101u8,96u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,67u8,79u8,73u8,78u8,95u8,83u8, - 84u8,79u8,82u8,69u8,95u8,78u8,79u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,52u8,65u8, - 99u8,99u8,111u8,117u8,110u8,116u8,32u8,104u8,97u8,115u8,110u8,39u8,116u8,32u8,114u8,101u8,103u8,105u8,115u8,116u8, - 101u8,114u8,101u8,100u8,32u8,96u8,67u8,111u8,105u8,110u8,83u8,116u8,111u8,114u8,101u8,96u8,32u8,102u8,111u8,114u8, - 32u8,96u8,67u8,111u8,105u8,110u8,84u8,121u8,112u8,101u8,96u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8, - 69u8,73u8,78u8,83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,66u8,65u8,76u8,65u8,78u8,67u8, - 69u8,40u8,78u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,99u8,111u8,105u8,110u8,115u8,32u8,116u8, - 111u8,32u8,99u8,111u8,109u8,112u8,108u8,101u8,116u8,101u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8, - 111u8,110u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,68u8,69u8,83u8,84u8,82u8,85u8,67u8,84u8, - 73u8,79u8,78u8,95u8,79u8,70u8,95u8,78u8,79u8,78u8,90u8,69u8,82u8,79u8,95u8,84u8,79u8,75u8,69u8,78u8, - 29u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,32u8,110u8,111u8,110u8,45u8, - 122u8,101u8,114u8,111u8,32u8,99u8,111u8,105u8,110u8,115u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8, - 90u8,69u8,82u8,79u8,95u8,67u8,79u8,73u8,78u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,26u8,67u8,111u8,105u8, - 110u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,122u8, - 101u8,114u8,111u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,69u8,70u8,82u8,79u8,90u8,69u8,78u8,59u8, - 67u8,111u8,105u8,110u8,83u8,116u8,111u8,114u8,101u8,32u8,105u8,115u8,32u8,102u8,114u8,111u8,122u8,101u8,110u8,46u8, - 32u8,67u8,111u8,105u8,110u8,115u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,100u8,101u8,112u8, - 111u8,115u8,105u8,116u8,101u8,100u8,32u8,111u8,114u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,110u8,11u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,67u8,79u8,73u8,78u8,95u8,83u8,85u8,80u8,80u8,76u8,89u8, - 95u8,85u8,80u8,71u8,82u8,65u8,68u8,69u8,95u8,78u8,79u8,84u8,95u8,83u8,85u8,80u8,80u8,79u8,82u8,84u8, - 69u8,68u8,69u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,32u8,116u8,104u8, - 101u8,32u8,116u8,111u8,116u8,97u8,108u8,32u8,115u8,117u8,112u8,112u8,108u8,121u8,32u8,111u8,102u8,32u8,99u8,111u8, - 105u8,110u8,115u8,32u8,116u8,111u8,32u8,100u8,105u8,102u8,102u8,101u8,114u8,101u8,110u8,116u8,32u8,105u8,109u8,112u8, - 108u8,101u8,109u8,101u8,110u8,116u8,97u8,116u8,105u8,111u8,110u8,46u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 19u8,69u8,67u8,79u8,73u8,78u8,95u8,78u8,65u8,77u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8, - 28u8,78u8,97u8,109u8,101u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,99u8,111u8,105u8,110u8,32u8,105u8,115u8, - 32u8,116u8,111u8,111u8,32u8,108u8,111u8,110u8,103u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,67u8, - 79u8,73u8,78u8,95u8,83u8,89u8,77u8,66u8,79u8,76u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8,30u8, - 83u8,121u8,109u8,98u8,111u8,108u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,99u8,111u8,105u8,110u8,32u8,105u8, - 115u8,32u8,116u8,111u8,111u8,32u8,108u8,111u8,110u8,103u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8, - 65u8,71u8,71u8,82u8,69u8,71u8,65u8,84u8,65u8,66u8,76u8,69u8,95u8,67u8,79u8,73u8,78u8,95u8,86u8,65u8, - 76u8,85u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,65u8,82u8,71u8,69u8,92u8,84u8,104u8,101u8,32u8,118u8,97u8, - 108u8,117u8,101u8,32u8,111u8,102u8,32u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8,101u8,32u8, - 99u8,111u8,105u8,110u8,32u8,117u8,115u8,101u8,100u8,32u8,102u8,111u8,114u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8, - 99u8,116u8,105u8,111u8,110u8,32u8,102u8,101u8,101u8,115u8,32u8,114u8,101u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8, - 117u8,116u8,105u8,111u8,110u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,102u8,105u8,116u8,32u8,105u8, - 110u8,32u8,117u8,54u8,52u8,46u8,0u8,7u8,4u8,110u8,97u8,109u8,101u8,1u8,1u8,0u8,6u8,115u8,117u8,112u8, - 112u8,108u8,121u8,1u8,1u8,0u8,6u8,115u8,121u8,109u8,98u8,111u8,108u8,1u8,1u8,0u8,7u8,98u8,97u8,108u8, - 97u8,110u8,99u8,101u8,1u8,1u8,0u8,8u8,100u8,101u8,99u8,105u8,109u8,97u8,108u8,115u8,1u8,1u8,0u8,19u8, - 105u8,115u8,95u8,99u8,111u8,105u8,110u8,95u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,100u8,1u8, - 1u8,0u8,21u8,105u8,115u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8, - 101u8,114u8,101u8,100u8,1u8,1u8,0u8,0u8,2u8,1u8,61u8,8u8,12u8,1u8,2u8,1u8,65u8,1u8,2u8,2u8, - 1u8,61u8,3u8,3u8,2u8,4u8,53u8,8u8,10u8,57u8,8u8,10u8,31u8,2u8,56u8,11u8,11u8,1u8,8u8,13u8, - 4u8,2u8,4u8,3u8,11u8,2u8,1u8,9u8,0u8,67u8,1u8,68u8,11u8,14u8,1u8,8u8,5u8,70u8,11u8,14u8, - 1u8,8u8,9u8,5u8,2u8,1u8,71u8,3u8,6u8,2u8,1u8,65u8,1u8,7u8,2u8,1u8,65u8,1u8,8u8,2u8, - 1u8,72u8,1u8,9u8,2u8,1u8,71u8,3u8,4u8,33u8,2u8,33u8,3u8,33u8,1u8,33u8,6u8,33u8,7u8,33u8, - 0u8,33u8,0u8,1u8,0u8,1u8,8u8,32u8,10u8,11u8,0u8,17u8,37u8,7u8,16u8,42u8,8u8,15u8,0u8,12u8, - 2u8,11u8,1u8,11u8,2u8,21u8,2u8,1u8,1u8,0u8,1u8,4u8,1u8,13u8,10u8,0u8,56u8,0u8,4u8,4u8, - 5u8,7u8,7u8,6u8,17u8,38u8,39u8,11u8,0u8,61u8,0u8,55u8,0u8,55u8,1u8,20u8,2u8,2u8,1u8,0u8, - 1u8,3u8,34u8,28u8,11u8,0u8,58u8,1u8,12u8,2u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,36u8,4u8,8u8,5u8,11u8,7u8,12u8,17u8,39u8,39u8,56u8,1u8,60u8,2u8,54u8,2u8,12u8,3u8,10u8, - 3u8,46u8,56u8,2u8,4u8,25u8,11u8,3u8,56u8,3u8,11u8,2u8,53u8,17u8,42u8,5u8,27u8,11u8,3u8,1u8, - 2u8,3u8,1u8,0u8,2u8,3u8,4u8,1u8,15u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 33u8,4u8,7u8,11u8,2u8,1u8,2u8,11u8,0u8,60u8,0u8,54u8,0u8,11u8,1u8,56u8,4u8,11u8,2u8,56u8, - 5u8,2u8,4u8,0u8,0u8,0u8,40u8,5u8,56u8,6u8,12u8,0u8,14u8,0u8,17u8,44u8,2u8,5u8,3u8,0u8, - 1u8,4u8,12u8,17u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,11u8,2u8, - 1u8,2u8,11u8,0u8,60u8,0u8,54u8,0u8,11u8,1u8,56u8,4u8,12u8,3u8,11u8,2u8,11u8,3u8,56u8,7u8, - 2u8,6u8,1u8,0u8,1u8,3u8,1u8,5u8,56u8,1u8,61u8,2u8,55u8,3u8,20u8,2u8,7u8,1u8,0u8,1u8, - 4u8,42u8,33u8,10u8,0u8,56u8,0u8,4u8,4u8,5u8,7u8,7u8,6u8,17u8,38u8,39u8,11u8,0u8,60u8,0u8, - 12u8,2u8,10u8,2u8,55u8,4u8,20u8,32u8,4u8,16u8,5u8,21u8,11u8,2u8,1u8,7u8,10u8,17u8,45u8,39u8, - 10u8,2u8,54u8,5u8,14u8,1u8,55u8,1u8,20u8,18u8,5u8,56u8,8u8,11u8,2u8,54u8,0u8,11u8,1u8,56u8, - 9u8,2u8,8u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,58u8,3u8,1u8,2u8,9u8,1u8,0u8,0u8,1u8,4u8, - 11u8,0u8,58u8,4u8,1u8,2u8,10u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,58u8,5u8,1u8,2u8,11u8,1u8, - 0u8,0u8,1u8,10u8,11u8,0u8,58u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,6u8, - 5u8,9u8,7u8,9u8,17u8,39u8,39u8,2u8,12u8,3u8,0u8,0u8,45u8,23u8,40u8,10u8,0u8,55u8,6u8,17u8, - 47u8,12u8,1u8,10u8,1u8,7u8,15u8,37u8,4u8,10u8,5u8,15u8,11u8,0u8,1u8,7u8,0u8,17u8,48u8,39u8, - 11u8,0u8,54u8,6u8,10u8,1u8,17u8,49u8,11u8,1u8,52u8,57u8,1u8,2u8,13u8,1u8,0u8,0u8,1u8,23u8, - 10u8,0u8,55u8,1u8,20u8,10u8,1u8,38u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8,11u8,17u8,39u8,39u8, - 10u8,0u8,55u8,1u8,20u8,10u8,1u8,23u8,11u8,0u8,54u8,1u8,21u8,11u8,1u8,57u8,1u8,2u8,14u8,1u8, - 0u8,0u8,3u8,11u8,10u8,0u8,55u8,1u8,20u8,12u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 11u8,0u8,54u8,1u8,21u8,11u8,1u8,57u8,1u8,2u8,15u8,1u8,4u8,1u8,4u8,42u8,8u8,11u8,0u8,60u8, - 0u8,12u8,2u8,8u8,11u8,2u8,54u8,4u8,21u8,2u8,16u8,1u8,0u8,0u8,1u8,8u8,11u8,0u8,11u8,1u8, - 11u8,2u8,11u8,3u8,11u8,4u8,9u8,56u8,10u8,2u8,17u8,3u8,0u8,0u8,1u8,5u8,11u8,0u8,7u8,15u8, - 17u8,50u8,57u8,6u8,2u8,18u8,0u8,0u8,0u8,50u8,77u8,10u8,0u8,17u8,51u8,12u8,10u8,56u8,1u8,10u8, - 10u8,33u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,1u8,17u8,39u8,39u8,11u8,10u8,59u8,2u8,32u8,4u8, - 18u8,5u8,23u8,11u8,0u8,1u8,7u8,2u8,17u8,52u8,39u8,14u8,1u8,17u8,53u8,7u8,13u8,37u8,4u8,29u8, - 5u8,34u8,11u8,0u8,1u8,7u8,4u8,17u8,39u8,39u8,14u8,2u8,17u8,53u8,7u8,10u8,37u8,4u8,40u8,5u8, - 45u8,11u8,0u8,1u8,7u8,8u8,17u8,39u8,39u8,11u8,1u8,12u8,9u8,11u8,2u8,12u8,8u8,11u8,3u8,12u8, - 7u8,11u8,4u8,4u8,59u8,7u8,14u8,11u8,5u8,17u8,54u8,56u8,11u8,12u8,6u8,5u8,61u8,56u8,12u8,12u8, - 6u8,11u8,9u8,11u8,8u8,11u8,7u8,11u8,6u8,57u8,2u8,12u8,11u8,11u8,0u8,11u8,11u8,63u8,2u8,9u8, - 57u8,3u8,9u8,57u8,4u8,9u8,57u8,5u8,2u8,19u8,3u8,0u8,0u8,1u8,7u8,10u8,0u8,17u8,37u8,11u8, - 0u8,9u8,18u8,8u8,45u8,8u8,2u8,20u8,3u8,0u8,0u8,1u8,10u8,10u8,0u8,17u8,37u8,11u8,0u8,11u8, - 1u8,11u8,2u8,11u8,3u8,11u8,4u8,8u8,56u8,10u8,2u8,21u8,1u8,0u8,0u8,1u8,3u8,11u8,0u8,59u8, - 0u8,2u8,22u8,3u8,0u8,0u8,1u8,6u8,11u8,0u8,55u8,6u8,17u8,47u8,50u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,2u8,23u8,1u8,0u8,0u8,1u8,3u8,56u8, - 1u8,59u8,2u8,2u8,24u8,1u8,0u8,0u8,3u8,13u8,40u8,11u8,1u8,58u8,1u8,12u8,2u8,10u8,0u8,55u8, - 1u8,20u8,11u8,2u8,22u8,11u8,0u8,54u8,1u8,21u8,2u8,25u8,3u8,0u8,0u8,45u8,9u8,11u8,1u8,58u8, - 1u8,53u8,12u8,2u8,11u8,0u8,54u8,6u8,11u8,2u8,17u8,57u8,2u8,26u8,1u8,0u8,1u8,3u8,54u8,25u8, - 10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,6u8,56u8,13u8,2u8,56u8,1u8,60u8, - 2u8,54u8,2u8,12u8,2u8,10u8,2u8,46u8,56u8,2u8,4u8,20u8,11u8,2u8,56u8,3u8,10u8,0u8,53u8,17u8, - 58u8,5u8,22u8,11u8,2u8,1u8,11u8,0u8,57u8,1u8,2u8,27u8,1u8,0u8,1u8,3u8,1u8,5u8,56u8,1u8, - 61u8,2u8,55u8,7u8,20u8,2u8,28u8,1u8,0u8,0u8,55u8,24u8,10u8,0u8,17u8,51u8,12u8,1u8,10u8,1u8, - 56u8,0u8,4u8,9u8,11u8,0u8,1u8,2u8,11u8,1u8,56u8,14u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,57u8,1u8,9u8,10u8,0u8,56u8,15u8,10u8,0u8,56u8,16u8,57u8,0u8,12u8,2u8,11u8,0u8,11u8,2u8, - 63u8,0u8,2u8,29u8,1u8,0u8,1u8,3u8,58u8,19u8,56u8,1u8,61u8,2u8,55u8,2u8,12u8,1u8,10u8,1u8, - 56u8,2u8,4u8,13u8,11u8,1u8,56u8,17u8,17u8,62u8,56u8,18u8,12u8,0u8,5u8,17u8,11u8,1u8,1u8,56u8, - 19u8,12u8,0u8,11u8,0u8,2u8,30u8,1u8,0u8,1u8,3u8,1u8,5u8,56u8,1u8,61u8,2u8,55u8,8u8,20u8, - 2u8,31u8,1u8,4u8,1u8,4u8,12u8,8u8,11u8,0u8,11u8,2u8,56u8,20u8,12u8,3u8,11u8,1u8,11u8,3u8, - 56u8,21u8,2u8,32u8,1u8,4u8,1u8,4u8,42u8,8u8,11u8,0u8,60u8,0u8,12u8,2u8,9u8,11u8,2u8,54u8, - 4u8,21u8,2u8,33u8,1u8,4u8,2u8,3u8,8u8,61u8,45u8,11u8,0u8,17u8,51u8,12u8,1u8,56u8,1u8,10u8, - 1u8,33u8,4u8,8u8,5u8,11u8,7u8,1u8,17u8,39u8,39u8,7u8,16u8,42u8,8u8,16u8,0u8,20u8,4u8,17u8, - 5u8,20u8,7u8,7u8,17u8,45u8,39u8,11u8,1u8,60u8,2u8,54u8,2u8,12u8,2u8,10u8,2u8,46u8,56u8,2u8, - 4u8,42u8,11u8,2u8,56u8,3u8,12u8,3u8,10u8,3u8,46u8,17u8,63u8,32u8,4u8,39u8,11u8,3u8,17u8,64u8, - 5u8,41u8,11u8,3u8,1u8,5u8,44u8,11u8,2u8,1u8,2u8,34u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,55u8, - 1u8,20u8,2u8,35u8,1u8,0u8,1u8,4u8,63u8,34u8,11u8,0u8,17u8,51u8,12u8,2u8,10u8,2u8,56u8,0u8, - 4u8,7u8,5u8,10u8,7u8,6u8,17u8,38u8,39u8,11u8,2u8,60u8,0u8,12u8,3u8,10u8,3u8,55u8,4u8,20u8, - 32u8,4u8,19u8,5u8,24u8,11u8,3u8,1u8,7u8,10u8,17u8,45u8,39u8,10u8,3u8,54u8,9u8,10u8,1u8,18u8, - 9u8,56u8,22u8,11u8,3u8,54u8,0u8,11u8,1u8,56u8,4u8,2u8,36u8,1u8,0u8,0u8,1u8,3u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,1u8,2u8,8u8,0u8,4u8,0u8,2u8,0u8,3u8,3u8,3u8,2u8, - 4u8,1u8,4u8,2u8,0u8,0u8,3u8,0u8,3u8,1u8,4u8,3u8,1u8,33u8,2u8,33u8,3u8,33u8,4u8,33u8, - 5u8,33u8,6u8,33u8,7u8,33u8,8u8,33u8,9u8,33u8,10u8,33u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk9); - let chunk10 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,16u8,2u8,16u8,50u8,3u8,66u8,136u8,1u8, - 4u8,202u8,1u8,22u8,5u8,224u8,1u8,204u8,1u8,7u8,172u8,3u8,188u8,4u8,8u8,232u8,7u8,32u8,6u8,136u8, - 8u8,119u8,16u8,255u8,8u8,143u8,2u8,10u8,142u8,11u8,26u8,12u8,168u8,11u8,189u8,3u8,13u8,229u8,14u8,6u8, - 15u8,235u8,14u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8, - 0u8,9u8,8u8,0u8,0u8,10u8,4u8,0u8,0u8,11u8,8u8,0u8,0u8,12u8,8u8,0u8,1u8,14u8,5u8,1u8, - 0u8,1u8,3u8,18u8,7u8,1u8,0u8,0u8,1u8,21u8,5u8,1u8,0u8,1u8,1u8,32u8,4u8,1u8,0u8,1u8, - 1u8,40u8,5u8,1u8,0u8,1u8,5u8,41u8,7u8,0u8,0u8,13u8,0u8,1u8,0u8,0u8,15u8,2u8,1u8,0u8, - 0u8,16u8,3u8,1u8,0u8,0u8,17u8,0u8,1u8,0u8,0u8,19u8,4u8,5u8,0u8,0u8,20u8,0u8,6u8,0u8, - 0u8,22u8,0u8,7u8,0u8,0u8,23u8,8u8,1u8,0u8,4u8,28u8,0u8,4u8,0u8,3u8,29u8,11u8,6u8,1u8, - 0u8,3u8,30u8,11u8,12u8,1u8,0u8,7u8,31u8,14u8,15u8,1u8,0u8,6u8,33u8,0u8,1u8,0u8,1u8,34u8, - 0u8,1u8,1u8,0u8,1u8,23u8,18u8,19u8,1u8,0u8,1u8,35u8,20u8,1u8,1u8,0u8,6u8,36u8,0u8,1u8, - 0u8,2u8,37u8,10u8,10u8,0u8,1u8,17u8,22u8,1u8,1u8,0u8,3u8,38u8,1u8,24u8,1u8,0u8,3u8,39u8, - 15u8,24u8,1u8,0u8,5u8,42u8,26u8,27u8,0u8,1u8,43u8,28u8,29u8,1u8,0u8,1u8,44u8,30u8,1u8,1u8, - 0u8,2u8,45u8,10u8,10u8,0u8,9u8,10u8,10u8,10u8,11u8,13u8,13u8,17u8,14u8,17u8,15u8,17u8,18u8,17u8, - 19u8,10u8,20u8,10u8,22u8,17u8,23u8,17u8,1u8,6u8,12u8,0u8,3u8,6u8,12u8,6u8,12u8,11u8,4u8,1u8, - 8u8,0u8,2u8,12u8,5u8,1u8,5u8,1u8,11u8,5u8,1u8,3u8,1u8,1u8,2u8,11u8,6u8,1u8,8u8,0u8, - 11u8,4u8,1u8,8u8,0u8,3u8,6u8,12u8,5u8,3u8,3u8,3u8,11u8,5u8,1u8,3u8,11u8,4u8,1u8,8u8, - 0u8,1u8,3u8,1u8,6u8,11u8,5u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,1u8,8u8,1u8,2u8,7u8,10u8, - 9u8,0u8,3u8,1u8,9u8,0u8,1u8,11u8,7u8,1u8,8u8,0u8,1u8,8u8,0u8,2u8,3u8,6u8,11u8,4u8, - 1u8,9u8,0u8,1u8,11u8,7u8,1u8,9u8,0u8,2u8,5u8,11u8,7u8,1u8,9u8,0u8,3u8,3u8,7u8,10u8, - 8u8,1u8,3u8,1u8,11u8,4u8,1u8,9u8,0u8,4u8,6u8,10u8,8u8,1u8,3u8,11u8,5u8,1u8,3u8,3u8, - 1u8,11u8,5u8,1u8,9u8,0u8,3u8,11u8,6u8,1u8,8u8,0u8,11u8,8u8,1u8,8u8,0u8,11u8,4u8,1u8, - 8u8,0u8,1u8,10u8,2u8,1u8,8u8,9u8,5u8,6u8,12u8,8u8,9u8,8u8,9u8,2u8,1u8,3u8,11u8,6u8, - 1u8,9u8,0u8,11u8,8u8,1u8,9u8,0u8,11u8,4u8,1u8,9u8,0u8,1u8,11u8,8u8,1u8,9u8,0u8,3u8, - 5u8,11u8,7u8,1u8,8u8,0u8,6u8,11u8,4u8,1u8,8u8,0u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8, - 10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8, - 111u8,114u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8,116u8,114u8, - 105u8,110u8,103u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8, - 6u8,118u8,101u8,99u8,116u8,111u8,114u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,23u8,68u8,101u8, - 108u8,101u8,103u8,97u8,116u8,101u8,100u8,77u8,105u8,110u8,116u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,11u8,68u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,115u8,12u8,77u8,105u8,110u8,116u8,67u8,97u8, - 112u8,83u8,116u8,111u8,114u8,101u8,21u8,99u8,108u8,97u8,105u8,109u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,14u8,77u8,105u8,110u8,116u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,27u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,115u8,95u8,102u8,111u8,114u8,95u8,116u8,101u8,115u8,116u8,24u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8, - 95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,16u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8, - 15u8,102u8,105u8,110u8,100u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,19u8,104u8,97u8,115u8, - 95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,14u8,66u8,117u8,114u8, - 110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,4u8,109u8,105u8,110u8,116u8,11u8,100u8,117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8,2u8, - 116u8,111u8,5u8,105u8,110u8,110u8,101u8,114u8,8u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,10u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8,6u8,98u8,111u8,114u8, - 114u8,111u8,119u8,11u8,115u8,119u8,97u8,112u8,95u8,114u8,101u8,109u8,111u8,118u8,101u8,4u8,67u8,111u8,105u8,110u8, - 22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8, - 111u8,114u8,107u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,7u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8, - 20u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,99u8,111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8, - 101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,4u8,110u8, - 111u8,110u8,101u8,4u8,115u8,111u8,109u8,101u8,16u8,70u8,114u8,101u8,101u8,122u8,101u8,67u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,4u8,117u8,116u8,102u8,56u8,37u8,105u8,110u8, - 105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,112u8,97u8,114u8,97u8,108u8,108u8, - 101u8,108u8,105u8,122u8,97u8,98u8,108u8,101u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,18u8,100u8,101u8,115u8,116u8, - 114u8,111u8,121u8,95u8,102u8,114u8,101u8,101u8,122u8,101u8,95u8,99u8,97u8,112u8,9u8,110u8,111u8,116u8,95u8,102u8, - 111u8,117u8,110u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,85u8,12u8,24u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8,11u8,10u8,65u8,112u8, - 116u8,111u8,115u8,32u8,67u8,111u8,105u8,110u8,10u8,2u8,4u8,3u8,65u8,80u8,84u8,18u8,97u8,112u8,116u8,111u8, - 115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,250u8,1u8,3u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,16u8,69u8,78u8,79u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8,73u8, - 69u8,83u8,37u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8, - 104u8,97u8,118u8,101u8,32u8,109u8,105u8,110u8,116u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,68u8,69u8, - 76u8,69u8,71u8,65u8,84u8,69u8,68u8,68u8,77u8,105u8,110u8,116u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,32u8,104u8,97u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,98u8,101u8,101u8,110u8, - 32u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,32u8,116u8,111u8,32u8,116u8,104u8,105u8,115u8,32u8,115u8, - 112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,21u8,69u8,68u8,69u8,76u8,69u8,71u8,65u8,84u8,73u8,79u8,78u8,95u8,78u8,79u8,84u8, - 95u8,70u8,79u8,85u8,78u8,68u8,57u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,102u8,105u8,110u8,100u8,32u8,100u8, - 101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,32u8,111u8,102u8,32u8,109u8,105u8,110u8,116u8,32u8,99u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,32u8,116u8,111u8,32u8,116u8,104u8,105u8,115u8,32u8,97u8,99u8,99u8, - 111u8,117u8,110u8,116u8,0u8,0u8,0u8,2u8,1u8,24u8,1u8,1u8,2u8,1u8,25u8,5u8,2u8,2u8,1u8,26u8, - 10u8,8u8,1u8,3u8,2u8,1u8,27u8,11u8,4u8,1u8,8u8,0u8,0u8,1u8,4u8,2u8,2u8,3u8,9u8,33u8, - 10u8,0u8,17u8,8u8,17u8,4u8,12u8,2u8,14u8,2u8,56u8,0u8,4u8,8u8,5u8,12u8,11u8,0u8,1u8,7u8, - 1u8,39u8,14u8,2u8,56u8,1u8,20u8,12u8,1u8,7u8,3u8,42u8,2u8,15u8,0u8,11u8,1u8,56u8,2u8,19u8, - 1u8,1u8,7u8,3u8,43u8,3u8,16u8,1u8,20u8,12u8,3u8,11u8,0u8,11u8,3u8,18u8,3u8,45u8,3u8,2u8, - 1u8,3u8,0u8,0u8,16u8,21u8,11u8,0u8,17u8,12u8,10u8,1u8,56u8,3u8,6u8,255u8,255u8,255u8,255u8,255u8, - 255u8,255u8,255u8,14u8,2u8,56u8,4u8,12u8,3u8,10u8,1u8,17u8,8u8,11u8,3u8,56u8,5u8,10u8,1u8,11u8, - 2u8,18u8,3u8,45u8,3u8,11u8,1u8,64u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,2u8,45u8, - 2u8,2u8,2u8,1u8,4u8,1u8,2u8,21u8,42u8,14u8,0u8,17u8,16u8,7u8,3u8,42u8,2u8,15u8,0u8,12u8, - 3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,4u8,10u8,3u8,46u8,65u8,13u8,35u8, - 4u8,37u8,5u8,15u8,10u8,3u8,10u8,4u8,12u8,2u8,46u8,11u8,2u8,66u8,13u8,16u8,2u8,20u8,10u8,1u8, - 34u8,4u8,27u8,5u8,32u8,11u8,3u8,1u8,7u8,0u8,17u8,17u8,39u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,8u8,11u8,3u8,11u8,1u8,18u8,1u8,68u8,13u8,2u8,3u8,3u8, - 0u8,1u8,3u8,1u8,7u8,11u8,0u8,17u8,12u8,7u8,4u8,44u8,3u8,19u8,3u8,56u8,6u8,2u8,4u8,0u8, - 0u8,1u8,2u8,23u8,37u8,7u8,3u8,43u8,2u8,16u8,0u8,12u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,12u8,2u8,10u8,1u8,65u8,13u8,12u8,4u8,56u8,7u8,12u8,3u8,10u8,2u8,10u8,4u8,35u8,4u8, - 35u8,5u8,16u8,10u8,1u8,10u8,2u8,66u8,13u8,16u8,2u8,20u8,10u8,0u8,33u8,4u8,30u8,11u8,1u8,1u8, - 11u8,2u8,56u8,8u8,12u8,3u8,5u8,35u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,2u8,5u8,11u8,11u8,3u8,2u8,5u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,17u8,8u8,41u8,3u8,2u8, - 6u8,3u8,0u8,0u8,25u8,22u8,10u8,0u8,17u8,12u8,10u8,0u8,7u8,5u8,17u8,21u8,7u8,6u8,17u8,21u8, - 49u8,8u8,8u8,56u8,9u8,12u8,3u8,12u8,2u8,12u8,1u8,11u8,0u8,10u8,3u8,18u8,3u8,45u8,3u8,11u8, - 2u8,56u8,10u8,11u8,1u8,11u8,3u8,2u8,7u8,1u8,4u8,1u8,3u8,31u8,22u8,11u8,0u8,17u8,8u8,12u8, - 3u8,10u8,3u8,41u8,3u8,4u8,7u8,5u8,10u8,7u8,2u8,17u8,24u8,39u8,11u8,3u8,43u8,3u8,16u8,1u8, - 12u8,5u8,11u8,2u8,11u8,5u8,56u8,4u8,12u8,4u8,11u8,1u8,11u8,4u8,56u8,5u8,2u8,2u8,0u8,3u8, - 0u8,1u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk10); - let chunk11 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,16u8,2u8,16u8,24u8,3u8,40u8,130u8,1u8, - 4u8,170u8,1u8,22u8,5u8,192u8,1u8,81u8,7u8,145u8,2u8,239u8,4u8,8u8,128u8,7u8,32u8,6u8,160u8,7u8, - 50u8,16u8,210u8,7u8,161u8,4u8,10u8,243u8,11u8,16u8,12u8,131u8,12u8,235u8,3u8,13u8,238u8,15u8,4u8,15u8, - 242u8,15u8,4u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8, - 10u8,6u8,0u8,0u8,11u8,8u8,0u8,3u8,18u8,4u8,1u8,0u8,1u8,6u8,26u8,4u8,1u8,6u8,1u8,2u8, - 29u8,8u8,0u8,0u8,12u8,0u8,1u8,0u8,0u8,13u8,0u8,1u8,0u8,0u8,14u8,2u8,1u8,0u8,0u8,15u8, - 2u8,1u8,1u8,0u8,0u8,16u8,0u8,3u8,0u8,0u8,17u8,0u8,1u8,0u8,0u8,19u8,4u8,1u8,1u8,0u8, - 0u8,20u8,5u8,1u8,0u8,0u8,21u8,6u8,1u8,0u8,0u8,22u8,6u8,1u8,1u8,0u8,1u8,27u8,0u8,3u8, - 0u8,5u8,28u8,7u8,7u8,0u8,3u8,30u8,0u8,3u8,1u8,0u8,5u8,31u8,7u8,7u8,0u8,1u8,17u8,0u8, - 11u8,0u8,3u8,32u8,12u8,1u8,1u8,0u8,5u8,33u8,7u8,7u8,0u8,4u8,6u8,0u8,11u8,0u8,3u8,34u8, - 4u8,1u8,1u8,0u8,7u8,35u8,12u8,0u8,0u8,6u8,36u8,15u8,1u8,1u8,6u8,1u8,37u8,12u8,16u8,1u8, - 6u8,3u8,21u8,6u8,1u8,1u8,0u8,3u8,38u8,17u8,18u8,1u8,0u8,12u8,8u8,9u8,10u8,15u8,8u8,12u8, - 10u8,15u8,10u8,18u8,10u8,20u8,14u8,21u8,14u8,22u8,8u8,23u8,10u8,6u8,10u8,1u8,5u8,0u8,3u8,6u8, - 12u8,10u8,5u8,10u8,3u8,1u8,1u8,2u8,5u8,11u8,2u8,1u8,9u8,0u8,2u8,6u8,12u8,1u8,3u8,6u8, - 12u8,5u8,3u8,1u8,3u8,1u8,8u8,4u8,4u8,3u8,3u8,3u8,5u8,1u8,9u8,0u8,1u8,12u8,1u8,6u8, - 12u8,3u8,5u8,7u8,8u8,1u8,8u8,1u8,1u8,8u8,0u8,2u8,7u8,11u8,3u8,1u8,9u8,0u8,9u8,0u8, - 1u8,11u8,3u8,1u8,9u8,0u8,2u8,6u8,12u8,3u8,1u8,11u8,2u8,1u8,9u8,0u8,7u8,103u8,101u8,110u8, - 101u8,115u8,105u8,115u8,16u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,13u8,97u8,112u8,116u8,111u8,115u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,7u8,97u8,99u8,99u8,111u8, - 117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,4u8,99u8,111u8,105u8,110u8,13u8, - 99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8, - 101u8,118u8,101u8,110u8,116u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,36u8,68u8,105u8,114u8,101u8,99u8,116u8,67u8, - 111u8,105u8,110u8,84u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,67u8,111u8,110u8,102u8,105u8,103u8,85u8,112u8,100u8, - 97u8,116u8,101u8,100u8,69u8,118u8,101u8,110u8,116u8,20u8,68u8,105u8,114u8,101u8,99u8,116u8,84u8,114u8,97u8,110u8, - 115u8,102u8,101u8,114u8,67u8,111u8,110u8,102u8,105u8,103u8,21u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,36u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,105u8,115u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8, - 100u8,95u8,102u8,111u8,114u8,95u8,97u8,112u8,116u8,14u8,98u8,97u8,116u8,99u8,104u8,95u8,116u8,114u8,97u8,110u8, - 115u8,102u8,101u8,114u8,20u8,98u8,97u8,116u8,99u8,104u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8, - 99u8,111u8,105u8,110u8,115u8,33u8,99u8,97u8,110u8,95u8,114u8,101u8,99u8,101u8,105u8,118u8,101u8,95u8,100u8,105u8, - 114u8,101u8,99u8,116u8,95u8,99u8,111u8,105u8,110u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,115u8,14u8, - 99u8,114u8,101u8,97u8,116u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,4u8,67u8,111u8,105u8,110u8,13u8, - 100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,99u8,111u8,105u8,110u8,115u8,31u8,115u8,101u8,116u8,95u8,97u8,108u8, - 108u8,111u8,119u8,95u8,100u8,105u8,114u8,101u8,99u8,116u8,95u8,99u8,111u8,105u8,110u8,95u8,116u8,114u8,97u8,110u8, - 115u8,102u8,101u8,114u8,115u8,8u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,14u8,116u8,114u8,97u8,110u8,115u8, - 102u8,101u8,114u8,95u8,99u8,111u8,105u8,110u8,115u8,26u8,110u8,101u8,119u8,95u8,97u8,108u8,108u8,111u8,119u8,95u8, - 100u8,105u8,114u8,101u8,99u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,115u8,30u8,97u8,108u8,108u8, - 111u8,119u8,95u8,97u8,114u8,98u8,105u8,116u8,114u8,97u8,114u8,121u8,95u8,99u8,111u8,105u8,110u8,95u8,116u8,114u8, - 97u8,110u8,115u8,102u8,101u8,114u8,115u8,27u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8, - 116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8, - 116u8,72u8,97u8,110u8,100u8,108u8,101u8,9u8,101u8,120u8,105u8,115u8,116u8,115u8,95u8,97u8,116u8,9u8,110u8,111u8, - 116u8,95u8,102u8,111u8,117u8,110u8,100u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,21u8,105u8,115u8, - 95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8,16u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,8u8,114u8,101u8,103u8, - 105u8,115u8,116u8,101u8,114u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8, - 105u8,101u8,100u8,7u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8, - 111u8,102u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8, - 101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8, - 140u8,4u8,5u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8, - 95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,23u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8, - 111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,31u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,78u8,79u8,84u8,95u8,82u8,69u8,71u8,73u8, - 83u8,84u8,69u8,82u8,69u8,68u8,95u8,70u8,79u8,82u8,95u8,65u8,80u8,84u8,41u8,65u8,99u8,99u8,111u8,117u8, - 110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8, - 32u8,116u8,111u8,32u8,114u8,101u8,99u8,101u8,105u8,118u8,101u8,32u8,65u8,80u8,84u8,46u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,46u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8, - 79u8,84u8,95u8,65u8,67u8,67u8,69u8,80u8,84u8,95u8,68u8,73u8,82u8,69u8,67u8,84u8,95u8,67u8,79u8,73u8, - 78u8,95u8,84u8,82u8,65u8,78u8,83u8,70u8,69u8,82u8,83u8,75u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8, - 111u8,112u8,116u8,101u8,100u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8,32u8,114u8,101u8,99u8,101u8,105u8,118u8,105u8, - 110u8,103u8,32u8,99u8,111u8,105u8,110u8,115u8,32u8,116u8,104u8,97u8,116u8,32u8,116u8,104u8,101u8,121u8,32u8,100u8, - 105u8,100u8,32u8,110u8,111u8,116u8,32u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,32u8,116u8,111u8,32u8,114u8, - 101u8,99u8,101u8,105u8,118u8,101u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,47u8,69u8,65u8,67u8,67u8, - 79u8,85u8,78u8,84u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,65u8,67u8,67u8,69u8,80u8,84u8, - 95u8,68u8,73u8,82u8,69u8,67u8,84u8,95u8,84u8,79u8,75u8,69u8,78u8,95u8,84u8,82u8,65u8,78u8,83u8,70u8, - 69u8,82u8,83u8,51u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,111u8,112u8,116u8,101u8,100u8,32u8,111u8,117u8, - 116u8,32u8,111u8,102u8,32u8,100u8,105u8,114u8,101u8,99u8,116u8,108u8,121u8,32u8,114u8,101u8,99u8,101u8,105u8,118u8, - 105u8,110u8,103u8,32u8,78u8,70u8,84u8,32u8,116u8,111u8,107u8,101u8,110u8,115u8,46u8,5u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,42u8,69u8,77u8,73u8,83u8,77u8,65u8,84u8,67u8,72u8,73u8,78u8,71u8,95u8,82u8,69u8,67u8, - 73u8,80u8,73u8,69u8,78u8,84u8,83u8,95u8,65u8,78u8,68u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,83u8,95u8, - 76u8,69u8,78u8,71u8,84u8,72u8,60u8,84u8,104u8,101u8,32u8,108u8,101u8,110u8,103u8,116u8,104u8,115u8,32u8,111u8, - 102u8,32u8,116u8,104u8,101u8,32u8,114u8,101u8,99u8,105u8,112u8,105u8,101u8,110u8,116u8,115u8,32u8,97u8,110u8,100u8, - 32u8,97u8,109u8,111u8,117u8,110u8,116u8,115u8,32u8,108u8,105u8,115u8,116u8,115u8,32u8,100u8,111u8,110u8,39u8,116u8, - 32u8,109u8,97u8,116u8,99u8,104u8,46u8,0u8,1u8,33u8,99u8,97u8,110u8,95u8,114u8,101u8,99u8,101u8,105u8,118u8, - 101u8,95u8,100u8,105u8,114u8,101u8,99u8,116u8,95u8,99u8,111u8,105u8,110u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8, - 101u8,114u8,115u8,1u8,1u8,0u8,0u8,2u8,1u8,23u8,1u8,1u8,2u8,2u8,24u8,1u8,25u8,11u8,3u8,1u8, - 8u8,0u8,0u8,1u8,0u8,0u8,1u8,8u8,11u8,0u8,17u8,10u8,4u8,4u8,5u8,7u8,7u8,2u8,17u8,11u8, - 39u8,2u8,1u8,1u8,0u8,0u8,1u8,10u8,10u8,0u8,17u8,0u8,11u8,0u8,56u8,0u8,4u8,6u8,5u8,9u8, - 7u8,3u8,17u8,11u8,39u8,2u8,2u8,1u8,4u8,0u8,9u8,43u8,14u8,1u8,65u8,0u8,12u8,5u8,10u8,5u8, - 14u8,2u8,65u8,7u8,33u8,4u8,9u8,5u8,14u8,11u8,0u8,1u8,7u8,4u8,17u8,13u8,39u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,4u8,10u8,5u8,35u8,4u8,40u8,5u8,21u8,14u8,1u8,10u8, - 4u8,66u8,0u8,20u8,12u8,6u8,14u8,2u8,10u8,4u8,66u8,7u8,20u8,12u8,3u8,10u8,0u8,11u8,6u8,11u8, - 3u8,17u8,8u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,16u8,11u8, - 0u8,1u8,2u8,3u8,1u8,4u8,1u8,1u8,9u8,43u8,14u8,1u8,65u8,0u8,12u8,5u8,10u8,5u8,14u8,2u8, - 65u8,7u8,33u8,4u8,9u8,5u8,14u8,11u8,0u8,1u8,7u8,4u8,17u8,13u8,39u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,4u8,10u8,4u8,10u8,5u8,35u8,4u8,40u8,5u8,21u8,14u8,1u8,10u8,4u8,66u8, - 0u8,20u8,12u8,6u8,14u8,2u8,10u8,4u8,66u8,7u8,20u8,12u8,3u8,10u8,0u8,11u8,6u8,11u8,3u8,56u8, - 1u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,16u8,11u8,0u8,1u8, - 2u8,4u8,1u8,0u8,1u8,1u8,3u8,14u8,10u8,0u8,41u8,1u8,32u8,4u8,7u8,8u8,12u8,1u8,5u8,12u8, - 11u8,0u8,43u8,1u8,16u8,0u8,20u8,12u8,1u8,11u8,1u8,2u8,5u8,1u8,4u8,0u8,11u8,6u8,11u8,0u8, - 17u8,14u8,12u8,1u8,14u8,1u8,56u8,2u8,2u8,6u8,1u8,0u8,1u8,1u8,11u8,26u8,10u8,0u8,17u8,10u8, - 32u8,4u8,6u8,10u8,0u8,17u8,5u8,10u8,0u8,56u8,3u8,32u8,4u8,22u8,10u8,0u8,17u8,4u8,4u8,14u8, - 5u8,17u8,7u8,0u8,17u8,16u8,39u8,10u8,0u8,17u8,17u8,12u8,2u8,14u8,2u8,56u8,4u8,11u8,0u8,11u8, - 1u8,56u8,5u8,2u8,7u8,1u8,4u8,1u8,1u8,13u8,44u8,10u8,0u8,17u8,19u8,12u8,2u8,10u8,2u8,41u8, - 1u8,4u8,30u8,11u8,0u8,1u8,11u8,2u8,42u8,1u8,12u8,3u8,10u8,3u8,16u8,0u8,20u8,10u8,1u8,33u8, - 4u8,20u8,11u8,3u8,1u8,2u8,10u8,1u8,10u8,3u8,15u8,0u8,21u8,11u8,3u8,15u8,1u8,11u8,1u8,18u8, - 0u8,56u8,6u8,5u8,43u8,10u8,1u8,10u8,0u8,56u8,7u8,18u8,1u8,12u8,4u8,13u8,4u8,15u8,1u8,11u8, - 1u8,18u8,0u8,56u8,6u8,11u8,0u8,11u8,4u8,45u8,1u8,2u8,8u8,1u8,4u8,0u8,11u8,20u8,10u8,1u8, - 17u8,10u8,32u8,4u8,6u8,10u8,1u8,17u8,5u8,10u8,1u8,56u8,0u8,32u8,4u8,15u8,10u8,1u8,17u8,17u8, - 12u8,3u8,14u8,3u8,56u8,2u8,11u8,0u8,11u8,1u8,11u8,2u8,56u8,8u8,2u8,9u8,1u8,4u8,1u8,1u8, - 1u8,6u8,11u8,1u8,11u8,0u8,11u8,2u8,56u8,9u8,56u8,10u8,2u8,1u8,0u8,1u8,1u8,0u8,0u8,0u8, - 1u8,0u8, - ]; - vector::push_back(&mut code, chunk11); - let chunk12 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,2u8,3u8,2u8,5u8,5u8,7u8,4u8,7u8, - 11u8,36u8,8u8,47u8,32u8,12u8,79u8,4u8,0u8,0u8,0u8,1u8,0u8,1u8,0u8,0u8,1u8,10u8,2u8,19u8, - 116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,99u8,111u8,110u8,116u8,101u8,120u8,116u8,15u8, - 103u8,101u8,116u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,95u8,104u8,97u8,115u8,104u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,1u8,2u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk12); - let chunk13 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,35u8,5u8, - 45u8,8u8,7u8,53u8,182u8,1u8,8u8,235u8,1u8,32u8,6u8,139u8,2u8,54u8,16u8,193u8,2u8,190u8,1u8,10u8, - 255u8,3u8,5u8,12u8,132u8,4u8,77u8,15u8,209u8,4u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,8u8, - 0u8,0u8,5u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,7u8,0u8,1u8,0u8,0u8,8u8,0u8,1u8, - 0u8,0u8,9u8,2u8,0u8,0u8,1u8,11u8,3u8,3u8,0u8,2u8,12u8,2u8,0u8,0u8,0u8,1u8,1u8,1u8, - 6u8,12u8,1u8,3u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,12u8,99u8,104u8,97u8,105u8,110u8,95u8,115u8, - 116u8,97u8,116u8,117u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,16u8,71u8,101u8,110u8,101u8,115u8,105u8,115u8,69u8,110u8,100u8,77u8, - 97u8,114u8,107u8,101u8,114u8,14u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8, - 16u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,110u8,103u8,10u8,105u8,115u8, - 95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,12u8,105u8,115u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,110u8, - 103u8,15u8,115u8,101u8,116u8,95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,95u8,101u8,110u8,100u8,11u8,100u8,117u8, - 109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8, - 97u8,116u8,101u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8, - 109u8,101u8,119u8,111u8,114u8,107u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,169u8,1u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,14u8,69u8,78u8,79u8,84u8,95u8,79u8,80u8,69u8,82u8,65u8,84u8,73u8,78u8,71u8,46u8,84u8,104u8, - 101u8,32u8,98u8,108u8,111u8,99u8,107u8,99u8,104u8,97u8,105u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8, - 105u8,110u8,32u8,116u8,104u8,101u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,110u8,103u8,32u8,115u8,116u8,97u8, - 116u8,117u8,115u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,69u8,78u8,79u8,84u8,95u8,71u8,69u8, - 78u8,69u8,83u8,73u8,83u8,44u8,84u8,104u8,101u8,32u8,98u8,108u8,111u8,99u8,107u8,99u8,104u8,97u8,105u8,110u8, - 32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,103u8,101u8,110u8,101u8,115u8, - 105u8,115u8,32u8,115u8,116u8,97u8,116u8,117u8,115u8,46u8,0u8,2u8,10u8,105u8,115u8,95u8,103u8,101u8,110u8,101u8, - 115u8,105u8,115u8,1u8,1u8,0u8,12u8,105u8,115u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,105u8,110u8,103u8,1u8, - 1u8,0u8,0u8,2u8,1u8,10u8,1u8,0u8,1u8,0u8,0u8,0u8,7u8,17u8,2u8,4u8,3u8,5u8,6u8,7u8, - 1u8,17u8,5u8,39u8,2u8,1u8,1u8,0u8,0u8,0u8,7u8,17u8,3u8,4u8,3u8,5u8,6u8,7u8,1u8,17u8, - 5u8,39u8,2u8,2u8,1u8,0u8,0u8,0u8,4u8,7u8,2u8,41u8,0u8,32u8,2u8,3u8,1u8,0u8,0u8,0u8, - 3u8,7u8,2u8,41u8,0u8,2u8,4u8,3u8,0u8,0u8,0u8,7u8,10u8,0u8,17u8,6u8,11u8,0u8,9u8,18u8, - 0u8,45u8,0u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk13); - let chunk14 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,35u8,5u8, - 45u8,16u8,7u8,61u8,197u8,1u8,8u8,130u8,2u8,32u8,6u8,162u8,2u8,98u8,16u8,132u8,3u8,191u8,1u8,10u8, - 195u8,4u8,5u8,12u8,200u8,4u8,129u8,1u8,13u8,201u8,5u8,2u8,15u8,203u8,5u8,2u8,0u8,1u8,0u8,2u8, - 0u8,3u8,0u8,4u8,8u8,0u8,0u8,5u8,0u8,1u8,0u8,0u8,6u8,0u8,1u8,0u8,0u8,7u8,2u8,0u8, - 0u8,0u8,8u8,3u8,0u8,0u8,2u8,10u8,2u8,0u8,0u8,2u8,11u8,2u8,0u8,0u8,1u8,12u8,1u8,1u8, - 0u8,0u8,1u8,3u8,1u8,6u8,12u8,3u8,6u8,12u8,5u8,3u8,2u8,7u8,8u8,0u8,3u8,7u8,103u8,101u8, - 110u8,101u8,115u8,105u8,115u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,5u8,101u8,114u8,114u8,111u8, - 114u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,23u8,67u8, - 117u8,114u8,114u8,101u8,110u8,116u8,84u8,105u8,109u8,101u8,77u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8,111u8,110u8, - 100u8,115u8,16u8,110u8,111u8,119u8,95u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,11u8, - 110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,20u8,115u8,101u8,116u8,95u8,116u8,105u8,109u8,101u8, - 95u8,104u8,97u8,115u8,95u8,115u8,116u8,97u8,114u8,116u8,101u8,100u8,18u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8, - 103u8,108u8,111u8,98u8,97u8,108u8,95u8,116u8,105u8,109u8,101u8,12u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8, - 111u8,110u8,100u8,115u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8, - 97u8,109u8,101u8,119u8,111u8,114u8,107u8,9u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,118u8,109u8,16u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,64u8,66u8,15u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8, - 95u8,118u8,49u8,170u8,1u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,78u8,79u8,84u8,95u8, - 79u8,80u8,69u8,82u8,65u8,84u8,73u8,78u8,71u8,47u8,84u8,104u8,101u8,32u8,98u8,108u8,111u8,99u8,107u8,99u8, - 104u8,97u8,105u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,105u8,110u8,32u8,97u8,110u8,32u8,111u8,112u8, - 101u8,114u8,97u8,116u8,105u8,110u8,103u8,32u8,115u8,116u8,97u8,116u8,101u8,32u8,121u8,101u8,116u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,84u8,73u8,77u8,69u8,83u8, - 84u8,65u8,77u8,80u8,33u8,65u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,116u8,105u8,109u8,101u8, - 115u8,116u8,97u8,109u8,112u8,32u8,119u8,97u8,115u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,0u8,2u8, - 11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,1u8,1u8,0u8,16u8,110u8,111u8,119u8,95u8, - 109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,1u8,1u8,0u8,0u8,2u8,1u8,9u8,3u8, - 0u8,1u8,0u8,1u8,0u8,0u8,5u8,7u8,3u8,43u8,0u8,16u8,0u8,20u8,2u8,1u8,1u8,0u8,1u8,0u8, - 0u8,4u8,17u8,0u8,7u8,2u8,26u8,2u8,2u8,3u8,0u8,0u8,0u8,7u8,10u8,0u8,17u8,4u8,11u8,0u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,0u8,45u8,0u8,2u8,3u8,1u8,0u8,1u8,0u8,4u8, - 39u8,11u8,0u8,17u8,5u8,7u8,3u8,42u8,0u8,12u8,3u8,10u8,3u8,16u8,0u8,20u8,12u8,4u8,11u8,1u8, - 7u8,4u8,33u8,4u8,24u8,11u8,3u8,1u8,11u8,4u8,11u8,2u8,33u8,4u8,20u8,5u8,23u8,7u8,0u8,17u8, - 6u8,39u8,5u8,38u8,11u8,4u8,10u8,2u8,35u8,4u8,29u8,5u8,34u8,11u8,3u8,1u8,7u8,0u8,17u8,6u8, - 39u8,11u8,2u8,11u8,3u8,15u8,0u8,21u8,2u8,0u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk14); - let chunk15 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,28u8,2u8,28u8,68u8,3u8,96u8,251u8,1u8, - 4u8,219u8,2u8,60u8,5u8,151u8,3u8,200u8,3u8,7u8,223u8,6u8,247u8,8u8,8u8,214u8,15u8,32u8,6u8,246u8, - 15u8,231u8,1u8,16u8,221u8,17u8,187u8,13u8,10u8,152u8,31u8,137u8,1u8,11u8,161u8,32u8,4u8,12u8,165u8,32u8, - 157u8,11u8,13u8,194u8,43u8,34u8,14u8,228u8,43u8,26u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8, - 0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8, - 6u8,0u8,0u8,15u8,4u8,1u8,4u8,0u8,0u8,16u8,6u8,0u8,0u8,17u8,6u8,0u8,0u8,18u8,6u8,0u8, - 0u8,19u8,4u8,0u8,0u8,20u8,8u8,1u8,4u8,0u8,6u8,22u8,7u8,1u8,0u8,0u8,8u8,23u8,7u8,2u8, - 0u8,0u8,0u8,0u8,9u8,24u8,7u8,0u8,13u8,53u8,7u8,0u8,4u8,57u8,4u8,1u8,6u8,1u8,10u8,62u8, - 4u8,2u8,3u8,1u8,0u8,1u8,0u8,21u8,0u8,1u8,1u8,4u8,0u8,25u8,2u8,3u8,1u8,4u8,0u8,26u8, - 4u8,3u8,1u8,4u8,0u8,27u8,5u8,6u8,1u8,4u8,0u8,28u8,5u8,3u8,1u8,4u8,0u8,29u8,5u8,3u8, - 1u8,4u8,0u8,30u8,5u8,1u8,1u8,4u8,0u8,31u8,5u8,7u8,1u8,4u8,0u8,32u8,5u8,1u8,1u8,4u8, - 0u8,33u8,5u8,1u8,1u8,4u8,0u8,34u8,0u8,1u8,1u8,4u8,0u8,35u8,8u8,7u8,1u8,4u8,0u8,36u8, - 5u8,9u8,1u8,4u8,0u8,37u8,10u8,7u8,1u8,4u8,0u8,38u8,11u8,7u8,1u8,4u8,6u8,65u8,14u8,1u8, - 1u8,0u8,6u8,66u8,14u8,15u8,1u8,0u8,3u8,67u8,3u8,3u8,0u8,9u8,68u8,6u8,18u8,0u8,2u8,69u8, - 15u8,6u8,1u8,0u8,8u8,70u8,20u8,7u8,2u8,4u8,4u8,8u8,71u8,21u8,1u8,2u8,4u8,4u8,8u8,72u8, - 22u8,23u8,2u8,4u8,4u8,11u8,73u8,7u8,3u8,0u8,6u8,74u8,9u8,24u8,1u8,0u8,10u8,70u8,26u8,7u8, - 2u8,3u8,0u8,4u8,75u8,28u8,7u8,1u8,6u8,10u8,76u8,29u8,30u8,2u8,3u8,0u8,10u8,66u8,32u8,33u8, - 2u8,3u8,0u8,8u8,66u8,21u8,33u8,2u8,4u8,4u8,5u8,77u8,6u8,1u8,0u8,3u8,78u8,3u8,3u8,0u8, - 5u8,79u8,6u8,3u8,0u8,12u8,80u8,7u8,6u8,0u8,7u8,81u8,8u8,38u8,0u8,3u8,82u8,3u8,3u8,0u8, - 10u8,83u8,7u8,39u8,2u8,3u8,4u8,1u8,84u8,8u8,40u8,1u8,6u8,13u8,85u8,7u8,44u8,1u8,0u8,3u8, - 86u8,3u8,3u8,0u8,6u8,87u8,46u8,9u8,1u8,0u8,8u8,76u8,22u8,30u8,2u8,4u8,4u8,15u8,13u8,16u8, - 13u8,2u8,9u8,19u8,1u8,20u8,19u8,21u8,19u8,22u8,19u8,24u8,9u8,25u8,25u8,26u8,27u8,27u8,25u8,9u8, - 9u8,28u8,25u8,29u8,19u8,5u8,9u8,0u8,9u8,10u8,9u8,36u8,25u8,37u8,27u8,37u8,41u8,37u8,42u8,37u8, - 43u8,38u8,9u8,26u8,41u8,7u8,9u8,26u8,42u8,40u8,9u8,41u8,19u8,19u8,3u8,26u8,43u8,1u8,6u8,11u8, - 1u8,1u8,9u8,0u8,1u8,1u8,8u8,5u8,5u8,9u8,0u8,10u8,2u8,4u8,3u8,11u8,7u8,1u8,4u8,11u8, - 8u8,2u8,8u8,9u8,10u8,2u8,1u8,3u8,9u8,5u8,5u8,9u8,0u8,10u8,2u8,4u8,3u8,11u8,7u8,1u8, - 4u8,11u8,8u8,2u8,8u8,9u8,10u8,2u8,1u8,2u8,5u8,3u8,1u8,10u8,2u8,0u8,1u8,6u8,12u8,1u8, - 9u8,0u8,3u8,5u8,3u8,10u8,2u8,5u8,6u8,9u8,0u8,5u8,3u8,3u8,1u8,2u8,1u8,4u8,1u8,4u8, - 1u8,6u8,11u8,7u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,17u8,1u8,11u8,8u8,2u8,8u8,9u8,10u8,2u8, - 4u8,3u8,11u8,7u8,1u8,4u8,3u8,7u8,11u8,12u8,2u8,3u8,11u8,1u8,1u8,9u8,0u8,8u8,9u8,7u8, - 11u8,8u8,2u8,8u8,9u8,10u8,2u8,6u8,8u8,9u8,5u8,3u8,11u8,7u8,1u8,9u8,0u8,10u8,2u8,8u8, - 9u8,3u8,7u8,11u8,6u8,1u8,9u8,0u8,1u8,2u8,1u8,8u8,9u8,2u8,8u8,9u8,10u8,2u8,3u8,7u8, - 11u8,8u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,2u8,6u8,11u8,8u8,2u8,9u8,0u8,9u8,1u8, - 6u8,9u8,0u8,2u8,7u8,11u8,8u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,2u8,9u8,0u8,9u8,1u8, - 1u8,11u8,7u8,1u8,9u8,0u8,2u8,3u8,11u8,1u8,1u8,9u8,0u8,3u8,7u8,11u8,12u8,2u8,9u8,0u8, - 9u8,1u8,9u8,0u8,9u8,1u8,1u8,8u8,0u8,2u8,7u8,11u8,11u8,1u8,9u8,0u8,9u8,0u8,2u8,7u8, - 11u8,12u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8,1u8,6u8,1u8,3u8,3u8,4u8,6u8,11u8, - 1u8,1u8,9u8,0u8,4u8,2u8,6u8,11u8,12u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,6u8,9u8,1u8, - 2u8,8u8,9u8,7u8,11u8,1u8,1u8,9u8,0u8,4u8,8u8,9u8,6u8,11u8,8u8,2u8,8u8,9u8,10u8,2u8, - 7u8,11u8,1u8,1u8,9u8,0u8,3u8,2u8,1u8,6u8,11u8,1u8,1u8,9u8,0u8,4u8,11u8,12u8,2u8,3u8, - 11u8,1u8,1u8,9u8,0u8,8u8,5u8,5u8,11u8,6u8,1u8,9u8,0u8,1u8,5u8,1u8,11u8,12u8,2u8,9u8, - 0u8,9u8,1u8,1u8,11u8,11u8,1u8,9u8,0u8,1u8,8u8,2u8,1u8,8u8,3u8,1u8,8u8,4u8,1u8,8u8, - 10u8,4u8,8u8,9u8,7u8,11u8,1u8,1u8,9u8,0u8,1u8,7u8,11u8,6u8,1u8,9u8,0u8,1u8,7u8,11u8, - 7u8,1u8,9u8,0u8,12u8,1u8,1u8,1u8,1u8,7u8,10u8,2u8,7u8,10u8,2u8,8u8,9u8,8u8,9u8,1u8, - 7u8,11u8,1u8,1u8,9u8,0u8,1u8,7u8,11u8,6u8,1u8,9u8,0u8,11u8,8u8,9u8,6u8,11u8,8u8,2u8, - 8u8,9u8,10u8,2u8,8u8,9u8,6u8,11u8,8u8,2u8,8u8,9u8,10u8,2u8,1u8,1u8,3u8,8u8,9u8,7u8, - 11u8,1u8,1u8,9u8,0u8,10u8,2u8,7u8,11u8,6u8,1u8,9u8,0u8,6u8,118u8,111u8,116u8,105u8,110u8,103u8, - 7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,3u8,98u8,99u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8, - 118u8,101u8,110u8,116u8,8u8,102u8,114u8,111u8,109u8,95u8,98u8,99u8,115u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8, - 6u8,115u8,105u8,103u8,110u8,101u8,114u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,6u8,115u8, - 116u8,114u8,105u8,110u8,103u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8, - 112u8,19u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,99u8,111u8,110u8,116u8,101u8,120u8, - 116u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,19u8,67u8,114u8,101u8,97u8,116u8,101u8,80u8,114u8, - 111u8,112u8,111u8,115u8,97u8,108u8,69u8,118u8,101u8,110u8,116u8,8u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8, - 18u8,82u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,70u8,111u8,114u8,117u8,109u8,69u8,118u8,101u8,110u8,116u8,15u8, - 82u8,101u8,115u8,111u8,108u8,118u8,101u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,9u8,86u8,111u8,116u8,101u8, - 69u8,118u8,101u8,110u8,116u8,12u8,86u8,111u8,116u8,105u8,110u8,103u8,69u8,118u8,101u8,110u8,116u8,115u8,11u8,86u8, - 111u8,116u8,105u8,110u8,103u8,70u8,111u8,114u8,117u8,109u8,21u8,99u8,97u8,110u8,95u8,98u8,101u8,95u8,114u8,101u8, - 115u8,111u8,108u8,118u8,101u8,100u8,95u8,101u8,97u8,114u8,108u8,121u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,9u8, - 83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,15u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,18u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8, - 112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,118u8,50u8,18u8,103u8,101u8,116u8,95u8,101u8,120u8,101u8,99u8, - 117u8,116u8,105u8,111u8,110u8,95u8,104u8,97u8,115u8,104u8,28u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8, - 115u8,97u8,108u8,95u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8,115u8,18u8, - 103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,115u8,116u8,97u8,116u8,101u8,35u8,105u8, - 115u8,95u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,116u8,101u8,112u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8, - 108u8,95u8,105u8,110u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,22u8,105u8,115u8,95u8,112u8,114u8, - 111u8,112u8,111u8,115u8,97u8,108u8,95u8,114u8,101u8,115u8,111u8,108u8,118u8,97u8,98u8,108u8,101u8,11u8,105u8,115u8, - 95u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,16u8,105u8,115u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8, - 99u8,108u8,111u8,115u8,101u8,100u8,21u8,105u8,115u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,101u8,114u8, - 105u8,111u8,100u8,95u8,111u8,118u8,101u8,114u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,7u8,114u8,101u8, - 115u8,111u8,108u8,118u8,101u8,19u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,95u8,118u8,50u8,4u8,118u8,111u8,116u8,101u8,11u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8, - 105u8,100u8,31u8,101u8,97u8,114u8,108u8,121u8,95u8,114u8,101u8,115u8,111u8,108u8,117u8,116u8,105u8,111u8,110u8,95u8, - 118u8,111u8,116u8,101u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,14u8,101u8,120u8,101u8,99u8,117u8, - 116u8,105u8,111u8,110u8,95u8,104u8,97u8,115u8,104u8,15u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8,110u8, - 95u8,115u8,101u8,99u8,115u8,8u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,18u8,109u8,105u8,110u8,95u8,118u8, - 111u8,116u8,101u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,8u8,112u8,114u8,111u8,112u8,111u8,115u8, - 101u8,114u8,17u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,99u8,111u8,110u8,116u8,101u8,110u8,116u8, - 18u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,116u8,105u8,109u8,101u8,95u8,115u8,101u8,99u8,115u8,9u8, - 121u8,101u8,115u8,95u8,118u8,111u8,116u8,101u8,115u8,8u8,110u8,111u8,95u8,118u8,111u8,116u8,101u8,115u8,20u8,114u8, - 101u8,115u8,111u8,108u8,117u8,116u8,105u8,111u8,110u8,95u8,116u8,105u8,109u8,101u8,95u8,115u8,101u8,99u8,115u8,15u8, - 104u8,111u8,115u8,116u8,105u8,110u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,18u8,112u8,114u8,111u8,112u8, - 111u8,115u8,97u8,108u8,95u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,8u8,84u8,121u8,112u8,101u8,73u8, - 110u8,102u8,111u8,14u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,95u8,101u8,97u8,114u8,108u8,121u8,9u8,110u8, - 117u8,109u8,95u8,118u8,111u8,116u8,101u8,115u8,22u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8, - 111u8,115u8,97u8,108u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8, - 100u8,108u8,101u8,21u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,95u8,102u8,111u8,114u8,117u8,109u8,95u8,101u8, - 118u8,101u8,110u8,116u8,115u8,23u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,118u8,111u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8, - 115u8,9u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,115u8,5u8,84u8,97u8,98u8,108u8,101u8,6u8,101u8,118u8, - 101u8,110u8,116u8,115u8,16u8,110u8,101u8,120u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,105u8, - 100u8,7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,16u8,105u8,110u8,118u8, - 97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,4u8,117u8,116u8,102u8,56u8,8u8,116u8, - 111u8,95u8,98u8,121u8,116u8,101u8,115u8,3u8,97u8,100u8,100u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8, - 95u8,107u8,101u8,121u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8, - 110u8,100u8,115u8,4u8,115u8,111u8,109u8,101u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,10u8, - 98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,7u8,116u8,111u8,95u8,98u8,111u8,111u8,108u8,13u8,105u8, - 110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,6u8,116u8,111u8,95u8,117u8,54u8,52u8,15u8, - 103u8,101u8,116u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,95u8,104u8,97u8,115u8,104u8,10u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,95u8,111u8,102u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8, - 115u8,3u8,110u8,101u8,119u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8, - 108u8,101u8,7u8,116u8,121u8,112u8,101u8,95u8,111u8,102u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8, - 110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,36u8,35u8,73u8,83u8,95u8,77u8,85u8,76u8,84u8,73u8, - 95u8,83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,73u8,78u8,95u8,69u8,88u8, - 69u8,67u8,85u8,84u8,73u8,79u8,78u8,10u8,2u8,27u8,26u8,73u8,83u8,95u8,77u8,85u8,76u8,84u8,73u8,95u8, - 83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,75u8,69u8,89u8,3u8,8u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,29u8,28u8,82u8,69u8,83u8,79u8,76u8,86u8,65u8,66u8,76u8, - 69u8,95u8,84u8,73u8,77u8,69u8,95u8,77u8,69u8,84u8,65u8,68u8,65u8,84u8,65u8,95u8,75u8,69u8,89u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,166u8,13u8, - 12u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8, - 69u8,88u8,69u8,67u8,85u8,84u8,73u8,79u8,78u8,95u8,72u8,65u8,83u8,72u8,95u8,78u8,79u8,84u8,95u8,77u8, - 65u8,84u8,67u8,72u8,73u8,78u8,71u8,71u8,67u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,115u8,99u8,114u8,105u8, - 112u8,116u8,39u8,115u8,32u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,32u8,104u8,97u8,115u8,104u8,32u8, - 100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,32u8,116u8,104u8,101u8,32u8,115u8, - 112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,39u8,115u8,2u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,67u8,65u8, - 78u8,78u8,79u8,84u8,95u8,66u8,69u8,95u8,82u8,69u8,83u8,79u8,76u8,86u8,69u8,68u8,112u8,80u8,114u8,111u8, - 112u8,111u8,115u8,97u8,108u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,114u8,101u8,115u8,111u8, - 108u8,118u8,101u8,100u8,46u8,32u8,69u8,105u8,116u8,104u8,101u8,114u8,32u8,118u8,111u8,116u8,105u8,110u8,103u8,32u8, - 100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,116u8,32u8,112u8,97u8,115u8, - 115u8,101u8,100u8,44u8,32u8,110u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,118u8,111u8,116u8,101u8, - 115u8,44u8,32u8,111u8,114u8,32u8,102u8,101u8,119u8,101u8,114u8,32u8,121u8,101u8,115u8,32u8,116u8,104u8,97u8,110u8, - 32u8,110u8,111u8,32u8,118u8,111u8,116u8,101u8,115u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,80u8, - 82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,82u8,69u8,83u8,79u8, - 76u8,86u8,69u8,68u8,42u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8, - 32u8,98u8,101u8,32u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8, - 97u8,110u8,32u8,111u8,110u8,99u8,101u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8,80u8,82u8,79u8, - 80u8,79u8,83u8,65u8,76u8,95u8,69u8,77u8,80u8,84u8,89u8,95u8,69u8,88u8,69u8,67u8,85u8,84u8,73u8,79u8, - 78u8,95u8,72u8,65u8,83u8,72u8,54u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,32u8,99u8,97u8,110u8,110u8, - 111u8,116u8,32u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,32u8,97u8,110u8,32u8,101u8,109u8,112u8,116u8,121u8,32u8, - 101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,32u8,115u8,99u8,114u8,105u8,112u8,116u8,32u8,104u8,97u8,115u8, - 104u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8, - 86u8,79u8,84u8,73u8,78u8,71u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,78u8,68u8,69u8,68u8, - 43u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,39u8,115u8,32u8,118u8,111u8,116u8,105u8,110u8,103u8,32u8,112u8, - 101u8,114u8,105u8,111u8,100u8,32u8,104u8,97u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,110u8, - 100u8,101u8,100u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,32u8,69u8,86u8,79u8,84u8,73u8,78u8,71u8, - 95u8,70u8,79u8,82u8,85u8,77u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,82u8,69u8,71u8,73u8,83u8, - 84u8,69u8,82u8,69u8,68u8,41u8,86u8,111u8,116u8,105u8,110u8,103u8,32u8,102u8,111u8,114u8,117u8,109u8,32u8,104u8, - 97u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,98u8,101u8,101u8,110u8,32u8,114u8,101u8,103u8,105u8, - 115u8,116u8,101u8,114u8,101u8,100u8,46u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,73u8,78u8,86u8, - 65u8,76u8,73u8,68u8,95u8,77u8,73u8,78u8,95u8,86u8,79u8,84u8,69u8,95u8,84u8,72u8,82u8,69u8,83u8,72u8, - 79u8,76u8,68u8,72u8,77u8,105u8,110u8,105u8,109u8,117u8,109u8,32u8,118u8,111u8,116u8,101u8,32u8,116u8,104u8,114u8, - 101u8,115u8,104u8,111u8,108u8,100u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,104u8,105u8,103u8, - 104u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8,32u8,101u8,97u8,114u8,108u8,121u8,32u8,114u8,101u8,115u8,111u8,108u8, - 117u8,116u8,105u8,111u8,110u8,32u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,46u8,8u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,28u8,69u8,82u8,69u8,83u8,79u8,76u8,85u8,84u8,73u8,79u8,78u8,95u8,67u8,65u8,78u8, - 78u8,79u8,84u8,95u8,66u8,69u8,95u8,65u8,84u8,79u8,77u8,73u8,67u8,91u8,82u8,101u8,115u8,111u8,108u8,117u8, - 116u8,105u8,111u8,110u8,32u8,111u8,102u8,32u8,97u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,32u8,99u8, - 97u8,110u8,110u8,111u8,116u8,32u8,104u8,97u8,112u8,112u8,101u8,110u8,32u8,97u8,116u8,111u8,109u8,105u8,99u8,97u8, - 108u8,108u8,121u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8,109u8,101u8,32u8,116u8,114u8,97u8,110u8, - 115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,97u8,115u8,32u8,116u8,104u8,101u8,32u8,108u8,97u8,115u8,116u8,32u8, - 118u8,111u8,116u8,101u8,46u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,69u8,77u8,85u8,76u8,84u8,73u8, - 95u8,83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,73u8,78u8,95u8,69u8,88u8, - 69u8,67u8,85u8,84u8,73u8,79u8,78u8,65u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,118u8,111u8,116u8,101u8,32u8, - 105u8,102u8,32u8,116u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,109u8,117u8,108u8, - 116u8,105u8,45u8,115u8,116u8,101u8,112u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,32u8,105u8,115u8,32u8, - 105u8,110u8,32u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,46u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,60u8,69u8,77u8,85u8,76u8,84u8,73u8,95u8,83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8, - 65u8,76u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,85u8,83u8,69u8,95u8,83u8,73u8,78u8,71u8,76u8,69u8, - 95u8,83u8,84u8,69u8,80u8,95u8,82u8,69u8,83u8,79u8,76u8,86u8,69u8,95u8,70u8,85u8,78u8,67u8,84u8,73u8, - 79u8,78u8,219u8,1u8,73u8,102u8,32u8,97u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,32u8,105u8,115u8, - 32u8,109u8,117u8,108u8,116u8,105u8,45u8,115u8,116u8,101u8,112u8,44u8,32u8,119u8,101u8,32u8,110u8,101u8,101u8,100u8, - 32u8,116u8,111u8,32u8,117u8,115u8,101u8,32u8,96u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,95u8,112u8,114u8,111u8, - 112u8,111u8,115u8,97u8,108u8,95u8,118u8,50u8,40u8,41u8,96u8,32u8,116u8,111u8,32u8,114u8,101u8,115u8,111u8,108u8, - 118u8,101u8,32u8,105u8,116u8,46u8,10u8,32u8,73u8,102u8,32u8,119u8,101u8,32u8,117u8,115u8,101u8,32u8,96u8,114u8, - 101u8,115u8,111u8,108u8,118u8,101u8,40u8,41u8,96u8,32u8,116u8,111u8,32u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8, - 32u8,97u8,32u8,109u8,117u8,108u8,116u8,105u8,45u8,115u8,116u8,101u8,112u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,44u8,32u8,105u8,116u8,32u8,119u8,105u8,108u8,108u8,32u8,102u8,97u8,105u8,108u8,32u8,119u8,105u8,116u8, - 104u8,32u8,69u8,77u8,85u8,76u8,84u8,73u8,95u8,83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8, - 65u8,76u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,85u8,83u8,69u8,95u8,83u8,73u8,78u8,71u8,76u8,69u8, - 95u8,83u8,84u8,69u8,80u8,95u8,82u8,69u8,83u8,79u8,76u8,86u8,69u8,95u8,70u8,85u8,78u8,67u8,84u8,73u8, - 79u8,78u8,46u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,53u8,69u8,83u8,73u8,78u8,71u8,76u8,69u8,95u8, - 83u8,84u8,69u8,80u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8, - 95u8,72u8,65u8,86u8,69u8,95u8,78u8,69u8,88u8,84u8,95u8,69u8,88u8,69u8,67u8,85u8,84u8,73u8,79u8,78u8, - 95u8,72u8,65u8,83u8,72u8,132u8,1u8,73u8,102u8,32u8,119u8,101u8,32u8,99u8,97u8,108u8,108u8,32u8,96u8,114u8, - 101u8,115u8,111u8,108u8,118u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,118u8,50u8,40u8,41u8, - 96u8,32u8,116u8,111u8,32u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,32u8,97u8,32u8,115u8,105u8,110u8,103u8,108u8, - 101u8,45u8,115u8,116u8,101u8,112u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,44u8,32u8,116u8,104u8,101u8, - 32u8,96u8,110u8,101u8,120u8,116u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,104u8,97u8,115u8, - 104u8,96u8,32u8,112u8,97u8,114u8,97u8,109u8,101u8,116u8,101u8,114u8,32u8,115u8,104u8,111u8,117u8,108u8,100u8,32u8, - 98u8,101u8,32u8,97u8,110u8,32u8,101u8,109u8,112u8,116u8,121u8,32u8,118u8,101u8,99u8,116u8,111u8,114u8,46u8,12u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,73u8,83u8, - 95u8,83u8,73u8,78u8,71u8,76u8,69u8,95u8,83u8,84u8,69u8,80u8,77u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8, - 99u8,97u8,108u8,108u8,32u8,96u8,105u8,115u8,95u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,116u8,101u8,112u8,95u8, - 112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,105u8,110u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8, - 110u8,40u8,41u8,96u8,32u8,111u8,110u8,32u8,115u8,105u8,110u8,103u8,108u8,101u8,45u8,115u8,116u8,101u8,112u8,32u8, - 112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,115u8,46u8,0u8,6u8,11u8,105u8,115u8,95u8,114u8,101u8,115u8,111u8, - 108u8,118u8,101u8,100u8,1u8,1u8,0u8,16u8,105u8,115u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,99u8,108u8, - 111u8,115u8,101u8,100u8,1u8,1u8,0u8,18u8,103u8,101u8,116u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8, - 110u8,95u8,104u8,97u8,115u8,104u8,1u8,1u8,0u8,18u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,95u8,115u8,116u8,97u8,116u8,101u8,1u8,1u8,0u8,28u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8, - 111u8,115u8,97u8,108u8,95u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8,115u8, - 1u8,1u8,0u8,35u8,105u8,115u8,95u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,116u8,101u8,112u8,95u8,112u8,114u8, - 111u8,112u8,111u8,115u8,97u8,108u8,95u8,105u8,110u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,1u8, - 1u8,0u8,0u8,2u8,6u8,39u8,3u8,40u8,11u8,7u8,1u8,4u8,41u8,10u8,2u8,42u8,3u8,43u8,11u8,8u8, - 2u8,8u8,9u8,10u8,2u8,44u8,4u8,1u8,2u8,12u8,45u8,5u8,46u8,11u8,7u8,1u8,9u8,0u8,43u8,11u8, - 8u8,2u8,8u8,9u8,10u8,2u8,47u8,3u8,41u8,10u8,2u8,44u8,4u8,42u8,3u8,40u8,11u8,7u8,1u8,4u8, - 48u8,4u8,49u8,4u8,32u8,1u8,50u8,3u8,2u8,2u8,2u8,51u8,5u8,52u8,8u8,10u8,3u8,2u8,4u8,39u8, - 3u8,48u8,4u8,49u8,4u8,54u8,1u8,4u8,2u8,2u8,39u8,3u8,55u8,3u8,5u8,2u8,4u8,56u8,11u8,11u8, - 1u8,8u8,0u8,58u8,11u8,11u8,1u8,8u8,2u8,59u8,11u8,11u8,1u8,8u8,3u8,60u8,11u8,11u8,1u8,8u8, - 4u8,6u8,2u8,3u8,61u8,11u8,12u8,2u8,3u8,11u8,1u8,1u8,9u8,0u8,63u8,8u8,5u8,64u8,3u8,6u8, - 9u8,1u8,9u8,0u8,1u8,0u8,0u8,12u8,35u8,10u8,0u8,55u8,0u8,56u8,0u8,4u8,31u8,10u8,0u8,55u8, - 0u8,56u8,1u8,20u8,12u8,2u8,10u8,0u8,55u8,1u8,20u8,10u8,2u8,38u8,4u8,20u8,11u8,0u8,1u8,8u8, - 12u8,1u8,5u8,26u8,11u8,0u8,55u8,2u8,20u8,11u8,2u8,38u8,12u8,1u8,11u8,1u8,4u8,30u8,8u8,2u8, - 5u8,33u8,11u8,0u8,1u8,9u8,2u8,1u8,1u8,0u8,1u8,6u8,7u8,11u8,11u8,0u8,11u8,1u8,11u8,2u8, - 11u8,3u8,11u8,4u8,11u8,5u8,11u8,6u8,11u8,7u8,9u8,56u8,2u8,2u8,2u8,1u8,0u8,1u8,6u8,16u8, - 123u8,14u8,6u8,56u8,0u8,4u8,13u8,10u8,4u8,14u8,6u8,56u8,1u8,20u8,37u8,4u8,10u8,5u8,13u8,7u8, - 0u8,17u8,17u8,39u8,14u8,3u8,65u8,17u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,19u8, - 5u8,22u8,7u8,5u8,17u8,17u8,39u8,11u8,1u8,60u8,0u8,12u8,25u8,10u8,25u8,55u8,3u8,20u8,12u8,24u8, - 10u8,25u8,55u8,3u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,25u8,54u8,3u8,21u8, - 13u8,7u8,7u8,13u8,17u8,18u8,14u8,8u8,56u8,3u8,56u8,4u8,7u8,12u8,17u8,18u8,12u8,23u8,11u8,8u8, - 4u8,60u8,13u8,7u8,12u8,17u8,11u8,23u8,12u8,16u8,9u8,12u8,9u8,11u8,17u8,11u8,16u8,14u8,9u8,56u8, - 3u8,56u8,4u8,5u8,72u8,13u8,7u8,14u8,23u8,12u8,18u8,46u8,11u8,18u8,56u8,5u8,4u8,72u8,13u8,7u8, - 14u8,23u8,56u8,6u8,1u8,1u8,10u8,25u8,54u8,4u8,12u8,15u8,10u8,24u8,12u8,14u8,11u8,0u8,12u8,19u8, - 17u8,23u8,12u8,20u8,11u8,2u8,56u8,7u8,12u8,21u8,10u8,3u8,12u8,22u8,10u8,7u8,12u8,10u8,10u8,4u8, - 12u8,11u8,10u8,5u8,12u8,12u8,10u8,6u8,12u8,13u8,11u8,15u8,11u8,14u8,11u8,19u8,11u8,21u8,11u8,10u8, - 11u8,20u8,11u8,22u8,11u8,11u8,11u8,12u8,11u8,13u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,1u8,56u8,8u8,11u8,25u8, - 54u8,5u8,15u8,6u8,10u8,24u8,11u8,6u8,11u8,3u8,11u8,5u8,11u8,7u8,11u8,4u8,18u8,0u8,56u8,9u8, - 11u8,24u8,2u8,3u8,1u8,0u8,1u8,6u8,7u8,8u8,11u8,0u8,60u8,0u8,54u8,4u8,11u8,1u8,56u8,10u8, - 55u8,6u8,20u8,2u8,4u8,1u8,0u8,1u8,6u8,7u8,8u8,11u8,0u8,60u8,0u8,54u8,4u8,11u8,1u8,56u8, - 10u8,55u8,7u8,20u8,2u8,5u8,1u8,0u8,1u8,6u8,31u8,49u8,10u8,0u8,10u8,1u8,56u8,11u8,4u8,45u8, - 11u8,0u8,61u8,0u8,55u8,4u8,11u8,1u8,56u8,12u8,12u8,6u8,10u8,6u8,55u8,1u8,20u8,12u8,7u8,10u8, - 6u8,55u8,2u8,20u8,12u8,5u8,10u8,7u8,10u8,5u8,36u8,4u8,31u8,11u8,7u8,11u8,5u8,22u8,11u8,6u8, - 55u8,8u8,20u8,38u8,12u8,2u8,5u8,35u8,11u8,6u8,1u8,9u8,12u8,2u8,11u8,2u8,4u8,40u8,7u8,6u8, - 12u8,3u8,5u8,42u8,7u8,3u8,12u8,3u8,11u8,3u8,12u8,4u8,5u8,47u8,7u8,14u8,12u8,4u8,11u8,4u8, - 2u8,6u8,1u8,0u8,1u8,6u8,34u8,27u8,11u8,0u8,60u8,0u8,54u8,4u8,11u8,1u8,56u8,10u8,12u8,3u8, - 7u8,12u8,17u8,18u8,12u8,2u8,10u8,3u8,55u8,9u8,14u8,2u8,56u8,5u8,4u8,15u8,5u8,20u8,11u8,3u8, - 1u8,7u8,7u8,17u8,17u8,39u8,11u8,3u8,55u8,9u8,14u8,2u8,56u8,13u8,20u8,17u8,30u8,2u8,7u8,0u8, - 0u8,1u8,6u8,35u8,60u8,10u8,0u8,10u8,1u8,56u8,14u8,7u8,6u8,33u8,4u8,7u8,5u8,10u8,7u8,4u8, - 17u8,31u8,39u8,11u8,0u8,60u8,0u8,54u8,4u8,11u8,1u8,56u8,10u8,12u8,4u8,10u8,4u8,55u8,10u8,20u8, - 32u8,4u8,22u8,5u8,27u8,11u8,4u8,1u8,7u8,3u8,17u8,31u8,39u8,10u8,4u8,55u8,9u8,12u8,3u8,7u8, - 15u8,17u8,18u8,12u8,2u8,11u8,3u8,14u8,2u8,56u8,13u8,20u8,17u8,32u8,12u8,5u8,17u8,23u8,11u8,5u8, - 36u8,4u8,44u8,5u8,49u8,11u8,4u8,1u8,7u8,9u8,17u8,31u8,39u8,17u8,33u8,11u8,4u8,55u8,6u8,20u8, - 33u8,4u8,56u8,5u8,59u8,7u8,6u8,17u8,17u8,39u8,2u8,8u8,1u8,0u8,1u8,6u8,7u8,8u8,11u8,0u8, - 60u8,0u8,54u8,4u8,11u8,1u8,56u8,10u8,55u8,10u8,20u8,2u8,9u8,1u8,0u8,1u8,6u8,36u8,19u8,11u8, - 0u8,61u8,0u8,55u8,4u8,11u8,1u8,56u8,12u8,12u8,3u8,10u8,3u8,56u8,15u8,4u8,14u8,11u8,3u8,1u8, - 8u8,12u8,2u8,5u8,17u8,11u8,3u8,56u8,16u8,12u8,2u8,11u8,2u8,2u8,10u8,0u8,0u8,0u8,7u8,6u8, - 17u8,23u8,11u8,0u8,55u8,7u8,20u8,36u8,2u8,11u8,1u8,0u8,0u8,37u8,41u8,10u8,0u8,17u8,34u8,12u8, - 3u8,10u8,3u8,59u8,0u8,32u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,11u8,17u8,35u8,39u8,56u8,17u8, - 12u8,1u8,10u8,0u8,56u8,18u8,10u8,0u8,56u8,19u8,10u8,0u8,56u8,20u8,10u8,0u8,56u8,21u8,18u8,5u8, - 12u8,2u8,11u8,1u8,11u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,57u8,0u8,12u8,4u8,13u8, - 4u8,54u8,5u8,15u8,12u8,11u8,3u8,56u8,22u8,18u8,2u8,56u8,23u8,11u8,0u8,11u8,4u8,63u8,0u8,2u8, - 12u8,1u8,0u8,1u8,6u8,45u8,64u8,10u8,0u8,10u8,1u8,56u8,24u8,11u8,0u8,60u8,0u8,12u8,5u8,10u8, - 5u8,54u8,4u8,10u8,1u8,56u8,10u8,12u8,3u8,7u8,13u8,17u8,18u8,12u8,2u8,10u8,3u8,55u8,9u8,14u8, - 2u8,56u8,5u8,4u8,35u8,10u8,3u8,55u8,9u8,14u8,2u8,56u8,13u8,20u8,17u8,30u8,32u8,4u8,28u8,5u8, - 35u8,11u8,5u8,1u8,11u8,3u8,1u8,7u8,1u8,17u8,39u8,39u8,10u8,3u8,46u8,56u8,15u8,12u8,4u8,8u8, - 10u8,3u8,54u8,10u8,21u8,17u8,23u8,10u8,3u8,54u8,11u8,21u8,11u8,5u8,54u8,5u8,15u8,14u8,11u8,1u8, - 10u8,3u8,55u8,1u8,20u8,10u8,3u8,55u8,2u8,20u8,11u8,4u8,18u8,3u8,56u8,25u8,11u8,3u8,54u8,12u8, - 56u8,26u8,2u8,13u8,1u8,0u8,1u8,6u8,47u8,113u8,10u8,0u8,10u8,1u8,56u8,24u8,11u8,0u8,60u8,0u8, - 12u8,14u8,10u8,14u8,54u8,4u8,10u8,1u8,56u8,10u8,12u8,12u8,7u8,12u8,17u8,18u8,12u8,9u8,10u8,12u8, - 55u8,9u8,14u8,9u8,56u8,5u8,4u8,30u8,10u8,12u8,54u8,9u8,14u8,9u8,56u8,27u8,12u8,7u8,8u8,12u8, - 3u8,14u8,3u8,56u8,3u8,11u8,7u8,21u8,7u8,13u8,17u8,18u8,12u8,10u8,10u8,12u8,55u8,9u8,14u8,10u8, - 56u8,5u8,4u8,46u8,10u8,12u8,55u8,9u8,14u8,10u8,56u8,13u8,20u8,17u8,30u8,12u8,4u8,5u8,48u8,9u8, - 12u8,4u8,11u8,4u8,12u8,6u8,14u8,2u8,65u8,17u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8, - 12u8,11u8,10u8,6u8,10u8,11u8,30u8,4u8,60u8,5u8,67u8,11u8,14u8,1u8,11u8,12u8,1u8,7u8,10u8,17u8, - 17u8,39u8,11u8,11u8,4u8,91u8,8u8,10u8,12u8,54u8,10u8,21u8,17u8,23u8,10u8,12u8,54u8,11u8,21u8,11u8, - 6u8,4u8,90u8,10u8,12u8,54u8,9u8,14u8,9u8,56u8,27u8,12u8,8u8,9u8,12u8,5u8,14u8,5u8,56u8,3u8, - 11u8,8u8,21u8,5u8,95u8,11u8,2u8,10u8,12u8,54u8,6u8,21u8,10u8,12u8,46u8,56u8,15u8,12u8,13u8,11u8, - 14u8,54u8,5u8,15u8,14u8,11u8,1u8,10u8,12u8,55u8,1u8,20u8,11u8,12u8,55u8,2u8,20u8,11u8,13u8,18u8, - 3u8,56u8,25u8,2u8,14u8,1u8,0u8,1u8,6u8,48u8,128u8,1u8,11u8,1u8,60u8,0u8,12u8,15u8,10u8,15u8, - 54u8,4u8,10u8,2u8,56u8,10u8,12u8,13u8,10u8,13u8,46u8,56u8,16u8,32u8,4u8,14u8,5u8,21u8,11u8,15u8, - 1u8,11u8,13u8,1u8,7u8,8u8,17u8,31u8,39u8,10u8,13u8,55u8,10u8,20u8,32u8,4u8,27u8,5u8,34u8,11u8, - 15u8,1u8,11u8,13u8,1u8,7u8,3u8,17u8,31u8,39u8,10u8,13u8,55u8,9u8,12u8,6u8,7u8,12u8,17u8,18u8, - 12u8,5u8,11u8,6u8,14u8,5u8,56u8,5u8,32u8,4u8,48u8,8u8,12u8,10u8,5u8,64u8,10u8,13u8,55u8,9u8, - 12u8,8u8,7u8,12u8,17u8,18u8,12u8,7u8,9u8,12u8,9u8,11u8,8u8,14u8,7u8,56u8,13u8,20u8,14u8,9u8, - 56u8,3u8,33u8,12u8,10u8,11u8,10u8,4u8,67u8,5u8,74u8,11u8,15u8,1u8,11u8,13u8,1u8,7u8,2u8,17u8, - 31u8,39u8,11u8,4u8,4u8,86u8,10u8,13u8,55u8,1u8,20u8,10u8,3u8,53u8,22u8,10u8,13u8,54u8,1u8,21u8, - 5u8,95u8,10u8,13u8,55u8,2u8,20u8,10u8,3u8,53u8,22u8,10u8,13u8,54u8,2u8,21u8,17u8,23u8,12u8,11u8, - 14u8,11u8,56u8,28u8,12u8,14u8,7u8,15u8,17u8,18u8,12u8,12u8,10u8,13u8,55u8,9u8,14u8,12u8,56u8,5u8, - 4u8,115u8,11u8,14u8,11u8,13u8,54u8,9u8,14u8,12u8,56u8,27u8,21u8,5u8,120u8,11u8,13u8,54u8,9u8,11u8, - 12u8,11u8,14u8,56u8,4u8,11u8,15u8,54u8,5u8,15u8,16u8,11u8,2u8,11u8,3u8,18u8,4u8,56u8,29u8,2u8, - 1u8,7u8,1u8,8u8,1u8,9u8,6u8,2u8,6u8,0u8,6u8,1u8,5u8,0u8,1u8,4u8,1u8,6u8,1u8,5u8, - 1u8,2u8,1u8,10u8,5u8,1u8,1u8,11u8,5u8,2u8,1u8,1u8,5u8,3u8,0u8,9u8,1u8,9u8,2u8,9u8, - 3u8,9u8,4u8,9u8,5u8,9u8,7u8,9u8,8u8,9u8,9u8,9u8,10u8,9u8,11u8,9u8,13u8,9u8,15u8,9u8, - 0u8, - ]; - vector::push_back(&mut code, chunk15); - let chunk16 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,4u8,3u8,10u8,70u8,5u8, - 80u8,46u8,7u8,126u8,150u8,4u8,8u8,148u8,5u8,32u8,6u8,180u8,5u8,94u8,16u8,146u8,6u8,229u8,3u8,10u8, - 247u8,9u8,17u8,12u8,136u8,10u8,146u8,4u8,13u8,154u8,14u8,14u8,15u8,168u8,14u8,2u8,0u8,1u8,0u8,2u8, - 0u8,3u8,0u8,4u8,11u8,0u8,0u8,5u8,0u8,1u8,0u8,0u8,6u8,2u8,3u8,0u8,0u8,7u8,2u8,4u8, - 0u8,0u8,8u8,2u8,5u8,0u8,0u8,9u8,2u8,5u8,0u8,0u8,10u8,2u8,4u8,0u8,0u8,11u8,6u8,0u8, - 0u8,0u8,12u8,7u8,0u8,0u8,0u8,13u8,8u8,0u8,0u8,0u8,14u8,8u8,0u8,0u8,0u8,15u8,7u8,0u8, - 0u8,0u8,16u8,5u8,0u8,0u8,2u8,24u8,9u8,0u8,0u8,1u8,25u8,4u8,4u8,0u8,0u8,1u8,8u8,0u8, - 1u8,6u8,8u8,0u8,1u8,1u8,1u8,3u8,2u8,3u8,3u8,8u8,6u8,12u8,3u8,3u8,3u8,1u8,3u8,3u8, - 3u8,2u8,6u8,12u8,3u8,3u8,6u8,12u8,3u8,3u8,1u8,6u8,12u8,1u8,7u8,8u8,0u8,2u8,1u8,7u8, - 8u8,0u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,14u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8, - 111u8,110u8,102u8,105u8,103u8,5u8,101u8,114u8,114u8,111u8,114u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,13u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8,67u8,111u8,110u8,102u8, - 105u8,103u8,3u8,103u8,101u8,116u8,30u8,103u8,101u8,116u8,95u8,97u8,108u8,108u8,111u8,119u8,95u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8,95u8,99u8,104u8,97u8,110u8,103u8,101u8,29u8,103u8,101u8, - 116u8,95u8,114u8,101u8,99u8,117u8,114u8,114u8,105u8,110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,100u8, - 117u8,114u8,97u8,116u8,105u8,111u8,110u8,18u8,103u8,101u8,116u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8, - 95u8,115u8,116u8,97u8,107u8,101u8,15u8,103u8,101u8,116u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,95u8,114u8,97u8, - 116u8,101u8,31u8,103u8,101u8,116u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,95u8, - 105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,105u8,109u8,105u8,116u8,10u8,105u8,110u8,105u8,116u8,105u8, - 97u8,108u8,105u8,122u8,101u8,37u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,114u8,101u8,99u8,117u8,114u8,114u8,105u8, - 110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8, - 101u8,99u8,115u8,21u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8, - 115u8,116u8,97u8,107u8,101u8,19u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8, - 95u8,114u8,97u8,116u8,101u8,34u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8, - 112u8,111u8,119u8,101u8,114u8,95u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,105u8,109u8,105u8,116u8, - 23u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,115u8, - 116u8,97u8,107u8,101u8,13u8,109u8,105u8,110u8,105u8,109u8,117u8,109u8,95u8,115u8,116u8,97u8,107u8,101u8,13u8,109u8, - 97u8,120u8,105u8,109u8,117u8,109u8,95u8,115u8,116u8,97u8,107u8,101u8,30u8,114u8,101u8,99u8,117u8,114u8,114u8,105u8, - 110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8, - 101u8,99u8,115u8,26u8,97u8,108u8,108u8,111u8,119u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8, - 115u8,101u8,116u8,95u8,99u8,104u8,97u8,110u8,103u8,101u8,12u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,95u8,114u8, - 97u8,116u8,101u8,24u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,95u8,114u8,97u8,116u8,101u8,95u8,100u8,101u8,110u8, - 111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,27u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8, - 114u8,95u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,105u8,109u8,105u8,116u8,22u8,97u8,115u8,115u8, - 101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,16u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 64u8,66u8,15u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8, - 49u8,208u8,3u8,5u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,90u8,69u8,82u8,79u8,95u8,76u8, - 79u8,67u8,75u8,85u8,80u8,95u8,68u8,85u8,82u8,65u8,84u8,73u8,79u8,78u8,37u8,83u8,116u8,97u8,107u8,101u8, - 32u8,108u8,111u8,99u8,107u8,117u8,112u8,32u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,99u8,97u8,110u8, - 110u8,111u8,116u8,32u8,98u8,101u8,32u8,122u8,101u8,114u8,111u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 30u8,69u8,90u8,69u8,82u8,79u8,95u8,82u8,69u8,87u8,65u8,82u8,68u8,83u8,95u8,82u8,65u8,84u8,69u8,95u8, - 68u8,69u8,78u8,79u8,77u8,73u8,78u8,65u8,84u8,79u8,82u8,39u8,82u8,101u8,119u8,97u8,114u8,100u8,32u8,114u8, - 97u8,116u8,101u8,32u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,32u8,99u8,97u8,110u8,110u8, - 111u8,116u8,32u8,98u8,101u8,32u8,122u8,101u8,114u8,111u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8, - 69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,82u8,65u8,78u8,71u8,69u8, - 63u8,83u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,114u8,97u8,110u8, - 103u8,101u8,32u8,105u8,115u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,46u8,32u8,77u8,97u8,120u8,32u8,109u8, - 117u8,115u8,116u8,32u8,98u8,101u8,32u8,103u8,114u8,101u8,97u8,116u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8,32u8, - 109u8,105u8,110u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8, - 68u8,95u8,86u8,79u8,84u8,73u8,78u8,71u8,95u8,80u8,79u8,87u8,69u8,82u8,95u8,73u8,78u8,67u8,82u8,69u8, - 65u8,83u8,69u8,95u8,76u8,73u8,77u8,73u8,84u8,66u8,84u8,104u8,101u8,32u8,118u8,111u8,116u8,105u8,110u8,103u8, - 32u8,112u8,111u8,119u8,101u8,114u8,32u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,32u8,108u8,105u8,109u8,105u8, - 116u8,32u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8, - 32u8,119u8,105u8,116u8,104u8,105u8,110u8,32u8,40u8,48u8,44u8,32u8,53u8,48u8,93u8,46u8,5u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,21u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,82u8,69u8,87u8,65u8,82u8,68u8, - 83u8,95u8,82u8,65u8,84u8,69u8,78u8,83u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,114u8,101u8,119u8, - 97u8,114u8,100u8,115u8,32u8,114u8,97u8,116u8,101u8,32u8,105u8,115u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8, - 44u8,32u8,119u8,104u8,105u8,99u8,104u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,119u8,105u8,116u8,104u8, - 105u8,110u8,32u8,91u8,48u8,44u8,32u8,77u8,65u8,88u8,95u8,82u8,69u8,87u8,65u8,82u8,68u8,83u8,95u8,82u8, - 65u8,84u8,69u8,93u8,46u8,0u8,0u8,0u8,2u8,7u8,17u8,3u8,18u8,3u8,19u8,3u8,20u8,1u8,21u8,3u8, - 22u8,3u8,23u8,3u8,0u8,1u8,0u8,1u8,0u8,0u8,4u8,7u8,6u8,43u8,0u8,20u8,2u8,1u8,1u8,0u8, - 0u8,0u8,4u8,11u8,0u8,16u8,0u8,20u8,2u8,2u8,1u8,0u8,0u8,0u8,4u8,11u8,0u8,16u8,1u8,20u8, - 2u8,3u8,1u8,0u8,0u8,0u8,7u8,10u8,0u8,16u8,2u8,20u8,11u8,0u8,16u8,3u8,20u8,2u8,4u8,1u8, - 0u8,0u8,0u8,7u8,10u8,0u8,16u8,4u8,20u8,11u8,0u8,16u8,5u8,20u8,2u8,5u8,1u8,0u8,0u8,0u8, - 4u8,11u8,0u8,16u8,6u8,20u8,2u8,6u8,3u8,0u8,0u8,3u8,75u8,10u8,0u8,17u8,12u8,10u8,1u8,10u8, - 2u8,17u8,11u8,10u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,10u8,5u8,15u8,11u8, - 0u8,1u8,7u8,3u8,17u8,13u8,39u8,10u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8, - 20u8,5u8,25u8,11u8,0u8,1u8,7u8,4u8,17u8,13u8,39u8,10u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,34u8,10u8,7u8,6u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,12u8,8u8,5u8, - 36u8,9u8,12u8,8u8,11u8,8u8,4u8,39u8,5u8,44u8,11u8,0u8,1u8,7u8,2u8,17u8,13u8,39u8,10u8,5u8, - 7u8,5u8,37u8,4u8,49u8,5u8,54u8,11u8,0u8,1u8,7u8,0u8,17u8,13u8,39u8,10u8,5u8,10u8,6u8,37u8, - 4u8,59u8,5u8,64u8,11u8,0u8,1u8,7u8,0u8,17u8,13u8,39u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8, - 11u8,4u8,11u8,5u8,11u8,6u8,11u8,7u8,18u8,0u8,45u8,0u8,2u8,7u8,1u8,0u8,1u8,0u8,10u8,20u8, - 10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8, - 3u8,17u8,13u8,39u8,11u8,0u8,17u8,12u8,7u8,6u8,42u8,0u8,12u8,2u8,11u8,1u8,11u8,2u8,15u8,1u8, - 21u8,2u8,8u8,1u8,0u8,1u8,0u8,10u8,17u8,11u8,0u8,17u8,12u8,10u8,1u8,10u8,2u8,17u8,11u8,7u8, - 6u8,42u8,0u8,12u8,3u8,11u8,1u8,10u8,3u8,15u8,2u8,21u8,11u8,2u8,11u8,3u8,15u8,3u8,21u8,2u8, - 9u8,1u8,0u8,1u8,0u8,10u8,38u8,11u8,0u8,17u8,12u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,7u8,5u8,10u8,7u8,4u8,17u8,13u8,39u8,10u8,1u8,7u8,5u8,37u8,4u8,15u8,5u8, - 18u8,7u8,0u8,17u8,13u8,39u8,10u8,1u8,10u8,2u8,37u8,4u8,23u8,5u8,26u8,7u8,0u8,17u8,13u8,39u8, - 7u8,6u8,42u8,0u8,12u8,3u8,11u8,1u8,10u8,3u8,15u8,4u8,21u8,11u8,2u8,11u8,3u8,15u8,5u8,21u8, - 2u8,10u8,1u8,0u8,1u8,0u8,11u8,27u8,11u8,0u8,17u8,12u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,36u8,4u8,11u8,10u8,1u8,6u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,12u8,2u8, - 5u8,13u8,9u8,12u8,2u8,11u8,2u8,4u8,16u8,5u8,19u8,7u8,2u8,17u8,13u8,39u8,7u8,6u8,42u8,0u8, - 12u8,3u8,11u8,1u8,11u8,3u8,15u8,6u8,21u8,2u8,11u8,0u8,0u8,0u8,3u8,18u8,11u8,0u8,10u8,1u8, - 37u8,4u8,9u8,11u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,12u8,2u8,5u8,11u8,9u8, - 12u8,2u8,11u8,2u8,4u8,14u8,5u8,17u8,7u8,1u8,17u8,13u8,39u8,2u8,0u8,3u8,0u8,2u8,0u8,0u8, - 0u8,1u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk16); - let chunk17 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,32u8,2u8,32u8,139u8,1u8,3u8,171u8,1u8, - 182u8,4u8,4u8,225u8,5u8,96u8,5u8,193u8,6u8,141u8,7u8,7u8,206u8,13u8,152u8,26u8,8u8,230u8,39u8,32u8, - 6u8,134u8,40u8,244u8,1u8,16u8,250u8,41u8,146u8,14u8,10u8,140u8,56u8,194u8,2u8,12u8,206u8,58u8,132u8,34u8, - 13u8,210u8,92u8,74u8,15u8,156u8,93u8,8u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8, - 0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8,17u8,0u8,18u8,0u8,19u8, - 0u8,20u8,6u8,0u8,0u8,21u8,8u8,0u8,0u8,22u8,8u8,0u8,0u8,23u8,6u8,0u8,0u8,24u8,6u8,0u8, - 0u8,25u8,6u8,0u8,0u8,26u8,6u8,0u8,0u8,27u8,6u8,0u8,0u8,28u8,12u8,0u8,0u8,29u8,6u8,0u8, - 0u8,30u8,6u8,0u8,0u8,31u8,6u8,0u8,0u8,32u8,6u8,0u8,0u8,33u8,8u8,0u8,0u8,34u8,6u8,0u8, - 0u8,35u8,6u8,0u8,0u8,36u8,15u8,0u8,0u8,37u8,8u8,0u8,0u8,38u8,7u8,0u8,0u8,39u8,8u8,0u8, - 0u8,40u8,8u8,0u8,0u8,41u8,6u8,0u8,4u8,43u8,4u8,1u8,0u8,1u8,2u8,44u8,8u8,0u8,9u8,56u8, - 7u8,1u8,0u8,0u8,4u8,93u8,5u8,1u8,0u8,1u8,11u8,99u8,11u8,0u8,6u8,126u8,4u8,1u8,6u8,1u8, - 13u8,148u8,1u8,4u8,2u8,3u8,1u8,0u8,1u8,3u8,181u8,1u8,7u8,0u8,3u8,182u8,1u8,7u8,0u8,0u8, - 42u8,0u8,1u8,0u8,0u8,45u8,2u8,1u8,0u8,0u8,46u8,3u8,1u8,0u8,0u8,47u8,4u8,1u8,1u8,0u8, - 0u8,48u8,5u8,1u8,0u8,0u8,49u8,5u8,1u8,0u8,0u8,50u8,6u8,7u8,0u8,0u8,51u8,8u8,1u8,0u8, - 0u8,52u8,9u8,1u8,0u8,0u8,53u8,10u8,1u8,0u8,0u8,54u8,11u8,7u8,0u8,0u8,55u8,12u8,10u8,0u8, - 0u8,57u8,13u8,14u8,0u8,0u8,58u8,15u8,16u8,0u8,0u8,59u8,7u8,17u8,0u8,0u8,60u8,5u8,7u8,0u8, - 0u8,61u8,5u8,5u8,0u8,0u8,62u8,5u8,7u8,0u8,0u8,63u8,18u8,7u8,0u8,0u8,64u8,5u8,5u8,0u8, - 0u8,65u8,19u8,5u8,0u8,0u8,66u8,5u8,7u8,0u8,0u8,67u8,5u8,20u8,0u8,0u8,68u8,5u8,21u8,0u8, - 0u8,69u8,5u8,7u8,0u8,0u8,70u8,5u8,7u8,0u8,0u8,71u8,12u8,1u8,0u8,0u8,72u8,19u8,1u8,0u8, - 0u8,73u8,12u8,1u8,0u8,0u8,74u8,12u8,1u8,0u8,0u8,75u8,22u8,1u8,0u8,0u8,76u8,23u8,1u8,0u8, - 0u8,77u8,12u8,1u8,0u8,0u8,78u8,5u8,24u8,0u8,0u8,79u8,5u8,24u8,0u8,0u8,80u8,25u8,1u8,0u8, - 0u8,81u8,25u8,1u8,0u8,0u8,82u8,25u8,1u8,0u8,0u8,83u8,1u8,1u8,0u8,0u8,84u8,0u8,1u8,0u8, - 0u8,85u8,26u8,1u8,0u8,0u8,86u8,27u8,1u8,0u8,0u8,87u8,28u8,1u8,0u8,0u8,88u8,25u8,1u8,0u8, - 0u8,89u8,29u8,1u8,0u8,0u8,90u8,25u8,1u8,0u8,0u8,91u8,29u8,1u8,0u8,0u8,92u8,5u8,24u8,0u8, - 0u8,94u8,30u8,1u8,0u8,0u8,95u8,0u8,1u8,0u8,0u8,96u8,31u8,1u8,0u8,0u8,97u8,28u8,1u8,0u8, - 0u8,98u8,32u8,1u8,0u8,0u8,100u8,33u8,1u8,0u8,0u8,101u8,7u8,1u8,0u8,0u8,102u8,0u8,1u8,0u8, - 0u8,103u8,26u8,34u8,0u8,10u8,158u8,1u8,12u8,5u8,0u8,4u8,102u8,0u8,36u8,1u8,0u8,4u8,159u8,1u8, - 38u8,7u8,1u8,0u8,4u8,160u8,1u8,36u8,1u8,1u8,0u8,9u8,161u8,1u8,39u8,24u8,1u8,0u8,4u8,162u8, - 1u8,40u8,1u8,1u8,0u8,11u8,163u8,1u8,1u8,41u8,0u8,11u8,164u8,1u8,42u8,17u8,0u8,5u8,165u8,1u8, - 7u8,7u8,0u8,6u8,166u8,1u8,44u8,1u8,1u8,6u8,13u8,167u8,1u8,46u8,24u8,2u8,3u8,0u8,13u8,168u8, - 1u8,47u8,48u8,2u8,3u8,0u8,13u8,169u8,1u8,49u8,1u8,2u8,3u8,0u8,15u8,170u8,1u8,51u8,24u8,1u8, - 0u8,5u8,171u8,1u8,7u8,7u8,0u8,12u8,172u8,1u8,12u8,1u8,0u8,4u8,173u8,1u8,55u8,36u8,1u8,0u8, - 9u8,174u8,1u8,50u8,56u8,1u8,0u8,9u8,175u8,1u8,1u8,56u8,1u8,0u8,14u8,176u8,1u8,1u8,7u8,0u8, - 11u8,177u8,1u8,42u8,7u8,0u8,5u8,178u8,1u8,7u8,7u8,0u8,4u8,179u8,1u8,1u8,36u8,1u8,0u8,1u8, - 180u8,1u8,12u8,68u8,1u8,6u8,3u8,183u8,1u8,80u8,81u8,0u8,3u8,184u8,1u8,82u8,83u8,0u8,13u8,185u8, - 1u8,1u8,85u8,2u8,3u8,4u8,15u8,167u8,1u8,86u8,24u8,1u8,0u8,11u8,186u8,1u8,42u8,24u8,0u8,5u8, - 187u8,1u8,7u8,7u8,0u8,5u8,188u8,1u8,7u8,7u8,0u8,9u8,189u8,1u8,90u8,50u8,1u8,0u8,15u8,190u8, - 1u8,91u8,50u8,1u8,0u8,8u8,191u8,1u8,17u8,7u8,0u8,4u8,189u8,1u8,94u8,36u8,1u8,0u8,9u8,192u8, - 1u8,39u8,96u8,1u8,0u8,11u8,193u8,1u8,42u8,17u8,0u8,4u8,194u8,1u8,105u8,36u8,1u8,0u8,7u8,195u8, - 1u8,1u8,24u8,0u8,13u8,196u8,1u8,47u8,106u8,2u8,3u8,0u8,11u8,197u8,1u8,42u8,7u8,0u8,4u8,198u8, - 1u8,109u8,1u8,1u8,0u8,58u8,35u8,59u8,35u8,60u8,35u8,61u8,7u8,62u8,35u8,66u8,43u8,67u8,3u8,68u8, - 3u8,69u8,3u8,70u8,50u8,73u8,35u8,74u8,7u8,75u8,7u8,66u8,64u8,79u8,35u8,80u8,67u8,80u8,69u8,80u8, - 43u8,80u8,70u8,80u8,71u8,80u8,72u8,80u8,64u8,80u8,73u8,80u8,74u8,80u8,75u8,80u8,76u8,80u8,77u8,61u8, - 84u8,83u8,3u8,84u8,5u8,70u8,78u8,66u8,73u8,88u8,7u8,89u8,16u8,66u8,77u8,3u8,16u8,91u8,35u8,66u8, - 70u8,92u8,7u8,66u8,71u8,66u8,69u8,66u8,75u8,66u8,72u8,94u8,35u8,96u8,3u8,66u8,74u8,98u8,35u8,66u8, - 76u8,2u8,6u8,12u8,3u8,0u8,2u8,6u8,8u8,8u8,11u8,22u8,1u8,8u8,23u8,2u8,5u8,11u8,22u8,1u8, - 8u8,23u8,2u8,7u8,10u8,9u8,0u8,7u8,10u8,9u8,0u8,1u8,5u8,5u8,3u8,3u8,3u8,3u8,3u8,1u8, - 3u8,2u8,6u8,12u8,10u8,5u8,2u8,6u8,12u8,8u8,8u8,1u8,8u8,8u8,5u8,7u8,11u8,22u8,1u8,8u8, - 23u8,3u8,3u8,3u8,3u8,1u8,6u8,12u8,2u8,6u8,10u8,8u8,18u8,5u8,1u8,11u8,24u8,1u8,3u8,3u8, - 5u8,6u8,8u8,13u8,8u8,16u8,1u8,8u8,18u8,2u8,3u8,3u8,1u8,6u8,8u8,13u8,1u8,6u8,8u8,8u8, - 4u8,3u8,3u8,3u8,3u8,3u8,10u8,2u8,10u8,2u8,10u8,2u8,4u8,6u8,12u8,3u8,5u8,5u8,5u8,6u8, - 12u8,10u8,2u8,10u8,2u8,10u8,2u8,10u8,2u8,1u8,1u8,2u8,6u8,12u8,5u8,2u8,6u8,8u8,8u8,3u8, - 2u8,6u8,12u8,6u8,10u8,5u8,4u8,6u8,12u8,5u8,10u8,2u8,10u8,2u8,2u8,6u8,8u8,8u8,5u8,2u8, - 6u8,12u8,11u8,25u8,1u8,8u8,23u8,2u8,3u8,6u8,8u8,8u8,2u8,11u8,24u8,1u8,3u8,10u8,3u8,3u8, - 6u8,8u8,19u8,5u8,6u8,8u8,26u8,1u8,11u8,22u8,1u8,8u8,23u8,1u8,8u8,23u8,1u8,11u8,22u8,1u8, - 9u8,0u8,9u8,11u8,24u8,1u8,3u8,11u8,24u8,1u8,3u8,1u8,8u8,26u8,3u8,3u8,5u8,7u8,8u8,13u8, - 7u8,8u8,20u8,1u8,6u8,11u8,22u8,1u8,9u8,0u8,1u8,6u8,11u8,24u8,1u8,9u8,0u8,2u8,7u8,11u8, - 22u8,1u8,9u8,0u8,11u8,22u8,1u8,9u8,0u8,1u8,8u8,26u8,1u8,6u8,8u8,26u8,1u8,8u8,0u8,2u8, - 7u8,11u8,27u8,1u8,9u8,0u8,9u8,0u8,2u8,5u8,7u8,11u8,28u8,2u8,5u8,11u8,22u8,1u8,8u8,23u8, - 2u8,6u8,11u8,28u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,2u8,7u8,11u8,28u8,2u8,9u8,0u8,9u8,1u8, - 9u8,0u8,1u8,7u8,9u8,1u8,3u8,7u8,11u8,28u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8, - 9u8,0u8,1u8,6u8,10u8,9u8,0u8,3u8,3u8,4u8,4u8,2u8,7u8,8u8,1u8,5u8,5u8,3u8,6u8,11u8, - 25u8,1u8,8u8,23u8,11u8,22u8,1u8,8u8,23u8,3u8,3u8,2u8,3u8,6u8,11u8,25u8,1u8,9u8,0u8,1u8, - 11u8,24u8,1u8,9u8,0u8,1u8,6u8,8u8,5u8,1u8,8u8,5u8,5u8,1u8,3u8,3u8,3u8,3u8,3u8,3u8, - 3u8,3u8,1u8,6u8,8u8,16u8,7u8,11u8,24u8,1u8,3u8,11u8,24u8,1u8,3u8,11u8,24u8,1u8,3u8,3u8, - 3u8,3u8,6u8,8u8,20u8,5u8,8u8,26u8,3u8,3u8,5u8,7u8,8u8,13u8,1u8,8u8,4u8,4u8,10u8,8u8, - 18u8,10u8,8u8,18u8,10u8,8u8,18u8,6u8,12u8,20u8,11u8,22u8,1u8,8u8,23u8,11u8,27u8,1u8,8u8,0u8, - 11u8,27u8,1u8,8u8,9u8,11u8,27u8,1u8,8u8,11u8,11u8,27u8,1u8,8u8,15u8,11u8,27u8,1u8,8u8,4u8, - 11u8,27u8,1u8,8u8,6u8,11u8,27u8,1u8,8u8,3u8,11u8,27u8,1u8,8u8,14u8,11u8,27u8,1u8,8u8,21u8, - 11u8,27u8,1u8,8u8,7u8,11u8,22u8,1u8,8u8,23u8,6u8,12u8,11u8,22u8,1u8,8u8,23u8,11u8,22u8,1u8, - 8u8,23u8,5u8,5u8,11u8,27u8,1u8,8u8,10u8,11u8,27u8,1u8,8u8,12u8,5u8,1u8,8u8,10u8,1u8,11u8, - 27u8,1u8,9u8,0u8,1u8,8u8,12u8,1u8,8u8,9u8,1u8,8u8,11u8,1u8,8u8,15u8,1u8,8u8,6u8,1u8, - 8u8,3u8,1u8,8u8,14u8,1u8,8u8,21u8,1u8,8u8,7u8,1u8,2u8,3u8,8u8,29u8,10u8,2u8,11u8,24u8, - 1u8,8u8,30u8,1u8,10u8,2u8,1u8,8u8,29u8,2u8,10u8,2u8,6u8,8u8,29u8,1u8,11u8,24u8,1u8,8u8, - 30u8,1u8,8u8,30u8,1u8,11u8,28u8,2u8,9u8,0u8,9u8,1u8,2u8,6u8,10u8,9u8,0u8,6u8,9u8,0u8, - 2u8,1u8,3u8,11u8,5u8,7u8,8u8,13u8,8u8,16u8,7u8,10u8,8u8,18u8,8u8,26u8,3u8,3u8,7u8,8u8, - 13u8,7u8,8u8,16u8,7u8,8u8,20u8,3u8,7u8,8u8,26u8,11u8,24u8,1u8,3u8,11u8,24u8,1u8,3u8,7u8, - 8u8,13u8,8u8,18u8,7u8,8u8,20u8,4u8,1u8,7u8,11u8,24u8,1u8,9u8,0u8,2u8,7u8,10u8,9u8,0u8, - 3u8,29u8,8u8,16u8,5u8,6u8,8u8,26u8,5u8,6u8,8u8,26u8,8u8,26u8,3u8,3u8,3u8,3u8,3u8,3u8, - 8u8,18u8,10u8,8u8,18u8,5u8,3u8,7u8,8u8,13u8,7u8,8u8,13u8,4u8,6u8,8u8,18u8,6u8,8u8,18u8, - 7u8,8u8,16u8,7u8,8u8,16u8,3u8,7u8,8u8,18u8,7u8,8u8,19u8,7u8,8u8,20u8,3u8,3u8,4u8,5u8, - 11u8,22u8,1u8,8u8,23u8,7u8,8u8,13u8,3u8,2u8,7u8,11u8,22u8,1u8,9u8,0u8,3u8,9u8,5u8,7u8, - 10u8,8u8,18u8,3u8,3u8,7u8,10u8,8u8,18u8,5u8,11u8,24u8,1u8,3u8,8u8,18u8,7u8,8u8,20u8,1u8, - 6u8,9u8,0u8,6u8,8u8,29u8,10u8,2u8,11u8,24u8,1u8,8u8,30u8,10u8,2u8,7u8,8u8,13u8,7u8,8u8, - 16u8,2u8,5u8,7u8,8u8,13u8,3u8,5u8,5u8,7u8,8u8,13u8,2u8,5u8,6u8,8u8,8u8,4u8,3u8,5u8, - 7u8,8u8,13u8,11u8,22u8,1u8,8u8,23u8,4u8,10u8,2u8,10u8,2u8,7u8,8u8,13u8,7u8,8u8,16u8,8u8, - 3u8,3u8,3u8,7u8,8u8,5u8,7u8,8u8,5u8,3u8,3u8,7u8,8u8,19u8,14u8,5u8,11u8,22u8,1u8,8u8, - 23u8,6u8,8u8,5u8,3u8,7u8,11u8,28u8,2u8,5u8,11u8,22u8,1u8,8u8,23u8,3u8,3u8,3u8,3u8,3u8, - 3u8,3u8,7u8,8u8,13u8,6u8,8u8,16u8,1u8,7u8,11u8,22u8,1u8,9u8,0u8,1u8,9u8,1u8,3u8,8u8, - 26u8,7u8,8u8,20u8,4u8,2u8,11u8,22u8,1u8,8u8,23u8,5u8,2u8,5u8,11u8,22u8,1u8,9u8,0u8,4u8, - 1u8,11u8,22u8,1u8,8u8,23u8,5u8,7u8,8u8,13u8,5u8,98u8,108u8,111u8,99u8,107u8,7u8,103u8,101u8,110u8, - 101u8,115u8,105u8,115u8,15u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8, - 15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,5u8,115u8,116u8,97u8, - 107u8,101u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8, - 110u8,8u8,98u8,108u8,115u8,49u8,50u8,51u8,56u8,49u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8, - 114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,109u8,97u8,116u8, - 104u8,54u8,52u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,14u8,115u8,116u8, - 97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8, - 97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8, - 115u8,116u8,97u8,109u8,112u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,13u8,65u8,100u8,100u8,83u8,116u8,97u8,107u8, - 101u8,69u8,118u8,101u8,110u8,116u8,17u8,65u8,108u8,108u8,111u8,119u8,101u8,100u8,86u8,97u8,108u8,105u8,100u8,97u8, - 116u8,111u8,114u8,115u8,21u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8, - 108u8,105u8,116u8,105u8,101u8,115u8,22u8,68u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,82u8,101u8,119u8, - 97u8,114u8,100u8,115u8,69u8,118u8,101u8,110u8,116u8,19u8,73u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,76u8,111u8, - 99u8,107u8,117u8,112u8,69u8,118u8,101u8,110u8,116u8,30u8,73u8,110u8,100u8,105u8,118u8,105u8,100u8,117u8,97u8,108u8, - 86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,80u8,101u8,114u8,102u8,111u8,114u8,109u8,97u8,110u8,99u8,101u8, - 21u8,74u8,111u8,105u8,110u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,83u8,101u8,116u8,69u8,118u8,101u8, - 110u8,116u8,22u8,76u8,101u8,97u8,118u8,101u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,83u8,101u8,116u8, - 69u8,118u8,101u8,110u8,116u8,15u8,79u8,119u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,20u8,82u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8,83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8, - 110u8,116u8,31u8,82u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8, - 67u8,97u8,110u8,100u8,105u8,100u8,97u8,116u8,101u8,69u8,118u8,101u8,110u8,116u8,23u8,82u8,111u8,116u8,97u8,116u8, - 101u8,67u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,75u8,101u8,121u8,69u8,118u8,101u8,110u8,116u8,16u8,83u8, - 101u8,116u8,79u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,69u8,118u8,101u8,110u8,116u8,9u8,83u8,116u8,97u8,107u8, - 101u8,80u8,111u8,111u8,108u8,16u8,85u8,110u8,108u8,111u8,99u8,107u8,83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8, - 110u8,116u8,38u8,85u8,112u8,100u8,97u8,116u8,101u8,78u8,101u8,116u8,119u8,111u8,114u8,107u8,65u8,110u8,100u8,70u8, - 117u8,108u8,108u8,110u8,111u8,100u8,101u8,65u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,69u8,118u8,101u8,110u8, - 116u8,15u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,67u8,111u8,110u8,102u8,105u8,103u8,13u8,86u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,70u8,101u8,101u8,115u8,13u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,73u8,110u8,102u8,111u8,20u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,80u8,101u8,114u8,102u8,111u8, - 114u8,109u8,97u8,110u8,99u8,101u8,12u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,83u8,101u8,116u8,18u8, - 87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8,110u8,116u8,9u8,97u8, - 100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,4u8,67u8,111u8,105u8,110u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8, - 111u8,105u8,110u8,18u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8, - 97u8,112u8,19u8,97u8,100u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8, - 101u8,101u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8,23u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,111u8,119u8,110u8, - 101u8,114u8,95u8,99u8,97u8,112u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,24u8,97u8,115u8,115u8,101u8,114u8,116u8, - 95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,24u8,99u8, - 97u8,108u8,99u8,117u8,108u8,97u8,116u8,101u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,95u8,97u8,109u8,111u8, - 117u8,110u8,116u8,28u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,95u8,97u8,108u8,108u8,111u8,119u8,101u8, - 100u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,17u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8, - 95u8,111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,17u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,111u8, - 119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,18u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8, - 114u8,101u8,119u8,97u8,114u8,100u8,115u8,17u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,95u8,111u8,119u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,14u8,102u8,105u8,110u8,100u8,95u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,23u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,111u8,114u8,95u8,105u8,110u8,102u8,111u8,33u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8,114u8, - 101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,99u8, - 111u8,117u8,110u8,116u8,115u8,30u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8, - 111u8,99u8,104u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,19u8,103u8,101u8,116u8, - 95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,15u8,103u8,101u8,116u8, - 95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8,99u8,115u8,27u8,103u8,101u8,116u8,95u8,110u8,101u8,120u8, - 116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8, - 12u8,103u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,22u8,103u8,101u8,116u8,95u8,111u8,119u8, - 110u8,101u8,100u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,25u8,103u8,101u8,116u8, - 95u8,114u8,101u8,109u8,97u8,105u8,110u8,105u8,110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8, - 99u8,115u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,20u8,103u8,101u8,116u8,95u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,111u8,114u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,19u8,103u8,101u8,116u8,95u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,105u8,110u8,100u8,101u8,120u8,19u8,103u8,101u8,116u8,95u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,116u8,97u8,116u8,101u8,15u8,105u8,110u8,99u8,114u8,101u8,97u8, - 115u8,101u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,24u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8, - 111u8,99u8,107u8,117u8,112u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,10u8,105u8,110u8,105u8,116u8,105u8, - 97u8,108u8,105u8,122u8,101u8,16u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,111u8,119u8,110u8, - 101u8,114u8,22u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8, - 111u8,119u8,110u8,101u8,114u8,20u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,111u8,114u8,25u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,102u8,101u8,101u8,115u8,10u8,105u8,115u8,95u8,97u8,108u8,108u8,111u8, - 119u8,101u8,100u8,26u8,105u8,115u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8, - 95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,18u8,106u8,111u8,105u8,110u8,95u8,118u8,97u8,108u8,105u8, - 100u8,97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8,27u8,106u8,111u8,105u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,19u8,108u8,101u8, - 97u8,118u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8,12u8,111u8,110u8, - 95u8,110u8,101u8,119u8,95u8,101u8,112u8,111u8,99u8,104u8,16u8,114u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8, - 101u8,95u8,115u8,116u8,97u8,107u8,101u8,25u8,114u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8,95u8,115u8, - 116u8,97u8,107u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,17u8,114u8,101u8,109u8,111u8,118u8,101u8, - 95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,20u8,114u8,111u8,116u8,97u8,116u8,101u8,95u8,99u8, - 111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,107u8,101u8,121u8,19u8,115u8,101u8,116u8,95u8,100u8,101u8,108u8, - 101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,28u8,115u8,101u8,116u8,95u8,100u8,101u8,108u8, - 101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8, - 112u8,12u8,115u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,21u8,115u8,101u8,116u8,95u8,111u8, - 112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,17u8,115u8,116u8,97u8, - 107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,14u8,77u8,105u8,110u8,116u8,67u8, - 97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,25u8,115u8,116u8,111u8,114u8,101u8,95u8,97u8,112u8,116u8,111u8, - 115u8,95u8,99u8,111u8,105u8,110u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,6u8,117u8,110u8,108u8,111u8, - 99u8,107u8,15u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,37u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8,110u8,100u8,95u8,102u8,117u8, - 108u8,108u8,110u8,111u8,100u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,29u8,117u8,112u8,100u8, - 97u8,116u8,101u8,95u8,112u8,101u8,114u8,102u8,111u8,114u8,109u8,97u8,110u8,99u8,101u8,95u8,115u8,116u8,97u8,116u8, - 105u8,115u8,116u8,105u8,99u8,115u8,13u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8,67u8,111u8,110u8,102u8,105u8,103u8, - 17u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,28u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,95u8,105u8, - 110u8,99u8,114u8,101u8,97u8,115u8,101u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,17u8,119u8,105u8,116u8, - 104u8,100u8,114u8,97u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,12u8,112u8,111u8,111u8,108u8,95u8, - 97u8,100u8,100u8,114u8,101u8,115u8,115u8,12u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,101u8,100u8, - 8u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,115u8,8u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,14u8,114u8, - 101u8,119u8,97u8,114u8,100u8,115u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,21u8,111u8,108u8,100u8,95u8,108u8,111u8, - 99u8,107u8,101u8,100u8,95u8,117u8,110u8,116u8,105u8,108u8,95u8,115u8,101u8,99u8,115u8,21u8,110u8,101u8,119u8,95u8, - 108u8,111u8,99u8,107u8,101u8,100u8,95u8,117u8,110u8,116u8,105u8,108u8,95u8,115u8,101u8,99u8,115u8,20u8,115u8,117u8, - 99u8,99u8,101u8,115u8,115u8,102u8,117u8,108u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,115u8,16u8,102u8, - 97u8,105u8,108u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,115u8,6u8,97u8,109u8,111u8,117u8, - 110u8,116u8,20u8,111u8,108u8,100u8,95u8,99u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,112u8,117u8,98u8, - 107u8,101u8,121u8,20u8,110u8,101u8,119u8,95u8,99u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,112u8,117u8, - 98u8,107u8,101u8,121u8,12u8,111u8,108u8,100u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,12u8,110u8,101u8, - 119u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,6u8,97u8,99u8,116u8,105u8,118u8,101u8,8u8,105u8,110u8, - 97u8,99u8,116u8,105u8,118u8,101u8,14u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,97u8,99u8,116u8,105u8,118u8, - 101u8,16u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,17u8,108u8, - 111u8,99u8,107u8,101u8,100u8,95u8,117u8,110u8,116u8,105u8,108u8,95u8,115u8,101u8,99u8,115u8,16u8,111u8,112u8,101u8, - 114u8,97u8,116u8,111u8,114u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,15u8,100u8,101u8,108u8,101u8,103u8,97u8, - 116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,27u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8, - 95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8, - 101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,19u8,115u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,16u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,23u8,114u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8,95u8,115u8,116u8, - 97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,27u8,114u8,111u8,116u8,97u8,116u8,101u8,95u8,99u8,111u8, - 110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,107u8,101u8,121u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,44u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8,110u8,100u8,95u8,102u8,117u8, - 108u8,108u8,110u8,111u8,100u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,95u8,101u8,118u8,101u8, - 110u8,116u8,115u8,22u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,25u8,106u8,111u8,105u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,95u8,115u8,101u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,25u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8, - 117u8,116u8,101u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,117u8, - 110u8,108u8,111u8,99u8,107u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,21u8,119u8, - 105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8, - 26u8,108u8,101u8,97u8,118u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8, - 95u8,101u8,118u8,101u8,110u8,116u8,115u8,15u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,117u8,110u8,108u8,111u8,99u8, - 107u8,101u8,100u8,21u8,111u8,108u8,100u8,95u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,101u8,115u8,21u8,110u8,101u8,119u8,95u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,101u8,115u8,22u8,111u8,108u8,100u8,95u8,102u8,117u8,108u8,108u8,110u8,111u8,100u8,101u8, - 95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,22u8,110u8,101u8,119u8,95u8,102u8,117u8,108u8,108u8,110u8, - 111u8,100u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,16u8,99u8,111u8,110u8,115u8,101u8,110u8, - 115u8,117u8,115u8,95u8,112u8,117u8,98u8,107u8,101u8,121u8,17u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,18u8,102u8,117u8,108u8,108u8,110u8,111u8,100u8,101u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,101u8,115u8,15u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,105u8,110u8, - 100u8,101u8,120u8,10u8,102u8,101u8,101u8,115u8,95u8,116u8,97u8,98u8,108u8,101u8,5u8,84u8,97u8,98u8,108u8,101u8, - 4u8,97u8,100u8,100u8,114u8,12u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,6u8,99u8, - 111u8,110u8,102u8,105u8,103u8,10u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,16u8,99u8,111u8,110u8, - 115u8,101u8,110u8,115u8,117u8,115u8,95u8,115u8,99u8,104u8,101u8,109u8,101u8,17u8,97u8,99u8,116u8,105u8,118u8,101u8, - 95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,18u8,116u8,111u8,116u8,97u8,108u8,95u8,118u8,111u8, - 116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,19u8,116u8,111u8,116u8,97u8,108u8,95u8,106u8,111u8,105u8, - 110u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,16u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8, - 116u8,104u8,100u8,114u8,97u8,119u8,110u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,5u8,118u8, - 97u8,108u8,117u8,101u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,122u8,101u8,114u8,111u8,7u8,105u8,115u8, - 95u8,115u8,111u8,109u8,101u8,5u8,109u8,101u8,114u8,103u8,101u8,3u8,103u8,101u8,116u8,18u8,103u8,101u8,116u8,95u8, - 114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,16u8,105u8,110u8,118u8,97u8,108u8, - 105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8, - 110u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8, - 117u8,116u8,3u8,97u8,100u8,100u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,9u8,110u8,111u8,116u8,95u8, - 102u8,111u8,117u8,110u8,100u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,4u8,109u8,105u8,110u8,116u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8, - 111u8,110u8,101u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,29u8,103u8,101u8,116u8,95u8, - 114u8,101u8,99u8,117u8,114u8,114u8,105u8,110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,100u8,117u8,114u8, - 97u8,116u8,105u8,111u8,110u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8, - 4u8,122u8,101u8,114u8,111u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8, - 108u8,101u8,17u8,80u8,114u8,111u8,111u8,102u8,79u8,102u8,80u8,111u8,115u8,115u8,101u8,115u8,115u8,105u8,111u8,110u8, - 16u8,80u8,117u8,98u8,108u8,105u8,99u8,75u8,101u8,121u8,87u8,105u8,116u8,104u8,80u8,111u8,80u8,30u8,112u8,114u8, - 111u8,111u8,102u8,95u8,111u8,102u8,95u8,112u8,111u8,115u8,115u8,101u8,115u8,115u8,105u8,111u8,110u8,95u8,102u8,114u8, - 111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,30u8,112u8,117u8,98u8,108u8,105u8,99u8,95u8,107u8,101u8,121u8,95u8, - 102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,95u8,119u8,105u8,116u8,104u8,95u8,112u8,111u8,112u8,3u8, - 110u8,101u8,119u8,30u8,103u8,101u8,116u8,95u8,97u8,108u8,108u8,111u8,119u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8, - 116u8,111u8,114u8,95u8,115u8,101u8,116u8,95u8,99u8,104u8,97u8,110u8,103u8,101u8,15u8,117u8,110u8,97u8,117u8,116u8, - 104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,101u8,100u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8, - 116u8,97u8,116u8,101u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,11u8,115u8,119u8,97u8,112u8,95u8,114u8,101u8, - 109u8,111u8,118u8,101u8,3u8,109u8,105u8,110u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,15u8,103u8,101u8,116u8,95u8, - 114u8,101u8,119u8,97u8,114u8,100u8,95u8,114u8,97u8,116u8,101u8,11u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,95u8, - 97u8,108u8,108u8,31u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8, - 114u8,105u8,98u8,117u8,116u8,101u8,95u8,103u8,97u8,115u8,95u8,102u8,101u8,101u8,115u8,6u8,114u8,101u8,109u8,111u8, - 118u8,101u8,31u8,103u8,101u8,116u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,95u8, - 105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,105u8,109u8,105u8,116u8,7u8,100u8,101u8,112u8,111u8,115u8, - 105u8,116u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,19u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,18u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,16u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,64u8,66u8,15u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,0u8,1u8,0u8, - 0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8, - 112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,253u8,13u8,19u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,95u8, - 67u8,79u8,78u8,70u8,73u8,71u8,31u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,67u8,111u8,110u8, - 102u8,105u8,103u8,32u8,110u8,111u8,116u8,32u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,46u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,83u8,84u8,65u8,75u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8, - 87u8,39u8,78u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,116u8, - 111u8,32u8,106u8,111u8,105u8,110u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,115u8,101u8,116u8, - 46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,83u8,84u8,65u8,75u8,69u8,95u8,84u8,79u8,79u8, - 95u8,72u8,73u8,71u8,72u8,37u8,84u8,111u8,111u8,32u8,109u8,117u8,99u8,104u8,32u8,115u8,116u8,97u8,107u8,101u8, - 32u8,116u8,111u8,32u8,106u8,111u8,105u8,110u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,115u8, - 101u8,116u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8, - 95u8,65u8,67u8,84u8,73u8,86u8,69u8,95u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,52u8,65u8,99u8, - 99u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,97u8,32u8,118u8, - 97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,111u8,114u8,32u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,32u8, - 118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,46u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8, - 78u8,79u8,84u8,95u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,27u8,65u8,99u8,99u8,111u8,117u8,110u8, - 116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8, - 46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,76u8,65u8,83u8,84u8,95u8,86u8,65u8,76u8,73u8, - 68u8,65u8,84u8,79u8,82u8,28u8,67u8,97u8,110u8,39u8,116u8,32u8,114u8,101u8,109u8,111u8,118u8,101u8,32u8,108u8, - 97u8,115u8,116u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,46u8,7u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,18u8,69u8,83u8,84u8,65u8,75u8,69u8,95u8,69u8,88u8,67u8,69u8,69u8,68u8,83u8,95u8,77u8,65u8, - 88u8,36u8,84u8,111u8,116u8,97u8,108u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8, - 115u8,32u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,32u8,97u8,108u8,108u8,111u8,119u8,101u8,100u8,46u8,8u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,82u8,69u8,71u8,73u8, - 83u8,84u8,69u8,82u8,69u8,68u8,55u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8,97u8,108u8, - 114u8,101u8,97u8,100u8,121u8,32u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8,32u8,97u8,115u8,32u8, - 97u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,99u8,97u8,110u8,100u8,105u8,100u8,97u8,116u8, - 101u8,46u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,78u8,79u8,84u8,95u8,79u8,80u8,69u8,82u8, - 65u8,84u8,79u8,82u8,52u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8, - 116u8,32u8,104u8,97u8,118u8,101u8,32u8,116u8,104u8,101u8,32u8,114u8,105u8,103u8,104u8,116u8,32u8,111u8,112u8,101u8, - 114u8,97u8,116u8,111u8,114u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,46u8,10u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,45u8,69u8,78u8,79u8,95u8,80u8,79u8,83u8,84u8,95u8,71u8,69u8,78u8,69u8,83u8, - 73u8,83u8,95u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,95u8,83u8,69u8,84u8,95u8,67u8,72u8,65u8, - 78u8,71u8,69u8,95u8,65u8,76u8,76u8,79u8,87u8,69u8,68u8,66u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,115u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,106u8,111u8,105u8,110u8,32u8,111u8,114u8,32u8,108u8,101u8, - 97u8,118u8,101u8,32u8,112u8,111u8,115u8,116u8,32u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,32u8,111u8,110u8,32u8, - 116u8,104u8,105u8,115u8,32u8,116u8,101u8,115u8,116u8,32u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,46u8,11u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,80u8,85u8,66u8,76u8, - 73u8,67u8,95u8,75u8,69u8,89u8,28u8,73u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,99u8,111u8,110u8,115u8,101u8, - 110u8,115u8,117u8,115u8,32u8,112u8,117u8,98u8,108u8,105u8,99u8,32u8,107u8,101u8,121u8,12u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,24u8,69u8,86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,95u8,83u8,69u8,84u8,95u8,84u8, - 79u8,79u8,95u8,76u8,65u8,82u8,71u8,69u8,31u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,115u8, - 101u8,116u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,115u8,32u8,116u8,104u8,101u8,32u8,108u8,105u8,109u8,105u8,116u8, - 13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,69u8,86u8,79u8,84u8,73u8,78u8,71u8,95u8,80u8,79u8,87u8, - 69u8,82u8,95u8,73u8,78u8,67u8,82u8,69u8,65u8,83u8,69u8,95u8,69u8,88u8,67u8,69u8,69u8,68u8,83u8,95u8, - 76u8,73u8,77u8,73u8,84u8,68u8,86u8,111u8,116u8,105u8,110u8,103u8,32u8,112u8,111u8,119u8,101u8,114u8,32u8,105u8, - 110u8,99u8,114u8,101u8,97u8,115u8,101u8,32u8,104u8,97u8,115u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,101u8,100u8, - 32u8,116u8,104u8,101u8,32u8,108u8,105u8,109u8,105u8,116u8,32u8,102u8,111u8,114u8,32u8,116u8,104u8,105u8,115u8,32u8, - 99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,101u8,112u8,111u8,99u8,104u8,46u8,14u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,26u8,69u8,83u8,84u8,65u8,75u8,69u8,95u8,80u8,79u8,79u8,76u8,95u8,68u8,79u8,69u8,83u8,95u8, - 78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,55u8,83u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8, - 32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8, - 104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,112u8,111u8,111u8,108u8,32u8,97u8,100u8,100u8, - 114u8,101u8,115u8,115u8,46u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,79u8,87u8,78u8,69u8,82u8, - 95u8,67u8,65u8,80u8,95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,56u8,79u8,119u8,110u8,101u8,114u8, - 32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8, - 32u8,101u8,120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8, - 101u8,100u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,46u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8, - 69u8,79u8,87u8,78u8,69u8,82u8,95u8,67u8,65u8,80u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8, - 88u8,73u8,83u8,84u8,83u8,53u8,65u8,110u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,99u8,97u8,110u8, - 110u8,111u8,116u8,32u8,111u8,119u8,110u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8,111u8,110u8, - 101u8,32u8,111u8,119u8,110u8,101u8,114u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,46u8,17u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,73u8,78u8,69u8,76u8,73u8,71u8,73u8,66u8,76u8,69u8,95u8, - 86u8,65u8,76u8,73u8,68u8,65u8,84u8,79u8,82u8,72u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8, - 105u8,115u8,32u8,110u8,111u8,116u8,32u8,100u8,101u8,102u8,105u8,110u8,101u8,100u8,32u8,105u8,110u8,32u8,116u8,104u8, - 101u8,32u8,65u8,67u8,76u8,32u8,111u8,102u8,32u8,101u8,110u8,116u8,105u8,116u8,105u8,101u8,115u8,32u8,97u8,108u8, - 108u8,111u8,119u8,101u8,100u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,115u8,18u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8, - 76u8,79u8,67u8,75u8,85u8,80u8,65u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,117u8,112u8,100u8,97u8,116u8,101u8, - 32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,39u8,115u8,32u8,108u8,111u8,99u8,107u8,117u8,112u8, - 32u8,116u8,111u8,32u8,101u8,97u8,114u8,108u8,105u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8,32u8,99u8,117u8,114u8, - 114u8,101u8,110u8,116u8,32u8,108u8,111u8,99u8,107u8,117u8,112u8,46u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 26u8,69u8,70u8,69u8,69u8,83u8,95u8,84u8,65u8,66u8,76u8,69u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8, - 95u8,69u8,88u8,73u8,83u8,84u8,83u8,76u8,84u8,97u8,98u8,108u8,101u8,32u8,116u8,111u8,32u8,115u8,116u8,111u8, - 114u8,101u8,32u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,101u8,100u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8, - 116u8,105u8,111u8,110u8,32u8,102u8,101u8,101u8,115u8,32u8,102u8,111u8,114u8,32u8,101u8,97u8,99u8,104u8,32u8,118u8, - 97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8, - 115u8,116u8,115u8,46u8,0u8,11u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,1u8,1u8,0u8,12u8, - 103u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,1u8,1u8,0u8,15u8,103u8,101u8,116u8,95u8, - 108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8,99u8,115u8,1u8,1u8,0u8,17u8,115u8,116u8,97u8,107u8,101u8, - 95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,1u8,1u8,0u8,19u8,103u8,101u8,116u8,95u8, - 100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,1u8,1u8,0u8,19u8,103u8, - 101u8,116u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,105u8,110u8,100u8,101u8,120u8,1u8,1u8, - 0u8,19u8,103u8,101u8,116u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,116u8,97u8,116u8, - 101u8,1u8,1u8,0u8,20u8,103u8,101u8,116u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,99u8, - 111u8,110u8,102u8,105u8,103u8,1u8,1u8,0u8,25u8,103u8,101u8,116u8,95u8,114u8,101u8,109u8,97u8,105u8,110u8,105u8, - 110u8,103u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8,99u8,115u8,1u8,1u8,0u8,30u8,103u8,101u8, - 116u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,118u8,111u8,116u8,105u8, - 110u8,103u8,95u8,112u8,111u8,119u8,101u8,114u8,1u8,1u8,0u8,33u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8,114u8, - 101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,99u8, - 111u8,117u8,110u8,116u8,115u8,1u8,1u8,0u8,0u8,2u8,2u8,104u8,5u8,105u8,3u8,1u8,2u8,1u8,106u8,10u8, - 5u8,2u8,2u8,1u8,107u8,11u8,25u8,1u8,8u8,23u8,3u8,2u8,2u8,104u8,5u8,108u8,3u8,4u8,2u8,3u8, - 104u8,5u8,109u8,3u8,110u8,3u8,5u8,2u8,2u8,111u8,3u8,112u8,3u8,6u8,2u8,1u8,104u8,5u8,7u8,2u8, - 1u8,104u8,5u8,8u8,2u8,1u8,104u8,5u8,9u8,2u8,2u8,104u8,5u8,113u8,3u8,10u8,2u8,1u8,104u8,5u8, - 11u8,2u8,3u8,104u8,5u8,114u8,10u8,2u8,115u8,10u8,2u8,12u8,2u8,3u8,104u8,5u8,116u8,5u8,117u8,5u8, - 13u8,2u8,19u8,118u8,11u8,22u8,1u8,8u8,23u8,119u8,11u8,22u8,1u8,8u8,23u8,120u8,11u8,22u8,1u8,8u8, - 23u8,121u8,11u8,22u8,1u8,8u8,23u8,122u8,3u8,123u8,5u8,124u8,5u8,125u8,11u8,27u8,1u8,8u8,10u8,127u8, - 11u8,27u8,1u8,8u8,12u8,128u8,1u8,11u8,27u8,1u8,8u8,0u8,129u8,1u8,11u8,27u8,1u8,8u8,9u8,130u8, - 1u8,11u8,27u8,1u8,8u8,11u8,131u8,1u8,11u8,27u8,1u8,8u8,15u8,132u8,1u8,11u8,27u8,1u8,8u8,4u8, - 133u8,1u8,11u8,27u8,1u8,8u8,6u8,134u8,1u8,11u8,27u8,1u8,8u8,3u8,135u8,1u8,11u8,27u8,1u8,8u8, - 14u8,136u8,1u8,11u8,27u8,1u8,8u8,21u8,137u8,1u8,11u8,27u8,1u8,8u8,7u8,14u8,2u8,2u8,104u8,5u8, - 138u8,1u8,3u8,15u8,2u8,5u8,104u8,5u8,139u8,1u8,10u8,2u8,140u8,1u8,10u8,2u8,141u8,1u8,10u8,2u8, - 142u8,1u8,10u8,2u8,16u8,2u8,4u8,143u8,1u8,10u8,2u8,144u8,1u8,10u8,2u8,145u8,1u8,10u8,2u8,146u8, - 1u8,3u8,17u8,2u8,1u8,147u8,1u8,11u8,28u8,2u8,5u8,11u8,22u8,1u8,8u8,23u8,18u8,2u8,3u8,149u8, - 1u8,5u8,150u8,1u8,3u8,151u8,1u8,8u8,16u8,19u8,2u8,1u8,152u8,1u8,10u8,8u8,5u8,20u8,2u8,6u8, - 153u8,1u8,2u8,154u8,1u8,10u8,8u8,18u8,121u8,10u8,8u8,18u8,120u8,10u8,8u8,18u8,155u8,1u8,4u8,156u8, - 1u8,4u8,21u8,2u8,2u8,104u8,5u8,157u8,1u8,3u8,0u8,1u8,4u8,3u8,8u8,13u8,20u8,5u8,12u8,10u8, - 0u8,17u8,57u8,12u8,2u8,10u8,2u8,17u8,4u8,11u8,2u8,43u8,8u8,11u8,0u8,11u8,1u8,56u8,0u8,17u8, - 1u8,2u8,1u8,1u8,0u8,2u8,13u8,20u8,37u8,84u8,11u8,0u8,16u8,0u8,20u8,12u8,8u8,10u8,8u8,17u8, - 5u8,14u8,1u8,56u8,1u8,12u8,6u8,10u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8, - 16u8,11u8,1u8,56u8,2u8,2u8,7u8,21u8,42u8,20u8,12u8,10u8,10u8,10u8,16u8,1u8,10u8,8u8,17u8,12u8, - 12u8,2u8,14u8,2u8,56u8,3u8,4u8,32u8,11u8,10u8,1u8,8u8,12u8,4u8,5u8,40u8,11u8,10u8,16u8,2u8, - 10u8,8u8,17u8,12u8,12u8,3u8,14u8,3u8,56u8,3u8,12u8,4u8,11u8,4u8,4u8,44u8,10u8,6u8,17u8,54u8, - 10u8,8u8,42u8,13u8,12u8,9u8,10u8,8u8,17u8,34u8,4u8,55u8,10u8,9u8,15u8,3u8,11u8,1u8,56u8,4u8, - 5u8,59u8,10u8,9u8,15u8,4u8,11u8,1u8,56u8,4u8,17u8,63u8,12u8,5u8,14u8,5u8,17u8,64u8,12u8,7u8, - 1u8,10u8,9u8,46u8,17u8,18u8,11u8,7u8,37u8,4u8,72u8,5u8,77u8,11u8,9u8,1u8,7u8,12u8,17u8,65u8, - 39u8,11u8,9u8,15u8,5u8,11u8,8u8,11u8,6u8,18u8,0u8,56u8,5u8,2u8,2u8,3u8,0u8,1u8,17u8,45u8, - 22u8,7u8,21u8,42u8,17u8,15u8,6u8,12u8,3u8,10u8,3u8,10u8,0u8,12u8,2u8,46u8,11u8,2u8,56u8,6u8, - 4u8,17u8,11u8,3u8,11u8,0u8,56u8,7u8,11u8,1u8,56u8,4u8,5u8,21u8,11u8,3u8,11u8,0u8,11u8,1u8, - 56u8,8u8,2u8,3u8,0u8,0u8,0u8,1u8,16u8,10u8,1u8,46u8,56u8,9u8,32u8,4u8,11u8,5u8,6u8,10u8, - 0u8,10u8,1u8,69u8,50u8,68u8,50u8,5u8,0u8,11u8,1u8,1u8,11u8,0u8,1u8,2u8,4u8,0u8,0u8,0u8, - 1u8,8u8,11u8,0u8,41u8,8u8,4u8,4u8,5u8,7u8,7u8,11u8,17u8,71u8,39u8,2u8,5u8,0u8,0u8,0u8, - 1u8,8u8,11u8,0u8,17u8,47u8,4u8,4u8,5u8,7u8,7u8,13u8,17u8,65u8,39u8,2u8,6u8,0u8,0u8,0u8, - 52u8,30u8,40u8,11u8,0u8,53u8,11u8,3u8,53u8,24u8,11u8,1u8,53u8,24u8,12u8,7u8,11u8,4u8,53u8,11u8, - 2u8,53u8,24u8,12u8,6u8,10u8,6u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,36u8,4u8,26u8,11u8,7u8,11u8,6u8,26u8,52u8,12u8,5u8,5u8,28u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,11u8,5u8,2u8,7u8,1u8,0u8,1u8,1u8,53u8,24u8,10u8,0u8, - 17u8,57u8,12u8,3u8,10u8,0u8,17u8,72u8,10u8,3u8,41u8,1u8,32u8,4u8,14u8,11u8,0u8,11u8,1u8,18u8, - 1u8,45u8,1u8,5u8,23u8,11u8,0u8,1u8,11u8,3u8,42u8,1u8,12u8,2u8,11u8,1u8,11u8,2u8,15u8,7u8, - 21u8,2u8,8u8,1u8,0u8,0u8,1u8,15u8,10u8,0u8,17u8,57u8,41u8,8u8,32u8,4u8,6u8,5u8,11u8,11u8, - 0u8,1u8,7u8,10u8,17u8,71u8,39u8,11u8,0u8,11u8,1u8,45u8,8u8,2u8,9u8,1u8,0u8,0u8,1u8,4u8, - 11u8,0u8,19u8,8u8,1u8,2u8,10u8,0u8,0u8,1u8,2u8,54u8,40u8,10u8,0u8,46u8,56u8,1u8,12u8,9u8, - 10u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,16u8,11u8,9u8,11u8,1u8,11u8,2u8, - 11u8,3u8,11u8,4u8,17u8,6u8,12u8,5u8,5u8,18u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8, - 5u8,11u8,5u8,12u8,8u8,10u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,36u8,7u8, - 21u8,43u8,2u8,16u8,8u8,12u8,6u8,10u8,8u8,11u8,6u8,56u8,10u8,12u8,7u8,11u8,0u8,11u8,7u8,56u8, - 4u8,5u8,38u8,11u8,0u8,1u8,11u8,8u8,2u8,11u8,1u8,0u8,1u8,8u8,5u8,8u8,11u8,0u8,17u8,57u8, - 12u8,1u8,10u8,1u8,17u8,4u8,11u8,1u8,44u8,8u8,2u8,12u8,0u8,0u8,0u8,17u8,33u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8,0u8,65u8,16u8,12u8,3u8,40u8,10u8,2u8,10u8,3u8,35u8, - 4u8,29u8,5u8,11u8,10u8,0u8,10u8,2u8,66u8,16u8,16u8,9u8,20u8,10u8,1u8,33u8,4u8,24u8,11u8,0u8, - 1u8,11u8,2u8,56u8,11u8,2u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8, - 5u8,5u8,11u8,0u8,1u8,56u8,12u8,2u8,13u8,0u8,0u8,0u8,7u8,8u8,11u8,1u8,17u8,18u8,12u8,3u8, - 11u8,0u8,11u8,3u8,11u8,2u8,18u8,18u8,2u8,14u8,1u8,0u8,1u8,19u8,57u8,13u8,7u8,21u8,43u8,19u8, - 16u8,10u8,11u8,0u8,66u8,58u8,12u8,1u8,10u8,1u8,16u8,11u8,20u8,11u8,1u8,16u8,12u8,20u8,2u8,15u8, - 1u8,0u8,2u8,13u8,20u8,59u8,37u8,10u8,0u8,17u8,5u8,10u8,0u8,17u8,25u8,12u8,5u8,10u8,5u8,7u8, - 15u8,33u8,4u8,12u8,8u8,12u8,1u8,5u8,16u8,11u8,5u8,7u8,14u8,33u8,12u8,1u8,11u8,1u8,4u8,33u8, - 10u8,0u8,43u8,13u8,16u8,4u8,56u8,1u8,12u8,3u8,11u8,0u8,43u8,13u8,16u8,13u8,56u8,1u8,12u8,4u8, - 11u8,3u8,11u8,4u8,22u8,12u8,2u8,5u8,35u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8, - 11u8,2u8,2u8,16u8,1u8,0u8,1u8,13u8,1u8,7u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,13u8,16u8,14u8, - 20u8,2u8,17u8,1u8,0u8,1u8,13u8,1u8,7u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,13u8,16u8,15u8,20u8, - 2u8,18u8,0u8,0u8,0u8,60u8,19u8,10u8,0u8,16u8,3u8,56u8,1u8,12u8,2u8,10u8,0u8,16u8,4u8,56u8, - 1u8,12u8,1u8,11u8,0u8,16u8,13u8,56u8,1u8,12u8,3u8,40u8,11u8,2u8,11u8,1u8,22u8,11u8,3u8,22u8, - 2u8,19u8,1u8,0u8,1u8,13u8,1u8,7u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,13u8,16u8,16u8,20u8,2u8, - 20u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,0u8,20u8,2u8,21u8,1u8,0u8,1u8,13u8,17u8,20u8,10u8, - 0u8,17u8,5u8,11u8,0u8,43u8,13u8,16u8,15u8,20u8,12u8,2u8,10u8,2u8,17u8,76u8,37u8,4u8,14u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,5u8,18u8,11u8,2u8,17u8,76u8,23u8,12u8,1u8,11u8, - 1u8,2u8,22u8,1u8,0u8,1u8,13u8,18u8,18u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,13u8,12u8,1u8,10u8, - 1u8,16u8,4u8,56u8,1u8,10u8,1u8,16u8,17u8,56u8,1u8,10u8,1u8,16u8,3u8,56u8,1u8,11u8,1u8,16u8, - 13u8,56u8,1u8,2u8,23u8,1u8,0u8,1u8,16u8,61u8,15u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,16u8,12u8, - 1u8,10u8,1u8,16u8,18u8,20u8,10u8,1u8,16u8,19u8,20u8,11u8,1u8,16u8,20u8,20u8,2u8,24u8,1u8,0u8, - 1u8,16u8,1u8,7u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,16u8,16u8,21u8,20u8,2u8,25u8,1u8,0u8,1u8, - 20u8,62u8,48u8,7u8,21u8,43u8,20u8,12u8,7u8,10u8,7u8,16u8,2u8,10u8,0u8,17u8,12u8,12u8,1u8,14u8, - 1u8,56u8,3u8,4u8,16u8,11u8,7u8,1u8,7u8,16u8,12u8,6u8,5u8,46u8,10u8,7u8,16u8,1u8,10u8,0u8, - 17u8,12u8,12u8,2u8,14u8,2u8,56u8,3u8,4u8,29u8,11u8,7u8,1u8,7u8,15u8,12u8,5u8,5u8,44u8,11u8, - 7u8,16u8,22u8,11u8,0u8,17u8,12u8,12u8,3u8,14u8,3u8,56u8,3u8,4u8,40u8,7u8,14u8,12u8,4u8,5u8, - 42u8,7u8,0u8,12u8,4u8,11u8,4u8,12u8,5u8,11u8,5u8,12u8,6u8,11u8,6u8,2u8,26u8,1u8,4u8,2u8, - 8u8,13u8,5u8,9u8,11u8,0u8,17u8,57u8,12u8,1u8,10u8,1u8,17u8,4u8,11u8,1u8,43u8,8u8,17u8,27u8, - 2u8,27u8,1u8,0u8,1u8,13u8,63u8,42u8,11u8,0u8,16u8,0u8,20u8,12u8,4u8,10u8,4u8,17u8,5u8,17u8, - 63u8,12u8,1u8,10u8,4u8,42u8,13u8,12u8,5u8,10u8,5u8,16u8,15u8,20u8,12u8,3u8,17u8,76u8,14u8,1u8, - 17u8,77u8,22u8,12u8,2u8,10u8,3u8,10u8,2u8,35u8,4u8,25u8,5u8,30u8,11u8,5u8,1u8,7u8,4u8,17u8, - 65u8,39u8,10u8,2u8,10u8,5u8,15u8,15u8,21u8,11u8,5u8,15u8,23u8,11u8,4u8,11u8,3u8,11u8,2u8,18u8, - 4u8,56u8,13u8,2u8,28u8,3u8,0u8,0u8,65u8,24u8,10u8,0u8,17u8,72u8,10u8,0u8,12u8,4u8,64u8,16u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,64u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 12u8,2u8,64u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,11u8,4u8,49u8,0u8,11u8,1u8, - 11u8,3u8,11u8,2u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,20u8, - 45u8,20u8,11u8,0u8,64u8,58u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,19u8,45u8,19u8,2u8,29u8, - 0u8,0u8,1u8,1u8,66u8,99u8,10u8,0u8,17u8,57u8,12u8,20u8,10u8,20u8,17u8,33u8,4u8,7u8,5u8,12u8, - 11u8,0u8,1u8,7u8,3u8,17u8,71u8,39u8,10u8,20u8,17u8,47u8,32u8,4u8,17u8,5u8,22u8,11u8,0u8,1u8, - 7u8,1u8,17u8,78u8,39u8,10u8,0u8,12u8,13u8,56u8,14u8,12u8,1u8,56u8,14u8,12u8,12u8,56u8,14u8,12u8, - 14u8,56u8,14u8,12u8,15u8,10u8,20u8,12u8,16u8,10u8,20u8,12u8,17u8,10u8,0u8,56u8,15u8,12u8,18u8,10u8, - 0u8,56u8,16u8,12u8,19u8,10u8,0u8,56u8,17u8,12u8,2u8,10u8,0u8,56u8,18u8,12u8,3u8,10u8,0u8,56u8, - 19u8,12u8,4u8,10u8,0u8,56u8,20u8,12u8,5u8,10u8,0u8,56u8,21u8,12u8,6u8,10u8,0u8,56u8,22u8,12u8, - 7u8,10u8,0u8,56u8,23u8,12u8,8u8,10u8,0u8,56u8,24u8,12u8,9u8,10u8,0u8,56u8,25u8,12u8,10u8,10u8, - 0u8,56u8,26u8,12u8,11u8,11u8,13u8,11u8,1u8,11u8,15u8,11u8,12u8,11u8,14u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,11u8,16u8,11u8,17u8,11u8,18u8,11u8,19u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8, - 11u8,6u8,11u8,7u8,11u8,8u8,11u8,9u8,11u8,10u8,11u8,11u8,18u8,13u8,45u8,13u8,11u8,0u8,11u8,20u8, - 18u8,8u8,45u8,8u8,2u8,30u8,1u8,4u8,4u8,1u8,8u8,13u8,20u8,5u8,37u8,10u8,0u8,17u8,29u8,10u8, - 0u8,64u8,78u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,64u8,78u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,64u8,78u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 18u8,16u8,45u8,16u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,16u8,10u8,0u8, - 11u8,1u8,17u8,0u8,10u8,0u8,17u8,57u8,12u8,4u8,10u8,4u8,10u8,2u8,34u8,4u8,26u8,10u8,0u8,11u8, - 2u8,17u8,45u8,11u8,4u8,10u8,3u8,34u8,4u8,34u8,11u8,0u8,11u8,3u8,17u8,43u8,5u8,36u8,11u8,0u8, - 1u8,2u8,31u8,1u8,4u8,1u8,1u8,79u8,29u8,10u8,1u8,12u8,6u8,11u8,2u8,17u8,81u8,12u8,5u8,11u8, - 6u8,14u8,5u8,17u8,82u8,12u8,7u8,13u8,7u8,46u8,56u8,27u8,4u8,14u8,5u8,19u8,11u8,0u8,1u8,7u8, - 5u8,17u8,65u8,39u8,10u8,0u8,17u8,29u8,11u8,0u8,11u8,1u8,11u8,3u8,11u8,4u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,16u8,45u8,16u8,2u8,32u8,3u8,0u8,0u8,1u8,17u8,10u8,0u8,17u8,72u8, - 7u8,21u8,41u8,17u8,32u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8,2u8,17u8,78u8,39u8,11u8,0u8,56u8, - 28u8,18u8,17u8,45u8,17u8,2u8,33u8,0u8,0u8,1u8,1u8,24u8,15u8,7u8,21u8,41u8,1u8,32u8,4u8,7u8, - 8u8,12u8,1u8,5u8,13u8,7u8,21u8,43u8,1u8,16u8,7u8,14u8,0u8,56u8,29u8,12u8,1u8,11u8,1u8,2u8, - 34u8,1u8,0u8,1u8,20u8,87u8,18u8,10u8,0u8,17u8,5u8,11u8,0u8,17u8,25u8,12u8,2u8,10u8,2u8,7u8, - 15u8,33u8,4u8,12u8,8u8,12u8,1u8,5u8,16u8,11u8,2u8,7u8,14u8,33u8,12u8,1u8,11u8,1u8,2u8,35u8, - 1u8,4u8,3u8,13u8,16u8,20u8,41u8,15u8,17u8,63u8,12u8,2u8,14u8,2u8,17u8,85u8,4u8,6u8,5u8,11u8, - 11u8,0u8,1u8,7u8,9u8,17u8,65u8,39u8,11u8,0u8,11u8,1u8,17u8,36u8,2u8,36u8,3u8,0u8,3u8,13u8, - 16u8,20u8,88u8,119u8,10u8,1u8,17u8,5u8,10u8,1u8,42u8,13u8,12u8,9u8,11u8,0u8,17u8,57u8,10u8,9u8, - 16u8,16u8,20u8,33u8,4u8,13u8,5u8,18u8,11u8,9u8,1u8,7u8,7u8,17u8,86u8,39u8,10u8,1u8,17u8,25u8, - 7u8,0u8,33u8,4u8,24u8,5u8,29u8,11u8,9u8,1u8,7u8,0u8,17u8,87u8,39u8,17u8,63u8,12u8,6u8,14u8, - 6u8,17u8,64u8,12u8,7u8,12u8,8u8,10u8,9u8,46u8,17u8,18u8,12u8,12u8,10u8,12u8,11u8,8u8,38u8,4u8, - 44u8,5u8,49u8,11u8,9u8,1u8,7u8,15u8,17u8,65u8,39u8,10u8,12u8,11u8,7u8,37u8,4u8,54u8,5u8,59u8, - 11u8,9u8,1u8,7u8,14u8,17u8,65u8,39u8,11u8,12u8,17u8,54u8,10u8,1u8,42u8,16u8,12u8,10u8,10u8,10u8, - 16u8,18u8,56u8,30u8,32u8,4u8,70u8,5u8,77u8,11u8,10u8,1u8,11u8,9u8,1u8,7u8,5u8,17u8,65u8,39u8, - 7u8,21u8,42u8,20u8,12u8,11u8,10u8,11u8,15u8,2u8,12u8,5u8,10u8,1u8,10u8,9u8,11u8,10u8,20u8,12u8, - 4u8,12u8,3u8,12u8,2u8,11u8,5u8,11u8,2u8,11u8,3u8,46u8,11u8,4u8,17u8,13u8,68u8,16u8,10u8,11u8, - 16u8,1u8,65u8,16u8,11u8,11u8,16u8,2u8,65u8,16u8,22u8,7u8,20u8,37u8,4u8,108u8,5u8,113u8,11u8,9u8, - 1u8,7u8,17u8,17u8,65u8,39u8,11u8,9u8,15u8,24u8,11u8,1u8,18u8,6u8,56u8,31u8,2u8,37u8,1u8,4u8, - 2u8,13u8,20u8,89u8,117u8,17u8,63u8,12u8,2u8,14u8,2u8,17u8,85u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8, - 7u8,9u8,17u8,65u8,39u8,10u8,1u8,17u8,5u8,10u8,1u8,42u8,13u8,12u8,5u8,11u8,0u8,17u8,57u8,10u8, - 5u8,16u8,16u8,20u8,33u8,4u8,24u8,5u8,29u8,11u8,5u8,1u8,7u8,7u8,17u8,86u8,39u8,7u8,21u8,42u8, - 20u8,12u8,7u8,10u8,7u8,16u8,2u8,10u8,1u8,17u8,12u8,12u8,4u8,14u8,4u8,56u8,3u8,4u8,71u8,10u8, - 7u8,15u8,2u8,13u8,4u8,56u8,32u8,56u8,33u8,1u8,11u8,5u8,46u8,17u8,18u8,53u8,12u8,8u8,10u8,7u8, - 16u8,25u8,20u8,10u8,8u8,36u8,4u8,66u8,10u8,7u8,16u8,25u8,20u8,11u8,8u8,23u8,11u8,7u8,15u8,25u8, - 21u8,5u8,70u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 11u8,7u8,15u8,25u8,21u8,5u8,116u8,10u8,7u8,16u8,1u8,10u8,1u8,17u8,12u8,12u8,3u8,14u8,3u8,56u8, - 3u8,4u8,80u8,5u8,87u8,11u8,7u8,1u8,11u8,5u8,1u8,7u8,8u8,17u8,87u8,39u8,10u8,7u8,15u8,1u8, - 13u8,3u8,56u8,32u8,56u8,33u8,12u8,6u8,10u8,7u8,16u8,1u8,65u8,16u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,36u8,4u8,100u8,5u8,107u8,11u8,7u8,1u8,11u8,5u8,1u8,7u8,6u8,17u8,87u8,39u8,11u8, - 7u8,15u8,22u8,11u8,6u8,68u8,16u8,11u8,5u8,15u8,26u8,11u8,1u8,18u8,7u8,56u8,34u8,2u8,38u8,3u8, - 0u8,6u8,2u8,13u8,16u8,17u8,19u8,20u8,92u8,232u8,1u8,7u8,21u8,42u8,20u8,12u8,26u8,17u8,63u8,12u8, - 5u8,7u8,21u8,42u8,19u8,12u8,25u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,6u8,10u8,26u8, - 16u8,1u8,65u8,16u8,12u8,9u8,10u8,6u8,10u8,9u8,35u8,4u8,40u8,5u8,19u8,10u8,26u8,16u8,1u8,10u8, - 6u8,66u8,16u8,12u8,19u8,10u8,25u8,11u8,19u8,16u8,9u8,20u8,14u8,5u8,12u8,2u8,12u8,1u8,46u8,11u8, - 1u8,11u8,2u8,17u8,53u8,11u8,6u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,6u8,5u8, - 14u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,8u8,10u8,26u8,16u8,22u8,65u8,16u8,12u8,10u8, - 10u8,8u8,10u8,10u8,35u8,4u8,72u8,5u8,51u8,10u8,26u8,16u8,22u8,10u8,8u8,66u8,16u8,12u8,20u8,10u8, - 25u8,11u8,20u8,16u8,9u8,20u8,14u8,5u8,12u8,4u8,12u8,3u8,46u8,11u8,3u8,11u8,4u8,17u8,53u8,11u8, - 8u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,8u8,5u8,46u8,10u8,26u8,15u8,1u8,10u8, - 26u8,15u8,2u8,56u8,35u8,64u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,26u8,15u8,22u8,21u8, - 64u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,13u8,14u8,5u8,17u8,64u8,1u8,12u8,11u8,10u8, - 26u8,16u8,1u8,65u8,16u8,12u8,27u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,12u8,18u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,40u8,10u8,7u8, - 10u8,27u8,35u8,4u8,145u8,1u8,5u8,101u8,10u8,26u8,15u8,1u8,10u8,7u8,67u8,16u8,16u8,9u8,20u8,12u8, - 14u8,10u8,14u8,42u8,16u8,12u8,21u8,10u8,14u8,42u8,13u8,12u8,16u8,11u8,14u8,11u8,16u8,11u8,21u8,20u8, - 12u8,0u8,46u8,11u8,0u8,17u8,13u8,12u8,12u8,14u8,12u8,16u8,27u8,20u8,10u8,11u8,38u8,4u8,140u8,1u8, - 40u8,11u8,18u8,14u8,12u8,16u8,27u8,20u8,53u8,22u8,12u8,18u8,13u8,13u8,11u8,12u8,68u8,16u8,11u8,7u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,7u8,5u8,95u8,11u8,13u8,10u8,26u8,15u8,1u8, - 21u8,11u8,18u8,10u8,26u8,15u8,28u8,21u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,10u8,26u8,15u8,25u8,21u8,64u8,58u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 10u8,25u8,15u8,10u8,21u8,14u8,5u8,17u8,77u8,12u8,15u8,10u8,26u8,16u8,1u8,65u8,16u8,12u8,28u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,23u8,40u8,10u8,23u8,10u8,28u8,35u8,4u8,227u8,1u8,5u8, - 176u8,1u8,10u8,26u8,15u8,1u8,10u8,23u8,67u8,16u8,12u8,24u8,10u8,23u8,10u8,24u8,15u8,29u8,15u8,21u8, - 21u8,10u8,24u8,16u8,9u8,20u8,42u8,16u8,12u8,22u8,10u8,23u8,11u8,22u8,15u8,21u8,21u8,10u8,25u8,15u8, - 10u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8, - 5u8,68u8,58u8,11u8,24u8,16u8,9u8,20u8,42u8,13u8,12u8,17u8,10u8,17u8,16u8,15u8,20u8,17u8,76u8,37u8, - 4u8,220u8,1u8,40u8,17u8,76u8,10u8,15u8,22u8,11u8,17u8,15u8,15u8,21u8,5u8,222u8,1u8,11u8,17u8,1u8, - 11u8,23u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,23u8,5u8,170u8,1u8,11u8,26u8,1u8, - 11u8,25u8,1u8,2u8,39u8,1u8,4u8,2u8,8u8,13u8,5u8,10u8,11u8,0u8,17u8,57u8,12u8,2u8,10u8,2u8, - 17u8,4u8,11u8,2u8,43u8,8u8,11u8,1u8,17u8,40u8,2u8,40u8,1u8,0u8,1u8,13u8,93u8,33u8,11u8,0u8, - 16u8,0u8,20u8,12u8,2u8,10u8,2u8,17u8,5u8,10u8,2u8,42u8,13u8,12u8,4u8,10u8,4u8,16u8,13u8,56u8, - 1u8,12u8,5u8,11u8,1u8,11u8,5u8,17u8,90u8,12u8,1u8,10u8,4u8,15u8,13u8,10u8,1u8,56u8,36u8,12u8, - 3u8,10u8,4u8,15u8,4u8,11u8,3u8,56u8,4u8,11u8,4u8,15u8,30u8,11u8,2u8,11u8,1u8,18u8,9u8,56u8, - 37u8,2u8,41u8,1u8,0u8,1u8,20u8,95u8,58u8,11u8,0u8,17u8,72u8,7u8,21u8,42u8,20u8,12u8,10u8,10u8, - 10u8,15u8,1u8,12u8,3u8,11u8,10u8,15u8,22u8,12u8,6u8,10u8,1u8,65u8,5u8,12u8,5u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,40u8,10u8,4u8,10u8,5u8,35u8,4u8,51u8,5u8,22u8,10u8,1u8, - 10u8,4u8,66u8,5u8,20u8,12u8,7u8,10u8,3u8,11u8,7u8,12u8,2u8,46u8,11u8,2u8,17u8,12u8,12u8,8u8, - 14u8,8u8,56u8,3u8,4u8,46u8,10u8,3u8,14u8,8u8,56u8,38u8,20u8,56u8,33u8,12u8,9u8,10u8,6u8,11u8, - 9u8,68u8,16u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,16u8,11u8, - 1u8,1u8,11u8,6u8,1u8,11u8,3u8,1u8,2u8,42u8,1u8,4u8,2u8,13u8,16u8,97u8,67u8,10u8,1u8,17u8, - 5u8,10u8,1u8,42u8,13u8,12u8,8u8,11u8,0u8,17u8,57u8,10u8,8u8,16u8,16u8,20u8,33u8,4u8,13u8,5u8, - 18u8,11u8,8u8,1u8,7u8,7u8,17u8,86u8,39u8,10u8,1u8,41u8,16u8,4u8,22u8,5u8,27u8,11u8,8u8,1u8, - 7u8,16u8,17u8,71u8,39u8,10u8,1u8,42u8,16u8,12u8,9u8,10u8,9u8,16u8,18u8,20u8,12u8,7u8,10u8,2u8, - 12u8,5u8,11u8,3u8,17u8,81u8,12u8,4u8,11u8,5u8,14u8,4u8,17u8,82u8,12u8,6u8,13u8,6u8,46u8,56u8, - 27u8,4u8,48u8,5u8,55u8,11u8,9u8,1u8,11u8,8u8,1u8,7u8,5u8,17u8,65u8,39u8,10u8,2u8,11u8,9u8, - 15u8,18u8,21u8,11u8,8u8,15u8,31u8,11u8,1u8,11u8,7u8,11u8,2u8,18u8,11u8,56u8,39u8,2u8,43u8,1u8, - 4u8,2u8,8u8,13u8,5u8,10u8,11u8,0u8,17u8,57u8,12u8,2u8,10u8,2u8,17u8,4u8,11u8,2u8,43u8,8u8, - 11u8,1u8,17u8,44u8,2u8,44u8,1u8,0u8,1u8,13u8,98u8,14u8,11u8,0u8,16u8,0u8,20u8,12u8,2u8,10u8, - 2u8,17u8,5u8,11u8,2u8,42u8,13u8,12u8,3u8,11u8,1u8,11u8,3u8,15u8,14u8,21u8,2u8,45u8,1u8,4u8, - 2u8,8u8,13u8,5u8,10u8,11u8,0u8,17u8,57u8,12u8,2u8,10u8,2u8,17u8,4u8,11u8,2u8,43u8,8u8,11u8, - 1u8,17u8,46u8,2u8,46u8,1u8,0u8,1u8,13u8,99u8,25u8,11u8,0u8,16u8,0u8,20u8,12u8,3u8,10u8,3u8, - 17u8,5u8,10u8,3u8,42u8,13u8,12u8,4u8,10u8,4u8,16u8,16u8,20u8,12u8,2u8,10u8,1u8,10u8,4u8,15u8, - 16u8,21u8,11u8,4u8,15u8,32u8,11u8,3u8,11u8,2u8,11u8,1u8,18u8,12u8,56u8,40u8,2u8,47u8,1u8,0u8, - 0u8,1u8,3u8,11u8,0u8,41u8,13u8,2u8,48u8,3u8,0u8,0u8,1u8,7u8,10u8,0u8,17u8,72u8,11u8,0u8, - 11u8,1u8,18u8,2u8,45u8,2u8,2u8,49u8,1u8,4u8,2u8,8u8,13u8,100u8,12u8,11u8,0u8,17u8,57u8,12u8, - 2u8,10u8,2u8,17u8,4u8,11u8,2u8,43u8,8u8,12u8,3u8,11u8,1u8,11u8,3u8,17u8,50u8,2u8,50u8,1u8, - 0u8,1u8,13u8,101u8,38u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,11u8, - 1u8,1u8,2u8,11u8,1u8,16u8,0u8,20u8,12u8,3u8,10u8,3u8,17u8,5u8,10u8,3u8,42u8,13u8,12u8,4u8, - 11u8,0u8,10u8,4u8,16u8,4u8,56u8,1u8,17u8,90u8,12u8,2u8,10u8,4u8,15u8,4u8,10u8,2u8,56u8,36u8, - 12u8,5u8,10u8,4u8,15u8,13u8,11u8,5u8,56u8,4u8,11u8,4u8,15u8,33u8,11u8,3u8,11u8,2u8,18u8,14u8, - 56u8,41u8,2u8,51u8,1u8,4u8,2u8,13u8,16u8,102u8,56u8,10u8,1u8,17u8,5u8,10u8,1u8,42u8,13u8,12u8, - 6u8,11u8,0u8,17u8,57u8,10u8,6u8,16u8,16u8,20u8,33u8,4u8,13u8,5u8,18u8,11u8,6u8,1u8,7u8,7u8, - 17u8,86u8,39u8,10u8,1u8,41u8,16u8,4u8,22u8,5u8,27u8,11u8,6u8,1u8,7u8,16u8,17u8,71u8,39u8,10u8, - 1u8,42u8,16u8,12u8,7u8,10u8,7u8,16u8,19u8,20u8,12u8,5u8,10u8,2u8,10u8,7u8,15u8,19u8,21u8,10u8, - 7u8,16u8,20u8,20u8,12u8,4u8,10u8,3u8,11u8,7u8,15u8,20u8,21u8,11u8,6u8,15u8,34u8,11u8,1u8,11u8, - 5u8,11u8,2u8,11u8,4u8,11u8,3u8,18u8,15u8,56u8,42u8,2u8,52u8,3u8,0u8,1u8,19u8,103u8,73u8,7u8, - 21u8,42u8,19u8,12u8,9u8,10u8,9u8,16u8,10u8,65u8,58u8,12u8,8u8,14u8,0u8,56u8,3u8,4u8,31u8,13u8, - 0u8,56u8,32u8,12u8,2u8,10u8,2u8,10u8,8u8,35u8,4u8,31u8,10u8,9u8,15u8,10u8,11u8,2u8,67u8,58u8, - 12u8,5u8,40u8,10u8,5u8,16u8,11u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,5u8, - 15u8,11u8,21u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,14u8,1u8,65u8,7u8,12u8,4u8, - 40u8,10u8,3u8,10u8,4u8,35u8,4u8,70u8,5u8,42u8,14u8,1u8,10u8,3u8,66u8,7u8,20u8,12u8,7u8,10u8, - 7u8,10u8,8u8,35u8,4u8,65u8,10u8,9u8,15u8,10u8,11u8,7u8,67u8,58u8,12u8,6u8,40u8,10u8,6u8,16u8, - 12u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,11u8,6u8,15u8,12u8,21u8,11u8,3u8,6u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,36u8,11u8,9u8,1u8,2u8,53u8,0u8,0u8, - 4u8,2u8,13u8,16u8,17u8,104u8,102u8,10u8,1u8,42u8,13u8,12u8,15u8,10u8,1u8,43u8,16u8,12u8,16u8,11u8, - 0u8,16u8,10u8,11u8,16u8,16u8,21u8,20u8,66u8,58u8,12u8,5u8,10u8,5u8,16u8,11u8,20u8,12u8,8u8,40u8, - 10u8,5u8,16u8,11u8,20u8,11u8,5u8,16u8,12u8,20u8,22u8,12u8,9u8,11u8,2u8,17u8,93u8,12u8,14u8,12u8, - 13u8,10u8,15u8,15u8,4u8,10u8,8u8,10u8,9u8,10u8,13u8,10u8,14u8,17u8,10u8,12u8,10u8,10u8,15u8,15u8, - 13u8,11u8,8u8,11u8,9u8,11u8,13u8,11u8,14u8,17u8,10u8,12u8,12u8,40u8,11u8,10u8,11u8,12u8,22u8,12u8, - 11u8,10u8,15u8,15u8,4u8,10u8,15u8,15u8,3u8,56u8,43u8,56u8,4u8,17u8,95u8,4u8,81u8,7u8,21u8,42u8, - 17u8,15u8,6u8,12u8,7u8,10u8,7u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8,6u8,4u8,79u8,11u8,7u8, - 10u8,1u8,56u8,44u8,12u8,4u8,10u8,15u8,15u8,4u8,11u8,4u8,56u8,4u8,5u8,81u8,11u8,7u8,1u8,10u8, - 15u8,16u8,15u8,20u8,12u8,6u8,17u8,76u8,11u8,6u8,38u8,4u8,95u8,10u8,15u8,15u8,17u8,10u8,15u8,15u8, - 13u8,56u8,43u8,56u8,4u8,11u8,15u8,15u8,35u8,11u8,1u8,11u8,11u8,18u8,3u8,56u8,45u8,2u8,54u8,0u8, - 0u8,1u8,20u8,107u8,44u8,7u8,21u8,42u8,20u8,12u8,2u8,17u8,63u8,12u8,1u8,14u8,1u8,17u8,97u8,53u8, - 12u8,3u8,10u8,2u8,16u8,25u8,20u8,11u8,0u8,53u8,22u8,10u8,2u8,15u8,25u8,21u8,10u8,2u8,16u8,28u8, - 20u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8, - 41u8,10u8,2u8,16u8,25u8,20u8,11u8,2u8,16u8,28u8,20u8,11u8,3u8,24u8,50u8,100u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,37u8,4u8,37u8,5u8,40u8,7u8,18u8,17u8, - 65u8,39u8,5u8,43u8,11u8,2u8,1u8,2u8,55u8,1u8,4u8,3u8,8u8,13u8,20u8,108u8,14u8,11u8,0u8,17u8, - 57u8,12u8,3u8,10u8,3u8,17u8,4u8,10u8,3u8,43u8,8u8,11u8,1u8,17u8,56u8,12u8,2u8,11u8,3u8,11u8, - 2u8,56u8,46u8,2u8,56u8,1u8,0u8,2u8,13u8,20u8,110u8,58u8,11u8,0u8,16u8,0u8,20u8,12u8,4u8,10u8, - 4u8,17u8,5u8,10u8,4u8,42u8,13u8,12u8,5u8,10u8,4u8,17u8,25u8,7u8,0u8,33u8,4u8,21u8,17u8,76u8, - 10u8,5u8,16u8,15u8,20u8,38u8,12u8,2u8,5u8,23u8,9u8,12u8,2u8,11u8,2u8,4u8,33u8,10u8,5u8,15u8, - 13u8,56u8,43u8,12u8,3u8,10u8,5u8,15u8,17u8,11u8,3u8,56u8,4u8,11u8,1u8,10u8,5u8,16u8,17u8,56u8, - 1u8,17u8,90u8,12u8,1u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,47u8,11u8, - 5u8,1u8,56u8,14u8,2u8,10u8,5u8,15u8,36u8,11u8,4u8,10u8,1u8,18u8,21u8,56u8,47u8,11u8,5u8,15u8, - 17u8,11u8,1u8,56u8,36u8,2u8,8u8,0u8,20u8,1u8,20u8,3u8,13u8,2u8,13u8,0u8,13u8,9u8,17u8,0u8, - 1u8,0u8,2u8,0u8,18u8,0u8,19u8,0u8,5u8,0u8,5u8,1u8,13u8,3u8,13u8,6u8,13u8,4u8,13u8,5u8, - 13u8,1u8,16u8,0u8,16u8,1u8,16u8,2u8,16u8,3u8,20u8,2u8,13u8,13u8,13u8,14u8,20u8,5u8,13u8,18u8, - 18u8,1u8,20u8,4u8,18u8,2u8,13u8,10u8,13u8,11u8,13u8,8u8,13u8,16u8,13u8,12u8,13u8,15u8,13u8,17u8, - 0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8, - ]; - vector::push_back(&mut code, chunk17); - let chunk18 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,14u8,2u8,14u8,36u8,3u8,50u8,148u8,1u8, - 4u8,198u8,1u8,26u8,5u8,224u8,1u8,172u8,1u8,7u8,140u8,3u8,243u8,5u8,8u8,255u8,8u8,32u8,6u8,159u8, - 9u8,88u8,16u8,247u8,9u8,255u8,1u8,10u8,246u8,11u8,25u8,12u8,143u8,12u8,162u8,3u8,13u8,177u8,15u8,8u8, - 15u8,185u8,15u8,8u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8, - 8u8,0u8,0u8,12u8,8u8,0u8,2u8,13u8,4u8,1u8,0u8,1u8,1u8,14u8,8u8,0u8,2u8,22u8,5u8,1u8, - 0u8,1u8,2u8,27u8,4u8,1u8,0u8,1u8,4u8,29u8,7u8,1u8,0u8,0u8,0u8,15u8,0u8,1u8,0u8,0u8, - 16u8,2u8,1u8,0u8,0u8,17u8,2u8,1u8,0u8,0u8,18u8,3u8,1u8,0u8,0u8,19u8,1u8,4u8,0u8,0u8, - 20u8,1u8,1u8,0u8,0u8,21u8,5u8,1u8,0u8,0u8,23u8,6u8,1u8,0u8,0u8,24u8,3u8,1u8,0u8,3u8, - 31u8,8u8,8u8,0u8,2u8,32u8,10u8,8u8,1u8,0u8,2u8,33u8,11u8,12u8,1u8,0u8,2u8,34u8,13u8,1u8, - 1u8,0u8,2u8,35u8,14u8,1u8,1u8,0u8,2u8,36u8,16u8,1u8,1u8,0u8,6u8,37u8,18u8,1u8,0u8,3u8, - 38u8,8u8,8u8,0u8,5u8,39u8,18u8,1u8,0u8,2u8,40u8,18u8,19u8,1u8,0u8,4u8,41u8,1u8,20u8,1u8, - 0u8,2u8,42u8,22u8,4u8,1u8,0u8,4u8,43u8,23u8,4u8,1u8,0u8,4u8,33u8,24u8,25u8,1u8,0u8,2u8, - 44u8,26u8,12u8,1u8,0u8,2u8,45u8,12u8,1u8,1u8,0u8,5u8,46u8,27u8,1u8,0u8,4u8,47u8,28u8,20u8, - 1u8,0u8,10u8,9u8,11u8,9u8,12u8,9u8,13u8,9u8,14u8,9u8,18u8,9u8,19u8,5u8,20u8,9u8,21u8,5u8, - 22u8,5u8,23u8,9u8,24u8,9u8,26u8,5u8,2u8,7u8,11u8,2u8,1u8,8u8,3u8,2u8,0u8,2u8,5u8,3u8, - 2u8,6u8,12u8,2u8,1u8,1u8,1u8,5u8,2u8,6u8,12u8,11u8,4u8,1u8,8u8,3u8,2u8,3u8,3u8,1u8, - 3u8,1u8,8u8,3u8,1u8,6u8,11u8,2u8,1u8,9u8,0u8,2u8,7u8,11u8,2u8,1u8,9u8,0u8,3u8,1u8, - 11u8,2u8,1u8,9u8,0u8,2u8,11u8,2u8,1u8,9u8,0u8,6u8,11u8,4u8,1u8,9u8,0u8,3u8,5u8,3u8, - 6u8,11u8,4u8,1u8,9u8,0u8,1u8,7u8,11u8,5u8,1u8,8u8,3u8,3u8,5u8,3u8,7u8,11u8,5u8,1u8, - 9u8,0u8,1u8,8u8,1u8,1u8,6u8,12u8,1u8,11u8,5u8,1u8,9u8,0u8,1u8,11u8,6u8,1u8,9u8,0u8, - 3u8,11u8,2u8,1u8,8u8,3u8,7u8,8u8,1u8,5u8,1u8,6u8,11u8,5u8,1u8,9u8,0u8,1u8,6u8,11u8, - 6u8,1u8,9u8,0u8,1u8,7u8,11u8,6u8,1u8,9u8,0u8,1u8,9u8,0u8,1u8,7u8,11u8,5u8,1u8,9u8, - 0u8,2u8,5u8,11u8,2u8,1u8,8u8,3u8,2u8,7u8,11u8,6u8,1u8,9u8,0u8,9u8,0u8,1u8,7u8,2u8, - 5u8,98u8,108u8,111u8,99u8,107u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,15u8,114u8,101u8,99u8,111u8,110u8, - 102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,22u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8, - 111u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8, - 99u8,116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8, - 4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,5u8,115u8, - 116u8,97u8,107u8,101u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8, - 115u8,21u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 105u8,101u8,115u8,21u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,101u8,100u8,70u8,101u8,101u8,115u8,80u8,101u8,114u8, - 66u8,108u8,111u8,99u8,107u8,4u8,67u8,111u8,105u8,110u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8, - 18u8,98u8,117u8,114u8,110u8,95u8,99u8,111u8,105u8,110u8,95u8,102u8,114u8,97u8,99u8,116u8,105u8,111u8,110u8,8u8, - 98u8,117u8,114u8,110u8,95u8,102u8,101u8,101u8,11u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,102u8,101u8,101u8, - 42u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,102u8,101u8,101u8,95u8,99u8,111u8,108u8,108u8, - 101u8,99u8,116u8,105u8,111u8,110u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8, - 105u8,111u8,110u8,26u8,105u8,115u8,95u8,102u8,101u8,101u8,115u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8, - 111u8,110u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,22u8,112u8,114u8,111u8,99u8,101u8,115u8,115u8,95u8,99u8, - 111u8,108u8,108u8,101u8,99u8,116u8,101u8,100u8,95u8,102u8,101u8,101u8,115u8,36u8,114u8,101u8,103u8,105u8,115u8,116u8, - 101u8,114u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,95u8,102u8,111u8,114u8,95u8,102u8,101u8,101u8,95u8, - 99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,14u8,66u8,117u8,114u8,110u8,67u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,25u8,115u8,116u8,111u8,114u8,101u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8, - 105u8,110u8,95u8,98u8,117u8,114u8,110u8,95u8,99u8,97u8,112u8,23u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,95u8, - 98u8,117u8,114u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,8u8,98u8,117u8,114u8,110u8, - 95u8,99u8,97u8,112u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,16u8,65u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8, - 97u8,98u8,108u8,101u8,67u8,111u8,105u8,110u8,8u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,6u8,79u8,112u8, - 116u8,105u8,111u8,110u8,15u8,98u8,117u8,114u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8, - 12u8,111u8,117u8,116u8,95u8,111u8,102u8,95u8,114u8,97u8,110u8,103u8,101u8,5u8,118u8,97u8,108u8,117u8,101u8,7u8, - 101u8,120u8,116u8,114u8,97u8,99u8,116u8,4u8,98u8,117u8,114u8,110u8,9u8,98u8,117u8,114u8,110u8,95u8,102u8,114u8, - 111u8,109u8,30u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,105u8,110u8,116u8,111u8,95u8,97u8,103u8,103u8,114u8, - 101u8,103u8,97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8, - 95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,14u8,97u8,108u8,114u8, - 101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,25u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,102u8,101u8,101u8,115u8,28u8,105u8,110u8, - 105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,97u8,98u8,108u8, - 101u8,95u8,99u8,111u8,105u8,110u8,4u8,110u8,111u8,110u8,101u8,25u8,105u8,115u8,95u8,97u8,103u8,103u8,114u8,101u8, - 103u8,97u8,116u8,97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8,122u8,101u8,114u8,111u8,7u8,105u8,115u8, - 95u8,115u8,111u8,109u8,101u8,23u8,100u8,114u8,97u8,105u8,110u8,95u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8, - 97u8,98u8,108u8,101u8,95u8,99u8,111u8,105u8,110u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,122u8,101u8, - 114u8,111u8,19u8,97u8,100u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8, - 101u8,101u8,12u8,115u8,119u8,97u8,112u8,95u8,111u8,114u8,95u8,102u8,105u8,108u8,108u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8, - 115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,234u8,1u8,2u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,24u8,69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,67u8,79u8,76u8,76u8,69u8, - 67u8,84u8,73u8,78u8,71u8,95u8,70u8,69u8,69u8,83u8,118u8,71u8,97u8,115u8,32u8,102u8,101u8,101u8,115u8,32u8, - 97u8,114u8,101u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,98u8,101u8,105u8,110u8,103u8,32u8,99u8,111u8, - 108u8,108u8,101u8,99u8,116u8,101u8,100u8,32u8,97u8,110u8,100u8,32u8,116u8,104u8,101u8,32u8,115u8,116u8,114u8,117u8, - 99u8,116u8,32u8,104u8,111u8,108u8,100u8,105u8,110u8,103u8,10u8,32u8,105u8,110u8,102u8,111u8,114u8,109u8,97u8,116u8, - 105u8,111u8,110u8,32u8,97u8,98u8,111u8,117u8,116u8,32u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,101u8,100u8,32u8, - 97u8,109u8,111u8,117u8,110u8,116u8,115u8,32u8,105u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,112u8, - 117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,73u8, - 78u8,86u8,65u8,76u8,73u8,68u8,95u8,66u8,85u8,82u8,78u8,95u8,80u8,69u8,82u8,67u8,69u8,78u8,84u8,65u8, - 71u8,69u8,45u8,84u8,104u8,101u8,32u8,98u8,117u8,114u8,110u8,32u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8, - 103u8,101u8,32u8,105u8,115u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8,32u8,114u8,97u8,110u8,103u8,101u8,32u8,91u8, - 48u8,44u8,32u8,49u8,48u8,48u8,93u8,46u8,0u8,0u8,0u8,2u8,1u8,25u8,11u8,4u8,1u8,8u8,3u8,1u8, - 2u8,3u8,26u8,11u8,5u8,1u8,8u8,3u8,28u8,11u8,6u8,1u8,5u8,30u8,2u8,0u8,0u8,0u8,1u8,0u8, - 7u8,37u8,10u8,1u8,49u8,100u8,37u8,4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,1u8,17u8,9u8,39u8,10u8, - 0u8,46u8,56u8,0u8,12u8,3u8,40u8,11u8,1u8,52u8,11u8,3u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,26u8,12u8,2u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,34u8,11u8, - 0u8,11u8,2u8,56u8,1u8,7u8,2u8,43u8,0u8,16u8,0u8,56u8,2u8,5u8,36u8,11u8,0u8,1u8,2u8,1u8, - 3u8,0u8,1u8,0u8,1u8,7u8,11u8,0u8,11u8,1u8,7u8,2u8,43u8,0u8,16u8,0u8,56u8,3u8,2u8,2u8, - 3u8,0u8,1u8,1u8,15u8,9u8,7u8,2u8,42u8,1u8,15u8,1u8,12u8,2u8,11u8,0u8,11u8,1u8,11u8,2u8, - 56u8,4u8,2u8,3u8,1u8,0u8,0u8,17u8,34u8,10u8,0u8,17u8,15u8,7u8,2u8,41u8,1u8,32u8,4u8,7u8, - 5u8,12u8,11u8,0u8,1u8,7u8,0u8,17u8,16u8,39u8,10u8,1u8,49u8,100u8,37u8,4u8,17u8,5u8,22u8,11u8, - 0u8,1u8,7u8,1u8,17u8,9u8,39u8,10u8,0u8,17u8,17u8,10u8,0u8,56u8,5u8,56u8,6u8,11u8,1u8,18u8, - 1u8,12u8,2u8,11u8,0u8,11u8,2u8,45u8,1u8,2u8,4u8,0u8,0u8,0u8,1u8,3u8,7u8,2u8,41u8,1u8, - 2u8,5u8,3u8,0u8,2u8,0u8,1u8,21u8,64u8,17u8,4u8,32u8,4u8,4u8,2u8,7u8,2u8,42u8,1u8,12u8, - 1u8,10u8,1u8,16u8,1u8,56u8,7u8,4u8,23u8,10u8,1u8,16u8,2u8,56u8,8u8,4u8,20u8,11u8,1u8,15u8, - 2u8,56u8,9u8,1u8,5u8,22u8,11u8,1u8,1u8,2u8,10u8,1u8,15u8,1u8,56u8,10u8,12u8,0u8,10u8,1u8, - 16u8,2u8,56u8,8u8,4u8,56u8,10u8,1u8,15u8,2u8,56u8,9u8,12u8,2u8,10u8,2u8,7u8,3u8,33u8,4u8, - 47u8,11u8,1u8,1u8,13u8,0u8,49u8,100u8,17u8,0u8,11u8,0u8,56u8,11u8,2u8,13u8,0u8,11u8,1u8,16u8, - 3u8,20u8,17u8,0u8,11u8,2u8,11u8,0u8,17u8,25u8,2u8,11u8,1u8,1u8,13u8,0u8,49u8,100u8,17u8,0u8, - 11u8,0u8,56u8,11u8,2u8,6u8,3u8,0u8,1u8,1u8,1u8,9u8,17u8,4u8,4u8,8u8,7u8,2u8,42u8,1u8, - 15u8,2u8,11u8,0u8,56u8,12u8,1u8,2u8,7u8,3u8,0u8,0u8,1u8,7u8,10u8,0u8,17u8,15u8,11u8,0u8, - 11u8,1u8,18u8,0u8,45u8,0u8,2u8,8u8,1u8,0u8,2u8,0u8,1u8,29u8,21u8,11u8,0u8,17u8,15u8,10u8, - 1u8,49u8,100u8,37u8,4u8,7u8,5u8,10u8,7u8,1u8,17u8,9u8,39u8,17u8,5u8,17u8,4u8,4u8,20u8,7u8, - 2u8,42u8,1u8,15u8,3u8,12u8,2u8,11u8,1u8,11u8,2u8,21u8,2u8,0u8,0u8,1u8,0u8,1u8,1u8,1u8, - 2u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8, - ]; - vector::push_back(&mut code, chunk18); - let chunk19 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,6u8,2u8,6u8,12u8,3u8,18u8,40u8,5u8, - 58u8,20u8,7u8,78u8,168u8,2u8,8u8,246u8,2u8,32u8,6u8,150u8,3u8,44u8,16u8,194u8,3u8,53u8,10u8,247u8, - 3u8,21u8,12u8,140u8,4u8,180u8,1u8,13u8,192u8,5u8,8u8,15u8,200u8,5u8,8u8,0u8,4u8,0u8,5u8,0u8, - 6u8,0u8,7u8,12u8,0u8,0u8,8u8,12u8,0u8,0u8,9u8,7u8,0u8,0u8,10u8,0u8,1u8,0u8,0u8,11u8, - 0u8,2u8,0u8,0u8,12u8,3u8,0u8,0u8,0u8,13u8,4u8,0u8,0u8,0u8,14u8,0u8,0u8,0u8,1u8,19u8, - 4u8,4u8,0u8,2u8,20u8,3u8,0u8,0u8,1u8,21u8,4u8,4u8,0u8,0u8,2u8,3u8,3u8,1u8,8u8,2u8, - 1u8,6u8,12u8,1u8,3u8,1u8,6u8,8u8,1u8,1u8,7u8,8u8,1u8,5u8,98u8,108u8,111u8,99u8,107u8,7u8, - 103u8,101u8,110u8,101u8,115u8,105u8,115u8,15u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8, - 105u8,111u8,110u8,11u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,95u8,103u8,97u8,115u8,13u8,115u8,116u8,97u8,116u8, - 101u8,95u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,5u8,101u8,114u8,114u8,111u8,114u8,16u8,115u8,121u8,115u8,116u8, - 101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,12u8,71u8,97u8,115u8,80u8,97u8,114u8,97u8, - 109u8,101u8,116u8,101u8,114u8,17u8,83u8,116u8,97u8,116u8,101u8,83u8,116u8,111u8,114u8,97u8,103u8,101u8,85u8,115u8, - 97u8,103u8,101u8,5u8,85u8,115u8,97u8,103u8,101u8,23u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,105u8,116u8, - 101u8,109u8,115u8,95u8,97u8,110u8,100u8,95u8,98u8,121u8,116u8,101u8,115u8,47u8,103u8,101u8,116u8,95u8,115u8,116u8, - 97u8,116u8,101u8,95u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,95u8,117u8,115u8,97u8,103u8,101u8,95u8,111u8,110u8, - 108u8,121u8,95u8,97u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,98u8,101u8,103u8,105u8,110u8,110u8,105u8,110u8, - 103u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,12u8,111u8,110u8,95u8,110u8,101u8,119u8,95u8, - 98u8,108u8,111u8,99u8,107u8,11u8,111u8,110u8,95u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,5u8,117u8,115u8, - 97u8,103u8,101u8,5u8,101u8,112u8,111u8,99u8,104u8,5u8,105u8,116u8,101u8,109u8,115u8,5u8,98u8,121u8,116u8,101u8, - 115u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8, - 112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,14u8,97u8,108u8,114u8,101u8,97u8, - 100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,33u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,83u8,84u8,65u8,84u8, - 69u8,95u8,83u8,84u8,79u8,82u8,65u8,71u8,69u8,95u8,85u8,83u8,65u8,71u8,69u8,0u8,0u8,0u8,0u8,2u8, - 1u8,15u8,8u8,2u8,1u8,2u8,2u8,16u8,3u8,15u8,8u8,2u8,2u8,2u8,2u8,17u8,3u8,18u8,3u8,0u8, - 3u8,0u8,1u8,1u8,5u8,19u8,7u8,1u8,41u8,1u8,4u8,4u8,5u8,7u8,7u8,0u8,17u8,5u8,39u8,7u8, - 1u8,43u8,1u8,12u8,0u8,10u8,0u8,16u8,0u8,16u8,1u8,20u8,11u8,0u8,16u8,0u8,16u8,2u8,20u8,2u8, - 1u8,0u8,2u8,0u8,2u8,3u8,0u8,0u8,0u8,20u8,10u8,0u8,17u8,6u8,7u8,1u8,41u8,1u8,32u8,4u8, - 7u8,5u8,12u8,11u8,0u8,1u8,7u8,0u8,17u8,7u8,39u8,11u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 18u8,2u8,18u8,1u8,45u8,1u8,2u8,3u8,3u8,0u8,1u8,1u8,6u8,28u8,7u8,1u8,41u8,1u8,4u8,4u8, - 5u8,7u8,7u8,0u8,17u8,5u8,39u8,7u8,1u8,42u8,1u8,12u8,1u8,10u8,0u8,10u8,1u8,16u8,3u8,20u8, - 34u8,4u8,25u8,11u8,0u8,10u8,1u8,15u8,3u8,21u8,17u8,1u8,11u8,1u8,15u8,0u8,21u8,5u8,27u8,11u8, - 1u8,1u8,2u8,4u8,3u8,0u8,0u8,0u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,39u8,1u8, - 1u8,2u8,0u8,2u8,1u8,1u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8, - ]; - vector::push_back(&mut code, chunk19); - let chunk20 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,8u8,2u8,8u8,20u8,3u8,28u8,95u8,5u8, - 123u8,156u8,1u8,7u8,151u8,2u8,143u8,5u8,8u8,166u8,7u8,32u8,6u8,198u8,7u8,124u8,16u8,194u8,8u8,245u8, - 1u8,10u8,183u8,10u8,56u8,12u8,239u8,10u8,130u8,13u8,13u8,241u8,23u8,34u8,15u8,147u8,24u8,6u8,0u8,3u8, - 0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,7u8,0u8,0u8,8u8,7u8,0u8,0u8,9u8,8u8,0u8,0u8,10u8, - 11u8,0u8,0u8,11u8,7u8,0u8,0u8,12u8,0u8,1u8,0u8,0u8,13u8,2u8,3u8,0u8,0u8,14u8,4u8,3u8, - 0u8,0u8,15u8,2u8,3u8,0u8,0u8,16u8,2u8,3u8,0u8,0u8,17u8,5u8,6u8,0u8,0u8,18u8,7u8,3u8, - 0u8,0u8,19u8,8u8,1u8,0u8,0u8,20u8,0u8,9u8,0u8,0u8,21u8,10u8,11u8,0u8,0u8,22u8,12u8,13u8, - 0u8,0u8,23u8,6u8,6u8,0u8,0u8,24u8,14u8,6u8,0u8,0u8,25u8,15u8,6u8,0u8,3u8,43u8,5u8,6u8, - 0u8,1u8,44u8,3u8,3u8,0u8,1u8,45u8,3u8,3u8,0u8,1u8,46u8,3u8,3u8,0u8,2u8,47u8,6u8,0u8, - 0u8,2u8,3u8,3u8,1u8,8u8,0u8,2u8,6u8,8u8,4u8,3u8,1u8,3u8,3u8,3u8,3u8,6u8,8u8,0u8, - 1u8,6u8,12u8,0u8,5u8,3u8,3u8,3u8,3u8,3u8,3u8,3u8,3u8,10u8,8u8,1u8,1u8,8u8,1u8,2u8, - 8u8,4u8,8u8,4u8,1u8,8u8,3u8,4u8,3u8,8u8,0u8,8u8,0u8,8u8,0u8,1u8,8u8,4u8,2u8,6u8, - 12u8,8u8,3u8,1u8,6u8,10u8,8u8,1u8,23u8,3u8,6u8,8u8,1u8,6u8,8u8,1u8,6u8,8u8,1u8,6u8, - 8u8,1u8,8u8,1u8,8u8,1u8,6u8,8u8,1u8,8u8,1u8,8u8,1u8,6u8,8u8,1u8,6u8,8u8,1u8,6u8, - 8u8,1u8,3u8,3u8,3u8,3u8,6u8,8u8,1u8,3u8,3u8,6u8,10u8,8u8,1u8,6u8,8u8,1u8,3u8,4u8, - 8u8,4u8,8u8,4u8,3u8,3u8,1u8,1u8,4u8,3u8,7u8,8u8,2u8,6u8,8u8,3u8,3u8,9u8,8u8,1u8, - 6u8,8u8,1u8,8u8,1u8,6u8,8u8,1u8,1u8,6u8,8u8,1u8,3u8,3u8,6u8,8u8,1u8,12u8,103u8,97u8, - 115u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,15u8,114u8, - 101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,11u8,115u8,116u8,111u8,114u8,97u8, - 103u8,101u8,95u8,103u8,97u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,13u8,115u8,116u8,97u8,116u8,101u8,95u8,115u8, - 116u8,111u8,114u8,97u8,103u8,101u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,101u8,115u8,8u8,71u8,97u8,115u8,67u8,117u8,114u8,118u8,101u8,5u8,80u8,111u8,105u8,110u8,116u8,10u8,83u8, - 116u8,111u8,114u8,97u8,103u8,101u8,71u8,97u8,115u8,16u8,83u8,116u8,111u8,114u8,97u8,103u8,101u8,71u8,97u8,115u8, - 67u8,111u8,110u8,102u8,105u8,103u8,14u8,85u8,115u8,97u8,103u8,101u8,71u8,97u8,115u8,67u8,111u8,110u8,102u8,105u8, - 103u8,27u8,98u8,97u8,115u8,101u8,95u8,56u8,49u8,57u8,50u8,95u8,101u8,120u8,112u8,111u8,110u8,101u8,110u8,116u8, - 105u8,97u8,108u8,95u8,99u8,117u8,114u8,118u8,101u8,20u8,99u8,97u8,108u8,99u8,117u8,108u8,97u8,116u8,101u8,95u8, - 99u8,114u8,101u8,97u8,116u8,101u8,95u8,103u8,97u8,115u8,13u8,99u8,97u8,108u8,99u8,117u8,108u8,97u8,116u8,101u8, - 95u8,103u8,97u8,115u8,18u8,99u8,97u8,108u8,99u8,117u8,108u8,97u8,116u8,101u8,95u8,114u8,101u8,97u8,100u8,95u8, - 103u8,97u8,115u8,19u8,99u8,97u8,108u8,99u8,117u8,108u8,97u8,116u8,101u8,95u8,119u8,114u8,105u8,116u8,101u8,95u8, - 103u8,97u8,115u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,11u8,105u8,110u8,116u8,101u8,114u8, - 112u8,111u8,108u8,97u8,116u8,101u8,13u8,110u8,101u8,119u8,95u8,103u8,97u8,115u8,95u8,99u8,117u8,114u8,118u8,101u8, - 9u8,110u8,101u8,119u8,95u8,112u8,111u8,105u8,110u8,116u8,22u8,110u8,101u8,119u8,95u8,115u8,116u8,111u8,114u8,97u8, - 103u8,101u8,95u8,103u8,97u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,20u8,110u8,101u8,119u8,95u8,117u8,115u8, - 97u8,103u8,101u8,95u8,103u8,97u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,11u8,111u8,110u8,95u8,114u8,101u8, - 99u8,111u8,110u8,102u8,105u8,103u8,10u8,115u8,101u8,116u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,15u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,101u8,95u8,112u8,111u8,105u8,110u8,116u8,115u8,7u8,109u8,105u8,110u8,95u8,103u8,97u8, - 115u8,7u8,109u8,97u8,120u8,95u8,103u8,97u8,115u8,6u8,112u8,111u8,105u8,110u8,116u8,115u8,1u8,120u8,1u8,121u8, - 13u8,112u8,101u8,114u8,95u8,105u8,116u8,101u8,109u8,95u8,114u8,101u8,97u8,100u8,15u8,112u8,101u8,114u8,95u8,105u8, - 116u8,101u8,109u8,95u8,99u8,114u8,101u8,97u8,116u8,101u8,14u8,112u8,101u8,114u8,95u8,105u8,116u8,101u8,109u8,95u8, - 119u8,114u8,105u8,116u8,101u8,13u8,112u8,101u8,114u8,95u8,98u8,121u8,116u8,101u8,95u8,114u8,101u8,97u8,100u8,15u8, - 112u8,101u8,114u8,95u8,98u8,121u8,116u8,101u8,95u8,99u8,114u8,101u8,97u8,116u8,101u8,14u8,112u8,101u8,114u8,95u8, - 98u8,121u8,116u8,101u8,95u8,119u8,114u8,105u8,116u8,101u8,11u8,105u8,116u8,101u8,109u8,95u8,99u8,111u8,110u8,102u8, - 105u8,103u8,11u8,98u8,121u8,116u8,101u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,12u8,116u8,97u8,114u8,103u8,101u8, - 116u8,95u8,117u8,115u8,97u8,103u8,101u8,10u8,114u8,101u8,97u8,100u8,95u8,99u8,117u8,114u8,118u8,101u8,12u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,99u8,117u8,114u8,118u8,101u8,11u8,119u8,114u8,105u8,116u8,101u8,95u8,99u8,117u8, - 114u8,118u8,101u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8, - 109u8,101u8,119u8,111u8,114u8,107u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8, - 115u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,9u8,110u8, - 111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,23u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,105u8,116u8,101u8, - 109u8,115u8,95u8,97u8,110u8,100u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,3u8,8u8,16u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,5u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8, - 100u8,97u8,116u8,97u8,95u8,118u8,49u8,224u8,1u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8, - 83u8,84u8,79u8,82u8,65u8,71u8,69u8,95u8,71u8,65u8,83u8,95u8,67u8,79u8,78u8,70u8,73u8,71u8,0u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,69u8,83u8,84u8,79u8,82u8,65u8,71u8,69u8,95u8,71u8,65u8,83u8, - 0u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,71u8, - 65u8,83u8,95u8,82u8,65u8,78u8,71u8,69u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,90u8, - 69u8,82u8,79u8,95u8,84u8,65u8,82u8,71u8,69u8,84u8,95u8,85u8,83u8,65u8,71u8,69u8,0u8,4u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,21u8,69u8,84u8,65u8,82u8,71u8,69u8,84u8,95u8,85u8,83u8,65u8,71u8,69u8,95u8, - 84u8,79u8,79u8,95u8,66u8,73u8,71u8,0u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,43u8,69u8,73u8,78u8, - 86u8,65u8,76u8,73u8,68u8,95u8,77u8,79u8,78u8,79u8,84u8,79u8,78u8,73u8,67u8,65u8,76u8,76u8,89u8,95u8, - 78u8,79u8,78u8,95u8,68u8,69u8,67u8,82u8,69u8,65u8,83u8,73u8,78u8,71u8,95u8,67u8,85u8,82u8,86u8,69u8, - 0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,80u8, - 79u8,73u8,78u8,84u8,95u8,82u8,65u8,78u8,71u8,69u8,0u8,0u8,0u8,0u8,2u8,3u8,26u8,3u8,27u8,3u8, - 28u8,10u8,8u8,1u8,1u8,2u8,2u8,29u8,3u8,30u8,3u8,2u8,2u8,6u8,31u8,3u8,32u8,3u8,33u8,3u8, - 34u8,3u8,35u8,3u8,36u8,3u8,3u8,2u8,2u8,37u8,8u8,4u8,38u8,8u8,4u8,4u8,2u8,4u8,39u8,3u8, - 40u8,8u8,0u8,41u8,8u8,0u8,42u8,8u8,0u8,0u8,1u8,0u8,0u8,6u8,38u8,11u8,0u8,11u8,1u8,6u8, - 232u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 208u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 184u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 160u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,44u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 136u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,109u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 112u8,23u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,15u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 88u8,27u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,157u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 64u8,31u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,112u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 40u8,35u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,221u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 28u8,37u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,228u8,24u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,6u8, - 172u8,38u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,178u8,35u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,8u8,64u8, - 9u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,7u8,2u8,1u8,0u8,0u8,0u8,6u8,8u8,10u8,0u8, - 16u8,0u8,20u8,11u8,1u8,11u8,0u8,16u8,1u8,17u8,2u8,2u8,2u8,0u8,0u8,0u8,16u8,174u8,1u8,10u8, - 1u8,10u8,0u8,36u8,4u8,7u8,10u8,0u8,12u8,3u8,5u8,9u8,11u8,1u8,12u8,3u8,11u8,3u8,12u8,16u8, - 10u8,2u8,16u8,2u8,12u8,23u8,10u8,23u8,65u8,9u8,12u8,22u8,11u8,16u8,7u8,0u8,24u8,11u8,0u8,26u8, - 12u8,17u8,10u8,22u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,44u8,11u8,23u8,1u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,1u8,12u8, - 9u8,14u8,9u8,12u8,10u8,7u8,0u8,7u8,0u8,18u8,1u8,12u8,8u8,11u8,10u8,14u8,8u8,12u8,7u8,12u8, - 6u8,5u8,144u8,1u8,10u8,17u8,10u8,23u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,66u8,9u8,16u8, - 3u8,20u8,35u8,4u8,63u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,18u8,1u8,12u8,11u8,14u8,11u8,11u8,23u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 66u8,9u8,12u8,5u8,12u8,4u8,5u8,140u8,1u8,10u8,23u8,10u8,22u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,66u8,9u8,16u8,3u8,20u8,10u8,17u8,37u8,4u8,88u8,11u8,23u8,11u8,22u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,23u8,66u8,9u8,12u8,13u8,7u8,0u8,7u8,0u8,18u8,1u8,12u8,12u8,11u8, - 13u8,14u8,12u8,12u8,15u8,12u8,14u8,5u8,136u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8, - 22u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,19u8,12u8,18u8,40u8,10u8,18u8,10u8,19u8, - 35u8,4u8,126u8,5u8,100u8,10u8,19u8,10u8,19u8,10u8,18u8,23u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,26u8,23u8,12u8,21u8,10u8,17u8,10u8,23u8,10u8,21u8,66u8,9u8,16u8,3u8,20u8,35u8,4u8,122u8,40u8, - 11u8,21u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,12u8,19u8,5u8,125u8,40u8,11u8,21u8,12u8, - 18u8,5u8,94u8,10u8,23u8,10u8,18u8,66u8,9u8,11u8,23u8,11u8,18u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,66u8,9u8,12u8,15u8,12u8,14u8,11u8,14u8,11u8,15u8,12u8,5u8,12u8,4u8,11u8,4u8,11u8, - 5u8,12u8,7u8,12u8,6u8,11u8,6u8,11u8,7u8,12u8,24u8,12u8,20u8,10u8,20u8,16u8,3u8,20u8,10u8,24u8, - 16u8,3u8,20u8,11u8,20u8,16u8,4u8,20u8,11u8,24u8,16u8,4u8,20u8,11u8,17u8,17u8,6u8,12u8,25u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,0u8,10u8,2u8,16u8,5u8,20u8,11u8,2u8,16u8,6u8,20u8, - 11u8,25u8,17u8,6u8,2u8,3u8,0u8,0u8,0u8,6u8,8u8,10u8,0u8,16u8,0u8,20u8,11u8,1u8,11u8,0u8, - 16u8,7u8,17u8,2u8,2u8,4u8,0u8,0u8,0u8,6u8,8u8,10u8,0u8,16u8,0u8,20u8,11u8,1u8,11u8,0u8, - 16u8,8u8,17u8,2u8,2u8,5u8,1u8,0u8,0u8,17u8,113u8,10u8,0u8,17u8,14u8,7u8,9u8,41u8,3u8,32u8, - 4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8,5u8,17u8,15u8,39u8,6u8,232u8,3u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,3u8,6u8,64u8,66u8,15u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,6u8,2u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,10u8,3u8,24u8,10u8,4u8,24u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8, - 24u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,24u8,17u8,0u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,44u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,17u8, - 0u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,10u8,3u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,17u8,0u8,18u8,4u8,12u8, - 2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,4u8,24u8,10u8,4u8,24u8,6u8,44u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,6u8,48u8,117u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,0u8,6u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8, - 6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,17u8,0u8,6u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,10u8,3u8,24u8,6u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,100u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,24u8,17u8,0u8,18u8,4u8,12u8,1u8,10u8,0u8,11u8,2u8,11u8,1u8,18u8,3u8, - 45u8,3u8,7u8,9u8,41u8,2u8,32u8,4u8,88u8,5u8,93u8,11u8,0u8,1u8,7u8,4u8,17u8,15u8,39u8,11u8, - 0u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,5u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,11u8,4u8,24u8,6u8,44u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,44u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,6u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,3u8,24u8,6u8,5u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,3u8,24u8,18u8,2u8,45u8,2u8,2u8,6u8,0u8,0u8,0u8,6u8, - 14u8,10u8,2u8,11u8,4u8,10u8,0u8,23u8,11u8,3u8,11u8,2u8,23u8,24u8,11u8,1u8,11u8,0u8,23u8,26u8, - 22u8,2u8,7u8,1u8,0u8,0u8,6u8,25u8,10u8,1u8,10u8,0u8,38u8,4u8,5u8,5u8,8u8,7u8,1u8,17u8, - 16u8,39u8,10u8,1u8,7u8,8u8,7u8,0u8,26u8,37u8,4u8,15u8,5u8,18u8,7u8,1u8,17u8,16u8,39u8,14u8, - 2u8,17u8,13u8,11u8,0u8,11u8,1u8,11u8,2u8,18u8,0u8,2u8,8u8,1u8,0u8,0u8,18u8,21u8,10u8,0u8, - 7u8,0u8,37u8,4u8,9u8,10u8,1u8,7u8,0u8,37u8,12u8,2u8,5u8,11u8,9u8,12u8,2u8,11u8,2u8,4u8, - 14u8,5u8,17u8,7u8,3u8,17u8,16u8,39u8,11u8,0u8,11u8,1u8,18u8,1u8,2u8,9u8,1u8,0u8,0u8,6u8, - 4u8,11u8,0u8,11u8,1u8,18u8,3u8,2u8,10u8,1u8,0u8,0u8,6u8,24u8,10u8,0u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8,8u8,7u8,7u8,17u8,16u8,39u8,10u8,0u8,7u8,8u8,7u8, - 0u8,26u8,37u8,4u8,15u8,5u8,18u8,7u8,6u8,17u8,16u8,39u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8, - 18u8,4u8,2u8,11u8,3u8,0u8,2u8,2u8,3u8,19u8,66u8,7u8,9u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8, - 5u8,17u8,17u8,39u8,7u8,9u8,41u8,2u8,4u8,11u8,5u8,14u8,7u8,4u8,17u8,17u8,39u8,17u8,18u8,12u8, - 0u8,12u8,3u8,7u8,9u8,43u8,3u8,12u8,2u8,7u8,9u8,42u8,2u8,12u8,1u8,10u8,2u8,16u8,9u8,10u8, - 3u8,17u8,3u8,10u8,1u8,15u8,10u8,21u8,10u8,2u8,16u8,9u8,10u8,3u8,17u8,1u8,10u8,1u8,15u8,11u8, - 21u8,10u8,2u8,16u8,9u8,11u8,3u8,17u8,4u8,10u8,1u8,15u8,12u8,21u8,10u8,2u8,16u8,13u8,10u8,0u8, - 17u8,3u8,10u8,1u8,15u8,14u8,21u8,10u8,2u8,16u8,13u8,10u8,0u8,17u8,1u8,10u8,1u8,15u8,15u8,21u8, - 11u8,2u8,16u8,13u8,11u8,0u8,17u8,4u8,11u8,1u8,15u8,16u8,21u8,2u8,12u8,3u8,0u8,1u8,3u8,6u8, - 7u8,11u8,0u8,17u8,14u8,11u8,1u8,7u8,9u8,42u8,3u8,21u8,2u8,13u8,0u8,0u8,0u8,20u8,87u8,10u8, - 0u8,65u8,9u8,12u8,8u8,40u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,40u8,10u8,7u8, - 10u8,8u8,37u8,4u8,84u8,5u8,12u8,10u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8, - 23u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8, - 1u8,12u8,1u8,14u8,1u8,12u8,2u8,5u8,29u8,10u8,0u8,10u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,66u8,9u8,12u8,2u8,11u8,2u8,12u8,6u8,10u8,7u8,10u8,8u8,33u8,4u8,42u8,7u8,0u8, - 7u8,0u8,18u8,1u8,12u8,3u8,14u8,3u8,12u8,4u8,5u8,46u8,10u8,0u8,10u8,7u8,66u8,9u8,12u8,4u8, - 11u8,4u8,12u8,9u8,10u8,6u8,16u8,3u8,20u8,10u8,9u8,16u8,3u8,20u8,35u8,4u8,65u8,11u8,6u8,16u8, - 4u8,20u8,11u8,9u8,16u8,4u8,20u8,37u8,12u8,5u8,5u8,71u8,11u8,9u8,1u8,11u8,6u8,1u8,9u8,12u8, - 5u8,11u8,5u8,4u8,74u8,5u8,79u8,11u8,0u8,1u8,7u8,2u8,17u8,16u8,39u8,11u8,7u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,7u8,5u8,6u8,11u8,0u8,1u8,2u8,4u8,0u8,4u8,2u8,0u8, - 2u8,1u8,0u8,1u8,1u8,0u8,0u8,0u8,1u8,4u8,1u8,4u8,3u8,3u8,0u8,2u8,0u8,2u8,1u8,2u8, - 2u8,3u8,1u8,2u8,3u8,2u8,4u8,2u8,5u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk20); - let chunk21 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,24u8,2u8,24u8,18u8,3u8,42u8,102u8,4u8, - 144u8,1u8,4u8,5u8,148u8,1u8,40u8,7u8,188u8,1u8,151u8,5u8,8u8,211u8,6u8,32u8,6u8,243u8,6u8,84u8, - 16u8,199u8,7u8,145u8,3u8,10u8,216u8,10u8,23u8,12u8,239u8,10u8,160u8,3u8,13u8,143u8,14u8,6u8,15u8,149u8, - 14u8,12u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8, - 0u8,15u8,0u8,16u8,0u8,17u8,0u8,18u8,8u8,0u8,0u8,19u8,8u8,0u8,0u8,20u8,6u8,0u8,4u8,31u8, - 4u8,1u8,6u8,1u8,0u8,21u8,0u8,1u8,0u8,0u8,22u8,2u8,0u8,0u8,0u8,23u8,0u8,0u8,0u8,0u8, - 24u8,2u8,0u8,0u8,0u8,25u8,2u8,0u8,0u8,0u8,26u8,0u8,1u8,0u8,0u8,27u8,0u8,3u8,0u8,0u8, - 28u8,0u8,0u8,0u8,9u8,33u8,2u8,0u8,0u8,3u8,34u8,1u8,1u8,0u8,4u8,35u8,6u8,0u8,1u8,6u8, - 6u8,36u8,2u8,7u8,0u8,1u8,37u8,7u8,1u8,0u8,1u8,38u8,2u8,8u8,1u8,6u8,2u8,39u8,0u8,3u8, - 0u8,10u8,40u8,0u8,1u8,0u8,5u8,41u8,0u8,3u8,0u8,11u8,42u8,0u8,0u8,0u8,7u8,43u8,0u8,0u8, - 0u8,8u8,44u8,0u8,0u8,0u8,10u8,5u8,13u8,5u8,0u8,1u8,3u8,1u8,6u8,12u8,1u8,1u8,2u8,1u8, - 7u8,8u8,0u8,1u8,8u8,2u8,2u8,7u8,11u8,3u8,1u8,9u8,0u8,9u8,0u8,1u8,5u8,1u8,11u8,3u8, - 1u8,9u8,0u8,4u8,1u8,1u8,7u8,8u8,0u8,3u8,16u8,97u8,112u8,116u8,111u8,115u8,95u8,103u8,111u8,118u8, - 101u8,114u8,110u8,97u8,110u8,99u8,101u8,5u8,98u8,108u8,111u8,99u8,107u8,16u8,99u8,111u8,110u8,115u8,101u8,110u8, - 115u8,117u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,12u8,103u8,97u8,115u8,95u8,115u8,99u8,104u8,101u8,100u8, - 117u8,108u8,101u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,7u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,15u8, - 114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,7u8,97u8,99u8,99u8,111u8, - 117u8,110u8,116u8,12u8,99u8,104u8,97u8,105u8,110u8,95u8,115u8,116u8,97u8,116u8,117u8,115u8,5u8,101u8,114u8,114u8, - 111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,115u8,105u8, - 103u8,110u8,101u8,114u8,5u8,115u8,116u8,97u8,107u8,101u8,11u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,95u8,103u8, - 97u8,115u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,9u8, - 116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8, - 110u8,95u8,102u8,101u8,101u8,13u8,67u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,22u8, - 68u8,105u8,115u8,97u8,98u8,108u8,101u8,82u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8, - 111u8,110u8,13u8,78u8,101u8,119u8,69u8,112u8,111u8,99u8,104u8,69u8,118u8,101u8,110u8,116u8,13u8,99u8,117u8,114u8, - 114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,23u8,100u8,105u8,115u8,97u8,98u8,108u8,101u8,95u8,114u8, - 101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,34u8,101u8,109u8,105u8,116u8,95u8, - 103u8,101u8,110u8,101u8,115u8,105u8,115u8,95u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8, - 105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,22u8,101u8,110u8,97u8,98u8,108u8,101u8,95u8,114u8,101u8,99u8, - 111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8, - 105u8,122u8,101u8,25u8,108u8,97u8,115u8,116u8,95u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8, - 116u8,105u8,111u8,110u8,95u8,116u8,105u8,109u8,101u8,23u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8, - 97u8,116u8,105u8,111u8,110u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,11u8,114u8,101u8,99u8,111u8,110u8,102u8, - 105u8,103u8,117u8,114u8,101u8,5u8,101u8,112u8,111u8,99u8,104u8,6u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8, - 118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,11u8,100u8,117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8, - 108u8,100u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8, - 101u8,119u8,111u8,114u8,107u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,10u8, - 101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8, - 102u8,26u8,103u8,101u8,116u8,95u8,103u8,117u8,105u8,100u8,95u8,110u8,101u8,120u8,116u8,95u8,99u8,114u8,101u8,97u8, - 116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8, - 97u8,110u8,100u8,108u8,101u8,10u8,105u8,115u8,95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,16u8,110u8,111u8,119u8, - 95u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,31u8,99u8,111u8,108u8,108u8,101u8,99u8, - 116u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,103u8,97u8,115u8, - 95u8,102u8,101u8,101u8,115u8,22u8,112u8,114u8,111u8,99u8,101u8,115u8,115u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8, - 116u8,101u8,100u8,95u8,102u8,101u8,101u8,115u8,12u8,111u8,110u8,95u8,110u8,101u8,119u8,95u8,101u8,112u8,111u8,99u8, - 104u8,11u8,111u8,110u8,95u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8, - 97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,252u8,2u8,5u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8, - 69u8,67u8,79u8,78u8,70u8,73u8,71u8,85u8,82u8,65u8,84u8,73u8,79u8,78u8,51u8,84u8,104u8,101u8,32u8,96u8, - 67u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,96u8,32u8,114u8,101u8,115u8,111u8,117u8, - 114u8,99u8,101u8,32u8,105u8,115u8,32u8,105u8,110u8,32u8,97u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8, - 32u8,115u8,116u8,97u8,116u8,101u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,69u8,67u8,79u8,78u8,70u8, - 73u8,71u8,51u8,65u8,32u8,96u8,82u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8, - 110u8,96u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8,105u8,115u8,32u8,105u8,110u8,32u8,97u8,110u8, - 32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,115u8,116u8,97u8,116u8,101u8,3u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,18u8,69u8,77u8,79u8,68u8,73u8,70u8,89u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8, - 89u8,68u8,65u8,32u8,96u8,77u8,111u8,100u8,105u8,102u8,121u8,67u8,111u8,110u8,102u8,105u8,103u8,67u8,97u8,112u8, - 97u8,98u8,105u8,108u8,105u8,116u8,121u8,96u8,32u8,105u8,115u8,32u8,105u8,110u8,32u8,97u8,32u8,100u8,105u8,102u8, - 102u8,101u8,114u8,101u8,110u8,116u8,32u8,115u8,116u8,97u8,116u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8,119u8,97u8, - 115u8,32u8,101u8,120u8,112u8,101u8,99u8,116u8,101u8,100u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8, - 73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,66u8,76u8,79u8,67u8,75u8,95u8,84u8,73u8,77u8,69u8,38u8,65u8, - 110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,98u8,108u8,111u8,99u8,107u8,32u8,116u8,105u8,109u8,101u8, - 32u8,119u8,97u8,115u8,32u8,101u8,110u8,99u8,111u8,117u8,110u8,116u8,101u8,114u8,101u8,100u8,46u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,23u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,71u8,85u8,73u8,68u8,95u8, - 70u8,79u8,82u8,95u8,69u8,86u8,69u8,78u8,84u8,38u8,65u8,110u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8, - 32u8,98u8,108u8,111u8,99u8,107u8,32u8,116u8,105u8,109u8,101u8,32u8,119u8,97u8,115u8,32u8,101u8,110u8,99u8,111u8, - 117u8,110u8,116u8,101u8,114u8,101u8,100u8,46u8,0u8,0u8,0u8,2u8,3u8,29u8,3u8,26u8,3u8,30u8,11u8,3u8, - 1u8,8u8,2u8,1u8,2u8,1u8,32u8,1u8,2u8,2u8,1u8,29u8,3u8,0u8,1u8,0u8,1u8,0u8,0u8,5u8, - 7u8,5u8,43u8,0u8,16u8,0u8,20u8,2u8,1u8,0u8,0u8,0u8,0u8,15u8,10u8,0u8,17u8,8u8,17u8,6u8, - 4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,1u8,17u8,9u8,39u8,11u8,0u8,9u8,18u8,1u8,45u8,1u8,2u8, - 2u8,0u8,0u8,1u8,0u8,4u8,38u8,7u8,5u8,42u8,0u8,12u8,1u8,10u8,1u8,16u8,0u8,20u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,16u8,10u8,1u8,16u8,1u8,20u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,33u8,12u8,0u8,5u8,18u8,9u8,12u8,0u8,11u8,0u8,4u8,21u8,5u8,26u8,11u8,1u8, - 1u8,7u8,1u8,17u8,9u8,39u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,1u8,15u8,0u8,21u8, - 10u8,1u8,15u8,2u8,11u8,1u8,16u8,0u8,20u8,18u8,2u8,56u8,0u8,2u8,3u8,0u8,0u8,1u8,1u8,0u8, - 17u8,10u8,0u8,17u8,8u8,17u8,6u8,32u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,1u8,17u8,9u8,39u8, - 11u8,0u8,17u8,11u8,44u8,1u8,19u8,1u8,1u8,2u8,4u8,3u8,0u8,0u8,0u8,22u8,10u8,0u8,17u8,8u8, - 10u8,0u8,17u8,11u8,17u8,12u8,6u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,9u8,5u8,14u8, - 11u8,0u8,1u8,7u8,3u8,17u8,9u8,39u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,0u8,56u8,1u8,18u8,0u8,45u8,0u8,2u8,5u8,1u8,0u8, - 1u8,0u8,0u8,5u8,7u8,5u8,43u8,0u8,16u8,1u8,20u8,2u8,6u8,0u8,0u8,0u8,0u8,4u8,7u8,5u8, - 41u8,1u8,32u8,2u8,7u8,3u8,0u8,1u8,0u8,9u8,72u8,17u8,14u8,4u8,5u8,8u8,12u8,0u8,5u8,9u8, - 17u8,15u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,12u8,0u8,11u8,0u8,4u8,14u8,8u8,12u8, - 1u8,5u8,17u8,17u8,6u8,32u8,12u8,1u8,11u8,1u8,4u8,20u8,2u8,7u8,5u8,42u8,0u8,12u8,2u8,17u8, - 15u8,12u8,3u8,10u8,3u8,10u8,2u8,16u8,1u8,20u8,33u8,4u8,34u8,11u8,2u8,1u8,2u8,17u8,16u8,4u8, - 37u8,17u8,17u8,17u8,18u8,17u8,19u8,10u8,3u8,10u8,2u8,16u8,1u8,20u8,36u8,4u8,46u8,5u8,51u8,11u8, - 2u8,1u8,7u8,2u8,17u8,9u8,39u8,11u8,3u8,10u8,2u8,15u8,1u8,21u8,40u8,10u8,2u8,16u8,0u8,20u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,2u8,15u8,0u8,21u8,10u8,2u8,15u8,2u8,11u8, - 2u8,16u8,0u8,20u8,18u8,2u8,56u8,0u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,0u8,0u8,1u8,0u8, - 2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8, - ]; - vector::push_back(&mut code, chunk21); - let chunk22 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,9u8,1u8,0u8,2u8,2u8,2u8,4u8,3u8,6u8,10u8,5u8, - 16u8,4u8,7u8,20u8,106u8,8u8,126u8,32u8,10u8,158u8,1u8,5u8,12u8,163u8,1u8,19u8,15u8,182u8,1u8,2u8, - 0u8,1u8,0u8,2u8,6u8,0u8,0u8,3u8,0u8,1u8,0u8,0u8,4u8,0u8,1u8,0u8,0u8,1u8,8u8,0u8, - 16u8,97u8,112u8,116u8,111u8,115u8,95u8,103u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,19u8,103u8,111u8, - 118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,18u8,71u8,111u8, - 118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,21u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,101u8,109u8,112u8,116u8,121u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,15u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,11u8,100u8,117u8,109u8,109u8,121u8, - 95u8,102u8,105u8,101u8,108u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8, - 1u8,5u8,1u8,0u8,3u8,0u8,0u8,0u8,2u8,17u8,1u8,2u8,1u8,3u8,0u8,0u8,0u8,3u8,9u8,18u8, - 0u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk22); - let chunk23 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,36u8,2u8,36u8,84u8,3u8,120u8,226u8,2u8, - 4u8,218u8,3u8,66u8,5u8,156u8,4u8,147u8,3u8,7u8,175u8,7u8,189u8,12u8,8u8,236u8,19u8,32u8,6u8,140u8, - 20u8,172u8,1u8,16u8,184u8,21u8,151u8,8u8,10u8,207u8,29u8,109u8,12u8,188u8,30u8,171u8,8u8,13u8,231u8,38u8, - 18u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8, - 9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8,17u8,0u8,18u8,8u8, - 0u8,0u8,19u8,6u8,0u8,0u8,20u8,8u8,0u8,0u8,21u8,8u8,0u8,0u8,22u8,8u8,0u8,0u8,23u8,7u8, - 0u8,0u8,24u8,6u8,0u8,0u8,25u8,6u8,0u8,0u8,26u8,8u8,0u8,10u8,30u8,7u8,2u8,0u8,0u8,0u8, - 0u8,13u8,31u8,7u8,0u8,1u8,45u8,6u8,0u8,5u8,59u8,4u8,1u8,6u8,1u8,15u8,67u8,4u8,2u8,3u8, - 1u8,0u8,1u8,6u8,68u8,6u8,0u8,7u8,78u8,7u8,1u8,0u8,0u8,2u8,83u8,8u8,0u8,12u8,94u8,11u8, - 0u8,0u8,27u8,0u8,1u8,0u8,0u8,28u8,0u8,1u8,0u8,0u8,29u8,2u8,1u8,0u8,0u8,32u8,3u8,4u8, - 0u8,0u8,33u8,5u8,1u8,0u8,0u8,34u8,1u8,6u8,0u8,0u8,35u8,1u8,0u8,0u8,0u8,36u8,7u8,8u8, - 0u8,0u8,37u8,9u8,8u8,0u8,0u8,38u8,1u8,0u8,0u8,0u8,39u8,7u8,0u8,0u8,0u8,40u8,10u8,1u8, - 0u8,0u8,41u8,11u8,1u8,0u8,0u8,42u8,0u8,1u8,0u8,0u8,43u8,12u8,8u8,0u8,0u8,44u8,13u8,8u8, - 0u8,0u8,46u8,14u8,1u8,0u8,0u8,47u8,10u8,1u8,0u8,0u8,48u8,15u8,1u8,0u8,17u8,69u8,18u8,0u8, - 1u8,4u8,4u8,70u8,0u8,0u8,0u8,17u8,71u8,18u8,19u8,1u8,4u8,10u8,72u8,21u8,22u8,2u8,4u8,4u8, - 10u8,73u8,23u8,24u8,2u8,4u8,4u8,10u8,74u8,25u8,1u8,2u8,4u8,4u8,13u8,75u8,19u8,27u8,0u8,13u8, - 76u8,28u8,0u8,0u8,10u8,77u8,1u8,30u8,2u8,4u8,4u8,9u8,79u8,11u8,7u8,0u8,11u8,80u8,7u8,7u8, - 0u8,16u8,81u8,1u8,0u8,0u8,11u8,82u8,7u8,0u8,0u8,3u8,84u8,1u8,33u8,1u8,0u8,7u8,85u8,1u8, - 34u8,1u8,0u8,7u8,86u8,35u8,22u8,1u8,0u8,7u8,87u8,35u8,36u8,1u8,0u8,7u8,88u8,37u8,34u8,1u8, - 0u8,6u8,29u8,1u8,17u8,0u8,17u8,33u8,38u8,0u8,1u8,4u8,5u8,89u8,40u8,1u8,1u8,6u8,10u8,87u8, - 21u8,42u8,2u8,4u8,4u8,1u8,90u8,43u8,8u8,0u8,14u8,91u8,11u8,1u8,0u8,2u8,92u8,11u8,22u8,0u8, - 4u8,93u8,0u8,0u8,0u8,12u8,95u8,1u8,45u8,0u8,12u8,96u8,46u8,22u8,0u8,11u8,97u8,7u8,47u8,0u8, - 11u8,98u8,7u8,0u8,0u8,14u8,99u8,11u8,1u8,0u8,17u8,100u8,11u8,1u8,1u8,4u8,1u8,101u8,11u8,49u8, - 1u8,6u8,15u8,102u8,1u8,53u8,2u8,3u8,4u8,8u8,41u8,1u8,1u8,0u8,17u8,103u8,18u8,22u8,1u8,4u8, - 10u8,104u8,23u8,55u8,2u8,4u8,4u8,17u8,43u8,18u8,37u8,1u8,4u8,17u8,105u8,56u8,1u8,1u8,4u8,14u8, - 106u8,7u8,1u8,0u8,15u8,107u8,60u8,22u8,2u8,3u8,0u8,15u8,74u8,61u8,1u8,2u8,3u8,0u8,17u8,108u8, - 18u8,0u8,1u8,4u8,6u8,109u8,1u8,17u8,0u8,17u8,48u8,62u8,1u8,1u8,4u8,19u8,17u8,21u8,17u8,22u8, - 20u8,23u8,20u8,24u8,20u8,27u8,29u8,24u8,29u8,32u8,32u8,33u8,6u8,34u8,6u8,35u8,6u8,36u8,6u8,38u8, - 17u8,39u8,39u8,40u8,41u8,50u8,17u8,51u8,39u8,51u8,50u8,51u8,51u8,52u8,52u8,27u8,20u8,54u8,17u8,55u8, - 20u8,56u8,17u8,57u8,17u8,27u8,41u8,24u8,41u8,39u8,50u8,59u8,52u8,60u8,52u8,61u8,17u8,63u8,17u8,39u8, - 51u8,1u8,3u8,0u8,5u8,6u8,12u8,5u8,10u8,2u8,10u8,2u8,10u8,2u8,2u8,10u8,2u8,10u8,2u8,1u8, - 11u8,9u8,2u8,8u8,10u8,10u8,2u8,6u8,6u8,12u8,5u8,10u8,2u8,10u8,2u8,10u8,2u8,1u8,1u8,4u8, - 1u8,5u8,1u8,12u8,2u8,6u8,12u8,5u8,4u8,6u8,12u8,4u8,3u8,3u8,1u8,6u8,12u8,2u8,3u8,5u8, - 3u8,3u8,5u8,10u8,2u8,3u8,6u8,12u8,5u8,8u8,11u8,4u8,6u8,12u8,5u8,3u8,1u8,3u8,7u8,8u8, - 0u8,7u8,10u8,2u8,10u8,2u8,1u8,8u8,14u8,2u8,5u8,3u8,1u8,10u8,2u8,2u8,3u8,10u8,2u8,2u8, - 6u8,11u8,9u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,1u8,2u8,7u8,11u8,9u8,2u8,9u8,0u8, - 9u8,1u8,6u8,9u8,0u8,1u8,7u8,9u8,1u8,3u8,7u8,11u8,9u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8, - 9u8,1u8,3u8,8u8,10u8,8u8,10u8,11u8,9u8,2u8,8u8,10u8,10u8,2u8,1u8,8u8,10u8,1u8,6u8,8u8, - 10u8,2u8,8u8,10u8,10u8,2u8,1u8,11u8,9u8,2u8,9u8,0u8,9u8,1u8,13u8,3u8,5u8,5u8,10u8,2u8, - 11u8,9u8,2u8,8u8,10u8,10u8,2u8,7u8,11u8,12u8,1u8,8u8,1u8,11u8,15u8,1u8,4u8,6u8,8u8,2u8, - 3u8,3u8,11u8,9u8,2u8,8u8,10u8,10u8,2u8,5u8,11u8,15u8,1u8,4u8,1u8,8u8,16u8,1u8,11u8,15u8, - 1u8,4u8,1u8,11u8,15u8,1u8,9u8,0u8,1u8,6u8,11u8,15u8,1u8,9u8,0u8,1u8,6u8,9u8,0u8,1u8, - 9u8,0u8,9u8,5u8,5u8,9u8,0u8,10u8,2u8,4u8,3u8,11u8,15u8,1u8,4u8,11u8,9u8,2u8,8u8,10u8, - 10u8,2u8,1u8,1u8,8u8,1u8,2u8,7u8,11u8,12u8,1u8,9u8,0u8,9u8,0u8,2u8,5u8,8u8,11u8,1u8, - 6u8,9u8,1u8,1u8,6u8,8u8,11u8,4u8,8u8,17u8,3u8,3u8,3u8,1u8,8u8,17u8,1u8,6u8,8u8,17u8, - 4u8,3u8,3u8,3u8,3u8,4u8,3u8,4u8,3u8,6u8,12u8,1u8,11u8,12u8,1u8,9u8,0u8,1u8,8u8,6u8, - 1u8,8u8,7u8,2u8,8u8,5u8,1u8,1u8,11u8,13u8,2u8,9u8,0u8,9u8,1u8,2u8,6u8,3u8,7u8,11u8, - 9u8,2u8,3u8,10u8,2u8,2u8,9u8,0u8,9u8,1u8,3u8,5u8,3u8,10u8,2u8,1u8,2u8,1u8,7u8,8u8, - 2u8,6u8,8u8,14u8,3u8,8u8,5u8,5u8,3u8,7u8,8u8,8u8,2u8,6u8,11u8,13u8,2u8,9u8,0u8,9u8, - 1u8,9u8,0u8,3u8,7u8,11u8,13u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,5u8,6u8,9u8,0u8, - 5u8,3u8,3u8,1u8,16u8,97u8,112u8,116u8,111u8,115u8,95u8,103u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8, - 101u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8, - 4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,19u8,103u8,111u8, - 118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,6u8,111u8,112u8, - 116u8,105u8,111u8,110u8,15u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8, - 6u8,115u8,105u8,103u8,110u8,101u8,114u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,5u8,115u8, - 116u8,97u8,107u8,101u8,14u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,6u8, - 115u8,116u8,114u8,105u8,110u8,103u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,101u8,115u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,6u8, - 118u8,111u8,116u8,105u8,110u8,103u8,23u8,65u8,112u8,112u8,114u8,111u8,118u8,101u8,100u8,69u8,120u8,101u8,99u8,117u8, - 116u8,105u8,111u8,110u8,72u8,97u8,115u8,104u8,101u8,115u8,19u8,67u8,114u8,101u8,97u8,116u8,101u8,80u8,114u8,111u8, - 112u8,111u8,115u8,97u8,108u8,69u8,118u8,101u8,110u8,116u8,16u8,71u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8, - 101u8,67u8,111u8,110u8,102u8,105u8,103u8,16u8,71u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,69u8,118u8, - 101u8,110u8,116u8,115u8,23u8,71u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,82u8,101u8,115u8,112u8,111u8, - 110u8,115u8,98u8,105u8,108u8,105u8,116u8,121u8,9u8,82u8,101u8,99u8,111u8,114u8,100u8,75u8,101u8,121u8,17u8,85u8, - 112u8,100u8,97u8,116u8,101u8,67u8,111u8,110u8,102u8,105u8,103u8,69u8,118u8,101u8,110u8,116u8,9u8,86u8,111u8,116u8, - 101u8,69u8,118u8,101u8,110u8,116u8,13u8,86u8,111u8,116u8,105u8,110u8,103u8,82u8,101u8,99u8,111u8,114u8,100u8,115u8, - 24u8,97u8,100u8,100u8,95u8,97u8,112u8,112u8,114u8,111u8,118u8,101u8,100u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8, - 95u8,104u8,97u8,115u8,104u8,31u8,97u8,100u8,100u8,95u8,97u8,112u8,112u8,114u8,111u8,118u8,101u8,100u8,95u8,115u8, - 99u8,114u8,105u8,112u8,116u8,95u8,104u8,97u8,115u8,104u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,15u8,99u8,114u8, - 101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8, - 77u8,97u8,112u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,112u8,114u8, - 111u8,112u8,111u8,115u8,97u8,108u8,95u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,18u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,118u8,50u8,24u8,103u8,101u8,116u8,95u8,109u8, - 105u8,110u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,27u8, - 103u8,101u8,116u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8, - 114u8,95u8,115u8,116u8,97u8,107u8,101u8,10u8,103u8,101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,23u8,103u8, - 101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,116u8,101u8,115u8,116u8,110u8,101u8,116u8,95u8,111u8,110u8, - 108u8,121u8,24u8,103u8,101u8,116u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8, - 111u8,110u8,95u8,115u8,101u8,99u8,115u8,16u8,103u8,101u8,116u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8, - 111u8,119u8,101u8,114u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,11u8,114u8,101u8,99u8,111u8, - 110u8,102u8,105u8,103u8,117u8,114u8,101u8,20u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8,97u8,112u8,112u8,114u8,111u8, - 118u8,101u8,100u8,95u8,104u8,97u8,115u8,104u8,7u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,27u8,114u8,101u8,115u8, - 111u8,108u8,118u8,101u8,95u8,109u8,117u8,108u8,116u8,105u8,95u8,115u8,116u8,101u8,112u8,95u8,112u8,114u8,111u8,112u8, - 111u8,115u8,97u8,108u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,16u8,115u8,116u8,111u8,114u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,24u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,103u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,95u8,99u8,111u8,110u8, - 102u8,105u8,103u8,4u8,118u8,111u8,116u8,101u8,6u8,104u8,97u8,115u8,104u8,101u8,115u8,8u8,112u8,114u8,111u8,112u8, - 111u8,115u8,101u8,114u8,10u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,11u8,112u8,114u8,111u8,112u8, - 111u8,115u8,97u8,108u8,95u8,105u8,100u8,14u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,104u8,97u8, - 115u8,104u8,17u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8, - 20u8,109u8,105u8,110u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8, - 100u8,23u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,95u8, - 115u8,116u8,97u8,107u8,101u8,20u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8, - 110u8,95u8,115u8,101u8,99u8,115u8,22u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8, - 101u8,20u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,11u8,118u8,111u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,115u8,105u8,103u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,115u8,5u8,118u8,111u8,116u8,101u8,114u8,9u8,110u8,117u8,109u8,95u8,118u8,111u8,116u8, - 101u8,115u8,11u8,115u8,104u8,111u8,117u8,108u8,100u8,95u8,112u8,97u8,115u8,115u8,5u8,118u8,111u8,116u8,101u8,115u8, - 5u8,84u8,97u8,98u8,108u8,101u8,18u8,71u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,80u8,114u8,111u8, - 112u8,111u8,115u8,97u8,108u8,18u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,115u8, - 116u8,97u8,116u8,101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8, - 116u8,18u8,103u8,101u8,116u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,104u8,97u8,115u8,104u8, - 12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8, - 95u8,109u8,117u8,116u8,3u8,97u8,100u8,100u8,4u8,117u8,116u8,102u8,56u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8, - 6u8,99u8,114u8,101u8,97u8,116u8,101u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,10u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,95u8,111u8,102u8,19u8,103u8,101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8, - 118u8,111u8,116u8,101u8,114u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,15u8,103u8,101u8, - 116u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8,99u8,115u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8, - 111u8,105u8,110u8,6u8,115u8,117u8,112u8,112u8,108u8,121u8,4u8,110u8,111u8,110u8,101u8,7u8,105u8,115u8,95u8,115u8, - 111u8,109u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,4u8,115u8,111u8,109u8,101u8,10u8,101u8,109u8,105u8,116u8, - 95u8,101u8,118u8,101u8,110u8,116u8,29u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8, - 95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,20u8,97u8,115u8,115u8, - 101u8,114u8,116u8,95u8,99u8,111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,19u8,104u8,97u8, - 115u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,15u8,117u8,110u8, - 97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,101u8,100u8,13u8,83u8,116u8,97u8,107u8,105u8,110u8, - 103u8,67u8,111u8,110u8,102u8,105u8,103u8,3u8,103u8,101u8,116u8,30u8,103u8,101u8,116u8,95u8,97u8,108u8,108u8,111u8, - 119u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,101u8,116u8,95u8,99u8,104u8,97u8,110u8, - 103u8,101u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,30u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8, - 114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8,95u8,112u8,111u8, - 119u8,101u8,114u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8, - 109u8,101u8,119u8,111u8,114u8,107u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,16u8,110u8,101u8,119u8,95u8, - 101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,3u8,110u8,101u8,119u8,11u8,105u8,115u8,95u8, - 114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,19u8,114u8,101u8,115u8,111u8, - 108u8,118u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,95u8,118u8,50u8,25u8,97u8,115u8,115u8,101u8, - 114u8,116u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,95u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8, - 100u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,28u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,111u8, - 115u8,97u8,108u8,95u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8,115u8,21u8, - 99u8,114u8,101u8,97u8,116u8,101u8,95u8,101u8,109u8,112u8,116u8,121u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8, - 108u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,14u8,13u8,109u8,101u8,116u8, - 97u8,100u8,97u8,116u8,97u8,95u8,104u8,97u8,115u8,104u8,10u8,2u8,18u8,17u8,109u8,101u8,116u8,97u8,100u8,97u8, - 116u8,97u8,95u8,108u8,111u8,99u8,97u8,116u8,105u8,111u8,110u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,130u8,8u8,10u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,73u8,78u8,83u8, - 85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,69u8,82u8,95u8,83u8, - 84u8,65u8,75u8,69u8,76u8,84u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8, - 116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8, - 97u8,118u8,101u8,32u8,115u8,117u8,102u8,102u8,105u8,99u8,105u8,101u8,110u8,116u8,32u8,115u8,116u8,97u8,107u8,101u8, - 32u8,116u8,111u8,32u8,99u8,114u8,101u8,97u8,116u8,101u8,32u8,97u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8, - 108u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,78u8,79u8,84u8,95u8,68u8,69u8,76u8,69u8,71u8, - 65u8,84u8,69u8,68u8,95u8,86u8,79u8,84u8,69u8,82u8,68u8,84u8,104u8,105u8,115u8,32u8,97u8,99u8,99u8,111u8, - 117u8,110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,116u8,104u8,101u8,32u8,100u8,101u8,115u8,105u8,103u8, - 110u8,97u8,116u8,101u8,100u8,32u8,118u8,111u8,116u8,101u8,114u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,115u8, - 112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,3u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,73u8,78u8,83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8, - 84u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,76u8,79u8,67u8,75u8,85u8,80u8,96u8,84u8,104u8,101u8,32u8,115u8, - 112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,32u8, - 100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,108u8,111u8,110u8,103u8,32u8,101u8, - 110u8,111u8,117u8,103u8,104u8,32u8,114u8,101u8,109u8,97u8,105u8,110u8,105u8,110u8,103u8,32u8,108u8,111u8,99u8,107u8, - 117u8,112u8,32u8,116u8,111u8,32u8,99u8,114u8,101u8,97u8,116u8,101u8,32u8,97u8,32u8,112u8,114u8,111u8,112u8,111u8, - 115u8,97u8,108u8,32u8,111u8,114u8,32u8,118u8,111u8,116u8,101u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8, - 69u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,86u8,79u8,84u8,69u8,68u8,75u8,84u8,104u8,101u8,32u8,115u8, - 112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,32u8, - 104u8,97u8,115u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,98u8,101u8,101u8,110u8,32u8,117u8,115u8,101u8, - 100u8,32u8,116u8,111u8,32u8,118u8,111u8,116u8,101u8,32u8,111u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8,109u8, - 101u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8, - 78u8,79u8,95u8,86u8,79u8,84u8,73u8,78u8,71u8,95u8,80u8,79u8,87u8,69u8,82u8,58u8,84u8,104u8,101u8,32u8, - 115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8, - 32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,112u8,97u8,114u8,116u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8, - 32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,115u8,101u8,116u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,28u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,78u8,79u8,84u8,95u8,82u8,69u8,83u8, - 79u8,76u8,86u8,65u8,66u8,76u8,69u8,95u8,89u8,69u8,84u8,62u8,80u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8, - 32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,114u8,101u8,97u8,100u8,121u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8, - 114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,46u8,32u8,87u8,97u8,105u8,116u8,105u8,110u8,103u8,32u8,111u8,110u8, - 32u8,116u8,105u8,109u8,101u8,32u8,111u8,114u8,32u8,118u8,111u8,116u8,101u8,115u8,8u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,26u8,69u8,80u8,82u8,79u8,80u8,79u8,83u8,65u8,76u8,95u8,78u8,79u8,84u8,95u8,82u8,69u8,83u8, - 79u8,76u8,86u8,69u8,68u8,95u8,89u8,69u8,84u8,38u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8, - 97u8,108u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,116u8,32u8,98u8,101u8,101u8,110u8,32u8,114u8,101u8,115u8,111u8, - 108u8,118u8,101u8,100u8,32u8,121u8,101u8,116u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,77u8,69u8, - 84u8,65u8,68u8,65u8,84u8,65u8,95u8,76u8,79u8,67u8,65u8,84u8,73u8,79u8,78u8,95u8,84u8,79u8,79u8,95u8, - 76u8,79u8,78u8,71u8,49u8,77u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,32u8,108u8,111u8,99u8,97u8,116u8,105u8, - 111u8,110u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,108u8,111u8,110u8,103u8,101u8,114u8,32u8, - 116u8,104u8,97u8,110u8,32u8,50u8,53u8,54u8,32u8,99u8,104u8,97u8,114u8,115u8,10u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,69u8,77u8,69u8,84u8,65u8,68u8,65u8,84u8,65u8,95u8,72u8,65u8,83u8,72u8,95u8,84u8,79u8, - 79u8,95u8,76u8,79u8,78u8,71u8,45u8,77u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,32u8,104u8,97u8,115u8,104u8, - 32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,108u8,111u8,110u8,103u8,101u8,114u8,32u8,116u8,104u8, - 97u8,110u8,32u8,50u8,53u8,54u8,32u8,99u8,104u8,97u8,114u8,115u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 13u8,69u8,85u8,78u8,65u8,85u8,84u8,72u8,79u8,82u8,73u8,90u8,69u8,68u8,48u8,65u8,99u8,99u8,111u8,117u8, - 110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,117u8,116u8,104u8,111u8,114u8,105u8,122u8,101u8,100u8, - 32u8,116u8,111u8,32u8,99u8,97u8,108u8,108u8,32u8,116u8,104u8,105u8,115u8,32u8,102u8,117u8,110u8,99u8,116u8,105u8, - 111u8,110u8,46u8,0u8,3u8,24u8,103u8,101u8,116u8,95u8,109u8,105u8,110u8,95u8,118u8,111u8,116u8,105u8,110u8,103u8, - 95u8,116u8,104u8,114u8,101u8,115u8,104u8,111u8,108u8,100u8,1u8,1u8,0u8,24u8,103u8,101u8,116u8,95u8,118u8,111u8, - 116u8,105u8,110u8,103u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8,115u8,1u8,1u8, - 0u8,27u8,103u8,101u8,116u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,111u8, - 115u8,101u8,114u8,95u8,115u8,116u8,97u8,107u8,101u8,1u8,1u8,0u8,0u8,2u8,1u8,49u8,11u8,9u8,2u8,3u8, - 10u8,2u8,1u8,2u8,5u8,50u8,5u8,51u8,5u8,52u8,3u8,53u8,10u8,2u8,54u8,11u8,9u8,2u8,8u8,10u8, - 10u8,2u8,2u8,2u8,3u8,55u8,4u8,56u8,3u8,57u8,3u8,3u8,2u8,3u8,58u8,11u8,12u8,1u8,8u8,1u8, - 60u8,11u8,12u8,1u8,8u8,6u8,61u8,11u8,12u8,1u8,8u8,7u8,4u8,2u8,1u8,62u8,11u8,9u8,2u8,5u8, - 8u8,11u8,5u8,2u8,2u8,51u8,5u8,52u8,3u8,6u8,2u8,3u8,55u8,4u8,56u8,3u8,57u8,3u8,7u8,2u8, - 5u8,52u8,3u8,63u8,5u8,51u8,5u8,64u8,3u8,65u8,1u8,8u8,2u8,1u8,66u8,11u8,13u8,2u8,8u8,5u8, - 1u8,0u8,1u8,0u8,1u8,0u8,16u8,39u8,7u8,12u8,42u8,0u8,12u8,1u8,7u8,12u8,10u8,0u8,56u8,0u8, - 7u8,1u8,33u8,4u8,10u8,5u8,15u8,11u8,1u8,1u8,7u8,7u8,17u8,20u8,39u8,7u8,12u8,10u8,0u8,56u8, - 1u8,12u8,3u8,10u8,1u8,16u8,0u8,14u8,0u8,56u8,2u8,4u8,33u8,11u8,1u8,15u8,0u8,14u8,0u8,56u8, - 3u8,12u8,2u8,11u8,3u8,11u8,2u8,21u8,5u8,38u8,11u8,1u8,15u8,0u8,11u8,0u8,11u8,3u8,56u8,4u8, - 2u8,1u8,1u8,4u8,1u8,0u8,1u8,3u8,11u8,0u8,17u8,0u8,2u8,2u8,1u8,4u8,2u8,2u8,3u8,1u8, - 8u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,9u8,17u8,4u8,2u8,3u8,0u8,0u8,0u8,26u8, - 38u8,10u8,0u8,17u8,25u8,12u8,2u8,14u8,2u8,17u8,26u8,6u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 37u8,4u8,9u8,5u8,12u8,7u8,4u8,17u8,20u8,39u8,10u8,1u8,17u8,25u8,12u8,3u8,14u8,3u8,17u8,26u8, - 6u8,0u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,4u8,21u8,5u8,24u8,7u8,3u8,17u8,20u8,39u8,56u8, - 5u8,12u8,4u8,13u8,4u8,7u8,11u8,17u8,25u8,11u8,0u8,56u8,6u8,13u8,4u8,7u8,10u8,17u8,25u8,11u8, - 1u8,56u8,6u8,11u8,4u8,2u8,4u8,1u8,4u8,2u8,2u8,3u8,31u8,101u8,11u8,0u8,17u8,28u8,12u8,17u8, - 10u8,1u8,17u8,29u8,10u8,17u8,33u8,4u8,9u8,5u8,12u8,7u8,5u8,17u8,20u8,39u8,7u8,12u8,43u8,2u8, - 12u8,13u8,10u8,1u8,17u8,10u8,10u8,13u8,16u8,1u8,20u8,38u8,4u8,23u8,5u8,28u8,11u8,13u8,1u8,7u8, - 1u8,17u8,20u8,39u8,17u8,30u8,10u8,13u8,16u8,2u8,20u8,22u8,12u8,14u8,10u8,1u8,17u8,31u8,10u8,14u8, - 38u8,4u8,40u8,5u8,45u8,11u8,13u8,1u8,7u8,2u8,17u8,20u8,39u8,11u8,3u8,11u8,4u8,17u8,3u8,12u8, - 16u8,56u8,7u8,12u8,18u8,56u8,8u8,12u8,12u8,14u8,18u8,56u8,9u8,4u8,65u8,14u8,18u8,56u8,10u8,20u8, - 50u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,50u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,56u8,11u8,12u8,12u8, - 10u8,17u8,7u8,12u8,17u8,37u8,10u8,2u8,11u8,13u8,16u8,3u8,20u8,11u8,14u8,11u8,12u8,10u8,16u8,11u8, - 5u8,56u8,12u8,12u8,15u8,7u8,12u8,42u8,3u8,15u8,4u8,12u8,11u8,11u8,15u8,12u8,6u8,11u8,17u8,12u8, - 7u8,11u8,1u8,12u8,8u8,11u8,2u8,12u8,9u8,11u8,16u8,12u8,10u8,11u8,11u8,11u8,7u8,11u8,8u8,11u8, - 6u8,11u8,9u8,11u8,10u8,18u8,1u8,56u8,13u8,2u8,5u8,1u8,0u8,1u8,2u8,1u8,5u8,7u8,12u8,43u8, - 2u8,16u8,3u8,20u8,2u8,6u8,1u8,0u8,1u8,2u8,1u8,5u8,7u8,12u8,43u8,2u8,16u8,1u8,20u8,2u8, - 7u8,0u8,0u8,1u8,4u8,1u8,7u8,7u8,12u8,43u8,4u8,16u8,5u8,14u8,0u8,56u8,14u8,17u8,41u8,2u8, - 8u8,1u8,0u8,1u8,4u8,1u8,12u8,10u8,0u8,17u8,42u8,11u8,0u8,17u8,43u8,4u8,6u8,5u8,9u8,7u8, - 9u8,17u8,44u8,39u8,11u8,1u8,17u8,7u8,2u8,9u8,1u8,0u8,1u8,2u8,1u8,5u8,7u8,12u8,43u8,2u8, - 16u8,2u8,20u8,2u8,10u8,0u8,0u8,0u8,44u8,21u8,17u8,45u8,12u8,1u8,14u8,1u8,17u8,46u8,4u8,16u8, - 11u8,0u8,17u8,47u8,12u8,4u8,12u8,3u8,1u8,11u8,3u8,22u8,11u8,4u8,22u8,12u8,2u8,5u8,19u8,11u8, - 0u8,17u8,48u8,12u8,2u8,11u8,2u8,2u8,11u8,0u8,0u8,0u8,48u8,36u8,10u8,0u8,17u8,49u8,10u8,0u8, - 56u8,15u8,10u8,0u8,12u8,7u8,11u8,3u8,12u8,4u8,11u8,1u8,12u8,5u8,11u8,2u8,12u8,6u8,11u8,7u8, - 11u8,5u8,11u8,6u8,11u8,4u8,18u8,2u8,45u8,2u8,10u8,0u8,10u8,0u8,56u8,16u8,10u8,0u8,56u8,17u8, - 10u8,0u8,56u8,18u8,18u8,3u8,45u8,3u8,10u8,0u8,56u8,19u8,18u8,8u8,45u8,8u8,11u8,0u8,56u8,20u8, - 18u8,0u8,45u8,0u8,2u8,12u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,17u8,49u8,17u8,53u8,2u8,13u8,1u8, - 0u8,1u8,0u8,54u8,28u8,7u8,12u8,10u8,0u8,56u8,21u8,4u8,5u8,5u8,8u8,7u8,8u8,17u8,20u8,39u8, - 7u8,12u8,42u8,0u8,15u8,0u8,12u8,2u8,10u8,2u8,14u8,0u8,12u8,1u8,46u8,11u8,1u8,56u8,2u8,4u8, - 25u8,11u8,2u8,14u8,0u8,56u8,22u8,1u8,1u8,5u8,27u8,11u8,2u8,1u8,2u8,14u8,1u8,0u8,2u8,0u8, - 4u8,1u8,9u8,7u8,12u8,10u8,0u8,56u8,23u8,1u8,11u8,0u8,17u8,13u8,11u8,1u8,17u8,7u8,2u8,15u8, - 1u8,0u8,2u8,0u8,4u8,1u8,17u8,7u8,12u8,10u8,0u8,10u8,2u8,56u8,24u8,14u8,2u8,65u8,57u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,12u8,11u8,0u8,17u8,13u8,5u8,14u8,11u8,0u8,17u8, - 0u8,11u8,1u8,17u8,7u8,2u8,16u8,1u8,0u8,1u8,4u8,1u8,22u8,10u8,0u8,17u8,49u8,10u8,1u8,17u8, - 58u8,7u8,12u8,41u8,4u8,32u8,4u8,13u8,11u8,0u8,56u8,25u8,18u8,4u8,45u8,4u8,5u8,15u8,11u8,0u8, - 1u8,7u8,12u8,42u8,4u8,15u8,5u8,11u8,1u8,11u8,2u8,56u8,26u8,2u8,17u8,1u8,0u8,2u8,2u8,3u8, - 58u8,26u8,11u8,0u8,17u8,49u8,7u8,12u8,42u8,2u8,12u8,4u8,10u8,3u8,10u8,4u8,15u8,2u8,21u8,10u8, - 1u8,10u8,4u8,15u8,3u8,21u8,10u8,2u8,11u8,4u8,15u8,1u8,21u8,7u8,12u8,42u8,3u8,15u8,6u8,11u8, - 1u8,11u8,2u8,11u8,3u8,18u8,6u8,56u8,27u8,2u8,18u8,1u8,4u8,3u8,0u8,3u8,8u8,59u8,87u8,11u8, - 0u8,17u8,28u8,12u8,7u8,10u8,1u8,17u8,29u8,10u8,7u8,33u8,4u8,9u8,5u8,12u8,7u8,5u8,17u8,20u8, - 39u8,7u8,12u8,42u8,8u8,12u8,9u8,10u8,1u8,10u8,2u8,18u8,5u8,12u8,6u8,10u8,9u8,16u8,7u8,10u8, - 6u8,56u8,28u8,32u8,4u8,26u8,5u8,31u8,11u8,9u8,1u8,7u8,0u8,17u8,20u8,39u8,11u8,9u8,15u8,7u8, - 11u8,6u8,8u8,56u8,29u8,10u8,1u8,17u8,10u8,12u8,8u8,10u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,44u8,5u8,47u8,7u8,6u8,17u8,20u8,39u8,7u8,12u8,10u8,2u8,56u8,30u8,12u8,5u8, - 10u8,1u8,17u8,31u8,11u8,5u8,38u8,4u8,57u8,5u8,60u8,7u8,2u8,17u8,20u8,39u8,17u8,62u8,12u8,4u8, - 14u8,4u8,7u8,12u8,10u8,2u8,10u8,8u8,10u8,3u8,56u8,31u8,7u8,12u8,42u8,3u8,15u8,8u8,10u8,2u8, - 11u8,7u8,11u8,1u8,11u8,8u8,11u8,3u8,18u8,7u8,56u8,32u8,7u8,12u8,10u8,2u8,56u8,0u8,7u8,1u8, - 33u8,4u8,86u8,11u8,2u8,17u8,0u8,2u8,0u8,0u8,2u8,1u8,2u8,2u8,2u8,0u8,3u8,0u8,4u8,0u8, - 3u8,1u8,8u8,0u8,3u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk23); - let chunk24 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,24u8,2u8,24u8,24u8,3u8,48u8,150u8,1u8, - 4u8,198u8,1u8,14u8,5u8,212u8,1u8,113u8,7u8,197u8,2u8,198u8,7u8,8u8,139u8,10u8,32u8,6u8,171u8,10u8, - 108u8,16u8,151u8,11u8,129u8,3u8,10u8,152u8,14u8,47u8,12u8,199u8,14u8,132u8,4u8,13u8,203u8,18u8,14u8,15u8, - 217u8,18u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8, - 9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,8u8,0u8,0u8,14u8,6u8,0u8,0u8,15u8,6u8,0u8,3u8, - 18u8,4u8,1u8,6u8,1u8,5u8,38u8,7u8,1u8,0u8,0u8,0u8,16u8,0u8,1u8,0u8,0u8,17u8,2u8,1u8, - 0u8,0u8,19u8,3u8,1u8,0u8,0u8,20u8,4u8,1u8,0u8,0u8,21u8,1u8,5u8,0u8,0u8,22u8,1u8,5u8, - 0u8,0u8,23u8,6u8,1u8,0u8,0u8,24u8,6u8,1u8,0u8,9u8,39u8,8u8,1u8,0u8,7u8,40u8,9u8,10u8, - 0u8,2u8,41u8,5u8,5u8,0u8,5u8,42u8,1u8,11u8,1u8,0u8,7u8,43u8,9u8,5u8,0u8,5u8,44u8,12u8, - 11u8,1u8,0u8,3u8,45u8,14u8,5u8,1u8,6u8,4u8,46u8,1u8,10u8,0u8,11u8,47u8,1u8,1u8,0u8,11u8, - 48u8,9u8,1u8,0u8,7u8,49u8,15u8,1u8,0u8,6u8,50u8,1u8,5u8,0u8,8u8,51u8,5u8,1u8,0u8,6u8, - 52u8,1u8,5u8,0u8,6u8,53u8,1u8,1u8,0u8,10u8,54u8,18u8,1u8,0u8,2u8,55u8,5u8,5u8,0u8,3u8, - 56u8,19u8,1u8,1u8,6u8,10u8,57u8,1u8,5u8,0u8,9u8,58u8,8u8,1u8,0u8,1u8,59u8,8u8,20u8,1u8, - 6u8,11u8,5u8,13u8,5u8,14u8,13u8,25u8,13u8,28u8,13u8,28u8,21u8,25u8,21u8,8u8,12u8,5u8,3u8,3u8, - 5u8,10u8,3u8,10u8,2u8,3u8,0u8,1u8,12u8,3u8,6u8,12u8,7u8,11u8,3u8,1u8,8u8,1u8,8u8,1u8, - 2u8,6u8,12u8,5u8,1u8,3u8,2u8,6u8,12u8,3u8,4u8,1u8,7u8,8u8,0u8,8u8,1u8,11u8,4u8,1u8, - 3u8,1u8,6u8,12u8,1u8,5u8,1u8,1u8,1u8,11u8,4u8,1u8,9u8,0u8,1u8,9u8,0u8,1u8,8u8,1u8, - 1u8,6u8,11u8,3u8,1u8,9u8,0u8,2u8,11u8,4u8,1u8,3u8,10u8,3u8,1u8,7u8,8u8,0u8,1u8,2u8, - 3u8,6u8,12u8,5u8,3u8,2u8,7u8,11u8,3u8,1u8,9u8,0u8,9u8,0u8,1u8,11u8,3u8,1u8,9u8,0u8, - 1u8,8u8,2u8,2u8,7u8,8u8,0u8,3u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,5u8,98u8,108u8,111u8, - 99u8,107u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8, - 110u8,116u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,15u8,114u8, - 101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,5u8,115u8,116u8,97u8,107u8,101u8, - 13u8,115u8,116u8,97u8,116u8,101u8,95u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,16u8,115u8,121u8,115u8,116u8,101u8, - 109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8, - 112u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,13u8,66u8,108u8, - 111u8,99u8,107u8,82u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,13u8,78u8,101u8,119u8,66u8,108u8,111u8,99u8,107u8, - 69u8,118u8,101u8,110u8,116u8,24u8,85u8,112u8,100u8,97u8,116u8,101u8,69u8,112u8,111u8,99u8,104u8,73u8,110u8,116u8, - 101u8,114u8,118u8,97u8,108u8,69u8,118u8,101u8,110u8,116u8,14u8,98u8,108u8,111u8,99u8,107u8,95u8,112u8,114u8,111u8, - 108u8,111u8,103u8,117u8,101u8,24u8,101u8,109u8,105u8,116u8,95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,95u8,98u8, - 108u8,111u8,99u8,107u8,95u8,101u8,118u8,101u8,110u8,116u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8, - 108u8,101u8,20u8,101u8,109u8,105u8,116u8,95u8,110u8,101u8,119u8,95u8,98u8,108u8,111u8,99u8,107u8,95u8,101u8,118u8, - 101u8,110u8,116u8,25u8,101u8,109u8,105u8,116u8,95u8,119u8,114u8,105u8,116u8,101u8,115u8,101u8,116u8,95u8,98u8,108u8, - 111u8,99u8,107u8,95u8,101u8,118u8,101u8,110u8,116u8,24u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8, - 116u8,95u8,98u8,108u8,111u8,99u8,107u8,95u8,104u8,101u8,105u8,103u8,104u8,116u8,23u8,103u8,101u8,116u8,95u8,101u8, - 112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8,118u8,97u8,108u8,95u8,115u8,101u8,99u8,115u8,10u8,105u8, - 110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,31u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,101u8,112u8,111u8, - 99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8,118u8,97u8,108u8,95u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8, - 115u8,6u8,104u8,101u8,105u8,103u8,104u8,116u8,14u8,101u8,112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8, - 118u8,97u8,108u8,16u8,110u8,101u8,119u8,95u8,98u8,108u8,111u8,99u8,107u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8, - 28u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8,118u8, - 97u8,108u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,4u8,104u8,97u8,115u8,104u8,5u8,101u8,112u8,111u8,99u8,104u8, - 5u8,114u8,111u8,117u8,110u8,100u8,27u8,112u8,114u8,101u8,118u8,105u8,111u8,117u8,115u8,95u8,98u8,108u8,111u8,99u8, - 107u8,95u8,118u8,111u8,116u8,101u8,115u8,95u8,98u8,105u8,116u8,118u8,101u8,99u8,8u8,112u8,114u8,111u8,112u8,111u8, - 115u8,101u8,114u8,23u8,102u8,97u8,105u8,108u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,95u8, - 105u8,110u8,100u8,105u8,99u8,101u8,115u8,17u8,116u8,105u8,109u8,101u8,95u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8, - 99u8,111u8,110u8,100u8,115u8,18u8,111u8,108u8,100u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8, - 114u8,118u8,97u8,108u8,18u8,110u8,101u8,119u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8, - 118u8,97u8,108u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,9u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,118u8,109u8, - 26u8,105u8,115u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,111u8,114u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8, - 101u8,110u8,105u8,101u8,100u8,4u8,110u8,111u8,110u8,101u8,19u8,103u8,101u8,116u8,95u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,111u8,114u8,95u8,105u8,110u8,100u8,101u8,120u8,4u8,115u8,111u8,109u8,101u8,7u8,99u8,111u8,117u8,110u8, - 116u8,101u8,114u8,31u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8, - 114u8,105u8,98u8,117u8,116u8,101u8,95u8,103u8,97u8,115u8,95u8,102u8,101u8,101u8,115u8,22u8,112u8,114u8,111u8,99u8, - 101u8,115u8,115u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,101u8,100u8,95u8,102u8,101u8,101u8,115u8,36u8,114u8, - 101u8,103u8,105u8,115u8,116u8,101u8,114u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,95u8,102u8,111u8,114u8, - 95u8,102u8,101u8,101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,29u8,117u8,112u8,100u8,97u8, - 116u8,101u8,95u8,112u8,101u8,114u8,102u8,111u8,114u8,109u8,97u8,110u8,99u8,101u8,95u8,115u8,116u8,97u8,116u8,105u8, - 115u8,116u8,105u8,99u8,115u8,13u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,12u8, - 111u8,110u8,95u8,110u8,101u8,119u8,95u8,98u8,108u8,111u8,99u8,107u8,25u8,108u8,97u8,115u8,116u8,95u8,114u8,101u8, - 99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,116u8,105u8,109u8,101u8,11u8,114u8, - 101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,18u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,103u8,108u8, - 111u8,98u8,97u8,108u8,95u8,116u8,105u8,109u8,101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8, - 103u8,117u8,109u8,101u8,110u8,116u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,16u8,110u8,111u8, - 119u8,95u8,109u8,105u8,99u8,114u8,111u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,22u8,97u8,115u8,115u8,101u8,114u8, - 116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,16u8,110u8,101u8, - 119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,255u8,255u8, - 255u8,255u8,255u8,255u8,255u8,255u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8, - 58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,236u8,2u8,3u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,49u8,69u8,78u8,85u8,77u8,95u8,78u8,69u8,87u8,95u8,66u8,76u8,79u8,67u8,75u8,95u8, - 69u8,86u8,69u8,78u8,84u8,83u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8, - 72u8,95u8,66u8,76u8,79u8,67u8,75u8,95u8,72u8,69u8,73u8,71u8,72u8,84u8,71u8,84u8,104u8,101u8,32u8,110u8, - 117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,110u8,101u8,119u8,32u8,98u8,108u8,111u8,99u8,107u8,32u8,101u8, - 118u8,101u8,110u8,116u8,115u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,113u8,117u8,97u8,108u8, - 32u8,116u8,104u8,101u8,32u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,98u8,108u8,111u8,99u8,107u8,32u8,104u8, - 101u8,105u8,103u8,104u8,116u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,69u8,73u8,78u8,86u8,65u8, - 76u8,73u8,68u8,95u8,80u8,82u8,79u8,80u8,79u8,83u8,69u8,82u8,92u8,65u8,110u8,32u8,105u8,110u8,118u8,97u8, - 108u8,105u8,100u8,32u8,112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,32u8,119u8,97u8,115u8,32u8,112u8,114u8,111u8, - 118u8,105u8,100u8,101u8,100u8,46u8,32u8,69u8,120u8,112u8,101u8,99u8,116u8,101u8,100u8,32u8,116u8,104u8,101u8,32u8, - 112u8,114u8,111u8,112u8,111u8,115u8,101u8,114u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,116u8,104u8,101u8,32u8,86u8, - 77u8,32u8,111u8,114u8,32u8,97u8,110u8,32u8,97u8,99u8,116u8,105u8,118u8,101u8,32u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,111u8,114u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,90u8,69u8,82u8,79u8,95u8, - 69u8,80u8,79u8,67u8,72u8,95u8,73u8,78u8,84u8,69u8,82u8,86u8,65u8,76u8,27u8,69u8,112u8,111u8,99u8,104u8, - 32u8,105u8,110u8,116u8,101u8,114u8,118u8,97u8,108u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8, - 48u8,46u8,0u8,2u8,23u8,103u8,101u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8,105u8,110u8,116u8,101u8,114u8, - 118u8,97u8,108u8,95u8,115u8,101u8,99u8,115u8,1u8,1u8,0u8,24u8,103u8,101u8,116u8,95u8,99u8,117u8,114u8,114u8, - 101u8,110u8,116u8,95u8,98u8,108u8,111u8,99u8,107u8,95u8,104u8,101u8,105u8,103u8,104u8,116u8,1u8,1u8,0u8,0u8, - 2u8,4u8,25u8,3u8,26u8,3u8,27u8,11u8,3u8,1u8,8u8,1u8,28u8,11u8,3u8,1u8,8u8,2u8,1u8,2u8, - 8u8,29u8,5u8,30u8,3u8,31u8,3u8,25u8,3u8,32u8,10u8,2u8,33u8,5u8,34u8,10u8,3u8,35u8,3u8,2u8, - 2u8,2u8,36u8,3u8,37u8,3u8,0u8,0u8,0u8,1u8,0u8,7u8,74u8,14u8,0u8,17u8,8u8,10u8,4u8,7u8, - 4u8,33u8,4u8,9u8,8u8,12u8,8u8,5u8,12u8,10u8,4u8,17u8,9u8,12u8,8u8,11u8,8u8,4u8,15u8,5u8, - 18u8,7u8,0u8,17u8,10u8,39u8,56u8,0u8,12u8,11u8,10u8,4u8,7u8,4u8,34u8,4u8,28u8,10u8,4u8,17u8, - 12u8,56u8,1u8,12u8,11u8,7u8,5u8,42u8,0u8,12u8,9u8,10u8,9u8,16u8,0u8,56u8,2u8,10u8,9u8,15u8, - 1u8,21u8,11u8,1u8,11u8,2u8,11u8,3u8,10u8,9u8,16u8,1u8,20u8,11u8,6u8,10u8,4u8,10u8,5u8,10u8, - 7u8,18u8,1u8,12u8,10u8,14u8,0u8,10u8,9u8,15u8,0u8,11u8,10u8,17u8,2u8,17u8,15u8,4u8,59u8,17u8, - 16u8,11u8,4u8,17u8,17u8,11u8,11u8,11u8,5u8,17u8,18u8,17u8,19u8,17u8,20u8,11u8,7u8,17u8,21u8,23u8, - 11u8,9u8,16u8,2u8,20u8,38u8,4u8,73u8,17u8,22u8,2u8,1u8,0u8,0u8,1u8,0u8,16u8,17u8,7u8,5u8, - 42u8,0u8,12u8,1u8,14u8,0u8,11u8,1u8,15u8,0u8,7u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,64u8, - 17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,4u8,64u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,1u8,17u8,2u8,2u8,2u8,0u8,0u8,0u8,1u8, - 26u8,11u8,0u8,14u8,2u8,16u8,3u8,20u8,14u8,2u8,16u8,4u8,20u8,17u8,23u8,10u8,1u8,46u8,56u8,2u8, - 14u8,2u8,16u8,5u8,20u8,33u8,4u8,17u8,5u8,22u8,11u8,1u8,1u8,7u8,1u8,17u8,24u8,39u8,11u8,1u8, - 11u8,2u8,56u8,3u8,2u8,3u8,1u8,0u8,1u8,0u8,16u8,26u8,11u8,0u8,17u8,8u8,7u8,5u8,42u8,0u8, - 12u8,2u8,10u8,2u8,16u8,0u8,56u8,2u8,10u8,2u8,15u8,1u8,21u8,10u8,2u8,15u8,0u8,11u8,1u8,17u8, - 19u8,7u8,3u8,11u8,2u8,16u8,1u8,20u8,64u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,4u8, - 64u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,26u8,18u8,1u8,56u8,3u8,2u8,4u8,1u8,0u8, - 1u8,0u8,1u8,5u8,7u8,5u8,43u8,0u8,16u8,1u8,20u8,2u8,5u8,1u8,0u8,1u8,0u8,1u8,7u8,7u8, - 5u8,43u8,0u8,16u8,2u8,20u8,6u8,64u8,66u8,15u8,0u8,0u8,0u8,0u8,0u8,26u8,2u8,6u8,3u8,0u8, - 0u8,1u8,22u8,10u8,0u8,17u8,27u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8, - 7u8,5u8,12u8,11u8,0u8,1u8,7u8,2u8,17u8,24u8,39u8,10u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,11u8,1u8,10u8,0u8,56u8,4u8,11u8,0u8,56u8,5u8,18u8,0u8,45u8,0u8,2u8,7u8,1u8,0u8, - 1u8,0u8,22u8,28u8,11u8,0u8,17u8,27u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8, - 4u8,7u8,5u8,10u8,7u8,2u8,17u8,24u8,39u8,7u8,5u8,42u8,0u8,12u8,2u8,10u8,2u8,16u8,2u8,20u8, - 12u8,3u8,10u8,1u8,10u8,2u8,15u8,2u8,21u8,11u8,2u8,15u8,6u8,11u8,3u8,11u8,1u8,18u8,2u8,56u8, - 6u8,2u8,0u8,2u8,0u8,0u8,0u8,1u8,1u8,5u8,1u8,7u8,1u8,3u8,0u8,3u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk24); - let chunk25 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,8u8,1u8,0u8,2u8,3u8,2u8,11u8,4u8,13u8,2u8,5u8, - 15u8,9u8,7u8,24u8,53u8,8u8,77u8,32u8,12u8,109u8,15u8,15u8,124u8,4u8,0u8,2u8,0u8,3u8,0u8,1u8, - 0u8,0u8,4u8,0u8,2u8,1u8,0u8,1u8,1u8,1u8,10u8,2u8,1u8,5u8,1u8,9u8,0u8,0u8,4u8,99u8, - 111u8,100u8,101u8,12u8,103u8,97u8,115u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,4u8,117u8,116u8,105u8, - 108u8,18u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8, - 10u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,1u8,0u8,1u8,0u8,0u8,3u8,3u8,11u8,0u8,56u8,0u8,2u8,1u8,3u8,2u8,0u8,0u8,0u8, - 0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk25); - let chunk26 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,20u8,2u8,20u8,38u8,3u8,58u8,117u8,4u8, - 175u8,1u8,4u8,5u8,179u8,1u8,212u8,1u8,7u8,135u8,3u8,250u8,5u8,8u8,129u8,9u8,32u8,6u8,161u8,9u8, - 168u8,3u8,16u8,201u8,12u8,250u8,5u8,10u8,195u8,18u8,77u8,12u8,144u8,19u8,210u8,10u8,13u8,226u8,29u8,20u8, - 0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8, - 0u8,10u8,2u8,0u8,0u8,11u8,6u8,0u8,0u8,12u8,7u8,0u8,0u8,13u8,6u8,0u8,0u8,14u8,14u8,0u8, - 0u8,15u8,7u8,0u8,6u8,17u8,7u8,0u8,4u8,37u8,7u8,1u8,0u8,0u8,1u8,38u8,7u8,0u8,0u8,16u8, - 0u8,1u8,0u8,0u8,18u8,2u8,3u8,0u8,0u8,19u8,4u8,5u8,0u8,0u8,20u8,6u8,3u8,0u8,0u8,21u8, - 7u8,8u8,0u8,0u8,22u8,9u8,3u8,0u8,0u8,23u8,10u8,1u8,0u8,0u8,24u8,11u8,3u8,0u8,0u8,25u8, - 12u8,3u8,0u8,0u8,26u8,13u8,3u8,0u8,0u8,27u8,14u8,3u8,0u8,0u8,28u8,3u8,15u8,0u8,0u8,29u8, - 3u8,15u8,0u8,0u8,30u8,3u8,15u8,0u8,2u8,48u8,19u8,19u8,0u8,2u8,49u8,19u8,19u8,0u8,6u8,50u8, - 23u8,18u8,0u8,2u8,51u8,19u8,19u8,0u8,9u8,52u8,26u8,1u8,1u8,0u8,7u8,53u8,28u8,3u8,0u8,5u8, - 54u8,28u8,10u8,0u8,3u8,55u8,3u8,1u8,0u8,8u8,56u8,23u8,31u8,1u8,0u8,18u8,18u8,22u8,24u8,2u8, - 8u8,5u8,8u8,5u8,1u8,1u8,2u8,6u8,8u8,3u8,6u8,10u8,8u8,6u8,0u8,2u8,5u8,6u8,8u8,3u8, - 1u8,10u8,8u8,0u8,3u8,6u8,8u8,3u8,6u8,8u8,3u8,6u8,10u8,8u8,6u8,1u8,6u8,8u8,3u8,1u8, - 10u8,8u8,6u8,3u8,6u8,12u8,6u8,12u8,8u8,3u8,1u8,5u8,3u8,6u8,12u8,8u8,3u8,10u8,10u8,2u8, - 3u8,6u8,12u8,10u8,2u8,10u8,10u8,2u8,4u8,5u8,10u8,8u8,6u8,10u8,10u8,2u8,2u8,5u8,5u8,10u8, - 8u8,6u8,10u8,8u8,0u8,10u8,10u8,2u8,2u8,1u8,8u8,5u8,4u8,3u8,3u8,6u8,8u8,6u8,6u8,8u8, - 1u8,1u8,8u8,1u8,1u8,8u8,6u8,1u8,3u8,16u8,5u8,5u8,10u8,8u8,0u8,6u8,8u8,2u8,6u8,8u8, - 3u8,6u8,10u8,8u8,2u8,1u8,3u8,3u8,3u8,3u8,8u8,6u8,8u8,6u8,3u8,3u8,6u8,8u8,4u8,1u8, - 8u8,0u8,1u8,8u8,2u8,1u8,10u8,2u8,1u8,8u8,3u8,3u8,8u8,5u8,3u8,10u8,8u8,6u8,2u8,6u8, - 10u8,9u8,0u8,6u8,9u8,0u8,2u8,3u8,10u8,8u8,6u8,1u8,6u8,12u8,9u8,1u8,1u8,1u8,1u8,1u8, - 1u8,1u8,1u8,1u8,12u8,8u8,5u8,3u8,5u8,10u8,8u8,0u8,3u8,3u8,3u8,10u8,8u8,6u8,6u8,8u8, - 3u8,7u8,10u8,8u8,3u8,8u8,5u8,3u8,1u8,9u8,0u8,4u8,99u8,111u8,100u8,101u8,12u8,99u8,111u8,112u8, - 121u8,97u8,98u8,108u8,101u8,95u8,97u8,110u8,121u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,101u8,97u8,116u8, - 117u8,114u8,101u8,115u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8, - 116u8,114u8,105u8,110u8,103u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8, - 101u8,115u8,4u8,117u8,116u8,105u8,108u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,10u8,65u8,108u8,108u8,111u8,119u8, - 101u8,100u8,68u8,101u8,112u8,14u8,77u8,111u8,100u8,117u8,108u8,101u8,77u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8, - 10u8,80u8,97u8,99u8,107u8,97u8,103u8,101u8,68u8,101u8,112u8,15u8,80u8,97u8,99u8,107u8,97u8,103u8,101u8,77u8, - 101u8,116u8,97u8,100u8,97u8,116u8,97u8,15u8,80u8,97u8,99u8,107u8,97u8,103u8,101u8,82u8,101u8,103u8,105u8,115u8, - 116u8,114u8,121u8,13u8,85u8,112u8,103u8,114u8,97u8,100u8,101u8,80u8,111u8,108u8,105u8,99u8,121u8,28u8,99u8,97u8, - 110u8,95u8,99u8,104u8,97u8,110u8,103u8,101u8,95u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,95u8,112u8,111u8,108u8, - 105u8,99u8,121u8,95u8,116u8,111u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,17u8,99u8,104u8,101u8,99u8,107u8,95u8, - 99u8,111u8,101u8,120u8,105u8,115u8,116u8,101u8,110u8,99u8,101u8,18u8,99u8,104u8,101u8,99u8,107u8,95u8,100u8,101u8, - 112u8,101u8,110u8,100u8,101u8,110u8,99u8,105u8,101u8,115u8,19u8,99u8,104u8,101u8,99u8,107u8,95u8,117u8,112u8,103u8, - 114u8,97u8,100u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,16u8,103u8,101u8,116u8,95u8,109u8,111u8,100u8,117u8,108u8, - 101u8,95u8,110u8,97u8,109u8,101u8,115u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,26u8,105u8, - 115u8,95u8,112u8,111u8,108u8,105u8,99u8,121u8,95u8,101u8,120u8,101u8,109u8,112u8,116u8,101u8,100u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,15u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,95u8,112u8,97u8,99u8,107u8,97u8,103u8, - 101u8,19u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,95u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,95u8,116u8,120u8, - 110u8,15u8,114u8,101u8,113u8,117u8,101u8,115u8,116u8,95u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,33u8,114u8,101u8, - 113u8,117u8,101u8,115u8,116u8,95u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,95u8,119u8,105u8,116u8,104u8,95u8,97u8, - 108u8,108u8,111u8,119u8,101u8,100u8,95u8,100u8,101u8,112u8,115u8,24u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,95u8, - 112u8,111u8,108u8,105u8,99u8,121u8,95u8,97u8,114u8,98u8,105u8,116u8,114u8,97u8,114u8,121u8,21u8,117u8,112u8,103u8, - 114u8,97u8,100u8,101u8,95u8,112u8,111u8,108u8,105u8,99u8,121u8,95u8,99u8,111u8,109u8,112u8,97u8,116u8,24u8,117u8, - 112u8,103u8,114u8,97u8,100u8,101u8,95u8,112u8,111u8,108u8,105u8,99u8,121u8,95u8,105u8,109u8,109u8,117u8,116u8,97u8, - 98u8,108u8,101u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,11u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,110u8, - 97u8,109u8,101u8,4u8,110u8,97u8,109u8,101u8,6u8,115u8,111u8,117u8,114u8,99u8,101u8,10u8,115u8,111u8,117u8,114u8, - 99u8,101u8,95u8,109u8,97u8,112u8,9u8,101u8,120u8,116u8,101u8,110u8,115u8,105u8,111u8,110u8,6u8,79u8,112u8,116u8, - 105u8,111u8,110u8,3u8,65u8,110u8,121u8,12u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,95u8,110u8,97u8,109u8,101u8, - 14u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,95u8,112u8,111u8,108u8,105u8,99u8,121u8,14u8,117u8,112u8,103u8,114u8, - 97u8,100u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,13u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,100u8,105u8, - 103u8,101u8,115u8,116u8,8u8,109u8,97u8,110u8,105u8,102u8,101u8,115u8,116u8,7u8,109u8,111u8,100u8,117u8,108u8,101u8, - 115u8,4u8,100u8,101u8,112u8,115u8,8u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,115u8,6u8,112u8,111u8,108u8,105u8, - 99u8,121u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,9u8,110u8,111u8, - 116u8,95u8,102u8,111u8,117u8,110u8,100u8,4u8,117u8,116u8,102u8,56u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8, - 95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,22u8,97u8, - 115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8, - 107u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,29u8,99u8,111u8,100u8,101u8,95u8,100u8,101u8, - 112u8,101u8,110u8,100u8,101u8,110u8,99u8,121u8,95u8,99u8,104u8,101u8,99u8,107u8,95u8,101u8,110u8,97u8,98u8,108u8, - 101u8,100u8,10u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8,101u8,115u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,10u8,2u8,1u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,1u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,5u8,32u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,4u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,5u8, - 32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,7u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 8u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,9u8,5u8,32u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8, - 97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,229u8,5u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8, - 69u8,77u8,79u8,68u8,85u8,76u8,69u8,95u8,78u8,65u8,77u8,69u8,95u8,67u8,76u8,65u8,83u8,72u8,104u8,80u8, - 97u8,99u8,107u8,97u8,103u8,101u8,32u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,32u8,100u8,117u8,112u8,108u8, - 105u8,99u8,97u8,116u8,101u8,32u8,109u8,111u8,100u8,117u8,108u8,101u8,32u8,110u8,97u8,109u8,101u8,115u8,32u8,119u8, - 105u8,116u8,104u8,32u8,101u8,120u8,105u8,115u8,116u8,105u8,110u8,103u8,32u8,109u8,111u8,100u8,117u8,108u8,101u8,115u8, - 32u8,112u8,117u8,98u8,108u8,105u8,115u8,101u8,100u8,32u8,105u8,110u8,32u8,111u8,116u8,104u8,101u8,114u8,32u8,112u8, - 97u8,99u8,107u8,97u8,103u8,101u8,115u8,32u8,111u8,110u8,32u8,116u8,104u8,105u8,115u8,32u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,85u8,80u8,71u8,82u8,65u8,68u8,69u8, - 95u8,73u8,77u8,77u8,85u8,84u8,65u8,66u8,76u8,69u8,35u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,117u8,112u8, - 103u8,114u8,97u8,100u8,101u8,32u8,97u8,110u8,32u8,105u8,109u8,109u8,117u8,116u8,97u8,98u8,108u8,101u8,32u8,112u8, - 97u8,99u8,107u8,97u8,103u8,101u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,85u8,80u8,71u8,82u8, - 65u8,68u8,69u8,95u8,87u8,69u8,65u8,75u8,69u8,82u8,95u8,80u8,79u8,76u8,73u8,67u8,89u8,49u8,67u8,97u8, - 110u8,110u8,111u8,116u8,32u8,100u8,111u8,119u8,110u8,103u8,114u8,97u8,100u8,101u8,32u8,97u8,32u8,112u8,97u8,99u8, - 107u8,97u8,103u8,101u8,39u8,115u8,32u8,117u8,112u8,103u8,114u8,97u8,100u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 32u8,112u8,111u8,108u8,105u8,99u8,121u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,77u8,79u8,68u8, - 85u8,76u8,69u8,95u8,77u8,73u8,83u8,83u8,73u8,78u8,71u8,61u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,100u8, - 101u8,108u8,101u8,116u8,101u8,32u8,97u8,32u8,109u8,111u8,100u8,117u8,108u8,101u8,32u8,116u8,104u8,97u8,116u8,32u8, - 119u8,97u8,115u8,32u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8, - 32u8,115u8,97u8,109u8,101u8,32u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,20u8,69u8,80u8,65u8,67u8,75u8,65u8,71u8,69u8,95u8,68u8,69u8,80u8,95u8,77u8,73u8,83u8,83u8,73u8, - 78u8,71u8,58u8,68u8,101u8,112u8,101u8,110u8,100u8,101u8,110u8,99u8,121u8,32u8,99u8,111u8,117u8,108u8,100u8,32u8, - 110u8,111u8,116u8,32u8,98u8,101u8,32u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,32u8,116u8,111u8,32u8,97u8, - 110u8,121u8,32u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,32u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8, - 46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,68u8,69u8,80u8,95u8,87u8,69u8,65u8,75u8,69u8, - 82u8,95u8,80u8,79u8,76u8,73u8,67u8,89u8,49u8,65u8,32u8,100u8,101u8,112u8,101u8,110u8,100u8,101u8,110u8,99u8, - 121u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,97u8,32u8,119u8,101u8,97u8,107u8, - 101u8,114u8,32u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,32u8,112u8,111u8,108u8,105u8,99u8,121u8,46u8,7u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,31u8,69u8,68u8,69u8,80u8,95u8,65u8,82u8,66u8,73u8,84u8,82u8,65u8,82u8, - 89u8,95u8,78u8,79u8,84u8,95u8,83u8,65u8,77u8,69u8,95u8,65u8,68u8,68u8,82u8,69u8,83u8,83u8,67u8,65u8, - 32u8,100u8,101u8,112u8,101u8,110u8,100u8,101u8,110u8,99u8,121u8,32u8,116u8,111u8,32u8,97u8,110u8,32u8,96u8,97u8, - 114u8,98u8,105u8,116u8,114u8,97u8,114u8,121u8,96u8,32u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,32u8,109u8,117u8, - 115u8,116u8,32u8,98u8,101u8,32u8,111u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8,109u8,101u8,32u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,46u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,73u8,78u8,67u8,79u8, - 77u8,80u8,65u8,84u8,73u8,66u8,76u8,69u8,95u8,80u8,79u8,76u8,73u8,67u8,89u8,95u8,68u8,73u8,83u8,65u8, - 66u8,76u8,69u8,68u8,64u8,67u8,114u8,101u8,97u8,116u8,105u8,110u8,103u8,32u8,97u8,32u8,112u8,97u8,99u8,107u8, - 97u8,103u8,101u8,32u8,119u8,105u8,116u8,104u8,32u8,105u8,110u8,99u8,111u8,109u8,112u8,97u8,116u8,105u8,98u8,108u8, - 101u8,32u8,117u8,112u8,103u8,114u8,97u8,100u8,101u8,32u8,112u8,111u8,108u8,105u8,99u8,121u8,32u8,105u8,115u8,32u8, - 100u8,105u8,115u8,97u8,98u8,108u8,101u8,100u8,46u8,0u8,0u8,0u8,2u8,2u8,31u8,5u8,32u8,8u8,6u8,1u8, - 2u8,4u8,33u8,8u8,6u8,34u8,10u8,2u8,35u8,10u8,2u8,36u8,11u8,7u8,1u8,8u8,8u8,2u8,2u8,2u8, - 31u8,5u8,39u8,8u8,6u8,3u8,2u8,8u8,33u8,8u8,6u8,40u8,8u8,5u8,41u8,3u8,42u8,8u8,6u8,43u8, - 10u8,2u8,44u8,10u8,8u8,1u8,45u8,10u8,8u8,2u8,36u8,11u8,7u8,1u8,8u8,8u8,4u8,2u8,1u8,46u8, - 10u8,8u8,3u8,5u8,2u8,1u8,47u8,2u8,0u8,1u8,0u8,0u8,3u8,8u8,14u8,0u8,16u8,0u8,20u8,14u8, - 1u8,16u8,0u8,20u8,37u8,2u8,1u8,0u8,0u8,0u8,16u8,58u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,2u8,10u8,2u8,10u8,0u8,16u8,1u8,65u8,17u8,35u8,4u8,53u8,5u8,9u8,10u8,0u8,16u8,1u8, - 10u8,2u8,66u8,17u8,12u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,3u8,10u8, - 1u8,65u8,18u8,35u8,4u8,46u8,5u8,22u8,10u8,1u8,10u8,3u8,66u8,18u8,12u8,4u8,10u8,5u8,16u8,2u8, - 11u8,4u8,34u8,4u8,32u8,5u8,41u8,11u8,0u8,1u8,11u8,5u8,1u8,11u8,1u8,1u8,7u8,4u8,17u8,14u8, - 39u8,11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,16u8,11u8,5u8,1u8, - 11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,2u8,11u8,0u8,1u8,11u8, - 1u8,1u8,2u8,2u8,0u8,0u8,1u8,4u8,20u8,202u8,1u8,64u8,21u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,4u8,10u8,1u8,16u8,3u8,12u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,9u8, - 10u8,7u8,65u8,22u8,12u8,15u8,10u8,9u8,10u8,15u8,35u8,4u8,196u8,1u8,5u8,15u8,10u8,7u8,10u8,9u8, - 66u8,22u8,12u8,5u8,10u8,5u8,16u8,4u8,20u8,41u8,4u8,4u8,25u8,5u8,34u8,11u8,1u8,1u8,11u8,7u8, - 1u8,11u8,5u8,1u8,7u8,5u8,17u8,15u8,39u8,10u8,5u8,16u8,4u8,20u8,17u8,6u8,4u8,56u8,11u8,5u8, - 16u8,4u8,20u8,12u8,2u8,7u8,8u8,17u8,16u8,12u8,13u8,13u8,4u8,11u8,2u8,11u8,13u8,18u8,0u8,68u8, - 21u8,11u8,9u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,9u8,5u8,10u8,10u8,5u8,16u8, - 4u8,20u8,43u8,4u8,12u8,17u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,10u8,10u8,17u8,16u8, - 5u8,65u8,24u8,12u8,12u8,9u8,12u8,8u8,10u8,10u8,10u8,12u8,35u8,4u8,181u8,1u8,5u8,74u8,10u8,17u8, - 16u8,5u8,10u8,10u8,66u8,24u8,12u8,6u8,10u8,6u8,16u8,6u8,20u8,10u8,5u8,16u8,7u8,20u8,33u8,4u8, - 174u8,1u8,11u8,17u8,1u8,8u8,12u8,8u8,10u8,6u8,16u8,8u8,16u8,0u8,20u8,10u8,1u8,16u8,8u8,16u8, - 0u8,20u8,38u8,4u8,102u8,5u8,113u8,11u8,1u8,1u8,11u8,7u8,1u8,11u8,6u8,1u8,11u8,5u8,1u8,7u8, - 1u8,17u8,17u8,39u8,10u8,6u8,16u8,8u8,20u8,17u8,11u8,33u8,4u8,137u8,1u8,10u8,5u8,16u8,4u8,20u8, - 10u8,0u8,33u8,4u8,126u8,5u8,137u8,1u8,11u8,1u8,1u8,11u8,7u8,1u8,11u8,6u8,1u8,11u8,5u8,1u8, - 7u8,0u8,17u8,17u8,39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,11u8,10u8,6u8,16u8,1u8, - 65u8,17u8,12u8,16u8,10u8,11u8,10u8,16u8,35u8,4u8,169u8,1u8,5u8,148u8,1u8,10u8,5u8,16u8,4u8,20u8, - 12u8,3u8,10u8,6u8,16u8,1u8,10u8,11u8,66u8,17u8,16u8,2u8,20u8,12u8,14u8,13u8,4u8,11u8,3u8,11u8, - 14u8,18u8,0u8,68u8,21u8,11u8,11u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,11u8,5u8, - 143u8,1u8,11u8,6u8,1u8,11u8,5u8,1u8,5u8,181u8,1u8,11u8,6u8,1u8,11u8,10u8,6u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,22u8,12u8,10u8,5u8,69u8,11u8,8u8,4u8,184u8,1u8,5u8,191u8,1u8,11u8,1u8, - 1u8,11u8,7u8,1u8,7u8,5u8,17u8,15u8,39u8,11u8,9u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,12u8,9u8,5u8,10u8,11u8,1u8,1u8,11u8,7u8,1u8,11u8,4u8,2u8,3u8,0u8,0u8,0u8,25u8,67u8, - 17u8,13u8,12u8,3u8,10u8,0u8,16u8,8u8,16u8,0u8,20u8,14u8,3u8,16u8,0u8,20u8,35u8,4u8,12u8,5u8, - 21u8,11u8,0u8,1u8,11u8,1u8,1u8,11u8,2u8,1u8,7u8,6u8,17u8,17u8,39u8,10u8,0u8,16u8,8u8,20u8, - 11u8,1u8,16u8,8u8,20u8,17u8,0u8,4u8,30u8,5u8,37u8,11u8,0u8,1u8,11u8,2u8,1u8,7u8,7u8,17u8, - 17u8,39u8,11u8,0u8,17u8,4u8,12u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8, - 4u8,14u8,5u8,65u8,18u8,35u8,4u8,64u8,5u8,48u8,10u8,2u8,14u8,5u8,10u8,4u8,66u8,18u8,56u8,0u8, - 4u8,55u8,5u8,59u8,11u8,2u8,1u8,7u8,3u8,39u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,22u8,12u8,4u8,5u8,42u8,11u8,2u8,1u8,2u8,4u8,0u8,0u8,0u8,27u8,28u8,64u8,18u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8, - 1u8,10u8,0u8,16u8,1u8,65u8,17u8,35u8,4u8,24u8,5u8,11u8,13u8,2u8,10u8,0u8,16u8,1u8,10u8,1u8, - 66u8,17u8,16u8,2u8,20u8,68u8,18u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8, - 1u8,5u8,4u8,11u8,0u8,1u8,11u8,2u8,2u8,5u8,0u8,0u8,1u8,4u8,10u8,23u8,11u8,0u8,17u8,19u8, - 10u8,1u8,17u8,20u8,12u8,3u8,10u8,3u8,41u8,4u8,32u8,4u8,15u8,11u8,1u8,11u8,2u8,64u8,24u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,4u8,45u8,4u8,5u8,22u8,11u8,1u8,1u8,11u8,3u8,42u8,4u8, - 15u8,5u8,11u8,2u8,68u8,24u8,2u8,6u8,0u8,0u8,0u8,29u8,85u8,10u8,0u8,7u8,9u8,33u8,4u8,7u8, - 8u8,12u8,1u8,5u8,11u8,10u8,0u8,7u8,10u8,33u8,12u8,1u8,11u8,1u8,4u8,16u8,8u8,12u8,2u8,5u8, - 20u8,10u8,0u8,7u8,11u8,33u8,12u8,2u8,11u8,2u8,4u8,25u8,8u8,12u8,3u8,5u8,29u8,10u8,0u8,7u8, - 12u8,33u8,12u8,3u8,11u8,3u8,4u8,34u8,8u8,12u8,4u8,5u8,38u8,10u8,0u8,7u8,13u8,33u8,12u8,4u8, - 11u8,4u8,4u8,43u8,8u8,12u8,5u8,5u8,47u8,10u8,0u8,7u8,14u8,33u8,12u8,5u8,11u8,5u8,4u8,52u8, - 8u8,12u8,6u8,5u8,56u8,10u8,0u8,7u8,15u8,33u8,12u8,6u8,11u8,6u8,4u8,61u8,8u8,12u8,7u8,5u8, - 65u8,10u8,0u8,7u8,16u8,33u8,12u8,7u8,11u8,7u8,4u8,70u8,8u8,12u8,8u8,5u8,74u8,10u8,0u8,7u8, - 17u8,33u8,12u8,8u8,11u8,8u8,4u8,79u8,8u8,12u8,9u8,5u8,83u8,11u8,0u8,7u8,18u8,33u8,12u8,9u8, - 11u8,9u8,2u8,7u8,1u8,0u8,1u8,4u8,30u8,133u8,1u8,17u8,11u8,12u8,3u8,14u8,1u8,16u8,8u8,16u8, - 0u8,20u8,14u8,3u8,16u8,0u8,20u8,36u8,4u8,12u8,5u8,17u8,11u8,0u8,1u8,7u8,2u8,17u8,17u8,39u8, - 10u8,0u8,17u8,20u8,12u8,5u8,10u8,5u8,41u8,4u8,32u8,4u8,29u8,11u8,0u8,64u8,24u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,4u8,45u8,4u8,5u8,31u8,11u8,0u8,1u8,10u8,5u8,14u8,1u8,17u8,2u8, - 12u8,6u8,14u8,1u8,17u8,4u8,12u8,10u8,10u8,5u8,42u8,4u8,15u8,5u8,12u8,12u8,10u8,12u8,46u8,65u8, - 24u8,12u8,9u8,10u8,9u8,12u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,14u8,10u8,7u8,10u8,9u8,35u8,4u8,93u8,5u8,57u8,10u8,12u8, - 10u8,7u8,12u8,4u8,46u8,11u8,4u8,66u8,24u8,12u8,11u8,10u8,11u8,16u8,6u8,20u8,14u8,1u8,16u8,6u8, - 20u8,33u8,4u8,85u8,10u8,11u8,16u8,9u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8, - 14u8,11u8,11u8,14u8,1u8,14u8,10u8,17u8,3u8,10u8,7u8,12u8,8u8,5u8,88u8,11u8,11u8,14u8,10u8,17u8, - 1u8,11u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,7u8,5u8,52u8,11u8,14u8,13u8, - 1u8,15u8,9u8,21u8,14u8,1u8,16u8,8u8,20u8,12u8,13u8,10u8,8u8,11u8,9u8,35u8,4u8,111u8,11u8,1u8, - 11u8,12u8,11u8,8u8,67u8,24u8,21u8,5u8,114u8,11u8,12u8,11u8,1u8,68u8,24u8,17u8,21u8,4u8,125u8,11u8, - 5u8,11u8,10u8,11u8,6u8,11u8,2u8,14u8,13u8,16u8,0u8,20u8,17u8,10u8,5u8,132u8,1u8,11u8,5u8,11u8, - 10u8,11u8,2u8,14u8,13u8,16u8,0u8,20u8,17u8,9u8,2u8,8u8,1u8,4u8,1u8,4u8,3u8,6u8,11u8,0u8, - 11u8,1u8,56u8,1u8,11u8,2u8,17u8,7u8,2u8,9u8,0u8,2u8,0u8,10u8,0u8,2u8,0u8,11u8,1u8,0u8, - 0u8,3u8,3u8,49u8,0u8,18u8,5u8,2u8,12u8,1u8,0u8,0u8,3u8,3u8,49u8,1u8,18u8,5u8,2u8,13u8, - 1u8,0u8,0u8,3u8,3u8,49u8,2u8,18u8,5u8,2u8,5u8,0u8,3u8,5u8,1u8,0u8,3u8,6u8,2u8,0u8, - 4u8,0u8,3u8,0u8,2u8,1u8,3u8,1u8,3u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk26); - let chunk27 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,8u8,2u8,8u8,4u8,3u8,12u8,25u8,5u8, - 37u8,17u8,7u8,54u8,154u8,1u8,8u8,208u8,1u8,32u8,6u8,240u8,1u8,44u8,16u8,156u8,2u8,103u8,10u8,131u8, - 3u8,6u8,12u8,137u8,3u8,95u8,13u8,232u8,3u8,2u8,15u8,234u8,3u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8, - 0u8,4u8,0u8,5u8,8u8,0u8,0u8,6u8,0u8,1u8,0u8,0u8,7u8,0u8,1u8,0u8,3u8,9u8,2u8,1u8, - 0u8,1u8,10u8,4u8,4u8,0u8,2u8,11u8,1u8,1u8,0u8,2u8,6u8,12u8,10u8,2u8,0u8,1u8,6u8,12u8, - 1u8,2u8,1u8,3u8,1u8,7u8,10u8,2u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,16u8,99u8,111u8,110u8, - 115u8,101u8,110u8,115u8,117u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,5u8,101u8,114u8,114u8,111u8,114u8,15u8, - 114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,16u8,115u8,121u8,115u8,116u8, - 101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,15u8,67u8,111u8,110u8,115u8,101u8,110u8,115u8, - 117u8,115u8,67u8,111u8,110u8,102u8,105u8,103u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,3u8, - 115u8,101u8,116u8,6u8,99u8,111u8,110u8,102u8,105u8,103u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8, - 116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8, - 100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,11u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8, - 114u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8, - 112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,83u8,1u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,67u8,79u8,78u8, - 70u8,73u8,71u8,55u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,111u8,110u8,32u8, - 99u8,104u8,97u8,105u8,110u8,32u8,99u8,111u8,110u8,102u8,105u8,103u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8,97u8, - 114u8,101u8,32u8,101u8,109u8,112u8,116u8,121u8,32u8,111u8,114u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,0u8, - 0u8,0u8,2u8,1u8,8u8,10u8,2u8,0u8,3u8,0u8,0u8,1u8,18u8,10u8,0u8,17u8,2u8,14u8,1u8,65u8, - 3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,0u8, - 17u8,3u8,39u8,11u8,0u8,11u8,1u8,18u8,0u8,45u8,0u8,2u8,1u8,1u8,0u8,1u8,0u8,5u8,20u8,11u8, - 0u8,17u8,2u8,14u8,1u8,65u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,8u8,5u8, - 11u8,7u8,0u8,17u8,3u8,39u8,7u8,1u8,42u8,0u8,15u8,0u8,12u8,2u8,11u8,1u8,11u8,2u8,21u8,17u8, - 4u8,2u8,0u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk27); - let chunk28 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,28u8,2u8,28u8,62u8,3u8,90u8,217u8,3u8, - 4u8,179u8,4u8,46u8,5u8,225u8,4u8,175u8,3u8,7u8,144u8,8u8,141u8,18u8,8u8,157u8,26u8,32u8,6u8,189u8, - 26u8,220u8,1u8,16u8,153u8,28u8,171u8,13u8,10u8,196u8,41u8,120u8,12u8,188u8,42u8,216u8,21u8,13u8,148u8,64u8, - 28u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8, - 9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,6u8,0u8,0u8,15u8,8u8,0u8,0u8,16u8,12u8, - 0u8,0u8,17u8,6u8,0u8,0u8,18u8,7u8,0u8,0u8,19u8,6u8,0u8,0u8,20u8,6u8,0u8,0u8,21u8,6u8, - 0u8,7u8,23u8,4u8,0u8,11u8,69u8,4u8,2u8,3u8,1u8,0u8,1u8,1u8,72u8,6u8,0u8,5u8,75u8,4u8, - 1u8,6u8,1u8,2u8,88u8,8u8,0u8,10u8,104u8,11u8,0u8,0u8,22u8,0u8,1u8,0u8,0u8,24u8,2u8,3u8, - 0u8,0u8,25u8,4u8,1u8,0u8,0u8,26u8,5u8,1u8,0u8,0u8,27u8,5u8,1u8,0u8,0u8,28u8,4u8,1u8, - 0u8,0u8,29u8,6u8,3u8,0u8,0u8,30u8,7u8,8u8,0u8,0u8,31u8,4u8,9u8,0u8,0u8,32u8,2u8,10u8, - 0u8,0u8,33u8,11u8,10u8,0u8,0u8,34u8,4u8,9u8,0u8,0u8,35u8,12u8,1u8,0u8,0u8,36u8,13u8,10u8, - 0u8,0u8,37u8,4u8,14u8,0u8,0u8,38u8,4u8,4u8,0u8,0u8,39u8,15u8,16u8,0u8,0u8,40u8,7u8,4u8, - 0u8,0u8,41u8,15u8,17u8,0u8,0u8,42u8,18u8,1u8,0u8,0u8,43u8,17u8,10u8,0u8,0u8,44u8,4u8,10u8, - 0u8,0u8,45u8,10u8,19u8,0u8,0u8,46u8,4u8,10u8,0u8,0u8,47u8,4u8,9u8,0u8,0u8,48u8,7u8,20u8, - 0u8,0u8,49u8,21u8,22u8,0u8,0u8,50u8,5u8,23u8,0u8,0u8,51u8,0u8,1u8,0u8,0u8,52u8,6u8,10u8, - 0u8,0u8,53u8,24u8,10u8,0u8,0u8,54u8,7u8,25u8,0u8,0u8,55u8,26u8,1u8,0u8,0u8,56u8,26u8,1u8, - 0u8,0u8,57u8,4u8,10u8,0u8,0u8,58u8,4u8,1u8,0u8,0u8,59u8,10u8,3u8,0u8,0u8,60u8,0u8,1u8, - 0u8,0u8,61u8,0u8,1u8,0u8,0u8,62u8,6u8,1u8,0u8,8u8,87u8,28u8,4u8,0u8,3u8,89u8,0u8,1u8, - 1u8,0u8,9u8,22u8,30u8,1u8,0u8,7u8,90u8,31u8,3u8,0u8,5u8,91u8,33u8,1u8,1u8,6u8,7u8,92u8, - 34u8,10u8,0u8,7u8,93u8,34u8,3u8,0u8,7u8,94u8,35u8,3u8,0u8,4u8,95u8,10u8,10u8,0u8,4u8,96u8, - 10u8,10u8,0u8,11u8,97u8,38u8,39u8,2u8,3u8,2u8,4u8,98u8,10u8,10u8,0u8,9u8,41u8,4u8,14u8,0u8, - 7u8,99u8,20u8,10u8,0u8,9u8,100u8,4u8,10u8,0u8,12u8,101u8,1u8,10u8,0u8,9u8,102u8,4u8,10u8,0u8, - 7u8,103u8,42u8,10u8,0u8,9u8,105u8,4u8,9u8,0u8,10u8,106u8,1u8,45u8,0u8,10u8,107u8,46u8,41u8,0u8, - 11u8,108u8,49u8,50u8,2u8,3u8,0u8,7u8,109u8,51u8,10u8,0u8,1u8,110u8,52u8,4u8,0u8,7u8,111u8,20u8, - 3u8,0u8,7u8,112u8,54u8,10u8,0u8,9u8,113u8,4u8,4u8,0u8,6u8,114u8,1u8,9u8,0u8,4u8,115u8,10u8, - 10u8,0u8,13u8,116u8,57u8,1u8,1u8,0u8,1u8,117u8,58u8,59u8,0u8,3u8,118u8,28u8,1u8,1u8,0u8,9u8, - 119u8,60u8,1u8,0u8,11u8,120u8,1u8,61u8,2u8,3u8,4u8,7u8,121u8,10u8,62u8,0u8,11u8,122u8,38u8,1u8, - 2u8,3u8,0u8,1u8,123u8,28u8,63u8,1u8,6u8,11u8,124u8,68u8,39u8,2u8,3u8,0u8,11u8,125u8,49u8,9u8, - 2u8,3u8,0u8,9u8,51u8,30u8,1u8,0u8,7u8,126u8,70u8,10u8,0u8,11u8,127u8,68u8,72u8,2u8,3u8,0u8, - 7u8,128u8,1u8,62u8,1u8,0u8,1u8,129u8,1u8,52u8,25u8,0u8,9u8,55u8,26u8,1u8,0u8,9u8,56u8,26u8, - 1u8,0u8,7u8,130u8,1u8,20u8,10u8,0u8,7u8,131u8,1u8,75u8,1u8,0u8,9u8,60u8,30u8,1u8,0u8,9u8, - 61u8,30u8,1u8,0u8,41u8,29u8,44u8,32u8,50u8,37u8,61u8,48u8,69u8,56u8,71u8,29u8,73u8,48u8,75u8,48u8, - 73u8,37u8,76u8,32u8,76u8,64u8,76u8,65u8,76u8,66u8,76u8,67u8,77u8,48u8,78u8,37u8,61u8,37u8,44u8,64u8, - 81u8,37u8,81u8,48u8,44u8,67u8,44u8,65u8,44u8,66u8,3u8,6u8,12u8,5u8,3u8,0u8,3u8,6u8,8u8,8u8, - 5u8,3u8,1u8,4u8,1u8,5u8,2u8,6u8,8u8,1u8,5u8,3u8,7u8,8u8,1u8,5u8,3u8,1u8,6u8,8u8, - 1u8,5u8,1u8,3u8,3u8,3u8,3u8,1u8,1u8,1u8,3u8,4u8,6u8,8u8,8u8,6u8,8u8,8u8,5u8,3u8, - 2u8,7u8,8u8,1u8,5u8,2u8,5u8,3u8,4u8,3u8,3u8,3u8,3u8,2u8,5u8,5u8,2u8,1u8,3u8,3u8, - 3u8,3u8,3u8,3u8,6u8,12u8,3u8,10u8,2u8,1u8,8u8,4u8,1u8,6u8,8u8,8u8,1u8,7u8,8u8,1u8, - 1u8,7u8,8u8,8u8,2u8,1u8,8u8,4u8,4u8,7u8,8u8,1u8,5u8,3u8,8u8,4u8,1u8,12u8,2u8,6u8, - 12u8,5u8,5u8,12u8,5u8,3u8,5u8,7u8,8u8,1u8,1u8,6u8,12u8,1u8,8u8,12u8,2u8,6u8,12u8,3u8, - 3u8,7u8,8u8,8u8,5u8,3u8,1u8,8u8,0u8,2u8,7u8,11u8,11u8,1u8,9u8,0u8,9u8,0u8,2u8,6u8, - 8u8,8u8,5u8,2u8,6u8,8u8,8u8,3u8,2u8,4u8,8u8,4u8,2u8,5u8,8u8,4u8,3u8,7u8,11u8,9u8, - 2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8,7u8,9u8,1u8,9u8,3u8,3u8,3u8,3u8,3u8,3u8, - 1u8,3u8,3u8,2u8,3u8,3u8,2u8,6u8,8u8,8u8,4u8,3u8,5u8,1u8,8u8,4u8,6u8,8u8,13u8,3u8, - 3u8,6u8,8u8,1u8,3u8,3u8,1u8,8u8,13u8,1u8,6u8,8u8,13u8,10u8,1u8,3u8,1u8,3u8,3u8,6u8, - 8u8,8u8,1u8,3u8,6u8,8u8,1u8,8u8,4u8,2u8,8u8,4u8,8u8,8u8,2u8,6u8,11u8,9u8,2u8,9u8, - 0u8,9u8,1u8,9u8,0u8,1u8,6u8,9u8,1u8,3u8,6u8,8u8,8u8,4u8,3u8,1u8,6u8,8u8,10u8,13u8, - 3u8,3u8,3u8,3u8,3u8,4u8,3u8,1u8,3u8,3u8,6u8,8u8,1u8,4u8,3u8,4u8,6u8,8u8,8u8,4u8, - 3u8,4u8,6u8,11u8,9u8,2u8,8u8,4u8,8u8,8u8,5u8,5u8,10u8,2u8,12u8,8u8,10u8,1u8,2u8,2u8, - 7u8,10u8,9u8,0u8,10u8,9u8,0u8,2u8,6u8,12u8,10u8,2u8,2u8,12u8,8u8,10u8,4u8,6u8,12u8,3u8, - 5u8,5u8,1u8,11u8,9u8,2u8,9u8,0u8,9u8,1u8,1u8,8u8,8u8,1u8,11u8,11u8,1u8,9u8,0u8,1u8, - 8u8,5u8,1u8,8u8,6u8,1u8,8u8,7u8,1u8,8u8,3u8,2u8,7u8,11u8,9u8,2u8,9u8,0u8,9u8,1u8, - 9u8,0u8,5u8,12u8,5u8,5u8,8u8,4u8,7u8,8u8,1u8,3u8,7u8,8u8,8u8,5u8,4u8,7u8,5u8,3u8, - 5u8,1u8,7u8,8u8,8u8,3u8,4u8,1u8,9u8,1u8,2u8,12u8,5u8,8u8,3u8,3u8,3u8,3u8,1u8,3u8, - 3u8,7u8,8u8,1u8,2u8,7u8,8u8,8u8,3u8,9u8,5u8,1u8,1u8,12u8,3u8,3u8,5u8,6u8,12u8,8u8, - 4u8,15u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,7u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,4u8,99u8,111u8,105u8, - 110u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8, - 101u8,115u8,16u8,112u8,111u8,111u8,108u8,95u8,117u8,54u8,52u8,95u8,117u8,110u8,98u8,111u8,117u8,110u8,100u8,6u8, - 115u8,105u8,103u8,110u8,101u8,114u8,5u8,115u8,116u8,97u8,107u8,101u8,14u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8, - 95u8,99u8,111u8,110u8,102u8,105u8,103u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8, - 97u8,109u8,112u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,13u8,65u8,100u8,100u8,83u8,116u8,97u8,107u8,101u8,69u8, - 118u8,101u8,110u8,116u8,14u8,68u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,80u8,111u8,111u8,108u8,23u8, - 68u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,80u8,111u8,111u8,108u8,79u8,119u8,110u8,101u8,114u8,115u8, - 104u8,105u8,112u8,25u8,68u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,67u8,111u8,109u8,109u8,105u8,115u8, - 115u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,19u8,79u8,98u8,115u8,101u8,114u8,118u8,101u8,100u8,76u8,111u8, - 99u8,107u8,117u8,112u8,67u8,121u8,99u8,108u8,101u8,20u8,82u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8, - 83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8,110u8,116u8,16u8,85u8,110u8,108u8,111u8,99u8,107u8,83u8,116u8,97u8, - 107u8,101u8,69u8,118u8,101u8,110u8,116u8,18u8,87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,83u8,116u8,97u8,107u8, - 101u8,69u8,118u8,101u8,110u8,116u8,9u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,4u8,80u8,111u8,111u8, - 108u8,26u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,116u8,111u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,116u8, - 111u8,95u8,114u8,101u8,100u8,101u8,101u8,109u8,29u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,100u8,101u8,108u8,101u8, - 103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,25u8,97u8, - 115u8,115u8,101u8,114u8,116u8,95u8,109u8,105u8,110u8,95u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,98u8,97u8,108u8, - 97u8,110u8,99u8,101u8,35u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,109u8,105u8,110u8,95u8,112u8,101u8,110u8,100u8, - 105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8, - 23u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,95u8,101u8,120u8, - 105u8,115u8,116u8,115u8,30u8,98u8,117u8,121u8,95u8,105u8,110u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8, - 105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,26u8,99u8,97u8,108u8,99u8, - 117u8,108u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,100u8,114u8,105u8, - 102u8,116u8,29u8,99u8,97u8,110u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,112u8,101u8,110u8,100u8, - 105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,35u8,99u8,111u8,105u8,110u8,115u8,95u8,116u8, - 111u8,95u8,114u8,101u8,100u8,101u8,101u8,109u8,95u8,116u8,111u8,95u8,101u8,110u8,115u8,117u8,114u8,101u8,95u8,109u8, - 105u8,110u8,95u8,115u8,116u8,97u8,107u8,101u8,37u8,99u8,111u8,105u8,110u8,115u8,95u8,116u8,111u8,95u8,116u8,114u8, - 97u8,110u8,115u8,102u8,101u8,114u8,95u8,116u8,111u8,95u8,101u8,110u8,115u8,117u8,114u8,101u8,95u8,109u8,105u8,110u8, - 95u8,115u8,116u8,97u8,107u8,101u8,22u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8, - 111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,26u8,101u8,120u8,101u8,99u8,117u8,116u8,101u8,95u8,112u8,101u8, - 110u8,100u8,105u8,110u8,103u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,97u8,108u8,17u8,103u8,101u8,116u8, - 95u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,102u8,101u8,101u8,25u8,103u8,101u8,116u8,95u8,100u8, - 101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,95u8,115u8,116u8,97u8,107u8,101u8, - 22u8,103u8,101u8,116u8,95u8,111u8,119u8,110u8,101u8,100u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,22u8,103u8,101u8,116u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,119u8,105u8,116u8,104u8, - 100u8,114u8,97u8,119u8,97u8,108u8,16u8,103u8,101u8,116u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,26u8,105u8,110u8,105u8,116u8,105u8,97u8, - 108u8,105u8,122u8,101u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8, - 20u8,109u8,117u8,108u8,116u8,105u8,112u8,108u8,121u8,95u8,116u8,104u8,101u8,110u8,95u8,100u8,105u8,118u8,105u8,100u8, - 101u8,21u8,111u8,98u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,99u8,121u8, - 99u8,108u8,101u8,14u8,111u8,108u8,99u8,95u8,119u8,105u8,116u8,104u8,95u8,105u8,110u8,100u8,101u8,120u8,30u8,111u8, - 112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8, - 101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,16u8,111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,95u8, - 101u8,120u8,105u8,115u8,116u8,115u8,28u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8, - 105u8,118u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,112u8,111u8,111u8,108u8,32u8,112u8,101u8,110u8,100u8, - 105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8, - 112u8,111u8,111u8,108u8,95u8,109u8,117u8,116u8,25u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,119u8,105u8,116u8, - 104u8,100u8,114u8,97u8,119u8,97u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,16u8,114u8,101u8,97u8,99u8,116u8, - 105u8,118u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,20u8,114u8,101u8,100u8,101u8,101u8,109u8,95u8,97u8, - 99u8,116u8,105u8,118u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,22u8,114u8,101u8,100u8,101u8,101u8,109u8,95u8, - 105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,25u8,114u8,101u8,116u8,114u8, - 105u8,101u8,118u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,111u8,119u8,110u8,101u8, - 114u8,19u8,115u8,101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8, - 114u8,12u8,115u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,30u8,115u8,104u8,97u8,114u8,101u8, - 104u8,111u8,108u8,100u8,101u8,114u8,115u8,95u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,99u8,116u8,105u8,118u8,101u8, - 95u8,112u8,111u8,111u8,108u8,27u8,115u8,121u8,110u8,99u8,104u8,114u8,111u8,110u8,105u8,122u8,101u8,95u8,100u8,101u8, - 108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,7u8,116u8,111u8,95u8,117u8,49u8,50u8, - 56u8,6u8,117u8,110u8,108u8,111u8,99u8,107u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,17u8,119u8,105u8, - 116u8,104u8,100u8,114u8,97u8,119u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,12u8,112u8,111u8,111u8,108u8, - 95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,17u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,12u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,101u8,100u8,13u8, - 97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,102u8,101u8,101u8,13u8,97u8,99u8,116u8,105u8,118u8,101u8, - 95u8,115u8,104u8,97u8,114u8,101u8,115u8,15u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,115u8,104u8,97u8, - 114u8,101u8,115u8,5u8,84u8,97u8,98u8,108u8,101u8,19u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,119u8,105u8, - 116u8,104u8,100u8,114u8,97u8,119u8,97u8,108u8,115u8,21u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8, - 95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,20u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8, - 95u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,16u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,23u8,114u8, - 101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,19u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,21u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8, - 101u8,110u8,116u8,115u8,28u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,99u8,111u8,109u8,109u8, - 105u8,115u8,115u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,8u8,111u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,17u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,97u8,99u8,116u8,105u8,118u8,101u8, - 27u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8, - 105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,5u8,105u8,110u8,100u8,101u8,120u8,18u8,97u8,109u8,111u8,117u8,110u8, - 116u8,95u8,114u8,101u8,97u8,99u8,116u8,105u8,118u8,97u8,116u8,101u8,100u8,15u8,97u8,109u8,111u8,117u8,110u8,116u8, - 95u8,117u8,110u8,108u8,111u8,99u8,107u8,101u8,100u8,16u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8, - 104u8,100u8,114u8,97u8,119u8,110u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,9u8,65u8,112u8, - 116u8,111u8,115u8,67u8,111u8,105u8,110u8,8u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,6u8,98u8,117u8,121u8, - 95u8,105u8,110u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,7u8,98u8,97u8,108u8,97u8,110u8, - 99u8,101u8,6u8,115u8,104u8,97u8,114u8,101u8,115u8,16u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,116u8,111u8,95u8, - 115u8,104u8,97u8,114u8,101u8,115u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8, - 101u8,110u8,116u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,23u8,98u8,111u8,114u8,114u8,111u8,119u8, - 95u8,109u8,117u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,13u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,11u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8, - 105u8,110u8,115u8,19u8,103u8,101u8,116u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,95u8,115u8,116u8, - 97u8,116u8,101u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,15u8,103u8,101u8,116u8,95u8, - 108u8,111u8,99u8,107u8,117u8,112u8,95u8,115u8,101u8,99u8,115u8,16u8,115u8,104u8,97u8,114u8,101u8,115u8,95u8,116u8, - 111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,13u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8,67u8,111u8,110u8,102u8, - 105u8,103u8,26u8,105u8,115u8,95u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,95u8,101u8,112u8,111u8,99u8,104u8,95u8, - 118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,3u8,103u8,101u8,116u8,15u8,103u8,101u8,116u8,95u8,114u8,101u8, - 119u8,97u8,114u8,100u8,95u8,114u8,97u8,116u8,101u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,33u8,115u8,104u8,97u8, - 114u8,101u8,115u8,95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8, - 111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,29u8,103u8,101u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8, - 12u8,116u8,111u8,116u8,97u8,108u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,33u8,115u8,104u8,97u8,114u8,101u8,115u8, - 95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8, - 108u8,95u8,115u8,116u8,97u8,116u8,115u8,12u8,103u8,101u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8, - 24u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,115u8,95u8,101u8,110u8, - 97u8,98u8,108u8,101u8,100u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8, - 6u8,97u8,112u8,112u8,101u8,110u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8, - 114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8, - 22u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,111u8,119u8, - 110u8,101u8,114u8,3u8,110u8,101u8,119u8,26u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8, - 115u8,99u8,97u8,108u8,105u8,110u8,103u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,3u8,97u8,100u8,100u8,16u8,110u8, - 101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,10u8,98u8,111u8,114u8,114u8, - 111u8,119u8,95u8,109u8,117u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,13u8,114u8,101u8,100u8,101u8, - 101u8,109u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,13u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8,121u8,29u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8, - 103u8,110u8,101u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 18u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,95u8,99u8,111u8,117u8,110u8,116u8,18u8, - 117u8,112u8,100u8,97u8,116u8,101u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,16u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 3u8,8u8,0u8,202u8,154u8,59u8,0u8,0u8,0u8,0u8,10u8,2u8,33u8,32u8,97u8,112u8,116u8,111u8,115u8,95u8, - 102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,58u8,58u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8, - 110u8,95u8,112u8,111u8,111u8,108u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 3u8,8u8,0u8,0u8,193u8,111u8,242u8,134u8,35u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8, - 116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,150u8,13u8,11u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 20u8,69u8,79u8,87u8,78u8,69u8,82u8,95u8,67u8,65u8,80u8,95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8, - 68u8,72u8,68u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,32u8,112u8,111u8,111u8,108u8,32u8,111u8,119u8, - 110u8,101u8,114u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,32u8,100u8,111u8,101u8,115u8,32u8, - 110u8,111u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8,101u8,32u8,112u8,114u8,111u8, - 118u8,105u8,100u8,101u8,100u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,25u8,69u8,79u8,87u8,78u8,69u8,82u8,95u8,67u8,65u8,80u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8, - 89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,44u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8, - 97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,111u8,119u8,110u8,105u8,110u8,103u8,32u8,97u8,32u8,100u8,101u8,108u8, - 101u8,103u8,97u8,116u8,105u8,111u8,110u8,32u8,112u8,111u8,111u8,108u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,31u8,69u8,68u8,69u8,76u8,69u8,71u8,65u8,84u8,73u8,79u8,78u8,95u8,80u8,79u8,79u8,76u8,95u8,68u8, - 79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,60u8,68u8,101u8,108u8,101u8,103u8,97u8, - 116u8,105u8,111u8,110u8,32u8,112u8,111u8,111u8,108u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8, - 120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8, - 32u8,112u8,111u8,111u8,108u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,26u8,69u8,80u8,69u8,78u8,68u8,73u8,78u8,71u8,95u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8, - 65u8,76u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,78u8,84u8,104u8,101u8,114u8,101u8,32u8,105u8,115u8,32u8,97u8, - 32u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,97u8,108u8,32u8, - 116u8,111u8,32u8,98u8,101u8,32u8,101u8,120u8,101u8,99u8,117u8,116u8,101u8,100u8,32u8,98u8,101u8,102u8,111u8,114u8, - 101u8,32u8,96u8,117u8,110u8,108u8,111u8,99u8,107u8,96u8,105u8,110u8,103u8,32u8,97u8,110u8,121u8,32u8,110u8,101u8, - 119u8,32u8,115u8,116u8,97u8,107u8,101u8,46u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8,73u8,78u8, - 86u8,65u8,76u8,73u8,68u8,95u8,67u8,79u8,77u8,77u8,73u8,83u8,83u8,73u8,79u8,78u8,95u8,80u8,69u8,82u8, - 67u8,69u8,78u8,84u8,65u8,71u8,69u8,63u8,67u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,32u8,112u8, - 101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,32u8,104u8,97u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8, - 98u8,101u8,116u8,119u8,101u8,101u8,110u8,32u8,48u8,32u8,97u8,110u8,100u8,32u8,96u8,77u8,65u8,88u8,95u8,70u8, - 69u8,69u8,96u8,32u8,45u8,32u8,49u8,48u8,48u8,37u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8, - 69u8,78u8,79u8,84u8,95u8,69u8,78u8,79u8,85u8,71u8,72u8,95u8,65u8,67u8,84u8,73u8,86u8,69u8,95u8,83u8, - 84u8,65u8,75u8,69u8,95u8,84u8,79u8,95u8,85u8,78u8,76u8,79u8,67u8,75u8,65u8,84u8,104u8,101u8,114u8,101u8, - 32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,96u8,97u8,99u8,116u8,105u8, - 118u8,101u8,96u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,111u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,116u8,97u8, - 107u8,101u8,32u8,112u8,111u8,111u8,108u8,32u8,116u8,111u8,32u8,96u8,117u8,110u8,108u8,111u8,99u8,107u8,96u8,46u8, - 7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,35u8,69u8,83u8,76u8,65u8,83u8,72u8,69u8,68u8,95u8,73u8,78u8, - 65u8,67u8,84u8,73u8,86u8,69u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,79u8,78u8,95u8,80u8,65u8,83u8,84u8, - 95u8,79u8,76u8,67u8,211u8,2u8,83u8,108u8,97u8,115u8,104u8,105u8,110u8,103u8,32u8,40u8,105u8,102u8,32u8,105u8, - 109u8,112u8,108u8,101u8,109u8,101u8,110u8,116u8,101u8,100u8,41u8,32u8,115u8,104u8,111u8,117u8,108u8,100u8,32u8,110u8, - 111u8,116u8,32u8,98u8,101u8,32u8,97u8,112u8,112u8,108u8,105u8,101u8,100u8,32u8,116u8,111u8,32u8,97u8,108u8,114u8, - 101u8,97u8,100u8,121u8,32u8,96u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,96u8,32u8,115u8,116u8,97u8,107u8, - 101u8,46u8,10u8,32u8,78u8,111u8,116u8,32u8,111u8,110u8,108u8,121u8,32u8,105u8,116u8,32u8,105u8,110u8,118u8,97u8, - 108u8,105u8,100u8,97u8,116u8,101u8,115u8,32u8,116u8,104u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,105u8, - 110u8,103u8,32u8,111u8,102u8,32u8,112u8,97u8,115u8,116u8,32u8,111u8,98u8,115u8,101u8,114u8,118u8,101u8,100u8,32u8, - 108u8,111u8,99u8,107u8,117u8,112u8,32u8,99u8,121u8,99u8,108u8,101u8,115u8,32u8,40u8,79u8,76u8,67u8,41u8,44u8, - 10u8,32u8,98u8,117u8,116u8,32u8,105u8,115u8,32u8,97u8,108u8,115u8,111u8,32u8,117u8,110u8,102u8,97u8,105u8,114u8, - 32u8,116u8,111u8,32u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,111u8,114u8,115u8,32u8,119u8,104u8,111u8,115u8,101u8, - 32u8,115u8,116u8,97u8,107u8,101u8,32u8,104u8,97u8,115u8,32u8,98u8,101u8,101u8,110u8,32u8,105u8,110u8,97u8,99u8, - 116u8,105u8,118u8,101u8,32u8,98u8,101u8,102u8,111u8,114u8,101u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,32u8,115u8,116u8,97u8,114u8,116u8,101u8,100u8,32u8,109u8,105u8,115u8,98u8,101u8,104u8,97u8,118u8,105u8,110u8, - 103u8,46u8,10u8,32u8,65u8,100u8,100u8,105u8,116u8,105u8,111u8,110u8,97u8,108u8,108u8,121u8,44u8,32u8,116u8,104u8, - 101u8,32u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,100u8,111u8,101u8, - 115u8,32u8,110u8,111u8,116u8,32u8,99u8,111u8,117u8,110u8,116u8,32u8,111u8,110u8,32u8,116u8,104u8,101u8,32u8,118u8, - 111u8,116u8,105u8,110u8,103u8,32u8,112u8,111u8,119u8,101u8,114u8,32u8,111u8,102u8,32u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,111u8,114u8,46u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,69u8,68u8,69u8,76u8,69u8,71u8, - 65u8,84u8,79u8,82u8,95u8,65u8,67u8,84u8,73u8,86u8,69u8,95u8,66u8,65u8,76u8,65u8,78u8,67u8,69u8,95u8, - 84u8,79u8,79u8,95u8,76u8,79u8,87u8,74u8,68u8,101u8,108u8,101u8,103u8,97u8,116u8,111u8,114u8,39u8,115u8,32u8, - 97u8,99u8,116u8,105u8,118u8,101u8,32u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,32u8,99u8,97u8,110u8,110u8,111u8, - 116u8,32u8,98u8,101u8,32u8,108u8,101u8,115u8,115u8,32u8,116u8,104u8,97u8,110u8,32u8,96u8,77u8,73u8,78u8,95u8, - 67u8,79u8,73u8,78u8,83u8,95u8,79u8,78u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,80u8,79u8,79u8,76u8, - 96u8,46u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,43u8,69u8,68u8,69u8,76u8,69u8,71u8,65u8,84u8,79u8, - 82u8,95u8,80u8,69u8,78u8,68u8,73u8,78u8,71u8,95u8,73u8,78u8,65u8,67u8,84u8,73u8,86u8,69u8,95u8,66u8, - 65u8,76u8,65u8,78u8,67u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,87u8,84u8,68u8,101u8,108u8,101u8,103u8, - 97u8,116u8,111u8,114u8,39u8,115u8,32u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,105u8,110u8,97u8,99u8,116u8, - 105u8,118u8,101u8,32u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8, - 101u8,32u8,108u8,101u8,115u8,115u8,32u8,116u8,104u8,97u8,110u8,32u8,96u8,77u8,73u8,78u8,95u8,67u8,79u8,73u8, - 78u8,83u8,95u8,79u8,78u8,95u8,83u8,72u8,65u8,82u8,69u8,83u8,95u8,80u8,79u8,79u8,76u8,96u8,46u8,10u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,68u8,69u8,76u8,69u8,71u8,65u8,84u8,73u8,79u8,78u8,95u8, - 80u8,79u8,79u8,76u8,83u8,95u8,68u8,73u8,83u8,65u8,66u8,76u8,69u8,68u8,45u8,67u8,114u8,101u8,97u8,116u8, - 105u8,110u8,103u8,32u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,32u8,112u8,111u8,111u8,108u8,115u8, - 32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,32u8,121u8,101u8,116u8,46u8, - 11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8,95u8,90u8, - 69u8,82u8,79u8,95u8,83u8,84u8,65u8,75u8,69u8,38u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,114u8,101u8,113u8, - 117u8,101u8,115u8,116u8,32u8,116u8,111u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,32u8,122u8,101u8,114u8, - 111u8,32u8,115u8,116u8,97u8,107u8,101u8,46u8,0u8,11u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8, - 1u8,1u8,0u8,16u8,111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8, - 1u8,1u8,0u8,17u8,103u8,101u8,116u8,95u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,102u8,101u8, - 101u8,1u8,1u8,0u8,21u8,111u8,98u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8, - 95u8,99u8,121u8,99u8,108u8,101u8,1u8,1u8,0u8,22u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8, - 95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,1u8,1u8,0u8,22u8,103u8,101u8,116u8,95u8, - 111u8,119u8,110u8,101u8,100u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,1u8,1u8, - 0u8,22u8,103u8,101u8,116u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8, - 97u8,119u8,97u8,108u8,1u8,1u8,0u8,25u8,103u8,101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8, - 111u8,110u8,95u8,112u8,111u8,111u8,108u8,95u8,115u8,116u8,97u8,107u8,101u8,1u8,1u8,0u8,29u8,99u8,97u8,110u8, - 95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,105u8,110u8, - 97u8,99u8,116u8,105u8,118u8,101u8,1u8,1u8,0u8,30u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,99u8, - 111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8, - 1u8,1u8,0u8,30u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,95u8,99u8,111u8,117u8, - 110u8,116u8,95u8,97u8,99u8,116u8,105u8,118u8,101u8,95u8,112u8,111u8,111u8,108u8,1u8,1u8,0u8,0u8,2u8,4u8, - 63u8,5u8,64u8,5u8,65u8,3u8,66u8,3u8,1u8,2u8,12u8,67u8,8u8,8u8,44u8,8u8,4u8,68u8,11u8,9u8, - 2u8,8u8,4u8,8u8,8u8,70u8,11u8,9u8,2u8,5u8,8u8,4u8,71u8,8u8,10u8,73u8,3u8,46u8,3u8,74u8, - 11u8,11u8,1u8,8u8,0u8,76u8,11u8,11u8,1u8,8u8,5u8,77u8,11u8,11u8,1u8,8u8,6u8,78u8,11u8,11u8, - 1u8,8u8,7u8,79u8,11u8,11u8,1u8,8u8,3u8,2u8,2u8,1u8,63u8,5u8,3u8,2u8,4u8,63u8,5u8,80u8, - 5u8,81u8,3u8,82u8,3u8,4u8,2u8,1u8,83u8,3u8,5u8,2u8,3u8,63u8,5u8,64u8,5u8,84u8,3u8,6u8, - 2u8,3u8,63u8,5u8,64u8,5u8,85u8,3u8,7u8,2u8,3u8,63u8,5u8,64u8,5u8,86u8,3u8,0u8,1u8,4u8, - 1u8,1u8,27u8,59u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,11u8,0u8, - 1u8,2u8,10u8,1u8,17u8,35u8,10u8,1u8,10u8,2u8,17u8,13u8,12u8,5u8,10u8,1u8,42u8,1u8,12u8,7u8, - 10u8,0u8,17u8,40u8,12u8,6u8,11u8,0u8,10u8,1u8,10u8,2u8,56u8,0u8,10u8,7u8,46u8,17u8,31u8,12u8, - 3u8,14u8,3u8,10u8,2u8,17u8,42u8,10u8,7u8,15u8,0u8,10u8,6u8,10u8,2u8,10u8,5u8,23u8,17u8,43u8, - 1u8,10u8,7u8,10u8,6u8,12u8,4u8,46u8,11u8,4u8,17u8,3u8,10u8,7u8,15u8,0u8,7u8,15u8,10u8,5u8, - 17u8,43u8,1u8,11u8,7u8,15u8,1u8,11u8,1u8,11u8,6u8,11u8,2u8,11u8,5u8,18u8,0u8,56u8,1u8,2u8, - 1u8,0u8,0u8,0u8,3u8,17u8,10u8,2u8,10u8,0u8,10u8,1u8,17u8,45u8,38u8,4u8,11u8,11u8,0u8,11u8, - 1u8,17u8,46u8,12u8,3u8,5u8,15u8,11u8,0u8,11u8,2u8,17u8,47u8,12u8,3u8,11u8,3u8,2u8,2u8,0u8, - 0u8,0u8,1u8,8u8,11u8,0u8,17u8,11u8,4u8,4u8,5u8,7u8,7u8,1u8,17u8,48u8,39u8,2u8,3u8,0u8, - 0u8,0u8,1u8,12u8,11u8,0u8,16u8,0u8,11u8,1u8,17u8,45u8,7u8,13u8,38u8,4u8,8u8,5u8,11u8,7u8, - 2u8,17u8,48u8,39u8,2u8,4u8,0u8,0u8,0u8,1u8,12u8,11u8,0u8,17u8,25u8,11u8,1u8,17u8,45u8,7u8, - 13u8,38u8,4u8,8u8,5u8,11u8,7u8,3u8,17u8,48u8,39u8,2u8,5u8,0u8,0u8,0u8,1u8,8u8,11u8,0u8, - 17u8,24u8,4u8,4u8,5u8,7u8,7u8,7u8,17u8,49u8,39u8,2u8,6u8,0u8,0u8,0u8,36u8,36u8,10u8,0u8, - 17u8,26u8,10u8,1u8,11u8,2u8,17u8,43u8,12u8,3u8,10u8,3u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,14u8,11u8,0u8,1u8,50u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8,0u8,10u8,1u8,17u8,12u8,10u8, - 0u8,16u8,2u8,20u8,12u8,4u8,11u8,0u8,15u8,3u8,11u8,1u8,10u8,4u8,56u8,2u8,20u8,11u8,4u8,33u8, - 4u8,31u8,5u8,34u8,7u8,8u8,17u8,51u8,39u8,11u8,3u8,2u8,7u8,0u8,0u8,0u8,40u8,89u8,10u8,0u8, - 17u8,17u8,17u8,52u8,12u8,9u8,12u8,8u8,12u8,6u8,12u8,3u8,10u8,6u8,10u8,0u8,16u8,4u8,20u8,38u8, - 4u8,14u8,5u8,19u8,11u8,0u8,1u8,7u8,9u8,17u8,51u8,39u8,10u8,6u8,10u8,0u8,16u8,4u8,20u8,36u8, - 12u8,7u8,11u8,3u8,11u8,8u8,22u8,12u8,3u8,10u8,7u8,4u8,37u8,11u8,6u8,10u8,0u8,16u8,4u8,20u8, - 23u8,12u8,9u8,10u8,0u8,16u8,0u8,17u8,53u8,12u8,4u8,10u8,3u8,10u8,4u8,36u8,4u8,55u8,10u8,3u8, - 11u8,4u8,23u8,10u8,0u8,16u8,5u8,20u8,7u8,11u8,17u8,20u8,12u8,1u8,5u8,57u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,12u8,1u8,11u8,1u8,12u8,4u8,10u8,0u8,17u8,25u8,17u8,53u8,12u8,5u8,10u8, - 9u8,10u8,5u8,36u8,4u8,77u8,10u8,9u8,11u8,5u8,23u8,11u8,0u8,16u8,5u8,20u8,7u8,11u8,17u8,20u8, - 12u8,2u8,5u8,81u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,11u8,2u8, - 12u8,5u8,11u8,7u8,11u8,3u8,11u8,9u8,11u8,4u8,11u8,5u8,2u8,8u8,1u8,0u8,0u8,9u8,15u8,10u8, - 0u8,17u8,54u8,7u8,8u8,33u8,4u8,11u8,17u8,55u8,11u8,0u8,17u8,56u8,38u8,12u8,1u8,5u8,13u8,9u8, - 12u8,1u8,11u8,1u8,2u8,9u8,0u8,0u8,0u8,41u8,21u8,10u8,0u8,10u8,0u8,10u8,1u8,10u8,2u8,17u8, - 1u8,17u8,57u8,12u8,3u8,11u8,0u8,11u8,1u8,17u8,45u8,12u8,4u8,10u8,4u8,11u8,3u8,23u8,7u8,13u8, - 35u8,4u8,19u8,11u8,4u8,12u8,2u8,11u8,2u8,2u8,10u8,0u8,0u8,0u8,41u8,28u8,10u8,0u8,10u8,0u8, - 10u8,2u8,10u8,3u8,17u8,1u8,17u8,57u8,12u8,5u8,11u8,1u8,10u8,2u8,17u8,45u8,12u8,4u8,10u8,4u8, - 11u8,5u8,22u8,7u8,13u8,35u8,4u8,23u8,7u8,13u8,11u8,4u8,23u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,12u8,3u8,11u8,0u8,11u8,2u8,11u8,3u8,17u8,9u8,2u8,11u8,1u8,0u8,0u8,1u8,3u8, - 11u8,0u8,41u8,1u8,2u8,12u8,0u8,0u8,0u8,43u8,30u8,10u8,0u8,10u8,1u8,12u8,2u8,46u8,11u8,2u8, - 17u8,27u8,12u8,4u8,4u8,18u8,14u8,4u8,16u8,6u8,20u8,10u8,0u8,16u8,2u8,16u8,6u8,20u8,35u8,12u8, - 3u8,5u8,20u8,9u8,12u8,3u8,11u8,3u8,4u8,27u8,11u8,0u8,11u8,1u8,7u8,12u8,17u8,39u8,5u8,29u8, - 11u8,0u8,1u8,2u8,13u8,1u8,0u8,1u8,1u8,44u8,53u8,10u8,0u8,17u8,58u8,4u8,49u8,17u8,59u8,12u8, - 2u8,14u8,2u8,17u8,60u8,12u8,7u8,12u8,6u8,10u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 36u8,4u8,44u8,10u8,0u8,17u8,2u8,11u8,0u8,43u8,1u8,12u8,5u8,11u8,6u8,7u8,11u8,11u8,5u8,16u8, - 5u8,20u8,23u8,24u8,12u8,6u8,11u8,7u8,7u8,11u8,24u8,12u8,7u8,11u8,1u8,53u8,10u8,6u8,53u8,24u8, - 11u8,6u8,53u8,11u8,7u8,53u8,22u8,26u8,52u8,12u8,3u8,5u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,12u8,3u8,11u8,3u8,12u8,4u8,5u8,51u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8, - 4u8,11u8,4u8,2u8,14u8,1u8,0u8,0u8,1u8,5u8,10u8,0u8,17u8,2u8,11u8,0u8,17u8,52u8,2u8,15u8, - 1u8,0u8,1u8,2u8,1u8,7u8,10u8,0u8,17u8,5u8,11u8,0u8,43u8,2u8,16u8,7u8,20u8,2u8,16u8,1u8, - 0u8,1u8,1u8,47u8,66u8,10u8,0u8,17u8,2u8,11u8,0u8,43u8,1u8,12u8,10u8,10u8,10u8,17u8,7u8,12u8, - 6u8,1u8,12u8,9u8,1u8,12u8,8u8,10u8,10u8,10u8,1u8,17u8,27u8,12u8,11u8,32u8,4u8,25u8,11u8,10u8, - 1u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,12u8,4u8,5u8,63u8,10u8,10u8,16u8, - 8u8,10u8,11u8,56u8,3u8,12u8,7u8,14u8,11u8,16u8,6u8,20u8,11u8,10u8,16u8,2u8,16u8,6u8,20u8,35u8, - 4u8,46u8,8u8,11u8,7u8,11u8,1u8,17u8,45u8,12u8,3u8,12u8,2u8,5u8,59u8,10u8,7u8,11u8,7u8,11u8, - 1u8,17u8,46u8,11u8,9u8,11u8,6u8,23u8,17u8,62u8,12u8,9u8,11u8,8u8,11u8,9u8,12u8,3u8,12u8,2u8, - 11u8,2u8,11u8,3u8,12u8,5u8,12u8,4u8,11u8,4u8,11u8,5u8,2u8,17u8,0u8,0u8,0u8,1u8,4u8,11u8, - 0u8,16u8,9u8,17u8,63u8,2u8,18u8,1u8,0u8,1u8,1u8,53u8,95u8,10u8,0u8,17u8,2u8,10u8,0u8,43u8, - 1u8,12u8,12u8,10u8,12u8,17u8,7u8,12u8,6u8,12u8,5u8,1u8,12u8,4u8,12u8,9u8,10u8,12u8,16u8,0u8, - 17u8,64u8,12u8,13u8,10u8,12u8,16u8,0u8,10u8,1u8,17u8,46u8,12u8,7u8,10u8,0u8,17u8,52u8,1u8,12u8, - 10u8,1u8,1u8,11u8,10u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,44u8,11u8,13u8,10u8, - 12u8,16u8,0u8,7u8,15u8,17u8,46u8,23u8,12u8,13u8,10u8,1u8,7u8,15u8,33u8,4u8,44u8,50u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,11u8,12u8,16u8,0u8, - 11u8,7u8,11u8,4u8,10u8,5u8,23u8,11u8,13u8,17u8,65u8,12u8,4u8,10u8,0u8,10u8,1u8,17u8,16u8,12u8, - 14u8,4u8,63u8,11u8,14u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,12u8,2u8,5u8,67u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,14u8,12u8,3u8,12u8,2u8,11u8,2u8,11u8,3u8,12u8, - 11u8,12u8,8u8,11u8,1u8,11u8,0u8,17u8,66u8,33u8,4u8,91u8,11u8,4u8,11u8,5u8,22u8,12u8,4u8,11u8, - 9u8,4u8,87u8,11u8,8u8,11u8,6u8,22u8,12u8,8u8,5u8,91u8,11u8,11u8,11u8,6u8,22u8,12u8,11u8,11u8, - 4u8,11u8,8u8,11u8,11u8,2u8,19u8,1u8,4u8,0u8,55u8,89u8,17u8,67u8,4u8,3u8,5u8,8u8,11u8,0u8, - 1u8,7u8,0u8,17u8,51u8,39u8,10u8,0u8,17u8,40u8,12u8,4u8,10u8,4u8,17u8,24u8,32u8,4u8,16u8,5u8, - 21u8,11u8,0u8,1u8,7u8,6u8,17u8,68u8,39u8,10u8,1u8,7u8,11u8,37u8,4u8,26u8,5u8,31u8,11u8,0u8, - 1u8,7u8,4u8,17u8,48u8,39u8,64u8,56u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,6u8,13u8,6u8, - 7u8,14u8,56u8,4u8,13u8,6u8,11u8,2u8,56u8,4u8,10u8,0u8,11u8,6u8,17u8,70u8,12u8,8u8,12u8,7u8, - 14u8,7u8,56u8,5u8,14u8,7u8,17u8,40u8,12u8,5u8,14u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,10u8,4u8,11u8,4u8,17u8,72u8,56u8,6u8,12u8,3u8,13u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,17u8,22u8,7u8,16u8,17u8,74u8,56u8,7u8,14u8,7u8,7u8,16u8,17u8,74u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,17u8,22u8,11u8,3u8,56u8,8u8,11u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,11u8,1u8,14u8,7u8,56u8,9u8,14u8,7u8,56u8,10u8,14u8,7u8,56u8,11u8,14u8,7u8,56u8,12u8, - 14u8,7u8,56u8,13u8,18u8,1u8,45u8,1u8,11u8,0u8,11u8,5u8,18u8,2u8,45u8,2u8,2u8,20u8,1u8,0u8, - 0u8,1u8,10u8,11u8,0u8,17u8,36u8,11u8,1u8,17u8,36u8,24u8,11u8,2u8,17u8,36u8,26u8,52u8,2u8,21u8, - 1u8,0u8,1u8,1u8,1u8,8u8,10u8,0u8,17u8,2u8,11u8,0u8,43u8,1u8,16u8,2u8,16u8,6u8,20u8,2u8, - 22u8,0u8,0u8,0u8,1u8,3u8,11u8,0u8,18u8,4u8,2u8,23u8,1u8,0u8,1u8,1u8,1u8,7u8,10u8,0u8, - 17u8,2u8,11u8,0u8,43u8,1u8,16u8,5u8,20u8,2u8,24u8,1u8,0u8,0u8,1u8,3u8,11u8,0u8,41u8,2u8, - 2u8,25u8,0u8,0u8,0u8,1u8,7u8,10u8,0u8,16u8,8u8,11u8,0u8,16u8,2u8,20u8,56u8,3u8,2u8,26u8, - 0u8,0u8,0u8,19u8,9u8,10u8,0u8,16u8,2u8,20u8,12u8,1u8,11u8,0u8,15u8,8u8,11u8,1u8,56u8,14u8, - 2u8,27u8,0u8,0u8,0u8,23u8,24u8,10u8,0u8,16u8,3u8,10u8,1u8,56u8,15u8,4u8,14u8,8u8,11u8,0u8, - 16u8,3u8,11u8,1u8,56u8,16u8,20u8,12u8,3u8,12u8,2u8,5u8,21u8,11u8,0u8,1u8,9u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,17u8,22u8,12u8,3u8,12u8,2u8,11u8,2u8,11u8,3u8,2u8,28u8,1u8,4u8, - 1u8,1u8,69u8,61u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,11u8,0u8, - 1u8,2u8,10u8,1u8,17u8,35u8,10u8,1u8,42u8,1u8,12u8,7u8,11u8,0u8,17u8,40u8,12u8,5u8,10u8,7u8, - 46u8,17u8,25u8,10u8,7u8,16u8,0u8,10u8,5u8,11u8,2u8,17u8,10u8,12u8,2u8,10u8,7u8,16u8,2u8,20u8, - 12u8,6u8,10u8,7u8,10u8,5u8,11u8,2u8,11u8,6u8,17u8,30u8,12u8,2u8,10u8,7u8,46u8,17u8,31u8,12u8, - 3u8,14u8,3u8,10u8,2u8,17u8,79u8,10u8,7u8,15u8,0u8,10u8,5u8,10u8,2u8,17u8,43u8,1u8,10u8,7u8, - 10u8,5u8,12u8,4u8,46u8,11u8,4u8,17u8,3u8,11u8,7u8,15u8,10u8,11u8,1u8,11u8,5u8,11u8,2u8,18u8, - 5u8,56u8,17u8,2u8,29u8,0u8,0u8,0u8,3u8,20u8,10u8,0u8,16u8,0u8,10u8,1u8,11u8,2u8,17u8,1u8, - 12u8,3u8,10u8,3u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,33u8,4u8,14u8,11u8,0u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,11u8,0u8,15u8, - 0u8,11u8,1u8,11u8,3u8,17u8,80u8,2u8,30u8,0u8,0u8,0u8,71u8,76u8,10u8,0u8,15u8,8u8,10u8,3u8, - 56u8,14u8,12u8,8u8,10u8,8u8,10u8,1u8,11u8,2u8,12u8,5u8,12u8,4u8,46u8,11u8,4u8,11u8,5u8,17u8, - 1u8,12u8,10u8,10u8,10u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,4u8,25u8,11u8,0u8,1u8,11u8,8u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 2u8,10u8,8u8,10u8,1u8,11u8,10u8,17u8,80u8,12u8,9u8,10u8,8u8,10u8,1u8,12u8,6u8,46u8,11u8,6u8, - 17u8,46u8,50u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8, - 4u8,44u8,10u8,0u8,15u8,3u8,11u8,1u8,56u8,18u8,1u8,14u8,3u8,16u8,6u8,20u8,10u8,0u8,16u8,2u8, - 16u8,6u8,20u8,35u8,4u8,60u8,11u8,8u8,46u8,17u8,53u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 33u8,12u8,7u8,5u8,64u8,11u8,8u8,1u8,9u8,12u8,7u8,11u8,7u8,4u8,72u8,11u8,0u8,15u8,8u8,11u8, - 3u8,56u8,19u8,17u8,82u8,5u8,74u8,11u8,0u8,1u8,11u8,9u8,2u8,31u8,0u8,0u8,0u8,1u8,4u8,11u8, - 0u8,16u8,9u8,17u8,83u8,2u8,32u8,1u8,4u8,2u8,1u8,2u8,73u8,14u8,11u8,0u8,17u8,40u8,17u8,15u8, - 12u8,3u8,10u8,3u8,17u8,35u8,11u8,3u8,43u8,1u8,17u8,31u8,12u8,2u8,14u8,2u8,11u8,1u8,17u8,84u8, - 2u8,33u8,1u8,4u8,2u8,1u8,2u8,73u8,14u8,11u8,0u8,17u8,40u8,17u8,15u8,12u8,3u8,10u8,3u8,17u8, - 35u8,11u8,3u8,43u8,1u8,17u8,31u8,12u8,2u8,14u8,2u8,11u8,1u8,17u8,85u8,2u8,34u8,1u8,0u8,1u8, - 1u8,1u8,7u8,10u8,0u8,17u8,2u8,11u8,0u8,43u8,1u8,16u8,0u8,17u8,86u8,2u8,35u8,1u8,4u8,1u8, - 1u8,74u8,96u8,10u8,0u8,17u8,2u8,10u8,0u8,42u8,1u8,12u8,8u8,10u8,8u8,46u8,17u8,7u8,12u8,3u8, - 12u8,2u8,12u8,7u8,12u8,1u8,12u8,5u8,10u8,0u8,17u8,52u8,1u8,12u8,6u8,1u8,1u8,11u8,6u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,28u8,10u8,8u8,7u8,15u8,7u8,12u8,17u8,29u8,1u8, - 10u8,8u8,15u8,0u8,11u8,1u8,10u8,2u8,23u8,17u8,87u8,10u8,8u8,17u8,26u8,11u8,7u8,10u8,3u8,23u8, - 17u8,87u8,10u8,8u8,15u8,0u8,10u8,0u8,17u8,66u8,10u8,2u8,17u8,43u8,1u8,10u8,8u8,10u8,0u8,17u8, - 66u8,10u8,3u8,17u8,6u8,1u8,10u8,8u8,15u8,11u8,10u8,0u8,10u8,0u8,17u8,66u8,11u8,2u8,11u8,3u8, - 18u8,3u8,56u8,20u8,11u8,5u8,4u8,93u8,11u8,0u8,17u8,52u8,1u8,1u8,12u8,4u8,1u8,11u8,4u8,10u8, - 8u8,15u8,4u8,21u8,10u8,8u8,16u8,2u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,10u8,8u8,15u8,2u8,15u8,6u8,21u8,10u8,8u8,15u8,8u8,11u8,8u8,16u8,2u8,20u8,7u8,16u8,17u8, - 74u8,56u8,7u8,5u8,95u8,11u8,8u8,1u8,2u8,36u8,0u8,0u8,0u8,1u8,3u8,11u8,0u8,53u8,2u8,37u8, - 1u8,4u8,1u8,1u8,27u8,71u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8, - 11u8,0u8,1u8,2u8,10u8,1u8,17u8,52u8,1u8,1u8,1u8,12u8,5u8,10u8,2u8,11u8,5u8,37u8,4u8,18u8, - 5u8,23u8,11u8,0u8,1u8,7u8,5u8,17u8,48u8,39u8,10u8,1u8,17u8,35u8,10u8,1u8,42u8,1u8,12u8,7u8, - 11u8,0u8,17u8,40u8,12u8,6u8,10u8,7u8,16u8,0u8,10u8,7u8,46u8,17u8,25u8,10u8,6u8,11u8,2u8,17u8, - 10u8,12u8,2u8,10u8,7u8,10u8,6u8,11u8,2u8,17u8,29u8,12u8,2u8,10u8,7u8,46u8,17u8,31u8,12u8,3u8, - 14u8,3u8,10u8,2u8,17u8,88u8,10u8,7u8,10u8,6u8,10u8,2u8,17u8,6u8,1u8,10u8,7u8,10u8,6u8,12u8, - 4u8,46u8,11u8,4u8,17u8,4u8,11u8,7u8,15u8,12u8,11u8,1u8,11u8,6u8,11u8,2u8,18u8,6u8,56u8,21u8, - 2u8,38u8,1u8,4u8,1u8,1u8,1u8,19u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8, - 4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,10u8,17u8,48u8,39u8,10u8,1u8,17u8,35u8,11u8,1u8,42u8,1u8, - 11u8,0u8,17u8,40u8,11u8,2u8,17u8,39u8,2u8,39u8,0u8,0u8,0u8,76u8,130u8,1u8,10u8,2u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,7u8,11u8,0u8,1u8,2u8,10u8,0u8,46u8,17u8,17u8,12u8, - 9u8,10u8,0u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,17u8,27u8,12u8,11u8,4u8,37u8,14u8,11u8,16u8,6u8, - 20u8,10u8,0u8,16u8,2u8,16u8,6u8,20u8,35u8,4u8,31u8,8u8,12u8,4u8,5u8,34u8,10u8,9u8,17u8,8u8, - 12u8,4u8,11u8,4u8,12u8,5u8,5u8,39u8,9u8,12u8,5u8,11u8,5u8,32u8,4u8,45u8,11u8,0u8,1u8,2u8, - 14u8,11u8,16u8,6u8,20u8,10u8,0u8,16u8,2u8,16u8,6u8,20u8,33u8,4u8,61u8,10u8,0u8,46u8,17u8,25u8, - 10u8,1u8,11u8,2u8,17u8,9u8,12u8,2u8,10u8,0u8,10u8,1u8,11u8,2u8,10u8,11u8,17u8,30u8,12u8,2u8, - 10u8,0u8,46u8,17u8,31u8,12u8,6u8,14u8,6u8,12u8,10u8,10u8,9u8,17u8,8u8,4u8,105u8,10u8,9u8,17u8, - 52u8,12u8,8u8,1u8,1u8,1u8,14u8,11u8,16u8,6u8,20u8,10u8,0u8,16u8,2u8,16u8,6u8,20u8,33u8,4u8, - 95u8,11u8,8u8,10u8,2u8,23u8,12u8,8u8,10u8,10u8,10u8,8u8,17u8,79u8,10u8,10u8,10u8,2u8,17u8,89u8, - 10u8,10u8,11u8,8u8,17u8,88u8,5u8,108u8,10u8,10u8,10u8,2u8,17u8,89u8,11u8,10u8,10u8,1u8,10u8,2u8, - 56u8,0u8,10u8,9u8,17u8,52u8,1u8,1u8,12u8,7u8,1u8,11u8,7u8,10u8,0u8,15u8,4u8,21u8,11u8,0u8, - 15u8,13u8,11u8,9u8,11u8,1u8,11u8,2u8,18u8,7u8,56u8,22u8,2u8,1u8,0u8,1u8,7u8,1u8,1u8,1u8, - 3u8,1u8,5u8,1u8,6u8,4u8,0u8,2u8,0u8,1u8,2u8,1u8,4u8,1u8,8u8,1u8,11u8,1u8,9u8,1u8, - 10u8,0u8, - ]; - vector::push_back(&mut code, chunk28); - let chunk29 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,16u8,2u8,16u8,20u8,3u8,36u8,47u8,4u8, - 83u8,4u8,5u8,87u8,42u8,7u8,129u8,1u8,188u8,2u8,8u8,189u8,3u8,32u8,6u8,221u8,3u8,54u8,16u8,147u8, - 4u8,144u8,1u8,10u8,163u8,5u8,24u8,12u8,187u8,5u8,161u8,1u8,13u8,220u8,6u8,2u8,15u8,222u8,6u8,2u8, - 0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,7u8,0u8, - 0u8,10u8,11u8,0u8,0u8,11u8,11u8,0u8,3u8,14u8,11u8,0u8,4u8,17u8,7u8,0u8,0u8,12u8,0u8,1u8, - 0u8,0u8,13u8,0u8,1u8,0u8,0u8,15u8,2u8,1u8,0u8,5u8,21u8,4u8,1u8,0u8,7u8,22u8,6u8,7u8, - 1u8,0u8,1u8,23u8,8u8,8u8,0u8,6u8,24u8,9u8,10u8,1u8,0u8,2u8,25u8,1u8,1u8,0u8,3u8,26u8, - 2u8,1u8,0u8,4u8,5u8,6u8,3u8,2u8,6u8,12u8,10u8,2u8,0u8,2u8,6u8,12u8,8u8,3u8,1u8,8u8, - 2u8,1u8,6u8,12u8,1u8,2u8,1u8,6u8,10u8,9u8,0u8,1u8,1u8,1u8,3u8,1u8,10u8,2u8,1u8,9u8, - 0u8,3u8,7u8,8u8,2u8,8u8,2u8,8u8,2u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,12u8,103u8,97u8, - 115u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,5u8,101u8,114u8,114u8,111u8,114u8,15u8,114u8,101u8,99u8, - 111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,11u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8, - 95u8,103u8,97u8,115u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,4u8,117u8,116u8,105u8,108u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8, - 8u8,71u8,97u8,115u8,69u8,110u8,116u8,114u8,121u8,11u8,71u8,97u8,115u8,83u8,99u8,104u8,101u8,100u8,117u8,108u8, - 101u8,13u8,71u8,97u8,115u8,83u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,86u8,50u8,10u8,105u8,110u8,105u8,116u8, - 105u8,97u8,108u8,105u8,122u8,101u8,16u8,115u8,101u8,116u8,95u8,103u8,97u8,115u8,95u8,115u8,99u8,104u8,101u8,100u8, - 117u8,108u8,101u8,16u8,83u8,116u8,111u8,114u8,97u8,103u8,101u8,71u8,97u8,115u8,67u8,111u8,110u8,102u8,105u8,103u8, - 22u8,115u8,101u8,116u8,95u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,95u8,103u8,97u8,115u8,95u8,99u8,111u8,110u8, - 102u8,105u8,103u8,3u8,107u8,101u8,121u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,3u8,118u8,97u8,108u8,7u8,101u8, - 110u8,116u8,114u8,105u8,101u8,115u8,15u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,95u8,118u8,101u8,114u8,115u8,105u8, - 111u8,110u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8, - 101u8,119u8,111u8,114u8,107u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,16u8,105u8,110u8,118u8,97u8,108u8, - 105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,10u8,102u8,114u8,111u8,109u8,95u8,98u8,121u8,116u8, - 101u8,115u8,11u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,10u8,115u8,101u8,116u8,95u8,99u8, - 111u8,110u8,102u8,105u8,103u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8, - 101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,124u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 21u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,71u8,65u8,83u8,95u8,83u8,67u8,72u8,69u8,68u8,85u8, - 76u8,69u8,52u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,103u8,97u8,115u8,32u8, - 115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8,97u8,114u8,101u8,32u8,101u8, - 109u8,112u8,116u8,121u8,32u8,111u8,114u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,2u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,28u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,71u8,65u8,83u8,95u8,70u8,69u8,65u8, - 84u8,85u8,82u8,69u8,95u8,86u8,69u8,82u8,83u8,73u8,79u8,78u8,0u8,0u8,0u8,0u8,2u8,2u8,16u8,8u8, - 4u8,18u8,3u8,1u8,2u8,1u8,19u8,10u8,8u8,0u8,2u8,2u8,2u8,20u8,3u8,19u8,10u8,8u8,0u8,0u8, - 3u8,0u8,0u8,3u8,19u8,10u8,0u8,17u8,3u8,14u8,1u8,56u8,0u8,32u8,4u8,7u8,5u8,12u8,11u8,0u8, - 1u8,7u8,1u8,17u8,5u8,39u8,11u8,1u8,56u8,1u8,12u8,2u8,11u8,0u8,11u8,2u8,45u8,2u8,2u8,1u8, - 1u8,0u8,2u8,1u8,2u8,11u8,55u8,10u8,0u8,17u8,3u8,14u8,1u8,56u8,0u8,32u8,4u8,7u8,5u8,12u8, - 11u8,0u8,1u8,7u8,1u8,17u8,5u8,39u8,7u8,2u8,41u8,2u8,4u8,41u8,11u8,0u8,1u8,7u8,2u8,42u8, - 2u8,12u8,2u8,11u8,1u8,56u8,1u8,12u8,3u8,14u8,3u8,16u8,0u8,20u8,10u8,2u8,16u8,0u8,20u8,38u8, - 4u8,32u8,5u8,37u8,11u8,2u8,1u8,7u8,0u8,17u8,5u8,39u8,11u8,3u8,11u8,2u8,21u8,5u8,53u8,7u8, - 2u8,41u8,1u8,4u8,47u8,7u8,2u8,44u8,1u8,1u8,11u8,1u8,56u8,1u8,12u8,4u8,11u8,0u8,11u8,4u8, - 45u8,2u8,17u8,7u8,2u8,2u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8,11u8,1u8,17u8,8u8,17u8,7u8,2u8, - 2u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk29); - let chunk30 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,26u8,2u8,26u8,96u8,3u8,122u8,134u8,3u8, - 4u8,128u8,4u8,66u8,5u8,194u8,4u8,203u8,4u8,7u8,141u8,9u8,153u8,17u8,8u8,166u8,26u8,32u8,6u8,198u8, - 26u8,127u8,16u8,197u8,27u8,218u8,8u8,10u8,159u8,36u8,198u8,1u8,12u8,229u8,37u8,157u8,17u8,13u8,130u8,55u8, - 32u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8, - 9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,6u8,0u8,0u8,14u8,6u8,0u8,0u8,15u8,6u8,0u8,0u8, - 16u8,6u8,0u8,0u8,17u8,6u8,0u8,0u8,18u8,6u8,0u8,0u8,19u8,4u8,0u8,0u8,20u8,0u8,0u8,0u8, - 21u8,8u8,0u8,0u8,22u8,8u8,0u8,0u8,23u8,6u8,0u8,0u8,24u8,6u8,0u8,0u8,25u8,6u8,0u8,0u8, - 26u8,6u8,0u8,6u8,27u8,4u8,1u8,6u8,1u8,1u8,32u8,6u8,0u8,10u8,33u8,12u8,0u8,4u8,36u8,4u8, - 1u8,0u8,1u8,2u8,37u8,8u8,0u8,7u8,56u8,4u8,0u8,9u8,73u8,7u8,2u8,0u8,0u8,0u8,0u8,11u8, - 106u8,11u8,0u8,0u8,28u8,0u8,1u8,0u8,0u8,29u8,2u8,1u8,0u8,0u8,30u8,3u8,1u8,0u8,0u8,31u8, - 3u8,4u8,0u8,0u8,34u8,5u8,6u8,0u8,0u8,35u8,7u8,1u8,0u8,0u8,38u8,8u8,9u8,0u8,0u8,39u8, - 3u8,1u8,0u8,0u8,40u8,10u8,1u8,0u8,0u8,41u8,11u8,12u8,0u8,0u8,42u8,3u8,4u8,0u8,0u8,43u8, - 13u8,14u8,0u8,0u8,44u8,3u8,4u8,0u8,0u8,45u8,15u8,1u8,0u8,0u8,46u8,16u8,4u8,0u8,0u8,47u8, - 17u8,1u8,0u8,0u8,48u8,3u8,9u8,0u8,0u8,49u8,3u8,12u8,0u8,0u8,50u8,3u8,18u8,0u8,0u8,51u8, - 19u8,1u8,0u8,0u8,52u8,15u8,1u8,0u8,0u8,53u8,17u8,1u8,0u8,0u8,54u8,2u8,1u8,0u8,0u8,55u8, - 2u8,1u8,0u8,0u8,57u8,20u8,1u8,0u8,0u8,58u8,15u8,1u8,0u8,10u8,91u8,9u8,22u8,0u8,7u8,92u8, - 21u8,4u8,0u8,6u8,93u8,24u8,1u8,1u8,6u8,8u8,94u8,13u8,9u8,0u8,9u8,95u8,27u8,28u8,2u8,4u8, - 4u8,4u8,96u8,30u8,31u8,1u8,0u8,10u8,97u8,32u8,1u8,0u8,5u8,98u8,4u8,4u8,0u8,9u8,99u8,35u8, - 18u8,2u8,4u8,4u8,9u8,100u8,35u8,36u8,2u8,4u8,4u8,3u8,101u8,38u8,39u8,1u8,0u8,12u8,102u8,41u8, - 1u8,1u8,0u8,1u8,103u8,42u8,43u8,0u8,10u8,104u8,44u8,1u8,0u8,10u8,105u8,13u8,45u8,0u8,5u8,107u8, - 4u8,4u8,0u8,11u8,108u8,1u8,48u8,0u8,11u8,109u8,49u8,50u8,0u8,4u8,110u8,51u8,4u8,1u8,0u8,5u8, - 111u8,4u8,4u8,0u8,7u8,112u8,4u8,52u8,0u8,9u8,113u8,53u8,1u8,2u8,4u8,4u8,10u8,114u8,57u8,46u8, - 0u8,4u8,115u8,31u8,1u8,1u8,0u8,7u8,116u8,58u8,4u8,0u8,7u8,117u8,58u8,59u8,0u8,7u8,118u8,60u8, - 4u8,0u8,7u8,119u8,21u8,4u8,0u8,4u8,120u8,61u8,31u8,1u8,0u8,4u8,121u8,62u8,1u8,1u8,0u8,7u8, - 122u8,64u8,1u8,0u8,9u8,112u8,1u8,65u8,2u8,4u8,4u8,1u8,123u8,13u8,66u8,1u8,6u8,5u8,124u8,4u8, - 4u8,0u8,10u8,125u8,74u8,1u8,0u8,10u8,126u8,76u8,1u8,0u8,5u8,127u8,4u8,4u8,0u8,9u8,128u8,1u8, - 27u8,78u8,2u8,4u8,4u8,10u8,129u8,1u8,79u8,1u8,0u8,7u8,130u8,1u8,58u8,4u8,0u8,7u8,131u8,1u8, - 60u8,4u8,0u8,7u8,132u8,1u8,86u8,4u8,0u8,7u8,133u8,1u8,86u8,4u8,0u8,7u8,134u8,1u8,87u8,1u8, - 0u8,10u8,135u8,1u8,9u8,9u8,0u8,10u8,136u8,1u8,79u8,1u8,0u8,28u8,23u8,30u8,26u8,31u8,29u8,28u8, - 33u8,34u8,26u8,35u8,26u8,36u8,9u8,37u8,40u8,44u8,29u8,47u8,26u8,28u8,54u8,49u8,29u8,54u8,29u8,55u8, - 29u8,28u8,63u8,57u8,26u8,58u8,54u8,58u8,67u8,58u8,68u8,58u8,33u8,58u8,69u8,58u8,70u8,58u8,71u8,58u8, - 23u8,58u8,63u8,28u8,69u8,28u8,68u8,63u8,26u8,28u8,71u8,28u8,70u8,58u8,84u8,28u8,84u8,28u8,67u8,5u8, - 5u8,7u8,8u8,6u8,5u8,3u8,7u8,11u8,14u8,1u8,8u8,0u8,0u8,3u8,6u8,12u8,5u8,3u8,2u8,5u8, - 5u8,1u8,3u8,4u8,6u8,12u8,5u8,5u8,10u8,2u8,3u8,12u8,8u8,15u8,8u8,16u8,6u8,6u8,12u8,5u8, - 5u8,3u8,3u8,10u8,2u8,6u8,6u8,12u8,5u8,5u8,11u8,17u8,1u8,8u8,18u8,3u8,10u8,2u8,1u8,5u8, - 4u8,5u8,5u8,7u8,8u8,6u8,7u8,11u8,14u8,1u8,8u8,3u8,1u8,6u8,8u8,6u8,3u8,3u8,3u8,3u8, - 1u8,6u8,12u8,1u8,8u8,9u8,3u8,6u8,12u8,5u8,5u8,4u8,5u8,7u8,8u8,6u8,7u8,11u8,14u8,1u8, - 8u8,0u8,7u8,11u8,14u8,1u8,8u8,4u8,2u8,6u8,12u8,5u8,1u8,1u8,4u8,6u8,12u8,5u8,5u8,3u8, - 4u8,7u8,8u8,19u8,3u8,5u8,3u8,3u8,7u8,8u8,19u8,5u8,3u8,4u8,3u8,3u8,3u8,3u8,1u8,8u8, - 0u8,2u8,7u8,11u8,14u8,1u8,9u8,0u8,9u8,0u8,5u8,5u8,11u8,17u8,1u8,8u8,18u8,5u8,7u8,8u8, - 6u8,7u8,8u8,9u8,2u8,5u8,8u8,6u8,2u8,7u8,11u8,20u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8, - 1u8,7u8,9u8,1u8,1u8,8u8,18u8,2u8,6u8,12u8,3u8,1u8,11u8,17u8,1u8,9u8,0u8,2u8,6u8,8u8, - 16u8,11u8,17u8,1u8,8u8,18u8,1u8,8u8,1u8,1u8,6u8,5u8,2u8,6u8,11u8,20u8,2u8,9u8,0u8,9u8, - 1u8,6u8,9u8,0u8,1u8,6u8,9u8,1u8,5u8,5u8,8u8,16u8,10u8,2u8,12u8,8u8,15u8,1u8,6u8,9u8, - 0u8,1u8,10u8,2u8,1u8,2u8,2u8,7u8,10u8,9u8,0u8,10u8,9u8,0u8,2u8,6u8,12u8,10u8,2u8,2u8, - 12u8,8u8,15u8,4u8,6u8,12u8,3u8,5u8,5u8,1u8,8u8,16u8,1u8,11u8,17u8,1u8,8u8,18u8,12u8,1u8, - 8u8,21u8,6u8,5u8,3u8,8u8,16u8,5u8,3u8,12u8,8u8,15u8,5u8,7u8,11u8,20u8,2u8,5u8,8u8,6u8, - 7u8,8u8,9u8,1u8,8u8,21u8,1u8,6u8,8u8,21u8,2u8,3u8,3u8,1u8,6u8,11u8,17u8,1u8,9u8,0u8, - 1u8,8u8,19u8,3u8,7u8,11u8,20u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8,8u8,2u8,2u8, - 7u8,8u8,6u8,7u8,8u8,9u8,12u8,5u8,3u8,11u8,17u8,1u8,8u8,18u8,3u8,3u8,7u8,8u8,19u8,3u8, - 3u8,5u8,5u8,10u8,5u8,3u8,2u8,6u8,8u8,16u8,3u8,1u8,6u8,8u8,19u8,1u8,10u8,5u8,2u8,6u8, - 8u8,19u8,5u8,2u8,7u8,11u8,17u8,1u8,9u8,0u8,3u8,2u8,5u8,11u8,17u8,1u8,9u8,0u8,1u8,8u8, - 3u8,2u8,7u8,8u8,19u8,3u8,1u8,11u8,20u8,2u8,9u8,0u8,9u8,1u8,1u8,11u8,14u8,1u8,9u8,0u8, - 1u8,8u8,13u8,1u8,8u8,5u8,1u8,8u8,4u8,1u8,8u8,11u8,1u8,8u8,10u8,4u8,1u8,5u8,7u8,8u8, - 6u8,7u8,8u8,9u8,3u8,3u8,3u8,5u8,2u8,3u8,6u8,8u8,16u8,4u8,5u8,5u8,7u8,8u8,6u8,7u8, - 8u8,9u8,1u8,6u8,8u8,16u8,10u8,6u8,5u8,5u8,5u8,5u8,7u8,11u8,14u8,1u8,8u8,10u8,5u8,5u8, - 8u8,6u8,7u8,11u8,20u8,2u8,5u8,8u8,6u8,7u8,8u8,9u8,2u8,9u8,0u8,9u8,1u8,2u8,6u8,8u8, - 16u8,5u8,2u8,3u8,5u8,4u8,3u8,5u8,3u8,3u8,11u8,5u8,5u8,3u8,3u8,7u8,11u8,14u8,1u8,8u8, - 11u8,3u8,3u8,5u8,5u8,7u8,8u8,6u8,7u8,8u8,9u8,5u8,1u8,3u8,5u8,7u8,8u8,6u8,7u8,8u8, - 9u8,1u8,8u8,12u8,15u8,10u8,5u8,3u8,3u8,5u8,5u8,3u8,3u8,3u8,3u8,3u8,5u8,6u8,10u8,5u8, - 3u8,3u8,3u8,3u8,6u8,8u8,19u8,3u8,3u8,4u8,7u8,8u8,19u8,5u8,5u8,3u8,5u8,5u8,5u8,5u8, - 7u8,8u8,6u8,7u8,8u8,9u8,16u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8, - 97u8,99u8,116u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8, - 105u8,110u8,3u8,98u8,99u8,115u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8, - 101u8,110u8,116u8,8u8,112u8,111u8,111u8,108u8,95u8,117u8,54u8,52u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,10u8, - 115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,5u8,115u8,116u8,97u8,107u8,101u8,14u8,115u8,116u8,97u8, - 107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,20u8,65u8, - 100u8,100u8,68u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,13u8, - 65u8,100u8,100u8,83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8,110u8,116u8,26u8,67u8,114u8,101u8,97u8,116u8,101u8, - 83u8,116u8,97u8,107u8,105u8,110u8,103u8,67u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,69u8,118u8,101u8,110u8,116u8, - 15u8,68u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,69u8,118u8,101u8,110u8,116u8,22u8,82u8,101u8,113u8, - 117u8,101u8,115u8,116u8,67u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,16u8, - 82u8,101u8,115u8,101u8,116u8,76u8,111u8,99u8,107u8,117u8,112u8,69u8,118u8,101u8,110u8,116u8,15u8,83u8,116u8,97u8, - 107u8,105u8,110u8,103u8,67u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,21u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8, - 71u8,114u8,111u8,117u8,112u8,67u8,111u8,110u8,116u8,97u8,105u8,110u8,101u8,114u8,33u8,83u8,116u8,97u8,107u8,105u8, - 110u8,103u8,71u8,114u8,111u8,117u8,112u8,85u8,112u8,100u8,97u8,116u8,101u8,67u8,111u8,109u8,109u8,105u8,115u8,115u8, - 105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,5u8,83u8,116u8,111u8,114u8,101u8,19u8,83u8,119u8,105u8,116u8,99u8, - 104u8,79u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,69u8,118u8,101u8,110u8,116u8,16u8,85u8,110u8,108u8,111u8,99u8, - 107u8,83u8,116u8,97u8,107u8,101u8,69u8,118u8,101u8,110u8,116u8,21u8,85u8,112u8,100u8,97u8,116u8,101u8,67u8,111u8, - 109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,16u8,85u8,112u8,100u8,97u8,116u8,101u8, - 86u8,111u8,116u8,101u8,114u8,69u8,118u8,101u8,110u8,116u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8, - 108u8,101u8,16u8,97u8,100u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,9u8, - 97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,30u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,115u8,116u8,97u8, - 107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8, - 21u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8, - 103u8,101u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,15u8, - 79u8,119u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,17u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,4u8,67u8,111u8, - 105u8,110u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,34u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8, - 115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,119u8,105u8,116u8, - 104u8,95u8,99u8,111u8,105u8,110u8,115u8,10u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,19u8,100u8, - 105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,37u8,103u8, - 101u8,116u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8, - 97u8,109u8,111u8,117u8,110u8,116u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,23u8,108u8,97u8,115u8, - 116u8,95u8,114u8,101u8,99u8,111u8,114u8,100u8,101u8,100u8,95u8,112u8,114u8,105u8,110u8,99u8,105u8,112u8,97u8,108u8, - 28u8,110u8,101u8,119u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,115u8,95u8,104u8,111u8,108u8,100u8,101u8,114u8,27u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,100u8,105u8, - 115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,95u8,99u8,111u8,117u8,110u8,116u8,115u8,18u8,114u8,101u8, - 113u8,117u8,101u8,115u8,116u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,27u8,114u8,101u8,113u8, - 117u8,101u8,115u8,116u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,105u8,110u8,116u8,101u8, - 114u8,110u8,97u8,108u8,12u8,114u8,101u8,115u8,101u8,116u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,18u8,115u8,116u8, - 97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,24u8,115u8,116u8,97u8, - 107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8, - 115u8,23u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,101u8, - 120u8,105u8,115u8,116u8,115u8,15u8,115u8,119u8,105u8,116u8,99u8,104u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8, - 114u8,36u8,115u8,119u8,105u8,116u8,99u8,104u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,119u8,105u8, - 116u8,104u8,95u8,115u8,97u8,109u8,101u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,14u8,117u8, - 110u8,108u8,111u8,99u8,107u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,12u8,117u8,110u8,108u8,111u8,99u8,107u8, - 95u8,115u8,116u8,97u8,107u8,101u8,16u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8, - 105u8,111u8,110u8,4u8,80u8,111u8,111u8,108u8,24u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,100u8,105u8,115u8,116u8, - 114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8,108u8,12u8,117u8,112u8,100u8,97u8,116u8,101u8, - 95u8,118u8,111u8,116u8,101u8,114u8,8u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,12u8,112u8,111u8,111u8,108u8, - 95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,5u8,118u8,111u8,116u8,101u8, - 114u8,9u8,112u8,114u8,105u8,110u8,99u8,105u8,112u8,97u8,108u8,9u8,114u8,101u8,99u8,105u8,112u8,105u8,101u8,110u8, - 116u8,19u8,97u8,99u8,99u8,117u8,109u8,117u8,108u8,97u8,116u8,101u8,100u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8, - 115u8,17u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,9u8, - 111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,17u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8, - 111u8,110u8,95u8,112u8,111u8,111u8,108u8,10u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,11u8,100u8, - 117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8,24u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,99u8,111u8, - 109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,17u8,115u8,116u8,97u8,107u8, - 105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8, - 77u8,97u8,112u8,30u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8, - 111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,117u8,112u8,100u8,97u8,116u8, - 101u8,95u8,118u8,111u8,116u8,101u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,114u8,101u8,115u8,101u8,116u8, - 95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,16u8,97u8,100u8,100u8,95u8,115u8, - 116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,25u8,114u8,101u8,113u8,117u8,101u8,115u8,116u8,95u8, - 99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,117u8,110u8, - 108u8,111u8,99u8,107u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,22u8,115u8,119u8, - 105u8,116u8,99u8,104u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8, - 23u8,97u8,100u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,95u8,101u8,118u8, - 101u8,110u8,116u8,115u8,17u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,12u8,111u8,108u8,100u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,12u8,110u8,101u8,119u8,95u8, - 111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,15u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8, - 112u8,97u8,105u8,100u8,6u8,115u8,116u8,97u8,107u8,101u8,114u8,25u8,111u8,108u8,100u8,95u8,99u8,111u8,109u8,109u8, - 105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,25u8,110u8,101u8, - 119u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8, - 97u8,103u8,101u8,9u8,111u8,108u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,9u8,110u8,101u8,119u8,95u8,118u8,111u8, - 116u8,101u8,114u8,9u8,103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,6u8,98u8,117u8,121u8,95u8,105u8,110u8, - 10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8, - 111u8,102u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8, - 97u8,119u8,18u8,97u8,100u8,100u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8, - 112u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8, - 95u8,107u8,101u8,121u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8, - 6u8,97u8,112u8,112u8,101u8,110u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8, - 114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,22u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,111u8,119u8,110u8,101u8,114u8,17u8,101u8,120u8,116u8,114u8,97u8, - 99u8,116u8,95u8,111u8,119u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,13u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8, - 67u8,111u8,110u8,102u8,105u8,103u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8, - 101u8,110u8,116u8,3u8,103u8,101u8,116u8,18u8,103u8,101u8,116u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8, - 95u8,115u8,116u8,97u8,107u8,101u8,5u8,118u8,97u8,108u8,117u8,101u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8, - 95u8,101u8,120u8,105u8,115u8,116u8,115u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,3u8,97u8,100u8,100u8,17u8,119u8, - 105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,12u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,122u8,101u8,114u8,111u8,18u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8, - 114u8,115u8,95u8,99u8,111u8,117u8,110u8,116u8,12u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8, - 115u8,6u8,115u8,104u8,97u8,114u8,101u8,115u8,13u8,114u8,101u8,100u8,101u8,101u8,109u8,95u8,115u8,104u8,97u8,114u8, - 101u8,115u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,7u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,18u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,16u8,110u8,101u8, - 119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,15u8,117u8,110u8,97u8,117u8,116u8, - 104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,101u8,100u8,15u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,119u8,105u8, - 116u8,104u8,95u8,99u8,97u8,112u8,24u8,105u8,110u8,99u8,114u8,101u8,97u8,115u8,101u8,95u8,108u8,111u8,99u8,107u8, - 117u8,112u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8, - 115u8,116u8,97u8,116u8,101u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,21u8,115u8,101u8,116u8,95u8,111u8,112u8,101u8, - 114u8,97u8,116u8,111u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,11u8,116u8,111u8,116u8,97u8,108u8, - 95u8,99u8,111u8,105u8,110u8,115u8,7u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,33u8,115u8,104u8,97u8,114u8,101u8, - 115u8,95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8, - 97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,33u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,116u8,111u8,95u8,115u8, - 104u8,97u8,114u8,101u8,115u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8, - 110u8,115u8,15u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,115u8,104u8,97u8,114u8,101u8,115u8,19u8,103u8, - 101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,28u8,115u8, - 101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8,95u8,119u8, - 105u8,116u8,104u8,95u8,99u8,97u8,112u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8, - 8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,34u8,33u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,58u8,58u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8, - 110u8,116u8,114u8,97u8,99u8,116u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8, - 116u8,97u8,95u8,118u8,49u8,197u8,8u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,73u8,78u8, - 83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,65u8,77u8,79u8, - 85u8,78u8,84u8,96u8,83u8,116u8,111u8,114u8,101u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,109u8,117u8,115u8, - 116u8,32u8,98u8,101u8,32u8,97u8,116u8,32u8,108u8,101u8,97u8,115u8,116u8,32u8,116u8,104u8,101u8,32u8,109u8,105u8, - 110u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,32u8,102u8,111u8,114u8, - 32u8,97u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,112u8,111u8,111u8,108u8,32u8,116u8,111u8,32u8,106u8,111u8,105u8, - 110u8,32u8,116u8,104u8,101u8,32u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,32u8,115u8,101u8,116u8,46u8, - 2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,67u8,79u8, - 77u8,77u8,73u8,83u8,83u8,73u8,79u8,78u8,95u8,80u8,69u8,82u8,67u8,69u8,78u8,84u8,65u8,71u8,69u8,50u8, - 67u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,32u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8, - 101u8,32u8,104u8,97u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,98u8,101u8,116u8,119u8,101u8,101u8,110u8,32u8, - 48u8,32u8,97u8,110u8,100u8,32u8,49u8,48u8,48u8,46u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,69u8, - 78u8,79u8,95u8,83u8,84u8,65u8,75u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8, - 70u8,79u8,85u8,78u8,68u8,95u8,70u8,79u8,82u8,95u8,83u8,84u8,65u8,75u8,69u8,82u8,32u8,83u8,116u8,97u8, - 107u8,101u8,114u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,32u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,32u8,99u8, - 111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,39u8,69u8,78u8, - 79u8,95u8,83u8,84u8,65u8,75u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8,70u8, - 79u8,85u8,78u8,68u8,95u8,70u8,79u8,82u8,95u8,79u8,80u8,69u8,82u8,65u8,84u8,79u8,82u8,58u8,78u8,111u8, - 32u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,32u8,98u8,101u8, - 116u8,119u8,101u8,101u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,116u8,97u8,107u8,101u8,114u8,32u8,97u8,110u8,100u8, - 32u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,32u8,102u8,111u8,117u8,110u8,100u8,46u8,5u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,29u8,69u8,67u8,65u8,78u8,84u8,95u8,77u8,69u8,82u8,71u8,69u8,95u8,83u8,84u8,65u8, - 75u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,83u8,34u8,83u8,116u8,97u8,107u8,105u8, - 110u8,103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,32u8,99u8,97u8,110u8,39u8,116u8,32u8,98u8, - 101u8,32u8,109u8,101u8,114u8,103u8,101u8,100u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,32u8,69u8,83u8, - 84u8,65u8,75u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8,65u8,76u8,82u8,69u8, - 65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,61u8,84u8,104u8,101u8,32u8,115u8,116u8,97u8,107u8,105u8, - 110u8,103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8, - 101u8,120u8,105u8,115u8,116u8,115u8,32u8,97u8,110u8,100u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8, - 32u8,114u8,101u8,45u8,99u8,114u8,101u8,97u8,116u8,101u8,100u8,46u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 38u8,69u8,73u8,78u8,83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,65u8,67u8,84u8,73u8,86u8, - 69u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,84u8,79u8,95u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8,105u8, - 78u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,97u8,99u8,116u8,105u8,118u8,101u8,32u8,115u8,116u8, - 97u8,107u8,101u8,32u8,116u8,111u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,46u8,32u8,83u8,111u8,109u8, - 101u8,32u8,115u8,116u8,97u8,107u8,101u8,32u8,109u8,105u8,103u8,104u8,116u8,32u8,115u8,116u8,105u8,108u8,108u8,32u8, - 112u8,101u8,110u8,100u8,105u8,110u8,103u8,32u8,97u8,110u8,100u8,32u8,119u8,105u8,108u8,108u8,32u8,98u8,101u8,32u8, - 97u8,99u8,116u8,105u8,118u8,101u8,32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,110u8,101u8,120u8,116u8,32u8,101u8, - 112u8,111u8,99u8,104u8,46u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,78u8,79u8,84u8,95u8,83u8, - 84u8,65u8,75u8,69u8,82u8,95u8,79u8,82u8,95u8,79u8,80u8,69u8,82u8,65u8,84u8,79u8,82u8,45u8,67u8,97u8, - 108u8,108u8,101u8,114u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,101u8,105u8,116u8,104u8,101u8,114u8,32u8, - 116u8,104u8,101u8,32u8,115u8,116u8,97u8,107u8,101u8,114u8,32u8,111u8,114u8,32u8,111u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,46u8,2u8,21u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8,71u8,114u8,111u8,117u8,112u8,67u8,111u8,110u8, - 116u8,97u8,105u8,110u8,101u8,114u8,1u8,2u8,1u8,7u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,33u8,83u8,116u8, - 97u8,107u8,105u8,110u8,103u8,71u8,114u8,111u8,117u8,112u8,85u8,112u8,100u8,97u8,116u8,101u8,67u8,111u8,109u8,109u8, - 105u8,115u8,115u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,1u8,3u8,1u8,44u8,48u8,120u8,49u8,58u8,58u8, - 115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,58u8,58u8,83u8,116u8, - 97u8,107u8,105u8,110u8,103u8,71u8,114u8,111u8,117u8,112u8,67u8,111u8,110u8,116u8,97u8,105u8,110u8,101u8,114u8,6u8, - 18u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,1u8, - 1u8,0u8,21u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8, - 116u8,97u8,103u8,101u8,1u8,1u8,0u8,23u8,108u8,97u8,115u8,116u8,95u8,114u8,101u8,99u8,111u8,114u8,100u8,101u8, - 100u8,95u8,112u8,114u8,105u8,110u8,99u8,105u8,112u8,97u8,108u8,1u8,1u8,0u8,23u8,115u8,116u8,97u8,107u8,105u8, - 110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,1u8,1u8, - 0u8,24u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,97u8, - 109u8,111u8,117u8,110u8,116u8,115u8,1u8,1u8,0u8,27u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,100u8,105u8, - 115u8,116u8,114u8,105u8,98u8,117u8,116u8,105u8,111u8,110u8,95u8,99u8,111u8,117u8,110u8,116u8,115u8,1u8,1u8,0u8, - 0u8,2u8,3u8,59u8,5u8,60u8,5u8,61u8,3u8,1u8,2u8,3u8,59u8,5u8,60u8,5u8,61u8,3u8,2u8,2u8, - 5u8,59u8,5u8,62u8,5u8,60u8,5u8,63u8,3u8,31u8,3u8,3u8,2u8,4u8,59u8,5u8,60u8,5u8,64u8,5u8, - 61u8,3u8,4u8,2u8,4u8,59u8,5u8,60u8,5u8,65u8,3u8,66u8,3u8,5u8,2u8,2u8,59u8,5u8,60u8,5u8, - 6u8,2u8,6u8,63u8,3u8,60u8,5u8,67u8,8u8,16u8,31u8,3u8,68u8,8u8,19u8,69u8,8u8,15u8,7u8,2u8, - 1u8,70u8,1u8,8u8,2u8,1u8,71u8,11u8,14u8,1u8,8u8,12u8,9u8,2u8,10u8,72u8,11u8,20u8,2u8,5u8, - 8u8,6u8,74u8,11u8,14u8,1u8,8u8,2u8,75u8,11u8,14u8,1u8,8u8,13u8,76u8,11u8,14u8,1u8,8u8,5u8, - 77u8,11u8,14u8,1u8,8u8,1u8,78u8,11u8,14u8,1u8,8u8,4u8,79u8,11u8,14u8,1u8,8u8,11u8,80u8,11u8, - 14u8,1u8,8u8,10u8,81u8,11u8,14u8,1u8,8u8,0u8,82u8,11u8,14u8,1u8,8u8,3u8,10u8,2u8,3u8,83u8, - 5u8,84u8,5u8,60u8,5u8,11u8,2u8,4u8,59u8,5u8,60u8,5u8,61u8,3u8,85u8,3u8,12u8,2u8,4u8,86u8, - 5u8,59u8,5u8,87u8,3u8,88u8,3u8,13u8,2u8,4u8,59u8,5u8,60u8,5u8,89u8,5u8,90u8,5u8,0u8,0u8, - 0u8,0u8,21u8,34u8,10u8,1u8,15u8,0u8,12u8,5u8,10u8,1u8,16u8,1u8,20u8,17u8,26u8,12u8,7u8,1u8, - 1u8,1u8,10u8,5u8,11u8,7u8,10u8,0u8,10u8,1u8,16u8,2u8,20u8,17u8,24u8,11u8,5u8,11u8,2u8,10u8, - 3u8,17u8,27u8,1u8,11u8,1u8,16u8,1u8,20u8,12u8,6u8,11u8,4u8,11u8,0u8,11u8,6u8,11u8,3u8,18u8, - 0u8,56u8,0u8,2u8,1u8,1u8,4u8,1u8,9u8,25u8,42u8,10u8,0u8,17u8,29u8,12u8,5u8,10u8,5u8,10u8, - 1u8,17u8,2u8,11u8,5u8,42u8,9u8,12u8,7u8,10u8,7u8,15u8,3u8,14u8,1u8,56u8,1u8,12u8,6u8,11u8, - 0u8,10u8,2u8,56u8,2u8,12u8,4u8,10u8,6u8,16u8,4u8,11u8,4u8,17u8,32u8,10u8,6u8,16u8,5u8,20u8, - 10u8,2u8,22u8,10u8,6u8,15u8,5u8,21u8,11u8,6u8,16u8,1u8,20u8,12u8,3u8,11u8,7u8,15u8,6u8,11u8, - 1u8,11u8,3u8,11u8,2u8,18u8,1u8,56u8,3u8,2u8,2u8,0u8,0u8,1u8,9u8,34u8,21u8,10u8,0u8,41u8, - 9u8,4u8,4u8,5u8,7u8,7u8,6u8,17u8,33u8,39u8,11u8,0u8,42u8,9u8,15u8,3u8,14u8,1u8,12u8,2u8, - 46u8,11u8,2u8,56u8,4u8,4u8,17u8,5u8,20u8,7u8,5u8,17u8,33u8,39u8,2u8,3u8,1u8,0u8,1u8,9u8, - 1u8,11u8,10u8,0u8,10u8,1u8,17u8,2u8,11u8,0u8,43u8,9u8,16u8,3u8,14u8,1u8,56u8,5u8,16u8,2u8, - 20u8,2u8,4u8,0u8,0u8,0u8,37u8,33u8,10u8,0u8,17u8,29u8,12u8,4u8,14u8,4u8,56u8,6u8,12u8,6u8, - 13u8,6u8,14u8,1u8,56u8,6u8,56u8,7u8,13u8,6u8,7u8,9u8,56u8,7u8,13u8,6u8,11u8,3u8,56u8,7u8, - 11u8,0u8,11u8,6u8,17u8,38u8,12u8,8u8,12u8,7u8,14u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,11u8,1u8,11u8,2u8,17u8,39u8,14u8,7u8,17u8,40u8,12u8,5u8,11u8,7u8,11u8,8u8,11u8,5u8,2u8, - 5u8,1u8,4u8,1u8,9u8,46u8,13u8,10u8,0u8,11u8,3u8,56u8,2u8,12u8,6u8,11u8,0u8,11u8,1u8,11u8, - 2u8,11u8,6u8,11u8,4u8,11u8,5u8,17u8,6u8,1u8,2u8,6u8,1u8,0u8,1u8,9u8,47u8,109u8,10u8,4u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,38u8,4u8,9u8,10u8,4u8,6u8,100u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,37u8,12u8,6u8,5u8,11u8,9u8,12u8,6u8,11u8,6u8,4u8,14u8,5u8,19u8,11u8,0u8,1u8, - 7u8,3u8,17u8,41u8,39u8,17u8,42u8,12u8,7u8,14u8,7u8,17u8,43u8,1u8,12u8,9u8,14u8,3u8,56u8,8u8, - 12u8,12u8,10u8,12u8,11u8,9u8,38u8,4u8,33u8,5u8,38u8,11u8,0u8,1u8,7u8,2u8,17u8,41u8,39u8,10u8, - 0u8,17u8,29u8,12u8,15u8,10u8,15u8,41u8,9u8,32u8,4u8,49u8,10u8,0u8,10u8,0u8,17u8,11u8,45u8,9u8, - 11u8,15u8,42u8,9u8,12u8,17u8,10u8,17u8,15u8,3u8,12u8,16u8,10u8,16u8,14u8,1u8,12u8,8u8,46u8,11u8, - 8u8,56u8,4u8,32u8,4u8,64u8,5u8,73u8,11u8,17u8,1u8,11u8,16u8,1u8,11u8,0u8,1u8,7u8,7u8,17u8, - 45u8,39u8,11u8,0u8,10u8,1u8,10u8,2u8,11u8,5u8,17u8,4u8,12u8,10u8,12u8,14u8,12u8,13u8,14u8,10u8, - 11u8,3u8,17u8,32u8,14u8,13u8,17u8,29u8,12u8,11u8,11u8,16u8,10u8,1u8,10u8,12u8,10u8,11u8,11u8,10u8, - 10u8,4u8,7u8,8u8,17u8,46u8,11u8,14u8,18u8,6u8,56u8,9u8,11u8,17u8,15u8,7u8,11u8,1u8,11u8,2u8, - 10u8,11u8,11u8,12u8,11u8,4u8,18u8,2u8,56u8,10u8,11u8,11u8,2u8,7u8,1u8,4u8,1u8,9u8,55u8,18u8, - 10u8,0u8,10u8,1u8,17u8,2u8,10u8,0u8,42u8,9u8,12u8,3u8,10u8,3u8,15u8,3u8,14u8,1u8,56u8,1u8, - 12u8,2u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8,15u8,8u8,17u8,8u8,2u8,8u8,0u8,0u8,0u8,56u8, - 104u8,10u8,2u8,16u8,1u8,20u8,12u8,12u8,10u8,12u8,17u8,26u8,12u8,11u8,1u8,12u8,10u8,1u8,11u8,10u8, - 11u8,11u8,22u8,12u8,15u8,10u8,2u8,16u8,4u8,11u8,15u8,17u8,48u8,12u8,6u8,14u8,6u8,56u8,8u8,12u8, - 8u8,10u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,33u8,11u8,2u8,1u8,11u8,3u8, - 1u8,11u8,6u8,56u8,11u8,2u8,10u8,2u8,15u8,0u8,12u8,9u8,10u8,9u8,11u8,8u8,10u8,1u8,11u8,2u8, - 16u8,2u8,20u8,17u8,24u8,10u8,9u8,46u8,17u8,50u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8, - 4u8,85u8,5u8,50u8,10u8,9u8,46u8,17u8,51u8,12u8,14u8,13u8,14u8,46u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,66u8,9u8,20u8,12u8,13u8,10u8,9u8,10u8,13u8,12u8,4u8,46u8,11u8,4u8,17u8,52u8,12u8, - 7u8,10u8,9u8,10u8,13u8,11u8,7u8,17u8,53u8,12u8,5u8,10u8,13u8,13u8,6u8,10u8,5u8,56u8,12u8,56u8, - 13u8,10u8,3u8,10u8,1u8,10u8,12u8,11u8,13u8,11u8,5u8,18u8,3u8,56u8,14u8,5u8,43u8,11u8,3u8,1u8, - 14u8,6u8,56u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,99u8,11u8,0u8,11u8,6u8, - 56u8,13u8,11u8,9u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,56u8,5u8,103u8,11u8,9u8,1u8, - 11u8,6u8,56u8,11u8,2u8,9u8,0u8,0u8,0u8,22u8,28u8,10u8,0u8,16u8,1u8,20u8,17u8,26u8,1u8,12u8, - 3u8,1u8,11u8,3u8,22u8,12u8,4u8,10u8,4u8,10u8,0u8,16u8,5u8,20u8,23u8,12u8,1u8,10u8,1u8,11u8, - 0u8,16u8,2u8,20u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,2u8,11u8,4u8,11u8, - 1u8,11u8,2u8,2u8,10u8,1u8,0u8,1u8,9u8,1u8,11u8,10u8,0u8,10u8,1u8,17u8,2u8,11u8,0u8,43u8, - 9u8,16u8,3u8,14u8,1u8,56u8,5u8,16u8,5u8,20u8,2u8,11u8,0u8,0u8,0u8,1u8,21u8,56u8,15u8,10u8, - 0u8,56u8,16u8,10u8,0u8,56u8,17u8,10u8,0u8,56u8,18u8,10u8,0u8,56u8,19u8,10u8,0u8,56u8,20u8,10u8, - 0u8,56u8,21u8,10u8,0u8,56u8,22u8,10u8,0u8,56u8,23u8,11u8,0u8,56u8,24u8,18u8,9u8,2u8,12u8,1u8, - 0u8,1u8,9u8,1u8,11u8,10u8,0u8,10u8,1u8,17u8,2u8,11u8,0u8,43u8,9u8,16u8,3u8,14u8,1u8,56u8, - 5u8,16u8,0u8,17u8,50u8,2u8,13u8,1u8,4u8,1u8,9u8,72u8,57u8,11u8,0u8,17u8,29u8,12u8,4u8,10u8, - 4u8,10u8,1u8,33u8,4u8,10u8,8u8,12u8,3u8,5u8,14u8,11u8,4u8,10u8,2u8,33u8,12u8,3u8,11u8,3u8, - 4u8,17u8,5u8,20u8,7u8,4u8,17u8,59u8,39u8,10u8,1u8,10u8,2u8,17u8,2u8,10u8,1u8,42u8,9u8,12u8, - 6u8,10u8,6u8,15u8,3u8,14u8,2u8,56u8,1u8,12u8,5u8,10u8,5u8,16u8,2u8,20u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,33u8,4u8,42u8,11u8,6u8,1u8,11u8,5u8,1u8,2u8,11u8,1u8,10u8,2u8,10u8, - 5u8,10u8,6u8,15u8,8u8,17u8,8u8,11u8,2u8,11u8,5u8,10u8,6u8,15u8,9u8,11u8,6u8,15u8,10u8,17u8, - 14u8,1u8,2u8,14u8,0u8,0u8,0u8,73u8,45u8,10u8,1u8,46u8,17u8,9u8,12u8,5u8,12u8,4u8,10u8,5u8, - 23u8,10u8,1u8,15u8,5u8,21u8,10u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,22u8, - 11u8,1u8,1u8,11u8,3u8,1u8,11u8,2u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,10u8, - 0u8,10u8,1u8,10u8,0u8,10u8,5u8,11u8,2u8,17u8,0u8,10u8,5u8,10u8,1u8,16u8,4u8,17u8,60u8,11u8, - 1u8,16u8,1u8,20u8,12u8,6u8,11u8,3u8,11u8,0u8,11u8,6u8,11u8,4u8,10u8,5u8,18u8,4u8,56u8,25u8, - 11u8,5u8,2u8,15u8,1u8,4u8,1u8,9u8,75u8,28u8,11u8,0u8,17u8,29u8,12u8,3u8,10u8,3u8,10u8,1u8, - 17u8,2u8,11u8,3u8,42u8,9u8,12u8,5u8,10u8,5u8,15u8,3u8,14u8,1u8,56u8,1u8,12u8,4u8,10u8,4u8, - 16u8,1u8,20u8,12u8,2u8,11u8,4u8,16u8,4u8,17u8,61u8,11u8,5u8,15u8,11u8,11u8,1u8,11u8,2u8,18u8, - 5u8,56u8,26u8,2u8,16u8,1u8,0u8,1u8,9u8,1u8,11u8,10u8,0u8,10u8,1u8,17u8,2u8,11u8,0u8,43u8, - 9u8,16u8,3u8,14u8,1u8,56u8,5u8,16u8,1u8,20u8,2u8,17u8,1u8,0u8,1u8,9u8,1u8,10u8,10u8,0u8, - 10u8,1u8,17u8,2u8,11u8,0u8,43u8,9u8,16u8,3u8,14u8,1u8,56u8,5u8,17u8,9u8,2u8,18u8,1u8,0u8, - 1u8,9u8,1u8,12u8,10u8,0u8,41u8,9u8,32u8,4u8,6u8,9u8,2u8,11u8,0u8,43u8,9u8,16u8,3u8,14u8, - 1u8,56u8,4u8,2u8,19u8,1u8,4u8,1u8,9u8,77u8,79u8,11u8,0u8,17u8,29u8,12u8,10u8,10u8,10u8,10u8, - 1u8,17u8,2u8,10u8,10u8,42u8,9u8,12u8,13u8,10u8,13u8,15u8,3u8,12u8,12u8,10u8,12u8,14u8,2u8,12u8, - 4u8,46u8,11u8,4u8,56u8,4u8,32u8,4u8,21u8,5u8,28u8,11u8,13u8,1u8,11u8,12u8,1u8,7u8,0u8,17u8, - 62u8,39u8,10u8,12u8,14u8,1u8,56u8,27u8,12u8,11u8,1u8,11u8,10u8,10u8,1u8,13u8,11u8,10u8,13u8,15u8, - 8u8,17u8,8u8,10u8,1u8,13u8,11u8,10u8,13u8,15u8,9u8,10u8,13u8,15u8,10u8,17u8,14u8,1u8,14u8,11u8, - 16u8,4u8,10u8,2u8,17u8,64u8,11u8,3u8,13u8,11u8,15u8,2u8,21u8,14u8,11u8,16u8,1u8,20u8,12u8,9u8, - 11u8,12u8,10u8,2u8,11u8,11u8,56u8,9u8,11u8,13u8,15u8,12u8,12u8,8u8,11u8,9u8,12u8,5u8,11u8,1u8, - 12u8,6u8,11u8,2u8,12u8,7u8,11u8,8u8,11u8,6u8,11u8,7u8,11u8,5u8,18u8,10u8,56u8,28u8,2u8,20u8, - 1u8,4u8,1u8,9u8,80u8,16u8,10u8,0u8,17u8,29u8,12u8,4u8,10u8,4u8,10u8,1u8,17u8,2u8,11u8,4u8, - 10u8,1u8,17u8,3u8,12u8,3u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8,17u8,19u8,2u8,21u8,1u8,4u8, - 1u8,9u8,81u8,21u8,10u8,0u8,17u8,29u8,12u8,3u8,10u8,3u8,10u8,1u8,17u8,2u8,11u8,3u8,10u8,1u8, - 17u8,17u8,12u8,5u8,12u8,2u8,1u8,11u8,2u8,11u8,5u8,23u8,12u8,4u8,11u8,0u8,11u8,1u8,11u8,4u8, - 17u8,22u8,2u8,22u8,1u8,4u8,1u8,9u8,82u8,91u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,33u8,4u8,7u8,11u8,0u8,1u8,2u8,11u8,0u8,17u8,29u8,12u8,11u8,10u8,11u8,10u8,1u8,17u8,2u8, - 10u8,11u8,42u8,9u8,12u8,13u8,10u8,13u8,15u8,3u8,14u8,1u8,56u8,1u8,12u8,12u8,10u8,11u8,10u8,1u8, - 10u8,12u8,10u8,13u8,15u8,8u8,17u8,8u8,10u8,1u8,10u8,12u8,10u8,13u8,15u8,9u8,10u8,13u8,15u8,10u8, - 17u8,14u8,12u8,9u8,10u8,12u8,16u8,1u8,20u8,17u8,26u8,1u8,1u8,1u8,12u8,8u8,10u8,8u8,10u8,2u8, - 35u8,4u8,49u8,11u8,8u8,12u8,2u8,10u8,12u8,16u8,5u8,20u8,10u8,2u8,23u8,10u8,12u8,15u8,5u8,21u8, - 10u8,1u8,10u8,12u8,11u8,11u8,10u8,2u8,10u8,13u8,15u8,9u8,17u8,0u8,10u8,2u8,10u8,12u8,16u8,4u8, - 17u8,60u8,11u8,12u8,16u8,1u8,20u8,12u8,10u8,11u8,13u8,15u8,13u8,12u8,7u8,11u8,10u8,12u8,3u8,11u8, - 1u8,12u8,4u8,11u8,2u8,12u8,5u8,11u8,9u8,12u8,6u8,11u8,7u8,11u8,4u8,11u8,3u8,11u8,5u8,11u8, - 6u8,18u8,11u8,56u8,29u8,2u8,23u8,1u8,4u8,2u8,8u8,9u8,83u8,83u8,10u8,2u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,38u8,4u8,9u8,10u8,2u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8, - 12u8,3u8,5u8,11u8,9u8,12u8,3u8,11u8,3u8,4u8,14u8,5u8,19u8,11u8,0u8,1u8,7u8,3u8,17u8,41u8, - 39u8,10u8,0u8,17u8,29u8,12u8,5u8,10u8,5u8,41u8,9u8,4u8,26u8,5u8,31u8,11u8,0u8,1u8,7u8,6u8, - 17u8,33u8,39u8,10u8,5u8,42u8,9u8,12u8,7u8,10u8,7u8,15u8,3u8,14u8,1u8,56u8,1u8,12u8,6u8,10u8, - 5u8,10u8,1u8,10u8,6u8,10u8,7u8,15u8,8u8,17u8,8u8,10u8,1u8,10u8,6u8,10u8,7u8,15u8,9u8,11u8, - 7u8,15u8,10u8,17u8,14u8,1u8,10u8,6u8,16u8,2u8,20u8,12u8,4u8,10u8,2u8,11u8,6u8,15u8,2u8,21u8, - 10u8,5u8,41u8,8u8,32u8,4u8,71u8,10u8,0u8,11u8,0u8,56u8,30u8,18u8,8u8,45u8,8u8,5u8,73u8,11u8, - 0u8,1u8,10u8,5u8,42u8,8u8,15u8,14u8,11u8,5u8,11u8,1u8,11u8,4u8,11u8,2u8,18u8,12u8,56u8,31u8, - 2u8,24u8,0u8,0u8,0u8,85u8,90u8,10u8,0u8,46u8,17u8,65u8,10u8,1u8,33u8,4u8,9u8,11u8,0u8,1u8, - 2u8,10u8,0u8,46u8,17u8,51u8,12u8,4u8,14u8,4u8,12u8,15u8,10u8,15u8,65u8,9u8,12u8,12u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,11u8,10u8,11u8,10u8,12u8,35u8,4u8,84u8,5u8,25u8,10u8,15u8, - 10u8,11u8,66u8,9u8,20u8,12u8,14u8,10u8,14u8,10u8,2u8,34u8,4u8,79u8,10u8,0u8,10u8,14u8,12u8,7u8, - 46u8,11u8,7u8,17u8,52u8,12u8,16u8,10u8,0u8,10u8,14u8,12u8,8u8,46u8,11u8,8u8,17u8,66u8,12u8,13u8, - 10u8,0u8,11u8,16u8,10u8,1u8,12u8,10u8,12u8,9u8,46u8,11u8,9u8,11u8,10u8,17u8,67u8,11u8,13u8,23u8, - 10u8,3u8,24u8,6u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,12u8,18u8,10u8,0u8,11u8,18u8,10u8, - 1u8,12u8,6u8,12u8,5u8,46u8,11u8,5u8,11u8,6u8,17u8,68u8,12u8,17u8,10u8,0u8,11u8,14u8,10u8,2u8, - 11u8,17u8,17u8,69u8,11u8,11u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,11u8,5u8,20u8, - 11u8,15u8,1u8,11u8,0u8,11u8,1u8,17u8,56u8,2u8,25u8,1u8,4u8,1u8,9u8,88u8,34u8,11u8,0u8,17u8, - 29u8,12u8,5u8,10u8,5u8,10u8,1u8,17u8,2u8,11u8,5u8,42u8,9u8,12u8,7u8,10u8,7u8,15u8,3u8,14u8, - 1u8,56u8,1u8,12u8,6u8,10u8,6u8,16u8,1u8,20u8,12u8,4u8,10u8,4u8,17u8,70u8,12u8,3u8,11u8,6u8, - 16u8,4u8,10u8,2u8,17u8,71u8,11u8,7u8,15u8,15u8,11u8,1u8,11u8,4u8,11u8,3u8,11u8,2u8,18u8,13u8, - 56u8,32u8,2u8,6u8,4u8,6u8,1u8,6u8,3u8,9u8,0u8,6u8,2u8,6u8,0u8,9u8,4u8,9u8,1u8,9u8, - 9u8,9u8,8u8,9u8,5u8,9u8,3u8,9u8,7u8,9u8,6u8,8u8,0u8,9u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk30); - let chunk31 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,13u8,1u8,0u8,38u8,2u8,38u8,100u8,3u8,138u8,1u8,144u8, - 4u8,4u8,154u8,5u8,90u8,5u8,244u8,5u8,202u8,4u8,7u8,190u8,10u8,159u8,19u8,8u8,221u8,29u8,32u8,6u8, - 253u8,29u8,133u8,2u8,16u8,130u8,32u8,254u8,12u8,10u8,128u8,45u8,249u8,1u8,12u8,249u8,46u8,158u8,22u8,13u8, - 151u8,69u8,58u8,15u8,209u8,69u8,2u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8, - 7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8, - 17u8,0u8,18u8,0u8,19u8,0u8,20u8,8u8,0u8,0u8,21u8,6u8,0u8,0u8,22u8,6u8,0u8,0u8,23u8,6u8, - 0u8,0u8,24u8,6u8,0u8,0u8,25u8,6u8,0u8,0u8,26u8,4u8,0u8,0u8,27u8,6u8,0u8,0u8,28u8,6u8, - 0u8,0u8,29u8,6u8,0u8,0u8,30u8,6u8,0u8,0u8,31u8,6u8,0u8,0u8,32u8,8u8,0u8,0u8,33u8,8u8, - 0u8,0u8,34u8,7u8,0u8,12u8,40u8,7u8,2u8,0u8,0u8,0u8,0u8,5u8,41u8,4u8,1u8,0u8,1u8,3u8, - 42u8,8u8,0u8,1u8,44u8,6u8,0u8,8u8,46u8,7u8,0u8,15u8,51u8,7u8,0u8,7u8,85u8,4u8,1u8,6u8, - 1u8,10u8,105u8,4u8,0u8,0u8,35u8,0u8,1u8,0u8,0u8,36u8,2u8,3u8,0u8,0u8,37u8,4u8,3u8,0u8, - 0u8,38u8,4u8,3u8,0u8,0u8,39u8,0u8,4u8,0u8,0u8,43u8,5u8,4u8,0u8,0u8,45u8,6u8,7u8,0u8, - 0u8,47u8,8u8,9u8,0u8,0u8,48u8,4u8,3u8,0u8,0u8,49u8,10u8,3u8,0u8,0u8,50u8,11u8,4u8,0u8, - 0u8,52u8,12u8,4u8,0u8,0u8,53u8,2u8,13u8,0u8,0u8,54u8,14u8,13u8,0u8,0u8,55u8,4u8,4u8,0u8, - 0u8,56u8,4u8,1u8,0u8,0u8,57u8,4u8,1u8,0u8,0u8,58u8,4u8,1u8,0u8,0u8,59u8,15u8,3u8,0u8, - 0u8,60u8,2u8,3u8,0u8,0u8,61u8,16u8,3u8,0u8,0u8,62u8,15u8,3u8,0u8,0u8,63u8,17u8,3u8,0u8, - 0u8,64u8,0u8,4u8,0u8,0u8,65u8,4u8,10u8,0u8,0u8,66u8,4u8,4u8,0u8,0u8,67u8,2u8,3u8,0u8, - 0u8,68u8,4u8,1u8,0u8,0u8,69u8,4u8,3u8,0u8,0u8,70u8,10u8,3u8,0u8,0u8,71u8,18u8,3u8,0u8, - 0u8,72u8,19u8,3u8,0u8,0u8,73u8,15u8,3u8,0u8,0u8,74u8,15u8,3u8,0u8,0u8,75u8,20u8,3u8,0u8, - 0u8,76u8,4u8,3u8,0u8,0u8,77u8,10u8,3u8,0u8,0u8,78u8,4u8,10u8,0u8,0u8,79u8,4u8,9u8,0u8, - 0u8,80u8,4u8,1u8,0u8,0u8,81u8,4u8,4u8,0u8,0u8,82u8,11u8,21u8,0u8,10u8,122u8,23u8,1u8,0u8, - 10u8,123u8,24u8,1u8,0u8,6u8,124u8,1u8,1u8,0u8,5u8,125u8,27u8,1u8,1u8,0u8,5u8,126u8,28u8,3u8, - 1u8,0u8,2u8,127u8,29u8,3u8,1u8,0u8,7u8,128u8,1u8,31u8,3u8,1u8,6u8,6u8,129u8,1u8,1u8,1u8, - 0u8,16u8,130u8,1u8,4u8,33u8,0u8,6u8,131u8,1u8,1u8,1u8,0u8,2u8,132u8,1u8,4u8,3u8,0u8,12u8, - 133u8,1u8,35u8,1u8,2u8,4u8,4u8,5u8,134u8,1u8,3u8,28u8,1u8,0u8,10u8,135u8,1u8,1u8,36u8,0u8, - 12u8,136u8,1u8,37u8,38u8,2u8,4u8,4u8,5u8,137u8,1u8,39u8,3u8,1u8,0u8,10u8,138u8,1u8,40u8,1u8, - 0u8,11u8,139u8,1u8,41u8,4u8,0u8,1u8,140u8,1u8,41u8,43u8,1u8,6u8,14u8,141u8,1u8,44u8,4u8,0u8, - 12u8,135u8,1u8,3u8,45u8,2u8,4u8,4u8,12u8,142u8,1u8,45u8,3u8,2u8,4u8,4u8,4u8,143u8,1u8,55u8, - 56u8,1u8,0u8,18u8,144u8,1u8,58u8,3u8,1u8,0u8,1u8,145u8,1u8,6u8,7u8,0u8,5u8,146u8,1u8,41u8, - 3u8,1u8,0u8,17u8,147u8,1u8,3u8,1u8,0u8,10u8,65u8,61u8,10u8,0u8,5u8,148u8,1u8,62u8,28u8,1u8, - 0u8,12u8,149u8,1u8,64u8,33u8,2u8,4u8,4u8,12u8,150u8,1u8,64u8,65u8,2u8,4u8,4u8,1u8,151u8,1u8, - 69u8,13u8,0u8,15u8,152u8,1u8,56u8,71u8,0u8,6u8,153u8,1u8,1u8,1u8,0u8,14u8,60u8,2u8,3u8,0u8, - 13u8,154u8,1u8,4u8,1u8,0u8,12u8,155u8,1u8,37u8,74u8,2u8,4u8,4u8,12u8,156u8,1u8,75u8,3u8,2u8, - 4u8,4u8,13u8,157u8,1u8,4u8,79u8,0u8,14u8,158u8,1u8,0u8,81u8,0u8,14u8,71u8,82u8,3u8,0u8,14u8, - 159u8,1u8,19u8,3u8,0u8,14u8,74u8,15u8,3u8,0u8,6u8,160u8,1u8,1u8,1u8,0u8,10u8,161u8,1u8,61u8, - 1u8,0u8,8u8,162u8,1u8,85u8,1u8,0u8,9u8,163u8,1u8,63u8,1u8,0u8,14u8,48u8,0u8,3u8,0u8,5u8, - 164u8,1u8,4u8,1u8,1u8,0u8,5u8,165u8,1u8,87u8,28u8,1u8,0u8,45u8,26u8,46u8,26u8,47u8,26u8,48u8, - 30u8,53u8,34u8,54u8,26u8,56u8,34u8,57u8,26u8,60u8,42u8,48u8,42u8,62u8,0u8,60u8,46u8,60u8,47u8,60u8, - 48u8,60u8,49u8,60u8,50u8,60u8,51u8,60u8,52u8,60u8,53u8,60u8,30u8,63u8,34u8,64u8,4u8,64u8,1u8,65u8, - 57u8,67u8,26u8,70u8,26u8,48u8,52u8,71u8,0u8,72u8,0u8,71u8,67u8,72u8,67u8,56u8,0u8,48u8,48u8,78u8, - 0u8,79u8,0u8,48u8,49u8,62u8,67u8,78u8,67u8,79u8,67u8,48u8,53u8,48u8,46u8,48u8,47u8,48u8,51u8,90u8, - 26u8,91u8,26u8,2u8,5u8,5u8,1u8,3u8,2u8,6u8,12u8,5u8,0u8,1u8,5u8,9u8,6u8,12u8,6u8,10u8, - 5u8,11u8,15u8,2u8,5u8,11u8,16u8,1u8,8u8,17u8,8u8,14u8,5u8,5u8,5u8,3u8,10u8,2u8,2u8,6u8, - 12u8,10u8,2u8,2u8,12u8,8u8,18u8,3u8,10u8,8u8,19u8,3u8,3u8,1u8,8u8,14u8,1u8,10u8,5u8,2u8, - 6u8,8u8,13u8,5u8,2u8,5u8,8u8,20u8,1u8,12u8,1u8,6u8,8u8,13u8,3u8,6u8,12u8,5u8,5u8,4u8, - 6u8,12u8,5u8,5u8,5u8,4u8,6u8,12u8,5u8,8u8,20u8,5u8,2u8,6u8,8u8,13u8,3u8,4u8,6u8,12u8, - 5u8,5u8,3u8,2u8,6u8,12u8,6u8,8u8,13u8,1u8,11u8,16u8,1u8,8u8,17u8,4u8,5u8,3u8,3u8,6u8, - 8u8,13u8,2u8,6u8,8u8,22u8,5u8,3u8,6u8,8u8,22u8,3u8,3u8,4u8,5u8,3u8,11u8,16u8,1u8,8u8, - 17u8,7u8,8u8,13u8,1u8,8u8,17u8,1u8,6u8,11u8,16u8,1u8,9u8,0u8,1u8,11u8,16u8,1u8,9u8,0u8, - 2u8,5u8,11u8,16u8,1u8,9u8,0u8,1u8,8u8,1u8,2u8,7u8,11u8,21u8,1u8,9u8,0u8,9u8,0u8,22u8, - 5u8,5u8,5u8,3u8,5u8,5u8,3u8,7u8,11u8,21u8,1u8,8u8,2u8,5u8,7u8,8u8,0u8,11u8,16u8,1u8, - 8u8,17u8,3u8,5u8,12u8,8u8,18u8,11u8,16u8,1u8,8u8,17u8,3u8,8u8,22u8,3u8,3u8,5u8,5u8,1u8, - 1u8,2u8,5u8,11u8,16u8,1u8,8u8,17u8,1u8,6u8,11u8,15u8,2u8,9u8,0u8,9u8,1u8,1u8,8u8,22u8, - 2u8,7u8,11u8,15u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,2u8,9u8,0u8,9u8,1u8,2u8,7u8,11u8, - 16u8,1u8,9u8,0u8,11u8,16u8,1u8,9u8,0u8,3u8,7u8,8u8,22u8,5u8,3u8,1u8,6u8,12u8,1u8,8u8, - 2u8,1u8,11u8,21u8,1u8,9u8,0u8,6u8,6u8,12u8,5u8,5u8,11u8,16u8,1u8,8u8,17u8,3u8,10u8,2u8, - 1u8,11u8,15u8,2u8,9u8,0u8,9u8,1u8,1u8,8u8,9u8,1u8,8u8,10u8,1u8,8u8,4u8,1u8,8u8,5u8, - 1u8,8u8,8u8,1u8,8u8,11u8,1u8,8u8,3u8,1u8,8u8,7u8,5u8,5u8,12u8,7u8,8u8,0u8,10u8,2u8, - 8u8,18u8,1u8,6u8,9u8,0u8,1u8,10u8,2u8,1u8,2u8,2u8,7u8,10u8,9u8,0u8,10u8,9u8,0u8,1u8, - 8u8,19u8,14u8,5u8,10u8,5u8,5u8,3u8,11u8,16u8,1u8,8u8,17u8,6u8,8u8,22u8,3u8,3u8,11u8,16u8, - 1u8,8u8,17u8,5u8,6u8,10u8,5u8,3u8,3u8,7u8,8u8,13u8,1u8,6u8,8u8,22u8,2u8,7u8,11u8,16u8, - 1u8,9u8,0u8,3u8,2u8,3u8,3u8,2u8,6u8,11u8,15u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8, - 6u8,9u8,1u8,1u8,6u8,11u8,15u8,2u8,8u8,20u8,5u8,2u8,8u8,20u8,5u8,1u8,7u8,8u8,13u8,1u8, - 6u8,8u8,18u8,5u8,1u8,6u8,5u8,5u8,7u8,11u8,15u8,2u8,5u8,5u8,7u8,8u8,13u8,1u8,8u8,20u8, - 2u8,12u8,7u8,8u8,13u8,6u8,5u8,6u8,5u8,7u8,11u8,15u8,2u8,5u8,5u8,7u8,5u8,5u8,7u8,8u8, - 13u8,1u8,7u8,9u8,1u8,3u8,7u8,11u8,15u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,4u8,12u8, - 6u8,8u8,20u8,7u8,11u8,15u8,2u8,8u8,20u8,5u8,7u8,8u8,13u8,6u8,10u8,5u8,3u8,3u8,5u8,6u8, - 10u8,5u8,6u8,8u8,13u8,4u8,3u8,3u8,3u8,7u8,8u8,13u8,4u8,3u8,3u8,3u8,3u8,2u8,3u8,6u8, - 8u8,13u8,3u8,3u8,3u8,3u8,3u8,6u8,12u8,5u8,3u8,4u8,12u8,6u8,12u8,5u8,7u8,8u8,13u8,9u8, - 8u8,19u8,3u8,3u8,6u8,10u8,8u8,19u8,3u8,3u8,7u8,8u8,13u8,8u8,19u8,7u8,8u8,14u8,2u8,3u8, - 8u8,19u8,2u8,12u8,3u8,2u8,6u8,12u8,3u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,7u8,118u8,101u8, - 115u8,116u8,105u8,110u8,103u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,13u8,97u8,112u8,116u8,111u8,115u8,95u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,3u8,98u8, - 99u8,115u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,13u8, - 102u8,105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8,51u8,50u8,6u8,109u8,97u8,116u8,104u8,54u8,52u8, - 8u8,112u8,111u8,111u8,108u8,95u8,117u8,54u8,52u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,10u8,115u8,105u8,109u8, - 112u8,108u8,101u8,95u8,109u8,97u8,112u8,5u8,115u8,116u8,97u8,107u8,101u8,16u8,115u8,116u8,97u8,107u8,105u8,110u8, - 103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,16u8,115u8,121u8, - 115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,9u8,116u8,105u8,109u8,101u8,115u8, - 116u8,97u8,109u8,112u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,10u8,65u8,100u8,109u8,105u8,110u8,83u8,116u8,111u8, - 114u8,101u8,18u8,65u8,100u8,109u8,105u8,110u8,87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,69u8,118u8,101u8,110u8, - 116u8,26u8,67u8,114u8,101u8,97u8,116u8,101u8,86u8,101u8,115u8,116u8,105u8,110u8,103u8,67u8,111u8,110u8,116u8,114u8, - 97u8,99u8,116u8,69u8,118u8,101u8,110u8,116u8,15u8,68u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,69u8, - 118u8,101u8,110u8,116u8,16u8,82u8,101u8,115u8,101u8,116u8,76u8,111u8,99u8,107u8,117u8,112u8,69u8,118u8,101u8,110u8, - 116u8,19u8,83u8,101u8,116u8,66u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,69u8,118u8,101u8,110u8, - 116u8,11u8,83u8,116u8,97u8,107u8,105u8,110u8,103u8,73u8,110u8,102u8,111u8,14u8,84u8,101u8,114u8,109u8,105u8,110u8, - 97u8,116u8,101u8,69u8,118u8,101u8,110u8,116u8,18u8,85u8,110u8,108u8,111u8,99u8,107u8,82u8,101u8,119u8,97u8,114u8, - 100u8,115u8,69u8,118u8,101u8,110u8,116u8,19u8,85u8,112u8,100u8,97u8,116u8,101u8,79u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,69u8,118u8,101u8,110u8,116u8,16u8,85u8,112u8,100u8,97u8,116u8,101u8,86u8,111u8,116u8,101u8,114u8,69u8, - 118u8,101u8,110u8,116u8,9u8,86u8,101u8,115u8,116u8,69u8,118u8,101u8,110u8,116u8,24u8,86u8,101u8,115u8,116u8,105u8, - 110u8,103u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,77u8,97u8,110u8,97u8,103u8,101u8,109u8,101u8,110u8,116u8,15u8, - 86u8,101u8,115u8,116u8,105u8,110u8,103u8,67u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,15u8,86u8,101u8,115u8,116u8, - 105u8,110u8,103u8,83u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,19u8,97u8,99u8,99u8,117u8,109u8,117u8,108u8,97u8, - 116u8,101u8,100u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,14u8,97u8,100u8,109u8,105u8,110u8,95u8,119u8,105u8, - 116u8,104u8,100u8,114u8,97u8,119u8,30u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,99u8,116u8,105u8,118u8,101u8, - 95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,30u8,97u8,115u8, - 115u8,101u8,114u8,116u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,11u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8, - 9u8,83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,4u8,67u8,111u8,105u8,110u8,9u8,65u8,112u8,116u8,111u8, - 115u8,67u8,111u8,105u8,110u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8,112u8,97u8, - 98u8,105u8,108u8,105u8,116u8,121u8,31u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8, - 103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,12u8,70u8, - 105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,51u8,50u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,118u8, - 101u8,115u8,116u8,105u8,110u8,103u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,10u8,100u8,105u8,115u8,116u8, - 114u8,105u8,98u8,117u8,116u8,101u8,15u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,109u8,97u8, - 110u8,121u8,15u8,103u8,101u8,116u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,6u8,83u8, - 116u8,114u8,105u8,110u8,103u8,15u8,103u8,101u8,116u8,95u8,114u8,111u8,108u8,101u8,95u8,104u8,111u8,108u8,100u8,101u8, - 114u8,26u8,103u8,101u8,116u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,35u8,103u8,101u8,116u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,105u8,110u8,116u8,101u8, - 114u8,110u8,97u8,108u8,8u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,30u8,111u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8, - 116u8,97u8,103u8,101u8,20u8,112u8,101u8,114u8,105u8,111u8,100u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8, - 95u8,115u8,101u8,99u8,115u8,15u8,114u8,101u8,109u8,97u8,105u8,110u8,105u8,110u8,103u8,95u8,103u8,114u8,97u8,110u8, - 116u8,17u8,114u8,101u8,115u8,101u8,116u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,12u8, - 114u8,101u8,115u8,101u8,116u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,15u8,115u8,101u8,116u8,95u8,98u8,101u8,110u8, - 101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,24u8,115u8,101u8,116u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8, - 105u8,97u8,114u8,121u8,95u8,114u8,101u8,115u8,101u8,116u8,116u8,101u8,114u8,19u8,115u8,101u8,116u8,95u8,109u8,97u8, - 110u8,97u8,103u8,101u8,109u8,101u8,110u8,116u8,95u8,114u8,111u8,108u8,101u8,11u8,115u8,104u8,97u8,114u8,101u8,104u8, - 111u8,108u8,100u8,101u8,114u8,12u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,18u8,115u8, - 116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,26u8,116u8,101u8, - 114u8,109u8,105u8,110u8,97u8,116u8,101u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8, - 114u8,97u8,99u8,116u8,25u8,116u8,111u8,116u8,97u8,108u8,95u8,97u8,99u8,99u8,117u8,109u8,117u8,108u8,97u8,116u8, - 101u8,100u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,14u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,114u8,101u8, - 119u8,97u8,114u8,100u8,115u8,19u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8, - 95u8,109u8,97u8,110u8,121u8,12u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,115u8,116u8,97u8,107u8,101u8,15u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,36u8,117u8,112u8,100u8,97u8,116u8, - 101u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,115u8,97u8,109u8,101u8, - 95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,12u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,118u8, - 111u8,116u8,101u8,114u8,12u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,97u8,100u8,109u8,105u8,110u8,4u8,118u8,101u8, - 115u8,116u8,9u8,118u8,101u8,115u8,116u8,95u8,109u8,97u8,110u8,121u8,17u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,16u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,115u8, - 99u8,104u8,101u8,100u8,117u8,108u8,101u8,18u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,115u8,116u8,97u8,114u8, - 116u8,95u8,115u8,101u8,99u8,115u8,5u8,118u8,111u8,116u8,101u8,114u8,14u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8, - 119u8,95u8,115u8,116u8,97u8,107u8,101u8,5u8,110u8,111u8,110u8,99u8,101u8,13u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,5u8, - 97u8,100u8,109u8,105u8,110u8,24u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8, - 99u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,12u8,103u8,114u8, - 97u8,110u8,116u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,18u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,97u8, - 108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,20u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,112u8,111u8, - 111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,21u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8, - 110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,26u8,110u8,101u8,119u8,95u8,108u8,111u8,99u8, - 107u8,117u8,112u8,95u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8,115u8,15u8, - 111u8,108u8,100u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,15u8,110u8,101u8,119u8,95u8, - 98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,12u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8, - 114u8,101u8,115u8,115u8,12u8,111u8,108u8,100u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,12u8,110u8,101u8, - 119u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,9u8,111u8,108u8,100u8,95u8,118u8,111u8,116u8,101u8,114u8, - 9u8,110u8,101u8,119u8,95u8,118u8,111u8,116u8,101u8,114u8,13u8,112u8,101u8,114u8,105u8,111u8,100u8,95u8,118u8,101u8, - 115u8,116u8,101u8,100u8,5u8,114u8,111u8,108u8,101u8,115u8,5u8,115u8,116u8,97u8,116u8,101u8,10u8,103u8,114u8,97u8, - 110u8,116u8,95u8,112u8,111u8,111u8,108u8,4u8,80u8,111u8,111u8,108u8,13u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8, - 105u8,97u8,114u8,105u8,101u8,115u8,7u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,10u8,115u8,105u8,103u8,110u8,101u8, - 114u8,95u8,99u8,97u8,112u8,22u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8, - 114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,118u8,111u8,116u8,101u8, - 114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,19u8,114u8,101u8,115u8,101u8,116u8,95u8,108u8,111u8,99u8,107u8,117u8, - 112u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,22u8,115u8,101u8,116u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8, - 105u8,97u8,114u8,121u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,21u8,117u8,110u8,108u8,111u8,99u8,107u8,95u8,114u8, - 101u8,119u8,97u8,114u8,100u8,115u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,118u8,101u8,115u8,116u8,95u8,101u8, - 118u8,101u8,110u8,116u8,115u8,17u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8,101u8,118u8,101u8, - 110u8,116u8,115u8,16u8,116u8,101u8,114u8,109u8,105u8,110u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8, - 21u8,97u8,100u8,109u8,105u8,110u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,8u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,20u8,115u8,116u8,97u8,114u8,116u8,95u8,116u8,105u8, - 109u8,101u8,115u8,116u8,97u8,109u8,112u8,95u8,115u8,101u8,99u8,115u8,15u8,112u8,101u8,114u8,105u8,111u8,100u8,95u8, - 100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,18u8,108u8,97u8,115u8,116u8,95u8,118u8,101u8,115u8,116u8,101u8,100u8, - 95u8,112u8,101u8,114u8,105u8,111u8,100u8,6u8,115u8,104u8,97u8,114u8,101u8,115u8,33u8,115u8,104u8,97u8,114u8,101u8, - 115u8,95u8,116u8,111u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,116u8,111u8,116u8, - 97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8, - 116u8,101u8,5u8,118u8,97u8,108u8,117u8,101u8,12u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,122u8,101u8,114u8, - 111u8,13u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,99u8,111u8,105u8,110u8,115u8,10u8,101u8,109u8,105u8,116u8, - 95u8,101u8,118u8,101u8,110u8,116u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,19u8,105u8,115u8,95u8, - 114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,16u8,105u8,110u8,118u8, - 97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,36u8,97u8,115u8,115u8,101u8,114u8,116u8, - 95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,105u8,115u8,95u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8, - 101u8,100u8,95u8,102u8,111u8,114u8,95u8,97u8,112u8,116u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,4u8,122u8,101u8, - 114u8,111u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,5u8,109u8,101u8,114u8, - 103u8,101u8,6u8,98u8,117u8,121u8,95u8,105u8,110u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8, - 16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,34u8,99u8,114u8, - 101u8,97u8,116u8,101u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,111u8,105u8,110u8,115u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8, - 95u8,101u8,109u8,112u8,116u8,121u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,6u8,97u8,112u8,112u8,101u8, - 110u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8, - 99u8,99u8,111u8,117u8,110u8,116u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,11u8,110u8,111u8,119u8,95u8, - 115u8,101u8,99u8,111u8,110u8,100u8,115u8,7u8,101u8,120u8,116u8,114u8,97u8,99u8,116u8,12u8,99u8,111u8,110u8,116u8, - 97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,29u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,4u8,117u8,116u8,102u8,56u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8, - 110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,15u8,103u8,101u8,116u8,95u8,108u8,111u8,99u8,107u8,117u8,112u8,95u8, - 115u8,101u8,99u8,115u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,3u8,97u8,100u8,100u8,9u8, - 103u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,24u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8, - 110u8,116u8,114u8,97u8,99u8,116u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,115u8,15u8,115u8,119u8,105u8,116u8,99u8, - 104u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,15u8,117u8,110u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8, - 105u8,99u8,97u8,116u8,101u8,100u8,11u8,116u8,111u8,116u8,97u8,108u8,95u8,99u8,111u8,105u8,110u8,115u8,12u8,109u8, - 117u8,108u8,116u8,105u8,112u8,108u8,121u8,95u8,117u8,54u8,52u8,3u8,109u8,105u8,110u8,7u8,98u8,97u8,108u8,97u8, - 110u8,99u8,101u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,15u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,3u8,8u8,30u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,26u8,25u8,82u8,79u8, - 76u8,69u8,95u8,66u8,69u8,78u8,69u8,70u8,73u8,67u8,73u8,65u8,82u8,89u8,95u8,82u8,69u8,83u8,69u8,84u8, - 84u8,69u8,82u8,10u8,2u8,25u8,24u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8, - 114u8,107u8,58u8,58u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,95u8,118u8,49u8,233u8,12u8,16u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8,69u8,73u8,78u8,86u8, - 65u8,76u8,73u8,68u8,95u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8,65u8,76u8,95u8,65u8,68u8,68u8,82u8, - 69u8,83u8,83u8,30u8,87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,97u8,108u8,32u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,32u8,105u8,115u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,23u8,69u8,69u8,77u8,80u8,84u8,89u8,95u8,86u8,69u8,83u8,84u8,73u8,78u8,71u8,95u8,83u8,67u8, - 72u8,69u8,68u8,85u8,76u8,69u8,33u8,86u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,115u8,99u8,104u8,101u8,100u8, - 117u8,108u8,101u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,101u8,109u8,112u8,116u8,121u8,46u8, - 3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,90u8,69u8,82u8,79u8,95u8,86u8,69u8,83u8,84u8,73u8, - 78u8,71u8,95u8,83u8,67u8,72u8,69u8,68u8,85u8,76u8,69u8,95u8,80u8,69u8,82u8,73u8,79u8,68u8,27u8,86u8, - 101u8,115u8,116u8,105u8,110u8,103u8,32u8,112u8,101u8,114u8,105u8,111u8,100u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8, - 32u8,98u8,101u8,32u8,48u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,78u8,79u8,95u8,83u8, - 72u8,65u8,82u8,69u8,72u8,79u8,76u8,68u8,69u8,82u8,83u8,34u8,83u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8, - 100u8,101u8,114u8,115u8,32u8,108u8,105u8,115u8,116u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8, - 101u8,109u8,112u8,116u8,121u8,46u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,83u8,72u8,65u8,82u8, - 69u8,83u8,95u8,76u8,69u8,78u8,71u8,84u8,72u8,95u8,77u8,73u8,83u8,77u8,65u8,84u8,67u8,72u8,56u8,84u8, - 104u8,101u8,32u8,108u8,101u8,110u8,103u8,116u8,104u8,32u8,111u8,102u8,32u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8, - 108u8,100u8,101u8,114u8,115u8,32u8,97u8,110u8,100u8,32u8,115u8,104u8,97u8,114u8,101u8,115u8,32u8,108u8,105u8,115u8, - 116u8,115u8,32u8,100u8,111u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,46u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,23u8,69u8,86u8,69u8,83u8,84u8,73u8,78u8,71u8,95u8,83u8,84u8,65u8,82u8,84u8,95u8,84u8, - 79u8,79u8,95u8,83u8,79u8,79u8,78u8,87u8,86u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,99u8,97u8,110u8,110u8, - 111u8,116u8,32u8,115u8,116u8,97u8,114u8,116u8,32u8,98u8,101u8,102u8,111u8,114u8,101u8,32u8,111u8,114u8,32u8,97u8, - 116u8,32u8,116u8,104u8,101u8,32u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,98u8,108u8,111u8,99u8,107u8,32u8, - 116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,46u8,32u8,72u8,97u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8, - 32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,102u8,117u8,116u8,117u8,114u8,101u8,46u8,7u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,65u8,68u8,77u8,73u8,78u8,52u8,84u8,104u8,101u8,32u8,115u8, - 105u8,103u8,110u8,101u8,114u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,116u8,104u8,101u8,32u8,97u8,100u8,109u8, - 105u8,110u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,99u8,111u8, - 110u8,116u8,114u8,97u8,99u8,116u8,46u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,86u8,69u8,83u8, - 84u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8,78u8,79u8,84u8,95u8,65u8,67u8, - 84u8,73u8,86u8,69u8,45u8,86u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,32u8,110u8,101u8,101u8,100u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,105u8,110u8,32u8,97u8,99u8,116u8, - 105u8,118u8,101u8,32u8,115u8,116u8,97u8,116u8,101u8,46u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8, - 86u8,69u8,83u8,84u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8,83u8,84u8,73u8, - 76u8,76u8,95u8,65u8,67u8,84u8,73u8,86u8,69u8,81u8,65u8,100u8,109u8,105u8,110u8,32u8,99u8,97u8,110u8,32u8, - 111u8,110u8,108u8,121u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,32u8,102u8,114u8,111u8,109u8,32u8,97u8, - 110u8,32u8,105u8,110u8,97u8,99u8,116u8,105u8,118u8,101u8,32u8,40u8,112u8,97u8,117u8,115u8,101u8,100u8,32u8,111u8, - 114u8,32u8,116u8,101u8,114u8,109u8,105u8,110u8,97u8,116u8,101u8,100u8,41u8,32u8,118u8,101u8,115u8,116u8,105u8,110u8, - 103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,46u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,27u8, - 69u8,86u8,69u8,83u8,84u8,73u8,78u8,71u8,95u8,67u8,79u8,78u8,84u8,82u8,65u8,67u8,84u8,95u8,78u8,79u8, - 84u8,95u8,70u8,79u8,85u8,78u8,68u8,46u8,78u8,111u8,32u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,99u8, - 111u8,110u8,116u8,114u8,97u8,99u8,116u8,32u8,102u8,111u8,117u8,110u8,100u8,32u8,97u8,116u8,32u8,112u8,114u8,111u8, - 118u8,105u8,100u8,101u8,100u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,46u8,11u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,20u8,69u8,80u8,69u8,78u8,68u8,73u8,78u8,71u8,95u8,83u8,84u8,65u8,75u8,69u8,95u8,70u8,79u8, - 85u8,78u8,68u8,95u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,116u8,101u8,114u8,109u8,105u8,110u8,97u8,116u8,101u8, - 32u8,116u8,104u8,101u8,32u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,32u8,119u8,105u8,116u8,104u8,32u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,32u8,97u8,99u8,116u8,105u8,118u8, - 101u8,32u8,115u8,116u8,97u8,107u8,101u8,46u8,32u8,78u8,101u8,101u8,100u8,32u8,116u8,111u8,32u8,119u8,97u8,105u8, - 116u8,32u8,117u8,110u8,116u8,105u8,108u8,32u8,110u8,101u8,120u8,116u8,32u8,101u8,112u8,111u8,99u8,104u8,46u8,12u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,69u8,90u8,69u8,82u8,79u8,95u8,71u8,82u8,65u8,78u8,84u8,25u8, - 71u8,114u8,97u8,110u8,116u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8, - 98u8,101u8,32u8,48u8,46u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,86u8,69u8,83u8,84u8,73u8, - 78u8,71u8,95u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,72u8,65u8,83u8,95u8,78u8,79u8,95u8,82u8,79u8, - 76u8,69u8,83u8,59u8,86u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8, - 104u8,97u8,115u8,32u8,110u8,111u8,32u8,111u8,116u8,104u8,101u8,114u8,32u8,109u8,97u8,110u8,97u8,103u8,101u8,109u8, - 101u8,110u8,116u8,32u8,114u8,111u8,108u8,101u8,115u8,32u8,98u8,101u8,115u8,105u8,100u8,101u8,32u8,97u8,100u8,109u8, - 105u8,110u8,46u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,82u8,79u8,76u8,69u8,95u8,78u8,79u8, - 84u8,95u8,70u8,79u8,85u8,78u8,68u8,48u8,84u8,104u8,101u8,32u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,32u8,115u8,117u8,99u8,104u8,32u8, - 109u8,97u8,110u8,97u8,103u8,101u8,109u8,101u8,110u8,116u8,32u8,114u8,111u8,108u8,101u8,46u8,15u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,18u8,69u8,80u8,69u8,82u8,77u8,73u8,83u8,83u8,73u8,79u8,78u8,95u8,68u8,69u8,78u8, - 73u8,69u8,68u8,76u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8, - 100u8,109u8,105u8,110u8,32u8,111u8,114u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8,97u8,118u8, - 101u8,32u8,116u8,104u8,101u8,32u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,32u8,114u8,111u8,108u8,101u8,32u8, - 116u8,111u8,32u8,116u8,97u8,107u8,101u8,32u8,116u8,104u8,105u8,115u8,32u8,97u8,99u8,116u8,105u8,111u8,110u8,46u8, - 16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,86u8,69u8,67u8,95u8,69u8,77u8,80u8,84u8,89u8,95u8, - 70u8,79u8,82u8,95u8,77u8,65u8,78u8,89u8,95u8,70u8,85u8,78u8,67u8,84u8,73u8,79u8,78u8,46u8,90u8,101u8, - 114u8,111u8,32u8,105u8,116u8,101u8,109u8,115u8,32u8,119u8,101u8,114u8,101u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8, - 101u8,100u8,32u8,116u8,111u8,32u8,97u8,32u8,42u8,95u8,109u8,97u8,110u8,121u8,32u8,102u8,117u8,110u8,99u8,116u8, - 105u8,111u8,110u8,46u8,0u8,14u8,5u8,118u8,111u8,116u8,101u8,114u8,1u8,1u8,0u8,8u8,111u8,112u8,101u8,114u8, - 97u8,116u8,111u8,114u8,1u8,1u8,0u8,11u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,1u8, - 1u8,0u8,11u8,115u8,104u8,97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,1u8,1u8,0u8,12u8,115u8,104u8, - 97u8,114u8,101u8,104u8,111u8,108u8,100u8,101u8,114u8,115u8,1u8,1u8,0u8,15u8,114u8,101u8,109u8,97u8,105u8,110u8, - 105u8,110u8,103u8,95u8,103u8,114u8,97u8,110u8,116u8,1u8,1u8,0u8,16u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,1u8,1u8,0u8,17u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,1u8,1u8,0u8,18u8,115u8,116u8,97u8,107u8,101u8,95u8, - 112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,1u8,1u8,0u8,18u8,118u8,101u8,115u8,116u8, - 105u8,110u8,103u8,95u8,115u8,116u8,97u8,114u8,116u8,95u8,115u8,101u8,99u8,115u8,1u8,1u8,0u8,19u8,97u8,99u8, - 99u8,117u8,109u8,117u8,108u8,97u8,116u8,101u8,100u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,1u8,1u8,0u8, - 20u8,112u8,101u8,114u8,105u8,111u8,100u8,95u8,100u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,95u8,115u8,101u8,99u8, - 115u8,1u8,1u8,0u8,25u8,116u8,111u8,116u8,97u8,108u8,95u8,97u8,99u8,99u8,117u8,109u8,117u8,108u8,97u8,116u8, - 101u8,100u8,95u8,114u8,101u8,119u8,97u8,114u8,100u8,115u8,1u8,1u8,0u8,30u8,111u8,112u8,101u8,114u8,97u8,116u8, - 111u8,114u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8, - 116u8,97u8,103u8,101u8,1u8,1u8,0u8,0u8,2u8,3u8,78u8,10u8,5u8,83u8,3u8,84u8,11u8,21u8,1u8,8u8, - 2u8,1u8,2u8,3u8,86u8,5u8,87u8,5u8,88u8,3u8,2u8,2u8,7u8,55u8,5u8,81u8,5u8,89u8,3u8,90u8, - 5u8,87u8,5u8,91u8,5u8,92u8,3u8,3u8,2u8,3u8,86u8,5u8,87u8,5u8,88u8,3u8,4u8,2u8,4u8,86u8, - 5u8,87u8,5u8,91u8,5u8,93u8,3u8,5u8,2u8,5u8,86u8,5u8,87u8,5u8,64u8,5u8,94u8,5u8,95u8,5u8, - 6u8,2u8,4u8,96u8,5u8,55u8,5u8,81u8,5u8,92u8,3u8,7u8,2u8,2u8,86u8,5u8,87u8,5u8,8u8,2u8, - 4u8,86u8,5u8,87u8,5u8,91u8,5u8,88u8,3u8,9u8,2u8,6u8,86u8,5u8,87u8,5u8,91u8,5u8,97u8,5u8, - 98u8,5u8,92u8,3u8,10u8,2u8,5u8,86u8,5u8,87u8,5u8,91u8,5u8,99u8,5u8,100u8,5u8,11u8,2u8,5u8, - 86u8,5u8,87u8,5u8,91u8,5u8,101u8,3u8,88u8,3u8,12u8,2u8,1u8,102u8,11u8,15u8,2u8,8u8,20u8,5u8, - 13u8,2u8,18u8,103u8,3u8,86u8,5u8,104u8,8u8,22u8,106u8,11u8,15u8,2u8,5u8,5u8,79u8,8u8,14u8,90u8, - 5u8,107u8,8u8,6u8,58u8,3u8,108u8,8u8,18u8,109u8,11u8,21u8,1u8,8u8,9u8,110u8,11u8,21u8,1u8,8u8, - 10u8,111u8,11u8,21u8,1u8,8u8,4u8,112u8,11u8,21u8,1u8,8u8,5u8,113u8,11u8,21u8,1u8,8u8,8u8,114u8, - 11u8,21u8,1u8,8u8,11u8,115u8,11u8,21u8,1u8,8u8,3u8,116u8,11u8,21u8,1u8,8u8,7u8,117u8,11u8,21u8, - 1u8,8u8,1u8,14u8,2u8,4u8,118u8,10u8,8u8,19u8,119u8,3u8,120u8,3u8,121u8,3u8,0u8,1u8,0u8,1u8, - 13u8,22u8,23u8,10u8,0u8,17u8,2u8,10u8,0u8,17u8,27u8,12u8,4u8,10u8,0u8,11u8,1u8,17u8,23u8,12u8, - 2u8,11u8,0u8,43u8,13u8,12u8,5u8,10u8,5u8,16u8,0u8,11u8,2u8,17u8,42u8,12u8,3u8,11u8,5u8,16u8, - 0u8,11u8,3u8,11u8,4u8,17u8,43u8,2u8,1u8,1u8,4u8,1u8,13u8,25u8,54u8,10u8,1u8,43u8,13u8,16u8, - 1u8,20u8,7u8,0u8,33u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,12u8,17u8,44u8,39u8,10u8,1u8,42u8, - 13u8,12u8,5u8,11u8,0u8,10u8,5u8,46u8,17u8,34u8,10u8,5u8,10u8,1u8,12u8,2u8,46u8,11u8,2u8,17u8, - 41u8,12u8,4u8,14u8,4u8,56u8,0u8,12u8,3u8,10u8,3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 33u8,4u8,39u8,11u8,5u8,1u8,11u8,4u8,56u8,1u8,2u8,10u8,5u8,16u8,2u8,20u8,11u8,4u8,56u8,2u8, - 10u8,5u8,15u8,3u8,11u8,5u8,16u8,4u8,20u8,11u8,1u8,11u8,3u8,18u8,1u8,56u8,3u8,2u8,2u8,0u8, - 0u8,1u8,13u8,3u8,14u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,1u8,20u8,7u8,1u8,33u8,4u8, - 10u8,5u8,13u8,7u8,10u8,17u8,44u8,39u8,2u8,3u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,41u8,13u8,4u8, - 4u8,5u8,7u8,7u8,11u8,17u8,49u8,39u8,2u8,4u8,1u8,0u8,1u8,13u8,3u8,7u8,10u8,0u8,17u8,3u8, - 11u8,0u8,43u8,13u8,11u8,1u8,17u8,10u8,2u8,5u8,1u8,0u8,1u8,0u8,32u8,205u8,1u8,10u8,4u8,17u8, - 50u8,32u8,4u8,5u8,5u8,12u8,11u8,1u8,1u8,11u8,0u8,1u8,7u8,1u8,17u8,51u8,39u8,10u8,4u8,17u8, - 52u8,10u8,1u8,65u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,20u8,5u8,27u8,11u8, - 1u8,1u8,11u8,0u8,1u8,7u8,3u8,17u8,51u8,39u8,14u8,2u8,56u8,4u8,10u8,1u8,65u8,4u8,33u8,4u8, - 34u8,5u8,41u8,11u8,1u8,1u8,11u8,0u8,1u8,7u8,7u8,17u8,51u8,39u8,56u8,5u8,12u8,24u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,25u8,7u8,16u8,17u8,55u8,12u8,26u8,10u8,1u8,65u8,4u8,12u8, - 28u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,27u8,10u8,27u8,10u8,28u8,35u8,4u8,91u8,5u8, - 58u8,10u8,1u8,10u8,27u8,66u8,4u8,20u8,12u8,30u8,13u8,2u8,14u8,30u8,56u8,6u8,12u8,19u8,1u8,14u8, - 19u8,56u8,0u8,12u8,20u8,13u8,24u8,11u8,19u8,56u8,7u8,13u8,26u8,10u8,1u8,10u8,27u8,66u8,4u8,20u8, - 10u8,20u8,17u8,58u8,1u8,11u8,25u8,11u8,20u8,22u8,12u8,25u8,11u8,27u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,12u8,27u8,5u8,53u8,11u8,1u8,1u8,10u8,25u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,98u8,5u8,103u8,11u8,0u8,1u8,7u8,14u8,17u8,51u8,39u8,10u8,0u8,17u8,59u8,12u8, - 17u8,10u8,17u8,41u8,0u8,32u8,4u8,117u8,10u8,0u8,64u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,56u8,8u8,18u8,0u8,45u8,0u8,11u8,0u8,10u8, - 8u8,17u8,6u8,12u8,23u8,12u8,22u8,14u8,22u8,10u8,5u8,10u8,6u8,11u8,24u8,10u8,7u8,11u8,8u8,17u8, - 61u8,12u8,29u8,14u8,22u8,17u8,59u8,12u8,21u8,10u8,17u8,42u8,0u8,12u8,18u8,10u8,18u8,15u8,5u8,10u8, - 21u8,68u8,4u8,11u8,18u8,15u8,6u8,12u8,16u8,10u8,5u8,12u8,9u8,10u8,6u8,12u8,10u8,10u8,4u8,12u8, - 11u8,10u8,25u8,12u8,12u8,10u8,21u8,12u8,13u8,10u8,29u8,12u8,14u8,10u8,7u8,12u8,15u8,11u8,16u8,11u8, - 9u8,11u8,10u8,11u8,12u8,11u8,11u8,11u8,13u8,11u8,14u8,11u8,15u8,18u8,2u8,56u8,9u8,14u8,22u8,7u8, - 1u8,11u8,17u8,11u8,26u8,56u8,10u8,11u8,3u8,11u8,4u8,11u8,29u8,11u8,5u8,11u8,6u8,11u8,7u8,18u8, - 6u8,11u8,25u8,11u8,23u8,14u8,22u8,56u8,11u8,14u8,22u8,56u8,12u8,14u8,22u8,56u8,13u8,14u8,22u8,56u8, - 14u8,14u8,22u8,56u8,15u8,14u8,22u8,56u8,16u8,14u8,22u8,56u8,17u8,14u8,22u8,56u8,18u8,14u8,22u8,56u8, - 19u8,18u8,13u8,45u8,13u8,11u8,2u8,56u8,20u8,11u8,21u8,2u8,6u8,0u8,0u8,1u8,0u8,54u8,39u8,10u8, - 0u8,17u8,59u8,42u8,0u8,12u8,4u8,10u8,0u8,17u8,59u8,12u8,2u8,14u8,2u8,56u8,21u8,12u8,5u8,13u8, - 5u8,10u8,4u8,16u8,7u8,56u8,22u8,56u8,23u8,10u8,4u8,16u8,7u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,11u8,4u8,15u8,7u8,21u8,13u8,5u8,7u8,18u8,56u8,23u8,13u8,5u8,11u8,1u8,56u8, - 23u8,11u8,0u8,11u8,5u8,17u8,66u8,12u8,6u8,12u8,3u8,14u8,3u8,56u8,24u8,11u8,3u8,11u8,6u8,2u8, - 7u8,1u8,0u8,0u8,3u8,31u8,14u8,0u8,65u8,59u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8, - 4u8,6u8,5u8,9u8,7u8,0u8,17u8,51u8,39u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 36u8,4u8,14u8,5u8,17u8,7u8,15u8,17u8,51u8,39u8,10u8,1u8,17u8,68u8,38u8,4u8,22u8,5u8,25u8,7u8, - 13u8,17u8,51u8,39u8,11u8,0u8,11u8,1u8,11u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8, - 14u8,2u8,8u8,1u8,4u8,1u8,13u8,60u8,100u8,10u8,0u8,17u8,2u8,10u8,0u8,42u8,13u8,12u8,14u8,10u8, - 14u8,10u8,0u8,12u8,1u8,46u8,11u8,1u8,17u8,41u8,12u8,5u8,14u8,5u8,56u8,0u8,12u8,13u8,10u8,13u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,24u8,11u8,14u8,1u8,11u8,5u8,56u8,1u8,2u8, - 10u8,14u8,16u8,0u8,12u8,6u8,10u8,6u8,17u8,69u8,12u8,2u8,14u8,2u8,12u8,11u8,10u8,11u8,65u8,4u8, - 12u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,7u8,10u8,7u8,10u8,8u8,35u8,4u8,73u8, - 5u8,42u8,10u8,11u8,10u8,7u8,66u8,4u8,20u8,12u8,10u8,10u8,6u8,10u8,10u8,17u8,42u8,12u8,12u8,10u8, - 6u8,11u8,12u8,10u8,13u8,17u8,43u8,12u8,4u8,13u8,5u8,11u8,4u8,56u8,25u8,12u8,9u8,10u8,14u8,11u8, - 10u8,12u8,3u8,46u8,11u8,3u8,17u8,10u8,11u8,9u8,56u8,2u8,11u8,7u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,12u8,7u8,5u8,37u8,11u8,11u8,1u8,11u8,6u8,1u8,14u8,5u8,56u8,0u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,88u8,10u8,14u8,16u8,2u8,20u8,11u8,5u8,56u8,2u8,5u8, - 90u8,11u8,5u8,56u8,1u8,10u8,14u8,15u8,8u8,11u8,14u8,16u8,4u8,20u8,11u8,0u8,11u8,13u8,18u8,3u8, - 56u8,26u8,2u8,9u8,1u8,4u8,1u8,13u8,63u8,29u8,14u8,0u8,65u8,4u8,12u8,2u8,10u8,2u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,8u8,5u8,11u8,7u8,8u8,17u8,51u8,39u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8,1u8,10u8,2u8,35u8,4u8,28u8,5u8,18u8,14u8,0u8,10u8, - 1u8,66u8,4u8,20u8,17u8,8u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8, - 5u8,13u8,2u8,10u8,0u8,0u8,0u8,4u8,18u8,10u8,0u8,16u8,9u8,14u8,1u8,56u8,27u8,4u8,12u8,11u8, - 0u8,16u8,9u8,14u8,1u8,56u8,28u8,20u8,12u8,2u8,5u8,16u8,11u8,0u8,1u8,11u8,1u8,12u8,2u8,11u8, - 2u8,2u8,11u8,1u8,0u8,1u8,12u8,66u8,26u8,10u8,0u8,41u8,12u8,4u8,4u8,5u8,7u8,7u8,9u8,17u8, - 49u8,39u8,11u8,0u8,43u8,12u8,16u8,10u8,12u8,2u8,10u8,2u8,14u8,1u8,56u8,29u8,4u8,16u8,5u8,21u8, - 11u8,2u8,1u8,7u8,6u8,17u8,49u8,39u8,11u8,2u8,14u8,1u8,56u8,30u8,20u8,2u8,12u8,1u8,0u8,1u8, - 13u8,68u8,11u8,11u8,1u8,42u8,13u8,12u8,2u8,11u8,0u8,10u8,2u8,46u8,17u8,34u8,11u8,2u8,46u8,17u8, - 13u8,2u8,13u8,0u8,0u8,0u8,3u8,4u8,11u8,0u8,16u8,11u8,17u8,73u8,2u8,14u8,1u8,0u8,1u8,13u8, - 3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,12u8,16u8,13u8,20u8,2u8,15u8,1u8,0u8,1u8, - 13u8,3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,12u8,16u8,14u8,20u8,2u8,16u8,1u8,0u8, - 1u8,13u8,3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,15u8,16u8,16u8,20u8,2u8,17u8,1u8, - 0u8,1u8,13u8,3u8,7u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,17u8,20u8,2u8,18u8,1u8,4u8, - 2u8,12u8,13u8,70u8,49u8,10u8,1u8,42u8,13u8,12u8,7u8,11u8,0u8,17u8,59u8,12u8,5u8,10u8,5u8,10u8, - 7u8,16u8,4u8,20u8,33u8,4u8,15u8,8u8,12u8,3u8,5u8,22u8,11u8,5u8,11u8,1u8,7u8,17u8,17u8,74u8, - 17u8,11u8,33u8,12u8,3u8,11u8,3u8,4u8,25u8,5u8,30u8,11u8,7u8,1u8,7u8,5u8,17u8,75u8,39u8,11u8, - 7u8,15u8,9u8,12u8,6u8,10u8,6u8,14u8,2u8,12u8,4u8,46u8,11u8,4u8,56u8,27u8,4u8,46u8,11u8,6u8, - 14u8,2u8,56u8,31u8,1u8,1u8,5u8,48u8,11u8,6u8,1u8,2u8,19u8,1u8,4u8,1u8,13u8,72u8,35u8,10u8, - 1u8,42u8,13u8,12u8,3u8,11u8,0u8,10u8,3u8,46u8,17u8,34u8,10u8,3u8,46u8,17u8,13u8,12u8,2u8,14u8, - 2u8,10u8,3u8,16u8,12u8,16u8,13u8,20u8,17u8,76u8,10u8,3u8,15u8,18u8,10u8,3u8,16u8,4u8,20u8,11u8, - 1u8,10u8,3u8,16u8,12u8,16u8,19u8,20u8,11u8,3u8,16u8,12u8,16u8,19u8,20u8,17u8,77u8,18u8,4u8,56u8, - 32u8,2u8,20u8,1u8,4u8,1u8,13u8,73u8,50u8,10u8,3u8,17u8,52u8,10u8,1u8,42u8,13u8,12u8,9u8,11u8, - 0u8,10u8,9u8,46u8,17u8,34u8,10u8,9u8,10u8,2u8,12u8,4u8,46u8,11u8,4u8,17u8,10u8,12u8,8u8,10u8, - 9u8,15u8,9u8,12u8,6u8,10u8,6u8,14u8,2u8,12u8,5u8,46u8,11u8,5u8,56u8,27u8,4u8,34u8,11u8,6u8, - 14u8,2u8,56u8,33u8,12u8,7u8,10u8,3u8,11u8,7u8,21u8,5u8,38u8,11u8,6u8,10u8,2u8,10u8,3u8,56u8, - 34u8,10u8,9u8,15u8,20u8,11u8,9u8,16u8,4u8,20u8,11u8,1u8,11u8,2u8,11u8,8u8,11u8,3u8,18u8,5u8, - 56u8,35u8,2u8,21u8,1u8,4u8,2u8,12u8,13u8,3u8,7u8,11u8,0u8,11u8,1u8,7u8,17u8,17u8,74u8,11u8, - 2u8,17u8,22u8,2u8,22u8,1u8,4u8,2u8,12u8,13u8,76u8,44u8,10u8,1u8,42u8,13u8,12u8,7u8,11u8,0u8, - 10u8,7u8,46u8,17u8,34u8,10u8,1u8,41u8,12u8,32u8,4u8,20u8,11u8,7u8,46u8,17u8,13u8,12u8,4u8,14u8, - 4u8,56u8,36u8,18u8,12u8,45u8,12u8,5u8,22u8,11u8,7u8,1u8,11u8,1u8,42u8,12u8,15u8,10u8,12u8,6u8, - 10u8,6u8,14u8,2u8,12u8,5u8,46u8,11u8,5u8,56u8,29u8,4u8,39u8,11u8,3u8,11u8,6u8,14u8,2u8,56u8, - 37u8,21u8,5u8,43u8,11u8,6u8,11u8,2u8,11u8,3u8,56u8,38u8,2u8,23u8,1u8,0u8,1u8,13u8,77u8,48u8, - 10u8,0u8,17u8,2u8,10u8,0u8,17u8,24u8,12u8,2u8,14u8,2u8,12u8,6u8,11u8,0u8,43u8,13u8,12u8,7u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,6u8,65u8,4u8,12u8,4u8,10u8,3u8,10u8, - 4u8,35u8,4u8,42u8,5u8,20u8,10u8,6u8,10u8,3u8,66u8,4u8,20u8,12u8,5u8,10u8,1u8,10u8,7u8,10u8, - 5u8,17u8,10u8,33u8,4u8,37u8,11u8,7u8,1u8,11u8,6u8,1u8,11u8,5u8,2u8,11u8,3u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,15u8,11u8,7u8,1u8,11u8,6u8,1u8,7u8,19u8,2u8, - 24u8,1u8,0u8,1u8,13u8,3u8,7u8,10u8,0u8,17u8,2u8,11u8,0u8,43u8,13u8,16u8,0u8,17u8,69u8,2u8, - 25u8,1u8,0u8,1u8,13u8,3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8,12u8,16u8,19u8,20u8, - 2u8,26u8,1u8,4u8,1u8,13u8,78u8,53u8,10u8,1u8,17u8,2u8,10u8,1u8,17u8,8u8,10u8,1u8,42u8,13u8, - 12u8,5u8,11u8,0u8,10u8,5u8,46u8,17u8,34u8,10u8,5u8,16u8,12u8,16u8,19u8,20u8,17u8,80u8,1u8,12u8, - 4u8,1u8,12u8,3u8,11u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,25u8,5u8,30u8, - 11u8,5u8,1u8,7u8,4u8,17u8,44u8,39u8,7u8,0u8,10u8,5u8,15u8,1u8,21u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,10u8,5u8,15u8,17u8,21u8,10u8,5u8,11u8,3u8,12u8,2u8,46u8,11u8,2u8,17u8,30u8, - 10u8,5u8,15u8,21u8,11u8,5u8,16u8,4u8,20u8,11u8,1u8,18u8,7u8,56u8,39u8,2u8,27u8,1u8,0u8,1u8, - 13u8,80u8,20u8,10u8,0u8,17u8,2u8,10u8,0u8,43u8,13u8,12u8,2u8,11u8,0u8,10u8,2u8,16u8,12u8,16u8, - 13u8,20u8,17u8,81u8,12u8,1u8,1u8,11u8,2u8,16u8,17u8,20u8,23u8,11u8,1u8,23u8,2u8,28u8,1u8,4u8, - 1u8,13u8,1u8,8u8,10u8,0u8,17u8,27u8,12u8,1u8,11u8,0u8,43u8,13u8,11u8,1u8,17u8,30u8,2u8,29u8, - 1u8,4u8,1u8,13u8,63u8,29u8,14u8,0u8,65u8,4u8,12u8,2u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,34u8,4u8,8u8,5u8,11u8,7u8,8u8,17u8,51u8,39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,12u8,1u8,10u8,1u8,10u8,2u8,35u8,4u8,28u8,5u8,18u8,14u8,0u8,10u8,1u8,66u8,4u8,20u8, - 17u8,28u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,13u8,2u8,30u8, - 0u8,0u8,0u8,13u8,11u8,10u8,0u8,17u8,13u8,12u8,2u8,14u8,2u8,11u8,0u8,16u8,12u8,16u8,13u8,20u8, - 11u8,1u8,17u8,82u8,2u8,31u8,1u8,4u8,1u8,13u8,83u8,49u8,10u8,1u8,42u8,13u8,12u8,7u8,11u8,0u8, - 10u8,7u8,46u8,17u8,34u8,10u8,7u8,46u8,17u8,13u8,12u8,4u8,14u8,4u8,12u8,5u8,10u8,7u8,16u8,12u8, - 16u8,13u8,20u8,12u8,6u8,11u8,5u8,10u8,6u8,10u8,2u8,10u8,3u8,17u8,83u8,10u8,2u8,10u8,7u8,15u8, - 12u8,15u8,13u8,21u8,10u8,3u8,10u8,7u8,15u8,12u8,15u8,14u8,21u8,10u8,7u8,15u8,22u8,10u8,7u8,16u8, - 4u8,20u8,11u8,1u8,11u8,7u8,16u8,12u8,16u8,19u8,20u8,11u8,6u8,11u8,2u8,11u8,3u8,18u8,9u8,56u8, - 40u8,2u8,32u8,1u8,4u8,1u8,13u8,1u8,9u8,10u8,1u8,17u8,15u8,12u8,3u8,11u8,0u8,11u8,1u8,11u8, - 2u8,11u8,3u8,17u8,31u8,2u8,33u8,1u8,4u8,1u8,13u8,83u8,45u8,10u8,1u8,42u8,13u8,12u8,6u8,11u8, - 0u8,10u8,6u8,46u8,17u8,34u8,10u8,6u8,46u8,17u8,13u8,12u8,3u8,14u8,3u8,12u8,4u8,10u8,6u8,16u8, - 12u8,16u8,23u8,20u8,12u8,5u8,11u8,4u8,10u8,6u8,16u8,12u8,16u8,13u8,20u8,10u8,2u8,17u8,84u8,10u8, - 2u8,10u8,6u8,15u8,12u8,15u8,23u8,21u8,10u8,6u8,15u8,24u8,10u8,6u8,16u8,4u8,20u8,11u8,1u8,11u8, - 6u8,16u8,12u8,16u8,19u8,20u8,11u8,5u8,11u8,2u8,18u8,10u8,56u8,41u8,2u8,34u8,0u8,0u8,0u8,3u8, - 12u8,11u8,0u8,17u8,59u8,11u8,1u8,16u8,4u8,20u8,33u8,4u8,8u8,5u8,11u8,7u8,2u8,17u8,85u8,39u8, - 2u8,35u8,1u8,4u8,1u8,13u8,84u8,112u8,10u8,0u8,17u8,28u8,10u8,0u8,42u8,13u8,12u8,7u8,10u8,7u8, - 16u8,15u8,16u8,25u8,20u8,17u8,68u8,36u8,4u8,15u8,11u8,7u8,1u8,2u8,10u8,7u8,15u8,15u8,12u8,9u8, - 10u8,9u8,16u8,26u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,17u8,68u8,10u8, - 9u8,16u8,25u8,20u8,23u8,10u8,9u8,16u8,16u8,20u8,26u8,10u8,3u8,35u8,4u8,41u8,11u8,9u8,1u8,11u8, - 7u8,1u8,2u8,10u8,9u8,16u8,27u8,12u8,4u8,10u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 23u8,12u8,5u8,10u8,5u8,10u8,4u8,65u8,59u8,35u8,4u8,59u8,11u8,4u8,11u8,5u8,66u8,59u8,20u8,12u8, - 1u8,5u8,67u8,10u8,4u8,11u8,4u8,65u8,59u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,66u8, - 59u8,20u8,12u8,1u8,11u8,1u8,12u8,8u8,10u8,7u8,16u8,0u8,17u8,86u8,11u8,8u8,17u8,87u8,10u8,7u8, - 16u8,17u8,20u8,17u8,88u8,12u8,6u8,10u8,7u8,16u8,17u8,20u8,10u8,6u8,23u8,10u8,7u8,15u8,17u8,21u8, - 10u8,3u8,11u8,9u8,15u8,26u8,21u8,10u8,7u8,10u8,6u8,12u8,2u8,46u8,11u8,2u8,17u8,30u8,10u8,7u8, - 15u8,28u8,10u8,7u8,16u8,4u8,20u8,11u8,0u8,11u8,7u8,16u8,12u8,16u8,19u8,20u8,11u8,3u8,11u8,6u8, - 18u8,11u8,56u8,42u8,2u8,36u8,1u8,4u8,1u8,13u8,63u8,29u8,14u8,0u8,65u8,4u8,12u8,2u8,10u8,2u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,8u8,5u8,11u8,7u8,8u8,17u8,51u8,39u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,10u8,1u8,10u8,2u8,35u8,4u8,28u8,5u8,18u8,14u8, - 0u8,10u8,1u8,66u8,4u8,20u8,17u8,35u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,1u8,5u8,13u8,2u8,37u8,1u8,0u8,1u8,0u8,10u8,14u8,10u8,0u8,41u8,0u8,32u8,4u8,7u8,64u8, - 4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,5u8,12u8,11u8,0u8,43u8,0u8,16u8,5u8,20u8, - 12u8,1u8,11u8,1u8,2u8,38u8,1u8,0u8,1u8,13u8,3u8,7u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8, - 16u8,15u8,20u8,2u8,39u8,1u8,0u8,1u8,13u8,3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8,16u8, - 15u8,16u8,25u8,20u8,2u8,40u8,1u8,0u8,1u8,13u8,3u8,8u8,10u8,0u8,17u8,3u8,11u8,0u8,43u8,13u8, - 16u8,12u8,16u8,23u8,20u8,2u8,41u8,0u8,0u8,0u8,86u8,16u8,10u8,1u8,10u8,0u8,16u8,12u8,16u8,13u8, - 20u8,17u8,89u8,11u8,1u8,56u8,43u8,12u8,3u8,11u8,0u8,17u8,13u8,12u8,2u8,14u8,2u8,11u8,3u8,56u8, - 44u8,2u8,13u8,2u8,13u8,0u8,13u8,5u8,13u8,17u8,13u8,1u8,0u8,0u8,0u8,2u8,0u8,1u8,13u8,15u8, - 13u8,3u8,12u8,0u8,13u8,8u8,13u8,6u8,6u8,1u8,6u8,3u8,13u8,4u8,14u8,2u8,13u8,7u8,13u8,11u8, - 6u8,0u8,13u8,12u8,13u8,16u8,13u8,9u8,6u8,2u8,13u8,10u8,14u8,1u8,14u8,3u8,14u8,0u8,13u8,14u8, - 0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk31); - let chunk32 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,10u8,2u8,10u8,8u8,3u8,18u8,45u8,5u8, - 63u8,16u8,7u8,79u8,254u8,1u8,8u8,205u8,2u8,32u8,6u8,237u8,2u8,54u8,16u8,163u8,3u8,209u8,1u8,10u8, - 244u8,4u8,10u8,12u8,254u8,4u8,101u8,13u8,227u8,5u8,2u8,15u8,229u8,5u8,2u8,0u8,1u8,0u8,2u8,0u8, - 3u8,0u8,4u8,0u8,5u8,0u8,6u8,8u8,0u8,0u8,7u8,8u8,0u8,0u8,8u8,0u8,1u8,0u8,0u8,9u8, - 2u8,1u8,0u8,0u8,10u8,0u8,1u8,0u8,4u8,13u8,2u8,1u8,0u8,4u8,14u8,2u8,1u8,0u8,3u8,15u8, - 2u8,4u8,0u8,1u8,16u8,5u8,5u8,0u8,1u8,17u8,5u8,5u8,0u8,2u8,18u8,1u8,1u8,0u8,2u8,6u8, - 12u8,3u8,0u8,1u8,6u8,12u8,1u8,7u8,8u8,1u8,1u8,5u8,1u8,3u8,7u8,103u8,101u8,110u8,101u8,115u8, - 105u8,115u8,7u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,15u8,114u8,101u8,99u8, - 111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,16u8, - 115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,20u8,83u8,101u8,116u8, - 86u8,101u8,114u8,115u8,105u8,111u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,7u8,86u8,101u8, - 114u8,115u8,105u8,111u8,110u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,19u8,105u8,110u8,105u8, - 116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,102u8,111u8,114u8,95u8,116u8,101u8,115u8,116u8,11u8,115u8,101u8,116u8, - 95u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,11u8,100u8,117u8,109u8,109u8,121u8,95u8,102u8,105u8,101u8,108u8,100u8, - 5u8,109u8,97u8,106u8,111u8,114u8,22u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8, - 102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,20u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,99u8,111u8,114u8, - 101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8, - 102u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8,110u8,105u8,101u8,100u8,16u8, - 105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,11u8,114u8,101u8,99u8, - 111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8, - 3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,18u8,97u8,112u8,116u8,111u8,115u8, - 58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,188u8,1u8,2u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,29u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,77u8,65u8,74u8,79u8,82u8,95u8, - 86u8,69u8,82u8,83u8,73u8,79u8,78u8,95u8,78u8,85u8,77u8,66u8,69u8,82u8,75u8,83u8,112u8,101u8,99u8,105u8, - 102u8,105u8,101u8,100u8,32u8,109u8,97u8,106u8,111u8,114u8,32u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,32u8,110u8, - 117u8,109u8,98u8,101u8,114u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,103u8,114u8,101u8,97u8,116u8,101u8, - 114u8,32u8,116u8,104u8,97u8,110u8,32u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8,118u8,101u8,114u8,115u8,105u8, - 111u8,110u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,46u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8, - 78u8,79u8,84u8,95u8,65u8,85u8,84u8,72u8,79u8,82u8,73u8,90u8,69u8,68u8,46u8,65u8,99u8,99u8,111u8,117u8, - 110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,117u8,116u8,104u8,111u8,114u8,105u8,122u8,101u8,100u8, - 32u8,116u8,111u8,32u8,109u8,97u8,107u8,101u8,32u8,116u8,104u8,105u8,115u8,32u8,99u8,104u8,97u8,110u8,103u8,101u8, - 46u8,0u8,0u8,0u8,2u8,1u8,11u8,1u8,1u8,2u8,1u8,12u8,3u8,0u8,3u8,0u8,0u8,1u8,11u8,10u8, - 0u8,17u8,3u8,10u8,0u8,11u8,1u8,18u8,1u8,45u8,1u8,11u8,0u8,9u8,18u8,0u8,45u8,0u8,2u8,1u8, - 0u8,0u8,0u8,1u8,7u8,10u8,0u8,17u8,4u8,11u8,0u8,9u8,18u8,0u8,45u8,0u8,2u8,2u8,1u8,4u8, - 1u8,1u8,3u8,28u8,11u8,0u8,17u8,5u8,41u8,0u8,4u8,5u8,5u8,8u8,7u8,1u8,17u8,6u8,39u8,7u8, - 2u8,43u8,1u8,16u8,0u8,20u8,10u8,1u8,35u8,4u8,16u8,5u8,19u8,7u8,0u8,17u8,7u8,39u8,7u8,2u8, - 42u8,1u8,12u8,2u8,11u8,1u8,11u8,2u8,15u8,0u8,21u8,17u8,8u8,2u8,1u8,0u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk32); - let chunk33 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,22u8,2u8,22u8,8u8,3u8,30u8,107u8,4u8, - 137u8,1u8,4u8,5u8,141u8,1u8,80u8,7u8,221u8,1u8,226u8,4u8,8u8,191u8,6u8,32u8,6u8,223u8,6u8,178u8, - 1u8,16u8,145u8,8u8,86u8,10u8,231u8,8u8,20u8,12u8,251u8,8u8,196u8,3u8,15u8,191u8,12u8,2u8,0u8,1u8, - 0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8, - 0u8,12u8,8u8,0u8,2u8,28u8,8u8,0u8,0u8,13u8,0u8,1u8,0u8,0u8,14u8,2u8,1u8,0u8,0u8,15u8, - 3u8,1u8,0u8,0u8,16u8,4u8,1u8,0u8,0u8,17u8,3u8,1u8,0u8,0u8,18u8,5u8,1u8,0u8,5u8,25u8, - 7u8,7u8,0u8,5u8,26u8,7u8,7u8,0u8,7u8,27u8,8u8,9u8,0u8,4u8,29u8,9u8,7u8,1u8,0u8,6u8, - 30u8,1u8,11u8,0u8,10u8,31u8,12u8,1u8,0u8,10u8,32u8,12u8,1u8,0u8,1u8,33u8,9u8,1u8,0u8,8u8, - 34u8,8u8,1u8,0u8,1u8,35u8,9u8,11u8,0u8,1u8,36u8,9u8,14u8,0u8,9u8,37u8,1u8,7u8,0u8,3u8, - 38u8,1u8,15u8,0u8,1u8,39u8,9u8,7u8,0u8,4u8,40u8,9u8,11u8,1u8,0u8,9u8,10u8,20u8,10u8,5u8, - 12u8,3u8,3u8,3u8,3u8,0u8,5u8,6u8,12u8,10u8,2u8,10u8,2u8,10u8,2u8,10u8,2u8,7u8,12u8,3u8, - 10u8,2u8,3u8,3u8,3u8,2u8,9u8,12u8,3u8,10u8,2u8,10u8,5u8,10u8,10u8,2u8,3u8,3u8,3u8,2u8, - 8u8,12u8,3u8,10u8,2u8,3u8,3u8,3u8,2u8,10u8,2u8,3u8,5u8,3u8,3u8,1u8,3u8,1u8,6u8,12u8, - 1u8,5u8,1u8,8u8,1u8,1u8,1u8,2u8,5u8,3u8,3u8,3u8,3u8,5u8,1u8,10u8,2u8,1u8,2u8,7u8, - 103u8,101u8,110u8,101u8,115u8,105u8,115u8,22u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8, - 118u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8, - 112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,8u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8,4u8,99u8, - 111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,101u8,97u8,116u8,117u8,114u8,101u8,115u8,6u8,115u8, - 105u8,103u8,110u8,101u8,114u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8, - 101u8,115u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8, - 116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,21u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8, - 86u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8,8u8,101u8,112u8,105u8,108u8,111u8,103u8,117u8,101u8,10u8, - 105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,15u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,112u8,114u8, - 111u8,108u8,111u8,103u8,117u8,101u8,27u8,109u8,117u8,108u8,116u8,105u8,95u8,97u8,103u8,101u8,110u8,116u8,95u8,115u8, - 99u8,114u8,105u8,112u8,116u8,95u8,112u8,114u8,111u8,108u8,111u8,103u8,117u8,101u8,15u8,112u8,114u8,111u8,108u8,111u8, - 103u8,117u8,101u8,95u8,99u8,111u8,109u8,109u8,111u8,110u8,15u8,115u8,99u8,114u8,105u8,112u8,116u8,95u8,112u8,114u8, - 111u8,108u8,111u8,103u8,117u8,101u8,11u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,97u8,100u8,100u8,114u8,11u8,109u8, - 111u8,100u8,117u8,108u8,101u8,95u8,110u8,97u8,109u8,101u8,20u8,115u8,99u8,114u8,105u8,112u8,116u8,95u8,112u8,114u8, - 111u8,108u8,111u8,103u8,117u8,101u8,95u8,110u8,97u8,109u8,101u8,20u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,112u8, - 114u8,111u8,108u8,111u8,103u8,117u8,101u8,95u8,110u8,97u8,109u8,101u8,25u8,109u8,117u8,108u8,116u8,105u8,95u8,97u8, - 103u8,101u8,110u8,116u8,95u8,112u8,114u8,111u8,108u8,111u8,103u8,117u8,101u8,95u8,110u8,97u8,109u8,101u8,18u8,117u8, - 115u8,101u8,114u8,95u8,101u8,112u8,105u8,108u8,111u8,103u8,117u8,101u8,95u8,110u8,97u8,109u8,101u8,16u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,12u8,111u8,117u8,116u8,95u8,111u8, - 102u8,95u8,114u8,97u8,110u8,103u8,101u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,9u8,65u8, - 112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,7u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,31u8,99u8,111u8,108u8, - 108u8,101u8,99u8,116u8,95u8,97u8,110u8,100u8,95u8,100u8,105u8,115u8,116u8,114u8,105u8,98u8,117u8,116u8,101u8,95u8, - 103u8,97u8,115u8,95u8,102u8,101u8,101u8,115u8,11u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,95u8,102u8,101u8,101u8, - 8u8,98u8,117u8,114u8,110u8,95u8,102u8,101u8,101u8,25u8,105u8,110u8,99u8,114u8,101u8,109u8,101u8,110u8,116u8,95u8, - 115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,22u8,97u8,115u8,115u8,101u8, - 114u8,116u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,9u8,101u8, - 120u8,105u8,115u8,116u8,115u8,95u8,97u8,116u8,22u8,103u8,101u8,116u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8, - 105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8, - 110u8,100u8,115u8,3u8,103u8,101u8,116u8,19u8,103u8,101u8,116u8,95u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8, - 95u8,110u8,117u8,109u8,98u8,101u8,114u8,21u8,105u8,115u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,114u8, - 101u8,103u8,105u8,115u8,116u8,101u8,114u8,101u8,100u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,4u8,16u8,255u8,255u8,255u8,255u8,255u8,255u8,255u8, - 255u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,236u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,239u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,237u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,233u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,241u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,240u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,235u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8, - 8u8,234u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,238u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8, - 32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8,23u8,22u8,116u8,114u8,97u8, - 110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,66u8,1u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,11u8,69u8,79u8,85u8,84u8,95u8,79u8,70u8,95u8,71u8,65u8,83u8, - 42u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,101u8, - 100u8,32u8,105u8,116u8,115u8,32u8,97u8,108u8,108u8,111u8,99u8,97u8,116u8,101u8,100u8,32u8,109u8,97u8,120u8,32u8, - 103u8,97u8,115u8,0u8,0u8,0u8,2u8,6u8,19u8,5u8,20u8,10u8,2u8,21u8,10u8,2u8,22u8,10u8,2u8,23u8, - 10u8,2u8,24u8,10u8,2u8,0u8,0u8,0u8,0u8,6u8,52u8,10u8,3u8,10u8,4u8,38u8,4u8,5u8,5u8,8u8, - 7u8,0u8,17u8,6u8,39u8,11u8,3u8,11u8,4u8,23u8,12u8,6u8,10u8,2u8,53u8,10u8,6u8,53u8,24u8,7u8, - 1u8,37u8,4u8,21u8,5u8,24u8,7u8,0u8,17u8,7u8,39u8,11u8,2u8,11u8,6u8,24u8,12u8,7u8,14u8,0u8, - 17u8,8u8,12u8,5u8,10u8,5u8,56u8,0u8,10u8,7u8,38u8,4u8,37u8,5u8,40u8,7u8,4u8,17u8,7u8,39u8, - 17u8,10u8,4u8,46u8,10u8,5u8,11u8,7u8,17u8,11u8,5u8,49u8,10u8,5u8,11u8,7u8,17u8,12u8,11u8,5u8, - 17u8,13u8,2u8,1u8,3u8,0u8,0u8,1u8,12u8,10u8,0u8,17u8,14u8,11u8,0u8,7u8,11u8,7u8,12u8,11u8, - 1u8,11u8,2u8,11u8,3u8,11u8,4u8,18u8,0u8,45u8,0u8,2u8,2u8,0u8,0u8,0u8,1u8,9u8,11u8,0u8, - 11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,11u8,6u8,17u8,4u8,2u8,3u8,0u8,0u8,0u8,13u8, - 57u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,5u8,11u8,6u8,11u8,7u8,11u8,8u8,17u8,4u8,14u8,3u8,65u8, - 9u8,12u8,10u8,14u8,4u8,65u8,14u8,10u8,10u8,33u8,4u8,17u8,5u8,20u8,7u8,6u8,17u8,6u8,39u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,9u8,10u8,9u8,10u8,10u8,35u8,4u8,56u8,5u8,27u8,14u8, - 3u8,10u8,9u8,66u8,9u8,20u8,12u8,11u8,10u8,11u8,17u8,15u8,4u8,36u8,5u8,39u8,7u8,2u8,17u8,6u8, - 39u8,14u8,4u8,10u8,9u8,66u8,14u8,20u8,11u8,11u8,17u8,16u8,33u8,4u8,48u8,5u8,51u8,7u8,5u8,17u8, - 6u8,39u8,11u8,9u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,9u8,5u8,22u8,2u8,4u8, - 0u8,0u8,0u8,13u8,84u8,17u8,17u8,11u8,5u8,35u8,4u8,5u8,5u8,8u8,7u8,10u8,17u8,6u8,39u8,17u8, - 18u8,11u8,6u8,33u8,4u8,13u8,5u8,16u8,7u8,3u8,17u8,6u8,39u8,14u8,0u8,17u8,8u8,12u8,9u8,10u8, - 9u8,17u8,15u8,4u8,23u8,5u8,26u8,7u8,2u8,17u8,6u8,39u8,11u8,2u8,10u8,9u8,17u8,16u8,33u8,4u8, - 32u8,5u8,35u8,7u8,5u8,17u8,6u8,39u8,10u8,1u8,53u8,7u8,1u8,35u8,4u8,41u8,5u8,44u8,7u8,7u8, - 17u8,7u8,39u8,10u8,9u8,17u8,19u8,12u8,7u8,10u8,1u8,10u8,7u8,38u8,4u8,52u8,5u8,55u8,7u8,9u8, - 17u8,6u8,39u8,11u8,1u8,11u8,7u8,33u8,4u8,60u8,5u8,63u8,7u8,8u8,17u8,6u8,39u8,11u8,3u8,11u8, - 4u8,24u8,12u8,8u8,10u8,9u8,56u8,1u8,4u8,71u8,5u8,74u8,7u8,4u8,17u8,6u8,39u8,11u8,9u8,56u8, - 0u8,11u8,8u8,38u8,4u8,80u8,5u8,83u8,7u8,4u8,17u8,6u8,39u8,2u8,5u8,0u8,0u8,0u8,1u8,9u8, - 11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,11u8,6u8,17u8,4u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk33); - let chunk34 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,54u8,2u8,54u8,58u8,3u8,112u8,181u8,2u8, - 4u8,165u8,3u8,14u8,5u8,179u8,3u8,134u8,3u8,7u8,185u8,6u8,175u8,13u8,8u8,232u8,19u8,32u8,6u8,136u8, - 20u8,244u8,3u8,16u8,252u8,23u8,84u8,10u8,208u8,24u8,56u8,12u8,136u8,25u8,245u8,9u8,13u8,253u8,34u8,36u8, - 0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8, - 0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8,17u8,0u8,18u8,0u8,19u8, - 0u8,20u8,0u8,21u8,0u8,22u8,0u8,23u8,0u8,24u8,0u8,25u8,0u8,26u8,0u8,27u8,2u8,0u8,0u8,28u8, - 3u8,0u8,0u8,29u8,3u8,0u8,0u8,30u8,3u8,0u8,3u8,61u8,8u8,0u8,15u8,66u8,7u8,2u8,0u8,0u8, - 0u8,0u8,8u8,67u8,4u8,1u8,0u8,1u8,12u8,68u8,7u8,0u8,26u8,69u8,7u8,0u8,1u8,83u8,6u8,0u8, - 8u8,90u8,5u8,1u8,0u8,1u8,8u8,91u8,5u8,1u8,0u8,1u8,0u8,31u8,0u8,1u8,0u8,0u8,32u8,2u8, - 3u8,0u8,0u8,33u8,4u8,3u8,0u8,0u8,34u8,5u8,3u8,0u8,0u8,35u8,6u8,3u8,0u8,0u8,36u8,7u8, - 3u8,0u8,0u8,37u8,8u8,3u8,0u8,0u8,38u8,9u8,3u8,0u8,0u8,39u8,10u8,3u8,0u8,0u8,40u8,11u8, - 3u8,0u8,0u8,41u8,9u8,3u8,0u8,1u8,60u8,13u8,14u8,0u8,10u8,10u8,13u8,1u8,0u8,1u8,31u8,13u8, - 1u8,0u8,8u8,62u8,9u8,3u8,1u8,0u8,3u8,63u8,0u8,3u8,0u8,24u8,64u8,18u8,14u8,1u8,0u8,11u8, - 65u8,19u8,19u8,0u8,15u8,70u8,3u8,23u8,2u8,4u8,4u8,8u8,43u8,13u8,19u8,1u8,0u8,8u8,71u8,24u8, - 25u8,1u8,0u8,15u8,72u8,26u8,3u8,2u8,4u8,4u8,12u8,73u8,28u8,27u8,0u8,26u8,74u8,29u8,30u8,0u8, - 26u8,75u8,31u8,13u8,0u8,26u8,76u8,13u8,13u8,0u8,26u8,77u8,32u8,3u8,0u8,11u8,78u8,19u8,19u8,0u8, - 18u8,79u8,34u8,3u8,0u8,18u8,76u8,35u8,13u8,0u8,16u8,80u8,36u8,3u8,0u8,3u8,81u8,9u8,3u8,0u8, - 16u8,82u8,3u8,3u8,0u8,1u8,84u8,13u8,42u8,0u8,1u8,37u8,9u8,3u8,0u8,23u8,37u8,43u8,3u8,0u8, - 4u8,85u8,44u8,3u8,0u8,24u8,86u8,45u8,14u8,1u8,0u8,9u8,37u8,10u8,3u8,0u8,25u8,37u8,24u8,3u8, - 0u8,16u8,37u8,9u8,3u8,0u8,17u8,37u8,46u8,3u8,0u8,20u8,37u8,9u8,3u8,0u8,13u8,37u8,10u8,3u8, - 0u8,2u8,87u8,9u8,3u8,0u8,8u8,88u8,9u8,3u8,0u8,6u8,37u8,47u8,3u8,0u8,14u8,37u8,9u8,3u8, - 0u8,5u8,37u8,24u8,3u8,0u8,19u8,37u8,9u8,3u8,0u8,21u8,89u8,9u8,3u8,0u8,3u8,37u8,9u8,48u8, - 0u8,16u8,92u8,49u8,3u8,0u8,22u8,93u8,50u8,3u8,0u8,1u8,94u8,10u8,3u8,0u8,3u8,95u8,52u8,3u8, - 0u8,16u8,96u8,54u8,3u8,0u8,16u8,97u8,54u8,3u8,0u8,16u8,98u8,55u8,3u8,0u8,7u8,41u8,9u8,3u8, - 0u8,14u8,15u8,16u8,13u8,18u8,22u8,19u8,15u8,20u8,15u8,21u8,22u8,37u8,13u8,3u8,6u8,12u8,5u8,3u8, - 1u8,12u8,2u8,6u8,12u8,10u8,8u8,0u8,0u8,3u8,3u8,3u8,10u8,8u8,1u8,3u8,6u8,12u8,6u8,8u8, - 3u8,1u8,2u8,6u8,12u8,10u8,8u8,2u8,3u8,6u8,12u8,1u8,10u8,8u8,3u8,12u8,10u8,2u8,2u8,3u8, - 10u8,2u8,3u8,3u8,3u8,3u8,1u8,3u8,3u8,3u8,1u8,6u8,12u8,2u8,6u8,12u8,10u8,2u8,2u8,5u8, - 6u8,8u8,2u8,2u8,12u8,12u8,1u8,5u8,1u8,1u8,1u8,8u8,4u8,4u8,6u8,8u8,0u8,3u8,3u8,10u8, - 5u8,1u8,8u8,0u8,2u8,6u8,10u8,9u8,0u8,6u8,9u8,0u8,1u8,3u8,22u8,12u8,6u8,5u8,5u8,6u8, - 12u8,11u8,5u8,2u8,5u8,11u8,6u8,1u8,8u8,4u8,11u8,6u8,1u8,8u8,4u8,5u8,12u8,6u8,8u8,1u8, - 8u8,7u8,3u8,3u8,3u8,3u8,3u8,3u8,5u8,10u8,8u8,7u8,3u8,10u8,5u8,6u8,8u8,2u8,8u8,8u8, - 1u8,8u8,1u8,2u8,5u8,11u8,6u8,1u8,8u8,4u8,1u8,11u8,5u8,2u8,9u8,0u8,9u8,1u8,2u8,6u8, - 12u8,3u8,1u8,11u8,6u8,1u8,9u8,0u8,3u8,7u8,11u8,5u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8, - 1u8,1u8,8u8,7u8,2u8,3u8,3u8,3u8,10u8,8u8,7u8,3u8,3u8,1u8,8u8,8u8,9u8,6u8,12u8,6u8, - 10u8,5u8,11u8,5u8,2u8,5u8,11u8,6u8,1u8,8u8,4u8,8u8,8u8,5u8,5u8,5u8,3u8,10u8,2u8,3u8, - 6u8,12u8,5u8,5u8,5u8,12u8,5u8,6u8,12u8,5u8,6u8,8u8,2u8,6u8,6u8,12u8,5u8,5u8,3u8,3u8, - 10u8,2u8,2u8,5u8,5u8,4u8,6u8,12u8,3u8,5u8,5u8,4u8,3u8,3u8,8u8,3u8,10u8,8u8,3u8,1u8, - 8u8,2u8,1u8,8u8,3u8,3u8,3u8,3u8,6u8,8u8,3u8,5u8,5u8,12u8,8u8,9u8,10u8,5u8,8u8,9u8, - 2u8,12u8,8u8,9u8,5u8,6u8,12u8,10u8,2u8,10u8,2u8,10u8,2u8,10u8,2u8,3u8,6u8,12u8,5u8,8u8, - 9u8,1u8,6u8,10u8,9u8,0u8,8u8,6u8,12u8,3u8,3u8,3u8,1u8,3u8,3u8,3u8,2u8,6u8,12u8,2u8, - 2u8,11u8,10u8,1u8,8u8,4u8,11u8,11u8,1u8,8u8,4u8,2u8,6u8,12u8,11u8,11u8,1u8,8u8,4u8,2u8, - 6u8,12u8,11u8,10u8,1u8,8u8,4u8,3u8,11u8,10u8,1u8,8u8,4u8,12u8,11u8,11u8,1u8,8u8,4u8,3u8, - 6u8,12u8,6u8,12u8,11u8,11u8,1u8,8u8,4u8,2u8,12u8,6u8,12u8,4u8,6u8,12u8,5u8,10u8,2u8,10u8, - 2u8,2u8,6u8,12u8,5u8,7u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,18u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,121u8, - 10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,16u8,97u8,112u8,116u8,111u8,115u8,95u8,103u8,111u8, - 118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,5u8,98u8,108u8,111u8,99u8,107u8,8u8,99u8,104u8,97u8,105u8,110u8, - 95u8,105u8,100u8,12u8,99u8,104u8,97u8,105u8,110u8,95u8,115u8,116u8,97u8,116u8,117u8,115u8,4u8,99u8,111u8,105u8, - 110u8,16u8,99u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,13u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,13u8,102u8, - 105u8,120u8,101u8,100u8,95u8,112u8,111u8,105u8,110u8,116u8,51u8,50u8,12u8,103u8,97u8,115u8,95u8,115u8,99u8,104u8, - 101u8,100u8,117u8,108u8,101u8,15u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8, - 110u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,5u8,115u8,116u8,97u8,107u8,101u8,14u8,115u8, - 116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,16u8,115u8,116u8,97u8,107u8,105u8,110u8, - 103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,13u8,115u8,116u8,97u8,116u8,101u8,95u8,115u8,116u8,111u8, - 114u8,97u8,103u8,101u8,11u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,95u8,103u8,97u8,115u8,9u8,116u8,105u8,109u8, - 101u8,115u8,116u8,97u8,109u8,112u8,15u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8, - 101u8,101u8,22u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,105u8,111u8,110u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,7u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8, - 7u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,10u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,77u8,97u8,112u8,18u8, - 69u8,109u8,112u8,108u8,111u8,121u8,101u8,101u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,77u8,97u8,112u8,22u8,86u8, - 97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,67u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8,116u8,105u8,111u8, - 110u8,36u8,86u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,67u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,97u8, - 116u8,105u8,111u8,110u8,87u8,105u8,116u8,104u8,67u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,14u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,15u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,115u8,26u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,101u8,109u8,112u8, - 108u8,111u8,121u8,101u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,27u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8, - 97u8,116u8,111u8,114u8,28u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,115u8,44u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8, - 114u8,115u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,10u8,105u8, - 110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,21u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8, - 95u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,40u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8, - 122u8,101u8,95u8,99u8,111u8,114u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,115u8,95u8,97u8,110u8, - 100u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,20u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8, - 105u8,122u8,101u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,15u8,115u8,101u8,116u8,95u8,103u8,101u8, - 110u8,101u8,115u8,105u8,115u8,95u8,101u8,110u8,100u8,15u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,7u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,8u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,115u8,9u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8,26u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,95u8,110u8,117u8,109u8,101u8,114u8,97u8,116u8,111u8,114u8,28u8, - 118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,95u8,100u8,101u8,110u8, - 111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,20u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8, - 95u8,114u8,101u8,115u8,101u8,116u8,116u8,101u8,114u8,13u8,111u8,119u8,110u8,101u8,114u8,95u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,16u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8, - 13u8,118u8,111u8,116u8,101u8,114u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,12u8,115u8,116u8,97u8,107u8,101u8, - 95u8,97u8,109u8,111u8,117u8,110u8,116u8,16u8,99u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,112u8,117u8, - 98u8,107u8,101u8,121u8,19u8,112u8,114u8,111u8,111u8,102u8,95u8,111u8,102u8,95u8,112u8,111u8,115u8,115u8,101u8,115u8, - 115u8,105u8,111u8,110u8,17u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8, - 101u8,115u8,27u8,102u8,117u8,108u8,108u8,95u8,110u8,111u8,100u8,101u8,95u8,110u8,101u8,116u8,119u8,111u8,114u8,107u8, - 95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,16u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,111u8,114u8, - 95u8,99u8,111u8,110u8,102u8,105u8,103u8,21u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8, - 101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,19u8,106u8,111u8,105u8,110u8,95u8,100u8,117u8,114u8,105u8,110u8, - 103u8,95u8,103u8,101u8,110u8,101u8,115u8,105u8,115u8,9u8,101u8,120u8,105u8,115u8,116u8,115u8,95u8,97u8,116u8,9u8, - 65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,4u8,109u8, - 105u8,110u8,116u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8, - 95u8,101u8,120u8,105u8,115u8,116u8,115u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,4u8,67u8,111u8, - 105u8,110u8,12u8,70u8,105u8,120u8,101u8,100u8,80u8,111u8,105u8,110u8,116u8,51u8,50u8,15u8,86u8,101u8,115u8,116u8, - 105u8,110u8,103u8,83u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,8u8,119u8, - 105u8,116u8,104u8,100u8,114u8,97u8,119u8,3u8,97u8,100u8,100u8,20u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8, - 114u8,111u8,109u8,95u8,114u8,97u8,116u8,105u8,111u8,110u8,97u8,108u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8, - 118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,115u8,99u8,104u8,101u8,100u8,117u8,108u8,101u8,23u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8, - 18u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,24u8, - 115u8,101u8,116u8,95u8,98u8,101u8,110u8,101u8,102u8,105u8,99u8,105u8,97u8,114u8,121u8,95u8,114u8,101u8,115u8,101u8, - 116u8,116u8,101u8,114u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8, - 101u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,22u8,105u8, - 110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,111u8,119u8,110u8,101u8, - 114u8,16u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,12u8,111u8, - 110u8,95u8,110u8,101u8,119u8,95u8,101u8,112u8,111u8,99u8,104u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,33u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,102u8,114u8,97u8,109u8, - 101u8,119u8,111u8,114u8,107u8,95u8,114u8,101u8,115u8,101u8,114u8,118u8,101u8,100u8,95u8,97u8,99u8,99u8,111u8,117u8, - 110u8,116u8,16u8,115u8,116u8,111u8,114u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8,112u8,8u8, - 105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,29u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8, - 97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,95u8,102u8,97u8,99u8,116u8,111u8,114u8,121u8,24u8,105u8, - 110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,95u8,99u8,111u8,110u8, - 102u8,105u8,103u8,20u8,115u8,101u8,116u8,95u8,116u8,105u8,109u8,101u8,95u8,104u8,97u8,115u8,95u8,115u8,116u8,97u8, - 114u8,116u8,101u8,100u8,14u8,66u8,117u8,114u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,14u8, - 77u8,105u8,110u8,116u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,25u8,115u8,116u8,111u8,114u8,101u8, - 95u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,95u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8, - 25u8,115u8,116u8,111u8,114u8,101u8,95u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,95u8,98u8,117u8, - 114u8,110u8,95u8,99u8,97u8,112u8,34u8,114u8,111u8,116u8,97u8,116u8,101u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8, - 116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8, - 108u8,27u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,115u8, - 95u8,102u8,111u8,114u8,95u8,116u8,101u8,115u8,116u8,20u8,114u8,111u8,116u8,97u8,116u8,101u8,95u8,99u8,111u8,110u8, - 115u8,101u8,110u8,115u8,117u8,115u8,95u8,107u8,101u8,121u8,37u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,110u8,101u8, - 116u8,119u8,111u8,114u8,107u8,95u8,97u8,110u8,100u8,95u8,102u8,117u8,108u8,108u8,110u8,111u8,100u8,101u8,95u8,97u8, - 100u8,100u8,114u8,101u8,115u8,115u8,101u8,115u8,27u8,106u8,111u8,105u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8, - 116u8,111u8,114u8,95u8,115u8,101u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8, - 1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,1u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,5u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8, - 10u8,2u8,16u8,15u8,115u8,99u8,114u8,105u8,112u8,116u8,95u8,112u8,114u8,111u8,108u8,111u8,103u8,117u8,101u8,10u8, - 2u8,16u8,15u8,109u8,111u8,100u8,117u8,108u8,101u8,95u8,112u8,114u8,111u8,108u8,111u8,103u8,117u8,101u8,10u8,2u8, - 28u8,27u8,109u8,117u8,108u8,116u8,105u8,95u8,97u8,103u8,101u8,110u8,116u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8, - 95u8,112u8,114u8,111u8,108u8,111u8,103u8,117u8,101u8,10u8,2u8,9u8,8u8,101u8,112u8,105u8,108u8,111u8,103u8,117u8, - 101u8,10u8,5u8,161u8,2u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,7u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,5u8,32u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,10u8,85u8,12u8,24u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8, - 100u8,97u8,116u8,97u8,95u8,118u8,49u8,64u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,68u8, - 85u8,80u8,76u8,73u8,67u8,65u8,84u8,69u8,95u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,0u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,23u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,68u8,79u8,69u8,83u8,95u8, - 78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,0u8,0u8,0u8,0u8,2u8,2u8,42u8,5u8,43u8,3u8,1u8, - 2u8,5u8,44u8,10u8,5u8,45u8,8u8,3u8,46u8,10u8,3u8,47u8,3u8,48u8,5u8,2u8,2u8,8u8,49u8,5u8, - 50u8,5u8,51u8,5u8,52u8,3u8,53u8,10u8,2u8,54u8,10u8,2u8,55u8,10u8,2u8,56u8,10u8,2u8,3u8,2u8, - 3u8,57u8,8u8,2u8,58u8,3u8,59u8,1u8,0u8,0u8,0u8,0u8,12u8,22u8,10u8,1u8,17u8,11u8,4u8,9u8, - 11u8,0u8,1u8,11u8,1u8,17u8,12u8,12u8,3u8,5u8,20u8,10u8,1u8,17u8,13u8,12u8,4u8,14u8,4u8,56u8, - 0u8,11u8,0u8,11u8,1u8,11u8,2u8,17u8,15u8,11u8,4u8,12u8,3u8,11u8,3u8,2u8,1u8,0u8,0u8,0u8, - 16u8,52u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,14u8,1u8,65u8,17u8,12u8,4u8,64u8, - 13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,3u8,10u8,4u8,35u8,4u8,49u8,5u8,12u8, - 14u8,1u8,10u8,3u8,66u8,17u8,12u8,2u8,14u8,5u8,10u8,2u8,16u8,0u8,56u8,1u8,32u8,4u8,23u8,5u8, - 30u8,11u8,0u8,1u8,11u8,2u8,1u8,7u8,1u8,17u8,17u8,39u8,13u8,5u8,10u8,2u8,16u8,0u8,20u8,68u8, - 13u8,10u8,0u8,10u8,2u8,16u8,0u8,20u8,11u8,2u8,16u8,1u8,20u8,17u8,0u8,1u8,11u8,3u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,7u8,11u8,0u8,1u8,2u8,2u8,0u8,0u8,0u8, - 20u8,221u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,13u8,14u8,2u8,65u8,21u8,12u8,16u8, - 64u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,22u8,10u8,13u8,10u8,16u8,35u8,4u8,220u8,1u8, - 5u8,12u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,14u8,14u8,2u8,10u8,13u8,66u8,21u8,12u8, - 11u8,10u8,11u8,16u8,2u8,65u8,13u8,12u8,17u8,56u8,2u8,12u8,7u8,10u8,14u8,10u8,17u8,35u8,4u8,73u8, - 5u8,29u8,10u8,11u8,16u8,2u8,10u8,14u8,66u8,13u8,12u8,4u8,14u8,22u8,10u8,4u8,56u8,1u8,32u8,4u8, - 40u8,5u8,47u8,11u8,11u8,1u8,11u8,4u8,1u8,7u8,1u8,17u8,17u8,39u8,13u8,22u8,10u8,4u8,20u8,68u8, - 13u8,10u8,4u8,20u8,17u8,12u8,12u8,10u8,10u8,4u8,20u8,56u8,3u8,12u8,21u8,14u8,10u8,11u8,21u8,56u8, - 4u8,12u8,8u8,13u8,7u8,11u8,4u8,20u8,11u8,8u8,56u8,5u8,11u8,14u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,12u8,14u8,5u8,24u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,15u8,10u8, - 11u8,16u8,3u8,65u8,19u8,12u8,18u8,64u8,27u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,20u8,10u8, - 15u8,10u8,18u8,35u8,4u8,104u8,5u8,86u8,10u8,11u8,16u8,3u8,10u8,15u8,66u8,19u8,20u8,10u8,11u8,16u8, - 4u8,20u8,17u8,22u8,12u8,12u8,13u8,20u8,11u8,12u8,68u8,27u8,11u8,15u8,6u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,22u8,12u8,15u8,5u8,81u8,11u8,20u8,10u8,0u8,10u8,1u8,17u8,23u8,12u8,24u8,10u8,11u8, - 16u8,5u8,16u8,6u8,16u8,7u8,20u8,12u8,5u8,10u8,5u8,17u8,12u8,12u8,3u8,14u8,3u8,12u8,6u8,10u8, - 6u8,10u8,11u8,16u8,2u8,11u8,7u8,11u8,24u8,11u8,5u8,10u8,11u8,16u8,5u8,16u8,6u8,16u8,8u8,20u8, - 10u8,11u8,16u8,5u8,16u8,6u8,16u8,9u8,20u8,10u8,11u8,16u8,5u8,16u8,10u8,20u8,7u8,2u8,17u8,24u8, - 12u8,9u8,10u8,9u8,17u8,25u8,12u8,19u8,10u8,11u8,16u8,11u8,20u8,7u8,3u8,34u8,4u8,159u8,1u8,11u8, - 6u8,11u8,9u8,10u8,11u8,16u8,11u8,20u8,17u8,26u8,5u8,161u8,1u8,11u8,6u8,1u8,10u8,11u8,16u8,5u8, - 16u8,6u8,12u8,23u8,10u8,23u8,16u8,7u8,20u8,17u8,11u8,4u8,171u8,1u8,5u8,178u8,1u8,11u8,23u8,1u8, - 11u8,11u8,1u8,7u8,0u8,17u8,27u8,39u8,10u8,23u8,16u8,8u8,20u8,17u8,11u8,4u8,184u8,1u8,5u8,191u8, - 1u8,11u8,23u8,1u8,11u8,11u8,1u8,7u8,0u8,17u8,27u8,39u8,10u8,23u8,16u8,9u8,20u8,17u8,11u8,4u8, - 197u8,1u8,5u8,204u8,1u8,11u8,23u8,1u8,11u8,11u8,1u8,7u8,0u8,17u8,27u8,39u8,11u8,11u8,16u8,5u8, - 16u8,12u8,20u8,4u8,213u8,1u8,11u8,19u8,11u8,23u8,17u8,9u8,5u8,215u8,1u8,11u8,23u8,1u8,11u8,13u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,13u8,5u8,7u8,2u8,3u8,0u8,0u8,0u8,33u8, - 82u8,10u8,1u8,16u8,6u8,12u8,7u8,10u8,0u8,10u8,7u8,16u8,7u8,20u8,10u8,7u8,16u8,13u8,20u8,17u8, - 0u8,12u8,3u8,14u8,3u8,12u8,5u8,10u8,0u8,10u8,7u8,16u8,8u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,17u8,0u8,1u8,11u8,0u8,10u8,7u8,16u8,9u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,17u8,0u8,1u8,11u8,2u8,4u8,54u8,11u8,5u8,10u8,7u8,16u8,8u8,20u8,10u8,7u8,16u8,9u8, - 20u8,10u8,7u8,16u8,13u8,20u8,10u8,1u8,16u8,10u8,20u8,7u8,2u8,17u8,28u8,10u8,7u8,16u8,7u8,20u8, - 10u8,7u8,16u8,8u8,20u8,17u8,29u8,12u8,4u8,5u8,69u8,11u8,5u8,10u8,7u8,16u8,13u8,20u8,10u8,7u8, - 16u8,8u8,20u8,10u8,7u8,16u8,9u8,20u8,17u8,30u8,10u8,7u8,16u8,7u8,20u8,12u8,4u8,11u8,4u8,12u8, - 6u8,11u8,1u8,16u8,12u8,20u8,4u8,79u8,11u8,6u8,11u8,7u8,17u8,9u8,5u8,81u8,11u8,7u8,1u8,2u8, - 4u8,0u8,0u8,0u8,37u8,31u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,14u8,1u8,65u8, - 38u8,12u8,3u8,64u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,2u8,10u8,3u8,35u8, - 4u8,26u8,5u8,12u8,13u8,1u8,69u8,38u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,8u8,18u8,3u8, - 12u8,4u8,13u8,5u8,11u8,4u8,68u8,39u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,2u8,5u8,7u8,11u8,0u8,9u8,11u8,5u8,17u8,5u8,2u8,5u8,0u8,0u8,0u8,40u8,27u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,14u8,2u8,65u8,39u8,12u8,4u8,10u8,3u8,10u8,4u8,35u8, - 4u8,23u8,5u8,10u8,14u8,2u8,10u8,3u8,66u8,39u8,12u8,5u8,10u8,0u8,11u8,5u8,10u8,1u8,17u8,3u8, - 11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,5u8,11u8,0u8,17u8,31u8, - 17u8,32u8,2u8,6u8,0u8,0u8,0u8,41u8,74u8,7u8,4u8,17u8,33u8,12u8,14u8,12u8,13u8,14u8,13u8,17u8, - 34u8,14u8,13u8,7u8,5u8,7u8,6u8,7u8,7u8,7u8,8u8,17u8,35u8,14u8,13u8,7u8,4u8,11u8,14u8,17u8, - 36u8,7u8,9u8,12u8,15u8,14u8,15u8,56u8,6u8,32u8,4u8,35u8,5u8,23u8,13u8,15u8,69u8,13u8,12u8,12u8, - 10u8,12u8,17u8,33u8,12u8,16u8,1u8,14u8,13u8,11u8,12u8,11u8,16u8,17u8,36u8,5u8,18u8,14u8,13u8,11u8, - 3u8,17u8,38u8,14u8,13u8,11u8,2u8,17u8,39u8,14u8,13u8,17u8,40u8,14u8,13u8,11u8,5u8,11u8,6u8,11u8, - 7u8,11u8,8u8,11u8,9u8,11u8,10u8,11u8,11u8,17u8,41u8,14u8,13u8,17u8,42u8,14u8,13u8,11u8,0u8,17u8, - 43u8,14u8,13u8,17u8,44u8,14u8,13u8,17u8,45u8,14u8,13u8,11u8,1u8,17u8,46u8,14u8,13u8,17u8,47u8,14u8, - 13u8,11u8,4u8,17u8,48u8,14u8,13u8,17u8,49u8,14u8,13u8,17u8,50u8,2u8,7u8,0u8,0u8,0u8,48u8,11u8, - 10u8,0u8,17u8,51u8,12u8,2u8,12u8,1u8,10u8,0u8,11u8,2u8,17u8,52u8,11u8,0u8,11u8,1u8,17u8,53u8, - 2u8,8u8,0u8,0u8,0u8,51u8,21u8,10u8,0u8,17u8,51u8,12u8,4u8,12u8,2u8,10u8,0u8,10u8,4u8,17u8, - 52u8,10u8,0u8,11u8,2u8,17u8,53u8,7u8,10u8,17u8,13u8,12u8,3u8,14u8,3u8,11u8,1u8,17u8,54u8,11u8, - 0u8,14u8,3u8,11u8,4u8,17u8,55u8,2u8,9u8,0u8,0u8,0u8,53u8,29u8,10u8,1u8,16u8,8u8,20u8,17u8, - 12u8,12u8,2u8,14u8,2u8,12u8,3u8,10u8,3u8,10u8,0u8,10u8,1u8,16u8,14u8,20u8,10u8,1u8,16u8,15u8, - 20u8,17u8,56u8,10u8,3u8,10u8,0u8,10u8,1u8,16u8,16u8,20u8,11u8,1u8,16u8,17u8,20u8,17u8,57u8,11u8, - 3u8,11u8,0u8,17u8,58u8,2u8,10u8,0u8,0u8,0u8,3u8,3u8,11u8,0u8,17u8,59u8,2u8,0u8,0u8,0u8, - 1u8,1u8,0u8,1u8,2u8,1u8,3u8,1u8,1u8,3u8,0u8,2u8,0u8,2u8,1u8,2u8,2u8,3u8,1u8,1u8, - 4u8,3u8,2u8,2u8,3u8,2u8,4u8,2u8,5u8,2u8,6u8,2u8,7u8,0u8, - ]; - vector::push_back(&mut code, chunk34); - let chunk35 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,10u8,2u8,10u8,34u8,3u8,44u8,75u8,4u8, - 119u8,12u8,5u8,131u8,1u8,114u8,7u8,245u8,1u8,212u8,1u8,8u8,201u8,3u8,32u8,6u8,233u8,3u8,10u8,16u8, - 243u8,3u8,89u8,10u8,204u8,4u8,21u8,11u8,225u8,4u8,2u8,12u8,227u8,4u8,149u8,1u8,13u8,248u8,5u8,4u8, - 14u8,252u8,5u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,8u8,1u8,0u8,1u8, - 1u8,11u8,5u8,1u8,0u8,1u8,1u8,13u8,5u8,1u8,0u8,1u8,1u8,15u8,5u8,1u8,0u8,1u8,1u8,18u8, - 4u8,1u8,0u8,1u8,4u8,20u8,7u8,0u8,0u8,6u8,0u8,1u8,1u8,0u8,0u8,7u8,2u8,1u8,1u8,0u8, - 0u8,8u8,3u8,1u8,1u8,0u8,0u8,9u8,4u8,1u8,1u8,0u8,3u8,16u8,4u8,6u8,0u8,2u8,17u8,8u8, - 8u8,0u8,1u8,19u8,0u8,9u8,1u8,0u8,1u8,6u8,10u8,1u8,1u8,0u8,4u8,21u8,12u8,13u8,0u8,1u8, - 7u8,14u8,11u8,1u8,0u8,1u8,8u8,16u8,9u8,1u8,0u8,1u8,22u8,17u8,1u8,1u8,0u8,1u8,9u8,4u8, - 1u8,1u8,0u8,6u8,7u8,7u8,7u8,9u8,7u8,10u8,7u8,11u8,7u8,12u8,7u8,2u8,6u8,12u8,3u8,0u8, - 5u8,6u8,12u8,10u8,2u8,10u8,2u8,2u8,1u8,3u8,6u8,12u8,5u8,3u8,1u8,6u8,12u8,2u8,5u8,6u8, - 11u8,0u8,1u8,9u8,0u8,1u8,5u8,1u8,9u8,0u8,1u8,3u8,1u8,11u8,4u8,1u8,9u8,0u8,2u8,11u8, - 4u8,1u8,9u8,0u8,6u8,11u8,1u8,1u8,9u8,0u8,3u8,11u8,1u8,1u8,9u8,0u8,11u8,2u8,1u8,9u8, - 0u8,11u8,3u8,1u8,9u8,0u8,1u8,10u8,2u8,1u8,8u8,5u8,5u8,6u8,12u8,8u8,5u8,8u8,5u8,2u8, - 1u8,3u8,5u8,6u8,11u8,0u8,1u8,9u8,0u8,11u8,4u8,1u8,9u8,0u8,2u8,3u8,6u8,11u8,3u8,1u8, - 9u8,0u8,2u8,5u8,11u8,4u8,1u8,9u8,0u8,12u8,109u8,97u8,110u8,97u8,103u8,101u8,100u8,95u8,99u8,111u8, - 105u8,110u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8, - 6u8,115u8,116u8,114u8,105u8,110u8,103u8,12u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,105u8,101u8,115u8, - 4u8,98u8,117u8,114u8,110u8,10u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,4u8,109u8,105u8,110u8, - 116u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,8u8,98u8,117u8,114u8,110u8,95u8,99u8,97u8,112u8,14u8, - 66u8,117u8,114u8,110u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,10u8,102u8,114u8,101u8,101u8,122u8, - 101u8,95u8,99u8,97u8,112u8,16u8,70u8,114u8,101u8,101u8,122u8,101u8,67u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8, - 116u8,121u8,8u8,109u8,105u8,110u8,116u8,95u8,99u8,97u8,112u8,14u8,77u8,105u8,110u8,116u8,67u8,97u8,112u8,97u8, - 98u8,105u8,108u8,105u8,116u8,121u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,9u8,110u8,111u8, - 116u8,95u8,102u8,111u8,117u8,110u8,100u8,4u8,67u8,111u8,105u8,110u8,8u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8, - 119u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,4u8,117u8,116u8,102u8,56u8,7u8,100u8,101u8,112u8,111u8,115u8,105u8, - 116u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8, - 118u8,49u8,69u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,78u8,79u8,95u8,67u8,65u8,80u8, - 65u8,66u8,73u8,76u8,73u8,84u8,73u8,69u8,83u8,40u8,65u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,104u8,97u8, - 115u8,32u8,110u8,111u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,105u8,101u8,115u8,32u8,40u8,98u8, - 117u8,114u8,110u8,47u8,109u8,105u8,110u8,116u8,41u8,46u8,0u8,0u8,0u8,2u8,3u8,10u8,11u8,1u8,1u8,9u8, - 0u8,12u8,11u8,2u8,1u8,9u8,0u8,14u8,11u8,3u8,1u8,9u8,0u8,0u8,7u8,0u8,1u8,4u8,1u8,0u8, - 5u8,22u8,10u8,0u8,17u8,4u8,12u8,2u8,10u8,2u8,59u8,0u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8, - 0u8,17u8,5u8,39u8,11u8,2u8,61u8,0u8,12u8,3u8,11u8,0u8,11u8,1u8,56u8,0u8,11u8,3u8,55u8,0u8, - 56u8,1u8,2u8,1u8,1u8,4u8,0u8,11u8,18u8,10u8,0u8,11u8,1u8,17u8,8u8,11u8,2u8,17u8,8u8,11u8, - 3u8,11u8,4u8,56u8,2u8,12u8,7u8,12u8,6u8,12u8,5u8,11u8,0u8,11u8,5u8,11u8,6u8,11u8,7u8,57u8, - 0u8,63u8,0u8,2u8,2u8,1u8,4u8,1u8,0u8,15u8,22u8,11u8,0u8,17u8,4u8,12u8,3u8,10u8,3u8,59u8, - 0u8,4u8,7u8,5u8,10u8,7u8,0u8,17u8,5u8,39u8,11u8,3u8,61u8,0u8,12u8,4u8,11u8,2u8,11u8,4u8, - 55u8,1u8,56u8,3u8,12u8,5u8,11u8,1u8,11u8,5u8,56u8,4u8,2u8,3u8,1u8,4u8,0u8,1u8,3u8,11u8, - 0u8,56u8,5u8,2u8,0u8,0u8,0u8,2u8,0u8,7u8,1u8,7u8,0u8, - ]; - vector::push_back(&mut code, chunk35); - let chunk36 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,36u8,2u8,36u8,92u8,3u8,128u8,1u8,194u8, - 3u8,4u8,194u8,4u8,94u8,5u8,160u8,5u8,188u8,5u8,7u8,220u8,10u8,215u8,18u8,8u8,179u8,29u8,32u8,6u8, - 211u8,29u8,201u8,1u8,16u8,156u8,31u8,167u8,14u8,10u8,195u8,45u8,230u8,1u8,12u8,169u8,47u8,218u8,21u8,13u8, - 131u8,69u8,36u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8, - 8u8,0u8,9u8,0u8,10u8,0u8,11u8,0u8,12u8,0u8,13u8,0u8,14u8,0u8,15u8,0u8,16u8,0u8,17u8,0u8, - 18u8,6u8,0u8,0u8,19u8,6u8,0u8,0u8,20u8,6u8,0u8,0u8,21u8,7u8,0u8,0u8,22u8,6u8,0u8,0u8, - 23u8,8u8,0u8,0u8,24u8,3u8,0u8,0u8,25u8,7u8,0u8,0u8,26u8,6u8,0u8,0u8,27u8,6u8,0u8,0u8, - 28u8,6u8,0u8,0u8,29u8,6u8,0u8,0u8,30u8,6u8,0u8,14u8,39u8,7u8,0u8,1u8,41u8,6u8,0u8,11u8, - 48u8,7u8,1u8,0u8,0u8,13u8,57u8,7u8,2u8,0u8,0u8,0u8,0u8,15u8,87u8,4u8,2u8,3u8,1u8,0u8, - 1u8,8u8,91u8,4u8,1u8,6u8,1u8,2u8,125u8,8u8,0u8,0u8,31u8,0u8,1u8,0u8,0u8,32u8,2u8,1u8, - 0u8,0u8,33u8,3u8,1u8,0u8,0u8,34u8,4u8,1u8,0u8,0u8,35u8,5u8,1u8,0u8,0u8,36u8,6u8,1u8, - 0u8,0u8,37u8,7u8,8u8,0u8,0u8,38u8,7u8,8u8,0u8,0u8,40u8,9u8,1u8,0u8,0u8,42u8,10u8,11u8, - 0u8,0u8,43u8,12u8,12u8,0u8,0u8,44u8,13u8,1u8,0u8,0u8,45u8,13u8,1u8,0u8,0u8,46u8,14u8,1u8, - 0u8,0u8,47u8,15u8,1u8,0u8,0u8,49u8,16u8,1u8,0u8,0u8,50u8,0u8,1u8,0u8,0u8,51u8,17u8,1u8, - 0u8,0u8,52u8,6u8,6u8,0u8,0u8,53u8,18u8,12u8,0u8,0u8,54u8,6u8,19u8,0u8,0u8,55u8,7u8,20u8, - 0u8,0u8,56u8,6u8,21u8,0u8,0u8,58u8,6u8,22u8,0u8,0u8,59u8,6u8,21u8,0u8,0u8,60u8,23u8,24u8, - 0u8,0u8,61u8,6u8,21u8,0u8,0u8,62u8,6u8,25u8,0u8,0u8,63u8,4u8,1u8,0u8,0u8,64u8,26u8,24u8, - 0u8,0u8,65u8,0u8,1u8,0u8,0u8,66u8,2u8,1u8,0u8,0u8,67u8,27u8,1u8,0u8,0u8,68u8,28u8,1u8, - 0u8,0u8,69u8,29u8,1u8,0u8,0u8,70u8,30u8,1u8,0u8,0u8,71u8,13u8,1u8,0u8,0u8,72u8,31u8,1u8, - 0u8,0u8,73u8,32u8,33u8,0u8,0u8,74u8,34u8,1u8,0u8,12u8,113u8,10u8,6u8,0u8,17u8,114u8,36u8,1u8, - 1u8,0u8,8u8,115u8,38u8,1u8,1u8,6u8,13u8,116u8,40u8,1u8,2u8,4u8,4u8,15u8,116u8,42u8,1u8,2u8, - 3u8,0u8,17u8,117u8,45u8,8u8,1u8,0u8,7u8,118u8,21u8,21u8,0u8,7u8,119u8,21u8,21u8,0u8,7u8,120u8, - 21u8,21u8,0u8,15u8,121u8,47u8,48u8,2u8,3u8,0u8,1u8,122u8,6u8,21u8,0u8,3u8,123u8,50u8,12u8,1u8, - 0u8,1u8,124u8,51u8,11u8,0u8,5u8,126u8,6u8,8u8,1u8,0u8,5u8,127u8,10u8,1u8,1u8,0u8,11u8,128u8, - 1u8,54u8,55u8,1u8,0u8,11u8,129u8,1u8,1u8,55u8,1u8,0u8,13u8,40u8,1u8,56u8,2u8,4u8,4u8,16u8, - 130u8,1u8,1u8,21u8,0u8,4u8,131u8,1u8,1u8,53u8,0u8,1u8,132u8,1u8,59u8,1u8,1u8,2u8,6u8,6u8, - 6u8,60u8,0u8,9u8,133u8,1u8,1u8,8u8,0u8,7u8,134u8,1u8,21u8,21u8,0u8,15u8,135u8,1u8,1u8,63u8, - 2u8,3u8,4u8,1u8,136u8,1u8,10u8,65u8,1u8,6u8,15u8,117u8,47u8,8u8,2u8,3u8,0u8,7u8,137u8,1u8, - 21u8,21u8,0u8,1u8,138u8,1u8,75u8,6u8,0u8,11u8,139u8,1u8,77u8,8u8,1u8,0u8,11u8,121u8,77u8,50u8, - 1u8,0u8,13u8,140u8,1u8,81u8,8u8,2u8,4u8,4u8,13u8,121u8,81u8,48u8,2u8,4u8,4u8,15u8,141u8,1u8, - 82u8,83u8,2u8,3u8,0u8,17u8,142u8,1u8,45u8,85u8,1u8,0u8,17u8,143u8,1u8,86u8,54u8,1u8,0u8,10u8, - 144u8,1u8,12u8,12u8,0u8,15u8,145u8,1u8,82u8,95u8,2u8,3u8,0u8,13u8,145u8,1u8,96u8,95u8,2u8,4u8, - 4u8,41u8,6u8,42u8,37u8,43u8,39u8,44u8,41u8,42u8,43u8,45u8,6u8,49u8,41u8,51u8,21u8,53u8,52u8,54u8, - 52u8,41u8,53u8,55u8,12u8,56u8,12u8,57u8,39u8,60u8,58u8,56u8,61u8,55u8,61u8,64u8,41u8,57u8,64u8,65u8, - 37u8,65u8,66u8,65u8,67u8,65u8,43u8,65u8,68u8,65u8,69u8,65u8,70u8,65u8,71u8,65u8,72u8,66u8,41u8,42u8, - 69u8,42u8,71u8,69u8,12u8,70u8,12u8,71u8,39u8,72u8,39u8,73u8,41u8,74u8,6u8,75u8,6u8,42u8,66u8,42u8, - 70u8,71u8,64u8,43u8,64u8,42u8,72u8,42u8,67u8,77u8,41u8,78u8,39u8,42u8,68u8,2u8,6u8,12u8,5u8,0u8, - 2u8,6u8,12u8,10u8,5u8,3u8,5u8,7u8,8u8,5u8,8u8,7u8,3u8,6u8,12u8,5u8,3u8,2u8,6u8,12u8, - 6u8,8u8,5u8,1u8,5u8,2u8,5u8,3u8,1u8,1u8,4u8,6u8,12u8,3u8,10u8,8u8,13u8,10u8,10u8,2u8, - 1u8,6u8,12u8,2u8,12u8,8u8,14u8,1u8,10u8,2u8,3u8,6u8,12u8,5u8,10u8,2u8,8u8,5u8,10u8,5u8, - 3u8,2u8,10u8,2u8,10u8,2u8,10u8,8u8,13u8,10u8,10u8,2u8,5u8,6u8,12u8,10u8,5u8,3u8,10u8,8u8, - 13u8,10u8,10u8,2u8,6u8,6u8,12u8,10u8,5u8,3u8,11u8,15u8,1u8,8u8,14u8,10u8,8u8,13u8,10u8,10u8, - 2u8,4u8,5u8,5u8,10u8,2u8,8u8,3u8,2u8,5u8,10u8,2u8,1u8,10u8,8u8,7u8,1u8,8u8,7u8,1u8, - 3u8,1u8,11u8,16u8,2u8,8u8,13u8,10u8,2u8,2u8,6u8,10u8,5u8,6u8,8u8,7u8,2u8,3u8,3u8,1u8, - 10u8,5u8,1u8,7u8,8u8,5u8,3u8,5u8,5u8,10u8,2u8,3u8,6u8,12u8,10u8,8u8,13u8,10u8,10u8,2u8, - 4u8,6u8,12u8,10u8,8u8,13u8,10u8,10u8,2u8,1u8,2u8,6u8,12u8,3u8,2u8,6u8,10u8,5u8,5u8,3u8, - 5u8,3u8,5u8,2u8,1u8,1u8,4u8,6u8,12u8,5u8,3u8,1u8,2u8,7u8,8u8,5u8,5u8,2u8,7u8,10u8, - 9u8,0u8,10u8,9u8,0u8,1u8,8u8,0u8,2u8,7u8,11u8,18u8,1u8,9u8,0u8,9u8,0u8,2u8,5u8,1u8, - 3u8,7u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,2u8,3u8,8u8,7u8,3u8,7u8,11u8, - 17u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8,8u8,1u8,2u8,5u8,6u8,10u8,5u8,2u8,6u8, - 10u8,9u8,0u8,6u8,9u8,0u8,5u8,1u8,1u8,6u8,8u8,5u8,3u8,6u8,8u8,7u8,2u8,6u8,11u8,17u8, - 2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,6u8,9u8,1u8,3u8,12u8,8u8,14u8,3u8,1u8,6u8,9u8,0u8, - 2u8,6u8,12u8,10u8,2u8,1u8,8u8,19u8,1u8,2u8,1u8,9u8,0u8,1u8,11u8,15u8,1u8,9u8,0u8,1u8, - 11u8,16u8,2u8,9u8,0u8,9u8,1u8,2u8,12u8,8u8,6u8,1u8,8u8,6u8,5u8,5u8,2u8,10u8,2u8,10u8, - 2u8,9u8,0u8,1u8,12u8,1u8,8u8,14u8,17u8,1u8,11u8,18u8,1u8,8u8,8u8,11u8,18u8,1u8,8u8,11u8, - 11u8,18u8,1u8,8u8,1u8,11u8,18u8,1u8,8u8,12u8,11u8,18u8,1u8,8u8,2u8,11u8,18u8,1u8,8u8,10u8, - 11u8,18u8,1u8,8u8,9u8,11u8,18u8,1u8,8u8,4u8,6u8,12u8,10u8,5u8,3u8,11u8,17u8,2u8,3u8,8u8, - 7u8,11u8,16u8,2u8,8u8,13u8,10u8,2u8,11u8,15u8,1u8,8u8,14u8,11u8,18u8,1u8,8u8,0u8,5u8,1u8, - 11u8,17u8,2u8,9u8,0u8,9u8,1u8,2u8,8u8,13u8,10u8,2u8,1u8,11u8,18u8,1u8,9u8,0u8,1u8,8u8, - 8u8,1u8,8u8,11u8,1u8,8u8,12u8,1u8,8u8,2u8,1u8,8u8,10u8,1u8,8u8,9u8,1u8,8u8,4u8,3u8, - 7u8,8u8,5u8,3u8,3u8,2u8,7u8,8u8,5u8,3u8,2u8,6u8,5u8,10u8,2u8,4u8,10u8,2u8,6u8,8u8, - 5u8,3u8,6u8,8u8,7u8,1u8,6u8,11u8,15u8,1u8,9u8,0u8,4u8,3u8,6u8,8u8,5u8,3u8,10u8,8u8, - 7u8,2u8,1u8,6u8,8u8,5u8,7u8,3u8,3u8,3u8,3u8,6u8,5u8,6u8,10u8,5u8,6u8,11u8,16u8,2u8, - 5u8,1u8,2u8,6u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,2u8,7u8,11u8,17u8,2u8,9u8, - 0u8,9u8,1u8,9u8,0u8,1u8,9u8,1u8,10u8,6u8,5u8,3u8,3u8,3u8,7u8,8u8,5u8,5u8,5u8,7u8, - 10u8,5u8,10u8,5u8,6u8,10u8,5u8,2u8,1u8,3u8,2u8,7u8,10u8,9u8,0u8,3u8,7u8,3u8,10u8,2u8, - 3u8,5u8,7u8,11u8,18u8,1u8,8u8,10u8,7u8,8u8,5u8,3u8,9u8,6u8,8u8,13u8,3u8,8u8,13u8,7u8, - 11u8,16u8,2u8,8u8,13u8,10u8,2u8,7u8,8u8,5u8,5u8,3u8,11u8,16u8,2u8,8u8,13u8,10u8,2u8,10u8, - 2u8,1u8,8u8,13u8,5u8,1u8,7u8,8u8,5u8,5u8,3u8,3u8,4u8,6u8,8u8,5u8,6u8,10u8,2u8,3u8, - 6u8,8u8,7u8,5u8,10u8,5u8,3u8,3u8,5u8,6u8,10u8,5u8,6u8,1u8,1u8,7u8,8u8,5u8,1u8,1u8, - 6u8,11u8,16u8,2u8,5u8,1u8,4u8,6u8,5u8,7u8,8u8,5u8,5u8,7u8,11u8,16u8,2u8,5u8,1u8,1u8, - 7u8,9u8,1u8,2u8,7u8,11u8,16u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,16u8,109u8,117u8,108u8,116u8, - 105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8, - 10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,3u8,98u8,99u8,115u8,8u8,99u8,104u8,97u8,105u8, - 110u8,95u8,105u8,100u8,4u8,99u8,111u8,105u8,110u8,13u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8, - 110u8,101u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,101u8,97u8,116u8, - 117u8,114u8,101u8,115u8,4u8,104u8,97u8,115u8,104u8,6u8,111u8,112u8,116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8, - 110u8,101u8,114u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,6u8,115u8,116u8,114u8,105u8,110u8, - 103u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,6u8,118u8,101u8, - 99u8,116u8,111u8,114u8,14u8,65u8,100u8,100u8,79u8,119u8,110u8,101u8,114u8,115u8,69u8,118u8,101u8,110u8,116u8,22u8, - 67u8,114u8,101u8,97u8,116u8,101u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8, - 110u8,116u8,31u8,69u8,120u8,101u8,99u8,117u8,116u8,101u8,82u8,101u8,106u8,101u8,99u8,116u8,101u8,100u8,84u8,114u8, - 97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,14u8,69u8,120u8,101u8,99u8,117u8, - 116u8,105u8,111u8,110u8,69u8,114u8,114u8,111u8,114u8,20u8,77u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,85u8,112u8, - 100u8,97u8,116u8,101u8,100u8,69u8,118u8,101u8,110u8,116u8,15u8,77u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,65u8, - 99u8,99u8,111u8,117u8,110u8,116u8,30u8,77u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,65u8,99u8,99u8,111u8,117u8, - 110u8,116u8,67u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,77u8,101u8,115u8,115u8,97u8,103u8,101u8,19u8,77u8,117u8, - 108u8,116u8,105u8,115u8,105u8,103u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,17u8,82u8,101u8, - 109u8,111u8,118u8,101u8,79u8,119u8,110u8,101u8,114u8,115u8,69u8,118u8,101u8,110u8,116u8,31u8,84u8,114u8,97u8,110u8, - 115u8,97u8,99u8,116u8,105u8,111u8,110u8,69u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,70u8,97u8,105u8,108u8, - 101u8,100u8,69u8,118u8,101u8,110u8,116u8,34u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,69u8, - 120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,83u8,117u8,99u8,99u8,101u8,101u8,100u8,101u8,100u8,69u8,118u8,101u8, - 110u8,116u8,29u8,85u8,112u8,100u8,97u8,116u8,101u8,83u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,82u8, - 101u8,113u8,117u8,105u8,114u8,101u8,100u8,69u8,118u8,101u8,110u8,116u8,9u8,86u8,111u8,116u8,101u8,69u8,118u8,101u8, - 110u8,116u8,9u8,97u8,100u8,100u8,95u8,111u8,119u8,110u8,101u8,114u8,10u8,97u8,100u8,100u8,95u8,111u8,119u8,110u8, - 101u8,114u8,115u8,15u8,97u8,100u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,19u8, - 97u8,112u8,112u8,114u8,111u8,118u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,15u8, - 97u8,115u8,115u8,101u8,114u8,116u8,95u8,105u8,115u8,95u8,111u8,119u8,110u8,101u8,114u8,30u8,97u8,115u8,115u8,101u8, - 114u8,116u8,95u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8, - 101u8,120u8,105u8,115u8,116u8,115u8,15u8,99u8,97u8,110u8,95u8,98u8,101u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8, - 101u8,100u8,15u8,99u8,97u8,110u8,95u8,98u8,101u8,95u8,114u8,101u8,106u8,101u8,99u8,116u8,101u8,100u8,6u8,83u8, - 116u8,114u8,105u8,110u8,103u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8, - 97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,109u8,117u8,108u8, - 116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,28u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,115u8,101u8, - 101u8,100u8,18u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8, - 110u8,28u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8, - 95u8,119u8,105u8,116u8,104u8,95u8,104u8,97u8,115u8,104u8,28u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8, - 116u8,104u8,95u8,101u8,120u8,105u8,115u8,116u8,105u8,110u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,18u8, - 99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,111u8,119u8,110u8,101u8,114u8,115u8,6u8,79u8, - 112u8,116u8,105u8,111u8,110u8,27u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,119u8,105u8,116u8,104u8,95u8,111u8,119u8, - 110u8,101u8,114u8,115u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,28u8,101u8,120u8,101u8,99u8,117u8,116u8, - 101u8,95u8,114u8,101u8,106u8,101u8,99u8,116u8,101u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8, - 111u8,110u8,36u8,102u8,97u8,105u8,108u8,101u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8, - 110u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,99u8,108u8,101u8,97u8,110u8,117u8,112u8,33u8, - 103u8,101u8,116u8,95u8,110u8,101u8,120u8,116u8,95u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,28u8,103u8,101u8,116u8,95u8,110u8,101u8, - 120u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,112u8,97u8,121u8,108u8,111u8, - 97u8,100u8,24u8,103u8,101u8,116u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,116u8,114u8,97u8,110u8,115u8, - 97u8,99u8,116u8,105u8,111u8,110u8,115u8,15u8,103u8,101u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8, - 105u8,111u8,110u8,29u8,108u8,97u8,115u8,116u8,95u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,95u8,115u8,101u8, - 113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8, - 77u8,97u8,112u8,8u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,20u8,110u8,101u8,120u8,116u8,95u8,115u8,101u8, - 113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,28u8,110u8,117u8,109u8,95u8,97u8,112u8, - 112u8,114u8,111u8,118u8,97u8,108u8,115u8,95u8,97u8,110u8,100u8,95u8,114u8,101u8,106u8,101u8,99u8,116u8,105u8,111u8, - 110u8,115u8,23u8,110u8,117u8,109u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,95u8,114u8,101u8, - 113u8,117u8,105u8,114u8,101u8,100u8,6u8,111u8,119u8,110u8,101u8,114u8,115u8,18u8,114u8,101u8,106u8,101u8,99u8,116u8, - 95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,27u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8, - 101u8,120u8,101u8,99u8,117u8,116u8,101u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8, - 12u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8,111u8,119u8,110u8,101u8,114u8,13u8,114u8,101u8,109u8,111u8,118u8,101u8, - 95u8,111u8,119u8,110u8,101u8,114u8,115u8,40u8,115u8,117u8,99u8,99u8,101u8,115u8,115u8,102u8,117u8,108u8,95u8,116u8, - 114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8, - 95u8,99u8,108u8,101u8,97u8,110u8,117u8,112u8,15u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,109u8,101u8,116u8,97u8, - 100u8,97u8,116u8,97u8,24u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8, - 95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,26u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8, - 110u8,97u8,116u8,117u8,114u8,101u8,115u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,29u8,118u8,97u8,108u8, - 105u8,100u8,97u8,116u8,101u8,95u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,116u8,114u8,97u8,110u8,115u8, - 97u8,99u8,116u8,105u8,111u8,110u8,15u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,101u8,95u8,111u8,119u8,110u8,101u8, - 114u8,115u8,4u8,118u8,111u8,116u8,101u8,17u8,118u8,111u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,110u8, - 99u8,116u8,105u8,111u8,110u8,12u8,111u8,119u8,110u8,101u8,114u8,115u8,95u8,97u8,100u8,100u8,101u8,100u8,7u8,99u8, - 114u8,101u8,97u8,116u8,111u8,114u8,15u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8, - 101u8,114u8,11u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,14u8,110u8,117u8,109u8,95u8,114u8, - 101u8,106u8,101u8,99u8,116u8,105u8,111u8,110u8,115u8,8u8,101u8,120u8,101u8,99u8,117u8,116u8,111u8,114u8,14u8,97u8, - 98u8,111u8,114u8,116u8,95u8,108u8,111u8,99u8,97u8,116u8,105u8,111u8,110u8,10u8,101u8,114u8,114u8,111u8,114u8,95u8, - 116u8,121u8,112u8,101u8,10u8,101u8,114u8,114u8,111u8,114u8,95u8,99u8,111u8,100u8,101u8,12u8,111u8,108u8,100u8,95u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,12u8,110u8,101u8,119u8,95u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8, - 97u8,12u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,115u8,5u8,84u8,97u8,98u8,108u8,101u8, - 29u8,108u8,97u8,115u8,116u8,95u8,101u8,120u8,101u8,99u8,117u8,116u8,101u8,100u8,95u8,115u8,101u8,113u8,117u8,101u8, - 110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,10u8,115u8,105u8,103u8,110u8,101u8,114u8,95u8,99u8,97u8, - 112u8,17u8,97u8,100u8,100u8,95u8,111u8,119u8,110u8,101u8,114u8,115u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8, - 69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,20u8,114u8,101u8,109u8,111u8,118u8,101u8,95u8,111u8, - 119u8,110u8,101u8,114u8,115u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,32u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8, - 115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,95u8,101u8, - 118u8,101u8,110u8,116u8,115u8,25u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8, - 116u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,118u8,111u8,116u8,101u8,95u8,101u8,118u8,101u8, - 110u8,116u8,115u8,35u8,101u8,120u8,101u8,99u8,117u8,116u8,101u8,95u8,114u8,101u8,106u8,101u8,99u8,116u8,101u8,100u8, - 95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,26u8, - 101u8,120u8,101u8,99u8,117u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,35u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8, - 120u8,101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,102u8,97u8,105u8,108u8,101u8,100u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,23u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,117u8,112u8,100u8,97u8,116u8,101u8,100u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,15u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,7u8,112u8,97u8,121u8,108u8,111u8,97u8,100u8,12u8,112u8,97u8,121u8,108u8,111u8,97u8,100u8,95u8,104u8, - 97u8,115u8,104u8,5u8,118u8,111u8,116u8,101u8,115u8,18u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,116u8, - 105u8,109u8,101u8,95u8,115u8,101u8,99u8,115u8,14u8,111u8,119u8,110u8,101u8,114u8,115u8,95u8,114u8,101u8,109u8,111u8, - 118u8,101u8,100u8,19u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,112u8,97u8,121u8,108u8, - 111u8,97u8,100u8,13u8,110u8,117u8,109u8,95u8,97u8,112u8,112u8,114u8,111u8,118u8,97u8,108u8,115u8,15u8,101u8,120u8, - 101u8,99u8,117u8,116u8,105u8,111u8,110u8,95u8,101u8,114u8,114u8,111u8,114u8,27u8,111u8,108u8,100u8,95u8,110u8,117u8, - 109u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8, - 100u8,27u8,110u8,101u8,119u8,95u8,110u8,117u8,109u8,95u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8, - 95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,5u8,111u8,119u8,110u8,101u8,114u8,8u8,97u8,112u8,112u8,114u8, - 111u8,118u8,101u8,100u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,6u8,97u8,112u8,112u8,101u8, - 110u8,100u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,3u8,97u8,100u8,100u8,8u8,99u8,111u8, - 110u8,116u8,97u8,105u8,110u8,115u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8,101u8, - 110u8,105u8,101u8,100u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,16u8,105u8, - 110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,6u8,98u8,111u8,114u8,114u8, - 111u8,119u8,19u8,103u8,101u8,116u8,95u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8, - 101u8,114u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8, - 101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,9u8,65u8,112u8,116u8,111u8, - 115u8,67u8,111u8,105u8,110u8,21u8,105u8,115u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,114u8,101u8,103u8, - 105u8,115u8,116u8,101u8,114u8,101u8,100u8,8u8,114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,4u8,115u8,111u8,109u8, - 101u8,4u8,110u8,111u8,110u8,101u8,11u8,110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,3u8,103u8, - 101u8,116u8,21u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,115u8,105u8,103u8,110u8,101u8,100u8,95u8,109u8,101u8,115u8, - 115u8,97u8,103u8,101u8,25u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,115u8,95u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,11u8,117u8,110u8,97u8,118u8,97u8,105u8,108u8,97u8,98u8, - 108u8,101u8,3u8,110u8,101u8,119u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8, - 100u8,108u8,101u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,7u8,105u8,115u8, - 95u8,115u8,111u8,109u8,101u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,6u8,114u8, - 101u8,109u8,111u8,118u8,101u8,8u8,105u8,110u8,100u8,101u8,120u8,95u8,111u8,102u8,11u8,115u8,119u8,97u8,112u8,95u8, - 114u8,101u8,109u8,111u8,118u8,101u8,8u8,115u8,104u8,97u8,51u8,95u8,50u8,53u8,54u8,10u8,98u8,111u8,114u8,114u8, - 111u8,119u8,95u8,109u8,117u8,116u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8, - 34u8,33u8,97u8,112u8,116u8,111u8,115u8,95u8,102u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,58u8,58u8,109u8, - 117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,3u8,8u8,210u8,7u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,17u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,14u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,217u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,211u8,7u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,216u8,7u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,214u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,5u8,1u8,0u8,18u8, - 97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,146u8,14u8, - 16u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,68u8,85u8,80u8,76u8,73u8,67u8,65u8,84u8,69u8, - 95u8,79u8,87u8,78u8,69u8,82u8,58u8,79u8,119u8,110u8,101u8,114u8,32u8,108u8,105u8,115u8,116u8,32u8,99u8,97u8, - 110u8,110u8,111u8,116u8,32u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,115u8,97u8,109u8, - 101u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8, - 111u8,110u8,99u8,101u8,46u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,80u8,65u8,89u8,76u8,79u8, - 65u8,68u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,66u8,69u8,95u8,69u8,77u8,80u8,84u8,89u8,36u8,84u8, - 114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,112u8,97u8,121u8,108u8,111u8,97u8,100u8,32u8,99u8, - 97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,101u8,109u8,112u8,116u8,121u8,46u8,5u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,18u8,69u8,78u8,79u8,84u8,95u8,69u8,78u8,79u8,85u8,71u8,72u8,95u8,79u8,87u8,78u8,69u8, - 82u8,83u8,46u8,77u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8, - 109u8,117u8,115u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,97u8,116u8,32u8,108u8,101u8,97u8,115u8,116u8,32u8,111u8, - 110u8,101u8,32u8,111u8,119u8,110u8,101u8,114u8,46u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,78u8, - 79u8,84u8,95u8,69u8,78u8,79u8,85u8,71u8,72u8,95u8,82u8,69u8,74u8,69u8,67u8,84u8,73u8,79u8,78u8,83u8, - 73u8,84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,116u8, - 32u8,114u8,101u8,99u8,101u8,105u8,118u8,101u8,100u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,114u8,101u8,106u8, - 101u8,99u8,116u8,105u8,111u8,110u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,111u8,102u8,102u8,105u8,99u8,105u8, - 97u8,108u8,108u8,121u8,32u8,114u8,101u8,106u8,101u8,99u8,116u8,101u8,100u8,46u8,11u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,28u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,83u8,73u8,71u8,78u8,65u8,84u8,85u8,82u8, - 69u8,83u8,95u8,82u8,69u8,81u8,85u8,73u8,82u8,69u8,68u8,92u8,78u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8, - 102u8,32u8,115u8,105u8,103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,32u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8, - 100u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,109u8,111u8,114u8,101u8,32u8,116u8,104u8,97u8,110u8,32u8, - 122u8,101u8,114u8,111u8,32u8,97u8,110u8,100u8,32u8,97u8,116u8,32u8,109u8,111u8,115u8,116u8,32u8,116u8,104u8,101u8, - 32u8,116u8,111u8,116u8,97u8,108u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8,32u8,111u8,119u8,110u8, - 101u8,114u8,115u8,46u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8, - 68u8,95u8,80u8,65u8,89u8,76u8,79u8,65u8,68u8,95u8,72u8,65u8,83u8,72u8,49u8,80u8,97u8,121u8,108u8,111u8, - 97u8,100u8,32u8,104u8,97u8,115u8,104u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,101u8,120u8,97u8,99u8, - 116u8,108u8,121u8,32u8,51u8,50u8,32u8,98u8,121u8,116u8,101u8,115u8,32u8,40u8,115u8,104u8,97u8,51u8,45u8,50u8, - 53u8,54u8,41u8,46u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,40u8,69u8,79u8,87u8,78u8,69u8,82u8,95u8, - 67u8,65u8,78u8,78u8,79u8,84u8,95u8,66u8,69u8,95u8,77u8,85u8,76u8,84u8,73u8,83u8,73u8,71u8,95u8,65u8, - 67u8,67u8,79u8,85u8,78u8,84u8,95u8,73u8,84u8,83u8,69u8,76u8,70u8,47u8,84u8,104u8,101u8,32u8,109u8,117u8, - 108u8,116u8,105u8,115u8,105u8,103u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,105u8,116u8,115u8,101u8,108u8, - 102u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,97u8,110u8,32u8,111u8,119u8,110u8,101u8,114u8, - 46u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,77u8,85u8,76u8,84u8,73u8,83u8,73u8,71u8,95u8, - 65u8,67u8,67u8,79u8,85u8,78u8,84u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,78u8,65u8,66u8,76u8,69u8,68u8, - 95u8,89u8,69u8,84u8,67u8,77u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8, - 116u8,115u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,116u8,32u8,98u8,101u8,101u8,110u8,32u8,101u8,110u8,97u8,98u8, - 108u8,101u8,100u8,32u8,111u8,110u8,32u8,116u8,104u8,105u8,115u8,32u8,99u8,117u8,114u8,114u8,101u8,110u8,116u8,32u8, - 110u8,101u8,116u8,119u8,111u8,114u8,107u8,32u8,121u8,101u8,116u8,46u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 46u8,69u8,78u8,85u8,77u8,66u8,69u8,82u8,95u8,79u8,70u8,95u8,77u8,69u8,84u8,65u8,68u8,65u8,84u8,65u8, - 95u8,75u8,69u8,89u8,83u8,95u8,65u8,78u8,68u8,95u8,86u8,65u8,76u8,85u8,69u8,83u8,95u8,68u8,79u8,78u8, - 84u8,95u8,77u8,65u8,84u8,67u8,72u8,51u8,84u8,104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8, - 102u8,32u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,32u8,107u8,101u8,121u8,115u8,32u8,97u8,110u8,100u8,32u8, - 118u8,97u8,108u8,117u8,101u8,115u8,32u8,100u8,111u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,46u8,16u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,68u8,85u8,80u8,76u8,73u8,67u8,65u8,84u8,69u8,95u8,77u8, - 69u8,84u8,65u8,68u8,65u8,84u8,65u8,95u8,75u8,69u8,89u8,60u8,84u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8, - 105u8,102u8,105u8,101u8,100u8,32u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,32u8,99u8,111u8,110u8,116u8,97u8, - 105u8,110u8,115u8,32u8,100u8,117u8,112u8,108u8,105u8,99u8,97u8,116u8,101u8,32u8,97u8,116u8,116u8,114u8,105u8,98u8, - 117u8,116u8,101u8,115u8,32u8,40u8,107u8,101u8,121u8,115u8,41u8,46u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 24u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,83u8,69u8,81u8,85u8,69u8,78u8,67u8,69u8,95u8,78u8, - 85u8,77u8,66u8,69u8,82u8,94u8,84u8,104u8,101u8,32u8,115u8,101u8,113u8,117u8,101u8,110u8,99u8,101u8,32u8,110u8, - 117u8,109u8,98u8,101u8,114u8,32u8,112u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,105u8,115u8,32u8,105u8,110u8, - 118u8,97u8,108u8,105u8,100u8,46u8,32u8,73u8,116u8,32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,98u8,101u8, - 116u8,119u8,101u8,101u8,110u8,32u8,91u8,49u8,44u8,32u8,110u8,101u8,120u8,116u8,32u8,112u8,101u8,110u8,100u8,105u8, - 110u8,103u8,32u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,45u8,32u8,49u8,93u8,46u8, - 210u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,78u8,79u8, - 84u8,95u8,77u8,85u8,76u8,84u8,73u8,83u8,73u8,71u8,44u8,83u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8, - 32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,32u8,109u8,117u8, - 108u8,116u8,105u8,115u8,105u8,103u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,46u8,211u8,7u8,0u8,0u8,0u8, - 0u8,0u8,0u8,10u8,69u8,78u8,79u8,84u8,95u8,79u8,87u8,78u8,69u8,82u8,73u8,65u8,99u8,99u8,111u8,117u8, - 110u8,116u8,32u8,101u8,120u8,101u8,99u8,117u8,116u8,105u8,110u8,103u8,32u8,116u8,104u8,105u8,115u8,32u8,111u8,112u8, - 101u8,114u8,97u8,116u8,105u8,111u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,110u8,32u8,111u8,119u8, - 110u8,101u8,114u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,32u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,46u8,214u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,84u8,82u8, - 65u8,78u8,83u8,65u8,67u8,84u8,73u8,79u8,78u8,95u8,78u8,79u8,84u8,95u8,70u8,79u8,85u8,78u8,68u8,46u8, - 84u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,119u8,105u8,116u8,104u8,32u8,115u8,112u8,101u8, - 99u8,105u8,102u8,105u8,101u8,100u8,32u8,105u8,100u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8, - 102u8,111u8,117u8,110u8,100u8,46u8,216u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,28u8,69u8,80u8,65u8,89u8,76u8, - 79u8,65u8,68u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8,95u8,72u8, - 65u8,83u8,72u8,84u8,80u8,114u8,111u8,118u8,105u8,100u8,101u8,100u8,32u8,116u8,97u8,114u8,103u8,101u8,116u8,32u8, - 102u8,117u8,110u8,99u8,116u8,105u8,111u8,110u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,109u8,97u8, - 116u8,99u8,104u8,32u8,116u8,104u8,101u8,32u8,104u8,97u8,115u8,104u8,32u8,115u8,116u8,111u8,114u8,101u8,100u8,32u8, - 105u8,110u8,32u8,116u8,104u8,101u8,32u8,111u8,110u8,45u8,99u8,104u8,97u8,105u8,110u8,32u8,116u8,114u8,97u8,110u8, - 115u8,97u8,99u8,116u8,105u8,111u8,110u8,46u8,217u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,78u8,79u8, - 84u8,95u8,69u8,78u8,79u8,85u8,71u8,72u8,95u8,65u8,80u8,80u8,82u8,79u8,86u8,65u8,76u8,83u8,61u8,84u8, - 114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,32u8,104u8,97u8,115u8,32u8,110u8,111u8,116u8,32u8,114u8, - 101u8,99u8,101u8,105u8,118u8,101u8,100u8,32u8,101u8,110u8,111u8,117u8,103u8,104u8,32u8,97u8,112u8,112u8,114u8,111u8, - 118u8,97u8,108u8,115u8,32u8,116u8,111u8,32u8,98u8,101u8,32u8,101u8,120u8,101u8,99u8,117u8,116u8,101u8,100u8,46u8, - 0u8,12u8,4u8,118u8,111u8,116u8,101u8,1u8,1u8,0u8,6u8,111u8,119u8,110u8,101u8,114u8,115u8,1u8,1u8,0u8, - 8u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,1u8,1u8,0u8,15u8,99u8,97u8,110u8,95u8,98u8,101u8,95u8, - 101u8,120u8,101u8,99u8,117u8,116u8,101u8,100u8,1u8,1u8,0u8,15u8,99u8,97u8,110u8,95u8,98u8,101u8,95u8,114u8, - 101u8,106u8,101u8,99u8,116u8,101u8,100u8,1u8,1u8,0u8,15u8,103u8,101u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8, - 97u8,99u8,116u8,105u8,111u8,110u8,1u8,1u8,0u8,20u8,110u8,101u8,120u8,116u8,95u8,115u8,101u8,113u8,117u8,101u8, - 110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,1u8,1u8,0u8,23u8,110u8,117u8,109u8,95u8,115u8,105u8, - 103u8,110u8,97u8,116u8,117u8,114u8,101u8,115u8,95u8,114u8,101u8,113u8,117u8,105u8,114u8,101u8,100u8,1u8,1u8,0u8, - 24u8,103u8,101u8,116u8,95u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8, - 116u8,105u8,111u8,110u8,115u8,1u8,1u8,0u8,28u8,103u8,101u8,116u8,95u8,110u8,101u8,120u8,116u8,95u8,116u8,114u8, - 97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8,95u8,112u8,97u8,121u8,108u8,111u8,97u8,100u8,1u8,1u8,0u8, - 29u8,108u8,97u8,115u8,116u8,95u8,114u8,101u8,115u8,111u8,108u8,118u8,101u8,100u8,95u8,115u8,101u8,113u8,117u8,101u8, - 110u8,99u8,101u8,95u8,110u8,117u8,109u8,98u8,101u8,114u8,1u8,1u8,0u8,33u8,103u8,101u8,116u8,95u8,110u8,101u8, - 120u8,116u8,95u8,109u8,117u8,108u8,116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8, - 97u8,100u8,100u8,114u8,101u8,115u8,115u8,1u8,1u8,0u8,0u8,2u8,1u8,75u8,10u8,5u8,1u8,2u8,3u8,76u8, - 5u8,77u8,3u8,78u8,8u8,7u8,2u8,2u8,3u8,77u8,3u8,79u8,3u8,80u8,5u8,3u8,2u8,3u8,81u8,8u8, - 13u8,82u8,8u8,13u8,83u8,3u8,4u8,2u8,2u8,84u8,11u8,16u8,2u8,8u8,13u8,10u8,2u8,85u8,11u8,16u8, - 2u8,8u8,13u8,10u8,2u8,5u8,2u8,16u8,62u8,10u8,5u8,61u8,3u8,86u8,11u8,17u8,2u8,3u8,8u8,7u8, - 88u8,3u8,59u8,3u8,89u8,11u8,15u8,1u8,8u8,14u8,58u8,11u8,16u8,2u8,8u8,13u8,10u8,2u8,90u8,11u8, - 18u8,1u8,8u8,0u8,92u8,11u8,18u8,1u8,8u8,8u8,93u8,11u8,18u8,1u8,8u8,11u8,94u8,11u8,18u8,1u8, - 8u8,1u8,95u8,11u8,18u8,1u8,8u8,12u8,96u8,11u8,18u8,1u8,8u8,2u8,97u8,11u8,18u8,1u8,8u8,10u8, - 98u8,11u8,18u8,1u8,8u8,9u8,99u8,11u8,18u8,1u8,8u8,4u8,6u8,2u8,5u8,4u8,2u8,100u8,5u8,77u8, - 3u8,62u8,10u8,5u8,61u8,3u8,7u8,2u8,5u8,101u8,11u8,15u8,1u8,10u8,2u8,102u8,11u8,15u8,1u8,10u8, - 2u8,103u8,11u8,16u8,2u8,5u8,1u8,76u8,5u8,104u8,3u8,8u8,2u8,1u8,105u8,10u8,5u8,9u8,2u8,5u8, - 80u8,5u8,77u8,3u8,106u8,10u8,2u8,107u8,3u8,108u8,8u8,3u8,10u8,2u8,4u8,80u8,5u8,77u8,3u8,106u8, - 10u8,2u8,107u8,3u8,11u8,2u8,2u8,109u8,3u8,110u8,3u8,12u8,2u8,3u8,111u8,5u8,77u8,3u8,112u8,1u8, - 0u8,0u8,4u8,1u8,5u8,1u8,5u8,11u8,0u8,11u8,1u8,64u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,17u8,1u8,2u8,1u8,0u8,4u8,1u8,5u8,35u8,30u8,14u8,1u8,65u8,6u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,33u8,4u8,8u8,11u8,0u8,1u8,2u8,11u8,0u8,17u8,40u8,12u8,3u8,10u8,3u8,17u8, - 5u8,10u8,3u8,42u8,5u8,12u8,2u8,10u8,2u8,15u8,0u8,10u8,1u8,56u8,0u8,10u8,2u8,16u8,0u8,11u8, - 3u8,17u8,37u8,11u8,2u8,15u8,1u8,11u8,1u8,18u8,0u8,56u8,1u8,2u8,2u8,0u8,0u8,0u8,21u8,28u8, - 13u8,2u8,15u8,2u8,10u8,0u8,8u8,56u8,2u8,10u8,1u8,16u8,3u8,20u8,12u8,3u8,10u8,3u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,10u8,1u8,15u8,3u8,21u8,10u8,1u8,15u8,4u8,10u8,3u8,10u8, - 2u8,56u8,3u8,11u8,1u8,15u8,5u8,11u8,0u8,11u8,3u8,11u8,2u8,18u8,1u8,56u8,4u8,2u8,3u8,1u8, - 4u8,1u8,5u8,1u8,6u8,11u8,0u8,11u8,1u8,11u8,2u8,8u8,17u8,39u8,2u8,4u8,0u8,0u8,0u8,44u8, - 15u8,11u8,1u8,16u8,0u8,12u8,3u8,11u8,0u8,17u8,40u8,12u8,2u8,11u8,3u8,14u8,2u8,56u8,5u8,4u8, - 11u8,5u8,14u8,7u8,11u8,17u8,46u8,39u8,2u8,5u8,0u8,0u8,0u8,1u8,8u8,11u8,0u8,41u8,5u8,4u8, - 4u8,5u8,7u8,7u8,1u8,17u8,47u8,39u8,2u8,6u8,1u8,0u8,1u8,5u8,46u8,56u8,11u8,0u8,43u8,5u8, - 12u8,4u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,14u8,10u8,1u8,10u8,4u8, - 16u8,3u8,20u8,35u8,12u8,2u8,5u8,16u8,9u8,12u8,2u8,11u8,2u8,4u8,19u8,5u8,24u8,11u8,4u8,1u8, - 7u8,5u8,17u8,48u8,39u8,10u8,4u8,16u8,4u8,10u8,1u8,56u8,6u8,12u8,6u8,10u8,4u8,16u8,0u8,11u8, - 6u8,17u8,25u8,1u8,12u8,5u8,11u8,1u8,10u8,4u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,33u8,4u8,50u8,11u8,5u8,11u8,4u8,16u8,7u8,20u8,38u8,12u8,3u8,5u8,54u8,11u8,4u8, - 1u8,9u8,12u8,3u8,11u8,3u8,2u8,7u8,1u8,0u8,1u8,5u8,46u8,56u8,11u8,0u8,43u8,5u8,12u8,4u8, - 10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,14u8,10u8,1u8,10u8,4u8,16u8,3u8, - 20u8,35u8,12u8,2u8,5u8,16u8,9u8,12u8,2u8,11u8,2u8,4u8,19u8,5u8,24u8,11u8,4u8,1u8,7u8,5u8, - 17u8,48u8,39u8,10u8,4u8,16u8,4u8,10u8,1u8,56u8,6u8,12u8,6u8,10u8,4u8,16u8,0u8,11u8,6u8,17u8, - 25u8,12u8,5u8,1u8,11u8,1u8,10u8,4u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,33u8,4u8,50u8,11u8,5u8,11u8,4u8,16u8,7u8,20u8,38u8,12u8,3u8,5u8,54u8,11u8,4u8,1u8,9u8, - 12u8,3u8,11u8,3u8,2u8,8u8,1u8,4u8,1u8,5u8,1u8,7u8,11u8,0u8,7u8,17u8,11u8,1u8,11u8,2u8, - 11u8,3u8,17u8,14u8,2u8,9u8,0u8,0u8,0u8,49u8,21u8,10u8,0u8,17u8,40u8,17u8,50u8,12u8,3u8,11u8, - 0u8,14u8,3u8,56u8,7u8,17u8,10u8,17u8,52u8,12u8,2u8,12u8,1u8,14u8,1u8,17u8,40u8,56u8,8u8,32u8, - 4u8,18u8,14u8,1u8,56u8,9u8,11u8,1u8,11u8,2u8,2u8,10u8,0u8,0u8,0u8,12u8,10u8,64u8,53u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,1u8,13u8,1u8,7u8,0u8,56u8,10u8,13u8,1u8,11u8,0u8,56u8, - 10u8,11u8,1u8,2u8,11u8,1u8,4u8,1u8,5u8,3u8,36u8,14u8,2u8,65u8,53u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,36u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,14u8,17u8,48u8,39u8,10u8,1u8,17u8, - 5u8,11u8,1u8,42u8,5u8,12u8,4u8,10u8,0u8,10u8,4u8,46u8,17u8,4u8,11u8,0u8,17u8,40u8,12u8,3u8, - 11u8,2u8,56u8,11u8,56u8,12u8,56u8,13u8,10u8,3u8,17u8,58u8,18u8,7u8,12u8,5u8,11u8,3u8,11u8,4u8, - 11u8,5u8,17u8,2u8,2u8,12u8,1u8,4u8,1u8,5u8,3u8,36u8,14u8,2u8,65u8,53u8,6u8,32u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,33u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,4u8,17u8,48u8,39u8,10u8,1u8, - 17u8,5u8,11u8,1u8,42u8,5u8,12u8,4u8,10u8,0u8,10u8,4u8,46u8,17u8,4u8,11u8,0u8,17u8,40u8,12u8, - 3u8,56u8,12u8,11u8,2u8,56u8,11u8,56u8,13u8,10u8,3u8,17u8,58u8,18u8,7u8,12u8,5u8,11u8,3u8,11u8, - 4u8,11u8,5u8,17u8,2u8,2u8,13u8,1u8,4u8,1u8,5u8,57u8,25u8,17u8,59u8,10u8,0u8,10u8,0u8,17u8, - 50u8,10u8,1u8,10u8,2u8,18u8,6u8,12u8,9u8,10u8,0u8,11u8,3u8,11u8,4u8,11u8,5u8,11u8,9u8,56u8, - 14u8,11u8,0u8,17u8,61u8,12u8,8u8,14u8,8u8,11u8,1u8,11u8,2u8,56u8,15u8,11u8,6u8,11u8,7u8,17u8, - 15u8,2u8,14u8,1u8,4u8,1u8,5u8,11u8,17u8,10u8,0u8,17u8,9u8,12u8,6u8,12u8,5u8,13u8,1u8,11u8, - 0u8,17u8,40u8,68u8,6u8,14u8,5u8,11u8,1u8,11u8,2u8,11u8,6u8,56u8,16u8,11u8,3u8,11u8,4u8,17u8, - 15u8,2u8,15u8,0u8,0u8,1u8,5u8,62u8,98u8,17u8,62u8,4u8,3u8,5u8,8u8,11u8,0u8,1u8,7u8,7u8, - 17u8,63u8,39u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,18u8,10u8,2u8,14u8, - 1u8,65u8,6u8,37u8,12u8,6u8,5u8,20u8,9u8,12u8,6u8,11u8,6u8,4u8,23u8,5u8,28u8,11u8,0u8,1u8, - 7u8,6u8,17u8,48u8,39u8,10u8,0u8,17u8,40u8,12u8,22u8,14u8,1u8,11u8,22u8,17u8,37u8,10u8,0u8,12u8, - 15u8,11u8,1u8,12u8,16u8,11u8,2u8,12u8,17u8,56u8,17u8,12u8,18u8,56u8,18u8,12u8,19u8,11u8,3u8,12u8, - 20u8,10u8,0u8,56u8,19u8,12u8,21u8,10u8,0u8,56u8,20u8,12u8,7u8,10u8,0u8,56u8,21u8,12u8,8u8,10u8, - 0u8,56u8,22u8,12u8,9u8,10u8,0u8,56u8,23u8,12u8,10u8,10u8,0u8,56u8,24u8,12u8,11u8,10u8,0u8,56u8, - 25u8,12u8,12u8,10u8,0u8,56u8,26u8,12u8,13u8,10u8,0u8,56u8,27u8,12u8,14u8,11u8,15u8,11u8,16u8,11u8, - 17u8,11u8,18u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,11u8,20u8,11u8,19u8,11u8,21u8,11u8,7u8,11u8,8u8,11u8,9u8,11u8,10u8,11u8,11u8,11u8,12u8,11u8, - 13u8,11u8,14u8,18u8,5u8,45u8,5u8,11u8,0u8,11u8,4u8,11u8,5u8,9u8,17u8,34u8,2u8,16u8,1u8,4u8, - 1u8,5u8,73u8,55u8,10u8,1u8,17u8,5u8,11u8,1u8,42u8,5u8,12u8,2u8,10u8,0u8,10u8,2u8,46u8,17u8, - 4u8,10u8,2u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,10u8,2u8, - 16u8,4u8,10u8,4u8,56u8,28u8,4u8,21u8,5u8,28u8,11u8,0u8,1u8,11u8,2u8,1u8,7u8,16u8,17u8,67u8, - 39u8,10u8,2u8,17u8,29u8,12u8,3u8,1u8,10u8,3u8,10u8,2u8,16u8,7u8,20u8,38u8,4u8,39u8,5u8,46u8, - 11u8,0u8,1u8,11u8,2u8,1u8,7u8,10u8,17u8,47u8,39u8,11u8,2u8,15u8,8u8,11u8,4u8,11u8,3u8,11u8, - 0u8,17u8,40u8,18u8,2u8,56u8,29u8,2u8,17u8,0u8,0u8,1u8,5u8,74u8,19u8,11u8,1u8,42u8,5u8,12u8, - 4u8,10u8,4u8,17u8,29u8,1u8,12u8,5u8,10u8,4u8,15u8,9u8,11u8,0u8,11u8,4u8,16u8,6u8,20u8,11u8, - 2u8,11u8,5u8,11u8,3u8,18u8,9u8,56u8,30u8,2u8,18u8,1u8,0u8,0u8,21u8,9u8,10u8,0u8,17u8,50u8, - 12u8,1u8,14u8,0u8,14u8,1u8,56u8,7u8,17u8,10u8,17u8,68u8,2u8,19u8,1u8,0u8,1u8,5u8,76u8,30u8, - 11u8,0u8,43u8,5u8,12u8,3u8,10u8,3u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,12u8,4u8,11u8,3u8,16u8,4u8,11u8,4u8,56u8,6u8,12u8,5u8,10u8,5u8,16u8,10u8,56u8,31u8,4u8, - 24u8,11u8,5u8,16u8,10u8,56u8,32u8,20u8,12u8,2u8,5u8,28u8,11u8,5u8,1u8,11u8,1u8,12u8,2u8,11u8, - 2u8,2u8,20u8,1u8,0u8,1u8,5u8,78u8,36u8,64u8,20u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8, - 4u8,11u8,0u8,43u8,5u8,12u8,2u8,10u8,2u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,22u8,12u8,1u8,10u8,2u8,16u8,3u8,20u8,12u8,3u8,10u8,1u8,10u8,3u8,35u8,4u8,32u8,5u8,20u8, - 13u8,4u8,10u8,2u8,16u8,4u8,10u8,1u8,56u8,6u8,20u8,68u8,20u8,11u8,1u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,22u8,12u8,1u8,5u8,15u8,11u8,2u8,1u8,11u8,4u8,2u8,21u8,1u8,0u8,1u8,5u8, - 79u8,30u8,11u8,0u8,43u8,5u8,12u8,3u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8, - 4u8,14u8,10u8,1u8,10u8,3u8,16u8,3u8,20u8,35u8,12u8,2u8,5u8,16u8,9u8,12u8,2u8,11u8,2u8,4u8, - 19u8,5u8,24u8,11u8,3u8,1u8,7u8,5u8,17u8,48u8,39u8,11u8,3u8,16u8,4u8,11u8,1u8,56u8,6u8,20u8, - 2u8,22u8,1u8,0u8,1u8,5u8,1u8,5u8,11u8,0u8,42u8,5u8,16u8,6u8,20u8,2u8,23u8,1u8,0u8,1u8, - 5u8,1u8,5u8,11u8,0u8,43u8,5u8,16u8,11u8,20u8,2u8,24u8,1u8,0u8,1u8,5u8,1u8,5u8,11u8,0u8, - 42u8,5u8,16u8,3u8,20u8,2u8,25u8,0u8,0u8,0u8,80u8,56u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,12u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,11u8,1u8,16u8,2u8,12u8,8u8, - 11u8,0u8,12u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8,7u8,65u8,6u8,12u8, - 3u8,10u8,2u8,10u8,3u8,35u8,4u8,49u8,5u8,19u8,10u8,7u8,10u8,2u8,66u8,6u8,12u8,6u8,10u8,8u8, - 10u8,6u8,56u8,33u8,4u8,42u8,10u8,8u8,11u8,6u8,56u8,34u8,20u8,4u8,37u8,11u8,4u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,4u8,5u8,41u8,11u8,5u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,12u8,5u8,5u8,44u8,11u8,6u8,1u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,22u8,12u8,2u8,5u8,14u8,11u8,8u8,1u8,11u8,7u8,1u8,11u8,4u8,11u8,5u8,2u8,26u8,1u8,0u8, - 1u8,5u8,1u8,5u8,11u8,0u8,43u8,5u8,16u8,7u8,20u8,2u8,27u8,1u8,0u8,1u8,5u8,1u8,5u8,11u8, - 0u8,43u8,5u8,16u8,0u8,20u8,2u8,28u8,1u8,4u8,1u8,5u8,1u8,6u8,11u8,0u8,11u8,1u8,11u8,2u8, - 9u8,17u8,39u8,2u8,29u8,0u8,0u8,0u8,41u8,20u8,10u8,0u8,16u8,6u8,20u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,22u8,12u8,1u8,10u8,0u8,15u8,4u8,10u8,1u8,56u8,35u8,12u8,2u8,11u8,1u8,10u8, - 0u8,15u8,6u8,21u8,11u8,0u8,16u8,0u8,14u8,2u8,17u8,25u8,2u8,30u8,0u8,4u8,1u8,5u8,1u8,5u8, - 11u8,0u8,11u8,1u8,64u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,31u8,2u8,31u8,0u8,4u8, - 1u8,5u8,84u8,80u8,14u8,1u8,65u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,8u8, - 11u8,0u8,1u8,2u8,11u8,0u8,17u8,40u8,12u8,7u8,10u8,7u8,17u8,5u8,11u8,7u8,42u8,5u8,12u8,6u8, - 10u8,6u8,15u8,0u8,12u8,9u8,64u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,10u8,14u8,1u8, - 12u8,11u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,11u8,65u8,6u8,12u8,5u8,10u8, - 3u8,10u8,5u8,35u8,4u8,58u8,5u8,33u8,10u8,11u8,10u8,3u8,66u8,6u8,20u8,12u8,8u8,10u8,9u8,14u8, - 8u8,12u8,2u8,46u8,11u8,2u8,56u8,36u8,12u8,4u8,4u8,53u8,13u8,10u8,11u8,8u8,68u8,6u8,10u8,9u8, - 11u8,4u8,56u8,37u8,1u8,11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8, - 28u8,11u8,11u8,1u8,11u8,9u8,46u8,65u8,6u8,10u8,6u8,16u8,7u8,20u8,38u8,4u8,69u8,5u8,74u8,11u8, - 6u8,1u8,7u8,9u8,17u8,47u8,39u8,11u8,6u8,15u8,12u8,11u8,10u8,18u8,8u8,56u8,38u8,2u8,32u8,0u8, - 0u8,1u8,5u8,87u8,28u8,11u8,1u8,42u8,5u8,12u8,8u8,10u8,8u8,17u8,29u8,1u8,12u8,9u8,10u8,8u8, - 15u8,13u8,12u8,7u8,11u8,8u8,16u8,6u8,20u8,12u8,3u8,11u8,2u8,12u8,4u8,11u8,9u8,12u8,5u8,11u8, - 0u8,12u8,6u8,11u8,7u8,11u8,6u8,11u8,3u8,11u8,4u8,11u8,5u8,18u8,10u8,56u8,39u8,2u8,33u8,0u8, - 4u8,1u8,5u8,1u8,6u8,11u8,0u8,11u8,1u8,11u8,2u8,8u8,17u8,34u8,2u8,34u8,0u8,0u8,1u8,5u8, - 88u8,91u8,14u8,1u8,65u8,89u8,12u8,10u8,10u8,10u8,14u8,2u8,65u8,12u8,33u8,4u8,9u8,5u8,14u8,11u8, - 0u8,1u8,7u8,12u8,17u8,48u8,39u8,11u8,0u8,17u8,40u8,12u8,9u8,10u8,9u8,17u8,5u8,11u8,9u8,42u8, - 5u8,12u8,8u8,10u8,8u8,16u8,11u8,20u8,12u8,11u8,56u8,18u8,10u8,8u8,15u8,11u8,21u8,10u8,8u8,15u8, - 11u8,12u8,7u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,5u8,10u8,10u8,35u8,4u8, - 75u8,5u8,40u8,14u8,1u8,10u8,5u8,66u8,89u8,20u8,12u8,6u8,14u8,2u8,10u8,5u8,66u8,12u8,20u8,12u8, - 12u8,10u8,7u8,14u8,6u8,12u8,4u8,46u8,11u8,4u8,56u8,40u8,32u8,4u8,59u8,5u8,66u8,11u8,8u8,1u8, - 11u8,7u8,1u8,7u8,2u8,17u8,48u8,39u8,10u8,7u8,11u8,6u8,11u8,12u8,56u8,41u8,11u8,5u8,6u8,1u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,35u8,11u8,7u8,1u8,11u8,3u8,4u8,88u8,10u8, - 8u8,15u8,14u8,11u8,11u8,11u8,8u8,16u8,11u8,20u8,18u8,4u8,56u8,42u8,5u8,90u8,11u8,8u8,1u8,2u8, - 35u8,0u8,4u8,1u8,5u8,90u8,55u8,11u8,0u8,17u8,40u8,12u8,4u8,10u8,4u8,17u8,5u8,11u8,4u8,42u8, - 5u8,12u8,3u8,10u8,3u8,16u8,7u8,20u8,10u8,1u8,33u8,4u8,17u8,11u8,3u8,1u8,2u8,10u8,3u8,16u8, - 0u8,65u8,6u8,12u8,5u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,30u8,10u8, - 1u8,11u8,5u8,37u8,12u8,2u8,5u8,32u8,9u8,12u8,2u8,11u8,2u8,4u8,35u8,5u8,40u8,11u8,3u8,1u8, - 7u8,6u8,17u8,48u8,39u8,10u8,3u8,16u8,7u8,20u8,12u8,6u8,10u8,1u8,10u8,3u8,15u8,7u8,21u8,11u8, - 3u8,15u8,15u8,11u8,6u8,11u8,1u8,18u8,11u8,56u8,43u8,2u8,36u8,0u8,0u8,1u8,5u8,91u8,68u8,10u8, - 1u8,17u8,5u8,11u8,1u8,43u8,5u8,12u8,3u8,11u8,0u8,10u8,3u8,17u8,4u8,10u8,3u8,16u8,6u8,20u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,10u8,3u8,16u8,4u8,10u8,5u8,56u8,28u8, - 4u8,20u8,5u8,25u8,11u8,3u8,1u8,7u8,16u8,17u8,48u8,39u8,10u8,3u8,16u8,4u8,11u8,5u8,56u8,6u8, - 12u8,6u8,10u8,3u8,16u8,0u8,10u8,6u8,17u8,25u8,1u8,11u8,3u8,16u8,7u8,20u8,38u8,4u8,41u8,5u8, - 46u8,11u8,6u8,1u8,7u8,8u8,17u8,48u8,39u8,10u8,6u8,16u8,16u8,56u8,31u8,4u8,65u8,11u8,6u8,16u8, - 16u8,56u8,32u8,12u8,4u8,11u8,2u8,17u8,76u8,11u8,4u8,20u8,33u8,4u8,61u8,5u8,64u8,7u8,15u8,17u8, - 48u8,39u8,5u8,67u8,11u8,6u8,1u8,2u8,37u8,0u8,0u8,0u8,92u8,52u8,7u8,17u8,12u8,2u8,11u8,0u8, - 12u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,6u8,65u8,6u8,12u8,4u8,10u8, - 3u8,10u8,4u8,35u8,4u8,49u8,5u8,14u8,10u8,6u8,10u8,3u8,66u8,6u8,20u8,12u8,5u8,10u8,5u8,10u8, - 1u8,34u8,4u8,24u8,5u8,29u8,11u8,6u8,1u8,7u8,13u8,17u8,48u8,39u8,14u8,2u8,14u8,5u8,56u8,36u8, - 1u8,32u8,4u8,36u8,5u8,41u8,11u8,6u8,1u8,7u8,3u8,17u8,48u8,39u8,13u8,2u8,11u8,5u8,68u8,6u8, - 11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,3u8,5u8,9u8,11u8,6u8,1u8,2u8, - 38u8,1u8,0u8,1u8,5u8,93u8,51u8,11u8,0u8,42u8,5u8,12u8,5u8,10u8,1u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,36u8,4u8,14u8,10u8,1u8,10u8,5u8,16u8,3u8,20u8,35u8,12u8,3u8,5u8,16u8,9u8, - 12u8,3u8,11u8,3u8,4u8,19u8,5u8,24u8,11u8,5u8,1u8,7u8,5u8,17u8,48u8,39u8,11u8,5u8,16u8,4u8, - 11u8,1u8,56u8,6u8,16u8,2u8,12u8,8u8,10u8,8u8,14u8,2u8,56u8,33u8,12u8,7u8,10u8,7u8,4u8,42u8, - 11u8,8u8,14u8,2u8,56u8,34u8,20u8,12u8,4u8,5u8,46u8,11u8,8u8,1u8,9u8,12u8,4u8,11u8,4u8,12u8, - 6u8,11u8,7u8,11u8,6u8,2u8,39u8,1u8,4u8,1u8,5u8,94u8,56u8,10u8,1u8,17u8,5u8,11u8,1u8,42u8, - 5u8,12u8,5u8,10u8,0u8,10u8,5u8,46u8,17u8,4u8,10u8,5u8,16u8,4u8,10u8,2u8,56u8,28u8,4u8,15u8, - 5u8,22u8,11u8,0u8,1u8,11u8,5u8,1u8,7u8,16u8,17u8,67u8,39u8,10u8,5u8,15u8,4u8,10u8,2u8,56u8, - 44u8,15u8,2u8,12u8,7u8,11u8,0u8,17u8,40u8,12u8,6u8,10u8,7u8,14u8,6u8,12u8,4u8,46u8,11u8,4u8, - 56u8,33u8,4u8,44u8,10u8,3u8,11u8,7u8,14u8,6u8,56u8,45u8,21u8,5u8,48u8,11u8,7u8,10u8,6u8,10u8, - 3u8,56u8,2u8,11u8,5u8,15u8,17u8,11u8,6u8,11u8,2u8,11u8,3u8,18u8,12u8,56u8,46u8,2u8,5u8,0u8, - 5u8,7u8,7u8,2u8,5u8,4u8,5u8,2u8,5u8,10u8,5u8,3u8,5u8,1u8,5u8,12u8,5u8,14u8,7u8,0u8, - 5u8,6u8,5u8,8u8,5u8,13u8,5u8,15u8,5u8,9u8,7u8,1u8,5u8,11u8,0u8, - ]; - vector::push_back(&mut code, chunk36); - let chunk37 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,14u8,1u8,0u8,22u8,2u8,22u8,48u8,3u8,70u8,139u8,2u8, - 4u8,209u8,2u8,26u8,5u8,235u8,2u8,199u8,1u8,7u8,178u8,4u8,225u8,8u8,8u8,147u8,13u8,32u8,6u8,179u8, - 13u8,89u8,16u8,140u8,14u8,212u8,4u8,10u8,224u8,18u8,63u8,11u8,159u8,19u8,4u8,12u8,163u8,19u8,228u8,7u8, - 13u8,135u8,27u8,24u8,14u8,159u8,27u8,4u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8, - 0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,0u8,10u8,0u8,11u8,2u8,0u8,0u8,12u8,6u8,0u8,0u8,13u8, - 6u8,0u8,0u8,14u8,2u8,0u8,0u8,15u8,7u8,1u8,0u8,1u8,0u8,16u8,8u8,0u8,0u8,17u8,0u8,0u8, - 0u8,18u8,6u8,0u8,0u8,19u8,6u8,0u8,7u8,25u8,6u8,0u8,5u8,44u8,4u8,1u8,6u8,1u8,0u8,20u8, - 0u8,1u8,0u8,0u8,21u8,2u8,1u8,0u8,0u8,22u8,3u8,1u8,0u8,0u8,23u8,1u8,4u8,1u8,8u8,0u8, - 24u8,0u8,5u8,0u8,0u8,26u8,6u8,7u8,0u8,0u8,27u8,8u8,9u8,0u8,0u8,28u8,10u8,1u8,0u8,0u8, - 29u8,6u8,9u8,0u8,0u8,30u8,11u8,9u8,0u8,0u8,31u8,6u8,9u8,0u8,0u8,32u8,12u8,9u8,0u8,0u8, - 33u8,13u8,14u8,0u8,0u8,34u8,15u8,14u8,0u8,0u8,35u8,15u8,14u8,0u8,0u8,36u8,1u8,5u8,1u8,8u8, - 0u8,37u8,0u8,13u8,0u8,0u8,38u8,0u8,16u8,0u8,0u8,39u8,15u8,17u8,0u8,0u8,40u8,0u8,18u8,0u8, - 0u8,41u8,3u8,18u8,0u8,0u8,42u8,0u8,19u8,0u8,0u8,43u8,20u8,5u8,1u8,8u8,0u8,45u8,6u8,21u8, - 1u8,6u8,0u8,46u8,22u8,1u8,1u8,8u8,0u8,47u8,0u8,4u8,1u8,8u8,0u8,48u8,2u8,4u8,1u8,8u8, - 0u8,49u8,4u8,1u8,1u8,8u8,0u8,50u8,20u8,5u8,1u8,8u8,0u8,51u8,23u8,14u8,1u8,8u8,0u8,52u8, - 24u8,14u8,0u8,0u8,53u8,24u8,14u8,0u8,0u8,54u8,25u8,14u8,2u8,8u8,8u8,0u8,55u8,26u8,14u8,0u8, - 0u8,56u8,27u8,14u8,0u8,4u8,66u8,28u8,28u8,0u8,9u8,67u8,6u8,1u8,0u8,7u8,68u8,31u8,7u8,0u8, - 2u8,69u8,33u8,32u8,1u8,0u8,10u8,70u8,35u8,14u8,1u8,0u8,8u8,71u8,32u8,32u8,0u8,6u8,72u8,32u8, - 1u8,0u8,1u8,26u8,6u8,7u8,0u8,4u8,73u8,28u8,28u8,0u8,3u8,3u8,1u8,18u8,0u8,5u8,45u8,7u8, - 21u8,1u8,6u8,5u8,74u8,21u8,14u8,1u8,6u8,4u8,75u8,28u8,28u8,0u8,4u8,76u8,28u8,28u8,0u8,5u8, - 77u8,42u8,14u8,1u8,6u8,15u8,29u8,38u8,1u8,39u8,34u8,38u8,7u8,45u8,38u8,46u8,38u8,27u8,41u8,27u8, - 29u8,45u8,29u8,3u8,29u8,24u8,29u8,49u8,38u8,29u8,29u8,1u8,6u8,8u8,0u8,1u8,5u8,1u8,6u8,8u8, - 1u8,1u8,6u8,8u8,2u8,1u8,11u8,4u8,1u8,9u8,0u8,1u8,1u8,1u8,6u8,12u8,1u8,8u8,9u8,2u8, - 6u8,12u8,10u8,2u8,1u8,8u8,0u8,2u8,6u8,5u8,10u8,2u8,2u8,5u8,8u8,9u8,3u8,5u8,5u8,1u8, - 1u8,8u8,1u8,0u8,1u8,6u8,8u8,8u8,1u8,8u8,2u8,1u8,8u8,3u8,1u8,12u8,1u8,8u8,8u8,2u8, - 11u8,4u8,1u8,9u8,0u8,5u8,1u8,11u8,10u8,1u8,9u8,0u8,1u8,6u8,11u8,4u8,1u8,9u8,0u8,3u8, - 6u8,12u8,11u8,4u8,1u8,9u8,0u8,5u8,3u8,6u8,12u8,5u8,5u8,3u8,6u8,12u8,11u8,4u8,1u8,9u8, - 0u8,11u8,4u8,1u8,9u8,1u8,2u8,8u8,3u8,5u8,2u8,5u8,5u8,1u8,3u8,1u8,9u8,0u8,2u8,5u8, - 7u8,8u8,5u8,2u8,5u8,7u8,3u8,1u8,10u8,2u8,1u8,6u8,9u8,0u8,1u8,2u8,2u8,7u8,10u8,9u8, - 0u8,10u8,9u8,0u8,2u8,10u8,2u8,5u8,3u8,3u8,12u8,8u8,9u8,1u8,8u8,7u8,1u8,11u8,10u8,1u8, - 8u8,7u8,1u8,7u8,8u8,5u8,1u8,8u8,5u8,2u8,7u8,11u8,10u8,1u8,9u8,0u8,9u8,0u8,1u8,9u8, - 1u8,4u8,5u8,5u8,6u8,8u8,5u8,6u8,8u8,5u8,6u8,111u8,98u8,106u8,101u8,99u8,116u8,7u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,3u8,98u8,99u8,115u8,13u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8, - 110u8,101u8,114u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,8u8,102u8,114u8,111u8,109u8, - 95u8,98u8,99u8,115u8,4u8,103u8,117u8,105u8,100u8,4u8,104u8,97u8,115u8,104u8,6u8,115u8,105u8,103u8,110u8,101u8, - 114u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8,14u8,67u8,111u8,110u8,115u8,116u8,114u8,117u8,99u8,116u8,111u8,114u8, - 82u8,101u8,102u8,9u8,68u8,101u8,108u8,101u8,116u8,101u8,82u8,101u8,102u8,9u8,69u8,120u8,116u8,101u8,110u8,100u8, - 82u8,101u8,102u8,17u8,76u8,105u8,110u8,101u8,97u8,114u8,84u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,82u8,101u8, - 102u8,6u8,79u8,98u8,106u8,101u8,99u8,116u8,10u8,79u8,98u8,106u8,101u8,99u8,116u8,67u8,111u8,114u8,101u8,11u8, - 79u8,98u8,106u8,101u8,99u8,116u8,71u8,114u8,111u8,117u8,112u8,13u8,84u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8, - 69u8,118u8,101u8,110u8,116u8,11u8,84u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,82u8,101u8,102u8,28u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,95u8,102u8,114u8,111u8,109u8,95u8,99u8,111u8,110u8,115u8,116u8,114u8,117u8,99u8,116u8, - 111u8,114u8,95u8,114u8,101u8,102u8,23u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,102u8,114u8,111u8,109u8,95u8, - 100u8,101u8,108u8,101u8,116u8,101u8,95u8,114u8,101u8,102u8,23u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,102u8, - 114u8,111u8,109u8,95u8,101u8,120u8,116u8,101u8,110u8,100u8,95u8,114u8,101u8,102u8,17u8,97u8,100u8,100u8,114u8,101u8, - 115u8,115u8,95u8,116u8,111u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,23u8,99u8,97u8,110u8,95u8,103u8,101u8,110u8, - 101u8,114u8,97u8,116u8,101u8,95u8,100u8,101u8,108u8,101u8,116u8,101u8,95u8,114u8,101u8,102u8,4u8,71u8,85u8,73u8, - 68u8,11u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,103u8,117u8,105u8,100u8,19u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,110u8,97u8,109u8,101u8,100u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,21u8,99u8,114u8,101u8,97u8,116u8,101u8, - 95u8,111u8,98u8,106u8,101u8,99u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,26u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,95u8,102u8,114u8,111u8,109u8,95u8,97u8,99u8,99u8,111u8,117u8, - 110u8,116u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,95u8,102u8,114u8,111u8, - 109u8,95u8,103u8,117u8,105u8,100u8,25u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8, - 95u8,102u8,114u8,111u8,109u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,22u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8, - 111u8,98u8,106u8,101u8,99u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,6u8,100u8,101u8,108u8,101u8, - 116u8,101u8,24u8,100u8,105u8,115u8,97u8,98u8,108u8,101u8,95u8,117u8,110u8,103u8,97u8,116u8,101u8,100u8,95u8,116u8, - 114u8,97u8,110u8,115u8,102u8,101u8,114u8,23u8,101u8,110u8,97u8,98u8,108u8,101u8,95u8,117u8,110u8,103u8,97u8,116u8, - 101u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,9u8,101u8,120u8,105u8,115u8,116u8,115u8,95u8,97u8, - 116u8,19u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8,100u8,101u8,108u8,101u8,116u8,101u8,95u8,114u8,101u8, - 102u8,19u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8,101u8,120u8,116u8,101u8,110u8,100u8,95u8,114u8,101u8, - 102u8,28u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8,108u8,105u8,110u8,101u8,97u8,114u8,95u8,116u8,114u8, - 97u8,110u8,115u8,102u8,101u8,114u8,95u8,114u8,101u8,102u8,15u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8, - 115u8,105u8,103u8,110u8,101u8,114u8,29u8,103u8,101u8,110u8,101u8,114u8,97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8, - 101u8,114u8,95u8,102u8,111u8,114u8,95u8,101u8,120u8,116u8,101u8,110u8,100u8,105u8,110u8,103u8,21u8,103u8,101u8,110u8, - 101u8,114u8,97u8,116u8,101u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,114u8,101u8,102u8,8u8,105u8, - 115u8,95u8,111u8,119u8,110u8,101u8,114u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,16u8, - 110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,14u8,111u8,98u8,106u8, - 101u8,99u8,116u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,27u8,111u8,98u8,106u8,101u8,99u8,116u8,95u8,102u8, - 114u8,111u8,109u8,95u8,99u8,111u8,110u8,115u8,116u8,114u8,117u8,99u8,116u8,111u8,114u8,95u8,114u8,101u8,102u8,22u8, - 111u8,98u8,106u8,101u8,99u8,116u8,95u8,102u8,114u8,111u8,109u8,95u8,100u8,101u8,108u8,101u8,116u8,101u8,95u8,114u8, - 101u8,102u8,5u8,111u8,119u8,110u8,101u8,114u8,4u8,111u8,119u8,110u8,115u8,8u8,116u8,114u8,97u8,110u8,115u8,102u8, - 101u8,114u8,13u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,99u8,97u8,108u8,108u8,12u8,116u8,114u8,97u8, - 110u8,115u8,102u8,101u8,114u8,95u8,114u8,97u8,119u8,18u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,116u8, - 111u8,95u8,111u8,98u8,106u8,101u8,99u8,116u8,17u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,119u8,105u8, - 116u8,104u8,95u8,114u8,101u8,102u8,29u8,118u8,101u8,114u8,105u8,102u8,121u8,95u8,117u8,110u8,103u8,97u8,116u8,101u8, - 100u8,95u8,97u8,110u8,100u8,95u8,100u8,101u8,115u8,99u8,101u8,110u8,100u8,97u8,110u8,116u8,4u8,115u8,101u8,108u8, - 102u8,10u8,99u8,97u8,110u8,95u8,100u8,101u8,108u8,101u8,116u8,101u8,5u8,105u8,110u8,110u8,101u8,114u8,17u8,103u8, - 117u8,105u8,100u8,95u8,99u8,114u8,101u8,97u8,116u8,105u8,111u8,110u8,95u8,110u8,117u8,109u8,22u8,97u8,108u8,108u8, - 111u8,119u8,95u8,117u8,110u8,103u8,97u8,116u8,101u8,100u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,15u8, - 116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,100u8,117u8,109u8,109u8, - 121u8,95u8,102u8,105u8,101u8,108u8,100u8,4u8,102u8,114u8,111u8,109u8,2u8,116u8,111u8,9u8,110u8,111u8,116u8,95u8, - 102u8,111u8,117u8,110u8,100u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,6u8,99u8,114u8,101u8, - 97u8,116u8,101u8,8u8,116u8,111u8,95u8,98u8,121u8,116u8,101u8,115u8,6u8,97u8,112u8,112u8,101u8,110u8,100u8,8u8, - 115u8,104u8,97u8,51u8,95u8,50u8,53u8,54u8,10u8,116u8,111u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,14u8, - 97u8,108u8,114u8,101u8,97u8,100u8,121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,14u8,100u8,101u8,115u8,116u8,114u8, - 111u8,121u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8, - 95u8,100u8,101u8,110u8,105u8,101u8,100u8,12u8,111u8,117u8,116u8,95u8,111u8,102u8,95u8,114u8,97u8,110u8,103u8,101u8, - 10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,1u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8, - 0u8,4u8,0u8,2u8,1u8,8u8,2u8,1u8,253u8,2u8,1u8,254u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,191u8,4u8,7u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,14u8,69u8,79u8,66u8,74u8,69u8,67u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,40u8,65u8,110u8, - 32u8,111u8,98u8,106u8,101u8,99u8,116u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8,115u8, - 116u8,115u8,32u8,97u8,116u8,32u8,116u8,104u8,105u8,115u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,69u8,79u8,66u8,74u8,69u8,67u8,84u8,95u8,68u8,79u8,69u8,83u8,95u8, - 78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,40u8,65u8,110u8,32u8,111u8,98u8,106u8,101u8,99u8,116u8,32u8, - 100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,32u8,97u8,116u8,32u8,116u8,104u8, - 105u8,115u8,32u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8, - 78u8,79u8,95u8,85u8,78u8,71u8,65u8,84u8,69u8,68u8,95u8,84u8,82u8,65u8,78u8,83u8,70u8,69u8,82u8,83u8, - 50u8,84u8,104u8,101u8,32u8,111u8,98u8,106u8,101u8,99u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8, - 32u8,104u8,97u8,118u8,101u8,32u8,117u8,110u8,103u8,97u8,116u8,101u8,100u8,32u8,116u8,114u8,97u8,110u8,115u8,102u8, - 101u8,114u8,115u8,32u8,101u8,110u8,97u8,98u8,108u8,101u8,100u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8, - 69u8,78u8,79u8,84u8,95u8,79u8,66u8,74u8,69u8,67u8,84u8,95u8,79u8,87u8,78u8,69u8,82u8,46u8,84u8,104u8, - 101u8,32u8,99u8,97u8,108u8,108u8,101u8,114u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,104u8,97u8, - 118u8,101u8,32u8,111u8,119u8,110u8,101u8,114u8,115u8,104u8,105u8,112u8,32u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8, - 105u8,111u8,110u8,115u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,14u8,69u8,67u8,65u8,78u8,78u8,79u8,84u8, - 95u8,68u8,69u8,76u8,69u8,84u8,69u8,38u8,84u8,104u8,101u8,32u8,111u8,98u8,106u8,101u8,99u8,116u8,32u8,100u8, - 111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,97u8,108u8,108u8,111u8,119u8,32u8,102u8,111u8,114u8,32u8,100u8,101u8, - 108u8,101u8,116u8,105u8,111u8,110u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,77u8,65u8,88u8,73u8, - 77u8,85u8,77u8,95u8,78u8,69u8,83u8,84u8,73u8,78u8,71u8,47u8,69u8,120u8,99u8,101u8,101u8,100u8,115u8,32u8, - 109u8,97u8,120u8,105u8,109u8,117u8,109u8,32u8,110u8,101u8,115u8,116u8,105u8,110u8,103u8,32u8,102u8,111u8,114u8,32u8, - 97u8,110u8,32u8,111u8,98u8,106u8,101u8,99u8,116u8,32u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,46u8,7u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,82u8,69u8,83u8,79u8,85u8,82u8,67u8,69u8,95u8,68u8,79u8, - 69u8,83u8,95u8,78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,52u8,84u8,104u8,101u8,32u8,114u8,101u8,115u8, - 111u8,117u8,114u8,99u8,101u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,115u8,116u8,111u8,114u8,101u8,100u8,32u8, - 97u8,116u8,32u8,116u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8,105u8,101u8,100u8,32u8,97u8,100u8,100u8, - 114u8,101u8,115u8,115u8,46u8,2u8,10u8,79u8,98u8,106u8,101u8,99u8,116u8,67u8,111u8,114u8,101u8,1u8,3u8,1u8, - 24u8,48u8,120u8,49u8,58u8,58u8,111u8,98u8,106u8,101u8,99u8,116u8,58u8,58u8,79u8,98u8,106u8,101u8,99u8,116u8, - 71u8,114u8,111u8,117u8,112u8,11u8,79u8,98u8,106u8,101u8,99u8,116u8,71u8,114u8,111u8,117u8,112u8,1u8,2u8,1u8, - 6u8,103u8,108u8,111u8,98u8,97u8,108u8,0u8,0u8,2u8,2u8,57u8,5u8,58u8,1u8,1u8,2u8,1u8,57u8,5u8, - 2u8,2u8,1u8,57u8,5u8,3u8,2u8,2u8,57u8,5u8,49u8,5u8,4u8,2u8,1u8,59u8,5u8,5u8,2u8,4u8, - 60u8,3u8,49u8,5u8,61u8,1u8,62u8,11u8,10u8,1u8,8u8,7u8,6u8,2u8,1u8,63u8,1u8,7u8,2u8,3u8, - 0u8,5u8,64u8,5u8,65u8,5u8,8u8,2u8,1u8,57u8,5u8,4u8,29u8,4u8,41u8,0u8,1u8,0u8,0u8,14u8, - 4u8,11u8,0u8,16u8,0u8,20u8,2u8,1u8,1u8,0u8,0u8,14u8,4u8,11u8,0u8,16u8,1u8,20u8,2u8,2u8, - 1u8,0u8,0u8,14u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,3u8,1u8,0u8,0u8,14u8,17u8,10u8,0u8,41u8, - 5u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8,35u8,39u8,10u8,0u8,56u8,0u8,4u8,11u8,5u8,14u8,7u8,6u8, - 17u8,35u8,39u8,11u8,0u8,57u8,0u8,2u8,4u8,1u8,0u8,0u8,14u8,4u8,11u8,0u8,16u8,3u8,20u8,2u8, - 5u8,1u8,0u8,1u8,5u8,30u8,11u8,11u8,0u8,17u8,36u8,12u8,1u8,10u8,1u8,42u8,5u8,12u8,2u8,11u8, - 1u8,11u8,2u8,15u8,4u8,17u8,37u8,2u8,6u8,1u8,0u8,0u8,27u8,12u8,11u8,0u8,17u8,36u8,12u8,2u8, - 14u8,2u8,11u8,1u8,17u8,7u8,12u8,3u8,11u8,2u8,11u8,3u8,9u8,17u8,11u8,2u8,7u8,1u8,0u8,0u8, - 32u8,13u8,11u8,0u8,56u8,1u8,12u8,2u8,13u8,2u8,11u8,1u8,56u8,2u8,13u8,2u8,7u8,10u8,68u8,34u8, - 11u8,2u8,17u8,40u8,17u8,41u8,2u8,8u8,1u8,0u8,0u8,7u8,8u8,10u8,0u8,17u8,42u8,12u8,1u8,11u8, - 0u8,17u8,36u8,11u8,1u8,17u8,9u8,2u8,9u8,0u8,0u8,0u8,36u8,15u8,14u8,1u8,56u8,3u8,12u8,2u8, - 13u8,2u8,7u8,9u8,68u8,34u8,11u8,2u8,17u8,40u8,17u8,41u8,12u8,3u8,11u8,0u8,11u8,3u8,8u8,17u8, - 11u8,2u8,10u8,1u8,0u8,1u8,5u8,7u8,8u8,10u8,0u8,17u8,5u8,12u8,1u8,11u8,0u8,17u8,36u8,11u8, - 1u8,17u8,9u8,2u8,11u8,0u8,0u8,0u8,37u8,29u8,10u8,1u8,41u8,5u8,32u8,4u8,5u8,5u8,8u8,7u8, - 5u8,17u8,43u8,39u8,10u8,1u8,17u8,44u8,12u8,4u8,7u8,7u8,12u8,3u8,10u8,1u8,13u8,3u8,17u8,37u8, - 12u8,5u8,14u8,4u8,11u8,3u8,11u8,0u8,8u8,11u8,5u8,56u8,4u8,18u8,5u8,45u8,5u8,11u8,1u8,11u8, - 2u8,18u8,0u8,2u8,12u8,1u8,0u8,1u8,5u8,39u8,12u8,14u8,0u8,16u8,1u8,20u8,44u8,5u8,19u8,5u8, - 12u8,1u8,1u8,1u8,1u8,11u8,1u8,56u8,5u8,2u8,13u8,1u8,0u8,1u8,5u8,40u8,10u8,11u8,0u8,16u8, - 5u8,20u8,42u8,5u8,12u8,1u8,9u8,11u8,1u8,15u8,6u8,21u8,2u8,14u8,1u8,0u8,1u8,5u8,40u8,10u8, - 11u8,0u8,16u8,5u8,20u8,42u8,5u8,12u8,1u8,8u8,11u8,1u8,15u8,6u8,21u8,2u8,15u8,0u8,2u8,0u8, - 16u8,1u8,0u8,0u8,14u8,15u8,10u8,0u8,16u8,3u8,20u8,4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,0u8, - 17u8,47u8,39u8,11u8,0u8,16u8,0u8,20u8,18u8,1u8,2u8,17u8,1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8, - 0u8,20u8,18u8,2u8,2u8,18u8,1u8,0u8,1u8,5u8,1u8,12u8,10u8,0u8,16u8,5u8,20u8,57u8,1u8,56u8, - 6u8,12u8,1u8,11u8,0u8,16u8,5u8,20u8,11u8,1u8,18u8,3u8,2u8,19u8,1u8,0u8,0u8,14u8,5u8,11u8, - 0u8,16u8,0u8,20u8,17u8,44u8,2u8,20u8,1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8,2u8,20u8,17u8,44u8, - 2u8,21u8,1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8,0u8,20u8,18u8,8u8,2u8,22u8,1u8,0u8,1u8,5u8, - 14u8,5u8,11u8,0u8,56u8,7u8,11u8,1u8,33u8,2u8,23u8,1u8,0u8,1u8,5u8,14u8,4u8,11u8,0u8,17u8, - 5u8,56u8,8u8,2u8,24u8,1u8,0u8,0u8,14u8,4u8,11u8,0u8,55u8,0u8,20u8,2u8,25u8,1u8,0u8,0u8, - 14u8,5u8,11u8,0u8,16u8,0u8,20u8,56u8,9u8,2u8,26u8,1u8,0u8,0u8,14u8,5u8,11u8,0u8,16u8,1u8, - 20u8,56u8,9u8,2u8,27u8,1u8,0u8,1u8,5u8,14u8,16u8,14u8,0u8,55u8,0u8,20u8,41u8,5u8,4u8,6u8, - 5u8,9u8,7u8,4u8,17u8,35u8,39u8,14u8,0u8,55u8,0u8,20u8,43u8,5u8,16u8,8u8,20u8,2u8,28u8,1u8, - 0u8,1u8,5u8,27u8,48u8,14u8,0u8,56u8,10u8,12u8,2u8,10u8,2u8,10u8,1u8,33u8,4u8,9u8,8u8,2u8, - 10u8,2u8,41u8,5u8,4u8,13u8,5u8,16u8,7u8,4u8,17u8,35u8,39u8,11u8,2u8,43u8,5u8,16u8,8u8,20u8, - 12u8,3u8,10u8,1u8,10u8,3u8,34u8,4u8,46u8,5u8,26u8,49u8,1u8,7u8,8u8,35u8,4u8,31u8,5u8,34u8, - 7u8,1u8,17u8,48u8,39u8,10u8,3u8,41u8,5u8,32u8,4u8,40u8,9u8,2u8,11u8,3u8,43u8,5u8,16u8,8u8, - 20u8,12u8,3u8,5u8,21u8,8u8,2u8,29u8,1u8,0u8,1u8,5u8,14u8,7u8,11u8,0u8,14u8,1u8,55u8,0u8, - 20u8,11u8,2u8,17u8,31u8,2u8,30u8,1u8,4u8,1u8,5u8,14u8,5u8,11u8,0u8,11u8,1u8,11u8,2u8,17u8, - 31u8,2u8,31u8,1u8,0u8,1u8,5u8,40u8,30u8,11u8,0u8,17u8,36u8,10u8,1u8,17u8,34u8,10u8,1u8,42u8, - 5u8,12u8,3u8,10u8,3u8,16u8,8u8,20u8,10u8,2u8,33u8,4u8,16u8,11u8,3u8,1u8,2u8,10u8,3u8,15u8, - 9u8,11u8,1u8,10u8,3u8,16u8,8u8,20u8,10u8,2u8,18u8,7u8,56u8,11u8,11u8,2u8,11u8,3u8,15u8,8u8, - 21u8,2u8,32u8,1u8,0u8,1u8,5u8,14u8,7u8,11u8,0u8,11u8,1u8,14u8,2u8,55u8,1u8,20u8,56u8,12u8, - 2u8,33u8,1u8,0u8,1u8,5u8,40u8,35u8,14u8,0u8,16u8,10u8,20u8,42u8,5u8,12u8,2u8,10u8,2u8,16u8, - 8u8,20u8,14u8,0u8,16u8,11u8,20u8,33u8,4u8,14u8,5u8,19u8,11u8,2u8,1u8,7u8,2u8,17u8,47u8,39u8, - 10u8,2u8,15u8,9u8,14u8,0u8,16u8,10u8,20u8,10u8,2u8,16u8,8u8,20u8,10u8,1u8,18u8,7u8,56u8,11u8, - 11u8,1u8,11u8,2u8,15u8,8u8,21u8,2u8,34u8,0u8,0u8,1u8,5u8,44u8,65u8,11u8,1u8,12u8,2u8,10u8, - 2u8,41u8,5u8,4u8,6u8,5u8,9u8,7u8,4u8,17u8,35u8,39u8,11u8,2u8,43u8,5u8,12u8,4u8,10u8,4u8, - 16u8,6u8,20u8,4u8,17u8,5u8,22u8,11u8,4u8,1u8,7u8,3u8,17u8,47u8,39u8,11u8,4u8,16u8,8u8,20u8, - 12u8,3u8,10u8,0u8,10u8,3u8,34u8,4u8,64u8,5u8,31u8,49u8,1u8,7u8,8u8,35u8,4u8,36u8,5u8,39u8, - 7u8,1u8,17u8,48u8,39u8,10u8,3u8,41u8,5u8,4u8,43u8,5u8,46u8,7u8,2u8,17u8,47u8,39u8,11u8,3u8, - 43u8,5u8,12u8,5u8,10u8,5u8,16u8,6u8,20u8,4u8,54u8,5u8,59u8,11u8,5u8,1u8,7u8,3u8,17u8,47u8, - 39u8,11u8,5u8,16u8,8u8,20u8,12u8,3u8,5u8,26u8,2u8,0u8,0u8,1u8,0u8,2u8,0u8,0u8,1u8,5u8, - 0u8,8u8,0u8,5u8,2u8,4u8,0u8,5u8,1u8,5u8,3u8,3u8,0u8,3u8,1u8,7u8,29u8,7u8,43u8,0u8, - ]; - vector::push_back(&mut code, chunk37); - let chunk38 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,18u8,2u8,18u8,20u8,3u8,38u8,120u8,4u8, - 158u8,1u8,18u8,5u8,176u8,1u8,168u8,1u8,7u8,216u8,2u8,255u8,3u8,8u8,215u8,6u8,32u8,6u8,247u8,6u8, - 56u8,16u8,175u8,7u8,198u8,1u8,10u8,245u8,8u8,10u8,12u8,255u8,8u8,174u8,2u8,13u8,173u8,11u8,2u8,0u8, - 0u8,0u8,1u8,0u8,2u8,0u8,3u8,0u8,4u8,0u8,5u8,0u8,6u8,0u8,7u8,0u8,8u8,0u8,9u8,8u8, - 0u8,1u8,13u8,6u8,0u8,7u8,17u8,7u8,2u8,0u8,0u8,0u8,0u8,2u8,18u8,8u8,0u8,0u8,10u8,0u8, - 1u8,0u8,0u8,11u8,2u8,1u8,0u8,0u8,12u8,3u8,1u8,0u8,0u8,14u8,4u8,5u8,0u8,0u8,15u8,6u8, - 1u8,0u8,1u8,10u8,8u8,7u8,0u8,4u8,19u8,10u8,1u8,1u8,0u8,6u8,20u8,10u8,11u8,0u8,4u8,21u8, - 12u8,1u8,1u8,0u8,3u8,22u8,13u8,1u8,0u8,5u8,23u8,15u8,15u8,0u8,7u8,24u8,17u8,18u8,2u8,4u8, - 4u8,5u8,25u8,15u8,15u8,0u8,7u8,26u8,19u8,20u8,2u8,4u8,4u8,7u8,27u8,21u8,15u8,2u8,4u8,4u8, - 7u8,28u8,22u8,1u8,2u8,4u8,4u8,1u8,29u8,8u8,1u8,0u8,7u8,30u8,1u8,22u8,2u8,4u8,4u8,7u8, - 31u8,24u8,1u8,2u8,4u8,4u8,8u8,32u8,26u8,18u8,1u8,0u8,1u8,33u8,11u8,27u8,0u8,6u8,9u8,8u8, - 9u8,11u8,16u8,13u8,16u8,14u8,16u8,15u8,16u8,17u8,16u8,18u8,16u8,19u8,25u8,3u8,6u8,12u8,10u8,2u8, - 10u8,2u8,0u8,4u8,6u8,12u8,10u8,2u8,10u8,2u8,3u8,4u8,6u8,12u8,10u8,2u8,10u8,2u8,10u8,10u8, - 2u8,2u8,6u8,12u8,5u8,1u8,8u8,1u8,4u8,6u8,12u8,12u8,8u8,1u8,10u8,2u8,2u8,12u8,8u8,1u8, - 2u8,6u8,12u8,10u8,2u8,1u8,8u8,3u8,1u8,6u8,12u8,1u8,5u8,3u8,6u8,12u8,5u8,3u8,3u8,6u8, - 12u8,10u8,2u8,10u8,10u8,2u8,5u8,7u8,8u8,0u8,1u8,5u8,8u8,1u8,8u8,1u8,1u8,3u8,2u8,5u8, - 8u8,1u8,2u8,6u8,11u8,2u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,1u8,2u8,7u8,11u8,2u8, - 2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,2u8,9u8,0u8,9u8,1u8,1u8,6u8,11u8,2u8,2u8,9u8,0u8, - 9u8,1u8,1u8,11u8,2u8,2u8,9u8,0u8,9u8,1u8,5u8,10u8,2u8,10u8,2u8,7u8,8u8,0u8,5u8,5u8, - 3u8,7u8,11u8,2u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8,2u8,1u8,6u8,10u8,9u8,0u8, - 1u8,10u8,2u8,16u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8, - 7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,10u8,97u8,112u8,116u8,111u8,115u8,95u8,99u8,111u8,105u8,110u8,4u8, - 99u8,111u8,100u8,101u8,4u8,99u8,111u8,105u8,110u8,5u8,101u8,114u8,114u8,111u8,114u8,6u8,115u8,105u8,103u8,110u8, - 101u8,114u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,6u8,118u8,101u8,99u8,116u8,111u8,114u8, - 9u8,67u8,111u8,110u8,116u8,97u8,105u8,110u8,101u8,114u8,23u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8, - 115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,99u8,114u8,101u8,97u8,116u8, - 101u8,95u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8, - 110u8,100u8,95u8,102u8,117u8,110u8,100u8,43u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,114u8,101u8,115u8,111u8,117u8, - 114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,110u8,100u8,95u8,112u8,117u8,98u8,108u8, - 105u8,115u8,104u8,95u8,112u8,97u8,99u8,107u8,97u8,103u8,101u8,16u8,83u8,105u8,103u8,110u8,101u8,114u8,67u8,97u8, - 112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,29u8,114u8,101u8,116u8,114u8,105u8,101u8,118u8,101u8,95u8,114u8,101u8, - 115u8,111u8,117u8,114u8,99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,99u8,97u8,112u8,54u8,114u8, - 111u8,116u8,97u8,116u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8, - 116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,95u8,97u8,110u8,100u8,95u8,115u8,116u8,111u8, - 114u8,101u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,5u8,115u8,116u8,111u8,114u8,101u8,9u8, - 83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,9u8,65u8,112u8,116u8,111u8,115u8,67u8,111u8,105u8,110u8,8u8, - 114u8,101u8,103u8,105u8,115u8,116u8,101u8,114u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,8u8, - 116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,19u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,95u8,112u8,97u8,99u8, - 107u8,97u8,103u8,101u8,95u8,116u8,120u8,110u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,12u8,99u8, - 111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8, - 97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,6u8,108u8,101u8,110u8,103u8, - 116u8,104u8,13u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,101u8,109u8,112u8,116u8,121u8,34u8,114u8,111u8,116u8, - 97u8,116u8,101u8,95u8,97u8,117u8,116u8,104u8,101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8, - 101u8,121u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,3u8,97u8, - 100u8,100u8,8u8,105u8,115u8,95u8,101u8,109u8,112u8,116u8,121u8,22u8,103u8,101u8,116u8,95u8,97u8,117u8,116u8,104u8, - 101u8,110u8,116u8,105u8,99u8,97u8,116u8,105u8,111u8,110u8,95u8,107u8,101u8,121u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,33u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8, - 49u8,177u8,1u8,2u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,67u8,79u8,78u8,84u8,65u8,73u8, - 78u8,69u8,82u8,95u8,78u8,79u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,39u8,67u8,111u8, - 110u8,116u8,97u8,105u8,110u8,101u8,114u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8,110u8,111u8,116u8, - 32u8,102u8,111u8,117u8,110u8,100u8,32u8,105u8,110u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,23u8,69u8,85u8,78u8,65u8,85u8,84u8,72u8,79u8,82u8,73u8,90u8,69u8,68u8,95u8, - 78u8,79u8,84u8,95u8,79u8,87u8,78u8,69u8,82u8,68u8,84u8,104u8,101u8,32u8,114u8,101u8,115u8,111u8,117u8,114u8, - 99u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,119u8,97u8,115u8,32u8,110u8,111u8,116u8,32u8,99u8, - 114u8,101u8,97u8,116u8,101u8,100u8,32u8,98u8,121u8,32u8,116u8,104u8,101u8,32u8,115u8,112u8,101u8,99u8,105u8,102u8, - 105u8,101u8,100u8,32u8,115u8,111u8,117u8,114u8,99u8,101u8,32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,0u8,0u8, - 0u8,2u8,1u8,16u8,11u8,2u8,2u8,5u8,8u8,1u8,0u8,1u8,4u8,1u8,0u8,7u8,11u8,10u8,0u8,11u8, - 1u8,17u8,5u8,12u8,4u8,12u8,3u8,11u8,0u8,11u8,3u8,11u8,4u8,11u8,2u8,17u8,4u8,2u8,1u8,1u8, - 4u8,1u8,0u8,7u8,18u8,10u8,0u8,11u8,1u8,17u8,5u8,12u8,5u8,12u8,4u8,14u8,4u8,56u8,0u8,10u8, - 0u8,14u8,4u8,17u8,7u8,11u8,3u8,56u8,1u8,11u8,0u8,11u8,4u8,11u8,5u8,11u8,2u8,17u8,4u8,2u8, - 2u8,1u8,4u8,1u8,0u8,7u8,15u8,10u8,0u8,11u8,1u8,17u8,5u8,12u8,5u8,12u8,4u8,14u8,4u8,11u8, - 2u8,11u8,3u8,17u8,9u8,11u8,0u8,11u8,4u8,11u8,5u8,7u8,2u8,17u8,4u8,2u8,3u8,1u8,0u8,1u8, - 0u8,14u8,53u8,10u8,1u8,41u8,0u8,4u8,4u8,5u8,9u8,11u8,0u8,1u8,7u8,0u8,17u8,10u8,39u8,10u8, - 0u8,17u8,7u8,12u8,4u8,10u8,1u8,42u8,0u8,12u8,2u8,10u8,2u8,16u8,0u8,14u8,4u8,56u8,2u8,4u8, - 21u8,5u8,28u8,11u8,0u8,1u8,11u8,2u8,1u8,7u8,1u8,17u8,12u8,39u8,10u8,2u8,15u8,0u8,14u8,4u8, - 56u8,3u8,12u8,6u8,1u8,11u8,6u8,11u8,2u8,16u8,0u8,56u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,12u8,3u8,12u8,5u8,11u8,3u8,4u8,48u8,11u8,1u8,44u8,0u8,19u8,0u8,56u8,5u8,11u8, - 0u8,7u8,2u8,17u8,16u8,11u8,5u8,2u8,4u8,0u8,0u8,1u8,0u8,23u8,40u8,10u8,0u8,17u8,7u8,12u8, - 7u8,10u8,7u8,41u8,0u8,32u8,4u8,12u8,11u8,0u8,56u8,6u8,18u8,0u8,45u8,0u8,5u8,14u8,11u8,0u8, - 1u8,10u8,7u8,42u8,0u8,12u8,6u8,14u8,1u8,17u8,7u8,12u8,8u8,11u8,6u8,15u8,0u8,11u8,8u8,11u8, - 2u8,56u8,7u8,14u8,3u8,56u8,8u8,4u8,32u8,11u8,7u8,17u8,20u8,12u8,4u8,5u8,34u8,11u8,3u8,12u8, - 4u8,11u8,4u8,12u8,5u8,14u8,1u8,11u8,5u8,17u8,16u8,2u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk38); - let chunk39 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,6u8,1u8,0u8,10u8,3u8,10u8,105u8,5u8,115u8,53u8,7u8, - 168u8,1u8,176u8,3u8,8u8,216u8,4u8,32u8,12u8,248u8,4u8,250u8,2u8,0u8,0u8,0u8,1u8,0u8,2u8,0u8, - 3u8,0u8,4u8,0u8,5u8,0u8,1u8,0u8,0u8,6u8,2u8,1u8,0u8,0u8,7u8,2u8,1u8,0u8,0u8,8u8, - 0u8,1u8,0u8,0u8,9u8,0u8,1u8,0u8,0u8,10u8,0u8,1u8,0u8,0u8,11u8,0u8,1u8,0u8,0u8,12u8, - 0u8,1u8,0u8,1u8,13u8,3u8,4u8,0u8,2u8,14u8,4u8,5u8,0u8,2u8,5u8,2u8,1u8,0u8,2u8,15u8, - 2u8,1u8,0u8,3u8,16u8,7u8,5u8,0u8,3u8,17u8,7u8,8u8,0u8,3u8,18u8,9u8,1u8,0u8,3u8,19u8, - 0u8,1u8,0u8,4u8,20u8,4u8,11u8,0u8,4u8,21u8,4u8,4u8,0u8,4u8,22u8,4u8,8u8,0u8,4u8,23u8, - 9u8,1u8,0u8,4u8,19u8,0u8,1u8,0u8,3u8,6u8,12u8,5u8,5u8,0u8,2u8,6u8,12u8,5u8,1u8,6u8, - 12u8,1u8,5u8,1u8,1u8,2u8,3u8,5u8,2u8,5u8,5u8,1u8,3u8,4u8,6u8,12u8,5u8,5u8,3u8,6u8, - 10u8,5u8,3u8,3u8,3u8,5u8,6u8,10u8,5u8,1u8,10u8,5u8,5u8,10u8,5u8,3u8,3u8,5u8,6u8,10u8, - 5u8,13u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,112u8,114u8,111u8,120u8,121u8,6u8,115u8,105u8,103u8,110u8, - 101u8,114u8,5u8,115u8,116u8,97u8,107u8,101u8,16u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8, - 116u8,114u8,97u8,99u8,116u8,7u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,12u8,115u8,101u8,116u8,95u8,111u8,112u8, - 101u8,114u8,97u8,116u8,111u8,114u8,23u8,115u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8, - 108u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,20u8,115u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,101u8, - 95u8,112u8,111u8,111u8,108u8,95u8,118u8,111u8,116u8,101u8,114u8,29u8,115u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8, - 105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8, - 114u8,26u8,115u8,101u8,116u8,95u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8, - 99u8,116u8,95u8,118u8,111u8,116u8,101u8,114u8,29u8,115u8,101u8,116u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8, - 95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,26u8,115u8, - 101u8,116u8,95u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,95u8, - 118u8,111u8,116u8,101u8,114u8,9u8,115u8,101u8,116u8,95u8,118u8,111u8,116u8,101u8,114u8,10u8,97u8,100u8,100u8,114u8, - 101u8,115u8,115u8,95u8,111u8,102u8,17u8,115u8,116u8,97u8,107u8,101u8,95u8,112u8,111u8,111u8,108u8,95u8,101u8,120u8, - 105u8,115u8,116u8,115u8,19u8,115u8,101u8,116u8,95u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,101u8,100u8,95u8,118u8, - 111u8,116u8,101u8,114u8,23u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8, - 116u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,21u8,99u8,111u8,109u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8, - 112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,15u8,115u8,119u8,105u8,116u8,99u8,104u8,95u8,111u8,112u8, - 101u8,114u8,97u8,116u8,111u8,114u8,12u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,118u8,111u8,116u8,101u8,114u8,17u8, - 118u8,101u8,115u8,116u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,115u8,8u8,111u8,112u8, - 101u8,114u8,97u8,116u8,111u8,114u8,30u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,95u8,99u8,111u8,109u8,109u8, - 105u8,115u8,115u8,105u8,111u8,110u8,95u8,112u8,101u8,114u8,99u8,101u8,110u8,116u8,97u8,103u8,101u8,15u8,117u8,112u8, - 100u8,97u8,116u8,101u8,95u8,111u8,112u8,101u8,114u8,97u8,116u8,111u8,114u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,0u8,1u8,4u8,0u8,1u8,12u8,10u8,0u8,10u8,1u8,10u8,2u8,17u8,5u8,10u8, - 0u8,11u8,1u8,10u8,2u8,17u8,3u8,11u8,0u8,11u8,2u8,17u8,1u8,2u8,1u8,1u8,4u8,0u8,1u8,11u8, - 10u8,0u8,17u8,8u8,17u8,9u8,4u8,8u8,11u8,0u8,11u8,1u8,17u8,10u8,5u8,10u8,11u8,0u8,1u8,2u8, - 2u8,1u8,4u8,0u8,1u8,11u8,10u8,0u8,17u8,8u8,17u8,9u8,4u8,8u8,11u8,0u8,11u8,1u8,17u8,11u8, - 5u8,10u8,11u8,0u8,1u8,2u8,3u8,1u8,4u8,0u8,6u8,20u8,10u8,0u8,17u8,8u8,12u8,4u8,10u8,4u8, - 10u8,1u8,17u8,12u8,4u8,17u8,11u8,4u8,10u8,1u8,17u8,13u8,12u8,3u8,11u8,0u8,11u8,1u8,11u8,2u8, - 11u8,3u8,17u8,14u8,5u8,19u8,11u8,0u8,1u8,2u8,4u8,1u8,4u8,0u8,1u8,13u8,10u8,0u8,17u8,8u8, - 10u8,1u8,17u8,12u8,4u8,10u8,11u8,0u8,11u8,1u8,11u8,2u8,17u8,15u8,5u8,12u8,11u8,0u8,1u8,2u8, - 5u8,1u8,4u8,0u8,10u8,44u8,10u8,0u8,17u8,8u8,17u8,16u8,12u8,3u8,14u8,3u8,12u8,8u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,8u8,65u8,4u8,12u8,6u8,10u8,5u8,10u8,6u8,35u8, - 4u8,39u8,5u8,16u8,10u8,8u8,10u8,5u8,66u8,4u8,20u8,12u8,7u8,10u8,7u8,17u8,17u8,10u8,1u8,33u8, - 4u8,34u8,10u8,7u8,17u8,18u8,12u8,4u8,10u8,0u8,11u8,7u8,10u8,2u8,11u8,4u8,17u8,19u8,11u8,5u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,11u8,11u8,8u8,1u8,11u8,0u8,1u8, - 2u8,6u8,1u8,4u8,0u8,12u8,40u8,10u8,0u8,17u8,8u8,17u8,16u8,12u8,3u8,14u8,3u8,12u8,7u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,4u8,10u8,7u8,65u8,4u8,12u8,5u8,10u8,4u8,10u8,5u8, - 35u8,4u8,35u8,5u8,16u8,10u8,7u8,10u8,4u8,66u8,4u8,20u8,12u8,6u8,10u8,6u8,17u8,17u8,10u8,1u8, - 33u8,4u8,30u8,10u8,0u8,11u8,6u8,10u8,2u8,17u8,20u8,11u8,4u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,12u8,4u8,5u8,11u8,11u8,7u8,1u8,11u8,0u8,1u8,2u8,7u8,1u8,4u8,0u8,1u8,12u8, - 10u8,0u8,10u8,1u8,10u8,2u8,17u8,6u8,10u8,0u8,11u8,1u8,10u8,2u8,17u8,4u8,11u8,0u8,11u8,2u8, - 17u8,2u8,2u8,0u8, - ]; - vector::push_back(&mut code, chunk39); - let chunk1 = vector[ - 14u8,65u8,112u8,116u8,111u8,115u8,70u8,114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,64u8,52u8,68u8,70u8,70u8,67u8,65u8,51u8,69u8,69u8,54u8,51u8,53u8,57u8,70u8, - 57u8,49u8,68u8,54u8,55u8,56u8,55u8,48u8,65u8,70u8,54u8,48u8,48u8,48u8,49u8,50u8,52u8,65u8,49u8,57u8, - 70u8,56u8,53u8,68u8,67u8,52u8,48u8,54u8,52u8,65u8,67u8,52u8,50u8,57u8,50u8,69u8,56u8,70u8,50u8,52u8, - 68u8,69u8,49u8,51u8,51u8,51u8,52u8,48u8,65u8,52u8,191u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,117u8,142u8,177u8,10u8,194u8,48u8,20u8,69u8,247u8,124u8,69u8,201u8,110u8,76u8,145u8,130u8,139u8,67u8, - 17u8,220u8,156u8,28u8,75u8,41u8,49u8,121u8,74u8,105u8,155u8,87u8,242u8,98u8,21u8,196u8,127u8,55u8,73u8,21u8, - 68u8,113u8,204u8,61u8,55u8,247u8,157u8,106u8,84u8,186u8,83u8,103u8,168u8,153u8,85u8,3u8,100u8,155u8,140u8,151u8, - 163u8,71u8,218u8,185u8,240u8,186u8,162u8,235u8,56u8,155u8,192u8,81u8,139u8,54u8,162u8,92u8,72u8,33u8,57u8,99u8, - 149u8,50u8,198u8,1u8,17u8,80u8,205u8,200u8,155u8,136u8,228u8,45u8,231u8,76u8,197u8,175u8,205u8,111u8,114u8,122u8, - 175u8,125u8,229u8,30u8,59u8,176u8,115u8,182u8,226u8,76u8,163u8,131u8,38u8,204u8,226u8,197u8,105u8,160u8,57u8,46u8, - 139u8,66u8,110u8,243u8,117u8,176u8,24u8,34u8,2u8,55u8,193u8,107u8,60u8,121u8,24u8,24u8,193u8,26u8,176u8,186u8, - 141u8,42u8,201u8,252u8,224u8,77u8,223u8,30u8,67u8,231u8,158u8,245u8,168u8,85u8,31u8,219u8,66u8,44u8,211u8,193u8, - 5u8,37u8,200u8,179u8,7u8,219u8,227u8,4u8,127u8,170u8,67u8,64u8,31u8,205u8,39u8,184u8,76u8,13u8,225u8,34u8, - 1u8,0u8,0u8,40u8,16u8,115u8,121u8,115u8,116u8,101u8,109u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,101u8, - 115u8,165u8,5u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,189u8,86u8,95u8,111u8,218u8,48u8,16u8, - 127u8,239u8,167u8,184u8,190u8,108u8,32u8,85u8,165u8,127u8,88u8,183u8,25u8,33u8,13u8,149u8,244u8,101u8,98u8,76u8, - 129u8,177u8,199u8,212u8,36u8,166u8,68u8,75u8,108u8,100u8,59u8,172u8,104u8,237u8,119u8,223u8,197u8,16u8,2u8,33u8, - 49u8,65u8,211u8,234u8,23u8,200u8,157u8,239u8,238u8,119u8,119u8,63u8,159u8,29u8,139u8,32u8,137u8,24u8,208u8,133u8, - 22u8,202u8,155u8,73u8,26u8,179u8,223u8,66u8,254u8,34u8,68u8,173u8,148u8,102u8,177u8,71u8,131u8,64u8,50u8,165u8, - 152u8,130u8,63u8,103u8,128u8,43u8,81u8,12u8,148u8,14u8,8u8,97u8,82u8,10u8,217u8,217u8,151u8,169u8,240u8,137u8, - 51u8,20u8,26u8,105u8,171u8,213u8,130u8,241u8,28u8,61u8,175u8,61u8,180u8,168u8,239u8,139u8,132u8,107u8,8u8,194u8, - 0u8,184u8,208u8,224u8,11u8,137u8,242u8,133u8,224u8,1u8,104u8,1u8,26u8,119u8,162u8,132u8,1u8,202u8,68u8,34u8, - 253u8,173u8,157u8,241u8,229u8,11u8,174u8,52u8,56u8,223u8,134u8,99u8,239u8,126u8,232u8,58u8,158u8,235u8,140u8,134u8, - 63u8,220u8,123u8,199u8,235u8,245u8,251u8,248u8,127u8,68u8,32u8,185u8,107u8,67u8,23u8,174u8,59u8,123u8,161u8,197u8, - 130u8,73u8,170u8,67u8,193u8,193u8,167u8,28u8,4u8,143u8,86u8,48u8,101u8,128u8,194u8,153u8,144u8,49u8,11u8,96u8, - 186u8,50u8,113u8,39u8,131u8,221u8,32u8,147u8,65u8,230u8,237u8,166u8,243u8,47u8,137u8,108u8,107u8,89u8,149u8,73u8, - 239u8,251u8,120u8,56u8,242u8,30u8,220u8,222u8,192u8,249u8,57u8,116u8,191u8,22u8,115u8,185u8,45u8,141u8,14u8,161u8, - 50u8,65u8,115u8,239u8,40u8,101u8,114u8,137u8,217u8,84u8,132u8,201u8,3u8,160u8,119u8,199u8,157u8,56u8,253u8,98u8, - 164u8,246u8,166u8,99u8,139u8,100u8,26u8,133u8,62u8,204u8,18u8,14u8,20u8,91u8,46u8,181u8,151u8,38u8,226u8,101u8, - 29u8,105u8,108u8,18u8,39u8,240u8,110u8,221u8,232u8,230u8,134u8,19u8,233u8,42u8,51u8,200u8,200u8,211u8,88u8,111u8, - 39u8,100u8,243u8,237u8,137u8,89u8,230u8,171u8,217u8,52u8,30u8,94u8,107u8,1u8,216u8,250u8,75u8,127u8,73u8,150u8, - 239u8,33u8,138u8,243u8,70u8,168u8,44u8,150u8,205u8,11u8,48u8,228u8,37u8,4u8,153u8,16u8,135u8,74u8,33u8,65u8, - 188u8,128u8,241u8,144u8,5u8,141u8,106u8,138u8,85u8,34u8,181u8,198u8,202u8,81u8,18u8,152u8,10u8,17u8,237u8,98u8, - 69u8,5u8,116u8,187u8,240u8,101u8,207u8,90u8,217u8,235u8,81u8,56u8,166u8,53u8,90u8,114u8,222u8,216u8,10u8,210u8, - 133u8,104u8,11u8,62u8,106u8,117u8,233u8,98u8,207u8,137u8,189u8,124u8,21u8,188u8,222u8,241u8,113u8,164u8,233u8,57u8, - 180u8,140u8,219u8,121u8,77u8,143u8,83u8,240u8,208u8,218u8,154u8,89u8,231u8,84u8,44u8,199u8,217u8,87u8,44u8,184u8, - 45u8,31u8,195u8,199u8,19u8,138u8,91u8,125u8,154u8,171u8,234u8,155u8,14u8,16u8,151u8,233u8,68u8,114u8,208u8,50u8, - 97u8,16u8,206u8,224u8,49u8,13u8,251u8,152u8,142u8,146u8,171u8,231u8,43u8,16u8,18u8,18u8,30u8,48u8,105u8,70u8, - 87u8,58u8,43u8,231u8,52u8,228u8,240u8,36u8,150u8,76u8,114u8,202u8,125u8,246u8,94u8,165u8,227u8,68u8,75u8,17u8, - 93u8,150u8,112u8,255u8,72u8,114u8,150u8,3u8,96u8,161u8,162u8,41u8,11u8,188u8,188u8,236u8,21u8,102u8,123u8,96u8, - 174u8,158u8,111u8,44u8,186u8,91u8,139u8,174u8,109u8,209u8,125u8,176u8,232u8,238u8,44u8,186u8,143u8,22u8,221u8,39u8, - 139u8,238u8,179u8,69u8,71u8,79u8,104u8,224u8,117u8,89u8,87u8,108u8,133u8,173u8,51u8,147u8,10u8,246u8,69u8,56u8, - 61u8,195u8,116u8,36u8,12u8,213u8,134u8,53u8,235u8,3u8,6u8,115u8,170u8,54u8,23u8,234u8,193u8,173u8,116u8,89u8, - 113u8,190u8,150u8,113u8,173u8,25u8,134u8,41u8,229u8,59u8,173u8,3u8,124u8,50u8,104u8,214u8,167u8,63u8,61u8,192u8, - 9u8,248u8,52u8,200u8,114u8,192u8,219u8,220u8,167u8,81u8,4u8,235u8,167u8,16u8,196u8,230u8,165u8,164u8,202u8,202u8, - 93u8,150u8,69u8,25u8,223u8,151u8,241u8,201u8,119u8,226u8,27u8,225u8,63u8,153u8,33u8,104u8,146u8,197u8,174u8,13u8, - 151u8,133u8,136u8,108u8,11u8,47u8,67u8,140u8,128u8,241u8,117u8,214u8,75u8,25u8,7u8,15u8,197u8,71u8,83u8,25u8, - 218u8,255u8,48u8,104u8,74u8,138u8,144u8,117u8,225u8,245u8,236u8,47u8,60u8,166u8,210u8,184u8,35u8,11u8,0u8,0u8, - 0u8,0u8,4u8,103u8,117u8,105u8,100u8,214u8,5u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,165u8, - 149u8,219u8,110u8,155u8,64u8,16u8,134u8,239u8,253u8,20u8,115u8,149u8,35u8,181u8,27u8,169u8,234u8,5u8,145u8,47u8, - 210u8,198u8,74u8,45u8,85u8,73u8,149u8,38u8,215u8,176u8,192u8,96u8,182u8,193u8,187u8,100u8,15u8,141u8,172u8,202u8, - 239u8,222u8,217u8,5u8,99u8,14u8,117u8,82u8,41u8,150u8,37u8,27u8,118u8,118u8,230u8,155u8,127u8,126u8,150u8,217u8, - 108u8,6u8,87u8,176u8,150u8,153u8,45u8,17u8,114u8,169u8,96u8,133u8,2u8,21u8,51u8,92u8,172u8,96u8,85u8,202u8, - 132u8,149u8,229u8,6u8,172u8,224u8,207u8,22u8,129u8,103u8,40u8,12u8,207u8,57u8,42u8,61u8,105u8,118u8,176u8,202u8, - 72u8,29u8,229u8,138u8,173u8,241u8,69u8,170u8,167u8,48u8,92u8,89u8,158u8,193u8,159u8,9u8,208u8,39u8,87u8,28u8, - 69u8,54u8,14u8,97u8,105u8,42u8,173u8,48u8,151u8,175u8,6u8,201u8,228u8,23u8,166u8,20u8,227u8,131u8,102u8,30u8, - 242u8,48u8,14u8,100u8,168u8,248u8,111u8,204u8,40u8,155u8,92u8,131u8,41u8,16u8,52u8,37u8,69u8,117u8,172u8,129u8, - 101u8,153u8,66u8,77u8,191u8,174u8,8u8,248u8,194u8,168u8,124u8,82u8,109u8,148u8,77u8,13u8,220u8,60u8,46u8,175u8, - 161u8,96u8,26u8,50u8,37u8,171u8,128u8,238u8,74u8,133u8,77u8,3u8,238u8,195u8,179u8,16u8,150u8,215u8,254u8,114u8, - 219u8,133u8,17u8,82u8,124u8,168u8,168u8,40u8,47u8,113u8,69u8,133u8,59u8,44u8,166u8,96u8,6u8,82u8,38u8,32u8, - 33u8,65u8,21u8,34u8,1u8,167u8,10u8,153u8,161u8,168u8,100u8,67u8,28u8,27u8,41u8,112u8,10u8,143u8,26u8,115u8, - 91u8,122u8,197u8,75u8,41u8,159u8,156u8,220u8,182u8,242u8,48u8,199u8,122u8,218u8,229u8,107u8,232u8,82u8,89u8,109u8, - 130u8,3u8,140u8,142u8,104u8,153u8,215u8,85u8,184u8,20u8,145u8,176u8,107u8,224u8,26u8,98u8,30u8,7u8,68u8,67u8, - 255u8,232u8,235u8,68u8,137u8,249u8,249u8,69u8,108u8,138u8,186u8,229u8,14u8,83u8,236u8,68u8,138u8,219u8,116u8,221u8, - 60u8,33u8,216u8,207u8,159u8,130u8,94u8,165u8,171u8,70u8,209u8,186u8,207u8,38u8,139u8,75u8,239u8,210u8,182u8,145u8, - 46u8,101u8,184u8,83u8,127u8,40u8,159u8,7u8,104u8,140u8,70u8,2u8,172u8,173u8,54u8,78u8,173u8,202u8,38u8,37u8, - 215u8,5u8,165u8,99u8,5u8,178u8,12u8,100u8,14u8,57u8,87u8,180u8,102u8,53u8,91u8,161u8,187u8,140u8,235u8,122u8, - 209u8,11u8,55u8,69u8,148u8,178u8,138u8,37u8,188u8,228u8,102u8,19u8,67u8,110u8,69u8,234u8,152u8,107u8,233u8,82u8, - 41u8,104u8,215u8,194u8,149u8,137u8,110u8,22u8,183u8,139u8,251u8,171u8,135u8,187u8,251u8,232u8,246u8,238u8,33u8,250u8, - 241u8,248u8,229u8,251u8,242u8,231u8,183u8,197u8,181u8,111u8,12u8,230u8,240u8,177u8,99u8,177u8,175u8,62u8,185u8,183u8, - 138u8,66u8,99u8,149u8,32u8,199u8,8u8,124u8,169u8,113u8,189u8,181u8,24u8,208u8,80u8,180u8,107u8,184u8,246u8,127u8, - 93u8,206u8,115u8,167u8,39u8,181u8,149u8,79u8,29u8,75u8,163u8,203u8,73u8,79u8,133u8,160u8,167u8,108u8,164u8,48u8, - 15u8,225u8,104u8,109u8,141u8,35u8,57u8,13u8,235u8,42u8,251u8,161u8,150u8,104u8,250u8,19u8,157u8,195u8,217u8,112u8, - 255u8,101u8,27u8,61u8,90u8,162u8,240u8,222u8,238u8,115u8,184u8,216u8,71u8,15u8,74u8,237u8,125u8,62u8,184u8,57u8, - 116u8,67u8,48u8,90u8,117u8,173u8,245u8,239u8,110u8,39u8,253u8,127u8,219u8,177u8,190u8,227u8,135u8,167u8,86u8,183u8, - 246u8,161u8,215u8,63u8,238u8,150u8,141u8,59u8,42u8,119u8,212u8,141u8,120u8,246u8,154u8,192u8,97u8,163u8,107u8,175u8, - 43u8,119u8,209u8,111u8,201u8,111u8,30u8,179u8,222u8,144u8,252u8,206u8,212u8,3u8,84u8,218u8,207u8,180u8,150u8,41u8, - 247u8,174u8,119u8,54u8,164u8,118u8,90u8,227u8,119u8,16u8,137u8,205u8,29u8,133u8,52u8,97u8,183u8,58u8,196u8,56u8, - 59u8,114u8,139u8,83u8,158u8,13u8,203u8,222u8,215u8,190u8,115u8,149u8,155u8,99u8,178u8,61u8,193u8,14u8,63u8,111u8, - 67u8,101u8,164u8,138u8,154u8,77u8,3u8,134u8,93u8,170u8,61u8,72u8,131u8,49u8,117u8,43u8,239u8,97u8,241u8,117u8, - 194u8,127u8,201u8,16u8,13u8,145u8,60u8,208u8,1u8,156u8,183u8,73u8,118u8,179u8,3u8,154u8,93u8,66u8,167u8,237u8, - 112u8,26u8,175u8,10u8,211u8,204u8,124u8,160u8,138u8,59u8,8u8,198u8,138u8,116u8,55u8,188u8,147u8,231u8,45u8,113u8, - 118u8,88u8,123u8,101u8,250u8,72u8,255u8,73u8,163u8,220u8,59u8,49u8,111u8,21u8,160u8,151u8,31u8,121u8,206u8,191u8, - 7u8,178u8,209u8,19u8,132u8,207u8,81u8,223u8,161u8,1u8,236u8,171u8,39u8,82u8,150u8,157u8,242u8,59u8,175u8,194u8, - 124u8,14u8,173u8,99u8,183u8,147u8,191u8,103u8,190u8,90u8,202u8,58u8,8u8,0u8,0u8,0u8,0u8,5u8,101u8,118u8, - 101u8,110u8,116u8,169u8,6u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,149u8,85u8,219u8,106u8,219u8, - 64u8,16u8,125u8,247u8,87u8,204u8,83u8,26u8,83u8,99u8,183u8,37u8,132u8,34u8,59u8,1u8,67u8,66u8,26u8,104u8, - 41u8,180u8,14u8,244u8,205u8,90u8,75u8,35u8,121u8,27u8,73u8,171u8,238u8,37u8,198u8,20u8,255u8,123u8,103u8,119u8, - 117u8,183u8,157u8,18u8,63u8,201u8,171u8,153u8,179u8,103u8,206u8,156u8,25u8,205u8,102u8,51u8,88u8,109u8,17u8,238u8, - 95u8,176u8,208u8,144u8,139u8,216u8,100u8,8u8,49u8,38u8,188u8,64u8,5u8,172u8,128u8,208u8,189u8,248u8,194u8,138u8, - 56u8,195u8,7u8,44u8,80u8,50u8,45u8,100u8,8u8,122u8,203u8,52u8,112u8,5u8,70u8,97u8,12u8,90u8,64u8,36u8, - 145u8,105u8,28u8,205u8,8u8,173u8,155u8,17u8,42u8,216u8,113u8,189u8,5u8,83u8,240u8,63u8,6u8,225u8,225u8,233u8, - 241u8,78u8,77u8,225u8,81u8,67u8,36u8,10u8,205u8,120u8,65u8,87u8,208u8,163u8,41u8,52u8,74u8,72u8,132u8,36u8, - 88u8,132u8,194u8,228u8,27u8,148u8,14u8,74u8,36u8,67u8,52u8,174u8,33u8,245u8,44u8,144u8,128u8,150u8,125u8,126u8, - 97u8,143u8,145u8,133u8,237u8,0u8,18u8,152u8,195u8,68u8,27u8,175u8,0u8,115u8,174u8,181u8,143u8,100u8,176u8,117u8, - 233u8,84u8,110u8,236u8,206u8,235u8,24u8,122u8,103u8,243u8,221u8,63u8,80u8,84u8,55u8,78u8,71u8,149u8,66u8,172u8, - 212u8,66u8,173u8,19u8,201u8,114u8,220u8,9u8,249u8,28u8,4u8,62u8,232u8,239u8,8u8,232u8,71u8,20u8,40u8,60u8, - 14u8,130u8,77u8,164u8,230u8,163u8,230u8,232u8,40u8,39u8,53u8,156u8,130u8,172u8,40u8,85u8,84u8,34u8,57u8,18u8, - 135u8,163u8,64u8,22u8,185u8,106u8,230u8,175u8,6u8,137u8,205u8,111u8,140u8,116u8,133u8,100u8,43u8,93u8,214u8,117u8, - 89u8,101u8,169u8,149u8,85u8,29u8,38u8,218u8,186u8,246u8,5u8,77u8,224u8,199u8,41u8,124u8,167u8,66u8,101u8,213u8, - 126u8,5u8,145u8,141u8,30u8,10u8,65u8,218u8,122u8,188u8,105u8,147u8,248u8,105u8,10u8,63u8,73u8,22u8,150u8,162u8, - 75u8,177u8,69u8,118u8,226u8,108u8,90u8,41u8,197u8,11u8,58u8,21u8,181u8,208u8,44u8,107u8,123u8,209u8,64u8,91u8, - 35u8,109u8,89u8,89u8,82u8,87u8,99u8,224u8,133u8,139u8,45u8,153u8,210u8,254u8,22u8,165u8,165u8,137u8,52u8,116u8, - 154u8,188u8,40u8,9u8,93u8,139u8,28u8,86u8,1u8,196u8,82u8,148u8,240u8,222u8,183u8,230u8,150u8,80u8,148u8,127u8, - 172u8,218u8,80u8,179u8,92u8,157u8,190u8,185u8,227u8,0u8,71u8,186u8,110u8,51u8,57u8,57u8,159u8,54u8,0u8,149u8, - 59u8,3u8,48u8,215u8,87u8,147u8,30u8,236u8,18u8,210u8,76u8,108u8,88u8,150u8,237u8,107u8,123u8,63u8,222u8,85u8, - 38u8,62u8,139u8,230u8,58u8,238u8,198u8,192u8,99u8,29u8,218u8,110u8,61u8,41u8,132u8,83u8,179u8,102u8,249u8,213u8, - 150u8,39u8,171u8,86u8,87u8,121u8,248u8,78u8,127u8,67u8,197u8,211u8,208u8,129u8,149u8,102u8,147u8,241u8,232u8,210u8, - 219u8,100u8,12u8,137u8,41u8,160u8,192u8,221u8,218u8,101u8,172u8,125u8,198u8,98u8,168u8,221u8,101u8,75u8,108u8,28u8, - 244u8,212u8,94u8,221u8,118u8,212u8,60u8,251u8,162u8,167u8,212u8,135u8,73u8,239u8,220u8,66u8,183u8,39u8,135u8,97u8, - 221u8,247u8,214u8,103u8,141u8,59u8,221u8,182u8,40u8,217u8,62u8,19u8,44u8,134u8,48u8,87u8,105u8,8u8,27u8,210u8, - 87u8,241u8,34u8,133u8,208u8,147u8,95u8,75u8,76u8,194u8,119u8,10u8,158u8,113u8,239u8,38u8,182u8,186u8,119u8,218u8, - 41u8,222u8,21u8,109u8,251u8,235u8,171u8,62u8,46u8,183u8,69u8,10u8,224u8,34u8,55u8,122u8,80u8,217u8,4u8,232u8, - 230u8,0u8,86u8,227u8,78u8,137u8,59u8,201u8,53u8,174u8,181u8,168u8,132u8,116u8,64u8,20u8,122u8,73u8,51u8,30u8, - 4u8,116u8,188u8,217u8,211u8,74u8,186u8,188u8,104u8,145u8,167u8,182u8,240u8,241u8,4u8,58u8,39u8,21u8,85u8,7u8, - 63u8,158u8,55u8,208u8,170u8,196u8,104u8,32u8,38u8,83u8,202u8,228u8,120u8,34u8,151u8,106u8,248u8,8u8,139u8,27u8, - 248u8,182u8,252u8,181u8,126u8,186u8,190u8,106u8,65u8,14u8,237u8,227u8,137u8,172u8,155u8,51u8,80u8,243u8,97u8,59u8, - 126u8,160u8,54u8,210u8,79u8,161u8,117u8,131u8,37u8,34u8,34u8,206u8,236u8,160u8,184u8,222u8,56u8,115u8,119u8,212u8, - 26u8,170u8,110u8,139u8,254u8,143u8,222u8,125u8,173u8,201u8,111u8,23u8,238u8,166u8,86u8,128u8,161u8,136u8,175u8,112u8, - 140u8,140u8,148u8,214u8,55u8,117u8,73u8,111u8,165u8,91u8,229u8,189u8,149u8,49u8,45u8,131u8,14u8,223u8,99u8,105u8, - 135u8,140u8,191u8,138u8,180u8,114u8,51u8,83u8,142u8,119u8,232u8,2u8,67u8,98u8,232u8,125u8,127u8,204u8,27u8,123u8, - 43u8,4u8,120u8,76u8,127u8,120u8,194u8,41u8,128u8,6u8,34u8,180u8,178u8,248u8,113u8,47u8,152u8,230u8,180u8,98u8, - 109u8,49u8,167u8,45u8,122u8,122u8,212u8,95u8,232u8,91u8,33u8,228u8,194u8,124u8,38u8,179u8,59u8,46u8,126u8,195u8, - 213u8,198u8,239u8,124u8,70u8,238u8,144u8,40u8,136u8,125u8,187u8,123u8,186u8,95u8,129u8,142u8,148u8,177u8,143u8,59u8, - 187u8,98u8,252u8,249u8,112u8,187u8,140u8,95u8,89u8,47u8,237u8,74u8,89u8,79u8,170u8,213u8,185u8,134u8,67u8,227u8, - 229u8,218u8,187u8,135u8,209u8,63u8,48u8,88u8,147u8,58u8,197u8,8u8,0u8,0u8,0u8,0u8,13u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,115u8,105u8,103u8,110u8,101u8,114u8,227u8,2u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,141u8,82u8,187u8,110u8,195u8,48u8,12u8,220u8,253u8,21u8,28u8,27u8,32u8,136u8,119u8,103u8,234u8,210u8, - 177u8,232u8,208u8,61u8,161u8,101u8,218u8,86u8,35u8,139u8,134u8,30u8,113u8,138u8,162u8,255u8,94u8,74u8,78u8,208u8, - 186u8,143u8,52u8,134u8,61u8,152u8,186u8,59u8,242u8,78u8,44u8,203u8,18u8,158u8,28u8,31u8,117u8,67u8,30u8,16u8, - 20u8,15u8,3u8,91u8,24u8,13u8,42u8,130u8,150u8,29u8,208u8,105u8,100u8,23u8,180u8,237u8,96u8,175u8,28u8,97u8, - 160u8,157u8,215u8,157u8,37u8,183u8,7u8,84u8,142u8,189u8,135u8,208u8,19u8,220u8,143u8,129u8,61u8,60u8,56u8,28u8, - 104u8,98u8,119u8,216u8,20u8,101u8,89u8,166u8,15u8,158u8,25u8,162u8,39u8,88u8,48u8,215u8,128u8,77u8,147u8,105u8, - 3u8,55u8,209u8,16u8,212u8,100u8,120u8,90u8,131u8,143u8,170u8,151u8,50u8,134u8,42u8,83u8,247u8,173u8,211u8,100u8, - 27u8,192u8,164u8,189u8,107u8,47u8,218u8,85u8,53u8,215u8,119u8,19u8,218u8,224u8,119u8,203u8,153u8,50u8,113u8,234u8, - 201u8,209u8,133u8,254u8,43u8,12u8,180u8,255u8,218u8,63u8,245u8,4u8,75u8,212u8,248u8,239u8,30u8,63u8,141u8,60u8, - 114u8,160u8,245u8,140u8,12u8,189u8,208u8,229u8,101u8,107u8,94u8,1u8,143u8,168u8,13u8,214u8,162u8,50u8,105u8,57u8, - 176u8,255u8,196u8,145u8,168u8,116u8,210u8,62u8,200u8,0u8,12u8,3u8,30u8,8u8,48u8,54u8,58u8,231u8,235u8,131u8, - 67u8,221u8,245u8,33u8,197u8,62u8,161u8,19u8,231u8,226u8,94u8,80u8,70u8,15u8,58u8,100u8,221u8,52u8,99u8,170u8, - 52u8,52u8,138u8,181u8,172u8,40u8,119u8,133u8,74u8,113u8,180u8,33u8,29u8,244u8,120u8,164u8,244u8,79u8,62u8,235u8, - 167u8,73u8,55u8,197u8,217u8,229u8,143u8,28u8,23u8,86u8,225u8,173u8,0u8,121u8,254u8,202u8,252u8,220u8,99u8,123u8, - 29u8,148u8,11u8,55u8,65u8,59u8,178u8,228u8,181u8,191u8,14u8,26u8,162u8,9u8,90u8,198u8,187u8,77u8,146u8,235u8, - 23u8,82u8,130u8,201u8,160u8,49u8,214u8,70u8,171u8,187u8,25u8,187u8,2u8,139u8,65u8,75u8,50u8,109u8,180u8,203u8, - 85u8,188u8,147u8,77u8,116u8,85u8,218u8,71u8,39u8,145u8,173u8,42u8,152u8,203u8,219u8,226u8,189u8,248u8,0u8,227u8, - 69u8,202u8,242u8,24u8,3u8,0u8,0u8,0u8,0u8,8u8,99u8,104u8,97u8,105u8,110u8,95u8,105u8,100u8,170u8,4u8, - 31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,149u8,83u8,93u8,111u8,211u8,48u8,20u8,125u8,239u8,175u8, - 184u8,8u8,9u8,53u8,82u8,149u8,178u8,55u8,212u8,49u8,52u8,4u8,47u8,123u8,2u8,193u8,222u8,208u8,148u8,185u8, - 241u8,77u8,122u8,181u8,196u8,46u8,246u8,117u8,187u8,50u8,245u8,191u8,115u8,237u8,164u8,105u8,27u8,24u8,98u8,121u8, - 137u8,108u8,159u8,156u8,123u8,62u8,156u8,249u8,124u8,14u8,183u8,43u8,132u8,114u8,165u8,200u8,0u8,105u8,208u8,228u8, - 153u8,76u8,29u8,200u8,175u8,208u8,195u8,18u8,121u8,139u8,104u8,100u8,183u8,170u8,208u8,161u8,225u8,14u8,232u8,97u8, - 138u8,121u8,157u8,207u8,128u8,209u8,179u8,65u8,6u8,101u8,52u8,176u8,208u8,180u8,145u8,69u8,54u8,182u8,214u8,61u8, - 100u8,249u8,100u8,46u8,236u8,95u8,12u8,2u8,181u8,107u8,235u8,88u8,201u8,231u8,206u8,54u8,178u8,244u8,192u8,22u8, - 214u8,14u8,55u8,145u8,145u8,157u8,50u8,94u8,149u8,76u8,86u8,120u8,201u8,48u8,26u8,141u8,26u8,42u8,235u8,192u8, - 154u8,131u8,176u8,202u8,217u8,86u8,196u8,136u8,48u8,192u8,71u8,44u8,3u8,11u8,194u8,26u8,25u8,107u8,101u8,170u8, - 235u8,6u8,221u8,174u8,132u8,183u8,180u8,26u8,133u8,217u8,110u8,72u8,139u8,124u8,37u8,107u8,195u8,66u8,128u8,46u8, - 17u8,122u8,182u8,46u8,114u8,168u8,163u8,223u8,168u8,188u8,10u8,166u8,31u8,47u8,178u8,200u8,16u8,147u8,106u8,232u8, - 23u8,166u8,179u8,90u8,220u8,17u8,231u8,147u8,214u8,234u8,32u8,210u8,213u8,154u8,173u8,47u8,42u8,167u8,90u8,140u8, - 30u8,23u8,139u8,68u8,84u8,8u8,209u8,211u8,4u8,228u8,9u8,254u8,47u8,24u8,191u8,243u8,140u8,109u8,161u8,180u8, - 118u8,232u8,61u8,250u8,203u8,73u8,194u8,86u8,142u8,196u8,235u8,159u8,240u8,26u8,13u8,122u8,58u8,160u8,60u8,187u8, - 80u8,50u8,124u8,138u8,115u8,110u8,52u8,172u8,148u8,135u8,7u8,220u8,245u8,227u8,226u8,67u8,122u8,1u8,225u8,93u8, - 90u8,238u8,187u8,79u8,186u8,216u8,155u8,29u8,148u8,170u8,105u8,36u8,41u8,29u8,146u8,235u8,158u8,55u8,31u8,48u8, - 95u8,195u8,178u8,145u8,158u8,83u8,115u8,93u8,32u8,55u8,159u8,225u8,158u8,244u8,61u8,216u8,74u8,54u8,41u8,214u8, - 225u8,165u8,182u8,18u8,33u8,72u8,41u8,46u8,1u8,191u8,39u8,51u8,31u8,15u8,94u8,160u8,119u8,149u8,72u8,215u8, - 145u8,176u8,156u8,118u8,198u8,178u8,152u8,235u8,73u8,156u8,211u8,177u8,81u8,120u8,227u8,169u8,150u8,106u8,102u8,189u8, - 133u8,236u8,196u8,212u8,56u8,178u8,197u8,66u8,201u8,203u8,113u8,49u8,34u8,25u8,147u8,102u8,151u8,3u8,69u8,107u8, - 55u8,88u8,176u8,29u8,35u8,102u8,67u8,148u8,79u8,177u8,255u8,125u8,118u8,154u8,220u8,235u8,31u8,27u8,194u8,237u8, - 221u8,144u8,208u8,55u8,228u8,224u8,204u8,121u8,64u8,227u8,108u8,242u8,19u8,235u8,201u8,178u8,92u8,153u8,105u8,22u8, - 13u8,129u8,42u8,127u8,6u8,18u8,3u8,199u8,145u8,131u8,186u8,165u8,117u8,206u8,110u8,139u8,186u8,177u8,75u8,213u8, - 188u8,239u8,207u8,63u8,76u8,175u8,199u8,126u8,114u8,210u8,231u8,2u8,227u8,47u8,87u8,88u8,105u8,247u8,110u8,60u8, - 247u8,24u8,117u8,33u8,151u8,189u8,136u8,192u8,23u8,101u8,254u8,124u8,85u8,17u8,221u8,39u8,123u8,166u8,99u8,140u8, - 131u8,43u8,184u8,126u8,251u8,120u8,145u8,117u8,210u8,162u8,166u8,164u8,54u8,6u8,242u8,156u8,144u8,236u8,95u8,33u8, - 253u8,135u8,163u8,25u8,92u8,136u8,139u8,99u8,235u8,221u8,53u8,121u8,53u8,77u8,37u8,192u8,213u8,85u8,60u8,22u8, - 204u8,160u8,126u8,63u8,249u8,13u8,173u8,36u8,72u8,93u8,243u8,4u8,0u8,0u8,0u8,0u8,7u8,97u8,99u8,99u8, - 111u8,117u8,110u8,116u8,152u8,87u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,61u8,109u8,123u8, - 219u8,198u8,145u8,223u8,251u8,43u8,80u8,247u8,57u8,153u8,116u8,105u8,70u8,146u8,101u8,39u8,145u8,99u8,95u8,101u8, - 89u8,73u8,244u8,56u8,182u8,124u8,146u8,156u8,180u8,151u8,39u8,7u8,130u8,36u8,40u8,162u8,34u8,9u8,30u8,1u8, - 90u8,86u8,83u8,255u8,247u8,155u8,153u8,125u8,127u8,3u8,64u8,82u8,114u8,156u8,156u8,124u8,215u8,86u8,36u8,129u8, - 221u8,217u8,153u8,217u8,121u8,223u8,217u8,105u8,62u8,92u8,78u8,210u8,40u8,153u8,151u8,121u8,17u8,143u8,22u8,201u8, - 52u8,189u8,202u8,23u8,151u8,251u8,251u8,201u8,96u8,144u8,47u8,103u8,101u8,244u8,235u8,159u8,34u8,248u8,183u8,44u8, - 210u8,168u8,40u8,135u8,251u8,251u8,253u8,65u8,241u8,212u8,252u8,38u8,93u8,44u8,242u8,133u8,245u8,221u8,56u8,41u8, - 198u8,214u8,87u8,249u8,188u8,204u8,242u8,217u8,254u8,254u8,175u8,103u8,233u8,100u8,212u8,137u8,78u8,232u8,227u8,71u8, - 235u8,161u8,34u8,187u8,152u8,165u8,246u8,96u8,239u8,211u8,65u8,169u8,207u8,224u8,128u8,58u8,24u8,39u8,217u8,44u8, - 206u8,134u8,85u8,143u8,44u8,210u8,164u8,76u8,99u8,54u8,190u8,245u8,177u8,226u8,181u8,244u8,125u8,58u8,43u8,37u8, - 204u8,71u8,248u8,233u8,251u8,100u8,54u8,156u8,164u8,31u8,43u8,94u8,186u8,88u8,86u8,130u8,82u8,92u8,23u8,101u8, - 58u8,141u8,147u8,225u8,112u8,145u8,22u8,69u8,90u8,216u8,143u8,50u8,164u8,14u8,119u8,31u8,63u8,222u8,249u8,218u8, - 251u8,219u8,104u8,145u8,79u8,99u8,131u8,14u8,218u8,143u8,211u8,229u8,164u8,204u8,226u8,170u8,215u8,203u8,164u8,63u8, - 73u8,229u8,154u8,206u8,241u8,211u8,71u8,255u8,131u8,215u8,243u8,52u8,206u8,102u8,163u8,92u8,61u8,12u8,223u8,28u8, - 195u8,23u8,240u8,60u8,189u8,48u8,90u8,100u8,233u8,108u8,232u8,225u8,29u8,250u8,130u8,115u8,208u8,211u8,202u8,71u8, - 7u8,121u8,54u8,171u8,126u8,226u8,34u8,157u8,165u8,69u8,86u8,84u8,63u8,4u8,168u8,204u8,151u8,139u8,65u8,218u8, - 108u8,210u8,114u8,145u8,204u8,138u8,100u8,128u8,28u8,24u8,191u8,79u8,38u8,217u8,48u8,193u8,63u8,249u8,162u8,190u8, - 248u8,226u8,139u8,232u8,148u8,15u8,23u8,45u8,210u8,57u8,12u8,13u8,100u8,207u8,102u8,23u8,81u8,50u8,139u8,248u8, - 240u8,93u8,122u8,178u8,40u8,23u8,203u8,65u8,25u8,29u8,240u8,157u8,2u8,76u8,31u8,93u8,166u8,215u8,29u8,248u8, - 62u8,95u8,164u8,124u8,227u8,224u8,191u8,100u8,89u8,142u8,113u8,136u8,1u8,77u8,19u8,195u8,51u8,251u8,17u8,99u8, - 234u8,111u8,150u8,95u8,61u8,239u8,200u8,231u8,138u8,244u8,127u8,151u8,233u8,12u8,22u8,49u8,91u8,78u8,251u8,192u8, - 166u8,209u8,242u8,201u8,158u8,250u8,21u8,217u8,42u8,38u8,198u8,197u8,65u8,224u8,17u8,235u8,119u8,196u8,100u8,188u8, - 72u8,47u8,50u8,96u8,175u8,69u8,76u8,156u8,91u8,236u8,235u8,60u8,251u8,205u8,33u8,60u8,113u8,202u8,31u8,160u8, - 239u8,181u8,185u8,1u8,168u8,120u8,145u8,151u8,108u8,112u8,223u8,203u8,175u8,210u8,235u8,83u8,254u8,187u8,253u8,174u8, - 124u8,111u8,144u8,204u8,147u8,126u8,54u8,201u8,202u8,235u8,56u8,31u8,141u8,112u8,5u8,135u8,242u8,155u8,19u8,252u8, - 226u8,27u8,49u8,132u8,250u8,94u8,95u8,63u8,109u8,199u8,6u8,163u8,156u8,209u8,131u8,206u8,24u8,31u8,255u8,164u8, - 147u8,197u8,6u8,152u8,232u8,51u8,92u8,228u8,115u8,151u8,64u8,249u8,100u8,24u8,55u8,37u8,210u8,44u8,189u8,106u8, - 244u8,172u8,9u8,140u8,131u8,250u8,10u8,104u8,212u8,190u8,147u8,27u8,206u8,59u8,164u8,133u8,148u8,249u8,56u8,153u8, - 149u8,249u8,52u8,58u8,127u8,78u8,99u8,243u8,81u8,163u8,81u8,14u8,8u8,100u8,210u8,246u8,27u8,46u8,115u8,158u8, - 91u8,35u8,185u8,68u8,113u8,161u8,19u8,156u8,191u8,31u8,241u8,65u8,172u8,49u8,108u8,146u8,172u8,48u8,2u8,110u8, - 184u8,227u8,50u8,202u8,138u8,40u8,77u8,138u8,235u8,168u8,204u8,163u8,81u8,90u8,14u8,198u8,17u8,224u8,216u8,218u8, - 58u8,200u8,165u8,81u8,62u8,162u8,141u8,200u8,199u8,232u8,95u8,3u8,219u8,76u8,231u8,147u8,107u8,216u8,170u8,201u8, - 16u8,119u8,105u8,86u8,70u8,40u8,33u8,233u8,245u8,30u8,223u8,155u8,61u8,1u8,102u8,82u8,194u8,247u8,240u8,95u8, - 252u8,245u8,174u8,4u8,224u8,28u8,158u8,38u8,201u8,24u8,101u8,51u8,120u8,36u8,43u8,196u8,27u8,211u8,228u8,50u8, - 45u8,112u8,208u8,121u8,94u8,20u8,25u8,62u8,0u8,0u8,14u8,243u8,40u8,129u8,9u8,223u8,167u8,11u8,16u8,153u8, - 147u8,60u8,191u8,92u8,206u8,247u8,241u8,153u8,105u8,50u8,47u8,8u8,58u8,7u8,236u8,14u8,190u8,70u8,43u8,226u8, - 128u8,195u8,50u8,232u8,35u8,23u8,30u8,87u8,227u8,12u8,150u8,140u8,72u8,99u8,240u8,185u8,235u8,46u8,210u8,82u8, - 130u8,171u8,129u8,13u8,144u8,194u8,172u8,115u8,90u8,121u8,1u8,220u8,153u8,14u8,211u8,33u8,140u8,150u8,206u8,0u8, - 190u8,65u8,14u8,16u8,226u8,47u8,87u8,201u8,100u8,146u8,150u8,5u8,50u8,131u8,152u8,177u8,128u8,135u8,242u8,194u8, - 139u8,97u8,132u8,162u8,159u8,226u8,8u8,200u8,24u8,233u8,208u8,157u8,246u8,91u8,24u8,39u8,253u8,144u8,0u8,222u8, - 211u8,78u8,148u8,77u8,147u8,139u8,108u8,6u8,35u8,1u8,222u8,211u8,98u8,60u8,185u8,126u8,200u8,212u8,235u8,144u8, - 79u8,27u8,93u8,101u8,229u8,88u8,174u8,187u8,151u8,244u8,0u8,67u8,67u8,88u8,230u8,18u8,80u8,53u8,41u8,114u8, - 254u8,179u8,11u8,6u8,60u8,217u8,137u8,134u8,176u8,128u8,247u8,48u8,20u8,145u8,52u8,137u8,222u8,190u8,138u8,122u8, - 243u8,203u8,24u8,198u8,160u8,183u8,6u8,249u8,2u8,6u8,157u8,231u8,51u8,162u8,252u8,25u8,252u8,88u8,224u8,143u8, - 221u8,0u8,95u8,113u8,140u8,68u8,197u8,18u8,112u8,157u8,8u8,240u8,46u8,96u8,130u8,89u8,244u8,207u8,101u8,81u8, - 18u8,69u8,138u8,20u8,192u8,47u8,25u8,4u8,52u8,26u8,112u8,112u8,6u8,82u8,153u8,126u8,132u8,249u8,7u8,64u8, - 223u8,126u8,106u8,2u8,134u8,63u8,157u8,189u8,234u8,132u8,88u8,22u8,95u8,193u8,47u8,189u8,239u8,189u8,133u8,247u8, - 24u8,66u8,20u8,115u8,128u8,34u8,0u8,212u8,132u8,134u8,107u8,73u8,120u8,96u8,95u8,93u8,1u8,177u8,102u8,57u8, - 125u8,47u8,228u8,112u8,219u8,37u8,216u8,247u8,249u8,21u8,114u8,43u8,16u8,107u8,100u8,173u8,158u8,209u8,24u8,57u8, - 188u8,240u8,205u8,5u8,88u8,235u8,245u8,123u8,54u8,25u8,134u8,25u8,138u8,28u8,20u8,99u8,156u8,32u8,125u8,78u8, - 16u8,78u8,130u8,62u8,32u8,109u8,156u8,95u8,69u8,87u8,249u8,114u8,50u8,148u8,60u8,206u8,209u8,127u8,29u8,161u8, - 22u8,254u8,79u8,99u8,235u8,241u8,159u8,96u8,6u8,129u8,2u8,246u8,42u8,172u8,108u8,146u8,207u8,46u8,128u8,102u8, - 128u8,186u8,251u8,201u8,253u8,167u8,184u8,203u8,216u8,79u8,240u8,69u8,15u8,231u8,97u8,59u8,7u8,168u8,12u8,72u8, - 65u8,118u8,24u8,148u8,93u8,115u8,123u8,240u8,141u8,92u8,228u8,147u8,247u8,105u8,193u8,246u8,246u8,124u8,145u8,195u8, - 78u8,158u8,162u8,244u8,16u8,155u8,7u8,113u8,13u8,178u8,221u8,203u8,136u8,176u8,58u8,190u8,123u8,243u8,69u8,6u8, - 188u8,158u8,76u8,252u8,236u8,60u8,78u8,39u8,114u8,44u8,142u8,222u8,34u8,31u8,149u8,87u8,9u8,208u8,105u8,184u8, - 164u8,157u8,40u8,113u8,48u8,202u8,56u8,205u8,57u8,208u8,166u8,72u8,226u8,48u8,159u8,176u8,233u8,208u8,250u8,56u8, - 224u8,19u8,114u8,43u8,67u8,55u8,47u8,216u8,47u8,49u8,172u8,100u8,159u8,217u8,115u8,66u8,208u8,119u8,196u8,111u8, - 166u8,78u8,178u8,48u8,195u8,117u8,5u8,99u8,55u8,176u8,165u8,1u8,110u8,192u8,120u8,52u8,133u8,247u8,146u8,139u8, - 148u8,201u8,163u8,98u8,44u8,112u8,78u8,74u8,122u8,40u8,150u8,163u8,179u8,93u8,55u8,250u8,54u8,91u8,20u8,101u8, - 199u8,16u8,159u8,89u8,33u8,167u8,228u8,111u8,2u8,206u8,117u8,193u8,151u8,95u8,129u8,222u8,184u8,95u8,68u8,131u8, - 229u8,130u8,24u8,106u8,190u8,236u8,79u8,178u8,1u8,19u8,153u8,140u8,180u8,64u8,44u8,36u8,28u8,202u8,95u8,0u8, - 9u8,137u8,130u8,42u8,32u8,82u8,102u8,2u8,109u8,109u8,98u8,99u8,26u8,25u8,94u8,85u8,28u8,112u8,6u8,248u8, - 158u8,13u8,109u8,152u8,44u8,88u8,144u8,238u8,106u8,94u8,182u8,98u8,7u8,70u8,160u8,40u8,74u8,77u8,109u8,174u8, - 220u8,132u8,80u8,206u8,121u8,57u8,203u8,175u8,38u8,233u8,16u8,112u8,71u8,66u8,158u8,196u8,178u8,62u8,1u8,172u8, - 54u8,41u8,138u8,124u8,144u8,145u8,144u8,84u8,242u8,166u8,139u8,91u8,1u8,68u8,114u8,121u8,149u8,19u8,128u8,73u8, - 185u8,68u8,178u8,128u8,244u8,152u8,229u8,37u8,226u8,30u8,172u8,209u8,73u8,114u8,13u8,239u8,128u8,142u8,74u8,224u8, - 59u8,16u8,0u8,114u8,82u8,88u8,102u8,153u8,126u8,192u8,199u8,6u8,9u8,26u8,242u8,240u8,227u8,53u8,110u8,137u8, - 201u8,114u8,200u8,208u8,114u8,254u8,247u8,55u8,48u8,241u8,114u8,150u8,129u8,145u8,41u8,77u8,205u8,136u8,153u8,154u8, - 93u8,159u8,57u8,240u8,118u8,145u8,231u8,163u8,67u8,201u8,15u8,200u8,120u8,131u8,124u8,14u8,100u8,65u8,181u8,174u8, - 241u8,159u8,215u8,108u8,5u8,152u8,184u8,52u8,53u8,38u8,114u8,213u8,30u8,234u8,32u8,68u8,122u8,134u8,58u8,135u8, - 118u8,8u8,83u8,58u8,202u8,56u8,227u8,123u8,0u8,237u8,24u8,201u8,215u8,124u8,244u8,160u8,50u8,173u8,29u8,149u8, - 179u8,27u8,153u8,114u8,204u8,128u8,179u8,199u8,22u8,12u8,233u8,55u8,65u8,86u8,156u8,14u8,173u8,70u8,198u8,0u8, - 142u8,181u8,40u8,230u8,91u8,151u8,9u8,237u8,157u8,253u8,18u8,93u8,150u8,1u8,99u8,45u8,70u8,208u8,135u8,56u8, - 118u8,10u8,234u8,13u8,237u8,21u8,92u8,3u8,128u8,217u8,115u8,141u8,62u8,50u8,35u8,77u8,162u8,255u8,184u8,219u8, - 171u8,54u8,20u8,61u8,239u8,72u8,203u8,175u8,142u8,69u8,148u8,255u8,144u8,14u8,178u8,121u8,70u8,212u8,96u8,52u8, - 80u8,196u8,88u8,115u8,113u8,182u8,53u8,90u8,191u8,180u8,6u8,111u8,220u8,234u8,194u8,12u8,77u8,181u8,186u8,56u8, - 230u8,130u8,140u8,123u8,173u8,156u8,101u8,58u8,204u8,14u8,116u8,127u8,144u8,179u8,194u8,156u8,195u8,116u8,146u8,94u8, - 144u8,134u8,33u8,19u8,64u8,8u8,115u8,75u8,188u8,146u8,49u8,237u8,44u8,167u8,103u8,105u8,218u8,31u8,119u8,165u8, - 157u8,61u8,28u8,178u8,5u8,244u8,68u8,112u8,134u8,169u8,201u8,158u8,112u8,211u8,249u8,0u8,98u8,108u8,103u8,157u8, - 74u8,180u8,50u8,103u8,148u8,203u8,62u8,161u8,93u8,157u8,231u8,187u8,43u8,243u8,41u8,0u8,235u8,33u8,168u8,0u8, - 23u8,40u8,249u8,85u8,83u8,207u8,220u8,92u8,147u8,69u8,227u8,21u8,248u8,160u8,57u8,43u8,114u8,216u8,155u8,139u8, - 227u8,27u8,135u8,21u8,212u8,13u8,236u8,185u8,215u8,7u8,127u8,143u8,223u8,61u8,217u8,131u8,57u8,118u8,118u8,191u8, - 138u8,158u8,69u8,59u8,95u8,237u8,237u8,61u8,249u8,114u8,111u8,111u8,251u8,203u8,71u8,95u8,110u8,127u8,13u8,33u8, - 168u8,39u8,59u8,143u8,159u8,106u8,207u8,255u8,247u8,209u8,233u8,73u8,124u8,240u8,238u8,252u8,251u8,248u8,213u8,209u8, - 63u8,116u8,41u8,8u8,239u8,126u8,184u8,183u8,189u8,225u8,191u8,123u8,90u8,4u8,231u8,108u8,48u8,78u8,167u8,224u8, - 204u8,13u8,81u8,116u8,143u8,50u8,144u8,156u8,232u8,251u8,28u8,177u8,184u8,152u8,174u8,93u8,65u8,87u8,14u8,201u8, - 165u8,35u8,11u8,215u8,35u8,237u8,11u8,227u8,77u8,37u8,161u8,185u8,165u8,198u8,22u8,118u8,244u8,146u8,126u8,142u8, - 207u8,14u8,191u8,63u8,122u8,125u8,132u8,204u8,3u8,43u8,218u8,126u8,90u8,3u8,206u8,107u8,12u8,214u8,173u8,15u8, - 147u8,241u8,122u8,0u8,176u8,215u8,239u8,126u8,56u8,63u8,142u8,189u8,224u8,237u8,84u8,129u8,71u8,32u8,144u8,248u8, - 192u8,88u8,174u8,25u8,253u8,66u8,251u8,133u8,43u8,222u8,50u8,191u8,72u8,209u8,12u8,225u8,78u8,28u8,48u8,159u8, - 1u8,185u8,174u8,163u8,91u8,104u8,196u8,192u8,23u8,114u8,82u8,119u8,89u8,109u8,102u8,217u8,137u8,96u8,158u8,140u8, - 182u8,49u8,233u8,146u8,49u8,215u8,186u8,143u8,214u8,13u8,215u8,192u8,48u8,164u8,180u8,7u8,11u8,103u8,13u8,36u8, - 65u8,58u8,132u8,42u8,176u8,200u8,58u8,202u8,16u8,77u8,23u8,228u8,9u8,160u8,31u8,63u8,133u8,237u8,14u8,95u8, - 204u8,147u8,5u8,106u8,83u8,92u8,171u8,139u8,127u8,27u8,158u8,72u8,198u8,110u8,153u8,63u8,228u8,125u8,171u8,98u8, - 153u8,69u8,55u8,250u8,9u8,208u8,149u8,47u8,75u8,230u8,139u8,241u8,233u8,225u8,215u8,14u8,11u8,109u8,160u8,42u8, - 75u8,192u8,71u8,24u8,144u8,148u8,103u8,222u8,116u8,212u8,66u8,249u8,9u8,216u8,198u8,165u8,146u8,132u8,192u8,133u8, - 181u8,61u8,232u8,146u8,51u8,115u8,7u8,159u8,227u8,127u8,154u8,64u8,80u8,37u8,37u8,28u8,166u8,31u8,32u8,16u8, - 69u8,52u8,85u8,246u8,83u8,98u8,178u8,19u8,115u8,95u8,116u8,78u8,122u8,121u8,116u8,122u8,252u8,227u8,81u8,124u8, - 122u8,116u8,118u8,242u8,238u8,244u8,240u8,40u8,62u8,56u8,60u8,60u8,121u8,247u8,230u8,220u8,228u8,41u8,120u8,89u8, - 219u8,132u8,34u8,50u8,154u8,76u8,48u8,56u8,115u8,205u8,230u8,45u8,244u8,109u8,35u8,6u8,57u8,248u8,225u8,244u8, - 232u8,224u8,229u8,63u8,226u8,163u8,191u8,31u8,159u8,157u8,159u8,145u8,204u8,50u8,57u8,84u8,12u8,53u8,204u8,83u8, - 244u8,119u8,75u8,54u8,150u8,111u8,168u8,151u8,39u8,71u8,103u8,241u8,155u8,147u8,115u8,54u8,150u8,24u8,106u8,87u8, - 99u8,118u8,203u8,36u8,77u8,63u8,12u8,128u8,107u8,153u8,186u8,154u8,38u8,31u8,178u8,233u8,114u8,26u8,65u8,84u8, - 24u8,172u8,100u8,10u8,151u8,224u8,0u8,250u8,60u8,103u8,71u8,255u8,245u8,238u8,232u8,13u8,32u8,224u8,205u8,187u8, - 215u8,47u8,142u8,78u8,227u8,243u8,147u8,147u8,248u8,197u8,241u8,119u8,98u8,162u8,71u8,79u8,13u8,199u8,22u8,93u8, - 131u8,12u8,67u8,50u8,129u8,56u8,11u8,80u8,35u8,155u8,81u8,12u8,58u8,66u8,201u8,94u8,142u8,245u8,169u8,94u8, - 31u8,252u8,240u8,237u8,201u8,233u8,235u8,163u8,151u8,36u8,51u8,143u8,222u8,156u8,31u8,31u8,30u8,156u8,31u8,159u8, - 188u8,97u8,210u8,147u8,205u8,183u8,167u8,230u8,59u8,100u8,142u8,2u8,231u8,23u8,193u8,172u8,194u8,33u8,16u8,180u8, - 206u8,80u8,151u8,210u8,22u8,24u8,234u8,115u8,29u8,30u8,188u8,65u8,164u8,1u8,121u8,143u8,78u8,127u8,196u8,25u8, - 95u8,190u8,132u8,191u8,37u8,41u8,30u8,107u8,203u8,82u8,209u8,115u8,142u8,59u8,244u8,72u8,48u8,124u8,48u8,153u8, - 228u8,204u8,54u8,3u8,52u8,70u8,23u8,137u8,65u8,233u8,147u8,119u8,231u8,241u8,201u8,183u8,241u8,119u8,7u8,114u8, - 200u8,39u8,26u8,73u8,230u8,160u8,117u8,96u8,207u8,14u8,61u8,110u8,32u8,133u8,181u8,112u8,89u8,204u8,79u8,214u8, - 199u8,252u8,233u8,244u8,228u8,205u8,119u8,241u8,225u8,187u8,211u8,83u8,192u8,77u8,252u8,246u8,221u8,139u8,31u8,142u8, - 15u8,117u8,220u8,124u8,233u8,155u8,97u8,142u8,154u8,20u8,57u8,94u8,185u8,105u8,11u8,224u8,136u8,108u8,193u8,118u8, - 47u8,249u8,114u8,204u8,216u8,6u8,185u8,55u8,103u8,91u8,195u8,132u8,134u8,19u8,76u8,135u8,228u8,248u8,205u8,143u8, - 7u8,63u8,28u8,191u8,140u8,223u8,158u8,158u8,192u8,34u8,225u8,255u8,95u8,189u8,57u8,249u8,233u8,135u8,163u8,151u8, - 223u8,29u8,9u8,88u8,190u8,50u8,249u8,98u8,128u8,251u8,107u8,161u8,216u8,121u8,156u8,160u8,160u8,135u8,192u8,202u8, - 69u8,86u8,38u8,147u8,135u8,82u8,25u8,60u8,236u8,39u8,40u8,86u8,76u8,107u8,12u8,95u8,102u8,142u8,229u8,104u8, - 57u8,35u8,66u8,232u8,176u8,188u8,57u8,137u8,15u8,15u8,222u8,30u8,188u8,56u8,254u8,225u8,248u8,92u8,162u8,226u8, - 235u8,70u8,211u8,51u8,70u8,244u8,217u8,128u8,20u8,137u8,87u8,65u8,43u8,242u8,61u8,13u8,129u8,99u8,33u8,2u8, - 118u8,227u8,209u8,91u8,96u8,167u8,147u8,115u8,198u8,177u8,46u8,68u8,59u8,154u8,122u8,60u8,144u8,234u8,68u8,112u8, - 47u8,39u8,186u8,4u8,137u8,115u8,172u8,228u8,98u8,220u8,154u8,7u8,152u8,215u8,137u8,100u8,94u8,199u8,194u8,1u8, - 131u8,227u8,219u8,211u8,131u8,215u8,71u8,63u8,157u8,156u8,190u8,10u8,114u8,246u8,206u8,142u8,143u8,75u8,184u8,54u8, - 177u8,56u8,3u8,153u8,157u8,169u8,58u8,50u8,170u8,167u8,201u8,162u8,36u8,199u8,123u8,1u8,219u8,33u8,202u8,231u8, - 41u8,147u8,226u8,224u8,147u8,96u8,196u8,47u8,159u8,65u8,84u8,26u8,76u8,116u8,83u8,249u8,182u8,182u8,219u8,209u8, - 201u8,169u8,87u8,47u8,183u8,118u8,218u8,62u8,84u8,74u8,1u8,203u8,192u8,213u8,36u8,217u8,65u8,63u8,95u8,48u8, - 39u8,81u8,203u8,104u8,97u8,136u8,15u8,191u8,74u8,63u8,192u8,98u8,112u8,51u8,230u8,42u8,136u8,164u8,11u8,1u8, - 21u8,195u8,83u8,84u8,117u8,158u8,132u8,69u8,60u8,36u8,147u8,216u8,7u8,216u8,201u8,233u8,241u8,119u8,199u8,111u8, - 128u8,190u8,176u8,9u8,109u8,164u8,90u8,98u8,144u8,107u8,44u8,135u8,161u8,144u8,1u8,103u8,247u8,185u8,52u8,143u8, - 184u8,199u8,203u8,2u8,178u8,28u8,84u8,139u8,170u8,103u8,239u8,14u8,191u8,143u8,207u8,142u8,191u8,123u8,3u8,146u8, - 215u8,195u8,83u8,154u8,52u8,60u8,128u8,33u8,74u8,200u8,180u8,206u8,75u8,141u8,173u8,92u8,109u8,9u8,43u8,196u8, - 96u8,210u8,36u8,201u8,166u8,233u8,208u8,199u8,209u8,186u8,194u8,99u8,26u8,198u8,82u8,82u8,143u8,215u8,155u8,83u8, - 154u8,83u8,204u8,165u8,35u8,101u8,0u8,115u8,78u8,167u8,89u8,137u8,84u8,211u8,40u8,90u8,165u8,48u8,223u8,157u8, - 29u8,189u8,148u8,144u8,104u8,18u8,149u8,156u8,6u8,220u8,162u8,156u8,224u8,6u8,162u8,13u8,185u8,252u8,237u8,183u8, - 176u8,196u8,83u8,65u8,192u8,128u8,250u8,220u8,249u8,210u8,162u8,167u8,220u8,40u8,166u8,176u8,176u8,72u8,171u8,52u8, - 181u8,160u8,173u8,122u8,49u8,55u8,33u8,244u8,209u8,217u8,35u8,61u8,98u8,130u8,87u8,130u8,37u8,165u8,106u8,128u8, - 203u8,184u8,24u8,97u8,115u8,209u8,46u8,78u8,102u8,215u8,161u8,41u8,109u8,174u8,98u8,115u8,105u8,248u8,253u8,90u8, - 155u8,13u8,173u8,81u8,45u8,241u8,43u8,21u8,33u8,153u8,188u8,82u8,17u8,126u8,247u8,238u8,248u8,37u8,101u8,143u8, - 80u8,32u8,48u8,126u8,232u8,98u8,82u8,66u8,57u8,240u8,144u8,188u8,210u8,18u8,75u8,240u8,4u8,101u8,190u8,16u8, - 110u8,102u8,157u8,160u8,160u8,131u8,111u8,33u8,226u8,60u8,159u8,79u8,184u8,245u8,96u8,186u8,30u8,71u8,127u8,63u8, - 60u8,58u8,122u8,9u8,130u8,13u8,189u8,49u8,156u8,48u8,62u8,4u8,222u8,32u8,188u8,129u8,137u8,34u8,13u8,160u8, - 109u8,205u8,48u8,59u8,250u8,128u8,99u8,101u8,229u8,228u8,90u8,217u8,190u8,8u8,56u8,190u8,13u8,36u8,74u8,128u8, - 85u8,251u8,105u8,121u8,133u8,57u8,160u8,147u8,254u8,63u8,41u8,58u8,13u8,198u8,167u8,176u8,193u8,72u8,22u8,146u8, - 91u8,142u8,40u8,32u8,131u8,27u8,224u8,195u8,176u8,246u8,36u8,153u8,119u8,45u8,247u8,48u8,8u8,208u8,246u8,135u8, - 61u8,221u8,121u8,99u8,168u8,253u8,203u8,207u8,144u8,139u8,40u8,99u8,20u8,157u8,191u8,40u8,187u8,134u8,237u8,35u8, - 101u8,245u8,70u8,248u8,16u8,8u8,41u8,72u8,103u8,204u8,134u8,233u8,28u8,114u8,252u8,0u8,194u8,68u8,164u8,6u8, - 73u8,45u8,60u8,44u8,202u8,235u8,137u8,150u8,0u8,100u8,96u8,113u8,61u8,14u8,90u8,51u8,50u8,74u8,64u8,98u8, - 24u8,52u8,198u8,65u8,91u8,200u8,26u8,210u8,227u8,109u8,239u8,139u8,73u8,127u8,53u8,159u8,167u8,199u8,218u8,122u8, - 252u8,230u8,4u8,133u8,61u8,105u8,85u8,25u8,27u8,231u8,133u8,11u8,136u8,174u8,108u8,150u8,149u8,25u8,40u8,178u8, - 127u8,193u8,42u8,168u8,252u8,67u8,138u8,4u8,166u8,201u8,136u8,224u8,83u8,170u8,196u8,209u8,1u8,109u8,177u8,2u8, - 134u8,54u8,1u8,172u8,198u8,104u8,217u8,5u8,13u8,209u8,22u8,3u8,171u8,173u8,135u8,1u8,172u8,50u8,147u8,253u8, - 125u8,136u8,57u8,167u8,11u8,112u8,235u8,205u8,151u8,237u8,193u8,218u8,79u8,229u8,16u8,83u8,32u8,105u8,92u8,230u8, - 246u8,19u8,29u8,95u8,90u8,66u8,77u8,236u8,164u8,36u8,120u8,193u8,9u8,132u8,233u8,90u8,109u8,21u8,103u8,248u8, - 200u8,39u8,210u8,112u8,248u8,22u8,23u8,77u8,174u8,84u8,66u8,209u8,80u8,149u8,189u8,149u8,2u8,116u8,9u8,164u8, - 94u8,68u8,61u8,74u8,194u8,139u8,24u8,84u8,116u8,32u8,168u8,100u8,84u8,107u8,24u8,15u8,233u8,17u8,47u8,136u8, - 182u8,47u8,23u8,16u8,52u8,227u8,110u8,229u8,85u8,114u8,205u8,18u8,119u8,220u8,38u8,18u8,49u8,123u8,97u8,90u8, - 209u8,214u8,157u8,51u8,208u8,112u8,97u8,25u8,126u8,9u8,252u8,174u8,40u8,72u8,64u8,201u8,25u8,76u8,224u8,66u8, - 212u8,228u8,236u8,196u8,69u8,72u8,75u8,123u8,199u8,199u8,125u8,18u8,107u8,44u8,86u8,188u8,72u8,181u8,164u8,0u8, - 114u8,188u8,76u8,176u8,25u8,104u8,18u8,131u8,10u8,255u8,171u8,171u8,82u8,70u8,196u8,10u8,127u8,110u8,253u8,153u8, - 121u8,100u8,223u8,240u8,1u8,158u8,235u8,112u8,180u8,59u8,17u8,149u8,118u8,1u8,227u8,176u8,215u8,99u8,246u8,112u8, - 43u8,224u8,180u8,181u8,219u8,92u8,192u8,112u8,56u8,65u8,135u8,128u8,209u8,242u8,55u8,48u8,219u8,211u8,88u8,225u8, - 234u8,2u8,211u8,208u8,34u8,63u8,252u8,62u8,75u8,128u8,212u8,61u8,19u8,23u8,61u8,34u8,4u8,36u8,93u8,33u8, - 51u8,156u8,98u8,166u8,29u8,215u8,41u8,18u8,25u8,25u8,46u8,121u8,146u8,95u8,185u8,75u8,49u8,248u8,79u8,91u8, - 70u8,244u8,231u8,103u8,209u8,223u8,222u8,79u8,99u8,105u8,65u8,110u8,109u8,57u8,63u8,91u8,12u8,30u8,126u8,164u8, - 204u8,47u8,211u8,89u8,199u8,152u8,138u8,227u8,136u8,251u8,4u8,113u8,178u8,184u8,88u8,78u8,129u8,3u8,91u8,33u8, - 135u8,170u8,45u8,223u8,214u8,241u8,101u8,162u8,32u8,6u8,214u8,27u8,167u8,131u8,203u8,116u8,104u8,16u8,68u8,223u8, - 47u8,46u8,15u8,249u8,95u8,170u8,228u8,38u8,76u8,80u8,210u8,179u8,156u8,127u8,158u8,89u8,82u8,78u8,159u8,252u8, - 169u8,241u8,150u8,91u8,1u8,3u8,47u8,247u8,7u8,32u8,101u8,202u8,60u8,238u8,95u8,131u8,40u8,109u8,109u8,249u8, - 223u8,246u8,210u8,139u8,5u8,2u8,247u8,247u8,153u8,11u8,220u8,218u8,114u8,135u8,111u8,71u8,207u8,192u8,183u8,222u8, - 109u8,136u8,251u8,42u8,199u8,217u8,79u8,0u8,42u8,4u8,176u8,43u8,172u8,88u8,12u8,207u8,125u8,6u8,181u8,5u8, - 150u8,91u8,193u8,239u8,248u8,89u8,212u8,19u8,234u8,248u8,234u8,68u8,91u8,211u8,165u8,103u8,72u8,11u8,143u8,190u8, - 162u8,45u8,24u8,149u8,215u8,29u8,226u8,120u8,244u8,103u8,60u8,14u8,85u8,112u8,181u8,12u8,128u8,218u8,33u8,104u8, - 165u8,149u8,182u8,49u8,196u8,158u8,66u8,177u8,10u8,128u8,157u8,170u8,177u8,150u8,3u8,146u8,14u8,179u8,208u8,56u8, - 6u8,149u8,183u8,52u8,22u8,53u8,233u8,127u8,96u8,212u8,168u8,26u8,10u8,200u8,97u8,160u8,142u8,243u8,140u8,19u8, - 40u8,223u8,118u8,159u8,113u8,176u8,225u8,62u8,226u8,35u8,161u8,251u8,148u8,7u8,109u8,238u8,67u8,205u8,11u8,233u8, - 68u8,97u8,151u8,168u8,170u8,157u8,229u8,179u8,180u8,5u8,6u8,137u8,103u8,149u8,13u8,139u8,234u8,154u8,141u8,248u8, - 209u8,187u8,121u8,52u8,2u8,233u8,50u8,234u8,47u8,63u8,191u8,207u8,210u8,171u8,95u8,108u8,203u8,139u8,169u8,145u8, - 56u8,113u8,141u8,173u8,126u8,158u8,79u8,52u8,98u8,218u8,202u8,137u8,140u8,174u8,6u8,19u8,128u8,142u8,137u8,137u8, - 112u8,51u8,72u8,136u8,27u8,212u8,115u8,102u8,68u8,83u8,52u8,25u8,144u8,143u8,95u8,120u8,216u8,9u8,252u8,235u8, - 69u8,126u8,21u8,95u8,76u8,242u8,126u8,50u8,177u8,0u8,233u8,58u8,188u8,209u8,16u8,52u8,139u8,239u8,110u8,20u8, - 38u8,107u8,108u8,29u8,34u8,175u8,73u8,9u8,224u8,163u8,196u8,172u8,129u8,169u8,10u8,28u8,42u8,114u8,49u8,223u8, - 6u8,121u8,64u8,162u8,196u8,128u8,52u8,134u8,111u8,106u8,160u8,213u8,56u8,202u8,171u8,37u8,90u8,15u8,236u8,137u8, - 192u8,241u8,194u8,204u8,83u8,59u8,250u8,70u8,164u8,162u8,188u8,10u8,2u8,34u8,237u8,192u8,245u8,49u8,120u8,212u8, - 23u8,105u8,43u8,20u8,192u8,245u8,235u8,133u8,7u8,238u8,218u8,156u8,175u8,254u8,42u8,2u8,213u8,245u8,180u8,119u8, - 229u8,146u8,67u8,126u8,45u8,41u8,86u8,129u8,246u8,7u8,91u8,149u8,124u8,224u8,206u8,227u8,77u8,56u8,75u8,27u8, - 55u8,83u8,233u8,11u8,94u8,88u8,224u8,70u8,45u8,238u8,135u8,10u8,196u8,182u8,201u8,88u8,163u8,40u8,198u8,12u8, - 93u8,195u8,236u8,61u8,14u8,32u8,10u8,223u8,40u8,62u8,150u8,79u8,228u8,196u8,229u8,216u8,205u8,168u8,4u8,205u8, - 100u8,6u8,140u8,7u8,109u8,113u8,54u8,3u8,113u8,11u8,118u8,120u8,75u8,150u8,149u8,114u8,39u8,168u8,35u8,75u8, - 116u8,237u8,82u8,139u8,90u8,62u8,38u8,99u8,249u8,89u8,36u8,78u8,9u8,8u8,47u8,38u8,31u8,137u8,73u8,60u8, - 118u8,140u8,41u8,205u8,148u8,209u8,12u8,86u8,43u8,168u8,57u8,48u8,196u8,91u8,129u8,204u8,68u8,123u8,85u8,163u8, - 72u8,95u8,214u8,45u8,153u8,67u8,6u8,50u8,184u8,89u8,41u8,105u8,245u8,172u8,118u8,67u8,107u8,11u8,178u8,94u8, - 238u8,122u8,13u8,70u8,125u8,69u8,142u8,55u8,248u8,29u8,184u8,207u8,11u8,216u8,58u8,30u8,158u8,147u8,214u8,140u8, - 228u8,95u8,86u8,52u8,11u8,97u8,151u8,43u8,150u8,199u8,1u8,102u8,94u8,152u8,101u8,97u8,217u8,194u8,55u8,18u8, - 171u8,103u8,132u8,144u8,16u8,143u8,247u8,242u8,0u8,17u8,251u8,164u8,85u8,55u8,228u8,244u8,50u8,196u8,71u8,255u8, - 197u8,2u8,37u8,2u8,130u8,14u8,250u8,40u8,51u8,74u8,122u8,26u8,69u8,91u8,251u8,242u8,213u8,135u8,244u8,252u8, - 8u8,43u8,227u8,212u8,239u8,224u8,243u8,36u8,243u8,152u8,51u8,55u8,192u8,129u8,30u8,46u8,232u8,224u8,66u8,148u8, - 67u8,168u8,7u8,107u8,170u8,229u8,168u8,26u8,105u8,38u8,195u8,228u8,61u8,127u8,237u8,86u8,79u8,229u8,62u8,135u8, - 233u8,20u8,67u8,51u8,11u8,22u8,228u8,149u8,69u8,70u8,132u8,47u8,220u8,82u8,179u8,97u8,193u8,144u8,48u8,228u8, - 197u8,200u8,105u8,184u8,208u8,46u8,84u8,25u8,165u8,66u8,99u8,79u8,45u8,52u8,20u8,84u8,141u8,103u8,227u8,97u8, - 57u8,31u8,34u8,30u8,40u8,78u8,80u8,139u8,9u8,12u8,12u8,80u8,17u8,108u8,227u8,2u8,169u8,54u8,97u8,72u8, - 66u8,82u8,131u8,169u8,74u8,12u8,193u8,20u8,133u8,42u8,214u8,82u8,130u8,174u8,99u8,32u8,140u8,179u8,10u8,195u8, - 23u8,91u8,156u8,145u8,248u8,238u8,185u8,241u8,147u8,30u8,214u8,161u8,170u8,204u8,2u8,21u8,162u8,202u8,188u8,45u8, - 43u8,80u8,253u8,198u8,176u8,213u8,181u8,80u8,189u8,248u8,242u8,185u8,193u8,175u8,88u8,249u8,61u8,34u8,148u8,57u8, - 21u8,133u8,26u8,211u8,210u8,190u8,48u8,171u8,168u8,181u8,92u8,23u8,46u8,74u8,251u8,200u8,118u8,197u8,62u8,190u8, - 141u8,9u8,197u8,30u8,29u8,68u8,98u8,95u8,242u8,234u8,30u8,250u8,70u8,213u8,185u8,49u8,79u8,80u8,5u8,95u8, - 74u8,9u8,150u8,205u8,255u8,12u8,129u8,61u8,112u8,30u8,141u8,241u8,224u8,179u8,51u8,154u8,61u8,138u8,193u8,61u8, - 10u8,5u8,7u8,98u8,75u8,3u8,75u8,110u8,107u8,76u8,5u8,26u8,73u8,228u8,184u8,197u8,18u8,19u8,237u8,209u8, - 29u8,237u8,81u8,74u8,136u8,63u8,212u8,158u8,214u8,206u8,11u8,244u8,60u8,89u8,149u8,158u8,57u8,141u8,82u8,155u8, - 162u8,106u8,248u8,11u8,38u8,6u8,236u8,195u8,7u8,170u8,74u8,27u8,35u8,59u8,25u8,79u8,209u8,83u8,117u8,61u8, - 166u8,19u8,146u8,193u8,37u8,230u8,116u8,40u8,24u8,130u8,33u8,252u8,164u8,184u8,228u8,65u8,195u8,70u8,27u8,74u8, - 137u8,162u8,3u8,64u8,36u8,171u8,98u8,227u8,229u8,137u8,145u8,150u8,196u8,35u8,168u8,226u8,4u8,97u8,23u8,140u8, - 22u8,39u8,16u8,91u8,43u8,152u8,37u8,0u8,168u8,96u8,81u8,178u8,81u8,142u8,82u8,22u8,87u8,13u8,91u8,127u8, - 65u8,17u8,118u8,30u8,206u8,161u8,35u8,14u8,88u8,2u8,205u8,39u8,178u8,15u8,33u8,80u8,153u8,8u8,149u8,21u8, - 40u8,144u8,220u8,157u8,240u8,179u8,154u8,253u8,151u8,232u8,225u8,115u8,14u8,150u8,103u8,13u8,227u8,4u8,226u8,231u8, - 240u8,159u8,97u8,2u8,74u8,30u8,199u8,158u8,96u8,140u8,102u8,30u8,193u8,154u8,175u8,96u8,215u8,17u8,101u8,139u8, - 49u8,99u8,116u8,38u8,16u8,96u8,39u8,148u8,198u8,146u8,73u8,114u8,226u8,94u8,3u8,95u8,71u8,145u8,182u8,245u8, - 45u8,100u8,213u8,150u8,0u8,84u8,58u8,193u8,195u8,92u8,136u8,229u8,50u8,131u8,132u8,43u8,77u8,138u8,167u8,7u8, - 212u8,1u8,1u8,132u8,21u8,207u8,8u8,232u8,245u8,188u8,180u8,137u8,199u8,44u8,252u8,38u8,55u8,177u8,90u8,20u8, - 217u8,74u8,5u8,137u8,213u8,25u8,70u8,183u8,80u8,92u8,118u8,219u8,14u8,43u8,188u8,0u8,251u8,249u8,69u8,222u8, - 143u8,38u8,217u8,37u8,43u8,85u8,153u8,82u8,81u8,60u8,142u8,77u8,40u8,80u8,224u8,226u8,83u8,140u8,2u8,133u8, - 158u8,84u8,162u8,217u8,250u8,132u8,133u8,105u8,130u8,193u8,254u8,124u8,89u8,76u8,174u8,181u8,114u8,127u8,28u8,148u8, - 134u8,186u8,95u8,4u8,64u8,237u8,70u8,103u8,116u8,208u8,0u8,56u8,15u8,43u8,216u8,85u8,45u8,62u8,197u8,153u8, - 136u8,241u8,163u8,183u8,249u8,171u8,142u8,9u8,10u8,50u8,111u8,82u8,100u8,48u8,151u8,88u8,157u8,179u8,184u8,55u8, - 249u8,21u8,99u8,38u8,118u8,226u8,230u8,10u8,177u8,11u8,145u8,76u8,198u8,182u8,100u8,132u8,226u8,169u8,27u8,47u8, - 116u8,115u8,240u8,182u8,9u8,116u8,152u8,75u8,213u8,33u8,237u8,55u8,101u8,164u8,190u8,194u8,219u8,79u8,200u8,158u8, - 140u8,157u8,56u8,147u8,22u8,58u8,115u8,116u8,52u8,126u8,101u8,16u8,14u8,179u8,2u8,75u8,16u8,153u8,136u8,167u8, - 109u8,73u8,170u8,88u8,214u8,55u8,17u8,68u8,109u8,137u8,120u8,62u8,154u8,125u8,208u8,8u8,214u8,46u8,246u8,7u8, - 14u8,122u8,65u8,5u8,94u8,236u8,21u8,88u8,21u8,30u8,210u8,184u8,6u8,110u8,164u8,28u8,11u8,204u8,53u8,70u8, - 123u8,182u8,191u8,100u8,42u8,8u8,244u8,18u8,240u8,28u8,123u8,45u8,231u8,56u8,112u8,145u8,251u8,130u8,215u8,123u8, - 32u8,213u8,184u8,164u8,144u8,230u8,129u8,35u8,28u8,120u8,97u8,252u8,37u8,30u8,70u8,192u8,236u8,9u8,151u8,54u8, - 44u8,107u8,38u8,18u8,83u8,93u8,34u8,172u8,56u8,144u8,193u8,139u8,5u8,172u8,163u8,50u8,240u8,178u8,32u8,151u8, - 158u8,93u8,227u8,82u8,159u8,235u8,98u8,178u8,12u8,84u8,170u8,223u8,122u8,129u8,157u8,188u8,18u8,182u8,18u8,160u8, - 122u8,96u8,36u8,67u8,184u8,188u8,169u8,244u8,10u8,90u8,182u8,1u8,170u8,188u8,2u8,249u8,139u8,166u8,181u8,204u8, - 34u8,79u8,175u8,242u8,242u8,31u8,2u8,148u8,122u8,202u8,28u8,193u8,163u8,174u8,252u8,239u8,155u8,218u8,47u8,252u8, - 140u8,78u8,43u8,247u8,41u8,215u8,159u8,169u8,73u8,205u8,252u8,166u8,62u8,206u8,218u8,62u8,133u8,158u8,84u8,248u8, - 81u8,90u8,53u8,188u8,74u8,32u8,96u8,114u8,200u8,106u8,55u8,221u8,26u8,213u8,236u8,102u8,215u8,116u8,85u8,25u8, - 5u8,208u8,181u8,45u8,141u8,73u8,208u8,211u8,50u8,235u8,52u8,218u8,86u8,192u8,17u8,87u8,198u8,192u8,184u8,196u8, - 80u8,40u8,179u8,23u8,88u8,48u8,116u8,57u8,227u8,231u8,139u8,211u8,161u8,14u8,36u8,59u8,194u8,77u8,97u8,114u8, - 47u8,252u8,26u8,218u8,140u8,9u8,132u8,179u8,164u8,79u8,19u8,152u8,2u8,120u8,209u8,179u8,63u8,182u8,56u8,156u8, - 214u8,4u8,130u8,208u8,141u8,220u8,182u8,103u8,38u8,44u8,146u8,27u8,64u8,101u8,170u8,167u8,33u8,11u8,17u8,44u8, - 207u8,210u8,185u8,226u8,99u8,148u8,78u8,64u8,88u8,121u8,112u8,238u8,171u8,144u8,169u8,198u8,188u8,113u8,246u8,253u8, - 182u8,241u8,111u8,77u8,246u8,135u8,160u8,130u8,149u8,196u8,165u8,234u8,162u8,96u8,80u8,193u8,44u8,79u8,82u8,97u8, - 132u8,143u8,230u8,142u8,61u8,36u8,103u8,138u8,206u8,4u8,212u8,184u8,94u8,204u8,215u8,114u8,60u8,99u8,114u8,4u8, - 92u8,55u8,145u8,212u8,160u8,192u8,121u8,148u8,92u8,36u8,25u8,204u8,211u8,53u8,147u8,43u8,176u8,217u8,37u8,130u8, - 226u8,164u8,144u8,9u8,61u8,142u8,59u8,145u8,175u8,226u8,95u8,55u8,193u8,187u8,157u8,190u8,145u8,199u8,16u8,158u8, - 133u8,142u8,77u8,153u8,56u8,117u8,114u8,14u8,206u8,156u8,214u8,19u8,102u8,132u8,199u8,62u8,11u8,101u8,254u8,234u8, - 158u8,105u8,10u8,97u8,160u8,227u8,100u8,75u8,245u8,195u8,73u8,30u8,53u8,214u8,9u8,145u8,247u8,128u8,120u8,214u8, - 60u8,147u8,97u8,31u8,175u8,147u8,66u8,23u8,40u8,201u8,221u8,247u8,130u8,44u8,74u8,85u8,70u8,169u8,216u8,159u8, - 53u8,100u8,80u8,185u8,18u8,42u8,215u8,140u8,165u8,131u8,19u8,195u8,32u8,177u8,136u8,164u8,210u8,102u8,210u8,196u8, - 70u8,199u8,175u8,195u8,59u8,150u8,190u8,133u8,76u8,151u8,132u8,213u8,34u8,168u8,30u8,145u8,2u8,154u8,174u8,9u8, - 146u8,52u8,15u8,58u8,94u8,92u8,58u8,170u8,221u8,2u8,72u8,199u8,239u8,59u8,25u8,62u8,240u8,135u8,13u8,232u8, - 125u8,197u8,246u8,124u8,84u8,69u8,114u8,0u8,205u8,19u8,42u8,96u8,211u8,146u8,130u8,237u8,56u8,44u8,104u8,6u8, - 80u8,205u8,50u8,141u8,230u8,166u8,88u8,140u8,190u8,74u8,236u8,201u8,104u8,41u8,27u8,141u8,31u8,61u8,146u8,109u8, - 89u8,92u8,91u8,77u8,127u8,61u8,230u8,5u8,99u8,21u8,103u8,87u8,200u8,29u8,243u8,216u8,101u8,38u8,131u8,215u8, - 25u8,102u8,33u8,163u8,107u8,61u8,155u8,203u8,181u8,161u8,170u8,22u8,229u8,181u8,173u8,170u8,75u8,244u8,236u8,186u8, - 139u8,67u8,116u8,206u8,100u8,220u8,106u8,145u8,242u8,52u8,26u8,186u8,241u8,193u8,186u8,94u8,30u8,227u8,226u8,192u8, - 220u8,47u8,180u8,35u8,219u8,220u8,80u8,19u8,1u8,7u8,65u8,51u8,83u8,210u8,74u8,74u8,42u8,9u8,235u8,49u8, - 48u8,45u8,122u8,91u8,91u8,79u8,98u8,162u8,198u8,70u8,84u8,246u8,97u8,37u8,34u8,93u8,43u8,86u8,100u8,54u8, - 49u8,33u8,129u8,218u8,162u8,181u8,21u8,154u8,178u8,27u8,76u8,196u8,118u8,1u8,15u8,176u8,91u8,237u8,245u8,122u8, - 201u8,86u8,91u8,197u8,216u8,182u8,211u8,245u8,134u8,184u8,14u8,105u8,169u8,32u8,208u8,159u8,64u8,91u8,249u8,210u8, - 151u8,46u8,46u8,130u8,42u8,171u8,138u8,96u8,43u8,169u8,178u8,106u8,253u8,229u8,219u8,238u8,65u8,5u8,70u8,30u8, - 69u8,150u8,22u8,42u8,210u8,27u8,180u8,79u8,88u8,178u8,160u8,143u8,231u8,3u8,212u8,17u8,114u8,86u8,106u8,21u8, - 58u8,192u8,203u8,67u8,39u8,108u8,225u8,24u8,78u8,206u8,187u8,146u8,34u8,183u8,160u8,120u8,148u8,0u8,236u8,248u8, - 177u8,112u8,227u8,170u8,71u8,212u8,105u8,97u8,216u8,71u8,182u8,52u8,240u8,85u8,156u8,47u8,11u8,163u8,203u8,130u8, - 30u8,22u8,89u8,85u8,2u8,152u8,94u8,98u8,67u8,41u8,176u8,154u8,102u8,172u8,228u8,212u8,32u8,160u8,85u8,154u8, - 83u8,22u8,101u8,251u8,207u8,216u8,230u8,216u8,28u8,4u8,8u8,65u8,39u8,83u8,122u8,178u8,20u8,78u8,52u8,141u8, - 17u8,65u8,189u8,82u8,53u8,158u8,168u8,58u8,141u8,123u8,160u8,226u8,128u8,20u8,102u8,228u8,91u8,180u8,234u8,132u8, - 47u8,157u8,86u8,192u8,200u8,19u8,63u8,218u8,193u8,167u8,73u8,74u8,250u8,178u8,204u8,166u8,88u8,184u8,108u8,156u8, - 123u8,151u8,179u8,241u8,115u8,76u8,242u8,84u8,89u8,80u8,207u8,116u8,168u8,180u8,143u8,241u8,129u8,158u8,125u8,38u8, - 91u8,94u8,37u8,74u8,42u8,20u8,21u8,26u8,21u8,70u8,38u8,153u8,234u8,68u8,171u8,80u8,129u8,49u8,245u8,14u8, - 60u8,226u8,145u8,232u8,176u8,145u8,68u8,184u8,64u8,5u8,208u8,129u8,135u8,201u8,115u8,146u8,2u8,33u8,43u8,204u8, - 156u8,99u8,86u8,168u8,92u8,160u8,191u8,155u8,133u8,10u8,136u8,82u8,2u8,69u8,156u8,96u8,38u8,83u8,120u8,2u8, - 59u8,27u8,4u8,196u8,61u8,68u8,232u8,195u8,124u8,244u8,112u8,192u8,180u8,53u8,255u8,4u8,113u8,187u8,123u8,60u8, - 8u8,215u8,245u8,55u8,219u8,193u8,52u8,20u8,110u8,166u8,37u8,32u8,18u8,123u8,30u8,17u8,209u8,32u8,212u8,206u8, - 101u8,23u8,143u8,87u8,242u8,80u8,165u8,200u8,162u8,77u8,35u8,158u8,153u8,210u8,14u8,21u8,96u8,90u8,98u8,89u8, - 14u8,114u8,164u8,43u8,250u8,104u8,25u8,138u8,177u8,68u8,157u8,102u8,97u8,226u8,11u8,227u8,205u8,143u8,183u8,255u8, - 131u8,26u8,166u8,112u8,180u8,169u8,56u8,57u8,59u8,154u8,148u8,207u8,12u8,35u8,31u8,151u8,82u8,148u8,0u8,45u8, - 134u8,243u8,94u8,131u8,172u8,4u8,221u8,40u8,130u8,206u8,5u8,204u8,214u8,207u8,135u8,216u8,0u8,67u8,180u8,187u8, - 16u8,16u8,79u8,179u8,139u8,49u8,149u8,179u8,46u8,103u8,147u8,229u8,224u8,146u8,37u8,102u8,232u8,180u8,227u8,76u8, - 192u8,137u8,9u8,5u8,10u8,67u8,151u8,99u8,222u8,237u8,136u8,167u8,49u8,20u8,190u8,101u8,11u8,24u8,64u8,11u8, - 132u8,239u8,39u8,66u8,228u8,80u8,242u8,78u8,188u8,136u8,73u8,131u8,43u8,22u8,208u8,159u8,207u8,211u8,89u8,151u8, - 98u8,237u8,242u8,164u8,188u8,126u8,146u8,6u8,254u8,135u8,225u8,153u8,170u8,86u8,211u8,15u8,233u8,96u8,201u8,242u8, - 28u8,90u8,4u8,152u8,253u8,38u8,97u8,54u8,152u8,147u8,189u8,129u8,35u8,205u8,19u8,204u8,236u8,129u8,184u8,185u8, - 160u8,252u8,144u8,198u8,49u8,70u8,7u8,21u8,21u8,156u8,78u8,101u8,137u8,61u8,107u8,215u8,67u8,179u8,81u8,187u8, - 43u8,8u8,234u8,226u8,42u8,100u8,217u8,120u8,250u8,1u8,64u8,214u8,24u8,88u8,48u8,167u8,88u8,187u8,119u8,2u8, - 57u8,209u8,223u8,176u8,250u8,127u8,26u8,85u8,110u8,15u8,68u8,117u8,163u8,172u8,52u8,207u8,70u8,55u8,238u8,45u8, - 209u8,181u8,193u8,16u8,2u8,85u8,156u8,229u8,229u8,243u8,202u8,236u8,156u8,62u8,105u8,139u8,199u8,94u8,64u8,219u8, - 152u8,193u8,152u8,118u8,112u8,88u8,91u8,29u8,138u8,9u8,52u8,173u8,109u8,77u8,66u8,43u8,115u8,198u8,115u8,228u8, - 141u8,24u8,200u8,234u8,71u8,34u8,159u8,147u8,95u8,120u8,4u8,219u8,67u8,100u8,76u8,222u8,215u8,129u8,29u8,221u8, - 90u8,80u8,80u8,188u8,90u8,160u8,74u8,144u8,234u8,5u8,43u8,117u8,78u8,224u8,253u8,171u8,144u8,169u8,48u8,19u8, - 228u8,147u8,153u8,32u8,129u8,225u8,21u8,96u8,183u8,1u8,124u8,26u8,45u8,39u8,44u8,203u8,202u8,205u8,201u8,64u8, - 36u8,158u8,102u8,175u8,246u8,242u8,194u8,145u8,248u8,74u8,166u8,243u8,251u8,103u8,38u8,131u8,152u8,94u8,94u8,136u8, - 202u8,254u8,145u8,234u8,250u8,28u8,220u8,114u8,177u8,144u8,51u8,253u8,138u8,81u8,117u8,221u8,80u8,99u8,39u8,90u8, - 149u8,186u8,18u8,204u8,171u8,213u8,115u8,52u8,177u8,0u8,120u8,71u8,33u8,67u8,189u8,223u8,72u8,49u8,16u8,14u8, - 192u8,76u8,87u8,159u8,7u8,82u8,215u8,232u8,194u8,116u8,73u8,84u8,127u8,14u8,249u8,215u8,62u8,72u8,224u8,150u8, - 229u8,117u8,108u8,24u8,103u8,243u8,117u8,201u8,48u8,159u8,112u8,232u8,23u8,116u8,48u8,84u8,33u8,134u8,38u8,64u8, - 87u8,145u8,146u8,94u8,105u8,107u8,100u8,43u8,44u8,177u8,217u8,40u8,97u8,1u8,187u8,196u8,74u8,36u8,52u8,139u8, - 151u8,135u8,118u8,153u8,39u8,100u8,46u8,78u8,160u8,222u8,64u8,218u8,130u8,65u8,187u8,89u8,188u8,220u8,1u8,167u8, - 19u8,14u8,110u8,87u8,5u8,205u8,157u8,117u8,6u8,228u8,152u8,141u8,91u8,229u8,183u8,105u8,232u8,172u8,148u8,129u8, - 129u8,245u8,202u8,81u8,213u8,136u8,140u8,201u8,98u8,136u8,180u8,103u8,131u8,50u8,46u8,91u8,91u8,129u8,113u8,193u8, - 221u8,99u8,168u8,236u8,216u8,59u8,178u8,221u8,169u8,15u8,245u8,187u8,167u8,219u8,3u8,169u8,28u8,151u8,33u8,155u8, - 102u8,115u8,36u8,91u8,174u8,149u8,204u8,217u8,144u8,57u8,55u8,202u8,233u8,252u8,78u8,89u8,212u8,131u8,231u8,27u8, - 99u8,84u8,107u8,236u8,207u8,146u8,93u8,111u8,35u8,231u8,165u8,185u8,178u8,245u8,78u8,49u8,218u8,175u8,243u8,101u8, - 73u8,61u8,253u8,200u8,157u8,173u8,104u8,191u8,192u8,131u8,176u8,50u8,161u8,98u8,105u8,106u8,17u8,225u8,44u8,174u8, - 48u8,106u8,177u8,136u8,71u8,96u8,254u8,181u8,168u8,20u8,127u8,213u8,8u8,167u8,107u8,158u8,56u8,193u8,140u8,211u8, - 244u8,125u8,126u8,89u8,231u8,175u8,179u8,36u8,61u8,54u8,17u8,197u8,115u8,97u8,120u8,16u8,16u8,95u8,130u8,144u8, - 150u8,107u8,125u8,82u8,116u8,77u8,70u8,62u8,2u8,217u8,6u8,122u8,221u8,107u8,111u8,186u8,101u8,224u8,230u8,140u8, - 206u8,121u8,184u8,42u8,251u8,206u8,181u8,215u8,188u8,131u8,109u8,82u8,9u8,177u8,130u8,245u8,184u8,121u8,53u8,118u8, - 48u8,2u8,190u8,114u8,228u8,187u8,57u8,30u8,26u8,133u8,191u8,149u8,57u8,69u8,132u8,77u8,102u8,215u8,85u8,196u8, - 13u8,50u8,33u8,214u8,108u8,135u8,153u8,144u8,215u8,21u8,169u8,214u8,3u8,158u8,83u8,7u8,46u8,147u8,213u8,192u8, - 162u8,29u8,186u8,174u8,115u8,20u8,86u8,34u8,93u8,5u8,71u8,104u8,248u8,18u8,148u8,36u8,231u8,127u8,80u8,174u8, - 179u8,201u8,131u8,225u8,73u8,79u8,175u8,142u8,79u8,22u8,156u8,116u8,231u8,110u8,22u8,154u8,84u8,117u8,185u8,238u8, - 121u8,55u8,173u8,42u8,136u8,187u8,233u8,13u8,251u8,67u8,214u8,116u8,172u8,21u8,135u8,106u8,104u8,90u8,211u8,226u8, - 233u8,109u8,30u8,186u8,232u8,133u8,108u8,153u8,222u8,202u8,81u8,11u8,159u8,175u8,223u8,48u8,96u8,225u8,210u8,163u8, - 62u8,92u8,81u8,61u8,239u8,204u8,139u8,75u8,63u8,69u8,68u8,121u8,162u8,29u8,198u8,144u8,51u8,172u8,29u8,206u8, - 192u8,227u8,3u8,90u8,52u8,68u8,173u8,155u8,166u8,82u8,149u8,174u8,172u8,47u8,18u8,13u8,161u8,215u8,51u8,250u8, - 150u8,224u8,160u8,170u8,93u8,25u8,51u8,113u8,248u8,180u8,73u8,196u8,196u8,101u8,238u8,207u8,32u8,94u8,210u8,44u8, - 92u8,98u8,186u8,212u8,159u8,71u8,224u8,228u8,237u8,138u8,129u8,19u8,175u8,116u8,178u8,194u8,38u8,221u8,154u8,176u8, - 71u8,179u8,238u8,158u8,107u8,228u8,97u8,77u8,4u8,183u8,171u8,2u8,26u8,171u8,132u8,50u8,196u8,159u8,194u8,100u8, - 39u8,145u8,24u8,243u8,174u8,171u8,173u8,170u8,73u8,44u8,230u8,235u8,4u8,25u8,174u8,83u8,197u8,215u8,174u8,229u8, - 31u8,204u8,82u8,74u8,91u8,59u8,212u8,109u8,202u8,182u8,180u8,67u8,207u8,9u8,59u8,91u8,98u8,166u8,187u8,137u8, - 66u8,55u8,233u8,242u8,116u8,29u8,107u8,61u8,112u8,136u8,187u8,145u8,173u8,174u8,159u8,70u8,101u8,38u8,19u8,182u8, - 63u8,1u8,230u8,94u8,44u8,83u8,209u8,43u8,76u8,83u8,225u8,82u8,239u8,224u8,96u8,61u8,222u8,150u8,42u8,128u8, - 40u8,167u8,169u8,78u8,86u8,196u8,1u8,80u8,211u8,97u8,75u8,31u8,216u8,57u8,245u8,189u8,161u8,13u8,165u8,153u8, - 190u8,218u8,44u8,30u8,100u8,35u8,132u8,144u8,75u8,242u8,88u8,190u8,21u8,56u8,110u8,55u8,194u8,103u8,184u8,31u8, - 185u8,214u8,224u8,43u8,200u8,114u8,162u8,7u8,91u8,144u8,20u8,93u8,239u8,89u8,114u8,63u8,208u8,216u8,96u8,33u8, - 136u8,111u8,105u8,63u8,221u8,50u8,202u8,109u8,175u8,99u8,61u8,212u8,135u8,92u8,139u8,80u8,179u8,46u8,221u8,74u8, - 126u8,32u8,166u8,102u8,128u8,111u8,64u8,116u8,203u8,225u8,173u8,213u8,253u8,154u8,32u8,233u8,121u8,93u8,166u8,94u8, - 212u8,202u8,186u8,105u8,183u8,99u8,176u8,13u8,242u8,137u8,153u8,85u8,175u8,230u8,23u8,101u8,138u8,227u8,17u8,17u8, - 113u8,140u8,134u8,207u8,36u8,47u8,178u8,17u8,217u8,160u8,118u8,181u8,191u8,227u8,26u8,36u8,119u8,46u8,245u8,141u8, - 184u8,212u8,149u8,194u8,123u8,13u8,135u8,218u8,97u8,253u8,128u8,35u8,29u8,36u8,104u8,165u8,27u8,29u8,98u8,184u8, - 245u8,157u8,232u8,122u8,198u8,250u8,140u8,93u8,232u8,42u8,1u8,225u8,65u8,35u8,170u8,2u8,113u8,1u8,20u8,175u8, - 16u8,225u8,8u8,229u8,42u8,129u8,151u8,14u8,117u8,66u8,169u8,222u8,74u8,73u8,98u8,4u8,199u8,168u8,190u8,196u8, - 169u8,219u8,212u8,235u8,170u8,220u8,110u8,119u8,10u8,44u8,217u8,200u8,206u8,217u8,227u8,161u8,50u8,91u8,213u8,64u8, - 106u8,165u8,93u8,126u8,91u8,5u8,174u8,171u8,226u8,79u8,246u8,56u8,101u8,240u8,116u8,215u8,212u8,117u8,225u8,2u8, - 179u8,117u8,115u8,194u8,27u8,75u8,144u8,224u8,153u8,172u8,74u8,129u8,97u8,183u8,30u8,227u8,252u8,96u8,175u8,207u8, - 98u8,241u8,155u8,250u8,167u8,149u8,103u8,77u8,230u8,72u8,30u8,174u8,163u8,248u8,21u8,101u8,225u8,150u8,15u8,221u8, - 91u8,129u8,4u8,183u8,200u8,154u8,37u8,151u8,154u8,103u8,29u8,85u8,122u8,210u8,42u8,251u8,107u8,126u8,45u8,29u8, - 27u8,216u8,131u8,254u8,202u8,83u8,179u8,41u8,204u8,175u8,70u8,234u8,119u8,165u8,148u8,239u8,26u8,199u8,211u8,26u8, - 100u8,210u8,154u8,166u8,58u8,229u8,151u8,27u8,164u8,53u8,121u8,78u8,232u8,178u8,19u8,61u8,208u8,83u8,65u8,234u8, - 34u8,218u8,245u8,243u8,65u8,248u8,111u8,221u8,212u8,223u8,101u8,219u8,151u8,5u8,93u8,35u8,251u8,185u8,246u8,49u8, - 182u8,198u8,116u8,106u8,154u8,239u8,171u8,163u8,86u8,243u8,220u8,222u8,45u8,211u8,108u8,179u8,164u8,173u8,75u8,185u8, - 155u8,73u8,8u8,218u8,166u8,65u8,243u8,98u8,106u8,81u8,55u8,190u8,66u8,69u8,245u8,4u8,171u8,43u8,75u8,231u8, - 224u8,53u8,55u8,217u8,88u8,25u8,33u8,54u8,188u8,71u8,57u8,202u8,239u8,159u8,235u8,74u8,185u8,183u8,90u8,97u8, - 180u8,115u8,45u8,151u8,120u8,196u8,115u8,28u8,199u8,214u8,99u8,251u8,172u8,45u8,216u8,129u8,221u8,202u8,79u8,47u8, - 154u8,142u8,121u8,215u8,163u8,170u8,195u8,206u8,141u8,206u8,56u8,243u8,246u8,179u8,225u8,94u8,100u8,238u8,40u8,207u8, - 91u8,118u8,39u8,208u8,118u8,87u8,27u8,234u8,233u8,234u8,103u8,53u8,154u8,157u8,209u8,112u8,140u8,155u8,194u8,199u8, - 35u8,63u8,27u8,51u8,254u8,66u8,33u8,252u8,52u8,163u8,208u8,227u8,114u8,134u8,77u8,98u8,176u8,50u8,21u8,19u8, - 10u8,244u8,39u8,229u8,92u8,61u8,100u8,236u8,117u8,245u8,217u8,142u8,71u8,16u8,215u8,188u8,95u8,104u8,239u8,4u8, - 152u8,13u8,163u8,76u8,211u8,252u8,189u8,168u8,138u8,230u8,61u8,105u8,140u8,177u8,78u8,16u8,148u8,171u8,172u8,192u8, - 64u8,31u8,109u8,154u8,204u8,25u8,92u8,191u8,56u8,210u8,57u8,1u8,128u8,18u8,147u8,183u8,10u8,150u8,166u8,144u8, - 134u8,253u8,142u8,137u8,241u8,182u8,45u8,62u8,217u8,106u8,100u8,40u8,194u8,184u8,116u8,20u8,62u8,138u8,106u8,97u8, - 113u8,207u8,21u8,12u8,49u8,79u8,50u8,110u8,23u8,242u8,15u8,172u8,251u8,137u8,248u8,208u8,167u8,54u8,57u8,172u8, - 34u8,195u8,158u8,169u8,42u8,214u8,147u8,131u8,100u8,227u8,93u8,34u8,138u8,100u8,202u8,46u8,178u8,154u8,164u8,198u8, - 164u8,108u8,203u8,21u8,198u8,108u8,93u8,123u8,14u8,86u8,184u8,78u8,5u8,191u8,40u8,113u8,101u8,51u8,162u8,171u8, - 244u8,254u8,34u8,149u8,245u8,158u8,78u8,220u8,72u8,246u8,16u8,242u8,82u8,17u8,87u8,52u8,3u8,19u8,107u8,144u8, - 250u8,150u8,4u8,128u8,207u8,134u8,201u8,98u8,200u8,132u8,69u8,42u8,239u8,178u8,196u8,117u8,168u8,176u8,17u8,136u8, - 36u8,90u8,128u8,86u8,96u8,76u8,171u8,226u8,206u8,136u8,172u8,32u8,159u8,128u8,224u8,153u8,56u8,203u8,18u8,55u8, - 223u8,176u8,128u8,59u8,91u8,26u8,246u8,209u8,162u8,195u8,101u8,192u8,41u8,5u8,191u8,245u8,134u8,174u8,134u8,213u8, - 10u8,41u8,124u8,119u8,15u8,73u8,250u8,246u8,89u8,62u8,199u8,137u8,163u8,104u8,243u8,178u8,91u8,63u8,47u8,96u8, - 103u8,79u8,19u8,220u8,127u8,188u8,124u8,94u8,52u8,28u8,97u8,139u8,0u8,195u8,183u8,223u8,86u8,43u8,76u8,141u8, - 245u8,25u8,212u8,98u8,253u8,243u8,168u8,211u8,134u8,188u8,78u8,180u8,176u8,231u8,68u8,91u8,150u8,250u8,212u8,209u8, - 213u8,16u8,220u8,31u8,17u8,247u8,177u8,168u8,173u8,192u8,151u8,141u8,87u8,193u8,96u8,145u8,255u8,148u8,202u8,232u8, - 233u8,198u8,163u8,153u8,246u8,212u8,85u8,114u8,93u8,116u8,61u8,44u8,239u8,16u8,95u8,94u8,141u8,76u8,76u8,82u8, - 56u8,37u8,193u8,200u8,31u8,87u8,57u8,63u8,140u8,173u8,119u8,133u8,202u8,120u8,12u8,137u8,109u8,95u8,145u8,20u8, - 41u8,202u8,116u8,222u8,177u8,231u8,181u8,47u8,149u8,233u8,139u8,158u8,33u8,38u8,169u8,60u8,60u8,216u8,245u8,90u8, - 19u8,182u8,164u8,66u8,163u8,137u8,11u8,3u8,38u8,120u8,170u8,68u8,129u8,207u8,247u8,169u8,184u8,163u8,194u8,40u8, - 2u8,50u8,165u8,239u8,25u8,8u8,246u8,94u8,168u8,149u8,140u8,144u8,188u8,32u8,236u8,171u8,5u8,171u8,231u8,60u8, - 151u8,87u8,59u8,120u8,244u8,158u8,6u8,26u8,95u8,62u8,60u8,108u8,174u8,93u8,127u8,203u8,237u8,3u8,166u8,171u8, - 19u8,222u8,148u8,55u8,157u8,102u8,37u8,107u8,57u8,235u8,105u8,199u8,107u8,118u8,218u8,245u8,134u8,64u8,106u8,91u8, - 215u8,58u8,55u8,173u8,187u8,253u8,120u8,67u8,55u8,172u8,55u8,80u8,144u8,110u8,75u8,219u8,208u8,21u8,236u8,30u8, - 124u8,54u8,233u8,94u8,107u8,90u8,103u8,246u8,201u8,87u8,111u8,215u8,203u8,238u8,6u8,141u8,14u8,57u8,100u8,79u8, - 111u8,219u8,179u8,126u8,145u8,224u8,65u8,35u8,89u8,105u8,193u8,251u8,212u8,70u8,211u8,20u8,228u8,239u8,176u8,184u8, - 121u8,63u8,218u8,236u8,44u8,138u8,194u8,157u8,154u8,129u8,105u8,206u8,61u8,221u8,137u8,146u8,79u8,33u8,5u8,167u8, - 119u8,0u8,21u8,23u8,20u8,224u8,49u8,36u8,252u8,45u8,17u8,87u8,186u8,105u8,18u8,93u8,51u8,105u8,223u8,139u8, - 107u8,156u8,89u8,19u8,58u8,118u8,21u8,240u8,226u8,26u8,236u8,54u8,144u8,237u8,243u8,49u8,44u8,25u8,175u8,99u8, - 211u8,46u8,227u8,97u8,125u8,192u8,196u8,117u8,27u8,236u8,106u8,220u8,124u8,121u8,49u8,46u8,71u8,75u8,208u8,1u8, - 161u8,72u8,153u8,128u8,79u8,110u8,86u8,105u8,194u8,74u8,93u8,138u8,119u8,221u8,25u8,237u8,69u8,85u8,106u8,197u8, - 52u8,71u8,217u8,89u8,19u8,187u8,63u8,58u8,27u8,176u8,173u8,103u8,88u8,89u8,191u8,79u8,58u8,149u8,52u8,100u8, - 241u8,72u8,145u8,31u8,133u8,169u8,60u8,79u8,206u8,151u8,197u8,56u8,238u8,39u8,131u8,75u8,227u8,225u8,234u8,139u8, - 218u8,180u8,97u8,188u8,66u8,9u8,177u8,7u8,238u8,220u8,56u8,121u8,20u8,239u8,62u8,126u8,210u8,98u8,206u8,164u8, - 147u8,11u8,57u8,112u8,239u8,180u8,209u8,90u8,200u8,78u8,147u8,89u8,114u8,145u8,106u8,55u8,36u8,104u8,215u8,116u8, - 240u8,75u8,58u8,196u8,91u8,236u8,81u8,170u8,177u8,73u8,232u8,152u8,150u8,118u8,119u8,59u8,191u8,202u8,195u8,119u8, - 129u8,142u8,234u8,58u8,23u8,177u8,83u8,104u8,84u8,236u8,65u8,182u8,10u8,64u8,30u8,1u8,228u8,178u8,130u8,69u8, - 238u8,98u8,205u8,126u8,209u8,174u8,113u8,160u8,27u8,11u8,97u8,247u8,39u8,221u8,170u8,197u8,233u8,247u8,57u8,137u8, - 137u8,243u8,25u8,158u8,248u8,44u8,57u8,183u8,15u8,83u8,166u8,74u8,217u8,90u8,138u8,180u8,20u8,246u8,145u8,42u8, - 135u8,225u8,142u8,15u8,115u8,150u8,125u8,61u8,185u8,247u8,193u8,96u8,80u8,101u8,85u8,118u8,105u8,142u8,125u8,41u8, - 226u8,79u8,100u8,87u8,98u8,240u8,19u8,196u8,77u8,137u8,55u8,153u8,179u8,27u8,72u8,212u8,132u8,246u8,197u8,10u8, - 104u8,49u8,105u8,215u8,19u8,225u8,157u8,71u8,70u8,203u8,184u8,113u8,154u8,12u8,197u8,124u8,82u8,96u8,120u8,175u8, - 100u8,164u8,248u8,53u8,169u8,90u8,78u8,35u8,52u8,28u8,84u8,133u8,12u8,158u8,161u8,203u8,216u8,219u8,250u8,69u8, - 106u8,70u8,234u8,236u8,61u8,235u8,208u8,234u8,95u8,155u8,144u8,33u8,67u8,172u8,240u8,2u8,124u8,114u8,159u8,222u8, - 232u8,200u8,169u8,93u8,191u8,35u8,103u8,190u8,78u8,105u8,105u8,252u8,168u8,30u8,37u8,52u8,180u8,3u8,125u8,5u8, - 191u8,170u8,93u8,28u8,244u8,110u8,72u8,16u8,50u8,109u8,102u8,105u8,87u8,92u8,232u8,215u8,87u8,161u8,101u8,173u8, - 175u8,232u8,32u8,7u8,139u8,156u8,110u8,32u8,102u8,231u8,20u8,49u8,215u8,137u8,208u8,99u8,30u8,111u8,146u8,94u8, - 0u8,58u8,166u8,212u8,82u8,16u8,71u8,24u8,46u8,7u8,152u8,65u8,49u8,122u8,37u8,11u8,199u8,138u8,25u8,182u8, - 193u8,75u8,39u8,17u8,154u8,137u8,81u8,142u8,52u8,6u8,18u8,246u8,90u8,59u8,95u8,236u8,182u8,255u8,167u8,5u8, - 140u8,223u8,238u8,213u8,203u8,52u8,126u8,235u8,136u8,148u8,105u8,34u8,246u8,239u8,17u8,105u8,45u8,241u8,155u8,93u8, - 184u8,82u8,155u8,180u8,49u8,68u8,168u8,186u8,91u8,194u8,145u8,172u8,91u8,158u8,112u8,57u8,23u8,142u8,142u8,228u8, - 211u8,199u8,133u8,33u8,209u8,103u8,212u8,75u8,131u8,180u8,145u8,219u8,190u8,248u8,154u8,186u8,232u8,34u8,212u8,82u8, - 194u8,24u8,194u8,31u8,249u8,114u8,141u8,27u8,149u8,224u8,166u8,174u8,249u8,34u8,128u8,95u8,157u8,215u8,118u8,134u8, - 9u8,220u8,182u8,18u8,186u8,127u8,204u8,26u8,161u8,41u8,176u8,18u8,54u8,187u8,175u8,249u8,51u8,223u8,253u8,7u8, - 86u8,224u8,171u8,192u8,243u8,182u8,45u8,239u8,37u8,100u8,213u8,224u8,152u8,73u8,6u8,19u8,203u8,213u8,1u8,184u8, - 224u8,37u8,39u8,161u8,65u8,76u8,203u8,238u8,197u8,53u8,72u8,230u8,81u8,66u8,237u8,75u8,73u8,128u8,227u8,198u8, - 183u8,89u8,89u8,216u8,7u8,84u8,237u8,199u8,251u8,149u8,115u8,1u8,53u8,246u8,93u8,255u8,58u8,27u8,42u8,95u8, - 7u8,172u8,58u8,211u8,34u8,84u8,94u8,153u8,245u8,26u8,235u8,76u8,60u8,204u8,240u8,124u8,178u8,156u8,133u8,174u8, - 192u8,225u8,99u8,144u8,19u8,124u8,45u8,26u8,45u8,242u8,155u8,116u8,83u8,117u8,85u8,151u8,213u8,122u8,89u8,22u8, - 160u8,242u8,89u8,3u8,138u8,66u8,139u8,19u8,218u8,235u8,238u8,154u8,71u8,49u8,171u8,251u8,172u8,111u8,169u8,46u8, - 7u8,198u8,53u8,206u8,118u8,19u8,147u8,192u8,30u8,179u8,154u8,54u8,4u8,246u8,89u8,131u8,157u8,131u8,238u8,151u8, - 168u8,156u8,194u8,114u8,146u8,208u8,80u8,60u8,228u8,205u8,135u8,241u8,84u8,222u8,69u8,191u8,170u8,74u8,75u8,83u8, - 86u8,105u8,101u8,111u8,45u8,181u8,106u8,53u8,152u8,99u8,14u8,241u8,155u8,254u8,116u8,173u8,132u8,144u8,170u8,187u8, - 190u8,140u8,203u8,43u8,211u8,162u8,230u8,86u8,40u8,25u8,127u8,148u8,183u8,22u8,73u8,145u8,109u8,23u8,243u8,84u8, - 136u8,231u8,95u8,171u8,219u8,203u8,11u8,55u8,251u8,111u8,219u8,31u8,118u8,162u8,127u8,255u8,219u8,21u8,20u8,234u8, - 247u8,221u8,154u8,223u8,31u8,213u8,252u8,190u8,87u8,243u8,251u8,227u8,154u8,223u8,159u8,212u8,252u8,254u8,101u8,205u8, - 239u8,95u8,213u8,252u8,254u8,117u8,205u8,239u8,137u8,183u8,207u8,62u8,120u8,24u8,211u8,172u8,64u8,141u8,31u8,131u8, - 97u8,155u8,165u8,44u8,227u8,90u8,119u8,237u8,168u8,38u8,39u8,189u8,204u8,170u8,20u8,165u8,43u8,242u8,54u8,225u8, - 112u8,135u8,177u8,165u8,206u8,15u8,178u8,245u8,141u8,187u8,164u8,116u8,189u8,32u8,179u8,248u8,41u8,200u8,117u8,187u8, - 190u8,168u8,107u8,0u8,225u8,141u8,45u8,234u8,108u8,162u8,217u8,116u8,12u8,246u8,18u8,187u8,25u8,137u8,128u8,220u8, - 244u8,236u8,183u8,191u8,197u8,85u8,35u8,209u8,232u8,33u8,50u8,2u8,102u8,223u8,220u8,196u8,122u8,183u8,233u8,17u8, - 155u8,110u8,213u8,213u8,77u8,126u8,17u8,16u8,122u8,145u8,223u8,167u8,226u8,220u8,221u8,216u8,224u8,118u8,149u8,234u8, - 219u8,40u8,253u8,220u8,143u8,243u8,255u8,97u8,121u8,207u8,185u8,19u8,235u8,124u8,63u8,26u8,66u8,68u8,56u8,250u8, - 43u8,132u8,86u8,243u8,69u8,250u8,220u8,45u8,135u8,218u8,143u8,40u8,142u8,246u8,61u8,127u8,190u8,242u8,34u8,152u8, - 192u8,205u8,91u8,45u8,15u8,203u8,183u8,111u8,125u8,131u8,227u8,253u8,100u8,159u8,26u8,201u8,230u8,125u8,49u8,226u8, - 30u8,46u8,188u8,149u8,139u8,174u8,75u8,59u8,191u8,158u8,167u8,207u8,3u8,181u8,176u8,13u8,203u8,206u8,106u8,246u8, - 171u8,191u8,250u8,213u8,13u8,190u8,122u8,46u8,111u8,11u8,70u8,95u8,187u8,245u8,183u8,138u8,57u8,195u8,121u8,194u8, - 174u8,37u8,44u8,30u8,44u8,184u8,81u8,190u8,175u8,253u8,185u8,79u8,127u8,230u8,35u8,13u8,61u8,150u8,241u8,254u8, - 209u8,217u8,165u8,183u8,201u8,51u8,74u8,93u8,177u8,80u8,142u8,89u8,11u8,148u8,142u8,70u8,96u8,254u8,166u8,100u8, - 199u8,166u8,159u8,76u8,83u8,112u8,165u8,72u8,61u8,44u8,181u8,194u8,69u8,245u8,39u8,108u8,86u8,199u8,226u8,10u8, - 220u8,176u8,200u8,149u8,197u8,150u8,122u8,185u8,107u8,92u8,143u8,226u8,250u8,71u8,15u8,156u8,155u8,206u8,106u8,203u8, - 190u8,133u8,51u8,93u8,7u8,161u8,27u8,161u8,116u8,193u8,10u8,76u8,236u8,61u8,1u8,34u8,164u8,217u8,115u8,207u8, - 241u8,165u8,96u8,65u8,64u8,179u8,211u8,72u8,254u8,115u8,72u8,230u8,244u8,85u8,39u8,150u8,248u8,35u8,251u8,209u8, - 121u8,211u8,142u8,46u8,171u8,21u8,4u8,59u8,21u8,125u8,122u8,127u8,114u8,17u8,98u8,106u8,120u8,210u8,80u8,92u8, - 29u8,179u8,160u8,54u8,88u8,184u8,77u8,23u8,83u8,21u8,190u8,206u8,138u8,134u8,71u8,17u8,241u8,223u8,111u8,223u8, - 16u8,228u8,55u8,108u8,5u8,18u8,10u8,119u8,172u8,222u8,120u8,161u8,46u8,20u8,210u8,160u8,17u8,131u8,19u8,17u8, - 241u8,149u8,100u8,185u8,231u8,158u8,26u8,23u8,211u8,217u8,219u8,160u8,41u8,78u8,26u8,22u8,109u8,185u8,144u8,105u8, - 237u8,24u8,248u8,196u8,237u8,21u8,240u8,84u8,81u8,210u8,21u8,140u8,29u8,125u8,150u8,61u8,69u8,126u8,243u8,110u8, - 34u8,127u8,16u8,46u8,95u8,165u8,20u8,113u8,93u8,94u8,95u8,169u8,76u8,241u8,51u8,226u8,248u8,155u8,106u8,75u8, - 162u8,169u8,242u8,191u8,252u8,140u8,85u8,130u8,49u8,70u8,65u8,127u8,9u8,157u8,15u8,224u8,124u8,132u8,247u8,243u8, - 226u8,195u8,141u8,111u8,146u8,94u8,249u8,254u8,106u8,6u8,140u8,58u8,69u8,199u8,251u8,200u8,243u8,246u8,141u8,178u8, - 140u8,159u8,251u8,164u8,170u8,90u8,145u8,150u8,96u8,26u8,105u8,18u8,88u8,95u8,200u8,203u8,227u8,162u8,111u8,5u8, - 94u8,119u8,138u8,255u8,218u8,34u8,60u8,214u8,137u8,182u8,61u8,174u8,244u8,42u8,67u8,195u8,32u8,187u8,143u8,212u8, - 128u8,187u8,143u8,212u8,144u8,6u8,66u8,90u8,212u8,169u8,146u8,61u8,212u8,254u8,197u8,127u8,180u8,70u8,71u8,129u8, - 147u8,210u8,193u8,247u8,5u8,125u8,106u8,13u8,158u8,150u8,253u8,122u8,39u8,178u8,191u8,161u8,184u8,144u8,47u8,121u8, - 195u8,231u8,219u8,194u8,9u8,59u8,209u8,135u8,123u8,219u8,59u8,247u8,2u8,121u8,154u8,56u8,28u8,45u8,217u8,178u8, - 199u8,243u8,160u8,217u8,28u8,230u8,207u8,254u8,113u8,16u8,138u8,182u8,159u8,76u8,22u8,24u8,207u8,106u8,12u8,232u8, - 45u8,47u8,6u8,58u8,209u8,142u8,143u8,92u8,191u8,240u8,191u8,165u8,96u8,29u8,37u8,217u8,4u8,164u8,75u8,139u8, - 54u8,44u8,56u8,161u8,67u8,52u8,34u8,129u8,150u8,219u8,219u8,219u8,95u8,118u8,162u8,73u8,62u8,16u8,183u8,112u8, - 159u8,165u8,147u8,81u8,53u8,129u8,147u8,25u8,150u8,51u8,241u8,60u8,129u8,131u8,248u8,24u8,18u8,7u8,234u8,16u8, - 66u8,189u8,105u8,139u8,87u8,45u8,177u8,91u8,82u8,62u8,220u8,219u8,219u8,217u8,232u8,255u8,30u8,223u8,123u8,234u8, - 142u8,237u8,134u8,45u8,73u8,128u8,115u8,193u8,171u8,41u8,188u8,150u8,0u8,165u8,237u8,25u8,133u8,86u8,20u8,61u8, - 11u8,93u8,149u8,235u8,163u8,60u8,189u8,215u8,110u8,63u8,69u8,235u8,123u8,186u8,44u8,74u8,64u8,114u8,126u8,73u8, - 215u8,166u8,201u8,11u8,71u8,84u8,163u8,12u8,51u8,83u8,209u8,2u8,183u8,58u8,46u8,46u8,59u8,232u8,171u8,35u8, - 64u8,186u8,225u8,117u8,129u8,183u8,143u8,242u8,251u8,34u8,138u8,150u8,5u8,43u8,123u8,62u8,22u8,117u8,29u8,242u8, - 165u8,144u8,130u8,103u8,106u8,108u8,139u8,79u8,227u8,140u8,213u8,28u8,119u8,250u8,196u8,206u8,110u8,179u8,123u8,146u8, - 250u8,119u8,10u8,12u8,97u8,103u8,108u8,168u8,4u8,226u8,89u8,244u8,96u8,75u8,31u8,158u8,35u8,20u8,180u8,39u8, - 42u8,107u8,13u8,133u8,226u8,205u8,64u8,17u8,10u8,14u8,70u8,187u8,197u8,120u8,191u8,28u8,3u8,0u8,227u8,124u8, - 50u8,92u8,233u8,117u8,213u8,47u8,81u8,52u8,132u8,29u8,250u8,229u8,87u8,39u8,138u8,43u8,133u8,20u8,49u8,137u8, - 76u8,38u8,175u8,45u8,165u8,106u8,123u8,90u8,222u8,72u8,115u8,135u8,198u8,105u8,179u8,213u8,122u8,88u8,26u8,239u8, - 174u8,213u8,204u8,82u8,176u8,190u8,229u8,163u8,32u8,198u8,98u8,118u8,199u8,15u8,227u8,113u8,220u8,82u8,131u8,124u8, - 126u8,93u8,211u8,185u8,65u8,191u8,25u8,72u8,94u8,81u8,24u8,237u8,60u8,132u8,120u8,50u8,246u8,194u8,222u8,85u8, - 220u8,243u8,246u8,21u8,203u8,198u8,202u8,11u8,227u8,220u8,243u8,97u8,232u8,38u8,67u8,20u8,12u8,126u8,20u8,247u8, - 117u8,190u8,125u8,229u8,63u8,227u8,231u8,52u8,1u8,126u8,166u8,201u8,158u8,234u8,106u8,172u8,112u8,3u8,11u8,115u8, - 239u8,212u8,151u8,106u8,133u8,71u8,226u8,172u8,255u8,90u8,172u8,221u8,232u8,90u8,35u8,183u8,145u8,185u8,180u8,81u8, - 114u8,153u8,110u8,112u8,5u8,86u8,69u8,215u8,196u8,32u8,189u8,4u8,120u8,116u8,46u8,213u8,162u8,115u8,15u8,219u8, - 141u8,83u8,245u8,191u8,27u8,185u8,96u8,219u8,171u8,192u8,126u8,82u8,25u8,221u8,24u8,203u8,64u8,239u8,117u8,244u8, - 153u8,128u8,144u8,240u8,128u8,200u8,90u8,83u8,218u8,126u8,158u8,44u8,128u8,222u8,217u8,60u8,177u8,155u8,113u8,84u8, - 180u8,14u8,33u8,101u8,119u8,175u8,154u8,164u8,149u8,157u8,71u8,60u8,110u8,131u8,41u8,205u8,225u8,251u8,118u8,123u8, - 131u8,9u8,64u8,23u8,111u8,179u8,127u8,247u8,24u8,217u8,207u8,4u8,122u8,178u8,114u8,154u8,204u8,61u8,84u8,94u8, - 195u8,115u8,90u8,132u8,90u8,54u8,58u8,166u8,82u8,115u8,103u8,73u8,192u8,2u8,254u8,17u8,167u8,96u8,131u8,14u8, - 141u8,188u8,122u8,164u8,217u8,209u8,174u8,80u8,151u8,164u8,45u8,95u8,66u8,222u8,131u8,90u8,95u8,112u8,160u8,170u8, - 5u8,77u8,109u8,23u8,21u8,203u8,121u8,226u8,91u8,225u8,229u8,114u8,58u8,189u8,62u8,21u8,1u8,67u8,12u8,227u8, - 161u8,52u8,250u8,117u8,61u8,163u8,126u8,154u8,15u8,151u8,147u8,84u8,95u8,109u8,181u8,57u8,223u8,177u8,166u8,175u8, - 181u8,238u8,181u8,92u8,239u8,234u8,54u8,125u8,149u8,223u8,227u8,216u8,241u8,13u8,12u8,245u8,128u8,26u8,214u8,77u8, - 32u8,150u8,214u8,174u8,142u8,201u8,107u8,142u8,187u8,7u8,88u8,7u8,50u8,244u8,0u8,182u8,130u8,179u8,49u8,59u8, - 95u8,133u8,144u8,65u8,237u8,192u8,150u8,111u8,85u8,189u8,96u8,145u8,224u8,163u8,6u8,132u8,89u8,100u8,102u8,60u8, - 248u8,188u8,25u8,26u8,55u8,242u8,17u8,29u8,160u8,241u8,104u8,159u8,105u8,105u8,174u8,230u8,47u8,54u8,174u8,232u8, - 99u8,78u8,179u8,205u8,64u8,13u8,75u8,185u8,220u8,179u8,233u8,77u8,56u8,180u8,14u8,69u8,245u8,78u8,218u8,87u8, - 32u8,134u8,71u8,171u8,57u8,105u8,195u8,229u8,156u8,76u8,135u8,141u8,189u8,241u8,21u8,183u8,226u8,234u8,120u8,185u8, - 249u8,204u8,93u8,116u8,14u8,24u8,120u8,72u8,202u8,89u8,216u8,161u8,17u8,175u8,37u8,156u8,230u8,3u8,121u8,71u8, - 69u8,250u8,161u8,4u8,157u8,45u8,151u8,44u8,64u8,190u8,157u8,68u8,158u8,35u8,165u8,89u8,29u8,57u8,160u8,139u8, - 210u8,209u8,54u8,156u8,249u8,200u8,219u8,188u8,146u8,218u8,45u8,57u8,173u8,173u8,196u8,40u8,118u8,227u8,53u8,21u8, - 229u8,81u8,194u8,199u8,61u8,15u8,219u8,36u8,249u8,180u8,90u8,97u8,8u8,190u8,224u8,86u8,113u8,250u8,191u8,254u8, - 107u8,180u8,83u8,173u8,205u8,190u8,80u8,135u8,96u8,76u8,52u8,208u8,157u8,238u8,88u8,22u8,217u8,43u8,152u8,89u8, - 135u8,109u8,240u8,76u8,52u8,218u8,152u8,42u8,60u8,205u8,233u8,170u8,80u8,67u8,218u8,20u8,146u8,129u8,79u8,246u8, - 234u8,49u8,85u8,135u8,30u8,15u8,66u8,138u8,85u8,131u8,160u8,244u8,128u8,82u8,42u8,45u8,59u8,159u8,9u8,241u8, - 79u8,183u8,208u8,74u8,66u8,24u8,174u8,193u8,50u8,15u8,133u8,219u8,1u8,80u8,145u8,57u8,172u8,219u8,84u8,126u8, - 105u8,132u8,63u8,218u8,72u8,135u8,176u8,197u8,159u8,24u8,163u8,212u8,214u8,50u8,169u8,188u8,176u8,136u8,79u8,238u8, - 145u8,57u8,250u8,50u8,29u8,101u8,216u8,98u8,213u8,62u8,93u8,238u8,74u8,115u8,206u8,157u8,84u8,253u8,58u8,203u8, - 202u8,12u8,12u8,190u8,127u8,185u8,135u8,169u8,60u8,55u8,135u8,218u8,203u8,204u8,102u8,252u8,101u8,170u8,131u8,223u8, - 118u8,20u8,122u8,168u8,7u8,153u8,159u8,250u8,88u8,212u8,108u8,68u8,5u8,27u8,108u8,99u8,125u8,37u8,1u8,201u8, - 225u8,89u8,135u8,136u8,92u8,35u8,37u8,48u8,238u8,180u8,76u8,241u8,254u8,244u8,242u8,161u8,156u8,112u8,227u8,149u8, - 236u8,168u8,32u8,36u8,139u8,27u8,184u8,219u8,140u8,21u8,133u8,237u8,108u8,115u8,103u8,34u8,229u8,224u8,172u8,15u8, - 61u8,88u8,164u8,210u8,251u8,220u8,124u8,1u8,64u8,139u8,221u8,79u8,162u8,150u8,212u8,217u8,16u8,58u8,129u8,86u8, - 220u8,166u8,182u8,105u8,137u8,248u8,39u8,22u8,138u8,238u8,236u8,64u8,148u8,168u8,153u8,193u8,241u8,228u8,241u8,227u8, - 71u8,102u8,76u8,216u8,202u8,63u8,236u8,239u8,139u8,174u8,192u8,85u8,38u8,8u8,158u8,230u8,185u8,118u8,226u8,171u8, - 85u8,62u8,67u8,101u8,251u8,4u8,107u8,87u8,87u8,69u8,92u8,205u8,88u8,24u8,70u8,31u8,132u8,255u8,75u8,48u8, - 97u8,49u8,70u8,203u8,173u8,86u8,37u8,167u8,124u8,251u8,55u8,254u8,167u8,5u8,5u8,130u8,149u8,239u8,50u8,106u8, - 104u8,251u8,144u8,232u8,235u8,250u8,190u8,35u8,119u8,24u8,35u8,2u8,62u8,131u8,116u8,77u8,38u8,217u8,125u8,178u8, - 187u8,243u8,120u8,103u8,53u8,163u8,148u8,113u8,132u8,116u8,222u8,127u8,35u8,134u8,144u8,90u8,148u8,5u8,112u8,27u8, - 48u8,7u8,207u8,78u8,124u8,150u8,180u8,53u8,87u8,99u8,127u8,110u8,55u8,50u8,49u8,20u8,153u8,26u8,70u8,251u8, - 69u8,56u8,209u8,58u8,145u8,229u8,45u8,50u8,91u8,171u8,183u8,147u8,63u8,147u8,96u8,119u8,54u8,89u8,191u8,181u8, - 142u8,55u8,195u8,83u8,125u8,7u8,187u8,121u8,178u8,191u8,81u8,162u8,73u8,171u8,143u8,215u8,103u8,208u8,77u8,101u8, - 252u8,222u8,84u8,59u8,186u8,182u8,96u8,133u8,134u8,20u8,112u8,66u8,175u8,101u8,139u8,53u8,47u8,244u8,117u8,147u8, - 203u8,82u8,161u8,74u8,140u8,205u8,221u8,207u8,251u8,108u8,107u8,63u8,218u8,123u8,220u8,92u8,250u8,239u8,237u8,173u8, - 182u8,175u8,69u8,24u8,45u8,20u8,25u8,3u8,40u8,154u8,103u8,126u8,99u8,134u8,43u8,12u8,213u8,203u8,132u8,92u8, - 227u8,252u8,151u8,120u8,99u8,229u8,12u8,88u8,85u8,238u8,111u8,245u8,12u8,162u8,151u8,123u8,21u8,15u8,4u8,146u8, - 57u8,76u8,90u8,89u8,225u8,39u8,192u8,93u8,213u8,59u8,240u8,115u8,48u8,142u8,209u8,18u8,239u8,58u8,183u8,57u8, - 223u8,106u8,98u8,72u8,179u8,183u8,228u8,138u8,87u8,204u8,8u8,169u8,23u8,107u8,210u8,65u8,251u8,18u8,63u8,225u8, - 196u8,80u8,85u8,82u8,72u8,99u8,182u8,80u8,50u8,232u8,117u8,178u8,156u8,88u8,119u8,164u8,97u8,74u8,103u8,10u8, - 193u8,229u8,168u8,192u8,79u8,116u8,38u8,25u8,222u8,229u8,199u8,227u8,112u8,83u8,25u8,243u8,203u8,32u8,179u8,166u8, - 114u8,42u8,227u8,247u8,74u8,83u8,203u8,0u8,123u8,182u8,96u8,82u8,157u8,30u8,209u8,84u8,22u8,71u8,60u8,184u8, - 155u8,44u8,166u8,239u8,204u8,101u8,250u8,24u8,15u8,156u8,129u8,236u8,111u8,200u8,31u8,175u8,15u8,117u8,115u8,37u8, - 229u8,155u8,174u8,99u8,109u8,194u8,78u8,164u8,177u8,161u8,107u8,126u8,120u8,36u8,148u8,95u8,200u8,176u8,153u8,72u8, - 160u8,250u8,138u8,36u8,180u8,0u8,162u8,211u8,74u8,116u8,37u8,249u8,115u8,39u8,126u8,238u8,196u8,207u8,77u8,138u8, - 31u8,206u8,79u8,129u8,118u8,232u8,85u8,194u8,169u8,70u8,54u8,213u8,109u8,206u8,74u8,25u8,83u8,11u8,85u8,187u8, - 110u8,43u8,55u8,104u8,23u8,219u8,132u8,46u8,149u8,13u8,100u8,229u8,116u8,158u8,188u8,140u8,115u8,40u8,209u8,217u8, - 246u8,248u8,122u8,71u8,35u8,102u8,195u8,140u8,145u8,16u8,20u8,207u8,170u8,182u8,73u8,168u8,74u8,174u8,177u8,56u8, - 51u8,107u8,189u8,72u8,126u8,25u8,119u8,20u8,220u8,9u8,173u8,59u8,161u8,117u8,39u8,180u8,110u8,93u8,104u8,85u8, - 223u8,11u8,162u8,104u8,225u8,175u8,224u8,172u8,189u8,232u8,66u8,13u8,128u8,226u8,68u8,226u8,93u8,23u8,30u8,33u8, - 233u8,65u8,8u8,92u8,76u8,50u8,30u8,155u8,121u8,252u8,228u8,203u8,134u8,14u8,220u8,163u8,175u8,31u8,237u8,62u8, - 218u8,94u8,207u8,131u8,219u8,212u8,188u8,146u8,64u8,223u8,137u8,174u8,59u8,209u8,117u8,39u8,186u8,110u8,69u8,116u8, - 105u8,204u8,181u8,218u8,177u8,192u8,42u8,67u8,200u8,211u8,235u8,223u8,59u8,197u8,154u8,6u8,91u8,216u8,70u8,227u8, - 242u8,194u8,99u8,167u8,109u8,228u8,39u8,6u8,175u8,142u8,185u8,51u8,171u8,238u8,100u8,211u8,157u8,108u8,186u8,97u8, - 217u8,164u8,144u8,18u8,216u8,117u8,2u8,142u8,198u8,161u8,160u8,223u8,196u8,250u8,105u8,34u8,52u8,238u8,44u8,156u8, - 213u8,165u8,200u8,234u8,154u8,43u8,160u8,181u8,62u8,133u8,56u8,66u8,218u8,86u8,141u8,203u8,31u8,9u8,143u8,173u8, - 143u8,113u8,235u8,226u8,46u8,164u8,165u8,63u8,165u8,136u8,187u8,77u8,9u8,247u8,123u8,19u8,112u8,22u8,241u8,155u8, - 102u8,246u8,220u8,43u8,194u8,181u8,132u8,94u8,80u8,68u8,202u8,130u8,12u8,252u8,206u8,62u8,35u8,90u8,111u8,35u8, - 177u8,229u8,251u8,110u8,18u8,55u8,228u8,157u8,42u8,251u8,104u8,84u8,47u8,58u8,6u8,243u8,49u8,198u8,118u8,80u8, - 153u8,172u8,182u8,82u8,21u8,76u8,91u8,114u8,176u8,78u8,180u8,103u8,73u8,134u8,59u8,57u8,249u8,73u8,173u8,45u8, - 209u8,24u8,125u8,53u8,1u8,36u8,201u8,171u8,17u8,26u8,162u8,32u8,118u8,159u8,158u8,70u8,247u8,2u8,235u8,225u8, - 149u8,79u8,109u8,131u8,5u8,47u8,160u8,223u8,220u8,10u8,243u8,109u8,40u8,33u8,30u8,154u8,200u8,169u8,74u8,208u8, - 64u8,82u8,173u8,238u8,39u8,174u8,125u8,57u8,161u8,91u8,64u8,83u8,231u8,47u8,74u8,13u8,20u8,92u8,133u8,223u8, - 83u8,172u8,179u8,6u8,27u8,136u8,186u8,91u8,47u8,108u8,184u8,147u8,148u8,119u8,146u8,242u8,198u8,37u8,37u8,21u8, - 243u8,150u8,88u8,162u8,132u8,77u8,223u8,39u8,215u8,172u8,176u8,192u8,172u8,55u8,224u8,108u8,216u8,173u8,22u8,177u8, - 187u8,255u8,95u8,132u8,232u8,13u8,200u8,80u8,143u8,8u8,13u8,177u8,206u8,205u8,8u8,167u8,6u8,177u8,170u8,59u8, - 241u8,114u8,39u8,94u8,238u8,12u8,177u8,63u8,178u8,12u8,209u8,60u8,200u8,42u8,216u8,54u8,144u8,68u8,118u8,208u8, - 236u8,70u8,12u8,39u8,10u8,169u8,237u8,109u8,20u8,82u8,171u8,149u8,109u8,86u8,80u8,237u8,78u8,216u8,253u8,190u8, - 133u8,93u8,224u8,137u8,170u8,120u8,218u8,157u8,136u8,188u8,19u8,145u8,27u8,137u8,72u8,141u8,145u8,106u8,194u8,110u8, - 250u8,181u8,221u8,190u8,170u8,121u8,213u8,93u8,121u8,29u8,107u8,46u8,120u8,97u8,133u8,209u8,196u8,2u8,113u8,105u8, - 124u8,161u8,78u8,115u8,174u8,121u8,30u8,70u8,73u8,62u8,121u8,207u8,139u8,45u8,235u8,232u8,12u8,3u8,177u8,4u8, - 254u8,193u8,36u8,157u8,213u8,90u8,195u8,148u8,119u8,187u8,157u8,232u8,145u8,239u8,16u8,6u8,208u8,83u8,59u8,115u8, - 225u8,142u8,98u8,10u8,61u8,237u8,81u8,32u8,19u8,159u8,186u8,250u8,104u8,199u8,70u8,45u8,42u8,61u8,32u8,86u8, - 9u8,199u8,219u8,60u8,238u8,97u8,75u8,53u8,106u8,59u8,136u8,20u8,192u8,255u8,109u8,64u8,0u8,80u8,187u8,143u8, - 173u8,217u8,217u8,155u8,107u8,227u8,159u8,79u8,252u8,180u8,234u8,82u8,200u8,141u8,176u8,239u8,194u8,231u8,155u8,172u8, - 250u8,176u8,141u8,14u8,79u8,19u8,197u8,96u8,106u8,130u8,219u8,206u8,17u8,139u8,171u8,45u8,243u8,69u8,88u8,222u8, - 35u8,255u8,144u8,180u8,231u8,171u8,8u8,63u8,73u8,248u8,210u8,122u8,75u8,55u8,70u8,62u8,23u8,197u8,30u8,124u8, - 135u8,53u8,10u8,97u8,219u8,219u8,170u8,199u8,80u8,30u8,74u8,80u8,120u8,114u8,49u8,116u8,136u8,46u8,111u8,48u8, - 138u8,224u8,117u8,175u8,2u8,10u8,31u8,129u8,51u8,123u8,207u8,51u8,137u8,111u8,124u8,231u8,237u8,160u8,179u8,222u8, - 213u8,235u8,28u8,135u8,62u8,137u8,113u8,219u8,115u8,86u8,210u8,205u8,51u8,160u8,79u8,13u8,11u8,114u8,174u8,254u8, - 38u8,35u8,96u8,248u8,206u8,151u8,79u8,113u8,83u8,185u8,116u8,59u8,60u8,183u8,214u8,70u8,242u8,154u8,93u8,54u8, - 173u8,231u8,170u8,89u8,247u8,52u8,29u8,15u8,78u8,63u8,168u8,30u8,248u8,153u8,190u8,21u8,189u8,117u8,157u8,13u8, - 4u8,132u8,191u8,143u8,177u8,121u8,5u8,174u8,63u8,176u8,253u8,169u8,172u8,139u8,219u8,181u8,43u8,238u8,12u8,139u8, - 207u8,203u8,176u8,216u8,160u8,22u8,176u8,206u8,64u8,105u8,230u8,11u8,123u8,205u8,146u8,155u8,50u8,72u8,110u8,219u8, - 20u8,249u8,109u8,236u8,144u8,213u8,202u8,54u8,110u8,215u8,234u8,248u8,189u8,216u8,27u8,127u8,124u8,75u8,163u8,106u8, - 182u8,207u8,194u8,186u8,88u8,217u8,174u8,248u8,29u8,26u8,22u8,127u8,60u8,171u8,162u8,190u8,205u8,220u8,238u8,246u8, - 246u8,206u8,138u8,97u8,222u8,105u8,242u8,193u8,184u8,200u8,75u8,221u8,20u8,182u8,241u8,69u8,117u8,77u8,218u8,243u8, - 121u8,110u8,164u8,147u8,151u8,113u8,208u8,141u8,164u8,43u8,182u8,45u8,83u8,111u8,122u8,110u8,158u8,123u8,230u8,191u8, - 121u8,46u8,122u8,40u8,90u8,152u8,105u8,160u8,26u8,151u8,155u8,9u8,114u8,125u8,252u8,211u8,255u8,1u8,156u8,186u8, - 222u8,169u8,211u8,2u8,1u8,0u8,0u8,0u8,10u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,158u8, - 7u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,157u8,149u8,203u8,110u8,219u8,70u8,20u8,134u8,247u8, - 122u8,138u8,179u8,42u8,36u8,67u8,161u8,235u8,164u8,40u8,10u8,5u8,93u8,8u8,240u8,165u8,139u8,54u8,10u8,100u8, - 167u8,245u8,206u8,28u8,114u8,14u8,165u8,65u8,72u8,14u8,49u8,23u8,201u8,106u8,144u8,119u8,239u8,63u8,67u8,74u8, - 28u8,27u8,118u8,219u8,68u8,27u8,129u8,195u8,51u8,255u8,249u8,206u8,149u8,231u8,231u8,231u8,116u8,183u8,85u8,150u8, - 26u8,45u8,125u8,205u8,212u8,25u8,189u8,83u8,146u8,45u8,137u8,150u8,84u8,235u8,216u8,84u8,162u8,100u8,170u8,180u8, - 33u8,177u8,217u8,24u8,222u8,8u8,167u8,141u8,205u8,104u8,57u8,62u8,144u8,48u8,76u8,86u8,53u8,170u8,22u8,134u8, - 156u8,158u8,156u8,67u8,209u8,183u8,86u8,109u8,90u8,150u8,81u8,98u8,195u8,193u8,168u8,149u8,100u8,125u8,215u8,105u8, - 227u8,72u8,72u8,169u8,156u8,210u8,237u8,112u8,88u8,56u8,35u8,202u8,248u8,60u8,21u8,5u8,222u8,171u8,118u8,67u8, - 120u8,240u8,173u8,132u8,247u8,90u8,239u8,163u8,34u8,0u8,112u8,166u8,119u8,253u8,81u8,48u8,17u8,84u8,122u8,235u8, - 116u8,67u8,80u8,101u8,67u8,53u8,8u8,220u8,44u8,67u8,48u8,76u8,82u8,85u8,21u8,27u8,110u8,3u8,185u8,129u8, - 197u8,137u8,2u8,113u8,186u8,173u8,112u8,81u8,81u8,164u8,33u8,212u8,16u8,5u8,60u8,65u8,9u8,177u8,54u8,84u8, - 104u8,183u8,61u8,113u8,218u8,231u8,160u8,16u8,106u8,169u8,19u8,6u8,215u8,184u8,38u8,81u8,26u8,109u8,109u8,212u8, - 108u8,124u8,237u8,84u8,135u8,52u8,194u8,178u8,181u8,131u8,241u8,156u8,184u8,21u8,69u8,29u8,152u8,79u8,119u8,248u8, - 145u8,75u8,31u8,222u8,102u8,116u8,141u8,208u8,248u8,81u8,52u8,184u8,54u8,39u8,85u8,1u8,16u8,212u8,202u8,216u8, - 158u8,50u8,17u8,10u8,244u8,82u8,7u8,153u8,28u8,100u8,211u8,251u8,57u8,93u8,204u8,242u8,103u8,165u8,33u8,195u8, - 86u8,123u8,131u8,192u8,243u8,251u8,124u8,30u8,185u8,131u8,158u8,229u8,82u8,183u8,50u8,10u8,142u8,34u8,8u8,8u8, - 34u8,239u8,102u8,176u8,131u8,205u8,129u8,74u8,148u8,188u8,224u8,1u8,45u8,214u8,46u8,137u8,113u8,167u8,149u8,236u8, - 179u8,110u8,88u8,200u8,55u8,232u8,22u8,85u8,29u8,222u8,236u8,141u8,114u8,28u8,101u8,37u8,119u8,140u8,130u8,181u8, - 229u8,33u8,139u8,207u8,191u8,233u8,61u8,163u8,88u8,243u8,104u8,30u8,46u8,6u8,140u8,132u8,115u8,39u8,106u8,207u8, - 52u8,85u8,25u8,103u8,112u8,92u8,199u8,236u8,228u8,193u8,118u8,122u8,63u8,203u8,103u8,129u8,18u8,52u8,252u8,8u8, - 81u8,171u8,118u8,189u8,11u8,141u8,226u8,136u8,177u8,111u8,182u8,218u8,215u8,50u8,0u8,71u8,54u8,240u8,10u8,52u8, - 177u8,47u8,183u8,225u8,191u8,67u8,65u8,84u8,129u8,58u8,20u8,92u8,10u8,111u8,153u8,148u8,3u8,136u8,244u8,37u8, - 135u8,6u8,232u8,229u8,142u8,177u8,41u8,219u8,100u8,244u8,135u8,54u8,172u8,35u8,239u8,217u8,89u8,218u8,24u8,33u8, - 39u8,186u8,173u8,15u8,193u8,79u8,9u8,188u8,144u8,151u8,226u8,64u8,203u8,206u8,105u8,75u8,215u8,70u8,52u8,188u8, - 215u8,230u8,51u8,77u8,127u8,124u8,188u8,152u8,245u8,93u8,229u8,98u8,164u8,141u8,110u8,184u8,117u8,217u8,217u8,217u8, - 100u8,152u8,43u8,17u8,110u8,60u8,84u8,199u8,27u8,139u8,69u8,146u8,139u8,47u8,147u8,9u8,225u8,119u8,30u8,71u8, - 145u8,135u8,212u8,232u8,42u8,77u8,215u8,177u8,241u8,49u8,122u8,107u8,161u8,108u8,79u8,209u8,34u8,27u8,59u8,112u8, - 105u8,201u8,89u8,84u8,64u8,145u8,173u8,163u8,171u8,229u8,205u8,205u8,250u8,234u8,102u8,121u8,183u8,90u8,63u8,172u8, - 254u8,188u8,90u8,95u8,255u8,190u8,250u8,107u8,65u8,254u8,231u8,159u8,232u8,87u8,186u8,120u8,255u8,223u8,190u8,78u8, - 131u8,103u8,105u8,186u8,209u8,200u8,88u8,193u8,97u8,58u8,254u8,102u8,163u8,103u8,223u8,232u8,254u8,211u8,135u8,203u8, - 167u8,254u8,223u8,38u8,254u8,199u8,29u8,66u8,21u8,50u8,235u8,177u8,70u8,80u8,246u8,86u8,187u8,227u8,162u8,96u8, - 249u8,127u8,220u8,125u8,88u8,221u8,61u8,220u8,126u8,250u8,248u8,113u8,181u8,190u8,187u8,186u8,60u8,250u8,121u8,151u8, - 248u8,89u8,115u8,135u8,169u8,64u8,57u8,78u8,75u8,13u8,187u8,128u8,246u8,91u8,133u8,94u8,25u8,60u8,217u8,164u8, - 211u8,95u8,29u8,250u8,147u8,96u8,63u8,240u8,47u8,15u8,123u8,70u8,183u8,204u8,67u8,11u8,196u8,194u8,99u8,151u8, - 150u8,70u8,117u8,177u8,111u8,195u8,172u8,54u8,232u8,52u8,28u8,58u8,161u8,106u8,219u8,199u8,97u8,157u8,241u8,165u8, - 75u8,211u8,177u8,69u8,11u8,99u8,181u8,193u8,240u8,75u8,180u8,8u8,191u8,45u8,112u8,106u8,94u8,4u8,62u8,68u8, - 99u8,231u8,167u8,23u8,159u8,249u8,240u8,194u8,105u8,92u8,135u8,200u8,198u8,197u8,219u8,95u8,250u8,195u8,175u8,105u8, - 66u8,144u8,108u8,4u8,152u8,71u8,163u8,28u8,99u8,86u8,50u8,199u8,33u8,237u8,147u8,242u8,98u8,223u8,197u8,219u8, - 157u8,199u8,30u8,43u8,169u8,242u8,109u8,239u8,96u8,58u8,154u8,46u8,232u8,135u8,49u8,130u8,89u8,239u8,57u8,193u8, - 31u8,13u8,179u8,120u8,243u8,57u8,211u8,82u8,74u8,0u8,197u8,118u8,204u8,195u8,34u8,78u8,236u8,105u8,89u8,196u8, - 18u8,61u8,219u8,255u8,33u8,199u8,81u8,234u8,9u8,218u8,208u8,37u8,129u8,48u8,44u8,201u8,39u8,124u8,141u8,79u8, - 179u8,60u8,239u8,155u8,191u8,39u8,157u8,37u8,253u8,114u8,59u8,212u8,124u8,228u8,137u8,31u8,145u8,151u8,137u8,54u8, - 113u8,151u8,142u8,19u8,242u8,26u8,76u8,88u8,182u8,223u8,3u8,115u8,172u8,149u8,24u8,70u8,53u8,182u8,69u8,220u8, - 205u8,46u8,124u8,181u8,19u8,168u8,87u8,28u8,199u8,157u8,250u8,239u8,101u8,74u8,220u8,93u8,50u8,186u8,81u8,31u8, - 226u8,164u8,36u8,109u8,16u8,70u8,193u8,112u8,131u8,244u8,219u8,176u8,74u8,251u8,143u8,106u8,72u8,208u8,168u8,117u8, - 141u8,140u8,105u8,115u8,200u8,95u8,227u8,144u8,189u8,242u8,19u8,148u8,132u8,228u8,253u8,228u8,235u8,228u8,31u8,186u8, - 128u8,151u8,163u8,140u8,8u8,0u8,0u8,0u8,0u8,18u8,97u8,103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8, - 95u8,102u8,97u8,99u8,116u8,111u8,114u8,121u8,149u8,7u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8, - 157u8,86u8,93u8,79u8,219u8,64u8,16u8,124u8,207u8,175u8,216u8,170u8,82u8,229u8,72u8,110u8,40u8,85u8,85u8,85u8, - 166u8,173u8,26u8,209u8,130u8,250u8,2u8,18u8,133u8,135u8,170u8,170u8,204u8,197u8,222u8,216u8,39u8,236u8,187u8,244u8, - 238u8,76u8,72u8,17u8,255u8,189u8,123u8,31u8,177u8,141u8,147u8,0u8,37u8,66u8,10u8,216u8,119u8,179u8,187u8,51u8, - 179u8,187u8,236u8,237u8,237u8,193u8,121u8,201u8,53u8,212u8,50u8,111u8,42u8,132u8,133u8,146u8,215u8,60u8,71u8,13u8, - 115u8,217u8,136u8,156u8,25u8,46u8,133u8,6u8,35u8,33u8,83u8,200u8,12u8,2u8,43u8,10u8,133u8,5u8,51u8,82u8, - 233u8,9u8,28u8,54u8,74u8,161u8,48u8,213u8,10u8,164u8,168u8,86u8,163u8,61u8,130u8,154u8,46u8,140u8,212u8,112u8, - 164u8,88u8,141u8,75u8,169u8,174u8,32u8,122u8,115u8,179u8,63u8,134u8,140u8,137u8,53u8,128u8,41u8,177u8,142u8,65u8, - 75u8,250u8,165u8,11u8,90u8,98u8,181u8,112u8,81u8,150u8,138u8,45u8,28u8,14u8,29u8,131u8,140u8,66u8,27u8,213u8, - 100u8,20u8,12u8,228u8,28u8,46u8,167u8,109u8,236u8,75u8,240u8,47u8,60u8,14u8,51u8,46u8,62u8,48u8,208u8,43u8, - 109u8,176u8,6u8,150u8,101u8,148u8,188u8,113u8,64u8,54u8,52u8,23u8,220u8,112u8,86u8,241u8,191u8,72u8,7u8,113u8, - 2u8,223u8,133u8,195u8,159u8,55u8,166u8,81u8,24u8,135u8,76u8,120u8,81u8,26u8,200u8,74u8,38u8,10u8,170u8,82u8, - 228u8,253u8,74u8,91u8,160u8,25u8,2u8,10u8,54u8,171u8,48u8,39u8,122u8,148u8,3u8,89u8,52u8,179u8,138u8,103u8, - 147u8,81u8,168u8,132u8,217u8,250u8,211u8,249u8,186u8,254u8,36u8,233u8,96u8,210u8,57u8,179u8,165u8,172u8,224u8,118u8, - 4u8,244u8,105u8,52u8,82u8,21u8,121u8,146u8,160u8,82u8,82u8,29u8,140u8,218u8,135u8,27u8,16u8,190u8,170u8,148u8, - 229u8,185u8,66u8,173u8,81u8,31u8,12u8,142u8,58u8,148u8,46u8,78u8,146u8,116u8,68u8,109u8,61u8,106u8,108u8,5u8, - 73u8,114u8,251u8,3u8,171u8,121u8,12u8,231u8,246u8,175u8,187u8,16u8,127u8,174u8,56u8,218u8,218u8,135u8,41u8,20u8, - 40u8,80u8,243u8,16u8,121u8,215u8,33u8,185u8,176u8,110u8,97u8,85u8,202u8,122u8,241u8,221u8,13u8,231u8,140u8,246u8, - 33u8,172u8,137u8,32u8,218u8,133u8,52u8,158u8,66u8,93u8,18u8,169u8,43u8,52u8,19u8,119u8,193u8,73u8,15u8,223u8, - 166u8,199u8,199u8,103u8,223u8,142u8,167u8,231u8,167u8,103u8,233u8,209u8,244u8,144u8,190u8,126u8,166u8,39u8,167u8,231u8, - 233u8,209u8,233u8,197u8,201u8,215u8,4u8,154u8,247u8,239u8,224u8,19u8,236u8,247u8,98u8,28u8,58u8,143u8,17u8,42u8, - 46u8,239u8,59u8,245u8,66u8,19u8,186u8,117u8,177u8,20u8,70u8,201u8,202u8,41u8,39u8,154u8,122u8,134u8,74u8,91u8, - 107u8,245u8,206u8,146u8,89u8,236u8,219u8,22u8,114u8,109u8,41u8,42u8,120u8,89u8,202u8,161u8,147u8,39u8,48u8,53u8, - 14u8,172u8,150u8,53u8,117u8,66u8,236u8,141u8,184u8,181u8,9u8,90u8,196u8,224u8,78u8,11u8,229u8,139u8,13u8,94u8, - 238u8,8u8,58u8,10u8,252u8,148u8,76u8,195u8,21u8,174u8,13u8,99u8,63u8,11u8,242u8,167u8,145u8,117u8,234u8,37u8, - 244u8,218u8,125u8,12u8,198u8,136u8,161u8,217u8,127u8,251u8,225u8,115u8,236u8,14u8,223u8,109u8,146u8,194u8,28u8,45u8, - 107u8,238u8,173u8,127u8,239u8,55u8,51u8,85u8,230u8,178u8,39u8,147u8,103u8,172u8,178u8,30u8,207u8,27u8,197u8,69u8, - 1u8,65u8,124u8,159u8,172u8,247u8,123u8,228u8,61u8,48u8,166u8,30u8,234u8,183u8,87u8,186u8,233u8,246u8,104u8,104u8, - 19u8,120u8,165u8,121u8,33u8,80u8,141u8,123u8,101u8,13u8,29u8,78u8,118u8,166u8,47u8,101u8,210u8,193u8,229u8,33u8, - 216u8,248u8,160u8,133u8,168u8,208u8,192u8,150u8,94u8,251u8,180u8,133u8,214u8,46u8,238u8,22u8,74u8,67u8,115u8,16u8, - 85u8,209u8,184u8,61u8,119u8,215u8,197u8,169u8,229u8,53u8,166u8,70u8,14u8,51u8,137u8,183u8,4u8,15u8,217u8,237u8, - 148u8,162u8,187u8,65u8,20u8,106u8,195u8,68u8,134u8,228u8,49u8,158u8,149u8,64u8,49u8,212u8,188u8,146u8,75u8,50u8, - 167u8,0u8,188u8,201u8,16u8,115u8,43u8,4u8,131u8,203u8,138u8,215u8,220u8,92u8,238u8,148u8,194u8,91u8,179u8,47u8, - 3u8,23u8,6u8,21u8,53u8,101u8,228u8,110u8,38u8,206u8,35u8,227u8,164u8,223u8,141u8,44u8,251u8,211u8,112u8,226u8, - 253u8,65u8,166u8,188u8,30u8,47u8,162u8,123u8,212u8,225u8,13u8,215u8,70u8,127u8,220u8,184u8,247u8,57u8,250u8,50u8, - 20u8,42u8,190u8,127u8,209u8,142u8,60u8,34u8,89u8,154u8,212u8,173u8,153u8,232u8,193u8,78u8,239u8,116u8,24u8,135u8, - 94u8,127u8,80u8,240u8,153u8,36u8,244u8,101u8,90u8,84u8,114u8,70u8,179u8,168u8,110u8,204u8,147u8,18u8,236u8,20u8, - 38u8,105u8,122u8,4u8,70u8,155u8,49u8,98u8,112u8,92u8,142u8,135u8,234u8,186u8,53u8,74u8,63u8,89u8,187u8,22u8, - 153u8,21u8,37u8,179u8,83u8,17u8,178u8,74u8,234u8,176u8,54u8,194u8,202u8,240u8,231u8,195u8,90u8,105u8,22u8,180u8, - 107u8,233u8,61u8,239u8,175u8,38u8,152u8,217u8,205u8,250u8,154u8,150u8,18u8,61u8,46u8,172u8,41u8,132u8,53u8,73u8, - 27u8,144u8,6u8,26u8,53u8,170u8,36u8,39u8,137u8,21u8,248u8,174u8,114u8,67u8,142u8,30u8,246u8,29u8,178u8,221u8, - 25u8,81u8,152u8,67u8,109u8,67u8,134u8,178u8,158u8,109u8,17u8,74u8,232u8,116u8,231u8,244u8,235u8,79u8,61u8,151u8, - 160u8,223u8,186u8,150u8,14u8,33u8,151u8,147u8,103u8,76u8,2u8,143u8,215u8,211u8,237u8,49u8,239u8,111u8,232u8,117u8, - 134u8,68u8,178u8,216u8,236u8,70u8,159u8,141u8,160u8,255u8,125u8,174u8,209u8,113u8,247u8,168u8,35u8,136u8,67u8,242u8, - 217u8,38u8,59u8,187u8,41u8,13u8,70u8,126u8,249u8,139u8,198u8,129u8,73u8,237u8,220u8,253u8,61u8,84u8,236u8,193u8, - 177u8,74u8,125u8,163u8,82u8,123u8,247u8,73u8,243u8,245u8,191u8,38u8,116u8,59u8,182u8,238u8,70u8,255u8,0u8,233u8, - 3u8,34u8,209u8,29u8,10u8,0u8,0u8,0u8,0u8,19u8,111u8,112u8,116u8,105u8,111u8,110u8,97u8,108u8,95u8,97u8, - 103u8,103u8,114u8,101u8,103u8,97u8,116u8,111u8,114u8,220u8,14u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8, - 255u8,229u8,90u8,109u8,111u8,219u8,54u8,16u8,254u8,158u8,95u8,113u8,65u8,129u8,192u8,194u8,28u8,199u8,113u8,215u8, - 98u8,117u8,154u8,98u8,65u8,219u8,21u8,3u8,134u8,102u8,72u8,219u8,245u8,67u8,49u8,40u8,178u8,68u8,59u8,90u8, - 100u8,209u8,160u8,168u8,36u8,109u8,145u8,255u8,190u8,35u8,69u8,82u8,20u8,69u8,197u8,114u8,154u8,44u8,29u8,90u8, - 20u8,72u8,172u8,144u8,247u8,246u8,220u8,61u8,119u8,164u8,188u8,183u8,183u8,7u8,239u8,207u8,210u8,2u8,150u8,52u8, - 41u8,51u8,2u8,43u8,70u8,47u8,210u8,132u8,20u8,16u8,229u8,144u8,230u8,156u8,176u8,121u8,20u8,19u8,224u8,20u8, - 162u8,197u8,130u8,145u8,69u8,196u8,137u8,124u8,188u8,32u8,172u8,0u8,146u8,242u8,51u8,194u8,224u8,34u8,141u8,182u8, - 246u8,80u8,140u8,94u8,65u8,25u8,12u8,86u8,17u8,139u8,178u8,140u8,100u8,233u8,151u8,104u8,150u8,145u8,0u8,168u8, - 92u8,6u8,57u8,101u8,203u8,40u8,51u8,18u8,70u8,91u8,74u8,107u8,180u8,226u8,180u8,8u8,231u8,44u8,90u8,146u8, - 75u8,202u8,206u8,167u8,83u8,186u8,226u8,41u8,205u8,163u8,44u8,180u8,132u8,126u8,221u8,2u8,252u8,87u8,22u8,4u8, - 10u8,158u8,76u8,167u8,132u8,49u8,202u8,14u8,154u8,207u8,170u8,109u8,211u8,233u8,215u8,119u8,36u8,155u8,15u8,225u8, - 88u8,126u8,188u8,62u8,216u8,50u8,171u8,90u8,138u8,106u8,249u8,33u8,122u8,138u8,63u8,62u8,31u8,244u8,89u8,108u8, - 84u8,28u8,153u8,71u8,90u8,205u8,156u8,165u8,36u8,79u8,218u8,155u8,99u8,154u8,230u8,106u8,201u8,158u8,12u8,58u8, - 129u8,139u8,40u8,43u8,9u8,208u8,185u8,29u8,186u8,50u8,79u8,48u8,232u8,25u8,189u8,44u8,96u8,176u8,160u8,8u8, - 196u8,140u8,224u8,7u8,248u8,66u8,24u8,13u8,70u8,112u8,18u8,165u8,5u8,73u8,96u8,246u8,25u8,242u8,136u8,167u8, - 23u8,4u8,98u8,154u8,144u8,145u8,20u8,26u8,211u8,188u8,224u8,240u8,250u8,232u8,205u8,155u8,147u8,215u8,111u8,142u8, - 222u8,31u8,159u8,132u8,199u8,127u8,189u8,62u8,249u8,237u8,143u8,227u8,143u8,83u8,40u8,159u8,254u8,12u8,135u8,176u8, - 111u8,169u8,175u8,237u8,134u8,57u8,137u8,120u8,201u8,16u8,215u8,2u8,241u8,225u8,80u8,148u8,171u8,21u8,101u8,156u8, - 36u8,27u8,106u8,251u8,240u8,246u8,85u8,83u8,221u8,196u8,82u8,247u8,145u8,69u8,171u8,21u8,230u8,74u8,196u8,40u8, - 58u8,168u8,19u8,0u8,46u8,49u8,131u8,32u8,130u8,184u8,44u8,56u8,93u8,2u8,189u8,168u8,60u8,135u8,44u8,93u8, - 166u8,124u8,4u8,239u8,42u8,75u8,48u8,23u8,147u8,100u8,136u8,118u8,205u8,56u8,67u8,136u8,48u8,51u8,19u8,96u8, - 36u8,74u8,224u8,31u8,220u8,134u8,107u8,207u8,9u8,156u8,214u8,238u8,156u8,86u8,246u8,21u8,156u8,149u8,184u8,246u8, - 119u8,165u8,232u8,44u8,42u8,240u8,17u8,69u8,47u8,171u8,44u8,18u8,255u8,100u8,244u8,209u8,216u8,253u8,201u8,47u8, - 67u8,243u8,80u8,170u8,182u8,31u8,94u8,215u8,78u8,188u8,68u8,173u8,92u8,148u8,6u8,228u8,228u8,178u8,246u8,225u8, - 44u8,141u8,207u8,140u8,237u8,5u8,208u8,28u8,200u8,85u8,76u8,72u8,146u8,230u8,11u8,92u8,122u8,42u8,37u8,42u8, - 171u8,230u8,101u8,46u8,246u8,134u8,106u8,239u8,192u8,210u8,22u8,76u8,141u8,177u8,181u8,137u8,237u8,39u8,150u8,225u8, - 227u8,97u8,227u8,169u8,148u8,85u8,63u8,186u8,118u8,173u8,63u8,74u8,146u8,2u8,78u8,229u8,222u8,83u8,81u8,208u8, - 202u8,134u8,17u8,28u8,205u8,100u8,144u8,209u8,110u8,237u8,132u8,176u8,28u8,75u8,91u8,225u8,96u8,44u8,71u8,24u8, - 140u8,229u8,234u8,231u8,20u8,118u8,150u8,165u8,9u8,243u8,208u8,142u8,105u8,96u8,89u8,29u8,21u8,5u8,97u8,124u8, - 123u8,208u8,118u8,3u8,158u8,31u8,130u8,22u8,54u8,146u8,250u8,96u8,215u8,152u8,38u8,87u8,4u8,77u8,55u8,101u8, - 217u8,99u8,165u8,151u8,60u8,164u8,243u8,144u8,69u8,249u8,130u8,12u8,124u8,73u8,31u8,152u8,93u8,193u8,129u8,249u8, - 181u8,33u8,24u8,83u8,180u8,249u8,249u8,167u8,202u8,164u8,3u8,55u8,114u8,239u8,84u8,230u8,213u8,225u8,155u8,51u8, - 76u8,87u8,79u8,0u8,23u8,84u8,132u8,174u8,174u8,216u8,58u8,118u8,152u8,189u8,223u8,24u8,59u8,19u8,174u8,134u8, - 209u8,195u8,181u8,1u8,49u8,117u8,25u8,244u8,14u8,196u8,110u8,71u8,32u8,78u8,8u8,18u8,69u8,46u8,123u8,67u8, - 179u8,86u8,5u8,135u8,105u8,17u8,198u8,101u8,249u8,23u8,203u8,89u8,229u8,104u8,80u8,185u8,104u8,121u8,216u8,128u8, - 191u8,83u8,167u8,202u8,23u8,89u8,197u8,130u8,65u8,48u8,67u8,145u8,177u8,90u8,90u8,5u8,51u8,120u8,34u8,189u8, - 86u8,185u8,148u8,238u8,42u8,127u8,69u8,144u8,71u8,232u8,103u8,211u8,13u8,27u8,154u8,146u8,234u8,143u8,109u8,101u8, - 90u8,151u8,165u8,37u8,35u8,53u8,23u8,125u8,213u8,72u8,135u8,67u8,77u8,55u8,33u8,92u8,215u8,32u8,180u8,194u8, - 254u8,146u8,230u8,60u8,74u8,115u8,211u8,112u8,209u8,24u8,171u8,97u8,224u8,127u8,183u8,177u8,14u8,97u8,70u8,249u8, - 89u8,163u8,156u8,105u8,110u8,87u8,179u8,98u8,199u8,99u8,213u8,101u8,173u8,102u8,224u8,35u8,74u8,52u8,225u8,207u8, - 70u8,63u8,31u8,213u8,201u8,89u8,119u8,68u8,37u8,237u8,121u8,45u8,236u8,197u8,208u8,22u8,241u8,150u8,230u8,187u8, - 171u8,14u8,49u8,38u8,116u8,74u8,134u8,10u8,213u8,139u8,53u8,20u8,172u8,167u8,4u8,203u8,140u8,74u8,230u8,170u8, - 156u8,101u8,105u8,60u8,168u8,90u8,113u8,160u8,105u8,215u8,166u8,219u8,33u8,52u8,109u8,153u8,98u8,200u8,104u8,22u8, - 76u8,125u8,49u8,177u8,146u8,101u8,222u8,30u8,109u8,154u8,220u8,124u8,227u8,118u8,95u8,212u8,244u8,200u8,82u8,208u8, - 37u8,25u8,180u8,167u8,17u8,156u8,27u8,164u8,203u8,214u8,32u8,36u8,19u8,142u8,161u8,142u8,202u8,159u8,192u8,33u8, - 200u8,70u8,60u8,181u8,244u8,156u8,230u8,100u8,224u8,44u8,188u8,174u8,123u8,5u8,144u8,172u8,32u8,119u8,229u8,137u8, - 79u8,151u8,215u8,40u8,233u8,114u8,171u8,29u8,6u8,221u8,102u8,182u8,136u8,25u8,231u8,135u8,248u8,76u8,206u8,72u8, - 252u8,146u8,144u8,220u8,193u8,84u8,78u8,10u8,121u8,43u8,237u8,32u8,93u8,174u8,50u8,178u8,36u8,88u8,84u8,194u8, - 142u8,194u8,78u8,153u8,138u8,171u8,165u8,216u8,129u8,103u8,6u8,85u8,148u8,221u8,14u8,141u8,91u8,235u8,154u8,87u8, - 5u8,27u8,249u8,4u8,89u8,84u8,92u8,105u8,11u8,209u8,214u8,80u8,180u8,140u8,16u8,137u8,124u8,205u8,14u8,236u8, - 195u8,190u8,21u8,170u8,131u8,4u8,7u8,247u8,19u8,38u8,156u8,192u8,8u8,231u8,200u8,37u8,70u8,46u8,183u8,71u8, - 88u8,241u8,161u8,163u8,38u8,197u8,180u8,225u8,116u8,195u8,126u8,62u8,247u8,10u8,183u8,168u8,201u8,180u8,8u8,155u8, - 198u8,123u8,35u8,232u8,214u8,170u8,178u8,130u8,83u8,157u8,128u8,155u8,128u8,224u8,173u8,153u8,90u8,162u8,85u8,174u8, - 155u8,8u8,237u8,132u8,78u8,78u8,28u8,14u8,64u8,24u8,214u8,181u8,176u8,13u8,101u8,228u8,119u8,211u8,60u8,229u8, - 105u8,132u8,75u8,188u8,248u8,181u8,128u8,233u8,10u8,137u8,49u8,116u8,19u8,176u8,228u8,166u8,118u8,227u8,21u8,101u8, - 98u8,229u8,200u8,161u8,161u8,5u8,114u8,37u8,167u8,173u8,129u8,20u8,231u8,81u8,52u8,242u8,134u8,78u8,136u8,171u8, - 102u8,145u8,67u8,155u8,149u8,166u8,213u8,20u8,178u8,227u8,47u8,36u8,107u8,157u8,234u8,230u8,131u8,78u8,225u8,122u8, - 222u8,63u8,108u8,79u8,240u8,214u8,74u8,237u8,196u8,60u8,205u8,178u8,110u8,15u8,76u8,159u8,86u8,191u8,216u8,170u8, - 124u8,67u8,80u8,51u8,13u8,60u8,160u8,99u8,42u8,220u8,79u8,26u8,116u8,229u8,241u8,93u8,103u8,66u8,29u8,221u8, - 222u8,105u8,224u8,137u8,157u8,149u8,3u8,10u8,247u8,246u8,162u8,142u8,177u8,205u8,17u8,211u8,200u8,204u8,91u8,181u8, - 229u8,77u8,115u8,194u8,38u8,114u8,127u8,18u8,250u8,50u8,195u8,76u8,168u8,107u8,102u8,33u8,123u8,98u8,245u8,83u8, - 237u8,109u8,88u8,118u8,167u8,15u8,205u8,234u8,128u8,123u8,214u8,222u8,134u8,99u8,91u8,226u8,52u8,144u8,27u8,82u8, - 171u8,137u8,156u8,83u8,56u8,190u8,6u8,86u8,93u8,57u8,84u8,7u8,145u8,20u8,143u8,122u8,206u8,193u8,120u8,67u8, - 15u8,189u8,177u8,246u8,86u8,133u8,111u8,6u8,3u8,59u8,81u8,116u8,217u8,92u8,155u8,194u8,105u8,104u8,234u8,75u8, - 143u8,58u8,67u8,241u8,32u8,203u8,232u8,101u8,131u8,45u8,215u8,208u8,165u8,222u8,169u8,67u8,224u8,140u8,177u8,129u8, - 167u8,12u8,244u8,82u8,57u8,39u8,246u8,36u8,64u8,3u8,150u8,135u8,251u8,238u8,2u8,176u8,27u8,114u8,232u8,161u8, - 209u8,242u8,35u8,164u8,227u8,118u8,3u8,177u8,121u8,161u8,241u8,236u8,243u8,226u8,210u8,159u8,129u8,220u8,123u8,37u8, - 15u8,28u8,72u8,104u8,226u8,138u8,68u8,157u8,67u8,235u8,219u8,49u8,209u8,121u8,26u8,247u8,99u8,22u8,87u8,117u8, - 12u8,185u8,157u8,189u8,165u8,243u8,254u8,68u8,48u8,151u8,118u8,17u8,25u8,76u8,70u8,97u8,103u8,205u8,88u8,225u8, - 82u8,88u8,231u8,172u8,82u8,193u8,17u8,162u8,73u8,27u8,142u8,43u8,110u8,61u8,9u8,119u8,187u8,70u8,249u8,78u8, - 42u8,244u8,55u8,206u8,62u8,54u8,181u8,203u8,78u8,29u8,44u8,220u8,182u8,232u8,177u8,164u8,231u8,245u8,216u8,218u8, - 60u8,240u8,95u8,150u8,217u8,231u8,176u8,114u8,246u8,35u8,165u8,128u8,112u8,247u8,193u8,83u8,192u8,115u8,79u8,217u8, - 35u8,5u8,244u8,37u8,93u8,125u8,34u8,172u8,175u8,233u8,250u8,12u8,38u8,93u8,199u8,100u8,132u8,186u8,15u8,249u8, - 254u8,7u8,248u8,174u8,149u8,215u8,141u8,171u8,116u8,206u8,90u8,121u8,123u8,64u8,253u8,70u8,248u8,129u8,244u8,221u8, - 131u8,6u8,61u8,16u8,100u8,8u8,30u8,198u8,211u8,215u8,84u8,241u8,157u8,88u8,113u8,243u8,49u8,163u8,5u8,108u8, - 175u8,131u8,121u8,23u8,202u8,226u8,70u8,206u8,138u8,206u8,166u8,8u8,219u8,46u8,62u8,250u8,132u8,183u8,134u8,124u8, - 16u8,197u8,49u8,190u8,127u8,18u8,93u8,245u8,87u8,231u8,253u8,92u8,240u8,183u8,25u8,14u8,60u8,98u8,67u8,123u8, - 247u8,20u8,138u8,116u8,145u8,55u8,239u8,119u8,125u8,39u8,4u8,115u8,216u8,106u8,156u8,18u8,212u8,159u8,113u8,198u8, - 170u8,164u8,5u8,234u8,45u8,153u8,55u8,7u8,197u8,165u8,229u8,227u8,241u8,16u8,230u8,17u8,38u8,138u8,61u8,137u8, - 169u8,119u8,2u8,219u8,158u8,145u8,220u8,138u8,192u8,16u8,198u8,182u8,120u8,209u8,99u8,36u8,29u8,216u8,44u8,179u8, - 63u8,113u8,110u8,150u8,90u8,43u8,30u8,123u8,20u8,203u8,156u8,182u8,85u8,193u8,33u8,190u8,95u8,124u8,226u8,40u8, - 20u8,140u8,214u8,86u8,56u8,238u8,43u8,207u8,21u8,103u8,14u8,194u8,158u8,227u8,174u8,51u8,251u8,225u8,97u8,57u8, - 62u8,183u8,232u8,8u8,223u8,20u8,172u8,24u8,65u8,93u8,23u8,248u8,94u8,211u8,185u8,122u8,115u8,77u8,244u8,152u8, - 215u8,39u8,204u8,183u8,114u8,233u8,190u8,33u8,153u8,140u8,239u8,22u8,146u8,253u8,113u8,39u8,38u8,179u8,40u8,62u8, - 223u8,222u8,60u8,180u8,219u8,119u8,22u8,219u8,150u8,109u8,157u8,87u8,58u8,119u8,68u8,10u8,122u8,96u8,254u8,206u8, - 200u8,193u8,235u8,246u8,122u8,49u8,130u8,249u8,191u8,77u8,202u8,254u8,164u8,155u8,169u8,58u8,79u8,92u8,46u8,136u8, - 19u8,7u8,68u8,175u8,166u8,201u8,126u8,203u8,222u8,78u8,69u8,214u8,89u8,220u8,45u8,142u8,253u8,58u8,187u8,54u8, - 76u8,137u8,71u8,159u8,200u8,213u8,138u8,196u8,156u8,36u8,136u8,89u8,154u8,149u8,12u8,79u8,76u8,98u8,180u8,13u8, - 197u8,55u8,36u8,112u8,219u8,248u8,106u8,60u8,25u8,143u8,199u8,168u8,32u8,163u8,177u8,108u8,141u8,248u8,80u8,124u8, - 99u8,196u8,74u8,41u8,60u8,103u8,57u8,153u8,111u8,167u8,130u8,126u8,121u8,119u8,191u8,217u8,117u8,51u8,158u8,79u8, - 106u8,60u8,237u8,130u8,63u8,86u8,166u8,109u8,175u8,161u8,177u8,167u8,247u8,81u8,140u8,61u8,35u8,63u8,185u8,125u8, - 228u8,205u8,55u8,112u8,30u8,50u8,244u8,227u8,177u8,63u8,246u8,31u8,180u8,113u8,219u8,235u8,40u8,125u8,188u8,174u8, - 137u8,84u8,75u8,30u8,10u8,160u8,102u8,105u8,220u8,240u8,85u8,43u8,11u8,183u8,239u8,190u8,90u8,20u8,39u8,253u8, - 191u8,139u8,229u8,91u8,177u8,248u8,110u8,234u8,199u8,131u8,134u8,167u8,124u8,188u8,112u8,60u8,123u8,102u8,191u8,161u8, - 189u8,247u8,2u8,187u8,222u8,250u8,23u8,4u8,39u8,233u8,70u8,136u8,41u8,0u8,0u8,0u8,0u8,4u8,99u8,111u8, - 105u8,110u8,254u8,53u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,61u8,107u8,115u8,211u8,200u8, - 150u8,223u8,231u8,87u8,244u8,176u8,85u8,185u8,246u8,172u8,49u8,113u8,94u8,128u8,3u8,83u8,55u8,36u8,129u8,161u8, - 150u8,36u8,84u8,30u8,123u8,119u8,238u8,214u8,148u8,172u8,216u8,178u8,163u8,194u8,150u8,188u8,146u8,76u8,200u8,80u8, - 249u8,239u8,123u8,206u8,233u8,135u8,250u8,37u8,217u8,78u8,12u8,19u8,6u8,168u8,169u8,73u8,34u8,171u8,187u8,79u8, - 159u8,247u8,171u8,219u8,79u8,158u8,60u8,97u8,231u8,87u8,113u8,206u8,38u8,233u8,96u8,54u8,142u8,216u8,52u8,75u8, - 63u8,198u8,131u8,40u8,103u8,197u8,85u8,196u8,134u8,233u8,44u8,25u8,132u8,69u8,156u8,38u8,240u8,107u8,198u8,138u8, - 155u8,105u8,148u8,135u8,195u8,136u8,237u8,167u8,113u8,146u8,183u8,127u8,18u8,67u8,194u8,105u8,145u8,230u8,193u8,48u8, - 11u8,39u8,209u8,117u8,154u8,125u8,232u8,118u8,251u8,240u8,49u8,251u8,252u8,19u8,131u8,127u8,179u8,60u8,98u8,121u8, - 49u8,232u8,118u8,243u8,34u8,139u8,147u8,209u8,174u8,249u8,48u8,202u8,178u8,52u8,179u8,158u8,165u8,83u8,92u8,175u8, - 219u8,253u8,124u8,22u8,141u8,135u8,45u8,118u8,66u8,127u8,222u8,90u8,47u8,229u8,241u8,40u8,137u8,96u8,164u8,122u8, - 234u8,192u8,16u8,246u8,251u8,0u8,124u8,177u8,91u8,243u8,198u8,104u8,148u8,69u8,163u8,176u8,72u8,179u8,96u8,24u8, - 246u8,225u8,199u8,205u8,66u8,47u8,43u8,200u8,246u8,212u8,163u8,219u8,154u8,129u8,209u8,199u8,40u8,41u8,212u8,152u8, - 67u8,252u8,235u8,183u8,48u8,25u8,140u8,163u8,186u8,65u8,28u8,7u8,225u8,56u8,240u8,44u8,123u8,34u8,62u8,91u8, - 108u8,249u8,252u8,38u8,47u8,162u8,73u8,16u8,14u8,6u8,89u8,148u8,231u8,81u8,238u8,160u8,140u8,208u8,137u8,132u8, - 13u8,226u8,100u8,152u8,138u8,143u8,135u8,89u8,28u8,37u8,3u8,15u8,22u8,232u8,1u8,146u8,119u8,183u8,246u8,189u8, - 81u8,148u8,68u8,121u8,156u8,215u8,191u8,84u8,100u8,97u8,146u8,3u8,226u8,97u8,55u8,193u8,48u8,138u8,196u8,210u8, - 79u8,158u8,136u8,31u8,236u8,16u8,153u8,3u8,184u8,76u8,60u8,21u8,63u8,159u8,176u8,61u8,190u8,23u8,150u8,14u8, - 153u8,32u8,50u8,187u8,190u8,138u8,251u8,87u8,12u8,152u8,24u8,246u8,53u8,96u8,69u8,202u8,226u8,36u8,46u8,226u8, - 112u8,28u8,255u8,9u8,219u8,100u8,196u8,142u8,61u8,228u8,217u8,115u8,216u8,103u8,143u8,13u8,210u8,40u8,79u8,254u8, - 81u8,176u8,73u8,88u8,192u8,40u8,228u8,243u8,65u8,52u8,29u8,167u8,55u8,81u8,134u8,115u8,114u8,166u8,166u8,213u8, - 250u8,105u8,146u8,23u8,236u8,112u8,255u8,228u8,237u8,113u8,240u8,246u8,248u8,245u8,73u8,176u8,119u8,112u8,112u8,122u8, - 120u8,118u8,22u8,28u8,189u8,61u8,59u8,218u8,59u8,223u8,255u8,173u8,203u8,102u8,59u8,91u8,236u8,37u8,235u8,236u8, - 150u8,224u8,105u8,11u8,1u8,64u8,225u8,56u8,139u8,194u8,193u8,141u8,6u8,16u8,96u8,34u8,23u8,64u8,85u8,44u8, - 242u8,238u8,244u8,112u8,239u8,224u8,247u8,224u8,253u8,197u8,171u8,119u8,111u8,207u8,126u8,59u8,60u8,144u8,171u8,108u8, - 248u8,87u8,185u8,10u8,105u8,55u8,151u8,81u8,148u8,44u8,179u8,204u8,241u8,201u8,185u8,187u8,196u8,166u8,182u8,196u8, - 65u8,52u8,205u8,162u8,126u8,88u8,68u8,131u8,54u8,219u8,19u8,136u8,150u8,219u8,129u8,53u8,57u8,8u8,103u8,192u8, - 127u8,0u8,3u8,112u8,98u8,12u8,140u8,150u8,193u8,178u8,168u8,40u8,74u8,232u8,156u8,181u8,207u8,206u8,79u8,78u8, - 15u8,171u8,247u8,184u8,165u8,1u8,32u8,23u8,21u8,27u8,212u8,214u8,208u8,151u8,94u8,104u8,61u8,239u8,102u8,183u8, - 181u8,181u8,142u8,211u8,130u8,69u8,73u8,58u8,27u8,93u8,17u8,202u8,114u8,100u8,162u8,126u8,58u8,153u8,142u8,163u8, - 34u8,98u8,26u8,167u8,234u8,243u8,191u8,61u8,62u8,187u8,120u8,253u8,250u8,237u8,254u8,219u8,195u8,227u8,243u8,224u8, - 213u8,222u8,187u8,189u8,227u8,253u8,67u8,57u8,247u8,142u8,54u8,247u8,126u8,152u8,36u8,48u8,61u8,40u8,213u8,34u8, - 75u8,111u8,88u8,146u8,38u8,143u8,255u8,140u8,178u8,148u8,47u8,164u8,79u8,120u8,112u8,120u8,118u8,126u8,122u8,177u8, - 127u8,254u8,246u8,228u8,56u8,56u8,121u8,13u8,64u8,31u8,255u8,251u8,240u8,244u8,36u8,56u8,63u8,249u8,175u8,195u8, - 99u8,57u8,241u8,83u8,125u8,98u8,100u8,236u8,112u8,66u8,72u8,234u8,243u8,69u8,46u8,35u8,134u8,115u8,235u8,179u8, - 210u8,28u8,132u8,139u8,189u8,163u8,147u8,139u8,227u8,115u8,57u8,211u8,115u8,107u8,38u8,194u8,39u8,242u8,236u8,48u8, - 75u8,255u8,140u8,146u8,54u8,215u8,242u8,218u8,196u8,32u8,37u8,105u8,30u8,3u8,63u8,48u8,64u8,249u8,117u8,92u8, - 92u8,13u8,178u8,240u8,218u8,192u8,200u8,235u8,211u8,147u8,127u8,151u8,160u8,118u8,214u8,93u8,36u8,204u8,166u8,163u8, - 44u8,28u8,68u8,36u8,117u8,69u8,90u8,132u8,99u8,150u8,207u8,166u8,211u8,241u8,13u8,74u8,158u8,66u8,252u8,32u8, - 30u8,14u8,129u8,206u8,176u8,171u8,24u8,41u8,48u8,129u8,223u8,200u8,8u8,181u8,93u8,234u8,94u8,188u8,127u8,255u8, - 238u8,247u8,224u8,226u8,253u8,155u8,211u8,189u8,3u8,78u8,102u8,124u8,116u8,114u8,122u8,94u8,146u8,185u8,163u8,75u8, - 231u8,49u8,168u8,31u8,92u8,11u8,215u8,39u8,189u8,16u8,227u8,138u8,41u8,27u8,167u8,201u8,200u8,153u8,253u8,120u8, - 239u8,232u8,16u8,176u8,127u8,18u8,188u8,59u8,57u8,126u8,163u8,166u8,211u8,197u8,240u8,236u8,102u8,114u8,153u8,142u8, - 23u8,158u8,240u8,236u8,247u8,163u8,87u8,39u8,239u8,220u8,41u8,117u8,177u8,59u8,135u8,137u8,62u8,134u8,227u8,25u8, - 129u8,41u8,245u8,127u8,120u8,57u8,22u8,211u8,147u8,134u8,35u8,99u8,92u8,50u8,37u8,3u8,245u8,153u8,131u8,124u8, - 12u8,98u8,180u8,178u8,151u8,51u8,122u8,134u8,90u8,142u8,33u8,202u8,135u8,49u8,224u8,49u8,193u8,165u8,12u8,252u8, - 237u8,189u8,121u8,115u8,122u8,248u8,102u8,239u8,124u8,239u8,213u8,187u8,67u8,206u8,30u8,255u8,189u8,247u8,238u8,66u8, - 236u8,119u8,239u8,244u8,141u8,98u8,229u8,206u8,150u8,163u8,154u8,247u8,113u8,138u8,48u8,41u8,114u8,67u8,57u8,243u8, - 137u8,143u8,246u8,254u8,39u8,40u8,177u8,247u8,238u8,240u8,248u8,205u8,185u8,210u8,147u8,155u8,27u8,187u8,190u8,55u8, - 5u8,90u8,204u8,119u8,77u8,238u8,65u8,214u8,4u8,79u8,36u8,4u8,243u8,159u8,205u8,250u8,197u8,12u8,44u8,64u8, - 249u8,233u8,81u8,8u8,251u8,83u8,31u8,0u8,34u8,64u8,113u8,229u8,192u8,52u8,224u8,112u8,8u8,21u8,248u8,164u8, - 72u8,63u8,144u8,122u8,100u8,97u8,34u8,173u8,198u8,63u8,128u8,183u8,103u8,121u8,145u8,14u8,110u8,56u8,94u8,248u8, - 4u8,196u8,245u8,47u8,166u8,87u8,176u8,187u8,116u8,194u8,164u8,94u8,249u8,149u8,52u8,94u8,78u8,18u8,194u8,29u8, - 27u8,165u8,164u8,184u8,248u8,9u8,246u8,5u8,54u8,64u8,165u8,47u8,44u8,20u8,140u8,105u8,171u8,151u8,137u8,166u8, - 180u8,185u8,22u8,61u8,187u8,45u8,225u8,63u8,149u8,0u8,75u8,141u8,77u8,210u8,197u8,74u8,227u8,143u8,186u8,60u8, - 134u8,79u8,105u8,142u8,54u8,247u8,215u8,194u8,241u8,56u8,189u8,38u8,113u8,153u8,77u8,1u8,51u8,145u8,154u8,173u8, - 228u8,196u8,132u8,129u8,247u8,145u8,221u8,24u8,172u8,18u8,126u8,76u8,227u8,1u8,34u8,6u8,21u8,249u8,99u8,48u8, - 118u8,241u8,240u8,230u8,241u8,117u8,6u8,98u8,141u8,84u8,25u8,142u8,227u8,126u8,145u8,183u8,217u8,73u8,50u8,190u8, - 81u8,19u8,42u8,150u8,27u8,1u8,24u8,196u8,106u8,6u8,163u8,93u8,222u8,176u8,61u8,180u8,237u8,236u8,181u8,180u8, - 237u8,172u8,177u8,254u8,169u8,211u8,52u8,176u8,186u8,167u8,49u8,242u8,157u8,49u8,236u8,74u8,195u8,92u8,116u8,151u8, - 94u8,146u8,131u8,245u8,163u8,240u8,83u8,60u8,153u8,77u8,24u8,40u8,181u8,60u8,198u8,25u8,221u8,233u8,57u8,190u8, - 45u8,150u8,189u8,216u8,217u8,2u8,50u8,118u8,54u8,158u8,33u8,147u8,62u8,219u8,218u8,218u8,121u8,186u8,181u8,181u8, - 254u8,116u8,243u8,233u8,250u8,243u8,237u8,237u8,206u8,78u8,71u8,183u8,42u8,123u8,236u8,42u8,29u8,15u8,184u8,95u8, - 1u8,172u8,59u8,141u8,250u8,241u8,48u8,238u8,11u8,208u8,209u8,153u8,6u8,118u8,68u8,67u8,157u8,167u8,253u8,24u8, - 13u8,45u8,35u8,111u8,17u8,54u8,130u8,14u8,162u8,114u8,125u8,72u8,39u8,160u8,195u8,6u8,168u8,249u8,16u8,77u8, - 73u8,154u8,97u8,54u8,32u8,226u8,24u8,153u8,61u8,79u8,103u8,89u8,31u8,181u8,41u8,24u8,176u8,28u8,249u8,127u8, - 156u8,246u8,193u8,5u8,40u8,72u8,167u8,162u8,204u8,56u8,236u8,77u8,154u8,190u8,130u8,2u8,31u8,162u8,27u8,13u8, - 255u8,8u8,105u8,151u8,139u8,132u8,122u8,173u8,165u8,62u8,229u8,134u8,162u8,203u8,46u8,211u8,116u8,92u8,62u8,21u8, - 102u8,34u8,160u8,189u8,228u8,93u8,221u8,231u8,125u8,113u8,192u8,63u8,163u8,71u8,218u8,68u8,210u8,152u8,120u8,199u8, - 252u8,75u8,124u8,168u8,15u8,170u8,35u8,35u8,97u8,151u8,91u8,21u8,135u8,116u8,64u8,53u8,69u8,187u8,205u8,173u8, - 245u8,141u8,103u8,27u8,155u8,59u8,59u8,207u8,55u8,214u8,159u8,111u8,62u8,219u8,218u8,217u8,132u8,255u8,54u8,159u8, - 110u8,237u8,172u8,63u8,221u8,218u8,236u8,60u8,221u8,121u8,182u8,209u8,233u8,108u8,109u8,111u8,27u8,90u8,40u8,25u8, - 198u8,163u8,89u8,198u8,35u8,162u8,226u8,42u8,44u8,112u8,110u8,48u8,234u8,99u8,30u8,46u8,93u8,70u8,87u8,225u8, - 199u8,56u8,37u8,114u8,115u8,227u8,166u8,195u8,194u8,222u8,114u8,43u8,49u8,140u8,163u8,241u8,64u8,205u8,9u8,252u8, - 155u8,71u8,69u8,139u8,191u8,217u8,7u8,161u8,4u8,218u8,228u8,68u8,106u8,18u8,112u8,238u8,206u8,42u8,171u8,153u8, - 178u8,105u8,152u8,193u8,7u8,17u8,184u8,120u8,196u8,166u8,166u8,129u8,204u8,13u8,82u8,159u8,209u8,186u8,28u8,104u8, - 15u8,109u8,105u8,254u8,64u8,76u8,157u8,235u8,84u8,212u8,176u8,251u8,22u8,130u8,130u8,108u8,194u8,55u8,28u8,94u8, - 166u8,179u8,194u8,203u8,199u8,109u8,70u8,44u8,5u8,142u8,65u8,194u8,213u8,15u8,223u8,135u8,110u8,23u8,65u8,219u8, - 10u8,189u8,235u8,176u8,35u8,174u8,177u8,16u8,55u8,38u8,160u8,95u8,186u8,140u8,71u8,146u8,221u8,238u8,25u8,253u8, - 108u8,25u8,186u8,194u8,181u8,199u8,45u8,80u8,95u8,51u8,216u8,234u8,13u8,2u8,126u8,149u8,102u8,224u8,64u8,50u8, - 80u8,138u8,57u8,110u8,72u8,188u8,134u8,211u8,182u8,141u8,105u8,94u8,3u8,236u8,209u8,167u8,16u8,113u8,219u8,98u8, - 103u8,176u8,74u8,56u8,69u8,189u8,116u8,144u8,142u8,199u8,97u8,134u8,36u8,59u8,123u8,115u8,80u8,142u8,200u8,105u8, - 209u8,122u8,192u8,142u8,103u8,147u8,75u8,174u8,7u8,6u8,128u8,188u8,73u8,56u8,46u8,99u8,149u8,81u8,84u8,144u8, - 114u8,135u8,191u8,179u8,210u8,118u8,105u8,14u8,143u8,23u8,170u8,120u8,200u8,122u8,114u8,170u8,30u8,139u8,254u8,111u8, - 134u8,83u8,246u8,54u8,122u8,45u8,216u8,231u8,101u8,56u8,14u8,147u8,62u8,57u8,16u8,189u8,237u8,245u8,237u8,158u8, - 240u8,172u8,96u8,251u8,51u8,193u8,121u8,114u8,74u8,244u8,234u8,226u8,124u8,58u8,14u8,111u8,56u8,44u8,33u8,135u8, - 2u8,189u8,251u8,237u8,54u8,142u8,108u8,224u8,4u8,236u8,9u8,88u8,99u8,246u8,203u8,47u8,108u8,163u8,215u8,108u8, - 107u8,114u8,206u8,87u8,7u8,129u8,122u8,214u8,170u8,208u8,216u8,164u8,159u8,21u8,155u8,144u8,69u8,250u8,132u8,78u8, - 60u8,64u8,167u8,225u8,143u8,248u8,180u8,43u8,34u8,219u8,23u8,110u8,128u8,235u8,10u8,61u8,233u8,2u8,22u8,77u8, - 226u8,2u8,181u8,230u8,245u8,21u8,216u8,246u8,60u8,5u8,207u8,46u8,44u8,109u8,133u8,242u8,198u8,74u8,183u8,53u8, - 78u8,112u8,139u8,137u8,151u8,17u8,117u8,205u8,68u8,172u8,55u8,200u8,210u8,105u8,203u8,49u8,72u8,124u8,1u8,191u8, - 25u8,95u8,6u8,40u8,229u8,62u8,163u8,18u8,157u8,84u8,1u8,101u8,168u8,190u8,59u8,66u8,181u8,31u8,78u8,195u8, - 203u8,152u8,236u8,65u8,6u8,108u8,18u8,103u8,156u8,210u8,147u8,24u8,131u8,6u8,158u8,199u8,209u8,86u8,60u8,130u8, - 199u8,229u8,136u8,10u8,169u8,236u8,167u8,211u8,27u8,5u8,196u8,252u8,165u8,134u8,89u8,20u8,149u8,129u8,56u8,13u8, - 51u8,214u8,124u8,77u8,159u8,175u8,122u8,213u8,203u8,89u8,150u8,120u8,54u8,248u8,10u8,30u8,223u8,113u8,41u8,233u8, - 253u8,158u8,235u8,113u8,75u8,159u8,212u8,171u8,147u8,164u8,120u8,63u8,187u8,28u8,199u8,57u8,152u8,109u8,243u8,61u8, - 97u8,59u8,192u8,22u8,240u8,64u8,125u8,12u8,203u8,112u8,13u8,140u8,46u8,89u8,204u8,29u8,118u8,161u8,248u8,57u8, - 216u8,83u8,156u8,168u8,223u8,224u8,73u8,148u8,38u8,27u8,206u8,244u8,32u8,63u8,224u8,147u8,7u8,124u8,242u8,134u8, - 157u8,98u8,97u8,107u8,60u8,79u8,214u8,212u8,88u8,197u8,78u8,8u8,65u8,82u8,7u8,126u8,100u8,69u8,96u8,13u8, - 182u8,39u8,107u8,238u8,170u8,41u8,38u8,233u8,199u8,40u8,40u8,82u8,251u8,141u8,150u8,105u8,112u8,62u8,59u8,6u8, - 102u8,8u8,202u8,34u8,98u8,183u8,98u8,166u8,91u8,61u8,228u8,137u8,165u8,118u8,66u8,141u8,212u8,71u8,3u8,55u8, - 64u8,191u8,18u8,34u8,229u8,254u8,21u8,58u8,247u8,35u8,88u8,50u8,75u8,72u8,169u8,41u8,135u8,87u8,168u8,120u8, - 90u8,140u8,220u8,40u8,92u8,78u8,210u8,8u8,84u8,37u8,40u8,54u8,122u8,36u8,16u8,44u8,121u8,68u8,217u8,102u8, - 221u8,69u8,224u8,88u8,38u8,236u8,114u8,168u8,5u8,98u8,37u8,240u8,149u8,168u8,109u8,73u8,106u8,113u8,251u8,217u8, - 4u8,49u8,38u8,22u8,204u8,45u8,100u8,172u8,18u8,255u8,144u8,147u8,176u8,112u8,11u8,222u8,204u8,218u8,4u8,108u8, - 243u8,101u8,10u8,57u8,179u8,235u8,96u8,52u8,78u8,193u8,4u8,4u8,240u8,228u8,133u8,14u8,197u8,175u8,141u8,127u8, - 218u8,147u8,182u8,205u8,121u8,202u8,53u8,126u8,113u8,22u8,16u8,251u8,180u8,136u8,39u8,241u8,109u8,56u8,248u8,92u8, - 208u8,1u8,153u8,20u8,114u8,228u8,142u8,112u8,236u8,163u8,123u8,128u8,206u8,47u8,75u8,162u8,107u8,143u8,219u8,77u8, - 209u8,143u8,136u8,129u8,129u8,238u8,67u8,88u8,24u8,197u8,3u8,44u8,118u8,111u8,28u8,131u8,122u8,237u8,181u8,49u8, - 95u8,19u8,113u8,15u8,140u8,76u8,140u8,92u8,11u8,179u8,21u8,106u8,153u8,20u8,130u8,24u8,147u8,159u8,188u8,113u8, - 138u8,202u8,34u8,98u8,136u8,147u8,0u8,191u8,92u8,70u8,253u8,89u8,152u8,115u8,243u8,73u8,158u8,76u8,164u8,229u8, - 97u8,123u8,139u8,136u8,165u8,190u8,37u8,74u8,151u8,150u8,222u8,115u8,181u8,144u8,118u8,221u8,32u8,169u8,212u8,74u8, - 159u8,77u8,234u8,151u8,145u8,225u8,75u8,230u8,230u8,177u8,33u8,3u8,111u8,131u8,237u8,10u8,171u8,8u8,101u8,52u8, - 190u8,90u8,104u8,121u8,45u8,182u8,10u8,173u8,216u8,138u8,51u8,134u8,19u8,219u8,66u8,76u8,142u8,105u8,157u8,12u8, - 168u8,25u8,115u8,135u8,171u8,38u8,189u8,1u8,180u8,196u8,252u8,85u8,53u8,146u8,115u8,23u8,185u8,1u8,142u8,208u8, - 48u8,204u8,131u8,150u8,181u8,234u8,237u8,52u8,185u8,176u8,218u8,72u8,229u8,102u8,90u8,71u8,104u8,183u8,139u8,81u8, - 114u8,99u8,13u8,103u8,108u8,19u8,212u8,26u8,186u8,228u8,251u8,47u8,217u8,186u8,189u8,233u8,131u8,44u8,164u8,84u8, - 214u8,149u8,39u8,168u8,108u8,161u8,191u8,79u8,169u8,9u8,200u8,203u8,128u8,50u8,163u8,84u8,32u8,234u8,175u8,140u8, - 48u8,197u8,115u8,22u8,152u8,93u8,25u8,132u8,217u8,128u8,70u8,84u8,34u8,99u8,128u8,203u8,212u8,50u8,155u8,64u8, - 5u8,234u8,134u8,90u8,116u8,84u8,146u8,27u8,125u8,125u8,139u8,250u8,168u8,176u8,79u8,14u8,78u8,186u8,148u8,169u8, - 162u8,156u8,76u8,156u8,124u8,12u8,179u8,56u8,196u8,124u8,29u8,183u8,96u8,80u8,68u8,154u8,70u8,25u8,250u8,220u8, - 121u8,62u8,155u8,96u8,246u8,32u8,35u8,27u8,11u8,114u8,216u8,7u8,223u8,232u8,53u8,228u8,16u8,222u8,71u8,217u8, - 43u8,8u8,84u8,63u8,180u8,141u8,185u8,249u8,251u8,6u8,5u8,16u8,130u8,0u8,124u8,228u8,128u8,68u8,191u8,161u8, - 145u8,2u8,81u8,47u8,184u8,184u8,164u8,202u8,237u8,238u8,253u8,136u8,74u8,202u8,248u8,231u8,134u8,24u8,247u8,66u8, - 173u8,208u8,98u8,84u8,172u8,130u8,218u8,204u8,172u8,8u8,210u8,97u8,0u8,41u8,149u8,81u8,212u8,152u8,159u8,69u8, - 107u8,54u8,69u8,0u8,73u8,179u8,235u8,251u8,154u8,93u8,54u8,136u8,48u8,37u8,28u8,45u8,1u8,174u8,6u8,207u8, - 34u8,114u8,40u8,129u8,5u8,199u8,5u8,28u8,192u8,102u8,141u8,48u8,30u8,69u8,217u8,8u8,244u8,110u8,15u8,151u8, - 236u8,9u8,119u8,216u8,17u8,193u8,70u8,111u8,144u8,23u8,196u8,72u8,210u8,219u8,247u8,176u8,222u8,4u8,103u8,170u8, - 101u8,61u8,57u8,201u8,92u8,246u8,107u8,121u8,179u8,12u8,77u8,75u8,58u8,41u8,215u8,253u8,89u8,168u8,142u8,91u8, - 32u8,104u8,89u8,133u8,114u8,104u8,221u8,224u8,111u8,33u8,62u8,32u8,212u8,215u8,169u8,171u8,225u8,31u8,44u8,48u8, - 199u8,191u8,4u8,212u8,75u8,3u8,221u8,207u8,228u8,236u8,155u8,151u8,17u8,48u8,22u8,89u8,204u8,244u8,31u8,6u8, - 203u8,154u8,51u8,95u8,129u8,226u8,74u8,172u8,246u8,249u8,18u8,1u8,14u8,171u8,69u8,110u8,185u8,35u8,190u8,16u8, - 249u8,19u8,93u8,153u8,9u8,107u8,85u8,7u8,6u8,20u8,184u8,45u8,76u8,25u8,26u8,162u8,249u8,52u8,101u8,153u8, - 64u8,79u8,209u8,177u8,179u8,15u8,241u8,84u8,194u8,78u8,138u8,109u8,40u8,241u8,98u8,168u8,115u8,252u8,7u8,31u8, - 53u8,52u8,197u8,217u8,180u8,56u8,154u8,235u8,64u8,93u8,152u8,13u8,10u8,147u8,182u8,231u8,110u8,249u8,75u8,143u8, - 179u8,83u8,230u8,182u8,212u8,22u8,192u8,222u8,106u8,24u8,178u8,156u8,40u8,34u8,217u8,75u8,8u8,73u8,33u8,65u8, - 218u8,47u8,74u8,105u8,228u8,75u8,180u8,185u8,182u8,118u8,68u8,178u8,130u8,245u8,21u8,195u8,115u8,126u8,110u8,86u8, - 56u8,74u8,111u8,64u8,249u8,67u8,132u8,93u8,237u8,28u8,65u8,206u8,48u8,26u8,79u8,181u8,87u8,184u8,155u8,147u8, - 73u8,59u8,138u8,54u8,165u8,172u8,128u8,202u8,141u8,114u8,12u8,115u8,30u8,66u8,155u8,192u8,223u8,208u8,56u8,166u8, - 169u8,184u8,195u8,18u8,44u8,85u8,251u8,5u8,76u8,168u8,223u8,69u8,73u8,56u8,29u8,234u8,51u8,148u8,40u8,208u8, - 222u8,211u8,209u8,11u8,147u8,55u8,214u8,212u8,103u8,77u8,29u8,1u8,255u8,241u8,191u8,31u8,227u8,232u8,250u8,15u8, - 215u8,45u8,192u8,180u8,153u8,150u8,174u8,72u8,175u8,193u8,21u8,226u8,69u8,61u8,209u8,137u8,48u8,208u8,170u8,123u8, - 142u8,179u8,46u8,134u8,106u8,80u8,210u8,4u8,106u8,175u8,77u8,94u8,77u8,168u8,101u8,96u8,169u8,242u8,13u8,70u8, - 68u8,63u8,67u8,236u8,172u8,172u8,59u8,218u8,203u8,104u8,170u8,22u8,255u8,9u8,3u8,1u8,6u8,48u8,160u8,198u8, - 137u8,70u8,101u8,5u8,82u8,27u8,168u8,97u8,213u8,96u8,104u8,63u8,51u8,243u8,101u8,219u8,165u8,186u8,90u8,4u8, - 199u8,61u8,244u8,189u8,122u8,210u8,249u8,162u8,68u8,140u8,85u8,164u8,54u8,11u8,199u8,182u8,138u8,146u8,142u8,23u8, - 113u8,150u8,246u8,162u8,201u8,93u8,150u8,71u8,69u8,121u8,30u8,206u8,128u8,148u8,223u8,211u8,54u8,81u8,197u8,161u8, - 205u8,229u8,54u8,211u8,211u8,89u8,143u8,246u8,161u8,85u8,136u8,65u8,237u8,66u8,241u8,58u8,138u8,63u8,70u8,117u8, - 236u8,51u8,143u8,202u8,94u8,237u8,90u8,187u8,215u8,122u8,245u8,179u8,168u8,68u8,36u8,86u8,149u8,210u8,1u8,28u8, - 95u8,48u8,209u8,111u8,38u8,31u8,77u8,142u8,71u8,2u8,104u8,224u8,186u8,124u8,182u8,48u8,133u8,218u8,184u8,240u8, - 162u8,187u8,200u8,239u8,151u8,140u8,213u8,182u8,203u8,103u8,250u8,75u8,54u8,204u8,151u8,94u8,152u8,112u8,247u8,72u8, - 243u8,174u8,56u8,197u8,123u8,159u8,244u8,174u8,134u8,122u8,9u8,128u8,137u8,252u8,217u8,179u8,47u8,134u8,112u8,185u8, - 224u8,162u8,40u8,183u8,60u8,48u8,55u8,197u8,172u8,243u8,17u8,101u8,67u8,204u8,173u8,136u8,172u8,51u8,122u8,138u8, - 191u8,214u8,110u8,10u8,141u8,229u8,36u8,188u8,185u8,148u8,89u8,55u8,76u8,186u8,220u8,139u8,179u8,104u8,150u8,93u8, - 195u8,53u8,146u8,205u8,111u8,160u8,152u8,48u8,117u8,220u8,208u8,215u8,107u8,54u8,221u8,64u8,236u8,95u8,64u8,224u8, - 20u8,235u8,187u8,253u8,15u8,98u8,115u8,45u8,38u8,203u8,162u8,125u8,204u8,97u8,96u8,168u8,195u8,163u8,47u8,217u8, - 83u8,166u8,57u8,193u8,102u8,236u8,133u8,251u8,83u8,59u8,147u8,128u8,240u8,13u8,154u8,112u8,236u8,58u8,195u8,184u8, - 191u8,253u8,146u8,121u8,251u8,214u8,40u8,218u8,242u8,14u8,149u8,139u8,208u8,86u8,121u8,36u8,86u8,186u8,126u8,44u8, - 194u8,108u8,225u8,103u8,239u8,128u8,36u8,77u8,162u8,70u8,179u8,42u8,206u8,225u8,201u8,215u8,190u8,227u8,99u8,61u8, - 161u8,228u8,175u8,140u8,126u8,40u8,201u8,212u8,87u8,153u8,96u8,163u8,216u8,170u8,61u8,103u8,189u8,0u8,254u8,232u8, - 105u8,233u8,201u8,41u8,58u8,12u8,162u8,189u8,42u8,139u8,168u8,81u8,133u8,167u8,37u8,123u8,86u8,110u8,89u8,81u8, - 219u8,227u8,183u8,192u8,155u8,62u8,119u8,190u8,190u8,222u8,138u8,144u8,128u8,211u8,94u8,181u8,140u8,215u8,93u8,247u8, - 48u8,177u8,30u8,74u8,117u8,165u8,252u8,184u8,33u8,149u8,21u8,10u8,255u8,202u8,214u8,85u8,12u8,140u8,1u8,255u8, - 56u8,30u8,4u8,97u8,54u8,154u8,97u8,241u8,177u8,225u8,52u8,25u8,25u8,97u8,175u8,79u8,114u8,252u8,233u8,202u8, - 175u8,44u8,65u8,53u8,44u8,143u8,224u8,212u8,177u8,189u8,151u8,213u8,49u8,166u8,151u8,98u8,168u8,71u8,229u8,24u8, - 133u8,106u8,227u8,157u8,184u8,92u8,231u8,75u8,18u8,86u8,50u8,159u8,42u8,200u8,148u8,158u8,206u8,18u8,76u8,139u8, - 12u8,182u8,50u8,198u8,85u8,57u8,122u8,21u8,146u8,240u8,73u8,177u8,43u8,111u8,24u8,198u8,99u8,222u8,158u8,130u8, - 253u8,52u8,60u8,211u8,10u8,127u8,66u8,153u8,154u8,23u8,0u8,205u8,46u8,37u8,98u8,124u8,48u8,215u8,106u8,94u8, - 189u8,1u8,47u8,234u8,242u8,85u8,46u8,111u8,8u8,82u8,205u8,91u8,239u8,118u8,121u8,171u8,1u8,123u8,252u8,88u8, - 24u8,58u8,68u8,4u8,181u8,65u8,136u8,15u8,74u8,199u8,30u8,50u8,192u8,184u8,89u8,92u8,200u8,172u8,161u8,40u8, - 193u8,11u8,16u8,197u8,171u8,10u8,166u8,37u8,162u8,239u8,34u8,151u8,173u8,218u8,128u8,90u8,160u8,202u8,19u8,77u8, - 235u8,5u8,19u8,164u8,1u8,73u8,37u8,163u8,226u8,252u8,221u8,8u8,241u8,32u8,35u8,115u8,40u8,49u8,5u8,84u8, - 196u8,91u8,58u8,66u8,199u8,81u8,13u8,125u8,138u8,150u8,34u8,147u8,155u8,214u8,17u8,133u8,223u8,178u8,109u8,74u8, - 186u8,86u8,148u8,187u8,193u8,167u8,16u8,72u8,196u8,83u8,72u8,210u8,20u8,101u8,15u8,3u8,37u8,108u8,177u8,210u8, - 139u8,177u8,19u8,53u8,173u8,120u8,28u8,39u8,154u8,119u8,94u8,52u8,81u8,149u8,253u8,90u8,125u8,200u8,106u8,160u8, - 123u8,53u8,145u8,235u8,151u8,98u8,1u8,239u8,254u8,126u8,214u8,8u8,207u8,165u8,222u8,187u8,9u8,72u8,157u8,76u8, - 226u8,28u8,67u8,138u8,96u8,16u8,37u8,160u8,62u8,27u8,162u8,45u8,181u8,2u8,116u8,209u8,160u8,143u8,196u8,228u8, - 237u8,71u8,102u8,147u8,146u8,9u8,131u8,205u8,128u8,102u8,175u8,147u8,9u8,143u8,209u8,81u8,240u8,89u8,105u8,143u8, - 50u8,118u8,103u8,183u8,126u8,136u8,40u8,193u8,84u8,193u8,235u8,190u8,212u8,18u8,242u8,48u8,181u8,22u8,163u8,98u8, - 71u8,5u8,241u8,152u8,79u8,79u8,43u8,65u8,121u8,124u8,60u8,70u8,125u8,57u8,30u8,115u8,93u8,45u8,210u8,0u8, - 61u8,122u8,167u8,199u8,189u8,69u8,105u8,27u8,30u8,99u8,163u8,60u8,181u8,74u8,246u8,120u8,42u8,159u8,119u8,42u8, - 171u8,117u8,242u8,20u8,139u8,22u8,240u8,73u8,60u8,81u8,237u8,86u8,32u8,36u8,143u8,80u8,186u8,30u8,129u8,40u8, - 104u8,205u8,205u8,165u8,91u8,78u8,241u8,55u8,55u8,93u8,168u8,162u8,176u8,69u8,42u8,25u8,169u8,41u8,67u8,219u8, - 250u8,240u8,228u8,143u8,102u8,0u8,251u8,117u8,17u8,188u8,104u8,170u8,182u8,75u8,64u8,248u8,103u8,112u8,167u8,180u8, - 178u8,26u8,233u8,114u8,163u8,240u8,114u8,95u8,214u8,58u8,66u8,117u8,61u8,220u8,102u8,134u8,131u8,90u8,70u8,184u8, - 98u8,131u8,120u8,140u8,227u8,75u8,51u8,255u8,26u8,69u8,200u8,49u8,104u8,97u8,71u8,73u8,198u8,171u8,222u8,105u8, - 22u8,143u8,98u8,244u8,229u8,69u8,91u8,43u8,157u8,222u8,225u8,190u8,2u8,188u8,15u8,241u8,158u8,39u8,246u8,17u8, - 58u8,212u8,95u8,28u8,178u8,51u8,242u8,154u8,169u8,171u8,171u8,14u8,73u8,204u8,104u8,60u8,253u8,235u8,75u8,49u8, - 184u8,6u8,71u8,190u8,198u8,121u8,221u8,63u8,210u8,166u8,123u8,169u8,255u8,241u8,88u8,76u8,109u8,150u8,71u8,92u8, - 87u8,182u8,18u8,203u8,136u8,59u8,236u8,11u8,206u8,84u8,216u8,248u8,165u8,209u8,29u8,128u8,244u8,45u8,130u8,242u8, - 58u8,44u8,83u8,150u8,22u8,91u8,22u8,2u8,23u8,39u8,21u8,72u8,91u8,175u8,66u8,145u8,62u8,207u8,173u8,25u8, - 98u8,143u8,193u8,139u8,237u8,223u8,4u8,128u8,159u8,236u8,38u8,128u8,45u8,148u8,225u8,246u8,107u8,217u8,192u8,83u8, - 218u8,34u8,236u8,68u8,204u8,120u8,183u8,42u8,121u8,22u8,224u8,77u8,230u8,58u8,10u8,104u8,22u8,66u8,4u8,239u8, - 254u8,9u8,74u8,77u8,118u8,7u8,207u8,43u8,144u8,147u8,144u8,159u8,229u8,244u8,11u8,45u8,87u8,176u8,88u8,177u8, - 173u8,114u8,172u8,18u8,230u8,209u8,51u8,73u8,153u8,5u8,176u8,123u8,145u8,12u8,189u8,248u8,229u8,205u8,43u8,115u8, - 176u8,59u8,75u8,190u8,71u8,252u8,82u8,39u8,145u8,99u8,5u8,47u8,84u8,151u8,172u8,214u8,159u8,133u8,237u8,66u8, - 216u8,59u8,93u8,223u8,56u8,139u8,86u8,145u8,44u8,91u8,105u8,151u8,62u8,130u8,177u8,196u8,55u8,219u8,21u8,136u8, - 231u8,139u8,5u8,78u8,74u8,73u8,108u8,67u8,107u8,190u8,242u8,248u8,253u8,21u8,205u8,65u8,84u8,197u8,212u8,208u8, - 0u8,91u8,229u8,147u8,80u8,185u8,18u8,105u8,6u8,37u8,25u8,185u8,130u8,238u8,50u8,0u8,192u8,216u8,178u8,111u8, - 117u8,17u8,99u8,40u8,52u8,243u8,32u8,165u8,93u8,239u8,104u8,85u8,5u8,220u8,104u8,250u8,116u8,232u8,188u8,238u8, - 151u8,171u8,237u8,171u8,143u8,207u8,85u8,120u8,100u8,252u8,180u8,16u8,111u8,223u8,201u8,103u8,253u8,126u8,68u8,141u8, - 197u8,152u8,236u8,244u8,180u8,131u8,133u8,16u8,125u8,151u8,141u8,199u8,98u8,183u8,115u8,54u8,120u8,223u8,54u8,169u8, - 69u8,253u8,206u8,185u8,71u8,148u8,154u8,149u8,206u8,244u8,221u8,211u8,37u8,134u8,24u8,125u8,245u8,20u8,137u8,157u8, - 160u8,124u8,59u8,148u8,163u8,241u8,92u8,20u8,230u8,41u8,49u8,44u8,133u8,224u8,137u8,194u8,173u8,89u8,230u8,57u8, - 229u8,133u8,242u8,42u8,106u8,82u8,69u8,52u8,130u8,116u8,245u8,99u8,151u8,172u8,114u8,43u8,63u8,123u8,211u8,48u8, - 176u8,49u8,83u8,218u8,27u8,21u8,155u8,171u8,78u8,228u8,128u8,183u8,218u8,191u8,242u8,103u8,45u8,111u8,171u8,243u8, - 56u8,102u8,99u8,219u8,190u8,234u8,101u8,27u8,65u8,29u8,202u8,56u8,130u8,90u8,246u8,251u8,228u8,212u8,137u8,11u8, - 30u8,241u8,19u8,210u8,193u8,248u8,139u8,12u8,252u8,85u8,62u8,39u8,182u8,206u8,130u8,136u8,249u8,184u8,106u8,0u8, - 59u8,1u8,110u8,249u8,101u8,212u8,7u8,82u8,138u8,140u8,184u8,60u8,55u8,153u8,230u8,84u8,142u8,199u8,103u8,177u8, - 214u8,199u8,47u8,26u8,249u8,101u8,192u8,171u8,102u8,110u8,96u8,237u8,165u8,165u8,242u8,200u8,81u8,209u8,111u8,55u8, - 219u8,66u8,85u8,145u8,211u8,111u8,30u8,46u8,69u8,87u8,223u8,86u8,170u8,156u8,98u8,110u8,157u8,77u8,141u8,172u8, - 49u8,78u8,101u8,79u8,229u8,130u8,13u8,255u8,243u8,250u8,238u8,253u8,13u8,234u8,147u8,20u8,128u8,73u8,179u8,64u8, - 118u8,157u8,151u8,199u8,30u8,192u8,7u8,107u8,84u8,230u8,112u8,88u8,141u8,85u8,180u8,187u8,166u8,189u8,209u8,134u8, - 214u8,30u8,136u8,104u8,2u8,245u8,53u8,150u8,178u8,218u8,98u8,2u8,243u8,180u8,161u8,150u8,2u8,188u8,101u8,1u8, - 219u8,226u8,134u8,207u8,137u8,34u8,206u8,176u8,92u8,136u8,69u8,157u8,114u8,141u8,30u8,100u8,60u8,148u8,224u8,138u8, - 148u8,152u8,78u8,63u8,247u8,36u8,137u8,93u8,22u8,168u8,239u8,112u8,68u8,182u8,182u8,132u8,204u8,53u8,133u8,223u8, - 25u8,133u8,23u8,111u8,237u8,85u8,6u8,124u8,165u8,204u8,129u8,94u8,167u8,193u8,27u8,22u8,205u8,228u8,204u8,15u8, - 153u8,66u8,196u8,121u8,6u8,91u8,125u8,61u8,250u8,221u8,217u8,251u8,122u8,144u8,94u8,148u8,63u8,135u8,86u8,211u8, - 150u8,49u8,63u8,63u8,40u8,78u8,255u8,7u8,124u8,146u8,70u8,221u8,157u8,5u8,115u8,128u8,146u8,236u8,50u8,142u8, - 146u8,81u8,113u8,213u8,88u8,67u8,38u8,107u8,202u8,110u8,75u8,251u8,244u8,112u8,107u8,14u8,86u8,140u8,99u8,218u8, - 77u8,79u8,26u8,209u8,94u8,141u8,51u8,173u8,185u8,158u8,113u8,6u8,121u8,222u8,138u8,214u8,57u8,238u8,166u8,55u8, - 21u8,42u8,122u8,170u8,92u8,84u8,91u8,78u8,8u8,137u8,183u8,241u8,68u8,136u8,186u8,241u8,76u8,137u8,189u8,249u8, - 166u8,16u8,32u8,244u8,133u8,76u8,161u8,2u8,198u8,54u8,235u8,171u8,94u8,63u8,7u8,220u8,148u8,134u8,60u8,24u8, - 217u8,178u8,4u8,15u8,188u8,37u8,89u8,132u8,181u8,10u8,175u8,122u8,226u8,242u8,214u8,115u8,46u8,68u8,42u8,46u8, - 133u8,6u8,29u8,63u8,149u8,2u8,204u8,62u8,223u8,214u8,201u8,48u8,125u8,92u8,37u8,198u8,240u8,161u8,99u8,21u8, - 31u8,241u8,110u8,215u8,71u8,188u8,235u8,233u8,58u8,21u8,94u8,19u8,63u8,18u8,196u8,120u8,197u8,12u8,61u8,52u8, - 81u8,22u8,195u8,106u8,18u8,152u8,80u8,213u8,251u8,202u8,83u8,166u8,144u8,173u8,196u8,224u8,145u8,39u8,76u8,168u8, - 5u8,163u8,60u8,103u8,205u8,171u8,3u8,249u8,108u8,34u8,123u8,87u8,112u8,17u8,74u8,17u8,229u8,122u8,19u8,45u8, - 57u8,123u8,61u8,126u8,74u8,215u8,211u8,86u8,91u8,182u8,211u8,86u8,55u8,207u8,218u8,233u8,57u8,109u8,178u8,186u8, - 4u8,167u8,167u8,121u8,91u8,52u8,88u8,155u8,61u8,175u8,236u8,63u8,245u8,25u8,197u8,179u8,23u8,243u8,155u8,171u8, - 157u8,228u8,169u8,54u8,203u8,174u8,211u8,110u8,170u8,146u8,83u8,206u8,226u8,90u8,38u8,75u8,111u8,86u8,198u8,19u8, - 106u8,232u8,71u8,107u8,9u8,228u8,123u8,22u8,236u8,205u8,170u8,103u8,21u8,51u8,105u8,85u8,207u8,83u8,205u8,71u8, - 151u8,157u8,136u8,110u8,18u8,26u8,63u8,245u8,26u8,85u8,95u8,193u8,80u8,36u8,89u8,170u8,22u8,87u8,134u8,206u8, - 74u8,7u8,214u8,21u8,245u8,23u8,42u8,221u8,49u8,43u8,69u8,222u8,172u8,172u8,229u8,125u8,23u8,165u8,122u8,108u8, - 255u8,94u8,164u8,84u8,175u8,225u8,198u8,78u8,208u8,214u8,165u8,157u8,53u8,238u8,144u8,197u8,184u8,218u8,28u8,209u8, - 221u8,189u8,16u8,45u8,216u8,62u8,195u8,150u8,186u8,199u8,253u8,56u8,235u8,207u8,98u8,94u8,162u8,28u8,164u8,120u8, - 30u8,227u8,74u8,150u8,145u8,101u8,87u8,122u8,121u8,47u8,145u8,117u8,113u8,143u8,217u8,78u8,44u8,9u8,181u8,84u8, - 101u8,113u8,137u8,194u8,177u8,68u8,67u8,87u8,206u8,234u8,156u8,149u8,154u8,83u8,33u8,150u8,25u8,70u8,79u8,62u8, - 209u8,130u8,162u8,84u8,149u8,37u8,221u8,214u8,117u8,59u8,70u8,254u8,178u8,184u8,207u8,128u8,162u8,44u8,219u8,252u8, - 154u8,151u8,26u8,40u8,208u8,65u8,65u8,241u8,135u8,193u8,149u8,231u8,138u8,3u8,69u8,39u8,115u8,54u8,231u8,190u8, - 131u8,234u8,233u8,204u8,219u8,15u8,60u8,243u8,205u8,53u8,192u8,132u8,36u8,207u8,161u8,75u8,153u8,88u8,46u8,107u8, - 79u8,234u8,14u8,30u8,45u8,83u8,65u8,5u8,146u8,30u8,254u8,191u8,71u8,74u8,179u8,72u8,123u8,21u8,25u8,81u8, - 153u8,169u8,246u8,105u8,66u8,28u8,239u8,137u8,45u8,138u8,116u8,145u8,54u8,139u8,5u8,243u8,205u8,192u8,7u8,18u8, - 175u8,26u8,8u8,184u8,178u8,167u8,69u8,64u8,208u8,179u8,81u8,164u8,85u8,53u8,85u8,189u8,199u8,80u8,22u8,75u8, - 75u8,87u8,129u8,23u8,137u8,28u8,75u8,64u8,239u8,185u8,245u8,30u8,167u8,214u8,131u8,77u8,233u8,159u8,61u8,165u8, - 27u8,27u8,8u8,73u8,125u8,81u8,11u8,165u8,94u8,32u8,147u8,88u8,14u8,173u8,200u8,39u8,1u8,60u8,243u8,166u8, - 88u8,237u8,76u8,185u8,6u8,166u8,7u8,79u8,11u8,68u8,130u8,46u8,97u8,234u8,173u8,148u8,143u8,80u8,119u8,209u8, - 108u8,63u8,186u8,29u8,190u8,100u8,183u8,131u8,165u8,96u8,106u8,219u8,29u8,44u8,189u8,101u8,66u8,100u8,94u8,86u8, - 240u8,89u8,153u8,197u8,10u8,64u8,22u8,105u8,233u8,241u8,231u8,87u8,69u8,122u8,181u8,103u8,242u8,158u8,112u8,16u8, - 195u8,242u8,116u8,105u8,111u8,221u8,21u8,80u8,219u8,9u8,170u8,41u8,187u8,46u8,114u8,244u8,110u8,189u8,58u8,23u8, - 44u8,122u8,50u8,168u8,69u8,141u8,202u8,123u8,154u8,235u8,42u8,43u8,170u8,154u8,215u8,138u8,23u8,85u8,224u8,145u8, - 194u8,44u8,157u8,229u8,50u8,65u8,14u8,238u8,42u8,242u8,87u8,233u8,201u8,138u8,110u8,7u8,84u8,65u8,67u8,58u8, - 104u8,137u8,205u8,229u8,149u8,61u8,17u8,101u8,81u8,207u8,208u8,135u8,101u8,165u8,175u8,58u8,216u8,178u8,29u8,146u8, - 218u8,176u8,12u8,11u8,114u8,106u8,214u8,221u8,106u8,60u8,240u8,43u8,39u8,44u8,7u8,222u8,3u8,55u8,190u8,103u8, - 65u8,45u8,31u8,117u8,23u8,78u8,228u8,212u8,68u8,138u8,0u8,175u8,156u8,175u8,6u8,90u8,126u8,127u8,196u8,124u8, - 104u8,101u8,103u8,153u8,6u8,109u8,217u8,19u8,88u8,21u8,235u8,218u8,208u8,214u8,196u8,196u8,168u8,94u8,196u8,124u8, - 86u8,73u8,25u8,42u8,13u8,69u8,128u8,85u8,178u8,63u8,140u8,219u8,52u8,194u8,15u8,209u8,17u8,132u8,233u8,55u8, - 234u8,230u8,138u8,185u8,111u8,238u8,107u8,133u8,6u8,207u8,181u8,59u8,149u8,219u8,81u8,19u8,24u8,119u8,66u8,213u8, - 240u8,151u8,111u8,64u8,37u8,105u8,237u8,151u8,171u8,118u8,99u8,101u8,57u8,135u8,48u8,44u8,152u8,224u8,184u8,69u8, - 108u8,218u8,10u8,50u8,200u8,26u8,156u8,181u8,59u8,174u8,222u8,159u8,206u8,13u8,190u8,211u8,252u8,158u8,155u8,5u8, - 202u8,143u8,193u8,134u8,101u8,1u8,162u8,164u8,54u8,173u8,172u8,173u8,102u8,170u8,120u8,233u8,43u8,154u8,105u8,37u8, - 145u8,48u8,155u8,21u8,195u8,103u8,141u8,203u8,71u8,56u8,150u8,17u8,74u8,31u8,53u8,235u8,95u8,60u8,58u8,176u8, - 223u8,240u8,231u8,173u8,76u8,4u8,151u8,6u8,98u8,25u8,66u8,131u8,146u8,84u8,166u8,255u8,91u8,167u8,58u8,234u8, - 0u8,165u8,54u8,90u8,154u8,16u8,181u8,148u8,124u8,64u8,226u8,120u8,30u8,155u8,87u8,210u8,115u8,57u8,34u8,148u8, - 252u8,163u8,66u8,87u8,141u8,123u8,92u8,46u8,155u8,7u8,120u8,45u8,81u8,157u8,48u8,66u8,92u8,93u8,225u8,219u8, - 31u8,79u8,47u8,121u8,233u8,137u8,149u8,78u8,170u8,110u8,214u8,251u8,173u8,203u8,117u8,98u8,47u8,79u8,149u8,42u8, - 158u8,228u8,144u8,183u8,88u8,231u8,153u8,40u8,211u8,104u8,158u8,144u8,15u8,201u8,218u8,134u8,60u8,113u8,111u8,30u8, - 136u8,124u8,20u8,55u8,100u8,6u8,117u8,68u8,239u8,221u8,154u8,2u8,208u8,141u8,121u8,60u8,78u8,55u8,135u8,175u8, - 217u8,50u8,166u8,247u8,92u8,249u8,35u8,247u8,225u8,55u8,29u8,166u8,139u8,164u8,16u8,103u8,197u8,216u8,10u8,137u8, - 38u8,51u8,10u8,120u8,181u8,192u8,182u8,233u8,177u8,119u8,2u8,2u8,216u8,249u8,63u8,215u8,63u8,117u8,90u8,58u8, - 229u8,249u8,179u8,141u8,102u8,5u8,95u8,65u8,21u8,19u8,219u8,193u8,225u8,135u8,203u8,80u8,181u8,252u8,164u8,127u8, - 184u8,20u8,235u8,136u8,108u8,104u8,117u8,168u8,179u8,38u8,208u8,190u8,235u8,102u8,68u8,228u8,5u8,46u8,34u8,98u8, - 80u8,58u8,94u8,155u8,211u8,98u8,12u8,13u8,230u8,186u8,37u8,253u8,156u8,53u8,119u8,93u8,123u8,118u8,59u8,28u8, - 162u8,19u8,171u8,47u8,235u8,12u8,135u8,9u8,173u8,56u8,27u8,250u8,210u8,107u8,65u8,188u8,55u8,102u8,44u8,109u8, - 14u8,93u8,236u8,46u8,45u8,207u8,149u8,86u8,83u8,204u8,221u8,186u8,67u8,33u8,167u8,243u8,204u8,252u8,27u8,213u8, - 193u8,162u8,186u8,215u8,221u8,146u8,247u8,173u8,10u8,26u8,139u8,48u8,243u8,23u8,235u8,108u8,223u8,154u8,168u8,219u8, - 107u8,51u8,52u8,155u8,162u8,205u8,121u8,221u8,87u8,188u8,163u8,195u8,199u8,250u8,219u8,248u8,50u8,47u8,82u8,119u8, - 124u8,213u8,55u8,126u8,120u8,215u8,30u8,32u8,235u8,217u8,27u8,158u8,33u8,234u8,208u8,169u8,61u8,8u8,245u8,231u8, - 166u8,47u8,20u8,175u8,81u8,137u8,157u8,245u8,245u8,57u8,250u8,176u8,148u8,169u8,74u8,5u8,168u8,146u8,91u8,46u8, - 53u8,90u8,142u8,232u8,181u8,216u8,182u8,23u8,113u8,242u8,182u8,2u8,109u8,14u8,93u8,158u8,113u8,131u8,219u8,0u8, - 235u8,150u8,7u8,35u8,158u8,161u8,142u8,72u8,202u8,241u8,219u8,247u8,33u8,58u8,97u8,107u8,199u8,135u8,98u8,61u8, - 191u8,230u8,195u8,66u8,103u8,189u8,89u8,209u8,51u8,207u8,239u8,218u8,17u8,211u8,183u8,216u8,83u8,223u8,81u8,29u8, - 32u8,145u8,117u8,70u8,231u8,78u8,224u8,63u8,135u8,249u8,159u8,25u8,135u8,41u8,132u8,229u8,90u8,251u8,91u8,152u8, - 174u8,32u8,73u8,133u8,187u8,246u8,195u8,136u8,173u8,222u8,136u8,173u8,198u8,215u8,43u8,5u8,66u8,246u8,107u8,205u8, - 113u8,246u8,230u8,232u8,107u8,173u8,76u8,70u8,229u8,119u8,47u8,239u8,91u8,170u8,122u8,5u8,106u8,177u8,66u8,75u8, - 61u8,8u8,13u8,217u8,185u8,167u8,134u8,220u8,184u8,59u8,154u8,55u8,239u8,173u8,26u8,23u8,84u8,121u8,11u8,194u8, - 179u8,245u8,13u8,40u8,187u8,13u8,148u8,39u8,121u8,231u8,34u8,60u8,177u8,219u8,171u8,255u8,16u8,3u8,163u8,79u8, - 83u8,186u8,31u8,46u8,192u8,131u8,106u8,179u8,44u8,106u8,132u8,160u8,238u8,177u8,87u8,96u8,64u8,103u8,90u8,62u8, - 1u8,203u8,174u8,131u8,80u8,225u8,253u8,230u8,66u8,107u8,226u8,87u8,226u8,152u8,90u8,147u8,142u8,156u8,192u8,112u8, - 237u8,162u8,154u8,134u8,165u8,33u8,153u8,118u8,249u8,162u8,91u8,127u8,189u8,143u8,199u8,89u8,117u8,91u8,231u8,23u8, - 116u8,58u8,87u8,152u8,170u8,233u8,184u8,142u8,105u8,197u8,1u8,197u8,111u8,207u8,166u8,206u8,103u8,174u8,29u8,96u8, - 174u8,237u8,57u8,204u8,165u8,157u8,106u8,66u8,22u8,147u8,42u8,239u8,135u8,45u8,254u8,22u8,108u8,177u8,200u8,187u8, - 172u8,44u8,28u8,250u8,38u8,66u8,143u8,191u8,131u8,251u8,75u8,25u8,67u8,117u8,113u8,3u8,111u8,61u8,47u8,11u8, - 51u8,245u8,194u8,247u8,128u8,229u8,235u8,139u8,113u8,248u8,87u8,231u8,208u8,37u8,188u8,55u8,90u8,111u8,253u8,222u8, - 1u8,106u8,71u8,223u8,102u8,121u8,167u8,71u8,149u8,203u8,138u8,81u8,103u8,157u8,175u8,181u8,0u8,224u8,207u8,43u8, - 220u8,198u8,165u8,34u8,211u8,205u8,111u8,65u8,52u8,151u8,114u8,199u8,158u8,46u8,224u8,142u8,145u8,252u8,202u8,114u8, - 41u8,248u8,178u8,116u8,88u8,127u8,105u8,177u8,213u8,93u8,180u8,185u8,66u8,87u8,45u8,186u8,95u8,75u8,8u8,87u8, - 35u8,131u8,229u8,229u8,6u8,13u8,75u8,246u8,190u8,21u8,54u8,242u8,107u8,115u8,217u8,253u8,241u8,67u8,119u8,175u8, - 82u8,119u8,27u8,131u8,5u8,138u8,105u8,164u8,211u8,108u8,35u8,103u8,5u8,157u8,182u8,93u8,159u8,181u8,83u8,44u8, - 135u8,58u8,236u8,233u8,182u8,95u8,121u8,139u8,247u8,213u8,146u8,244u8,242u8,198u8,182u8,165u8,167u8,151u8,49u8,41u8, - 222u8,119u8,203u8,249u8,239u8,150u8,58u8,32u8,132u8,109u8,124u8,123u8,194u8,163u8,196u8,198u8,115u8,9u8,171u8,21u8, - 220u8,54u8,61u8,183u8,115u8,252u8,236u8,187u8,187u8,85u8,183u8,79u8,30u8,199u8,246u8,206u8,197u8,232u8,5u8,60u8, - 238u8,249u8,224u8,116u8,30u8,38u8,141u8,202u8,54u8,4u8,162u8,6u8,169u8,101u8,187u8,138u8,79u8,151u8,223u8,240u8, - 107u8,100u8,140u8,77u8,85u8,10u8,13u8,190u8,41u8,227u8,139u8,78u8,149u8,242u8,167u8,151u8,124u8,92u8,35u8,91u8, - 186u8,235u8,117u8,174u8,244u8,144u8,254u8,140u8,146u8,178u8,247u8,188u8,230u8,122u8,130u8,187u8,119u8,111u8,174u8,121u8, - 218u8,55u8,231u8,169u8,217u8,154u8,46u8,239u8,21u8,233u8,89u8,213u8,16u8,237u8,171u8,191u8,175u8,194u8,75u8,54u8, - 14u8,244u8,177u8,42u8,83u8,237u8,92u8,213u8,97u8,141u8,91u8,43u8,247u8,104u8,229u8,11u8,201u8,189u8,181u8,222u8, - 230u8,64u8,105u8,94u8,173u8,43u8,49u8,106u8,223u8,127u8,157u8,90u8,243u8,50u8,232u8,124u8,223u8,114u8,27u8,124u8, - 203u8,189u8,133u8,179u8,49u8,170u8,47u8,245u8,7u8,143u8,251u8,121u8,252u8,110u8,156u8,55u8,47u8,189u8,93u8,46u8, - 185u8,64u8,126u8,251u8,251u8,229u8,78u8,121u8,118u8,228u8,7u8,115u8,174u8,94u8,1u8,223u8,141u8,177u8,151u8,83u8, - 219u8,15u8,158u8,97u8,43u8,57u8,238u8,58u8,30u8,8u8,197u8,136u8,247u8,68u8,253u8,224u8,190u8,135u8,193u8,125u8, - 158u8,59u8,187u8,190u8,0u8,215u8,126u8,93u8,216u8,254u8,246u8,166u8,162u8,186u8,15u8,153u8,242u8,209u8,250u8,55u8, - 173u8,205u8,57u8,220u8,185u8,28u8,83u8,215u8,92u8,180u8,242u8,87u8,181u8,116u8,119u8,22u8,107u8,96u8,115u8,78u8, - 35u8,62u8,84u8,218u8,137u8,91u8,139u8,86u8,75u8,184u8,31u8,212u8,89u8,216u8,166u8,213u8,245u8,6u8,180u8,88u8, - 138u8,7u8,161u8,132u8,189u8,219u8,216u8,92u8,202u8,69u8,219u8,52u8,92u8,52u8,251u8,139u8,23u8,187u8,246u8,125u8, - 61u8,77u8,43u8,190u8,23u8,223u8,1u8,106u8,228u8,57u8,226u8,113u8,222u8,112u8,218u8,7u8,4u8,144u8,95u8,163u8, - 155u8,160u8,70u8,239u8,172u8,17u8,16u8,75u8,99u8,120u8,238u8,174u8,27u8,158u8,118u8,137u8,218u8,236u8,252u8,87u8, - 220u8,178u8,254u8,254u8,125u8,238u8,48u8,40u8,133u8,213u8,186u8,196u8,192u8,40u8,227u8,56u8,183u8,24u8,220u8,245u8, - 114u8,60u8,60u8,171u8,207u8,135u8,233u8,183u8,84u8,232u8,58u8,190u8,93u8,209u8,15u8,180u8,232u8,77u8,119u8,86u8, - 74u8,111u8,238u8,21u8,8u8,224u8,226u8,104u8,40u8,159u8,251u8,229u8,6u8,219u8,115u8,223u8,214u8,39u8,127u8,190u8, - 189u8,94u8,217u8,224u8,84u8,243u8,101u8,33u8,34u8,101u8,171u8,21u8,12u8,151u8,230u8,234u8,249u8,122u8,98u8,195u8, - 233u8,41u8,114u8,244u8,68u8,9u8,95u8,133u8,172u8,200u8,47u8,169u8,253u8,33u8,41u8,43u8,151u8,148u8,185u8,188u8, - 37u8,111u8,47u8,90u8,134u8,31u8,59u8,203u8,176u8,122u8,231u8,203u8,241u8,30u8,218u8,168u8,87u8,245u8,188u8,71u8, - 141u8,211u8,126u8,174u8,147u8,55u8,208u8,86u8,24u8,165u8,101u8,238u8,157u8,173u8,252u8,70u8,113u8,47u8,155u8,125u8, - 105u8,182u8,149u8,254u8,216u8,223u8,73u8,187u8,187u8,151u8,101u8,186u8,26u8,254u8,231u8,239u8,93u8,197u8,151u8,81u8, - 168u8,121u8,185u8,178u8,30u8,72u8,218u8,12u8,116u8,111u8,71u8,71u8,172u8,245u8,253u8,8u8,16u8,94u8,136u8,159u8, - 228u8,160u8,142u8,216u8,117u8,36u8,239u8,55u8,19u8,236u8,25u8,105u8,247u8,129u8,170u8,111u8,114u8,176u8,239u8,140u8, - 126u8,176u8,2u8,184u8,18u8,121u8,90u8,82u8,156u8,116u8,172u8,94u8,56u8,151u8,79u8,211u8,173u8,209u8,38u8,159u8, - 229u8,26u8,85u8,156u8,10u8,233u8,130u8,124u8,111u8,220u8,148u8,125u8,21u8,169u8,239u8,126u8,3u8,198u8,10u8,229u8, - 151u8,69u8,126u8,83u8,148u8,186u8,15u8,161u8,150u8,213u8,53u8,21u8,222u8,164u8,149u8,35u8,144u8,213u8,87u8,247u8, - 203u8,230u8,165u8,40u8,234u8,119u8,205u8,216u8,47u8,117u8,235u8,190u8,109u8,221u8,202u8,42u8,216u8,111u8,26u8,23u8, - 234u8,57u8,51u8,251u8,191u8,203u8,90u8,64u8,219u8,208u8,191u8,205u8,124u8,105u8,197u8,232u8,239u8,212u8,81u8,9u8, - 213u8,226u8,58u8,238u8,3u8,9u8,201u8,162u8,5u8,116u8,119u8,14u8,248u8,29u8,186u8,214u8,172u8,200u8,154u8,168u8, - 55u8,106u8,210u8,198u8,94u8,37u8,57u8,47u8,111u8,108u8,78u8,252u8,69u8,50u8,199u8,154u8,164u8,122u8,90u8,116u8, - 232u8,146u8,40u8,62u8,136u8,174u8,217u8,70u8,4u8,73u8,147u8,143u8,151u8,179u8,32u8,130u8,218u8,190u8,238u8,7u8, - 207u8,197u8,69u8,154u8,120u8,81u8,98u8,223u8,228u8,113u8,223u8,169u8,33u8,47u8,206u8,150u8,92u8,195u8,219u8,103u8, - 161u8,109u8,250u8,97u8,230u8,132u8,106u8,249u8,85u8,126u8,133u8,58u8,181u8,10u8,35u8,93u8,33u8,21u8,29u8,39u8, - 250u8,77u8,100u8,118u8,242u8,102u8,249u8,94u8,179u8,69u8,88u8,122u8,237u8,161u8,242u8,244u8,90u8,61u8,83u8,175u8, - 162u8,27u8,194u8,220u8,197u8,93u8,218u8,134u8,45u8,60u8,172u8,176u8,115u8,216u8,233u8,116u8,114u8,84u8,172u8,133u8, - 70u8,251u8,227u8,90u8,111u8,20u8,255u8,73u8,14u8,196u8,239u8,143u8,171u8,31u8,110u8,227u8,137u8,218u8,148u8,209u8, - 64u8,59u8,163u8,188u8,70u8,159u8,190u8,23u8,94u8,127u8,149u8,87u8,96u8,240u8,2u8,31u8,250u8,218u8,186u8,120u8, - 116u8,85u8,136u8,203u8,47u8,218u8,22u8,125u8,9u8,66u8,8u8,76u8,197u8,126u8,73u8,70u8,60u8,95u8,105u8,95u8, - 9u8,140u8,79u8,225u8,56u8,150u8,146u8,58u8,151u8,214u8,220u8,225u8,181u8,61u8,132u8,38u8,108u8,234u8,204u8,240u8, - 186u8,63u8,192u8,82u8,55u8,22u8,150u8,145u8,214u8,44u8,233u8,95u8,225u8,69u8,90u8,131u8,22u8,93u8,129u8,111u8, - 124u8,193u8,124u8,98u8,124u8,63u8,130u8,24u8,51u8,136u8,80u8,26u8,115u8,79u8,64u8,182u8,24u8,99u8,62u8,95u8, - 61u8,95u8,138u8,186u8,152u8,142u8,9u8,127u8,3u8,251u8,124u8,55u8,165u8,81u8,71u8,62u8,85u8,95u8,251u8,203u8, - 116u8,254u8,237u8,79u8,255u8,15u8,161u8,79u8,21u8,244u8,129u8,160u8,0u8,0u8,0u8,0u8,10u8,97u8,112u8,116u8, - 111u8,115u8,95u8,99u8,111u8,105u8,110u8,145u8,15u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,189u8, - 88u8,109u8,111u8,219u8,54u8,16u8,254u8,158u8,95u8,193u8,118u8,64u8,106u8,119u8,158u8,221u8,172u8,233u8,203u8,228u8, - 164u8,88u8,154u8,100u8,67u8,128u8,54u8,41u8,214u8,236u8,195u8,48u8,12u8,2u8,45u8,209u8,50u8,17u8,73u8,244u8, - 72u8,42u8,142u8,27u8,228u8,191u8,239u8,142u8,212u8,11u8,69u8,75u8,118u8,183u8,181u8,53u8,138u8,198u8,150u8,200u8, - 187u8,227u8,221u8,115u8,207u8,29u8,111u8,50u8,153u8,144u8,235u8,5u8,87u8,36u8,19u8,113u8,145u8,50u8,18u8,179u8, - 57u8,207u8,153u8,34u8,148u8,100u8,60u8,231u8,25u8,77u8,9u8,205u8,99u8,146u8,176u8,156u8,73u8,30u8,145u8,83u8, - 193u8,115u8,243u8,224u8,45u8,77u8,105u8,30u8,177u8,241u8,222u8,4u8,246u8,195u8,86u8,62u8,231u8,44u8,38u8,115u8, - 41u8,50u8,178u8,208u8,122u8,169u8,130u8,201u8,36u8,225u8,122u8,81u8,204u8,198u8,145u8,200u8,38u8,153u8,184u8,101u8, - 63u8,192u8,134u8,164u8,160u8,9u8,51u8,191u8,38u8,90u8,50u8,248u8,70u8,121u8,62u8,169u8,159u8,199u8,34u8,42u8, - 50u8,150u8,107u8,170u8,185u8,200u8,39u8,186u8,208u8,66u8,114u8,154u8,238u8,149u8,134u8,209u8,165u8,22u8,42u8,156u8, - 75u8,154u8,177u8,149u8,144u8,55u8,65u8,96u8,31u8,68u8,104u8,209u8,253u8,30u8,129u8,79u8,161u8,24u8,81u8,58u8, - 14u8,2u8,165u8,37u8,207u8,147u8,105u8,251u8,33u8,147u8,82u8,72u8,239u8,153u8,226u8,9u8,156u8,203u8,123u8,120u8, - 203u8,34u8,189u8,177u8,82u8,44u8,209u8,168u8,32u8,184u8,255u8,200u8,210u8,249u8,136u8,92u8,153u8,159u8,15u8,211u8, - 189u8,122u8,213u8,134u8,121u8,104u8,88u8,189u8,254u8,109u8,33u8,243u8,83u8,186u8,164u8,51u8,158u8,114u8,189u8,30u8, - 145u8,247u8,60u8,215u8,205u8,239u8,135u8,105u8,191u8,24u8,181u8,86u8,154u8,101u8,33u8,141u8,99u8,201u8,148u8,98u8, - 170u8,84u8,57u8,151u8,156u8,65u8,24u8,54u8,150u8,99u8,160u8,20u8,175u8,86u8,97u8,112u8,78u8,162u8,72u8,20u8, - 185u8,38u8,177u8,128u8,168u8,230u8,66u8,147u8,5u8,189u8,101u8,24u8,92u8,77u8,162u8,218u8,2u8,179u8,58u8,18u8, - 185u8,210u8,228u8,252u8,242u8,42u8,60u8,61u8,249u8,112u8,242u8,246u8,226u8,221u8,197u8,245u8,197u8,249u8,199u8,128u8, - 20u8,47u8,15u8,201u8,49u8,57u8,152u8,214u8,34u8,223u8,183u8,55u8,131u8,68u8,192u8,75u8,42u8,25u8,141u8,215u8, - 100u8,198u8,88u8,14u8,32u8,74u8,89u8,66u8,53u8,32u8,66u8,11u8,162u8,17u8,93u8,106u8,201u8,34u8,139u8,145u8, - 242u8,36u8,174u8,198u8,147u8,119u8,191u8,157u8,159u8,156u8,253u8,17u8,158u8,157u8,191u8,59u8,255u8,245u8,228u8,250u8, - 252u8,172u8,210u8,249u8,99u8,163u8,243u8,148u8,230u8,104u8,60u8,128u8,51u8,174u8,196u8,67u8,4u8,136u8,152u8,251u8, - 71u8,169u8,85u8,82u8,123u8,112u8,87u8,81u8,169u8,224u8,226u8,234u8,50u8,188u8,188u8,186u8,14u8,127u8,185u8,250u8, - 253u8,178u8,214u8,245u8,188u8,244u8,25u8,128u8,167u8,136u8,52u8,57u8,65u8,207u8,26u8,184u8,227u8,233u8,110u8,216u8, - 154u8,220u8,63u8,180u8,22u8,148u8,33u8,252u8,8u8,88u8,97u8,205u8,26u8,179u8,2u8,63u8,104u8,85u8,8u8,86u8, - 5u8,94u8,172u8,143u8,106u8,193u8,111u8,70u8,102u8,241u8,67u8,19u8,170u8,179u8,230u8,92u8,90u8,220u8,128u8,31u8, - 35u8,240u8,41u8,122u8,113u8,182u8,174u8,206u8,44u8,164u8,73u8,191u8,136u8,230u8,224u8,105u8,18u8,165u8,148u8,103u8, - 246u8,189u8,94u8,176u8,218u8,237u8,0u8,35u8,229u8,169u8,29u8,187u8,166u8,159u8,85u8,225u8,105u8,175u8,49u8,167u8, - 80u8,230u8,60u8,205u8,57u8,180u8,8u8,90u8,33u8,115u8,204u8,189u8,6u8,149u8,224u8,88u8,13u8,73u8,204u8,164u8, - 221u8,168u8,140u8,29u8,81u8,33u8,37u8,228u8,49u8,89u8,2u8,68u8,33u8,17u8,157u8,120u8,169u8,46u8,59u8,240u8, - 121u8,135u8,7u8,121u8,14u8,98u8,3u8,98u8,211u8,241u8,168u8,199u8,230u8,77u8,39u8,2u8,80u8,136u8,200u8,211u8, - 53u8,248u8,40u8,77u8,193u8,55u8,113u8,129u8,100u8,64u8,202u8,196u8,64u8,116u8,0u8,175u8,105u8,32u8,22u8,254u8, - 137u8,25u8,107u8,77u8,64u8,8u8,166u8,171u8,53u8,110u8,89u8,204u8,82u8,30u8,13u8,108u8,134u8,13u8,201u8,188u8, - 200u8,157u8,29u8,3u8,63u8,227u8,200u8,190u8,37u8,145u8,97u8,64u8,6u8,237u8,44u8,119u8,35u8,221u8,15u8,130u8, - 161u8,115u8,98u8,63u8,215u8,129u8,227u8,224u8,143u8,212u8,161u8,167u8,212u8,55u8,98u8,88u8,66u8,23u8,63u8,41u8, - 211u8,100u8,48u8,3u8,67u8,16u8,124u8,35u8,224u8,9u8,198u8,62u8,49u8,251u8,189u8,130u8,228u8,16u8,208u8,110u8, - 217u8,169u8,57u8,87u8,184u8,2u8,182u8,14u8,151u8,84u8,162u8,211u8,224u8,1u8,157u8,165u8,44u8,84u8,197u8,114u8, - 153u8,186u8,198u8,14u8,106u8,53u8,248u8,241u8,172u8,24u8,181u8,94u8,90u8,14u8,14u8,130u8,66u8,207u8,95u8,15u8, - 102u8,143u8,173u8,147u8,81u8,200u8,227u8,225u8,246u8,133u8,31u8,174u8,253u8,21u8,175u8,71u8,100u8,242u8,20u8,64u8, - 20u8,97u8,57u8,82u8,228u8,233u8,164u8,245u8,22u8,160u8,196u8,204u8,130u8,76u8,192u8,105u8,132u8,44u8,173u8,118u8, - 151u8,185u8,254u8,65u8,70u8,52u8,182u8,212u8,118u8,147u8,156u8,177u8,88u8,213u8,52u8,130u8,8u8,177u8,223u8,193u8, - 88u8,23u8,47u8,228u8,22u8,60u8,21u8,99u8,10u8,170u8,177u8,45u8,153u8,43u8,158u8,166u8,152u8,134u8,146u8,221u8, - 66u8,186u8,198u8,0u8,187u8,200u8,66u8,170u8,89u8,233u8,234u8,53u8,196u8,107u8,248u8,177u8,241u8,123u8,60u8,110u8, - 24u8,3u8,202u8,98u8,168u8,133u8,31u8,220u8,81u8,155u8,105u8,238u8,235u8,48u8,146u8,7u8,247u8,92u8,54u8,160u8, - 49u8,3u8,119u8,138u8,117u8,216u8,132u8,125u8,208u8,124u8,29u8,78u8,235u8,213u8,14u8,66u8,106u8,84u8,184u8,137u8, - 100u8,115u8,192u8,96u8,31u8,18u8,51u8,172u8,214u8,148u8,240u8,29u8,148u8,212u8,234u8,130u8,127u8,38u8,68u8,234u8, - 64u8,153u8,221u8,113u8,165u8,213u8,145u8,107u8,250u8,155u8,129u8,93u8,12u8,168u8,182u8,0u8,15u8,197u8,188u8,146u8, - 52u8,28u8,250u8,105u8,124u8,181u8,53u8,133u8,203u8,115u8,26u8,103u8,83u8,47u8,156u8,165u8,200u8,39u8,106u8,163u8, - 50u8,152u8,248u8,128u8,200u8,142u8,136u8,214u8,122u8,145u8,93u8,75u8,9u8,170u8,59u8,98u8,158u8,57u8,189u8,164u8, - 81u8,197u8,162u8,114u8,94u8,63u8,117u8,128u8,194u8,191u8,11u8,142u8,236u8,217u8,14u8,245u8,151u8,32u8,6u8,151u8, - 23u8,122u8,129u8,4u8,132u8,96u8,208u8,135u8,253u8,155u8,23u8,179u8,159u8,251u8,69u8,182u8,33u8,87u8,31u8,179u8, - 6u8,212u8,180u8,151u8,154u8,177u8,116u8,117u8,134u8,118u8,14u8,245u8,77u8,131u8,64u8,19u8,228u8,68u8,82u8,136u8, - 95u8,71u8,121u8,223u8,136u8,56u8,86u8,68u8,60u8,19u8,120u8,71u8,20u8,50u8,98u8,78u8,56u8,203u8,80u8,246u8, - 70u8,9u8,10u8,216u8,156u8,39u8,133u8,100u8,97u8,181u8,52u8,4u8,35u8,66u8,52u8,162u8,97u8,187u8,190u8,200u8, - 141u8,28u8,95u8,128u8,132u8,90u8,125u8,199u8,130u8,207u8,238u8,7u8,190u8,116u8,65u8,168u8,250u8,53u8,83u8,152u8, - 93u8,39u8,85u8,190u8,113u8,90u8,29u8,116u8,127u8,130u8,77u8,0u8,144u8,158u8,54u8,77u8,6u8,187u8,99u8,81u8, - 161u8,89u8,105u8,9u8,144u8,45u8,205u8,21u8,141u8,156u8,82u8,222u8,0u8,65u8,178u8,4u8,114u8,158u8,73u8,183u8, - 94u8,180u8,221u8,226u8,193u8,209u8,18u8,108u8,85u8,139u8,208u8,65u8,189u8,165u8,230u8,224u8,245u8,225u8,225u8,203u8, - 87u8,135u8,135u8,207u8,94u8,61u8,127u8,245u8,236u8,167u8,23u8,47u8,14u8,94u8,30u8,188u8,104u8,23u8,137u8,253u8, - 202u8,193u8,35u8,151u8,246u8,125u8,168u8,46u8,133u8,226u8,45u8,45u8,29u8,148u8,228u8,217u8,60u8,178u8,118u8,186u8, - 46u8,173u8,152u8,186u8,189u8,114u8,59u8,81u8,239u8,218u8,235u8,246u8,66u8,247u8,237u8,222u8,7u8,238u8,46u8,217u8, - 18u8,88u8,119u8,88u8,11u8,234u8,226u8,73u8,172u8,216u8,176u8,175u8,76u8,31u8,76u8,9u8,252u8,150u8,51u8,248u8, - 177u8,90u8,48u8,201u8,54u8,1u8,80u8,183u8,200u8,37u8,91u8,143u8,155u8,68u8,53u8,125u8,39u8,20u8,198u8,85u8, - 25u8,36u8,106u8,186u8,110u8,227u8,61u8,148u8,147u8,129u8,34u8,100u8,97u8,165u8,13u8,54u8,159u8,212u8,146u8,220u8, - 52u8,35u8,208u8,2u8,202u8,181u8,73u8,50u8,244u8,132u8,147u8,77u8,94u8,245u8,104u8,34u8,86u8,9u8,172u8,91u8, - 206u8,230u8,21u8,205u8,236u8,30u8,104u8,214u8,171u8,60u8,217u8,197u8,154u8,136u8,177u8,82u8,151u8,145u8,10u8,80u8, - 219u8,82u8,129u8,156u8,248u8,218u8,36u8,123u8,212u8,134u8,96u8,103u8,69u8,115u8,197u8,123u8,93u8,139u8,185u8,111u8, - 6u8,1u8,92u8,90u8,128u8,80u8,138u8,60u8,30u8,248u8,23u8,171u8,225u8,168u8,179u8,65u8,65u8,171u8,107u8,228u8, - 28u8,147u8,253u8,153u8,0u8,65u8,171u8,48u8,73u8,197u8,140u8,166u8,219u8,212u8,143u8,171u8,93u8,29u8,89u8,102u8, - 216u8,25u8,232u8,182u8,47u8,217u8,172u8,119u8,157u8,102u8,224u8,51u8,82u8,167u8,10u8,214u8,168u8,165u8,226u8,219u8, - 34u8,212u8,189u8,104u8,226u8,133u8,201u8,212u8,15u8,236u8,9u8,108u8,124u8,145u8,200u8,204u8,207u8,82u8,10u8,252u8, - 159u8,198u8,246u8,214u8,228u8,209u8,48u8,73u8,65u8,138u8,236u8,129u8,111u8,165u8,165u8,191u8,15u8,42u8,129u8,236u8, - 222u8,150u8,28u8,124u8,182u8,114u8,123u8,55u8,185u8,183u8,168u8,97u8,176u8,223u8,0u8,212u8,13u8,171u8,115u8,167u8, - 66u8,148u8,100u8,133u8,38u8,45u8,164u8,132u8,240u8,228u8,200u8,81u8,12u8,149u8,220u8,35u8,182u8,177u8,33u8,153u8, - 182u8,84u8,14u8,178u8,158u8,53u8,143u8,86u8,11u8,14u8,33u8,27u8,112u8,114u8,84u8,83u8,81u8,202u8,242u8,68u8, - 47u8,6u8,142u8,250u8,161u8,91u8,177u8,42u8,65u8,240u8,26u8,71u8,57u8,32u8,174u8,218u8,104u8,173u8,115u8,55u8, - 142u8,8u8,119u8,14u8,229u8,38u8,94u8,185u8,123u8,12u8,28u8,243u8,232u8,24u8,156u8,58u8,170u8,178u8,137u8,231u8, - 166u8,101u8,11u8,169u8,76u8,204u8,168u8,104u8,176u8,57u8,59u8,24u8,122u8,50u8,241u8,72u8,156u8,124u8,95u8,205u8, - 47u8,12u8,56u8,155u8,175u8,149u8,117u8,203u8,66u8,45u8,194u8,25u8,141u8,110u8,218u8,6u8,246u8,221u8,148u8,239u8, - 177u8,13u8,249u8,102u8,108u8,108u8,240u8,234u8,94u8,240u8,227u8,141u8,174u8,200u8,18u8,116u8,211u8,20u8,123u8,137u8, - 209u8,3u8,108u8,147u8,9u8,187u8,187u8,251u8,78u8,36u8,143u8,182u8,209u8,110u8,70u8,215u8,51u8,22u8,194u8,172u8, - 134u8,221u8,129u8,251u8,113u8,104u8,19u8,54u8,126u8,221u8,122u8,15u8,152u8,110u8,176u8,112u8,53u8,122u8,227u8,42u8, - 84u8,34u8,131u8,132u8,112u8,132u8,67u8,117u8,238u8,28u8,234u8,120u8,201u8,194u8,99u8,52u8,227u8,105u8,37u8,169u8, - 132u8,98u8,75u8,208u8,183u8,200u8,174u8,109u8,104u8,10u8,72u8,104u8,250u8,240u8,10u8,144u8,106u8,69u8,151u8,32u8, - 14u8,219u8,5u8,47u8,103u8,226u8,59u8,191u8,189u8,163u8,55u8,64u8,113u8,128u8,166u8,229u8,218u8,157u8,135u8,217u8, - 193u8,45u8,135u8,27u8,12u8,22u8,109u8,75u8,132u8,206u8,24u8,206u8,45u8,216u8,29u8,117u8,103u8,91u8,217u8,217u8, - 56u8,235u8,102u8,229u8,169u8,47u8,179u8,86u8,205u8,238u8,222u8,168u8,76u8,34u8,68u8,165u8,15u8,151u8,86u8,71u8, - 0u8,87u8,77u8,59u8,124u8,61u8,130u8,78u8,224u8,205u8,46u8,142u8,237u8,136u8,101u8,251u8,96u8,255u8,151u8,33u8, - 241u8,17u8,80u8,162u8,19u8,184u8,14u8,130u8,244u8,36u8,148u8,89u8,81u8,161u8,49u8,23u8,57u8,27u8,12u8,59u8, - 73u8,23u8,100u8,125u8,57u8,114u8,229u8,115u8,226u8,18u8,235u8,241u8,177u8,113u8,169u8,47u8,223u8,78u8,222u8,218u8, - 22u8,154u8,180u8,243u8,197u8,225u8,103u8,6u8,85u8,248u8,166u8,245u8,244u8,225u8,223u8,144u8,175u8,209u8,227u8,70u8, - 255u8,187u8,63u8,145u8,37u8,67u8,188u8,35u8,254u8,213u8,63u8,18u8,167u8,73u8,34u8,237u8,44u8,52u8,156u8,83u8, - 60u8,255u8,122u8,218u8,179u8,221u8,25u8,99u8,56u8,163u8,174u8,250u8,126u8,247u8,53u8,103u8,121u8,155u8,70u8,182u8, - 198u8,109u8,155u8,175u8,123u8,205u8,26u8,186u8,14u8,235u8,155u8,67u8,122u8,243u8,19u8,59u8,159u8,130u8,127u8,48u8, - 209u8,211u8,60u8,42u8,82u8,42u8,161u8,48u8,129u8,55u8,231u8,69u8,138u8,64u8,48u8,77u8,209u8,134u8,9u8,184u8, - 161u8,154u8,227u8,187u8,3u8,143u8,91u8,78u8,129u8,76u8,4u8,108u8,146u8,166u8,184u8,129u8,84u8,189u8,24u8,255u8, - 23u8,151u8,155u8,49u8,163u8,40u8,116u8,199u8,249u8,191u8,106u8,52u8,118u8,59u8,238u8,97u8,239u8,31u8,219u8,48u8, - 231u8,110u8,10u8,27u8,0u8,0u8,0u8,0u8,13u8,97u8,112u8,116u8,111u8,115u8,95u8,97u8,99u8,99u8,111u8,117u8, - 110u8,116u8,230u8,20u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,91u8,109u8,115u8,219u8,54u8, - 18u8,254u8,222u8,95u8,129u8,164u8,51u8,41u8,117u8,199u8,58u8,146u8,154u8,220u8,220u8,200u8,142u8,231u8,92u8,71u8, - 73u8,60u8,119u8,113u8,50u8,182u8,242u8,41u8,227u8,97u8,41u8,18u8,178u8,56u8,161u8,8u8,31u8,9u8,218u8,113u8, - 211u8,252u8,247u8,238u8,226u8,133u8,4u8,65u8,80u8,162u8,226u8,216u8,77u8,91u8,123u8,58u8,141u8,76u8,1u8,251u8, - 190u8,207u8,46u8,22u8,244u8,138u8,197u8,101u8,74u8,73u8,120u8,193u8,89u8,17u8,44u8,242u8,112u8,69u8,175u8,88u8, - 254u8,97u8,50u8,145u8,15u8,194u8,40u8,98u8,101u8,198u8,201u8,167u8,239u8,8u8,252u8,148u8,133u8,107u8,161u8,92u8, - 50u8,153u8,124u8,58u8,165u8,233u8,194u8,39u8,25u8,189u8,10u8,232u8,37u8,205u8,120u8,176u8,12u8,179u8,56u8,165u8, - 159u8,119u8,215u8,236u8,21u8,15u8,34u8,150u8,100u8,147u8,201u8,1u8,126u8,62u8,132u8,143u8,107u8,214u8,203u8,149u8, - 138u8,17u8,174u8,93u8,71u8,60u8,202u8,105u8,200u8,105u8,80u8,36u8,231u8,25u8,205u8,173u8,95u8,215u8,108u8,19u8, - 178u8,3u8,147u8,41u8,254u8,251u8,74u8,168u8,224u8,19u8,186u8,74u8,184u8,212u8,202u8,224u8,88u8,240u8,24u8,150u8, - 231u8,57u8,203u8,173u8,103u8,54u8,15u8,241u8,240u8,146u8,70u8,28u8,87u8,138u8,167u8,139u8,60u8,161u8,89u8,220u8, - 102u8,126u8,78u8,51u8,90u8,36u8,197u8,238u8,218u8,69u8,57u8,45u8,88u8,153u8,71u8,84u8,123u8,71u8,209u8,124u8, - 252u8,248u8,49u8,57u8,80u8,254u8,138u8,25u8,45u8,72u8,198u8,56u8,161u8,31u8,147u8,130u8,239u8,136u8,239u8,35u8, - 150u8,21u8,156u8,76u8,15u8,14u8,15u8,223u8,188u8,59u8,158u8,5u8,199u8,111u8,102u8,193u8,11u8,248u8,244u8,124u8, - 66u8,202u8,127u8,61u8,33u8,207u8,200u8,104u8,183u8,69u8,38u8,145u8,68u8,114u8,122u8,14u8,84u8,104u8,78u8,99u8, - 194u8,25u8,252u8,22u8,209u8,228u8,146u8,146u8,131u8,183u8,179u8,78u8,194u8,39u8,211u8,151u8,71u8,167u8,179u8,233u8, - 201u8,244u8,57u8,240u8,56u8,9u8,96u8,169u8,230u8,50u8,110u8,115u8,97u8,23u8,28u8,40u8,179u8,18u8,62u8,45u8, - 20u8,245u8,36u8,59u8,39u8,232u8,236u8,130u8,240u8,101u8,200u8,225u8,127u8,244u8,154u8,196u8,73u8,220u8,144u8,198u8, - 144u8,197u8,41u8,199u8,243u8,55u8,211u8,83u8,33u8,12u8,60u8,152u8,190u8,133u8,223u8,143u8,78u8,166u8,135u8,179u8, - 224u8,240u8,205u8,209u8,113u8,48u8,59u8,57u8,56u8,62u8,125u8,49u8,61u8,57u8,213u8,98u8,253u8,180u8,65u8,172u8, - 56u8,1u8,86u8,60u8,189u8,54u8,228u8,59u8,126u8,49u8,3u8,17u8,62u8,208u8,172u8,216u8,134u8,253u8,236u8,205u8, - 127u8,167u8,14u8,254u8,79u8,106u8,254u8,179u8,37u8,37u8,41u8,205u8,206u8,249u8,178u8,64u8,206u8,160u8,59u8,50u8, - 77u8,46u8,32u8,24u8,120u8,65u8,66u8,140u8,136u8,21u8,10u8,88u8,144u8,20u8,236u8,80u8,128u8,175u8,179u8,31u8, - 56u8,89u8,133u8,60u8,90u8,54u8,4u8,121u8,125u8,116u8,250u8,250u8,96u8,118u8,248u8,234u8,232u8,248u8,37u8,248u8, - 227u8,240u8,232u8,237u8,209u8,244u8,120u8,118u8,26u8,28u8,28u8,63u8,15u8,14u8,94u8,163u8,136u8,167u8,193u8,255u8, - 166u8,199u8,47u8,103u8,175u8,180u8,12u8,79u8,141u8,64u8,58u8,100u8,217u8,34u8,57u8,47u8,243u8,144u8,39u8,44u8, - 35u8,11u8,150u8,147u8,171u8,37u8,5u8,81u8,114u8,16u8,128u8,104u8,84u8,136u8,224u8,179u8,142u8,6u8,105u8,32u8, - 194u8,243u8,48u8,43u8,22u8,52u8,23u8,162u8,219u8,30u8,92u8,134u8,176u8,176u8,25u8,80u8,59u8,154u8,99u8,197u8, - 249u8,103u8,240u8,52u8,93u8,132u8,101u8,202u8,125u8,216u8,5u8,17u8,8u8,255u8,209u8,44u8,156u8,167u8,176u8,152u8, - 188u8,43u8,144u8,52u8,242u8,5u8,239u8,252u8,136u8,190u8,153u8,99u8,96u8,20u8,240u8,53u8,186u8,4u8,24u8,133u8, - 217u8,53u8,225u8,201u8,74u8,133u8,68u8,193u8,243u8,18u8,164u8,122u8,46u8,132u8,155u8,41u8,217u8,164u8,110u8,32u8, - 77u8,65u8,62u8,128u8,84u8,18u8,219u8,240u8,39u8,76u8,83u8,118u8,21u8,132u8,249u8,60u8,1u8,53u8,242u8,107u8, - 1u8,76u8,65u8,165u8,209u8,132u8,204u8,25u8,75u8,253u8,106u8,117u8,121u8,17u8,35u8,160u8,52u8,22u8,73u8,140u8, - 128u8,165u8,6u8,122u8,236u8,73u8,238u8,8u8,87u8,77u8,9u8,222u8,9u8,2u8,177u8,88u8,186u8,47u8,233u8,126u8, - 174u8,61u8,32u8,30u8,11u8,224u8,193u8,48u8,4u8,235u8,103u8,134u8,233u8,127u8,40u8,180u8,193u8,149u8,141u8,21u8, - 97u8,244u8,61u8,234u8,6u8,54u8,147u8,226u8,197u8,14u8,59u8,172u8,151u8,68u8,216u8,37u8,206u8,217u8,133u8,15u8, - 187u8,88u8,78u8,13u8,251u8,32u8,180u8,75u8,27u8,73u8,230u8,110u8,219u8,212u8,58u8,124u8,173u8,159u8,58u8,54u8, - 194u8,34u8,137u8,234u8,240u8,67u8,68u8,199u8,240u8,92u8,65u8,100u8,178u8,184u8,216u8,249u8,234u8,124u8,5u8,193u8, - 139u8,18u8,130u8,43u8,130u8,24u8,228u8,249u8,53u8,89u8,148u8,25u8,81u8,149u8,68u8,137u8,225u8,133u8,37u8,95u8, - 6u8,16u8,72u8,19u8,18u8,198u8,49u8,96u8,115u8,49u8,48u8,76u8,150u8,82u8,78u8,100u8,57u8,128u8,4u8,171u8, - 74u8,101u8,7u8,133u8,193u8,110u8,181u8,79u8,86u8,58u8,157u8,39u8,123u8,85u8,113u8,220u8,247u8,30u8,73u8,114u8, - 106u8,173u8,17u8,48u8,63u8,35u8,2u8,144u8,75u8,240u8,6u8,218u8,4u8,210u8,15u8,80u8,183u8,10u8,140u8,29u8, - 183u8,42u8,115u8,220u8,83u8,185u8,209u8,147u8,117u8,101u8,66u8,20u8,15u8,223u8,0u8,158u8,9u8,145u8,5u8,108u8, - 79u8,41u8,185u8,239u8,107u8,24u8,170u8,190u8,1u8,20u8,217u8,183u8,117u8,175u8,9u8,4u8,128u8,105u8,96u8,3u8, - 185u8,118u8,50u8,145u8,8u8,231u8,61u8,170u8,23u8,24u8,218u8,135u8,5u8,228u8,57u8,127u8,224u8,85u8,15u8,240u8, - 199u8,38u8,213u8,166u8,165u8,4u8,26u8,248u8,141u8,125u8,162u8,66u8,79u8,38u8,73u8,118u8,25u8,166u8,73u8,12u8, - 233u8,125u8,94u8,174u8,128u8,138u8,215u8,31u8,32u8,13u8,130u8,3u8,133u8,145u8,90u8,191u8,4u8,84u8,26u8,214u8, - 130u8,95u8,45u8,19u8,104u8,166u8,188u8,132u8,236u8,89u8,226u8,154u8,118u8,209u8,123u8,161u8,126u8,61u8,35u8,255u8, - 208u8,74u8,204u8,25u8,200u8,121u8,101u8,26u8,196u8,39u8,137u8,97u8,19u8,189u8,75u8,42u8,233u8,218u8,169u8,212u8, - 111u8,111u8,179u8,252u8,11u8,184u8,202u8,180u8,247u8,172u8,149u8,168u8,78u8,66u8,254u8,169u8,123u8,1u8,17u8,97u8, - 173u8,64u8,3u8,204u8,0u8,160u8,64u8,17u8,49u8,134u8,34u8,145u8,130u8,160u8,75u8,133u8,65u8,34u8,238u8,24u8, - 9u8,107u8,19u8,84u8,41u8,43u8,106u8,193u8,42u8,57u8,95u8,114u8,187u8,59u8,145u8,149u8,15u8,112u8,235u8,138u8, - 149u8,105u8,172u8,114u8,172u8,89u8,252u8,42u8,42u8,139u8,36u8,47u8,160u8,58u8,128u8,177u8,33u8,224u8,195u8,180u8, - 96u8,85u8,65u8,129u8,106u8,193u8,173u8,30u8,197u8,39u8,115u8,186u8,64u8,28u8,211u8,242u8,229u8,80u8,41u8,58u8, - 210u8,161u8,59u8,17u8,56u8,171u8,210u8,91u8,155u8,78u8,148u8,77u8,211u8,175u8,201u8,130u8,120u8,15u8,170u8,28u8, - 23u8,186u8,65u8,7u8,205u8,61u8,206u8,6u8,182u8,251u8,45u8,0u8,128u8,21u8,182u8,189u8,165u8,77u8,200u8,137u8, - 234u8,245u8,180u8,238u8,178u8,248u8,205u8,169u8,162u8,0u8,133u8,33u8,1u8,236u8,43u8,235u8,146u8,138u8,101u8,16u8, - 172u8,182u8,114u8,118u8,106u8,138u8,168u8,176u8,115u8,84u8,121u8,17u8,186u8,26u8,209u8,47u8,74u8,99u8,35u8,25u8, - 85u8,246u8,65u8,161u8,12u8,72u8,20u8,5u8,20u8,195u8,157u8,166u8,154u8,18u8,159u8,146u8,234u8,132u8,16u8,212u8, - 37u8,221u8,4u8,43u8,151u8,234u8,221u8,200u8,214u8,104u8,209u8,197u8,222u8,93u8,151u8,89u8,36u8,5u8,237u8,43u8, - 147u8,130u8,35u8,190u8,55u8,162u8,100u8,85u8,186u8,69u8,37u8,237u8,133u8,147u8,114u8,233u8,158u8,168u8,163u8,215u8, - 23u8,116u8,191u8,198u8,169u8,69u8,206u8,86u8,55u8,131u8,207u8,48u8,250u8,127u8,9u8,197u8,181u8,112u8,247u8,44u8, - 247u8,224u8,250u8,205u8,130u8,107u8,43u8,40u8,48u8,22u8,110u8,27u8,106u8,67u8,18u8,149u8,208u8,165u8,173u8,136u8, - 102u8,123u8,7u8,192u8,43u8,142u8,32u8,213u8,33u8,172u8,137u8,183u8,184u8,77u8,139u8,242u8,37u8,192u8,235u8,52u8, - 98u8,47u8,24u8,238u8,155u8,55u8,49u8,189u8,96u8,5u8,28u8,230u8,5u8,35u8,15u8,189u8,35u8,225u8,4u8,113u8, - 52u8,206u8,195u8,171u8,150u8,3u8,149u8,243u8,6u8,125u8,125u8,163u8,24u8,56u8,92u8,147u8,100u8,127u8,38u8,231u8, - 160u8,91u8,26u8,198u8,50u8,44u8,211u8,112u8,131u8,248u8,114u8,34u8,40u8,215u8,75u8,250u8,59u8,228u8,70u8,85u8, - 211u8,89u8,31u8,54u8,22u8,40u8,83u8,145u8,22u8,19u8,39u8,60u8,10u8,238u8,97u8,22u8,40u8,83u8,234u8,243u8, - 79u8,243u8,128u8,136u8,212u8,252u8,214u8,54u8,133u8,144u8,23u8,52u8,95u8,37u8,5u8,22u8,157u8,32u8,198u8,168u8, - 137u8,189u8,237u8,6u8,21u8,22u8,101u8,11u8,76u8,172u8,162u8,90u8,43u8,184u8,93u8,77u8,85u8,254u8,110u8,24u8, - 72u8,57u8,184u8,81u8,72u8,141u8,16u8,145u8,230u8,170u8,172u8,44u8,253u8,231u8,97u8,112u8,56u8,207u8,67u8,218u8, - 186u8,14u8,135u8,227u8,234u8,129u8,175u8,237u8,5u8,137u8,16u8,44u8,96u8,137u8,97u8,167u8,106u8,98u8,101u8,37u8, - 99u8,183u8,52u8,224u8,254u8,218u8,237u8,64u8,46u8,15u8,96u8,152u8,182u8,73u8,56u8,151u8,46u8,142u8,242u8,217u8, - 179u8,3u8,234u8,165u8,85u8,123u8,92u8,230u8,192u8,155u8,83u8,168u8,79u8,122u8,8u8,243u8,139u8,98u8,250u8,203u8, - 151u8,207u8,96u8,232u8,199u8,11u8,176u8,90u8,34u8,103u8,90u8,142u8,249u8,94u8,7u8,82u8,23u8,148u8,55u8,103u8, - 0u8,86u8,14u8,104u8,199u8,214u8,152u8,45u8,86u8,203u8,1u8,193u8,118u8,237u8,13u8,154u8,14u8,138u8,164u8,30u8, - 224u8,42u8,135u8,5u8,108u8,161u8,153u8,12u8,154u8,57u8,47u8,253u8,181u8,231u8,162u8,172u8,252u8,224u8,106u8,20u8, - 172u8,81u8,70u8,160u8,38u8,40u8,207u8,136u8,172u8,252u8,193u8,121u8,202u8,230u8,97u8,26u8,172u8,74u8,190u8,142u8, - 114u8,51u8,29u8,209u8,91u8,75u8,150u8,243u8,31u8,163u8,36u8,143u8,74u8,137u8,194u8,225u8,37u8,131u8,1u8,166u8, - 152u8,231u8,136u8,81u8,85u8,70u8,196u8,172u8,8u8,37u8,183u8,252u8,102u8,12u8,113u8,208u8,81u8,17u8,204u8,209u8, - 207u8,43u8,116u8,54u8,21u8,118u8,139u8,190u8,179u8,126u8,140u8,133u8,189u8,157u8,88u8,97u8,27u8,67u8,182u8,129u8, - 188u8,204u8,179u8,198u8,227u8,207u8,70u8,83u8,38u8,10u8,232u8,151u8,49u8,149u8,60u8,155u8,102u8,170u8,167u8,234u8, - 109u8,180u8,125u8,4u8,6u8,239u8,226u8,181u8,102u8,242u8,214u8,198u8,223u8,94u8,51u8,175u8,79u8,107u8,103u8,91u8, - 226u8,57u8,249u8,108u8,34u8,39u8,161u8,105u8,65u8,183u8,137u8,166u8,13u8,241u8,222u8,119u8,4u8,41u8,190u8,111u8, - 43u8,185u8,118u8,22u8,105u8,223u8,200u8,244u8,27u8,72u8,86u8,73u8,230u8,219u8,225u8,240u8,215u8,112u8,33u8,254u8, - 172u8,24u8,212u8,114u8,206u8,180u8,170u8,126u8,135u8,180u8,131u8,174u8,22u8,253u8,251u8,247u8,151u8,9u8,189u8,58u8, - 171u8,32u8,250u8,68u8,164u8,15u8,36u8,114u8,73u8,49u8,65u8,111u8,17u8,165u8,43u8,150u8,13u8,180u8,94u8,59u8, - 71u8,151u8,185u8,93u8,84u8,210u8,25u8,131u8,124u8,28u8,249u8,90u8,220u8,10u8,163u8,218u8,96u8,227u8,184u8,78u8, - 131u8,86u8,223u8,184u8,185u8,85u8,170u8,202u8,132u8,46u8,195u8,178u8,64u8,244u8,174u8,15u8,15u8,214u8,131u8,189u8, - 10u8,93u8,242u8,219u8,111u8,13u8,127u8,55u8,32u8,125u8,195u8,222u8,13u8,136u8,214u8,12u8,3u8,78u8,11u8,30u8, - 176u8,44u8,189u8,62u8,179u8,46u8,22u8,197u8,213u8,31u8,30u8,36u8,130u8,121u8,164u8,174u8,245u8,156u8,171u8,229u8, - 189u8,33u8,199u8,46u8,124u8,50u8,41u8,249u8,226u8,223u8,187u8,155u8,40u8,187u8,174u8,96u8,155u8,13u8,178u8,104u8, - 120u8,112u8,255u8,110u8,135u8,152u8,234u8,86u8,224u8,69u8,248u8,129u8,98u8,114u8,145u8,79u8,13u8,125u8,60u8,56u8, - 209u8,195u8,224u8,233u8,25u8,249u8,207u8,240u8,99u8,56u8,26u8,225u8,92u8,37u8,194u8,19u8,131u8,120u8,48u8,26u8, - 156u8,217u8,78u8,23u8,196u8,171u8,9u8,154u8,216u8,108u8,116u8,1u8,184u8,181u8,250u8,213u8,158u8,18u8,207u8,217u8, - 28u8,200u8,106u8,51u8,193u8,108u8,135u8,5u8,42u8,46u8,188u8,143u8,15u8,135u8,55u8,249u8,153u8,15u8,231u8,15u8, - 141u8,244u8,69u8,102u8,81u8,152u8,67u8,160u8,221u8,6u8,187u8,40u8,124u8,58u8,28u8,61u8,180u8,167u8,24u8,222u8, - 28u8,210u8,46u8,136u8,66u8,184u8,80u8,89u8,193u8,17u8,16u8,63u8,13u8,176u8,30u8,174u8,187u8,12u8,79u8,178u8, - 132u8,39u8,96u8,194u8,95u8,105u8,229u8,68u8,15u8,45u8,104u8,222u8,16u8,52u8,15u8,67u8,174u8,46u8,9u8,93u8, - 48u8,24u8,116u8,52u8,249u8,221u8,59u8,244u8,145u8,24u8,165u8,245u8,70u8,168u8,152u8,15u8,88u8,174u8,69u8,55u8, - 232u8,53u8,157u8,237u8,163u8,27u8,125u8,242u8,116u8,56u8,236u8,236u8,150u8,33u8,235u8,194u8,44u8,162u8,102u8,127u8, - 12u8,123u8,6u8,216u8,146u8,60u8,69u8,46u8,195u8,53u8,196u8,133u8,219u8,182u8,38u8,47u8,118u8,213u8,12u8,70u8, - 155u8,25u8,140u8,190u8,152u8,195u8,88u8,88u8,106u8,108u8,250u8,95u8,91u8,28u8,50u8,141u8,93u8,7u8,58u8,16u8, - 170u8,136u8,112u8,248u8,70u8,174u8,212u8,214u8,246u8,42u8,179u8,239u8,182u8,1u8,231u8,70u8,9u8,10u8,101u8,47u8, - 176u8,95u8,36u8,216u8,46u8,105u8,61u8,123u8,187u8,79u8,130u8,129u8,227u8,150u8,203u8,205u8,197u8,87u8,3u8,198u8, - 247u8,103u8,86u8,122u8,154u8,203u8,131u8,238u8,35u8,192u8,35u8,155u8,172u8,195u8,103u8,125u8,167u8,212u8,45u8,150u8, - 3u8,25u8,138u8,127u8,211u8,52u8,110u8,153u8,99u8,235u8,172u8,107u8,27u8,180u8,145u8,129u8,119u8,148u8,30u8,8u8, - 240u8,42u8,25u8,198u8,63u8,53u8,114u8,195u8,152u8,204u8,7u8,35u8,189u8,228u8,137u8,249u8,116u8,172u8,159u8,62u8, - 237u8,200u8,35u8,235u8,230u8,212u8,154u8,83u8,54u8,18u8,167u8,193u8,205u8,249u8,116u8,220u8,157u8,100u8,127u8,84u8, - 212u8,161u8,66u8,131u8,86u8,106u8,86u8,106u8,172u8,201u8,76u8,99u8,85u8,231u8,254u8,113u8,175u8,253u8,227u8,110u8, - 137u8,109u8,81u8,250u8,172u8,28u8,183u8,86u8,110u8,74u8,36u8,97u8,132u8,222u8,121u8,100u8,133u8,68u8,163u8,243u8, - 20u8,179u8,228u8,198u8,19u8,5u8,127u8,182u8,34u8,126u8,203u8,74u8,103u8,206u8,125u8,35u8,20u8,3u8,50u8,234u8, - 172u8,113u8,125u8,178u8,69u8,134u8,90u8,246u8,195u8,4u8,29u8,217u8,53u8,120u8,11u8,50u8,99u8,71u8,158u8,223u8, - 121u8,154u8,251u8,242u8,218u8,71u8,36u8,110u8,71u8,222u8,186u8,143u8,35u8,142u8,107u8,134u8,42u8,33u8,183u8,25u8, - 89u8,25u8,233u8,186u8,200u8,41u8,133u8,12u8,108u8,165u8,174u8,157u8,163u8,123u8,186u8,253u8,222u8,223u8,20u8,49u8, - 120u8,44u8,240u8,230u8,15u8,95u8,28u8,62u8,28u8,244u8,252u8,98u8,52u8,244u8,173u8,187u8,170u8,146u8,58u8,227u8, - 165u8,227u8,224u8,208u8,7u8,24u8,182u8,216u8,218u8,156u8,62u8,55u8,111u8,98u8,122u8,103u8,95u8,71u8,242u8,137u8, - 211u8,183u8,125u8,37u8,162u8,95u8,25u8,212u8,231u8,218u8,250u8,102u8,4u8,223u8,104u8,19u8,199u8,90u8,32u8,190u8, - 211u8,112u8,161u8,58u8,12u8,184u8,177u8,169u8,113u8,229u8,96u8,93u8,90u8,213u8,126u8,212u8,55u8,127u8,91u8,20u8, - 207u8,122u8,179u8,218u8,214u8,108u8,138u8,111u8,45u8,149u8,218u8,43u8,235u8,184u8,245u8,234u8,143u8,155u8,19u8,239u8, - 43u8,23u8,84u8,21u8,21u8,27u8,47u8,214u8,123u8,20u8,212u8,251u8,252u8,253u8,19u8,21u8,246u8,90u8,244u8,222u8, - 21u8,222u8,181u8,165u8,85u8,234u8,191u8,46u8,216u8,56u8,95u8,7u8,233u8,29u8,7u8,127u8,64u8,229u8,175u8,101u8, - 187u8,73u8,225u8,119u8,81u8,25u8,223u8,101u8,127u8,127u8,19u8,180u8,130u8,201u8,89u8,94u8,157u8,6u8,58u8,48u8, - 104u8,211u8,229u8,22u8,210u8,248u8,66u8,100u8,233u8,78u8,11u8,36u8,218u8,35u8,184u8,173u8,128u8,238u8,35u8,170u8, - 47u8,0u8,195u8,229u8,214u8,205u8,243u8,217u8,250u8,20u8,188u8,29u8,199u8,69u8,8u8,23u8,35u8,206u8,179u8,120u8, - 95u8,158u8,163u8,59u8,215u8,114u8,124u8,91u8,125u8,101u8,128u8,111u8,149u8,4u8,186u8,251u8,112u8,174u8,81u8,87u8, - 12u8,157u8,149u8,238u8,190u8,21u8,189u8,131u8,86u8,116u8,83u8,152u8,169u8,134u8,174u8,25u8,102u8,247u8,253u8,235u8, - 95u8,169u8,127u8,109u8,37u8,248u8,247u8,239u8,193u8,234u8,16u8,11u8,248u8,226u8,72u8,152u8,164u8,101u8,78u8,189u8, - 16u8,238u8,143u8,208u8,223u8,49u8,206u8,145u8,134u8,31u8,65u8,187u8,33u8,204u8,149u8,82u8,22u8,201u8,215u8,101u8, - 159u8,17u8,252u8,195u8,180u8,173u8,240u8,1u8,41u8,7u8,201u8,34u8,168u8,235u8,169u8,248u8,171u8,163u8,128u8,149u8, - 252u8,30u8,17u8,190u8,125u8,68u8,176u8,139u8,221u8,87u8,135u8,4u8,241u8,238u8,95u8,177u8,20u8,47u8,255u8,97u8, - 172u8,64u8,246u8,137u8,180u8,199u8,247u8,73u8,140u8,187u8,91u8,227u8,222u8,182u8,227u8,143u8,233u8,170u8,107u8,76u8, - 98u8,188u8,230u8,220u8,3u8,4u8,220u8,134u8,210u8,152u8,240u8,205u8,164u8,245u8,231u8,239u8,126u8,7u8,66u8,54u8, - 155u8,167u8,200u8,58u8,0u8,0u8,0u8,0u8,19u8,116u8,114u8,97u8,110u8,115u8,97u8,99u8,116u8,105u8,111u8,110u8, - 95u8,99u8,111u8,110u8,116u8,101u8,120u8,116u8,147u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8, - 37u8,140u8,49u8,14u8,194u8,48u8,16u8,4u8,251u8,188u8,98u8,75u8,104u8,72u8,139u8,2u8,226u8,17u8,124u8,192u8, - 50u8,230u8,66u8,44u8,146u8,59u8,235u8,124u8,14u8,32u8,196u8,223u8,73u8,156u8,149u8,182u8,217u8,213u8,204u8,36u8, - 247u8,50u8,18u8,124u8,50u8,201u8,174u8,87u8,63u8,209u8,75u8,244u8,217u8,117u8,166u8,158u8,179u8,15u8,22u8,133u8, - 93u8,16u8,54u8,122u8,27u8,190u8,13u8,150u8,180u8,109u8,139u8,43u8,89u8,81u8,134u8,13u8,132u8,28u8,52u8,38u8, - 195u8,224u8,243u8,0u8,233u8,235u8,20u8,138u8,42u8,177u8,97u8,169u8,126u8,208u8,23u8,174u8,154u8,67u8,165u8,83u8, - 185u8,141u8,49u8,128u8,189u8,197u8,153u8,214u8,15u8,15u8,50u8,183u8,73u8,220u8,42u8,217u8,237u8,59u8,204u8,20u8, - 76u8,244u8,92u8,142u8,151u8,83u8,243u8,107u8,254u8,59u8,23u8,187u8,40u8,163u8,0u8,0u8,0u8,0u8,0u8,12u8, - 99u8,104u8,97u8,105u8,110u8,95u8,115u8,116u8,97u8,116u8,117u8,115u8,201u8,5u8,31u8,139u8,8u8,0u8,0u8,0u8, - 0u8,0u8,2u8,255u8,173u8,85u8,193u8,110u8,19u8,49u8,16u8,189u8,231u8,43u8,166u8,66u8,66u8,187u8,82u8,149u8, - 8u8,132u8,56u8,108u8,0u8,209u8,67u8,84u8,122u8,160u8,69u8,164u8,55u8,132u8,18u8,199u8,158u8,205u8,90u8,217u8, - 216u8,193u8,227u8,77u8,41u8,85u8,255u8,157u8,177u8,215u8,221u8,36u8,219u8,166u8,2u8,68u8,213u8,147u8,119u8,230u8, - 205u8,188u8,247u8,102u8,38u8,163u8,209u8,8u8,174u8,43u8,77u8,176u8,182u8,170u8,169u8,17u8,164u8,85u8,8u8,222u8, - 130u8,32u8,66u8,231u8,193u8,87u8,194u8,131u8,230u8,127u8,2u8,215u8,24u8,163u8,205u8,18u8,180u8,129u8,37u8,26u8, - 36u8,126u8,202u8,230u8,83u8,172u8,203u8,162u8,104u8,131u8,103u8,233u8,121u8,158u8,131u8,117u8,32u8,74u8,143u8,110u8, - 48u8,98u8,248u8,35u8,209u8,118u8,131u8,78u8,120u8,70u8,156u8,231u8,67u8,110u8,1u8,9u8,65u8,56u8,4u8,228u8, - 175u8,198u8,107u8,81u8,215u8,183u8,160u8,52u8,113u8,128u8,244u8,64u8,94u8,120u8,36u8,176u8,37u8,55u8,132u8,64u8, - 183u8,228u8,113u8,61u8,132u8,233u8,6u8,165u8,46u8,181u8,12u8,177u8,167u8,177u8,148u8,46u8,225u8,88u8,17u8,160u8, - 70u8,74u8,68u8,69u8,167u8,129u8,91u8,179u8,222u8,120u8,109u8,13u8,129u8,88u8,216u8,134u8,233u8,153u8,173u8,112u8, - 90u8,24u8,207u8,53u8,182u8,232u8,98u8,149u8,101u8,109u8,23u8,162u8,110u8,75u8,131u8,20u8,6u8,22u8,8u8,107u8, - 161u8,48u8,22u8,186u8,169u8,180u8,172u8,192u8,97u8,89u8,163u8,76u8,42u8,237u8,58u8,131u8,74u8,16u8,71u8,163u8, - 105u8,107u8,18u8,149u8,77u8,96u8,163u8,141u8,14u8,188u8,244u8,47u8,84u8,195u8,65u8,146u8,91u8,108u8,188u8,165u8, - 89u8,233u8,196u8,26u8,111u8,172u8,91u8,21u8,133u8,172u8,132u8,54u8,179u8,80u8,180u8,33u8,184u8,27u8,0u8,255u8, - 53u8,244u8,68u8,92u8,91u8,105u8,38u8,148u8,114u8,65u8,49u8,26u8,119u8,161u8,228u8,85u8,81u8,160u8,115u8,214u8, - 141u8,7u8,241u8,177u8,116u8,26u8,141u8,122u8,12u8,145u8,124u8,73u8,81u8,129u8,213u8,103u8,225u8,86u8,129u8,189u8, - 133u8,77u8,179u8,168u8,53u8,85u8,144u8,120u8,133u8,124u8,22u8,63u8,101u8,12u8,99u8,2u8,121u8,215u8,48u8,247u8, - 243u8,246u8,109u8,98u8,84u8,202u8,14u8,228u8,87u8,120u8,11u8,119u8,247u8,59u8,96u8,182u8,23u8,22u8,181u8,149u8, - 171u8,72u8,47u8,76u8,147u8,177u8,65u8,245u8,8u8,222u8,121u8,4u8,45u8,237u8,22u8,94u8,178u8,59u8,30u8,38u8, - 151u8,87u8,215u8,179u8,171u8,47u8,147u8,175u8,103u8,215u8,23u8,151u8,231u8,5u8,52u8,111u8,223u8,192u8,123u8,120u8, - 53u8,254u8,51u8,228u8,135u8,201u8,59u8,130u8,123u8,62u8,185u8,156u8,76u8,47u8,166u8,15u8,168u8,175u8,123u8,74u8, - 80u8,235u8,235u8,3u8,72u8,224u8,85u8,178u8,135u8,84u8,5u8,255u8,66u8,96u8,20u8,73u8,102u8,173u8,190u8,57u8, - 148u8,13u8,27u8,142u8,221u8,26u8,204u8,248u8,49u8,235u8,139u8,14u8,47u8,73u8,47u8,13u8,186u8,60u8,121u8,27u8, - 133u8,236u8,89u8,217u8,13u8,111u8,47u8,185u8,15u8,150u8,143u8,59u8,136u8,53u8,79u8,237u8,204u8,219u8,126u8,196u8, - 233u8,99u8,119u8,238u8,238u8,83u8,90u8,114u8,231u8,197u8,183u8,173u8,198u8,155u8,239u8,29u8,241u8,79u8,88u8,179u8, - 29u8,129u8,139u8,12u8,11u8,18u8,102u8,65u8,33u8,111u8,242u8,90u8,27u8,12u8,235u8,117u8,22u8,42u8,4u8,153u8, - 247u8,206u8,64u8,92u8,145u8,125u8,73u8,162u8,20u8,172u8,64u8,10u8,200u8,242u8,2u8,22u8,214u8,214u8,123u8,156u8, - 79u8,240u8,39u8,239u8,54u8,189u8,235u8,247u8,247u8,33u8,251u8,216u8,103u8,249u8,95u8,186u8,237u8,102u8,108u8,216u8, - 94u8,59u8,77u8,29u8,70u8,220u8,91u8,46u8,198u8,103u8,1u8,230u8,39u8,251u8,93u8,207u8,65u8,240u8,220u8,115u8, - 244u8,198u8,217u8,173u8,86u8,168u8,160u8,228u8,195u8,198u8,3u8,180u8,69u8,195u8,150u8,203u8,196u8,57u8,142u8,33u8, - 82u8,156u8,224u8,57u8,231u8,119u8,181u8,2u8,66u8,188u8,172u8,124u8,214u8,74u8,135u8,63u8,26u8,62u8,108u8,97u8, - 166u8,76u8,12u8,219u8,149u8,121u8,74u8,186u8,61u8,144u8,71u8,226u8,253u8,155u8,118u8,71u8,244u8,74u8,135u8,126u8, - 183u8,132u8,89u8,88u8,160u8,212u8,92u8,126u8,196u8,219u8,254u8,117u8,205u8,246u8,231u8,185u8,253u8,120u8,146u8,29u8, - 178u8,56u8,133u8,120u8,147u8,138u8,34u8,220u8,218u8,90u8,171u8,120u8,227u8,48u8,59u8,92u8,240u8,252u8,112u8,56u8, - 159u8,111u8,249u8,249u8,241u8,59u8,252u8,77u8,58u8,214u8,96u8,247u8,249u8,47u8,219u8,187u8,31u8,252u8,6u8,144u8, - 244u8,239u8,126u8,57u8,7u8,0u8,0u8,0u8,0u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,173u8, - 8u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,205u8,86u8,95u8,143u8,218u8,70u8,16u8,127u8,231u8, - 83u8,76u8,84u8,169u8,50u8,233u8,9u8,210u8,170u8,234u8,131u8,201u8,85u8,65u8,244u8,26u8,33u8,5u8,136u8,8u8, - 185u8,151u8,42u8,178u8,22u8,123u8,128u8,21u8,182u8,151u8,238u8,174u8,77u8,104u8,116u8,223u8,189u8,179u8,107u8,99u8, - 47u8,6u8,167u8,144u8,38u8,82u8,252u8,112u8,135u8,236u8,217u8,153u8,249u8,253u8,153u8,177u8,251u8,253u8,62u8,44u8, - 54u8,92u8,65u8,34u8,162u8,44u8,70u8,216u8,34u8,238u8,20u8,48u8,88u8,199u8,98u8,201u8,98u8,216u8,179u8,56u8, - 134u8,48u8,22u8,225u8,22u8,244u8,134u8,105u8,80u8,90u8,72u8,84u8,244u8,27u8,33u8,204u8,164u8,196u8,84u8,195u8, - 251u8,148u8,127u8,4u8,205u8,19u8,4u8,158u8,66u8,194u8,67u8,41u8,20u8,134u8,34u8,141u8,84u8,175u8,211u8,167u8, - 228u8,99u8,77u8,247u8,53u8,74u8,22u8,106u8,5u8,123u8,174u8,55u8,246u8,176u8,160u8,63u8,178u8,172u8,169u8,204u8, - 65u8,115u8,115u8,37u8,226u8,88u8,236u8,121u8,186u8,166u8,178u8,7u8,229u8,219u8,227u8,207u8,97u8,141u8,41u8,42u8, - 174u8,124u8,208u8,130u8,2u8,185u8,230u8,44u8,230u8,255u8,160u8,61u8,96u8,170u8,42u8,205u8,146u8,93u8,25u8,186u8, - 52u8,141u8,218u8,64u8,137u8,44u8,220u8,0u8,181u8,161u8,48u8,85u8,153u8,2u8,81u8,84u8,184u8,128u8,138u8,82u8, - 116u8,74u8,236u8,108u8,167u8,133u8,10u8,86u8,146u8,37u8,184u8,23u8,114u8,235u8,251u8,85u8,126u8,248u8,212u8,1u8, - 186u8,50u8,117u8,33u8,72u8,29u8,148u8,198u8,36u8,96u8,81u8,68u8,204u8,40u8,84u8,131u8,42u8,84u8,233u8,200u8, - 247u8,81u8,74u8,33u8,7u8,29u8,123u8,115u8,37u8,57u8,166u8,209u8,121u8,138u8,18u8,98u8,25u8,101u8,176u8,12u8, - 65u8,17u8,15u8,49u8,106u8,106u8,156u8,210u8,138u8,76u8,134u8,8u8,27u8,17u8,71u8,134u8,157u8,171u8,216u8,183u8, - 169u8,148u8,150u8,89u8,168u8,97u8,84u8,4u8,47u8,40u8,108u8,226u8,132u8,192u8,134u8,41u8,18u8,252u8,80u8,162u8, - 51u8,151u8,155u8,193u8,135u8,236u8,183u8,95u8,239u8,236u8,163u8,167u8,186u8,179u8,145u8,72u8,115u8,148u8,138u8,83u8, - 99u8,43u8,210u8,84u8,72u8,88u8,162u8,222u8,35u8,166u8,112u8,204u8,202u8,8u8,226u8,89u8,39u8,70u8,10u8,13u8, - 147u8,241u8,104u8,62u8,11u8,70u8,179u8,233u8,227u8,195u8,252u8,221u8,120u8,54u8,13u8,254u8,28u8,142u8,22u8,179u8, - 185u8,173u8,4u8,247u8,240u8,243u8,11u8,123u8,57u8,60u8,44u8,8u8,170u8,85u8,53u8,220u8,48u8,2u8,72u8,62u8, - 77u8,133u8,49u8,20u8,21u8,1u8,177u8,35u8,87u8,105u8,67u8,8u8,105u8,164u8,17u8,14u8,168u8,157u8,82u8,15u8, - 211u8,217u8,34u8,152u8,189u8,125u8,152u8,15u8,23u8,227u8,233u8,235u8,170u8,194u8,160u8,166u8,152u8,210u8,165u8,57u8, - 153u8,41u8,170u8,125u8,68u8,206u8,80u8,176u8,147u8,34u8,231u8,17u8,70u8,110u8,178u8,241u8,244u8,113u8,248u8,102u8, - 252u8,71u8,176u8,24u8,79u8,30u8,222u8,45u8,134u8,147u8,183u8,199u8,132u8,191u8,56u8,205u8,78u8,152u8,220u8,170u8, - 98u8,78u8,172u8,34u8,134u8,94u8,74u8,43u8,53u8,70u8,189u8,98u8,200u8,66u8,211u8,118u8,26u8,31u8,136u8,51u8, - 250u8,29u8,199u8,24u8,145u8,39u8,68u8,114u8,244u8,184u8,165u8,174u8,154u8,17u8,107u8,19u8,168u8,108u8,2u8,44u8, - 12u8,69u8,150u8,234u8,158u8,173u8,183u8,203u8,150u8,49u8,15u8,189u8,194u8,81u8,93u8,88u8,101u8,134u8,127u8,29u8, - 152u8,186u8,1u8,213u8,13u8,202u8,186u8,94u8,211u8,107u8,240u8,163u8,226u8,235u8,20u8,101u8,215u8,17u8,189u8,233u8, - 96u8,223u8,103u8,244u8,79u8,234u8,160u8,113u8,184u8,153u8,172u8,59u8,168u8,82u8,144u8,83u8,45u8,102u8,73u8,148u8, - 180u8,185u8,237u8,83u8,195u8,93u8,47u8,224u8,169u8,78u8,144u8,136u8,28u8,3u8,45u8,154u8,37u8,238u8,138u8,172u8, - 101u8,37u8,199u8,134u8,239u8,119u8,17u8,105u8,94u8,108u8,162u8,198u8,56u8,195u8,242u8,80u8,79u8,126u8,15u8,230u8, - 248u8,119u8,198u8,205u8,210u8,122u8,156u8,144u8,180u8,60u8,231u8,49u8,174u8,177u8,100u8,154u8,206u8,45u8,205u8,228u8, - 228u8,98u8,75u8,82u8,68u8,153u8,52u8,110u8,178u8,126u8,51u8,46u8,136u8,197u8,58u8,67u8,151u8,110u8,75u8,115u8, - 102u8,43u8,7u8,197u8,42u8,177u8,132u8,123u8,21u8,138u8,82u8,162u8,138u8,229u8,187u8,234u8,9u8,165u8,219u8,17u8, - 116u8,233u8,67u8,73u8,115u8,253u8,168u8,114u8,159u8,245u8,148u8,189u8,221u8,165u8,76u8,101u8,215u8,173u8,108u8,86u8, - 231u8,205u8,80u8,58u8,190u8,58u8,162u8,33u8,22u8,134u8,134u8,76u8,130u8,93u8,52u8,211u8,251u8,111u8,193u8,243u8, - 196u8,43u8,33u8,116u8,75u8,95u8,31u8,197u8,117u8,224u8,26u8,141u8,151u8,130u8,214u8,218u8,254u8,72u8,66u8,146u8, - 233u8,151u8,45u8,125u8,254u8,238u8,189u8,250u8,188u8,107u8,82u8,177u8,167u8,124u8,110u8,250u8,158u8,107u8,147u8,58u8, - 152u8,175u8,192u8,59u8,178u8,8u8,247u8,247u8,240u8,42u8,79u8,2u8,106u8,31u8,101u8,142u8,145u8,235u8,230u8,146u8, - 146u8,233u8,248u8,77u8,169u8,164u8,29u8,168u8,52u8,35u8,173u8,75u8,192u8,80u8,12u8,185u8,205u8,68u8,131u8,89u8, - 13u8,127u8,146u8,209u8,168u8,19u8,131u8,100u8,24u8,22u8,247u8,78u8,18u8,22u8,252u8,60u8,243u8,108u8,183u8,247u8, - 181u8,100u8,119u8,96u8,247u8,187u8,239u8,151u8,171u8,36u8,96u8,114u8,157u8,37u8,196u8,131u8,119u8,190u8,50u8,186u8, - 14u8,242u8,39u8,192u8,152u8,222u8,16u8,231u8,93u8,11u8,153u8,208u8,11u8,202u8,54u8,94u8,180u8,86u8,116u8,197u8, - 162u8,156u8,165u8,33u8,182u8,246u8,244u8,242u8,255u8,183u8,100u8,174u8,86u8,25u8,192u8,193u8,236u8,160u8,56u8,25u8, - 203u8,31u8,254u8,162u8,129u8,212u8,129u8,241u8,225u8,135u8,230u8,208u8,92u8,218u8,77u8,193u8,74u8,200u8,192u8,28u8, - 161u8,161u8,243u8,154u8,147u8,227u8,42u8,106u8,148u8,127u8,134u8,31u8,185u8,210u8,234u8,6u8,155u8,53u8,61u8,113u8, - 121u8,61u8,86u8,110u8,111u8,195u8,148u8,115u8,220u8,127u8,168u8,214u8,206u8,107u8,212u8,167u8,95u8,63u8,151u8,63u8, - 124u8,26u8,232u8,73u8,161u8,192u8,13u8,240u8,186u8,197u8,235u8,227u8,134u8,33u8,63u8,25u8,184u8,27u8,88u8,232u8, - 157u8,189u8,137u8,111u8,69u8,246u8,57u8,80u8,95u8,142u8,231u8,156u8,18u8,232u8,183u8,125u8,30u8,92u8,107u8,178u8, - 243u8,205u8,92u8,89u8,204u8,171u8,220u8,91u8,85u8,45u8,190u8,111u8,110u8,218u8,180u8,223u8,199u8,34u8,108u8,153u8, - 251u8,26u8,216u8,151u8,110u8,128u8,171u8,166u8,191u8,46u8,51u8,248u8,10u8,186u8,24u8,7u8,41u8,71u8,28u8,247u8, - 211u8,243u8,38u8,105u8,174u8,211u8,254u8,120u8,238u8,121u8,155u8,213u8,186u8,87u8,131u8,90u8,49u8,122u8,66u8,37u8, - 246u8,76u8,70u8,213u8,28u8,124u8,91u8,32u8,5u8,89u8,39u8,115u8,7u8,63u8,193u8,89u8,205u8,10u8,195u8,83u8, - 231u8,95u8,191u8,176u8,101u8,52u8,222u8,13u8,0u8,0u8,0u8,0u8,6u8,118u8,111u8,116u8,105u8,110u8,103u8,144u8, - 62u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,61u8,105u8,115u8,27u8,199u8,177u8,223u8,245u8, - 43u8,214u8,126u8,245u8,104u8,64u8,129u8,105u8,82u8,178u8,108u8,135u8,20u8,85u8,97u8,40u8,218u8,98u8,69u8,87u8, - 137u8,148u8,146u8,84u8,202u8,181u8,92u8,2u8,3u8,98u8,159u8,128u8,93u8,100u8,15u8,82u8,140u8,195u8,255u8,254u8, - 186u8,123u8,142u8,157u8,107u8,15u8,128u8,0u8,69u8,217u8,116u8,249u8,131u8,184u8,152u8,157u8,158u8,233u8,238u8,233u8, - 123u8,122u8,191u8,123u8,248u8,240u8,65u8,240u8,48u8,56u8,153u8,196u8,121u8,0u8,255u8,23u8,19u8,22u8,156u8,179u8, - 132u8,101u8,209u8,52u8,248u8,144u8,22u8,113u8,114u8,30u8,204u8,210u8,81u8,57u8,101u8,240u8,75u8,84u8,4u8,195u8, - 40u8,9u8,206u8,88u8,80u8,230u8,108u8,20u8,68u8,121u8,48u8,143u8,178u8,34u8,72u8,199u8,65u8,20u8,60u8,223u8, - 127u8,19u8,252u8,146u8,94u8,176u8,44u8,137u8,146u8,33u8,219u8,148u8,239u8,194u8,140u8,35u8,150u8,199u8,231u8,9u8, - 140u8,47u8,82u8,245u8,234u8,217u8,21u8,194u8,204u8,139u8,40u8,25u8,69u8,211u8,52u8,1u8,144u8,234u8,85u8,1u8, - 46u8,31u8,4u8,151u8,147u8,52u8,152u8,0u8,144u8,113u8,57u8,157u8,6u8,195u8,52u8,41u8,178u8,116u8,26u8,224u8, - 56u8,90u8,228u8,5u8,7u8,48u8,158u8,166u8,151u8,1u8,76u8,131u8,144u8,50u8,150u8,207u8,211u8,36u8,143u8,207u8, - 96u8,181u8,227u8,52u8,147u8,67u8,230u8,233u8,37u8,203u8,16u8,222u8,48u8,154u8,14u8,203u8,105u8,84u8,196u8,105u8, - 194u8,95u8,73u8,134u8,211u8,114u8,68u8,67u8,178u8,116u8,14u8,243u8,14u8,163u8,121u8,116u8,22u8,79u8,227u8,34u8, - 102u8,57u8,128u8,103u8,73u8,48u8,204u8,88u8,68u8,147u8,32u8,72u8,28u8,149u8,230u8,128u8,152u8,60u8,69u8,88u8, - 233u8,180u8,164u8,169u8,16u8,39u8,231u8,41u8,140u8,200u8,210u8,242u8,124u8,178u8,137u8,144u8,222u8,36u8,223u8,14u8, - 39u8,81u8,156u8,232u8,251u8,2u8,52u8,225u8,36u8,251u8,243u8,34u8,205u8,131u8,132u8,21u8,151u8,105u8,246u8,49u8, - 136u8,166u8,48u8,23u8,160u8,36u8,23u8,8u8,195u8,183u8,57u8,57u8,140u8,29u8,238u8,224u8,179u8,237u8,77u8,122u8, - 108u8,82u8,69u8,16u8,100u8,196u8,230u8,211u8,244u8,10u8,137u8,82u8,0u8,49u8,62u8,38u8,233u8,37u8,108u8,113u8, - 52u8,130u8,85u8,230u8,65u8,143u8,109u8,158u8,111u8,6u8,91u8,159u8,182u8,9u8,41u8,28u8,126u8,234u8,46u8,176u8, - 143u8,48u8,30u8,113u8,24u8,14u8,57u8,6u8,1u8,77u8,66u8,47u8,87u8,100u8,30u8,56u8,224u8,1u8,187u8,128u8, - 71u8,68u8,238u8,136u8,141u8,99u8,32u8,108u8,164u8,113u8,197u8,91u8,137u8,63u8,68u8,94u8,153u8,193u8,228u8,197u8, - 213u8,156u8,33u8,88u8,197u8,91u8,132u8,15u8,164u8,53u8,174u8,45u8,5u8,124u8,101u8,64u8,37u8,88u8,246u8,140u8, - 83u8,45u8,47u8,135u8,19u8,228u8,187u8,3u8,73u8,168u8,171u8,106u8,46u8,220u8,92u8,84u8,22u8,147u8,52u8,139u8, - 255u8,67u8,163u8,137u8,18u8,143u8,107u8,54u8,244u8,13u8,224u8,224u8,50u8,33u8,154u8,39u8,72u8,151u8,4u8,38u8, - 58u8,143u8,243u8,66u8,112u8,151u8,92u8,234u8,9u8,44u8,48u8,184u8,140u8,139u8,137u8,164u8,15u8,63u8,40u8,180u8, - 204u8,73u8,154u8,23u8,185u8,201u8,24u8,83u8,152u8,1u8,161u8,246u8,96u8,49u8,229u8,172u8,15u8,104u8,166u8,223u8, - 129u8,241u8,166u8,72u8,179u8,104u8,56u8,76u8,203u8,164u8,160u8,133u8,125u8,15u8,216u8,20u8,47u8,178u8,108u8,32u8, - 185u8,135u8,159u8,63u8,23u8,251u8,184u8,74u8,156u8,69u8,44u8,99u8,103u8,135u8,24u8,147u8,133u8,10u8,50u8,28u8, - 46u8,254u8,8u8,48u8,46u8,31u8,110u8,6u8,214u8,40u8,126u8,8u8,146u8,36u8,45u8,144u8,104u8,56u8,31u8,144u8, - 108u8,20u8,103u8,108u8,88u8,76u8,175u8,2u8,124u8,220u8,184u8,12u8,92u8,113u8,62u8,204u8,226u8,121u8,129u8,231u8, - 114u8,34u8,57u8,90u8,59u8,10u8,226u8,87u8,69u8,78u8,98u8,7u8,132u8,90u8,129u8,131u8,133u8,178u8,79u8,108u8, - 88u8,22u8,204u8,68u8,29u8,157u8,223u8,127u8,151u8,176u8,152u8,17u8,225u8,231u8,9u8,66u8,131u8,35u8,176u8,12u8, - 114u8,240u8,53u8,196u8,189u8,142u8,10u8,122u8,38u8,32u8,160u8,224u8,202u8,115u8,162u8,72u8,176u8,161u8,147u8,26u8, - 1u8,35u8,243u8,22u8,147u8,18u8,207u8,8u8,32u8,197u8,11u8,146u8,111u8,80u8,50u8,76u8,110u8,114u8,139u8,90u8, - 13u8,66u8,164u8,173u8,252u8,176u8,9u8,2u8,97u8,104u8,238u8,23u8,248u8,143u8,125u8,154u8,199u8,25u8,231u8,235u8, - 34u8,158u8,49u8,146u8,117u8,184u8,46u8,60u8,197u8,176u8,134u8,89u8,154u8,17u8,32u8,206u8,66u8,252u8,56u8,225u8, - 202u8,96u8,249u8,147u8,116u8,58u8,162u8,225u8,8u8,98u8,20u8,92u8,177u8,92u8,114u8,154u8,156u8,126u8,192u8,119u8, - 114u8,133u8,178u8,85u8,45u8,136u8,8u8,117u8,1u8,252u8,60u8,137u8,225u8,24u8,101u8,172u8,40u8,179u8,132u8,51u8, - 48u8,158u8,56u8,150u8,20u8,146u8,162u8,138u8,40u8,61u8,124u8,128u8,155u8,210u8,119u8,216u8,119u8,85u8,65u8,69u8, - 84u8,218u8,240u8,143u8,184u8,97u8,129u8,60u8, - ]; - let chunk2 = vector[ - 151u8,59u8,232u8,56u8,225u8,79u8,121u8,4u8,187u8,214u8,25u8,42u8,159u8,179u8,97u8,60u8,142u8,25u8,138u8, - 102u8,115u8,33u8,14u8,137u8,229u8,94u8,42u8,37u8,196u8,229u8,136u8,1u8,16u8,94u8,31u8,130u8,4u8,196u8,53u8, - 125u8,247u8,64u8,144u8,46u8,66u8,17u8,22u8,142u8,51u8,128u8,141u8,18u8,152u8,152u8,5u8,57u8,225u8,183u8,7u8, - 1u8,252u8,7u8,187u8,1u8,181u8,52u8,218u8,217u8,57u8,27u8,230u8,59u8,59u8,69u8,26u8,158u8,93u8,21u8,44u8, - 223u8,53u8,127u8,99u8,89u8,150u8,102u8,214u8,179u8,116u8,142u8,16u8,119u8,118u8,126u8,59u8,102u8,211u8,241u8,32u8, - 120u8,67u8,127u8,94u8,91u8,131u8,72u8,17u8,218u8,111u8,230u8,69u8,70u8,59u8,250u8,237u8,152u8,254u8,49u8,8u8, - 202u8,98u8,252u8,147u8,253u8,230u8,5u8,28u8,81u8,132u8,169u8,158u8,242u8,109u8,208u8,111u8,227u8,44u8,157u8,133u8, - 114u8,189u8,229u8,15u8,223u8,239u8,250u8,6u8,229u8,241u8,108u8,62u8,101u8,225u8,44u8,154u8,171u8,53u8,30u8,211u8, - 163u8,87u8,209u8,252u8,218u8,251u8,70u8,17u8,129u8,30u8,85u8,131u8,79u8,240u8,175u8,154u8,129u8,192u8,19u8,33u8, - 74u8,233u8,106u8,48u8,60u8,57u8,130u8,7u8,215u8,206u8,130u8,53u8,188u8,11u8,97u8,184u8,91u8,63u8,130u8,93u8, - 0u8,79u8,170u8,73u8,15u8,241u8,175u8,23u8,112u8,50u8,60u8,235u8,208u8,94u8,194u8,163u8,4u8,134u8,197u8,108u8, - 222u8,52u8,38u8,139u8,146u8,60u8,26u8,34u8,137u8,66u8,98u8,253u8,79u8,197u8,110u8,19u8,102u8,197u8,54u8,190u8, - 251u8,238u8,187u8,224u8,160u8,204u8,50u8,60u8,41u8,156u8,109u8,233u8,20u8,35u8,223u8,35u8,183u8,17u8,11u8,143u8, - 82u8,56u8,140u8,40u8,73u8,65u8,99u8,13u8,5u8,155u8,43u8,174u8,174u8,14u8,63u8,205u8,7u8,160u8,243u8,34u8, - 56u8,124u8,251u8,238u8,205u8,219u8,55u8,199u8,251u8,47u8,195u8,195u8,127u8,28u8,30u8,188u8,63u8,57u8,122u8,243u8, - 58u8,124u8,177u8,127u8,252u8,34u8,124u8,253u8,230u8,36u8,124u8,181u8,127u8,114u8,240u8,226u8,232u8,245u8,47u8,59u8, - 1u8,16u8,54u8,216u8,11u8,182u8,119u8,213u8,74u8,222u8,106u8,103u8,67u8,72u8,116u8,113u8,40u8,70u8,155u8,193u8, - 97u8,76u8,186u8,83u8,240u8,246u8,168u8,20u8,114u8,6u8,101u8,6u8,142u8,229u8,98u8,102u8,64u8,255u8,102u8,9u8, - 201u8,86u8,20u8,37u8,96u8,116u8,129u8,18u8,29u8,51u8,176u8,151u8,72u8,168u8,144u8,244u8,73u8,82u8,254u8,155u8, - 119u8,201u8,7u8,251u8,175u8,113u8,153u8,127u8,61u8,12u8,223u8,29u8,30u8,191u8,121u8,249u8,225u8,240u8,185u8,92u8, - 233u8,163u8,78u8,43u8,213u8,164u8,92u8,10u8,34u8,210u8,11u8,99u8,255u8,229u8,187u8,195u8,253u8,231u8,255u8,116u8, - 32u8,60u8,174u8,135u8,32u8,45u8,8u8,152u8,150u8,205u8,230u8,96u8,37u8,84u8,84u8,210u8,228u8,141u8,159u8,8u8, - 175u8,222u8,158u8,252u8,211u8,34u8,133u8,132u8,248u8,189u8,11u8,241u8,155u8,92u8,153u8,153u8,44u8,139u8,83u8,46u8, - 150u8,163u8,41u8,232u8,221u8,17u8,64u8,77u8,70u8,164u8,205u8,60u8,96u8,62u8,188u8,57u8,1u8,178u8,170u8,189u8, - 29u8,190u8,126u8,94u8,109u8,236u8,73u8,5u8,70u8,24u8,123u8,100u8,78u8,24u8,83u8,159u8,49u8,205u8,100u8,177u8, - 128u8,136u8,185u8,127u8,126u8,243u8,238u8,253u8,43u8,13u8,123u8,191u8,28u8,29u8,159u8,28u8,190u8,171u8,192u8,252u8, - 80u8,129u8,121u8,21u8,39u8,241u8,12u8,32u8,144u8,146u8,172u8,84u8,76u8,69u8,175u8,73u8,124u8,62u8,33u8,211u8, - 8u8,49u8,26u8,101u8,211u8,43u8,93u8,204u8,170u8,23u8,140u8,85u8,28u8,189u8,254u8,176u8,255u8,242u8,232u8,121u8, - 248u8,234u8,232u8,53u8,238u8,246u8,48u8,60u8,121u8,1u8,20u8,124u8,241u8,230u8,165u8,90u8,192u8,143u8,213u8,2u8, - 222u8,85u8,179u8,145u8,59u8,49u8,183u8,8u8,58u8,137u8,230u8,115u8,216u8,112u8,84u8,164u8,179u8,24u8,245u8,192u8, - 149u8,212u8,16u8,164u8,67u8,180u8,243u8,140u8,10u8,1u8,159u8,79u8,35u8,88u8,131u8,208u8,192u8,213u8,154u8,136u8, - 133u8,56u8,93u8,43u8,206u8,221u8,63u8,121u8,243u8,234u8,232u8,64u8,46u8,234u8,167u8,106u8,81u8,7u8,28u8,54u8, - 33u8,37u8,30u8,91u8,103u8,121u8,86u8,78u8,139u8,248u8,91u8,192u8,254u8,220u8,176u8,97u8,96u8,89u8,138u8,215u8, - 12u8,208u8,175u8,222u8,191u8,60u8,57u8,10u8,129u8,2u8,111u8,67u8,197u8,4u8,128u8,25u8,197u8,104u8,18u8,254u8, - 159u8,43u8,248u8,71u8,6u8,38u8,96u8,242u8,10u8,38u8,120u8,71u8,12u8,156u8,8u8,174u8,122u8,81u8,106u8,157u8, - 138u8,19u8,165u8,108u8,189u8,240u8,226u8,81u8,175u8,127u8,138u8,63u8,75u8,85u8,25u8,23u8,155u8,250u8,212u8,151u8, - 204u8,120u8,209u8,26u8,28u8,249u8,54u8,56u8,128u8,57u8,64u8,125u8,131u8,26u8,30u8,71u8,241u8,148u8,43u8,114u8, - 239u8,198u8,4u8,114u8,223u8,31u8,31u8,134u8,199u8,192u8,138u8,47u8,15u8,249u8,239u8,226u8,4u8,135u8,63u8,191u8, - 127u8,125u8,128u8,123u8,110u8,69u8,80u8,199u8,121u8,148u8,116u8,220u8,218u8,181u8,118u8,72u8,54u8,67u8,7u8,220u8, - 68u8,1u8,154u8,131u8,83u8,102u8,239u8,23u8,105u8,126u8,154u8,128u8,126u8,8u8,21u8,85u8,67u8,20u8,29u8,167u8, - 104u8,117u8,0u8,235u8,161u8,191u8,0u8,172u8,95u8,194u8,97u8,129u8,83u8,162u8,196u8,13u8,87u8,215u8,198u8,246u8, - 244u8,245u8,219u8,251u8,123u8,177u8,15u8,155u8,121u8,125u8,248u8,143u8,147u8,26u8,185u8,179u8,189u8,237u8,48u8,37u8, - 223u8,88u8,156u8,135u8,68u8,166u8,16u8,151u8,93u8,109u8,47u8,78u8,170u8,229u8,226u8,70u8,81u8,236u8,121u8,182u8, - 151u8,251u8,69u8,211u8,209u8,177u8,142u8,108u8,181u8,134u8,71u8,154u8,22u8,148u8,210u8,239u8,184u8,0u8,3u8,255u8, - 48u8,1u8,201u8,145u8,177u8,57u8,224u8,18u8,52u8,163u8,116u8,167u8,185u8,163u8,140u8,191u8,235u8,80u8,20u8,144u8, - 227u8,147u8,125u8,16u8,9u8,111u8,65u8,238u8,105u8,218u8,77u8,144u8,207u8,59u8,244u8,248u8,253u8,193u8,193u8,225u8, - 161u8,38u8,37u8,125u8,170u8,144u8,194u8,5u8,192u8,154u8,24u8,98u8,96u8,195u8,8u8,249u8,155u8,113u8,77u8,136u8, - 132u8,156u8,193u8,225u8,180u8,36u8,92u8,44u8,148u8,53u8,43u8,80u8,243u8,205u8,162u8,255u8,3u8,239u8,17u8,233u8, - 71u8,118u8,117u8,146u8,54u8,44u8,253u8,231u8,253u8,163u8,151u8,134u8,46u8,82u8,171u8,249u8,27u8,187u8,82u8,86u8, - 49u8,72u8,166u8,225u8,199u8,202u8,44u8,189u8,64u8,43u8,138u8,91u8,251u8,150u8,133u8,11u8,122u8,4u8,22u8,17u8, - 141u8,162u8,34u8,210u8,129u8,114u8,78u8,223u8,255u8,43u8,16u8,227u8,228u8,232u8,213u8,97u8,248u8,234u8,240u8,100u8, - 255u8,249u8,254u8,201u8,126u8,248u8,183u8,195u8,127u8,238u8,8u8,54u8,123u8,90u8,254u8,244u8,12u8,150u8,112u8,246u8, - 117u8,211u8,224u8,175u8,119u8,27u8,214u8,23u8,143u8,29u8,95u8,172u8,58u8,253u8,218u8,106u8,128u8,51u8,124u8,167u8, - 212u8,183u8,150u8,250u8,161u8,93u8,86u8,210u8,65u8,184u8,6u8,223u8,9u8,148u8,82u8,152u8,137u8,108u8,253u8,243u8, - 140u8,27u8,251u8,109u8,235u8,213u8,197u8,238u8,34u8,139u8,215u8,223u8,251u8,90u8,35u8,248u8,225u8,39u8,88u8,188u8, - 34u8,159u8,136u8,183u8,64u8,216u8,139u8,44u8,13u8,88u8,42u8,120u8,168u8,233u8,8u8,164u8,109u8,54u8,237u8,75u8, - 175u8,73u8,6u8,206u8,156u8,88u8,3u8,120u8,3u8,229u8,80u8,8u8,106u8,254u8,111u8,245u8,251u8,83u8,125u8,224u8, - 14u8,252u8,10u8,166u8,211u8,51u8,98u8,121u8,250u8,167u8,240u8,96u8,42u8,141u8,42u8,156u8,105u8,138u8,127u8,200u8, - 88u8,144u8,225u8,224u8,49u8,33u8,161u8,240u8,63u8,249u8,100u8,71u8,14u8,29u8,60u8,168u8,153u8,238u8,152u8,139u8, - 59u8,105u8,98u8,9u8,243u8,81u8,143u8,210u8,104u8,46u8,62u8,5u8,0u8,6u8,20u8,155u8,97u8,159u8,34u8,244u8, - 55u8,196u8,97u8,224u8,179u8,85u8,17u8,183u8,171u8,77u8,3u8,156u8,12u8,71u8,210u8,206u8,40u8,212u8,136u8,22u8, - 226u8,156u8,91u8,112u8,41u8,151u8,236u8,137u8,112u8,97u8,81u8,41u8,1u8,72u8,205u8,75u8,167u8,208u8,157u8,27u8, - 93u8,16u8,198u8,177u8,2u8,84u8,137u8,116u8,225u8,255u8,238u8,8u8,191u8,205u8,192u8,244u8,51u8,11u8,19u8,124u8, - 8u8,134u8,20u8,62u8,68u8,211u8,146u8,209u8,42u8,193u8,244u8,139u8,166u8,241u8,127u8,96u8,165u8,23u8,244u8,12u8, - 109u8,24u8,180u8,86u8,192u8,163u8,59u8,43u8,165u8,4u8,180u8,92u8,136u8,233u8,21u8,41u8,241u8,73u8,116u8,193u8, - 5u8,18u8,171u8,198u8,231u8,220u8,195u8,142u8,50u8,21u8,46u8,181u8,67u8,158u8,230u8,140u8,16u8,25u8,108u8,22u8, - 20u8,133u8,64u8,103u8,201u8,196u8,105u8,131u8,144u8,79u8,154u8,141u8,188u8,130u8,9u8,169u8,151u8,228u8,101u8,166u8, - 162u8,28u8,202u8,50u8,67u8,86u8,227u8,65u8,220u8,17u8,198u8,21u8,18u8,8u8,34u8,86u8,6u8,153u8,185u8,34u8, - 136u8,35u8,54u8,9u8,11u8,181u8,30u8,251u8,240u8,155u8,246u8,142u8,174u8,180u8,80u8,60u8,43u8,169u8,96u8,2u8, - 123u8,188u8,217u8,253u8,164u8,19u8,100u8,133u8,104u8,30u8,227u8,1u8,219u8,114u8,138u8,17u8,95u8,88u8,136u8,71u8, - 240u8,228u8,20u8,246u8,242u8,168u8,79u8,110u8,9u8,113u8,235u8,244u8,130u8,25u8,43u8,34u8,40u8,96u8,38u8,78u8, - 41u8,146u8,17u8,23u8,149u8,112u8,7u8,29u8,51u8,231u8,71u8,242u8,66u8,114u8,143u8,68u8,66u8,156u8,140u8,0u8, - 149u8,220u8,214u8,140u8,58u8,217u8,151u8,104u8,228u8,92u8,81u8,168u8,156u8,241u8,197u8,140u8,226u8,156u8,8u8,57u8, - 46u8,51u8,212u8,122u8,198u8,154u8,46u8,148u8,43u8,193u8,151u8,231u8,1u8,80u8,161u8,85u8,46u8,119u8,167u8,138u8, - 18u8,60u8,149u8,241u8,137u8,74u8,88u8,218u8,71u8,227u8,68u8,250u8,222u8,158u8,19u8,120u8,9u8,204u8,195u8,195u8, - 145u8,218u8,9u8,228u8,225u8,117u8,56u8,128u8,200u8,121u8,97u8,206u8,32u8,136u8,129u8,10u8,181u8,86u8,246u8,32u8, - 222u8,200u8,205u8,230u8,187u8,96u8,174u8,95u8,39u8,35u8,149u8,42u8,254u8,68u8,14u8,2u8,200u8,158u8,97u8,97u8, - 14u8,17u8,2u8,132u8,155u8,127u8,136u8,16u8,3u8,164u8,139u8,17u8,211u8,254u8,211u8,85u8,134u8,181u8,218u8,125u8, - 131u8,94u8,196u8,96u8,202u8,219u8,5u8,202u8,106u8,81u8,63u8,43u8,224u8,135u8,171u8,5u8,83u8,234u8,12u8,172u8, - 21u8,144u8,31u8,228u8,115u8,227u8,12u8,209u8,89u8,122u8,193u8,108u8,31u8,139u8,104u8,4u8,102u8,30u8,142u8,10u8, - 213u8,111u8,128u8,188u8,237u8,71u8,63u8,13u8,180u8,53u8,75u8,88u8,181u8,184u8,173u8,164u8,217u8,97u8,189u8,91u8, - 135u8,124u8,166u8,60u8,159u8,129u8,27u8,153u8,211u8,29u8,122u8,238u8,29u8,10u8,61u8,94u8,164u8,5u8,70u8,157u8, - 53u8,120u8,213u8,6u8,41u8,106u8,153u8,169u8,240u8,2u8,199u8,68u8,206u8,121u8,211u8,179u8,91u8,124u8,249u8,231u8, - 74u8,141u8,12u8,248u8,64u8,1u8,61u8,103u8,164u8,2u8,158u8,108u8,253u8,175u8,212u8,112u8,4u8,25u8,242u8,5u8, - 112u8,180u8,175u8,228u8,51u8,113u8,2u8,138u8,244u8,35u8,3u8,157u8,12u8,106u8,4u8,86u8,249u8,140u8,94u8,34u8, - 171u8,80u8,45u8,104u8,96u8,157u8,229u8,134u8,221u8,158u8,177u8,49u8,170u8,222u8,10u8,209u8,26u8,195u8,32u8,34u8, - 194u8,10u8,159u8,14u8,173u8,132u8,174u8,65u8,146u8,217u8,60u8,244u8,218u8,98u8,3u8,210u8,159u8,17u8,196u8,147u8, - 210u8,178u8,24u8,166u8,51u8,75u8,161u8,224u8,12u8,40u8,161u8,68u8,136u8,89u8,79u8,123u8,17u8,3u8,137u8,144u8, - 1u8,90u8,170u8,200u8,103u8,148u8,103u8,25u8,141u8,130u8,114u8,78u8,18u8,79u8,5u8,96u8,240u8,119u8,252u8,205u8, - 196u8,59u8,96u8,133u8,86u8,158u8,219u8,204u8,149u8,164u8,230u8,115u8,99u8,73u8,127u8,159u8,48u8,101u8,118u8,207u8, - 117u8,235u8,92u8,196u8,45u8,108u8,85u8,12u8,142u8,140u8,124u8,184u8,19u8,156u8,165u8,233u8,116u8,96u8,73u8,128u8, - 138u8,43u8,149u8,140u8,177u8,173u8,86u8,119u8,250u8,96u8,139u8,231u8,144u8,46u8,227u8,92u8,67u8,153u8,70u8,18u8, - 91u8,236u8,224u8,207u8,215u8,15u8,116u8,227u8,139u8,7u8,96u8,126u8,198u8,248u8,75u8,189u8,253u8,245u8,17u8,236u8, - 88u8,211u8,250u8,122u8,15u8,158u8,199u8,73u8,36u8,19u8,145u8,149u8,148u8,66u8,19u8,102u8,38u8,242u8,83u8,32u8, - 203u8,65u8,250u8,70u8,35u8,36u8,243u8,7u8,146u8,38u8,52u8,248u8,28u8,5u8,37u8,164u8,150u8,4u8,69u8,197u8, - 47u8,100u8,192u8,192u8,88u8,244u8,176u8,50u8,70u8,241u8,15u8,36u8,30u8,3u8,242u8,93u8,25u8,144u8,33u8,214u8, - 135u8,212u8,175u8,64u8,66u8,180u8,127u8,202u8,164u8,28u8,154u8,178u8,139u8,8u8,162u8,151u8,124u8,105u8,48u8,87u8, - 134u8,122u8,15u8,236u8,12u8,219u8,16u8,4u8,181u8,183u8,195u8,71u8,61u8,69u8,196u8,248u8,109u8,80u8,148u8,255u8, - 138u8,219u8,49u8,58u8,11u8,239u8,112u8,124u8,81u8,172u8,54u8,55u8,105u8,248u8,62u8,137u8,255u8,141u8,10u8,111u8, - 132u8,91u8,0u8,65u8,194u8,119u8,171u8,167u8,104u8,68u8,106u8,13u8,204u8,27u8,206u8,241u8,143u8,48u8,233u8,185u8, - 245u8,240u8,225u8,246u8,159u8,109u8,151u8,149u8,248u8,15u8,189u8,242u8,202u8,229u8,29u8,181u8,144u8,144u8,47u8,201u8, - 107u8,46u8,91u8,89u8,178u8,80u8,238u8,69u8,139u8,56u8,63u8,61u8,160u8,49u8,114u8,247u8,244u8,139u8,182u8,123u8, - 25u8,136u8,11u8,41u8,80u8,231u8,157u8,224u8,157u8,24u8,66u8,172u8,228u8,190u8,111u8,5u8,40u8,252u8,51u8,208u8, - 32u8,185u8,6u8,237u8,117u8,146u8,44u8,190u8,87u8,96u8,235u8,76u8,135u8,101u8,34u8,198u8,179u8,39u8,194u8,207u8, - 8u8,158u8,12u8,28u8,44u8,249u8,81u8,189u8,132u8,164u8,235u8,162u8,81u8,155u8,53u8,216u8,194u8,86u8,74u8,55u8, - 149u8,105u8,98u8,199u8,37u8,88u8,3u8,114u8,48u8,23u8,12u8,96u8,67u8,145u8,212u8,208u8,188u8,40u8,7u8,125u8, - 85u8,186u8,68u8,229u8,73u8,106u8,184u8,150u8,45u8,79u8,18u8,80u8,179u8,74u8,58u8,251u8,79u8,133u8,197u8,77u8, - 75u8,65u8,233u8,172u8,27u8,44u8,62u8,31u8,133u8,196u8,50u8,92u8,210u8,235u8,107u8,155u8,151u8,103u8,211u8,120u8, - 8u8,230u8,107u8,21u8,219u8,246u8,74u8,221u8,158u8,194u8,243u8,6u8,207u8,169u8,245u8,181u8,245u8,78u8,193u8,24u8, - 64u8,244u8,131u8,43u8,207u8,127u8,132u8,92u8,19u8,167u8,70u8,152u8,142u8,229u8,155u8,253u8,93u8,53u8,30u8,237u8, - 142u8,172u8,248u8,170u8,247u8,21u8,251u8,4u8,16u8,243u8,167u8,117u8,34u8,255u8,25u8,128u8,133u8,121u8,250u8,131u8, - 128u8,18u8,128u8,48u8,43u8,87u8,172u8,33u8,127u8,175u8,215u8,22u8,119u8,239u8,247u8,119u8,31u8,24u8,139u8,228u8, - 138u8,154u8,203u8,12u8,88u8,108u8,45u8,92u8,109u8,111u8,126u8,193u8,183u8,53u8,48u8,6u8,104u8,34u8,92u8,36u8, - 241u8,18u8,118u8,217u8,40u8,199u8,123u8,125u8,115u8,6u8,159u8,52u8,183u8,86u8,209u8,36u8,56u8,37u8,117u8,16u8, - 46u8,127u8,8u8,71u8,188u8,86u8,140u8,42u8,146u8,12u8,28u8,0u8,53u8,114u8,181u8,126u8,126u8,143u8,148u8,109u8, - 156u8,190u8,70u8,236u8,54u8,1u8,48u8,133u8,112u8,195u8,236u8,134u8,84u8,174u8,159u8,177u8,146u8,209u8,53u8,115u8, - 93u8,171u8,191u8,174u8,53u8,6u8,18u8,201u8,81u8,54u8,139u8,11u8,62u8,159u8,119u8,239u8,198u8,68u8,27u8,179u8, - 210u8,100u8,186u8,77u8,190u8,186u8,77u8,47u8,154u8,205u8,69u8,120u8,164u8,161u8,203u8,15u8,94u8,41u8,232u8,34u8, - 198u8,39u8,10u8,181u8,36u8,50u8,253u8,51u8,29u8,155u8,44u8,106u8,115u8,232u8,117u8,245u8,167u8,126u8,172u8,102u8, - 224u8,43u8,133u8,69u8,42u8,49u8,57u8,48u8,182u8,43u8,206u8,252u8,181u8,150u8,205u8,149u8,37u8,51u8,53u8,254u8, - 189u8,40u8,84u8,56u8,143u8,97u8,195u8,85u8,196u8,63u8,151u8,239u8,171u8,121u8,254u8,66u8,191u8,25u8,192u8,66u8, - 25u8,114u8,67u8,159u8,149u8,158u8,64u8,136u8,87u8,62u8,3u8,247u8,56u8,179u8,234u8,95u8,200u8,127u8,71u8,87u8, - 134u8,34u8,94u8,155u8,246u8,212u8,78u8,172u8,138u8,230u8,173u8,172u8,60u8,249u8,148u8,98u8,54u8,114u8,46u8,190u8, - 238u8,51u8,140u8,173u8,152u8,145u8,28u8,180u8,123u8,133u8,189u8,69u8,149u8,21u8,60u8,150u8,167u8,96u8,82u8,160u8, - 66u8,214u8,90u8,69u8,90u8,144u8,174u8,170u8,182u8,146u8,65u8,139u8,124u8,152u8,206u8,153u8,233u8,138u8,55u8,172u8, - 157u8,124u8,247u8,53u8,57u8,241u8,15u8,140u8,224u8,139u8,233u8,192u8,107u8,139u8,113u8,77u8,0u8,90u8,208u8,76u8, - 100u8,64u8,109u8,7u8,28u8,115u8,108u8,124u8,167u8,24u8,82u8,6u8,227u8,53u8,51u8,231u8,71u8,52u8,97u8,141u8, - 201u8,184u8,156u8,122u8,246u8,109u8,24u8,47u8,4u8,71u8,198u8,254u8,225u8,65u8,154u8,140u8,48u8,20u8,37u8,202u8, - 113u8,12u8,102u8,160u8,55u8,209u8,50u8,23u8,238u8,218u8,60u8,69u8,226u8,198u8,148u8,248u8,212u8,243u8,252u8,14u8, - 196u8,102u8,75u8,76u8,150u8,21u8,234u8,25u8,16u8,238u8,90u8,90u8,174u8,63u8,57u8,204u8,141u8,88u8,148u8,225u8, - 44u8,12u8,141u8,201u8,202u8,18u8,206u8,124u8,196u8,192u8,185u8,17u8,16u8,134u8,16u8,70u8,89u8,212u8,206u8,40u8, - 162u8,184u8,39u8,70u8,188u8,86u8,236u8,77u8,51u8,8u8,44u8,101u8,227u8,183u8,11u8,154u8,226u8,217u8,154u8,92u8, - 118u8,78u8,169u8,103u8,152u8,39u8,58u8,172u8,131u8,92u8,204u8,142u8,93u8,54u8,86u8,115u8,67u8,19u8,123u8,33u8, - 3u8,185u8,207u8,147u8,89u8,209u8,80u8,212u8,200u8,105u8,246u8,72u8,131u8,179u8,4u8,169u8,84u8,143u8,241u8,193u8, - 44u8,177u8,239u8,195u8,185u8,101u8,114u8,216u8,232u8,174u8,251u8,25u8,145u8,108u8,254u8,230u8,34u8,215u8,126u8,215u8, - 64u8,173u8,245u8,99u8,51u8,102u8,45u8,72u8,2u8,159u8,230u8,211u8,49u8,152u8,90u8,85u8,252u8,183u8,223u8,73u8, - 201u8,144u8,235u8,235u8,139u8,241u8,222u8,43u8,157u8,181u8,41u8,157u8,124u8,18u8,125u8,251u8,232u8,201u8,15u8,171u8, - 84u8,62u8,10u8,170u8,30u8,73u8,190u8,87u8,62u8,95u8,142u8,242u8,225u8,51u8,250u8,235u8,40u8,96u8,126u8,116u8, - 80u8,69u8,166u8,134u8,230u8,151u8,57u8,154u8,220u8,151u8,51u8,111u8,73u8,87u8,221u8,64u8,217u8,129u8,148u8,189u8, - 215u8,119u8,171u8,214u8,119u8,34u8,238u8,236u8,33u8,188u8,30u8,130u8,238u8,166u8,21u8,129u8,27u8,122u8,178u8,4u8, - 24u8,230u8,204u8,33u8,70u8,223u8,219u8,104u8,217u8,68u8,191u8,111u8,185u8,80u8,50u8,16u8,225u8,56u8,76u8,30u8, - 177u8,241u8,116u8,47u8,120u8,40u8,1u8,158u8,165u8,16u8,143u8,184u8,108u8,135u8,231u8,122u8,98u8,34u8,144u8,17u8, - 39u8,192u8,226u8,241u8,40u8,140u8,178u8,243u8,114u8,6u8,148u8,239u8,53u8,20u8,239u8,89u8,179u8,104u8,113u8,148u8, - 235u8,93u8,45u8,222u8,27u8,188u8,138u8,62u8,130u8,232u8,44u8,51u8,230u8,149u8,178u8,223u8,228u8,92u8,10u8,139u8, - 250u8,27u8,170u8,154u8,218u8,116u8,34u8,50u8,156u8,100u8,59u8,59u8,83u8,150u8,156u8,23u8,19u8,216u8,160u8,193u8, - 121u8,125u8,72u8,222u8,108u8,13u8,234u8,247u8,208u8,88u8,210u8,217u8,22u8,140u8,225u8,56u8,13u8,207u8,167u8,233u8, - 25u8,28u8,63u8,112u8,156u8,155u8,194u8,66u8,190u8,19u8,166u8,33u8,6u8,167u8,215u8,226u8,53u8,48u8,187u8,225u8, - 132u8,219u8,241u8,156u8,93u8,239u8,201u8,117u8,134u8,181u8,77u8,19u8,252u8,9u8,75u8,164u8,116u8,146u8,236u8,67u8, - 210u8,39u8,10u8,198u8,211u8,232u8,220u8,78u8,55u8,155u8,90u8,165u8,91u8,230u8,93u8,175u8,40u8,135u8,61u8,247u8, - 40u8,186u8,160u8,12u8,36u8,170u8,98u8,239u8,213u8,215u8,0u8,64u8,20u8,77u8,86u8,215u8,247u8,54u8,252u8,167u8, - 208u8,33u8,145u8,57u8,76u8,47u8,114u8,11u8,49u8,3u8,179u8,215u8,8u8,211u8,46u8,5u8,208u8,232u8,131u8,135u8, - 183u8,102u8,9u8,214u8,9u8,229u8,117u8,133u8,154u8,117u8,166u8,161u8,172u8,166u8,98u8,82u8,166u8,230u8,163u8,102u8, - 228u8,119u8,46u8,39u8,213u8,150u8,66u8,166u8,148u8,42u8,35u8,56u8,187u8,194u8,75u8,34u8,17u8,204u8,195u8,13u8, - 210u8,205u8,224u8,239u8,112u8,246u8,80u8,217u8,20u8,213u8,56u8,170u8,181u8,40u8,69u8,133u8,204u8,37u8,26u8,126u8, - 88u8,136u8,36u8,64u8,136u8,107u8,110u8,190u8,194u8,0u8,13u8,150u8,189u8,10u8,105u8,247u8,73u8,227u8,204u8,170u8, - 65u8,144u8,233u8,200u8,104u8,140u8,181u8,146u8,18u8,36u8,38u8,138u8,91u8,129u8,26u8,144u8,90u8,216u8,173u8,153u8, - 55u8,116u8,102u8,35u8,228u8,244u8,251u8,134u8,176u8,58u8,114u8,149u8,121u8,93u8,69u8,40u8,150u8,30u8,77u8,152u8, - 86u8,170u8,38u8,141u8,17u8,145u8,201u8,23u8,181u8,58u8,195u8,105u8,44u8,99u8,247u8,248u8,103u8,71u8,166u8,196u8, - 76u8,226u8,166u8,187u8,176u8,43u8,89u8,37u8,196u8,2u8,218u8,140u8,228u8,169u8,140u8,97u8,188u8,43u8,136u8,33u8, - 206u8,37u8,203u8,29u8,107u8,66u8,89u8,100u8,188u8,224u8,221u8,132u8,228u8,155u8,66u8,212u8,13u8,35u8,65u8,13u8, - 112u8,215u8,1u8,3u8,204u8,208u8,105u8,208u8,145u8,45u8,236u8,244u8,28u8,241u8,104u8,99u8,125u8,163u8,25u8,237u8, - 142u8,134u8,211u8,231u8,229u8,139u8,95u8,116u8,198u8,93u8,111u8,48u8,84u8,132u8,185u8,21u8,95u8,24u8,130u8,81u8, - 133u8,195u8,7u8,186u8,20u8,174u8,98u8,225u8,214u8,26u8,253u8,14u8,172u8,175u8,82u8,69u8,165u8,162u8,33u8,170u8, - 11u8,186u8,66u8,88u8,213u8,78u8,44u8,221u8,181u8,171u8,164u8,226u8,70u8,51u8,193u8,10u8,115u8,58u8,163u8,251u8, - 11u8,248u8,193u8,94u8,239u8,244u8,246u8,188u8,99u8,45u8,29u8,100u8,101u8,36u8,170u8,124u8,144u8,245u8,131u8,145u8, - 247u8,167u8,147u8,105u8,254u8,238u8,207u8,213u8,107u8,147u8,92u8,247u8,27u8,99u8,226u8,222u8,124u8,67u8,183u8,160u8, - 184u8,63u8,185u8,97u8,46u8,207u8,151u8,65u8,253u8,173u8,62u8,222u8,29u8,143u8,60u8,38u8,216u8,34u8,24u8,110u8, - 227u8,128u8,86u8,122u8,214u8,179u8,73u8,23u8,86u8,169u8,9u8,186u8,107u8,251u8,179u8,99u8,31u8,31u8,196u8,77u8, - 76u8,87u8,101u8,110u8,214u8,197u8,51u8,16u8,227u8,224u8,2u8,202u8,162u8,47u8,172u8,211u8,105u8,187u8,137u8,201u8, - 47u8,71u8,122u8,238u8,97u8,130u8,95u8,13u8,142u8,107u8,33u8,107u8,98u8,42u8,63u8,204u8,252u8,143u8,148u8,219u8, - 121u8,9u8,224u8,225u8,200u8,201u8,234u8,75u8,161u8,183u8,216u8,52u8,62u8,143u8,69u8,32u8,2u8,61u8,98u8,163u8, - 186u8,6u8,43u8,52u8,197u8,205u8,244u8,105u8,37u8,251u8,179u8,248u8,124u8,82u8,104u8,107u8,221u8,236u8,28u8,178u8, - 177u8,202u8,116u8,185u8,49u8,234u8,198u8,109u8,168u8,100u8,163u8,46u8,104u8,163u8,155u8,128u8,94u8,55u8,83u8,27u8, - 171u8,210u8,196u8,118u8,197u8,145u8,186u8,192u8,207u8,247u8,89u8,93u8,113u8,144u8,119u8,232u8,249u8,110u8,27u8,246u8, - 200u8,95u8,9u8,81u8,43u8,26u8,213u8,64u8,28u8,28u8,174u8,31u8,157u8,127u8,85u8,115u8,229u8,120u8,192u8,56u8, - 174u8,197u8,231u8,229u8,156u8,178u8,99u8,94u8,225u8,93u8,216u8,239u8,93u8,44u8,137u8,78u8,90u8,172u8,218u8,154u8, - 225u8,41u8,182u8,57u8,137u8,183u8,232u8,102u8,192u8,212u8,66u8,39u8,10u8,8u8,48u8,117u8,103u8,213u8,104u8,218u8, - 70u8,178u8,223u8,0u8,49u8,53u8,228u8,247u8,184u8,181u8,7u8,99u8,121u8,163u8,5u8,110u8,215u8,57u8,133u8,88u8, - 162u8,44u8,28u8,233u8,203u8,41u8,206u8,11u8,135u8,205u8,50u8,65u8,97u8,86u8,94u8,18u8,115u8,225u8,33u8,215u8, - 225u8,78u8,177u8,220u8,9u8,222u8,82u8,144u8,230u8,122u8,251u8,0u8,17u8,43u8,194u8,203u8,30u8,232u8,99u8,198u8, - 218u8,85u8,116u8,176u8,136u8,120u8,76u8,201u8,185u8,230u8,65u8,114u8,2u8,36u8,2u8,140u8,224u8,69u8,250u8,79u8, - 169u8,250u8,15u8,28u8,37u8,29u8,176u8,81u8,65u8,168u8,215u8,12u8,130u8,211u8,202u8,11u8,10u8,251u8,155u8,193u8, - 81u8,34u8,11u8,16u8,65u8,101u8,241u8,34u8,186u8,11u8,29u8,81u8,16u8,45u8,44u8,41u8,98u8,166u8,48u8,134u8, - 125u8,48u8,50u8,164u8,137u8,97u8,224u8,253u8,162u8,73u8,52u8,54u8,132u8,148u8,112u8,156u8,207u8,192u8,150u8,47u8, - 99u8,48u8,227u8,83u8,105u8,186u8,59u8,29u8,31u8,34u8,42u8,107u8,4u8,22u8,136u8,121u8,245u8,102u8,144u8,97u8, - 203u8,140u8,128u8,170u8,189u8,88u8,34u8,95u8,192u8,229u8,77u8,83u8,222u8,170u8,67u8,135u8,9u8,111u8,3u8,42u8, - 179u8,56u8,210u8,43u8,211u8,85u8,21u8,68u8,76u8,74u8,28u8,121u8,132u8,223u8,104u8,12u8,113u8,133u8,61u8,229u8, - 17u8,57u8,14u8,55u8,221u8,241u8,233u8,53u8,223u8,108u8,236u8,251u8,202u8,45u8,148u8,38u8,208u8,140u8,128u8,214u8, - 217u8,237u8,203u8,160u8,150u8,33u8,191u8,79u8,147u8,55u8,250u8,179u8,116u8,199u8,181u8,241u8,198u8,11u8,93u8,115u8, - 19u8,44u8,99u8,92u8,57u8,185u8,98u8,133u8,7u8,97u8,245u8,198u8,178u8,218u8,161u8,102u8,223u8,46u8,228u8,163u8, - 246u8,29u8,21u8,253u8,223u8,255u8,6u8,15u8,117u8,128u8,50u8,246u8,115u8,115u8,80u8,193u8,222u8,158u8,227u8,37u8, - 181u8,70u8,140u8,4u8,121u8,218u8,32u8,24u8,174u8,60u8,57u8,24u8,149u8,8u8,237u8,123u8,237u8,111u8,216u8,137u8, - 50u8,39u8,65u8,152u8,121u8,30u8,254u8,41u8,232u8,85u8,234u8,11u8,36u8,14u8,134u8,2u8,117u8,247u8,128u8,123u8, - 51u8,53u8,115u8,75u8,131u8,84u8,159u8,90u8,61u8,107u8,155u8,217u8,140u8,170u8,188u8,91u8,227u8,85u8,10u8,20u8, - 233u8,202u8,203u8,32u8,115u8,142u8,211u8,39u8,208u8,73u8,85u8,231u8,134u8,88u8,170u8,65u8,11u8,145u8,52u8,93u8, - 22u8,177u8,226u8,34u8,139u8,49u8,183u8,207u8,231u8,243u8,176u8,107u8,165u8,141u8,234u8,38u8,193u8,13u8,122u8,246u8, - 221u8,66u8,94u8,111u8,140u8,192u8,3u8,130u8,71u8,4u8,60u8,243u8,215u8,80u8,217u8,245u8,43u8,180u8,162u8,157u8, - 110u8,222u8,132u8,86u8,12u8,100u8,30u8,170u8,170u8,206u8,239u8,55u8,211u8,61u8,173u8,88u8,208u8,52u8,187u8,45u8, - 11u8,251u8,32u8,157u8,205u8,48u8,21u8,135u8,49u8,9u8,210u8,0u8,206u8,45u8,154u8,138u8,43u8,7u8,88u8,80u8, - 21u8,101u8,163u8,41u8,218u8,152u8,139u8,39u8,48u8,208u8,44u8,3u8,89u8,173u8,86u8,89u8,77u8,220u8,98u8,168u8, - 221u8,200u8,18u8,235u8,100u8,86u8,169u8,87u8,73u8,32u8,1u8,247u8,156u8,51u8,45u8,24u8,74u8,15u8,45u8,71u8, - 219u8,155u8,145u8,174u8,177u8,130u8,164u8,168u8,183u8,161u8,236u8,213u8,94u8,82u8,109u8,213u8,99u8,110u8,227u8,132u8, - 207u8,21u8,145u8,94u8,149u8,169u8,184u8,122u8,189u8,174u8,75u8,217u8,191u8,87u8,215u8,221u8,103u8,90u8,102u8,33u8, - 42u8,236u8,158u8,47u8,113u8,46u8,154u8,5u8,240u8,123u8,161u8,134u8,221u8,3u8,137u8,79u8,116u8,78u8,10u8,179u8, - 99u8,0u8,118u8,249u8,48u8,123u8,6u8,208u8,117u8,66u8,112u8,7u8,193u8,112u8,58u8,71u8,129u8,87u8,96u8,43u8, - 157u8,42u8,15u8,138u8,81u8,220u8,124u8,50u8,77u8,249u8,21u8,63u8,72u8,116u8,231u8,166u8,196u8,174u8,206u8,5u8, - 5u8,45u8,184u8,176u8,6u8,126u8,238u8,45u8,166u8,185u8,27u8,165u8,180u8,207u8,162u8,170u8,83u8,4u8,144u8,39u8, - 177u8,150u8,84u8,71u8,142u8,134u8,254u8,8u8,6u8,65u8,188u8,153u8,42u8,79u8,87u8,149u8,157u8,29u8,60u8,135u8, - 60u8,229u8,67u8,225u8,138u8,30u8,153u8,25u8,106u8,203u8,77u8,177u8,140u8,14u8,121u8,157u8,250u8,126u8,41u8,253u8, - 38u8,145u8,249u8,174u8,241u8,210u8,63u8,175u8,192u8,224u8,193u8,10u8,112u8,153u8,241u8,194u8,61u8,15u8,64u8,72u8, - 101u8,205u8,5u8,39u8,176u8,31u8,58u8,177u8,192u8,128u8,224u8,168u8,32u8,135u8,128u8,5u8,137u8,92u8,162u8,172u8, - 6u8,188u8,109u8,174u8,238u8,185u8,66u8,228u8,64u8,171u8,79u8,147u8,47u8,147u8,219u8,160u8,185u8,72u8,188u8,221u8, - 10u8,93u8,183u8,18u8,215u8,214u8,185u8,47u8,146u8,83u8,148u8,129u8,27u8,251u8,185u8,118u8,103u8,93u8,92u8,130u8, - 18u8,177u8,96u8,48u8,211u8,139u8,205u8,133u8,171u8,68u8,110u8,37u8,228u8,96u8,212u8,117u8,19u8,242u8,215u8,170u8, - 49u8,204u8,204u8,117u8,107u8,238u8,182u8,93u8,165u8,117u8,83u8,23u8,95u8,162u8,244u8,246u8,58u8,79u8,66u8,170u8, - 186u8,141u8,139u8,36u8,239u8,69u8,205u8,249u8,26u8,92u8,160u8,22u8,196u8,111u8,79u8,206u8,153u8,118u8,39u8,190u8, - 14u8,242u8,32u8,116u8,166u8,88u8,204u8,24u8,53u8,95u8,183u8,236u8,90u8,119u8,126u8,219u8,110u8,117u8,51u8,142u8, - 26u8,206u8,141u8,158u8,91u8,24u8,2u8,234u8,46u8,219u8,45u8,168u8,218u8,186u8,108u8,55u8,220u8,3u8,90u8,9u8, - 110u8,240u8,205u8,103u8,49u8,4u8,70u8,64u8,116u8,142u8,64u8,215u8,177u8,81u8,239u8,38u8,13u8,84u8,250u8,53u8, - 134u8,111u8,165u8,205u8,228u8,157u8,13u8,216u8,59u8,4u8,32u8,194u8,51u8,22u8,154u8,207u8,171u8,0u8,193u8,174u8, - 115u8,58u8,117u8,67u8,0u8,249u8,21u8,18u8,144u8,158u8,65u8,190u8,80u8,191u8,238u8,6u8,88u8,74u8,173u8,165u8, - 20u8,222u8,170u8,210u8,239u8,90u8,7u8,239u8,189u8,15u8,96u8,87u8,194u8,155u8,23u8,103u8,22u8,140u8,247u8,107u8, - 73u8,18u8,215u8,169u8,117u8,135u8,87u8,153u8,19u8,199u8,79u8,173u8,189u8,207u8,32u8,72u8,210u8,41u8,118u8,47u8, - 83u8,81u8,12u8,123u8,88u8,12u8,109u8,175u8,204u8,205u8,70u8,117u8,211u8,165u8,93u8,171u8,27u8,245u8,144u8,244u8, - 151u8,173u8,103u8,239u8,110u8,136u8,223u8,109u8,94u8,68u8,239u8,224u8,115u8,187u8,121u8,93u8,92u8,87u8,219u8,96u8, - 123u8,130u8,174u8,62u8,95u8,160u8,84u8,237u8,230u8,113u8,121u8,119u8,75u8,110u8,21u8,90u8,255u8,94u8,237u8,119u8, - 84u8,251u8,239u8,231u8,163u8,72u8,116u8,99u8,93u8,160u8,76u8,193u8,168u8,98u8,81u8,38u8,195u8,66u8,101u8,44u8, - 150u8,153u8,176u8,218u8,130u8,158u8,165u8,109u8,133u8,214u8,34u8,134u8,122u8,195u8,192u8,124u8,151u8,87u8,220u8,152u8, - 86u8,75u8,151u8,248u8,87u8,183u8,242u8,7u8,10u8,173u8,45u8,178u8,12u8,45u8,96u8,8u8,10u8,184u8,65u8,225u8, - 223u8,208u8,114u8,51u8,214u8,116u8,83u8,163u8,45u8,216u8,216u8,88u8,143u8,173u8,133u8,43u8,245u8,200u8,145u8,16u8, - 86u8,207u8,91u8,184u8,237u8,5u8,118u8,245u8,161u8,103u8,56u8,121u8,179u8,91u8,245u8,86u8,116u8,75u8,105u8,93u8, - 183u8,254u8,114u8,240u8,78u8,77u8,125u8,164u8,137u8,106u8,72u8,13u8,52u8,237u8,168u8,161u8,82u8,114u8,153u8,222u8, - 116u8,118u8,128u8,70u8,148u8,80u8,117u8,221u8,12u8,229u8,88u8,102u8,44u8,74u8,68u8,182u8,157u8,235u8,97u8,125u8, - 194u8,111u8,93u8,212u8,213u8,213u8,100u8,165u8,109u8,47u8,106u8,98u8,9u8,115u8,248u8,151u8,236u8,27u8,76u8,220u8, - 203u8,62u8,76u8,90u8,35u8,49u8,21u8,6u8,162u8,177u8,162u8,71u8,91u8,163u8,175u8,195u8,99u8,83u8,152u8,148u8, - 155u8,69u8,217u8,71u8,41u8,9u8,45u8,248u8,158u8,134u8,23u8,32u8,162u8,154u8,168u8,85u8,155u8,21u8,105u8,178u8, - 165u8,87u8,100u8,79u8,139u8,109u8,29u8,51u8,46u8,211u8,79u8,59u8,10u8,224u8,211u8,170u8,198u8,144u8,178u8,71u8, - 65u8,57u8,231u8,45u8,219u8,197u8,21u8,5u8,167u8,198u8,95u8,71u8,117u8,58u8,110u8,115u8,42u8,37u8,214u8,12u8, - 166u8,239u8,123u8,204u8,239u8,187u8,35u8,159u8,111u8,32u8,163u8,121u8,254u8,205u8,156u8,238u8,186u8,37u8,253u8,81u8, - 157u8,194u8,161u8,236u8,82u8,76u8,92u8,156u8,139u8,214u8,238u8,26u8,190u8,7u8,246u8,139u8,101u8,101u8,4u8,24u8, - 45u8,202u8,13u8,251u8,80u8,125u8,53u8,0u8,203u8,72u8,141u8,170u8,110u8,189u8,31u8,60u8,89u8,150u8,102u8,173u8, - 176u8,193u8,152u8,150u8,49u8,186u8,231u8,147u8,90u8,181u8,217u8,54u8,108u8,1u8,228u8,237u8,101u8,73u8,85u8,145u8, - 232u8,2u8,6u8,232u8,59u8,156u8,90u8,94u8,218u8,41u8,119u8,18u8,177u8,73u8,175u8,255u8,12u8,255u8,108u8,228u8, - 59u8,150u8,153u8,182u8,77u8,98u8,240u8,90u8,7u8,18u8,121u8,58u8,96u8,126u8,119u8,74u8,192u8,160u8,247u8,176u8, - 73u8,94u8,13u8,160u8,92u8,93u8,91u8,162u8,75u8,73u8,173u8,65u8,152u8,229u8,125u8,247u8,123u8,159u8,122u8,97u8, - 159u8,90u8,243u8,140u8,255u8,231u8,95u8,23u8,49u8,187u8,252u8,213u8,246u8,149u8,170u8,210u8,10u8,94u8,151u8,225u8, - 119u8,148u8,154u8,253u8,35u8,199u8,47u8,234u8,243u8,26u8,164u8,155u8,22u8,31u8,173u8,201u8,49u8,233u8,109u8,44u8, - 150u8,69u8,106u8,97u8,80u8,52u8,114u8,90u8,10u8,84u8,220u8,8u8,5u8,47u8,130u8,199u8,178u8,119u8,95u8,203u8, - 168u8,12u8,27u8,107u8,169u8,198u8,101u8,190u8,254u8,103u8,160u8,115u8,180u8,254u8,103u8,125u8,247u8,178u8,150u8,111u8, - 205u8,126u8,218u8,86u8,55u8,140u8,54u8,252u8,173u8,56u8,36u8,53u8,91u8,174u8,22u8,85u8,194u8,116u8,193u8,59u8, - 70u8,72u8,43u8,231u8,157u8,106u8,167u8,158u8,91u8,69u8,157u8,65u8,237u8,58u8,170u8,218u8,83u8,181u8,241u8,108u8, - 175u8,9u8,60u8,80u8,215u8,45u8,199u8,104u8,124u8,197u8,103u8,0u8,100u8,21u8,197u8,107u8,245u8,103u8,245u8,207u8, - 234u8,246u8,174u8,231u8,240u8,234u8,12u8,52u8,97u8,188u8,159u8,177u8,243u8,149u8,12u8,51u8,97u8,116u8,135u8,35u8, - 66u8,2u8,49u8,111u8,141u8,246u8,204u8,162u8,185u8,42u8,195u8,46u8,206u8,100u8,135u8,56u8,252u8,221u8,146u8,75u8, - 95u8,125u8,246u8,166u8,227u8,133u8,187u8,70u8,113u8,218u8,37u8,118u8,227u8,59u8,28u8,107u8,146u8,145u8,171u8,150u8, - 147u8,114u8,190u8,230u8,34u8,41u8,119u8,124u8,83u8,225u8,147u8,229u8,2u8,32u8,138u8,181u8,131u8,91u8,189u8,10u8, - 62u8,185u8,94u8,134u8,165u8,31u8,212u8,202u8,94u8,118u8,74u8,201u8,125u8,39u8,181u8,174u8,124u8,194u8,60u8,182u8, - 62u8,91u8,215u8,243u8,54u8,239u8,200u8,93u8,215u8,58u8,199u8,55u8,137u8,191u8,27u8,249u8,3u8,243u8,237u8,118u8, - 169u8,80u8,255u8,77u8,159u8,230u8,163u8,100u8,85u8,234u8,127u8,254u8,67u8,245u8,5u8,86u8,157u8,104u8,94u8,133u8, - 129u8,204u8,101u8,72u8,167u8,59u8,51u8,94u8,202u8,153u8,62u8,202u8,154u8,233u8,165u8,245u8,36u8,255u8,93u8,147u8, - 77u8,71u8,105u8,23u8,170u8,105u8,118u8,156u8,118u8,29u8,196u8,48u8,233u8,172u8,143u8,164u8,232u8,177u8,23u8,211u8, - 26u8,151u8,191u8,173u8,153u8,148u8,171u8,176u8,207u8,239u8,50u8,17u8,53u8,76u8,46u8,72u8,65u8,205u8,131u8,245u8, - 17u8,211u8,155u8,58u8,48u8,73u8,216u8,22u8,91u8,185u8,39u8,237u8,50u8,164u8,93u8,231u8,117u8,107u8,25u8,189u8, - 94u8,44u8,49u8,208u8,114u8,47u8,180u8,75u8,31u8,0u8,243u8,195u8,38u8,122u8,54u8,224u8,6u8,41u8,134u8,182u8, - 27u8,176u8,29u8,220u8,81u8,243u8,3u8,82u8,169u8,55u8,9u8,91u8,243u8,89u8,41u8,51u8,170u8,160u8,249u8,195u8, - 43u8,115u8,63u8,27u8,138u8,10u8,187u8,233u8,94u8,176u8,12u8,139u8,16u8,19u8,251u8,191u8,234u8,237u8,71u8,79u8, - 224u8,169u8,209u8,123u8,84u8,116u8,29u8,173u8,123u8,141u8,127u8,208u8,67u8,220u8,45u8,121u8,254u8,254u8,221u8,62u8, - 49u8,214u8,241u8,225u8,193u8,113u8,245u8,241u8,31u8,252u8,111u8,183u8,230u8,125u8,183u8,183u8,10u8,13u8,80u8,220u8, - 79u8,223u8,188u8,141u8,135u8,149u8,96u8,168u8,196u8,145u8,106u8,50u8,218u8,208u8,158u8,164u8,181u8,51u8,73u8,153u8, - 51u8,9u8,35u8,180u8,111u8,150u8,194u8,162u8,168u8,106u8,114u8,241u8,182u8,35u8,196u8,79u8,226u8,179u8,165u8,23u8, - 250u8,167u8,194u8,168u8,119u8,143u8,243u8,61u8,80u8,167u8,95u8,241u8,83u8,157u8,12u8,207u8,122u8,218u8,199u8,96u8, - 77u8,81u8,80u8,253u8,160u8,60u8,87u8,111u8,115u8,213u8,218u8,9u8,52u8,249u8,100u8,80u8,254u8,55u8,43u8,188u8, - 43u8,238u8,131u8,101u8,31u8,115u8,117u8,163u8,61u8,45u8,179u8,224u8,21u8,94u8,115u8,47u8,19u8,252u8,208u8,7u8, - 188u8,13u8,10u8,161u8,96u8,73u8,78u8,69u8,26u8,179u8,116u8,248u8,49u8,224u8,13u8,126u8,156u8,120u8,52u8,191u8, - 96u8,241u8,175u8,237u8,95u8,205u8,160u8,168u8,19u8,127u8,150u8,137u8,62u8,74u8,188u8,160u8,193u8,213u8,211u8,86u8, - 47u8,127u8,156u8,151u8,144u8,157u8,193u8,174u8,86u8,92u8,156u8,90u8,149u8,171u8,193u8,182u8,181u8,97u8,213u8,22u8, - 192u8,74u8,128u8,18u8,77u8,124u8,61u8,105u8,122u8,246u8,21u8,153u8,46u8,28u8,99u8,251u8,84u8,158u8,182u8,65u8, - 38u8,133u8,29u8,31u8,202u8,165u8,236u8,96u8,169u8,49u8,218u8,135u8,71u8,173u8,255u8,218u8,238u8,43u8,111u8,111u8, - 185u8,207u8,106u8,133u8,206u8,159u8,188u8,34u8,160u8,195u8,173u8,234u8,134u8,11u8,213u8,245u8,247u8,161u8,187u8,144u8, - 192u8,236u8,134u8,211u8,236u8,111u8,218u8,45u8,12u8,239u8,105u8,179u8,28u8,109u8,250u8,117u8,46u8,122u8,189u8,208u8, - 119u8,210u8,16u8,32u8,39u8,73u8,5u8,116u8,8u8,26u8,53u8,4u8,223u8,173u8,198u8,35u8,66u8,130u8,7u8,120u8, - 205u8,20u8,196u8,133u8,102u8,24u8,40u8,76u8,243u8,33u8,253u8,142u8,241u8,173u8,250u8,76u8,231u8,226u8,146u8,108u8, - 73u8,105u8,198u8,245u8,133u8,91u8,228u8,101u8,50u8,111u8,107u8,176u8,109u8,96u8,1u8,233u8,123u8,162u8,77u8,13u8, - 104u8,243u8,199u8,121u8,87u8,176u8,42u8,15u8,202u8,22u8,76u8,187u8,90u8,250u8,77u8,22u8,184u8,47u8,184u8,18u8, - 79u8,108u8,207u8,210u8,148u8,90u8,200u8,206u8,9u8,100u8,95u8,47u8,103u8,246u8,172u8,197u8,220u8,89u8,168u8,143u8, - 168u8,223u8,10u8,59u8,215u8,62u8,49u8,223u8,32u8,46u8,120u8,236u8,190u8,191u8,32u8,6u8,20u8,52u8,140u8,219u8, - 243u8,106u8,11u8,187u8,10u8,67u8,92u8,79u8,197u8,219u8,229u8,158u8,53u8,85u8,24u8,242u8,157u8,254u8,214u8,163u8, - 173u8,90u8,129u8,139u8,245u8,136u8,191u8,149u8,68u8,234u8,253u8,197u8,250u8,96u8,241u8,42u8,141u8,177u8,86u8,224u8, - 238u8,236u8,218u8,219u8,139u8,218u8,141u8,11u8,153u8,125u8,162u8,201u8,4u8,125u8,90u8,84u8,125u8,189u8,209u8,42u8, - 80u8,117u8,74u8,161u8,90u8,229u8,228u8,93u8,52u8,137u8,206u8,190u8,254u8,186u8,155u8,174u8,229u8,62u8,141u8,251u8, - 92u8,230u8,232u8,146u8,52u8,97u8,252u8,232u8,245u8,60u8,119u8,181u8,187u8,26u8,158u8,238u8,155u8,6u8,70u8,107u8, - 251u8,253u8,125u8,17u8,38u8,206u8,157u8,198u8,117u8,191u8,89u8,144u8,107u8,7u8,11u8,78u8,208u8,95u8,182u8,62u8, - 109u8,63u8,122u8,220u8,255u8,85u8,12u8,0u8,167u8,27u8,38u8,102u8,92u8,74u8,149u8,25u8,235u8,69u8,16u8,166u8, - 40u8,66u8,250u8,212u8,228u8,94u8,0u8,99u8,97u8,55u8,96u8,159u8,76u8,211u8,33u8,207u8,71u8,236u8,5u8,248u8, - 29u8,245u8,254u8,106u8,164u8,162u8,79u8,26u8,246u8,23u8,109u8,29u8,189u8,172u8,252u8,149u8,114u8,255u8,139u8,194u8, - 152u8,118u8,160u8,62u8,51u8,242u8,180u8,242u8,223u8,22u8,165u8,137u8,31u8,136u8,186u8,162u8,45u8,211u8,239u8,50u8, - 202u8,68u8,141u8,238u8,212u8,212u8,150u8,178u8,210u8,148u8,163u8,87u8,99u8,162u8,79u8,37u8,246u8,226u8,218u8,205u8, - 248u8,163u8,180u8,234u8,110u8,85u8,175u8,106u8,78u8,77u8,14u8,137u8,30u8,42u8,152u8,196u8,43u8,98u8,162u8,89u8, - 161u8,154u8,3u8,192u8,246u8,220u8,89u8,110u8,28u8,143u8,249u8,204u8,106u8,221u8,109u8,80u8,218u8,217u8,58u8,243u8, - 74u8,71u8,63u8,145u8,61u8,33u8,96u8,79u8,101u8,65u8,157u8,101u8,225u8,55u8,154u8,61u8,119u8,240u8,69u8,22u8, - 119u8,16u8,108u8,89u8,148u8,249u8,144u8,234u8,253u8,110u8,196u8,158u8,33u8,218u8,234u8,177u8,77u8,148u8,187u8,148u8, - 58u8,43u8,218u8,160u8,151u8,6u8,65u8,203u8,202u8,6u8,168u8,99u8,2u8,171u8,210u8,190u8,214u8,168u8,79u8,199u8, - 54u8,19u8,209u8,25u8,216u8,244u8,241u8,231u8,56u8,202u8,137u8,174u8,151u8,81u8,54u8,82u8,222u8,183u8,207u8,243u8, - 198u8,190u8,70u8,183u8,132u8,115u8,173u8,239u8,193u8,118u8,223u8,99u8,218u8,97u8,253u8,20u8,66u8,194u8,128u8,60u8, - 213u8,192u8,243u8,98u8,42u8,234u8,118u8,162u8,74u8,26u8,41u8,180u8,119u8,234u8,145u8,52u8,188u8,222u8,147u8,59u8, - 132u8,88u8,107u8,165u8,135u8,101u8,100u8,24u8,166u8,215u8,63u8,53u8,27u8,103u8,98u8,139u8,97u8,113u8,98u8,85u8, - 183u8,83u8,234u8,162u8,217u8,212u8,33u8,83u8,251u8,72u8,169u8,108u8,230u8,166u8,15u8,62u8,181u8,132u8,48u8,0u8, - 213u8,191u8,113u8,71u8,221u8,77u8,171u8,143u8,193u8,83u8,27u8,152u8,84u8,43u8,128u8,174u8,190u8,4u8,138u8,197u8, - 239u8,101u8,46u8,11u8,206u8,197u8,94u8,188u8,223u8,169u8,63u8,13u8,228u8,14u8,77u8,155u8,247u8,43u8,239u8,17u8, - 195u8,210u8,12u8,191u8,12u8,181u8,205u8,226u8,118u8,223u8,185u8,149u8,193u8,155u8,61u8,103u8,175u8,117u8,88u8,31u8, - 133u8,89u8,16u8,182u8,127u8,147u8,206u8,129u8,187u8,222u8,93u8,174u8,254u8,209u8,88u8,205u8,179u8,102u8,225u8,169u8, - 186u8,47u8,44u8,94u8,206u8,99u8,118u8,200u8,120u8,228u8,53u8,104u8,44u8,109u8,131u8,86u8,141u8,245u8,72u8,151u8, - 69u8,182u8,209u8,211u8,65u8,145u8,47u8,166u8,192u8,91u8,181u8,112u8,119u8,75u8,65u8,159u8,95u8,152u8,116u8,77u8, - 150u8,221u8,218u8,17u8,161u8,91u8,104u8,119u8,3u8,39u8,200u8,205u8,245u8,246u8,218u8,13u8,49u8,210u8,104u8,15u8, - 239u8,109u8,125u8,122u8,2u8,214u8,112u8,212u8,98u8,13u8,119u8,68u8,39u8,74u8,237u8,36u8,45u8,72u8,112u8,235u8, - 194u8,92u8,158u8,96u8,37u8,196u8,239u8,18u8,218u8,63u8,39u8,39u8,118u8,210u8,120u8,119u8,234u8,220u8,46u8,237u8, - 84u8,8u8,214u8,144u8,172u8,80u8,92u8,198u8,67u8,182u8,172u8,111u8,113u8,219u8,209u8,184u8,123u8,175u8,97u8,165u8, - 94u8,131u8,25u8,199u8,187u8,247u8,22u8,254u8,32u8,222u8,194u8,202u8,141u8,67u8,131u8,143u8,28u8,180u8,222u8,2u8, - 152u8,91u8,83u8,211u8,20u8,182u8,122u8,12u8,138u8,250u8,241u8,98u8,138u8,218u8,39u8,116u8,215u8,161u8,79u8,22u8, - 17u8,238u8,30u8,181u8,242u8,37u8,163u8,115u8,205u8,214u8,228u8,242u8,152u8,93u8,81u8,248u8,143u8,167u8,229u8,238u8, - 21u8,245u8,31u8,67u8,81u8,211u8,173u8,51u8,136u8,143u8,223u8,53u8,45u8,173u8,55u8,91u8,232u8,218u8,188u8,4u8, - 171u8,13u8,249u8,133u8,235u8,83u8,146u8,50u8,167u8,88u8,99u8,236u8,148u8,118u8,46u8,144u8,98u8,108u8,233u8,217u8, - 101u8,150u8,35u8,183u8,227u8,2u8,145u8,188u8,93u8,223u8,111u8,248u8,214u8,13u8,19u8,215u8,50u8,185u8,169u8,161u8, - 99u8,95u8,110u8,95u8,212u8,210u8,145u8,221u8,227u8,95u8,139u8,142u8,160u8,113u8,130u8,12u8,157u8,179u8,234u8,156u8, - 7u8,81u8,174u8,95u8,229u8,228u8,210u8,82u8,52u8,6u8,149u8,151u8,180u8,233u8,10u8,117u8,241u8,169u8,187u8,217u8, - 244u8,25u8,236u8,163u8,71u8,118u8,85u8,96u8,35u8,35u8,46u8,121u8,32u8,120u8,139u8,250u8,110u8,199u8,2u8,34u8, - 155u8,99u8,44u8,212u8,173u8,249u8,42u8,213u8,26u8,142u8,194u8,227u8,182u8,90u8,164u8,21u8,217u8,111u8,146u8,41u8, - 155u8,48u8,218u8,177u8,197u8,81u8,112u8,86u8,22u8,212u8,115u8,65u8,117u8,234u8,48u8,58u8,199u8,235u8,136u8,90u8, - 29u8,158u8,190u8,247u8,84u8,16u8,45u8,116u8,235u8,96u8,145u8,160u8,167u8,173u8,86u8,86u8,117u8,239u8,192u8,224u8, - 33u8,127u8,35u8,225u8,39u8,53u8,162u8,241u8,150u8,220u8,135u8,181u8,162u8,117u8,101u8,177u8,228u8,31u8,90u8,90u8, - 123u8,118u8,21u8,13u8,234u8,235u8,114u8,96u8,54u8,241u8,111u8,250u8,130u8,184u8,149u8,242u8,161u8,173u8,191u8,139u8, - 255u8,179u8,118u8,159u8,87u8,177u8,254u8,216u8,86u8,114u8,177u8,254u8,224u8,30u8,111u8,105u8,176u8,246u8,232u8,93u8, - 163u8,129u8,126u8,59u8,206u8,86u8,119u8,63u8,226u8,54u8,67u8,239u8,11u8,32u8,102u8,165u8,190u8,18u8,112u8,43u8, - 154u8,28u8,97u8,241u8,201u8,91u8,162u8,81u8,9u8,129u8,58u8,4u8,116u8,43u8,214u8,244u8,248u8,86u8,221u8,26u8, - 34u8,126u8,62u8,7u8,235u8,15u8,229u8,39u8,173u8,221u8,98u8,255u8,126u8,181u8,6u8,251u8,15u8,55u8,142u8,76u8, - 210u8,103u8,212u8,168u8,187u8,138u8,94u8,226u8,9u8,57u8,142u8,161u8,110u8,177u8,3u8,187u8,224u8,199u8,41u8,241u8, - 27u8,72u8,231u8,248u8,217u8,55u8,254u8,179u8,222u8,150u8,127u8,243u8,247u8,29u8,195u8,251u8,233u8,38u8,201u8,182u8, - 38u8,17u8,115u8,67u8,209u8,210u8,77u8,122u8,44u8,43u8,246u8,238u8,112u8,20u8,112u8,109u8,4u8,209u8,213u8,221u8, - 23u8,75u8,155u8,155u8,234u8,70u8,132u8,180u8,124u8,25u8,225u8,125u8,4u8,241u8,119u8,149u8,234u8,187u8,91u8,153u8, - 183u8,27u8,199u8,203u8,110u8,26u8,224u8,250u8,98u8,82u8,121u8,188u8,231u8,206u8,173u8,231u8,241u8,110u8,95u8,23u8, - 60u8,90u8,74u8,23u8,112u8,25u8,183u8,70u8,151u8,166u8,89u8,136u8,222u8,37u8,101u8,122u8,19u8,4u8,222u8,142u8, - 123u8,184u8,8u8,46u8,63u8,43u8,47u8,62u8,89u8,42u8,59u8,73u8,221u8,184u8,232u8,107u8,173u8,86u8,187u8,8u8, - 56u8,102u8,212u8,65u8,177u8,222u8,18u8,105u8,50u8,68u8,22u8,177u8,67u8,110u8,75u8,1u8,111u8,172u8,198u8,197u8, - 220u8,248u8,108u8,154u8,84u8,7u8,237u8,87u8,161u8,222u8,111u8,244u8,138u8,14u8,34u8,16u8,119u8,195u8,183u8,213u8, - 55u8,156u8,133u8,215u8,149u8,164u8,151u8,228u8,121u8,173u8,84u8,185u8,220u8,185u8,194u8,152u8,91u8,61u8,146u8,123u8, - 252u8,64u8,254u8,249u8,166u8,7u8,210u8,23u8,220u8,36u8,198u8,206u8,101u8,128u8,19u8,184u8,250u8,119u8,125u8,62u8, - 111u8,213u8,66u8,222u8,248u8,252u8,38u8,242u8,70u8,75u8,244u8,200u8,98u8,249u8,63u8,70u8,9u8,92u8,117u8,212u8, - 235u8,147u8,190u8,119u8,57u8,91u8,187u8,22u8,43u8,216u8,173u8,70u8,190u8,125u8,23u8,229u8,166u8,78u8,254u8,125u8, - 177u8,208u8,125u8,177u8,208u8,231u8,247u8,243u8,239u8,29u8,253u8,123u8,71u8,127u8,189u8,126u8,234u8,186u8,83u8,187u8, - 93u8,196u8,233u8,29u8,114u8,84u8,111u8,142u8,203u8,91u8,245u8,252u8,87u8,145u8,49u8,239u8,122u8,223u8,5u8,53u8, - 11u8,246u8,200u8,86u8,205u8,108u8,194u8,73u8,124u8,62u8,1u8,87u8,0u8,63u8,78u8,24u8,58u8,61u8,111u8,110u8, - 33u8,73u8,60u8,248u8,82u8,178u8,196u8,173u8,43u8,104u8,86u8,148u8,166u8,231u8,108u8,119u8,158u8,217u8,92u8,188u8, - 65u8,145u8,161u8,246u8,158u8,212u8,100u8,126u8,111u8,247u8,240u8,97u8,39u8,139u8,31u8,151u8,114u8,74u8,23u8,227u8, - 203u8,149u8,240u8,227u8,66u8,5u8,239u8,75u8,158u8,155u8,59u8,26u8,24u8,189u8,45u8,58u8,173u8,46u8,225u8,120u8, - 71u8,72u8,182u8,38u8,181u8,230u8,71u8,121u8,198u8,230u8,211u8,8u8,140u8,23u8,179u8,207u8,202u8,138u8,245u8,209u8, - 189u8,199u8,114u8,103u8,146u8,147u8,127u8,200u8,216u8,203u8,125u8,179u8,146u8,149u8,181u8,196u8,248u8,215u8,246u8,86u8, - 249u8,211u8,175u8,253u8,91u8,232u8,61u8,177u8,220u8,23u8,101u8,182u8,60u8,136u8,174u8,253u8,58u8,223u8,158u8,177u8, - 169u8,170u8,75u8,133u8,254u8,242u8,87u8,55u8,174u8,87u8,126u8,172u8,132u8,249u8,245u8,131u8,255u8,7u8,204u8,229u8, - 129u8,157u8,146u8,210u8,0u8,0u8,0u8,0u8,14u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8, - 102u8,105u8,103u8,252u8,19u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,91u8,109u8,111u8,219u8, - 56u8,18u8,254u8,222u8,95u8,193u8,197u8,162u8,61u8,251u8,32u8,56u8,118u8,219u8,220u8,7u8,183u8,89u8,156u8,209u8, - 250u8,138u8,96u8,219u8,164u8,112u8,210u8,246u8,176u8,69u8,161u8,202u8,18u8,109u8,179u8,149u8,68u8,45u8,37u8,217u8, - 201u8,46u8,242u8,223u8,119u8,134u8,148u8,44u8,82u8,111u8,86u8,94u8,156u8,77u8,129u8,13u8,138u8,34u8,182u8,73u8, - 206u8,204u8,51u8,51u8,207u8,204u8,208u8,202u8,193u8,193u8,1u8,121u8,47u8,248u8,154u8,121u8,52u8,38u8,201u8,138u8, - 18u8,151u8,135u8,11u8,182u8,76u8,133u8,147u8,48u8,30u8,146u8,5u8,23u8,36u8,78u8,156u8,239u8,44u8,92u8,18u8, - 39u8,244u8,136u8,160u8,27u8,71u8,120u8,241u8,163u8,128u8,123u8,169u8,79u8,137u8,19u8,37u8,60u8,182u8,23u8,194u8, - 9u8,232u8,134u8,139u8,239u8,227u8,113u8,182u8,214u8,86u8,167u8,144u8,63u8,31u8,17u8,248u8,73u8,99u8,10u8,135u8, - 120u8,227u8,49u8,21u8,130u8,139u8,23u8,143u8,182u8,111u8,86u8,183u8,95u8,198u8,9u8,13u8,108u8,199u8,243u8,4u8, - 141u8,99u8,26u8,103u8,107u8,23u8,130u8,81u8,16u8,94u8,89u8,190u8,164u8,33u8,141u8,89u8,190u8,234u8,0u8,76u8, - 57u8,3u8,249u8,148u8,248u8,220u8,253u8,158u8,70u8,196u8,203u8,173u8,112u8,157u8,48u8,228u8,9u8,153u8,83u8,242u8, - 7u8,21u8,124u8,32u8,87u8,131u8,134u8,113u8,66u8,166u8,191u8,77u8,103u8,167u8,246u8,219u8,211u8,87u8,191u8,126u8, - 120u8,111u8,191u8,254u8,48u8,155u8,156u8,31u8,159u8,158u8,140u8,73u8,250u8,159u8,231u8,228u8,136u8,140u8,94u8,108u8, - 207u8,157u8,73u8,187u8,9u8,156u8,71u8,137u8,71u8,67u8,30u8,176u8,208u8,73u8,0u8,155u8,93u8,39u8,207u8,166u8, - 159u8,38u8,179u8,215u8,103u8,54u8,28u8,60u8,181u8,95u8,79u8,79u8,78u8,223u8,29u8,159u8,76u8,206u8,79u8,103u8, - 185u8,136u8,167u8,133u8,136u8,179u8,136u8,186u8,108u8,193u8,168u8,39u8,1u8,167u8,32u8,43u8,92u8,82u8,194u8,98u8, - 194u8,194u8,181u8,227u8,51u8,111u8,64u8,222u8,57u8,23u8,36u8,72u8,99u8,41u8,110u8,41u8,40u8,232u8,34u8,192u8, - 101u8,78u8,72u8,64u8,27u8,67u8,244u8,241u8,201u8,199u8,201u8,219u8,227u8,215u8,246u8,217u8,249u8,228u8,215u8,41u8, - 200u8,62u8,121u8,51u8,205u8,5u8,62u8,43u8,4u8,158u8,131u8,183u8,215u8,60u8,65u8,215u8,70u8,124u8,3u8,103u8, - 177u8,208u8,133u8,83u8,193u8,47u8,62u8,11u8,88u8,66u8,34u8,42u8,92u8,26u8,38u8,14u8,104u8,145u8,75u8,221u8, - 176u8,100u8,197u8,66u8,210u8,27u8,90u8,228u8,112u8,248u8,165u8,86u8,230u8,199u8,211u8,243u8,227u8,147u8,55u8,246u8, - 251u8,211u8,79u8,211u8,153u8,125u8,124u8,242u8,106u8,54u8,157u8,156u8,77u8,237u8,183u8,199u8,239u8,142u8,207u8,115u8, - 29u8,158u8,215u8,25u8,157u8,69u8,150u8,130u8,184u8,176u8,218u8,34u8,155u8,21u8,115u8,87u8,101u8,13u8,62u8,131u8, - 6u8,239u8,38u8,255u8,55u8,240u8,173u8,215u8,71u8,95u8,145u8,43u8,112u8,168u8,69u8,204u8,91u8,105u8,43u8,70u8, - 126u8,224u8,92u8,176u8,32u8,13u8,8u8,200u8,77u8,41u8,225u8,11u8,242u8,53u8,211u8,201u8,70u8,157u8,190u8,130u8, - 70u8,132u8,11u8,15u8,49u8,231u8,196u8,89u8,115u8,6u8,1u8,25u8,94u8,18u8,71u8,128u8,70u8,1u8,77u8,152u8, - 75u8,248u8,154u8,138u8,133u8,207u8,55u8,186u8,30u8,101u8,37u8,183u8,177u8,53u8,148u8,63u8,154u8,34u8,31u8,209u8, - 92u8,25u8,84u8,49u8,77u8,204u8,52u8,196u8,204u8,116u8,18u8,176u8,221u8,247u8,17u8,131u8,24u8,22u8,1u8,98u8, - 8u8,133u8,84u8,252u8,191u8,165u8,196u8,32u8,142u8,235u8,242u8,52u8,76u8,148u8,30u8,113u8,34u8,82u8,55u8,145u8, - 153u8,1u8,174u8,126u8,165u8,18u8,115u8,229u8,196u8,32u8,34u8,186u8,180u8,136u8,39u8,120u8,100u8,145u8,239u8,244u8, - 50u8,75u8,86u8,165u8,14u8,153u8,144u8,245u8,86u8,159u8,144u8,82u8,112u8,12u8,88u8,173u8,2u8,19u8,52u8,241u8, - 33u8,76u8,16u8,52u8,240u8,147u8,19u8,160u8,40u8,252u8,20u8,52u8,115u8,230u8,64u8,10u8,240u8,235u8,55u8,14u8, - 96u8,161u8,106u8,107u8,221u8,168u8,129u8,46u8,224u8,120u8,65u8,156u8,5u8,70u8,48u8,174u8,197u8,24u8,172u8,44u8, - 151u8,132u8,227u8,40u8,223u8,128u8,100u8,145u8,160u8,79u8,16u8,114u8,26u8,113u8,119u8,101u8,17u8,167u8,88u8,254u8, - 175u8,56u8,211u8,13u8,173u8,137u8,65u8,19u8,240u8,130u8,174u8,158u8,46u8,24u8,142u8,187u8,220u8,66u8,41u8,104u8, - 0u8,94u8,243u8,128u8,97u8,120u8,160u8,4u8,233u8,106u8,66u8,94u8,97u8,68u8,216u8,242u8,108u8,233u8,57u8,171u8, - 9u8,35u8,32u8,2u8,194u8,67u8,255u8,178u8,192u8,40u8,224u8,38u8,68u8,3u8,50u8,1u8,221u8,125u8,71u8,44u8, - 169u8,200u8,86u8,21u8,90u8,124u8,163u8,110u8,66u8,189u8,123u8,0u8,136u8,94u8,184u8,202u8,155u8,133u8,98u8,86u8, - 9u8,29u8,38u8,76u8,90u8,208u8,120u8,159u8,108u8,120u8,234u8,123u8,202u8,208u8,57u8,166u8,105u8,156u8,34u8,120u8, - 92u8,228u8,9u8,148u8,155u8,31u8,20u8,241u8,39u8,129u8,84u8,169u8,85u8,11u8,164u8,160u8,110u8,42u8,4u8,22u8, - 13u8,69u8,219u8,118u8,78u8,219u8,118u8,76u8,221u8,184u8,138u8,250u8,167u8,21u8,5u8,97u8,162u8,176u8,13u8,236u8, - 16u8,32u8,210u8,151u8,62u8,87u8,177u8,119u8,0u8,1u8,186u8,166u8,160u8,63u8,184u8,32u8,43u8,18u8,133u8,50u8, - 114u8,165u8,189u8,221u8,14u8,98u8,18u8,219u8,93u8,33u8,219u8,142u8,201u8,156u8,115u8,223u8,144u8,118u8,174u8,17u8, - 67u8,14u8,194u8,146u8,173u8,41u8,120u8,59u8,77u8,8u8,133u8,140u8,207u8,224u8,30u8,192u8,82u8,128u8,52u8,119u8, - 169u8,199u8,176u8,164u8,122u8,100u8,126u8,41u8,161u8,49u8,216u8,77u8,43u8,32u8,134u8,199u8,255u8,7u8,56u8,210u8, - 11u8,39u8,136u8,124u8,106u8,145u8,225u8,96u8,56u8,28u8,61u8,6u8,170u8,29u8,32u8,79u8,140u8,250u8,50u8,194u8, - 100u8,172u8,68u8,80u8,27u8,129u8,151u8,225u8,112u8,72u8,226u8,209u8,144u8,28u8,228u8,100u8,50u8,208u8,48u8,45u8, - 136u8,171u8,2u8,119u8,241u8,145u8,173u8,105u8,82u8,5u8,250u8,20u8,189u8,44u8,227u8,228u8,49u8,6u8,22u8,122u8, - 137u8,202u8,76u8,79u8,28u8,191u8,84u8,56u8,98u8,5u8,42u8,168u8,212u8,152u8,252u8,72u8,159u8,212u8,1u8,38u8, - 87u8,104u8,153u8,24u8,35u8,223u8,199u8,192u8,51u8,46u8,84u8,125u8,7u8,16u8,133u8,83u8,192u8,202u8,53u8,138u8, - 115u8,0u8,254u8,56u8,6u8,196u8,115u8,166u8,1u8,85u8,66u8,186u8,201u8,2u8,77u8,230u8,109u8,145u8,32u8,144u8, - 12u8,8u8,83u8,196u8,19u8,216u8,202u8,64u8,39u8,56u8,74u8,210u8,2u8,223u8,72u8,149u8,116u8,169u8,33u8,77u8, - 36u8,89u8,50u8,48u8,141u8,131u8,105u8,113u8,196u8,67u8,15u8,143u8,41u8,5u8,22u8,214u8,119u8,40u8,139u8,158u8, - 210u8,10u8,178u8,140u8,185u8,44u8,146u8,69u8,42u8,148u8,28u8,79u8,195u8,56u8,197u8,114u8,69u8,18u8,22u8,208u8, - 170u8,93u8,170u8,154u8,200u8,200u8,208u8,106u8,231u8,227u8,190u8,37u8,15u8,134u8,146u8,235u8,167u8,104u8,93u8,177u8, - 81u8,33u8,107u8,75u8,100u8,237u8,188u8,36u8,219u8,178u8,36u8,107u8,62u8,186u8,42u8,10u8,135u8,244u8,147u8,11u8, - 198u8,2u8,252u8,144u8,59u8,104u8,130u8,17u8,243u8,81u8,58u8,247u8,153u8,219u8,83u8,77u8,84u8,159u8,44u8,210u8, - 16u8,196u8,50u8,132u8,135u8,253u8,65u8,123u8,69u8,90u8,148u8,186u8,43u8,242u8,36u8,102u8,203u8,144u8,10u8,171u8, - 19u8,29u8,222u8,89u8,134u8,119u8,207u8,206u8,219u8,7u8,121u8,55u8,168u8,251u8,90u8,101u8,44u8,55u8,168u8,227u8, - 49u8,4u8,39u8,21u8,137u8,93u8,66u8,175u8,87u8,122u8,221u8,207u8,10u8,189u8,30u8,25u8,24u8,169u8,11u8,135u8, - 249u8,185u8,183u8,200u8,28u8,24u8,197u8,72u8,131u8,152u8,171u8,152u8,70u8,78u8,15u8,88u8,92u8,234u8,9u8,50u8, - 66u8,144u8,193u8,139u8,213u8,3u8,94u8,0u8,19u8,211u8,237u8,113u8,113u8,234u8,74u8,174u8,47u8,204u8,85u8,160u8, - 82u8,91u8,208u8,223u8,83u8,6u8,45u8,132u8,242u8,86u8,207u8,240u8,171u8,101u8,186u8,82u8,87u8,92u8,153u8,250u8, - 83u8,175u8,221u8,159u8,228u8,23u8,2u8,33u8,46u8,91u8,253u8,241u8,56u8,235u8,226u8,108u8,168u8,122u8,105u8,0u8, - 57u8,217u8,171u8,109u8,184u8,251u8,253u8,23u8,21u8,33u8,219u8,55u8,218u8,252u8,41u8,101u8,25u8,43u8,219u8,229u8, - 54u8,181u8,227u8,253u8,226u8,144u8,93u8,170u8,180u8,196u8,12u8,106u8,67u8,158u8,60u8,105u8,93u8,242u8,18u8,186u8, - 207u8,174u8,42u8,119u8,104u8,169u8,77u8,197u8,245u8,16u8,43u8,53u8,175u8,170u8,139u8,102u8,106u8,198u8,11u8,65u8, - 128u8,144u8,248u8,193u8,27u8,82u8,47u8,197u8,220u8,16u8,77u8,95u8,65u8,193u8,114u8,207u8,90u8,215u8,250u8,226u8, - 49u8,69u8,235u8,171u8,11u8,206u8,187u8,96u8,146u8,85u8,129u8,188u8,244u8,1u8,67u8,185u8,169u8,47u8,227u8,100u8, - 96u8,106u8,167u8,123u8,244u8,107u8,30u8,214u8,142u8,247u8,13u8,90u8,126u8,165u8,215u8,146u8,170u8,38u8,7u8,102u8, - 84u8,12u8,219u8,237u8,20u8,170u8,73u8,149u8,133u8,181u8,199u8,6u8,116u8,96u8,25u8,209u8,2u8,213u8,177u8,73u8, - 84u8,127u8,80u8,19u8,217u8,218u8,206u8,26u8,36u8,172u8,221u8,206u8,210u8,151u8,247u8,75u8,62u8,249u8,68u8,51u8, - 73u8,42u8,173u8,123u8,29u8,21u8,69u8,77u8,70u8,29u8,180u8,109u8,218u8,127u8,27u8,173u8,177u8,61u8,182u8,19u8, - 94u8,230u8,51u8,171u8,52u8,79u8,252u8,105u8,68u8,180u8,73u8,40u8,230u8,71u8,58u8,185u8,88u8,165u8,36u8,111u8, - 163u8,21u8,115u8,109u8,115u8,149u8,176u8,26u8,137u8,195u8,234u8,68u8,41u8,86u8,215u8,108u8,47u8,22u8,94u8,101u8, - 156u8,145u8,213u8,100u8,85u8,111u8,101u8,157u8,133u8,192u8,237u8,245u8,199u8,37u8,172u8,28u8,87u8,178u8,111u8,220u8, - 8u8,225u8,191u8,231u8,64u8,233u8,96u8,222u8,210u8,231u8,115u8,199u8,127u8,105u8,44u8,251u8,165u8,87u8,158u8,246u8, - 250u8,229u8,126u8,96u8,70u8,147u8,84u8,132u8,144u8,241u8,165u8,62u8,89u8,205u8,149u8,18u8,36u8,173u8,101u8,166u8, - 94u8,141u8,210u8,118u8,51u8,190u8,61u8,85u8,133u8,160u8,61u8,48u8,244u8,234u8,171u8,234u8,172u8,89u8,161u8,214u8, - 13u8,154u8,79u8,106u8,208u8,91u8,113u8,134u8,42u8,80u8,24u8,73u8,7u8,219u8,177u8,98u8,80u8,167u8,105u8,169u8, - 150u8,53u8,106u8,215u8,195u8,122u8,142u8,69u8,93u8,47u8,231u8,217u8,242u8,65u8,169u8,2u8,230u8,239u8,26u8,133u8, - 176u8,85u8,221u8,44u8,114u8,43u8,55u8,79u8,50u8,207u8,213u8,148u8,80u8,248u8,1u8,219u8,229u8,52u8,225u8,1u8, - 44u8,113u8,101u8,143u8,10u8,125u8,53u8,221u8,100u8,131u8,84u8,47u8,13u8,125u8,40u8,255u8,106u8,76u8,133u8,223u8, - 225u8,64u8,156u8,192u8,182u8,98u8,177u8,141u8,244u8,132u8,179u8,65u8,239u8,33u8,8u8,94u8,220u8,111u8,192u8,165u8, - 33u8,155u8,154u8,33u8,194u8,155u8,137u8,138u8,255u8,218u8,179u8,178u8,21u8,148u8,237u8,149u8,89u8,131u8,130u8,248u8, - 185u8,204u8,191u8,27u8,122u8,205u8,72u8,110u8,82u8,243u8,166u8,193u8,246u8,45u8,170u8,230u8,3u8,132u8,42u8,209u8, - 143u8,107u8,213u8,109u8,97u8,130u8,107u8,34u8,218u8,114u8,82u8,89u8,199u8,15u8,17u8,246u8,107u8,106u8,184u8,134u8, - 114u8,138u8,147u8,120u8,121u8,200u8,206u8,154u8,124u8,92u8,253u8,42u8,191u8,131u8,192u8,174u8,80u8,205u8,3u8,48u8, - 33u8,70u8,217u8,197u8,0u8,30u8,50u8,65u8,234u8,32u8,75u8,44u8,209u8,161u8,19u8,186u8,48u8,33u8,195u8,125u8, - 9u8,143u8,97u8,158u8,131u8,95u8,176u8,235u8,36u8,20u8,206u8,6u8,187u8,227u8,85u8,49u8,187u8,202u8,77u8,111u8, - 138u8,61u8,234u8,238u8,183u8,2u8,82u8,26u8,213u8,245u8,150u8,123u8,30u8,47u8,250u8,187u8,217u8,244u8,22u8,45u8, - 251u8,93u8,53u8,206u8,62u8,16u8,111u8,233u8,102u8,252u8,136u8,24u8,36u8,111u8,7u8,105u8,178u8,147u8,232u8,11u8, - 133u8,204u8,195u8,76u8,250u8,130u8,163u8,141u8,215u8,205u8,187u8,116u8,117u8,113u8,151u8,254u8,250u8,69u8,75u8,40u8, - 54u8,210u8,221u8,131u8,11u8,197u8,54u8,234u8,186u8,78u8,104u8,2u8,61u8,219u8,157u8,167u8,216u8,14u8,49u8,153u8, - 119u8,112u8,187u8,207u8,189u8,229u8,52u8,117u8,23u8,243u8,234u8,190u8,163u8,119u8,7u8,0u8,71u8,29u8,208u8,111u8, - 143u8,214u8,226u8,234u8,237u8,193u8,5u8,104u8,81u8,171u8,174u8,31u8,142u8,141u8,183u8,31u8,229u8,143u8,27u8,110u8, - 64u8,238u8,135u8,59u8,107u8,135u8,232u8,54u8,13u8,247u8,59u8,211u8,255u8,51u8,25u8,223u8,108u8,50u8,46u8,123u8, - 236u8,225u8,79u8,199u8,117u8,26u8,183u8,197u8,221u8,109u8,180u8,223u8,63u8,69u8,106u8,118u8,84u8,205u8,232u8,180u8, - 207u8,192u8,172u8,29u8,138u8,54u8,58u8,173u8,237u8,149u8,31u8,12u8,163u8,182u8,117u8,233u8,215u8,36u8,216u8,174u8, - 151u8,195u8,127u8,43u8,137u8,118u8,184u8,141u8,220u8,181u8,236u8,126u8,110u8,36u8,247u8,157u8,33u8,109u8,22u8,30u8, - 237u8,194u8,192u8,136u8,119u8,140u8,166u8,78u8,13u8,191u8,138u8,130u8,154u8,209u8,164u8,95u8,211u8,233u8,153u8,45u8, - 250u8,203u8,82u8,183u8,141u8,126u8,50u8,223u8,104u8,111u8,251u8,106u8,30u8,240u8,232u8,155u8,151u8,80u8,63u8,127u8, - 78u8,32u8,153u8,202u8,33u8,5u8,80u8,84u8,32u8,253u8,162u8,167u8,18u8,156u8,14u8,87u8,21u8,8u8,1u8,110u8, - 207u8,46u8,106u8,108u8,19u8,235u8,184u8,87u8,73u8,32u8,149u8,63u8,29u8,114u8,65u8,251u8,226u8,233u8,73u8,229u8, - 74u8,17u8,44u8,30u8,201u8,127u8,11u8,199u8,143u8,105u8,254u8,98u8,164u8,135u8,81u8,253u8,144u8,89u8,61u8,10u8, - 190u8,17u8,149u8,255u8,13u8,181u8,136u8,233u8,54u8,21u8,212u8,158u8,85u8,127u8,142u8,214u8,188u8,213u8,237u8,146u8, - 59u8,171u8,251u8,218u8,40u8,170u8,238u8,152u8,114u8,22u8,213u8,103u8,207u8,53u8,50u8,39u8,15u8,201u8,250u8,225u8, - 241u8,72u8,97u8,55u8,108u8,217u8,96u8,206u8,141u8,234u8,89u8,22u8,229u8,167u8,134u8,29u8,187u8,58u8,252u8,236u8, - 113u8,24u8,139u8,60u8,107u8,59u8,67u8,47u8,129u8,71u8,18u8,223u8,231u8,221u8,150u8,155u8,149u8,47u8,51u8,176u8, - 101u8,111u8,43u8,151u8,40u8,201u8,135u8,245u8,217u8,166u8,158u8,187u8,193u8,44u8,27u8,94u8,140u8,158u8,62u8,203u8, - 114u8,235u8,231u8,207u8,244u8,34u8,146u8,95u8,209u8,217u8,248u8,125u8,95u8,42u8,104u8,207u8,1u8,231u8,65u8,122u8, - 113u8,15u8,203u8,249u8,240u8,226u8,16u8,172u8,127u8,102u8,225u8,56u8,173u8,174u8,13u8,143u8,118u8,63u8,26u8,215u8, - 154u8,183u8,181u8,105u8,98u8,167u8,33u8,92u8,59u8,174u8,184u8,128u8,236u8,131u8,183u8,86u8,248u8,40u8,135u8,84u8, - 40u8,215u8,251u8,26u8,121u8,220u8,148u8,135u8,234u8,32u8,153u8,183u8,79u8,127u8,44u8,136u8,178u8,200u8,220u8,7u8, - 70u8,59u8,248u8,102u8,139u8,217u8,15u8,2u8,152u8,74u8,172u8,125u8,0u8,101u8,16u8,170u8,22u8,74u8,163u8,225u8, - 15u8,1u8,77u8,11u8,111u8,236u8,1u8,174u8,246u8,58u8,178u8,69u8,111u8,120u8,187u8,190u8,96u8,55u8,152u8,163u8, - 10u8,152u8,103u8,212u8,95u8,220u8,128u8,158u8,242u8,54u8,71u8,168u8,158u8,67u8,199u8,232u8,198u8,253u8,70u8,247u8, - 126u8,161u8,145u8,211u8,31u8,32u8,82u8,248u8,172u8,177u8,13u8,101u8,56u8,123u8,121u8,191u8,80u8,105u8,205u8,193u8, - 94u8,145u8,26u8,221u8,14u8,169u8,140u8,108u8,19u8,174u8,208u8,186u8,99u8,140u8,174u8,217u8,66u8,222u8,11u8,96u8, - 79u8,111u8,8u8,152u8,98u8,221u8,60u8,251u8,180u8,94u8,233u8,174u8,65u8,107u8,239u8,151u8,239u8,9u8,165u8,231u8, - 55u8,66u8,169u8,141u8,218u8,235u8,66u8,172u8,249u8,174u8,65u8,193u8,214u8,245u8,238u8,224u8,70u8,51u8,195u8,223u8, - 128u8,227u8,142u8,63u8,4u8,185u8,3u8,136u8,3u8,46u8,168u8,141u8,127u8,237u8,0u8,235u8,35u8,88u8,49u8,135u8, - 250u8,230u8,61u8,28u8,204u8,15u8,75u8,221u8,91u8,246u8,56u8,47u8,26u8,40u8,31u8,157u8,159u8,95u8,70u8,48u8, - 101u8,200u8,47u8,237u8,179u8,203u8,5u8,124u8,134u8,111u8,160u8,57u8,200u8,198u8,203u8,179u8,47u8,229u8,123u8,173u8, - 98u8,96u8,182u8,23u8,144u8,145u8,210u8,147u8,255u8,60u8,178u8,121u8,157u8,71u8,54u8,217u8,130u8,244u8,126u8,162u8, - 23u8,12u8,28u8,177u8,115u8,72u8,238u8,151u8,31u8,100u8,186u8,201u8,211u8,79u8,21u8,220u8,173u8,234u8,199u8,205u8, - 79u8,65u8,237u8,70u8,191u8,186u8,190u8,235u8,211u8,80u8,101u8,156u8,219u8,63u8,109u8,126u8,42u8,106u8,135u8,35u8, - 204u8,197u8,87u8,218u8,160u8,125u8,149u8,231u8,200u8,213u8,163u8,191u8,0u8,58u8,194u8,136u8,228u8,121u8,54u8,0u8, - 0u8,0u8,0u8,5u8,115u8,116u8,97u8,107u8,101u8,188u8,160u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,237u8,125u8,123u8,87u8,27u8,199u8,146u8,248u8,255u8,249u8,20u8,115u8,239u8,158u8,117u8,164u8,68u8,145u8, - 145u8,0u8,39u8,193u8,198u8,103u8,9u8,38u8,9u8,123u8,29u8,227u8,99u8,72u8,124u8,119u8,239u8,201u8,25u8,143u8, - 164u8,1u8,38u8,22u8,26u8,126u8,26u8,9u8,194u8,230u8,250u8,187u8,255u8,170u8,170u8,31u8,211u8,143u8,234u8,153u8, - 209u8,131u8,135u8,19u8,251u8,36u8,54u8,72u8,221u8,213u8,175u8,234u8,170u8,234u8,122u8,62u8,254u8,226u8,139u8,207u8, - 162u8,47u8,162u8,95u8,146u8,113u8,54u8,74u8,102u8,249u8,52u8,26u8,103u8,167u8,233u8,240u8,102u8,56u8,78u8,119u8, - 240u8,243u8,94u8,55u8,122u8,61u8,77u8,47u8,147u8,105u8,26u8,37u8,209u8,149u8,110u8,52u8,201u8,71u8,105u8,84u8, - 164u8,179u8,104u8,126u8,25u8,37u8,147u8,81u8,52u8,76u8,198u8,227u8,168u8,152u8,37u8,239u8,211u8,157u8,157u8,108u8, - 146u8,205u8,50u8,104u8,249u8,127u8,105u8,172u8,59u8,32u8,168u8,126u8,55u8,58u8,154u8,12u8,211u8,104u8,154u8,38u8, - 163u8,155u8,104u8,150u8,71u8,163u8,244u8,50u8,47u8,178u8,153u8,232u8,23u8,181u8,0u8,236u8,121u8,114u8,149u8,70u8, - 167u8,243u8,201u8,168u8,136u8,146u8,162u8,200u8,206u8,38u8,233u8,40u8,26u8,220u8,192u8,200u8,216u8,36u8,155u8,156u8, - 193u8,144u8,211u8,171u8,12u8,96u8,100u8,147u8,40u8,253u8,125u8,120u8,158u8,76u8,206u8,160u8,61u8,116u8,204u8,175u8, - 39u8,233u8,180u8,56u8,207u8,46u8,97u8,38u8,151u8,201u8,32u8,27u8,103u8,179u8,155u8,118u8,7u8,71u8,53u8,103u8, - 150u8,140u8,70u8,113u8,57u8,216u8,23u8,241u8,117u8,54u8,59u8,143u8,161u8,67u8,116u8,5u8,157u8,179u8,124u8,82u8, - 68u8,217u8,41u8,117u8,128u8,97u8,79u8,167u8,249u8,69u8,52u8,59u8,79u8,221u8,161u8,219u8,8u8,116u8,179u8,27u8, - 237u8,27u8,112u8,127u8,203u8,179u8,73u8,185u8,214u8,24u8,247u8,5u8,7u8,240u8,224u8,183u8,113u8,213u8,216u8,152u8, - 0u8,39u8,195u8,89u8,6u8,203u8,45u8,55u8,21u8,250u8,1u8,92u8,90u8,20u8,172u8,31u8,118u8,60u8,61u8,133u8, - 163u8,160u8,70u8,217u8,4u8,135u8,197u8,94u8,147u8,244u8,247u8,89u8,4u8,27u8,55u8,60u8,239u8,226u8,71u8,91u8, - 93u8,117u8,116u8,41u8,29u8,196u8,89u8,2u8,208u8,167u8,233u8,117u8,50u8,29u8,21u8,221u8,232u8,68u8,206u8,63u8, - 141u8,174u8,51u8,152u8,110u8,50u8,159u8,229u8,23u8,201u8,44u8,195u8,37u8,222u8,68u8,131u8,52u8,26u8,231u8,195u8, - 247u8,176u8,86u8,56u8,68u8,220u8,196u8,36u8,58u8,205u8,126u8,135u8,95u8,71u8,243u8,41u8,52u8,202u8,39u8,81u8, - 11u8,87u8,2u8,251u8,127u8,150u8,195u8,2u8,38u8,9u8,28u8,94u8,27u8,7u8,193u8,113u8,109u8,80u8,211u8,116u8, - 146u8,94u8,67u8,207u8,4u8,166u8,246u8,251u8,101u8,38u8,186u8,211u8,252u8,182u8,187u8,209u8,222u8,12u8,58u8,221u8, - 68u8,151u8,176u8,238u8,89u8,7u8,119u8,24u8,151u8,81u8,174u8,58u8,191u8,76u8,167u8,244u8,195u8,117u8,50u8,153u8, - 21u8,184u8,67u8,243u8,75u8,90u8,14u8,54u8,27u8,194u8,161u8,164u8,147u8,98u8,94u8,68u8,239u8,211u8,155u8,8u8, - 17u8,47u8,157u8,93u8,231u8,211u8,247u8,143u8,79u8,231u8,227u8,49u8,33u8,33u8,156u8,233u8,52u8,45u8,138u8,180u8, - 232u8,96u8,135u8,27u8,56u8,190u8,137u8,123u8,236u8,211u8,124u8,6u8,240u8,98u8,13u8,43u8,70u8,88u8,184u8,89u8, - 242u8,123u8,49u8,94u8,44u8,65u8,199u8,240u8,77u8,172u8,192u8,199u8,26u8,124u8,55u8,58u8,206u8,46u8,178u8,113u8, - 50u8,197u8,25u8,14u8,229u8,33u8,193u8,143u8,4u8,131u8,198u8,166u8,97u8,203u8,47u8,172u8,169u8,63u8,14u8,207u8, - 155u8,78u8,58u8,159u8,192u8,46u8,154u8,199u8,205u8,157u8,245u8,147u8,174u8,113u8,77u8,97u8,157u8,176u8,237u8,255u8, - 111u8,158u8,22u8,51u8,218u8,179u8,9u8,158u8,37u8,246u8,202u8,166u8,242u8,200u8,19u8,177u8,239u8,179u8,236u8,34u8, - 237u8,70u8,63u8,230u8,215u8,41u8,28u8,98u8,199u8,106u8,65u8,72u8,65u8,67u8,15u8,210u8,97u8,126u8,129u8,31u8, - 204u8,206u8,71u8,211u8,228u8,58u8,25u8,140u8,225u8,151u8,243u8,84u8,97u8,29u8,244u8,24u8,206u8,167u8,112u8,202u8, - 51u8,66u8,25u8,192u8,23u8,58u8,230u8,148u8,80u8,44u8,43u8,104u8,50u8,3u8,26u8,241u8,34u8,135u8,249u8,36u8, - 5u8,52u8,131u8,43u8,3u8,255u8,226u8,50u8,4u8,86u8,201u8,142u8,10u8,185u8,104u8,69u8,95u8,3u8,118u8,156u8, - 206u8,210u8,41u8,128u8,3u8,154u8,49u8,57u8,235u8,56u8,184u8,129u8,112u8,83u8,152u8,19u8,181u8,184u8,28u8,103u8, - 195u8,108u8,6u8,147u8,29u8,167u8,72u8,38u8,236u8,150u8,18u8,83u8,241u8,216u8,233u8,178u8,138u8,147u8,165u8,150u8, - 246u8,205u8,196u8,97u8,161u8,189u8,64u8,67u8,189u8,19u8,163u8,105u8,126u8,89u8,192u8,18u8,198u8,249u8,53u8,1u8, - 190u8,200u8,196u8,238u8,194u8,26u8,71u8,18u8,177u8,174u8,243u8,249u8,24u8,238u8,23u8,140u8,51u8,77u8,47u8,224u8, - 66u8,16u8,182u8,99u8,219u8,20u8,48u8,41u8,23u8,88u8,93u8,158u8,213u8,55u8,238u8,89u8,37u8,227u8,235u8,228u8, - 166u8,128u8,190u8,250u8,254u8,123u8,147u8,63u8,203u8,113u8,234u8,179u8,243u8,105u8,62u8,63u8,59u8,135u8,137u8,165u8, - 48u8,165u8,254u8,87u8,155u8,81u8,130u8,151u8,154u8,160u8,126u8,11u8,251u8,53u8,17u8,196u8,206u8,4u8,90u8,192u8, - 185u8,13u8,207u8,245u8,77u8,42u8,152u8,157u8,128u8,33u8,98u8,245u8,61u8,129u8,234u8,109u8,84u8,194u8,26u8,165u8, - 72u8,126u8,225u8,90u8,140u8,162u8,171u8,28u8,79u8,136u8,7u8,89u8,54u8,139u8,169u8,89u8,247u8,179u8,47u8,30u8, - 127u8,118u8,145u8,143u8,230u8,128u8,63u8,201u8,229u8,44u8,47u8,226u8,211u8,105u8,114u8,145u8,34u8,234u8,67u8,7u8, - 218u8,234u8,63u8,62u8,139u8,224u8,207u8,188u8,64u8,202u8,52u8,218u8,217u8,73u8,167u8,211u8,124u8,250u8,212u8,254u8, - 236u8,52u8,77u8,102u8,115u8,192u8,46u8,231u8,227u8,252u8,18u8,17u8,103u8,103u8,231u8,143u8,227u8,116u8,124u8,218u8, - 137u8,142u8,232u8,215u8,15u8,78u8,35u8,226u8,26u8,46u8,192u8,43u8,184u8,87u8,230u8,40u8,98u8,106u8,244u8,213u8, - 96u8,92u8,244u8,250u8,155u8,223u8,244u8,216u8,47u8,129u8,194u8,157u8,63u8,217u8,130u8,127u8,179u8,9u8,251u8,253u8, - 12u8,47u8,138u8,158u8,208u8,9u8,254u8,246u8,193u8,109u8,104u8,108u8,128u8,248u8,96u8,8u8,199u8,188u8,179u8,179u8, - 135u8,63u8,239u8,231u8,62u8,96u8,179u8,253u8,112u8,152u8,207u8,39u8,179u8,138u8,22u8,2u8,150u8,28u8,31u8,161u8, - 117u8,162u8,159u8,128u8,204u8,238u8,107u8,38u8,88u8,53u8,29u8,32u8,8u8,147u8,153u8,238u8,125u8,128u8,191u8,253u8, - 8u8,196u8,175u8,122u8,13u8,72u8,78u8,224u8,32u8,47u8,46u8,43u8,218u8,20u8,55u8,128u8,188u8,23u8,37u8,237u8, - 172u8,106u8,42u8,184u8,43u8,18u8,231u8,211u8,236u8,76u8,79u8,230u8,88u8,124u8,188u8,79u8,159u8,86u8,77u8,7u8, - 8u8,46u8,240u8,94u8,128u8,50u8,155u8,195u8,48u8,212u8,238u8,116u8,154u8,225u8,149u8,244u8,154u8,14u8,144u8,2u8, - 61u8,173u8,108u8,114u8,6u8,124u8,172u8,200u8,138u8,234u8,70u8,211u8,84u8,76u8,86u8,18u8,178u8,234u8,198u8,179u8, - 105u8,50u8,41u8,144u8,211u8,231u8,147u8,248u8,52u8,77u8,229u8,20u8,31u8,63u8,126u8,108u8,144u8,7u8,177u8,74u8, - 144u8,169u8,102u8,209u8,229u8,124u8,48u8,206u8,138u8,243u8,116u8,212u8,165u8,118u8,200u8,67u8,102u8,209u8,193u8,47u8, - 123u8,47u8,15u8,95u8,236u8,157u8,28u8,189u8,137u8,247u8,143u8,94u8,125u8,127u8,248u8,195u8,78u8,52u8,127u8,178u8, - 21u8,237u8,70u8,18u8,105u8,17u8,218u8,43u8,232u8,156u8,78u8,36u8,225u8,192u8,107u8,166u8,36u8,13u8,91u8,188u8, - 48u8,161u8,30u8,159u8,236u8,253u8,227u8,32u8,62u8,57u8,58u8,138u8,95u8,30u8,189u8,85u8,32u8,251u8,37u8,200u8, - 147u8,60u8,143u8,46u8,230u8,195u8,101u8,0u8,254u8,120u8,248u8,195u8,143u8,10u8,226u8,102u8,9u8,113u8,79u8,224u8, - 115u8,4u8,28u8,35u8,25u8,11u8,49u8,208u8,20u8,42u8,225u8,191u8,75u8,216u8,68u8,36u8,49u8,250u8,67u8,107u8, - 128u8,189u8,151u8,111u8,14u8,246u8,94u8,252u8,79u8,188u8,183u8,127u8,114u8,248u8,203u8,65u8,172u8,183u8,69u8,13u8, - 181u8,197u8,14u8,133u8,251u8,154u8,4u8,32u8,190u8,58u8,58u8,241u8,193u8,108u8,151u8,96u8,246u8,147u8,201u8,231u8, - 138u8,224u8,71u8,227u8,4u8,186u8,240u8,112u8,94u8,238u8,29u8,51u8,128u8,158u8,152u8,155u8,57u8,75u8,164u8,64u8, - 130u8,146u8,107u8,154u8,130u8,136u8,123u8,145u8,252u8,158u8,93u8,204u8,47u8,96u8,47u8,128u8,237u8,56u8,71u8,46u8, - 246u8,242u8,224u8,159u8,251u8,7u8,7u8,47u8,142u8,227u8,159u8,246u8,254u8,169u8,96u8,126u8,93u8,185u8,157u8,211u8, - 244u8,44u8,131u8,187u8,55u8,69u8,238u8,84u8,88u8,155u8,11u8,36u8,126u8,68u8,98u8,34u8,187u8,163u8,111u8,14u8, - 126u8,56u8,60u8,62u8,57u8,120u8,115u8,240u8,66u8,141u8,243u8,141u8,63u8,206u8,40u8,79u8,197u8,110u8,158u8,43u8, - 254u8,59u8,205u8,206u8,206u8,103u8,165u8,4u8,87u8,202u8,222u8,222u8,38u8,31u8,189u8,62u8,120u8,99u8,110u8,205u8, - 183u8,79u8,253u8,139u8,64u8,114u8,4u8,194u8,39u8,60u8,195u8,167u8,8u8,49u8,250u8,75u8,20u8,41u8,228u8,205u8, - 4u8,105u8,5u8,6u8,134u8,127u8,103u8,40u8,247u8,72u8,161u8,202u8,25u8,44u8,126u8,125u8,4u8,135u8,241u8,195u8, - 193u8,171u8,131u8,227u8,195u8,227u8,242u8,80u8,226u8,227u8,131u8,147u8,120u8,255u8,199u8,189u8,87u8,63u8,28u8,196u8, - 123u8,47u8,1u8,229u8,203u8,181u8,246u8,54u8,202u8,217u8,28u8,78u8,104u8,207u8,12u8,241u8,141u8,238u8,228u8,16u8, - 165u8,56u8,115u8,156u8,195u8,87u8,4u8,57u8,126u8,253u8,243u8,119u8,47u8,15u8,247u8,227u8,127u8,28u8,252u8,143u8, - 134u8,214u8,99u8,214u8,70u8,60u8,94u8,29u8,60u8,110u8,221u8,24u8,164u8,201u8,25u8,127u8,201u8,113u8,162u8,116u8, - 45u8,247u8,222u8,252u8,112u8,160u8,161u8,26u8,55u8,243u8,151u8,28u8,133u8,37u8,216u8,152u8,107u8,96u8,204u8,217u8, - 100u8,8u8,103u8,15u8,180u8,241u8,28u8,78u8,92u8,140u8,0u8,167u8,175u8,135u8,32u8,201u8,158u8,182u8,76u8,73u8, - 112u8,74u8,72u8,49u8,134u8,62u8,58u8,57u8,124u8,245u8,3u8,236u8,219u8,219u8,131u8,55u8,241u8,225u8,171u8,125u8, - 192u8,137u8,227u8,18u8,249u8,94u8,30u8,254u8,116u8,120u8,162u8,167u8,97u8,92u8,231u8,99u8,194u8,230u8,203u8,60u8, - 31u8,151u8,168u8,1u8,130u8,28u8,10u8,128u8,66u8,50u8,186u8,156u8,230u8,87u8,25u8,206u8,134u8,218u8,72u8,118u8, - 192u8,160u8,249u8,235u8,163u8,163u8,151u8,241u8,139u8,163u8,131u8,227u8,24u8,81u8,229u8,224u8,159u8,128u8,138u8,122u8, - 60u8,227u8,78u8,31u8,73u8,89u8,69u8,33u8,89u8,221u8,168u8,146u8,125u8,90u8,3u8,30u8,189u8,125u8,5u8,107u8, - 220u8,223u8,123u8,77u8,67u8,125u8,127u8,244u8,243u8,171u8,18u8,11u8,140u8,123u8,15u8,146u8,145u8,236u8,173u8,112u8, - 18u8,4u8,37u8,16u8,110u8,167u8,136u8,247u8,32u8,44u8,229u8,147u8,84u8,139u8,78u8,44u8,206u8,151u8,227u8,168u8, - 43u8,70u8,203u8,58u8,214u8,131u8,61u8,225u8,144u8,68u8,82u8,171u8,81u8,122u8,154u8,225u8,211u8,87u8,10u8,138u8, - 123u8,251u8,47u8,81u8,192u8,132u8,179u8,3u8,33u8,25u8,31u8,12u8,130u8,94u8,32u8,69u8,30u8,24u8,98u8,100u8, - 97u8,163u8,231u8,193u8,203u8,195u8,31u8,14u8,191u8,123u8,201u8,144u8,201u8,222u8,215u8,22u8,129u8,195u8,17u8,229u8, - 147u8,171u8,208u8,39u8,250u8,121u8,161u8,4u8,118u8,24u8,37u8,77u8,166u8,227u8,44u8,157u8,138u8,165u8,219u8,15u8, - 129u8,46u8,119u8,39u8,94u8,30u8,237u8,255u8,227u8,231u8,215u8,122u8,56u8,131u8,148u8,144u8,136u8,36u8,158u8,77u8, - 184u8,151u8,195u8,28u8,222u8,218u8,67u8,20u8,50u8,13u8,62u8,25u8,1u8,159u8,44u8,8u8,109u8,211u8,4u8,184u8, - 79u8,73u8,194u8,20u8,145u8,163u8,211u8,182u8,241u8,232u8,251u8,3u8,64u8,158u8,147u8,61u8,92u8,110u8,96u8,191u8, - 191u8,101u8,89u8,175u8,16u8,28u8,96u8,107u8,231u8,23u8,221u8,232u8,109u8,74u8,130u8,176u8,148u8,128u8,97u8,146u8, - 128u8,73u8,151u8,248u8,244u8,128u8,111u8,129u8,248u8,163u8,20u8,156u8,163u8,2u8,227u8,39u8,100u8,6u8,197u8,252u8, - 242u8,50u8,159u8,194u8,131u8,53u8,179u8,16u8,204u8,184u8,197u8,39u8,123u8,39u8,63u8,31u8,199u8,175u8,15u8,94u8, - 189u8,192u8,203u8,37u8,120u8,150u8,195u8,185u8,3u8,125u8,236u8,182u8,253u8,167u8,77u8,224u8,31u8,190u8,178u8,123u8, - 109u8,86u8,246u8,114u8,91u8,111u8,25u8,187u8,243u8,146u8,200u8,6u8,189u8,130u8,36u8,131u8,42u8,64u8,151u8,67u8, - 143u8,204u8,222u8,19u8,148u8,137u8,127u8,135u8,87u8,252u8,236u8,115u8,65u8,198u8,52u8,54u8,80u8,39u8,249u8,12u8, - 26u8,100u8,51u8,144u8,184u8,53u8,192u8,243u8,217u8,236u8,178u8,216u8,121u8,252u8,248u8,12u8,94u8,114u8,243u8,65u8, - 23u8,94u8,154u8,143u8,73u8,78u8,250u8,106u8,156u8,12u8,10u8,249u8,227u8,16u8,208u8,225u8,49u8,72u8,104u8,131u8, - 199u8,23u8,32u8,208u8,61u8,30u8,2u8,67u8,73u8,213u8,119u8,2u8,220u8,227u8,98u8,58u8,124u8,60u8,206u8,6u8, - 221u8,105u8,241u8,31u8,47u8,251u8,27u8,198u8,234u8,128u8,59u8,58u8,100u8,254u8,248u8,240u8,127u8,245u8,210u8,158u8, - 108u8,111u8,111u8,62u8,169u8,92u8,30u8,96u8,216u8,60u8,197u8,185u8,191u8,147u8,138u8,147u8,24u8,71u8,127u8,23u8, - 17u8,19u8,26u8,165u8,244u8,226u8,79u8,174u8,114u8,96u8,10u8,248u8,158u8,78u8,166u8,176u8,140u8,139u8,20u8,84u8, - 31u8,17u8,170u8,69u8,78u8,225u8,46u8,118u8,157u8,201u8,188u8,57u8,120u8,187u8,247u8,6u8,40u8,39u8,112u8,188u8, - 242u8,196u8,55u8,232u8,143u8,49u8,145u8,82u8,72u8,199u8,139u8,133u8,82u8,198u8,37u8,208u8,199u8,20u8,149u8,32u8, - 165u8,58u8,75u8,40u8,215u8,232u8,117u8,13u8,242u8,239u8,72u8,42u8,22u8,102u8,83u8,160u8,167u8,246u8,27u8,18u8, - 27u8,146u8,86u8,169u8,40u8,242u8,97u8,70u8,15u8,183u8,242u8,54u8,119u8,245u8,160u8,63u8,38u8,87u8,226u8,145u8, - 153u8,225u8,115u8,23u8,184u8,18u8,168u8,246u8,240u8,230u8,151u8,234u8,46u8,122u8,63u8,73u8,174u8,145u8,106u8,42u8, - 72u8,51u8,180u8,71u8,132u8,201u8,230u8,243u8,233u8,80u8,106u8,48u8,206u8,129u8,75u8,139u8,55u8,49u8,145u8,167u8, - 66u8,143u8,40u8,94u8,131u8,164u8,18u8,33u8,185u8,65u8,77u8,31u8,55u8,207u8,151u8,203u8,138u8,217u8,116u8,62u8, - 156u8,9u8,82u8,111u8,108u8,16u8,242u8,54u8,96u8,192u8,29u8,73u8,55u8,196u8,27u8,18u8,255u8,224u8,234u8,212u8, - 67u8,99u8,71u8,177u8,152u8,14u8,125u8,253u8,161u8,220u8,234u8,3u8,155u8,144u8,156u8,147u8,108u8,164u8,215u8,78u8, - 124u8,236u8,53u8,178u8,40u8,181u8,36u8,189u8,237u8,146u8,151u8,72u8,13u8,100u8,90u8,238u8,163u8,82u8,211u8,1u8, - 142u8,136u8,109u8,38u8,5u8,218u8,196u8,83u8,234u8,237u8,232u8,30u8,160u8,73u8,61u8,60u8,117u8,68u8,178u8,241u8, - 184u8,136u8,180u8,82u8,178u8,35u8,117u8,62u8,215u8,160u8,226u8,128u8,15u8,245u8,1u8,194u8,73u8,9u8,125u8,3u8, - 82u8,36u8,33u8,31u8,199u8,98u8,152u8,114u8,58u8,125u8,2u8,238u8,130u8,22u8,74u8,33u8,91u8,231u8,195u8,65u8, - 203u8,38u8,12u8,188u8,183u8,160u8,254u8,113u8,180u8,80u8,8u8,2u8,72u8,94u8,71u8,232u8,243u8,156u8,206u8,12u8, - 124u8,253u8,149u8,129u8,197u8,74u8,197u8,52u8,41u8,7u8,131u8,63u8,123u8,6u8,192u8,32u8,56u8,3u8,24u8,236u8, - 15u8,97u8,148u8,133u8,145u8,64u8,146u8,174u8,12u8,201u8,72u8,15u8,80u8,123u8,104u8,122u8,158u8,183u8,114u8,108u8, - 35u8,80u8,34u8,13u8,81u8,111u8,165u8,151u8,176u8,210u8,185u8,153u8,224u8,252u8,131u8,219u8,172u8,62u8,56u8,95u8, - 189u8,54u8,144u8,138u8,104u8,162u8,24u8,90u8,43u8,230u8,159u8,129u8,146u8,124u8,164u8,118u8,12u8,9u8,167u8,117u8, - 99u8,203u8,75u8,36u8,239u8,170u8,113u8,73u8,97u8,94u8,38u8,64u8,253u8,185u8,248u8,112u8,135u8,20u8,23u8,207u8, - 180u8,66u8,228u8,121u8,199u8,236u8,104u8,163u8,87u8,199u8,67u8,35u8,221u8,86u8,53u8,172u8,6u8,167u8,30u8,152u8, - 114u8,205u8,36u8,110u8,144u8,90u8,89u8,109u8,85u8,73u8,89u8,44u8,116u8,108u8,6u8,117u8,148u8,54u8,135u8,219u8, - 96u8,190u8,66u8,61u8,31u8,3u8,253u8,205u8,198u8,160u8,187u8,28u8,22u8,196u8,76u8,172u8,145u8,79u8,166u8,137u8, - 80u8,251u8,106u8,78u8,172u8,95u8,99u8,185u8,171u8,105u8,71u8,189u8,115u8,215u8,234u8,124u8,158u8,73u8,105u8,82u8, - 48u8,115u8,221u8,85u8,72u8,150u8,163u8,12u8,20u8,210u8,4u8,83u8,115u8,135u8,28u8,94u8,124u8,48u8,241u8,177u8, - 230u8,11u8,116u8,25u8,17u8,2u8,45u8,87u8,146u8,84u8,92u8,125u8,126u8,106u8,142u8,196u8,169u8,252u8,201u8,224u8, - 130u8,253u8,53u8,183u8,179u8,38u8,119u8,132u8,122u8,105u8,147u8,5u8,157u8,231u8,99u8,218u8,100u8,151u8,55u8,200u8, - 117u8,42u8,59u8,13u8,189u8,53u8,16u8,75u8,180u8,25u8,33u8,43u8,74u8,192u8,106u8,137u8,12u8,207u8,168u8,222u8, - 86u8,84u8,111u8,194u8,249u8,142u8,211u8,51u8,115u8,115u8,205u8,65u8,239u8,96u8,246u8,114u8,124u8,165u8,109u8,13u8, - 77u8,30u8,53u8,209u8,87u8,36u8,72u8,164u8,32u8,235u8,224u8,197u8,86u8,236u8,28u8,223u8,16u8,83u8,131u8,227u8, - 161u8,152u8,175u8,236u8,125u8,93u8,227u8,42u8,249u8,22u8,188u8,88u8,64u8,220u8,49u8,245u8,132u8,207u8,222u8,72u8, - 117u8,131u8,22u8,168u8,247u8,149u8,150u8,129u8,90u8,25u8,168u8,108u8,234u8,159u8,89u8,80u8,199u8,233u8,236u8,72u8, - 126u8,239u8,246u8,213u8,164u8,150u8,237u8,184u8,55u8,26u8,209u8,114u8,220u8,94u8,83u8,117u8,23u8,211u8,138u8,206u8, - 111u8,116u8,35u8,30u8,6u8,99u8,57u8,226u8,225u8,80u8,195u8,125u8,213u8,238u8,31u8,233u8,141u8,11u8,170u8,145u8, - 145u8,137u8,133u8,253u8,51u8,245u8,124u8,37u8,58u8,238u8,77u8,70u8,223u8,203u8,110u8,123u8,170u8,151u8,59u8,148u8, - 210u8,8u8,196u8,226u8,137u8,198u8,2u8,61u8,148u8,109u8,94u8,82u8,19u8,23u8,130u8,111u8,208u8,100u8,129u8,252u8, - 55u8,52u8,211u8,71u8,15u8,7u8,232u8,130u8,25u8,1u8,114u8,76u8,179u8,193u8,28u8,214u8,173u8,36u8,107u8,14u8, - 204u8,11u8,221u8,236u8,141u8,104u8,229u8,237u8,29u8,241u8,195u8,138u8,99u8,252u8,153u8,26u8,176u8,71u8,168u8,24u8, - 69u8,69u8,239u8,183u8,178u8,9u8,219u8,159u8,49u8,33u8,177u8,64u8,94u8,98u8,187u8,208u8,102u8,124u8,224u8,222u8, - 158u8,217u8,228u8,84u8,190u8,131u8,233u8,165u8,111u8,136u8,242u8,166u8,162u8,68u8,178u8,215u8,242u8,134u8,9u8,101u8, - 177u8,22u8,136u8,135u8,249u8,165u8,18u8,139u8,59u8,100u8,198u8,50u8,248u8,110u8,137u8,183u8,160u8,195u8,130u8,214u8, - 59u8,145u8,48u8,132u8,60u8,155u8,127u8,99u8,172u8,80u8,163u8,164u8,194u8,39u8,190u8,25u8,82u8,113u8,80u8,8u8, - 147u8,64u8,0u8,90u8,145u8,252u8,226u8,18u8,72u8,56u8,217u8,9u8,97u8,251u8,64u8,80u8,78u8,175u8,178u8,28u8, - 158u8,210u8,164u8,187u8,200u8,144u8,184u8,119u8,74u8,189u8,41u8,144u8,8u8,13u8,199u8,199u8,250u8,224u8,120u8,135u8, - 147u8,81u8,250u8,123u8,100u8,155u8,203u8,81u8,143u8,230u8,217u8,144u8,225u8,245u8,8u8,160u8,46u8,115u8,193u8,133u8, - 73u8,46u8,4u8,206u8,86u8,190u8,127u8,72u8,69u8,106u8,72u8,74u8,248u8,167u8,60u8,210u8,12u8,135u8,49u8,88u8, - 171u8,113u8,90u8,250u8,70u8,211u8,105u8,77u8,47u8,4u8,111u8,67u8,165u8,128u8,238u8,222u8,49u8,206u8,208u8,60u8, - 127u8,254u8,248u8,14u8,241u8,208u8,241u8,240u8,170u8,206u8,13u8,119u8,198u8,121u8,204u8,208u8,140u8,73u8,184u8,141u8, - 73u8,184u8,117u8,36u8,1u8,105u8,62u8,113u8,209u8,196u8,91u8,15u8,210u8,14u8,107u8,150u8,230u8,236u8,255u8,203u8, - 177u8,94u8,116u8,77u8,33u8,152u8,241u8,113u8,80u8,66u8,184u8,35u8,184u8,131u8,77u8,122u8,110u8,139u8,184u8,250u8, - 14u8,141u8,50u8,221u8,25u8,49u8,163u8,16u8,130u8,133u8,236u8,199u8,60u8,72u8,92u8,88u8,32u8,221u8,130u8,29u8, - 5u8,100u8,237u8,88u8,136u8,181u8,240u8,60u8,27u8,10u8,35u8,58u8,160u8,176u8,150u8,194u8,168u8,147u8,16u8,45u8, - 166u8,233u8,41u8,236u8,225u8,185u8,179u8,255u8,165u8,56u8,131u8,204u8,248u8,243u8,162u8,250u8,198u8,193u8,46u8,49u8, - 50u8,109u8,121u8,183u8,138u8,225u8,121u8,122u8,1u8,98u8,220u8,252u8,27u8,11u8,121u8,247u8,156u8,39u8,69u8,161u8, - 153u8,48u8,163u8,136u8,45u8,165u8,225u8,114u8,143u8,203u8,123u8,97u8,205u8,222u8,190u8,34u8,175u8,93u8,131u8,9u8, - 157u8,136u8,80u8,156u8,195u8,145u8,26u8,79u8,128u8,86u8,49u8,35u8,31u8,16u8,26u8,165u8,221u8,173u8,144u8,71u8, - 151u8,29u8,149u8,212u8,246u8,214u8,160u8,221u8,160u8,52u8,93u8,63u8,198u8,190u8,220u8,165u8,25u8,89u8,78u8,252u8, - 135u8,29u8,254u8,161u8,239u8,98u8,231u8,94u8,244u8,250u8,246u8,65u8,156u8,120u8,0u8,162u8,235u8,132u8,220u8,13u8, - 204u8,73u8,123u8,254u8,22u8,214u8,16u8,216u8,138u8,29u8,195u8,184u8,91u8,90u8,130u8,47u8,117u8,194u8,25u8,186u8, - 165u8,32u8,182u8,143u8,230u8,83u8,28u8,79u8,89u8,49u8,132u8,243u8,137u8,190u8,119u8,251u8,240u8,211u8,27u8,173u8, - 122u8,48u8,181u8,214u8,143u8,25u8,41u8,93u8,168u8,221u8,165u8,145u8,29u8,73u8,50u8,24u8,124u8,149u8,203u8,143u8, - 246u8,72u8,153u8,218u8,200u8,172u8,231u8,182u8,111u8,76u8,141u8,193u8,106u8,132u8,133u8,30u8,75u8,59u8,142u8,25u8, - 217u8,123u8,158u8,200u8,117u8,75u8,240u8,64u8,168u8,51u8,208u8,147u8,204u8,147u8,177u8,62u8,208u8,215u8,160u8,26u8, - 67u8,130u8,137u8,10u8,83u8,28u8,135u8,167u8,116u8,197u8,124u8,136u8,23u8,24u8,120u8,66u8,140u8,202u8,214u8,188u8, - 72u8,198u8,238u8,35u8,231u8,52u8,201u8,198u8,32u8,240u8,114u8,95u8,219u8,147u8,8u8,14u8,109u8,47u8,145u8,185u8, - 95u8,213u8,211u8,103u8,87u8,92u8,35u8,249u8,210u8,184u8,184u8,214u8,101u8,52u8,86u8,234u8,69u8,237u8,72u8,196u8, - 203u8,193u8,164u8,71u8,207u8,120u8,164u8,133u8,111u8,230u8,107u8,164u8,167u8,129u8,175u8,237u8,25u8,89u8,162u8,246u8, - 242u8,211u8,73u8,46u8,16u8,193u8,99u8,82u8,150u8,4u8,79u8,147u8,147u8,204u8,87u8,29u8,50u8,60u8,24u8,47u8, - 190u8,175u8,182u8,227u8,77u8,165u8,175u8,235u8,6u8,45u8,237u8,233u8,54u8,123u8,17u8,172u8,54u8,251u8,134u8,82u8, - 33u8,78u8,191u8,97u8,83u8,132u8,218u8,84u8,248u8,67u8,176u8,77u8,218u8,186u8,100u8,200u8,123u8,212u8,172u8,182u8, - 9u8,117u8,10u8,24u8,156u8,102u8,85u8,27u8,123u8,122u8,236u8,115u8,105u8,29u8,148u8,130u8,127u8,64u8,45u8,191u8, - 116u8,245u8,88u8,171u8,185u8,53u8,238u8,171u8,107u8,101u8,138u8,32u8,222u8,121u8,21u8,68u8,193u8,127u8,169u8,173u8, - 60u8,166u8,86u8,36u8,6u8,7u8,229u8,95u8,118u8,43u8,157u8,155u8,176u8,158u8,67u8,175u8,194u8,55u8,124u8,106u8, - 199u8,103u8,96u8,229u8,37u8,175u8,234u8,70u8,123u8,32u8,188u8,137u8,6u8,211u8,212u8,120u8,89u8,59u8,237u8,52u8, - 248u8,176u8,115u8,34u8,195u8,49u8,191u8,79u8,89u8,105u8,0u8,135u8,139u8,133u8,143u8,155u8,176u8,220u8,62u8,83u8, - 139u8,113u8,117u8,150u8,254u8,59u8,247u8,80u8,235u8,144u8,132u8,212u8,162u8,77u8,43u8,184u8,91u8,40u8,11u8,153u8, - 15u8,170u8,100u8,144u8,207u8,103u8,85u8,214u8,96u8,235u8,197u8,85u8,138u8,70u8,63u8,23u8,194u8,65u8,252u8,157u8, - 227u8,102u8,213u8,197u8,215u8,197u8,59u8,161u8,33u8,87u8,211u8,64u8,64u8,106u8,8u8,26u8,115u8,50u8,42u8,183u8, - 81u8,56u8,164u8,226u8,217u8,145u8,179u8,71u8,75u8,184u8,115u8,181u8,209u8,23u8,157u8,215u8,134u8,225u8,172u8,90u8, - 174u8,183u8,87u8,244u8,72u8,88u8,207u8,218u8,166u8,132u8,227u8,248u8,194u8,237u8,236u8,192u8,1u8,167u8,83u8,96u8, - 127u8,118u8,103u8,23u8,88u8,251u8,105u8,137u8,169u8,212u8,225u8,111u8,45u8,253u8,1u8,254u8,249u8,155u8,176u8,130u8, - 63u8,179u8,206u8,240u8,121u8,203u8,125u8,195u8,181u8,59u8,86u8,47u8,242u8,182u8,132u8,41u8,8u8,75u8,122u8,44u8, - 96u8,180u8,194u8,230u8,243u8,182u8,238u8,109u8,204u8,7u8,55u8,55u8,158u8,229u8,238u8,140u8,59u8,14u8,62u8,253u8, - 97u8,225u8,143u8,116u8,149u8,4u8,138u8,217u8,106u8,71u8,31u8,36u8,52u8,230u8,58u8,0u8,170u8,56u8,167u8,111u8, - 226u8,133u8,176u8,248u8,20u8,151u8,233u8,48u8,59u8,205u8,208u8,55u8,149u8,87u8,154u8,48u8,167u8,136u8,74u8,68u8, - 7u8,75u8,90u8,229u8,113u8,218u8,47u8,110u8,28u8,213u8,83u8,203u8,183u8,65u8,50u8,39u8,135u8,224u8,194u8,93u8, - 167u8,161u8,43u8,154u8,25u8,107u8,6u8,163u8,239u8,163u8,11u8,64u8,235u8,1u8,40u8,39u8,242u8,235u8,248u8,12u8, - 140u8,219u8,240u8,146u8,128u8,79u8,106u8,207u8,172u8,91u8,2u8,41u8,119u8,29u8,148u8,30u8,45u8,185u8,137u8,104u8, - 66u8,5u8,43u8,121u8,209u8,42u8,219u8,117u8,34u8,123u8,49u8,109u8,19u8,7u8,213u8,220u8,244u8,70u8,226u8,234u8, - 97u8,122u8,18u8,156u8,156u8,32u8,204u8,172u8,10u8,224u8,83u8,11u8,156u8,112u8,60u8,189u8,72u8,167u8,103u8,105u8, - 203u8,2u8,75u8,155u8,103u8,52u8,254u8,16u8,165u8,227u8,34u8,117u8,38u8,35u8,7u8,6u8,184u8,21u8,35u8,122u8, - 144u8,76u8,148u8,249u8,143u8,127u8,93u8,101u8,233u8,245u8,175u8,26u8,125u8,222u8,164u8,179u8,249u8,84u8,188u8,226u8, - 76u8,15u8,117u8,101u8,139u8,208u8,58u8,118u8,169u8,15u8,2u8,234u8,248u8,206u8,36u8,211u8,239u8,156u8,231u8,22u8, - 121u8,198u8,163u8,39u8,246u8,53u8,218u8,7u8,233u8,214u8,72u8,173u8,211u8,52u8,253u8,28u8,157u8,116u8,26u8,0u8, - 147u8,126u8,99u8,136u8,124u8,103u8,160u8,52u8,148u8,202u8,88u8,148u8,16u8,90u8,44u8,135u8,104u8,11u8,87u8,1u8, - 141u8,101u8,165u8,69u8,237u8,15u8,135u8,20u8,72u8,93u8,38u8,65u8,145u8,87u8,216u8,132u8,104u8,236u8,153u8,133u8, - 123u8,207u8,52u8,196u8,231u8,118u8,251u8,174u8,39u8,195u8,52u8,220u8,106u8,80u8,240u8,37u8,244u8,48u8,86u8,155u8, - 254u8,32u8,118u8,90u8,207u8,234u8,126u8,246u8,28u8,111u8,154u8,28u8,24u8,157u8,165u8,225u8,158u8,45u8,123u8,10u8, - 246u8,229u8,55u8,97u8,62u8,219u8,141u8,180u8,35u8,54u8,16u8,85u8,0u8,94u8,160u8,87u8,242u8,168u8,104u8,121u8, - 23u8,127u8,163u8,250u8,42u8,154u8,80u8,191u8,10u8,2u8,93u8,248u8,18u8,150u8,182u8,67u8,25u8,140u8,66u8,210u8, - 150u8,208u8,129u8,217u8,71u8,24u8,181u8,174u8,207u8,83u8,10u8,241u8,176u8,117u8,186u8,90u8,101u8,75u8,97u8,63u8, - 249u8,172u8,109u8,34u8,14u8,162u8,30u8,142u8,133u8,30u8,39u8,18u8,50u8,138u8,69u8,8u8,189u8,37u8,122u8,117u8, - 180u8,65u8,184u8,227u8,40u8,159u8,58u8,158u8,202u8,171u8,45u8,39u8,137u8,58u8,100u8,17u8,129u8,51u8,190u8,97u8, - 49u8,139u8,154u8,5u8,49u8,169u8,133u8,2u8,100u8,100u8,253u8,213u8,94u8,63u8,106u8,149u8,141u8,27u8,99u8,86u8, - 9u8,160u8,197u8,16u8,113u8,114u8,127u8,106u8,61u8,42u8,225u8,118u8,229u8,174u8,116u8,26u8,53u8,214u8,155u8,216u8, - 172u8,185u8,125u8,22u8,11u8,118u8,98u8,198u8,106u8,215u8,35u8,100u8,225u8,185u8,137u8,160u8,219u8,95u8,202u8,158u8, - 176u8,161u8,225u8,198u8,54u8,205u8,168u8,134u8,165u8,42u8,182u8,197u8,1u8,91u8,99u8,238u8,30u8,152u8,217u8,145u8, - 145u8,3u8,236u8,251u8,175u8,194u8,95u8,50u8,80u8,57u8,231u8,23u8,176u8,65u8,96u8,104u8,25u8,149u8,19u8,110u8, - 61u8,178u8,198u8,234u8,122u8,72u8,111u8,98u8,132u8,71u8,37u8,106u8,252u8,22u8,93u8,26u8,178u8,196u8,124u8,60u8, - 245u8,246u8,194u8,83u8,90u8,219u8,84u8,92u8,116u8,90u8,122u8,115u8,148u8,27u8,101u8,53u8,137u8,13u8,122u8,95u8, - 46u8,76u8,85u8,45u8,37u8,182u8,231u8,223u8,33u8,181u8,216u8,140u8,121u8,65u8,115u8,219u8,76u8,220u8,133u8,2u8, - 48u8,76u8,69u8,231u8,153u8,247u8,66u8,232u8,218u8,13u8,63u8,43u8,222u8,201u8,139u8,189u8,57u8,114u8,88u8,97u8, - 153u8,177u8,116u8,242u8,11u8,178u8,222u8,78u8,232u8,62u8,45u8,67u8,45u8,157u8,251u8,12u8,55u8,176u8,238u8,150u8, - 27u8,0u8,96u8,215u8,190u8,203u8,193u8,184u8,233u8,111u8,8u8,227u8,50u8,38u8,162u8,32u8,133u8,145u8,133u8,156u8, - 69u8,194u8,199u8,161u8,110u8,180u8,55u8,185u8,221u8,16u8,210u8,71u8,255u8,254u8,119u8,212u8,164u8,181u8,139u8,152u8, - 220u8,43u8,64u8,94u8,68u8,193u8,242u8,118u8,109u8,154u8,219u8,80u8,82u8,145u8,84u8,248u8,169u8,7u8,219u8,197u8, - 151u8,149u8,70u8,241u8,168u8,190u8,61u8,158u8,53u8,194u8,151u8,129u8,161u8,171u8,111u8,230u8,198u8,226u8,114u8,141u8, - 242u8,192u8,145u8,97u8,145u8,222u8,37u8,108u8,38u8,162u8,58u8,142u8,60u8,193u8,27u8,34u8,127u8,186u8,151u8,71u8, - 129u8,51u8,199u8,134u8,27u8,20u8,118u8,63u8,107u8,182u8,51u8,170u8,255u8,131u8,220u8,18u8,215u8,123u8,204u8,85u8, - 98u8,24u8,251u8,96u8,134u8,188u8,32u8,41u8,120u8,71u8,182u8,102u8,180u8,173u8,5u8,214u8,13u8,95u8,143u8,98u8, - 115u8,184u8,150u8,238u8,1u8,138u8,37u8,199u8,109u8,204u8,216u8,135u8,114u8,209u8,186u8,125u8,215u8,4u8,211u8,148u8, - 181u8,24u8,108u8,4u8,189u8,46u8,124u8,65u8,189u8,70u8,94u8,162u8,110u8,11u8,202u8,75u8,210u8,153u8,101u8,93u8, - 231u8,230u8,192u8,117u8,79u8,207u8,153u8,107u8,195u8,141u8,153u8,204u8,47u8,6u8,226u8,162u8,151u8,198u8,74u8,98u8, - 5u8,194u8,48u8,25u8,105u8,195u8,164u8,182u8,239u8,171u8,79u8,148u8,14u8,246u8,12u8,168u8,209u8,196u8,221u8,224u8, - 6u8,60u8,84u8,193u8,137u8,201u8,32u8,92u8,180u8,56u8,143u8,21u8,243u8,217u8,193u8,105u8,168u8,76u8,91u8,104u8, - 72u8,50u8,189u8,44u8,219u8,20u8,168u8,180u8,10u8,108u8,170u8,105u8,18u8,101u8,116u8,86u8,37u8,35u8,124u8,90u8, - 63u8,14u8,12u8,35u8,76u8,57u8,74u8,251u8,212u8,226u8,167u8,211u8,113u8,221u8,116u8,204u8,183u8,12u8,219u8,165u8, - 203u8,153u8,148u8,59u8,252u8,44u8,186u8,174u8,109u8,185u8,189u8,232u8,101u8,249u8,188u8,144u8,174u8,55u8,53u8,151u8, - 67u8,52u8,10u8,191u8,28u8,13u8,187u8,86u8,196u8,255u8,220u8,190u8,141u8,235u8,99u8,159u8,143u8,152u8,100u8,248u8, - 145u8,194u8,222u8,42u8,246u8,60u8,228u8,158u8,184u8,118u8,205u8,142u8,55u8,88u8,215u8,179u8,29u8,50u8,109u8,124u8, - 67u8,96u8,240u8,156u8,140u8,3u8,240u8,55u8,193u8,82u8,242u8,194u8,174u8,15u8,108u8,158u8,33u8,21u8,234u8,6u8, - 241u8,39u8,189u8,103u8,216u8,176u8,225u8,100u8,112u8,144u8,202u8,105u8,140u8,100u8,50u8,2u8,73u8,76u8,111u8,142u8, - 74u8,227u8,194u8,109u8,91u8,19u8,22u8,80u8,223u8,219u8,98u8,183u8,120u8,152u8,187u8,222u8,78u8,27u8,29u8,78u8, - 24u8,243u8,157u8,43u8,118u8,118u8,210u8,139u8,203u8,217u8,77u8,203u8,121u8,233u8,243u8,254u8,64u8,53u8,141u8,93u8, - 23u8,165u8,64u8,115u8,206u8,43u8,104u8,131u8,107u8,226u8,120u8,245u8,24u8,109u8,62u8,44u8,182u8,97u8,60u8,149u8, - 197u8,63u8,141u8,54u8,132u8,49u8,135u8,168u8,215u8,26u8,101u8,103u8,145u8,41u8,137u8,164u8,19u8,209u8,15u8,194u8, - 137u8,168u8,19u8,93u8,159u8,103u8,195u8,115u8,108u8,116u8,141u8,202u8,210u8,176u8,199u8,142u8,138u8,153u8,64u8,227u8, - 248u8,76u8,197u8,153u8,227u8,32u8,223u8,165u8,55u8,57u8,230u8,12u8,82u8,0u8,65u8,217u8,138u8,129u8,173u8,216u8, - 92u8,180u8,53u8,124u8,154u8,208u8,51u8,232u8,241u8,0u8,9u8,160u8,233u8,221u8,20u8,196u8,107u8,178u8,137u8,198u8, - 101u8,194u8,139u8,88u8,121u8,22u8,5u8,209u8,188u8,211u8,196u8,249u8,104u8,205u8,150u8,181u8,224u8,201u8,242u8,254u8, - 82u8,127u8,232u8,41u8,194u8,145u8,121u8,238u8,95u8,20u8,5u8,1u8,214u8,6u8,74u8,73u8,97u8,228u8,76u8,66u8, - 194u8,32u8,61u8,97u8,77u8,103u8,64u8,229u8,131u8,200u8,228u8,83u8,48u8,168u8,152u8,232u8,104u8,220u8,171u8,82u8, - 131u8,23u8,220u8,72u8,214u8,213u8,233u8,145u8,100u8,37u8,114u8,187u8,164u8,193u8,182u8,93u8,171u8,196u8,90u8,7u8, - 197u8,169u8,85u8,132u8,217u8,86u8,177u8,58u8,101u8,152u8,241u8,136u8,53u8,54u8,84u8,90u8,219u8,106u8,148u8,78u8, - 54u8,20u8,79u8,189u8,193u8,2u8,113u8,91u8,57u8,170u8,126u8,144u8,237u8,74u8,105u8,6u8,126u8,59u8,155u8,157u8, - 151u8,172u8,208u8,229u8,182u8,25u8,180u8,221u8,176u8,52u8,12u8,111u8,4u8,106u8,56u8,177u8,206u8,85u8,232u8,129u8, - 127u8,224u8,238u8,131u8,133u8,177u8,101u8,19u8,27u8,212u8,94u8,59u8,244u8,135u8,84u8,13u8,147u8,171u8,100u8,154u8, - 37u8,168u8,139u8,135u8,6u8,198u8,102u8,196u8,160u8,51u8,143u8,75u8,14u8,52u8,106u8,121u8,219u8,229u8,60u8,185u8, - 171u8,160u8,161u8,124u8,150u8,129u8,228u8,69u8,64u8,233u8,211u8,149u8,192u8,249u8,147u8,171u8,81u8,7u8,44u8,56u8, - 183u8,26u8,104u8,31u8,236u8,95u8,179u8,232u8,25u8,30u8,180u8,65u8,177u8,25u8,197u8,74u8,121u8,84u8,187u8,209u8, - 23u8,33u8,241u8,22u8,200u8,108u8,198u8,40u8,78u8,28u8,49u8,23u8,32u8,56u8,42u8,76u8,70u8,125u8,170u8,127u8, - 118u8,224u8,177u8,26u8,81u8,87u8,142u8,110u8,51u8,136u8,226u8,78u8,228u8,52u8,55u8,48u8,187u8,184u8,78u8,46u8, - 99u8,65u8,142u8,184u8,201u8,124u8,161u8,70u8,148u8,11u8,246u8,7u8,244u8,143u8,75u8,129u8,190u8,156u8,23u8,231u8, - 241u8,32u8,25u8,190u8,111u8,249u8,154u8,89u8,123u8,62u8,117u8,167u8,180u8,11u8,255u8,127u8,169u8,162u8,235u8,141u8, - 22u8,188u8,252u8,230u8,232u8,39u8,140u8,144u8,182u8,51u8,178u8,59u8,233u8,40u8,108u8,229u8,119u8,0u8,244u8,85u8, - 4u8,15u8,82u8,75u8,13u8,18u8,179u8,110u8,92u8,206u8,48u8,44u8,130u8,28u8,175u8,133u8,94u8,213u8,149u8,210u8, - 69u8,76u8,157u8,76u8,245u8,149u8,128u8,65u8,139u8,178u8,154u8,97u8,78u8,135u8,155u8,174u8,153u8,198u8,7u8,125u8, - 37u8,40u8,195u8,201u8,52u8,59u8,59u8,131u8,241u8,177u8,139u8,112u8,86u8,54u8,181u8,43u8,58u8,110u8,78u8,248u8, - 23u8,103u8,90u8,252u8,148u8,115u8,43u8,157u8,218u8,53u8,108u8,116u8,13u8,70u8,122u8,149u8,204u8,148u8,11u8,181u8, - 228u8,51u8,48u8,137u8,233u8,141u8,235u8,234u8,34u8,196u8,103u8,26u8,162u8,101u8,107u8,24u8,24u8,70u8,35u8,251u8, - 201u8,78u8,158u8,219u8,24u8,117u8,13u8,251u8,159u8,186u8,177u8,108u8,14u8,99u8,218u8,19u8,233u8,46u8,126u8,49u8, - 144u8,205u8,209u8,134u8,116u8,26u8,40u8,143u8,141u8,165u8,137u8,69u8,209u8,223u8,140u8,44u8,32u8,119u8,53u8,252u8, - 200u8,178u8,229u8,97u8,219u8,99u8,51u8,32u8,145u8,6u8,61u8,36u8,3u8,237u8,195u8,174u8,143u8,129u8,14u8,158u8, - 106u8,32u8,36u8,203u8,34u8,109u8,224u8,78u8,43u8,122u8,30u8,109u8,184u8,36u8,65u8,199u8,221u8,169u8,61u8,225u8, - 58u8,182u8,173u8,187u8,230u8,240u8,232u8,161u8,114u8,244u8,37u8,77u8,213u8,174u8,76u8,46u8,64u8,142u8,31u8,248u8, - 73u8,156u8,159u8,122u8,199u8,128u8,19u8,116u8,59u8,254u8,109u8,87u8,163u8,143u8,59u8,71u8,51u8,174u8,80u8,77u8, - 83u8,183u8,253u8,140u8,161u8,19u8,1u8,248u8,132u8,131u8,28u8,112u8,87u8,83u8,43u8,199u8,16u8,237u8,239u8,132u8, - 204u8,212u8,223u8,213u8,146u8,75u8,24u8,193u8,36u8,67u8,113u8,9u8,189u8,187u8,218u8,204u8,45u8,25u8,84u8,34u8, - 249u8,41u8,156u8,15u8,188u8,141u8,16u8,253u8,48u8,123u8,233u8,74u8,209u8,99u8,77u8,156u8,121u8,171u8,238u8,188u8, - 29u8,77u8,190u8,127u8,158u8,14u8,223u8,11u8,50u8,91u8,102u8,103u8,146u8,105u8,29u8,68u8,250u8,38u8,154u8,255u8, - 87u8,240u8,95u8,57u8,127u8,145u8,209u8,133u8,98u8,247u8,162u8,105u8,126u8,54u8,79u8,191u8,162u8,60u8,160u8,179u8, - 25u8,48u8,30u8,35u8,198u8,150u8,228u8,66u8,218u8,153u8,24u8,133u8,48u8,216u8,128u8,75u8,237u8,201u8,37u8,115u8, - 5u8,34u8,199u8,194u8,81u8,99u8,221u8,104u8,112u8,51u8,3u8,233u8,130u8,242u8,189u8,66u8,251u8,86u8,37u8,173u8, - 176u8,111u8,238u8,35u8,102u8,167u8,13u8,152u8,45u8,230u8,107u8,214u8,41u8,79u8,57u8,9u8,186u8,140u8,223u8,89u8, - 74u8,187u8,163u8,124u8,0u8,51u8,145u8,232u8,10u8,68u8,162u8,179u8,249u8,5u8,236u8,73u8,139u8,73u8,105u8,213u8, - 182u8,72u8,135u8,75u8,65u8,37u8,142u8,113u8,239u8,41u8,241u8,205u8,162u8,84u8,180u8,134u8,104u8,214u8,209u8,200u8, - 197u8,72u8,162u8,113u8,95u8,157u8,235u8,100u8,240u8,7u8,67u8,9u8,211u8,8u8,55u8,17u8,121u8,132u8,26u8,126u8, - 33u8,162u8,167u8,14u8,15u8,14u8,77u8,38u8,119u8,106u8,89u8,80u8,202u8,83u8,3u8,185u8,33u8,62u8,133u8,189u8, - 29u8,181u8,216u8,20u8,79u8,109u8,6u8,232u8,223u8,124u8,77u8,88u8,0u8,184u8,235u8,22u8,234u8,103u8,135u8,107u8, - 115u8,122u8,17u8,73u8,18u8,57u8,59u8,76u8,169u8,26u8,218u8,145u8,38u8,192u8,255u8,75u8,167u8,185u8,241u8,160u8, - 175u8,211u8,11u8,45u8,212u8,169u8,212u8,15u8,53u8,234u8,182u8,96u8,115u8,198u8,209u8,223u8,209u8,42u8,249u8,201u8, - 5u8,172u8,141u8,182u8,27u8,123u8,177u8,252u8,21u8,109u8,49u8,133u8,13u8,197u8,28u8,119u8,157u8,5u8,84u8,196u8, - 233u8,43u8,14u8,176u8,67u8,225u8,146u8,248u8,97u8,124u8,222u8,44u8,106u8,95u8,162u8,103u8,39u8,200u8,104u8,27u8, - 140u8,225u8,133u8,243u8,179u8,64u8,253u8,200u8,254u8,48u8,68u8,59u8,206u8,159u8,5u8,23u8,12u8,249u8,175u8,218u8, - 11u8,38u8,1u8,0u8,15u8,188u8,42u8,23u8,64u8,197u8,0u8,129u8,204u8,0u8,236u8,24u8,139u8,37u8,9u8,8u8, - 15u8,218u8,48u8,101u8,0u8,59u8,135u8,80u8,246u8,128u8,240u8,104u8,92u8,46u8,1u8,22u8,116u8,69u8,90u8,129u8, - 48u8,116u8,62u8,201u8,0u8,11u8,191u8,34u8,223u8,64u8,24u8,126u8,32u8,251u8,0u8,127u8,62u8,92u8,34u8,130u8, - 138u8,99u8,112u8,211u8,18u8,176u8,64u8,3u8,25u8,10u8,194u8,96u8,153u8,124u8,5u8,44u8,224u8,170u8,212u8,5u8, - 97u8,232u8,129u8,68u8,6u8,222u8,8u8,31u8,42u8,88u8,129u8,155u8,228u8,228u8,15u8,39u8,38u8,198u8,102u8,145u8, - 140u8,250u8,251u8,224u8,247u8,217u8,20u8,46u8,166u8,140u8,225u8,38u8,187u8,155u8,155u8,110u8,209u8,78u8,89u8,22u8, - 146u8,151u8,145u8,181u8,167u8,2u8,88u8,172u8,237u8,227u8,46u8,111u8,223u8,241u8,38u8,172u8,153u8,189u8,183u8,146u8, - 245u8,176u8,250u8,114u8,46u8,60u8,75u8,118u8,68u8,42u8,92u8,234u8,51u8,103u8,42u8,207u8,157u8,46u8,238u8,22u8, - 190u8,144u8,213u8,21u8,12u8,199u8,3u8,184u8,219u8,32u8,3u8,191u8,147u8,251u8,244u8,78u8,38u8,111u8,151u8,121u8, - 206u8,139u8,242u8,11u8,148u8,148u8,41u8,107u8,174u8,76u8,184u8,72u8,121u8,219u8,208u8,70u8,50u8,161u8,103u8,10u8, - 152u8,194u8,165u8,254u8,66u8,15u8,230u8,231u8,193u8,49u8,246u8,95u8,214u8,121u8,8u8,238u8,127u8,39u8,50u8,60u8, - 29u8,92u8,71u8,7u8,207u8,186u8,9u8,2u8,141u8,180u8,212u8,121u8,27u8,18u8,220u8,126u8,86u8,126u8,10u8,165u8, - 232u8,108u8,135u8,149u8,2u8,122u8,158u8,62u8,198u8,190u8,72u8,129u8,140u8,228u8,55u8,149u8,126u8,30u8,35u8,209u8, - 198u8,221u8,136u8,218u8,133u8,35u8,170u8,213u8,93u8,169u8,56u8,250u8,0u8,8u8,168u8,225u8,61u8,101u8,205u8,19u8, - 24u8,121u8,46u8,47u8,146u8,174u8,98u8,192u8,122u8,237u8,184u8,169u8,3u8,189u8,215u8,167u8,255u8,238u8,54u8,142u8, - 147u8,13u8,40u8,110u8,7u8,47u8,21u8,47u8,58u8,222u8,209u8,253u8,210u8,195u8,32u8,106u8,147u8,105u8,199u8,181u8, - 127u8,215u8,220u8,187u8,167u8,124u8,142u8,35u8,85u8,251u8,163u8,101u8,1u8,183u8,247u8,166u8,93u8,113u8,74u8,74u8, - 77u8,64u8,217u8,85u8,184u8,242u8,38u8,203u8,157u8,160u8,123u8,118u8,206u8,60u8,121u8,111u8,163u8,250u8,19u8,13u8, - 29u8,160u8,137u8,163u8,38u8,122u8,90u8,30u8,74u8,79u8,111u8,201u8,31u8,158u8,172u8,74u8,245u8,62u8,241u8,132u8, - 1u8,70u8,132u8,60u8,226u8,89u8,233u8,122u8,238u8,202u8,244u8,198u8,113u8,135u8,27u8,1u8,8u8,115u8,199u8,12u8, - 70u8,41u8,115u8,238u8,99u8,94u8,46u8,193u8,118u8,109u8,93u8,1u8,169u8,25u8,12u8,192u8,140u8,212u8,109u8,243u8, - 120u8,47u8,73u8,128u8,175u8,213u8,55u8,151u8,221u8,241u8,190u8,53u8,87u8,238u8,127u8,107u8,174u8,194u8,254u8,246u8, - 67u8,199u8,213u8,66u8,52u8,38u8,54u8,1u8,31u8,202u8,70u8,52u8,135u8,85u8,199u8,57u8,164u8,199u8,214u8,39u8, - 255u8,53u8,232u8,142u8,179u8,47u8,149u8,228u8,71u8,232u8,46u8,111u8,245u8,208u8,2u8,199u8,181u8,0u8,181u8,9u8, - 30u8,226u8,199u8,76u8,106u8,140u8,155u8,237u8,236u8,140u8,164u8,24u8,244u8,179u8,127u8,50u8,163u8,17u8,8u8,103u8, - 23u8,66u8,54u8,131u8,141u8,71u8,117u8,133u8,225u8,65u8,80u8,202u8,109u8,176u8,104u8,101u8,41u8,210u8,147u8,9u8, - 92u8,38u8,71u8,185u8,111u8,92u8,33u8,195u8,124u8,211u8,232u8,238u8,116u8,170u8,226u8,95u8,62u8,174u8,155u8,84u8, - 234u8,35u8,66u8,247u8,71u8,104u8,138u8,212u8,147u8,205u8,212u8,22u8,73u8,17u8,81u8,90u8,69u8,218u8,252u8,33u8, - 210u8,201u8,41u8,57u8,220u8,246u8,174u8,21u8,86u8,63u8,45u8,140u8,75u8,87u8,50u8,35u8,173u8,153u8,41u8,200u8, - 11u8,99u8,226u8,37u8,110u8,21u8,166u8,16u8,242u8,238u8,94u8,104u8,33u8,161u8,27u8,71u8,243u8,170u8,10u8,121u8, - 110u8,118u8,218u8,183u8,116u8,7u8,109u8,171u8,146u8,176u8,87u8,57u8,33u8,5u8,180u8,0u8,215u8,140u8,36u8,91u8, - 238u8,250u8,166u8,45u8,209u8,87u8,201u8,227u8,168u8,248u8,107u8,185u8,16u8,132u8,66u8,9u8,159u8,158u8,172u8,129u8, - 75u8,231u8,248u8,164u8,76u8,161u8,248u8,78u8,189u8,82u8,117u8,216u8,174u8,216u8,242u8,12u8,148u8,107u8,216u8,143u8, - 172u8,113u8,195u8,72u8,186u8,76u8,158u8,173u8,216u8,143u8,54u8,17u8,113u8,179u8,131u8,84u8,23u8,163u8,226u8,29u8, - 70u8,170u8,50u8,91u8,145u8,145u8,202u8,133u8,171u8,131u8,128u8,140u8,10u8,97u8,131u8,84u8,172u8,18u8,70u8,185u8, - 150u8,185u8,133u8,111u8,34u8,190u8,140u8,85u8,247u8,182u8,124u8,126u8,48u8,67u8,64u8,154u8,76u8,177u8,50u8,149u8, - 202u8,127u8,12u8,66u8,9u8,154u8,208u8,231u8,133u8,200u8,172u8,229u8,69u8,245u8,144u8,193u8,155u8,94u8,174u8,19u8, - 80u8,220u8,76u8,82u8,244u8,10u8,78u8,128u8,250u8,141u8,243u8,252u8,178u8,232u8,174u8,18u8,90u8,87u8,27u8,202u8, - 22u8,253u8,251u8,223u8,142u8,98u8,248u8,22u8,67u8,247u8,164u8,210u8,208u8,244u8,119u8,140u8,21u8,214u8,181u8,42u8, - 141u8,180u8,146u8,42u8,249u8,41u8,252u8,96u8,79u8,40u8,195u8,127u8,82u8,166u8,191u8,213u8,167u8,60u8,72u8,135u8, - 9u8,150u8,98u8,42u8,185u8,191u8,172u8,90u8,65u8,60u8,8u8,171u8,9u8,162u8,130u8,188u8,10u8,235u8,142u8,240u8, - 220u8,174u8,179u8,66u8,166u8,206u8,150u8,156u8,16u8,45u8,116u8,42u8,33u8,53u8,165u8,209u8,46u8,51u8,126u8,235u8, - 35u8,119u8,67u8,214u8,68u8,233u8,155u8,34u8,55u8,83u8,88u8,79u8,220u8,100u8,39u8,107u8,227u8,222u8,100u8,59u8, - 47u8,220u8,64u8,55u8,125u8,136u8,14u8,10u8,252u8,17u8,202u8,151u8,96u8,50u8,13u8,87u8,222u8,119u8,79u8,222u8, - 37u8,74u8,108u8,20u8,83u8,67u8,216u8,65u8,152u8,14u8,129u8,109u8,197u8,29u8,85u8,26u8,65u8,144u8,230u8,182u8, - 124u8,17u8,153u8,117u8,186u8,68u8,156u8,189u8,40u8,144u8,39u8,229u8,136u8,71u8,76u8,155u8,86u8,219u8,245u8,54u8, - 55u8,176u8,84u8,6u8,227u8,33u8,150u8,112u8,81u8,131u8,229u8,212u8,25u8,163u8,150u8,5u8,231u8,217u8,174u8,61u8, - 225u8,10u8,27u8,167u8,87u8,87u8,201u8,178u8,104u8,45u8,248u8,58u8,115u8,205u8,23u8,246u8,11u8,201u8,206u8,150u8, - 182u8,232u8,187u8,204u8,78u8,146u8,38u8,126u8,91u8,224u8,5u8,70u8,53u8,74u8,2u8,50u8,163u8,231u8,245u8,232u8, - 36u8,166u8,247u8,100u8,69u8,215u8,174u8,178u8,178u8,200u8,248u8,81u8,11u8,137u8,158u8,149u8,41u8,36u8,43u8,90u8, - 180u8,87u8,158u8,141u8,229u8,218u8,91u8,9u8,39u8,36u8,170u8,241u8,123u8,125u8,199u8,175u8,34u8,203u8,25u8,3u8, - 54u8,147u8,60u8,207u8,132u8,184u8,69u8,222u8,206u8,106u8,101u8,232u8,235u8,102u8,124u8,151u8,77u8,2u8,53u8,39u8, - 214u8,170u8,204u8,17u8,206u8,253u8,140u8,111u8,111u8,227u8,244u8,2u8,79u8,157u8,44u8,97u8,208u8,247u8,34u8,155u8, - 72u8,102u8,218u8,9u8,12u8,224u8,108u8,203u8,113u8,70u8,190u8,223u8,40u8,212u8,235u8,58u8,85u8,210u8,243u8,135u8, - 220u8,84u8,76u8,33u8,81u8,190u8,179u8,91u8,94u8,116u8,177u8,8u8,39u8,30u8,202u8,12u8,23u8,133u8,43u8,90u8, - 138u8,178u8,179u8,229u8,152u8,86u8,172u8,113u8,187u8,35u8,249u8,179u8,228u8,170u8,217u8,197u8,69u8,138u8,9u8,120u8, - 83u8,96u8,164u8,134u8,204u8,232u8,141u8,233u8,80u8,3u8,9u8,250u8,109u8,42u8,56u8,237u8,40u8,199u8,138u8,120u8, - 147u8,84u8,214u8,162u8,65u8,71u8,156u8,128u8,192u8,43u8,152u8,181u8,204u8,226u8,50u8,113u8,14u8,186u8,68u8,145u8, - 81u8,44u8,72u8,147u8,58u8,30u8,105u8,173u8,9u8,178u8,198u8,210u8,53u8,212u8,147u8,109u8,204u8,164u8,68u8,33u8, - 238u8,231u8,13u8,188u8,2u8,253u8,15u8,216u8,155u8,109u8,50u8,205u8,38u8,178u8,92u8,142u8,27u8,44u8,64u8,255u8, - 133u8,217u8,153u8,171u8,161u8,236u8,132u8,229u8,82u8,45u8,39u8,76u8,193u8,67u8,149u8,9u8,169u8,2u8,49u8,151u8, - 195u8,215u8,231u8,9u8,140u8,57u8,188u8,197u8,120u8,123u8,122u8,254u8,103u8,53u8,105u8,249u8,154u8,229u8,195u8,36u8, - 64u8,77u8,92u8,212u8,106u8,158u8,176u8,107u8,137u8,160u8,91u8,153u8,104u8,41u8,161u8,134u8,227u8,123u8,218u8,239u8, - 113u8,183u8,82u8,45u8,173u8,69u8,158u8,249u8,36u8,153u8,195u8,1u8,79u8,176u8,52u8,55u8,224u8,120u8,203u8,170u8, - 190u8,104u8,9u8,59u8,106u8,84u8,55u8,145u8,28u8,27u8,230u8,199u8,25u8,208u8,220u8,178u8,164u8,237u8,96u8,100u8, - 161u8,116u8,37u8,175u8,120u8,255u8,213u8,196u8,22u8,42u8,29u8,189u8,139u8,25u8,232u8,159u8,110u8,141u8,226u8,197u8, - 28u8,62u8,253u8,104u8,253u8,6u8,185u8,155u8,240u8,145u8,249u8,14u8,170u8,1u8,106u8,206u8,72u8,42u8,63u8,253u8, - 163u8,91u8,154u8,48u8,135u8,125u8,117u8,28u8,226u8,28u8,72u8,252u8,187u8,140u8,21u8,165u8,250u8,172u8,154u8,157u8, - 104u8,37u8,77u8,255u8,89u8,149u8,132u8,73u8,149u8,111u8,164u8,136u8,67u8,199u8,156u8,254u8,78u8,77u8,121u8,151u8, - 202u8,75u8,83u8,63u8,69u8,19u8,226u8,54u8,21u8,6u8,169u8,231u8,94u8,234u8,30u8,185u8,111u8,228u8,153u8,180u8, - 46u8,250u8,191u8,64u8,238u8,225u8,5u8,253u8,138u8,63u8,241u8,128u8,143u8,159u8,7u8,120u8,216u8,225u8,51u8,1u8, - 175u8,73u8,144u8,20u8,113u8,192u8,88u8,20u8,244u8,231u8,225u8,163u8,158u8,63u8,17u8,191u8,77u8,112u8,38u8,44u8, - 56u8,30u8,197u8,151u8,39u8,140u8,139u8,56u8,24u8,218u8,148u8,169u8,97u8,206u8,241u8,101u8,8u8,103u8,141u8,163u8, - 119u8,144u8,42u8,240u8,208u8,234u8,252u8,194u8,195u8,148u8,99u8,1u8,82u8,124u8,156u8,93u8,100u8,227u8,100u8,42u8, - 50u8,12u8,219u8,254u8,146u8,234u8,21u8,31u8,13u8,230u8,51u8,161u8,63u8,71u8,109u8,41u8,235u8,193u8,209u8,200u8, - 131u8,205u8,140u8,248u8,176u8,70u8,10u8,123u8,168u8,255u8,73u8,21u8,47u8,161u8,157u8,182u8,245u8,46u8,12u8,223u8, - 36u8,55u8,76u8,171u8,214u8,76u8,169u8,115u8,150u8,181u8,108u8,131u8,53u8,49u8,81u8,235u8,235u8,121u8,126u8,71u8, - 207u8,212u8,83u8,91u8,36u8,206u8,204u8,75u8,79u8,56u8,173u8,171u8,54u8,1u8,208u8,16u8,168u8,0u8,16u8,115u8, - 240u8,203u8,132u8,122u8,6u8,188u8,234u8,181u8,6u8,146u8,2u8,61u8,8u8,35u8,185u8,206u8,34u8,194u8,170u8,131u8, - 29u8,93u8,243u8,218u8,156u8,119u8,252u8,35u8,178u8,88u8,101u8,69u8,106u8,85u8,132u8,192u8,230u8,241u8,143u8,194u8, - 57u8,86u8,163u8,47u8,3u8,10u8,113u8,196u8,10u8,35u8,241u8,236u8,104u8,46u8,50u8,16u8,163u8,125u8,18u8,155u8, - 113u8,50u8,55u8,59u8,249u8,103u8,252u8,148u8,26u8,8u8,224u8,162u8,246u8,118u8,155u8,119u8,58u8,224u8,214u8,200u8, - 14u8,180u8,60u8,135u8,225u8,221u8,199u8,109u8,210u8,202u8,213u8,105u8,88u8,134u8,113u8,248u8,251u8,195u8,82u8,250u8, - 154u8,102u8,149u8,132u8,158u8,196u8,230u8,33u8,85u8,124u8,47u8,19u8,113u8,72u8,165u8,102u8,48u8,213u8,217u8,227u8, - 160u8,59u8,108u8,73u8,204u8,125u8,95u8,248u8,229u8,165u8,231u8,230u8,82u8,110u8,77u8,110u8,69u8,39u8,199u8,60u8, - 135u8,227u8,20u8,159u8,228u8,56u8,149u8,11u8,245u8,101u8,200u8,0u8,196u8,102u8,160u8,247u8,17u8,248u8,213u8,81u8, - 252u8,250u8,232u8,248u8,36u8,254u8,225u8,224u8,213u8,193u8,241u8,225u8,177u8,83u8,209u8,123u8,255u8,199u8,189u8,87u8, - 63u8,96u8,118u8,250u8,151u8,71u8,111u8,15u8,94u8,180u8,173u8,35u8,171u8,42u8,91u8,152u8,129u8,73u8,114u8,58u8, - 73u8,198u8,90u8,136u8,118u8,172u8,169u8,190u8,226u8,12u8,140u8,90u8,112u8,231u8,117u8,185u8,106u8,39u8,89u8,113u8, - 192u8,236u8,13u8,74u8,112u8,137u8,34u8,152u8,103u8,69u8,96u8,73u8,114u8,58u8,75u8,69u8,69u8,95u8,242u8,222u8, - 224u8,130u8,117u8,140u8,100u8,216u8,135u8,110u8,186u8,188u8,243u8,68u8,21u8,78u8,16u8,86u8,54u8,169u8,204u8,109u8, - 149u8,165u8,120u8,101u8,25u8,94u8,122u8,4u8,142u8,145u8,160u8,139u8,79u8,101u8,85u8,115u8,25u8,72u8,214u8,238u8, - 8u8,123u8,189u8,180u8,228u8,151u8,245u8,33u8,148u8,141u8,149u8,148u8,33u8,92u8,5u8,55u8,170u8,56u8,97u8,36u8, - 216u8,160u8,133u8,195u8,109u8,160u8,111u8,181u8,103u8,128u8,5u8,199u8,168u8,209u8,119u8,109u8,86u8,33u8,46u8,35u8, - 229u8,141u8,106u8,196u8,81u8,43u8,29u8,103u8,103u8,153u8,148u8,133u8,74u8,127u8,6u8,210u8,41u8,195u8,196u8,253u8, - 250u8,205u8,34u8,41u8,142u8,60u8,81u8,120u8,230u8,77u8,11u8,101u8,52u8,118u8,182u8,94u8,94u8,80u8,153u8,41u8, - 71u8,213u8,202u8,178u8,243u8,231u8,4u8,83u8,215u8,84u8,97u8,209u8,253u8,95u8,209u8,63u8,243u8,179u8,180u8,146u8, - 24u8,213u8,230u8,109u8,101u8,19u8,162u8,170u8,68u8,168u8,149u8,68u8,72u8,0u8,211u8,225u8,142u8,162u8,135u8,17u8, - 84u8,201u8,83u8,155u8,38u8,194u8,142u8,217u8,182u8,37u8,239u8,171u8,178u8,81u8,47u8,101u8,99u8,247u8,228u8,136u8, - 91u8,51u8,170u8,63u8,223u8,141u8,156u8,9u8,215u8,24u8,213u8,79u8,142u8,142u8,64u8,250u8,120u8,219u8,94u8,187u8, - 181u8,30u8,1u8,255u8,120u8,248u8,195u8,143u8,109u8,199u8,194u8,118u8,210u8,208u8,245u8,170u8,235u8,22u8,225u8,229u8, - 93u8,101u8,204u8,79u8,219u8,190u8,151u8,76u8,73u8,153u8,61u8,127u8,153u8,142u8,244u8,199u8,243u8,188u8,80u8,56u8, - 7u8,152u8,38u8,57u8,247u8,22u8,210u8,139u8,232u8,176u8,24u8,149u8,33u8,2u8,212u8,183u8,34u8,73u8,196u8,163u8, - 218u8,140u8,124u8,43u8,132u8,131u8,151u8,181u8,108u8,83u8,187u8,80u8,182u8,229u8,127u8,86u8,96u8,2u8,4u8,100u8, - 101u8,248u8,250u8,193u8,60u8,41u8,41u8,177u8,11u8,44u8,211u8,1u8,79u8,232u8,219u8,243u8,23u8,243u8,83u8,203u8, - 84u8,36u8,118u8,82u8,167u8,136u8,137u8,208u8,166u8,132u8,32u8,150u8,106u8,198u8,218u8,248u8,142u8,65u8,234u8,58u8, - 209u8,23u8,238u8,14u8,135u8,245u8,95u8,200u8,71u8,104u8,55u8,188u8,100u8,81u8,117u8,222u8,101u8,248u8,220u8,168u8, - 238u8,226u8,36u8,213u8,103u8,46u8,160u8,63u8,13u8,184u8,134u8,63u8,237u8,253u8,211u8,17u8,179u8,142u8,15u8,255u8, - 247u8,160u8,2u8,37u8,236u8,198u8,116u8,225u8,247u8,222u8,252u8,112u8,176u8,138u8,15u8,77u8,48u8,38u8,212u8,166u8, - 216u8,124u8,237u8,52u8,59u8,8u8,170u8,177u8,202u8,70u8,134u8,113u8,222u8,158u8,166u8,70u8,12u8,240u8,215u8,118u8, - 147u8,113u8,54u8,89u8,59u8,79u8,52u8,83u8,214u8,104u8,215u8,37u8,189u8,243u8,86u8,2u8,122u8,225u8,123u8,139u8, - 102u8,45u8,42u8,65u8,45u8,11u8,66u8,203u8,34u8,52u8,72u8,110u8,168u8,250u8,143u8,202u8,117u8,104u8,232u8,85u8, - 248u8,89u8,137u8,92u8,69u8,209u8,242u8,186u8,21u8,196u8,175u8,243u8,124u8,58u8,251u8,106u8,152u8,77u8,135u8,243u8, - 140u8,74u8,84u8,151u8,254u8,48u8,98u8,80u8,116u8,86u8,220u8,136u8,138u8,60u8,186u8,78u8,165u8,23u8,5u8,222u8, - 17u8,113u8,95u8,28u8,127u8,212u8,10u8,207u8,229u8,106u8,151u8,228u8,159u8,101u8,5u8,61u8,233u8,238u8,133u8,53u8, - 81u8,130u8,154u8,173u8,110u8,244u8,86u8,201u8,232u8,138u8,118u8,203u8,13u8,28u8,222u8,12u8,97u8,83u8,197u8,22u8, - 22u8,246u8,251u8,65u8,130u8,67u8,7u8,118u8,115u8,220u8,82u8,217u8,37u8,221u8,47u8,147u8,233u8,56u8,195u8,103u8, - 148u8,62u8,34u8,33u8,249u8,83u8,5u8,174u8,172u8,44u8,183u8,118u8,135u8,122u8,169u8,149u8,69u8,97u8,233u8,234u8, - 228u8,29u8,43u8,60u8,54u8,244u8,155u8,203u8,43u8,146u8,160u8,70u8,103u8,253u8,136u8,106u8,234u8,175u8,56u8,11u8, - 80u8,213u8,17u8,157u8,156u8,250u8,33u8,127u8,153u8,38u8,94u8,50u8,77u8,92u8,81u8,75u8,127u8,27u8,123u8,2u8, - 43u8,208u8,124u8,38u8,140u8,222u8,49u8,54u8,184u8,149u8,37u8,151u8,244u8,158u8,44u8,11u8,74u8,46u8,236u8,64u8, - 163u8,130u8,236u8,5u8,1u8,210u8,33u8,54u8,159u8,23u8,81u8,69u8,45u8,140u8,146u8,3u8,168u8,8u8,145u8,250u8, - 220u8,105u8,58u8,252u8,223u8,32u8,69u8,238u8,107u8,241u8,47u8,17u8,124u8,35u8,222u8,83u8,194u8,21u8,76u8,111u8, - 74u8,200u8,183u8,210u8,217u8,53u8,15u8,189u8,101u8,204u8,185u8,23u8,160u8,83u8,138u8,113u8,166u8,27u8,116u8,248u8, - 220u8,45u8,237u8,143u8,119u8,248u8,34u8,58u8,183u8,50u8,84u8,199u8,227u8,64u8,254u8,210u8,252u8,4u8,254u8,140u8, - 223u8,103u8,3u8,124u8,241u8,226u8,119u8,30u8,72u8,248u8,206u8,186u8,169u8,240u8,137u8,244u8,42u8,196u8,122u8,113u8, - 163u8,51u8,84u8,250u8,128u8,216u8,38u8,114u8,32u8,39u8,198u8,243u8,67u8,92u8,126u8,82u8,162u8,101u8,83u8,85u8, - 134u8,140u8,84u8,101u8,58u8,71u8,163u8,253u8,86u8,25u8,164u8,167u8,160u8,91u8,51u8,71u8,178u8,98u8,28u8,208u8, - 96u8,120u8,163u8,137u8,96u8,212u8,10u8,50u8,206u8,242u8,185u8,67u8,50u8,72u8,116u8,147u8,170u8,226u8,101u8,122u8, - 1u8,82u8,169u8,76u8,83u8,177u8,38u8,88u8,204u8,230u8,40u8,41u8,248u8,126u8,178u8,68u8,99u8,165u8,78u8,177u8, - 142u8,101u8,219u8,194u8,196u8,106u8,138u8,147u8,232u8,209u8,35u8,59u8,115u8,118u8,200u8,36u8,242u8,188u8,218u8,224u8, - 194u8,37u8,76u8,173u8,169u8,22u8,163u8,242u8,118u8,128u8,78u8,175u8,150u8,59u8,85u8,148u8,167u8,244u8,45u8,20u8, - 161u8,58u8,112u8,154u8,177u8,5u8,164u8,43u8,100u8,255u8,234u8,250u8,41u8,142u8,142u8,170u8,70u8,10u8,152u8,202u8, - 146u8,137u8,56u8,40u8,34u8,44u8,221u8,208u8,125u8,149u8,34u8,128u8,243u8,105u8,167u8,190u8,188u8,154u8,19u8,136u8, - 226u8,129u8,37u8,89u8,81u8,230u8,74u8,9u8,228u8,179u8,90u8,158u8,99u8,179u8,57u8,106u8,108u8,110u8,202u8,212u8, - 102u8,94u8,146u8,107u8,27u8,37u8,153u8,221u8,109u8,170u8,224u8,223u8,14u8,3u8,8u8,137u8,69u8,229u8,233u8,187u8, - 44u8,100u8,65u8,75u8,128u8,190u8,187u8,174u8,41u8,224u8,196u8,141u8,80u8,34u8,229u8,52u8,140u8,58u8,79u8,144u8, - 128u8,120u8,1u8,123u8,74u8,91u8,94u8,38u8,106u8,181u8,52u8,71u8,82u8,91u8,110u8,87u8,63u8,28u8,39u8,197u8, - 204u8,175u8,252u8,133u8,144u8,128u8,172u8,32u8,237u8,161u8,217u8,117u8,117u8,26u8,121u8,139u8,84u8,130u8,153u8,0u8, - 182u8,228u8,60u8,159u8,143u8,71u8,48u8,14u8,168u8,209u8,97u8,125u8,151u8,151u8,72u8,93u8,10u8,32u8,39u8,248u8, - 184u8,45u8,76u8,39u8,51u8,61u8,112u8,86u8,72u8,127u8,116u8,161u8,225u8,5u8,33u8,62u8,25u8,251u8,74u8,250u8, - 253u8,160u8,58u8,126u8,37u8,123u8,25u8,147u8,204u8,232u8,118u8,181u8,241u8,62u8,123u8,108u8,170u8,236u8,93u8,139u8, - 45u8,77u8,170u8,147u8,238u8,206u8,120u8,118u8,95u8,220u8,124u8,79u8,38u8,58u8,33u8,123u8,23u8,169u8,81u8,77u8, - 92u8,233u8,222u8,179u8,131u8,220u8,250u8,195u8,88u8,61u8,67u8,159u8,190u8,86u8,174u8,58u8,82u8,187u8,146u8,188u8, - 207u8,64u8,38u8,176u8,59u8,229u8,115u8,71u8,119u8,122u8,145u8,220u8,12u8,210u8,216u8,6u8,17u8,202u8,37u8,190u8, - 72u8,184u8,105u8,77u8,13u8,202u8,240u8,176u8,94u8,8u8,36u8,151u8,72u8,220u8,99u8,1u8,13u8,180u8,180u8,106u8, - 14u8,22u8,133u8,175u8,154u8,135u8,113u8,156u8,242u8,12u8,94u8,164u8,50u8,70u8,197u8,43u8,170u8,232u8,68u8,175u8, - 232u8,232u8,24u8,51u8,64u8,218u8,234u8,112u8,157u8,20u8,210u8,6u8,90u8,198u8,70u8,79u8,5u8,231u8,72u8,71u8, - 238u8,184u8,128u8,223u8,168u8,239u8,236u8,70u8,175u8,242u8,107u8,65u8,134u8,169u8,189u8,184u8,243u8,35u8,41u8,8u8, - 130u8,120u8,48u8,234u8,200u8,159u8,173u8,161u8,20u8,201u8,198u8,204u8,92u8,194u8,123u8,23u8,103u8,40u8,139u8,152u8, - 8u8,125u8,186u8,32u8,173u8,89u8,225u8,14u8,236u8,216u8,32u8,24u8,204u8,150u8,146u8,87u8,171u8,161u8,241u8,8u8, - 247u8,103u8,222u8,235u8,127u8,227u8,200u8,93u8,180u8,70u8,175u8,180u8,138u8,154u8,249u8,0u8,25u8,23u8,136u8,100u8, - 83u8,97u8,169u8,118u8,71u8,71u8,53u8,236u8,111u8,243u8,130u8,156u8,146u8,37u8,163u8,66u8,153u8,30u8,9u8,195u8, - 32u8,69u8,246u8,20u8,21u8,23u8,64u8,50u8,221u8,1u8,167u8,232u8,137u8,138u8,59u8,32u8,138u8,71u8,35u8,94u8, - 138u8,199u8,0u8,109u8,176u8,20u8,179u8,69u8,56u8,49u8,234u8,255u8,70u8,233u8,244u8,20u8,200u8,110u8,7u8,213u8, - 112u8,226u8,222u8,93u8,39u8,66u8,159u8,67u8,228u8,88u8,37u8,95u8,47u8,75u8,208u8,27u8,154u8,46u8,57u8,34u8, - 149u8,152u8,82u8,202u8,69u8,243u8,122u8,216u8,136u8,203u8,237u8,196u8,115u8,119u8,213u8,92u8,30u8,254u8,122u8,48u8, - 187u8,13u8,218u8,124u8,229u8,14u8,229u8,228u8,206u8,231u8,66u8,137u8,155u8,142u8,190u8,17u8,204u8,195u8,207u8,130u8, - 181u8,109u8,69u8,178u8,44u8,154u8,19u8,197u8,45u8,82u8,205u8,93u8,130u8,172u8,227u8,151u8,14u8,180u8,50u8,11u8, - 216u8,20u8,111u8,33u8,74u8,87u8,23u8,184u8,239u8,20u8,152u8,12u8,132u8,69u8,60u8,242u8,71u8,246u8,237u8,104u8, - 210u8,166u8,140u8,140u8,133u8,205u8,206u8,235u8,95u8,193u8,138u8,122u8,11u8,77u8,200u8,36u8,179u8,182u8,10u8,74u8, - 233u8,82u8,72u8,110u8,221u8,11u8,155u8,169u8,158u8,71u8,27u8,161u8,125u8,120u8,185u8,119u8,92u8,177u8,17u8,11u8, - 89u8,235u8,42u8,106u8,67u8,216u8,194u8,82u8,245u8,19u8,135u8,123u8,230u8,132u8,51u8,102u8,250u8,143u8,21u8,62u8, - 79u8,38u8,115u8,159u8,234u8,223u8,61u8,31u8,236u8,143u8,218u8,85u8,229u8,43u8,116u8,73u8,235u8,233u8,92u8,219u8, - 61u8,124u8,211u8,107u8,227u8,82u8,180u8,134u8,91u8,205u8,112u8,60u8,31u8,129u8,44u8,108u8,120u8,252u8,208u8,85u8, - 213u8,204u8,12u8,73u8,164u8,120u8,244u8,32u8,161u8,70u8,211u8,130u8,24u8,65u8,2u8,247u8,20u8,22u8,194u8,79u8, - 8u8,53u8,46u8,78u8,174u8,18u8,61u8,176u8,246u8,17u8,242u8,31u8,58u8,166u8,43u8,108u8,179u8,148u8,11u8,94u8, - 101u8,183u8,186u8,202u8,70u8,119u8,94u8,78u8,248u8,246u8,203u8,253u8,86u8,68u8,12u8,149u8,184u8,97u8,150u8,97u8, - 108u8,233u8,90u8,149u8,56u8,8u8,172u8,62u8,3u8,77u8,77u8,151u8,173u8,62u8,6u8,143u8,184u8,1u8,106u8,117u8, - 224u8,150u8,78u8,243u8,49u8,198u8,193u8,181u8,218u8,14u8,14u8,193u8,105u8,13u8,85u8,30u8,15u8,146u8,82u8,64u8, - 82u8,14u8,151u8,190u8,147u8,206u8,24u8,198u8,116u8,226u8,114u8,18u8,114u8,98u8,169u8,206u8,222u8,126u8,68u8,180u8, - 236u8,25u8,40u8,59u8,159u8,119u8,34u8,171u8,110u8,99u8,170u8,203u8,251u8,148u8,161u8,63u8,208u8,170u8,121u8,53u8, - 204u8,146u8,59u8,73u8,69u8,160u8,156u8,190u8,140u8,202u8,46u8,243u8,153u8,164u8,88u8,116u8,229u8,180u8,196u8,216u8, - 14u8,90u8,243u8,184u8,226u8,169u8,112u8,37u8,212u8,219u8,172u8,144u8,194u8,143u8,57u8,218u8,69u8,50u8,3u8,116u8, - 151u8,186u8,227u8,28u8,109u8,206u8,14u8,195u8,35u8,103u8,59u8,118u8,214u8,170u8,176u8,96u8,183u8,162u8,198u8,102u8, - 245u8,19u8,165u8,186u8,140u8,103u8,8u8,221u8,217u8,42u8,87u8,143u8,236u8,113u8,187u8,86u8,161u8,39u8,115u8,193u8, - 246u8,97u8,74u8,133u8,132u8,224u8,78u8,58u8,159u8,76u8,54u8,83u8,129u8,234u8,23u8,89u8,65u8,57u8,125u8,90u8, - 152u8,50u8,233u8,85u8,54u8,254u8,142u8,244u8,184u8,237u8,234u8,135u8,136u8,61u8,68u8,155u8,83u8,55u8,2u8,21u8, - 137u8,157u8,153u8,236u8,242u8,60u8,210u8,1u8,230u8,201u8,184u8,63u8,130u8,120u8,217u8,33u8,210u8,134u8,39u8,141u8, - 26u8,17u8,74u8,69u8,100u8,215u8,124u8,2u8,236u8,120u8,159u8,129u8,189u8,112u8,114u8,99u8,227u8,7u8,98u8,170u8, - 32u8,173u8,72u8,67u8,225u8,33u8,135u8,199u8,63u8,64u8,33u8,214u8,207u8,250u8,78u8,194u8,40u8,58u8,236u8,76u8, - 85u8,159u8,153u8,117u8,209u8,48u8,179u8,0u8,90u8,146u8,233u8,166u8,73u8,118u8,128u8,106u8,112u8,23u8,176u8,224u8, - 202u8,133u8,47u8,177u8,50u8,123u8,242u8,204u8,62u8,246u8,218u8,66u8,81u8,94u8,45u8,87u8,196u8,55u8,151u8,129u8, - 59u8,248u8,209u8,97u8,14u8,131u8,41u8,16u8,21u8,40u8,104u8,38u8,169u8,55u8,220u8,47u8,35u8,130u8,130u8,171u8, - 251u8,138u8,181u8,160u8,148u8,163u8,203u8,207u8,79u8,182u8,252u8,1u8,62u8,60u8,13u8,139u8,191u8,60u8,196u8,221u8, - 6u8,67u8,134u8,133u8,99u8,71u8,179u8,112u8,106u8,139u8,210u8,244u8,81u8,224u8,170u8,5u8,72u8,158u8,177u8,105u8, - 203u8,149u8,132u8,3u8,248u8,173u8,240u8,53u8,70u8,174u8,99u8,97u8,67u8,101u8,253u8,173u8,83u8,192u8,29u8,154u8, - 127u8,195u8,58u8,105u8,250u8,18u8,186u8,213u8,210u8,66u8,171u8,5u8,202u8,207u8,212u8,58u8,115u8,225u8,221u8,13u8, - 2u8,135u8,11u8,19u8,47u8,131u8,189u8,110u8,37u8,226u8,213u8,48u8,215u8,131u8,182u8,91u8,51u8,84u8,205u8,177u8, - 194u8,187u8,170u8,174u8,172u8,218u8,137u8,42u8,87u8,150u8,40u8,189u8,53u8,81u8,158u8,100u8,122u8,211u8,117u8,132u8, - 3u8,193u8,15u8,53u8,213u8,242u8,53u8,200u8,189u8,110u8,84u8,38u8,229u8,183u8,158u8,226u8,167u8,105u8,90u8,200u8, - 156u8,240u8,148u8,170u8,159u8,82u8,188u8,233u8,12u8,164u8,20u8,246u8,236u8,167u8,216u8,139u8,252u8,236u8,121u8,69u8, - 212u8,178u8,117u8,52u8,178u8,62u8,90u8,41u8,222u8,34u8,255u8,191u8,161u8,60u8,37u8,36u8,178u8,26u8,146u8,78u8, - 191u8,27u8,29u8,157u8,158u8,102u8,195u8,140u8,180u8,249u8,84u8,187u8,81u8,13u8,100u8,153u8,134u8,203u8,244u8,101u8, - 56u8,23u8,171u8,157u8,99u8,68u8,166u8,32u8,66u8,51u8,229u8,138u8,210u8,242u8,155u8,106u8,241u8,207u8,185u8,228u8, - 47u8,152u8,121u8,45u8,189u8,150u8,251u8,173u8,164u8,108u8,33u8,87u8,105u8,159u8,124u8,97u8,157u8,242u8,93u8,22u8, - 112u8,144u8,205u8,46u8,185u8,182u8,58u8,243u8,55u8,95u8,0u8,158u8,95u8,191u8,224u8,49u8,55u8,81u8,129u8,146u8, - 218u8,233u8,141u8,10u8,79u8,144u8,162u8,78u8,46u8,21u8,90u8,168u8,221u8,193u8,135u8,135u8,56u8,43u8,92u8,188u8, - 30u8,179u8,234u8,84u8,84u8,255u8,73u8,78u8,134u8,136u8,116u8,170u8,33u8,149u8,115u8,222u8,114u8,44u8,44u8,236u8, - 198u8,120u8,105u8,16u8,11u8,115u8,91u8,6u8,41u8,99u8,179u8,103u8,55u8,187u8,156u8,53u8,194u8,14u8,138u8,178u8, - 249u8,36u8,166u8,58u8,10u8,120u8,14u8,45u8,222u8,174u8,192u8,86u8,148u8,173u8,114u8,254u8,55u8,228u8,52u8,219u8, - 42u8,97u8,252u8,246u8,61u8,222u8,135u8,63u8,110u8,179u8,206u8,234u8,34u8,14u8,238u8,235u8,148u8,66u8,173u8,44u8, - 152u8,211u8,28u8,217u8,172u8,198u8,156u8,210u8,148u8,63u8,106u8,72u8,38u8,80u8,144u8,164u8,2u8,171u8,242u8,33u8, - 41u8,236u8,113u8,129u8,188u8,155u8,76u8,153u8,214u8,96u8,157u8,215u8,90u8,157u8,136u8,199u8,158u8,101u8,37u8,209u8, - 186u8,250u8,161u8,46u8,67u8,172u8,213u8,245u8,184u8,69u8,69u8,229u8,235u8,170u8,124u8,221u8,58u8,92u8,222u8,96u8, - 96u8,152u8,199u8,110u8,218u8,137u8,252u8,216u8,2u8,98u8,176u8,108u8,77u8,205u8,91u8,63u8,158u8,203u8,96u8,230u8, - 83u8,115u8,232u8,86u8,64u8,57u8,129u8,212u8,91u8,217u8,89u8,109u8,239u8,139u8,21u8,14u8,183u8,194u8,229u8,96u8, - 157u8,103u8,235u8,171u8,185u8,238u8,239u8,104u8,247u8,84u8,26u8,55u8,255u8,96u8,60u8,226u8,109u8,88u8,234u8,208u8, - 152u8,60u8,106u8,53u8,211u8,81u8,214u8,26u8,124u8,28u8,98u8,96u8,240u8,222u8,81u8,153u8,102u8,46u8,49u8,204u8, - 103u8,12u8,99u8,33u8,150u8,33u8,125u8,88u8,39u8,160u8,227u8,47u8,249u8,11u8,88u8,216u8,82u8,108u8,43u8,81u8, - 177u8,251u8,25u8,175u8,5u8,103u8,146u8,201u8,57u8,245u8,56u8,29u8,63u8,92u8,161u8,120u8,241u8,178u8,246u8,82u8, - 72u8,68u8,46u8,94u8,114u8,58u8,135u8,139u8,80u8,20u8,61u8,54u8,50u8,18u8,169u8,188u8,235u8,70u8,166u8,22u8, - 203u8,129u8,232u8,167u8,124u8,154u8,98u8,125u8,113u8,76u8,103u8,6u8,234u8,153u8,225u8,124u8,172u8,212u8,60u8,130u8, - 219u8,155u8,12u8,81u8,188u8,83u8,141u8,189u8,178u8,185u8,227u8,53u8,42u8,32u8,76u8,216u8,54u8,51u8,45u8,140u8, - 200u8,66u8,236u8,168u8,98u8,14u8,237u8,168u8,196u8,174u8,19u8,77u8,253u8,187u8,167u8,170u8,43u8,184u8,29u8,51u8, - 59u8,185u8,209u8,81u8,241u8,106u8,17u8,81u8,171u8,82u8,235u8,50u8,143u8,160u8,19u8,92u8,181u8,81u8,89u8,207u8, - 251u8,118u8,202u8,114u8,179u8,27u8,90u8,95u8,176u8,250u8,170u8,246u8,37u8,134u8,65u8,212u8,65u8,3u8,68u8,248u8, - 45u8,212u8,144u8,5u8,113u8,46u8,133u8,222u8,128u8,68u8,161u8,170u8,204u8,34u8,107u8,136u8,144u8,82u8,64u8,87u8, - 118u8,87u8,40u8,49u8,252u8,218u8,223u8,183u8,117u8,68u8,15u8,249u8,166u8,234u8,61u8,227u8,174u8,98u8,214u8,69u8, - 122u8,96u8,1u8,167u8,43u8,102u8,234u8,62u8,74u8,134u8,171u8,111u8,164u8,52u8,50u8,215u8,153u8,208u8,240u8,229u8, - 236u8,175u8,163u8,91u8,25u8,3u8,200u8,61u8,165u8,235u8,95u8,185u8,204u8,61u8,250u8,50u8,170u8,27u8,90u8,189u8, - 125u8,193u8,198u8,220u8,232u8,241u8,203u8,94u8,86u8,118u8,228u8,218u8,85u8,243u8,166u8,109u8,98u8,10u8,188u8,157u8, - 138u8,189u8,159u8,29u8,102u8,137u8,203u8,148u8,47u8,15u8,112u8,36u8,239u8,254u8,81u8,42u8,7u8,102u8,30u8,79u8, - 3u8,0u8,26u8,110u8,88u8,117u8,119u8,198u8,46u8,204u8,176u8,65u8,79u8,253u8,137u8,124u8,171u8,32u8,7u8,208u8, - 82u8,179u8,93u8,192u8,115u8,12u8,191u8,17u8,130u8,33u8,62u8,107u8,133u8,107u8,43u8,203u8,147u8,29u8,109u8,76u8, - 29u8,115u8,9u8,38u8,230u8,48u8,83u8,133u8,172u8,146u8,205u8,99u8,109u8,60u8,199u8,215u8,141u8,221u8,58u8,119u8, - 105u8,62u8,193u8,58u8,237u8,33u8,35u8,188u8,145u8,95u8,234u8,114u8,16u8,180u8,156u8,93u8,11u8,99u8,3u8,169u8, - 133u8,187u8,115u8,143u8,30u8,121u8,31u8,97u8,43u8,79u8,135u8,105u8,131u8,18u8,135u8,184u8,203u8,76u8,107u8,177u8, - 141u8,1u8,180u8,70u8,153u8,52u8,67u8,117u8,196u8,70u8,183u8,235u8,22u8,35u8,102u8,201u8,165u8,12u8,152u8,240u8, - 184u8,90u8,205u8,60u8,254u8,149u8,253u8,74u8,76u8,180u8,221u8,173u8,82u8,132u8,210u8,39u8,79u8,215u8,164u8,8u8, - 102u8,128u8,57u8,180u8,140u8,153u8,202u8,66u8,122u8,225u8,213u8,36u8,145u8,106u8,221u8,172u8,159u8,141u8,18u8,67u8, - 163u8,253u8,155u8,87u8,185u8,222u8,229u8,133u8,20u8,70u8,254u8,9u8,78u8,176u8,249u8,220u8,22u8,243u8,167u8,240u8, - 180u8,217u8,135u8,112u8,231u8,174u8,178u8,209u8,60u8,25u8,215u8,24u8,75u8,53u8,217u8,97u8,108u8,31u8,94u8,65u8, - 100u8,210u8,30u8,59u8,234u8,102u8,175u8,209u8,7u8,78u8,234u8,153u8,207u8,114u8,176u8,146u8,102u8,67u8,233u8,50u8, - 141u8,236u8,32u8,177u8,116u8,126u8,50u8,238u8,1u8,85u8,7u8,174u8,199u8,2u8,61u8,243u8,132u8,111u8,194u8,32u8, - 13u8,232u8,3u8,237u8,212u8,217u8,114u8,84u8,46u8,92u8,127u8,113u8,217u8,177u8,254u8,124u8,81u8,244u8,170u8,204u8, - 202u8,244u8,44u8,156u8,122u8,106u8,73u8,17u8,172u8,132u8,70u8,236u8,192u8,205u8,102u8,85u8,195u8,32u8,23u8,52u8, - 68u8,84u8,103u8,156u8,98u8,231u8,89u8,145u8,105u8,171u8,122u8,110u8,30u8,21u8,170u8,36u8,67u8,62u8,155u8,168u8, - 49u8,107u8,72u8,9u8,38u8,211u8,215u8,195u8,66u8,194u8,210u8,246u8,96u8,26u8,47u8,170u8,212u8,94u8,218u8,42u8, - 240u8,216u8,83u8,41u8,148u8,57u8,233u8,10u8,211u8,216u8,96u8,64u8,51u8,172u8,30u8,139u8,0u8,1u8,45u8,255u8, - 165u8,173u8,163u8,139u8,67u8,17u8,48u8,182u8,78u8,124u8,124u8,19u8,240u8,239u8,96u8,77u8,56u8,134u8,79u8,135u8, - 161u8,154u8,10u8,8u8,111u8,59u8,209u8,35u8,86u8,211u8,221u8,52u8,77u8,172u8,35u8,176u8,69u8,143u8,142u8,197u8, - 7u8,82u8,141u8,94u8,225u8,224u8,191u8,176u8,34u8,158u8,81u8,178u8,175u8,37u8,109u8,93u8,29u8,247u8,88u8,40u8, - 49u8,42u8,154u8,211u8,61u8,253u8,123u8,88u8,209u8,88u8,97u8,205u8,228u8,89u8,142u8,51u8,220u8,4u8,95u8,132u8, - 188u8,121u8,220u8,159u8,9u8,107u8,39u8,55u8,242u8,211u8,249u8,212u8,75u8,218u8,191u8,78u8,115u8,244u8,200u8,37u8, - 203u8,212u8,104u8,148u8,25u8,152u8,71u8,10u8,94u8,84u8,128u8,161u8,39u8,175u8,246u8,32u8,121u8,135u8,179u8,146u8, - 181u8,25u8,212u8,48u8,239u8,148u8,51u8,143u8,227u8,123u8,252u8,205u8,147u8,173u8,141u8,13u8,198u8,221u8,34u8,213u8, - 177u8,220u8,0u8,108u8,32u8,234u8,28u8,150u8,171u8,67u8,111u8,143u8,104u8,148u8,220u8,68u8,173u8,158u8,254u8,20u8, - 31u8,49u8,145u8,160u8,87u8,237u8,238u8,103u8,12u8,237u8,109u8,184u8,33u8,64u8,137u8,152u8,150u8,158u8,177u8,150u8, - 35u8,197u8,31u8,252u8,195u8,113u8,182u8,161u8,249u8,185u8,52u8,155u8,134u8,91u8,50u8,72u8,149u8,91u8,71u8,253u8, - 71u8,39u8,50u8,127u8,139u8,71u8,233u8,36u8,7u8,37u8,130u8,140u8,207u8,8u8,188u8,180u8,176u8,61u8,53u8,111u8, - 217u8,223u8,123u8,239u8,56u8,1u8,88u8,43u8,97u8,253u8,138u8,239u8,53u8,21u8,123u8,4u8,221u8,179u8,243u8,172u8, - 7u8,112u8,217u8,111u8,229u8,108u8,106u8,199u8,73u8,204u8,96u8,108u8,65u8,240u8,27u8,115u8,59u8,184u8,84u8,236u8, - 230u8,42u8,25u8,165u8,243u8,162u8,235u8,245u8,140u8,9u8,15u8,119u8,229u8,12u8,25u8,144u8,23u8,200u8,57u8,245u8, - 47u8,195u8,27u8,212u8,224u8,114u8,104u8,96u8,42u8,26u8,179u8,49u8,244u8,167u8,76u8,101u8,62u8,219u8,228u8,143u8, - 198u8,234u8,9u8,17u8,36u8,175u8,130u8,74u8,147u8,210u8,36u8,205u8,163u8,94u8,203u8,144u8,80u8,39u8,75u8,85u8, - 38u8,162u8,242u8,198u8,55u8,157u8,42u8,41u8,196u8,142u8,15u8,62u8,77u8,147u8,25u8,250u8,151u8,237u8,236u8,12u8, - 129u8,222u8,166u8,56u8,244u8,100u8,20u8,27u8,152u8,118u8,150u8,128u8,109u8,22u8,186u8,181u8,88u8,175u8,58u8,252u8, - 38u8,158u8,37u8,152u8,227u8,67u8,149u8,59u8,8u8,191u8,129u8,144u8,151u8,50u8,246u8,222u8,110u8,9u8,196u8,151u8, - 148u8,233u8,99u8,156u8,220u8,100u8,150u8,100u8,147u8,162u8,85u8,182u8,245u8,202u8,238u8,241u8,142u8,62u8,184u8,175u8, - 168u8,219u8,18u8,112u8,164u8,75u8,125u8,16u8,138u8,47u8,206u8,54u8,61u8,187u8,118u8,35u8,191u8,47u8,3u8,121u8, - 28u8,63u8,16u8,229u8,112u8,235u8,4u8,154u8,43u8,247u8,17u8,145u8,117u8,197u8,202u8,43u8,92u8,25u8,130u8,46u8, - 195u8,207u8,93u8,49u8,129u8,28u8,169u8,85u8,114u8,86u8,108u8,34u8,74u8,239u8,53u8,77u8,151u8,75u8,39u8,82u8, - 17u8,3u8,30u8,28u8,161u8,162u8,34u8,94u8,189u8,99u8,62u8,79u8,191u8,74u8,48u8,11u8,133u8,138u8,87u8,121u8, - 219u8,47u8,27u8,32u8,237u8,19u8,102u8,54u8,112u8,160u8,244u8,107u8,122u8,35u8,90u8,45u8,23u8,40u8,109u8,19u8, - 177u8,5u8,178u8,154u8,236u8,91u8,198u8,66u8,245u8,158u8,16u8,96u8,74u8,41u8,94u8,155u8,20u8,99u8,123u8,160u8, - 150u8,147u8,83u8,216u8,76u8,158u8,244u8,89u8,29u8,83u8,97u8,154u8,57u8,92u8,197u8,105u8,97u8,50u8,143u8,138u8, - 175u8,76u8,190u8,98u8,52u8,107u8,211u8,207u8,198u8,206u8,54u8,17u8,51u8,17u8,143u8,133u8,156u8,121u8,129u8,113u8, - 104u8,231u8,57u8,5u8,173u8,145u8,116u8,233u8,118u8,108u8,245u8,218u8,97u8,41u8,24u8,248u8,16u8,199u8,53u8,241u8, - 42u8,123u8,112u8,250u8,237u8,37u8,4u8,215u8,58u8,73u8,213u8,29u8,133u8,110u8,110u8,141u8,240u8,42u8,84u8,239u8, - 252u8,92u8,168u8,50u8,113u8,65u8,54u8,81u8,208u8,182u8,98u8,204u8,248u8,13u8,167u8,40u8,145u8,12u8,59u8,184u8, - 47u8,95u8,16u8,123u8,126u8,115u8,240u8,118u8,239u8,205u8,139u8,227u8,248u8,205u8,222u8,201u8,65u8,29u8,203u8,150u8, - 71u8,100u8,99u8,42u8,78u8,7u8,172u8,93u8,48u8,125u8,152u8,78u8,75u8,250u8,155u8,136u8,111u8,190u8,208u8,77u8, - 17u8,55u8,224u8,87u8,211u8,196u8,112u8,49u8,31u8,207u8,178u8,203u8,113u8,150u8,78u8,219u8,110u8,109u8,179u8,81u8, - 174u8,191u8,29u8,10u8,130u8,8u8,219u8,133u8,86u8,32u8,153u8,21u8,36u8,194u8,247u8,191u8,170u8,6u8,148u8,92u8, - 229u8,153u8,72u8,98u8,168u8,223u8,32u8,68u8,164u8,209u8,84u8,134u8,169u8,245u8,232u8,110u8,89u8,81u8,137u8,93u8, - 86u8,6u8,129u8,125u8,146u8,17u8,241u8,187u8,74u8,33u8,36u8,151u8,161u8,76u8,80u8,176u8,0u8,75u8,182u8,182u8, - 190u8,8u8,110u8,179u8,111u8,192u8,50u8,71u8,53u8,238u8,11u8,142u8,27u8,186u8,75u8,222u8,80u8,238u8,147u8,194u8, - 31u8,5u8,121u8,3u8,55u8,202u8,115u8,63u8,141u8,89u8,171u8,229u8,111u8,194u8,99u8,110u8,138u8,34u8,210u8,244u8, - 201u8,86u8,187u8,58u8,194u8,112u8,163u8,252u8,218u8,43u8,133u8,153u8,77u8,244u8,226u8,29u8,159u8,63u8,172u8,96u8, - 103u8,198u8,68u8,129u8,74u8,231u8,29u8,157u8,195u8,187u8,242u8,46u8,24u8,59u8,140u8,142u8,136u8,197u8,187u8,146u8, - 78u8,86u8,73u8,227u8,4u8,102u8,71u8,240u8,9u8,39u8,45u8,207u8,67u8,165u8,149u8,90u8,91u8,194u8,170u8,72u8, - 88u8,21u8,8u8,91u8,140u8,219u8,205u8,162u8,194u8,138u8,221u8,165u8,14u8,84u8,126u8,196u8,32u8,73u8,144u8,15u8, - 153u8,29u8,59u8,225u8,103u8,12u8,79u8,132u8,27u8,190u8,85u8,27u8,227u8,27u8,143u8,255u8,225u8,85u8,81u8,236u8, - 42u8,224u8,164u8,204u8,150u8,245u8,200u8,86u8,250u8,176u8,123u8,207u8,137u8,204u8,10u8,132u8,111u8,150u8,80u8,184u8, - 174u8,78u8,5u8,91u8,58u8,243u8,234u8,232u8,25u8,84u8,36u8,206u8,145u8,158u8,56u8,178u8,103u8,155u8,165u8,207u8, - 54u8,88u8,243u8,234u8,81u8,25u8,122u8,242u8,4u8,123u8,118u8,2u8,74u8,241u8,158u8,188u8,11u8,50u8,66u8,235u8, - 228u8,57u8,232u8,156u8,250u8,238u8,103u8,230u8,86u8,73u8,99u8,167u8,159u8,213u8,246u8,170u8,31u8,76u8,45u8,80u8, - 218u8,59u8,174u8,122u8,157u8,242u8,211u8,252u8,82u8,126u8,216u8,55u8,3u8,79u8,63u8,184u8,179u8,117u8,98u8,135u8, - 175u8,96u8,118u8,114u8,102u8,250u8,253u8,114u8,8u8,26u8,125u8,152u8,57u8,10u8,102u8,102u8,188u8,161u8,17u8,156u8, - 230u8,92u8,147u8,166u8,190u8,134u8,87u8,43u8,7u8,114u8,168u8,34u8,78u8,209u8,111u8,210u8,230u8,152u8,237u8,68u8, - 87u8,255u8,250u8,77u8,216u8,6u8,209u8,124u8,199u8,24u8,34u8,24u8,167u8,161u8,74u8,43u8,29u8,69u8,91u8,216u8, - 74u8,197u8,43u8,244u8,251u8,177u8,199u8,96u8,166u8,41u8,243u8,18u8,169u8,96u8,43u8,10u8,217u8,202u8,218u8,139u8, - 59u8,67u8,168u8,31u8,21u8,32u8,44u8,44u8,218u8,106u8,187u8,7u8,25u8,242u8,196u8,177u8,142u8,205u8,116u8,197u8, - 145u8,250u8,99u8,169u8,33u8,86u8,58u8,101u8,71u8,255u8,218u8,54u8,62u8,65u8,60u8,112u8,29u8,175u8,87u8,76u8, - 194u8,29u8,130u8,77u8,114u8,21u8,122u8,113u8,218u8,24u8,111u8,192u8,235u8,56u8,55u8,184u8,212u8,128u8,179u8,252u8, - 80u8,69u8,16u8,155u8,134u8,12u8,35u8,221u8,144u8,237u8,59u8,40u8,194u8,130u8,233u8,3u8,199u8,116u8,160u8,255u8, - 45u8,35u8,45u8,98u8,46u8,53u8,162u8,56u8,144u8,38u8,155u8,97u8,158u8,130u8,39u8,190u8,75u8,141u8,249u8,220u8, - 77u8,242u8,209u8,160u8,172u8,175u8,231u8,174u8,91u8,194u8,170u8,133u8,81u8,209u8,119u8,61u8,5u8,134u8,195u8,106u8, - 47u8,118u8,181u8,95u8,218u8,19u8,255u8,50u8,52u8,151u8,26u8,177u8,122u8,21u8,216u8,238u8,109u8,171u8,76u8,170u8, - 174u8,203u8,193u8,240u8,185u8,143u8,43u8,178u8,45u8,173u8,57u8,144u8,129u8,157u8,94u8,44u8,146u8,166u8,216u8,38u8, - 201u8,22u8,167u8,162u8,174u8,232u8,30u8,170u8,123u8,194u8,8u8,200u8,235u8,73u8,236u8,241u8,101u8,228u8,110u8,171u8, - 49u8,148u8,229u8,43u8,141u8,218u8,164u8,210u8,240u8,205u8,39u8,186u8,9u8,230u8,16u8,128u8,225u8,221u8,216u8,26u8, - 144u8,102u8,108u8,221u8,97u8,173u8,75u8,25u8,35u8,255u8,176u8,233u8,177u8,154u8,237u8,206u8,179u8,221u8,122u8,39u8, - 182u8,47u8,42u8,143u8,26u8,12u8,180u8,27u8,140u8,147u8,66u8,56u8,227u8,249u8,209u8,9u8,6u8,216u8,191u8,62u8, - 122u8,123u8,240u8,38u8,62u8,124u8,181u8,255u8,230u8,96u8,239u8,248u8,32u8,62u8,248u8,231u8,254u8,193u8,1u8,188u8, - 96u8,95u8,30u8,254u8,116u8,120u8,82u8,161u8,79u8,242u8,228u8,161u8,38u8,41u8,7u8,74u8,193u8,130u8,169u8,207u8, - 83u8,147u8,173u8,160u8,182u8,150u8,194u8,235u8,163u8,163u8,151u8,241u8,139u8,163u8,131u8,227u8,24u8,51u8,147u8,28u8, - 252u8,243u8,240u8,248u8,164u8,29u8,168u8,122u8,4u8,34u8,51u8,152u8,188u8,41u8,84u8,35u8,218u8,219u8,127u8,73u8, - 174u8,22u8,39u8,160u8,247u8,155u8,80u8,161u8,219u8,41u8,6u8,102u8,22u8,221u8,232u8,16u8,94u8,196u8,226u8,195u8, - 142u8,208u8,11u8,0u8,71u8,166u8,0u8,111u8,16u8,99u8,18u8,84u8,174u8,202u8,31u8,84u8,112u8,119u8,226u8,134u8, - 220u8,232u8,241u8,40u8,253u8,14u8,118u8,148u8,240u8,40u8,82u8,107u8,106u8,90u8,255u8,224u8,193u8,143u8,57u8,187u8, - 232u8,53u8,66u8,186u8,80u8,223u8,177u8,3u8,131u8,234u8,49u8,242u8,121u8,152u8,95u8,192u8,252u8,47u8,50u8,10u8, - 36u8,87u8,174u8,30u8,56u8,84u8,129u8,222u8,4u8,152u8,210u8,93u8,198u8,220u8,203u8,193u8,100u8,144u8,34u8,185u8, - 142u8,192u8,206u8,224u8,76u8,211u8,9u8,106u8,135u8,41u8,196u8,251u8,39u8,80u8,60u8,79u8,148u8,3u8,45u8,188u8, - 249u8,230u8,195u8,25u8,86u8,199u8,193u8,250u8,58u8,191u8,148u8,206u8,39u8,168u8,116u8,69u8,239u8,125u8,227u8,212u8, - 68u8,110u8,179u8,50u8,49u8,129u8,60u8,39u8,249u8,4u8,148u8,59u8,110u8,228u8,186u8,16u8,20u8,4u8,180u8,241u8, - 177u8,44u8,223u8,99u8,184u8,53u8,181u8,28u8,2u8,103u8,38u8,150u8,15u8,141u8,99u8,144u8,90u8,127u8,190u8,54u8, - 189u8,117u8,160u8,87u8,167u8,13u8,14u8,211u8,218u8,226u8,166u8,152u8,165u8,23u8,70u8,57u8,214u8,29u8,137u8,244u8, - 78u8,151u8,10u8,16u8,72u8,88u8,254u8,38u8,43u8,146u8,122u8,179u8,126u8,222u8,10u8,76u8,212u8,123u8,20u8,160u8, - 206u8,28u8,94u8,127u8,110u8,251u8,14u8,183u8,19u8,122u8,7u8,201u8,27u8,169u8,242u8,229u8,71u8,123u8,37u8,32u8, - 176u8,92u8,105u8,129u8,25u8,59u8,105u8,119u8,68u8,199u8,174u8,158u8,202u8,174u8,158u8,85u8,5u8,73u8,129u8,103u8, - 145u8,236u8,216u8,146u8,173u8,195u8,137u8,80u8,170u8,80u8,160u8,102u8,211u8,61u8,238u8,234u8,238u8,54u8,230u8,163u8, - 89u8,122u8,227u8,26u8,141u8,200u8,167u8,12u8,210u8,102u8,161u8,71u8,238u8,254u8,117u8,162u8,71u8,242u8,199u8,118u8, - 45u8,69u8,230u8,243u8,93u8,87u8,82u8,98u8,185u8,87u8,124u8,122u8,107u8,182u8,86u8,238u8,209u8,219u8,87u8,192u8, - 68u8,246u8,247u8,94u8,19u8,237u8,253u8,254u8,232u8,231u8,87u8,47u8,28u8,218u8,251u8,31u8,255u8,66u8,114u8,20u8, - 163u8,21u8,232u8,87u8,250u8,0u8,221u8,26u8,220u8,107u8,191u8,35u8,62u8,64u8,89u8,243u8,169u8,211u8,168u8,152u8, - 141u8,224u8,97u8,86u8,86u8,27u8,175u8,44u8,15u8,254u8,52u8,48u8,38u8,214u8,136u8,153u8,69u8,7u8,175u8,143u8, - 246u8,127u8,140u8,95u8,252u8,12u8,74u8,218u8,195u8,163u8,87u8,66u8,26u8,223u8,141u8,158u8,108u8,84u8,247u8,18u8, - 213u8,7u8,227u8,253u8,255u8,217u8,127u8,121u8,16u8,31u8,31u8,236u8,31u8,189u8,122u8,113u8,172u8,250u8,110u8,62u8, - 217u8,8u8,246u8,182u8,42u8,95u8,234u8,58u8,105u8,192u8,113u8,98u8,108u8,24u8,164u8,124u8,230u8,185u8,48u8,253u8, - 226u8,33u8,104u8,241u8,243u8,11u8,159u8,2u8,160u8,12u8,128u8,127u8,225u8,63u8,220u8,148u8,59u8,132u8,206u8,208u8, - 196u8,108u8,186u8,177u8,81u8,115u8,88u8,198u8,34u8,152u8,170u8,38u8,122u8,49u8,165u8,139u8,19u8,46u8,164u8,60u8, - 173u8,215u8,212u8,253u8,31u8,102u8,73u8,112u8,208u8,92u8,216u8,77u8,240u8,64u8,143u8,78u8,95u8,235u8,227u8,236u8, - 172u8,161u8,226u8,182u8,112u8,169u8,137u8,65u8,202u8,23u8,207u8,51u8,65u8,53u8,60u8,23u8,170u8,128u8,223u8,84u8, - 163u8,66u8,102u8,75u8,198u8,50u8,95u8,190u8,23u8,152u8,138u8,228u8,162u8,68u8,106u8,218u8,38u8,170u8,232u8,62u8, - 203u8,85u8,161u8,123u8,87u8,218u8,39u8,149u8,143u8,223u8,149u8,185u8,15u8,37u8,140u8,220u8,212u8,133u8,113u8,21u8, - 228u8,3u8,53u8,4u8,59u8,122u8,158u8,157u8,114u8,92u8,3u8,20u8,83u8,233u8,177u8,178u8,24u8,161u8,162u8,198u8, - 238u8,185u8,184u8,20u8,87u8,127u8,209u8,10u8,137u8,156u8,85u8,24u8,122u8,154u8,20u8,132u8,147u8,228u8,121u8,3u8, - 155u8,32u8,75u8,214u8,240u8,130u8,232u8,103u8,66u8,152u8,185u8,63u8,76u8,40u8,205u8,200u8,162u8,224u8,174u8,208u8, - 175u8,72u8,27u8,51u8,26u8,168u8,67u8,190u8,113u8,134u8,169u8,90u8,190u8,83u8,37u8,171u8,198u8,47u8,74u8,66u8, - 65u8,32u8,156u8,65u8,12u8,40u8,222u8,86u8,107u8,89u8,25u8,116u8,251u8,19u8,48u8,225u8,102u8,248u8,116u8,210u8, - 238u8,145u8,167u8,66u8,24u8,165u8,135u8,19u8,152u8,228u8,49u8,16u8,192u8,169u8,226u8,168u8,137u8,149u8,112u8,213u8, - 236u8,46u8,67u8,21u8,21u8,117u8,51u8,226u8,110u8,3u8,242u8,161u8,110u8,97u8,5u8,115u8,57u8,118u8,2u8,171u8, - 38u8,155u8,103u8,67u8,112u8,92u8,112u8,113u8,191u8,156u8,54u8,225u8,100u8,198u8,38u8,65u8,49u8,117u8,198u8,164u8, - 162u8,211u8,230u8,159u8,197u8,205u8,22u8,174u8,30u8,204u8,121u8,235u8,153u8,6u8,14u8,3u8,157u8,76u8,87u8,232u8, - 84u8,212u8,111u8,142u8,65u8,114u8,143u8,41u8,251u8,28u8,8u8,220u8,106u8,123u8,1u8,108u8,67u8,25u8,181u8,70u8, - 15u8,225u8,41u8,81u8,245u8,81u8,86u8,192u8,255u8,240u8,52u8,228u8,234u8,186u8,195u8,49u8,72u8,91u8,144u8,116u8, - 216u8,157u8,45u8,21u8,91u8,97u8,182u8,246u8,87u8,86u8,85u8,62u8,199u8,145u8,139u8,65u8,128u8,142u8,47u8,190u8, - 178u8,231u8,95u8,225u8,19u8,166u8,79u8,191u8,153u8,223u8,88u8,88u8,7u8,234u8,156u8,61u8,159u8,226u8,218u8,146u8, - 113u8,27u8,218u8,89u8,56u8,235u8,77u8,107u8,0u8,10u8,84u8,81u8,135u8,68u8,155u8,81u8,80u8,90u8,215u8,114u8, - 25u8,127u8,72u8,213u8,2u8,45u8,220u8,101u8,124u8,246u8,105u8,24u8,177u8,2u8,237u8,139u8,47u8,149u8,182u8,27u8, - 120u8,177u8,207u8,166u8,249u8,77u8,172u8,230u8,104u8,86u8,0u8,80u8,159u8,85u8,100u8,152u8,116u8,60u8,189u8,133u8, - 46u8,178u8,48u8,42u8,97u8,200u8,98u8,170u8,42u8,97u8,43u8,233u8,142u8,202u8,66u8,105u8,240u8,40u8,199u8,217u8, - 137u8,189u8,48u8,75u8,33u8,84u8,17u8,55u8,177u8,30u8,108u8,107u8,214u8,227u8,170u8,170u8,103u8,210u8,196u8,66u8, - 121u8,43u8,54u8,54u8,195u8,162u8,230u8,89u8,210u8,154u8,178u8,94u8,209u8,91u8,61u8,214u8,106u8,170u8,195u8,213u8, - 173u8,84u8,76u8,104u8,154u8,158u8,1u8,86u8,167u8,83u8,243u8,176u8,213u8,227u8,39u8,80u8,26u8,135u8,75u8,164u8, - 174u8,186u8,116u8,252u8,35u8,105u8,55u8,23u8,126u8,169u8,47u8,250u8,16u8,2u8,100u8,25u8,165u8,239u8,106u8,68u8, - 150u8,91u8,118u8,103u8,153u8,66u8,72u8,230u8,110u8,51u8,213u8,176u8,202u8,57u8,242u8,109u8,62u8,44u8,244u8,98u8, - 161u8,70u8,165u8,9u8,177u8,20u8,193u8,181u8,208u8,90u8,43u8,242u8,251u8,50u8,106u8,211u8,39u8,128u8,30u8,151u8, - 225u8,252u8,172u8,171u8,150u8,148u8,48u8,125u8,1u8,213u8,229u8,216u8,77u8,159u8,8u8,238u8,43u8,190u8,179u8,210u8, - 57u8,174u8,63u8,37u8,82u8,165u8,78u8,75u8,55u8,115u8,217u8,188u8,194u8,217u8,29u8,193u8,58u8,226u8,132u8,189u8, - 59u8,101u8,119u8,143u8,103u8,104u8,0u8,200u8,162u8,102u8,104u8,96u8,161u8,223u8,75u8,206u8,224u8,205u8,176u8,29u8, - 206u8,25u8,216u8,252u8,81u8,164u8,63u8,91u8,195u8,227u8,200u8,255u8,210u8,220u8,37u8,166u8,180u8,121u8,185u8,38u8, - 246u8,125u8,212u8,113u8,3u8,182u8,189u8,15u8,92u8,198u8,29u8,118u8,168u8,96u8,232u8,141u8,49u8,184u8,119u8,227u8, - 63u8,56u8,128u8,131u8,183u8,192u8,29u8,135u8,121u8,200u8,25u8,227u8,212u8,156u8,225u8,234u8,47u8,187u8,198u8,244u8, - 72u8,98u8,89u8,160u8,86u8,74u8,253u8,43u8,193u8,13u8,168u8,53u8,148u8,185u8,188u8,30u8,219u8,166u8,112u8,101u8, - 43u8,134u8,208u8,61u8,247u8,5u8,114u8,210u8,11u8,50u8,217u8,12u8,44u8,124u8,112u8,92u8,48u8,220u8,0u8,127u8, - 62u8,13u8,11u8,37u8,65u8,113u8,194u8,241u8,195u8,139u8,107u8,87u8,199u8,36u8,151u8,180u8,195u8,13u8,171u8,10u8, - 131u8,12u8,228u8,68u8,121u8,207u8,0u8,49u8,246u8,207u8,239u8,21u8,136u8,231u8,101u8,34u8,159u8,171u8,188u8,8u8, - 36u8,187u8,3u8,246u8,240u8,133u8,55u8,87u8,223u8,36u8,102u8,74u8,215u8,108u8,96u8,111u8,192u8,77u8,34u8,16u8, - 136u8,234u8,86u8,158u8,222u8,169u8,87u8,233u8,116u8,88u8,64u8,50u8,109u8,146u8,137u8,148u8,131u8,191u8,255u8,157u8, - 111u8,139u8,158u8,244u8,147u8,124u8,148u8,54u8,106u8,236u8,198u8,232u8,115u8,107u8,254u8,224u8,69u8,46u8,91u8,47u8, - 187u8,234u8,4u8,30u8,65u8,131u8,68u8,64u8,114u8,177u8,183u8,173u8,24u8,158u8,167u8,23u8,169u8,55u8,43u8,144u8, - 216u8,253u8,60u8,138u8,168u8,128u8,240u8,18u8,190u8,59u8,44u8,201u8,69u8,29u8,23u8,170u8,87u8,249u8,164u8,40u8, - 115u8,172u8,129u8,73u8,204u8,240u8,24u8,105u8,137u8,88u8,107u8,233u8,218u8,96u8,193u8,113u8,125u8,7u8,118u8,106u8, - 110u8,117u8,195u8,89u8,80u8,126u8,27u8,107u8,18u8,236u8,168u8,203u8,140u8,233u8,91u8,148u8,189u8,45u8,103u8,44u8, - 211u8,86u8,155u8,15u8,237u8,69u8,73u8,53u8,23u8,47u8,235u8,73u8,202u8,14u8,125u8,222u8,9u8,187u8,149u8,250u8, - 187u8,30u8,108u8,234u8,69u8,122u8,88u8,90u8,147u8,42u8,201u8,110u8,201u8,154u8,164u8,74u8,246u8,169u8,54u8,47u8, - 122u8,79u8,23u8,67u8,196u8,16u8,155u8,69u8,102u8,150u8,82u8,102u8,223u8,232u8,184u8,144u8,189u8,15u8,150u8,174u8, - 93u8,21u8,134u8,211u8,36u8,50u8,200u8,243u8,248u8,169u8,234u8,228u8,39u8,255u8,171u8,114u8,29u8,170u8,12u8,179u8, - 143u8,2u8,65u8,60u8,229u8,75u8,127u8,79u8,24u8,220u8,231u8,176u8,181u8,100u8,6u8,23u8,233u8,116u8,41u8,74u8, - 8u8,21u8,139u8,124u8,201u8,128u8,6u8,207u8,121u8,229u8,16u8,100u8,133u8,144u8,170u8,238u8,156u8,29u8,164u8,34u8, - 213u8,191u8,110u8,212u8,36u8,229u8,191u8,135u8,182u8,53u8,185u8,44u8,22u8,169u8,64u8,16u8,44u8,58u8,208u8,252u8, - 178u8,107u8,183u8,196u8,108u8,68u8,110u8,12u8,32u8,228u8,238u8,128u8,46u8,73u8,179u8,193u8,99u8,172u8,74u8,53u8, - 195u8,215u8,96u8,196u8,61u8,17u8,163u8,138u8,71u8,160u8,43u8,79u8,181u8,138u8,247u8,40u8,118u8,163u8,77u8,195u8, - 18u8,242u8,245u8,20u8,80u8,206u8,104u8,249u8,239u8,2u8,190u8,49u8,35u8,253u8,183u8,30u8,21u8,239u8,189u8,250u8, - 212u8,242u8,196u8,113u8,147u8,222u8,135u8,158u8,39u8,84u8,245u8,22u8,159u8,1u8,192u8,233u8,39u8,184u8,235u8,99u8, - 144u8,125u8,222u8,219u8,198u8,23u8,154u8,189u8,13u8,142u8,158u8,14u8,140u8,134u8,197u8,101u8,166u8,48u8,236u8,127u8, - 121u8,252u8,213u8,76u8,190u8,249u8,95u8,27u8,191u8,195u8,164u8,218u8,191u8,74u8,24u8,160u8,243u8,7u8,252u8,65u8, - 181u8,47u8,156u8,238u8,124u8,10u8,90u8,0u8,76u8,104u8,0u8,154u8,143u8,17u8,26u8,25u8,160u8,45u8,152u8,253u8, - 190u8,238u8,224u8,93u8,82u8,97u8,114u8,199u8,233u8,248u8,180u8,253u8,43u8,95u8,164u8,144u8,206u8,94u8,87u8,19u8, - 53u8,98u8,234u8,147u8,73u8,249u8,56u8,137u8,179u8,83u8,176u8,50u8,99u8,1u8,41u8,188u8,215u8,160u8,116u8,213u8, - 6u8,252u8,5u8,100u8,244u8,208u8,91u8,255u8,97u8,191u8,198u8,23u8,211u8,138u8,18u8,34u8,199u8,2u8,147u8,197u8, - 249u8,155u8,89u8,246u8,202u8,75u8,196u8,114u8,9u8,71u8,19u8,243u8,8u8,65u8,60u8,2u8,24u8,6u8,54u8,72u8, - 227u8,238u8,105u8,50u8,46u8,82u8,249u8,143u8,31u8,81u8,11u8,82u8,219u8,212u8,72u8,212u8,45u8,14u8,14u8,85u8, - 229u8,210u8,99u8,72u8,38u8,36u8,64u8,244u8,233u8,126u8,214u8,236u8,69u8,250u8,237u8,183u8,27u8,61u8,142u8,106u8, - 112u8,184u8,12u8,24u8,104u8,62u8,45u8,123u8,26u8,131u8,205u8,79u8,251u8,226u8,211u8,254u8,230u8,214u8,109u8,226u8, - 181u8,83u8,92u8,207u8,194u8,110u8,244u8,114u8,184u8,37u8,4u8,143u8,123u8,149u8,223u8,246u8,63u8,246u8,11u8,16u8, - 116u8,82u8,216u8,94u8,192u8,71u8,65u8,185u8,40u8,216u8,165u8,38u8,127u8,68u8,89u8,29u8,253u8,241u8,74u8,2u8, - 168u8,51u8,67u8,202u8,96u8,62u8,81u8,91u8,132u8,207u8,152u8,78u8,53u8,172u8,81u8,100u8,22u8,133u8,154u8,46u8, - 146u8,108u8,226u8,64u8,42u8,186u8,222u8,101u8,141u8,123u8,164u8,239u8,233u8,9u8,93u8,79u8,111u8,181u8,43u8,139u8, - 96u8,30u8,17u8,28u8,235u8,18u8,200u8,139u8,43u8,214u8,143u8,127u8,59u8,215u8,182u8,172u8,230u8,211u8,23u8,211u8, - 116u8,11u8,125u8,207u8,213u8,211u8,69u8,151u8,234u8,158u8,96u8,5u8,199u8,34u8,183u8,139u8,210u8,25u8,101u8,184u8, - 75u8,191u8,246u8,89u8,202u8,44u8,186u8,79u8,139u8,238u8,139u8,69u8,247u8,87u8,92u8,116u8,95u8,46u8,186u8,111u8, - 221u8,113u8,107u8,209u8,119u8,69u8,172u8,112u8,216u8,69u8,200u8,213u8,61u8,179u8,222u8,59u8,38u8,77u8,127u8,98u8, - 222u8,107u8,221u8,34u8,238u8,14u8,81u8,93u8,183u8,36u8,155u8,201u8,236u8,237u8,116u8,155u8,176u8,62u8,22u8,220u8, - 161u8,12u8,253u8,198u8,171u8,19u8,208u8,222u8,17u8,107u8,15u8,82u8,136u8,251u8,103u8,236u8,15u8,237u8,166u8,8u8, - 185u8,220u8,121u8,108u8,202u8,75u8,243u8,233u8,22u8,125u8,186u8,69u8,220u8,45u8,122u8,35u8,170u8,47u8,224u8,245u8, - 17u8,238u8,99u8,32u8,179u8,8u8,155u8,127u8,7u8,85u8,240u8,176u8,150u8,179u8,28u8,191u8,116u8,209u8,170u8,107u8, - 236u8,10u8,114u8,96u8,232u8,53u8,77u8,81u8,188u8,16u8,73u8,81u8,188u8,228u8,57u8,210u8,55u8,205u8,152u8,215u8, - 246u8,134u8,87u8,111u8,221u8,171u8,194u8,88u8,105u8,163u8,19u8,226u8,149u8,248u8,143u8,126u8,98u8,8u8,4u8,94u8, - 103u8,162u8,18u8,64u8,17u8,40u8,173u8,190u8,78u8,211u8,2u8,19u8,110u8,169u8,228u8,180u8,95u8,210u8,111u8,126u8, - 210u8,218u8,47u8,69u8,255u8,231u8,66u8,50u8,195u8,19u8,21u8,106u8,82u8,125u8,113u8,136u8,214u8,220u8,41u8,101u8, - 121u8,16u8,207u8,7u8,77u8,91u8,62u8,61u8,27u8,110u8,249u8,221u8,188u8,168u8,40u8,126u8,59u8,210u8,108u8,3u8, - 17u8,94u8,73u8,179u8,107u8,19u8,143u8,25u8,90u8,69u8,229u8,122u8,253u8,74u8,28u8,33u8,121u8,223u8,140u8,100u8, - 181u8,37u8,126u8,175u8,66u8,112u8,203u8,90u8,86u8,21u8,213u8,129u8,51u8,120u8,200u8,100u8,6u8,167u8,127u8,59u8, - 34u8,12u8,79u8,17u8,208u8,204u8,13u8,90u8,64u8,172u8,21u8,115u8,235u8,114u8,67u8,213u8,85u8,189u8,139u8,210u8, - 92u8,15u8,82u8,243u8,213u8,224u8,1u8,141u8,236u8,57u8,81u8,185u8,197u8,48u8,183u8,157u8,42u8,223u8,126u8,243u8, - 57u8,92u8,14u8,20u8,105u8,210u8,81u8,157u8,136u8,178u8,130u8,255u8,141u8,10u8,182u8,17u8,89u8,34u8,165u8,136u8, - 96u8,249u8,125u8,251u8,110u8,23u8,152u8,135u8,128u8,87u8,152u8,244u8,130u8,203u8,132u8,190u8,133u8,249u8,32u8,16u8, - 14u8,4u8,202u8,199u8,113u8,144u8,150u8,169u8,254u8,186u8,110u8,106u8,34u8,232u8,170u8,221u8,186u8,229u8,155u8,193u8, - 150u8,122u8,36u8,33u8,129u8,79u8,80u8,210u8,35u8,65u8,79u8,214u8,226u8,65u8,206u8,73u8,170u8,21u8,195u8,152u8, - 106u8,22u8,193u8,86u8,23u8,214u8,146u8,4u8,44u8,37u8,15u8,43u8,44u8,244u8,54u8,54u8,152u8,45u8,20u8,102u8, - 39u8,48u8,111u8,33u8,218u8,154u8,126u8,130u8,252u8,14u8,126u8,131u8,8u8,210u8,175u8,151u8,178u8,124u8,79u8,2u8, - 129u8,91u8,27u8,229u8,191u8,174u8,108u8,245u8,218u8,211u8,169u8,8u8,114u8,167u8,42u8,20u8,141u8,148u8,216u8,171u8, - 107u8,27u8,118u8,109u8,169u8,147u8,156u8,131u8,49u8,128u8,179u8,39u8,114u8,251u8,97u8,185u8,91u8,96u8,33u8,185u8, - 145u8,144u8,103u8,164u8,109u8,224u8,228u8,180u8,229u8,165u8,106u8,196u8,206u8,27u8,27u8,166u8,131u8,42u8,254u8,97u8, - 29u8,111u8,76u8,28u8,172u8,93u8,125u8,187u8,162u8,170u8,119u8,39u8,218u8,92u8,106u8,63u8,251u8,27u8,61u8,37u8, - 170u8,50u8,187u8,169u8,132u8,112u8,41u8,129u8,91u8,11u8,67u8,4u8,45u8,210u8,242u8,3u8,218u8,41u8,145u8,75u8, - 144u8,17u8,203u8,173u8,90u8,237u8,58u8,253u8,160u8,42u8,210u8,110u8,142u8,233u8,228u8,28u8,20u8,153u8,6u8,139u8, - 42u8,129u8,157u8,195u8,201u8,102u8,184u8,164u8,215u8,46u8,208u8,201u8,89u8,253u8,193u8,36u8,159u8,159u8,157u8,147u8, - 83u8,63u8,209u8,170u8,75u8,4u8,61u8,82u8,117u8,185u8,217u8,220u8,136u8,242u8,146u8,158u8,163u8,60u8,64u8,122u8, - 63u8,239u8,74u8,159u8,102u8,211u8,66u8,221u8,199u8,228u8,116u8,150u8,10u8,68u8,178u8,128u8,72u8,133u8,161u8,0u8, - 149u8,88u8,217u8,234u8,169u8,212u8,167u8,222u8,62u8,134u8,101u8,19u8,6u8,154u8,99u8,122u8,5u8,64u8,201u8,203u8, - 160u8,140u8,83u8,176u8,98u8,117u8,84u8,182u8,69u8,142u8,196u8,85u8,133u8,172u8,56u8,183u8,231u8,58u8,85u8,151u8, - 6u8,41u8,12u8,143u8,16u8,229u8,43u8,142u8,34u8,55u8,75u8,204u8,241u8,103u8,187u8,200u8,137u8,246u8,173u8,99u8, - 117u8,116u8,214u8,47u8,197u8,46u8,83u8,194u8,57u8,184u8,255u8,50u8,255u8,165u8,37u8,166u8,153u8,238u8,38u8,221u8, - 219u8,184u8,166u8,91u8,183u8,198u8,129u8,182u8,131u8,28u8,8u8,245u8,47u8,163u8,105u8,2u8,246u8,120u8,140u8,208u8, - 116u8,83u8,131u8,202u8,220u8,116u8,50u8,145u8,127u8,185u8,104u8,213u8,171u8,230u8,109u8,188u8,56u8,245u8,199u8,55u8, - 241u8,147u8,37u8,111u8,44u8,156u8,239u8,54u8,123u8,188u8,236u8,108u8,123u8,171u8,207u8,246u8,91u8,196u8,166u8,175u8, - 151u8,159u8,109u8,144u8,182u8,6u8,168u8,75u8,114u8,134u8,166u8,16u8,196u8,203u8,153u8,83u8,137u8,118u8,236u8,97u8, - 111u8,78u8,137u8,7u8,73u8,186u8,255u8,165u8,30u8,137u8,111u8,227u8,210u8,175u8,241u8,98u8,124u8,115u8,107u8,23u8, - 227u8,219u8,187u8,122u8,116u8,48u8,214u8,121u8,82u8,125u8,42u8,49u8,65u8,173u8,128u8,113u8,246u8,253u8,244u8,72u8, - 121u8,96u8,230u8,249u8,95u8,204u8,55u8,135u8,190u8,124u8,50u8,97u8,39u8,166u8,223u8,141u8,164u8,147u8,158u8,182u8, - 120u8,226u8,157u8,29u8,99u8,219u8,243u8,100u8,124u8,138u8,178u8,146u8,236u8,131u8,23u8,59u8,26u8,220u8,24u8,201u8, - 109u8,116u8,56u8,25u8,53u8,96u8,31u8,39u8,203u8,92u8,214u8,232u8,177u8,37u8,84u8,63u8,160u8,231u8,17u8,206u8, - 204u8,127u8,34u8,253u8,55u8,91u8,73u8,144u8,40u8,57u8,230u8,133u8,209u8,194u8,181u8,24u8,179u8,42u8,230u8,185u8, - 198u8,85u8,158u8,63u8,87u8,124u8,21u8,208u8,251u8,74u8,38u8,123u8,81u8,214u8,91u8,121u8,108u8,68u8,152u8,203u8, - 98u8,36u8,36u8,210u8,137u8,152u8,195u8,209u8,221u8,9u8,245u8,253u8,91u8,60u8,144u8,232u8,43u8,39u8,17u8,195u8, - 178u8,111u8,8u8,253u8,22u8,179u8,152u8,242u8,93u8,91u8,169u8,122u8,253u8,197u8,116u8,198u8,82u8,69u8,60u8,77u8, - 71u8,243u8,161u8,190u8,138u8,159u8,200u8,240u8,3u8,35u8,195u8,135u8,42u8,149u8,152u8,56u8,160u8,197u8,136u8,40u8, - 61u8,73u8,240u8,120u8,203u8,24u8,99u8,125u8,187u8,115u8,51u8,221u8,152u8,27u8,6u8,45u8,157u8,55u8,171u8,171u8, - 67u8,49u8,169u8,63u8,236u8,177u8,15u8,95u8,89u8,115u8,47u8,235u8,203u8,92u8,147u8,229u8,87u8,215u8,150u8,81u8, - 234u8,8u8,217u8,236u8,25u8,86u8,178u8,93u8,120u8,189u8,15u8,198u8,116u8,243u8,98u8,29u8,30u8,141u8,120u8,51u8, - 137u8,206u8,107u8,187u8,141u8,155u8,24u8,239u8,222u8,237u8,54u8,247u8,125u8,147u8,85u8,14u8,190u8,237u8,141u8,255u8, - 12u8,101u8,223u8,43u8,116u8,78u8,36u8,96u8,180u8,105u8,130u8,246u8,95u8,91u8,227u8,117u8,123u8,46u8,99u8,214u8, - 67u8,241u8,35u8,50u8,26u8,57u8,164u8,104u8,13u8,86u8,163u8,70u8,50u8,102u8,79u8,214u8,95u8,198u8,180u8,116u8, - 186u8,172u8,160u8,103u8,70u8,234u8,127u8,94u8,88u8,42u8,97u8,153u8,203u8,139u8,158u8,140u8,178u8,102u8,51u8,93u8, - 142u8,110u8,35u8,89u8,169u8,153u8,69u8,137u8,149u8,116u8,170u8,29u8,226u8,124u8,145u8,14u8,197u8,42u8,248u8,52u8, - 155u8,42u8,21u8,56u8,17u8,66u8,176u8,119u8,141u8,74u8,247u8,24u8,37u8,74u8,243u8,184u8,236u8,172u8,75u8,40u8, - 211u8,166u8,36u8,141u8,99u8,193u8,130u8,144u8,79u8,77u8,229u8,250u8,251u8,53u8,235u8,239u8,183u8,239u8,142u8,182u8, - 46u8,238u8,22u8,75u8,86u8,54u8,124u8,252u8,123u8,97u8,240u8,127u8,81u8,130u8,120u8,203u8,254u8,175u8,54u8,111u8, - 127u8,133u8,14u8,93u8,61u8,3u8,205u8,173u8,155u8,235u8,70u8,205u8,169u8,171u8,172u8,158u8,140u8,34u8,95u8,40u8, - 78u8,101u8,13u8,94u8,174u8,247u8,71u8,37u8,165u8,117u8,239u8,118u8,137u8,164u8,227u8,78u8,39u8,172u8,103u8,66u8, - 195u8,101u8,95u8,143u8,144u8,97u8,176u8,223u8,240u8,237u8,27u8,247u8,171u8,19u8,107u8,89u8,179u8,102u8,192u8,47u8, - 252u8,2u8,236u8,87u8,191u8,1u8,95u8,31u8,188u8,122u8,129u8,249u8,97u8,213u8,91u8,208u8,196u8,192u8,58u8,191u8, - 218u8,5u8,77u8,34u8,125u8,211u8,32u8,180u8,230u8,199u8,220u8,189u8,74u8,151u8,77u8,226u8,10u8,74u8,26u8,26u8, - 99u8,21u8,156u8,79u8,242u8,165u8,190u8,122u8,176u8,225u8,15u8,81u8,182u8,236u8,61u8,96u8,98u8,124u8,55u8,33u8, - 7u8,188u8,132u8,21u8,8u8,59u8,32u8,135u8,138u8,105u8,29u8,205u8,196u8,116u8,115u8,42u8,77u8,119u8,248u8,196u8, - 133u8,207u8,2u8,226u8,133u8,244u8,100u8,150u8,161u8,28u8,120u8,99u8,177u8,207u8,13u8,231u8,66u8,250u8,0u8,35u8, - 24u8,148u8,245u8,17u8,223u8,226u8,66u8,252u8,20u8,155u8,74u8,24u8,47u8,100u8,205u8,21u8,162u8,26u8,238u8,204u8, - 203u8,41u8,72u8,222u8,200u8,149u8,172u8,248u8,100u8,99u8,88u8,208u8,129u8,218u8,186u8,69u8,168u8,245u8,165u8,75u8, - 212u8,81u8,38u8,127u8,198u8,82u8,92u8,37u8,130u8,220u8,153u8,151u8,149u8,251u8,90u8,93u8,163u8,230u8,127u8,73u8, - 253u8,53u8,39u8,187u8,88u8,164u8,230u8,60u8,29u8,190u8,23u8,254u8,94u8,65u8,106u8,131u8,85u8,23u8,223u8,155u8, - 202u8,118u8,53u8,175u8,64u8,37u8,107u8,62u8,67u8,34u8,95u8,9u8,97u8,151u8,247u8,248u8,16u8,142u8,156u8,76u8, - 192u8,218u8,197u8,5u8,60u8,80u8,97u8,253u8,227u8,155u8,134u8,238u8,154u8,213u8,57u8,123u8,214u8,176u8,195u8,135u8, - 175u8,212u8,222u8,246u8,22u8,221u8,91u8,228u8,16u8,131u8,20u8,44u8,88u8,163u8,84u8,124u8,82u8,58u8,226u8,232u8, - 156u8,24u8,30u8,211u8,20u8,20u8,229u8,150u8,14u8,99u8,227u8,86u8,104u8,230u8,173u8,72u8,148u8,11u8,75u8,146u8, - 49u8,166u8,91u8,148u8,46u8,22u8,66u8,131u8,82u8,252u8,249u8,41u8,241u8,71u8,164u8,156u8,148u8,178u8,129u8,28u8, - 77u8,190u8,50u8,209u8,150u8,251u8,91u8,254u8,128u8,35u8,100u8,214u8,66u8,226u8,151u8,55u8,238u8,213u8,216u8,62u8, - 27u8,1u8,238u8,221u8,22u8,224u8,126u8,47u8,0u8,216u8,20u8,3u8,169u8,224u8,233u8,182u8,41u8,237u8,153u8,197u8, - 90u8,65u8,152u8,77u8,151u8,139u8,210u8,251u8,179u8,146u8,177u8,79u8,52u8,235u8,1u8,26u8,84u8,238u8,61u8,196u8, - 213u8,191u8,69u8,203u8,69u8,183u8,110u8,223u8,217u8,147u8,201u8,67u8,124u8,225u8,77u8,27u8,95u8,38u8,83u8,220u8, - 160u8,21u8,148u8,233u8,31u8,27u8,178u8,11u8,111u8,93u8,81u8,203u8,24u8,229u8,225u8,255u8,188u8,123u8,221u8,205u8, - 125u8,227u8,241u8,207u8,194u8,175u8,93u8,57u8,108u8,145u8,91u8,183u8,159u8,122u8,226u8,65u8,197u8,152u8,40u8,200u8, - 203u8,197u8,173u8,50u8,236u8,178u8,58u8,86u8,117u8,237u8,62u8,240u8,42u8,138u8,215u8,106u8,102u8,4u8,4u8,160u8, - 118u8,95u8,212u8,143u8,87u8,65u8,187u8,219u8,189u8,168u8,5u8,189u8,190u8,212u8,197u8,98u8,85u8,87u8,88u8,210u8, - 141u8,204u8,68u8,38u8,34u8,95u8,128u8,129u8,129u8,81u8,80u8,76u8,227u8,163u8,241u8,123u8,237u8,135u8,84u8,18u8, - 211u8,116u8,152u8,102u8,232u8,20u8,175u8,106u8,238u8,2u8,207u8,25u8,228u8,232u8,27u8,87u8,70u8,84u8,168u8,231u8, - 218u8,42u8,78u8,243u8,219u8,219u8,226u8,127u8,95u8,94u8,90u8,47u8,246u8,110u8,222u8,27u8,137u8,71u8,107u8,169u8, - 242u8,15u8,199u8,192u8,95u8,41u8,220u8,32u8,55u8,210u8,53u8,134u8,49u8,175u8,218u8,240u8,47u8,64u8,248u8,31u8, - 126u8,176u8,224u8,131u8,36u8,189u8,27u8,94u8,249u8,75u8,225u8,121u8,47u8,213u8,133u8,68u8,22u8,201u8,125u8,67u8, - 109u8,142u8,186u8,181u8,119u8,231u8,43u8,218u8,91u8,53u8,8u8,138u8,33u8,255u8,146u8,65u8,98u8,128u8,142u8,160u8, - 218u8,166u8,249u8,21u8,72u8,55u8,169u8,138u8,60u8,95u8,181u8,181u8,122u8,170u8,250u8,94u8,170u8,253u8,74u8,54u8, - 216u8,219u8,88u8,110u8,35u8,54u8,202u8,88u8,176u8,94u8,211u8,243u8,46u8,217u8,21u8,203u8,9u8,91u8,88u8,150u8, - 12u8,171u8,66u8,194u8,242u8,44u8,179u8,76u8,115u8,230u8,212u8,179u8,61u8,15u8,66u8,145u8,87u8,134u8,183u8,186u8, - 114u8,50u8,156u8,161u8,9u8,12u8,118u8,159u8,226u8,170u8,40u8,238u8,199u8,215u8,107u8,22u8,25u8,70u8,144u8,8u8, - 207u8,156u8,50u8,144u8,47u8,24u8,170u8,46u8,129u8,155u8,3u8,11u8,76u8,32u8,71u8,36u8,192u8,126u8,18u8,40u8, - 205u8,183u8,211u8,173u8,132u8,80u8,109u8,174u8,122u8,190u8,253u8,91u8,146u8,115u8,162u8,239u8,231u8,112u8,102u8,20u8, - 8u8,73u8,78u8,158u8,115u8,12u8,217u8,83u8,178u8,205u8,93u8,8u8,36u8,13u8,247u8,161u8,183u8,177u8,25u8,80u8, - 143u8,24u8,81u8,203u8,231u8,82u8,73u8,252u8,62u8,35u8,1u8,11u8,11u8,159u8,74u8,17u8,217u8,73u8,51u8,83u8, - 88u8,174u8,93u8,240u8,254u8,219u8,192u8,213u8,119u8,111u8,71u8,217u8,189u8,117u8,207u8,239u8,68u8,74u8,31u8,130u8, - 250u8,17u8,210u8,140u8,36u8,87u8,240u8,198u8,197u8,74u8,174u8,82u8,162u8,144u8,201u8,247u8,177u8,176u8,206u8,39u8, - 65u8,226u8,193u8,6u8,244u8,136u8,147u8,180u8,98u8,242u8,73u8,143u8,129u8,134u8,114u8,113u8,157u8,85u8,42u8,33u8, - 83u8,157u8,33u8,220u8,4u8,16u8,255u8,101u8,52u8,52u8,48u8,123u8,24u8,179u8,42u8,48u8,185u8,191u8,177u8,122u8, - 42u8,161u8,13u8,47u8,68u8,249u8,126u8,208u8,95u8,203u8,207u8,37u8,142u8,199u8,131u8,27u8,39u8,155u8,215u8,39u8, - 172u8,247u8,76u8,204u8,135u8,210u8,174u8,32u8,3u8,78u8,35u8,140u8,40u8,221u8,136u8,90u8,217u8,104u8,76u8,9u8, - 86u8,144u8,235u8,138u8,194u8,219u8,163u8,54u8,105u8,97u8,76u8,140u8,186u8,79u8,233u8,155u8,207u8,13u8,81u8,125u8, - 161u8,104u8,29u8,205u8,35u8,245u8,107u8,217u8,174u8,230u8,40u8,119u8,193u8,73u8,249u8,165u8,97u8,101u8,113u8,76u8, - 184u8,173u8,227u8,139u8,65u8,220u8,189u8,72u8,126u8,87u8,169u8,16u8,180u8,226u8,66u8,72u8,78u8,20u8,23u8,40u8, - 138u8,222u8,223u8,80u8,89u8,70u8,153u8,193u8,76u8,119u8,46u8,149u8,166u8,22u8,233u8,224u8,130u8,151u8,251u8,158u8, - 166u8,108u8,133u8,199u8,16u8,169u8,251u8,72u8,157u8,16u8,97u8,137u8,15u8,83u8,160u8,215u8,209u8,118u8,96u8,111u8, - 143u8,122u8,164u8,72u8,241u8,158u8,46u8,75u8,198u8,78u8,99u8,84u8,214u8,178u8,137u8,62u8,110u8,57u8,164u8,108u8, - 1u8,205u8,1u8,232u8,254u8,101u8,242u8,144u8,64u8,14u8,195u8,79u8,84u8,239u8,161u8,102u8,24u8,178u8,8u8,147u8, - 206u8,56u8,129u8,49u8,186u8,226u8,89u8,4u8,52u8,142u8,201u8,32u8,216u8,84u8,199u8,122u8,103u8,54u8,216u8,106u8, - 21u8,173u8,77u8,172u8,74u8,108u8,101u8,31u8,113u8,38u8,125u8,54u8,16u8,219u8,55u8,200u8,108u8,60u8,204u8,80u8, - 208u8,134u8,247u8,214u8,88u8,26u8,39u8,173u8,7u8,114u8,145u8,126u8,18u8,223u8,31u8,254u8,149u8,158u8,77u8,51u8, - 161u8,104u8,51u8,16u8,189u8,228u8,170u8,250u8,136u8,3u8,200u8,175u8,104u8,128u8,45u8,211u8,231u8,128u8,238u8,31u8, - 195u8,173u8,111u8,116u8,113u8,123u8,31u8,245u8,197u8,21u8,58u8,69u8,56u8,181u8,98u8,126u8,122u8,154u8,13u8,177u8, - 246u8,181u8,161u8,66u8,148u8,122u8,123u8,76u8,155u8,164u8,31u8,36u8,96u8,179u8,69u8,138u8,30u8,11u8,61u8,197u8, - 167u8,139u8,251u8,176u8,46u8,174u8,84u8,32u8,167u8,66u8,206u8,31u8,74u8,239u8,117u8,145u8,230u8,207u8,76u8,204u8, - 171u8,148u8,163u8,226u8,57u8,144u8,71u8,23u8,105u8,42u8,218u8,192u8,59u8,68u8,23u8,56u8,255u8,100u8,150u8,125u8, - 208u8,102u8,89u8,251u8,16u8,57u8,159u8,105u8,113u8,71u8,205u8,113u8,121u8,197u8,116u8,247u8,182u8,13u8,173u8,15u8, - 212u8,38u8,92u8,234u8,57u8,251u8,75u8,35u8,205u8,246u8,70u8,109u8,246u8,179u8,73u8,14u8,168u8,49u8,57u8,75u8, - 167u8,58u8,147u8,148u8,54u8,4u8,216u8,231u8,85u8,54u8,76u8,34u8,116u8,144u8,97u8,53u8,192u8,171u8,219u8,125u8, - 54u8,214u8,107u8,28u8,94u8,34u8,208u8,54u8,20u8,64u8,161u8,18u8,67u8,12u8,230u8,179u8,152u8,76u8,29u8,192u8, - 160u8,192u8,88u8,188u8,198u8,116u8,33u8,214u8,119u8,31u8,127u8,196u8,237u8,71u8,157u8,58u8,186u8,50u8,168u8,2u8, - 110u8,208u8,219u8,148u8,194u8,244u8,225u8,50u8,8u8,58u8,97u8,224u8,30u8,6u8,163u8,71u8,191u8,205u8,139u8,153u8, - 162u8,123u8,34u8,207u8,227u8,149u8,245u8,54u8,35u8,164u8,234u8,222u8,65u8,42u8,106u8,38u8,169u8,82u8,19u8,187u8, - 237u8,131u8,179u8,184u8,151u8,19u8,91u8,58u8,212u8,194u8,77u8,249u8,20u8,54u8,110u8,98u8,240u8,15u8,134u8,157u8, - 164u8,195u8,243u8,137u8,76u8,183u8,41u8,99u8,124u8,22u8,163u8,124u8,43u8,134u8,204u8,172u8,202u8,3u8,108u8,73u8, - 190u8,215u8,68u8,221u8,121u8,160u8,3u8,7u8,249u8,90u8,69u8,37u8,27u8,184u8,155u8,173u8,40u8,183u8,96u8,211u8, - 41u8,58u8,5u8,20u8,240u8,10u8,104u8,163u8,105u8,113u8,236u8,160u8,127u8,238u8,120u8,78u8,110u8,80u8,82u8,20u8, - 233u8,148u8,30u8,98u8,120u8,126u8,197u8,124u8,240u8,91u8,58u8,156u8,169u8,60u8,95u8,78u8,94u8,177u8,238u8,202u8, - 78u8,19u8,189u8,219u8,74u8,210u8,229u8,187u8,62u8,108u8,185u8,233u8,145u8,164u8,174u8,25u8,153u8,153u8,174u8,148u8, - 209u8,113u8,13u8,178u8,53u8,89u8,121u8,105u8,171u8,72u8,201u8,61u8,72u8,181u8,42u8,154u8,30u8,209u8,58u8,65u8, - 173u8,57u8,166u8,148u8,42u8,9u8,93u8,40u8,57u8,239u8,168u8,123u8,11u8,226u8,111u8,207u8,20u8,127u8,157u8,196u8, - 74u8,123u8,232u8,112u8,128u8,25u8,75u8,1u8,19u8,69u8,68u8,141u8,18u8,128u8,141u8,252u8,109u8,218u8,225u8,66u8, - 204u8,209u8,147u8,71u8,63u8,151u8,106u8,65u8,41u8,210u8,138u8,248u8,207u8,2u8,216u8,86u8,141u8,199u8,78u8,243u8, - 15u8,215u8,180u8,218u8,239u8,65u8,64u8,141u8,164u8,128u8,234u8,8u8,215u8,78u8,142u8,228u8,232u8,21u8,37u8,81u8, - 79u8,29u8,182u8,67u8,108u8,9u8,9u8,90u8,105u8,135u8,24u8,65u8,251u8,225u8,108u8,124u8,99u8,26u8,39u8,204u8, - 49u8,121u8,237u8,73u8,177u8,54u8,203u8,75u8,195u8,220u8,178u8,139u8,237u8,221u8,154u8,163u8,200u8,110u8,65u8,140u8, - 20u8,30u8,135u8,20u8,107u8,170u8,50u8,131u8,18u8,114u8,106u8,2u8,33u8,20u8,144u8,3u8,76u8,61u8,70u8,130u8, - 249u8,39u8,9u8,243u8,147u8,132u8,249u8,73u8,194u8,124u8,216u8,18u8,102u8,80u8,172u8,106u8,78u8,184u8,215u8,69u8, - 86u8,87u8,120u8,128u8,135u8,235u8,87u8,72u8,130u8,197u8,200u8,124u8,209u8,145u8,210u8,26u8,40u8,141u8,147u8,174u8, - 50u8,86u8,40u8,33u8,196u8,206u8,125u8,8u8,2u8,134u8,149u8,208u8,222u8,28u8,81u8,146u8,188u8,102u8,201u8,187u8, - 22u8,56u8,146u8,91u8,86u8,37u8,173u8,23u8,39u8,251u8,15u8,37u8,229u8,24u8,147u8,151u8,113u8,148u8,3u8,43u8, - 195u8,32u8,65u8,81u8,178u8,30u8,179u8,51u8,126u8,202u8,201u8,248u8,41u8,39u8,227u8,61u8,21u8,242u8,250u8,56u8, - 184u8,165u8,153u8,226u8,81u8,8u8,131u8,28u8,29u8,61u8,88u8,36u8,115u8,143u8,239u8,167u8,176u8,134u8,194u8,97u8, - 129u8,41u8,59u8,181u8,139u8,108u8,143u8,72u8,156u8,107u8,49u8,31u8,82u8,109u8,83u8,152u8,58u8,88u8,82u8,141u8, - 47u8,132u8,75u8,56u8,85u8,120u8,98u8,111u8,66u8,243u8,178u8,97u8,219u8,183u8,85u8,208u8,220u8,252u8,116u8,83u8, - 124u8,186u8,185u8,181u8,93u8,73u8,29u8,117u8,34u8,7u8,221u8,179u8,16u8,121u8,106u8,113u8,254u8,116u8,12u8,119u8, - 65u8,4u8,237u8,111u8,55u8,245u8,183u8,31u8,1u8,133u8,180u8,5u8,192u8,94u8,211u8,212u8,124u8,189u8,160u8,1u8, - 126u8,169u8,236u8,126u8,54u8,136u8,205u8,166u8,32u8,54u8,205u8,203u8,189u8,8u8,157u8,38u8,26u8,209u8,148u8,80u8, - 7u8,163u8,100u8,151u8,33u8,213u8,216u8,62u8,30u8,220u8,192u8,236u8,160u8,245u8,96u8,92u8,192u8,101u8,248u8,166u8, - 183u8,179u8,35u8,176u8,59u8,126u8,159u8,222u8,96u8,149u8,60u8,250u8,94u8,208u8,225u8,53u8,80u8,122u8,221u8,111u8, - 147u8,250u8,109u8,138u8,126u8,155u8,31u8,117u8,214u8,222u8,102u8,16u8,55u8,37u8,68u8,139u8,222u8,108u8,46u8,150u8, - 7u8,24u8,53u8,114u8,253u8,218u8,220u8,35u8,171u8,165u8,167u8,172u8,203u8,10u8,204u8,92u8,208u8,245u8,8u8,215u8, - 189u8,102u8,226u8,245u8,198u8,186u8,83u8,105u8,134u8,82u8,37u8,25u8,150u8,1u8,224u8,173u8,195u8,148u8,242u8,217u8, - 9u8,51u8,55u8,168u8,153u8,65u8,92u8,203u8,167u8,35u8,224u8,89u8,82u8,225u8,173u8,18u8,6u8,210u8,135u8,11u8, - 168u8,141u8,123u8,77u8,178u8,169u8,44u8,146u8,163u8,51u8,172u8,226u8,183u8,9u8,27u8,62u8,203u8,225u8,214u8,47u8, - 146u8,161u8,41u8,4u8,74u8,100u8,198u8,39u8,150u8,122u8,5u8,154u8,67u8,80u8,50u8,237u8,8u8,184u8,173u8,71u8, - 214u8,120u8,93u8,247u8,213u8,80u8,240u8,199u8,233u8,67u8,238u8,226u8,242u8,240u8,244u8,216u8,141u8,235u8,55u8,131u8, - 33u8,126u8,232u8,150u8,223u8,192u8,185u8,166u8,191u8,59u8,54u8,230u8,224u8,234u8,250u8,203u8,172u8,174u8,215u8,100u8, - 102u8,125u8,102u8,117u8,198u8,129u8,110u8,53u8,131u8,17u8,94u8,93u8,175u8,170u8,252u8,87u8,47u8,154u8,230u8,136u8, - 76u8,5u8,8u8,101u8,147u8,34u8,157u8,20u8,243u8,34u8,2u8,202u8,223u8,181u8,18u8,75u8,202u8,252u8,94u8,198u8, - 103u8,155u8,34u8,41u8,30u8,151u8,19u8,115u8,32u8,24u8,209u8,64u8,114u8,162u8,65u8,99u8,86u8,52u8,88u8,132u8, - 23u8,13u8,92u8,0u8,52u8,22u8,7u8,97u8,154u8,231u8,167u8,192u8,170u8,227u8,203u8,28u8,195u8,50u8,11u8,172u8, - 208u8,96u8,128u8,18u8,83u8,52u8,220u8,231u8,104u8,55u8,98u8,189u8,25u8,56u8,116u8,45u8,1u8,236u8,152u8,243u8, - 239u8,88u8,115u8,105u8,170u8,70u8,90u8,145u8,48u8,219u8,92u8,165u8,206u8,212u8,217u8,215u8,238u8,56u8,167u8,167u8, - 41u8,97u8,237u8,248u8,70u8,135u8,131u8,130u8,104u8,158u8,141u8,141u8,202u8,160u8,221u8,245u8,146u8,91u8,223u8,148u8, - 249u8,132u8,67u8,112u8,231u8,166u8,45u8,150u8,73u8,206u8,175u8,39u8,184u8,209u8,174u8,190u8,100u8,193u8,80u8,199u8, - 77u8,110u8,171u8,68u8,158u8,253u8,85u8,55u8,106u8,115u8,177u8,52u8,137u8,95u8,223u8,218u8,54u8,85u8,110u8,210u8, - 38u8,191u8,73u8,235u8,153u8,1u8,203u8,16u8,20u8,41u8,43u8,239u8,32u8,80u8,1u8,184u8,134u8,56u8,175u8,82u8, - 100u8,229u8,242u8,70u8,98u8,1u8,38u8,120u8,84u8,94u8,94u8,142u8,51u8,124u8,118u8,146u8,129u8,82u8,215u8,114u8, - 189u8,67u8,25u8,229u8,155u8,69u8,120u8,119u8,175u8,42u8,222u8,126u8,173u8,87u8,175u8,188u8,114u8,223u8,218u8,232u8, - 126u8,98u8,251u8,118u8,17u8,227u8,0u8,185u8,198u8,40u8,46u8,65u8,249u8,84u8,74u8,51u8,168u8,44u8,218u8,0u8, - 87u8,3u8,245u8,126u8,152u8,12u8,21u8,180u8,182u8,184u8,219u8,34u8,224u8,87u8,92u8,10u8,114u8,64u8,148u8,209u8, - 78u8,74u8,102u8,53u8,71u8,181u8,196u8,87u8,165u8,18u8,22u8,131u8,83u8,49u8,216u8,51u8,12u8,66u8,79u8,81u8, - 91u8,48u8,21u8,6u8,201u8,238u8,146u8,2u8,81u8,19u8,123u8,252u8,210u8,215u8,84u8,139u8,143u8,11u8,9u8,108u8, - 155u8,77u8,4u8,182u8,123u8,188u8,99u8,131u8,208u8,37u8,43u8,245u8,5u8,100u8,167u8,197u8,200u8,111u8,101u8,189u8, - 16u8,41u8,248u8,101u8,25u8,95u8,201u8,77u8,194u8,214u8,127u8,210u8,222u8,108u8,60u8,8u8,183u8,203u8,94u8,227u8, - 108u8,170u8,119u8,150u8,137u8,108u8,148u8,142u8,211u8,51u8,44u8,73u8,173u8,205u8,195u8,100u8,18u8,206u8,81u8,67u8, - 243u8,151u8,141u8,65u8,185u8,101u8,21u8,202u8,237u8,185u8,193u8,135u8,117u8,6u8,56u8,182u8,62u8,84u8,24u8,150u8, - 34u8,45u8,135u8,51u8,227u8,160u8,77u8,83u8,167u8,155u8,102u8,79u8,85u8,189u8,72u8,39u8,156u8,123u8,238u8,12u8, - 51u8,174u8,151u8,197u8,205u8,187u8,126u8,241u8,118u8,129u8,84u8,56u8,202u8,35u8,61u8,96u8,71u8,232u8,92u8,201u8, - 225u8,27u8,51u8,140u8,180u8,61u8,65u8,59u8,31u8,175u8,45u8,178u8,197u8,4u8,198u8,61u8,128u8,155u8,86u8,132u8, - 52u8,73u8,80u8,83u8,27u8,161u8,57u8,246u8,42u8,116u8,196u8,130u8,83u8,171u8,186u8,96u8,226u8,31u8,200u8,239u8, - 12u8,112u8,108u8,154u8,242u8,65u8,210u8,229u8,41u8,209u8,6u8,149u8,71u8,181u8,232u8,254u8,242u8,233u8,2u8,214u8, - 77u8,123u8,45u8,35u8,140u8,242u8,41u8,18u8,152u8,250u8,101u8,40u8,122u8,184u8,126u8,226u8,129u8,194u8,221u8,99u8, - 146u8,15u8,208u8,2u8,189u8,171u8,253u8,134u8,120u8,172u8,230u8,82u8,255u8,168u8,160u8,101u8,24u8,125u8,158u8,182u8, - 30u8,17u8,32u8,25u8,156u8,220u8,107u8,148u8,51u8,41u8,176u8,191u8,12u8,246u8,30u8,93u8,34u8,57u8,145u8,182u8, - 165u8,34u8,189u8,76u8,166u8,148u8,49u8,92u8,190u8,55u8,157u8,199u8,183u8,255u8,170u8,6u8,41u8,150u8,30u8,153u8, - 226u8,95u8,120u8,95u8,194u8,15u8,77u8,31u8,214u8,208u8,116u8,129u8,151u8,53u8,2u8,246u8,159u8,214u8,1u8,24u8, - 181u8,111u8,107u8,27u8,90u8,245u8,227u8,218u8,190u8,146u8,29u8,107u8,234u8,29u8,123u8,22u8,53u8,202u8,154u8,176u8, - 98u8,75u8,112u8,166u8,231u8,173u8,192u8,229u8,15u8,233u8,87u8,66u8,114u8,146u8,49u8,191u8,126u8,213u8,129u8,139u8, - 154u8,145u8,32u8,22u8,207u8,72u8,64u8,64u8,61u8,46u8,166u8,45u8,152u8,96u8,146u8,94u8,57u8,11u8,211u8,211u8, - 67u8,150u8,152u8,148u8,205u8,201u8,126u8,164u8,154u8,199u8,186u8,121u8,120u8,227u8,6u8,127u8,239u8,253u8,29u8,255u8, - 238u8,255u8,253u8,206u8,247u8,73u8,79u8,89u8,205u8,18u8,55u8,74u8,204u8,103u8,179u8,73u8,127u8,127u8,153u8,2u8, - 64u8,255u8,239u8,190u8,127u8,235u8,62u8,40u8,84u8,38u8,243u8,75u8,99u8,223u8,196u8,109u8,158u8,166u8,103u8,89u8, - 1u8,143u8,17u8,54u8,7u8,129u8,49u8,9u8,209u8,122u8,4u8,132u8,171u8,200u8,102u8,206u8,85u8,22u8,164u8,160u8, - 108u8,43u8,91u8,113u8,124u8,185u8,19u8,185u8,4u8,249u8,206u8,243u8,59u8,239u8,45u8,150u8,223u8,153u8,175u8,166u8, - 9u8,11u8,156u8,197u8,72u8,79u8,138u8,236u8,14u8,146u8,208u8,7u8,68u8,200u8,42u8,169u8,243u8,1u8,139u8,151u8, - 82u8,186u8,179u8,229u8,75u8,87u8,120u8,209u8,69u8,12u8,237u8,52u8,107u8,101u8,66u8,100u8,124u8,61u8,227u8,41u8, - 68u8,242u8,20u8,156u8,150u8,162u8,192u8,180u8,146u8,237u8,164u8,167u8,200u8,253u8,39u8,132u8,185u8,115u8,108u8,63u8, - 88u8,180u8,136u8,44u8,141u8,23u8,155u8,23u8,252u8,47u8,137u8,223u8,15u8,52u8,149u8,103u8,181u8,148u8,46u8,189u8, - 210u8,62u8,82u8,194u8,42u8,84u8,254u8,119u8,75u8,89u8,255u8,92u8,15u8,119u8,150u8,178u8,222u8,49u8,218u8,86u8, - 88u8,231u8,191u8,187u8,193u8,224u8,18u8,225u8,230u8,42u8,74u8,235u8,136u8,218u8,113u8,101u8,5u8,52u8,173u8,50u8, - 5u8,44u8,25u8,18u8,34u8,161u8,115u8,43u8,168u8,167u8,71u8,115u8,170u8,57u8,254u8,131u8,192u8,139u8,181u8,185u8, - 65u8,251u8,215u8,9u8,40u8,32u8,72u8,67u8,147u8,100u8,188u8,178u8,211u8,50u8,186u8,119u8,223u8,58u8,23u8,91u8, - 202u8,215u8,218u8,166u8,12u8,161u8,59u8,198u8,145u8,8u8,206u8,225u8,170u8,97u8,59u8,229u8,45u8,198u8,58u8,94u8, - 73u8,159u8,49u8,238u8,187u8,45u8,241u8,221u8,22u8,247u8,221u8,182u8,248u8,110u8,91u8,92u8,244u8,42u8,98u8,163u8, - 20u8,131u8,142u8,197u8,247u8,62u8,125u8,204u8,216u8,197u8,86u8,126u8,187u8,253u8,81u8,145u8,54u8,186u8,154u8,66u8, - 91u8,188u8,176u8,83u8,90u8,191u8,81u8,55u8,207u8,17u8,109u8,179u8,81u8,55u8,207u8,123u8,97u8,171u8,81u8,183u8, - 45u8,183u8,219u8,118u8,163u8,110u8,219u8,53u8,174u8,110u8,140u8,160u8,113u8,111u8,174u8,197u8,203u8,58u8,156u8,233u8, - 126u8,91u8,212u8,111u8,75u8,244u8,219u8,106u8,222u8,111u8,155u8,250u8,109u8,139u8,126u8,219u8,193u8,126u8,127u8,81u8, - 15u8,183u8,102u8,16u8,183u8,36u8,68u8,203u8,27u8,119u8,107u8,21u8,136u8,219u8,18u8,226u8,182u8,9u8,113u8,187u8, - 134u8,207u8,215u8,122u8,94u8,136u8,75u8,186u8,156u8,18u8,89u8,16u8,109u8,5u8,66u8,122u8,35u8,109u8,52u8,30u8, - 30u8,183u8,102u8,107u8,221u8,195u8,247u8,154u8,118u8,219u8,42u8,187u8,245u8,28u8,21u8,88u8,173u8,31u8,97u8,207u8, - 157u8,117u8,173u8,223u8,98u8,223u8,237u8,193u8,5u8,163u8,102u8,228u8,0u8,32u8,220u8,228u8,41u8,61u8,144u8,225u8, - 172u8,183u8,182u8,13u8,122u8,178u8,220u8,6u8,125u8,221u8,180u8,91u8,191u8,236u8,214u8,231u8,220u8,9u8,2u8,221u8, - 122u8,101u8,183u8,77u8,97u8,228u8,111u8,182u8,185u8,120u8,27u8,182u8,215u8,142u8,68u8,27u8,203u8,109u8,82u8,175u8, - 183u8,220u8,46u8,245u8,250u8,203u8,109u8,83u8,111u8,179u8,105u8,191u8,237u8,178u8,31u8,18u8,33u8,87u8,31u8,41u8, - 252u8,77u8,138u8,107u8,48u8,228u8,9u8,235u8,119u8,39u8,202u8,102u8,159u8,23u8,48u8,198u8,86u8,167u8,223u8,217u8, - 110u8,30u8,145u8,225u8,95u8,141u8,10u8,232u8,219u8,8u8,189u8,33u8,236u8,122u8,90u8,181u8,240u8,70u8,224u8,65u8, - 111u8,47u8,121u8,208u8,79u8,150u8,60u8,232u8,175u8,239u8,59u8,14u8,206u8,204u8,37u8,41u8,75u8,196u8,76u8,49u8, - 113u8,164u8,22u8,40u8,227u8,1u8,86u8,78u8,253u8,20u8,243u8,182u8,132u8,112u8,120u8,127u8,1u8,32u8,214u8,187u8, - 30u8,107u8,41u8,149u8,46u8,51u8,165u8,207u8,61u8,91u8,229u8,243u8,163u8,141u8,147u8,187u8,221u8,168u8,182u8,62u8, - 233u8,3u8,128u8,15u8,131u8,157u8,16u8,52u8,2u8,201u8,216u8,222u8,57u8,241u8,101u8,44u8,190u8,76u8,233u8,178u8, - 147u8,155u8,125u8,233u8,103u8,157u8,94u8,92u8,206u8,110u8,158u8,205u8,159u8,108u8,61u8,111u8,5u8,143u8,184u8,167u8, - 28u8,157u8,235u8,76u8,89u8,156u8,191u8,143u8,235u8,46u8,29u8,198u8,163u8,133u8,7u8,233u8,55u8,24u8,68u8,173u8, - 243u8,114u8,94u8,156u8,199u8,152u8,123u8,187u8,245u8,232u8,98u8,30u8,220u8,152u8,142u8,63u8,33u8,215u8,82u8,235u8, - 111u8,101u8,126u8,137u8,58u8,167u8,157u8,157u8,34u8,191u8,176u8,93u8,158u8,220u8,254u8,210u8,230u8,104u8,18u8,49u8, - 180u8,113u8,99u8,58u8,156u8,97u8,209u8,242u8,231u8,18u8,152u8,228u8,98u8,25u8,212u8,251u8,101u8,33u8,179u8,73u8, - 174u8,157u8,3u8,44u8,247u8,244u8,81u8,54u8,66u8,255u8,66u8,210u8,44u8,41u8,60u8,42u8,168u8,234u8,58u8,198u8, - 20u8,82u8,240u8,163u8,132u8,81u8,52u8,246u8,46u8,104u8,224u8,124u8,217u8,91u8,75u8,248u8,197u8,70u8,197u8,202u8, - 71u8,233u8,16u8,238u8,56u8,165u8,171u8,21u8,177u8,166u8,30u8,209u8,113u8,110u8,79u8,81u8,225u8,76u8,215u8,247u8, - 85u8,163u8,43u8,57u8,155u8,127u8,186u8,159u8,171u8,221u8,79u8,255u8,126u8,173u8,243u8,174u8,87u8,223u8,85u8,117u8, - 229u8,39u8,249u8,36u8,109u8,181u8,155u8,220u8,84u8,55u8,31u8,133u8,149u8,227u8,171u8,175u8,147u8,33u8,221u8,107u8, - 177u8,5u8,21u8,253u8,102u8,18u8,12u8,56u8,193u8,12u8,215u8,154u8,140u8,117u8,29u8,196u8,209u8,60u8,197u8,27u8, - 229u8,94u8,157u8,91u8,113u8,199u8,110u8,70u8,17u8,54u8,108u8,162u8,112u8,87u8,30u8,164u8,149u8,56u8,162u8,146u8, - 9u8,161u8,141u8,10u8,183u8,42u8,134u8,109u8,67u8,207u8,157u8,124u8,62u8,67u8,119u8,158u8,65u8,142u8,181u8,170u8, - 62u8,165u8,75u8,174u8,20u8,79u8,23u8,182u8,204u8,60u8,140u8,154u8,240u8,177u8,23u8,33u8,214u8,156u8,102u8,54u8, - 35u8,203u8,22u8,18u8,233u8,65u8,248u8,193u8,169u8,200u8,141u8,147u8,182u8,78u8,216u8,235u8,189u8,128u8,4u8,41u8, - 128u8,91u8,55u8,27u8,84u8,59u8,146u8,176u8,154u8,233u8,5u8,128u8,247u8,81u8,70u8,59u8,50u8,232u8,174u8,71u8, - 230u8,92u8,138u8,122u8,187u8,171u8,93u8,1u8,30u8,179u8,167u8,183u8,200u8,15u8,26u8,144u8,99u8,70u8,110u8,51u8, - 105u8,47u8,89u8,9u8,115u8,117u8,74u8,203u8,165u8,81u8,172u8,18u8,164u8,106u8,144u8,68u8,173u8,173u8,33u8,86u8, - 152u8,91u8,145u8,254u8,30u8,139u8,13u8,3u8,166u8,226u8,136u8,207u8,171u8,28u8,66u8,96u8,136u8,14u8,139u8,119u8, - 85u8,90u8,184u8,59u8,247u8,129u8,248u8,110u8,57u8,119u8,27u8,225u8,208u8,247u8,145u8,57u8,218u8,0u8,183u8,88u8, - 210u8,147u8,97u8,123u8,153u8,8u8,4u8,203u8,155u8,17u8,115u8,48u8,26u8,35u8,10u8,7u8,125u8,114u8,45u8,84u8, - 133u8,41u8,40u8,13u8,103u8,217u8,164u8,48u8,234u8,96u8,161u8,115u8,19u8,5u8,101u8,33u8,166u8,27u8,137u8,108u8, - 132u8,87u8,229u8,218u8,82u8,172u8,13u8,41u8,115u8,203u8,206u8,14u8,166u8,105u8,1u8,92u8,151u8,191u8,151u8,204u8, - 179u8,202u8,171u8,128u8,95u8,91u8,203u8,14u8,147u8,96u8,8u8,65u8,21u8,204u8,170u8,244u8,48u8,33u8,55u8,188u8, - 70u8,49u8,4u8,86u8,46u8,27u8,122u8,123u8,14u8,210u8,97u8,50u8,47u8,220u8,228u8,118u8,242u8,117u8,90u8,174u8, - 173u8,252u8,206u8,28u8,215u8,61u8,135u8,229u8,178u8,151u8,221u8,186u8,204u8,248u8,233u8,230u8,126u8,186u8,185u8,15u8, - 244u8,230u8,30u8,50u8,55u8,108u8,221u8,155u8,244u8,23u8,139u8,182u8,96u8,78u8,172u8,58u8,228u8,98u8,5u8,74u8, - 138u8,37u8,143u8,41u8,151u8,180u8,214u8,229u8,113u8,85u8,37u8,100u8,84u8,66u8,50u8,6u8,36u8,29u8,221u8,136u8, - 100u8,220u8,197u8,90u8,169u8,102u8,21u8,245u8,83u8,182u8,156u8,97u8,50u8,30u8,206u8,199u8,36u8,240u8,180u8,218u8, - 142u8,247u8,143u8,44u8,50u8,116u8,65u8,9u8,204u8,118u8,177u8,14u8,227u8,134u8,125u8,124u8,147u8,249u8,69u8,76u8, - 121u8,207u8,138u8,2u8,116u8,37u8,177u8,86u8,64u8,96u8,253u8,206u8,111u8,191u8,245u8,219u8,206u8,224u8,144u8,236u8, - 102u8,125u8,23u8,164u8,154u8,23u8,85u8,142u8,223u8,141u8,190u8,174u8,250u8,62u8,6u8,196u8,204u8,225u8,110u8,73u8, - 154u8,255u8,245u8,215u8,95u8,243u8,109u8,245u8,10u8,212u8,98u8,211u8,216u8,254u8,170u8,36u8,255u8,248u8,199u8,92u8, - 118u8,199u8,250u8,38u8,180u8,94u8,191u8,149u8,179u8,82u8,187u8,129u8,185u8,134u8,240u8,55u8,230u8,234u8,116u8,35u8, - 91u8,143u8,5u8,140u8,161u8,200u8,48u8,47u8,207u8,59u8,49u8,219u8,56u8,187u8,184u8,132u8,183u8,74u8,86u8,164u8, - 239u8,72u8,129u8,164u8,62u8,214u8,31u8,142u8,210u8,211u8,108u8,34u8,146u8,212u8,157u8,230u8,200u8,164u8,138u8,29u8, - 203u8,236u8,234u8,64u8,129u8,45u8,107u8,89u8,72u8,240u8,133u8,125u8,62u8,143u8,131u8,19u8,110u8,67u8,211u8,32u8, - 118u8,60u8,230u8,182u8,136u8,153u8,71u8,57u8,139u8,170u8,73u8,84u8,14u8,212u8,10u8,226u8,203u8,23u8,220u8,36u8, - 218u8,86u8,208u8,233u8,24u8,67u8,190u8,177u8,184u8,16u8,134u8,245u8,99u8,45u8,115u8,172u8,190u8,4u8,55u8,15u8, - 243u8,187u8,74u8,130u8,0u8,119u8,119u8,12u8,95u8,1u8,33u8,184u8,72u8,225u8,233u8,211u8,41u8,219u8,34u8,135u8, - 147u8,141u8,208u8,37u8,149u8,74u8,199u8,232u8,118u8,234u8,245u8,152u8,0u8,221u8,154u8,147u8,79u8,71u8,148u8,130u8, - 90u8,98u8,218u8,117u8,227u8,9u8,169u8,63u8,94u8,88u8,44u8,56u8,0u8,92u8,124u8,134u8,74u8,0u8,120u8,183u8, - 160u8,69u8,156u8,57u8,116u8,32u8,62u8,48u8,71u8,152u8,18u8,150u8,100u8,254u8,250u8,219u8,158u8,22u8,228u8,242u8, - 83u8,44u8,109u8,58u8,159u8,208u8,115u8,14u8,16u8,192u8,30u8,214u8,28u8,85u8,100u8,47u8,246u8,48u8,199u8,129u8, - 220u8,239u8,98u8,206u8,73u8,241u8,162u8,162u8,57u8,194u8,115u8,241u8,82u8,20u8,70u8,127u8,23u8,186u8,101u8,239u8, - 176u8,239u8,32u8,21u8,221u8,61u8,91u8,184u8,123u8,89u8,119u8,169u8,157u8,243u8,24u8,102u8,168u8,18u8,73u8,51u8, - 214u8,159u8,198u8,52u8,234u8,219u8,111u8,155u8,17u8,169u8,158u8,15u8,213u8,33u8,83u8,155u8,189u8,173u8,222u8,246u8, - 183u8,253u8,198u8,164u8,170u8,231u8,77u8,149u8,18u8,83u8,216u8,25u8,37u8,233u8,121u8,170u8,49u8,101u8,98u8,162u8, - 15u8,156u8,255u8,244u8,116u8,108u8,214u8,150u8,255u8,243u8,211u8,187u8,48u8,162u8,224u8,238u8,247u8,191u8,254u8,250u8, - 155u8,173u8,141u8,111u8,196u8,182u8,6u8,52u8,207u8,49u8,74u8,166u8,22u8,83u8,68u8,118u8,88u8,88u8,78u8,24u8, - 168u8,209u8,136u8,19u8,10u8,102u8,160u8,14u8,105u8,60u8,32u8,235u8,147u8,33u8,221u8,115u8,74u8,216u8,96u8,98u8, - 71u8,132u8,231u8,106u8,29u8,99u8,208u8,126u8,61u8,227u8,160u8,212u8,166u8,33u8,3u8,210u8,99u8,104u8,239u8,224u8, - 183u8,179u8,217u8,185u8,153u8,153u8,11u8,71u8,43u8,245u8,149u8,174u8,141u8,43u8,67u8,53u8,135u8,81u8,122u8,128u8, - 110u8,122u8,43u8,139u8,158u8,33u8,92u8,147u8,249u8,123u8,203u8,240u8,210u8,129u8,225u8,18u8,132u8,22u8,47u8,56u8, - 120u8,39u8,202u8,140u8,241u8,73u8,180u8,62u8,141u8,74u8,25u8,166u8,203u8,94u8,202u8,47u8,75u8,120u8,93u8,75u8, - 117u8,135u8,95u8,62u8,3u8,67u8,165u8,51u8,75u8,235u8,65u8,214u8,13u8,201u8,34u8,246u8,52u8,62u8,56u8,179u8, - 130u8,22u8,25u8,234u8,102u8,203u8,143u8,63u8,220u8,78u8,154u8,213u8,118u8,181u8,64u8,118u8,145u8,95u8,217u8,46u8, - 238u8,88u8,18u8,78u8,85u8,82u8,64u8,249u8,239u8,147u8,135u8,205u8,159u8,172u8,8u8,194u8,29u8,57u8,172u8,184u8, - 20u8,212u8,165u8,31u8,43u8,166u8,216u8,81u8,174u8,106u8,238u8,43u8,246u8,13u8,121u8,238u8,25u8,68u8,164u8,87u8, - 22u8,57u8,148u8,254u8,171u8,94u8,22u8,125u8,208u8,28u8,92u8,89u8,14u8,19u8,84u8,145u8,38u8,244u8,232u8,133u8, - 7u8,161u8,112u8,15u8,92u8,196u8,105u8,74u8,244u8,48u8,230u8,239u8,107u8,73u8,30u8,137u8,29u8,250u8,23u8,51u8, - 210u8,175u8,119u8,180u8,159u8,189u8,101u8,82u8,98u8,233u8,89u8,54u8,205u8,70u8,215u8,107u8,206u8,38u8,13u8,221u8, - 124u8,73u8,35u8,238u8,62u8,171u8,252u8,177u8,172u8,134u8,10u8,66u8,199u8,64u8,36u8,36u8,29u8,136u8,202u8,230u8, - 40u8,151u8,118u8,240u8,239u8,51u8,68u8,44u8,76u8,213u8,164u8,44u8,53u8,240u8,25u8,88u8,131u8,190u8,82u8,150u8, - 154u8,114u8,163u8,75u8,180u8,106u8,40u8,3u8,52u8,207u8,33u8,98u8,151u8,171u8,50u8,58u8,130u8,26u8,3u8,181u8, - 12u8,97u8,43u8,7u8,191u8,251u8,210u8,148u8,36u8,68u8,53u8,212u8,162u8,149u8,204u8,192u8,12u8,245u8,221u8,81u8, - 9u8,22u8,58u8,134u8,50u8,172u8,172u8,235u8,189u8,19u8,129u8,201u8,165u8,99u8,208u8,151u8,138u8,47u8,237u8,124u8, - 121u8,149u8,77u8,130u8,112u8,56u8,76u8,97u8,117u8,12u8,164u8,23u8,116u8,109u8,179u8,186u8,75u8,48u8,51u8,195u8, - 88u8,164u8,252u8,129u8,103u8,137u8,53u8,77u8,148u8,124u8,205u8,84u8,39u8,229u8,24u8,242u8,178u8,49u8,23u8,139u8, - 5u8,179u8,107u8,237u8,94u8,135u8,27u8,139u8,159u8,141u8,189u8,35u8,21u8,243u8,81u8,13u8,195u8,51u8,114u8,65u8, - 237u8,58u8,199u8,214u8,225u8,199u8,228u8,231u8,197u8,29u8,106u8,197u8,236u8,236u8,230u8,225u8,57u8,242u8,96u8,119u8, - 89u8,28u8,234u8,84u8,205u8,165u8,122u8,214u8,141u8,119u8,213u8,237u8,80u8,63u8,115u8,127u8,151u8,249u8,111u8,58u8, - 213u8,115u8,90u8,248u8,66u8,187u8,180u8,252u8,99u8,184u8,213u8,182u8,4u8,73u8,38u8,222u8,154u8,59u8,239u8,177u8, - 1u8,131u8,8u8,248u8,148u8,205u8,78u8,78u8,98u8,239u8,190u8,123u8,26u8,60u8,134u8,85u8,30u8,142u8,47u8,70u8, - 220u8,86u8,126u8,24u8,38u8,61u8,177u8,243u8,81u8,39u8,170u8,235u8,180u8,92u8,197u8,135u8,134u8,53u8,231u8,133u8, - 132u8,111u8,138u8,66u8,247u8,234u8,5u8,21u8,176u8,111u8,17u8,37u8,168u8,143u8,212u8,180u8,146u8,221u8,224u8,86u8, - 130u8,137u8,159u8,91u8,162u8,111u8,55u8,20u8,194u8,30u8,194u8,252u8,213u8,138u8,10u8,169u8,51u8,35u8,57u8,177u8, - 37u8,139u8,37u8,228u8,105u8,100u8,103u8,194u8,17u8,196u8,223u8,235u8,193u8,131u8,122u8,39u8,136u8,39u8,27u8,27u8, - 189u8,222u8,98u8,78u8,16u8,232u8,87u8,247u8,145u8,160u8,210u8,82u8,136u8,241u8,171u8,171u8,129u8,92u8,12u8,25u8, - 31u8,60u8,26u8,241u8,12u8,138u8,242u8,202u8,73u8,17u8,186u8,37u8,116u8,109u8,68u8,229u8,219u8,244u8,183u8,115u8, - 61u8,91u8,32u8,142u8,139u8,12u8,96u8,157u8,200u8,178u8,2u8,8u8,213u8,61u8,210u8,98u8,177u8,245u8,59u8,59u8, - 162u8,58u8,24u8,9u8,203u8,248u8,146u8,22u8,124u8,219u8,105u8,96u8,57u8,5u8,73u8,61u8,223u8,151u8,145u8,86u8, - 255u8,235u8,193u8,64u8,193u8,239u8,42u8,13u8,27u8,172u8,204u8,126u8,67u8,157u8,166u8,174u8,71u8,150u8,102u8,183u8, - 114u8,169u8,190u8,226u8,143u8,137u8,148u8,7u8,48u8,69u8,60u8,163u8,90u8,177u8,187u8,81u8,232u8,5u8,136u8,253u8, - 184u8,39u8,96u8,217u8,217u8,79u8,246u8,7u8,224u8,232u8,27u8,157u8,17u8,183u8,108u8,236u8,154u8,1u8,61u8,36u8, - 18u8,146u8,17u8,254u8,220u8,94u8,80u8,46u8,153u8,228u8,49u8,141u8,131u8,72u8,90u8,170u8,29u8,66u8,251u8,116u8, - 79u8,91u8,164u8,24u8,239u8,223u8,228u8,6u8,1u8,6u8,205u8,80u8,93u8,80u8,181u8,69u8,245u8,170u8,97u8,52u8, - 33u8,207u8,162u8,253u8,163u8,151u8,47u8,15u8,246u8,79u8,226u8,189u8,87u8,47u8,226u8,23u8,135u8,199u8,39u8,111u8, - 14u8,191u8,251u8,249u8,228u8,32u8,254u8,97u8,239u8,56u8,254u8,254u8,224u8,224u8,88u8,96u8,198u8,110u8,244u8,228u8, - 233u8,93u8,150u8,98u8,42u8,51u8,230u8,2u8,109u8,152u8,102u8,131u8,249u8,44u8,181u8,49u8,185u8,184u8,255u8,132u8, - 24u8,15u8,59u8,83u8,20u8,104u8,16u8,126u8,66u8,1u8,191u8,0u8,30u8,0u8,122u8,132u8,68u8,96u8,37u8,156u8, - 249u8,120u8,156u8,138u8,132u8,53u8,104u8,55u8,213u8,219u8,139u8,31u8,160u8,229u8,107u8,130u8,184u8,100u8,68u8,0u8, - 156u8,2u8,37u8,7u8,8u8,5u8,224u8,28u8,101u8,120u8,137u8,229u8,7u8,241u8,233u8,56u8,57u8,11u8,115u8,146u8, - 26u8,164u8,250u8,213u8,98u8,57u8,46u8,150u8,27u8,99u8,138u8,217u8,146u8,71u8,137u8,129u8,9u8,103u8,73u8,33u8, - 112u8,160u8,205u8,185u8,144u8,106u8,143u8,18u8,73u8,111u8,105u8,161u8,165u8,160u8,64u8,219u8,64u8,87u8,166u8,187u8, - 164u8,186u8,213u8,104u8,237u8,224u8,228u8,131u8,13u8,130u8,228u8,65u8,44u8,87u8,5u8,203u8,78u8,23u8,110u8,134u8, - 80u8,210u8,78u8,151u8,245u8,64u8,239u8,47u8,162u8,242u8,30u8,234u8,81u8,221u8,146u8,98u8,123u8,141u8,185u8,58u8, - 130u8,161u8,157u8,175u8,210u8,223u8,103u8,88u8,48u8,240u8,130u8,44u8,168u8,30u8,157u8,144u8,25u8,173u8,102u8,231u8, - 211u8,52u8,149u8,58u8,72u8,116u8,128u8,195u8,106u8,139u8,165u8,187u8,126u8,50u8,229u8,139u8,0u8,20u8,148u8,65u8, - 162u8,67u8,152u8,209u8,139u8,146u8,51u8,224u8,90u8,118u8,198u8,234u8,217u8,52u8,153u8,20u8,9u8,141u8,228u8,72u8, - 40u8,70u8,200u8,78u8,56u8,119u8,117u8,53u8,136u8,62u8,11u8,98u8,123u8,17u8,16u8,252u8,44u8,250u8,2u8,132u8, - 21u8,106u8,133u8,247u8,232u8,156u8,10u8,37u8,147u8,241u8,31u8,168u8,25u8,94u8,41u8,114u8,78u8,39u8,7u8,138u8, - 236u8,236u8,220u8,184u8,124u8,152u8,208u8,85u8,184u8,7u8,154u8,64u8,74u8,34u8,55u8,194u8,52u8,223u8,117u8,181u8, - 71u8,2u8,83u8,21u8,57u8,16u8,54u8,26u8,21u8,155u8,8u8,108u8,24u8,129u8,216u8,230u8,65u8,212u8,202u8,76u8, - 155u8,65u8,221u8,193u8,50u8,37u8,179u8,250u8,107u8,47u8,153u8,181u8,124u8,29u8,7u8,139u8,219u8,4u8,227u8,41u8, - 14u8,20u8,9u8,4u8,78u8,106u8,210u8,202u8,11u8,44u8,78u8,74u8,40u8,130u8,206u8,38u8,41u8,198u8,89u8,136u8, - 242u8,24u8,74u8,129u8,15u8,183u8,4u8,208u8,12u8,179u8,172u8,101u8,23u8,151u8,240u8,90u8,77u8,38u8,179u8,241u8, - 77u8,199u8,4u8,45u8,218u8,227u8,238u8,119u8,23u8,61u8,149u8,94u8,248u8,84u8,234u8,186u8,246u8,151u8,239u8,186u8, - 42u8,46u8,108u8,89u8,209u8,122u8,203u8,226u8,194u8,147u8,229u8,163u8,128u8,55u8,235u8,34u8,7u8,63u8,124u8,246u8, - 225u8,179u8,255u8,15u8,46u8,154u8,246u8,179u8,247u8,12u8,2u8,0u8,0u8,0u8,15u8,116u8,114u8,97u8,110u8,115u8, - 97u8,99u8,116u8,105u8,111u8,110u8,95u8,102u8,101u8,101u8,218u8,26u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,221u8,91u8,123u8,111u8,219u8,70u8,18u8,255u8,63u8,159u8,98u8,131u8,67u8,13u8,41u8,96u8,20u8,201u8, - 73u8,122u8,142u8,28u8,7u8,113u8,92u8,55u8,23u8,32u8,47u8,196u8,73u8,123u8,135u8,34u8,96u8,87u8,228u8,74u8, - 34u8,66u8,113u8,117u8,75u8,210u8,138u8,18u8,248u8,187u8,223u8,204u8,238u8,146u8,220u8,93u8,62u8,36u8,57u8,114u8, - 47u8,141u8,209u8,162u8,54u8,31u8,179u8,179u8,243u8,248u8,205u8,111u8,102u8,217u8,123u8,247u8,238u8,145u8,247u8,243u8, - 40u8,37u8,11u8,30u8,230u8,49u8,35u8,75u8,193u8,47u8,163u8,144u8,165u8,132u8,38u8,36u8,74u8,50u8,38u8,166u8, - 52u8,96u8,36u8,227u8,100u8,146u8,139u8,132u8,112u8,65u8,2u8,30u8,199u8,44u8,200u8,224u8,126u8,72u8,4u8,11u8, - 163u8,52u8,19u8,209u8,36u8,207u8,224u8,25u8,65u8,147u8,148u8,6u8,89u8,196u8,19u8,50u8,101u8,44u8,29u8,220u8, - 210u8,34u8,233u8,50u8,227u8,169u8,63u8,21u8,116u8,193u8,86u8,92u8,124u8,26u8,143u8,141u8,39u8,125u8,120u8,146u8, - 124u8,189u8,69u8,224u8,39u8,79u8,27u8,30u8,13u8,120u8,148u8,140u8,199u8,95u8,47u8,88u8,60u8,245u8,200u8,233u8, - 108u8,38u8,216u8,140u8,102u8,116u8,18u8,179u8,51u8,184u8,225u8,145u8,103u8,160u8,212u8,25u8,93u8,210u8,73u8,20u8, - 71u8,217u8,218u8,35u8,120u8,245u8,234u8,184u8,93u8,156u8,186u8,160u8,132u8,158u8,226u8,239u8,248u8,66u8,199u8,243u8, - 105u8,70u8,63u8,177u8,174u8,251u8,235u8,52u8,99u8,11u8,159u8,134u8,161u8,96u8,105u8,202u8,210u8,234u8,209u8,52u8, - 11u8,199u8,99u8,38u8,4u8,23u8,206u8,53u8,190u8,196u8,125u8,151u8,123u8,122u8,35u8,255u8,4u8,157u8,229u8,83u8, - 83u8,17u8,49u8,176u8,107u8,109u8,157u8,73u8,204u8,131u8,79u8,199u8,157u8,143u8,204u8,88u8,194u8,210u8,40u8,237u8, - 126u8,72u8,176u8,128u8,39u8,211u8,104u8,150u8,11u8,138u8,203u8,118u8,63u8,108u8,186u8,233u8,146u8,198u8,81u8,168u8, - 223u8,145u8,47u8,221u8,131u8,184u8,121u8,78u8,83u8,233u8,105u8,66u8,5u8,216u8,38u8,22u8,140u8,134u8,107u8,50u8, - 97u8,81u8,50u8,43u8,162u8,132u8,133u8,50u8,78u8,178u8,57u8,110u8,94u8,228u8,16u8,54u8,115u8,30u8,135u8,240u8, - 64u8,41u8,35u8,74u8,166u8,92u8,44u8,164u8,100u8,66u8,39u8,60u8,207u8,204u8,87u8,23u8,60u8,79u8,178u8,148u8, - 64u8,116u8,22u8,210u8,151u8,249u8,36u8,142u8,210u8,57u8,11u8,7u8,82u8,2u8,236u8,38u8,205u8,200u8,249u8,233u8, - 203u8,119u8,231u8,167u8,191u8,252u8,199u8,63u8,123u8,243u8,242u8,229u8,249u8,217u8,251u8,23u8,175u8,159u8,251u8,191u8, - 158u8,159u8,95u8,140u8,73u8,254u8,243u8,3u8,114u8,66u8,70u8,134u8,206u8,239u8,65u8,21u8,25u8,202u8,75u8,38u8, - 2u8,150u8,100u8,116u8,198u8,80u8,62u8,46u8,204u8,167u8,4u8,118u8,12u8,127u8,255u8,49u8,244u8,200u8,104u8,56u8, - 252u8,104u8,173u8,241u8,226u8,245u8,111u8,167u8,47u8,95u8,252u8,226u8,63u8,251u8,240u8,238u8,181u8,255u8,246u8,252u8, - 221u8,217u8,249u8,235u8,247u8,167u8,207u8,207u8,139u8,53u8,238u8,27u8,107u8,92u8,100u8,28u8,194u8,65u8,45u8,19u8, - 148u8,209u8,89u8,38u8,17u8,90u8,99u8,166u8,77u8,167u8,150u8,208u8,182u8,41u8,35u8,178u8,12u8,233u8,8u8,196u8, - 204u8,225u8,209u8,79u8,108u8,173u8,179u8,4u8,127u8,80u8,138u8,15u8,130u8,199u8,78u8,252u8,63u8,46u8,5u8,60u8, - 241u8,228u8,195u8,87u8,53u8,157u8,234u8,230u8,70u8,109u8,100u8,120u8,97u8,234u8,47u8,121u8,202u8,68u8,233u8,50u8, - 101u8,127u8,180u8,11u8,170u8,90u8,138u8,170u8,28u8,180u8,154u8,179u8,132u8,176u8,207u8,44u8,200u8,51u8,116u8,122u8, - 41u8,202u8,218u8,213u8,89u8,241u8,248u8,175u8,32u8,228u8,45u8,19u8,207u8,228u8,98u8,245u8,93u8,169u8,213u8,198u8, - 181u8,44u8,175u8,237u8,10u8,127u8,10u8,93u8,199u8,58u8,129u8,30u8,235u8,20u8,52u8,30u8,145u8,86u8,170u8,188u8, - 12u8,142u8,58u8,170u8,89u8,229u8,69u8,2u8,54u8,134u8,208u8,254u8,2u8,166u8,65u8,237u8,65u8,4u8,207u8,225u8, - 13u8,208u8,157u8,11u8,220u8,82u8,221u8,94u8,133u8,231u8,10u8,51u8,200u8,59u8,73u8,88u8,138u8,44u8,17u8,17u8, - 110u8,12u8,200u8,197u8,156u8,231u8,113u8,8u8,73u8,1u8,129u8,0u8,143u8,195u8,111u8,107u8,194u8,147u8,187u8,193u8, - 156u8,70u8,9u8,153u8,241u8,75u8,38u8,18u8,154u8,4u8,76u8,25u8,76u8,6u8,119u8,64u8,166u8,57u8,66u8,111u8, - 161u8,24u8,34u8,164u8,95u8,173u8,229u8,195u8,90u8,190u8,185u8,70u8,207u8,205u8,91u8,114u8,144u8,70u8,179u8,132u8, - 9u8,175u8,201u8,2u8,125u8,195u8,224u8,46u8,120u8,1u8,60u8,194u8,127u8,68u8,230u8,59u8,18u8,221u8,21u8,250u8, - 199u8,149u8,207u8,228u8,11u8,183u8,123u8,229u8,5u8,252u8,185u8,205u8,62u8,131u8,126u8,233u8,227u8,70u8,199u8,63u8, - 233u8,61u8,117u8,197u8,121u8,214u8,219u8,18u8,51u8,65u8,21u8,149u8,240u8,190u8,146u8,213u8,107u8,75u8,241u8,126u8, - 249u8,110u8,131u8,86u8,206u8,254u8,201u8,227u8,19u8,76u8,107u8,175u8,88u8,2u8,188u8,233u8,243u8,169u8,47u8,115u8, - 190u8,215u8,150u8,223u8,253u8,190u8,78u8,108u8,229u8,95u8,242u8,10u8,10u8,2u8,73u8,115u8,129u8,33u8,66u8,63u8, - 65u8,132u8,232u8,34u8,135u8,24u8,181u8,66u8,28u8,132u8,108u8,113u8,43u8,161u8,17u8,42u8,131u8,202u8,250u8,88u8, - 90u8,198u8,99u8,195u8,209u8,26u8,99u8,185u8,64u8,151u8,167u8,13u8,86u8,55u8,245u8,208u8,145u8,27u8,67u8,209u8, - 75u8,120u8,133u8,194u8,117u8,220u8,117u8,114u8,27u8,244u8,76u8,120u8,70u8,82u8,150u8,85u8,170u8,196u8,204u8,64u8, - 93u8,185u8,56u8,0u8,90u8,115u8,218u8,126u8,181u8,124u8,85u8,164u8,172u8,42u8,168u8,198u8,86u8,168u8,145u8,195u8, - 178u8,220u8,246u8,186u8,157u8,94u8,165u8,115u8,81u8,30u8,19u8,158u8,176u8,158u8,243u8,148u8,227u8,207u8,234u8,230u8, - 85u8,229u8,251u8,5u8,228u8,148u8,159u8,113u8,119u8,61u8,207u8,217u8,160u8,142u8,22u8,13u8,5u8,50u8,227u8,82u8, - 121u8,195u8,76u8,53u8,150u8,160u8,254u8,97u8,175u8,63u8,38u8,19u8,206u8,99u8,99u8,243u8,59u8,70u8,120u8,13u8, - 140u8,89u8,166u8,240u8,198u8,173u8,68u8,211u8,138u8,96u8,129u8,11u8,165u8,39u8,160u8,106u8,80u8,146u8,176u8,21u8, - 129u8,240u8,200u8,217u8,245u8,17u8,37u8,95u8,206u8,4u8,13u8,153u8,239u8,24u8,177u8,74u8,221u8,86u8,28u8,41u8, - 159u8,0u8,45u8,252u8,6u8,76u8,145u8,247u8,251u8,132u8,6u8,255u8,205u8,35u8,172u8,49u8,141u8,101u8,204u8,219u8, - 24u8,80u8,123u8,132u8,163u8,6u8,69u8,247u8,146u8,252u8,111u8,69u8,4u8,14u8,2u8,143u8,40u8,99u8,150u8,53u8, - 207u8,246u8,162u8,71u8,22u8,37u8,72u8,192u8,179u8,16u8,219u8,1u8,108u8,168u8,242u8,171u8,41u8,81u8,114u8,0u8, - 242u8,6u8,164u8,136u8,85u8,4u8,20u8,113u8,5u8,255u8,72u8,255u8,34u8,95u8,68u8,217u8,232u8,249u8,94u8,148u8, - 4u8,92u8,0u8,119u8,203u8,250u8,110u8,14u8,200u8,18u8,108u8,10u8,212u8,171u8,161u8,106u8,50u8,124u8,98u8,10u8, - 28u8,254u8,182u8,89u8,55u8,241u8,182u8,111u8,103u8,67u8,207u8,220u8,103u8,52u8,133u8,5u8,59u8,146u8,161u8,239u8, - 128u8,0u8,44u8,250u8,65u8,197u8,150u8,44u8,234u8,128u8,70u8,108u8,58u8,197u8,14u8,33u8,79u8,98u8,220u8,119u8, - 137u8,77u8,14u8,51u8,28u8,88u8,82u8,16u8,131u8,220u8,189u8,157u8,144u8,131u8,5u8,84u8,220u8,9u8,236u8,157u8, - 175u8,252u8,89u8,204u8,39u8,52u8,246u8,225u8,202u8,214u8,121u8,55u8,112u8,36u8,30u8,91u8,75u8,222u8,169u8,175u8, - 215u8,16u8,56u8,21u8,200u8,184u8,89u8,252u8,142u8,205u8,0u8,5u8,152u8,80u8,169u8,92u8,34u8,44u8,22u8,128u8, - 18u8,119u8,49u8,161u8,27u8,24u8,195u8,64u8,245u8,92u8,144u8,151u8,242u8,207u8,138u8,90u8,65u8,215u8,197u8,147u8, - 120u8,109u8,164u8,55u8,213u8,20u8,13u8,22u8,75u8,18u8,52u8,160u8,41u8,223u8,204u8,242u8,158u8,226u8,241u8,125u8, - 153u8,237u8,66u8,235u8,230u8,23u8,106u8,249u8,83u8,85u8,87u8,12u8,151u8,246u8,202u8,123u8,152u8,115u8,99u8,162u8, - 51u8,207u8,200u8,228u8,77u8,41u8,187u8,107u8,168u8,52u8,22u8,154u8,235u8,187u8,247u8,184u8,38u8,220u8,7u8,121u8, - 69u8,5u8,73u8,87u8,116u8,233u8,227u8,166u8,163u8,56u8,238u8,201u8,64u8,178u8,151u8,30u8,20u8,219u8,247u8,136u8, - 101u8,8u8,67u8,108u8,205u8,229u8,200u8,183u8,33u8,152u8,73u8,186u8,100u8,65u8,52u8,141u8,16u8,163u8,133u8,46u8, - 244u8,218u8,45u8,88u8,236u8,6u8,101u8,81u8,81u8,76u8,29u8,46u8,249u8,197u8,115u8,61u8,89u8,43u8,85u8,96u8, - 187u8,236u8,182u8,153u8,176u8,117u8,227u8,170u8,73u8,160u8,111u8,138u8,246u8,216u8,110u8,211u8,93u8,193u8,137u8,174u8, - 250u8,178u8,44u8,201u8,93u8,25u8,134u8,67u8,251u8,212u8,97u8,226u8,119u8,134u8,58u8,230u8,11u8,68u8,54u8,8u8, - 235u8,63u8,93u8,69u8,239u8,212u8,86u8,249u8,147u8,132u8,156u8,41u8,198u8,130u8,149u8,109u8,26u8,243u8,149u8,13u8, - 27u8,90u8,222u8,102u8,73u8,104u8,133u8,87u8,167u8,255u8,246u8,63u8,252u8,252u8,224u8,184u8,137u8,55u8,224u8,30u8, - 213u8,147u8,192u8,30u8,36u8,6u8,192u8,14u8,107u8,166u8,132u8,76u8,134u8,118u8,175u8,223u8,36u8,255u8,30u8,26u8, - 249u8,216u8,74u8,13u8,71u8,222u8,19u8,50u8,108u8,78u8,8u8,8u8,143u8,106u8,77u8,101u8,85u8,246u8,57u8,195u8, - 128u8,145u8,118u8,245u8,28u8,197u8,156u8,184u8,87u8,47u8,224u8,13u8,155u8,134u8,23u8,247u8,138u8,215u8,188u8,218u8, - 221u8,3u8,43u8,245u8,30u8,55u8,198u8,87u8,43u8,170u8,66u8,255u8,105u8,75u8,236u8,202u8,155u8,51u8,26u8,7u8, - 57u8,150u8,34u8,133u8,149u8,56u8,244u8,89u8,205u8,163u8,96u8,78u8,210u8,146u8,205u8,84u8,147u8,164u8,16u8,171u8, - 102u8,83u8,91u8,42u8,145u8,176u8,148u8,137u8,51u8,11u8,72u8,58u8,64u8,76u8,182u8,228u8,193u8,220u8,211u8,19u8, - 41u8,168u8,148u8,33u8,244u8,185u8,25u8,180u8,80u8,106u8,238u8,32u8,105u8,133u8,3u8,184u8,13u8,64u8,91u8,202u8, - 237u8,4u8,92u8,156u8,128u8,133u8,185u8,108u8,13u8,157u8,89u8,74u8,43u8,20u8,183u8,149u8,220u8,111u8,38u8,76u8, - 24u8,98u8,183u8,119u8,129u8,95u8,193u8,50u8,240u8,92u8,91u8,244u8,239u8,21u8,152u8,173u8,86u8,69u8,218u8,15u8, - 71u8,68u8,240u8,47u8,208u8,3u8,155u8,224u8,122u8,202u8,15u8,121u8,2u8,45u8,137u8,85u8,72u8,129u8,234u8,50u8, - 197u8,129u8,148u8,129u8,105u8,44u8,249u8,177u8,41u8,25u8,94u8,145u8,131u8,135u8,50u8,68u8,32u8,112u8,236u8,6u8, - 2u8,220u8,104u8,97u8,6u8,218u8,76u8,119u8,43u8,105u8,189u8,75u8,241u8,191u8,48u8,193u8,123u8,7u8,78u8,157u8, - 80u8,217u8,87u8,179u8,38u8,138u8,42u8,22u8,3u8,97u8,41u8,95u8,176u8,218u8,171u8,133u8,98u8,181u8,151u8,155u8, - 10u8,86u8,145u8,243u8,93u8,181u8,202u8,201u8,253u8,171u8,227u8,13u8,14u8,54u8,173u8,85u8,177u8,204u8,153u8,54u8, - 181u8,229u8,9u8,149u8,64u8,193u8,156u8,65u8,144u8,193u8,222u8,32u8,131u8,48u8,73u8,204u8,180u8,148u8,92u8,210u8, - 109u8,27u8,163u8,10u8,180u8,66u8,1u8,61u8,72u8,67u8,239u8,215u8,180u8,33u8,109u8,84u8,27u8,48u8,175u8,109u8, - 78u8,216u8,221u8,185u8,178u8,158u8,26u8,92u8,41u8,38u8,131u8,153u8,91u8,134u8,134u8,138u8,64u8,9u8,17u8,24u8, - 105u8,176u8,189u8,90u8,176u8,40u8,144u8,112u8,5u8,3u8,108u8,44u8,169u8,200u8,34u8,4u8,48u8,129u8,145u8,154u8, - 178u8,105u8,30u8,203u8,190u8,236u8,146u8,71u8,216u8,104u8,99u8,244u8,194u8,200u8,28u8,114u8,57u8,36u8,41u8,12u8, - 207u8,239u8,42u8,2u8,156u8,34u8,57u8,23u8,138u8,18u8,72u8,6u8,236u8,74u8,174u8,204u8,15u8,246u8,5u8,238u8, - 9u8,21u8,206u8,178u8,55u8,23u8,77u8,168u8,184u8,18u8,28u8,66u8,158u8,6u8,1u8,26u8,112u8,80u8,171u8,177u8, - 81u8,54u8,135u8,199u8,64u8,105u8,212u8,104u8,150u8,120u8,102u8,71u8,224u8,180u8,150u8,12u8,135u8,91u8,129u8,196u8, - 100u8,0u8,61u8,64u8,65u8,117u8,121u8,5u8,132u8,9u8,113u8,17u8,97u8,62u8,115u8,229u8,107u8,82u8,111u8,49u8, - 94u8,88u8,172u8,34u8,68u8,26u8,118u8,75u8,236u8,142u8,249u8,12u8,219u8,214u8,223u8,113u8,84u8,184u8,194u8,22u8, - 225u8,18u8,220u8,96u8,52u8,170u8,238u8,2u8,74u8,42u8,141u8,149u8,82u8,176u8,249u8,217u8,12u8,41u8,182u8,131u8, - 182u8,30u8,202u8,50u8,14u8,31u8,150u8,0u8,183u8,101u8,207u8,131u8,94u8,46u8,38u8,173u8,174u8,120u8,44u8,61u8, - 72u8,202u8,171u8,13u8,208u8,216u8,104u8,192u8,60u8,181u8,172u8,44u8,80u8,69u8,101u8,138u8,25u8,180u8,115u8,165u8, - 249u8,19u8,72u8,83u8,147u8,124u8,155u8,217u8,80u8,90u8,100u8,231u8,164u8,118u8,213u8,188u8,128u8,150u8,143u8,25u8, - 134u8,196u8,84u8,156u8,72u8,141u8,195u8,60u8,80u8,77u8,63u8,222u8,252u8,237u8,21u8,4u8,114u8,42u8,15u8,18u8, - 10u8,219u8,130u8,154u8,85u8,251u8,185u8,194u8,234u8,150u8,5u8,115u8,87u8,188u8,12u8,144u8,128u8,166u8,108u8,80u8, - 131u8,180u8,106u8,15u8,39u8,228u8,233u8,229u8,194u8,199u8,116u8,17u8,151u8,44u8,108u8,66u8,176u8,6u8,122u8,171u8, - 247u8,137u8,172u8,5u8,248u8,144u8,3u8,87u8,21u8,93u8,129u8,224u8,204u8,4u8,95u8,43u8,192u8,117u8,200u8,99u8, - 11u8,154u8,185u8,136u8,182u8,89u8,1u8,199u8,220u8,14u8,153u8,115u8,86u8,212u8,243u8,56u8,128u8,14u8,223u8,57u8, - 159u8,234u8,85u8,141u8,66u8,131u8,166u8,221u8,152u8,11u8,181u8,79u8,34u8,42u8,164u8,35u8,64u8,5u8,242u8,216u8, - 37u8,80u8,86u8,15u8,96u8,98u8,177u8,68u8,246u8,129u8,81u8,138u8,137u8,87u8,101u8,166u8,70u8,11u8,9u8,84u8, - 40u8,153u8,32u8,54u8,13u8,110u8,93u8,199u8,228u8,109u8,166u8,110u8,234u8,106u8,234u8,67u8,203u8,8u8,137u8,85u8, - 164u8,210u8,183u8,141u8,219u8,72u8,109u8,208u8,68u8,26u8,142u8,202u8,38u8,210u8,67u8,25u8,99u8,69u8,150u8,183u8, - 110u8,96u8,42u8,38u8,11u8,155u8,227u8,11u8,163u8,53u8,178u8,153u8,173u8,94u8,204u8,166u8,159u8,88u8,193u8,110u8, - 221u8,12u8,187u8,181u8,135u8,133u8,146u8,207u8,234u8,67u8,208u8,107u8,25u8,77u8,251u8,122u8,123u8,187u8,109u8,34u8, - 130u8,55u8,202u8,222u8,254u8,5u8,37u8,76u8,162u8,139u8,58u8,228u8,91u8,209u8,117u8,42u8,193u8,109u8,1u8,224u8, - 11u8,163u8,69u8,12u8,84u8,125u8,181u8,56u8,25u8,86u8,51u8,45u8,197u8,250u8,154u8,6u8,207u8,158u8,41u8,30u8, - 160u8,88u8,130u8,84u8,130u8,55u8,173u8,243u8,228u8,106u8,124u8,37u8,1u8,27u8,9u8,1u8,60u8,64u8,83u8,48u8, - 117u8,143u8,13u8,102u8,131u8,34u8,12u8,170u8,151u8,3u8,22u8,1u8,0u8,162u8,163u8,207u8,202u8,48u8,215u8,203u8, - 172u8,88u8,89u8,17u8,22u8,50u8,225u8,0u8,228u8,57u8,142u8,73u8,85u8,95u8,0u8,250u8,203u8,45u8,202u8,130u8, - 14u8,48u8,138u8,165u8,132u8,106u8,46u8,132u8,139u8,99u8,105u8,94u8,91u8,222u8,182u8,189u8,220u8,209u8,37u8,183u8, - 147u8,31u8,55u8,81u8,139u8,184u8,128u8,99u8,119u8,94u8,103u8,81u8,102u8,54u8,20u8,9u8,160u8,104u8,155u8,187u8, - 104u8,61u8,92u8,223u8,32u8,195u8,214u8,243u8,36u8,221u8,189u8,232u8,227u8,226u8,214u8,32u8,197u8,3u8,48u8,230u8, - 87u8,231u8,229u8,126u8,145u8,22u8,155u8,206u8,155u8,186u8,207u8,37u8,247u8,124u8,254u8,212u8,58u8,237u8,111u8,129u8, - 154u8,82u8,69u8,114u8,101u8,161u8,224u8,63u8,254u8,128u8,214u8,20u8,198u8,34u8,96u8,167u8,143u8,29u8,95u8,15u8, - 104u8,159u8,224u8,80u8,9u8,194u8,128u8,139u8,245u8,177u8,249u8,186u8,171u8,5u8,56u8,191u8,150u8,91u8,31u8,203u8, - 209u8,144u8,92u8,240u8,91u8,142u8,249u8,148u8,213u8,119u8,128u8,137u8,186u8,250u8,141u8,103u8,54u8,213u8,109u8,57u8, - 51u8,148u8,59u8,59u8,104u8,119u8,193u8,14u8,91u8,56u8,168u8,185u8,233u8,240u8,161u8,131u8,54u8,103u8,50u8,229u8, - 138u8,111u8,5u8,96u8,220u8,50u8,97u8,192u8, - ]; - let chunk3 = vector[ - 27u8,157u8,83u8,127u8,115u8,216u8,181u8,235u8,89u8,35u8,25u8,54u8,174u8,40u8,89u8,159u8,222u8,10u8,1u8, - 192u8,68u8,102u8,138u8,144u8,53u8,44u8,206u8,209u8,4u8,83u8,8u8,86u8,1u8,218u8,230u8,35u8,52u8,187u8,0u8, - 237u8,62u8,213u8,44u8,246u8,120u8,205u8,134u8,85u8,237u8,213u8,21u8,102u8,244u8,88u8,178u8,235u8,105u8,237u8,177u8, - 154u8,95u8,239u8,230u8,84u8,72u8,26u8,15u8,31u8,86u8,111u8,94u8,125u8,99u8,126u8,104u8,46u8,160u8,185u8,88u8, - 160u8,167u8,72u8,219u8,165u8,195u8,38u8,182u8,177u8,225u8,3u8,33u8,123u8,56u8,210u8,43u8,89u8,1u8,89u8,0u8, - 62u8,227u8,111u8,125u8,208u8,223u8,252u8,158u8,200u8,76u8,132u8,45u8,242u8,166u8,29u8,96u8,235u8,105u8,82u8,220u8, - 170u8,13u8,101u8,71u8,101u8,239u8,141u8,90u8,153u8,37u8,66u8,78u8,124u8,15u8,74u8,93u8,235u8,142u8,188u8,83u8, - 4u8,130u8,138u8,82u8,140u8,3u8,148u8,147u8,230u8,75u8,32u8,166u8,166u8,36u8,104u8,182u8,79u8,244u8,4u8,217u8, - 205u8,29u8,132u8,121u8,44u8,38u8,135u8,15u8,127u8,218u8,130u8,161u8,142u8,116u8,190u8,55u8,135u8,183u8,154u8,35u8, - 31u8,4u8,35u8,185u8,220u8,63u8,31u8,54u8,135u8,223u8,142u8,90u8,23u8,98u8,154u8,148u8,30u8,110u8,169u8,243u8, - 240u8,251u8,81u8,89u8,176u8,5u8,76u8,88u8,240u8,55u8,112u8,199u8,150u8,234u8,219u8,45u8,65u8,215u8,6u8,134u8, - 123u8,209u8,223u8,13u8,147u8,166u8,38u8,100u8,212u8,119u8,70u8,143u8,135u8,29u8,113u8,188u8,231u8,48u8,118u8,212u8, - 107u8,53u8,221u8,161u8,71u8,54u8,7u8,235u8,161u8,37u8,244u8,175u8,212u8,109u8,115u8,167u8,119u8,216u8,122u8,191u8, - 68u8,26u8,3u8,87u8,154u8,159u8,44u8,12u8,223u8,115u8,60u8,176u8,43u8,174u8,195u8,108u8,17u8,8u8,38u8,158u8, - 176u8,62u8,29u8,126u8,166u8,163u8,81u8,0u8,180u8,117u8,194u8,39u8,234u8,239u8,201u8,112u8,2u8,36u8,150u8,194u8, - 168u8,72u8,253u8,29u8,208u8,209u8,112u8,228u8,86u8,2u8,57u8,228u8,182u8,216u8,68u8,251u8,135u8,11u8,238u8,119u8, - 11u8,114u8,237u8,250u8,101u8,80u8,160u8,126u8,81u8,234u8,97u8,95u8,254u8,246u8,73u8,125u8,249u8,241u8,167u8,18u8, - 123u8,188u8,77u8,13u8,210u8,28u8,255u8,120u8,251u8,122u8,213u8,240u8,121u8,80u8,244u8,133u8,218u8,95u8,29u8,125u8, - 87u8,181u8,108u8,47u8,20u8,114u8,84u8,35u8,116u8,208u8,36u8,66u8,251u8,24u8,230u8,139u8,197u8,186u8,232u8,17u8, - 83u8,219u8,2u8,50u8,32u8,100u8,227u8,1u8,251u8,86u8,62u8,145u8,147u8,31u8,236u8,67u8,224u8,108u8,20u8,86u8, - 193u8,7u8,28u8,144u8,130u8,112u8,233u8,122u8,5u8,110u8,187u8,168u8,134u8,161u8,212u8,245u8,138u8,124u8,192u8,196u8, - 13u8,211u8,241u8,208u8,15u8,202u8,141u8,20u8,127u8,247u8,42u8,165u8,183u8,126u8,165u8,80u8,121u8,235u8,23u8,42u8, - 149u8,27u8,32u8,1u8,104u8,97u8,100u8,170u8,225u8,25u8,144u8,141u8,124u8,195u8,102u8,28u8,173u8,2u8,10u8,165u8, - 174u8,249u8,122u8,165u8,226u8,214u8,2u8,174u8,137u8,199u8,247u8,149u8,200u8,90u8,49u8,150u8,201u8,61u8,194u8,129u8, - 161u8,48u8,3u8,171u8,253u8,211u8,154u8,106u8,84u8,184u8,197u8,71u8,25u8,150u8,155u8,91u8,26u8,21u8,213u8,143u8, - 172u8,212u8,215u8,54u8,178u8,217u8,48u8,147u8,164u8,24u8,193u8,79u8,35u8,145u8,214u8,70u8,213u8,127u8,159u8,70u8, - 165u8,193u8,91u8,205u8,141u8,10u8,250u8,202u8,140u8,202u8,225u8,141u8,185u8,254u8,34u8,90u8,200u8,83u8,108u8,119u8, - 238u8,103u8,126u8,11u8,124u8,87u8,157u8,52u8,193u8,52u8,73u8,207u8,120u8,139u8,177u8,152u8,28u8,96u8,73u8,131u8, - 227u8,112u8,147u8,60u8,227u8,147u8,129u8,17u8,226u8,213u8,72u8,176u8,202u8,15u8,183u8,198u8,55u8,61u8,243u8,112u8, - 139u8,103u8,30u8,12u8,221u8,141u8,188u8,230u8,43u8,84u8,128u8,44u8,242u8,52u8,83u8,199u8,6u8,152u8,63u8,68u8, - 30u8,237u8,64u8,244u8,224u8,72u8,172u8,56u8,96u8,34u8,167u8,178u8,114u8,99u8,27u8,124u8,38u8,107u8,180u8,58u8, - 101u8,192u8,147u8,117u8,8u8,132u8,226u8,27u8,253u8,65u8,75u8,44u8,232u8,86u8,218u8,26u8,94u8,85u8,1u8,174u8, - 27u8,139u8,225u8,176u8,139u8,101u8,55u8,136u8,40u8,113u8,13u8,5u8,60u8,218u8,253u8,125u8,3u8,230u8,28u8,21u8, - 234u8,137u8,126u8,120u8,83u8,137u8,110u8,128u8,179u8,149u8,230u8,246u8,25u8,93u8,17u8,43u8,134u8,171u8,228u8,72u8, - 4u8,246u8,136u8,21u8,69u8,30u8,75u8,73u8,15u8,213u8,61u8,160u8,79u8,52u8,224u8,176u8,215u8,254u8,180u8,216u8, - 245u8,192u8,163u8,157u8,237u8,247u8,183u8,113u8,225u8,105u8,156u8,114u8,207u8,24u8,124u8,169u8,243u8,106u8,60u8,28u8, - 197u8,163u8,234u8,226u8,43u8,105u8,158u8,193u8,208u8,71u8,65u8,129u8,60u8,203u8,140u8,163u8,217u8,60u8,195u8,204u8, - 157u8,227u8,55u8,82u8,33u8,129u8,226u8,242u8,19u8,158u8,37u8,203u8,28u8,137u8,212u8,255u8,216u8,145u8,245u8,127u8, - 120u8,76u8,173u8,0u8,100u8,15u8,136u8,122u8,248u8,232u8,209u8,117u8,17u8,21u8,166u8,84u8,48u8,232u8,197u8,143u8, - 105u8,163u8,5u8,27u8,108u8,198u8,195u8,173u8,0u8,209u8,82u8,228u8,47u8,15u8,248u8,155u8,4u8,172u8,251u8,55u8, - 5u8,88u8,22u8,57u8,236u8,128u8,44u8,253u8,77u8,87u8,51u8,86u8,33u8,148u8,77u8,88u8,64u8,177u8,155u8,153u8, - 43u8,14u8,35u8,143u8,137u8,152u8,254u8,232u8,231u8,187u8,68u8,177u8,86u8,29u8,44u8,167u8,30u8,141u8,190u8,199u8, - 160u8,56u8,157u8,81u8,249u8,9u8,225u8,110u8,24u8,88u8,64u8,223u8,100u8,77u8,52u8,248u8,33u8,70u8,255u8,240u8, - 144u8,103u8,54u8,23u8,123u8,1u8,189u8,225u8,183u8,128u8,94u8,76u8,161u8,228u8,183u8,131u8,158u8,201u8,121u8,71u8, - 237u8,176u8,215u8,240u8,88u8,29u8,52u8,30u8,252u8,63u8,219u8,153u8,67u8,172u8,171u8,136u8,4u8,198u8,247u8,73u8, - 72u8,123u8,90u8,120u8,205u8,182u8,9u8,125u8,180u8,31u8,144u8,182u8,148u8,150u8,12u8,184u8,162u8,98u8,21u8,167u8, - 47u8,49u8,46u8,49u8,63u8,55u8,188u8,209u8,52u8,105u8,69u8,37u8,23u8,24u8,142u8,186u8,237u8,240u8,3u8,244u8, - 107u8,135u8,71u8,71u8,195u8,238u8,193u8,243u8,62u8,102u8,158u8,87u8,183u8,254u8,7u8,90u8,19u8,56u8,192u8,46u8, - 63u8,0u8,0u8,0u8,0u8,13u8,115u8,116u8,97u8,116u8,101u8,95u8,115u8,116u8,111u8,114u8,97u8,103u8,101u8,231u8, - 6u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,229u8,86u8,193u8,110u8,219u8,56u8,16u8,189u8,231u8, - 43u8,38u8,88u8,96u8,87u8,6u8,132u8,36u8,135u8,197u8,30u8,228u8,245u8,98u8,123u8,8u8,122u8,90u8,236u8,98u8, - 157u8,182u8,135u8,162u8,32u8,104u8,105u8,36u8,19u8,145u8,72u8,151u8,28u8,38u8,113u8,11u8,255u8,123u8,135u8,148u8, - 34u8,211u8,138u8,227u8,54u8,232u8,173u8,37u8,130u8,200u8,18u8,103u8,30u8,103u8,248u8,134u8,111u8,216u8,153u8,202u8, - 183u8,8u8,114u8,67u8,198u8,137u8,218u8,202u8,14u8,239u8,141u8,189u8,45u8,10u8,71u8,146u8,80u8,56u8,50u8,86u8, - 54u8,8u8,159u8,207u8,206u8,128u8,135u8,119u8,199u8,44u8,183u8,142u8,176u8,19u8,178u8,170u8,44u8,58u8,135u8,110u8, - 62u8,154u8,58u8,170u8,138u8,2u8,173u8,53u8,118u8,222u8,251u8,215u8,86u8,161u8,174u8,158u8,66u8,172u8,90u8,83u8, - 222u8,206u8,79u8,154u8,52u8,168u8,209u8,41u8,119u8,218u8,104u8,8u8,87u8,52u8,210u8,13u8,43u8,150u8,70u8,59u8, - 130u8,235u8,229u8,205u8,171u8,155u8,107u8,177u8,188u8,249u8,247u8,255u8,87u8,175u8,175u8,197u8,155u8,37u8,255u8,47u8, - 192u8,255u8,241u8,59u8,44u8,224u8,106u8,176u8,116u8,100u8,125u8,73u8,240u8,198u8,133u8,124u8,215u8,210u8,177u8,235u8, - 102u8,155u8,67u8,101u8,205u8,38u8,135u8,0u8,28u8,118u8,1u8,134u8,161u8,56u8,99u8,23u8,17u8,242u8,241u8,219u8, - 106u8,75u8,152u8,126u8,219u8,245u8,184u8,151u8,151u8,151u8,112u8,179u8,86u8,14u8,248u8,207u8,111u8,42u8,222u8,85u8, - 14u8,156u8,128u8,214u8,8u8,43u8,108u8,148u8,86u8,186u8,1u8,83u8,3u8,202u8,114u8,13u8,102u8,99u8,202u8,117u8, - 14u8,22u8,235u8,22u8,75u8,10u8,51u8,193u8,108u8,200u8,106u8,68u8,243u8,49u8,68u8,89u8,19u8,218u8,56u8,223u8, - 74u8,78u8,145u8,30u8,116u8,128u8,9u8,239u8,27u8,139u8,119u8,202u8,120u8,7u8,24u8,224u8,194u8,186u8,165u8,233u8, - 58u8,69u8,188u8,242u8,69u8,154u8,234u8,50u8,80u8,188u8,236u8,193u8,247u8,105u8,223u8,226u8,246u8,105u8,190u8,17u8, - 105u8,146u8,111u8,12u8,163u8,232u8,55u8,236u8,32u8,227u8,141u8,95u8,181u8,170u8,204u8,122u8,146u8,102u8,80u8,123u8, - 13u8,156u8,38u8,41u8,217u8,170u8,79u8,152u8,77u8,73u8,131u8,95u8,157u8,106u8,52u8,218u8,89u8,178u8,220u8,180u8, - 164u8,138u8,66u8,242u8,195u8,146u8,152u8,56u8,79u8,193u8,102u8,243u8,17u8,162u8,119u8,56u8,207u8,198u8,15u8,97u8, - 156u8,227u8,131u8,114u8,228u8,254u8,124u8,146u8,249u8,95u8,217u8,223u8,83u8,168u8,252u8,192u8,51u8,86u8,49u8,135u8, - 209u8,90u8,148u8,213u8,86u8,244u8,56u8,217u8,177u8,186u8,154u8,141u8,126u8,73u8,52u8,157u8,185u8,67u8,65u8,102u8, - 26u8,111u8,126u8,132u8,131u8,207u8,135u8,235u8,246u8,27u8,127u8,117u8,24u8,77u8,186u8,245u8,19u8,135u8,164u8,58u8, - 39u8,78u8,73u8,141u8,78u8,102u8,118u8,227u8,219u8,110u8,136u8,249u8,121u8,38u8,141u8,22u8,26u8,239u8,69u8,60u8, - 177u8,217u8,190u8,44u8,102u8,32u8,203u8,143u8,94u8,49u8,93u8,39u8,83u8,58u8,202u8,202u8,247u8,146u8,162u8,13u8, - 137u8,218u8,120u8,93u8,125u8,59u8,31u8,45u8,210u8,112u8,140u8,22u8,176u8,50u8,12u8,115u8,47u8,154u8,214u8,172u8, - 100u8,43u8,58u8,79u8,223u8,20u8,201u8,30u8,75u8,213u8,208u8,111u8,4u8,156u8,47u8,122u8,208u8,139u8,248u8,58u8, - 155u8,48u8,147u8,76u8,241u8,170u8,241u8,57u8,63u8,98u8,240u8,24u8,86u8,131u8,36u8,14u8,68u8,88u8,196u8,25u8, - 97u8,116u8,187u8,21u8,146u8,68u8,4u8,16u8,81u8,66u8,130u8,134u8,100u8,73u8,68u8,187u8,175u8,112u8,88u8,122u8, - 107u8,81u8,147u8,136u8,117u8,34u8,164u8,174u8,68u8,172u8,139u8,108u8,86u8,64u8,22u8,142u8,248u8,143u8,68u8,232u8, - 11u8,201u8,204u8,18u8,18u8,46u8,226u8,254u8,228u8,41u8,47u8,23u8,113u8,163u8,102u8,83u8,125u8,127u8,39u8,109u8, - 224u8,160u8,136u8,242u8,203u8,91u8,230u8,91u8,226u8,7u8,121u8,171u8,89u8,236u8,89u8,127u8,87u8,210u8,241u8,15u8, - 163u8,123u8,213u8,151u8,177u8,53u8,114u8,84u8,112u8,167u8,240u8,30u8,214u8,216u8,86u8,124u8,48u8,195u8,220u8,8u8, - 248u8,246u8,31u8,168u8,77u8,175u8,238u8,76u8,20u8,179u8,0u8,241u8,192u8,1u8,127u8,43u8,215u8,94u8,223u8,70u8, - 169u8,183u8,82u8,59u8,201u8,109u8,130u8,91u8,92u8,206u8,103u8,254u8,55u8,7u8,161u8,54u8,160u8,66u8,110u8,12u8, - 29u8,43u8,174u8,35u8,85u8,142u8,136u8,92u8,165u8,165u8,108u8,91u8,14u8,163u8,182u8,166u8,139u8,200u8,181u8,178u8, - 161u8,113u8,236u8,97u8,30u8,27u8,72u8,191u8,214u8,10u8,75u8,25u8,186u8,120u8,12u8,226u8,1u8,75u8,31u8,77u8, - 90u8,185u8,69u8,59u8,162u8,54u8,94u8,178u8,59u8,33u8,87u8,137u8,100u8,96u8,116u8,235u8,52u8,51u8,246u8,212u8, - 125u8,199u8,209u8,146u8,212u8,29u8,198u8,226u8,123u8,113u8,93u8,15u8,58u8,55u8,180u8,233u8,95u8,222u8,51u8,1u8, - 20u8,173u8,63u8,36u8,245u8,29u8,161u8,29u8,134u8,186u8,177u8,34u8,88u8,36u8,218u8,148u8,167u8,237u8,58u8,105u8, - 211u8,63u8,177u8,102u8,157u8,212u8,162u8,67u8,29u8,58u8,214u8,100u8,250u8,131u8,113u8,246u8,164u8,179u8,236u8,21u8, - 104u8,126u8,120u8,68u8,96u8,241u8,204u8,224u8,106u8,229u8,235u8,74u8,25u8,111u8,69u8,139u8,19u8,227u8,228u8,181u8, - 143u8,1u8,140u8,174u8,85u8,227u8,173u8,12u8,53u8,122u8,120u8,161u8,123u8,45u8,221u8,127u8,50u8,216u8,134u8,235u8, - 210u8,179u8,23u8,156u8,151u8,92u8,102u8,184u8,5u8,62u8,174u8,152u8,165u8,34u8,47u8,153u8,21u8,130u8,171u8,193u8, - 123u8,119u8,246u8,5u8,141u8,6u8,227u8,158u8,91u8,11u8,0u8,0u8,0u8,0u8,11u8,115u8,116u8,111u8,114u8,97u8, - 103u8,101u8,95u8,103u8,97u8,115u8,207u8,45u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,60u8, - 107u8,119u8,26u8,199u8,146u8,223u8,253u8,43u8,250u8,58u8,178u8,14u8,248u8,34u8,12u8,18u8,232u8,129u8,44u8,159u8, - 149u8,109u8,69u8,87u8,39u8,137u8,148u8,99u8,43u8,185u8,187u8,235u8,248u8,142u8,6u8,104u8,208u8,216u8,192u8,144u8, - 121u8,72u8,176u8,150u8,254u8,251u8,86u8,85u8,63u8,166u8,123u8,166u8,103u8,0u8,219u8,123u8,79u8,150u8,156u8,200u8, - 162u8,167u8,186u8,186u8,186u8,186u8,222u8,213u8,163u8,23u8,47u8,94u8,176u8,115u8,63u8,102u8,115u8,63u8,242u8,167u8, - 60u8,225u8,81u8,204u8,70u8,97u8,196u8,198u8,147u8,176u8,239u8,79u8,88u8,156u8,132u8,145u8,63u8,230u8,205u8,39u8, - 47u8,94u8,188u8,192u8,255u8,217u8,15u8,236u8,156u8,207u8,120u8,4u8,143u8,194u8,59u8,30u8,221u8,5u8,252u8,158u8, - 197u8,124u8,144u8,4u8,225u8,44u8,214u8,48u8,31u8,222u8,242u8,81u8,48u8,11u8,104u8,240u8,99u8,237u8,135u8,97u8, - 246u8,173u8,174u8,97u8,158u8,179u8,15u8,191u8,37u8,193u8,36u8,248u8,31u8,31u8,31u8,176u8,97u8,48u8,229u8,179u8, - 88u8,78u8,72u8,179u8,7u8,59u8,217u8,131u8,186u8,107u8,94u8,132u8,255u8,228u8,231u8,136u8,65u8,13u8,143u8,187u8, - 27u8,164u8,209u8,29u8,103u8,147u8,48u8,252u8,156u8,206u8,1u8,122u8,236u8,199u8,59u8,52u8,180u8,35u8,134u8,52u8, - 236u8,69u8,194u8,167u8,59u8,247u8,65u8,204u8,89u8,56u8,231u8,132u8,135u8,40u8,10u8,212u8,240u8,78u8,54u8,172u8, - 231u8,188u8,94u8,38u8,220u8,49u8,167u8,175u8,134u8,243u8,115u8,4u8,143u8,126u8,76u8,103u8,3u8,177u8,117u8,62u8, - 231u8,179u8,33u8,159u8,13u8,2u8,142u8,211u8,70u8,114u8,124u8,199u8,28u8,183u8,248u8,118u8,129u8,204u8,244u8,213u8, - 110u8,145u8,58u8,107u8,64u8,211u8,245u8,142u8,15u8,194u8,217u8,40u8,24u8,167u8,145u8,2u8,140u8,236u8,17u8,13u8, - 249u8,158u8,39u8,73u8,48u8,27u8,51u8,235u8,41u8,18u8,19u8,139u8,7u8,59u8,246u8,131u8,186u8,33u8,13u8,198u8, - 81u8,103u8,163u8,63u8,48u8,247u8,217u8,106u8,136u8,115u8,75u8,186u8,24u8,28u8,7u8,27u8,77u8,210u8,65u8,146u8, - 250u8,9u8,143u8,25u8,247u8,7u8,183u8,140u8,207u8,67u8,248u8,217u8,247u8,99u8,62u8,100u8,128u8,36u8,9u8,19u8, - 152u8,96u8,156u8,114u8,131u8,16u8,221u8,223u8,6u8,0u8,21u8,196u8,140u8,132u8,12u8,64u8,253u8,65u8,20u8,198u8, - 49u8,75u8,238u8,67u8,99u8,221u8,158u8,94u8,184u8,221u8,100u8,215u8,183u8,156u8,205u8,210u8,105u8,159u8,71u8,44u8, - 28u8,177u8,167u8,120u8,180u8,241u8,83u8,22u8,204u8,92u8,34u8,207u8,118u8,243u8,240u8,120u8,170u8,113u8,9u8,52u8, - 205u8,120u8,122u8,161u8,16u8,14u8,38u8,233u8,144u8,91u8,75u8,191u8,227u8,113u8,152u8,70u8,3u8,64u8,112u8,235u8, - 223u8,33u8,195u8,19u8,192u8,125u8,243u8,153u8,47u8,111u8,152u8,159u8,36u8,81u8,208u8,79u8,19u8,222u8,144u8,123u8, - 2u8,8u8,206u8,250u8,156u8,207u8,216u8,20u8,52u8,109u8,8u8,248u8,146u8,144u8,240u8,192u8,199u8,94u8,155u8,221u8, - 5u8,62u8,243u8,217u8,13u8,194u8,121u8,73u8,88u8,171u8,223u8,100u8,130u8,168u8,119u8,193u8,174u8,253u8,254u8,132u8, - 51u8,62u8,131u8,101u8,120u8,220u8,44u8,59u8,43u8,161u8,58u8,250u8,233u8,141u8,22u8,45u8,142u8,104u8,65u8,28u8, - 98u8,230u8,207u8,152u8,31u8,245u8,131u8,36u8,242u8,163u8,37u8,123u8,154u8,248u8,209u8,152u8,39u8,79u8,205u8,147u8, - 33u8,251u8,209u8,15u8,147u8,91u8,66u8,161u8,85u8,7u8,38u8,14u8,153u8,86u8,10u8,69u8,124u8,3u8,121u8,48u8, - 51u8,142u8,188u8,65u8,210u8,96u8,88u8,35u8,63u8,226u8,132u8,40u8,19u8,94u8,224u8,70u8,38u8,23u8,192u8,193u8, - 167u8,105u8,126u8,7u8,79u8,137u8,6u8,66u8,10u8,103u8,134u8,48u8,32u8,16u8,132u8,37u8,117u8,10u8,166u8,56u8, - 229u8,2u8,26u8,194u8,226u8,179u8,113u8,112u8,199u8,13u8,104u8,33u8,121u8,60u8,0u8,180u8,145u8,177u8,61u8,220u8, - 180u8,218u8,93u8,3u8,69u8,50u8,241u8,63u8,195u8,52u8,216u8,12u8,174u8,255u8,103u8,26u8,38u8,1u8,112u8,31u8, - 233u8,241u8,81u8,208u8,39u8,5u8,114u8,144u8,63u8,130u8,157u8,230u8,112u8,147u8,253u8,136u8,91u8,89u8,248u8,211u8, - 249u8,4u8,240u8,10u8,90u8,124u8,214u8,109u8,181u8,216u8,249u8,107u8,194u8,33u8,231u8,224u8,244u8,221u8,46u8,142u8, - 202u8,5u8,44u8,109u8,33u8,34u8,50u8,238u8,27u8,143u8,4u8,115u8,105u8,187u8,64u8,116u8,183u8,245u8,44u8,19u8, - 142u8,247u8,28u8,196u8,19u8,89u8,237u8,29u8,182u8,143u8,118u8,61u8,190u8,152u8,135u8,51u8,216u8,3u8,8u8,132u8, - 71u8,214u8,19u8,101u8,2u8,89u8,52u8,245u8,1u8,59u8,252u8,8u8,6u8,176u8,170u8,97u8,238u8,45u8,49u8,203u8, - 155u8,97u8,253u8,204u8,205u8,250u8,96u8,166u8,57u8,47u8,76u8,1u8,16u8,151u8,226u8,161u8,3u8,26u8,95u8,162u8, - 96u8,119u8,254u8,36u8,229u8,0u8,74u8,120u8,124u8,118u8,150u8,78u8,120u8,20u8,160u8,128u8,206u8,231u8,81u8,184u8, - 8u8,166u8,2u8,97u8,18u8,162u8,208u8,26u8,212u8,11u8,58u8,26u8,236u8,243u8,44u8,188u8,167u8,19u8,242u8,133u8, - 180u8,3u8,137u8,111u8,240u8,201u8,77u8,163u8,104u8,88u8,128u8,158u8,21u8,172u8,16u8,114u8,161u8,37u8,211u8,103u8, - 83u8,224u8,195u8,52u8,157u8,146u8,64u8,15u8,110u8,241u8,148u8,232u8,144u8,224u8,129u8,191u8,200u8,61u8,104u8,90u8, - 220u8,40u8,153u8,88u8,156u8,6u8,102u8,131u8,14u8,22u8,28u8,197u8,60u8,4u8,11u8,17u8,131u8,120u8,9u8,153u8, - 128u8,65u8,193u8,108u8,80u8,31u8,212u8,91u8,162u8,223u8,210u8,230u8,6u8,225u8,28u8,132u8,81u8,196u8,99u8,216u8, - 204u8,16u8,217u8,132u8,211u8,70u8,225u8,100u8,18u8,222u8,131u8,117u8,34u8,68u8,153u8,239u8,178u8,56u8,241u8,94u8, - 104u8,47u8,48u8,236u8,198u8,178u8,112u8,191u8,242u8,104u8,7u8,85u8,2u8,180u8,213u8,31u8,42u8,219u8,163u8,7u8, - 7u8,48u8,154u8,8u8,109u8,222u8,51u8,134u8,239u8,163u8,64u8,142u8,118u8,196u8,40u8,202u8,105u8,134u8,161u8,107u8, - 12u8,26u8,24u8,246u8,141u8,97u8,141u8,129u8,158u8,88u8,10u8,19u8,140u8,202u8,69u8,223u8,18u8,123u8,97u8,137u8, - 8u8,193u8,220u8,164u8,33u8,102u8,247u8,193u8,100u8,162u8,24u8,158u8,24u8,199u8,163u8,193u8,232u8,64u8,194u8,56u8, - 105u8,176u8,249u8,36u8,141u8,37u8,47u8,90u8,71u8,207u8,148u8,241u8,25u8,6u8,163u8,17u8,143u8,192u8,149u8,163u8, - 69u8,79u8,238u8,209u8,168u8,19u8,30u8,121u8,158u8,164u8,248u8,6u8,94u8,68u8,213u8,220u8,72u8,1u8,125u8,22u8, - 167u8,243u8,121u8,24u8,9u8,47u8,238u8,79u8,6u8,233u8,36u8,51u8,255u8,74u8,1u8,93u8,177u8,77u8,233u8,217u8, - 209u8,158u8,128u8,49u8,126u8,28u8,243u8,24u8,37u8,250u8,30u8,152u8,195u8,33u8,228u8,67u8,85u8,34u8,40u8,120u8, - 72u8,128u8,163u8,40u8,156u8,86u8,56u8,166u8,155u8,62u8,72u8,88u8,120u8,239u8,137u8,71u8,47u8,175u8,95u8,145u8, - 127u8,138u8,164u8,211u8,74u8,180u8,87u8,90u8,102u8,7u8,14u8,159u8,162u8,3u8,203u8,9u8,209u8,90u8,244u8,9u8, - 208u8,161u8,50u8,16u8,37u8,4u8,74u8,191u8,89u8,69u8,26u8,33u8,82u8,182u8,178u8,72u8,94u8,65u8,152u8,215u8, - 162u8,14u8,3u8,104u8,132u8,78u8,64u8,28u8,170u8,41u8,180u8,88u8,232u8,77u8,211u8,4u8,104u8,117u8,83u8,170u8, - 176u8,0u8,140u8,144u8,112u8,155u8,82u8,37u8,10u8,174u8,144u8,85u8,63u8,119u8,61u8,36u8,51u8,162u8,119u8,67u8, - 182u8,121u8,234u8,207u8,32u8,17u8,96u8,113u8,48u8,13u8,38u8,126u8,132u8,214u8,99u8,46u8,121u8,144u8,51u8,28u8, - 13u8,6u8,113u8,13u8,248u8,164u8,65u8,152u8,130u8,255u8,67u8,113u8,77u8,28u8,33u8,149u8,15u8,42u8,50u8,192u8, - 195u8,234u8,47u8,241u8,57u8,225u8,16u8,230u8,63u8,219u8,2u8,187u8,12u8,113u8,175u8,75u8,84u8,85u8,96u8,160u8, - 68u8,1u8,49u8,114u8,136u8,24u8,200u8,205u8,83u8,152u8,168u8,113u8,11u8,139u8,172u8,66u8,54u8,224u8,253u8,115u8, - 212u8,30u8,216u8,10u8,146u8,249u8,188u8,89u8,106u8,49u8,124u8,99u8,85u8,193u8,75u8,164u8,144u8,221u8,164u8,135u8, - 160u8,111u8,1u8,159u8,72u8,22u8,68u8,50u8,158u8,131u8,165u8,253u8,132u8,16u8,221u8,194u8,185u8,119u8,89u8,72u8, - 161u8,193u8,77u8,218u8,222u8,85u8,240u8,177u8,112u8,192u8,150u8,197u8,16u8,146u8,130u8,202u8,46u8,44u8,140u8,228u8, - 16u8,225u8,65u8,46u8,109u8,213u8,186u8,16u8,156u8,3u8,142u8,58u8,123u8,193u8,14u8,217u8,223u8,89u8,155u8,157u8, - 176u8,195u8,246u8,150u8,216u8,80u8,147u8,253u8,14u8,252u8,10u8,69u8,128u8,164u8,78u8,97u8,2u8,188u8,19u8,50u8, - 47u8,60u8,98u8,44u8,87u8,55u8,243u8,55u8,103u8,226u8,97u8,185u8,159u8,62u8,7u8,7u8,144u8,61u8,94u8,146u8, - 237u8,75u8,208u8,251u8,198u8,160u8,45u8,60u8,154u8,250u8,193u8,176u8,249u8,41u8,134u8,120u8,112u8,57u8,75u8,252u8, - 133u8,114u8,150u8,3u8,224u8,90u8,95u8,28u8,156u8,159u8,38u8,161u8,140u8,7u8,38u8,168u8,216u8,128u8,39u8,146u8, - 49u8,44u8,176u8,109u8,24u8,248u8,99u8,8u8,239u8,88u8,77u8,44u8,129u8,54u8,75u8,70u8,114u8,125u8,16u8,243u8, - 152u8,71u8,117u8,25u8,219u8,195u8,225u8,99u8,134u8,169u8,34u8,229u8,97u8,56u8,72u8,33u8,6u8,147u8,146u8,61u8, - 10u8,64u8,232u8,199u8,148u8,140u8,162u8,216u8,160u8,25u8,98u8,242u8,56u8,6u8,225u8,144u8,55u8,217u8,197u8,72u8, - 134u8,5u8,18u8,171u8,164u8,35u8,150u8,38u8,153u8,104u8,64u8,227u8,158u8,0u8,233u8,225u8,36u8,140u8,196u8,50u8, - 224u8,81u8,167u8,16u8,184u8,129u8,192u8,36u8,100u8,183u8,3u8,176u8,165u8,226u8,76u8,128u8,118u8,52u8,81u8,112u8, - 146u8,96u8,17u8,124u8,109u8,212u8,19u8,133u8,223u8,54u8,182u8,86u8,78u8,150u8,5u8,212u8,55u8,138u8,129u8,122u8, - 104u8,4u8,204u8,22u8,12u8,254u8,249u8,157u8,30u8,204u8,60u8,53u8,219u8,217u8,121u8,197u8,42u8,92u8,128u8,14u8, - 58u8,74u8,158u8,211u8,252u8,25u8,7u8,235u8,225u8,199u8,27u8,206u8,160u8,144u8,130u8,160u8,173u8,249u8,244u8,28u8, - 34u8,175u8,96u8,8u8,140u8,23u8,64u8,177u8,185u8,65u8,147u8,13u8,185u8,28u8,116u8,99u8,62u8,40u8,95u8,198u8, - 113u8,121u8,90u8,248u8,98u8,6u8,89u8,193u8,60u8,196u8,49u8,246u8,236u8,25u8,0u8,204u8,131u8,132u8,24u8,53u8, - 204u8,77u8,192u8,179u8,210u8,179u8,44u8,60u8,57u8,64u8,225u8,37u8,214u8,2u8,37u8,149u8,173u8,128u8,12u8,103u8, - 158u8,74u8,83u8,114u8,16u8,138u8,154u8,21u8,96u8,25u8,45u8,43u8,0u8,53u8,37u8,185u8,220u8,136u8,216u8,220u8, - 235u8,25u8,201u8,18u8,77u8,52u8,16u8,57u8,143u8,234u8,31u8,32u8,201u8,194u8,68u8,169u8,250u8,3u8,70u8,139u8, - 154u8,211u8,232u8,24u8,193u8,224u8,198u8,115u8,62u8,153u8,128u8,174u8,221u8,24u8,135u8,112u8,3u8,58u8,63u8,240u8, - 193u8,54u8,168u8,132u8,209u8,120u8,130u8,30u8,16u8,141u8,36u8,20u8,138u8,208u8,7u8,134u8,145u8,8u8,30u8,13u8, - 19u8,98u8,233u8,140u8,187u8,12u8,177u8,177u8,204u8,160u8,160u8,198u8,131u8,91u8,62u8,132u8,108u8,160u8,215u8,131u8, - 216u8,215u8,147u8,206u8,84u8,136u8,112u8,198u8,77u8,124u8,84u8,193u8,147u8,31u8,216u8,155u8,16u8,61u8,66u8,66u8, - 150u8,103u8,76u8,94u8,122u8,200u8,23u8,14u8,83u8,73u8,227u8,180u8,91u8,203u8,238u8,149u8,27u8,167u8,222u8,147u8, - 105u8,136u8,228u8,65u8,162u8,146u8,132u8,177u8,55u8,194u8,100u8,23u8,216u8,243u8,25u8,200u8,205u8,72u8,101u8,95u8, - 158u8,60u8,65u8,95u8,158u8,198u8,46u8,184u8,101u8,12u8,46u8,204u8,243u8,135u8,195u8,8u8,125u8,114u8,124u8,172u8, - 65u8,227u8,100u8,216u8,235u8,113u8,8u8,24u8,162u8,227u8,138u8,233u8,232u8,210u8,20u8,95u8,114u8,115u8,239u8,200u8, - 175u8,28u8,139u8,197u8,71u8,80u8,46u8,192u8,156u8,37u8,143u8,192u8,100u8,242u8,113u8,53u8,36u8,112u8,33u8,14u8, - 226u8,106u8,160u8,156u8,0u8,203u8,197u8,97u8,12u8,156u8,227u8,217u8,251u8,235u8,171u8,119u8,167u8,231u8,103u8,222u8, - 249u8,233u8,123u8,239u8,205u8,213u8,229u8,143u8,23u8,231u8,61u8,150u8,238u8,119u8,192u8,27u8,182u8,142u8,75u8,224u8, - 20u8,64u8,219u8,2u8,184u8,184u8,252u8,253u8,244u8,231u8,139u8,183u8,132u8,232u8,221u8,233u8,229u8,249u8,153u8,2u8, - 219u8,181u8,192u8,254u8,251u8,236u8,221u8,149u8,119u8,125u8,250u8,238u8,252u8,236u8,218u8,251u8,237u8,253u8,105u8,6u8, - 182u8,103u8,129u8,153u8,16u8,222u8,245u8,213u8,149u8,247u8,58u8,35u8,172u8,227u8,92u8,247u8,151u8,171u8,203u8,171u8, - 235u8,171u8,203u8,139u8,55u8,167u8,63u8,255u8,252u8,95u8,222u8,229u8,213u8,165u8,247u8,246u8,236u8,205u8,187u8,179u8, - 211u8,247u8,23u8,151u8,231u8,222u8,155u8,223u8,222u8,253u8,174u8,87u8,234u8,58u8,231u8,255u8,122u8,117u8,113u8,121u8, - 109u8,83u8,190u8,111u8,177u8,234u8,53u8,160u8,122u8,47u8,193u8,222u8,158u8,93u8,94u8,253u8,114u8,113u8,121u8,122u8, - 125u8,113u8,117u8,169u8,217u8,209u8,130u8,143u8,53u8,227u8,151u8,211u8,255u8,244u8,126u8,219u8,239u8,104u8,128u8,195u8, - 78u8,103u8,255u8,160u8,211u8,105u8,29u8,236u8,29u8,180u8,142u8,186u8,221u8,246u8,126u8,187u8,43u8,225u8,41u8,31u8, - 145u8,81u8,105u8,86u8,158u8,105u8,216u8,117u8,153u8,172u8,142u8,211u8,84u8,147u8,244u8,228u8,95u8,173u8,154u8,14u8, - 75u8,231u8,67u8,210u8,141u8,97u8,74u8,158u8,55u8,39u8,1u8,24u8,218u8,234u8,153u8,55u8,134u8,249u8,162u8,204u8, - 85u8,39u8,217u8,42u8,76u8,182u8,234u8,40u8,34u8,39u8,238u8,243u8,113u8,48u8,155u8,97u8,30u8,171u8,208u8,200u8, - 108u8,76u8,150u8,19u8,102u8,50u8,108u8,193u8,161u8,252u8,226u8,80u8,220u8,154u8,197u8,190u8,176u8,131u8,65u8,172u8, - 17u8,240u8,5u8,31u8,64u8,137u8,110u8,40u8,202u8,69u8,168u8,162u8,74u8,5u8,32u8,90u8,138u8,130u8,59u8,165u8, - 233u8,128u8,50u8,54u8,153u8,68u8,241u8,157u8,198u8,66u8,5u8,175u8,62u8,23u8,213u8,12u8,8u8,42u8,180u8,81u8, - 23u8,117u8,80u8,25u8,31u8,139u8,64u8,53u8,89u8,74u8,170u8,245u8,108u8,157u8,158u8,171u8,114u8,89u8,156u8,210u8, - 38u8,228u8,174u8,129u8,167u8,62u8,37u8,23u8,51u8,181u8,209u8,112u8,2u8,180u8,33u8,192u8,76u8,227u8,8u8,134u8, - 220u8,159u8,24u8,209u8,244u8,109u8,120u8,143u8,201u8,73u8,3u8,227u8,83u8,17u8,90u8,83u8,1u8,197u8,71u8,244u8, - 136u8,244u8,46u8,136u8,168u8,160u8,52u8,133u8,17u8,40u8,1u8,104u8,60u8,195u8,16u8,226u8,36u8,8u8,190u8,33u8, - 62u8,16u8,91u8,137u8,192u8,28u8,202u8,220u8,209u8,172u8,224u8,141u8,18u8,194u8,107u8,108u8,34u8,136u8,64u8,246u8, - 76u8,46u8,135u8,20u8,116u8,27u8,146u8,19u8,39u8,17u8,20u8,132u8,89u8,86u8,104u8,160u8,216u8,26u8,106u8,165u8, - 96u8,22u8,153u8,252u8,32u8,170u8,55u8,24u8,63u8,203u8,208u8,76u8,103u8,87u8,116u8,10u8,249u8,10u8,173u8,154u8, - 5u8,155u8,244u8,16u8,138u8,124u8,50u8,9u8,126u8,195u8,137u8,81u8,38u8,154u8,58u8,99u8,155u8,173u8,198u8,40u8, - 166u8,84u8,224u8,84u8,57u8,223u8,102u8,104u8,105u8,70u8,5u8,86u8,177u8,119u8,202u8,11u8,86u8,111u8,29u8,161u8, - 214u8,221u8,186u8,64u8,185u8,130u8,68u8,66u8,184u8,201u8,206u8,215u8,199u8,154u8,223u8,248u8,99u8,102u8,138u8,78u8, - 25u8,69u8,158u8,34u8,17u8,211u8,133u8,63u8,89u8,243u8,50u8,203u8,127u8,13u8,17u8,226u8,147u8,101u8,26u8,132u8, - 16u8,131u8,4u8,51u8,44u8,33u8,41u8,60u8,34u8,199u8,4u8,44u8,96u8,83u8,64u8,240u8,69u8,56u8,219u8,43u8, - 24u8,175u8,7u8,246u8,35u8,229u8,125u8,162u8,226u8,248u8,128u8,249u8,254u8,0u8,83u8,17u8,176u8,61u8,15u8,25u8, - 208u8,142u8,249u8,177u8,190u8,25u8,64u8,236u8,166u8,125u8,163u8,118u8,10u8,223u8,90u8,173u8,102u8,171u8,205u8,158u8, - 209u8,23u8,11u8,168u8,117u8,99u8,1u8,181u8,91u8,78u8,32u8,13u8,5u8,64u8,80u8,116u8,42u8,1u8,82u8,80u8, - 15u8,224u8,2u8,114u8,64u8,82u8,209u8,126u8,37u8,110u8,222u8,82u8,37u8,107u8,14u8,54u8,97u8,24u8,133u8,243u8, - 6u8,29u8,13u8,207u8,105u8,220u8,98u8,39u8,99u8,163u8,197u8,181u8,134u8,81u8,77u8,164u8,100u8,42u8,180u8,234u8, - 204u8,38u8,14u8,93u8,227u8,93u8,81u8,221u8,202u8,100u8,98u8,225u8,144u8,173u8,229u8,119u8,160u8,100u8,10u8,233u8, - 93u8,48u8,159u8,4u8,88u8,203u8,223u8,128u8,156u8,37u8,145u8,83u8,20u8,74u8,81u8,10u8,52u8,125u8,9u8,117u8, - 34u8,68u8,179u8,64u8,21u8,72u8,176u8,116u8,163u8,75u8,1u8,88u8,0u8,136u8,139u8,222u8,242u8,77u8,8u8,210u8, - 21u8,96u8,229u8,69u8,55u8,5u8,98u8,148u8,54u8,127u8,138u8,69u8,130u8,6u8,230u8,246u8,247u8,28u8,171u8,6u8, - 177u8,33u8,255u8,122u8,182u8,93u8,0u8,151u8,198u8,53u8,95u8,0u8,39u8,210u8,168u8,196u8,217u8,208u8,21u8,174u8, - 152u8,202u8,194u8,153u8,203u8,84u8,90u8,11u8,245u8,6u8,42u8,70u8,102u8,246u8,248u8,13u8,237u8,242u8,198u8,178u8, - 214u8,191u8,197u8,230u8,179u8,149u8,210u8,36u8,54u8,230u8,209u8,198u8,114u8,199u8,75u8,249u8,18u8,81u8,217u8,99u8, - 170u8,44u8,159u8,61u8,149u8,105u8,82u8,233u8,115u8,145u8,29u8,57u8,31u8,27u8,167u8,85u8,210u8,46u8,40u8,225u8, - 86u8,241u8,136u8,78u8,227u8,24u8,234u8,17u8,177u8,93u8,53u8,207u8,106u8,241u8,69u8,35u8,242u8,28u8,106u8,58u8, - 11u8,175u8,213u8,96u8,75u8,175u8,85u8,135u8,104u8,171u8,6u8,191u8,182u8,234u8,91u8,185u8,231u8,35u8,124u8,62u8, - 162u8,231u8,20u8,175u8,53u8,68u8,216u8,150u8,193u8,105u8,120u8,74u8,195u8,166u8,28u8,106u8,25u8,32u8,71u8,178u8, - 1u8,48u8,77u8,193u8,222u8,198u8,176u8,147u8,120u8,180u8,44u8,82u8,0u8,101u8,222u8,173u8,133u8,23u8,176u8,87u8, - 108u8,225u8,125u8,9u8,216u8,14u8,107u8,63u8,110u8,177u8,26u8,140u8,109u8,81u8,126u8,7u8,205u8,193u8,65u8,2u8, - 153u8,11u8,52u8,47u8,129u8,195u8,49u8,108u8,167u8,174u8,183u8,141u8,69u8,216u8,173u8,22u8,251u8,99u8,194u8,255u8, - 100u8,136u8,130u8,126u8,33u8,202u8,12u8,12u8,170u8,188u8,221u8,162u8,162u8,182u8,160u8,59u8,195u8,0u8,117u8,210u8, - 173u8,37u8,78u8,29u8,195u8,212u8,165u8,69u8,192u8,146u8,166u8,207u8,168u8,239u8,237u8,88u8,187u8,147u8,173u8,189u8, - 44u8,174u8,189u8,92u8,189u8,182u8,198u8,244u8,179u8,232u8,28u8,233u8,58u8,60u8,52u8,138u8,33u8,130u8,130u8,158u8, - 108u8,12u8,142u8,64u8,49u8,17u8,107u8,199u8,42u8,42u8,27u8,82u8,149u8,117u8,2u8,113u8,143u8,31u8,101u8,17u8, - 148u8,78u8,115u8,201u8,195u8,240u8,230u8,184u8,73u8,42u8,41u8,186u8,13u8,32u8,228u8,247u8,248u8,195u8,71u8,142u8, - 250u8,193u8,248u8,54u8,161u8,249u8,106u8,81u8,51u8,18u8,156u8,22u8,41u8,92u8,163u8,232u8,111u8,41u8,157u8,146u8, - 239u8,149u8,234u8,6u8,253u8,5u8,204u8,43u8,115u8,154u8,6u8,93u8,8u8,199u8,168u8,20u8,97u8,38u8,146u8,193u8, - 151u8,228u8,29u8,94u8,21u8,52u8,8u8,186u8,255u8,62u8,216u8,79u8,135u8,105u8,145u8,13u8,110u8,220u8,6u8,195u8, - 109u8,20u8,183u8,105u8,85u8,34u8,85u8,203u8,176u8,0u8,69u8,87u8,83u8,64u8,42u8,185u8,48u8,116u8,186u8,58u8, - 1u8,74u8,234u8,108u8,228u8,232u8,123u8,0u8,14u8,245u8,219u8,218u8,26u8,215u8,82u8,47u8,66u8,205u8,26u8,123u8, - 95u8,128u8,27u8,143u8,80u8,84u8,253u8,3u8,178u8,208u8,193u8,151u8,90u8,255u8,95u8,95u8,224u8,209u8,35u8,10u8, - 100u8,253u8,241u8,75u8,159u8,4u8,147u8,253u8,241u8,150u8,79u8,18u8,223u8,27u8,111u8,109u8,173u8,135u8,43u8,245u8, - 166u8,85u8,115u8,30u8,216u8,239u8,62u8,216u8,27u8,44u8,214u8,87u8,124u8,30u8,128u8,171u8,241u8,32u8,10u8,230u8, - 180u8,49u8,243u8,65u8,73u8,156u8,81u8,246u8,41u8,133u8,50u8,131u8,131u8,45u8,73u8,253u8,86u8,37u8,69u8,55u8, - 82u8,116u8,110u8,114u8,15u8,242u8,136u8,252u8,197u8,74u8,68u8,66u8,218u8,170u8,16u8,73u8,14u8,126u8,25u8,63u8, - 74u8,230u8,2u8,86u8,56u8,15u8,147u8,208u8,7u8,146u8,139u8,33u8,194u8,85u8,32u8,74u8,171u8,104u8,81u8,20u8, - 153u8,183u8,31u8,74u8,17u8,121u8,201u8,214u8,74u8,68u8,215u8,133u8,214u8,189u8,11u8,81u8,4u8,123u8,74u8,161u8, - 158u8,95u8,138u8,242u8,161u8,120u8,31u8,195u8,73u8,209u8,20u8,16u8,85u8,200u8,238u8,86u8,14u8,145u8,17u8,230u8, - 88u8,136u8,250u8,212u8,83u8,56u8,218u8,221u8,170u8,218u8,218u8,153u8,84u8,111u8,161u8,206u8,69u8,30u8,25u8,58u8, - 125u8,38u8,186u8,40u8,133u8,39u8,255u8,160u8,238u8,167u8,232u8,85u8,22u8,21u8,23u8,180u8,25u8,58u8,176u8,104u8, - 202u8,5u8,135u8,90u8,205u8,238u8,22u8,171u8,187u8,53u8,24u8,158u8,185u8,52u8,24u8,55u8,241u8,175u8,47u8,240u8, - 144u8,248u8,240u8,72u8,223u8,215u8,210u8,98u8,194u8,247u8,135u8,8u8,1u8,12u8,172u8,24u8,149u8,183u8,142u8,170u8, - 38u8,255u8,147u8,202u8,7u8,83u8,14u8,73u8,108u8,150u8,127u8,75u8,91u8,213u8,135u8,192u8,201u8,208u8,26u8,149u8, - 81u8,83u8,140u8,193u8,39u8,75u8,195u8,31u8,87u8,183u8,137u8,51u8,125u8,65u8,159u8,166u8,17u8,186u8,172u8,169u8, - 251u8,180u8,99u8,135u8,21u8,66u8,38u8,111u8,73u8,41u8,2u8,255u8,105u8,80u8,86u8,119u8,24u8,153u8,135u8,149u8, - 54u8,164u8,221u8,122u8,38u8,242u8,143u8,102u8,107u8,247u8,25u8,43u8,87u8,200u8,221u8,12u8,110u8,191u8,10u8,110u8, - 79u8,195u8,181u8,15u8,170u8,224u8,58u8,26u8,174u8,211u8,169u8,130u8,235u8,74u8,56u8,193u8,236u8,114u8,184u8,125u8, - 9u8,183u8,219u8,60u8,104u8,87u8,193u8,29u8,72u8,184u8,253u8,230u8,126u8,37u8,190u8,67u8,181u8,238u8,126u8,179u8, - 115u8,248u8,172u8,28u8,238u8,72u8,194u8,117u8,90u8,205u8,253u8,118u8,21u8,92u8,87u8,174u8,187u8,215u8,60u8,216u8, - 173u8,130u8,59u8,18u8,112u8,71u8,237u8,230u8,158u8,123u8,221u8,121u8,218u8,159u8,4u8,3u8,244u8,166u8,85u8,141u8, - 160u8,154u8,21u8,48u8,88u8,129u8,66u8,61u8,11u8,171u8,141u8,0u8,195u8,106u8,19u8,169u8,217u8,122u8,98u8,22u8, - 92u8,224u8,71u8,4u8,22u8,31u8,158u8,228u8,173u8,141u8,110u8,60u8,81u8,240u8,219u8,96u8,187u8,245u8,70u8,5u8, - 204u8,46u8,193u8,236u8,87u8,194u8,236u8,137u8,24u8,250u8,160u8,18u8,168u8,67u8,64u8,157u8,78u8,37u8,80u8,87u8, - 70u8,227u8,71u8,149u8,80u8,251u8,130u8,238u8,131u8,118u8,37u8,212u8,129u8,160u8,124u8,191u8,26u8,215u8,161u8,88u8, - 113u8,191u8,115u8,88u8,9u8,118u8,36u8,168u8,111u8,237u8,87u8,175u8,121u8,212u8,165u8,53u8,247u8,14u8,170u8,153u8, - 122u8,116u8,132u8,96u8,71u8,237u8,189u8,252u8,162u8,31u8,245u8,183u8,122u8,62u8,18u8,60u8,207u8,231u8,189u8,162u8, - 146u8,169u8,51u8,94u8,52u8,95u8,58u8,229u8,37u8,35u8,25u8,151u8,212u8,249u8,74u8,114u8,199u8,98u8,229u8,79u8, - 95u8,185u8,40u8,228u8,220u8,89u8,154u8,46u8,170u8,114u8,244u8,172u8,151u8,203u8,78u8,27u8,5u8,100u8,198u8,93u8, - 30u8,39u8,50u8,81u8,232u8,170u8,64u8,38u8,249u8,97u8,104u8,87u8,198u8,84u8,89u8,191u8,144u8,133u8,3u8,208u8, - 31u8,81u8,112u8,201u8,182u8,132u8,183u8,43u8,162u8,228u8,111u8,53u8,139u8,227u8,11u8,246u8,242u8,164u8,180u8,170u8, - 207u8,182u8,183u8,217u8,178u8,10u8,192u8,62u8,61u8,234u8,9u8,245u8,122u8,193u8,140u8,122u8,183u8,30u8,132u8,43u8, - 212u8,77u8,175u8,185u8,218u8,11u8,245u8,236u8,164u8,143u8,245u8,175u8,146u8,98u8,6u8,29u8,255u8,37u8,236u8,181u8, - 124u8,199u8,5u8,27u8,224u8,176u8,32u8,13u8,119u8,138u8,225u8,54u8,44u8,138u8,55u8,18u8,1u8,123u8,117u8,194u8, - 180u8,117u8,89u8,185u8,45u8,221u8,237u8,169u8,27u8,123u8,201u8,99u8,4u8,46u8,202u8,86u8,8u8,4u8,103u8,165u8, - 252u8,252u8,202u8,197u8,114u8,205u8,242u8,218u8,182u8,248u8,215u8,128u8,112u8,236u8,25u8,63u8,106u8,147u8,246u8,160u8, - 203u8,156u8,202u8,54u8,188u8,250u8,90u8,117u8,58u8,84u8,110u8,49u8,122u8,161u8,181u8,98u8,25u8,198u8,93u8,126u8, - 41u8,43u8,187u8,184u8,203u8,45u8,245u8,188u8,142u8,56u8,206u8,211u8,92u8,25u8,106u8,18u8,173u8,10u8,6u8,23u8, - 154u8,114u8,174u8,211u8,180u8,240u8,125u8,235u8,145u8,186u8,250u8,123u8,230u8,162u8,165u8,187u8,195u8,143u8,73u8,137u8, - 125u8,82u8,25u8,107u8,237u8,113u8,147u8,185u8,246u8,19u8,131u8,189u8,141u8,181u8,78u8,184u8,216u8,239u8,174u8,85u8, - 25u8,194u8,42u8,195u8,6u8,167u8,88u8,48u8,205u8,217u8,78u8,43u8,30u8,229u8,172u8,175u8,189u8,35u8,99u8,193u8, - 170u8,13u8,213u8,68u8,187u8,184u8,78u8,27u8,203u8,186u8,245u8,181u8,124u8,251u8,152u8,109u8,199u8,193u8,120u8,134u8, - 13u8,44u8,181u8,137u8,60u8,97u8,117u8,230u8,15u8,254u8,76u8,161u8,161u8,22u8,87u8,209u8,156u8,111u8,170u8,247u8, - 122u8,66u8,176u8,188u8,220u8,130u8,121u8,2u8,12u8,161u8,120u8,94u8,188u8,171u8,151u8,95u8,240u8,85u8,237u8,63u8, - 242u8,243u8,33u8,159u8,17u8,148u8,31u8,231u8,253u8,234u8,69u8,118u8,17u8,200u8,237u8,75u8,199u8,170u8,64u8,226u8, - 168u8,33u8,59u8,82u8,82u8,172u8,242u8,113u8,106u8,200u8,236u8,178u8,62u8,116u8,37u8,105u8,12u8,223u8,88u8,16u8, - 69u8,51u8,118u8,253u8,186u8,136u8,38u8,187u8,23u8,109u8,92u8,52u8,198u8,22u8,110u8,118u8,75u8,137u8,174u8,101u8, - 139u8,26u8,168u8,179u8,121u8,242u8,22u8,219u8,146u8,113u8,178u8,156u8,96u8,239u8,228u8,74u8,95u8,173u8,123u8,96u8, - 191u8,24u8,87u8,157u8,225u8,155u8,113u8,195u8,185u8,164u8,220u8,241u8,80u8,90u8,225u8,120u8,40u8,77u8,80u8,126u8, - 149u8,23u8,245u8,41u8,42u8,126u8,135u8,205u8,50u8,153u8,216u8,66u8,104u8,248u8,83u8,150u8,230u8,210u8,183u8,231u8, - 88u8,57u8,180u8,227u8,105u8,107u8,250u8,27u8,209u8,26u8,83u8,19u8,62u8,91u8,211u8,63u8,171u8,233u8,21u8,8u8, - 254u8,73u8,77u8,176u8,77u8,215u8,167u8,179u8,118u8,144u8,207u8,172u8,233u8,122u8,249u8,210u8,233u8,38u8,249u8,221u8, - 159u8,204u8,36u8,191u8,251u8,83u8,57u8,241u8,122u8,186u8,73u8,252u8,26u8,211u8,51u8,17u8,50u8,46u8,140u8,139u8, - 30u8,154u8,188u8,82u8,58u8,28u8,82u8,221u8,143u8,238u8,203u8,24u8,226u8,36u8,238u8,140u8,130u8,156u8,148u8,223u8, - 1u8,128u8,140u8,121u8,41u8,46u8,78u8,246u8,185u8,125u8,9u8,65u8,191u8,251u8,17u8,167u8,253u8,152u8,255u8,153u8, - 130u8,53u8,207u8,90u8,248u8,212u8,19u8,23u8,29u8,105u8,251u8,189u8,169u8,175u8,42u8,196u8,226u8,90u8,156u8,77u8, - 67u8,82u8,6u8,248u8,93u8,181u8,16u8,140u8,26u8,111u8,241u8,141u8,142u8,188u8,201u8,54u8,174u8,229u8,151u8,89u8, - 182u8,250u8,247u8,181u8,82u8,206u8,88u8,243u8,111u8,124u8,17u8,64u8,43u8,106u8,29u8,123u8,229u8,12u8,44u8,253u8, - 9u8,186u8,182u8,165u8,39u8,208u8,212u8,28u8,215u8,118u8,172u8,168u8,82u8,255u8,62u8,1u8,246u8,124u8,54u8,111u8, - 168u8,28u8,91u8,143u8,166u8,230u8,35u8,33u8,99u8,173u8,220u8,108u8,195u8,207u8,0u8,220u8,218u8,126u8,185u8,7u8, - 54u8,240u8,57u8,67u8,157u8,157u8,54u8,24u8,54u8,56u8,148u8,69u8,44u8,241u8,215u8,189u8,202u8,100u8,89u8,168u8, - 223u8,231u8,134u8,212u8,67u8,105u8,10u8,234u8,229u8,78u8,254u8,59u8,160u8,179u8,2u8,175u8,111u8,194u8,246u8,104u8, - 51u8,221u8,112u8,208u8,155u8,113u8,180u8,141u8,220u8,212u8,28u8,109u8,95u8,191u8,254u8,90u8,94u8,54u8,50u8,123u8, - 246u8,213u8,60u8,236u8,138u8,61u8,179u8,238u8,183u8,51u8,176u8,26u8,149u8,193u8,61u8,245u8,22u8,94u8,78u8,101u8, - 26u8,223u8,35u8,86u8,50u8,22u8,52u8,21u8,104u8,77u8,109u8,254u8,14u8,122u8,236u8,76u8,11u8,215u8,216u8,113u8, - 110u8,175u8,185u8,27u8,56u8,74u8,56u8,221u8,48u8,234u8,102u8,73u8,151u8,164u8,202u8,13u8,35u8,239u8,137u8,148u8, - 34u8,50u8,238u8,188u8,160u8,104u8,185u8,159u8,155u8,11u8,149u8,225u8,144u8,11u8,229u8,64u8,30u8,235u8,86u8,220u8, - 134u8,54u8,61u8,159u8,242u8,169u8,100u8,119u8,219u8,206u8,118u8,13u8,198u8,160u8,218u8,77u8,160u8,228u8,123u8,34u8, - 19u8,226u8,94u8,15u8,190u8,141u8,147u8,219u8,90u8,33u8,89u8,140u8,231u8,124u8,144u8,227u8,168u8,79u8,173u8,104u8, - 154u8,255u8,82u8,101u8,61u8,199u8,101u8,202u8,29u8,100u8,55u8,39u8,73u8,7u8,110u8,241u8,38u8,125u8,205u8,70u8, - 232u8,88u8,131u8,164u8,20u8,178u8,36u8,108u8,153u8,211u8,123u8,28u8,224u8,180u8,217u8,39u8,188u8,53u8,209u8,106u8, - 54u8,131u8,158u8,3u8,90u8,45u8,8u8,42u8,4u8,75u8,66u8,111u8,180u8,246u8,137u8,157u8,192u8,218u8,176u8,237u8, - 172u8,152u8,208u8,195u8,180u8,15u8,170u8,34u8,45u8,246u8,8u8,255u8,241u8,9u8,116u8,24u8,190u8,200u8,116u8,246u8, - 195u8,39u8,172u8,224u8,127u8,52u8,233u8,207u8,163u8,158u8,241u8,69u8,98u8,226u8,6u8,14u8,228u8,176u8,151u8,103u8, - 126u8,203u8,242u8,135u8,46u8,90u8,74u8,233u8,128u8,237u8,53u8,161u8,80u8,67u8,180u8,192u8,47u8,80u8,149u8,193u8, - 17u8,170u8,204u8,208u8,208u8,178u8,48u8,43u8,135u8,40u8,247u8,53u8,192u8,153u8,19u8,62u8,51u8,228u8,43u8,199u8, - 92u8,155u8,169u8,129u8,102u8,234u8,118u8,53u8,87u8,149u8,96u8,137u8,28u8,165u8,166u8,238u8,202u8,80u8,27u8,190u8, - 158u8,167u8,34u8,199u8,221u8,192u8,224u8,238u8,246u8,247u8,100u8,111u8,25u8,81u8,5u8,130u8,148u8,153u8,91u8,201u8, - 238u8,53u8,202u8,52u8,171u8,111u8,226u8,154u8,153u8,190u8,56u8,20u8,224u8,3u8,190u8,132u8,115u8,236u8,206u8,88u8, - 81u8,233u8,173u8,23u8,2u8,168u8,180u8,100u8,214u8,84u8,128u8,74u8,124u8,93u8,36u8,63u8,134u8,158u8,103u8,219u8, - 40u8,154u8,96u8,176u8,99u8,91u8,133u8,1u8,116u8,104u8,56u8,249u8,205u8,108u8,190u8,60u8,21u8,123u8,236u8,21u8, - 211u8,107u8,214u8,179u8,95u8,5u8,163u8,45u8,72u8,219u8,30u8,200u8,140u8,238u8,132u8,109u8,139u8,139u8,46u8,226u8, - 187u8,13u8,51u8,75u8,167u8,158u8,134u8,91u8,101u8,160u8,164u8,120u8,102u8,235u8,121u8,253u8,57u8,206u8,115u8,110u8, - 228u8,121u8,185u8,148u8,188u8,200u8,54u8,97u8,184u8,59u8,188u8,28u8,117u8,203u8,7u8,159u8,197u8,75u8,174u8,97u8, - 132u8,111u8,161u8,13u8,176u8,49u8,73u8,77u8,184u8,226u8,186u8,88u8,63u8,198u8,219u8,33u8,20u8,170u8,211u8,93u8, - 29u8,186u8,125u8,74u8,84u8,55u8,45u8,154u8,107u8,19u8,62u8,74u8,160u8,248u8,133u8,55u8,54u8,234u8,146u8,195u8, - 230u8,198u8,133u8,150u8,89u8,98u8,81u8,115u8,170u8,92u8,227u8,123u8,168u8,72u8,230u8,108u8,165u8,174u8,20u8,14u8, - 156u8,182u8,247u8,178u8,76u8,131u8,90u8,245u8,230u8,98u8,93u8,114u8,75u8,81u8,56u8,137u8,40u8,129u8,54u8,88u8, - 133u8,6u8,165u8,73u8,197u8,235u8,2u8,193u8,5u8,146u8,214u8,195u8,246u8,127u8,195u8,210u8,162u8,89u8,173u8,5u8, - 13u8,246u8,73u8,93u8,206u8,178u8,104u8,216u8,205u8,89u8,4u8,167u8,223u8,172u8,240u8,157u8,182u8,255u8,36u8,35u8, - 255u8,233u8,120u8,5u8,212u8,39u8,180u8,115u8,22u8,35u8,86u8,205u8,144u8,222u8,42u8,248u8,88u8,194u8,254u8,85u8, - 243u8,93u8,2u8,166u8,189u8,49u8,152u8,63u8,192u8,123u8,188u8,202u8,159u8,73u8,39u8,198u8,62u8,217u8,94u8,174u8, - 238u8,96u8,10u8,101u8,120u8,193u8,16u8,248u8,141u8,190u8,190u8,134u8,63u8,2u8,124u8,223u8,113u8,215u8,129u8,113u8, - 51u8,241u8,7u8,172u8,14u8,5u8,88u8,227u8,140u8,164u8,141u8,249u8,100u8,221u8,136u8,203u8,110u8,165u8,53u8,75u8, - 39u8,9u8,7u8,69u8,219u8,129u8,131u8,194u8,253u8,187u8,153u8,93u8,18u8,70u8,64u8,252u8,162u8,230u8,58u8,56u8, - 236u8,146u8,215u8,245u8,55u8,19u8,148u8,92u8,239u8,91u8,185u8,25u8,60u8,70u8,32u8,106u8,163u8,141u8,4u8,98u8, - 35u8,199u8,27u8,134u8,61u8,181u8,210u8,72u8,160u8,81u8,30u8,185u8,128u8,68u8,154u8,54u8,202u8,246u8,65u8,75u8, - 207u8,120u8,29u8,13u8,101u8,204u8,124u8,165u8,13u8,109u8,125u8,115u8,33u8,173u8,61u8,254u8,66u8,3u8,75u8,53u8, - 176u8,108u8,56u8,76u8,87u8,134u8,222u8,196u8,4u8,134u8,162u8,220u8,30u8,9u8,151u8,170u8,187u8,85u8,242u8,171u8, - 108u8,225u8,228u8,40u8,204u8,117u8,85u8,205u8,119u8,29u8,99u8,104u8,185u8,97u8,93u8,107u8,161u8,94u8,149u8,53u8, - 47u8,20u8,178u8,218u8,2u8,109u8,57u8,56u8,39u8,44u8,226u8,214u8,22u8,109u8,248u8,210u8,150u8,119u8,31u8,69u8, - 129u8,41u8,35u8,118u8,209u8,146u8,161u8,199u8,162u8,173u8,58u8,146u8,106u8,100u8,169u8,70u8,22u8,170u8,71u8,105u8, - 199u8,34u8,203u8,22u8,176u8,187u8,182u8,0u8,1u8,93u8,192u8,82u8,207u8,89u8,109u8,217u8,134u8,223u8,113u8,217u8, - 23u8,184u8,168u8,24u8,47u8,143u8,137u8,212u8,43u8,144u8,53u8,85u8,164u8,223u8,206u8,247u8,33u8,178u8,200u8,168u8, - 176u8,182u8,29u8,90u8,9u8,12u8,77u8,171u8,201u8,194u8,228u8,63u8,219u8,242u8,97u8,86u8,107u8,168u8,160u8,41u8, - 123u8,223u8,242u8,223u8,68u8,149u8,89u8,188u8,168u8,160u8,75u8,191u8,222u8,249u8,111u8,34u8,203u8,168u8,131u8,212u8, - 87u8,244u8,97u8,172u8,58u8,172u8,171u8,173u8,82u8,89u8,234u8,112u8,214u8,42u8,190u8,177u8,240u8,56u8,11u8,19u8, - 111u8,20u8,166u8,179u8,225u8,202u8,154u8,227u8,102u8,84u8,124u8,219u8,250u8,206u8,133u8,69u8,132u8,129u8,253u8,22u8, - 209u8,117u8,139u8,49u8,210u8,176u8,94u8,198u8,236u8,245u8,148u8,217u8,33u8,48u8,15u8,52u8,154u8,74u8,15u8,113u8, - 45u8,135u8,198u8,120u8,153u8,245u8,132u8,217u8,127u8,100u8,99u8,13u8,70u8,22u8,112u8,229u8,145u8,228u8,90u8,87u8, - 149u8,40u8,96u8,122u8,211u8,170u8,233u8,80u8,172u8,95u8,208u8,251u8,237u8,140u8,228u8,166u8,89u8,238u8,18u8,13u8, - 168u8,50u8,124u8,242u8,93u8,164u8,19u8,183u8,214u8,126u8,29u8,78u8,241u8,34u8,210u8,137u8,83u8,225u8,54u8,198u8, - 168u8,139u8,75u8,171u8,119u8,109u8,214u8,242u8,228u8,249u8,151u8,224u8,91u8,127u8,215u8,235u8,227u8,92u8,119u8,215u8, - 165u8,24u8,51u8,207u8,116u8,125u8,245u8,246u8,170u8,135u8,229u8,220u8,65u8,18u8,220u8,33u8,161u8,244u8,214u8,32u8, - 64u8,38u8,178u8,183u8,50u8,10u8,22u8,120u8,255u8,91u8,40u8,26u8,189u8,198u8,45u8,102u8,254u8,240u8,1u8,129u8, - 106u8,90u8,138u8,128u8,156u8,130u8,96u8,137u8,107u8,70u8,2u8,210u8,11u8,103u8,147u8,229u8,71u8,109u8,33u8,105u8, - 40u8,235u8,151u8,144u8,118u8,104u8,107u8,100u8,180u8,77u8,84u8,215u8,100u8,83u8,243u8,148u8,83u8,69u8,163u8,51u8, - 179u8,237u8,18u8,252u8,85u8,207u8,45u8,91u8,89u8,84u8,95u8,253u8,142u8,100u8,133u8,6u8,87u8,42u8,158u8,178u8, - 99u8,22u8,178u8,188u8,42u8,98u8,231u8,4u8,83u8,185u8,181u8,167u8,41u8,217u8,219u8,120u8,162u8,20u8,176u8,77u8, - 230u8,25u8,202u8,3u8,211u8,54u8,154u8,101u8,144u8,185u8,209u8,188u8,140u8,202u8,108u8,154u8,148u8,108u8,33u8,115u8, - 57u8,113u8,147u8,189u8,190u8,124u8,81u8,6u8,95u8,159u8,134u8,132u8,73u8,254u8,77u8,140u8,147u8,220u8,197u8,167u8, - 110u8,131u8,117u8,85u8,216u8,250u8,225u8,99u8,238u8,236u8,197u8,203u8,36u8,37u8,51u8,219u8,226u8,125u8,159u8,210u8, - 201u8,184u8,234u8,208u8,143u8,134u8,122u8,122u8,85u8,219u8,66u8,34u8,203u8,161u8,144u8,61u8,201u8,147u8,170u8,202u8, - 203u8,110u8,161u8,64u8,44u8,103u8,189u8,164u8,174u8,89u8,217u8,76u8,87u8,165u8,50u8,87u8,111u8,86u8,195u8,225u8, - 100u8,232u8,217u8,155u8,241u8,132u8,31u8,106u8,59u8,243u8,108u8,74u8,152u8,37u8,13u8,127u8,103u8,7u8,174u8,196u8, - 78u8,87u8,8u8,173u8,248u8,71u8,204u8,1u8,251u8,45u8,194u8,29u8,227u8,212u8,234u8,40u8,5u8,93u8,91u8,120u8, - 214u8,199u8,101u8,158u8,34u8,97u8,170u8,137u8,58u8,233u8,75u8,73u8,37u8,86u8,74u8,219u8,64u8,234u8,209u8,209u8, - 17u8,6u8,203u8,65u8,117u8,165u8,75u8,205u8,121u8,81u8,85u8,52u8,149u8,37u8,83u8,234u8,132u8,62u8,214u8,221u8, - 132u8,139u8,186u8,237u8,189u8,155u8,181u8,229u8,187u8,177u8,193u8,43u8,24u8,82u8,130u8,251u8,213u8,73u8,201u8,129u8, - 186u8,169u8,44u8,61u8,124u8,55u8,122u8,87u8,125u8,65u8,212u8,99u8,247u8,42u8,115u8,74u8,69u8,116u8,217u8,114u8, - 39u8,82u8,215u8,242u8,36u8,106u8,13u8,209u8,2u8,87u8,118u8,42u8,37u8,229u8,224u8,245u8,253u8,156u8,182u8,51u8, - 238u8,191u8,84u8,242u8,23u8,244u8,106u8,89u8,203u8,221u8,109u8,192u8,196u8,253u8,231u8,86u8,190u8,209u8,38u8,237u8, - 89u8,254u8,98u8,54u8,244u8,81u8,11u8,87u8,172u8,241u8,167u8,53u8,124u8,152u8,13u8,127u8,44u8,139u8,171u8,133u8, - 83u8,112u8,146u8,212u8,178u8,109u8,106u8,175u8,135u8,181u8,15u8,152u8,19u8,206u8,100u8,35u8,174u8,150u8,167u8,0u8, - 105u8,171u8,215u8,93u8,155u8,22u8,213u8,0u8,29u8,121u8,59u8,111u8,84u8,182u8,17u8,5u8,222u8,94u8,54u8,248u8, - 180u8,122u8,192u8,181u8,159u8,117u8,86u8,19u8,151u8,224u8,9u8,89u8,198u8,131u8,213u8,3u8,5u8,191u8,82u8,248u8, - 35u8,57u8,39u8,149u8,183u8,9u8,77u8,210u8,26u8,69u8,106u8,205u8,134u8,101u8,118u8,117u8,111u8,219u8,232u8,15u8, - 23u8,17u8,27u8,115u8,114u8,237u8,72u8,91u8,130u8,17u8,33u8,84u8,67u8,60u8,210u8,49u8,18u8,54u8,58u8,224u8, - 156u8,18u8,151u8,68u8,95u8,223u8,53u8,2u8,219u8,40u8,10u8,115u8,89u8,154u8,53u8,67u8,163u8,189u8,220u8,212u8, - 199u8,175u8,228u8,84u8,7u8,254u8,63u8,252u8,171u8,114u8,202u8,136u8,232u8,118u8,187u8,155u8,243u8,42u8,155u8,190u8, - 219u8,249u8,78u8,236u8,218u8,71u8,193u8,218u8,253u8,171u8,242u8,43u8,139u8,100u8,187u8,95u8,33u8,90u8,122u8,118u8, - 231u8,123u8,113u8,235u8,8u8,185u8,245u8,255u8,65u8,186u8,14u8,190u8,73u8,186u8,14u8,247u8,215u8,229u8,23u8,254u8, - 133u8,103u8,106u8,114u8,226u8,223u8,81u8,192u8,63u8,86u8,70u8,141u8,202u8,77u8,88u8,218u8,198u8,84u8,106u8,247u8, - 47u8,43u8,129u8,202u8,60u8,237u8,126u8,139u8,109u8,43u8,26u8,198u8,71u8,149u8,149u8,61u8,62u8,249u8,95u8,177u8, - 162u8,248u8,105u8,160u8,96u8,0u8,0u8,0u8,0u8,15u8,114u8,101u8,99u8,111u8,110u8,102u8,105u8,103u8,117u8,114u8, - 97u8,116u8,105u8,111u8,110u8,143u8,19u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,90u8,111u8, - 115u8,211u8,70u8,26u8,127u8,207u8,167u8,88u8,184u8,25u8,46u8,153u8,115u8,13u8,189u8,227u8,250u8,194u8,1u8,166u8, - 105u8,8u8,52u8,83u8,66u8,24u8,26u8,232u8,117u8,110u8,110u8,148u8,181u8,180u8,182u8,247u8,34u8,105u8,125u8,187u8, - 43u8,140u8,219u8,242u8,221u8,239u8,247u8,60u8,43u8,201u8,43u8,217u8,50u8,161u8,133u8,78u8,235u8,23u8,36u8,150u8, - 118u8,159u8,255u8,255u8,126u8,15u8,185u8,119u8,239u8,158u8,120u8,89u8,77u8,115u8,237u8,22u8,202u8,137u8,212u8,148u8, - 51u8,61u8,175u8,172u8,244u8,218u8,148u8,66u8,151u8,51u8,99u8,139u8,240u8,59u8,126u8,19u8,111u8,101u8,174u8,51u8, - 233u8,141u8,117u8,35u8,33u8,203u8,76u8,104u8,231u8,42u8,92u8,178u8,170u8,123u8,77u8,189u8,85u8,165u8,119u8,183u8, - 238u8,129u8,178u8,55u8,194u8,173u8,203u8,116u8,97u8,77u8,169u8,127u8,82u8,61u8,242u8,233u8,66u8,150u8,115u8,220u8, - 39u8,210u8,126u8,161u8,34u8,242u8,227u8,91u8,133u8,201u8,170u8,92u8,9u8,185u8,244u8,198u8,37u8,51u8,43u8,11u8, - 181u8,50u8,246u8,122u8,50u8,233u8,243u8,250u8,249u8,150u8,192u8,167u8,114u8,74u8,56u8,159u8,77u8,38u8,202u8,90u8, - 99u8,143u8,186u8,207u8,102u8,74u8,250u8,202u8,42u8,215u8,123u8,236u8,244u8,188u8,84u8,56u8,219u8,62u8,221u8,226u8, - 37u8,211u8,212u8,84u8,165u8,63u8,26u8,62u8,193u8,154u8,238u8,121u8,239u8,188u8,188u8,86u8,251u8,222u8,175u8,157u8, - 87u8,69u8,34u8,179u8,12u8,242u8,185u8,88u8,196u8,173u8,163u8,94u8,23u8,10u8,228u8,138u8,229u8,158u8,51u8,48u8, - 168u8,46u8,19u8,156u8,242u8,149u8,219u8,43u8,149u8,177u8,114u8,174u8,146u8,185u8,220u8,203u8,208u8,202u8,210u8,201u8, - 148u8,172u8,156u8,204u8,148u8,170u8,13u8,53u8,179u8,90u8,193u8,243u8,219u8,182u8,226u8,7u8,115u8,243u8,86u8,217u8, - 82u8,150u8,105u8,173u8,244u8,208u8,233u8,105u8,110u8,210u8,235u8,253u8,71u8,224u8,102u8,167u8,74u8,87u8,185u8,36u8, - 56u8,124u8,255u8,105u8,168u8,146u8,184u8,116u8,161u8,40u8,104u8,62u8,112u8,82u8,149u8,202u8,105u8,183u8,255u8,16u8, - 180u8,112u8,208u8,187u8,214u8,153u8,2u8,249u8,148u8,60u8,141u8,40u8,149u8,94u8,80u8,224u8,200u8,156u8,115u8,37u8, - 72u8,200u8,81u8,238u8,165u8,245u8,66u8,138u8,82u8,173u8,132u8,90u8,154u8,116u8,49u8,106u8,175u8,174u8,180u8,95u8, - 240u8,243u8,193u8,228u8,26u8,139u8,203u8,133u8,118u8,72u8,39u8,1u8,186u8,70u8,164u8,50u8,207u8,21u8,164u8,106u8, - 41u8,220u8,217u8,153u8,97u8,119u8,248u8,189u8,243u8,182u8,74u8,189u8,120u8,161u8,86u8,167u8,196u8,54u8,200u8,185u8, - 144u8,78u8,100u8,214u8,44u8,71u8,130u8,124u8,173u8,234u8,44u8,161u8,15u8,203u8,54u8,17u8,213u8,87u8,15u8,130u8, - 128u8,239u8,55u8,42u8,126u8,107u8,242u8,204u8,117u8,114u8,94u8,78u8,77u8,229u8,73u8,53u8,175u8,132u8,153u8,245u8, - 243u8,60u8,102u8,127u8,210u8,17u8,143u8,216u8,95u8,171u8,117u8,196u8,150u8,77u8,72u8,172u8,69u8,89u8,21u8,83u8, - 101u8,7u8,229u8,105u8,14u8,95u8,34u8,220u8,137u8,103u8,46u8,157u8,239u8,51u8,30u8,139u8,139u8,50u8,95u8,183u8, - 229u8,3u8,12u8,119u8,87u8,160u8,113u8,75u8,145u8,168u8,36u8,189u8,67u8,9u8,101u8,212u8,14u8,206u8,141u8,5u8, - 203u8,12u8,245u8,135u8,74u8,211u8,64u8,121u8,107u8,53u8,224u8,175u8,147u8,240u8,115u8,50u8,225u8,235u8,223u8,242u8, - 237u8,135u8,29u8,167u8,60u8,222u8,50u8,248u8,171u8,30u8,225u8,149u8,206u8,115u8,49u8,85u8,34u8,211u8,78u8,78u8, - 41u8,2u8,244u8,12u8,17u8,167u8,169u8,192u8,58u8,83u8,217u8,84u8,81u8,132u8,44u8,235u8,90u8,157u8,137u8,170u8, - 204u8,20u8,23u8,206u8,150u8,96u8,47u8,142u8,69u8,40u8,47u8,162u8,46u8,47u8,177u8,191u8,158u8,4u8,22u8,125u8, - 9u8,90u8,199u8,69u8,82u8,94u8,162u8,50u8,95u8,117u8,252u8,123u8,213u8,17u8,72u8,35u8,78u8,40u8,154u8,185u8, - 122u8,135u8,96u8,225u8,187u8,148u8,30u8,94u8,156u8,158u8,92u8,188u8,120u8,122u8,246u8,236u8,245u8,171u8,227u8,203u8, - 179u8,139u8,23u8,108u8,110u8,241u8,72u8,124u8,121u8,212u8,146u8,63u8,22u8,87u8,61u8,41u8,126u8,5u8,249u8,134u8, - 238u8,223u8,59u8,116u8,207u8,77u8,166u8,103u8,235u8,32u8,250u8,137u8,92u8,202u8,169u8,206u8,181u8,95u8,95u8,53u8, - 84u8,97u8,231u8,217u8,76u8,89u8,242u8,118u8,8u8,113u8,100u8,55u8,156u8,0u8,19u8,168u8,119u8,75u8,149u8,122u8, - 149u8,197u8,140u8,206u8,47u8,158u8,156u8,61u8,253u8,49u8,57u8,57u8,126u8,121u8,252u8,205u8,217u8,243u8,179u8,203u8, - 31u8,27u8,158u8,255u8,136u8,120u8,110u8,4u8,229u8,10u8,39u8,40u8,196u8,2u8,197u8,146u8,91u8,9u8,184u8,101u8, - 227u8,152u8,234u8,217u8,139u8,55u8,199u8,207u8,207u8,158u8,36u8,223u8,60u8,191u8,56u8,249u8,46u8,185u8,60u8,59u8, - 63u8,109u8,200u8,62u8,248u8,20u8,100u8,159u8,189u8,198u8,63u8,79u8,47u8,94u8,37u8,167u8,111u8,78u8,95u8,92u8, - 54u8,164u8,255u8,25u8,213u8,181u8,144u8,73u8,161u8,224u8,100u8,149u8,213u8,229u8,92u8,212u8,21u8,114u8,220u8,158u8, - 217u8,140u8,7u8,67u8,113u8,48u8,22u8,39u8,48u8,157u8,33u8,90u8,136u8,95u8,8u8,107u8,174u8,65u8,111u8,186u8, - 14u8,17u8,41u8,54u8,17u8,89u8,183u8,212u8,48u8,62u8,240u8,121u8,137u8,122u8,90u8,206u8,145u8,106u8,172u8,20u8, - 252u8,242u8,44u8,102u8,207u8,209u8,158u8,30u8,132u8,58u8,125u8,40u8,102u8,21u8,153u8,66u8,123u8,13u8,99u8,252u8, - 164u8,14u8,250u8,117u8,91u8,220u8,13u8,77u8,253u8,48u8,170u8,60u8,253u8,22u8,139u8,86u8,133u8,31u8,214u8,39u8, - 189u8,203u8,125u8,98u8,135u8,181u8,145u8,130u8,17u8,68u8,184u8,36u8,180u8,23u8,168u8,141u8,41u8,219u8,2u8,37u8, - 61u8,225u8,242u8,149u8,112u8,234u8,39u8,72u8,156u8,131u8,195u8,171u8,145u8,48u8,72u8,73u8,187u8,210u8,78u8,241u8, - 80u8,195u8,239u8,96u8,225u8,242u8,175u8,158u8,108u8,67u8,197u8,100u8,78u8,179u8,80u8,214u8,82u8,15u8,164u8,111u8, - 31u8,212u8,182u8,161u8,22u8,229u8,147u8,121u8,165u8,179u8,164u8,84u8,239u8,124u8,146u8,90u8,21u8,234u8,21u8,42u8, - 231u8,65u8,208u8,15u8,58u8,4u8,117u8,18u8,51u8,219u8,146u8,251u8,80u8,60u8,66u8,34u8,140u8,4u8,143u8,66u8, - 147u8,73u8,29u8,57u8,60u8,21u8,168u8,131u8,129u8,200u8,56u8,60u8,60u8,106u8,197u8,41u8,208u8,195u8,19u8,111u8, - 30u8,118u8,92u8,253u8,248u8,160u8,125u8,207u8,34u8,119u8,121u8,142u8,58u8,47u8,79u8,118u8,12u8,104u8,241u8,167u8, - 46u8,249u8,247u8,71u8,91u8,111u8,246u8,148u8,233u8,29u8,167u8,155u8,194u8,219u8,218u8,141u8,29u8,194u8,174u8,88u8, - 236u8,170u8,191u8,91u8,150u8,234u8,146u8,124u8,223u8,126u8,171u8,141u8,17u8,213u8,192u8,151u8,86u8,191u8,165u8,10u8, - 129u8,248u8,227u8,129u8,136u8,58u8,62u8,162u8,106u8,137u8,57u8,202u8,106u8,4u8,241u8,66u8,230u8,219u8,93u8,42u8, - 170u8,160u8,168u8,56u8,237u8,85u8,183u8,48u8,85u8,158u8,181u8,201u8,130u8,217u8,43u8,227u8,54u8,99u8,102u8,179u8, - 92u8,151u8,74u8,252u8,96u8,181u8,87u8,223u8,43u8,207u8,121u8,88u8,27u8,113u8,89u8,217u8,165u8,161u8,33u8,13u8, - 89u8,83u8,223u8,47u8,161u8,169u8,141u8,179u8,45u8,12u8,212u8,186u8,230u8,75u8,153u8,82u8,247u8,145u8,190u8,69u8, - 63u8,119u8,218u8,244u8,227u8,186u8,239u8,80u8,85u8,114u8,119u8,59u8,56u8,28u8,138u8,209u8,78u8,203u8,216u8,17u8, - 154u8,125u8,158u8,163u8,161u8,118u8,246u8,243u8,251u8,195u8,155u8,120u8,18u8,26u8,86u8,133u8,250u8,227u8,249u8,47u8, - 24u8,234u8,35u8,220u8,39u8,211u8,255u8,85u8,26u8,218u8,12u8,218u8,227u8,147u8,86u8,197u8,198u8,191u8,183u8,63u8, - 157u8,131u8,7u8,253u8,136u8,174u8,197u8,206u8,159u8,89u8,83u8,60u8,220u8,125u8,234u8,241u8,141u8,138u8,99u8,39u8, - 181u8,201u8,200u8,131u8,194u8,79u8,196u8,212u8,152u8,60u8,178u8,217u8,109u8,245u8,78u8,59u8,239u8,6u8,217u8,127u8, - 221u8,103u8,214u8,143u8,189u8,239u8,25u8,56u8,68u8,48u8,119u8,3u8,28u8,42u8,106u8,130u8,219u8,32u8,97u8,44u8, - 206u8,43u8,199u8,125u8,163u8,110u8,209u8,164u8,127u8,131u8,90u8,194u8,73u8,17u8,144u8,242u8,112u8,191u8,220u8,40u8, - 168u8,14u8,162u8,16u8,25u8,170u8,210u8,144u8,243u8,137u8,17u8,165u8,241u8,34u8,51u8,136u8,215u8,53u8,6u8,80u8, - 72u8,134u8,73u8,180u8,30u8,11u8,120u8,70u8,164u8,215u8,51u8,52u8,98u8,154u8,67u8,55u8,83u8,54u8,14u8,29u8, - 196u8,16u8,20u8,126u8,7u8,36u8,12u8,215u8,192u8,250u8,151u8,95u8,68u8,11u8,98u8,81u8,172u8,205u8,42u8,41u8, - 116u8,106u8,145u8,22u8,144u8,47u8,163u8,3u8,232u8,91u8,247u8,233u8,212u8,112u8,64u8,29u8,246u8,218u8,137u8,85u8, - 64u8,246u8,101u8,251u8,232u8,125u8,20u8,157u8,57u8,50u8,48u8,80u8,65u8,2u8,205u8,16u8,63u8,83u8,131u8,88u8, - 92u8,37u8,243u8,220u8,76u8,101u8,158u8,20u8,149u8,239u8,119u8,184u8,175u8,135u8,75u8,25u8,19u8,171u8,44u8,205u8, - 138u8,220u8,143u8,64u8,110u8,175u8,42u8,221u8,217u8,97u8,183u8,61u8,229u8,110u8,96u8,17u8,80u8,32u8,122u8,126u8, - 182u8,22u8,170u8,208u8,30u8,99u8,40u8,35u8,72u8,76u8,70u8,140u8,5u8,34u8,88u8,62u8,142u8,152u8,196u8,252u8, - 26u8,48u8,121u8,241u8,29u8,34u8,39u8,149u8,168u8,80u8,147u8,248u8,245u8,23u8,60u8,210u8,179u8,26u8,13u8,132u8, - 210u8,204u8,220u8,174u8,33u8,103u8,249u8,5u8,250u8,154u8,95u8,135u8,73u8,179u8,123u8,237u8,184u8,30u8,63u8,101u8, - 229u8,13u8,33u8,68u8,138u8,73u8,8u8,9u8,141u8,133u8,156u8,97u8,8u8,133u8,74u8,145u8,116u8,1u8,44u8,147u8, - 10u8,110u8,72u8,215u8,145u8,88u8,45u8,52u8,48u8,33u8,100u8,157u8,87u8,18u8,87u8,189u8,226u8,177u8,49u8,102u8, - 43u8,196u8,155u8,115u8,225u8,48u8,143u8,7u8,122u8,96u8,25u8,51u8,33u8,20u8,94u8,20u8,100u8,209u8,70u8,130u8, - 62u8,163u8,88u8,162u8,6u8,96u8,133u8,176u8,33u8,136u8,237u8,0u8,194u8,188u8,237u8,49u8,12u8,209u8,59u8,238u8, - 42u8,127u8,42u8,33u8,104u8,76u8,172u8,160u8,204u8,36u8,245u8,4u8,196u8,42u8,12u8,190u8,152u8,82u8,237u8,214u8, - 115u8,208u8,79u8,21u8,22u8,105u8,236u8,85u8,76u8,148u8,48u8,44u8,109u8,19u8,40u8,55u8,131u8,166u8,219u8,198u8, - 204u8,12u8,94u8,22u8,85u8,238u8,245u8,18u8,3u8,243u8,214u8,86u8,192u8,42u8,206u8,237u8,236u8,142u8,104u8,140u8, - 19u8,172u8,79u8,173u8,42u8,230u8,59u8,40u8,231u8,206u8,128u8,226u8,172u8,238u8,196u8,254u8,163u8,40u8,177u8,198u8, - 131u8,51u8,219u8,71u8,228u8,234u8,14u8,44u8,124u8,7u8,45u8,53u8,101u8,83u8,168u8,16u8,117u8,119u8,168u8,88u8, - 34u8,214u8,70u8,228u8,180u8,2u8,194u8,224u8,20u8,121u8,112u8,138u8,182u8,64u8,251u8,19u8,96u8,32u8,92u8,81u8, - 8u8,41u8,21u8,92u8,179u8,180u8,6u8,4u8,2u8,133u8,212u8,160u8,122u8,18u8,162u8,19u8,216u8,100u8,185u8,152u8, - 47u8,160u8,30u8,10u8,166u8,246u8,8u8,100u8,108u8,94u8,194u8,126u8,7u8,75u8,59u8,138u8,40u8,204u8,236u8,52u8, - 66u8,121u8,171u8,167u8,21u8,163u8,67u8,85u8,236u8,205u8,56u8,222u8,221u8,132u8,201u8,179u8,230u8,42u8,209u8,242u8, - 87u8,11u8,69u8,88u8,179u8,45u8,250u8,130u8,241u8,60u8,53u8,51u8,2u8,92u8,138u8,39u8,73u8,156u8,221u8,108u8, - 206u8,72u8,112u8,12u8,11u8,50u8,31u8,139u8,51u8,76u8,14u8,232u8,14u8,58u8,173u8,114u8,105u8,71u8,225u8,26u8, - 36u8,139u8,57u8,119u8,246u8,166u8,130u8,147u8,125u8,71u8,186u8,141u8,197u8,15u8,42u8,86u8,181u8,177u8,13u8,89u8, - 99u8,36u8,244u8,24u8,22u8,132u8,217u8,50u8,43u8,9u8,23u8,207u8,231u8,86u8,205u8,165u8,167u8,146u8,11u8,195u8, - 49u8,254u8,206u8,196u8,210u8,44u8,33u8,68u8,141u8,189u8,35u8,222u8,68u8,65u8,240u8,217u8,17u8,168u8,106u8,218u8, - 228u8,134u8,5u8,22u8,89u8,240u8,138u8,80u8,75u8,131u8,146u8,128u8,141u8,200u8,79u8,210u8,243u8,235u8,17u8,213u8, - 28u8,10u8,136u8,81u8,100u8,227u8,78u8,117u8,11u8,164u8,105u8,178u8,162u8,156u8,167u8,103u8,111u8,59u8,220u8,89u8, - 42u8,4u8,4u8,24u8,37u8,186u8,12u8,239u8,59u8,27u8,234u8,186u8,182u8,148u8,105u8,94u8,101u8,52u8,122u8,173u8, - 35u8,67u8,177u8,163u8,53u8,55u8,96u8,174u8,7u8,236u8,143u8,110u8,39u8,107u8,214u8,198u8,180u8,138u8,228u8,232u8, - 73u8,192u8,49u8,217u8,8u8,75u8,235u8,83u8,90u8,140u8,186u8,237u8,182u8,68u8,152u8,189u8,95u8,167u8,66u8,125u8, - 234u8,167u8,157u8,196u8,114u8,142u8,43u8,80u8,55u8,132u8,41u8,102u8,200u8,186u8,4u8,0u8,251u8,148u8,67u8,13u8, - 230u8,177u8,193u8,177u8,17u8,32u8,143u8,98u8,77u8,48u8,144u8,97u8,194u8,184u8,55u8,133u8,85u8,123u8,17u8,79u8, - 184u8,181u8,41u8,126u8,255u8,165u8,228u8,184u8,79u8,55u8,251u8,148u8,209u8,164u8,184u8,57u8,53u8,39u8,3u8,189u8, - 216u8,46u8,244u8,233u8,237u8,133u8,39u8,147u8,58u8,152u8,146u8,150u8,101u8,109u8,149u8,163u8,161u8,92u8,63u8,33u8, - 135u8,242u8,106u8,156u8,131u8,197u8,20u8,203u8,58u8,191u8,120u8,0u8,218u8,56u8,201u8,161u8,235u8,146u8,130u8,81u8, - 18u8,90u8,181u8,146u8,54u8,107u8,244u8,238u8,198u8,202u8,70u8,76u8,166u8,61u8,153u8,116u8,163u8,239u8,40u8,122u8, - 221u8,238u8,191u8,249u8,80u8,227u8,148u8,131u8,93u8,211u8,109u8,167u8,244u8,61u8,190u8,81u8,229u8,251u8,16u8,246u8, - 222u8,44u8,123u8,226u8,217u8,247u8,38u8,148u8,49u8,119u8,196u8,242u8,68u8,42u8,81u8,131u8,236u8,6u8,33u8,20u8, - 32u8,84u8,19u8,145u8,101u8,67u8,136u8,191u8,137u8,47u8,197u8,195u8,71u8,226u8,252u8,248u8,95u8,201u8,235u8,175u8, - 30u8,116u8,156u8,180u8,67u8,148u8,112u8,231u8,209u8,78u8,50u8,145u8,181u8,234u8,229u8,39u8,245u8,155u8,0u8,193u8, - 251u8,216u8,187u8,35u8,218u8,93u8,76u8,95u8,29u8,138u8,140u8,228u8,187u8,112u8,188u8,187u8,208u8,30u8,220u8,34u8, - 244u8,229u8,234u8,97u8,250u8,209u8,0u8,168u8,15u8,115u8,50u8,207u8,199u8,131u8,198u8,38u8,8u8,64u8,11u8,179u8, - 15u8,142u8,204u8,157u8,201u8,242u8,131u8,83u8,229u8,176u8,119u8,7u8,4u8,108u8,60u8,94u8,199u8,241u8,231u8,17u8, - 138u8,137u8,247u8,1u8,203u8,41u8,79u8,55u8,226u8,170u8,227u8,139u8,171u8,122u8,86u8,232u8,193u8,226u8,166u8,104u8, - 68u8,203u8,191u8,6u8,49u8,100u8,16u8,52u8,165u8,182u8,131u8,108u8,175u8,65u8,113u8,72u8,119u8,30u8,57u8,103u8, - 218u8,58u8,223u8,178u8,220u8,51u8,151u8,48u8,36u8,166u8,0u8,171u8,233u8,110u8,217u8,144u8,207u8,222u8,4u8,227u8, - 124u8,82u8,108u8,208u8,22u8,138u8,173u8,164u8,33u8,56u8,115u8,247u8,238u8,13u8,19u8,27u8,135u8,63u8,26u8,46u8, - 239u8,200u8,211u8,63u8,115u8,82u8,194u8,253u8,79u8,169u8,55u8,3u8,87u8,161u8,125u8,187u8,107u8,189u8,12u8,115u8, - 20u8,237u8,66u8,155u8,182u8,208u8,194u8,158u8,191u8,252u8,155u8,206u8,37u8,52u8,219u8,254u8,167u8,159u8,49u8,155u8, - 21u8,113u8,130u8,134u8,154u8,208u8,193u8,118u8,187u8,250u8,219u8,150u8,93u8,129u8,200u8,199u8,110u8,75u8,235u8,165u8, - 247u8,159u8,100u8,75u8,90u8,235u8,120u8,179u8,237u8,232u8,62u8,55u8,68u8,155u8,135u8,141u8,31u8,110u8,144u8,158u8, - 157u8,141u8,69u8,63u8,66u8,26u8,112u8,203u8,123u8,55u8,111u8,176u8,43u8,199u8,220u8,20u8,254u8,143u8,181u8,110u8, - 251u8,152u8,9u8,155u8,90u8,25u8,254u8,180u8,0u8,108u8,195u8,88u8,131u8,51u8,251u8,139u8,47u8,232u8,159u8,249u8, - 0u8,31u8,54u8,59u8,23u8,197u8,200u8,143u8,146u8,20u8,128u8,32u8,228u8,89u8,141u8,155u8,127u8,149u8,5u8,146u8, - 20u8,228u8,77u8,241u8,187u8,215u8,169u8,143u8,223u8,97u8,124u8,126u8,4u8,248u8,73u8,102u8,159u8,27u8,79u8,43u8, - 33u8,144u8,222u8,223u8,250u8,63u8,143u8,60u8,106u8,189u8,243u8,34u8,0u8,0u8,0u8,0u8,19u8,103u8,111u8,118u8, - 101u8,114u8,110u8,97u8,110u8,99u8,101u8,95u8,112u8,114u8,111u8,112u8,111u8,115u8,97u8,108u8,226u8,2u8,31u8,139u8, - 8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,149u8,82u8,189u8,106u8,195u8,48u8,16u8,222u8,243u8,20u8,7u8,93u8, - 18u8,104u8,227u8,61u8,157u8,74u8,10u8,93u8,11u8,109u8,167u8,82u8,194u8,69u8,62u8,213u8,34u8,178u8,206u8,156u8, - 164u8,132u8,16u8,242u8,238u8,149u8,236u8,252u8,152u8,216u8,208u8,86u8,216u8,139u8,116u8,223u8,175u8,84u8,20u8,5u8, - 60u8,147u8,54u8,142u8,32u8,84u8,4u8,47u8,188u8,37u8,113u8,232u8,20u8,189u8,10u8,55u8,236u8,209u8,166u8,109u8, - 12u8,176u8,51u8,214u8,194u8,154u8,32u8,122u8,42u8,1u8,61u8,52u8,40u8,1u8,88u8,3u8,187u8,7u8,85u8,161u8, - 113u8,240u8,125u8,1u8,194u8,122u8,15u8,79u8,77u8,96u8,127u8,229u8,154u8,79u8,138u8,162u8,200u8,63u8,188u8,87u8, - 198u8,67u8,250u8,60u8,37u8,6u8,12u8,4u8,90u8,184u8,110u8,149u8,111u8,32u8,80u8,115u8,25u8,109u8,50u8,197u8, - 128u8,91u8,54u8,37u8,40u8,35u8,42u8,90u8,20u8,40u8,169u8,33u8,87u8,146u8,83u8,251u8,100u8,40u8,236u8,136u8, - 220u8,0u8,139u8,174u8,132u8,183u8,128u8,155u8,36u8,124u8,162u8,193u8,60u8,177u8,210u8,130u8,53u8,237u8,88u8,54u8, - 139u8,197u8,213u8,239u8,170u8,57u8,39u8,61u8,76u8,32u8,45u8,45u8,38u8,241u8,15u8,17u8,221u8,198u8,21u8,247u8, - 56u8,105u8,199u8,125u8,144u8,168u8,194u8,88u8,113u8,85u8,234u8,201u8,7u8,22u8,186u8,135u8,50u8,237u8,193u8,225u8, - 216u8,33u8,114u8,15u8,75u8,161u8,156u8,62u8,27u8,21u8,10u8,81u8,28u8,224u8,24u8,133u8,144u8,231u8,40u8,169u8, - 64u8,88u8,162u8,75u8,101u8,219u8,156u8,25u8,20u8,90u8,155u8,174u8,97u8,216u8,115u8,75u8,223u8,196u8,181u8,53u8, - 106u8,218u8,197u8,152u8,129u8,142u8,14u8,84u8,171u8,118u8,9u8,58u8,157u8,45u8,198u8,196u8,186u8,248u8,121u8,141u8, - 29u8,30u8,219u8,211u8,94u8,132u8,15u8,79u8,58u8,90u8,208u8,44u8,131u8,254u8,211u8,165u8,169u8,115u8,64u8,160u8, - 186u8,9u8,123u8,184u8,148u8,156u8,31u8,143u8,48u8,235u8,249u8,47u8,102u8,91u8,216u8,223u8,45u8,15u8,34u8,246u8, - 237u8,222u8,125u8,6u8,242u8,97u8,149u8,251u8,251u8,234u8,201u8,246u8,229u8,218u8,129u8,255u8,170u8,221u8,122u8,60u8, - 105u8,30u8,39u8,63u8,161u8,152u8,155u8,103u8,86u8,3u8,0u8,0u8,0u8,0u8,16u8,97u8,112u8,116u8,111u8,115u8, - 95u8,103u8,111u8,118u8,101u8,114u8,110u8,97u8,110u8,99u8,101u8,165u8,51u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8, - 0u8,2u8,255u8,237u8,61u8,107u8,115u8,219u8,182u8,178u8,223u8,251u8,43u8,216u8,158u8,185u8,174u8,148u8,170u8,138u8, - 108u8,55u8,157u8,94u8,57u8,206u8,212u8,55u8,81u8,27u8,79u8,211u8,56u8,147u8,56u8,237u8,244u8,118u8,58u8,44u8, - 45u8,65u8,22u8,111u8,36u8,146u8,151u8,15u8,59u8,62u8,29u8,255u8,247u8,179u8,187u8,120u8,16u8,0u8,1u8,138u8, - 146u8,242u8,240u8,57u8,77u8,230u8,156u8,52u8,34u8,129u8,93u8,96u8,177u8,111u8,44u8,192u8,251u8,247u8,238u8,125u8, - 22u8,220u8,11u8,78u8,178u8,50u8,45u8,126u8,76u8,175u8,88u8,158u8,68u8,201u8,148u8,5u8,57u8,203u8,114u8,86u8, - 176u8,164u8,44u8,130u8,114u8,193u8,130u8,52u8,249u8,122u8,186u8,136u8,226u8,36u8,184u8,172u8,155u8,164u8,115u8,122u8, - 69u8,61u8,131u8,132u8,149u8,215u8,105u8,254u8,102u8,24u8,252u8,146u8,150u8,113u8,114u8,25u8,100u8,233u8,53u8,203u8, - 131u8,184u8,8u8,166u8,209u8,114u8,90u8,45u8,163u8,146u8,205u8,130u8,139u8,168u8,128u8,191u8,211u8,4u8,187u8,33u8, - 206u8,105u8,149u8,231u8,128u8,33u8,96u8,89u8,58u8,93u8,124u8,89u8,4u8,87u8,122u8,87u8,1u8,61u8,203u8,211u8, - 44u8,45u8,240u8,119u8,142u8,239u8,89u8,14u8,237u8,46u8,162u8,233u8,27u8,108u8,88u8,148u8,209u8,27u8,104u8,145u8, - 166u8,203u8,97u8,112u8,154u8,4u8,209u8,108u8,22u8,151u8,113u8,154u8,12u8,130u8,57u8,52u8,142u8,203u8,160u8,76u8, - 131u8,105u8,90u8,37u8,229u8,0u8,145u8,33u8,176u8,186u8,3u8,64u8,89u8,166u8,211u8,55u8,85u8,6u8,3u8,103u8, - 179u8,2u8,155u8,94u8,176u8,32u8,42u8,131u8,37u8,139u8,138u8,50u8,136u8,240u8,53u8,96u8,136u8,10u8,109u8,16u8, - 17u8,246u8,154u8,85u8,121u8,132u8,72u8,134u8,0u8,19u8,193u8,158u8,150u8,248u8,246u8,42u8,158u8,49u8,222u8,116u8, - 158u8,46u8,151u8,233u8,53u8,142u8,110u8,14u8,255u8,29u8,99u8,147u8,253u8,97u8,240u8,66u8,204u8,2u8,201u8,145u8, - 4u8,211u8,156u8,1u8,61u8,130u8,72u8,193u8,13u8,46u8,110u8,144u8,78u8,75u8,236u8,102u8,45u8,196u8,120u8,204u8, - 91u8,135u8,178u8,237u8,48u8,56u8,215u8,200u8,226u8,164u8,6u8,34u8,85u8,211u8,90u8,68u8,87u8,140u8,70u8,182u8, - 138u8,147u8,120u8,85u8,173u8,106u8,138u8,242u8,14u8,57u8,251u8,255u8,42u8,206u8,217u8,108u8,24u8,156u8,205u8,231u8, - 98u8,145u8,167u8,233u8,42u8,75u8,19u8,90u8,124u8,28u8,111u8,81u8,93u8,20u8,211u8,60u8,6u8,2u8,1u8,188u8, - 199u8,52u8,156u8,23u8,98u8,52u8,147u8,43u8,92u8,192u8,50u8,37u8,10u8,231u8,48u8,146u8,122u8,78u8,52u8,110u8, - 160u8,84u8,16u8,37u8,179u8,250u8,105u8,60u8,43u8,144u8,116u8,193u8,1u8,113u8,138u8,164u8,8u8,174u8,44u8,50u8, - 70u8,77u8,18u8,154u8,102u8,156u8,155u8,60u8,1u8,236u8,52u8,99u8,121u8,124u8,5u8,92u8,52u8,207u8,211u8,21u8, - 77u8,203u8,197u8,12u8,147u8,104u8,186u8,208u8,30u8,32u8,10u8,196u8,153u8,38u8,203u8,27u8,92u8,230u8,10u8,217u8, - 16u8,166u8,34u8,177u8,50u8,108u8,174u8,70u8,200u8,222u8,70u8,211u8,18u8,26u8,166u8,64u8,125u8,177u8,202u8,247u8, - 63u8,91u8,165u8,179u8,106u8,9u8,75u8,134u8,139u8,19u8,206u8,243u8,104u8,197u8,144u8,219u8,199u8,99u8,254u8,64u8, - 19u8,138u8,191u8,62u8,11u8,224u8,15u8,96u8,0u8,252u8,179u8,241u8,152u8,229u8,121u8,154u8,31u8,153u8,207u8,210u8, - 12u8,201u8,98u8,61u8,44u8,226u8,203u8,132u8,217u8,45u8,139u8,50u8,135u8,169u8,141u8,199u8,127u8,189u8,98u8,203u8, - 249u8,32u8,120u8,69u8,63u8,7u8,65u8,85u8,206u8,191u8,187u8,181u8,154u8,94u8,177u8,105u8,137u8,152u8,212u8,83u8, - 62u8,50u8,1u8,123u8,149u8,45u8,89u8,184u8,138u8,178u8,26u8,20u8,61u8,250u8,57u8,202u8,52u8,56u8,90u8,143u8, - 50u8,186u8,88u8,50u8,213u8,248u8,28u8,127u8,221u8,54u8,96u8,235u8,100u8,152u8,146u8,160u8,105u8,224u8,113u8,54u8, - 143u8,163u8,44u8,186u8,136u8,151u8,113u8,121u8,51u8,16u8,60u8,31u8,242u8,105u8,134u8,215u8,113u8,185u8,8u8,167u8, - 234u8,117u8,99u8,16u8,26u8,232u8,105u8,26u8,39u8,45u8,175u8,25u8,50u8,160u8,194u8,75u8,236u8,248u8,20u8,24u8, - 142u8,198u8,235u8,237u8,84u8,175u8,151u8,18u8,43u8,5u8,162u8,22u8,61u8,201u8,227u8,109u8,144u8,114u8,54u8,77u8, - 147u8,121u8,124u8,41u8,180u8,66u8,75u8,75u8,98u8,199u8,53u8,239u8,97u8,117u8,67u8,14u8,175u8,173u8,225u8,77u8, - 81u8,178u8,85u8,8u8,218u8,14u8,20u8,116u8,193u8,138u8,150u8,166u8,252u8,1u8,82u8,80u8,77u8,143u8,212u8,203u8, - 99u8,120u8,210u8,54u8,171u8,50u8,94u8,49u8,24u8,207u8,42u8,107u8,105u8,195u8,5u8,83u8,48u8,197u8,253u8,251u8, - 247u8,73u8,45u8,21u8,25u8,155u8,198u8,243u8,24u8,196u8,75u8,147u8,190u8,89u8,10u8,186u8,49u8,73u8,75u8,174u8, - 138u8,138u8,106u8,62u8,143u8,167u8,49u8,170u8,13u8,222u8,6u8,181u8,180u8,173u8,16u8,9u8,40u8,80u8,2u8,84u8, - 241u8,228u8,244u8,249u8,171u8,215u8,63u8,252u8,112u8,250u8,248u8,116u8,242u8,252u8,60u8,124u8,241u8,242u8,236u8,197u8, - 217u8,171u8,201u8,203u8,240u8,213u8,249u8,201u8,79u8,147u8,113u8,80u8,125u8,251u8,77u8,112u8,28u8,236u8,31u8,105u8, - 163u8,0u8,37u8,33u8,216u8,17u8,245u8,5u8,226u8,69u8,53u8,1u8,250u8,25u8,88u8,143u8,204u8,16u8,153u8,17u8, - 105u8,95u8,92u8,35u8,214u8,177u8,63u8,63u8,59u8,15u8,159u8,76u8,158u8,77u8,126u8,60u8,57u8,159u8,60u8,9u8, - 127u8,57u8,59u8,159u8,188u8,148u8,88u8,15u8,142u8,54u8,157u8,59u8,217u8,20u8,150u8,164u8,213u8,229u8,2u8,244u8, - 238u8,10u8,84u8,45u8,106u8,46u8,97u8,136u8,92u8,116u8,144u8,86u8,207u8,75u8,15u8,34u8,67u8,248u8,236u8,236u8, - 241u8,79u8,175u8,95u8,200u8,113u8,29u8,118u8,24u8,215u8,2u8,44u8,91u8,180u8,4u8,116u8,51u8,212u8,136u8,44u8, - 105u8,232u8,68u8,162u8,13u8,44u8,182u8,115u8,73u8,78u8,158u8,189u8,156u8,156u8,60u8,249u8,141u8,200u8,241u8,68u8, - 162u8,253u8,166u8,3u8,218u8,85u8,5u8,221u8,65u8,3u8,103u8,81u8,94u8,202u8,21u8,184u8,138u8,150u8,241u8,44u8, - 2u8,229u8,21u8,20u8,172u8,52u8,73u8,143u8,8u8,78u8,159u8,255u8,24u8,190u8,56u8,251u8,181u8,38u8,251u8,131u8, - 26u8,207u8,11u8,101u8,85u8,56u8,145u8,249u8,124u8,184u8,45u8,7u8,225u8,72u8,151u8,87u8,104u8,215u8,126u8,141u8, - 98u8,178u8,33u8,56u8,43u8,96u8,108u8,73u8,211u8,66u8,199u8,197u8,249u8,234u8,228u8,89u8,136u8,235u8,253u8,114u8, - 242u8,234u8,236u8,217u8,47u8,39u8,255u8,243u8,108u8,18u8,254u8,54u8,57u8,151u8,120u8,191u8,53u8,231u8,167u8,150u8, - 8u8,73u8,137u8,200u8,137u8,140u8,18u8,107u8,112u8,99u8,206u8,197u8,1u8,31u8,216u8,73u8,131u8,254u8,93u8,13u8, - 253u8,103u8,86u8,70u8,64u8,144u8,8u8,25u8,131u8,91u8,80u8,176u8,95u8,28u8,3u8,113u8,16u8,48u8,111u8,185u8, - 0u8,163u8,121u8,240u8,224u8,219u8,0u8,236u8,117u8,110u8,76u8,228u8,231u8,201u8,249u8,201u8,147u8,147u8,243u8,19u8, - 228u8,137u8,147u8,243u8,211u8,179u8,231u8,225u8,249u8,217u8,25u8,252u8,120u8,254u8,163u8,196u8,244u8,223u8,14u8,76u8, - 48u8,135u8,197u8,86u8,88u8,158u8,158u8,188u8,122u8,218u8,192u8,176u8,63u8,170u8,81u8,156u8,152u8,162u8,24u8,85u8, - 229u8,34u8,205u8,227u8,127u8,114u8,102u8,67u8,159u8,7u8,176u8,192u8,187u8,121u8,149u8,76u8,133u8,91u8,85u8,99u8, - 121u8,253u8,252u8,228u8,245u8,249u8,211u8,179u8,151u8,167u8,255u8,91u8,115u8,217u8,254u8,190u8,161u8,114u8,160u8,235u8, - 42u8,42u8,167u8,11u8,225u8,125u8,17u8,199u8,178u8,4u8,188u8,28u8,14u8,34u8,78u8,132u8,3u8,1u8,92u8,192u8, - 132u8,43u8,148u8,130u8,15u8,151u8,45u8,227u8,41u8,74u8,90u8,76u8,174u8,222u8,207u8,160u8,242u8,73u8,82u8,147u8, - 47u8,149u8,146u8,202u8,178u8,20u8,88u8,20u8,29u8,73u8,4u8,87u8,224u8,122u8,234u8,99u8,83u8,235u8,9u8,2u8, - 120u8,62u8,9u8,95u8,189u8,126u8,252u8,120u8,50u8,121u8,162u8,13u8,83u8,27u8,165u8,226u8,210u8,149u8,36u8,119u8, - 84u8,130u8,37u8,191u8,168u8,96u8,0u8,111u8,216u8,77u8,161u8,195u8,109u8,46u8,223u8,79u8,147u8,223u8,198u8,1u8, - 183u8,238u8,15u8,171u8,239u8,30u8,1u8,236u8,139u8,47u8,36u8,160u8,80u8,114u8,200u8,23u8,71u8,46u8,24u8,180u8, - 56u8,173u8,253u8,113u8,221u8,191u8,208u8,134u8,250u8,10u8,90u8,113u8,111u8,209u8,178u8,224u8,49u8,16u8,24u8,228u8, - 85u8,40u8,214u8,34u8,168u8,146u8,25u8,49u8,136u8,51u8,50u8,0u8,167u8,20u8,70u8,82u8,230u8,224u8,138u8,17u8, - 100u8,112u8,99u8,170u8,105u8,169u8,89u8,214u8,151u8,172u8,0u8,255u8,178u8,224u8,198u8,159u8,4u8,8u8,232u8,32u8, - 92u8,39u8,234u8,192u8,189u8,4u8,112u8,16u8,138u8,113u8,237u8,170u8,60u8,20u8,38u8,175u8,233u8,94u8,60u8,26u8, - 80u8,215u8,219u8,122u8,34u8,143u8,117u8,171u8,92u8,24u8,161u8,74u8,61u8,140u8,1u8,106u8,28u8,244u8,232u8,81u8, - 59u8,252u8,200u8,18u8,48u8,17u8,5u8,121u8,171u8,232u8,146u8,162u8,159u8,152u8,205u8,120u8,220u8,114u8,99u8,168u8, - 195u8,41u8,140u8,1u8,32u8,206u8,25u8,120u8,205u8,10u8,33u8,181u8,129u8,238u8,118u8,28u8,197u8,29u8,71u8,15u8, - 29u8,248u8,40u8,29u8,4u8,0u8,95u8,61u8,228u8,140u8,27u8,150u8,11u8,152u8,243u8,34u8,93u8,206u8,128u8,181u8, - 246u8,15u8,190u8,27u8,168u8,54u8,210u8,113u8,15u8,165u8,67u8,31u8,146u8,162u8,37u8,14u8,172u8,91u8,9u8,40u8, - 50u8,108u8,9u8,11u8,54u8,45u8,180u8,38u8,130u8,100u8,98u8,100u8,47u8,193u8,155u8,201u8,103u8,63u8,49u8,190u8, - 38u8,211u8,52u8,3u8,199u8,109u8,6u8,208u8,129u8,80u8,196u8,24u8,218u8,10u8,33u8,170u8,16u8,117u8,250u8,56u8, - 144u8,203u8,162u8,94u8,74u8,213u8,24u8,198u8,179u8,38u8,42u8,36u8,22u8,199u8,67u8,225u8,9u8,15u8,26u8,244u8, - 32u8,171u8,224u8,254u8,184u8,101u8,172u8,92u8,70u8,202u8,160u8,42u8,143u8,59u8,37u8,228u8,38u8,73u8,73u8,229u8, - 143u8,185u8,47u8,251u8,80u8,77u8,116u8,16u8,92u8,0u8,134u8,71u8,246u8,0u8,95u8,11u8,68u8,124u8,116u8,215u8, - 139u8,24u8,6u8,196u8,222u8,178u8,105u8,69u8,234u8,24u8,131u8,161u8,172u8,36u8,165u8,201u8,10u8,174u8,48u8,104u8, - 120u8,81u8,134u8,161u8,32u8,103u8,152u8,90u8,26u8,134u8,166u8,186u8,130u8,255u8,201u8,149u8,35u8,75u8,117u8,147u8, - 69u8,5u8,6u8,65u8,224u8,201u8,1u8,34u8,38u8,196u8,207u8,70u8,6u8,68u8,121u8,59u8,101u8,216u8,5u8,25u8, - 17u8,244u8,103u8,176u8,140u8,87u8,160u8,189u8,64u8,54u8,210u8,130u8,99u8,92u8,177u8,21u8,69u8,63u8,58u8,81u8, - 78u8,196u8,136u8,38u8,18u8,220u8,83u8,57u8,102u8,155u8,60u8,124u8,50u8,186u8,180u8,225u8,194u8,105u8,138u8,163u8, - 41u8,99u8,228u8,108u8,23u8,193u8,37u8,8u8,78u8,46u8,229u8,36u8,78u8,192u8,183u8,138u8,166u8,92u8,234u8,208u8, - 203u8,119u8,201u8,93u8,187u8,80u8,8u8,176u8,205u8,33u8,90u8,129u8,112u8,72u8,174u8,63u8,12u8,89u8,115u8,250u8, - 31u8,58u8,162u8,211u8,71u8,53u8,99u8,114u8,129u8,22u8,46u8,182u8,179u8,255u8,107u8,106u8,193u8,229u8,210u8,238u8, - 141u8,28u8,228u8,236u8,132u8,129u8,172u8,222u8,216u8,166u8,81u8,192u8,96u8,169u8,144u8,64u8,176u8,190u8,122u8,140u8, - 75u8,233u8,17u8,26u8,240u8,204u8,160u8,133u8,43u8,196u8,70u8,114u8,184u8,69u8,81u8,138u8,191u8,67u8,16u8,183u8, - 144u8,82u8,252u8,163u8,152u8,143u8,236u8,131u8,110u8,61u8,28u8,189u8,165u8,45u8,209u8,89u8,71u8,6u8,170u8,107u8, - 185u8,199u8,164u8,76u8,137u8,236u8,15u8,198u8,35u8,106u8,230u8,3u8,142u8,44u8,81u8,103u8,221u8,168u8,226u8,154u8, - 29u8,185u8,255u8,155u8,18u8,11u8,124u8,128u8,80u8,104u8,16u8,3u8,24u8,104u8,230u8,106u8,9u8,42u8,24u8,100u8, - 120u8,76u8,170u8,164u8,227u8,60u8,245u8,60u8,26u8,103u8,71u8,152u8,118u8,174u8,76u8,142u8,193u8,14u8,13u8,158u8, - 108u8,153u8,246u8,135u8,54u8,28u8,100u8,107u8,185u8,181u8,68u8,103u8,14u8,102u8,40u8,204u8,233u8,165u8,48u8,167u8, - 224u8,64u8,9u8,243u8,169u8,205u8,56u8,46u8,11u8,8u8,70u8,135u8,166u8,219u8,33u8,60u8,56u8,50u8,237u8,65u8, - 157u8,27u8,32u8,31u8,44u8,10u8,46u8,33u8,235u8,147u8,200u8,117u8,225u8,61u8,179u8,234u8,2u8,220u8,56u8,116u8, - 29u8,57u8,25u8,194u8,218u8,109u8,232u8,169u8,105u8,216u8,113u8,107u8,176u8,199u8,91u8,13u8,108u8,103u8,67u8,128u8, - 118u8,49u8,134u8,2u8,59u8,110u8,38u8,54u8,168u8,85u8,31u8,28u8,35u8,162u8,105u8,225u8,117u8,114u8,52u8,211u8, - 105u8,197u8,239u8,16u8,166u8,195u8,127u8,242u8,50u8,180u8,134u8,218u8,179u8,126u8,247u8,143u8,214u8,131u8,80u8,141u8, - 67u8,76u8,222u8,230u8,160u8,249u8,123u8,230u8,244u8,250u8,194u8,217u8,195u8,63u8,241u8,60u8,232u8,125u8,206u8,222u8, - 198u8,69u8,89u8,60u8,244u8,12u8,251u8,81u8,239u8,123u8,123u8,20u8,125u8,109u8,42u8,196u8,113u8,208u8,51u8,44u8, - 83u8,123u8,180u8,3u8,63u8,37u8,76u8,247u8,78u8,79u8,78u8,113u8,101u8,216u8,226u8,231u8,245u8,250u8,193u8,173u8, - 70u8,135u8,91u8,109u8,54u8,75u8,112u8,229u8,52u8,192u8,224u8,231u8,238u8,173u8,42u8,136u8,102u8,82u8,200u8,192u8, - 93u8,135u8,151u8,203u8,244u8,2u8,149u8,85u8,85u8,110u8,48u8,213u8,161u8,6u8,79u8,35u8,190u8,54u8,96u8,24u8, - 105u8,79u8,107u8,52u8,176u8,152u8,105u8,160u8,141u8,72u8,12u8,91u8,147u8,155u8,211u8,4u8,252u8,106u8,8u8,126u8, - 255u8,41u8,89u8,191u8,196u8,144u8,4u8,217u8,157u8,167u8,212u8,235u8,129u8,14u8,73u8,198u8,100u8,250u8,210u8,20u8, - 52u8,233u8,183u8,146u8,177u8,141u8,4u8,66u8,133u8,3u8,161u8,33u8,108u8,107u8,106u8,65u8,111u8,244u8,118u8,191u8, - 47u8,221u8,121u8,203u8,67u8,145u8,177u8,24u8,154u8,167u8,12u8,178u8,173u8,56u8,170u8,11u8,54u8,141u8,48u8,13u8, - 20u8,151u8,95u8,22u8,106u8,0u8,192u8,240u8,148u8,41u8,85u8,185u8,216u8,171u8,21u8,7u8,133u8,34u8,25u8,171u8, - 217u8,109u8,34u8,140u8,31u8,76u8,127u8,245u8,223u8,141u8,68u8,90u8,232u8,48u8,33u8,120u8,9u8,2u8,197u8,242u8, - 135u8,205u8,60u8,226u8,163u8,22u8,137u8,238u8,32u8,69u8,34u8,88u8,48u8,165u8,207u8,53u8,203u8,129u8,41u8,159u8, - 14u8,138u8,154u8,45u8,60u8,244u8,172u8,27u8,221u8,110u8,54u8,80u8,225u8,192u8,153u8,3u8,245u8,57u8,111u8,42u8, - 115u8,156u8,176u8,107u8,254u8,16u8,156u8,14u8,175u8,43u8,215u8,160u8,160u8,57u8,19u8,183u8,127u8,231u8,71u8,209u8, - 244u8,246u8,214u8,32u8,48u8,92u8,64u8,63u8,220u8,218u8,33u8,108u8,129u8,215u8,137u8,170u8,102u8,84u8,211u8,88u8, - 123u8,244u8,71u8,68u8,174u8,30u8,6u8,209u8,219u8,20u8,186u8,47u8,60u8,48u8,241u8,200u8,208u8,192u8,161u8,169u8, - 237u8,24u8,193u8,28u8,65u8,35u8,170u8,34u8,114u8,187u8,221u8,31u8,25u8,170u8,15u8,185u8,14u8,154u8,54u8,245u8, - 29u8,248u8,60u8,50u8,109u8,200u8,179u8,108u8,168u8,253u8,116u8,103u8,58u8,225u8,17u8,56u8,134u8,233u8,117u8,250u8, - 201u8,140u8,60u8,26u8,142u8,131u8,224u8,24u8,109u8,67u8,128u8,15u8,231u8,142u8,234u8,44u8,135u8,155u8,193u8,185u8, - 183u8,85u8,254u8,222u8,133u8,126u8,67u8,243u8,218u8,160u8,18u8,38u8,147u8,90u8,236u8,43u8,31u8,154u8,195u8,176u8, - 214u8,172u8,217u8,0u8,57u8,116u8,209u8,0u8,240u8,184u8,30u8,183u8,129u8,113u8,45u8,10u8,128u8,113u8,61u8,110u8, - 3u8,227u8,89u8,55u8,128u8,228u8,121u8,99u8,145u8,140u8,235u8,138u8,53u8,116u8,226u8,11u8,214u8,74u8,39u8,177u8, - 205u8,133u8,97u8,4u8,87u8,53u8,46u8,221u8,101u8,72u8,45u8,121u8,63u8,28u8,253u8,208u8,165u8,23u8,77u8,197u8, - 214u8,140u8,49u8,76u8,29u8,208u8,205u8,146u8,116u8,178u8,38u8,109u8,204u8,110u8,52u8,186u8,173u8,251u8,152u8,206u8, - 211u8,63u8,126u8,191u8,138u8,217u8,245u8,31u8,182u8,44u8,95u8,178u8,50u8,116u8,1u8,237u8,245u8,121u8,86u8,214u8, - 43u8,60u8,218u8,92u8,141u8,101u8,234u8,194u8,202u8,67u8,239u8,52u8,214u8,15u8,214u8,69u8,80u8,26u8,44u8,168u8, - 142u8,247u8,52u8,90u8,23u8,202u8,142u8,163u8,245u8,44u8,236u8,251u8,165u8,174u8,7u8,105u8,35u8,4u8,149u8,27u8, - 103u8,5u8,204u8,108u8,201u8,190u8,6u8,125u8,151u8,213u8,150u8,65u8,229u8,161u8,100u8,173u8,192u8,159u8,117u8,172u8, - 255u8,103u8,237u8,252u8,126u8,15u8,230u8,37u8,90u8,89u8,217u8,15u8,200u8,85u8,202u8,250u8,8u8,153u8,190u8,67u8, - 72u8,244u8,74u8,100u8,149u8,201u8,26u8,233u8,201u8,58u8,216u8,108u8,144u8,97u8,190u8,158u8,234u8,145u8,123u8,67u8, - 3u8,133u8,145u8,44u8,28u8,79u8,247u8,65u8,206u8,76u8,230u8,21u8,105u8,184u8,180u8,171u8,129u8,99u8,149u8,91u8, - 51u8,104u8,9u8,139u8,106u8,138u8,201u8,231u8,121u8,181u8,132u8,126u8,124u8,156u8,50u8,87u8,32u8,22u8,11u8,36u8, - 55u8,191u8,161u8,37u8,179u8,28u8,174u8,158u8,35u8,95u8,212u8,12u8,133u8,219u8,82u8,32u8,93u8,178u8,66u8,141u8, - 141u8,137u8,53u8,205u8,220u8,144u8,182u8,181u8,115u8,182u8,143u8,121u8,117u8,208u8,147u8,147u8,29u8,104u8,115u8,27u8, - 88u8,83u8,25u8,52u8,135u8,61u8,48u8,135u8,56u8,8u8,230u8,144u8,151u8,102u8,205u8,16u8,206u8,201u8,119u8,16u8, - 114u8,173u8,170u8,101u8,25u8,127u8,226u8,194u8,196u8,181u8,36u8,255u8,70u8,140u8,72u8,89u8,146u8,34u8,164u8,213u8, - 12u8,113u8,53u8,235u8,114u8,17u8,45u8,217u8,183u8,45u8,187u8,162u8,127u8,160u8,212u8,154u8,152u8,37u8,120u8,10u8, - 156u8,22u8,148u8,91u8,192u8,39u8,97u8,58u8,87u8,92u8,172u8,57u8,4u8,220u8,131u8,251u8,188u8,199u8,221u8,200u8, - 49u8,42u8,232u8,25u8,91u8,178u8,75u8,204u8,30u8,82u8,166u8,50u8,239u8,213u8,68u8,236u8,7u8,199u8,199u8,13u8, - 84u8,32u8,6u8,88u8,156u8,52u8,30u8,199u8,9u8,109u8,193u8,135u8,81u8,126u8,89u8,173u8,96u8,140u8,61u8,87u8, - 209u8,67u8,95u8,119u8,6u8,141u8,45u8,112u8,42u8,66u8,227u8,94u8,145u8,179u8,150u8,142u8,51u8,167u8,216u8,241u8, - 184u8,72u8,97u8,123u8,45u8,90u8,213u8,89u8,135u8,206u8,190u8,229u8,102u8,126u8,37u8,101u8,131u8,104u8,254u8,208u8, - 149u8,194u8,140u8,99u8,221u8,63u8,160u8,114u8,50u8,157u8,64u8,77u8,194u8,26u8,174u8,136u8,9u8,234u8,209u8,113u8, - 119u8,79u8,209u8,244u8,123u8,188u8,20u8,111u8,41u8,114u8,233u8,27u8,110u8,208u8,70u8,107u8,128u8,149u8,37u8,64u8, - 244u8,42u8,235u8,82u8,217u8,40u8,43u8,237u8,88u8,30u8,167u8,51u8,115u8,113u8,68u8,161u8,102u8,72u8,229u8,19u8, - 199u8,129u8,42u8,15u8,130u8,168u8,19u8,214u8,167u8,192u8,26u8,168u8,25u8,184u8,89u8,71u8,14u8,214u8,198u8,80u8, - 255u8,109u8,22u8,115u8,231u8,8u8,250u8,26u8,160u8,190u8,234u8,232u8,255u8,119u8,89u8,29u8,46u8,3u8,188u8,150u8, - 134u8,187u8,125u8,58u8,255u8,63u8,58u8,118u8,141u8,103u8,155u8,197u8,209u8,43u8,110u8,252u8,75u8,35u8,13u8,3u8, - 112u8,187u8,168u8,111u8,209u8,180u8,176u8,212u8,58u8,67u8,55u8,189u8,84u8,153u8,192u8,113u8,67u8,125u8,202u8,87u8, - 189u8,117u8,22u8,203u8,26u8,206u8,175u8,44u8,184u8,142u8,168u8,80u8,51u8,136u8,176u8,62u8,21u8,182u8,88u8,115u8, - 80u8,222u8,154u8,205u8,0u8,43u8,82u8,111u8,192u8,66u8,102u8,120u8,197u8,11u8,1u8,64u8,227u8,63u8,24u8,253u8, - 151u8,180u8,49u8,101u8,90u8,194u8,240u8,177u8,56u8,2u8,235u8,35u8,249u8,51u8,81u8,248u8,27u8,96u8,149u8,89u8, - 161u8,227u8,196u8,77u8,10u8,84u8,68u8,210u8,102u8,201u8,2u8,11u8,226u8,82u8,216u8,39u8,76u8,169u8,174u8,20u8, - 164u8,6u8,163u8,141u8,56u8,153u8,47u8,105u8,30u8,247u8,103u8,76u8,252u8,43u8,232u8,229u8,236u8,58u8,194u8,180u8, - 7u8,110u8,140u8,196u8,69u8,81u8,1u8,43u8,67u8,236u8,2u8,150u8,133u8,10u8,134u8,137u8,184u8,151u8,128u8,100u8, - 206u8,152u8,129u8,24u8,155u8,95u8,84u8,57u8,64u8,141u8,230u8,88u8,233u8,197u8,251u8,192u8,86u8,110u8,82u8,240u8, - 157u8,201u8,62u8,236u8,250u8,86u8,38u8,78u8,129u8,18u8,134u8,73u8,141u8,171u8,100u8,25u8,191u8,97u8,203u8,27u8, - 85u8,64u8,27u8,129u8,41u8,252u8,63u8,172u8,42u8,94u8,101u8,104u8,35u8,177u8,148u8,8u8,105u8,161u8,163u8,21u8, - 100u8,17u8,105u8,89u8,74u8,135u8,250u8,37u8,138u8,122u8,43u8,79u8,60u8,125u8,195u8,128u8,209u8,121u8,255u8,227u8, - 128u8,215u8,235u8,241u8,159u8,15u8,85u8,189u8,222u8,35u8,91u8,190u8,104u8,9u8,195u8,122u8,9u8,73u8,235u8,27u8, - 193u8,46u8,47u8,52u8,69u8,33u8,77u8,32u8,85u8,3u8,65u8,133u8,1u8,3u8,211u8,255u8,178u8,5u8,24u8,185u8, - 34u8,93u8,177u8,222u8,158u8,119u8,92u8,141u8,188u8,127u8,61u8,11u8,53u8,240u8,123u8,18u8,28u8,87u8,220u8,109u8, - 208u8,142u8,12u8,88u8,64u8,61u8,228u8,178u8,175u8,130u8,125u8,226u8,208u8,171u8,52u8,158u8,5u8,57u8,216u8,136u8, - 25u8,146u8,143u8,196u8,178u8,24u8,154u8,162u8,218u8,121u8,238u8,52u8,45u8,99u8,156u8,247u8,131u8,3u8,196u8,212u8, - 178u8,141u8,160u8,237u8,29u8,170u8,204u8,67u8,163u8,14u8,219u8,112u8,101u8,116u8,119u8,38u8,108u8,56u8,44u8,248u8, - 199u8,182u8,85u8,230u8,91u8,103u8,65u8,170u8,237u8,193u8,91u8,185u8,72u8,203u8,137u8,245u8,193u8,107u8,201u8,135u8, - 12u8,28u8,227u8,111u8,211u8,143u8,237u8,68u8,247u8,64u8,147u8,106u8,201u8,124u8,237u8,246u8,170u8,220u8,234u8,244u8, - 253u8,230u8,81u8,156u8,105u8,102u8,111u8,38u8,197u8,157u8,198u8,54u8,231u8,230u8,218u8,190u8,111u8,102u8,83u8,52u8, - 38u8,27u8,120u8,94u8,162u8,107u8,220u8,206u8,85u8,166u8,175u8,220u8,124u8,215u8,198u8,35u8,29u8,86u8,201u8,155u8, - 129u8,193u8,176u8,225u8,23u8,177u8,71u8,111u8,6u8,56u8,127u8,106u8,243u8,250u8,147u8,219u8,63u8,189u8,130u8,159u8, - 182u8,138u8,154u8,129u8,79u8,35u8,110u8,64u8,214u8,234u8,217u8,91u8,246u8,155u8,133u8,9u8,254u8,10u8,0u8,207u8, - 166u8,189u8,230u8,199u8,123u8,18u8,227u8,77u8,119u8,222u8,159u8,161u8,71u8,174u8,165u8,113u8,183u8,187u8,246u8,212u8, - 100u8,39u8,191u8,222u8,64u8,178u8,155u8,83u8,63u8,73u8,138u8,74u8,20u8,32u8,241u8,34u8,105u8,105u8,179u8,103u8, - 41u8,44u8,16u8,127u8,88u8,199u8,177u8,84u8,15u8,167u8,29u8,187u8,176u8,167u8,142u8,186u8,38u8,23u8,116u8,113u8, - 9u8,174u8,65u8,185u8,181u8,222u8,60u8,135u8,20u8,98u8,73u8,208u8,177u8,86u8,168u8,246u8,215u8,103u8,93u8,100u8, - 193u8,41u8,108u8,183u8,107u8,220u8,202u8,207u8,197u8,206u8,10u8,214u8,50u8,70u8,224u8,220u8,244u8,246u8,204u8,73u8, - 13u8,105u8,3u8,102u8,160u8,141u8,172u8,223u8,209u8,161u8,52u8,234u8,167u8,251u8,218u8,68u8,5u8,70u8,220u8,86u8, - 38u8,173u8,179u8,14u8,225u8,0u8,220u8,154u8,138u8,217u8,202u8,82u8,15u8,114u8,58u8,199u8,61u8,88u8,134u8,177u8, - 72u8,243u8,242u8,235u8,105u8,156u8,79u8,43u8,44u8,45u8,155u8,107u8,140u8,192u8,139u8,156u8,13u8,89u8,30u8,54u8, - 168u8,103u8,224u8,125u8,20u8,140u8,90u8,153u8,209u8,168u8,237u8,118u8,69u8,151u8,242u8,180u8,215u8,142u8,97u8,77u8, - 109u8,205u8,134u8,29u8,194u8,19u8,105u8,236u8,145u8,102u8,142u8,22u8,20u8,88u8,56u8,183u8,118u8,27u8,246u8,93u8, - 231u8,185u8,254u8,191u8,89u8,0u8,35u8,169u8,128u8,75u8,224u8,156u8,173u8,105u8,31u8,219u8,188u8,23u8,182u8,202u8, - 202u8,27u8,175u8,15u8,211u8,238u8,21u8,121u8,77u8,164u8,206u8,104u8,230u8,27u8,77u8,181u8,127u8,12u8,103u8,66u8, - 219u8,248u8,245u8,186u8,16u8,218u8,30u8,178u8,57u8,246u8,186u8,172u8,109u8,67u8,111u8,65u8,152u8,71u8,211u8,16u8, - 108u8,228u8,39u8,104u8,229u8,109u8,126u8,218u8,122u8,233u8,219u8,116u8,21u8,220u8,146u8,198u8,11u8,92u8,60u8,66u8, - 70u8,47u8,119u8,18u8,45u8,12u8,104u8,108u8,92u8,199u8,222u8,74u8,125u8,59u8,158u8,1u8,186u8,133u8,178u8,128u8, - 55u8,228u8,57u8,80u8,114u8,154u8,122u8,110u8,100u8,183u8,186u8,59u8,212u8,112u8,95u8,124u8,192u8,196u8,191u8,123u8, - 182u8,111u8,178u8,222u8,245u8,208u8,70u8,219u8,105u8,164u8,182u8,183u8,118u8,50u8,155u8,57u8,11u8,139u8,85u8,14u8, - 57u8,210u8,242u8,186u8,250u8,246u8,188u8,114u8,239u8,176u8,28u8,154u8,42u8,137u8,68u8,145u8,243u8,50u8,46u8,202u8, - 102u8,109u8,51u8,234u8,105u8,163u8,178u8,153u8,142u8,158u8,242u8,210u8,100u8,61u8,6u8,215u8,139u8,152u8,169u8,194u8, - 79u8,130u8,117u8,162u8,174u8,251u8,33u8,192u8,168u8,84u8,104u8,49u8,214u8,47u8,211u8,52u8,88u8,130u8,178u8,99u8, - 65u8,143u8,13u8,47u8,135u8,162u8,186u8,24u8,204u8,195u8,101u8,30u8,205u8,88u8,209u8,111u8,108u8,250u8,119u8,33u8, - 224u8,230u8,203u8,130u8,172u8,174u8,160u8,138u8,218u8,112u8,151u8,138u8,241u8,64u8,114u8,106u8,26u8,143u8,115u8,86u8, - 159u8,177u8,229u8,25u8,121u8,117u8,214u8,233u8,163u8,200u8,157u8,52u8,105u8,221u8,101u8,175u8,197u8,49u8,104u8,57u8, - 136u8,213u8,111u8,232u8,113u8,115u8,143u8,196u8,156u8,160u8,249u8,114u8,171u8,9u8,234u8,212u8,63u8,157u8,243u8,234u8, - 18u8,60u8,33u8,226u8,218u8,222u8,25u8,88u8,27u8,45u8,51u8,240u8,147u8,151u8,75u8,117u8,198u8,142u8,202u8,44u8, - 121u8,137u8,10u8,243u8,242u8,210u8,42u8,202u8,134u8,118u8,214u8,14u8,129u8,240u8,237u8,123u8,77u8,116u8,105u8,186u8, - 0u8,204u8,7u8,136u8,187u8,73u8,60u8,59u8,247u8,214u8,38u8,197u8,208u8,80u8,152u8,70u8,81u8,143u8,112u8,116u8, - 209u8,179u8,236u8,237u8,89u8,188u8,60u8,92u8,136u8,16u8,104u8,79u8,39u8,146u8,43u8,43u8,36u8,83u8,188u8,141u8, - 229u8,209u8,113u8,9u8,185u8,0u8,129u8,224u8,158u8,110u8,39u8,108u8,102u8,214u8,232u8,158u8,23u8,145u8,249u8,64u8, - 83u8,218u8,1u8,131u8,109u8,60u8,59u8,102u8,176u8,234u8,57u8,91u8,135u8,163u8,219u8,97u8,11u8,141u8,207u8,56u8, - 240u8,3u8,47u8,36u8,160u8,166u8,146u8,117u8,237u8,85u8,139u8,92u8,233u8,53u8,90u8,89u8,216u8,116u8,140u8,151u8, - 210u8,13u8,207u8,172u8,163u8,142u8,26u8,156u8,30u8,254u8,22u8,167u8,74u8,201u8,136u8,211u8,14u8,36u8,230u8,111u8, - 147u8,84u8,141u8,128u8,82u8,185u8,55u8,46u8,109u8,40u8,148u8,71u8,67u8,249u8,13u8,124u8,149u8,209u8,253u8,177u8, - 172u8,210u8,222u8,36u8,94u8,246u8,149u8,67u8,215u8,133u8,147u8,52u8,140u8,157u8,52u8,81u8,206u8,168u8,212u8,205u8, - 88u8,60u8,143u8,253u8,70u8,21u8,193u8,103u8,97u8,87u8,72u8,119u8,90u8,59u8,135u8,6u8,216u8,98u8,233u8,124u8, - 107u8,225u8,202u8,141u8,117u8,95u8,159u8,129u8,75u8,234u8,245u8,205u8,77u8,199u8,2u8,122u8,214u8,169u8,75u8,137u8, - 160u8,181u8,128u8,122u8,130u8,116u8,243u8,197u8,116u8,142u8,221u8,140u8,82u8,79u8,57u8,85u8,229u8,229u8,33u8,180u8, - 6u8,98u8,63u8,124u8,137u8,161u8,32u8,223u8,131u8,23u8,234u8,218u8,165u8,169u8,117u8,96u8,215u8,66u8,197u8,114u8, - 206u8,177u8,60u8,36u8,210u8,38u8,170u8,184u8,185u8,147u8,214u8,70u8,149u8,202u8,9u8,61u8,30u8,47u8,89u8,114u8, - 89u8,46u8,122u8,123u8,174u8,25u8,161u8,117u8,28u8,217u8,202u8,115u8,3u8,246u8,117u8,106u8,50u8,63u8,113u8,228u8, - 41u8,248u8,38u8,129u8,154u8,84u8,169u8,41u8,147u8,179u8,108u8,25u8,77u8,153u8,1u8,209u8,162u8,142u8,74u8,6u8, - 225u8,36u8,173u8,151u8,54u8,196u8,77u8,172u8,223u8,102u8,126u8,174u8,149u8,205u8,217u8,68u8,182u8,105u8,217u8,35u8, - 237u8,236u8,158u8,145u8,58u8,112u8,248u8,202u8,14u8,153u8,109u8,95u8,181u8,45u8,28u8,124u8,87u8,142u8,64u8,138u8, - 25u8,100u8,206u8,165u8,187u8,183u8,133u8,174u8,236u8,152u8,48u8,240u8,30u8,87u8,239u8,251u8,195u8,188u8,166u8,239u8, - 235u8,57u8,128u8,209u8,221u8,1u8,22u8,102u8,247u8,168u8,155u8,199u8,98u8,13u8,96u8,141u8,167u8,162u8,131u8,225u8, - 75u8,184u8,6u8,192u8,145u8,205u8,106u8,26u8,31u8,253u8,144u8,230u8,116u8,103u8,146u8,172u8,101u8,102u8,96u8,12u8, - 82u8,189u8,118u8,153u8,139u8,31u8,131u8,116u8,56u8,133u8,90u8,117u8,100u8,3u8,193u8,12u8,56u8,137u8,116u8,17u8, - 142u8,58u8,73u8,45u8,14u8,132u8,57u8,56u8,77u8,193u8,239u8,249u8,202u8,146u8,251u8,239u8,246u8,164u8,145u8,117u8, - 191u8,137u8,126u8,225u8,9u8,235u8,53u8,247u8,4u8,206u8,176u8,148u8,72u8,204u8,25u8,197u8,157u8,21u8,37u8,236u8, - 15u8,107u8,231u8,90u8,167u8,232u8,150u8,32u8,255u8,86u8,64u8,176u8,250u8,162u8,14u8,126u8,10u8,137u8,182u8,11u8, - 212u8,97u8,95u8,136u8,222u8,18u8,60u8,50u8,199u8,211u8,137u8,224u8,210u8,194u8,22u8,86u8,41u8,142u8,198u8,208u8, - 102u8,243u8,208u8,85u8,165u8,40u8,4u8,94u8,96u8,14u8,177u8,182u8,169u8,150u8,35u8,196u8,30u8,42u8,236u8,245u8, - 134u8,194u8,6u8,158u8,206u8,14u8,231u8,187u8,12u8,236u8,61u8,115u8,44u8,166u8,137u8,123u8,236u8,166u8,18u8,21u8, - 106u8,33u8,125u8,136u8,18u8,218u8,17u8,57u8,65u8,234u8,226u8,190u8,152u8,118u8,209u8,204u8,205u8,234u8,87u8,193u8, - 0u8,8u8,172u8,61u8,45u8,181u8,27u8,120u8,236u8,225u8,168u8,8u8,173u8,74u8,240u8,202u8,6u8,80u8,10u8,116u8, - 97u8,194u8,172u8,103u8,92u8,202u8,208u8,223u8,210u8,161u8,42u8,97u8,119u8,222u8,216u8,33u8,167u8,37u8,142u8,236u8, - 51u8,223u8,100u8,92u8,0u8,68u8,6u8,6u8,21u8,25u8,192u8,145u8,26u8,40u8,234u8,3u8,71u8,141u8,52u8,55u8, - 194u8,113u8,45u8,41u8,86u8,171u8,90u8,97u8,59u8,214u8,68u8,132u8,234u8,34u8,18u8,200u8,190u8,2u8,109u8,192u8, - 97u8,190u8,196u8,168u8,217u8,188u8,152u8,135u8,7u8,151u8,254u8,14u8,189u8,61u8,71u8,251u8,94u8,223u8,74u8,83u8, - 249u8,251u8,187u8,2u8,170u8,30u8,166u8,62u8,174u8,216u8,32u8,8u8,65u8,153u8,51u8,218u8,21u8,15u8,229u8,35u8, - 249u8,59u8,78u8,248u8,147u8,190u8,24u8,177u8,72u8,39u8,243u8,18u8,93u8,157u8,18u8,205u8,173u8,247u8,95u8,89u8, - 125u8,73u8,155u8,99u8,85u8,10u8,81u8,246u8,145u8,128u8,126u8,146u8,88u8,56u8,10u8,117u8,175u8,130u8,184u8,117u8, - 11u8,79u8,104u8,198u8,246u8,165u8,46u8,194u8,251u8,136u8,197u8,173u8,111u8,38u8,106u8,1u8,205u8,184u8,2u8,102u8, - 96u8,37u8,243u8,111u8,100u8,5u8,134u8,184u8,39u8,167u8,87u8,48u8,44u8,145u8,44u8,177u8,60u8,99u8,186u8,96u8, - 180u8,21u8,16u8,55u8,42u8,13u8,181u8,173u8,199u8,62u8,197u8,230u8,113u8,110u8,35u8,231u8,220u8,198u8,253u8,245u8, - 162u8,68u8,47u8,80u8,92u8,85u8,146u8,240u8,246u8,214u8,206u8,37u8,140u8,206u8,207u8,128u8,74u8,220u8,248u8,132u8, - 190u8,178u8,214u8,73u8,123u8,32u8,73u8,184u8,38u8,40u8,173u8,151u8,80u8,69u8,185u8,88u8,244u8,226u8,231u8,240u8, - 126u8,91u8,244u8,73u8,2u8,39u8,15u8,7u8,210u8,84u8,86u8,196u8,164u8,1u8,103u8,58u8,202u8,24u8,140u8,222u8, - 238u8,235u8,103u8,108u8,92u8,55u8,254u8,41u8,226u8,138u8,91u8,48u8,76u8,225u8,115u8,74u8,255u8,110u8,186u8,212u8, - 170u8,12u8,204u8,121u8,179u8,88u8,180u8,243u8,87u8,8u8,174u8,59u8,217u8,121u8,228u8,57u8,54u8,234u8,76u8,82u8, - 244u8,246u8,188u8,67u8,24u8,26u8,135u8,63u8,247u8,26u8,103u8,109u8,149u8,225u8,105u8,187u8,253u8,76u8,59u8,65u8, - 106u8,232u8,74u8,87u8,253u8,172u8,191u8,0u8,204u8,168u8,94u8,245u8,23u8,182u8,246u8,215u8,156u8,211u8,119u8,184u8, - 161u8,242u8,22u8,58u8,25u8,207u8,224u8,245u8,115u8,77u8,252u8,224u8,96u8,61u8,60u8,198u8,219u8,130u8,90u8,146u8, - 123u8,254u8,203u8,137u8,250u8,206u8,45u8,240u8,54u8,188u8,20u8,70u8,109u8,130u8,211u8,184u8,170u8,168u8,145u8,75u8, - 212u8,138u8,238u8,28u8,231u8,220u8,28u8,132u8,210u8,75u8,169u8,156u8,41u8,36u8,85u8,82u8,65u8,247u8,245u8,245u8, - 156u8,55u8,251u8,244u8,29u8,181u8,231u8,219u8,194u8,149u8,183u8,253u8,244u8,155u8,165u8,128u8,18u8,158u8,124u8,110u8, - 158u8,248u8,64u8,167u8,129u8,28u8,165u8,63u8,186u8,213u8,112u8,131u8,250u8,32u8,255u8,170u8,87u8,87u8,169u8,72u8, - 63u8,170u8,206u8,91u8,140u8,47u8,104u8,219u8,114u8,251u8,202u8,232u8,102u8,114u8,87u8,132u8,213u8,180u8,163u8,136u8, - 75u8,160u8,175u8,128u8,124u8,153u8,85u8,176u8,201u8,130u8,5u8,246u8,156u8,86u8,118u8,161u8,255u8,190u8,125u8,254u8, - 189u8,30u8,112u8,127u8,205u8,129u8,85u8,187u8,220u8,139u8,182u8,245u8,212u8,241u8,130u8,230u8,70u8,89u8,179u8,212u8, - 67u8,181u8,238u8,111u8,94u8,168u8,115u8,241u8,197u8,23u8,29u8,31u8,98u8,65u8,128u8,249u8,116u8,93u8,218u8,192u8, - 123u8,82u8,228u8,174u8,78u8,211u8,27u8,138u8,173u8,231u8,229u8,70u8,146u8,202u8,98u8,230u8,78u8,233u8,53u8,141u8, - 203u8,121u8,249u8,80u8,48u8,143u8,147u8,24u8,24u8,79u8,75u8,219u8,169u8,153u8,242u8,38u8,239u8,54u8,103u8,218u8, - 206u8,185u8,155u8,75u8,207u8,182u8,18u8,36u8,199u8,210u8,50u8,253u8,190u8,99u8,51u8,187u8,99u8,150u8,179u8,121u8, - 155u8,130u8,99u8,18u8,125u8,115u8,23u8,218u8,197u8,223u8,59u8,161u8,180u8,242u8,118u8,38u8,182u8,118u8,185u8,114u8, - 36u8,214u8,109u8,248u8,30u8,167u8,109u8,61u8,31u8,211u8,123u8,225u8,11u8,210u8,5u8,76u8,241u8,180u8,229u8,200u8, - 178u8,93u8,55u8,215u8,80u8,220u8,234u8,13u8,236u8,16u8,132u8,162u8,156u8,192u8,126u8,149u8,164u8,190u8,55u8,13u8, - 121u8,168u8,47u8,92u8,42u8,152u8,28u8,158u8,204u8,90u8,133u8,242u8,122u8,137u8,237u8,75u8,239u8,124u8,22u8,100u8, - 224u8,79u8,99u8,251u8,170u8,244u8,32u8,222u8,168u8,50u8,65u8,72u8,220u8,242u8,178u8,82u8,103u8,181u8,230u8,11u8, - 246u8,20u8,105u8,224u8,223u8,146u8,22u8,235u8,119u8,36u8,223u8,181u8,209u8,90u8,107u8,140u8,117u8,245u8,100u8,213u8, - 244u8,64u8,112u8,170u8,77u8,194u8,165u8,186u8,213u8,107u8,112u8,34u8,70u8,170u8,184u8,204u8,4u8,33u8,231u8,238u8, - 134u8,160u8,40u8,67u8,0u8,228u8,177u8,57u8,61u8,185u8,113u8,134u8,145u8,132u8,86u8,116u8,69u8,39u8,65u8,22u8, - 20u8,121u8,128u8,227u8,55u8,179u8,54u8,82u8,121u8,193u8,9u8,165u8,210u8,208u8,237u8,158u8,225u8,165u8,123u8,82u8, - 178u8,128u8,37u8,175u8,69u8,144u8,8u8,249u8,18u8,44u8,5u8,160u8,93u8,47u8,152u8,132u8,118u8,193u8,167u8,192u8, - 74u8,251u8,95u8,73u8,90u8,71u8,107u8,218u8,33u8,22u8,121u8,9u8,0u8,79u8,81u8,226u8,139u8,154u8,172u8,251u8, - 35u8,248u8,67u8,127u8,141u8,70u8,190u8,3u8,46u8,91u8,238u8,175u8,187u8,200u8,215u8,184u8,46u8,34u8,24u8,237u8, - 184u8,211u8,110u8,182u8,181u8,214u8,2u8,171u8,66u8,84u8,230u8,187u8,145u8,228u8,110u8,77u8,194u8,219u8,212u8,88u8, - 83u8,250u8,176u8,75u8,214u8,87u8,78u8,251u8,158u8,43u8,54u8,107u8,38u8,109u8,71u8,180u8,187u8,98u8,75u8,209u8, - 200u8,154u8,186u8,220u8,214u8,211u8,185u8,205u8,172u8,159u8,144u8,105u8,183u8,227u8,22u8,175u8,1u8,120u8,188u8,153u8, - 112u8,111u8,211u8,122u8,13u8,161u8,82u8,209u8,142u8,139u8,27u8,248u8,8u8,104u8,62u8,77u8,52u8,251u8,14u8,40u8, - 219u8,239u8,18u8,140u8,128u8,215u8,14u8,62u8,252u8,154u8,126u8,222u8,185u8,0u8,129u8,86u8,118u8,16u8,28u8,246u8, - 29u8,46u8,159u8,157u8,193u8,14u8,92u8,244u8,82u8,23u8,223u8,195u8,75u8,72u8,118u8,28u8,28u8,14u8,106u8,115u8, - 199u8,159u8,29u8,28u8,130u8,219u8,39u8,53u8,24u8,62u8,58u8,252u8,230u8,65u8,127u8,189u8,5u8,254u8,56u8,150u8, - 247u8,163u8,217u8,76u8,151u8,239u8,225u8,37u8,182u8,70u8,227u8,154u8,180u8,194u8,48u8,56u8,143u8,85u8,223u8,133u8, - 5u8,213u8,220u8,196u8,79u8,107u8,187u8,241u8,218u8,82u8,4u8,170u8,171u8,185u8,15u8,188u8,178u8,255u8,248u8,29u8, - 188u8,11u8,112u8,173u8,64u8,115u8,96u8,25u8,6u8,237u8,150u8,129u8,222u8,194u8,13u8,152u8,25u8,59u8,30u8,189u8, - 125u8,0u8,166u8,28u8,178u8,39u8,234u8,118u8,237u8,99u8,223u8,229u8,246u8,155u8,113u8,74u8,200u8,239u8,207u8,14u8, - 81u8,251u8,243u8,210u8,30u8,254u8,88u8,134u8,1u8,159u8,24u8,105u8,59u8,70u8,186u8,171u8,58u8,66u8,95u8,99u8, - 88u8,250u8,176u8,205u8,234u8,127u8,90u8,252u8,109u8,45u8,132u8,79u8,141u8,172u8,13u8,142u8,113u8,73u8,92u8,165u8, - 25u8,97u8,60u8,15u8,229u8,165u8,21u8,161u8,188u8,194u8,48u8,188u8,138u8,163u8,59u8,30u8,78u8,255u8,39u8,132u8, - 200u8,155u8,6u8,175u8,119u8,46u8,118u8,93u8,31u8,47u8,237u8,22u8,86u8,182u8,70u8,91u8,221u8,163u8,151u8,247u8, - 145u8,31u8,196u8,94u8,142u8,74u8,182u8,119u8,156u8,90u8,52u8,186u8,237u8,92u8,88u8,56u8,90u8,87u8,78u8,248u8, - 238u8,66u8,167u8,145u8,5u8,117u8,167u8,66u8,64u8,158u8,61u8,116u8,148u8,149u8,217u8,88u8,188u8,249u8,206u8,22u8, - 70u8,50u8,1u8,172u8,217u8,27u8,216u8,174u8,58u8,119u8,244u8,65u8,72u8,188u8,150u8,66u8,183u8,119u8,63u8,154u8, - 221u8,191u8,75u8,142u8,205u8,182u8,38u8,243u8,239u8,232u8,221u8,236u8,236u8,94u8,108u8,229u8,15u8,253u8,39u8,112u8, - 203u8,223u8,60u8,184u8,254u8,224u8,140u8,243u8,174u8,3u8,113u8,126u8,204u8,116u8,95u8,227u8,26u8,254u8,228u8,96u8, - 131u8,40u8,28u8,154u8,2u8,52u8,240u8,125u8,190u8,217u8,57u8,14u8,23u8,81u8,55u8,191u8,161u8,32u8,52u8,239u8, - 141u8,216u8,129u8,171u8,196u8,44u8,125u8,47u8,14u8,238u8,8u8,75u8,109u8,224u8,156u8,139u8,25u8,169u8,127u8,29u8, - 180u8,120u8,230u8,214u8,233u8,101u8,247u8,254u8,255u8,6u8,123u8,255u8,141u8,45u8,252u8,246u8,7u8,150u8,179u8,251u8, - 68u8,221u8,62u8,65u8,31u8,250u8,228u8,59u8,66u8,229u8,34u8,199u8,221u8,159u8,132u8,87u8,254u8,12u8,45u8,71u8, - 95u8,77u8,214u8,53u8,72u8,241u8,178u8,37u8,78u8,216u8,170u8,251u8,223u8,67u8,186u8,120u8,245u8,218u8,44u8,166u8, - 239u8,95u8,37u8,101u8,104u8,156u8,58u8,103u8,197u8,39u8,225u8,251u8,251u8,9u8,31u8,21u8,148u8,30u8,4u8,138u8, - 39u8,248u8,50u8,21u8,162u8,22u8,248u8,70u8,20u8,215u8,226u8,231u8,35u8,90u8,175u8,141u8,217u8,81u8,114u8,69u8, - 201u8,108u8,225u8,184u8,49u8,167u8,11u8,208u8,131u8,190u8,71u8,11u8,28u8,236u8,160u8,5u8,154u8,89u8,42u8,250u8, - 90u8,141u8,206u8,55u8,27u8,92u8,52u8,223u8,114u8,97u8,170u8,230u8,4u8,237u8,249u8,189u8,160u8,61u8,143u8,216u8, - 172u8,175u8,56u8,106u8,124u8,103u8,88u8,127u8,225u8,251u8,28u8,240u8,154u8,86u8,245u8,151u8,125u8,253u8,128u8,218u8, - 63u8,96u8,235u8,202u8,186u8,224u8,250u8,83u8,174u8,5u8,79u8,57u8,192u8,179u8,156u8,116u8,164u8,200u8,187u8,32u8, - 193u8,253u8,181u8,192u8,234u8,131u8,14u8,66u8,32u8,197u8,239u8,58u8,107u8,227u8,224u8,131u8,198u8,231u8,113u8,118u8, - 3u8,167u8,68u8,120u8,71u8,56u8,117u8,50u8,108u8,71u8,64u8,42u8,39u8,102u8,159u8,135u8,87u8,159u8,119u8,177u8, - 190u8,231u8,48u8,52u8,4u8,82u8,63u8,8u8,81u8,127u8,17u8,166u8,198u8,232u8,74u8,217u8,160u8,229u8,130u8,189u8, - 104u8,250u8,155u8,111u8,1u8,224u8,67u8,122u8,174u8,31u8,163u8,168u8,191u8,47u8,211u8,220u8,25u8,231u8,80u8,56u8, - 40u8,67u8,69u8,248u8,62u8,19u8,229u8,16u8,192u8,77u8,110u8,170u8,177u8,41u8,75u8,178u8,175u8,225u8,249u8,222u8, - 255u8,25u8,144u8,118u8,186u8,214u8,122u8,178u8,32u8,45u8,171u8,60u8,6u8,113u8,188u8,129u8,233u8,55u8,35u8,242u8, - 162u8,5u8,60u8,92u8,80u8,31u8,45u8,41u8,26u8,9u8,58u8,177u8,184u8,93u8,42u8,145u8,26u8,192u8,6u8,65u8, - 71u8,158u8,221u8,9u8,160u8,147u8,121u8,119u8,130u8,168u8,115u8,177u8,78u8,173u8,94u8,88u8,188u8,65u8,179u8,144u8, - 209u8,223u8,97u8,150u8,102u8,160u8,209u8,245u8,19u8,52u8,252u8,211u8,134u8,97u8,60u8,195u8,19u8,80u8,229u8,77u8, - 207u8,213u8,255u8,128u8,250u8,31u8,136u8,254u8,7u8,27u8,247u8,63u8,164u8,254u8,135u8,162u8,255u8,225u8,38u8,253u8, - 179u8,55u8,245u8,250u8,254u8,158u8,201u8,169u8,240u8,1u8,29u8,254u8,209u8,176u8,141u8,130u8,59u8,141u8,115u8,71u8, - 77u8,233u8,113u8,144u8,20u8,240u8,216u8,165u8,110u8,61u8,188u8,98u8,21u8,153u8,123u8,16u8,200u8,179u8,100u8,125u8, - 229u8,100u8,114u8,141u8,221u8,65u8,224u8,173u8,11u8,200u8,50u8,188u8,45u8,67u8,48u8,125u8,180u8,162u8,51u8,64u8, - 252u8,252u8,12u8,178u8,187u8,125u8,130u8,70u8,125u8,148u8,234u8,34u8,45u8,23u8,116u8,241u8,140u8,188u8,122u8,154u8, - 75u8,35u8,252u8,70u8,145u8,161u8,79u8,238u8,77u8,211u8,85u8,86u8,145u8,7u8,195u8,205u8,174u8,142u8,213u8,186u8, - 205u8,204u8,164u8,85u8,125u8,107u8,146u8,182u8,73u8,193u8,167u8,135u8,211u8,238u8,61u8,0u8,45u8,179u8,167u8,8u8, - 208u8,250u8,106u8,127u8,100u8,107u8,35u8,15u8,34u8,45u8,140u8,215u8,192u8,237u8,251u8,49u8,237u8,111u8,139u8,169u8, - 206u8,18u8,232u8,227u8,246u8,207u8,168u8,29u8,13u8,111u8,57u8,131u8,101u8,206u8,211u8,27u8,117u8,194u8,80u8,191u8, - 194u8,86u8,117u8,246u8,117u8,146u8,76u8,165u8,119u8,146u8,207u8,182u8,12u8,179u8,90u8,227u8,155u8,45u8,190u8,255u8, - 179u8,214u8,135u8,234u8,112u8,192u8,97u8,27u8,75u8,188u8,215u8,226u8,106u8,104u8,198u8,176u8,25u8,135u8,128u8,82u8, - 56u8,168u8,235u8,166u8,200u8,215u8,242u8,205u8,122u8,207u8,105u8,74u8,15u8,224u8,255u8,135u8,35u8,91u8,1u8,188u8, - 139u8,107u8,218u8,101u8,222u8,188u8,245u8,163u8,61u8,199u8,52u8,136u8,145u8,191u8,155u8,247u8,35u8,61u8,199u8,52u8, - 244u8,125u8,127u8,79u8,247u8,199u8,134u8,142u8,97u8,182u8,222u8,66u8,51u8,85u8,20u8,200u8,35u8,246u8,206u8,241u8, - 56u8,86u8,157u8,28u8,182u8,199u8,227u8,246u8,201u8,225u8,173u8,56u8,55u8,228u8,39u8,118u8,249u8,71u8,214u8,67u8, - 113u8,95u8,27u8,142u8,169u8,215u8,224u8,190u8,64u8,30u8,25u8,223u8,142u8,137u8,13u8,166u8,227u8,32u8,55u8,100u8, - 54u8,213u8,73u8,103u8,178u8,59u8,147u8,224u8,22u8,215u8,95u8,88u8,59u8,118u8,31u8,61u8,103u8,237u8,77u8,83u8, - 116u8,201u8,113u8,120u8,19u8,36u8,119u8,99u8,247u8,223u8,153u8,140u8,251u8,180u8,239u8,95u8,15u8,100u8,125u8,213u8, - 242u8,102u8,219u8,243u8,91u8,238u8,234u8,111u8,177u8,163u8,239u8,233u8,226u8,64u8,142u8,250u8,192u8,253u8,105u8,205u8, - 119u8,94u8,7u8,240u8,30u8,239u8,237u8,25u8,173u8,59u8,118u8,215u8,101u8,139u8,254u8,118u8,237u8,29u8,43u8,31u8, - 183u8,200u8,94u8,68u8,30u8,251u8,163u8,234u8,187u8,193u8,31u8,142u8,221u8,108u8,88u8,156u8,120u8,126u8,227u8,206u8, - 130u8,89u8,209u8,1u8,53u8,141u8,185u8,113u8,188u8,243u8,95u8,140u8,109u8,75u8,62u8,56u8,111u8,218u8,247u8,126u8, - 22u8,207u8,137u8,85u8,145u8,241u8,246u8,179u8,127u8,1u8,114u8,60u8,181u8,30u8,159u8,143u8,0u8,0u8,0u8,0u8, - 5u8,98u8,108u8,111u8,99u8,107u8,239u8,19u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,90u8, - 109u8,111u8,219u8,182u8,22u8,254u8,222u8,95u8,193u8,161u8,64u8,97u8,3u8,174u8,227u8,164u8,105u8,186u8,235u8,52u8, - 193u8,210u8,206u8,104u8,130u8,187u8,188u8,32u8,73u8,187u8,139u8,91u8,12u8,154u8,44u8,209u8,54u8,17u8,89u8,244u8, - 72u8,202u8,142u8,111u8,151u8,255u8,126u8,15u8,95u8,68u8,145u8,148u8,100u8,167u8,203u8,58u8,108u8,249u8,146u8,72u8, - 34u8,207u8,57u8,60u8,231u8,57u8,175u8,204u8,206u8,206u8,14u8,186u8,157u8,17u8,142u8,230u8,52u8,45u8,50u8,140u8, - 82u8,60u8,33u8,57u8,230u8,40u8,70u8,92u8,176u8,34u8,17u8,240u8,139u8,50u8,146u8,79u8,145u8,152u8,97u8,52u8, - 199u8,34u8,78u8,99u8,17u8,35u8,58u8,81u8,207u8,227u8,140u8,38u8,119u8,40u8,206u8,83u8,148u8,227u8,149u8,121u8, - 194u8,75u8,156u8,11u8,222u8,127u8,102u8,232u8,197u8,11u8,65u8,121u8,52u8,97u8,241u8,28u8,175u8,40u8,187u8,27u8, - 14u8,245u8,170u8,47u8,207u8,16u8,252u8,20u8,28u8,3u8,253u8,116u8,56u8,196u8,140u8,81u8,118u8,232u8,191u8,155u8, - 224u8,88u8,20u8,12u8,243u8,224u8,245u8,18u8,39u8,162u8,182u8,150u8,46u8,4u8,161u8,249u8,225u8,51u8,251u8,182u8, - 198u8,54u8,78u8,18u8,90u8,228u8,226u8,176u8,125u8,133u8,18u8,124u8,56u8,252u8,114u8,131u8,179u8,73u8,15u8,141u8, - 228u8,211u8,41u8,28u8,45u8,195u8,15u8,27u8,54u8,49u8,156u8,208u8,124u8,66u8,166u8,5u8,139u8,181u8,4u8,173u8, - 43u8,185u8,136u8,239u8,240u8,230u8,239u8,2u8,71u8,82u8,219u8,241u8,116u8,227u8,186u8,53u8,23u8,120u8,30u8,197u8, - 105u8,10u8,186u8,225u8,174u8,122u8,106u8,75u8,5u8,153u8,99u8,32u8,59u8,95u8,108u8,90u8,195u8,226u8,156u8,199u8, - 137u8,20u8,62u8,154u8,96u8,108u8,84u8,56u8,97u8,4u8,131u8,85u8,107u8,171u8,167u8,24u8,176u8,65u8,184u8,89u8, - 5u8,71u8,231u8,2u8,157u8,159u8,252u8,39u8,250u8,120u8,176u8,63u8,68u8,197u8,193u8,62u8,58u8,66u8,187u8,223u8, - 239u8,239u8,31u8,188u8,217u8,223u8,31u8,188u8,121u8,245u8,102u8,240u8,175u8,215u8,175u8,119u8,15u8,118u8,95u8,155u8, - 245u8,59u8,128u8,181u8,155u8,25u8,45u8,178u8,20u8,141u8,49u8,34u8,249u8,75u8,190u8,206u8,19u8,180u8,34u8,98u8, - 134u8,222u8,73u8,84u8,92u8,99u8,78u8,11u8,150u8,96u8,196u8,10u8,46u8,74u8,252u8,145u8,92u8,130u8,43u8,82u8, - 176u8,233u8,51u8,174u8,8u8,153u8,79u8,254u8,166u8,89u8,204u8,209u8,29u8,94u8,27u8,104u8,149u8,252u8,78u8,49u8, - 153u8,206u8,68u8,9u8,216u8,164u8,96u8,12u8,236u8,170u8,161u8,106u8,215u8,205u8,212u8,26u8,117u8,128u8,158u8,183u8, - 249u8,22u8,20u8,136u8,22u8,152u8,17u8,42u8,37u8,22u8,43u8,140u8,115u8,132u8,23u8,52u8,153u8,1u8,190u8,203u8, - 117u8,234u8,57u8,34u8,185u8,192u8,108u8,25u8,103u8,13u8,68u8,52u8,136u8,208u8,106u8,134u8,25u8,54u8,222u8,161u8, - 207u8,44u8,5u8,146u8,38u8,146u8,194u8,89u8,255u8,1u8,207u8,147u8,203u8,230u8,68u8,8u8,156u8,90u8,58u8,86u8, - 3u8,145u8,38u8,48u8,116u8,241u8,249u8,246u8,2u8,175u8,148u8,42u8,212u8,187u8,227u8,138u8,123u8,177u8,72u8,37u8, - 166u8,124u8,9u8,27u8,41u8,124u8,84u8,43u8,71u8,114u8,225u8,153u8,89u8,231u8,18u8,123u8,216u8,106u8,63u8,79u8, - 132u8,199u8,218u8,207u8,223u8,36u8,237u8,151u8,50u8,186u8,232u8,169u8,144u8,131u8,29u8,59u8,194u8,151u8,217u8,16u8, - 25u8,196u8,247u8,124u8,205u8,7u8,10u8,103u8,224u8,228u8,105u8,240u8,174u8,209u8,188u8,11u8,134u8,151u8,132u8,22u8, - 220u8,168u8,117u8,73u8,5u8,134u8,191u8,137u8,128u8,240u8,50u8,68u8,58u8,198u8,188u8,45u8,190u8,63u8,118u8,55u8, - 208u8,5u8,229u8,152u8,53u8,8u8,50u8,137u8,73u8,134u8,211u8,168u8,92u8,1u8,170u8,78u8,73u8,130u8,121u8,69u8, - 231u8,96u8,255u8,216u8,199u8,196u8,101u8,254u8,50u8,153u8,197u8,160u8,26u8,101u8,255u8,180u8,176u8,17u8,214u8,68u8, - 84u8,161u8,30u8,166u8,4u8,20u8,99u8,196u8,183u8,219u8,229u8,142u8,104u8,78u8,18u8,6u8,172u8,192u8,1u8,83u8, - 238u8,28u8,204u8,177u8,147u8,214u8,169u8,193u8,145u8,4u8,95u8,14u8,33u8,93u8,75u8,24u8,103u8,8u8,226u8,125u8, - 194u8,32u8,190u8,226u8,180u8,239u8,26u8,164u8,13u8,6u8,27u8,108u8,67u8,179u8,52u8,218u8,232u8,2u8,210u8,248u8, - 173u8,11u8,28u8,129u8,111u8,225u8,192u8,121u8,49u8,31u8,99u8,230u8,249u8,67u8,233u8,49u8,41u8,133u8,164u8,148u8, - 83u8,56u8,210u8,111u8,5u8,28u8,160u8,230u8,204u8,70u8,77u8,125u8,39u8,48u8,141u8,46u8,62u8,158u8,71u8,23u8, - 163u8,159u8,163u8,119u8,63u8,93u8,190u8,255u8,119u8,52u8,250u8,52u8,186u8,184u8,189u8,137u8,126u8,188u8,28u8,221u8, - 68u8,23u8,151u8,183u8,209u8,249u8,201u8,237u8,251u8,83u8,243u8,237u8,116u8,116u8,246u8,225u8,244u8,214u8,134u8,176u8, - 67u8,43u8,213u8,73u8,14u8,0u8,6u8,153u8,73u8,106u8,1u8,128u8,86u8,160u8,12u8,120u8,88u8,146u8,20u8,212u8, - 135u8,70u8,247u8,11u8,176u8,50u8,232u8,88u8,138u8,100u8,215u8,8u8,42u8,93u8,68u8,190u8,250u8,116u8,142u8,40u8, - 131u8,28u8,137u8,100u8,144u8,93u8,98u8,164u8,136u8,197u8,160u8,69u8,79u8,212u8,179u8,139u8,79u8,39u8,63u8,157u8, - 253u8,24u8,93u8,93u8,95u8,94u8,93u8,222u8,140u8,174u8,75u8,81u8,246u8,42u8,81u8,148u8,89u8,80u8,169u8,68u8, - 148u8,196u8,185u8,84u8,7u8,112u8,25u8,120u8,148u8,254u8,59u8,186u8,190u8,140u8,70u8,87u8,151u8,112u8,182u8,179u8, - 139u8,219u8,209u8,53u8,208u8,45u8,137u8,189u8,58u8,116u8,213u8,45u8,49u8,0u8,98u8,209u8,60u8,91u8,75u8,42u8, - 73u8,156u8,1u8,136u8,75u8,44u8,126u8,208u8,129u8,94u8,83u8,94u8,20u8,227u8,140u8,36u8,29u8,157u8,20u8,186u8, - 104u8,82u8,72u8,165u8,16u8,65u8,224u8,36u8,255u8,195u8,157u8,48u8,73u8,160u8,23u8,156u8,76u8,115u8,204u8,122u8, - 65u8,104u8,180u8,160u8,213u8,136u8,237u8,58u8,32u8,10u8,115u8,26u8,164u8,107u8,248u8,197u8,68u8,20u8,208u8,14u8, - 121u8,117u8,15u8,45u8,9u8,189u8,225u8,187u8,78u8,27u8,79u8,116u8,140u8,6u8,32u8,145u8,172u8,51u8,134u8,67u8, - 99u8,210u8,40u8,102u8,211u8,98u8,14u8,16u8,234u8,52u8,105u8,173u8,219u8,53u8,234u8,146u8,63u8,115u8,186u8,196u8, - 145u8,160u8,111u8,189u8,148u8,115u8,220u8,177u8,223u8,149u8,8u8,190u8,112u8,61u8,239u8,163u8,159u8,171u8,190u8,120u8, - 223u8,220u8,32u8,53u8,232u8,213u8,62u8,133u8,222u8,211u8,118u8,196u8,250u8,206u8,122u8,218u8,48u8,101u8,208u8,112u8, - 168u8,220u8,82u8,190u8,140u8,102u8,77u8,73u8,164u8,166u8,233u8,58u8,241u8,205u8,217u8,165u8,157u8,81u8,107u8,174u8, - 217u8,194u8,243u8,193u8,62u8,25u8,179u8,59u8,193u8,67u8,211u8,84u8,254u8,134u8,61u8,55u8,233u8,219u8,37u8,239u8, - 235u8,88u8,151u8,126u8,28u8,51u8,91u8,28u8,156u8,72u8,246u8,104u8,10u8,150u8,102u8,121u8,156u8,39u8,184u8,10u8, - 151u8,240u8,7u8,196u8,115u8,142u8,100u8,53u8,5u8,174u8,192u8,103u8,176u8,121u8,188u8,174u8,54u8,125u8,168u8,246u8, - 232u8,178u8,215u8,245u8,27u8,229u8,47u8,205u8,202u8,178u8,166u8,171u8,128u8,212u8,234u8,77u8,143u8,11u8,168u8,93u8, - 80u8,252u8,111u8,5u8,1u8,55u8,106u8,69u8,220u8,159u8,232u8,108u8,117u8,81u8,158u8,232u8,102u8,25u8,54u8,225u8, - 60u8,98u8,165u8,224u8,71u8,104u8,76u8,129u8,220u8,42u8,154u8,102u8,116u8,44u8,117u8,86u8,136u8,208u8,11u8,127u8, - 104u8,151u8,84u8,18u8,172u8,167u8,40u8,73u8,212u8,227u8,210u8,247u8,191u8,87u8,251u8,55u8,46u8,3u8,50u8,117u8, - 13u8,56u8,167u8,49u8,29u8,133u8,76u8,194u8,218u8,5u8,54u8,96u8,223u8,131u8,250u8,11u8,56u8,100u8,200u8,122u8, - 147u8,187u8,249u8,142u8,210u8,154u8,197u8,191u8,52u8,168u8,162u8,215u8,112u8,4u8,244u8,208u8,107u8,241u8,181u8,231u8, - 159u8,151u8,4u8,175u8,126u8,177u8,78u8,117u8,141u8,161u8,69u8,203u8,3u8,159u8,147u8,37u8,159u8,169u8,77u8,106u8, - 126u8,48u8,197u8,34u8,60u8,130u8,194u8,127u8,87u8,231u8,168u8,173u8,224u8,245u8,176u8,176u8,21u8,7u8,161u8,189u8, - 118u8,208u8,238u8,64u8,253u8,212u8,170u8,90u8,44u8,252u8,6u8,119u8,2u8,57u8,187u8,86u8,99u8,244u8,189u8,98u8, - 133u8,65u8,120u8,147u8,213u8,91u8,156u8,173u8,226u8,53u8,151u8,143u8,28u8,182u8,64u8,86u8,29u8,227u8,137u8,172u8, - 145u8,240u8,61u8,78u8,10u8,81u8,22u8,118u8,78u8,159u8,197u8,165u8,130u8,98u8,151u8,164u8,212u8,140u8,54u8,55u8, - 68u8,154u8,140u8,78u8,11u8,92u8,225u8,97u8,57u8,31u8,162u8,48u8,4u8,60u8,177u8,32u8,254u8,211u8,74u8,217u8, - 175u8,46u8,162u8,109u8,71u8,170u8,36u8,122u8,108u8,196u8,146u8,5u8,51u8,116u8,97u8,170u8,195u8,150u8,149u8,143u8, - 44u8,116u8,24u8,84u8,207u8,144u8,46u8,221u8,10u8,6u8,194u8,13u8,189u8,171u8,34u8,243u8,167u8,243u8,254u8,246u8, - 152u8,183u8,156u8,119u8,94u8,44u8,231u8,110u8,8u8,2u8,102u8,239u8,116u8,35u8,230u8,210u8,6u8,133u8,164u8,69u8, - 162u8,137u8,199u8,40u8,40u8,8u8,1u8,43u8,150u8,39u8,34u8,130u8,195u8,0u8,65u8,1u8,232u8,130u8,100u8,101u8, - 83u8,215u8,201u8,169u8,236u8,191u8,1u8,84u8,247u8,188u8,219u8,175u8,5u8,82u8,207u8,127u8,45u8,221u8,163u8,35u8, - 244u8,195u8,114u8,46u8,67u8,0u8,192u8,23u8,120u8,255u8,254u8,59u8,82u8,83u8,4u8,136u8,172u8,60u8,50u8,200u8, - 52u8,222u8,100u8,171u8,202u8,78u8,185u8,59u8,200u8,158u8,38u8,36u8,131u8,26u8,231u8,132u8,115u8,217u8,237u8,167u8, - 56u8,39u8,56u8,237u8,212u8,74u8,207u8,174u8,231u8,252u8,94u8,36u8,117u8,97u8,129u8,239u8,33u8,252u8,233u8,185u8, - 11u8,36u8,121u8,154u8,227u8,142u8,19u8,118u8,201u8,4u8,89u8,65u8,208u8,119u8,254u8,49u8,186u8,65u8,245u8,211u8, - 74u8,148u8,211u8,57u8,238u8,152u8,3u8,203u8,184u8,97u8,207u8,168u8,87u8,86u8,39u8,117u8,24u8,63u8,52u8,38u8, - 147u8,210u8,173u8,65u8,134u8,201u8,19u8,19u8,74u8,157u8,96u8,95u8,151u8,110u8,64u8,215u8,196u8,123u8,85u8,248u8, - 96u8,214u8,121u8,209u8,176u8,54u8,172u8,200u8,66u8,13u8,7u8,223u8,129u8,168u8,223u8,36u8,251u8,170u8,147u8,113u8, - 32u8,48u8,179u8,68u8,131u8,255u8,74u8,69u8,0u8,255u8,85u8,89u8,108u8,182u8,30u8,166u8,23u8,88u8,168u8,213u8, - 205u8,123u8,141u8,166u8,244u8,223u8,182u8,196u8,20u8,127u8,81u8,67u8,91u8,107u8,131u8,69u8,207u8,53u8,175u8,61u8, - 169u8,204u8,170u8,129u8,186u8,164u8,47u8,247u8,220u8,236u8,185u8,81u8,247u8,189u8,80u8,219u8,174u8,49u8,36u8,130u8, - 203u8,9u8,164u8,52u8,41u8,20u8,140u8,9u8,148u8,72u8,121u8,26u8,165u8,4u8,162u8,15u8,25u8,23u8,144u8,140u8, - 167u8,49u8,151u8,67u8,51u8,72u8,95u8,33u8,168u8,101u8,235u8,200u8,101u8,216u8,86u8,97u8,65u8,174u8,65u8,134u8, - 6u8,248u8,241u8,132u8,209u8,185u8,105u8,23u8,181u8,98u8,77u8,11u8,11u8,77u8,99u8,195u8,219u8,82u8,111u8,253u8, - 144u8,193u8,153u8,142u8,50u8,113u8,190u8,70u8,208u8,203u8,115u8,234u8,242u8,178u8,205u8,97u8,172u8,164u8,192u8,105u8, - 79u8,231u8,38u8,200u8,52u8,42u8,7u8,161u8,49u8,228u8,109u8,174u8,83u8,156u8,149u8,43u8,161u8,36u8,231u8,62u8, - 155u8,96u8,60u8,8u8,1u8,68u8,151u8,194u8,145u8,221u8,101u8,20u8,112u8,24u8,74u8,87u8,166u8,212u8,42u8,74u8, - 78u8,76u8,122u8,212u8,35u8,14u8,205u8,28u8,230u8,167u8,24u8,186u8,98u8,86u8,214u8,224u8,146u8,24u8,12u8,25u8, - 164u8,34u8,204u8,16u8,68u8,157u8,71u8,167u8,99u8,194u8,67u8,38u8,134u8,22u8,171u8,206u8,89u8,42u8,145u8,41u8, - 159u8,52u8,93u8,200u8,150u8,51u8,49u8,60u8,5u8,155u8,2u8,50u8,45u8,68u8,129u8,159u8,252u8,84u8,158u8,18u8, - 86u8,86u8,209u8,166u8,57u8,216u8,128u8,48u8,87u8,152u8,193u8,190u8,185u8,106u8,3u8,120u8,66u8,101u8,70u8,155u8, - 197u8,208u8,241u8,235u8,89u8,128u8,174u8,223u8,210u8,178u8,58u8,168u8,90u8,21u8,37u8,14u8,81u8,70u8,49u8,74u8, - 113u8,4u8,52u8,122u8,128u8,211u8,76u8,49u8,83u8,95u8,93u8,142u8,206u8,86u8,162u8,183u8,102u8,49u8,47u8,7u8, - 34u8,36u8,247u8,209u8,164u8,184u8,57u8,41u8,81u8,7u8,86u8,83u8,86u8,46u8,42u8,217u8,35u8,57u8,137u8,6u8, - 125u8,16u8,40u8,203u8,252u8,216u8,220u8,107u8,115u8,101u8,71u8,39u8,222u8,24u8,27u8,6u8,243u8,121u8,229u8,163u8, - 157u8,96u8,88u8,14u8,110u8,229u8,230u8,176u8,78u8,55u8,244u8,63u8,27u8,0u8,208u8,75u8,84u8,219u8,43u8,79u8, - 26u8,5u8,111u8,35u8,185u8,163u8,211u8,69u8,199u8,71u8,77u8,254u8,239u8,23u8,131u8,161u8,199u8,214u8,24u8,84u8, - 47u8,188u8,212u8,246u8,176u8,177u8,32u8,254u8,96u8,80u8,223u8,52u8,155u8,106u8,170u8,133u8,75u8,13u8,104u8,113u8, - 245u8,186u8,111u8,85u8,12u8,59u8,82u8,184u8,131u8,66u8,136u8,163u8,26u8,141u8,42u8,193u8,200u8,203u8,28u8,141u8, - 9u8,35u8,181u8,122u8,163u8,89u8,84u8,17u8,217u8,86u8,173u8,141u8,81u8,88u8,150u8,172u8,213u8,16u8,200u8,233u8, - 253u8,135u8,58u8,54u8,111u8,152u8,93u8,135u8,33u8,121u8,232u8,231u8,63u8,215u8,104u8,85u8,45u8,89u8,130u8,216u8, - 36u8,116u8,5u8,2u8,153u8,8u8,2u8,90u8,125u8,155u8,158u8,106u8,95u8,106u8,217u8,167u8,161u8,217u8,245u8,243u8, - 172u8,159u8,234u8,221u8,67u8,118u8,101u8,221u8,22u8,50u8,104u8,74u8,171u8,173u8,93u8,242u8,215u8,206u8,45u8,187u8, - 181u8,118u8,173u8,185u8,251u8,12u8,70u8,60u8,174u8,208u8,77u8,185u8,176u8,9u8,40u8,49u8,250u8,213u8,163u8,242u8, - 171u8,102u8,211u8,215u8,243u8,68u8,155u8,93u8,86u8,36u8,203u8,130u8,146u8,220u8,92u8,29u8,161u8,20u8,32u8,157u8, - 8u8,168u8,170u8,33u8,44u8,202u8,119u8,172u8,28u8,220u8,64u8,22u8,88u8,163u8,9u8,97u8,92u8,88u8,150u8,129u8, - 79u8,26u8,94u8,62u8,248u8,12u8,221u8,26u8,0u8,53u8,254u8,30u8,209u8,93u8,124u8,147u8,50u8,81u8,18u8,45u8, - 37u8,131u8,86u8,1u8,42u8,224u8,193u8,253u8,96u8,75u8,241u8,226u8,143u8,0u8,0u8,192u8,109u8,51u8,129u8,205u8, - 85u8,141u8,183u8,107u8,83u8,245u8,88u8,117u8,146u8,149u8,164u8,45u8,211u8,199u8,198u8,185u8,164u8,105u8,45u8,27u8, - 190u8,108u8,24u8,102u8,62u8,162u8,101u8,148u8,152u8,93u8,136u8,117u8,167u8,219u8,180u8,187u8,236u8,92u8,221u8,142u8, - 162u8,190u8,110u8,75u8,27u8,187u8,129u8,67u8,67u8,29u8,58u8,120u8,252u8,28u8,242u8,169u8,78u8,178u8,98u8,4u8, - 188u8,129u8,3u8,120u8,120u8,194u8,200u8,66u8,180u8,122u8,75u8,197u8,49u8,184u8,41u8,81u8,165u8,210u8,207u8,146u8, - 12u8,20u8,96u8,87u8,241u8,58u8,163u8,113u8,90u8,155u8,194u8,40u8,248u8,41u8,94u8,192u8,42u8,240u8,156u8,72u8, - 59u8,142u8,19u8,193u8,39u8,80u8,40u8,148u8,9u8,202u8,29u8,60u8,60u8,101u8,214u8,8u8,125u8,183u8,101u8,21u8, - 184u8,205u8,63u8,169u8,101u8,219u8,26u8,99u8,255u8,50u8,23u8,14u8,140u8,212u8,234u8,199u8,219u8,106u8,176u8,86u8, - 55u8,55u8,215u8,252u8,237u8,206u8,254u8,200u8,102u8,242u8,31u8,26u8,4u8,156u8,106u8,35u8,7u8,32u8,186u8,31u8, - 59u8,143u8,188u8,168u8,120u8,254u8,25u8,142u8,41u8,34u8,57u8,82u8,250u8,37u8,244u8,200u8,234u8,62u8,77u8,245u8, - 30u8,114u8,97u8,167u8,188u8,66u8,249u8,3u8,23u8,106u8,238u8,245u8,156u8,166u8,210u8,190u8,185u8,73u8,200u8,112u8, - 246u8,47u8,19u8,88u8,232u8,96u8,222u8,25u8,0u8,65u8,50u8,123u8,195u8,73u8,212u8,25u8,27u8,135u8,213u8,245u8, - 155u8,194u8,71u8,231u8,104u8,123u8,155u8,164u8,111u8,173u8,35u8,243u8,92u8,233u8,106u8,131u8,251u8,59u8,186u8,120u8, - 17u8,94u8,209u8,161u8,221u8,134u8,66u8,239u8,105u8,51u8,102u8,40u8,254u8,118u8,123u8,104u8,224u8,208u8,221u8,118u8, - 247u8,83u8,151u8,106u8,239u8,91u8,72u8,181u8,87u8,157u8,246u8,107u8,109u8,221u8,43u8,13u8,160u8,11u8,153u8,221u8, - 189u8,87u8,198u8,250u8,207u8,63u8,99u8,115u8,253u8,29u8,73u8,111u8,147u8,173u8,82u8,12u8,130u8,66u8,95u8,67u8, - 83u8,121u8,117u8,51u8,184u8,127u8,13u8,211u8,246u8,87u8,61u8,4u8,226u8,234u8,18u8,238u8,104u8,251u8,127u8,52u8, - 125u8,61u8,178u8,162u8,34u8,143u8,11u8,49u8,163u8,12u8,140u8,156u8,70u8,92u8,253u8,139u8,138u8,18u8,104u8,195u8, - 189u8,90u8,56u8,83u8,183u8,222u8,230u8,126u8,248u8,187u8,64u8,115u8,59u8,132u8,74u8,47u8,223u8,179u8,38u8,126u8, - 120u8,246u8,127u8,34u8,23u8,251u8,134u8,220u8,39u8,0u8,0u8,0u8,0u8,4u8,117u8,116u8,105u8,108u8,183u8,2u8, - 31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,125u8,81u8,187u8,110u8,195u8,48u8,12u8,220u8,253u8,21u8, - 55u8,38u8,64u8,17u8,175u8,133u8,19u8,100u8,239u8,210u8,201u8,157u8,19u8,217u8,162u8,27u8,162u8,182u8,100u8,72u8, - 148u8,3u8,183u8,200u8,191u8,87u8,126u8,196u8,105u8,218u8,34u8,26u8,4u8,2u8,228u8,29u8,239u8,142u8,105u8,154u8, - 226u8,77u8,184u8,102u8,233u8,81u8,5u8,83u8,10u8,91u8,227u8,17u8,60u8,105u8,20u8,61u8,228u8,68u8,168u8,156u8, - 106u8,232u8,108u8,221u8,7u8,26u8,171u8,67u8,77u8,126u8,147u8,76u8,5u8,84u8,43u8,214u8,31u8,150u8,126u8,150u8, - 133u8,72u8,132u8,175u8,4u8,241u8,85u8,142u8,201u8,232u8,191u8,35u8,165u8,213u8,180u8,125u8,56u8,241u8,174u8,252u8, - 193u8,151u8,39u8,26u8,86u8,108u8,147u8,113u8,52u8,141u8,34u8,95u8,149u8,112u8,71u8,139u8,70u8,136u8,133u8,38u8, - 79u8,142u8,85u8,205u8,159u8,81u8,11u8,164u8,111u8,9u8,249u8,230u8,10u8,184u8,1u8,173u8,80u8,52u8,162u8,36u8, - 126u8,236u8,111u8,4u8,218u8,146u8,135u8,177u8,130u8,54u8,8u8,148u8,233u8,81u8,70u8,231u8,226u8,20u8,27u8,65u8, - 108u8,31u8,243u8,227u8,6u8,47u8,21u8,6u8,193u8,67u8,30u8,254u8,23u8,92u8,236u8,178u8,225u8,135u8,142u8,232u8, - 7u8,53u8,27u8,82u8,14u8,157u8,170u8,3u8,61u8,129u8,101u8,64u8,18u8,59u8,56u8,242u8,109u8,92u8,193u8,197u8, - 148u8,246u8,44u8,137u8,160u8,149u8,168u8,161u8,232u8,239u8,12u8,177u8,95u8,248u8,237u8,217u8,144u8,158u8,140u8,181u8, - 161u8,168u8,185u8,92u8,77u8,217u8,173u8,97u8,150u8,80u8,98u8,156u8,182u8,57u8,20u8,189u8,144u8,223u8,229u8,251u8, - 213u8,88u8,100u8,232u8,168u8,20u8,235u8,118u8,225u8,121u8,191u8,206u8,144u8,207u8,105u8,78u8,28u8,35u8,72u8,105u8, - 29u8,85u8,13u8,241u8,95u8,193u8,255u8,34u8,231u8,177u8,249u8,180u8,211u8,241u8,238u8,1u8,235u8,177u8,115u8,73u8, - 46u8,201u8,55u8,192u8,13u8,203u8,50u8,81u8,2u8,0u8,0u8,0u8,0u8,4u8,99u8,111u8,100u8,101u8,205u8,30u8, - 31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,181u8,91u8,109u8,115u8,219u8,54u8,18u8,254u8,158u8,95u8, - 129u8,230u8,67u8,42u8,246u8,104u8,181u8,73u8,211u8,52u8,39u8,199u8,158u8,83u8,108u8,95u8,234u8,105u8,156u8,120u8, - 236u8,100u8,58u8,157u8,155u8,27u8,26u8,34u8,33u8,139u8,49u8,69u8,178u8,4u8,105u8,69u8,189u8,230u8,191u8,223u8, - 46u8,222u8,8u8,128u8,160u8,236u8,164u8,173u8,166u8,51u8,46u8,73u8,96u8,119u8,177u8,216u8,151u8,103u8,23u8,200u8, - 183u8,223u8,126u8,75u8,222u8,173u8,114u8,78u8,214u8,85u8,214u8,21u8,140u8,240u8,174u8,174u8,171u8,166u8,229u8,100u8, - 217u8,149u8,105u8,155u8,87u8,37u8,45u8,242u8,118u8,75u8,26u8,86u8,208u8,150u8,101u8,164u8,173u8,72u8,90u8,101u8, - 140u8,172u8,105u8,73u8,175u8,217u8,154u8,149u8,237u8,244u8,129u8,154u8,73u8,235u8,182u8,226u8,201u8,178u8,161u8,107u8, - 182u8,169u8,154u8,155u8,217u8,76u8,12u8,252u8,223u8,3u8,2u8,191u8,142u8,3u8,229u8,54u8,155u8,205u8,120u8,219u8, - 228u8,229u8,245u8,108u8,118u8,41u8,254u8,238u8,187u8,31u8,89u8,211u8,84u8,141u8,247u8,142u8,231u8,215u8,37u8,243u8, - 95u8,222u8,178u8,180u8,29u8,140u8,92u8,50u8,218u8,118u8,13u8,227u8,251u8,15u8,204u8,251u8,129u8,72u8,93u8,155u8, - 23u8,251u8,227u8,159u8,249u8,150u8,183u8,108u8,157u8,208u8,44u8,3u8,58u8,28u8,73u8,185u8,67u8,5u8,159u8,180u8, - 170u8,183u8,116u8,81u8,176u8,132u8,150u8,219u8,217u8,108u8,94u8,110u8,61u8,57u8,170u8,26u8,213u8,54u8,155u8,189u8, - 21u8,127u8,247u8,67u8,235u8,87u8,34u8,130u8,234u8,247u8,254u8,146u8,159u8,166u8,118u8,132u8,26u8,63u8,239u8,22u8, - 69u8,206u8,87u8,192u8,70u8,115u8,193u8,29u8,102u8,164u8,166u8,233u8,13u8,236u8,25u8,108u8,229u8,117u8,14u8,98u8, - 108u8,9u8,109u8,73u8,11u8,239u8,175u8,243u8,91u8,86u8,18u8,181u8,230u8,169u8,152u8,2u8,159u8,187u8,180u8,37u8, - 231u8,114u8,198u8,133u8,158u8,176u8,162u8,156u8,220u8,176u8,109u8,12u8,223u8,171u8,134u8,197u8,36u8,107u8,170u8,90u8, - 109u8,176u8,102u8,164u8,166u8,112u8,146u8,151u8,188u8,165u8,69u8,1u8,38u8,35u8,216u8,128u8,129u8,57u8,28u8,240u8, - 167u8,4u8,226u8,51u8,34u8,247u8,243u8,133u8,154u8,124u8,198u8,90u8,154u8,209u8,150u8,30u8,198u8,98u8,232u8,167u8, - 126u8,25u8,250u8,11u8,89u8,86u8,13u8,161u8,154u8,192u8,148u8,204u8,139u8,130u8,44u8,182u8,45u8,35u8,139u8,162u8, - 90u8,0u8,167u8,6u8,87u8,89u8,3u8,55u8,176u8,79u8,148u8,128u8,147u8,5u8,229u8,236u8,217u8,211u8,189u8,106u8, - 185u8,119u8,253u8,123u8,94u8,215u8,44u8,219u8,195u8,241u8,60u8,176u8,90u8,195u8,3u8,87u8,59u8,190u8,208u8,55u8, - 96u8,52u8,164u8,90u8,202u8,165u8,105u8,65u8,204u8,136u8,18u8,190u8,206u8,136u8,52u8,242u8,216u8,153u8,135u8,59u8, - 209u8,213u8,215u8,13u8,133u8,157u8,170u8,171u8,34u8,79u8,183u8,227u8,84u8,212u8,184u8,68u8,142u8,155u8,145u8,247u8, - 242u8,249u8,92u8,60u8,14u8,201u8,150u8,221u8,122u8,193u8,26u8,46u8,232u8,229u8,107u8,216u8,131u8,214u8,242u8,107u8, - 92u8,205u8,130u8,193u8,70u8,43u8,162u8,25u8,106u8,141u8,87u8,132u8,179u8,230u8,22u8,134u8,82u8,46u8,108u8,161u8, - 42u8,247u8,210u8,21u8,205u8,75u8,216u8,145u8,134u8,131u8,245u8,78u8,61u8,38u8,64u8,111u8,153u8,179u8,34u8,35u8, - 155u8,28u8,85u8,14u8,126u8,209u8,181u8,213u8,154u8,182u8,121u8,10u8,123u8,13u8,22u8,197u8,133u8,187u8,102u8,64u8, - 7u8,2u8,73u8,154u8,194u8,118u8,47u8,187u8,66u8,179u8,28u8,46u8,76u8,74u8,60u8,35u8,221u8,179u8,167u8,195u8, - 229u8,240u8,170u8,107u8,82u8,70u8,178u8,28u8,76u8,164u8,149u8,74u8,210u8,47u8,209u8,188u8,196u8,163u8,177u8,0u8, - 33u8,26u8,252u8,151u8,86u8,165u8,220u8,78u8,16u8,98u8,177u8,5u8,97u8,27u8,152u8,187u8,232u8,242u8,34u8,131u8, - 157u8,192u8,41u8,14u8,27u8,190u8,162u8,79u8,126u8,120u8,134u8,180u8,25u8,77u8,87u8,64u8,52u8,203u8,111u8,243u8, - 172u8,163u8,133u8,226u8,19u8,195u8,12u8,10u8,75u8,129u8,128u8,168u8,166u8,175u8,9u8,45u8,234u8,21u8,93u8,48u8, - 181u8,228u8,152u8,208u8,50u8,211u8,116u8,228u8,247u8,107u8,208u8,95u8,191u8,84u8,73u8,40u8,145u8,171u8,24u8,55u8, - 9u8,237u8,156u8,16u8,87u8,243u8,37u8,140u8,140u8,245u8,10u8,207u8,170u8,91u8,54u8,5u8,29u8,23u8,104u8,243u8, - 160u8,233u8,41u8,121u8,37u8,13u8,152u8,180u8,236u8,99u8,219u8,243u8,209u8,19u8,141u8,51u8,117u8,207u8,15u8,135u8, - 108u8,32u8,48u8,8u8,85u8,74u8,163u8,176u8,189u8,20u8,148u8,21u8,54u8,66u8,53u8,212u8,208u8,61u8,19u8,207u8, - 158u8,143u8,106u8,30u8,63u8,85u8,69u8,198u8,181u8,39u8,29u8,179u8,218u8,242u8,246u8,12u8,158u8,124u8,79u8,135u8, - 17u8,30u8,129u8,127u8,131u8,99u8,47u8,59u8,140u8,227u8,4u8,150u8,199u8,74u8,52u8,66u8,51u8,192u8,188u8,153u8, - 17u8,25u8,92u8,95u8,64u8,244u8,61u8,244u8,131u8,196u8,28u8,89u8,177u8,50u8,99u8,37u8,248u8,22u8,228u8,43u8, - 19u8,39u8,72u8,45u8,35u8,163u8,140u8,73u8,42u8,28u8,5u8,34u8,0u8,72u8,229u8,59u8,127u8,76u8,48u8,234u8, - 91u8,33u8,128u8,166u8,105u8,213u8,149u8,160u8,109u8,69u8,38u8,246u8,163u8,90u8,98u8,135u8,128u8,209u8,56u8,70u8, - 23u8,85u8,7u8,162u8,104u8,39u8,133u8,61u8,167u8,238u8,14u8,40u8,201u8,92u8,165u8,223u8,59u8,52u8,49u8,69u8, - 248u8,62u8,129u8,233u8,82u8,186u8,27u8,90u8,85u8,76u8,84u8,144u8,84u8,3u8,167u8,228u8,100u8,93u8,3u8,8u8, - 200u8,151u8,164u8,172u8,90u8,82u8,55u8,21u8,120u8,9u8,68u8,16u8,207u8,200u8,199u8,77u8,79u8,209u8,94u8,211u8, - 90u8,216u8,117u8,90u8,173u8,107u8,145u8,96u8,51u8,242u8,242u8,232u8,242u8,158u8,212u8,19u8,152u8,61u8,206u8,33u8, - 100u8,55u8,150u8,241u8,133u8,45u8,103u8,144u8,95u8,142u8,25u8,79u8,155u8,124u8,129u8,241u8,176u8,244u8,162u8,180u8, - 189u8,27u8,78u8,28u8,182u8,55u8,3u8,141u8,100u8,176u8,37u8,58u8,122u8,119u8,207u8,125u8,118u8,202u8,222u8,48u8, - 104u8,181u8,16u8,52u8,56u8,201u8,186u8,26u8,198u8,2u8,208u8,210u8,6u8,129u8,219u8,197u8,33u8,216u8,182u8,43u8, - 88u8,2u8,248u8,46u8,70u8,33u8,237u8,187u8,210u8,154u8,65u8,135u8,160u8,209u8,10u8,246u8,186u8,49u8,9u8,21u8, - 195u8,175u8,157u8,114u8,5u8,67u8,17u8,25u8,201u8,201u8,217u8,219u8,227u8,247u8,175u8,79u8,146u8,55u8,243u8,179u8, - 147u8,228u8,232u8,245u8,252u8,242u8,39u8,17u8,125u8,201u8,1u8,249u8,238u8,227u8,227u8,253u8,94u8,180u8,35u8,90u8, - 226u8,102u8,104u8,29u8,128u8,58u8,242u8,245u8,186u8,107u8,17u8,247u8,104u8,54u8,54u8,213u8,247u8,231u8,175u8,46u8, - 230u8,199u8,39u8,201u8,233u8,217u8,217u8,251u8,119u8,243u8,151u8,175u8,79u8,122u8,170u8,79u8,134u8,84u8,179u8,106u8, - 83u8,42u8,186u8,154u8,216u8,215u8,92u8,241u8,162u8,139u8,92u8,0u8,78u8,75u8,235u8,30u8,143u8,95u8,78u8,230u8, - 63u8,159u8,92u8,36u8,231u8,111u8,95u8,159u8,30u8,253u8,218u8,243u8,249u8,62u8,192u8,135u8,21u8,172u8,101u8,189u8, - 123u8,65u8,48u8,111u8,201u8,134u8,114u8,43u8,16u8,168u8,40u8,203u8,209u8,101u8,2u8,235u8,82u8,218u8,58u8,59u8, - 189u8,188u8,60u8,125u8,243u8,170u8,103u8,246u8,116u8,223u8,54u8,26u8,19u8,109u8,32u8,38u8,64u8,110u8,68u8,214u8, - 11u8,68u8,32u8,188u8,42u8,110u8,37u8,102u8,6u8,168u8,104u8,49u8,117u8,60u8,92u8,113u8,58u8,159u8,31u8,253u8, - 60u8,127u8,117u8,146u8,28u8,159u8,156u8,15u8,217u8,253u8,176u8,63u8,18u8,222u8,82u8,185u8,208u8,21u8,189u8,197u8, - 101u8,110u8,24u8,189u8,1u8,51u8,112u8,13u8,215u8,225u8,130u8,212u8,71u8,244u8,247u8,108u8,127u8,71u8,8u8,45u8, - 201u8,21u8,109u8,22u8,121u8,219u8,208u8,102u8,123u8,213u8,39u8,171u8,142u8,139u8,149u8,86u8,150u8,18u8,29u8,148u8, - 103u8,113u8,157u8,95u8,188u8,60u8,125u8,119u8,49u8,191u8,248u8,53u8,121u8,243u8,246u8,93u8,114u8,137u8,230u8,55u8, - 63u8,62u8,190u8,56u8,185u8,188u8,236u8,69u8,248u8,209u8,222u8,194u8,6u8,224u8,60u8,154u8,123u8,31u8,190u8,133u8, - 27u8,228u8,37u8,70u8,15u8,248u8,132u8,150u8,232u8,1u8,41u8,176u8,248u8,44u8,231u8,104u8,163u8,153u8,195u8,255u8, - 244u8,205u8,209u8,219u8,179u8,243u8,249u8,187u8,83u8,48u8,76u8,181u8,238u8,228u8,248u8,244u8,18u8,237u8,244u8,184u8, - 231u8,254u8,220u8,226u8,254u8,203u8,138u8,9u8,143u8,130u8,26u8,168u8,2u8,80u8,32u8,235u8,32u8,89u8,249u8,104u8, - 158u8,66u8,154u8,178u8,34u8,90u8,28u8,105u8,182u8,233u8,138u8,165u8,55u8,40u8,8u8,100u8,213u8,106u8,131u8,40u8, - 11u8,145u8,137u8,161u8,43u8,108u8,0u8,28u8,28u8,232u8,161u8,81u8,130u8,206u8,86u8,194u8,100u8,170u8,18u8,208u8, - 19u8,104u8,178u8,67u8,127u8,70u8,112u8,171u8,157u8,124u8,179u8,202u8,1u8,152u8,0u8,160u8,45u8,191u8,110u8,17u8, - 102u8,52u8,44u8,147u8,156u8,97u8,100u8,35u8,221u8,94u8,233u8,90u8,167u8,248u8,140u8,221u8,178u8,162u8,170u8,225u8, - 43u8,72u8,1u8,91u8,81u8,131u8,14u8,132u8,178u8,144u8,172u8,48u8,77u8,208u8,236u8,141u8,8u8,36u8,108u8,93u8, - 1u8,170u8,47u8,232u8,22u8,147u8,16u8,36u8,12u8,180u8,82u8,180u8,90u8,9u8,177u8,128u8,22u8,45u8,96u8,108u8, - 182u8,53u8,244u8,69u8,152u8,19u8,232u8,78u8,160u8,68u8,201u8,88u8,174u8,9u8,235u8,69u8,15u8,176u8,38u8,198u8, - 100u8,38u8,145u8,7u8,94u8,173u8,240u8,232u8,189u8,55u8,225u8,242u8,59u8,136u8,148u8,94u8,188u8,212u8,187u8,66u8, - 131u8,74u8,87u8,170u8,4u8,45u8,194u8,226u8,17u8,44u8,41u8,85u8,42u8,161u8,248u8,84u8,168u8,71u8,142u8,21u8, - 250u8,174u8,41u8,214u8,120u8,144u8,119u8,12u8,3u8,74u8,74u8,182u8,177u8,17u8,243u8,132u8,70u8,86u8,124u8,48u8, - 11u8,21u8,133u8,49u8,124u8,93u8,68u8,130u8,131u8,9u8,205u8,70u8,121u8,49u8,90u8,134u8,82u8,44u8,168u8,170u8, - 212u8,206u8,62u8,174u8,43u8,185u8,162u8,47u8,81u8,212u8,227u8,113u8,69u8,245u8,8u8,192u8,71u8,204u8,162u8,70u8, - 234u8,67u8,58u8,130u8,88u8,21u8,75u8,22u8,172u8,47u8,17u8,238u8,144u8,217u8,204u8,255u8,18u8,177u8,159u8,236u8, - 22u8,219u8,115u8,109u8,16u8,14u8,37u8,147u8,186u8,4u8,175u8,58u8,45u8,201u8,53u8,131u8,198u8,0u8,45u8,98u8, - 185u8,44u8,103u8,24u8,110u8,175u8,101u8,180u8,224u8,61u8,215u8,48u8,8u8,11u8,147u8,5u8,108u8,8u8,174u8,82u8, - 132u8,202u8,50u8,176u8,68u8,160u8,144u8,72u8,46u8,137u8,183u8,218u8,182u8,154u8,44u8,155u8,106u8,237u8,151u8,97u8, - 16u8,31u8,189u8,87u8,160u8,140u8,69u8,85u8,21u8,150u8,14u8,112u8,222u8,84u8,137u8,248u8,226u8,0u8,102u8,76u8, - 173u8,244u8,102u8,41u8,224u8,180u8,132u8,80u8,3u8,13u8,151u8,223u8,173u8,130u8,192u8,46u8,121u8,95u8,129u8,200u8, - 60u8,87u8,14u8,143u8,210u8,230u8,102u8,194u8,196u8,111u8,102u8,144u8,71u8,178u8,117u8,18u8,27u8,68u8,10u8,89u8, - 23u8,139u8,45u8,243u8,94u8,147u8,158u8,249u8,165u8,111u8,244u8,64u8,66u8,220u8,223u8,186u8,28u8,44u8,122u8,208u8, - 6u8,232u8,215u8,229u8,247u8,75u8,102u8,51u8,116u8,169u8,166u8,77u8,60u8,97u8,124u8,225u8,162u8,125u8,67u8,2u8, - 50u8,180u8,200u8,25u8,16u8,130u8,165u8,92u8,64u8,67u8,146u8,75u8,170u8,229u8,196u8,17u8,221u8,154u8,5u8,88u8, - 113u8,242u8,149u8,112u8,59u8,254u8,194u8,19u8,239u8,112u8,130u8,243u8,163u8,200u8,18u8,83u8,150u8,49u8,183u8,12u8, - 183u8,208u8,161u8,24u8,15u8,215u8,54u8,232u8,74u8,252u8,71u8,171u8,233u8,191u8,228u8,83u8,100u8,72u8,126u8,34u8, - 172u8,224u8,204u8,227u8,33u8,39u8,204u8,102u8,117u8,199u8,87u8,201u8,2u8,232u8,76u8,30u8,129u8,131u8,128u8,49u8, - 64u8,95u8,107u8,147u8,92u8,67u8,111u8,130u8,22u8,9u8,188u8,25u8,17u8,121u8,170u8,89u8,247u8,91u8,99u8,49u8, - 28u8,128u8,71u8,5u8,39u8,184u8,149u8,33u8,157u8,142u8,142u8,84u8,231u8,215u8,125u8,227u8,69u8,198u8,63u8,44u8, - 247u8,26u8,153u8,186u8,21u8,212u8,30u8,218u8,90u8,38u8,113u8,176u8,93u8,50u8,139u8,236u8,101u8,227u8,22u8,203u8, - 103u8,20u8,178u8,73u8,212u8,247u8,137u8,111u8,104u8,248u8,126u8,96u8,100u8,177u8,72u8,167u8,6u8,209u8,247u8,192u8, - 254u8,48u8,186u8,143u8,233u8,33u8,240u8,130u8,92u8,143u8,89u8,54u8,140u8,9u8,48u8,189u8,66u8,177u8,12u8,219u8, - 222u8,148u8,180u8,76u8,153u8,136u8,13u8,25u8,75u8,113u8,193u8,216u8,203u8,108u8,208u8,136u8,4u8,74u8,238u8,19u8, - 71u8,195u8,16u8,49u8,192u8,136u8,198u8,174u8,63u8,164u8,77u8,127u8,53u8,113u8,246u8,26u8,215u8,52u8,117u8,35u8, - 132u8,246u8,240u8,195u8,29u8,121u8,80u8,141u8,137u8,29u8,90u8,162u8,239u8,57u8,155u8,229u8,229u8,45u8,184u8,114u8, - 6u8,195u8,175u8,59u8,236u8,172u8,78u8,118u8,2u8,151u8,168u8,167u8,17u8,41u8,228u8,114u8,183u8,63u8,253u8,117u8, - 126u8,116u8,111u8,255u8,129u8,182u8,46u8,150u8,119u8,147u8,200u8,113u8,31u8,75u8,98u8,4u8,123u8,152u8,148u8,185u8, - 8u8,113u8,66u8,5u8,61u8,252u8,204u8,177u8,141u8,85u8,121u8,245u8,141u8,187u8,88u8,9u8,180u8,18u8,236u8,47u8, - 192u8,162u8,69u8,126u8,79u8,236u8,249u8,98u8,29u8,49u8,121u8,132u8,179u8,163u8,0u8,223u8,222u8,123u8,176u8,115u8, - 3u8,110u8,1u8,54u8,176u8,132u8,77u8,106u8,93u8,62u8,50u8,143u8,38u8,178u8,34u8,59u8,128u8,236u8,211u8,38u8, - 246u8,171u8,137u8,166u8,111u8,207u8,49u8,5u8,217u8,1u8,249u8,178u8,72u8,224u8,146u8,43u8,192u8,171u8,15u8,140u8, - 90u8,11u8,145u8,213u8,116u8,64u8,227u8,30u8,107u8,232u8,101u8,177u8,143u8,48u8,26u8,70u8,121u8,31u8,16u8,236u8, - 186u8,175u8,220u8,118u8,156u8,251u8,29u8,0u8,40u8,56u8,213u8,36u8,39u8,47u8,144u8,148u8,111u8,13u8,56u8,27u8, - 90u8,62u8,150u8,84u8,114u8,137u8,147u8,62u8,148u8,229u8,150u8,96u8,218u8,228u8,96u8,202u8,20u8,181u8,70u8,14u8, - 14u8,164u8,35u8,225u8,131u8,79u8,123u8,216u8,40u8,4u8,54u8,56u8,211u8,123u8,249u8,15u8,242u8,120u8,127u8,48u8, - 81u8,154u8,129u8,83u8,82u8,34u8,87u8,101u8,6u8,240u8,199u8,222u8,187u8,104u8,56u8,95u8,171u8,47u8,119u8,63u8, - 5u8,195u8,126u8,207u8,47u8,173u8,132u8,47u8,129u8,221u8,49u8,197u8,205u8,97u8,227u8,146u8,242u8,212u8,130u8,204u8, - 220u8,181u8,120u8,78u8,50u8,23u8,205u8,85u8,7u8,28u8,137u8,30u8,20u8,107u8,250u8,80u8,245u8,141u8,48u8,51u8, - 39u8,54u8,25u8,213u8,185u8,47u8,92u8,218u8,239u8,235u8,12u8,187u8,14u8,250u8,104u8,192u8,53u8,98u8,25u8,213u8, - 14u8,66u8,33u8,207u8,141u8,36u8,82u8,105u8,65u8,67u8,249u8,198u8,181u8,15u8,180u8,125u8,219u8,70u8,112u8,98u8, - 164u8,88u8,124u8,110u8,142u8,237u8,201u8,8u8,15u8,28u8,83u8,223u8,5u8,251u8,173u8,195u8,70u8,178u8,74u8,86u8, - 142u8,224u8,250u8,232u8,72u8,158u8,89u8,245u8,209u8,3u8,96u8,185u8,216u8,87u8,86u8,138u8,162u8,114u8,18u8,185u8, - 91u8,216u8,72u8,146u8,137u8,206u8,127u8,88u8,152u8,37u8,118u8,60u8,82u8,161u8,199u8,54u8,130u8,216u8,9u8,88u8, - 50u8,9u8,198u8,196u8,73u8,32u8,61u8,19u8,92u8,191u8,189u8,6u8,209u8,229u8,135u8,10u8,229u8,234u8,78u8,206u8, - 87u8,162u8,124u8,65u8,164u8,187u8,133u8,45u8,108u8,42u8,209u8,227u8,133u8,106u8,4u8,206u8,110u8,42u8,129u8,1u8, - 68u8,19u8,102u8,67u8,155u8,140u8,219u8,12u8,172u8,60u8,138u8,130u8,77u8,119u8,45u8,55u8,184u8,184u8,209u8,229u8, - 88u8,200u8,229u8,82u8,244u8,9u8,56u8,185u8,242u8,112u8,195u8,149u8,64u8,231u8,84u8,52u8,223u8,32u8,253u8,65u8, - 54u8,209u8,165u8,150u8,42u8,131u8,117u8,17u8,160u8,206u8,149u8,16u8,249u8,64u8,90u8,45u8,57u8,21u8,131u8,166u8, - 228u8,37u8,75u8,105u8,199u8,123u8,188u8,2u8,37u8,109u8,218u8,53u8,128u8,254u8,91u8,172u8,204u8,160u8,149u8,169u8, - 234u8,54u8,204u8,51u8,237u8,71u8,192u8,45u8,20u8,1u8,41u8,56u8,16u8,151u8,69u8,132u8,193u8,63u8,37u8,99u8, - 153u8,200u8,59u8,88u8,76u8,82u8,174u8,154u8,108u8,128u8,2u8,36u8,214u8,22u8,117u8,229u8,218u8,129u8,65u8,70u8, - 94u8,31u8,12u8,37u8,192u8,104u8,50u8,134u8,188u8,147u8,158u8,164u8,211u8,218u8,28u8,135u8,70u8,247u8,69u8,230u8, - 65u8,72u8,22u8,19u8,60u8,255u8,132u8,147u8,82u8,40u8,71u8,18u8,113u8,254u8,53u8,56u8,114u8,155u8,4u8,68u8, - 139u8,164u8,60u8,222u8,62u8,146u8,159u8,88u8,1u8,133u8,54u8,247u8,78u8,50u8,173u8,14u8,142u8,76u8,234u8,27u8, - 171u8,168u8,147u8,192u8,84u8,231u8,219u8,188u8,239u8,249u8,21u8,76u8,30u8,164u8,52u8,12u8,252u8,16u8,118u8,8u8, - 250u8,172u8,12u8,61u8,147u8,142u8,181u8,87u8,112u8,135u8,52u8,70u8,19u8,101u8,91u8,32u8,222u8,27u8,101u8,64u8, - 40u8,78u8,36u8,6u8,125u8,52u8,0u8,161u8,224u8,78u8,59u8,191u8,153u8,227u8,143u8,71u8,106u8,27u8,100u8,75u8, - 252u8,208u8,14u8,112u8,26u8,32u8,106u8,62u8,35u8,184u8,240u8,197u8,142u8,250u8,249u8,243u8,112u8,225u8,160u8,221u8, - 26u8,89u8,41u8,76u8,75u8,179u8,179u8,146u8,29u8,17u8,181u8,215u8,135u8,247u8,33u8,250u8,76u8,201u8,156u8,38u8, - 99u8,228u8,97u8,19u8,100u8,174u8,219u8,18u8,1u8,36u8,165u8,101u8,139u8,118u8,2u8,23u8,11u8,152u8,120u8,136u8, - 232u8,145u8,69u8,127u8,128u8,95u8,131u8,96u8,222u8,78u8,44u8,186u8,25u8,63u8,177u8,118u8,63u8,246u8,225u8,141u8, - 205u8,2u8,33u8,142u8,167u8,30u8,252u8,121u8,189u8,99u8,231u8,123u8,116u8,119u8,238u8,247u8,131u8,166u8,231u8,77u8, - 178u8,83u8,229u8,116u8,70u8,165u8,115u8,73u8,124u8,138u8,145u8,50u8,173u8,246u8,4u8,26u8,145u8,95u8,17u8,165u8, - 57u8,245u8,91u8,239u8,53u8,30u8,106u8,249u8,211u8,238u8,160u8,18u8,85u8,223u8,120u8,106u8,27u8,248u8,223u8,84u8, - 30u8,50u8,138u8,131u8,86u8,167u8,129u8,140u8,73u8,10u8,171u8,180u8,130u8,214u8,82u8,212u8,43u8,177u8,134u8,171u8, - 233u8,159u8,216u8,125u8,97u8,193u8,99u8,38u8,96u8,153u8,224u8,16u8,184u8,14u8,166u8,15u8,17u8,44u8,206u8,255u8, - 224u8,138u8,99u8,137u8,244u8,97u8,40u8,146u8,165u8,184u8,40u8,132u8,114u8,145u8,160u8,132u8,195u8,190u8,52u8,142u8, - 13u8,126u8,8u8,224u8,84u8,109u8,206u8,218u8,32u8,37u8,172u8,254u8,234u8,64u8,208u8,139u8,181u8,159u8,170u8,62u8, - 110u8,34u8,43u8,189u8,201u8,240u8,16u8,40u8,10u8,144u8,198u8,37u8,126u8,24u8,162u8,235u8,79u8,95u8,104u8,187u8, - 242u8,244u8,101u8,208u8,218u8,195u8,2u8,15u8,91u8,207u8,128u8,72u8,76u8,181u8,132u8,189u8,73u8,192u8,26u8,208u8, - 113u8,135u8,68u8,189u8,202u8,175u8,209u8,224u8,241u8,17u8,51u8,128u8,56u8,144u8,119u8,78u8,170u8,197u8,61u8,6u8, - 195u8,11u8,243u8,69u8,215u8,50u8,193u8,200u8,61u8,237u8,118u8,139u8,74u8,211u8,88u8,215u8,120u8,76u8,158u8,128u8, - 15u8,155u8,33u8,178u8,109u8,34u8,58u8,6u8,134u8,137u8,224u8,46u8,96u8,1u8,30u8,125u8,84u8,98u8,94u8,9u8, - 89u8,234u8,22u8,7u8,110u8,49u8,217u8,85u8,120u8,131u8,34u8,95u8,110u8,229u8,170u8,49u8,215u8,138u8,195u8,3u8, - 71u8,4u8,193u8,60u8,109u8,59u8,113u8,117u8,98u8,131u8,227u8,144u8,46u8,164u8,64u8,28u8,146u8,65u8,255u8,189u8, - 25u8,184u8,170u8,83u8,215u8,234u8,28u8,175u8,234u8,251u8,254u8,44u8,154u8,132u8,61u8,56u8,50u8,136u8,98u8,46u8, - 151u8,140u8,199u8,239u8,247u8,5u8,20u8,118u8,169u8,173u8,162u8,181u8,170u8,184u8,189u8,50u8,223u8,13u8,218u8,106u8, - 140u8,168u8,186u8,166u8,248u8,112u8,103u8,49u8,26u8,40u8,113u8,113u8,94u8,20u8,244u8,253u8,96u8,65u8,10u8,195u8, - 135u8,158u8,36u8,209u8,182u8,239u8,202u8,218u8,125u8,198u8,90u8,32u8,48u8,107u8,170u8,142u8,250u8,35u8,227u8,77u8, - 16u8,176u8,146u8,37u8,188u8,202u8,38u8,161u8,83u8,187u8,40u8,80u8,239u8,230u8,92u8,167u8,94u8,246u8,17u8,213u8, - 4u8,58u8,84u8,123u8,229u8,112u8,8u8,197u8,6u8,44u8,252u8,68u8,147u8,11u8,61u8,68u8,135u8,84u8,4u8,112u8, - 206u8,9u8,111u8,140u8,214u8,219u8,113u8,108u8,216u8,61u8,124u8,40u8,145u8,241u8,38u8,47u8,178u8,20u8,240u8,189u8, - 110u8,252u8,247u8,123u8,30u8,140u8,63u8,74u8,6u8,80u8,156u8,37u8,209u8,126u8,112u8,168u8,149u8,172u8,177u8,203u8, - 164u8,110u8,244u8,117u8,237u8,242u8,249u8,100u8,241u8,240u8,97u8,32u8,146u8,140u8,244u8,69u8,3u8,230u8,20u8,91u8, - 98u8,66u8,47u8,73u8,137u8,225u8,20u8,25u8,208u8,70u8,10u8,148u8,235u8,195u8,40u8,100u8,42u8,115u8,200u8,231u8, - 121u8,217u8,177u8,93u8,65u8,12u8,87u8,101u8,174u8,197u8,29u8,184u8,109u8,154u8,221u8,70u8,113u8,175u8,204u8,32u8, - 148u8,54u8,52u8,235u8,71u8,154u8,229u8,52u8,208u8,195u8,209u8,243u8,132u8,161u8,193u8,220u8,37u8,133u8,90u8,112u8, - 52u8,225u8,172u8,199u8,210u8,10u8,200u8,42u8,178u8,89u8,32u8,209u8,13u8,184u8,135u8,51u8,12u8,218u8,175u8,166u8, - 98u8,154u8,54u8,168u8,2u8,251u8,162u8,75u8,136u8,189u8,136u8,92u8,74u8,122u8,196u8,243u8,251u8,193u8,17u8,125u8, - 23u8,174u8,63u8,17u8,25u8,75u8,114u8,193u8,143u8,248u8,51u8,242u8,141u8,52u8,100u8,15u8,118u8,180u8,107u8,227u8, - 81u8,170u8,163u8,32u8,119u8,112u8,138u8,30u8,5u8,105u8,68u8,225u8,21u8,59u8,10u8,117u8,69u8,66u8,213u8,142u8, - 247u8,142u8,199u8,148u8,124u8,47u8,21u8,41u8,53u8,77u8,141u8,159u8,31u8,16u8,47u8,127u8,196u8,59u8,231u8,238u8, - 84u8,198u8,248u8,225u8,126u8,52u8,74u8,52u8,252u8,229u8,211u8,168u8,149u8,204u8,179u8,204u8,164u8,106u8,12u8,21u8, - 193u8,113u8,104u8,242u8,55u8,67u8,7u8,116u8,252u8,60u8,224u8,136u8,102u8,59u8,52u8,74u8,11u8,79u8,87u8,14u8, - 119u8,3u8,14u8,215u8,236u8,218u8,140u8,207u8,136u8,167u8,227u8,113u8,213u8,119u8,87u8,95u8,198u8,152u8,220u8,68u8, - 194u8,31u8,199u8,137u8,254u8,253u8,113u8,87u8,255u8,80u8,231u8,55u8,225u8,248u8,187u8,99u8,87u8,197u8,77u8,132u8, - 7u8,247u8,24u8,124u8,63u8,40u8,170u8,125u8,64u8,4u8,157u8,47u8,205u8,214u8,193u8,54u8,172u8,225u8,48u8,212u8, - 220u8,0u8,235u8,86u8,141u8,185u8,4u8,227u8,192u8,76u8,157u8,249u8,117u8,242u8,214u8,87u8,17u8,52u8,44u8,206u8, - 27u8,221u8,105u8,93u8,211u8,54u8,93u8,49u8,121u8,33u8,183u8,97u8,75u8,88u8,7u8,230u8,117u8,3u8,119u8,127u8, - 129u8,26u8,9u8,58u8,121u8,134u8,163u8,192u8,1u8,146u8,58u8,116u8,183u8,0u8,227u8,64u8,197u8,103u8,223u8,67u8, - 170u8,43u8,46u8,47u8,128u8,0u8,40u8,205u8,216u8,50u8,47u8,195u8,215u8,185u8,196u8,21u8,233u8,76u8,223u8,229u8, - 73u8,113u8,25u8,242u8,120u8,55u8,150u8,139u8,48u8,252u8,176u8,13u8,40u8,192u8,162u8,236u8,143u8,245u8,45u8,28u8, - 121u8,113u8,5u8,106u8,103u8,0u8,176u8,226u8,196u8,13u8,59u8,58u8,236u8,182u8,42u8,58u8,41u8,24u8,62u8,202u8, - 43u8,186u8,203u8,252u8,186u8,107u8,228u8,205u8,40u8,160u8,112u8,213u8,119u8,29u8,175u8,172u8,163u8,237u8,113u8,204u8, - 132u8,127u8,13u8,218u8,29u8,158u8,182u8,203u8,227u8,175u8,3u8,242u8,175u8,199u8,228u8,143u8,63u8,250u8,167u8,39u8, - 206u8,211u8,247u8,206u8,211u8,83u8,231u8,233u8,7u8,120u8,114u8,45u8,75u8,127u8,121u8,230u8,140u8,251u8,209u8,121u8, - 122u8,238u8,60u8,253u8,211u8,121u8,122u8,252u8,157u8,111u8,39u8,175u8,88u8,171u8,202u8,134u8,181u8,172u8,128u8,188u8, - 187u8,26u8,116u8,88u8,173u8,15u8,26u8,37u8,119u8,193u8,124u8,85u8,155u8,123u8,40u8,222u8,59u8,200u8,218u8,9u8, - 223u8,239u8,95u8,117u8,239u8,172u8,184u8,71u8,2u8,145u8,219u8,58u8,246u8,3u8,158u8,95u8,132u8,203u8,67u8,162u8, - 17u8,119u8,13u8,121u8,171u8,77u8,222u8,87u8,255u8,27u8,89u8,173u8,153u8,6u8,51u8,88u8,162u8,188u8,75u8,209u8, - 223u8,189u8,44u8,42u8,154u8,233u8,187u8,187u8,165u8,25u8,62u8,232u8,130u8,247u8,93u8,70u8,217u8,228u8,29u8,100u8, - 83u8,246u8,177u8,102u8,120u8,31u8,61u8,241u8,111u8,82u8,171u8,237u8,233u8,71u8,46u8,32u8,74u8,21u8,193u8,190u8, - 111u8,60u8,118u8,155u8,52u8,114u8,110u8,237u8,173u8,68u8,75u8,150u8,180u8,219u8,90u8,58u8,227u8,157u8,199u8,4u8, - 246u8,173u8,86u8,43u8,3u8,224u8,217u8,65u8,224u8,98u8,241u8,92u8,46u8,109u8,236u8,110u8,241u8,248u8,157u8,232u8, - 145u8,91u8,201u8,228u8,84u8,157u8,192u8,231u8,50u8,216u8,9u8,19u8,84u8,5u8,133u8,232u8,200u8,171u8,74u8,191u8, - 255u8,55u8,10u8,86u8,89u8,3u8,235u8,115u8,232u8,239u8,172u8,143u8,250u8,127u8,32u8,96u8,110u8,216u8,241u8,26u8, - 90u8,200u8,24u8,88u8,187u8,218u8,185u8,81u8,137u8,65u8,25u8,95u8,227u8,24u8,211u8,145u8,64u8,130u8,14u8,183u8, - 13u8,43u8,138u8,189u8,155u8,18u8,246u8,156u8,152u8,91u8,45u8,125u8,216u8,199u8,144u8,9u8,213u8,59u8,140u8,18u8, - 157u8,56u8,72u8,76u8,0u8,150u8,76u8,164u8,111u8,228u8,237u8,171u8,178u8,130u8,96u8,137u8,29u8,144u8,170u8,51u8, - 205u8,12u8,62u8,13u8,89u8,239u8,216u8,53u8,242u8,251u8,27u8,49u8,30u8,177u8,165u8,69u8,151u8,201u8,11u8,149u8, - 186u8,47u8,98u8,193u8,41u8,211u8,85u8,152u8,222u8,97u8,239u8,129u8,67u8,174u8,191u8,208u8,3u8,108u8,186u8,161u8, - 110u8,197u8,159u8,245u8,149u8,79u8,15u8,254u8,15u8,11u8,232u8,25u8,159u8,240u8,54u8,0u8,0u8,0u8,0u8,16u8, - 99u8,111u8,110u8,115u8,101u8,110u8,115u8,117u8,115u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,201u8,4u8,31u8,139u8, - 8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,181u8,84u8,239u8,203u8,211u8,48u8,16u8,254u8,190u8,191u8,226u8,252u8, - 50u8,58u8,153u8,155u8,130u8,136u8,100u8,175u8,67u8,153u8,63u8,24u8,232u8,20u8,17u8,191u8,150u8,52u8,189u8,117u8, - 97u8,109u8,50u8,147u8,203u8,94u8,234u8,203u8,254u8,119u8,211u8,166u8,237u8,182u8,76u8,17u8,4u8,3u8,165u8,37u8, - 119u8,125u8,158u8,187u8,231u8,201u8,101u8,62u8,159u8,195u8,39u8,46u8,21u8,249u8,199u8,2u8,237u8,16u8,132u8,86u8, - 22u8,149u8,117u8,182u8,249u8,218u8,202u8,2u8,182u8,218u8,180u8,129u8,172u8,212u8,98u8,47u8,118u8,62u8,113u8,6u8, - 223u8,66u8,98u8,19u8,150u8,22u8,44u8,105u8,131u8,57u8,72u8,5u8,124u8,52u8,247u8,128u8,95u8,49u8,196u8,156u8, - 225u8,36u8,181u8,154u8,2u8,87u8,57u8,84u8,188u8,134u8,12u8,193u8,29u8,114u8,78u8,62u8,57u8,171u8,193u8,104u8, - 77u8,179u8,81u8,165u8,115u8,87u8,34u8,240u8,3u8,105u8,155u8,110u8,13u8,175u8,240u8,94u8,155u8,61u8,99u8,67u8, - 29u8,105u8,71u8,244u8,48u8,2u8,191u8,156u8,69u8,207u8,151u8,51u8,134u8,198u8,104u8,179u8,184u8,222u8,59u8,162u8, - 160u8,102u8,115u8,216u8,189u8,65u8,53u8,215u8,165u8,45u8,254u8,156u8,105u8,107u8,75u8,88u8,165u8,60u8,207u8,13u8, - 90u8,139u8,182u8,67u8,221u8,26u8,137u8,190u8,155u8,155u8,244u8,2u8,21u8,90u8,217u8,103u8,89u8,50u8,78u8,16u8, - 172u8,250u8,30u8,86u8,161u8,133u8,29u8,183u8,176u8,199u8,186u8,107u8,165u8,89u8,161u8,26u8,6u8,161u8,242u8,59u8, - 247u8,114u8,57u8,109u8,99u8,167u8,128u8,211u8,168u8,217u8,104u8,125u8,48u8,250u8,40u8,115u8,47u8,155u8,86u8,208u8, - 90u8,208u8,171u8,159u8,213u8,132u8,22u8,184u8,65u8,192u8,234u8,64u8,53u8,120u8,175u8,164u8,58u8,242u8,82u8,230u8, - 163u8,14u8,222u8,18u8,188u8,91u8,111u8,190u8,191u8,249u8,184u8,126u8,155u8,174u8,62u8,111u8,222u8,175u8,63u8,48u8, - 112u8,47u8,158u8,195u8,43u8,120u8,182u8,56u8,147u8,124u8,113u8,89u8,41u8,237u8,14u8,195u8,25u8,136u8,235u8,14u8, - 100u8,179u8,54u8,251u8,208u8,100u8,138u8,36u8,232u8,48u8,129u8,173u8,83u8,158u8,81u8,146u8,244u8,156u8,63u8,49u8, - 137u8,117u8,129u8,177u8,149u8,133u8,66u8,51u8,253u8,77u8,167u8,147u8,11u8,29u8,98u8,181u8,25u8,227u8,254u8,101u8, - 40u8,141u8,0u8,99u8,130u8,201u8,98u8,128u8,8u8,63u8,60u8,74u8,2u8,1u8,99u8,37u8,170u8,130u8,118u8,201u8, - 56u8,16u8,79u8,96u8,9u8,79u8,167u8,208u8,158u8,26u8,198u8,58u8,141u8,82u8,110u8,10u8,87u8,161u8,162u8,36u8, - 146u8,104u8,114u8,129u8,91u8,233u8,35u8,166u8,164u8,99u8,230u8,233u8,141u8,74u8,15u8,189u8,41u8,167u8,238u8,239u8, - 43u8,19u8,253u8,156u8,8u8,174u8,154u8,17u8,16u8,188u8,44u8,195u8,4u8,104u8,245u8,36u8,184u8,89u8,120u8,14u8, - 163u8,184u8,18u8,8u8,164u8,187u8,25u8,57u8,71u8,227u8,145u8,180u8,151u8,78u8,180u8,14u8,88u8,164u8,132u8,11u8, - 161u8,157u8,162u8,191u8,72u8,206u8,197u8,15u8,39u8,189u8,198u8,183u8,213u8,255u8,131u8,23u8,129u8,241u8,63u8,122u8, - 48u8,0u8,151u8,72u8,93u8,59u8,169u8,193u8,173u8,63u8,188u8,227u8,202u8,17u8,100u8,218u8,67u8,221u8,167u8,69u8, - 169u8,51u8,94u8,166u8,126u8,231u8,46u8,234u8,106u8,153u8,188u8,142u8,15u8,204u8,44u8,192u8,156u8,107u8,126u8,124u8, - 133u8,219u8,71u8,135u8,176u8,55u8,111u8,131u8,222u8,46u8,239u8,11u8,25u8,89u8,20u8,104u8,32u8,186u8,68u8,192u8, - 106u8,104u8,91u8,225u8,190u8,103u8,80u8,58u8,199u8,96u8,181u8,173u8,149u8,104u8,6u8,182u8,153u8,168u8,254u8,218u8, - 187u8,50u8,176u8,89u8,17u8,214u8,229u8,13u8,133u8,201u8,112u8,142u8,78u8,163u8,95u8,11u8,54u8,239u8,241u8,169u8, - 5u8,0u8,0u8,0u8,0u8,15u8,100u8,101u8,108u8,101u8,103u8,97u8,116u8,105u8,111u8,110u8,95u8,112u8,111u8,111u8, - 108u8,214u8,161u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,125u8,107u8,119u8,219u8,214u8, - 177u8,232u8,247u8,252u8,10u8,52u8,119u8,37u8,37u8,19u8,202u8,6u8,192u8,135u8,72u8,57u8,246u8,58u8,170u8,173u8, - 52u8,94u8,117u8,164u8,172u8,200u8,105u8,79u8,239u8,89u8,189u8,20u8,72u8,130u8,18u8,27u8,146u8,208u8,33u8,72u8, - 203u8,234u8,57u8,249u8,239u8,119u8,102u8,191u8,159u8,0u8,8u8,66u8,178u8,157u8,90u8,109u8,18u8,137u8,4u8,246u8, - 99u8,246u8,204u8,236u8,121u8,207u8,211u8,111u8,190u8,249u8,34u8,248u8,38u8,56u8,93u8,46u8,179u8,187u8,96u8,181u8, - 91u8,110u8,23u8,183u8,203u8,52u8,152u8,165u8,203u8,244u8,58u8,217u8,102u8,155u8,60u8,216u8,102u8,193u8,109u8,178u8, - 217u8,46u8,166u8,139u8,219u8,100u8,155u8,6u8,139u8,117u8,176u8,189u8,73u8,131u8,60u8,89u8,193u8,191u8,182u8,201u8, - 175u8,105u8,112u8,155u8,101u8,75u8,252u8,56u8,219u8,204u8,210u8,13u8,62u8,62u8,205u8,150u8,203u8,116u8,186u8,37u8, - 207u8,173u8,22u8,235u8,197u8,106u8,183u8,194u8,41u8,232u8,211u8,155u8,244u8,191u8,119u8,139u8,77u8,58u8,195u8,7u8, - 255u8,153u8,177u8,209u8,222u8,37u8,203u8,197u8,12u8,167u8,11u8,242u8,116u8,251u8,36u8,120u8,37u8,103u8,79u8,54u8, - 248u8,202u8,93u8,2u8,99u8,207u8,130u8,108u8,183u8,13u8,178u8,185u8,241u8,6u8,253u8,54u8,199u8,25u8,110u8,55u8, - 217u8,109u8,6u8,75u8,205u8,214u8,201u8,114u8,121u8,143u8,51u8,192u8,163u8,139u8,13u8,155u8,57u8,89u8,207u8,240u8, - 137u8,119u8,11u8,28u8,74u8,223u8,195u8,209u8,42u8,89u8,39u8,215u8,233u8,42u8,93u8,111u8,131u8,211u8,159u8,94u8, - 7u8,73u8,78u8,31u8,144u8,251u8,203u8,238u8,214u8,233u8,230u8,9u8,76u8,130u8,243u8,188u8,197u8,141u8,37u8,176u8, - 246u8,100u8,58u8,205u8,118u8,235u8,237u8,98u8,125u8,29u8,44u8,179u8,235u8,197u8,148u8,67u8,135u8,129u8,15u8,22u8, - 66u8,223u8,158u8,102u8,235u8,237u8,38u8,1u8,144u8,220u8,192u8,34u8,150u8,41u8,29u8,125u8,158u8,33u8,204u8,225u8, - 221u8,19u8,28u8,51u8,122u8,18u8,188u8,133u8,71u8,126u8,205u8,131u8,27u8,114u8,16u8,211u8,27u8,54u8,125u8,154u8, - 192u8,175u8,226u8,60u8,112u8,33u8,121u8,7u8,246u8,177u8,120u8,7u8,167u8,1u8,155u8,156u8,165u8,183u8,89u8,190u8, - 216u8,194u8,150u8,96u8,209u8,119u8,233u8,114u8,137u8,255u8,77u8,147u8,205u8,58u8,157u8,61u8,33u8,7u8,43u8,151u8, - 184u8,88u8,207u8,22u8,176u8,251u8,93u8,178u8,84u8,198u8,35u8,147u8,228u8,193u8,2u8,64u8,61u8,189u8,89u8,164u8, - 239u8,8u8,108u8,54u8,217u8,238u8,250u8,134u8,130u8,224u8,6u8,78u8,32u8,63u8,154u8,36u8,57u8,124u8,65u8,246u8, - 50u8,75u8,231u8,139u8,53u8,78u8,183u8,197u8,225u8,175u8,146u8,219u8,109u8,150u8,143u8,243u8,237u8,236u8,228u8,4u8, - 191u8,30u8,239u8,6u8,189u8,171u8,78u8,112u8,147u8,174u8,167u8,26u8,22u8,193u8,178u8,217u8,88u8,193u8,38u8,129u8, - 129u8,1u8,81u8,0u8,22u8,65u8,50u8,201u8,179u8,229u8,110u8,155u8,138u8,101u8,172u8,233u8,161u8,153u8,16u8,36u8, - 91u8,137u8,5u8,136u8,216u8,153u8,179u8,125u8,6u8,147u8,123u8,227u8,184u8,58u8,193u8,98u8,117u8,187u8,4u8,148u8, - 221u8,2u8,132u8,216u8,183u8,202u8,136u8,217u8,58u8,237u8,240u8,163u8,90u8,165u8,9u8,64u8,103u8,149u8,226u8,4u8, - 136u8,33u8,179u8,69u8,190u8,221u8,44u8,38u8,184u8,38u8,248u8,122u8,69u8,142u8,120u8,51u8,3u8,240u8,45u8,239u8, - 201u8,34u8,186u8,98u8,17u8,203u8,108u8,250u8,235u8,238u8,54u8,152u8,222u8,79u8,241u8,68u8,179u8,181u8,137u8,50u8, - 42u8,73u8,228u8,41u8,208u8,16u8,37u8,31u8,64u8,131u8,197u8,59u8,254u8,96u8,107u8,157u8,109u8,201u8,46u8,240u8, - 124u8,216u8,174u8,218u8,56u8,205u8,124u8,147u8,173u8,130u8,219u8,116u8,141u8,51u8,143u8,205u8,151u8,204u8,23u8,200u8, - 194u8,19u8,66u8,193u8,139u8,109u8,110u8,16u8,239u8,221u8,98u8,123u8,51u8,219u8,36u8,119u8,12u8,231u8,54u8,43u8, - 138u8,199u8,65u8,207u8,70u8,184u8,105u8,182u8,90u8,45u8,242u8,28u8,65u8,52u8,79u8,83u8,64u8,86u8,50u8,192u8, - 4u8,246u8,147u8,44u8,102u8,140u8,156u8,130u8,236u8,54u8,221u8,80u8,76u8,164u8,212u8,184u8,88u8,195u8,107u8,202u8, - 106u8,224u8,121u8,152u8,133u8,192u8,83u8,192u8,18u8,191u8,38u8,208u8,100u8,99u8,192u8,49u8,167u8,27u8,32u8,210u8, - 128u8,163u8,12u8,249u8,37u8,231u8,244u8,245u8,90u8,129u8,28u8,142u8,1u8,175u8,239u8,22u8,249u8,13u8,140u8,188u8, - 189u8,75u8,211u8,181u8,68u8,22u8,248u8,118u8,62u8,79u8,55u8,72u8,182u8,240u8,217u8,22u8,62u8,67u8,72u8,0u8, - 246u8,110u8,83u8,177u8,28u8,229u8,0u8,59u8,132u8,13u8,241u8,163u8,208u8,231u8,38u8,156u8,102u8,135u8,88u8,14u8, - 171u8,87u8,73u8,133u8,194u8,156u8,14u8,79u8,40u8,149u8,252u8,0u8,185u8,2u8,22u8,225u8,254u8,175u8,232u8,217u8, - 92u8,5u8,223u8,6u8,87u8,252u8,192u8,248u8,71u8,228u8,93u8,241u8,78u8,44u8,223u8,225u8,39u8,202u8,30u8,9u8, - 190u8,191u8,248u8,153u8,18u8,250u8,109u8,146u8,3u8,84u8,39u8,121u8,186u8,65u8,74u8,84u8,81u8,44u8,104u8,93u8, - 188u8,121u8,217u8,182u8,241u8,76u8,12u8,223u8,149u8,195u8,155u8,136u8,195u8,167u8,201u8,167u8,55u8,233u8,108u8,183u8, - 132u8,145u8,103u8,187u8,13u8,61u8,148u8,5u8,162u8,238u8,117u8,134u8,127u8,192u8,248u8,236u8,0u8,78u8,115u8,198u8, - 23u8,201u8,182u8,3u8,224u8,93u8,107u8,224u8,49u8,128u8,23u8,12u8,192u8,28u8,180u8,0u8,50u8,56u8,252u8,219u8, - 29u8,178u8,159u8,108u8,13u8,180u8,150u8,108u8,77u8,42u8,88u8,2u8,75u8,1u8,122u8,116u8,208u8,52u8,78u8,116u8, - 157u8,2u8,190u8,194u8,113u8,1u8,43u8,7u8,126u8,21u8,188u8,205u8,224u8,194u8,216u8,46u8,174u8,201u8,164u8,176u8, - 180u8,14u8,14u8,152u8,172u8,239u8,41u8,194u8,224u8,94u8,224u8,93u8,68u8,104u8,215u8,120u8,240u8,48u8,242u8,247u8, - 105u8,154u8,231u8,8u8,132u8,252u8,126u8,61u8,5u8,70u8,182u8,94u8,252u8,139u8,60u8,130u8,179u8,49u8,236u8,219u8, - 173u8,1u8,183u8,150u8,247u8,184u8,101u8,149u8,92u8,129u8,157u8,188u8,79u8,167u8,100u8,47u8,20u8,133u8,201u8,195u8, - 120u8,95u8,165u8,57u8,217u8,32u8,65u8,124u8,156u8,13u8,136u8,44u8,93u8,206u8,57u8,182u8,158u8,110u8,205u8,185u8, - 40u8,154u8,28u8,177u8,209u8,103u8,233u8,187u8,69u8,66u8,129u8,199u8,17u8,24u8,7u8,222u8,222u8,101u8,10u8,230u8, - 193u8,222u8,118u8,244u8,210u8,186u8,145u8,168u8,11u8,187u8,153u8,237u8,166u8,48u8,181u8,193u8,171u8,158u8,176u8,241u8, - 241u8,51u8,131u8,102u8,201u8,46u8,200u8,93u8,131u8,152u8,140u8,76u8,68u8,25u8,176u8,195u8,254u8,192u8,187u8,75u8, - 238u8,30u8,94u8,145u8,76u8,111u8,102u8,146u8,40u8,185u8,87u8,13u8,74u8,89u8,147u8,251u8,115u8,109u8,206u8,79u8, - 135u8,35u8,52u8,132u8,108u8,99u8,119u8,207u8,57u8,62u8,82u8,20u8,103u8,27u8,124u8,237u8,139u8,57u8,0u8,102u8, - 11u8,98u8,2u8,69u8,67u8,134u8,56u8,12u8,225u8,211u8,247u8,183u8,68u8,70u8,176u8,144u8,221u8,137u8,72u8,128u8, - 17u8,75u8,60u8,191u8,108u8,73u8,56u8,236u8,150u8,202u8,2u8,30u8,222u8,185u8,70u8,38u8,201u8,62u8,163u8,140u8, - 115u8,186u8,73u8,241u8,189u8,36u8,88u8,167u8,119u8,114u8,171u8,176u8,131u8,155u8,12u8,104u8,113u8,190u8,219u8,238u8, - 224u8,124u8,220u8,195u8,17u8,126u8,34u8,104u8,138u8,16u8,19u8,12u8,2u8,167u8,8u8,223u8,110u8,182u8,130u8,128u8, - 201u8,158u8,95u8,137u8,221u8,38u8,218u8,54u8,249u8,149u8,100u8,18u8,14u8,0u8,110u8,183u8,74u8,9u8,7u8,150u8, - 128u8,194u8,37u8,234u8,75u8,16u8,88u8,40u8,239u8,121u8,34u8,154u8,44u8,214u8,239u8,146u8,205u8,2u8,240u8,37u8, - 231u8,216u8,72u8,248u8,139u8,37u8,238u8,72u8,172u8,110u8,37u8,179u8,217u8,211u8,221u8,26u8,215u8,246u8,20u8,64u8, - 130u8,115u8,0u8,92u8,158u8,242u8,251u8,131u8,2u8,75u8,112u8,255u8,41u8,92u8,223u8,215u8,200u8,26u8,22u8,215u8, - 215u8,233u8,134u8,128u8,156u8,108u8,64u8,167u8,4u8,65u8,144u8,140u8,162u8,40u8,133u8,17u8,97u8,100u8,75u8,133u8, - 130u8,249u8,110u8,77u8,200u8,250u8,137u8,186u8,74u8,224u8,68u8,176u8,139u8,91u8,178u8,121u8,194u8,211u8,54u8,193u8, - 42u8,35u8,146u8,32u8,48u8,100u8,10u8,188u8,220u8,133u8,28u8,128u8,193u8,1u8,222u8,168u8,244u8,158u8,162u8,130u8, - 209u8,19u8,0u8,13u8,165u8,173u8,14u8,227u8,81u8,100u8,24u8,156u8,142u8,29u8,3u8,97u8,40u8,127u8,36u8,27u8, - 94u8,108u8,239u8,255u8,24u8,180u8,238u8,110u8,22u8,176u8,8u8,6u8,103u8,228u8,76u8,65u8,158u8,173u8,144u8,111u8, - 224u8,11u8,130u8,112u8,218u8,98u8,243u8,148u8,16u8,16u8,141u8,200u8,81u8,206u8,25u8,46u8,193u8,46u8,248u8,174u8, - 228u8,171u8,0u8,196u8,167u8,176u8,29u8,250u8,1u8,142u8,76u8,150u8,159u8,193u8,72u8,48u8,31u8,129u8,12u8,161u8, - 90u8,121u8,177u8,105u8,123u8,70u8,86u8,177u8,6u8,142u8,10u8,240u8,88u8,188u8,39u8,116u8,6u8,28u8,20u8,165u8, - 41u8,20u8,156u8,196u8,25u8,160u8,92u8,203u8,17u8,25u8,152u8,42u8,162u8,37u8,162u8,3u8,145u8,152u8,153u8,240u8, - 169u8,72u8,174u8,176u8,94u8,186u8,97u8,121u8,129u8,47u8,51u8,36u8,74u8,144u8,29u8,110u8,178u8,21u8,223u8,194u8, - 58u8,83u8,164u8,198u8,41u8,200u8,110u8,55u8,201u8,187u8,180u8,116u8,99u8,45u8,70u8,56u8,66u8,12u8,73u8,150u8, - 32u8,175u8,104u8,55u8,55u8,128u8,41u8,103u8,171u8,76u8,215u8,57u8,16u8,27u8,65u8,38u8,69u8,136u8,153u8,101u8, - 228u8,80u8,201u8,124u8,176u8,168u8,95u8,211u8,244u8,22u8,175u8,164u8,233u8,175u8,92u8,25u8,192u8,17u8,96u8,173u8, - 148u8,35u8,221u8,139u8,37u8,60u8,9u8,254u8,134u8,159u8,209u8,131u8,33u8,116u8,71u8,40u8,220u8,90u8,80u8,135u8, - 3u8,46u8,125u8,79u8,133u8,13u8,130u8,115u8,234u8,125u8,208u8,226u8,15u8,175u8,219u8,200u8,184u8,80u8,88u8,94u8, - 194u8,168u8,179u8,123u8,65u8,140u8,28u8,74u8,87u8,64u8,68u8,99u8,178u8,245u8,43u8,228u8,199u8,140u8,185u8,47u8, - 239u8,146u8,123u8,148u8,90u8,231u8,120u8,245u8,204u8,40u8,158u8,82u8,92u8,156u8,111u8,137u8,40u8,12u8,115u8,223u8, - 102u8,112u8,4u8,114u8,19u8,100u8,183u8,19u8,188u8,44u8,128u8,210u8,54u8,215u8,176u8,8u8,88u8,119u8,206u8,167u8, - 225u8,235u8,193u8,197u8,250u8,56u8,29u8,25u8,75u8,234u8,73u8,55u8,201u8,44u8,184u8,38u8,27u8,227u8,207u8,49u8, - 154u8,68u8,74u8,212u8,249u8,46u8,145u8,64u8,103u8,25u8,172u8,30u8,225u8,46u8,233u8,128u8,92u8,197u8,76u8,153u8, - 154u8,205u8,22u8,84u8,209u8,226u8,23u8,134u8,114u8,81u8,174u8,201u8,57u8,209u8,195u8,36u8,180u8,124u8,151u8,237u8, - 150u8,51u8,50u8,88u8,50u8,123u8,151u8,160u8,106u8,0u8,2u8,22u8,234u8,17u8,11u8,212u8,97u8,212u8,235u8,204u8, - 62u8,29u8,122u8,42u8,57u8,149u8,4u8,200u8,104u8,128u8,57u8,134u8,66u8,68u8,233u8,147u8,221u8,54u8,248u8,42u8, - 215u8,82u8,233u8,39u8,84u8,131u8,155u8,211u8,107u8,134u8,81u8,37u8,206u8,121u8,246u8,62u8,89u8,161u8,182u8,59u8, - 7u8,201u8,153u8,43u8,97u8,167u8,176u8,204u8,153u8,34u8,229u8,210u8,219u8,1u8,166u8,183u8,110u8,29u8,208u8,43u8, - 166u8,192u8,86u8,112u8,181u8,87u8,184u8,147u8,5u8,128u8,250u8,95u8,233u8,88u8,62u8,54u8,198u8,199u8,174u8,8u8, - 219u8,4u8,181u8,150u8,96u8,53u8,194u8,218u8,184u8,177u8,1u8,82u8,225u8,87u8,65u8,11u8,239u8,200u8,156u8,107u8, - 45u8,247u8,109u8,92u8,136u8,46u8,156u8,208u8,133u8,204u8,40u8,183u8,2u8,98u8,100u8,83u8,42u8,202u8,45u8,234u8, - 168u8,168u8,12u8,17u8,85u8,6u8,239u8,143u8,108u8,183u8,153u8,10u8,82u8,167u8,74u8,40u8,170u8,230u8,51u8,135u8, - 74u8,164u8,42u8,89u8,66u8,253u8,134u8,53u8,192u8,57u8,231u8,65u8,20u8,134u8,108u8,26u8,198u8,25u8,113u8,19u8, - 107u8,224u8,66u8,76u8,94u8,32u8,223u8,83u8,80u8,11u8,5u8,142u8,225u8,24u8,103u8,67u8,76u8,117u8,186u8,224u8, - 96u8,69u8,213u8,63u8,183u8,117u8,127u8,135u8,230u8,141u8,90u8,8u8,94u8,218u8,138u8,53u8,65u8,94u8,190u8,132u8, - 189u8,105u8,143u8,51u8,149u8,80u8,136u8,168u8,235u8,25u8,121u8,27u8,71u8,137u8,97u8,153u8,218u8,213u8,25u8,156u8, - 254u8,49u8,23u8,159u8,208u8,245u8,35u8,185u8,222u8,129u8,13u8,225u8,134u8,60u8,61u8,37u8,171u8,76u8,196u8,237u8, - 102u8,11u8,91u8,225u8,87u8,4u8,106u8,253u8,4u8,15u8,140u8,145u8,64u8,30u8,92u8,81u8,22u8,116u8,69u8,36u8, - 31u8,1u8,61u8,242u8,224u8,228u8,73u8,112u8,105u8,220u8,145u8,244u8,122u8,167u8,235u8,59u8,34u8,143u8,179u8,53u8, - 241u8,77u8,80u8,168u8,227u8,210u8,128u8,95u8,2u8,129u8,2u8,227u8,104u8,1u8,206u8,200u8,213u8,180u8,241u8,156u8, - 164u8,236u8,166u8,192u8,158u8,174u8,110u8,250u8,132u8,12u8,75u8,183u8,243u8,60u8,104u8,225u8,31u8,223u8,224u8,71u8, - 237u8,224u8,41u8,153u8,246u8,121u8,208u8,15u8,85u8,16u8,0u8,19u8,72u8,65u8,62u8,80u8,68u8,72u8,101u8,76u8, - 2u8,212u8,244u8,61u8,149u8,0u8,102u8,114u8,143u8,228u8,117u8,130u8,229u8,28u8,9u8,44u8,214u8,68u8,46u8,243u8, - 53u8,129u8,251u8,36u8,189u8,73u8,150u8,115u8,124u8,124u8,160u8,162u8,220u8,159u8,40u8,202u8,197u8,37u8,40u8,215u8, - 138u8,201u8,22u8,250u8,100u8,7u8,17u8,217u8,65u8,9u8,22u8,226u8,76u8,199u8,251u8,160u8,203u8,64u8,30u8,4u8, - 126u8,140u8,19u8,186u8,89u8,45u8,129u8,241u8,208u8,192u8,0u8,41u8,65u8,137u8,235u8,192u8,196u8,133u8,97u8,1u8, - 46u8,12u8,8u8,46u8,116u8,237u8,37u8,80u8,4u8,177u8,22u8,98u8,163u8,202u8,36u8,3u8,36u8,230u8,248u8,162u8, - 105u8,227u8,2u8,81u8,192u8,52u8,6u8,186u8,49u8,0u8,236u8,54u8,163u8,92u8,151u8,200u8,247u8,100u8,105u8,13u8, - 96u8,140u8,181u8,70u8,55u8,238u8,224u8,116u8,124u8,120u8,122u8,156u8,3u8,50u8,124u8,220u8,247u8,157u8,166u8,19u8, - 137u8,70u8,244u8,104u8,181u8,139u8,204u8,41u8,35u8,138u8,235u8,140u8,25u8,37u8,24u8,140u8,10u8,36u8,252u8,40u8, - 132u8,179u8,253u8,83u8,21u8,234u8,142u8,194u8,130u8,35u8,5u8,158u8,205u8,55u8,176u8,241u8,30u8,32u8,149u8,13u8, - 224u8,152u8,95u8,144u8,145u8,237u8,197u8,56u8,118u8,148u8,195u8,101u8,32u8,110u8,86u8,202u8,63u8,221u8,22u8,4u8, - 169u8,237u8,224u8,125u8,38u8,84u8,29u8,220u8,6u8,190u8,132u8,210u8,17u8,125u8,158u8,72u8,68u8,212u8,134u8,151u8, - 48u8,35u8,37u8,72u8,242u8,160u8,199u8,102u8,187u8,220u8,1u8,40u8,122u8,76u8,212u8,84u8,196u8,241u8,128u8,130u8, - 195u8,131u8,70u8,199u8,125u8,60u8,231u8,99u8,227u8,156u8,107u8,48u8,30u8,58u8,139u8,77u8,247u8,138u8,160u8,103u8, - 49u8,159u8,63u8,105u8,120u8,19u8,69u8,251u8,240u8,4u8,34u8,103u8,248u8,206u8,142u8,240u8,129u8,40u8,54u8,24u8, - 1u8,151u8,100u8,40u8,186u8,160u8,0u8,192u8,16u8,206u8,129u8,104u8,113u8,49u8,250u8,216u8,218u8,31u8,69u8,151u8, - 194u8,85u8,73u8,150u8,224u8,102u8,2u8,192u8,74u8,215u8,179u8,182u8,155u8,90u8,233u8,162u8,166u8,244u8,162u8,148u8, - 244u8,14u8,7u8,217u8,87u8,216u8,65u8,159u8,115u8,95u8,118u8,93u8,58u8,143u8,177u8,21u8,229u8,219u8,118u8,96u8, - 243u8,2u8,141u8,126u8,128u8,203u8,19u8,91u8,19u8,104u8,6u8,204u8,69u8,112u8,10u8,31u8,63u8,253u8,98u8,149u8, - 161u8,134u8,29u8,80u8,91u8,243u8,124u8,3u8,250u8,13u8,220u8,209u8,191u8,158u8,156u8,24u8,178u8,86u8,240u8,63u8, - 95u8,160u8,33u8,12u8,44u8,16u8,1u8,49u8,72u8,195u8,40u8,217u8,230u8,153u8,254u8,217u8,28u8,208u8,31u8,117u8, - 12u8,227u8,227u8,124u8,113u8,13u8,6u8,126u8,227u8,195u8,119u8,0u8,120u8,28u8,64u8,124u8,234u8,48u8,118u8,143u8, - 119u8,235u8,9u8,136u8,87u8,240u8,201u8,255u8,92u8,130u8,81u8,8u8,5u8,24u8,254u8,21u8,24u8,43u8,178u8,109u8, - 178u8,28u8,19u8,176u8,252u8,246u8,204u8,53u8,202u8,54u8,153u8,44u8,83u8,246u8,106u8,39u8,120u8,139u8,127u8,253u8, - 102u8,205u8,167u8,108u8,152u8,201u8,114u8,207u8,10u8,158u8,32u8,31u8,224u8,148u8,39u8,39u8,167u8,248u8,251u8,75u8, - 248u8,181u8,224u8,249u8,105u8,241u8,215u8,160u8,107u8,174u8,183u8,98u8,129u8,103u8,248u8,215u8,15u8,196u8,135u8,241u8, - 91u8,193u8,75u8,228u8,60u8,75u8,190u8,71u8,92u8,131u8,27u8,125u8,190u8,184u8,46u8,120u8,16u8,141u8,93u8,240u8, - 240u8,234u8,150u8,193u8,4u8,94u8,0u8,147u8,204u8,143u8,23u8,175u8,126u8,121u8,115u8,54u8,190u8,60u8,125u8,243u8, - 246u8,36u8,160u8,103u8,244u8,221u8,110u8,248u8,2u8,144u8,112u8,242u8,101u8,25u8,138u8,124u8,201u8,70u8,122u8,250u8, - 244u8,41u8,23u8,58u8,132u8,64u8,79u8,124u8,60u8,32u8,211u8,223u8,38u8,147u8,197u8,18u8,132u8,112u8,169u8,2u8, - 17u8,21u8,132u8,27u8,55u8,133u8,15u8,137u8,29u8,198u8,19u8,101u8,105u8,103u8,23u8,127u8,59u8,63u8,251u8,121u8, - 252u8,242u8,244u8,167u8,241u8,249u8,197u8,219u8,241u8,247u8,23u8,191u8,156u8,191u8,58u8,9u8,144u8,201u8,2u8,133u8, - 40u8,83u8,51u8,107u8,141u8,170u8,73u8,194u8,236u8,84u8,73u8,181u8,165u8,114u8,215u8,240u8,167u8,111u8,126u8,62u8, - 59u8,125u8,245u8,247u8,241u8,217u8,127u8,190u8,190u8,124u8,123u8,201u8,231u8,136u8,11u8,182u8,87u8,178u8,27u8,74u8, - 145u8,179u8,25u8,208u8,70u8,174u8,205u8,249u8,234u8,236u8,205u8,217u8,159u8,79u8,223u8,190u8,190u8,56u8,31u8,255u8, - 116u8,113u8,241u8,102u8,252u8,234u8,226u8,236u8,146u8,236u8,142u8,76u8,205u8,103u8,238u8,42u8,51u8,3u8,63u8,221u8, - 16u8,233u8,56u8,113u8,233u8,115u8,244u8,158u8,48u8,109u8,173u8,252u8,82u8,165u8,166u8,132u8,123u8,194u8,235u8,184u8, - 116u8,37u8,215u8,242u8,211u8,217u8,249u8,171u8,215u8,231u8,127u8,30u8,255u8,237u8,245u8,219u8,31u8,94u8,253u8,124u8, - 250u8,183u8,211u8,55u8,6u8,0u8,122u8,202u8,50u8,94u8,74u8,105u8,29u8,212u8,142u8,41u8,160u8,46u8,24u8,189u8, - 20u8,175u8,6u8,183u8,198u8,134u8,132u8,17u8,93u8,253u8,120u8,250u8,159u8,227u8,239u8,207u8,206u8,174u8,168u8,136u8, - 245u8,149u8,54u8,239u8,235u8,243u8,191u8,158u8,190u8,121u8,253u8,106u8,252u8,242u8,226u8,199u8,31u8,95u8,95u8,94u8, - 18u8,88u8,156u8,253u8,252u8,242u8,236u8,252u8,237u8,233u8,159u8,207u8,248u8,220u8,125u8,23u8,8u8,8u8,192u8,215u8, - 196u8,159u8,118u8,165u8,27u8,227u8,109u8,147u8,31u8,44u8,139u8,131u8,65u8,155u8,157u8,0u8,251u8,252u8,226u8,151u8, - 63u8,255u8,48u8,62u8,125u8,249u8,246u8,245u8,95u8,1u8,247u8,223u8,158u8,254u8,229u8,108u8,252u8,246u8,98u8,252u8, - 203u8,249u8,155u8,139u8,151u8,127u8,225u8,43u8,24u8,40u8,43u8,184u8,92u8,38u8,249u8,13u8,66u8,179u8,133u8,22u8, - 11u8,212u8,115u8,209u8,222u8,7u8,202u8,61u8,112u8,113u8,161u8,142u8,79u8,144u8,232u8,64u8,231u8,164u8,204u8,150u8, - 163u8,162u8,233u8,158u8,120u8,34u8,6u8,61u8,207u8,152u8,225u8,98u8,177u8,45u8,49u8,41u8,249u8,29u8,25u8,57u8, - 245u8,100u8,116u8,196u8,160u8,120u8,133u8,17u8,66u8,0u8,65u8,102u8,183u8,158u8,39u8,11u8,234u8,248u8,145u8,6u8, - 32u8,48u8,67u8,229u8,28u8,80u8,120u8,124u8,196u8,44u8,98u8,154u8,49u8,20u8,61u8,146u8,25u8,95u8,225u8,240u8, - 241u8,158u8,127u8,7u8,43u8,146u8,59u8,56u8,21u8,134u8,11u8,52u8,8u8,82u8,75u8,183u8,102u8,55u8,17u8,100u8, - 66u8,41u8,148u8,157u8,210u8,187u8,108u8,75u8,5u8,228u8,59u8,96u8,17u8,176u8,61u8,49u8,155u8,118u8,80u8,151u8, - 111u8,78u8,47u8,127u8,56u8,123u8,53u8,126u8,125u8,174u8,157u8,19u8,98u8,203u8,233u8,229u8,219u8,49u8,108u8,155u8, - 159u8,212u8,177u8,77u8,168u8,217u8,70u8,106u8,163u8,147u8,100u8,73u8,204u8,36u8,96u8,103u8,99u8,199u8,180u8,68u8, - 43u8,42u8,241u8,153u8,94u8,253u8,248u8,250u8,28u8,16u8,241u8,245u8,249u8,37u8,14u8,124u8,249u8,195u8,233u8,207u8, - 64u8,143u8,72u8,153u8,87u8,46u8,178u8,189u8,248u8,153u8,227u8,204u8,159u8,78u8,223u8,156u8,158u8,191u8,68u8,172u8, - 185u8,24u8,191u8,185u8,248u8,27u8,95u8,201u8,208u8,179u8,18u8,75u8,32u8,104u8,116u8,77u8,156u8,144u8,5u8,164u8, - 60u8,171u8,27u8,169u8,244u8,204u8,13u8,121u8,6u8,135u8,204u8,37u8,157u8,225u8,5u8,58u8,11u8,238u8,211u8,109u8, - 17u8,7u8,187u8,28u8,191u8,122u8,125u8,121u8,250u8,167u8,55u8,103u8,146u8,53u8,135u8,234u8,52u8,116u8,127u8,76u8, - 130u8,211u8,92u8,168u8,255u8,74u8,55u8,153u8,131u8,43u8,113u8,110u8,52u8,254u8,191u8,103u8,63u8,95u8,208u8,51u8, - 23u8,35u8,71u8,250u8,213u8,5u8,60u8,230u8,151u8,65u8,79u8,124u8,59u8,236u8,245u8,6u8,199u8,189u8,94u8,120u8, - 220u8,61u8,14u8,71u8,253u8,126u8,52u8,136u8,84u8,38u8,242u8,99u8,242u8,158u8,152u8,59u8,132u8,49u8,74u8,97u8, - 99u8,96u8,127u8,104u8,1u8,30u8,206u8,178u8,221u8,4u8,227u8,53u8,192u8,124u8,188u8,69u8,17u8,125u8,186u8,32u8, - 246u8,128u8,147u8,32u8,142u8,159u8,12u8,251u8,95u8,33u8,100u8,54u8,41u8,124u8,158u8,19u8,218u8,71u8,233u8,36u8, - 142u8,135u8,125u8,99u8,69u8,192u8,245u8,36u8,36u8,224u8,71u8,91u8,50u8,97u8,124u8,228u8,204u8,96u8,99u8,111u8, - 127u8,185u8,20u8,71u8,230u8,98u8,185u8,151u8,183u8,176u8,2u8,52u8,74u8,161u8,160u8,120u8,147u8,45u8,137u8,19u8, - 55u8,93u8,221u8,102u8,27u8,112u8,70u8,44u8,197u8,37u8,135u8,20u8,101u8,89u8,74u8,185u8,157u8,83u8,117u8,77u8, - 18u8,171u8,168u8,36u8,222u8,139u8,53u8,245u8,15u8,168u8,239u8,74u8,7u8,6u8,94u8,27u8,232u8,53u8,89u8,18u8, - 223u8,23u8,181u8,207u8,168u8,222u8,40u8,93u8,93u8,161u8,90u8,15u8,60u8,162u8,172u8,85u8,206u8,244u8,253u8,98u8, - 147u8,91u8,126u8,61u8,97u8,174u8,229u8,43u8,35u8,54u8,89u8,234u8,126u8,82u8,226u8,6u8,128u8,17u8,238u8,86u8, - 187u8,101u8,194u8,150u8,145u8,75u8,11u8,105u8,46u8,226u8,87u8,232u8,109u8,203u8,109u8,194u8,218u8,133u8,123u8,254u8, - 203u8,155u8,55u8,148u8,132u8,126u8,184u8,120u8,243u8,234u8,236u8,231u8,19u8,126u8,43u8,3u8,172u8,255u8,35u8,124u8, - 175u8,162u8,234u8,143u8,204u8,30u8,70u8,5u8,111u8,180u8,185u8,146u8,171u8,29u8,87u8,170u8,217u8,62u8,209u8,118u8, - 10u8,107u8,36u8,226u8,148u8,220u8,227u8,217u8,26u8,96u8,128u8,238u8,69u8,128u8,160u8,98u8,80u8,69u8,192u8,16u8, - 227u8,129u8,98u8,121u8,112u8,106u8,7u8,202u8,80u8,196u8,122u8,79u8,150u8,191u8,1u8,231u8,211u8,150u8,27u8,242u8, - 165u8,17u8,22u8,35u8,86u8,36u8,239u8,200u8,222u8,161u8,3u8,22u8,67u8,7u8,128u8,164u8,115u8,58u8,197u8,18u8, - 174u8,158u8,0u8,157u8,24u8,108u8,200u8,124u8,55u8,129u8,19u8,94u8,19u8,27u8,231u8,18u8,248u8,63u8,179u8,240u8, - 195u8,65u8,18u8,15u8,12u8,221u8,93u8,11u8,29u8,17u8,248u8,45u8,110u8,113u8,133u8,254u8,186u8,136u8,205u8,71u8, - 181u8,158u8,85u8,114u8,47u8,175u8,25u8,110u8,217u8,67u8,255u8,219u8,22u8,237u8,248u8,211u8,221u8,134u8,198u8,14u8, - 40u8,43u8,132u8,125u8,220u8,44u8,174u8,111u8,218u8,254u8,189u8,9u8,102u8,195u8,160u8,151u8,173u8,117u8,181u8,38u8, - 103u8,70u8,204u8,123u8,102u8,226u8,39u8,113u8,25u8,84u8,33u8,202u8,197u8,160u8,174u8,133u8,227u8,41u8,194u8,131u8, - 215u8,160u8,181u8,45u8,153u8,19u8,219u8,177u8,194u8,182u8,138u8,43u8,62u8,182u8,171u8,145u8,115u8,168u8,208u8,52u8, - 33u8,82u8,48u8,91u8,19u8,71u8,83u8,50u8,37u8,6u8,244u8,185u8,138u8,43u8,140u8,100u8,20u8,35u8,186u8,229u8, - 182u8,23u8,179u8,179u8,9u8,47u8,95u8,2u8,139u8,0u8,94u8,254u8,61u8,240u8,133u8,139u8,159u8,237u8,169u8,237u8, - 21u8,188u8,148u8,50u8,54u8,177u8,201u8,11u8,22u8,149u8,83u8,33u8,28u8,4u8,151u8,91u8,130u8,36u8,36u8,208u8, - 105u8,1u8,243u8,171u8,254u8,119u8,97u8,214u8,113u8,58u8,242u8,41u8,120u8,224u8,176u8,118u8,211u8,173u8,34u8,3u8, - 255u8,4u8,95u8,93u8,136u8,177u8,81u8,138u8,248u8,53u8,5u8,1u8,32u8,223u8,162u8,236u8,64u8,245u8,70u8,190u8, - 58u8,96u8,252u8,84u8,76u8,229u8,116u8,199u8,104u8,214u8,178u8,168u8,43u8,220u8,76u8,141u8,199u8,96u8,63u8,68u8, - 13u8,100u8,67u8,8u8,26u8,166u8,82u8,207u8,111u8,95u8,168u8,139u8,188u8,96u8,82u8,210u8,27u8,34u8,36u8,189u8, - 36u8,166u8,26u8,92u8,224u8,52u8,187u8,133u8,21u8,206u8,54u8,217u8,173u8,189u8,78u8,244u8,159u8,188u8,39u8,160u8, - 118u8,141u8,168u8,111u8,155u8,239u8,86u8,219u8,102u8,112u8,169u8,251u8,70u8,42u8,132u8,177u8,176u8,31u8,250u8,233u8, - 152u8,226u8,204u8,137u8,208u8,118u8,79u8,78u8,112u8,174u8,142u8,58u8,197u8,107u8,92u8,164u8,138u8,198u8,110u8,179u8, - 84u8,230u8,14u8,154u8,203u8,23u8,196u8,81u8,180u8,205u8,133u8,107u8,85u8,140u8,205u8,199u8,25u8,211u8,113u8,198u8, - 100u8,156u8,19u8,23u8,32u8,59u8,158u8,45u8,231u8,206u8,48u8,156u8,140u8,221u8,47u8,41u8,186u8,233u8,136u8,175u8, - 137u8,104u8,6u8,190u8,112u8,26u8,182u8,112u8,177u8,187u8,53u8,19u8,11u8,216u8,116u8,44u8,188u8,138u8,17u8,87u8, - 54u8,55u8,68u8,90u8,197u8,99u8,166u8,56u8,71u8,209u8,55u8,202u8,125u8,224u8,202u8,105u8,27u8,64u8,39u8,150u8, - 130u8,239u8,92u8,251u8,53u8,206u8,227u8,133u8,6u8,128u8,31u8,65u8,216u8,151u8,46u8,102u8,193u8,241u8,57u8,158u8, - 179u8,123u8,10u8,247u8,141u8,129u8,93u8,219u8,220u8,165u8,190u8,1u8,223u8,164u8,50u8,53u8,238u8,87u8,34u8,59u8, - 3u8,145u8,178u8,19u8,190u8,74u8,142u8,249u8,174u8,227u8,209u8,151u8,119u8,73u8,140u8,48u8,170u8,6u8,94u8,143u8, - 242u8,200u8,71u8,68u8,221u8,31u8,83u8,187u8,206u8,24u8,134u8,60u8,225u8,47u8,159u8,156u8,208u8,121u8,36u8,19u8, - 210u8,22u8,241u8,22u8,109u8,54u8,160u8,52u8,137u8,24u8,17u8,122u8,185u8,102u8,150u8,71u8,49u8,167u8,28u8,10u8, - 47u8,86u8,129u8,47u8,185u8,24u8,73u8,49u8,253u8,8u8,188u8,81u8,232u8,149u8,77u8,246u8,82u8,119u8,40u8,169u8, - 113u8,118u8,186u8,27u8,146u8,5u8,219u8,177u8,200u8,16u8,22u8,241u8,202u8,169u8,129u8,61u8,52u8,150u8,14u8,161u8, - 177u8,148u8,20u8,217u8,164u8,218u8,22u8,209u8,234u8,253u8,142u8,176u8,219u8,116u8,181u8,216u8,110u8,169u8,71u8,208u8, - 31u8,26u8,146u8,123u8,72u8,84u8,178u8,4u8,46u8,142u8,141u8,233u8,176u8,39u8,170u8,165u8,232u8,59u8,80u8,185u8, - 46u8,241u8,75u8,242u8,153u8,114u8,226u8,166u8,139u8,196u8,249u8,242u8,207u8,226u8,33u8,231u8,24u8,84u8,93u8,46u8, - 120u8,255u8,23u8,242u8,128u8,243u8,93u8,142u8,171u8,5u8,111u8,255u8,141u8,61u8,226u8,124u8,95u8,138u8,124u8,42u8, - 228u8,93u8,195u8,188u8,18u8,79u8,202u8,3u8,87u8,71u8,211u8,185u8,183u8,6u8,47u8,194u8,188u8,221u8,119u8,64u8, - 193u8,5u8,67u8,150u8,199u8,233u8,187u8,224u8,153u8,100u8,133u8,20u8,49u8,38u8,222u8,67u8,3u8,59u8,229u8,153u8, - 2u8,102u8,122u8,111u8,26u8,215u8,241u8,60u8,202u8,146u8,37u8,242u8,204u8,188u8,139u8,51u8,207u8,254u8,81u8,22u8, - 198u8,163u8,79u8,188u8,171u8,178u8,113u8,234u8,81u8,214u8,37u8,66u8,88u8,252u8,82u8,131u8,15u8,75u8,235u8,175u8, - 143u8,179u8,38u8,199u8,87u8,10u8,201u8,56u8,185u8,163u8,198u8,204u8,244u8,251u8,215u8,222u8,193u8,255u8,249u8,175u8, - 119u8,139u8,244u8,238u8,31u8,66u8,192u8,252u8,57u8,5u8,35u8,255u8,26u8,53u8,6u8,18u8,29u8,149u8,239u8,152u8, - 145u8,139u8,223u8,116u8,168u8,64u8,110u8,174u8,80u8,148u8,167u8,70u8,95u8,18u8,179u8,229u8,52u8,188u8,222u8,130u8, - 126u8,189u8,152u8,98u8,152u8,26u8,125u8,20u8,175u8,146u8,49u8,13u8,73u8,193u8,128u8,57u8,185u8,51u8,208u8,185u8, - 39u8,210u8,7u8,129u8,63u8,244u8,169u8,239u8,60u8,50u8,232u8,11u8,242u8,122u8,187u8,194u8,46u8,12u8,49u8,116u8, - 230u8,48u8,91u8,19u8,6u8,126u8,69u8,22u8,120u8,133u8,238u8,69u8,48u8,162u8,145u8,155u8,122u8,43u8,205u8,144u8, - 92u8,56u8,81u8,54u8,116u8,157u8,110u8,199u8,228u8,237u8,177u8,122u8,140u8,45u8,50u8,140u8,186u8,47u8,190u8,128u8, - 100u8,74u8,114u8,55u8,114u8,175u8,88u8,45u8,247u8,158u8,228u8,112u8,215u8,211u8,209u8,53u8,144u8,145u8,15u8,218u8, - 207u8,196u8,115u8,19u8,240u8,63u8,103u8,119u8,227u8,235u8,101u8,6u8,230u8,37u8,63u8,168u8,232u8,107u8,79u8,212u8, - 101u8,238u8,113u8,252u8,118u8,236u8,142u8,140u8,41u8,242u8,160u8,134u8,5u8,43u8,195u8,143u8,112u8,8u8,10u8,84u8, - 63u8,121u8,106u8,158u8,172u8,42u8,68u8,155u8,155u8,188u8,82u8,193u8,101u8,239u8,200u8,41u8,68u8,183u8,156u8,4u8, - 221u8,166u8,74u8,157u8,231u8,252u8,237u8,99u8,247u8,0u8,75u8,29u8,187u8,34u8,14u8,188u8,208u8,95u8,122u8,226u8, - 92u8,244u8,19u8,2u8,163u8,138u8,240u8,148u8,145u8,93u8,78u8,247u8,0u8,6u8,33u8,121u8,20u8,146u8,50u8,120u8, - 22u8,138u8,97u8,31u8,61u8,96u8,11u8,87u8,95u8,17u8,182u8,235u8,221u8,106u8,66u8,185u8,169u8,158u8,250u8,67u8, - 156u8,58u8,170u8,145u8,157u8,153u8,23u8,74u8,32u8,170u8,88u8,230u8,208u8,165u8,73u8,36u8,21u8,170u8,7u8,225u8, - 123u8,31u8,2u8,162u8,82u8,185u8,178u8,215u8,214u8,250u8,122u8,31u8,120u8,107u8,10u8,93u8,85u8,94u8,192u8,98u8, - 253u8,200u8,133u8,78u8,132u8,114u8,29u8,128u8,34u8,253u8,204u8,200u8,138u8,57u8,145u8,102u8,39u8,174u8,230u8,119u8, - 164u8,238u8,219u8,49u8,245u8,253u8,142u8,173u8,240u8,182u8,93u8,87u8,135u8,9u8,58u8,178u8,58u8,239u8,169u8,180u8, - 136u8,71u8,92u8,251u8,87u8,251u8,208u8,227u8,32u8,51u8,158u8,156u8,224u8,98u8,236u8,201u8,219u8,123u8,220u8,16u8, - 8u8,181u8,107u8,216u8,232u8,90u8,209u8,138u8,81u8,232u8,65u8,147u8,178u8,80u8,105u8,209u8,208u8,206u8,2u8,7u8, - 95u8,207u8,169u8,133u8,15u8,12u8,238u8,0u8,230u8,229u8,189u8,162u8,203u8,111u8,51u8,166u8,148u8,72u8,75u8,102u8, - 38u8,114u8,37u8,89u8,234u8,132u8,162u8,180u8,254u8,81u8,79u8,36u8,99u8,225u8,142u8,91u8,170u8,82u8,118u8,68u8, - 148u8,22u8,15u8,172u8,192u8,175u8,180u8,5u8,9u8,95u8,135u8,113u8,56u8,182u8,66u8,222u8,58u8,92u8,168u8,36u8, - 143u8,224u8,89u8,78u8,72u8,224u8,17u8,57u8,194u8,7u8,162u8,180u8,37u8,240u8,97u8,194u8,117u8,159u8,239u8,195u8, - 198u8,244u8,247u8,229u8,142u8,201u8,39u8,202u8,141u8,49u8,166u8,224u8,213u8,190u8,31u8,235u8,127u8,154u8,52u8,80u8, - 248u8,112u8,129u8,176u8,42u8,158u8,107u8,195u8,86u8,192u8,242u8,58u8,37u8,158u8,129u8,177u8,98u8,163u8,152u8,109u8, - 22u8,243u8,45u8,217u8,71u8,251u8,217u8,23u8,250u8,6u8,228u8,233u8,49u8,112u8,117u8,20u8,83u8,204u8,56u8,91u8, - 78u8,113u8,80u8,251u8,168u8,85u8,216u8,118u8,236u8,243u8,84u8,192u8,4u8,50u8,98u8,235u8,15u8,214u8,139u8,42u8, - 97u8,50u8,155u8,1u8,60u8,184u8,206u8,92u8,193u8,243u8,138u8,140u8,153u8,46u8,88u8,46u8,2u8,227u8,245u8,107u8, - 87u8,160u8,155u8,110u8,76u8,196u8,159u8,214u8,28u8,172u8,69u8,96u8,185u8,10u8,219u8,226u8,227u8,223u8,130u8,20u8, - 62u8,178u8,87u8,161u8,83u8,167u8,57u8,159u8,119u8,58u8,240u8,41u8,145u8,64u8,235u8,100u8,183u8,205u8,86u8,128u8, - 59u8,83u8,213u8,226u8,166u8,35u8,73u8,186u8,53u8,173u8,109u8,0u8,97u8,22u8,166u8,67u8,17u8,177u8,245u8,53u8, - 209u8,14u8,140u8,167u8,172u8,131u8,121u8,166u8,141u8,139u8,112u8,214u8,31u8,160u8,66u8,75u8,240u8,29u8,213u8,53u8, - 10u8,228u8,26u8,243u8,48u8,228u8,129u8,200u8,1u8,77u8,46u8,66u8,13u8,82u8,14u8,153u8,144u8,228u8,109u8,45u8, - 136u8,19u8,195u8,66u8,80u8,113u8,30u8,160u8,14u8,106u8,134u8,68u8,230u8,249u8,109u8,89u8,91u8,182u8,49u8,171u8, - 173u8,141u8,230u8,60u8,70u8,23u8,113u8,33u8,22u8,235u8,87u8,107u8,62u8,222u8,102u8,99u8,69u8,117u8,29u8,43u8, - 6u8,181u8,150u8,53u8,158u8,195u8,72u8,218u8,113u8,62u8,100u8,206u8,82u8,101u8,79u8,238u8,145u8,208u8,81u8,240u8, - 126u8,186u8,220u8,169u8,86u8,58u8,111u8,12u8,30u8,141u8,39u8,80u8,226u8,151u8,25u8,106u8,1u8,247u8,118u8,47u8, - 211u8,28u8,232u8,168u8,18u8,139u8,17u8,172u8,230u8,89u8,77u8,140u8,185u8,56u8,127u8,243u8,247u8,64u8,164u8,242u8, - 153u8,24u8,67u8,34u8,52u8,252u8,104u8,227u8,96u8,176u8,214u8,70u8,12u8,252u8,248u8,66u8,255u8,173u8,68u8,2u8, - 34u8,150u8,89u8,102u8,122u8,23u8,74u8,175u8,117u8,94u8,87u8,92u8,186u8,44u8,150u8,223u8,197u8,232u8,220u8,224u8, - 206u8,76u8,222u8,122u8,234u8,47u8,74u8,79u8,37u8,82u8,83u8,177u8,144u8,84u8,32u8,20u8,117u8,10u8,110u8,90u8, - 75u8,96u8,250u8,29u8,93u8,180u8,251u8,221u8,167u8,174u8,167u8,31u8,232u8,186u8,165u8,44u8,198u8,100u8,254u8,146u8, - 101u8,208u8,239u8,25u8,227u8,160u8,183u8,128u8,46u8,198u8,235u8,80u8,81u8,78u8,215u8,59u8,164u8,127u8,48u8,247u8, - 189u8,173u8,131u8,125u8,220u8,1u8,192u8,5u8,186u8,248u8,14u8,31u8,225u8,214u8,139u8,69u8,99u8,93u8,0u8,208u8, - 7u8,8u8,158u8,63u8,135u8,171u8,216u8,190u8,123u8,73u8,240u8,139u8,219u8,55u8,72u8,136u8,102u8,202u8,34u8,193u8, - 192u8,168u8,194u8,46u8,102u8,42u8,25u8,80u8,177u8,213u8,142u8,247u8,192u8,232u8,109u8,125u8,6u8,17u8,98u8,78u8, - 131u8,43u8,200u8,21u8,79u8,216u8,18u8,73u8,161u8,162u8,81u8,107u8,139u8,25u8,134u8,79u8,207u8,69u8,110u8,177u8, - 244u8,4u8,229u8,41u8,203u8,184u8,195u8,168u8,138u8,37u8,198u8,21u8,204u8,180u8,9u8,220u8,167u8,235u8,250u8,244u8, - 168u8,226u8,1u8,153u8,145u8,26u8,142u8,59u8,223u8,58u8,67u8,132u8,174u8,245u8,158u8,227u8,134u8,244u8,35u8,79u8, - 232u8,99u8,161u8,207u8,12u8,23u8,109u8,229u8,75u8,21u8,249u8,156u8,113u8,169u8,186u8,246u8,251u8,69u8,133u8,245u8, - 117u8,190u8,40u8,187u8,35u8,107u8,222u8,140u8,174u8,123u8,208u8,197u8,26u8,28u8,231u8,249u8,133u8,114u8,45u8,170u8, - 110u8,48u8,160u8,13u8,202u8,226u8,41u8,126u8,209u8,172u8,201u8,208u8,50u8,155u8,182u8,69u8,204u8,142u8,37u8,252u8, - 122u8,133u8,117u8,161u8,52u8,168u8,82u8,33u8,203u8,26u8,126u8,238u8,211u8,207u8,84u8,10u8,45u8,150u8,218u8,97u8, - 241u8,155u8,20u8,75u8,221u8,224u8,2u8,143u8,84u8,73u8,87u8,171u8,119u8,193u8,93u8,137u8,116u8,147u8,21u8,119u8, - 33u8,151u8,110u8,221u8,221u8,176u8,116u8,67u8,142u8,149u8,95u8,181u8,204u8,141u8,162u8,60u8,79u8,197u8,191u8,86u8, - 232u8,0u8,131u8,126u8,20u8,140u8,184u8,153u8,128u8,65u8,49u8,70u8,177u8,207u8,113u8,100u8,129u8,171u8,122u8,138u8, - 241u8,66u8,108u8,43u8,10u8,78u8,17u8,11u8,140u8,58u8,162u8,248u8,78u8,45u8,86u8,33u8,170u8,44u8,56u8,138u8, - 20u8,136u8,60u8,179u8,5u8,166u8,73u8,206u8,118u8,83u8,216u8,186u8,182u8,68u8,212u8,226u8,23u8,43u8,17u8,99u8, - 23u8,180u8,80u8,249u8,95u8,188u8,19u8,169u8,206u8,52u8,201u8,117u8,2u8,224u8,94u8,46u8,174u8,111u8,208u8,40u8, - 176u8,20u8,86u8,8u8,54u8,6u8,70u8,186u8,98u8,230u8,172u8,82u8,45u8,37u8,227u8,41u8,43u8,34u8,249u8,88u8, - 84u8,101u8,128u8,63u8,177u8,156u8,79u8,112u8,157u8,101u8,51u8,12u8,211u8,221u8,100u8,239u8,23u8,43u8,61u8,16u8, - 194u8,203u8,98u8,20u8,246u8,207u8,33u8,161u8,223u8,0u8,38u8,207u8,17u8,44u8,131u8,253u8,242u8,173u8,77u8,104u8, - 207u8,44u8,157u8,112u8,125u8,52u8,39u8,123u8,181u8,229u8,85u8,229u8,240u8,48u8,210u8,105u8,154u8,209u8,200u8,52u8, - 146u8,188u8,106u8,230u8,78u8,187u8,80u8,145u8,111u8,206u8,150u8,37u8,92u8,204u8,82u8,81u8,35u8,196u8,175u8,223u8, - 86u8,150u8,152u8,247u8,210u8,82u8,204u8,143u8,246u8,152u8,70u8,229u8,212u8,226u8,247u8,22u8,167u8,182u8,2u8,186u8, - 171u8,32u8,27u8,211u8,56u8,66u8,105u8,162u8,18u8,81u8,246u8,90u8,45u8,16u8,154u8,37u8,122u8,69u8,111u8,128u8, - 43u8,116u8,127u8,184u8,35u8,41u8,139u8,44u8,222u8,56u8,233u8,107u8,179u8,8u8,23u8,171u8,84u8,34u8,43u8,51u8, - 201u8,24u8,201u8,14u8,155u8,149u8,46u8,235u8,26u8,163u8,171u8,103u8,32u8,198u8,78u8,183u8,148u8,63u8,89u8,2u8, - 5u8,240u8,98u8,49u8,143u8,76u8,89u8,72u8,54u8,130u8,11u8,60u8,9u8,126u8,200u8,238u8,176u8,6u8,66u8,135u8, - 196u8,94u8,232u8,89u8,223u8,9u8,11u8,120u8,188u8,37u8,116u8,138u8,108u8,111u8,183u8,94u8,96u8,213u8,35u8,140u8, - 196u8,3u8,46u8,207u8,18u8,236u8,181u8,0u8,78u8,105u8,30u8,61u8,226u8,146u8,11u8,91u8,178u8,204u8,75u8,163u8, - 180u8,232u8,90u8,44u8,124u8,133u8,21u8,26u8,8u8,124u8,5u8,81u8,207u8,37u8,57u8,95u8,217u8,81u8,84u8,116u8, - 42u8,98u8,196u8,0u8,12u8,90u8,101u8,239u8,40u8,51u8,114u8,196u8,25u8,225u8,248u8,120u8,77u8,205u8,119u8,75u8, - 17u8,247u8,1u8,250u8,57u8,154u8,30u8,103u8,139u8,252u8,118u8,153u8,76u8,201u8,121u8,106u8,178u8,24u8,158u8,42u8, - 203u8,34u8,104u8,203u8,32u8,70u8,171u8,178u8,14u8,225u8,103u8,140u8,145u8,170u8,39u8,164u8,85u8,144u8,193u8,249u8, - 231u8,168u8,104u8,97u8,181u8,5u8,153u8,214u8,203u8,130u8,87u8,201u8,252u8,212u8,6u8,46u8,38u8,226u8,201u8,132u8, - 18u8,17u8,236u8,138u8,51u8,148u8,83u8,146u8,205u8,179u8,92u8,59u8,32u8,117u8,230u8,218u8,21u8,3u8,137u8,69u8, - 28u8,97u8,100u8,204u8,115u8,32u8,20u8,130u8,181u8,120u8,70u8,234u8,55u8,237u8,224u8,27u8,54u8,215u8,17u8,86u8, - 117u8,250u8,138u8,101u8,147u8,126u8,133u8,0u8,102u8,140u8,239u8,72u8,82u8,232u8,87u8,78u8,29u8,77u8,11u8,117u8, - 240u8,233u8,106u8,116u8,122u8,226u8,93u8,168u8,236u8,99u8,64u8,14u8,198u8,56u8,241u8,2u8,188u8,4u8,212u8,129u8, - 55u8,38u8,152u8,55u8,22u8,196u8,83u8,204u8,151u8,201u8,157u8,204u8,128u8,57u8,198u8,29u8,118u8,2u8,245u8,47u8, - 80u8,246u8,214u8,88u8,126u8,11u8,7u8,226u8,98u8,191u8,76u8,173u8,162u8,23u8,0u8,125u8,158u8,60u8,222u8,250u8, - 218u8,241u8,64u8,171u8,237u8,16u8,92u8,125u8,147u8,4u8,47u8,108u8,229u8,160u8,182u8,238u8,121u8,168u8,14u8,106u8, - 13u8,164u8,174u8,26u8,6u8,211u8,254u8,4u8,196u8,96u8,193u8,238u8,76u8,192u8,47u8,241u8,121u8,57u8,214u8,233u8, - 5u8,202u8,115u8,255u8,87u8,223u8,240u8,16u8,123u8,123u8,184u8,22u8,252u8,48u8,172u8,6u8,106u8,218u8,69u8,241u8, - 16u8,177u8,89u8,3u8,189u8,248u8,2u8,19u8,45u8,91u8,238u8,175u8,224u8,14u8,242u8,31u8,151u8,120u8,191u8,77u8, - 126u8,5u8,228u8,117u8,222u8,125u8,65u8,168u8,222u8,79u8,230u8,135u8,213u8,156u8,40u8,222u8,56u8,73u8,148u8,0u8, - 38u8,169u8,100u8,251u8,34u8,20u8,133u8,112u8,77u8,49u8,156u8,179u8,166u8,151u8,82u8,200u8,15u8,173u8,51u8,122u8, - 38u8,55u8,149u8,110u8,89u8,206u8,1u8,74u8,134u8,218u8,88u8,181u8,106u8,159u8,88u8,206u8,20u8,88u8,188u8,16u8, - 210u8,173u8,11u8,222u8,235u8,237u8,50u8,226u8,0u8,20u8,113u8,76u8,172u8,138u8,168u8,93u8,134u8,94u8,142u8,162u8, - 155u8,55u8,225u8,34u8,248u8,250u8,107u8,93u8,213u8,225u8,217u8,145u8,39u8,39u8,107u8,32u8,153u8,60u8,5u8,154u8, - 158u8,229u8,173u8,118u8,240u8,66u8,147u8,255u8,152u8,8u8,5u8,95u8,231u8,126u8,255u8,24u8,185u8,213u8,69u8,17u8, - 19u8,71u8,196u8,4u8,9u8,64u8,200u8,193u8,124u8,30u8,204u8,73u8,229u8,161u8,171u8,98u8,226u8,81u8,132u8,133u8, - 83u8,59u8,124u8,83u8,41u8,103u8,66u8,238u8,77u8,30u8,185u8,66u8,163u8,53u8,201u8,173u8,67u8,14u8,155u8,199u8, - 102u8,92u8,153u8,108u8,133u8,71u8,4u8,195u8,174u8,210u8,153u8,52u8,229u8,241u8,162u8,93u8,46u8,239u8,189u8,92u8, - 4u8,45u8,149u8,82u8,22u8,65u8,78u8,242u8,77u8,244u8,96u8,244u8,60u8,221u8,138u8,170u8,1u8,124u8,251u8,79u8, - 223u8,101u8,152u8,19u8,2u8,59u8,186u8,222u8,36u8,107u8,166u8,195u8,242u8,253u8,96u8,148u8,47u8,73u8,88u8,195u8, - 116u8,138u8,77u8,182u8,36u8,41u8,234u8,4u8,200u8,164u8,234u8,141u8,68u8,52u8,0u8,218u8,230u8,158u8,160u8,155u8, - 191u8,146u8,140u8,84u8,208u8,89u8,128u8,206u8,215u8,20u8,92u8,157u8,253u8,2u8,65u8,13u8,5u8,222u8,13u8,82u8, - 53u8,175u8,150u8,190u8,98u8,251u8,106u8,255u8,208u8,226u8,185u8,212u8,86u8,150u8,109u8,62u8,102u8,169u8,88u8,173u8, - 118u8,39u8,32u8,89u8,216u8,112u8,1u8,210u8,116u8,65u8,134u8,245u8,254u8,156u8,172u8,182u8,97u8,69u8,163u8,33u8, - 68u8,50u8,5u8,134u8,110u8,250u8,228u8,132u8,125u8,50u8,206u8,230u8,86u8,80u8,17u8,95u8,223u8,31u8,220u8,225u8, - 71u8,210u8,176u8,207u8,215u8,198u8,52u8,18u8,254u8,148u8,55u8,205u8,182u8,237u8,152u8,164u8,24u8,232u8,193u8,119u8, - 207u8,57u8,255u8,183u8,32u8,145u8,108u8,174u8,119u8,24u8,104u8,219u8,42u8,78u8,47u8,109u8,91u8,102u8,139u8,117u8, - 74u8,249u8,127u8,128u8,71u8,197u8,36u8,125u8,174u8,209u8,178u8,106u8,118u8,206u8,168u8,105u8,164u8,13u8,142u8,191u8, - 190u8,80u8,94u8,4u8,57u8,25u8,246u8,57u8,67u8,129u8,147u8,147u8,116u8,117u8,187u8,189u8,71u8,76u8,104u8,233u8, - 38u8,8u8,174u8,168u8,179u8,236u8,252u8,60u8,89u8,130u8,168u8,194u8,24u8,43u8,169u8,20u8,181u8,155u8,224u8,72u8, - 192u8,213u8,208u8,245u8,246u8,46u8,91u8,204u8,208u8,210u8,3u8,26u8,35u8,150u8,51u8,32u8,154u8,96u8,70u8,238u8, - 13u8,250u8,126u8,46u8,11u8,117u8,153u8,11u8,151u8,22u8,28u8,190u8,36u8,144u8,239u8,129u8,19u8,183u8,190u8,94u8, - 237u8,232u8,106u8,59u8,106u8,82u8,184u8,123u8,149u8,201u8,90u8,45u8,90u8,69u8,22u8,203u8,205u8,9u8,162u8,104u8, - 154u8,13u8,177u8,68u8,38u8,104u8,211u8,98u8,92u8,148u8,109u8,85u8,88u8,80u8,33u8,125u8,89u8,150u8,92u8,43u8, - 100u8,189u8,227u8,142u8,98u8,111u8,19u8,13u8,157u8,197u8,177u8,211u8,197u8,140u8,249u8,170u8,199u8,236u8,11u8,138u8, - 229u8,157u8,128u8,77u8,36u8,141u8,230u8,88u8,19u8,96u8,147u8,94u8,3u8,142u8,167u8,155u8,239u8,68u8,113u8,128u8, - 23u8,84u8,40u8,212u8,166u8,50u8,205u8,51u8,230u8,247u8,52u8,231u8,109u8,146u8,202u8,240u8,73u8,35u8,149u8,25u8, - 153u8,44u8,17u8,240u8,145u8,143u8,95u8,177u8,219u8,136u8,176u8,83u8,25u8,118u8,127u8,101u8,249u8,32u8,138u8,201u8, - 220u8,185u8,76u8,227u8,134u8,85u8,216u8,39u8,125u8,154u8,44u8,208u8,241u8,42u8,152u8,167u8,58u8,58u8,111u8,49u8, - 254u8,52u8,143u8,200u8,235u8,21u8,94u8,167u8,119u8,149u8,50u8,49u8,84u8,234u8,97u8,175u8,194u8,84u8,134u8,217u8, - 21u8,241u8,167u8,208u8,145u8,9u8,158u8,99u8,106u8,186u8,37u8,190u8,225u8,86u8,104u8,120u8,39u8,229u8,156u8,12u8, - 57u8,200u8,179u8,57u8,205u8,248u8,26u8,211u8,140u8,175u8,150u8,51u8,111u8,171u8,237u8,52u8,147u8,162u8,114u8,10u8, - 86u8,98u8,39u8,0u8,189u8,106u8,143u8,180u8,39u8,57u8,178u8,132u8,246u8,94u8,151u8,177u8,127u8,119u8,234u8,79u8, - 9u8,88u8,10u8,65u8,234u8,204u8,93u8,81u8,14u8,183u8,48u8,129u8,165u8,101u8,204u8,228u8,164u8,90u8,151u8,137u8, - 218u8,76u8,13u8,9u8,141u8,125u8,22u8,94u8,43u8,134u8,67u8,205u8,74u8,191u8,16u8,44u8,2u8,214u8,79u8,63u8, - 28u8,223u8,184u8,146u8,49u8,92u8,52u8,165u8,143u8,237u8,77u8,210u8,240u8,79u8,225u8,76u8,217u8,40u8,159u8,201u8, - 153u8,202u8,225u8,159u8,197u8,74u8,236u8,40u8,159u8,193u8,147u8,240u8,225u8,159u8,195u8,145u8,254u8,81u8,62u8,75u8, - 113u8,90u8,136u8,127u8,50u8,127u8,146u8,72u8,241u8,156u8,191u8,153u8,204u8,26u8,217u8,174u8,43u8,72u8,156u8,74u8, - 178u8,180u8,128u8,179u8,113u8,207u8,113u8,190u8,11u8,214u8,127u8,228u8,231u8,42u8,35u8,231u8,154u8,9u8,70u8,27u8, - 82u8,177u8,214u8,98u8,14u8,236u8,190u8,241u8,70u8,133u8,235u8,188u8,253u8,55u8,198u8,7u8,153u8,230u8,129u8,66u8, - 111u8,97u8,180u8,184u8,84u8,171u8,28u8,34u8,168u8,39u8,192u8,92u8,200u8,90u8,96u8,60u8,28u8,207u8,177u8,62u8, - 79u8,203u8,85u8,154u8,165u8,237u8,93u8,74u8,5u8,59u8,70u8,225u8,186u8,170u8,216u8,65u8,10u8,4u8,194u8,226u8, - 154u8,43u8,254u8,101u8,131u8,214u8,207u8,125u8,95u8,60u8,104u8,7u8,167u8,4u8,93u8,65u8,63u8,156u8,194u8,208u8, - 3u8,101u8,59u8,120u8,241u8,241u8,186u8,15u8,207u8,29u8,17u8,65u8,149u8,253u8,214u8,38u8,124u8,248u8,160u8,160u8, - 170u8,250u8,114u8,152u8,203u8,193u8,227u8,173u8,109u8,81u8,12u8,32u8,83u8,137u8,127u8,28u8,80u8,89u8,179u8,50u8, - 143u8,172u8,8u8,101u8,110u8,87u8,131u8,156u8,198u8,107u8,170u8,128u8,81u8,123u8,161u8,2u8,72u8,203u8,74u8,115u8, - 104u8,130u8,130u8,1u8,101u8,122u8,169u8,129u8,147u8,153u8,230u8,187u8,227u8,111u8,180u8,20u8,0u8,1u8,60u8,13u8, - 68u8,144u8,34u8,219u8,102u8,170u8,2u8,225u8,132u8,58u8,157u8,93u8,185u8,196u8,74u8,176u8,181u8,55u8,193u8,73u8, - 81u8,111u8,153u8,125u8,86u8,75u8,119u8,158u8,47u8,80u8,34u8,197u8,78u8,7u8,168u8,154u8,208u8,128u8,90u8,225u8, - 177u8,19u8,229u8,202u8,192u8,242u8,73u8,140u8,14u8,247u8,212u8,114u8,79u8,253u8,36u8,218u8,17u8,243u8,71u8,199u8, - 188u8,166u8,157u8,223u8,197u8,174u8,31u8,149u8,177u8,91u8,227u8,6u8,167u8,62u8,121u8,120u8,85u8,14u8,66u8,103u8, - 106u8,153u8,239u8,169u8,192u8,224u8,86u8,105u8,235u8,72u8,184u8,238u8,51u8,23u8,24u8,130u8,233u8,91u8,57u8,181u8, - 119u8,96u8,131u8,143u8,52u8,199u8,17u8,104u8,112u8,23u8,157u8,8u8,99u8,188u8,100u8,185u8,72u8,85u8,27u8,132u8, - 5u8,20u8,33u8,118u8,209u8,2u8,141u8,64u8,19u8,117u8,168u8,35u8,19u8,148u8,223u8,121u8,81u8,216u8,242u8,85u8, - 82u8,155u8,233u8,115u8,117u8,105u8,207u8,156u8,113u8,16u8,43u8,225u8,22u8,118u8,33u8,41u8,175u8,53u8,215u8,20u8, - 154u8,206u8,242u8,237u8,199u8,134u8,208u8,196u8,188u8,198u8,46u8,251u8,223u8,47u8,114u8,147u8,64u8,87u8,180u8,109u8, - 80u8,137u8,71u8,128u8,65u8,22u8,40u8,114u8,163u8,60u8,79u8,148u8,152u8,166u8,52u8,84u8,130u8,61u8,163u8,71u8, - 113u8,193u8,161u8,22u8,161u8,191u8,113u8,232u8,69u8,232u8,175u8,14u8,245u8,109u8,125u8,244u8,135u8,189u8,95u8,233u8, - 47u8,95u8,225u8,5u8,32u8,220u8,186u8,71u8,65u8,68u8,131u8,160u8,200u8,30u8,61u8,133u8,70u8,220u8,4u8,229u8, - 91u8,2u8,140u8,169u8,47u8,62u8,114u8,210u8,27u8,172u8,108u8,122u8,147u8,162u8,171u8,112u8,78u8,10u8,184u8,137u8, - 37u8,81u8,238u8,163u8,28u8,23u8,67u8,73u8,147u8,31u8,145u8,70u8,54u8,179u8,127u8,130u8,161u8,89u8,51u8,91u8, - 148u8,93u8,41u8,213u8,209u8,72u8,49u8,119u8,131u8,247u8,98u8,131u8,13u8,143u8,148u8,42u8,53u8,179u8,138u8,149u8, - 6u8,68u8,111u8,31u8,213u8,242u8,12u8,118u8,47u8,244u8,193u8,160u8,89u8,206u8,223u8,100u8,129u8,160u8,217u8,34u8, - 87u8,13u8,207u8,210u8,228u8,140u8,204u8,105u8,195u8,86u8,165u8,70u8,44u8,82u8,27u8,134u8,83u8,58u8,1u8,206u8, - 192u8,44u8,50u8,138u8,12u8,106u8,216u8,135u8,152u8,30u8,74u8,212u8,99u8,89u8,91u8,129u8,201u8,110u8,110u8,43u8, - 147u8,9u8,171u8,63u8,167u8,91u8,238u8,40u8,230u8,169u8,168u8,182u8,125u8,157u8,147u8,18u8,137u8,26u8,184u8,146u8, - 91u8,34u8,225u8,78u8,106u8,130u8,169u8,111u8,39u8,124u8,116u8,199u8,86u8,72u8,28u8,163u8,88u8,31u8,219u8,130u8, - 24u8,176u8,234u8,86u8,72u8,154u8,158u8,110u8,41u8,144u8,133u8,84u8,218u8,206u8,2u8,34u8,202u8,98u8,156u8,223u8, - 178u8,252u8,204u8,223u8,76u8,136u8,145u8,134u8,106u8,152u8,64u8,196u8,76u8,100u8,104u8,154u8,101u8,45u8,51u8,180u8, - 38u8,74u8,243u8,50u8,63u8,132u8,229u8,33u8,200u8,213u8,152u8,158u8,82u8,159u8,0u8,170u8,153u8,86u8,22u8,54u8, - 179u8,232u8,151u8,229u8,243u8,22u8,152u8,120u8,28u8,22u8,59u8,79u8,54u8,177u8,215u8,94u8,175u8,115u8,115u8,89u8, - 106u8,75u8,83u8,94u8,101u8,100u8,30u8,45u8,185u8,161u8,152u8,151u8,193u8,226u8,189u8,145u8,148u8,165u8,142u8,197u8, - 58u8,15u8,16u8,64u8,47u8,149u8,158u8,37u8,64u8,119u8,164u8,194u8,6u8,218u8,36u8,119u8,107u8,162u8,156u8,147u8, - 226u8,23u8,118u8,164u8,153u8,188u8,168u8,229u8,178u8,44u8,127u8,76u8,113u8,82u8,154u8,118u8,76u8,95u8,251u8,201u8, - 122u8,15u8,135u8,55u8,104u8,8u8,234u8,113u8,234u8,226u8,119u8,57u8,210u8,177u8,245u8,195u8,134u8,169u8,207u8,170u8, - 38u8,238u8,137u8,97u8,198u8,100u8,152u8,106u8,40u8,72u8,30u8,253u8,183u8,192u8,191u8,195u8,48u8,198u8,4u8,110u8, - 163u8,136u8,67u8,134u8,116u8,96u8,205u8,76u8,137u8,9u8,67u8,63u8,47u8,175u8,38u8,183u8,103u8,18u8,180u8,68u8, - 19u8,97u8,144u8,148u8,193u8,137u8,18u8,47u8,130u8,10u8,1u8,54u8,21u8,98u8,107u8,104u8,212u8,232u8,102u8,123u8, - 52u8,93u8,108u8,166u8,187u8,5u8,41u8,232u8,198u8,228u8,24u8,90u8,186u8,139u8,180u8,103u8,192u8,226u8,232u8,235u8, - 140u8,86u8,187u8,33u8,161u8,77u8,148u8,228u8,53u8,177u8,140u8,75u8,63u8,52u8,236u8,29u8,111u8,96u8,140u8,106u8, - 248u8,237u8,131u8,163u8,135u8,38u8,237u8,167u8,60u8,164u8,143u8,151u8,73u8,156u8,211u8,194u8,77u8,170u8,44u8,175u8, - 150u8,165u8,90u8,228u8,214u8,209u8,145u8,138u8,191u8,60u8,242u8,77u8,163u8,36u8,45u8,0u8,138u8,145u8,146u8,63u8, - 40u8,74u8,72u8,84u8,134u8,135u8,196u8,21u8,193u8,51u8,6u8,135u8,198u8,62u8,153u8,36u8,142u8,96u8,86u8,151u8, - 43u8,72u8,60u8,230u8,114u8,84u8,169u8,85u8,253u8,37u8,70u8,120u8,106u8,69u8,81u8,175u8,24u8,215u8,4u8,85u8, - 175u8,152u8,152u8,67u8,199u8,88u8,101u8,247u8,6u8,253u8,74u8,172u8,255u8,186u8,88u8,138u8,107u8,187u8,32u8,200u8, - 194u8,131u8,141u8,182u8,105u8,50u8,225u8,17u8,133u8,98u8,37u8,186u8,142u8,71u8,212u8,241u8,248u8,33u8,82u8,153u8, - 18u8,78u8,202u8,145u8,170u8,14u8,67u8,130u8,176u8,67u8,125u8,147u8,149u8,236u8,117u8,124u8,121u8,32u8,245u8,107u8, - 88u8,96u8,89u8,163u8,124u8,246u8,198u8,146u8,220u8,21u8,116u8,98u8,99u8,176u8,132u8,40u8,13u8,122u8,111u8,150u8, - 253u8,179u8,146u8,69u8,144u8,164u8,101u8,235u8,70u8,181u8,188u8,168u8,210u8,102u8,243u8,68u8,157u8,225u8,136u8,4u8, - 142u8,194u8,189u8,206u8,81u8,98u8,69u8,160u8,39u8,226u8,18u8,177u8,173u8,237u8,189u8,46u8,140u8,137u8,104u8,36u8, - 49u8,132u8,217u8,13u8,84u8,244u8,219u8,18u8,77u8,8u8,69u8,240u8,165u8,44u8,183u8,111u8,71u8,152u8,234u8,254u8, - 104u8,217u8,220u8,82u8,9u8,83u8,85u8,66u8,89u8,89u8,249u8,9u8,173u8,82u8,254u8,54u8,91u8,45u8,176u8,219u8, - 208u8,125u8,141u8,243u8,53u8,211u8,77u8,58u8,214u8,169u8,202u8,122u8,37u8,180u8,220u8,59u8,114u8,73u8,234u8,156u8, - 112u8,184u8,39u8,233u8,20u8,134u8,203u8,73u8,215u8,248u8,245u8,10u8,86u8,142u8,192u8,106u8,149u8,158u8,138u8,82u8, - 95u8,124u8,143u8,232u8,117u8,171u8,232u8,95u8,142u8,167u8,212u8,109u8,234u8,95u8,255u8,214u8,241u8,88u8,49u8,241u8, - 66u8,164u8,174u8,37u8,201u8,86u8,205u8,94u8,20u8,223u8,26u8,41u8,87u8,156u8,239u8,206u8,149u8,108u8,196u8,43u8, - 158u8,223u8,132u8,3u8,114u8,213u8,91u8,116u8,90u8,213u8,222u8,19u8,217u8,38u8,86u8,69u8,112u8,207u8,245u8,74u8, - 61u8,101u8,31u8,236u8,110u8,165u8,211u8,31u8,126u8,189u8,106u8,247u8,27u8,22u8,44u8,98u8,3u8,3u8,28u8,73u8, - 183u8,63u8,14u8,29u8,172u8,47u8,205u8,169u8,204u8,209u8,254u8,84u8,185u8,61u8,68u8,48u8,61u8,77u8,140u8,219u8, - 43u8,17u8,142u8,219u8,215u8,217u8,114u8,191u8,227u8,153u8,16u8,5u8,110u8,136u8,210u8,186u8,236u8,102u8,232u8,206u8, - 7u8,149u8,34u8,62u8,134u8,43u8,90u8,24u8,154u8,246u8,180u8,193u8,18u8,230u8,83u8,150u8,146u8,86u8,193u8,195u8, - 242u8,197u8,30u8,124u8,198u8,228u8,42u8,237u8,103u8,246u8,54u8,152u8,77u8,74u8,155u8,207u8,119u8,3u8,186u8,46u8, - 125u8,134u8,156u8,140u8,160u8,235u8,136u8,13u8,148u8,249u8,143u8,61u8,123u8,175u8,176u8,22u8,199u8,141u8,94u8,232u8, - 32u8,43u8,185u8,219u8,43u8,223u8,32u8,14u8,119u8,191u8,14u8,127u8,171u8,122u8,223u8,195u8,221u8,35u8,178u8,102u8, - 159u8,235u8,42u8,41u8,188u8,43u8,126u8,4u8,121u8,197u8,161u8,61u8,185u8,251u8,119u8,111u8,121u8,11u8,40u8,15u8, - 99u8,55u8,131u8,45u8,62u8,24u8,139u8,151u8,11u8,249u8,253u8,104u8,81u8,159u8,58u8,255u8,219u8,151u8,189u8,237u8, - 147u8,194u8,187u8,39u8,255u8,35u8,113u8,185u8,174u8,64u8,44u8,230u8,33u8,113u8,151u8,51u8,241u8,243u8,207u8,61u8, - 185u8,86u8,199u8,61u8,185u8,131u8,177u8,90u8,4u8,85u8,135u8,197u8,54u8,160u8,73u8,53u8,165u8,57u8,85u8,230u8, - 174u8,158u8,176u8,45u8,253u8,132u8,157u8,197u8,91u8,31u8,142u8,203u8,106u8,37u8,91u8,247u8,102u8,180u8,60u8,22u8, - 75u8,99u8,182u8,180u8,12u8,136u8,81u8,236u8,71u8,136u8,235u8,14u8,3u8,72u8,37u8,243u8,21u8,143u8,19u8,123u8, - 64u8,246u8,107u8,72u8,155u8,47u8,48u8,26u8,213u8,43u8,103u8,58u8,250u8,139u8,124u8,68u8,214u8,75u8,17u8,85u8, - 199u8,91u8,198u8,183u8,246u8,100u8,175u8,157u8,98u8,246u8,105u8,208u8,144u8,226u8,202u8,177u8,103u8,102u8,158u8,37u8, - 36u8,131u8,234u8,81u8,60u8,198u8,225u8,85u8, - ]; - let chunk4 = vector[ - 189u8,35u8,69u8,87u8,152u8,230u8,20u8,33u8,143u8,85u8,219u8,114u8,158u8,153u8,229u8,87u8,30u8,188u8,76u8, - 24u8,201u8,199u8,164u8,0u8,88u8,103u8,106u8,53u8,120u8,176u8,154u8,180u8,104u8,113u8,32u8,94u8,87u8,128u8,52u8, - 153u8,164u8,109u8,62u8,4u8,132u8,60u8,85u8,185u8,104u8,112u8,130u8,192u8,207u8,182u8,94u8,149u8,172u8,229u8,140u8, - 221u8,20u8,171u8,54u8,179u8,161u8,240u8,167u8,110u8,137u8,173u8,224u8,127u8,255u8,119u8,143u8,60u8,47u8,165u8,200u8, - 85u8,219u8,115u8,144u8,222u8,114u8,95u8,207u8,159u8,239u8,93u8,239u8,203u8,150u8,25u8,170u8,4u8,87u8,213u8,149u8, - 26u8,246u8,98u8,233u8,250u8,231u8,109u8,127u8,8u8,206u8,1u8,87u8,189u8,85u8,86u8,77u8,15u8,78u8,50u8,110u8, - 113u8,152u8,168u8,236u8,138u8,127u8,102u8,27u8,140u8,201u8,237u8,64u8,82u8,26u8,148u8,102u8,231u8,133u8,125u8,97u8, - 201u8,93u8,194u8,132u8,12u8,217u8,225u8,19u8,184u8,194u8,138u8,228u8,80u8,235u8,69u8,26u8,213u8,9u8,111u8,120u8, - 238u8,250u8,228u8,254u8,22u8,46u8,1u8,30u8,91u8,32u8,187u8,210u8,98u8,250u8,61u8,166u8,143u8,79u8,145u8,254u8, - 89u8,81u8,18u8,185u8,170u8,25u8,49u8,18u8,171u8,196u8,181u8,110u8,243u8,110u8,59u8,234u8,52u8,188u8,143u8,3u8, - 107u8,86u8,195u8,154u8,195u8,206u8,180u8,190u8,235u8,36u8,164u8,151u8,101u8,138u8,31u8,41u8,125u8,74u8,193u8,71u8, - 138u8,161u8,16u8,104u8,110u8,204u8,53u8,140u8,222u8,135u8,68u8,236u8,152u8,21u8,204u8,99u8,215u8,182u8,197u8,110u8, - 163u8,73u8,138u8,64u8,16u8,157u8,173u8,149u8,189u8,218u8,137u8,208u8,99u8,110u8,218u8,113u8,213u8,38u8,169u8,106u8, - 233u8,105u8,154u8,64u8,217u8,254u8,132u8,92u8,66u8,162u8,142u8,216u8,78u8,149u8,154u8,107u8,36u8,214u8,163u8,188u8, - 26u8,162u8,139u8,104u8,93u8,133u8,39u8,142u8,212u8,232u8,54u8,7u8,225u8,113u8,182u8,157u8,79u8,147u8,219u8,84u8, - 7u8,60u8,225u8,187u8,42u8,198u8,217u8,25u8,12u8,14u8,209u8,217u8,164u8,38u8,199u8,33u8,60u8,115u8,13u8,36u8, - 164u8,41u8,123u8,0u8,75u8,56u8,102u8,139u8,198u8,78u8,89u8,136u8,26u8,218u8,170u8,125u8,61u8,197u8,73u8,66u8, - 161u8,107u8,98u8,102u8,85u8,217u8,107u8,221u8,190u8,194u8,147u8,120u8,183u8,171u8,139u8,129u8,115u8,181u8,201u8,95u8, - 148u8,171u8,80u8,184u8,8u8,109u8,189u8,117u8,40u8,92u8,126u8,123u8,86u8,193u8,97u8,101u8,143u8,83u8,201u8,6u8, - 133u8,65u8,91u8,36u8,20u8,66u8,187u8,215u8,177u8,215u8,8u8,176u8,167u8,201u8,242u8,222u8,20u8,175u8,217u8,57u8, - 92u8,185u8,210u8,88u8,174u8,212u8,81u8,127u8,93u8,99u8,154u8,237u8,228u8,222u8,41u8,139u8,171u8,110u8,16u8,213u8, - 81u8,179u8,160u8,78u8,174u8,156u8,119u8,175u8,4u8,248u8,173u8,211u8,247u8,86u8,43u8,54u8,171u8,14u8,154u8,44u8, - 159u8,178u8,175u8,225u8,151u8,144u8,187u8,107u8,51u8,74u8,53u8,153u8,58u8,42u8,152u8,51u8,219u8,68u8,191u8,49u8, - 29u8,173u8,32u8,30u8,78u8,253u8,82u8,26u8,64u8,236u8,173u8,124u8,41u8,53u8,168u8,119u8,235u8,197u8,127u8,239u8, - 124u8,29u8,198u8,239u8,136u8,43u8,67u8,58u8,45u8,93u8,213u8,25u8,87u8,224u8,118u8,195u8,212u8,64u8,49u8,56u8, - 165u8,83u8,210u8,170u8,20u8,125u8,218u8,44u8,221u8,146u8,219u8,228u8,218u8,90u8,65u8,27u8,189u8,116u8,128u8,59u8, - 58u8,65u8,203u8,61u8,39u8,137u8,248u8,164u8,11u8,34u8,238u8,128u8,149u8,24u8,3u8,117u8,131u8,118u8,50u8,114u8, - 54u8,168u8,149u8,205u8,1u8,128u8,114u8,101u8,24u8,91u8,177u8,252u8,188u8,95u8,222u8,128u8,40u8,161u8,236u8,136u8, - 41u8,107u8,27u8,5u8,68u8,88u8,38u8,218u8,52u8,91u8,111u8,19u8,44u8,126u8,74u8,237u8,58u8,142u8,164u8,53u8, - 103u8,61u8,86u8,3u8,161u8,88u8,109u8,215u8,111u8,92u8,21u8,109u8,171u8,14u8,89u8,204u8,44u8,121u8,53u8,95u8, - 43u8,35u8,175u8,237u8,46u8,246u8,169u8,54u8,186u8,0u8,231u8,24u8,89u8,152u8,18u8,76u8,200u8,61u8,247u8,70u8, - 183u8,178u8,226u8,150u8,92u8,178u8,128u8,144u8,89u8,78u8,34u8,89u8,222u8,37u8,247u8,52u8,226u8,115u8,137u8,29u8, - 106u8,65u8,16u8,194u8,171u8,80u8,182u8,2u8,99u8,114u8,233u8,149u8,125u8,238u8,46u8,9u8,26u8,149u8,91u8,191u8, - 210u8,217u8,62u8,145u8,236u8,64u8,70u8,129u8,27u8,81u8,76u8,135u8,216u8,209u8,180u8,83u8,36u8,107u8,145u8,236u8, - 199u8,42u8,108u8,235u8,182u8,153u8,153u8,58u8,117u8,169u8,190u8,224u8,10u8,220u8,252u8,218u8,187u8,195u8,74u8,149u8, - 147u8,253u8,91u8,181u8,2u8,81u8,207u8,104u8,219u8,104u8,79u8,37u8,59u8,221u8,5u8,43u8,89u8,142u8,175u8,57u8, - 134u8,82u8,4u8,86u8,169u8,216u8,68u8,130u8,144u8,205u8,26u8,101u8,164u8,61u8,50u8,118u8,114u8,36u8,29u8,223u8, - 88u8,46u8,55u8,237u8,27u8,131u8,145u8,206u8,162u8,189u8,10u8,14u8,230u8,197u8,78u8,37u8,48u8,125u8,190u8,5u8, - 14u8,183u8,205u8,192u8,25u8,12u8,60u8,9u8,239u8,6u8,173u8,60u8,47u8,210u8,128u8,44u8,122u8,169u8,182u8,176u8, - 76u8,223u8,139u8,122u8,40u8,146u8,23u8,194u8,29u8,43u8,118u8,45u8,78u8,146u8,117u8,216u8,246u8,85u8,26u8,220u8, - 219u8,76u8,98u8,38u8,59u8,61u8,78u8,113u8,114u8,151u8,222u8,31u8,52u8,84u8,73u8,219u8,109u8,63u8,114u8,138u8, - 77u8,172u8,113u8,175u8,42u8,146u8,153u8,168u8,249u8,39u8,25u8,185u8,35u8,43u8,101u8,185u8,250u8,166u8,34u8,58u8, - 98u8,99u8,234u8,229u8,92u8,235u8,148u8,1u8,2u8,165u8,12u8,107u8,33u8,181u8,67u8,196u8,208u8,34u8,157u8,227u8, - 138u8,74u8,40u8,190u8,192u8,4u8,222u8,69u8,60u8,71u8,172u8,194u8,154u8,12u8,76u8,154u8,18u8,55u8,172u8,86u8, - 194u8,109u8,102u8,119u8,123u8,118u8,93u8,131u8,36u8,147u8,95u8,111u8,186u8,141u8,202u8,89u8,135u8,35u8,24u8,153u8, - 4u8,219u8,229u8,46u8,239u8,213u8,112u8,120u8,25u8,132u8,203u8,122u8,193u8,83u8,206u8,75u8,122u8,162u8,98u8,41u8, - 30u8,101u8,38u8,214u8,60u8,65u8,178u8,220u8,18u8,167u8,166u8,38u8,186u8,185u8,241u8,184u8,98u8,166u8,141u8,10u8, - 78u8,35u8,223u8,38u8,138u8,135u8,186u8,41u8,144u8,247u8,189u8,222u8,169u8,18u8,49u8,231u8,173u8,184u8,43u8,235u8, - 176u8,65u8,130u8,224u8,77u8,123u8,84u8,41u8,73u8,163u8,34u8,140u8,203u8,116u8,212u8,5u8,102u8,158u8,134u8,74u8, - 119u8,79u8,219u8,200u8,119u8,80u8,55u8,165u8,91u8,59u8,214u8,104u8,122u8,224u8,37u8,66u8,40u8,227u8,114u8,28u8, - 60u8,96u8,11u8,42u8,58u8,18u8,49u8,65u8,140u8,207u8,105u8,189u8,56u8,91u8,249u8,210u8,43u8,172u8,34u8,197u8, - 170u8,59u8,210u8,77u8,157u8,161u8,25u8,245u8,145u8,22u8,115u8,115u8,206u8,148u8,41u8,18u8,42u8,37u8,235u8,185u8, - 233u8,64u8,84u8,12u8,17u8,76u8,88u8,233u8,188u8,84u8,200u8,253u8,204u8,52u8,29u8,43u8,129u8,218u8,45u8,223u8, - 242u8,248u8,59u8,55u8,228u8,154u8,243u8,143u8,49u8,239u8,192u8,55u8,214u8,5u8,79u8,69u8,42u8,176u8,225u8,36u8, - 187u8,165u8,87u8,243u8,112u8,9u8,114u8,186u8,6u8,170u8,160u8,75u8,121u8,117u8,5u8,181u8,164u8,246u8,115u8,247u8, - 35u8,133u8,73u8,166u8,172u8,226u8,15u8,207u8,43u8,229u8,222u8,140u8,211u8,55u8,188u8,170u8,142u8,179u8,242u8,132u8, - 68u8,35u8,147u8,209u8,190u8,164u8,229u8,130u8,77u8,102u8,168u8,70u8,46u8,219u8,41u8,112u8,138u8,252u8,120u8,165u8, - 16u8,145u8,86u8,59u8,138u8,150u8,97u8,5u8,20u8,83u8,26u8,25u8,177u8,23u8,137u8,79u8,156u8,166u8,73u8,170u8, - 241u8,123u8,211u8,27u8,52u8,52u8,146u8,112u8,63u8,201u8,189u8,10u8,242u8,221u8,116u8,182u8,116u8,96u8,154u8,96u8, - 101u8,230u8,69u8,108u8,119u8,202u8,195u8,152u8,45u8,230u8,200u8,227u8,244u8,37u8,177u8,57u8,108u8,121u8,96u8,83u8, - 98u8,173u8,214u8,73u8,84u8,49u8,45u8,204u8,47u8,250u8,220u8,106u8,221u8,223u8,49u8,206u8,18u8,219u8,170u8,157u8, - 189u8,62u8,191u8,252u8,229u8,251u8,239u8,95u8,191u8,124u8,125u8,118u8,254u8,150u8,229u8,150u8,17u8,201u8,140u8,148u8, - 9u8,130u8,11u8,96u8,205u8,243u8,15u8,61u8,5u8,77u8,24u8,243u8,247u8,46u8,179u8,88u8,45u8,145u8,227u8,152u8, - 199u8,163u8,143u8,168u8,49u8,207u8,34u8,141u8,133u8,32u8,3u8,143u8,212u8,117u8,221u8,195u8,123u8,223u8,242u8,119u8, - 9u8,118u8,117u8,146u8,1u8,118u8,54u8,126u8,99u8,226u8,138u8,106u8,139u8,81u8,122u8,185u8,139u8,146u8,157u8,44u8, - 19u8,148u8,213u8,231u8,113u8,20u8,58u8,46u8,17u8,78u8,212u8,250u8,171u8,48u8,64u8,242u8,46u8,89u8,44u8,137u8, - 60u8,138u8,188u8,79u8,97u8,206u8,36u8,206u8,149u8,95u8,111u8,104u8,106u8,78u8,69u8,31u8,123u8,26u8,202u8,226u8, - 136u8,140u8,122u8,148u8,139u8,91u8,75u8,148u8,37u8,62u8,2u8,131u8,10u8,177u8,132u8,146u8,159u8,68u8,157u8,37u8, - 13u8,42u8,222u8,175u8,249u8,98u8,73u8,219u8,65u8,73u8,103u8,217u8,22u8,227u8,150u8,21u8,122u8,32u8,37u8,175u8, - 238u8,22u8,57u8,103u8,80u8,76u8,39u8,32u8,241u8,143u8,36u8,168u8,248u8,234u8,76u8,9u8,213u8,149u8,165u8,42u8, - 174u8,244u8,68u8,106u8,107u8,75u8,228u8,166u8,229u8,247u8,172u8,171u8,97u8,25u8,59u8,16u8,94u8,207u8,222u8,23u8, - 194u8,160u8,237u8,212u8,156u8,166u8,93u8,145u8,12u8,12u8,140u8,2u8,86u8,173u8,94u8,22u8,87u8,32u8,170u8,171u8, - 136u8,163u8,18u8,138u8,169u8,185u8,187u8,8u8,70u8,18u8,138u8,112u8,48u8,186u8,73u8,133u8,155u8,144u8,76u8,98u8, - 241u8,172u8,218u8,67u8,15u8,214u8,234u8,1u8,214u8,205u8,44u8,95u8,137u8,178u8,42u8,218u8,128u8,200u8,204u8,146u8, - 59u8,248u8,30u8,6u8,94u8,38u8,219u8,45u8,233u8,150u8,141u8,214u8,150u8,180u8,6u8,253u8,251u8,168u8,158u8,185u8, - 12u8,52u8,151u8,148u8,70u8,237u8,188u8,98u8,166u8,139u8,224u8,31u8,93u8,86u8,39u8,84u8,94u8,173u8,13u8,188u8, - 147u8,59u8,148u8,116u8,123u8,42u8,179u8,170u8,24u8,1u8,72u8,245u8,249u8,142u8,53u8,242u8,39u8,203u8,115u8,96u8, - 81u8,209u8,19u8,130u8,220u8,196u8,130u8,79u8,20u8,192u8,85u8,10u8,254u8,10u8,83u8,165u8,224u8,91u8,126u8,234u8, - 81u8,43u8,144u8,246u8,144u8,194u8,116u8,250u8,171u8,90u8,177u8,64u8,103u8,120u8,197u8,224u8,181u8,24u8,157u8,174u8, - 17u8,160u8,34u8,194u8,253u8,147u8,74u8,20u8,147u8,207u8,55u8,172u8,92u8,148u8,107u8,165u8,59u8,217u8,58u8,237u8, - 232u8,29u8,28u8,142u8,104u8,149u8,76u8,133u8,204u8,245u8,148u8,31u8,102u8,214u8,97u8,53u8,27u8,23u8,121u8,145u8, - 202u8,65u8,250u8,184u8,148u8,54u8,183u8,82u8,229u8,37u8,95u8,159u8,151u8,248u8,137u8,44u8,255u8,90u8,249u8,164u8, - 136u8,2u8,156u8,108u8,133u8,223u8,153u8,49u8,176u8,130u8,5u8,115u8,76u8,9u8,190u8,14u8,226u8,19u8,175u8,6u8, - 184u8,205u8,83u8,96u8,172u8,194u8,254u8,128u8,165u8,40u8,177u8,32u8,172u8,20u8,204u8,89u8,184u8,9u8,233u8,187u8, - 144u8,204u8,241u8,128u8,8u8,200u8,204u8,222u8,47u8,148u8,164u8,55u8,41u8,150u8,172u8,106u8,21u8,107u8,74u8,158u8, - 2u8,14u8,122u8,36u8,43u8,150u8,155u8,216u8,100u8,150u8,37u8,64u8,24u8,168u8,111u8,209u8,184u8,76u8,28u8,240u8, - 115u8,146u8,218u8,35u8,107u8,23u8,139u8,189u8,8u8,20u8,209u8,206u8,208u8,17u8,156u8,82u8,33u8,126u8,5u8,108u8, - 98u8,106u8,127u8,52u8,227u8,220u8,221u8,71u8,45u8,177u8,133u8,109u8,102u8,76u8,10u8,125u8,182u8,124u8,176u8,42u8, - 102u8,125u8,58u8,164u8,196u8,239u8,58u8,125u8,90u8,154u8,28u8,239u8,207u8,196u8,219u8,244u8,165u8,239u8,22u8,228u8, - 94u8,64u8,16u8,109u8,239u8,210u8,212u8,106u8,196u8,107u8,70u8,180u8,41u8,218u8,152u8,24u8,21u8,110u8,175u8,45u8, - 207u8,255u8,230u8,89u8,89u8,188u8,206u8,60u8,29u8,16u8,185u8,17u8,86u8,79u8,238u8,48u8,37u8,38u8,157u8,57u8, - 219u8,2u8,171u8,151u8,162u8,218u8,41u8,84u8,175u8,24u8,237u8,75u8,219u8,161u8,197u8,163u8,11u8,27u8,80u8,185u8, - 108u8,233u8,178u8,183u8,102u8,65u8,183u8,84u8,45u8,217u8,197u8,238u8,28u8,81u8,212u8,193u8,197u8,114u8,139u8,186u8, - 35u8,204u8,202u8,42u8,79u8,9u8,196u8,103u8,186u8,164u8,211u8,131u8,90u8,197u8,44u8,112u8,249u8,230u8,244u8,242u8, - 135u8,179u8,87u8,178u8,220u8,20u8,205u8,161u8,193u8,202u8,103u8,167u8,151u8,111u8,199u8,64u8,67u8,190u8,2u8,48u8, - 51u8,32u8,243u8,205u8,106u8,177u8,78u8,149u8,70u8,223u8,104u8,152u8,177u8,250u8,169u8,82u8,214u8,193u8,91u8,34u8, - 218u8,149u8,74u8,245u8,244u8,60u8,201u8,222u8,47u8,47u8,126u8,60u8,43u8,48u8,230u8,147u8,134u8,49u8,172u8,115u8, - 77u8,190u8,205u8,110u8,9u8,146u8,169u8,189u8,93u8,192u8,255u8,221u8,209u8,131u8,114u8,118u8,57u8,49u8,118u8,46u8, - 54u8,68u8,166u8,163u8,233u8,140u8,204u8,143u8,64u8,124u8,234u8,5u8,110u8,45u8,79u8,251u8,111u8,117u8,124u8,222u8, - 93u8,118u8,190u8,35u8,248u8,143u8,86u8,86u8,182u8,95u8,0u8,11u8,112u8,70u8,24u8,139u8,116u8,89u8,50u8,222u8, - 68u8,100u8,178u8,251u8,172u8,168u8,189u8,84u8,94u8,248u8,143u8,88u8,191u8,40u8,105u8,131u8,29u8,38u8,131u8,234u8, - 125u8,4u8,39u8,233u8,50u8,91u8,95u8,147u8,171u8,76u8,19u8,63u8,21u8,198u8,105u8,247u8,175u8,242u8,36u8,220u8, - 61u8,171u8,59u8,167u8,202u8,165u8,121u8,53u8,100u8,31u8,208u8,181u8,57u8,182u8,197u8,237u8,215u8,59u8,188u8,113u8, - 8u8,27u8,235u8,196u8,53u8,170u8,226u8,220u8,86u8,176u8,236u8,196u8,203u8,254u8,221u8,253u8,110u8,208u8,191u8,164u8, - 140u8,169u8,237u8,247u8,57u8,9u8,237u8,21u8,109u8,212u8,174u8,76u8,52u8,250u8,214u8,28u8,73u8,127u8,220u8,135u8, - 125u8,188u8,30u8,187u8,104u8,64u8,66u8,234u8,153u8,43u8,123u8,104u8,23u8,38u8,42u8,168u8,152u8,116u8,228u8,199u8, - 36u8,231u8,253u8,129u8,157u8,156u8,214u8,5u8,5u8,117u8,224u8,26u8,162u8,214u8,39u8,113u8,252u8,84u8,255u8,164u8, - 254u8,28u8,245u8,188u8,153u8,17u8,89u8,116u8,119u8,180u8,162u8,0u8,115u8,23u8,115u8,0u8,50u8,218u8,221u8,206u8, - 8u8,55u8,200u8,23u8,43u8,184u8,42u8,146u8,117u8,10u8,176u8,90u8,222u8,119u8,40u8,53u8,147u8,252u8,225u8,53u8, - 165u8,45u8,28u8,98u8,146u8,78u8,177u8,245u8,130u8,214u8,57u8,106u8,183u8,198u8,224u8,21u8,236u8,166u8,178u8,145u8, - 23u8,18u8,185u8,200u8,48u8,200u8,5u8,155u8,230u8,100u8,59u8,156u8,128u8,180u8,74u8,203u8,1u8,18u8,164u8,51u8, - 206u8,83u8,248u8,208u8,204u8,117u8,116u8,54u8,184u8,178u8,50u8,144u8,81u8,1u8,128u8,47u8,243u8,5u8,233u8,71u8, - 125u8,159u8,110u8,43u8,80u8,28u8,242u8,0u8,171u8,225u8,147u8,104u8,143u8,71u8,165u8,138u8,226u8,142u8,135u8,174u8, - 183u8,73u8,212u8,51u8,103u8,33u8,214u8,3u8,38u8,102u8,35u8,120u8,23u8,183u8,203u8,251u8,49u8,150u8,232u8,26u8, - 147u8,115u8,74u8,91u8,5u8,125u8,223u8,170u8,52u8,241u8,232u8,240u8,234u8,235u8,229u8,189u8,133u8,105u8,25u8,86u8, - 90u8,185u8,92u8,68u8,31u8,177u8,6u8,4u8,88u8,211u8,223u8,205u8,28u8,240u8,39u8,244u8,8u8,137u8,242u8,136u8, - 108u8,202u8,42u8,61u8,44u8,159u8,164u8,93u8,116u8,108u8,14u8,202u8,83u8,15u8,176u8,60u8,22u8,216u8,125u8,158u8, - 46u8,130u8,86u8,218u8,70u8,42u8,215u8,68u8,193u8,75u8,149u8,78u8,187u8,60u8,18u8,178u8,176u8,17u8,110u8,199u8, - 25u8,60u8,245u8,100u8,159u8,90u8,202u8,248u8,195u8,80u8,198u8,23u8,220u8,92u8,19u8,127u8,202u8,174u8,25u8,11u8, - 147u8,100u8,143u8,48u8,87u8,103u8,93u8,159u8,140u8,215u8,113u8,81u8,74u8,209u8,185u8,152u8,66u8,249u8,101u8,165u8, - 92u8,146u8,19u8,181u8,54u8,2u8,160u8,237u8,17u8,150u8,17u8,218u8,166u8,196u8,4u8,165u8,212u8,88u8,32u8,209u8, - 18u8,96u8,72u8,73u8,243u8,219u8,140u8,106u8,119u8,156u8,55u8,139u8,249u8,84u8,30u8,221u8,65u8,97u8,115u8,113u8, - 189u8,86u8,165u8,112u8,24u8,69u8,54u8,250u8,91u8,211u8,96u8,9u8,214u8,198u8,15u8,174u8,174u8,219u8,100u8,99u8, - 7u8,23u8,146u8,218u8,40u8,150u8,44u8,232u8,171u8,105u8,84u8,49u8,229u8,69u8,9u8,132u8,120u8,228u8,86u8,188u8, - 251u8,102u8,40u8,30u8,222u8,142u8,183u8,152u8,176u8,30u8,191u8,59u8,239u8,195u8,182u8,159u8,173u8,216u8,122u8,150u8, - 117u8,149u8,85u8,5u8,73u8,165u8,58u8,160u8,8u8,31u8,194u8,209u8,63u8,96u8,155u8,94u8,16u8,82u8,193u8,72u8, - 56u8,77u8,205u8,146u8,42u8,180u8,88u8,137u8,1u8,132u8,201u8,61u8,211u8,208u8,9u8,179u8,82u8,74u8,144u8,204u8, - 205u8,97u8,213u8,78u8,75u8,154u8,233u8,76u8,105u8,208u8,4u8,207u8,44u8,54u8,1u8,234u8,55u8,160u8,225u8,161u8, - 152u8,70u8,174u8,157u8,78u8,128u8,146u8,12u8,239u8,52u8,231u8,191u8,203u8,164u8,185u8,192u8,153u8,79u8,111u8,87u8, - 49u8,113u8,196u8,228u8,232u8,40u8,163u8,112u8,40u8,206u8,146u8,148u8,194u8,57u8,164u8,98u8,142u8,196u8,85u8,20u8, - 36u8,165u8,217u8,11u8,187u8,236u8,101u8,66u8,92u8,151u8,189u8,57u8,169u8,212u8,163u8,21u8,237u8,97u8,89u8,12u8, - 187u8,123u8,249u8,128u8,136u8,19u8,144u8,165u8,101u8,176u8,252u8,220u8,54u8,231u8,73u8,37u8,10u8,123u8,83u8,107u8, - 246u8,160u8,204u8,192u8,109u8,181u8,29u8,241u8,242u8,31u8,115u8,194u8,202u8,120u8,163u8,67u8,218u8,41u8,85u8,180u8, - 50u8,65u8,187u8,155u8,44u8,35u8,163u8,181u8,33u8,4u8,123u8,176u8,8u8,73u8,7u8,108u8,208u8,96u8,67u8,165u8, - 90u8,77u8,120u8,78u8,166u8,211u8,221u8,138u8,16u8,36u8,237u8,172u8,206u8,105u8,236u8,91u8,7u8,217u8,233u8,97u8, - 31u8,90u8,136u8,146u8,137u8,99u8,84u8,234u8,37u8,1u8,253u8,148u8,156u8,228u8,177u8,204u8,248u8,21u8,129u8,223u8, - 139u8,6u8,181u8,172u8,187u8,160u8,89u8,138u8,79u8,218u8,171u8,232u8,218u8,199u8,154u8,108u8,234u8,243u8,103u8,249u8, - 229u8,71u8,221u8,152u8,80u8,1u8,30u8,142u8,11u8,92u8,152u8,169u8,11u8,181u8,195u8,106u8,91u8,168u8,156u8,236u8, - 173u8,4u8,254u8,124u8,81u8,95u8,86u8,242u8,117u8,69u8,166u8,96u8,87u8,138u8,38u8,2u8,214u8,42u8,232u8,202u8, - 104u8,89u8,45u8,159u8,104u8,116u8,116u8,110u8,217u8,24u8,32u8,200u8,136u8,180u8,226u8,81u8,212u8,197u8,234u8,169u8, - 213u8,165u8,13u8,109u8,59u8,101u8,39u8,188u8,255u8,198u8,44u8,128u8,154u8,40u8,89u8,169u8,248u8,198u8,126u8,43u8, - 119u8,228u8,166u8,236u8,159u8,122u8,80u8,212u8,130u8,66u8,199u8,25u8,111u8,211u8,137u8,189u8,19u8,17u8,100u8,145u8, - 209u8,242u8,13u8,91u8,47u8,151u8,72u8,20u8,37u8,82u8,69u8,81u8,34u8,131u8,102u8,197u8,161u8,89u8,112u8,186u8, - 129u8,208u8,17u8,27u8,76u8,108u8,246u8,12u8,101u8,185u8,213u8,80u8,49u8,10u8,180u8,78u8,207u8,95u8,177u8,191u8, - 149u8,104u8,92u8,167u8,57u8,164u8,178u8,73u8,135u8,219u8,172u8,149u8,6u8,202u8,196u8,158u8,129u8,55u8,55u8,222u8, - 203u8,116u8,29u8,234u8,194u8,73u8,179u8,14u8,164u8,39u8,225u8,29u8,98u8,238u8,19u8,108u8,89u8,238u8,204u8,156u8, - 171u8,159u8,34u8,35u8,52u8,169u8,234u8,105u8,50u8,37u8,32u8,47u8,106u8,65u8,38u8,245u8,182u8,2u8,143u8,71u8, - 121u8,154u8,158u8,94u8,165u8,90u8,90u8,122u8,54u8,91u8,219u8,74u8,76u8,251u8,82u8,195u8,237u8,153u8,230u8,55u8, - 154u8,145u8,112u8,158u8,109u8,170u8,105u8,110u8,190u8,142u8,78u8,58u8,97u8,22u8,246u8,33u8,42u8,222u8,183u8,251u8, - 217u8,67u8,123u8,61u8,49u8,42u8,17u8,180u8,163u8,42u8,131u8,74u8,19u8,76u8,167u8,178u8,78u8,203u8,38u8,119u8, - 130u8,123u8,246u8,223u8,127u8,169u8,205u8,113u8,255u8,199u8,112u8,211u8,146u8,88u8,177u8,231u8,40u8,85u8,141u8,49u8, - 58u8,173u8,245u8,30u8,91u8,157u8,242u8,63u8,238u8,73u8,123u8,83u8,254u8,215u8,191u8,148u8,5u8,181u8,120u8,148u8, - 153u8,210u8,187u8,84u8,201u8,86u8,224u8,175u8,172u8,119u8,43u8,49u8,185u8,30u8,251u8,134u8,95u8,137u8,38u8,168u8, - 122u8,51u8,83u8,208u8,23u8,128u8,67u8,129u8,241u8,142u8,118u8,52u8,221u8,97u8,141u8,116u8,204u8,166u8,27u8,207u8, - 55u8,201u8,42u8,189u8,203u8,54u8,191u8,162u8,23u8,140u8,54u8,202u8,221u8,81u8,115u8,227u8,51u8,207u8,187u8,240u8, - 16u8,8u8,48u8,47u8,47u8,206u8,47u8,207u8,206u8,47u8,127u8,185u8,28u8,255u8,229u8,236u8,239u8,227u8,72u8,109u8, - 162u8,8u8,91u8,127u8,255u8,229u8,48u8,233u8,247u8,38u8,163u8,56u8,30u8,14u8,103u8,189u8,73u8,210u8,15u8,143u8, - 187u8,179u8,110u8,210u8,143u8,211u8,97u8,56u8,157u8,134u8,97u8,146u8,142u8,230u8,147u8,201u8,52u8,154u8,78u8,251u8, - 147u8,94u8,183u8,59u8,233u8,13u8,194u8,225u8,104u8,114u8,60u8,12u8,123u8,211u8,238u8,48u8,57u8,30u8,204u8,195u8, - 112u8,62u8,29u8,244u8,142u8,123u8,131u8,233u8,241u8,96u8,216u8,79u8,211u8,65u8,60u8,156u8,79u8,227u8,89u8,8u8, - 67u8,142u8,166u8,113u8,60u8,234u8,125u8,249u8,172u8,218u8,234u8,126u8,186u8,248u8,201u8,177u8,186u8,100u8,52u8,27u8, - 76u8,163u8,121u8,20u8,31u8,135u8,243u8,120u8,22u8,245u8,250u8,189u8,233u8,112u8,148u8,12u8,187u8,73u8,47u8,28u8, - 141u8,230u8,195u8,8u8,150u8,58u8,152u8,77u8,143u8,103u8,147u8,126u8,191u8,63u8,138u8,102u8,189u8,65u8,146u8,244u8, - 210u8,193u8,116u8,154u8,164u8,199u8,195u8,209u8,112u8,18u8,119u8,123u8,97u8,60u8,154u8,36u8,199u8,97u8,63u8,158u8, - 71u8,195u8,227u8,126u8,63u8,29u8,204u8,19u8,248u8,215u8,228u8,184u8,155u8,198u8,221u8,254u8,60u8,234u8,165u8,243u8, - 105u8,47u8,141u8,211u8,9u8,60u8,56u8,77u8,226u8,201u8,112u8,222u8,31u8,76u8,146u8,217u8,96u8,52u8,31u8,13u8, - 250u8,243u8,105u8,20u8,77u8,142u8,39u8,113u8,63u8,157u8,68u8,211u8,81u8,63u8,9u8,7u8,243u8,97u8,119u8,54u8, - 155u8,207u8,194u8,184u8,11u8,30u8,247u8,30u8,204u8,57u8,25u8,244u8,135u8,241u8,96u8,52u8,152u8,206u8,211u8,100u8, - 4u8,143u8,198u8,199u8,243u8,222u8,52u8,237u8,79u8,224u8,161u8,121u8,58u8,11u8,191u8,44u8,62u8,156u8,179u8,159u8, - 46u8,94u8,254u8,48u8,126u8,245u8,203u8,207u8,164u8,173u8,14u8,69u8,210u8,231u8,193u8,32u8,44u8,132u8,25u8,22u8, - 113u8,251u8,229u8,167u8,241u8,203u8,191u8,191u8,196u8,222u8,130u8,103u8,0u8,194u8,87u8,151u8,252u8,213u8,238u8,32u8, - 12u8,139u8,167u8,188u8,56u8,63u8,27u8,159u8,254u8,244u8,150u8,191u8,16u8,133u8,236u8,167u8,248u8,45u8,171u8,255u8, - 43u8,15u8,156u8,165u8,254u8,49u8,49u8,218u8,179u8,189u8,6u8,209u8,95u8,142u8,159u8,213u8,90u8,1u8,247u8,209u8, - 9u8,16u8,20u8,239u8,196u8,236u8,241u8,41u8,150u8,30u8,249u8,222u8,83u8,88u8,14u8,240u8,220u8,49u8,37u8,71u8, - 162u8,146u8,183u8,218u8,118u8,59u8,93u8,124u8,132u8,125u8,249u8,140u8,94u8,54u8,138u8,82u8,196u8,202u8,241u8,231u8, - 198u8,193u8,7u8,172u8,77u8,174u8,226u8,161u8,214u8,40u8,92u8,37u8,249u8,116u8,12u8,131u8,140u8,201u8,18u8,105u8, - 239u8,219u8,150u8,158u8,25u8,89u8,180u8,124u8,165u8,17u8,33u8,31u8,164u8,101u8,114u8,23u8,94u8,84u8,70u8,203u8, - 248u8,179u8,223u8,227u8,147u8,235u8,86u8,27u8,125u8,44u8,253u8,118u8,0u8,76u8,3u8,238u8,202u8,208u8,207u8,250u8, - 202u8,255u8,165u8,11u8,221u8,141u8,222u8,117u8,152u8,62u8,168u8,15u8,104u8,141u8,239u8,152u8,48u8,12u8,61u8,201u8, - 165u8,123u8,130u8,208u8,2u8,133u8,15u8,164u8,114u8,13u8,96u8,218u8,0u8,199u8,205u8,138u8,10u8,56u8,70u8,220u8, - 216u8,42u8,121u8,239u8,253u8,14u8,208u8,0u8,116u8,58u8,188u8,73u8,149u8,6u8,202u8,198u8,51u8,36u8,35u8,76u8, - 237u8,232u8,12u8,130u8,20u8,45u8,184u8,78u8,59u8,64u8,171u8,163u8,41u8,253u8,185u8,225u8,74u8,226u8,178u8,178u8, - 49u8,165u8,187u8,137u8,183u8,241u8,216u8,187u8,12u8,109u8,17u8,160u8,10u8,222u8,165u8,27u8,144u8,73u8,240u8,242u8, - 207u8,211u8,241u8,114u8,1u8,106u8,129u8,26u8,10u8,91u8,208u8,17u8,130u8,253u8,45u8,49u8,211u8,81u8,128u8,199u8, - 128u8,108u8,187u8,176u8,215u8,102u8,125u8,84u8,213u8,142u8,199u8,248u8,74u8,61u8,29u8,179u8,43u8,161u8,227u8,112u8, - 12u8,179u8,166u8,247u8,108u8,204u8,161u8,92u8,39u8,83u8,240u8,140u8,114u8,48u8,250u8,83u8,5u8,231u8,226u8,172u8, - 162u8,102u8,113u8,158u8,10u8,60u8,67u8,121u8,93u8,118u8,95u8,166u8,219u8,26u8,179u8,15u8,198u8,243u8,101u8,114u8, - 157u8,155u8,47u8,118u8,216u8,117u8,255u8,95u8,38u8,83u8,254u8,135u8,248u8,230u8,31u8,181u8,72u8,147u8,60u8,36u8, - 192u8,44u8,79u8,94u8,124u8,228u8,160u8,73u8,103u8,16u8,39u8,181u8,178u8,142u8,255u8,9u8,202u8,133u8,126u8,108u8, - 38u8,45u8,177u8,7u8,5u8,247u8,87u8,191u8,63u8,184u8,169u8,128u8,156u8,185u8,176u8,186u8,159u8,120u8,204u8,176u8, - 209u8,254u8,65u8,144u8,26u8,53u8,245u8,143u8,147u8,109u8,203u8,26u8,210u8,202u8,168u8,40u8,165u8,79u8,123u8,8u8, - 183u8,209u8,179u8,160u8,123u8,184u8,24u8,130u8,116u8,190u8,117u8,244u8,118u8,118u8,56u8,34u8,202u8,187u8,43u8,184u8, - 22u8,102u8,21u8,225u8,203u8,48u8,210u8,103u8,140u8,50u8,1u8,38u8,5u8,230u8,227u8,95u8,211u8,123u8,117u8,57u8, - 122u8,117u8,117u8,67u8,136u8,238u8,152u8,114u8,107u8,219u8,40u8,179u8,36u8,43u8,168u8,153u8,80u8,101u8,211u8,3u8, - 173u8,110u8,213u8,233u8,156u8,69u8,71u8,100u8,237u8,118u8,239u8,194u8,92u8,69u8,57u8,140u8,0u8,89u8,15u8,2u8, - 123u8,22u8,102u8,63u8,232u8,155u8,188u8,124u8,82u8,65u8,12u8,230u8,92u8,150u8,24u8,101u8,69u8,52u8,250u8,200u8, - 93u8,86u8,154u8,166u8,138u8,165u8,40u8,50u8,5u8,250u8,95u8,78u8,187u8,187u8,155u8,205u8,228u8,157u8,196u8,238u8, - 46u8,152u8,231u8,226u8,5u8,197u8,20u8,172u8,202u8,127u8,159u8,122u8,249u8,207u8,154u8,117u8,131u8,13u8,4u8,218u8, - 191u8,108u8,48u8,31u8,160u8,145u8,170u8,193u8,26u8,226u8,152u8,247u8,13u8,108u8,242u8,63u8,236u8,43u8,136u8,163u8, - 55u8,126u8,27u8,190u8,143u8,226u8,110u8,251u8,31u8,108u8,12u8,176u8,164u8,17u8,231u8,241u8,24u8,163u8,210u8,225u8, - 22u8,107u8,37u8,112u8,74u8,112u8,99u8,103u8,51u8,52u8,48u8,133u8,239u8,187u8,32u8,67u8,158u8,146u8,152u8,83u8, - 106u8,49u8,122u8,30u8,92u8,166u8,203u8,121u8,251u8,31u8,110u8,159u8,46u8,65u8,101u8,3u8,51u8,114u8,7u8,222u8, - 150u8,139u8,143u8,190u8,107u8,172u8,68u8,102u8,111u8,254u8,222u8,254u8,135u8,255u8,6u8,111u8,55u8,123u8,3u8,52u8, - 120u8,174u8,238u8,131u8,81u8,91u8,35u8,141u8,193u8,35u8,228u8,111u8,41u8,116u8,200u8,249u8,28u8,32u8,8u8,84u8, - 59u8,208u8,198u8,228u8,134u8,143u8,234u8,210u8,230u8,209u8,190u8,165u8,86u8,124u8,116u8,64u8,211u8,195u8,238u8,4u8, - 145u8,35u8,92u8,88u8,25u8,192u8,60u8,223u8,194u8,113u8,36u8,151u8,83u8,167u8,86u8,32u8,129u8,15u8,71u8,37u8, - 51u8,22u8,47u8,57u8,2u8,177u8,34u8,54u8,167u8,50u8,23u8,105u8,206u8,24u8,55u8,176u8,199u8,40u8,166u8,19u8, - 63u8,58u8,251u8,4u8,91u8,85u8,24u8,237u8,199u8,62u8,105u8,189u8,131u8,177u8,187u8,221u8,220u8,71u8,77u8,152u8, - 252u8,105u8,18u8,28u8,76u8,219u8,56u8,170u8,126u8,112u8,18u8,162u8,229u8,115u8,62u8,84u8,66u8,186u8,79u8,232u8, - 232u8,62u8,77u8,214u8,122u8,232u8,9u8,86u8,165u8,229u8,71u8,61u8,200u8,33u8,28u8,100u8,188u8,223u8,65u8,50u8, - 55u8,36u8,50u8,241u8,220u8,186u8,28u8,62u8,168u8,20u8,83u8,249u8,210u8,122u8,255u8,101u8,24u8,126u8,89u8,243u8, - 197u8,232u8,203u8,15u8,113u8,78u8,104u8,177u8,252u8,83u8,45u8,130u8,19u8,5u8,133u8,48u8,88u8,205u8,172u8,200u8, - 251u8,73u8,16u8,92u8,237u8,99u8,21u8,213u8,44u8,149u8,199u8,170u8,119u8,100u8,148u8,98u8,17u8,104u8,43u8,225u8, - 99u8,201u8,161u8,5u8,155u8,253u8,44u8,129u8,218u8,167u8,14u8,224u8,236u8,249u8,132u8,80u8,51u8,240u8,149u8,168u8, - 159u8,216u8,127u8,151u8,135u8,188u8,22u8,89u8,176u8,14u8,147u8,92u8,141u8,153u8,171u8,133u8,220u8,250u8,36u8,57u8, - 69u8,131u8,118u8,189u8,40u8,145u8,115u8,111u8,185u8,211u8,90u8,122u8,233u8,88u8,165u8,18u8,165u8,103u8,72u8,107u8, - 76u8,103u8,60u8,129u8,61u8,92u8,232u8,94u8,145u8,43u8,230u8,163u8,242u8,203u8,197u8,161u8,247u8,246u8,48u8,20u8, - 199u8,236u8,145u8,252u8,57u8,11u8,202u8,222u8,59u8,212u8,61u8,165u8,189u8,206u8,123u8,51u8,210u8,81u8,228u8,241u8, - 218u8,239u8,178u8,255u8,55u8,200u8,124u8,20u8,19u8,74u8,68u8,63u8,12u8,163u8,80u8,249u8,48u8,102u8,31u8,198u8, - 97u8,177u8,16u8,160u8,117u8,226u8,108u8,128u8,51u8,105u8,22u8,187u8,168u8,232u8,203u8,248u8,129u8,121u8,90u8,243u8, - 174u8,205u8,15u8,239u8,221u8,108u8,138u8,41u8,215u8,114u8,10u8,104u8,5u8,235u8,104u8,157u8,55u8,51u8,186u8,141u8, - 196u8,58u8,117u8,143u8,159u8,116u8,251u8,95u8,185u8,178u8,158u8,247u8,188u8,20u8,186u8,199u8,221u8,254u8,239u8,197u8, - 157u8,32u8,178u8,12u8,244u8,0u8,86u8,12u8,31u8,146u8,196u8,205u8,42u8,234u8,223u8,110u8,178u8,217u8,110u8,170u8, - 102u8,251u8,58u8,122u8,235u8,114u8,46u8,88u8,214u8,79u8,55u8,50u8,17u8,87u8,101u8,169u8,122u8,4u8,227u8,44u8, - 136u8,126u8,12u8,16u8,181u8,3u8,116u8,31u8,144u8,56u8,58u8,185u8,182u8,28u8,123u8,249u8,174u8,89u8,68u8,46u8, - 9u8,103u8,198u8,53u8,233u8,193u8,106u8,30u8,175u8,136u8,189u8,4u8,5u8,19u8,203u8,157u8,35u8,174u8,215u8,191u8, - 104u8,194u8,223u8,225u8,240u8,95u8,184u8,205u8,241u8,81u8,69u8,123u8,124u8,180u8,15u8,137u8,217u8,163u8,123u8,231u8, - 143u8,43u8,206u8,31u8,215u8,154u8,63u8,246u8,208u8,184u8,137u8,170u8,24u8,175u8,72u8,207u8,130u8,70u8,144u8,158u8, - 240u8,191u8,190u8,9u8,194u8,39u8,97u8,56u8,136u8,7u8,253u8,224u8,105u8,208u8,138u8,130u8,111u8,197u8,223u8,237u8, - 154u8,232u8,106u8,96u8,235u8,32u8,142u8,251u8,163u8,81u8,47u8,234u8,13u8,6u8,110u8,188u8,117u8,246u8,76u8,37u8, - 249u8,10u8,44u8,186u8,80u8,41u8,28u8,158u8,59u8,209u8,85u8,158u8,134u8,99u8,13u8,46u8,116u8,85u8,95u8,40u8, - 217u8,194u8,179u8,226u8,25u8,227u8,142u8,25u8,57u8,83u8,60u8,97u8,236u8,156u8,208u8,77u8,30u8,5u8,46u8,58u8, - 181u8,146u8,84u8,206u8,243u8,144u8,208u8,0u8,66u8,210u8,113u8,224u8,130u8,20u8,253u8,159u8,115u8,206u8,175u8,73u8, - 5u8,148u8,219u8,44u8,95u8,168u8,53u8,80u8,44u8,217u8,216u8,129u8,217u8,30u8,146u8,102u8,63u8,29u8,183u8,132u8, - 231u8,28u8,50u8,174u8,52u8,100u8,199u8,199u8,224u8,148u8,230u8,175u8,106u8,145u8,119u8,196u8,236u8,106u8,104u8,225u8, - 60u8,36u8,18u8,5u8,173u8,238u8,218u8,136u8,133u8,118u8,7u8,65u8,119u8,2u8,39u8,11u8,216u8,27u8,213u8,220u8, - 39u8,143u8,107u8,170u8,214u8,113u8,189u8,0u8,249u8,106u8,28u8,173u8,5u8,6u8,140u8,149u8,214u8,103u8,8u8,142u8, - 112u8,101u8,206u8,51u8,146u8,231u8,17u8,115u8,148u8,36u8,133u8,113u8,211u8,117u8,58u8,199u8,18u8,84u8,212u8,60u8, - 119u8,151u8,220u8,243u8,210u8,106u8,11u8,150u8,192u8,164u8,221u8,67u8,31u8,2u8,127u8,226u8,106u8,68u8,126u8,32u8, - 149u8,63u8,58u8,153u8,195u8,233u8,33u8,196u8,185u8,32u8,66u8,69u8,147u8,84u8,43u8,107u8,162u8,19u8,51u8,44u8, - 48u8,250u8,10u8,254u8,213u8,66u8,105u8,250u8,136u8,10u8,131u8,237u8,175u8,14u8,227u8,22u8,17u8,185u8,76u8,232u8, - 248u8,145u8,197u8,45u8,204u8,37u8,212u8,95u8,65u8,92u8,176u8,2u8,185u8,132u8,200u8,137u8,29u8,139u8,245u8,209u8, - 124u8,185u8,184u8,190u8,217u8,58u8,203u8,253u8,112u8,248u8,205u8,104u8,71u8,146u8,100u8,7u8,154u8,9u8,76u8,78u8, - 218u8,174u8,147u8,12u8,53u8,56u8,251u8,167u8,240u8,252u8,45u8,28u8,197u8,236u8,160u8,88u8,4u8,89u8,114u8,66u8, - 164u8,150u8,98u8,90u8,162u8,60u8,238u8,163u8,224u8,10u8,109u8,142u8,74u8,106u8,198u8,21u8,77u8,163u8,158u8,98u8, - 170u8,137u8,72u8,156u8,37u8,245u8,39u8,86u8,15u8,121u8,110u8,15u8,113u8,16u8,117u8,8u8,65u8,228u8,231u8,82u8, - 186u8,6u8,236u8,97u8,210u8,203u8,129u8,155u8,143u8,186u8,209u8,168u8,127u8,220u8,15u8,65u8,120u8,105u8,108u8,243u8, - 199u8,253u8,56u8,26u8,197u8,56u8,230u8,224u8,65u8,238u8,184u8,216u8,203u8,162u8,62u8,200u8,253u8,22u8,251u8,89u8, - 94u8,181u8,187u8,45u8,126u8,240u8,187u8,45u8,174u8,113u8,183u8,69u8,44u8,248u8,41u8,15u8,104u8,110u8,21u8,201u8, - 219u8,165u8,117u8,148u8,96u8,46u8,94u8,84u8,84u8,188u8,203u8,98u8,113u8,42u8,131u8,137u8,174u8,161u8,88u8,209u8, - 241u8,172u8,203u8,69u8,22u8,26u8,38u8,135u8,60u8,37u8,83u8,146u8,9u8,81u8,11u8,99u8,169u8,23u8,4u8,222u8, - 116u8,208u8,122u8,116u8,20u8,135u8,209u8,96u8,52u8,138u8,163u8,1u8,113u8,113u8,133u8,108u8,187u8,252u8,231u8,17u8, - 120u8,95u8,141u8,198u8,165u8,15u8,180u8,211u8,7u8,176u8,30u8,10u8,227u8,97u8,161u8,157u8,144u8,84u8,247u8,31u8, - 51u8,85u8,210u8,209u8,119u8,99u8,49u8,31u8,175u8,69u8,225u8,209u8,73u8,182u8,131u8,123u8,176u8,113u8,107u8,226u8, - 71u8,225u8,185u8,50u8,66u8,146u8,13u8,43u8,135u8,98u8,30u8,100u8,246u8,98u8,210u8,177u8,168u8,65u8,203u8,93u8, - 67u8,246u8,174u8,154u8,209u8,134u8,251u8,27u8,23u8,60u8,34u8,10u8,222u8,83u8,172u8,212u8,29u8,32u8,17u8,230u8, - 71u8,211u8,108u8,110u8,135u8,53u8,140u8,241u8,155u8,251u8,116u8,91u8,124u8,123u8,225u8,9u8,84u8,21u8,174u8,109u8, - 217u8,218u8,249u8,102u8,145u8,129u8,136u8,49u8,229u8,34u8,171u8,85u8,161u8,197u8,170u8,204u8,107u8,48u8,138u8,12u8, - 109u8,68u8,83u8,81u8,170u8,49u8,247u8,202u8,83u8,137u8,201u8,228u8,84u8,145u8,139u8,193u8,150u8,196u8,236u8,250u8, - 225u8,107u8,166u8,203u8,147u8,166u8,44u8,112u8,223u8,208u8,11u8,227u8,27u8,220u8,45u8,216u8,142u8,96u8,33u8,79u8, - 208u8,155u8,17u8,62u8,25u8,141u8,236u8,238u8,8u8,228u8,198u8,137u8,224u8,94u8,90u8,76u8,129u8,147u8,191u8,8u8, - 248u8,117u8,3u8,216u8,163u8,233u8,2u8,236u8,227u8,101u8,150u8,147u8,14u8,2u8,236u8,237u8,217u8,142u8,52u8,90u8, - 82u8,135u8,166u8,221u8,65u8,39u8,164u8,119u8,91u8,21u8,206u8,61u8,246u8,171u8,142u8,35u8,250u8,227u8,19u8,243u8, - 28u8,157u8,66u8,74u8,71u8,101u8,189u8,206u8,66u8,249u8,91u8,19u8,39u8,17u8,187u8,79u8,34u8,150u8,39u8,17u8, - 142u8,16u8,68u8,242u8,48u8,162u8,39u8,163u8,97u8,249u8,97u8,68u8,238u8,195u8,32u8,14u8,1u8,254u8,29u8,22u8, - 10u8,143u8,240u8,205u8,16u8,15u8,27u8,80u8,204u8,56u8,234u8,197u8,218u8,83u8,39u8,157u8,77u8,166u8,156u8,249u8, - 98u8,93u8,253u8,200u8,201u8,6u8,142u8,232u8,16u8,142u8,147u8,71u8,251u8,4u8,123u8,95u8,84u8,58u8,127u8,160u8, - 171u8,191u8,20u8,129u8,142u8,93u8,202u8,173u8,167u8,126u8,177u8,168u8,62u8,74u8,121u8,240u8,12u8,153u8,39u8,2u8, - 200u8,87u8,220u8,13u8,251u8,34u8,210u8,23u8,232u8,253u8,220u8,4u8,142u8,50u8,159u8,168u8,27u8,69u8,81u8,26u8, - 219u8,77u8,177u8,177u8,233u8,124u8,183u8,20u8,197u8,147u8,125u8,219u8,105u8,177u8,67u8,195u8,138u8,4u8,228u8,54u8, - 227u8,235u8,38u8,13u8,204u8,224u8,66u8,6u8,68u8,98u8,167u8,223u8,86u8,210u8,156u8,140u8,70u8,178u8,149u8,56u8, - 80u8,179u8,39u8,244u8,96u8,36u8,46u8,218u8,87u8,98u8,34u8,155u8,236u8,63u8,131u8,178u8,185u8,187u8,97u8,18u8, - 235u8,182u8,18u8,180u8,200u8,81u8,99u8,201u8,33u8,202u8,14u8,240u8,253u8,35u8,254u8,229u8,244u8,38u8,5u8,197u8, - 163u8,125u8,48u8,35u8,233u8,186u8,25u8,73u8,215u8,205u8,72u8,194u8,17u8,102u8,6u8,63u8,25u8,29u8,151u8,115u8, - 146u8,184u8,128u8,147u8,196u8,10u8,39u8,137u8,13u8,78u8,162u8,242u8,169u8,10u8,156u8,36u8,170u8,193u8,73u8,200u8, - 6u8,142u8,232u8,204u8,126u8,110u8,18u8,127u8,44u8,220u8,164u8,71u8,175u8,242u8,79u8,150u8,155u8,104u8,14u8,212u8, - 122u8,164u8,46u8,170u8,71u8,137u8,147u8,125u8,241u8,92u8,173u8,130u8,207u8,136u8,2u8,240u8,65u8,169u8,80u8,70u8, - 171u8,74u8,177u8,154u8,83u8,168u8,220u8,186u8,170u8,199u8,231u8,86u8,49u8,248u8,67u8,142u8,170u8,95u8,135u8,241u8, - 179u8,5u8,224u8,81u8,201u8,45u8,225u8,183u8,254u8,99u8,91u8,103u8,119u8,15u8,198u8,184u8,154u8,82u8,78u8,247u8, - 10u8,166u8,28u8,238u8,25u8,244u8,42u8,108u8,69u8,200u8,235u8,168u8,229u8,251u8,211u8,13u8,163u8,244u8,43u8,163u8, - 63u8,190u8,62u8,31u8,191u8,188u8,120u8,125u8,126u8,137u8,197u8,206u8,89u8,157u8,22u8,76u8,232u8,65u8,57u8,72u8, - 28u8,26u8,215u8,77u8,27u8,62u8,184u8,50u8,200u8,231u8,128u8,109u8,203u8,79u8,56u8,120u8,181u8,186u8,9u8,192u8, - 128u8,243u8,71u8,102u8,3u8,0u8,14u8,163u8,41u8,219u8,178u8,232u8,210u8,11u8,213u8,12u8,137u8,95u8,137u8,122u8, - 125u8,164u8,109u8,8u8,105u8,16u8,184u8,175u8,146u8,105u8,194u8,166u8,138u8,215u8,215u8,17u8,132u8,88u8,62u8,232u8, - 71u8,17u8,117u8,131u8,134u8,210u8,210u8,136u8,27u8,34u8,138u8,105u8,107u8,227u8,153u8,253u8,185u8,81u8,18u8,58u8, - 215u8,251u8,188u8,233u8,245u8,114u8,203u8,194u8,111u8,186u8,5u8,94u8,107u8,126u8,1u8,62u8,39u8,183u8,163u8,104u8, - 216u8,247u8,221u8,41u8,18u8,194u8,75u8,248u8,232u8,69u8,113u8,92u8,108u8,121u8,244u8,142u8,14u8,9u8,23u8,0u8, - 84u8,92u8,147u8,98u8,5u8,200u8,78u8,235u8,124u8,158u8,110u8,72u8,23u8,12u8,90u8,63u8,79u8,1u8,0u8,181u8, - 55u8,89u8,199u8,178u8,199u8,38u8,240u8,76u8,196u8,245u8,175u8,175u8,210u8,186u8,136u8,157u8,56u8,52u8,133u8,27u8, - 245u8,154u8,247u8,128u8,116u8,153u8,237u8,235u8,224u8,179u8,185u8,142u8,106u8,75u8,81u8,97u8,5u8,98u8,234u8,122u8, - 183u8,212u8,10u8,169u8,22u8,172u8,200u8,174u8,124u8,234u8,140u8,93u8,53u8,210u8,102u8,116u8,230u8,160u8,59u8,51u8, - 175u8,148u8,246u8,1u8,130u8,163u8,136u8,141u8,239u8,205u8,53u8,28u8,0u8,113u8,120u8,189u8,120u8,76u8,154u8,119u8, - 158u8,198u8,227u8,195u8,152u8,13u8,19u8,151u8,199u8,84u8,18u8,162u8,4u8,24u8,129u8,72u8,128u8,163u8,139u8,37u8, - 120u8,118u8,222u8,111u8,13u8,62u8,226u8,33u8,85u8,147u8,88u8,154u8,160u8,51u8,22u8,8u8,18u8,85u8,241u8,150u8, - 245u8,171u8,250u8,202u8,202u8,49u8,185u8,111u8,123u8,164u8,156u8,174u8,123u8,155u8,9u8,34u8,70u8,11u8,25u8,215u8, - 64u8,109u8,210u8,112u8,164u8,57u8,244u8,118u8,46u8,170u8,62u8,106u8,58u8,184u8,136u8,21u8,97u8,9u8,96u8,145u8, - 181u8,164u8,154u8,196u8,157u8,232u8,160u8,8u8,203u8,66u8,220u8,137u8,171u8,69u8,17u8,53u8,134u8,59u8,3u8,7u8, - 238u8,208u8,255u8,196u8,31u8,37u8,10u8,129u8,210u8,239u8,92u8,91u8,125u8,76u8,234u8,22u8,96u8,82u8,85u8,111u8, - 238u8,53u8,8u8,53u8,88u8,136u8,214u8,174u8,204u8,157u8,0u8,184u8,190u8,229u8,211u8,138u8,88u8,153u8,175u8,0u8, - 196u8,225u8,87u8,237u8,192u8,83u8,144u8,182u8,214u8,65u8,226u8,28u8,158u8,32u8,191u8,170u8,192u8,177u8,198u8,240u8, - 9u8,82u8,164u8,62u8,187u8,236u8,20u8,108u8,158u8,177u8,34u8,91u8,176u8,106u8,235u8,179u8,70u8,47u8,198u8,218u8, - 198u8,157u8,63u8,216u8,205u8,245u8,190u8,214u8,202u8,136u8,20u8,151u8,16u8,49u8,171u8,25u8,155u8,43u8,247u8,167u8, - 208u8,212u8,219u8,175u8,117u8,5u8,134u8,205u8,94u8,129u8,245u8,217u8,88u8,92u8,16u8,43u8,18u8,85u8,11u8,22u8, - 105u8,140u8,133u8,13u8,37u8,214u8,126u8,204u8,151u8,31u8,181u8,7u8,52u8,68u8,153u8,177u8,22u8,64u8,217u8,32u8, - 203u8,18u8,28u8,75u8,153u8,145u8,114u8,174u8,6u8,121u8,213u8,48u8,30u8,14u8,216u8,232u8,181u8,33u8,98u8,141u8, - 241u8,153u8,87u8,125u8,80u8,94u8,245u8,193u8,226u8,101u8,20u8,59u8,99u8,178u8,190u8,255u8,28u8,10u8,131u8,134u8, - 125u8,252u8,247u8,231u8,72u8,152u8,188u8,70u8,44u8,200u8,135u8,178u8,157u8,233u8,26u8,46u8,49u8,116u8,208u8,18u8, - 183u8,150u8,1u8,198u8,19u8,145u8,83u8,174u8,207u8,250u8,3u8,21u8,30u8,85u8,159u8,45u8,245u8,120u8,104u8,67u8, - 249u8,110u8,244u8,230u8,78u8,225u8,112u8,4u8,137u8,107u8,107u8,16u8,108u8,48u8,226u8,225u8,212u8,18u8,170u8,190u8, - 37u8,131u8,186u8,114u8,173u8,246u8,215u8,120u8,6u8,37u8,8u8,93u8,89u8,44u8,104u8,236u8,80u8,157u8,150u8,46u8, - 218u8,255u8,211u8,232u8,66u8,34u8,47u8,103u8,187u8,221u8,12u8,54u8,41u8,194u8,126u8,192u8,155u8,86u8,222u8,62u8, - 16u8,47u8,70u8,35u8,51u8,170u8,200u8,164u8,205u8,100u8,77u8,90u8,250u8,152u8,52u8,58u8,95u8,108u8,208u8,123u8, - 93u8,64u8,162u8,15u8,108u8,114u8,250u8,0u8,230u8,38u8,76u8,16u8,100u8,63u8,213u8,169u8,243u8,64u8,4u8,169u8, - 143u8,236u8,21u8,12u8,69u8,252u8,108u8,163u8,240u8,0u8,254u8,27u8,133u8,7u8,132u8,68u8,62u8,166u8,77u8,168u8, - 244u8,36u8,186u8,54u8,255u8,141u8,27u8,225u8,191u8,21u8,16u8,167u8,254u8,57u8,215u8,55u8,227u8,144u8,54u8,184u8, - 74u8,190u8,10u8,178u8,64u8,202u8,94u8,156u8,230u8,28u8,245u8,85u8,102u8,214u8,113u8,176u8,111u8,60u8,143u8,66u8, - 246u8,93u8,235u8,96u8,226u8,226u8,204u8,172u8,168u8,246u8,101u8,82u8,235u8,56u8,143u8,57u8,227u8,28u8,214u8,215u8, - 107u8,227u8,120u8,224u8,176u8,56u8,125u8,48u8,101u8,130u8,133u8,104u8,53u8,231u8,55u8,255u8,196u8,116u8,137u8,207u8, - 170u8,68u8,245u8,160u8,250u8,216u8,202u8,194u8,249u8,136u8,236u8,112u8,213u8,115u8,146u8,27u8,52u8,197u8,117u8,11u8, - 210u8,179u8,246u8,23u8,179u8,205u8,139u8,60u8,44u8,188u8,200u8,105u8,237u8,54u8,30u8,198u8,233u8,172u8,82u8,208u8, - 34u8,45u8,190u8,97u8,228u8,167u8,184u8,80u8,250u8,25u8,188u8,6u8,242u8,37u8,74u8,151u8,183u8,203u8,100u8,170u8, - 54u8,253u8,218u8,55u8,235u8,224u8,0u8,184u8,197u8,30u8,184u8,149u8,76u8,225u8,8u8,39u8,43u8,157u8,202u8,10u8, - 252u8,43u8,46u8,159u8,80u8,173u8,224u8,146u8,113u8,52u8,37u8,203u8,222u8,167u8,6u8,148u8,87u8,52u8,34u8,209u8, - 134u8,194u8,25u8,205u8,242u8,250u8,236u8,80u8,60u8,26u8,185u8,177u8,201u8,174u8,113u8,92u8,210u8,147u8,221u8,31u8, - 229u8,232u8,61u8,105u8,159u8,236u8,93u8,231u8,164u8,251u8,238u8,147u8,238u8,63u8,248u8,65u8,247u8,15u8,58u8,231u8, - 126u8,9u8,53u8,250u8,64u8,244u8,65u8,128u8,92u8,133u8,13u8,29u8,0u8,89u8,111u8,68u8,102u8,211u8,236u8,77u8, - 193u8,245u8,162u8,54u8,242u8,196u8,137u8,65u8,75u8,55u8,40u8,145u8,170u8,45u8,248u8,16u8,153u8,91u8,14u8,119u8, - 209u8,50u8,184u8,162u8,220u8,236u8,138u8,6u8,90u8,93u8,188u8,121u8,217u8,62u8,180u8,50u8,219u8,30u8,22u8,14u8, - 197u8,141u8,224u8,146u8,77u8,149u8,39u8,154u8,150u8,81u8,187u8,97u8,116u8,184u8,82u8,233u8,24u8,196u8,121u8,253u8, - 136u8,20u8,2u8,76u8,10u8,7u8,175u8,195u8,26u8,163u8,218u8,141u8,142u8,212u8,73u8,174u8,134u8,26u8,235u8,215u8, - 211u8,36u8,157u8,146u8,26u8,17u8,122u8,34u8,111u8,233u8,53u8,212u8,100u8,32u8,9u8,63u8,45u8,170u8,95u8,68u8, - 61u8,161u8,175u8,213u8,192u8,112u8,99u8,48u8,239u8,120u8,77u8,94u8,102u8,218u8,4u8,69u8,69u8,25u8,69u8,3u8, - 105u8,181u8,243u8,144u8,93u8,199u8,208u8,85u8,107u8,14u8,24u8,138u8,222u8,139u8,76u8,71u8,42u8,155u8,28u8,246u8, - 238u8,105u8,86u8,76u8,248u8,4u8,87u8,88u8,210u8,53u8,49u8,143u8,9u8,210u8,87u8,122u8,151u8,106u8,36u8,40u8, - 79u8,194u8,167u8,172u8,74u8,184u8,217u8,19u8,242u8,4u8,140u8,131u8,245u8,69u8,208u8,94u8,35u8,21u8,187u8,250u8, - 124u8,218u8,225u8,3u8,97u8,131u8,115u8,130u8,202u8,232u8,171u8,175u8,214u8,90u8,176u8,24u8,112u8,187u8,88u8,97u8, - 81u8,145u8,213u8,237u8,201u8,201u8,60u8,201u8,137u8,14u8,129u8,167u8,59u8,102u8,167u8,217u8,242u8,224u8,80u8,23u8, - 0u8,171u8,159u8,127u8,163u8,72u8,68u8,2u8,87u8,211u8,247u8,183u8,68u8,187u8,212u8,186u8,88u8,235u8,168u8,33u8, - 118u8,233u8,67u8,13u8,185u8,105u8,145u8,62u8,100u8,96u8,72u8,107u8,157u8,221u8,137u8,248u8,195u8,118u8,19u8,136u8, - 210u8,11u8,187u8,100u8,89u8,88u8,9u8,170u8,15u8,236u8,183u8,199u8,211u8,193u8,26u8,70u8,20u8,38u8,99u8,202u8, - 41u8,234u8,224u8,137u8,119u8,177u8,110u8,99u8,163u8,110u8,63u8,230u8,166u8,199u8,138u8,70u8,198u8,250u8,62u8,158u8, - 130u8,208u8,96u8,103u8,179u8,115u8,141u8,195u8,176u8,246u8,191u8,51u8,188u8,208u8,20u8,155u8,88u8,11u8,27u8,42u8, - 11u8,247u8,54u8,57u8,221u8,118u8,221u8,170u8,181u8,145u8,1u8,173u8,106u8,186u8,107u8,115u8,78u8,167u8,158u8,97u8, - 154u8,108u8,196u8,224u8,89u8,9u8,145u8,15u8,70u8,180u8,126u8,21u8,189u8,152u8,211u8,7u8,35u8,108u8,42u8,85u8, - 192u8,137u8,78u8,238u8,3u8,130u8,129u8,185u8,93u8,2u8,188u8,42u8,46u8,153u8,39u8,238u8,141u8,12u8,183u8,141u8, - 42u8,158u8,104u8,125u8,177u8,88u8,76u8,192u8,20u8,75u8,213u8,25u8,139u8,171u8,82u8,185u8,9u8,49u8,23u8,93u8, - 215u8,140u8,95u8,47u8,227u8,67u8,181u8,241u8,34u8,244u8,201u8,250u8,206u8,60u8,180u8,244u8,125u8,58u8,37u8,46u8, - 48u8,100u8,239u8,158u8,172u8,184u8,198u8,117u8,26u8,82u8,114u8,203u8,161u8,119u8,0u8,249u8,131u8,38u8,17u8,132u8, - 118u8,154u8,28u8,138u8,178u8,216u8,136u8,25u8,61u8,137u8,60u8,219u8,128u8,61u8,194u8,87u8,181u8,174u8,167u8,128u8, - 120u8,178u8,119u8,177u8,48u8,157u8,3u8,110u8,204u8,186u8,195u8,19u8,45u8,97u8,193u8,26u8,143u8,139u8,42u8,75u8, - 217u8,242u8,196u8,14u8,247u8,65u8,171u8,246u8,110u8,49u8,110u8,163u8,87u8,155u8,204u8,59u8,211u8,198u8,87u8,1u8, - 6u8,99u8,251u8,96u8,81u8,71u8,160u8,169u8,30u8,202u8,95u8,174u8,16u8,202u8,53u8,63u8,154u8,196u8,33u8,75u8, - 112u8,32u8,200u8,250u8,199u8,240u8,79u8,20u8,134u8,13u8,11u8,28u8,17u8,255u8,69u8,206u8,224u8,206u8,65u8,191u8, - 187u8,73u8,215u8,190u8,140u8,84u8,90u8,206u8,158u8,144u8,63u8,92u8,142u8,102u8,70u8,8u8,103u8,15u8,77u8,38u8, - 30u8,149u8,219u8,65u8,35u8,71u8,169u8,148u8,3u8,211u8,132u8,190u8,45u8,59u8,134u8,125u8,142u8,215u8,85u8,103u8, - 69u8,129u8,58u8,49u8,137u8,44u8,168u8,98u8,238u8,225u8,35u8,155u8,20u8,173u8,195u8,248u8,33u8,62u8,36u8,152u8, - 112u8,182u8,78u8,155u8,67u8,143u8,88u8,177u8,201u8,70u8,158u8,146u8,91u8,188u8,22u8,248u8,110u8,181u8,186u8,87u8, - 220u8,93u8,219u8,44u8,192u8,26u8,218u8,155u8,148u8,45u8,15u8,80u8,4u8,215u8,42u8,31u8,64u8,62u8,184u8,76u8, - 73u8,169u8,2u8,172u8,207u8,165u8,212u8,247u8,241u8,59u8,134u8,124u8,190u8,109u8,195u8,49u8,100u8,95u8,18u8,133u8, - 217u8,65u8,100u8,21u8,77u8,167u8,7u8,81u8,61u8,71u8,231u8,103u8,25u8,171u8,69u8,102u8,231u8,238u8,29u8,200u8, - 223u8,144u8,164u8,156u8,66u8,45u8,16u8,88u8,5u8,97u8,183u8,242u8,166u8,10u8,197u8,242u8,9u8,138u8,112u8,180u8, - 241u8,32u8,73u8,74u8,95u8,103u8,230u8,61u8,235u8,50u8,6u8,28u8,16u8,139u8,90u8,81u8,140u8,119u8,62u8,230u8, - 40u8,86u8,194u8,19u8,218u8,44u8,107u8,3u8,150u8,70u8,221u8,234u8,22u8,77u8,231u8,78u8,42u8,154u8,173u8,1u8, - 115u8,251u8,186u8,18u8,31u8,118u8,225u8,127u8,145u8,205u8,5u8,20u8,162u8,227u8,215u8,142u8,231u8,246u8,57u8,252u8, - 190u8,41u8,89u8,138u8,135u8,245u8,203u8,163u8,7u8,118u8,148u8,109u8,88u8,57u8,2u8,106u8,223u8,101u8,143u8,63u8, - 12u8,55u8,82u8,151u8,89u8,158u8,210u8,156u8,72u8,201u8,130u8,210u8,229u8,12u8,179u8,156u8,61u8,118u8,44u8,120u8, - 81u8,172u8,109u8,178u8,148u8,64u8,61u8,240u8,206u8,170u8,160u8,28u8,184u8,119u8,117u8,240u8,181u8,165u8,140u8,219u8, - 208u8,189u8,245u8,17u8,121u8,45u8,108u8,114u8,250u8,24u8,221u8,20u8,21u8,88u8,149u8,211u8,145u8,225u8,205u8,233u8, - 174u8,235u8,156u8,247u8,188u8,89u8,46u8,80u8,53u8,230u8,9u8,139u8,244u8,192u8,198u8,40u8,244u8,73u8,64u8,31u8, - 23u8,195u8,113u8,73u8,63u8,7u8,114u8,4u8,23u8,114u8,242u8,33u8,239u8,228u8,149u8,138u8,9u8,33u8,219u8,27u8, - 120u8,23u8,147u8,67u8,144u8,111u8,177u8,146u8,54u8,204u8,193u8,204u8,222u8,216u8,135u8,207u8,84u8,92u8,131u8,173u8, - 31u8,19u8,70u8,234u8,213u8,2u8,12u8,57u8,255u8,112u8,220u8,120u8,12u8,54u8,211u8,0u8,119u8,213u8,179u8,158u8, - 112u8,208u8,143u8,168u8,197u8,147u8,179u8,199u8,214u8,239u8,170u8,211u8,211u8,161u8,33u8,102u8,241u8,39u8,26u8,98u8, - 246u8,111u8,216u8,150u8,166u8,160u8,55u8,199u8,94u8,13u8,87u8,226u8,195u8,250u8,187u8,212u8,110u8,226u8,224u8,155u8, - 183u8,72u8,237u8,170u8,217u8,96u8,165u8,32u8,198u8,160u8,78u8,241u8,249u8,184u8,52u8,91u8,136u8,25u8,138u8,72u8, - 109u8,194u8,146u8,242u8,243u8,165u8,21u8,205u8,251u8,205u8,21u8,109u8,239u8,235u8,198u8,77u8,183u8,55u8,30u8,150u8, - 191u8,202u8,104u8,209u8,196u8,15u8,98u8,128u8,84u8,107u8,176u8,123u8,180u8,146u8,66u8,37u8,186u8,30u8,96u8,100u8, - 246u8,110u8,63u8,236u8,201u8,168u8,233u8,131u8,49u8,37u8,46u8,194u8,148u8,3u8,244u8,253u8,106u8,237u8,34u8,205u8, - 189u8,236u8,133u8,163u8,177u8,23u8,71u8,227u8,195u8,113u8,52u8,46u8,8u8,67u8,208u8,226u8,82u8,188u8,72u8,42u8, - 61u8,102u8,184u8,131u8,108u8,30u8,24u8,72u8,162u8,85u8,73u8,138u8,10u8,220u8,103u8,142u8,30u8,2u8,254u8,192u8, - 135u8,218u8,104u8,213u8,13u8,149u8,109u8,213u8,62u8,80u8,115u8,148u8,82u8,170u8,141u8,245u8,206u8,76u8,227u8,142u8, - 0u8,19u8,233u8,89u8,97u8,18u8,24u8,182u8,176u8,80u8,194u8,112u8,236u8,78u8,22u8,202u8,202u8,31u8,139u8,13u8, - 196u8,143u8,199u8,6u8,184u8,254u8,92u8,122u8,94u8,251u8,226u8,116u8,151u8,97u8,117u8,212u8,41u8,164u8,73u8,23u8, - 18u8,176u8,206u8,13u8,185u8,98u8,80u8,100u8,125u8,28u8,18u8,80u8,164u8,81u8,238u8,6u8,76u8,7u8,103u8,222u8, - 134u8,117u8,219u8,129u8,37u8,229u8,53u8,177u8,75u8,162u8,198u8,129u8,25u8,242u8,190u8,173u8,192u8,28u8,34u8,224u8, - 206u8,56u8,204u8,111u8,229u8,55u8,158u8,227u8,70u8,125u8,11u8,29u8,150u8,238u8,10u8,7u8,251u8,238u8,17u8,39u8, - 52u8,231u8,113u8,124u8,68u8,28u8,98u8,5u8,30u8,240u8,42u8,204u8,71u8,167u8,65u8,55u8,96u8,130u8,242u8,213u8, - 63u8,43u8,224u8,97u8,177u8,109u8,219u8,240u8,242u8,176u8,170u8,144u8,114u8,46u8,244u8,72u8,142u8,124u8,92u8,227u8, - 78u8,185u8,18u8,34u8,253u8,85u8,117u8,223u8,110u8,65u8,213u8,188u8,15u8,225u8,111u8,137u8,62u8,126u8,135u8,139u8, - 243u8,42u8,232u8,202u8,6u8,70u8,159u8,206u8,53u8,80u8,192u8,250u8,93u8,14u8,8u8,171u8,156u8,165u8,213u8,120u8, - 199u8,188u8,6u8,199u8,224u8,171u8,118u8,194u8,0u8,191u8,216u8,7u8,14u8,156u8,184u8,212u8,247u8,117u8,166u8,170u8, - 141u8,236u8,40u8,39u8,226u8,88u8,0u8,225u8,152u8,198u8,231u8,158u8,165u8,54u8,116u8,147u8,36u8,196u8,199u8,10u8, - 166u8,221u8,199u8,147u8,82u8,245u8,107u8,199u8,201u8,102u8,11u8,19u8,72u8,246u8,48u8,247u8,253u8,120u8,250u8,159u8, - 227u8,95u8,6u8,189u8,134u8,150u8,170u8,210u8,211u8,248u8,96u8,66u8,242u8,225u8,1u8,11u8,194u8,178u8,134u8,46u8, - 6u8,85u8,25u8,136u8,252u8,226u8,175u8,13u8,163u8,10u8,247u8,205u8,161u8,128u8,13u8,109u8,136u8,106u8,28u8,106u8, - 124u8,8u8,41u8,178u8,9u8,20u8,200u8,53u8,101u8,152u8,44u8,52u8,55u8,154u8,201u8,53u8,191u8,227u8,114u8,207u8, - 159u8,144u8,221u8,208u8,47u8,166u8,16u8,78u8,72u8,17u8,70u8,141u8,28u8,121u8,196u8,68u8,67u8,219u8,86u8,117u8, - 96u8,254u8,95u8,228u8,170u8,150u8,243u8,1u8,210u8,22u8,173u8,134u8,87u8,18u8,244u8,32u8,166u8,216u8,233u8,54u8, - 219u8,242u8,78u8,234u8,245u8,139u8,117u8,68u8,253u8,195u8,27u8,5u8,55u8,151u8,213u8,227u8,75u8,57u8,108u8,252u8, - 228u8,163u8,126u8,97u8,22u8,168u8,153u8,45u8,165u8,164u8,111u8,146u8,188u8,220u8,146u8,187u8,183u8,122u8,38u8,97u8, - 212u8,100u8,42u8,97u8,191u8,177u8,140u8,230u8,184u8,12u8,96u8,15u8,233u8,129u8,171u8,152u8,173u8,167u8,230u8,226u8, - 105u8,242u8,48u8,11u8,133u8,249u8,150u8,20u8,27u8,126u8,176u8,108u8,61u8,97u8,154u8,11u8,11u8,173u8,222u8,84u8, - 91u8,39u8,149u8,177u8,237u8,12u8,187u8,22u8,109u8,101u8,65u8,57u8,192u8,98u8,5u8,223u8,78u8,23u8,164u8,47u8, - 48u8,137u8,170u8,4u8,197u8,5u8,53u8,53u8,26u8,60u8,176u8,71u8,174u8,119u8,131u8,228u8,24u8,198u8,205u8,37u8, - 217u8,153u8,131u8,133u8,222u8,28u8,56u8,93u8,91u8,44u8,161u8,181u8,135u8,85u8,185u8,148u8,69u8,251u8,144u8,172u8, - 40u8,31u8,77u8,125u8,246u8,176u8,131u8,232u8,133u8,194u8,104u8,166u8,164u8,57u8,61u8,92u8,154u8,145u8,51u8,125u8, - 76u8,166u8,104u8,40u8,12u8,177u8,1u8,30u8,24u8,85u8,178u8,49u8,31u8,8u8,37u8,47u8,117u8,50u8,189u8,15u8, - 85u8,61u8,205u8,70u8,208u8,242u8,4u8,52u8,96u8,247u8,7u8,22u8,211u8,208u8,84u8,5u8,134u8,26u8,41u8,30u8, - 81u8,85u8,207u8,76u8,5u8,65u8,139u8,131u8,141u8,150u8,78u8,26u8,61u8,72u8,230u8,171u8,140u8,243u8,31u8,249u8, - 136u8,94u8,65u8,170u8,130u8,248u8,230u8,100u8,185u8,194u8,46u8,92u8,84u8,181u8,171u8,85u8,35u8,97u8,248u8,192u8, - 27u8,244u8,133u8,46u8,41u8,251u8,19u8,57u8,189u8,210u8,70u8,234u8,223u8,114u8,29u8,154u8,10u8,189u8,113u8,238u8, - 205u8,134u8,202u8,124u8,144u8,202u8,71u8,124u8,229u8,159u8,171u8,168u8,126u8,46u8,125u8,84u8,90u8,69u8,213u8,42u8, - 62u8,90u8,191u8,248u8,157u8,183u8,130u8,209u8,129u8,106u8,241u8,131u8,106u8,153u8,15u8,40u8,32u8,213u8,41u8,157u8, - 184u8,103u8,4u8,74u8,41u8,28u8,70u8,225u8,80u8,198u8,6u8,106u8,201u8,81u8,15u8,36u8,26u8,21u8,164u8,16u8, - 53u8,86u8,46u8,136u8,86u8,3u8,55u8,211u8,97u8,13u8,177u8,242u8,160u8,188u8,88u8,159u8,141u8,242u8,99u8,62u8, - 76u8,111u8,12u8,70u8,125u8,43u8,84u8,149u8,86u8,140u8,154u8,158u8,87u8,36u8,28u8,237u8,183u8,211u8,145u8,28u8, - 183u8,6u8,218u8,86u8,236u8,154u8,89u8,65u8,234u8,170u8,137u8,107u8,180u8,1u8,239u8,98u8,142u8,121u8,207u8,106u8, - 127u8,7u8,203u8,201u8,244u8,72u8,184u8,248u8,97u8,14u8,170u8,200u8,235u8,64u8,219u8,21u8,123u8,212u8,163u8,6u8, - 146u8,29u8,15u8,241u8,240u8,28u8,16u8,213u8,220u8,60u8,135u8,173u8,16u8,135u8,125u8,64u8,198u8,245u8,199u8,117u8, - 255u8,105u8,240u8,147u8,213u8,156u8,135u8,205u8,211u8,189u8,184u8,101u8,70u8,94u8,229u8,186u8,2u8,80u8,197u8,64u8, - 94u8,254u8,129u8,185u8,243u8,219u8,44u8,11u8,242u8,21u8,233u8,209u8,13u8,94u8,83u8,81u8,93u8,138u8,54u8,82u8, - 69u8,171u8,54u8,173u8,152u8,13u8,150u8,83u8,222u8,187u8,68u8,105u8,248u8,172u8,22u8,153u8,210u8,218u8,50u8,123u8, - 250u8,99u8,172u8,193u8,151u8,159u8,206u8,183u8,224u8,214u8,223u8,168u8,69u8,183u8,105u8,163u8,70u8,154u8,57u8,47u8, - 179u8,41u8,65u8,165u8,144u8,199u8,67u8,70u8,205u8,247u8,183u8,30u8,71u8,156u8,9u8,68u8,230u8,153u8,233u8,199u8, - 86u8,183u8,105u8,118u8,84u8,199u8,164u8,93u8,117u8,81u8,159u8,28u8,146u8,106u8,118u8,192u8,213u8,45u8,232u8,127u8, - 139u8,173u8,204u8,23u8,247u8,165u8,153u8,192u8,197u8,68u8,193u8,143u8,159u8,99u8,103u8,116u8,225u8,81u8,193u8,174u8, - 224u8,172u8,41u8,247u8,193u8,39u8,21u8,31u8,116u8,82u8,162u8,110u8,112u8,83u8,109u8,231u8,217u8,113u8,27u8,137u8, - 59u8,152u8,14u8,84u8,84u8,152u8,131u8,102u8,136u8,87u8,42u8,198u8,81u8,241u8,36u8,99u8,173u8,57u8,119u8,45u8, - 46u8,115u8,16u8,235u8,143u8,5u8,38u8,125u8,72u8,179u8,132u8,52u8,90u8,143u8,215u8,217u8,56u8,125u8,63u8,69u8, - 213u8,150u8,170u8,148u8,255u8,110u8,54u8,138u8,79u8,53u8,119u8,230u8,119u8,108u8,163u8,144u8,33u8,130u8,90u8,250u8, - 179u8,219u8,103u8,145u8,23u8,212u8,36u8,40u8,51u8,118u8,196u8,31u8,192u8,18u8,177u8,175u8,70u8,104u8,139u8,22u8, - 32u8,85u8,80u8,138u8,181u8,33u8,34u8,67u8,231u8,72u8,88u8,29u8,243u8,23u8,228u8,224u8,186u8,91u8,144u8,186u8, - 61u8,121u8,106u8,38u8,168u8,74u8,102u8,114u8,147u8,204u8,130u8,235u8,108u8,173u8,0u8,125u8,146u8,206u8,177u8,232u8, - 38u8,70u8,239u8,234u8,169u8,231u8,95u8,52u8,87u8,182u8,225u8,115u8,29u8,142u8,66u8,239u8,177u8,163u8,200u8,166u8, - 50u8,58u8,70u8,92u8,216u8,65u8,89u8,127u8,61u8,125u8,243u8,250u8,213u8,233u8,219u8,139u8,159u8,199u8,151u8,111u8, - 79u8,223u8,254u8,114u8,57u8,126u8,125u8,126u8,250u8,242u8,237u8,235u8,191u8,158u8,217u8,138u8,160u8,94u8,226u8,35u8, - 153u8,111u8,211u8,141u8,30u8,165u8,253u8,128u8,132u8,80u8,47u8,64u8,29u8,144u8,31u8,93u8,15u8,134u8,210u8,63u8, - 75u8,183u8,224u8,157u8,166u8,53u8,17u8,176u8,120u8,214u8,44u8,75u8,105u8,164u8,41u8,171u8,177u8,247u8,192u8,133u8, - 55u8,246u8,12u8,119u8,221u8,91u8,46u8,139u8,186u8,113u8,55u8,138u8,152u8,4u8,45u8,29u8,189u8,60u8,46u8,40u8, - 102u8,66u8,214u8,241u8,161u8,178u8,90u8,204u8,165u8,233u8,189u8,6u8,174u8,224u8,182u8,19u8,213u8,27u8,66u8,115u8, - 253u8,133u8,17u8,250u8,6u8,60u8,91u8,210u8,23u8,229u8,226u8,124u8,8u8,106u8,5u8,202u8,237u8,61u8,204u8,15u8, - 177u8,195u8,80u8,90u8,243u8,112u8,66u8,61u8,14u8,225u8,147u8,59u8,156u8,130u8,84u8,37u8,253u8,112u8,60u8,72u8, - 127u8,240u8,33u8,21u8,201u8,11u8,163u8,186u8,245u8,68u8,75u8,14u8,73u8,252u8,221u8,244u8,33u8,85u8,27u8,184u8, - 201u8,67u8,250u8,248u8,24u8,100u8,185u8,196u8,35u8,160u8,86u8,24u8,189u8,92u8,237u8,189u8,7u8,216u8,64u8,13u8, - 76u8,48u8,207u8,63u8,110u8,236u8,252u8,35u8,199u8,249u8,55u8,239u8,59u8,138u8,139u8,48u8,183u8,9u8,35u8,191u8, - 27u8,52u8,149u8,13u8,21u8,126u8,112u8,152u8,67u8,123u8,194u8,26u8,188u8,34u8,220u8,63u8,193u8,184u8,188u8,183u8, - 4u8,215u8,136u8,152u8,246u8,211u8,217u8,249u8,171u8,215u8,231u8,127u8,30u8,171u8,194u8,90u8,117u8,219u8,238u8,129u8, - 147u8,187u8,38u8,165u8,220u8,196u8,110u8,26u8,40u8,236u8,151u8,247u8,32u8,253u8,163u8,102u8,38u8,234u8,164u8,25u8, - 57u8,61u8,76u8,174u8,84u8,204u8,57u8,115u8,44u8,2u8,83u8,67u8,2u8,170u8,115u8,224u8,181u8,51u8,96u8,62u8, - 97u8,2u8,122u8,24u8,249u8,214u8,116u8,185u8,121u8,83u8,61u8,239u8,177u8,75u8,195u8,94u8,44u8,188u8,128u8,65u8, - 54u8,15u8,37u8,77u8,245u8,73u8,54u8,68u8,72u8,225u8,232u8,189u8,192u8,20u8,213u8,124u8,183u8,2u8,204u8,101u8, - 105u8,5u8,11u8,30u8,252u8,154u8,173u8,171u8,168u8,106u8,149u8,81u8,185u8,23u8,14u8,224u8,159u8,200u8,131u8,202u8, - 145u8,103u8,189u8,74u8,192u8,149u8,9u8,119u8,220u8,138u8,72u8,176u8,5u8,35u8,50u8,134u8,233u8,210u8,27u8,30u8, - 245u8,97u8,180u8,177u8,238u8,151u8,24u8,33u8,23u8,248u8,80u8,81u8,103u8,98u8,134u8,240u8,161u8,142u8,61u8,114u8, - 224u8,85u8,169u8,158u8,221u8,208u8,238u8,250u8,100u8,250u8,62u8,26u8,97u8,31u8,104u8,119u8,92u8,240u8,137u8,31u8, - 43u8,41u8,138u8,219u8,181u8,9u8,86u8,51u8,76u8,251u8,61u8,102u8,68u8,125u8,74u8,33u8,107u8,149u8,91u8,4u8, - 57u8,179u8,14u8,14u8,173u8,236u8,92u8,161u8,243u8,182u8,8u8,90u8,174u8,147u8,41u8,244u8,187u8,105u8,223u8,23u8, - 197u8,145u8,63u8,219u8,165u8,242u8,97u8,70u8,101u8,41u8,36u8,101u8,13u8,159u8,200u8,109u8,172u8,200u8,115u8,135u8, - 109u8,73u8,104u8,94u8,225u8,222u8,91u8,81u8,222u8,109u8,28u8,47u8,99u8,80u8,243u8,133u8,154u8,236u8,192u8,74u8, - 245u8,129u8,194u8,201u8,203u8,246u8,208u8,235u8,13u8,98u8,122u8,129u8,29u8,52u8,76u8,255u8,56u8,60u8,14u8,99u8, - 188u8,41u8,14u8,26u8,102u8,48u8,26u8,244u8,194u8,81u8,31u8,219u8,247u8,30u8,50u8,204u8,48u8,238u8,118u8,143u8, - 187u8,131u8,56u8,138u8,235u8,14u8,83u8,235u8,204u8,70u8,253u8,104u8,16u8,30u8,119u8,251u8,199u8,189u8,162u8,252u8, - 164u8,88u8,111u8,35u8,125u8,88u8,113u8,138u8,38u8,201u8,59u8,20u8,235u8,239u8,83u8,17u8,206u8,27u8,127u8,146u8, - 146u8,210u8,147u8,11u8,163u8,115u8,208u8,195u8,153u8,218u8,53u8,6u8,34u8,87u8,233u8,75u8,15u8,26u8,61u8,66u8, - 122u8,16u8,216u8,224u8,6u8,17u8,80u8,97u8,175u8,27u8,17u8,67u8,86u8,52u8,114u8,103u8,190u8,148u8,236u8,70u8, - 25u8,197u8,203u8,14u8,197u8,208u8,65u8,115u8,37u8,166u8,163u8,40u8,58u8,142u8,142u8,187u8,189u8,193u8,160u8,223u8, - 45u8,90u8,126u8,19u8,23u8,160u8,46u8,138u8,236u8,153u8,43u8,27u8,134u8,254u8,206u8,246u8,213u8,250u8,158u8,55u8, - 151u8,154u8,167u8,192u8,140u8,223u8,126u8,245u8,15u8,94u8,25u8,203u8,131u8,198u8,97u8,217u8,69u8,248u8,80u8,200u8, - 1u8,124u8,115u8,216u8,237u8,245u8,97u8,240u8,104u8,84u8,127u8,139u8,234u8,40u8,143u8,138u8,219u8,113u8,212u8,27u8, - 13u8,226u8,33u8,64u8,54u8,118u8,145u8,102u8,83u8,106u8,198u8,222u8,177u8,42u8,46u8,213u8,99u8,188u8,218u8,45u8, - 183u8,139u8,219u8,127u8,191u8,102u8,226u8,159u8,163u8,85u8,246u8,106u8,38u8,190u8,63u8,3u8,86u8,76u8,85u8,221u8, - 250u8,161u8,45u8,221u8,195u8,53u8,144u8,110u8,115u8,153u8,1u8,123u8,116u8,238u8,173u8,221u8,95u8,187u8,185u8,252u8, - 253u8,110u8,65u8,71u8,242u8,98u8,214u8,121u8,37u8,32u8,113u8,21u8,92u8,163u8,159u8,113u8,155u8,171u8,141u8,245u8, - 8u8,224u8,39u8,9u8,70u8,251u8,130u8,32u8,166u8,136u8,141u8,228u8,65u8,141u8,213u8,150u8,235u8,72u8,251u8,1u8, - 189u8,25u8,112u8,71u8,222u8,210u8,226u8,149u8,251u8,77u8,199u8,229u8,61u8,119u8,197u8,206u8,114u8,98u8,88u8,68u8, - 27u8,226u8,98u8,67u8,122u8,198u8,113u8,11u8,35u8,17u8,192u8,177u8,204u8,85u8,53u8,195u8,104u8,45u8,224u8,117u8, - 155u8,6u8,94u8,143u8,131u8,175u8,62u8,240u8,142u8,121u8,228u8,130u8,19u8,116u8,77u8,67u8,96u8,16u8,118u8,13u8, - 13u8,251u8,160u8,253u8,15u8,224u8,127u8,209u8,65u8,251u8,143u8,226u8,112u8,68u8,33u8,240u8,40u8,251u8,199u8,80u8, - 228u8,110u8,115u8,251u8,31u8,194u8,242u8,135u8,8u8,129u8,250u8,251u8,63u8,6u8,81u8,150u8,196u8,93u8,248u8,72u8, - 7u8,175u8,28u8,210u8,167u8,186u8,240u8,222u8,161u8,93u8,172u8,167u8,187u8,205u8,6u8,36u8,157u8,229u8,189u8,217u8, - 154u8,218u8,115u8,27u8,85u8,80u8,7u8,10u8,221u8,44u8,143u8,164u8,14u8,148u8,199u8,72u8,40u8,39u8,219u8,228u8, - 93u8,100u8,159u8,239u8,167u8,120u8,107u8,68u8,93u8,48u8,23u8,226u8,86u8,186u8,13u8,225u8,61u8,226u8,107u8,204u8, - 156u8,77u8,97u8,205u8,22u8,169u8,113u8,220u8,29u8,118u8,99u8,56u8,184u8,216u8,108u8,80u8,243u8,224u8,126u8,7u8, - 171u8,102u8,226u8,103u8,15u8,196u8,167u8,225u8,129u8,120u8,24u8,175u8,194u8,71u8,110u8,11u8,28u8,70u8,134u8,55u8, - 215u8,109u8,10u8,116u8,247u8,33u8,87u8,94u8,110u8,204u8,66u8,231u8,155u8,137u8,207u8,21u8,85u8,176u8,48u8,20u8, - 207u8,244u8,112u8,230u8,75u8,178u8,210u8,24u8,44u8,18u8,81u8,241u8,74u8,185u8,96u8,119u8,124u8,24u8,76u8,192u8, - 230u8,210u8,235u8,226u8,92u8,254u8,153u8,184u8,8u8,53u8,240u8,204u8,164u8,55u8,80u8,61u8,8u8,145u8,122u8,241u8, - 112u8,56u8,138u8,123u8,195u8,40u8,82u8,101u8,183u8,193u8,65u8,20u8,17u8,220u8,221u8,44u8,166u8,55u8,142u8,68u8, - 181u8,156u8,92u8,101u8,5u8,213u8,92u8,14u8,35u8,161u8,189u8,115u8,120u8,173u8,13u8,31u8,0u8,201u8,129u8,128u8, - 100u8,92u8,98u8,157u8,215u8,42u8,42u8,177u8,72u8,85u8,0u8,73u8,58u8,235u8,20u8,180u8,217u8,163u8,109u8,187u8, - 88u8,44u8,73u8,9u8,138u8,41u8,75u8,121u8,96u8,2u8,31u8,244u8,70u8,93u8,160u8,113u8,140u8,178u8,56u8,152u8, - 192u8,75u8,102u8,234u8,247u8,135u8,24u8,92u8,27u8,23u8,248u8,37u8,155u8,33u8,208u8,193u8,32u8,238u8,14u8,70u8, - 189u8,46u8,72u8,35u8,181u8,9u8,180u8,1u8,7u8,198u8,96u8,48u8,28u8,1u8,112u8,193u8,105u8,117u8,76u8,240u8, - 73u8,10u8,158u8,253u8,7u8,77u8,32u8,162u8,32u8,16u8,115u8,123u8,65u8,32u8,86u8,115u8,24u8,176u8,143u8,251u8, - 131u8,33u8,170u8,160u8,195u8,129u8,111u8,38u8,42u8,88u8,134u8,123u8,115u8,67u8,207u8,132u8,195u8,184u8,215u8,29u8, - 13u8,71u8,81u8,239u8,184u8,194u8,132u8,205u8,25u8,189u8,1u8,162u8,49u8,208u8,37u8,232u8,35u8,199u8,29u8,117u8, - 138u8,143u8,178u8,17u8,90u8,182u8,131u8,127u8,230u8,227u8,108u8,51u8,75u8,55u8,99u8,218u8,66u8,239u8,115u8,31u8, - 180u8,223u8,69u8,193u8,169u8,207u8,141u8,208u8,68u8,85u8,243u8,238u8,1u8,141u8,205u8,188u8,102u8,119u8,95u8,23u8, - 181u8,238u8,1u8,157u8,208u8,188u8,147u8,149u8,20u8,234u8,100u8,89u8,142u8,158u8,226u8,0u8,122u8,255u8,35u8,52u8, - 33u8,105u8,114u8,73u8,88u8,189u8,3u8,89u8,73u8,117u8,213u8,162u8,64u8,196u8,168u8,114u8,41u8,175u8,143u8,179u8, - 25u8,217u8,30u8,32u8,142u8,45u8,16u8,71u8,213u8,27u8,104u8,29u8,0u8,226u8,184u8,114u8,176u8,167u8,179u8,146u8, - 45u8,250u8,194u8,235u8,150u8,115u8,61u8,236u8,228u8,203u8,202u8,174u8,150u8,244u8,179u8,122u8,248u8,26u8,55u8,135u8, - 65u8,221u8,181u8,189u8,71u8,133u8,223u8,36u8,219u8,222u8,168u8,62u8,152u8,187u8,155u8,76u8,214u8,180u8,74u8,128u8, - 103u8,47u8,230u8,243u8,20u8,45u8,199u8,26u8,132u8,115u8,165u8,173u8,48u8,22u8,6u8,193u8,19u8,16u8,225u8,250u8, - 212u8,131u8,67u8,179u8,229u8,247u8,105u8,41u8,225u8,134u8,66u8,133u8,182u8,18u8,238u8,205u8,53u8,121u8,62u8,117u8, - 179u8,43u8,162u8,135u8,233u8,176u8,235u8,184u8,106u8,43u8,212u8,190u8,173u8,50u8,96u8,172u8,119u8,6u8,140u8,252u8, - 93u8,156u8,54u8,233u8,65u8,55u8,75u8,188u8,199u8,205u8,82u8,80u8,162u8,160u8,164u8,135u8,209u8,39u8,66u8,254u8, - 78u8,244u8,130u8,221u8,141u8,30u8,159u8,229u8,198u8,229u8,165u8,135u8,124u8,165u8,240u8,140u8,139u8,14u8,75u8,81u8, - 81u8,243u8,144u8,126u8,223u8,33u8,42u8,152u8,231u8,84u8,129u8,200u8,139u8,104u8,112u8,127u8,204u8,174u8,81u8,42u8, - 188u8,252u8,88u8,139u8,139u8,246u8,87u8,133u8,92u8,164u8,64u8,78u8,148u8,197u8,119u8,134u8,112u8,86u8,225u8,169u8, - 5u8,85u8,206u8,14u8,98u8,48u8,52u8,244u8,173u8,118u8,253u8,171u8,253u8,25u8,227u8,199u8,164u8,24u8,147u8,22u8, - 138u8,160u8,119u8,160u8,235u8,242u8,247u8,174u8,17u8,55u8,165u8,199u8,150u8,234u8,111u8,101u8,253u8,120u8,120u8,185u8, - 22u8,189u8,104u8,29u8,210u8,206u8,52u8,91u8,173u8,22u8,57u8,233u8,15u8,129u8,222u8,211u8,40u8,126u8,50u8,232u8, - 127u8,229u8,218u8,166u8,153u8,150u8,169u8,21u8,102u8,26u8,244u8,1u8,101u8,210u8,41u8,30u8,207u8,73u8,186u8,186u8, - 221u8,222u8,127u8,183u8,27u8,190u8,104u8,181u8,27u8,83u8,197u8,77u8,226u8,83u8,242u8,103u8,57u8,66u8,217u8,25u8, - 147u8,14u8,139u8,82u8,248u8,89u8,173u8,175u8,223u8,223u8,188u8,78u8,191u8,242u8,38u8,155u8,163u8,199u8,251u8,84u8, - 165u8,114u8,246u8,40u8,36u8,113u8,1u8,180u8,111u8,138u8,112u8,11u8,128u8,35u8,102u8,155u8,144u8,224u8,142u8,43u8, - 250u8,208u8,213u8,190u8,94u8,247u8,174u8,171u8,5u8,84u8,232u8,93u8,143u8,168u8,231u8,192u8,114u8,179u8,69u8,172u8, - 214u8,189u8,82u8,13u8,233u8,240u8,228u8,213u8,74u8,171u8,194u8,162u8,187u8,164u8,114u8,84u8,135u8,254u8,26u8,36u8, - 240u8,207u8,58u8,11u8,56u8,81u8,169u8,236u8,193u8,181u8,204u8,26u8,102u8,92u8,79u8,56u8,78u8,105u8,178u8,255u8, - 38u8,195u8,128u8,156u8,49u8,200u8,158u8,57u8,150u8,152u8,202u8,199u8,191u8,166u8,247u8,126u8,55u8,23u8,72u8,163u8, - 151u8,103u8,231u8,151u8,191u8,92u8,142u8,255u8,114u8,246u8,247u8,113u8,164u8,126u8,240u8,211u8,197u8,79u8,99u8,71u8, - 109u8,204u8,90u8,197u8,4u8,202u8,122u8,107u8,178u8,19u8,22u8,221u8,33u8,245u8,160u8,147u8,38u8,14u8,185u8,171u8, - 55u8,12u8,178u8,33u8,219u8,53u8,147u8,209u8,248u8,57u8,126u8,19u8,132u8,79u8,144u8,115u8,31u8,212u8,176u8,232u8, - 120u8,212u8,119u8,198u8,125u8,233u8,89u8,141u8,166u8,159u8,160u8,69u8,60u8,104u8,95u8,177u8,165u8,28u8,109u8,240u8, - 224u8,113u8,57u8,195u8,227u8,110u8,191u8,125u8,80u8,131u8,105u8,152u8,20u8,7u8,241u8,173u8,169u8,184u8,189u8,19u8, - 121u8,245u8,192u8,118u8,244u8,209u8,113u8,239u8,120u8,255u8,100u8,187u8,202u8,199u8,109u8,68u8,26u8,218u8,199u8,45u8, - 30u8,240u8,29u8,55u8,97u8,120u8,105u8,50u8,131u8,203u8,95u8,71u8,147u8,178u8,247u8,190u8,21u8,167u8,93u8,10u8, - 58u8,180u8,141u8,164u8,211u8,4u8,139u8,248u8,22u8,113u8,18u8,202u8,108u8,182u8,65u8,178u8,219u8,102u8,43u8,0u8, - 241u8,148u8,20u8,243u8,221u8,164u8,100u8,220u8,25u8,190u8,113u8,11u8,143u8,204u8,72u8,169u8,223u8,37u8,92u8,113u8, - 179u8,123u8,86u8,254u8,250u8,96u8,95u8,210u8,241u8,32u8,38u8,123u8,241u8,224u8,45u8,71u8,33u8,223u8,70u8,1u8, - 22u8,188u8,45u8,53u8,106u8,240u8,124u8,63u8,240u8,167u8,80u8,58u8,148u8,237u8,46u8,230u8,98u8,79u8,7u8,226u8, - 118u8,116u8,220u8,239u8,147u8,165u8,185u8,113u8,91u8,224u8,222u8,7u8,89u8,119u8,1u8,81u8,116u8,251u8,81u8,68u8, - 150u8,230u8,68u8,89u8,54u8,73u8,17u8,170u8,212u8,41u8,20u8,210u8,40u8,221u8,25u8,17u8,174u8,46u8,54u8,203u8, - 40u8,175u8,128u8,126u8,56u8,214u8,213u8,96u8,61u8,21u8,210u8,221u8,250u8,131u8,254u8,176u8,79u8,220u8,161u8,14u8, - 148u8,22u8,152u8,115u8,8u8,219u8,43u8,192u8,76u8,240u8,204u8,246u8,250u8,199u8,81u8,183u8,231u8,198u8,76u8,129u8, - 0u8,15u8,196u8,117u8,251u8,49u8,250u8,163u8,227u8,193u8,200u8,121u8,54u8,180u8,22u8,167u8,64u8,175u8,5u8,47u8, - 192u8,65u8,59u8,15u8,120u8,172u8,67u8,219u8,44u8,219u8,195u8,187u8,112u8,88u8,43u8,201u8,88u8,224u8,151u8,232u8, - 98u8,87u8,53u8,28u8,109u8,239u8,169u8,162u8,136u8,5u8,173u8,70u8,74u8,233u8,171u8,209u8,94u8,54u8,224u8,195u8, - 106u8,239u8,99u8,222u8,194u8,34u8,223u8,110u8,22u8,19u8,44u8,188u8,45u8,78u8,197u8,58u8,134u8,102u8,184u8,128u8, - 195u8,34u8,104u8,79u8,85u8,38u8,18u8,53u8,210u8,74u8,7u8,72u8,196u8,97u8,16u8,20u8,71u8,95u8,192u8,55u8, - 4u8,109u8,87u8,185u8,120u8,235u8,239u8,114u8,47u8,118u8,211u8,235u8,2u8,30u8,29u8,51u8,118u8,163u8,236u8,77u8, - 227u8,58u8,130u8,43u8,60u8,16u8,215u8,233u8,246u8,123u8,152u8,101u8,58u8,24u8,186u8,153u8,30u8,227u8,10u8,199u8, - 225u8,62u8,112u8,43u8,177u8,23u8,31u8,206u8,173u8,162u8,112u8,16u8,135u8,195u8,97u8,175u8,219u8,29u8,112u8,242u8, - 131u8,49u8,123u8,38u8,169u8,63u8,92u8,142u8,121u8,101u8,78u8,209u8,141u8,71u8,192u8,38u8,250u8,97u8,79u8,171u8, - 97u8,233u8,100u8,177u8,126u8,50u8,22u8,66u8,198u8,218u8,108u8,84u8,42u8,171u8,148u8,185u8,10u8,174u8,146u8,252u8, - 90u8,198u8,171u8,138u8,104u8,131u8,35u8,98u8,149u8,51u8,22u8,156u8,174u8,156u8,56u8,112u8,104u8,134u8,213u8,7u8, - 225u8,67u8,57u8,37u8,29u8,71u8,225u8,16u8,112u8,1u8,3u8,212u8,250u8,253u8,238u8,96u8,52u8,234u8,127u8,12u8, - 252u8,178u8,193u8,242u8,106u8,108u8,87u8,165u8,151u8,50u8,109u8,1u8,42u8,47u8,102u8,35u8,179u8,35u8,175u8,111u8, - 184u8,98u8,86u8,195u8,10u8,185u8,51u8,202u8,251u8,193u8,11u8,43u8,169u8,227u8,16u8,3u8,88u8,3u8,82u8,104u8, - 212u8,239u8,197u8,253u8,81u8,175u8,31u8,141u8,202u8,9u8,82u8,80u8,111u8,17u8,237u8,112u8,212u8,123u8,16u8,129u8, - 20u8,142u8,188u8,63u8,234u8,246u8,44u8,188u8,246u8,245u8,137u8,87u8,178u8,177u8,176u8,99u8,57u8,51u8,157u8,20u8, - 160u8,185u8,78u8,217u8,130u8,169u8,218u8,198u8,6u8,174u8,253u8,252u8,63u8,240u8,172u8,17u8,224u8,43u8,141u8,143u8, - 133u8,194u8,215u8,123u8,20u8,214u8,63u8,12u8,143u8,227u8,30u8,92u8,231u8,17u8,83u8,231u8,122u8,131u8,174u8,85u8, - 26u8,180u8,182u8,180u8,73u8,246u8,34u8,102u8,8u8,142u8,200u8,131u8,152u8,71u8,252u8,93u8,112u8,245u8,227u8,235u8, - 243u8,241u8,203u8,139u8,215u8,231u8,151u8,227u8,139u8,243u8,241u8,229u8,15u8,167u8,63u8,159u8,161u8,37u8,236u8,226u8, - 205u8,21u8,104u8,130u8,59u8,35u8,57u8,10u8,171u8,233u8,177u8,70u8,168u8,34u8,158u8,225u8,160u8,125u8,115u8,57u8, - 83u8,172u8,173u8,186u8,9u8,109u8,177u8,62u8,154u8,47u8,23u8,215u8,55u8,219u8,66u8,134u8,135u8,101u8,229u8,167u8, - 25u8,169u8,55u8,79u8,139u8,39u8,137u8,171u8,166u8,8u8,111u8,234u8,36u8,105u8,197u8,199u8,253u8,110u8,124u8,76u8, - 98u8,253u8,41u8,86u8,15u8,48u8,19u8,108u8,0u8,159u8,12u8,194u8,135u8,100u8,159u8,3u8,51u8,245u8,87u8,94u8, - 9u8,149u8,64u8,212u8,177u8,147u8,9u8,48u8,232u8,0u8,205u8,0u8,226u8,105u8,61u8,2u8,129u8,179u8,102u8,49u8, - 43u8,245u8,120u8,110u8,210u8,107u8,152u8,58u8,221u8,56u8,115u8,3u8,84u8,166u8,86u8,247u8,206u8,217u8,63u8,7u8, - 65u8,28u8,68u8,232u8,69u8,30u8,5u8,87u8,68u8,137u8,83u8,92u8,33u8,166u8,12u8,40u8,128u8,1u8,139u8,213u8, - 140u8,119u8,247u8,97u8,189u8,164u8,64u8,238u8,178u8,65u8,65u8,235u8,241u8,99u8,151u8,117u8,113u8,129u8,201u8,206u8, - 64u8,64u8,70u8,100u8,82u8,24u8,111u8,137u8,14u8,29u8,146u8,67u8,202u8,17u8,146u8,24u8,184u8,110u8,179u8,205u8, - 150u8,221u8,113u8,151u8,233u8,114u8,78u8,29u8,89u8,244u8,166u8,107u8,8u8,57u8,67u8,142u8,149u8,253u8,135u8,106u8, - 71u8,172u8,143u8,190u8,191u8,135u8,57u8,91u8,206u8,132u8,239u8,174u8,176u8,226u8,72u8,7u8,235u8,105u8,26u8,143u8, - 150u8,121u8,153u8,167u8,0u8,242u8,235u8,84u8,250u8,6u8,247u8,112u8,52u8,171u8,235u8,218u8,163u8,250u8,8u8,254u8, - 168u8,235u8,252u8,192u8,206u8,102u8,117u8,23u8,197u8,158u8,69u8,245u8,201u8,125u8,124u8,139u8,174u8,25u8,204u8,85u8, - 168u8,0u8,41u8,94u8,133u8,250u8,228u8,62u8,171u8,112u8,205u8,240u8,240u8,190u8,111u8,117u8,239u8,77u8,187u8,191u8, - 61u8,112u8,173u8,229u8,1u8,119u8,141u8,85u8,228u8,4u8,255u8,253u8,53u8,42u8,242u8,201u8,84u8,251u8,196u8,201u8, - 213u8,244u8,77u8,234u8,72u8,242u8,0u8,238u8,73u8,255u8,4u8,181u8,61u8,148u8,206u8,222u8,77u8,205u8,187u8,45u8, - 245u8,126u8,159u8,70u8,53u8,229u8,226u8,44u8,66u8,55u8,74u8,155u8,117u8,10u8,7u8,210u8,3u8,200u8,255u8,104u8, - 212u8,54u8,98u8,118u8,143u8,8u8,139u8,91u8,28u8,236u8,187u8,129u8,24u8,20u8,189u8,1u8,119u8,243u8,240u8,63u8, - 44u8,253u8,25u8,175u8,56u8,91u8,52u8,201u8,85u8,142u8,160u8,163u8,72u8,25u8,179u8,108u8,2u8,46u8,162u8,248u8, - 187u8,132u8,139u8,248u8,200u8,240u8,10u8,176u8,125u8,121u8,157u8,85u8,136u8,140u8,226u8,161u8,67u8,20u8,179u8,106u8, - 32u8,31u8,244u8,134u8,3u8,1u8,114u8,250u8,135u8,101u8,207u8,164u8,251u8,240u8,170u8,212u8,184u8,98u8,249u8,212u8, - 33u8,6u8,88u8,215u8,81u8,57u8,202u8,113u8,134u8,61u8,114u8,247u8,132u8,242u8,15u8,143u8,111u8,237u8,74u8,29u8, - 240u8,234u8,227u8,243u8,176u8,41u8,117u8,198u8,37u8,226u8,136u8,143u8,76u8,196u8,161u8,167u8,83u8,130u8,56u8,236u8, - 161u8,135u8,71u8,156u8,225u8,49u8,24u8,10u8,35u8,134u8,56u8,244u8,15u8,19u8,113u8,24u8,9u8,20u8,35u8,14u8, - 59u8,193u8,106u8,134u8,76u8,62u8,98u8,185u8,33u8,83u8,27u8,251u8,16u8,104u8,84u8,193u8,201u8,120u8,16u8,246u8, - 67u8,152u8,140u8,66u8,131u8,253u8,241u8,241u8,69u8,161u8,202u8,238u8,172u8,139u8,124u8,12u8,170u8,25u8,109u8,173u8, - 240u8,57u8,67u8,211u8,200u8,208u8,180u8,19u8,52u8,137u8,246u8,247u8,57u8,67u8,243u8,99u8,11u8,229u8,212u8,106u8, - 40u8,162u8,90u8,159u8,237u8,72u8,241u8,194u8,28u8,91u8,187u8,104u8,161u8,144u8,36u8,222u8,134u8,146u8,190u8,26u8, - 200u8,231u8,51u8,169u8,247u8,235u8,135u8,130u8,246u8,11u8,93u8,226u8,86u8,32u8,104u8,52u8,168u8,29u8,7u8,106u8, - 188u8,234u8,14u8,187u8,156u8,128u8,223u8,108u8,149u8,230u8,170u8,164u8,75u8,44u8,60u8,32u8,2u8,47u8,166u8,196u8, - 230u8,25u8,29u8,218u8,158u8,221u8,235u8,169u8,236u8,135u8,97u8,232u8,41u8,16u8,231u8,106u8,73u8,200u8,44u8,95u8, - 193u8,29u8,79u8,19u8,219u8,173u8,103u8,233u8,6u8,236u8,72u8,216u8,145u8,7u8,123u8,132u8,60u8,127u8,65u8,147u8, - 119u8,124u8,70u8,93u8,90u8,95u8,164u8,122u8,150u8,144u8,119u8,156u8,163u8,170u8,77u8,165u8,189u8,91u8,239u8,142u8, - 180u8,74u8,136u8,145u8,154u8,136u8,82u8,9u8,8u8,112u8,48u8,216u8,148u8,93u8,238u8,94u8,188u8,37u8,91u8,68u8, - 149u8,123u8,101u8,14u8,220u8,69u8,47u8,52u8,250u8,56u8,42u8,186u8,74u8,51u8,71u8,73u8,13u8,238u8,252u8,249u8, - 15u8,177u8,71u8,7u8,146u8,90u8,90u8,240u8,225u8,27u8,42u8,197u8,69u8,185u8,12u8,192u8,190u8,86u8,33u8,102u8, - 30u8,184u8,97u8,186u8,207u8,190u8,239u8,44u8,31u8,136u8,24u8,31u8,241u8,68u8,141u8,18u8,87u8,93u8,95u8,2u8, - 120u8,101u8,138u8,43u8,79u8,55u8,108u8,106u8,197u8,244u8,108u8,122u8,31u8,35u8,157u8,245u8,30u8,13u8,65u8,171u8, - 81u8,228u8,183u8,126u8,104u8,124u8,23u8,96u8,17u8,89u8,223u8,18u8,213u8,177u8,168u8,228u8,64u8,186u8,51u8,227u8, - 0u8,160u8,23u8,102u8,107u8,112u8,85u8,96u8,143u8,105u8,22u8,44u8,202u8,220u8,33u8,184u8,194u8,252u8,48u8,39u8, - 232u8,160u8,160u8,94u8,108u8,185u8,239u8,179u8,26u8,76u8,203u8,92u8,145u8,110u8,97u8,195u8,143u8,5u8,141u8,175u8, - 195u8,9u8,133u8,234u8,96u8,216u8,171u8,190u8,231u8,131u8,3u8,35u8,222u8,183u8,226u8,104u8,77u8,176u8,32u8,162u8, - 42u8,50u8,27u8,23u8,233u8,72u8,217u8,33u8,64u8,89u8,146u8,143u8,175u8,35u8,232u8,131u8,115u8,172u8,34u8,249u8, - 166u8,130u8,121u8,164u8,158u8,48u8,210u8,149u8,169u8,163u8,138u8,229u8,244u8,145u8,69u8,170u8,189u8,218u8,240u8,22u8, - 108u8,166u8,167u8,111u8,198u8,35u8,31u8,70u8,228u8,26u8,5u8,142u8,70u8,14u8,59u8,207u8,72u8,35u8,75u8,202u8, - 146u8,16u8,33u8,104u8,137u8,165u8,244u8,3u8,9u8,135u8,213u8,182u8,80u8,227u8,60u8,136u8,5u8,239u8,191u8,119u8, - 160u8,0u8,162u8,179u8,62u8,102u8,117u8,3u8,225u8,245u8,29u8,75u8,149u8,192u8,93u8,231u8,28u8,56u8,52u8,237u8, - 217u8,156u8,5u8,23u8,91u8,7u8,44u8,113u8,83u8,96u8,25u8,21u8,203u8,204u8,159u8,194u8,201u8,142u8,62u8,94u8, - 177u8,63u8,62u8,88u8,200u8,232u8,153u8,165u8,234u8,255u8,109u8,148u8,83u8,31u8,229u8,254u8,190u8,212u8,58u8,109u8, - 143u8,78u8,99u8,236u8,24u8,133u8,61u8,205u8,94u8,138u8,150u8,82u8,123u8,54u8,219u8,136u8,201u8,231u8,57u8,9u8, - 248u8,132u8,226u8,25u8,117u8,25u8,142u8,175u8,213u8,34u8,209u8,39u8,193u8,110u8,208u8,235u8,40u8,230u8,201u8,130u8, - 47u8,221u8,101u8,166u8,149u8,135u8,188u8,118u8,84u8,197u8,92u8,138u8,246u8,186u8,22u8,101u8,163u8,172u8,222u8,123u8, - 135u8,113u8,85u8,49u8,174u8,248u8,192u8,156u8,176u8,205u8,108u8,148u8,244u8,224u8,116u8,96u8,187u8,188u8,221u8,166u8, - 231u8,94u8,155u8,22u8,189u8,244u8,234u8,62u8,196u8,172u8,108u8,46u8,239u8,235u8,2u8,21u8,97u8,0u8,29u8,20u8, - 214u8,78u8,252u8,131u8,88u8,120u8,13u8,131u8,185u8,225u8,235u8,135u8,198u8,190u8,232u8,228u8,8u8,62u8,106u8,18u8, - 173u8,72u8,12u8,32u8,124u8,49u8,129u8,199u8,212u8,160u8,158u8,169u8,7u8,199u8,204u8,39u8,235u8,96u8,147u8,69u8, - 40u8,212u8,114u8,77u8,151u8,226u8,11u8,118u8,227u8,150u8,111u8,192u8,166u8,73u8,182u8,217u8,100u8,119u8,227u8,235u8, - 101u8,6u8,124u8,226u8,59u8,125u8,154u8,23u8,69u8,239u8,183u8,36u8,16u8,217u8,108u8,29u8,37u8,136u8,111u8,12u8, - 187u8,70u8,100u8,181u8,33u8,174u8,46u8,173u8,26u8,210u8,90u8,239u8,34u8,170u8,240u8,41u8,195u8,226u8,23u8,96u8, - 25u8,79u8,22u8,192u8,31u8,223u8,211u8,144u8,148u8,169u8,185u8,70u8,250u8,101u8,193u8,214u8,36u8,73u8,42u8,31u8, - 146u8,131u8,226u8,180u8,232u8,64u8,170u8,253u8,9u8,211u8,49u8,163u8,74u8,93u8,165u8,251u8,164u8,58u8,52u8,188u8, - 193u8,40u8,198u8,90u8,236u8,190u8,164u8,34u8,73u8,144u8,136u8,67u8,212u8,111u8,90u8,149u8,16u8,108u8,148u8,119u8, - 147u8,198u8,39u8,131u8,240u8,28u8,226u8,91u8,44u8,154u8,6u8,142u8,146u8,108u8,189u8,77u8,64u8,144u8,104u8,125u8, - 141u8,207u8,63u8,49u8,96u8,133u8,241u8,133u8,83u8,130u8,14u8,99u8,130u8,93u8,45u8,164u8,133u8,182u8,15u8,105u8, - 23u8,243u8,160u8,69u8,63u8,111u8,43u8,187u8,228u8,139u8,102u8,156u8,143u8,29u8,111u8,176u8,205u8,182u8,240u8,23u8, - 17u8,98u8,248u8,90u8,232u8,126u8,246u8,89u8,137u8,50u8,185u8,131u8,47u8,155u8,152u8,164u8,126u8,172u8,188u8,249u8, - 91u8,144u8,130u8,55u8,206u8,88u8,49u8,31u8,42u8,84u8,222u8,87u8,247u8,250u8,91u8,85u8,36u8,84u8,246u8,41u8, - 48u8,177u8,229u8,68u8,186u8,54u8,65u8,159u8,10u8,120u8,179u8,199u8,185u8,63u8,113u8,77u8,207u8,150u8,254u8,219u8, - 23u8,255u8,31u8,125u8,190u8,236u8,119u8,238u8,21u8,2u8,0u8,0u8,0u8,12u8,103u8,97u8,115u8,95u8,115u8,99u8, - 104u8,101u8,100u8,117u8,108u8,101u8,170u8,7u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,86u8, - 77u8,111u8,219u8,70u8,16u8,189u8,251u8,87u8,76u8,46u8,129u8,84u8,168u8,22u8,26u8,20u8,69u8,65u8,197u8,70u8, - 141u8,88u8,77u8,13u8,36u8,54u8,16u8,57u8,190u8,46u8,86u8,203u8,33u8,185u8,48u8,185u8,171u8,238u8,135u8,100u8, - 181u8,208u8,127u8,239u8,44u8,73u8,201u8,228u8,74u8,150u8,213u8,180u8,61u8,148u8,23u8,65u8,203u8,157u8,55u8,111u8, - 102u8,222u8,12u8,103u8,60u8,30u8,195u8,125u8,33u8,45u8,84u8,58u8,245u8,37u8,66u8,138u8,153u8,84u8,104u8,193u8, - 58u8,227u8,133u8,179u8,192u8,85u8,10u8,21u8,186u8,66u8,167u8,22u8,156u8,6u8,169u8,164u8,147u8,188u8,148u8,127u8, - 32u8,184u8,2u8,33u8,231u8,116u8,83u8,20u8,24u8,76u8,71u8,176u8,42u8,164u8,40u8,32u8,149u8,194u8,113u8,71u8, - 16u8,133u8,94u8,65u8,229u8,69u8,113u8,54u8,38u8,31u8,210u8,129u8,208u8,214u8,213u8,32u8,248u8,132u8,194u8,59u8, - 132u8,207u8,122u8,137u8,160u8,85u8,141u8,164u8,208u8,173u8,180u8,121u8,60u8,63u8,107u8,121u8,240u8,133u8,211u8,150u8, - 101u8,134u8,87u8,24u8,206u8,147u8,132u8,92u8,177u8,173u8,43u8,248u8,243u8,12u8,232u8,241u8,22u8,137u8,103u8,154u8, - 36u8,104u8,140u8,54u8,147u8,254u8,25u8,5u8,32u8,85u8,158u8,36u8,179u8,250u8,55u8,122u8,185u8,68u8,225u8,130u8, - 197u8,238u8,116u8,207u8,157u8,65u8,161u8,85u8,38u8,115u8,111u8,184u8,147u8,90u8,77u8,94u8,190u8,105u8,215u8,214u8, - 97u8,197u8,120u8,154u8,26u8,180u8,22u8,237u8,145u8,171u8,222u8,201u8,50u8,73u8,50u8,163u8,43u8,54u8,95u8,187u8, - 163u8,87u8,45u8,241u8,227u8,57u8,50u8,10u8,59u8,196u8,80u8,255u8,249u8,200u8,237u8,135u8,154u8,212u8,105u8,118u8, - 109u8,120u8,153u8,145u8,72u8,69u8,220u8,79u8,40u8,82u8,157u8,229u8,246u8,214u8,184u8,214u8,1u8,194u8,194u8,232u8, - 165u8,76u8,49u8,237u8,149u8,22u8,106u8,182u8,192u8,13u8,2u8,86u8,11u8,183u8,6u8,109u8,72u8,9u8,75u8,210u8, - 65u8,90u8,27u8,83u8,170u8,172u8,131u8,233u8,205u8,237u8,195u8,213u8,167u8,155u8,107u8,246u8,241u8,106u8,198u8,102u8, - 31u8,126u8,155u8,94u8,127u8,253u8,52u8,77u8,192u8,255u8,244u8,35u8,92u8,192u8,15u8,147u8,151u8,110u8,254u8,58u8, - 189u8,186u8,255u8,250u8,101u8,202u8,30u8,166u8,95u8,102u8,55u8,119u8,183u8,91u8,131u8,119u8,45u8,177u8,70u8,136u8, - 64u8,177u8,79u8,149u8,51u8,107u8,40u8,2u8,47u8,10u8,146u8,244u8,38u8,244u8,98u8,61u8,130u8,212u8,232u8,69u8, - 43u8,136u8,240u8,60u8,226u8,58u8,129u8,166u8,232u8,163u8,221u8,33u8,81u8,173u8,113u8,155u8,147u8,77u8,140u8,60u8, - 219u8,198u8,25u8,192u8,9u8,224u8,5u8,104u8,36u8,2u8,18u8,109u8,2u8,141u8,124u8,222u8,111u8,57u8,93u8,30u8, - 71u8,125u8,120u8,247u8,10u8,110u8,134u8,220u8,121u8,131u8,108u8,137u8,198u8,146u8,214u8,58u8,76u8,143u8,122u8,237u8, - 5u8,19u8,234u8,119u8,167u8,202u8,53u8,8u8,94u8,150u8,84u8,190u8,212u8,135u8,12u8,64u8,91u8,228u8,243u8,250u8, - 206u8,194u8,207u8,75u8,41u8,6u8,141u8,32u8,134u8,144u8,121u8,213u8,233u8,231u8,65u8,44u8,16u8,120u8,107u8,101u8, - 174u8,208u8,140u8,160u8,219u8,122u8,108u8,94u8,234u8,249u8,142u8,139u8,255u8,249u8,114u8,216u8,9u8,36u8,110u8,133u8, - 36u8,225u8,244u8,99u8,28u8,139u8,176u8,99u8,95u8,195u8,201u8,14u8,162u8,49u8,120u8,51u8,120u8,211u8,120u8,72u8, - 18u8,105u8,89u8,173u8,185u8,193u8,219u8,61u8,26u8,195u8,17u8,212u8,157u8,79u8,151u8,26u8,49u8,50u8,110u8,114u8, - 95u8,81u8,194u8,6u8,7u8,181u8,56u8,28u8,182u8,154u8,106u8,18u8,6u8,247u8,119u8,215u8,119u8,3u8,74u8,231u8, - 48u8,1u8,130u8,21u8,143u8,32u8,179u8,190u8,236u8,105u8,44u8,6u8,193u8,74u8,138u8,74u8,185u8,157u8,101u8,137u8, - 174u8,151u8,146u8,36u8,42u8,246u8,5u8,60u8,119u8,248u8,96u8,159u8,244u8,115u8,172u8,21u8,205u8,63u8,230u8,244u8, - 251u8,158u8,249u8,101u8,156u8,156u8,126u8,254u8,91u8,243u8,77u8,183u8,107u8,3u8,77u8,174u8,96u8,142u8,219u8,226u8, - 207u8,169u8,67u8,213u8,247u8,162u8,224u8,82u8,65u8,78u8,78u8,140u8,226u8,74u8,96u8,24u8,189u8,126u8,145u8,210u8, - 108u8,222u8,155u8,221u8,93u8,125u8,212u8,186u8,176u8,232u8,88u8,215u8,237u8,183u8,171u8,131u8,139u8,223u8,189u8,36u8, - 57u8,116u8,147u8,52u8,138u8,50u8,246u8,255u8,84u8,16u8,169u8,101u8,128u8,79u8,36u8,14u8,27u8,87u8,240u8,151u8, - 152u8,93u8,183u8,73u8,14u8,73u8,136u8,52u8,51u8,215u8,68u8,100u8,197u8,114u8,98u8,197u8,75u8,86u8,121u8,247u8, - 42u8,232u8,100u8,15u8,82u8,225u8,138u8,253u8,43u8,202u8,236u8,230u8,49u8,6u8,61u8,143u8,198u8,21u8,92u8,94u8, - 192u8,177u8,247u8,163u8,30u8,106u8,61u8,209u8,78u8,73u8,121u8,244u8,89u8,24u8,70u8,236u8,190u8,189u8,125u8,195u8, - 243u8,93u8,148u8,252u8,56u8,198u8,103u8,95u8,155u8,231u8,57u8,92u8,90u8,140u8,202u8,120u8,88u8,1u8,39u8,212u8, - 63u8,60u8,140u8,252u8,214u8,19u8,32u8,212u8,228u8,21u8,251u8,126u8,236u8,155u8,255u8,184u8,242u8,255u8,44u8,183u8, - 167u8,78u8,181u8,152u8,114u8,135u8,196u8,166u8,63u8,167u8,111u8,145u8,102u8,26u8,13u8,47u8,250u8,14u8,230u8,57u8, - 26u8,136u8,118u8,51u8,176u8,26u8,106u8,37u8,113u8,106u8,119u8,80u8,58u8,197u8,102u8,30u8,218u8,181u8,18u8,219u8, - 245u8,178u8,153u8,123u8,233u8,129u8,161u8,23u8,158u8,8u8,176u8,187u8,253u8,225u8,160u8,63u8,113u8,163u8,25u8,217u8, - 89u8,183u8,88u8,99u8,114u8,100u8,82u8,54u8,23u8,194u8,118u8,210u8,95u8,231u8,122u8,159u8,208u8,238u8,222u8,23u8, - 60u8,28u8,70u8,221u8,130u8,117u8,146u8,118u8,90u8,162u8,66u8,50u8,30u8,62u8,135u8,242u8,229u8,158u8,27u8,174u8, - 92u8,107u8,82u8,106u8,158u8,182u8,139u8,248u8,170u8,206u8,82u8,134u8,97u8,87u8,230u8,198u8,133u8,53u8,34u8,136u8, - 167u8,125u8,251u8,228u8,186u8,30u8,29u8,65u8,88u8,46u8,2u8,250u8,223u8,77u8,231u8,230u8,236u8,47u8,45u8,0u8, - 127u8,245u8,124u8,12u8,0u8,0u8,0u8,0u8,16u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,99u8,111u8,110u8, - 116u8,114u8,97u8,99u8,116u8,249u8,79u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,61u8,107u8, - 115u8,27u8,199u8,145u8,223u8,253u8,43u8,214u8,151u8,178u8,12u8,68u8,16u8,68u8,194u8,162u8,227u8,128u8,162u8,234u8, - 104u8,73u8,118u8,152u8,179u8,36u8,151u8,68u8,37u8,185u8,74u8,185u8,214u8,75u8,96u8,0u8,110u8,4u8,96u8,113u8, - 187u8,11u8,81u8,140u8,163u8,255u8,126u8,221u8,61u8,239u8,215u8,98u8,65u8,2u8,212u8,195u8,76u8,41u8,137u8,132u8, - 221u8,157u8,233u8,233u8,233u8,215u8,244u8,107u8,238u8,223u8,191u8,159u8,28u8,207u8,102u8,197u8,69u8,82u8,213u8,217u8, - 27u8,86u8,86u8,73u8,182u8,24u8,39u8,197u8,146u8,149u8,89u8,93u8,192u8,191u8,234u8,34u8,97u8,139u8,154u8,149u8, - 73u8,70u8,47u8,228u8,139u8,105u8,50u8,42u8,22u8,117u8,153u8,141u8,234u8,228u8,34u8,175u8,207u8,147u8,146u8,93u8, - 100u8,229u8,56u8,169u8,206u8,179u8,18u8,30u8,246u8,191u8,184u8,15u8,3u8,158u8,158u8,179u8,100u8,158u8,229u8,139u8, - 36u8,27u8,141u8,138u8,213u8,162u8,198u8,175u8,102u8,197u8,52u8,31u8,37u8,248u8,155u8,63u8,16u8,252u8,165u8,202u8, - 171u8,186u8,74u8,138u8,73u8,50u8,72u8,150u8,89u8,89u8,87u8,67u8,26u8,104u8,191u8,159u8,156u8,194u8,27u8,111u8, - 170u8,228u8,28u8,32u8,156u8,175u8,70u8,231u8,240u8,238u8,124u8,158u8,87u8,85u8,94u8,44u8,146u8,5u8,99u8,99u8, - 130u8,240u8,140u8,193u8,71u8,57u8,128u8,189u8,170u8,241u8,159u8,53u8,76u8,47u8,87u8,0u8,3u8,156u8,231u8,85u8, - 2u8,127u8,112u8,170u8,55u8,108u8,204u8,161u8,206u8,22u8,0u8,202u8,168u8,100u8,89u8,133u8,128u8,44u8,1u8,244u8, - 81u8,190u8,204u8,102u8,52u8,105u8,54u8,71u8,152u8,97u8,148u8,172u8,254u8,186u8,74u8,86u8,203u8,113u8,86u8,195u8, - 103u8,236u8,45u8,43u8,47u8,147u8,58u8,159u8,51u8,107u8,124u8,88u8,255u8,255u8,173u8,24u8,194u8,174u8,1u8,235u8, - 209u8,27u8,28u8,159u8,52u8,221u8,184u8,204u8,46u8,170u8,100u8,178u8,90u8,140u8,171u8,94u8,2u8,223u8,232u8,199u8, - 52u8,97u8,5u8,239u8,140u8,206u8,89u8,165u8,209u8,206u8,209u8,56u8,232u8,39u8,79u8,0u8,45u8,101u8,126u8,182u8, - 170u8,97u8,88u8,66u8,15u8,13u8,130u8,139u8,212u8,91u8,212u8,209u8,51u8,87u8,93u8,218u8,62u8,185u8,149u8,29u8, - 250u8,139u8,2u8,33u8,155u8,193u8,243u8,85u8,197u8,23u8,128u8,27u8,6u8,83u8,206u8,139u8,49u8,155u8,1u8,2u8, - 138u8,183u8,249u8,152u8,141u8,105u8,218u8,179u8,75u8,122u8,97u8,89u8,20u8,179u8,116u8,245u8,237u8,3u8,194u8,40u8, - 226u8,78u8,126u8,130u8,136u8,145u8,216u8,99u8,184u8,163u8,128u8,190u8,17u8,75u8,178u8,74u8,175u8,139u8,62u8,198u8, - 221u8,95u8,205u8,87u8,51u8,64u8,95u8,37u8,200u8,132u8,47u8,140u8,102u8,121u8,250u8,46u8,155u8,47u8,103u8,44u8, - 153u8,0u8,237u8,169u8,205u8,62u8,150u8,88u8,195u8,193u8,241u8,179u8,24u8,213u8,193u8,254u8,169u8,29u8,0u8,120u8, - 71u8,217u8,108u8,70u8,47u8,209u8,103u8,169u8,248u8,38u8,149u8,223u8,116u8,186u8,252u8,171u8,253u8,189u8,61u8,24u8, - 39u8,39u8,76u8,210u8,148u8,249u8,34u8,175u8,243u8,108u8,38u8,128u8,70u8,212u8,25u8,228u8,117u8,4u8,239u8,127u8, - 37u8,232u8,103u8,206u8,178u8,69u8,101u8,111u8,252u8,69u8,62u8,155u8,193u8,178u8,70u8,44u8,127u8,203u8,240u8,77u8, - 220u8,157u8,108u8,113u8,105u8,172u8,122u8,172u8,86u8,13u8,11u8,91u8,48u8,193u8,98u8,52u8,49u8,225u8,135u8,70u8, - 0u8,218u8,229u8,80u8,143u8,105u8,254u8,243u8,162u8,194u8,191u8,114u8,62u8,97u8,192u8,10u8,240u8,68u8,178u8,145u8, - 36u8,73u8,90u8,86u8,49u8,155u8,193u8,123u8,98u8,175u8,92u8,36u8,41u8,250u8,57u8,53u8,33u8,174u8,88u8,141u8, - 244u8,12u8,35u8,191u8,205u8,102u8,249u8,152u8,126u8,91u8,192u8,254u8,227u8,196u8,64u8,151u8,11u8,216u8,68u8,128u8, - 100u8,124u8,217u8,75u8,254u8,69u8,72u8,194u8,145u8,245u8,155u8,240u8,181u8,137u8,106u8,90u8,203u8,112u8,136u8,111u8, - 166u8,234u8,165u8,20u8,94u8,162u8,169u8,191u8,129u8,37u8,79u8,80u8,108u8,84u8,5u8,50u8,12u8,112u8,77u8,207u8, - 165u8,142u8,105u8,134u8,115u8,8u8,12u8,209u8,218u8,23u8,192u8,227u8,231u8,64u8,70u8,251u8,7u8,98u8,155u8,248u8, - 50u8,30u8,244u8,147u8,23u8,114u8,9u8,35u8,216u8,120u8,124u8,13u8,161u8,144u8,172u8,151u8,234u8,61u8,235u8,203u8, - 141u8,232u8,224u8,24u8,247u8,112u8,195u8,187u8,176u8,143u8,7u8,98u8,215u8,37u8,194u8,87u8,139u8,89u8,65u8,178u8, - 96u8,82u8,22u8,115u8,7u8,46u8,194u8,25u8,103u8,75u8,34u8,69u8,192u8,183u8,18u8,15u8,40u8,70u8,112u8,118u8, - 41u8,19u8,232u8,115u8,36u8,42u8,96u8,145u8,253u8,7u8,7u8,124u8,86u8,62u8,213u8,61u8,53u8,41u8,64u8,163u8, - 1u8,236u8,242u8,45u8,89u8,178u8,197u8,24u8,145u8,56u8,54u8,216u8,155u8,102u8,167u8,137u8,17u8,7u8,242u8,235u8, - 186u8,168u8,97u8,94u8,196u8,142u8,69u8,124u8,197u8,5u8,60u8,67u8,28u8,28u8,72u8,174u8,132u8,105u8,114u8,177u8, - 239u8,7u8,253u8,228u8,21u8,162u8,125u8,94u8,148u8,28u8,247u8,52u8,226u8,50u8,171u8,42u8,54u8,22u8,243u8,227u8, - 6u8,72u8,116u8,3u8,204u8,244u8,38u8,159u8,16u8,40u8,207u8,220u8,147u8,76u8,64u8,112u8,150u8,205u8,50u8,36u8, - 16u8,152u8,102u8,255u8,207u8,7u8,54u8,97u8,209u8,164u8,184u8,35u8,85u8,96u8,75u8,146u8,12u8,55u8,26u8,32u8, - 202u8,241u8,115u8,18u8,44u8,37u8,123u8,155u8,23u8,43u8,189u8,198u8,243u8,12u8,56u8,8u8,161u8,57u8,99u8,35u8, - 132u8,91u8,201u8,171u8,179u8,25u8,144u8,77u8,94u8,127u8,205u8,119u8,109u8,204u8,150u8,69u8,149u8,115u8,238u8,224u8, - 82u8,158u8,38u8,150u8,80u8,192u8,70u8,73u8,62u8,153u8,228u8,101u8,85u8,19u8,140u8,121u8,73u8,140u8,103u8,128u8, - 35u8,105u8,64u8,17u8,202u8,159u8,15u8,212u8,150u8,225u8,30u8,170u8,205u8,54u8,8u8,167u8,159u8,252u8,108u8,144u8, - 0u8,205u8,42u8,73u8,128u8,107u8,159u8,253u8,63u8,239u8,241u8,145u8,96u8,219u8,97u8,139u8,127u8,142u8,109u8,175u8, - 181u8,181u8,166u8,162u8,245u8,55u8,148u8,111u8,229u8,183u8,128u8,56u8,33u8,17u8,9u8,191u8,156u8,114u8,83u8,78u8, - 174u8,48u8,55u8,255u8,119u8,114u8,160u8,229u8,26u8,39u8,219u8,94u8,114u8,113u8,158u8,131u8,182u8,156u8,34u8,203u8, - 103u8,227u8,49u8,135u8,180u8,110u8,34u8,189u8,126u8,242u8,61u8,200u8,115u8,174u8,2u8,224u8,39u8,169u8,31u8,178u8, - 250u8,188u8,167u8,117u8,25u8,199u8,29u8,0u8,139u8,67u8,192u8,172u8,226u8,45u8,107u8,41u8,85u8,141u8,175u8,209u8, - 130u8,76u8,234u8,196u8,201u8,15u8,14u8,238u8,33u8,156u8,65u8,32u8,148u8,92u8,228u8,43u8,255u8,147u8,32u8,98u8, - 162u8,95u8,162u8,221u8,74u8,177u8,193u8,129u8,68u8,161u8,32u8,152u8,201u8,106u8,54u8,187u8,180u8,200u8,38u8,204u8, - 217u8,79u8,86u8,132u8,180u8,128u8,132u8,238u8,41u8,114u8,114u8,199u8,254u8,147u8,148u8,69u8,201u8,99u8,33u8,249u8, - 20u8,208u8,12u8,244u8,138u8,254u8,71u8,149u8,124u8,171u8,120u8,214u8,102u8,86u8,4u8,27u8,244u8,168u8,245u8,80u8, - 137u8,204u8,254u8,23u8,160u8,130u8,87u8,0u8,113u8,182u8,172u8,139u8,42u8,157u8,148u8,217u8,156u8,93u8,20u8,229u8, - 155u8,225u8,208u8,213u8,98u8,201u8,111u8,95u8,36u8,240u8,31u8,84u8,224u8,85u8,61u8,30u8,14u8,207u8,70u8,213u8, - 161u8,253u8,11u8,43u8,203u8,162u8,116u8,126u8,171u8,242u8,233u8,130u8,185u8,63u8,190u8,101u8,163u8,26u8,223u8,84u8, - 191u8,242u8,201u8,233u8,153u8,212u8,251u8,195u8,225u8,111u8,175u8,216u8,108u8,210u8,75u8,126u8,134u8,31u8,222u8,31u8, - 134u8,94u8,173u8,114u8,84u8,225u8,233u8,60u8,91u8,170u8,151u8,95u8,209u8,79u8,207u8,178u8,229u8,123u8,111u8,116u8, - 99u8,105u8,130u8,81u8,141u8,175u8,16u8,200u8,199u8,217u8,50u8,59u8,203u8,103u8,121u8,125u8,233u8,77u8,103u8,126u8, - 75u8,63u8,32u8,50u8,135u8,195u8,99u8,252u8,251u8,99u8,248u8,107u8,195u8,251u8,252u8,77u8,49u8,17u8,190u8,219u8, - 52u8,56u8,88u8,121u8,4u8,214u8,83u8,252u8,255u8,191u8,192u8,198u8,161u8,4u8,98u8,243u8,188u8,78u8,233u8,73u8, - 211u8,167u8,66u8,33u8,138u8,137u8,94u8,92u8,180u8,94u8,144u8,177u8,207u8,147u8,124u8,42u8,208u8,134u8,38u8,113u8, - 157u8,188u8,58u8,254u8,233u8,116u8,152u8,240u8,221u8,122u8,184u8,250u8,238u8,17u8,136u8,164u8,179u8,255u8,90u8,75u8, - 40u8,255u8,37u8,198u8,64u8,114u8,126u8,85u8,163u8,104u8,23u8,38u8,237u8,124u8,5u8,99u8,2u8,255u8,130u8,253u8, - 54u8,3u8,227u8,173u8,38u8,74u8,156u8,3u8,63u8,114u8,38u8,65u8,225u8,157u8,151u8,168u8,215u8,10u8,105u8,239u8, - 11u8,85u8,1u8,68u8,139u8,138u8,222u8,183u8,8u8,250u8,6u8,176u8,79u8,79u8,158u8,191u8,122u8,253u8,195u8,15u8, - 39u8,143u8,79u8,158u8,62u8,63u8,77u8,95u8,157u8,30u8,255u8,207u8,211u8,244u8,248u8,217u8,139u8,215u8,207u8,97u8, - 9u8,104u8,71u8,130u8,61u8,117u8,168u8,224u8,122u8,172u8,37u8,50u8,176u8,201u8,8u8,112u8,155u8,77u8,185u8,158u8, - 226u8,18u8,245u8,140u8,213u8,23u8,140u8,45u8,146u8,61u8,226u8,30u8,208u8,177u8,206u8,76u8,127u8,59u8,254u8,233u8, - 228u8,73u8,250u8,248u8,197u8,179u8,103u8,39u8,175u8,94u8,157u8,188u8,120u8,158u8,254u8,252u8,244u8,229u8,99u8,152u8, - 247u8,248u8,199u8,167u8,114u8,182u8,193u8,161u8,129u8,5u8,146u8,95u8,56u8,252u8,162u8,240u8,172u8,165u8,202u8,26u8, - 251u8,249u8,11u8,130u8,253u8,228u8,249u8,143u8,48u8,252u8,243u8,211u8,151u8,199u8,143u8,79u8,211u8,31u8,96u8,21u8, - 79u8,224u8,127u8,95u8,242u8,101u8,189u8,148u8,83u8,124u8,163u8,167u8,120u8,238u8,143u8,171u8,22u8,97u8,28u8,7u8, - 44u8,73u8,57u8,129u8,93u8,25u8,111u8,48u8,249u8,11u8,88u8,228u8,241u8,233u8,11u8,53u8,253u8,3u8,123u8,133u8, - 214u8,154u8,208u8,84u8,250u8,154u8,118u8,123u8,206u8,202u8,41u8,179u8,167u8,121u8,124u8,12u8,59u8,244u8,236u8,233u8, - 203u8,31u8,159u8,122u8,211u8,189u8,146u8,131u8,31u8,232u8,193u8,79u8,3u8,38u8,38u8,40u8,43u8,50u8,22u8,19u8, - 246u8,142u8,142u8,110u8,100u8,60u8,103u8,139u8,69u8,65u8,115u8,150u8,236u8,158u8,176u8,106u8,173u8,121u8,189u8,181u8, - 29u8,255u8,244u8,242u8,233u8,241u8,147u8,255u8,77u8,159u8,254u8,227u8,228u8,149u8,158u8,250u8,91u8,19u8,173u8,53u8, - 28u8,64u8,139u8,213u8,20u8,108u8,254u8,81u8,141u8,166u8,182u8,210u8,125u8,82u8,222u8,11u8,69u8,193u8,127u8,159u8, - 231u8,211u8,243u8,90u8,168u8,32u8,169u8,101u8,16u8,52u8,169u8,186u8,196u8,32u8,130u8,150u8,23u8,236u8,29u8,12u8, - 191u8,44u8,70u8,231u8,113u8,66u8,6u8,48u8,79u8,254u8,246u8,84u8,208u8,243u8,233u8,139u8,244u8,239u8,39u8,167u8, - 127u8,121u8,242u8,242u8,248u8,239u8,18u8,216u8,63u8,25u8,68u8,13u8,26u8,2u8,246u8,88u8,178u8,25u8,3u8,0u8, - 153u8,121u8,218u8,195u8,179u8,159u8,58u8,152u8,218u8,219u8,46u8,24u8,6u8,246u8,216u8,223u8,230u8,239u8,12u8,126u8, - 126u8,150u8,189u8,203u8,231u8,171u8,121u8,178u8,88u8,205u8,207u8,112u8,192u8,137u8,165u8,67u8,43u8,155u8,109u8,209u8, - 90u8,174u8,86u8,203u8,101u8,81u8,90u8,140u8,250u8,236u8,248u8,31u8,39u8,207u8,94u8,63u8,3u8,150u8,121u8,254u8, - 4u8,119u8,227u8,9u8,224u8,254u8,229u8,201u8,247u8,175u8,79u8,129u8,143u8,212u8,30u8,12u8,246u8,196u8,172u8,127u8, - 248u8,39u8,40u8,240u8,98u8,5u8,60u8,154u8,78u8,203u8,98u8,181u8,236u8,84u8,35u8,88u8,195u8,17u8,215u8,90u8, - 105u8,247u8,23u8,122u8,9u8,64u8,88u8,1u8,77u8,8u8,42u8,252u8,17u8,223u8,123u8,12u8,132u8,2u8,150u8,31u8, - 0u8,249u8,219u8,251u8,47u8,2u8,47u8,61u8,150u8,132u8,132u8,92u8,89u8,145u8,152u8,226u8,10u8,142u8,47u8,53u8, - 121u8,9u8,250u8,183u8,68u8,195u8,69u8,155u8,223u8,25u8,29u8,41u8,16u8,159u8,51u8,148u8,94u8,134u8,85u8,103u8, - 34u8,161u8,111u8,142u8,34u8,79u8,255u8,197u8,2u8,44u8,4u8,16u8,191u8,100u8,7u8,129u8,45u8,53u8,34u8,253u8, - 79u8,99u8,25u8,195u8,88u8,106u8,187u8,58u8,47u8,86u8,179u8,49u8,167u8,102u8,60u8,229u8,145u8,123u8,67u8,14u8, - 173u8,128u8,34u8,124u8,245u8,244u8,239u8,168u8,61u8,193u8,224u8,2u8,156u8,85u8,195u8,68u8,252u8,165u8,103u8,67u8, - 100u8,26u8,37u8,96u8,181u8,22u8,168u8,42u8,0u8,38u8,169u8,43u8,196u8,145u8,19u8,55u8,15u8,207u8,42u8,18u8, - 102u8,126u8,232u8,19u8,14u8,128u8,124u8,225u8,26u8,55u8,106u8,10u8,26u8,46u8,133u8,225u8,134u8,174u8,18u8,210u8, - 96u8,232u8,37u8,167u8,90u8,250u8,58u8,43u8,65u8,122u8,94u8,149u8,37u8,60u8,179u8,73u8,172u8,135u8,103u8,255u8, - 217u8,138u8,56u8,75u8,225u8,202u8,50u8,176u8,149u8,183u8,193u8,112u8,70u8,224u8,49u8,10u8,252u8,58u8,120u8,216u8, - 54u8,94u8,208u8,80u8,155u8,83u8,164u8,184u8,160u8,33u8,217u8,29u8,22u8,56u8,127u8,69u8,190u8,130u8,149u8,143u8, - 208u8,239u8,112u8,193u8,200u8,255u8,67u8,104u8,80u8,230u8,3u8,105u8,46u8,219u8,3u8,65u8,138u8,79u8,160u8,107u8, - 178u8,170u8,87u8,37u8,211u8,147u8,114u8,219u8,136u8,227u8,202u8,53u8,65u8,248u8,204u8,46u8,229u8,34u8,149u8,34u8, - 189u8,190u8,97u8,151u8,6u8,181u8,186u8,10u8,184u8,26u8,106u8,59u8,232u8,161u8,164u8,1u8,151u8,238u8,31u8,245u8, - 190u8,48u8,23u8,71u8,22u8,135u8,129u8,144u8,136u8,43u8,131u8,219u8,33u8,48u8,129u8,97u8,161u8,60u8,124u8,76u8, - 239u8,58u8,227u8,211u8,11u8,143u8,52u8,2u8,249u8,137u8,37u8,125u8,91u8,0u8,15u8,5u8,71u8,121u8,77u8,47u8, - 252u8,13u8,159u8,187u8,223u8,194u8,2u8,88u8,157u8,226u8,81u8,99u8,181u8,12u8,126u8,251u8,18u8,95u8,248u8,137u8, - 158u8,187u8,223u8,194u8,250u8,249u8,121u8,37u8,248u8,225u8,241u8,120u8,76u8,202u8,217u8,159u8,209u8,61u8,72u8,70u8, - 230u8,165u8,215u8,180u8,53u8,225u8,173u8,218u8,56u8,48u8,133u8,87u8,77u8,47u8,4u8,97u8,224u8,126u8,185u8,84u8, - 18u8,121u8,240u8,243u8,87u8,244u8,142u8,244u8,79u8,132u8,214u8,110u8,81u8,118u8,4u8,5u8,166u8,155u8,207u8,29u8, - 67u8,159u8,48u8,130u8,95u8,171u8,79u8,45u8,240u8,109u8,186u8,229u8,59u8,235u8,32u8,137u8,232u8,120u8,92u8,22u8, - 203u8,158u8,39u8,125u8,57u8,203u8,6u8,196u8,151u8,68u8,69u8,232u8,209u8,108u8,156u8,182u8,17u8,43u8,112u8,28u8, - 95u8,251u8,222u8,251u8,176u8,230u8,73u8,231u8,12u8,149u8,94u8,135u8,254u8,1u8,42u8,106u8,173u8,33u8,60u8,28u8, - 6u8,21u8,82u8,92u8,103u8,197u8,17u8,101u8,51u8,188u8,224u8,165u8,53u8,196u8,25u8,28u8,45u8,184u8,65u8,113u8, - 6u8,110u8,216u8,165u8,134u8,205u8,32u8,38u8,15u8,252u8,190u8,70u8,59u8,197u8,148u8,90u8,203u8,253u8,178u8,168u8, - 77u8,203u8,145u8,171u8,45u8,97u8,13u8,168u8,72u8,110u8,177u8,85u8,34u8,137u8,133u8,158u8,217u8,96u8,186u8,34u8, - 107u8,235u8,96u8,218u8,211u8,89u8,130u8,110u8,39u8,40u8,225u8,135u8,60u8,218u8,150u8,224u8,114u8,67u8,146u8,114u8, - 55u8,128u8,104u8,255u8,74u8,42u8,252u8,43u8,113u8,130u8,50u8,160u8,14u8,18u8,147u8,35u8,158u8,119u8,141u8,185u8, - 48u8,209u8,67u8,112u8,41u8,10u8,97u8,64u8,3u8,52u8,1u8,9u8,116u8,219u8,0u8,40u8,146u8,238u8,150u8,168u8, - 205u8,211u8,41u8,55u8,131u8,58u8,27u8,14u8,71u8,59u8,237u8,4u8,4u8,176u8,206u8,243u8,101u8,142u8,126u8,155u8, - 246u8,224u8,253u8,225u8,159u8,111u8,115u8,118u8,241u8,139u8,58u8,86u8,189u8,100u8,96u8,25u8,114u8,35u8,81u8,140u8, - 33u8,93u8,150u8,96u8,110u8,179u8,114u8,118u8,169u8,66u8,30u8,220u8,176u8,156u8,24u8,81u8,188u8,232u8,145u8,95u8, - 70u8,213u8,140u8,179u8,191u8,154u8,208u8,62u8,6u8,98u8,80u8,76u8,31u8,182u8,193u8,248u8,39u8,79u8,94u8,197u8, - 3u8,154u8,161u8,105u8,40u8,156u8,21u8,153u8,67u8,91u8,229u8,227u8,130u8,85u8,232u8,4u8,160u8,35u8,58u8,159u8, - 105u8,185u8,58u8,155u8,65u8,28u8,22u8,78u8,17u8,252u8,139u8,212u8,68u8,110u8,199u8,85u8,252u8,254u8,174u8,116u8, - 213u8,223u8,128u8,205u8,201u8,93u8,84u8,9u8,179u8,88u8,111u8,37u8,58u8,107u8,203u8,58u8,96u8,186u8,146u8,167u8, - 64u8,76u8,162u8,199u8,238u8,30u8,170u8,47u8,103u8,172u8,246u8,141u8,105u8,208u8,244u8,119u8,206u8,10u8,64u8,200u8, - 69u8,58u8,157u8,21u8,16u8,136u8,120u8,72u8,19u8,62u8,18u8,3u8,117u8,251u8,222u8,23u8,135u8,134u8,133u8,175u8, - 61u8,148u8,124u8,140u8,142u8,247u8,118u8,47u8,185u8,163u8,64u8,233u8,155u8,232u8,104u8,73u8,48u8,116u8,22u8,45u8, - 253u8,211u8,106u8,135u8,168u8,73u8,7u8,147u8,209u8,139u8,245u8,21u8,16u8,200u8,172u8,88u8,76u8,149u8,31u8,216u8, - 136u8,18u8,155u8,135u8,40u8,233u8,92u8,161u8,176u8,54u8,16u8,91u8,87u8,77u8,44u8,79u8,56u8,155u8,80u8,221u8, - 199u8,64u8,109u8,136u8,165u8,84u8,98u8,41u8,85u8,88u8,106u8,71u8,114u8,232u8,146u8,248u8,60u8,201u8,77u8,101u8, - 30u8,172u8,167u8,53u8,195u8,79u8,138u8,161u8,102u8,63u8,136u8,193u8,169u8,76u8,122u8,186u8,136u8,116u8,188u8,80u8, - 132u8,153u8,167u8,16u8,164u8,37u8,53u8,235u8,39u8,64u8,83u8,65u8,187u8,244u8,119u8,78u8,81u8,65u8,156u8,180u8, - 160u8,46u8,8u8,244u8,174u8,48u8,31u8,131u8,212u8,94u8,201u8,152u8,240u8,48u8,86u8,67u8,245u8,226u8,62u8,143u8, - 250u8,138u8,136u8,180u8,233u8,140u8,21u8,238u8,149u8,160u8,170u8,84u8,159u8,15u8,236u8,207u8,35u8,212u8,139u8,177u8, - 96u8,220u8,240u8,243u8,204u8,202u8,197u8,144u8,233u8,61u8,106u8,180u8,111u8,248u8,104u8,166u8,200u8,228u8,146u8,22u8, - 221u8,95u8,42u8,176u8,95u8,84u8,44u8,152u8,143u8,241u8,97u8,149u8,174u8,69u8,83u8,28u8,236u8,150u8,170u8,183u8, - 131u8,118u8,76u8,34u8,255u8,167u8,251u8,73u8,80u8,112u8,104u8,64u8,24u8,111u8,83u8,194u8,214u8,3u8,66u8,76u8, - 59u8,141u8,161u8,49u8,205u8,49u8,89u8,109u8,33u8,244u8,138u8,249u8,70u8,183u8,165u8,54u8,215u8,142u8,245u8,80u8, - 144u8,186u8,74u8,58u8,172u8,63u8,237u8,91u8,201u8,94u8,166u8,195u8,147u8,232u8,78u8,164u8,95u8,117u8,63u8,32u8, - 153u8,9u8,216u8,109u8,231u8,211u8,104u8,3u8,74u8,251u8,84u8,228u8,163u8,142u8,86u8,83u8,166u8,193u8,57u8,156u8, - 175u8,0u8,245u8,124u8,169u8,157u8,59u8,27u8,75u8,79u8,207u8,13u8,221u8,138u8,108u8,202u8,21u8,200u8,192u8,201u8, - 149u8,14u8,4u8,122u8,67u8,57u8,242u8,214u8,11u8,12u8,11u8,201u8,107u8,118u8,241u8,140u8,59u8,193u8,35u8,219u8, - 8u8,32u8,119u8,190u8,228u8,195u8,57u8,24u8,239u8,26u8,111u8,241u8,35u8,22u8,45u8,116u8,2u8,36u8,206u8,212u8, - 3u8,25u8,225u8,215u8,251u8,138u8,195u8,67u8,132u8,186u8,97u8,43u8,195u8,138u8,109u8,196u8,61u8,115u8,85u8,10u8, - 94u8,54u8,216u8,52u8,252u8,162u8,223u8,184u8,79u8,230u8,166u8,24u8,241u8,93u8,12u8,153u8,80u8,102u8,88u8,141u8, - 124u8,6u8,216u8,27u8,81u8,118u8,11u8,70u8,79u8,200u8,193u8,134u8,209u8,49u8,154u8,54u8,150u8,85u8,152u8,84u8, - 75u8,56u8,76u8,78u8,114u8,230u8,154u8,55u8,98u8,67u8,64u8,155u8,150u8,151u8,220u8,244u8,136u8,100u8,26u8,122u8, - 62u8,212u8,59u8,60u8,200u8,208u8,187u8,142u8,219u8,110u8,173u8,127u8,162u8,33u8,134u8,243u8,98u8,137u8,56u8,192u8, - 28u8,71u8,140u8,150u8,80u8,52u8,233u8,226u8,156u8,137u8,5u8,32u8,10u8,130u8,84u8,43u8,2u8,39u8,70u8,56u8, - 66u8,146u8,31u8,255u8,14u8,38u8,197u8,241u8,204u8,220u8,4u8,62u8,107u8,131u8,78u8,146u8,188u8,207u8,198u8,41u8, - 79u8,133u8,57u8,74u8,120u8,110u8,134u8,20u8,160u8,15u8,85u8,58u8,199u8,35u8,37u8,67u8,248u8,210u8,13u8,186u8, - 137u8,197u8,69u8,112u8,16u8,62u8,110u8,199u8,34u8,93u8,87u8,24u8,245u8,56u8,150u8,123u8,22u8,40u8,189u8,48u8, - 54u8,123u8,145u8,101u8,11u8,112u8,62u8,0u8,1u8,54u8,144u8,94u8,16u8,3u8,219u8,164u8,66u8,26u8,122u8,72u8, - 41u8,52u8,198u8,78u8,125u8,244u8,4u8,217u8,218u8,97u8,241u8,165u8,77u8,56u8,193u8,229u8,36u8,143u8,142u8,146u8, - 189u8,228u8,206u8,157u8,200u8,211u8,135u8,152u8,51u8,188u8,215u8,179u8,198u8,33u8,117u8,63u8,28u8,230u8,11u8,74u8, - 156u8,73u8,179u8,114u8,186u8,154u8,195u8,235u8,157u8,230u8,36u8,150u8,174u8,30u8,196u8,32u8,126u8,17u8,62u8,22u8, - 166u8,174u8,14u8,80u8,187u8,9u8,61u8,34u8,222u8,37u8,19u8,122u8,128u8,216u8,11u8,55u8,253u8,86u8,30u8,19u8, - 217u8,44u8,159u8,230u8,152u8,49u8,103u8,228u8,248u8,152u8,243u8,5u8,210u8,125u8,36u8,51u8,119u8,66u8,51u8,165u8, - 152u8,55u8,105u8,167u8,52u8,13u8,135u8,104u8,178u8,201u8,87u8,248u8,23u8,40u8,230u8,189u8,119u8,58u8,93u8,199u8, - 84u8,208u8,206u8,20u8,41u8,43u8,0u8,156u8,21u8,124u8,76u8,180u8,104u8,188u8,44u8,119u8,80u8,127u8,0u8,27u8, - 21u8,2u8,175u8,97u8,55u8,34u8,201u8,75u8,8u8,147u8,137u8,144u8,19u8,158u8,51u8,158u8,255u8,155u8,9u8,58u8, - 146u8,65u8,42u8,110u8,2u8,136u8,98u8,3u8,140u8,54u8,99u8,22u8,170u8,174u8,24u8,168u8,116u8,14u8,210u8,152u8, - 205u8,216u8,84u8,166u8,143u8,102u8,139u8,203u8,98u8,193u8,250u8,190u8,152u8,44u8,165u8,31u8,138u8,172u8,101u8,100u8, - 223u8,225u8,80u8,252u8,146u8,22u8,19u8,95u8,163u8,70u8,213u8,185u8,28u8,199u8,83u8,235u8,243u8,226u8,45u8,75u8, - 235u8,66u8,9u8,91u8,244u8,62u8,123u8,122u8,55u8,229u8,54u8,149u8,50u8,13u8,14u8,131u8,6u8,0u8,101u8,191u8, - 80u8,246u8,143u8,144u8,116u8,65u8,46u8,206u8,49u8,73u8,217u8,201u8,28u8,234u8,175u8,55u8,34u8,210u8,249u8,170u8, - 142u8,44u8,169u8,133u8,105u8,9u8,31u8,39u8,17u8,155u8,226u8,176u8,89u8,4u8,124u8,25u8,53u8,82u8,26u8,173u8, - 147u8,32u8,255u8,139u8,69u8,75u8,243u8,109u8,93u8,42u8,84u8,215u8,100u8,255u8,8u8,249u8,57u8,28u8,77u8,53u8, - 3u8,152u8,237u8,172u8,8u8,82u8,10u8,77u8,126u8,238u8,200u8,176u8,204u8,71u8,20u8,105u8,100u8,115u8,69u8,144u8, - 50u8,183u8,68u8,41u8,33u8,56u8,44u8,215u8,57u8,44u8,219u8,156u8,117u8,156u8,79u8,38u8,140u8,114u8,65u8,140u8, - 26u8,21u8,75u8,22u8,24u8,222u8,100u8,161u8,107u8,18u8,239u8,39u8,204u8,180u8,232u8,233u8,4u8,21u8,144u8,20u8, - 182u8,196u8,213u8,138u8,141u8,127u8,214u8,137u8,42u8,239u8,168u8,94u8,54u8,129u8,134u8,32u8,136u8,129u8,163u8,186u8, - 136u8,166u8,204u8,136u8,212u8,77u8,157u8,169u8,192u8,85u8,105u8,182u8,236u8,220u8,81u8,192u8,246u8,18u8,41u8,115u8, - 44u8,154u8,215u8,196u8,174u8,144u8,200u8,157u8,156u8,54u8,122u8,76u8,183u8,114u8,152u8,157u8,239u8,120u8,216u8,138u8, - 24u8,203u8,240u8,77u8,136u8,252u8,52u8,138u8,220u8,252u8,42u8,155u8,231u8,149u8,132u8,180u8,169u8,212u8,4u8,208u8, - 126u8,162u8,49u8,176u8,94u8,61u8,218u8,175u8,80u8,186u8,26u8,32u8,187u8,90u8,149u8,148u8,175u8,51u8,46u8,184u8, - 103u8,231u8,45u8,110u8,70u8,145u8,204u8,177u8,90u8,70u8,30u8,178u8,85u8,16u8,71u8,229u8,54u8,249u8,9u8,234u8, - 238u8,224u8,47u8,48u8,189u8,238u8,34u8,175u8,192u8,78u8,203u8,146u8,105u8,153u8,179u8,9u8,101u8,251u8,213u8,53u8, - 22u8,43u8,1u8,193u8,47u8,11u8,128u8,15u8,85u8,27u8,216u8,23u8,165u8,37u8,132u8,209u8,78u8,123u8,195u8,216u8, - 82u8,164u8,120u8,152u8,169u8,76u8,34u8,147u8,81u8,236u8,107u8,81u8,184u8,147u8,90u8,80u8,91u8,174u8,1u8,35u8, - 111u8,11u8,148u8,49u8,201u8,118u8,56u8,227u8,223u8,43u8,38u8,247u8,166u8,32u8,243u8,39u8,89u8,62u8,35u8,60u8, - 128u8,173u8,131u8,181u8,100u8,118u8,26u8,57u8,125u8,135u8,21u8,61u8,246u8,34u8,3u8,89u8,81u8,250u8,192u8,203u8, - 97u8,236u8,52u8,102u8,243u8,57u8,178u8,200u8,76u8,120u8,10u8,115u8,167u8,22u8,238u8,38u8,165u8,235u8,12u8,104u8, - 91u8,64u8,26u8,178u8,181u8,57u8,95u8,201u8,6u8,163u8,33u8,221u8,225u8,55u8,159u8,215u8,45u8,210u8,52u8,232u8, - 55u8,98u8,131u8,189u8,15u8,90u8,78u8,177u8,184u8,206u8,125u8,33u8,39u8,168u8,2u8,70u8,9u8,10u8,216u8,66u8, - 146u8,211u8,210u8,149u8,105u8,215u8,86u8,5u8,15u8,132u8,74u8,120u8,116u8,92u8,155u8,59u8,96u8,107u8,155u8,39u8, - 186u8,54u8,39u8,166u8,77u8,77u8,129u8,86u8,14u8,154u8,212u8,115u8,29u8,116u8,219u8,156u8,232u8,175u8,172u8,140u8, - 131u8,78u8,63u8,28u8,173u8,211u8,164u8,162u8,109u8,31u8,224u8,149u8,196u8,251u8,118u8,14u8,158u8,13u8,74u8,194u8, - 5u8,186u8,111u8,104u8,13u8,115u8,98u8,115u8,1u8,222u8,55u8,166u8,173u8,219u8,240u8,240u8,174u8,128u8,236u8,176u8, - 81u8,191u8,120u8,223u8,27u8,207u8,15u8,55u8,97u8,107u8,55u8,123u8,207u8,102u8,100u8,59u8,169u8,197u8,228u8,93u8, - 155u8,105u8,197u8,129u8,197u8,103u8,77u8,131u8,11u8,65u8,14u8,192u8,40u8,168u8,2u8,172u8,131u8,51u8,89u8,45u8, - 166u8,236u8,198u8,170u8,39u8,202u8,48u8,226u8,213u8,8u8,5u8,21u8,39u8,139u8,149u8,135u8,203u8,139u8,225u8,189u8, - 75u8,144u8,219u8,99u8,22u8,97u8,93u8,51u8,51u8,178u8,21u8,247u8,250u8,9u8,70u8,183u8,76u8,188u8,1u8,19u8, - 95u8,159u8,112u8,241u8,75u8,149u8,1u8,38u8,62u8,99u8,252u8,152u8,169u8,206u8,86u8,98u8,63u8,205u8,1u8,124u8, - 110u8,174u8,252u8,47u8,90u8,242u8,181u8,162u8,129u8,13u8,245u8,100u8,32u8,13u8,215u8,230u8,41u8,47u8,125u8,46u8, - 206u8,86u8,10u8,5u8,6u8,56u8,91u8,99u8,50u8,202u8,249u8,197u8,223u8,242u8,210u8,206u8,87u8,231u8,105u8,192u8, - 24u8,142u8,206u8,11u8,178u8,114u8,224u8,105u8,89u8,99u8,1u8,103u8,132u8,191u8,204u8,236u8,225u8,22u8,252u8,117u8, - 203u8,75u8,55u8,202u8,75u8,130u8,19u8,100u8,65u8,191u8,204u8,242u8,110u8,195u8,5u8,17u8,210u8,55u8,160u8,15u8, - 36u8,142u8,247u8,252u8,204u8,203u8,24u8,133u8,147u8,17u8,26u8,163u8,224u8,17u8,11u8,144u8,112u8,22u8,214u8,18u8, - 163u8,96u8,181u8,89u8,40u8,67u8,162u8,175u8,99u8,117u8,47u8,158u8,188u8,24u8,130u8,19u8,231u8,29u8,61u8,175u8, - 47u8,151u8,5u8,170u8,23u8,53u8,229u8,2u8,142u8,208u8,50u8,14u8,136u8,255u8,190u8,247u8,200u8,152u8,165u8,81u8, - 207u8,168u8,111u8,90u8,235u8,154u8,184u8,103u8,213u8,229u8,149u8,94u8,139u8,116u8,230u8,53u8,126u8,207u8,232u8,132u8, - 202u8,247u8,25u8,127u8,99u8,87u8,254u8,207u8,109u8,9u8,128u8,47u8,59u8,141u8,254u8,49u8,229u8,30u8,4u8,63u8, - 86u8,74u8,197u8,121u8,157u8,86u8,229u8,128u8,221u8,79u8,71u8,78u8,24u8,213u8,4u8,86u8,168u8,60u8,36u8,253u8, - 122u8,30u8,36u8,61u8,83u8,147u8,121u8,133u8,9u8,198u8,52u8,129u8,242u8,13u8,53u8,157u8,237u8,93u8,144u8,115u8, - 121u8,65u8,27u8,107u8,222u8,38u8,243u8,52u8,80u8,96u8,17u8,125u8,63u8,90u8,86u8,18u8,60u8,52u8,74u8,51u8, - 35u8,76u8,236u8,1u8,9u8,27u8,124u8,241u8,48u8,110u8,243u8,199u8,6u8,142u8,114u8,88u8,204u8,219u8,187u8,134u8, - 231u8,55u8,246u8,4u8,183u8,144u8,34u8,13u8,69u8,16u8,170u8,188u8,27u8,215u8,65u8,63u8,166u8,231u8,77u8,37u8, - 17u8,146u8,93u8,223u8,119u8,77u8,223u8,114u8,43u8,163u8,42u8,196u8,97u8,155u8,34u8,163u8,31u8,91u8,73u8,200u8, - 48u8,243u8,241u8,32u8,165u8,120u8,156u8,141u8,162u8,52u8,212u8,32u8,222u8,65u8,231u8,197u8,205u8,56u8,158u8,21u8, - 31u8,200u8,126u8,10u8,182u8,61u8,80u8,221u8,83u8,84u8,195u8,164u8,139u8,12u8,220u8,240u8,50u8,135u8,89u8,166u8, - 168u8,22u8,161u8,254u8,27u8,106u8,202u8,140u8,7u8,151u8,24u8,166u8,44u8,76u8,156u8,41u8,60u8,163u8,240u8,140u8, - 77u8,10u8,238u8,113u8,187u8,36u8,127u8,24u8,208u8,250u8,42u8,195u8,30u8,13u8,248u8,15u8,57u8,188u8,217u8,183u8, - 197u8,203u8,148u8,121u8,129u8,245u8,154u8,126u8,213u8,172u8,29u8,97u8,141u8,218u8,155u8,46u8,147u8,119u8,36u8,69u8, - 106u8,85u8,219u8,34u8,127u8,162u8,217u8,14u8,21u8,67u8,210u8,126u8,135u8,149u8,144u8,120u8,35u8,160u8,133u8,236u8, - 111u8,143u8,228u8,66u8,255u8,243u8,159u8,196u8,125u8,162u8,137u8,72u8,168u8,166u8,213u8,34u8,91u8,1u8,94u8,23u8, - 117u8,62u8,194u8,51u8,75u8,39u8,82u8,59u8,220u8,221u8,212u8,248u8,189u8,182u8,209u8,123u8,131u8,74u8,12u8,3u8, - 238u8,231u8,69u8,89u8,223u8,27u8,229u8,229u8,104u8,149u8,83u8,92u8,233u8,223u8,172u8,44u8,18u8,151u8,162u8,164u8, - 160u8,108u8,43u8,120u8,193u8,192u8,9u8,167u8,190u8,196u8,98u8,94u8,63u8,20u8,24u8,103u8,177u8,92u8,213u8,178u8, - 13u8,148u8,8u8,114u8,229u8,11u8,51u8,85u8,178u8,223u8,66u8,17u8,95u8,75u8,1u8,127u8,70u8,26u8,88u8,200u8, - 187u8,48u8,71u8,7u8,22u8,211u8,144u8,210u8,224u8,21u8,6u8,114u8,136u8,28u8,15u8,116u8,139u8,186u8,77u8,250u8, - 108u8,131u8,226u8,205u8,134u8,50u8,86u8,111u8,168u8,198u8,90u8,86u8,145u8,146u8,103u8,85u8,200u8,11u8,45u8,240u8, - 175u8,149u8,8u8,252u8,155u8,167u8,28u8,224u8,14u8,202u8,240u8,14u8,232u8,3u8,59u8,110u8,71u8,249u8,184u8,41u8, - 167u8,209u8,84u8,244u8,14u8,10u8,20u8,112u8,245u8,252u8,154u8,45u8,55u8,124u8,119u8,197u8,188u8,204u8,195u8,118u8, - 46u8,81u8,31u8,206u8,228u8,158u8,15u8,147u8,205u8,157u8,158u8,144u8,192u8,128u8,17u8,251u8,154u8,154u8,128u8,152u8, - 85u8,255u8,5u8,28u8,5u8,47u8,109u8,137u8,225u8,141u8,220u8,32u8,28u8,146u8,189u8,152u8,120u8,64u8,23u8,117u8, - 102u8,139u8,7u8,169u8,117u8,237u8,3u8,103u8,136u8,232u8,58u8,77u8,98u8,64u8,63u8,243u8,32u8,237u8,197u8,200u8, - 215u8,241u8,159u8,11u8,122u8,51u8,218u8,72u8,57u8,68u8,212u8,72u8,60u8,50u8,155u8,245u8,162u8,224u8,109u8,70u8, - 140u8,70u8,76u8,170u8,189u8,26u8,182u8,126u8,156u8,69u8,109u8,5u8,246u8,110u8,153u8,83u8,163u8,43u8,199u8,19u8, - 33u8,74u8,182u8,149u8,3u8,34u8,176u8,192u8,150u8,62u8,137u8,237u8,123u8,198u8,91u8,72u8,47u8,252u8,79u8,164u8, - 216u8,178u8,193u8,65u8,222u8,138u8,227u8,28u8,223u8,94u8,188u8,156u8,178u8,85u8,122u8,26u8,249u8,248u8,56u8,9u8, - 232u8,20u8,102u8,74u8,124u8,70u8,119u8,30u8,22u8,139u8,204u8,102u8,194u8,214u8,203u8,75u8,31u8,113u8,118u8,82u8, - 115u8,54u8,195u8,20u8,163u8,50u8,159u8,78u8,177u8,241u8,36u8,176u8,19u8,247u8,192u8,155u8,60u8,102u8,21u8,162u8, - 80u8,139u8,33u8,221u8,29u8,148u8,76u8,131u8,124u8,132u8,221u8,47u8,34u8,190u8,19u8,163u8,142u8,127u8,139u8,17u8, - 182u8,144u8,144u8,16u8,184u8,134u8,69u8,237u8,217u8,50u8,193u8,18u8,4u8,156u8,245u8,183u8,231u8,154u8,248u8,204u8, - 131u8,117u8,187u8,48u8,148u8,182u8,226u8,177u8,112u8,128u8,52u8,40u8,177u8,135u8,57u8,11u8,146u8,67u8,108u8,82u8, - 54u8,27u8,206u8,170u8,102u8,131u8,200u8,5u8,111u8,139u8,124u8,76u8,13u8,13u8,41u8,145u8,17u8,123u8,231u8,82u8, - 242u8,3u8,119u8,146u8,35u8,11u8,138u8,180u8,125u8,115u8,86u8,201u8,124u8,170u8,173u8,97u8,64u8,145u8,219u8,74u8, - 219u8,41u8,77u8,134u8,189u8,250u8,180u8,173u8,61u8,43u8,191u8,73u8,107u8,233u8,25u8,79u8,218u8,52u8,10u8,141u8, - 74u8,134u8,109u8,133u8,121u8,146u8,104u8,182u8,48u8,79u8,175u8,98u8,34u8,80u8,58u8,16u8,241u8,169u8,241u8,120u8, - 68u8,231u8,205u8,49u8,239u8,248u8,103u8,52u8,224u8,180u8,114u8,81u8,169u8,43u8,144u8,194u8,63u8,232u8,36u8,177u8, - 1u8,98u8,82u8,62u8,182u8,99u8,48u8,241u8,103u8,189u8,36u8,229u8,127u8,186u8,118u8,248u8,73u8,11u8,168u8,168u8, - 150u8,113u8,82u8,246u8,196u8,92u8,15u8,101u8,196u8,217u8,49u8,49u8,164u8,208u8,17u8,48u8,29u8,134u8,60u8,36u8, - 87u8,141u8,37u8,223u8,75u8,66u8,86u8,19u8,111u8,188u8,20u8,179u8,91u8,56u8,29u8,55u8,88u8,45u8,97u8,114u8, - 11u8,48u8,166u8,203u8,196u8,74u8,185u8,175u8,37u8,183u8,86u8,134u8,140u8,5u8,125u8,131u8,123u8,228u8,3u8,88u8, - 52u8,31u8,220u8,140u8,49u8,227u8,145u8,126u8,131u8,28u8,199u8,237u8,229u8,118u8,96u8,248u8,205u8,141u8,66u8,170u8, - 61u8,150u8,235u8,114u8,229u8,83u8,99u8,72u8,82u8,156u8,98u8,208u8,216u8,8u8,85u8,5u8,86u8,170u8,111u8,108u8, - 164u8,206u8,186u8,106u8,182u8,24u8,196u8,56u8,159u8,110u8,220u8,145u8,247u8,149u8,19u8,253u8,202u8,84u8,27u8,116u8, - 137u8,158u8,112u8,57u8,57u8,111u8,148u8,102u8,168u8,17u8,204u8,134u8,131u8,157u8,112u8,68u8,89u8,26u8,49u8,60u8, - 87u8,11u8,124u8,217u8,16u8,218u8,221u8,0u8,201u8,57u8,149u8,139u8,193u8,69u8,4u8,240u8,39u8,1u8,63u8,10u8, - 77u8,13u8,18u8,201u8,155u8,251u8,48u8,216u8,203u8,41u8,226u8,43u8,209u8,19u8,248u8,132u8,118u8,204u8,243u8,95u8, - 117u8,128u8,144u8,39u8,255u8,153u8,29u8,189u8,235u8,115u8,212u8,232u8,211u8,130u8,107u8,152u8,146u8,218u8,14u8,18u8, - 233u8,177u8,197u8,244u8,188u8,62u8,191u8,196u8,10u8,174u8,17u8,238u8,62u8,137u8,26u8,217u8,105u8,87u8,124u8,70u8, - 177u8,206u8,218u8,182u8,123u8,35u8,180u8,233u8,118u8,150u8,34u8,201u8,128u8,73u8,185u8,166u8,207u8,178u8,69u8,17u8, - 199u8,149u8,155u8,137u8,124u8,12u8,180u8,110u8,0u8,239u8,74u8,187u8,88u8,92u8,164u8,169u8,224u8,59u8,58u8,178u8, - 66u8,163u8,141u8,116u8,77u8,65u8,198u8,235u8,61u8,11u8,105u8,145u8,20u8,195u8,155u8,34u8,173u8,118u8,196u8,179u8, - 83u8,58u8,145u8,143u8,215u8,213u8,246u8,124u8,196u8,244u8,132u8,89u8,200u8,172u8,156u8,18u8,222u8,47u8,138u8,120u8, - 94u8,39u8,175u8,219u8,40u8,200u8,88u8,132u8,115u8,207u8,59u8,236u8,149u8,112u8,193u8,190u8,30u8,139u8,54u8,131u8, - 5u8,239u8,163u8,74u8,99u8,168u8,198u8,252u8,218u8,8u8,248u8,124u8,202u8,24u8,76u8,106u8,232u8,54u8,134u8,242u8, - 225u8,235u8,154u8,117u8,26u8,155u8,201u8,54u8,132u8,241u8,81u8,251u8,120u8,206u8,64u8,231u8,216u8,9u8,246u8,62u8, - 4u8,38u8,195u8,213u8,22u8,97u8,38u8,223u8,253u8,81u8,211u8,146u8,22u8,98u8,87u8,62u8,243u8,51u8,103u8,251u8, - 211u8,165u8,137u8,156u8,144u8,197u8,249u8,209u8,28u8,51u8,95u8,235u8,228u8,32u8,87u8,16u8,124u8,109u8,245u8,72u8, - 225u8,23u8,112u8,200u8,62u8,166u8,170u8,103u8,171u8,231u8,189u8,53u8,18u8,10u8,109u8,181u8,222u8,42u8,147u8,48u8, - 164u8,174u8,182u8,144u8,44u8,176u8,141u8,244u8,176u8,245u8,165u8,39u8,182u8,202u8,108u8,112u8,240u8,183u8,56u8,142u8, - 132u8,123u8,110u8,218u8,27u8,31u8,234u8,184u8,246u8,91u8,32u8,53u8,50u8,172u8,212u8,27u8,3u8,235u8,252u8,42u8, - 36u8,94u8,146u8,135u8,28u8,164u8,185u8,87u8,9u8,15u8,125u8,121u8,8u8,118u8,226u8,21u8,76u8,136u8,44u8,150u8, - 96u8,245u8,91u8,54u8,153u8,48u8,170u8,1u8,162u8,91u8,145u8,80u8,149u8,96u8,26u8,145u8,236u8,66u8,77u8,158u8, - 13u8,10u8,141u8,227u8,7u8,106u8,86u8,252u8,80u8,106u8,25u8,106u8,55u8,140u8,147u8,142u8,68u8,125u8,160u8,10u8, - 239u8,196u8,122u8,72u8,123u8,6u8,130u8,81u8,89u8,114u8,173u8,40u8,247u8,117u8,219u8,60u8,124u8,108u8,97u8,227u8, - 29u8,133u8,92u8,29u8,18u8,122u8,98u8,146u8,204u8,76u8,147u8,75u8,71u8,106u8,157u8,174u8,232u8,225u8,140u8,238u8, - 239u8,114u8,44u8,132u8,182u8,165u8,174u8,228u8,13u8,32u8,137u8,8u8,127u8,134u8,0u8,191u8,86u8,179u8,214u8,77u8, - 227u8,161u8,129u8,30u8,180u8,94u8,248u8,50u8,220u8,136u8,182u8,235u8,88u8,130u8,87u8,207u8,247u8,70u8,123u8,65u8, - 162u8,144u8,92u8,111u8,178u8,199u8,137u8,198u8,107u8,192u8,19u8,23u8,113u8,188u8,225u8,128u8,60u8,162u8,168u8,188u8, - 133u8,169u8,117u8,127u8,200u8,145u8,54u8,18u8,238u8,122u8,51u8,29u8,58u8,135u8,21u8,94u8,83u8,34u8,230u8,86u8, - 233u8,45u8,237u8,164u8,127u8,19u8,20u8,14u8,192u8,150u8,70u8,84u8,126u8,193u8,198u8,186u8,104u8,116u8,48u8,6u8, - 63u8,11u8,4u8,51u8,249u8,64u8,99u8,20u8,62u8,197u8,101u8,138u8,249u8,20u8,29u8,119u8,56u8,110u8,12u8,68u8, - 19u8,34u8,60u8,32u8,169u8,46u8,245u8,40u8,172u8,252u8,253u8,94u8,43u8,135u8,110u8,127u8,93u8,239u8,141u8,78u8, - 115u8,117u8,92u8,47u8,132u8,162u8,38u8,222u8,238u8,199u8,206u8,119u8,166u8,169u8,240u8,253u8,234u8,50u8,225u8,215u8, - 77u8,169u8,18u8,69u8,180u8,193u8,68u8,22u8,84u8,67u8,153u8,226u8,197u8,121u8,14u8,148u8,212u8,105u8,108u8,81u8, - 227u8,55u8,156u8,73u8,30u8,249u8,91u8,131u8,136u8,53u8,166u8,63u8,138u8,180u8,189u8,9u8,140u8,118u8,24u8,31u8, - 7u8,134u8,249u8,35u8,239u8,150u8,160u8,154u8,227u8,208u8,70u8,233u8,137u8,122u8,0u8,137u8,63u8,192u8,136u8,119u8, - 133u8,79u8,197u8,213u8,62u8,30u8,48u8,1u8,48u8,122u8,122u8,208u8,192u8,128u8,124u8,159u8,210u8,186u8,72u8,13u8, - 117u8,107u8,14u8,91u8,178u8,49u8,99u8,243u8,180u8,197u8,232u8,61u8,7u8,58u8,103u8,54u8,73u8,226u8,116u8,145u8, - 84u8,199u8,252u8,138u8,30u8,176u8,119u8,188u8,137u8,11u8,161u8,65u8,52u8,6u8,9u8,1u8,103u8,229u8,251u8,54u8, - 25u8,54u8,65u8,217u8,217u8,243u8,94u8,113u8,187u8,164u8,198u8,227u8,196u8,6u8,200u8,50u8,192u8,25u8,196u8,222u8, - 123u8,107u8,142u8,120u8,205u8,254u8,137u8,232u8,166u8,47u8,67u8,46u8,120u8,74u8,26u8,163u8,193u8,49u8,99u8,19u8, - 116u8,209u8,51u8,110u8,180u8,204u8,137u8,1u8,44u8,31u8,163u8,155u8,46u8,225u8,9u8,161u8,16u8,29u8,219u8,248u8, - 151u8,186u8,55u8,36u8,101u8,244u8,230u8,11u8,89u8,192u8,229u8,36u8,111u8,111u8,18u8,160u8,0u8,147u8,80u8,223u8, - 39u8,108u8,86u8,177u8,205u8,165u8,219u8,123u8,91u8,149u8,39u8,199u8,100u8,255u8,240u8,190u8,116u8,153u8,111u8,153u8, - 136u8,139u8,79u8,236u8,192u8,200u8,125u8,101u8,102u8,194u8,97u8,173u8,212u8,42u8,188u8,149u8,45u8,117u8,45u8,59u8, - 45u8,156u8,219u8,126u8,173u8,156u8,246u8,86u8,62u8,137u8,117u8,246u8,221u8,198u8,238u8,138u8,45u8,54u8,93u8,104u8, - 189u8,102u8,149u8,54u8,25u8,143u8,84u8,136u8,100u8,30u8,108u8,176u8,224u8,5u8,198u8,126u8,85u8,124u8,249u8,43u8, - 217u8,249u8,191u8,114u8,166u8,252u8,213u8,100u8,25u8,231u8,136u8,105u8,119u8,149u8,155u8,169u8,38u8,110u8,178u8,150u8, - 56u8,28u8,83u8,219u8,162u8,77u8,215u8,212u8,54u8,153u8,56u8,35u8,13u8,182u8,157u8,218u8,74u8,106u8,156u8,107u8, - 21u8,110u8,197u8,104u8,144u8,70u8,34u8,255u8,195u8,229u8,69u8,192u8,30u8,184u8,86u8,192u8,246u8,202u8,134u8,73u8, - 20u8,156u8,235u8,153u8,39u8,90u8,76u8,158u8,173u8,46u8,193u8,62u8,93u8,167u8,28u8,141u8,125u8,237u8,238u8,178u8, - 130u8,184u8,149u8,3u8,39u8,216u8,164u8,124u8,93u8,17u8,241u8,208u8,90u8,68u8,195u8,97u8,222u8,136u8,154u8,133u8, - 226u8,138u8,246u8,101u8,175u8,50u8,206u8,72u8,49u8,70u8,190u8,201u8,154u8,29u8,175u8,150u8,216u8,8u8,28u8,225u8, - 240u8,94u8,160u8,129u8,167u8,149u8,137u8,244u8,179u8,115u8,204u8,224u8,215u8,141u8,214u8,226u8,190u8,29u8,126u8,43u8, - 44u8,185u8,234u8,196u8,194u8,80u8,250u8,64u8,152u8,58u8,195u8,91u8,229u8,220u8,35u8,10u8,229u8,122u8,81u8,122u8, - 3u8,15u8,99u8,67u8,224u8,123u8,104u8,206u8,181u8,223u8,79u8,94u8,172u8,106u8,30u8,142u8,48u8,208u8,96u8,93u8, - 119u8,107u8,120u8,193u8,176u8,107u8,145u8,116u8,128u8,156u8,97u8,59u8,67u8,97u8,42u8,153u8,13u8,43u8,120u8,143u8, - 215u8,87u8,238u8,149u8,195u8,86u8,79u8,204u8,190u8,145u8,45u8,182u8,193u8,36u8,160u8,40u8,204u8,105u8,26u8,252u8, - 145u8,186u8,147u8,6u8,98u8,203u8,140u8,24u8,70u8,179u8,56u8,36u8,234u8,212u8,79u8,215u8,146u8,16u8,250u8,100u8, - 105u8,229u8,170u8,202u8,252u8,13u8,227u8,52u8,25u8,58u8,75u8,134u8,98u8,162u8,145u8,220u8,215u8,120u8,94u8,199u8, - 97u8,44u8,150u8,102u8,228u8,146u8,248u8,211u8,252u8,177u8,173u8,199u8,241u8,62u8,214u8,248u8,25u8,50u8,232u8,58u8, - 41u8,196u8,110u8,162u8,183u8,223u8,2u8,104u8,71u8,173u8,236u8,90u8,55u8,143u8,235u8,200u8,124u8,1u8,239u8,130u8, - 40u8,247u8,122u8,45u8,135u8,155u8,127u8,100u8,11u8,86u8,138u8,158u8,127u8,252u8,162u8,42u8,163u8,57u8,183u8,186u8, - 213u8,75u8,247u8,15u8,114u8,187u8,54u8,137u8,126u8,200u8,5u8,218u8,152u8,225u8,59u8,158u8,45u8,243u8,12u8,167u8, - 0u8,103u8,219u8,168u8,26u8,14u8,225u8,40u8,112u8,118u8,89u8,195u8,73u8,233u8,78u8,60u8,204u8,102u8,80u8,172u8, - 60u8,3u8,102u8,75u8,164u8,76u8,225u8,87u8,99u8,216u8,44u8,205u8,25u8,77u8,217u8,90u8,118u8,188u8,229u8,132u8, - 139u8,39u8,92u8,103u8,54u8,163u8,44u8,28u8,10u8,73u8,32u8,160u8,19u8,112u8,76u8,214u8,149u8,188u8,178u8,251u8, - 50u8,41u8,232u8,66u8,60u8,126u8,117u8,156u8,234u8,128u8,75u8,133u8,66u8,176u8,84u8,126u8,117u8,31u8,137u8,134u8, - 169u8,192u8,157u8,213u8,130u8,138u8,193u8,94u8,206u8,243u8,5u8,134u8,239u8,70u8,30u8,182u8,12u8,59u8,220u8,232u8, - 109u8,117u8,215u8,232u8,124u8,206u8,81u8,96u8,102u8,231u8,196u8,23u8,142u8,247u8,110u8,218u8,139u8,36u8,227u8,79u8, - 72u8,36u8,190u8,206u8,41u8,80u8,251u8,194u8,188u8,128u8,27u8,230u8,147u8,151u8,147u8,101u8,234u8,234u8,123u8,221u8, - 205u8,151u8,224u8,146u8,10u8,214u8,19u8,117u8,214u8,109u8,137u8,205u8,192u8,173u8,239u8,125u8,181u8,81u8,67u8,174u8, - 46u8,151u8,7u8,188u8,96u8,79u8,240u8,159u8,186u8,119u8,73u8,60u8,80u8,71u8,53u8,163u8,251u8,165u8,17u8,244u8, - 200u8,85u8,79u8,50u8,193u8,185u8,228u8,223u8,10u8,180u8,180u8,130u8,115u8,154u8,215u8,204u8,203u8,241u8,182u8,60u8, - 229u8,50u8,95u8,183u8,157u8,210u8,73u8,91u8,164u8,90u8,232u8,86u8,56u8,236u8,103u8,120u8,193u8,120u8,43u8,41u8, - 121u8,49u8,94u8,129u8,77u8,149u8,242u8,218u8,188u8,26u8,207u8,62u8,175u8,224u8,77u8,243u8,193u8,187u8,1u8,201u8, - 181u8,139u8,58u8,70u8,100u8,62u8,79u8,130u8,3u8,160u8,234u8,90u8,85u8,61u8,235u8,214u8,6u8,234u8,191u8,136u8, - 42u8,154u8,32u8,25u8,195u8,233u8,108u8,84u8,123u8,0u8,137u8,216u8,191u8,57u8,179u8,157u8,6u8,112u8,193u8,102u8, - 179u8,123u8,99u8,236u8,94u8,197u8,163u8,10u8,35u8,126u8,38u8,160u8,133u8,160u8,50u8,38u8,118u8,177u8,217u8,93u8, - 99u8,71u8,169u8,42u8,161u8,44u8,83u8,245u8,40u8,220u8,82u8,76u8,139u8,237u8,171u8,116u8,108u8,115u8,229u8,245u8, - 122u8,123u8,56u8,208u8,194u8,138u8,104u8,217u8,190u8,221u8,79u8,92u8,167u8,109u8,158u8,242u8,157u8,115u8,71u8,131u8, - 148u8,95u8,159u8,156u8,208u8,144u8,240u8,141u8,118u8,224u8,184u8,192u8,77u8,164u8,54u8,96u8,162u8,141u8,179u8,8u8, - 243u8,241u8,102u8,245u8,228u8,214u8,16u8,151u8,199u8,194u8,246u8,158u8,103u8,139u8,169u8,201u8,169u8,232u8,255u8,208u8, - 118u8,120u8,163u8,159u8,162u8,139u8,142u8,215u8,192u8,90u8,55u8,43u8,58u8,123u8,12u8,174u8,173u8,41u8,143u8,46u8, - 208u8,134u8,9u8,239u8,95u8,2u8,14u8,128u8,17u8,91u8,214u8,94u8,137u8,9u8,121u8,112u8,42u8,54u8,123u8,11u8, - 254u8,48u8,211u8,222u8,194u8,188u8,72u8,16u8,202u8,210u8,0u8,96u8,89u8,73u8,137u8,180u8,104u8,89u8,190u8,101u8, - 96u8,57u8,138u8,75u8,214u8,141u8,153u8,213u8,213u8,237u8,28u8,37u8,235u8,93u8,160u8,164u8,154u8,12u8,15u8,37u8, - 158u8,235u8,174u8,224u8,187u8,196u8,97u8,102u8,32u8,37u8,143u8,148u8,88u8,196u8,180u8,153u8,250u8,188u8,99u8,142u8, - 224u8,188u8,159u8,195u8,219u8,123u8,135u8,174u8,51u8,54u8,79u8,30u8,226u8,72u8,33u8,7u8,171u8,49u8,86u8,192u8, - 53u8,106u8,206u8,4u8,129u8,8u8,199u8,93u8,69u8,69u8,134u8,198u8,247u8,95u8,234u8,202u8,77u8,119u8,42u8,107u8, - 186u8,182u8,238u8,83u8,99u8,108u8,103u8,102u8,117u8,126u8,19u8,91u8,147u8,94u8,0u8,117u8,159u8,91u8,195u8,130u8, - 95u8,38u8,131u8,195u8,205u8,213u8,198u8,149u8,254u8,84u8,127u8,88u8,14u8,45u8,58u8,30u8,133u8,11u8,146u8,2u8, - 30u8,38u8,253u8,123u8,3u8,70u8,14u8,199u8,124u8,164u8,94u8,144u8,51u8,194u8,80u8,121u8,89u8,130u8,0u8,89u8, - 199u8,6u8,245u8,158u8,131u8,146u8,46u8,88u8,184u8,141u8,6u8,173u8,59u8,15u8,106u8,136u8,18u8,142u8,40u8,19u8, - 86u8,202u8,205u8,34u8,85u8,36u8,166u8,177u8,232u8,37u8,112u8,207u8,202u8,50u8,187u8,196u8,62u8,23u8,253u8,224u8, - 184u8,252u8,30u8,109u8,205u8,66u8,98u8,124u8,209u8,209u8,119u8,197u8,61u8,178u8,18u8,31u8,1u8,105u8,212u8,111u8, - 32u8,41u8,220u8,145u8,90u8,2u8,110u8,110u8,152u8,246u8,20u8,139u8,23u8,175u8,188u8,97u8,30u8,250u8,219u8,238u8, - 157u8,33u8,39u8,5u8,132u8,105u8,43u8,162u8,183u8,188u8,36u8,222u8,42u8,157u8,137u8,222u8,59u8,222u8,121u8,148u8, - 4u8,57u8,216u8,130u8,251u8,97u8,55u8,248u8,166u8,174u8,230u8,248u8,74u8,13u8,223u8,131u8,108u8,247u8,141u8,238u8, - 66u8,223u8,20u8,145u8,246u8,149u8,246u8,49u8,180u8,104u8,189u8,171u8,142u8,62u8,112u8,42u8,113u8,93u8,192u8,238u8, - 191u8,67u8,14u8,65u8,208u8,168u8,150u8,107u8,149u8,64u8,140u8,95u8,79u8,219u8,233u8,122u8,221u8,60u8,221u8,59u8, - 106u8,73u8,249u8,174u8,185u8,167u8,54u8,222u8,11u8,162u8,225u8,214u8,90u8,229u8,188u8,182u8,166u8,10u8,222u8,96u8, - 187u8,174u8,215u8,132u8,113u8,159u8,109u8,120u8,212u8,224u8,221u8,182u8,241u8,81u8,189u8,155u8,110u8,195u8,163u8,250u8, - 183u8,222u8,198u8,135u8,180u8,239u8,192u8,141u8,65u8,25u8,45u8,36u8,110u8,130u8,53u8,88u8,81u8,28u8,193u8,110u8, - 232u8,166u8,220u8,6u8,236u8,186u8,247u8,230u8,134u8,71u8,141u8,221u8,161u8,27u8,31u8,56u8,116u8,163u8,110u8,28u8, - 199u8,65u8,87u8,116u8,35u8,170u8,125u8,159u8,116u8,120u8,244u8,64u8,206u8,67u8,124u8,92u8,55u8,249u8,193u8,31u8, - 242u8,189u8,125u8,145u8,72u8,141u8,155u8,137u8,254u8,185u8,95u8,140u8,139u8,217u8,169u8,31u8,18u8,134u8,33u8,48u8, - 84u8,113u8,250u8,250u8,149u8,184u8,123u8,94u8,93u8,201u8,126u8,184u8,209u8,199u8,39u8,207u8,237u8,207u8,31u8,28u8, - 70u8,38u8,71u8,117u8,19u8,188u8,75u8,87u8,100u8,51u8,72u8,7u8,206u8,97u8,35u8,240u8,39u8,207u8,79u8,78u8, - 79u8,142u8,127u8,74u8,191u8,63u8,254u8,233u8,248u8,249u8,99u8,53u8,43u8,168u8,86u8,243u8,63u8,135u8,228u8,124u8, - 124u8,150u8,28u8,255u8,124u8,42u8,18u8,39u8,232u8,128u8,252u8,29u8,28u8,239u8,71u8,249u8,156u8,170u8,57u8,154u8, - 38u8,145u8,205u8,110u8,41u8,70u8,21u8,153u8,66u8,205u8,242u8,253u8,230u8,179u8,152u8,23u8,175u8,128u8,13u8,190u8, - 236u8,184u8,120u8,241u8,27u8,136u8,4u8,10u8,74u8,212u8,79u8,226u8,96u8,156u8,10u8,11u8,108u8,24u8,112u8,250u8, - 190u8,228u8,153u8,98u8,228u8,41u8,2u8,99u8,96u8,175u8,191u8,255u8,21u8,118u8,81u8,73u8,32u8,70u8,58u8,58u8, - 239u8,55u8,156u8,180u8,193u8,202u8,79u8,9u8,250u8,17u8,196u8,107u8,139u8,185u8,11u8,105u8,207u8,221u8,144u8,158u8, - 141u8,188u8,94u8,242u8,205u8,183u8,123u8,112u8,26u8,199u8,203u8,107u8,122u8,128u8,194u8,30u8,71u8,99u8,79u8,98u8, - 115u8,43u8,93u8,183u8,168u8,73u8,145u8,226u8,26u8,30u8,159u8,76u8,179u8,122u8,93u8,55u8,34u8,215u8,29u8,33u8, - 155u8,162u8,200u8,37u8,199u8,179u8,167u8,223u8,59u8,205u8,155u8,164u8,216u8,105u8,4u8,59u8,144u8,42u8,22u8,3u8, - 220u8,29u8,112u8,115u8,208u8,189u8,17u8,162u8,21u8,125u8,176u8,221u8,224u8,238u8,210u8,190u8,23u8,135u8,152u8,186u8, - 225u8,183u8,181u8,157u8,20u8,126u8,255u8,125u8,75u8,202u8,111u8,184u8,218u8,38u8,202u8,19u8,155u8,248u8,106u8,189u8, - 71u8,107u8,46u8,185u8,105u8,87u8,108u8,16u8,100u8,218u8,94u8,224u8,18u8,152u8,96u8,12u8,108u8,99u8,114u8,49u8, - 25u8,153u8,12u8,13u8,116u8,39u8,9u8,188u8,99u8,255u8,34u8,138u8,5u8,232u8,83u8,193u8,25u8,119u8,120u8,46u8, - 148u8,97u8,207u8,243u8,61u8,221u8,132u8,138u8,216u8,221u8,66u8,238u8,42u8,252u8,146u8,43u8,191u8,152u8,210u8,52u8, - 209u8,229u8,137u8,150u8,205u8,151u8,245u8,37u8,58u8,185u8,213u8,125u8,27u8,22u8,81u8,184u8,232u8,3u8,44u8,252u8, - 247u8,222u8,187u8,125u8,137u8,69u8,241u8,207u8,193u8,55u8,122u8,90u8,254u8,211u8,224u8,155u8,7u8,221u8,95u8,194u8, - 25u8,169u8,68u8,107u8,224u8,198u8,68u8,163u8,29u8,189u8,153u8,215u8,145u8,170u8,235u8,118u8,223u8,199u8,91u8,11u8, - 114u8,240u8,100u8,229u8,254u8,94u8,184u8,124u8,109u8,51u8,249u8,119u8,45u8,25u8,116u8,197u8,218u8,193u8,128u8,96u8, - 137u8,140u8,180u8,118u8,136u8,24u8,94u8,44u8,178u8,103u8,101u8,62u8,185u8,228u8,30u8,124u8,231u8,66u8,10u8,244u8, - 152u8,145u8,115u8,27u8,249u8,0u8,155u8,131u8,149u8,56u8,193u8,236u8,178u8,191u8,46u8,240u8,28u8,188u8,149u8,184u8, - 213u8,74u8,101u8,147u8,110u8,189u8,96u8,17u8,70u8,178u8,67u8,201u8,222u8,178u8,246u8,248u8,159u8,64u8,171u8,174u8, - 230u8,123u8,107u8,27u8,128u8,66u8,87u8,159u8,63u8,143u8,131u8,61u8,213u8,154u8,237u8,95u8,100u8,165u8,212u8,220u8, - 47u8,16u8,189u8,112u8,39u8,173u8,128u8,122u8,151u8,248u8,223u8,98u8,105u8,69u8,41u8,121u8,184u8,36u8,205u8,199u8, - 152u8,69u8,90u8,95u8,118u8,124u8,148u8,224u8,4u8,169u8,26u8,59u8,197u8,51u8,143u8,210u8,73u8,119u8,112u8,72u8, - 240u8,203u8,45u8,123u8,209u8,224u8,59u8,154u8,9u8,1u8,236u8,24u8,97u8,82u8,99u8,108u8,170u8,11u8,178u8,226u8, - 163u8,136u8,140u8,136u8,117u8,11u8,52u8,229u8,150u8,196u8,96u8,16u8,30u8,128u8,35u8,195u8,168u8,214u8,209u8,32u8, - 251u8,250u8,78u8,99u8,109u8,40u8,89u8,200u8,94u8,234u8,56u8,188u8,135u8,118u8,186u8,80u8,127u8,128u8,45u8,211u8, - 150u8,237u8,56u8,91u8,179u8,57u8,13u8,25u8,99u8,155u8,244u8,19u8,220u8,221u8,209u8,44u8,203u8,231u8,21u8,48u8, - 207u8,87u8,104u8,227u8,169u8,210u8,230u8,34u8,153u8,100u8,206u8,157u8,188u8,78u8,29u8,25u8,123u8,183u8,100u8,24u8, - 0u8,55u8,207u8,125u8,251u8,232u8,235u8,50u8,87u8,118u8,47u8,185u8,50u8,145u8,118u8,201u8,249u8,117u8,104u8,213u8, - 250u8,105u8,132u8,217u8,147u8,4u8,97u8,105u8,106u8,249u8,217u8,241u8,106u8,115u8,183u8,200u8,194u8,1u8,244u8,7u8, - 1u8,220u8,54u8,75u8,219u8,243u8,250u8,66u8,214u8,202u8,219u8,90u8,47u8,96u8,253u8,95u8,214u8,173u8,66u8,224u8, - 103u8,130u8,139u8,16u8,76u8,130u8,106u8,149u8,31u8,223u8,221u8,124u8,119u8,43u8,143u8,25u8,66u8,28u8,73u8,81u8, - 230u8,211u8,156u8,110u8,95u8,35u8,57u8,109u8,221u8,64u8,105u8,166u8,195u8,80u8,64u8,11u8,112u8,196u8,32u8,20u8, - 48u8,246u8,25u8,47u8,70u8,151u8,22u8,139u8,173u8,91u8,73u8,3u8,119u8,26u8,143u8,174u8,73u8,26u8,65u8,32u8, - 60u8,105u8,239u8,149u8,204u8,180u8,34u8,85u8,75u8,189u8,235u8,149u8,240u8,84u8,83u8,241u8,111u8,243u8,110u8,137u8, - 53u8,67u8,41u8,80u8,3u8,99u8,58u8,226u8,10u8,188u8,153u8,107u8,216u8,81u8,18u8,187u8,63u8,214u8,81u8,124u8, - 162u8,158u8,183u8,156u8,173u8,201u8,69u8,135u8,77u8,22u8,69u8,26u8,186u8,163u8,182u8,149u8,178u8,183u8,130u8,121u8, - 162u8,225u8,193u8,120u8,92u8,25u8,119u8,184u8,244u8,27u8,15u8,82u8,113u8,209u8,111u8,5u8,82u8,204u8,38u8,42u8, - 87u8,22u8,25u8,135u8,95u8,120u8,30u8,193u8,6u8,59u8,126u8,235u8,74u8,41u8,104u8,128u8,108u8,85u8,30u8,6u8, - 208u8,117u8,119u8,189u8,221u8,131u8,1u8,16u8,128u8,21u8,206u8,72u8,48u8,150u8,188u8,16u8,23u8,184u8,112u8,140u8, - 173u8,87u8,132u8,216u8,177u8,130u8,147u8,50u8,207u8,13u8,227u8,233u8,160u8,48u8,41u8,179u8,141u8,127u8,42u8,226u8, - 253u8,232u8,104u8,15u8,251u8,76u8,130u8,214u8,65u8,75u8,217u8,227u8,175u8,197u8,33u8,63u8,192u8,26u8,136u8,208u8, - 18u8,115u8,140u8,81u8,181u8,155u8,133u8,163u8,92u8,31u8,222u8,215u8,169u8,112u8,235u8,85u8,251u8,224u8,35u8,82u8, - 237u8,131u8,237u8,171u8,246u8,93u8,170u8,200u8,193u8,141u8,41u8,250u8,141u8,20u8,112u8,147u8,194u8,28u8,180u8,81u8, - 152u8,131u8,107u8,107u8,169u8,45u8,105u8,168u8,38u8,237u8,20u8,127u8,118u8,119u8,29u8,109u8,109u8,95u8,79u8,109u8, - 65u8,195u8,108u8,108u8,161u8,152u8,50u8,225u8,57u8,94u8,193u8,88u8,80u8,137u8,167u8,54u8,246u8,251u8,215u8,35u8, - 159u8,43u8,1u8,34u8,116u8,163u8,20u8,64u8,149u8,206u8,24u8,233u8,97u8,58u8,196u8,232u8,92u8,198u8,155u8,41u8, - 231u8,46u8,208u8,117u8,192u8,196u8,155u8,45u8,188u8,130u8,241u8,247u8,29u8,9u8,174u8,198u8,182u8,63u8,65u8,213u8, - 183u8,185u8,222u8,220u8,147u8,127u8,194u8,163u8,108u8,75u8,112u8,249u8,141u8,149u8,108u8,219u8,67u8,110u8,149u8,145u8, - 170u8,107u8,35u8,181u8,161u8,59u8,210u8,213u8,96u8,116u8,223u8,112u8,33u8,216u8,182u8,84u8,13u8,156u8,146u8,159u8, - 130u8,234u8,228u8,154u8,155u8,204u8,2u8,195u8,129u8,36u8,200u8,19u8,188u8,11u8,201u8,155u8,156u8,138u8,168u8,141u8, - 194u8,75u8,203u8,79u8,130u8,135u8,232u8,188u8,38u8,143u8,211u8,158u8,209u8,37u8,48u8,220u8,230u8,227u8,74u8,28u8, - 104u8,30u8,231u8,5u8,92u8,85u8,141u8,217u8,188u8,152u8,54u8,69u8,39u8,123u8,59u8,179u8,138u8,167u8,244u8,114u8, - 227u8,133u8,26u8,120u8,44u8,173u8,190u8,249u8,229u8,215u8,149u8,135u8,235u8,104u8,147u8,71u8,248u8,19u8,160u8,140u8, - 142u8,37u8,6u8,188u8,141u8,3u8,106u8,241u8,127u8,115u8,57u8,203u8,231u8,98u8,107u8,208u8,64u8,35u8,176u8,187u8, - 107u8,0u8,51u8,50u8,174u8,124u8,144u8,215u8,66u8,220u8,118u8,236u8,246u8,108u8,29u8,149u8,149u8,81u8,143u8,223u8, - 181u8,124u8,90u8,50u8,232u8,234u8,147u8,185u8,217u8,23u8,0u8,40u8,254u8,45u8,247u8,155u8,10u8,104u8,170u8,254u8, - 53u8,149u8,252u8,22u8,149u8,222u8,150u8,236u8,133u8,43u8,41u8,118u8,32u8,175u8,64u8,110u8,81u8,131u8,178u8,55u8, - 92u8,243u8,45u8,192u8,141u8,198u8,13u8,181u8,182u8,20u8,135u8,15u8,238u8,139u8,229u8,233u8,222u8,84u8,16u8,43u8, - 58u8,137u8,150u8,178u8,157u8,7u8,85u8,24u8,246u8,195u8,196u8,99u8,173u8,213u8,99u8,131u8,187u8,137u8,138u8,165u8, - 236u8,206u8,122u8,185u8,145u8,136u8,142u8,154u8,123u8,68u8,87u8,127u8,167u8,242u8,156u8,224u8,116u8,178u8,75u8,229u8, - 141u8,210u8,41u8,94u8,134u8,94u8,181u8,8u8,36u8,222u8,134u8,129u8,14u8,183u8,31u8,35u8,185u8,141u8,59u8,252u8, - 238u8,226u8,14u8,32u8,55u8,152u8,213u8,232u8,221u8,80u8,245u8,146u8,41u8,19u8,98u8,202u8,126u8,242u8,253u8,74u8, - 222u8,246u8,232u8,244u8,162u8,65u8,91u8,7u8,221u8,50u8,60u8,241u8,125u8,173u8,19u8,99u8,135u8,39u8,129u8,79u8, - 192u8,15u8,209u8,189u8,133u8,118u8,215u8,208u8,222u8,136u8,106u8,115u8,186u8,7u8,223u8,170u8,172u8,91u8,149u8,117u8, - 171u8,178u8,118u8,168u8,178u8,130u8,190u8,170u8,64u8,221u8,190u8,227u8,185u8,90u8,102u8,151u8,214u8,141u8,53u8,212u8, - 23u8,211u8,245u8,28u8,217u8,45u8,192u8,215u8,70u8,244u8,194u8,53u8,216u8,182u8,74u8,243u8,130u8,2u8,235u8,149u8, - 98u8,104u8,88u8,91u8,189u8,109u8,208u8,37u8,59u8,48u8,197u8,182u8,61u8,65u8,193u8,118u8,218u8,159u8,164u8,132u8, - 255u8,195u8,63u8,213u8,84u8,147u8,44u8,159u8,173u8,74u8,214u8,201u8,206u8,138u8,18u8,117u8,221u8,24u8,137u8,125u8, - 239u8,221u8,119u8,123u8,123u8,123u8,223u8,246u8,176u8,141u8,63u8,239u8,170u8,112u8,4u8,129u8,157u8,217u8,164u8,81u8, - 65u8,136u8,21u8,139u8,147u8,143u8,76u8,217u8,195u8,131u8,143u8,151u8,176u8,181u8,241u8,241u8,231u8,202u8,121u8,148u8, - 31u8,72u8,195u8,92u8,89u8,81u8,108u8,22u8,157u8,221u8,70u8,94u8,100u8,96u8,49u8,55u8,152u8,24u8,185u8,158u8, - 18u8,49u8,251u8,122u8,176u8,13u8,74u8,116u8,137u8,144u8,36u8,186u8,236u8,220u8,28u8,234u8,50u8,255u8,233u8,82u8, - 227u8,254u8,135u8,220u8,172u8,253u8,157u8,109u8,22u8,94u8,127u8,147u8,226u8,61u8,55u8,41u8,240u8,135u8,80u8,174u8, - 104u8,165u8,3u8,66u8,199u8,159u8,220u8,182u8,29u8,240u8,202u8,130u8,27u8,53u8,225u8,141u8,114u8,177u8,207u8,80u8, - 232u8,222u8,156u8,89u8,191u8,155u8,44u8,247u8,173u8,231u8,254u8,6u8,12u8,237u8,49u8,155u8,177u8,41u8,153u8,78u8, - 156u8,10u8,60u8,67u8,219u8,87u8,16u8,123u8,126u8,183u8,47u8,254u8,109u8,92u8,203u8,68u8,189u8,202u8,87u8,129u8, - 200u8,93u8,240u8,254u8,77u8,178u8,140u8,89u8,11u8,121u8,203u8,50u8,31u8,229u8,73u8,152u8,192u8,161u8,36u8,78u8, - 85u8,179u8,138u8,183u8,78u8,73u8,5u8,100u8,144u8,154u8,120u8,92u8,177u8,81u8,21u8,11u8,106u8,90u8,251u8,221u8, - 226u8,120u8,164u8,98u8,58u8,49u8,0u8,30u8,182u8,2u8,64u8,115u8,217u8,53u8,169u8,154u8,114u8,78u8,57u8,93u8, - 27u8,63u8,14u8,248u8,143u8,223u8,60u8,56u8,104u8,169u8,138u8,211u8,232u8,149u8,42u8,91u8,32u8,253u8,116u8,191u8, - 233u8,225u8,96u8,247u8,156u8,129u8,233u8,174u8,205u8,188u8,177u8,190u8,222u8,46u8,78u8,236u8,233u8,160u8,187u8,166u8, - 18u8,45u8,29u8,172u8,75u8,185u8,188u8,62u8,95u8,238u8,183u8,227u8,76u8,43u8,253u8,216u8,26u8,96u8,208u8,114u8, - 128u8,129u8,163u8,15u8,255u8,10u8,190u8,34u8,55u8,21u8,1u8,243u8,25u8,48u8,41u8,160u8,194u8,206u8,123u8,158u8, - 35u8,230u8,154u8,34u8,97u8,63u8,226u8,181u8,184u8,65u8,135u8,23u8,18u8,84u8,163u8,203u8,171u8,201u8,215u8,180u8, - 85u8,119u8,152u8,231u8,69u8,178u8,175u8,93u8,50u8,221u8,95u8,177u8,203u8,159u8,60u8,188u8,246u8,2u8,68u8,209u8, - 75u8,6u8,123u8,118u8,180u8,248u8,52u8,208u8,33u8,138u8,247u8,107u8,188u8,192u8,37u8,22u8,163u8,156u8,76u8,31u8, - 42u8,59u8,86u8,58u8,120u8,32u8,109u8,35u8,204u8,83u8,230u8,14u8,172u8,51u8,108u8,18u8,33u8,34u8,189u8,222u8, - 157u8,53u8,60u8,20u8,110u8,78u8,107u8,95u8,9u8,163u8,6u8,222u8,239u8,95u8,223u8,151u8,231u8,228u8,156u8,32u8, - 1u8,88u8,98u8,182u8,211u8,232u8,1u8,115u8,67u8,53u8,27u8,57u8,137u8,6u8,77u8,116u8,222u8,139u8,67u8,101u8, - 111u8,201u8,107u8,194u8,150u8,137u8,34u8,141u8,99u8,125u8,105u8,71u8,211u8,13u8,144u8,77u8,41u8,178u8,81u8,32u8, - 182u8,225u8,13u8,109u8,179u8,194u8,43u8,167u8,95u8,13u8,154u8,211u8,90u8,67u8,84u8,173u8,26u8,96u8,25u8,146u8, - 237u8,220u8,110u8,118u8,134u8,13u8,194u8,155u8,156u8,240u8,22u8,159u8,183u8,148u8,109u8,54u8,164u8,54u8,214u8,246u8, - 119u8,37u8,67u8,6u8,177u8,116u8,52u8,238u8,79u8,175u8,121u8,89u8,193u8,13u8,167u8,113u8,26u8,240u8,188u8,52u8, - 146u8,200u8,236u8,123u8,154u8,206u8,24u8,10u8,13u8,34u8,122u8,67u8,24u8,0u8,179u8,90u8,249u8,105u8,64u8,253u8, - 35u8,106u8,24u8,102u8,177u8,134u8,238u8,70u8,57u8,45u8,224u8,107u8,235u8,82u8,92u8,181u8,29u8,135u8,13u8,9u8, - 105u8,6u8,163u8,90u8,98u8,235u8,200u8,42u8,135u8,183u8,115u8,212u8,226u8,100u8,222u8,196u8,96u8,40u8,90u8,14u8, - 14u8,67u8,221u8,222u8,60u8,25u8,117u8,165u8,217u8,226u8,139u8,137u8,87u8,186u8,94u8,224u8,249u8,214u8,172u8,151u8, - 202u8,74u8,221u8,244u8,118u8,209u8,211u8,141u8,210u8,130u8,162u8,221u8,18u8,221u8,60u8,251u8,246u8,156u8,250u8,60u8, - 86u8,78u8,127u8,66u8,70u8,141u8,129u8,172u8,204u8,194u8,9u8,168u8,162u8,175u8,228u8,125u8,158u8,214u8,72u8,131u8, - 126u8,43u8,89u8,62u8,112u8,183u8,232u8,154u8,193u8,250u8,129u8,21u8,174u8,63u8,56u8,188u8,150u8,52u8,109u8,87u8, - 116u8,128u8,22u8,101u8,11u8,120u8,182u8,168u8,141u8,6u8,235u8,181u8,209u8,32u8,110u8,155u8,110u8,146u8,72u8,183u8, - 223u8,38u8,149u8,110u8,223u8,76u8,48u8,243u8,75u8,195u8,162u8,148u8,223u8,11u8,64u8,213u8,221u8,165u8,2u8,27u8, - 220u8,128u8,2u8,187u8,146u8,68u8,14u8,70u8,150u8,7u8,246u8,213u8,111u8,185u8,209u8,165u8,223u8,108u8,248u8,216u8, - 38u8,67u8,115u8,176u8,174u8,92u8,112u8,176u8,17u8,85u8,12u8,182u8,161u8,76u8,26u8,36u8,66u8,59u8,201u8,25u8, - 218u8,205u8,224u8,117u8,223u8,230u8,234u8,54u8,33u8,208u8,129u8,151u8,94u8,28u8,145u8,204u8,189u8,53u8,211u8,122u8, - 215u8,56u8,52u8,146u8,183u8,127u8,243u8,136u8,215u8,99u8,212u8,65u8,184u8,253u8,96u8,175u8,237u8,63u8,63u8,106u8, - 175u8,199u8,154u8,91u8,136u8,127u8,31u8,174u8,144u8,79u8,220u8,29u8,209u8,250u8,0u8,28u8,222u8,236u8,205u8,78u8, - 197u8,45u8,78u8,196u8,234u8,212u8,112u8,65u8,39u8,221u8,230u8,131u8,49u8,253u8,70u8,109u8,178u8,157u8,251u8,55u8, - 195u8,167u8,138u8,141u8,154u8,162u8,12u8,2u8,206u8,71u8,115u8,192u8,47u8,55u8,30u8,113u8,223u8,24u8,49u8,116u8, - 56u8,105u8,121u8,87u8,117u8,88u8,201u8,97u8,84u8,122u8,112u8,147u8,158u8,127u8,67u8,44u8,168u8,11u8,236u8,176u8, - 27u8,54u8,54u8,144u8,226u8,149u8,87u8,187u8,78u8,129u8,163u8,158u8,189u8,118u8,203u8,170u8,64u8,221u8,251u8,31u8, - 19u8,195u8,68u8,188u8,122u8,172u8,192u8,153u8,232u8,247u8,155u8,53u8,151u8,80u8,229u8,159u8,184u8,18u8,5u8,246u8, - 67u8,186u8,11u8,199u8,183u8,233u8,116u8,187u8,74u8,167u8,91u8,219u8,199u8,109u8,23u8,233u8,116u8,251u8,247u8,31u8, - 88u8,245u8,103u8,215u8,174u8,0u8,213u8,101u8,45u8,242u8,130u8,19u8,115u8,201u8,247u8,177u8,219u8,164u8,249u8,122u8, - 139u8,130u8,81u8,23u8,47u8,129u8,236u8,184u8,166u8,35u8,165u8,11u8,80u8,99u8,177u8,100u8,203u8,154u8,82u8,103u8, - 204u8,173u8,108u8,149u8,11u8,231u8,221u8,198u8,170u8,208u8,155u8,168u8,60u8,221u8,118u8,229u8,168u8,139u8,171u8,221u8, - 148u8,227u8,187u8,182u8,135u8,22u8,107u8,116u8,165u8,3u8,186u8,182u8,68u8,107u8,8u8,89u8,5u8,42u8,203u8,68u8, - 43u8,163u8,66u8,148u8,239u8,129u8,250u8,34u8,59u8,43u8,224u8,77u8,8u8,95u8,37u8,50u8,211u8,230u8,166u8,61u8, - 126u8,87u8,168u8,201u8,180u8,204u8,176u8,199u8,158u8,251u8,219u8,115u8,3u8,70u8,171u8,83u8,157u8,45u8,180u8,156u8, - 81u8,175u8,156u8,14u8,231u8,98u8,112u8,217u8,171u8,67u8,180u8,182u8,218u8,112u8,220u8,166u8,58u8,87u8,41u8,86u8, - 98u8,101u8,174u8,156u8,202u8,124u8,198u8,111u8,83u8,228u8,234u8,255u8,118u8,183u8,25u8,142u8,80u8,85u8,171u8,4u8, - 112u8,29u8,124u8,45u8,7u8,190u8,138u8,68u8,105u8,33u8,75u8,118u8,83u8,230u8,250u8,201u8,23u8,185u8,110u8,177u8, - 117u8,83u8,83u8,161u8,171u8,107u8,208u8,126u8,160u8,242u8,214u8,54u8,213u8,169u8,194u8,44u8,8u8,22u8,166u8,126u8, - 28u8,71u8,144u8,52u8,159u8,232u8,243u8,235u8,130u8,1u8,113u8,165u8,104u8,91u8,98u8,46u8,149u8,105u8,93u8,222u8, - 158u8,84u8,62u8,201u8,147u8,202u8,83u8,138u8,185u8,1u8,95u8,211u8,205u8,99u8,120u8,219u8,21u8,221u8,200u8,168u8, - 213u8,58u8,237u8,120u8,194u8,119u8,156u8,27u8,178u8,172u8,6u8,121u8,168u8,207u8,47u8,23u8,24u8,87u8,199u8,212u8, - 147u8,128u8,214u8,14u8,26u8,234u8,45u8,69u8,174u8,183u8,5u8,215u8,180u8,185u8,199u8,5u8,171u8,168u8,160u8,82u8, - 92u8,18u8,177u8,184u8,140u8,153u8,220u8,104u8,175u8,200u8,171u8,153u8,245u8,66u8,215u8,25u8,226u8,46u8,109u8,122u8, - 198u8,184u8,109u8,148u8,184u8,175u8,123u8,26u8,245u8,99u8,52u8,155u8,119u8,96u8,34u8,239u8,125u8,86u8,38u8,241u8, - 39u8,163u8,137u8,13u8,39u8,5u8,80u8,185u8,36u8,66u8,183u8,236u8,88u8,113u8,2u8,121u8,41u8,98u8,188u8,112u8, - 243u8,26u8,189u8,81u8,123u8,7u8,186u8,74u8,156u8,101u8,163u8,55u8,36u8,17u8,20u8,109u8,136u8,86u8,221u8,112u8, - 26u8,86u8,139u8,234u8,246u8,127u8,151u8,6u8,128u8,170u8,147u8,178u8,104u8,41u8,165u8,54u8,127u8,233u8,25u8,131u8, - 51u8,151u8,241u8,232u8,214u8,33u8,121u8,235u8,144u8,188u8,117u8,72u8,222u8,58u8,36u8,111u8,29u8,146u8,183u8,14u8, - 201u8,143u8,207u8,250u8,114u8,146u8,8u8,5u8,211u8,90u8,9u8,225u8,129u8,146u8,247u8,5u8,191u8,31u8,217u8,101u8, - 141u8,126u8,179u8,127u8,47u8,238u8,217u8,195u8,44u8,116u8,253u8,171u8,202u8,73u8,195u8,241u8,43u8,231u8,154u8,246u8, - 246u8,130u8,100u8,215u8,254u8,58u8,127u8,14u8,155u8,201u8,58u8,241u8,102u8,189u8,247u8,172u8,246u8,141u8,59u8,109u8, - 118u8,39u8,89u8,37u8,10u8,233u8,230u8,173u8,134u8,239u8,181u8,25u8,76u8,10u8,157u8,13u8,164u8,221u8,6u8,253u8, - 51u8,227u8,44u8,211u8,36u8,232u8,2u8,95u8,238u8,204u8,95u8,10u8,40u8,111u8,156u8,238u8,166u8,196u8,32u8,15u8, - 58u8,208u8,193u8,156u8,184u8,186u8,154u8,99u8,195u8,10u8,187u8,59u8,92u8,130u8,47u8,244u8,119u8,47u8,57u8,1u8, - 103u8,251u8,159u8,188u8,244u8,228u8,167u8,140u8,221u8,31u8,50u8,68u8,77u8,167u8,145u8,244u8,178u8,205u8,227u8,131u8, - 186u8,70u8,243u8,71u8,32u8,133u8,37u8,191u8,129u8,210u8,185u8,126u8,241u8,246u8,132u8,113u8,123u8,194u8,248u8,44u8, - 79u8,24u8,130u8,176u8,210u8,125u8,110u8,248u8,180u8,62u8,100u8,68u8,173u8,113u8,103u8,192u8,181u8,6u8,121u8,75u8, - 217u8,111u8,15u8,27u8,60u8,176u8,88u8,66u8,34u8,152u8,24u8,167u8,70u8,59u8,104u8,89u8,247u8,102u8,98u8,104u8, - 16u8,196u8,144u8,183u8,220u8,38u8,253u8,179u8,225u8,90u8,7u8,214u8,90u8,215u8,152u8,61u8,237u8,111u8,10u8,140u8, - 93u8,44u8,230u8,165u8,132u8,122u8,188u8,107u8,252u8,164u8,52u8,7u8,191u8,226u8,47u8,124u8,73u8,183u8,184u8,58u8, - 179u8,209u8,89u8,227u8,101u8,31u8,30u8,89u8,23u8,30u8,139u8,139u8,220u8,239u8,240u8,255u8,79u8,167u8,179u8,2u8, - 176u8,243u8,144u8,6u8,82u8,247u8,181u8,246u8,189u8,75u8,147u8,129u8,87u8,163u8,119u8,179u8,249u8,121u8,136u8,125u8, - 227u8,210u8,144u8,35u8,189u8,178u8,94u8,18u8,127u8,177u8,205u8,168u8,225u8,123u8,203u8,143u8,142u8,194u8,184u8,10u8, - 204u8,22u8,124u8,111u8,227u8,125u8,94u8,227u8,238u8,109u8,218u8,230u8,143u8,103u8,235u8,112u8,58u8,239u8,86u8,111u8, - 152u8,239u8,142u8,135u8,52u8,239u8,45u8,71u8,93u8,234u8,235u8,201u8,171u8,116u8,36u8,58u8,92u8,235u8,203u8,196u8, - 253u8,199u8,254u8,101u8,226u8,161u8,189u8,15u8,140u8,74u8,109u8,211u8,253u8,7u8,206u8,162u8,140u8,251u8,200u8,83u8, - 157u8,224u8,98u8,130u8,212u8,120u8,177u8,121u8,0u8,150u8,200u8,136u8,4u8,78u8,240u8,217u8,198u8,20u8,101u8,25u8, - 169u8,27u8,73u8,13u8,48u8,60u8,243u8,101u8,206u8,240u8,198u8,80u8,163u8,52u8,7u8,161u8,49u8,46u8,18u8,253u8, - 136u8,169u8,78u8,219u8,123u8,122u8,127u8,196u8,111u8,157u8,22u8,164u8,104u8,32u8,32u8,176u8,113u8,225u8,153u8,142u8, - 44u8,4u8,245u8,130u8,240u8,168u8,29u8,124u8,255u8,197u8,255u8,3u8,137u8,102u8,159u8,239u8,27u8,8u8,1u8,0u8, - 0u8,0u8,7u8,118u8,101u8,115u8,116u8,105u8,110u8,103u8,181u8,96u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,237u8,61u8,105u8,87u8,220u8,214u8,146u8,223u8,243u8,43u8,244u8,50u8,103u8,156u8,238u8,68u8,198u8,52u8, - 216u8,30u8,63u8,48u8,62u8,225u8,97u8,252u8,204u8,25u8,27u8,124u8,128u8,36u8,243u8,38u8,199u8,163u8,17u8,221u8, - 106u8,208u8,164u8,91u8,234u8,145u8,212u8,198u8,188u8,140u8,255u8,251u8,84u8,213u8,221u8,23u8,45u8,221u8,52u8,224u8, - 5u8,159u8,196u8,6u8,73u8,119u8,171u8,91u8,219u8,173u8,91u8,203u8,163u8,31u8,191u8,11u8,126u8,12u8,78u8,210u8, - 233u8,108u8,146u8,4u8,31u8,146u8,178u8,74u8,179u8,243u8,96u8,152u8,103u8,85u8,17u8,15u8,171u8,160u8,186u8,136u8, - 171u8,32u8,158u8,76u8,242u8,203u8,50u8,40u8,103u8,201u8,48u8,29u8,95u8,225u8,251u8,139u8,252u8,50u8,152u8,206u8, - 135u8,23u8,193u8,238u8,187u8,83u8,248u8,56u8,205u8,224u8,237u8,69u8,62u8,159u8,140u8,130u8,51u8,213u8,73u8,154u8, - 5u8,73u8,12u8,223u8,140u8,211u8,143u8,201u8,232u8,97u8,153u8,254u8,51u8,9u8,102u8,73u8,145u8,230u8,163u8,181u8, - 224u8,244u8,34u8,193u8,81u8,157u8,225u8,226u8,73u8,153u8,195u8,111u8,211u8,164u8,12u8,46u8,211u8,234u8,34u8,40u8, - 171u8,248u8,15u8,252u8,32u8,206u8,70u8,114u8,22u8,23u8,113u8,145u8,92u8,228u8,147u8,81u8,82u8,148u8,65u8,149u8, - 211u8,119u8,163u8,34u8,190u8,12u8,138u8,228u8,50u8,46u8,70u8,37u8,124u8,123u8,85u8,165u8,211u8,100u8,13u8,6u8, - 192u8,49u8,126u8,229u8,99u8,148u8,195u8,139u8,100u8,52u8,135u8,53u8,166u8,37u8,124u8,58u8,43u8,146u8,50u8,201u8, - 170u8,4u8,186u8,133u8,22u8,48u8,145u8,97u8,149u8,23u8,65u8,62u8,14u8,70u8,105u8,89u8,21u8,233u8,217u8,188u8, - 74u8,243u8,172u8,92u8,11u8,94u8,193u8,211u8,228u8,99u8,140u8,192u8,9u8,233u8,59u8,171u8,179u8,124u8,140u8,99u8, - 252u8,190u8,249u8,232u8,241u8,179u8,48u8,96u8,127u8,15u8,224u8,239u8,247u8,193u8,52u8,137u8,1u8,40u8,12u8,124u8, - 227u8,42u8,41u8,224u8,71u8,5u8,25u8,88u8,86u8,81u8,149u8,91u8,216u8,116u8,64u8,192u8,0u8,24u8,21u8,101u8, - 69u8,203u8,44u8,19u8,128u8,198u8,136u8,67u8,10u8,193u8,48u8,153u8,80u8,59u8,234u8,29u8,103u8,136u8,29u8,85u8, - 121u8,21u8,79u8,130u8,188u8,72u8,207u8,211u8,12u8,126u8,56u8,47u8,226u8,172u8,194u8,5u8,7u8,27u8,172u8,187u8, - 234u8,34u8,45u8,68u8,31u8,90u8,23u8,56u8,53u8,250u8,108u8,115u8,45u8,216u8,133u8,135u8,229u8,252u8,172u8,76u8, - 254u8,119u8,14u8,128u8,48u8,199u8,163u8,109u8,144u8,45u8,130u8,222u8,36u8,134u8,31u8,117u8,200u8,224u8,222u8,226u8, - 60u8,4u8,28u8,250u8,193u8,60u8,171u8,210u8,9u8,61u8,51u8,103u8,21u8,20u8,115u8,128u8,67u8,62u8,175u8,196u8, - 126u8,156u8,168u8,253u8,11u8,198u8,176u8,163u8,2u8,10u8,187u8,163u8,41u8,116u8,58u8,132u8,93u8,46u8,131u8,97u8, - 145u8,196u8,85u8,18u8,113u8,112u8,69u8,18u8,61u8,8u8,39u8,98u8,29u8,250u8,62u8,208u8,243u8,175u8,36u8,114u8, - 77u8,210u8,241u8,24u8,63u8,29u8,4u8,87u8,73u8,92u8,32u8,144u8,117u8,220u8,227u8,64u8,162u8,247u8,83u8,24u8, - 232u8,66u8,192u8,113u8,151u8,182u8,45u8,102u8,15u8,113u8,243u8,53u8,204u8,227u8,243u8,156u8,103u8,147u8,124u8,248u8, - 71u8,36u8,48u8,15u8,176u8,177u8,64u8,112u8,2u8,176u8,248u8,35u8,218u8,141u8,43u8,248u8,58u8,227u8,168u8,29u8, - 243u8,173u8,232u8,245u8,131u8,203u8,139u8,20u8,136u8,227u8,146u8,168u8,6u8,95u8,226u8,176u8,172u8,195u8,82u8,226u8, - 50u8,64u8,59u8,40u8,211u8,108u8,152u8,16u8,100u8,249u8,2u8,216u8,130u8,46u8,0u8,113u8,179u8,28u8,54u8,46u8, - 46u8,75u8,64u8,227u8,158u8,88u8,143u8,120u8,78u8,56u8,150u8,140u8,250u8,161u8,28u8,143u8,70u8,194u8,87u8,69u8, - 50u8,73u8,226u8,50u8,65u8,74u8,225u8,8u8,236u8,238u8,155u8,68u8,19u8,137u8,188u8,108u8,110u8,48u8,148u8,156u8, - 92u8,130u8,148u8,26u8,140u8,231u8,147u8,201u8,149u8,36u8,195u8,248u8,12u8,246u8,165u8,7u8,115u8,72u8,171u8,31u8, - 74u8,68u8,176u8,255u8,73u8,144u8,141u8,228u8,146u8,148u8,177u8,147u8,249u8,12u8,166u8,101u8,16u8,50u8,66u8,8u8, - 129u8,131u8,163u8,74u8,68u8,75u8,96u8,214u8,216u8,50u8,97u8,212u8,111u8,142u8,49u8,158u8,103u8,12u8,228u8,248u8, - 198u8,232u8,235u8,44u8,70u8,128u8,228u8,153u8,103u8,85u8,63u8,112u8,254u8,1u8,255u8,84u8,197u8,124u8,88u8,205u8, - 11u8,226u8,19u8,193u8,99u8,177u8,80u8,133u8,35u8,2u8,29u8,66u8,147u8,114u8,5u8,234u8,101u8,192u8,1u8,25u8, - 25u8,175u8,233u8,40u8,93u8,26u8,91u8,12u8,179u8,99u8,80u8,163u8,7u8,48u8,41u8,98u8,149u8,107u8,252u8,53u8, - 14u8,12u8,221u8,225u8,126u8,235u8,180u8,68u8,131u8,11u8,68u8,208u8,137u8,222u8,34u8,44u8,160u8,150u8,120u8,52u8, - 74u8,137u8,28u8,17u8,12u8,176u8,153u8,241u8,112u8,56u8,159u8,206u8,39u8,113u8,165u8,237u8,18u8,67u8,31u8,164u8, - 96u8,133u8,96u8,2u8,99u8,123u8,253u8,181u8,224u8,72u8,96u8,151u8,220u8,95u8,198u8,207u8,249u8,238u8,234u8,48u8, - 175u8,217u8,51u8,99u8,195u8,8u8,156u8,79u8,0u8,156u8,101u8,57u8,159u8,34u8,196u8,104u8,105u8,122u8,179u8,113u8, - 94u8,156u8,231u8,132u8,18u8,58u8,168u8,224u8,105u8,176u8,193u8,32u8,94u8,134u8,64u8,27u8,9u8,109u8,223u8,149u8, - 241u8,77u8,124u8,30u8,167u8,89u8,200u8,158u8,19u8,159u8,50u8,128u8,75u8,120u8,156u8,255u8,145u8,100u8,52u8,4u8, - 141u8,155u8,37u8,31u8,5u8,103u8,211u8,224u8,64u8,223u8,35u8,101u8,130u8,36u8,184u8,20u8,66u8,75u8,52u8,97u8, - 140u8,152u8,230u8,193u8,6u8,154u8,166u8,72u8,95u8,26u8,33u8,27u8,51u8,194u8,65u8,57u8,199u8,70u8,161u8,163u8, - 237u8,184u8,232u8,144u8,191u8,117u8,123u8,228u8,252u8,144u8,241u8,61u8,131u8,19u8,18u8,38u8,18u8,15u8,20u8,0u8, - 180u8,5u8,37u8,176u8,35u8,206u8,46u8,51u8,36u8,232u8,225u8,69u8,156u8,157u8,39u8,94u8,68u8,149u8,220u8,76u8, - 124u8,207u8,230u8,63u8,159u8,141u8,136u8,195u8,230u8,48u8,84u8,40u8,126u8,203u8,1u8,84u8,49u8,200u8,193u8,16u8, - 80u8,45u8,64u8,1u8,89u8,69u8,140u8,90u8,3u8,148u8,98u8,128u8,95u8,114u8,137u8,244u8,61u8,199u8,26u8,216u8, - 212u8,201u8,21u8,151u8,106u8,72u8,225u8,132u8,214u8,124u8,158u8,146u8,139u8,200u8,209u8,21u8,27u8,228u8,131u8,158u8, - 37u8,89u8,50u8,78u8,135u8,105u8,92u8,92u8,17u8,204u8,112u8,28u8,13u8,95u8,140u8,125u8,146u8,156u8,192u8,144u8, - 210u8,65u8,143u8,163u8,115u8,168u8,97u8,2u8,33u8,113u8,159u8,145u8,14u8,204u8,94u8,231u8,221u8,176u8,0u8,156u8, - 185u8,62u8,48u8,16u8,78u8,14u8,18u8,108u8,45u8,248u8,219u8,85u8,48u8,74u8,198u8,241u8,4u8,32u8,156u8,142u8, - 129u8,231u8,235u8,31u8,165u8,156u8,177u8,38u8,21u8,227u8,9u8,230u8,36u8,160u8,127u8,134u8,12u8,48u8,197u8,81u8, - 90u8,0u8,215u8,3u8,174u8,200u8,135u8,210u8,71u8,23u8,67u8,9u8,182u8,99u8,238u8,11u8,108u8,7u8,252u8,238u8, - 21u8,126u8,216u8,151u8,120u8,171u8,111u8,181u8,1u8,33u8,134u8,190u8,227u8,52u8,75u8,203u8,11u8,2u8,165u8,33u8, - 181u8,225u8,31u8,28u8,150u8,104u8,7u8,52u8,160u8,15u8,40u8,248u8,241u8,163u8,241u8,188u8,128u8,14u8,11u8,201u8, - 51u8,96u8,27u8,4u8,30u8,153u8,139u8,28u8,23u8,249u8,20u8,96u8,66u8,168u8,72u8,50u8,122u8,164u8,49u8,16u8, - 206u8,62u8,0u8,93u8,16u8,19u8,18u8,206u8,65u8,74u8,54u8,162u8,206u8,69u8,98u8,115u8,205u8,244u8,107u8,36u8, - 245u8,56u8,93u8,167u8,195u8,215u8,146u8,209u8,251u8,8u8,225u8,135u8,82u8,245u8,141u8,61u8,141u8,0u8,109u8,75u8, - 132u8,237u8,163u8,239u8,166u8,57u8,227u8,166u8,179u8,42u8,47u8,163u8,113u8,17u8,79u8,147u8,203u8,188u8,248u8,99u8, - 107u8,75u8,116u8,241u8,231u8,119u8,1u8,252u8,153u8,131u8,28u8,44u8,171u8,209u8,214u8,214u8,217u8,176u8,220u8,54u8, - 159u8,36u8,69u8,145u8,23u8,214u8,51u8,210u8,101u8,163u8,25u8,32u8,87u8,181u8,185u8,177u8,181u8,245u8,231u8,73u8, - 50u8,25u8,135u8,193u8,43u8,124u8,250u8,142u8,61u8,252u8,100u8,181u8,40u8,211u8,243u8,44u8,177u8,187u8,65u8,152u8, - 102u8,231u8,208u8,126u8,94u8,141u8,65u8,111u8,57u8,161u8,95u8,237u8,150u8,76u8,41u8,221u8,254u8,78u8,62u8,101u8, - 75u8,161u8,119u8,179u8,60u8,159u8,68u8,243u8,167u8,143u8,229u8,20u8,222u8,193u8,3u8,173u8,3u8,237u8,211u8,146u8, - 84u8,251u8,104u8,26u8,207u8,228u8,199u8,76u8,219u8,127u8,27u8,207u8,62u8,57u8,189u8,107u8,128u8,226u8,152u8,170u8, - 181u8,194u8,149u8,236u8,197u8,179u8,248u8,44u8,157u8,164u8,213u8,85u8,8u8,252u8,245u8,50u8,34u8,44u8,138u8,128u8, - 1u8,141u8,38u8,137u8,51u8,1u8,189u8,55u8,122u8,96u8,247u8,137u8,202u8,75u8,81u8,137u8,199u8,81u8,90u8,130u8, - 104u8,58u8,7u8,148u8,75u8,10u8,0u8,51u8,176u8,131u8,8u8,154u8,181u8,247u8,138u8,196u8,190u8,181u8,181u8,139u8, - 63u8,239u8,193u8,143u8,13u8,223u8,179u8,47u8,249u8,224u8,248u8,109u8,83u8,231u8,180u8,52u8,248u8,122u8,31u8,255u8, - 125u8,77u8,43u8,12u8,131u8,100u8,154u8,86u8,108u8,209u8,77u8,77u8,137u8,2u8,90u8,222u8,235u8,212u8,221u8,244u8, - 233u8,21u8,128u8,99u8,26u8,113u8,220u8,78u8,202u8,134u8,79u8,145u8,63u8,67u8,207u8,211u8,25u8,223u8,214u8,113u8, - 145u8,18u8,199u8,180u8,191u8,59u8,7u8,174u8,86u8,166u8,37u8,255u8,10u8,230u8,0u8,60u8,227u8,215u8,253u8,147u8, - 211u8,131u8,195u8,191u8,71u8,239u8,142u8,142u8,222u8,68u8,39u8,187u8,111u8,78u8,183u8,248u8,169u8,232u8,249u8,252u8, - 217u8,139u8,96u8,39u8,56u8,251u8,190u8,142u8,146u8,190u8,231u8,253u8,60u8,122u8,244u8,40u8,248u8,205u8,33u8,70u8, - 100u8,155u8,105u8,246u8,33u8,158u8,164u8,40u8,242u8,228u8,112u8,251u8,7u8,135u8,191u8,238u8,190u8,57u8,120u8,25u8, - 253u8,118u8,112u8,250u8,250u8,229u8,241u8,238u8,111u8,187u8,111u8,162u8,221u8,151u8,47u8,143u8,247u8,79u8,78u8,182u8, - 2u8,64u8,108u8,24u8,113u8,176u8,45u8,187u8,117u8,14u8,113u8,92u8,240u8,129u8,224u8,78u8,166u8,179u8,234u8,202u8, - 232u8,121u8,255u8,237u8,187u8,211u8,127u8,68u8,98u8,61u8,39u8,123u8,175u8,247u8,95u8,254u8,242u8,102u8,95u8,244u8, - 186u8,225u8,246u8,202u8,53u8,4u8,213u8,231u8,186u8,209u8,223u8,127u8,238u8,31u8,31u8,57u8,221u8,69u8,239u8,246u8, - 143u8,15u8,142u8,94u8,138u8,94u8,55u8,85u8,175u8,134u8,54u8,56u8,1u8,92u8,110u8,156u8,236u8,225u8,81u8,116u8, - 242u8,122u8,247u8,120u8,255u8,245u8,209u8,155u8,151u8,251u8,199u8,114u8,237u8,143u8,85u8,127u8,120u8,158u8,155u8,36u8, - 217u8,57u8,168u8,15u8,32u8,205u8,12u8,85u8,138u8,206u8,139u8,76u8,159u8,197u8,129u8,202u8,96u8,148u8,103u8,63u8, - 128u8,246u8,18u8,87u8,195u8,11u8,99u8,20u8,26u8,226u8,36u8,122u8,179u8,127u8,248u8,247u8,211u8,215u8,209u8,219u8, - 131u8,147u8,183u8,187u8,167u8,123u8,175u8,197u8,96u8,79u8,92u8,144u8,240u8,41u8,147u8,134u8,11u8,19u8,7u8,74u8, - 68u8,253u8,19u8,245u8,0u8,228u8,194u8,195u8,121u8,81u8,160u8,236u8,56u8,35u8,77u8,71u8,98u8,221u8,90u8,240u8, - 58u8,38u8,78u8,13u8,43u8,229u8,7u8,196u8,241u8,156u8,235u8,216u8,106u8,46u8,18u8,146u8,167u8,187u8,199u8,167u8, - 209u8,233u8,17u8,0u8,224u8,232u8,232u8,80u8,204u8,229u8,169u8,185u8,112u8,198u8,61u8,133u8,232u8,197u8,30u8,153u8, - 240u8,224u8,42u8,177u8,45u8,14u8,44u8,208u8,158u8,2u8,74u8,189u8,61u8,144u8,125u8,255u8,155u8,103u8,157u8,66u8, - 186u8,102u8,73u8,50u8,210u8,230u8,14u8,143u8,210u8,15u8,200u8,135u8,65u8,198u8,121u8,103u8,191u8,119u8,116u8,120u8, - 122u8,188u8,187u8,119u8,26u8,209u8,40u8,123u8,167u8,7u8,191u8,74u8,12u8,123u8,166u8,134u8,81u8,210u8,61u8,207u8, - 180u8,67u8,18u8,147u8,163u8,49u8,30u8,163u8,249u8,64u8,189u8,89u8,60u8,167u8,83u8,75u8,161u8,132u8,252u8,168u8, - 223u8,188u8,62u8,103u8,42u8,240u8,235u8,155u8,55u8,214u8,100u8,254u8,170u8,38u8,115u8,152u8,187u8,6u8,151u8,49u8, - 176u8,221u8,17u8,110u8,235u8,172u8,200u8,63u8,164u8,35u8,180u8,135u8,72u8,25u8,218u8,178u8,232u8,87u8,71u8,191u8, - 28u8,74u8,252u8,31u8,172u8,171u8,113u8,246u8,24u8,238u8,120u8,149u8,149u8,192u8,60u8,203u8,207u8,128u8,61u8,145u8, - 125u8,71u8,130u8,251u8,143u8,100u8,45u8,56u8,132u8,189u8,32u8,53u8,32u8,78u8,43u8,110u8,88u8,32u8,77u8,62u8, - 153u8,229u8,22u8,94u8,191u8,219u8,63u8,124u8,201u8,113u8,233u8,223u8,247u8,173u8,25u8,105u8,236u8,227u8,239u8,116u8, - 96u8,138u8,167u8,40u8,99u8,154u8,201u8,252u8,239u8,199u8,187u8,135u8,167u8,178u8,11u8,15u8,175u8,224u8,146u8,138u8, - 31u8,179u8,131u8,156u8,244u8,166u8,105u8,156u8,197u8,231u8,201u8,20u8,73u8,162u8,200u8,39u8,9u8,30u8,158u8,74u8, - 0u8,38u8,195u8,85u8,47u8,36u8,119u8,247u8,246u8,96u8,178u8,167u8,209u8,235u8,221u8,19u8,0u8,102u8,116u8,124u8, - 244u8,102u8,95u8,241u8,189u8,77u8,147u8,6u8,62u8,248u8,135u8,46u8,209u8,254u8,102u8,141u8,108u8,140u8,133u8,157u8, - 122u8,118u8,74u8,99u8,45u8,187u8,188u8,71u8,78u8,94u8,156u8,180u8,10u8,96u8,35u8,9u8,123u8,114u8,17u8,127u8, - 96u8,219u8,135u8,118u8,13u8,80u8,106u8,71u8,52u8,10u8,41u8,99u8,168u8,225u8,85u8,168u8,120u8,226u8,222u8,229u8, - 153u8,181u8,45u8,199u8,192u8,99u8,78u8,14u8,142u8,14u8,163u8,151u8,251u8,135u8,7u8,251u8,106u8,112u8,141u8,213u8, - 252u8,103u8,82u8,228u8,96u8,37u8,72u8,166u8,160u8,194u8,129u8,184u8,87u8,24u8,136u8,135u8,217u8,224u8,71u8,80u8, - 88u8,72u8,51u8,205u8,220u8,238u8,127u8,221u8,223u8,139u8,24u8,147u8,127u8,117u8,116u8,28u8,189u8,221u8,61u8,132u8, - 31u8,126u8,57u8,4u8,180u8,87u8,108u8,100u8,240u8,84u8,19u8,74u8,111u8,227u8,143u8,233u8,116u8,62u8,13u8,178u8, - 249u8,244u8,44u8,41u8,92u8,46u8,170u8,108u8,65u8,160u8,68u8,17u8,181u8,150u8,243u8,217u8,44u8,47u8,12u8,114u8, - 123u8,187u8,251u8,31u8,7u8,111u8,127u8,121u8,235u8,229u8,214u8,155u8,235u8,218u8,104u8,14u8,107u8,33u8,38u8,194u8, - 9u8,202u8,251u8,1u8,7u8,226u8,7u8,118u8,244u8,55u8,53u8,110u8,156u8,13u8,160u8,234u8,52u8,30u8,25u8,155u8, - 107u8,72u8,107u8,147u8,226u8,7u8,13u8,92u8,14u8,81u8,231u8,44u8,193u8,179u8,181u8,100u8,50u8,194u8,182u8,202u8, - 245u8,108u8,218u8,113u8,250u8,132u8,91u8,137u8,224u8,116u8,28u8,35u8,147u8,103u8,250u8,183u8,87u8,217u8,246u8,207u8, - 234u8,20u8,145u8,224u8,112u8,247u8,84u8,237u8,254u8,134u8,6u8,165u8,99u8,162u8,19u8,58u8,155u8,225u8,18u8,25u8, - 30u8,7u8,67u8,80u8,6u8,99u8,100u8,191u8,104u8,107u8,6u8,121u8,102u8,177u8,121u8,65u8,1u8,103u8,201u8,21u8, - 157u8,184u8,47u8,16u8,48u8,100u8,7u8,177u8,232u8,140u8,80u8,255u8,111u8,251u8,135u8,251u8,175u8,14u8,246u8,14u8, - 118u8,143u8,255u8,17u8,129u8,244u8,219u8,63u8,133u8,25u8,57u8,74u8,77u8,237u8,151u8,66u8,171u8,97u8,134u8,34u8, - 1u8,202u8,19u8,161u8,128u8,32u8,36u8,135u8,249u8,12u8,148u8,224u8,81u8,145u8,207u8,192u8,50u8,82u8,161u8,136u8, - 100u8,103u8,10u8,182u8,70u8,131u8,118u8,149u8,113u8,7u8,241u8,141u8,180u8,2u8,88u8,219u8,184u8,96u8,196u8,195u8, - 1u8,65u8,22u8,87u8,60u8,3u8,147u8,217u8,92u8,179u8,149u8,51u8,179u8,5u8,71u8,95u8,102u8,191u8,166u8,195u8, - 151u8,178u8,186u8,234u8,227u8,74u8,104u8,49u8,126u8,167u8,25u8,96u8,213u8,55u8,154u8,93u8,59u8,248u8,125u8,240u8, - 104u8,227u8,49u8,26u8,79u8,217u8,223u8,154u8,9u8,213u8,177u8,141u8,186u8,102u8,109u8,211u8,164u8,29u8,106u8,54u8, - 148u8,234u8,50u8,231u8,246u8,28u8,125u8,96u8,221u8,24u8,189u8,241u8,216u8,49u8,109u8,49u8,195u8,54u8,155u8,57u8, - 88u8,223u8,81u8,76u8,86u8,210u8,158u8,205u8,166u8,128u8,146u8,148u8,205u8,83u8,235u8,76u8,153u8,159u8,197u8,132u8, - 16u8,157u8,245u8,129u8,189u8,96u8,152u8,111u8,110u8,60u8,130u8,255u8,181u8,203u8,139u8,114u8,62u8,198u8,243u8,62u8, - 50u8,84u8,52u8,61u8,50u8,62u8,224u8,110u8,163u8,220u8,57u8,213u8,157u8,120u8,39u8,81u8,76u8,63u8,21u8,190u8, - 8u8,245u8,97u8,127u8,227u8,230u8,45u8,213u8,47u8,27u8,159u8,128u8,168u8,245u8,136u8,191u8,70u8,82u8,189u8,138u8, - 192u8,148u8,84u8,18u8,45u8,25u8,157u8,29u8,100u8,220u8,198u8,4u8,134u8,197u8,215u8,96u8,149u8,156u8,228u8,208u8, - 33u8,97u8,144u8,101u8,226u8,78u8,205u8,251u8,12u8,205u8,222u8,205u8,255u8,176u8,239u8,162u8,209u8,28u8,172u8,64u8, - 176u8,54u8,119u8,168u8,55u8,194u8,122u8,166u8,122u8,133u8,157u8,120u8,152u8,130u8,13u8,232u8,35u8,154u8,2u8,244u8, - 222u8,193u8,124u8,34u8,236u8,121u8,140u8,169u8,48u8,67u8,53u8,195u8,144u8,137u8,175u8,163u8,212u8,188u8,125u8,49u8, - 176u8,38u8,46u8,133u8,105u8,29u8,187u8,144u8,86u8,59u8,48u8,108u8,189u8,4u8,115u8,205u8,124u8,2u8,189u8,197u8, - 147u8,57u8,93u8,238u8,172u8,115u8,203u8,186u8,64u8,85u8,148u8,45u8,138u8,171u8,101u8,185u8,53u8,110u8,9u8,22u8, - 95u8,13u8,230u8,56u8,179u8,136u8,153u8,145u8,34u8,246u8,129u8,6u8,134u8,79u8,6u8,75u8,56u8,97u8,103u8,184u8, - 131u8,108u8,156u8,19u8,59u8,240u8,176u8,128u8,223u8,104u8,116u8,109u8,167u8,209u8,0u8,77u8,162u8,147u8,68u8,13u8, - 204u8,22u8,52u8,104u8,198u8,133u8,1u8,221u8,15u8,178u8,225u8,100u8,142u8,18u8,16u8,121u8,0u8,236u8,40u8,28u8, - 50u8,1u8,17u8,135u8,137u8,182u8,67u8,120u8,236u8,231u8,140u8,119u8,75u8,112u8,224u8,208u8,102u8,58u8,92u8,61u8, - 7u8,133u8,19u8,64u8,142u8,10u8,52u8,144u8,48u8,55u8,237u8,169u8,174u8,196u8,147u8,133u8,186u8,33u8,123u8,161u8, - 234u8,131u8,126u8,245u8,119u8,176u8,151u8,79u8,209u8,206u8,137u8,86u8,167u8,89u8,156u8,142u8,132u8,252u8,16u8,163u8, - 10u8,194u8,87u8,192u8,80u8,189u8,14u8,101u8,83u8,220u8,129u8,33u8,76u8,1u8,68u8,67u8,237u8,46u8,112u8,198u8, - 188u8,167u8,139u8,184u8,63u8,192u8,202u8,250u8,167u8,78u8,70u8,85u8,98u8,33u8,51u8,73u8,12u8,207u8,204u8,201u8, - 180u8,30u8,225u8,124u8,182u8,200u8,164u8,162u8,222u8,40u8,75u8,96u8,154u8,0u8,236u8,165u8,9u8,229u8,185u8,232u8, - 67u8,116u8,166u8,145u8,186u8,176u8,225u8,41u8,214u8,96u8,137u8,17u8,147u8,43u8,184u8,103u8,231u8,75u8,194u8,31u8, - 37u8,157u8,37u8,226u8,59u8,194u8,57u8,29u8,107u8,167u8,163u8,132u8,44u8,102u8,58u8,51u8,196u8,179u8,27u8,191u8, - 192u8,77u8,135u8,250u8,160u8,66u8,164u8,234u8,7u8,15u8,214u8,22u8,224u8,14u8,26u8,95u8,205u8,17u8,4u8,255u8, - 40u8,93u8,160u8,1u8,37u8,185u8,161u8,99u8,75u8,167u8,22u8,99u8,213u8,199u8,9u8,138u8,112u8,77u8,94u8,241u8, - 147u8,35u8,187u8,133u8,34u8,150u8,2u8,86u8,194u8,33u8,222u8,106u8,112u8,21u8,192u8,190u8,226u8,80u8,51u8,42u8, - 68u8,95u8,17u8,181u8,118u8,25u8,216u8,47u8,37u8,211u8,47u8,105u8,49u8,64u8,128u8,124u8,118u8,26u8,203u8,165u8, - 243u8,102u8,52u8,4u8,99u8,153u8,107u8,239u8,250u8,78u8,239u8,138u8,140u8,66u8,218u8,216u8,150u8,13u8,157u8,153u8, - 136u8,0u8,34u8,154u8,241u8,232u8,249u8,47u8,244u8,205u8,17u8,255u8,132u8,222u8,104u8,216u8,162u8,219u8,228u8,27u8, - 154u8,255u8,138u8,239u8,237u8,182u8,186u8,181u8,222u8,219u8,246u8,24u8,63u8,120u8,67u8,239u8,237u8,182u8,216u8,82u8, - 179u8,115u8,123u8,155u8,159u8,36u8,213u8,223u8,212u8,39u8,206u8,204u8,141u8,91u8,37u8,255u8,220u8,233u8,147u8,99u8, - 246u8,133u8,221u8,30u8,113u8,204u8,219u8,10u8,201u8,197u8,254u8,88u8,221u8,54u8,121u8,155u8,188u8,148u8,175u8,237u8, - 134u8,202u8,182u8,238u8,107u8,119u8,42u8,222u8,218u8,205u8,76u8,243u8,180u8,183u8,45u8,29u8,243u8,5u8,249u8,234u8, - 237u8,189u8,172u8,138u8,159u8,186u8,222u8,170u8,163u8,155u8,203u8,179u8,232u8,24u8,169u8,115u8,25u8,102u8,34u8,182u8, - 153u8,140u8,217u8,63u8,77u8,227u8,132u8,164u8,144u8,219u8,163u8,125u8,159u8,80u8,74u8,61u8,197u8,229u8,91u8,58u8, - 165u8,144u8,153u8,31u8,241u8,43u8,159u8,3u8,47u8,22u8,252u8,130u8,221u8,177u8,129u8,253u8,215u8,225u8,15u8,32u8, - 224u8,114u8,38u8,117u8,127u8,160u8,99u8,170u8,96u8,101u8,195u8,124u8,2u8,106u8,175u8,60u8,202u8,225u8,159u8,44u8, - 7u8,225u8,198u8,137u8,84u8,113u8,127u8,118u8,247u8,239u8,3u8,243u8,30u8,189u8,178u8,24u8,126u8,61u8,184u8,235u8, - 63u8,39u8,240u8,248u8,245u8,246u8,6u8,177u8,88u8,39u8,237u8,152u8,204u8,96u8,220u8,203u8,98u8,57u8,157u8,88u8, - 164u8,189u8,51u8,237u8,220u8,52u8,106u8,209u8,3u8,22u8,147u8,160u8,30u8,174u8,212u8,0u8,160u8,58u8,201u8,185u8, - 250u8,85u8,192u8,241u8,60u8,106u8,216u8,14u8,188u8,124u8,104u8,120u8,189u8,12u8,12u8,20u8,107u8,253u8,108u8,0u8, - 80u8,135u8,116u8,184u8,122u8,223u8,59u8,115u8,89u8,54u8,215u8,255u8,44u8,150u8,133u8,83u8,23u8,146u8,234u8,227u8, - 44u8,101u8,135u8,13u8,251u8,112u8,99u8,169u8,218u8,174u8,248u8,185u8,225u8,165u8,40u8,3u8,81u8,205u8,198u8,104u8, - 18u8,179u8,102u8,141u8,245u8,95u8,88u8,184u8,231u8,136u8,198u8,207u8,98u8,155u8,28u8,134u8,230u8,10u8,179u8,207u8, - 103u8,178u8,252u8,232u8,202u8,14u8,110u8,182u8,174u8,223u8,188u8,16u8,75u8,97u8,184u8,217u8,229u8,180u8,204u8,197u8, - 84u8,66u8,110u8,110u8,42u8,30u8,189u8,193u8,80u8,95u8,238u8,6u8,8u8,255u8,242u8,251u8,135u8,52u8,185u8,124u8, - 175u8,108u8,132u8,9u8,92u8,30u8,101u8,252u8,92u8,195u8,180u8,8u8,126u8,108u8,180u8,188u8,78u8,248u8,113u8,186u8, - 87u8,38u8,179u8,184u8,240u8,169u8,43u8,253u8,230u8,11u8,35u8,24u8,76u8,179u8,184u8,195u8,169u8,156u8,174u8,254u8, - 201u8,92u8,36u8,14u8,86u8,254u8,251u8,11u8,124u8,99u8,223u8,156u8,144u8,245u8,28u8,175u8,225u8,146u8,143u8,128u8, - 89u8,108u8,128u8,217u8,252u8,108u8,146u8,14u8,241u8,16u8,199u8,230u8,107u8,160u8,115u8,175u8,21u8,116u8,125u8,249u8, - 19u8,44u8,136u8,12u8,241u8,165u8,115u8,238u8,213u8,118u8,136u8,221u8,169u8,59u8,189u8,210u8,124u8,234u8,71u8,235u8, - 111u8,171u8,179u8,110u8,14u8,235u8,191u8,140u8,206u8,39u8,249u8,89u8,60u8,121u8,110u8,141u8,244u8,162u8,190u8,135u8, - 53u8,113u8,158u8,210u8,151u8,215u8,113u8,135u8,13u8,67u8,162u8,186u8,83u8,12u8,122u8,169u8,180u8,111u8,181u8,239u8, - 162u8,180u8,120u8,147u8,101u8,144u8,117u8,69u8,119u8,150u8,176u8,167u8,216u8,101u8,72u8,214u8,110u8,84u8,59u8,225u8, - 56u8,140u8,142u8,139u8,194u8,54u8,134u8,248u8,174u8,27u8,168u8,152u8,9u8,147u8,76u8,139u8,212u8,21u8,247u8,134u8, - 19u8,62u8,114u8,183u8,143u8,54u8,210u8,148u8,64u8,54u8,65u8,20u8,150u8,157u8,208u8,6u8,13u8,239u8,159u8,55u8, - 202u8,216u8,54u8,146u8,53u8,159u8,213u8,179u8,35u8,14u8,9u8,203u8,37u8,226u8,73u8,158u8,37u8,182u8,1u8,84u8, - 71u8,37u8,133u8,50u8,251u8,194u8,84u8,202u8,44u8,235u8,220u8,174u8,194u8,172u8,219u8,216u8,137u8,141u8,38u8,220u8, - 177u8,54u8,100u8,63u8,144u8,117u8,69u8,152u8,169u8,185u8,129u8,37u8,25u8,121u8,77u8,183u8,183u8,143u8,52u8,150u8, - 65u8,247u8,171u8,70u8,27u8,107u8,173u8,29u8,49u8,70u8,90u8,140u8,216u8,41u8,46u8,164u8,251u8,163u8,148u8,129u8, - 31u8,144u8,104u8,158u8,233u8,142u8,187u8,236u8,222u8,131u8,108u8,201u8,236u8,6u8,25u8,236u8,201u8,202u8,26u8,65u8, - 199u8,101u8,253u8,98u8,81u8,161u8,216u8,59u8,152u8,90u8,193u8,157u8,160u8,29u8,180u8,8u8,125u8,19u8,17u8,126u8, - 248u8,151u8,241u8,85u8,73u8,142u8,35u8,255u8,59u8,167u8,219u8,17u8,191u8,167u8,54u8,31u8,134u8,60u8,241u8,116u8, - 63u8,112u8,137u8,170u8,200u8,245u8,248u8,74u8,184u8,139u8,44u8,76u8,83u8,159u8,123u8,195u8,36u8,70u8,9u8,30u8, - 201u8,75u8,24u8,246u8,3u8,58u8,79u8,34u8,23u8,149u8,35u8,162u8,120u8,45u8,232u8,6u8,223u8,242u8,207u8,211u8, - 220u8,79u8,111u8,25u8,231u8,45u8,27u8,224u8,87u8,130u8,238u8,214u8,170u8,58u8,98u8,183u8,199u8,197u8,85u8,26u8, - 222u8,21u8,171u8,210u8,156u8,83u8,241u8,46u8,181u8,65u8,186u8,210u8,222u8,165u8,220u8,93u8,61u8,158u8,210u8,109u8, - 166u8,227u8,223u8,202u8,119u8,233u8,140u8,121u8,210u8,226u8,213u8,12u8,147u8,185u8,19u8,116u8,70u8,29u8,143u8,19u8, - 242u8,28u8,130u8,93u8,167u8,0u8,1u8,162u8,33u8,56u8,5u8,194u8,238u8,165u8,120u8,211u8,80u8,38u8,119u8,160u8, - 146u8,105u8,96u8,106u8,71u8,151u8,208u8,123u8,54u8,188u8,109u8,13u8,237u8,220u8,180u8,217u8,246u8,150u8,69u8,44u8, - 99u8,53u8,253u8,142u8,104u8,165u8,236u8,41u8,136u8,77u8,190u8,160u8,3u8,98u8,148u8,105u8,233u8,191u8,250u8,161u8, - 155u8,115u8,97u8,156u8,185u8,253u8,221u8,150u8,22u8,122u8,175u8,133u8,232u8,43u8,225u8,23u8,66u8,17u8,247u8,174u8, - 177u8,195u8,54u8,147u8,47u8,186u8,7u8,234u8,232u8,189u8,112u8,158u8,194u8,17u8,81u8,119u8,239u8,140u8,51u8,233u8, - 149u8,87u8,171u8,183u8,202u8,14u8,122u8,230u8,1u8,178u8,239u8,152u8,161u8,21u8,100u8,53u8,123u8,182u8,2u8,42u8, - 160u8,67u8,239u8,47u8,12u8,130u8,207u8,213u8,7u8,47u8,88u8,183u8,253u8,190u8,246u8,37u8,59u8,149u8,98u8,223u8, - 91u8,91u8,228u8,120u8,41u8,135u8,232u8,245u8,229u8,71u8,159u8,130u8,100u8,82u8,38u8,86u8,43u8,19u8,236u8,238u8, - 40u8,107u8,206u8,170u8,84u8,127u8,29u8,105u8,72u8,82u8,195u8,229u8,69u8,206u8,124u8,19u8,8u8,220u8,232u8,38u8, - 75u8,143u8,69u8,248u8,200u8,221u8,31u8,88u8,197u8,76u8,191u8,194u8,99u8,170u8,88u8,90u8,215u8,35u8,42u8,218u8, - 91u8,105u8,195u8,248u8,153u8,16u8,159u8,144u8,166u8,136u8,254u8,157u8,15u8,135u8,23u8,232u8,139u8,116u8,142u8,106u8, - 82u8,22u8,163u8,42u8,6u8,128u8,158u8,229u8,101u8,60u8,41u8,241u8,131u8,179u8,228u8,34u8,158u8,140u8,235u8,78u8, - 176u8,194u8,47u8,64u8,14u8,171u8,174u8,196u8,111u8,245u8,148u8,137u8,43u8,252u8,10u8,55u8,154u8,214u8,181u8,160u8, - 33u8,66u8,219u8,26u8,71u8,171u8,101u8,254u8,17u8,200u8,152u8,26u8,195u8,138u8,133u8,159u8,23u8,200u8,139u8,135u8, - 20u8,149u8,40u8,199u8,147u8,254u8,67u8,33u8,191u8,227u8,39u8,245u8,59u8,61u8,191u8,168u8,166u8,211u8,28u8,131u8, - 125u8,55u8,130u8,179u8,180u8,146u8,238u8,127u8,163u8,148u8,237u8,34u8,40u8,85u8,27u8,255u8,5u8,239u8,64u8,164u8, - 130u8,6u8,192u8,44u8,21u8,188u8,39u8,166u8,228u8,99u8,240u8,243u8,5u8,78u8,149u8,184u8,26u8,83u8,213u8,228u8, - 160u8,128u8,185u8,24u8,185u8,72u8,110u8,100u8,46u8,110u8,233u8,206u8,59u8,44u8,156u8,22u8,30u8,172u8,175u8,173u8, - 63u8,221u8,120u8,18u8,74u8,132u8,183u8,150u8,184u8,241u8,244u8,217u8,227u8,205u8,39u8,143u8,159u8,60u8,229u8,81u8, - 204u8,124u8,46u8,112u8,52u8,65u8,183u8,41u8,60u8,137u8,11u8,143u8,5u8,57u8,140u8,108u8,18u8,60u8,98u8,107u8, - 217u8,225u8,131u8,172u8,193u8,109u8,39u8,15u8,37u8,66u8,159u8,7u8,248u8,127u8,156u8,92u8,82u8,236u8,41u8,192u8, - 128u8,195u8,131u8,29u8,83u8,244u8,117u8,200u8,129u8,144u8,48u8,64u8,187u8,36u8,63u8,74u8,80u8,36u8,215u8,149u8, - 214u8,122u8,146u8,107u8,131u8,202u8,209u8,238u8,206u8,130u8,195u8,49u8,166u8,19u8,153u8,217u8,110u8,135u8,95u8,214u8, - 161u8,188u8,35u8,197u8,113u8,167u8,191u8,58u8,61u8,178u8,243u8,129u8,187u8,221u8,60u8,200u8,246u8,248u8,35u8,185u8, - 89u8,81u8,166u8,0u8,56u8,225u8,147u8,142u8,170u8,244u8,36u8,54u8,166u8,33u8,162u8,69u8,72u8,53u8,94u8,97u8, - 3u8,130u8,221u8,133u8,249u8,143u8,64u8,20u8,105u8,32u8,146u8,33u8,188u8,171u8,215u8,87u8,153u8,47u8,178u8,131u8, - 64u8,77u8,152u8,163u8,220u8,231u8,146u8,202u8,81u8,249u8,208u8,207u8,118u8,73u8,140u8,218u8,54u8,58u8,238u8,9u8, - 40u8,208u8,252u8,72u8,94u8,134u8,65u8,20u8,234u8,87u8,189u8,236u8,106u8,161u8,31u8,236u8,24u8,250u8,156u8,29u8, - 109u8,229u8,198u8,95u8,241u8,134u8,245u8,224u8,12u8,157u8,101u8,57u8,90u8,132u8,54u8,91u8,119u8,162u8,193u8,67u8, - 183u8,3u8,235u8,80u8,15u8,159u8,56u8,43u8,233u8,122u8,61u8,178u8,20u8,237u8,24u8,120u8,104u8,196u8,220u8,238u8, - 129u8,254u8,79u8,89u8,14u8,120u8,192u8,46u8,154u8,198u8,229u8,184u8,142u8,101u8,65u8,156u8,4u8,50u8,48u8,48u8, - 197u8,35u8,203u8,191u8,222u8,244u8,19u8,191u8,77u8,154u8,241u8,81u8,203u8,2u8,215u8,85u8,250u8,34u8,34u8,56u8, - 46u8,250u8,174u8,115u8,111u8,157u8,182u8,106u8,249u8,0u8,16u8,217u8,226u8,60u8,194u8,162u8,47u8,125u8,215u8,118u8, - 244u8,223u8,26u8,168u8,162u8,30u8,72u8,86u8,231u8,55u8,198u8,21u8,120u8,0u8,220u8,78u8,160u8,98u8,98u8,217u8, - 163u8,222u8,3u8,135u8,226u8,148u8,123u8,167u8,105u8,248u8,216u8,54u8,61u8,108u8,181u8,78u8,162u8,42u8,231u8,164u8, - 72u8,46u8,96u8,17u8,3u8,50u8,153u8,131u8,187u8,244u8,143u8,26u8,75u8,221u8,182u8,116u8,53u8,183u8,8u8,45u8, - 210u8,73u8,116u8,145u8,102u8,13u8,226u8,78u8,191u8,114u8,212u8,26u8,117u8,146u8,23u8,181u8,39u8,242u8,47u8,83u8, - 118u8,88u8,123u8,42u8,32u8,209u8,180u8,125u8,93u8,247u8,198u8,103u8,249u8,100u8,246u8,145u8,122u8,46u8,41u8,77u8, - 40u8,206u8,198u8,5u8,7u8,99u8,77u8,253u8,5u8,116u8,169u8,210u8,217u8,68u8,169u8,206u8,198u8,230u8,75u8,174u8, - 72u8,134u8,88u8,207u8,72u8,33u8,179u8,185u8,170u8,219u8,76u8,195u8,208u8,139u8,167u8,21u8,92u8,69u8,173u8,50u8, - 195u8,94u8,151u8,193u8,250u8,199u8,117u8,228u8,204u8,89u8,110u8,183u8,103u8,161u8,131u8,194u8,78u8,193u8,86u8,164u8, - 77u8,163u8,9u8,11u8,187u8,88u8,89u8,107u8,216u8,237u8,226u8,231u8,206u8,235u8,162u8,164u8,1u8,245u8,157u8,224u8, - 65u8,39u8,106u8,186u8,45u8,230u8,151u8,226u8,129u8,198u8,124u8,52u8,129u8,125u8,216u8,145u8,134u8,47u8,22u8,57u8, - 220u8,211u8,39u8,173u8,117u8,113u8,121u8,145u8,98u8,186u8,158u8,52u8,120u8,142u8,205u8,108u8,227u8,153u8,43u8,19u8, - 126u8,20u8,221u8,178u8,217u8,27u8,221u8,134u8,65u8,170u8,245u8,204u8,112u8,137u8,167u8,166u8,96u8,151u8,241u8,248u8, - 119u8,225u8,39u8,157u8,212u8,84u8,25u8,118u8,118u8,92u8,76u8,210u8,237u8,127u8,58u8,182u8,195u8,183u8,182u8,49u8, - 220u8,134u8,157u8,201u8,235u8,237u8,85u8,226u8,31u8,62u8,45u8,237u8,51u8,227u8,147u8,79u8,230u8,186u8,16u8,232u8, - 105u8,240u8,147u8,136u8,180u8,227u8,95u8,200u8,159u8,127u8,6u8,146u8,209u8,217u8,7u8,197u8,196u8,50u8,55u8,90u8, - 79u8,6u8,49u8,73u8,199u8,140u8,130u8,244u8,220u8,86u8,70u8,36u8,160u8,145u8,126u8,76u8,250u8,72u8,208u8,161u8, - 159u8,95u8,78u8,139u8,171u8,79u8,135u8,242u8,172u8,116u8,90u8,242u8,0u8,186u8,112u8,232u8,82u8,135u8,128u8,164u8, - 250u8,24u8,34u8,207u8,105u8,214u8,166u8,213u8,191u8,244u8,44u8,180u8,125u8,160u8,18u8,140u8,189u8,8u8,214u8,67u8, - 166u8,45u8,110u8,109u8,241u8,124u8,2u8,81u8,92u8,156u8,207u8,209u8,129u8,186u8,87u8,19u8,243u8,223u8,215u8,16u8, - 82u8,140u8,96u8,77u8,176u8,165u8,223u8,166u8,216u8,127u8,95u8,239u8,246u8,177u8,195u8,1u8,87u8,240u8,98u8,71u8, - 57u8,183u8,108u8,109u8,101u8,192u8,2u8,184u8,75u8,66u8,175u8,31u8,26u8,173u8,107u8,231u8,228u8,15u8,160u8,215u8, - 90u8,235u8,28u8,172u8,30u8,228u8,250u8,198u8,135u8,173u8,243u8,54u8,191u8,176u8,128u8,104u8,190u8,244u8,133u8,80u8, - 173u8,135u8,126u8,11u8,185u8,151u8,54u8,236u8,124u8,111u8,140u8,50u8,224u8,233u8,56u8,61u8,231u8,35u8,150u8,109u8, - 72u8,46u8,217u8,188u8,237u8,184u8,246u8,128u8,133u8,120u8,120u8,93u8,61u8,1u8,153u8,31u8,212u8,58u8,197u8,159u8, - 205u8,175u8,34u8,208u8,250u8,188u8,161u8,63u8,152u8,73u8,228u8,185u8,204u8,63u8,242u8,98u8,185u8,16u8,160u8,78u8, - 206u8,226u8,75u8,248u8,167u8,183u8,121u8,66u8,115u8,86u8,125u8,52u8,227u8,166u8,187u8,18u8,195u8,239u8,41u8,27u8, - 1u8,165u8,225u8,50u8,18u8,81u8,137u8,44u8,110u8,42u8,99u8,163u8,76u8,103u8,36u8,71u8,227u8,2u8,139u8,181u8, - 35u8,175u8,19u8,116u8,201u8,84u8,113u8,183u8,146u8,29u8,56u8,178u8,220u8,123u8,225u8,227u8,37u8,173u8,191u8,216u8, - 89u8,81u8,128u8,80u8,48u8,119u8,12u8,124u8,250u8,1u8,208u8,78u8,120u8,214u8,185u8,32u8,237u8,74u8,100u8,245u8, - 233u8,73u8,76u8,66u8,115u8,148u8,141u8,134u8,68u8,54u8,190u8,233u8,108u8,183u8,177u8,64u8,67u8,114u8,183u8,112u8, - 43u8,43u8,153u8,72u8,59u8,131u8,210u8,82u8,3u8,9u8,142u8,203u8,209u8,188u8,143u8,130u8,181u8,105u8,42u8,29u8, - 225u8,232u8,207u8,60u8,82u8,195u8,172u8,116u8,102u8,192u8,60u8,110u8,200u8,175u8,19u8,77u8,21u8,5u8,198u8,185u8, - 213u8,37u8,63u8,225u8,118u8,63u8,205u8,29u8,70u8,168u8,47u8,204u8,176u8,178u8,19u8,176u8,188u8,63u8,255u8,76u8, - 138u8,92u8,35u8,211u8,158u8,165u8,76u8,233u8,129u8,28u8,174u8,94u8,165u8,14u8,7u8,198u8,89u8,147u8,241u8,157u8, - 158u8,47u8,61u8,64u8,127u8,121u8,197u8,204u8,163u8,219u8,221u8,172u8,174u8,70u8,246u8,53u8,176u8,165u8,177u8,237u8, - 239u8,227u8,249u8,95u8,67u8,14u8,176u8,84u8,193u8,181u8,86u8,239u8,193u8,20u8,204u8,52u8,28u8,65u8,66u8,67u8, - 19u8,246u8,244u8,198u8,190u8,83u8,224u8,100u8,91u8,64u8,97u8,186u8,2u8,203u8,172u8,86u8,236u8,139u8,105u8,82u8, - 156u8,243u8,161u8,184u8,79u8,150u8,247u8,99u8,181u8,3u8,236u8,117u8,207u8,81u8,234u8,84u8,23u8,236u8,68u8,238u8, - 124u8,208u8,6u8,33u8,183u8,133u8,177u8,38u8,243u8,181u8,53u8,61u8,11u8,151u8,140u8,95u8,127u8,50u8,251u8,209u8, - 8u8,160u8,94u8,187u8,180u8,233u8,216u8,232u8,176u8,131u8,14u8,67u8,137u8,77u8,250u8,22u8,177u8,209u8,113u8,83u8, - 185u8,244u8,240u8,184u8,125u8,74u8,242u8,71u8,153u8,61u8,232u8,82u8,95u8,79u8,59u8,194u8,147u8,190u8,121u8,100u8, - 53u8,192u8,43u8,75u8,171u8,20u8,134u8,254u8,103u8,162u8,69u8,162u8,146u8,247u8,184u8,73u8,146u8,44u8,158u8,78u8, - 240u8,254u8,29u8,30u8,114u8,185u8,181u8,197u8,159u8,68u8,249u8,152u8,223u8,178u8,111u8,119u8,187u8,243u8,151u8,172u8, - 212u8,38u8,9u8,68u8,89u8,176u8,215u8,176u8,175u8,66u8,191u8,112u8,169u8,51u8,7u8,202u8,232u8,56u8,215u8,117u8, - 192u8,197u8,10u8,30u8,199u8,182u8,238u8,190u8,177u8,130u8,217u8,236u8,124u8,105u8,77u8,17u8,109u8,28u8,8u8,102u8, - 159u8,159u8,250u8,254u8,35u8,7u8,165u8,1u8,48u8,192u8,239u8,232u8,82u8,100u8,119u8,192u8,128u8,61u8,219u8,55u8, - 158u8,231u8,15u8,228u8,233u8,149u8,149u8,107u8,23u8,237u8,159u8,138u8,0u8,52u8,172u8,17u8,124u8,76u8,36u8,171u8, - 210u8,220u8,221u8,158u8,148u8,255u8,92u8,207u8,10u8,172u8,7u8,24u8,91u8,139u8,236u8,165u8,70u8,95u8,19u8,18u8, - 84u8,236u8,154u8,95u8,157u8,176u8,56u8,165u8,238u8,110u8,142u8,24u8,229u8,152u8,251u8,249u8,96u8,142u8,213u8,159u8, - 76u8,122u8,204u8,152u8,103u8,192u8,249u8,129u8,179u8,10u8,149u8,27u8,147u8,231u8,204u8,148u8,254u8,162u8,30u8,229u8, - 170u8,97u8,218u8,58u8,248u8,118u8,71u8,35u8,158u8,166u8,244u8,18u8,12u8,53u8,130u8,182u8,60u8,247u8,207u8,98u8, - 105u8,220u8,102u8,95u8,75u8,90u8,182u8,177u8,192u8,79u8,93u8,246u8,226u8,44u8,96u8,50u8,162u8,98u8,97u8,31u8, - 150u8,153u8,34u8,2u8,142u8,218u8,64u8,128u8,219u8,154u8,214u8,203u8,104u8,103u8,54u8,47u8,47u8,34u8,12u8,81u8, - 103u8,252u8,92u8,235u8,218u8,117u8,161u8,9u8,131u8,6u8,91u8,135u8,202u8,186u8,103u8,109u8,148u8,221u8,173u8,65u8, - 113u8,38u8,241u8,52u8,196u8,131u8,186u8,12u8,65u8,238u8,183u8,203u8,42u8,104u8,255u8,157u8,199u8,174u8,110u8,231u8, - 126u8,163u8,51u8,237u8,176u8,149u8,7u8,169u8,83u8,128u8,99u8,118u8,119u8,218u8,250u8,195u8,164u8,244u8,223u8,60u8, - 204u8,201u8,139u8,186u8,38u8,191u8,241u8,235u8,105u8,130u8,179u8,186u8,100u8,82u8,111u8,141u8,227u8,179u8,196u8,76u8, - 12u8,158u8,228u8,69u8,230u8,176u8,210u8,69u8,76u8,195u8,174u8,208u8,35u8,94u8,93u8,177u8,110u8,101u8,105u8,208u8, - 117u8,24u8,134u8,25u8,110u8,186u8,6u8,155u8,163u8,219u8,39u8,54u8,243u8,109u8,219u8,54u8,251u8,242u8,29u8,4u8, - 127u8,154u8,59u8,225u8,50u8,19u8,239u8,86u8,232u8,208u8,199u8,63u8,78u8,118u8,131u8,122u8,116u8,210u8,211u8,24u8, - 120u8,248u8,175u8,249u8,113u8,93u8,230u8,2u8,71u8,90u8,249u8,210u8,23u8,184u8,220u8,196u8,219u8,185u8,153u8,212u8, - 160u8,166u8,103u8,45u8,179u8,65u8,91u8,183u8,222u8,124u8,7u8,78u8,183u8,78u8,210u8,131u8,182u8,110u8,235u8,82u8, - 33u8,56u8,61u8,251u8,242u8,33u8,180u8,130u8,194u8,159u8,37u8,193u8,5u8,134u8,155u8,42u8,161u8,173u8,107u8,35u8, - 129u8,130u8,211u8,161u8,202u8,162u8,208u8,214u8,143u8,39u8,183u8,130u8,211u8,155u8,157u8,96u8,161u8,173u8,79u8,55u8, - 237u8,130u8,211u8,165u8,149u8,123u8,161u8,173u8,199u8,154u8,140u8,12u8,78u8,183u8,158u8,180u8,12u8,77u8,93u8,127u8, - 210u8,89u8,156u8,206u8,57u8,70u8,0u8,191u8,34u8,7u8,108u8,64u8,77u8,176u8,39u8,14u8,200u8,219u8,174u8,201u8, - 195u8,19u8,241u8,134u8,6u8,46u8,182u8,161u8,117u8,25u8,218u8,13u8,75u8,22u8,204u8,177u8,160u8,172u8,119u8,118u8, - 166u8,246u8,250u8,235u8,186u8,46u8,87u8,33u8,36u8,215u8,23u8,188u8,52u8,94u8,249u8,149u8,70u8,67u8,135u8,124u8, - 177u8,228u8,41u8,225u8,49u8,223u8,251u8,46u8,79u8,183u8,29u8,51u8,34u8,222u8,143u8,254u8,183u8,9u8,182u8,255u8, - 166u8,251u8,41u8,202u8,36u8,232u8,120u8,50u8,119u8,130u8,59u8,101u8,33u8,116u8,102u8,158u8,184u8,201u8,51u8,58u8, - 111u8,131u8,215u8,10u8,240u8,192u8,29u8,65u8,71u8,70u8,113u8,232u8,195u8,182u8,127u8,217u8,105u8,60u8,236u8,53u8, - 228u8,70u8,236u8,219u8,55u8,91u8,11u8,27u8,24u8,60u8,250u8,165u8,125u8,134u8,246u8,172u8,196u8,53u8,54u8,180u8, - 224u8,118u8,127u8,187u8,227u8,129u8,216u8,79u8,99u8,60u8,208u8,73u8,56u8,6u8,186u8,198u8,33u8,103u8,207u8,41u8, - 155u8,255u8,245u8,40u8,76u8,155u8,2u8,93u8,115u8,49u8,250u8,162u8,19u8,53u8,75u8,37u8,159u8,105u8,247u8,88u8, - 237u8,235u8,247u8,244u8,43u8,78u8,117u8,120u8,252u8,158u8,234u8,7u8,55u8,102u8,3u8,208u8,195u8,84u8,141u8,66u8, - 25u8,162u8,150u8,130u8,63u8,104u8,250u8,135u8,146u8,87u8,228u8,208u8,71u8,164u8,180u8,12u8,137u8,117u8,178u8,107u8, - 35u8,120u8,58u8,28u8,44u8,66u8,244u8,148u8,43u8,25u8,54u8,233u8,225u8,48u8,45u8,134u8,243u8,148u8,28u8,128u8, - 180u8,232u8,53u8,116u8,236u8,225u8,1u8,108u8,102u8,138u8,54u8,180u8,6u8,56u8,87u8,247u8,157u8,194u8,72u8,193u8, - 80u8,82u8,119u8,247u8,98u8,163u8,59u8,187u8,17u8,172u8,59u8,108u8,239u8,97u8,217u8,14u8,225u8,176u8,68u8,185u8, - 105u8,5u8,210u8,169u8,56u8,209u8,120u8,2u8,10u8,231u8,232u8,138u8,135u8,21u8,211u8,221u8,126u8,150u8,211u8,93u8, - 164u8,190u8,230u8,82u8,249u8,188u8,82u8,190u8,28u8,230u8,189u8,91u8,177u8,44u8,117u8,254u8,13u8,144u8,87u8,132u8, - 59u8,236u8,248u8,211u8,10u8,11u8,203u8,12u8,233u8,92u8,207u8,16u8,63u8,178u8,192u8,231u8,126u8,101u8,246u8,130u8, - 139u8,230u8,47u8,208u8,69u8,134u8,226u8,103u8,119u8,124u8,125u8,27u8,116u8,43u8,199u8,7u8,52u8,5u8,241u8,170u8, - 79u8,193u8,128u8,126u8,175u8,110u8,155u8,52u8,55u8,186u8,198u8,157u8,238u8,7u8,143u8,130u8,182u8,16u8,81u8,211u8, - 190u8,228u8,159u8,214u8,115u8,223u8,66u8,23u8,196u8,21u8,158u8,189u8,44u8,81u8,85u8,179u8,16u8,61u8,62u8,240u8, - 92u8,134u8,204u8,27u8,21u8,55u8,221u8,73u8,103u8,70u8,86u8,157u8,81u8,242u8,145u8,178u8,26u8,50u8,7u8,114u8, - 206u8,164u8,101u8,70u8,199u8,96u8,192u8,159u8,151u8,121u8,112u8,153u8,80u8,22u8,107u8,150u8,183u8,242u8,140u8,209u8, - 232u8,192u8,68u8,33u8,29u8,117u8,92u8,56u8,122u8,209u8,69u8,60u8,141u8,40u8,203u8,35u8,52u8,244u8,237u8,252u8, - 67u8,123u8,147u8,69u8,231u8,194u8,35u8,28u8,89u8,56u8,0u8,217u8,234u8,236u8,185u8,99u8,13u8,23u8,215u8,189u8, - 54u8,136u8,29u8,83u8,173u8,56u8,156u8,89u8,243u8,107u8,9u8,236u8,177u8,115u8,88u8,58u8,249u8,60u8,25u8,27u8, - 101u8,249u8,85u8,181u8,164u8,162u8,78u8,41u8,171u8,78u8,83u8,171u8,91u8,28u8,130u8,171u8,239u8,51u8,238u8,42u8, - 15u8,63u8,113u8,111u8,161u8,172u8,220u8,93u8,29u8,207u8,60u8,106u8,26u8,94u8,136u8,9u8,67u8,180u8,85u8,192u8, - 130u8,91u8,245u8,174u8,112u8,140u8,158u8,54u8,116u8,232u8,236u8,160u8,201u8,184u8,247u8,226u8,153u8,41u8,140u8,48u8, - 52u8,193u8,23u8,66u8,204u8,223u8,50u8,244u8,100u8,233u8,233u8,81u8,234u8,2u8,142u8,170u8,179u8,6u8,216u8,19u8, - 40u8,2u8,34u8,6u8,240u8,95u8,196u8,104u8,240u8,146u8,189u8,172u8,25u8,23u8,169u8,250u8,66u8,64u8,181u8,239u8, - 25u8,207u8,194u8,86u8,7u8,91u8,195u8,64u8,213u8,226u8,139u8,187u8,211u8,197u8,93u8,215u8,24u8,127u8,187u8,246u8, - 206u8,119u8,205u8,203u8,114u8,61u8,116u8,212u8,89u8,33u8,54u8,6u8,214u8,213u8,133u8,70u8,59u8,153u8,87u8,80u8, - 120u8,77u8,101u8,42u8,185u8,143u8,107u8,25u8,227u8,198u8,25u8,167u8,51u8,102u8,176u8,189u8,5u8,171u8,86u8,173u8, - 35u8,118u8,179u8,185u8,203u8,74u8,17u8,228u8,1u8,191u8,219u8,70u8,100u8,170u8,49u8,17u8,173u8,193u8,68u8,230u8, - 61u8,147u8,96u8,235u8,37u8,78u8,34u8,180u8,59u8,247u8,231u8,143u8,27u8,56u8,127u8,120u8,245u8,253u8,101u8,78u8, - 29u8,202u8,22u8,66u8,39u8,15u8,67u8,239u8,102u8,58u8,182u8,202u8,13u8,98u8,101u8,186u8,117u8,182u8,91u8,171u8, - 162u8,118u8,189u8,163u8,72u8,139u8,223u8,227u8,117u8,93u8,112u8,23u8,214u8,243u8,217u8,206u8,225u8,109u8,254u8,142u8, - 132u8,80u8,45u8,103u8,107u8,233u8,135u8,137u8,40u8,221u8,49u8,174u8,230u8,130u8,153u8,213u8,255u8,50u8,85u8,188u8, - 134u8,198u8,128u8,136u8,54u8,194u8,177u8,238u8,132u8,237u8,7u8,93u8,7u8,122u8,118u8,167u8,237u8,202u8,159u8,134u8, - 32u8,60u8,133u8,72u8,190u8,160u8,31u8,183u8,88u8,185u8,225u8,126u8,208u8,164u8,0u8,108u8,55u8,122u8,175u8,214u8, - 248u8,66u8,215u8,234u8,15u8,159u8,189u8,211u8,66u8,189u8,251u8,127u8,187u8,179u8,191u8,52u8,141u8,9u8,20u8,90u8, - 196u8,251u8,191u8,222u8,215u8,223u8,131u8,97u8,117u8,19u8,135u8,251u8,184u8,72u8,208u8,6u8,67u8,56u8,16u8,75u8, - 68u8,166u8,36u8,177u8,233u8,85u8,24u8,212u8,246u8,81u8,36u8,195u8,116u8,134u8,201u8,234u8,53u8,142u8,184u8,152u8, - 55u8,172u8,217u8,167u8,85u8,164u8,107u8,148u8,204u8,242u8,50u8,173u8,248u8,130u8,157u8,193u8,66u8,107u8,13u8,253u8, - 78u8,158u8,12u8,198u8,249u8,63u8,97u8,49u8,171u8,154u8,218u8,248u8,253u8,104u8,94u8,86u8,223u8,195u8,121u8,44u8, - 25u8,87u8,148u8,113u8,102u8,52u8,167u8,130u8,35u8,5u8,186u8,155u8,83u8,100u8,43u8,138u8,158u8,126u8,91u8,65u8, - 10u8,65u8,243u8,30u8,166u8,128u8,238u8,18u8,54u8,38u8,54u8,46u8,220u8,33u8,54u8,207u8,37u8,80u8,96u8,243u8, - 6u8,239u8,249u8,163u8,3u8,79u8,249u8,180u8,180u8,54u8,231u8,24u8,239u8,77u8,69u8,197u8,206u8,115u8,120u8,167u8, - 154u8,157u8,80u8,172u8,106u8,9u8,102u8,97u8,37u8,75u8,173u8,126u8,9u8,85u8,75u8,3u8,221u8,189u8,194u8,117u8, - 3u8,10u8,87u8,131u8,110u8,179u8,140u8,218u8,117u8,218u8,92u8,90u8,138u8,149u8,209u8,54u8,202u8,218u8,116u8,169u8, - 97u8,227u8,96u8,69u8,125u8,49u8,206u8,158u8,229u8,76u8,28u8,220u8,153u8,198u8,102u8,41u8,161u8,118u8,149u8,100u8, - 46u8,64u8,88u8,69u8,12u8,94u8,101u8,68u8,21u8,195u8,32u8,229u8,20u8,11u8,129u8,166u8,249u8,92u8,21u8,157u8, - 214u8,224u8,195u8,11u8,129u8,174u8,125u8,215u8,109u8,35u8,111u8,86u8,143u8,4u8,97u8,144u8,142u8,175u8,34u8,2u8, - 189u8,112u8,15u8,178u8,7u8,177u8,35u8,134u8,157u8,88u8,97u8,94u8,113u8,204u8,14u8,34u8,238u8,115u8,183u8,161u8, - 4u8,139u8,35u8,86u8,53u8,218u8,169u8,247u8,160u8,233u8,13u8,49u8,112u8,199u8,32u8,21u8,211u8,33u8,99u8,114u8, - 136u8,232u8,249u8,202u8,152u8,245u8,253u8,151u8,0u8,236u8,114u8,65u8,86u8,76u8,208u8,203u8,166u8,213u8,219u8,54u8, - 104u8,20u8,88u8,96u8,77u8,121u8,166u8,133u8,172u8,34u8,235u8,11u8,92u8,218u8,169u8,213u8,47u8,111u8,162u8,176u8, - 111u8,143u8,77u8,185u8,96u8,229u8,203u8,189u8,51u8,145u8,214u8,40u8,159u8,126u8,171u8,173u8,102u8,75u8,185u8,52u8, - 170u8,142u8,69u8,109u8,217u8,109u8,143u8,172u8,37u8,120u8,150u8,80u8,245u8,92u8,48u8,192u8,241u8,107u8,8u8,209u8, - 90u8,142u8,171u8,95u8,66u8,88u8,69u8,191u8,106u8,24u8,158u8,121u8,177u8,190u8,98u8,38u8,183u8,242u8,75u8,99u8, - 229u8,48u8,239u8,199u8,247u8,90u8,132u8,175u8,35u8,195u8,198u8,130u8,138u8,253u8,207u8,145u8,191u8,173u8,234u8,88u8, - 221u8,253u8,12u8,125u8,19u8,7u8,230u8,213u8,43u8,225u8,75u8,50u8,27u8,175u8,99u8,137u8,73u8,233u8,158u8,92u8, - 217u8,159u8,129u8,34u8,221u8,145u8,27u8,185u8,206u8,14u8,166u8,235u8,87u8,123u8,176u8,84u8,135u8,236u8,222u8,215u8, - 172u8,203u8,176u8,50u8,94u8,114u8,59u8,244u8,103u8,56u8,19u8,161u8,129u8,5u8,213u8,7u8,209u8,74u8,68u8,3u8, - 113u8,207u8,59u8,184u8,115u8,193u8,212u8,96u8,147u8,94u8,75u8,175u8,122u8,225u8,11u8,223u8,69u8,132u8,157u8,120u8, - 100u8,219u8,201u8,150u8,175u8,37u8,58u8,1u8,108u8,30u8,94u8,168u8,29u8,118u8,29u8,155u8,181u8,193u8,66u8,99u8, - 243u8,106u8,220u8,18u8,155u8,110u8,81u8,236u8,153u8,209u8,53u8,135u8,234u8,177u8,67u8,75u8,191u8,39u8,228u8,142u8, - 127u8,42u8,75u8,19u8,186u8,223u8,229u8,209u8,164u8,34u8,95u8,89u8,148u8,175u8,231u8,54u8,196u8,216u8,118u8,55u8, - 162u8,65u8,71u8,131u8,235u8,59u8,14u8,47u8,192u8,130u8,152u8,157u8,13u8,35u8,16u8,180u8,252u8,156u8,55u8,205u8, - 151u8,58u8,243u8,156u8,58u8,236u8,108u8,201u8,41u8,218u8,228u8,249u8,102u8,113u8,96u8,59u8,236u8,65u8,138u8,182u8, - 238u8,148u8,217u8,2u8,105u8,150u8,227u8,111u8,85u8,16u8,245u8,5u8,157u8,222u8,179u8,112u8,85u8,186u8,167u8,137u8, - 127u8,211u8,7u8,77u8,204u8,219u8,216u8,49u8,135u8,115u8,183u8,114u8,223u8,80u8,237u8,80u8,23u8,166u8,45u8,230u8, - 43u8,27u8,93u8,151u8,189u8,234u8,78u8,223u8,62u8,222u8,170u8,149u8,91u8,250u8,186u8,24u8,107u8,77u8,136u8,136u8, - 4u8,236u8,178u8,188u8,82u8,247u8,122u8,191u8,54u8,5u8,127u8,227u8,84u8,234u8,82u8,155u8,1u8,221u8,197u8,169u8, - 109u8,249u8,99u8,135u8,39u8,152u8,193u8,196u8,16u8,167u8,140u8,215u8,215u8,67u8,46u8,77u8,197u8,192u8,52u8,131u8, - 28u8,255u8,196u8,91u8,192u8,194u8,111u8,150u8,91u8,150u8,198u8,172u8,16u8,144u8,149u8,8u8,202u8,230u8,106u8,98u8, - 205u8,181u8,194u8,186u8,122u8,31u8,255u8,74u8,68u8,165u8,178u8,87u8,250u8,114u8,255u8,131u8,253u8,8u8,237u8,77u8, - 243u8,25u8,93u8,168u8,37u8,195u8,4u8,173u8,135u8,187u8,239u8,78u8,215u8,100u8,102u8,255u8,152u8,202u8,44u8,195u8, - 104u8,84u8,151u8,178u8,212u8,189u8,180u8,122u8,125u8,150u8,204u8,53u8,251u8,161u8,210u8,199u8,29u8,199u8,233u8,132u8, - 44u8,238u8,103u8,210u8,64u8,153u8,163u8,59u8,171u8,86u8,41u8,18u8,173u8,204u8,108u8,52u8,180u8,114u8,193u8,120u8, - 104u8,73u8,192u8,210u8,42u8,250u8,28u8,83u8,150u8,99u8,83u8,101u8,77u8,88u8,91u8,44u8,183u8,130u8,5u8,197u8, - 207u8,197u8,90u8,227u8,104u8,5u8,70u8,238u8,162u8,165u8,47u8,107u8,41u8,192u8,94u8,143u8,117u8,171u8,117u8,6u8, - 54u8,190u8,50u8,109u8,57u8,70u8,116u8,28u8,124u8,15u8,166u8,222u8,50u8,250u8,35u8,185u8,234u8,25u8,109u8,172u8, - 64u8,127u8,223u8,53u8,145u8,185u8,38u8,189u8,91u8,14u8,99u8,152u8,89u8,99u8,167u8,166u8,133u8,232u8,71u8,179u8, - 63u8,107u8,103u8,91u8,174u8,88u8,245u8,209u8,97u8,215u8,236u8,97u8,181u8,81u8,195u8,192u8,197u8,153u8,64u8,89u8, - 165u8,150u8,100u8,230u8,254u8,16u8,50u8,147u8,27u8,249u8,202u8,25u8,222u8,45u8,75u8,215u8,160u8,226u8,213u8,104u8, - 180u8,21u8,249u8,121u8,120u8,237u8,7u8,141u8,166u8,241u8,99u8,202u8,43u8,225u8,240u8,43u8,51u8,169u8,156u8,145u8, - 153u8,116u8,23u8,43u8,210u8,232u8,121u8,177u8,120u8,118u8,49u8,4u8,225u8,8u8,248u8,214u8,16u8,171u8,136u8,112u8, - 243u8,186u8,157u8,206u8,235u8,145u8,170u8,119u8,189u8,214u8,164u8,96u8,249u8,217u8,63u8,55u8,75u8,174u8,88u8,0u8, - 184u8,236u8,221u8,41u8,15u8,28u8,222u8,182u8,126u8,198u8,162u8,192u8,71u8,69u8,77u8,114u8,6u8,94u8,82u8,175u8, - 37u8,183u8,12u8,235u8,96u8,167u8,6u8,125u8,131u8,255u8,251u8,63u8,15u8,190u8,179u8,22u8,200u8,19u8,177u8,254u8, - 113u8,196u8,147u8,5u8,186u8,103u8,225u8,121u8,53u8,126u8,214u8,59u8,62u8,122u8,179u8,31u8,253u8,109u8,255u8,112u8, - 255u8,213u8,193u8,222u8,193u8,238u8,241u8,63u8,162u8,227u8,253u8,147u8,253u8,83u8,176u8,242u8,247u8,253u8,185u8,105u8, - 102u8,120u8,251u8,193u8,142u8,204u8,163u8,36u8,75u8,147u8,17u8,222u8,187u8,193u8,149u8,192u8,201u8,9u8,92u8,149u8, - 71u8,47u8,247u8,15u8,15u8,246u8,95u8,214u8,228u8,165u8,249u8,124u8,184u8,173u8,39u8,41u8,75u8,55u8,190u8,250u8, - 169u8,93u8,235u8,153u8,74u8,140u8,35u8,240u8,175u8,68u8,243u8,193u8,142u8,48u8,22u8,154u8,138u8,87u8,27u8,79u8, - 163u8,47u8,134u8,28u8,22u8,149u8,244u8,122u8,182u8,146u8,186u8,69u8,120u8,6u8,108u8,244u8,191u8,88u8,217u8,177u8, - 7u8,255u8,136u8,240u8,253u8,186u8,232u8,125u8,183u8,64u8,185u8,39u8,99u8,34u8,43u8,80u8,238u8,9u8,176u8,183u8, - 43u8,149u8,247u8,156u8,28u8,38u8,117u8,161u8,15u8,212u8,167u8,32u8,173u8,218u8,61u8,235u8,2u8,203u8,53u8,234u8, - 170u8,35u8,253u8,209u8,183u8,64u8,56u8,248u8,175u8,27u8,120u8,82u8,163u8,205u8,24u8,141u8,96u8,206u8,26u8,74u8, - 47u8,168u8,160u8,240u8,158u8,240u8,159u8,80u8,239u8,102u8,49u8,226u8,213u8,85u8,14u8,146u8,98u8,171u8,50u8,242u8, - 249u8,58u8,94u8,45u8,209u8,250u8,120u8,79u8,173u8,25u8,180u8,133u8,245u8,135u8,222u8,249u8,122u8,143u8,124u8,8u8, - 185u8,54u8,81u8,163u8,229u8,163u8,213u8,57u8,89u8,67u8,30u8,218u8,38u8,218u8,17u8,114u8,114u8,113u8,222u8,32u8, - 111u8,172u8,225u8,136u8,20u8,81u8,226u8,93u8,117u8,91u8,189u8,187u8,183u8,119u8,244u8,203u8,225u8,105u8,244u8,122u8, - 247u8,36u8,58u8,60u8,138u8,16u8,54u8,70u8,230u8,55u8,131u8,178u8,188u8,183u8,237u8,203u8,80u8,148u8,88u8,75u8, - 55u8,170u8,242u8,205u8,159u8,54u8,241u8,240u8,232u8,84u8,249u8,187u8,52u8,208u8,156u8,217u8,157u8,173u8,70u8,82u8, - 233u8,18u8,202u8,27u8,150u8,13u8,175u8,48u8,117u8,33u8,186u8,84u8,15u8,177u8,102u8,161u8,74u8,151u8,131u8,161u8, - 113u8,165u8,246u8,21u8,173u8,48u8,159u8,96u8,120u8,176u8,235u8,64u8,166u8,43u8,137u8,50u8,35u8,178u8,200u8,54u8, - 143u8,90u8,169u8,214u8,49u8,171u8,94u8,49u8,203u8,177u8,86u8,9u8,175u8,135u8,199u8,94u8,176u8,210u8,39u8,44u8, - 239u8,173u8,8u8,204u8,27u8,231u8,152u8,102u8,137u8,69u8,74u8,205u8,113u8,79u8,240u8,60u8,126u8,118u8,165u8,82u8, - 60u8,91u8,86u8,41u8,230u8,203u8,70u8,14u8,68u8,211u8,28u8,99u8,137u8,220u8,196u8,151u8,245u8,2u8,97u8,1u8, - 7u8,142u8,45u8,174u8,233u8,125u8,41u8,54u8,193u8,165u8,164u8,160u8,142u8,54u8,205u8,144u8,171u8,239u8,4u8,160u8, - 105u8,45u8,75u8,193u8,238u8,79u8,231u8,176u8,32u8,19u8,79u8,177u8,94u8,89u8,186u8,169u8,120u8,22u8,159u8,165u8, - 147u8,180u8,186u8,242u8,196u8,241u8,105u8,57u8,178u8,76u8,36u8,87u8,137u8,17u8,203u8,120u8,82u8,209u8,9u8,9u8, - 240u8,24u8,45u8,143u8,34u8,53u8,167u8,157u8,215u8,139u8,87u8,128u8,16u8,149u8,123u8,144u8,195u8,137u8,79u8,173u8, - 21u8,8u8,60u8,231u8,241u8,228u8,156u8,179u8,113u8,111u8,65u8,172u8,62u8,148u8,48u8,207u8,33u8,172u8,139u8,58u8, - 148u8,135u8,51u8,45u8,75u8,152u8,40u8,47u8,106u8,16u8,145u8,200u8,124u8,197u8,210u8,163u8,173u8,73u8,168u8,183u8, - 37u8,254u8,234u8,46u8,180u8,218u8,51u8,140u8,246u8,4u8,230u8,159u8,208u8,191u8,123u8,18u8,244u8,253u8,150u8,164u8, - 163u8,139u8,38u8,194u8,170u8,77u8,98u8,103u8,23u8,43u8,64u8,62u8,0u8,157u8,129u8,121u8,115u8,171u8,202u8,163u8, - 179u8,171u8,10u8,235u8,20u8,116u8,105u8,45u8,220u8,110u8,227u8,25u8,186u8,23u8,178u8,240u8,0u8,236u8,45u8,180u8, - 59u8,211u8,179u8,97u8,17u8,232u8,141u8,52u8,160u8,246u8,75u8,152u8,140u8,251u8,140u8,220u8,111u8,205u8,48u8,100u8, - 42u8,153u8,35u8,176u8,15u8,131u8,88u8,62u8,228u8,84u8,54u8,39u8,27u8,3u8,31u8,170u8,120u8,58u8,122u8,116u8, - 185u8,102u8,198u8,63u8,206u8,172u8,168u8,174u8,71u8,197u8,11u8,92u8,1u8,50u8,78u8,177u8,194u8,21u8,43u8,41u8, - 194u8,177u8,215u8,200u8,39u8,103u8,34u8,154u8,139u8,208u8,62u8,212u8,227u8,216u8,250u8,147u8,142u8,102u8,205u8,240u8, - 50u8,188u8,194u8,78u8,118u8,223u8,156u8,118u8,130u8,114u8,123u8,70u8,55u8,238u8,112u8,170u8,243u8,145u8,48u8,48u8, - 147u8,222u8,217u8,124u8,65u8,44u8,209u8,206u8,122u8,103u8,229u8,184u8,35u8,75u8,9u8,179u8,120u8,214u8,120u8,60u8, - 115u8,3u8,175u8,105u8,214u8,69u8,113u8,148u8,86u8,63u8,48u8,250u8,39u8,11u8,9u8,188u8,79u8,43u8,158u8,214u8, - 119u8,18u8,167u8,83u8,236u8,68u8,212u8,52u8,215u8,2u8,214u8,244u8,129u8,61u8,153u8,35u8,252u8,65u8,74u8,204u8, - 253u8,75u8,88u8,102u8,245u8,52u8,171u8,15u8,76u8,160u8,232u8,48u8,107u8,130u8,151u8,205u8,174u8,93u8,17u8,161u8, - 137u8,182u8,14u8,124u8,218u8,163u8,137u8,213u8,18u8,94u8,189u8,229u8,66u8,170u8,50u8,243u8,44u8,158u8,3u8,112u8, - 50u8,64u8,85u8,116u8,169u8,196u8,236u8,187u8,167u8,209u8,238u8,203u8,183u8,7u8,135u8,125u8,83u8,223u8,36u8,207u8, - 202u8,230u8,162u8,92u8,13u8,158u8,149u8,109u8,234u8,99u8,131u8,116u8,109u8,210u8,26u8,165u8,143u8,163u8,163u8,129u8, - 185u8,179u8,238u8,234u8,151u8,190u8,148u8,211u8,123u8,87u8,144u8,220u8,100u8,250u8,161u8,5u8,61u8,73u8,121u8,202u8, - 186u8,174u8,94u8,164u8,132u8,23u8,210u8,135u8,212u8,2u8,113u8,163u8,19u8,181u8,139u8,196u8,161u8,140u8,156u8,153u8, - 63u8,125u8,220u8,119u8,188u8,83u8,110u8,254u8,146u8,212u8,152u8,239u8,50u8,46u8,9u8,70u8,44u8,155u8,6u8,136u8, - 22u8,23u8,86u8,31u8,40u8,154u8,212u8,89u8,43u8,35u8,187u8,121u8,87u8,182u8,135u8,188u8,207u8,141u8,216u8,213u8, - 237u8,202u8,102u8,224u8,174u8,89u8,165u8,229u8,84u8,11u8,34u8,201u8,184u8,15u8,174u8,80u8,179u8,108u8,67u8,180u8, - 205u8,74u8,125u8,217u8,61u8,237u8,20u8,234u8,46u8,212u8,27u8,194u8,46u8,22u8,43u8,76u8,134u8,104u8,34u8,167u8, - 110u8,69u8,30u8,2u8,213u8,96u8,217u8,82u8,157u8,115u8,183u8,70u8,245u8,174u8,26u8,225u8,216u8,76u8,196u8,12u8, - 189u8,83u8,17u8,184u8,102u8,45u8,195u8,171u8,221u8,235u8,150u8,252u8,38u8,76u8,106u8,41u8,233u8,253u8,103u8,55u8, - 67u8,210u8,3u8,191u8,25u8,184u8,197u8,164u8,235u8,59u8,237u8,118u8,235u8,169u8,197u8,206u8,228u8,41u8,102u8,98u8, - 85u8,39u8,174u8,48u8,195u8,0u8,70u8,19u8,176u8,218u8,70u8,120u8,112u8,102u8,62u8,216u8,227u8,2u8,52u8,172u8, - 203u8,188u8,248u8,131u8,85u8,202u8,75u8,216u8,150u8,136u8,132u8,85u8,219u8,139u8,180u8,183u8,85u8,30u8,129u8,20u8, - 120u8,111u8,91u8,201u8,156u8,23u8,170u8,105u8,89u8,141u8,182u8,182u8,166u8,113u8,117u8,129u8,81u8,184u8,32u8,111u8, - 235u8,6u8,3u8,8u8,149u8,85u8,0u8,210u8,150u8,69u8,205u8,176u8,66u8,108u8,59u8,193u8,96u8,221u8,248u8,179u8, - 141u8,116u8,55u8,120u8,75u8,218u8,144u8,32u8,84u8,80u8,84u8,159u8,129u8,170u8,57u8,76u8,167u8,49u8,102u8,47u8, - 110u8,234u8,158u8,178u8,102u8,71u8,187u8,111u8,209u8,192u8,34u8,70u8,216u8,88u8,95u8,247u8,140u8,1u8,79u8,151u8, - 30u8,197u8,169u8,47u8,178u8,247u8,230u8,224u8,213u8,43u8,49u8,222u8,230u8,224u8,201u8,230u8,83u8,185u8,148u8,224u8, - 42u8,137u8,139u8,78u8,189u8,177u8,34u8,37u8,114u8,214u8,79u8,254u8,186u8,33u8,58u8,217u8,92u8,15u8,70u8,241u8, - 85u8,217u8,220u8,11u8,86u8,63u8,216u8,61u8,61u8,58u8,70u8,240u8,158u8,254u8,114u8,18u8,137u8,24u8,37u8,38u8, - 201u8,36u8,184u8,183u8,23u8,234u8,196u8,108u8,188u8,177u8,88u8,227u8,131u8,67u8,179u8,249u8,227u8,58u8,212u8,240u8, - 25u8,72u8,231u8,179u8,158u8,141u8,154u8,74u8,121u8,20u8,7u8,12u8,183u8,252u8,135u8,78u8,172u8,126u8,244u8,54u8, - 99u8,22u8,76u8,36u8,215u8,243u8,73u8,50u8,50u8,82u8,73u8,210u8,37u8,1u8,68u8,195u8,57u8,156u8,189u8,166u8, - 246u8,244u8,66u8,133u8,219u8,161u8,129u8,135u8,193u8,143u8,128u8,228u8,97u8,128u8,56u8,17u8,6u8,85u8,49u8,7u8, - 171u8,49u8,254u8,78u8,136u8,31u8,10u8,252u8,183u8,207u8,37u8,222u8,32u8,84u8,177u8,236u8,235u8,134u8,240u8,243u8, - 187u8,66u8,59u8,50u8,84u8,116u8,239u8,134u8,131u8,210u8,13u8,137u8,132u8,25u8,83u8,248u8,162u8,24u8,79u8,63u8, - 35u8,127u8,125u8,38u8,19u8,172u8,236u8,187u8,237u8,197u8,75u8,52u8,53u8,49u8,63u8,189u8,82u8,25u8,162u8,203u8, - 13u8,86u8,146u8,97u8,217u8,2u8,212u8,23u8,128u8,206u8,47u8,22u8,44u8,3u8,211u8,26u8,69u8,209u8,181u8,186u8, - 138u8,127u8,173u8,220u8,11u8,219u8,41u8,20u8,37u8,87u8,31u8,214u8,137u8,154u8,210u8,243u8,166u8,92u8,44u8,133u8, - 114u8,7u8,247u8,114u8,14u8,185u8,223u8,55u8,195u8,96u8,3u8,112u8,254u8,189u8,249u8,246u8,241u8,51u8,237u8,26u8, - 247u8,122u8,155u8,94u8,7u8,136u8,47u8,0u8,5u8,152u8,57u8,131u8,45u8,39u8,155u8,79u8,153u8,42u8,88u8,59u8, - 164u8,248u8,18u8,174u8,198u8,115u8,10u8,18u8,68u8,39u8,249u8,197u8,209u8,201u8,74u8,50u8,103u8,214u8,80u8,48u8, - 170u8,138u8,245u8,150u8,172u8,100u8,231u8,46u8,105u8,161u8,122u8,118u8,208u8,204u8,195u8,170u8,220u8,78u8,125u8,73u8, - 131u8,188u8,57u8,237u8,85u8,186u8,55u8,43u8,181u8,26u8,231u8,89u8,120u8,142u8,136u8,152u8,91u8,35u8,168u8,190u8, - 48u8,64u8,232u8,131u8,117u8,191u8,115u8,24u8,188u8,125u8,0u8,214u8,160u8,221u8,90u8,231u8,141u8,48u8,207u8,155u8, - 214u8,188u8,54u8,223u8,226u8,79u8,53u8,218u8,137u8,149u8,47u8,204u8,208u8,57u8,234u8,157u8,40u8,150u8,168u8,253u8, - 161u8,74u8,201u8,148u8,150u8,111u8,153u8,157u8, - ]; - let chunk5 = vector[ - 208u8,221u8,46u8,194u8,213u8,187u8,78u8,173u8,196u8,219u8,171u8,146u8,104u8,223u8,7u8,155u8,69u8,118u8,12u8, - 127u8,53u8,174u8,68u8,0u8,128u8,68u8,84u8,164u8,119u8,40u8,26u8,164u8,27u8,70u8,125u8,103u8,73u8,216u8,122u8, - 161u8,183u8,48u8,235u8,23u8,75u8,88u8,97u8,114u8,253u8,134u8,130u8,0u8,13u8,175u8,58u8,200u8,19u8,46u8,78u8, - 222u8,55u8,202u8,13u8,91u8,67u8,3u8,128u8,254u8,188u8,254u8,113u8,16u8,114u8,75u8,52u8,251u8,109u8,99u8,211u8, - 172u8,137u8,60u8,96u8,207u8,55u8,54u8,31u8,155u8,207u8,55u8,216u8,243u8,205u8,199u8,79u8,66u8,61u8,216u8,27u8, - 30u8,14u8,6u8,131u8,254u8,251u8,186u8,132u8,20u8,152u8,175u8,47u8,163u8,132u8,117u8,104u8,158u8,182u8,34u8,103u8, - 61u8,170u8,237u8,34u8,2u8,43u8,26u8,180u8,188u8,223u8,240u8,188u8,87u8,115u8,183u8,94u8,122u8,111u8,88u8,218u8, - 124u8,119u8,150u8,228u8,15u8,46u8,222u8,248u8,219u8,170u8,239u8,234u8,43u8,95u8,71u8,131u8,230u8,62u8,140u8,79u8, - 27u8,186u8,217u8,232u8,222u8,205u8,70u8,191u8,57u8,103u8,23u8,199u8,78u8,239u8,36u8,67u8,255u8,160u8,239u8,155u8, - 214u8,71u8,191u8,65u8,199u8,198u8,169u8,226u8,17u8,158u8,167u8,234u8,23u8,227u8,109u8,243u8,99u8,176u8,89u8,211u8, - 174u8,126u8,222u8,244u8,91u8,232u8,235u8,253u8,189u8,183u8,18u8,92u8,115u8,102u8,52u8,118u8,178u8,243u8,164u8,114u8, - 210u8,206u8,81u8,98u8,30u8,38u8,139u8,240u8,49u8,155u8,96u8,33u8,16u8,215u8,25u8,195u8,180u8,125u8,247u8,31u8, - 36u8,196u8,37u8,143u8,33u8,22u8,4u8,231u8,246u8,77u8,107u8,189u8,189u8,76u8,160u8,229u8,217u8,81u8,95u8,164u8, - 199u8,45u8,188u8,67u8,151u8,29u8,3u8,115u8,20u8,218u8,72u8,148u8,54u8,145u8,167u8,182u8,146u8,249u8,176u8,201u8, - 72u8,200u8,5u8,22u8,55u8,245u8,171u8,182u8,61u8,183u8,27u8,243u8,144u8,27u8,98u8,178u8,148u8,117u8,54u8,39u8, - 29u8,41u8,78u8,141u8,76u8,138u8,20u8,208u8,64u8,254u8,21u8,192u8,127u8,121u8,214u8,21u8,35u8,41u8,74u8,136u8, - 33u8,12u8,102u8,182u8,117u8,121u8,89u8,205u8,47u8,184u8,209u8,163u8,99u8,148u8,163u8,49u8,152u8,82u8,111u8,91u8, - 213u8,172u8,162u8,18u8,16u8,104u8,134u8,255u8,231u8,51u8,35u8,71u8,12u8,187u8,188u8,140u8,210u8,17u8,94u8,0u8, - 193u8,85u8,190u8,187u8,232u8,255u8,1u8,1u8,29u8,209u8,205u8,0u8,69u8,134u8,162u8,151u8,147u8,56u8,242u8,247u8, - 30u8,96u8,151u8,15u8,160u8,79u8,46u8,60u8,194u8,192u8,7u8,144u8,49u8,88u8,142u8,18u8,207u8,214u8,107u8,113u8, - 49u8,90u8,255u8,116u8,251u8,224u8,118u8,67u8,187u8,219u8,98u8,205u8,129u8,35u8,148u8,91u8,140u8,161u8,67u8,118u8, - 254u8,182u8,60u8,154u8,43u8,223u8,254u8,223u8,226u8,180u8,146u8,183u8,192u8,114u8,237u8,120u8,173u8,137u8,208u8,182u8, - 158u8,2u8,196u8,215u8,130u8,195u8,92u8,230u8,53u8,66u8,6u8,6u8,198u8,51u8,176u8,207u8,95u8,37u8,85u8,59u8, - 98u8,232u8,227u8,50u8,36u8,3u8,52u8,225u8,9u8,218u8,215u8,236u8,37u8,162u8,80u8,78u8,102u8,249u8,240u8,162u8, - 119u8,51u8,251u8,37u8,246u8,105u8,227u8,203u8,216u8,167u8,19u8,69u8,162u8,8u8,113u8,160u8,82u8,44u8,75u8,32u8, - 18u8,156u8,219u8,128u8,231u8,158u8,38u8,115u8,85u8,231u8,160u8,96u8,126u8,2u8,83u8,247u8,94u8,164u8,14u8,214u8, - 44u8,27u8,162u8,168u8,106u8,130u8,160u8,94u8,176u8,166u8,73u8,119u8,104u8,94u8,27u8,90u8,102u8,9u8,19u8,29u8, - 83u8,172u8,124u8,75u8,238u8,44u8,16u8,57u8,172u8,46u8,219u8,64u8,79u8,201u8,181u8,37u8,232u8,53u8,4u8,55u8, - 0u8,206u8,65u8,7u8,87u8,76u8,128u8,227u8,34u8,181u8,119u8,149u8,255u8,145u8,64u8,15u8,60u8,91u8,189u8,91u8, - 151u8,161u8,219u8,214u8,168u8,109u8,209u8,47u8,17u8,122u8,18u8,10u8,56u8,235u8,119u8,156u8,121u8,195u8,105u8,88u8, - 99u8,223u8,156u8,238u8,216u8,58u8,156u8,244u8,248u8,170u8,95u8,241u8,211u8,79u8,203u8,108u8,252u8,141u8,19u8,199u8, - 141u8,110u8,247u8,43u8,76u8,103u8,15u8,28u8,17u8,135u8,160u8,204u8,255u8,4u8,55u8,22u8,46u8,25u8,168u8,136u8, - 74u8,100u8,119u8,58u8,31u8,28u8,207u8,39u8,112u8,61u8,41u8,92u8,63u8,172u8,146u8,3u8,132u8,17u8,211u8,36u8, - 206u8,200u8,44u8,32u8,23u8,160u8,237u8,134u8,183u8,86u8,1u8,230u8,97u8,196u8,61u8,52u8,66u8,6u8,57u8,170u8, - 81u8,58u8,62u8,209u8,143u8,27u8,158u8,83u8,58u8,152u8,52u8,198u8,148u8,233u8,124u8,93u8,120u8,220u8,97u8,83u8, - 245u8,241u8,203u8,238u8,136u8,214u8,49u8,139u8,93u8,189u8,226u8,124u8,70u8,39u8,180u8,218u8,219u8,82u8,175u8,246u8, - 216u8,120u8,68u8,88u8,160u8,195u8,13u8,127u8,5u8,149u8,151u8,250u8,157u8,245u8,121u8,14u8,138u8,55u8,79u8,196u8, - 111u8,150u8,70u8,174u8,215u8,160u8,165u8,144u8,114u8,87u8,170u8,8u8,11u8,148u8,252u8,208u8,133u8,133u8,79u8,212u8, - 185u8,203u8,83u8,189u8,240u8,227u8,66u8,232u8,2u8,161u8,17u8,169u8,169u8,30u8,109u8,110u8,44u8,131u8,248u8,143u8, - 90u8,131u8,102u8,189u8,226u8,169u8,7u8,184u8,155u8,30u8,190u8,80u8,23u8,45u8,70u8,244u8,48u8,171u8,26u8,66u8, - 143u8,220u8,80u8,140u8,109u8,31u8,94u8,19u8,235u8,164u8,220u8,107u8,12u8,25u8,75u8,139u8,107u8,86u8,122u8,245u8, - 33u8,98u8,157u8,194u8,31u8,152u8,21u8,214u8,21u8,147u8,87u8,53u8,98u8,144u8,149u8,178u8,242u8,48u8,102u8,133u8, - 155u8,107u8,9u8,178u8,175u8,139u8,159u8,33u8,248u8,144u8,119u8,112u8,60u8,102u8,144u8,100u8,16u8,100u8,194u8,76u8, - 241u8,24u8,48u8,92u8,10u8,145u8,182u8,249u8,232u8,241u8,51u8,209u8,132u8,9u8,49u8,47u8,178u8,24u8,76u8,70u8, - 216u8,60u8,77u8,91u8,230u8,2u8,128u8,245u8,150u8,219u8,224u8,213u8,51u8,122u8,230u8,170u8,193u8,72u8,244u8,248u8, - 153u8,163u8,189u8,216u8,185u8,22u8,141u8,179u8,118u8,109u8,181u8,9u8,42u8,128u8,203u8,51u8,78u8,26u8,30u8,44u8, - 186u8,92u8,172u8,105u8,187u8,208u8,198u8,89u8,31u8,133u8,65u8,107u8,171u8,101u8,177u8,206u8,25u8,137u8,33u8,158u8, - 111u8,149u8,11u8,162u8,79u8,62u8,47u8,224u8,98u8,94u8,224u8,207u8,111u8,9u8,229u8,83u8,196u8,60u8,138u8,2u8, - 151u8,54u8,25u8,126u8,136u8,10u8,76u8,212u8,10u8,203u8,119u8,192u8,11u8,214u8,106u8,105u8,76u8,66u8,246u8,183u8, - 0u8,54u8,117u8,196u8,164u8,13u8,11u8,147u8,92u8,44u8,234u8,92u8,182u8,164u8,6u8,137u8,188u8,143u8,107u8,49u8, - 234u8,6u8,247u8,121u8,197u8,144u8,27u8,124u8,123u8,144u8,227u8,6u8,148u8,137u8,94u8,2u8,73u8,86u8,62u8,234u8, - 13u8,128u8,105u8,246u8,209u8,164u8,194u8,202u8,31u8,37u8,232u8,251u8,127u8,5u8,136u8,172u8,49u8,94u8,15u8,17u8, - 180u8,237u8,198u8,55u8,0u8,214u8,235u8,113u8,81u8,67u8,2u8,182u8,29u8,113u8,69u8,185u8,78u8,58u8,35u8,57u8, - 122u8,110u8,93u8,181u8,163u8,26u8,64u8,26u8,173u8,91u8,214u8,216u8,77u8,101u8,94u8,8u8,216u8,250u8,90u8,116u8, - 67u8,130u8,13u8,214u8,5u8,245u8,109u8,4u8,178u8,171u8,204u8,254u8,228u8,7u8,2u8,169u8,163u8,75u8,15u8,186u8, - 81u8,59u8,232u8,70u8,211u8,160u8,82u8,15u8,30u8,152u8,180u8,249u8,155u8,147u8,244u8,87u8,120u8,243u8,51u8,53u8, - 18u8,75u8,34u8,60u8,228u8,53u8,17u8,80u8,21u8,29u8,7u8,3u8,58u8,59u8,248u8,75u8,36u8,172u8,117u8,95u8, - 147u8,107u8,115u8,214u8,109u8,194u8,102u8,201u8,122u8,10u8,121u8,19u8,137u8,132u8,203u8,150u8,195u8,69u8,91u8,6u8, - 245u8,198u8,196u8,180u8,11u8,33u8,211u8,186u8,248u8,79u8,199u8,170u8,101u8,105u8,212u8,66u8,196u8,107u8,28u8,73u8, - 77u8,127u8,91u8,41u8,146u8,12u8,202u8,171u8,153u8,241u8,162u8,203u8,183u8,135u8,113u8,232u8,137u8,20u8,69u8,158u8, - 236u8,189u8,237u8,228u8,233u8,193u8,8u8,59u8,136u8,200u8,76u8,19u8,221u8,184u8,147u8,203u8,34u8,160u8,49u8,219u8, - 159u8,124u8,43u8,236u8,111u8,47u8,125u8,79u8,203u8,47u8,89u8,255u8,229u8,119u8,176u8,146u8,192u8,93u8,10u8,166u8, - 18u8,138u8,211u8,201u8,188u8,72u8,122u8,49u8,220u8,165u8,224u8,5u8,251u8,8u8,89u8,44u8,124u8,187u8,190u8,190u8, - 190u8,23u8,162u8,77u8,37u8,230u8,140u8,243u8,36u8,153u8,140u8,27u8,239u8,104u8,235u8,98u8,220u8,104u8,223u8,49u8, - 51u8,50u8,195u8,190u8,136u8,157u8,92u8,104u8,224u8,235u8,94u8,230u8,118u8,143u8,104u8,91u8,232u8,122u8,213u8,235u8, - 58u8,89u8,115u8,167u8,246u8,222u8,110u8,87u8,75u8,245u8,162u8,249u8,207u8,131u8,247u8,234u8,151u8,245u8,247u8,161u8, - 117u8,141u8,127u8,123u8,251u8,251u8,120u8,133u8,251u8,155u8,229u8,145u8,110u8,218u8,250u8,230u8,55u8,249u8,189u8,241u8, - 227u8,93u8,237u8,240u8,147u8,21u8,238u8,240u8,20u8,164u8,249u8,52u8,30u8,94u8,144u8,29u8,233u8,126u8,171u8,117u8, - 122u8,14u8,131u8,159u8,55u8,180u8,13u8,31u8,220u8,209u8,142u8,163u8,195u8,252u8,192u8,216u8,241u8,102u8,207u8,237u8, - 229u8,113u8,65u8,68u8,127u8,185u8,34u8,236u8,30u8,27u8,124u8,216u8,240u8,243u8,147u8,175u8,14u8,5u8,200u8,187u8, - 12u8,158u8,234u8,40u8,32u8,66u8,159u8,238u8,81u8,192u8,131u8,2u8,131u8,193u8,45u8,226u8,192u8,198u8,173u8,224u8, - 192u8,60u8,211u8,114u8,82u8,126u8,83u8,136u8,80u8,19u8,215u8,213u8,131u8,93u8,190u8,35u8,116u8,89u8,72u8,47u8, - 216u8,88u8,74u8,47u8,32u8,135u8,118u8,199u8,211u8,218u8,216u8,229u8,186u8,205u8,237u8,219u8,209u8,23u8,245u8,48u8, - 247u8,128u8,217u8,113u8,238u8,86u8,154u8,213u8,64u8,25u8,20u8,86u8,14u8,165u8,205u8,107u8,105u8,79u8,18u8,62u8, - 234u8,252u8,195u8,43u8,47u8,143u8,230u8,34u8,227u8,239u8,157u8,65u8,174u8,139u8,219u8,62u8,129u8,150u8,129u8,248u8, - 214u8,20u8,214u8,167u8,43u8,4u8,185u8,80u8,82u8,172u8,203u8,207u8,155u8,131u8,249u8,34u8,55u8,178u8,3u8,22u8, - 175u8,214u8,182u8,93u8,62u8,135u8,237u8,5u8,246u8,206u8,104u8,254u8,215u8,245u8,117u8,243u8,193u8,224u8,26u8,187u8, - 26u8,90u8,238u8,255u8,204u8,221u8,187u8,113u8,179u8,168u8,180u8,119u8,117u8,153u8,14u8,37u8,203u8,192u8,20u8,13u8, - 163u8,28u8,62u8,198u8,186u8,23u8,102u8,26u8,156u8,85u8,248u8,118u8,223u8,161u8,115u8,182u8,110u8,22u8,237u8,234u8, - 18u8,189u8,164u8,88u8,10u8,125u8,131u8,45u8,239u8,167u8,235u8,137u8,125u8,240u8,58u8,53u8,139u8,129u8,212u8,91u8, - 253u8,166u8,201u8,127u8,2u8,210u8,141u8,170u8,162u8,198u8,12u8,207u8,81u8,86u8,235u8,205u8,168u8,242u8,223u8,40u8, - 31u8,47u8,248u8,216u8,239u8,144u8,179u8,34u8,15u8,222u8,187u8,247u8,130u8,197u8,16u8,215u8,187u8,116u8,18u8,88u8, - 137u8,71u8,137u8,22u8,71u8,245u8,165u8,248u8,21u8,172u8,226u8,226u8,204u8,232u8,250u8,102u8,110u8,204u8,44u8,7u8, - 29u8,216u8,233u8,56u8,96u8,23u8,240u8,204u8,115u8,72u8,121u8,121u8,15u8,47u8,226u8,236u8,60u8,241u8,120u8,122u8, - 175u8,212u8,99u8,230u8,214u8,33u8,113u8,139u8,98u8,203u8,244u8,145u8,189u8,23u8,96u8,247u8,2u8,236u8,94u8,128u8, - 45u8,37u8,192u8,238u8,253u8,227u8,239u8,204u8,193u8,80u8,8u8,11u8,59u8,248u8,163u8,155u8,208u8,64u8,143u8,206u8, - 44u8,199u8,138u8,7u8,18u8,154u8,84u8,102u8,87u8,193u8,114u8,237u8,238u8,33u8,182u8,58u8,105u8,16u8,6u8,90u8, - 37u8,71u8,22u8,174u8,186u8,136u8,128u8,224u8,162u8,97u8,22u8,95u8,233u8,133u8,241u8,72u8,61u8,187u8,89u8,241u8, - 128u8,127u8,84u8,173u8,191u8,59u8,147u8,28u8,178u8,44u8,96u8,99u8,203u8,154u8,52u8,93u8,119u8,45u8,118u8,66u8, - 103u8,250u8,159u8,167u8,32u8,178u8,121u8,196u8,162u8,181u8,24u8,53u8,135u8,15u8,157u8,79u8,12u8,214u8,255u8,85u8, - 139u8,28u8,151u8,249u8,221u8,102u8,113u8,42u8,143u8,28u8,98u8,164u8,181u8,197u8,171u8,57u8,218u8,160u8,197u8,196u8, - 61u8,43u8,90u8,203u8,96u8,253u8,94u8,64u8,123u8,4u8,180u8,202u8,64u8,248u8,89u8,203u8,104u8,143u8,84u8,94u8, - 78u8,94u8,155u8,229u8,74u8,89u8,238u8,89u8,167u8,103u8,68u8,243u8,109u8,142u8,237u8,158u8,141u8,45u8,180u8,41u8, - 248u8,154u8,63u8,212u8,70u8,184u8,19u8,77u8,193u8,51u8,169u8,155u8,209u8,26u8,140u8,48u8,27u8,190u8,215u8,32u8, - 214u8,116u8,8u8,3u8,157u8,72u8,97u8,201u8,44u8,16u8,49u8,197u8,60u8,241u8,177u8,69u8,141u8,117u8,140u8,209u8, - 90u8,117u8,224u8,211u8,66u8,112u8,52u8,124u8,207u8,244u8,85u8,247u8,173u8,119u8,62u8,224u8,58u8,78u8,101u8,58u8, - 115u8,161u8,188u8,206u8,90u8,208u8,161u8,6u8,28u8,153u8,77u8,83u8,160u8,14u8,11u8,196u8,205u8,25u8,15u8,82u8, - 201u8,52u8,189u8,76u8,70u8,99u8,126u8,120u8,83u8,96u8,99u8,102u8,207u8,152u8,181u8,249u8,186u8,15u8,56u8,106u8, - 63u8,33u8,164u8,55u8,97u8,167u8,119u8,215u8,210u8,91u8,211u8,100u8,182u8,61u8,201u8,153u8,236u8,30u8,213u8,27u8, - 52u8,10u8,117u8,235u8,173u8,155u8,243u8,45u8,102u8,137u8,54u8,60u8,64u8,1u8,120u8,49u8,227u8,246u8,49u8,243u8, - 24u8,229u8,40u8,72u8,142u8,163u8,208u8,75u8,86u8,142u8,225u8,187u8,68u8,202u8,51u8,54u8,252u8,218u8,114u8,190u8, - 176u8,134u8,39u8,172u8,9u8,211u8,159u8,236u8,236u8,2u8,173u8,29u8,219u8,226u8,145u8,122u8,213u8,96u8,250u8,112u8, - 209u8,203u8,174u8,155u8,81u8,119u8,177u8,86u8,34u8,221u8,189u8,168u8,93u8,212u8,172u8,33u8,134u8,42u8,140u8,34u8, - 73u8,167u8,251u8,123u8,253u8,247u8,94u8,255u8,189u8,215u8,127u8,239u8,245u8,223u8,47u8,85u8,255u8,109u8,84u8,104u8, - 189u8,160u8,231u8,204u8,178u8,212u8,177u8,74u8,38u8,202u8,166u8,124u8,185u8,78u8,241u8,27u8,82u8,163u8,214u8,190u8, - 54u8,253u8,216u8,87u8,121u8,218u8,150u8,35u8,61u8,181u8,93u8,237u8,196u8,99u8,65u8,252u8,23u8,118u8,6u8,17u8, - 81u8,25u8,242u8,196u8,66u8,213u8,93u8,68u8,60u8,62u8,133u8,242u8,163u8,6u8,203u8,43u8,116u8,232u8,243u8,215u8, - 3u8,245u8,253u8,186u8,234u8,189u8,138u8,127u8,175u8,226u8,223u8,171u8,248u8,247u8,42u8,254u8,231u8,173u8,226u8,47u8, - 104u8,187u8,54u8,117u8,23u8,158u8,249u8,23u8,198u8,208u8,217u8,242u8,10u8,180u8,246u8,47u8,89u8,47u8,95u8,76u8, - 169u8,38u8,199u8,205u8,219u8,81u8,162u8,97u8,168u8,206u8,74u8,243u8,192u8,224u8,252u8,157u8,182u8,189u8,179u8,30u8, - 187u8,98u8,29u8,246u8,70u8,238u8,246u8,219u8,221u8,15u8,55u8,215u8,215u8,215u8,159u8,45u8,232u8,126u8,24u8,103u8, - 120u8,230u8,181u8,46u8,128u8,226u8,113u8,133u8,197u8,156u8,196u8,18u8,211u8,50u8,146u8,17u8,161u8,163u8,123u8,7u8, - 129u8,111u8,199u8,65u8,224u8,96u8,58u8,77u8,70u8,41u8,236u8,58u8,214u8,196u8,17u8,24u8,176u8,86u8,119u8,23u8, - 171u8,185u8,106u8,33u8,130u8,174u8,40u8,156u8,184u8,155u8,206u8,122u8,139u8,14u8,160u8,53u8,145u8,161u8,252u8,85u8, - 121u8,79u8,31u8,119u8,78u8,31u8,11u8,150u8,65u8,104u8,164u8,28u8,239u8,151u8,38u8,21u8,117u8,77u8,104u8,189u8, - 238u8,239u8,108u8,157u8,252u8,255u8,224u8,239u8,141u8,166u8,122u8,8u8,43u8,203u8,241u8,106u8,56u8,100u8,202u8,4u8, - 87u8,202u8,211u8,146u8,95u8,133u8,173u8,83u8,117u8,149u8,130u8,213u8,30u8,205u8,101u8,250u8,249u8,207u8,211u8,213u8, - 114u8,37u8,137u8,48u8,111u8,224u8,108u8,122u8,152u8,124u8,20u8,14u8,173u8,2u8,188u8,148u8,157u8,8u8,93u8,88u8, - 191u8,222u8,204u8,86u8,95u8,136u8,7u8,234u8,210u8,96u8,215u8,141u8,14u8,44u8,209u8,181u8,72u8,120u8,32u8,178u8, - 200u8,217u8,183u8,199u8,157u8,79u8,137u8,117u8,8u8,3u8,212u8,120u8,14u8,75u8,185u8,93u8,223u8,215u8,245u8,27u8, - 243u8,252u8,173u8,91u8,230u8,198u8,103u8,71u8,23u8,43u8,204u8,211u8,117u8,239u8,142u8,204u8,149u8,40u8,204u8,99u8, - 197u8,66u8,105u8,248u8,173u8,155u8,34u8,143u8,72u8,205u8,141u8,173u8,241u8,94u8,155u8,186u8,215u8,166u8,58u8,105u8, - 83u8,134u8,54u8,131u8,229u8,247u8,30u8,61u8,166u8,252u8,158u8,50u8,105u8,154u8,96u8,52u8,148u8,9u8,244u8,44u8, - 145u8,87u8,70u8,202u8,12u8,60u8,120u8,244u8,120u8,205u8,95u8,174u8,202u8,86u8,204u8,110u8,79u8,47u8,195u8,188u8, - 110u8,168u8,45u8,124u8,13u8,17u8,46u8,247u8,234u8,197u8,93u8,171u8,23u8,111u8,108u8,98u8,56u8,75u8,44u8,26u8, - 224u8,251u8,136u8,73u8,5u8,251u8,107u8,215u8,66u8,4u8,11u8,58u8,77u8,34u8,116u8,253u8,155u8,22u8,146u8,55u8, - 105u8,215u8,35u8,41u8,123u8,111u8,205u8,187u8,183u8,230u8,117u8,176u8,230u8,233u8,124u8,97u8,197u8,54u8,188u8,38u8, - 254u8,241u8,69u8,209u8,147u8,130u8,2u8,5u8,209u8,221u8,83u8,207u8,183u8,67u8,61u8,72u8,38u8,13u8,84u8,16u8, - 16u8,66u8,8u8,250u8,89u8,33u8,237u8,44u8,221u8,248u8,142u8,8u8,235u8,175u8,75u8,17u8,22u8,230u8,242u8,142u8, - 204u8,4u8,149u8,81u8,58u8,54u8,132u8,150u8,65u8,127u8,247u8,130u8,235u8,27u8,35u8,61u8,82u8,78u8,13u8,252u8, - 208u8,105u8,13u8,227u8,251u8,36u8,37u8,94u8,80u8,176u8,95u8,5u8,202u8,109u8,162u8,229u8,188u8,213u8,34u8,252u8, - 22u8,74u8,132u8,250u8,229u8,36u8,59u8,67u8,23u8,194u8,179u8,36u8,75u8,198u8,233u8,48u8,141u8,139u8,43u8,51u8, - 201u8,217u8,109u8,37u8,180u8,186u8,1u8,34u8,89u8,54u8,195u8,213u8,42u8,221u8,20u8,172u8,252u8,86u8,230u8,225u8, - 182u8,187u8,179u8,175u8,181u8,69u8,245u8,14u8,10u8,52u8,226u8,96u8,240u8,197u8,37u8,91u8,243u8,226u8,160u8,145u8, - 100u8,237u8,30u8,17u8,63u8,7u8,68u8,236u8,156u8,253u8,237u8,150u8,209u8,181u8,27u8,86u8,113u8,220u8,193u8,234u8, - 84u8,70u8,138u8,246u8,175u8,17u8,145u8,8u8,176u8,55u8,133u8,77u8,75u8,69u8,44u8,44u8,137u8,21u8,186u8,9u8, - 70u8,111u8,238u8,107u8,71u8,38u8,24u8,153u8,64u8,240u8,219u8,203u8,33u8,212u8,230u8,224u8,124u8,158u8,139u8,181u8, - 107u8,144u8,12u8,56u8,53u8,119u8,139u8,90u8,197u8,107u8,56u8,173u8,184u8,92u8,169u8,69u8,84u8,100u8,9u8,22u8, - 43u8,192u8,0u8,133u8,100u8,228u8,41u8,206u8,185u8,138u8,251u8,229u8,238u8,165u8,220u8,184u8,15u8,106u8,83u8,214u8, - 251u8,26u8,28u8,19u8,13u8,119u8,204u8,73u8,134u8,162u8,203u8,155u8,228u8,86u8,211u8,56u8,139u8,207u8,147u8,41u8, - 188u8,137u8,138u8,124u8,146u8,220u8,44u8,99u8,218u8,101u8,251u8,254u8,86u8,142u8,249u8,149u8,202u8,190u8,229u8,216u8, - 21u8,221u8,53u8,192u8,30u8,192u8,168u8,243u8,106u8,252u8,172u8,119u8,246u8,61u8,52u8,125u8,121u8,244u8,246u8,123u8, - 139u8,165u8,217u8,91u8,86u8,203u8,214u8,240u8,45u8,50u8,169u8,13u8,15u8,210u8,97u8,68u8,11u8,190u8,143u8,216u8, - 209u8,167u8,231u8,111u8,205u8,217u8,219u8,134u8,203u8,87u8,23u8,158u8,196u8,230u8,117u8,39u8,177u8,185u8,146u8,220u8, - 152u8,117u8,129u8,150u8,182u8,172u8,248u8,166u8,232u8,128u8,187u8,82u8,195u8,70u8,127u8,94u8,20u8,177u8,128u8,0u8, - 31u8,212u8,33u8,122u8,155u8,4u8,31u8,152u8,56u8,254u8,109u8,139u8,240u8,99u8,164u8,3u8,91u8,92u8,235u8,117u8, - 35u8,23u8,217u8,145u8,133u8,212u8,131u8,188u8,72u8,207u8,193u8,252u8,49u8,249u8,250u8,116u8,131u8,214u8,72u8,21u8, - 129u8,131u8,150u8,240u8,95u8,95u8,172u8,143u8,13u8,17u8,104u8,123u8,189u8,152u8,22u8,218u8,226u8,170u8,171u8,135u8, - 140u8,131u8,16u8,17u8,15u8,84u8,98u8,157u8,172u8,68u8,165u8,144u8,136u8,87u8,45u8,104u8,253u8,188u8,231u8,178u8, - 95u8,4u8,151u8,245u8,242u8,28u8,22u8,176u8,203u8,174u8,252u8,57u8,66u8,34u8,46u8,173u8,89u8,142u8,25u8,28u8, - 205u8,26u8,183u8,76u8,124u8,85u8,191u8,98u8,137u8,175u8,13u8,90u8,140u8,53u8,148u8,175u8,52u8,113u8,58u8,157u8, - 129u8,42u8,51u8,141u8,129u8,153u8,79u8,146u8,236u8,188u8,186u8,232u8,61u8,0u8,3u8,86u8,145u8,95u8,114u8,166u8, - 254u8,188u8,14u8,47u8,95u8,184u8,236u8,99u8,13u8,87u8,107u8,84u8,73u8,171u8,231u8,193u8,98u8,110u8,245u8,108u8, - 120u8,217u8,221u8,26u8,92u8,215u8,155u8,192u8,225u8,38u8,240u8,40u8,206u8,70u8,249u8,212u8,14u8,153u8,107u8,183u8, - 254u8,61u8,1u8,235u8,223u8,171u8,197u8,174u8,108u8,106u8,184u8,211u8,60u8,139u8,231u8,213u8,5u8,8u8,156u8,127u8, - 174u8,230u8,118u8,166u8,134u8,57u8,209u8,43u8,90u8,236u8,151u8,207u8,181u8,62u8,15u8,134u8,213u8,145u8,107u8,196u8, - 2u8,199u8,132u8,42u8,97u8,68u8,166u8,35u8,122u8,233u8,119u8,44u8,139u8,243u8,130u8,38u8,30u8,211u8,76u8,168u8, - 52u8,173u8,102u8,50u8,53u8,232u8,44u8,66u8,31u8,64u8,134u8,225u8,136u8,212u8,93u8,179u8,16u8,108u8,137u8,242u8, - 142u8,253u8,173u8,96u8,254u8,244u8,177u8,66u8,183u8,102u8,68u8,114u8,110u8,138u8,119u8,2u8,47u8,255u8,18u8,205u8, - 95u8,180u8,102u8,157u8,96u8,53u8,7u8,153u8,119u8,84u8,24u8,68u8,236u8,63u8,35u8,253u8,4u8,119u8,157u8,234u8, - 217u8,99u8,175u8,241u8,36u8,6u8,107u8,53u8,225u8,234u8,122u8,207u8,220u8,3u8,207u8,104u8,110u8,121u8,69u8,181u8, - 130u8,86u8,106u8,124u8,84u8,44u8,145u8,192u8,22u8,6u8,217u8,124u8,42u8,66u8,105u8,233u8,247u8,81u8,146u8,229u8, - 116u8,71u8,199u8,159u8,112u8,240u8,42u8,40u8,90u8,229u8,2u8,96u8,159u8,170u8,116u8,54u8,185u8,138u8,224u8,43u8, - 214u8,113u8,24u8,116u8,169u8,40u8,32u8,199u8,53u8,134u8,236u8,247u8,249u8,42u8,62u8,125u8,247u8,255u8,215u8,79u8, - 160u8,92u8,84u8,77u8,1u8,0u8,0u8,0u8,7u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,157u8,8u8,31u8,139u8, - 8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,205u8,87u8,219u8,106u8,35u8,71u8,16u8,125u8,247u8,87u8,84u8,88u8, - 48u8,163u8,32u8,164u8,181u8,54u8,108u8,130u8,100u8,47u8,113u8,178u8,38u8,113u8,136u8,165u8,224u8,219u8,67u8,150u8, - 208u8,180u8,102u8,74u8,163u8,142u8,71u8,221u8,74u8,95u8,108u8,43u8,198u8,255u8,158u8,234u8,233u8,185u8,75u8,118u8, - 108u8,216u8,133u8,29u8,48u8,22u8,173u8,186u8,156u8,170u8,115u8,170u8,122u8,52u8,28u8,14u8,225u8,140u8,11u8,105u8, - 233u8,207u8,128u8,93u8,34u8,220u8,162u8,54u8,66u8,73u8,144u8,110u8,53u8,71u8,13u8,11u8,165u8,243u8,227u8,121u8, - 166u8,226u8,155u8,120u8,73u8,102u8,131u8,189u8,149u8,74u8,92u8,134u8,192u8,215u8,86u8,25u8,182u8,208u8,124u8,133u8, - 119u8,74u8,223u8,140u8,199u8,165u8,235u8,195u8,30u8,208u8,227u8,12u8,130u8,177u8,201u8,120u8,140u8,90u8,43u8,61u8, - 105u8,159u8,25u8,145u8,74u8,164u8,195u8,234u8,116u8,43u8,152u8,198u8,88u8,201u8,133u8,72u8,157u8,230u8,150u8,130u8, - 78u8,158u8,182u8,52u8,27u8,99u8,113u8,197u8,120u8,146u8,104u8,52u8,6u8,77u8,17u8,117u8,161u8,5u8,202u8,100u8, - 219u8,60u8,69u8,137u8,70u8,148u8,86u8,198u8,106u8,23u8,91u8,184u8,46u8,160u8,47u8,185u8,129u8,27u8,220u8,20u8, - 37u8,248u8,103u8,197u8,255u8,86u8,122u8,12u8,238u8,253u8,119u8,253u8,252u8,236u8,177u8,229u8,119u8,129u8,182u8,112u8, - 253u8,153u8,175u8,249u8,92u8,100u8,194u8,110u8,234u8,32u8,133u8,237u8,144u8,154u8,124u8,177u8,198u8,88u8,44u8,4u8, - 38u8,33u8,96u8,183u8,205u8,43u8,103u8,44u8,204u8,17u8,82u8,141u8,220u8,162u8,111u8,57u8,151u8,16u8,59u8,173u8, - 81u8,218u8,142u8,237u8,32u8,143u8,73u8,205u8,33u8,143u8,147u8,211u8,233u8,245u8,241u8,239u8,167u8,31u8,217u8,217u8, - 241u8,111u8,179u8,115u8,118u8,125u8,114u8,126u8,113u8,58u8,155u8,178u8,233u8,213u8,217u8,79u8,39u8,231u8,57u8,104u8, - 56u8,130u8,131u8,73u8,133u8,226u8,56u8,142u8,149u8,163u8,136u8,194u8,128u8,84u8,22u8,184u8,179u8,75u8,165u8,197u8, - 191u8,132u8,202u8,42u8,2u8,118u8,131u8,148u8,152u8,190u8,35u8,158u8,101u8,138u8,173u8,68u8,211u8,217u8,37u8,59u8, - 190u8,186u8,252u8,117u8,118u8,126u8,250u8,231u8,201u8,199u8,50u8,244u8,104u8,82u8,87u8,56u8,147u8,217u8,6u8,98u8, - 158u8,101u8,20u8,45u8,113u8,90u8,200u8,20u8,138u8,86u8,15u8,42u8,155u8,63u8,220u8,60u8,19u8,102u8,137u8,65u8, - 106u8,101u8,211u8,3u8,209u8,193u8,106u8,237u8,45u8,226u8,40u8,144u8,215u8,131u8,133u8,147u8,32u8,164u8,176u8,130u8, - 103u8,4u8,51u8,234u8,146u8,9u8,251u8,65u8,72u8,253u8,210u8,136u8,21u8,189u8,202u8,1u8,246u8,26u8,52u8,118u8, - 69u8,50u8,30u8,115u8,250u8,167u8,45u8,235u8,132u8,236u8,166u8,232u8,21u8,21u8,230u8,82u8,80u8,183u8,200u8,172u8, - 234u8,154u8,244u8,171u8,66u8,30u8,74u8,181u8,116u8,208u8,192u8,99u8,111u8,82u8,69u8,161u8,54u8,252u8,34u8,110u8, - 11u8,29u8,67u8,21u8,5u8,120u8,193u8,77u8,92u8,11u8,137u8,72u8,241u8,13u8,5u8,131u8,149u8,8u8,6u8,112u8, - 233u8,25u8,162u8,83u8,117u8,103u8,192u8,55u8,207u8,207u8,36u8,164u8,4u8,77u8,75u8,46u8,99u8,244u8,78u8,137u8, - 2u8,97u8,169u8,197u8,90u8,185u8,116u8,217u8,76u8,76u8,157u8,182u8,90u8,101u8,160u8,22u8,57u8,1u8,79u8,64u8, - 24u8,252u8,127u8,197u8,59u8,117u8,255u8,80u8,150u8,217u8,208u8,253u8,213u8,58u8,33u8,65u8,7u8,190u8,219u8,202u8, - 39u8,156u8,28u8,50u8,174u8,83u8,172u8,206u8,106u8,161u8,228u8,69u8,198u8,52u8,3u8,52u8,19u8,133u8,166u8,230u8, - 155u8,93u8,229u8,54u8,85u8,3u8,52u8,44u8,122u8,147u8,107u8,134u8,58u8,86u8,54u8,63u8,42u8,170u8,106u8,136u8, - 165u8,158u8,233u8,30u8,213u8,252u8,143u8,19u8,164u8,137u8,154u8,196u8,170u8,250u8,160u8,144u8,111u8,34u8,188u8,23u8, - 198u8,154u8,195u8,93u8,53u8,127u8,136u8,66u8,76u8,146u8,83u8,80u8,22u8,83u8,139u8,50u8,97u8,175u8,215u8,135u8, - 124u8,247u8,141u8,199u8,107u8,212u8,43u8,97u8,188u8,43u8,75u8,80u8,210u8,22u8,136u8,58u8,3u8,213u8,107u8,234u8, - 44u8,35u8,186u8,85u8,150u8,176u8,208u8,175u8,35u8,248u8,118u8,127u8,174u8,40u8,206u8,29u8,75u8,51u8,53u8,231u8, - 217u8,97u8,129u8,226u8,67u8,244u8,99u8,87u8,172u8,131u8,220u8,101u8,178u8,85u8,65u8,29u8,237u8,48u8,20u8,95u8, - 33u8,19u8,242u8,150u8,70u8,43u8,97u8,196u8,131u8,91u8,81u8,255u8,162u8,103u8,87u8,202u8,22u8,204u8,48u8,186u8, - 132u8,177u8,133u8,144u8,173u8,156u8,125u8,6u8,101u8,141u8,175u8,152u8,252u8,178u8,208u8,2u8,125u8,83u8,177u8,83u8, - 12u8,203u8,201u8,106u8,145u8,122u8,165u8,116u8,174u8,5u8,48u8,10u8,242u8,2u8,184u8,165u8,8u8,82u8,37u8,24u8, - 100u8,99u8,54u8,50u8,246u8,114u8,241u8,178u8,115u8,185u8,4u8,147u8,182u8,200u8,252u8,211u8,137u8,213u8,188u8,115u8, - 48u8,218u8,150u8,114u8,115u8,193u8,145u8,6u8,73u8,213u8,150u8,6u8,145u8,174u8,24u8,255u8,73u8,162u8,53u8,237u8, - 217u8,244u8,169u8,99u8,165u8,145u8,210u8,24u8,229u8,116u8,76u8,200u8,10u8,97u8,244u8,225u8,110u8,41u8,226u8,37u8, - 225u8,163u8,128u8,65u8,92u8,85u8,192u8,97u8,25u8,172u8,95u8,229u8,165u8,234u8,67u8,9u8,205u8,251u8,57u8,84u8, - 209u8,94u8,143u8,140u8,46u8,107u8,230u8,253u8,35u8,159u8,151u8,85u8,121u8,43u8,229u8,191u8,104u8,33u8,182u8,124u8, - 59u8,145u8,26u8,212u8,149u8,171u8,161u8,109u8,240u8,194u8,205u8,240u8,230u8,83u8,14u8,179u8,35u8,12u8,82u8,192u8, - 150u8,86u8,254u8,218u8,61u8,223u8,222u8,157u8,181u8,134u8,188u8,123u8,51u8,148u8,21u8,63u8,51u8,223u8,141u8,139u8, - 101u8,127u8,107u8,195u8,29u8,244u8,182u8,199u8,232u8,117u8,99u8,8u8,71u8,116u8,247u8,246u8,225u8,109u8,35u8,80u8, - 19u8,241u8,118u8,202u8,209u8,103u8,73u8,57u8,170u8,177u8,191u8,182u8,219u8,125u8,104u8,179u8,233u8,77u8,58u8,2u8, - 120u8,33u8,31u8,109u8,21u8,153u8,168u8,174u8,107u8,55u8,79u8,253u8,198u8,74u8,104u8,75u8,183u8,249u8,253u8,87u8, - 198u8,230u8,174u8,201u8,219u8,127u8,114u8,96u8,90u8,220u8,119u8,167u8,230u8,43u8,160u8,94u8,211u8,42u8,83u8,180u8, - 16u8,138u8,215u8,16u8,50u8,121u8,123u8,127u8,48u8,122u8,87u8,48u8,254u8,230u8,19u8,222u8,211u8,43u8,44u8,237u8, - 81u8,182u8,224u8,34u8,243u8,27u8,146u8,19u8,64u8,191u8,46u8,18u8,36u8,227u8,119u8,163u8,239u8,223u8,255u8,64u8, - 249u8,233u8,103u8,66u8,88u8,204u8,71u8,180u8,9u8,178u8,197u8,139u8,245u8,226u8,100u8,253u8,50u8,202u8,204u8,82u8, - 185u8,44u8,36u8,122u8,133u8,114u8,218u8,5u8,124u8,9u8,229u8,180u8,40u8,108u8,167u8,171u8,41u8,124u8,220u8,123u8, - 220u8,251u8,15u8,142u8,174u8,125u8,44u8,87u8,13u8,0u8,0u8,0u8,0u8,22u8,116u8,114u8,97u8,110u8,115u8,97u8, - 99u8,116u8,105u8,111u8,110u8,95u8,118u8,97u8,108u8,105u8,100u8,97u8,116u8,105u8,111u8,110u8,227u8,15u8,31u8,139u8, - 8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,89u8,235u8,115u8,211u8,56u8,16u8,255u8,206u8,95u8,161u8,227u8, - 3u8,151u8,112u8,161u8,52u8,80u8,74u8,49u8,148u8,57u8,147u8,228u8,32u8,115u8,52u8,238u8,53u8,41u8,7u8,115u8, - 195u8,104u8,20u8,91u8,73u8,68u8,253u8,200u8,89u8,114u8,211u8,222u8,77u8,255u8,247u8,91u8,249u8,21u8,89u8,150u8, - 147u8,148u8,199u8,12u8,92u8,63u8,64u8,108u8,175u8,86u8,187u8,191u8,93u8,237u8,75u8,65u8,228u8,37u8,62u8,69u8, - 100u8,41u8,34u8,142u8,103u8,49u8,9u8,232u8,42u8,138u8,47u8,44u8,75u8,196u8,36u8,228u8,196u8,21u8,44u8,10u8, - 241u8,37u8,241u8,153u8,71u8,228u8,79u8,244u8,239u8,29u8,4u8,127u8,9u8,167u8,136u8,11u8,207u8,178u8,104u8,28u8, - 71u8,241u8,243u8,234u8,187u8,25u8,37u8,34u8,137u8,41u8,215u8,94u8,115u8,54u8,15u8,169u8,78u8,123u8,73u8,93u8, - 33u8,25u8,148u8,111u8,107u8,114u8,16u8,215u8,141u8,146u8,80u8,60u8,223u8,64u8,145u8,190u8,112u8,35u8,22u8,90u8, - 150u8,45u8,127u8,247u8,224u8,231u8,6u8,122u8,119u8,65u8,88u8,136u8,153u8,183u8,137u8,100u8,51u8,7u8,126u8,205u8, - 5u8,13u8,48u8,241u8,60u8,80u8,147u8,171u8,154u8,214u8,97u8,100u8,1u8,229u8,130u8,4u8,203u8,77u8,52u8,10u8, - 212u8,51u8,74u8,115u8,52u8,102u8,49u8,163u8,161u8,87u8,167u8,158u8,211u8,144u8,114u8,198u8,115u8,170u8,135u8,15u8, - 31u8,162u8,201u8,130u8,113u8,180u8,136u8,124u8,143u8,35u8,22u8,206u8,162u8,56u8,200u8,76u8,37u8,22u8,68u8,160u8, - 21u8,243u8,125u8,52u8,165u8,104u8,201u8,220u8,11u8,234u8,161u8,100u8,137u8,166u8,215u8,240u8,133u8,162u8,119u8,39u8, - 72u8,68u8,200u8,37u8,240u8,25u8,30u8,75u8,94u8,110u8,20u8,199u8,96u8,19u8,148u8,130u8,244u8,128u8,47u8,169u8, - 203u8,102u8,204u8,69u8,203u8,56u8,242u8,163u8,121u8,2u8,194u8,131u8,72u8,116u8,201u8,178u8,135u8,89u8,18u8,166u8, - 82u8,243u8,116u8,57u8,23u8,113u8,2u8,43u8,39u8,107u8,109u8,222u8,173u8,253u8,102u8,65u8,56u8,186u8,160u8,215u8, - 185u8,255u8,200u8,191u8,32u8,117u8,188u8,20u8,68u8,11u8,229u8,80u8,118u8,244u8,175u8,33u8,168u8,109u8,161u8,204u8, - 75u8,94u8,36u8,71u8,47u8,215u8,4u8,220u8,141u8,217u8,82u8,224u8,66u8,176u8,13u8,148u8,57u8,171u8,93u8,40u8, - 19u8,95u8,48u8,76u8,0u8,225u8,157u8,24u8,131u8,49u8,99u8,92u8,128u8,209u8,64u8,119u8,147u8,153u8,201u8,5u8, - 144u8,4u8,58u8,177u8,223u8,227u8,243u8,195u8,3u8,11u8,37u8,221u8,71u8,71u8,232u8,24u8,117u8,143u8,14u8,14u8, - 14u8,159u8,30u8,28u8,236u8,63u8,125u8,252u8,116u8,255u8,217u8,147u8,39u8,221u8,195u8,238u8,19u8,213u8,174u8,107u8, - 36u8,17u8,189u8,114u8,41u8,245u8,192u8,130u8,76u8,112u8,4u8,70u8,139u8,92u8,34u8,224u8,41u8,32u8,87u8,104u8, - 78u8,184u8,178u8,199u8,192u8,57u8,159u8,96u8,231u8,55u8,252u8,218u8,30u8,195u8,62u8,135u8,7u8,176u8,205u8,161u8, - 194u8,243u8,180u8,48u8,100u8,122u8,114u8,249u8,30u8,56u8,15u8,149u8,62u8,25u8,195u8,129u8,164u8,75u8,18u8,167u8, - 92u8,163u8,68u8,128u8,251u8,69u8,65u8,234u8,40u8,17u8,252u8,19u8,231u8,228u8,224u8,95u8,240u8,142u8,241u8,146u8, - 93u8,134u8,45u8,226u8,44u8,116u8,169u8,164u8,190u8,78u8,57u8,5u8,100u8,185u8,4u8,54u8,5u8,67u8,255u8,90u8, - 186u8,90u8,64u8,62u8,69u8,177u8,116u8,59u8,56u8,14u8,2u8,160u8,227u8,157u8,212u8,153u8,128u8,190u8,228u8,198u8, - 130u8,101u8,20u8,11u8,18u8,10u8,73u8,47u8,247u8,230u8,52u8,128u8,39u8,230u8,114u8,20u8,205u8,178u8,23u8,233u8, - 201u8,219u8,83u8,244u8,61u8,61u8,115u8,222u8,58u8,175u8,207u8,7u8,120u8,48u8,28u8,189u8,179u8,223u8,14u8,251u8, - 216u8,238u8,245u8,156u8,243u8,209u8,4u8,219u8,231u8,147u8,55u8,248u8,247u8,193u8,135u8,2u8,134u8,238u8,254u8,126u8, - 247u8,185u8,113u8,229u8,120u8,240u8,199u8,249u8,96u8,212u8,27u8,224u8,209u8,249u8,201u8,171u8,193u8,25u8,158u8,56u8, - 14u8,118u8,222u8,246u8,149u8,133u8,143u8,118u8,95u8,56u8,26u8,252u8,169u8,44u8,124u8,108u8,94u8,88u8,200u8,216u8, - 119u8,6u8,99u8,60u8,114u8,38u8,120u8,240u8,126u8,56u8,158u8,40u8,235u8,14u8,204u8,235u8,122u8,54u8,44u8,58u8, - 181u8,63u8,72u8,243u8,226u8,254u8,224u8,212u8,25u8,15u8,213u8,85u8,79u8,204u8,171u8,38u8,103u8,246u8,104u8,108u8, - 247u8,38u8,67u8,103u8,4u8,27u8,157u8,14u8,207u8,6u8,170u8,110u8,135u8,230u8,69u8,175u8,236u8,62u8,238u8,189u8, - 177u8,135u8,35u8,60u8,84u8,169u8,159u8,238u8,142u8,196u8,171u8,225u8,107u8,101u8,225u8,81u8,211u8,194u8,158u8,51u8, - 234u8,219u8,103u8,31u8,164u8,169u8,198u8,216u8,238u8,247u8,207u8,6u8,227u8,49u8,160u8,146u8,1u8,116u8,50u8,28u8, - 159u8,216u8,147u8,222u8,27u8,133u8,209u8,51u8,197u8,157u8,157u8,16u8,156u8,75u8,6u8,49u8,112u8,55u8,47u8,137u8, - 89u8,56u8,71u8,121u8,124u8,148u8,46u8,196u8,66u8,38u8,24u8,196u8,160u8,127u8,10u8,199u8,65u8,16u8,100u8,162u8, - 36u8,118u8,41u8,71u8,16u8,39u8,83u8,87u8,206u8,93u8,56u8,115u8,169u8,101u8,50u8,245u8,153u8,219u8,202u8,66u8, - 111u8,91u8,198u8,55u8,133u8,71u8,171u8,60u8,248u8,122u8,76u8,70u8,247u8,178u8,228u8,246u8,195u8,69u8,167u8,182u8, - 18u8,143u8,245u8,164u8,6u8,137u8,21u8,254u8,139u8,5u8,214u8,180u8,109u8,105u8,207u8,237u8,220u8,28u8,153u8,50u8, - 151u8,20u8,139u8,72u8,39u8,233u8,52u8,36u8,133u8,245u8,230u8,181u8,132u8,240u8,171u8,206u8,194u8,68u8,154u8,41u8, - 53u8,189u8,107u8,174u8,85u8,238u8,86u8,215u8,152u8,76u8,98u8,228u8,186u8,137u8,162u8,201u8,4u8,85u8,178u8,58u8, - 244u8,235u8,239u8,55u8,237u8,231u8,106u8,98u8,144u8,46u8,86u8,114u8,114u8,163u8,32u8,136u8,194u8,181u8,159u8,113u8, - 240u8,66u8,10u8,96u8,232u8,222u8,37u8,174u8,66u8,204u8,233u8,223u8,9u8,133u8,168u8,139u8,195u8,36u8,152u8,74u8, - 34u8,56u8,31u8,85u8,10u8,146u8,64u8,196u8,148u8,209u8,51u8,69u8,3u8,67u8,246u8,53u8,59u8,138u8,164u8,133u8, - 28u8,2u8,10u8,49u8,151u8,26u8,248u8,64u8,142u8,73u8,191u8,39u8,112u8,18u8,184u8,225u8,59u8,189u8,90u8,178u8, - 56u8,219u8,67u8,86u8,59u8,26u8,69u8,81u8,109u8,193u8,235u8,163u8,186u8,211u8,101u8,46u8,246u8,83u8,171u8,2u8, - 95u8,89u8,51u8,89u8,86u8,24u8,173u8,64u8,83u8,136u8,25u8,30u8,111u8,181u8,209u8,11u8,211u8,126u8,85u8,232u8, - 211u8,60u8,101u8,89u8,44u8,76u8,253u8,0u8,147u8,120u8,158u8,4u8,0u8,66u8,107u8,99u8,48u8,108u8,175u8,121u8, - 228u8,214u8,81u8,101u8,43u8,53u8,128u8,210u8,75u8,128u8,20u8,199u8,199u8,165u8,82u8,157u8,29u8,246u8,83u8,227u8, - 104u8,91u8,61u8,44u8,62u8,133u8,60u8,167u8,120u8,110u8,102u8,109u8,136u8,113u8,153u8,185u8,225u8,252u8,101u8,71u8, - 17u8,71u8,179u8,214u8,189u8,236u8,163u8,65u8,186u8,188u8,62u8,134u8,82u8,252u8,138u8,113u8,193u8,49u8,17u8,173u8, - 58u8,211u8,246u8,46u8,130u8,154u8,115u8,82u8,219u8,176u8,103u8,213u8,90u8,70u8,87u8,147u8,40u8,149u8,162u8,1u8, - 110u8,6u8,18u8,163u8,156u8,183u8,52u8,102u8,83u8,206u8,175u8,90u8,116u8,179u8,2u8,45u8,195u8,113u8,2u8,202u8, - 180u8,72u8,147u8,62u8,151u8,87u8,109u8,70u8,209u8,160u8,82u8,2u8,243u8,96u8,80u8,100u8,78u8,91u8,91u8,179u8, - 97u8,219u8,40u8,148u8,244u8,131u8,28u8,170u8,154u8,24u8,26u8,136u8,218u8,103u8,19u8,130u8,59u8,88u8,75u8,223u8, - 228u8,229u8,113u8,211u8,254u8,183u8,53u8,71u8,67u8,33u8,101u8,214u8,27u8,210u8,247u8,95u8,167u8,61u8,187u8,251u8, - 232u8,163u8,133u8,122u8,11u8,234u8,94u8,100u8,29u8,139u8,44u8,243u8,20u8,189u8,126u8,230u8,168u8,16u8,10u8,229u8, - 242u8,66u8,127u8,227u8,66u8,213u8,90u8,246u8,45u8,57u8,51u8,55u8,129u8,214u8,5u8,106u8,71u8,141u8,124u8,15u8, - 57u8,178u8,130u8,93u8,49u8,217u8,115u8,106u8,156u8,210u8,50u8,33u8,66u8,33u8,93u8,201u8,182u8,40u8,149u8,166u8, - 251u8,113u8,239u8,246u8,8u8,30u8,127u8,83u8,4u8,161u8,162u8,108u8,246u8,28u8,25u8,157u8,181u8,6u8,18u8,188u8, - 166u8,18u8,211u8,209u8,253u8,122u8,28u8,223u8,226u8,38u8,89u8,55u8,205u8,32u8,156u8,228u8,106u8,197u8,116u8,14u8, - 225u8,133u8,198u8,212u8,123u8,81u8,246u8,216u8,47u8,191u8,198u8,9u8,54u8,85u8,180u8,230u8,120u8,44u8,181u8,157u8, - 18u8,159u8,72u8,251u8,29u8,231u8,18u8,230u8,207u8,91u8,100u8,170u8,43u8,91u8,240u8,1u8,215u8,55u8,0u8,216u8, - 249u8,92u8,185u8,235u8,185u8,93u8,171u8,41u8,190u8,86u8,110u8,207u8,10u8,213u8,239u8,51u8,167u8,235u8,229u8,76u8, - 166u8,105u8,199u8,164u8,89u8,71u8,83u8,166u8,83u8,21u8,188u8,83u8,151u8,181u8,99u8,44u8,1u8,74u8,137u8,218u8, - 58u8,254u8,90u8,213u8,247u8,127u8,197u8,95u8,254u8,225u8,92u8,87u8,24u8,181u8,44u8,54u8,151u8,250u8,223u8,149u8, - 133u8,212u8,154u8,250u8,27u8,89u8,43u8,91u8,189u8,213u8,104u8,89u8,161u8,73u8,226u8,107u8,156u8,109u8,163u8,244u8, - 66u8,5u8,125u8,254u8,102u8,211u8,162u8,245u8,46u8,169u8,37u8,148u8,197u8,235u8,61u8,127u8,212u8,147u8,90u8,3u8, - 242u8,203u8,221u8,65u8,75u8,103u8,176u8,27u8,214u8,33u8,229u8,16u8,238u8,51u8,236u8,44u8,203u8,167u8,225u8,92u8, - 44u8,100u8,61u8,220u8,100u8,171u8,173u8,245u8,222u8,86u8,86u8,53u8,11u8,166u8,53u8,191u8,81u8,178u8,219u8,167u8, - 247u8,157u8,166u8,29u8,13u8,229u8,171u8,4u8,136u8,1u8,24u8,251u8,235u8,132u8,182u8,90u8,48u8,152u8,191u8,181u8, - 24u8,212u8,169u8,70u8,1u8,219u8,90u8,147u8,45u8,89u8,172u8,137u8,114u8,208u8,128u8,229u8,253u8,2u8,149u8,41u8, - 12u8,128u8,163u8,213u8,38u8,128u8,59u8,136u8,41u8,25u8,117u8,75u8,47u8,82u8,219u8,235u8,11u8,91u8,145u8,186u8, - 50u8,70u8,155u8,237u8,164u8,81u8,205u8,206u8,205u8,154u8,85u8,94u8,166u8,199u8,190u8,97u8,219u8,237u8,93u8,143u8, - 1u8,145u8,26u8,247u8,175u8,210u8,248u8,104u8,229u8,147u8,252u8,147u8,206u8,195u8,208u8,47u8,168u8,187u8,126u8,125u8, - 163u8,198u8,99u8,57u8,82u8,27u8,232u8,211u8,125u8,89u8,37u8,199u8,16u8,168u8,201u8,76u8,200u8,198u8,72u8,45u8, - 206u8,229u8,39u8,158u8,184u8,48u8,79u8,227u8,179u8,196u8,135u8,81u8,28u8,189u8,162u8,110u8,2u8,35u8,228u8,189u8, - 146u8,93u8,47u8,27u8,206u8,229u8,183u8,14u8,182u8,7u8,115u8,29u8,26u8,151u8,193u8,191u8,24u8,146u8,40u8,179u8, - 181u8,28u8,190u8,90u8,168u8,199u8,187u8,197u8,250u8,207u8,141u8,165u8,229u8,123u8,40u8,117u8,3u8,136u8,77u8,48u8, - 74u8,76u8,41u8,26u8,71u8,22u8,53u8,134u8,178u8,152u8,52u8,112u8,105u8,246u8,119u8,101u8,84u8,223u8,214u8,234u8, - 220u8,148u8,15u8,7u8,220u8,142u8,235u8,130u8,163u8,7u8,166u8,109u8,118u8,234u8,114u8,215u8,45u8,65u8,217u8,223u8, - 222u8,71u8,173u8,114u8,179u8,117u8,211u8,123u8,188u8,123u8,215u8,171u8,106u8,209u8,84u8,182u8,107u8,245u8,53u8,38u8, - 129u8,180u8,177u8,161u8,79u8,41u8,68u8,169u8,46u8,151u8,7u8,165u8,97u8,46u8,146u8,187u8,139u8,178u8,159u8,188u8, - 71u8,16u8,208u8,53u8,86u8,46u8,19u8,36u8,72u8,130u8,164u8,183u8,23u8,249u8,213u8,7u8,52u8,16u8,30u8,77u8, - 167u8,209u8,178u8,175u8,9u8,229u8,77u8,153u8,88u8,160u8,203u8,64u8,229u8,3u8,11u8,189u8,8u8,205u8,8u8,147u8, - 30u8,172u8,186u8,189u8,235u8,83u8,18u8,38u8,203u8,189u8,93u8,154u8,40u8,67u8,139u8,34u8,21u8,104u8,75u8,119u8, - 49u8,227u8,114u8,155u8,73u8,195u8,246u8,246u8,169u8,252u8,205u8,102u8,168u8,85u8,220u8,205u8,202u8,43u8,78u8,56u8, - 151u8,46u8,68u8,168u8,208u8,195u8,30u8,64u8,16u8,179u8,41u8,28u8,220u8,212u8,24u8,32u8,9u8,76u8,219u8,244u8, - 204u8,1u8,120u8,12u8,103u8,21u8,20u8,36u8,93u8,122u8,223u8,3u8,93u8,97u8,201u8,193u8,147u8,168u8,229u8,67u8, - 88u8,184u8,49u8,130u8,76u8,159u8,109u8,36u8,145u8,15u8,16u8,52u8,226u8,84u8,142u8,222u8,117u8,206u8,62u8,145u8, - 177u8,69u8,225u8,3u8,252u8,247u8,170u8,109u8,119u8,21u8,170u8,181u8,2u8,240u8,144u8,2u8,218u8,105u8,64u8,83u8, - 113u8,141u8,27u8,68u8,125u8,152u8,2u8,212u8,244u8,42u8,7u8,4u8,29u8,244u8,41u8,129u8,219u8,137u8,105u8,18u8, - 103u8,158u8,2u8,92u8,246u8,116u8,226u8,137u8,211u8,119u8,172u8,236u8,234u8,96u8,10u8,27u8,186u8,11u8,196u8,23u8, - 81u8,226u8,123u8,242u8,150u8,21u8,78u8,35u8,76u8,192u8,61u8,208u8,57u8,88u8,130u8,227u8,202u8,155u8,175u8,21u8, - 196u8,127u8,29u8,181u8,2u8,19u8,120u8,212u8,185u8,203u8,137u8,4u8,12u8,63u8,229u8,97u8,132u8,203u8,49u8,120u8, - 202u8,226u8,42u8,84u8,106u8,151u8,146u8,79u8,132u8,228u8,48u8,28u8,173u8,168u8,239u8,111u8,129u8,71u8,234u8,112u8, - 43u8,108u8,170u8,147u8,153u8,97u8,232u8,130u8,50u8,134u8,113u8,74u8,45u8,80u8,67u8,92u8,203u8,73u8,107u8,227u8, - 169u8,212u8,207u8,139u8,150u8,248u8,230u8,206u8,127u8,239u8,235u8,119u8,191u8,107u8,32u8,0u8,0u8,0u8,0u8,7u8, - 103u8,101u8,110u8,101u8,115u8,105u8,115u8,210u8,30u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8, - 28u8,127u8,143u8,211u8,56u8,246u8,127u8,62u8,133u8,119u8,78u8,154u8,237u8,236u8,101u8,135u8,118u8,96u8,88u8,232u8, - 44u8,104u8,185u8,97u8,110u8,133u8,116u8,11u8,39u8,193u8,222u8,157u8,132u8,144u8,73u8,27u8,183u8,13u8,164u8,73u8, - 54u8,78u8,102u8,166u8,183u8,226u8,187u8,223u8,123u8,182u8,227u8,248u8,87u8,210u8,20u8,184u8,19u8,210u8,109u8,133u8, - 170u8,105u8,252u8,252u8,252u8,252u8,252u8,126u8,251u8,133u8,109u8,145u8,52u8,25u8,35u8,113u8,89u8,23u8,156u8,174u8, - 170u8,120u8,203u8,110u8,138u8,234u8,195u8,124u8,190u8,102u8,57u8,227u8,41u8,39u8,191u8,223u8,33u8,240u8,105u8,56u8, - 35u8,188u8,78u8,230u8,115u8,86u8,85u8,69u8,117u8,97u8,63u8,91u8,165u8,183u8,44u8,161u8,101u8,145u8,230u8,245u8, - 189u8,51u8,103u8,236u8,154u8,45u8,107u8,156u8,160u8,159u8,202u8,133u8,196u8,24u8,79u8,183u8,101u8,198u8,232u8,54u8, - 46u8,189u8,113u8,131u8,144u8,120u8,185u8,44u8,154u8,188u8,190u8,24u8,128u8,88u8,175u8,43u8,182u8,142u8,97u8,29u8, - 186u8,138u8,113u8,185u8,221u8,16u8,176u8,120u8,176u8,4u8,98u8,231u8,243u8,223u8,95u8,177u8,108u8,21u8,145u8,167u8, - 248u8,232u8,18u8,158u8,124u8,220u8,59u8,111u8,93u8,92u8,179u8,42u8,143u8,243u8,37u8,27u8,0u8,93u8,100u8,197u8, - 242u8,195u8,192u8,248u8,114u8,19u8,167u8,57u8,77u8,147u8,189u8,32u8,188u8,142u8,235u8,134u8,15u8,129u8,1u8,209u8, - 131u8,195u8,57u8,103u8,57u8,111u8,112u8,191u8,249u8,42u8,93u8,15u8,129u8,86u8,44u8,174u8,25u8,229u8,233u8,58u8, - 103u8,149u8,243u8,115u8,96u8,218u8,58u8,134u8,179u8,92u8,110u8,24u8,202u8,208u8,0u8,88u8,197u8,36u8,1u8,77u8, - 21u8,215u8,105u8,49u8,68u8,50u8,108u8,249u8,3u8,219u8,51u8,158u8,230u8,107u8,220u8,80u8,93u8,193u8,97u8,143u8, - 3u8,29u8,222u8,59u8,178u8,25u8,246u8,10u8,130u8,19u8,175u8,135u8,215u8,22u8,16u8,20u8,54u8,61u8,0u8,85u8, - 167u8,91u8,6u8,24u8,183u8,229u8,16u8,76u8,21u8,231u8,28u8,168u8,7u8,94u8,208u8,21u8,99u8,35u8,33u8,175u8, - 227u8,44u8,77u8,246u8,49u8,16u8,228u8,147u8,239u8,131u8,224u8,53u8,240u8,69u8,233u8,28u8,202u8,72u8,77u8,174u8, - 158u8,253u8,250u8,247u8,191u8,61u8,191u8,124u8,250u8,250u8,138u8,62u8,189u8,188u8,124u8,249u8,235u8,139u8,215u8,115u8, - 210u8,60u8,184u8,79u8,30u8,147u8,217u8,133u8,9u8,166u8,6u8,233u8,179u8,151u8,87u8,175u8,232u8,139u8,151u8,175u8, - 233u8,213u8,191u8,158u8,191u8,210u8,176u8,103u8,10u8,39u8,175u8,171u8,102u8,89u8,147u8,167u8,82u8,115u8,127u8,137u8, - 75u8,178u8,137u8,57u8,73u8,170u8,162u8,84u8,230u8,4u8,63u8,74u8,175u8,105u8,156u8,36u8,21u8,227u8,124u8,78u8, - 212u8,31u8,145u8,134u8,88u8,196u8,25u8,106u8,154u8,192u8,46u8,159u8,126u8,180u8,240u8,95u8,129u8,249u8,40u8,118u8, - 140u8,57u8,235u8,44u8,139u8,114u8,23u8,245u8,172u8,6u8,203u8,72u8,139u8,244u8,163u8,90u8,237u8,73u8,183u8,156u8, - 98u8,111u8,81u8,205u8,201u8,63u8,218u8,63u8,47u8,77u8,177u8,253u8,103u8,90u8,111u8,46u8,139u8,237u8,54u8,229u8, - 200u8,97u8,99u8,162u8,100u8,168u8,86u8,4u8,154u8,55u8,91u8,86u8,73u8,76u8,106u8,53u8,216u8,193u8,147u8,129u8, - 9u8,9u8,203u8,139u8,109u8,154u8,203u8,41u8,122u8,183u8,130u8,7u8,96u8,134u8,87u8,233u8,50u8,141u8,171u8,29u8, - 5u8,122u8,89u8,93u8,131u8,122u8,218u8,172u8,178u8,153u8,18u8,38u8,189u8,159u8,49u8,197u8,13u8,104u8,248u8,192u8, - 33u8,20u8,165u8,220u8,202u8,0u8,200u8,117u8,81u8,15u8,162u8,16u8,58u8,77u8,227u8,45u8,30u8,128u8,179u8,189u8, - 206u8,66u8,149u8,205u8,226u8,3u8,219u8,117u8,12u8,123u8,104u8,240u8,171u8,172u8,138u8,98u8,69u8,225u8,95u8,89u8, - 112u8,206u8,4u8,247u8,195u8,128u8,57u8,171u8,81u8,198u8,91u8,90u8,24u8,15u8,131u8,173u8,154u8,44u8,163u8,121u8, - 145u8,192u8,73u8,237u8,159u8,48u8,134u8,189u8,182u8,100u8,244u8,51u8,91u8,139u8,152u8,50u8,74u8,125u8,146u8,102u8, - 50u8,168u8,197u8,74u8,225u8,32u8,150u8,44u8,175u8,193u8,2u8,57u8,76u8,124u8,15u8,94u8,128u8,38u8,77u8,133u8, - 34u8,165u8,220u8,246u8,156u8,44u8,138u8,34u8,179u8,54u8,112u8,247u8,238u8,93u8,242u8,179u8,114u8,234u8,188u8,102u8, - 37u8,153u8,205u8,201u8,243u8,60u8,173u8,83u8,160u8,224u8,223u8,202u8,76u8,16u8,109u8,38u8,90u8,133u8,33u8,113u8, - 158u8,0u8,17u8,21u8,35u8,91u8,17u8,40u8,112u8,2u8,251u8,19u8,222u8,233u8,244u8,142u8,100u8,101u8,78u8,82u8, - 141u8,101u8,162u8,105u8,50u8,29u8,67u8,248u8,16u8,90u8,47u8,8u8,155u8,121u8,216u8,61u8,85u8,184u8,168u8,50u8, - 99u8,189u8,226u8,210u8,242u8,47u8,132u8,153u8,149u8,197u8,114u8,67u8,33u8,26u8,97u8,21u8,48u8,156u8,110u8,211u8, - 101u8,85u8,112u8,182u8,228u8,14u8,50u8,80u8,184u8,116u8,219u8,108u8,169u8,16u8,79u8,119u8,44u8,190u8,237u8,29u8, - 3u8,103u8,214u8,84u8,130u8,217u8,232u8,231u8,155u8,18u8,89u8,47u8,78u8,141u8,6u8,22u8,137u8,179u8,172u8,184u8, - 161u8,221u8,185u8,131u8,6u8,83u8,216u8,121u8,142u8,135u8,216u8,29u8,145u8,68u8,123u8,19u8,87u8,9u8,167u8,128u8, - 203u8,95u8,177u8,27u8,26u8,176u8,21u8,160u8,135u8,72u8,85u8,89u8,220u8,128u8,58u8,166u8,57u8,250u8,112u8,206u8, - 104u8,150u8,110u8,83u8,83u8,237u8,78u8,12u8,121u8,4u8,145u8,48u8,68u8,160u8,222u8,244u8,138u8,193u8,41u8,121u8, - 189u8,1u8,185u8,129u8,127u8,2u8,72u8,201u8,198u8,205u8,134u8,129u8,96u8,240u8,29u8,136u8,211u8,22u8,136u8,228u8, - 69u8,3u8,18u8,202u8,133u8,200u8,180u8,210u8,114u8,147u8,102u8,25u8,24u8,49u8,115u8,197u8,132u8,9u8,179u8,157u8, - 144u8,186u8,80u8,104u8,21u8,16u8,1u8,233u8,78u8,43u8,150u8,237u8,128u8,253u8,57u8,136u8,121u8,66u8,22u8,59u8, - 16u8,183u8,239u8,133u8,164u8,144u8,46u8,244u8,18u8,11u8,228u8,133u8,0u8,175u8,83u8,88u8,100u8,19u8,95u8,75u8, - 234u8,193u8,130u8,144u8,162u8,2u8,123u8,145u8,94u8,167u8,25u8,91u8,51u8,110u8,174u8,91u8,23u8,194u8,31u8,214u8, - 184u8,98u8,187u8,45u8,61u8,158u8,177u8,154u8,76u8,28u8,87u8,73u8,21u8,84u8,228u8,250u8,80u8,21u8,23u8,209u8, - 101u8,92u8,158u8,128u8,219u8,83u8,96u8,58u8,106u8,234u8,224u8,208u8,98u8,87u8,215u8,16u8,28u8,43u8,144u8,201u8, - 79u8,14u8,166u8,147u8,139u8,158u8,179u8,104u8,89u8,44u8,229u8,92u8,104u8,93u8,223u8,209u8,184u8,62u8,110u8,62u8, - 55u8,20u8,242u8,184u8,103u8,79u8,39u8,202u8,85u8,227u8,39u8,28u8,99u8,88u8,104u8,52u8,44u8,126u8,250u8,112u8, - 70u8,22u8,212u8,226u8,136u8,47u8,171u8,180u8,172u8,41u8,88u8,239u8,172u8,88u8,55u8,236u8,200u8,29u8,151u8,50u8, - 50u8,48u8,222u8,100u8,117u8,74u8,65u8,16u8,32u8,74u8,216u8,135u8,139u8,149u8,169u8,59u8,96u8,110u8,17u8,109u8, - 95u8,170u8,132u8,36u8,97u8,104u8,70u8,43u8,177u8,177u8,36u8,40u8,95u8,34u8,184u8,44u8,50u8,130u8,79u8,196u8, - 20u8,97u8,0u8,135u8,56u8,239u8,36u8,7u8,50u8,86u8,100u8,134u8,156u8,244u8,30u8,68u8,68u8,92u8,153u8,24u8, - 20u8,55u8,123u8,79u8,101u8,83u8,147u8,86u8,198u8,12u8,250u8,244u8,163u8,54u8,234u8,33u8,77u8,158u8,192u8,86u8, - 164u8,4u8,117u8,100u8,90u8,210u8,31u8,146u8,218u8,214u8,43u8,130u8,144u8,59u8,97u8,211u8,155u8,159u8,166u8,183u8, - 103u8,64u8,251u8,244u8,246u8,158u8,248u8,190u8,47u8,190u8,207u8,197u8,247u8,3u8,241u8,253u8,131u8,248u8,126u8,40u8, - 190u8,31u8,137u8,239u8,248u8,109u8,39u8,237u8,55u8,27u8,80u8,80u8,50u8,249u8,70u8,162u8,5u8,89u8,227u8,148u8, - 109u8,203u8,122u8,55u8,57u8,30u8,162u8,227u8,196u8,52u8,94u8,45u8,225u8,106u8,84u8,211u8,56u8,159u8,151u8,69u8, - 73u8,23u8,241u8,242u8,131u8,166u8,118u8,114u8,188u8,109u8,134u8,119u8,104u8,40u8,162u8,54u8,7u8,52u8,34u8,95u8, - 64u8,229u8,213u8,18u8,206u8,2u8,159u8,37u8,49u8,109u8,132u8,69u8,122u8,68u8,164u8,93u8,228u8,163u8,33u8,45u8, - 158u8,235u8,28u8,99u8,37u8,34u8,111u8,154u8,129u8,189u8,117u8,209u8,227u8,16u8,57u8,142u8,221u8,192u8,35u8,125u8, - 236u8,72u8,163u8,101u8,78u8,234u8,146u8,187u8,207u8,181u8,85u8,86u8,40u8,224u8,12u8,153u8,145u8,128u8,61u8,52u8, - 28u8,8u8,216u8,176u8,253u8,113u8,128u8,139u8,179u8,115u8,245u8,253u8,35u8,102u8,16u8,96u8,67u8,13u8,196u8,0u8, - 150u8,101u8,236u8,248u8,168u8,115u8,218u8,67u8,143u8,192u8,10u8,241u8,198u8,9u8,129u8,57u8,197u8,49u8,101u8,87u8, - 32u8,100u8,96u8,100u8,111u8,192u8,216u8,198u8,16u8,97u8,10u8,181u8,34u8,93u8,101u8,7u8,28u8,31u8,56u8,118u8, - 222u8,148u8,101u8,6u8,97u8,245u8,2u8,180u8,57u8,47u8,106u8,136u8,0u8,226u8,5u8,152u8,145u8,180u8,22u8,163u8, - 24u8,43u8,131u8,159u8,68u8,39u8,255u8,190u8,129u8,108u8,117u8,199u8,76u8,19u8,237u8,21u8,137u8,76u8,154u8,169u8, - 63u8,60u8,134u8,5u8,178u8,140u8,100u8,160u8,145u8,4u8,42u8,169u8,28u8,229u8,124u8,117u8,24u8,60u8,82u8,31u8, - 21u8,184u8,65u8,132u8,83u8,92u8,57u8,244u8,24u8,69u8,181u8,106u8,228u8,234u8,125u8,145u8,181u8,173u8,152u8,93u8, - 49u8,229u8,80u8,90u8,116u8,237u8,4u8,204u8,33u8,40u8,8u8,254u8,164u8,27u8,148u8,153u8,58u8,174u8,106u8,150u8, - 236u8,67u8,209u8,151u8,233u8,156u8,89u8,153u8,142u8,40u8,247u8,137u8,163u8,11u8,165u8,48u8,180u8,43u8,16u8,186u8, - 241u8,224u8,156u8,28u8,75u8,51u8,107u8,122u8,33u8,225u8,41u8,22u8,77u8,149u8,163u8,237u8,141u8,208u8,146u8,212u8, - 218u8,73u8,24u8,165u8,70u8,131u8,15u8,131u8,33u8,160u8,136u8,82u8,132u8,173u8,81u8,33u8,52u8,249u8,5u8,48u8, - 94u8,198u8,101u8,188u8,72u8,179u8,180u8,222u8,253u8,168u8,139u8,149u8,79u8,8u8,47u8,80u8,242u8,81u8,93u8,112u8, - 213u8,214u8,64u8,156u8,186u8,134u8,85u8,58u8,150u8,142u8,24u8,218u8,210u8,56u8,241u8,2u8,15u8,77u8,189u8,79u8, - 147u8,83u8,178u8,106u8,169u8,251u8,11u8,236u8,124u8,31u8,117u8,200u8,29u8,84u8,253u8,211u8,96u8,196u8,9u8,200u8, - 2u8,52u8,182u8,28u8,245u8,105u8,108u8,71u8,252u8,51u8,127u8,153u8,67u8,234u8,176u8,4u8,75u8,139u8,193u8,16u8, - 216u8,131u8,26u8,100u8,9u8,18u8,124u8,153u8,144u8,176u8,51u8,38u8,30u8,240u8,224u8,161u8,99u8,128u8,71u8,117u8, - 6u8,67u8,97u8,130u8,41u8,7u8,78u8,156u8,231u8,11u8,132u8,153u8,161u8,218u8,136u8,154u8,122u8,67u8,131u8,117u8, - 141u8,63u8,100u8,232u8,235u8,144u8,33u8,235u8,16u8,236u8,211u8,11u8,196u8,121u8,58u8,143u8,179u8,33u8,141u8,189u8, - 234u8,25u8,85u8,33u8,44u8,33u8,74u8,0u8,38u8,170u8,75u8,25u8,25u8,128u8,44u8,72u8,251u8,153u8,199u8,217u8, - 228u8,216u8,198u8,18u8,245u8,73u8,143u8,137u8,222u8,144u8,135u8,214u8,236u8,107u8,178u8,96u8,143u8,224u8,191u8,80u8, - 202u8,253u8,45u8,123u8,107u8,57u8,231u8,164u8,244u8,8u8,245u8,194u8,222u8,43u8,239u8,53u8,131u8,145u8,95u8,85u8, - 237u8,74u8,177u8,79u8,92u8,1u8,79u8,129u8,157u8,211u8,11u8,235u8,17u8,212u8,73u8,245u8,42u8,70u8,248u8,158u8, - 177u8,124u8,93u8,111u8,192u8,216u8,171u8,161u8,19u8,123u8,86u8,147u8,167u8,191u8,53u8,44u8,52u8,81u8,102u8,16u8, - 230u8,161u8,170u8,52u8,35u8,37u8,63u8,90u8,139u8,5u8,211u8,8u8,85u8,143u8,134u8,235u8,40u8,3u8,229u8,162u8, - 128u8,11u8,175u8,155u8,142u8,22u8,8u8,100u8,221u8,72u8,30u8,210u8,135u8,170u8,254u8,198u8,14u8,59u8,241u8,163u8, - 115u8,27u8,76u8,39u8,193u8,107u8,243u8,201u8,177u8,67u8,58u8,156u8,138u8,177u8,234u8,169u8,83u8,17u8,63u8,137u8, - 60u8,148u8,226u8,246u8,13u8,238u8,162u8,50u8,56u8,161u8,100u8,71u8,217u8,109u8,10u8,6u8,109u8,226u8,23u8,239u8, - 157u8,169u8,14u8,197u8,58u8,77u8,106u8,248u8,70u8,228u8,73u8,50u8,61u8,242u8,136u8,27u8,162u8,205u8,224u8,177u8, - 48u8,123u8,182u8,122u8,120u8,132u8,187u8,226u8,232u8,3u8,244u8,175u8,53u8,12u8,172u8,46u8,9u8,188u8,13u8,91u8, - 191u8,81u8,246u8,82u8,242u8,231u8,246u8,50u8,67u8,229u8,70u8,142u8,251u8,16u8,165u8,40u8,185u8,17u8,116u8,25u8, - 168u8,10u8,137u8,248u8,163u8,173u8,197u8,164u8,43u8,180u8,70u8,73u8,193u8,120u8,254u8,45u8,196u8,158u8,200u8,252u8, - 83u8,61u8,251u8,185u8,24u8,148u8,39u8,18u8,225u8,159u8,34u8,4u8,173u8,88u8,13u8,166u8,70u8,86u8,205u8,164u8, - 210u8,156u8,246u8,168u8,217u8,126u8,45u8,243u8,43u8,236u8,214u8,13u8,201u8,201u8,92u8,173u8,96u8,200u8,54u8,80u8, - 60u8,209u8,6u8,73u8,210u8,70u8,227u8,122u8,226u8,30u8,166u8,171u8,14u8,214u8,237u8,159u8,7u8,221u8,177u8,144u8, - 176u8,12u8,98u8,237u8,94u8,85u8,26u8,176u8,159u8,190u8,56u8,89u8,235u8,11u8,243u8,6u8,241u8,56u8,16u8,204u8, - 42u8,195u8,33u8,104u8,61u8,12u8,102u8,211u8,114u8,26u8,218u8,53u8,223u8,252u8,185u8,50u8,213u8,178u8,206u8,69u8, - 36u8,193u8,186u8,45u8,246u8,216u8,70u8,166u8,174u8,158u8,186u8,140u8,142u8,119u8,66u8,223u8,13u8,182u8,247u8,60u8, - 24u8,190u8,58u8,197u8,90u8,15u8,8u8,202u8,251u8,105u8,145u8,232u8,28u8,178u8,7u8,188u8,51u8,181u8,254u8,237u8, - 87u8,79u8,100u8,209u8,99u8,120u8,53u8,1u8,235u8,170u8,104u8,202u8,144u8,253u8,213u8,107u8,126u8,57u8,3u8,236u8, - 44u8,26u8,178u8,195u8,239u8,109u8,114u8,219u8,199u8,246u8,204u8,128u8,133u8,214u8,212u8,250u8,38u8,218u8,221u8,50u8, - 7u8,23u8,236u8,225u8,113u8,119u8,45u8,1u8,78u8,13u8,39u8,228u8,225u8,92u8,52u8,232u8,204u8,145u8,1u8,93u8, - 43u8,67u8,43u8,234u8,19u8,119u8,130u8,226u8,196u8,123u8,135u8,19u8,29u8,45u8,46u8,51u8,124u8,109u8,234u8,219u8, - 178u8,67u8,106u8,68u8,222u8,59u8,12u8,24u8,244u8,83u8,99u8,125u8,85u8,171u8,122u8,81u8,16u8,195u8,39u8,186u8, - 166u8,128u8,123u8,58u8,196u8,69u8,125u8,23u8,200u8,168u8,67u8,82u8,3u8,204u8,179u8,141u8,218u8,119u8,97u8,67u8, - 210u8,206u8,171u8,33u8,120u8,203u8,112u8,146u8,48u8,41u8,202u8,86u8,152u8,134u8,104u8,207u8,124u8,156u8,199u8,245u8, - 252u8,27u8,184u8,30u8,76u8,170u8,248u8,198u8,178u8,100u8,45u8,105u8,145u8,92u8,44u8,128u8,200u8,20u8,42u8,176u8, - 91u8,146u8,5u8,74u8,230u8,186u8,173u8,71u8,114u8,177u8,16u8,7u8,80u8,149u8,222u8,219u8,110u8,79u8,185u8,190u8, - 177u8,90u8,135u8,146u8,218u8,26u8,40u8,118u8,205u8,194u8,113u8,154u8,35u8,134u8,253u8,215u8,225u8,1u8,181u8,108u8, - 129u8,6u8,141u8,72u8,64u8,125u8,108u8,162u8,250u8,84u8,71u8,175u8,188u8,95u8,121u8,250u8,169u8,14u8,171u8,147u8, - 144u8,46u8,92u8,28u8,80u8,91u8,13u8,80u8,70u8,77u8,184u8,216u8,82u8,105u8,204u8,33u8,214u8,255u8,206u8,192u8, - 183u8,111u8,105u8,163u8,186u8,55u8,94u8,55u8,218u8,217u8,145u8,36u8,235u8,51u8,229u8,193u8,165u8,73u8,48u8,80u8, - 60u8,210u8,251u8,115u8,65u8,124u8,203u8,162u8,73u8,242u8,3u8,217u8,160u8,139u8,28u8,1u8,231u8,120u8,201u8,225u8, - 176u8,79u8,222u8,14u8,0u8,39u8,129u8,122u8,151u8,231u8,173u8,223u8,62u8,117u8,239u8,228u8,79u8,173u8,142u8,136u8, - 139u8,48u8,66u8,101u8,70u8,0u8,239u8,177u8,19u8,44u8,225u8,104u8,64u8,206u8,219u8,126u8,37u8,106u8,222u8,86u8, - 132u8,249u8,217u8,130u8,6u8,162u8,104u8,99u8,105u8,159u8,87u8,189u8,238u8,192u8,131u8,108u8,109u8,72u8,64u8,180u8, - 236u8,35u8,141u8,194u8,36u8,12u8,156u8,211u8,126u8,238u8,58u8,253u8,36u8,159u8,131u8,203u8,106u8,60u8,57u8,0u8, - 81u8,176u8,169u8,194u8,159u8,127u8,123u8,116u8,52u8,152u8,71u8,225u8,185u8,150u8,112u8,135u8,31u8,58u8,83u8,217u8, - 243u8,98u8,142u8,78u8,92u8,17u8,240u8,146u8,20u8,8u8,214u8,29u8,146u8,67u8,221u8,63u8,228u8,155u8,199u8,120u8, - 153u8,54u8,13u8,217u8,188u8,110u8,121u8,168u8,158u8,134u8,38u8,79u8,44u8,17u8,242u8,164u8,210u8,51u8,77u8,33u8, - 28u8,39u8,35u8,172u8,71u8,203u8,106u8,84u8,144u8,209u8,231u8,57u8,50u8,175u8,14u8,36u8,52u8,29u8,78u8,75u8, - 121u8,251u8,51u8,104u8,184u8,60u8,128u8,90u8,9u8,36u8,120u8,147u8,158u8,150u8,182u8,225u8,12u8,250u8,19u8,105u8, - 115u8,68u8,255u8,43u8,35u8,207u8,210u8,166u8,255u8,26u8,109u8,1u8,41u8,239u8,72u8,8u8,116u8,50u8,133u8,196u8, - 220u8,40u8,220u8,234u8,185u8,19u8,83u8,215u8,162u8,78u8,2u8,247u8,72u8,107u8,168u8,50u8,208u8,147u8,243u8,133u8, - 86u8,229u8,20u8,99u8,59u8,218u8,25u8,148u8,67u8,106u8,197u8,112u8,81u8,69u8,221u8,126u8,86u8,183u8,43u8,168u8, - 91u8,73u8,39u8,126u8,99u8,90u8,208u8,14u8,75u8,5u8,187u8,69u8,2u8,209u8,93u8,55u8,120u8,114u8,209u8,151u8, - 214u8,25u8,48u8,129u8,140u8,206u8,180u8,6u8,110u8,20u8,214u8,205u8,244u8,211u8,182u8,1u8,190u8,251u8,9u8,190u8, - 30u8,138u8,130u8,124u8,29u8,87u8,17u8,50u8,43u8,214u8,207u8,192u8,150u8,86u8,197u8,110u8,168u8,43u8,234u8,91u8, - 40u8,15u8,201u8,162u8,53u8,54u8,23u8,137u8,2u8,186u8,12u8,252u8,243u8,226u8,6u8,230u8,197u8,53u8,92u8,103u8, - 126u8,11u8,151u8,154u8,73u8,145u8,195u8,205u8,38u8,28u8,16u8,65u8,11u8,10u8,100u8,17u8,200u8,60u8,17u8,175u8, - 218u8,154u8,185u8,108u8,199u8,144u8,211u8,96u8,209u8,55u8,145u8,100u8,245u8,22u8,224u8,205u8,141u8,170u8,226u8,61u8, - 184u8,185u8,156u8,221u8,80u8,113u8,121u8,55u8,241u8,175u8,77u8,94u8,225u8,21u8,137u8,77u8,144u8,113u8,100u8,156u8, - 201u8,203u8,85u8,28u8,85u8,221u8,146u8,167u8,70u8,201u8,140u8,25u8,160u8,71u8,194u8,248u8,30u8,17u8,35u8,101u8, - 132u8,139u8,23u8,152u8,152u8,86u8,196u8,46u8,131u8,107u8,4u8,79u8,117u8,135u8,201u8,4u8,97u8,161u8,214u8,13u8, - 18u8,20u8,87u8,216u8,10u8,182u8,44u8,18u8,184u8,199u8,129u8,216u8,13u8,87u8,126u8,39u8,80u8,243u8,119u8,122u8, - 226u8,85u8,188u8,220u8,152u8,84u8,130u8,106u8,241u8,174u8,89u8,129u8,192u8,53u8,34u8,7u8,159u8,206u8,37u8,215u8, - 17u8,3u8,246u8,132u8,225u8,149u8,50u8,182u8,136u8,65u8,41u8,28u8,86u8,45u8,139u8,60u8,193u8,179u8,128u8,163u8, - 67u8,128u8,171u8,228u8,236u8,252u8,124u8,246u8,72u8,175u8,80u8,54u8,139u8,44u8,93u8,10u8,120u8,32u8,226u8,157u8, - 219u8,169u8,202u8,223u8,117u8,108u8,248u8,43u8,196u8,234u8,25u8,222u8,75u8,51u8,155u8,170u8,45u8,150u8,0u8,121u8, - 201u8,150u8,233u8,106u8,103u8,50u8,176u8,45u8,224u8,105u8,4u8,19u8,14u8,41u8,106u8,189u8,43u8,25u8,191u8,203u8, - 171u8,229u8,93u8,167u8,43u8,245u8,46u8,220u8,147u8,156u8,162u8,106u8,225u8,57u8,164u8,53u8,103u8,217u8,74u8,112u8, - 22u8,254u8,20u8,173u8,172u8,4u8,91u8,89u8,185u8,38u8,71u8,99u8,125u8,97u8,175u8,6u8,105u8,10u8,203u8,176u8, - 162u8,9u8,236u8,141u8,149u8,34u8,146u8,18u8,219u8,130u8,218u8,68u8,82u8,246u8,243u8,137u8,77u8,96u8,135u8,212u8, - 14u8,251u8,253u8,52u8,100u8,177u8,34u8,186u8,91u8,70u8,47u8,97u8,28u8,83u8,12u8,135u8,144u8,175u8,241u8,58u8, - 231u8,242u8,21u8,89u8,236u8,240u8,246u8,190u8,170u8,226u8,221u8,233u8,40u8,51u8,58u8,80u8,253u8,28u8,109u8,1u8, - 159u8,124u8,113u8,107u8,119u8,39u8,104u8,197u8,60u8,155u8,127u8,72u8,1u8,108u8,164u8,165u8,12u8,44u8,49u8,170u8, - 243u8,56u8,16u8,18u8,122u8,45u8,199u8,110u8,151u8,148u8,76u8,31u8,13u8,194u8,124u8,215u8,223u8,211u8,133u8,60u8, - 245u8,33u8,131u8,189u8,200u8,117u8,213u8,56u8,225u8,245u8,199u8,81u8,55u8,18u8,253u8,44u8,143u8,250u8,89u8,117u8, - 160u8,165u8,63u8,200u8,185u8,123u8,94u8,103u8,21u8,67u8,5u8,60u8,26u8,32u8,180u8,247u8,118u8,45u8,232u8,213u8, - 14u8,186u8,104u8,214u8,71u8,210u8,30u8,236u8,241u8,97u8,175u8,45u8,236u8,11u8,63u8,92u8,117u8,178u8,130u8,122u8, - 111u8,249u8,64u8,52u8,111u8,205u8,22u8,6u8,220u8,200u8,151u8,123u8,174u8,61u8,12u8,94u8,218u8,177u8,188u8,57u8, - 96u8,190u8,80u8,96u8,118u8,227u8,140u8,199u8,236u8,38u8,161u8,100u8,250u8,73u8,120u8,236u8,4u8,84u8,32u8,25u8, - 232u8,173u8,150u8,87u8,242u8,24u8,167u8,10u8,243u8,141u8,170u8,34u8,158u8,91u8,206u8,214u8,110u8,73u8,118u8,210u8, - 75u8,12u8,158u8,131u8,209u8,141u8,163u8,248u8,222u8,177u8,234u8,23u8,186u8,156u8,1u8,63u8,69u8,16u8,76u8,143u8, - 250u8,205u8,200u8,136u8,4u8,190u8,143u8,63u8,3u8,128u8,230u8,137u8,14u8,154u8,31u8,37u8,108u8,95u8,40u8,131u8, - 247u8,249u8,20u8,200u8,219u8,71u8,8u8,164u8,151u8,217u8,13u8,223u8,145u8,121u8,29u8,143u8,178u8,187u8,144u8,10u8, - 252u8,159u8,112u8,36u8,195u8,220u8,251u8,226u8,71u8,231u8,94u8,39u8,135u8,249u8,19u8,180u8,182u8,40u8,192u8,254u8, - 105u8,142u8,72u8,251u8,62u8,49u8,229u8,251u8,232u8,153u8,223u8,189u8,136u8,140u8,235u8,85u8,227u8,141u8,176u8,30u8, - 219u8,234u8,218u8,200u8,150u8,195u8,126u8,97u8,112u8,64u8,88u8,252u8,136u8,93u8,245u8,142u8,116u8,49u8,39u8,4u8, - 156u8,182u8,92u8,180u8,72u8,236u8,147u8,177u8,24u8,210u8,115u8,72u8,110u8,32u8,219u8,7u8,23u8,120u8,233u8,170u8, - 167u8,131u8,84u8,144u8,220u8,148u8,9u8,146u8,172u8,67u8,215u8,60u8,161u8,24u8,155u8,138u8,183u8,172u8,116u8,220u8, - 248u8,165u8,246u8,224u8,189u8,182u8,213u8,7u8,56u8,240u8,166u8,215u8,208u8,102u8,132u8,64u8,218u8,205u8,186u8,186u8, - 105u8,71u8,83u8,109u8,81u8,122u8,18u8,104u8,39u8,96u8,36u8,139u8,49u8,1u8,192u8,22u8,68u8,136u8,158u8,149u8, - 100u8,119u8,1u8,49u8,162u8,85u8,15u8,41u8,131u8,122u8,202u8,152u8,118u8,67u8,243u8,197u8,96u8,89u8,230u8,27u8, - 192u8,96u8,19u8,245u8,167u8,55u8,215u8,80u8,60u8,95u8,237u8,104u8,145u8,103u8,187u8,183u8,206u8,43u8,220u8,32u8, - 170u8,13u8,236u8,227u8,162u8,23u8,212u8,209u8,29u8,108u8,52u8,18u8,48u8,42u8,163u8,251u8,227u8,205u8,175u8,175u8, - 255u8,205u8,175u8,113u8,129u8,37u8,214u8,135u8,21u8,190u8,122u8,3u8,34u8,177u8,41u8,50u8,60u8,169u8,217u8,217u8, - 67u8,147u8,172u8,223u8,154u8,180u8,194u8,59u8,175u8,170u8,0u8,243u8,0u8,139u8,134u8,216u8,165u8,176u8,12u8,50u8, - 105u8,160u8,129u8,44u8,250u8,90u8,26u8,42u8,44u8,147u8,114u8,96u8,29u8,206u8,139u8,164u8,251u8,94u8,84u8,48u8, - 213u8,198u8,182u8,102u8,173u8,186u8,68u8,33u8,135u8,216u8,170u8,139u8,51u8,197u8,209u8,22u8,123u8,180u8,79u8,75u8, - 254u8,15u8,223u8,137u8,8u8,249u8,128u8,214u8,22u8,138u8,255u8,133u8,1u8,136u8,161u8,234u8,1u8,93u8,101u8,241u8, - 154u8,7u8,210u8,1u8,33u8,14u8,111u8,102u8,17u8,57u8,123u8,171u8,127u8,189u8,53u8,16u8,142u8,106u8,46u8,247u8, - 122u8,60u8,205u8,119u8,131u8,70u8,25u8,222u8,128u8,122u8,123u8,39u8,234u8,105u8,182u8,203u8,207u8,160u8,94u8,7u8, - 217u8,105u8,29u8,108u8,136u8,143u8,123u8,218u8,72u8,187u8,238u8,81u8,127u8,78u8,168u8,189u8,170u8,231u8,202u8,120u8, - 255u8,21u8,49u8,9u8,245u8,49u8,125u8,94u8,218u8,47u8,10u8,26u8,36u8,88u8,25u8,63u8,208u8,37u8,99u8,179u8, - 174u8,225u8,101u8,85u8,209u8,81u8,69u8,7u8,77u8,57u8,25u8,99u8,58u8,110u8,143u8,166u8,222u8,231u8,40u8,194u8, - 4u8,84u8,148u8,163u8,208u8,180u8,232u8,171u8,120u8,107u8,226u8,125u8,240u8,191u8,8u8,246u8,250u8,234u8,213u8,235u8, - 231u8,47u8,126u8,150u8,150u8,134u8,60u8,127u8,102u8,1u8,57u8,69u8,158u8,219u8,163u8,217u8,153u8,147u8,84u8,205u8, - 162u8,161u8,9u8,179u8,193u8,159u8,126u8,105u8,104u8,24u8,254u8,158u8,129u8,253u8,196u8,103u8,101u8,23u8,171u8,8u8, - 198u8,250u8,60u8,84u8,79u8,12u8,101u8,83u8,119u8,101u8,129u8,171u8,49u8,239u8,101u8,221u8,136u8,204u8,70u8,206u8, - 156u8,222u8,158u8,29u8,4u8,125u8,239u8,32u8,232u8,251u8,7u8,65u8,159u8,31u8,4u8,253u8,224u8,32u8,232u8,31u8, - 14u8,130u8,126u8,120u8,16u8,244u8,163u8,131u8,160u8,99u8,3u8,218u8,18u8,9u8,87u8,3u8,137u8,184u8,62u8,159u8, - 157u8,56u8,162u8,50u8,178u8,27u8,119u8,80u8,150u8,198u8,26u8,122u8,43u8,101u8,196u8,4u8,66u8,209u8,116u8,54u8, - 187u8,119u8,127u8,118u8,129u8,26u8,57u8,157u8,145u8,239u8,159u8,144u8,105u8,44u8,138u8,245u8,104u8,130u8,115u8,107u8, - 142u8,20u8,110u8,249u8,202u8,231u8,130u8,173u8,240u8,237u8,228u8,199u8,123u8,203u8,86u8,184u8,16u8,240u8,232u8,220u8, - 233u8,230u8,52u8,113u8,197u8,171u8,90u8,20u8,233u8,198u8,161u8,58u8,159u8,78u8,3u8,199u8,19u8,162u8,237u8,177u8, - 191u8,138u8,93u8,114u8,107u8,39u8,247u8,246u8,219u8,225u8,138u8,39u8,136u8,104u8,118u8,254u8,37u8,207u8,153u8,255u8, - 143u8,14u8,122u8,255u8,145u8,226u8,6u8,167u8,150u8,28u8,120u8,195u8,51u8,99u8,248u8,220u8,149u8,34u8,183u8,25u8, - 247u8,141u8,101u8,54u8,141u8,255u8,34u8,231u8,247u8,222u8,118u8,122u8,171u8,0u8,18u8,168u8,237u8,235u8,110u8,243u8, - 217u8,25u8,16u8,224u8,20u8,244u8,163u8,207u8,92u8,111u8,54u8,176u8,222u8,131u8,31u8,30u8,62u8,154u8,246u8,174u8, - 247u8,214u8,175u8,230u8,31u8,20u8,115u8,140u8,146u8,189u8,169u8,20u8,62u8,177u8,241u8,195u8,37u8,119u8,38u8,102u8, - 203u8,109u8,8u8,233u8,29u8,93u8,107u8,150u8,71u8,65u8,112u8,221u8,7u8,159u8,69u8,243u8,153u8,214u8,153u8,143u8, - 119u8,254u8,3u8,55u8,71u8,238u8,189u8,244u8,77u8,0u8,0u8,0u8,0u8,12u8,109u8,97u8,110u8,97u8,103u8,101u8, - 100u8,95u8,99u8,111u8,105u8,110u8,198u8,12u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,88u8, - 91u8,111u8,219u8,54u8,20u8,126u8,207u8,175u8,56u8,107u8,129u8,206u8,6u8,132u8,218u8,238u8,178u8,162u8,80u8,147u8, - 98u8,73u8,218u8,2u8,1u8,150u8,173u8,88u8,3u8,236u8,161u8,40u8,20u8,90u8,162u8,109u8,33u8,18u8,169u8,145u8, - 84u8,29u8,183u8,200u8,127u8,223u8,225u8,69u8,18u8,41u8,217u8,110u8,131u8,102u8,64u8,183u8,213u8,47u8,134u8,200u8, - 115u8,227u8,57u8,223u8,185u8,144u8,147u8,201u8,4u8,46u8,8u8,35u8,75u8,154u8,157u8,241u8,156u8,65u8,46u8,97u8, - 94u8,231u8,133u8,2u8,197u8,161u8,36u8,215u8,20u8,8u8,200u8,188u8,172u8,10u8,10u8,107u8,82u8,92u8,171u8,149u8, - 224u8,245u8,114u8,5u8,124u8,1u8,106u8,69u8,65u8,115u8,72u8,40u8,121u8,86u8,23u8,244u8,241u8,193u8,4u8,69u8, - 157u8,43u8,72u8,57u8,83u8,68u8,175u8,203u8,84u8,228u8,149u8,146u8,176u8,225u8,53u8,172u8,243u8,162u8,0u8,70u8, - 105u8,166u8,229u8,230u8,44u8,87u8,57u8,41u8,242u8,143u8,52u8,130u8,50u8,103u8,42u8,66u8,149u8,130u8,69u8,160u8, - 4u8,97u8,114u8,65u8,5u8,138u8,64u8,126u8,43u8,240u8,116u8,3u8,181u8,202u8,145u8,54u8,103u8,75u8,212u8,137u8, - 230u8,165u8,181u8,16u8,148u8,41u8,167u8,54u8,66u8,3u8,51u8,250u8,129u8,22u8,188u8,210u8,140u8,132u8,65u8,42u8, - 40u8,81u8,20u8,52u8,41u8,95u8,51u8,35u8,11u8,8u8,203u8,112u8,79u8,80u8,40u8,168u8,148u8,64u8,230u8,188u8, - 86u8,70u8,179u8,217u8,208u8,202u8,113u8,183u8,34u8,115u8,212u8,163u8,114u8,42u8,163u8,3u8,43u8,27u8,72u8,165u8, - 184u8,76u8,22u8,130u8,148u8,116u8,205u8,197u8,117u8,28u8,151u8,214u8,83u8,137u8,17u8,250u8,233u8,0u8,240u8,87u8, - 75u8,10u8,82u8,101u8,113u8,44u8,149u8,64u8,27u8,159u8,135u8,139u8,84u8,8u8,46u8,122u8,107u8,50u8,95u8,50u8, - 138u8,139u8,237u8,234u8,64u8,141u8,22u8,31u8,199u8,159u8,222u8,210u8,98u8,17u8,193u8,41u8,154u8,119u8,214u8,88u8, - 183u8,137u8,224u8,181u8,160u8,244u8,35u8,245u8,87u8,46u8,240u8,36u8,221u8,247u8,173u8,147u8,60u8,153u8,184u8,63u8, - 120u8,165u8,141u8,144u8,205u8,162u8,251u8,159u8,192u8,73u8,154u8,242u8,26u8,125u8,176u8,34u8,18u8,24u8,15u8,60u8, - 0u8,35u8,237u8,148u8,137u8,118u8,209u8,248u8,177u8,97u8,192u8,168u8,74u8,5u8,175u8,126u8,251u8,61u8,57u8,59u8, - 121u8,115u8,114u8,122u8,254u8,235u8,249u8,229u8,249u8,171u8,183u8,49u8,212u8,79u8,15u8,225u8,24u8,102u8,3u8,157u8, - 47u8,137u8,66u8,228u8,40u8,81u8,167u8,170u8,22u8,116u8,168u8,252u8,204u8,215u8,134u8,4u8,188u8,22u8,169u8,246u8, - 15u8,215u8,78u8,220u8,19u8,155u8,199u8,173u8,132u8,75u8,4u8,96u8,203u8,136u8,193u8,214u8,188u8,136u8,47u8,206u8, - 12u8,52u8,137u8,59u8,156u8,90u8,17u8,229u8,1u8,46u8,179u8,120u8,184u8,210u8,200u8,189u8,220u8,84u8,244u8,202u8, - 202u8,179u8,134u8,6u8,70u8,29u8,85u8,43u8,194u8,20u8,47u8,161u8,33u8,125u8,97u8,28u8,117u8,77u8,55u8,46u8, - 240u8,250u8,167u8,205u8,75u8,208u8,188u8,184u8,23u8,165u8,163u8,150u8,41u8,106u8,105u8,23u8,38u8,110u8,150u8,186u8, - 31u8,195u8,109u8,244u8,218u8,7u8,150u8,58u8,140u8,111u8,159u8,246u8,182u8,239u8,250u8,55u8,245u8,188u8,200u8,83u8, - 88u8,212u8,44u8,85u8,57u8,134u8,109u8,224u8,251u8,63u8,115u8,181u8,202u8,4u8,89u8,163u8,135u8,225u8,138u8,148u8, - 218u8,79u8,87u8,58u8,165u8,123u8,174u8,65u8,139u8,241u8,248u8,87u8,206u8,147u8,87u8,93u8,60u8,114u8,101u8,189u8, - 86u8,89u8,69u8,152u8,140u8,98u8,163u8,213u8,153u8,221u8,206u8,188u8,81u8,123u8,20u8,39u8,34u8,134u8,71u8,22u8, - 251u8,221u8,33u8,173u8,118u8,3u8,35u8,187u8,56u8,70u8,226u8,191u8,234u8,28u8,3u8,27u8,66u8,164u8,243u8,121u8, - 65u8,85u8,35u8,48u8,33u8,89u8,38u8,16u8,126u8,86u8,106u8,28u8,235u8,79u8,76u8,240u8,132u8,47u8,70u8,142u8, - 98u8,236u8,128u8,105u8,116u8,73u8,73u8,133u8,250u8,161u8,51u8,75u8,255u8,232u8,77u8,46u8,149u8,60u8,10u8,34u8, - 223u8,158u8,224u8,197u8,200u8,87u8,52u8,142u8,66u8,78u8,157u8,85u8,113u8,204u8,184u8,74u8,22u8,72u8,147u8,141u8, - 250u8,201u8,225u8,209u8,251u8,102u8,104u8,251u8,131u8,92u8,59u8,134u8,57u8,71u8,89u8,235u8,100u8,89u8,240u8,57u8, - 41u8,190u8,204u8,150u8,158u8,64u8,197u8,19u8,19u8,154u8,99u8,176u8,181u8,99u8,237u8,66u8,236u8,69u8,195u8,177u8, - 71u8,206u8,231u8,227u8,231u8,173u8,0u8,203u8,162u8,249u8,71u8,78u8,78u8,4u8,143u8,130u8,164u8,107u8,112u8,238u8, - 152u8,110u8,59u8,44u8,157u8,183u8,169u8,133u8,229u8,125u8,61u8,128u8,16u8,126u8,157u8,232u8,234u8,6u8,167u8,5u8, - 79u8,175u8,211u8,21u8,54u8,134u8,46u8,131u8,47u8,154u8,44u8,215u8,201u8,19u8,134u8,219u8,244u8,139u8,57u8,109u8, - 146u8,26u8,221u8,139u8,213u8,189u8,131u8,162u8,81u8,226u8,209u8,95u8,181u8,149u8,96u8,7u8,50u8,187u8,10u8,112u8, - 55u8,124u8,50u8,172u8,201u8,49u8,124u8,160u8,41u8,218u8,113u8,84u8,63u8,243u8,210u8,83u8,110u8,202u8,57u8,47u8, - 182u8,239u8,101u8,52u8,205u8,75u8,82u8,72u8,196u8,245u8,51u8,47u8,161u8,57u8,26u8,193u8,69u8,34u8,235u8,170u8, - 42u8,54u8,49u8,70u8,157u8,23u8,13u8,232u8,67u8,120u8,143u8,26u8,127u8,71u8,94u8,213u8,136u8,218u8,138u8,48u8, - 110u8,195u8,188u8,255u8,88u8,222u8,209u8,66u8,232u8,218u8,86u8,21u8,199u8,181u8,90u8,60u8,27u8,233u8,35u8,142u8, - 247u8,236u8,219u8,131u8,246u8,40u8,154u8,19u8,134u8,171u8,225u8,9u8,183u8,195u8,191u8,228u8,31u8,104u8,162u8,120u8, - 7u8,199u8,237u8,112u8,247u8,60u8,226u8,23u8,218u8,80u8,159u8,231u8,156u8,208u8,14u8,231u8,168u8,110u8,245u8,118u8, - 136u8,220u8,51u8,59u8,39u8,52u8,168u8,149u8,62u8,108u8,53u8,40u8,51u8,90u8,113u8,153u8,235u8,14u8,66u8,75u8, - 196u8,15u8,206u8,45u8,153u8,180u8,201u8,247u8,163u8,108u8,220u8,186u8,3u8,109u8,90u8,255u8,221u8,112u8,214u8,136u8, - 142u8,193u8,213u8,176u8,239u8,37u8,242u8,30u8,74u8,100u8,32u8,79u8,199u8,56u8,209u8,145u8,193u8,122u8,210u8,228u8, - 143u8,254u8,28u8,89u8,7u8,247u8,75u8,94u8,155u8,108u8,253u8,58u8,233u8,112u8,49u8,106u8,66u8,22u8,5u8,162u8, - 119u8,224u8,76u8,15u8,53u8,164u8,27u8,87u8,204u8,84u8,98u8,106u8,27u8,142u8,219u8,164u8,32u8,12u8,215u8,176u8, - 1u8,123u8,8u8,196u8,49u8,6u8,71u8,67u8,15u8,106u8,17u8,172u8,187u8,182u8,221u8,161u8,19u8,71u8,95u8,51u8, - 194u8,177u8,172u8,160u8,194u8,155u8,142u8,254u8,160u8,6u8,34u8,25u8,228u8,11u8,35u8,8u8,199u8,119u8,134u8,179u8, - 56u8,162u8,88u8,42u8,34u8,12u8,60u8,104u8,101u8,204u8,114u8,146u8,100u8,207u8,0u8,44u8,177u8,122u8,132u8,222u8, - 15u8,116u8,65u8,151u8,136u8,13u8,42u8,134u8,109u8,166u8,197u8,184u8,95u8,219u8,172u8,3u8,119u8,51u8,245u8,157u8, - 215u8,76u8,52u8,151u8,20u8,1u8,24u8,140u8,49u8,15u8,223u8,41u8,92u8,75u8,56u8,43u8,54u8,239u8,195u8,233u8, - 154u8,87u8,122u8,232u8,121u8,190u8,135u8,108u8,48u8,110u8,147u8,229u8,18u8,109u8,34u8,186u8,114u8,45u8,136u8,174u8, - 230u8,155u8,93u8,236u8,110u8,78u8,124u8,141u8,23u8,163u8,11u8,206u8,244u8,40u8,120u8,235u8,83u8,142u8,92u8,120u8, - 143u8,225u8,151u8,233u8,13u8,153u8,205u8,82u8,188u8,158u8,100u8,184u8,158u8,51u8,162u8,141u8,178u8,235u8,243u8,233u8, - 60u8,210u8,151u8,151u8,164u8,25u8,81u8,205u8,234u8,108u8,252u8,126u8,187u8,139u8,141u8,5u8,148u8,101u8,88u8,51u8, - 245u8,95u8,151u8,154u8,86u8,87u8,12u8,131u8,82u8,210u8,41u8,28u8,110u8,122u8,122u8,155u8,205u8,59u8,212u8,20u8, - 171u8,114u8,79u8,73u8,121u8,100u8,41u8,122u8,169u8,231u8,89u8,180u8,143u8,215u8,35u8,243u8,4u8,12u8,163u8,229u8, - 204u8,143u8,237u8,69u8,175u8,57u8,14u8,86u8,24u8,145u8,120u8,65u8,232u8,215u8,128u8,187u8,203u8,233u8,155u8,253u8, - 117u8,70u8,109u8,57u8,176u8,23u8,140u8,177u8,47u8,124u8,0u8,71u8,191u8,209u8,39u8,195u8,237u8,78u8,75u8,32u8, - 210u8,43u8,168u8,222u8,156u8,208u8,98u8,183u8,55u8,40u8,248u8,172u8,97u8,21u8,159u8,63u8,208u8,60u8,96u8,152u8, - 30u8,12u8,182u8,46u8,94u8,246u8,214u8,102u8,211u8,240u8,27u8,83u8,134u8,250u8,85u8,190u8,223u8,107u8,220u8,36u8, - 35u8,205u8,165u8,58u8,241u8,174u8,106u8,190u8,169u8,227u8,8u8,166u8,254u8,129u8,122u8,149u8,196u8,163u8,236u8,185u8, - 160u8,97u8,216u8,74u8,58u8,64u8,235u8,86u8,170u8,16u8,151u8,193u8,253u8,108u8,151u8,226u8,200u8,79u8,149u8,8u8, - 126u8,158u8,122u8,58u8,246u8,243u8,245u8,65u8,23u8,161u8,63u8,119u8,58u8,205u8,181u8,14u8,95u8,154u8,15u8,126u8, - 56u8,62u8,70u8,213u8,40u8,225u8,46u8,2u8,6u8,168u8,215u8,82u8,48u8,166u8,240u8,164u8,223u8,161u8,237u8,148u8, - 215u8,246u8,82u8,251u8,25u8,4u8,109u8,168u8,214u8,86u8,103u8,19u8,110u8,201u8,75u8,138u8,33u8,48u8,92u8,227u8, - 237u8,70u8,54u8,212u8,244u8,6u8,159u8,134u8,82u8,13u8,239u8,186u8,81u8,107u8,204u8,122u8,218u8,55u8,203u8,90u8, - 210u8,188u8,35u8,109u8,137u8,245u8,253u8,59u8,248u8,16u8,109u8,248u8,233u8,171u8,29u8,252u8,4u8,165u8,28u8,222u8, - 241u8,36u8,159u8,43u8,41u8,40u8,209u8,63u8,153u8,185u8,156u8,239u8,196u8,221u8,225u8,116u8,219u8,28u8,248u8,197u8, - 94u8,112u8,40u8,11u8,240u8,129u8,147u8,117u8,242u8,53u8,24u8,9u8,162u8,222u8,9u8,107u8,253u8,245u8,36u8,28u8, - 23u8,238u8,173u8,5u8,63u8,124u8,71u8,111u8,42u8,188u8,213u8,225u8,59u8,223u8,130u8,228u8,5u8,62u8,95u8,141u8, - 240u8,197u8,80u8,224u8,52u8,200u8,51u8,45u8,120u8,122u8,243u8,116u8,58u8,157u8,206u8,34u8,192u8,43u8,109u8,35u8, - 84u8,63u8,213u8,237u8,234u8,224u8,90u8,136u8,153u8,14u8,239u8,191u8,119u8,71u8,255u8,76u8,243u8,254u8,6u8,154u8, - 239u8,231u8,6u8,132u8,255u8,92u8,51u8,14u8,211u8,209u8,245u8,220u8,210u8,246u8,220u8,166u8,207u8,194u8,44u8,50u8, - 237u8,116u8,112u8,39u8,249u8,182u8,26u8,161u8,71u8,215u8,107u8,132u8,179u8,233u8,244u8,223u8,148u8,180u8,230u8,73u8, - 236u8,123u8,210u8,126u8,79u8,218u8,255u8,65u8,210u8,238u8,158u8,94u8,187u8,164u8,221u8,58u8,70u8,4u8,217u8,62u8, - 235u8,242u8,251u8,246u8,224u8,111u8,178u8,158u8,122u8,130u8,92u8,28u8,0u8,0u8,0u8,0u8,16u8,109u8,117u8,108u8, - 116u8,105u8,115u8,105u8,103u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,212u8,92u8,31u8,139u8,8u8,0u8,0u8, - 0u8,0u8,0u8,2u8,255u8,237u8,125u8,107u8,115u8,219u8,86u8,146u8,232u8,247u8,252u8,10u8,56u8,83u8,37u8,147u8, - 185u8,180u8,34u8,74u8,113u8,118u8,86u8,182u8,83u8,67u8,203u8,202u8,196u8,123u8,45u8,201u8,87u8,146u8,179u8,149u8, - 74u8,185u8,176u8,16u8,9u8,74u8,184u8,166u8,0u8,46u8,0u8,90u8,86u8,178u8,254u8,239u8,219u8,221u8,231u8,253u8, - 2u8,193u8,135u8,108u8,105u8,66u8,215u8,84u8,198u8,38u8,128u8,243u8,232u8,211u8,239u8,238u8,211u8,253u8,253u8,247u8, - 223u8,71u8,135u8,249u8,85u8,146u8,15u8,211u8,81u8,116u8,61u8,155u8,212u8,89u8,149u8,93u8,70u8,201u8,112u8,88u8, - 204u8,242u8,58u8,170u8,234u8,36u8,31u8,37u8,229u8,40u8,42u8,242u8,104u8,48u8,173u8,139u8,106u8,59u8,58u8,191u8, - 202u8,170u8,8u8,254u8,55u8,202u8,198u8,227u8,180u8,76u8,225u8,165u8,113u8,89u8,92u8,71u8,245u8,85u8,26u8,229u8, - 73u8,157u8,125u8,76u8,213u8,40u8,213u8,240u8,42u8,189u8,78u8,163u8,106u8,54u8,157u8,22u8,101u8,29u8,165u8,249u8, - 184u8,40u8,113u8,154u8,143u8,89u8,242u8,205u8,247u8,48u8,47u8,126u8,195u8,103u8,122u8,92u8,69u8,201u8,172u8,190u8, - 138u8,62u8,164u8,183u8,219u8,248u8,140u8,158u8,211u8,84u8,215u8,197u8,104u8,54u8,129u8,247u8,38u8,147u8,226u8,166u8, - 138u8,134u8,101u8,10u8,147u8,228u8,176u8,194u8,104u8,60u8,73u8,63u8,101u8,23u8,248u8,40u8,31u8,69u8,211u8,226u8, - 38u8,45u8,199u8,179u8,137u8,187u8,133u8,155u8,12u8,134u8,173u8,210u8,228u8,122u8,146u8,86u8,149u8,92u8,11u8,172u8, - 36u8,154u8,77u8,71u8,108u8,172u8,226u8,38u8,79u8,203u8,138u8,166u8,196u8,183u8,139u8,89u8,29u8,13u8,1u8,34u8, - 151u8,248u8,140u8,214u8,40u8,86u8,22u8,189u8,171u8,224u8,197u8,104u8,152u8,228u8,240u8,66u8,81u8,84u8,105u8,84u8, - 23u8,0u8,163u8,162u8,132u8,191u8,148u8,73u8,94u8,37u8,195u8,58u8,3u8,72u8,77u8,147u8,219u8,73u8,145u8,140u8, - 170u8,232u8,38u8,201u8,104u8,2u8,156u8,141u8,38u8,137u8,96u8,101u8,0u8,165u8,89u8,153u8,86u8,8u8,82u8,152u8, - 37u8,203u8,105u8,94u8,124u8,97u8,60u8,102u8,63u8,68u8,157u8,105u8,153u8,93u8,39u8,229u8,109u8,52u8,44u8,242u8, - 42u8,27u8,165u8,101u8,66u8,195u8,34u8,208u8,211u8,33u8,64u8,188u8,76u8,38u8,217u8,31u8,244u8,219u8,247u8,52u8, - 237u8,52u8,129u8,115u8,24u8,222u8,70u8,31u8,171u8,232u8,50u8,129u8,229u8,21u8,85u8,221u8,213u8,161u8,152u8,186u8, - 80u8,129u8,177u8,146u8,8u8,86u8,81u8,204u8,224u8,68u8,228u8,175u8,179u8,28u8,38u8,203u8,1u8,194u8,87u8,219u8, - 209u8,203u8,91u8,152u8,109u8,156u8,192u8,135u8,189u8,40u8,171u8,163u8,43u8,24u8,56u8,47u8,36u8,40u8,8u8,232u8, - 8u8,135u8,34u8,159u8,220u8,70u8,23u8,41u8,46u8,181u8,46u8,139u8,201u8,196u8,58u8,222u8,106u8,154u8,14u8,179u8, - 68u8,59u8,22u8,29u8,78u8,99u8,56u8,210u8,237u8,232u8,23u8,56u8,186u8,143u8,105u8,217u8,227u8,199u8,192u8,160u8, - 139u8,199u8,12u8,235u8,50u8,94u8,7u8,88u8,211u8,177u8,164u8,198u8,161u8,224u8,207u8,215u8,73u8,61u8,188u8,138u8, - 216u8,172u8,0u8,187u8,143u8,248u8,45u8,62u8,82u8,32u8,205u8,198u8,81u8,85u8,192u8,134u8,170u8,172u8,76u8,71u8, - 26u8,108u8,212u8,240u8,176u8,189u8,20u8,22u8,15u8,163u8,193u8,110u8,210u8,79u8,233u8,112u8,86u8,195u8,63u8,225u8, - 211u8,162u8,4u8,160u8,192u8,80u8,28u8,247u8,138u8,188u8,7u8,167u8,120u8,157u8,77u8,146u8,18u8,95u8,174u8,245u8, - 17u8,240u8,156u8,19u8,0u8,83u8,121u8,13u8,91u8,38u8,122u8,145u8,160u8,237u8,8u8,236u8,151u8,104u8,38u8,31u8, - 229u8,5u8,16u8,159u8,117u8,96u8,8u8,26u8,60u8,164u8,73u8,246u8,33u8,133u8,5u8,193u8,191u8,246u8,233u8,89u8, - 127u8,59u8,58u8,241u8,128u8,41u8,79u8,111u8,220u8,51u8,190u8,0u8,4u8,2u8,178u8,65u8,4u8,228u8,47u8,118u8, - 16u8,253u8,96u8,51u8,132u8,73u8,116u8,182u8,176u8,149u8,252u8,18u8,104u8,136u8,96u8,223u8,69u8,52u8,196u8,165u8, - 209u8,84u8,236u8,155u8,24u8,255u8,29u8,243u8,163u8,185u8,185u8,2u8,138u8,103u8,19u8,77u8,225u8,163u8,44u8,7u8, - 244u8,134u8,157u8,50u8,212u8,78u8,70u8,35u8,64u8,168u8,42u8,101u8,75u8,187u8,224u8,103u8,63u8,206u8,0u8,222u8, - 30u8,166u8,209u8,65u8,4u8,74u8,147u8,42u8,195u8,105u8,145u8,131u8,72u8,148u8,9u8,112u8,17u8,54u8,55u8,190u8, - 192u8,22u8,243u8,56u8,154u8,206u8,46u8,38u8,217u8,16u8,15u8,186u8,2u8,244u8,252u8,152u8,242u8,179u8,211u8,166u8, - 253u8,5u8,62u8,233u8,49u8,20u8,85u8,171u8,3u8,98u8,161u8,163u8,230u8,120u8,16u8,237u8,26u8,32u8,133u8,1u8, - 224u8,213u8,116u8,244u8,125u8,153u8,94u8,23u8,31u8,1u8,3u8,146u8,28u8,80u8,44u8,131u8,5u8,104u8,224u8,132u8, - 55u8,4u8,72u8,0u8,98u8,236u8,77u8,254u8,195u8,54u8,29u8,159u8,129u8,21u8,176u8,172u8,17u8,178u8,137u8,108u8, - 50u8,161u8,137u8,217u8,78u8,11u8,192u8,22u8,228u8,105u8,180u8,165u8,15u8,79u8,138u8,241u8,147u8,92u8,50u8,76u8, - 177u8,3u8,66u8,168u8,218u8,67u8,191u8,108u8,229u8,123u8,48u8,89u8,97u8,34u8,129u8,54u8,113u8,47u8,66u8,242u8, - 164u8,163u8,33u8,92u8,129u8,181u8,139u8,51u8,213u8,169u8,138u8,80u8,177u8,190u8,242u8,114u8,48u8,126u8,112u8,55u8, - 184u8,114u8,98u8,115u8,242u8,144u8,128u8,213u8,78u8,124u8,95u8,72u8,174u8,214u8,131u8,3u8,203u8,144u8,40u8,71u8, - 35u8,151u8,109u8,69u8,29u8,248u8,119u8,85u8,148u8,213u8,85u8,54u8,69u8,180u8,200u8,139u8,26u8,56u8,120u8,85u8, - 49u8,118u8,94u8,209u8,12u8,192u8,155u8,19u8,120u8,70u8,51u8,38u8,31u8,19u8,160u8,55u8,124u8,40u8,134u8,239u8, - 18u8,3u8,186u8,78u8,62u8,192u8,129u8,2u8,119u8,98u8,136u8,68u8,64u8,77u8,137u8,21u8,152u8,171u8,51u8,57u8, - 49u8,35u8,110u8,248u8,121u8,59u8,122u8,13u8,108u8,33u8,249u8,136u8,15u8,144u8,113u8,18u8,89u8,16u8,135u8,232u8, - 177u8,105u8,117u8,248u8,37u8,147u8,26u8,120u8,35u8,225u8,230u8,228u8,54u8,4u8,77u8,70u8,45u8,192u8,43u8,175u8, - 56u8,190u8,18u8,238u8,225u8,118u8,4u8,124u8,232u8,25u8,76u8,68u8,224u8,4u8,0u8,191u8,129u8,17u8,180u8,37u8, - 113u8,238u8,0u8,131u8,3u8,34u8,2u8,87u8,100u8,104u8,48u8,171u8,132u8,40u8,194u8,207u8,1u8,95u8,113u8,84u8, - 151u8,93u8,26u8,59u8,70u8,248u8,152u8,252u8,51u8,27u8,69u8,157u8,44u8,135u8,119u8,175u8,83u8,130u8,69u8,54u8, - 234u8,138u8,169u8,216u8,118u8,43u8,98u8,15u8,156u8,44u8,126u8,32u8,228u8,74u8,166u8,211u8,18u8,144u8,155u8,161u8, - 249u8,255u8,79u8,135u8,181u8,57u8,38u8,16u8,23u8,172u8,170u8,52u8,214u8,130u8,144u8,225u8,159u8,117u8,186u8,234u8, - 195u8,78u8,215u8,143u8,106u8,25u8,159u8,239u8,41u8,157u8,70u8,77u8,80u8,67u8,18u8,77u8,243u8,98u8,118u8,121u8, - 197u8,71u8,74u8,38u8,85u8,143u8,40u8,81u8,157u8,7u8,103u8,209u8,206u8,120u8,10u8,86u8,66u8,246u8,28u8,113u8, - 226u8,57u8,215u8,101u8,201u8,237u8,52u8,85u8,156u8,216u8,93u8,19u8,10u8,12u8,137u8,231u8,226u8,236u8,80u8,104u8, - 78u8,0u8,212u8,163u8,91u8,126u8,124u8,18u8,29u8,5u8,227u8,12u8,17u8,18u8,14u8,199u8,216u8,16u8,77u8,106u8, - 35u8,129u8,190u8,50u8,137u8,10u8,236u8,108u8,198u8,89u8,89u8,161u8,58u8,146u8,14u8,63u8,136u8,25u8,148u8,82u8, - 84u8,95u8,37u8,117u8,112u8,74u8,148u8,216u8,151u8,69u8,93u8,167u8,12u8,169u8,56u8,60u8,149u8,2u8,194u8,176u8, - 191u8,32u8,233u8,46u8,48u8,78u8,202u8,61u8,78u8,132u8,14u8,223u8,33u8,238u8,198u8,14u8,225u8,230u8,170u8,16u8, - 239u8,115u8,22u8,1u8,51u8,19u8,129u8,1u8,45u8,177u8,51u8,253u8,209u8,127u8,166u8,12u8,33u8,16u8,75u8,237u8, - 67u8,29u8,103u8,57u8,50u8,9u8,118u8,170u8,242u8,53u8,157u8,245u8,242u8,25u8,99u8,246u8,48u8,29u8,233u8,212u8, - 215u8,209u8,132u8,232u8,113u8,81u8,75u8,248u8,32u8,192u8,108u8,225u8,8u8,16u8,76u8,39u8,130u8,247u8,32u8,213u8, - 35u8,230u8,35u8,3u8,153u8,129u8,86u8,199u8,196u8,115u8,4u8,34u8,30u8,148u8,141u8,124u8,118u8,125u8,193u8,20u8, - 0u8,157u8,195u8,95u8,163u8,210u8,199u8,209u8,158u8,54u8,200u8,72u8,8u8,229u8,77u8,45u8,30u8,167u8,159u8,166u8, - 192u8,230u8,80u8,152u8,125u8,44u8,152u8,162u8,153u8,91u8,92u8,137u8,129u8,125u8,88u8,92u8,167u8,4u8,169u8,208u8, - 148u8,130u8,51u8,225u8,26u8,65u8,251u8,152u8,33u8,135u8,171u8,80u8,55u8,97u8,232u8,52u8,158u8,192u8,54u8,47u8, - 129u8,222u8,128u8,63u8,129u8,42u8,1u8,104u8,85u8,206u8,134u8,120u8,198u8,189u8,104u8,56u8,201u8,128u8,243u8,86u8, - 28u8,250u8,176u8,245u8,50u8,185u8,100u8,251u8,188u8,41u8,51u8,70u8,61u8,25u8,145u8,47u8,71u8,41u8,82u8,75u8, - 235u8,98u8,138u8,115u8,7u8,1u8,7u8,239u8,113u8,254u8,8u8,136u8,125u8,13u8,154u8,192u8,53u8,74u8,116u8,220u8, - 182u8,182u8,8u8,190u8,229u8,73u8,113u8,9u8,50u8,154u8,13u8,186u8,253u8,141u8,80u8,230u8,81u8,53u8,138u8,199u8, - 101u8,114u8,157u8,222u8,20u8,229u8,135u8,253u8,125u8,49u8,75u8,44u8,102u8,249u8,243u8,155u8,8u8,254u8,224u8,89u8, - 56u8,239u8,242u8,87u8,246u8,247u8,255u8,60u8,75u8,39u8,227u8,94u8,116u8,70u8,58u8,205u8,65u8,50u8,77u8,46u8, - 178u8,73u8,86u8,223u8,246u8,80u8,16u8,198u8,160u8,82u8,230u8,53u8,112u8,226u8,124u8,52u8,65u8,48u8,48u8,78u8, - 45u8,20u8,222u8,152u8,107u8,3u8,159u8,159u8,53u8,76u8,67u8,63u8,12u8,139u8,44u8,223u8,223u8,39u8,85u8,238u8, - 0u8,254u8,218u8,240u8,62u8,241u8,129u8,56u8,27u8,53u8,189u8,194u8,86u8,193u8,148u8,48u8,235u8,159u8,77u8,159u8, - 53u8,79u8,76u8,59u8,5u8,104u8,28u8,226u8,255u8,255u8,194u8,55u8,156u8,94u8,103u8,53u8,131u8,65u8,211u8,30u8, - 81u8,175u8,1u8,19u8,239u8,122u8,186u8,191u8,159u8,23u8,55u8,113u8,5u8,152u8,152u8,143u8,42u8,251u8,131u8,170u8, - 30u8,237u8,239u8,87u8,116u8,208u8,241u8,117u8,50u8,213u8,0u8,143u8,63u8,29u8,37u8,211u8,207u8,222u8,47u8,106u8, - 20u8,216u8,242u8,229u8,115u8,252u8,151u8,246u8,34u8,189u8,114u8,49u8,172u8,224u8,189u8,34u8,190u8,184u8,5u8,30u8, - 98u8,61u8,75u8,203u8,178u8,40u8,173u8,223u8,144u8,111u8,194u8,82u8,174u8,146u8,189u8,120u8,247u8,233u8,143u8,214u8, - 195u8,98u8,138u8,68u8,37u8,39u8,60u8,161u8,127u8,218u8,51u8,10u8,216u8,115u8,4u8,136u8,139u8,177u8,253u8,66u8, - 93u8,2u8,218u8,238u8,239u8,159u8,209u8,255u8,91u8,15u8,63u8,2u8,203u8,193u8,69u8,209u8,175u8,66u8,85u8,175u8, - 64u8,51u8,192u8,87u8,136u8,170u8,164u8,50u8,230u8,216u8,86u8,163u8,25u8,14u8,232u8,210u8,147u8,176u8,42u8,182u8, - 181u8,65u8,153u8,178u8,44u8,198u8,76u8,62u8,22u8,32u8,144u8,224u8,104u8,198u8,160u8,238u8,214u8,21u8,227u8,78u8, - 76u8,238u8,10u8,162u8,37u8,70u8,7u8,66u8,82u8,46u8,192u8,158u8,190u8,82u8,226u8,169u8,130u8,227u8,103u8,124u8, - 69u8,206u8,40u8,181u8,74u8,252u8,1u8,141u8,206u8,58u8,122u8,117u8,114u8,52u8,120u8,125u8,28u8,159u8,29u8,190u8, - 29u8,156u8,14u8,206u8,79u8,78u8,247u8,35u8,182u8,249u8,231u8,179u8,191u8,255u8,20u8,189u8,136u8,46u8,190u8,157u8, - 75u8,195u8,223u8,74u8,56u8,69u8,3u8,96u8,242u8,116u8,158u8,48u8,56u8,240u8,177u8,232u8,167u8,104u8,119u8,103u8, - 103u8,71u8,232u8,219u8,245u8,85u8,137u8,12u8,8u8,56u8,26u8,24u8,178u8,53u8,113u8,30u8,93u8,156u8,129u8,85u8, - 89u8,92u8,206u8,82u8,5u8,31u8,210u8,214u8,193u8,50u8,66u8,185u8,152u8,228u8,200u8,191u8,209u8,248u8,68u8,41u8, - 44u8,183u8,199u8,143u8,151u8,177u8,97u8,0u8,14u8,218u8,169u8,195u8,84u8,223u8,223u8,225u8,171u8,119u8,111u8,223u8, - 188u8,62u8,24u8,156u8,31u8,198u8,39u8,255u8,121u8,124u8,8u8,251u8,155u8,253u8,248u8,3u8,108u8,172u8,255u8,76u8, - 206u8,115u8,38u8,213u8,112u8,205u8,106u8,198u8,233u8,18u8,143u8,62u8,174u8,141u8,60u8,56u8,56u8,56u8,121u8,119u8, - 124u8,30u8,31u8,159u8,156u8,199u8,71u8,239u8,222u8,156u8,191u8,62u8,123u8,253u8,79u8,49u8,60u8,236u8,123u8,87u8, - 205u8,48u8,224u8,227u8,114u8,153u8,79u8,122u8,11u8,204u8,81u8,76u8,53u8,147u8,159u8,38u8,20u8,138u8,104u8,49u8, - 14u8,152u8,3u8,218u8,244u8,56u8,173u8,177u8,37u8,152u8,115u8,79u8,205u8,121u8,238u8,81u8,21u8,56u8,24u8,81u8, - 1u8,184u8,158u8,214u8,183u8,198u8,112u8,111u8,7u8,191u8,189u8,57u8,25u8,188u8,138u8,15u8,6u8,199u8,56u8,242u8, - 203u8,195u8,248u8,240u8,232u8,237u8,249u8,111u8,98u8,240u8,31u8,212u8,200u8,71u8,142u8,168u8,152u8,85u8,53u8,179u8, - 204u8,0u8,59u8,39u8,160u8,163u8,195u8,225u8,230u8,28u8,247u8,156u8,37u8,195u8,127u8,222u8,253u8,243u8,23u8,182u8, - 242u8,51u8,49u8,250u8,83u8,255u8,186u8,153u8,79u8,71u8,30u8,79u8,166u8,239u8,96u8,12u8,83u8,143u8,140u8,209u8, - 207u8,79u8,7u8,199u8,103u8,131u8,131u8,243u8,215u8,39u8,199u8,116u8,38u8,63u8,195u8,233u8,188u8,210u8,128u8,243u8, - 163u8,154u8,228u8,45u8,232u8,154u8,224u8,112u8,1u8,178u8,67u8,57u8,12u8,110u8,162u8,89u8,206u8,102u8,28u8,21u8, - 41u8,59u8,10u8,230u8,106u8,16u8,170u8,184u8,208u8,3u8,57u8,238u8,21u8,249u8,19u8,166u8,14u8,106u8,8u8,236u8, - 5u8,230u8,171u8,147u8,195u8,51u8,134u8,31u8,131u8,243u8,131u8,95u8,226u8,95u8,6u8,103u8,191u8,104u8,11u8,250u8, - 187u8,127u8,215u8,204u8,5u8,83u8,3u8,109u8,15u8,211u8,12u8,77u8,83u8,91u8,69u8,182u8,156u8,23u8,33u8,16u8, - 15u8,222u8,190u8,61u8,61u8,249u8,117u8,240u8,230u8,76u8,155u8,242u8,223u8,23u8,155u8,82u8,105u8,112u8,124u8,78u8, - 240u8,180u8,100u8,168u8,108u8,131u8,138u8,43u8,52u8,179u8,208u8,244u8,167u8,135u8,255u8,113u8,72u8,135u8,33u8,231u8, - 239u8,239u8,168u8,217u8,143u8,165u8,234u8,163u8,185u8,202u8,202u8,244u8,191u8,103u8,104u8,150u8,49u8,140u8,186u8,72u8, - 53u8,186u8,254u8,35u8,45u8,11u8,102u8,235u8,160u8,106u8,82u8,113u8,117u8,184u8,168u8,65u8,239u8,119u8,20u8,55u8, - 125u8,65u8,175u8,143u8,1u8,2u8,175u8,95u8,197u8,64u8,155u8,199u8,131u8,243u8,119u8,167u8,112u8,30u8,167u8,135u8, - 255u8,239u8,221u8,235u8,211u8,67u8,137u8,26u8,125u8,141u8,23u8,188u8,213u8,205u8,55u8,177u8,138u8,244u8,19u8,128u8, - 9u8,118u8,188u8,183u8,27u8,145u8,4u8,3u8,255u8,10u8,136u8,166u8,39u8,32u8,154u8,186u8,222u8,185u8,196u8,233u8, - 235u8,199u8,221u8,215u8,216u8,129u8,223u8,83u8,87u8,87u8,32u8,207u8,52u8,4u8,23u8,204u8,192u8,152u8,130u8,200u8, - 70u8,163u8,81u8,193u8,119u8,98u8,193u8,140u8,94u8,159u8,159u8,29u8,190u8,249u8,89u8,206u8,186u8,23u8,38u8,219u8, - 74u8,158u8,250u8,69u8,154u8,130u8,61u8,146u8,163u8,228u8,38u8,83u8,135u8,152u8,211u8,112u8,86u8,146u8,3u8,39u8, - 79u8,107u8,228u8,251u8,209u8,109u8,106u8,178u8,31u8,123u8,102u8,134u8,231u8,135u8,199u8,131u8,151u8,111u8,14u8,95u8, - 197u8,191u8,29u8,158u8,203u8,53u8,252u8,96u8,238u8,92u8,29u8,23u8,216u8,167u8,9u8,153u8,255u8,228u8,218u8,193u8, - 195u8,5u8,236u8,158u8,1u8,124u8,71u8,69u8,254u8,152u8,147u8,160u8,137u8,93u8,239u8,142u8,94u8,194u8,246u8,79u8, - 126u8,142u8,143u8,14u8,207u8,7u8,175u8,6u8,231u8,131u8,248u8,255u8,30u8,254u8,118u8,22u8,15u8,142u8,95u8,197u8, - 0u8,250u8,119u8,112u8,184u8,175u8,78u8,142u8,57u8,181u8,201u8,249u8,159u8,154u8,243u8,43u8,126u8,34u8,231u8,231u8, - 114u8,5u8,38u8,158u8,77u8,65u8,246u8,146u8,100u8,175u8,65u8,49u8,184u8,32u8,155u8,167u8,131u8,203u8,235u8,6u8, - 132u8,138u8,190u8,16u8,57u8,227u8,143u8,214u8,140u8,128u8,214u8,41u8,234u8,202u8,124u8,235u8,83u8,193u8,125u8,80u8, - 254u8,231u8,176u8,103u8,176u8,141u8,163u8,215u8,181u8,196u8,183u8,11u8,0u8,57u8,30u8,201u8,239u8,125u8,212u8,113u8, - 63u8,129u8,207u8,36u8,205u8,71u8,36u8,53u8,52u8,146u8,125u8,18u8,245u8,223u8,251u8,49u8,29u8,208u8,251u8,240u8, - 248u8,224u8,48u8,102u8,208u8,146u8,139u8,250u8,55u8,77u8,159u8,57u8,77u8,167u8,64u8,111u8,204u8,98u8,112u8,80u8, - 241u8,113u8,69u8,74u8,72u8,118u8,57u8,99u8,210u8,137u8,29u8,141u8,110u8,208u8,88u8,58u8,140u8,176u8,42u8,77u8, - 38u8,233u8,96u8,120u8,135u8,233u8,44u8,100u8,116u8,122u8,20u8,167u8,42u8,5u8,181u8,0u8,65u8,79u8,113u8,6u8, - 101u8,47u8,10u8,116u8,229u8,39u8,192u8,140u8,30u8,137u8,205u8,66u8,180u8,34u8,46u8,163u8,43u8,152u8,25u8,20u8, - 92u8,31u8,65u8,224u8,147u8,254u8,0u8,200u8,134u8,238u8,10u8,203u8,129u8,185u8,45u8,223u8,101u8,140u8,67u8,234u8, - 62u8,252u8,141u8,159u8,122u8,246u8,96u8,121u8,35u8,215u8,2u8,22u8,57u8,5u8,231u8,138u8,229u8,142u8,233u8,124u8, - 64u8,136u8,48u8,207u8,95u8,87u8,77u8,9u8,67u8,197u8,106u8,144u8,88u8,12u8,66u8,199u8,101u8,204u8,11u8,138u8, - 55u8,15u8,189u8,248u8,125u8,60u8,185u8,116u8,242u8,216u8,110u8,234u8,90u8,26u8,238u8,220u8,193u8,232u8,177u8,243u8, - 182u8,245u8,169u8,6u8,220u8,235u8,33u8,221u8,3u8,166u8,167u8,9u8,120u8,49u8,24u8,211u8,105u8,205u8,182u8,90u8, - 161u8,192u8,135u8,165u8,224u8,169u8,131u8,177u8,25u8,93u8,204u8,106u8,238u8,64u8,187u8,73u8,128u8,156u8,47u8,232u8, - 100u8,17u8,206u8,20u8,41u8,136u8,200u8,72u8,209u8,64u8,174u8,15u8,189u8,207u8,204u8,134u8,231u8,184u8,117u8,159u8, - 27u8,199u8,61u8,8u8,155u8,164u8,132u8,79u8,139u8,96u8,128u8,167u8,142u8,74u8,136u8,220u8,134u8,116u8,78u8,153u8, - 91u8,162u8,56u8,15u8,125u8,194u8,157u8,246u8,112u8,80u8,79u8,40u8,12u8,160u8,79u8,39u8,29u8,53u8,21u8,158u8, - 59u8,208u8,46u8,120u8,48u8,201u8,79u8,165u8,69u8,7u8,236u8,229u8,24u8,1u8,130u8,14u8,243u8,189u8,63u8,1u8, - 61u8,31u8,124u8,224u8,14u8,220u8,113u8,169u8,177u8,88u8,106u8,44u8,70u8,138u8,217u8,72u8,46u8,58u8,248u8,118u8, - 143u8,198u8,3u8,1u8,64u8,108u8,159u8,184u8,134u8,177u8,83u8,97u8,108u8,144u8,154u8,201u8,78u8,168u8,113u8,226u8, - 232u8,255u8,68u8,125u8,125u8,218u8,132u8,187u8,63u8,132u8,50u8,47u8,131u8,3u8,30u8,222u8,196u8,93u8,39u8,138u8, - 88u8,124u8,239u8,68u8,21u8,4u8,224u8,38u8,35u8,146u8,175u8,255u8,61u8,3u8,48u8,213u8,133u8,62u8,31u8,238u8, - 192u8,89u8,211u8,147u8,168u8,51u8,119u8,209u8,58u8,137u8,121u8,198u8,8u8,0u8,148u8,197u8,75u8,134u8,210u8,185u8, - 32u8,227u8,92u8,194u8,201u8,40u8,233u8,166u8,35u8,24u8,87u8,87u8,119u8,150u8,101u8,50u8,170u8,144u8,126u8,98u8, - 193u8,43u8,229u8,212u8,103u8,131u8,27u8,132u8,118u8,192u8,132u8,43u8,232u8,20u8,120u8,30u8,100u8,0u8,114u8,79u8, - 156u8,207u8,137u8,137u8,35u8,147u8,148u8,32u8,193u8,132u8,1u8,57u8,97u8,125u8,242u8,117u8,143u8,128u8,115u8,144u8, - 134u8,194u8,89u8,239u8,175u8,71u8,72u8,141u8,250u8,132u8,181u8,182u8,66u8,152u8,14u8,35u8,168u8,128u8,42u8,164u8, - 179u8,83u8,208u8,82u8,40u8,180u8,224u8,162u8,66u8,244u8,230u8,64u8,224u8,195u8,141u8,103u8,200u8,163u8,212u8,6u8, - 216u8,172u8,49u8,64u8,107u8,159u8,155u8,223u8,207u8,109u8,231u8,140u8,75u8,177u8,30u8,41u8,35u8,229u8,175u8,240u8, - 116u8,229u8,96u8,211u8,245u8,208u8,3u8,54u8,44u8,179u8,41u8,243u8,59u8,131u8,155u8,223u8,132u8,47u8,69u8,115u8, - 145u8,106u8,193u8,154u8,68u8,21u8,85u8,63u8,28u8,99u8,199u8,86u8,0u8,18u8,248u8,229u8,54u8,120u8,223u8,80u8, - 201u8,122u8,194u8,198u8,232u8,26u8,71u8,130u8,142u8,196u8,253u8,104u8,160u8,196u8,190u8,136u8,18u8,149u8,23u8,25u8, - 140u8,85u8,102u8,0u8,222u8,42u8,165u8,104u8,155u8,87u8,186u8,145u8,140u8,188u8,154u8,113u8,105u8,40u8,194u8,165u8, - 116u8,180u8,99u8,22u8,245u8,101u8,32u8,214u8,231u8,28u8,101u8,213u8,116u8,2u8,206u8,212u8,233u8,172u8,4u8,168u8, - 147u8,83u8,110u8,114u8,75u8,196u8,115u8,203u8,117u8,32u8,30u8,8u8,37u8,105u8,40u8,206u8,168u8,74u8,175u8,19u8, - 96u8,250u8,195u8,170u8,217u8,76u8,196u8,63u8,2u8,194u8,251u8,202u8,137u8,243u8,156u8,121u8,60u8,122u8,154u8,157u8, - 15u8,135u8,165u8,175u8,234u8,208u8,226u8,212u8,42u8,10u8,198u8,60u8,77u8,192u8,174u8,53u8,31u8,212u8,243u8,193u8, - 104u8,196u8,226u8,106u8,244u8,163u8,118u8,240u8,70u8,188u8,204u8,251u8,233u8,41u8,189u8,225u8,255u8,154u8,157u8,145u8, - 18u8,145u8,82u8,66u8,122u8,71u8,122u8,71u8,111u8,159u8,73u8,121u8,122u8,202u8,95u8,182u8,71u8,245u8,132u8,114u8, - 124u8,195u8,29u8,208u8,107u8,26u8,13u8,218u8,227u8,128u8,215u8,51u8,245u8,126u8,249u8,43u8,60u8,176u8,95u8,110u8, - 242u8,101u8,123u8,7u8,57u8,100u8,31u8,156u8,242u8,247u8,27u8,214u8,33u8,134u8,158u8,55u8,162u8,62u8,132u8,144u8, - 103u8,103u8,51u8,144u8,207u8,24u8,31u8,181u8,7u8,53u8,6u8,19u8,111u8,199u8,99u8,136u8,205u8,5u8,160u8,239u8, - 27u8,253u8,103u8,122u8,221u8,30u8,90u8,32u8,100u8,204u8,201u8,216u8,59u8,220u8,17u8,127u8,137u8,29u8,170u8,49u8, - 198u8,103u8,165u8,195u8,14u8,236u8,164u8,1u8,43u8,158u8,31u8,242u8,222u8,168u8,140u8,19u8,84u8,183u8,133u8,75u8, - 41u8,205u8,200u8,209u8,214u8,28u8,243u8,44u8,209u8,74u8,99u8,182u8,97u8,135u8,43u8,187u8,192u8,181u8,200u8,38u8, - 244u8,107u8,167u8,182u8,141u8,61u8,44u8,166u8,224u8,183u8,30u8,129u8,14u8,209u8,227u8,249u8,36u8,74u8,95u8,229u8, - 179u8,72u8,150u8,106u8,208u8,167u8,245u8,18u8,133u8,30u8,155u8,223u8,100u8,154u8,227u8,148u8,130u8,161u8,168u8,61u8, - 10u8,221u8,151u8,252u8,101u8,0u8,45u8,196u8,224u8,168u8,115u8,155u8,242u8,108u8,6u8,22u8,200u8,235u8,97u8,238u8, - 199u8,88u8,170u8,75u8,93u8,210u8,144u8,42u8,18u8,52u8,200u8,62u8,192u8,36u8,155u8,82u8,152u8,59u8,149u8,134u8, - 210u8,182u8,65u8,18u8,149u8,206u8,104u8,184u8,22u8,221u8,139u8,46u8,138u8,98u8,226u8,10u8,4u8,21u8,93u8,18u8, - 118u8,1u8,9u8,41u8,199u8,165u8,34u8,201u8,182u8,0u8,225u8,45u8,198u8,180u8,7u8,147u8,30u8,238u8,136u8,24u8, - 36u8,249u8,183u8,49u8,52u8,155u8,59u8,97u8,179u8,155u8,164u8,18u8,19u8,90u8,227u8,35u8,146u8,227u8,56u8,232u8, - 31u8,175u8,52u8,61u8,65u8,195u8,185u8,3u8,97u8,40u8,102u8,168u8,48u8,94u8,51u8,231u8,93u8,114u8,129u8,153u8, - 68u8,42u8,158u8,135u8,132u8,34u8,229u8,37u8,71u8,8u8,73u8,22u8,135u8,228u8,30u8,157u8,135u8,11u8,66u8,102u8, - 178u8,48u8,138u8,74u8,137u8,96u8,206u8,213u8,98u8,8u8,198u8,185u8,206u8,160u8,47u8,138u8,178u8,142u8,39u8,197u8, - 144u8,214u8,3u8,71u8,192u8,56u8,188u8,53u8,26u8,143u8,205u8,237u8,241u8,65u8,48u8,50u8,90u8,245u8,34u8,133u8, - 198u8,204u8,35u8,94u8,237u8,235u8,159u8,245u8,183u8,65u8,151u8,160u8,37u8,131u8,129u8,10u8,58u8,28u8,158u8,56u8, - 154u8,131u8,124u8,12u8,153u8,17u8,246u8,235u8,17u8,72u8,234u8,237u8,203u8,237u8,8u8,33u8,1u8,82u8,9u8,34u8, - 131u8,61u8,97u8,218u8,202u8,12u8,30u8,38u8,204u8,245u8,225u8,119u8,183u8,163u8,35u8,192u8,186u8,1u8,174u8,223u8, - 154u8,128u8,246u8,196u8,7u8,5u8,221u8,54u8,45u8,117u8,208u8,246u8,216u8,204u8,25u8,101u8,77u8,49u8,192u8,8u8, - 194u8,199u8,241u8,200u8,251u8,108u8,76u8,180u8,199u8,38u8,50u8,152u8,19u8,30u8,81u8,112u8,83u8,114u8,24u8,13u8, - 248u8,160u8,85u8,13u8,73u8,101u8,21u8,174u8,232u8,44u8,7u8,119u8,117u8,244u8,51u8,165u8,26u8,36u8,136u8,245u8, - 6u8,200u8,65u8,101u8,168u8,175u8,48u8,4u8,63u8,20u8,203u8,214u8,142u8,140u8,166u8,138u8,241u8,12u8,66u8,199u8, - 5u8,68u8,86u8,19u8,11u8,213u8,60u8,234u8,24u8,105u8,156u8,0u8,254u8,33u8,81u8,179u8,180u8,11u8,13u8,33u8, - 74u8,29u8,161u8,217u8,248u8,248u8,141u8,31u8,145u8,201u8,240u8,33u8,37u8,5u8,169u8,157u8,18u8,16u8,110u8,27u8, - 195u8,22u8,90u8,188u8,48u8,253u8,4u8,38u8,53u8,165u8,199u8,112u8,203u8,188u8,201u8,48u8,63u8,224u8,223u8,31u8, - 1u8,213u8,162u8,149u8,104u8,98u8,190u8,137u8,242u8,7u8,44u8,133u8,139u8,251u8,69u8,134u8,147u8,217u8,136u8,27u8, - 213u8,37u8,73u8,9u8,88u8,75u8,81u8,85u8,92u8,9u8,42u8,83u8,212u8,156u8,52u8,10u8,230u8,97u8,57u8,216u8, - 238u8,223u8,13u8,80u8,10u8,7u8,129u8,8u8,21u8,52u8,142u8,45u8,118u8,205u8,70u8,143u8,58u8,196u8,65u8,164u8, - 169u8,35u8,29u8,103u8,224u8,254u8,227u8,56u8,193u8,162u8,16u8,28u8,193u8,53u8,133u8,146u8,191u8,42u8,2u8,144u8, - 126u8,198u8,117u8,102u8,153u8,113u8,220u8,60u8,99u8,73u8,75u8,156u8,13u8,51u8,253u8,176u8,4u8,222u8,202u8,216u8, - 87u8,32u8,96u8,111u8,230u8,232u8,113u8,126u8,170u8,98u8,33u8,124u8,194u8,151u8,51u8,244u8,51u8,62u8,54u8,33u8, - 0u8,161u8,235u8,89u8,201u8,56u8,232u8,228u8,86u8,99u8,99u8,232u8,51u8,241u8,198u8,174u8,232u8,176u8,219u8,88u8, - 85u8,194u8,245u8,194u8,131u8,219u8,225u8,36u8,167u8,245u8,186u8,97u8,58u8,242u8,71u8,52u8,18u8,82u8,176u8,51u8, - 39u8,163u8,133u8,28u8,47u8,26u8,157u8,144u8,238u8,65u8,81u8,86u8,4u8,40u8,161u8,3u8,166u8,95u8,241u8,29u8, - 33u8,22u8,80u8,22u8,153u8,176u8,187u8,253u8,91u8,227u8,148u8,97u8,42u8,202u8,68u8,9u8,126u8,238u8,207u8,53u8, - 102u8,26u8,57u8,0u8,140u8,133u8,150u8,40u8,82u8,220u8,36u8,187u8,110u8,92u8,166u8,163u8,148u8,207u8,95u8,41u8, - 159u8,96u8,169u8,181u8,214u8,115u8,207u8,19u8,67u8,164u8,76u8,19u8,52u8,214u8,217u8,168u8,242u8,55u8,173u8,121u8, - 50u8,138u8,219u8,57u8,223u8,48u8,191u8,96u8,85u8,108u8,177u8,82u8,179u8,44u8,221u8,131u8,111u8,197u8,111u8,110u8, - 52u8,236u8,33u8,172u8,15u8,53u8,19u8,166u8,182u8,150u8,125u8,159u8,146u8,218u8,106u8,71u8,34u8,72u8,201u8,117u8, - 198u8,74u8,185u8,215u8,44u8,215u8,167u8,177u8,71u8,105u8,24u8,205u8,67u8,167u8,133u8,55u8,197u8,215u8,1u8,103u8, - 130u8,138u8,230u8,82u8,135u8,226u8,9u8,107u8,97u8,190u8,78u8,130u8,25u8,1u8,38u8,130u8,106u8,17u8,49u8,220u8, - 7u8,156u8,195u8,240u8,138u8,84u8,215u8,84u8,78u8,216u8,136u8,205u8,30u8,173u8,48u8,104u8,231u8,53u8,64u8,170u8, - 25u8,34u8,136u8,180u8,106u8,161u8,214u8,67u8,198u8,225u8,29u8,228u8,89u8,16u8,98u8,102u8,240u8,145u8,239u8,103u8, - 190u8,149u8,217u8,176u8,165u8,192u8,186u8,22u8,66u8,234u8,88u8,26u8,81u8,202u8,38u8,50u8,193u8,34u8,131u8,168u8, - 203u8,81u8,240u8,227u8,202u8,82u8,244u8,91u8,64u8,64u8,179u8,132u8,239u8,229u8,246u8,213u8,236u8,100u8,240u8,163u8, - 230u8,184u8,111u8,25u8,44u8,203u8,192u8,73u8,122u8,247u8,2u8,252u8,219u8,103u8,229u8,207u8,97u8,219u8,173u8,221u8, - 89u8,58u8,7u8,95u8,232u8,35u8,181u8,197u8,192u8,159u8,232u8,215u8,12u8,132u8,171u8,200u8,23u8,168u8,26u8,222u8, - 164u8,63u8,108u8,184u8,191u8,253u8,254u8,17u8,190u8,122u8,175u8,133u8,223u8,128u8,41u8,248u8,163u8,100u8,26u8,224u8, - 24u8,180u8,120u8,246u8,59u8,204u8,40u8,31u8,116u8,236u8,20u8,28u8,137u8,51u8,221u8,57u8,155u8,132u8,89u8,136u8, - 13u8,85u8,78u8,252u8,76u8,1u8,26u8,12u8,175u8,178u8,184u8,137u8,47u8,39u8,197u8,69u8,50u8,121u8,110u8,189u8, - 247u8,147u8,51u8,119u8,119u8,91u8,44u8,75u8,7u8,96u8,195u8,142u8,231u8,198u8,208u8,68u8,204u8,74u8,166u8,112u8, - 167u8,79u8,124u8,57u8,201u8,194u8,59u8,45u8,162u8,169u8,114u8,46u8,191u8,118u8,163u8,193u8,49u8,32u8,211u8,27u8, - 193u8,138u8,65u8,212u8,187u8,0u8,94u8,96u8,45u8,45u8,96u8,153u8,240u8,131u8,149u8,193u8,205u8,177u8,1u8,14u8, - 31u8,102u8,233u8,249u8,17u8,26u8,60u8,216u8,207u8,141u8,219u8,183u8,20u8,187u8,59u8,1u8,5u8,191u8,158u8,213u8, - 14u8,139u8,188u8,87u8,27u8,46u8,179u8,143u8,105u8,238u8,100u8,157u8,91u8,187u8,189u8,76u8,107u8,35u8,153u8,88u8, - 249u8,38u8,67u8,251u8,111u8,195u8,137u8,187u8,94u8,125u8,170u8,13u8,156u8,32u8,160u8,234u8,76u8,45u8,51u8,88u8, - 49u8,5u8,111u8,65u8,56u8,62u8,83u8,138u8,17u8,249u8,77u8,30u8,169u8,29u8,122u8,182u8,16u8,253u8,20u8,237u8, - 68u8,91u8,91u8,206u8,207u8,207u8,195u8,107u8,218u8,246u8,197u8,213u8,122u8,198u8,36u8,76u8,136u8,236u8,115u8,15u8, - 80u8,156u8,148u8,151u8,51u8,12u8,84u8,119u8,66u8,73u8,10u8,93u8,245u8,185u8,182u8,254u8,239u8,120u8,106u8,41u8, - 131u8,64u8,103u8,43u8,188u8,34u8,61u8,176u8,216u8,179u8,247u8,210u8,109u8,67u8,76u8,152u8,214u8,238u8,139u8,100u8, - 250u8,176u8,135u8,191u8,168u8,99u8,81u8,43u8,234u8,241u8,69u8,182u8,219u8,98u8,136u8,111u8,206u8,230u8,129u8,95u8, - 240u8,167u8,191u8,191u8,127u8,214u8,136u8,107u8,43u8,161u8,24u8,14u8,152u8,193u8,8u8,246u8,43u8,219u8,115u8,227u8, - 180u8,230u8,24u8,222u8,112u8,175u8,103u8,88u8,223u8,123u8,106u8,36u8,240u8,137u8,129u8,155u8,166u8,147u8,1u8,246u8, - 250u8,94u8,236u8,106u8,48u8,37u8,143u8,53u8,193u8,103u8,127u8,127u8,58u8,171u8,174u8,226u8,139u8,100u8,248u8,1u8, - 81u8,204u8,15u8,234u8,222u8,92u8,100u8,180u8,112u8,48u8,235u8,106u8,80u8,194u8,63u8,8u8,165u8,204u8,220u8,248u8, - 103u8,245u8,87u8,223u8,156u8,45u8,57u8,161u8,136u8,78u8,8u8,247u8,138u8,29u8,251u8,23u8,114u8,18u8,64u8,33u8, - 178u8,107u8,45u8,124u8,38u8,88u8,121u8,212u8,200u8,54u8,172u8,81u8,202u8,27u8,159u8,238u8,217u8,53u8,178u8,137u8, - 191u8,42u8,47u8,196u8,145u8,231u8,227u8,151u8,226u8,40u8,11u8,226u8,175u8,14u8,239u8,23u8,209u8,154u8,248u8,214u8, - 51u8,21u8,150u8,205u8,198u8,81u8,71u8,36u8,190u8,103u8,85u8,92u8,21u8,215u8,105u8,103u8,75u8,55u8,181u8,57u8, - 244u8,187u8,54u8,142u8,127u8,39u8,190u8,18u8,139u8,241u8,125u8,164u8,48u8,50u8,74u8,39u8,85u8,106u8,13u8,97u8, - 159u8,176u8,122u8,187u8,13u8,134u8,150u8,179u8,84u8,92u8,178u8,114u8,100u8,182u8,79u8,94u8,171u8,60u8,10u8,238u8, - 209u8,207u8,139u8,27u8,7u8,107u8,225u8,157u8,248u8,34u8,149u8,231u8,211u8,10u8,81u8,125u8,178u8,187u8,203u8,124u8, - 6u8,27u8,49u8,189u8,138u8,152u8,190u8,67u8,252u8,215u8,167u8,232u8,24u8,246u8,107u8,47u8,138u8,187u8,48u8,147u8, - 241u8,91u8,12u8,225u8,99u8,205u8,239u8,209u8,52u8,49u8,211u8,53u8,123u8,250u8,186u8,181u8,233u8,28u8,70u8,177u8, - 18u8,167u8,128u8,115u8,51u8,160u8,110u8,172u8,57u8,250u8,169u8,105u8,236u8,229u8,173u8,131u8,165u8,233u8,46u8,152u8, - 225u8,237u8,146u8,159u8,120u8,190u8,33u8,191u8,191u8,2u8,249u8,197u8,61u8,203u8,175u8,248u8,144u8,233u8,79u8,115u8, - 227u8,222u8,21u8,1u8,146u8,45u8,158u8,66u8,120u8,153u8,82u8,158u8,121u8,244u8,209u8,80u8,210u8,220u8,59u8,0u8, - 99u8,25u8,187u8,147u8,177u8,26u8,70u8,171u8,220u8,225u8,47u8,198u8,9u8,171u8,113u8,206u8,86u8,248u8,23u8,29u8, - 59u8,100u8,208u8,149u8,127u8,179u8,168u8,140u8,142u8,38u8,166u8,98u8,21u8,112u8,192u8,242u8,22u8,38u8,206u8,96u8, - 1u8,87u8,140u8,169u8,157u8,92u8,224u8,2u8,102u8,103u8,139u8,191u8,42u8,175u8,104u8,58u8,235u8,172u8,192u8,73u8, - 220u8,17u8,119u8,4u8,59u8,91u8,218u8,42u8,186u8,221u8,110u8,75u8,120u8,3u8,23u8,227u8,94u8,16u8,202u8,255u8, - 53u8,242u8,131u8,240u8,230u8,28u8,230u8,126u8,72u8,189u8,162u8,83u8,205u8,40u8,63u8,25u8,115u8,33u8,49u8,236u8, - 79u8,94u8,218u8,174u8,42u8,244u8,224u8,178u8,61u8,194u8,52u8,220u8,218u8,228u8,163u8,139u8,105u8,235u8,240u8,30u8, - 45u8,196u8,228u8,0u8,132u8,245u8,66u8,140u8,110u8,73u8,18u8,90u8,24u8,246u8,142u8,1u8,98u8,132u8,188u8,116u8, - 63u8,156u8,135u8,67u8,62u8,100u8,48u8,250u8,246u8,211u8,202u8,145u8,71u8,66u8,176u8,158u8,177u8,90u8,43u8,148u8, - 144u8,194u8,50u8,60u8,82u8,202u8,73u8,147u8,1u8,55u8,116u8,142u8,99u8,150u8,213u8,136u8,221u8,200u8,102u8,183u8, - 250u8,197u8,91u8,53u8,38u8,141u8,178u8,167u8,183u8,44u8,38u8,151u8,23u8,14u8,196u8,241u8,249u8,210u8,210u8,186u8, - 103u8,197u8,231u8,224u8,64u8,58u8,20u8,113u8,163u8,245u8,119u8,191u8,254u8,201u8,108u8,36u8,121u8,43u8,73u8,78u8, - 137u8,122u8,48u8,184u8,97u8,10u8,210u8,143u8,238u8,123u8,35u8,120u8,79u8,191u8,18u8,46u8,110u8,74u8,197u8,31u8, - 210u8,219u8,14u8,125u8,211u8,139u8,24u8,175u8,246u8,76u8,130u8,46u8,40u8,26u8,3u8,224u8,253u8,157u8,62u8,10u8, - 223u8,79u8,232u8,123u8,122u8,48u8,234u8,209u8,199u8,221u8,150u8,97u8,153u8,163u8,96u8,226u8,210u8,130u8,177u8,26u8, - 74u8,243u8,35u8,134u8,85u8,133u8,10u8,46u8,169u8,84u8,40u8,170u8,79u8,98u8,102u8,67u8,201u8,36u8,83u8,51u8, - 217u8,180u8,192u8,130u8,72u8,116u8,219u8,42u8,187u8,228u8,151u8,126u8,167u8,9u8,168u8,226u8,148u8,124u8,233u8,142u8, - 34u8,74u8,83u8,208u8,220u8,79u8,210u8,209u8,238u8,211u8,167u8,253u8,127u8,87u8,181u8,176u8,58u8,118u8,241u8,36u8, - 233u8,68u8,151u8,179u8,190u8,22u8,21u8,173u8,84u8,138u8,15u8,12u8,7u8,232u8,57u8,204u8,138u8,153u8,172u8,44u8, - 194u8,175u8,27u8,22u8,23u8,148u8,4u8,139u8,62u8,176u8,81u8,65u8,201u8,102u8,116u8,251u8,33u8,194u8,50u8,15u8, - 190u8,229u8,245u8,196u8,221u8,3u8,188u8,68u8,71u8,137u8,93u8,114u8,94u8,190u8,112u8,150u8,250u8,167u8,50u8,113u8, - 164u8,62u8,227u8,22u8,130u8,195u8,0u8,161u8,25u8,30u8,231u8,28u8,11u8,107u8,9u8,221u8,50u8,131u8,67u8,171u8, - 81u8,37u8,214u8,34u8,136u8,192u8,199u8,207u8,130u8,89u8,87u8,115u8,51u8,140u8,218u8,101u8,134u8,72u8,165u8,133u8, - 10u8,57u8,153u8,57u8,103u8,226u8,25u8,219u8,4u8,18u8,138u8,63u8,28u8,27u8,84u8,132u8,8u8,174u8,49u8,135u8, - 171u8,255u8,91u8,153u8,70u8,141u8,87u8,19u8,229u8,43u8,44u8,224u8,231u8,123u8,141u8,221u8,171u8,148u8,47u8,58u8, - 97u8,207u8,86u8,204u8,27u8,131u8,159u8,148u8,40u8,168u8,234u8,209u8,252u8,87u8,115u8,182u8,223u8,127u8,145u8,200u8, - 162u8,26u8,47u8,217u8,229u8,85u8,109u8,102u8,205u8,230u8,148u8,217u8,195u8,177u8,136u8,223u8,170u8,224u8,48u8,208u8, - 167u8,164u8,19u8,123u8,92u8,177u8,138u8,129u8,134u8,71u8,188u8,44u8,138u8,113u8,60u8,188u8,2u8,83u8,53u8,197u8, - 59u8,18u8,47u8,230u8,165u8,30u8,154u8,94u8,45u8,149u8,48u8,40u8,255u8,134u8,58u8,110u8,167u8,107u8,114u8,118u8, - 39u8,145u8,207u8,70u8,178u8,94u8,147u8,180u8,217u8,111u8,214u8,159u8,237u8,177u8,172u8,185u8,185u8,145u8,228u8,24u8, - 47u8,30u8,252u8,236u8,249u8,156u8,202u8,114u8,110u8,150u8,223u8,105u8,97u8,150u8,41u8,38u8,155u8,183u8,101u8,226u8, - 187u8,255u8,153u8,194u8,119u8,243u8,121u8,59u8,60u8,239u8,217u8,46u8,71u8,253u8,108u8,13u8,105u8,169u8,35u8,199u8, - 127u8,166u8,178u8,38u8,150u8,186u8,197u8,21u8,202u8,51u8,140u8,40u8,147u8,183u8,202u8,208u8,176u8,169u8,249u8,117u8, - 56u8,61u8,238u8,12u8,59u8,55u8,174u8,96u8,13u8,100u8,50u8,40u8,19u8,178u8,102u8,22u8,98u8,166u8,223u8,96u8, - 171u8,146u8,49u8,187u8,153u8,133u8,60u8,173u8,40u8,179u8,63u8,180u8,252u8,161u8,155u8,148u8,221u8,179u8,146u8,85u8, - 190u8,228u8,82u8,37u8,243u8,84u8,220u8,209u8,102u8,180u8,230u8,29u8,46u8,44u8,254u8,33u8,38u8,48u8,106u8,100u8, - 250u8,171u8,97u8,214u8,198u8,109u8,103u8,145u8,16u8,152u8,235u8,108u8,89u8,38u8,72u8,110u8,207u8,139u8,17u8,109u8, - 25u8,101u8,108u8,92u8,212u8,117u8,108u8,64u8,173u8,160u8,96u8,156u8,229u8,84u8,84u8,109u8,18u8,194u8,57u8,46u8, - 85u8,86u8,199u8,126u8,250u8,144u8,123u8,188u8,193u8,120u8,76u8,221u8,139u8,105u8,54u8,129u8,27u8,172u8,52u8,240u8, - 136u8,177u8,79u8,71u8,103u8,251u8,220u8,90u8,99u8,32u8,212u8,24u8,141u8,116u8,44u8,77u8,216u8,181u8,10u8,85u8, - 149u8,177u8,81u8,244u8,117u8,236u8,28u8,185u8,45u8,54u8,204u8,162u8,130u8,235u8,171u8,8u8,14u8,23u8,33u8,58u8, - 244u8,127u8,61u8,25u8,161u8,236u8,5u8,79u8,214u8,58u8,31u8,123u8,101u8,11u8,159u8,132u8,135u8,48u8,224u8,96u8, - 50u8,196u8,24u8,89u8,225u8,146u8,242u8,150u8,13u8,58u8,177u8,21u8,20u8,93u8,189u8,251u8,7u8,94u8,91u8,191u8, - 214u8,70u8,17u8,37u8,35u8,213u8,37u8,23u8,57u8,57u8,94u8,118u8,1u8,158u8,86u8,49u8,254u8,35u8,171u8,142u8, - 168u8,162u8,15u8,60u8,86u8,231u8,14u8,198u8,174u8,23u8,98u8,253u8,49u8,49u8,175u8,94u8,41u8,226u8,86u8,171u8, - 92u8,32u8,74u8,137u8,177u8,145u8,112u8,47u8,219u8,246u8,90u8,3u8,192u8,110u8,119u8,197u8,93u8,164u8,231u8,152u8, - 169u8,158u8,112u8,252u8,172u8,120u8,129u8,44u8,1u8,211u8,199u8,53u8,171u8,210u8,67u8,45u8,203u8,118u8,52u8,170u8, - 126u8,28u8,121u8,230u8,210u8,130u8,3u8,190u8,149u8,21u8,190u8,175u8,66u8,55u8,228u8,118u8,117u8,152u8,164u8,98u8, - 155u8,234u8,30u8,46u8,186u8,98u8,3u8,114u8,182u8,99u8,91u8,87u8,129u8,168u8,183u8,3u8,179u8,94u8,164u8,138u8, - 103u8,241u8,65u8,150u8,97u8,242u8,91u8,205u8,92u8,222u8,157u8,118u8,121u8,134u8,79u8,81u8,81u8,31u8,116u8,214u8, - 205u8,241u8,253u8,120u8,233u8,1u8,128u8,235u8,97u8,113u8,112u8,117u8,93u8,8u8,234u8,85u8,175u8,90u8,95u8,210u8, - 254u8,42u8,248u8,45u8,252u8,53u8,227u8,148u8,237u8,205u8,173u8,45u8,86u8,197u8,188u8,44u8,13u8,72u8,109u8,225u8, - 100u8,153u8,229u8,178u8,76u8,108u8,103u8,126u8,21u8,154u8,238u8,60u8,255u8,80u8,136u8,23u8,114u8,63u8,81u8,232u8, - 241u8,243u8,23u8,146u8,144u8,80u8,69u8,173u8,175u8,184u8,203u8,218u8,214u8,225u8,231u8,123u8,134u8,220u8,194u8,68u8, - 93u8,191u8,190u8,107u8,42u8,102u8,220u8,119u8,255u8,66u8,167u8,210u8,6u8,223u8,152u8,168u8,31u8,32u8,120u8,232u8, - 150u8,32u8,242u8,6u8,69u8,142u8,174u8,111u8,215u8,133u8,135u8,5u8,133u8,207u8,116u8,37u8,205u8,205u8,76u8,110u8, - 226u8,206u8,47u8,16u8,227u8,225u8,194u8,32u8,33u8,109u8,110u8,223u8,244u8,90u8,17u8,173u8,250u8,18u8,98u8,237u8, - 239u8,65u8,82u8,253u8,76u8,149u8,88u8,205u8,152u8,41u8,213u8,69u8,198u8,162u8,117u8,32u8,198u8,192u8,173u8,209u8, - 199u8,171u8,135u8,117u8,154u8,144u8,203u8,123u8,103u8,219u8,24u8,97u8,78u8,57u8,143u8,29u8,11u8,44u8,222u8,18u8, - 21u8,125u8,203u8,168u8,212u8,72u8,185u8,129u8,206u8,29u8,198u8,106u8,95u8,189u8,183u8,235u8,101u8,218u8,87u8,247u8, - 93u8,252u8,49u8,135u8,244u8,95u8,232u8,119u8,70u8,117u8,111u8,245u8,207u8,27u8,120u8,254u8,93u8,127u8,103u8,146u8, - 230u8,11u8,255u8,243u8,38u8,108u8,40u8,3u8,224u8,204u8,20u8,168u8,5u8,48u8,111u8,10u8,163u8,66u8,128u8,51u8, - 168u8,42u8,19u8,48u8,111u8,156u8,86u8,197u8,3u8,156u8,241u8,231u8,85u8,16u8,104u8,59u8,107u8,171u8,201u8,90u8, - 20u8,23u8,152u8,55u8,95u8,171u8,146u8,3u8,173u8,38u8,214u8,235u8,14u8,204u8,155u8,53u8,88u8,141u8,192u8,153u8, - 201u8,91u8,146u8,160u8,105u8,248u8,207u8,58u8,231u8,230u8,248u8,45u8,167u8,147u8,138u8,130u8,71u8,185u8,107u8,52u8, - 119u8,122u8,16u8,166u8,156u8,84u8,169u8,99u8,245u8,132u8,188u8,226u8,103u8,170u8,218u8,201u8,2u8,238u8,239u8,51u8, - 85u8,216u8,72u8,113u8,145u8,30u8,21u8,120u8,98u8,197u8,173u8,169u8,158u8,61u8,106u8,110u8,84u8,235u8,216u8,176u8, - 95u8,149u8,226u8,46u8,63u8,237u8,132u8,181u8,32u8,2u8,180u8,21u8,85u8,106u8,165u8,57u8,200u8,101u8,121u8,64u8, - 200u8,141u8,74u8,57u8,244u8,123u8,215u8,72u8,4u8,198u8,167u8,95u8,146u8,12u8,221u8,222u8,84u8,133u8,103u8,196u8, - 213u8,90u8,144u8,227u8,197u8,7u8,229u8,175u8,12u8,84u8,241u8,235u8,233u8,213u8,105u8,228u8,164u8,162u8,116u8,20u8, - 235u8,135u8,225u8,216u8,145u8,86u8,37u8,107u8,105u8,28u8,138u8,186u8,249u8,204u8,48u8,170u8,10u8,172u8,227u8,237u8, - 91u8,146u8,86u8,4u8,72u8,202u8,38u8,44u8,188u8,149u8,8u8,95u8,63u8,184u8,197u8,88u8,97u8,29u8,182u8,47u8, - 57u8,49u8,139u8,14u8,136u8,226u8,178u8,254u8,22u8,30u8,220u8,216u8,156u8,224u8,184u8,136u8,22u8,185u8,94u8,151u8, - 150u8,135u8,15u8,156u8,82u8,73u8,194u8,211u8,145u8,139u8,202u8,246u8,176u8,114u8,94u8,229u8,137u8,238u8,105u8,136u8, - 96u8,4u8,161u8,84u8,205u8,171u8,111u8,240u8,35u8,81u8,118u8,171u8,7u8,163u8,170u8,54u8,26u8,182u8,194u8,45u8, - 87u8,187u8,110u8,235u8,239u8,62u8,187u8,194u8,38u8,50u8,195u8,172u8,28u8,206u8,50u8,74u8,207u8,208u8,144u8,134u8, - 188u8,4u8,120u8,19u8,78u8,149u8,82u8,213u8,157u8,131u8,84u8,205u8,183u8,98u8,87u8,164u8,200u8,73u8,151u8,179u8, - 18u8,104u8,52u8,138u8,232u8,112u8,130u8,151u8,8u8,167u8,211u8,84u8,118u8,81u8,200u8,88u8,233u8,61u8,94u8,219u8, - 190u8,80u8,85u8,131u8,88u8,97u8,166u8,220u8,44u8,204u8,131u8,73u8,161u8,182u8,18u8,170u8,54u8,221u8,197u8,180u8, - 153u8,29u8,59u8,31u8,180u8,164u8,152u8,179u8,238u8,45u8,94u8,143u8,126u8,201u8,116u8,107u8,215u8,203u8,75u8,14u8, - 205u8,170u8,201u8,93u8,184u8,214u8,152u8,176u8,28u8,221u8,49u8,119u8,9u8,206u8,35u8,102u8,235u8,206u8,207u8,75u8, - 210u8,192u8,248u8,204u8,57u8,90u8,86u8,176u8,31u8,228u8,12u8,158u8,128u8,30u8,29u8,99u8,222u8,30u8,60u8,124u8, - 186u8,20u8,158u8,92u8,194u8,201u8,109u8,135u8,85u8,240u8,249u8,203u8,104u8,0u8,155u8,170u8,252u8,61u8,111u8,79u8, - 142u8,38u8,216u8,179u8,175u8,162u8,251u8,212u8,119u8,113u8,253u8,92u8,65u8,194u8,20u8,110u8,22u8,71u8,213u8,100u8, - 134u8,161u8,38u8,58u8,98u8,131u8,158u8,54u8,10u8,14u8,253u8,251u8,86u8,132u8,206u8,114u8,133u8,234u8,130u8,223u8, - 67u8,95u8,76u8,148u8,24u8,171u8,13u8,75u8,19u8,107u8,14u8,143u8,76u8,97u8,106u8,175u8,44u8,118u8,16u8,188u8, - 107u8,255u8,69u8,133u8,202u8,185u8,33u8,74u8,170u8,15u8,217u8,180u8,82u8,165u8,53u8,43u8,114u8,72u8,82u8,159u8, - 156u8,162u8,14u8,149u8,237u8,124u8,92u8,89u8,117u8,28u8,182u8,55u8,18u8,171u8,181u8,196u8,50u8,145u8,171u8,53u8, - 46u8,87u8,58u8,50u8,175u8,75u8,116u8,213u8,110u8,69u8,14u8,73u8,172u8,247u8,79u8,142u8,217u8,112u8,216u8,72u8, - 51u8,57u8,60u8,63u8,188u8,23u8,81u8,43u8,89u8,246u8,204u8,243u8,173u8,40u8,216u8,17u8,41u8,39u8,22u8,157u8, - 190u8,68u8,178u8,142u8,199u8,95u8,12u8,97u8,213u8,24u8,171u8,29u8,192u8,199u8,99u8,247u8,124u8,122u8,209u8,255u8, - 88u8,44u8,242u8,127u8,172u8,179u8,82u8,185u8,165u8,242u8,21u8,152u8,255u8,59u8,235u8,167u8,103u8,206u8,55u8,29u8, - 170u8,244u8,142u8,229u8,164u8,70u8,233u8,167u8,174u8,182u8,100u8,250u8,65u8,58u8,167u8,101u8,34u8,145u8,134u8,51u8, - 207u8,108u8,223u8,14u8,53u8,84u8,226u8,115u8,203u8,180u8,58u8,70u8,27u8,183u8,143u8,75u8,74u8,216u8,173u8,8u8, - 183u8,115u8,63u8,101u8,235u8,24u8,75u8,203u8,178u8,17u8,178u8,193u8,193u8,110u8,130u8,222u8,145u8,90u8,214u8,98u8, - 245u8,129u8,170u8,155u8,100u8,202u8,223u8,146u8,123u8,101u8,208u8,48u8,191u8,209u8,66u8,255u8,159u8,173u8,64u8,249u8, - 81u8,242u8,1u8,27u8,52u8,178u8,170u8,69u8,101u8,250u8,184u8,226u8,125u8,202u8,100u8,180u8,6u8,194u8,145u8,215u8, - 154u8,96u8,224u8,105u8,27u8,45u8,74u8,89u8,24u8,188u8,2u8,59u8,83u8,24u8,204u8,89u8,204u8,230u8,54u8,6u8, - 128u8,127u8,143u8,105u8,90u8,79u8,48u8,90u8,86u8,194u8,254u8,73u8,247u8,174u8,121u8,221u8,183u8,22u8,223u8,16u8, - 170u8,239u8,50u8,73u8,221u8,141u8,142u8,91u8,112u8,255u8,65u8,20u8,214u8,237u8,96u8,16u8,240u8,213u8,182u8,215u8, - 206u8,124u8,78u8,181u8,158u8,167u8,8u8,207u8,159u8,54u8,237u8,122u8,148u8,48u8,230u8,157u8,88u8,236u8,150u8,189u8, - 231u8,166u8,160u8,86u8,33u8,61u8,84u8,65u8,209u8,84u8,48u8,86u8,84u8,102u8,2u8,74u8,204u8,70u8,191u8,40u8, - 219u8,18u8,161u8,210u8,56u8,108u8,87u8,170u8,86u8,222u8,160u8,173u8,205u8,220u8,20u8,112u8,90u8,34u8,203u8,247u8, - 129u8,139u8,94u8,91u8,165u8,122u8,98u8,169u8,84u8,232u8,17u8,152u8,91u8,191u8,74u8,149u8,138u8,195u8,194u8,232u8, - 32u8,73u8,211u8,246u8,138u8,150u8,169u8,39u8,45u8,204u8,213u8,80u8,111u8,106u8,56u8,213u8,249u8,26u8,149u8,14u8, - 94u8,28u8,68u8,170u8,31u8,182u8,202u8,54u8,79u8,19u8,153u8,27u8,137u8,11u8,175u8,82u8,70u8,227u8,26u8,94u8, - 121u8,254u8,66u8,91u8,222u8,157u8,6u8,225u8,26u8,42u8,136u8,69u8,75u8,200u8,157u8,86u8,87u8,11u8,130u8,211u8, - 53u8,128u8,196u8,235u8,44u8,176u8,131u8,243u8,77u8,178u8,105u8,94u8,92u8,198u8,132u8,114u8,115u8,65u8,54u8,87u8, - 79u8,106u8,0u8,99u8,207u8,121u8,185u8,97u8,155u8,230u8,203u8,159u8,27u8,178u8,180u8,6u8,178u8,17u8,171u8,35u8, - 150u8,176u8,35u8,32u8,147u8,158u8,88u8,226u8,151u8,26u8,229u8,137u8,50u8,60u8,182u8,28u8,194u8,164u8,232u8,50u8, - 27u8,165u8,140u8,172u8,177u8,255u8,66u8,169u8,37u8,238u8,153u8,213u8,123u8,40u8,19u8,123u8,76u8,38u8,183u8,214u8, - 81u8,68u8,152u8,220u8,90u8,107u8,35u8,110u8,124u8,243u8,143u8,123u8,236u8,58u8,135u8,104u8,172u8,193u8,69u8,254u8, - 163u8,141u8,244u8,125u8,24u8,210u8,87u8,22u8,106u8,106u8,35u8,114u8,189u8,25u8,23u8,81u8,56u8,193u8,162u8,149u8, - 8u8,94u8,32u8,226u8,196u8,2u8,77u8,34u8,190u8,132u8,247u8,134u8,221u8,100u8,151u8,224u8,112u8,139u8,100u8,186u8, - 52u8,167u8,150u8,204u8,203u8,40u8,49u8,217u8,152u8,94u8,235u8,175u8,181u8,78u8,66u8,151u8,85u8,21u8,21u8,186u8, - 18u8,140u8,26u8,253u8,180u8,200u8,23u8,209u8,7u8,113u8,71u8,225u8,153u8,137u8,45u8,69u8,208u8,66u8,61u8,141u8, - 238u8,34u8,51u8,228u8,190u8,42u8,92u8,66u8,210u8,202u8,58u8,114u8,77u8,162u8,85u8,188u8,212u8,70u8,150u8,106u8, - 3u8,182u8,77u8,18u8,177u8,246u8,172u8,6u8,152u8,35u8,64u8,221u8,101u8,137u8,130u8,57u8,59u8,254u8,170u8,53u8, - 6u8,114u8,117u8,61u8,30u8,20u8,188u8,47u8,3u8,94u8,19u8,129u8,115u8,226u8,102u8,24u8,35u8,226u8,172u8,235u8, - 186u8,79u8,8u8,27u8,125u8,159u8,8u8,138u8,183u8,63u8,242u8,98u8,61u8,254u8,121u8,20u8,188u8,8u8,166u8,196u8, - 22u8,46u8,164u8,235u8,74u8,239u8,32u8,246u8,251u8,27u8,105u8,89u8,35u8,232u8,168u8,142u8,127u8,244u8,117u8,0u8, - 222u8,104u8,211u8,83u8,149u8,113u8,218u8,88u8,171u8,234u8,59u8,134u8,102u8,173u8,152u8,139u8,13u8,247u8,144u8,246u8, - 212u8,66u8,131u8,10u8,100u8,24u8,184u8,16u8,242u8,86u8,67u8,116u8,117u8,38u8,161u8,55u8,201u8,45u8,123u8,223u8, - 48u8,203u8,30u8,206u8,95u8,157u8,59u8,202u8,103u8,251u8,0u8,44u8,131u8,96u8,129u8,11u8,121u8,78u8,35u8,150u8, - 5u8,175u8,225u8,5u8,84u8,0u8,225u8,106u8,38u8,29u8,233u8,138u8,245u8,162u8,74u8,101u8,143u8,68u8,80u8,58u8, - 178u8,154u8,85u8,177u8,232u8,200u8,80u8,140u8,184u8,174u8,30u8,204u8,225u8,102u8,221u8,32u8,99u8,169u8,234u8,80u8, - 103u8,1u8,171u8,67u8,100u8,205u8,82u8,184u8,101u8,207u8,154u8,157u8,79u8,253u8,221u8,189u8,253u8,125u8,166u8,121u8, - 160u8,59u8,15u8,159u8,130u8,183u8,148u8,191u8,47u8,126u8,113u8,114u8,176u8,97u8,216u8,42u8,250u8,85u8,86u8,240u8, - 123u8,121u8,112u8,246u8,4u8,27u8,55u8,83u8,220u8,144u8,147u8,135u8,104u8,135u8,7u8,83u8,50u8,165u8,138u8,101u8, - 79u8,91u8,11u8,194u8,12u8,212u8,230u8,220u8,105u8,111u8,181u8,187u8,80u8,238u8,116u8,139u8,42u8,120u8,193u8,42u8, - 163u8,11u8,101u8,125u8,218u8,98u8,83u8,20u8,35u8,66u8,91u8,175u8,23u8,230u8,24u8,129u8,62u8,165u8,93u8,157u8, - 73u8,180u8,22u8,105u8,222u8,50u8,81u8,119u8,125u8,65u8,57u8,206u8,42u8,30u8,220u8,228u8,183u8,32u8,130u8,19u8, - 218u8,50u8,94u8,84u8,129u8,120u8,225u8,166u8,102u8,55u8,221u8,34u8,246u8,21u8,38u8,180u8,74u8,60u8,137u8,19u8, - 53u8,82u8,169u8,197u8,137u8,244u8,124u8,239u8,242u8,22u8,36u8,198u8,101u8,155u8,134u8,196u8,74u8,222u8,29u8,196u8, - 35u8,112u8,205u8,54u8,33u8,29u8,95u8,166u8,94u8,81u8,122u8,126u8,180u8,218u8,117u8,104u8,141u8,173u8,245u8,49u8, - 244u8,235u8,112u8,35u8,179u8,139u8,188u8,44u8,73u8,17u8,132u8,191u8,175u8,62u8,201u8,231u8,150u8,252u8,73u8,92u8, - 132u8,173u8,205u8,22u8,48u8,87u8,186u8,57u8,36u8,187u8,205u8,112u8,152u8,218u8,13u8,106u8,210u8,36u8,175u8,140u8, - 26u8,111u8,86u8,167u8,69u8,8u8,171u8,177u8,219u8,139u8,116u8,231u8,237u8,18u8,157u8,233u8,9u8,198u8,214u8,183u8, - 163u8,55u8,176u8,48u8,216u8,25u8,111u8,70u8,45u8,51u8,234u8,122u8,90u8,179u8,12u8,248u8,130u8,134u8,195u8,118u8, - 3u8,114u8,94u8,234u8,136u8,64u8,229u8,190u8,156u8,197u8,25u8,12u8,23u8,214u8,32u8,210u8,25u8,120u8,158u8,3u8, - 191u8,219u8,161u8,119u8,203u8,21u8,29u8,171u8,130u8,252u8,86u8,50u8,73u8,250u8,138u8,26u8,177u8,93u8,37u8,216u8, - 87u8,149u8,13u8,35u8,33u8,164u8,88u8,47u8,227u8,131u8,188u8,47u8,200u8,42u8,172u8,183u8,229u8,148u8,130u8,239u8, - 84u8,209u8,147u8,136u8,250u8,132u8,226u8,21u8,155u8,156u8,118u8,173u8,170u8,175u8,226u8,23u8,23u8,195u8,74u8,240u8, - 111u8,57u8,157u8,59u8,74u8,107u8,62u8,205u8,238u8,21u8,224u8,146u8,214u8,201u8,177u8,57u8,205u8,46u8,197u8,182u8, - 237u8,230u8,184u8,25u8,221u8,95u8,227u8,125u8,112u8,233u8,167u8,30u8,119u8,6u8,132u8,218u8,230u8,110u8,183u8,21u8, - 1u8,180u8,76u8,10u8,248u8,238u8,237u8,246u8,230u8,59u8,239u8,244u8,118u8,187u8,27u8,49u8,176u8,86u8,49u8,48u8, - 143u8,171u8,251u8,101u8,129u8,46u8,60u8,216u8,89u8,254u8,235u8,203u8,130u8,1u8,107u8,66u8,16u8,16u8,6u8,1u8, - 186u8,231u8,157u8,11u8,90u8,41u8,104u8,11u8,215u8,86u8,107u8,67u8,212u8,148u8,164u8,206u8,102u8,103u8,172u8,42u8, - 132u8,139u8,206u8,28u8,62u8,79u8,17u8,203u8,130u8,226u8,149u8,186u8,23u8,0u8,3u8,75u8,109u8,127u8,152u8,80u8, - 240u8,231u8,99u8,71u8,255u8,76u8,97u8,0u8,86u8,116u8,71u8,171u8,63u8,165u8,26u8,112u8,178u8,16u8,44u8,235u8, - 57u8,199u8,177u8,64u8,181u8,209u8,8u8,64u8,207u8,15u8,60u8,119u8,241u8,235u8,130u8,93u8,207u8,234u8,172u8,177u8, - 128u8,118u8,255u8,175u8,197u8,115u8,189u8,30u8,17u8,126u8,59u8,73u8,248u8,65u8,150u8,47u8,206u8,227u8,245u8,19u8, - 130u8,63u8,59u8,166u8,4u8,147u8,206u8,225u8,249u8,233u8,224u8,248u8,108u8,112u8,112u8,254u8,250u8,228u8,152u8,46u8, - 183u8,253u8,124u8,242u8,238u8,248u8,213u8,82u8,5u8,132u8,16u8,104u8,243u8,210u8,17u8,150u8,42u8,36u8,132u8,67u8, - 206u8,41u8,38u8,196u8,178u8,93u8,16u8,223u8,252u8,98u8,203u8,112u8,197u8,180u8,171u8,56u8,68u8,163u8,185u8,5u8, - 111u8,221u8,66u8,67u8,180u8,111u8,207u8,167u8,184u8,18u8,142u8,225u8,207u8,154u8,75u8,224u8,218u8,30u8,39u8,62u8, - 152u8,26u8,75u8,209u8,74u8,215u8,239u8,97u8,90u8,50u8,2u8,167u8,221u8,34u8,50u8,241u8,68u8,181u8,211u8,241u8, - 4u8,214u8,24u8,221u8,107u8,171u8,115u8,94u8,105u8,172u8,102u8,69u8,56u8,207u8,183u8,211u8,62u8,184u8,198u8,243u8, - 95u8,253u8,37u8,168u8,199u8,168u8,24u8,98u8,49u8,152u8,106u8,70u8,245u8,69u8,113u8,225u8,44u8,13u8,72u8,85u8, - 98u8,12u8,72u8,135u8,166u8,11u8,80u8,43u8,43u8,199u8,127u8,53u8,158u8,166u8,47u8,236u8,142u8,202u8,97u8,63u8, - 40u8,110u8,233u8,175u8,106u8,202u8,115u8,178u8,228u8,126u8,117u8,140u8,107u8,3u8,221u8,96u8,224u8,104u8,197u8,178u8, - 163u8,11u8,102u8,168u8,157u8,30u8,254u8,199u8,33u8,65u8,162u8,69u8,150u8,218u8,34u8,76u8,169u8,197u8,149u8,68u8, - 115u8,165u8,243u8,250u8,92u8,253u8,185u8,56u8,127u8,50u8,161u8,233u8,137u8,56u8,216u8,237u8,148u8,148u8,176u8,105u8, - 205u8,210u8,66u8,174u8,237u8,115u8,234u8,53u8,140u8,174u8,5u8,22u8,105u8,255u8,245u8,136u8,5u8,191u8,219u8,59u8, - 182u8,229u8,167u8,188u8,175u8,40u8,48u8,198u8,41u8,94u8,47u8,70u8,103u8,130u8,222u8,120u8,184u8,44u8,38u8,197u8, - 229u8,44u8,213u8,210u8,170u8,101u8,116u8,157u8,121u8,118u8,174u8,211u8,235u8,41u8,21u8,143u8,212u8,190u8,226u8,142u8, - 25u8,81u8,100u8,43u8,81u8,145u8,117u8,114u8,109u8,208u8,245u8,102u8,112u8,65u8,77u8,237u8,217u8,164u8,151u8,200u8, - 147u8,229u8,231u8,233u8,134u8,140u8,153u8,16u8,83u8,94u8,55u8,133u8,248u8,59u8,166u8,121u8,78u8,192u8,142u8,26u8, - 221u8,42u8,7u8,144u8,230u8,158u8,178u8,42u8,81u8,111u8,203u8,224u8,178u8,188u8,23u8,35u8,49u8,110u8,85u8,75u8, - 192u8,215u8,138u8,224u8,30u8,49u8,250u8,13u8,147u8,95u8,134u8,201u8,187u8,14u8,160u8,245u8,105u8,198u8,15u8,176u8, - 70u8,125u8,56u8,57u8,97u8,165u8,210u8,242u8,109u8,179u8,22u8,148u8,148u8,25u8,188u8,125u8,123u8,122u8,2u8,158u8, - 184u8,144u8,144u8,97u8,25u8,80u8,118u8,29u8,122u8,141u8,139u8,80u8,38u8,148u8,201u8,48u8,122u8,188u8,37u8,177u8, - 170u8,52u8,40u8,219u8,90u8,137u8,15u8,175u8,147u8,122u8,120u8,197u8,115u8,176u8,208u8,199u8,148u8,86u8,124u8,16u8, - 163u8,120u8,32u8,31u8,111u8,123u8,225u8,70u8,26u8,204u8,111u8,229u8,139u8,192u8,235u8,47u8,192u8,233u8,182u8,232u8, - 175u8,193u8,198u8,106u8,25u8,101u8,71u8,151u8,106u8,12u8,46u8,85u8,25u8,125u8,65u8,63u8,232u8,119u8,250u8,72u8, - 11u8,132u8,215u8,133u8,107u8,244u8,213u8,201u8,33u8,171u8,140u8,66u8,89u8,36u8,204u8,83u8,218u8,107u8,29u8,226u8, - 141u8,222u8,22u8,85u8,253u8,68u8,245u8,61u8,28u8,78u8,32u8,48u8,49u8,155u8,178u8,218u8,165u8,145u8,86u8,70u8, - 219u8,27u8,7u8,113u8,197u8,138u8,117u8,43u8,13u8,254u8,62u8,45u8,179u8,143u8,24u8,74u8,1u8,7u8,114u8,94u8, - 68u8,44u8,151u8,139u8,26u8,94u8,163u8,199u8,132u8,194u8,173u8,148u8,131u8,118u8,145u8,202u8,134u8,223u8,32u8,52u8, - 89u8,46u8,155u8,46u8,59u8,61u8,30u8,78u8,37u8,102u8,212u8,58u8,99u8,127u8,181u8,1u8,190u8,173u8,78u8,155u8, - 150u8,140u8,45u8,124u8,238u8,173u8,250u8,50u8,222u8,131u8,170u8,201u8,65u8,190u8,181u8,170u8,22u8,188u8,162u8,118u8, - 57u8,79u8,169u8,108u8,209u8,108u8,116u8,174u8,94u8,185u8,191u8,172u8,52u8,116u8,105u8,208u8,115u8,220u8,126u8,173u8, - 85u8,129u8,57u8,168u8,180u8,182u8,55u8,186u8,27u8,41u8,147u8,149u8,207u8,184u8,223u8,84u8,201u8,75u8,124u8,220u8, - 31u8,138u8,84u8,211u8,204u8,233u8,134u8,186u8,33u8,221u8,38u8,85u8,41u8,84u8,197u8,101u8,62u8,13u8,235u8,237u8, - 114u8,255u8,108u8,73u8,34u8,15u8,133u8,180u8,37u8,74u8,173u8,108u8,131u8,190u8,229u8,180u8,185u8,112u8,117u8,243u8, - 102u8,63u8,157u8,214u8,42u8,14u8,73u8,152u8,140u8,186u8,139u8,52u8,205u8,85u8,219u8,12u8,52u8,41u8,75u8,95u8, - 55u8,87u8,165u8,119u8,146u8,171u8,111u8,164u8,104u8,124u8,41u8,108u8,220u8,103u8,200u8,102u8,145u8,3u8,246u8,28u8, - 160u8,208u8,4u8,197u8,118u8,254u8,252u8,170u8,237u8,221u8,248u8,141u8,204u8,53u8,57u8,212u8,151u8,93u8,235u8,139u8, - 40u8,216u8,14u8,113u8,85u8,195u8,67u8,215u8,101u8,187u8,118u8,74u8,120u8,32u8,130u8,170u8,89u8,225u8,158u8,148u8, - 112u8,207u8,137u8,246u8,230u8,54u8,165u8,239u8,154u8,89u8,8u8,231u8,150u8,37u8,33u8,91u8,227u8,224u8,229u8,211u8, - 4u8,120u8,3u8,22u8,24u8,31u8,82u8,234u8,31u8,11u8,80u8,4u8,157u8,15u8,196u8,53u8,44u8,207u8,190u8,63u8, - 150u8,209u8,147u8,177u8,103u8,17u8,246u8,92u8,16u8,243u8,230u8,52u8,174u8,108u8,245u8,186u8,123u8,212u8,38u8,162u8, - 114u8,180u8,148u8,187u8,152u8,211u8,161u8,210u8,19u8,208u8,245u8,217u8,153u8,11u8,137u8,130u8,237u8,96u8,97u8,53u8, - 147u8,225u8,249u8,107u8,170u8,69u8,127u8,42u8,48u8,55u8,173u8,46u8,250u8,220u8,162u8,48u8,167u8,191u8,14u8,170u8, - 244u8,31u8,33u8,35u8,17u8,158u8,36u8,187u8,46u8,102u8,119u8,249u8,142u8,72u8,77u8,37u8,83u8,205u8,114u8,174u8, - 142u8,27u8,203u8,168u8,230u8,234u8,171u8,176u8,46u8,178u8,36u8,180u8,238u8,74u8,250u8,214u8,150u8,108u8,173u8,100u8, - 220u8,112u8,60u8,77u8,47u8,193u8,211u8,197u8,111u8,190u8,104u8,87u8,163u8,202u8,116u8,152u8,98u8,191u8,137u8,193u8, - 219u8,115u8,118u8,53u8,156u8,85u8,79u8,71u8,203u8,125u8,132u8,105u8,183u8,224u8,201u8,28u8,165u8,227u8,4u8,38u8, - 54u8,92u8,153u8,88u8,248u8,95u8,232u8,58u8,118u8,95u8,14u8,125u8,90u8,117u8,241u8,72u8,152u8,232u8,143u8,134u8, - 69u8,198u8,44u8,116u8,197u8,157u8,216u8,210u8,210u8,209u8,243u8,193u8,180u8,46u8,170u8,3u8,120u8,225u8,39u8,29u8, - 222u8,91u8,22u8,36u8,187u8,142u8,245u8,206u8,198u8,20u8,3u8,233u8,195u8,56u8,223u8,250u8,131u8,120u8,237u8,78u8, - 175u8,37u8,66u8,178u8,99u8,193u8,255u8,132u8,91u8,166u8,26u8,140u8,143u8,114u8,10u8,88u8,194u8,33u8,126u8,197u8, - 91u8,90u8,241u8,108u8,60u8,145u8,83u8,160u8,149u8,195u8,119u8,64u8,79u8,31u8,92u8,129u8,197u8,80u8,249u8,43u8, - 211u8,52u8,107u8,173u8,52u8,167u8,93u8,48u8,2u8,22u8,233u8,171u8,21u8,209u8,88u8,108u8,9u8,7u8,234u8,69u8, - 175u8,78u8,142u8,6u8,175u8,143u8,227u8,51u8,240u8,81u8,156u8,14u8,206u8,79u8,78u8,151u8,27u8,5u8,255u8,171u8, - 115u8,99u8,239u8,107u8,246u8,129u8,216u8,133u8,152u8,68u8,145u8,176u8,45u8,187u8,6u8,111u8,216u8,168u8,176u8,89u8, - 196u8,136u8,110u8,255u8,13u8,235u8,80u8,193u8,49u8,111u8,55u8,102u8,111u8,101u8,13u8,33u8,124u8,89u8,57u8,141u8, - 96u8,17u8,13u8,89u8,58u8,195u8,239u8,86u8,98u8,239u8,60u8,122u8,225u8,73u8,97u8,9u8,58u8,139u8,168u8,160u8, - 129u8,150u8,87u8,109u8,215u8,214u8,141u8,95u8,159u8,159u8,29u8,190u8,249u8,185u8,219u8,13u8,215u8,232u8,136u8,189u8, - 245u8,57u8,182u8,44u8,232u8,120u8,90u8,254u8,232u8,107u8,127u8,196u8,7u8,107u8,113u8,105u8,132u8,150u8,108u8,47u8, - 40u8,80u8,127u8,195u8,89u8,132u8,189u8,134u8,207u8,174u8,44u8,241u8,58u8,223u8,27u8,99u8,16u8,91u8,182u8,194u8, - 234u8,201u8,124u8,247u8,174u8,54u8,236u8,53u8,87u8,250u8,152u8,43u8,93u8,188u8,238u8,218u8,105u8,90u8,94u8,103u8, - 85u8,133u8,194u8,119u8,148u8,230u8,25u8,112u8,25u8,242u8,215u8,50u8,96u8,53u8,137u8,206u8,70u8,133u8,49u8,76u8, - 37u8,134u8,2u8,183u8,229u8,211u8,224u8,26u8,180u8,118u8,211u8,101u8,109u8,92u8,140u8,18u8,143u8,181u8,168u8,41u8, - 61u8,95u8,184u8,155u8,213u8,18u8,132u8,214u8,38u8,13u8,133u8,159u8,129u8,199u8,74u8,197u8,175u8,27u8,58u8,94u8, - 133u8,63u8,116u8,212u8,118u8,59u8,126u8,96u8,170u8,124u8,18u8,117u8,125u8,73u8,43u8,250u8,120u8,6u8,8u8,173u8, - 31u8,252u8,35u8,62u8,11u8,88u8,168u8,70u8,101u8,25u8,219u8,249u8,96u8,133u8,210u8,3u8,228u8,212u8,54u8,190u8, - 230u8,229u8,182u8,130u8,136u8,216u8,55u8,45u8,220u8,37u8,189u8,64u8,160u8,92u8,112u8,53u8,114u8,139u8,115u8,86u8, - 215u8,109u8,31u8,3u8,78u8,81u8,140u8,182u8,178u8,185u8,255u8,246u8,59u8,28u8,58u8,176u8,29u8,8u8,17u8,179u8, - 222u8,127u8,216u8,21u8,38u8,65u8,229u8,35u8,30u8,151u8,201u8,117u8,122u8,83u8,148u8,31u8,80u8,216u8,225u8,15u8, - 182u8,170u8,199u8,255u8,253u8,108u8,129u8,129u8,48u8,15u8,22u8,182u8,120u8,61u8,157u8,251u8,81u8,85u8,143u8,128u8, - 38u8,202u8,226u8,58u8,190u8,24u8,86u8,237u8,222u8,38u8,232u8,198u8,188u8,31u8,89u8,195u8,39u8,244u8,114u8,69u8, - 87u8,41u8,247u8,247u8,103u8,245u8,248u8,239u8,207u8,204u8,71u8,162u8,228u8,251u8,179u8,0u8,136u8,128u8,220u8,32u8, - 74u8,205u8,195u8,23u8,134u8,74u8,36u8,5u8,105u8,191u8,23u8,237u8,246u8,162u8,189u8,247u8,207u8,154u8,70u8,56u8, - 60u8,61u8,61u8,57u8,141u8,207u8,127u8,123u8,123u8,104u8,13u8,114u8,241u8,237u8,17u8,24u8,239u8,3u8,160u8,203u8, - 250u8,219u8,198u8,17u8,6u8,47u8,79u8,78u8,207u8,227u8,55u8,39u8,32u8,111u8,32u8,206u8,232u8,140u8,146u8,224u8, - 8u8,241u8,164u8,24u8,146u8,82u8,251u8,109u8,139u8,197u8,28u8,156u8,188u8,58u8,100u8,173u8,44u8,95u8,68u8,253u8, - 157u8,16u8,0u8,84u8,230u8,147u8,244u8,25u8,117u8,186u8,182u8,35u8,82u8,35u8,139u8,224u8,3u8,162u8,25u8,99u8, - 145u8,48u8,57u8,28u8,72u8,199u8,220u8,152u8,79u8,146u8,196u8,245u8,237u8,52u8,229u8,111u8,43u8,64u8,122u8,223u8, - 68u8,183u8,240u8,190u8,182u8,193u8,94u8,168u8,73u8,188u8,103u8,155u8,21u8,120u8,147u8,166u8,29u8,91u8,46u8,72u8, - 140u8,230u8,170u8,181u8,219u8,130u8,232u8,31u8,59u8,159u8,250u8,154u8,248u8,86u8,93u8,4u8,88u8,77u8,186u8,152u8, - 255u8,16u8,143u8,39u8,201u8,101u8,101u8,10u8,93u8,123u8,112u8,89u8,230u8,81u8,13u8,130u8,214u8,157u8,219u8,142u8, - 128u8,63u8,239u8,116u8,223u8,171u8,230u8,53u8,218u8,26u8,36u8,237u8,1u8,242u8,195u8,247u8,148u8,146u8,14u8,33u8, - 187u8,152u8,138u8,199u8,131u8,243u8,6u8,5u8,16u8,66u8,0u8,200u8,162u8,99u8,175u8,65u8,111u8,174u8,33u8,59u8, - 146u8,241u8,107u8,140u8,217u8,31u8,169u8,252u8,182u8,227u8,46u8,190u8,111u8,178u8,45u8,6u8,101u8,38u8,224u8,226u8, - 62u8,0u8,238u8,31u8,116u8,83u8,70u8,228u8,43u8,238u8,138u8,95u8,68u8,71u8,207u8,120u8,79u8,252u8,242u8,180u8, - 251u8,222u8,159u8,132u8,71u8,199u8,6u8,202u8,56u8,222u8,169u8,65u8,157u8,220u8,204u8,202u8,136u8,251u8,78u8,53u8, - 206u8,93u8,231u8,151u8,61u8,101u8,123u8,183u8,241u8,156u8,115u8,172u8,240u8,101u8,145u8,246u8,67u8,121u8,164u8,113u8, - 223u8,251u8,254u8,110u8,240u8,253u8,93u8,239u8,251u8,123u8,193u8,247u8,247u8,220u8,14u8,40u8,134u8,13u8,206u8,151u8, - 54u8,39u8,49u8,4u8,70u8,158u8,223u8,175u8,57u8,48u8,94u8,168u8,151u8,82u8,220u8,183u8,74u8,149u8,238u8,242u8, - 188u8,84u8,125u8,75u8,239u8,137u8,101u8,170u8,166u8,75u8,26u8,6u8,235u8,134u8,230u8,129u8,176u8,33u8,203u8,212u8, - 240u8,154u8,105u8,55u8,120u8,60u8,55u8,75u8,229u8,50u8,92u8,147u8,131u8,243u8,113u8,119u8,27u8,238u8,247u8,187u8, - 203u8,126u8,175u8,221u8,152u8,226u8,91u8,246u8,230u8,239u8,139u8,232u8,186u8,24u8,210u8,147u8,81u8,129u8,103u8,131u8, - 134u8,39u8,246u8,152u8,212u8,119u8,239u8,170u8,22u8,170u8,250u8,195u8,239u8,6u8,123u8,193u8,17u8,154u8,156u8,220u8, - 72u8,177u8,189u8,5u8,191u8,216u8,93u8,248u8,139u8,61u8,237u8,11u8,56u8,236u8,29u8,235u8,148u8,169u8,184u8,92u8, - 180u8,7u8,14u8,156u8,180u8,202u8,31u8,215u8,116u8,15u8,144u8,21u8,134u8,195u8,78u8,82u8,25u8,222u8,220u8,18u8, - 55u8,19u8,88u8,53u8,192u8,91u8,217u8,156u8,60u8,232u8,77u8,245u8,93u8,104u8,105u8,192u8,139u8,61u8,167u8,184u8, - 114u8,57u8,178u8,47u8,76u8,70u8,187u8,90u8,100u8,1u8,107u8,9u8,171u8,190u8,87u8,50u8,38u8,81u8,204u8,234u8, - 39u8,197u8,248u8,9u8,245u8,59u8,117u8,111u8,153u8,61u8,130u8,47u8,226u8,11u8,21u8,127u8,240u8,131u8,42u8,0u8, - 159u8,254u8,23u8,130u8,143u8,23u8,239u8,251u8,38u8,124u8,220u8,206u8,36u8,54u8,124u8,84u8,53u8,30u8,13u8,60u8, - 46u8,68u8,230u8,3u8,4u8,240u8,115u8,254u8,236u8,70u8,59u8,117u8,149u8,94u8,49u8,209u8,170u8,200u8,182u8,78u8, - 186u8,48u8,153u8,150u8,179u8,32u8,143u8,172u8,253u8,146u8,228u8,186u8,94u8,226u8,243u8,220u8,119u8,106u8,32u8,18u8, - 93u8,70u8,5u8,191u8,220u8,155u8,247u8,37u8,53u8,131u8,192u8,27u8,113u8,14u8,254u8,244u8,85u8,209u8,4u8,238u8, - 54u8,218u8,235u8,82u8,148u8,111u8,87u8,203u8,205u8,231u8,143u8,240u8,237u8,45u8,24u8,151u8,183u8,163u8,155u8,85u8, - 2u8,215u8,120u8,133u8,170u8,32u8,170u8,137u8,84u8,97u8,63u8,108u8,141u8,149u8,54u8,230u8,251u8,7u8,225u8,244u8, - 149u8,16u8,99u8,14u8,143u8,117u8,57u8,154u8,69u8,155u8,120u8,85u8,145u8,184u8,154u8,202u8,225u8,192u8,112u8,185u8, - 38u8,105u8,91u8,37u8,72u8,232u8,66u8,190u8,65u8,128u8,246u8,92u8,147u8,98u8,93u8,112u8,227u8,155u8,15u8,104u8, - 161u8,82u8,7u8,109u8,212u8,47u8,117u8,221u8,134u8,117u8,198u8,244u8,186u8,221u8,86u8,85u8,31u8,195u8,151u8,144u8, - 26u8,85u8,59u8,175u8,34u8,38u8,66u8,47u8,253u8,128u8,90u8,181u8,38u8,53u8,208u8,154u8,123u8,249u8,60u8,101u8, - 195u8,63u8,220u8,124u8,162u8,106u8,230u8,230u8,179u8,93u8,167u8,133u8,161u8,99u8,0u8,152u8,77u8,88u8,145u8,24u8, - 141u8,28u8,149u8,158u8,138u8,191u8,170u8,42u8,143u8,15u8,193u8,12u8,105u8,103u8,38u8,180u8,80u8,235u8,61u8,198u8, - 75u8,207u8,103u8,161u8,192u8,105u8,237u8,221u8,45u8,54u8,246u8,215u8,131u8,143u8,45u8,248u8,196u8,223u8,126u8,7u8, - 109u8,139u8,73u8,1u8,228u8,131u8,104u8,121u8,51u8,95u8,6u8,165u8,161u8,189u8,192u8,10u8,16u8,59u8,59u8,59u8, - 47u8,123u8,145u8,240u8,109u8,192u8,111u8,216u8,243u8,168u8,53u8,146u8,253u8,145u8,150u8,133u8,47u8,13u8,58u8,102u8, - 189u8,157u8,105u8,214u8,208u8,21u8,132u8,229u8,240u8,199u8,194u8,135u8,249u8,61u8,54u8,5u8,139u8,217u8,105u8,56u8, - 212u8,251u8,0u8,202u8,186u8,40u8,36u8,193u8,62u8,0u8,112u8,238u8,46u8,6u8,206u8,229u8,184u8,92u8,59u8,160u8, - 247u8,151u8,7u8,186u8,108u8,251u8,43u8,10u8,136u8,135u8,1u8,253u8,69u8,184u8,227u8,156u8,227u8,0u8,206u8,216u8, - 204u8,244u8,220u8,70u8,52u8,177u8,213u8,12u8,209u8,167u8,167u8,113u8,109u8,235u8,149u8,217u8,3u8,25u8,45u8,35u8, - 54u8,191u8,94u8,123u8,68u8,242u8,44u8,15u8,59u8,157u8,255u8,210u8,94u8,155u8,151u8,236u8,145u8,222u8,155u8,255u8, - 220u8,245u8,110u8,232u8,189u8,255u8,215u8,117u8,18u8,248u8,43u8,192u8,181u8,195u8,229u8,113u8,13u8,255u8,3u8,150u8, - 182u8,225u8,87u8,21u8,253u8,89u8,215u8,70u8,224u8,127u8,113u8,122u8,230u8,249u8,79u8,49u8,184u8,127u8,157u8,206u8, - 200u8,49u8,246u8,161u8,120u8,232u8,4u8,110u8,107u8,53u8,223u8,204u8,161u8,225u8,126u8,136u8,134u8,61u8,51u8,247u8, - 190u8,89u8,136u8,44u8,27u8,137u8,155u8,92u8,165u8,11u8,208u8,105u8,40u8,172u8,206u8,112u8,174u8,197u8,241u8,59u8, - 202u8,19u8,21u8,176u8,154u8,194u8,234u8,98u8,81u8,91u8,58u8,54u8,162u8,111u8,242u8,12u8,88u8,152u8,117u8,89u8, - 93u8,182u8,51u8,156u8,149u8,101u8,92u8,125u8,232u8,69u8,244u8,151u8,233u8,135u8,174u8,72u8,95u8,20u8,19u8,97u8, - 44u8,132u8,101u8,30u8,81u8,83u8,202u8,206u8,174u8,233u8,184u8,163u8,59u8,81u8,31u8,226u8,89u8,174u8,10u8,130u8, - 57u8,3u8,176u8,157u8,227u8,231u8,24u8,64u8,208u8,94u8,237u8,108u8,137u8,89u8,205u8,1u8,147u8,25u8,160u8,12u8, - 171u8,117u8,106u8,13u8,165u8,125u8,28u8,155u8,195u8,226u8,55u8,88u8,141u8,155u8,81u8,30u8,5u8,234u8,183u8,204u8, - 133u8,5u8,85u8,96u8,89u8,50u8,87u8,68u8,67u8,33u8,140u8,90u8,72u8,173u8,87u8,44u8,38u8,76u8,6u8,243u8, - 90u8,246u8,72u8,174u8,97u8,21u8,207u8,255u8,93u8,112u8,32u8,193u8,119u8,24u8,183u8,121u8,111u8,65u8,183u8,44u8, - 138u8,177u8,86u8,192u8,137u8,159u8,236u8,1u8,207u8,164u8,59u8,130u8,9u8,147u8,75u8,59u8,250u8,47u8,99u8,72u8, - 90u8,52u8,9u8,20u8,124u8,187u8,166u8,146u8,165u8,225u8,239u8,59u8,64u8,177u8,90u8,17u8,219u8,73u8,246u8,141u8, - 217u8,144u8,14u8,88u8,122u8,158u8,70u8,115u8,251u8,54u8,116u8,90u8,53u8,142u8,222u8,215u8,201u8,211u8,234u8,82u8, - 64u8,252u8,13u8,176u8,131u8,131u8,205u8,66u8,32u8,124u8,10u8,1u8,185u8,114u8,54u8,172u8,57u8,246u8,33u8,242u8, - 211u8,203u8,1u8,214u8,37u8,233u8,79u8,28u8,183u8,217u8,68u8,182u8,17u8,96u8,141u8,123u8,179u8,56u8,12u8,112u8, - 69u8,44u8,156u8,138u8,185u8,8u8,241u8,225u8,43u8,70u8,228u8,103u8,7u8,191u8,28u8,30u8,29u8,186u8,19u8,182u8, - 33u8,8u8,158u8,239u8,105u8,209u8,64u8,175u8,105u8,48u8,85u8,234u8,95u8,125u8,175u8,131u8,179u8,187u8,18u8,79u8, - 92u8,200u8,86u8,116u8,10u8,66u8,7u8,125u8,23u8,34u8,73u8,4u8,124u8,23u8,54u8,184u8,191u8,148u8,227u8,162u8, - 77u8,219u8,149u8,141u8,143u8,66u8,247u8,81u8,244u8,191u8,138u8,143u8,226u8,81u8,39u8,192u8,79u8,252u8,174u8,176u8, - 190u8,66u8,32u8,252u8,211u8,112u8,202u8,86u8,110u8,130u8,39u8,11u8,105u8,119u8,213u8,197u8,236u8,58u8,33u8,153u8, - 1u8,111u8,218u8,229u8,235u8,58u8,3u8,158u8,125u8,117u8,55u8,135u8,139u8,157u8,206u8,94u8,119u8,123u8,61u8,155u8, - 217u8,91u8,117u8,51u8,123u8,10u8,32u8,75u8,251u8,139u8,237u8,110u8,11u8,247u8,222u8,77u8,252u8,101u8,189u8,196u8, - 193u8,102u8,20u8,248u8,103u8,238u8,17u8,251u8,56u8,58u8,101u8,2u8,93u8,124u8,11u8,226u8,165u8,255u8,45u8,32u8, - 129u8,250u8,231u8,238u8,183u8,93u8,191u8,12u8,16u8,185u8,90u8,106u8,191u8,187u8,239u8,223u8,7u8,43u8,52u8,136u8, - 34u8,233u8,122u8,201u8,127u8,177u8,250u8,22u8,78u8,109u8,61u8,225u8,82u8,148u8,20u8,181u8,135u8,20u8,148u8,180u8, - 51u8,231u8,123u8,113u8,157u8,223u8,254u8,190u8,23u8,109u8,25u8,80u8,160u8,241u8,182u8,180u8,109u8,174u8,101u8,224u8, - 93u8,107u8,224u8,221u8,197u8,194u8,43u8,119u8,226u8,235u8,227u8,232u8,180u8,180u8,219u8,116u8,43u8,250u8,26u8,126u8, - 190u8,59u8,35u8,185u8,166u8,21u8,172u8,196u8,90u8,239u8,211u8,57u8,63u8,44u8,159u8,238u8,195u8,59u8,235u8,221u8, - 47u8,163u8,157u8,170u8,14u8,224u8,15u8,196u8,101u8,164u8,20u8,203u8,54u8,103u8,122u8,159u8,178u8,2u8,215u8,173u8, - 182u8,26u8,227u8,133u8,50u8,97u8,215u8,25u8,240u8,237u8,219u8,33u8,95u8,161u8,107u8,142u8,70u8,162u8,43u8,33u8, - 222u8,252u8,210u8,202u8,159u8,99u8,7u8,68u8,174u8,96u8,50u8,150u8,128u8,9u8,14u8,121u8,241u8,164u8,152u8,110u8, - 27u8,245u8,150u8,237u8,37u8,88u8,169u8,183u8,239u8,215u8,178u8,106u8,253u8,172u8,91u8,76u8,218u8,144u8,43u8,185u8, - 250u8,114u8,122u8,81u8,115u8,42u8,230u8,151u8,33u8,254u8,64u8,15u8,242u8,77u8,226u8,238u8,202u8,214u8,110u8,227u8, - 233u8,126u8,105u8,251u8,246u8,139u8,51u8,10u8,223u8,190u8,123u8,81u8,51u8,19u8,161u8,218u8,19u8,1u8,54u8,50u8, - 143u8,133u8,152u8,120u8,124u8,39u8,92u8,164u8,213u8,150u8,250u8,70u8,62u8,94u8,155u8,53u8,233u8,131u8,175u8,145u8, - 173u8,56u8,140u8,196u8,6u8,50u8,135u8,40u8,93u8,251u8,29u8,21u8,152u8,195u8,74u8,78u8,182u8,85u8,65u8,140u8, - 60u8,103u8,111u8,231u8,46u8,54u8,178u8,23u8,216u8,8u8,181u8,82u8,25u8,153u8,145u8,33u8,137u8,41u8,172u8,145u8, - 57u8,222u8,135u8,216u8,94u8,234u8,92u8,246u8,26u8,14u8,125u8,109u8,162u8,0u8,54u8,247u8,195u8,151u8,15u8,70u8, - 238u8,129u8,246u8,255u8,116u8,49u8,237u8,159u8,67u8,46u8,153u8,76u8,238u8,73u8,70u8,193u8,70u8,122u8,124u8,73u8, - 239u8,232u8,221u8,113u8,255u8,85u8,229u8,211u8,34u8,36u8,221u8,66u8,253u8,122u8,72u8,196u8,200u8,9u8,145u8,144u8, - 105u8,156u8,222u8,192u8,244u8,240u8,128u8,53u8,184u8,141u8,129u8,185u8,231u8,90u8,131u8,102u8,188u8,179u8,3u8,4u8, - 59u8,25u8,109u8,200u8,246u8,107u8,144u8,237u8,238u8,3u8,83u8,250u8,84u8,57u8,176u8,93u8,41u8,85u8,11u8,188u8, - 190u8,82u8,166u8,143u8,43u8,106u8,168u8,56u8,73u8,199u8,181u8,86u8,126u8,122u8,130u8,97u8,114u8,196u8,57u8,214u8, - 210u8,94u8,224u8,93u8,36u8,241u8,14u8,53u8,185u8,221u8,237u8,21u8,20u8,164u8,59u8,166u8,211u8,198u8,28u8,140u8, - 112u8,221u8,233u8,13u8,205u8,252u8,235u8,220u8,112u8,92u8,245u8,238u8,162u8,91u8,16u8,174u8,205u8,125u8,63u8,87u8, - 214u8,234u8,55u8,196u8,100u8,75u8,175u8,23u8,145u8,41u8,194u8,124u8,225u8,132u8,54u8,245u8,139u8,187u8,254u8,57u8, - 181u8,79u8,177u8,183u8,87u8,67u8,185u8,98u8,211u8,172u8,208u8,10u8,31u8,107u8,53u8,143u8,225u8,120u8,203u8,100u8, - 88u8,187u8,69u8,211u8,196u8,34u8,220u8,5u8,200u8,65u8,94u8,168u8,75u8,41u8,63u8,88u8,158u8,38u8,81u8,195u8, - 45u8,186u8,5u8,250u8,193u8,146u8,25u8,148u8,150u8,35u8,106u8,131u8,109u8,183u8,138u8,251u8,56u8,213u8,64u8,68u8, - 60u8,247u8,169u8,103u8,81u8,190u8,82u8,29u8,91u8,158u8,34u8,112u8,91u8,6u8,202u8,170u8,177u8,214u8,230u8,170u8, - 255u8,97u8,169u8,204u8,65u8,231u8,22u8,44u8,153u8,214u8,162u8,84u8,229u8,198u8,79u8,223u8,72u8,245u8,62u8,154u8, - 95u8,32u8,249u8,182u8,7u8,54u8,116u8,30u8,27u8,191u8,253u8,208u8,250u8,200u8,159u8,238u8,252u8,219u8,171u8,189u8, - 245u8,28u8,185u8,92u8,69u8,155u8,227u8,214u8,22u8,253u8,23u8,71u8,1u8,9u8,135u8,249u8,172u8,127u8,213u8,107u8, - 109u8,222u8,187u8,234u8,105u8,181u8,33u8,201u8,134u8,75u8,252u8,139u8,92u8,225u8,95u8,27u8,19u8,62u8,88u8,39u8, - 19u8,198u8,189u8,108u8,56u8,240u8,10u8,199u8,253u8,0u8,121u8,49u8,35u8,108u8,170u8,159u8,182u8,97u8,203u8,43u8, - 226u8,69u8,35u8,131u8,94u8,128u8,21u8,172u8,53u8,156u8,62u8,183u8,109u8,235u8,198u8,92u8,220u8,152u8,139u8,210u8, - 200u8,88u8,190u8,230u8,71u8,248u8,211u8,189u8,121u8,159u8,174u8,203u8,72u8,109u8,111u8,91u8,237u8,249u8,141u8,213u8, - 101u8,109u8,171u8,254u8,234u8,99u8,237u8,138u8,177u8,118u8,87u8,31u8,107u8,79u8,140u8,181u8,247u8,101u8,120u8,140u8, - 172u8,81u8,171u8,179u8,69u8,46u8,67u8,176u8,221u8,33u8,86u8,148u8,203u8,70u8,204u8,21u8,140u8,149u8,55u8,86u8, - 138u8,239u8,175u8,150u8,154u8,183u8,225u8,67u8,95u8,132u8,15u8,105u8,37u8,137u8,88u8,117u8,22u8,209u8,162u8,149u8, - 234u8,135u8,224u8,213u8,39u8,121u8,101u8,238u8,178u8,168u8,221u8,138u8,44u8,95u8,145u8,143u8,193u8,210u8,95u8,166u8, - 227u8,162u8,84u8,151u8,250u8,178u8,74u8,44u8,176u8,231u8,180u8,6u8,171u8,211u8,225u8,85u8,206u8,43u8,248u8,91u8, - 45u8,99u8,101u8,165u8,163u8,101u8,107u8,26u8,125u8,221u8,136u8,212u8,123u8,19u8,38u8,199u8,197u8,13u8,11u8,206u8, - 7u8,160u8,146u8,149u8,170u8,52u8,143u8,138u8,217u8,103u8,218u8,69u8,59u8,86u8,134u8,199u8,4u8,95u8,94u8,128u8, - 18u8,155u8,95u8,166u8,165u8,62u8,21u8,194u8,49u8,205u8,139u8,217u8,229u8,149u8,86u8,45u8,170u8,46u8,154u8,203u8, - 68u8,61u8,90u8,172u8,78u8,212u8,90u8,12u8,176u8,31u8,65u8,13u8,255u8,113u8,49u8,53u8,220u8,131u8,152u8,140u8, - 12u8,101u8,193u8,87u8,243u8,90u8,218u8,189u8,48u8,199u8,238u8,80u8,147u8,238u8,180u8,172u8,73u8,99u8,245u8,245u8, - 4u8,228u8,19u8,245u8,203u8,40u8,223u8,34u8,27u8,69u8,253u8,237u8,165u8,220u8,103u8,11u8,240u8,142u8,166u8,178u8, - 85u8,247u8,201u8,212u8,11u8,226u8,216u8,253u8,55u8,240u8,54u8,152u8,214u8,104u8,67u8,246u8,87u8,117u8,239u8,9u8, - 126u8,106u8,224u8,70u8,50u8,174u8,73u8,55u8,163u8,250u8,105u8,249u8,229u8,122u8,217u8,204u8,186u8,239u8,40u8,173u8, - 247u8,218u8,81u8,59u8,212u8,88u8,211u8,113u8,135u8,10u8,226u8,45u8,109u8,89u8,125u8,41u8,187u8,106u8,65u8,243u8, - 195u8,50u8,138u8,238u8,60u8,101u8,216u8,129u8,234u8,198u8,191u8,177u8,241u8,111u8,44u8,70u8,130u8,254u8,239u8,251u8, - 109u8,190u8,220u8,93u8,250u8,203u8,135u8,227u8,23u8,121u8,180u8,78u8,199u8,200u8,163u8,117u8,122u8,70u8,30u8,221u8, - 129u8,107u8,164u8,157u8,40u8,117u8,143u8,149u8,11u8,82u8,198u8,174u8,55u8,130u8,244u8,254u8,9u8,210u8,117u8,209u8, - 211u8,163u8,181u8,11u8,196u8,47u8,98u8,123u8,122u8,48u8,118u8,99u8,122u8,222u8,43u8,131u8,96u8,17u8,196u8,190u8, - 143u8,150u8,103u8,8u8,195u8,54u8,134u8,103u8,11u8,60u8,243u8,28u8,254u8,178u8,198u8,224u8,90u8,27u8,105u8,120u8, - 90u8,156u8,171u8,74u8,233u8,27u8,93u8,123u8,163u8,107u8,7u8,152u8,217u8,253u8,107u8,75u8,176u8,124u8,75u8,1u8, - 153u8,6u8,203u8,218u8,214u8,170u8,254u8,119u8,70u8,79u8,244u8,22u8,13u8,190u8,172u8,190u8,182u8,182u8,155u8,127u8, - 193u8,22u8,4u8,123u8,45u8,91u8,16u8,124u8,5u8,22u8,193u8,74u8,193u8,111u8,216u8,195u8,134u8,61u8,108u8,216u8, - 195u8,218u8,216u8,195u8,58u8,250u8,43u8,168u8,83u8,13u8,245u8,87u8,248u8,10u8,220u8,130u8,93u8,23u8,155u8,77u8, - 38u8,34u8,17u8,124u8,195u8,56u8,254u8,66u8,140u8,99u8,245u8,118u8,88u8,247u8,163u8,69u8,212u8,95u8,83u8,215u8, - 152u8,159u8,122u8,126u8,23u8,220u8,195u8,219u8,113u8,103u8,195u8,52u8,54u8,218u8,198u8,130u8,142u8,255u8,59u8,116u8, - 223u8,183u8,111u8,43u8,245u8,181u8,88u8,64u8,139u8,246u8,85u8,123u8,193u8,246u8,85u8,247u8,201u8,241u8,212u8,180u8, - 145u8,141u8,11u8,106u8,94u8,198u8,252u8,87u8,118u8,203u8,55u8,98u8,97u8,131u8,11u8,236u8,235u8,148u8,69u8,24u8, - 172u8,25u8,51u8,177u8,75u8,133u8,202u8,26u8,212u8,58u8,146u8,111u8,170u8,34u8,108u8,36u8,219u8,157u8,74u8,182u8, - 21u8,185u8,255u8,231u8,111u8,254u8,23u8,34u8,33u8,23u8,192u8,168u8,55u8,1u8,0u8,0u8,0u8,6u8,111u8,98u8, - 106u8,101u8,99u8,116u8,165u8,41u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,229u8,28u8,253u8,111u8, - 219u8,198u8,245u8,247u8,254u8,21u8,215u8,14u8,240u8,164u8,77u8,85u8,221u8,54u8,237u8,50u8,57u8,54u8,230u8,197u8, - 106u8,154u8,97u8,177u8,6u8,219u8,65u8,7u8,20u8,5u8,77u8,147u8,39u8,137u8,51u8,69u8,106u8,252u8,176u8,173u8, - 101u8,249u8,223u8,247u8,222u8,187u8,119u8,199u8,59u8,242u8,40u8,201u8,137u8,59u8,116u8,93u8,128u8,36u8,22u8,117u8, - 247u8,190u8,191u8,239u8,232u8,47u8,190u8,248u8,66u8,92u8,45u8,147u8,82u8,196u8,114u8,158u8,100u8,178u8,20u8,213u8, - 82u8,138u8,55u8,249u8,157u8,20u8,249u8,205u8,63u8,100u8,84u8,137u8,85u8,30u8,203u8,84u8,220u8,39u8,213u8,146u8, - 190u8,194u8,191u8,243u8,60u8,77u8,243u8,251u8,36u8,91u8,136u8,117u8,145u8,175u8,101u8,81u8,37u8,178u8,156u8,124u8, - 242u8,5u8,64u8,250u8,92u8,92u8,38u8,171u8,117u8,154u8,204u8,19u8,25u8,139u8,178u8,202u8,139u8,112u8,33u8,69u8, - 146u8,85u8,178u8,152u8,135u8,17u8,110u8,14u8,43u8,81u8,214u8,235u8,117u8,94u8,84u8,165u8,8u8,197u8,82u8,194u8, - 55u8,249u8,66u8,102u8,50u8,175u8,75u8,17u8,1u8,88u8,192u8,152u8,228u8,153u8,200u8,231u8,162u8,144u8,101u8,94u8, - 23u8,17u8,82u8,148u8,139u8,27u8,73u8,224u8,5u8,193u8,4u8,208u8,21u8,108u8,2u8,74u8,138u8,177u8,162u8,93u8, - 102u8,225u8,77u8,10u8,43u8,227u8,176u8,10u8,69u8,181u8,89u8,171u8,77u8,229u8,50u8,44u8,36u8,96u8,137u8,242u8, - 213u8,10u8,96u8,70u8,176u8,83u8,173u8,72u8,195u8,141u8,44u8,196u8,64u8,142u8,23u8,227u8,17u8,44u8,188u8,149u8, - 89u8,57u8,28u8,49u8,130u8,251u8,101u8,146u8,74u8,177u8,12u8,239u8,144u8,189u8,34u8,137u8,0u8,137u8,144u8,15u8, - 21u8,44u8,1u8,186u8,74u8,189u8,41u8,202u8,179u8,8u8,248u8,22u8,85u8,18u8,221u8,202u8,106u8,36u8,202u8,251u8, - 188u8,136u8,135u8,99u8,150u8,193u8,171u8,52u8,191u8,9u8,211u8,116u8,35u8,194u8,8u8,232u8,47u8,19u8,160u8,77u8, - 33u8,14u8,179u8,88u8,228u8,247u8,153u8,44u8,202u8,101u8,178u8,102u8,201u8,146u8,76u8,52u8,3u8,81u8,33u8,67u8, - 224u8,176u8,164u8,149u8,177u8,188u8,147u8,41u8,202u8,151u8,184u8,137u8,147u8,168u8,10u8,43u8,45u8,7u8,84u8,67u8, - 184u8,6u8,73u8,71u8,33u8,9u8,12u8,215u8,131u8,216u8,101u8,149u8,172u8,36u8,74u8,15u8,209u8,105u8,114u8,166u8, - 138u8,124u8,164u8,2u8,244u8,181u8,40u8,194u8,213u8,10u8,153u8,179u8,208u8,27u8,149u8,36u8,89u8,156u8,220u8,37u8, - 113u8,29u8,166u8,201u8,191u8,66u8,173u8,137u8,186u8,4u8,25u8,88u8,200u8,74u8,218u8,196u8,132u8,164u8,64u8,36u8, - 105u8,25u8,41u8,34u8,17u8,207u8,1u8,129u8,4u8,121u8,220u8,2u8,180u8,40u8,173u8,99u8,196u8,165u8,132u8,172u8, - 9u8,186u8,84u8,232u8,132u8,92u8,37u8,85u8,133u8,95u8,3u8,140u8,12u8,176u8,199u8,73u8,1u8,250u8,79u8,55u8, - 160u8,148u8,37u8,216u8,3u8,88u8,82u8,145u8,147u8,30u8,226u8,164u8,140u8,114u8,196u8,115u8,147u8,164u8,73u8,181u8, - 65u8,162u8,120u8,75u8,88u8,150u8,121u8,148u8,128u8,92u8,98u8,50u8,81u8,38u8,74u8,153u8,174u8,193u8,247u8,18u8, - 104u8,78u8,98u8,216u8,94u8,145u8,108u8,144u8,210u8,58u8,131u8,207u8,233u8,6u8,129u8,151u8,155u8,178u8,146u8,43u8, - 113u8,179u8,209u8,188u8,144u8,230u8,217u8,250u8,196u8,162u8,200u8,235u8,117u8,9u8,38u8,95u8,136u8,69u8,8u8,150u8, - 54u8,159u8,39u8,81u8,34u8,179u8,104u8,163u8,13u8,38u8,188u8,203u8,19u8,226u8,49u8,202u8,75u8,160u8,29u8,180u8, - 6u8,226u8,74u8,26u8,1u8,162u8,102u8,220u8,39u8,184u8,176u8,28u8,169u8,47u8,148u8,36u8,136u8,71u8,153u8,202u8, - 138u8,25u8,36u8,194u8,9u8,254u8,213u8,236u8,108u8,166u8,92u8,235u8,119u8,96u8,234u8,18u8,196u8,11u8,230u8,158u8, - 229u8,98u8,37u8,195u8,76u8,121u8,70u8,94u8,20u8,249u8,61u8,0u8,211u8,254u8,10u8,132u8,134u8,64u8,254u8,28u8, - 214u8,102u8,232u8,114u8,121u8,243u8,221u8,88u8,252u8,0u8,86u8,3u8,48u8,228u8,195u8,58u8,205u8,11u8,196u8,186u8, - 132u8,189u8,85u8,206u8,172u8,172u8,194u8,91u8,128u8,15u8,230u8,144u8,43u8,179u8,104u8,195u8,17u8,11u8,50u8,108u8, - 141u8,41u8,2u8,192u8,55u8,18u8,214u8,84u8,117u8,145u8,129u8,2u8,230u8,69u8,190u8,130u8,69u8,243u8,58u8,35u8, - 39u8,30u8,127u8,2u8,22u8,86u8,167u8,104u8,167u8,85u8,94u8,6u8,198u8,44u8,38u8,19u8,222u8,255u8,238u8,19u8, - 192u8,137u8,214u8,5u8,94u8,29u8,79u8,38u8,55u8,81u8,121u8,228u8,62u8,145u8,192u8,90u8,209u8,122u8,182u8,12u8, - 203u8,101u8,235u8,81u8,153u8,44u8,192u8,165u8,90u8,15u8,239u8,0u8,3u8,110u8,54u8,79u8,59u8,84u8,128u8,115u8, - 230u8,117u8,86u8,29u8,245u8,175u8,32u8,111u8,148u8,129u8,130u8,223u8,250u8,184u8,101u8,27u8,153u8,230u8,150u8,239u8, - 81u8,78u8,129u8,195u8,109u8,103u8,201u8,162u8,78u8,98u8,166u8,29u8,85u8,115u8,106u8,180u8,27u8,166u8,64u8,68u8, - 188u8,1u8,13u8,38u8,37u8,154u8,63u8,132u8,32u8,12u8,128u8,97u8,28u8,131u8,197u8,150u8,180u8,1u8,98u8,83u8, - 89u8,137u8,233u8,236u8,207u8,127u8,153u8,190u8,188u8,10u8,166u8,127u8,127u8,125u8,121u8,117u8,57u8,17u8,245u8,183u8, - 207u8,196u8,177u8,248u8,242u8,200u8,3u8,51u8,206u8,37u8,218u8,84u8,165u8,128u8,238u8,1u8,243u8,108u8,54u8,189u8, - 12u8,206u8,103u8,12u8,92u8,195u8,254u8,170u8,129u8,13u8,150u8,218u8,1u8,14u8,33u8,21u8,221u8,110u8,65u8,190u8, - 90u8,21u8,96u8,190u8,115u8,140u8,108u8,42u8,242u8,197u8,54u8,150u8,243u8,89u8,240u8,246u8,252u8,213u8,233u8,213u8, - 244u8,44u8,184u8,186u8,56u8,61u8,191u8,252u8,110u8,122u8,97u8,24u8,248u8,218u8,69u8,18u8,65u8,152u8,133u8,224u8, - 228u8,34u8,105u8,34u8,44u8,196u8,206u8,85u8,82u8,82u8,228u8,118u8,17u8,92u8,5u8,204u8,202u8,236u8,135u8,243u8, - 233u8,133u8,6u8,254u8,108u8,59u8,7u8,33u8,102u8,61u8,138u8,4u8,228u8,171u8,0u8,213u8,6u8,250u8,242u8,244u8, - 28u8,225u8,158u8,77u8,255u8,58u8,189u8,154u8,106u8,136u8,223u8,52u8,16u8,167u8,15u8,145u8,148u8,113u8,9u8,78u8, - 246u8,144u8,172u8,234u8,149u8,128u8,92u8,75u8,126u8,143u8,240u8,26u8,239u8,213u8,130u8,25u8,219u8,160u8,223u8,156u8, - 254u8,253u8,245u8,155u8,183u8,111u8,130u8,243u8,233u8,229u8,213u8,235u8,243u8,87u8,26u8,248u8,183u8,46u8,185u8,38u8, - 100u8,37u8,138u8,92u8,78u8,149u8,164u8,78u8,112u8,138u8,181u8,140u8,84u8,90u8,102u8,189u8,58u8,24u8,46u8,166u8, - 151u8,179u8,183u8,23u8,47u8,167u8,61u8,170u8,253u8,131u8,101u8,139u8,211u8,7u8,76u8,4u8,9u8,134u8,186u8,82u8, - 174u8,67u8,10u8,170u8,136u8,224u8,213u8,219u8,215u8,103u8,128u8,5u8,211u8,252u8,141u8,172u8,238u8,165u8,204u8,196u8, - 140u8,109u8,22u8,2u8,221u8,169u8,114u8,54u8,12u8,35u8,235u8,130u8,28u8,4u8,147u8,35u8,4u8,229u8,172u8,194u8, - 128u8,2u8,113u8,55u8,13u8,215u8,54u8,73u8,175u8,207u8,95u8,95u8,5u8,8u8,51u8,120u8,121u8,49u8,61u8,189u8, - 122u8,61u8,59u8,15u8,206u8,223u8,190u8,209u8,4u8,29u8,62u8,60u8,59u8,180u8,254u8,88u8,228u8,189u8,105u8,139u8, - 23u8,131u8,82u8,158u8,25u8,109u8,82u8,60u8,204u8,117u8,237u8,0u8,194u8,1u8,105u8,113u8,186u8,160u8,128u8,86u8, - 201u8,104u8,153u8,37u8,17u8,165u8,111u8,178u8,166u8,36u8,155u8,99u8,1u8,99u8,16u8,48u8,224u8,145u8,184u8,169u8, - 145u8,181u8,141u8,128u8,18u8,33u8,186u8,45u8,33u8,148u8,71u8,75u8,72u8,71u8,70u8,131u8,144u8,143u8,82u8,224u8, - 44u8,3u8,56u8,16u8,36u8,229u8,93u8,152u8,214u8,202u8,244u8,85u8,177u8,37u8,29u8,110u8,181u8,134u8,217u8,42u8, - 27u8,69u8,63u8,7u8,102u8,159u8,91u8,236u8,93u8,2u8,54u8,200u8,240u8,36u8,56u8,84u8,103u8,129u8,241u8,3u8, - 235u8,33u8,129u8,85u8,20u8,41u8,195u8,24u8,211u8,111u8,141u8,15u8,139u8,107u8,120u8,18u8,224u8,135u8,107u8,113u8, - 151u8,132u8,24u8,164u8,225u8,241u8,82u8,233u8,76u8,111u8,140u8,49u8,5u8,82u8,22u8,87u8,69u8,8u8,88u8,142u8, - 65u8,123u8,173u8,76u8,75u8,129u8,24u8,91u8,222u8,97u8,225u8,0u8,182u8,26u8,80u8,33u8,20u8,132u8,102u8,243u8, - 245u8,181u8,249u8,89u8,168u8,236u8,76u8,128u8,128u8,53u8,40u8,209u8,190u8,14u8,190u8,250u8,230u8,219u8,1u8,6u8, - 60u8,241u8,111u8,208u8,235u8,119u8,103u8,67u8,239u8,198u8,123u8,74u8,126u8,215u8,180u8,238u8,88u8,112u8,236u8,54u8, - 49u8,25u8,31u8,15u8,156u8,248u8,60u8,176u8,40u8,30u8,14u8,13u8,28u8,203u8,99u8,128u8,94u8,68u8,167u8,52u8, - 16u8,130u8,65u8,66u8,150u8,190u8,147u8,88u8,82u8,128u8,124u8,226u8,124u8,21u8,38u8,153u8,182u8,112u8,204u8,218u8, - 85u8,184u8,176u8,173u8,151u8,98u8,37u8,26u8,88u8,88u8,131u8,204u8,64u8,25u8,92u8,136u8,221u8,202u8,13u8,25u8, - 188u8,241u8,70u8,38u8,213u8,32u8,134u8,186u8,35u8,185u8,99u8,144u8,8u8,14u8,114u8,100u8,100u8,41u8,205u8,231u8, - 158u8,108u8,21u8,223u8,93u8,204u8,222u8,40u8,151u8,56u8,61u8,59u8,3u8,143u8,189u8,12u8,46u8,95u8,126u8,63u8, - 125u8,51u8,101u8,51u8,65u8,102u8,158u8,212u8,82u8,200u8,117u8,44u8,131u8,128u8,85u8,142u8,29u8,16u8,159u8,33u8, - 60u8,131u8,184u8,118u8,237u8,104u8,188u8,79u8,203u8,214u8,110u8,80u8,54u8,238u8,83u8,58u8,159u8,14u8,199u8,189u8, - 10u8,154u8,254u8,143u8,41u8,232u8,114u8,58u8,237u8,87u8,208u8,148u8,21u8,244u8,155u8,31u8,53u8,250u8,128u8,10u8, - 204u8,96u8,37u8,87u8,55u8,96u8,180u8,244u8,1u8,237u8,219u8,95u8,50u8,77u8,38u8,42u8,164u8,190u8,194u8,101u8, - 195u8,159u8,220u8,84u8,136u8,181u8,55u8,87u8,183u8,78u8,235u8,70u8,165u8,156u8,238u8,238u8,76u8,110u8,28u8,153u8, - 64u8,197u8,53u8,167u8,42u8,72u8,85u8,89u8,173u8,120u8,43u8,171u8,162u8,6u8,40u8,10u8,229u8,75u8,4u8,15u8, - 197u8,23u8,201u8,79u8,85u8,110u8,26u8,251u8,219u8,82u8,197u8,14u8,242u8,77u8,180u8,175u8,26u8,84u8,2u8,33u8, - 83u8,114u8,189u8,8u8,17u8,176u8,206u8,146u8,127u8,214u8,210u8,4u8,90u8,196u8,164u8,188u8,85u8,33u8,68u8,84u8, - 50u8,92u8,149u8,6u8,44u8,130u8,10u8,104u8,5u8,168u8,33u8,200u8,234u8,21u8,5u8,254u8,145u8,131u8,22u8,153u8, - 214u8,86u8,59u8,176u8,138u8,95u8,165u8,206u8,161u8,226u8,27u8,248u8,45u8,85u8,200u8,85u8,43u8,12u8,4u8,18u8, - 196u8,68u8,3u8,112u8,33u8,207u8,220u8,92u8,76u8,133u8,50u8,22u8,68u8,186u8,147u8,196u8,158u8,140u8,40u8,27u8, - 113u8,165u8,132u8,133u8,129u8,234u8,17u8,160u8,83u8,129u8,122u8,134u8,204u8,14u8,165u8,153u8,169u8,15u8,14u8,244u8, - 166u8,246u8,185u8,217u8,172u8,161u8,127u8,161u8,254u8,8u8,91u8,146u8,146u8,244u8,23u8,138u8,43u8,94u8,112u8,33u8, - 231u8,99u8,179u8,147u8,112u8,4u8,92u8,63u8,5u8,26u8,198u8,4u8,234u8,255u8,60u8,117u8,169u8,159u8,98u8,87u8, - 37u8,181u8,42u8,69u8,189u8,70u8,67u8,182u8,89u8,1u8,36u8,198u8,12u8,26u8,4u8,122u8,73u8,160u8,246u8,77u8, - 212u8,254u8,201u8,100u8,138u8,255u8,125u8,15u8,220u8,164u8,242u8,133u8,166u8,140u8,158u8,157u8,40u8,180u8,239u8,253u8, - 22u8,61u8,128u8,142u8,109u8,45u8,193u8,148u8,149u8,13u8,88u8,198u8,122u8,170u8,26u8,243u8,184u8,213u8,99u8,145u8, - 248u8,176u8,102u8,33u8,18u8,149u8,6u8,236u8,25u8,128u8,106u8,248u8,5u8,250u8,188u8,154u8,43u8,120u8,140u8,148u8, - 252u8,66u8,188u8,211u8,52u8,41u8,108u8,235u8,156u8,6u8,16u8,78u8,39u8,36u8,62u8,255u8,28u8,101u8,94u8,74u8, - 202u8,249u8,148u8,164u8,169u8,215u8,140u8,101u8,99u8,189u8,160u8,159u8,16u8,45u8,91u8,9u8,208u8,237u8,25u8,177u8, - 189u8,54u8,40u8,112u8,224u8,48u8,82u8,230u8,150u8,168u8,249u8,9u8,36u8,123u8,232u8,9u8,85u8,163u8,122u8,101u8, - 69u8,34u8,44u8,24u8,236u8,124u8,89u8,230u8,43u8,224u8,74u8,25u8,64u8,72u8,13u8,21u8,22u8,110u8,80u8,46u8, - 64u8,61u8,68u8,197u8,154u8,193u8,113u8,179u8,161u8,90u8,131u8,10u8,23u8,193u8,125u8,21u8,236u8,81u8,132u8,53u8, - 159u8,181u8,163u8,199u8,226u8,106u8,140u8,163u8,152u8,36u8,13u8,11u8,213u8,79u8,251u8,86u8,149u8,226u8,202u8,32u8, - 64u8,57u8,20u8,114u8,133u8,131u8,31u8,232u8,0u8,41u8,244u8,235u8,249u8,77u8,168u8,234u8,28u8,146u8,35u8,202u8, - 31u8,103u8,12u8,30u8,225u8,191u8,88u8,3u8,61u8,21u8,236u8,187u8,58u8,161u8,64u8,1u8,250u8,7u8,204u8,49u8, - 76u8,136u8,70u8,170u8,22u8,181u8,194u8,70u8,146u8,117u8,189u8,207u8,82u8,26u8,197u8,126u8,242u8,56u8,172u8,220u8, - 104u8,166u8,161u8,91u8,255u8,69u8,2u8,182u8,135u8,186u8,180u8,43u8,21u8,248u8,8u8,129u8,120u8,158u8,44u8,234u8, - 66u8,218u8,1u8,16u8,168u8,200u8,36u8,142u8,96u8,194u8,98u8,99u8,19u8,140u8,3u8,1u8,250u8,41u8,71u8,39u8, - 35u8,106u8,145u8,78u8,139u8,192u8,82u8,166u8,243u8,158u8,232u8,112u8,41u8,169u8,138u8,132u8,253u8,18u8,59u8,229u8, - 52u8,71u8,205u8,150u8,220u8,19u8,176u8,47u8,51u8,126u8,96u8,97u8,157u8,171u8,1u8,208u8,88u8,124u8,151u8,227u8, - 36u8,41u8,132u8,241u8,152u8,28u8,217u8,139u8,238u8,195u8,210u8,129u8,175u8,2u8,99u8,76u8,133u8,26u8,151u8,52u8, - 25u8,228u8,129u8,56u8,224u8,38u8,222u8,44u8,6u8,149u8,5u8,132u8,85u8,218u8,97u8,192u8,18u8,227u8,91u8,78u8, - 248u8,172u8,215u8,198u8,250u8,109u8,245u8,58u8,154u8,60u8,35u8,112u8,182u8,76u8,186u8,186u8,243u8,136u8,198u8,131u8, - 211u8,14u8,239u8,37u8,198u8,101u8,69u8,66u8,12u8,110u8,1u8,129u8,19u8,106u8,254u8,198u8,183u8,193u8,172u8,114u8, - 77u8,153u8,143u8,40u8,154u8,95u8,197u8,79u8,72u8,212u8,95u8,193u8,246u8,195u8,194u8,10u8,179u8,35u8,152u8,68u8, - 226u8,144u8,163u8,233u8,28u8,221u8,62u8,140u8,9u8,177u8,118u8,124u8,52u8,41u8,144u8,66u8,32u8,224u8,173u8,154u8, - 108u8,192u8,163u8,204u8,52u8,199u8,102u8,194u8,137u8,215u8,218u8,244u8,105u8,4u8,131u8,233u8,34u8,101u8,159u8,192u8, - 140u8,113u8,3u8,201u8,14u8,1u8,54u8,209u8,8u8,253u8,162u8,46u8,10u8,76u8,171u8,196u8,143u8,195u8,66u8,135u8, - 245u8,71u8,24u8,191u8,47u8,101u8,90u8,156u8,233u8,188u8,3u8,149u8,122u8,134u8,131u8,52u8,203u8,198u8,127u8,203u8, - 133u8,135u8,128u8,152u8,150u8,198u8,232u8,22u8,17u8,196u8,138u8,133u8,140u8,189u8,226u8,165u8,236u8,178u8,69u8,192u8, - 92u8,11u8,117u8,9u8,68u8,179u8,246u8,60u8,174u8,242u8,126u8,162u8,255u8,166u8,202u8,59u8,44u8,73u8,56u8,140u8, - 189u8,142u8,155u8,194u8,87u8,5u8,27u8,93u8,240u8,153u8,176u8,132u8,65u8,90u8,71u8,104u8,197u8,193u8,186u8,134u8, - 60u8,31u8,225u8,232u8,75u8,175u8,14u8,170u8,156u8,29u8,246u8,5u8,180u8,209u8,80u8,52u8,157u8,12u8,90u8,116u8, - 15u8,39u8,58u8,112u8,66u8,192u8,108u8,184u8,131u8,138u8,0u8,38u8,203u8,159u8,14u8,212u8,132u8,231u8,69u8,83u8, - 125u8,105u8,0u8,195u8,145u8,160u8,193u8,216u8,100u8,2u8,116u8,4u8,115u8,212u8,255u8,192u8,63u8,153u8,25u8,14u8, - 143u8,122u8,192u8,6u8,33u8,98u8,221u8,6u8,176u8,103u8,34u8,96u8,131u8,52u8,212u8,191u8,211u8,241u8,156u8,157u8, - 248u8,125u8,91u8,202u8,103u8,88u8,82u8,43u8,33u8,187u8,69u8,52u8,71u8,34u8,85u8,6u8,172u8,192u8,55u8,113u8, - 78u8,58u8,105u8,26u8,134u8,31u8,77u8,47u8,202u8,235u8,157u8,174u8,225u8,167u8,97u8,71u8,250u8,28u8,48u8,21u8, - 150u8,128u8,119u8,113u8,223u8,49u8,17u8,7u8,218u8,12u8,8u8,202u8,68u8,168u8,33u8,225u8,139u8,250u8,249u8,201u8, - 208u8,168u8,197u8,82u8,5u8,196u8,66u8,72u8,188u8,21u8,16u8,126u8,44u8,96u8,94u8,55u8,153u8,128u8,78u8,233u8, - 51u8,3u8,180u8,100u8,161u8,32u8,193u8,80u8,113u8,189u8,134u8,88u8,53u8,56u8,88u8,213u8,188u8,85u8,161u8,242u8, - 172u8,92u8,215u8,229u8,50u8,184u8,9u8,163u8,91u8,103u8,241u8,142u8,78u8,194u8,130u8,163u8,167u8,136u8,68u8,149u8, - 102u8,20u8,167u8,163u8,48u8,16u8,213u8,226u8,35u8,160u8,195u8,161u8,173u8,142u8,12u8,10u8,87u8,8u8,196u8,40u8, - 43u8,203u8,20u8,122u8,45u8,20u8,243u8,138u8,213u8,84u8,94u8,208u8,172u8,87u8,21u8,57u8,90u8,94u8,80u8,227u8, - 224u8,232u8,61u8,201u8,108u8,31u8,234u8,40u8,198u8,213u8,72u8,7u8,227u8,129u8,49u8,38u8,175u8,38u8,56u8,245u8, - 145u8,153u8,181u8,141u8,235u8,165u8,10u8,234u8,33u8,164u8,250u8,123u8,65u8,153u8,210u8,24u8,25u8,181u8,121u8,72u8, - 49u8,17u8,236u8,102u8,253u8,177u8,56u8,183u8,214u8,150u8,122u8,154u8,13u8,109u8,74u8,129u8,83u8,50u8,221u8,186u8, - 216u8,21u8,216u8,109u8,166u8,78u8,189u8,184u8,90u8,47u8,172u8,121u8,7u8,89u8,101u8,237u8,166u8,25u8,88u8,182u8, - 242u8,96u8,225u8,18u8,79u8,101u8,238u8,184u8,207u8,130u8,237u8,148u8,63u8,96u8,47u8,0u8,49u8,169u8,225u8,134u8, - 215u8,126u8,91u8,69u8,141u8,107u8,198u8,12u8,65u8,203u8,31u8,155u8,114u8,158u8,106u8,235u8,120u8,149u8,207u8,53u8, - 26u8,203u8,200u8,112u8,171u8,213u8,200u8,251u8,189u8,235u8,160u8,5u8,188u8,99u8,242u8,238u8,54u8,170u8,198u8,161u8, - 2u8,24u8,116u8,182u8,105u8,76u8,35u8,49u8,15u8,211u8,82u8,14u8,183u8,106u8,218u8,46u8,101u8,66u8,207u8,36u8, - 11u8,43u8,109u8,213u8,10u8,238u8,8u8,18u8,228u8,72u8,188u8,180u8,35u8,233u8,93u8,114u8,221u8,57u8,143u8,114u8, - 196u8,233u8,65u8,76u8,43u8,183u8,232u8,98u8,68u8,40u8,62u8,90u8,20u8,118u8,237u8,184u8,93u8,18u8,61u8,38u8, - 215u8,21u8,68u8,24u8,253u8,179u8,134u8,3u8,187u8,210u8,30u8,16u8,120u8,133u8,243u8,223u8,144u8,73u8,15u8,51u8, - 54u8,78u8,109u8,103u8,77u8,49u8,64u8,96u8,38u8,234u8,223u8,9u8,138u8,109u8,151u8,182u8,253u8,201u8,224u8,128u8, - 168u8,249u8,144u8,8u8,239u8,25u8,230u8,245u8,59u8,223u8,35u8,2u8,254u8,199u8,184u8,30u8,246u8,53u8,59u8,68u8, - 107u8,192u8,184u8,104u8,124u8,34u8,222u8,93u8,180u8,249u8,187u8,152u8,109u8,154u8,208u8,165u8,204u8,167u8,123u8,148u8, - 72u8,124u8,94u8,22u8,168u8,165u8,3u8,247u8,84u8,12u8,5u8,213u8,150u8,54u8,242u8,167u8,76u8,175u8,177u8,92u8, - 158u8,40u8,51u8,236u8,163u8,142u8,141u8,59u8,147u8,42u8,216u8,230u8,63u8,187u8,112u8,247u8,181u8,198u8,45u8,1u8, - 251u8,138u8,178u8,69u8,133u8,151u8,17u8,142u8,4u8,153u8,79u8,7u8,145u8,77u8,60u8,54u8,87u8,80u8,113u8,54u8, - 10u8,193u8,63u8,7u8,14u8,59u8,35u8,231u8,59u8,175u8,207u8,246u8,78u8,223u8,70u8,157u8,37u8,220u8,11u8,180u8, - 237u8,168u8,179u8,174u8,111u8,108u8,133u8,86u8,214u8,93u8,221u8,55u8,131u8,130u8,32u8,167u8,30u8,5u8,75u8,26u8, - 68u8,13u8,124u8,210u8,27u8,186u8,240u8,222u8,55u8,31u8,45u8,149u8,181u8,141u8,138u8,123u8,29u8,45u8,233u8,198u8, - 24u8,219u8,69u8,172u8,138u8,184u8,56u8,20u8,94u8,202u8,20u8,239u8,100u8,52u8,177u8,248u8,21u8,199u8,90u8,85u8, - 22u8,153u8,22u8,122u8,132u8,119u8,73u8,224u8,76u8,136u8,11u8,139u8,218u8,109u8,198u8,45u8,5u8,80u8,220u8,230u8, - 163u8,117u8,167u8,255u8,181u8,2u8,181u8,142u8,231u8,76u8,93u8,0u8,7u8,243u8,3u8,248u8,11u8,225u8,217u8,229u8, - 8u8,252u8,166u8,233u8,225u8,187u8,46u8,3u8,91u8,198u8,13u8,143u8,198u8,75u8,154u8,115u8,82u8,248u8,38u8,131u8, - 242u8,103u8,224u8,30u8,103u8,218u8,17u8,197u8,2u8,207u8,194u8,67u8,160u8,248u8,83u8,183u8,238u8,119u8,37u8,99u8, - 250u8,248u8,30u8,201u8,128u8,17u8,81u8,58u8,211u8,87u8,59u8,172u8,33u8,125u8,169u8,39u8,62u8,61u8,121u8,204u8, - 136u8,135u8,174u8,234u8,196u8,219u8,196u8,211u8,76u8,19u8,26u8,241u8,88u8,207u8,30u8,201u8,147u8,51u8,68u8,240u8, - 114u8,181u8,10u8,51u8,28u8,162u8,181u8,206u8,119u8,203u8,126u8,22u8,140u8,109u8,111u8,97u8,194u8,110u8,224u8,27u8, - 54u8,156u8,167u8,59u8,25u8,49u8,37u8,4u8,135u8,59u8,156u8,191u8,118u8,203u8,228u8,94u8,50u8,57u8,40u8,246u8, - 16u8,200u8,48u8,223u8,181u8,19u8,81u8,179u8,139u8,168u8,234u8,212u8,53u8,190u8,6u8,163u8,117u8,199u8,135u8,111u8, - 26u8,25u8,132u8,125u8,13u8,56u8,229u8,75u8,107u8,221u8,54u8,121u8,118u8,59u8,14u8,77u8,97u8,31u8,129u8,166u8, - 215u8,193u8,238u8,157u8,60u8,88u8,55u8,65u8,141u8,135u8,244u8,244u8,64u8,62u8,194u8,76u8,67u8,212u8,67u8,160u8, - 119u8,82u8,208u8,157u8,52u8,156u8,236u8,150u8,44u8,140u8,103u8,104u8,136u8,12u8,218u8,198u8,78u8,164u8,171u8,241u8, - 182u8,1u8,115u8,55u8,211u8,203u8,21u8,198u8,147u8,71u8,132u8,39u8,204u8,241u8,45u8,57u8,55u8,17u8,169u8,21u8, - 115u8,47u8,149u8,21u8,21u8,146u8,42u8,205u8,216u8,92u8,45u8,42u8,61u8,86u8,76u8,9u8,84u8,219u8,176u8,142u8, - 231u8,48u8,154u8,231u8,179u8,124u8,226u8,6u8,191u8,86u8,209u8,165u8,167u8,16u8,166u8,122u8,209u8,244u8,164u8,166u8, - 242u8,109u8,42u8,196u8,61u8,170u8,94u8,125u8,232u8,217u8,173u8,95u8,189u8,5u8,4u8,27u8,5u8,221u8,15u8,60u8, - 230u8,235u8,92u8,129u8,202u8,6u8,1u8,228u8,125u8,167u8,186u8,161u8,195u8,236u8,35u8,231u8,148u8,204u8,20u8,11u8, - 170u8,122u8,163u8,82u8,193u8,2u8,57u8,238u8,150u8,13u8,125u8,1u8,141u8,155u8,9u8,201u8,211u8,55u8,204u8,179u8, - 157u8,32u8,213u8,78u8,196u8,104u8,182u8,52u8,60u8,252u8,189u8,154u8,210u8,157u8,12u8,58u8,5u8,223u8,129u8,93u8, - 126u8,12u8,253u8,199u8,74u8,39u8,59u8,196u8,218u8,87u8,6u8,116u8,181u8,54u8,108u8,177u8,167u8,204u8,214u8,155u8, - 182u8,159u8,58u8,212u8,116u8,76u8,223u8,120u8,204u8,207u8,22u8,96u8,198u8,219u8,34u8,76u8,67u8,79u8,43u8,184u8, - 216u8,116u8,61u8,69u8,92u8,193u8,122u8,166u8,108u8,134u8,166u8,205u8,77u8,163u8,153u8,213u8,151u8,238u8,168u8,111u8, - 20u8,177u8,138u8,192u8,134u8,190u8,61u8,188u8,141u8,57u8,166u8,211u8,239u8,99u8,85u8,7u8,35u8,62u8,199u8,109u8, - 12u8,237u8,174u8,231u8,245u8,214u8,192u8,158u8,211u8,231u8,192u8,45u8,46u8,185u8,252u8,109u8,61u8,237u8,43u8,118u8, - 91u8,203u8,90u8,197u8,107u8,243u8,229u8,123u8,224u8,192u8,226u8,231u8,168u8,109u8,255u8,177u8,4u8,67u8,204u8,55u8, - 61u8,69u8,48u8,115u8,215u8,152u8,254u8,84u8,95u8,91u8,238u8,218u8,126u8,95u8,238u8,55u8,133u8,208u8,142u8,180u8, - 15u8,83u8,226u8,130u8,107u8,45u8,24u8,136u8,177u8,93u8,153u8,205u8,79u8,144u8,255u8,173u8,115u8,82u8,215u8,246u8, - 13u8,146u8,241u8,86u8,103u8,236u8,212u8,129u8,54u8,113u8,251u8,58u8,163u8,41u8,168u8,76u8,226u8,9u8,241u8,32u8, - 198u8,154u8,107u8,211u8,105u8,191u8,228u8,139u8,203u8,70u8,177u8,35u8,235u8,140u8,223u8,156u8,47u8,67u8,70u8,173u8, - 138u8,100u8,177u8,144u8,5u8,159u8,237u8,57u8,7u8,253u8,29u8,127u8,80u8,144u8,59u8,198u8,196u8,220u8,88u8,59u8, - 247u8,119u8,146u8,157u8,233u8,197u8,227u8,39u8,92u8,111u8,251u8,45u8,27u8,135u8,18u8,56u8,168u8,59u8,234u8,28u8, - 5u8,101u8,62u8,177u8,116u8,52u8,166u8,238u8,126u8,254u8,226u8,121u8,196u8,94u8,245u8,168u8,183u8,122u8,238u8,158u8, - 168u8,209u8,141u8,78u8,60u8,185u8,254u8,156u8,78u8,233u8,12u8,251u8,234u8,252u8,136u8,139u8,25u8,117u8,137u8,94u8, - 21u8,43u8,116u8,52u8,166u8,62u8,52u8,199u8,120u8,124u8,145u8,159u8,29u8,15u8,189u8,152u8,47u8,23u8,216u8,203u8, - 205u8,50u8,251u8,192u8,176u8,191u8,181u8,72u8,137u8,86u8,95u8,135u8,97u8,11u8,123u8,226u8,225u8,105u8,15u8,249u8, - 223u8,171u8,153u8,9u8,253u8,63u8,224u8,188u8,98u8,73u8,94u8,152u8,83u8,162u8,166u8,37u8,177u8,148u8,208u8,69u8, - 233u8,198u8,100u8,183u8,155u8,241u8,68u8,99u8,43u8,140u8,118u8,174u8,19u8,104u8,93u8,114u8,19u8,25u8,211u8,21u8, - 78u8,190u8,158u8,207u8,177u8,160u8,166u8,123u8,56u8,30u8,117u8,118u8,164u8,105u8,164u8,135u8,49u8,169u8,17u8,161u8, - 231u8,80u8,217u8,58u8,120u8,252u8,121u8,45u8,88u8,183u8,247u8,174u8,84u8,148u8,89u8,179u8,94u8,142u8,73u8,118u8, - 45u8,73u8,81u8,110u8,233u8,29u8,1u8,180u8,175u8,73u8,15u8,189u8,19u8,21u8,206u8,77u8,248u8,46u8,135u8,74u8, - 68u8,173u8,33u8,84u8,83u8,144u8,142u8,123u8,19u8,159u8,221u8,188u8,170u8,211u8,223u8,238u8,80u8,74u8,215u8,147u8, - 126u8,19u8,104u8,142u8,127u8,109u8,182u8,61u8,19u8,166u8,124u8,159u8,41u8,145u8,43u8,58u8,216u8,228u8,137u8,112u8, - 85u8,177u8,49u8,105u8,193u8,92u8,223u8,177u8,251u8,166u8,38u8,27u8,36u8,243u8,158u8,210u8,128u8,238u8,0u8,209u8, - 48u8,176u8,118u8,75u8,34u8,169u8,193u8,55u8,214u8,134u8,109u8,204u8,160u8,125u8,22u8,127u8,208u8,158u8,237u8,245u8, - 142u8,89u8,59u8,135u8,224u8,187u8,44u8,178u8,9u8,18u8,225u8,253u8,64u8,73u8,179u8,105u8,171u8,242u8,97u8,159u8, - 143u8,89u8,87u8,12u8,91u8,183u8,17u8,7u8,116u8,93u8,52u8,77u8,237u8,34u8,219u8,204u8,119u8,134u8,189u8,247u8, - 212u8,13u8,6u8,12u8,171u8,77u8,185u8,202u8,174u8,252u8,89u8,149u8,127u8,230u8,222u8,200u8,244u8,120u8,170u8,169u8, - 127u8,247u8,22u8,159u8,193u8,243u8,179u8,8u8,80u8,157u8,99u8,122u8,197u8,120u8,90u8,85u8,114u8,181u8,174u8,74u8, - 219u8,128u8,116u8,108u8,82u8,200u8,241u8,18u8,39u8,212u8,19u8,99u8,75u8,226u8,205u8,85u8,5u8,125u8,239u8,104u8, - 110u8,0u8,238u8,97u8,120u8,226u8,60u8,175u8,244u8,5u8,54u8,251u8,62u8,99u8,147u8,108u8,232u8,90u8,34u8,94u8, - 124u8,111u8,206u8,86u8,237u8,27u8,39u8,38u8,188u8,210u8,141u8,75u8,182u8,17u8,235u8,106u8,84u8,63u8,13u8,252u8, - 246u8,7u8,42u8,95u8,134u8,48u8,59u8,43u8,43u8,245u8,194u8,160u8,147u8,10u8,151u8,9u8,100u8,175u8,34u8,90u8, - 110u8,250u8,67u8,49u8,138u8,248u8,191u8,232u8,27u8,38u8,223u8,109u8,63u8,79u8,165u8,37u8,206u8,193u8,80u8,145u8, - 204u8,55u8,70u8,16u8,224u8,15u8,16u8,107u8,203u8,8u8,234u8,210u8,16u8,194u8,166u8,3u8,80u8,155u8,74u8,207u8, - 9u8,5u8,183u8,59u8,219u8,19u8,69u8,103u8,220u8,0u8,113u8,104u8,96u8,1u8,104u8,82u8,3u8,24u8,98u8,43u8, - 226u8,170u8,99u8,243u8,38u8,165u8,90u8,100u8,236u8,31u8,240u8,21u8,150u8,39u8,137u8,250u8,108u8,85u8,91u8,99u8, - 190u8,197u8,212u8,199u8,5u8,126u8,71u8,58u8,190u8,232u8,223u8,20u8,20u8,109u8,207u8,107u8,94u8,47u8,49u8,239u8, - 220u8,93u8,74u8,41u8,174u8,181u8,16u8,174u8,41u8,140u8,173u8,80u8,125u8,73u8,134u8,23u8,192u8,66u8,245u8,170u8, - 92u8,159u8,97u8,55u8,61u8,248u8,140u8,98u8,216u8,72u8,124u8,104u8,44u8,155u8,181u8,98u8,89u8,43u8,196u8,237u8, - 27u8,205u8,186u8,169u8,64u8,69u8,179u8,161u8,247u8,14u8,39u8,191u8,27u8,99u8,74u8,93u8,95u8,229u8,133u8,97u8, - 0u8,181u8,95u8,211u8,156u8,14u8,193u8,155u8,55u8,81u8,184u8,212u8,197u8,11u8,110u8,75u8,14u8,17u8,6u8,62u8, - 11u8,92u8,191u8,107u8,68u8,197u8,240u8,125u8,110u8,223u8,197u8,238u8,188u8,108u8,214u8,68u8,58u8,117u8,143u8,43u8, - 77u8,192u8,134u8,249u8,236u8,1u8,72u8,91u8,195u8,192u8,7u8,34u8,221u8,243u8,22u8,10u8,124u8,101u8,139u8,223u8, - 5u8,138u8,54u8,17u8,106u8,8u8,150u8,130u8,207u8,202u8,44u8,74u8,36u8,39u8,29u8,212u8,217u8,30u8,222u8,109u8, - 29u8,61u8,91u8,146u8,120u8,84u8,145u8,200u8,87u8,251u8,172u8,192u8,99u8,65u8,218u8,81u8,17u8,122u8,206u8,75u8, - 91u8,224u8,134u8,222u8,218u8,112u8,231u8,61u8,51u8,199u8,151u8,246u8,168u8,105u8,183u8,146u8,176u8,95u8,89u8,235u8, - 207u8,41u8,251u8,151u8,182u8,221u8,87u8,12u8,183u8,112u8,209u8,21u8,186u8,93u8,33u8,182u8,23u8,211u8,139u8,110u8, - 199u8,226u8,176u8,97u8,68u8,189u8,84u8,174u8,108u8,64u8,124u8,122u8,220u8,134u8,215u8,14u8,190u8,54u8,20u8,245u8, - 255u8,239u8,245u8,155u8,155u8,109u8,209u8,168u8,175u8,95u8,244u8,188u8,64u8,102u8,14u8,248u8,242u8,186u8,130u8,132u8, - 4u8,105u8,50u8,91u8,200u8,65u8,251u8,125u8,66u8,231u8,24u8,156u8,39u8,31u8,167u8,92u8,10u8,208u8,165u8,111u8, - 117u8,93u8,121u8,158u8,20u8,165u8,81u8,168u8,126u8,255u8,20u8,223u8,97u8,206u8,249u8,122u8,57u8,216u8,106u8,154u8, - 220u8,74u8,112u8,226u8,40u8,44u8,165u8,106u8,87u8,173u8,174u8,214u8,130u8,222u8,186u8,21u8,202u8,87u8,42u8,173u8, - 247u8,148u8,47u8,115u8,113u8,175u8,223u8,44u8,6u8,223u8,36u8,216u8,165u8,126u8,137u8,157u8,120u8,26u8,123u8,197u8, - 209u8,9u8,251u8,143u8,54u8,249u8,15u8,110u8,137u8,90u8,153u8,228u8,73u8,28u8,96u8,43u8,107u8,143u8,112u8,132u8, - 143u8,114u8,134u8,182u8,67u8,208u8,52u8,111u8,135u8,67u8,88u8,117u8,67u8,187u8,192u8,165u8,95u8,137u8,144u8,119u8, - 7u8,241u8,125u8,23u8,151u8,237u8,233u8,54u8,62u8,237u8,92u8,29u8,244u8,221u8,28u8,220u8,30u8,71u8,247u8,13u8, - 142u8,118u8,181u8,254u8,20u8,145u8,81u8,255u8,216u8,111u8,10u8,14u8,70u8,37u8,76u8,255u8,132u8,84u8,189u8,126u8, - 144u8,168u8,6u8,139u8,95u8,93u8,137u8,237u8,148u8,186u8,91u8,154u8,9u8,212u8,168u8,219u8,5u8,58u8,106u8,93u8, - 249u8,214u8,167u8,119u8,219u8,165u8,171u8,6u8,64u8,92u8,124u8,98u8,117u8,249u8,193u8,108u8,224u8,21u8,112u8,252u8, - 141u8,20u8,5u8,191u8,218u8,165u8,127u8,106u8,55u8,152u8,102u8,99u8,207u8,237u8,0u8,124u8,9u8,236u8,169u8,185u8, - 220u8,150u8,22u8,154u8,155u8,145u8,222u8,18u8,188u8,179u8,141u8,69u8,228u8,175u8,193u8,73u8,68u8,222u8,66u8,252u8, - 87u8,145u8,227u8,255u8,63u8,210u8,171u8,13u8,27u8,77u8,224u8,211u8,61u8,212u8,52u8,244u8,180u8,67u8,108u8,18u8, - 52u8,117u8,119u8,219u8,153u8,86u8,128u8,126u8,146u8,212u8,243u8,200u8,40u8,175u8,218u8,132u8,90u8,186u8,175u8,5u8, - 86u8,80u8,158u8,6u8,56u8,177u8,248u8,201u8,253u8,5u8,29u8,249u8,154u8,106u8,223u8,201u8,187u8,75u8,28u8,230u8, - 137u8,25u8,125u8,124u8,127u8,212u8,179u8,141u8,127u8,79u8,193u8,247u8,211u8,139u8,89u8,207u8,239u8,40u8,56u8,124u8, - 248u8,242u8,144u8,109u8,162u8,111u8,247u8,15u8,211u8,211u8,191u8,205u8,206u8,183u8,236u8,255u8,178u8,15u8,61u8,191u8, - 161u8,242u8,189u8,44u8,242u8,41u8,68u8,133u8,245u8,174u8,87u8,84u8,238u8,101u8,184u8,134u8,12u8,139u8,183u8,75u8, - 21u8,99u8,44u8,245u8,23u8,63u8,208u8,23u8,39u8,237u8,119u8,39u8,91u8,232u8,158u8,240u8,245u8,96u8,139u8,114u8, - 207u8,235u8,187u8,120u8,22u8,177u8,222u8,246u8,198u8,167u8,203u8,177u8,213u8,66u8,42u8,14u8,127u8,41u8,236u8,41u8, - 188u8,13u8,131u8,189u8,168u8,187u8,87u8,62u8,160u8,85u8,207u8,125u8,183u8,157u8,7u8,238u8,205u8,149u8,145u8,78u8, - 24u8,40u8,145u8,147u8,125u8,58u8,53u8,4u8,220u8,190u8,230u8,211u8,220u8,41u8,245u8,221u8,239u8,31u8,137u8,155u8, - 207u8,112u8,215u8,103u8,173u8,248u8,72u8,144u8,204u8,165u8,212u8,246u8,13u8,172u8,3u8,31u8,34u8,223u8,45u8,85u8, - 58u8,179u8,181u8,20u8,222u8,186u8,154u8,125u8,96u8,161u8,177u8,182u8,251u8,175u8,150u8,90u8,107u8,221u8,116u8,66u8, - 134u8,214u8,13u8,92u8,218u8,92u8,180u8,215u8,103u8,112u8,60u8,54u8,240u8,149u8,222u8,62u8,131u8,236u8,220u8,251u8, - 240u8,178u8,179u8,245u8,254u8,103u8,71u8,158u8,77u8,178u8,246u8,94u8,199u8,34u8,45u8,239u8,20u8,237u8,192u8,247u8, - 253u8,136u8,224u8,15u8,183u8,122u8,64u8,215u8,12u8,149u8,128u8,30u8,99u8,136u8,236u8,107u8,195u8,150u8,217u8,113u8, - 232u8,121u8,180u8,225u8,169u8,125u8,109u8,211u8,99u8,104u8,253u8,198u8,231u8,71u8,231u8,177u8,159u8,3u8,7u8,212u8, - 72u8,187u8,236u8,59u8,231u8,20u8,176u8,65u8,185u8,67u8,63u8,204u8,252u8,30u8,248u8,7u8,254u8,21u8,35u8,198u8, - 179u8,183u8,158u8,72u8,213u8,100u8,111u8,187u8,71u8,111u8,184u8,118u8,226u8,4u8,12u8,79u8,224u8,116u8,180u8,216u8, - 25u8,194u8,225u8,174u8,209u8,142u8,131u8,5u8,51u8,28u8,212u8,67u8,57u8,5u8,155u8,13u8,208u8,19u8,67u8,96u8, - 181u8,119u8,106u8,172u8,172u8,189u8,93u8,188u8,18u8,20u8,123u8,56u8,202u8,190u8,59u8,79u8,224u8,36u8,138u8,198u8, - 188u8,26u8,232u8,88u8,35u8,102u8,153u8,62u8,238u8,116u8,208u8,64u8,177u8,189u8,185u8,27u8,85u8,172u8,196u8,251u8, - 206u8,78u8,176u8,154u8,44u8,124u8,153u8,158u8,149u8,61u8,244u8,140u8,119u8,247u8,85u8,113u8,157u8,253u8,162u8,148u8, - 220u8,86u8,109u8,239u8,49u8,131u8,71u8,223u8,79u8,160u8,107u8,249u8,0u8,132u8,128u8,129u8,25u8,69u8,141u8,63u8, - 88u8,197u8,31u8,169u8,94u8,149u8,49u8,118u8,41u8,86u8,71u8,52u8,96u8,253u8,79u8,80u8,209u8,125u8,245u8,53u8, - 215u8,9u8,52u8,84u8,39u8,197u8,247u8,188u8,222u8,180u8,151u8,82u8,80u8,176u8,131u8,128u8,253u8,171u8,9u8,169u8, - 118u8,9u8,209u8,82u8,3u8,174u8,214u8,54u8,121u8,236u8,143u8,245u8,67u8,79u8,75u8,135u8,29u8,235u8,64u8,171u8, - 156u8,25u8,25u8,137u8,67u8,11u8,184u8,21u8,142u8,76u8,16u8,95u8,18u8,229u8,29u8,5u8,109u8,7u8,250u8,101u8, - 27u8,168u8,118u8,128u8,109u8,96u8,31u8,39u8,243u8,214u8,181u8,147u8,109u8,194u8,239u8,23u8,123u8,59u8,213u8,62u8, - 70u8,11u8,246u8,133u8,23u8,59u8,137u8,57u8,23u8,97u8,58u8,201u8,190u8,5u8,196u8,115u8,121u8,198u8,134u8,229u8, - 187u8,91u8,115u8,96u8,127u8,26u8,30u8,117u8,35u8,184u8,185u8,66u8,226u8,217u8,77u8,58u8,122u8,246u8,205u8,183u8, - 126u8,77u8,130u8,24u8,153u8,255u8,99u8,94u8,231u8,218u8,135u8,163u8,116u8,165u8,65u8,6u8,215u8,168u8,124u8,47u8, - 45u8,254u8,230u8,71u8,249u8,0u8,103u8,242u8,56u8,92u8,156u8,135u8,73u8,90u8,23u8,114u8,16u8,66u8,84u8,193u8, - 67u8,178u8,88u8,82u8,223u8,244u8,205u8,225u8,225u8,225u8,179u8,17u8,254u8,230u8,2u8,117u8,176u8,115u8,44u8,176u8, - 165u8,107u8,27u8,193u8,77u8,24u8,255u8,122u8,13u8,33u8,88u8,228u8,121u8,252u8,161u8,214u8,160u8,15u8,203u8,232u8, - 23u8,198u8,1u8,218u8,42u8,84u8,7u8,224u8,246u8,27u8,214u8,214u8,111u8,59u8,217u8,73u8,11u8,72u8,250u8,103u8, - 53u8,76u8,98u8,246u8,227u8,172u8,115u8,63u8,60u8,192u8,8u8,161u8,249u8,195u8,243u8,63u8,26u8,139u8,125u8,255u8, - 201u8,127u8,0u8,151u8,242u8,7u8,240u8,178u8,88u8,0u8,0u8,0u8,0u8,16u8,114u8,101u8,115u8,111u8,117u8,114u8, - 99u8,101u8,95u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,171u8,21u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8, - 2u8,255u8,237u8,90u8,109u8,79u8,220u8,72u8,18u8,254u8,190u8,191u8,162u8,55u8,145u8,102u8,61u8,146u8,119u8,2u8, - 36u8,89u8,157u8,12u8,65u8,199u8,1u8,82u8,208u8,37u8,33u8,10u8,228u8,246u8,118u8,87u8,39u8,143u8,177u8,123u8, - 152u8,22u8,30u8,183u8,207u8,47u8,192u8,28u8,202u8,127u8,191u8,170u8,234u8,118u8,119u8,251u8,109u8,2u8,155u8,77u8, - 196u8,233u8,118u8,62u8,160u8,193u8,93u8,174u8,170u8,174u8,151u8,167u8,170u8,171u8,231u8,217u8,179u8,103u8,236u8,128u8, - 21u8,188u8,148u8,117u8,17u8,115u8,22u8,197u8,177u8,172u8,179u8,138u8,137u8,146u8,213u8,37u8,79u8,88u8,37u8,217u8, - 42u8,202u8,162u8,75u8,110u8,72u8,74u8,38u8,178u8,132u8,231u8,28u8,254u8,0u8,157u8,92u8,176u8,40u8,51u8,111u8, - 41u8,210u8,132u8,93u8,172u8,89u8,132u8,239u8,23u8,179u8,239u8,158u8,1u8,255u8,243u8,37u8,176u8,139u8,101u8,86u8, - 69u8,34u8,43u8,89u8,201u8,175u8,121u8,17u8,165u8,172u8,174u8,68u8,42u8,42u8,1u8,12u8,73u8,200u8,21u8,135u8, - 55u8,68u8,118u8,217u8,211u8,165u8,100u8,43u8,89u8,112u8,198u8,23u8,11u8,30u8,87u8,226u8,154u8,19u8,79u8,226u8, - 251u8,244u8,41u8,251u8,208u8,16u8,31u8,52u8,196u8,86u8,227u8,84u8,252u8,187u8,22u8,137u8,168u8,214u8,44u8,151u8, - 50u8,45u8,205u8,107u8,7u8,44u8,225u8,215u8,236u8,70u8,148u8,75u8,20u8,7u8,244u8,160u8,233u8,128u8,212u8,133u8, - 44u8,96u8,23u8,109u8,38u8,62u8,187u8,145u8,117u8,154u8,192u8,211u8,43u8,158u8,174u8,89u8,34u8,89u8,181u8,228u8, - 64u8,153u8,166u8,242u8,6u8,152u8,5u8,196u8,127u8,123u8,198u8,14u8,11u8,30u8,85u8,192u8,139u8,101u8,252u8,198u8, - 88u8,71u8,109u8,111u8,222u8,72u8,10u8,245u8,243u8,32u8,136u8,137u8,58u8,236u8,46u8,204u8,103u8,218u8,114u8,180u8, - 92u8,162u8,40u8,226u8,175u8,151u8,125u8,86u8,86u8,96u8,24u8,122u8,206u8,230u8,165u8,184u8,204u8,120u8,17u8,198u8, - 81u8,62u8,135u8,173u8,85u8,176u8,53u8,144u8,62u8,32u8,235u8,80u8,185u8,129u8,23u8,115u8,31u8,60u8,151u8,176u8, - 66u8,86u8,13u8,115u8,118u8,197u8,215u8,96u8,14u8,146u8,129u8,255u8,198u8,117u8,81u8,160u8,143u8,141u8,69u8,162u8, - 26u8,30u8,103u8,149u8,136u8,163u8,74u8,200u8,140u8,200u8,201u8,70u8,121u8,33u8,175u8,69u8,2u8,126u8,239u8,19u8, - 168u8,0u8,216u8,153u8,177u8,35u8,190u8,0u8,169u8,196u8,247u8,77u8,99u8,210u8,247u8,96u8,81u8,112u8,110u8,82u8, - 167u8,252u8,7u8,224u8,158u8,36u8,160u8,46u8,57u8,240u8,66u8,17u8,150u8,209u8,10u8,108u8,168u8,116u8,235u8,250u8, - 71u8,49u8,126u8,14u8,166u8,150u8,89u8,89u8,21u8,117u8,12u8,122u8,178u8,170u8,136u8,178u8,50u8,138u8,73u8,120u8, - 30u8,197u8,87u8,24u8,5u8,121u8,125u8,145u8,54u8,174u8,118u8,150u8,209u8,187u8,67u8,124u8,181u8,151u8,140u8,173u8, - 251u8,91u8,166u8,188u8,0u8,243u8,150u8,21u8,207u8,193u8,215u8,68u8,246u8,98u8,198u8,78u8,178u8,77u8,155u8,155u8, - 139u8,76u8,84u8,161u8,250u8,119u8,206u8,22u8,117u8,70u8,90u8,248u8,44u8,142u8,210u8,20u8,221u8,84u8,21u8,2u8, - 210u8,162u8,23u8,2u8,228u8,79u8,146u8,112u8,179u8,20u8,241u8,18u8,60u8,11u8,228u8,68u8,125u8,205u8,251u8,126u8, - 183u8,254u8,28u8,220u8,218u8,15u8,131u8,30u8,212u8,14u8,159u8,111u8,221u8,110u8,65u8,72u8,152u8,44u8,131u8,232u8, - 78u8,101u8,124u8,133u8,182u8,16u8,152u8,230u8,11u8,181u8,209u8,151u8,51u8,246u8,51u8,112u8,64u8,103u8,225u8,146u8, - 10u8,240u8,88u8,10u8,216u8,10u8,138u8,108u8,103u8,139u8,82u8,55u8,149u8,81u8,162u8,226u8,41u8,202u8,163u8,11u8, - 204u8,249u8,53u8,105u8,10u8,108u8,98u8,237u8,103u8,218u8,67u8,163u8,73u8,193u8,47u8,5u8,216u8,182u8,32u8,34u8, - 10u8,113u8,18u8,98u8,12u8,123u8,8u8,210u8,44u8,26u8,89u8,52u8,56u8,148u8,9u8,176u8,202u8,68u8,158u8,115u8, - 5u8,3u8,75u8,158u8,230u8,42u8,35u8,231u8,115u8,101u8,68u8,176u8,59u8,115u8,28u8,225u8,53u8,92u8,2u8,54u8, - 81u8,58u8,76u8,217u8,29u8,17u8,50u8,150u8,242u8,10u8,97u8,34u8,108u8,162u8,242u8,21u8,251u8,235u8,209u8,241u8, - 63u8,194u8,131u8,163u8,163u8,15u8,187u8,14u8,137u8,53u8,63u8,80u8,108u8,116u8,163u8,17u8,230u8,187u8,124u8,167u8, - 46u8,179u8,20u8,153u8,180u8,2u8,232u8,36u8,91u8,72u8,118u8,231u8,72u8,9u8,156u8,239u8,62u8,155u8,205u8,102u8, - 236u8,83u8,195u8,97u8,37u8,65u8,114u8,37u8,29u8,57u8,105u8,174u8,217u8,127u8,114u8,173u8,64u8,223u8,223u8,68u8, - 104u8,97u8,8u8,129u8,164u8,46u8,148u8,35u8,99u8,101u8,86u8,180u8,125u8,65u8,209u8,209u8,54u8,29u8,165u8,81u8, - 76u8,22u8,4u8,213u8,67u8,164u8,222u8,251u8,167u8,207u8,126u8,217u8,247u8,192u8,200u8,108u8,242u8,230u8,189u8,207u8, - 110u8,3u8,134u8,190u8,217u8,187u8,221u8,247u8,217u8,90u8,127u8,95u8,239u8,91u8,147u8,50u8,38u8,22u8,222u8,247u8, - 252u8,22u8,36u8,148u8,123u8,45u8,119u8,106u8,78u8,111u8,222u8,7u8,193u8,129u8,178u8,11u8,112u8,157u8,250u8,108u8, - 128u8,104u8,234u8,242u8,99u8,218u8,112u8,43u8,129u8,112u8,120u8,81u8,23u8,25u8,24u8,16u8,105u8,131u8,0u8,221u8, - 44u8,162u8,84u8,252u8,135u8,15u8,137u8,218u8,247u8,192u8,116u8,198u8,246u8,234u8,211u8,216u8,111u8,162u8,225u8,88u8, - 27u8,26u8,193u8,52u8,180u8,161u8,235u8,77u8,210u8,124u8,6u8,255u8,14u8,107u8,119u8,231u8,170u8,242u8,201u8,17u8, - 240u8,201u8,124u8,3u8,193u8,125u8,159u8,180u8,74u8,89u8,212u8,47u8,101u8,78u8,141u8,69u8,224u8,82u8,225u8,235u8, - 66u8,155u8,39u8,102u8,124u8,230u8,83u8,145u8,45u8,162u8,216u8,96u8,246u8,116u8,160u8,232u8,169u8,130u8,187u8,140u8, - 174u8,91u8,108u8,1u8,190u8,17u8,21u8,156u8,146u8,223u8,132u8,133u8,226u8,136u8,181u8,136u8,91u8,254u8,162u8,42u8, - 121u8,186u8,128u8,2u8,8u8,220u8,50u8,89u8,233u8,196u8,5u8,123u8,20u8,38u8,157u8,115u8,89u8,86u8,204u8,184u8, - 129u8,66u8,74u8,49u8,65u8,69u8,98u8,42u8,163u8,95u8,183u8,126u8,134u8,0u8,32u8,161u8,182u8,81u8,168u8,203u8, - 193u8,220u8,237u8,72u8,108u8,93u8,53u8,50u8,16u8,115u8,244u8,43u8,122u8,165u8,169u8,35u8,170u8,96u8,68u8,85u8, - 187u8,0u8,65u8,101u8,59u8,192u8,202u8,147u8,82u8,62u8,229u8,16u8,8u8,184u8,103u8,86u8,137u8,21u8,247u8,91u8, - 140u8,73u8,24u8,176u8,136u8,193u8,230u8,24u8,105u8,46u8,240u8,57u8,192u8,88u8,41u8,139u8,40u8,7u8,91u8,108u8, - 107u8,194u8,68u8,87u8,186u8,183u8,235u8,183u8,68u8,96u8,66u8,102u8,9u8,85u8,18u8,193u8,252u8,174u8,23u8,102u8, - 248u8,49u8,134u8,113u8,113u8,228u8,52u8,71u8,143u8,236u8,157u8,209u8,163u8,67u8,163u8,193u8,190u8,111u8,163u8,179u8, - 155u8,248u8,186u8,206u8,59u8,108u8,154u8,164u8,24u8,148u8,208u8,101u8,237u8,100u8,46u8,38u8,173u8,241u8,18u8,100u8, - 60u8,164u8,173u8,241u8,230u8,37u8,175u8,250u8,18u8,26u8,188u8,28u8,146u8,100u8,211u8,12u8,217u8,218u8,40u8,80u8, - 124u8,171u8,117u8,206u8,67u8,1u8,56u8,26u8,4u8,174u8,68u8,228u8,53u8,113u8,214u8,232u8,171u8,92u8,236u8,117u8, - 77u8,187u8,239u8,77u8,45u8,255u8,168u8,132u8,158u8,182u8,250u8,222u8,107u8,171u8,254u8,170u8,45u8,211u8,103u8,199u8, - 88u8,37u8,142u8,207u8,206u8,194u8,183u8,39u8,103u8,111u8,15u8,206u8,15u8,95u8,183u8,53u8,212u8,217u8,251u8,138u8, - 93u8,200u8,162u8,144u8,55u8,225u8,101u8,42u8,47u8,162u8,52u8,92u8,213u8,213u8,128u8,116u8,87u8,148u8,101u8,163u8, - 227u8,99u8,192u8,24u8,192u8,86u8,146u8,103u8,131u8,160u8,148u8,43u8,190u8,193u8,94u8,22u8,128u8,180u8,66u8,81u8, - 94u8,201u8,50u8,92u8,20u8,208u8,117u8,221u8,200u8,226u8,42u8,8u8,186u8,9u8,5u8,254u8,67u8,225u8,216u8,46u8, - 151u8,85u8,18u8,4u8,28u8,212u8,47u8,118u8,219u8,207u8,148u8,152u8,206u8,195u8,107u8,232u8,41u8,92u8,202u8,158u8, - 36u8,45u8,96u8,19u8,5u8,61u8,136u8,9u8,219u8,15u8,240u8,59u8,130u8,238u8,6u8,250u8,120u8,96u8,89u8,43u8, - 184u8,202u8,83u8,30u8,174u8,32u8,66u8,131u8,187u8,51u8,0u8,49u8,31u8,2u8,21u8,31u8,189u8,141u8,114u8,168u8, - 165u8,244u8,138u8,106u8,39u8,116u8,163u8,108u8,251u8,40u8,0u8,58u8,128u8,129u8,58u8,163u8,246u8,79u8,171u8,76u8, - 244u8,49u8,246u8,160u8,236u8,248u8,240u8,244u8,221u8,249u8,193u8,201u8,187u8,227u8,15u8,225u8,187u8,211u8,243u8,240u8, - 253u8,199u8,191u8,189u8,57u8,57u8,123u8,125u8,124u8,20u8,176u8,250u8,167u8,23u8,224u8,151u8,237u8,93u8,195u8,252u8, - 124u8,168u8,243u8,188u8,137u8,8u8,75u8,53u8,46u8,209u8,41u8,138u8,64u8,34u8,231u8,177u8,88u8,8u8,120u8,208u8, - 166u8,119u8,37u8,127u8,124u8,119u8,240u8,241u8,252u8,245u8,233u8,135u8,147u8,95u8,143u8,143u8,72u8,248u8,233u8,207u8, - 160u8,70u8,35u8,120u8,71u8,111u8,75u8,17u8,255u8,122u8,252u8,225u8,52u8,68u8,242u8,240u8,239u8,199u8,191u8,4u8, - 76u8,249u8,102u8,175u8,254u8,203u8,62u8,80u8,222u8,62u8,217u8,250u8,194u8,207u8,19u8,45u8,75u8,35u8,149u8,181u8, - 162u8,133u8,168u8,166u8,224u8,82u8,111u8,23u8,88u8,235u8,239u8,233u8,188u8,244u8,45u8,22u8,12u8,160u8,147u8,170u8, - 166u8,214u8,77u8,26u8,196u8,85u8,169u8,232u8,217u8,180u8,123u8,196u8,25u8,108u8,128u8,25u8,135u8,26u8,207u8,11u8, - 195u8,19u8,9u8,85u8,18u8,193u8,49u8,21u8,223u8,32u8,58u8,177u8,192u8,78u8,88u8,160u8,143u8,178u8,31u8,249u8, - 42u8,7u8,180u8,246u8,170u8,165u8,172u8,47u8,151u8,134u8,6u8,212u8,128u8,250u8,247u8,124u8,231u8,199u8,139u8,53u8, - 8u8,156u8,26u8,134u8,250u8,172u8,209u8,173u8,239u8,230u8,128u8,165u8,95u8,159u8,209u8,11u8,26u8,112u8,97u8,161u8, - 88u8,19u8,236u8,142u8,148u8,55u8,207u8,24u8,82u8,22u8,2u8,170u8,181u8,105u8,98u8,125u8,107u8,97u8,206u8,19u8, - 215u8,195u8,118u8,165u8,217u8,94u8,136u8,178u8,67u8,144u8,221u8,39u8,155u8,130u8,162u8,84u8,208u8,75u8,199u8,139u8, - 214u8,123u8,136u8,100u8,78u8,175u8,57u8,132u8,51u8,46u8,170u8,143u8,109u8,66u8,233u8,238u8,147u8,170u8,211u8,93u8, - 195u8,93u8,249u8,204u8,86u8,242u8,150u8,219u8,80u8,95u8,42u8,238u8,20u8,65u8,110u8,29u8,50u8,239u8,91u8,179u8, - 248u8,173u8,103u8,70u8,225u8,193u8,167u8,142u8,242u8,109u8,130u8,158u8,185u8,236u8,178u8,86u8,250u8,222u8,33u8,233u8, - 171u8,99u8,232u8,130u8,171u8,152u8,136u8,86u8,20u8,167u8,48u8,71u8,65u8,196u8,130u8,169u8,67u8,33u8,87u8,42u8, - 0u8,73u8,251u8,166u8,33u8,104u8,184u8,24u8,33u8,134u8,219u8,67u8,34u8,252u8,51u8,145u8,109u8,152u8,223u8,59u8, - 194u8,239u8,29u8,217u8,236u8,157u8,172u8,180u8,209u8,85u8,134u8,65u8,30u8,53u8,167u8,96u8,60u8,80u8,208u8,241u8, - 95u8,104u8,197u8,140u8,201u8,228u8,13u8,120u8,3u8,26u8,220u8,188u8,107u8,134u8,86u8,126u8,151u8,75u8,106u8,38u8, - 101u8,6u8,103u8,214u8,11u8,107u8,33u8,58u8,167u8,99u8,215u8,214u8,31u8,237u8,80u8,31u8,151u8,113u8,156u8,88u8, - 196u8,177u8,158u8,57u8,80u8,19u8,111u8,42u8,203u8,254u8,195u8,114u8,145u8,162u8,17u8,214u8,147u8,175u8,157u8,148u8, - 248u8,65u8,57u8,161u8,138u8,27u8,66u8,248u8,71u8,144u8,174u8,170u8,46u8,55u8,39u8,119u8,199u8,140u8,222u8,164u8, - 97u8,209u8,163u8,110u8,210u8,192u8,165u8,54u8,236u8,73u8,43u8,168u8,250u8,170u8,34u8,64u8,99u8,230u8,48u8,242u8, - 93u8,11u8,252u8,159u8,96u8,198u8,240u8,153u8,4u8,204u8,64u8,73u8,13u8,201u8,212u8,36u8,132u8,59u8,227u8,162u8, - 229u8,118u8,206u8,185u8,121u8,147u8,114u8,56u8,3u8,150u8,157u8,131u8,8u8,200u8,142u8,18u8,1u8,153u8,20u8,93u8, - 71u8,34u8,141u8,46u8,82u8,117u8,242u8,201u8,69u8,124u8,85u8,231u8,191u8,35u8,41u8,58u8,231u8,175u8,47u8,202u8, - 143u8,21u8,175u8,162u8,36u8,170u8,162u8,16u8,186u8,112u8,117u8,182u8,31u8,33u8,140u8,97u8,20u8,100u8,86u8,44u8, - 193u8,99u8,40u8,108u8,3u8,141u8,42u8,232u8,26u8,116u8,204u8,20u8,86u8,183u8,153u8,141u8,120u8,127u8,104u8,231u8, - 62u8,237u8,242u8,241u8,134u8,127u8,171u8,205u8,28u8,11u8,125u8,140u8,158u8,47u8,213u8,123u8,52u8,144u8,236u8,116u8, - 111u8,108u8,165u8,117u8,92u8,29u8,109u8,58u8,255u8,232u8,174u8,73u8,41u8,220u8,156u8,79u8,7u8,160u8,78u8,17u8, - 56u8,174u8,133u8,2u8,109u8,6u8,103u8,134u8,107u8,131u8,150u8,234u8,104u8,56u8,117u8,132u8,184u8,211u8,172u8,38u8, - 12u8,29u8,101u8,154u8,198u8,219u8,61u8,9u8,169u8,40u8,246u8,166u8,48u8,180u8,50u8,108u8,154u8,211u8,80u8,163u8, - 119u8,108u8,88u8,12u8,29u8,93u8,71u8,244u8,218u8,109u8,177u8,232u8,30u8,206u8,55u8,226u8,188u8,125u8,213u8,213u8, - 20u8,72u8,189u8,9u8,8u8,180u8,234u8,204u8,104u8,63u8,126u8,247u8,20u8,62u8,124u8,242u8,109u8,169u8,211u8,184u8, - 17u8,52u8,65u8,19u8,43u8,95u8,194u8,236u8,176u8,12u8,169u8,251u8,241u8,38u8,61u8,135u8,247u8,236u8,220u8,26u8, - 91u8,244u8,99u8,183u8,101u8,11u8,107u8,89u8,198u8,83u8,56u8,149u8,222u8,109u8,174u8,23u8,174u8,35u8,122u8,226u8, - 154u8,172u8,233u8,103u8,11u8,140u8,160u8,120u8,1u8,124u8,92u8,252u8,48u8,234u8,247u8,10u8,15u8,77u8,243u8,241u8, - 22u8,194u8,30u8,56u8,251u8,213u8,7u8,186u8,67u8,123u8,243u8,192u8,245u8,240u8,202u8,29u8,231u8,151u8,165u8,140u8, - 5u8,29u8,90u8,113u8,92u8,74u8,253u8,85u8,183u8,83u8,237u8,222u8,78u8,180u8,47u8,37u8,154u8,62u8,21u8,110u8, - 33u8,240u8,46u8,80u8,95u8,193u8,216u8,235u8,200u8,76u8,53u8,106u8,2u8,235u8,17u8,138u8,144u8,117u8,213u8,58u8, - 174u8,117u8,115u8,182u8,85u8,169u8,8u8,101u8,54u8,206u8,230u8,7u8,48u8,163u8,95u8,151u8,108u8,112u8,5u8,205u8, - 101u8,149u8,206u8,249u8,13u8,224u8,177u8,25u8,14u8,154u8,129u8,82u8,63u8,183u8,29u8,105u8,208u8,240u8,208u8,164u8, - 37u8,8u8,96u8,58u8,16u8,210u8,0u8,194u8,27u8,155u8,53u8,76u8,187u8,33u8,126u8,143u8,140u8,27u8,72u8,184u8, - 86u8,13u8,116u8,33u8,158u8,81u8,98u8,132u8,38u8,247u8,176u8,20u8,182u8,195u8,248u8,65u8,72u8,225u8,238u8,114u8, - 183u8,157u8,87u8,218u8,50u8,45u8,144u8,210u8,87u8,200u8,148u8,88u8,147u8,94u8,254u8,79u8,90u8,123u8,181u8,86u8, - 19u8,217u8,53u8,148u8,76u8,104u8,24u8,139u8,203u8,122u8,5u8,201u8,226u8,141u8,140u8,75u8,166u8,29u8,21u8,200u8, - 8u8,97u8,7u8,85u8,218u8,93u8,128u8,171u8,93u8,193u8,17u8,115u8,71u8,176u8,169u8,163u8,91u8,91u8,144u8,231u8, - 26u8,216u8,101u8,153u8,242u8,236u8,178u8,90u8,246u8,182u8,58u8,197u8,153u8,227u8,214u8,48u8,80u8,35u8,142u8,245u8, - 124u8,180u8,217u8,67u8,84u8,42u8,240u8,220u8,185u8,193u8,29u8,248u8,78u8,175u8,132u8,0u8,142u8,189u8,178u8,156u8, - 218u8,47u8,184u8,219u8,72u8,56u8,76u8,131u8,228u8,90u8,131u8,170u8,218u8,194u8,238u8,160u8,246u8,15u8,129u8,55u8, - 139u8,110u8,173u8,86u8,195u8,237u8,136u8,250u8,1u8,236u8,194u8,223u8,211u8,223u8,160u8,227u8,174u8,60u8,252u8,157u8, - 2u8,94u8,239u8,109u8,221u8,110u8,195u8,103u8,250u8,175u8,225u8,30u8,23u8,73u8,67u8,221u8,235u8,185u8,253u8,173u8, - 193u8,21u8,132u8,17u8,100u8,213u8,116u8,27u8,247u8,104u8,4u8,144u8,124u8,83u8,37u8,196u8,245u8,233u8,0u8,246u8, - 183u8,213u8,240u8,12u8,155u8,110u8,238u8,99u8,235u8,169u8,134u8,122u8,219u8,79u8,156u8,165u8,177u8,142u8,149u8,228u8, - 97u8,91u8,153u8,175u8,233u8,85u8,159u8,53u8,5u8,81u8,57u8,110u8,218u8,1u8,136u8,209u8,44u8,119u8,51u8,124u8, - 84u8,185u8,46u8,48u8,141u8,205u8,132u8,7u8,58u8,236u8,102u8,124u8,111u8,120u8,247u8,186u8,236u8,150u8,0u8,53u8, - 21u8,119u8,3u8,82u8,105u8,251u8,121u8,8u8,25u8,81u8,121u8,160u8,253u8,31u8,185u8,44u8,116u8,117u8,232u8,168u8, - 23u8,118u8,244u8,219u8,92u8,160u8,156u8,90u8,238u8,90u8,244u8,158u8,209u8,252u8,244u8,55u8,126u8,11u8,83u8,100u8, - 168u8,207u8,225u8,2u8,78u8,117u8,117u8,193u8,189u8,8u8,108u8,0u8,172u8,241u8,206u8,28u8,176u8,228u8,118u8,27u8, - 70u8,183u8,59u8,62u8,94u8,249u8,171u8,177u8,209u8,171u8,225u8,145u8,249u8,151u8,100u8,71u8,207u8,129u8,33u8,222u8, - 30u8,134u8,88u8,211u8,168u8,252u8,61u8,238u8,236u8,233u8,174u8,236u8,168u8,165u8,157u8,7u8,36u8,22u8,189u8,246u8, - 141u8,147u8,234u8,207u8,28u8,250u8,29u8,57u8,100u8,64u8,72u8,39u8,146u8,162u8,215u8,255u8,236u8,60u8,127u8,49u8, - 150u8,5u8,74u8,115u8,24u8,100u8,89u8,14u8,230u8,236u8,201u8,190u8,114u8,116u8,15u8,223u8,151u8,141u8,5u8,61u8, - 97u8,252u8,104u8,20u8,121u8,248u8,243u8,5u8,159u8,126u8,202u8,48u8,29u8,68u8,102u8,231u8,46u8,206u8,254u8,206u8, - 2u8,58u8,211u8,34u8,36u8,251u8,77u8,12u8,109u8,47u8,184u8,69u8,70u8,45u8,3u8,190u8,137u8,236u8,221u8,217u8, - 31u8,64u8,16u8,4u8,15u8,9u8,237u8,142u8,10u8,19u8,158u8,203u8,82u8,116u8,213u8,246u8,105u8,245u8,126u8,105u8, - 251u8,217u8,185u8,237u8,103u8,139u8,159u8,207u8,182u8,183u8,190u8,105u8,13u8,27u8,159u8,146u8,106u8,93u8,59u8,157u8, - 105u8,91u8,189u8,198u8,112u8,170u8,243u8,66u8,135u8,82u8,14u8,224u8,151u8,1u8,243u8,42u8,42u8,180u8,61u8,81u8, - 57u8,78u8,184u8,127u8,102u8,64u8,98u8,188u8,188u8,119u8,189u8,249u8,9u8,234u8,205u8,203u8,129u8,122u8,211u8,185u8, - 193u8,221u8,148u8,105u8,112u8,4u8,252u8,223u8,72u8,182u8,7u8,86u8,152u8,47u8,105u8,207u8,190u8,109u8,135u8,245u8, - 104u8,113u8,162u8,147u8,25u8,93u8,152u8,248u8,227u8,114u8,227u8,211u8,119u8,255u8,5u8,16u8,173u8,32u8,62u8,64u8, - 45u8,0u8,0u8,0u8,0u8,13u8,115u8,116u8,97u8,107u8,105u8,110u8,103u8,95u8,112u8,114u8,111u8,120u8,121u8,208u8, - 10u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,237u8,90u8,109u8,111u8,219u8,54u8,16u8,254u8,222u8, - 95u8,113u8,193u8,128u8,76u8,222u8,132u8,212u8,82u8,146u8,110u8,176u8,23u8,160u8,217u8,176u8,15u8,1u8,186u8,96u8, - 192u8,138u8,125u8,41u8,2u8,65u8,177u8,104u8,155u8,168u8,44u8,26u8,34u8,29u8,199u8,27u8,252u8,223u8,75u8,74u8, - 178u8,36u8,190u8,136u8,146u8,99u8,187u8,73u8,83u8,27u8,8u8,146u8,144u8,226u8,189u8,61u8,199u8,231u8,238u8,148u8, - 204u8,72u8,180u8,136u8,17u8,132u8,115u8,70u8,104u8,48u8,78u8,195u8,25u8,90u8,146u8,244u8,243u8,96u8,64u8,89u8, - 248u8,25u8,39u8,147u8,96u8,158u8,146u8,199u8,21u8,252u8,255u8,6u8,248u8,103u8,65u8,17u8,80u8,22u8,241u8,77u8, - 60u8,73u8,80u8,58u8,148u8,23u8,31u8,208u8,136u8,17u8,190u8,88u8,174u8,26u8,101u8,162u8,161u8,125u8,95u8,232u8, - 28u8,145u8,132u8,165u8,225u8,136u8,89u8,30u8,125u8,64u8,148u8,241u8,71u8,11u8,109u8,243u8,197u8,125u8,140u8,71u8, - 128u8,248u8,177u8,21u8,140u8,23u8,9u8,80u8,196u8,2u8,50u8,71u8,105u8,200u8,45u8,114u8,200u8,146u8,27u8,59u8, - 128u8,211u8,220u8,106u8,23u8,72u8,28u8,149u8,155u8,3u8,8u8,163u8,40u8,69u8,148u8,186u8,144u8,160u8,165u8,190u8, - 220u8,43u8,92u8,23u8,31u8,33u8,180u8,208u8,91u8,154u8,168u8,104u8,145u8,133u8,203u8,50u8,123u8,67u8,73u8,146u8, - 234u8,236u8,110u8,146u8,80u8,48u8,39u8,36u8,214u8,100u8,24u8,78u8,173u8,45u8,33u8,123u8,32u8,12u8,25u8,226u8, - 101u8,142u8,85u8,246u8,112u8,247u8,64u8,213u8,100u8,87u8,34u8,107u8,146u8,218u8,194u8,243u8,132u8,243u8,69u8,80u8, - 164u8,147u8,234u8,243u8,214u8,112u8,216u8,193u8,222u8,67u8,74u8,197u8,136u8,65u8,38u8,43u8,40u8,54u8,225u8,10u8, - 114u8,153u8,131u8,65u8,177u8,18u8,144u8,113u8,174u8,174u8,230u8,160u8,56u8,166u8,26u8,39u8,142u8,158u8,22u8,139u8, - 229u8,253u8,168u8,118u8,29u8,73u8,141u8,34u8,11u8,243u8,179u8,125u8,121u8,41u8,70u8,9u8,95u8,204u8,239u8,244u8, - 96u8,192u8,127u8,155u8,176u8,169u8,163u8,73u8,173u8,201u8,89u8,78u8,49u8,231u8,17u8,7u8,195u8,111u8,226u8,108u8, - 221u8,201u8,38u8,139u8,185u8,252u8,159u8,54u8,10u8,238u8,73u8,154u8,146u8,165u8,174u8,192u8,5u8,92u8,211u8,33u8, - 62u8,120u8,12u8,78u8,233u8,102u8,9u8,136u8,122u8,176u8,7u8,87u8,87u8,18u8,42u8,170u8,65u8,27u8,163u8,70u8, - 139u8,52u8,229u8,168u8,243u8,131u8,179u8,25u8,166u8,20u8,147u8,36u8,224u8,39u8,70u8,124u8,41u8,156u8,160u8,44u8, - 2u8,138u8,42u8,243u8,147u8,186u8,1u8,67u8,77u8,93u8,41u8,106u8,49u8,143u8,66u8,134u8,180u8,203u8,170u8,138u8, - 144u8,243u8,199u8,181u8,155u8,170u8,232u8,91u8,43u8,65u8,227u8,158u8,96u8,248u8,25u8,188u8,106u8,121u8,221u8,154u8, - 254u8,45u8,12u8,245u8,124u8,233u8,47u8,82u8,64u8,53u8,78u8,175u8,30u8,1u8,122u8,196u8,84u8,77u8,124u8,217u8, - 214u8,158u8,41u8,77u8,219u8,50u8,66u8,215u8,108u8,206u8,8u8,155u8,94u8,25u8,29u8,131u8,47u8,75u8,204u8,70u8, - 211u8,45u8,74u8,66u8,247u8,236u8,88u8,15u8,59u8,1u8,111u8,44u8,40u8,53u8,200u8,15u8,141u8,45u8,42u8,218u8, - 133u8,220u8,12u8,19u8,148u8,26u8,120u8,155u8,99u8,136u8,117u8,169u8,131u8,29u8,99u8,97u8,171u8,99u8,187u8,212u8, - 200u8,35u8,243u8,31u8,148u8,249u8,45u8,172u8,175u8,210u8,176u8,212u8,30u8,152u8,57u8,88u8,109u8,48u8,14u8,198u8, - 176u8,207u8,155u8,92u8,187u8,241u8,106u8,19u8,167u8,234u8,18u8,77u8,129u8,183u8,119u8,116u8,219u8,145u8,150u8,57u8, - 138u8,246u8,144u8,89u8,121u8,167u8,49u8,118u8,54u8,14u8,138u8,80u8,140u8,38u8,220u8,207u8,200u8,222u8,128u8,234u8, - 238u8,253u8,240u8,137u8,241u8,44u8,12u8,72u8,18u8,175u8,238u8,178u8,5u8,30u8,57u8,202u8,224u8,230u8,246u8,230u8, - 227u8,205u8,245u8,135u8,224u8,247u8,235u8,15u8,215u8,183u8,127u8,252u8,57u8,128u8,197u8,187u8,11u8,142u8,171u8,215u8, - 151u8,62u8,67u8,120u8,251u8,22u8,188u8,191u8,224u8,250u8,239u8,143u8,252u8,28u8,78u8,40u8,240u8,82u8,50u8,133u8, - 95u8,33u8,66u8,35u8,60u8,11u8,99u8,122u8,86u8,87u8,226u8,148u8,38u8,40u8,19u8,22u8,23u8,252u8,190u8,255u8, - 232u8,185u8,229u8,126u8,102u8,125u8,177u8,234u8,159u8,215u8,214u8,55u8,109u8,145u8,151u8,111u8,250u8,231u8,23u8,134u8, - 77u8,63u8,223u8,60u8,191u8,184u8,172u8,54u8,235u8,156u8,156u8,111u8,95u8,190u8,251u8,37u8,223u8,238u8,221u8,153u8, - 113u8,206u8,226u8,34u8,241u8,123u8,147u8,253u8,21u8,238u8,178u8,7u8,166u8,245u8,210u8,3u8,219u8,166u8,111u8,216u8, - 148u8,107u8,160u8,180u8,189u8,39u8,170u8,175u8,108u8,107u8,57u8,91u8,62u8,215u8,36u8,192u8,239u8,40u8,192u8,87u8, - 4u8,212u8,125u8,180u8,139u8,104u8,168u8,177u8,37u8,221u8,114u8,220u8,22u8,115u8,71u8,186u8,45u8,10u8,104u8,174u8, - 40u8,99u8,162u8,42u8,124u8,106u8,224u8,150u8,42u8,12u8,174u8,193u8,51u8,215u8,104u8,236u8,93u8,125u8,48u8,212u8, - 201u8,77u8,24u8,165u8,241u8,176u8,163u8,25u8,166u8,176u8,84u8,224u8,185u8,234u8,125u8,116u8,161u8,127u8,16u8,77u8, - 126u8,131u8,38u8,107u8,75u8,144u8,221u8,70u8,57u8,242u8,90u8,39u8,179u8,33u8,164u8,77u8,204u8,223u8,123u8,222u8, - 93u8,245u8,155u8,162u8,243u8,206u8,5u8,5u8,147u8,190u8,9u8,227u8,142u8,147u8,141u8,8u8,159u8,9u8,211u8,126u8, - 75u8,167u8,147u8,241u8,200u8,214u8,110u8,249u8,95u8,205u8,45u8,223u8,156u8,150u8,26u8,92u8,78u8,64u8,57u8,210u8, - 115u8,241u8,69u8,230u8,189u8,98u8,166u8,224u8, - ]; - let chunk6 = vector[ - 101u8,99u8,130u8,18u8,113u8,26u8,5u8,56u8,226u8,164u8,135u8,217u8,202u8,81u8,82u8,138u8,63u8,131u8,19u8, - 204u8,112u8,24u8,227u8,255u8,80u8,144u8,209u8,225u8,3u8,255u8,57u8,202u8,172u8,58u8,21u8,2u8,79u8,185u8,196u8, - 50u8,137u8,180u8,196u8,25u8,243u8,2u8,128u8,138u8,111u8,186u8,104u8,83u8,231u8,172u8,195u8,84u8,119u8,166u8,219u8, - 9u8,243u8,197u8,172u8,233u8,231u8,37u8,235u8,31u8,97u8,2u8,136u8,162u8,251u8,35u8,45u8,69u8,192u8,52u8,164u8, - 112u8,143u8,120u8,67u8,154u8,15u8,68u8,40u8,130u8,113u8,74u8,102u8,213u8,190u8,7u8,140u8,8u8,225u8,229u8,202u8, - 89u8,85u8,16u8,40u8,69u8,41u8,59u8,113u8,202u8,208u8,170u8,150u8,150u8,118u8,136u8,134u8,209u8,100u8,160u8,156u8, - 12u8,133u8,141u8,28u8,111u8,40u8,219u8,216u8,29u8,204u8,51u8,137u8,203u8,42u8,117u8,173u8,102u8,210u8,41u8,89u8, - 196u8,145u8,64u8,103u8,5u8,139u8,100u8,52u8,13u8,147u8,9u8,138u8,204u8,30u8,62u8,169u8,101u8,51u8,162u8,226u8, - 66u8,189u8,134u8,108u8,116u8,156u8,236u8,220u8,23u8,214u8,18u8,200u8,5u8,223u8,160u8,98u8,119u8,13u8,126u8,77u8, - 195u8,185u8,12u8,221u8,191u8,249u8,85u8,173u8,98u8,237u8,109u8,15u8,94u8,49u8,232u8,60u8,168u8,162u8,252u8,12u8, - 33u8,90u8,65u8,164u8,185u8,214u8,62u8,181u8,240u8,194u8,221u8,156u8,134u8,23u8,134u8,104u8,117u8,16u8,233u8,75u8, - 163u8,80u8,157u8,144u8,46u8,123u8,134u8,134u8,243u8,91u8,239u8,5u8,131u8,132u8,176u8,169u8,8u8,0u8,35u8,65u8, - 14u8,196u8,177u8,59u8,60u8,118u8,135u8,207u8,210u8,29u8,62u8,173u8,103u8,123u8,5u8,205u8,205u8,254u8,250u8,129u8, - 91u8,178u8,193u8,3u8,56u8,37u8,168u8,156u8,75u8,161u8,177u8,26u8,158u8,236u8,187u8,28u8,246u8,15u8,94u8,171u8, - 188u8,125u8,243u8,187u8,255u8,18u8,249u8,61u8,123u8,233u8,177u8,45u8,185u8,231u8,175u8,78u8,158u8,157u8,199u8,139u8, - 247u8,70u8,175u8,151u8,196u8,51u8,7u8,219u8,25u8,92u8,125u8,113u8,245u8,181u8,233u8,91u8,50u8,243u8,56u8,217u8, - 31u8,39u8,251u8,227u8,100u8,111u8,158u8,236u8,229u8,255u8,197u8,48u8,189u8,104u8,87u8,203u8,177u8,116u8,181u8,44u8, - 179u8,121u8,78u8,227u8,229u8,240u8,148u8,7u8,48u8,178u8,206u8,221u8,198u8,55u8,225u8,218u8,248u8,45u8,25u8,208u8, - 62u8,123u8,75u8,195u8,50u8,120u8,29u8,44u8,219u8,195u8,200u8,45u8,32u8,174u8,253u8,121u8,96u8,195u8,142u8,158u8, - 241u8,207u8,194u8,250u8,131u8,29u8,102u8,227u8,97u8,155u8,46u8,127u8,87u8,93u8,190u8,65u8,87u8,7u8,208u8,76u8, - 110u8,55u8,97u8,231u8,237u8,44u8,186u8,189u8,183u8,105u8,28u8,236u8,27u8,51u8,161u8,113u8,126u8,231u8,39u8,148u8, - 9u8,254u8,172u8,185u8,31u8,203u8,77u8,182u8,204u8,239u8,74u8,44u8,206u8,109u8,205u8,93u8,131u8,176u8,194u8,123u8, - 25u8,192u8,139u8,215u8,213u8,212u8,189u8,196u8,113u8,253u8,216u8,230u8,125u8,55u8,109u8,222u8,113u8,68u8,239u8,48u8, - 162u8,91u8,122u8,130u8,173u8,231u8,115u8,115u8,57u8,123u8,49u8,197u8,172u8,177u8,222u8,244u8,247u8,197u8,223u8,94u8, - 201u8,223u8,235u8,55u8,95u8,0u8,51u8,198u8,172u8,49u8,118u8,45u8,0u8,0u8,0u8,0u8,2u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,11u8,65u8,112u8,116u8,111u8,115u8,83u8,116u8,100u8,108u8,105u8, - 98u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,77u8,111u8,118u8,101u8,83u8,116u8, - 100u8,108u8,105u8,98u8,0u8, - ]; - vector::append(&mut chunk1, chunk2); - vector::append(&mut chunk1, chunk3); - vector::append(&mut chunk1, chunk4); - vector::append(&mut chunk1, chunk5); - vector::append(&mut chunk1, chunk6); - code::publish_package_txn(&framework_signer, chunk1, code) - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/3-aptos-token.move b/aptos-move/aptos-release-builder/data/example_output/3-aptos-token.move deleted file mode 100644 index dd676a1189190..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/3-aptos-token.move +++ /dev/null @@ -1,2316 +0,0 @@ -// Script hash: 60addb50 -// Framework commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Builder commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 -// Upgrade proposal for package `AptosToken` - -// source digest: 0E08A737463C233B100E2E877A8EC11C9A1BAF47326170149626706DAC27F3FF -script { - use std::vector; - use aptos_framework::aptos_governance; - use aptos_framework::code; - - fun main(proposal_id: u64){ - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000003, - vector[10u8,250u8,103u8,46u8,177u8,194u8,183u8,47u8,7u8,178u8,167u8,92u8,126u8,197u8,250u8,184u8,210u8,21u8,17u8,2u8,99u8,199u8,181u8,147u8,192u8,11u8,129u8,35u8,12u8,38u8,84u8,54u8,], - ); - let code = vector::empty(); - let chunk0 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,14u8,2u8,14u8,20u8,3u8,34u8,217u8,1u8, - 4u8,251u8,1u8,18u8,5u8,141u8,2u8,223u8,1u8,7u8,236u8,3u8,252u8,3u8,8u8,232u8,7u8,64u8,6u8,168u8, - 8u8,167u8,1u8,16u8,207u8,9u8,141u8,4u8,10u8,220u8,13u8,20u8,12u8,240u8,13u8,167u8,8u8,13u8,151u8,22u8, - 6u8,0u8,0u8,1u8,1u8,1u8,2u8,1u8,3u8,1u8,4u8,1u8,5u8,1u8,6u8,0u8,7u8,7u8,0u8,0u8, - 8u8,7u8,0u8,5u8,9u8,7u8,0u8,4u8,31u8,7u8,2u8,0u8,0u8,0u8,0u8,0u8,10u8,0u8,1u8,0u8, - 0u8,11u8,2u8,3u8,0u8,0u8,12u8,3u8,4u8,0u8,0u8,13u8,3u8,5u8,0u8,0u8,14u8,2u8,6u8,0u8, - 0u8,15u8,7u8,8u8,1u8,1u8,0u8,16u8,9u8,8u8,0u8,0u8,17u8,1u8,10u8,0u8,0u8,18u8,11u8,12u8, - 0u8,0u8,19u8,13u8,10u8,0u8,0u8,20u8,14u8,10u8,0u8,0u8,21u8,2u8,15u8,0u8,0u8,22u8,2u8,6u8, - 0u8,0u8,23u8,2u8,4u8,0u8,0u8,24u8,2u8,16u8,0u8,0u8,25u8,2u8,12u8,0u8,0u8,26u8,2u8,17u8, - 0u8,0u8,27u8,18u8,19u8,0u8,0u8,28u8,20u8,1u8,0u8,0u8,29u8,21u8,1u8,0u8,5u8,18u8,22u8,12u8, - 0u8,2u8,34u8,12u8,12u8,0u8,4u8,18u8,23u8,12u8,2u8,4u8,4u8,2u8,35u8,12u8,12u8,0u8,4u8,10u8, - 24u8,1u8,2u8,4u8,4u8,4u8,11u8,25u8,26u8,2u8,4u8,4u8,4u8,14u8,25u8,6u8,2u8,4u8,4u8,6u8, - 36u8,1u8,4u8,1u8,0u8,5u8,37u8,5u8,4u8,0u8,1u8,38u8,7u8,5u8,1u8,0u8,4u8,39u8,1u8,29u8, - 2u8,4u8,4u8,3u8,40u8,5u8,15u8,0u8,3u8,41u8,5u8,6u8,0u8,3u8,42u8,5u8,4u8,0u8,3u8,43u8, - 5u8,16u8,0u8,3u8,44u8,5u8,12u8,0u8,3u8,45u8,5u8,17u8,0u8,2u8,46u8,12u8,12u8,0u8,4u8,27u8, - 32u8,33u8,2u8,4u8,4u8,4u8,47u8,32u8,36u8,2u8,4u8,4u8,22u8,19u8,24u8,19u8,25u8,19u8,26u8,19u8, - 27u8,28u8,29u8,28u8,30u8,19u8,38u8,19u8,39u8,19u8,3u8,7u8,8u8,0u8,8u8,2u8,8u8,1u8,0u8,2u8, - 6u8,8u8,0u8,6u8,8u8,2u8,1u8,6u8,8u8,1u8,1u8,8u8,2u8,1u8,10u8,2u8,1u8,1u8,1u8,6u8, - 9u8,0u8,1u8,8u8,1u8,2u8,10u8,2u8,8u8,2u8,1u8,8u8,0u8,1u8,6u8,8u8,0u8,1u8,3u8,3u8, - 10u8,8u8,2u8,10u8,10u8,2u8,10u8,8u8,2u8,2u8,10u8,8u8,2u8,10u8,8u8,1u8,1u8,5u8,1u8,4u8, - 1u8,2u8,2u8,7u8,8u8,0u8,6u8,8u8,2u8,2u8,8u8,2u8,8u8,1u8,4u8,7u8,8u8,0u8,10u8,8u8, - 2u8,10u8,10u8,2u8,10u8,8u8,2u8,3u8,7u8,8u8,0u8,6u8,8u8,2u8,8u8,1u8,1u8,6u8,8u8,2u8, - 1u8,6u8,11u8,3u8,2u8,9u8,0u8,9u8,1u8,3u8,7u8,11u8,3u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8, - 9u8,1u8,2u8,6u8,11u8,3u8,2u8,9u8,0u8,9u8,1u8,6u8,9u8,0u8,1u8,6u8,9u8,1u8,7u8,1u8, - 1u8,1u8,1u8,1u8,8u8,1u8,8u8,2u8,1u8,9u8,0u8,1u8,11u8,3u8,2u8,9u8,0u8,9u8,1u8,4u8, - 3u8,8u8,2u8,3u8,8u8,0u8,5u8,3u8,8u8,2u8,3u8,8u8,0u8,8u8,1u8,2u8,7u8,11u8,3u8,2u8, - 9u8,0u8,9u8,1u8,6u8,9u8,0u8,2u8,9u8,0u8,9u8,1u8,7u8,6u8,8u8,2u8,3u8,6u8,8u8,2u8, - 3u8,8u8,1u8,3u8,3u8,1u8,7u8,8u8,1u8,1u8,7u8,9u8,1u8,12u8,112u8,114u8,111u8,112u8,101u8,114u8, - 116u8,121u8,95u8,109u8,97u8,112u8,3u8,98u8,99u8,115u8,5u8,101u8,114u8,114u8,111u8,114u8,8u8,102u8,114u8,111u8, - 109u8,95u8,98u8,99u8,115u8,10u8,115u8,105u8,109u8,112u8,108u8,101u8,95u8,109u8,97u8,112u8,6u8,115u8,116u8,114u8, - 105u8,110u8,103u8,9u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,11u8,80u8,114u8,111u8,112u8,101u8,114u8, - 116u8,121u8,77u8,97u8,112u8,13u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,86u8,97u8,108u8,117u8,101u8,6u8, - 83u8,116u8,114u8,105u8,110u8,103u8,3u8,97u8,100u8,100u8,6u8,98u8,111u8,114u8,114u8,111u8,119u8,11u8,98u8,111u8, - 114u8,114u8,111u8,119u8,95u8,116u8,121u8,112u8,101u8,12u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,118u8,97u8,108u8, - 117u8,101u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,95u8,107u8,101u8,121u8,21u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8,108u8,117u8,101u8,25u8,99u8,114u8, - 101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8,108u8,117u8,101u8,95u8, - 114u8,97u8,119u8,5u8,101u8,109u8,112u8,116u8,121u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,3u8,110u8,101u8,119u8, - 31u8,110u8,101u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,107u8,101u8,121u8,95u8,97u8,110u8,100u8,95u8,112u8,114u8, - 111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8,108u8,117u8,101u8,12u8,114u8,101u8,97u8,100u8,95u8,97u8,100u8, - 100u8,114u8,101u8,115u8,115u8,9u8,114u8,101u8,97u8,100u8,95u8,98u8,111u8,111u8,108u8,11u8,114u8,101u8,97u8,100u8, - 95u8,115u8,116u8,114u8,105u8,110u8,103u8,9u8,114u8,101u8,97u8,100u8,95u8,117u8,49u8,50u8,56u8,8u8,114u8,101u8, - 97u8,100u8,95u8,117u8,54u8,52u8,7u8,114u8,101u8,97u8,100u8,95u8,117u8,56u8,6u8,114u8,101u8,109u8,111u8,118u8, - 101u8,19u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,109u8,97u8, - 112u8,21u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8, - 108u8,117u8,101u8,3u8,109u8,97u8,112u8,9u8,83u8,105u8,109u8,112u8,108u8,101u8,77u8,97u8,112u8,5u8,118u8,97u8, - 108u8,117u8,101u8,4u8,116u8,121u8,112u8,101u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8, - 117u8,109u8,101u8,110u8,116u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8,116u8,97u8,116u8,101u8,9u8, - 116u8,121u8,112u8,101u8,95u8,110u8,97u8,109u8,101u8,4u8,117u8,116u8,102u8,56u8,8u8,116u8,111u8,95u8,98u8,121u8, - 116u8,101u8,115u8,6u8,99u8,114u8,101u8,97u8,116u8,101u8,10u8,116u8,111u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8, - 115u8,7u8,116u8,111u8,95u8,98u8,111u8,111u8,108u8,9u8,116u8,111u8,95u8,115u8,116u8,114u8,105u8,110u8,103u8,7u8, - 116u8,111u8,95u8,117u8,49u8,50u8,56u8,6u8,116u8,111u8,95u8,117u8,54u8,52u8,5u8,116u8,111u8,95u8,117u8,56u8, - 9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8, - 116u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,5u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,232u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,3u8,8u8,128u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,5u8,4u8,98u8, - 111u8,111u8,108u8,10u8,2u8,3u8,2u8,117u8,56u8,10u8,2u8,4u8,3u8,117u8,54u8,52u8,10u8,2u8,5u8,4u8, - 117u8,49u8,50u8,56u8,10u8,2u8,8u8,7u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,10u8,2u8,20u8,19u8,48u8, - 120u8,49u8,58u8,58u8,115u8,116u8,114u8,105u8,110u8,103u8,58u8,58u8,83u8,116u8,114u8,105u8,110u8,103u8,10u8,2u8, - 11u8,10u8,118u8,101u8,99u8,116u8,111u8,114u8,60u8,117u8,56u8,62u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8, - 109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,248u8,3u8,7u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,33u8,69u8,75u8,69u8,89u8,95u8,65u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8, - 95u8,73u8,78u8,95u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,89u8,95u8,77u8,65u8,80u8,31u8,84u8,104u8,101u8, - 32u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,107u8,101u8,121u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8, - 121u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,29u8,69u8,80u8,82u8, - 79u8,80u8,69u8,82u8,84u8,89u8,95u8,78u8,85u8,77u8,66u8,69u8,82u8,95u8,69u8,88u8,67u8,69u8,69u8,68u8, - 95u8,76u8,73u8,77u8,73u8,84u8,40u8,84u8,104u8,101u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8,111u8,102u8, - 32u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,101u8,120u8,99u8,101u8,101u8,100u8,115u8,32u8,116u8,104u8, - 101u8,32u8,108u8,105u8,109u8,105u8,116u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8,80u8,82u8,79u8, - 80u8,69u8,82u8,84u8,89u8,95u8,78u8,79u8,84u8,95u8,69u8,88u8,73u8,83u8,84u8,26u8,84u8,104u8,101u8,32u8, - 112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,101u8,120u8,105u8, - 115u8,116u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,32u8,69u8,75u8,69u8,89u8,95u8,67u8,79u8,85u8,78u8, - 84u8,95u8,78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8,95u8,86u8,65u8,76u8,85u8,69u8,95u8,67u8,79u8, - 85u8,78u8,84u8,40u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,107u8,101u8,121u8,32u8,97u8,110u8,100u8, - 32u8,118u8,97u8,108u8,117u8,101u8,32u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,110u8,39u8,116u8,32u8,109u8, - 97u8,116u8,99u8,104u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,31u8,69u8,75u8,69u8,89u8,95u8,67u8,79u8, - 85u8,78u8,84u8,95u8,78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8,95u8,84u8,89u8,80u8,69u8,95u8,67u8, - 79u8,85u8,78u8,84u8,39u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,107u8,101u8,121u8,32u8,97u8,110u8, - 100u8,32u8,116u8,121u8,112u8,101u8,32u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,110u8,39u8,116u8,32u8,109u8, - 97u8,116u8,99u8,104u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,15u8,69u8,84u8,89u8,80u8,69u8,95u8,78u8, - 79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8,27u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,116u8,121u8, - 112u8,101u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,7u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,27u8,69u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,89u8,95u8,77u8,65u8,80u8,95u8,78u8, - 65u8,77u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8,42u8,84u8,104u8,101u8,32u8,110u8,97u8,109u8, - 101u8,32u8,40u8,107u8,101u8,121u8,41u8,32u8,111u8,102u8,32u8,116u8,104u8,101u8,32u8,112u8,114u8,111u8,112u8,101u8, - 114u8,116u8,121u8,32u8,105u8,115u8,32u8,116u8,111u8,111u8,32u8,108u8,111u8,110u8,103u8,0u8,0u8,0u8,2u8,1u8, - 30u8,11u8,3u8,2u8,8u8,2u8,8u8,1u8,1u8,2u8,2u8,32u8,10u8,2u8,33u8,8u8,2u8,0u8,1u8,0u8, - 0u8,1u8,29u8,14u8,1u8,17u8,20u8,7u8,8u8,37u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,3u8,17u8, - 21u8,39u8,10u8,0u8,16u8,0u8,56u8,0u8,7u8,7u8,35u8,4u8,18u8,5u8,23u8,11u8,0u8,1u8,7u8,5u8, - 17u8,23u8,39u8,11u8,0u8,15u8,0u8,11u8,1u8,11u8,2u8,56u8,1u8,2u8,1u8,1u8,0u8,0u8,1u8,16u8, - 10u8,0u8,10u8,1u8,17u8,4u8,4u8,5u8,5u8,11u8,11u8,0u8,1u8,11u8,1u8,1u8,7u8,4u8,39u8,11u8, - 0u8,16u8,0u8,11u8,1u8,56u8,2u8,2u8,2u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,1u8,20u8,2u8, - 3u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,2u8,20u8,2u8,4u8,1u8,0u8,0u8,1u8,5u8,11u8,0u8, - 16u8,0u8,11u8,1u8,56u8,3u8,2u8,5u8,1u8,0u8,0u8,27u8,71u8,56u8,4u8,12u8,7u8,10u8,7u8,7u8, - 9u8,17u8,28u8,33u8,4u8,10u8,8u8,12u8,1u8,5u8,15u8,10u8,7u8,7u8,10u8,17u8,28u8,33u8,12u8,1u8, - 11u8,1u8,4u8,20u8,8u8,12u8,2u8,5u8,25u8,10u8,7u8,7u8,11u8,17u8,28u8,33u8,12u8,2u8,11u8,2u8, - 4u8,30u8,8u8,12u8,3u8,5u8,35u8,10u8,7u8,7u8,12u8,17u8,28u8,33u8,12u8,3u8,11u8,3u8,4u8,40u8, - 8u8,12u8,4u8,5u8,45u8,10u8,7u8,7u8,13u8,17u8,28u8,33u8,12u8,4u8,11u8,4u8,4u8,50u8,8u8,12u8, - 5u8,5u8,55u8,10u8,7u8,7u8,14u8,17u8,28u8,33u8,12u8,5u8,11u8,5u8,4u8,63u8,11u8,0u8,56u8,5u8, - 11u8,7u8,17u8,6u8,12u8,6u8,5u8,69u8,11u8,0u8,56u8,5u8,7u8,15u8,17u8,28u8,17u8,6u8,12u8,6u8, - 11u8,6u8,2u8,6u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,11u8,1u8,18u8,1u8,2u8,7u8,1u8,0u8,0u8, - 1u8,3u8,56u8,6u8,18u8,0u8,2u8,8u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,0u8,56u8,0u8,2u8, - 9u8,1u8,0u8,0u8,30u8,72u8,14u8,0u8,65u8,4u8,12u8,5u8,10u8,5u8,7u8,7u8,37u8,4u8,8u8,5u8, - 11u8,7u8,5u8,17u8,21u8,39u8,10u8,5u8,14u8,1u8,65u8,5u8,33u8,4u8,17u8,5u8,20u8,7u8,2u8,17u8, - 21u8,39u8,10u8,5u8,14u8,2u8,65u8,4u8,33u8,4u8,26u8,5u8,29u8,7u8,1u8,17u8,21u8,39u8,17u8,7u8, - 12u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,3u8,10u8,3u8,10u8,5u8,35u8,4u8,70u8, - 5u8,38u8,14u8,0u8,10u8,3u8,66u8,4u8,20u8,12u8,4u8,14u8,4u8,17u8,20u8,7u8,8u8,37u8,4u8,49u8, - 5u8,52u8,7u8,3u8,17u8,21u8,39u8,13u8,6u8,15u8,0u8,11u8,4u8,14u8,1u8,10u8,3u8,66u8,5u8,20u8, - 14u8,2u8,10u8,3u8,66u8,4u8,20u8,18u8,1u8,56u8,1u8,11u8,3u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,12u8,3u8,5u8,33u8,11u8,6u8,2u8,10u8,1u8,0u8,0u8,31u8,59u8,14u8,0u8,65u8,4u8, - 12u8,4u8,10u8,4u8,7u8,7u8,37u8,4u8,8u8,5u8,11u8,7u8,5u8,17u8,21u8,39u8,10u8,4u8,14u8,1u8, - 65u8,8u8,33u8,4u8,17u8,5u8,20u8,7u8,2u8,17u8,21u8,39u8,17u8,7u8,12u8,5u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,12u8,2u8,10u8,2u8,10u8,4u8,35u8,4u8,57u8,5u8,29u8,14u8,0u8,10u8,2u8, - 66u8,4u8,20u8,12u8,3u8,14u8,1u8,10u8,2u8,66u8,8u8,20u8,12u8,6u8,14u8,3u8,17u8,20u8,7u8,8u8, - 37u8,4u8,45u8,5u8,48u8,7u8,3u8,17u8,21u8,39u8,13u8,5u8,11u8,3u8,11u8,6u8,17u8,0u8,11u8,2u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,24u8,11u8,5u8,2u8,11u8,1u8,0u8, - 0u8,3u8,22u8,11u8,0u8,11u8,1u8,17u8,1u8,12u8,2u8,10u8,2u8,16u8,1u8,20u8,7u8,13u8,17u8,28u8, - 33u8,4u8,12u8,5u8,17u8,11u8,2u8,1u8,7u8,6u8,17u8,23u8,39u8,11u8,2u8,16u8,2u8,20u8,17u8,31u8, - 2u8,12u8,1u8,0u8,0u8,3u8,22u8,11u8,0u8,11u8,1u8,17u8,1u8,12u8,2u8,10u8,2u8,16u8,1u8,20u8, - 7u8,9u8,17u8,28u8,33u8,4u8,12u8,5u8,17u8,11u8,2u8,1u8,7u8,6u8,17u8,23u8,39u8,11u8,2u8,16u8, - 2u8,20u8,17u8,32u8,2u8,13u8,1u8,0u8,0u8,3u8,22u8,11u8,0u8,11u8,1u8,17u8,1u8,12u8,2u8,10u8, - 2u8,16u8,1u8,20u8,7u8,14u8,17u8,28u8,33u8,4u8,12u8,5u8,17u8,11u8,2u8,1u8,7u8,6u8,17u8,23u8, - 39u8,11u8,2u8,16u8,2u8,20u8,17u8,33u8,2u8,14u8,1u8,0u8,0u8,3u8,22u8,11u8,0u8,11u8,1u8,17u8, - 1u8,12u8,2u8,10u8,2u8,16u8,1u8,20u8,7u8,12u8,17u8,28u8,33u8,4u8,12u8,5u8,17u8,11u8,2u8,1u8, - 7u8,6u8,17u8,23u8,39u8,11u8,2u8,16u8,2u8,20u8,17u8,34u8,2u8,15u8,1u8,0u8,0u8,3u8,22u8,11u8, - 0u8,11u8,1u8,17u8,1u8,12u8,2u8,10u8,2u8,16u8,1u8,20u8,7u8,11u8,17u8,28u8,33u8,4u8,12u8,5u8, - 17u8,11u8,2u8,1u8,7u8,6u8,17u8,23u8,39u8,11u8,2u8,16u8,2u8,20u8,17u8,35u8,2u8,16u8,1u8,0u8, - 0u8,3u8,22u8,11u8,0u8,11u8,1u8,17u8,1u8,12u8,2u8,10u8,2u8,16u8,1u8,20u8,7u8,10u8,17u8,28u8, - 33u8,4u8,12u8,5u8,17u8,11u8,2u8,1u8,7u8,6u8,17u8,23u8,39u8,11u8,2u8,16u8,2u8,20u8,17u8,36u8, - 2u8,17u8,1u8,0u8,0u8,22u8,20u8,10u8,0u8,10u8,1u8,12u8,2u8,46u8,11u8,2u8,17u8,4u8,4u8,8u8, - 5u8,15u8,11u8,0u8,1u8,11u8,1u8,1u8,7u8,4u8,17u8,37u8,39u8,11u8,0u8,15u8,0u8,11u8,1u8,56u8, - 7u8,2u8,18u8,1u8,0u8,0u8,34u8,75u8,14u8,1u8,65u8,4u8,12u8,7u8,14u8,2u8,65u8,5u8,12u8,10u8, - 14u8,3u8,65u8,4u8,12u8,9u8,10u8,7u8,11u8,10u8,33u8,4u8,14u8,5u8,19u8,11u8,0u8,1u8,7u8,2u8, - 17u8,23u8,39u8,10u8,7u8,11u8,9u8,33u8,4u8,24u8,5u8,29u8,11u8,0u8,1u8,7u8,1u8,17u8,23u8,39u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,5u8,10u8,5u8,10u8,7u8,35u8,4u8,72u8,5u8,36u8, - 14u8,1u8,10u8,5u8,66u8,4u8,12u8,6u8,14u8,2u8,10u8,5u8,66u8,5u8,20u8,14u8,3u8,10u8,5u8,66u8, - 4u8,20u8,18u8,1u8,12u8,8u8,10u8,0u8,10u8,6u8,12u8,4u8,46u8,11u8,4u8,17u8,4u8,4u8,62u8,10u8, - 0u8,11u8,6u8,11u8,8u8,17u8,19u8,5u8,67u8,10u8,0u8,11u8,6u8,20u8,11u8,8u8,17u8,0u8,11u8,5u8, - 6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,5u8,5u8,31u8,11u8,0u8,1u8,2u8,19u8,1u8, - 0u8,0u8,35u8,9u8,11u8,0u8,15u8,0u8,11u8,1u8,56u8,8u8,12u8,3u8,11u8,2u8,11u8,3u8,21u8,2u8, - 0u8,0u8,1u8,1u8,1u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk0); - let chunk1 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,11u8,1u8,0u8,16u8,2u8,16u8,64u8,3u8,80u8,73u8,4u8, - 153u8,1u8,38u8,5u8,191u8,1u8,149u8,1u8,7u8,212u8,2u8,227u8,9u8,8u8,183u8,12u8,64u8,10u8,247u8,12u8, - 206u8,1u8,12u8,197u8,14u8,173u8,3u8,13u8,242u8,17u8,18u8,15u8,132u8,18u8,2u8,0u8,1u8,1u8,2u8,1u8, - 3u8,1u8,4u8,1u8,5u8,1u8,6u8,1u8,7u8,0u8,8u8,0u8,9u8,6u8,0u8,0u8,10u8,6u8,0u8,0u8, - 11u8,6u8,0u8,0u8,12u8,6u8,0u8,0u8,13u8,6u8,0u8,0u8,14u8,6u8,0u8,0u8,15u8,6u8,0u8,0u8, - 16u8,6u8,0u8,0u8,17u8,8u8,0u8,0u8,18u8,6u8,0u8,6u8,19u8,7u8,0u8,4u8,23u8,7u8,1u8,0u8, - 0u8,7u8,24u8,7u8,0u8,3u8,53u8,4u8,1u8,6u8,1u8,2u8,63u8,6u8,0u8,0u8,20u8,0u8,1u8,0u8, - 0u8,21u8,2u8,1u8,0u8,0u8,22u8,0u8,1u8,0u8,0u8,25u8,3u8,1u8,0u8,0u8,26u8,4u8,1u8,0u8, - 0u8,27u8,5u8,1u8,0u8,0u8,28u8,6u8,1u8,0u8,0u8,29u8,7u8,1u8,0u8,0u8,30u8,4u8,1u8,0u8, - 0u8,31u8,8u8,1u8,0u8,5u8,64u8,8u8,10u8,0u8,3u8,65u8,11u8,1u8,1u8,6u8,1u8,66u8,8u8,25u8, - 1u8,6u8,4u8,67u8,1u8,27u8,1u8,0u8,11u8,9u8,11u8,12u8,11u8,13u8,11u8,15u8,11u8,17u8,11u8,19u8, - 11u8,20u8,11u8,22u8,11u8,24u8,12u8,13u8,12u8,12u8,12u8,9u8,12u8,20u8,12u8,24u8,12u8,15u8,12u8,17u8, - 12u8,22u8,12u8,19u8,13u8,26u8,4u8,6u8,12u8,8u8,10u8,8u8,10u8,8u8,10u8,0u8,4u8,6u8,12u8,8u8, - 10u8,3u8,3u8,6u8,6u8,12u8,8u8,10u8,8u8,10u8,10u8,8u8,10u8,10u8,11u8,11u8,1u8,8u8,12u8,10u8, - 8u8,12u8,5u8,6u8,12u8,8u8,10u8,8u8,10u8,8u8,10u8,8u8,10u8,5u8,6u8,12u8,8u8,10u8,8u8,10u8, - 3u8,3u8,2u8,6u8,12u8,1u8,9u8,6u8,12u8,8u8,10u8,8u8,10u8,3u8,3u8,5u8,3u8,3u8,5u8,1u8, - 6u8,12u8,1u8,8u8,0u8,1u8,5u8,2u8,7u8,11u8,13u8,1u8,9u8,0u8,9u8,0u8,1u8,8u8,1u8,1u8, - 8u8,2u8,2u8,5u8,8u8,3u8,1u8,8u8,3u8,2u8,5u8,8u8,4u8,1u8,8u8,4u8,2u8,5u8,8u8,5u8, - 1u8,8u8,5u8,1u8,8u8,6u8,2u8,5u8,8u8,7u8,1u8,8u8,7u8,2u8,5u8,8u8,9u8,1u8,8u8,9u8, - 1u8,11u8,13u8,1u8,9u8,0u8,1u8,8u8,14u8,1u8,11u8,11u8,1u8,9u8,0u8,5u8,116u8,111u8,107u8,101u8, - 110u8,17u8,116u8,111u8,107u8,101u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,115u8,116u8,111u8,114u8,101u8,7u8, - 97u8,99u8,99u8,111u8,117u8,110u8,116u8,3u8,97u8,110u8,121u8,5u8,101u8,118u8,101u8,110u8,116u8,6u8,111u8,112u8, - 116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,12u8,112u8, - 114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,109u8,97u8,112u8,32u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8, - 111u8,110u8,68u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,77u8,117u8,116u8,97u8,116u8,101u8,69u8, - 118u8,101u8,110u8,116u8,27u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,77u8,97u8,120u8,105u8,117u8, - 109u8,77u8,117u8,116u8,97u8,116u8,101u8,69u8,118u8,101u8,110u8,116u8,24u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8, - 105u8,111u8,110u8,85u8,114u8,105u8,77u8,117u8,116u8,97u8,116u8,101u8,69u8,118u8,101u8,110u8,116u8,26u8,68u8,101u8, - 102u8,97u8,117u8,108u8,116u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,77u8,117u8,116u8,97u8,116u8,101u8,69u8, - 118u8,101u8,110u8,116u8,22u8,68u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,77u8,117u8,116u8,97u8, - 116u8,101u8,69u8,118u8,101u8,110u8,116u8,17u8,77u8,97u8,120u8,105u8,117u8,109u8,77u8,117u8,116u8,97u8,116u8,101u8, - 69u8,118u8,101u8,110u8,116u8,18u8,79u8,112u8,116u8,73u8,110u8,84u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,69u8, - 118u8,101u8,110u8,116u8,18u8,82u8,111u8,121u8,97u8,108u8,116u8,121u8,77u8,117u8,116u8,97u8,116u8,101u8,69u8,118u8, - 101u8,110u8,116u8,17u8,84u8,111u8,107u8,101u8,110u8,69u8,118u8,101u8,110u8,116u8,83u8,116u8,111u8,114u8,101u8,86u8, - 49u8,16u8,85u8,114u8,105u8,77u8,117u8,116u8,97u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,6u8,83u8, - 116u8,114u8,105u8,110u8,103u8,40u8,101u8,109u8,105u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8, - 110u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8, - 95u8,101u8,118u8,101u8,110u8,116u8,36u8,101u8,109u8,105u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8, - 111u8,110u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8, - 101u8,110u8,116u8,32u8,101u8,109u8,105u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8, - 117u8,114u8,105u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,6u8,79u8,112u8,116u8, - 105u8,111u8,110u8,13u8,80u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,86u8,97u8,108u8,117u8,101u8,34u8,101u8,109u8, - 105u8,116u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8, - 109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,34u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8, - 107u8,101u8,110u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,116u8, - 101u8,95u8,101u8,118u8,101u8,110u8,116u8,31u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,109u8, - 97u8,120u8,105u8,109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,23u8, - 101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,111u8,112u8,116u8,95u8,105u8,110u8,95u8,101u8,118u8, - 101u8,110u8,116u8,31u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,114u8,111u8,121u8,97u8,108u8, - 116u8,121u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,27u8,101u8,109u8,105u8,116u8, - 95u8,116u8,111u8,107u8,101u8,110u8,95u8,117u8,114u8,105u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8, - 101u8,110u8,116u8,28u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8, - 95u8,101u8,118u8,101u8,110u8,116u8,95u8,115u8,116u8,111u8,114u8,101u8,12u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8, - 95u8,97u8,100u8,100u8,114u8,15u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,110u8,97u8,109u8, - 101u8,15u8,111u8,108u8,100u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,15u8,110u8,101u8, - 119u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,11u8,111u8,108u8,100u8,95u8,109u8,97u8, - 120u8,105u8,109u8,117u8,109u8,11u8,110u8,101u8,119u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,7u8,111u8,108u8, - 100u8,95u8,117u8,114u8,105u8,7u8,110u8,101u8,119u8,95u8,117u8,114u8,105u8,7u8,99u8,114u8,101u8,97u8,116u8,111u8, - 114u8,10u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,4u8,107u8,101u8,121u8,115u8,10u8,111u8,108u8, - 100u8,95u8,118u8,97u8,108u8,117u8,101u8,115u8,10u8,110u8,101u8,119u8,95u8,118u8,97u8,108u8,117u8,101u8,115u8,6u8, - 111u8,112u8,116u8,95u8,105u8,110u8,21u8,111u8,108u8,100u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,110u8, - 117u8,109u8,101u8,114u8,97u8,116u8,111u8,114u8,23u8,111u8,108u8,100u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8, - 95u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,22u8,111u8,108u8,100u8,95u8,114u8,111u8,121u8, - 97u8,108u8,116u8,121u8,95u8,112u8,97u8,121u8,101u8,101u8,95u8,97u8,100u8,100u8,114u8,21u8,110u8,101u8,119u8,95u8, - 114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,110u8,117u8,109u8,101u8,114u8,97u8,116u8,111u8,114u8,23u8,110u8,101u8, - 119u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8, - 114u8,22u8,110u8,101u8,119u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,112u8,97u8,121u8,101u8,101u8,95u8, - 97u8,100u8,100u8,114u8,28u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,117u8,114u8,105u8,95u8, - 109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8, - 97u8,110u8,100u8,108u8,101u8,32u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,97u8,120u8, - 105u8,109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,36u8,99u8, - 111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8, - 110u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,13u8,111u8,112u8,116u8,95u8, - 105u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,17u8,117u8,114u8,105u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8, - 95u8,101u8,118u8,101u8,110u8,116u8,115u8,30u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,95u8,112u8,114u8,111u8,112u8, - 101u8,114u8,116u8,121u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,25u8,100u8, - 101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8, - 101u8,110u8,116u8,115u8,21u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8, - 101u8,118u8,101u8,110u8,116u8,115u8,21u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8, - 101u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,9u8,101u8,120u8,116u8,101u8,110u8,115u8,105u8,111u8,110u8,3u8,65u8, - 110u8,121u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8, - 118u8,101u8,110u8,116u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8, - 101u8,4u8,110u8,111u8,110u8,101u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,0u8,2u8,4u8,32u8,5u8,33u8,8u8,10u8,34u8,8u8, - 10u8,35u8,8u8,10u8,1u8,2u8,4u8,32u8,5u8,33u8,8u8,10u8,36u8,3u8,37u8,3u8,2u8,2u8,4u8,32u8, - 5u8,33u8,8u8,10u8,38u8,8u8,10u8,39u8,8u8,10u8,3u8,2u8,6u8,40u8,5u8,41u8,8u8,10u8,0u8,8u8, - 10u8,42u8,10u8,8u8,10u8,43u8,10u8,11u8,11u8,1u8,8u8,12u8,44u8,10u8,8u8,12u8,4u8,2u8,5u8,40u8, - 5u8,41u8,8u8,10u8,0u8,8u8,10u8,34u8,8u8,10u8,35u8,8u8,10u8,5u8,2u8,5u8,40u8,5u8,41u8,8u8, - 10u8,0u8,8u8,10u8,36u8,3u8,37u8,3u8,6u8,2u8,1u8,45u8,1u8,7u8,2u8,9u8,40u8,5u8,41u8,8u8, - 10u8,0u8,8u8,10u8,46u8,3u8,47u8,3u8,48u8,5u8,49u8,3u8,50u8,3u8,51u8,5u8,8u8,2u8,10u8,52u8, - 11u8,13u8,1u8,8u8,2u8,54u8,11u8,13u8,1u8,8u8,1u8,55u8,11u8,13u8,1u8,8u8,0u8,56u8,11u8,13u8, - 1u8,8u8,6u8,57u8,11u8,13u8,1u8,8u8,9u8,58u8,11u8,13u8,1u8,8u8,3u8,59u8,11u8,13u8,1u8,8u8, - 4u8,60u8,11u8,13u8,1u8,8u8,7u8,61u8,11u8,13u8,1u8,8u8,5u8,62u8,11u8,11u8,1u8,8u8,14u8,9u8, - 2u8,5u8,40u8,5u8,41u8,8u8,10u8,0u8,8u8,10u8,38u8,8u8,10u8,39u8,8u8,10u8,0u8,3u8,0u8,1u8, - 8u8,9u8,16u8,10u8,0u8,17u8,10u8,11u8,1u8,11u8,2u8,11u8,3u8,18u8,0u8,12u8,4u8,10u8,0u8,17u8, - 9u8,11u8,0u8,17u8,10u8,42u8,8u8,15u8,0u8,11u8,4u8,56u8,0u8,2u8,1u8,3u8,0u8,1u8,8u8,12u8, - 16u8,10u8,0u8,17u8,10u8,11u8,1u8,11u8,2u8,11u8,3u8,18u8,1u8,12u8,4u8,10u8,0u8,17u8,9u8,11u8, - 0u8,17u8,10u8,42u8,8u8,15u8,1u8,11u8,4u8,56u8,1u8,2u8,2u8,3u8,0u8,1u8,8u8,13u8,16u8,10u8, - 0u8,17u8,10u8,11u8,1u8,11u8,2u8,11u8,3u8,18u8,2u8,12u8,4u8,10u8,0u8,17u8,9u8,11u8,0u8,17u8, - 10u8,42u8,8u8,15u8,2u8,11u8,4u8,56u8,2u8,2u8,3u8,3u8,0u8,1u8,8u8,14u8,19u8,10u8,0u8,17u8, - 10u8,12u8,6u8,10u8,6u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,18u8,3u8,12u8,7u8,11u8, - 0u8,17u8,9u8,11u8,6u8,42u8,8u8,15u8,3u8,11u8,7u8,56u8,3u8,2u8,4u8,3u8,0u8,1u8,8u8,16u8, - 18u8,10u8,0u8,17u8,10u8,12u8,5u8,10u8,5u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,18u8,4u8,12u8, - 6u8,11u8,0u8,17u8,9u8,11u8,5u8,42u8,8u8,15u8,4u8,11u8,6u8,56u8,4u8,2u8,5u8,3u8,0u8,1u8, - 8u8,18u8,18u8,10u8,0u8,17u8,10u8,12u8,5u8,10u8,5u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,18u8, - 5u8,12u8,6u8,11u8,0u8,17u8,9u8,11u8,5u8,42u8,8u8,15u8,5u8,11u8,6u8,56u8,5u8,2u8,6u8,3u8, - 0u8,1u8,8u8,20u8,12u8,11u8,1u8,18u8,6u8,12u8,2u8,10u8,0u8,17u8,9u8,11u8,0u8,17u8,10u8,42u8, - 8u8,15u8,6u8,11u8,2u8,56u8,6u8,2u8,7u8,3u8,0u8,1u8,8u8,21u8,22u8,10u8,0u8,17u8,10u8,12u8, - 9u8,10u8,9u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,11u8,6u8,11u8,7u8,11u8,8u8,18u8, - 7u8,12u8,10u8,11u8,0u8,17u8,9u8,11u8,9u8,42u8,8u8,15u8,7u8,11u8,10u8,56u8,7u8,2u8,8u8,3u8, - 0u8,1u8,8u8,23u8,18u8,10u8,0u8,17u8,10u8,12u8,5u8,10u8,5u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8, - 4u8,18u8,9u8,12u8,6u8,11u8,0u8,17u8,9u8,11u8,5u8,42u8,8u8,15u8,8u8,11u8,6u8,56u8,8u8,2u8, - 9u8,0u8,0u8,0u8,1u8,31u8,10u8,0u8,17u8,10u8,41u8,8u8,32u8,4u8,28u8,10u8,0u8,10u8,0u8,56u8, - 9u8,10u8,0u8,56u8,10u8,10u8,0u8,56u8,11u8,10u8,0u8,56u8,12u8,10u8,0u8,56u8,13u8,10u8,0u8,56u8, - 14u8,10u8,0u8,56u8,15u8,10u8,0u8,56u8,16u8,11u8,0u8,56u8,17u8,56u8,18u8,18u8,8u8,45u8,8u8,5u8, - 30u8,11u8,0u8,1u8,2u8,8u8,2u8,8u8,1u8,8u8,0u8,8u8,5u8,8u8,6u8,8u8,8u8,8u8,3u8,8u8, - 7u8,8u8,4u8,0u8,0u8,0u8, - ]; - vector::push_back(&mut code, chunk1); - let chunk2 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,22u8,2u8,22u8,106u8,3u8,128u8,1u8,171u8, - 5u8,4u8,171u8,6u8,78u8,5u8,249u8,6u8,231u8,7u8,7u8,224u8,14u8,172u8,27u8,8u8,140u8,42u8,64u8,6u8, - 204u8,42u8,217u8,3u8,16u8,165u8,46u8,201u8,20u8,10u8,238u8,66u8,208u8,2u8,12u8,190u8,69u8,201u8,46u8,13u8, - 135u8,116u8,96u8,0u8,0u8,1u8,1u8,1u8,2u8,1u8,3u8,1u8,4u8,1u8,5u8,1u8,6u8,1u8,7u8,1u8, - 8u8,0u8,9u8,0u8,10u8,0u8,11u8,6u8,0u8,0u8,12u8,4u8,0u8,0u8,13u8,7u8,0u8,0u8,14u8,8u8, - 0u8,0u8,15u8,6u8,0u8,0u8,16u8,6u8,0u8,0u8,17u8,6u8,0u8,0u8,18u8,6u8,0u8,0u8,19u8,6u8, - 0u8,0u8,20u8,7u8,0u8,0u8,21u8,4u8,0u8,0u8,22u8,4u8,0u8,0u8,23u8,7u8,0u8,0u8,24u8,7u8, - 0u8,0u8,25u8,7u8,0u8,0u8,26u8,8u8,0u8,0u8,27u8,6u8,0u8,0u8,28u8,6u8,0u8,6u8,29u8,7u8, - 0u8,4u8,62u8,7u8,1u8,0u8,0u8,9u8,66u8,7u8,0u8,7u8,126u8,4u8,2u8,3u8,1u8,0u8,1u8,3u8, - 129u8,1u8,4u8,1u8,6u8,1u8,9u8,188u8,1u8,7u8,0u8,0u8,30u8,0u8,1u8,0u8,0u8,31u8,2u8,1u8, - 0u8,0u8,32u8,3u8,1u8,0u8,0u8,33u8,4u8,5u8,0u8,0u8,34u8,6u8,1u8,0u8,0u8,35u8,6u8,1u8, - 0u8,0u8,36u8,0u8,7u8,0u8,0u8,37u8,8u8,7u8,0u8,0u8,38u8,9u8,1u8,0u8,0u8,39u8,10u8,11u8, - 0u8,0u8,40u8,9u8,1u8,0u8,0u8,41u8,12u8,13u8,0u8,0u8,42u8,8u8,14u8,0u8,0u8,43u8,15u8,16u8, - 0u8,0u8,44u8,17u8,16u8,0u8,0u8,45u8,10u8,18u8,0u8,0u8,46u8,19u8,1u8,0u8,0u8,47u8,20u8,14u8, - 0u8,0u8,48u8,21u8,22u8,0u8,0u8,49u8,23u8,1u8,0u8,0u8,50u8,24u8,1u8,0u8,0u8,51u8,25u8,1u8, - 0u8,0u8,52u8,26u8,1u8,0u8,0u8,53u8,26u8,1u8,0u8,0u8,54u8,27u8,1u8,0u8,0u8,55u8,28u8,1u8, - 0u8,0u8,56u8,0u8,29u8,0u8,0u8,57u8,0u8,5u8,0u8,0u8,58u8,0u8,11u8,0u8,0u8,59u8,30u8,7u8, - 0u8,0u8,60u8,30u8,7u8,0u8,0u8,61u8,30u8,7u8,0u8,0u8,63u8,0u8,31u8,0u8,0u8,64u8,0u8,29u8, - 0u8,0u8,65u8,32u8,7u8,0u8,0u8,67u8,4u8,33u8,0u8,0u8,68u8,16u8,13u8,0u8,0u8,69u8,34u8,5u8, - 0u8,0u8,70u8,34u8,5u8,0u8,0u8,71u8,34u8,32u8,0u8,0u8,72u8,35u8,5u8,0u8,0u8,73u8,36u8,8u8, - 0u8,0u8,74u8,35u8,16u8,0u8,0u8,75u8,37u8,17u8,0u8,0u8,76u8,38u8,7u8,0u8,0u8,77u8,38u8,7u8, - 0u8,0u8,78u8,38u8,7u8,0u8,0u8,79u8,38u8,7u8,0u8,0u8,80u8,38u8,7u8,0u8,0u8,81u8,39u8,31u8, - 0u8,0u8,82u8,14u8,29u8,0u8,0u8,83u8,16u8,14u8,0u8,0u8,84u8,39u8,5u8,0u8,0u8,85u8,14u8,5u8, - 0u8,0u8,86u8,14u8,18u8,0u8,0u8,87u8,14u8,13u8,0u8,0u8,88u8,39u8,29u8,0u8,0u8,89u8,32u8,7u8, - 0u8,0u8,90u8,40u8,1u8,0u8,0u8,91u8,41u8,1u8,0u8,0u8,92u8,41u8,1u8,0u8,0u8,93u8,42u8,1u8, - 0u8,0u8,94u8,43u8,1u8,0u8,0u8,95u8,44u8,16u8,0u8,0u8,96u8,45u8,1u8,0u8,0u8,97u8,46u8,1u8, - 0u8,0u8,98u8,47u8,1u8,0u8,0u8,99u8,46u8,1u8,0u8,0u8,100u8,48u8,16u8,0u8,0u8,101u8,49u8,1u8, - 0u8,0u8,102u8,50u8,1u8,0u8,0u8,103u8,44u8,1u8,0u8,0u8,104u8,51u8,1u8,0u8,0u8,105u8,52u8,1u8, - 0u8,0u8,106u8,50u8,1u8,0u8,0u8,107u8,53u8,1u8,0u8,0u8,108u8,54u8,55u8,0u8,0u8,109u8,56u8,57u8, - 0u8,0u8,110u8,35u8,37u8,0u8,0u8,111u8,58u8,1u8,0u8,0u8,112u8,59u8,1u8,0u8,0u8,113u8,60u8,1u8, - 0u8,0u8,114u8,61u8,57u8,0u8,0u8,115u8,22u8,57u8,0u8,0u8,116u8,62u8,57u8,0u8,2u8,161u8,1u8,5u8, - 5u8,0u8,7u8,162u8,1u8,64u8,7u8,2u8,3u8,0u8,6u8,163u8,1u8,66u8,5u8,0u8,6u8,164u8,1u8,67u8, - 29u8,0u8,6u8,165u8,1u8,68u8,29u8,0u8,2u8,166u8,1u8,5u8,5u8,0u8,5u8,167u8,1u8,41u8,32u8,0u8, - 7u8,168u8,1u8,64u8,73u8,2u8,3u8,0u8,2u8,169u8,1u8,5u8,5u8,0u8,7u8,170u8,1u8,75u8,76u8,2u8, - 3u8,0u8,9u8,171u8,1u8,77u8,7u8,0u8,9u8,172u8,1u8,77u8,7u8,0u8,3u8,173u8,1u8,79u8,1u8,1u8, - 6u8,7u8,174u8,1u8,75u8,80u8,2u8,3u8,0u8,7u8,175u8,1u8,1u8,84u8,2u8,3u8,4u8,1u8,176u8,1u8, - 41u8,86u8,1u8,6u8,2u8,177u8,1u8,5u8,5u8,0u8,7u8,178u8,1u8,89u8,1u8,2u8,3u8,0u8,1u8,179u8, - 1u8,32u8,7u8,0u8,9u8,175u8,1u8,92u8,33u8,0u8,4u8,180u8,1u8,97u8,98u8,1u8,0u8,4u8,181u8,1u8, - 1u8,98u8,1u8,0u8,9u8,182u8,1u8,1u8,33u8,0u8,10u8,183u8,1u8,107u8,1u8,0u8,10u8,184u8,1u8,109u8, - 1u8,0u8,10u8,185u8,1u8,107u8,1u8,0u8,10u8,186u8,1u8,113u8,1u8,0u8,10u8,187u8,1u8,115u8,1u8,0u8, - 2u8,189u8,1u8,5u8,5u8,0u8,9u8,168u8,1u8,77u8,119u8,0u8,9u8,190u8,1u8,120u8,118u8,0u8,4u8,191u8, - 1u8,121u8,7u8,1u8,0u8,9u8,192u8,1u8,122u8,1u8,0u8,9u8,178u8,1u8,123u8,1u8,0u8,10u8,193u8,1u8, - 124u8,1u8,0u8,10u8,194u8,1u8,125u8,1u8,0u8,10u8,195u8,1u8,113u8,1u8,0u8,10u8,196u8,1u8,53u8,1u8, - 0u8,8u8,197u8,1u8,1u8,5u8,0u8,9u8,198u8,1u8,129u8,1u8,1u8,0u8,86u8,63u8,86u8,70u8,86u8,72u8, - 92u8,72u8,94u8,70u8,97u8,78u8,98u8,70u8,94u8,63u8,98u8,63u8,99u8,63u8,99u8,70u8,100u8,85u8,100u8,87u8, - 100u8,88u8,102u8,63u8,97u8,85u8,102u8,70u8,97u8,87u8,97u8,94u8,102u8,72u8,94u8,72u8,92u8,63u8,105u8,5u8, - 106u8,5u8,92u8,70u8,99u8,72u8,100u8,94u8,100u8,103u8,100u8,78u8,100u8,104u8,97u8,88u8,97u8,104u8,105u8,118u8, - 106u8,118u8,116u8,118u8,106u8,22u8,105u8,22u8,97u8,103u8,98u8,72u8,2u8,5u8,8u8,18u8,0u8,1u8,6u8,10u8, - 8u8,18u8,2u8,6u8,12u8,8u8,12u8,2u8,5u8,8u8,13u8,1u8,3u8,6u8,6u8,12u8,5u8,8u8,18u8,8u8, - 18u8,3u8,3u8,1u8,1u8,3u8,5u8,8u8,18u8,8u8,18u8,6u8,6u8,12u8,8u8,18u8,8u8,18u8,8u8,18u8, - 3u8,10u8,1u8,1u8,6u8,10u8,1u8,1u8,8u8,2u8,3u8,3u8,3u8,5u8,1u8,8u8,9u8,1u8,8u8,12u8, - 2u8,8u8,12u8,3u8,1u8,8u8,13u8,4u8,5u8,8u8,18u8,8u8,18u8,3u8,1u8,8u8,14u8,14u8,6u8,12u8, - 8u8,18u8,8u8,18u8,8u8,18u8,3u8,3u8,8u8,18u8,5u8,3u8,3u8,10u8,1u8,10u8,8u8,18u8,10u8,10u8, - 2u8,10u8,8u8,18u8,13u8,6u8,12u8,8u8,18u8,8u8,18u8,8u8,18u8,3u8,8u8,18u8,5u8,3u8,3u8,8u8, - 14u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8,18u8,4u8,6u8,12u8,8u8,13u8,3u8,3u8,1u8,8u8,16u8, - 2u8,6u8,12u8,8u8,10u8,1u8,8u8,1u8,1u8,8u8,11u8,2u8,5u8,8u8,10u8,4u8,6u8,12u8,6u8,12u8, - 8u8,13u8,3u8,7u8,6u8,12u8,6u8,12u8,5u8,8u8,18u8,8u8,18u8,3u8,3u8,1u8,8u8,18u8,1u8,6u8, - 8u8,2u8,1u8,11u8,19u8,1u8,3u8,1u8,5u8,1u8,8u8,20u8,1u8,6u8,8u8,9u8,1u8,6u8,8u8,10u8, - 1u8,6u8,8u8,12u8,1u8,6u8,8u8,13u8,1u8,6u8,8u8,14u8,2u8,5u8,8u8,12u8,2u8,6u8,12u8,8u8, - 13u8,1u8,6u8,12u8,2u8,7u8,8u8,10u8,8u8,10u8,5u8,6u8,12u8,5u8,8u8,18u8,8u8,18u8,3u8,3u8, - 6u8,12u8,8u8,12u8,3u8,4u8,6u8,12u8,5u8,8u8,12u8,3u8,3u8,6u8,12u8,8u8,18u8,8u8,18u8,3u8, - 6u8,12u8,8u8,18u8,3u8,6u8,6u8,12u8,5u8,8u8,13u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8,18u8, - 10u8,6u8,12u8,5u8,5u8,8u8,18u8,8u8,18u8,3u8,3u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8,18u8, - 3u8,6u8,12u8,8u8,12u8,8u8,18u8,5u8,6u8,12u8,8u8,12u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8, - 18u8,3u8,6u8,12u8,8u8,12u8,8u8,9u8,2u8,6u8,12u8,1u8,2u8,8u8,16u8,3u8,2u8,8u8,10u8,11u8, - 19u8,1u8,8u8,16u8,2u8,7u8,8u8,10u8,3u8,1u8,8u8,10u8,4u8,6u8,12u8,8u8,13u8,5u8,3u8,7u8, - 6u8,12u8,5u8,8u8,18u8,8u8,18u8,3u8,5u8,3u8,5u8,5u8,8u8,13u8,10u8,8u8,18u8,10u8,10u8,2u8, - 10u8,8u8,18u8,3u8,6u8,12u8,8u8,13u8,3u8,3u8,5u8,8u8,13u8,3u8,2u8,8u8,18u8,8u8,1u8,2u8, - 6u8,11u8,21u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,4u8,8u8,18u8,3u8,6u8,8u8,18u8,3u8,1u8,6u8, - 8u8,18u8,3u8,6u8,8u8,18u8,3u8,3u8,1u8,10u8,2u8,2u8,8u8,12u8,5u8,2u8,8u8,12u8,8u8,11u8, - 2u8,3u8,6u8,8u8,15u8,2u8,8u8,13u8,8u8,10u8,1u8,6u8,9u8,1u8,11u8,8u8,18u8,6u8,8u8,20u8, - 8u8,18u8,6u8,8u8,20u8,3u8,7u8,8u8,1u8,7u8,8u8,3u8,5u8,7u8,8u8,11u8,7u8,8u8,11u8,8u8, - 13u8,2u8,7u8,11u8,21u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,7u8,9u8,1u8,2u8,6u8,8u8,20u8, - 6u8,8u8,18u8,1u8,8u8,0u8,2u8,7u8,11u8,22u8,1u8,9u8,0u8,9u8,0u8,1u8,9u8,1u8,10u8,8u8, - 18u8,6u8,8u8,20u8,8u8,18u8,6u8,8u8,20u8,3u8,7u8,8u8,1u8,7u8,8u8,3u8,5u8,7u8,8u8,11u8, - 8u8,13u8,2u8,6u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,8u8,12u8,5u8,8u8,18u8,5u8,8u8,1u8,7u8, - 11u8,21u8,2u8,8u8,18u8,8u8,1u8,8u8,2u8,1u8,11u8,21u8,2u8,9u8,0u8,9u8,1u8,1u8,8u8,4u8, - 1u8,11u8,22u8,1u8,9u8,0u8,1u8,8u8,5u8,1u8,8u8,7u8,3u8,7u8,11u8,21u8,2u8,9u8,0u8,9u8, - 1u8,9u8,0u8,9u8,1u8,2u8,8u8,14u8,8u8,12u8,5u8,5u8,7u8,8u8,1u8,7u8,8u8,3u8,8u8,11u8, - 8u8,12u8,3u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8,18u8,1u8,7u8,8u8,15u8,1u8,8u8,6u8,1u8, - 6u8,11u8,21u8,2u8,8u8,18u8,8u8,1u8,2u8,11u8,19u8,1u8,3u8,7u8,8u8,1u8,1u8,9u8,0u8,1u8, - 11u8,19u8,1u8,9u8,0u8,2u8,8u8,20u8,6u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,3u8,11u8,19u8,1u8, - 3u8,6u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,6u8,8u8,11u8,2u8,6u8,11u8,21u8,2u8,8u8,12u8,8u8, - 11u8,5u8,1u8,6u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,1u8,8u8,17u8,1u8,8u8,8u8,5u8,8u8,12u8, - 7u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,5u8,7u8,8u8,11u8,8u8,13u8,2u8,7u8,8u8,1u8,5u8,4u8, - 6u8,12u8,8u8,18u8,8u8,18u8,8u8,18u8,3u8,1u8,7u8,8u8,1u8,5u8,4u8,6u8,12u8,8u8,18u8,3u8, - 3u8,13u8,8u8,12u8,8u8,18u8,6u8,8u8,20u8,8u8,18u8,6u8,8u8,20u8,8u8,13u8,7u8,11u8,21u8,2u8, - 8u8,12u8,8u8,11u8,5u8,3u8,8u8,10u8,8u8,13u8,8u8,10u8,7u8,8u8,11u8,2u8,3u8,8u8,13u8,1u8, - 7u8,8u8,11u8,5u8,6u8,12u8,8u8,18u8,8u8,18u8,8u8,18u8,8u8,18u8,2u8,1u8,7u8,8u8,11u8,5u8, - 6u8,12u8,8u8,18u8,8u8,18u8,3u8,3u8,11u8,11u8,19u8,1u8,8u8,23u8,3u8,6u8,8u8,18u8,3u8,8u8, - 23u8,10u8,8u8,23u8,11u8,19u8,1u8,8u8,23u8,10u8,11u8,19u8,1u8,8u8,23u8,7u8,8u8,11u8,3u8,3u8, - 1u8,11u8,19u8,1u8,8u8,23u8,1u8,8u8,23u8,1u8,6u8,8u8,23u8,2u8,10u8,2u8,8u8,18u8,1u8,6u8, - 11u8,19u8,1u8,9u8,0u8,3u8,7u8,8u8,20u8,6u8,8u8,18u8,8u8,23u8,3u8,7u8,8u8,20u8,8u8,18u8, - 8u8,23u8,6u8,6u8,12u8,8u8,18u8,8u8,18u8,10u8,8u8,18u8,10u8,11u8,19u8,1u8,8u8,23u8,10u8,8u8, - 23u8,9u8,6u8,12u8,8u8,18u8,8u8,18u8,3u8,3u8,5u8,3u8,3u8,5u8,2u8,5u8,7u8,1u8,2u8,11u8, - 19u8,1u8,8u8,16u8,11u8,19u8,1u8,8u8,16u8,3u8,8u8,13u8,7u8,11u8,21u8,2u8,8u8,13u8,8u8,10u8, - 7u8,8u8,20u8,4u8,7u8,8u8,20u8,10u8,8u8,18u8,10u8,10u8,2u8,10u8,8u8,18u8,4u8,8u8,13u8,8u8, - 10u8,7u8,3u8,7u8,11u8,21u8,2u8,8u8,13u8,8u8,10u8,5u8,116u8,111u8,107u8,101u8,110u8,7u8,97u8,99u8, - 99u8,111u8,117u8,110u8,116u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8,116u8,6u8,111u8,112u8, - 116u8,105u8,111u8,110u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,5u8,116u8, - 97u8,98u8,108u8,101u8,9u8,116u8,105u8,109u8,101u8,115u8,116u8,97u8,109u8,112u8,12u8,112u8,114u8,111u8,112u8,101u8, - 114u8,116u8,121u8,95u8,109u8,97u8,112u8,17u8,116u8,111u8,107u8,101u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8, - 115u8,116u8,111u8,114u8,101u8,14u8,66u8,117u8,114u8,110u8,84u8,111u8,107u8,101u8,110u8,69u8,118u8,101u8,110u8,116u8, - 14u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,68u8,97u8,116u8,97u8,26u8,67u8,111u8,108u8,108u8, - 101u8,99u8,116u8,105u8,111u8,110u8,77u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,67u8,111u8,110u8,102u8, - 105u8,103u8,11u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,115u8,21u8,67u8,114u8,101u8,97u8,116u8, - 101u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,69u8,118u8,101u8,110u8,116u8,20u8,67u8,114u8,101u8, - 97u8,116u8,101u8,84u8,111u8,107u8,101u8,110u8,68u8,97u8,116u8,97u8,69u8,118u8,101u8,110u8,116u8,12u8,68u8,101u8, - 112u8,111u8,115u8,105u8,116u8,69u8,118u8,101u8,110u8,116u8,14u8,77u8,105u8,110u8,116u8,84u8,111u8,107u8,101u8,110u8, - 69u8,118u8,101u8,110u8,116u8,27u8,77u8,117u8,116u8,97u8,116u8,101u8,84u8,111u8,107u8,101u8,110u8,80u8,114u8,111u8, - 112u8,101u8,114u8,116u8,121u8,77u8,97u8,112u8,69u8,118u8,101u8,110u8,116u8,7u8,82u8,111u8,121u8,97u8,108u8,116u8, - 121u8,5u8,84u8,111u8,107u8,101u8,110u8,9u8,84u8,111u8,107u8,101u8,110u8,68u8,97u8,116u8,97u8,11u8,84u8,111u8, - 107u8,101u8,110u8,68u8,97u8,116u8,97u8,73u8,100u8,7u8,84u8,111u8,107u8,101u8,110u8,73u8,100u8,21u8,84u8,111u8, - 107u8,101u8,110u8,77u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,67u8,111u8,110u8,102u8,105u8,103u8,10u8, - 84u8,111u8,107u8,101u8,110u8,83u8,116u8,111u8,114u8,101u8,18u8,87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,67u8, - 97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,13u8,87u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,69u8,118u8, - 101u8,110u8,116u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,24u8,97u8,115u8,115u8,101u8,114u8,116u8,95u8,99u8,111u8, - 108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,37u8,97u8,115u8,115u8,101u8, - 114u8,116u8,95u8,110u8,111u8,110u8,95u8,115u8,116u8,97u8,110u8,100u8,97u8,114u8,100u8,95u8,114u8,101u8,115u8,101u8, - 114u8,118u8,101u8,100u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,23u8,97u8,115u8,115u8,101u8,114u8,116u8, - 95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,10u8,98u8,97u8, - 108u8,97u8,110u8,99u8,101u8,95u8,111u8,102u8,4u8,98u8,117u8,114u8,110u8,15u8,98u8,117u8,114u8,110u8,95u8,98u8, - 121u8,95u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8,23u8,99u8,104u8,101u8,99u8,107u8,95u8,99u8,111u8,108u8,108u8, - 101u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,22u8,99u8,104u8,101u8,99u8,107u8,95u8, - 116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,17u8,99u8,114u8,101u8, - 97u8,116u8,101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,35u8,99u8,114u8,101u8,97u8,116u8, - 101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8, - 105u8,116u8,121u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,99u8,111u8, - 108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,14u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,20u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8, - 107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,95u8,105u8,100u8,15u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8, - 111u8,107u8,101u8,110u8,95u8,105u8,100u8,19u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8, - 95u8,105u8,100u8,95u8,114u8,97u8,119u8,30u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8, - 95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,19u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,16u8,99u8, - 114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,26u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8, - 116u8,121u8,13u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,23u8,100u8,101u8,115u8, - 116u8,114u8,111u8,121u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,100u8,97u8,116u8,97u8, - 18u8,100u8,101u8,115u8,116u8,114u8,111u8,121u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,14u8, - 100u8,105u8,114u8,101u8,99u8,116u8,95u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,26u8,100u8,105u8,114u8,101u8,99u8, - 116u8,95u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,119u8,105u8,116u8,104u8,95u8,111u8,112u8,116u8,95u8,105u8, - 110u8,15u8,100u8,105u8,114u8,101u8,99u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,22u8,100u8,105u8, - 114u8,101u8,99u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8, - 26u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,100u8,101u8,115u8,99u8, - 114u8,105u8,112u8,116u8,105u8,111u8,110u8,22u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8, - 111u8,110u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,32u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8, - 99u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,99u8,111u8,110u8, - 102u8,105u8,103u8,37u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8, - 117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8, - 110u8,33u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8, - 97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,29u8,103u8,101u8,116u8,95u8, - 99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8, - 121u8,95u8,117u8,114u8,105u8,6u8,79u8,112u8,116u8,105u8,111u8,110u8,21u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8, - 108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,115u8,117u8,112u8,112u8,108u8,121u8,18u8,103u8,101u8,116u8,95u8,99u8, - 111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,117u8,114u8,105u8,19u8,103u8,101u8,116u8,95u8,100u8,105u8, - 114u8,101u8,99u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,11u8,80u8,114u8,111u8,112u8,101u8,114u8, - 116u8,121u8,77u8,97u8,112u8,16u8,103u8,101u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,109u8, - 97u8,112u8,11u8,103u8,101u8,116u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,23u8,103u8,101u8,116u8,95u8,114u8, - 111u8,121u8,97u8,108u8,116u8,121u8,95u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8,21u8,103u8, - 101u8,116u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,110u8,117u8,109u8,101u8,114u8,97u8,116u8,111u8,114u8, - 17u8,103u8,101u8,116u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,112u8,97u8,121u8,101u8,101u8,16u8,103u8, - 101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,24u8,103u8,101u8,116u8,95u8, - 116u8,111u8,107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,95u8,105u8,100u8,95u8,102u8,105u8,101u8,108u8,100u8,115u8, - 12u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,105u8,100u8,19u8,103u8,101u8,116u8,95u8,116u8,111u8, - 107u8,101u8,110u8,95u8,105u8,100u8,95u8,102u8,105u8,101u8,108u8,100u8,115u8,39u8,103u8,101u8,116u8,95u8,116u8,111u8, - 107u8,101u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,100u8,101u8,102u8,97u8,117u8, - 108u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,105u8,101u8,115u8,32u8,103u8,101u8,116u8,95u8,116u8,111u8, - 107u8,101u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,100u8,101u8,115u8,99u8,114u8, - 105u8,112u8,116u8,105u8,111u8,110u8,28u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,109u8,117u8,116u8, - 97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,28u8,103u8,101u8,116u8,95u8, - 116u8,111u8,107u8,101u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,114u8,111u8,121u8, - 97u8,108u8,116u8,121u8,24u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,109u8,117u8,116u8,97u8,98u8, - 105u8,108u8,105u8,116u8,121u8,95u8,117u8,114u8,105u8,16u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8, - 115u8,117u8,112u8,112u8,108u8,121u8,25u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8, - 95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,16u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8, - 101u8,110u8,100u8,97u8,116u8,97u8,95u8,105u8,100u8,38u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8, - 97u8,116u8,97u8,95u8,108u8,97u8,114u8,103u8,101u8,115u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8, - 95u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,21u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8, - 116u8,97u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,31u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8, - 100u8,97u8,116u8,97u8,95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,99u8,111u8,110u8,102u8, - 105u8,103u8,21u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,114u8,111u8,121u8, - 97u8,108u8,116u8,121u8,17u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,117u8, - 114u8,105u8,15u8,104u8,97u8,115u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,115u8,116u8,111u8,114u8,101u8,16u8,105u8, - 110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,23u8,105u8,110u8,105u8,116u8, - 105u8,97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,22u8, - 105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,115u8,116u8,111u8, - 114u8,101u8,5u8,109u8,101u8,114u8,103u8,101u8,11u8,109u8,105u8,110u8,116u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8, - 10u8,109u8,105u8,110u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,13u8,109u8,105u8,110u8,116u8,95u8,116u8,111u8,107u8, - 101u8,110u8,95u8,116u8,111u8,29u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8, - 105u8,111u8,110u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,25u8,109u8,117u8,116u8,97u8, - 116u8,101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8, - 109u8,21u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8, - 117u8,114u8,105u8,16u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,111u8,110u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8, - 23u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8, - 116u8,105u8,101u8,115u8,28u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8, - 97u8,95u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,24u8,109u8,117u8,116u8,97u8,116u8,101u8, - 95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,25u8,109u8, - 117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,95u8,112u8,114u8,111u8,112u8, - 101u8,114u8,116u8,121u8,24u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8, - 97u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,20u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8, - 101u8,110u8,100u8,97u8,116u8,97u8,95u8,117u8,114u8,105u8,22u8,111u8,112u8,116u8,95u8,105u8,110u8,95u8,100u8,105u8, - 114u8,101u8,99u8,116u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,32u8,112u8,97u8,114u8,116u8,105u8,97u8, - 108u8,95u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8, - 98u8,105u8,108u8,105u8,116u8,121u8,5u8,115u8,112u8,108u8,105u8,116u8,8u8,116u8,111u8,107u8,101u8,110u8,95u8,105u8, - 100u8,8u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,20u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,95u8, - 119u8,105u8,116u8,104u8,95u8,111u8,112u8,116u8,95u8,105u8,110u8,30u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,116u8, - 111u8,107u8,101u8,110u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8, - 97u8,108u8,14u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,116u8,111u8,107u8,101u8,110u8,24u8,119u8,105u8, - 116u8,104u8,100u8,114u8,97u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8, - 116u8,121u8,28u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,119u8,105u8,116u8,104u8,95u8,101u8,118u8,101u8, - 110u8,116u8,95u8,105u8,110u8,116u8,101u8,114u8,110u8,97u8,108u8,2u8,105u8,100u8,6u8,97u8,109u8,111u8,117u8,110u8, - 116u8,11u8,100u8,101u8,115u8,99u8,114u8,105u8,112u8,116u8,105u8,111u8,110u8,4u8,110u8,97u8,109u8,101u8,3u8,117u8, - 114u8,105u8,6u8,115u8,117u8,112u8,112u8,108u8,121u8,7u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8,17u8,109u8,117u8, - 116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,15u8,99u8,111u8,108u8,108u8, - 101u8,99u8,116u8,105u8,111u8,110u8,95u8,100u8,97u8,116u8,97u8,5u8,84u8,97u8,98u8,108u8,101u8,10u8,116u8,111u8, - 107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,99u8,111u8,108u8,108u8, - 101u8,99u8,116u8,105u8,111u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8, - 97u8,110u8,100u8,108u8,101u8,24u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,100u8, - 97u8,116u8,97u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,17u8,109u8,105u8,110u8,116u8,95u8,116u8,111u8,107u8,101u8, - 110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,7u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8,15u8,99u8,111u8,108u8, - 108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,110u8,97u8,109u8,101u8,21u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8, - 95u8,112u8,97u8,121u8,101u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,26u8,114u8,111u8,121u8,97u8,108u8, - 116u8,121u8,95u8,112u8,111u8,105u8,110u8,116u8,115u8,95u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8, - 114u8,24u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,112u8,111u8,105u8,110u8,116u8,115u8,95u8,110u8,117u8,109u8, - 101u8,114u8,97u8,116u8,111u8,114u8,13u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,107u8,101u8,121u8,115u8, - 15u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8,108u8,117u8,101u8,115u8,14u8,112u8,114u8,111u8, - 112u8,101u8,114u8,116u8,121u8,95u8,116u8,121u8,112u8,101u8,115u8,6u8,111u8,108u8,100u8,95u8,105u8,100u8,6u8,110u8, - 101u8,119u8,95u8,105u8,100u8,4u8,107u8,101u8,121u8,115u8,6u8,118u8,97u8,108u8,117u8,101u8,115u8,5u8,116u8,121u8, - 112u8,101u8,115u8,13u8,112u8,97u8,121u8,101u8,101u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,16u8,116u8,111u8, - 107u8,101u8,110u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,105u8,101u8,115u8,24u8,108u8,97u8,114u8,103u8,101u8, - 115u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,7u8, - 114u8,111u8,121u8,97u8,108u8,116u8,121u8,18u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,95u8,112u8,114u8,111u8,112u8, - 101u8,114u8,116u8,105u8,101u8,115u8,10u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,13u8,116u8,111u8, - 107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,95u8,105u8,100u8,16u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8, - 95u8,118u8,101u8,114u8,115u8,105u8,111u8,110u8,10u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,105u8,101u8,115u8,6u8, - 116u8,111u8,107u8,101u8,110u8,115u8,14u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,95u8,101u8,118u8,101u8,110u8,116u8, - 115u8,15u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,98u8,117u8, - 114u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,28u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8, - 101u8,110u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,116u8, - 111u8,107u8,101u8,110u8,95u8,111u8,119u8,110u8,101u8,114u8,14u8,101u8,120u8,112u8,105u8,114u8,97u8,116u8,105u8,111u8, - 110u8,95u8,115u8,101u8,99u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8,110u8,100u8,8u8,99u8,111u8,110u8,116u8, - 97u8,105u8,110u8,115u8,6u8,108u8,101u8,110u8,103u8,116u8,104u8,10u8,115u8,117u8,98u8,95u8,115u8,116u8,114u8,105u8, - 110u8,103u8,4u8,117u8,116u8,102u8,56u8,17u8,112u8,101u8,114u8,109u8,105u8,115u8,115u8,105u8,111u8,110u8,95u8,100u8, - 101u8,110u8,105u8,101u8,100u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,6u8,98u8,111u8,114u8, - 114u8,111u8,119u8,16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8, - 10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,12u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8, - 95u8,107u8,101u8,121u8,9u8,114u8,101u8,97u8,100u8,95u8,98u8,111u8,111u8,108u8,10u8,101u8,109u8,105u8,116u8,95u8, - 101u8,118u8,101u8,110u8,116u8,6u8,114u8,101u8,109u8,111u8,118u8,101u8,3u8,110u8,101u8,119u8,16u8,110u8,101u8,119u8, - 95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8,100u8,108u8,101u8,14u8,97u8,108u8,114u8,101u8,97u8,100u8, - 121u8,95u8,101u8,120u8,105u8,115u8,116u8,115u8,3u8,97u8,100u8,100u8,9u8,101u8,120u8,105u8,115u8,116u8,115u8,95u8, - 97u8,116u8,4u8,115u8,111u8,109u8,101u8,4u8,110u8,111u8,110u8,101u8,5u8,101u8,109u8,112u8,116u8,121u8,40u8,101u8, - 109u8,105u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,100u8,101u8,115u8,99u8,114u8, - 105u8,112u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,36u8, - 101u8,109u8,105u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,109u8,97u8,120u8,105u8, - 109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,32u8,101u8,109u8,105u8, - 116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,95u8,117u8,114u8,105u8,95u8,109u8,117u8,116u8, - 97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,34u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8, - 95u8,100u8,101u8,115u8,99u8,114u8,105u8,116u8,105u8,111u8,110u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8, - 118u8,101u8,110u8,116u8,31u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,109u8,97u8,120u8,105u8, - 109u8,117u8,109u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,13u8,80u8,114u8,111u8, - 112u8,101u8,114u8,116u8,121u8,86u8,97u8,108u8,117u8,101u8,13u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,115u8, - 116u8,97u8,116u8,101u8,25u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8, - 95u8,118u8,97u8,108u8,117u8,101u8,95u8,114u8,97u8,119u8,7u8,105u8,115u8,95u8,115u8,111u8,109u8,101u8,21u8,117u8, - 112u8,100u8,97u8,116u8,101u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,118u8,97u8,108u8,117u8,101u8, - 34u8,101u8,109u8,105u8,116u8,95u8,100u8,101u8,102u8,97u8,117u8,108u8,116u8,95u8,112u8,114u8,111u8,112u8,101u8,114u8, - 116u8,121u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,31u8,101u8,109u8,105u8,116u8, - 95u8,116u8,111u8,107u8,101u8,110u8,95u8,114u8,111u8,121u8,97u8,108u8,116u8,121u8,95u8,109u8,117u8,116u8,97u8,116u8, - 101u8,95u8,101u8,118u8,101u8,110u8,116u8,27u8,101u8,109u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,117u8, - 114u8,105u8,95u8,109u8,117u8,116u8,97u8,116u8,101u8,95u8,101u8,118u8,101u8,110u8,116u8,23u8,101u8,109u8,105u8,116u8, - 95u8,116u8,111u8,107u8,101u8,110u8,95u8,111u8,112u8,116u8,95u8,105u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,11u8, - 110u8,111u8,119u8,95u8,115u8,101u8,99u8,111u8,110u8,100u8,115u8,19u8,117u8,112u8,100u8,97u8,116u8,101u8,95u8,112u8, - 114u8,111u8,112u8,101u8,114u8,116u8,121u8,95u8,109u8,97u8,112u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,10u8,2u8,26u8,25u8,84u8, - 79u8,75u8,69u8,78u8,95u8,66u8,85u8,82u8,78u8,65u8,66u8,76u8,69u8,95u8,66u8,89u8,95u8,67u8,82u8,69u8, - 65u8,84u8,79u8,82u8,10u8,2u8,24u8,23u8,84u8,79u8,75u8,69u8,78u8,95u8,66u8,85u8,82u8,78u8,65u8,66u8, - 76u8,69u8,95u8,66u8,89u8,95u8,79u8,87u8,78u8,69u8,82u8,3u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,25u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,31u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,38u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,36u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,34u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,26u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,18u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,29u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,28u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,15u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,30u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,40u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,35u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,33u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,9u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,10u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,37u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,12u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,11u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,27u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,16u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,17u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,128u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,0u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,2u8,24u8,23u8,84u8,79u8,75u8,69u8,78u8, - 95u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,89u8,95u8,77u8,85u8,84u8,65u8,84u8,66u8,76u8,69u8,10u8,2u8, - 7u8,6u8,84u8,79u8,75u8,69u8,78u8,95u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8, - 100u8,97u8,116u8,97u8,95u8,118u8,49u8,180u8,20u8,36u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8, - 65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,72u8,65u8,83u8,95u8,66u8,65u8,76u8,65u8,78u8,67u8,69u8,47u8, - 84u8,104u8,101u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,104u8,97u8,115u8,32u8,98u8,97u8,108u8,97u8,110u8,99u8, - 101u8,32u8,97u8,110u8,100u8,32u8,99u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8,105u8,110u8,105u8,116u8, - 105u8,97u8,108u8,105u8,122u8,101u8,100u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,67u8,79u8,76u8, - 76u8,69u8,67u8,84u8,73u8,79u8,78u8,83u8,95u8,78u8,79u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8, - 69u8,68u8,45u8,84u8,104u8,101u8,114u8,101u8,32u8,105u8,115u8,110u8,39u8,116u8,32u8,97u8,110u8,121u8,32u8,99u8, - 111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,32u8,117u8,110u8,100u8,101u8,114u8,32u8,116u8,104u8,105u8,115u8, - 32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,67u8,79u8, - 76u8,76u8,69u8,67u8,84u8,73u8,79u8,78u8,95u8,78u8,79u8,84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8, - 69u8,68u8,43u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,102u8,105u8,110u8,100u8,32u8,99u8,111u8,108u8,108u8,101u8, - 99u8,116u8,105u8,111u8,110u8,32u8,105u8,110u8,32u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8,39u8,115u8,32u8,97u8, - 99u8,99u8,111u8,117u8,110u8,116u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,67u8,79u8,76u8,76u8, - 69u8,67u8,84u8,73u8,79u8,78u8,95u8,65u8,76u8,82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8, - 83u8,29u8,84u8,104u8,101u8,32u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,32u8,97u8,108u8,114u8, - 101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,39u8, - 69u8,67u8,82u8,69u8,65u8,84u8,69u8,95u8,87u8,79u8,85u8,76u8,68u8,95u8,69u8,88u8,67u8,69u8,69u8,68u8, - 95u8,67u8,79u8,76u8,76u8,69u8,67u8,84u8,73u8,79u8,78u8,95u8,77u8,65u8,88u8,73u8,77u8,85u8,77u8,53u8, - 69u8,120u8,99u8,101u8,101u8,100u8,115u8,32u8,116u8,104u8,101u8,32u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8, - 111u8,110u8,39u8,115u8,32u8,109u8,97u8,120u8,105u8,109u8,97u8,108u8,32u8,110u8,117u8,109u8,98u8,101u8,114u8,32u8, - 111u8,102u8,32u8,116u8,111u8,107u8,101u8,110u8,95u8,100u8,97u8,116u8,97u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,21u8,69u8,73u8,78u8,83u8,85u8,70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,66u8,65u8,76u8,65u8, - 78u8,67u8,69u8,26u8,73u8,110u8,115u8,117u8,102u8,102u8,105u8,99u8,105u8,101u8,110u8,116u8,32u8,116u8,111u8,107u8, - 101u8,110u8,32u8,98u8,97u8,108u8,97u8,110u8,99u8,101u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8, - 73u8,78u8,86u8,65u8,76u8,73u8,68u8,95u8,84u8,79u8,75u8,69u8,78u8,95u8,77u8,69u8,82u8,71u8,69u8,51u8, - 67u8,97u8,110u8,110u8,111u8,116u8,32u8,109u8,101u8,114u8,103u8,101u8,32u8,116u8,104u8,101u8,32u8,116u8,119u8,111u8, - 32u8,116u8,111u8,107u8,101u8,110u8,115u8,32u8,119u8,105u8,116u8,104u8,32u8,100u8,105u8,102u8,102u8,101u8,114u8,101u8, - 110u8,116u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,105u8,100u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,32u8, - 69u8,77u8,73u8,78u8,84u8,95u8,87u8,79u8,85u8,76u8,68u8,95u8,69u8,88u8,67u8,69u8,69u8,68u8,95u8,84u8, - 79u8,75u8,69u8,78u8,95u8,77u8,65u8,88u8,73u8,77u8,85u8,77u8,37u8,69u8,120u8,99u8,101u8,101u8,100u8,32u8, - 116u8,104u8,101u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,100u8,97u8,116u8,97u8,32u8,109u8,97u8,120u8,105u8,109u8, - 97u8,108u8,32u8,97u8,108u8,108u8,111u8,119u8,101u8,100u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,19u8,69u8, - 78u8,79u8,95u8,66u8,85u8,82u8,78u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8,89u8,18u8,78u8, - 111u8,32u8,98u8,117u8,114u8,110u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,9u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,26u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,68u8,65u8,84u8,65u8,95u8,65u8,76u8, - 82u8,69u8,65u8,68u8,89u8,95u8,69u8,88u8,73u8,83u8,84u8,83u8,24u8,84u8,111u8,107u8,101u8,110u8,68u8,97u8, - 116u8,97u8,32u8,97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,10u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,25u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,68u8,65u8,84u8,65u8,95u8,78u8,79u8, - 84u8,95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,23u8,84u8,111u8,107u8,101u8,110u8,68u8,97u8,116u8, - 97u8,32u8,110u8,111u8,116u8,32u8,112u8,117u8,98u8,108u8,105u8,115u8,104u8,101u8,100u8,11u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,26u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,83u8,84u8,79u8,82u8,69u8,95u8,78u8,79u8,84u8, - 95u8,80u8,85u8,66u8,76u8,73u8,83u8,72u8,69u8,68u8,24u8,84u8,111u8,107u8,101u8,110u8,83u8,116u8,111u8,114u8, - 101u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,12u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,51u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,83u8,80u8,76u8,73u8,84u8,95u8,65u8,77u8,79u8, - 85u8,78u8,84u8,95u8,76u8,65u8,82u8,71u8,69u8,82u8,95u8,79u8,82u8,95u8,69u8,81u8,85u8,65u8,76u8,95u8, - 84u8,79u8,95u8,84u8,79u8,75u8,69u8,78u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,54u8,67u8,97u8,110u8,110u8, - 111u8,116u8,32u8,115u8,112u8,108u8,105u8,116u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,116u8,111u8,32u8,97u8,110u8, - 32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,108u8,97u8,114u8,103u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8,32u8, - 105u8,116u8,115u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,13u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8, - 70u8,73u8,69u8,76u8,68u8,95u8,78u8,79u8,84u8,95u8,77u8,85u8,84u8,65u8,66u8,76u8,69u8,24u8,84u8,104u8, - 101u8,32u8,102u8,105u8,101u8,108u8,100u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,109u8,117u8,116u8,97u8,98u8, - 108u8,101u8,14u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,78u8,79u8,95u8,77u8,85u8,84u8,65u8,84u8, - 69u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8,89u8,24u8,78u8,111u8,116u8,32u8,97u8,117u8,116u8, - 104u8,111u8,114u8,105u8,122u8,101u8,100u8,32u8,116u8,111u8,32u8,109u8,117u8,116u8,97u8,116u8,101u8,15u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,24u8,69u8,78u8,79u8,95u8,84u8,79u8,75u8,69u8,78u8,95u8,73u8,78u8,95u8,84u8, - 79u8,75u8,69u8,78u8,95u8,83u8,84u8,79u8,82u8,69u8,28u8,84u8,111u8,107u8,101u8,110u8,32u8,110u8,111u8,116u8, - 32u8,105u8,110u8,32u8,116u8,104u8,101u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,115u8,116u8,111u8,114u8,101u8,16u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,32u8,69u8,85u8,83u8,69u8,82u8,95u8,78u8,79u8,84u8,95u8,79u8,80u8, - 84u8,95u8,73u8,78u8,95u8,68u8,73u8,82u8,69u8,67u8,84u8,95u8,84u8,82u8,65u8,78u8,83u8,70u8,69u8,82u8, - 34u8,85u8,115u8,101u8,114u8,32u8,100u8,105u8,100u8,110u8,39u8,116u8,32u8,111u8,112u8,116u8,45u8,105u8,110u8,32u8, - 100u8,105u8,114u8,101u8,99u8,116u8,32u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8,17u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,14u8,69u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8,95u8,90u8,69u8,82u8,79u8,23u8,67u8, - 97u8,110u8,110u8,111u8,116u8,32u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,32u8,48u8,32u8,116u8,111u8,107u8, - 101u8,110u8,18u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,78u8,70u8,84u8,95u8,78u8,79u8,84u8,95u8, - 83u8,80u8,76u8,73u8,84u8,65u8,66u8,76u8,69u8,43u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,115u8,112u8,108u8, - 105u8,116u8,32u8,97u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,116u8,104u8,97u8,116u8,32u8,111u8,110u8,108u8,121u8, - 32u8,104u8,97u8,115u8,32u8,49u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,19u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,19u8,69u8,78u8,79u8,95u8,77u8,73u8,78u8,84u8,95u8,67u8,65u8,80u8,65u8,66u8,73u8,76u8,73u8,84u8, - 89u8,18u8,78u8,111u8,32u8,109u8,105u8,110u8,116u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8, - 25u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,25u8,69u8,67u8,79u8,76u8,76u8,69u8,67u8,84u8,73u8,79u8,78u8, - 95u8,78u8,65u8,77u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8,31u8,84u8,104u8,101u8,32u8,99u8, - 111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,32u8,110u8,97u8,109u8,101u8,32u8,105u8,115u8,32u8,116u8,111u8, - 111u8,32u8,108u8,111u8,110u8,103u8,26u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,78u8,70u8,84u8,95u8, - 78u8,65u8,77u8,69u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8,24u8,84u8,104u8,101u8,32u8,78u8,70u8, - 84u8,32u8,110u8,97u8,109u8,101u8,32u8,105u8,115u8,32u8,116u8,111u8,111u8,32u8,108u8,111u8,110u8,103u8,27u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,13u8,69u8,85u8,82u8,73u8,95u8,84u8,79u8,79u8,95u8,76u8,79u8,78u8,71u8, - 19u8,84u8,104u8,101u8,32u8,85u8,82u8,73u8,32u8,105u8,115u8,32u8,116u8,111u8,111u8,32u8,108u8,111u8,110u8,103u8, - 28u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,69u8,78u8,79u8,95u8,68u8,69u8,80u8,79u8,83u8,73u8,84u8, - 95u8,84u8,79u8,75u8,69u8,78u8,95u8,87u8,73u8,84u8,72u8,95u8,90u8,69u8,82u8,79u8,95u8,65u8,77u8,79u8, - 85u8,78u8,84u8,36u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,100u8,101u8,112u8,111u8,115u8,105u8,116u8,32u8,97u8, - 32u8,84u8,111u8,107u8,101u8,110u8,32u8,119u8,105u8,116u8,104u8,32u8,48u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8, - 29u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,31u8,69u8,78u8,79u8,95u8,66u8,85u8,82u8,78u8,95u8,84u8,79u8, - 75u8,69u8,78u8,95u8,87u8,73u8,84u8,72u8,95u8,90u8,69u8,82u8,79u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8, - 19u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,117u8,114u8,110u8,32u8,48u8,32u8,84u8,111u8,107u8,101u8,110u8, - 30u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,24u8,69u8,79u8,87u8,78u8,69u8,82u8,95u8,67u8,65u8,78u8,78u8, - 79u8,84u8,95u8,66u8,85u8,82u8,78u8,95u8,84u8,79u8,75u8,69u8,78u8,30u8,84u8,111u8,107u8,101u8,110u8,32u8, - 105u8,115u8,32u8,110u8,111u8,116u8,32u8,98u8,117u8,114u8,110u8,97u8,98u8,108u8,101u8,32u8,98u8,121u8,32u8,111u8, - 119u8,110u8,101u8,114u8,31u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,67u8,82u8,69u8,65u8,84u8,79u8, - 82u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,66u8,85u8,82u8,78u8,95u8,84u8,79u8,75u8,69u8,78u8,32u8, - 84u8,111u8,107u8,101u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8,32u8,98u8,117u8,114u8,110u8,97u8,98u8,108u8, - 101u8,32u8,98u8,121u8,32u8,99u8,114u8,101u8,97u8,116u8,111u8,114u8,32u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 32u8,69u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,85u8,80u8,68u8,65u8,84u8,69u8,95u8,82u8,69u8,83u8,69u8, - 82u8,86u8,69u8,68u8,95u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,89u8,61u8,82u8,101u8,115u8,101u8,114u8,118u8, - 101u8,100u8,32u8,102u8,105u8,101u8,108u8,100u8,115u8,32u8,102u8,111u8,114u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8, - 99u8,111u8,110u8,116u8,114u8,97u8,99u8,116u8,10u8,32u8,67u8,97u8,110u8,110u8,111u8,116u8,32u8,98u8,101u8,32u8, - 117u8,112u8,100u8,97u8,116u8,101u8,100u8,32u8,98u8,121u8,32u8,117u8,115u8,101u8,114u8,33u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,30u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,72u8,65u8, - 86u8,69u8,95u8,90u8,69u8,82u8,79u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,34u8,84u8,79u8,75u8,69u8,78u8, - 32u8,119u8,105u8,116u8,104u8,32u8,48u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,105u8,115u8,32u8,110u8,111u8, - 116u8,32u8,97u8,108u8,108u8,111u8,119u8,101u8,100u8,34u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,38u8,69u8,73u8, - 78u8,86u8,65u8,76u8,73u8,68u8,95u8,82u8,79u8,89u8,65u8,76u8,84u8,89u8,95u8,78u8,85u8,77u8,69u8,82u8, - 65u8,84u8,79u8,82u8,95u8,68u8,69u8,78u8,79u8,77u8,73u8,78u8,65u8,84u8,79u8,82u8,63u8,82u8,111u8,121u8, - 97u8,108u8,116u8,121u8,32u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,32u8,105u8,102u8,32u8,116u8,104u8,101u8,32u8, - 110u8,117u8,109u8,101u8,114u8,97u8,116u8,111u8,114u8,32u8,105u8,115u8,32u8,108u8,97u8,114u8,103u8,101u8,114u8,32u8, - 116u8,104u8,97u8,110u8,32u8,116u8,104u8,101u8,32u8,100u8,101u8,110u8,111u8,109u8,105u8,110u8,97u8,116u8,111u8,114u8, - 35u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,37u8,69u8,82u8,79u8,89u8,65u8,76u8,84u8,89u8,95u8,80u8,65u8, - 89u8,69u8,69u8,95u8,65u8,67u8,67u8,79u8,85u8,78u8,84u8,95u8,68u8,79u8,69u8,83u8,95u8,78u8,79u8,84u8, - 95u8,69u8,88u8,73u8,83u8,84u8,36u8,82u8,111u8,121u8,97u8,108u8,116u8,121u8,32u8,112u8,97u8,121u8,101u8,101u8, - 32u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,32u8,110u8,111u8,116u8,32u8,101u8,120u8, - 105u8,115u8,116u8,36u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,73u8,78u8,86u8,65u8,76u8,73u8,68u8, - 95u8,77u8,65u8,88u8,73u8,77u8,85u8,77u8,58u8,67u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8,32u8, - 111u8,114u8,32u8,116u8,111u8,107u8,101u8,110u8,100u8,97u8,116u8,97u8,32u8,109u8,97u8,120u8,105u8,109u8,117u8,109u8, - 32u8,109u8,117u8,115u8,116u8,32u8,98u8,101u8,32u8,108u8,97u8,114u8,103u8,101u8,114u8,32u8,116u8,104u8,97u8,110u8, - 32u8,115u8,117u8,112u8,112u8,108u8,121u8,37u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,69u8,84u8,79u8,75u8, - 69u8,78u8,95u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,73u8,69u8,83u8,95u8,67u8,79u8,85u8,78u8,84u8,95u8, - 78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8,36u8,84u8,111u8,107u8,101u8,110u8,32u8,80u8,114u8,111u8,112u8, - 101u8,114u8,116u8,105u8,101u8,115u8,32u8,99u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8, - 32u8,109u8,97u8,116u8,99u8,104u8,38u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,40u8,69u8,73u8,78u8,83u8,85u8, - 70u8,70u8,73u8,67u8,73u8,69u8,78u8,84u8,95u8,87u8,73u8,84u8,72u8,68u8,82u8,65u8,87u8,95u8,67u8,65u8, - 80u8,65u8,66u8,73u8,76u8,73u8,84u8,89u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,50u8,87u8,105u8,116u8,104u8, - 100u8,114u8,97u8,119u8,32u8,99u8,97u8,112u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,32u8,100u8,111u8,101u8,115u8, - 110u8,39u8,116u8,32u8,104u8,97u8,118u8,101u8,32u8,115u8,117u8,102u8,102u8,105u8,99u8,105u8,101u8,110u8,116u8,32u8, - 97u8,109u8,111u8,117u8,110u8,116u8,39u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,87u8,73u8,84u8,72u8, - 68u8,82u8,65u8,87u8,95u8,80u8,82u8,79u8,79u8,70u8,95u8,69u8,88u8,80u8,73u8,82u8,69u8,83u8,22u8,87u8, - 105u8,116u8,104u8,100u8,114u8,97u8,119u8,32u8,112u8,114u8,111u8,111u8,102u8,32u8,101u8,120u8,112u8,105u8,114u8,101u8, - 115u8,40u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,30u8,69u8,80u8,82u8,79u8,80u8,69u8,82u8,84u8,89u8,95u8, - 82u8,69u8,83u8,69u8,82u8,86u8,69u8,68u8,95u8,66u8,89u8,95u8,83u8,84u8,65u8,78u8,68u8,65u8,82u8,68u8, - 42u8,84u8,104u8,101u8,32u8,112u8,114u8,111u8,112u8,101u8,114u8,116u8,121u8,32u8,105u8,115u8,32u8,114u8,101u8,115u8, - 101u8,114u8,118u8,101u8,100u8,32u8,98u8,121u8,32u8,116u8,111u8,107u8,101u8,110u8,32u8,115u8,116u8,97u8,110u8,100u8, - 97u8,114u8,100u8,0u8,1u8,32u8,103u8,101u8,116u8,95u8,99u8,111u8,108u8,108u8,101u8,99u8,116u8,105u8,111u8,110u8, - 95u8,109u8,117u8,116u8,97u8,98u8,105u8,108u8,105u8,116u8,121u8,95u8,99u8,111u8,110u8,102u8,105u8,103u8,1u8,1u8, - 0u8,0u8,2u8,2u8,117u8,8u8,13u8,118u8,3u8,1u8,2u8,6u8,119u8,8u8,18u8,120u8,8u8,18u8,121u8,8u8, - 18u8,122u8,3u8,123u8,3u8,124u8,8u8,2u8,2u8,2u8,3u8,119u8,1u8,121u8,1u8,123u8,1u8,3u8,2u8,5u8, - 125u8,11u8,21u8,2u8,8u8,18u8,8u8,1u8,127u8,11u8,21u8,2u8,8u8,12u8,8u8,11u8,128u8,1u8,11u8,22u8, - 1u8,8u8,4u8,130u8,1u8,11u8,22u8,1u8,8u8,5u8,131u8,1u8,11u8,22u8,1u8,8u8,7u8,4u8,2u8,5u8, - 132u8,1u8,5u8,133u8,1u8,8u8,18u8,121u8,8u8,18u8,119u8,8u8,18u8,123u8,3u8,5u8,2u8,12u8,117u8,8u8, - 12u8,119u8,8u8,18u8,123u8,3u8,121u8,8u8,18u8,134u8,1u8,5u8,135u8,1u8,3u8,136u8,1u8,3u8,120u8,8u8, - 18u8,124u8,8u8,14u8,137u8,1u8,10u8,8u8,18u8,138u8,1u8,10u8,10u8,2u8,139u8,1u8,10u8,8u8,18u8,6u8, - 2u8,2u8,117u8,8u8,13u8,118u8,3u8,7u8,2u8,2u8,117u8,8u8,12u8,118u8,3u8,8u8,2u8,5u8,140u8,1u8, - 8u8,13u8,141u8,1u8,8u8,13u8,142u8,1u8,10u8,8u8,18u8,143u8,1u8,10u8,10u8,2u8,144u8,1u8,10u8,8u8, - 18u8,9u8,2u8,3u8,136u8,1u8,3u8,135u8,1u8,3u8,145u8,1u8,5u8,10u8,2u8,3u8,117u8,8u8,13u8,118u8, - 3u8,146u8,1u8,8u8,20u8,11u8,2u8,9u8,123u8,3u8,147u8,1u8,3u8,122u8,3u8,121u8,8u8,18u8,148u8,1u8, - 8u8,9u8,120u8,8u8,18u8,119u8,8u8,18u8,149u8,1u8,8u8,20u8,124u8,8u8,14u8,12u8,2u8,3u8,132u8,1u8, - 5u8,150u8,1u8,8u8,18u8,120u8,8u8,18u8,13u8,2u8,2u8,151u8,1u8,8u8,12u8,152u8,1u8,3u8,14u8,2u8, - 5u8,123u8,1u8,121u8,1u8,148u8,1u8,1u8,119u8,1u8,153u8,1u8,1u8,15u8,2u8,6u8,154u8,1u8,11u8,21u8, - 2u8,8u8,13u8,8u8,10u8,54u8,1u8,155u8,1u8,11u8,22u8,1u8,8u8,6u8,156u8,1u8,11u8,22u8,1u8,8u8, - 17u8,157u8,1u8,11u8,22u8,1u8,8u8,0u8,158u8,1u8,11u8,22u8,1u8,8u8,8u8,16u8,2u8,4u8,159u8,1u8, - 5u8,110u8,8u8,13u8,118u8,3u8,160u8,1u8,3u8,17u8,2u8,2u8,117u8,8u8,13u8,118u8,3u8,0u8,0u8,0u8, - 1u8,3u8,1u8,18u8,10u8,0u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8,85u8,39u8,11u8,0u8,43u8, - 3u8,16u8,0u8,11u8,1u8,56u8,0u8,4u8,14u8,5u8,17u8,7u8,3u8,17u8,85u8,39u8,2u8,1u8,0u8,0u8, - 0u8,65u8,47u8,10u8,0u8,65u8,29u8,12u8,4u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8, - 10u8,2u8,10u8,4u8,35u8,4u8,44u8,5u8,10u8,10u8,0u8,10u8,2u8,66u8,29u8,12u8,3u8,10u8,3u8,17u8, - 87u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,38u8,4u8,37u8,11u8,3u8,20u8,12u8,1u8,14u8,1u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,88u8, - 7u8,41u8,17u8,89u8,34u8,4u8,31u8,5u8,36u8,11u8,0u8,1u8,7u8,26u8,17u8,90u8,39u8,5u8,39u8,11u8, - 3u8,1u8,11u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,2u8,5u8,5u8,11u8,0u8, - 1u8,2u8,2u8,0u8,0u8,1u8,3u8,69u8,34u8,14u8,1u8,16u8,1u8,20u8,12u8,3u8,11u8,0u8,17u8,91u8, - 10u8,3u8,33u8,4u8,10u8,5u8,13u8,7u8,23u8,17u8,90u8,39u8,10u8,3u8,41u8,3u8,4u8,17u8,5u8,20u8, - 7u8,4u8,17u8,85u8,39u8,11u8,3u8,42u8,3u8,15u8,2u8,11u8,1u8,12u8,2u8,46u8,11u8,2u8,56u8,1u8, - 4u8,30u8,5u8,33u8,7u8,30u8,17u8,85u8,39u8,2u8,3u8,1u8,0u8,1u8,15u8,71u8,28u8,10u8,0u8,41u8, - 15u8,32u8,4u8,6u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,2u8,11u8,0u8,43u8,15u8,12u8,3u8, - 10u8,3u8,16u8,3u8,10u8,1u8,56u8,2u8,4u8,22u8,11u8,3u8,16u8,3u8,11u8,1u8,56u8,3u8,16u8,4u8, - 20u8,12u8,2u8,5u8,26u8,11u8,3u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,2u8,11u8, - 2u8,2u8,4u8,1u8,4u8,2u8,3u8,15u8,74u8,197u8,1u8,10u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,5u8,5u8,10u8,11u8,0u8,1u8,7u8,20u8,17u8,93u8,39u8,11u8,1u8,11u8,2u8,11u8, - 3u8,11u8,4u8,17u8,14u8,12u8,16u8,14u8,16u8,16u8,5u8,16u8,1u8,20u8,12u8,13u8,10u8,13u8,41u8,3u8, - 4u8,25u8,5u8,30u8,11u8,0u8,1u8,7u8,4u8,17u8,85u8,39u8,11u8,13u8,42u8,3u8,12u8,12u8,10u8,12u8, - 16u8,2u8,14u8,16u8,16u8,5u8,20u8,56u8,1u8,4u8,41u8,5u8,48u8,11u8,0u8,1u8,11u8,12u8,1u8,7u8, - 30u8,17u8,85u8,39u8,10u8,12u8,15u8,2u8,14u8,16u8,16u8,5u8,20u8,56u8,4u8,12u8,14u8,10u8,14u8,16u8, - 6u8,12u8,7u8,7u8,1u8,17u8,89u8,12u8,6u8,11u8,7u8,14u8,6u8,17u8,95u8,4u8,66u8,5u8,75u8,11u8, - 14u8,1u8,11u8,0u8,1u8,11u8,12u8,1u8,7u8,25u8,17u8,90u8,39u8,11u8,14u8,16u8,6u8,12u8,9u8,7u8, - 1u8,17u8,89u8,12u8,8u8,11u8,9u8,14u8,8u8,17u8,96u8,4u8,86u8,5u8,93u8,11u8,0u8,1u8,11u8,12u8, - 1u8,7u8,25u8,17u8,90u8,39u8,10u8,0u8,10u8,16u8,11u8,5u8,17u8,82u8,19u8,10u8,1u8,12u8,10u8,1u8, - 11u8,0u8,17u8,91u8,42u8,15u8,15u8,7u8,10u8,16u8,10u8,10u8,18u8,0u8,56u8,5u8,10u8,12u8,15u8,2u8, - 14u8,16u8,16u8,5u8,20u8,56u8,4u8,12u8,15u8,10u8,15u8,16u8,8u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,36u8,4u8,192u8,1u8,10u8,15u8,16u8,9u8,20u8,11u8,10u8,23u8,10u8,15u8,15u8,9u8,21u8, - 11u8,15u8,16u8,9u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,189u8,1u8,10u8,12u8, - 15u8,2u8,14u8,16u8,16u8,5u8,20u8,56u8,6u8,17u8,21u8,10u8,12u8,15u8,0u8,14u8,16u8,16u8,5u8,16u8, - 10u8,20u8,56u8,7u8,12u8,11u8,10u8,11u8,16u8,11u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 36u8,4u8,184u8,1u8,10u8,11u8,16u8,12u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,10u8, - 11u8,15u8,12u8,21u8,10u8,11u8,16u8,12u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8, - 179u8,1u8,11u8,12u8,15u8,0u8,11u8,11u8,16u8,13u8,20u8,56u8,8u8,17u8,20u8,5u8,183u8,1u8,11u8,12u8, - 1u8,11u8,11u8,1u8,5u8,188u8,1u8,11u8,12u8,1u8,11u8,11u8,1u8,5u8,191u8,1u8,11u8,12u8,1u8,5u8, - 196u8,1u8,11u8,15u8,1u8,11u8,12u8,1u8,2u8,5u8,1u8,4u8,2u8,3u8,15u8,81u8,182u8,1u8,11u8,0u8, - 17u8,91u8,12u8,13u8,10u8,5u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,8u8,5u8,11u8, - 7u8,20u8,17u8,93u8,39u8,10u8,13u8,11u8,2u8,11u8,3u8,11u8,4u8,17u8,14u8,12u8,15u8,14u8,15u8,16u8, - 5u8,16u8,1u8,20u8,41u8,3u8,4u8,24u8,5u8,27u8,7u8,4u8,17u8,85u8,39u8,11u8,13u8,42u8,3u8,12u8, - 12u8,10u8,12u8,16u8,2u8,14u8,15u8,16u8,5u8,20u8,56u8,1u8,4u8,38u8,5u8,43u8,11u8,12u8,1u8,7u8, - 30u8,17u8,85u8,39u8,10u8,12u8,15u8,2u8,14u8,15u8,16u8,5u8,20u8,56u8,4u8,12u8,14u8,10u8,14u8,16u8, - 6u8,12u8,7u8,7u8,0u8,17u8,89u8,12u8,6u8,11u8,7u8,14u8,6u8,17u8,95u8,4u8,61u8,5u8,68u8,11u8, - 14u8,1u8,11u8,12u8,1u8,7u8,9u8,17u8,90u8,39u8,10u8,14u8,16u8,6u8,12u8,9u8,7u8,0u8,17u8,89u8, - 12u8,8u8,11u8,9u8,14u8,8u8,17u8,96u8,4u8,79u8,5u8,86u8,11u8,14u8,1u8,11u8,12u8,1u8,7u8,9u8, - 17u8,90u8,39u8,10u8,1u8,10u8,15u8,11u8,5u8,17u8,84u8,19u8,10u8,1u8,12u8,10u8,1u8,11u8,1u8,42u8, - 15u8,15u8,7u8,10u8,15u8,10u8,10u8,18u8,0u8,56u8,5u8,10u8,14u8,16u8,8u8,20u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,36u8,4u8,177u8,1u8,10u8,14u8,16u8,9u8,20u8,11u8,10u8,23u8,10u8,14u8,15u8, - 9u8,21u8,11u8,14u8,16u8,9u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,174u8,1u8, - 10u8,12u8,15u8,2u8,14u8,15u8,16u8,5u8,20u8,56u8,6u8,17u8,21u8,10u8,12u8,15u8,0u8,14u8,15u8,16u8, - 5u8,16u8,10u8,20u8,56u8,7u8,12u8,11u8,10u8,11u8,16u8,11u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,169u8,1u8,10u8,11u8,16u8,12u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 23u8,10u8,11u8,15u8,12u8,21u8,10u8,11u8,16u8,12u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 33u8,4u8,164u8,1u8,11u8,12u8,15u8,0u8,11u8,11u8,16u8,13u8,20u8,56u8,8u8,17u8,20u8,5u8,168u8,1u8, - 11u8,12u8,1u8,11u8,11u8,1u8,5u8,173u8,1u8,11u8,12u8,1u8,11u8,11u8,1u8,5u8,176u8,1u8,11u8,12u8, - 1u8,5u8,181u8,1u8,11u8,14u8,1u8,11u8,12u8,1u8,2u8,6u8,1u8,0u8,1u8,3u8,1u8,13u8,10u8,0u8, - 41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8,85u8,39u8,11u8,0u8,43u8,3u8,16u8,0u8,11u8,1u8,56u8, - 0u8,2u8,7u8,1u8,0u8,1u8,3u8,82u8,20u8,10u8,0u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8, - 85u8,39u8,10u8,0u8,43u8,3u8,16u8,2u8,12u8,3u8,11u8,0u8,11u8,1u8,11u8,2u8,17u8,12u8,12u8,4u8, - 11u8,3u8,11u8,4u8,56u8,1u8,2u8,8u8,1u8,0u8,1u8,3u8,83u8,89u8,14u8,1u8,17u8,87u8,7u8,38u8, - 37u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,7u8,17u8,93u8,39u8,14u8,3u8,17u8,87u8,7u8,39u8,37u8, - 4u8,17u8,5u8,22u8,11u8,0u8,1u8,7u8,34u8,17u8,93u8,39u8,10u8,0u8,17u8,91u8,12u8,7u8,10u8,7u8, - 41u8,3u8,32u8,4u8,41u8,10u8,0u8,56u8,9u8,56u8,10u8,10u8,0u8,56u8,11u8,10u8,0u8,56u8,12u8,11u8, - 0u8,56u8,13u8,18u8,3u8,45u8,3u8,5u8,43u8,11u8,0u8,1u8,10u8,7u8,42u8,3u8,15u8,0u8,12u8,9u8, - 10u8,9u8,10u8,1u8,12u8,6u8,46u8,11u8,6u8,56u8,0u8,32u8,4u8,56u8,5u8,61u8,11u8,9u8,1u8,7u8, - 6u8,17u8,101u8,39u8,14u8,5u8,17u8,9u8,12u8,10u8,10u8,2u8,14u8,1u8,20u8,10u8,3u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,10u8,4u8,11u8,10u8,18u8,1u8,12u8,8u8,11u8,9u8,10u8,1u8,11u8,8u8, - 56u8,14u8,10u8,7u8,42u8,3u8,15u8,14u8,11u8,7u8,14u8,1u8,20u8,11u8,3u8,11u8,2u8,11u8,4u8,18u8, - 4u8,56u8,15u8,2u8,9u8,1u8,0u8,0u8,1u8,14u8,10u8,0u8,7u8,2u8,66u8,7u8,20u8,10u8,0u8,7u8, - 4u8,66u8,7u8,20u8,11u8,0u8,7u8,3u8,66u8,7u8,20u8,18u8,2u8,2u8,10u8,1u8,4u8,1u8,3u8,1u8, - 8u8,11u8,0u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,17u8,8u8,2u8,11u8,1u8,0u8,0u8, - 1u8,20u8,10u8,0u8,10u8,1u8,37u8,4u8,5u8,5u8,8u8,7u8,14u8,17u8,93u8,39u8,10u8,2u8,17u8,103u8, - 4u8,12u8,5u8,15u8,7u8,27u8,17u8,93u8,39u8,11u8,0u8,11u8,1u8,11u8,2u8,18u8,9u8,2u8,12u8,1u8, - 0u8,0u8,1u8,23u8,14u8,1u8,17u8,87u8,7u8,38u8,37u8,4u8,6u8,5u8,9u8,7u8,7u8,17u8,93u8,39u8, - 14u8,2u8,17u8,87u8,7u8,38u8,37u8,4u8,15u8,5u8,18u8,7u8,17u8,17u8,93u8,39u8,11u8,0u8,11u8,1u8, - 11u8,2u8,18u8,12u8,2u8,13u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,11u8,1u8,18u8,13u8,2u8,14u8,1u8, - 0u8,0u8,1u8,7u8,11u8,0u8,11u8,1u8,11u8,2u8,17u8,12u8,11u8,3u8,18u8,13u8,2u8,15u8,1u8,0u8, - 0u8,1u8,22u8,10u8,0u8,7u8,2u8,66u8,7u8,20u8,10u8,0u8,7u8,4u8,66u8,7u8,20u8,10u8,0u8,7u8, - 3u8,66u8,7u8,20u8,10u8,0u8,7u8,6u8,66u8,7u8,20u8,11u8,0u8,7u8,8u8,66u8,7u8,20u8,18u8,14u8, - 2u8,16u8,1u8,4u8,2u8,3u8,15u8,90u8,24u8,14u8,10u8,17u8,15u8,12u8,14u8,10u8,0u8,11u8,1u8,11u8, - 2u8,11u8,3u8,11u8,5u8,11u8,6u8,11u8,7u8,11u8,8u8,11u8,9u8,11u8,14u8,11u8,11u8,11u8,12u8,11u8, - 13u8,17u8,17u8,12u8,15u8,11u8,0u8,11u8,15u8,11u8,4u8,17u8,63u8,1u8,2u8,17u8,1u8,0u8,1u8,3u8, - 91u8,164u8,1u8,14u8,2u8,17u8,87u8,7u8,38u8,37u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,17u8,17u8, - 93u8,39u8,14u8,1u8,17u8,87u8,7u8,38u8,37u8,4u8,17u8,5u8,22u8,11u8,0u8,1u8,7u8,7u8,17u8,93u8, - 39u8,14u8,5u8,17u8,87u8,7u8,39u8,37u8,4u8,28u8,5u8,33u8,11u8,0u8,1u8,7u8,34u8,17u8,93u8,39u8, - 10u8,8u8,10u8,7u8,37u8,4u8,38u8,5u8,43u8,11u8,0u8,1u8,7u8,14u8,17u8,93u8,39u8,11u8,0u8,17u8, - 91u8,12u8,13u8,10u8,13u8,41u8,3u8,4u8,50u8,5u8,53u8,7u8,4u8,17u8,85u8,39u8,10u8,13u8,42u8,3u8, - 12u8,15u8,11u8,13u8,11u8,1u8,10u8,2u8,17u8,12u8,12u8,17u8,10u8,15u8,16u8,0u8,14u8,17u8,16u8,10u8, - 20u8,56u8,0u8,4u8,69u8,5u8,74u8,11u8,15u8,1u8,7u8,3u8,17u8,85u8,39u8,10u8,15u8,16u8,2u8,10u8, - 17u8,56u8,1u8,32u8,4u8,81u8,5u8,86u8,11u8,15u8,1u8,7u8,29u8,17u8,101u8,39u8,10u8,15u8,15u8,0u8, - 14u8,17u8,16u8,10u8,20u8,56u8,7u8,12u8,14u8,10u8,14u8,16u8,11u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,36u8,4u8,122u8,10u8,14u8,16u8,12u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 22u8,10u8,14u8,15u8,12u8,21u8,10u8,14u8,16u8,11u8,20u8,11u8,14u8,16u8,12u8,20u8,38u8,4u8,116u8,5u8, - 121u8,11u8,15u8,1u8,7u8,8u8,17u8,93u8,39u8,5u8,124u8,11u8,14u8,1u8,10u8,4u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,10u8,5u8,10u8,8u8,10u8,7u8, - 10u8,6u8,17u8,11u8,10u8,2u8,10u8,3u8,10u8,10u8,10u8,11u8,10u8,12u8,17u8,104u8,10u8,9u8,18u8,11u8, - 12u8,16u8,10u8,15u8,15u8,2u8,10u8,17u8,11u8,16u8,56u8,16u8,11u8,15u8,15u8,15u8,10u8,17u8,11u8,3u8, - 11u8,4u8,11u8,5u8,11u8,6u8,11u8,7u8,11u8,8u8,11u8,2u8,11u8,9u8,11u8,10u8,11u8,11u8,11u8,12u8, - 18u8,5u8,56u8,17u8,11u8,17u8,2u8,18u8,1u8,0u8,0u8,1u8,7u8,11u8,0u8,17u8,91u8,11u8,1u8,11u8, - 2u8,11u8,3u8,18u8,16u8,2u8,19u8,1u8,0u8,1u8,15u8,32u8,9u8,10u8,0u8,17u8,91u8,12u8,2u8,11u8, - 0u8,17u8,60u8,11u8,2u8,11u8,1u8,17u8,22u8,2u8,20u8,0u8,0u8,0u8,1u8,9u8,11u8,0u8,19u8,1u8, - 1u8,1u8,1u8,1u8,1u8,1u8,2u8,21u8,0u8,0u8,0u8,1u8,12u8,11u8,0u8,19u8,11u8,1u8,1u8,1u8, - 1u8,1u8,1u8,1u8,1u8,1u8,2u8,22u8,0u8,0u8,1u8,15u8,93u8,57u8,14u8,1u8,16u8,4u8,20u8,6u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,7u8,5u8,10u8,7u8,28u8,17u8,93u8,39u8,10u8,0u8, - 42u8,15u8,12u8,2u8,10u8,2u8,15u8,16u8,14u8,1u8,16u8,17u8,20u8,14u8,1u8,16u8,4u8,20u8,18u8,6u8, - 56u8,18u8,11u8,0u8,41u8,15u8,4u8,27u8,5u8,32u8,11u8,2u8,1u8,7u8,33u8,17u8,85u8,39u8,10u8,2u8, - 16u8,3u8,14u8,1u8,16u8,17u8,20u8,56u8,2u8,32u8,4u8,48u8,11u8,2u8,15u8,3u8,14u8,1u8,16u8,17u8, - 20u8,11u8,1u8,56u8,19u8,5u8,56u8,11u8,2u8,15u8,3u8,14u8,1u8,16u8,17u8,20u8,56u8,20u8,11u8,1u8, - 17u8,61u8,2u8,23u8,1u8,0u8,1u8,15u8,1u8,13u8,10u8,0u8,43u8,15u8,16u8,18u8,20u8,4u8,6u8,5u8, - 9u8,7u8,35u8,17u8,90u8,39u8,11u8,0u8,11u8,1u8,17u8,22u8,2u8,24u8,1u8,0u8,1u8,15u8,57u8,9u8, - 11u8,0u8,11u8,2u8,11u8,3u8,17u8,82u8,12u8,4u8,11u8,1u8,11u8,4u8,17u8,19u8,2u8,25u8,1u8,4u8, - 1u8,15u8,16u8,12u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,17u8,14u8,12u8,7u8,11u8,0u8,11u8,1u8, - 11u8,7u8,11u8,6u8,17u8,24u8,2u8,26u8,1u8,0u8,1u8,3u8,1u8,11u8,10u8,0u8,10u8,1u8,17u8,0u8, - 11u8,0u8,42u8,3u8,15u8,0u8,11u8,1u8,56u8,7u8,16u8,19u8,20u8,2u8,27u8,1u8,0u8,1u8,3u8,1u8, - 11u8,10u8,0u8,10u8,1u8,17u8,0u8,11u8,0u8,42u8,3u8,15u8,0u8,11u8,1u8,56u8,7u8,16u8,11u8,20u8, - 2u8,28u8,1u8,0u8,1u8,3u8,95u8,27u8,10u8,0u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8,85u8, - 39u8,11u8,0u8,43u8,3u8,16u8,0u8,12u8,2u8,10u8,2u8,10u8,1u8,56u8,0u8,4u8,16u8,5u8,21u8,11u8, - 2u8,1u8,7u8,3u8,17u8,85u8,39u8,11u8,2u8,11u8,1u8,56u8,21u8,16u8,20u8,20u8,2u8,29u8,1u8,0u8, - 0u8,1u8,4u8,11u8,0u8,16u8,21u8,20u8,2u8,30u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,22u8,20u8, - 2u8,31u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,23u8,20u8,2u8,32u8,1u8,0u8,1u8,3u8,96u8,27u8, - 10u8,0u8,10u8,1u8,17u8,0u8,11u8,0u8,42u8,3u8,15u8,0u8,11u8,1u8,56u8,7u8,12u8,3u8,10u8,3u8, - 16u8,11u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,21u8,11u8,3u8,16u8,12u8,20u8, - 56u8,22u8,12u8,2u8,5u8,25u8,11u8,3u8,1u8,56u8,23u8,12u8,2u8,11u8,2u8,2u8,33u8,1u8,0u8,1u8, - 3u8,1u8,11u8,10u8,0u8,10u8,1u8,17u8,0u8,11u8,0u8,42u8,3u8,15u8,0u8,11u8,1u8,56u8,7u8,16u8, - 24u8,20u8,2u8,34u8,1u8,0u8,1u8,15u8,1u8,11u8,10u8,0u8,41u8,15u8,32u8,4u8,6u8,9u8,2u8,11u8, - 0u8,43u8,15u8,16u8,18u8,20u8,2u8,35u8,1u8,0u8,2u8,3u8,15u8,99u8,54u8,10u8,0u8,10u8,1u8,17u8, - 3u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,7u8,5u8,10u8,7u8,11u8,17u8,85u8,39u8, - 14u8,1u8,16u8,25u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,44u8,14u8,1u8,16u8, - 5u8,16u8,1u8,20u8,43u8,3u8,16u8,2u8,12u8,3u8,10u8,3u8,14u8,1u8,16u8,5u8,20u8,56u8,1u8,4u8, - 30u8,5u8,35u8,11u8,3u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,3u8,14u8,1u8,16u8,5u8,20u8,56u8,24u8, - 16u8,6u8,20u8,12u8,2u8,5u8,52u8,11u8,0u8,43u8,15u8,16u8,3u8,11u8,1u8,56u8,3u8,16u8,26u8,20u8, - 12u8,2u8,11u8,2u8,2u8,36u8,1u8,0u8,1u8,3u8,1u8,5u8,14u8,0u8,16u8,5u8,20u8,17u8,55u8,2u8, - 37u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,27u8,20u8,2u8,38u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8, - 16u8,28u8,20u8,2u8,39u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,29u8,20u8,2u8,40u8,1u8,0u8,0u8, - 1u8,4u8,11u8,0u8,16u8,4u8,20u8,2u8,41u8,1u8,0u8,0u8,1u8,10u8,10u8,0u8,16u8,1u8,20u8,10u8, - 0u8,16u8,10u8,20u8,11u8,0u8,16u8,30u8,20u8,2u8,42u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,17u8, - 20u8,2u8,43u8,1u8,0u8,0u8,1u8,16u8,10u8,0u8,16u8,5u8,16u8,1u8,20u8,10u8,0u8,16u8,5u8,16u8, - 10u8,20u8,10u8,0u8,16u8,5u8,16u8,30u8,20u8,11u8,0u8,16u8,25u8,20u8,2u8,44u8,1u8,0u8,0u8,1u8, - 4u8,11u8,0u8,16u8,31u8,20u8,2u8,45u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,32u8,20u8,2u8,46u8, - 1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,33u8,20u8,2u8,47u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8, - 34u8,20u8,2u8,48u8,1u8,0u8,0u8,1u8,4u8,11u8,0u8,16u8,35u8,20u8,2u8,49u8,1u8,0u8,1u8,3u8, - 100u8,43u8,10u8,0u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8,85u8,39u8,11u8,0u8,43u8,3u8,16u8, - 2u8,12u8,3u8,10u8,3u8,10u8,1u8,56u8,1u8,4u8,16u8,5u8,21u8,11u8,3u8,1u8,7u8,30u8,17u8,85u8, - 39u8,11u8,3u8,11u8,1u8,56u8,24u8,12u8,4u8,10u8,4u8,16u8,8u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,36u8,4u8,37u8,11u8,4u8,16u8,9u8,20u8,56u8,22u8,12u8,2u8,5u8,41u8,11u8,4u8,1u8, - 56u8,23u8,12u8,2u8,11u8,2u8,2u8,50u8,1u8,0u8,1u8,3u8,101u8,31u8,14u8,0u8,16u8,1u8,20u8,12u8, - 2u8,10u8,2u8,41u8,3u8,4u8,8u8,5u8,11u8,7u8,4u8,17u8,85u8,39u8,11u8,2u8,43u8,3u8,16u8,2u8, - 12u8,1u8,10u8,1u8,10u8,0u8,56u8,1u8,4u8,20u8,5u8,25u8,11u8,1u8,1u8,7u8,30u8,17u8,85u8,39u8, - 11u8,1u8,11u8,0u8,56u8,24u8,16u8,36u8,20u8,2u8,51u8,1u8,0u8,0u8,1u8,4u8,14u8,0u8,16u8,5u8, - 20u8,2u8,52u8,1u8,0u8,1u8,3u8,102u8,27u8,10u8,0u8,41u8,3u8,4u8,4u8,5u8,7u8,7u8,4u8,17u8, - 85u8,39u8,11u8,0u8,43u8,3u8,16u8,2u8,12u8,2u8,10u8,2u8,10u8,1u8,56u8,1u8,4u8,16u8,5u8,21u8, - 11u8,2u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,2u8,11u8,1u8,56u8,24u8,16u8,37u8,20u8,2u8,53u8,1u8, - 0u8,1u8,3u8,101u8,31u8,14u8,0u8,16u8,1u8,20u8,12u8,2u8,10u8,2u8,41u8,3u8,4u8,8u8,5u8,11u8, - 7u8,4u8,17u8,85u8,39u8,11u8,2u8,43u8,3u8,16u8,2u8,12u8,1u8,10u8,1u8,10u8,0u8,56u8,1u8,4u8, - 20u8,5u8,25u8,11u8,1u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,1u8,11u8,0u8,56u8,24u8,16u8,8u8,20u8, - 2u8,54u8,1u8,0u8,1u8,3u8,101u8,31u8,14u8,0u8,16u8,1u8,20u8,12u8,2u8,10u8,2u8,41u8,3u8,4u8, - 8u8,5u8,11u8,7u8,4u8,17u8,85u8,39u8,11u8,2u8,43u8,3u8,16u8,2u8,12u8,1u8,10u8,1u8,10u8,0u8, - 56u8,1u8,4u8,20u8,5u8,25u8,11u8,1u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,1u8,11u8,0u8,56u8,24u8, - 16u8,38u8,20u8,2u8,55u8,1u8,0u8,1u8,3u8,101u8,31u8,14u8,0u8,16u8,1u8,20u8,12u8,2u8,10u8,2u8, - 41u8,3u8,4u8,8u8,5u8,11u8,7u8,4u8,17u8,85u8,39u8,11u8,2u8,43u8,3u8,16u8,2u8,12u8,1u8,10u8, - 1u8,10u8,0u8,56u8,1u8,4u8,20u8,5u8,25u8,11u8,1u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,1u8,11u8, - 0u8,56u8,24u8,16u8,39u8,20u8,2u8,56u8,1u8,0u8,1u8,3u8,102u8,27u8,10u8,0u8,41u8,3u8,4u8,4u8, - 5u8,7u8,7u8,4u8,17u8,85u8,39u8,11u8,0u8,43u8,3u8,16u8,2u8,12u8,2u8,10u8,2u8,10u8,1u8,56u8, - 1u8,4u8,16u8,5u8,21u8,11u8,2u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,2u8,11u8,1u8,56u8,24u8,16u8, - 40u8,20u8,2u8,57u8,1u8,0u8,0u8,1u8,3u8,11u8,0u8,41u8,15u8,2u8,58u8,1u8,0u8,0u8,1u8,2u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,39u8,59u8,1u8,4u8,0u8,1u8,2u8,6u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,39u8,60u8,1u8,0u8,0u8,1u8,22u8,10u8,0u8,17u8,91u8,41u8,15u8,32u8,4u8, - 19u8,10u8,0u8,56u8,25u8,9u8,10u8,0u8,56u8,26u8,10u8,0u8,56u8,27u8,10u8,0u8,56u8,28u8,11u8,0u8, - 56u8,29u8,18u8,15u8,45u8,15u8,5u8,21u8,11u8,0u8,1u8,2u8,61u8,1u8,0u8,0u8,1u8,28u8,10u8,0u8, - 16u8,17u8,14u8,1u8,16u8,17u8,33u8,4u8,7u8,5u8,12u8,11u8,0u8,1u8,7u8,15u8,17u8,93u8,39u8,10u8, - 0u8,16u8,4u8,20u8,14u8,1u8,16u8,4u8,20u8,22u8,11u8,0u8,15u8,4u8,21u8,11u8,1u8,19u8,10u8,1u8, - 1u8,1u8,2u8,62u8,1u8,4u8,2u8,3u8,15u8,14u8,24u8,11u8,1u8,11u8,2u8,11u8,3u8,17u8,12u8,12u8, - 5u8,14u8,5u8,16u8,1u8,20u8,10u8,0u8,17u8,91u8,33u8,4u8,13u8,5u8,18u8,11u8,0u8,1u8,7u8,22u8, - 17u8,90u8,39u8,11u8,0u8,11u8,5u8,11u8,4u8,17u8,63u8,1u8,2u8,63u8,1u8,0u8,2u8,3u8,15u8,105u8, - 94u8,14u8,1u8,16u8,1u8,20u8,10u8,0u8,17u8,91u8,33u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,22u8, - 17u8,90u8,39u8,14u8,1u8,16u8,1u8,20u8,12u8,5u8,10u8,5u8,42u8,3u8,15u8,2u8,12u8,4u8,10u8,4u8, - 10u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8,1u8,4u8,29u8,5u8,36u8,11u8,4u8,1u8,11u8,0u8,1u8,7u8, - 30u8,17u8,85u8,39u8,11u8,4u8,10u8,1u8,56u8,4u8,12u8,6u8,10u8,6u8,16u8,8u8,20u8,6u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,73u8,10u8,6u8,16u8,9u8,20u8,10u8,2u8,22u8,10u8,6u8,16u8, - 8u8,20u8,37u8,4u8,57u8,5u8,64u8,11u8,6u8,1u8,11u8,0u8,1u8,7u8,16u8,17u8,93u8,39u8,10u8,6u8, - 16u8,9u8,20u8,10u8,2u8,22u8,11u8,6u8,15u8,9u8,21u8,5u8,75u8,11u8,6u8,1u8,10u8,1u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,17u8,13u8,12u8,7u8,11u8,5u8,42u8,3u8,15u8,41u8,11u8,1u8,10u8, - 2u8,18u8,7u8,56u8,30u8,11u8,0u8,10u8,7u8,11u8,2u8,17u8,107u8,18u8,10u8,17u8,19u8,11u8,7u8,2u8, - 64u8,1u8,0u8,2u8,3u8,15u8,105u8,107u8,10u8,1u8,41u8,15u8,4u8,4u8,5u8,9u8,11u8,0u8,1u8,7u8, - 33u8,17u8,85u8,39u8,10u8,1u8,43u8,15u8,16u8,18u8,20u8,4u8,15u8,5u8,20u8,11u8,0u8,1u8,7u8,35u8, - 17u8,90u8,39u8,14u8,2u8,16u8,1u8,20u8,11u8,0u8,17u8,91u8,33u8,4u8,28u8,5u8,31u8,7u8,22u8,17u8, - 90u8,39u8,14u8,2u8,16u8,1u8,20u8,12u8,6u8,10u8,6u8,42u8,3u8,15u8,2u8,12u8,5u8,10u8,5u8,10u8, - 2u8,12u8,4u8,46u8,11u8,4u8,56u8,1u8,4u8,47u8,5u8,52u8,11u8,5u8,1u8,7u8,30u8,17u8,85u8,39u8, - 11u8,5u8,10u8,2u8,56u8,4u8,12u8,7u8,10u8,7u8,16u8,8u8,20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,36u8,4u8,87u8,10u8,7u8,16u8,9u8,20u8,10u8,3u8,22u8,10u8,7u8,16u8,8u8,20u8,37u8,4u8, - 73u8,5u8,78u8,11u8,7u8,1u8,7u8,16u8,17u8,93u8,39u8,10u8,7u8,16u8,9u8,20u8,10u8,3u8,22u8,11u8, - 7u8,15u8,9u8,21u8,5u8,89u8,11u8,7u8,1u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 17u8,13u8,12u8,8u8,11u8,6u8,42u8,3u8,15u8,41u8,11u8,2u8,10u8,3u8,18u8,7u8,56u8,30u8,11u8,1u8, - 11u8,8u8,11u8,3u8,17u8,107u8,18u8,10u8,17u8,22u8,2u8,65u8,1u8,0u8,1u8,3u8,106u8,37u8,10u8,0u8, - 17u8,91u8,12u8,4u8,10u8,4u8,10u8,1u8,17u8,0u8,11u8,4u8,42u8,3u8,15u8,0u8,10u8,1u8,56u8,7u8, - 12u8,3u8,10u8,3u8,16u8,20u8,16u8,21u8,20u8,4u8,18u8,5u8,25u8,11u8,0u8,1u8,11u8,3u8,1u8,7u8, - 10u8,17u8,90u8,39u8,11u8,0u8,11u8,1u8,10u8,3u8,16u8,19u8,20u8,10u8,2u8,17u8,108u8,11u8,2u8,11u8, - 3u8,15u8,19u8,21u8,2u8,66u8,1u8,0u8,1u8,3u8,108u8,74u8,10u8,0u8,17u8,91u8,12u8,5u8,10u8,5u8, - 10u8,1u8,17u8,0u8,11u8,5u8,42u8,3u8,15u8,0u8,10u8,1u8,56u8,7u8,12u8,4u8,10u8,4u8,16u8,11u8, - 20u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,23u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,34u8,12u8,3u8,5u8,25u8,9u8,12u8,3u8,11u8,3u8,4u8,28u8,5u8,35u8,11u8,0u8, - 1u8,11u8,4u8,1u8,7u8,13u8,17u8,93u8,39u8,10u8,2u8,10u8,4u8,16u8,12u8,20u8,38u8,4u8,42u8,5u8, - 49u8,11u8,0u8,1u8,11u8,4u8,1u8,7u8,13u8,17u8,93u8,39u8,10u8,4u8,16u8,20u8,16u8,22u8,20u8,4u8, - 55u8,5u8,62u8,11u8,0u8,1u8,11u8,4u8,1u8,7u8,10u8,17u8,90u8,39u8,11u8,0u8,11u8,1u8,10u8,4u8, - 16u8,11u8,20u8,10u8,2u8,17u8,109u8,11u8,2u8,11u8,4u8,15u8,11u8,21u8,2u8,67u8,1u8,0u8,1u8,3u8, - 106u8,48u8,14u8,2u8,17u8,87u8,7u8,39u8,37u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,34u8,17u8,93u8, - 39u8,10u8,0u8,17u8,91u8,12u8,4u8,10u8,4u8,10u8,1u8,17u8,0u8,11u8,4u8,42u8,3u8,15u8,0u8,10u8, - 1u8,56u8,7u8,12u8,3u8,10u8,3u8,16u8,20u8,16u8,23u8,20u8,4u8,29u8,5u8,36u8,11u8,0u8,1u8,11u8, - 3u8,1u8,7u8,10u8,17u8,90u8,39u8,11u8,0u8,11u8,1u8,10u8,3u8,16u8,24u8,20u8,10u8,2u8,17u8,110u8, - 11u8,2u8,11u8,3u8,15u8,24u8,21u8,2u8,68u8,1u8,0u8,2u8,3u8,15u8,110u8,167u8,1u8,14u8,2u8,16u8, - 5u8,16u8,1u8,20u8,12u8,13u8,11u8,0u8,17u8,91u8,10u8,13u8,33u8,4u8,11u8,5u8,14u8,7u8,23u8,17u8, - 90u8,39u8,10u8,13u8,41u8,3u8,4u8,18u8,5u8,21u8,7u8,4u8,17u8,85u8,39u8,11u8,13u8,42u8,3u8,15u8, - 2u8,12u8,12u8,10u8,12u8,14u8,2u8,16u8,5u8,20u8,12u8,6u8,46u8,11u8,6u8,56u8,1u8,4u8,35u8,5u8, - 40u8,11u8,12u8,1u8,7u8,30u8,17u8,85u8,39u8,11u8,12u8,14u8,2u8,16u8,5u8,20u8,56u8,4u8,12u8,18u8, - 10u8,18u8,16u8,38u8,16u8,31u8,20u8,32u8,4u8,84u8,10u8,18u8,16u8,6u8,12u8,8u8,7u8,40u8,17u8,89u8, - 12u8,7u8,11u8,8u8,14u8,7u8,17u8,95u8,4u8,63u8,5u8,68u8,11u8,18u8,1u8,7u8,10u8,17u8,90u8,39u8, - 10u8,18u8,16u8,6u8,12u8,10u8,7u8,40u8,17u8,89u8,12u8,9u8,11u8,10u8,14u8,9u8,17u8,96u8,4u8,79u8, - 5u8,84u8,11u8,18u8,1u8,7u8,10u8,17u8,90u8,39u8,14u8,2u8,16u8,25u8,20u8,6u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,33u8,4u8,145u8,1u8,10u8,1u8,10u8,2u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,17u8,84u8,12u8,17u8,10u8,18u8,16u8,37u8,20u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,14u8,14u8,2u8,16u8,5u8,20u8,10u8,14u8,17u8,13u8,12u8,16u8,10u8,16u8,6u8,1u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,10u8,18u8,16u8,6u8,20u8,18u8,10u8,12u8,15u8,10u8,1u8,11u8,15u8,17u8,22u8,10u8, - 1u8,10u8,16u8,10u8,3u8,10u8,4u8,10u8,5u8,17u8,81u8,11u8,1u8,42u8,15u8,15u8,42u8,11u8,2u8,10u8, - 16u8,11u8,3u8,11u8,4u8,11u8,5u8,18u8,8u8,56u8,31u8,11u8,14u8,11u8,18u8,15u8,37u8,21u8,11u8,17u8, - 19u8,10u8,1u8,1u8,1u8,11u8,16u8,12u8,11u8,5u8,165u8,1u8,11u8,18u8,1u8,10u8,1u8,10u8,2u8,10u8, - 3u8,10u8,4u8,10u8,5u8,17u8,81u8,11u8,1u8,42u8,15u8,15u8,42u8,10u8,2u8,10u8,2u8,11u8,3u8,11u8, - 4u8,11u8,5u8,18u8,8u8,56u8,31u8,11u8,2u8,12u8,11u8,11u8,11u8,2u8,69u8,1u8,4u8,2u8,3u8,15u8, - 111u8,40u8,10u8,0u8,17u8,91u8,10u8,2u8,33u8,4u8,6u8,5u8,11u8,11u8,0u8,1u8,7u8,23u8,17u8,85u8, - 39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,10u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8, - 17u8,14u8,12u8,11u8,10u8,10u8,10u8,6u8,35u8,4u8,37u8,5u8,24u8,10u8,0u8,10u8,1u8,10u8,11u8,10u8, - 7u8,10u8,8u8,10u8,9u8,17u8,68u8,1u8,11u8,10u8,6u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,22u8, - 12u8,10u8,5u8,19u8,11u8,0u8,1u8,2u8,70u8,1u8,0u8,1u8,3u8,112u8,41u8,10u8,0u8,10u8,1u8,17u8, - 2u8,14u8,1u8,16u8,1u8,20u8,42u8,3u8,15u8,2u8,10u8,1u8,56u8,4u8,12u8,3u8,10u8,3u8,16u8,38u8, - 16u8,32u8,20u8,4u8,17u8,5u8,24u8,11u8,3u8,1u8,11u8,0u8,1u8,7u8,10u8,17u8,90u8,39u8,11u8,0u8, - 14u8,1u8,16u8,10u8,20u8,14u8,1u8,16u8,30u8,20u8,10u8,3u8,16u8,36u8,20u8,10u8,2u8,17u8,111u8,11u8, - 2u8,11u8,3u8,15u8,36u8,21u8,2u8,71u8,1u8,0u8,1u8,3u8,114u8,78u8,10u8,0u8,10u8,1u8,17u8,2u8, - 14u8,1u8,16u8,1u8,20u8,42u8,3u8,15u8,2u8,10u8,1u8,56u8,4u8,12u8,4u8,10u8,4u8,16u8,8u8,20u8, - 6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,34u8,4u8,22u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,34u8,12u8,3u8,5u8,24u8,9u8,12u8,3u8,11u8,3u8,4u8,27u8,5u8,34u8,11u8,4u8,1u8, - 11u8,0u8,1u8,7u8,13u8,17u8,93u8,39u8,10u8,2u8,10u8,4u8,16u8,9u8,20u8,38u8,4u8,41u8,5u8,48u8, - 11u8,4u8,1u8,11u8,0u8,1u8,7u8,13u8,17u8,93u8,39u8,10u8,4u8,16u8,38u8,16u8,33u8,20u8,4u8,54u8, - 5u8,61u8,11u8,4u8,1u8,11u8,0u8,1u8,7u8,10u8,17u8,90u8,39u8,11u8,0u8,14u8,1u8,16u8,10u8,20u8, - 14u8,1u8,16u8,30u8,20u8,10u8,4u8,16u8,8u8,20u8,10u8,2u8,17u8,112u8,11u8,2u8,11u8,4u8,15u8,8u8, - 21u8,2u8,72u8,1u8,0u8,1u8,3u8,116u8,138u8,1u8,10u8,0u8,10u8,1u8,17u8,2u8,14u8,2u8,65u8,29u8, - 12u8,8u8,14u8,3u8,65u8,68u8,12u8,15u8,14u8,4u8,65u8,29u8,12u8,14u8,10u8,8u8,11u8,15u8,33u8,4u8, - 17u8,5u8,22u8,11u8,0u8,1u8,7u8,31u8,17u8,113u8,39u8,11u8,8u8,11u8,14u8,33u8,4u8,27u8,5u8,32u8, - 11u8,0u8,1u8,7u8,31u8,17u8,113u8,39u8,14u8,1u8,16u8,1u8,20u8,42u8,3u8,15u8,2u8,10u8,1u8,56u8, - 4u8,12u8,13u8,10u8,13u8,16u8,38u8,16u8,31u8,20u8,4u8,46u8,5u8,53u8,11u8,13u8,1u8,11u8,0u8,1u8, - 7u8,10u8,17u8,90u8,39u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,6u8,64u8,117u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,12u8,12u8,64u8,118u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,12u8,10u8, - 14u8,2u8,17u8,1u8,10u8,6u8,14u8,2u8,65u8,29u8,35u8,4u8,124u8,5u8,67u8,14u8,2u8,10u8,6u8,66u8, - 29u8,12u8,7u8,10u8,13u8,16u8,6u8,10u8,7u8,17u8,95u8,4u8,84u8,10u8,13u8,16u8,6u8,10u8,7u8,17u8, - 114u8,20u8,56u8,32u8,12u8,5u8,5u8,86u8,56u8,33u8,12u8,5u8,11u8,5u8,12u8,11u8,13u8,12u8,10u8,11u8, - 68u8,117u8,14u8,3u8,10u8,6u8,66u8,68u8,20u8,14u8,4u8,10u8,6u8,66u8,29u8,20u8,17u8,115u8,12u8,9u8, - 13u8,10u8,10u8,9u8,68u8,118u8,14u8,11u8,56u8,34u8,4u8,113u8,10u8,13u8,15u8,6u8,11u8,7u8,11u8,9u8, - 17u8,117u8,5u8,119u8,10u8,13u8,15u8,6u8,11u8,7u8,20u8,11u8,9u8,17u8,118u8,11u8,6u8,6u8,1u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,22u8,12u8,6u8,5u8,61u8,11u8,13u8,1u8,11u8,0u8,14u8,1u8,16u8,10u8, - 20u8,14u8,1u8,16u8,30u8,20u8,11u8,2u8,11u8,12u8,11u8,10u8,17u8,119u8,2u8,73u8,1u8,0u8,1u8,3u8, - 112u8,58u8,10u8,0u8,10u8,1u8,17u8,2u8,14u8,1u8,16u8,1u8,20u8,42u8,3u8,15u8,2u8,10u8,1u8,56u8, - 4u8,12u8,3u8,10u8,3u8,16u8,38u8,16u8,34u8,20u8,4u8,17u8,5u8,24u8,11u8,3u8,1u8,11u8,0u8,1u8, - 7u8,10u8,17u8,90u8,39u8,11u8,0u8,14u8,1u8,16u8,10u8,20u8,14u8,1u8,16u8,30u8,20u8,10u8,3u8,16u8, - 39u8,16u8,28u8,20u8,10u8,3u8,16u8,39u8,16u8,27u8,20u8,10u8,3u8,16u8,39u8,16u8,29u8,20u8,14u8,2u8, - 16u8,28u8,20u8,14u8,2u8,16u8,27u8,20u8,14u8,2u8,16u8,29u8,20u8,17u8,120u8,11u8,2u8,11u8,3u8,15u8, - 39u8,21u8,2u8,74u8,1u8,0u8,1u8,3u8,112u8,52u8,14u8,2u8,17u8,87u8,7u8,39u8,37u8,4u8,6u8,5u8, - 11u8,11u8,0u8,1u8,7u8,34u8,17u8,93u8,39u8,10u8,0u8,10u8,1u8,17u8,2u8,14u8,1u8,16u8,1u8,20u8, - 42u8,3u8,15u8,2u8,10u8,1u8,56u8,4u8,12u8,3u8,10u8,3u8,16u8,38u8,16u8,35u8,20u8,4u8,28u8,5u8, - 35u8,11u8,3u8,1u8,11u8,0u8,1u8,7u8,10u8,17u8,90u8,39u8,11u8,0u8,14u8,1u8,16u8,10u8,20u8,14u8, - 1u8,16u8,30u8,20u8,10u8,3u8,16u8,40u8,20u8,10u8,2u8,17u8,121u8,11u8,2u8,11u8,3u8,15u8,40u8,21u8, - 2u8,75u8,1u8,4u8,1u8,15u8,126u8,16u8,10u8,0u8,17u8,91u8,12u8,2u8,10u8,0u8,17u8,60u8,11u8,2u8, - 42u8,15u8,15u8,18u8,12u8,3u8,10u8,1u8,11u8,3u8,21u8,11u8,0u8,11u8,1u8,17u8,122u8,2u8,76u8,1u8, - 0u8,1u8,15u8,127u8,58u8,17u8,123u8,14u8,0u8,16u8,43u8,20u8,37u8,4u8,7u8,5u8,10u8,7u8,36u8,17u8, - 93u8,39u8,10u8,1u8,14u8,0u8,16u8,44u8,20u8,37u8,4u8,17u8,5u8,20u8,7u8,12u8,17u8,93u8,39u8,10u8, - 1u8,14u8,0u8,16u8,44u8,20u8,33u8,4u8,29u8,56u8,35u8,12u8,2u8,5u8,46u8,14u8,0u8,16u8,45u8,20u8, - 14u8,0u8,16u8,46u8,20u8,14u8,0u8,16u8,44u8,20u8,10u8,1u8,23u8,14u8,0u8,16u8,43u8,20u8,18u8,16u8, - 56u8,36u8,12u8,2u8,11u8,2u8,12u8,3u8,14u8,0u8,16u8,45u8,20u8,14u8,0u8,16u8,46u8,20u8,11u8,1u8, - 17u8,84u8,11u8,3u8,2u8,77u8,1u8,0u8,0u8,1u8,50u8,10u8,0u8,16u8,17u8,16u8,25u8,20u8,6u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,33u8,4u8,8u8,5u8,13u8,11u8,0u8,1u8,7u8,18u8,17u8,113u8,39u8, - 10u8,0u8,16u8,4u8,20u8,10u8,1u8,36u8,4u8,20u8,5u8,25u8,11u8,0u8,1u8,7u8,32u8,17u8,93u8,39u8, - 10u8,1u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,30u8,5u8,35u8,11u8,0u8,1u8,7u8, - 28u8,17u8,93u8,39u8,10u8,0u8,16u8,4u8,20u8,10u8,1u8,23u8,10u8,0u8,15u8,4u8,21u8,11u8,0u8,16u8, - 17u8,20u8,11u8,1u8,17u8,107u8,18u8,10u8,2u8,78u8,1u8,0u8,0u8,1u8,3u8,11u8,0u8,16u8,17u8,2u8, - 79u8,1u8,0u8,1u8,15u8,57u8,20u8,10u8,2u8,43u8,15u8,16u8,18u8,20u8,4u8,6u8,5u8,11u8,11u8,0u8, - 1u8,7u8,35u8,17u8,90u8,39u8,11u8,0u8,11u8,1u8,11u8,3u8,17u8,82u8,12u8,4u8,11u8,2u8,11u8,4u8, - 17u8,22u8,2u8,80u8,1u8,4u8,1u8,15u8,16u8,12u8,11u8,1u8,11u8,2u8,11u8,3u8,11u8,4u8,17u8,14u8, - 12u8,7u8,11u8,0u8,11u8,7u8,11u8,5u8,11u8,6u8,17u8,79u8,2u8,81u8,0u8,0u8,1u8,15u8,128u8,1u8, - 30u8,11u8,0u8,42u8,15u8,15u8,3u8,12u8,6u8,10u8,6u8,10u8,1u8,12u8,5u8,46u8,11u8,5u8,56u8,2u8, - 4u8,12u8,5u8,17u8,11u8,6u8,1u8,7u8,24u8,17u8,85u8,39u8,11u8,6u8,11u8,1u8,56u8,20u8,15u8,26u8, - 12u8,7u8,14u8,2u8,17u8,1u8,11u8,7u8,11u8,2u8,11u8,3u8,11u8,4u8,17u8,124u8,2u8,82u8,1u8,0u8, - 1u8,15u8,1u8,6u8,11u8,0u8,17u8,91u8,11u8,1u8,11u8,2u8,17u8,84u8,2u8,83u8,1u8,0u8,1u8,15u8, - 1u8,21u8,17u8,123u8,14u8,0u8,16u8,43u8,20u8,37u8,4u8,7u8,5u8,10u8,7u8,36u8,17u8,93u8,39u8,14u8, - 0u8,16u8,45u8,20u8,14u8,0u8,16u8,46u8,20u8,14u8,0u8,16u8,44u8,20u8,17u8,84u8,2u8,84u8,0u8,0u8, - 1u8,15u8,130u8,1u8,81u8,10u8,2u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,36u8,4u8,5u8,5u8, - 8u8,7u8,37u8,17u8,93u8,39u8,10u8,0u8,10u8,1u8,17u8,3u8,10u8,2u8,38u8,4u8,15u8,5u8,18u8,7u8, - 11u8,17u8,93u8,39u8,10u8,0u8,41u8,15u8,4u8,22u8,5u8,25u8,7u8,33u8,17u8,85u8,39u8,10u8,0u8,42u8, - 15u8,15u8,47u8,10u8,1u8,10u8,2u8,18u8,17u8,56u8,37u8,11u8,0u8,42u8,15u8,15u8,3u8,12u8,6u8,10u8, - 6u8,10u8,1u8,12u8,3u8,46u8,11u8,3u8,56u8,2u8,4u8,44u8,5u8,49u8,11u8,6u8,1u8,7u8,24u8,17u8, - 85u8,39u8,10u8,6u8,10u8,1u8,56u8,20u8,15u8,4u8,12u8,5u8,10u8,5u8,20u8,10u8,2u8,36u8,4u8,73u8, - 11u8,6u8,1u8,10u8,5u8,20u8,10u8,2u8,23u8,11u8,5u8,21u8,11u8,1u8,11u8,2u8,17u8,107u8,18u8,10u8, - 12u8,4u8,5u8,79u8,11u8,5u8,1u8,11u8,6u8,11u8,1u8,56u8,38u8,12u8,4u8,11u8,4u8,2u8,3u8,0u8, - 12u8,0u8,3u8,1u8,15u8,0u8,10u8,1u8,13u8,0u8,11u8,7u8,15u8,4u8,11u8,0u8,11u8,2u8,12u8,1u8, - 1u8,4u8,1u8,3u8,1u8,1u8,3u8,2u8,3u8,3u8,15u8,2u8,10u8,0u8,15u8,1u8,1u8,0u8,1u8,5u8, - 2u8,0u8,2u8,2u8,2u8,1u8,1u8,2u8,13u8,1u8,10u8,2u8,9u8,1u8,9u8,0u8,9u8,2u8,12u8,2u8, - 14u8,4u8,14u8,3u8,14u8,0u8,14u8,2u8,14u8,1u8,11u8,6u8,11u8,1u8,11u8,8u8,11u8,4u8,11u8,3u8, - 3u8,4u8,15u8,5u8,16u8,3u8,16u8,2u8,16u8,0u8,16u8,1u8,15u8,3u8,0u8, - ]; - vector::push_back(&mut code, chunk2); - let chunk3 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,10u8,1u8,0u8,14u8,2u8,14u8,58u8,3u8,72u8,55u8,5u8, - 127u8,58u8,7u8,185u8,1u8,220u8,4u8,8u8,149u8,6u8,64u8,6u8,213u8,6u8,80u8,16u8,165u8,7u8,182u8,4u8, - 10u8,219u8,11u8,82u8,12u8,173u8,12u8,99u8,0u8,0u8,1u8,1u8,1u8,2u8,1u8,3u8,1u8,4u8,1u8,5u8, - 0u8,6u8,0u8,7u8,6u8,1u8,0u8,1u8,0u8,8u8,4u8,0u8,0u8,9u8,6u8,0u8,0u8,10u8,8u8,1u8, - 0u8,1u8,0u8,11u8,8u8,0u8,0u8,12u8,6u8,0u8,6u8,13u8,7u8,0u8,6u8,15u8,4u8,0u8,3u8,18u8, - 7u8,0u8,5u8,32u8,7u8,0u8,4u8,34u8,4u8,2u8,3u8,1u8,0u8,1u8,2u8,36u8,4u8,1u8,6u8,1u8, - 0u8,14u8,0u8,1u8,1u8,0u8,0u8,16u8,2u8,1u8,0u8,0u8,17u8,3u8,4u8,1u8,0u8,0u8,19u8,5u8, - 1u8,1u8,0u8,0u8,20u8,6u8,1u8,1u8,0u8,0u8,21u8,6u8,1u8,0u8,0u8,22u8,7u8,1u8,1u8,0u8, - 0u8,23u8,0u8,8u8,0u8,0u8,24u8,9u8,8u8,0u8,1u8,41u8,10u8,10u8,0u8,3u8,6u8,12u8,8u8,6u8, - 3u8,0u8,4u8,6u8,12u8,8u8,6u8,8u8,7u8,3u8,2u8,5u8,8u8,6u8,1u8,1u8,8u8,6u8,12u8,3u8, - 5u8,5u8,8u8,8u8,8u8,8u8,3u8,3u8,1u8,6u8,12u8,8u8,6u8,12u8,5u8,8u8,8u8,8u8,8u8,3u8, - 3u8,3u8,3u8,1u8,8u8,7u8,3u8,5u8,8u8,6u8,3u8,1u8,3u8,15u8,116u8,111u8,107u8,101u8,110u8,95u8, - 99u8,111u8,105u8,110u8,95u8,115u8,119u8,97u8,112u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8, - 116u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,5u8,116u8,97u8,98u8,108u8,101u8,9u8,116u8,121u8,112u8,101u8,95u8, - 105u8,110u8,102u8,111u8,5u8,116u8,111u8,107u8,101u8,110u8,13u8,84u8,111u8,107u8,101u8,110u8,67u8,111u8,105u8,110u8, - 83u8,119u8,97u8,112u8,11u8,84u8,111u8,107u8,101u8,110u8,69u8,115u8,99u8,114u8,111u8,119u8,17u8,84u8,111u8,107u8, - 101u8,110u8,76u8,105u8,115u8,116u8,105u8,110u8,103u8,69u8,118u8,101u8,110u8,116u8,13u8,84u8,111u8,107u8,101u8,110u8, - 76u8,105u8,115u8,116u8,105u8,110u8,103u8,115u8,16u8,84u8,111u8,107u8,101u8,110u8,83u8,116u8,111u8,114u8,101u8,69u8, - 115u8,99u8,114u8,111u8,119u8,14u8,84u8,111u8,107u8,101u8,110u8,83u8,119u8,97u8,112u8,69u8,118u8,101u8,110u8,116u8, - 7u8,84u8,111u8,107u8,101u8,110u8,73u8,100u8,20u8,99u8,97u8,110u8,99u8,101u8,108u8,95u8,116u8,111u8,107u8,101u8, - 110u8,95u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,5u8,84u8,111u8,107u8,101u8,110u8,23u8,100u8,101u8,112u8,111u8, - 115u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,116u8,111u8,95u8,101u8,115u8,99u8,114u8,111u8,119u8,18u8, - 100u8,111u8,101u8,115u8,95u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,95u8,101u8,120u8,105u8,115u8,116u8,6u8,83u8, - 116u8,114u8,105u8,110u8,103u8,23u8,101u8,120u8,99u8,104u8,97u8,110u8,103u8,101u8,95u8,99u8,111u8,105u8,110u8,95u8, - 102u8,111u8,114u8,95u8,116u8,111u8,107u8,101u8,110u8,24u8,105u8,110u8,105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8, - 95u8,116u8,111u8,107u8,101u8,110u8,95u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,29u8,105u8,110u8,105u8,116u8,105u8, - 97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,115u8,116u8,111u8,114u8,101u8,95u8,101u8,115u8, - 99u8,114u8,111u8,119u8,19u8,108u8,105u8,115u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,102u8,111u8,114u8,95u8, - 115u8,119u8,97u8,112u8,26u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8, - 102u8,114u8,111u8,109u8,95u8,101u8,115u8,99u8,114u8,111u8,119u8,35u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8, - 95u8,116u8,111u8,107u8,101u8,110u8,95u8,102u8,114u8,111u8,109u8,95u8,101u8,115u8,99u8,114u8,111u8,119u8,95u8,105u8, - 110u8,116u8,101u8,114u8,110u8,97u8,108u8,12u8,116u8,111u8,107u8,101u8,110u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8, - 19u8,109u8,105u8,110u8,95u8,112u8,114u8,105u8,99u8,101u8,95u8,112u8,101u8,114u8,95u8,116u8,111u8,107u8,101u8,110u8, - 17u8,108u8,111u8,99u8,107u8,101u8,100u8,95u8,117u8,110u8,116u8,105u8,108u8,95u8,115u8,101u8,99u8,115u8,8u8,116u8, - 111u8,107u8,101u8,110u8,95u8,105u8,100u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,9u8,109u8,105u8,110u8,95u8,112u8, - 114u8,105u8,99u8,101u8,14u8,99u8,111u8,105u8,110u8,95u8,116u8,121u8,112u8,101u8,95u8,105u8,110u8,102u8,111u8,8u8, - 84u8,121u8,112u8,101u8,73u8,110u8,102u8,111u8,8u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,115u8,5u8,84u8,97u8, - 98u8,108u8,101u8,14u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,11u8,69u8, - 118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,11u8,115u8,119u8,97u8,112u8,95u8,101u8,118u8,101u8,110u8, - 116u8,115u8,13u8,116u8,111u8,107u8,101u8,110u8,95u8,101u8,115u8,99u8,114u8,111u8,119u8,115u8,11u8,116u8,111u8,107u8, - 101u8,110u8,95u8,98u8,117u8,121u8,101u8,114u8,11u8,99u8,111u8,105u8,110u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8, - 16u8,105u8,110u8,118u8,97u8,108u8,105u8,100u8,95u8,97u8,114u8,103u8,117u8,109u8,101u8,110u8,116u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 1u8,3u8,8u8,8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,2u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,3u8,8u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,8u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8,58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8, - 161u8,4u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,21u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,65u8, - 76u8,82u8,69u8,65u8,68u8,89u8,95u8,76u8,73u8,83u8,84u8,69u8,68u8,20u8,84u8,111u8,107u8,101u8,110u8,32u8, - 97u8,108u8,114u8,101u8,97u8,100u8,121u8,32u8,108u8,105u8,115u8,116u8,101u8,100u8,2u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,24u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,76u8,73u8,83u8,84u8,73u8,78u8,71u8,95u8,78u8,79u8, - 84u8,95u8,69u8,88u8,73u8,83u8,84u8,30u8,84u8,111u8,107u8,101u8,110u8,32u8,108u8,105u8,115u8,116u8,105u8,110u8, - 103u8,32u8,110u8,111u8,32u8,108u8,111u8,110u8,103u8,101u8,114u8,32u8,101u8,120u8,105u8,115u8,116u8,115u8,3u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,20u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,78u8,79u8,84u8,95u8,73u8,78u8, - 95u8,69u8,83u8,67u8,82u8,79u8,87u8,22u8,84u8,111u8,107u8,101u8,110u8,32u8,105u8,115u8,32u8,110u8,111u8,116u8, - 32u8,105u8,110u8,32u8,101u8,115u8,99u8,114u8,111u8,119u8,4u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,51u8,69u8, - 84u8,79u8,75u8,69u8,78u8,95u8,67u8,65u8,78u8,78u8,79u8,84u8,95u8,77u8,79u8,86u8,69u8,95u8,79u8,85u8, - 84u8,95u8,79u8,70u8,95u8,69u8,83u8,67u8,82u8,79u8,87u8,95u8,66u8,69u8,70u8,79u8,82u8,69u8,95u8,76u8, - 79u8,67u8,75u8,85u8,80u8,95u8,84u8,73u8,77u8,69u8,58u8,84u8,111u8,107u8,101u8,110u8,32u8,99u8,97u8,110u8, - 110u8,111u8,116u8,32u8,98u8,101u8,32u8,109u8,111u8,118u8,101u8,100u8,32u8,111u8,117u8,116u8,32u8,111u8,102u8,32u8, - 101u8,115u8,99u8,114u8,111u8,119u8,32u8,98u8,101u8,102u8,111u8,114u8,101u8,32u8,116u8,104u8,101u8,32u8,108u8,111u8, - 99u8,107u8,117u8,112u8,32u8,116u8,105u8,109u8,101u8,5u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,26u8,69u8,84u8, - 79u8,75u8,69u8,78u8,95u8,77u8,73u8,78u8,95u8,80u8,82u8,73u8,67u8,69u8,95u8,78u8,79u8,84u8,95u8,77u8, - 65u8,84u8,67u8,72u8,43u8,84u8,111u8,107u8,101u8,110u8,32u8,98u8,117u8,121u8,32u8,112u8,114u8,105u8,99u8,101u8, - 32u8,100u8,111u8,101u8,115u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,32u8,108u8,105u8,115u8,116u8,105u8, - 110u8,103u8,32u8,112u8,114u8,105u8,99u8,101u8,6u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,23u8,69u8,84u8,79u8, - 75u8,69u8,78u8,95u8,65u8,77u8,79u8,85u8,78u8,84u8,95u8,78u8,79u8,84u8,95u8,77u8,65u8,84u8,67u8,72u8, - 45u8,84u8,111u8,107u8,101u8,110u8,32u8,98u8,117u8,121u8,32u8,97u8,109u8,111u8,117u8,110u8,116u8,32u8,100u8,111u8, - 101u8,115u8,110u8,39u8,116u8,32u8,109u8,97u8,116u8,99u8,104u8,32u8,108u8,105u8,115u8,116u8,105u8,110u8,103u8,32u8, - 97u8,109u8,111u8,117u8,110u8,116u8,7u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,16u8,69u8,78u8,79u8,84u8,95u8, - 69u8,78u8,79u8,85u8,71u8,72u8,95u8,67u8,79u8,73u8,78u8,28u8,78u8,111u8,116u8,32u8,101u8,110u8,111u8,117u8, - 103u8,104u8,32u8,99u8,111u8,105u8,110u8,32u8,116u8,111u8,32u8,98u8,117u8,121u8,32u8,116u8,111u8,107u8,101u8,110u8, - 8u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,69u8,68u8,69u8,80u8,82u8,69u8,67u8,65u8,84u8,69u8,68u8, - 95u8,77u8,79u8,68u8,85u8,76u8,69u8,17u8,68u8,101u8,112u8,114u8,101u8,99u8,97u8,116u8,101u8,100u8,32u8,109u8, - 111u8,100u8,117u8,108u8,101u8,0u8,0u8,0u8,2u8,2u8,25u8,3u8,26u8,3u8,1u8,2u8,2u8,6u8,8u8,7u8, - 27u8,3u8,2u8,2u8,5u8,28u8,8u8,6u8,29u8,3u8,30u8,3u8,27u8,3u8,31u8,8u8,9u8,3u8,2u8,3u8, - 33u8,11u8,10u8,2u8,8u8,6u8,11u8,0u8,1u8,9u8,0u8,35u8,11u8,11u8,1u8,8u8,2u8,37u8,11u8,11u8, - 1u8,8u8,5u8,4u8,2u8,1u8,38u8,11u8,10u8,2u8,8u8,6u8,8u8,1u8,5u8,2u8,5u8,28u8,8u8,6u8, - 39u8,5u8,25u8,3u8,40u8,3u8,31u8,8u8,9u8,0u8,1u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8, - 1u8,1u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8,2u8,1u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8, - 9u8,39u8,3u8,1u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8,4u8,0u8,0u8,0u8,1u8,3u8,7u8, - 0u8,17u8,9u8,39u8,5u8,0u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8,6u8,1u8,4u8,0u8,1u8, - 3u8,7u8,0u8,17u8,9u8,39u8,7u8,1u8,0u8,0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8,8u8,0u8,0u8, - 0u8,1u8,3u8,7u8,0u8,17u8,9u8,39u8,0u8, - ]; - vector::push_back(&mut code, chunk3); - let chunk4 = - vector[ - 161u8,28u8,235u8,11u8,6u8,0u8,0u8,0u8,12u8,1u8,0u8,16u8,2u8,16u8,46u8,3u8,62u8,122u8,4u8, - 184u8,1u8,22u8,5u8,206u8,1u8,201u8,1u8,7u8,151u8,3u8,170u8,4u8,8u8,193u8,7u8,64u8,6u8,129u8,8u8, - 10u8,16u8,139u8,8u8,80u8,10u8,219u8,8u8,67u8,12u8,158u8,9u8,151u8,3u8,13u8,181u8,12u8,8u8,0u8,0u8, - 1u8,1u8,1u8,2u8,1u8,3u8,1u8,4u8,1u8,5u8,1u8,6u8,0u8,7u8,0u8,8u8,8u8,0u8,0u8,9u8, - 6u8,0u8,0u8,10u8,6u8,0u8,0u8,11u8,6u8,0u8,0u8,12u8,7u8,0u8,7u8,13u8,7u8,0u8,5u8,15u8, - 7u8,0u8,6u8,24u8,4u8,2u8,3u8,1u8,0u8,1u8,7u8,25u8,4u8,0u8,3u8,27u8,4u8,1u8,6u8,1u8, - 0u8,14u8,0u8,1u8,0u8,0u8,16u8,2u8,1u8,0u8,0u8,17u8,0u8,1u8,0u8,0u8,18u8,2u8,1u8,0u8, - 0u8,19u8,3u8,4u8,0u8,0u8,20u8,5u8,1u8,0u8,0u8,21u8,6u8,1u8,0u8,0u8,22u8,7u8,1u8,0u8, - 4u8,34u8,5u8,9u8,0u8,6u8,35u8,11u8,12u8,2u8,3u8,0u8,7u8,36u8,13u8,14u8,0u8,7u8,37u8,15u8, - 1u8,0u8,3u8,38u8,17u8,1u8,1u8,6u8,7u8,39u8,19u8,18u8,0u8,6u8,40u8,21u8,22u8,2u8,3u8,0u8, - 2u8,41u8,14u8,14u8,0u8,6u8,42u8,1u8,24u8,2u8,3u8,4u8,1u8,43u8,5u8,26u8,1u8,6u8,7u8,44u8, - 28u8,29u8,0u8,6u8,45u8,30u8,1u8,2u8,3u8,0u8,6u8,46u8,11u8,31u8,2u8,3u8,0u8,7u8,47u8,32u8, - 1u8,0u8,9u8,10u8,12u8,16u8,14u8,10u8,12u8,23u8,16u8,10u8,17u8,25u8,17u8,16u8,17u8,23u8,19u8,10u8, - 20u8,10u8,12u8,25u8,3u8,6u8,12u8,5u8,8u8,5u8,0u8,6u8,12u8,5u8,5u8,8u8,6u8,8u8,6u8,3u8, - 2u8,5u8,8u8,5u8,1u8,8u8,4u8,1u8,6u8,12u8,4u8,6u8,12u8,5u8,8u8,5u8,3u8,7u8,12u8,5u8, - 5u8,8u8,6u8,8u8,6u8,3u8,3u8,4u8,3u8,5u8,8u8,8u8,8u8,4u8,1u8,5u8,2u8,8u8,4u8,8u8, - 8u8,2u8,7u8,11u8,7u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,9u8,1u8,1u8,6u8,8u8,8u8,1u8, - 3u8,2u8,6u8,12u8,8u8,8u8,1u8,8u8,1u8,2u8,7u8,11u8,9u8,1u8,9u8,0u8,9u8,0u8,1u8,8u8, - 5u8,4u8,5u8,8u8,6u8,8u8,6u8,3u8,5u8,8u8,4u8,3u8,7u8,11u8,7u8,2u8,8u8,4u8,8u8,8u8, - 8u8,4u8,8u8,8u8,2u8,6u8,11u8,7u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,1u8,1u8,1u8,8u8,2u8, - 1u8,11u8,7u8,2u8,9u8,0u8,9u8,1u8,1u8,8u8,3u8,1u8,11u8,9u8,1u8,9u8,0u8,5u8,8u8,4u8, - 7u8,11u8,7u8,2u8,8u8,4u8,8u8,8u8,5u8,8u8,8u8,8u8,4u8,3u8,6u8,12u8,8u8,5u8,3u8,1u8, - 8u8,8u8,3u8,7u8,11u8,7u8,2u8,9u8,0u8,9u8,1u8,9u8,0u8,9u8,1u8,1u8,7u8,9u8,1u8,2u8, - 7u8,8u8,8u8,8u8,8u8,15u8,116u8,111u8,107u8,101u8,110u8,95u8,116u8,114u8,97u8,110u8,115u8,102u8,101u8,114u8, - 115u8,7u8,97u8,99u8,99u8,111u8,117u8,110u8,116u8,5u8,101u8,114u8,114u8,111u8,114u8,5u8,101u8,118u8,101u8,110u8, - 116u8,6u8,115u8,105u8,103u8,110u8,101u8,114u8,6u8,115u8,116u8,114u8,105u8,110u8,103u8,5u8,116u8,97u8,98u8,108u8, - 101u8,5u8,116u8,111u8,107u8,101u8,110u8,13u8,80u8,101u8,110u8,100u8,105u8,110u8,103u8,67u8,108u8,97u8,105u8,109u8, - 115u8,21u8,84u8,111u8,107u8,101u8,110u8,67u8,97u8,110u8,99u8,101u8,108u8,79u8,102u8,102u8,101u8,114u8,69u8,118u8, - 101u8,110u8,116u8,15u8,84u8,111u8,107u8,101u8,110u8,67u8,108u8,97u8,105u8,109u8,69u8,118u8,101u8,110u8,116u8,15u8, - 84u8,111u8,107u8,101u8,110u8,79u8,102u8,102u8,101u8,114u8,69u8,118u8,101u8,110u8,116u8,12u8,84u8,111u8,107u8,101u8, - 110u8,79u8,102u8,102u8,101u8,114u8,73u8,100u8,7u8,84u8,111u8,107u8,101u8,110u8,73u8,100u8,12u8,99u8,97u8,110u8, - 99u8,101u8,108u8,95u8,111u8,102u8,102u8,101u8,114u8,6u8,83u8,116u8,114u8,105u8,110u8,103u8,19u8,99u8,97u8,110u8, - 99u8,101u8,108u8,95u8,111u8,102u8,102u8,101u8,114u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,5u8,99u8,108u8,97u8, - 105u8,109u8,12u8,99u8,108u8,97u8,105u8,109u8,95u8,115u8,99u8,114u8,105u8,112u8,116u8,21u8,99u8,114u8,101u8,97u8, - 116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,111u8,102u8,102u8,101u8,114u8,95u8,105u8,100u8,26u8,105u8,110u8, - 105u8,116u8,105u8,97u8,108u8,105u8,122u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,116u8,114u8,97u8,110u8,115u8, - 102u8,101u8,114u8,115u8,5u8,111u8,102u8,102u8,101u8,114u8,12u8,111u8,102u8,102u8,101u8,114u8,95u8,115u8,99u8,114u8, - 105u8,112u8,116u8,14u8,112u8,101u8,110u8,100u8,105u8,110u8,103u8,95u8,99u8,108u8,97u8,105u8,109u8,115u8,5u8,84u8, - 97u8,98u8,108u8,101u8,5u8,84u8,111u8,107u8,101u8,110u8,12u8,111u8,102u8,102u8,101u8,114u8,95u8,101u8,118u8,101u8, - 110u8,116u8,115u8,11u8,69u8,118u8,101u8,110u8,116u8,72u8,97u8,110u8,100u8,108u8,101u8,19u8,99u8,97u8,110u8,99u8, - 101u8,108u8,95u8,111u8,102u8,102u8,101u8,114u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,12u8,99u8,108u8,97u8,105u8, - 109u8,95u8,101u8,118u8,101u8,110u8,116u8,115u8,10u8,116u8,111u8,95u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,8u8, - 116u8,111u8,107u8,101u8,110u8,95u8,105u8,100u8,6u8,97u8,109u8,111u8,117u8,110u8,116u8,7u8,116u8,111u8,95u8,97u8, - 100u8,100u8,114u8,10u8,97u8,100u8,100u8,114u8,101u8,115u8,115u8,95u8,111u8,102u8,6u8,114u8,101u8,109u8,111u8,118u8, - 101u8,16u8,103u8,101u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,97u8,109u8,111u8,117u8,110u8,116u8,13u8,100u8, - 101u8,112u8,111u8,115u8,105u8,116u8,95u8,116u8,111u8,107u8,101u8,110u8,10u8,101u8,109u8,105u8,116u8,95u8,101u8,118u8, - 101u8,110u8,116u8,19u8,99u8,114u8,101u8,97u8,116u8,101u8,95u8,116u8,111u8,107u8,101u8,110u8,95u8,105u8,100u8,95u8, - 114u8,97u8,119u8,8u8,99u8,111u8,110u8,116u8,97u8,105u8,110u8,115u8,9u8,110u8,111u8,116u8,95u8,102u8,111u8,117u8, - 110u8,100u8,3u8,110u8,101u8,119u8,16u8,110u8,101u8,119u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,104u8,97u8,110u8, - 100u8,108u8,101u8,14u8,119u8,105u8,116u8,104u8,100u8,114u8,97u8,119u8,95u8,116u8,111u8,107u8,101u8,110u8,3u8,97u8, - 100u8,100u8,10u8,98u8,111u8,114u8,114u8,111u8,119u8,95u8,109u8,117u8,116u8,5u8,109u8,101u8,114u8,103u8,101u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,1u8,3u8,8u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,18u8,97u8,112u8,116u8,111u8,115u8,58u8, - 58u8,109u8,101u8,116u8,97u8,100u8,97u8,116u8,97u8,95u8,118u8,49u8,60u8,1u8,1u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,22u8,69u8,84u8,79u8,75u8,69u8,78u8,95u8,79u8,70u8,70u8,69u8,82u8,95u8,78u8,79u8,84u8,95u8, - 69u8,88u8,73u8,83u8,84u8,25u8,84u8,111u8,107u8,101u8,110u8,32u8,111u8,102u8,102u8,101u8,114u8,32u8,100u8,111u8, - 101u8,115u8,110u8,39u8,116u8,32u8,101u8,120u8,105u8,115u8,116u8,0u8,0u8,0u8,2u8,4u8,23u8,11u8,7u8,2u8, - 8u8,4u8,8u8,8u8,26u8,11u8,9u8,1u8,8u8,3u8,28u8,11u8,9u8,1u8,8u8,1u8,29u8,11u8,9u8,1u8, - 8u8,2u8,1u8,2u8,3u8,30u8,5u8,31u8,8u8,5u8,32u8,3u8,2u8,2u8,3u8,30u8,5u8,31u8,8u8,5u8, - 32u8,3u8,3u8,2u8,3u8,30u8,5u8,31u8,8u8,5u8,32u8,3u8,4u8,2u8,2u8,33u8,5u8,31u8,8u8,5u8, - 0u8,1u8,0u8,1u8,0u8,8u8,36u8,10u8,0u8,17u8,8u8,12u8,4u8,10u8,1u8,10u8,2u8,17u8,4u8,12u8, - 6u8,10u8,4u8,41u8,0u8,4u8,11u8,5u8,15u8,11u8,0u8,1u8,7u8,0u8,39u8,10u8,4u8,42u8,0u8,15u8, - 0u8,11u8,6u8,56u8,0u8,12u8,5u8,14u8,5u8,17u8,10u8,12u8,3u8,11u8,0u8,11u8,5u8,17u8,11u8,11u8, - 4u8,42u8,0u8,15u8,1u8,11u8,1u8,11u8,2u8,11u8,3u8,18u8,1u8,56u8,1u8,2u8,1u8,1u8,4u8,1u8, - 0u8,18u8,11u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,17u8,13u8,12u8,6u8,14u8,0u8,11u8,1u8,11u8, - 6u8,17u8,0u8,2u8,2u8,1u8,0u8,1u8,0u8,20u8,52u8,10u8,1u8,41u8,0u8,4u8,4u8,5u8,8u8,11u8, - 0u8,1u8,7u8,0u8,39u8,10u8,1u8,42u8,0u8,15u8,0u8,12u8,5u8,10u8,0u8,17u8,8u8,10u8,2u8,17u8, - 4u8,12u8,6u8,10u8,5u8,10u8,6u8,12u8,3u8,46u8,11u8,3u8,56u8,2u8,4u8,25u8,5u8,32u8,11u8,0u8, - 1u8,11u8,5u8,1u8,7u8,0u8,17u8,15u8,39u8,11u8,5u8,11u8,6u8,56u8,0u8,12u8,7u8,14u8,7u8,17u8, - 10u8,12u8,4u8,10u8,0u8,11u8,7u8,17u8,11u8,11u8,1u8,42u8,0u8,15u8,2u8,11u8,0u8,17u8,8u8,11u8, - 2u8,11u8,4u8,18u8,2u8,56u8,3u8,2u8,3u8,1u8,4u8,1u8,0u8,18u8,11u8,11u8,2u8,11u8,3u8,11u8, - 4u8,11u8,5u8,17u8,13u8,12u8,6u8,14u8,0u8,11u8,1u8,11u8,6u8,17u8,2u8,2u8,4u8,0u8,0u8,0u8, - 1u8,4u8,11u8,0u8,11u8,1u8,18u8,4u8,2u8,5u8,0u8,0u8,0u8,1u8,11u8,10u8,0u8,56u8,4u8,10u8, - 0u8,56u8,5u8,10u8,0u8,56u8,6u8,11u8,0u8,56u8,7u8,18u8,0u8,45u8,0u8,2u8,6u8,1u8,0u8,1u8, - 0u8,27u8,49u8,10u8,0u8,17u8,8u8,12u8,6u8,10u8,6u8,41u8,0u8,32u8,4u8,9u8,10u8,0u8,17u8,5u8, - 10u8,6u8,42u8,0u8,15u8,0u8,12u8,5u8,10u8,1u8,10u8,2u8,17u8,4u8,12u8,8u8,11u8,0u8,10u8,2u8, - 10u8,3u8,17u8,18u8,12u8,7u8,10u8,5u8,10u8,8u8,12u8,4u8,46u8,11u8,4u8,56u8,2u8,32u8,4u8,35u8, - 11u8,5u8,11u8,8u8,11u8,7u8,56u8,8u8,5u8,40u8,11u8,5u8,11u8,8u8,56u8,9u8,11u8,7u8,17u8,21u8, - 11u8,6u8,42u8,0u8,15u8,3u8,11u8,1u8,11u8,2u8,11u8,3u8,18u8,3u8,56u8,10u8,2u8,7u8,1u8,4u8, - 1u8,0u8,18u8,12u8,11u8,2u8,11u8,3u8,11u8,4u8,11u8,5u8,17u8,13u8,12u8,7u8,14u8,0u8,11u8,1u8, - 11u8,7u8,11u8,6u8,17u8,6u8,2u8,0u8,0u8,0u8,2u8,0u8,3u8,0u8,1u8,0u8, - ]; - vector::push_back(&mut code, chunk4); - let chunk1 = vector[ - 10u8,65u8,112u8,116u8,111u8,115u8,84u8,111u8,107u8,101u8,110u8,1u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,64u8,48u8,69u8,48u8,56u8,65u8,55u8,51u8,55u8,52u8,54u8,51u8,67u8,50u8,51u8,51u8,66u8,49u8,48u8, - 48u8,69u8,50u8,69u8,56u8,55u8,55u8,65u8,56u8,69u8,67u8,49u8,49u8,67u8,57u8,65u8,49u8,66u8,65u8,70u8, - 52u8,55u8,51u8,50u8,54u8,49u8,55u8,48u8,49u8,52u8,57u8,54u8,50u8,54u8,55u8,48u8,54u8,68u8,65u8,67u8, - 50u8,55u8,70u8,51u8,70u8,70u8,166u8,1u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,93u8,142u8, - 189u8,10u8,194u8,48u8,20u8,133u8,247u8,251u8,20u8,33u8,123u8,99u8,139u8,179u8,131u8,139u8,155u8,147u8,110u8,165u8, - 72u8,108u8,174u8,82u8,154u8,38u8,33u8,55u8,84u8,65u8,250u8,238u8,230u8,70u8,16u8,237u8,122u8,126u8,190u8,115u8, - 218u8,160u8,251u8,81u8,223u8,177u8,3u8,167u8,39u8,20u8,59u8,33u8,247u8,33u8,121u8,58u8,251u8,17u8,157u8,132u8, - 25u8,35u8,13u8,222u8,177u8,220u8,168u8,90u8,213u8,18u8,160u8,213u8,198u8,68u8,36u8,66u8,234u8,128u8,146u8,97u8, - 171u8,126u8,54u8,18u8,52u8,215u8,46u8,183u8,152u8,41u8,15u8,31u8,199u8,149u8,158u8,152u8,247u8,209u8,182u8,12u8, - 49u8,24u8,208u8,25u8,116u8,253u8,192u8,156u8,163u8,159u8,241u8,148u8,140u8,29u8,174u8,57u8,242u8,18u8,214u8,247u8, - 218u8,114u8,88u8,169u8,205u8,148u8,173u8,138u8,138u8,39u8,197u8,2u8,229u8,220u8,225u8,103u8,228u8,63u8,93u8,198u8, - 170u8,239u8,9u8,185u8,192u8,27u8,88u8,109u8,165u8,157u8,224u8,0u8,0u8,0u8,5u8,12u8,112u8,114u8,111u8,112u8, - 101u8,114u8,116u8,121u8,95u8,109u8,97u8,112u8,234u8,18u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8, - 237u8,26u8,105u8,111u8,219u8,70u8,246u8,187u8,127u8,197u8,84u8,11u8,168u8,100u8,150u8,245u8,213u8,52u8,49u8,100u8, - 43u8,88u8,175u8,43u8,180u8,198u8,198u8,7u8,98u8,165u8,104u8,54u8,8u8,8u8,74u8,26u8,217u8,68u8,40u8,146u8, - 224u8,33u8,91u8,77u8,253u8,223u8,251u8,222u8,28u8,228u8,204u8,112u8,72u8,75u8,118u8,220u8,246u8,67u8,3u8,180u8, - 137u8,135u8,111u8,222u8,125u8,143u8,119u8,118u8,118u8,200u8,101u8,150u8,164u8,52u8,43u8,86u8,103u8,65u8,74u8,194u8, - 156u8,4u8,36u8,79u8,233u8,52u8,12u8,162u8,240u8,183u8,160u8,8u8,147u8,152u8,36u8,115u8,114u8,21u8,46u8,210u8, - 136u8,34u8,192u8,60u8,201u8,200u8,56u8,249u8,76u8,227u8,124u8,123u8,107u8,7u8,46u8,159u8,22u8,100u8,17u8,164u8, - 120u8,235u8,170u8,200u8,194u8,248u8,154u8,124u8,166u8,43u8,82u8,36u8,240u8,179u8,68u8,251u8,75u8,16u8,149u8,148u8, - 20u8,55u8,65u8,65u8,166u8,73u8,156u8,135u8,121u8,145u8,35u8,202u8,98u8,149u8,82u8,226u8,228u8,236u8,146u8,75u8, - 130u8,120u8,70u8,150u8,12u8,208u8,89u8,210u8,105u8,145u8,100u8,71u8,229u8,193u8,27u8,87u8,82u8,72u8,179u8,100u8, - 25u8,206u8,104u8,78u8,38u8,65u8,30u8,78u8,73u8,18u8,127u8,55u8,189u8,9u8,194u8,152u8,228u8,52u8,211u8,249u8, - 76u8,179u8,112u8,17u8,22u8,225u8,146u8,50u8,132u8,28u8,57u8,114u8,147u8,10u8,94u8,4u8,141u8,219u8,176u8,184u8, - 225u8,12u8,132u8,49u8,8u8,180u8,96u8,247u8,37u8,177u8,32u8,202u8,19u8,146u8,151u8,105u8,154u8,100u8,192u8,41u8, - 80u8,21u8,68u8,16u8,149u8,129u8,7u8,48u8,135u8,5u8,73u8,178u8,240u8,58u8,140u8,131u8,136u8,97u8,220u8,222u8, - 90u8,36u8,179u8,50u8,2u8,6u8,210u8,34u8,201u8,253u8,2u8,85u8,53u8,24u8,200u8,123u8,62u8,232u8,138u8,124u8, - 217u8,34u8,240u8,167u8,204u8,41u8,48u8,56u8,27u8,12u8,38u8,211u8,252u8,80u8,63u8,225u8,26u8,48u8,14u8,105u8, - 150u8,53u8,206u8,184u8,128u8,131u8,193u8,151u8,43u8,26u8,205u8,61u8,97u8,129u8,251u8,26u8,136u8,243u8,192u8,64u8, - 231u8,89u8,178u8,240u8,53u8,82u8,202u8,199u8,156u8,153u8,23u8,153u8,171u8,113u8,73u8,139u8,219u8,209u8,161u8,168u8, - 62u8,42u8,79u8,252u8,51u8,14u8,22u8,244u8,112u8,139u8,65u8,238u8,236u8,136u8,191u8,200u8,9u8,152u8,187u8,8u8, - 226u8,34u8,215u8,207u8,119u8,200u8,248u8,134u8,130u8,215u8,220u8,133u8,11u8,80u8,90u8,92u8,46u8,38u8,52u8,227u8, - 198u8,19u8,202u8,229u8,190u8,18u8,196u8,100u8,130u8,114u8,38u8,25u8,157u8,129u8,157u8,234u8,207u8,192u8,38u8,195u8, - 132u8,222u8,84u8,144u8,179u8,227u8,95u8,253u8,203u8,119u8,23u8,151u8,163u8,119u8,227u8,15u8,254u8,217u8,241u8,165u8, - 127u8,117u8,250u8,255u8,209u8,128u8,148u8,175u8,94u8,146u8,33u8,217u8,219u8,221u8,221u8,61u8,108u8,3u8,62u8,63u8, - 62u8,27u8,249u8,111u8,71u8,231u8,63u8,141u8,127u8,174u8,224u8,247u8,15u8,64u8,10u8,67u8,140u8,17u8,106u8,30u8, - 220u8,221u8,34u8,68u8,197u8,19u8,122u8,125u8,16u8,101u8,52u8,152u8,173u8,8u8,189u8,67u8,31u8,87u8,168u8,142u8, - 254u8,55u8,250u8,224u8,31u8,191u8,27u8,29u8,255u8,248u8,193u8,31u8,253u8,122u8,122u8,53u8,246u8,79u8,207u8,53u8, - 158u8,43u8,242u8,149u8,10u8,57u8,122u8,139u8,110u8,232u8,221u8,148u8,210u8,89u8,14u8,58u8,162u8,36u8,66u8,103u8, - 87u8,233u8,212u8,162u8,189u8,63u8,251u8,239u8,232u8,29u8,16u8,59u8,25u8,141u8,126u8,244u8,223u8,158u8,158u8,157u8, - 142u8,37u8,141u8,125u8,131u8,70u8,133u8,121u8,150u8,208u8,60u8,254u8,182u8,224u8,236u8,219u8,177u8,94u8,140u8,57u8, - 255u8,18u8,215u8,247u8,10u8,174u8,75u8,77u8,21u8,85u8,56u8,79u8,147u8,50u8,46u8,0u8,55u8,98u8,134u8,56u8, - 155u8,222u8,152u8,122u8,57u8,185u8,120u8,127u8,62u8,102u8,168u8,207u8,142u8,199u8,39u8,63u8,251u8,191u8,28u8,191u8, - 125u8,63u8,226u8,167u8,146u8,204u8,203u8,46u8,50u8,44u8,140u8,55u8,166u8,50u8,254u8,112u8,105u8,16u8,249u8,193u8, - 70u8,132u8,33u8,151u8,138u8,105u8,32u8,102u8,72u8,42u8,156u8,18u8,209u8,43u8,211u8,136u8,16u8,26u8,196u8,1u8, - 118u8,93u8,150u8,246u8,84u8,149u8,67u8,182u8,45u8,146u8,132u8,68u8,73u8,124u8,109u8,85u8,56u8,186u8,51u8,243u8, - 210u8,241u8,197u8,133u8,255u8,246u8,226u8,252u8,39u8,73u8,227u8,117u8,211u8,75u8,33u8,248u8,203u8,105u8,29u8,106u8, - 236u8,239u8,156u8,157u8,105u8,9u8,254u8,38u8,200u8,129u8,76u8,186u8,242u8,200u8,12u8,78u8,61u8,30u8,95u8,34u8, - 37u8,225u8,31u8,204u8,1u8,117u8,240u8,31u8,241u8,148u8,226u8,233u8,201u8,252u8,141u8,199u8,192u8,239u8,173u8,68u8, - 120u8,186u8,71u8,50u8,12u8,181u8,167u8,80u8,83u8,200u8,48u8,231u8,24u8,144u8,58u8,215u8,123u8,213u8,39u8,212u8, - 249u8,64u8,228u8,50u8,141u8,80u8,90u8,78u8,34u8,200u8,254u8,243u8,50u8,38u8,49u8,189u8,117u8,170u8,11u8,160u8, - 218u8,188u8,66u8,197u8,239u8,41u8,232u8,24u8,165u8,250u8,123u8,77u8,209u8,32u8,217u8,64u8,193u8,190u8,186u8,3u8, - 77u8,123u8,181u8,0u8,17u8,45u8,224u8,191u8,248u8,26u8,10u8,201u8,80u8,92u8,28u8,12u8,248u8,129u8,211u8,71u8, - 142u8,220u8,195u8,10u8,54u8,200u8,161u8,134u8,20u8,223u8,56u8,2u8,254u8,104u8,104u8,79u8,90u8,30u8,97u8,57u8, - 126u8,48u8,8u8,99u8,96u8,58u8,156u8,249u8,65u8,118u8,93u8,46u8,104u8,92u8,56u8,157u8,129u8,237u8,182u8,83u8, - 26u8,54u8,89u8,227u8,234u8,112u8,59u8,104u8,61u8,16u8,148u8,155u8,145u8,99u8,154u8,221u8,148u8,90u8,29u8,156u8, - 72u8,76u8,83u8,185u8,8u8,157u8,16u8,122u8,129u8,33u8,161u8,139u8,180u8,88u8,57u8,38u8,72u8,8u8,95u8,118u8, - 107u8,30u8,111u8,111u8,66u8,168u8,198u8,78u8,72u8,142u8,132u8,189u8,92u8,197u8,138u8,242u8,14u8,38u8,147u8,33u8, - 121u8,33u8,185u8,159u8,36u8,192u8,238u8,45u8,183u8,163u8,71u8,66u8,69u8,98u8,85u8,106u8,89u8,125u8,21u8,171u8, - 187u8,13u8,227u8,42u8,69u8,102u8,29u8,251u8,54u8,34u8,222u8,53u8,136u8,171u8,165u8,58u8,152u8,205u8,28u8,237u8, - 35u8,254u8,233u8,47u8,74u8,85u8,77u8,219u8,0u8,233u8,53u8,128u8,128u8,213u8,230u8,161u8,30u8,193u8,95u8,100u8, - 140u8,54u8,180u8,194u8,93u8,8u8,245u8,226u8,137u8,96u8,109u8,128u8,48u8,179u8,35u8,4u8,196u8,174u8,74u8,194u8, - 16u8,6u8,77u8,21u8,146u8,127u8,99u8,221u8,147u8,71u8,247u8,245u8,63u8,107u8,33u8,212u8,44u8,128u8,105u8,245u8, - 4u8,10u8,109u8,65u8,181u8,142u8,128u8,204u8,194u8,12u8,152u8,136u8,86u8,4u8,59u8,157u8,170u8,58u8,232u8,13u8, - 155u8,37u8,141u8,248u8,216u8,11u8,250u8,0u8,238u8,3u8,184u8,95u8,245u8,105u8,12u8,252u8,145u8,41u8,70u8,207u8, - 149u8,255u8,164u8,145u8,214u8,52u8,242u8,183u8,139u8,108u8,188u8,0u8,114u8,216u8,46u8,212u8,78u8,255u8,55u8,74u8, - 6u8,152u8,0u8,140u8,120u8,247u8,88u8,108u8,163u8,20u8,95u8,33u8,214u8,148u8,80u8,17u8,22u8,105u8,243u8,99u8, - 251u8,105u8,213u8,80u8,168u8,89u8,107u8,202u8,130u8,183u8,165u8,179u8,112u8,220u8,58u8,172u8,238u8,91u8,88u8,129u8, - 46u8,169u8,128u8,233u8,47u8,199u8,168u8,117u8,24u8,250u8,190u8,66u8,158u8,41u8,0u8,142u8,56u8,126u8,96u8,120u8, - 146u8,36u8,145u8,194u8,147u8,198u8,138u8,138u8,169u8,15u8,71u8,44u8,91u8,34u8,2u8,183u8,133u8,54u8,106u8,156u8, - 147u8,68u8,181u8,55u8,201u8,74u8,169u8,68u8,238u8,212u8,132u8,83u8,221u8,243u8,47u8,115u8,155u8,138u8,176u8,162u8, - 6u8,78u8,188u8,205u8,34u8,82u8,47u8,192u8,215u8,154u8,9u8,7u8,134u8,190u8,130u8,174u8,159u8,109u8,204u8,146u8, - 198u8,84u8,171u8,26u8,67u8,168u8,83u8,92u8,105u8,26u8,69u8,40u8,175u8,225u8,10u8,46u8,111u8,151u8,237u8,182u8, - 151u8,26u8,151u8,210u8,181u8,224u8,22u8,241u8,255u8,176u8,155u8,245u8,141u8,242u8,169u8,101u8,174u8,57u8,204u8,39u8, - 51u8,8u8,64u8,211u8,117u8,185u8,175u8,53u8,173u8,195u8,224u8,61u8,219u8,232u8,213u8,162u8,55u8,153u8,166u8,214u8, - 241u8,97u8,156u8,82u8,125u8,238u8,120u8,107u8,200u8,37u8,214u8,58u8,95u8,26u8,201u8,26u8,196u8,169u8,149u8,211u8, - 38u8,8u8,2u8,110u8,179u8,41u8,10u8,138u8,136u8,116u8,246u8,178u8,152u8,31u8,56u8,147u8,222u8,238u8,221u8,94u8, - 189u8,194u8,224u8,100u8,122u8,110u8,155u8,51u8,233u8,179u8,150u8,234u8,62u8,114u8,179u8,1u8,155u8,136u8,68u8,138u8, - 197u8,200u8,114u8,167u8,233u8,82u8,66u8,121u8,176u8,134u8,2u8,202u8,131u8,231u8,16u8,190u8,60u8,120u8,178u8,172u8, - 192u8,253u8,186u8,114u8,190u8,122u8,185u8,142u8,160u8,90u8,172u8,124u8,61u8,73u8,95u8,189u8,124u8,186u8,168u8,32u8, - 192u8,154u8,178u8,66u8,6u8,201u8,104u8,158u8,175u8,33u8,175u8,128u8,124u8,14u8,153u8,5u8,234u8,39u8,203u8,45u8, - 133u8,89u8,215u8,206u8,176u8,191u8,90u8,199u8,208u8,0u8,246u8,44u8,150u8,6u8,188u8,79u8,55u8,53u8,202u8,176u8, - 166u8,188u8,88u8,218u8,55u8,239u8,0u8,190u8,154u8,188u8,136u8,247u8,201u8,242u8,50u8,25u8,30u8,150u8,151u8,115u8, - 42u8,230u8,18u8,57u8,166u8,152u8,85u8,199u8,85u8,55u8,42u8,138u8,196u8,47u8,250u8,242u8,198u8,118u8,61u8,7u8, - 181u8,210u8,64u8,105u8,187u8,72u8,52u8,42u8,130u8,130u8,30u8,239u8,182u8,90u8,108u8,145u8,44u8,149u8,153u8,202u8, - 222u8,70u8,169u8,35u8,87u8,101u8,67u8,57u8,67u8,57u8,214u8,22u8,197u8,253u8,58u8,197u8,86u8,88u8,49u8,78u8, - 10u8,159u8,157u8,56u8,182u8,234u8,219u8,82u8,126u8,133u8,108u8,141u8,206u8,197u8,53u8,39u8,215u8,247u8,233u8,12u8, - 39u8,87u8,125u8,11u8,24u8,179u8,159u8,217u8,222u8,85u8,123u8,100u8,144u8,139u8,110u8,188u8,120u8,28u8,69u8,201u8, - 45u8,41u8,241u8,58u8,194u8,84u8,192u8,56u8,200u8,124u8,43u8,118u8,173u8,56u8,240u8,66u8,198u8,192u8,177u8,22u8, - 207u8,191u8,227u8,167u8,105u8,16u8,102u8,185u8,105u8,12u8,134u8,136u8,250u8,234u8,187u8,196u8,38u8,150u8,121u8,142u8, - 125u8,27u8,255u8,108u8,26u8,19u8,39u8,115u8,104u8,213u8,30u8,158u8,142u8,197u8,236u8,214u8,2u8,44u8,230u8,85u8, - 29u8,28u8,216u8,104u8,1u8,231u8,107u8,171u8,166u8,171u8,84u8,220u8,12u8,37u8,173u8,182u8,232u8,127u8,196u8,22u8, - 77u8,65u8,46u8,56u8,219u8,4u8,121u8,199u8,210u8,172u8,125u8,110u8,22u8,36u8,219u8,7u8,231u8,245u8,231u8,102u8, - 116u8,37u8,159u8,15u8,207u8,109u8,157u8,176u8,177u8,252u8,237u8,90u8,44u8,53u8,238u8,60u8,180u8,104u8,210u8,111u8, - 220u8,27u8,179u8,239u8,156u8,56u8,246u8,108u8,224u8,90u8,216u8,51u8,99u8,131u8,231u8,220u8,122u8,24u8,145u8,146u8, - 26u8,74u8,184u8,39u8,52u8,202u8,109u8,226u8,138u8,153u8,209u8,35u8,47u8,58u8,175u8,63u8,60u8,174u8,183u8,36u8, - 86u8,59u8,191u8,143u8,201u8,179u8,158u8,185u8,159u8,215u8,44u8,105u8,141u8,79u8,149u8,42u8,176u8,220u8,156u8,73u8, - 124u8,160u8,108u8,73u8,140u8,181u8,88u8,47u8,12u8,20u8,140u8,116u8,219u8,168u8,199u8,151u8,7u8,134u8,172u8,126u8, - 22u8,40u8,207u8,1u8,107u8,62u8,45u8,152u8,123u8,57u8,211u8,89u8,187u8,156u8,152u8,145u8,208u8,61u8,14u8,81u8, - 183u8,172u8,46u8,48u8,125u8,115u8,190u8,225u8,85u8,220u8,120u8,65u8,102u8,203u8,202u8,107u8,26u8,195u8,27u8,243u8, - 84u8,188u8,54u8,5u8,69u8,176u8,150u8,212u8,71u8,240u8,116u8,133u8,111u8,43u8,111u8,28u8,188u8,2u8,54u8,28u8, - 119u8,72u8,131u8,166u8,98u8,15u8,81u8,44u8,183u8,240u8,247u8,218u8,163u8,241u8,27u8,71u8,177u8,3u8,6u8,137u8, - 38u8,18u8,135u8,111u8,233u8,122u8,200u8,239u8,191u8,55u8,28u8,189u8,229u8,2u8,142u8,58u8,155u8,128u8,227u8,188u8, - 176u8,9u8,60u8,235u8,58u8,55u8,184u8,80u8,53u8,231u8,27u8,220u8,177u8,206u8,170u8,213u8,101u8,51u8,139u8,180u8, - 123u8,105u8,213u8,245u8,173u8,10u8,154u8,163u8,1u8,208u8,118u8,208u8,66u8,34u8,217u8,26u8,159u8,53u8,149u8,108u8, - 142u8,212u8,144u8,161u8,142u8,136u8,158u8,235u8,218u8,93u8,245u8,95u8,31u8,1u8,67u8,225u8,39u8,113u8,180u8,250u8, - 196u8,14u8,108u8,222u8,23u8,65u8,235u8,209u8,190u8,9u8,108u8,252u8,114u8,2u8,82u8,63u8,52u8,107u8,122u8,94u8, - 213u8,22u8,89u8,254u8,63u8,18u8,105u8,158u8,162u8,8u8,166u8,159u8,177u8,175u8,22u8,7u8,179u8,50u8,11u8,38u8, - 97u8,20u8,22u8,43u8,229u8,16u8,189u8,184u8,231u8,126u8,106u8,212u8,255u8,146u8,42u8,168u8,149u8,230u8,227u8,35u8, - 153u8,244u8,246u8,118u8,123u8,30u8,252u8,245u8,3u8,251u8,255u8,45u8,13u8,210u8,36u8,238u8,145u8,79u8,141u8,158u8, - 128u8,118u8,48u8,23u8,198u8,5u8,189u8,166u8,153u8,194u8,72u8,243u8,68u8,186u8,135u8,138u8,26u8,31u8,44u8,121u8, - 237u8,148u8,69u8,142u8,183u8,24u8,77u8,229u8,215u8,122u8,103u8,166u8,0u8,103u8,173u8,52u8,255u8,36u8,165u8,107u8, - 251u8,117u8,187u8,73u8,149u8,150u8,196u8,124u8,89u8,106u8,108u8,153u8,133u8,172u8,17u8,93u8,82u8,156u8,129u8,52u8, - 224u8,53u8,235u8,63u8,24u8,164u8,215u8,86u8,232u8,77u8,229u8,234u8,165u8,210u8,210u8,60u8,105u8,0u8,178u8,59u8, - 80u8,25u8,238u8,235u8,28u8,187u8,124u8,18u8,194u8,80u8,111u8,242u8,209u8,189u8,119u8,179u8,239u8,202u8,173u8,214u8, - 51u8,74u8,243u8,159u8,103u8,64u8,123u8,79u8,208u8,176u8,98u8,191u8,17u8,115u8,45u8,143u8,128u8,147u8,222u8,235u8, - 158u8,215u8,98u8,155u8,167u8,217u8,67u8,146u8,214u8,12u8,242u8,218u8,48u8,200u8,94u8,157u8,106u8,31u8,101u8,7u8, - 62u8,159u8,253u8,21u8,129u8,164u8,189u8,164u8,153u8,6u8,112u8,81u8,218u8,239u8,61u8,178u8,103u8,12u8,38u8,142u8, - 239u8,17u8,31u8,62u8,18u8,117u8,174u8,236u8,84u8,94u8,59u8,69u8,131u8,218u8,190u8,78u8,173u8,75u8,135u8,74u8, - 25u8,80u8,122u8,17u8,246u8,197u8,90u8,139u8,28u8,237u8,55u8,55u8,154u8,125u8,216u8,35u8,155u8,45u8,189u8,179u8, - 170u8,242u8,71u8,87u8,1u8,107u8,88u8,31u8,118u8,69u8,188u8,92u8,178u8,215u8,94u8,182u8,224u8,112u8,159u8,181u8, - 102u8,129u8,193u8,130u8,73u8,180u8,105u8,133u8,210u8,234u8,56u8,156u8,57u8,253u8,189u8,93u8,192u8,169u8,159u8,99u8, - 255u8,5u8,95u8,230u8,1u8,180u8,8u8,238u8,134u8,229u8,139u8,111u8,157u8,181u8,70u8,206u8,64u8,144u8,162u8,7u8, - 3u8,130u8,182u8,130u8,213u8,116u8,180u8,111u8,234u8,85u8,92u8,159u8,221u8,174u8,221u8,179u8,210u8,130u8,171u8,123u8, - 157u8,188u8,42u8,151u8,240u8,230u8,189u8,202u8,173u8,209u8,99u8,247u8,118u8,235u8,203u8,93u8,86u8,102u8,93u8,180u8, - 165u8,67u8,130u8,193u8,111u8,9u8,63u8,250u8,19u8,64u8,233u8,152u8,227u8,11u8,111u8,157u8,229u8,38u8,120u8,72u8, - 254u8,179u8,123u8,55u8,13u8,230u8,212u8,208u8,201u8,210u8,18u8,233u8,34u8,163u8,178u8,86u8,203u8,0u8,95u8,172u8, - 89u8,97u8,121u8,88u8,47u8,60u8,91u8,131u8,138u8,118u8,74u8,151u8,109u8,58u8,147u8,123u8,225u8,62u8,222u8,238u8, - 91u8,175u8,51u8,221u8,33u8,119u8,168u8,189u8,7u8,149u8,103u8,178u8,11u8,115u8,154u8,207u8,214u8,148u8,184u8,33u8, - 224u8,122u8,100u8,123u8,36u8,171u8,2u8,247u8,30u8,212u8,32u8,66u8,237u8,139u8,69u8,236u8,144u8,48u8,215u8,53u8, - 85u8,172u8,120u8,173u8,254u8,0u8,249u8,177u8,67u8,243u8,123u8,160u8,165u8,142u8,207u8,251u8,166u8,119u8,219u8,163u8, - 184u8,69u8,253u8,198u8,241u8,60u8,10u8,174u8,155u8,225u8,178u8,224u8,177u8,210u8,249u8,107u8,36u8,60u8,142u8,64u8, - 196u8,174u8,52u8,189u8,48u8,211u8,243u8,253u8,214u8,253u8,214u8,31u8,28u8,20u8,96u8,196u8,159u8,45u8,0u8,0u8, - 0u8,0u8,17u8,116u8,111u8,107u8,101u8,110u8,95u8,101u8,118u8,101u8,110u8,116u8,95u8,115u8,116u8,111u8,114u8,101u8, - 215u8,12u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,221u8,90u8,95u8,111u8,219u8,54u8,16u8,127u8, - 207u8,167u8,224u8,94u8,10u8,27u8,112u8,107u8,12u8,24u8,246u8,160u8,164u8,1u8,134u8,117u8,192u8,250u8,80u8,108u8, - 104u8,210u8,190u8,10u8,140u8,69u8,199u8,68u8,36u8,209u8,163u8,168u8,56u8,94u8,145u8,239u8,190u8,19u8,73u8,89u8, - 148u8,248u8,71u8,127u8,98u8,187u8,233u8,130u8,0u8,142u8,69u8,242u8,142u8,119u8,247u8,187u8,223u8,241u8,168u8,44u8, - 151u8,75u8,116u8,187u8,161u8,5u8,202u8,88u8,82u8,166u8,4u8,109u8,57u8,123u8,164u8,9u8,41u8,80u8,41u8,104u8, - 90u8,32u8,193u8,16u8,78u8,18u8,132u8,243u8,4u8,145u8,140u8,10u8,148u8,147u8,29u8,60u8,123u8,32u8,57u8,34u8, - 143u8,36u8,23u8,48u8,97u8,131u8,5u8,194u8,156u8,160u8,156u8,9u8,68u8,115u8,53u8,248u8,46u8,99u8,143u8,228u8, - 66u8,75u8,196u8,91u8,193u8,138u8,88u8,62u8,143u8,34u8,249u8,17u8,203u8,181u8,113u8,33u8,24u8,172u8,251u8,118u8, - 129u8,224u8,167u8,44u8,8u8,42u8,68u8,18u8,69u8,133u8,224u8,52u8,191u8,143u8,162u8,27u8,249u8,121u8,217u8,25u8, - 164u8,247u8,57u8,225u8,205u8,67u8,37u8,122u8,205u8,113u8,70u8,118u8,140u8,63u8,68u8,145u8,20u8,28u8,69u8,223u8, - 110u8,72u8,186u8,94u8,160u8,63u8,170u8,111u8,127u8,194u8,222u8,83u8,242u8,28u8,88u8,132u8,87u8,43u8,86u8,230u8, - 162u8,163u8,139u8,109u8,5u8,101u8,176u8,227u8,191u8,228u8,103u8,119u8,185u8,156u8,130u8,243u8,125u8,20u8,253u8,150u8, - 239u8,157u8,43u8,187u8,43u8,180u8,3u8,192u8,191u8,91u8,194u8,197u8,62u8,206u8,240u8,54u8,138u8,254u8,214u8,223u8, - 190u8,226u8,180u8,36u8,151u8,23u8,114u8,201u8,154u8,83u8,2u8,238u8,182u8,221u8,166u8,199u8,151u8,75u8,253u8,129u8, - 126u8,103u8,105u8,74u8,86u8,149u8,50u8,148u8,149u8,2u8,203u8,63u8,84u8,88u8,234u8,137u8,250u8,115u8,169u8,92u8, - 33u8,67u8,40u8,72u8,130u8,118u8,27u8,8u8,224u8,170u8,89u8,13u8,241u8,94u8,113u8,42u8,183u8,141u8,42u8,40u8, - 84u8,194u8,72u8,34u8,23u8,67u8,60u8,202u8,149u8,48u8,84u8,125u8,104u8,230u8,126u8,146u8,243u8,148u8,232u8,13u8, - 46u8,80u8,2u8,214u8,44u8,144u8,25u8,214u8,234u8,103u8,197u8,9u8,134u8,71u8,49u8,224u8,136u8,71u8,21u8,154u8, - 56u8,41u8,138u8,69u8,51u8,124u8,144u8,28u8,231u8,16u8,146u8,8u8,169u8,200u8,55u8,19u8,88u8,154u8,196u8,198u8, - 6u8,237u8,9u8,128u8,73u8,255u8,132u8,231u8,161u8,62u8,40u8,57u8,237u8,181u8,253u8,11u8,167u8,231u8,179u8,25u8, - 54u8,228u8,182u8,213u8,26u8,8u8,219u8,40u8,54u8,196u8,180u8,51u8,195u8,79u8,52u8,43u8,179u8,94u8,91u8,63u8, - 193u8,188u8,50u8,59u8,159u8,185u8,122u8,95u8,17u8,42u8,127u8,253u8,165u8,109u8,175u8,61u8,242u8,220u8,205u8,132u8, - 91u8,73u8,73u8,130u8,227u8,188u8,88u8,19u8,142u8,56u8,73u8,43u8,195u8,70u8,36u8,3u8,206u8,171u8,84u8,229u8, - 8u8,114u8,247u8,45u8,85u8,62u8,75u8,40u8,135u8,77u8,31u8,132u8,154u8,94u8,2u8,78u8,248u8,152u8,223u8,234u8, - 129u8,62u8,231u8,84u8,26u8,111u8,121u8,73u8,16u8,93u8,75u8,185u8,181u8,30u8,96u8,203u8,5u8,90u8,227u8,180u8, - 176u8,70u8,222u8,178u8,82u8,52u8,190u8,217u8,138u8,152u8,2u8,168u8,239u8,24u8,75u8,67u8,198u8,79u8,96u8,128u8, - 74u8,167u8,100u8,150u8,4u8,11u8,44u8,19u8,64u8,33u8,162u8,48u8,109u8,173u8,49u8,15u8,162u8,7u8,194u8,32u8, - 136u8,0u8,59u8,248u8,138u8,226u8,78u8,154u8,2u8,202u8,55u8,249u8,189u8,138u8,43u8,89u8,227u8,50u8,21u8,141u8, - 245u8,72u8,115u8,50u8,133u8,170u8,39u8,77u8,2u8,242u8,21u8,141u8,99u8,76u8,103u8,124u8,80u8,107u8,107u8,218u8, - 30u8,149u8,29u8,199u8,113u8,203u8,3u8,217u8,23u8,17u8,122u8,132u8,85u8,140u8,95u8,169u8,193u8,235u8,69u8,11u8, - 107u8,59u8,168u8,56u8,105u8,202u8,118u8,168u8,220u8,2u8,158u8,4u8,42u8,152u8,52u8,21u8,252u8,137u8,30u8,171u8, - 50u8,3u8,121u8,79u8,239u8,55u8,2u8,221u8,85u8,53u8,59u8,39u8,45u8,135u8,171u8,9u8,7u8,249u8,170u8,246u8, - 93u8,181u8,170u8,212u8,245u8,117u8,59u8,20u8,157u8,37u8,237u8,185u8,67u8,249u8,169u8,65u8,97u8,127u8,41u8,154u8, - 90u8,128u8,142u8,7u8,203u8,83u8,85u8,163u8,6u8,145u8,156u8,237u8,113u8,42u8,246u8,30u8,15u8,124u8,86u8,163u8, - 223u8,201u8,122u8,189u8,183u8,56u8,47u8,51u8,194u8,149u8,232u8,22u8,101u8,155u8,115u8,18u8,146u8,179u8,140u8,230u8, - 61u8,179u8,182u8,120u8,79u8,136u8,175u8,120u8,84u8,254u8,236u8,83u8,105u8,206u8,241u8,171u8,52u8,103u8,121u8,85u8, - 14u8,141u8,80u8,184u8,140u8,78u8,40u8,158u8,199u8,11u8,208u8,132u8,74u8,170u8,183u8,45u8,43u8,137u8,220u8,239u8, - 77u8,181u8,201u8,175u8,63u8,203u8,109u8,3u8,229u8,116u8,202u8,217u8,42u8,124u8,240u8,236u8,212u8,124u8,32u8,235u8, - 88u8,249u8,72u8,29u8,252u8,129u8,44u8,140u8,147u8,249u8,149u8,239u8,120u8,117u8,237u8,60u8,64u8,104u8,19u8,134u8, - 9u8,180u8,194u8,224u8,150u8,105u8,164u8,235u8,48u8,185u8,110u8,10u8,234u8,240u8,176u8,104u8,159u8,73u8,244u8,201u8, - 66u8,10u8,238u8,20u8,119u8,167u8,54u8,251u8,140u8,225u8,148u8,239u8,243u8,127u8,143u8,211u8,187u8,85u8,221u8,144u8, - 173u8,235u8,99u8,220u8,116u8,42u8,126u8,49u8,254u8,122u8,216u8,18u8,56u8,200u8,189u8,189u8,78u8,173u8,115u8,55u8, - 32u8,195u8,102u8,70u8,99u8,253u8,0u8,232u8,132u8,0u8,179u8,172u8,59u8,101u8,248u8,93u8,51u8,94u8,165u8,107u8, - 117u8,164u8,168u8,58u8,98u8,221u8,11u8,67u8,120u8,215u8,165u8,40u8,121u8,83u8,83u8,201u8,147u8,32u8,121u8,33u8, - 19u8,88u8,215u8,82u8,104u8,23u8,219u8,85u8,113u8,93u8,66u8,161u8,203u8,169u8,160u8,56u8,165u8,255u8,146u8,216u8, - 106u8,144u8,103u8,208u8,158u8,138u8,8u8,189u8,81u8,189u8,239u8,188u8,73u8,68u8,56u8,52u8,206u8,126u8,34u8,79u8, - 180u8,16u8,197u8,149u8,149u8,184u8,215u8,51u8,53u8,31u8,154u8,84u8,197u8,42u8,49u8,91u8,75u8,73u8,243u8,249u8, - 220u8,72u8,102u8,233u8,22u8,232u8,213u8,65u8,173u8,28u8,93u8,56u8,40u8,160u8,61u8,187u8,63u8,183u8,117u8,59u8, - 29u8,69u8,21u8,227u8,40u8,67u8,54u8,61u8,153u8,174u8,118u8,182u8,8u8,41u8,242u8,4u8,111u8,136u8,50u8,59u8, - 168u8,3u8,244u8,5u8,64u8,59u8,68u8,167u8,7u8,204u8,62u8,197u8,29u8,30u8,240u8,107u8,112u8,176u8,130u8,79u8, - 230u8,168u8,208u8,88u8,124u8,224u8,147u8,218u8,199u8,14u8,126u8,21u8,1u8,174u8,240u8,43u8,155u8,16u8,132u8,145u8, - 174u8,247u8,176u8,138u8,95u8,190u8,131u8,99u8,124u8,178u8,71u8,131u8,118u8,56u8,84u8,13u8,102u8,169u8,111u8,170u8, - 170u8,211u8,188u8,228u8,151u8,89u8,103u8,254u8,243u8,252u8,242u8,240u8,93u8,95u8,132u8,153u8,199u8,156u8,234u8,94u8, - 175u8,115u8,73u8,112u8,232u8,5u8,15u8,149u8,69u8,78u8,223u8,150u8,119u8,41u8,93u8,205u8,212u8,13u8,213u8,92u8, - 82u8,87u8,117u8,60u8,138u8,3u8,132u8,48u8,59u8,28u8,111u8,52u8,131u8,45u8,92u8,199u8,26u8,171u8,219u8,235u8, - 118u8,121u8,115u8,112u8,218u8,63u8,37u8,116u8,228u8,69u8,144u8,161u8,82u8,34u8,212u8,118u8,209u8,123u8,255u8,181u8, - 77u8,155u8,208u8,218u8,119u8,23u8,14u8,214u8,212u8,19u8,58u8,62u8,181u8,238u8,52u8,154u8,7u8,237u8,137u8,218u8, - 178u8,246u8,67u8,109u8,221u8,162u8,27u8,22u8,201u8,235u8,161u8,98u8,80u8,111u8,230u8,178u8,101u8,178u8,125u8,171u8, - 250u8,30u8,238u8,11u8,56u8,103u8,187u8,248u8,62u8,101u8,119u8,56u8,173u8,66u8,50u8,176u8,72u8,212u8,26u8,12u8, - 21u8,250u8,86u8,85u8,134u8,90u8,254u8,29u8,96u8,242u8,150u8,153u8,111u8,64u8,173u8,189u8,185u8,119u8,161u8,250u8, - 209u8,246u8,147u8,124u8,214u8,60u8,154u8,15u8,2u8,175u8,217u8,66u8,78u8,3u8,177u8,143u8,119u8,70u8,128u8,217u8, - 213u8,2u8,250u8,122u8,195u8,23u8,129u8,219u8,211u8,14u8,159u8,31u8,228u8,134u8,97u8,54u8,216u8,157u8,131u8,63u8, - 44u8,232u8,125u8,37u8,102u8,36u8,248u8,189u8,229u8,237u8,8u8,73u8,80u8,247u8,167u8,211u8,18u8,192u8,85u8,184u8, - 70u8,128u8,191u8,213u8,103u8,90u8,157u8,231u8,139u8,224u8,110u8,119u8,214u8,231u8,71u8,186u8,54u8,198u8,70u8,185u8, - 53u8,240u8,195u8,34u8,220u8,113u8,16u8,25u8,9u8,110u8,231u8,217u8,103u8,42u8,176u8,245u8,93u8,188u8,213u8,67u8, - 251u8,160u8,172u8,118u8,102u8,158u8,170u8,103u8,245u8,161u8,171u8,65u8,175u8,121u8,179u8,62u8,2u8,147u8,166u8,84u8, - 8u8,136u8,227u8,93u8,128u8,137u8,72u8,53u8,123u8,60u8,34u8,244u8,118u8,79u8,136u8,136u8,90u8,67u8,16u8,17u8, - 174u8,126u8,99u8,8u8,16u8,90u8,13u8,77u8,39u8,131u8,140u8,161u8,222u8,224u8,127u8,249u8,252u8,113u8,12u8,133u8, - 169u8,141u8,88u8,103u8,80u8,251u8,174u8,173u8,6u8,193u8,249u8,222u8,80u8,140u8,64u8,152u8,201u8,97u8,16u8,224u8, - 64u8,54u8,235u8,87u8,194u8,93u8,186u8,180u8,222u8,217u8,56u8,57u8,50u8,106u8,41u8,242u8,241u8,97u8,251u8,185u8, - 116u8,197u8,196u8,3u8,238u8,119u8,161u8,66u8,211u8,196u8,32u8,210u8,237u8,14u8,120u8,8u8,206u8,143u8,115u8,126u8, - 61u8,188u8,253u8,168u8,187u8,106u8,40u8,223u8,219u8,49u8,184u8,15u8,118u8,229u8,39u8,130u8,127u8,248u8,77u8,212u8, - 233u8,222u8,38u8,157u8,51u8,145u8,2u8,239u8,251u8,78u8,153u8,82u8,149u8,107u8,237u8,36u8,83u8,190u8,177u8,243u8, - 172u8,251u8,252u8,213u8,167u8,90u8,232u8,38u8,104u8,72u8,210u8,133u8,239u8,160u8,166u8,100u8,224u8,196u8,150u8,81u8, - 109u8,77u8,45u8,182u8,155u8,197u8,211u8,85u8,157u8,233u8,47u8,32u8,207u8,155u8,60u8,195u8,59u8,211u8,35u8,214u8, - 162u8,73u8,125u8,232u8,107u8,79u8,152u8,233u8,253u8,230u8,81u8,155u8,204u8,250u8,189u8,244u8,232u8,28u8,113u8,221u8, - 178u8,158u8,48u8,65u8,254u8,223u8,239u8,168u8,79u8,148u8,196u8,174u8,28u8,118u8,252u8,171u8,193u8,169u8,243u8,215u8, - 114u8,164u8,127u8,138u8,225u8,71u8,255u8,164u8,198u8,141u8,54u8,29u8,244u8,232u8,242u8,196u8,204u8,63u8,201u8,165u8, - 235u8,213u8,179u8,139u8,235u8,133u8,198u8,16u8,102u8,113u8,190u8,57u8,153u8,194u8,42u8,19u8,238u8,170u8,212u8,110u8, - 156u8,215u8,84u8,167u8,99u8,149u8,49u8,255u8,88u8,113u8,206u8,66u8,59u8,240u8,78u8,236u8,136u8,57u8,58u8,234u8, - 6u8,236u8,216u8,232u8,63u8,50u8,252u8,39u8,94u8,117u8,189u8,224u8,126u8,235u8,249u8,226u8,63u8,82u8,47u8,21u8, - 189u8,118u8,47u8,0u8,0u8,0u8,0u8,5u8,116u8,111u8,107u8,101u8,110u8,238u8,109u8,31u8,139u8,8u8,0u8,0u8, - 0u8,0u8,0u8,2u8,255u8,237u8,125u8,123u8,115u8,19u8,71u8,246u8,232u8,255u8,249u8,20u8,179u8,108u8,149u8,35u8, - 177u8,194u8,216u8,134u8,144u8,196u8,4u8,234u8,10u8,91u8,108u8,188u8,23u8,108u8,214u8,143u8,228u8,151u8,187u8,149u8, - 154u8,140u8,173u8,145u8,61u8,63u8,100u8,141u8,86u8,35u8,225u8,56u8,20u8,223u8,253u8,158u8,126u8,206u8,233u8,238u8, - 211u8,61u8,45u8,89u8,6u8,3u8,114u8,165u8,130u8,45u8,205u8,244u8,243u8,188u8,159u8,15u8,31u8,62u8,76u8,142u8, - 47u8,138u8,42u8,185u8,44u8,251u8,179u8,97u8,158u8,140u8,39u8,229u8,187u8,162u8,159u8,87u8,201u8,244u8,34u8,79u8, - 6u8,229u8,108u8,212u8,207u8,166u8,69u8,57u8,130u8,95u8,39u8,201u8,113u8,249u8,54u8,31u8,85u8,235u8,223u8,60u8, - 132u8,119u8,118u8,46u8,242u8,179u8,183u8,229u8,108u8,154u8,148u8,179u8,73u8,210u8,207u8,223u8,229u8,195u8,114u8,156u8, - 195u8,111u8,229u8,89u8,2u8,79u8,179u8,15u8,167u8,236u8,233u8,164u8,154u8,102u8,48u8,196u8,164u8,159u8,92u8,76u8, - 167u8,227u8,106u8,251u8,225u8,195u8,108u8,60u8,45u8,171u8,117u8,120u8,227u8,225u8,89u8,57u8,58u8,203u8,199u8,211u8, - 10u8,126u8,41u8,70u8,15u8,224u8,177u8,7u8,252u8,21u8,241u8,132u8,248u8,253u8,27u8,185u8,42u8,254u8,81u8,202u8, - 63u8,218u8,222u8,22u8,3u8,191u8,255u8,38u8,129u8,159u8,89u8,149u8,195u8,20u8,253u8,237u8,237u8,124u8,50u8,41u8, - 39u8,79u8,205u8,207u8,202u8,49u8,91u8,250u8,246u8,246u8,251u8,163u8,124u8,56u8,232u8,36u8,7u8,252u8,207u8,15u8, - 214u8,67u8,85u8,113u8,62u8,202u8,237u8,55u8,171u8,233u8,164u8,24u8,157u8,235u8,55u8,143u8,248u8,159u8,246u8,155u8, - 239u8,242u8,179u8,41u8,155u8,83u8,127u8,42u8,86u8,57u8,152u8,100u8,151u8,249u8,85u8,57u8,121u8,187u8,189u8,157u8, - 157u8,157u8,193u8,9u8,78u8,159u8,250u8,159u8,128u8,131u8,27u8,77u8,245u8,60u8,61u8,246u8,215u8,207u8,112u8,18u8, - 195u8,252u8,67u8,224u8,165u8,105u8,113u8,153u8,195u8,185u8,94u8,142u8,237u8,103u8,248u8,178u8,166u8,217u8,233u8,48u8, - 215u8,67u8,30u8,179u8,191u8,156u8,193u8,228u8,73u8,194u8,101u8,195u8,165u8,77u8,175u8,211u8,203u8,108u8,172u8,223u8, - 120u8,35u8,63u8,124u8,157u8,141u8,235u8,63u8,126u8,201u8,134u8,51u8,239u8,48u8,252u8,159u8,148u8,111u8,5u8,150u8, - 80u8,78u8,114u8,121u8,38u8,15u8,31u8,202u8,127u8,146u8,157u8,114u8,196u8,0u8,97u8,90u8,169u8,207u8,249u8,191u8, - 103u8,236u8,211u8,228u8,248u8,224u8,255u8,246u8,246u8,211u8,215u8,221u8,255u8,73u8,95u8,159u8,28u8,119u8,95u8,188u8, - 234u8,165u8,123u8,251u8,187u8,219u8,201u8,236u8,201u8,227u8,228u8,89u8,178u8,241u8,212u8,121u8,242u8,228u8,112u8,143u8, - 122u8,114u8,211u8,125u8,242u8,240u8,224u8,183u8,238u8,171u8,227u8,223u8,168u8,167u8,183u8,220u8,167u8,119u8,123u8,71u8, - 59u8,135u8,123u8,111u8,142u8,247u8,14u8,246u8,169u8,55u8,30u8,185u8,111u8,188u8,57u8,60u8,120u8,211u8,59u8,164u8, - 39u8,120u8,28u8,120u8,252u8,151u8,238u8,171u8,147u8,30u8,245u8,210u8,119u8,79u8,241u8,193u8,236u8,28u8,188u8,122u8, - 213u8,219u8,225u8,235u8,105u8,88u8,155u8,113u8,74u8,232u8,181u8,152u8,163u8,66u8,143u8,123u8,238u8,96u8,203u8,88u8, - 21u8,123u8,8u8,189u8,179u8,223u8,125u8,221u8,75u8,95u8,245u8,246u8,255u8,121u8,252u8,179u8,30u8,126u8,235u8,135u8, - 167u8,214u8,11u8,251u8,47u8,143u8,35u8,159u8,100u8,75u8,54u8,31u8,250u8,110u8,115u8,75u8,195u8,147u8,134u8,199u8, - 228u8,109u8,126u8,157u8,112u8,88u8,235u8,39u8,197u8,8u8,8u8,208u8,32u8,155u8,13u8,167u8,169u8,4u8,232u8,2u8, - 232u8,23u8,12u8,57u8,157u8,148u8,195u8,33u8,32u8,110u8,114u8,117u8,81u8,38u8,103u8,217u8,40u8,57u8,157u8,77u8, - 70u8,156u8,176u8,113u8,136u8,93u8,87u8,131u8,178u8,79u8,206u8,202u8,201u8,36u8,175u8,198u8,229u8,168u8,207u8,94u8, - 80u8,136u8,145u8,188u8,99u8,112u8,159u8,0u8,121u8,124u8,177u8,115u8,148u8,84u8,249u8,164u8,200u8,134u8,197u8,95u8, - 48u8,231u8,105u8,89u8,14u8,215u8,209u8,210u8,95u8,156u8,28u8,238u8,243u8,115u8,123u8,241u8,91u8,186u8,115u8,216u8, - 235u8,30u8,31u8,28u8,110u8,39u8,130u8,64u8,252u8,52u8,251u8,225u8,57u8,108u8,227u8,244u8,158u8,128u8,2u8,226u8, - 201u8,123u8,79u8,61u8,35u8,29u8,252u8,186u8,223u8,139u8,25u8,135u8,63u8,119u8,175u8,25u8,64u8,61u8,67u8,25u8, - 143u8,29u8,195u8,115u8,247u8,28u8,4u8,238u8,49u8,242u8,90u8,153u8,31u8,50u8,198u8,33u8,143u8,50u8,185u8,200u8, - 170u8,228u8,52u8,27u8,102u8,64u8,209u8,19u8,160u8,95u8,236u8,180u8,71u8,229u8,52u8,57u8,133u8,179u8,27u8,21u8, - 83u8,121u8,106u8,104u8,125u8,189u8,238u8,43u8,216u8,251u8,238u8,111u8,233u8,207u8,221u8,163u8,244u8,69u8,247u8,85u8, - 119u8,127u8,167u8,135u8,160u8,25u8,207u8,48u8,97u8,231u8,63u8,250u8,118u8,10u8,227u8,94u8,195u8,219u8,195u8,33u8, - 108u8,129u8,113u8,36u8,96u8,77u8,192u8,112u8,166u8,140u8,119u8,73u8,58u8,139u8,199u8,175u8,65u8,244u8,40u8,221u8, - 63u8,56u8,78u8,223u8,156u8,188u8,120u8,181u8,119u8,244u8,115u8,15u8,35u8,129u8,158u8,101u8,71u8,172u8,118u8,80u8, - 176u8,149u8,215u8,51u8,0u8,96u8,157u8,77u8,242u8,12u8,14u8,236u8,219u8,134u8,57u8,232u8,41u8,182u8,204u8,141u8, - 224u8,161u8,179u8,33u8,12u8,220u8,191u8,78u8,242u8,63u8,139u8,74u8,82u8,69u8,119u8,80u8,117u8,70u8,189u8,255u8, - 217u8,59u8,58u8,62u8,66u8,132u8,72u8,143u8,218u8,251u8,243u8,44u8,207u8,251u8,149u8,132u8,96u8,53u8,58u8,172u8, - 246u8,50u8,251u8,179u8,184u8,204u8,134u8,201u8,104u8,118u8,121u8,10u8,167u8,84u8,14u8,196u8,61u8,165u8,192u8,205u8, - 51u8,99u8,54u8,6u8,129u8,189u8,244u8,215u8,131u8,147u8,87u8,187u8,48u8,207u8,78u8,175u8,183u8,155u8,154u8,20u8, - 97u8,239u8,245u8,201u8,107u8,68u8,212u8,244u8,212u8,123u8,163u8,106u8,54u8,24u8,20u8,103u8,5u8,80u8,125u8,9u8, - 4u8,18u8,0u8,240u8,248u8,123u8,251u8,71u8,39u8,47u8,95u8,238u8,237u8,236u8,245u8,246u8,143u8,237u8,123u8,254u8, - 206u8,189u8,129u8,203u8,124u8,114u8,158u8,11u8,12u8,189u8,42u8,197u8,168u8,85u8,114u8,85u8,76u8,47u8,146u8,126u8, - 49u8,24u8,0u8,40u8,232u8,185u8,138u8,190u8,57u8,13u8,208u8,212u8,189u8,221u8,84u8,242u8,146u8,222u8,225u8,63u8, - 245u8,44u8,79u8,156u8,227u8,170u8,41u8,64u8,194u8,142u8,67u8,159u8,85u8,54u8,28u8,150u8,87u8,38u8,148u8,190u8, - 222u8,131u8,101u8,27u8,135u8,163u8,185u8,21u8,62u8,151u8,239u8,209u8,28u8,251u8,165u8,32u8,51u8,103u8,217u8,56u8, - 59u8,45u8,134u8,197u8,244u8,26u8,15u8,184u8,127u8,192u8,17u8,55u8,221u8,233u8,190u8,233u8,190u8,216u8,123u8,181u8, - 119u8,252u8,155u8,26u8,227u8,7u8,12u8,44u8,108u8,113u8,187u8,108u8,109u8,126u8,56u8,145u8,76u8,171u8,123u8,220u8, - 245u8,192u8,201u8,143u8,228u8,128u8,236u8,148u8,199u8,179u8,211u8,97u8,81u8,93u8,152u8,91u8,69u8,227u8,209u8,248u8, - 178u8,97u8,143u8,119u8,196u8,200u8,47u8,72u8,124u8,57u8,199u8,78u8,190u8,66u8,119u8,192u8,35u8,160u8,111u8,61u8, - 207u8,136u8,4u8,10u8,86u8,99u8,56u8,49u8,121u8,57u8,211u8,18u8,80u8,62u8,201u8,46u8,25u8,218u8,37u8,195u8, - 12u8,32u8,131u8,161u8,59u8,124u8,84u8,76u8,43u8,249u8,49u8,49u8,223u8,27u8,56u8,212u8,180u8,251u8,250u8,224u8, - 4u8,110u8,238u8,85u8,23u8,0u8,225u8,48u8,61u8,56u8,76u8,123u8,255u8,62u8,233u8,190u8,130u8,203u8,147u8,247u8, - 39u8,190u8,175u8,217u8,144u8,133u8,168u8,131u8,34u8,31u8,246u8,25u8,213u8,231u8,48u8,57u8,227u8,18u8,21u8,158u8, - 234u8,229u8,94u8,15u8,32u8,130u8,109u8,74u8,19u8,86u8,57u8,210u8,35u8,3u8,18u8,128u8,102u8,205u8,166u8,23u8, - 229u8,132u8,179u8,12u8,216u8,14u8,27u8,106u8,154u8,91u8,240u8,192u8,137u8,110u8,143u8,128u8,136u8,205u8,199u8,246u8, - 137u8,243u8,245u8,20u8,136u8,133u8,9u8,22u8,104u8,13u8,40u8,182u8,184u8,183u8,159u8,162u8,27u8,208u8,99u8,98u8, - 164u8,59u8,169u8,152u8,196u8,94u8,244u8,217u8,245u8,129u8,172u8,252u8,128u8,49u8,210u8,98u8,2u8,52u8,36u8,153u8, - 78u8,178u8,81u8,5u8,248u8,134u8,71u8,62u8,57u8,130u8,163u8,100u8,123u8,62u8,120u8,115u8,204u8,70u8,223u8,221u8, - 59u8,4u8,42u8,145u8,30u8,31u8,118u8,247u8,143u8,94u8,50u8,54u8,37u8,103u8,120u8,226u8,222u8,42u8,195u8,224u8, - 254u8,36u8,187u8,74u8,54u8,196u8,178u8,241u8,176u8,191u8,238u8,29u8,255u8,188u8,123u8,216u8,253u8,53u8,253u8,127u8, - 189u8,195u8,3u8,61u8,200u8,247u8,62u8,208u8,200u8,20u8,112u8,92u8,100u8,176u8,232u8,209u8,240u8,154u8,179u8,158u8, - 77u8,2u8,30u8,184u8,204u8,1u8,203u8,229u8,16u8,97u8,92u8,210u8,15u8,38u8,186u8,94u8,22u8,0u8,95u8,94u8, - 116u8,229u8,36u8,128u8,184u8,156u8,31u8,253u8,196u8,125u8,4u8,146u8,58u8,131u8,158u8,105u8,89u8,38u8,195u8,114u8, - 116u8,238u8,227u8,25u8,76u8,32u8,58u8,62u8,56u8,72u8,95u8,29u8,236u8,255u8,83u8,243u8,140u8,239u8,172u8,113u8, - 97u8,27u8,193u8,1u8,181u8,104u8,229u8,140u8,244u8,196u8,26u8,9u8,68u8,43u8,223u8,32u8,76u8,234u8,114u8,222u8, - 39u8,238u8,160u8,159u8,143u8,203u8,138u8,223u8,130u8,128u8,71u8,78u8,154u8,55u8,168u8,195u8,63u8,0u8,153u8,245u8, - 205u8,193u8,17u8,224u8,162u8,128u8,66u8,118u8,205u8,252u8,138u8,45u8,220u8,219u8,250u8,193u8,157u8,133u8,211u8,207u8, - 13u8,49u8,5u8,69u8,60u8,27u8,70u8,116u8,8u8,159u8,194u8,100u8,54u8,46u8,67u8,229u8,228u8,244u8,58u8,41u8, - 175u8,70u8,38u8,120u8,115u8,25u8,10u8,46u8,122u8,159u8,193u8,76u8,61u8,141u8,230u8,186u8,27u8,49u8,163u8,74u8, - 113u8,193u8,225u8,176u8,7u8,161u8,145u8,49u8,25u8,60u8,204u8,1u8,39u8,223u8,1u8,181u8,224u8,36u8,168u8,226u8, - 106u8,183u8,0u8,120u8,46u8,207u8,102u8,103u8,83u8,231u8,176u8,242u8,100u8,54u8,6u8,62u8,198u8,132u8,210u8,107u8, - 166u8,155u8,153u8,179u8,139u8,89u8,79u8,222u8,236u8,50u8,26u8,115u8,216u8,3u8,28u8,254u8,5u8,184u8,152u8,146u8, - 248u8,244u8,26u8,12u8,10u8,200u8,86u8,103u8,94u8,173u8,218u8,43u8,193u8,34u8,197u8,101u8,200u8,121u8,126u8,238u8, - 254u8,210u8,163u8,238u8,228u8,17u8,166u8,139u8,135u8,229u8,117u8,54u8,4u8,217u8,186u8,24u8,129u8,116u8,93u8,0u8, - 157u8,29u8,112u8,130u8,6u8,162u8,74u8,62u8,97u8,167u8,199u8,38u8,195u8,228u8,158u8,125u8,217u8,207u8,71u8,37u8, - 224u8,169u8,125u8,184u8,138u8,239u8,43u8,77u8,111u8,255u8,4u8,120u8,63u8,63u8,238u8,93u8,128u8,22u8,64u8,93u8, - 33u8,136u8,203u8,69u8,60u8,38u8,22u8,49u8,206u8,174u8,243u8,92u8,9u8,119u8,156u8,153u8,241u8,141u8,58u8,220u8, - 76u8,205u8,240u8,166u8,251u8,91u8,175u8,151u8,118u8,119u8,118u8,56u8,131u8,217u8,61u8,232u8,9u8,241u8,146u8,179u8, - 94u8,61u8,145u8,33u8,217u8,212u8,116u8,65u8,221u8,101u8,45u8,117u8,204u8,46u8,129u8,39u8,84u8,252u8,18u8,241u8, - 142u8,171u8,217u8,120u8,60u8,188u8,166u8,54u8,106u8,9u8,30u8,143u8,158u8,56u8,80u8,249u8,6u8,171u8,64u8,106u8, - 79u8,140u8,194u8,95u8,102u8,211u8,179u8,11u8,247u8,230u8,36u8,40u8,236u8,193u8,70u8,196u8,158u8,56u8,91u8,235u8, - 30u8,239u8,104u8,5u8,236u8,17u8,35u8,4u8,122u8,150u8,95u8,21u8,45u8,175u8,41u8,166u8,158u8,226u8,34u8,123u8, - 7u8,182u8,145u8,90u8,44u8,116u8,233u8,130u8,33u8,15u8,106u8,210u8,95u8,147u8,87u8,27u8,110u8,48u8,117u8,208u8, - 51u8,131u8,118u8,6u8,2u8,109u8,254u8,231u8,24u8,152u8,85u8,69u8,242u8,18u8,216u8,212u8,193u8,75u8,184u8,148u8, - 55u8,192u8,163u8,106u8,193u8,217u8,38u8,217u8,90u8,203u8,3u8,128u8,155u8,40u8,196u8,3u8,36u8,50u8,45u8,87u8, - 120u8,2u8,173u8,41u8,105u8,76u8,2u8,237u8,235u8,232u8,184u8,187u8,191u8,219u8,61u8,172u8,85u8,255u8,13u8,194u8, - 254u8,193u8,36u8,37u8,118u8,237u8,96u8,89u8,154u8,157u8,77u8,103u8,48u8,29u8,199u8,237u8,139u8,114u8,200u8,21u8, - 78u8,33u8,234u8,226u8,183u8,196u8,131u8,242u8,82u8,25u8,143u8,227u8,172u8,94u8,154u8,189u8,216u8,79u8,209u8,223u8, - 22u8,223u8,238u8,245u8,59u8,250u8,195u8,135u8,82u8,157u8,149u8,104u8,171u8,228u8,254u8,106u8,61u8,57u8,96u8,188u8, - 82u8,27u8,124u8,222u8,229u8,147u8,138u8,129u8,36u8,104u8,91u8,92u8,51u8,230u8,55u8,151u8,73u8,117u8,247u8,180u8, - 56u8,215u8,160u8,184u8,185u8,174u8,199u8,22u8,99u8,242u8,93u8,154u8,19u8,162u8,179u8,100u8,96u8,199u8,41u8,7u8, - 215u8,203u8,144u8,154u8,173u8,158u8,189u8,186u8,128u8,237u8,80u8,203u8,232u8,212u8,82u8,13u8,86u8,227u8,179u8,137u8, - 208u8,7u8,42u8,198u8,2u8,225u8,20u8,8u8,77u8,31u8,196u8,22u8,45u8,223u8,118u8,146u8,43u8,38u8,146u8,50u8, - 96u8,20u8,231u8,85u8,76u8,137u8,249u8,167u8,232u8,242u8,153u8,237u8,75u8,10u8,102u8,85u8,7u8,142u8,96u8,148u8, - 95u8,185u8,235u8,99u8,58u8,102u8,197u8,45u8,133u8,92u8,142u8,179u8,76u8,8u8,236u8,199u8,94u8,248u8,182u8,97u8, - 71u8,227u8,143u8,125u8,168u8,161u8,239u8,124u8,88u8,130u8,178u8,4u8,42u8,108u8,241u8,95u8,102u8,94u8,0u8,234u8, - 54u8,45u8,128u8,226u8,115u8,69u8,45u8,67u8,50u8,18u8,6u8,130u8,189u8,126u8,13u8,6u8,29u8,0u8,200u8,241u8, - 117u8,39u8,233u8,195u8,28u8,8u8,36u8,212u8,237u8,23u8,122u8,149u8,103u8,229u8,229u8,101u8,57u8,194u8,218u8,78u8, - 117u8,145u8,77u8,48u8,152u8,91u8,218u8,149u8,189u8,117u8,107u8,131u8,108u8,136u8,84u8,67u8,30u8,59u8,113u8,27u8, - 250u8,24u8,48u8,168u8,99u8,43u8,7u8,198u8,81u8,3u8,9u8,26u8,63u8,21u8,55u8,144u8,37u8,131u8,217u8,232u8, - 188u8,96u8,108u8,115u8,170u8,152u8,169u8,184u8,132u8,190u8,125u8,9u8,9u8,186u8,4u8,206u8,95u8,97u8,245u8,204u8, - 196u8,64u8,94u8,8u8,151u8,178u8,179u8,183u8,236u8,222u8,153u8,242u8,0u8,194u8,145u8,94u8,156u8,189u8,51u8,4u8, - 200u8,206u8,205u8,0u8,170u8,144u8,119u8,163u8,201u8,183u8,115u8,59u8,226u8,40u8,248u8,13u8,213u8,87u8,211u8,113u8, - 144u8,86u8,157u8,80u8,214u8,239u8,3u8,9u8,168u8,212u8,9u8,73u8,185u8,161u8,147u8,228u8,231u8,219u8,201u8,198u8, - 159u8,103u8,217u8,32u8,215u8,175u8,200u8,239u8,182u8,213u8,59u8,238u8,113u8,115u8,241u8,16u8,70u8,170u8,69u8,208u8, - 167u8,2u8,255u8,224u8,63u8,185u8,13u8,101u8,44u8,81u8,136u8,36u8,248u8,157u8,152u8,239u8,94u8,151u8,25u8,115u8, - 147u8,238u8,136u8,107u8,193u8,53u8,191u8,186u8,87u8,47u8,65u8,127u8,182u8,45u8,109u8,225u8,254u8,69u8,232u8,171u8, - 168u8,215u8,128u8,241u8,119u8,170u8,30u8,21u8,58u8,22u8,188u8,160u8,143u8,79u8,15u8,201u8,190u8,55u8,39u8,250u8, - 96u8,18u8,110u8,9u8,195u8,181u8,86u8,171u8,160u8,185u8,138u8,2u8,103u8,251u8,222u8,72u8,242u8,170u8,230u8,242u8, - 88u8,82u8,42u8,161u8,129u8,112u8,171u8,98u8,206u8,85u8,8u8,88u8,16u8,50u8,73u8,233u8,193u8,159u8,42u8,249u8, - 70u8,49u8,124u8,248u8,82u8,16u8,59u8,110u8,216u8,2u8,105u8,35u8,25u8,22u8,151u8,197u8,84u8,79u8,44u8,159u8, - 243u8,208u8,217u8,179u8,217u8,132u8,111u8,140u8,139u8,11u8,213u8,212u8,197u8,18u8,70u8,65u8,134u8,67u8,227u8,56u8, - 204u8,5u8,233u8,33u8,229u8,16u8,105u8,0u8,49u8,140u8,11u8,182u8,15u8,128u8,24u8,123u8,61u8,57u8,226u8,162u8, - 11u8,219u8,24u8,87u8,206u8,152u8,200u8,250u8,150u8,137u8,179u8,165u8,128u8,62u8,190u8,85u8,142u8,179u8,156u8,246u8, - 92u8,148u8,149u8,113u8,48u8,76u8,244u8,218u8,208u8,51u8,11u8,49u8,200u8,179u8,156u8,147u8,81u8,1u8,131u8,94u8, - 50u8,137u8,25u8,188u8,77u8,96u8,118u8,220u8,171u8,49u8,181u8,53u8,155u8,20u8,237u8,100u8,12u8,110u8,165u8,169u8, - 96u8,174u8,124u8,238u8,127u8,29u8,29u8,236u8,3u8,216u8,13u8,115u8,100u8,56u8,46u8,7u8,131u8,7u8,103u8,23u8, - 89u8,33u8,52u8,233u8,236u8,60u8,127u8,202u8,31u8,61u8,57u8,124u8,149u8,12u8,243u8,209u8,57u8,108u8,176u8,186u8, - 40u8,103u8,0u8,168u8,76u8,66u8,99u8,8u8,203u8,153u8,34u8,152u8,164u8,19u8,120u8,139u8,137u8,227u8,112u8,100u8, - 2u8,139u8,180u8,127u8,107u8,114u8,149u8,3u8,51u8,93u8,31u8,229u8,211u8,135u8,47u8,47u8,47u8,71u8,143u8,103u8, - 195u8,7u8,223u8,191u8,126u8,247u8,228u8,221u8,95u8,151u8,223u8,255u8,235u8,170u8,247u8,228u8,199u8,131u8,7u8,123u8, - 15u8,222u8,245u8,159u8,188u8,248u8,107u8,235u8,223u8,147u8,226u8,95u8,7u8,155u8,163u8,226u8,106u8,231u8,226u8,177u8, - 222u8,31u8,44u8,221u8,143u8,100u8,72u8,12u8,230u8,52u8,176u8,150u8,153u8,217u8,1u8,159u8,101u8,195u8,179u8,217u8, - 48u8,19u8,123u8,134u8,167u8,39u8,82u8,198u8,29u8,228u8,176u8,45u8,70u8,16u8,135u8,85u8,201u8,245u8,8u8,216u8, - 111u8,101u8,73u8,190u8,138u8,32u8,177u8,113u8,164u8,150u8,167u8,134u8,145u8,162u8,178u8,94u8,140u8,28u8,118u8,91u8, - 125u8,17u8,65u8,11u8,128u8,45u8,95u8,20u8,103u8,248u8,48u8,37u8,93u8,98u8,48u8,36u8,13u8,26u8,38u8,245u8, - 202u8,213u8,249u8,195u8,48u8,124u8,184u8,250u8,213u8,10u8,208u8,113u8,168u8,229u8,147u8,173u8,31u8,58u8,206u8,93u8, - 152u8,20u8,237u8,239u8,155u8,91u8,143u8,30u8,223u8,11u8,80u8,23u8,181u8,244u8,221u8,188u8,58u8,155u8,20u8,167u8, - 220u8,249u8,169u8,160u8,90u8,63u8,208u8,231u8,95u8,142u8,195u8,68u8,208u8,18u8,92u8,106u8,56u8,99u8,27u8,170u8, - 233u8,13u8,39u8,29u8,252u8,1u8,205u8,140u8,107u8,156u8,69u8,51u8,218u8,162u8,14u8,33u8,84u8,212u8,106u8,6u8, - 119u8,119u8,88u8,19u8,9u8,34u8,203u8,45u8,88u8,181u8,109u8,131u8,19u8,25u8,253u8,81u8,10u8,0u8,49u8,40u8, - 206u8,37u8,71u8,127u8,173u8,63u8,223u8,225u8,31u8,147u8,228u8,87u8,193u8,21u8,45u8,172u8,40u8,205u8,170u8,137u8, - 21u8,202u8,81u8,82u8,142u8,166u8,85u8,170u8,65u8,217u8,194u8,119u8,235u8,49u8,132u8,1u8,4u8,97u8,40u8,16u8, - 204u8,49u8,122u8,242u8,191u8,236u8,165u8,161u8,208u8,246u8,249u8,49u8,95u8,194u8,129u8,22u8,227u8,161u8,230u8,184u8, - 149u8,16u8,59u8,207u8,39u8,229u8,108u8,204u8,89u8,168u8,252u8,88u8,129u8,155u8,16u8,56u8,18u8,45u8,54u8,73u8, - 116u8,89u8,119u8,68u8,46u8,142u8,77u8,169u8,194u8,162u8,171u8,2u8,174u8,243u8,84u8,74u8,174u8,198u8,155u8,10u8, - 209u8,234u8,17u8,140u8,55u8,45u8,46u8,111u8,28u8,124u8,193u8,157u8,90u8,112u8,41u8,96u8,10u8,203u8,207u8,24u8, - 165u8,171u8,36u8,90u8,73u8,99u8,129u8,3u8,103u8,12u8,196u8,176u8,249u8,18u8,51u8,62u8,251u8,166u8,209u8,125u8, - 73u8,17u8,147u8,16u8,46u8,165u8,83u8,205u8,60u8,103u8,68u8,191u8,241u8,100u8,6u8,47u8,99u8,254u8,178u8,78u8, - 243u8,80u8,64u8,252u8,168u8,97u8,56u8,77u8,140u8,28u8,66u8,1u8,39u8,49u8,140u8,38u8,92u8,145u8,67u8,33u8, - 148u8,167u8,134u8,51u8,40u8,66u8,227u8,144u8,88u8,8u8,166u8,70u8,195u8,88u8,94u8,15u8,246u8,1u8,155u8,134u8, - 198u8,76u8,73u8,5u8,36u8,80u8,59u8,149u8,156u8,175u8,170u8,193u8,91u8,124u8,81u8,219u8,182u8,240u8,133u8,11u8, - 115u8,61u8,187u8,101u8,230u8,59u8,117u8,149u8,6u8,201u8,213u8,245u8,88u8,153u8,51u8,154u8,86u8,3u8,96u8,137u8, - 60u8,174u8,224u8,39u8,165u8,127u8,138u8,25u8,158u8,215u8,7u8,32u8,236u8,200u8,169u8,178u8,35u8,219u8,231u8,35u8, - 57u8,140u8,136u8,25u8,128u8,209u8,80u8,224u8,195u8,79u8,187u8,226u8,59u8,254u8,17u8,26u8,81u8,217u8,144u8,201u8, - 119u8,148u8,105u8,192u8,126u8,137u8,217u8,230u8,200u8,23u8,94u8,192u8,23u8,124u8,205u8,246u8,27u8,66u8,9u8,73u8, - 13u8,117u8,238u8,154u8,28u8,226u8,53u8,127u8,146u8,15u8,130u8,168u8,50u8,30u8,111u8,33u8,236u8,69u8,246u8,34u8, - 15u8,250u8,214u8,79u8,220u8,20u8,135u8,151u8,6u8,224u8,11u8,227u8,45u8,178u8,154u8,51u8,90u8,17u8,77u8,69u8, - 40u8,188u8,48u8,252u8,171u8,35u8,37u8,99u8,94u8,230u8,211u8,140u8,43u8,190u8,76u8,178u8,201u8,12u8,11u8,173u8, - 115u8,158u8,21u8,129u8,30u8,245u8,160u8,92u8,249u8,85u8,144u8,47u8,37u8,0u8,244u8,50u8,163u8,184u8,8u8,144u8, - 106u8,125u8,217u8,64u8,22u8,169u8,50u8,215u8,68u8,26u8,189u8,34u8,152u8,77u8,138u8,102u8,164u8,224u8,110u8,135u8, - 63u8,85u8,207u8,107u8,67u8,176u8,28u8,5u8,233u8,235u8,254u8,81u8,244u8,42u8,28u8,52u8,0u8,190u8,153u8,162u8, - 184u8,30u8,27u8,246u8,225u8,107u8,7u8,125u8,200u8,59u8,49u8,37u8,59u8,125u8,27u8,244u8,249u8,7u8,149u8,177u8, - 174u8,1u8,174u8,74u8,161u8,80u8,198u8,113u8,61u8,67u8,175u8,150u8,255u8,142u8,203u8,172u8,159u8,28u8,128u8,86u8, - 51u8,132u8,95u8,238u8,205u8,39u8,201u8,89u8,238u8,28u8,175u8,0u8,11u8,70u8,49u8,16u8,147u8,153u8,244u8,118u8, - 134u8,192u8,136u8,145u8,226u8,90u8,173u8,127u8,90u8,107u8,189u8,242u8,125u8,46u8,137u8,19u8,162u8,44u8,33u8,201u8, - 250u8,181u8,114u8,191u8,32u8,171u8,188u8,61u8,234u8,144u8,176u8,104u8,205u8,156u8,167u8,142u8,106u8,99u8,172u8,195u8, - 212u8,110u8,226u8,20u8,147u8,90u8,57u8,172u8,117u8,239u8,90u8,14u8,129u8,191u8,38u8,210u8,82u8,55u8,21u8,132u8, - 80u8,173u8,167u8,89u8,215u8,219u8,27u8,104u8,21u8,156u8,89u8,225u8,64u8,69u8,28u8,61u8,248u8,43u8,159u8,148u8, - 194u8,86u8,217u8,81u8,174u8,3u8,57u8,187u8,50u8,16u8,185u8,115u8,187u8,155u8,133u8,195u8,201u8,255u8,59u8,203u8, - 134u8,66u8,63u8,44u8,42u8,69u8,103u8,2u8,211u8,131u8,226u8,46u8,64u8,75u8,89u8,188u8,185u8,126u8,43u8,196u8, - 60u8,161u8,248u8,114u8,173u8,199u8,216u8,98u8,71u8,16u8,163u8,5u8,244u8,125u8,69u8,39u8,5u8,232u8,157u8,153u8, - 104u8,162u8,125u8,212u8,14u8,181u8,116u8,69u8,123u8,63u8,199u8,112u8,48u8,23u8,89u8,245u8,225u8,88u8,180u8,227u8, - 150u8,253u8,194u8,162u8,61u8,69u8,180u8,100u8,71u8,236u8,81u8,226u8,111u8,125u8,178u8,236u8,110u8,24u8,251u8,177u8, - 57u8,150u8,98u8,207u8,59u8,245u8,224u8,12u8,203u8,105u8,189u8,64u8,80u8,29u8,46u8,117u8,16u8,54u8,47u8,241u8, - 45u8,105u8,251u8,38u8,237u8,211u8,220u8,75u8,192u8,67u8,88u8,211u8,42u8,63u8,163u8,109u8,126u8,71u8,57u8,183u8, - 148u8,11u8,243u8,168u8,8u8,49u8,225u8,87u8,202u8,41u8,31u8,219u8,67u8,158u8,93u8,38u8,253u8,25u8,131u8,124u8, - 0u8,64u8,16u8,108u8,242u8,226u8,157u8,177u8,63u8,44u8,178u8,4u8,118u8,22u8,183u8,234u8,133u8,23u8,166u8,46u8, - 43u8,27u8,82u8,103u8,191u8,244u8,197u8,73u8,202u8,203u8,16u8,142u8,81u8,73u8,177u8,166u8,162u8,175u8,13u8,68u8, - 10u8,23u8,13u8,82u8,79u8,112u8,157u8,152u8,53u8,217u8,134u8,230u8,32u8,9u8,167u8,17u8,138u8,36u8,95u8,90u8, - 185u8,12u8,232u8,96u8,115u8,41u8,161u8,145u8,74u8,45u8,77u8,186u8,231u8,83u8,202u8,13u8,155u8,54u8,200u8,44u8, - 149u8,142u8,238u8,19u8,227u8,62u8,39u8,30u8,227u8,52u8,179u8,126u8,176u8,142u8,6u8,164u8,30u8,158u8,94u8,143u8, - 115u8,207u8,160u8,8u8,12u8,120u8,244u8,132u8,184u8,112u8,14u8,2u8,235u8,66u8,214u8,21u8,224u8,0u8,239u8,48u8, - 87u8,18u8,16u8,100u8,110u8,245u8,151u8,252u8,144u8,157u8,47u8,11u8,56u8,159u8,104u8,170u8,9u8,112u8,205u8,61u8, - 174u8,218u8,21u8,134u8,129u8,198u8,20u8,54u8,22u8,1u8,151u8,0u8,24u8,227u8,137u8,76u8,165u8,96u8,137u8,184u8, - 98u8,236u8,198u8,175u8,54u8,4u8,102u8,4u8,55u8,33u8,77u8,244u8,192u8,83u8,66u8,127u8,17u8,134u8,136u8,24u8, - 64u8,136u8,187u8,127u8,105u8,33u8,65u8,242u8,146u8,184u8,122u8,110u8,29u8,70u8,23u8,206u8,237u8,34u8,153u8,25u8, - 96u8,201u8,208u8,192u8,37u8,15u8,150u8,104u8,27u8,56u8,21u8,191u8,107u8,4u8,201u8,208u8,52u8,174u8,145u8,228u8, - 96u8,78u8,178u8,242u8,193u8,113u8,243u8,202u8,237u8,246u8,128u8,111u8,95u8,51u8,15u8,151u8,16u8,7u8,141u8,160u8, - 119u8,116u8,102u8,32u8,156u8,92u8,142u8,167u8,215u8,174u8,12u8,203u8,207u8,110u8,156u8,177u8,136u8,127u8,45u8,130u8, - 241u8,160u8,189u8,51u8,46u8,206u8,240u8,161u8,9u8,109u8,65u8,44u8,190u8,229u8,30u8,207u8,154u8,100u8,219u8,13u8, - 244u8,39u8,184u8,125u8,242u8,192u8,104u8,82u8,43u8,117u8,232u8,42u8,159u8,50u8,132u8,214u8,16u8,196u8,180u8,55u8, - 9u8,63u8,109u8,48u8,135u8,253u8,119u8,198u8,220u8,247u8,134u8,246u8,245u8,222u8,175u8,10u8,213u8,187u8,66u8,59u8, - 235u8,24u8,31u8,114u8,57u8,221u8,248u8,4u8,109u8,200u8,252u8,2u8,54u8,99u8,126u8,32u8,55u8,98u8,125u8,104u8, - 236u8,67u8,127u8,213u8,126u8,234u8,193u8,1u8,228u8,80u8,101u8,50u8,83u8,49u8,26u8,207u8,166u8,225u8,203u8,19u8, - 210u8,140u8,125u8,111u8,210u8,84u8,72u8,220u8,91u8,200u8,17u8,183u8,192u8,157u8,202u8,128u8,93u8,251u8,250u8,238u8, - 62u8,255u8,108u8,134u8,176u8,79u8,195u8,29u8,3u8,192u8,221u8,193u8,86u8,184u8,26u8,208u8,135u8,185u8,228u8,158u8, - 41u8,236u8,73u8,178u8,253u8,228u8,153u8,9u8,31u8,142u8,92u8,208u8,90u8,51u8,15u8,64u8,66u8,164u8,49u8,158u8, - 244u8,212u8,91u8,99u8,177u8,79u8,77u8,84u8,82u8,174u8,96u8,19u8,191u8,106u8,237u8,101u8,81u8,20u8,35u8,49u8, - 202u8,193u8,59u8,18u8,128u8,60u8,143u8,56u8,176u8,19u8,124u8,78u8,131u8,142u8,249u8,148u8,125u8,214u8,230u8,183u8, - 6u8,196u8,120u8,190u8,18u8,80u8,226u8,249u8,146u8,67u8,5u8,166u8,20u8,132u8,69u8,37u8,226u8,252u8,241u8,13u8, - 154u8,223u8,72u8,148u8,237u8,4u8,168u8,17u8,147u8,151u8,132u8,116u8,37u8,8u8,210u8,96u8,82u8,94u8,178u8,96u8, - 8u8,83u8,194u8,226u8,166u8,161u8,117u8,241u8,48u8,119u8,213u8,214u8,66u8,153u8,142u8,164u8,112u8,226u8,96u8,54u8, - 104u8,106u8,198u8,247u8,22u8,79u8,197u8,144u8,105u8,202u8,79u8,50u8,230u8,39u8,117u8,142u8,0u8,182u8,32u8,34u8, - 210u8,152u8,163u8,62u8,110u8,17u8,224u8,132u8,183u8,178u8,8u8,34u8,33u8,252u8,133u8,251u8,227u8,183u8,161u8,36u8, - 39u8,236u8,69u8,229u8,122u8,32u8,139u8,56u8,184u8,52u8,111u8,88u8,120u8,109u8,71u8,229u8,85u8,125u8,20u8,85u8, - 5u8,247u8,246u8,183u8,150u8,177u8,242u8,117u8,53u8,230u8,179u8,103u8,82u8,143u8,223u8,222u8,150u8,139u8,78u8,203u8, - 65u8,75u8,222u8,87u8,27u8,12u8,79u8,44u8,217u8,103u8,123u8,27u8,174u8,254u8,178u8,168u8,42u8,110u8,249u8,204u8, - 71u8,69u8,222u8,111u8,17u8,241u8,208u8,109u8,180u8,246u8,249u8,65u8,60u8,37u8,97u8,92u8,92u8,100u8,8u8,196u8, - 5u8,249u8,67u8,6u8,64u8,237u8,93u8,97u8,18u8,102u8,197u8,130u8,217u8,184u8,93u8,8u8,135u8,17u8,169u8,48u8, - 177u8,35u8,29u8,50u8,239u8,120u8,12u8,169u8,184u8,175u8,13u8,30u8,82u8,198u8,29u8,122u8,90u8,108u8,35u8,99u8, - 196u8,88u8,250u8,171u8,198u8,156u8,243u8,124u8,196u8,168u8,143u8,122u8,88u8,89u8,45u8,234u8,135u8,226u8,23u8,192u8, - 99u8,34u8,234u8,69u8,252u8,47u8,139u8,23u8,21u8,17u8,191u8,134u8,95u8,9u8,148u8,8u8,190u8,121u8,150u8,157u8, - 201u8,109u8,4u8,38u8,182u8,195u8,220u8,45u8,123u8,244u8,182u8,7u8,155u8,9u8,23u8,8u8,88u8,208u8,226u8,49u8, - 219u8,103u8,187u8,185u8,129u8,184u8,46u8,6u8,14u8,125u8,215u8,16u8,197u8,66u8,26u8,135u8,110u8,85u8,71u8,154u8, - 139u8,250u8,40u8,132u8,13u8,224u8,37u8,67u8,219u8,58u8,78u8,76u8,160u8,40u8,192u8,70u8,202u8,147u8,180u8,91u8, - 100u8,38u8,73u8,219u8,18u8,12u8,138u8,58u8,87u8,212u8,36u8,121u8,46u8,181u8,43u8,250u8,41u8,200u8,176u8,17u8, - 82u8,183u8,117u8,117u8,20u8,146u8,251u8,62u8,183u8,175u8,204u8,71u8,16u8,207u8,139u8,119u8,94u8,172u8,99u8,148u8, - 47u8,207u8,192u8,60u8,58u8,53u8,162u8,53u8,192u8,100u8,10u8,78u8,254u8,86u8,145u8,252u8,36u8,111u8,190u8,141u8, - 14u8,27u8,73u8,144u8,229u8,72u8,110u8,184u8,165u8,67u8,226u8,16u8,12u8,119u8,244u8,241u8,116u8,56u8,172u8,116u8, - 36u8,76u8,116u8,196u8,189u8,163u8,85u8,178u8,31u8,118u8,186u8,69u8,242u8,15u8,149u8,94u8,203u8,73u8,214u8,83u8, - 90u8,81u8,60u8,102u8,126u8,209u8,76u8,58u8,44u8,2u8,202u8,162u8,131u8,155u8,150u8,95u8,213u8,97u8,186u8,96u8, - 37u8,236u8,231u8,148u8,198u8,39u8,173u8,150u8,212u8,87u8,42u8,230u8,97u8,169u8,204u8,120u8,94u8,116u8,68u8,216u8, - 210u8,192u8,158u8,189u8,176u8,106u8,239u8,163u8,99u8,24u8,225u8,133u8,23u8,199u8,33u8,129u8,79u8,125u8,46u8,235u8, - 150u8,56u8,202u8,142u8,62u8,57u8,12u8,12u8,18u8,166u8,140u8,219u8,117u8,238u8,170u8,28u8,79u8,211u8,2u8,88u8, - 156u8,53u8,174u8,67u8,61u8,229u8,131u8,194u8,189u8,25u8,115u8,14u8,108u8,127u8,73u8,144u8,135u8,215u8,187u8,170u8, - 211u8,111u8,149u8,178u8,201u8,6u8,36u8,30u8,100u8,3u8,203u8,21u8,15u8,134u8,25u8,211u8,65u8,152u8,146u8,1u8, - 107u8,2u8,74u8,115u8,149u8,138u8,176u8,88u8,38u8,55u8,255u8,84u8,47u8,235u8,121u8,139u8,77u8,221u8,94u8,183u8, - 118u8,88u8,143u8,121u8,223u8,28u8,80u8,252u8,245u8,212u8,34u8,220u8,168u8,110u8,192u8,246u8,118u8,14u8,158u8,17u8, - 185u8,80u8,249u8,46u8,255u8,186u8,198u8,78u8,241u8,169u8,43u8,17u8,28u8,203u8,201u8,171u8,228u8,15u8,113u8,57u8, - 127u8,160u8,72u8,69u8,46u8,1u8,255u8,193u8,254u8,255u8,7u8,99u8,206u8,127u8,76u8,203u8,63u8,214u8,205u8,136u8, - 38u8,121u8,199u8,252u8,43u8,110u8,112u8,130u8,199u8,232u8,148u8,56u8,112u8,188u8,76u8,100u8,106u8,136u8,115u8,227u8, - 26u8,45u8,153u8,13u8,64u8,110u8,160u8,198u8,77u8,54u8,191u8,31u8,253u8,62u8,9u8,87u8,156u8,150u8,196u8,180u8, - 183u8,136u8,155u8,29u8,135u8,93u8,96u8,22u8,225u8,89u8,51u8,2u8,82u8,141u8,68u8,236u8,44u8,49u8,66u8,78u8, - 75u8,26u8,41u8,217u8,5u8,51u8,179u8,174u8,142u8,102u8,169u8,147u8,180u8,234u8,84u8,0u8,254u8,205u8,183u8,21u8, - 149u8,196u8,207u8,195u8,151u8,39u8,179u8,156u8,72u8,121u8,47u8,84u8,172u8,76u8,86u8,71u8,79u8,214u8,177u8,50u8, - 14u8,108u8,240u8,80u8,148u8,83u8,208u8,224u8,197u8,236u8,49u8,86u8,58u8,175u8,28u8,245u8,49u8,169u8,113u8,156u8, - 230u8,36u8,55u8,161u8,3u8,224u8,72u8,234u8,36u8,31u8,66u8,247u8,169u8,196u8,30u8,153u8,174u8,242u8,156u8,9u8, - 186u8,82u8,180u8,145u8,217u8,97u8,105u8,54u8,57u8,7u8,77u8,30u8,72u8,64u8,67u8,250u8,95u8,187u8,253u8,116u8, - 1u8,128u8,92u8,144u8,87u8,216u8,91u8,134u8,73u8,212u8,232u8,235u8,164u8,206u8,229u8,110u8,217u8,16u8,29u8,68u8, - 122u8,247u8,79u8,232u8,172u8,159u8,27u8,43u8,108u8,155u8,226u8,147u8,43u8,253u8,121u8,203u8,29u8,180u8,59u8,164u8, - 53u8,130u8,111u8,0u8,89u8,94u8,159u8,17u8,132u8,222u8,187u8,26u8,56u8,175u8,118u8,195u8,134u8,100u8,17u8,26u8, - 21u8,108u8,220u8,90u8,67u8,147u8,161u8,19u8,234u8,120u8,142u8,173u8,113u8,191u8,190u8,108u8,245u8,192u8,118u8,235u8, - 9u8,216u8,109u8,137u8,245u8,201u8,77u8,195u8,110u8,205u8,229u8,115u8,214u8,231u8,89u8,50u8,33u8,200u8,218u8,203u8, - 167u8,23u8,97u8,103u8,157u8,213u8,126u8,115u8,112u8,82u8,3u8,177u8,40u8,88u8,164u8,106u8,149u8,235u8,12u8,111u8, - 35u8,67u8,137u8,199u8,119u8,75u8,194u8,53u8,45u8,173u8,74u8,38u8,225u8,187u8,48u8,171u8,252u8,168u8,43u8,97u8, - 38u8,174u8,214u8,26u8,50u8,5u8,185u8,225u8,198u8,29u8,32u8,73u8,178u8,16u8,210u8,108u8,58u8,248u8,161u8,69u8, - 208u8,199u8,54u8,125u8,83u8,132u8,233u8,192u8,155u8,7u8,219u8,246u8,222u8,152u8,69u8,50u8,149u8,24u8,97u8,238u8, - 135u8,213u8,71u8,72u8,153u8,228u8,180u8,140u8,205u8,184u8,80u8,77u8,173u8,161u8,179u8,208u8,54u8,45u8,80u8,120u8, - 97u8,220u8,32u8,138u8,7u8,102u8,59u8,23u8,89u8,127u8,239u8,185u8,163u8,49u8,237u8,104u8,250u8,204u8,22u8,147u8, - 3u8,73u8,188u8,196u8,154u8,10u8,14u8,28u8,77u8,147u8,15u8,112u8,64u8,58u8,84u8,146u8,139u8,33u8,66u8,190u8, - 98u8,9u8,42u8,147u8,81u8,54u8,108u8,57u8,138u8,141u8,193u8,54u8,77u8,100u8,17u8,254u8,182u8,103u8,13u8,66u8, - 32u8,31u8,17u8,189u8,47u8,107u8,90u8,113u8,81u8,142u8,255u8,110u8,71u8,91u8,18u8,184u8,134u8,230u8,91u8,71u8, - 65u8,155u8,38u8,112u8,89u8,254u8,89u8,113u8,60u8,246u8,70u8,172u8,83u8,74u8,62u8,208u8,200u8,8u8,102u8,23u8, - 100u8,29u8,91u8,87u8,49u8,204u8,192u8,129u8,108u8,77u8,17u8,61u8,37u8,189u8,213u8,207u8,136u8,207u8,30u8,152u8, - 211u8,162u8,153u8,228u8,125u8,239u8,230u8,112u8,174u8,200u8,100u8,37u8,40u8,17u8,44u8,67u8,14u8,192u8,60u8,156u8, - 92u8,236u8,220u8,88u8,55u8,181u8,74u8,115u8,161u8,106u8,9u8,207u8,220u8,133u8,114u8,37u8,38u8,7u8,32u8,47u8, - 175u8,145u8,201u8,178u8,37u8,73u8,221u8,36u8,191u8,44u8,223u8,229u8,173u8,16u8,101u8,243u8,17u8,227u8,182u8,181u8, - 27u8,185u8,35u8,100u8,132u8,66u8,190u8,75u8,148u8,187u8,140u8,127u8,76u8,134u8,19u8,73u8,134u8,189u8,228u8,216u8, - 26u8,169u8,67u8,190u8,229u8,227u8,200u8,110u8,240u8,25u8,97u8,118u8,192u8,199u8,111u8,205u8,22u8,2u8,22u8,66u8, - 116u8,55u8,33u8,199u8,243u8,197u8,3u8,108u8,53u8,176u8,206u8,185u8,95u8,67u8,14u8,58u8,103u8,5u8,62u8,228u8, - 241u8,139u8,192u8,182u8,10u8,229u8,82u8,53u8,237u8,169u8,1u8,174u8,108u8,248u8,178u8,94u8,111u8,2u8,50u8,251u8, - 190u8,156u8,99u8,96u8,18u8,87u8,187u8,77u8,31u8,193u8,7u8,247u8,99u8,235u8,163u8,15u8,126u8,139u8,139u8,43u8, - 254u8,215u8,214u8,214u8,6u8,105u8,189u8,101u8,11u8,226u8,119u8,205u8,114u8,178u8,184u8,153u8,241u8,147u8,201u8,219u8, - 213u8,74u8,224u8,94u8,64u8,224u8,94u8,73u8,219u8,243u8,75u8,219u8,31u8,79u8,20u8,230u8,149u8,99u8,226u8,5u8, - 97u8,79u8,161u8,153u8,54u8,101u8,252u8,198u8,82u8,48u8,39u8,66u8,183u8,39u8,3u8,203u8,93u8,248u8,37u8,224u8, - 122u8,254u8,206u8,2u8,187u8,251u8,4u8,210u8,175u8,176u8,235u8,47u8,95u8,222u8,37u8,44u8,27u8,66u8,4u8,254u8, - 220u8,100u8,96u8,46u8,149u8,50u8,58u8,35u8,93u8,134u8,82u8,6u8,48u8,74u8,97u8,194u8,223u8,146u8,103u8,218u8, - 37u8,69u8,228u8,28u8,235u8,119u8,78u8,201u8,230u8,254u8,115u8,36u8,156u8,202u8,109u8,129u8,212u8,3u8,62u8,84u8, - 30u8,176u8,175u8,50u8,155u8,229u8,55u8,204u8,109u8,42u8,131u8,252u8,241u8,56u8,42u8,238u8,159u8,201u8,68u8,70u8, - 137u8,24u8,166u8,139u8,207u8,70u8,102u8,42u8,187u8,21u8,217u8,200u8,131u8,192u8,70u8,101u8,61u8,93u8,49u8,26u8, - 148u8,235u8,43u8,237u8,99u8,165u8,125u8,4u8,181u8,15u8,106u8,163u8,24u8,156u8,57u8,148u8,177u8,202u8,219u8,24u8, - 124u8,77u8,112u8,12u8,76u8,208u8,164u8,203u8,172u8,84u8,153u8,59u8,173u8,202u8,232u8,26u8,200u8,66u8,89u8,209u8, - 30u8,227u8,218u8,54u8,200u8,243u8,64u8,56u8,49u8,43u8,184u8,100u8,227u8,86u8,150u8,82u8,99u8,188u8,52u8,94u8, - 230u8,206u8,112u8,246u8,240u8,14u8,145u8,99u8,100u8,68u8,176u8,162u8,248u8,16u8,188u8,239u8,58u8,8u8,176u8,229u8, - 184u8,51u8,188u8,110u8,44u8,42u8,54u8,181u8,49u8,48u8,248u8,198u8,206u8,6u8,35u8,189u8,146u8,107u8,1u8,33u8, - 95u8,0u8,95u8,177u8,173u8,135u8,68u8,16u8,18u8,143u8,239u8,52u8,104u8,82u8,15u8,130u8,150u8,181u8,14u8,37u8, - 156u8,57u8,232u8,108u8,135u8,138u8,174u8,227u8,0u8,205u8,128u8,220u8,230u8,148u8,52u8,197u8,32u8,236u8,115u8,212u8, - 210u8,32u8,144u8,74u8,0u8,17u8,158u8,91u8,191u8,231u8,207u8,94u8,187u8,177u8,82u8,244u8,7u8,90u8,72u8,224u8, - 21u8,184u8,6u8,244u8,23u8,229u8,153u8,167u8,97u8,119u8,54u8,41u8,230u8,129u8,89u8,20u8,6u8,221u8,8u8,171u8, - 58u8,168u8,71u8,202u8,220u8,34u8,237u8,178u8,181u8,198u8,171u8,211u8,252u8,244u8,204u8,170u8,119u8,30u8,208u8,196u8, - 113u8,121u8,206u8,118u8,64u8,43u8,94u8,33u8,67u8,3u8,50u8,176u8,248u8,227u8,91u8,68u8,2u8,24u8,126u8,81u8, - 224u8,103u8,25u8,244u8,28u8,186u8,2u8,208u8,206u8,158u8,121u8,198u8,158u8,137u8,135u8,110u8,201u8,222u8,231u8,129u8, - 112u8,28u8,254u8,191u8,34u8,199u8,113u8,16u8,200u8,19u8,103u8,121u8,57u8,214u8,179u8,139u8,108u8,116u8,94u8,215u8, - 180u8,226u8,145u8,39u8,27u8,184u8,36u8,190u8,245u8,0u8,19u8,185u8,155u8,1u8,89u8,62u8,252u8,55u8,166u8,137u8, - 172u8,173u8,37u8,248u8,239u8,0u8,217u8,176u8,138u8,134u8,82u8,202u8,189u8,22u8,255u8,124u8,226u8,220u8,205u8,134u8, - 111u8,198u8,72u8,149u8,38u8,112u8,139u8,88u8,41u8,167u8,88u8,20u8,51u8,245u8,10u8,229u8,47u8,1u8,4u8,85u8, - 167u8,249u8,76u8,61u8,107u8,73u8,112u8,62u8,177u8,203u8,42u8,216u8,228u8,147u8,184u8,234u8,172u8,0u8,63u8,90u8, - 7u8,202u8,52u8,206u8,135u8,217u8,18u8,61u8,235u8,57u8,77u8,236u8,180u8,102u8,178u8,112u8,50u8,27u8,14u8,83u8, - 195u8,20u8,16u8,131u8,128u8,164u8,29u8,183u8,141u8,244u8,167u8,167u8,243u8,152u8,26u8,204u8,37u8,248u8,151u8,187u8, - 100u8,212u8,37u8,180u8,250u8,91u8,197u8,90u8,71u8,57u8,191u8,217u8,200u8,120u8,249u8,31u8,13u8,87u8,101u8,78u8, - 78u8,16u8,77u8,125u8,90u8,180u8,253u8,13u8,14u8,36u8,107u8,66u8,95u8,226u8,174u8,104u8,204u8,13u8,161u8,34u8, - 147u8,31u8,35u8,98u8,185u8,98u8,202u8,167u8,34u8,217u8,50u8,46u8,75u8,242u8,214u8,5u8,204u8,185u8,137u8,192u8, - 231u8,74u8,5u8,162u8,224u8,255u8,54u8,164u8,71u8,241u8,185u8,95u8,112u8,92u8,24u8,238u8,185u8,64u8,105u8,202u8, - 147u8,214u8,183u8,49u8,162u8,100u8,125u8,243u8,50u8,189u8,110u8,78u8,150u8,99u8,215u8,138u8,92u8,62u8,219u8,249u8, - 162u8,33u8,78u8,30u8,223u8,220u8,80u8,23u8,9u8,118u8,42u8,101u8,210u8,0u8,189u8,230u8,148u8,15u8,47u8,76u8, - 6u8,158u8,242u8,229u8,128u8,240u8,141u8,203u8,117u8,172u8,207u8,147u8,194u8,25u8,122u8,209u8,155u8,35u8,74u8,188u8, - 218u8,156u8,121u8,26u8,185u8,172u8,121u8,215u8,66u8,46u8,128u8,114u8,2u8,186u8,139u8,6u8,192u8,147u8,191u8,69u8, - 163u8,111u8,216u8,68u8,23u8,66u8,225u8,69u8,172u8,116u8,43u8,44u8,254u8,88u8,38u8,56u8,57u8,49u8,159u8,33u8, - 96u8,124u8,91u8,152u8,141u8,68u8,24u8,229u8,232u8,167u8,3u8,246u8,56u8,94u8,205u8,141u8,117u8,18u8,193u8,17u8, - 173u8,40u8,165u8,83u8,58u8,174u8,19u8,79u8,141u8,255u8,70u8,88u8,87u8,206u8,241u8,101u8,201u8,101u8,159u8,32u8, - 77u8,112u8,89u8,138u8,24u8,44u8,61u8,5u8,169u8,16u8,46u8,67u8,204u8,93u8,203u8,136u8,108u8,83u8,214u8,195u8, - 176u8,19u8,207u8,195u8,98u8,143u8,182u8,207u8,252u8,122u8,236u8,121u8,220u8,206u8,142u8,83u8,88u8,162u8,87u8,243u8, - 76u8,205u8,229u8,200u8,164u8,21u8,187u8,203u8,86u8,99u8,111u8,146u8,118u8,120u8,116u8,185u8,180u8,155u8,140u8,254u8, - 69u8,83u8,36u8,28u8,18u8,178u8,32u8,65u8,226u8,249u8,164u8,86u8,3u8,82u8,245u8,57u8,171u8,86u8,100u8,161u8, - 133u8,104u8,190u8,251u8,147u8,209u8,79u8,246u8,249u8,115u8,4u8,59u8,188u8,20u8,78u8,203u8,154u8,128u8,85u8,55u8, - 178u8,6u8,50u8,71u8,8u8,13u8,32u8,17u8,103u8,196u8,42u8,227u8,200u8,94u8,46u8,169u8,242u8,225u8,215u8,52u8, - 194u8,70u8,4u8,148u8,57u8,74u8,162u8,140u8,229u8,37u8,149u8,72u8,134u8,214u8,33u8,238u8,76u8,60u8,221u8,73u8, - 10u8,203u8,19u8,168u8,142u8,103u8,252u8,142u8,101u8,139u8,14u8,80u8,82u8,246u8,252u8,33u8,73u8,240u8,76u8,155u8, - 242u8,112u8,170u8,190u8,199u8,85u8,121u8,153u8,183u8,238u8,155u8,19u8,168u8,213u8,197u8,12u8,109u8,58u8,38u8,147u8, - 124u8,88u8,229u8,129u8,217u8,224u8,156u8,115u8,235u8,118u8,90u8,237u8,111u8,2u8,174u8,78u8,117u8,96u8,227u8,89u8, - 117u8,145u8,158u8,102u8,103u8,111u8,133u8,153u8,182u8,6u8,158u8,142u8,60u8,41u8,226u8,8u8,25u8,96u8,240u8,35u8, - 180u8,142u8,79u8,68u8,30u8,154u8,181u8,58u8,120u8,252u8,225u8,125u8,251u8,122u8,212u8,20u8,69u8,187u8,147u8,56u8, - 95u8,114u8,2u8,198u8,190u8,107u8,71u8,45u8,185u8,6u8,211u8,142u8,92u8,153u8,157u8,26u8,12u8,23u8,173u8,206u8, - 169u8,168u8,82u8,126u8,49u8,107u8,114u8,115u8,212u8,13u8,154u8,187u8,18u8,206u8,127u8,107u8,87u8,45u8,20u8,51u8, - 20u8,186u8,70u8,207u8,138u8,188u8,215u8,105u8,78u8,13u8,98u8,105u8,212u8,68u8,247u8,3u8,51u8,197u8,102u8,73u8, - 135u8,196u8,28u8,107u8,206u8,235u8,165u8,136u8,57u8,2u8,67u8,49u8,188u8,213u8,23u8,73u8,148u8,85u8,177u8,106u8, - 78u8,224u8,22u8,68u8,229u8,32u8,1u8,232u8,199u8,61u8,129u8,92u8,249u8,164u8,78u8,53u8,191u8,113u8,33u8,133u8, - 64u8,17u8,204u8,219u8,149u8,85u8,244u8,132u8,11u8,230u8,7u8,46u8,16u8,182u8,59u8,103u8,69u8,4u8,79u8,209u8, - 146u8,80u8,101u8,4u8,184u8,91u8,46u8,29u8,176u8,219u8,53u8,139u8,181u8,115u8,201u8,211u8,173u8,176u8,170u8,150u8, - 22u8,8u8,34u8,110u8,119u8,230u8,138u8,25u8,94u8,134u8,249u8,156u8,210u8,219u8,107u8,117u8,210u8,16u8,59u8,92u8, - 97u8,193u8,10u8,32u8,166u8,133u8,13u8,55u8,110u8,120u8,158u8,88u8,225u8,246u8,18u8,196u8,29u8,103u8,5u8,102u8, - 228u8,31u8,47u8,105u8,110u8,232u8,14u8,186u8,15u8,0u8,159u8,161u8,99u8,87u8,139u8,97u8,21u8,133u8,135u8,87u8, - 89u8,117u8,237u8,220u8,49u8,107u8,21u8,150u8,139u8,216u8,171u8,81u8,46u8,154u8,76u8,137u8,192u8,43u8,186u8,179u8, - 182u8,72u8,38u8,166u8,38u8,47u8,46u8,237u8,161u8,25u8,51u8,248u8,91u8,164u8,120u8,102u8,51u8,7u8,50u8,170u8, - 217u8,37u8,219u8,55u8,136u8,108u8,166u8,247u8,103u8,135u8,55u8,179u8,159u8,121u8,4u8,71u8,227u8,101u8,59u8,208u8, - 173u8,6u8,7u8,182u8,172u8,84u8,158u8,215u8,210u8,162u8,156u8,125u8,59u8,122u8,74u8,158u8,172u8,187u8,144u8,133u8, - 69u8,228u8,15u8,38u8,116u8,10u8,248u8,177u8,218u8,65u8,24u8,165u8,134u8,24u8,148u8,245u8,89u8,201u8,71u8,48u8, - 24u8,229u8,50u8,96u8,85u8,65u8,31u8,81u8,242u8,199u8,233u8,209u8,86u8,71u8,107u8,2u8,162u8,184u8,77u8,246u8, - 136u8,128u8,56u8,125u8,244u8,77u8,105u8,131u8,158u8,170u8,40u8,155u8,214u8,41u8,46u8,86u8,173u8,69u8,115u8,137u8, - 217u8,36u8,37u8,122u8,3u8,162u8,219u8,246u8,245u8,172u8,50u8,5u8,10u8,44u8,37u8,250u8,243u8,82u8,90u8,158u8, - 184u8,98u8,114u8,25u8,237u8,192u8,232u8,48u8,180u8,12u8,89u8,119u8,67u8,46u8,129u8,71u8,227u8,85u8,184u8,88u8, - 164u8,34u8,182u8,55u8,221u8,175u8,220u8,8u8,247u8,251u8,13u8,144u8,31u8,18u8,188u8,100u8,149u8,14u8,217u8,122u8, - 195u8,188u8,80u8,189u8,68u8,107u8,155u8,82u8,242u8,180u8,234u8,50u8,208u8,64u8,97u8,108u8,51u8,162u8,92u8,142u8, - 27u8,34u8,31u8,234u8,168u8,225u8,82u8,188u8,136u8,242u8,36u8,104u8,125u8,237u8,245u8,80u8,103u8,15u8,247u8,236u8, - 67u8,101u8,122u8,233u8,152u8,82u8,85u8,162u8,215u8,127u8,213u8,236u8,71u8,213u8,235u8,13u8,3u8,133u8,146u8,228u8, - 232u8,111u8,168u8,10u8,132u8,134u8,244u8,230u8,6u8,153u8,118u8,130u8,4u8,56u8,6u8,193u8,158u8,145u8,104u8,225u8, - 96u8,191u8,78u8,76u8,47u8,39u8,32u8,184u8,21u8,217u8,144u8,40u8,34u8,168u8,66u8,230u8,7u8,0u8,70u8,50u8, - 8u8,162u8,116u8,137u8,129u8,39u8,11u8,36u8,245u8,103u8,126u8,136u8,182u8,131u8,223u8,216u8,199u8,173u8,142u8,249u8, - 155u8,160u8,238u8,163u8,34u8,173u8,55u8,121u8,3u8,21u8,171u8,191u8,132u8,40u8,200u8,139u8,90u8,237u8,177u8,125u8, - 224u8,54u8,169u8,27u8,11u8,163u8,205u8,10u8,101u8,98u8,81u8,230u8,83u8,162u8,139u8,139u8,45u8,24u8,158u8,60u8, - 78u8,24u8,201u8,111u8,148u8,231u8,52u8,92u8,219u8,182u8,177u8,70u8,174u8,167u8,81u8,88u8,91u8,251u8,86u8,137u8, - 192u8,0u8,223u8,148u8,44u8,56u8,32u8,224u8,167u8,106u8,14u8,33u8,9u8,54u8,220u8,166u8,140u8,179u8,74u8,213u8, - 221u8,22u8,74u8,83u8,154u8,77u8,91u8,198u8,126u8,218u8,129u8,73u8,99u8,122u8,111u8,227u8,57u8,221u8,243u8,96u8, - 63u8,115u8,121u8,241u8,26u8,189u8,119u8,180u8,215u8,238u8,131u8,109u8,52u8,144u8,157u8,33u8,16u8,41u8,145u8,5u8, - 92u8,193u8,163u8,33u8,155u8,57u8,112u8,92u8,251u8,182u8,170u8,27u8,196u8,141u8,250u8,9u8,195u8,112u8,94u8,178u8, - 149u8,87u8,212u8,183u8,225u8,74u8,117u8,207u8,50u8,10u8,215u8,217u8,222u8,60u8,169u8,170u8,71u8,213u8,19u8,19u8, - 35u8,164u8,183u8,82u8,87u8,204u8,18u8,59u8,240u8,92u8,114u8,165u8,109u8,251u8,208u8,100u8,197u8,45u8,249u8,10u8, - 239u8,47u8,85u8,177u8,126u8,45u8,99u8,94u8,26u8,197u8,170u8,199u8,229u8,28u8,142u8,49u8,159u8,81u8,137u8,11u8, - 207u8,93u8,91u8,85u8,230u8,62u8,46u8,89u8,152u8,76u8,23u8,4u8,179u8,242u8,5u8,205u8,2u8,105u8,104u8,202u8, - 64u8,161u8,52u8,133u8,37u8,214u8,208u8,33u8,205u8,227u8,228u8,168u8,119u8,200u8,193u8,255u8,224u8,205u8,113u8,186u8, - 7u8,26u8,247u8,222u8,33u8,216u8,22u8,210u8,227u8,195u8,238u8,254u8,209u8,75u8,51u8,147u8,51u8,226u8,10u8,124u8, - 126u8,100u8,187u8,120u8,221u8,141u8,10u8,13u8,206u8,211u8,187u8,37u8,186u8,230u8,152u8,155u8,241u8,169u8,10u8,247u8, - 5u8,82u8,62u8,77u8,12u8,178u8,42u8,252u8,121u8,15u8,35u8,12u8,243u8,122u8,195u8,88u8,199u8,226u8,186u8,190u8, - 180u8,19u8,53u8,228u8,144u8,42u8,212u8,113u8,12u8,194u8,44u8,15u8,9u8,38u8,116u8,217u8,57u8,89u8,87u8,151u8, - 253u8,144u8,39u8,230u8,176u8,48u8,224u8,33u8,210u8,238u8,2u8,28u8,182u8,213u8,166u8,25u8,164u8,211u8,200u8,111u8, - 144u8,129u8,236u8,228u8,121u8,214u8,234u8,234u8,167u8,105u8,62u8,227u8,224u8,66u8,159u8,188u8,32u8,122u8,252u8,213u8, - 149u8,135u8,201u8,81u8,157u8,198u8,127u8,254u8,97u8,205u8,54u8,128u8,13u8,227u8,26u8,189u8,1u8,253u8,99u8,218u8, - 121u8,187u8,225u8,65u8,195u8,237u8,3u8,253u8,179u8,4u8,229u8,56u8,255u8,148u8,126u8,65u8,197u8,39u8,147u8,0u8, - 211u8,59u8,207u8,91u8,253u8,74u8,194u8,254u8,182u8,144u8,9u8,143u8,69u8,147u8,94u8,209u8,79u8,50u8,53u8,9u8, - 162u8,43u8,85u8,172u8,233u8,215u8,215u8,153u8,94u8,13u8,230u8,73u8,252u8,226u8,58u8,195u8,184u8,70u8,1u8,66u8, - 24u8,101u8,64u8,120u8,248u8,167u8,97u8,50u8,169u8,71u8,150u8,233u8,198u8,207u8,220u8,143u8,254u8,97u8,172u8,115u8, - 93u8,165u8,188u8,222u8,76u8,119u8,192u8,67u8,250u8,240u8,191u8,26u8,131u8,145u8,206u8,119u8,118u8,136u8,130u8,41u8, - 51u8,57u8,113u8,116u8,248u8,228u8,104u8,11u8,141u8,207u8,103u8,189u8,255u8,82u8,56u8,168u8,143u8,222u8,128u8,253u8, - 218u8,182u8,52u8,185u8,195u8,235u8,10u8,31u8,42u8,93u8,222u8,123u8,35u8,226u8,38u8,248u8,176u8,178u8,180u8,71u8, - 250u8,170u8,11u8,215u8,114u8,152u8,130u8,84u8,215u8,251u8,247u8,73u8,247u8,21u8,92u8,150u8,188u8,47u8,183u8,244u8, - 199u8,92u8,21u8,69u8,196u8,32u8,178u8,56u8,192u8,207u8,221u8,95u8,122u8,190u8,130u8,34u8,49u8,96u8,240u8,32u8, - 177u8,111u8,158u8,50u8,194u8,48u8,8u8,192u8,135u8,30u8,174u8,52u8,94u8,115u8,41u8,12u8,33u8,166u8,25u8,82u8, - 250u8,153u8,59u8,141u8,168u8,102u8,218u8,153u8,0u8,90u8,4u8,66u8,169u8,95u8,192u8,137u8,82u8,47u8,117u8,77u8, - 173u8,111u8,57u8,117u8,77u8,241u8,42u8,28u8,214u8,237u8,169u8,67u8,74u8,242u8,229u8,229u8,21u8,8u8,157u8,71u8, - 106u8,154u8,150u8,159u8,76u8,86u8,10u8,200u8,21u8,162u8,230u8,40u8,45u8,79u8,216u8,182u8,53u8,71u8,148u8,168u8, - 47u8,180u8,46u8,61u8,196u8,107u8,249u8,171u8,222u8,49u8,172u8,105u8,22u8,184u8,8u8,31u8,76u8,139u8,203u8,188u8, - 238u8,187u8,135u8,218u8,241u8,113u8,27u8,4u8,111u8,227u8,160u8,58u8,218u8,37u8,236u8,89u8,143u8,210u8,169u8,215u8, - 94u8,143u8,208u8,92u8,207u8,104u8,105u8,205u8,245u8,0u8,200u8,137u8,158u8,127u8,53u8,64u8,4u8,191u8,172u8,87u8, - 34u8,215u8,233u8,45u8,184u8,65u8,151u8,137u8,104u8,70u8,113u8,115u8,221u8,29u8,191u8,250u8,166u8,214u8,105u8,155u8, - 130u8,50u8,116u8,49u8,246u8,5u8,152u8,102u8,116u8,234u8,248u8,245u8,19u8,64u8,88u8,202u8,1u8,117u8,84u8,166u8, - 187u8,181u8,1u8,187u8,152u8,255u8,18u8,44u8,82u8,131u8,107u8,25u8,60u8,55u8,204u8,207u8,5u8,120u8,92u8,100u8, - 188u8,71u8,37u8,223u8,45u8,56u8,17u8,174u8,243u8,169u8,235u8,248u8,3u8,8u8,2u8,6u8,115u8,57u8,102u8,62u8, - 188u8,43u8,118u8,26u8,229u8,168u8,95u8,181u8,120u8,246u8,192u8,253u8,53u8,115u8,161u8,235u8,214u8,177u8,249u8,233u8, - 60u8,171u8,21u8,181u8,123u8,216u8,253u8,149u8,57u8,93u8,14u8,94u8,130u8,130u8,254u8,6u8,16u8,238u8,200u8,136u8, - 170u8,10u8,58u8,27u8,190u8,33u8,229u8,63u8,177u8,8u8,108u8,60u8,107u8,126u8,206u8,134u8,6u8,235u8,33u8,167u8, - 211u8,196u8,34u8,0u8,224u8,144u8,220u8,113u8,54u8,97u8,122u8,66u8,186u8,60u8,72u8,48u8,158u8,117u8,201u8,239u8, - 118u8,210u8,146u8,82u8,136u8,12u8,179u8,114u8,199u8,121u8,222u8,254u8,18u8,161u8,72u8,45u8,192u8,58u8,27u8,54u8, - 47u8,125u8,211u8,33u8,193u8,244u8,232u8,228u8,229u8,203u8,189u8,157u8,189u8,30u8,8u8,64u8,122u8,234u8,58u8,98u8, - 0u8,73u8,41u8,6u8,179u8,152u8,48u8,9u8,193u8,127u8,236u8,50u8,202u8,203u8,94u8,224u8,51u8,207u8,2u8,109u8, - 221u8,207u8,8u8,174u8,34u8,134u8,71u8,17u8,86u8,164u8,85u8,219u8,8u8,5u8,115u8,212u8,135u8,70u8,50u8,76u8, - 146u8,227u8,88u8,132u8,116u8,185u8,74u8,20u8,138u8,218u8,28u8,135u8,60u8,39u8,144u8,254u8,172u8,35u8,165u8,7u8, - 177u8,57u8,84u8,24u8,22u8,93u8,253u8,202u8,84u8,175u8,72u8,207u8,174u8,135u8,90u8,53u8,210u8,181u8,121u8,104u8, - 91u8,52u8,125u8,35u8,8u8,133u8,165u8,34u8,90u8,230u8,78u8,220u8,173u8,234u8,27u8,143u8,32u8,107u8,201u8,65u8, - 17u8,225u8,75u8,145u8,54u8,158u8,56u8,254u8,182u8,168u8,137u8,50u8,120u8,21u8,166u8,17u8,12u8,9u8,117u8,54u8, - 7u8,216u8,193u8,77u8,127u8,80u8,117u8,26u8,144u8,183u8,47u8,192u8,127u8,129u8,235u8,171u8,184u8,82u8,24u8,213u8, - 191u8,240u8,46u8,118u8,101u8,188u8,89u8,186u8,33u8,79u8,134u8,87u8,249u8,134u8,117u8,144u8,83u8,186u8,223u8,125u8, - 221u8,107u8,206u8,61u8,180u8,95u8,240u8,231u8,33u8,222u8,114u8,37u8,141u8,102u8,24u8,115u8,139u8,24u8,96u8,251u8, - 158u8,17u8,141u8,101u8,24u8,126u8,163u8,45u8,122u8,100u8,186u8,21u8,251u8,161u8,47u8,196u8,120u8,213u8,204u8,69u8, - 143u8,177u8,237u8,213u8,110u8,223u8,152u8,167u8,221u8,126u8,163u8,205u8,102u8,45u8,178u8,161u8,43u8,10u8,146u8,11u8, - 77u8,132u8,130u8,50u8,98u8,39u8,50u8,27u8,75u8,55u8,205u8,83u8,183u8,43u8,139u8,49u8,208u8,25u8,77u8,136u8, - 67u8,67u8,219u8,54u8,57u8,146u8,105u8,208u8,133u8,43u8,98u8,194u8,252u8,76u8,143u8,130u8,53u8,70u8,83u8,253u8, - 203u8,191u8,217u8,129u8,126u8,78u8,141u8,11u8,142u8,203u8,100u8,41u8,203u8,108u8,200u8,2u8,176u8,174u8,85u8,250u8, - 9u8,198u8,219u8,238u8,171u8,195u8,94u8,119u8,247u8,55u8,225u8,137u8,59u8,10u8,84u8,5u8,117u8,66u8,221u8,234u8, - 168u8,28u8,92u8,176u8,97u8,206u8,126u8,151u8,136u8,42u8,63u8,179u8,75u8,88u8,189u8,143u8,235u8,86u8,41u8,72u8, - 239u8,253u8,53u8,55u8,77u8,209u8,105u8,89u8,41u8,210u8,236u8,183u8,193u8,208u8,21u8,215u8,65u8,214u8,216u8,9u8, - 9u8,12u8,242u8,82u8,88u8,172u8,53u8,121u8,31u8,184u8,38u8,133u8,191u8,248u8,137u8,0u8,212u8,230u8,194u8,178u8, - 6u8,0u8,133u8,42u8,86u8,122u8,144u8,55u8,84u8,195u8,81u8,46u8,98u8,221u8,71u8,43u8,204u8,211u8,161u8,219u8, - 61u8,191u8,247u8,81u8,197u8,109u8,131u8,66u8,187u8,136u8,231u8,84u8,180u8,161u8,174u8,147u8,188u8,210u8,32u8,104u8, - 120u8,47u8,247u8,131u8,167u8,65u8,33u8,22u8,3u8,88u8,200u8,159u8,191u8,196u8,13u8,114u8,66u8,98u8,222u8,223u8, - 22u8,173u8,160u8,98u8,217u8,113u8,108u8,97u8,227u8,91u8,174u8,105u8,172u8,137u8,152u8,105u8,215u8,35u8,87u8,226u8, - 210u8,45u8,11u8,23u8,26u8,8u8,84u8,240u8,184u8,125u8,153u8,114u8,232u8,180u8,189u8,197u8,143u8,220u8,142u8,74u8, - 159u8,211u8,101u8,152u8,49u8,227u8,49u8,247u8,208u8,156u8,140u8,22u8,232u8,132u8,26u8,211u8,212u8,169u8,237u8,191u8, - 90u8,127u8,54u8,91u8,56u8,158u8,134u8,104u8,100u8,252u8,209u8,58u8,102u8,127u8,6u8,189u8,177u8,235u8,158u8,209u8, - 156u8,2u8,51u8,174u8,35u8,245u8,172u8,215u8,154u8,27u8,237u8,88u8,189u8,143u8,63u8,85u8,167u8,108u8,35u8,225u8, - 118u8,41u8,234u8,7u8,119u8,147u8,69u8,233u8,29u8,250u8,201u8,121u8,20u8,14u8,196u8,136u8,63u8,111u8,133u8,231u8, - 211u8,71u8,136u8,221u8,88u8,191u8,143u8,38u8,185u8,134u8,196u8,179u8,36u8,186u8,75u8,243u8,192u8,106u8,110u8,241u8, - 107u8,126u8,170u8,107u8,90u8,46u8,236u8,46u8,10u8,141u8,37u8,240u8,131u8,189u8,2u8,28u8,110u8,235u8,75u8,168u8, - 155u8,227u8,24u8,99u8,78u8,49u8,78u8,89u8,105u8,104u8,107u8,16u8,236u8,102u8,96u8,107u8,47u8,40u8,77u8,41u8, - 90u8,123u8,49u8,244u8,12u8,79u8,145u8,193u8,197u8,78u8,211u8,73u8,99u8,66u8,83u8,233u8,210u8,84u8,60u8,56u8, - 128u8,71u8,194u8,245u8,75u8,102u8,145u8,191u8,202u8,70u8,12u8,100u8,146u8,124u8,52u8,40u8,39u8,103u8,168u8,124u8, - 251u8,8u8,168u8,6u8,171u8,0u8,188u8,110u8,101u8,51u8,137u8,215u8,120u8,73u8,114u8,245u8,48u8,43u8,188u8,144u8, - 189u8,85u8,174u8,13u8,81u8,68u8,184u8,228u8,30u8,140u8,12u8,22u8,48u8,44u8,254u8,114u8,50u8,151u8,234u8,149u8, - 133u8,202u8,150u8,163u8,167u8,136u8,122u8,209u8,234u8,51u8,39u8,105u8,195u8,155u8,222u8,68u8,205u8,74u8,140u8,232u8, - 77u8,82u8,34u8,168u8,48u8,107u8,212u8,213u8,75u8,127u8,61u8,56u8,121u8,181u8,11u8,87u8,191u8,211u8,235u8,237u8, - 98u8,82u8,174u8,106u8,164u8,249u8,3u8,108u8,66u8,178u8,87u8,93u8,208u8,239u8,125u8,179u8,146u8,232u8,11u8,185u8, - 119u8,84u8,76u8,143u8,230u8,233u8,168u8,51u8,186u8,232u8,82u8,100u8,0u8,114u8,39u8,72u8,233u8,73u8,57u8,166u8, - 29u8,106u8,103u8,31u8,84u8,167u8,220u8,172u8,21u8,59u8,192u8,130u8,217u8,160u8,12u8,129u8,164u8,99u8,11u8,30u8, - 29u8,75u8,184u8,104u8,55u8,104u8,221u8,219u8,148u8,80u8,212u8,105u8,210u8,197u8,35u8,138u8,213u8,235u8,148u8,161u8, - 250u8,79u8,140u8,203u8,62u8,157u8,218u8,182u8,83u8,133u8,219u8,34u8,248u8,236u8,97u8,148u8,50u8,109u8,142u8,236u8, - 73u8,72u8,242u8,180u8,81u8,88u8,92u8,19u8,38u8,161u8,208u8,43u8,3u8,7u8,30u8,107u8,8u8,186u8,14u8,73u8, - 193u8,238u8,147u8,180u8,242u8,63u8,47u8,112u8,144u8,34u8,114u8,224u8,107u8,95u8,182u8,129u8,9u8,177u8,150u8,37u8, - 161u8,19u8,174u8,251u8,148u8,186u8,113u8,65u8,147u8,124u8,170u8,50u8,109u8,224u8,8u8,78u8,89u8,120u8,244u8,32u8, - 233u8,3u8,111u8,43u8,70u8,103u8,182u8,32u8,33u8,0u8,168u8,159u8,204u8,88u8,92u8,172u8,136u8,52u8,177u8,186u8, - 4u8,32u8,37u8,235u8,60u8,55u8,138u8,167u8,10u8,154u8,99u8,23u8,199u8,109u8,86u8,165u8,219u8,218u8,41u8,10u8, - 202u8,201u8,243u8,200u8,26u8,55u8,159u8,117u8,45u8,96u8,15u8,151u8,108u8,236u8,240u8,97u8,184u8,104u8,233u8,34u8, - 188u8,145u8,78u8,94u8,230u8,36u8,198u8,14u8,97u8,143u8,26u8,109u8,221u8,48u8,81u8,25u8,108u8,174u8,107u8,22u8, - 191u8,124u8,13u8,55u8,236u8,10u8,35u8,78u8,213u8,171u8,184u8,35u8,71u8,53u8,231u8,87u8,71u8,61u8,215u8,81u8, - 195u8,201u8,197u8,29u8,177u8,85u8,33u8,121u8,174u8,99u8,102u8,53u8,141u8,190u8,226u8,51u8,150u8,71u8,183u8,8u8, - 199u8,161u8,153u8,141u8,183u8,114u8,27u8,187u8,51u8,153u8,73u8,209u8,196u8,102u8,252u8,117u8,218u8,22u8,225u8,52u8, - 193u8,218u8,29u8,110u8,74u8,218u8,114u8,106u8,120u8,52u8,219u8,99u8,235u8,235u8,163u8,236u8,178u8,243u8,21u8,235u8, - 184u8,221u8,26u8,29u8,141u8,229u8,200u8,22u8,104u8,117u8,101u8,48u8,66u8,167u8,174u8,245u8,28u8,60u8,144u8,3u8, - 130u8,143u8,17u8,90u8,160u8,236u8,205u8,119u8,102u8,81u8,214u8,113u8,192u8,203u8,55u8,237u8,27u8,103u8,81u8,144u8, - 142u8,166u8,65u8,43u8,80u8,110u8,4u8,229u8,121u8,32u8,215u8,155u8,0u8,31u8,128u8,33u8,21u8,206u8,207u8,114u8, - 198u8,51u8,94u8,255u8,66u8,86u8,171u8,244u8,211u8,59u8,34u8,43u8,192u8,77u8,10u8,160u8,114u8,2u8,172u8,209u8, - 236u8,252u8,188u8,58u8,251u8,174u8,78u8,15u8,54u8,221u8,87u8,100u8,136u8,149u8,47u8,77u8,77u8,13u8,231u8,68u8, - 176u8,200u8,221u8,243u8,100u8,48u8,82u8,143u8,246u8,7u8,247u8,235u8,49u8,237u8,16u8,255u8,8u8,175u8,15u8,17u8, - 26u8,96u8,135u8,53u8,173u8,225u8,184u8,166u8,182u8,199u8,243u8,129u8,118u8,211u8,244u8,61u8,82u8,122u8,183u8,157u8, - 66u8,113u8,230u8,228u8,29u8,89u8,28u8,136u8,25u8,231u8,101u8,57u8,152u8,116u8,111u8,127u8,183u8,237u8,152u8,107u8, - 98u8,7u8,98u8,198u8,123u8,255u8,64u8,218u8,204u8,19u8,55u8,152u8,50u8,200u8,251u8,7u8,52u8,60u8,95u8,113u8, - 131u8,238u8,246u8,142u8,118u8,14u8,247u8,222u8,8u8,147u8,153u8,119u8,96u8,163u8,132u8,73u8,212u8,184u8,118u8,181u8, - 30u8,107u8,208u8,134u8,140u8,251u8,96u8,44u8,73u8,19u8,192u8,212u8,212u8,44u8,0u8,21u8,81u8,15u8,205u8,125u8, - 166u8,200u8,254u8,24u8,119u8,176u8,81u8,176u8,132u8,70u8,13u8,3u8,84u8,52u8,152u8,155u8,118u8,210u8,57u8,239u8, - 168u8,14u8,187u8,138u8,47u8,132u8,23u8,174u8,207u8,235u8,13u8,44u8,157u8,175u8,78u8,157u8,91u8,57u8,21u8,21u8, - 166u8,99u8,230u8,247u8,128u8,87u8,170u8,169u8,254u8,220u8,222u8,254u8,177u8,167u8,250u8,156u8,183u8,179u8,181u8,183u8, - 50u8,222u8,194u8,21u8,226u8,140u8,150u8,210u8,119u8,94u8,196u8,140u8,170u8,122u8,187u8,128u8,152u8,73u8,20u8,200u8, - 213u8,238u8,136u8,58u8,125u8,128u8,234u8,32u8,226u8,245u8,37u8,240u8,235u8,53u8,60u8,9u8,154u8,17u8,216u8,141u8, - 86u8,76u8,152u8,14u8,245u8,116u8,253u8,135u8,147u8,223u8,104u8,213u8,19u8,3u8,143u8,14u8,220u8,100u8,114u8,201u8, - 160u8,88u8,38u8,4u8,26u8,117u8,106u8,234u8,130u8,59u8,145u8,141u8,218u8,91u8,150u8,81u8,123u8,35u8,24u8,30u8, - 102u8,71u8,68u8,186u8,70u8,236u8,185u8,192u8,209u8,137u8,198u8,52u8,105u8,147u8,57u8,219u8,98u8,134u8,109u8,42u8, - 30u8,254u8,3u8,233u8,237u8,35u8,43u8,108u8,152u8,47u8,134u8,42u8,126u8,53u8,85u8,251u8,138u8,41u8,245u8,69u8, - 39u8,157u8,178u8,123u8,175u8,64u8,125u8,7u8,40u8,166u8,202u8,164u8,143u8,74u8,93u8,47u8,142u8,215u8,38u8,136u8, - 216u8,170u8,81u8,205u8,6u8,201u8,183u8,42u8,47u8,81u8,0u8,22u8,43u8,74u8,34u8,196u8,53u8,214u8,44u8,88u8, - 156u8,14u8,251u8,82u8,151u8,123u8,96u8,5u8,76u8,4u8,89u8,92u8,79u8,142u8,65u8,38u8,86u8,159u8,39u8,213u8, - 69u8,57u8,27u8,246u8,153u8,126u8,246u8,160u8,185u8,120u8,7u8,2u8,2u8,28u8,30u8,237u8,103u8,15u8,142u8,188u8, - 123u8,83u8,206u8,177u8,8u8,195u8,8u8,9u8,206u8,126u8,218u8,120u8,116u8,124u8,112u8,216u8,11u8,19u8,199u8,121u8, - 114u8,102u8,189u8,98u8,245u8,109u8,103u8,206u8,174u8,248u8,231u8,138u8,127u8,174u8,248u8,167u8,143u8,127u8,126u8,173u8, - 12u8,212u8,76u8,136u8,215u8,21u8,118u8,190u8,36u8,22u8,26u8,97u8,174u8,176u8,97u8,194u8,106u8,173u8,227u8,70u8, - 124u8,160u8,234u8,33u8,134u8,41u8,200u8,253u8,196u8,97u8,113u8,164u8,202u8,173u8,135u8,142u8,214u8,157u8,205u8,96u8, - 51u8,55u8,107u8,205u8,225u8,179u8,243u8,69u8,216u8,90u8,33u8,159u8,205u8,209u8,157u8,159u8,58u8,226u8,242u8,54u8, - 162u8,75u8,141u8,19u8,72u8,136u8,48u8,106u8,17u8,203u8,23u8,119u8,85u8,48u8,45u8,107u8,112u8,176u8,244u8,155u8, - 162u8,160u8,136u8,214u8,177u8,231u8,2u8,211u8,237u8,232u8,88u8,114u8,50u8,25u8,105u8,126u8,184u8,150u8,5u8,247u8, - 116u8,213u8,8u8,100u8,129u8,71u8,105u8,170u8,182u8,201u8,125u8,46u8,51u8,169u8,168u8,70u8,225u8,177u8,145u8,110u8, - 80u8,125u8,14u8,106u8,70u8,194u8,137u8,76u8,80u8,178u8,19u8,163u8,155u8,57u8,135u8,78u8,104u8,36u8,26u8,76u8, - 72u8,38u8,124u8,123u8,206u8,154u8,76u8,83u8,184u8,239u8,45u8,153u8,100u8,29u8,246u8,184u8,108u8,52u8,158u8,252u8, - 69u8,86u8,25u8,133u8,210u8,204u8,227u8,87u8,150u8,233u8,247u8,168u8,156u8,136u8,231u8,100u8,3u8,6u8,112u8,21u8, - 219u8,230u8,148u8,46u8,65u8,117u8,49u8,35u8,122u8,88u8,219u8,145u8,183u8,116u8,105u8,232u8,250u8,14u8,76u8,231u8, - 143u8,185u8,136u8,134u8,44u8,6u8,180u8,234u8,58u8,122u8,169u8,165u8,141u8,185u8,107u8,170u8,83u8,162u8,0u8,200u8, - 247u8,118u8,248u8,191u8,183u8,27u8,93u8,196u8,116u8,40u8,176u8,106u8,241u8,9u8,209u8,32u8,17u8,83u8,242u8,200u8, - 47u8,114u8,50u8,213u8,46u8,220u8,157u8,208u8,173u8,173u8,73u8,207u8,33u8,142u8,91u8,192u8,170u8,227u8,78u8,49u8, - 247u8,130u8,43u8,71u8,5u8,60u8,57u8,74u8,89u8,33u8,188u8,247u8,9u8,202u8,108u8,17u8,212u8,153u8,233u8,196u8, - 164u8,151u8,200u8,227u8,241u8,73u8,69u8,115u8,103u8,4u8,170u8,107u8,53u8,172u8,182u8,244u8,148u8,42u8,21u8,73u8, - 253u8,203u8,155u8,53u8,191u8,247u8,148u8,27u8,8u8,119u8,211u8,232u8,68u8,61u8,27u8,236u8,223u8,232u8,60u8,238u8, - 107u8,228u8,72u8,212u8,51u8,243u8,212u8,85u8,33u8,143u8,71u8,14u8,111u8,158u8,145u8,102u8,25u8,107u8,166u8,115u8, - 212u8,119u8,88u8,77u8,231u8,20u8,113u8,60u8,55u8,233u8,106u8,217u8,246u8,64u8,86u8,38u8,74u8,74u8,151u8,3u8, - 84u8,69u8,70u8,23u8,147u8,6u8,217u8,116u8,93u8,191u8,0u8,164u8,157u8,40u8,188u8,189u8,209u8,193u8,48u8,234u8, - 52u8,153u8,128u8,17u8,130u8,3u8,60u8,183u8,6u8,168u8,235u8,88u8,179u8,80u8,69u8,33u8,229u8,130u8,161u8,124u8, - 170u8,43u8,209u8,138u8,21u8,214u8,210u8,175u8,117u8,97u8,88u8,172u8,118u8,88u8,42u8,69u8,135u8,81u8,69u8,195u8, - 69u8,204u8,43u8,54u8,3u8,175u8,39u8,105u8,27u8,245u8,230u8,144u8,246u8,108u8,20u8,115u8,121u8,209u8,125u8,213u8, - 221u8,223u8,233u8,89u8,45u8,95u8,154u8,142u8,218u8,123u8,204u8,166u8,182u8,60u8,79u8,163u8,5u8,210u8,76u8,209u8, - 220u8,9u8,199u8,111u8,179u8,136u8,116u8,195u8,211u8,182u8,138u8,249u8,237u8,21u8,75u8,235u8,255u8,210u8,108u8,188u8, - 136u8,239u8,255u8,130u8,199u8,108u8,104u8,143u8,16u8,150u8,103u8,244u8,130u8,42u8,247u8,104u8,93u8,145u8,68u8,138u8, - 75u8,206u8,252u8,198u8,38u8,148u8,72u8,165u8,225u8,117u8,221u8,86u8,82u8,163u8,130u8,53u8,107u8,65u8,67u8,69u8, - 181u8,221u8,52u8,116u8,196u8,134u8,70u8,198u8,135u8,27u8,237u8,102u8,171u8,120u8,147u8,6u8,240u8,94u8,86u8,236u8, - 20u8,97u8,231u8,34u8,98u8,242u8,188u8,64u8,130u8,162u8,75u8,99u8,163u8,141u8,230u8,9u8,44u8,253u8,168u8,125u8, - 175u8,22u8,205u8,97u8,254u8,236u8,47u8,60u8,28u8,232u8,74u8,247u8,117u8,94u8,194u8,21u8,175u8,8u8,195u8,103u8, - 6u8,39u8,113u8,177u8,231u8,13u8,170u8,170u8,13u8,43u8,115u8,40u8,206u8,43u8,96u8,249u8,140u8,128u8,69u8,94u8, - 126u8,64u8,1u8,54u8,237u8,32u8,188u8,190u8,112u8,253u8,177u8,116u8,18u8,123u8,97u8,11u8,181u8,212u8,194u8,226u8, - 63u8,109u8,239u8,165u8,5u8,186u8,192u8,210u8,84u8,163u8,160u8,68u8,134u8,33u8,25u8,250u8,84u8,131u8,216u8,228u8, - 4u8,129u8,5u8,193u8,159u8,14u8,9u8,156u8,19u8,25u8,150u8,135u8,9u8,159u8,8u8,13u8,62u8,187u8,200u8,93u8, - 186u8,216u8,146u8,11u8,76u8,5u8,130u8,27u8,208u8,118u8,149u8,171u8,211u8,106u8,244u8,73u8,90u8,41u8,208u8,20u8, - 58u8,193u8,68u8,38u8,17u8,174u8,145u8,80u8,227u8,26u8,57u8,101u8,139u8,199u8,112u8,146u8,5u8,94u8,162u8,50u8, - 139u8,161u8,5u8,170u8,26u8,180u8,226u8,251u8,122u8,81u8,9u8,218u8,119u8,211u8,250u8,21u8,35u8,88u8,112u8,253u8, - 97u8,82u8,98u8,172u8,31u8,68u8,153u8,37u8,175u8,157u8,75u8,184u8,139u8,173u8,219u8,146u8,171u8,124u8,107u8,70u8, - 108u8,117u8,201u8,107u8,55u8,210u8,237u8,22u8,219u8,131u8,135u8,231u8,123u8,246u8,194u8,240u8,227u8,91u8,210u8,213u8, - 185u8,236u8,141u8,217u8,250u8,246u8,162u8,251u8,179u8,212u8,99u8,185u8,189u8,191u8,255u8,231u8,93u8,145u8,95u8,253u8, - 78u8,154u8,109u8,81u8,181u8,4u8,139u8,75u8,52u8,165u8,137u8,57u8,156u8,97u8,46u8,63u8,154u8,145u8,57u8,166u8, - 220u8,98u8,129u8,216u8,224u8,187u8,160u8,96u8,45u8,187u8,98u8,87u8,136u8,35u8,52u8,102u8,161u8,117u8,230u8,41u8, - 29u8,18u8,100u8,12u8,141u8,83u8,205u8,203u8,31u8,16u8,80u8,133u8,169u8,129u8,5u8,125u8,141u8,152u8,67u8,131u8, - 31u8,73u8,23u8,252u8,160u8,116u8,115u8,226u8,128u8,150u8,77u8,83u8,232u8,229u8,236u8,203u8,160u8,213u8,11u8,236u8, - 167u8,38u8,216u8,141u8,219u8,112u8,57u8,185u8,216u8,202u8,114u8,54u8,226u8,48u8,252u8,5u8,54u8,67u8,114u8,125u8, - 249u8,15u8,152u8,201u8,139u8,119u8,44u8,228u8,18u8,150u8,33u8,176u8,15u8,127u8,47u8,250u8,190u8,85u8,83u8,224u8, - 187u8,72u8,6u8,106u8,225u8,18u8,174u8,90u8,136u8,109u8,91u8,98u8,105u8,67u8,53u8,18u8,214u8,23u8,39u8,178u8, - 32u8,73u8,74u8,23u8,36u8,73u8,137u8,172u8,132u8,212u8,147u8,172u8,146u8,82u8,133u8,55u8,211u8,64u8,34u8,74u8, - 218u8,92u8,63u8,36u8,109u8,172u8,255u8,129u8,158u8,248u8,96u8,72u8,230u8,70u8,24u8,15u8,62u8,100u8,139u8,158u8, - 180u8,156u8,250u8,186u8,102u8,161u8,81u8,251u8,204u8,99u8,203u8,144u8,198u8,29u8,8u8,117u8,162u8,158u8,195u8,247u8, - 93u8,106u8,243u8,161u8,144u8,20u8,30u8,157u8,76u8,92u8,105u8,115u8,186u8,201u8,222u8,237u8,84u8,6u8,7u8,164u8, - 217u8,155u8,38u8,253u8,146u8,135u8,109u8,77u8,69u8,37u8,163u8,10u8,44u8,238u8,60u8,50u8,89u8,247u8,80u8,145u8, - 253u8,102u8,171u8,245u8,197u8,154u8,22u8,233u8,94u8,0u8,172u8,87u8,145u8,229u8,68u8,122u8,205u8,103u8,156u8,77u8, - 68u8,95u8,110u8,213u8,193u8,241u8,34u8,171u8,224u8,195u8,193u8,160u8,56u8,43u8,242u8,145u8,118u8,42u8,160u8,37u8, - 173u8,135u8,124u8,92u8,118u8,101u8,242u8,54u8,171u8,116u8,52u8,95u8,251u8,2u8,228u8,241u8,138u8,170u8,150u8,230u8, - 237u8,88u8,216u8,84u8,229u8,203u8,31u8,201u8,220u8,84u8,164u8,146u8,12u8,123u8,113u8,186u8,209u8,70u8,215u8,170u8, - 181u8,58u8,191u8,185u8,161u8,148u8,56u8,210u8,197u8,234u8,40u8,103u8,110u8,210u8,24u8,137u8,183u8,16u8,83u8,165u8, - 225u8,61u8,101u8,90u8,76u8,191u8,81u8,115u8,135u8,93u8,179u8,128u8,179u8,237u8,70u8,138u8,170u8,217u8,134u8,66u8, - 117u8,154u8,110u8,104u8,95u8,181u8,238u8,218u8,219u8,79u8,209u8,109u8,209u8,101u8,216u8,88u8,255u8,102u8,217u8,158u8, - 84u8,53u8,14u8,227u8,81u8,14u8,53u8,158u8,36u8,197u8,72u8,167u8,1u8,136u8,165u8,178u8,80u8,201u8,11u8,112u8, - 30u8,215u8,47u8,110u8,172u8,27u8,103u8,163u8,190u8,144u8,135u8,227u8,198u8,40u8,187u8,129u8,71u8,102u8,172u8,211u8, - 125u8,123u8,77u8,182u8,111u8,245u8,126u8,61u8,133u8,254u8,213u8,237u8,14u8,198u8,126u8,234u8,190u8,112u8,29u8,141u8, - 82u8,145u8,113u8,163u8,40u8,246u8,147u8,116u8,31u8,202u8,125u8,77u8,114u8,86u8,220u8,29u8,239u8,137u8,118u8,241u8, - 49u8,130u8,218u8,208u8,38u8,218u8,50u8,149u8,89u8,158u8,246u8,152u8,166u8,77u8,225u8,106u8,158u8,49u8,69u8,60u8, - 201u8,218u8,157u8,115u8,118u8,232u8,140u8,196u8,11u8,163u8,243u8,180u8,15u8,45u8,124u8,152u8,160u8,61u8,172u8,157u8, - 57u8,112u8,192u8,166u8,76u8,34u8,30u8,162u8,17u8,78u8,253u8,222u8,92u8,123u8,189u8,233u8,136u8,85u8,80u8,154u8, - 2u8,10u8,101u8,147u8,126u8,10u8,39u8,149u8,79u8,222u8,229u8,125u8,125u8,213u8,173u8,53u8,118u8,65u8,8u8,255u8, - 76u8,192u8,147u8,224u8,97u8,132u8,91u8,240u8,37u8,134u8,90u8,133u8,207u8,215u8,123u8,24u8,48u8,185u8,24u8,51u8, - 38u8,213u8,216u8,126u8,216u8,109u8,173u8,187u8,140u8,118u8,186u8,70u8,46u8,193u8,250u8,114u8,123u8,8u8,222u8,144u8, - 215u8,4u8,152u8,141u8,217u8,189u8,52u8,204u8,107u8,204u8,158u8,168u8,38u8,181u8,198u8,227u8,200u8,102u8,149u8,117u8, - 7u8,77u8,37u8,19u8,25u8,71u8,243u8,129u8,102u8,171u8,119u8,130u8,183u8,243u8,224u8,216u8,168u8,184u8,84u8,181u8, - 73u8,95u8,116u8,170u8,46u8,190u8,23u8,122u8,217u8,236u8,176u8,231u8,37u8,203u8,162u8,69u8,147u8,132u8,242u8,84u8, - 117u8,246u8,243u8,20u8,217u8,9u8,46u8,214u8,228u8,36u8,162u8,143u8,170u8,53u8,50u8,177u8,38u8,71u8,138u8,142u8, - 173u8,28u8,20u8,81u8,169u8,232u8,78u8,21u8,8u8,89u8,200u8,172u8,228u8,173u8,87u8,244u8,9u8,205u8,75u8,238u8, - 93u8,249u8,11u8,202u8,155u8,45u8,215u8,105u8,127u8,210u8,237u8,57u8,142u8,2u8,29u8,106u8,88u8,12u8,26u8,30u8, - 180u8,41u8,191u8,238u8,228u8,152u8,21u8,67u8,165u8,51u8,236u8,62u8,149u8,159u8,234u8,51u8,203u8,200u8,115u8,225u8, - 166u8,129u8,251u8,11u8,233u8,108u8,205u8,148u8,173u8,108u8,59u8,194u8,144u8,211u8,43u8,85u8,143u8,65u8,38u8,165u8, - 88u8,98u8,3u8,123u8,174u8,96u8,193u8,139u8,168u8,247u8,213u8,69u8,1u8,214u8,175u8,22u8,124u8,252u8,19u8,27u8, - 130u8,138u8,70u8,132u8,65u8,208u8,200u8,210u8,146u8,42u8,4u8,139u8,130u8,136u8,213u8,19u8,83u8,179u8,218u8,220u8, - 102u8,130u8,12u8,188u8,97u8,61u8,205u8,24u8,129u8,124u8,26u8,52u8,215u8,39u8,109u8,34u8,195u8,139u8,13u8,56u8, - 158u8,228u8,131u8,226u8,79u8,52u8,96u8,53u8,59u8,77u8,197u8,239u8,173u8,181u8,251u8,48u8,108u8,135u8,201u8,1u8, - 79u8,172u8,193u8,241u8,157u8,202u8,17u8,254u8,86u8,15u8,49u8,155u8,14u8,126u8,104u8,157u8,222u8,19u8,87u8,117u8, - 47u8,152u8,85u8,170u8,43u8,140u8,28u8,246u8,142u8,122u8,135u8,191u8,244u8,118u8,211u8,23u8,191u8,1u8,183u8,235u8, - 238u8,239u8,118u8,15u8,157u8,96u8,197u8,15u8,214u8,14u8,89u8,87u8,63u8,179u8,122u8,241u8,7u8,75u8,252u8,74u8, - 238u8,59u8,63u8,201u8,113u8,239u8,232u8,248u8,193u8,193u8,254u8,171u8,223u8,146u8,151u8,39u8,251u8,2u8,39u8,172u8, - 199u8,148u8,167u8,99u8,10u8,70u8,168u8,150u8,206u8,156u8,77u8,254u8,207u8,198u8,159u8,155u8,29u8,217u8,51u8,150u8, - 255u8,181u8,213u8,254u8,189u8,169u8,239u8,171u8,153u8,194u8,238u8,122u8,53u8,236u8,76u8,106u8,163u8,215u8,234u8,2u8, - 185u8,208u8,170u8,115u8,146u8,92u8,135u8,18u8,53u8,6u8,128u8,157u8,124u8,55u8,4u8,177u8,90u8,83u8,212u8,10u8, - 19u8,155u8,69u8,198u8,145u8,249u8,55u8,79u8,27u8,242u8,52u8,17u8,119u8,0u8,164u8,180u8,79u8,134u8,139u8,111u8, - 100u8,100u8,248u8,230u8,60u8,127u8,154u8,232u8,252u8,159u8,223u8,201u8,111u8,145u8,174u8,229u8,121u8,34u8,252u8,62u8, - 175u8,85u8,243u8,31u8,94u8,125u8,169u8,147u8,224u8,127u8,230u8,124u8,220u8,251u8,50u8,105u8,180u8,113u8,91u8,34u8, - 235u8,243u8,170u8,243u8,53u8,147u8,77u8,220u8,23u8,217u8,0u8,194u8,53u8,28u8,190u8,109u8,82u8,77u8,10u8,228u8, - 119u8,118u8,12u8,152u8,223u8,121u8,17u8,13u8,244u8,43u8,112u8,191u8,9u8,184u8,111u8,153u8,127u8,126u8,23u8,252u8, - 243u8,11u8,6u8,247u8,116u8,99u8,126u8,128u8,175u8,95u8,222u8,92u8,42u8,182u8,164u8,27u8,254u8,231u8,172u8,81u8, - 233u8,229u8,108u8,45u8,119u8,57u8,91u8,205u8,232u8,187u8,41u8,209u8,245u8,239u8,255u8,201u8,255u8,28u8,195u8,197u8, - 128u8,232u8,51u8,200u8,138u8,33u8,24u8,203u8,13u8,52u8,6u8,229u8,105u8,114u8,45u8,58u8,213u8,51u8,159u8,147u8, - 191u8,132u8,171u8,66u8,214u8,185u8,240u8,116u8,86u8,177u8,4u8,151u8,62u8,200u8,55u8,103u8,213u8,211u8,165u8,99u8, - 239u8,173u8,226u8,221u8,214u8,215u8,193u8,102u8,240u8,89u8,42u8,223u8,30u8,147u8,66u8,181u8,108u8,170u8,215u8,104u8, - 75u8,121u8,217u8,116u8,154u8,157u8,189u8,101u8,82u8,158u8,245u8,197u8,104u8,118u8,9u8,55u8,150u8,194u8,221u8,223u8, - 107u8,39u8,191u8,211u8,83u8,128u8,89u8,9u8,77u8,129u8,15u8,42u8,1u8,80u8,217u8,222u8,158u8,150u8,233u8,233u8, - 53u8,192u8,130u8,168u8,37u8,186u8,182u8,185u8,1u8,211u8,80u8,95u8,124u8,231u8,157u8,129u8,91u8,229u8,154u8,119u8, - 1u8,195u8,16u8,91u8,224u8,159u8,218u8,35u8,155u8,101u8,216u8,234u8,161u8,197u8,185u8,39u8,141u8,7u8,47u8,255u8, - 193u8,195u8,26u8,185u8,216u8,194u8,41u8,25u8,3u8,170u8,115u8,166u8,18u8,218u8,226u8,57u8,123u8,233u8,94u8,59u8, - 248u8,204u8,207u8,249u8,112u8,88u8,74u8,164u8,182u8,31u8,221u8,220u8,216u8,8u8,162u8,138u8,53u8,212u8,197u8,116u8, - 58u8,174u8,182u8,31u8,62u8,204u8,198u8,211u8,178u8,2u8,131u8,220u8,59u8,103u8,234u8,0u8,182u8,55u8,204u8,188u8, - 225u8,58u8,89u8,81u8,161u8,60u8,210u8,113u8,237u8,246u8,15u8,192u8,48u8,73u8,127u8,99u8,245u8,12u8,104u8,38u8, - 190u8,47u8,187u8,134u8,236u8,212u8,125u8,217u8,246u8,16u8,93u8,171u8,88u8,144u8,32u8,127u8,115u8,138u8,79u8,157u8, - 149u8,252u8,20u8,79u8,199u8,183u8,190u8,112u8,58u8,78u8,120u8,180u8,253u8,119u8,128u8,83u8,57u8,69u8,127u8,163u8, - 77u8,162u8,138u8,140u8,174u8,110u8,91u8,75u8,44u8,107u8,86u8,38u8,168u8,71u8,10u8,35u8,132u8,158u8,224u8,139u8, - 65u8,201u8,138u8,194u8,186u8,180u8,28u8,13u8,175u8,127u8,111u8,8u8,34u8,98u8,166u8,200u8,86u8,157u8,130u8,67u8, - 72u8,39u8,130u8,98u8,213u8,235u8,160u8,137u8,225u8,175u8,229u8,100u8,216u8,191u8,215u8,158u8,103u8,21u8,117u8,35u8, - 198u8,27u8,45u8,64u8,146u8,225u8,152u8,153u8,107u8,210u8,18u8,133u8,70u8,254u8,110u8,232u8,78u8,80u8,8u8,127u8, - 220u8,16u8,15u8,233u8,206u8,135u8,206u8,231u8,31u8,191u8,187u8,33u8,189u8,224u8,64u8,3u8,118u8,79u8,251u8,198u8, - 192u8,179u8,139u8,213u8,56u8,245u8,94u8,56u8,45u8,40u8,147u8,82u8,135u8,119u8,75u8,8u8,115u8,157u8,187u8,55u8, - 9u8,39u8,73u8,55u8,73u8,172u8,9u8,114u8,246u8,29u8,84u8,208u8,198u8,196u8,145u8,155u8,9u8,4u8,38u8,148u8, - 133u8,88u8,188u8,87u8,131u8,180u8,68u8,89u8,102u8,19u8,53u8,141u8,184u8,10u8,84u8,90u8,107u8,6u8,120u8,182u8, - 85u8,246u8,184u8,77u8,217u8,23u8,17u8,126u8,127u8,215u8,62u8,49u8,19u8,7u8,236u8,74u8,52u8,150u8,80u8,76u8, - 44u8,22u8,225u8,0u8,90u8,176u8,64u8,20u8,103u8,201u8,152u8,93u8,205u8,41u8,76u8,83u8,43u8,22u8,179u8,248u8, - 214u8,172u8,196u8,236u8,168u8,19u8,22u8,158u8,242u8,200u8,35u8,14u8,72u8,230u8,212u8,50u8,197u8,66u8,62u8,52u8, - 32u8,15u8,133u8,222u8,115u8,136u8,227u8,139u8,34u8,142u8,205u8,11u8,22u8,150u8,189u8,169u8,234u8,107u8,154u8,238u8, - 46u8,93u8,12u8,255u8,28u8,164u8,112u8,231u8,222u8,100u8,65u8,174u8,208u8,118u8,60u8,87u8,230u8,94u8,19u8,192u8, - 105u8,163u8,168u8,175u8,100u8,251u8,218u8,134u8,34u8,86u8,35u8,66u8,14u8,210u8,243u8,124u8,196u8,10u8,4u8,161u8, - 94u8,20u8,11u8,89u8,82u8,150u8,37u8,177u8,47u8,215u8,22u8,255u8,21u8,26u8,73u8,2u8,221u8,102u8,77u8,231u8, - 104u8,172u8,10u8,165u8,220u8,187u8,34u8,206u8,133u8,223u8,42u8,151u8,181u8,221u8,22u8,132u8,56u8,104u8,129u8,3u8, - 23u8,39u8,165u8,155u8,181u8,12u8,29u8,0u8,211u8,174u8,3u8,166u8,168u8,96u8,37u8,75u8,52u8,69u8,125u8,189u8, - 109u8,161u8,240u8,214u8,1u8,21u8,29u8,203u8,252u8,128u8,26u8,163u8,4u8,62u8,14u8,254u8,249u8,69u8,194u8,105u8, - 84u8,51u8,227u8,197u8,24u8,192u8,2u8,12u8,143u8,182u8,240u8,243u8,133u8,81u8,13u8,9u8,188u8,247u8,26u8,40u8, - 185u8,185u8,25u8,14u8,206u8,10u8,43u8,196u8,53u8,107u8,48u8,52u8,226u8,71u8,145u8,184u8,101u8,88u8,123u8,94u8, - 188u8,112u8,80u8,77u8,112u8,68u8,43u8,210u8,116u8,54u8,102u8,43u8,187u8,25u8,182u8,45u8,207u8,192u8,78u8,225u8, - 32u8,248u8,209u8,133u8,30u8,47u8,118u8,39u8,182u8,65u8,84u8,209u8,186u8,177u8,37u8,103u8,233u8,56u8,108u8,8u8, - 63u8,116u8,79u8,147u8,118u8,51u8,158u8,155u8,66u8,50u8,71u8,201u8,214u8,218u8,116u8,50u8,203u8,219u8,191u8,199u8, - 207u8,126u8,122u8,143u8,189u8,120u8,175u8,125u8,119u8,108u8,67u8,225u8,210u8,89u8,166u8,73u8,6u8,64u8,128u8,217u8, - 24u8,146u8,211u8,60u8,225u8,25u8,93u8,172u8,99u8,117u8,13u8,6u8,160u8,6u8,77u8,138u8,211u8,25u8,139u8,66u8, - 7u8,41u8,173u8,24u8,193u8,191u8,87u8,23u8,186u8,147u8,53u8,72u8,220u8,102u8,133u8,0u8,5u8,34u8,163u8,252u8, - 202u8,227u8,100u8,8u8,73u8,176u8,81u8,58u8,151u8,251u8,37u8,204u8,165u8,151u8,121u8,175u8,14u8,7u8,183u8,140u8, - 251u8,236u8,49u8,191u8,83u8,194u8,88u8,22u8,169u8,83u8,121u8,84u8,42u8,255u8,231u8,161u8,133u8,120u8,124u8,23u8, - 161u8,195u8,9u8,104u8,75u8,244u8,167u8,120u8,254u8,111u8,44u8,209u8,221u8,14u8,172u8,142u8,38u8,204u8,55u8,171u8, - 132u8,120u8,199u8,170u8,38u8,18u8,52u8,72u8,1u8,174u8,251u8,169u8,171u8,182u8,232u8,139u8,156u8,199u8,113u8,208u8, - 200u8,74u8,236u8,10u8,128u8,169u8,104u8,240u8,144u8,178u8,72u8,57u8,25u8,177u8,94u8,149u8,179u8,9u8,48u8,58u8, - 174u8,183u8,175u8,184u8,203u8,87u8,44u8,33u8,50u8,78u8,245u8,245u8,114u8,130u8,143u8,78u8,233u8,63u8,30u8,69u8, - 247u8,205u8,52u8,190u8,132u8,41u8,156u8,26u8,161u8,113u8,226u8,174u8,11u8,28u8,102u8,58u8,140u8,42u8,200u8,62u8, - 190u8,228u8,146u8,241u8,150u8,235u8,242u8,225u8,251u8,68u8,114u8,189u8,224u8,37u8,229u8,40u8,143u8,198u8,201u8,104u8, - 13u8,132u8,110u8,79u8,176u8,28u8,234u8,172u8,182u8,35u8,168u8,105u8,63u8,93u8,224u8,80u8,241u8,73u8,68u8,31u8, - 108u8,61u8,31u8,117u8,192u8,225u8,220u8,36u8,65u8,235u8,221u8,84u8,152u8,122u8,76u8,115u8,247u8,107u8,62u8,132u8, - 162u8,139u8,189u8,243u8,57u8,37u8,185u8,53u8,231u8,148u8,192u8,191u8,213u8,14u8,43u8,95u8,230u8,72u8,48u8,80u8, - 63u8,133u8,23u8,241u8,158u8,59u8,222u8,53u8,249u8,192u8,173u8,60u8,191u8,245u8,171u8,169u8,23u8,202u8,39u8,107u8, - 88u8,163u8,215u8,16u8,51u8,152u8,128u8,248u8,113u8,85u8,78u8,222u8,234u8,88u8,98u8,131u8,231u8,159u8,101u8,131u8, - 220u8,102u8,243u8,102u8,178u8,55u8,172u8,170u8,222u8,4u8,74u8,123u8,208u8,3u8,215u8,236u8,61u8,144u8,185u8,221u8, - 241u8,100u8,64u8,76u8,139u8,75u8,152u8,50u8,187u8,132u8,13u8,87u8,76u8,109u8,135u8,63u8,83u8,86u8,59u8,30u8, - 62u8,155u8,240u8,176u8,49u8,201u8,215u8,89u8,100u8,184u8,158u8,177u8,189u8,52u8,121u8,96u8,197u8,221u8,151u8,31u8, - 68u8,137u8,174u8,84u8,146u8,10u8,153u8,194u8,193u8,47u8,87u8,95u8,12u8,88u8,242u8,217u8,143u8,37u8,148u8,1u8, - 180u8,189u8,43u8,250u8,185u8,42u8,86u8,205u8,234u8,188u8,149u8,56u8,193u8,222u8,52u8,135u8,102u8,227u8,250u8,178u8, - 52u8,212u8,194u8,167u8,178u8,218u8,1u8,125u8,87u8,108u8,154u8,172u8,122u8,203u8,4u8,204u8,9u8,27u8,93u8,77u8, - 169u8,202u8,24u8,194u8,71u8,217u8,187u8,178u8,96u8,169u8,207u8,167u8,197u8,249u8,140u8,213u8,11u8,225u8,197u8,240u8, - 38u8,229u8,148u8,119u8,55u8,103u8,84u8,62u8,130u8,33u8,216u8,214u8,106u8,177u8,93u8,250u8,200u8,76u8,140u8,67u8, - 27u8,128u8,95u8,27u8,157u8,0u8,12u8,133u8,59u8,73u8,6u8,66u8,248u8,69u8,62u8,73u8,141u8,175u8,250u8,14u8, - 110u8,159u8,206u8,38u8,35u8,108u8,120u8,61u8,133u8,183u8,210u8,97u8,113u8,89u8,48u8,100u8,99u8,64u8,62u8,27u8, - 201u8,191u8,162u8,252u8,244u8,230u8,164u8,214u8,19u8,237u8,57u8,164u8,253u8,186u8,235u8,156u8,156u8,94u8,202u8,225u8, - 44u8,37u8,181u8,70u8,195u8,219u8,85u8,14u8,110u8,50u8,140u8,117u8,18u8,95u8,162u8,37u8,235u8,197u8,201u8,225u8, - 62u8,239u8,245u8,250u8,226u8,183u8,116u8,231u8,176u8,215u8,61u8,62u8,56u8,252u8,250u8,204u8,88u8,172u8,76u8,2u8, - 47u8,61u8,198u8,111u8,142u8,211u8,5u8,3u8,96u8,67u8,169u8,125u8,1u8,232u8,179u8,68u8,233u8,73u8,46u8,91u8, - 91u8,168u8,92u8,184u8,218u8,164u8,45u8,74u8,191u8,180u8,204u8,4u8,191u8,198u8,10u8,233u8,28u8,237u8,79u8,175u8, - 21u8,120u8,162u8,150u8,59u8,198u8,56u8,241u8,174u8,73u8,87u8,36u8,202u8,6u8,211u8,37u8,47u8,90u8,201u8,69u8, - 172u8,105u8,33u8,139u8,243u8,216u8,206u8,255u8,156u8,78u8,178u8,179u8,169u8,144u8,251u8,234u8,67u8,106u8,39u8,15u8, - 18u8,242u8,145u8,122u8,73u8,134u8,144u8,68u8,16u8,29u8,77u8,245u8,194u8,100u8,71u8,105u8,26u8,254u8,11u8,181u8, - 233u8,64u8,80u8,75u8,137u8,70u8,108u8,107u8,212u8,32u8,130u8,135u8,253u8,227u8,177u8,8u8,126u8,240u8,235u8,126u8, - 239u8,43u8,68u8,111u8,137u8,125u8,204u8,185u8,90u8,187u8,108u8,212u8,141u8,123u8,5u8,120u8,130u8,40u8,68u8,2u8, - 20u8,123u8,205u8,134u8,153u8,78u8,82u8,79u8,120u8,67u8,132u8,156u8,115u8,31u8,40u8,185u8,146u8,21u8,81u8,97u8, - 239u8,199u8,186u8,120u8,133u8,16u8,130u8,172u8,135u8,132u8,94u8,97u8,116u8,120u8,77u8,251u8,197u8,96u8,144u8,79u8, - 114u8,89u8,146u8,0u8,208u8,39u8,70u8,224u8,144u8,177u8,199u8,11u8,139u8,25u8,159u8,80u8,66u8,112u8,34u8,143u8, - 87u8,38u8,200u8,79u8,139u8,231u8,252u8,216u8,3u8,164u8,220u8,238u8,189u8,38u8,91u8,231u8,218u8,129u8,201u8,42u8, - 168u8,152u8,209u8,188u8,250u8,97u8,179u8,155u8,177u8,198u8,236u8,122u8,78u8,31u8,255u8,51u8,241u8,216u8,215u8,7u8, - 199u8,26u8,162u8,189u8,20u8,68u8,117u8,179u8,181u8,90u8,217u8,105u8,201u8,11u8,86u8,244u8,25u8,73u8,124u8,180u8, - 245u8,253u8,147u8,31u8,159u8,116u8,146u8,97u8,121u8,38u8,234u8,172u8,62u8,75u8,142u8,242u8,225u8,192u8,70u8,115u8, - 250u8,156u8,248u8,144u8,11u8,163u8,120u8,178u8,194u8,241u8,21u8,142u8,127u8,4u8,28u8,47u8,70u8,197u8,180u8,200u8, - 134u8,197u8,95u8,185u8,219u8,189u8,16u8,151u8,160u8,85u8,200u8,79u8,164u8,81u8,98u8,252u8,188u8,131u8,248u8,168u8, - 210u8,28u8,190u8,42u8,116u8,92u8,225u8,227u8,103u8,138u8,143u8,129u8,108u8,30u8,2u8,245u8,182u8,230u8,199u8,100u8, - 11u8,43u8,184u8,253u8,77u8,32u8,140u8,203u8,104u8,87u8,246u8,226u8,219u8,0u8,139u8,185u8,196u8,170u8,37u8,208u8, - 103u8,47u8,212u8,153u8,240u8,227u8,181u8,152u8,4u8,222u8,95u8,68u8,91u8,155u8,135u8,53u8,60u8,254u8,238u8,73u8, - 60u8,107u8,248u8,126u8,115u8,163u8,145u8,53u8,32u8,107u8,48u8,108u8,85u8,0u8,60u8,67u8,129u8,114u8,38u8,190u8, - 99u8,190u8,118u8,127u8,113u8,252u8,47u8,90u8,59u8,91u8,97u8,248u8,141u8,49u8,252u8,174u8,162u8,184u8,105u8,125u8, - 145u8,171u8,88u8,82u8,202u8,198u8,77u8,17u8,59u8,136u8,154u8,12u8,228u8,20u8,61u8,186u8,185u8,232u8,214u8,236u8, - 156u8,189u8,115u8,94u8,150u8,187u8,139u8,165u8,141u8,158u8,18u8,43u8,152u8,102u8,185u8,182u8,86u8,59u8,42u8,232u8, - 134u8,118u8,88u8,39u8,240u8,231u8,174u8,152u8,103u8,191u8,114u8,89u8,225u8,214u8,105u8,22u8,199u8,203u8,168u8,156u8, - 133u8,168u8,28u8,126u8,208u8,182u8,142u8,121u8,155u8,142u8,9u8,188u8,54u8,46u8,71u8,125u8,30u8,139u8,87u8,23u8, - 122u8,52u8,141u8,226u8,226u8,51u8,17u8,82u8,202u8,2u8,253u8,250u8,57u8,96u8,119u8,222u8,95u8,90u8,2u8,84u8, - 32u8,255u8,201u8,173u8,93u8,107u8,100u8,64u8,217u8,165u8,70u8,3u8,129u8,190u8,109u8,250u8,48u8,195u8,19u8,68u8, - 181u8,91u8,142u8,228u8,43u8,158u8,244u8,15u8,188u8,7u8,212u8,109u8,166u8,129u8,139u8,204u8,165u8,232u8,211u8,45u8, - 33u8,163u8,156u8,158u8,141u8,188u8,192u8,174u8,25u8,187u8,64u8,78u8,225u8,103u8,37u8,152u8,49u8,82u8,246u8,113u8, - 202u8,157u8,225u8,254u8,70u8,78u8,49u8,77u8,118u8,246u8,247u8,188u8,57u8,136u8,156u8,130u8,200u8,32u8,56u8,135u8, - 210u8,60u8,181u8,227u8,250u8,61u8,16u8,88u8,123u8,162u8,205u8,17u8,58u8,120u8,101u8,4u8,70u8,89u8,147u8,18u8, - 67u8,166u8,190u8,34u8,202u8,156u8,86u8,161u8,23u8,150u8,136u8,91u8,172u8,227u8,209u8,10u8,167u8,238u8,180u8,178u8, - 131u8,80u8,235u8,182u8,112u8,138u8,245u8,212u8,114u8,112u8,233u8,22u8,240u8,8u8,117u8,251u8,38u8,240u8,7u8,190u8, - 109u8,198u8,27u8,52u8,68u8,24u8,95u8,224u8,193u8,37u8,226u8,137u8,170u8,239u8,183u8,194u8,149u8,207u8,193u8,48u8, - 96u8,229u8,114u8,44u8,21u8,87u8,150u8,128u8,6u8,86u8,177u8,72u8,2u8,21u8,54u8,55u8,154u8,49u8,193u8,26u8, - 37u8,140u8,13u8,155u8,203u8,83u8,244u8,229u8,126u8,116u8,161u8,9u8,111u8,250u8,219u8,18u8,80u8,100u8,149u8,83u8, - 245u8,169u8,81u8,233u8,14u8,229u8,84u8,173u8,82u8,160u8,150u8,145u8,2u8,69u8,166u8,175u8,114u8,173u8,77u8,23u8, - 251u8,95u8,52u8,129u8,117u8,217u8,9u8,70u8,13u8,61u8,23u8,22u8,212u8,166u8,151u8,208u8,122u8,33u8,160u8,237u8, - 170u8,229u8,178u8,195u8,228u8,205u8,164u8,98u8,218u8,130u8,59u8,227u8,173u8,187u8,157u8,35u8,163u8,211u8,111u8,248u8, - 204u8,139u8,102u8,223u8,196u8,138u8,70u8,168u8,95u8,253u8,45u8,72u8,70u8,171u8,36u8,153u8,91u8,35u8,215u8,174u8, - 162u8,30u8,99u8,199u8,244u8,222u8,186u8,99u8,131u8,116u8,130u8,195u8,124u8,146u8,140u8,59u8,150u8,7u8,23u8,230u8, - 20u8,95u8,34u8,29u8,142u8,79u8,190u8,251u8,238u8,251u8,173u8,70u8,127u8,163u8,111u8,223u8,34u8,23u8,229u8,175u8, - 124u8,82u8,174u8,224u8,126u8,5u8,247u8,20u8,220u8,111u8,44u8,137u8,188u8,46u8,219u8,64u8,179u8,36u8,16u8,91u8, - 137u8,206u8,203u8,19u8,157u8,189u8,160u8,25u8,9u8,147u8,134u8,85u8,197u8,7u8,143u8,142u8,113u8,167u8,145u8,44u8, - 179u8,97u8,163u8,178u8,225u8,9u8,122u8,237u8,204u8,182u8,60u8,121u8,67u8,54u8,231u8,94u8,209u8,221u8,207u8,81u8, - 61u8,12u8,186u8,9u8,108u8,137u8,95u8,222u8,116u8,125u8,146u8,234u8,234u8,65u8,122u8,125u8,20u8,116u8,38u8,134u8, - 176u8,69u8,13u8,210u8,136u8,49u8,242u8,193u8,70u8,60u8,81u8,3u8,6u8,208u8,65u8,62u8,178u8,60u8,36u8,184u8, - 45u8,151u8,216u8,10u8,17u8,62u8,174u8,157u8,132u8,182u8,237u8,223u8,200u8,229u8,21u8,132u8,149u8,70u8,160u8,111u8, - 246u8,97u8,209u8,3u8,7u8,128u8,127u8,94u8,199u8,213u8,98u8,5u8,2u8,137u8,130u8,15u8,102u8,56u8,209u8,167u8, - 183u8,59u8,222u8,100u8,152u8,85u8,100u8,248u8,215u8,97u8,188u8,252u8,4u8,134u8,203u8,143u8,103u8,180u8,92u8,213u8, - 219u8,187u8,227u8,245u8,246u8,48u8,113u8,145u8,113u8,77u8,23u8,217u8,59u8,160u8,40u8,87u8,37u8,123u8,35u8,113u8, - 224u8,156u8,167u8,14u8,51u8,154u8,83u8,78u8,128u8,102u8,101u8,67u8,254u8,164u8,232u8,70u8,109u8,118u8,134u8,205u8, - 38u8,231u8,140u8,116u8,187u8,136u8,146u8,152u8,60u8,197u8,247u8,224u8,66u8,154u8,136u8,139u8,177u8,254u8,117u8,240u8, - 170u8,73u8,190u8,175u8,109u8,134u8,204u8,78u8,175u8,232u8,243u8,182u8,121u8,86u8,53u8,245u8,86u8,76u8,146u8,36u8, - 26u8,98u8,107u8,142u8,33u8,182u8,232u8,33u8,30u8,205u8,49u8,196u8,163u8,155u8,20u8,251u8,85u8,187u8,182u8,45u8, - 200u8,55u8,24u8,107u8,107u8,25u8,99u8,5u8,67u8,250u8,176u8,147u8,93u8,219u8,228u8,77u8,207u8,1u8,238u8,58u8, - 223u8,236u8,56u8,168u8,232u8,100u8,31u8,211u8,204u8,111u8,204u8,136u8,78u8,174u8,177u8,218u8,151u8,170u8,111u8,38u8, - 58u8,185u8,219u8,164u8,78u8,85u8,247u8,154u8,235u8,29u8,211u8,106u8,163u8,228u8,4u8,190u8,108u8,150u8,58u8,225u8, - 34u8,245u8,243u8,100u8,67u8,97u8,255u8,168u8,100u8,85u8,49u8,120u8,77u8,254u8,156u8,164u8,0u8,75u8,32u8,206u8, - 209u8,117u8,236u8,212u8,41u8,198u8,80u8,102u8,223u8,179u8,30u8,202u8,236u8,121u8,220u8,165u8,204u8,250u8,193u8,48u8, - 101u8,222u8,92u8,2u8,101u8,94u8,188u8,97u8,149u8,131u8,102u8,143u8,218u8,116u8,137u8,74u8,21u8,74u8,220u8,8u8, - 14u8,176u8,202u8,18u8,61u8,109u8,11u8,153u8,5u8,90u8,173u8,29u8,235u8,108u8,166u8,146u8,215u8,184u8,64u8,224u8, - 233u8,98u8,248u8,41u8,69u8,99u8,7u8,59u8,125u8,46u8,61u8,27u8,39u8,253u8,237u8,184u8,76u8,156u8,118u8,113u8, - 249u8,83u8,161u8,242u8,210u8,60u8,32u8,79u8,126u8,108u8,244u8,128u8,140u8,74u8,238u8,230u8,72u8,21u8,196u8,73u8, - 4u8,241u8,246u8,46u8,254u8,252u8,44u8,211u8,55u8,83u8,132u8,54u8,190u8,86u8,83u8,244u8,210u8,128u8,240u8,241u8, - 15u8,141u8,64u8,88u8,141u8,135u8,197u8,52u8,101u8,73u8,126u8,28u8,22u8,163u8,218u8,178u8,125u8,125u8,94u8,145u8, - 205u8,175u8,217u8,43u8,50u8,95u8,226u8,181u8,77u8,241u8,5u8,128u8,169u8,247u8,249u8,95u8,45u8,94u8,18u8,140u8, - 127u8,228u8,62u8,47u8,202u8,134u8,190u8,55u8,54u8,81u8,244u8,183u8,147u8,148u8,106u8,64u8,229u8,124u8,108u8,211u8, - 117u8,227u8,129u8,15u8,106u8,1u8,98u8,57u8,31u8,117u8,94u8,52u8,163u8,129u8,222u8,241u8,46u8,117u8,111u8,10u8, - 47u8,234u8,245u8,200u8,81u8,58u8,103u8,141u8,123u8,210u8,2u8,0u8,253u8,60u8,27u8,106u8,27u8,122u8,251u8,189u8, - 77u8,150u8,181u8,205u8,127u8,131u8,73u8,12u8,27u8,48u8,190u8,34u8,48u8,75u8,42u8,30u8,59u8,206u8,38u8,44u8, - 235u8,106u8,85u8,68u8,246u8,134u8,212u8,231u8,241u8,170u8,136u8,236u8,170u8,136u8,44u8,248u8,229u8,226u8,139u8,200u8, - 178u8,205u8,180u8,36u8,129u8,173u8,151u8,15u8,130u8,113u8,66u8,227u8,164u8,89u8,102u8,150u8,182u8,25u8,168u8,178u8, - 138u8,66u8,102u8,255u8,233u8,87u8,57u8,192u8,142u8,126u8,21u8,140u8,152u8,104u8,174u8,117u8,85u8,0u8,146u8,44u8, - 154u8,45u8,205u8,40u8,155u8,124u8,121u8,81u8,235u8,178u8,170u8,58u8,10u8,30u8,130u8,230u8,51u8,141u8,56u8,246u8, - 162u8,139u8,10u8,196u8,237u8,81u8,206u8,87u8,104u8,169u8,37u8,151u8,57u8,88u8,166u8,12u8,150u8,36u8,215u8,134u8, - 19u8,67u8,117u8,61u8,207u8,57u8,221u8,139u8,118u8,96u8,57u8,136u8,45u8,98u8,189u8,170u8,148u8,194u8,157u8,232u8, - 113u8,246u8,145u8,5u8,251u8,175u8,210u8,195u8,161u8,127u8,111u8,4u8,9u8,157u8,44u8,64u8,102u8,63u8,44u8,230u8, - 230u8,182u8,194u8,2u8,191u8,16u8,48u8,92u8,249u8,221u8,150u8,10u8,149u8,33u8,0u8,9u8,250u8,1u8,110u8,175u8, - 112u8,205u8,86u8,115u8,225u8,26u8,246u8,46u8,43u8,34u8,10u8,192u8,37u8,42u8,101u8,12u8,134u8,217u8,249u8,170u8, - 38u8,198u8,221u8,66u8,144u8,47u8,182u8,184u8,176u8,208u8,165u8,233u8,255u8,251u8,213u8,233u8,69u8,114u8,109u8,200u8, - 74u8,34u8,75u8,207u8,172u8,49u8,74u8,137u8,44u8,57u8,141u8,70u8,86u8,20u8,161u8,70u8,189u8,251u8,25u8,51u8, - 136u8,206u8,61u8,124u8,40u8,255u8,73u8,118u8,243u8,49u8,152u8,228u8,185u8,20u8,15u8,244u8,72u8,16u8,8u8,245u8, - 8u8,214u8,205u8,65u8,37u8,159u8,92u8,115u8,146u8,229u8,86u8,34u8,17u8,237u8,189u8,21u8,126u8,35u8,14u8,140u8, - 24u8,44u8,35u8,139u8,201u8,6u8,94u8,3u8,82u8,250u8,237u8,33u8,221u8,177u8,58u8,137u8,246u8,95u8,74u8,149u8, - 122u8,175u8,239u8,31u8,255u8,195u8,55u8,255u8,31u8,120u8,234u8,30u8,150u8,14u8,162u8,1u8,0u8,0u8,0u8,15u8, - 116u8,111u8,107u8,101u8,110u8,95u8,99u8,111u8,105u8,110u8,95u8,115u8,119u8,97u8,112u8,130u8,11u8,31u8,139u8,8u8, - 0u8,0u8,0u8,0u8,0u8,2u8,255u8,205u8,87u8,95u8,111u8,219u8,54u8,16u8,127u8,207u8,167u8,224u8,211u8,218u8, - 2u8,70u8,178u8,63u8,93u8,55u8,168u8,93u8,129u8,204u8,81u8,91u8,163u8,137u8,29u8,36u8,206u8,186u8,61u8,17u8, - 180u8,68u8,199u8,68u8,100u8,210u8,32u8,169u8,164u8,94u8,145u8,239u8,190u8,227u8,145u8,148u8,104u8,73u8,110u8,131u8, - 33u8,11u8,230u8,23u8,89u8,60u8,222u8,255u8,187u8,223u8,157u8,142u8,142u8,142u8,200u8,9u8,223u8,104u8,94u8,48u8, - 203u8,75u8,178u8,86u8,101u8,93u8,241u8,3u8,255u8,32u8,108u8,99u8,149u8,161u8,86u8,221u8,112u8,153u8,101u8,248u8, - 160u8,133u8,18u8,146u8,154u8,59u8,182u8,33u8,95u8,14u8,8u8,252u8,106u8,195u8,137u8,177u8,101u8,150u8,25u8,171u8, - 133u8,188u8,206u8,178u8,75u8,124u8,190u8,222u8,37u8,114u8,173u8,149u8,110u8,207u8,188u8,92u8,164u8,88u8,182u8,168u8, - 120u8,150u8,205u8,221u8,99u8,248u8,194u8,118u8,195u8,169u8,144u8,75u8,5u8,151u8,224u8,239u8,4u8,254u8,117u8,239u8, - 45u8,53u8,91u8,243u8,59u8,165u8,111u8,64u8,209u8,45u8,151u8,54u8,203u8,114u8,247u8,248u8,192u8,100u8,217u8,23u8, - 154u8,122u8,147u8,101u8,95u8,230u8,238u8,57u8,34u8,248u8,152u8,148u8,247u8,175u8,15u8,240u8,250u8,209u8,81u8,120u8, - 144u8,220u8,89u8,110u8,14u8,227u8,105u8,120u8,30u8,121u8,6u8,194u8,42u8,205u8,89u8,185u8,37u8,149u8,48u8,16u8, - 61u8,36u8,22u8,74u8,26u8,75u8,242u8,249u8,236u8,99u8,62u8,165u8,199u8,167u8,23u8,249u8,241u8,201u8,95u8,244u8, - 116u8,114u8,57u8,207u8,79u8,50u8,82u8,191u8,122u8,73u8,126u8,35u8,63u8,188u8,238u8,202u8,113u8,252u8,16u8,53u8, - 34u8,21u8,169u8,148u8,188u8,230u8,154u8,240u8,207u8,112u8,100u8,250u8,18u8,157u8,164u8,201u8,244u8,61u8,157u8,206u8, - 230u8,52u8,255u8,19u8,94u8,162u8,208u8,31u8,123u8,66u8,133u8,1u8,121u8,150u8,8u8,73u8,184u8,41u8,180u8,186u8, - 235u8,11u8,115u8,66u8,38u8,83u8,154u8,95u8,142u8,47u8,102u8,159u8,162u8,160u8,159u8,122u8,130u8,10u8,38u8,157u8, - 160u8,5u8,135u8,242u8,184u8,133u8,34u8,81u8,181u8,37u8,106u8,25u8,164u8,194u8,241u8,82u8,105u8,78u8,236u8,138u8, - 131u8,237u8,197u8,77u8,189u8,33u8,86u8,172u8,121u8,95u8,217u8,248u8,120u8,234u8,244u8,157u8,205u8,254u8,200u8,233u8, - 236u8,106u8,78u8,103u8,239u8,130u8,98u8,250u8,123u8,254u8,110u8,118u8,145u8,211u8,211u8,217u8,248u8,227u8,213u8,57u8, - 157u8,79u8,206u8,242u8,104u8,203u8,203u8,158u8,45u8,139u8,122u8,75u8,54u8,90u8,20u8,156u8,148u8,138u8,27u8,249u8, - 204u8,146u8,53u8,179u8,197u8,170u8,137u8,32u8,210u8,250u8,202u8,207u8,192u8,205u8,243u8,139u8,201u8,56u8,71u8,159u8, - 207u8,142u8,231u8,227u8,15u8,81u8,199u8,207u8,131u8,58u8,216u8,90u8,213u8,210u8,238u8,81u8,226u8,137u8,3u8,233u8, - 62u8,155u8,93u8,77u8,231u8,125u8,21u8,175u8,18u8,21u8,83u8,136u8,36u8,151u8,170u8,190u8,94u8,17u8,215u8,75u8, - 196u8,42u8,212u8,135u8,245u8,152u8,74u8,196u8,4u8,79u8,103u8,87u8,239u8,63u8,208u8,241u8,108u8,50u8,141u8,162u8, - 126u8,73u8,68u8,245u8,251u8,54u8,225u8,63u8,201u8,207u8,47u8,242u8,241u8,49u8,148u8,29u8,132u8,252u8,228u8,234u8, - 180u8,137u8,233u8,175u8,93u8,127u8,199u8,96u8,198u8,165u8,235u8,104u8,16u8,166u8,116u8,105u8,8u8,35u8,216u8,224u8, - 204u8,220u8,16u8,72u8,45u8,190u8,108u8,156u8,219u8,30u8,1u8,66u8,100u8,238u8,132u8,93u8,17u8,199u8,234u8,186u8, - 210u8,191u8,49u8,178u8,22u8,82u8,172u8,89u8,21u8,50u8,180u8,129u8,58u8,110u8,253u8,2u8,128u8,168u8,11u8,187u8, - 171u8,242u8,205u8,102u8,197u8,164u8,85u8,235u8,70u8,208u8,91u8,178u8,98u8,6u8,174u8,66u8,69u8,141u8,72u8,169u8, - 85u8,132u8,25u8,247u8,75u8,213u8,163u8,51u8,163u8,134u8,4u8,122u8,41u8,234u8,164u8,160u8,51u8,180u8,120u8,123u8, - 227u8,62u8,113u8,216u8,85u8,105u8,200u8,34u8,148u8,48u8,171u8,42u8,47u8,214u8,236u8,122u8,138u8,6u8,148u8,132u8, - 89u8,79u8,37u8,234u8,78u8,114u8,253u8,12u8,34u8,83u8,20u8,77u8,230u8,83u8,127u8,78u8,189u8,72u8,179u8,199u8, - 159u8,27u8,190u8,77u8,252u8,0u8,51u8,220u8,1u8,180u8,167u8,235u8,25u8,175u8,64u8,148u8,187u8,6u8,0u8,116u8, - 145u8,91u8,86u8,213u8,60u8,94u8,3u8,15u8,67u8,84u8,193u8,108u8,203u8,244u8,53u8,183u8,161u8,126u8,64u8,203u8, - 97u8,35u8,59u8,248u8,102u8,50u8,130u8,136u8,250u8,38u8,224u8,218u8,168u8,19u8,246u8,198u8,188u8,183u8,163u8,46u8, - 43u8,69u8,8u8,5u8,1u8,9u8,134u8,190u8,73u8,157u8,196u8,243u8,132u8,207u8,217u8,188u8,159u8,201u8,169u8,75u8, - 57u8,238u8,59u8,197u8,151u8,123u8,16u8,89u8,169u8,170u8,76u8,226u8,225u8,254u8,66u8,240u8,91u8,224u8,113u8,245u8, - 85u8,106u8,118u8,7u8,169u8,128u8,154u8,210u8,76u8,154u8,37u8,204u8,148u8,0u8,185u8,105u8,38u8,162u8,188u8,88u8, - 69u8,221u8,250u8,201u8,252u8,181u8,81u8,154u8,13u8,200u8,168u8,168u8,26u8,0u8,131u8,188u8,59u8,0u8,35u8,186u8, - 6u8,51u8,0u8,234u8,70u8,72u8,193u8,10u8,136u8,6u8,57u8,24u8,76u8,146u8,23u8,240u8,208u8,29u8,36u8,72u8, - 27u8,132u8,187u8,211u8,146u8,47u8,89u8,93u8,217u8,54u8,163u8,223u8,147u8,53u8,103u8,18u8,123u8,42u8,149u8,221u8, - 10u8,198u8,212u8,58u8,185u8,76u8,110u8,27u8,56u8,197u8,44u8,161u8,133u8,20u8,77u8,166u8,134u8,23u8,102u8,79u8, - 157u8,99u8,236u8,93u8,0u8,118u8,2u8,12u8,29u8,10u8,141u8,237u8,108u8,141u8,85u8,7u8,240u8,3u8,250u8,68u8, - 104u8,212u8,188u8,53u8,63u8,13u8,234u8,142u8,160u8,94u8,61u8,251u8,190u8,244u8,158u8,239u8,41u8,60u8,207u8,187u8, - 91u8,2u8,3u8,253u8,131u8,133u8,130u8,26u8,92u8,235u8,143u8,134u8,51u8,72u8,69u8,153u8,197u8,121u8,221u8,166u8, - 241u8,235u8,176u8,208u8,57u8,255u8,90u8,16u8,61u8,132u8,2u8,99u8,187u8,120u8,144u8,184u8,120u8,236u8,181u8,191u8, - 169u8,242u8,127u8,103u8,188u8,39u8,193u8,24u8,224u8,58u8,35u8,172u8,44u8,53u8,55u8,102u8,244u8,16u8,220u8,67u8, - 67u8,247u8,83u8,190u8,229u8,194u8,166u8,94u8,84u8,162u8,32u8,203u8,90u8,226u8,160u8,163u8,13u8,4u8,184u8,229u8, - 163u8,5u8,137u8,231u8,141u8,84u8,15u8,173u8,20u8,11u8,118u8,192u8,82u8,218u8,243u8,17u8,73u8,47u8,50u8,178u8, - 80u8,170u8,74u8,98u8,193u8,22u8,74u8,195u8,24u8,116u8,187u8,85u8,150u8,9u8,9u8,125u8,33u8,74u8,10u8,160u8, - 86u8,175u8,33u8,134u8,207u8,251u8,179u8,235u8,69u8,183u8,190u8,199u8,216u8,31u8,216u8,55u8,17u8,26u8,154u8,113u8, - 138u8,195u8,11u8,7u8,82u8,192u8,18u8,191u8,157u8,117u8,0u8,214u8,166u8,13u8,28u8,225u8,221u8,59u8,116u8,216u8, - 13u8,14u8,255u8,92u8,0u8,174u8,95u8,115u8,191u8,253u8,130u8,28u8,239u8,232u8,96u8,132u8,240u8,74u8,8u8,208u8, - 119u8,70u8,92u8,195u8,159u8,81u8,135u8,58u8,152u8,176u8,111u8,133u8,182u8,128u8,77u8,19u8,42u8,202u8,208u8,64u8, - 26u8,188u8,163u8,170u8,138u8,23u8,86u8,40u8,128u8,57u8,191u8,135u8,39u8,68u8,9u8,75u8,242u8,192u8,241u8,6u8, - 138u8,149u8,107u8,187u8,165u8,183u8,92u8,27u8,228u8,28u8,50u8,171u8,103u8,242u8,139u8,199u8,204u8,230u8,188u8,205u8, - 2u8,38u8,203u8,164u8,152u8,180u8,147u8,183u8,52u8,51u8,32u8,92u8,111u8,49u8,63u8,142u8,39u8,24u8,234u8,146u8, - 99u8,118u8,70u8,220u8,190u8,234u8,29u8,72u8,206u8,255u8,43u8,196u8,72u8,115u8,16u8,134u8,69u8,51u8,180u8,216u8, - 224u8,141u8,97u8,48u8,123u8,252u8,36u8,77u8,164u8,176u8,192u8,244u8,119u8,58u8,249u8,226u8,42u8,229u8,82u8,196u8, - 210u8,102u8,66u8,46u8,151u8,26u8,88u8,8u8,173u8,64u8,182u8,224u8,102u8,96u8,73u8,242u8,51u8,152u8,150u8,71u8, - 54u8,61u8,216u8,144u8,152u8,158u8,76u8,234u8,65u8,59u8,17u8,190u8,195u8,84u8,123u8,2u8,19u8,207u8,235u8,20u8, - 144u8,4u8,44u8,145u8,241u8,251u8,170u8,179u8,9u8,37u8,203u8,143u8,219u8,133u8,34u8,250u8,193u8,219u8,98u8,219u8, - 110u8,19u8,61u8,8u8,43u8,249u8,70u8,25u8,17u8,187u8,196u8,170u8,232u8,217u8,131u8,155u8,227u8,43u8,227u8,203u8, - 211u8,76u8,111u8,181u8,122u8,178u8,210u8,60u8,215u8,226u8,22u8,62u8,131u8,156u8,163u8,216u8,154u8,88u8,143u8,205u8, - 92u8,136u8,11u8,190u8,134u8,189u8,156u8,197u8,196u8,199u8,21u8,31u8,71u8,70u8,139u8,62u8,161u8,225u8,155u8,178u8, - 136u8,66u8,34u8,186u8,128u8,140u8,16u8,57u8,152u8,171u8,150u8,107u8,201u8,170u8,225u8,16u8,34u8,134u8,60u8,104u8, - 68u8,38u8,180u8,4u8,2u8,226u8,224u8,244u8,208u8,248u8,136u8,177u8,250u8,52u8,20u8,150u8,110u8,91u8,28u8,146u8, - 137u8,37u8,146u8,115u8,255u8,49u8,136u8,165u8,224u8,38u8,43u8,171u8,237u8,74u8,105u8,232u8,145u8,110u8,117u8,237u8, - 15u8,211u8,227u8,20u8,216u8,83u8,4u8,102u8,204u8,100u8,193u8,171u8,65u8,96u8,91u8,138u8,207u8,238u8,107u8,176u8, - 253u8,234u8,79u8,92u8,47u8,144u8,109u8,47u8,184u8,61u8,94u8,127u8,253u8,55u8,51u8,248u8,254u8,224u8,31u8,200u8, - 221u8,39u8,221u8,2u8,20u8,0u8,0u8,0u8,0u8,15u8,116u8,111u8,107u8,101u8,110u8,95u8,116u8,114u8,97u8,110u8, - 115u8,102u8,101u8,114u8,115u8,215u8,13u8,31u8,139u8,8u8,0u8,0u8,0u8,0u8,0u8,2u8,255u8,213u8,89u8,109u8, - 111u8,219u8,54u8,16u8,254u8,158u8,95u8,193u8,122u8,128u8,39u8,13u8,66u8,45u8,117u8,47u8,40u8,148u8,38u8,24u8, - 80u8,164u8,104u8,49u8,160u8,25u8,214u8,0u8,27u8,16u8,4u8,130u8,44u8,209u8,142u8,16u8,89u8,244u8,72u8,202u8, - 169u8,87u8,228u8,191u8,143u8,111u8,146u8,72u8,138u8,146u8,157u8,198u8,73u8,90u8,127u8,136u8,34u8,146u8,199u8,59u8, - 222u8,61u8,247u8,240u8,72u8,205u8,102u8,51u8,112u8,113u8,93u8,16u8,176u8,66u8,121u8,93u8,66u8,176u8,198u8,104u8, - 83u8,228u8,144u8,0u8,122u8,13u8,193u8,2u8,213u8,85u8,158u8,210u8,2u8,85u8,236u8,95u8,12u8,40u8,78u8,43u8, - 178u8,128u8,24u8,23u8,213u8,18u8,160u8,5u8,184u8,64u8,55u8,176u8,34u8,71u8,74u8,50u8,93u8,83u8,68u8,18u8, - 202u8,219u8,226u8,88u8,60u8,146u8,70u8,128u8,128u8,47u8,71u8,128u8,253u8,106u8,2u8,1u8,161u8,121u8,28u8,147u8, - 98u8,89u8,65u8,124u8,108u8,53u8,82u8,62u8,113u8,28u8,127u8,18u8,79u8,171u8,147u8,105u8,69u8,154u8,128u8,84u8, - 38u8,122u8,104u8,58u8,47u8,97u8,28u8,127u8,249u8,4u8,203u8,69u8,0u8,46u8,248u8,219u8,157u8,61u8,80u8,183u8, - 170u8,27u8,202u8,223u8,212u8,227u8,67u8,222u8,147u8,89u8,224u8,116u8,5u8,111u8,17u8,190u8,137u8,227u8,52u8,203u8, - 152u8,39u8,232u8,200u8,8u8,184u8,129u8,21u8,109u8,103u8,62u8,227u8,111u8,239u8,211u8,42u8,23u8,166u8,8u8,169u8, - 217u8,76u8,61u8,192u8,25u8,95u8,9u8,121u8,217u8,180u8,170u8,231u8,76u8,218u8,193u8,220u8,202u8,252u8,5u8,114u8, - 4u8,73u8,245u8,35u8,5u8,240u8,115u8,65u8,168u8,24u8,145u8,161u8,138u8,80u8,112u8,118u8,113u8,254u8,199u8,217u8, - 199u8,228u8,252u8,221u8,187u8,179u8,191u8,146u8,143u8,231u8,23u8,201u8,217u8,63u8,31u8,62u8,93u8,196u8,160u8,254u8, - 237u8,23u8,112u8,2u8,34u8,165u8,139u8,57u8,178u8,206u8,40u8,248u8,19u8,86u8,57u8,115u8,228u8,219u8,50u8,45u8, - 86u8,4u8,92u8,167u8,4u8,220u8,192u8,173u8,138u8,4u8,255u8,173u8,101u8,127u8,146u8,137u8,1u8,177u8,116u8,221u8, - 27u8,97u8,198u8,57u8,183u8,226u8,67u8,174u8,156u8,115u8,26u8,180u8,50u8,194u8,188u8,68u8,172u8,150u8,73u8,104u8, - 235u8,212u8,228u8,68u8,171u8,38u8,147u8,165u8,85u8,6u8,203u8,100u8,135u8,232u8,91u8,49u8,202u8,61u8,1u8,55u8, - 112u8,68u8,146u8,119u8,235u8,50u8,119u8,134u8,27u8,244u8,5u8,9u8,47u8,100u8,104u8,189u8,13u8,64u8,142u8,209u8, - 58u8,96u8,99u8,16u8,134u8,154u8,79u8,40u8,74u8,210u8,60u8,199u8,49u8,224u8,127u8,33u8,33u8,129u8,214u8,195u8, - 17u8,93u8,228u8,113u8,3u8,152u8,113u8,93u8,194u8,30u8,161u8,110u8,84u8,17u8,83u8,177u8,191u8,46u8,254u8,75u8, - 87u8,28u8,137u8,34u8,226u8,131u8,6u8,216u8,174u8,124u8,46u8,51u8,218u8,184u8,60u8,161u8,1u8,139u8,186u8,2u8, - 69u8,85u8,208u8,34u8,45u8,139u8,255u8,96u8,98u8,241u8,144u8,167u8,50u8,57u8,6u8,83u8,73u8,65u8,190u8,102u8, - 202u8,10u8,109u8,184u8,128u8,215u8,54u8,8u8,61u8,82u8,32u8,48u8,26u8,205u8,204u8,250u8,98u8,244u8,185u8,50u8, - 75u8,81u8,84u8,5u8,111u8,157u8,233u8,229u8,249u8,65u8,111u8,10u8,51u8,91u8,26u8,179u8,249u8,20u8,178u8,49u8, - 185u8,118u8,167u8,93u8,179u8,66u8,199u8,148u8,206u8,60u8,220u8,49u8,115u8,47u8,43u8,199u8,230u8,55u8,210u8,116u8, - 215u8,196u8,93u8,210u8,14u8,76u8,121u8,215u8,190u8,249u8,118u8,128u8,51u8,12u8,83u8,218u8,4u8,87u8,174u8,167u8, - 200u8,189u8,94u8,238u8,246u8,241u8,227u8,199u8,38u8,29u8,116u8,161u8,27u8,104u8,214u8,32u8,26u8,88u8,141u8,114u8, - 234u8,35u8,211u8,92u8,101u8,228u8,186u8,158u8,151u8,69u8,6u8,216u8,234u8,240u8,86u8,88u8,44u8,141u8,36u8,25u8, - 46u8,214u8,180u8,3u8,24u8,97u8,56u8,129u8,204u8,96u8,137u8,197u8,78u8,1u8,134u8,25u8,44u8,54u8,208u8,69u8, - 67u8,98u8,233u8,200u8,217u8,131u8,202u8,18u8,102u8,124u8,179u8,142u8,129u8,220u8,66u8,187u8,190u8,138u8,109u8,84u8, - 253u8,86u8,182u8,215u8,175u8,33u8,166u8,219u8,132u8,169u8,34u8,66u8,174u8,205u8,38u8,103u8,138u8,249u8,44u8,168u8, - 255u8,214u8,5u8,83u8,59u8,152u8,1u8,37u8,164u8,173u8,107u8,216u8,142u8,164u8,54u8,92u8,35u8,94u8,69u8,158u8, - 224u8,244u8,214u8,83u8,11u8,9u8,52u8,187u8,3u8,97u8,103u8,208u8,179u8,203u8,63u8,54u8,247u8,31u8,111u8,42u8, - 29u8,23u8,180u8,142u8,234u8,66u8,29u8,40u8,179u8,149u8,140u8,25u8,143u8,54u8,18u8,253u8,16u8,76u8,239u8,17u8, - 131u8,251u8,208u8,210u8,158u8,62u8,147u8,102u8,8u8,156u8,49u8,183u8,73u8,91u8,88u8,221u8,33u8,53u8,51u8,136u8, - 123u8,114u8,128u8,230u8,137u8,98u8,1u8,188u8,23u8,162u8,62u8,32u8,111u8,140u8,153u8,79u8,61u8,109u8,50u8,223u8, - 183u8,192u8,60u8,194u8,141u8,74u8,69u8,135u8,104u8,85u8,78u8,52u8,54u8,154u8,172u8,6u8,78u8,140u8,121u8,167u8, - 171u8,154u8,130u8,57u8,98u8,181u8,205u8,109u8,178u8,44u8,209u8,60u8,45u8,19u8,214u8,50u8,102u8,217u8,75u8,115u8, - 190u8,99u8,7u8,134u8,154u8,212u8,102u8,46u8,113u8,167u8,124u8,63u8,254u8,190u8,99u8,158u8,14u8,136u8,183u8,5u8, - 189u8,206u8,25u8,250u8,228u8,60u8,94u8,3u8,163u8,1u8,240u8,180u8,110u8,86u8,252u8,205u8,106u8,48u8,154u8,22u8, - 21u8,241u8,76u8,203u8,3u8,203u8,218u8,158u8,207u8,149u8,56u8,91u8,245u8,14u8,73u8,245u8,174u8,169u8,191u8,3u8, - 176u8,36u8,208u8,154u8,143u8,47u8,44u8,39u8,52u8,105u8,23u8,39u8,231u8,87u8,222u8,103u8,110u8,223u8,101u8,224u8, - 113u8,159u8,202u8,226u8,120u8,5u8,241u8,18u8,122u8,237u8,188u8,14u8,91u8,52u8,60u8,168u8,122u8,23u8,174u8,10u8, - 42u8,201u8,189u8,191u8,17u8,61u8,20u8,30u8,250u8,62u8,101u8,114u8,175u8,93u8,101u8,245u8,55u8,96u8,189u8,170u8, - 104u8,49u8,226u8,24u8,165u8,194u8,222u8,235u8,145u8,48u8,176u8,246u8,163u8,238u8,213u8,73u8,46u8,29u8,217u8,203u8, - 141u8,208u8,38u8,251u8,142u8,80u8,108u8,170u8,105u8,56u8,232u8,185u8,200u8,254u8,27u8,224u8,117u8,225u8,50u8,111u8, - 218u8,229u8,179u8,157u8,154u8,131u8,124u8,46u8,37u8,29u8,94u8,158u8,238u8,237u8,230u8,1u8,62u8,223u8,195u8,45u8, - 41u8,33u8,108u8,65u8,47u8,188u8,49u8,30u8,246u8,131u8,129u8,99u8,155u8,69u8,85u8,7u8,163u8,215u8,131u8,48u8, - 171u8,99u8,11u8,106u8,92u8,235u8,59u8,217u8,182u8,113u8,197u8,61u8,185u8,50u8,0u8,226u8,100u8,207u8,202u8,68u8, - 68u8,19u8,113u8,223u8,224u8,13u8,120u8,203u8,197u8,236u8,164u8,99u8,63u8,12u8,121u8,253u8,190u8,63u8,243u8,241u8, - 57u8,100u8,154u8,119u8,120u8,94u8,66u8,69u8,126u8,137u8,236u8,241u8,166u8,82u8,139u8,38u8,166u8,70u8,230u8,112u8, - 141u8,72u8,161u8,70u8,219u8,219u8,16u8,31u8,191u8,139u8,41u8,245u8,250u8,247u8,1u8,145u8,214u8,107u8,110u8,7u8, - 73u8,106u8,71u8,176u8,113u8,146u8,28u8,13u8,247u8,211u8,17u8,167u8,126u8,66u8,249u8,126u8,138u8,229u8,111u8,129u8, - 63u8,53u8,207u8,141u8,149u8,199u8,102u8,0u8,248u8,109u8,212u8,103u8,86u8,0u8,130u8,5u8,70u8,43u8,128u8,106u8, - 108u8,147u8,16u8,59u8,176u8,177u8,73u8,104u8,141u8,43u8,54u8,3u8,88u8,166u8,204u8,34u8,188u8,237u8,49u8,176u8, - 174u8,251u8,49u8,10u8,235u8,71u8,171u8,161u8,15u8,85u8,105u8,238u8,179u8,13u8,200u8,170u8,230u8,169u8,247u8,130u8, - 125u8,75u8,237u8,39u8,225u8,209u8,93u8,52u8,106u8,236u8,249u8,251u8,112u8,104u8,239u8,114u8,226u8,161u8,126u8,114u8, - 92u8,145u8,184u8,88u8,213u8,190u8,95u8,123u8,230u8,2u8,244u8,135u8,75u8,10u8,9u8,109u8,152u8,131u8,133u8,225u8, - 247u8,240u8,115u8,20u8,0u8,116u8,203u8,18u8,64u8,190u8,189u8,242u8,175u8,236u8,172u8,229u8,34u8,73u8,181u8,104u8, - 197u8,90u8,98u8,149u8,114u8,205u8,235u8,253u8,185u8,77u8,207u8,31u8,111u8,218u8,210u8,89u8,228u8,91u8,199u8,73u8, - 213u8,51u8,146u8,175u8,141u8,176u8,133u8,54u8,97u8,223u8,152u8,152u8,24u8,160u8,167u8,231u8,192u8,125u8,126u8,75u8, - 192u8,234u8,157u8,213u8,31u8,56u8,17u8,174u8,236u8,84u8,244u8,239u8,30u8,218u8,21u8,117u8,131u8,244u8,19u8,100u8, - 212u8,175u8,106u8,197u8,192u8,192u8,88u8,176u8,65u8,35u8,71u8,182u8,138u8,81u8,1u8,75u8,133u8,65u8,252u8,187u8, - 52u8,237u8,9u8,154u8,80u8,161u8,70u8,189u8,70u8,242u8,245u8,231u8,1u8,16u8,193u8,188u8,224u8,187u8,20u8,241u8, - 250u8,251u8,174u8,205u8,253u8,114u8,246u8,129u8,246u8,200u8,108u8,63u8,24u8,242u8,94u8,29u8,22u8,121u8,225u8,78u8, - 232u8,133u8,15u8,199u8,94u8,104u8,131u8,175u8,85u8,31u8,237u8,84u8,31u8,61u8,92u8,125u8,212u8,168u8,31u8,7u8, - 127u8,56u8,10u8,77u8,167u8,72u8,228u8,20u8,233u8,159u8,34u8,228u8,254u8,192u8,56u8,155u8,227u8,91u8,108u8,222u8, - 35u8,81u8,210u8,48u8,14u8,78u8,78u8,64u8,56u8,156u8,133u8,225u8,142u8,228u8,184u8,191u8,13u8,42u8,226u8,150u8, - 9u8,209u8,176u8,9u8,209u8,227u8,152u8,16u8,13u8,152u8,224u8,228u8,150u8,112u8,52u8,26u8,3u8,86u8,59u8,67u8, - 62u8,164u8,33u8,218u8,143u8,190u8,190u8,66u8,68u8,89u8,53u8,14u8,43u8,23u8,215u8,37u8,168u8,42u8,183u8,87u8, - 3u8,39u8,15u8,157u8,58u8,90u8,250u8,106u8,106u8,87u8,253u8,90u8,215u8,111u8,139u8,83u8,141u8,136u8,122u8,223u8, - 176u8,213u8,135u8,96u8,121u8,136u8,176u8,47u8,82u8,187u8,202u8,62u8,225u8,133u8,61u8,207u8,101u8,37u8,86u8,211u8, - 197u8,107u8,111u8,62u8,121u8,15u8,203u8,18u8,5u8,224u8,111u8,132u8,203u8,124u8,98u8,145u8,128u8,38u8,204u8,170u8, - 25u8,241u8,113u8,62u8,33u8,144u8,82u8,254u8,77u8,254u8,4u8,108u8,88u8,23u8,194u8,111u8,230u8,8u8,149u8,167u8, - 151u8,139u8,148u8,221u8,29u8,6u8,64u8,127u8,92u8,61u8,132u8,26u8,28u8,216u8,107u8,50u8,80u8,135u8,129u8,121u8, - 184u8,233u8,236u8,53u8,171u8,179u8,38u8,126u8,70u8,227u8,79u8,83u8,203u8,55u8,102u8,183u8,229u8,166u8,183u8,218u8, - 33u8,206u8,116u8,217u8,168u8,216u8,53u8,165u8,107u8,18u8,207u8,102u8,194u8,1u8,47u8,115u8,184u8,177u8,199u8,71u8, - 230u8,235u8,136u8,199u8,141u8,138u8,204u8,177u8,45u8,125u8,69u8,132u8,218u8,7u8,251u8,146u8,170u8,135u8,75u8,92u8, - 250u8,194u8,69u8,90u8,151u8,52u8,97u8,223u8,241u8,73u8,55u8,149u8,4u8,217u8,233u8,165u8,181u8,204u8,148u8,210u8, - 52u8,187u8,97u8,107u8,179u8,215u8,95u8,213u8,43u8,22u8,186u8,132u8,97u8,118u8,226u8,15u8,40u8,216u8,48u8,19u8, - 58u8,5u8,234u8,81u8,191u8,62u8,61u8,189u8,156u8,79u8,162u8,112u8,18u8,128u8,249u8,228u8,215u8,201u8,128u8,44u8, - 221u8,174u8,225u8,110u8,235u8,138u8,138u8,194u8,37u8,196u8,14u8,243u8,218u8,158u8,171u8,227u8,1u8,72u8,73u8,223u8, - 218u8,247u8,3u8,7u8,2u8,149u8,72u8,238u8,22u8,79u8,226u8,109u8,7u8,158u8,198u8,134u8,186u8,234u8,120u8,87u8, - 219u8,125u8,33u8,58u8,146u8,138u8,22u8,150u8,195u8,208u8,108u8,8u8,93u8,223u8,49u8,135u8,113u8,205u8,127u8,58u8, - 236u8,220u8,61u8,28u8,47u8,238u8,30u8,129u8,134u8,222u8,193u8,101u8,40u8,166u8,234u8,14u8,228u8,235u8,150u8,122u8, - 240u8,56u8,135u8,189u8,47u8,208u8,119u8,71u8,255u8,3u8,69u8,165u8,207u8,100u8,48u8,37u8,0u8,0u8,0u8,0u8, - 3u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8,14u8,65u8,112u8,116u8,111u8,115u8,70u8, - 114u8,97u8,109u8,101u8,119u8,111u8,114u8,107u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,1u8, - 11u8,65u8,112u8,116u8,111u8,115u8,83u8,116u8,100u8,108u8,105u8,98u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, - 0u8,0u8,0u8,1u8,10u8,77u8,111u8,118u8,101u8,83u8,116u8,100u8,108u8,105u8,98u8,0u8, - ]; - code::publish_package_txn(&framework_signer, chunk1, code) - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/4-gas-schedule.move b/aptos-move/aptos-release-builder/data/example_output/4-gas-schedule.move deleted file mode 100644 index 2bacc541f26b9..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/4-gas-schedule.move +++ /dev/null @@ -1,757 +0,0 @@ -// Script hash: 0afa672e -// source commit hash: 7bc3f195928488963bb947dc0e300f26527f2675 - -// Gas schedule upgrade proposal - -// Feature version: 7 -// -// Entries: -// instr.nop : 200 -// instr.ret : 1200 -// instr.abort : 1200 -// instr.br_true : 2400 -// instr.br_false : 2400 -// instr.branch : 1600 -// instr.pop : 800 -// instr.ld_u8 : 1200 -// instr.ld_u16 : 1200 -// instr.ld_u32 : 1200 -// instr.ld_u64 : 1200 -// instr.ld_u128 : 1600 -// instr.ld_u256 : 1600 -// instr.ld_true : 1200 -// instr.ld_false : 1200 -// instr.ld_const.base : 13000 -// instr.ld_const.per_byte : 700 -// instr.imm_borrow_loc : 1200 -// instr.mut_borrow_loc : 1200 -// instr.imm_borrow_field : 4000 -// instr.mut_borrow_field : 4000 -// instr.imm_borrow_field_generic : 4000 -// instr.mut_borrow_field_generic : 4000 -// instr.copy_loc.base : 1600 -// instr.copy_loc.per_abs_val_unit : 80 -// instr.move_loc.base : 2400 -// instr.st_loc.base : 2400 -// instr.call.base : 20000 -// instr.call.per_arg : 2000 -// instr.call.per_local : 2000 -// instr.call_generic.base : 20000 -// instr.call_generic.per_ty_arg : 2000 -// instr.call_generic.per_arg : 2000 -// instr.call_generic.per_local : 2000 -// instr.pack.base : 4400 -// instr.pack.per_field : 800 -// instr.pack_generic.base : 4400 -// instr.pack_generic.per_field : 800 -// instr.unpack.base : 4400 -// instr.unpack.per_field : 800 -// instr.unpack_generic.base : 4400 -// instr.unpack_generic.per_field : 800 -// instr.read_ref.base : 4000 -// instr.read_ref.per_abs_val_unit : 80 -// instr.write_ref.base : 4000 -// instr.freeze_ref : 200 -// instr.cast_u8 : 2400 -// instr.cast_u16 : 2400 -// instr.cast_u32 : 2400 -// instr.cast_u64 : 2400 -// instr.cast_u128 : 2400 -// instr.cast_u256 : 2400 -// instr.add : 3200 -// instr.sub : 3200 -// instr.mul : 3200 -// instr.mod : 3200 -// instr.div : 3200 -// instr.bit_or : 3200 -// instr.bit_and : 3200 -// instr.bit_xor : 3200 -// instr.bit_shl : 3200 -// instr.bit_shr : 3200 -// instr.or : 3200 -// instr.and : 3200 -// instr.not : 3200 -// instr.lt : 3200 -// instr.gt : 3200 -// instr.le : 3200 -// instr.ge : 3200 -// instr.eq.base : 2000 -// instr.eq.per_abs_val_unit : 80 -// instr.neq.base : 2000 -// instr.neq.per_abs_val_unit : 80 -// instr.imm_borrow_global.base : 10000 -// instr.imm_borrow_global_generic.base : 10000 -// instr.mut_borrow_global.base : 10000 -// instr.mut_borrow_global_generic.base : 10000 -// instr.exists.base : 5000 -// instr.exists_generic.base : 5000 -// instr.move_from.base : 7000 -// instr.move_from_generic.base : 7000 -// instr.move_to.base : 10000 -// instr.move_to_generic.base : 10000 -// instr.vec_len.base : 4400 -// instr.vec_imm_borrow.base : 6600 -// instr.vec_mut_borrow.base : 6600 -// instr.vec_push_back.base : 7600 -// instr.vec_pop_back.base : 5200 -// instr.vec_swap.base : 6000 -// instr.vec_pack.base : 12000 -// instr.vec_pack.per_elem : 800 -// instr.vec_unpack.base : 10000 -// instr.vec_unpack.per_expected_elem : 800 -// txn.min_transaction_gas_units : 1500000 -// txn.large_transaction_cutoff : 600 -// txn.intrinsic_gas_per_byte : 2000 -// txn.maximum_number_of_gas_units : 2000000 -// txn.min_price_per_gas_unit : 100 -// txn.max_price_per_gas_unit : 10000000000 -// txn.max_transaction_size_in_bytes : 65536 -// txn.gas_unit_scaling_factor : 1000000 -// txn.load_data.base : 16000 -// txn.load_data.per_byte : 1000 -// txn.load_data.failure : 0 -// txn.write_data.per_op : 160000 -// txn.write_data.new_item : 1280000 -// txn.write_data.per_byte_in_key : 10000 -// txn.write_data.per_byte_in_val : 10000 -// txn.memory_quota : 10000000 -// txn.free_write_bytes_quota : 1024 -// txn.free_event_bytes_quota : 1024 -// txn.max_bytes_per_write_op : 1048576 -// txn.max_bytes_all_write_ops_per_transaction : 10485760 -// txn.max_bytes_per_event : 1048576 -// txn.max_bytes_all_events_per_transaction : 10485760 -// txn.storage_fee_per_state_slot_create : 50000 -// txn.storage_fee_per_excess_state_byte : 50 -// txn.storage_fee_per_event_byte : 20 -// txn.storage_fee_per_transaction_byte : 20 -// txn.max_execution_gas : 20000000000 -// txn.max_io_gas : 10000000000 -// txn.max_storage_fee : 200000000 -// move_stdlib.bcs.to_bytes.per_byte_serialized : 200 -// move_stdlib.bcs.to_bytes.failure : 20000 -// move_stdlib.hash.sha2_256.base : 60000 -// move_stdlib.hash.sha2_256.per_byte : 1000 -// move_stdlib.hash.sha3_256.base : 80000 -// move_stdlib.hash.sha3_256.per_byte : 900 -// move_stdlib.signer.borrow_address.base : 4000 -// move_stdlib.string.check_utf8.base : 6000 -// move_stdlib.string.check_utf8.per_byte : 160 -// move_stdlib.string.is_char_boundary.base : 6000 -// move_stdlib.string.sub_string.base : 8000 -// move_stdlib.string.sub_string.per_byte : 60 -// move_stdlib.string.index_of.base : 8000 -// move_stdlib.string.index_of.per_byte_pattern : 400 -// move_stdlib.string.index_of.per_byte_searched : 200 -// aptos_framework.account.create_address.base : 6000 -// aptos_framework.account.create_signer.base : 6000 -// aptos_framework.bls12381.base : 3000 -// aptos_framework.bls12381.per_pubkey_deserialize : 2180000 -// aptos_framework.bls12381.per_pubkey_aggregate : 84000 -// aptos_framework.bls12381.per_pubkey_subgroup_check : 7400000 -// aptos_framework.bls12381.per_sig_deserialize : 4440000 -// aptos_framework.bls12381.per_sig_aggregate : 233000 -// aptos_framework.bls12381.per_sig_subgroup_check : 9210000 -// aptos_framework.bls12381.per_sig_verify : 169700000 -// aptos_framework.bls12381.per_pop_verify : 206000000 -// aptos_framework.bls12381.per_pairing : 80260000 -// aptos_framework.bls12381.per_msg_hashing : 30800000 -// aptos_framework.bls12381.per_byte_hashing : 1000 -// aptos_framework.signature.base : 3000 -// aptos_framework.signature.per_pubkey_deserialize : 760000 -// aptos_framework.signature.per_pubkey_small_order_check : 127000 -// aptos_framework.signature.per_sig_deserialize : 7500 -// aptos_framework.signature.per_sig_strict_verify : 5340000 -// aptos_framework.signature.per_msg_hashing_base : 64800 -// aptos_framework.signature.per_msg_byte_hashing : 1200 -// aptos_framework.secp256k1.base : 3000 -// aptos_framework.secp256k1.ecdsa_recover : 32200000 -// aptos_framework.ristretto255.basepoint_mul : 2560000 -// aptos_framework.ristretto255.basepoint_double_mul : 8800000 -// aptos_framework.ristretto255.point_add : 42700 -// aptos_framework.ristretto255.point_compress : 800000 -// aptos_framework.ristretto255.point_decompress : 810000 -// aptos_framework.ristretto255.point_equals : 46000 -// aptos_framework.ristretto255.point_from_64_uniform_bytes : 1630000 -// aptos_framework.ristretto255.point_identity : 3000 -// aptos_framework.ristretto255.point_mul : 9420000 -// aptos_framework.ristretto255.point_neg : 7200 -// aptos_framework.ristretto255.point_sub : 42600 -// aptos_framework.ristretto255.point_parse_arg : 3000 -// aptos_framework.ristretto255.scalar_sha512_per_byte : 1200 -// aptos_framework.ristretto255.scalar_sha512_per_hash : 64800 -// aptos_framework.ristretto255.scalar_add : 15400 -// aptos_framework.ristretto255.scalar_reduced_from_32_bytes : 14200 -// aptos_framework.ristretto255.scalar_uniform_from_64_bytes : 24900 -// aptos_framework.ristretto255.scalar_from_u128 : 3500 -// aptos_framework.ristretto255.scalar_from_u64 : 3500 -// aptos_framework.ristretto255.scalar_invert : 2200000 -// aptos_framework.ristretto255.scalar_is_canonical : 23000 -// aptos_framework.ristretto255.scalar_mul : 21300 -// aptos_framework.ristretto255.scalar_neg : 14500 -// aptos_framework.ristretto255.scalar_sub : 21200 -// aptos_framework.ristretto255.scalar_parse_arg : 3000 -// aptos_framework.hash.sip_hash.base : 20000 -// aptos_framework.hash.sip_hash.per_byte : 400 -// aptos_framework.hash.keccak256.base : 80000 -// aptos_framework.hash.keccak256.per_byte : 900 -// aptos_framework.type_info.type_of.base : 6000 -// aptos_framework.type_info.type_of.per_abstract_memory_unit : 100 -// aptos_framework.type_info.type_name.base : 6000 -// aptos_framework.type_info.type_name.per_abstract_memory_unit : 100 -// aptos_framework.type_info.chain_id.base : 3000 -// aptos_framework.hash.sha2_512.base : 3240 -// aptos_framework.hash.sha2_512.per_byte : 60 -// aptos_framework.hash.sha3_512.base : 4500 -// aptos_framework.hash.sha3_512.per_byte : 50 -// aptos_framework.hash.ripemd160.base : 3000 -// aptos_framework.hash.ripemd160.per_byte : 50 -// aptos_framework.hash.blake2b_256.base : 1750 -// aptos_framework.hash.blake2b_256.per_byte : 15 -// aptos_framework.util.from_bytes.base : 6000 -// aptos_framework.util.from_bytes.per_byte : 100 -// aptos_framework.transaction_context.get_script_hash.base : 4000 -// aptos_framework.code.request_publish.base : 10000 -// aptos_framework.code.request_publish.per_byte : 40 -// aptos_framework.event.write_to_event_store.base : 300000 -// aptos_framework.event.write_to_event_store.per_abstract_memory_unit : 5000 -// aptos_framework.state_storage.get_usage.base : 10000 -// aptos_framework.aggregator.add.base : 6000 -// aptos_framework.aggregator.read.base : 6000 -// aptos_framework.aggregator.sub.base : 6000 -// aptos_framework.aggregator.destroy.base : 10000 -// aptos_framework.aggregator_factory.new_aggregator.base : 10000 -// aptos_framework.object.exists_at.base : 5000 -// aptos_framework.object.exists_at.per_byte_loaded : 1000 -// aptos_framework.object.exists_at.per_item_loaded : 8000 -// table.common.load.base : 8000 -// table.common.load.base_new : 8000 -// table.common.load.per_byte : 1000 -// table.common.load.failure : 0 -// table.new_table_handle.base : 20000 -// table.add_box.base : 24000 -// table.add_box.per_byte_serialized : 200 -// table.borrow_box.base : 24000 -// table.borrow_box.per_byte_serialized : 200 -// table.contains_box.base : 24000 -// table.contains_box.per_byte_serialized : 200 -// table.remove_box.base : 24000 -// table.remove_box.per_byte_serialized : 200 -// table.destroy_empty_box.base : 24000 -// table.drop_unchecked_box.base : 2000 -// misc.abs_val.u8 : 40 -// misc.abs_val.u16 : 40 -// misc.abs_val.u32 : 40 -// misc.abs_val.u64 : 40 -// misc.abs_val.u128 : 40 -// misc.abs_val.u256 : 40 -// misc.abs_val.bool : 40 -// misc.abs_val.address : 40 -// misc.abs_val.struct : 40 -// misc.abs_val.vector : 40 -// misc.abs_val.reference : 40 -// misc.abs_val.per_u8_packed : 1 -// misc.abs_val.per_u16_packed : 2 -// misc.abs_val.per_u32_packed : 4 -// misc.abs_val.per_u64_packed : 8 -// misc.abs_val.per_u128_packed : 16 -// misc.abs_val.per_u256_packed : 32 -// misc.abs_val.per_bool_packed : 1 -// misc.abs_val.per_address_packed : 32 - -script { - use aptos_framework::aptos_governance; - use aptos_framework::gas_schedule; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000001, - vector[18u8,231u8,184u8,113u8,231u8,92u8,149u8,190u8,58u8,103u8,85u8,221u8,36u8,91u8,161u8,105u8,58u8,86u8,205u8,120u8,106u8,8u8,219u8,130u8,248u8,167u8,164u8,79u8,146u8,210u8,117u8,82u8,], - ); - let gas_schedule_blob: vector = vector[ - 7, 0, 0, 0, 0, 0, 0, 0, 252, 1, 9, 105, 110, 115, 116, 114, 46, 110, 111, 112, - 200, 0, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 114, 101, 116, 176, 4, - 0, 0, 0, 0, 0, 0, 11, 105, 110, 115, 116, 114, 46, 97, 98, 111, 114, 116, 176, 4, - 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 114, 95, 116, 114, 117, 101, - 96, 9, 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 98, 114, 95, 102, 97, - 108, 115, 101, 96, 9, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 98, 114, - 97, 110, 99, 104, 64, 6, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 112, - 111, 112, 32, 3, 0, 0, 0, 0, 0, 0, 11, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 117, 56, 176, 4, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 117, 49, 54, 176, 4, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, 100, - 95, 117, 51, 50, 176, 4, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 108, - 100, 95, 117, 54, 52, 176, 4, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, - 108, 100, 95, 117, 49, 50, 56, 64, 6, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, - 114, 46, 108, 100, 95, 117, 50, 53, 54, 64, 6, 0, 0, 0, 0, 0, 0, 13, 105, 110, - 115, 116, 114, 46, 108, 100, 95, 116, 114, 117, 101, 176, 4, 0, 0, 0, 0, 0, 0, 14, - 105, 110, 115, 116, 114, 46, 108, 100, 95, 102, 97, 108, 115, 101, 176, 4, 0, 0, 0, 0, - 0, 0, 19, 105, 110, 115, 116, 114, 46, 108, 100, 95, 99, 111, 110, 115, 116, 46, 98, 97, - 115, 101, 200, 50, 0, 0, 0, 0, 0, 0, 23, 105, 110, 115, 116, 114, 46, 108, 100, 95, - 99, 111, 110, 115, 116, 46, 112, 101, 114, 95, 98, 121, 116, 101, 188, 2, 0, 0, 0, 0, - 0, 0, 20, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, - 108, 111, 99, 176, 4, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 109, 117, - 116, 95, 98, 111, 114, 114, 111, 119, 95, 108, 111, 99, 176, 4, 0, 0, 0, 0, 0, 0, - 22, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, 102, 105, - 101, 108, 100, 160, 15, 0, 0, 0, 0, 0, 0, 22, 105, 110, 115, 116, 114, 46, 109, 117, - 116, 95, 98, 111, 114, 114, 111, 119, 95, 102, 105, 101, 108, 100, 160, 15, 0, 0, 0, 0, - 0, 0, 30, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 95, - 102, 105, 101, 108, 100, 95, 103, 101, 110, 101, 114, 105, 99, 160, 15, 0, 0, 0, 0, 0, - 0, 30, 105, 110, 115, 116, 114, 46, 109, 117, 116, 95, 98, 111, 114, 114, 111, 119, 95, 102, - 105, 101, 108, 100, 95, 103, 101, 110, 101, 114, 105, 99, 160, 15, 0, 0, 0, 0, 0, 0, - 19, 105, 110, 115, 116, 114, 46, 99, 111, 112, 121, 95, 108, 111, 99, 46, 98, 97, 115, 101, - 64, 6, 0, 0, 0, 0, 0, 0, 31, 105, 110, 115, 116, 114, 46, 99, 111, 112, 121, 95, - 108, 111, 99, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, 110, 105, 116, - 80, 0, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, - 108, 111, 99, 46, 98, 97, 115, 101, 96, 9, 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, - 116, 114, 46, 115, 116, 95, 108, 111, 99, 46, 98, 97, 115, 101, 96, 9, 0, 0, 0, 0, - 0, 0, 15, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 46, 98, 97, 115, 101, 32, 78, - 0, 0, 0, 0, 0, 0, 18, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 46, 112, 101, - 114, 95, 97, 114, 103, 208, 7, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, - 99, 97, 108, 108, 46, 112, 101, 114, 95, 108, 111, 99, 97, 108, 208, 7, 0, 0, 0, 0, - 0, 0, 23, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, - 99, 46, 98, 97, 115, 101, 32, 78, 0, 0, 0, 0, 0, 0, 29, 105, 110, 115, 116, 114, - 46, 99, 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 116, 121, - 95, 97, 114, 103, 208, 7, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, 116, 114, 46, 99, - 97, 108, 108, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 97, 114, 103, 208, - 7, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 99, 97, 108, 108, 95, 103, - 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 108, 111, 99, 97, 108, 208, 7, 0, 0, - 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, 46, 98, 97, 115, 101, - 48, 17, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, 46, - 112, 101, 114, 95, 102, 105, 101, 108, 100, 32, 3, 0, 0, 0, 0, 0, 0, 23, 105, 110, - 115, 116, 114, 46, 112, 97, 99, 107, 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, - 101, 48, 17, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 112, 97, 99, 107, - 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, 101, 114, 95, 102, 105, 101, 108, 100, 32, 3, - 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 46, - 98, 97, 115, 101, 48, 17, 0, 0, 0, 0, 0, 0, 22, 105, 110, 115, 116, 114, 46, 117, - 110, 112, 97, 99, 107, 46, 112, 101, 114, 95, 102, 105, 101, 108, 100, 32, 3, 0, 0, 0, - 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 95, 103, 101, 110, - 101, 114, 105, 99, 46, 98, 97, 115, 101, 48, 17, 0, 0, 0, 0, 0, 0, 30, 105, 110, - 115, 116, 114, 46, 117, 110, 112, 97, 99, 107, 95, 103, 101, 110, 101, 114, 105, 99, 46, 112, - 101, 114, 95, 102, 105, 101, 108, 100, 32, 3, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, - 116, 114, 46, 114, 101, 97, 100, 95, 114, 101, 102, 46, 98, 97, 115, 101, 160, 15, 0, 0, - 0, 0, 0, 0, 31, 105, 110, 115, 116, 114, 46, 114, 101, 97, 100, 95, 114, 101, 102, 46, - 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, 110, 105, 116, 80, 0, 0, 0, - 0, 0, 0, 0, 20, 105, 110, 115, 116, 114, 46, 119, 114, 105, 116, 101, 95, 114, 101, 102, - 46, 98, 97, 115, 101, 160, 15, 0, 0, 0, 0, 0, 0, 16, 105, 110, 115, 116, 114, 46, - 102, 114, 101, 101, 122, 101, 95, 114, 101, 102, 200, 0, 0, 0, 0, 0, 0, 0, 13, 105, - 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 56, 96, 9, 0, 0, 0, 0, 0, 0, - 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 49, 54, 96, 9, 0, 0, 0, - 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 51, 50, 96, 9, - 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, 95, 117, 54, - 52, 96, 9, 0, 0, 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, 99, 97, 115, 116, - 95, 117, 49, 50, 56, 96, 9, 0, 0, 0, 0, 0, 0, 15, 105, 110, 115, 116, 114, 46, - 99, 97, 115, 116, 95, 117, 50, 53, 54, 96, 9, 0, 0, 0, 0, 0, 0, 9, 105, 110, - 115, 116, 114, 46, 97, 100, 100, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, - 114, 46, 115, 117, 98, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, - 109, 117, 108, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 109, 111, - 100, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 100, 105, 118, 128, - 12, 0, 0, 0, 0, 0, 0, 12, 105, 110, 115, 116, 114, 46, 98, 105, 116, 95, 111, 114, - 128, 12, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 105, 116, 95, 97, - 110, 100, 128, 12, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, 105, 116, - 95, 120, 111, 114, 128, 12, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, 46, 98, - 105, 116, 95, 115, 104, 108, 128, 12, 0, 0, 0, 0, 0, 0, 13, 105, 110, 115, 116, 114, - 46, 98, 105, 116, 95, 115, 104, 114, 128, 12, 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, - 116, 114, 46, 111, 114, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, - 97, 110, 100, 128, 12, 0, 0, 0, 0, 0, 0, 9, 105, 110, 115, 116, 114, 46, 110, 111, - 116, 128, 12, 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 108, 116, 128, 12, - 0, 0, 0, 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 103, 116, 128, 12, 0, 0, 0, - 0, 0, 0, 8, 105, 110, 115, 116, 114, 46, 108, 101, 128, 12, 0, 0, 0, 0, 0, 0, - 8, 105, 110, 115, 116, 114, 46, 103, 101, 128, 12, 0, 0, 0, 0, 0, 0, 13, 105, 110, - 115, 116, 114, 46, 101, 113, 46, 98, 97, 115, 101, 208, 7, 0, 0, 0, 0, 0, 0, 25, - 105, 110, 115, 116, 114, 46, 101, 113, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, - 95, 117, 110, 105, 116, 80, 0, 0, 0, 0, 0, 0, 0, 14, 105, 110, 115, 116, 114, 46, - 110, 101, 113, 46, 98, 97, 115, 101, 208, 7, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, - 116, 114, 46, 110, 101, 113, 46, 112, 101, 114, 95, 97, 98, 115, 95, 118, 97, 108, 95, 117, - 110, 105, 116, 80, 0, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 105, 109, - 109, 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 46, 98, 97, 115, 101, - 16, 39, 0, 0, 0, 0, 0, 0, 36, 105, 110, 115, 116, 114, 46, 105, 109, 109, 95, 98, - 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 95, 103, 101, 110, 101, 114, 105, 99, - 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, - 109, 117, 116, 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 46, 98, 97, - 115, 101, 16, 39, 0, 0, 0, 0, 0, 0, 36, 105, 110, 115, 116, 114, 46, 109, 117, 116, - 95, 98, 111, 114, 114, 111, 119, 95, 103, 108, 111, 98, 97, 108, 95, 103, 101, 110, 101, 114, - 105, 99, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, 0, 0, 0, 17, 105, 110, 115, 116, - 114, 46, 101, 120, 105, 115, 116, 115, 46, 98, 97, 115, 101, 136, 19, 0, 0, 0, 0, 0, - 0, 25, 105, 110, 115, 116, 114, 46, 101, 120, 105, 115, 116, 115, 95, 103, 101, 110, 101, 114, - 105, 99, 46, 98, 97, 115, 101, 136, 19, 0, 0, 0, 0, 0, 0, 20, 105, 110, 115, 116, - 114, 46, 109, 111, 118, 101, 95, 102, 114, 111, 109, 46, 98, 97, 115, 101, 88, 27, 0, 0, - 0, 0, 0, 0, 28, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, 102, 114, 111, 109, - 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, 101, 88, 27, 0, 0, 0, 0, 0, - 0, 18, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, 116, 111, 46, 98, 97, 115, 101, - 16, 39, 0, 0, 0, 0, 0, 0, 26, 105, 110, 115, 116, 114, 46, 109, 111, 118, 101, 95, - 116, 111, 95, 103, 101, 110, 101, 114, 105, 99, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, - 0, 0, 0, 18, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 108, 101, 110, 46, 98, 97, - 115, 101, 48, 17, 0, 0, 0, 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 118, 101, 99, - 95, 105, 109, 109, 95, 98, 111, 114, 114, 111, 119, 46, 98, 97, 115, 101, 200, 25, 0, 0, - 0, 0, 0, 0, 25, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 109, 117, 116, 95, 98, - 111, 114, 114, 111, 119, 46, 98, 97, 115, 101, 200, 25, 0, 0, 0, 0, 0, 0, 24, 105, - 110, 115, 116, 114, 46, 118, 101, 99, 95, 112, 117, 115, 104, 95, 98, 97, 99, 107, 46, 98, - 97, 115, 101, 176, 29, 0, 0, 0, 0, 0, 0, 23, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 112, 111, 112, 95, 98, 97, 99, 107, 46, 98, 97, 115, 101, 80, 20, 0, 0, 0, - 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 115, 119, 97, 112, 46, 98, - 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 19, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 112, 97, 99, 107, 46, 98, 97, 115, 101, 224, 46, 0, 0, 0, 0, 0, 0, 23, - 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 112, 97, 99, 107, 46, 112, 101, 114, 95, 101, - 108, 101, 109, 32, 3, 0, 0, 0, 0, 0, 0, 21, 105, 110, 115, 116, 114, 46, 118, 101, - 99, 95, 117, 110, 112, 97, 99, 107, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, 0, 0, - 0, 34, 105, 110, 115, 116, 114, 46, 118, 101, 99, 95, 117, 110, 112, 97, 99, 107, 46, 112, - 101, 114, 95, 101, 120, 112, 101, 99, 116, 101, 100, 95, 101, 108, 101, 109, 32, 3, 0, 0, - 0, 0, 0, 0, 29, 116, 120, 110, 46, 109, 105, 110, 95, 116, 114, 97, 110, 115, 97, 99, - 116, 105, 111, 110, 95, 103, 97, 115, 95, 117, 110, 105, 116, 115, 96, 227, 22, 0, 0, 0, - 0, 0, 28, 116, 120, 110, 46, 108, 97, 114, 103, 101, 95, 116, 114, 97, 110, 115, 97, 99, - 116, 105, 111, 110, 95, 99, 117, 116, 111, 102, 102, 88, 2, 0, 0, 0, 0, 0, 0, 26, - 116, 120, 110, 46, 105, 110, 116, 114, 105, 110, 115, 105, 99, 95, 103, 97, 115, 95, 112, 101, - 114, 95, 98, 121, 116, 101, 208, 7, 0, 0, 0, 0, 0, 0, 31, 116, 120, 110, 46, 109, - 97, 120, 105, 109, 117, 109, 95, 110, 117, 109, 98, 101, 114, 95, 111, 102, 95, 103, 97, 115, - 95, 117, 110, 105, 116, 115, 128, 132, 30, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 109, - 105, 110, 95, 112, 114, 105, 99, 101, 95, 112, 101, 114, 95, 103, 97, 115, 95, 117, 110, 105, - 116, 100, 0, 0, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 109, 97, 120, 95, 112, 114, - 105, 99, 101, 95, 112, 101, 114, 95, 103, 97, 115, 95, 117, 110, 105, 116, 0, 228, 11, 84, - 2, 0, 0, 0, 33, 116, 120, 110, 46, 109, 97, 120, 95, 116, 114, 97, 110, 115, 97, 99, - 116, 105, 111, 110, 95, 115, 105, 122, 101, 95, 105, 110, 95, 98, 121, 116, 101, 115, 0, 0, - 1, 0, 0, 0, 0, 0, 27, 116, 120, 110, 46, 103, 97, 115, 95, 117, 110, 105, 116, 95, - 115, 99, 97, 108, 105, 110, 103, 95, 102, 97, 99, 116, 111, 114, 64, 66, 15, 0, 0, 0, - 0, 0, 18, 116, 120, 110, 46, 108, 111, 97, 100, 95, 100, 97, 116, 97, 46, 98, 97, 115, - 101, 128, 62, 0, 0, 0, 0, 0, 0, 22, 116, 120, 110, 46, 108, 111, 97, 100, 95, 100, - 97, 116, 97, 46, 112, 101, 114, 95, 98, 121, 116, 101, 232, 3, 0, 0, 0, 0, 0, 0, - 21, 116, 120, 110, 46, 108, 111, 97, 100, 95, 100, 97, 116, 97, 46, 102, 97, 105, 108, 117, - 114, 101, 0, 0, 0, 0, 0, 0, 0, 0, 21, 116, 120, 110, 46, 119, 114, 105, 116, 101, - 95, 100, 97, 116, 97, 46, 112, 101, 114, 95, 111, 112, 0, 113, 2, 0, 0, 0, 0, 0, - 23, 116, 120, 110, 46, 119, 114, 105, 116, 101, 95, 100, 97, 116, 97, 46, 110, 101, 119, 95, - 105, 116, 101, 109, 0, 136, 19, 0, 0, 0, 0, 0, 30, 116, 120, 110, 46, 119, 114, 105, - 116, 101, 95, 100, 97, 116, 97, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 105, 110, 95, - 107, 101, 121, 16, 39, 0, 0, 0, 0, 0, 0, 30, 116, 120, 110, 46, 119, 114, 105, 116, - 101, 95, 100, 97, 116, 97, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 105, 110, 95, 118, - 97, 108, 16, 39, 0, 0, 0, 0, 0, 0, 16, 116, 120, 110, 46, 109, 101, 109, 111, 114, - 121, 95, 113, 117, 111, 116, 97, 128, 150, 152, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, - 102, 114, 101, 101, 95, 119, 114, 105, 116, 101, 95, 98, 121, 116, 101, 115, 95, 113, 117, 111, - 116, 97, 0, 4, 0, 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 102, 114, 101, 101, 95, - 101, 118, 101, 110, 116, 95, 98, 121, 116, 101, 115, 95, 113, 117, 111, 116, 97, 0, 4, 0, - 0, 0, 0, 0, 0, 26, 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, - 112, 101, 114, 95, 119, 114, 105, 116, 101, 95, 111, 112, 0, 0, 16, 0, 0, 0, 0, 0, - 43, 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, 97, 108, 108, 95, 119, - 114, 105, 116, 101, 95, 111, 112, 115, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, - 116, 105, 111, 110, 0, 0, 160, 0, 0, 0, 0, 0, 23, 116, 120, 110, 46, 109, 97, 120, - 95, 98, 121, 116, 101, 115, 95, 112, 101, 114, 95, 101, 118, 101, 110, 116, 0, 0, 16, 0, - 0, 0, 0, 0, 40, 116, 120, 110, 46, 109, 97, 120, 95, 98, 121, 116, 101, 115, 95, 97, - 108, 108, 95, 101, 118, 101, 110, 116, 115, 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, - 99, 116, 105, 111, 110, 0, 0, 160, 0, 0, 0, 0, 0, 37, 116, 120, 110, 46, 115, 116, - 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, 112, 101, 114, 95, 115, 116, 97, 116, 101, 95, - 115, 108, 111, 116, 95, 99, 114, 101, 97, 116, 101, 80, 195, 0, 0, 0, 0, 0, 0, 37, - 116, 120, 110, 46, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, 95, 112, 101, 114, 95, - 101, 120, 99, 101, 115, 115, 95, 115, 116, 97, 116, 101, 95, 98, 121, 116, 101, 50, 0, 0, - 0, 0, 0, 0, 0, 30, 116, 120, 110, 46, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, - 101, 95, 112, 101, 114, 95, 101, 118, 101, 110, 116, 95, 98, 121, 116, 101, 20, 0, 0, 0, - 0, 0, 0, 0, 36, 116, 120, 110, 46, 115, 116, 111, 114, 97, 103, 101, 95, 102, 101, 101, - 95, 112, 101, 114, 95, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 98, 121, 116, - 101, 20, 0, 0, 0, 0, 0, 0, 0, 21, 116, 120, 110, 46, 109, 97, 120, 95, 101, 120, - 101, 99, 117, 116, 105, 111, 110, 95, 103, 97, 115, 0, 200, 23, 168, 4, 0, 0, 0, 14, - 116, 120, 110, 46, 109, 97, 120, 95, 105, 111, 95, 103, 97, 115, 0, 228, 11, 84, 2, 0, - 0, 0, 19, 116, 120, 110, 46, 109, 97, 120, 95, 115, 116, 111, 114, 97, 103, 101, 95, 102, - 101, 101, 0, 194, 235, 11, 0, 0, 0, 0, 44, 109, 111, 118, 101, 95, 115, 116, 100, 108, - 105, 98, 46, 98, 99, 115, 46, 116, 111, 95, 98, 121, 116, 101, 115, 46, 112, 101, 114, 95, - 98, 121, 116, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 200, 0, 0, 0, 0, - 0, 0, 0, 32, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 98, 99, 115, 46, - 116, 111, 95, 98, 121, 116, 101, 115, 46, 102, 97, 105, 108, 117, 114, 101, 32, 78, 0, 0, - 0, 0, 0, 0, 30, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 104, 97, 115, - 104, 46, 115, 104, 97, 50, 95, 50, 53, 54, 46, 98, 97, 115, 101, 96, 234, 0, 0, 0, - 0, 0, 0, 34, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 104, 97, 115, 104, - 46, 115, 104, 97, 50, 95, 50, 53, 54, 46, 112, 101, 114, 95, 98, 121, 116, 101, 232, 3, - 0, 0, 0, 0, 0, 0, 30, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 104, - 97, 115, 104, 46, 115, 104, 97, 51, 95, 50, 53, 54, 46, 98, 97, 115, 101, 128, 56, 1, - 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 104, 97, - 115, 104, 46, 115, 104, 97, 51, 95, 50, 53, 54, 46, 112, 101, 114, 95, 98, 121, 116, 101, - 132, 3, 0, 0, 0, 0, 0, 0, 38, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, - 46, 115, 105, 103, 110, 101, 114, 46, 98, 111, 114, 114, 111, 119, 95, 97, 100, 100, 114, 101, - 115, 115, 46, 98, 97, 115, 101, 160, 15, 0, 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, - 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 99, 104, 101, 99, 107, - 95, 117, 116, 102, 56, 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 38, 109, - 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 99, 104, - 101, 99, 107, 95, 117, 116, 102, 56, 46, 112, 101, 114, 95, 98, 121, 116, 101, 160, 0, 0, - 0, 0, 0, 0, 0, 40, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, - 114, 105, 110, 103, 46, 105, 115, 95, 99, 104, 97, 114, 95, 98, 111, 117, 110, 100, 97, 114, - 121, 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 34, 109, 111, 118, 101, 95, - 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 115, 117, 98, 95, 115, 116, - 114, 105, 110, 103, 46, 98, 97, 115, 101, 64, 31, 0, 0, 0, 0, 0, 0, 38, 109, 111, - 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 115, 117, 98, - 95, 115, 116, 114, 105, 110, 103, 46, 112, 101, 114, 95, 98, 121, 116, 101, 60, 0, 0, 0, - 0, 0, 0, 0, 32, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, - 105, 110, 103, 46, 105, 110, 100, 101, 120, 95, 111, 102, 46, 98, 97, 115, 101, 64, 31, 0, - 0, 0, 0, 0, 0, 44, 109, 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, - 114, 105, 110, 103, 46, 105, 110, 100, 101, 120, 95, 111, 102, 46, 112, 101, 114, 95, 98, 121, - 116, 101, 95, 112, 97, 116, 116, 101, 114, 110, 144, 1, 0, 0, 0, 0, 0, 0, 45, 109, - 111, 118, 101, 95, 115, 116, 100, 108, 105, 98, 46, 115, 116, 114, 105, 110, 103, 46, 105, 110, - 100, 101, 120, 95, 111, 102, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, 97, 114, - 99, 104, 101, 100, 200, 0, 0, 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 99, 99, 111, 117, 110, 116, 46, 99, 114, 101, - 97, 116, 101, 95, 97, 100, 100, 114, 101, 115, 115, 46, 98, 97, 115, 101, 112, 23, 0, 0, - 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 97, 99, 99, 111, 117, 110, 116, 46, 99, 114, 101, 97, 116, 101, 95, 115, 105, 103, 110, - 101, 114, 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 29, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, - 46, 98, 97, 115, 101, 184, 11, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, - 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, 100, 101, 115, 101, 114, 105, 97, 108, 105, 122, - 101, 160, 67, 33, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 112, - 117, 98, 107, 101, 121, 95, 97, 103, 103, 114, 101, 103, 97, 116, 101, 32, 72, 1, 0, 0, - 0, 0, 0, 50, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, - 115, 117, 98, 103, 114, 111, 117, 112, 95, 99, 104, 101, 99, 107, 64, 234, 112, 0, 0, 0, - 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, - 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, 100, 101, 115, 101, - 114, 105, 97, 108, 105, 122, 101, 192, 191, 67, 0, 0, 0, 0, 0, 42, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, - 46, 112, 101, 114, 95, 115, 105, 103, 95, 97, 103, 103, 114, 101, 103, 97, 116, 101, 40, 142, - 3, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, - 115, 117, 98, 103, 114, 111, 117, 112, 95, 99, 104, 101, 99, 107, 144, 136, 140, 0, 0, 0, - 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, - 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 115, 105, 103, 95, 118, 101, 114, 105, - 102, 121, 160, 106, 29, 10, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, - 112, 111, 112, 95, 118, 101, 114, 105, 102, 121, 128, 79, 71, 12, 0, 0, 0, 0, 36, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, - 51, 56, 49, 46, 112, 101, 114, 95, 112, 97, 105, 114, 105, 110, 103, 160, 171, 200, 4, 0, - 0, 0, 0, 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, 114, 95, 109, 115, 103, 95, 104, 97, 115, - 104, 105, 110, 103, 128, 248, 213, 1, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 98, 108, 115, 49, 50, 51, 56, 49, 46, 112, 101, - 114, 95, 98, 121, 116, 101, 95, 104, 97, 115, 104, 105, 110, 103, 232, 3, 0, 0, 0, 0, - 0, 0, 30, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, - 105, 103, 110, 97, 116, 117, 114, 101, 46, 98, 97, 115, 101, 184, 11, 0, 0, 0, 0, 0, - 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, - 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, 100, - 101, 115, 101, 114, 105, 97, 108, 105, 122, 101, 192, 152, 11, 0, 0, 0, 0, 0, 54, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, - 116, 117, 114, 101, 46, 112, 101, 114, 95, 112, 117, 98, 107, 101, 121, 95, 115, 109, 97, 108, - 108, 95, 111, 114, 100, 101, 114, 95, 99, 104, 101, 99, 107, 24, 240, 1, 0, 0, 0, 0, - 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, - 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 115, 105, 103, 95, 100, 101, 115, 101, - 114, 105, 97, 108, 105, 122, 101, 76, 29, 0, 0, 0, 0, 0, 0, 47, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, 116, 117, 114, - 101, 46, 112, 101, 114, 95, 115, 105, 103, 95, 115, 116, 114, 105, 99, 116, 95, 118, 101, 114, - 105, 102, 121, 96, 123, 81, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, - 114, 95, 109, 115, 103, 95, 104, 97, 115, 104, 105, 110, 103, 95, 98, 97, 115, 101, 32, 253, - 0, 0, 0, 0, 0, 0, 46, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 115, 105, 103, 110, 97, 116, 117, 114, 101, 46, 112, 101, 114, 95, 109, 115, 103, - 95, 98, 121, 116, 101, 95, 104, 97, 115, 104, 105, 110, 103, 176, 4, 0, 0, 0, 0, 0, - 0, 30, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 101, - 99, 112, 50, 53, 54, 107, 49, 46, 98, 97, 115, 101, 184, 11, 0, 0, 0, 0, 0, 0, - 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 115, 101, 99, - 112, 50, 53, 54, 107, 49, 46, 101, 99, 100, 115, 97, 95, 114, 101, 99, 111, 118, 101, 114, - 64, 85, 235, 1, 0, 0, 0, 0, 42, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 98, 97, - 115, 101, 112, 111, 105, 110, 116, 95, 109, 117, 108, 0, 16, 39, 0, 0, 0, 0, 0, 49, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, - 114, 101, 116, 116, 111, 50, 53, 53, 46, 98, 97, 115, 101, 112, 111, 105, 110, 116, 95, 100, - 111, 117, 98, 108, 101, 95, 109, 117, 108, 0, 71, 134, 0, 0, 0, 0, 0, 38, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, - 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 97, 100, 100, 204, 166, 0, 0, - 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, - 99, 111, 109, 112, 114, 101, 115, 115, 0, 53, 12, 0, 0, 0, 0, 0, 45, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, - 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 100, 101, 99, 111, 109, 112, 114, 101, - 115, 115, 16, 92, 12, 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, - 112, 111, 105, 110, 116, 95, 101, 113, 117, 97, 108, 115, 176, 179, 0, 0, 0, 0, 0, 0, - 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, - 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 102, 114, 111, 109, - 95, 54, 52, 95, 117, 110, 105, 102, 111, 114, 109, 95, 98, 121, 116, 101, 115, 48, 223, 24, - 0, 0, 0, 0, 0, 43, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, - 95, 105, 100, 101, 110, 116, 105, 116, 121, 184, 11, 0, 0, 0, 0, 0, 0, 38, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, - 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 109, 117, 108, 224, 188, 143, 0, - 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, - 110, 101, 103, 32, 28, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 112, 111, 105, 110, 116, 95, 115, 117, 98, 104, 166, 0, 0, 0, 0, 0, 0, 44, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, - 101, 116, 116, 111, 50, 53, 53, 46, 112, 111, 105, 110, 116, 95, 112, 97, 114, 115, 101, 95, - 97, 114, 103, 184, 11, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 115, 99, 97, 108, 97, 114, 95, 115, 104, 97, 53, 49, 50, 95, 112, 101, 114, 95, 98, - 121, 116, 101, 176, 4, 0, 0, 0, 0, 0, 0, 51, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 115, 99, 97, 108, 97, 114, 95, 115, 104, 97, 53, 49, 50, 95, 112, 101, 114, 95, 104, - 97, 115, 104, 32, 253, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 115, 99, 97, 108, 97, 114, 95, 97, 100, 100, 40, 60, 0, 0, 0, 0, 0, 0, 57, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, - 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 114, 101, 100, 117, - 99, 101, 100, 95, 102, 114, 111, 109, 95, 51, 50, 95, 98, 121, 116, 101, 115, 120, 55, 0, - 0, 0, 0, 0, 0, 57, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, - 114, 95, 117, 110, 105, 102, 111, 114, 109, 95, 102, 114, 111, 109, 95, 54, 52, 95, 98, 121, - 116, 101, 115, 68, 97, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, - 46, 115, 99, 97, 108, 97, 114, 95, 102, 114, 111, 109, 95, 117, 49, 50, 56, 172, 13, 0, - 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, - 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, - 114, 95, 102, 114, 111, 109, 95, 117, 54, 52, 172, 13, 0, 0, 0, 0, 0, 0, 42, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, - 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 105, 110, 118, 101, 114, - 116, 192, 145, 33, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, - 99, 97, 108, 97, 114, 95, 105, 115, 95, 99, 97, 110, 111, 110, 105, 99, 97, 108, 216, 89, - 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, - 97, 114, 95, 109, 117, 108, 52, 83, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, - 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 110, 101, 103, 164, 56, 0, 0, 0, 0, - 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 114, - 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, 115, 99, 97, 108, 97, 114, 95, 115, - 117, 98, 208, 82, 0, 0, 0, 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 114, 105, 115, 116, 114, 101, 116, 116, 111, 50, 53, 53, 46, - 115, 99, 97, 108, 97, 114, 95, 112, 97, 114, 115, 101, 95, 97, 114, 103, 184, 11, 0, 0, - 0, 0, 0, 0, 34, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 104, 97, 115, 104, 46, 115, 105, 112, 95, 104, 97, 115, 104, 46, 98, 97, 115, 101, 32, - 78, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, - 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 105, 112, 95, 104, 97, 115, 104, 46, 112, 101, - 114, 95, 98, 121, 116, 101, 144, 1, 0, 0, 0, 0, 0, 0, 35, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 107, 101, 99, 99, - 97, 107, 50, 53, 54, 46, 98, 97, 115, 101, 128, 56, 1, 0, 0, 0, 0, 0, 39, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, - 107, 101, 99, 99, 97, 107, 50, 53, 54, 46, 112, 101, 114, 95, 98, 121, 116, 101, 132, 3, - 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, 95, 111, 102, - 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 58, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, - 116, 121, 112, 101, 95, 111, 102, 46, 112, 101, 114, 95, 97, 98, 115, 116, 114, 97, 99, 116, - 95, 109, 101, 109, 111, 114, 121, 95, 117, 110, 105, 116, 100, 0, 0, 0, 0, 0, 0, 0, - 40, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, - 101, 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, 95, 110, 97, 109, 101, 46, 98, 97, 115, - 101, 112, 23, 0, 0, 0, 0, 0, 0, 60, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, 105, 110, 102, 111, 46, 116, 121, 112, 101, - 95, 110, 97, 109, 101, 46, 112, 101, 114, 95, 97, 98, 115, 116, 114, 97, 99, 116, 95, 109, - 101, 109, 111, 114, 121, 95, 117, 110, 105, 116, 100, 0, 0, 0, 0, 0, 0, 0, 39, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 116, 121, 112, 101, 95, - 105, 110, 102, 111, 46, 99, 104, 97, 105, 110, 95, 105, 100, 46, 98, 97, 115, 101, 184, 11, - 0, 0, 0, 0, 0, 0, 34, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 53, 49, 50, 46, 98, 97, 115, - 101, 168, 12, 0, 0, 0, 0, 0, 0, 38, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 104, 97, 50, 95, 53, 49, 50, 46, - 112, 101, 114, 95, 98, 121, 116, 101, 60, 0, 0, 0, 0, 0, 0, 0, 34, 97, 112, 116, - 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 115, 104, - 97, 51, 95, 53, 49, 50, 46, 98, 97, 115, 101, 148, 17, 0, 0, 0, 0, 0, 0, 38, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, - 46, 115, 104, 97, 51, 95, 53, 49, 50, 46, 112, 101, 114, 95, 98, 121, 116, 101, 50, 0, - 0, 0, 0, 0, 0, 0, 35, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 104, 97, 115, 104, 46, 114, 105, 112, 101, 109, 100, 49, 54, 48, 46, 98, 97, - 115, 101, 184, 11, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, 115, 95, 102, 114, 97, - 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, 114, 105, 112, 101, 109, 100, 49, 54, - 48, 46, 112, 101, 114, 95, 98, 121, 116, 101, 50, 0, 0, 0, 0, 0, 0, 0, 37, 97, - 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 104, 97, 115, 104, 46, - 98, 108, 97, 107, 101, 50, 98, 95, 50, 53, 54, 46, 98, 97, 115, 101, 214, 6, 0, 0, - 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, - 46, 104, 97, 115, 104, 46, 98, 108, 97, 107, 101, 50, 98, 95, 50, 53, 54, 46, 112, 101, - 114, 95, 98, 121, 116, 101, 15, 0, 0, 0, 0, 0, 0, 0, 36, 97, 112, 116, 111, 115, - 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 117, 116, 105, 108, 46, 102, 114, 111, 109, - 95, 98, 121, 116, 101, 115, 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 40, - 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 117, 116, 105, 108, - 46, 102, 114, 111, 109, 95, 98, 121, 116, 101, 115, 46, 112, 101, 114, 95, 98, 121, 116, 101, - 100, 0, 0, 0, 0, 0, 0, 0, 56, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, - 119, 111, 114, 107, 46, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 95, 99, 111, 110, - 116, 101, 120, 116, 46, 103, 101, 116, 95, 115, 99, 114, 105, 112, 116, 95, 104, 97, 115, 104, - 46, 98, 97, 115, 101, 160, 15, 0, 0, 0, 0, 0, 0, 41, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 99, 111, 100, 101, 46, 114, 101, 113, 117, 101, - 115, 116, 95, 112, 117, 98, 108, 105, 115, 104, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, - 0, 0, 0, 45, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 99, 111, 100, 101, 46, 114, 101, 113, 117, 101, 115, 116, 95, 112, 117, 98, 108, 105, 115, 104, - 46, 112, 101, 114, 95, 98, 121, 116, 101, 40, 0, 0, 0, 0, 0, 0, 0, 47, 97, 112, - 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 101, 118, 101, 110, 116, 46, - 119, 114, 105, 116, 101, 95, 116, 111, 95, 101, 118, 101, 110, 116, 95, 115, 116, 111, 114, 101, - 46, 98, 97, 115, 101, 224, 147, 4, 0, 0, 0, 0, 0, 67, 97, 112, 116, 111, 115, 95, - 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 101, 118, 101, 110, 116, 46, 119, 114, 105, 116, - 101, 95, 116, 111, 95, 101, 118, 101, 110, 116, 95, 115, 116, 111, 114, 101, 46, 112, 101, 114, - 95, 97, 98, 115, 116, 114, 97, 99, 116, 95, 109, 101, 109, 111, 114, 121, 95, 117, 110, 105, - 116, 136, 19, 0, 0, 0, 0, 0, 0, 44, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 115, 116, 97, 116, 101, 95, 115, 116, 111, 114, 97, 103, 101, 46, - 103, 101, 116, 95, 117, 115, 97, 103, 101, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, 0, - 0, 0, 35, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, - 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 97, 100, 100, 46, 98, 97, 115, 101, 112, 23, - 0, 0, 0, 0, 0, 0, 36, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 114, 101, 97, 100, 46, 98, - 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 35, 97, 112, 116, 111, 115, 95, 102, 114, - 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 115, - 117, 98, 46, 98, 97, 115, 101, 112, 23, 0, 0, 0, 0, 0, 0, 39, 97, 112, 116, 111, - 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, 97, 103, 103, 114, 101, 103, 97, 116, - 111, 114, 46, 100, 101, 115, 116, 114, 111, 121, 46, 98, 97, 115, 101, 16, 39, 0, 0, 0, - 0, 0, 0, 54, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, 114, 107, 46, - 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 102, 97, 99, 116, 111, 114, 121, 46, 110, - 101, 119, 95, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 46, 98, 97, 115, 101, 16, 39, - 0, 0, 0, 0, 0, 0, 37, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, 101, 119, 111, - 114, 107, 46, 111, 98, 106, 101, 99, 116, 46, 101, 120, 105, 115, 116, 115, 95, 97, 116, 46, - 98, 97, 115, 101, 136, 19, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, - 114, 97, 109, 101, 119, 111, 114, 107, 46, 111, 98, 106, 101, 99, 116, 46, 101, 120, 105, 115, - 116, 115, 95, 97, 116, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 108, 111, 97, 100, 101, - 100, 232, 3, 0, 0, 0, 0, 0, 0, 48, 97, 112, 116, 111, 115, 95, 102, 114, 97, 109, - 101, 119, 111, 114, 107, 46, 111, 98, 106, 101, 99, 116, 46, 101, 120, 105, 115, 116, 115, 95, - 97, 116, 46, 112, 101, 114, 95, 105, 116, 101, 109, 95, 108, 111, 97, 100, 101, 100, 64, 31, - 0, 0, 0, 0, 0, 0, 22, 116, 97, 98, 108, 101, 46, 99, 111, 109, 109, 111, 110, 46, - 108, 111, 97, 100, 46, 98, 97, 115, 101, 64, 31, 0, 0, 0, 0, 0, 0, 26, 116, 97, - 98, 108, 101, 46, 99, 111, 109, 109, 111, 110, 46, 108, 111, 97, 100, 46, 98, 97, 115, 101, - 95, 110, 101, 119, 64, 31, 0, 0, 0, 0, 0, 0, 26, 116, 97, 98, 108, 101, 46, 99, - 111, 109, 109, 111, 110, 46, 108, 111, 97, 100, 46, 112, 101, 114, 95, 98, 121, 116, 101, 232, - 3, 0, 0, 0, 0, 0, 0, 25, 116, 97, 98, 108, 101, 46, 99, 111, 109, 109, 111, 110, - 46, 108, 111, 97, 100, 46, 102, 97, 105, 108, 117, 114, 101, 0, 0, 0, 0, 0, 0, 0, - 0, 27, 116, 97, 98, 108, 101, 46, 110, 101, 119, 95, 116, 97, 98, 108, 101, 95, 104, 97, - 110, 100, 108, 101, 46, 98, 97, 115, 101, 32, 78, 0, 0, 0, 0, 0, 0, 18, 116, 97, - 98, 108, 101, 46, 97, 100, 100, 95, 98, 111, 120, 46, 98, 97, 115, 101, 192, 93, 0, 0, - 0, 0, 0, 0, 33, 116, 97, 98, 108, 101, 46, 97, 100, 100, 95, 98, 111, 120, 46, 112, - 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 200, 0, - 0, 0, 0, 0, 0, 0, 21, 116, 97, 98, 108, 101, 46, 98, 111, 114, 114, 111, 119, 95, - 98, 111, 120, 46, 98, 97, 115, 101, 192, 93, 0, 0, 0, 0, 0, 0, 36, 116, 97, 98, - 108, 101, 46, 98, 111, 114, 114, 111, 119, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, - 116, 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 200, 0, 0, 0, 0, 0, 0, - 0, 23, 116, 97, 98, 108, 101, 46, 99, 111, 110, 116, 97, 105, 110, 115, 95, 98, 111, 120, - 46, 98, 97, 115, 101, 192, 93, 0, 0, 0, 0, 0, 0, 38, 116, 97, 98, 108, 101, 46, - 99, 111, 110, 116, 97, 105, 110, 115, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, 116, - 101, 95, 115, 101, 114, 105, 97, 108, 105, 122, 101, 100, 200, 0, 0, 0, 0, 0, 0, 0, - 21, 116, 97, 98, 108, 101, 46, 114, 101, 109, 111, 118, 101, 95, 98, 111, 120, 46, 98, 97, - 115, 101, 192, 93, 0, 0, 0, 0, 0, 0, 36, 116, 97, 98, 108, 101, 46, 114, 101, 109, - 111, 118, 101, 95, 98, 111, 120, 46, 112, 101, 114, 95, 98, 121, 116, 101, 95, 115, 101, 114, - 105, 97, 108, 105, 122, 101, 100, 200, 0, 0, 0, 0, 0, 0, 0, 28, 116, 97, 98, 108, - 101, 46, 100, 101, 115, 116, 114, 111, 121, 95, 101, 109, 112, 116, 121, 95, 98, 111, 120, 46, - 98, 97, 115, 101, 192, 93, 0, 0, 0, 0, 0, 0, 29, 116, 97, 98, 108, 101, 46, 100, - 114, 111, 112, 95, 117, 110, 99, 104, 101, 99, 107, 101, 100, 95, 98, 111, 120, 46, 98, 97, - 115, 101, 208, 7, 0, 0, 0, 0, 0, 0, 15, 109, 105, 115, 99, 46, 97, 98, 115, 95, - 118, 97, 108, 46, 117, 56, 40, 0, 0, 0, 0, 0, 0, 0, 16, 109, 105, 115, 99, 46, - 97, 98, 115, 95, 118, 97, 108, 46, 117, 49, 54, 40, 0, 0, 0, 0, 0, 0, 0, 16, - 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 51, 50, 40, 0, 0, 0, - 0, 0, 0, 0, 16, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 54, - 52, 40, 0, 0, 0, 0, 0, 0, 0, 17, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, - 97, 108, 46, 117, 49, 50, 56, 40, 0, 0, 0, 0, 0, 0, 0, 17, 109, 105, 115, 99, - 46, 97, 98, 115, 95, 118, 97, 108, 46, 117, 50, 53, 54, 40, 0, 0, 0, 0, 0, 0, - 0, 17, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 98, 111, 111, 108, 40, - 0, 0, 0, 0, 0, 0, 0, 20, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, - 46, 97, 100, 100, 114, 101, 115, 115, 40, 0, 0, 0, 0, 0, 0, 0, 19, 109, 105, 115, - 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 115, 116, 114, 117, 99, 116, 40, 0, 0, 0, - 0, 0, 0, 0, 19, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 118, 101, - 99, 116, 111, 114, 40, 0, 0, 0, 0, 0, 0, 0, 22, 109, 105, 115, 99, 46, 97, 98, - 115, 95, 118, 97, 108, 46, 114, 101, 102, 101, 114, 101, 110, 99, 101, 40, 0, 0, 0, 0, - 0, 0, 0, 26, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, - 95, 117, 56, 95, 112, 97, 99, 107, 101, 100, 1, 0, 0, 0, 0, 0, 0, 0, 27, 109, - 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 117, 49, 54, 95, - 112, 97, 99, 107, 101, 100, 2, 0, 0, 0, 0, 0, 0, 0, 27, 109, 105, 115, 99, 46, - 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 117, 51, 50, 95, 112, 97, 99, 107, - 101, 100, 4, 0, 0, 0, 0, 0, 0, 0, 27, 109, 105, 115, 99, 46, 97, 98, 115, 95, - 118, 97, 108, 46, 112, 101, 114, 95, 117, 54, 52, 95, 112, 97, 99, 107, 101, 100, 8, 0, - 0, 0, 0, 0, 0, 0, 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, - 112, 101, 114, 95, 117, 49, 50, 56, 95, 112, 97, 99, 107, 101, 100, 16, 0, 0, 0, 0, - 0, 0, 0, 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, - 95, 117, 50, 53, 54, 95, 112, 97, 99, 107, 101, 100, 32, 0, 0, 0, 0, 0, 0, 0, - 28, 109, 105, 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 98, 111, - 111, 108, 95, 112, 97, 99, 107, 101, 100, 1, 0, 0, 0, 0, 0, 0, 0, 31, 109, 105, - 115, 99, 46, 97, 98, 115, 95, 118, 97, 108, 46, 112, 101, 114, 95, 97, 100, 100, 114, 101, - 115, 115, 95, 112, 97, 99, 107, 101, 100, 32, 0, 0, 0, 0, 0, 0, 0, - ]; - - gas_schedule::set_gas_schedule(&framework_signer, gas_schedule_blob); - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/5-features.move b/aptos-move/aptos-release-builder/data/example_output/5-features.move deleted file mode 100644 index 79525b4bcc608..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/5-features.move +++ /dev/null @@ -1,26 +0,0 @@ -// Script hash: 12e7b871 -// Modifying on-chain feature flags: -// Enabled Features: [CodeDependencyCheck, TreatFriendAsPrivate, Sha512AndRipeMd160Natives, AptosStdChainIdNatives, VMBinaryFormatV6, MultiEd25519PkValidateV2Natives, Blake2b256Native, ResourceGroups, MultisigAccounts, DelegationPools, Ed25519PubkeyValidateReturnFalseWrongLength] -// Disabled Features: [] -// -script { - use aptos_framework::aptos_governance; - use std::features; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000001, - vector[233u8,115u8,222u8,109u8,33u8,95u8,157u8,37u8,189u8,240u8,180u8,14u8,191u8,215u8,233u8,110u8,223u8,235u8,97u8,190u8,166u8,210u8,218u8,4u8,185u8,212u8,159u8,97u8,53u8,157u8,198u8,168u8,], - ); - let enabled_blob: vector = vector[ - 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, - ]; - - let disabled_blob: vector = vector[ - - ]; - - features::change_feature_flags(&framework_signer, enabled_blob, disabled_blob); - } -} diff --git a/aptos-move/aptos-release-builder/data/example_output/6-consensus-config.move b/aptos-move/aptos-release-builder/data/example_output/6-consensus-config.move deleted file mode 100644 index 2acdcf4b277a1..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_output/6-consensus-config.move +++ /dev/null @@ -1,44 +0,0 @@ -// Script hash: e973de6d -// Consensus config upgrade proposal - -// config: V1( -// ConsensusConfigV1 { -// decoupled_execution: true, -// back_pressure_limit: 10, -// exclude_round: 40, -// proposer_election_type: LeaderReputation( -// ProposerAndVoterV2( -// ProposerAndVoterConfig { -// active_weight: 1000, -// inactive_weight: 10, -// failed_weight: 1, -// failure_threshold_percent: 10, -// proposer_window_num_validators_multiplier: 10, -// voter_window_num_validators_multiplier: 1, -// weight_by_voting_power: true, -// use_history_from_previous_epoch_max_count: 5, -// }, -// ), -// ), -// max_failed_authors_to_store: 10, -// }, -// ) - -script { - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - use std::vector; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal(proposal_id, @0000000000000000000000000000000000000000000000000000000000000001, vector::empty()); - - let consensus_blob: vector = vector[ - 0, 1, 10, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 2, 1, - 232, 3, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 5, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, - ]; - - consensus_config::set(&framework_signer, consensus_blob); - } -} diff --git a/aptos-move/aptos-release-builder/data/example_proposals/empty.move b/aptos-move/aptos-release-builder/data/example_proposals/empty.move deleted file mode 100644 index a0ba850c6cd06..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_proposals/empty.move +++ /dev/null @@ -1,9 +0,0 @@ -// Empty governance proposal to demonstrate functionality for including proposal in the release builder; -// -script { - use aptos_framework::aptos_governance; - - fun main(proposal_id: u64) { - let _framework_signer = aptos_governance::resolve(proposal_id, @0x1); - } -} diff --git a/aptos-move/aptos-release-builder/data/example_proposals/empty_multi_step.move b/aptos-move/aptos-release-builder/data/example_proposals/empty_multi_step.move deleted file mode 100644 index 4ae4b2d34ae6e..0000000000000 --- a/aptos-move/aptos-release-builder/data/example_proposals/empty_multi_step.move +++ /dev/null @@ -1,14 +0,0 @@ -// Empty governance proposal to demonstrate functionality for including proposal in the release builder; -// -script { - use aptos_framework::aptos_governance; - use std::features; - - fun main(proposal_id: u64) { - let _framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000001, - {{ script_hash }}, - ); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/aip_28_initialization.move b/aptos-move/aptos-release-builder/data/proposals/aip_28_initialization.move deleted file mode 100644 index b7020a9ec021f..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/aip_28_initialization.move +++ /dev/null @@ -1,14 +0,0 @@ -// Initialize AIP-28 parital governance voting. -// This script MUST be run before enabling the feature flag, otherwise no new proposal can be passed anymore. -script { - use aptos_framework::aptos_governance; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0000000000000000000000000000000000000000000000000000000000000001, - {{ script_hash }}, - ); - aptos_governance::initialize_partial_voting(&framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/aip_32_initialization.move b/aptos-move/aptos-release-builder/data/proposals/aip_32_initialization.move deleted file mode 100644 index b1c93c0fde8f2..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/aip_32_initialization.move +++ /dev/null @@ -1,15 +0,0 @@ -// Initialize AIP-28 parital governance voting. -// This script MUST be run before enabling the feature flag, otherwise emitting the fee statement will fail. -script { - use aptos_framework::aptos_governance; - use aptos_framework::transaction_fee; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - transaction_fee::initialize_storage_refund(&framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/aip_67_initialization.move b/aptos-move/aptos-release-builder/data/proposals/aip_67_initialization.move deleted file mode 100644 index 7412defb884de..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/aip_67_initialization.move +++ /dev/null @@ -1,14 +0,0 @@ -// Initialize AIP-67 parital governance voting. -script { - use aptos_framework::aptos_governance; - use aptos_framework::jwks; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - jwks::initialize(&framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/enable_randomness.move b/aptos-move/aptos-release-builder/data/proposals/enable_randomness.move deleted file mode 100644 index 7d479eac6deb4..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/enable_randomness.move +++ /dev/null @@ -1,21 +0,0 @@ -// Enable on-chain randomness. -script { - use aptos_framework::aptos_governance; - use aptos_framework::randomness_config; - use aptos_std::fixed_point64; - - fun main(proposal_id: u64) { - let framework = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - - let config = randomness_config::new_v1( - fixed_point64::create_from_rational(1, 2), // secrecy_threshold: 1/2 - fixed_point64::create_from_rational(2, 3), // reconstruct_threshold: 2/3 - ); - randomness_config::set_for_next_epoch(&framework, config); - aptos_governance::reconfigure(&framework); // The resulting epoch does not have randomness. The one after it does. - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/jwk_consensus_config_migration.move b/aptos-move/aptos-release-builder/data/proposals/jwk_consensus_config_migration.move deleted file mode 100644 index 2896607252d34..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/jwk_consensus_config_migration.move +++ /dev/null @@ -1,23 +0,0 @@ -// Initialize `aptos_framework::jwk_consensus_config::JWKConsensusConfig` with Google. -// Start to ignore `aptos_framework::jwks::SupportedOIDCProviders`. -// Start to ignore move feature flag `std::features::JWK_CONSENSUS`. -script { - use aptos_framework::aptos_governance; - use aptos_framework::jwk_consensus_config; - use std::string::utf8; - - fun main(proposal_id: u64) { - let framework = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - let provider_google = jwk_consensus_config::new_oidc_provider( - utf8(b"https://accounts.google.com"), - utf8(b"https://accounts.google.com/.well-known/openid-configuration"), - ); - let config = jwk_consensus_config::new_v1(vector[provider_google]); - jwk_consensus_config::initialize(&framework, config); - aptos_governance::reconfigure(&framework); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/randomness_framework_initialization.move b/aptos-move/aptos-release-builder/data/proposals/randomness_framework_initialization.move deleted file mode 100644 index 2fed9f0868beb..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/randomness_framework_initialization.move +++ /dev/null @@ -1,24 +0,0 @@ -// Initialize on-chain randomness resources. -script { - use aptos_framework::aptos_governance; - use aptos_framework::config_buffer; - use aptos_framework::dkg; - use aptos_framework::randomness; - use aptos_framework::randomness_config; - use aptos_framework::reconfiguration_state; - - fun main(proposal_id: u64) { - let framework = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - config_buffer::initialize(&framework); // on-chain config buffer - dkg::initialize(&framework); // DKG state holder - reconfiguration_state::initialize(&framework); // reconfiguration in progress global indicator - randomness::initialize(&framework); // randomness seed holder - - let config = randomness_config::new_off(); - randomness_config::initialize(&framework, config); - } -} diff --git a/aptos-move/aptos-release-builder/data/proposals/start_jwk_consensus_for_google.move b/aptos-move/aptos-release-builder/data/proposals/start_jwk_consensus_for_google.move deleted file mode 100644 index 739dffcaf2c3b..0000000000000 --- a/aptos-move/aptos-release-builder/data/proposals/start_jwk_consensus_for_google.move +++ /dev/null @@ -1,20 +0,0 @@ -// Start JWK Consensus for Google. -script { - use aptos_framework::aptos_governance; - use aptos_framework::jwks; - - fun main(proposal_id: u64) { - let framework_signer = aptos_governance::resolve_multi_step_proposal( - proposal_id, - @0x1, - {{ script_hash }}, - ); - - jwks::upsert_oidc_provider_for_next_epoch( - &framework_signer, - b"https://accounts.google.com", - b"https://accounts.google.com/.well-known/openid-configuration" - ); - aptos_governance::reconfigure(&framework_signer); - } -} diff --git a/aptos-move/aptos-release-builder/data/release.yaml b/aptos-move/aptos-release-builder/data/release.yaml deleted file mode 100644 index 4e549912932a1..0000000000000 --- a/aptos-move/aptos-release-builder/data/release.yaml +++ /dev/null @@ -1,201 +0,0 @@ ---- -remote_endpoint: ~ -name: "v1.10-plus" -proposals: - - name: step_1_increase_max_txn_gas - metadata: - title: "Increase max txn gas temporarily for framework upgrade" - description: "Increase max txn gas temporarily for framework upgrade" - execution_mode: MultiStep - update_sequence: - - DefaultGasWithOverrideOld: # This is translated to `gas_schedule::set_gas_schedule(...)`. - - name: "txn.max_execution_gas" - value: 3676000000 - - name: step_2_upgrade_framework - metadata: - title: "Multi-step proposal to upgrade mainnet framework to v1.10-plus" - description: "This includes changes in https://github.com/aptos-labs/aptos-core/commits/aptos-release-v1.10 plus some others in main." - execution_mode: MultiStep - update_sequence: - - Framework: - bytecode_version: 6 - git_hash: ~ - - RawScript: aptos-move/aptos-release-builder/data/proposals/randomness_framework_initialization.move # Keep this in 1.11 - - DefaultGas # [probably 1.11] This is translated to `gas_schedule::set_for_next_epoch(...)`. - - name: step_3_storage_fee_for_state_bytes_refundable - metadata: - title: "AIP-65: Storage Fee for State Bytes refundable" - description: "AIP-65: This refunds storage fee charged according to the size of a state slot and stop penalizing large state items by charging non-refundable storage fee each time it is updated." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/328" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: # This and the following `FeatureFlag` entries are translated to `features::change_feature_flags_for_next_epoch(...)`. - enabled: - - refundable_bytes - - name: step_4_enable_fairness_shuffler - metadata: - title: "AIP-68: Reordering transactions in a block for fairness" - description: "AIP-68: This AIP proposes to update the Transaction Shuffler logic to add other aspects of fairness to how transactions are ordered in the block." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/333" - execution_mode: MultiStep - update_sequence: - - Execution: - V4: - transaction_shuffler_type: - fairness: - sender_conflict_window_size: 32 - module_conflict_window_size: 2 - entry_fun_conflict_window_size: 3 - block_gas_limit_type: - complex_limit_v1: - effective_block_gas_limit: 20000 - execution_gas_effective_multiplier: 1 - io_gas_effective_multiplier: 1 - conflict_penalty_window: 6 - use_granular_resource_group_conflicts: false - use_module_publishing_block_conflict: true - block_output_limit: 3145728 - include_user_txn_size_in_block_output: true - add_block_limit_outcome_onchain: false - transaction_deduper_type: txn_hash_and_authenticator_v1 - - name: step_5_passkey_authenticator - metadata: - title: "AIP-66: Passkey Accounts" - description: "AIP-66: The first WebAuthn Authenticator for Aptos, enabling users to utilize passkeys and other WebAuthn credentials for transaction authentication." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/322" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - web_authn_signature - - name: step_6_object_code_deployment - metadata: - title: "AIP-54: Object Code Deployment" - description: "AIP-54: Introduces a method to deploy code to objects, allowing more modular and resource efficient development." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/259" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - object_code_deployment - - name: step_7_enable_aggregator_v2_api - metadata: - title: "AIP-47: Enable Aggregator v2 API" - description: "AIP-47: This AIP revamps and expands upon the current concept of Aggregators. Enabling their usage for control flow, and storing their values elsewhere. This step only enables new APIs." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/226" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - aggregator_v2_api - - name: step_8_enable_concurrent_digital_assets - metadata: - title: "AIP-43: Enable Concurrent Digital Assets (Token V2)" - description: "AIP-43: This AIP proposes a solution to speedup minting and burning of Digital Assets (Token v2), by parallelizing them" - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/209" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - concurrent_token_v2 - - name: step_9_enable_aggregator_v2_parallelism - metadata: - title: "AIP-47: Enable Aggregator v2 parallelism" - description: "AIP-47: This step enables the backend that makes execution of Aggregators V2 parallel." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/226" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - aggregator_v2_delayed_fields - - resource_groups_split_in_vm_change_set - - name: step_10_enable_validator_txn - metadata: - title: "AIP-64: Validator Transaction Type" - description: "AIP-64: This AIP introduces a new transaction type for validators to perform on-chain operations." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/327" - execution_mode: MultiStep - update_sequence: - - Consensus: - V3: - alg: - Jolteon: - main: - decoupled_execution: true - back_pressure_limit: 10 - exclude_round: 40 - proposer_election_type: - leader_reputation: - proposer_and_voter_v2: - active_weight: 1000 - inactive_weight: 10 - failed_weight: 1 - failure_threshold_percent: 10 - proposer_window_num_validators_multiplier: 10 - voter_window_num_validators_multiplier: 1 - weight_by_voting_power: true - use_history_from_previous_epoch_max_count: 5 - max_failed_authors_to_store: 10 - quorum_store_enabled: true - vtxn: - V1: - per_block_limit_txn_count: 2 - per_block_limit_total_bytes: 2097152 - - name: step_11_enable_jwk_consensus - metadata: - title: "AIP-67: Native Consensus for JSON Web Key (JWK)" - description: "AIP-67: This AIP proposes a solution for validators to agree on OpenID Connect providers' JSON Web Keys." - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/331" - execution_mode: MultiStep - update_sequence: - - RawScript: aptos-move/aptos-release-builder/data/proposals/aip_67_initialization.move - - FeatureFlag: - enabled: - - jwk_consensus - - name: step_12_enable_keyless_accounts - metadata: - title: "AIP-61: Keyless accounts" - description: "Enable keyless blockchain accounts, allowing users to transact directly using their Web2 accounts instead of managing a secret key" - discussion_url: "https://github.com/aptos-foundation/AIPs/issues/297" - execution_mode: MultiStep - update_sequence: - - FeatureFlag: - enabled: - - keyless_accounts - - name: step_13_start_watching_google_jwks - metadata: - title: "Start JWK consensus for Google" - description: "JWK Consensus (AIP-67) for Google enables Google-based keyless accounts (AIP-61)." - execution_mode: MultiStep - update_sequence: - - RawScript: aptos-move/aptos-release-builder/data/proposals/start_jwk_consensus_for_google.move - - name: step_14_migrate_jwk_consensus_config - metadata: - title: "[probably 1.11] Migrate JWK consensus config" - description: "Move JWK consensus on-off control and the supported OIDC provider list to its own resource." - execution_mode: MultiStep - update_sequence: - - RawScript: aptos-move/aptos-release-builder/data/proposals/jwk_consensus_config_migration.move - - name: step_15_enable_randomness # Keep this the last step, so the above steps use fast reconfig and we save time. - metadata: - title: "[probably 1.11] Enable randomness" - description: "Move JWK consensus on-off control and the supported OIDC provider list to its own resource." - execution_mode: MultiStep - update_sequence: - - Randomness: # Generated script should be like: aptos-move/aptos-release-builder/data/proposals/enable_randomness.move - V1: - secrecy_threshold_in_percentage: 50 - reconstruct_threshold_in_percentage: 66 - - name: step_16_update_oidc_providers_test_only # - metadata: - title: "test only step" - description: "test only step" - execution_mode: MultiStep - update_sequence: - - JwkConsensus: - V1: - oidc_providers: - - name: "https://www.facebook.com" - config_url: "https://www.facebook.com/.well-known/openid-configuration" - - name: "https://accounts.google.com" - config_url: "https://accounts.google.com/.well-known/openid-configuration" diff --git a/aptos-move/aptos-release-builder/src/components/consensus_config.rs b/aptos-move/aptos-release-builder/src/components/consensus_config.rs deleted file mode 100644 index a1d449e450acd..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/consensus_config.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{components::get_signer_arg, utils::*}; -use anyhow::Result; -use aptos_types::on_chain_config::OnChainConsensusConfig; -use move_model::{code_writer::CodeWriter, emit, emitln, model::Loc}; - -pub fn generate_consensus_upgrade_proposal( - consensus_config: &OnChainConsensusConfig, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - emitln!(writer, "// Consensus config upgrade proposal\n"); - let config_comment = format!("// config: {:#?}", consensus_config).replace('\n', "\n// "); - emitln!(writer, "{}\n", config_comment); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::consensus_config"], - |writer| { - let consensus_config_blob = bcs::to_bytes(consensus_config).unwrap(); - assert!(consensus_config_blob.len() < 65536); - - emit!(writer, "let consensus_blob: vector = "); - generate_blob(writer, &consensus_config_blob); - emitln!(writer, ";\n"); - - emitln!( - writer, - "consensus_config::set_for_next_epoch({}, consensus_blob);", - signer_arg - ); - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("consensus-config".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/execution_config.rs b/aptos-move/aptos-release-builder/src/components/execution_config.rs deleted file mode 100644 index b899b83c206d2..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/execution_config.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{components::get_signer_arg, utils::*}; -use anyhow::Result; -use aptos_types::on_chain_config::OnChainExecutionConfig; -use move_model::{code_writer::CodeWriter, emit, emitln, model::Loc}; - -pub fn generate_execution_config_upgrade_proposal( - execution_config: &OnChainExecutionConfig, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - emitln!(writer, "// Execution config upgrade proposal\n"); - let config_comment = format!("// config: {:#?}", execution_config).replace('\n', "\n// "); - emitln!(writer, "{}\n", config_comment); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::execution_config"], - |writer| { - let execution_config_blob = bcs::to_bytes(execution_config).unwrap(); - assert!(execution_config_blob.len() < 65536); - - emit!(writer, "let execution_blob: vector = "); - generate_blob(writer, &execution_config_blob); - emitln!(writer, ";\n"); - - emitln!( - writer, - "execution_config::set_for_next_epoch({}, execution_blob);", - signer_arg - ); - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("execution-config".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/feature_flags.rs b/aptos-move/aptos-release-builder/src/components/feature_flags.rs deleted file mode 100644 index 2ba3ee278482f..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/feature_flags.rs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{components::get_signer_arg, utils::*}; -use anyhow::Result; -use aptos_types::on_chain_config::{FeatureFlag as AptosFeatureFlag, Features as AptosFeatures}; -use move_model::{code_writer::CodeWriter, emit, emitln, model::Loc}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; - -#[derive(Clone, Deserialize, PartialEq, Eq, Serialize, Debug)] -pub struct Features { - #[serde(default)] - pub enabled: Vec, - #[serde(default)] - pub disabled: Vec, -} - -impl Features { - pub fn empty() -> Self { - Self { - enabled: vec![], - disabled: vec![], - } - } - - pub fn squash(&mut self, rhs: Self) { - let mut enabled: HashSet<_> = self.enabled.iter().cloned().collect(); - let mut disabled: HashSet<_> = self.disabled.iter().cloned().collect(); - let to_enable: HashSet<_> = rhs.enabled.into_iter().collect(); - let to_disable: HashSet<_> = rhs.disabled.into_iter().collect(); - - disabled = disabled.difference(&to_enable).cloned().collect(); - enabled.extend(to_enable); - - enabled = enabled.difference(&to_disable).cloned().collect(); - disabled.extend(to_disable); - - self.enabled = enabled.into_iter().collect(); - self.disabled = disabled.into_iter().collect(); - } - - pub fn is_empty(&self) -> bool { - self.enabled.is_empty() && self.disabled.is_empty() - } -} - -#[derive(Clone, Debug, Deserialize, EnumIter, PartialEq, Eq, Serialize, Hash)] -#[allow(non_camel_case_types)] -#[serde(rename_all = "snake_case")] -pub enum FeatureFlag { - CodeDependencyCheck, - CollectAndDistributeGasFees, - TreatFriendAsPrivate, - Sha512AndRipeMd160Natives, - AptosStdChainIdNatives, - VMBinaryFormatV6, - MultiEd25519PkValidateV2Natives, - Blake2b256Native, - ResourceGroups, - MultisigAccounts, - DelegationPools, - CryptographyAlgebraNatives, - Bls12381Structures, - Ed25519PubkeyValidateReturnFalseWrongLength, - StructConstructors, - PeriodicalRewardRateReduction, - PartialGovernanceVoting, - SignatureCheckerV2, - StorageSlotMetadata, - ChargeInvariantViolation, - DelegationPoolPartialGovernanceVoting, - GasPayerEnabled, - AptosUniqueIdentifiers, - BulletproofsNatives, - SignerNativeFormatFix, - ModuleEvent, - EmitFeeStatement, - StorageDeletionRefund, - AggregatorV2Api, - SignatureCheckerV2ScriptFix, - SaferResourceGroups, - SaferMetadata, - SingleSenderAuthenticator, - SponsoredAutomaticAccountCreation, - FeePayerAccountOptional, - AggregatorV2DelayedFields, - ConcurrentTokenV2, - LimitMaxIdentifierLength, - OperatorBeneficiaryChange, - VMBinaryFormatV7, - ResourceGroupsSplitInVmChangeSet, - CommissionChangeDelegationPool, - Bn254Structures, - WebAuthnSignature, - ReconfigureWithDkg, - KeylessAccounts, - KeylessButZklessAccounts, - RemoveDetailedError, - JwkConsensus, - ConcurrentFungibleAssets, - RefundableBytes, - ObjectCodeDeployment, - MaxObjectNestingCheck, - KeylessAccountsWithPasskeys, - MultisigV2Enhancement, -} - -fn generate_features_blob(writer: &CodeWriter, data: &[u64]) { - emitln!(writer, "vector["); - writer.indent(); - for (i, b) in data.iter().enumerate() { - if i % 20 == 0 { - if i > 0 { - emitln!(writer); - } - } else { - emit!(writer, " "); - } - emit!(writer, "{},", b); - } - emitln!(writer); - writer.unindent(); - emit!(writer, "]") -} - -pub fn generate_feature_upgrade_proposal( - features: &Features, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let enabled = features - .enabled - .iter() - .map(|f| AptosFeatureFlag::from(f.clone()) as u64) - .collect::>(); - let disabled = features - .disabled - .iter() - .map(|f| AptosFeatureFlag::from(f.clone()) as u64) - .collect::>(); - - assert!(enabled.len() < u16::MAX as usize); - assert!(disabled.len() < u16::MAX as usize); - - let writer = CodeWriter::new(Loc::default()); - - emitln!(writer, "// Modifying on-chain feature flags: "); - emitln!(writer, "// Enabled Features: {:?}", features.enabled); - emitln!(writer, "// Disabled Features: {:?}", features.disabled); - emitln!(writer, "//"); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["std::features"], - |writer| { - emit!(writer, "let enabled_blob: vector = "); - generate_features_blob(writer, &enabled); - emitln!(writer, ";\n"); - - emit!(writer, "let disabled_blob: vector = "); - generate_features_blob(writer, &disabled); - emitln!(writer, ";\n"); - - emitln!( - writer, - "features::change_feature_flags_for_next_epoch({}, enabled_blob, disabled_blob);", - signer_arg - ); - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("features".to_string(), proposal)); - Ok(result) -} - -impl From for AptosFeatureFlag { - fn from(f: FeatureFlag) -> Self { - match f { - FeatureFlag::CodeDependencyCheck => AptosFeatureFlag::CODE_DEPENDENCY_CHECK, - FeatureFlag::CollectAndDistributeGasFees => { - AptosFeatureFlag::COLLECT_AND_DISTRIBUTE_GAS_FEES - }, - FeatureFlag::TreatFriendAsPrivate => AptosFeatureFlag::TREAT_FRIEND_AS_PRIVATE, - FeatureFlag::Sha512AndRipeMd160Natives => { - AptosFeatureFlag::SHA_512_AND_RIPEMD_160_NATIVES - }, - FeatureFlag::AptosStdChainIdNatives => AptosFeatureFlag::APTOS_STD_CHAIN_ID_NATIVES, - FeatureFlag::VMBinaryFormatV6 => AptosFeatureFlag::VM_BINARY_FORMAT_V6, - FeatureFlag::VMBinaryFormatV7 => AptosFeatureFlag::VM_BINARY_FORMAT_V7, - FeatureFlag::MultiEd25519PkValidateV2Natives => { - AptosFeatureFlag::MULTI_ED25519_PK_VALIDATE_V2_NATIVES - }, - FeatureFlag::Blake2b256Native => AptosFeatureFlag::BLAKE2B_256_NATIVE, - FeatureFlag::ResourceGroups => AptosFeatureFlag::RESOURCE_GROUPS, - FeatureFlag::MultisigAccounts => AptosFeatureFlag::MULTISIG_ACCOUNTS, - FeatureFlag::DelegationPools => AptosFeatureFlag::DELEGATION_POOLS, - FeatureFlag::CryptographyAlgebraNatives => { - AptosFeatureFlag::CRYPTOGRAPHY_ALGEBRA_NATIVES - }, - FeatureFlag::Bls12381Structures => AptosFeatureFlag::BLS12_381_STRUCTURES, - FeatureFlag::Ed25519PubkeyValidateReturnFalseWrongLength => { - AptosFeatureFlag::ED25519_PUBKEY_VALIDATE_RETURN_FALSE_WRONG_LENGTH - }, - FeatureFlag::StructConstructors => AptosFeatureFlag::STRUCT_CONSTRUCTORS, - FeatureFlag::PeriodicalRewardRateReduction => { - AptosFeatureFlag::PERIODICAL_REWARD_RATE_DECREASE - }, - FeatureFlag::PartialGovernanceVoting => AptosFeatureFlag::PARTIAL_GOVERNANCE_VOTING, - FeatureFlag::SignatureCheckerV2 => AptosFeatureFlag::SIGNATURE_CHECKER_V2, - FeatureFlag::StorageSlotMetadata => AptosFeatureFlag::STORAGE_SLOT_METADATA, - FeatureFlag::ChargeInvariantViolation => AptosFeatureFlag::CHARGE_INVARIANT_VIOLATION, - FeatureFlag::DelegationPoolPartialGovernanceVoting => { - AptosFeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING - }, - FeatureFlag::GasPayerEnabled => AptosFeatureFlag::GAS_PAYER_ENABLED, - FeatureFlag::AptosUniqueIdentifiers => AptosFeatureFlag::APTOS_UNIQUE_IDENTIFIERS, - FeatureFlag::BulletproofsNatives => AptosFeatureFlag::BULLETPROOFS_NATIVES, - FeatureFlag::SignerNativeFormatFix => AptosFeatureFlag::SIGNER_NATIVE_FORMAT_FIX, - FeatureFlag::ModuleEvent => AptosFeatureFlag::MODULE_EVENT, - FeatureFlag::EmitFeeStatement => AptosFeatureFlag::EMIT_FEE_STATEMENT, - FeatureFlag::StorageDeletionRefund => AptosFeatureFlag::STORAGE_DELETION_REFUND, - FeatureFlag::AggregatorV2Api => AptosFeatureFlag::AGGREGATOR_V2_API, - FeatureFlag::SignatureCheckerV2ScriptFix => { - AptosFeatureFlag::SIGNATURE_CHECKER_V2_SCRIPT_FIX - }, - FeatureFlag::SaferResourceGroups => AptosFeatureFlag::SAFER_RESOURCE_GROUPS, - FeatureFlag::SaferMetadata => AptosFeatureFlag::SAFER_METADATA, - FeatureFlag::SingleSenderAuthenticator => AptosFeatureFlag::SINGLE_SENDER_AUTHENTICATOR, - FeatureFlag::SponsoredAutomaticAccountCreation => { - AptosFeatureFlag::SPONSORED_AUTOMATIC_ACCOUNT_V1_CREATION - }, - FeatureFlag::FeePayerAccountOptional => AptosFeatureFlag::FEE_PAYER_ACCOUNT_OPTIONAL, - FeatureFlag::AggregatorV2DelayedFields => { - AptosFeatureFlag::AGGREGATOR_V2_DELAYED_FIELDS - }, - FeatureFlag::ConcurrentTokenV2 => AptosFeatureFlag::CONCURRENT_TOKEN_V2, - FeatureFlag::LimitMaxIdentifierLength => AptosFeatureFlag::LIMIT_MAX_IDENTIFIER_LENGTH, - FeatureFlag::OperatorBeneficiaryChange => AptosFeatureFlag::OPERATOR_BENEFICIARY_CHANGE, - FeatureFlag::ResourceGroupsSplitInVmChangeSet => { - AptosFeatureFlag::RESOURCE_GROUPS_SPLIT_IN_VM_CHANGE_SET - }, - FeatureFlag::CommissionChangeDelegationPool => { - AptosFeatureFlag::COMMISSION_CHANGE_DELEGATION_POOL - }, - FeatureFlag::Bn254Structures => AptosFeatureFlag::BN254_STRUCTURES, - FeatureFlag::WebAuthnSignature => AptosFeatureFlag::WEBAUTHN_SIGNATURE, - FeatureFlag::ReconfigureWithDkg => AptosFeatureFlag::RECONFIGURE_WITH_DKG, - FeatureFlag::KeylessAccounts => AptosFeatureFlag::KEYLESS_ACCOUNTS, - FeatureFlag::KeylessButZklessAccounts => AptosFeatureFlag::KEYLESS_BUT_ZKLESS_ACCOUNTS, - FeatureFlag::RemoveDetailedError => AptosFeatureFlag::REMOVE_DETAILED_ERROR_FROM_HASH, - FeatureFlag::JwkConsensus => AptosFeatureFlag::JWK_CONSENSUS, - FeatureFlag::ConcurrentFungibleAssets => AptosFeatureFlag::CONCURRENT_FUNGIBLE_ASSETS, - FeatureFlag::RefundableBytes => AptosFeatureFlag::REFUNDABLE_BYTES, - FeatureFlag::ObjectCodeDeployment => AptosFeatureFlag::OBJECT_CODE_DEPLOYMENT, - FeatureFlag::MaxObjectNestingCheck => AptosFeatureFlag::MAX_OBJECT_NESTING_CHECK, - FeatureFlag::KeylessAccountsWithPasskeys => { - AptosFeatureFlag::KEYLESS_ACCOUNTS_WITH_PASSKEYS - }, - FeatureFlag::MultisigV2Enhancement => AptosFeatureFlag::MULTISIG_V2_ENHANCEMENT, - } - } -} - -// We don't need this implementation. Just to make sure we have an exhaustive 1-1 mapping between the two structs. -impl From for FeatureFlag { - fn from(f: AptosFeatureFlag) -> Self { - match f { - AptosFeatureFlag::CODE_DEPENDENCY_CHECK => FeatureFlag::CodeDependencyCheck, - AptosFeatureFlag::COLLECT_AND_DISTRIBUTE_GAS_FEES => { - FeatureFlag::CollectAndDistributeGasFees - }, - AptosFeatureFlag::TREAT_FRIEND_AS_PRIVATE => FeatureFlag::TreatFriendAsPrivate, - AptosFeatureFlag::SHA_512_AND_RIPEMD_160_NATIVES => { - FeatureFlag::Sha512AndRipeMd160Natives - }, - AptosFeatureFlag::APTOS_STD_CHAIN_ID_NATIVES => FeatureFlag::AptosStdChainIdNatives, - AptosFeatureFlag::VM_BINARY_FORMAT_V6 => FeatureFlag::VMBinaryFormatV6, - AptosFeatureFlag::VM_BINARY_FORMAT_V7 => FeatureFlag::VMBinaryFormatV7, - AptosFeatureFlag::MULTI_ED25519_PK_VALIDATE_V2_NATIVES => { - FeatureFlag::MultiEd25519PkValidateV2Natives - }, - AptosFeatureFlag::BLAKE2B_256_NATIVE => FeatureFlag::Blake2b256Native, - AptosFeatureFlag::RESOURCE_GROUPS => FeatureFlag::ResourceGroups, - AptosFeatureFlag::MULTISIG_ACCOUNTS => FeatureFlag::MultisigAccounts, - AptosFeatureFlag::DELEGATION_POOLS => FeatureFlag::DelegationPools, - AptosFeatureFlag::CRYPTOGRAPHY_ALGEBRA_NATIVES => { - FeatureFlag::CryptographyAlgebraNatives - }, - AptosFeatureFlag::BLS12_381_STRUCTURES => FeatureFlag::Bls12381Structures, - AptosFeatureFlag::ED25519_PUBKEY_VALIDATE_RETURN_FALSE_WRONG_LENGTH => { - FeatureFlag::Ed25519PubkeyValidateReturnFalseWrongLength - }, - AptosFeatureFlag::STRUCT_CONSTRUCTORS => FeatureFlag::StructConstructors, - AptosFeatureFlag::PERIODICAL_REWARD_RATE_DECREASE => { - FeatureFlag::PeriodicalRewardRateReduction - }, - AptosFeatureFlag::PARTIAL_GOVERNANCE_VOTING => FeatureFlag::PartialGovernanceVoting, - AptosFeatureFlag::SIGNATURE_CHECKER_V2 => FeatureFlag::SignatureCheckerV2, - AptosFeatureFlag::STORAGE_SLOT_METADATA => FeatureFlag::StorageSlotMetadata, - AptosFeatureFlag::CHARGE_INVARIANT_VIOLATION => FeatureFlag::ChargeInvariantViolation, - AptosFeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING => { - FeatureFlag::DelegationPoolPartialGovernanceVoting - }, - AptosFeatureFlag::GAS_PAYER_ENABLED => FeatureFlag::GasPayerEnabled, - AptosFeatureFlag::APTOS_UNIQUE_IDENTIFIERS => FeatureFlag::AptosUniqueIdentifiers, - AptosFeatureFlag::BULLETPROOFS_NATIVES => FeatureFlag::BulletproofsNatives, - AptosFeatureFlag::SIGNER_NATIVE_FORMAT_FIX => FeatureFlag::SignerNativeFormatFix, - AptosFeatureFlag::MODULE_EVENT => FeatureFlag::ModuleEvent, - AptosFeatureFlag::EMIT_FEE_STATEMENT => FeatureFlag::EmitFeeStatement, - AptosFeatureFlag::STORAGE_DELETION_REFUND => FeatureFlag::StorageDeletionRefund, - AptosFeatureFlag::AGGREGATOR_V2_API => FeatureFlag::AggregatorV2Api, - AptosFeatureFlag::SIGNATURE_CHECKER_V2_SCRIPT_FIX => { - FeatureFlag::SignatureCheckerV2ScriptFix - }, - AptosFeatureFlag::SAFER_RESOURCE_GROUPS => FeatureFlag::SaferResourceGroups, - AptosFeatureFlag::SAFER_METADATA => FeatureFlag::SaferMetadata, - AptosFeatureFlag::SINGLE_SENDER_AUTHENTICATOR => FeatureFlag::SingleSenderAuthenticator, - AptosFeatureFlag::SPONSORED_AUTOMATIC_ACCOUNT_V1_CREATION => { - FeatureFlag::SponsoredAutomaticAccountCreation - }, - AptosFeatureFlag::FEE_PAYER_ACCOUNT_OPTIONAL => FeatureFlag::FeePayerAccountOptional, - AptosFeatureFlag::AGGREGATOR_V2_DELAYED_FIELDS => { - FeatureFlag::AggregatorV2DelayedFields - }, - AptosFeatureFlag::CONCURRENT_TOKEN_V2 => FeatureFlag::ConcurrentTokenV2, - AptosFeatureFlag::LIMIT_MAX_IDENTIFIER_LENGTH => FeatureFlag::LimitMaxIdentifierLength, - AptosFeatureFlag::OPERATOR_BENEFICIARY_CHANGE => FeatureFlag::OperatorBeneficiaryChange, - AptosFeatureFlag::RESOURCE_GROUPS_SPLIT_IN_VM_CHANGE_SET => { - FeatureFlag::ResourceGroupsSplitInVmChangeSet - }, - AptosFeatureFlag::COMMISSION_CHANGE_DELEGATION_POOL => { - FeatureFlag::CommissionChangeDelegationPool - }, - AptosFeatureFlag::BN254_STRUCTURES => FeatureFlag::Bn254Structures, - AptosFeatureFlag::WEBAUTHN_SIGNATURE => FeatureFlag::WebAuthnSignature, - AptosFeatureFlag::RECONFIGURE_WITH_DKG => FeatureFlag::ReconfigureWithDkg, - AptosFeatureFlag::KEYLESS_ACCOUNTS => FeatureFlag::KeylessAccounts, - AptosFeatureFlag::KEYLESS_BUT_ZKLESS_ACCOUNTS => FeatureFlag::KeylessButZklessAccounts, - AptosFeatureFlag::REMOVE_DETAILED_ERROR_FROM_HASH => FeatureFlag::RemoveDetailedError, - AptosFeatureFlag::JWK_CONSENSUS => FeatureFlag::JwkConsensus, - AptosFeatureFlag::CONCURRENT_FUNGIBLE_ASSETS => FeatureFlag::ConcurrentFungibleAssets, - AptosFeatureFlag::REFUNDABLE_BYTES => FeatureFlag::RefundableBytes, - AptosFeatureFlag::OBJECT_CODE_DEPLOYMENT => FeatureFlag::ObjectCodeDeployment, - AptosFeatureFlag::MAX_OBJECT_NESTING_CHECK => FeatureFlag::MaxObjectNestingCheck, - AptosFeatureFlag::KEYLESS_ACCOUNTS_WITH_PASSKEYS => { - FeatureFlag::KeylessAccountsWithPasskeys - }, - AptosFeatureFlag::MULTISIG_V2_ENHANCEMENT => FeatureFlag::MultisigV2Enhancement, - } - } -} - -impl Features { - // Compare if the current feature set is different from features that has been enabled on chain. - pub(crate) fn has_modified(&self, on_chain_features: &AptosFeatures) -> bool { - self.enabled - .iter() - .any(|f| !on_chain_features.is_enabled(AptosFeatureFlag::from(f.clone()))) - || self - .disabled - .iter() - .any(|f| on_chain_features.is_enabled(AptosFeatureFlag::from(f.clone()))) - } -} - -impl From<&AptosFeatures> for Features { - fn from(features: &AptosFeatures) -> Features { - let mut enabled = vec![]; - let mut disabled = vec![]; - for feature in FeatureFlag::iter() { - if features.is_enabled(AptosFeatureFlag::from(feature.clone())) { - enabled.push(feature); - } else { - disabled.push(feature); - } - } - Features { enabled, disabled } - } -} diff --git a/aptos-move/aptos-release-builder/src/components/framework.rs b/aptos-move/aptos-release-builder/src/components/framework.rs deleted file mode 100644 index d95c585d8e4fc..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/framework.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{aptos_core_path, components::get_execution_hash}; -use anyhow::Result; -use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; -use aptos_temppath::TempPath; -use aptos_types::account_address::AccountAddress; -use git2::Repository; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)] -pub struct FrameworkReleaseConfig { - /// Move bytecode version the framework release would be compiled to. - pub bytecode_version: u32, - /// Compile the framework release at a given git commit hash. - /// If set to None, we will use the aptos framework under current repo. - pub git_hash: Option, -} - -pub fn generate_upgrade_proposals( - config: &FrameworkReleaseConfig, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - const APTOS_GIT_PATH: &str = "https://github.com/aptos-labs/aptos-core.git"; - - let mut package_path_list = [ - ("0x1", "aptos-move/framework/move-stdlib"), - ("0x1", "aptos-move/framework/aptos-stdlib"), - ("0x1", "aptos-move/framework/aptos-framework"), - ("0x3", "aptos-move/framework/aptos-token"), - ("0x4", "aptos-move/framework/aptos-token-objects"), - ]; - - let mut result: Vec<(String, String)> = vec![]; - - let temp_root_path = TempPath::new(); - temp_root_path.create_as_dir()?; - - let commit_info = if let Some(revision) = &config.git_hash { - // If a commit hash is set, clone the repo from github and checkout to desired hash to a local temp directory. - let repository = Repository::clone(APTOS_GIT_PATH, temp_root_path.path())?; - let (commit, _) = repository.revparse_ext(revision.as_str())?; - let commit_info = commit - .describe(&git2::DescribeOptions::default())? - .format(None)?; - repository.checkout_tree(&commit, None)?; - commit_info - } else { - aptos_build_info::get_git_hash() - }; - - // For generating multi-step proposal files, we need to generate them in the reverse order since - // we need the hash of the next script. - // We will reverse the order back when writing the files into a directory. - if !next_execution_hash.is_empty() { - package_path_list.reverse(); - } - - for (publish_addr, relative_package_path) in package_path_list.iter() { - let account = AccountAddress::from_hex_literal(publish_addr)?; - let temp_script_path = TempPath::new(); - temp_script_path.create_as_file()?; - let mut move_script_path = temp_script_path.path().to_path_buf(); - move_script_path.set_extension("move"); - - let mut package_path = if config.git_hash.is_some() { - temp_root_path.path().to_path_buf() - } else { - aptos_core_path() - }; - - package_path.push(relative_package_path); - - let script_name = package_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(); - - // If this file is the first framework file being generated (if `result.is_empty()` is true), - // its `next_execution_hash` should be the `next_execution_hash` value being passed in. - // If the `result` vector is not empty, the current file's `next_execution_hash` should be the - // hash of the latest framework file being generated (the hash of result.last()). - // For example, let's say we are going to generate these files: - // 0-move-stdlib.move 2-aptos-framework.move 4-gas-schedule.move 6-features.move - // 1-aptos-stdlib.move 3-aptos-token.move 5-version.move 7-consensus-config.move - // The first framework file being generated is 3-aptos-token.move. It's using the next_execution_hash being passed in (so in this case, the hash of 4-gas-schedule.move being passed in mod.rs). - // The second framework file being generated would be 2-aptos-framework.move, and it's using the hash of 3-aptos-token.move (which would be result.last()). - - let options = BuildOptions { - with_srcs: true, - with_abis: false, - with_source_maps: false, - with_error_map: true, - skip_fetch_latest_git_deps: false, - bytecode_version: Some(config.bytecode_version), - ..BuildOptions::default() - }; - let package = BuiltPackage::build(package_path, options)?; - let release = ReleasePackage::new(package)?; - - // If we're generating a single-step proposal on testnet - if is_testnet && next_execution_hash.is_empty() { - release.generate_script_proposal_testnet(account, move_script_path.clone())?; - // If we're generating a single-step proposal on mainnet - } else if next_execution_hash.is_empty() { - release.generate_script_proposal(account, move_script_path.clone())?; - // If we're generating a multi-step proposal - } else { - let next_execution_hash_bytes = if result.is_empty() { - next_execution_hash.clone() - } else { - get_execution_hash(&result) - }; - release.generate_script_proposal_multi_step( - account, - move_script_path.clone(), - next_execution_hash_bytes, - )?; - }; - - let mut script = format!( - "// Framework commit hash: {}\n// Builder commit hash: {}\n", - commit_info, - aptos_build_info::get_git_hash() - ); - - script.push_str(&std::fs::read_to_string(move_script_path.as_path())?); - - result.push((script_name, script)); - } - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/gas.rs b/aptos-move/aptos-release-builder/src/components/gas.rs deleted file mode 100644 index 34d168a411ba2..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/gas.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{components::get_signer_arg, utils::*}; -use anyhow::Result; -use aptos_types::on_chain_config::GasScheduleV2; -use move_model::{code_writer::CodeWriter, emit, emitln, model::Loc}; - -pub fn generate_gas_upgrade_proposal( - post_randomness_framework: bool, - gas_schedule: &GasScheduleV2, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - emitln!( - writer, - "// source commit hash: {}\n", - aptos_build_info::get_git_hash() - ); - - emitln!(writer, "// Gas schedule upgrade proposal\n"); - - emitln!( - writer, - "// Feature version: {}", - gas_schedule.feature_version - ); - emitln!(writer, "//"); - emitln!(writer, "// Entries:"); - let max_len = gas_schedule - .entries - .iter() - .fold(0, |acc, (name, _)| usize::max(acc, name.len())); - for (name, val) in &gas_schedule.entries { - let name_with_spaces = format!("{}{}", name, " ".repeat(max_len - name.len())); - emitln!(writer, "// {} : {}", name_with_spaces, val); - } - emitln!(writer); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::gas_schedule"], - |writer| { - let gas_schedule_blob = bcs::to_bytes(gas_schedule).unwrap(); - assert!(gas_schedule_blob.len() < 65536); - emit!(writer, "let gas_schedule_blob: vector = "); - generate_blob(writer, &gas_schedule_blob); - emitln!(writer, ";\n"); - if !post_randomness_framework { - emitln!( - writer, - "gas_schedule::set_gas_schedule({}, gas_schedule_blob)", - signer_arg - ); - } else { - // The else statement has & before the framework_signer. - // The testnet single-step generation had something like let framework_signer = &core_signer; - // so that their framework_signer is of type &signer, but for mainnet single-step and multi-step, - // the framework_signer is of type signer. - emitln!( - writer, - "gas_schedule::set_for_next_epoch({}, gas_schedule_blob);", - signer_arg - ); - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - } - }, - ); - - result.push(("gas-schedule".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/jwk_consensus_config.rs b/aptos-move/aptos-release-builder/src/components/jwk_consensus_config.rs deleted file mode 100644 index ec64b64dc9872..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/jwk_consensus_config.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{components::get_signer_arg, utils::generate_governance_proposal}; -use aptos_types::on_chain_config::OnChainJWKConsensusConfig; -use move_model::{code_writer::CodeWriter, emitln, model::Loc}; - -pub fn generate_jwk_consensus_config_update_proposal( - config: &OnChainJWKConsensusConfig, - is_testnet: bool, - next_execution_hash: Vec, -) -> anyhow::Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::jwk_consensus_config", "std::string::utf8"], - |writer| { - match config { - OnChainJWKConsensusConfig::Off => { - emitln!(writer, "jwk_consensus_config::set_for_next_epoch({}, jwk_consensus_config::new_off());", signer_arg); - }, - OnChainJWKConsensusConfig::V1(v1) => { - emitln!(writer, "let config = jwk_consensus_config::new_v1(vector["); - for p in v1.oidc_providers.iter() { - emitln!(writer, "jwk_consensus_config::new_oidc_provider(utf8(b\"{}\"), utf8(b\"{}\")),", p.name, p.config_url); - } - emitln!(writer, "]);"); - emitln!( - writer, - "jwk_consensus_config::set_for_next_epoch({}, config);", - signer_arg - ); - }, - } - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("jwk-consensus-config".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/mod.rs b/aptos-move/aptos-release-builder/src/components/mod.rs deleted file mode 100644 index 3e77eb06a9c7d..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/mod.rs +++ /dev/null @@ -1,809 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use self::framework::FrameworkReleaseConfig; -use crate::{ - aptos_core_path, aptos_framework_path, - components::{ - feature_flags::Features, oidc_providers::OidcProviderOp, - randomness_config::ReleaseFriendlyRandomnessConfig, - }, -}; -use anyhow::{anyhow, bail, Context, Result}; -use aptos::governance::GenerateExecutionHash; -use aptos_infallible::duration_since_epoch; -use aptos_rest_client::Client; -use aptos_temppath::TempPath; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, - on_chain_config::{ - ExecutionConfigV1, FeatureFlag as AptosFeatureFlag, GasScheduleV2, OnChainConfig, - OnChainConsensusConfig, OnChainExecutionConfig, OnChainJWKConsensusConfig, - OnChainRandomnessConfig, RandomnessConfigMoveStruct, TransactionShufflerType, Version, - }, -}; -use futures::executor::block_on; -use handlebars::Handlebars; -use once_cell::sync::Lazy; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - fs::File, - io::{Read, Write}, - path::{Path, PathBuf}, - thread::sleep, - time::Duration, -}; -use url::Url; - -pub mod consensus_config; -pub mod execution_config; -pub mod feature_flags; -pub mod framework; -pub mod gas; -pub mod jwk_consensus_config; -pub mod oidc_providers; -pub mod randomness_config; -pub mod transaction_fee; -pub mod version; - -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct ReleaseConfig { - pub name: String, - pub remote_endpoint: Option, - pub proposals: Vec, -} - -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct Proposal { - pub name: String, - pub metadata: ProposalMetadata, - pub execution_mode: ExecutionMode, - pub update_sequence: Vec, -} - -impl Proposal { - fn consolidated_side_effects(&self) -> Vec { - let mut ret = vec![]; - let mut features_diff = Features::empty(); - for entry in &self.update_sequence { - match entry { - ReleaseEntry::FeatureFlag(feature_flags) => { - features_diff.squash(feature_flags.clone()) - }, - ReleaseEntry::Framework(_) - | ReleaseEntry::CustomGas(_) - | ReleaseEntry::DefaultGas - | ReleaseEntry::DefaultGasWithOverride(_) - | ReleaseEntry::DefaultGasWithOverrideOld(_) - | ReleaseEntry::Version(_) - | ReleaseEntry::Consensus(_) - | ReleaseEntry::Execution(_) - | ReleaseEntry::JwkConsensus(_) - | ReleaseEntry::Randomness(_) - | ReleaseEntry::RawScript(_) => ret.push(entry.clone()), - // Deprecated by `JwkConsensus`. - ReleaseEntry::OidcProviderOps(_) => {}, - } - } - - if !features_diff.is_empty() { - ret.push(ReleaseEntry::FeatureFlag(features_diff)); - } - - ret - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] -pub struct ProposalMetadata { - title: String, - description: String, - #[serde(default = "default_url")] - source_code_url: String, - #[serde(default = "default_url")] - discussion_url: String, -} - -fn default_url() -> String { - "https://github.com/aptos-labs/aptos-core".to_string() -} - -#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq)] -pub enum ExecutionMode { - MultiStep, - RootSigner, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct GasOverride { - name: String, - value: u64, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub enum ReleaseEntry { - Framework(FrameworkReleaseConfig), - CustomGas(GasScheduleV2), - DefaultGas, - DefaultGasWithOverride(Vec), - /// Only used before randomness framework upgrade. - DefaultGasWithOverrideOld(Vec), - Version(Version), - FeatureFlag(Features), - Consensus(OnChainConsensusConfig), - Execution(OnChainExecutionConfig), - RawScript(PathBuf), - /// Deprecated by `OnChainJwkConsensusConfig`. - OidcProviderOps(Vec), - JwkConsensus(OnChainJWKConsensusConfig), - Randomness(ReleaseFriendlyRandomnessConfig), -} - -impl ReleaseEntry { - pub fn generate_release_script( - &self, - client: Option<&Client>, - result: &mut Vec<(String, String)>, - execution_mode: ExecutionMode, - ) -> Result<()> { - let (is_testnet, is_multi_step) = match execution_mode { - ExecutionMode::MultiStep => (false, true), - ExecutionMode::RootSigner => (true, false), - }; - match self { - ReleaseEntry::Framework(framework_release) => { - result.append( - &mut framework::generate_upgrade_proposals( - framework_release, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - ) - .unwrap(), - ); - }, - ReleaseEntry::CustomGas(gas_schedule) => { - if !fetch_and_equals::(client, gas_schedule)? { - result.append(&mut gas::generate_gas_upgrade_proposal( - true, - gas_schedule, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::DefaultGas => { - let gas_schedule = aptos_gas_schedule_updator::current_gas_schedule(); - if !fetch_and_equals::(client, &gas_schedule)? { - result.append(&mut gas::generate_gas_upgrade_proposal( - true, - &gas_schedule, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::DefaultGasWithOverride(gas_overrides) => { - let gas_schedule = gas_override_default(gas_overrides)?; - if !fetch_and_equals::(client, &gas_schedule)? { - result.append(&mut gas::generate_gas_upgrade_proposal( - true, - &gas_schedule, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::DefaultGasWithOverrideOld(gas_overrides) => { - let gas_schedule = gas_override_default(gas_overrides)?; - if !fetch_and_equals::(client, &gas_schedule)? { - result.append(&mut gas::generate_gas_upgrade_proposal( - false, - &gas_schedule, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::Version(version) => { - if !fetch_and_equals::(client, version)? { - result.append(&mut version::generate_version_upgrade_proposal( - version, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::FeatureFlag(feature_flags) => { - let mut needs_update = true; - if let Some(client) = client { - let features = block_on(async { - client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::features::Features", - ) - .await - })?; - // Only update the feature flags section when there's a divergence between the local configs and on chain configs. - // If any flag in the release config diverges from the on chain value, we will emit a script that includes all flags - // we would like to enable/disable, regardless of their current on chain state. - needs_update = feature_flags.has_modified(features.inner()); - } - if needs_update { - result.append(&mut feature_flags::generate_feature_upgrade_proposal( - feature_flags, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::Consensus(consensus_config) => { - if !fetch_and_equals(client, consensus_config)? { - result.append(&mut consensus_config::generate_consensus_upgrade_proposal( - consensus_config, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - } - }, - ReleaseEntry::Execution(execution_config) => { - if !fetch_and_equals(client, execution_config)? { - result.append( - &mut execution_config::generate_execution_config_upgrade_proposal( - execution_config, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?, - ); - } - }, - ReleaseEntry::OidcProviderOps(ops) => { - result.append(&mut oidc_providers::generate_oidc_provider_ops_proposal( - ops, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?); - }, - ReleaseEntry::RawScript(script_path) => { - let base_path = aptos_core_path().join(script_path.as_path()); - let file_name = base_path - .file_name() - .and_then(|name| name.to_str()) - .ok_or_else(|| { - anyhow!("Unable to obtain file name for proposal: {:?}", script_path) - })? - .to_string(); - let file_content = std::fs::read_to_string(base_path) - .with_context(|| format!("Unable to read file: {}", script_path.display()))?; - - if let ExecutionMode::MultiStep = execution_mode { - // Render the hash for multi step proposal. - // {{ script_hash }} in the provided move file will be replaced with the real hash. - - let mut handlebars = Handlebars::new(); - handlebars - .register_template_string("move_template", file_content.as_str()) - .unwrap(); - - let execution_hash = get_execution_hash(result); - let mut hash_string = "vector[".to_string(); - for b in execution_hash.iter() { - hash_string.push_str(format!("{}u8,", b).as_str()); - } - hash_string.push(']'); - - let mut data = HashMap::new(); - data.insert("script_hash", hash_string); - - result.push(( - file_name, - handlebars - .render("move_template", &data) - .map_err(|err| anyhow!("Fail to render string: {:?}", err))?, - )); - } else { - result.push((file_name, file_content)); - } - }, - ReleaseEntry::JwkConsensus(config) => { - result.append( - &mut jwk_consensus_config::generate_jwk_consensus_config_update_proposal( - config, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?, - ); - }, - ReleaseEntry::Randomness(config) => { - result.append( - &mut randomness_config::generate_randomness_config_update_proposal( - config, - is_testnet, - if is_multi_step { - get_execution_hash(result) - } else { - "".to_owned().into_bytes() - }, - )?, - ); - }, - } - Ok(()) - } - - pub fn validate_upgrade(&self, client: &Client) -> Result<()> { - let client_opt = Some(client); - match self { - ReleaseEntry::Framework(_) => (), - ReleaseEntry::RawScript(_) => (), - ReleaseEntry::CustomGas(gas_schedule) => { - if !wait_until_equals(client_opt, gas_schedule, *MAX_ASYNC_RECONFIG_TIME) { - bail!("Gas schedule config mismatch: Expected {:?}", gas_schedule); - } - }, - ReleaseEntry::DefaultGas => { - if !wait_until_equals( - client_opt, - &aptos_gas_schedule_updator::current_gas_schedule(), - *MAX_ASYNC_RECONFIG_TIME, - ) { - bail!("Gas schedule config mismatch: Expected Default"); - } - }, - ReleaseEntry::DefaultGasWithOverrideOld(gas_overrides) - | ReleaseEntry::DefaultGasWithOverride(gas_overrides) => { - if !wait_until_equals( - client_opt, - &gas_override_default(gas_overrides)?, - Duration::from_secs(60), - ) { - bail!("Gas schedule config mismatch: Expected Default"); - } - }, - ReleaseEntry::Version(version) => { - if !wait_until_equals(client_opt, version, Duration::from_secs(60)) { - bail!("Version config mismatch: Expected {:?}", version); - } - }, - ReleaseEntry::FeatureFlag(features) => { - let on_chain_features = block_on(async { - client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::features::Features", - ) - .await - })?; - - for to_enable in &features.enabled { - let flag = to_enable.clone().into(); - if !on_chain_features.inner().is_enabled(flag) { - bail!( - "Feature flag config mismatch: Expected {:?} to be enabled", - to_enable - ); - } - } - - for to_disable in &features.disabled { - let flag = to_disable.clone().into(); - if on_chain_features.inner().is_enabled(flag) { - bail!( - "Feature flag config mismatch: Expected {:?} to be disabled", - to_disable - ); - } - } - }, - ReleaseEntry::Consensus(consensus_config) => { - if !wait_until_equals(client_opt, consensus_config, *MAX_ASYNC_RECONFIG_TIME) { - bail!("Consensus config mismatch: Expected {:?}", consensus_config); - } - }, - ReleaseEntry::Execution(execution_config) => { - if !wait_until_equals(client_opt, execution_config, *MAX_ASYNC_RECONFIG_TIME) { - bail!("Consensus config mismatch: Expected {:?}", execution_config); - } - }, - ReleaseEntry::OidcProviderOps(_) => {}, - ReleaseEntry::JwkConsensus(jwk_consensus_config) => { - if !wait_until_equals(client_opt, jwk_consensus_config, *MAX_ASYNC_RECONFIG_TIME) { - bail!( - "JWK consensus config mismatch: Expected {:?}", - jwk_consensus_config - ); - } - }, - ReleaseEntry::Randomness(config) => { - let expected_on_chain = - RandomnessConfigMoveStruct::from(OnChainRandomnessConfig::from(config.clone())); - if !wait_until_equals(client_opt, &expected_on_chain, *MAX_ASYNC_RECONFIG_TIME) { - bail!("randomness config mismatch: Expected {:?}", config); - } - }, - } - Ok(()) - } -} - -fn gas_override_default(gas_overrides: &[GasOverride]) -> Result { - let mut gas_schedule = aptos_gas_schedule_updator::current_gas_schedule(); - for gas_override in gas_overrides { - let mut found = false; - for (name, value) in &mut gas_schedule.entries { - if name == &gas_override.name { - *value = gas_override.value; - found = true; - break; - } - } - if !found { - bail!( - "Gas override config mismatch: Expected {:?} to be in the gas schedule", - gas_override.name - ); - } - } - Ok(gas_schedule) -} - -// Compare the current on chain config with the value recorded on chain. Return false if there's a difference. -fn fetch_and_equals( - client: Option<&Client>, - expected: &T, -) -> Result { - match client { - Some(client) => { - let config = fetch_config::(client)?; - - Ok(&config == expected) - }, - None => Ok(false), - } -} - -fn wait_until_equals( - client: Option<&Client>, - expected: &T, - time_limit: Duration, -) -> bool { - let deadline = duration_since_epoch() + time_limit; - while duration_since_epoch() < deadline { - if matches!(fetch_and_equals(client, expected), Ok(true)) { - return true; - } - sleep(Duration::from_secs(1)); - } - false -} - -pub fn fetch_config(client: &Client) -> Result { - T::deserialize_into_config( - block_on(async { - client - .get_account_resource_bytes( - CORE_CODE_ADDRESS, - format!( - "{}::{}::{}", - T::ADDRESS, - T::MODULE_IDENTIFIER, - T::TYPE_IDENTIFIER - ) - .as_str(), - ) - .await - })? - .inner(), - ) -} - -impl ReleaseConfig { - pub fn generate_release_proposal_scripts(&self, base_path: &Path) -> Result<()> { - let client = self - .remote_endpoint - .as_ref() - .map(|url| Client::new(url.clone())); - - // Create directories for source and metadata. - let mut source_dir = base_path.to_path_buf(); - - // If source dir doesnt exist create it, if it does exist error - if !source_dir.exists() { - println!("Creating source directory: {:?}", source_dir); - std::fs::create_dir(source_dir.as_path()).map_err(|err| { - anyhow!( - "Fail to create folder for source: {} {:?}", - source_dir.display(), - err - ) - })?; - } - - source_dir.push("sources"); - - std::fs::create_dir(source_dir.as_path()) - .map_err(|err| anyhow!("Fail to create folder for source: {:?}", err))?; - - source_dir.push(&self.name); - std::fs::create_dir(source_dir.as_path()) - .map_err(|err| anyhow!("Fail to create folder for source: {:?}", err))?; - - let mut metadata_dir = base_path.to_path_buf(); - metadata_dir.push("metadata"); - - std::fs::create_dir(metadata_dir.as_path()) - .map_err(|err| anyhow!("Fail to create folder for metadata: {:?}", err))?; - metadata_dir.push(&self.name); - std::fs::create_dir(metadata_dir.as_path()) - .map_err(|err| anyhow!("Fail to create folder for metadata: {:?}", err))?; - - // If we are generating multi-step proposal files, we generate the files in reverse order, - // since we need to pass in the hash of the next file to the previous file. - for proposal in &self.proposals { - let mut proposal_dir = base_path.to_path_buf(); - proposal_dir.push("sources"); - proposal_dir.push(&self.name); - proposal_dir.push(proposal.name.as_str()); - - std::fs::create_dir(proposal_dir.as_path()) - .map_err(|err| anyhow!("Fail to create folder for proposal: {:?}", err))?; - - let mut result: Vec<(String, String)> = vec![]; - if let ExecutionMode::MultiStep = &proposal.execution_mode { - for entry in proposal.update_sequence.iter().rev() { - entry.generate_release_script( - client.as_ref(), - &mut result, - proposal.execution_mode, - )?; - } - result.reverse(); - } else { - for entry in proposal.update_sequence.iter() { - entry.generate_release_script( - client.as_ref(), - &mut result, - proposal.execution_mode, - )?; - } - } - - for (idx, (script_name, script)) in result.into_iter().enumerate() { - let mut script_path = proposal_dir.clone(); - let proposal_name = format!("{}-{}", idx, script_name); - script_path.push(&proposal_name); - script_path.set_extension("move"); - - std::fs::write(script_path.as_path(), append_script_hash(script).as_bytes()) - .map_err(|err| anyhow!("Failed to write to file: {:?}", err))?; - } - - let mut metadata_path = base_path.to_path_buf(); - metadata_path.push("metadata"); - metadata_path.push(proposal.name.as_str()); - metadata_path.set_extension("json"); - - std::fs::write( - metadata_path.as_path(), - serde_json::to_string_pretty(&proposal.metadata)?, - ) - .map_err(|err| anyhow!("Failed to write to file: {:?}", err))?; - } - - Ok(()) - } - - pub fn load_config>(path: P) -> Result { - // Open the file and read it into a string - let config_path_string = path.as_ref().to_str().unwrap().to_string(); - let mut file = File::open(&path).map_err(|error| { - anyhow!( - "Failed to open config file: {:?}. Error: {:?}", - config_path_string, - error - ) - })?; - let mut contents = String::new(); - file.read_to_string(&mut contents).map_err(|error| { - anyhow!( - "Failed to read the config file into a string: {:?}. Error: {:?}", - config_path_string, - error - ) - })?; - - // Parse the file string - Self::parse(&contents) - } - - pub fn save_config>(&self, output_file: P) -> Result<()> { - let contents = - serde_yaml::to_vec(&self).map_err(|e| anyhow!("failed to generate config: {:?}", e))?; - let mut file = File::create(output_file.as_ref()) - .map_err(|e| anyhow!("failed to create file: {:?}", e))?; - file.write_all(&contents) - .map_err(|e| anyhow!("failed to write file: {:?}", e))?; - Ok(()) - } - - pub fn parse(serialized: &str) -> Result { - serde_yaml::from_str(serialized).map_err(|e| anyhow!("Failed to parse the config: {:?}", e)) - } - - // Fetch all configs from a remote rest endpoint and assert all the configs are the same as the ones specified locally. - pub fn validate_upgrade(&self, endpoint: &Url, proposal: &Proposal) -> Result<()> { - let client = Client::new(endpoint.clone()); - for entry in proposal.consolidated_side_effects() { - entry.validate_upgrade(&client)?; - } - Ok(()) - } -} - -impl Default for ReleaseConfig { - fn default() -> Self { - ReleaseConfig { - name: "TestingConfig".to_string(), - remote_endpoint: None, - proposals: vec![ - Proposal { - execution_mode: ExecutionMode::MultiStep, - metadata: ProposalMetadata::default(), - name: "framework".to_string(), - update_sequence: vec![ReleaseEntry::Framework(FrameworkReleaseConfig { - bytecode_version: 6, // TODO: remove explicit bytecode version from sources - git_hash: None, - })], - }, - Proposal { - execution_mode: ExecutionMode::MultiStep, - metadata: ProposalMetadata::default(), - name: "gas".to_string(), - update_sequence: vec![ReleaseEntry::DefaultGas], - }, - Proposal { - execution_mode: ExecutionMode::MultiStep, - metadata: ProposalMetadata::default(), - name: "feature_flags".to_string(), - update_sequence: vec![ - ReleaseEntry::FeatureFlag(Features { - enabled: AptosFeatureFlag::default_features() - .into_iter() - .map(crate::components::feature_flags::FeatureFlag::from) - .collect(), - disabled: vec![], - }), - ReleaseEntry::Consensus(OnChainConsensusConfig::default()), - ReleaseEntry::Execution(OnChainExecutionConfig::V1(ExecutionConfigV1 { - transaction_shuffler_type: - TransactionShufflerType::DeprecatedSenderAwareV1(32), - })), - ReleaseEntry::RawScript(PathBuf::from( - "data/proposals/empty_multi_step.move", - )), - ], - }, - ], - } - } -} - -pub fn get_execution_hash(result: &Vec<(String, String)>) -> Vec { - if result.is_empty() { - "vector::empty()".to_owned().into_bytes() - } else { - let temp_script_path = TempPath::new(); - temp_script_path.create_as_file().unwrap(); - let mut move_script_path = temp_script_path.path().to_path_buf(); - move_script_path.set_extension("move"); - std::fs::write(move_script_path.as_path(), result.last().unwrap().1.clone()) - .map_err(|err| { - anyhow!( - "Failed to get execution hash: failed to write to file: {:?}", - err - ) - }) - .unwrap(); - - let (_, hash) = GenerateExecutionHash { - script_path: Option::from(move_script_path), - framework_local_dir: Some(aptos_framework_path()), - } - .generate_hash() - .unwrap(); - hash.to_vec() - } -} - -fn append_script_hash(raw_script: String) -> String { - let temp_script_path = TempPath::new(); - temp_script_path.create_as_file().unwrap(); - - let mut move_script_path = temp_script_path.path().to_path_buf(); - move_script_path.set_extension("move"); - std::fs::write(move_script_path.as_path(), raw_script.as_bytes()) - .map_err(|err| { - anyhow!( - "Failed to get execution hash: failed to write to file: {:?}", - err - ) - }) - .unwrap(); - - let (_, hash) = GenerateExecutionHash { - script_path: Option::from(move_script_path), - framework_local_dir: Some(aptos_framework_path()), - } - .generate_hash() - .unwrap(); - - format!("// Script hash: {} \n{}", hash, raw_script) -} - -impl Default for ProposalMetadata { - fn default() -> Self { - ProposalMetadata { - title: "default".to_string(), - description: "default".to_string(), - // Aptos CLI need a valid url for the two fields. - source_code_url: default_url(), - discussion_url: default_url(), - } - } -} - -fn get_signer_arg(is_testnet: bool, next_execution_hash: &Vec) -> &str { - if is_testnet && next_execution_hash.is_empty() { - "framework_signer" - } else { - "&framework_signer" - } -} - -/// Estimated async reconfiguration time. -static MAX_ASYNC_RECONFIG_TIME: Lazy = Lazy::new(|| Duration::from_secs(60)); diff --git a/aptos-move/aptos-release-builder/src/components/oidc_providers.rs b/aptos-move/aptos-release-builder/src/components/oidc_providers.rs deleted file mode 100644 index afd1801ab6aa7..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/oidc_providers.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{components::get_signer_arg, utils::generate_governance_proposal}; -use move_model::{code_writer::CodeWriter, emitln, model::Loc}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub enum OidcProviderOp { - Upsert { - issuer: String, - config_url: String, - }, - Remove { - issuer: String, - keep_observed_jwks: bool, - }, -} - -pub fn generate_oidc_provider_ops_proposal( - ops: &[OidcProviderOp], - is_testnet: bool, - next_execution_hash: Vec, -) -> anyhow::Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::jwks"], - |writer| { - for op in ops { - write_op(writer, signer_arg, op); - } - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("oidc-provider-ops".to_string(), proposal)); - Ok(result) -} - -fn write_op(writer: &CodeWriter, signer_arg: &str, op: &OidcProviderOp) { - match op { - OidcProviderOp::Upsert { issuer, config_url } => { - emitln!( - writer, - "jwks::upsert_oidc_provider_for_next_epoch({}, b\"{}\", b\"{}\");", - signer_arg, - issuer, - config_url - ); - }, - OidcProviderOp::Remove { - issuer, - keep_observed_jwks, - } => { - emitln!( - writer, - "jwks::remove_oidc_provider_for_next_epoch({}, b\"{}\");", - signer_arg, - issuer - ); - if !keep_observed_jwks { - emitln!( - writer, - "jwks::remove_issuer_from_observed_jwks({}, b\"{}\");", - signer_arg, - issuer - ); - } - }, - } -} diff --git a/aptos-move/aptos-release-builder/src/components/randomness_config.rs b/aptos-move/aptos-release-builder/src/components/randomness_config.rs deleted file mode 100644 index d560c80e5fc31..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/randomness_config.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{components::get_signer_arg, utils::generate_governance_proposal}; -use aptos_types::on_chain_config::OnChainRandomnessConfig; -use move_model::{code_writer::CodeWriter, emitln, model::Loc}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum ReleaseFriendlyRandomnessConfig { - Off, - V1 { - secrecy_threshold_in_percentage: u64, - reconstruct_threshold_in_percentage: u64, - }, -} - -impl From for OnChainRandomnessConfig { - fn from(value: ReleaseFriendlyRandomnessConfig) -> Self { - match value { - ReleaseFriendlyRandomnessConfig::Off => OnChainRandomnessConfig::Off, - ReleaseFriendlyRandomnessConfig::V1 { - secrecy_threshold_in_percentage, - reconstruct_threshold_in_percentage, - } => OnChainRandomnessConfig::new_v1( - secrecy_threshold_in_percentage, - reconstruct_threshold_in_percentage, - ), - } - } -} - -pub fn generate_randomness_config_update_proposal( - config: &ReleaseFriendlyRandomnessConfig, - is_testnet: bool, - next_execution_hash: Vec, -) -> anyhow::Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &[ - "aptos_framework::randomness_config", - "aptos_std::fixed_point64", - ], - |writer| { - match config { - ReleaseFriendlyRandomnessConfig::Off => { - emitln!( - writer, - "randomness_config::set_for_next_epoch({}, randomness_config::new_off());", - signer_arg - ); - }, - ReleaseFriendlyRandomnessConfig::V1 { - secrecy_threshold_in_percentage, - reconstruct_threshold_in_percentage, - } => { - emitln!(writer, "let v1 = randomness_config::new_v1("); - emitln!( - writer, - " fixed_point64::create_from_rational({}, 100),", - secrecy_threshold_in_percentage - ); - emitln!( - writer, - " fixed_point64::create_from_rational({}, 100),", - reconstruct_threshold_in_percentage - ); - emitln!(writer, ");"); - emitln!( - writer, - "randomness_config::set_for_next_epoch({}, v1);", - signer_arg - ); - }, - } - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("randomness-config".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/components/transaction_fee.rs b/aptos-move/aptos-release-builder/src/components/transaction_fee.rs deleted file mode 100644 index 9b4b17dce06c7..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/transaction_fee.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils::*; -use anyhow::Result; -use move_model::{code_writer::CodeWriter, emitln, model::Loc}; - -pub fn generate_fee_distribution_proposal( - function_name: String, - burn_percentage: u8, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash, - &["aptos_framework::transaction_fee"], - |writer| { - emitln!( - writer, - "transaction_fee::{}(framework_signer, {});", - function_name, - burn_percentage, - ); - }, - ); - - result.push(("transaction_fee".to_string(), proposal)); - Ok(result) -} - -pub fn generate_proposal_to_initialize_fee_collection_and_distribution( - burn_percentage: u8, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - generate_fee_distribution_proposal( - "initialize_fee_collection_and_distribution".to_string(), - burn_percentage, - is_testnet, - next_execution_hash, - ) -} - -pub fn generate_proposal_to_upgrade_burn_percentage( - burn_percentage: u8, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - generate_fee_distribution_proposal( - "upgrade_burn_percentage".to_string(), - burn_percentage, - is_testnet, - next_execution_hash, - ) -} diff --git a/aptos-move/aptos-release-builder/src/components/version.rs b/aptos-move/aptos-release-builder/src/components/version.rs deleted file mode 100644 index 4a7d1e6f4fe41..0000000000000 --- a/aptos-move/aptos-release-builder/src/components/version.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{components::get_signer_arg, utils::*}; -use anyhow::Result; -use aptos_types::on_chain_config::Version; -use move_model::{code_writer::CodeWriter, emitln, model::Loc}; - -pub fn generate_version_upgrade_proposal( - version: &Version, - is_testnet: bool, - next_execution_hash: Vec, -) -> Result> { - let signer_arg = get_signer_arg(is_testnet, &next_execution_hash); - let mut result = vec![]; - - let writer = CodeWriter::new(Loc::default()); - - let proposal = generate_governance_proposal( - &writer, - is_testnet, - next_execution_hash.clone(), - &["aptos_framework::version"], - |writer| { - emitln!( - writer, - "version::set_for_next_epoch({}, {});", - signer_arg, - version.major, - ); - emitln!(writer, "aptos_governance::reconfigure({});", signer_arg); - }, - ); - - result.push(("version".to_string(), proposal)); - Ok(result) -} diff --git a/aptos-move/aptos-release-builder/src/lib.rs b/aptos-move/aptos-release-builder/src/lib.rs deleted file mode 100644 index b53dd7006f106..0000000000000 --- a/aptos-move/aptos-release-builder/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod components; -mod utils; -pub mod validate; - -pub use components::{ExecutionMode, ReleaseConfig, ReleaseEntry}; -use once_cell::sync::{Lazy, OnceCell}; -use std::{ - env, - path::{Path, PathBuf}, -}; - -// Update me after branch cut. -const RELEASE_CONFIG: &str = include_str!("../data/release.yaml"); - -static CURRENT_RELEASE_CONFIG: Lazy = - Lazy::new(|| ReleaseConfig::parse(RELEASE_CONFIG).expect("YAML NOT PARSABLE")); - -/// Returns the release bundle with which the last testnet was build or updated. -pub fn current_release_config() -> &'static ReleaseConfig { - &CURRENT_RELEASE_CONFIG -} - -static APTOS_CORE_PATH: OnceCell = OnceCell::new(); - -fn aptos_core_path_at_compile_time() -> PathBuf { - let mut path = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - path.pop(); - path.pop(); - path = path.canonicalize().unwrap(); - path -} - -pub fn initialize_aptos_core_path(overriden_path: Option) { - if let Some(path) = overriden_path { - APTOS_CORE_PATH.set(path).unwrap(); - } else { - APTOS_CORE_PATH - .set(aptos_core_path_at_compile_time()) - .unwrap(); - }; -} - -pub(crate) fn aptos_core_path() -> PathBuf { - APTOS_CORE_PATH - .get_or_init(aptos_core_path_at_compile_time) - .clone() -} - -pub(crate) fn aptos_framework_path() -> PathBuf { - let mut path = aptos_core_path(); - path.push("aptos-move/framework/aptos-framework"); - path -} diff --git a/aptos-move/aptos-release-builder/src/main.rs b/aptos-move/aptos-release-builder/src/main.rs deleted file mode 100644 index c8a02ca7e7100..0000000000000 --- a/aptos-move/aptos-release-builder/src/main.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Context; -use aptos_crypto::{ed25519::Ed25519PrivateKey, ValidCryptoMaterialStringExt}; -use aptos_framework::natives::code::PackageRegistry; -use aptos_release_builder::{ - components::fetch_config, - initialize_aptos_core_path, - validate::{DEFAULT_RESOLUTION_TIME, FAST_RESOLUTION_TIME}, -}; -use aptos_types::{ - account_address::AccountAddress, - chain_id::ChainId, - jwks::{ObservedJWKs, SupportedOIDCProviders}, -}; -use clap::{Parser, Subcommand}; -use std::path::PathBuf; - -#[derive(Parser)] -pub struct Argument { - #[clap(subcommand)] - cmd: Commands, - #[clap(long)] - aptos_core_path: Option, -} - -#[derive(Subcommand, Debug)] -pub enum Commands { - /// Generate sets of governance proposals based on the release_config file passed in - GenerateProposals { - #[clap(short, long)] - release_config: PathBuf, - #[clap(short, long)] - output_dir: PathBuf, - }, - /// Generate sets of governance proposals with default release config. - WriteDefault { - #[clap(short, long)] - output_path: PathBuf, - }, - /// Execute governance proposals generated from a given release config. - ValidateProposals { - /// Path to the config to be released. - #[clap(short, long)] - release_config: PathBuf, - #[clap(short, long)] - endpoint: url::Url, - #[clap(long)] - framework_git_rev: Option, - /// Set this value if you want to get the generated proposal at the same time. - #[clap(long)] - output_dir: Option, - #[clap(subcommand)] - input_option: InputOptions, - /// Mint to validator such that it has enough stake to allow fast voting resolution. - #[clap(long)] - mint_to_validator: bool, - }, - /// Print out current values of on chain configs. - PrintConfigs { - /// Url endpoint for the desired network. e.g: https://fullnode.mainnet.aptoslabs.com/v1. - #[clap(short, long)] - endpoint: url::Url, - /// Whether to print out the full gas schedule. - #[clap(short, long)] - print_gas_schedule: bool, - }, - /// Print out package metadata. - /// Usage: --endpoint '' - /// --package-address
    --package-name [--print-json] - PrintPackageMetadata { - /// Url endpoint for the desired network. e.g: https://fullnode.mainnet.aptoslabs.com/v1. - #[clap(short, long)] - endpoint: url::Url, - /// The address under which the package is published - #[clap(long)] - package_address: String, - /// The name of the package - #[clap(long)] - package_name: String, - /// Whether to print the original data in json - #[clap(long)] - print_json: bool, - }, -} - -#[derive(Subcommand, Debug)] -pub enum InputOptions { - FromDirectory { - /// Path to the local testnet folder. If you are running local testnet via cli, it should be `.aptos/testnet`. - #[clap(short, long)] - test_dir: PathBuf, - }, - FromArgs { - /// Hex encoded string for the root key of the network. - #[clap(long)] - root_key: String, - /// Hex encoded string for the address of a validator node. - #[clap(long)] - validator_address: String, - /// Hex encoded string for the private key of a validator node. - #[clap(long)] - validator_key: String, - }, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = Argument::parse(); - initialize_aptos_core_path(args.aptos_core_path.clone()); - - // TODO: Being able to parse the release config from a TOML file to generate the proposals. - match args.cmd { - Commands::GenerateProposals { - release_config, - output_dir, - } => { - aptos_release_builder::ReleaseConfig::load_config(release_config.as_path()) - .with_context(|| "Failed to load release config".to_string())? - .generate_release_proposal_scripts(output_dir.as_path()) - .with_context(|| "Failed to generate release proposal scripts".to_string())?; - Ok(()) - }, - Commands::WriteDefault { output_path } => { - aptos_release_builder::ReleaseConfig::default().save_config(output_path.as_path()) - }, - Commands::ValidateProposals { - release_config, - input_option, - endpoint, - framework_git_rev, - mint_to_validator, - output_dir, - } => { - let config = - aptos_release_builder::ReleaseConfig::load_config(release_config.as_path())?; - - let root_key_path = aptos_temppath::TempPath::new(); - root_key_path.create_as_file()?; - - let mut network_config = match input_option { - InputOptions::FromDirectory { test_dir } => { - aptos_release_builder::validate::NetworkConfig::new_from_dir( - endpoint.clone(), - test_dir.as_path(), - )? - }, - InputOptions::FromArgs { - root_key, - validator_address, - validator_key, - } => { - let root_key = Ed25519PrivateKey::from_encoded_string(&root_key)?; - let validator_key = Ed25519PrivateKey::from_encoded_string(&validator_key)?; - let validator_account = AccountAddress::from_hex(validator_address.as_bytes())?; - - let mut root_key_path = root_key_path.path().to_path_buf(); - root_key_path.set_extension("key"); - - std::fs::write(root_key_path.as_path(), bcs::to_bytes(&root_key)?)?; - - aptos_release_builder::validate::NetworkConfig { - root_key_path, - validator_account, - validator_key, - framework_git_rev: None, - endpoint: endpoint.clone(), - } - }, - }; - - network_config.framework_git_rev = framework_git_rev; - - if mint_to_validator { - let chain_id = aptos_rest_client::Client::new(endpoint) - .get_ledger_information() - .await? - .inner() - .chain_id; - - if chain_id == ChainId::mainnet().id() || chain_id == ChainId::testnet().id() { - panic!("Mint to mainnet/testnet is not allowed"); - } - - network_config.mint_to_validator().await?; - } - - network_config - .set_fast_resolve(FAST_RESOLUTION_TIME) - .await?; - aptos_release_builder::validate::validate_config_and_generate_release( - config, - network_config.clone(), - output_dir, - ) - .await?; - // Reset resolution time back to normal after resolution - network_config - .set_fast_resolve(DEFAULT_RESOLUTION_TIME) - .await?; - Ok(()) - }, - Commands::PrintConfigs { - endpoint, - print_gas_schedule, - } => { - use aptos_types::on_chain_config::*; - - let client = aptos_rest_client::Client::new(endpoint); - - macro_rules! print_configs { - ($($type:ty), *) => { - $( - println!("{}", std::any::type_name::<$type>()); - println!("{}", serde_yaml::to_string(&fetch_config::<$type>(&client)?)?); - )* - } - } - - print_configs!(OnChainConsensusConfig, OnChainExecutionConfig, Version); - - if print_gas_schedule { - print_configs!(GasScheduleV2, StorageGasSchedule); - } - - // Print Activated Features - let features = fetch_config::(&client)?; - println!( - "Features\n{}", - serde_yaml::to_string( - &aptos_release_builder::components::feature_flags::Features::from(&features) - )? - ); - - let oidc_providers = fetch_config::(&client); - let observed_jwks = fetch_config::(&client); - let jwk_consensus_config = fetch_config::(&client); - let randomness_config = fetch_config::(&client) - .and_then(OnChainRandomnessConfig::try_from); - println!(); - println!("SupportedOIDCProviders"); - println!("{:?}", oidc_providers); - println!(); - println!("ObservedJWKs"); - println!("{:?}", observed_jwks); - println!(); - println!("JWKConsensusConfig"); - println!("{:?}", jwk_consensus_config); - println!(); - println!("RandomnessConfig"); - println!("{:?}", randomness_config); - Ok(()) - }, - Commands::PrintPackageMetadata { - endpoint, - package_address, - package_name, - print_json, - } => { - let client = aptos_rest_client::Client::new(endpoint); - let address = AccountAddress::from_str_strict(&package_address)?; - let packages = client - .get_account_resource_bcs::(address, "0x1::code::PackageRegistry") - .await?; - for package in packages.into_inner().packages { - if package.name == package_name { - if print_json { - println!("{}", serde_json::to_string(&package).unwrap()); - } else { - println!("{}", package); - } - break; - } - } - Ok(()) - }, - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Argument::command().debug_assert() -} diff --git a/aptos-move/aptos-release-builder/src/utils.rs b/aptos-move/aptos-release-builder/src/utils.rs deleted file mode 100644 index 2d7a2c53d674a..0000000000000 --- a/aptos-move/aptos-release-builder/src/utils.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use move_core_types::account_address::AccountAddress; -use move_model::{code_writer::CodeWriter, emit, emitln}; - -pub(crate) fn generate_blob(writer: &CodeWriter, data: &[u8]) { - emitln!(writer, "vector["); - writer.indent(); - for (i, b) in data.iter().enumerate() { - if i % 20 == 0 { - if i > 0 { - emitln!(writer); - } - } else { - emit!(writer, " "); - } - emit!(writer, "{},", b); - } - emitln!(writer); - writer.unindent(); - emit!(writer, "]") -} - -pub(crate) fn generate_next_execution_hash_blob( - writer: &CodeWriter, - for_address: AccountAddress, - next_execution_hash: Vec, -) { - if next_execution_hash == "vector::empty()".as_bytes() { - emitln!( - writer, - "let framework_signer = aptos_governance::resolve_multi_step_proposal(proposal_id, @{}, {});\n", - for_address, - "vector::empty()", - ); - } else { - println!("{:?}", next_execution_hash); - emitln!( - writer, - "let framework_signer = aptos_governance::resolve_multi_step_proposal(" - ); - writer.indent(); - emitln!(writer, "proposal_id,"); - emitln!(writer, "@{},", for_address); - emit!(writer, "vector["); - for b in next_execution_hash.iter() { - emit!(writer, "{}u8,", b); - } - emitln!(writer, "],"); - writer.unindent(); - emitln!(writer, ");"); - } -} - -pub(crate) fn generate_governance_proposal_header( - writer: &CodeWriter, - deps_names: &[&str], - is_multi_step: bool, - next_execution_hash: Vec, -) { - emitln!(writer, "script {"); - writer.indent(); - - emitln!(writer, "use aptos_framework::aptos_governance;"); - for deps_name in deps_names { - emitln!(writer, "use {};", deps_name); - } - if next_execution_hash == "vector::empty()".as_bytes() { - emitln!(writer, "use std::vector;"); - } - emitln!(writer); - - emitln!(writer, "fun main(proposal_id: u64) {"); - writer.indent(); - - if is_multi_step && !next_execution_hash.is_empty() { - generate_next_execution_hash_blob(writer, AccountAddress::ONE, next_execution_hash); - } else { - emitln!( - writer, - "let framework_signer = aptos_governance::resolve(proposal_id, @{});\n", - AccountAddress::ONE, - ); - } -} - -pub(crate) fn generate_testnet_header(writer: &CodeWriter, deps_names: &[&str]) { - emitln!(writer, "script {"); - writer.indent(); - - emitln!(writer, "use aptos_framework::aptos_governance;"); - for deps_name in deps_names { - emitln!(writer, "use {};", deps_name); - } - emitln!(writer); - - emitln!(writer, "fun main(core_resources: &signer) {"); - writer.indent(); - - emitln!( - writer, - "let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @{});\n", - AccountAddress::ONE, - ); - emitln!(writer, "let framework_signer = &core_signer;\n"); -} - -pub(crate) fn finish_with_footer(writer: &CodeWriter) -> String { - writer.unindent(); - emitln!(writer, "}"); - - writer.unindent(); - emitln!(writer, "}"); - - writer.process_result(|s| s.to_string()) -} - -pub(crate) fn generate_governance_proposal( - writer: &CodeWriter, - is_testnet: bool, - next_execution_hash: Vec, - deps_names: &[&str], - body: F, -) -> String -where - F: FnOnce(&CodeWriter), -{ - if next_execution_hash.is_empty() { - if is_testnet { - generate_testnet_header(writer, deps_names); - } else { - generate_governance_proposal_header( - writer, - deps_names, - false, - "".to_owned().into_bytes(), - ); - } - } else { - generate_governance_proposal_header(writer, deps_names, true, next_execution_hash); - }; - - body(writer); - finish_with_footer(writer) -} diff --git a/aptos-move/aptos-release-builder/src/validate.rs b/aptos-move/aptos-release-builder/src/validate.rs deleted file mode 100644 index 96aea9f9e9ddc..0000000000000 --- a/aptos-move/aptos-release-builder/src/validate.rs +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{aptos_framework_path, components::ProposalMetadata, ExecutionMode, ReleaseConfig}; -use anyhow::Result; -use aptos::{ - common::types::CliCommand, - governance::{ExecuteProposal, SubmitProposal, SubmitVote}, - move_tool::{RunFunction, RunScript}, - stake::IncreaseLockup, -}; -use aptos_api_types::U64; -use aptos_crypto::ed25519::Ed25519PrivateKey; -use aptos_genesis::keys::PrivateIdentity; -use aptos_rest_client::Client; -use aptos_temppath::TempPath; -use aptos_types::account_address::AccountAddress; -use clap::Parser; -use serde::Deserialize; -use std::{ - fs, - path::{Path, PathBuf}, - thread::sleep, - time::Duration, -}; -use url::Url; - -pub const FAST_RESOLUTION_TIME: u64 = 30; -pub const DEFAULT_RESOLUTION_TIME: u64 = 43200; - -#[derive(Clone, Debug)] -pub struct NetworkConfig { - pub endpoint: Url, - pub root_key_path: PathBuf, - pub validator_account: AccountAddress, - pub validator_key: Ed25519PrivateKey, - pub framework_git_rev: Option, -} - -#[derive(Deserialize)] -struct CreateProposalEvent { - proposal_id: U64, -} - -impl NetworkConfig { - pub fn new_from_dir(endpoint: Url, test_dir: &Path) -> Result { - let root_key_path = test_dir.join("mint.key"); - let private_identity_file = test_dir.join("0/private-identity.yaml"); - let private_identity = - serde_yaml::from_slice::(&fs::read(private_identity_file)?)?; - - Ok(Self { - endpoint, - root_key_path, - validator_account: private_identity.account_address, - validator_key: private_identity.account_private_key, - framework_git_rev: None, - }) - } - - // ED25519 Private keys have a very silly to_string that returns what looks - // like a debug output: - // - // Which is almost never what you want unless you're debugging... - // Usually you want the hex encoded version, although you wanna be careful - // you're not accidentally leaking private keys, but thats a separate issue - pub fn get_hex_encoded_validator_key(&self) -> String { - hex::encode(self.validator_key.to_bytes()) - } - - /// Submit all govenerance proposal script inside script_path to the corresponding rest endpoint. - /// - /// For all script, we will: - /// - Generate a governance proposal and get its proposal id - /// - Use validator's privkey to vote for this proposal - /// - Add the proposal to allow list using validator account - /// - Execute this proposal - /// - /// We expect all the scripts here to be single step governance proposal. - pub async fn submit_and_execute_proposal( - &self, - metadata: &ProposalMetadata, - script_path: Vec, - ) -> Result<()> { - let mut proposals = vec![]; - for path in script_path.iter() { - let proposal_id = self - .create_governance_proposal(path.as_path(), metadata, false) - .await?; - self.vote_proposal(proposal_id).await?; - proposals.push(proposal_id); - } - - // Wait for the voting period to pass - sleep(Duration::from_secs(40)); - for (proposal_id, path) in proposals.iter().zip(script_path.iter()) { - self.add_proposal_to_allow_list(*proposal_id).await?; - self.execute_proposal(*proposal_id, path.as_path()).await?; - } - Ok(()) - } - - /// Submit all govenerance proposal script inside script_path to the corresponding rest endpoint. - /// - /// - We will first submit a governance proposal for the first script (in alphabetical order). - /// - Validator will vote for this proposal - /// - /// Once voting period has passed, we should be able to execute all the scripts in the folder in alphabetical order. - /// We expect all the scripts here to be multi step governance proposal. - pub async fn submit_and_execute_multi_step_proposal( - &self, - metadata: &ProposalMetadata, - script_path: Vec, - ) -> Result<()> { - let first_script = script_path.first().unwrap(); - let proposal_id = self - .create_governance_proposal(first_script.as_path(), metadata, true) - .await?; - self.vote_proposal(proposal_id).await?; - // Wait for the proposal to resolve. - sleep(Duration::from_secs(40)); - for path in script_path { - self.add_proposal_to_allow_list(proposal_id).await?; - self.execute_proposal(proposal_id, path.as_path()).await?; - } - Ok(()) - } - - /// Change the time for a network to resolve governance proposal - pub async fn set_fast_resolve(&self, resolution_time: u64) -> Result<()> { - let fast_resolve_script = aptos_temppath::TempPath::new(); - fast_resolve_script.create_as_file()?; - let mut fas_script_path = fast_resolve_script.path().to_path_buf(); - fas_script_path.set_extension("move"); - - std::fs::write(fas_script_path.as_path(), format!(r#" - script {{ - use aptos_framework::aptos_governance; - - fun main(core_resources: &signer) {{ - let core_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - - let framework_signer = &core_signer; - - aptos_governance::update_governance_config(framework_signer, 0, 0, {}); - }} - }} - "#, resolution_time).as_bytes())?; - - let mut args = vec![ - "", - "--script-path", - fas_script_path.as_path().to_str().unwrap(), - "--sender-account", - "0xa550c18", - "--private-key-file", - self.root_key_path.as_path().to_str().unwrap(), - "--assume-yes", - "--encoding", - "bcs", - "--url", - self.endpoint.as_str(), - ]; - let rev = self.framework_git_rev.clone(); - let framework_path = aptos_framework_path(); - if let Some(rev) = &rev { - args.push("--framework-git-rev"); - args.push(rev.as_str()); - } else { - args.push("--framework-local-dir"); - args.push(framework_path.as_os_str().to_str().unwrap()); - }; - - RunScript::try_parse_from(args)?.execute().await?; - Ok(()) - } - - pub async fn create_governance_proposal( - &self, - script_path: &Path, - metadata: &ProposalMetadata, - is_multi_step: bool, - ) -> Result { - println!("Creating proposal: {:?}", script_path); - - let address_string = format!("{}", self.validator_account); - let privkey_string = self.get_hex_encoded_validator_key(); - - let metadata_path = TempPath::new(); - metadata_path.create_as_file()?; - fs::write( - metadata_path.path(), - serde_json::to_string_pretty(metadata)?, - )?; - - let mut args = vec![ - "", - "--pool-address", - address_string.as_str(), - "--script-path", - script_path.to_str().unwrap(), - "--metadata-path", - metadata_path.path().to_str().unwrap(), - "--metadata-url", - "https://raw.githubusercontent.com/aptos-labs/aptos-core/b4fb9acfc297327c43d030def2b59037c4376611/testsuite/smoke-test/src/upgrade_multi_step_test_metadata.txt", - "--sender-account", - address_string.as_str(), - "--private-key", - privkey_string.as_str(), - "--url", - self.endpoint.as_str(), - "--assume-yes", - ]; - - if is_multi_step { - args.push("--is-multi-step"); - } - - let rev_string = self.framework_git_rev.clone(); - let framework_path = aptos_framework_path(); - if let Some(rev) = &rev_string { - args.push("--framework-git-rev"); - args.push(rev.as_str()); - SubmitProposal::try_parse_from(args)?.execute().await?; - } else { - args.push("--framework-local-dir"); - args.push(framework_path.as_os_str().to_str().unwrap()); - SubmitProposal::try_parse_from(args)?.execute().await?; - }; - - // Get proposal id. - let event = Client::new(self.endpoint.clone()) - .get_account_events( - AccountAddress::ONE, - "0x1::aptos_governance::GovernanceEvents", - "create_proposal_events", - None, - Some(1), - ) - .await? - .into_inner() - .pop() - .unwrap(); - - Ok(*serde_json::from_value::(event.data)? - .proposal_id - .inner()) - } - - pub async fn vote_proposal(&self, proposal_id: u64) -> Result<()> { - println!("Voting proposal id {:?}", proposal_id); - - let address_string = format!("{}", self.validator_account); - let privkey_string = self.get_hex_encoded_validator_key(); - let proposal_id = format!("{}", proposal_id); - - let args = vec![ - "", - "--pool-addresses", - address_string.as_str(), - "--sender-account", - address_string.as_str(), - "--private-key", - privkey_string.as_str(), - "--assume-yes", - "--proposal-id", - proposal_id.as_str(), - "--yes", - "--url", - self.endpoint.as_str(), - ]; - - SubmitVote::try_parse_from(args)?.execute().await?; - Ok(()) - } - - pub async fn mint_to_validator(&self) -> Result<()> { - let address_args = format!("address:{}", self.validator_account); - - println!("Minting to validator account"); - let args = vec![ - "", - "--function-id", - "0x1::aptos_coin::mint", - "--sender-account", - "0xa550c18", - "--args", - address_args.as_str(), - "u64:100000000000", - "--private-key-file", - self.root_key_path.as_path().to_str().unwrap(), - "--assume-yes", - "--encoding", - "bcs", - "--url", - self.endpoint.as_str(), - ]; - - RunFunction::try_parse_from(args)?.execute().await?; - Ok(()) - } - - pub async fn add_proposal_to_allow_list(&self, proposal_id: u64) -> Result<()> { - let proposal_id = format!("u64:{}", proposal_id); - - let args = vec![ - "", - "--function-id", - "0x1::aptos_governance::add_approved_script_hash_script", - "--sender-account", - "0xa550c18", - "--args", - proposal_id.as_str(), - "--private-key-file", - self.root_key_path.as_path().to_str().unwrap(), - "--assume-yes", - "--encoding", - "bcs", - "--url", - self.endpoint.as_str(), - ]; - RunFunction::try_parse_from(args)?.execute().await?; - Ok(()) - } - - pub async fn execute_proposal(&self, proposal_id: u64, script_path: &Path) -> Result<()> { - println!( - "Executing: {:?} at proposal id {:?}", - script_path, proposal_id - ); - - let address_string = format!("{}", self.validator_account); - let privkey_string = self.get_hex_encoded_validator_key(); - let proposal_id = format!("{}", proposal_id); - - let mut args = vec![ - "", - "--proposal-id", - proposal_id.as_str(), - "--script-path", - script_path.to_str().unwrap(), - "--sender-account", - address_string.as_str(), - "--private-key", - privkey_string.as_str(), - "--assume-yes", - "--url", - self.endpoint.as_str(), - // Use the max gas unit for now. The simulate API sometimes cannot get the right gas estimate for proposals. - "--max-gas", - "2000000", - ]; - - let rev = self.framework_git_rev.clone(); - let framework_path = aptos_framework_path(); - if let Some(rev) = &rev { - args.push("--framework-git-rev"); - args.push(rev.as_str()); - } else { - args.push("--framework-local-dir"); - args.push(framework_path.as_os_str().to_str().unwrap()); - }; - - ExecuteProposal::try_parse_from(args)?.execute().await?; - Ok(()) - } - - async fn increase_lockup(&self) -> Result<()> { - let validator_account = self.validator_account.to_string(); - let validator_key = self.get_hex_encoded_validator_key(); - let args = vec![ - // Ahhhhh this first empty string is very important - // parse_from requires argv[0] - "", - "--sender-account", - validator_account.as_str(), - "--private-key", - validator_key.as_str(), - "--url", - self.endpoint.as_str(), - "--assume-yes", - ]; - IncreaseLockup::try_parse_from(args)?.execute().await?; - Ok(()) - } -} - -async fn execute_release( - release_config: ReleaseConfig, - network_config: NetworkConfig, - output_dir: Option, - validate_release: bool, -) -> Result<()> { - let scripts_path = TempPath::new(); - scripts_path.create_as_dir()?; - - let proposal_folder = if let Some(dir) = &output_dir { - dir.as_path() - } else { - scripts_path.path() - }; - release_config.generate_release_proposal_scripts(proposal_folder)?; - - network_config.increase_lockup().await?; - - // Execute proposals - for proposal in &release_config.proposals { - let mut proposal_path = proposal_folder.to_path_buf(); - proposal_path.push("sources"); - proposal_path.push(&release_config.name); - proposal_path.push(proposal.name.as_str()); - - let mut script_paths: Vec = std::fs::read_dir(proposal_path.as_path())? - .filter_map(|entry| entry.ok()) - .filter_map(|entry| { - let path = entry.path(); - if path.extension().map(|s| s == "move").unwrap_or(false) { - Some(path) - } else { - None - } - }) - .collect(); - - script_paths.sort(); - - match proposal.execution_mode { - ExecutionMode::MultiStep => { - network_config.set_fast_resolve(30).await?; - network_config - .submit_and_execute_multi_step_proposal(&proposal.metadata, script_paths) - .await?; - - network_config.set_fast_resolve(43200).await?; - }, - ExecutionMode::RootSigner => { - for entry in script_paths { - println!("Executing: {:?}", entry); - let mut args = vec![ - "", - "--script-path", - entry.as_path().to_str().unwrap(), - "--sender-account", - "0xa550c18", - "--private-key-file", - network_config.root_key_path.as_path().to_str().unwrap(), - "--assume-yes", - "--encoding", - "bcs", - "--url", - network_config.endpoint.as_str(), - ]; - - let rev = network_config.framework_git_rev.clone(); - let framework_path = aptos_framework_path(); - if let Some(rev) = &rev { - args.push("--framework-git-rev"); - args.push(rev.as_str()); - } else { - args.push("--framework-local-dir"); - args.push(framework_path.as_os_str().to_str().unwrap()); - }; - - RunScript::try_parse_from(args)?.execute().await?; - } - }, - }; - if validate_release { - release_config.validate_upgrade(&network_config.endpoint, proposal)?; - } - } - Ok(()) -} - -pub async fn validate_config( - release_config: ReleaseConfig, - network_config: NetworkConfig, -) -> Result<()> { - validate_config_and_generate_release(release_config, network_config, None).await -} - -pub async fn validate_config_and_generate_release( - release_config: ReleaseConfig, - network_config: NetworkConfig, - output_dir: Option, -) -> Result<()> { - execute_release( - release_config.clone(), - network_config.clone(), - output_dir, - true, - ) - .await -} - -#[cfg(test)] -pub mod test { - use super::NetworkConfig; - use aptos_crypto::PrivateKey; - use aptos_keygen::KeyGen; - use aptos_types::transaction::authenticator::AuthenticationKey; - - #[tokio::test] - pub async fn test_network_config() { - let seed_slice = [0u8; 32]; - let mut keygen = KeyGen::from_seed(seed_slice); - let validator_key = keygen.generate_ed25519_private_key(); - let validator_account = - AuthenticationKey::ed25519(&validator_key.public_key()).account_address(); - - let network_info = NetworkConfig { - endpoint: "https://banana.com/".parse().unwrap(), - root_key_path: "".into(), - validator_account, - validator_key, - framework_git_rev: None, - }; - - let private_key_string = network_info.get_hex_encoded_validator_key(); - assert_eq!( - private_key_string.as_str(), - "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" - ); - } -} diff --git a/aptos-move/aptos-vm-benchmarks/Cargo.toml b/aptos-move/aptos-vm-benchmarks/Cargo.toml deleted file mode 100644 index e7cce2decde8f..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "aptos-vm-benchmarks" -version = "0.1.0" -edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -aptos = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-framework = { workspace = true } -aptos-language-e2e-tests = { workspace = true } -aptos-types = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -move-binary-format = { workspace = true } -move-bytecode-source-map = { workspace = true } -move-core-types = { workspace = true } diff --git a/aptos-move/aptos-vm-benchmarks/README.md b/aptos-move/aptos-vm-benchmarks/README.md deleted file mode 100644 index 09522941a2092..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/README.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -id: Aptos-vm-benchmarks -title: Aptos VM Benchmarks ---- - -## Aptos VM Benchmarks - -The Aptos VM Benchmark allows anyone to specify a set of Move modules and benchmark -the running time of transactions in the AptosVM measured in milliseconds. The Aptos -VM Benchmark allows an arbitrary amount of packages to be benchmarked. - -## Usage - -Move packages should be placed under the `/samples` directory. In order to benchmark -a transaction, the function must be marked with `entry` and have a prefix of `benchmark`. -For example, the following functions would work: - -```Move - -//// acceptable formats -public entry fun benchmark() {} - -public entry fun benchmark_another_txn() {} - -public entry fun benchmark123() {} - -//// inacceptable formats -public fun benchmark() {} - -public fun test_my_txn() {} - -public entry fun benchmark_addition(_x: u64, _y: u64) {} - -``` - -To run the benchmarks, simply run `cargo run`. There is also support for filtering specific -tests to run. If no pattern is provided, it will run all the benchmarks. - -```Bash -cargo run --release PATTERN -``` - -## Adding a benchmark - -``` -cd aptos-vm-benchmarks/samples -mkdir proj-name -cd proj-name -cargo run -p aptos -- move init --name proj-name -``` - -## Examples - -Two basics examples are included under the `/samples` directory to demonstrate how the -crate works: `/samples/add-numbers` and `/samples/do-nothing`. The `do-nothing` package -demonstrates basic usage, as well as situations where the benchmark script will complain. - -Additionally, `add-numbers` shows a simple implementation of a Move module and how one -might benchmark it. \ No newline at end of file diff --git a/aptos-move/aptos-vm-benchmarks/samples/add-numbers/Move.toml b/aptos-move/aptos-vm-benchmarks/samples/add-numbers/Move.toml deleted file mode 100644 index 2cf87321920de..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/samples/add-numbers/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = 'add-numbers' -version = '1.0.0' -[dependencies.AptosFramework] -local = '../../../..' -subdir = 'aptos-move/framework/aptos-framework' diff --git a/aptos-move/aptos-vm-benchmarks/samples/add-numbers/sources/AddNumbers.move b/aptos-move/aptos-vm-benchmarks/samples/add-numbers/sources/AddNumbers.move deleted file mode 100644 index c8c18b4526693..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/samples/add-numbers/sources/AddNumbers.move +++ /dev/null @@ -1,11 +0,0 @@ -module 0xcafe::test1 { - // implementation - public fun add_numbers(x: u64, y: u64): u64 { - x + y - } - - // now we want to benchmark our implementation - public entry fun benchmark_add_numbers() { - add_numbers(5,5); - } -} diff --git a/aptos-move/aptos-vm-benchmarks/samples/do-nothing/Move.toml b/aptos-move/aptos-vm-benchmarks/samples/do-nothing/Move.toml deleted file mode 100644 index aedf01f521f95..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/samples/do-nothing/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = 'do-nothing' -version = '1.0.0' -[dependencies] -AptosFramework = { local = "../../../framework/aptos-framework" } -AptosStdlib = { local = "../../../framework/aptos-stdlib" } \ No newline at end of file diff --git a/aptos-move/aptos-vm-benchmarks/samples/do-nothing/sources/DoNothing.move b/aptos-move/aptos-vm-benchmarks/samples/do-nothing/sources/DoNothing.move deleted file mode 100644 index 04d8fdf45d4ea..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/samples/do-nothing/sources/DoNothing.move +++ /dev/null @@ -1,27 +0,0 @@ -// test.move -module 0xbeef::test_entry_cases { - // this will run - public entry fun benchmark() { - } - - // this is not marked as entry so it will not run - public fun benchmark1() { - } -} - -// test multiple modules in a package -module 0xbeef::test_with_params { - // this will run - public entry fun benchmark_test1() { - } - - // this will give a warning, no params allowed - public entry fun benchmark_test2(_x: u64) { - } -} - -module 0xbeef::test_diff_names { - // this will run, but convention is snake case - public entry fun benchmark1() { - } -} diff --git a/aptos-move/aptos-vm-benchmarks/src/helper.rs b/aptos-move/aptos-vm-benchmarks/src/helper.rs deleted file mode 100644 index aea964f3e20e2..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/src/helper.rs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_cached_packages::aptos_stdlib; -use aptos_framework::BuiltPackage; -use aptos_language_e2e_tests::{ - account::Account, - executor::{ExecFuncTimerDynamicArgs, FakeExecutor, GasMeterType}, -}; -use aptos_types::{move_utils::MemberId, transaction::TransactionPayload}; -use move_binary_format::CompiledModule; -use move_core_types::{account_address::AccountAddress, language_storage::ModuleId}; -use std::{fs::ReadDir, path::PathBuf, string::String, time::Instant}; - -// CONSTANTS -const PREFIX: &str = "benchmark"; - -// generate a TransactionPayload for modules -pub fn generate_module_payload(package: &BuiltPackage) -> TransactionPayload { - // extract package data - let code = package.extract_code(); - let metadata = package - .extract_metadata() - .expect("extracting package metadata must succeed"); - - // publish package similar to create_publish_package in harness.rs - aptos_stdlib::code_publish_package_txn( - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - code, - ) -} - -// sign transaction to create a Module and return transaction status -pub fn execute_module_txn( - executor: &mut FakeExecutor, - account: &Account, - payload: TransactionPayload, - sequence_number: u64, -) { - // build and sign transaction - let sign_tx = account - .transaction() - .sequence_number(sequence_number) - .max_gas_amount(2_000_000) - .gas_unit_price(200) - .payload(payload) - .sign(); - - // Restart timer and sequence counter for each new package - // only count running time of entry function - let start = Instant::now(); - let txn_output = executor.execute_transaction(sign_tx); - - // apply write set to avoid LINKER_ERROR - executor.apply_write_set(txn_output.write_set()); - let elapsed = start.elapsed(); - println!("running time (microseconds): {}", elapsed.as_micros()); - - // validate successful transaction - let txn_status = txn_output.status().to_owned(); - assert!(txn_output.status().status().unwrap().is_success()); - println!("txn status: {:?}", txn_status); -} - -// sign user transaction and only records the body of the transaction -pub fn execute_user_txn(executor: &mut FakeExecutor, module_name: &ModuleId, function_name: &str) { - let elapsed = executor.exec_func_record_running_time( - module_name, - function_name, - vec![], - vec![], - 10, - ExecFuncTimerDynamicArgs::NoArgs, - GasMeterType::UnmeteredGasMeter, - ); - println!("running time (microseconds): {}", elapsed); -} - -// publish module under user and sign user transaction -pub fn publish( - package: &BuiltPackage, - executor: &mut FakeExecutor, - func_identifiers: Vec, - address: AccountAddress, - identifier: &String, -) { - //// publish test-package under module address - let creator = executor.new_account_at(address); - - //// iterate over all the functions that satisfied the requirements above - for (sequence_num_counter, func_identifier) in func_identifiers.into_iter().enumerate() { - println!( - "Executing {}::{}::{}", - address, - identifier, - func_identifier.clone(), - ); - - // publish package similar to create_publish_package in harness.rs - print!("Signing txn for module... "); - let module_payload = generate_module_payload(package); - let counter = sequence_num_counter.try_into().unwrap(); - execute_module_txn(executor, &creator, module_payload, counter); - - //// send a txn that invokes the entry function 0x{address}::{name}::benchmark - print!("Signing user txn... "); - let module_name = get_module_name(address, identifier, &func_identifier); - execute_user_txn(executor, &module_name, &func_identifier); - } -} - -/* - * - * GETTER FUNCTIONS - * - */ -// get module name -pub fn get_module_name( - address: AccountAddress, - identifier: &String, - func_identifier: &String, -) -> ModuleId { - let MemberId { - module_id, - member_id: _function_id, - } = str::parse(&format!( - "0x{}::{}::{}", - address.to_hex(), - identifier, - func_identifier, - )) - .unwrap(); - - module_id -} - -// get all directories of Move projects -pub fn get_dir_paths(dirs: ReadDir) -> Vec { - let mut dir_paths = Vec::new(); - for dir in dirs { - // validate path is directory - let entry = dir.unwrap(); - if !entry.path().is_dir() { - continue; - } - dir_paths.push(entry.path()); - } - dir_paths -} - -// get functional identifiers -pub fn get_functional_identifiers( - cm: CompiledModule, - identifier: &String, - address: AccountAddress, - pattern: String, -) -> Vec { - // find non-entry functions and ignore them - // keep entry function names in func_identifiers vector - let funcs = cm.function_defs; - let func_handles = cm.function_handles; - let func_identifier_pool = cm.identifiers; - - // find # of params in each func if it is entry function - let signature_pool = cm.signatures; - - let mut func_identifiers: Vec = Vec::new(); - for func in funcs { - // check if function is marked as entry, if not skip it - let is_entry = func.is_entry; - if !is_entry { - continue; - } - - // extract some info from the function - let func_idx: usize = func.function.0.into(); - let handle = &func_handles[func_idx]; - let func_identifier_idx: usize = handle.name.0.into(); - let func_identifier = &func_identifier_pool[func_identifier_idx]; - - // check if it doesn't start with "benchmark", if not skip it - let func_name = func_identifier.to_string(); - if !func_name.starts_with(PREFIX) { - continue; - } - - // check if it doesn't match pattern, if not skip it - let fully_qualified_path = format!("{}::{}::{}", address, identifier, func_name); - if !fully_qualified_path.contains(&pattern) && !pattern.is_empty() { - continue; - } - - // if it does, ensure no params in benchmark function - let signature_idx: usize = handle.parameters.0.into(); - let func_params = &signature_pool[signature_idx]; - if !func_params.is_empty() { - eprintln!( - "\n[WARNING] benchmark function should not have parameters: {}\n", - func_name, - ); - // TODO: should we exit instead of continuing with the benchmark - continue; - } - - // save function to later run benchmark for it - func_identifiers.push(func_name); - } - - func_identifiers -} diff --git a/aptos-move/aptos-vm-benchmarks/src/main.rs b/aptos-move/aptos-vm-benchmarks/src/main.rs deleted file mode 100644 index 0360127e36da7..0000000000000 --- a/aptos-move/aptos-vm-benchmarks/src/main.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod helper; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_language_e2e_tests::executor::FakeExecutor; -use clap::Parser; -use move_binary_format::CompiledModule; -use std::{fs::read_dir, path::PathBuf}; - -// CLI options -#[derive(Parser, Debug)] -struct Cli { - #[clap(default_value = "")] - pattern: String, -} - -fn main() { - //// Implement CLI - let args = Cli::parse(); - let pattern = &args.pattern; - - //// Discover all top-level packages in samples directory - let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("samples"); - let dirs = read_dir(path.as_path()).unwrap(); - - //// Setting up local execution environment once - // disable parallel execution - let executor = FakeExecutor::from_head_genesis(); - let mut executor = executor.set_not_parallel(); - - //// get all paths for Move projects - let dir_paths = helper::get_dir_paths(dirs); - - //// Go over all Move projects - for dir_path in dir_paths { - // configure and build Move package - let build_options = BuildOptions { - with_srcs: true, - with_abis: true, - with_source_maps: true, - with_error_map: true, - ..BuildOptions::default() - }; - let package = - BuiltPackage::build(dir_path, build_options).expect("build package must succeed"); - - // iterate over all Move package code - let codes = package.extract_code(); - for code in codes { - let compiled_module = CompiledModule::deserialize(&code).unwrap(); - let module_id = compiled_module.self_id(); - let identifier = &module_id.name().to_string(); - - //// get module address - let address = module_id.address(); - - //// get all benchmark tagged functions - let func_identifiers = helper::get_functional_identifiers( - compiled_module, - identifier, - *address, - pattern.clone(), - ); - - //// publish module and sign user transaction - //// the benchmark happens when in signing user txn - helper::publish( - &package, - &mut executor, - func_identifiers, - *address, - identifier, - ) - } - } -} diff --git a/aptos-node/Cargo.toml b/aptos-node/Cargo.toml deleted file mode 100644 index 3f7aa4d9dcb4e..0000000000000 --- a/aptos-node/Cargo.toml +++ /dev/null @@ -1,101 +0,0 @@ -[package] -name = "aptos-node" -description = "Aptos node" -version = "0.0.0-main" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-admin-service = { workspace = true } -aptos-api = { workspace = true } -aptos-backup-service = { workspace = true } -aptos-build-info = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-channels = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus = { workspace = true } -aptos-consensus-notifications = { workspace = true } -aptos-crash-handler = { workspace = true } -aptos-crypto = { workspace = true } -aptos-data-client = { workspace = true } -aptos-data-streaming-service = { workspace = true } -aptos-db = { workspace = true } -aptos-db-indexer = { workspace = true } -aptos-dkg-runtime = { workspace = true } -aptos-event-notifications = { workspace = true } -aptos-executor = { workspace = true } -aptos-executor-types = { workspace = true } -aptos-framework = { workspace = true } -aptos-genesis = { workspace = true } -aptos-indexer = { workspace = true, optional = true } -aptos-indexer-grpc-fullnode = { workspace = true } -aptos-indexer-grpc-table-info = { workspace = true } -aptos-infallible = { workspace = true } -aptos-inspection-service = { workspace = true } -aptos-jwk-consensus = { workspace = true } -aptos-logger = { workspace = true } -aptos-mempool = { workspace = true } -aptos-mempool-notifications = { workspace = true } -aptos-network = { workspace = true } -aptos-network-benchmark = { workspace = true } -aptos-network-builder = { workspace = true } -aptos-node-identity = { workspace = true } -aptos-peer-monitoring-service-client = { workspace = true } -aptos-peer-monitoring-service-server = { workspace = true } -aptos-peer-monitoring-service-types = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-safety-rules = { workspace = true } -aptos-secure-storage = { workspace = true } -aptos-state-sync-driver = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-storage-service-client = { workspace = true } -aptos-storage-service-notifications = { workspace = true } -aptos-storage-service-server = { workspace = true } -aptos-storage-service-types = { workspace = true } -aptos-telemetry = { workspace = true } -aptos-temppath = { workspace = true } -aptos-time-service = { workspace = true } -aptos-types = { workspace = true } -aptos-validator-transaction-pool = { workspace = true } -aptos-vm = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -either = { workspace = true } -fail = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -maplit = { workspace = true } -num_cpus = { workspace = true } -rand = { workspace = true } -rayon = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -tokio = { workspace = true } -tokio-stream = { workspace = true } -url = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } - -[target.'cfg(target_os = "linux")'.dependencies] -rstack-self = { workspace = true } - -[features] -assert-private-keys-not-cloneable = ["aptos-crypto/assert-private-keys-not-cloneable"] -check-vm-features = [] -consensus-only-perf-test = ["aptos-executor/consensus-only-perf-test", "aptos-mempool/consensus-only-perf-test", "aptos-db/consensus-only-perf-test"] -default = [] -failpoints = ["fail/failpoints", "aptos-consensus/failpoints", "aptos-executor/failpoints", "aptos-mempool/failpoints", "aptos-api/failpoints", "aptos-config/failpoints"] -indexer = ["aptos-indexer"] -network-perf-test = ["aptos-peer-monitoring-service-client/network-perf-test", "aptos-peer-monitoring-service-server/network-perf-test", "aptos-peer-monitoring-service-types/network-perf-test", "aptos-config/network-perf-test"] -tokio-console = ["aptos-logger/tokio-console", "aptos-config/tokio-console"] -smoke-test = ["aptos-jwk-consensus/smoke-test", "aptos-dkg-runtime/smoke-test"] diff --git a/aptos-node/src/indexer.rs b/aptos-node/src/indexer.rs deleted file mode 100644 index bede7d1a8ec1d..0000000000000 --- a/aptos-node/src/indexer.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_config::config::NodeConfig; -use aptos_mempool::MempoolClientSender; -use aptos_storage_interface::DbReader; -use aptos_types::chain_id::ChainId; -use std::sync::Arc; -use tokio::runtime::Runtime; - -#[cfg(feature = "indexer")] -pub fn bootstrap_indexer( - node_config: &NodeConfig, - chain_id: ChainId, - aptos_db: Arc, - mp_client_sender: MempoolClientSender, -) -> Result, anyhow::Error> { - use aptos_indexer::runtime::bootstrap as bootstrap_indexer_stream; - - match bootstrap_indexer_stream(&node_config, chain_id, aptos_db, mp_client_sender) { - None => Ok(None), - Some(res) => res.map(Some), - } -} - -#[cfg(not(feature = "indexer"))] -pub fn bootstrap_indexer( - _node_config: &NodeConfig, - _chain_id: ChainId, - _aptos_db: Arc, - _mp_client_sender: MempoolClientSender, -) -> Result, anyhow::Error> { - Ok(None) -} diff --git a/aptos-node/src/lib.rs b/aptos-node/src/lib.rs deleted file mode 100644 index 60aeb47b9a4d3..0000000000000 --- a/aptos-node/src/lib.rs +++ /dev/null @@ -1,776 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -mod indexer; -mod logger; -pub mod network; -mod services; -mod state_sync; -pub mod storage; -pub mod utils; - -#[cfg(test)] -mod tests; - -use crate::network::ApplicationNetworkInterfaces; -use anyhow::anyhow; -use aptos_admin_service::AdminService; -use aptos_api::bootstrap as bootstrap_api; -use aptos_build_info::build_information; -use aptos_config::config::{ - merge_node_config, InitialSafetyRulesConfig, NodeConfig, PersistableConfig, -}; -use aptos_dkg_runtime::start_dkg_runtime; -use aptos_framework::ReleaseBundle; -use aptos_jwk_consensus::start_jwk_consensus_runtime; -use aptos_logger::{prelude::*, telemetry_log_writer::TelemetryLog, Level, LoggerFilterUpdater}; -use aptos_safety_rules::safety_rules_manager::load_consensus_key_from_secure_storage; -use aptos_state_sync_driver::driver_factory::StateSyncRuntimes; -use aptos_types::chain_id::ChainId; -use aptos_validator_transaction_pool::VTxnPoolState; -use clap::Parser; -use futures::channel::mpsc; -use hex::{FromHex, FromHexError}; -use rand::{rngs::StdRng, SeedableRng}; -use std::{ - fs, - io::Write, - path::{Path, PathBuf}, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - thread, -}; -use tokio::runtime::Runtime; - -const EPOCH_LENGTH_SECS: u64 = 60; - -/// Runs an Aptos validator or fullnode -#[derive(Clone, Debug, Parser)] -#[clap(name = "Aptos Node", author, version)] -pub struct AptosNodeArgs { - /// Path to node configuration file (or template for local test mode). - #[clap( - short = 'f', - long, - value_parser, - required_unless_present_any = ["test", "info"], - )] - #[cfg_attr(target_os = "linux", clap(required_unless_present_any = ["stacktrace"]))] - config: Option, - - /// Directory to run the test mode in. - /// - /// Repeated runs will start up from previous state. - #[clap(long, value_parser, requires("test"))] - test_dir: Option, - - /// Path to node configuration file override for local test mode. Cannot be used with --config - #[clap(long, value_parser, requires("test"), conflicts_with("config"))] - test_config_override: Option, - - /// Run only a single validator node testnet. - #[clap(long)] - test: bool, - - /// Random number generator seed for starting a single validator testnet. - #[clap(long, value_parser = load_seed, requires("test"))] - seed: Option<[u8; 32]>, - - /// Use random ports instead of ports from the node configuration. - #[clap(long, requires("test"))] - random_ports: bool, - - /// Paths to the Aptos framework release package to be used for genesis. - #[clap(long, requires("test"))] - genesis_framework: Option, - - /// Enable lazy mode. - /// - /// Setting this flag will set `consensus#mempool_poll_count` config to `u64::MAX` and - /// only commit a block when there are user transactions in mempool. - #[clap(long, requires("test"))] - lazy: bool, - - /// Display information about the build of this node - #[clap(long)] - info: bool, - - #[cfg(target_os = "linux")] - /// Start as a child process to collect thread dump. - /// See rstack-self crate for more details. - #[clap(long)] - stacktrace: bool, -} - -impl AptosNodeArgs { - /// Runs an Aptos node based on the given command line arguments and config flags - pub fn run(self) { - #[cfg(target_os = "linux")] - // https://sfackler.github.io/rstack/doc/rstack_self/index.html - // - // TODO(grao): I don't like this way, but I didn't find other existing solution in Rust. - // Maybe try to use libc directly? - if self.stacktrace { - let _ = rstack_self::child(); - return; - } - - if self.info { - let build_information = build_information!(); - println!( - "{}", - serde_json::to_string_pretty(&build_information) - .expect("Failed to print build information") - ); - return; - } - - if self.test { - println!("WARNING: Entering test mode! This should never be used in production!"); - - // Set the genesis framework - let genesis_framework = if let Some(path) = self.genesis_framework { - ReleaseBundle::read(path).unwrap() - } else { - aptos_cached_packages::head_release_bundle().clone() - }; - - // Create a seeded RNG, setup the test environment and start the node - let rng = self - .seed - .map(StdRng::from_seed) - .unwrap_or_else(StdRng::from_entropy); - setup_test_environment_and_start_node( - &self.config, - &self.test_config_override, - None, - self.test_dir, - self.random_ports, - self.lazy, - &genesis_framework, - rng, - ) - .expect("Test node should start correctly!"); - } else { - // Get the config file path - let config_path = self.config.expect("Config is required to launch node"); - if !config_path.exists() { - panic!( - "The node config file could not be found! Ensure the given path is correct: {:?}", - config_path.display() - ) - } - - // A config file exists, attempt to parse the config - let config = NodeConfig::load_from_path(config_path.clone()).unwrap_or_else(|error| { - panic!( - "Failed to load the node config file! Given file path: {:?}. Error: {:?}", - config_path.display(), - error - ) - }); - - // Start the node - start(config, None, true).expect("Node should start correctly"); - }; - } -} - -pub fn load_seed(input: &str) -> Result<[u8; 32], FromHexError> { - let trimmed_input = input.trim(); - FromHex::from_hex(trimmed_input) -} - -/// Runtime handle to ensure that all inner runtimes stay in scope -pub struct AptosHandle { - _admin_service: AdminService, - _api_runtime: Option, - _backup_runtime: Option, - _consensus_runtime: Option, - _dkg_runtime: Option, - _indexer_grpc_runtime: Option, - _indexer_runtime: Option, - _indexer_table_info_runtime: Option, - _jwk_consensus_runtime: Option, - _mempool_runtime: Runtime, - _network_runtimes: Vec, - _peer_monitoring_service_runtime: Runtime, - _state_sync_runtimes: StateSyncRuntimes, - _telemetry_runtime: Option, -} - -/// Start an Aptos node -pub fn start( - config: NodeConfig, - log_file: Option, - create_global_rayon_pool: bool, -) -> anyhow::Result<()> { - // Setup panic handler - aptos_crash_handler::setup_panic_handler(); - - // Create global rayon thread pool - utils::create_global_rayon_pool(create_global_rayon_pool); - - // Initialize the global aptos-node-identity - aptos_node_identity::init(config.get_peer_id())?; - - // Instantiate the global logger - let (remote_log_receiver, logger_filter_update) = logger::create_logger(&config, log_file); - - assert!( - !cfg!(feature = "testing") && !cfg!(feature = "fuzzing"), - "Testing features shouldn't be compiled" - ); - - // Ensure failpoints are configured correctly - if fail::has_failpoints() { - warn!("Failpoints are enabled!"); - - // Set all of the failpoints - if let Some(failpoints) = &config.failpoints { - for (point, actions) in failpoints { - fail::cfg(point, actions).unwrap_or_else(|_| { - panic!( - "Failed to set actions for failpoint! Failpoint: {:?}, Actions: {:?}", - point, actions - ) - }); - } - } - } else if config.failpoints.is_some() { - warn!("Failpoints is set in the node config, but the binary didn't compile with this feature!"); - } - - // Set up the node environment and start it - let _node_handle = - setup_environment_and_start_node(config, remote_log_receiver, Some(logger_filter_update))?; - let term = Arc::new(AtomicBool::new(false)); - while !term.load(Ordering::Acquire) { - thread::park(); - } - - Ok(()) -} - -/// Load a config based on a variety of different ways to provide config options. For -/// more information about each argument and its precedence, see -/// `setup_test_environment_and_start_node`. -pub fn load_node_config( - config_path: &Option, - test_config_override_path: &Option, - test_dir: &Path, - random_ports: bool, - enable_lazy_mode: bool, - framework: &ReleaseBundle, - rng: R, -) -> anyhow::Result -where - R: rand::RngCore + rand::CryptoRng, -{ - // The validator builder puts the first node in the 0 directory - let validator_config_path = test_dir.join("0").join("node.yaml"); - - let config = if validator_config_path.exists() { - NodeConfig::load_from_path(&validator_config_path) - .map_err(|error| anyhow!("Unable to load config: {:?}", error))? - } else { - // Create a test only config for a single validator node. - let config = create_single_node_test_config( - config_path, - test_config_override_path, - test_dir, - random_ports, - enable_lazy_mode, - framework, - rng, - )?; - if let Some(ref test_config_override_path) = test_config_override_path { - println!( - "\tMerged default config with override from path: {:?}", - test_config_override_path - ); - } - if let Some(ref config_path) = config_path { - println!("\tUsed user-provided config from path: {:?}", config_path); - } - config - }; - - Ok(config) -} - -/// Print details about a node config configured for a test environment and start it. -pub fn start_test_environment_node( - config: NodeConfig, - test_dir: PathBuf, - enable_lazy_mode: bool, -) -> anyhow::Result<()> { - let aptos_root_key_path = test_dir.join("mint.key"); - - // Prepare log file since we cannot automatically route logs to stderr - let log_file = test_dir.join("validator.log"); - - // Print out useful information about the environment and the node - println!("Completed generating configuration:"); - println!("\tLog file: {:?}", log_file); - println!("\tTest dir: {:?}", test_dir); - println!("\tAptos root key path: {:?}", aptos_root_key_path); - println!("\tWaypoint: {}", config.base.waypoint.genesis_waypoint()); - println!("\tChainId: {}", ChainId::test().id()); - println!("\tREST API endpoint: http://{}", &config.api.address); - println!( - "\tMetrics endpoint: http://{}:{}/metrics", - &config.inspection_service.address, &config.inspection_service.port - ); - println!( - "\tAptosnet fullnode network endpoint: {}", - &config.full_node_networks[0].listen_address - ); - if config.indexer_grpc.enabled { - println!( - "\tIndexer gRPC node stream endpoint: {}", - config.indexer_grpc.address - ); - } - if enable_lazy_mode { - println!("\tLazy mode is enabled"); - } - println!("\nAptos is running, press ctrl-c to exit\n"); - - start(config, Some(log_file), false) -} - -/// Creates a simple test environment and starts the node. -/// -/// You will notice many args referring to configs. Let's explain them: -/// - `test_config_override_path` is the path to a config file that will be used as -/// a template when building the final config. If not provided, a default template -/// will be used. Many overrides are applied on top of this base config. -/// - `config_path` is similar to `test_config_override_path`, but many of the -/// overrides that are applied when using `test_config_override_path` are not -/// applied when using `config_path`. Read the code for more info. -/// - `config` is a complete NodeConfig. No overrides are applied on top of this if -/// it is provided. If both `config` and `test_dir` are provided, `config` takes -/// precedence. -/// - `test_dir` is a directory that contains a config file. Much like `config`, the -/// config read from this file is used without any overrides. -pub fn setup_test_environment_and_start_node( - config_path: &Option, - test_config_override_path: &Option, - config: Option, - test_dir: Option, - random_ports: bool, - enable_lazy_mode: bool, - framework: &ReleaseBundle, - rng: R, -) -> anyhow::Result<()> -where - R: rand::RngCore + rand::CryptoRng, -{ - // If there wasn't a test directory specified, create a temporary one - let test_dir = - test_dir.unwrap_or_else(|| aptos_temppath::TempPath::new().as_ref().to_path_buf()); - - // Create the directories for the node - fs::DirBuilder::new().recursive(true).create(&test_dir)?; - let test_dir = test_dir.canonicalize()?; - - let config = match config { - Some(config) => config, - None => load_node_config( - config_path, - test_config_override_path, - &test_dir, - random_ports, - enable_lazy_mode, - framework, - rng, - )?, - }; - - start_test_environment_node(config, test_dir, enable_lazy_mode) -} - -/// Creates a single node test config, with a few config tweaks to reduce -/// the overhead of running the node on a local machine. It writes necessary -/// configuration artifacts (e.g. the mint key) to disk. -pub fn create_single_node_test_config( - config_path: &Option, - test_config_override_path: &Option, - test_dir: &Path, - random_ports: bool, - enable_lazy_mode: bool, - framework: &ReleaseBundle, - rng: R, -) -> anyhow::Result -where - R: rand::RngCore + rand::CryptoRng, -{ - let mut node_config = match test_config_override_path { - // If a config override path was provided, merge it with the default config - Some(test_config_override_path) => { - let reader = fs::File::open(test_config_override_path).map_err(|e| { - anyhow!( - "Unable to open config override file {:?}. Error: {}", - test_config_override_path, - e - ) - })?; - let values: serde_yaml::Value = serde_yaml::from_reader(&reader).map_err(|e| { - anyhow!( - "Unable to read config override file as YAML {:?}. Error: {}", - test_config_override_path, - e - ) - })?; - merge_node_config(NodeConfig::get_default_validator_config(), values)? - }, - None => NodeConfig::get_default_validator_config(), - }; - - // Adjust some fields in the default template to lower the overhead of - // running on a local machine. - node_config - .consensus - .quorum_store - .num_workers_for_remote_batches = 1; - node_config.consensus.quorum_store_poll_time_ms = 1000; - - node_config.execution.concurrency_level = 1; - node_config.execution.num_proof_reading_threads = 1; - node_config.execution.paranoid_hot_potato_verification = false; - node_config.execution.paranoid_type_verification = false; - node_config - .execution - .processed_transactions_detailed_counters = false; - - node_config.peer_monitoring_service.max_concurrent_requests = 1; - node_config - .peer_monitoring_service - .enable_peer_monitoring_client = false; - - node_config - .mempool - .shared_mempool_max_concurrent_inbound_syncs = 1; - node_config.mempool.default_failovers = 1; - node_config.mempool.max_broadcasts_per_peer = 1; - - node_config - .state_sync - .state_sync_driver - .enable_auto_bootstrapping = true; - node_config - .state_sync - .state_sync_driver - .max_connection_deadline_secs = 1; - node_config - .state_sync - .state_sync_driver - .progress_check_interval_ms = 10_000; - node_config - .state_sync - .data_streaming_service - .progress_check_interval_ms = 10_000; - - // Configure the validator network - let validator_network = node_config.validator_network.as_mut().unwrap(); - validator_network.max_concurrent_network_reqs = 1; - validator_network.connectivity_check_interval_ms = 10000; - validator_network.max_connection_delay_ms = 10000; - validator_network.ping_interval_ms = 10000; - validator_network.runtime_threads = Some(1); - - // Configure the fullnode network - let fullnode_network = node_config.full_node_networks.get_mut(0).unwrap(); - fullnode_network.max_concurrent_network_reqs = 1; - fullnode_network.connectivity_check_interval_ms = 10000; - fullnode_network.max_connection_delay_ms = 10000; - fullnode_network.ping_interval_ms = 10000; - fullnode_network.runtime_threads = Some(1); - - // If a config path was provided, use that as the template - if let Some(config_path) = config_path { - node_config = NodeConfig::load_config(config_path).map_err(|e| { - anyhow!( - "Unable to load config from path: {:?}. Error: {:?}", - config_path, - e - ) - })?; - } - - // Change the default log level - node_config.logger.level = Level::Debug; - - // Enable the REST API - node_config.api.address = format!("0.0.0.0:{}", node_config.api.address.port()) - .parse() - .expect("Unable to set the REST API address!"); - - // Set the correct poll count for mempool - if enable_lazy_mode { - node_config.consensus.quorum_store_poll_time_ms = 3_600_000; - } - - // The validator builder puts the first node in the 0 directory - let aptos_root_key_path = test_dir.join("mint.key"); - - // Build genesis and the validator node - let builder = aptos_genesis::builder::Builder::new(test_dir, framework.clone())? - .with_init_config(Some(Arc::new(move |_, config, _| { - *config = node_config.clone(); - }))) - .with_init_genesis_config(Some(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.epoch_duration_secs = EPOCH_LENGTH_SECS; - genesis_config.recurring_lockup_duration_secs = 7200; - }))) - .with_randomize_first_validator_ports(random_ports); - let (root_key, _genesis, genesis_waypoint, mut validators) = builder.build(rng)?; - - // Write the mint key to disk - let serialized_keys = bcs::to_bytes(&root_key)?; - let mut key_file = fs::File::create(aptos_root_key_path)?; - key_file.write_all(&serialized_keys)?; - - // Build a waypoint file so that clients / docker can grab it easily - let waypoint_file_path = test_dir.join("waypoint.txt"); - Write::write_all( - &mut fs::File::create(waypoint_file_path)?, - genesis_waypoint.to_string().as_bytes(), - )?; - - aptos_config::config::sanitize_node_config(validators[0].config.override_config_mut())?; - - let mut node_config = validators[0].config.override_config().clone(); - - // Enable the AdminService. - node_config.admin_service.enabled = Some(true); - - Ok(node_config) -} - -/// Initializes the node environment and starts the node -pub fn setup_environment_and_start_node( - mut node_config: NodeConfig, - remote_log_rx: Option>, - logger_filter_update_job: Option, -) -> anyhow::Result { - // Log the node config at node startup - node_config.log_all_configs(); - - // Starts the admin service - let admin_service = services::start_admin_service(&node_config); - - // Set up the storage database and any RocksDB checkpoints - let (db_rw, backup_service, genesis_waypoint) = - storage::initialize_database_and_checkpoints(&mut node_config)?; - - admin_service.set_aptos_db(db_rw.clone().into()); - - // Set the Aptos VM configurations - utils::set_aptos_vm_configurations(&node_config); - - // Obtain the chain_id from the DB - let chain_id = utils::fetch_chain_id(&db_rw)?; - - // Set the chain_id in global AptosNodeIdentity - aptos_node_identity::set_chain_id(chain_id)?; - - // Start the telemetry service (as early as possible and before any blocking calls) - let telemetry_runtime = services::start_telemetry_service( - &node_config, - remote_log_rx, - logger_filter_update_job, - chain_id, - ); - - // Create an event subscription service (and reconfig subscriptions for consensus and mempool) - let ( - mut event_subscription_service, - mempool_reconfig_subscription, - consensus_reconfig_subscription, - dkg_subscriptions, - jwk_consensus_subscriptions, - ) = state_sync::create_event_subscription_service(&node_config, &db_rw); - - // Set up the networks and gather the application network handles - let peers_and_metadata = network::create_peers_and_metadata(&node_config); - let ( - network_runtimes, - consensus_network_interfaces, - dkg_network_interfaces, - jwk_consensus_network_interfaces, - mempool_network_interfaces, - peer_monitoring_service_network_interfaces, - storage_service_network_interfaces, - ) = network::setup_networks_and_get_interfaces( - &node_config, - chain_id, - peers_and_metadata.clone(), - &mut event_subscription_service, - ); - - // Start the peer monitoring service - let peer_monitoring_service_runtime = services::start_peer_monitoring_service( - &node_config, - peer_monitoring_service_network_interfaces, - db_rw.reader.clone(), - ); - - // Start state sync and get the notification endpoints for mempool and consensus - let (aptos_data_client, state_sync_runtimes, mempool_listener, consensus_notifier) = - state_sync::start_state_sync_and_get_notification_handles( - &node_config, - storage_service_network_interfaces, - genesis_waypoint, - event_subscription_service, - db_rw.clone(), - )?; - - // Start the node inspection service - services::start_node_inspection_service( - &node_config, - aptos_data_client, - peers_and_metadata.clone(), - ); - - // Bootstrap the API and indexer - let ( - mempool_client_receiver, - api_runtime, - indexer_table_info_runtime, - indexer_runtime, - indexer_grpc_runtime, - ) = services::bootstrap_api_and_indexer(&node_config, db_rw.clone(), chain_id)?; - - // Create mempool and get the consensus to mempool sender - let (mempool_runtime, consensus_to_mempool_sender) = - services::start_mempool_runtime_and_get_consensus_sender( - &mut node_config, - &db_rw, - mempool_reconfig_subscription, - mempool_network_interfaces, - mempool_listener, - mempool_client_receiver, - peers_and_metadata, - ); - - // Ensure consensus key in secure DB. - if !matches!( - node_config - .consensus - .safety_rules - .initial_safety_rules_config, - InitialSafetyRulesConfig::None - ) { - aptos_safety_rules::safety_rules_manager::storage(&node_config.consensus.safety_rules); - } - - let vtxn_pool = VTxnPoolState::default(); - let maybe_dkg_dealer_sk = - load_consensus_key_from_secure_storage(&node_config.consensus.safety_rules); - debug!("maybe_dkg_dealer_sk={:?}", maybe_dkg_dealer_sk); - let dkg_runtime = match (dkg_network_interfaces, maybe_dkg_dealer_sk) { - (Some(interfaces), Ok(dkg_dealer_sk)) => { - let ApplicationNetworkInterfaces { - network_client, - network_service_events, - } = interfaces; - let (reconfig_events, dkg_start_events) = dkg_subscriptions - .expect("DKG needs to listen to NewEpochEvents events and DKGStartEvents"); - let my_addr = node_config.validator_network.as_ref().unwrap().peer_id(); - let dkg_runtime = start_dkg_runtime( - my_addr, - dkg_dealer_sk, - network_client, - network_service_events, - reconfig_events, - dkg_start_events, - vtxn_pool.clone(), - ); - Some(dkg_runtime) - }, - _ => None, - }; - - let maybe_jwk_consensus_key = - load_consensus_key_from_secure_storage(&node_config.consensus.safety_rules); - debug!( - "jwk_consensus_key_err={:?}", - maybe_jwk_consensus_key.as_ref().err() - ); - - let jwk_consensus_runtime = match (jwk_consensus_network_interfaces, maybe_jwk_consensus_key) { - (Some(interfaces), Ok(consensus_key)) => { - let ApplicationNetworkInterfaces { - network_client, - network_service_events, - } = interfaces; - let (reconfig_events, onchain_jwk_updated_events) = jwk_consensus_subscriptions.expect( - "JWK consensus needs to listen to NewEpochEvents and OnChainJWKMapUpdated events.", - ); - let my_addr = node_config.validator_network.as_ref().unwrap().peer_id(); - let jwk_consensus_runtime = start_jwk_consensus_runtime( - my_addr, - consensus_key, - network_client, - network_service_events, - reconfig_events, - onchain_jwk_updated_events, - vtxn_pool.clone(), - ); - Some(jwk_consensus_runtime) - }, - _ => None, - }; - - // Create the consensus runtime (this blocks on state sync first) - let consensus_runtime = consensus_network_interfaces.map(|consensus_network_interfaces| { - // Wait until state sync has been initialized - debug!("Waiting until state sync is initialized!"); - state_sync_runtimes.block_until_initialized(); - debug!("State sync initialization complete."); - - // Initialize and start consensus - let (runtime, consensus_db, quorum_store_db) = services::start_consensus_runtime( - &mut node_config, - db_rw, - consensus_reconfig_subscription, - consensus_network_interfaces, - consensus_notifier, - consensus_to_mempool_sender, - vtxn_pool, - ); - admin_service.set_consensus_dbs(consensus_db, quorum_store_db); - runtime - }); - - Ok(AptosHandle { - _admin_service: admin_service, - _api_runtime: api_runtime, - _backup_runtime: backup_service, - _consensus_runtime: consensus_runtime, - _dkg_runtime: dkg_runtime, - _indexer_grpc_runtime: indexer_grpc_runtime, - _indexer_runtime: indexer_runtime, - _indexer_table_info_runtime: indexer_table_info_runtime, - _jwk_consensus_runtime: jwk_consensus_runtime, - _mempool_runtime: mempool_runtime, - _network_runtimes: network_runtimes, - _peer_monitoring_service_runtime: peer_monitoring_service_runtime, - _state_sync_runtimes: state_sync_runtimes, - _telemetry_runtime: telemetry_runtime, - }) -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - AptosNodeArgs::command().debug_assert() -} diff --git a/aptos-node/src/logger.rs b/aptos-node/src/logger.rs deleted file mode 100644 index 158cf97f13f8f..0000000000000 --- a/aptos-node/src/logger.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::mpsc::Receiver; -use aptos_build_info::build_information; -use aptos_config::config::NodeConfig; -use aptos_logger::{ - aptos_logger::FileWriter, info, telemetry_log_writer::TelemetryLog, LoggerFilterUpdater, -}; -use futures::channel::mpsc; -use std::path::PathBuf; - -const TELEMETRY_LOG_INGEST_BUFFER_SIZE: usize = 128; - -// Simple macro to help print out feature configurations -macro_rules! log_feature_info { - ($($feature:literal),*) => { - $( - if cfg!(feature = $feature) { - info!("Running with {} feature enabled", $feature); - } else { - info!("Running with {} feature disabled", $feature); - } - )* - } -} - -/// Creates the logger and returns the remote log receiver alongside -/// the logger filter updater. -pub fn create_logger( - node_config: &NodeConfig, - log_file: Option, -) -> (Option>, LoggerFilterUpdater) { - // Create the logger builder - let mut logger_builder = aptos_logger::Logger::builder(); - let mut remote_log_receiver = None; - logger_builder - .channel_size(node_config.logger.chan_size) - .is_async(node_config.logger.is_async) - .level(node_config.logger.level) - .telemetry_level(node_config.logger.telemetry_level) - .enable_telemetry_flush(node_config.logger.enable_telemetry_flush) - .tokio_console_port(node_config.logger.tokio_console_port); - if node_config.logger.enable_backtrace { - logger_builder.enable_backtrace(); - } - if let Some(log_file) = log_file { - logger_builder.printer(Box::new(FileWriter::new(log_file))); - } - if node_config.logger.enable_telemetry_remote_log { - let (tx, rx) = mpsc::channel(TELEMETRY_LOG_INGEST_BUFFER_SIZE); - logger_builder.remote_log_tx(tx); - remote_log_receiver = Some(rx); - } - - // Create the logger and the logger filter updater - let logger = logger_builder.build(); - let logger_filter_updater = LoggerFilterUpdater::new(logger, logger_builder); - - // Log the build information and the config - log_config_and_build_information(node_config); - - (remote_log_receiver, logger_filter_updater) -} - -/// Logs the node config and build information -fn log_config_and_build_information(node_config: &NodeConfig) { - // Log the build information - info!("Build information:"); - let build_info = build_information!(); - for (key, value) in build_info { - info!("{}: {}", key, value); - } - - // Log the feature information. Note: this should be kept up-to-date - // with the features defined in the aptos-node Cargo.toml file. - info!("Feature information:"); - log_feature_info!( - "assert-private-keys-not-cloneable", - "check-vm-features", - "consensus-only-perf-test", - "default", - "failpoints", - "indexer", - "network-perf-test", - "tokio-console" - ); - - // Log the node config - let mut config = node_config; - let mut masked_config; - if let Some(u) = &node_config.indexer.postgres_uri { - let mut parsed_url = url::Url::parse(u).expect("Invalid postgres uri"); - if parsed_url.password().is_some() { - masked_config = node_config.clone(); - parsed_url.set_password(Some("*")).unwrap(); - masked_config.indexer.postgres_uri = Some(parsed_url.to_string()); - config = &masked_config; - } - } - - info!("Loaded node config: {:?}", config); -} diff --git a/aptos-node/src/main.rs b/aptos-node/src/main.rs deleted file mode 100644 index a309501758097..0000000000000 --- a/aptos-node/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use aptos_node::{utils::ERROR_MSG_BAD_FEATURE_FLAGS, AptosNodeArgs}; -use clap::Parser; - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -fn main() { - // Check that we are not including any Move test natives - aptos_vm::natives::assert_no_test_natives(ERROR_MSG_BAD_FEATURE_FLAGS); - - // Start the node - AptosNodeArgs::parse().run() -} diff --git a/aptos-node/src/network.rs b/aptos-node/src/network.rs deleted file mode 100644 index 9c95ec7a30d0c..0000000000000 --- a/aptos-node/src/network.rs +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::services::start_netbench_service; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::{ - config::{NetworkConfig, NodeConfig}, - network_id::NetworkId, -}; -use aptos_consensus::network_interface::ConsensusMsg; -use aptos_dkg_runtime::DKGMessage; -use aptos_event_notifications::EventSubscriptionService; -use aptos_jwk_consensus::types::JWKConsensusMsg; -use aptos_logger::debug; -use aptos_mempool::network::MempoolSyncMsg; -use aptos_network::{ - application::{ - interface::{NetworkClient, NetworkServiceEvents}, - storage::PeersAndMetadata, - }, - protocols::network::{ - NetworkApplicationConfig, NetworkClientConfig, NetworkEvents, NetworkSender, - NetworkServiceConfig, - }, - ProtocolId, -}; -use aptos_network_benchmark::NetbenchMessage; -use aptos_network_builder::builder::NetworkBuilder; -use aptos_peer_monitoring_service_types::PeerMonitoringServiceMessage; -use aptos_storage_service_types::StorageServiceMessage; -use aptos_time_service::TimeService; -use aptos_types::chain_id::ChainId; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; -use tokio::runtime::Runtime; - -/// A simple struct that holds both the network client -/// and receiving interfaces for an application. -pub struct ApplicationNetworkInterfaces { - pub network_client: NetworkClient, - pub network_service_events: NetworkServiceEvents, -} - -/// A simple struct that holds an individual application -/// network handle (i.e., network id, sender and receiver). -struct ApplicationNetworkHandle { - pub network_id: NetworkId, - pub network_sender: NetworkSender, - pub network_events: NetworkEvents, -} - -/// TODO: make this configurable (e.g., for compression) -/// Returns the network application config for the consensus client and service -pub fn consensus_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols: Vec = - aptos_consensus::network_interface::DIRECT_SEND.into(); - let rpc_protocols: Vec = aptos_consensus::network_interface::RPC.into(); - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(node_config.consensus.max_network_channel_size) - .queue_style(QueueStyle::FIFO) - .counters(&aptos_consensus::counters::PENDING_CONSENSUS_NETWORK_EVENTS), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -pub fn dkg_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols: Vec = - aptos_dkg_runtime::network_interface::DIRECT_SEND.into(); - let rpc_protocols: Vec = aptos_dkg_runtime::network_interface::RPC.into(); - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(node_config.dkg.max_network_channel_size) - .queue_style(QueueStyle::FIFO), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -pub fn jwk_consensus_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols: Vec = - aptos_jwk_consensus::network_interface::DIRECT_SEND.into(); - let rpc_protocols: Vec = aptos_jwk_consensus::network_interface::RPC.into(); - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(node_config.jwk_consensus.max_network_channel_size) - .queue_style(QueueStyle::FIFO), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -/// Returns the network application config for the mempool client and service -pub fn mempool_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols = vec![ProtocolId::MempoolDirectSend]; - let rpc_protocols = vec![]; // Mempool does not use RPC - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(node_config.mempool.max_network_channel_size) - .queue_style(QueueStyle::KLAST) // TODO: why is this not FIFO? - .counters(&aptos_mempool::counters::PENDING_MEMPOOL_NETWORK_EVENTS), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -/// Returns the network application config for the peer monitoring client and server -pub fn peer_monitoring_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols = vec![]; // The monitoring service does not use direct send - let rpc_protocols = vec![ProtocolId::PeerMonitoringServiceRpc]; - let max_network_channel_size = - node_config.peer_monitoring_service.max_network_channel_size as usize; - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(max_network_channel_size) - .queue_style(QueueStyle::FIFO) - .counters( - &aptos_peer_monitoring_service_server::metrics::PENDING_PEER_MONITORING_SERVER_NETWORK_EVENTS, - ), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -/// Returns the network application config for the storage service client and server -pub fn storage_service_network_configuration(node_config: &NodeConfig) -> NetworkApplicationConfig { - let direct_send_protocols = vec![]; // The storage service does not use direct send - let rpc_protocols = vec![ProtocolId::StorageServiceRpc]; - let max_network_channel_size = node_config - .state_sync - .storage_service - .max_network_channel_size as usize; - - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(max_network_channel_size) - .queue_style(QueueStyle::FIFO) - .counters( - &aptos_storage_service_server::metrics::PENDING_STORAGE_SERVER_NETWORK_EVENTS, - ), - ); - NetworkApplicationConfig::new(network_client_config, network_service_config) -} - -pub fn netbench_network_configuration( - node_config: &NodeConfig, -) -> Option { - let cfg = match node_config.netbench { - None => return None, - Some(x) => x, - }; - if !cfg.enabled { - return None; - } - let direct_send_protocols = vec![ProtocolId::NetbenchDirectSend]; - let rpc_protocols = vec![ProtocolId::NetbenchRpc]; - let network_client_config = - NetworkClientConfig::new(direct_send_protocols.clone(), rpc_protocols.clone()); - let max_network_channel_size = cfg.max_network_channel_size as usize; - let network_service_config = NetworkServiceConfig::new( - direct_send_protocols, - rpc_protocols, - aptos_channel::Config::new(max_network_channel_size) - .queue_style(QueueStyle::FIFO) - .counters(&aptos_network_benchmark::PENDING_NETBENCH_NETWORK_EVENTS), - ); - Some(NetworkApplicationConfig::new( - network_client_config, - network_service_config, - )) -} - -/// Extracts all network configs from the given node config -fn extract_network_configs(node_config: &NodeConfig) -> Vec { - let mut network_configs: Vec = node_config.full_node_networks.to_vec(); - if let Some(network_config) = node_config.validator_network.as_ref() { - // Ensure that mutual authentication is enabled by default! - if !network_config.mutual_authentication { - panic!("Validator networks must always have mutual_authentication enabled!"); - } - network_configs.push(network_config.clone()); - } - network_configs -} - -/// Extracts all network ids from the given node config -fn extract_network_ids(node_config: &NodeConfig) -> Vec { - extract_network_configs(node_config) - .into_iter() - .map(|network_config| network_config.network_id) - .collect() -} - -/// Creates the global peers and metadata struct -pub fn create_peers_and_metadata(node_config: &NodeConfig) -> Arc { - let network_ids = extract_network_ids(node_config); - PeersAndMetadata::new(&network_ids) -} - -/// Sets up all networks and returns the appropriate application network interfaces -pub fn setup_networks_and_get_interfaces( - node_config: &NodeConfig, - chain_id: ChainId, - peers_and_metadata: Arc, - event_subscription_service: &mut EventSubscriptionService, -) -> ( - Vec, - Option>, - Option>, - Option>, - ApplicationNetworkInterfaces, - ApplicationNetworkInterfaces, - ApplicationNetworkInterfaces, -) { - // Gather all network configs - let network_configs = extract_network_configs(node_config); - - // Create each network and register the application handles - let mut network_runtimes = vec![]; - let mut consensus_network_handle = None; - let mut dkg_network_handle = None; - let mut jwk_consensus_network_handle = None; - let mut mempool_network_handles = vec![]; - let mut peer_monitoring_service_network_handles = vec![]; - let mut storage_service_network_handles = vec![]; - let mut netbench_handles = Vec::>::new(); - for network_config in network_configs.into_iter() { - // Create a network runtime for the config - let runtime = create_network_runtime(&network_config); - - // Entering gives us a runtime to instantiate all the pieces of the builder - let _enter = runtime.enter(); - - // Create a new network builder - let mut network_builder = NetworkBuilder::create( - chain_id, - node_config.base.role, - &network_config, - TimeService::real(), - Some(event_subscription_service), - peers_and_metadata.clone(), - ); - - // Register consensus (both client and server) with the network - let network_id = network_config.network_id; - if network_id.is_validator_network() { - // A validator node must have only a single consensus network handle - if consensus_network_handle.is_some() { - panic!("There can be at most one validator network!"); - } else { - consensus_network_handle = Some(register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - consensus_network_configuration(node_config), - )); - } - - if dkg_network_handle.is_some() { - panic!("There can be at most one validator network!"); - } else { - dkg_network_handle = Some(register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - dkg_network_configuration(node_config), - )); - } - - if jwk_consensus_network_handle.is_some() { - panic!("There can be at most one validator network!"); - } else { - jwk_consensus_network_handle = Some(register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - jwk_consensus_network_configuration(node_config), - )); - } - } - - // Register mempool (both client and server) with the network - let mempool_network_handle = register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - mempool_network_configuration(node_config), - ); - mempool_network_handles.push(mempool_network_handle); - - // Register the peer monitoring service (both client and server) with the network - let peer_monitoring_service_network_handle = register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - peer_monitoring_network_configuration(node_config), - ); - peer_monitoring_service_network_handles.push(peer_monitoring_service_network_handle); - - // Register the storage service (both client and server) with the network - let storage_service_network_handle = register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - storage_service_network_configuration(node_config), - ); - storage_service_network_handles.push(storage_service_network_handle); - - // Register benchmark test service - if let Some(app_config) = netbench_network_configuration(node_config) { - let netbench_handle = register_client_and_service_with_network( - &mut network_builder, - network_id, - &network_config, - app_config, - ); - netbench_handles.push(netbench_handle); - } - - // Build and start the network on the runtime - network_builder.build(runtime.handle().clone()); - network_builder.start(); - network_runtimes.push(runtime); - debug!( - "Network built for the network context: {}", - network_builder.network_context() - ); - } - - // Transform all network handles into application interfaces - let ( - consensus_interfaces, - dkg_interfaces, - jwk_consensus_interfaces, - mempool_interfaces, - peer_monitoring_service_interfaces, - storage_service_interfaces, - ) = transform_network_handles_into_interfaces( - node_config, - consensus_network_handle, - dkg_network_handle, - jwk_consensus_network_handle, - mempool_network_handles, - peer_monitoring_service_network_handles, - storage_service_network_handles, - peers_and_metadata.clone(), - ); - - if !netbench_handles.is_empty() { - let netbench_interfaces = create_network_interfaces( - netbench_handles, - netbench_network_configuration(node_config).unwrap(), - peers_and_metadata, - ); - let netbench_service_threads = node_config.netbench.unwrap().netbench_service_threads; - let netbench_runtime = - aptos_runtimes::spawn_named_runtime("benchmark".into(), netbench_service_threads); - start_netbench_service(node_config, netbench_interfaces, netbench_runtime.handle()); - network_runtimes.push(netbench_runtime); - } - - ( - network_runtimes, - consensus_interfaces, - dkg_interfaces, - jwk_consensus_interfaces, - mempool_interfaces, - peer_monitoring_service_interfaces, - storage_service_interfaces, - ) -} - -/// Creates a network runtime for the given network config -fn create_network_runtime(network_config: &NetworkConfig) -> Runtime { - let network_id = network_config.network_id; - debug!("Creating runtime for network ID: {}", network_id); - - // Create the runtime - let thread_name = format!( - "network-{}", - network_id.as_str().chars().take(3).collect::() - ); - aptos_runtimes::spawn_named_runtime(thread_name, network_config.runtime_threads) -} - -/// Registers a new application client and service with the network -fn register_client_and_service_with_network< - T: Serialize + for<'de> Deserialize<'de> + Send + 'static, ->( - network_builder: &mut NetworkBuilder, - network_id: NetworkId, - network_config: &NetworkConfig, - application_config: NetworkApplicationConfig, -) -> ApplicationNetworkHandle { - let (network_sender, network_events) = network_builder.add_client_and_service( - &application_config, - network_config.max_parallel_deserialization_tasks, - ); - ApplicationNetworkHandle { - network_id, - network_sender, - network_events, - } -} - -/// Tranforms the given network handles into interfaces that can -/// be used by the applications themselves. -fn transform_network_handles_into_interfaces( - node_config: &NodeConfig, - consensus_network_handle: Option>, - dkg_network_handle: Option>, - jwk_consensus_network_handle: Option>, - mempool_network_handles: Vec>, - peer_monitoring_service_network_handles: Vec< - ApplicationNetworkHandle, - >, - storage_service_network_handles: Vec>, - peers_and_metadata: Arc, -) -> ( - Option>, - Option>, - Option>, - ApplicationNetworkInterfaces, - ApplicationNetworkInterfaces, - ApplicationNetworkInterfaces, -) { - let consensus_interfaces = consensus_network_handle.map(|consensus_network_handle| { - create_network_interfaces( - vec![consensus_network_handle], - consensus_network_configuration(node_config), - peers_and_metadata.clone(), - ) - }); - - let dkg_interfaces = dkg_network_handle.map(|handle| { - create_network_interfaces( - vec![handle], - dkg_network_configuration(node_config), - peers_and_metadata.clone(), - ) - }); - - let jwk_consensus_interfaces = jwk_consensus_network_handle.map(|handle| { - create_network_interfaces( - vec![handle], - jwk_consensus_network_configuration(node_config), - peers_and_metadata.clone(), - ) - }); - - let mempool_interfaces = create_network_interfaces( - mempool_network_handles, - mempool_network_configuration(node_config), - peers_and_metadata.clone(), - ); - let peer_monitoring_service_interfaces = create_network_interfaces( - peer_monitoring_service_network_handles, - peer_monitoring_network_configuration(node_config), - peers_and_metadata.clone(), - ); - let storage_service_interfaces = create_network_interfaces( - storage_service_network_handles, - storage_service_network_configuration(node_config), - peers_and_metadata, - ); - - ( - consensus_interfaces, - dkg_interfaces, - jwk_consensus_interfaces, - mempool_interfaces, - peer_monitoring_service_interfaces, - storage_service_interfaces, - ) -} - -/// Creates an application network inteface using the given -/// handles and config. -fn create_network_interfaces< - T: Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone + 'static, ->( - network_handles: Vec>, - network_application_config: NetworkApplicationConfig, - peers_and_metadata: Arc, -) -> ApplicationNetworkInterfaces { - // Gather the network senders and events - let mut network_senders = HashMap::new(); - let mut network_and_events = HashMap::new(); - for network_handle in network_handles { - let network_id = network_handle.network_id; - network_senders.insert(network_id, network_handle.network_sender); - network_and_events.insert(network_id, network_handle.network_events); - } - - // Create the network client - let network_client_config = network_application_config.network_client_config; - let network_client = NetworkClient::new( - network_client_config.direct_send_protocols_and_preferences, - network_client_config.rpc_protocols_and_preferences, - network_senders, - peers_and_metadata, - ); - - // Create the network service events - let network_service_events = NetworkServiceEvents::new(network_and_events); - - // Create and return the new network interfaces - ApplicationNetworkInterfaces { - network_client, - network_service_events, - } -} diff --git a/aptos-node/src/services.rs b/aptos-node/src/services.rs deleted file mode 100644 index ad2c37a914c0f..0000000000000 --- a/aptos-node/src/services.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{bootstrap_api, indexer, mpsc::Receiver, network::ApplicationNetworkInterfaces}; -use aptos_admin_service::AdminService; -use aptos_build_info::build_information; -use aptos_config::config::NodeConfig; -use aptos_consensus::{ - network_interface::ConsensusMsg, persistent_liveness_storage::StorageWriteProxy, - quorum_store::quorum_store_db::QuorumStoreDB, -}; -use aptos_consensus_notifications::ConsensusNotifier; -use aptos_data_client::client::AptosDataClient; -use aptos_db_indexer::table_info_reader::TableInfoReader; -use aptos_event_notifications::{DbBackedOnChainConfig, ReconfigNotificationListener}; -use aptos_indexer_grpc_fullnode::runtime::bootstrap as bootstrap_indexer_grpc; -use aptos_indexer_grpc_table_info::runtime::bootstrap as bootstrap_indexer_table_info; -use aptos_logger::{debug, telemetry_log_writer::TelemetryLog, LoggerFilterUpdater}; -use aptos_mempool::{network::MempoolSyncMsg, MempoolClientRequest, QuorumStoreRequest}; -use aptos_mempool_notifications::MempoolNotificationListener; -use aptos_network::application::{interface::NetworkClientInterface, storage::PeersAndMetadata}; -use aptos_network_benchmark::{run_netbench_service, NetbenchMessage}; -use aptos_peer_monitoring_service_server::{ - network::PeerMonitoringServiceNetworkEvents, storage::StorageReader, - PeerMonitoringServiceServer, -}; -use aptos_peer_monitoring_service_types::PeerMonitoringServiceMessage; -use aptos_storage_interface::{DbReader, DbReaderWriter}; -use aptos_time_service::TimeService; -use aptos_types::chain_id::ChainId; -use aptos_validator_transaction_pool::VTxnPoolState; -use futures::channel::{mpsc, mpsc::Sender}; -use std::{sync::Arc, time::Instant}; -use tokio::runtime::{Handle, Runtime}; - -const AC_SMP_CHANNEL_BUFFER_SIZE: usize = 1_024; -const INTRA_NODE_CHANNEL_BUFFER_SIZE: usize = 1; - -/// Bootstraps the API and the indexer. Returns the Mempool client -/// receiver, and both the api and indexer runtimes. -pub fn bootstrap_api_and_indexer( - node_config: &NodeConfig, - db_rw: DbReaderWriter, - chain_id: ChainId, -) -> anyhow::Result<( - Receiver, - Option, - Option, - Option, - Option, -)> { - // Create the mempool client and sender - let (mempool_client_sender, mempool_client_receiver) = - mpsc::channel(AC_SMP_CHANNEL_BUFFER_SIZE); - - let (indexer_table_info_runtime, indexer_async_v2) = match bootstrap_indexer_table_info( - node_config, - chain_id, - db_rw.clone(), - mempool_client_sender.clone(), - ) { - Some((runtime, indexer_v2)) => (Some(runtime), Some(indexer_v2)), - None => (None, None), - }; - - // Create the API runtime - let table_info_reader: Option> = indexer_async_v2.map(|arc| { - let trait_object: Arc = arc; - trait_object - }); - let api_runtime = if node_config.api.enabled { - Some(bootstrap_api( - node_config, - chain_id, - db_rw.reader.clone(), - mempool_client_sender.clone(), - table_info_reader.clone(), - )?) - } else { - None - }; - - // Creates the indexer grpc runtime - let indexer_grpc = bootstrap_indexer_grpc( - node_config, - chain_id, - db_rw.reader.clone(), - mempool_client_sender.clone(), - table_info_reader, - ); - - // Create the indexer runtime - let indexer_runtime = indexer::bootstrap_indexer( - node_config, - chain_id, - db_rw.reader.clone(), - mempool_client_sender, - )?; - - Ok(( - mempool_client_receiver, - api_runtime, - indexer_table_info_runtime, - indexer_runtime, - indexer_grpc, - )) -} - -/// Starts consensus and returns the runtime -pub fn start_consensus_runtime( - node_config: &mut NodeConfig, - db_rw: DbReaderWriter, - consensus_reconfig_subscription: Option>, - consensus_network_interfaces: ApplicationNetworkInterfaces, - consensus_notifier: ConsensusNotifier, - consensus_to_mempool_sender: Sender, - vtxn_pool: VTxnPoolState, -) -> (Runtime, Arc, Arc) { - let instant = Instant::now(); - let consensus = aptos_consensus::consensus_provider::start_consensus( - node_config, - consensus_network_interfaces.network_client, - consensus_network_interfaces.network_service_events, - Arc::new(consensus_notifier), - consensus_to_mempool_sender, - db_rw, - consensus_reconfig_subscription - .expect("Consensus requires a reconfiguration subscription!"), - vtxn_pool, - ); - debug!("Consensus started in {} ms", instant.elapsed().as_millis()); - consensus -} - -/// Create the mempool runtime and start mempool -pub fn start_mempool_runtime_and_get_consensus_sender( - node_config: &mut NodeConfig, - db_rw: &DbReaderWriter, - mempool_reconfig_subscription: ReconfigNotificationListener, - network_interfaces: ApplicationNetworkInterfaces, - mempool_listener: MempoolNotificationListener, - mempool_client_receiver: Receiver, - peers_and_metadata: Arc, -) -> (Runtime, Sender) { - // Create a communication channel between consensus and mempool - let (consensus_to_mempool_sender, consensus_to_mempool_receiver) = - mpsc::channel(INTRA_NODE_CHANNEL_BUFFER_SIZE); - - // Bootstrap and start mempool - let instant = Instant::now(); - let mempool = aptos_mempool::bootstrap( - node_config, - Arc::clone(&db_rw.reader), - network_interfaces.network_client, - network_interfaces.network_service_events, - mempool_client_receiver, - consensus_to_mempool_receiver, - mempool_listener, - mempool_reconfig_subscription, - peers_and_metadata, - ); - debug!("Mempool started in {} ms", instant.elapsed().as_millis()); - - (mempool, consensus_to_mempool_sender) -} - -/// Spawns a new thread for the admin service -pub fn start_admin_service(node_config: &NodeConfig) -> AdminService { - AdminService::new(node_config) -} - -/// Spawns a new thread for the node inspection service -pub fn start_node_inspection_service( - node_config: &NodeConfig, - aptos_data_client: AptosDataClient, - peers_and_metadata: Arc, -) { - aptos_inspection_service::start_inspection_service( - node_config.clone(), - aptos_data_client, - peers_and_metadata, - ) -} - -/// Starts the peer monitoring service and returns the runtime -pub fn start_peer_monitoring_service( - node_config: &NodeConfig, - network_interfaces: ApplicationNetworkInterfaces, - db_reader: Arc, -) -> Runtime { - // Get the network client and events - let network_client = network_interfaces.network_client; - let network_service_events = network_interfaces.network_service_events; - - // Create a new runtime for the monitoring service - let peer_monitoring_service_runtime = - aptos_runtimes::spawn_named_runtime("peer-mon".into(), None); - - // Create and spawn the peer monitoring server - let peer_monitoring_network_events = - PeerMonitoringServiceNetworkEvents::new(network_service_events); - let peer_monitoring_server = PeerMonitoringServiceServer::new( - node_config.clone(), - peer_monitoring_service_runtime.handle().clone(), - peer_monitoring_network_events, - network_client.get_peers_and_metadata(), - StorageReader::new(db_reader), - TimeService::real(), - ); - peer_monitoring_service_runtime.spawn(peer_monitoring_server.start()); - - // Spawn the peer monitoring client - if node_config - .peer_monitoring_service - .enable_peer_monitoring_client - { - peer_monitoring_service_runtime.spawn( - aptos_peer_monitoring_service_client::start_peer_monitor( - node_config.clone(), - network_client, - Some(peer_monitoring_service_runtime.handle().clone()), - ), - ); - } - - // Return the runtime - peer_monitoring_service_runtime -} - -pub fn start_netbench_service( - node_config: &NodeConfig, - network_interfaces: ApplicationNetworkInterfaces, - runtime: &Handle, -) { - let network_client = network_interfaces.network_client; - runtime.spawn(run_netbench_service( - node_config.clone(), - network_client, - network_interfaces.network_service_events, - TimeService::real(), - )); -} - -/// Starts the telemetry service and grabs the build information -pub fn start_telemetry_service( - node_config: &NodeConfig, - remote_log_rx: Option>, - logger_filter_update_job: Option, - chain_id: ChainId, -) -> Option { - let build_info = build_information!(); - aptos_telemetry::service::start_telemetry_service( - node_config.clone(), - chain_id, - build_info, - remote_log_rx, - logger_filter_update_job, - ) -} diff --git a/aptos-node/src/state_sync.rs b/aptos-node/src/state_sync.rs deleted file mode 100644 index 4ae8f6492f948..0000000000000 --- a/aptos-node/src/state_sync.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::network::ApplicationNetworkInterfaces; -use aptos_config::config::{NodeConfig, StateSyncConfig}; -use aptos_consensus_notifications::ConsensusNotifier; -use aptos_data_client::{client::AptosDataClient, poller}; -use aptos_data_streaming_service::{ - streaming_client::{new_streaming_service_client_listener_pair, StreamingServiceClient}, - streaming_service::DataStreamingService, -}; -use aptos_event_notifications::{ - DbBackedOnChainConfig, EventNotificationListener, EventSubscriptionService, - ReconfigNotificationListener, -}; -use aptos_executor::chunk_executor::ChunkExecutor; -use aptos_infallible::RwLock; -use aptos_mempool_notifications::MempoolNotificationListener; -use aptos_network::application::{ - interface::{NetworkClient, NetworkClientInterface, NetworkServiceEvents}, - storage::PeersAndMetadata, -}; -use aptos_state_sync_driver::{ - driver_factory::{DriverFactory, StateSyncRuntimes}, - metadata_storage::PersistentMetadataStorage, -}; -use aptos_storage_interface::{DbReader, DbReaderWriter}; -use aptos_storage_service_client::StorageServiceClient; -use aptos_storage_service_notifications::StorageServiceNotificationListener; -use aptos_storage_service_server::{ - network::StorageServiceNetworkEvents, storage::StorageReader, StorageServiceServer, -}; -use aptos_storage_service_types::StorageServiceMessage; -use aptos_time_service::TimeService; -use aptos_types::waypoint::Waypoint; -use aptos_vm::AptosVM; -use std::sync::Arc; -use tokio::runtime::Runtime; - -/// Creates the event subscription service and two reconfiguration -/// notification listeners (for mempool and consensus, respectively). -pub fn create_event_subscription_service( - node_config: &NodeConfig, - db_rw: &DbReaderWriter, -) -> ( - EventSubscriptionService, - ReconfigNotificationListener, - Option>, - Option<( - ReconfigNotificationListener, - EventNotificationListener, - )>, // (reconfig_events, dkg_start_events) for DKG - Option<( - ReconfigNotificationListener, - EventNotificationListener, - )>, // (reconfig_events, jwk_updated_events) for JWK consensus -) { - // Create the event subscription service - let mut event_subscription_service = - EventSubscriptionService::new(Arc::new(RwLock::new(db_rw.clone()))); - - // Create a reconfiguration subscription for mempool - let mempool_reconfig_subscription = event_subscription_service - .subscribe_to_reconfigurations() - .expect("Mempool must subscribe to reconfigurations"); - - // Create a reconfiguration subscription for consensus (if this is a validator) - let consensus_reconfig_subscription = if node_config.base.role.is_validator() { - Some( - event_subscription_service - .subscribe_to_reconfigurations() - .expect("Consensus must subscribe to reconfigurations"), - ) - } else { - None - }; - - let dkg_subscriptions = if node_config.base.role.is_validator() { - let reconfig_events = event_subscription_service - .subscribe_to_reconfigurations() - .expect("DKG must subscribe to reconfigurations"); - let dkg_start_events = event_subscription_service - .subscribe_to_events(vec![], vec!["0x1::dkg::DKGStartEvent".to_string()]) - .expect("Consensus must subscribe to DKG events"); - Some((reconfig_events, dkg_start_events)) - } else { - None - }; - - let jwk_consensus_subscriptions = if node_config.base.role.is_validator() { - let reconfig_events = event_subscription_service - .subscribe_to_reconfigurations() - .expect("JWK consensus must subscribe to reconfigurations"); - let jwk_updated_events = event_subscription_service - .subscribe_to_events(vec![], vec!["0x1::jwks::ObservedJWKsUpdated".to_string()]) - .expect("JWK consensus must subscribe to DKG events"); - Some((reconfig_events, jwk_updated_events)) - } else { - None - }; - - ( - event_subscription_service, - mempool_reconfig_subscription, - consensus_reconfig_subscription, - dkg_subscriptions, - jwk_consensus_subscriptions, - ) -} - -/// Sets up all state sync runtimes and return the notification endpoints -pub fn start_state_sync_and_get_notification_handles( - node_config: &NodeConfig, - storage_network_interfaces: ApplicationNetworkInterfaces, - waypoint: Waypoint, - event_subscription_service: EventSubscriptionService, - db_rw: DbReaderWriter, -) -> anyhow::Result<( - AptosDataClient, - StateSyncRuntimes, - MempoolNotificationListener, - ConsensusNotifier, -)> { - // Get the network client and events - let network_client = storage_network_interfaces.network_client; - let network_service_events = storage_network_interfaces.network_service_events; - - // Start the data client - let peers_and_metadata = network_client.get_peers_and_metadata(); - let (aptos_data_client, aptos_data_client_runtime) = - setup_aptos_data_client(node_config, network_client, db_rw.reader.clone())?; - - // Start the data streaming service - let state_sync_config = node_config.state_sync; - let (streaming_service_client, streaming_service_runtime) = - setup_data_streaming_service(state_sync_config, aptos_data_client.clone())?; - - // Create the chunk executor and persistent storage - let chunk_executor = Arc::new(ChunkExecutor::::new(db_rw.clone())); - let metadata_storage = PersistentMetadataStorage::new(&node_config.storage.dir()); - - // Create notification senders and listeners for mempool, consensus and the storage service - let (mempool_notifier, mempool_listener) = - aptos_mempool_notifications::new_mempool_notifier_listener_pair( - state_sync_config - .state_sync_driver - .max_pending_mempool_notifications, - ); - let (consensus_notifier, consensus_listener) = - aptos_consensus_notifications::new_consensus_notifier_listener_pair( - state_sync_config - .state_sync_driver - .commit_notification_timeout_ms, - ); - let (storage_service_notifier, storage_service_listener) = - aptos_storage_service_notifications::new_storage_service_notifier_listener_pair(); - - // Start the state sync storage service - let storage_service_runtime = setup_state_sync_storage_service( - state_sync_config, - peers_and_metadata, - network_service_events, - &db_rw, - storage_service_listener, - )?; - - // Create the state sync driver factory - let state_sync = DriverFactory::create_and_spawn_driver( - true, - node_config, - waypoint, - db_rw, - chunk_executor, - mempool_notifier, - storage_service_notifier, - metadata_storage, - consensus_listener, - event_subscription_service, - aptos_data_client.clone(), - streaming_service_client, - TimeService::real(), - ); - - // Create a new state sync runtime handle - let state_sync_runtimes = StateSyncRuntimes::new( - aptos_data_client_runtime, - state_sync, - storage_service_runtime, - streaming_service_runtime, - ); - - Ok(( - aptos_data_client, - state_sync_runtimes, - mempool_listener, - consensus_notifier, - )) -} - -/// Sets up the data streaming service runtime -fn setup_data_streaming_service( - state_sync_config: StateSyncConfig, - aptos_data_client: AptosDataClient, -) -> anyhow::Result<(StreamingServiceClient, Runtime)> { - // Create the data streaming service - let (streaming_service_client, streaming_service_listener) = - new_streaming_service_client_listener_pair(); - let data_streaming_service = DataStreamingService::new( - state_sync_config.aptos_data_client, - state_sync_config.data_streaming_service, - aptos_data_client, - streaming_service_listener, - TimeService::real(), - ); - - // Start the data streaming service - let streaming_service_runtime = aptos_runtimes::spawn_named_runtime("stream-serv".into(), None); - streaming_service_runtime.spawn(data_streaming_service.start_service()); - - Ok((streaming_service_client, streaming_service_runtime)) -} - -/// Sets up the aptos data client runtime -fn setup_aptos_data_client( - node_config: &NodeConfig, - network_client: NetworkClient, - storage: Arc, -) -> anyhow::Result<(AptosDataClient, Runtime)> { - // Create the storage service client - let storage_service_client = StorageServiceClient::new(network_client); - - // Create a new runtime for the data client - let aptos_data_client_runtime = aptos_runtimes::spawn_named_runtime("data-client".into(), None); - - // Create the data client and spawn the data poller - let (aptos_data_client, data_summary_poller) = AptosDataClient::new( - node_config.state_sync.aptos_data_client, - node_config.base.clone(), - TimeService::real(), - storage, - storage_service_client, - Some(aptos_data_client_runtime.handle().clone()), - ); - aptos_data_client_runtime.spawn(poller::start_poller(data_summary_poller)); - - Ok((aptos_data_client, aptos_data_client_runtime)) -} - -/// Sets up the state sync storage service runtime -fn setup_state_sync_storage_service( - config: StateSyncConfig, - peers_and_metadata: Arc, - network_service_events: NetworkServiceEvents, - db_rw: &DbReaderWriter, - storage_service_listener: StorageServiceNotificationListener, -) -> anyhow::Result { - // Create a new state sync storage service runtime - let storage_service_runtime = aptos_runtimes::spawn_named_runtime("stor-server".into(), None); - - // Spawn the state sync storage service servers on the runtime - let storage_reader = StorageReader::new(config.storage_service, Arc::clone(&db_rw.reader)); - let service = StorageServiceServer::new( - config, - storage_service_runtime.handle().clone(), - storage_reader, - TimeService::real(), - peers_and_metadata, - StorageServiceNetworkEvents::new(network_service_events), - storage_service_listener, - ); - storage_service_runtime.spawn(service.start()); - - Ok(storage_service_runtime) -} diff --git a/aptos-node/src/storage.rs b/aptos-node/src/storage.rs deleted file mode 100644 index 60e9d6f4b5833..0000000000000 --- a/aptos-node/src/storage.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{anyhow, Result}; -use aptos_backup_service::start_backup_service; -use aptos_config::{config::NodeConfig, utils::get_genesis_txn}; -use aptos_db::{fast_sync_storage_wrapper::FastSyncStorageWrapper, AptosDB}; -use aptos_executor::db_bootstrapper::maybe_bootstrap; -use aptos_logger::{debug, info}; -use aptos_storage_interface::{DbReader, DbReaderWriter}; -use aptos_types::{ledger_info::LedgerInfoWithSignatures, waypoint::Waypoint}; -use aptos_vm::AptosVM; -use either::Either; -use std::{fs, path::Path, sync::Arc, time::Instant}; -use tokio::runtime::Runtime; - -pub(crate) fn maybe_apply_genesis( - db_rw: &DbReaderWriter, - node_config: &NodeConfig, -) -> Result> { - // We read from the storage genesis waypoint and fallback to the node config one if it is none - let genesis_waypoint = node_config - .execution - .genesis_waypoint - .as_ref() - .unwrap_or(&node_config.base.waypoint) - .genesis_waypoint(); - if let Some(genesis) = get_genesis_txn(node_config) { - let ledger_info_opt = maybe_bootstrap::(db_rw, genesis, genesis_waypoint) - .map_err(|err| anyhow!("DB failed to bootstrap {}", err))?; - Ok(ledger_info_opt) - } else { - info ! ("Genesis txn not provided! This is fine only if you don't expect to apply it. Otherwise, the config is incorrect!"); - Ok(None) - } -} - -#[cfg(not(feature = "consensus-only-perf-test"))] -pub(crate) fn bootstrap_db( - node_config: &NodeConfig, -) -> Result<(Arc, DbReaderWriter, Option)> { - let (aptos_db_reader, db_rw, backup_service) = - match FastSyncStorageWrapper::initialize_dbs(node_config)? { - Either::Left(db) => { - let (db_arc, db_rw) = DbReaderWriter::wrap(db); - let db_backup_service = start_backup_service( - node_config.storage.backup_service_address, - db_arc.clone(), - ); - maybe_apply_genesis(&db_rw, node_config)?; - (db_arc as Arc, db_rw, Some(db_backup_service)) - }, - Either::Right(fast_sync_db_wrapper) => { - let temp_db = fast_sync_db_wrapper.get_temporary_db_with_genesis(); - maybe_apply_genesis(&DbReaderWriter::from_arc(temp_db), node_config)?; - let (db_arc, db_rw) = DbReaderWriter::wrap(fast_sync_db_wrapper); - let fast_sync_db = db_arc.get_fast_sync_db(); - // FastSyncDB requires ledger info at epoch 0 to establish provenance to genesis - let ledger_info = db_arc - .get_temporary_db_with_genesis() - .get_epoch_ending_ledger_info(0) - .expect("Genesis ledger info must exist"); - - if fast_sync_db - .get_latest_ledger_info_option() - .expect("should returns Ok results") - .is_none() - { - // it means the DB is empty and we need to - // commit the genesis ledger info to the DB. - fast_sync_db.commit_genesis_ledger_info(&ledger_info)?; - } - - let db_backup_service = - start_backup_service(node_config.storage.backup_service_address, fast_sync_db); - - (db_arc as Arc, db_rw, Some(db_backup_service)) - }, - }; - - Ok((aptos_db_reader, db_rw, backup_service)) -} - -/// In consensus-only mode, return a in-memory based [FakeAptosDB] and -/// do not run the backup service. -#[cfg(feature = "consensus-only-perf-test")] -pub(crate) fn bootstrap_db( - node_config: &NodeConfig, -) -> Result<(Arc, DbReaderWriter, Option)> { - use aptos_db::fake_aptosdb::FakeAptosDB; - - let aptos_db = AptosDB::open( - &node_config.storage.dir(), - false, /* readonly */ - node_config.storage.storage_pruner_config, - node_config.storage.rocksdb_configs, - node_config.storage.enable_indexer, - node_config.storage.buffered_state_target_items, - node_config.storage.max_num_nodes_per_lru_cache_shard, - ) - .map_err(|err| anyhow!("DB failed to open {}", err))?; - let (aptos_db, db_rw) = DbReaderWriter::wrap(FakeAptosDB::new(aptos_db)); - maybe_apply_genesis(&db_rw, node_config)?; - Ok((aptos_db, db_rw, None)) -} - -/// Creates a RocksDb checkpoint for the consensus_db, state_sync_db, -/// ledger_db and state_merkle_db and saves it to the checkpoint_path. -/// Also, changes the working directory to run the node on the new path, -/// so that the existing data won't change. For now this is a test-only feature. -fn create_rocksdb_checkpoint_and_change_working_dir( - node_config: &mut NodeConfig, - working_dir: impl AsRef, -) { - // Update the source and checkpoint directories - let source_dir = node_config.storage.dir(); - node_config.set_data_dir(working_dir.as_ref().to_path_buf()); - let checkpoint_dir = node_config.storage.dir(); - assert!(source_dir != checkpoint_dir); - - // Create rocksdb checkpoint directory - fs::create_dir_all(&checkpoint_dir).unwrap(); - - // Open the database and create a checkpoint - AptosDB::create_checkpoint( - &source_dir, - &checkpoint_dir, - node_config.storage.rocksdb_configs.enable_storage_sharding, - ) - .expect("AptosDB checkpoint creation failed."); - - // Create a consensus db checkpoint - aptos_consensus::create_checkpoint(&source_dir, &checkpoint_dir) - .expect("ConsensusDB checkpoint creation failed."); - - // Create a state sync db checkpoint - let state_sync_db = - aptos_state_sync_driver::metadata_storage::PersistentMetadataStorage::new(&source_dir); - state_sync_db - .create_checkpoint(&checkpoint_dir) - .expect("StateSyncDB checkpoint creation failed."); -} - -/// Creates any rocksdb checkpoints, opens the storage database, -/// starts the backup service, handles genesis initialization and returns -/// the various handles. -pub fn initialize_database_and_checkpoints( - node_config: &mut NodeConfig, -) -> Result<(DbReaderWriter, Option, Waypoint)> { - // If required, create RocksDB checkpoints and change the working directory. - // This is test-only. - if let Some(working_dir) = node_config.base.working_dir.clone() { - create_rocksdb_checkpoint_and_change_working_dir(node_config, working_dir); - } - - // Open the database - let instant = Instant::now(); - let (_aptos_db, db_rw, backup_service) = bootstrap_db(node_config)?; - - // Log the duration to open storage - debug!( - "Storage service started in {} ms", - instant.elapsed().as_millis() - ); - - Ok(( - db_rw, - backup_service, - node_config.base.waypoint.genesis_waypoint(), - )) -} diff --git a/aptos-node/src/tests.rs b/aptos-node/src/tests.rs deleted file mode 100644 index 5ea040d2135a7..0000000000000 --- a/aptos-node/src/tests.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{create_single_node_test_config, network}; -use aptos_config::config::{NodeConfig, WaypointConfig}; -use aptos_event_notifications::EventSubscriptionService; -use aptos_infallible::RwLock; -use aptos_storage_interface::{DbReader, DbReaderWriter, DbWriter}; -use aptos_temppath::TempPath; -use aptos_types::{chain_id::ChainId, waypoint::Waypoint}; -use rand::SeedableRng; -use std::{fs, sync::Arc}; - -/// A mock database implementing DbReader and DbWriter -pub struct MockDatabase; -impl DbReader for MockDatabase {} -impl DbWriter for MockDatabase {} - -#[test] -#[should_panic(expected = "Validator networks must always have mutual_authentication enabled!")] -fn test_mutual_authentication_validators() { - // Create a default node config for the validator - let temp_path = TempPath::new(); - let mut node_config = NodeConfig::get_default_validator_config(); - node_config.set_data_dir(temp_path.path().to_path_buf()); - node_config.base.waypoint = WaypointConfig::FromConfig(Waypoint::default()); - - // Disable mutual authentication for the config - let validator_network = node_config.validator_network.as_mut().unwrap(); - validator_network.mutual_authentication = false; - - // Create an event subscription service - let mut event_subscription_service = - EventSubscriptionService::new(Arc::new(RwLock::new(DbReaderWriter::new(MockDatabase {})))); - - // Set up the networks and gather the application network handles. This should panic. - let peers_and_metadata = network::create_peers_and_metadata(&node_config); - let _ = network::setup_networks_and_get_interfaces( - &node_config, - ChainId::test(), - peers_and_metadata, - &mut event_subscription_service, - ); -} - -#[cfg(feature = "check-vm-features")] -#[test] -fn test_aptos_vm_does_not_have_test_natives() { - aptos_vm::natives::assert_no_test_natives(crate::utils::ERROR_MSG_BAD_FEATURE_FLAGS) -} - -// This test confirms that the overriding behavior works as intended. -#[test] -fn test_create_single_node_test_config() { - // Create a test config override and merge it with the default config. - // This will get cleaned up by the tempdir when it goes out of scope. - let test_dir = aptos_temppath::TempPath::new().as_ref().to_path_buf(); - fs::DirBuilder::new() - .recursive(true) - .create(&test_dir) - .expect("Failed to create test_dir"); - let config_override_path = test_dir.join("override.yaml"); - let config_override: serde_yaml::Value = serde_yaml::from_str( - r#" - storage: - enable_indexer: true - indexer_grpc: - enabled: true - address: 0.0.0.0:50053 - processor_task_count: 10 - processor_batch_size: 100 - output_batch_size: 100 - api: - address: 0.0.0.0:8081 - execution: - genesis_waypoint: - from_config: "0:6072b68a942aace147e0655c5704beaa255c84a7829baa4e72a500f1516584c4" - "#, - ) - .unwrap(); - let f = std::fs::OpenOptions::new() - .write(true) - .create(true) - .open(&config_override_path) - .expect("Couldn't open file"); - serde_yaml::to_writer(f, &config_override).unwrap(); - - // merge it - let default_node_config = NodeConfig::get_default_validator_config(); - let merged_config = create_single_node_test_config( - &None, - &Some(config_override_path), - &test_dir, - false, - false, - aptos_cached_packages::head_release_bundle(), - rand::rngs::StdRng::from_entropy(), - ) - .unwrap(); - - // overriden configs - assert!(merged_config.storage.enable_indexer); - assert!(merged_config.indexer_grpc.enabled); - // default config is unchanged - assert_eq!( - merged_config - .state_sync - .state_sync_driver - .bootstrapping_mode, - default_node_config - .state_sync - .state_sync_driver - .bootstrapping_mode - ); -} diff --git a/aptos-node/src/utils.rs b/aptos-node/src/utils.rs deleted file mode 100644 index 285e335aab1df..0000000000000 --- a/aptos-node/src/utils.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::anyhow; -use aptos_config::config::{NodeConfig, DEFAULT_CONCURRENCY_LEVEL}; -use aptos_storage_interface::{state_view::LatestDbStateCheckpointView, DbReaderWriter}; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, account_view::AccountView, chain_id::ChainId, - state_store::account_with_state_view::AsAccountWithStateView, -}; -use aptos_vm::AptosVM; -use std::cmp::min; - -/// Error message to display when non-production features are enabled -pub const ERROR_MSG_BAD_FEATURE_FLAGS: &str = r#" -aptos-node was compiled with feature flags that shouldn't be enabled. - -This is caused by cargo's feature unification. -When you compile two crates with a shared dependency, if one enables a feature flag for the dependency, then it is also enabled for the other crate. - -PLEASE RECOMPILE APTOS-NODE SEPARATELY using the following command: - cargo build --package aptos-node - -"#; - -/// Initializes a global rayon thread pool iff `create_global_rayon_pool` is true -pub fn create_global_rayon_pool(create_global_rayon_pool: bool) { - if create_global_rayon_pool { - rayon::ThreadPoolBuilder::new() - .thread_name(|index| format!("rayon-global-{}", index)) - .build_global() - .expect("Failed to build rayon global thread pool."); - } -} - -/// Fetches the chain ID from on-chain resources -pub fn fetch_chain_id(db: &DbReaderWriter) -> anyhow::Result { - let db_state_view = db - .reader - .latest_state_checkpoint_view() - .map_err(|err| anyhow!("[aptos-node] failed to create db state view {}", err))?; - Ok(db_state_view - .as_account_with_state_view(&CORE_CODE_ADDRESS) - .get_chain_id_resource() - .map_err(|err| anyhow!("[aptos-node] failed to get chain id resource {}", err))? - .expect("[aptos-node] missing chain ID resource") - .chain_id()) -} - -/// Sets the Aptos VM configuration based on the node configurations -pub fn set_aptos_vm_configurations(node_config: &NodeConfig) { - AptosVM::set_paranoid_type_checks(node_config.execution.paranoid_type_verification); - let effective_concurrency_level = if node_config.execution.concurrency_level == 0 { - min(DEFAULT_CONCURRENCY_LEVEL, (num_cpus::get() / 2) as u16) - } else { - node_config.execution.concurrency_level - }; - AptosVM::set_concurrency_level_once(effective_concurrency_level as usize); - AptosVM::set_discard_failed_blocks(node_config.execution.discard_failed_blocks); - AptosVM::set_num_proof_reading_threads_once( - node_config.execution.num_proof_reading_threads as usize, - ); - - if node_config - .execution - .processed_transactions_detailed_counters - { - AptosVM::set_processed_transactions_detailed_counters(); - } -} diff --git a/crates/aptos-inspection-service/Cargo.toml b/crates/aptos-inspection-service/Cargo.toml deleted file mode 100644 index 9aa95351d226b..0000000000000 --- a/crates/aptos-inspection-service/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "aptos-inspection-service" -description = "The Node Inspection Service" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-build-info = { workspace = true } -aptos-config = { workspace = true } -aptos-data-client = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-network = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-storage-service-client = { workspace = true } -aptos-telemetry = { workspace = true } -aptos-time-service = { workspace = true } -futures = { workspace = true } -hyper = { workspace = true } -once_cell = { workspace = true } -prometheus = { workspace = true } -reqwest = { workspace = true } -serde_json = { workspace = true } -sysinfo = { workspace = true } -tokio = { workspace = true } - -[dev-dependencies] -aptos-time-service = { workspace = true, features = ["testing"] } -assert_approx_eq = { workspace = true } -rusty-fork = { workspace = true } diff --git a/crates/aptos-inspection-service/src/inspection_client.rs b/crates/aptos-inspection-service/src/inspection_client.rs deleted file mode 100644 index 3cddb49c5c72d..0000000000000 --- a/crates/aptos-inspection-service/src/inspection_client.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use reqwest::Url; -use std::collections::HashMap; - -/// A simple metric value enum (to represent different value types) -#[derive(Clone, Debug)] -pub enum MetricValue { - I64(i64), - F64(f64), - I64orF64(i64, f64), -} - -impl MetricValue { - /// Convert the value to i64 - pub fn to_i64(&self) -> Result { - match self { - MetricValue::I64(v) => Ok(*v), - MetricValue::F64(v) => Err(anyhow::format_err!("Value not i64: {}", v)), - MetricValue::I64orF64(v, _) => Ok(*v), - } - } - - /// Convert the value to f64 - pub fn to_f64(&self) -> Result { - match self { - MetricValue::I64(v) => Err(anyhow::format_err!("Value not f64: {}", v)), - MetricValue::F64(v) => Ok(*v), - MetricValue::I64orF64(_, v) => Ok(*v), - } - } -} - -/// A simple inspection client for querying metrics from a node -pub struct InspectionClient { - client: reqwest::Client, - url: Url, -} - -impl InspectionClient { - /// Create a new client from the given url - pub fn new(url: Url) -> Self { - let client = reqwest::Client::new(); - Self { client, url } - } - - /// Get an i64 metric value from the node - pub async fn get_node_metric_i64>(&self, metric: S) -> Result> { - let node_metrics = self.get_forge_metrics().await?; - node_metrics - .get(metric.as_ref()) - .map_or(Ok(None), |v| v.to_i64().map(Some)) - } - - /// Retrieves all node metrics for a given metric name - pub async fn get_node_metric_with_name( - &self, - metric_name: &str, - ) -> Result>> { - let metrics = self.get_forge_metrics().await?; - let search_string = format!("{}{{", metric_name); - - // Filter out all metrics that don't start with the search string - let result: HashMap<_, _> = metrics - .iter() - .filter_map(|(key, value)| { - if key.starts_with(&search_string) { - Some((key.clone(), value.clone())) - } else { - None - } - }) - .collect(); - - // Return None if the result is empty - if result.is_empty() { - Ok(None) - } else { - Ok(Some(result)) - } - } - - /// Fetches and returns all node metrics by pinging the forge_metrics endpoint - pub async fn get_forge_metrics(&self) -> Result> { - let mut url = self.url.clone(); - url.set_path("forge_metrics"); - - // Fetch the metrics from the node - let response = self.client.get(url).send().await?; - response - .json::>() - .await? - .into_iter() - .map(|(k, v)| match (v.parse::(), v.parse::()) { - (Ok(v), Err(_)) => Ok((k, MetricValue::I64(v))), - (Err(_), Ok(v)) => Ok((k, MetricValue::F64(v))), - (Ok(iv), Ok(fv)) => Ok((k, MetricValue::I64orF64(iv, fv))), - (Err(_), Err(_)) => Err(anyhow::format_err!( - "Failed to parse stat value to i64 or f64 {}: {}", - &k, - &v - )), - }) - .collect() - } -} diff --git a/crates/aptos-inspection-service/src/lib.rs b/crates/aptos-inspection-service/src/lib.rs deleted file mode 100644 index 04b550bd4adb3..0000000000000 --- a/crates/aptos-inspection-service/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod inspection_client; -pub mod server; - -pub use server::*; diff --git a/crates/aptos-inspection-service/src/server/configuration.rs b/crates/aptos-inspection-service/src/server/configuration.rs deleted file mode 100644 index 8b76471d99787..0000000000000 --- a/crates/aptos-inspection-service/src/server/configuration.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::CONTENT_TYPE_TEXT; -use aptos_config::config::NodeConfig; -use hyper::{Body, StatusCode}; - -// The message to display when the configuration endpoint is disabled -pub const CONFIGURATION_DISABLED_MESSAGE: &str = - "This endpoint is disabled! Enable it in the node config at inspection_service.expose_configuration: true"; - -/// Handles a new configuration request -pub fn handle_configuration_request(node_config: &NodeConfig) -> (StatusCode, Body, String) { - // Only return configuration if the endpoint is enabled - let (status_code, body) = if node_config.inspection_service.expose_configuration { - // We format the configuration using debug formatting. This is important to - // prevent secret/private keys from being serialized and leaked (i.e., - // all secret keys are marked with SilentDisplay and SilentDebug). - let encoded_configuration = format!("{:?}", node_config); - (StatusCode::OK, Body::from(encoded_configuration)) - } else { - ( - StatusCode::FORBIDDEN, - Body::from(CONFIGURATION_DISABLED_MESSAGE), - ) - }; - - (status_code, body, CONTENT_TYPE_TEXT.into()) -} diff --git a/crates/aptos-inspection-service/src/server/index.rs b/crates/aptos-inspection-service/src/server/index.rs deleted file mode 100644 index ef8ccfea8f24a..0000000000000 --- a/crates/aptos-inspection-service/src/server/index.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - server::utils::CONTENT_TYPE_TEXT, CONFIGURATION_PATH, FORGE_METRICS_PATH, JSON_METRICS_PATH, - METRICS_PATH, PEER_INFORMATION_PATH, SYSTEM_INFORMATION_PATH, -}; -use hyper::{Body, StatusCode}; - -/// Handles a new index request -pub fn handle_index_request() -> (StatusCode, Body, String) { - ( - StatusCode::OK, - Body::from(get_index_response()), - CONTENT_TYPE_TEXT.into(), - ) -} - -/// Returns the response for the index page. The response -/// simply lists a welcome message and all available endpoints. -fn get_index_response() -> String { - let mut index_response: Vec = Vec::new(); - - // Add the list of available endpoints - index_response.push("Welcome to the Aptos Inspection Service!".into()); - index_response.push("The following endpoints are available:".into()); - index_response.push(format!("\t- {}", CONFIGURATION_PATH)); - index_response.push(format!("\t- {}", FORGE_METRICS_PATH)); - index_response.push(format!("\t- {}", JSON_METRICS_PATH)); - index_response.push(format!("\t- {}", METRICS_PATH)); - index_response.push(format!("\t- {}", PEER_INFORMATION_PATH)); - index_response.push(format!("\t- {}", SYSTEM_INFORMATION_PATH)); - - index_response.join("\n") // Separate each entry with a newline -} diff --git a/crates/aptos-inspection-service/src/server/json_encoder.rs b/crates/aptos-inspection-service/src/server/json_encoder.rs deleted file mode 100644 index ff29edccf71c9..0000000000000 --- a/crates/aptos-inspection-service/src/server/json_encoder.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils::CONTENT_TYPE_JSON; -use aptos_logger::error; -use prometheus::{ - proto::{LabelPair, Metric, MetricFamily, MetricType}, - Encoder, Result, -}; -use std::{collections::HashMap, io::Write}; - -// TODO: figure out if we really need all metric endpoints... - -/// An implementation of an [`Encoder`](::Encoder) that converts a `MetricFamily` proto message -/// into `fbagent` json. -/// -/// This implementation converts metric{dimensions,...} -> value to a flat string with a value. -/// e.g., `"requests{method="GET", service="accounts"} -> 8` into `requests.GET.account -> 8`. -/// For now, it ignores timestamps (if set on the metric). -#[derive(Debug, Default)] -pub struct JsonEncoder; - -impl Encoder for JsonEncoder { - fn encode(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> { - let mut encoded_metrics: HashMap = HashMap::new(); - - // Go through each metric family and encode it - for metric_family in metric_families { - let name = metric_family.get_name(); - let metric_type = metric_family.get_field_type(); - for metric in metric_family.get_metric() { - match metric_type { - MetricType::COUNTER => { - encoded_metrics.insert( - flatten_metric_with_labels(name, metric), - metric.get_counter().get_value(), - ); - }, - MetricType::GAUGE => { - encoded_metrics.insert( - flatten_metric_with_labels(name, metric), - metric.get_gauge().get_value(), - ); - }, - MetricType::HISTOGRAM => { - // write the sum and counts - let h = metric.get_histogram(); - encoded_metrics.insert( - flatten_metric_with_labels(&format!("{}_count", name), metric), - h.get_sample_count() as f64, - ); - encoded_metrics.insert( - flatten_metric_with_labels(&format!("{}_sum", name), metric), - h.get_sample_sum(), - ); - }, - _ => { - // Do nothing (not supported) - }, - } - } - } - - // Write the encoded metrics to the writer - match serde_json::to_string(&encoded_metrics) { - Ok(json_encoded_metrics) => { - writer.write_all(json_encoded_metrics.as_bytes())?; - }, - Err(error) => { - error!("Failed to JSON encode the metrics! Error: {}", error); - }, - }; - - Ok(()) - } - - fn format_type(&self) -> &str { - CONTENT_TYPE_JSON - } -} - -/** -This method takes Prometheus metrics with dimensions (represented as label:value tags) -and converts it into a dot-separated string. - -Example: -Prometheus metric: error_count{method: "get_account", error="connection_error"} -Result: error_count.get_account.connection_error - -If the set of labels is empty, only the name is returned -Example: -Prometheus metric: errors -Result: errors - -This is useful when exporting metric data to flat time series. -*/ -fn flatten_metric_with_labels(name: &str, metric: &Metric) -> String { - // If the metric has no labels, return the name - let name_string = String::from(name); - if metric.get_label().is_empty() { - return name_string; - } - - // Join the values of the labels with "." - let values: Vec<&str> = metric - .get_label() - .iter() - .map(LabelPair::get_value) - .filter(|&x| !x.is_empty()) - .collect(); - let values = values.join("."); - - // If the values are empty, return the name - if values.is_empty() { - return name_string; - } - - // Otherwise, return the name with the values - format!("{}.{}", name_string, values) -} - -#[cfg(test)] -mod tests { - use super::*; - use prometheus::{ - core::{Collector, Metric}, - IntCounter, IntCounterVec, Opts, - }; - use serde_json::Value; - - #[test] - fn test_flatten_labels() { - // Generate a counter for testing - let counter_name_1 = "counter_1"; - let counter_1 = IntCounter::new(counter_name_1, "Test counter 1").unwrap(); - - // Flatten the metric and check the result - let flattened_metric = flatten_metric_with_labels(counter_name_1, &counter_1.metric()); - assert_eq!(flattened_metric, counter_name_1.to_string()); - - // Generate another counter for testing - let counter_name_2 = "counter_2"; - let counter_2 = - IntCounterVec::new(Opts::new(counter_name_2, "Test counter 2"), &["label_me"]).unwrap(); - - // Flatten the metric (without a label) and check the result - let flattened_metric = flatten_metric_with_labels( - counter_name_2, - &counter_2.with_label_values(&[""]).metric(), - ); - assert_eq!(flattened_metric, counter_name_2.to_string()); - - // Flatten the metric (with a label) and check the result - let flattened_metric = flatten_metric_with_labels( - counter_name_2, - &counter_2.with_label_values(&["hello"]).metric(), - ); - assert_eq!(flattened_metric, "counter_2.hello".to_string()); - - // Generate another counter for testing - let another_counter_2 = - IntCounterVec::new(Opts::new(counter_name_2, "Example counter for testing"), &[ - "label_me", - "label_me_too", - ]) - .unwrap(); - - // Flatten a mismatched metric (without a label) and check the result - let counter_name_3 = "counter_3"; - let flattened_metric = flatten_metric_with_labels( - counter_name_3, - &another_counter_2.with_label_values(&["", ""]).metric(), - ); - assert_eq!(flattened_metric, counter_name_3.to_string()); - - // Flatten a mismatched metric (with a label) and check the result - let flattened_metric = flatten_metric_with_labels( - counter_name_3, - &another_counter_2 - .with_label_values(&["hello", "world"]) - .metric(), - ); - assert_eq!(flattened_metric, "counter_3.hello.world"); - } - - #[test] - fn test_encoder() { - // Generate a counter for testing - let counter = IntCounterVec::new(Opts::new("testing_count", "Test Counter"), &[ - "method", "result", - ]) - .unwrap(); - - // Add test data to the counter - counter.with_label_values(&["get", "302"]).inc(); - counter.with_label_values(&["get", "302"]).inc(); - counter.with_label_values(&["get", "404"]).inc(); - counter.with_label_values(&["put", ""]).inc(); - - // Get the counter data and JSON encode it - let metric_family = counter.collect(); - let mut data_writer = Vec::::new(); - let res = JsonEncoder.encode(&metric_family, &mut data_writer); - assert!(res.is_ok()); - - // Decode the JSON and check the result - let decoded_value: Value = serde_json::from_slice(&data_writer).unwrap(); - let expected_json: &str = r#" - { - "testing_count.get.302": 2.0, - "testing_count.get.404": 1.0, - "testing_count.put": 1.0 - }"#; - let expected_value: Value = serde_json::from_str(expected_json).unwrap(); - assert_eq!(decoded_value, expected_value); - } -} diff --git a/crates/aptos-inspection-service/src/server/metrics.rs b/crates/aptos-inspection-service/src/server/metrics.rs deleted file mode 100644 index a762688cd9525..0000000000000 --- a/crates/aptos-inspection-service/src/server/metrics.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::{ - json_encoder::JsonEncoder, - utils, - utils::{CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT}, -}; -use hyper::{Body, StatusCode}; -use prometheus::TextEncoder; - -/// Handles a new forge metrics request -pub fn handle_forge_metrics() -> (StatusCode, Body, String) { - // Get and encode the metrics - let metrics = utils::get_all_metrics(); - let encoded_metrics = match serde_json::to_string(&metrics) { - Ok(encoded_metrics) => encoded_metrics, - Err(error) => format!("Failed to get forge metrics! Error: {}", error), - }; - - ( - StatusCode::OK, - Body::from(encoded_metrics), - CONTENT_TYPE_JSON.into(), - ) -} - -/// Handles a new metrics request (with JSON encoding) -pub fn handle_json_metrics_request() -> (StatusCode, Body, String) { - let buffer = utils::get_encoded_metrics(JsonEncoder); - (StatusCode::OK, Body::from(buffer), CONTENT_TYPE_JSON.into()) -} - -/// Handles a new metrics request (with text encoding) -pub fn handle_metrics_request() -> (StatusCode, Body, String) { - let buffer = utils::get_encoded_metrics(TextEncoder::new()); - (StatusCode::OK, Body::from(buffer), CONTENT_TYPE_TEXT.into()) -} diff --git a/crates/aptos-inspection-service/src/server/mod.rs b/crates/aptos-inspection-service/src/server/mod.rs deleted file mode 100644 index c352d4de373a8..0000000000000 --- a/crates/aptos-inspection-service/src/server/mod.rs +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::CONTENT_TYPE_TEXT; -use aptos_config::config::NodeConfig; -use aptos_data_client::client::AptosDataClient; -use aptos_logger::debug; -use aptos_network::application::storage::PeersAndMetadata; -use hyper::{ - service::{make_service_fn, service_fn}, - Body, Method, Request, Response, Server, StatusCode, -}; -use std::{ - convert::Infallible, - net::{SocketAddr, ToSocketAddrs}, - sync::Arc, - thread, -}; - -mod configuration; -mod index; -mod json_encoder; -mod metrics; -mod peer_information; -mod system_information; -pub mod utils; - -#[cfg(test)] -mod tests; - -// The list of endpoints offered by the inspection service -pub const CONFIGURATION_PATH: &str = "/configuration"; -pub const FORGE_METRICS_PATH: &str = "/forge_metrics"; -pub const INDEX_PATH: &str = "/"; -pub const JSON_METRICS_PATH: &str = "/json_metrics"; -pub const METRICS_PATH: &str = "/metrics"; -pub const PEER_INFORMATION_PATH: &str = "/peer_information"; -pub const SYSTEM_INFORMATION_PATH: &str = "/system_information"; - -// Useful string constants -pub const HEADER_CONTENT_TYPE: &str = "Content-Type"; -pub const INVALID_ENDPOINT_MESSAGE: &str = "The requested endpoint is invalid!"; -pub const UNEXPECTED_ERROR_MESSAGE: &str = "An unexpected error was encountered!"; - -/// Starts the inspection service that listens on the configured -/// address and handles various endpoint requests. -pub fn start_inspection_service( - node_config: NodeConfig, - aptos_data_client: AptosDataClient, - peers_and_metadata: Arc, -) { - // Fetch the service port and address - let service_port = node_config.inspection_service.port; - let service_address = node_config.inspection_service.address.clone(); - - // Create the inspection service socket address - let address: SocketAddr = (service_address.as_str(), service_port) - .to_socket_addrs() - .unwrap_or_else(|_| { - panic!( - "Failed to parse {}:{} as address", - service_address, service_port - ) - }) - .next() - .unwrap(); - - // Create a runtime for the inspection service - let runtime = aptos_runtimes::spawn_named_runtime("inspection".into(), None); - - // Spawn the inspection service - thread::spawn(move || { - // Create the service function that handles the endpoint requests - let make_service = make_service_fn(move |_conn| { - let node_config = node_config.clone(); - let aptos_data_client = aptos_data_client.clone(); - let peers_and_metadata = peers_and_metadata.clone(); - async move { - Ok::<_, Infallible>(service_fn(move |request| { - serve_requests( - request, - node_config.clone(), - aptos_data_client.clone(), - peers_and_metadata.clone(), - ) - })) - } - }); - - // Start and block on the server - runtime - .block_on(async { - let server = Server::bind(&address).serve(make_service); - server.await - }) - .unwrap(); - }); -} - -/// A simple helper function that handles each endpoint request -async fn serve_requests( - req: Request, - node_config: NodeConfig, - aptos_data_client: AptosDataClient, - peers_and_metadata: Arc, -) -> Result, hyper::Error> { - // Process the request and get the response components - let (status_code, body, content_type) = match req.uri().path() { - CONFIGURATION_PATH => { - // /configuration - // Exposes the node configuration - configuration::handle_configuration_request(&node_config) - }, - FORGE_METRICS_PATH => { - // /forge_metrics - // Exposes forge encoded metrics - metrics::handle_forge_metrics() - }, - INDEX_PATH => { - // / - // Exposes the index and list of available endpoints - index::handle_index_request() - }, - JSON_METRICS_PATH => { - // /json_metrics - // Exposes JSON encoded metrics - metrics::handle_json_metrics_request() - }, - METRICS_PATH => { - // /metrics - // Exposes text encoded metrics - metrics::handle_metrics_request() - }, - PEER_INFORMATION_PATH => { - // /peer_information - // Exposes the peer information - peer_information::handle_peer_information_request( - &node_config, - aptos_data_client, - peers_and_metadata, - ) - }, - SYSTEM_INFORMATION_PATH => { - // /system_information - // Exposes the system and build information - system_information::handle_system_information_request(node_config) - }, - _ => { - // Handle the invalid path - ( - StatusCode::NOT_FOUND, - Body::from(INVALID_ENDPOINT_MESSAGE), - CONTENT_TYPE_TEXT.into(), - ) - }, - }; - - // Create a response builder - let response_builder = Response::builder() - .header(HEADER_CONTENT_TYPE, content_type) - .status(status_code); - - // Build the response based on the request methods - let response = match *req.method() { - Method::HEAD => response_builder.body(Body::empty()), // Return only the headers - Method::GET => response_builder.body(body), // Include the response body - _ => { - // Invalid method found - Response::builder() - .status(StatusCode::METHOD_NOT_ALLOWED) - .body(Body::empty()) - }, - }; - - // Return the processed response - Ok(response.unwrap_or_else(|error| { - // Log the internal error - debug!("Error encountered when generating response: {:?}", error); - - // Return a failure response - let mut response = Response::new(Body::from(UNEXPECTED_ERROR_MESSAGE)); - *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - response - })) -} diff --git a/crates/aptos-inspection-service/src/server/peer_information.rs b/crates/aptos-inspection-service/src/server/peer_information.rs deleted file mode 100644 index f4a5921beba5d..0000000000000 --- a/crates/aptos-inspection-service/src/server/peer_information.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::CONTENT_TYPE_TEXT; -use aptos_config::{ - config::NodeConfig, - network_id::{NetworkId, PeerNetworkId}, -}; -use aptos_data_client::{ - client::AptosDataClient, interface::AptosDataClientInterface, peer_states, -}; -use aptos_network::application::storage::PeersAndMetadata; -use hyper::{Body, StatusCode}; -use std::{collections::BTreeMap, ops::Deref, sync::Arc}; - -// The message to display when the peer information endpoint is disabled -pub const PEER_INFO_DISABLED_MESSAGE: &str = - "This endpoint is disabled! Enable it in the node config at inspection_service.expose_peer_information: true"; - -/// Handles a new peer information request -pub fn handle_peer_information_request( - node_config: &NodeConfig, - aptos_data_client: AptosDataClient, - peers_and_metadata: Arc, -) -> (StatusCode, Body, String) { - // Only return peer information if the endpoint is enabled - let (status_code, body) = if node_config.inspection_service.expose_peer_information { - let peer_information = get_peer_information(aptos_data_client, peers_and_metadata); - (StatusCode::OK, Body::from(peer_information)) - } else { - ( - StatusCode::FORBIDDEN, - Body::from(PEER_INFO_DISABLED_MESSAGE), - ) - }; - - (status_code, body, CONTENT_TYPE_TEXT.into()) -} - -/// Returns a simple text formatted string with peer and network information -fn get_peer_information( - aptos_data_client: AptosDataClient, - peers_and_metadata: Arc, -) -> String { - // Get all registered networks - let registered_networks: Vec = - peers_and_metadata.get_registered_networks().collect(); - - // Get all peers (sorted by peer ID) - let mut all_peers = peers_and_metadata.get_all_peers().unwrap_or_default(); - all_peers.sort(); - - // Display a summary of all peers and networks - let mut peer_information_output = Vec::::new(); - display_peer_information_summary( - &mut peer_information_output, - &all_peers, - ®istered_networks, - ); - peer_information_output.push("\n".into()); - - // Display connection metadata for each peer - display_peer_connection_metadata( - &mut peer_information_output, - &all_peers, - peers_and_metadata.deref(), - ); - peer_information_output.push("\n".into()); - - // Display the entire set of trusted peers - display_trusted_peers( - &mut peer_information_output, - registered_networks, - peers_and_metadata.deref(), - ); - peer_information_output.push("\n".into()); - - // Display basic peer metadata for each peer - display_peer_monitoring_metadata( - &mut peer_information_output, - &all_peers, - peers_and_metadata.deref(), - ); - peer_information_output.push("\n".into()); - - // Display state sync metadata for each peer - display_state_sync_metadata(&mut peer_information_output, &all_peers, aptos_data_client); - peer_information_output.push("\n".into()); - - // Display detailed peer metadata for each peer - display_detailed_monitoring_metadata( - &mut peer_information_output, - &all_peers, - peers_and_metadata.deref(), - ); - peer_information_output.push("\n".into()); - - // Display the internal client state for each peer - display_internal_client_state( - &mut peer_information_output, - &all_peers, - peers_and_metadata.deref(), - ); - - peer_information_output.join("\n") // Separate each entry with a newline to construct the output -} - -/// Displays detailed peer monitoring metadata for each peer -fn display_detailed_monitoring_metadata( - peer_information_output: &mut Vec, - all_peers: &Vec, - peers_and_metadata: &PeersAndMetadata, -) { - peer_information_output.push("Detailed monitoring metadata for each peer:".into()); - - // Fetch and display the detailed metadata for each peer - for peer in all_peers { - if let Ok(peer_metadata) = peers_and_metadata.get_metadata_for_peer(*peer) { - let peer_monitoring_metadata = peer_metadata.get_peer_monitoring_metadata(); - peer_information_output.push(format!( - "\t- Peer: {}, detailed metadata: {:?}", // Debug formatting for detailed metadata - peer, peer_monitoring_metadata - )); - } - } -} - -/// Displays the internal client state for each peer -fn display_internal_client_state( - peer_information_output: &mut Vec, - all_peers: &Vec, - peers_and_metadata: &PeersAndMetadata, -) { - peer_information_output.push("Internal client state for each peer:".into()); - - // Fetch and display the internal client state for each peer - for peer in all_peers { - if let Ok(peer_metadata) = peers_and_metadata.get_metadata_for_peer(*peer) { - let peer_monitoring_metadata = peer_metadata.get_peer_monitoring_metadata(); - peer_information_output.push(format!( - "\t- Peer: {}, internal client state: {:?}", - peer, peer_monitoring_metadata.internal_client_state - )); - } - } -} - -/// Displays connection metadata for each peer -fn display_peer_connection_metadata( - peer_information_output: &mut Vec, - all_peers: &Vec, - peers_and_metadata: &PeersAndMetadata, -) { - peer_information_output.push("Connection metadata for each peer:".into()); - - // Fetch and display the connection metadata for each peer - for peer in all_peers { - if let Ok(peer_metadata) = peers_and_metadata.get_metadata_for_peer(*peer) { - let connection_metadata = peer_metadata.get_connection_metadata(); - peer_information_output.push(format!( - "\t- Peer: {}, connection state: {:?}, connection metadata: {}", - peer, - peer_metadata.get_connection_state(), - serde_json::to_string(&connection_metadata).unwrap_or_default() - )); - } - } -} - -/// Displays a summary of all peers and registered networks -fn display_peer_information_summary( - peer_information_output: &mut Vec, - all_peers: &Vec, - registered_networks: &Vec, -) { - peer_information_output.push("Peer information summary:".into()); - peer_information_output.push(format!("\t- Number of peers: {}", all_peers.len())); - peer_information_output.push(format!( - "\t- Registered networks: {:?}", - registered_networks - )); - peer_information_output.push(format!("\t- Peers and network IDs: {:?}", all_peers)); -} - -/// Displays peer monitoring metadata for each peer -fn display_peer_monitoring_metadata( - peer_information_output: &mut Vec, - all_peers: &Vec, - peers_and_metadata: &PeersAndMetadata, -) { - peer_information_output.push("Basic monitoring metadata for each peer:".into()); - - // Fetch and display the basic metadata for each peer - for peer in all_peers { - if let Ok(peer_metadata) = peers_and_metadata.get_metadata_for_peer(*peer) { - let peer_monitoring_metadata = peer_metadata.get_peer_monitoring_metadata(); - peer_information_output.push(format!( - "\t- Peer: {}, basic metadata: {}", // Display formatting for basic metadata - peer, peer_monitoring_metadata - )); - } - } -} - -/// Displays state sync metadata for each peer -fn display_state_sync_metadata( - peer_information_output: &mut Vec, - all_peers: &Vec, - aptos_data_client: AptosDataClient, -) { - peer_information_output.push("State sync metadata for each peer:".into()); - - // Fetch and display the priority and regular peers - if let Ok((priority_peers, regular_peers)) = aptos_data_client.get_priority_and_regular_peers() - { - // Sort the peer lists before displaying them - let mut priority_peers: Vec<_> = priority_peers.into_iter().collect(); - priority_peers.sort(); - let mut regular_peers: Vec<_> = regular_peers.into_iter().collect(); - regular_peers.sort(); - - // Display the priority and regular peers - peer_information_output.push(format!( - "\t- Priority peers: {:?}, regular peers: {:?}", - priority_peers, regular_peers - )); - } - - // Fetch and display the global advertised data summary - let global_data_summary = aptos_data_client.get_global_data_summary(); - peer_information_output.push(format!( - "\t- Global advertised data summary: {:?}", - global_data_summary - )); - - // Fetch and display the state sync metadata for each peer - let peer_to_state = aptos_data_client.get_peer_states().get_peer_to_states(); - for peer in all_peers { - if let Some(peer_state_entry) = peer_to_state.get(peer) { - // Get the peer states - let peer = *peer_state_entry.key(); - let peer_bucket_id = peer_states::get_bucket_id_for_peer(peer); - let peer_score = peer_state_entry.get_score(); - let peer_storage_summary = peer_state_entry.get_storage_summary(); - - // Display the peer states - peer_information_output.push(format!( - "\t- Peer: {}, score: {}, bucket ID: {}", - peer, peer_score, peer_bucket_id - )); - peer_information_output.push(format!( - "\t\t- Advertised storage summary: {:?}", - peer_storage_summary - )); - - // Get the peer's request/response counts - let sent_requests_by_type = peer_state_entry.get_sent_requests_by_type(); - let received_responses_by_type = peer_state_entry.get_received_responses_by_type(); - - // Display the peer's request/response counts - peer_information_output.push(format!( - "\t\t- Sent requests by type: {:?}", - sent_requests_by_type - )); - peer_information_output.push(format!( - "\t\t- Received responses by type: {:?}", - received_responses_by_type - )); - } - } -} - -/// Displays the entire set of trusted peers -fn display_trusted_peers( - peer_information_output: &mut Vec, - registered_networks: Vec, - peers_and_metadata: &PeersAndMetadata, -) { - peer_information_output.push("Trusted peers (validator set & seeds):".into()); - - // Fetch and display the trusted peers for each network - for network in registered_networks { - peer_information_output.push(format!("\t- Network: {}", network)); - if let Ok(trusted_peers) = peers_and_metadata.get_trusted_peers(&network) { - // Sort the peers before displaying them - let mut sorted_trusted_peers = BTreeMap::new(); - for (peer_id, peer_info) in trusted_peers { - sorted_trusted_peers.insert(peer_id, peer_info); - } - - // Display the trusted peers - for (peer_id, peer_info) in sorted_trusted_peers { - peer_information_output.push(format!( - "\t\t- Peer: {:?}, peer information: {:?}", - peer_id, peer_info - )); - } - } - } -} diff --git a/crates/aptos-inspection-service/src/server/system_information.rs b/crates/aptos-inspection-service/src/server/system_information.rs deleted file mode 100644 index ac603084b0abd..0000000000000 --- a/crates/aptos-inspection-service/src/server/system_information.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::{CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT}; -use aptos_build_info::build_information; -use aptos_config::config::NodeConfig; -use hyper::{Body, StatusCode}; - -// The message to display when the system information endpoint is disabled -pub const SYS_INFO_DISABLED_MESSAGE: &str = - "This endpoint is disabled! Enable it in the node config at inspection_service.expose_system_information: true"; - -/// Handles a new system information request -pub fn handle_system_information_request(node_config: NodeConfig) -> (StatusCode, Body, String) { - // Only return system information if the endpoint is enabled - if node_config.inspection_service.expose_system_information { - ( - StatusCode::OK, - Body::from(get_system_information_json()), - CONTENT_TYPE_JSON.into(), - ) - } else { - ( - StatusCode::FORBIDDEN, - Body::from(SYS_INFO_DISABLED_MESSAGE), - CONTENT_TYPE_TEXT.into(), - ) - } -} - -/// Returns a simple JSON formatted string with system information -fn get_system_information_json() -> String { - // Get the system and build information - let mut system_information = aptos_telemetry::system_information::get_system_information(); - system_information.extend(build_information!()); - - // Return the system information as a JSON string - match serde_json::to_string(&system_information) { - Ok(system_information) => system_information, - Err(error) => format!("Failed to get system information! Error: {}", error), - } -} diff --git a/crates/aptos-inspection-service/src/server/tests.rs b/crates/aptos-inspection-service/src/server/tests.rs deleted file mode 100644 index 219201ec5f56a..0000000000000 --- a/crates/aptos-inspection-service/src/server/tests.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - server::{ - configuration::CONFIGURATION_DISABLED_MESSAGE, - peer_information::PEER_INFO_DISABLED_MESSAGE, serve_requests, - system_information::SYS_INFO_DISABLED_MESSAGE, utils::get_all_metrics, - }, - CONFIGURATION_PATH, FORGE_METRICS_PATH, INDEX_PATH, JSON_METRICS_PATH, METRICS_PATH, - PEER_INFORMATION_PATH, SYSTEM_INFORMATION_PATH, -}; -use aptos_config::config::{AptosDataClientConfig, BaseConfig, NodeConfig}; -use aptos_data_client::client::AptosDataClient; -use aptos_network::application::{interface::NetworkClient, storage::PeersAndMetadata}; -use aptos_storage_interface::DbReader; -use aptos_storage_service_client::StorageServiceClient; -use aptos_time_service::TimeService; -use assert_approx_eq::assert_approx_eq; -use futures::executor::block_on; -use hyper::{body, Body, Method, Request, Response, StatusCode}; -use once_cell::sync::Lazy; -use prometheus::{proto::MetricFamily, register_int_counter, Counter, IntCounter, Opts, Registry}; -use rusty_fork::rusty_fork_test; -use std::{collections::HashMap, io::read_to_string, string::String, sync::Arc}; - -// This metrics counter only exists in this test context; the rest of the -// system's metrics counters don't exist, so we need to add this for tests. -const INT_COUNTER_NAME: &str = "INT_COUNTER"; -static INT_COUNTER: Lazy = - Lazy::new(|| register_int_counter!(INT_COUNTER_NAME, "An integer counter").unwrap()); - -#[tokio::test] -async fn test_inspect_configuration() { - // Create a validator config - let mut node_config = NodeConfig::get_default_validator_config(); - - // Disable the configuration endpoint and ping it - node_config.inspection_service.expose_configuration = false; - let mut response = send_get_request_to_path(&node_config, CONFIGURATION_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - - // Verify that the response contains an error - assert_eq!(response.status(), StatusCode::FORBIDDEN); - assert_eq!(response_body, CONFIGURATION_DISABLED_MESSAGE); - - // Enable the configuration endpoint and ping it - node_config.inspection_service.expose_configuration = true; - let mut response = send_get_request_to_path(&node_config, CONFIGURATION_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains("NodeConfig")); - assert!(response_body_string.contains("InspectionServiceConfig")); - assert!(response_body_string.contains("expose_configuration: true")); -} - -#[tokio::test] -async fn test_inspect_forge_metrics() { - // Create a VFN config - let config = NodeConfig::get_default_vfn_config(); - - // Increment a counter and get the forge metrics - INT_COUNTER.inc(); - let mut response = send_get_request_to_path(&config, FORGE_METRICS_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains(INT_COUNTER_NAME)); -} - -#[tokio::test] -async fn test_inspect_index() { - // Create a PFN config - let config = NodeConfig::get_default_pfn_config(); - - // Ping the index - let mut response = send_get_request_to_path(&config, INDEX_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string: String = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains all the endpoints - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains(CONFIGURATION_PATH)); - assert!(response_body_string.contains(FORGE_METRICS_PATH)); - assert!(response_body_string.contains(JSON_METRICS_PATH)); - assert!(response_body_string.contains(METRICS_PATH)); - assert!(response_body_string.contains(PEER_INFORMATION_PATH)); - assert!(response_body_string.contains(SYSTEM_INFORMATION_PATH)); -} - -#[tokio::test] -async fn test_inspect_json_metrics() { - // Create a validator config - let config = NodeConfig::get_default_validator_config(); - - // Increment a counter and get the JSON metrics - INT_COUNTER.inc(); - let mut response = send_get_request_to_path(&config, JSON_METRICS_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains(INT_COUNTER_NAME)); -} - -#[tokio::test] -async fn test_inspect_metrics() { - // Create a validator config - let config = NodeConfig::get_default_validator_config(); - - // Increment a counter and get the metrics - INT_COUNTER.inc(); - let mut response = send_get_request_to_path(&config, METRICS_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains(INT_COUNTER_NAME)); -} - -#[tokio::test] -async fn test_inspect_system_information() { - // Create a validator node config - let mut config = NodeConfig::get_default_validator_config(); - - // Disable the system information endpoint and ping it - config.inspection_service.expose_system_information = false; - let mut response = send_get_request_to_path(&config, SYSTEM_INFORMATION_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - - // Verify that the response contains an error - assert_eq!(response.status(), StatusCode::FORBIDDEN); - assert_eq!(response_body, SYS_INFO_DISABLED_MESSAGE); - - // Enable the system information endpoint and ping it - config.inspection_service.expose_system_information = true; - let mut response = send_get_request_to_path(&config, SYSTEM_INFORMATION_PATH).await; - let response_body = body::to_bytes(response.body_mut()).await.unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains("build_commit_hash")); - assert!(response_body_string.contains("cpu_count")); - assert!(response_body_string.contains("memory_available")); -} - -#[tokio::test] -async fn test_inspect_peer_information() { - // Create a validator node config - let mut config = NodeConfig::get_default_validator_config(); - - // Disable the peer information endpoint and ping it - config.inspection_service.expose_peer_information = false; - let mut response = send_get_request_to_path(&config, PEER_INFORMATION_PATH).await; - let response_body = block_on(body::to_bytes(response.body_mut())).unwrap(); - - // Verify that the response contains an error - assert_eq!(response.status(), StatusCode::FORBIDDEN); - assert_eq!(response_body, PEER_INFO_DISABLED_MESSAGE); - - // Enable the peer information endpoint and ping it - config.inspection_service.expose_peer_information = true; - let mut response = send_get_request_to_path(&config, PEER_INFORMATION_PATH).await; - let response_body = block_on(body::to_bytes(response.body_mut())).unwrap(); - let response_body_string = read_to_string(response_body.as_ref()).unwrap(); - - // Verify that the response contains the expected information - assert_eq!(response.status(), StatusCode::OK); - assert!(response_body_string.contains("Number of peers")); - assert!(response_body_string.contains("Registered networks")); - assert!(response_body_string.contains("Peers and network IDs")); - assert!(response_body_string.contains("State sync metadata")); -} - -rusty_fork_test! { -#[test] -fn test_gather_metrics() { - // Increment the counter - let iterations = 12; - for _ in 0..iterations { - INT_COUNTER.inc(); - } - - // Fetch the metrics and verify that a new entry was added - let all_metrics = get_all_metrics(); - assert_eq!(all_metrics.len(), 1); - - // Verify that the counter has the expected value - for (metric, value) in get_all_metrics() { - if metric.starts_with(INT_COUNTER_NAME) { - assert_eq!(value, iterations.to_string()); - return; - } - } - panic!("Metric {} not found", INT_COUNTER_NAME); -} -} - -rusty_fork_test! { -#[test] -fn test_get_all_metrics() { - // Increment the counter - INT_COUNTER.inc(); - - // Verify that the metrics map only has one entry - let metrics = get_all_metrics(); - assert_eq!(metrics.len(), 1); - - // Verify that the counter has the expected value - let counter_value = metrics.values().next().unwrap().parse::().unwrap(); - assert_eq!(counter_value, 1); -} -} - -#[test] -fn test_publish_metrics() { - // Create a counter metric - let counter_opts = Opts::new("test_counter", "test counter help"); - let counter = Counter::with_opts(counter_opts).unwrap(); - - // Register the counter metric - let register = Registry::new(); - register.register(Box::new(counter.clone())).unwrap(); - - // Increment the counter and verify that the metric families are updated - counter.inc(); - let metric_families = register.gather(); - assert_eq!(metric_families.len(), 1); - - // Verify that the metric family has the expected values - let metric_family: &MetricFamily = metric_families.first().unwrap(); - assert_eq!("test counter help", metric_family.get_help()); - assert_eq!("test_counter", metric_family.get_name()); - - // Verify that the metric has the expected value - let metrics = metric_family.get_metric(); - assert_eq!(metrics.len(), 1); - assert_approx_eq!(1.0, metrics.first().unwrap().get_counter().get_value()); -} - -// Exercise the serve_requests() handler with a GET request to the given path -async fn send_get_request_to_path(config: &NodeConfig, endpoint: &str) -> Response { - // Build the URI - let uri = format!("http://127.0.0.1:9201{}", endpoint); - - // Create the peers and metadata - let peers_and_metadata = PeersAndMetadata::new(&[]); - - // Create the data client - let network_client = - NetworkClient::new(vec![], vec![], HashMap::new(), peers_and_metadata.clone()); - let (aptos_data_client, _) = AptosDataClient::new( - AptosDataClientConfig::default(), - BaseConfig::default(), - TimeService::mock(), - Arc::new(MockDatabaseReader {}), - StorageServiceClient::new(network_client), - None, - ); - - // Serve the request - serve_requests( - Request::builder() - .uri(uri) - .method(Method::GET) - .body(Body::from("")) - .unwrap(), - config.clone(), - aptos_data_client, - peers_and_metadata, - ) - .await - .unwrap() -} - -/// A simple mock database reader -pub struct MockDatabaseReader {} -impl DbReader for MockDatabaseReader {} diff --git a/crates/aptos-inspection-service/src/server/utils.rs b/crates/aptos-inspection-service/src/server/utils.rs deleted file mode 100644 index c7f7d293faf0a..0000000000000 --- a/crates/aptos-inspection-service/src/server/utils.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_logger::{error, warn}; -use aptos_metrics_core::{register_int_counter_vec, IntCounterVec}; -use once_cell::sync::Lazy; -use prometheus::{ - proto::{MetricFamily, MetricType}, - Encoder, -}; -use std::collections::HashMap; - -// Useful string constants -pub const CONTENT_TYPE_JSON: &str = "application/json"; -pub const CONTENT_TYPE_TEXT: &str = "text/plain"; - -/// Counter for the number of metrics in various states -pub static NUM_METRICS: Lazy = Lazy::new(|| { - register_int_counter_vec!("aptos_metrics", "Number of metrics in certain states", &[ - "type" - ]) - .unwrap() -}); - -/// A simple utility function that returns all metrics as a HashMap -pub fn get_all_metrics() -> HashMap { - let metric_families = get_metric_families(); - get_metrics_map(metric_families) -} - -/// A simple utility function that encodes the metrics using the given encoder -pub fn get_encoded_metrics(encoder: impl Encoder) -> Vec { - // Gather and encode the metrics - let metric_families = get_metric_families(); - let mut encoded_buffer = vec![]; - if let Err(error) = encoder.encode(&metric_families, &mut encoded_buffer) { - error!("Failed to encode metrics! Error: {}", error); - return vec![]; - } - - // Update the total metric bytes counter - NUM_METRICS - .with_label_values(&["total_bytes"]) - .inc_by(encoded_buffer.len() as u64); - - encoded_buffer -} - -/// A simple utility function that returns all metric families -fn get_metric_families() -> Vec { - let metric_families = aptos_metrics_core::gather(); - let mut total: u64 = 0; - let mut families_over_1000: u64 = 0; - - // Take metrics of metric gathering so we know possible overhead of this process - for metric_family in &metric_families { - let family_count = metric_family.get_metric().len(); - if family_count > 1000 { - families_over_1000 = families_over_1000.saturating_add(1); - let name = metric_family.get_name(); - warn!( - count = family_count, - metric_family = name, - "Metric Family '{}' over 1000 dimensions '{}'", - name, - family_count - ); - } - total = total.saturating_add(family_count as u64); - } - - // These metrics will be reported on the next pull, rather than create a new family - NUM_METRICS.with_label_values(&["total"]).inc_by(total); - NUM_METRICS - .with_label_values(&["families_over_1000"]) - .inc_by(families_over_1000); - - metric_families -} - -/// A simple utility function that parses and collects all metrics -/// associated with the given families. -fn get_metrics_map(metric_families: Vec) -> HashMap { - // TODO: use an existing metric encoder (same as used by prometheus/metric-server) - let mut all_metrics = HashMap::new(); - - // Process each metric family - for metric_family in metric_families { - let values: Vec<_> = match metric_family.get_field_type() { - MetricType::COUNTER => metric_family - .get_metric() - .iter() - .map(|m| m.get_counter().get_value().to_string()) - .collect(), - MetricType::GAUGE => metric_family - .get_metric() - .iter() - .map(|m| m.get_gauge().get_value().to_string()) - .collect(), - MetricType::SUMMARY => { - error!("Unsupported Metric 'SUMMARY'"); - vec![] - }, - MetricType::UNTYPED => { - error!("Unsupported Metric 'UNTYPED'"); - vec![] - }, - MetricType::HISTOGRAM => metric_family - .get_metric() - .iter() - .map(|m| m.get_histogram().get_sample_count().to_string()) - .collect(), - }; - let metric_names = metric_family.get_metric().iter().map(|m| { - let label_strings: Vec = m - .get_label() - .iter() - .map(|l| format!("{}={}", l.get_name(), l.get_value())) - .collect(); - let labels_string = format!("{{{}}}", label_strings.join(",")); - format!("{}{}", metric_family.get_name(), labels_string) - }); - - for (name, value) in metric_names.zip(values.into_iter()) { - all_metrics.insert(name, value); - } - } - - all_metrics -} diff --git a/crates/aptos-rosetta-cli/Cargo.toml b/crates/aptos-rosetta-cli/Cargo.toml deleted file mode 100644 index ff37ec922ae04..0000000000000 --- a/crates/aptos-rosetta-cli/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "aptos-rosetta-cli" -description = "Aptos Rosetta CLI for testing" -version = "0.0.1" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true } -aptos-logger = { workspace = true } -aptos-rosetta = { workspace = true } -aptos-types = { workspace = true } -clap = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } - diff --git a/crates/aptos-rosetta-cli/README.md b/crates/aptos-rosetta-cli/README.md deleted file mode 100644 index 35242587b1b87..0000000000000 --- a/crates/aptos-rosetta-cli/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Rosetta CLI - -The Rosetta CLI is used for testing and is not used for any production use case. The CLI uses the same client as the end-to-end tests to ensure consistency but with the caveat that it will panic for cases that aren't expected. - -The intent of this CLI is to let developers that know about the Aptos ecosystem simply call Rosetta APIs while actively developing on [Rosetta](https://en.wikipedia.org/wiki/Rosetta_(software). - -You can get more help about the commands with `aptos-rosetta-cli --help` diff --git a/crates/aptos-rosetta-cli/src/account.rs b/crates/aptos-rosetta-cli/src/account.rs deleted file mode 100644 index 0f734884b8445..0000000000000 --- a/crates/aptos-rosetta-cli/src/account.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{format_output, BlockArgs, NetworkArgs, UrlArgs}; -use aptos_rosetta::{ - common::native_coin, - types::{AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier}, -}; -use aptos_types::account_address::AccountAddress; -use clap::{Parser, Subcommand}; - -/// Account APIs -/// -/// Used for pulling state of an account at a point in time -/// -/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html) -#[derive(Debug, Subcommand)] -pub enum AccountCommand { - Balance(AccountBalanceCommand), -} - -impl AccountCommand { - pub async fn execute(self) -> anyhow::Result { - match self { - AccountCommand::Balance(inner) => format_output(inner.execute().await), - } - } -} - -/// Retrieve the balance for an account -/// -/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html#accountbalance) -#[derive(Debug, Parser)] -pub struct AccountBalanceCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - block_args: BlockArgs, - /// Whether to filter the currency to the native coin - #[clap(long)] - filter_currency: bool, - /// Account to list the balance - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - account: AccountAddress, - /// Whether to show the amount of stake instead of the normal balance - #[clap(long)] - stake_amount: bool, -} - -impl AccountBalanceCommand { - #[allow(clippy::manual_retain)] - pub async fn execute(self) -> anyhow::Result { - let account_identifier = if self.stake_amount { - AccountIdentifier::total_stake_account(self.account) - } else { - AccountIdentifier::base_account(self.account) - }; - - let client = self.url_args.client(); - client - .account_balance(&AccountBalanceRequest { - network_identifier: self.network_args.network_identifier(), - account_identifier, - block_identifier: self.block_args.into(), - currencies: if self.filter_currency { - Some(vec![native_coin()]) - } else { - None - }, - }) - .await - } -} diff --git a/crates/aptos-rosetta-cli/src/block.rs b/crates/aptos-rosetta-cli/src/block.rs deleted file mode 100644 index d22f35be8125f..0000000000000 --- a/crates/aptos-rosetta-cli/src/block.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{format_output, BlockArgs, NetworkArgs, UrlArgs}; -use aptos_rosetta::types::{BlockRequest, BlockRequestMetadata, BlockResponse}; -use clap::{Parser, Subcommand}; - -/// Block APIs -/// -/// Used for pulling blocks from the blockchain -/// -/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html) -#[derive(Debug, Subcommand)] -pub enum BlockCommand { - Get(GetBlockCommand), -} - -impl BlockCommand { - pub async fn execute(self) -> anyhow::Result { - match self { - BlockCommand::Get(inner) => format_output(inner.execute().await), - } - } -} - -/// Get a block by transaction hash or version -/// -/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block) -#[derive(Debug, Parser)] -pub struct GetBlockCommand { - #[clap(flatten)] - block_args: BlockArgs, - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, -} - -impl GetBlockCommand { - pub async fn execute(self) -> anyhow::Result { - let metadata = self - .block_args - .keep_all_transactions - .map(|inner| BlockRequestMetadata { - keep_empty_transactions: Some(inner), - }); - let request = BlockRequest { - network_identifier: self.network_args.network_identifier(), - block_identifier: self.block_args.into(), - metadata, - }; - self.url_args.client().block(&request).await - } -} diff --git a/crates/aptos-rosetta-cli/src/common.rs b/crates/aptos-rosetta-cli/src/common.rs deleted file mode 100644 index dd55263365f1d..0000000000000 --- a/crates/aptos-rosetta-cli/src/common.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{account, block, construction, network}; -use aptos_rosetta::{ - client::RosettaClient, - types::{NetworkIdentifier, NetworkRequest, PartialBlockIdentifier}, -}; -use aptos_types::chain_id::ChainId; -use clap::Parser; -use serde::Serialize; - -/// Aptos Rosetta CLI -/// -/// Provides an implementation of [Rosetta](https://www.rosetta-api.org/docs/Reference.html) on Aptos. -#[derive(Debug, Parser)] -#[clap(name = "aptos-rosetta-cli", author, version, propagate_version = true)] -pub enum RosettaCliArgs { - #[clap(subcommand)] - Account(account::AccountCommand), - #[clap(subcommand)] - Block(block::BlockCommand), - #[clap(subcommand)] - Construction(construction::ConstructionCommand), - #[clap(subcommand)] - Network(network::NetworkCommand), -} - -impl RosettaCliArgs { - pub async fn execute(self) -> anyhow::Result { - use RosettaCliArgs::*; - match self { - Account(inner) => inner.execute().await, - Block(inner) => inner.execute().await, - Construction(inner) => inner.execute().await, - Network(inner) => inner.execute().await, - } - } -} - -/// Format output to a human readable form -pub fn format_output(input: anyhow::Result) -> anyhow::Result { - input.map(|value| serde_json::to_string_pretty(&value).unwrap()) -} - -#[derive(Debug, Parser)] -pub struct UrlArgs { - /// URL for the Aptos Rosetta API. e.g. http://localhost:8082 - #[clap(long, default_value = "http://localhost:8082")] - rosetta_api_url: url::Url, -} - -impl UrlArgs { - /// Retrieve a [`RosettaClient`] - pub fn client(self) -> RosettaClient { - RosettaClient::new(self.rosetta_api_url) - } -} - -#[derive(Debug, Parser)] -pub struct NetworkArgs { - /// ChainId to be used for the server e.g. TESTNET - #[clap(long, default_value_t = ChainId::test())] - pub chain_id: ChainId, -} - -impl NetworkArgs { - pub fn network_identifier(self) -> NetworkIdentifier { - self.chain_id.into() - } - - pub fn network_request(self) -> NetworkRequest { - NetworkRequest { - network_identifier: self.network_identifier(), - } - } -} - -/// Wrapper so that it's easy to tell that the output is an error -#[derive(Serialize)] -pub struct ErrorWrapper { - pub error: String, -} - -/// Arguments for requesting a block -#[derive(Debug, Parser)] -pub struct BlockArgs { - /// The height of the block to request - #[clap(long)] - block_index: Option, - /// The hash of the block to request - #[clap(long)] - block_hash: Option, - - #[clap(long)] - pub keep_all_transactions: Option, -} - -impl From for Option { - fn from(args: BlockArgs) -> Self { - if args.block_index.is_none() && args.block_hash.is_none() { - None - } else { - Some(PartialBlockIdentifier { - index: args.block_index, - hash: args.block_hash, - }) - } - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - RosettaCliArgs::command().debug_assert() -} diff --git a/crates/aptos-rosetta-cli/src/construction.rs b/crates/aptos-rosetta-cli/src/construction.rs deleted file mode 100644 index c0b6485fc157a..0000000000000 --- a/crates/aptos-rosetta-cli/src/construction.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{format_output, NetworkArgs, UrlArgs}; -use aptos::common::types::{EncodingOptions, PrivateKeyInputOptions, ProfileOptions}; -use aptos_logger::info; -use aptos_rosetta::types::TransactionIdentifier; -use aptos_types::account_address::AccountAddress; -use clap::{Parser, Subcommand}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -/// Construction commands -/// -/// At a high level, this provides the full E2E commands provided by the construction API for -/// Rosetta. This can be used for testing to ensure everything works properly -#[derive(Debug, Subcommand)] -pub enum ConstructionCommand { - CreateAccount(CreateAccountCommand), - SetOperator(SetOperatorCommand), - SetVoter(SetVoterCommand), - Transfer(TransferCommand), - CreateStakePool(CreateStakePoolCommand), -} - -impl ConstructionCommand { - pub async fn execute(self) -> anyhow::Result { - use ConstructionCommand::*; - match self { - CreateAccount(inner) => format_output(inner.execute().await), - SetOperator(inner) => format_output(inner.execute().await), - SetVoter(inner) => format_output(inner.execute().await), - Transfer(inner) => format_output(inner.execute().await), - CreateStakePool(inner) => format_output(inner.execute().await), - } - } -} - -#[derive(Debug, Parser)] -pub struct TransactionArgs { - /// Number of seconds from now to expire - /// - /// If not provided, it will default to 60 seconds - #[clap(long, default_value_t = 60)] - expiry_offset_secs: i64, - /// Sequence number for transaction - /// - /// If not provided, the Rosetta server will pull from onchain - #[clap(long)] - sequence_number: Option, - /// Maximum gas amount for a transaction - /// - /// If not provided, the Rosetta server will estimate it - #[clap(long)] - max_gas: Option, - /// Gas price per unit of gas - /// - /// If not provided, the Rosetta server will estimate it - #[clap(long)] - gas_price: Option, -} - -impl TransactionArgs { - /// Calculate expiry time given the offset seconds - pub fn expiry_time(&self) -> anyhow::Result { - let offset = self.expiry_offset_secs; - if offset > 0 { - Ok( - (SystemTime::now().duration_since(UNIX_EPOCH)? - + Duration::from_secs(offset as u64)) - .as_secs(), - ) - } else { - Ok((SystemTime::now().duration_since(UNIX_EPOCH)? - - Duration::from_secs((-offset) as u64)) - .as_secs()) - } - } -} - -/// Creates an account using Rosetta, no funds will be transferred -/// -/// EncodingOptions are here so we can allow using the BCS encoded mint key -#[derive(Debug, Parser)] -pub struct CreateAccountCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - encoding_options: EncodingOptions, - #[clap(flatten)] - profile_options: ProfileOptions, - #[clap(flatten)] - private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - txn_args: TransactionArgs, - /// The sending account, since the private key doesn't always match the - /// AccountAddress if it rotates - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - sender: Option, - /// The new account (TODO: Maybe we want to take in the public key instead) - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - new_account: AccountAddress, -} - -impl CreateAccountCommand { - pub async fn execute(self) -> anyhow::Result { - info!("Create account: {:?}", self); - let client = self.url_args.client(); - let network_identifier = self.network_args.network_identifier(); - let private_key = self - .private_key_options - .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; - - client - .create_account( - &network_identifier, - &private_key, - self.new_account, - self.txn_args.expiry_time()?, - self.txn_args.sequence_number, - self.txn_args.max_gas, - self.txn_args.gas_price, - ) - .await - } -} - -/// Transfer coins via Rosetta -/// -/// Only the native coin is allowed for now. It will create the account if it -/// does not exist -#[derive(Debug, Parser)] -pub struct TransferCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - encoding_options: EncodingOptions, - #[clap(flatten)] - profile_options: ProfileOptions, - #[clap(flatten)] - private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - txn_args: TransactionArgs, - /// The sending account, since the private key doesn't always match the - /// AccountAddress if it rotates - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - sender: Option, - /// The receiving account - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - receiver: AccountAddress, - /// The amount of coins to send - #[clap(long)] - amount: u64, -} - -impl TransferCommand { - pub async fn execute(self) -> anyhow::Result { - info!("Transfer {:?}", self); - let client = self.url_args.client(); - let network_identifier = self.network_args.network_identifier(); - let private_key = self - .private_key_options - .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; - - client - .transfer( - &network_identifier, - &private_key, - self.receiver, - self.amount, - self.txn_args.expiry_time()?, - self.txn_args.sequence_number, - self.txn_args.max_gas, - self.txn_args.gas_price, - ) - .await - } -} - -/// Set operator -/// -/// -#[derive(Debug, Parser)] -pub struct SetOperatorCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - encoding_options: EncodingOptions, - #[clap(flatten)] - profile_options: ProfileOptions, - #[clap(flatten)] - private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - txn_args: TransactionArgs, - /// The sending account, since the private key doesn't always match the - /// AccountAddress if it rotates - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - sender: Option, - /// The old operator of the stake pool - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - old_operator: Option, - /// The new operator of the stake pool - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - new_operator: AccountAddress, -} - -impl SetOperatorCommand { - pub async fn execute(self) -> anyhow::Result { - info!("Set operator {:?}", self); - let client = self.url_args.client(); - let network_identifier = self.network_args.network_identifier(); - let private_key = self - .private_key_options - .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; - - client - .set_operator( - &network_identifier, - &private_key, - self.old_operator, - self.new_operator, - self.txn_args.expiry_time()?, - self.txn_args.sequence_number, - self.txn_args.max_gas, - self.txn_args.gas_price, - ) - .await - } -} - -/// Set voter -/// -/// -#[derive(Debug, Parser)] -pub struct SetVoterCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - encoding_options: EncodingOptions, - #[clap(flatten)] - profile_options: ProfileOptions, - #[clap(flatten)] - private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - txn_args: TransactionArgs, - /// The sending account, since the private key doesn't always match the - /// AccountAddress if it rotates - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - sender: Option, - /// The operator of the stake pool - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - operator: Option, - /// The new voter for the stake pool - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - new_voter: AccountAddress, -} - -impl SetVoterCommand { - pub async fn execute(self) -> anyhow::Result { - info!("Set voter {:?}", self); - let client = self.url_args.client(); - let network_identifier = self.network_args.network_identifier(); - let private_key = self - .private_key_options - .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; - - client - .set_voter( - &network_identifier, - &private_key, - self.operator, - self.new_voter, - self.txn_args.expiry_time()?, - self.txn_args.sequence_number, - self.txn_args.max_gas, - self.txn_args.gas_price, - ) - .await - } -} - -/// Initialize stake amount -/// -/// -#[derive(Debug, Parser)] -pub struct CreateStakePoolCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, - #[clap(flatten)] - encoding_options: EncodingOptions, - #[clap(flatten)] - profile_options: ProfileOptions, - #[clap(flatten)] - private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - txn_args: TransactionArgs, - /// The sending account, since the private key doesn't always match the - /// AccountAddress if it rotates - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - sender: Option, - /// Operator - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - operator: Option, - /// Voter - #[clap(long, value_parser = aptos::common::types::load_account_arg)] - voter: Option, - /// Amount - #[clap(long)] - amount: Option, - /// Commission percentage - #[clap(long)] - commission_percentage: Option, -} - -impl CreateStakePoolCommand { - pub async fn execute(self) -> anyhow::Result { - info!("CreateStakePool {:?}", self); - let client = self.url_args.client(); - let network_identifier = self.network_args.network_identifier(); - let private_key = self - .private_key_options - .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; - - client - .create_stake_pool( - &network_identifier, - &private_key, - self.operator, - self.voter, - self.amount, - self.commission_percentage, - self.txn_args.expiry_time()?, - self.txn_args.sequence_number, - self.txn_args.max_gas, - self.txn_args.gas_price, - ) - .await - } -} diff --git a/crates/aptos-rosetta-cli/src/main.rs b/crates/aptos-rosetta-cli/src/main.rs deleted file mode 100644 index b62d63dbbf297..0000000000000 --- a/crates/aptos-rosetta-cli/src/main.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Aptos Rosetta CLI -//! -//! Why have an Aptos version of the Rosetta CLI? -//! -//! The Rosetta CLI doesn't build on my Mac easily and I just wanted something simple to test out -//! the POST requests -//! -//! Why have a separate CLI? -//! -//! We want users to use the Aptos CLI over the Rosetta CLI because of the added complexity of a -//! proxy server. So, we split it out so general users aren't confused. -//! -//! TODO: Make Aptos CLI framework common among multiple CLIs - -#![forbid(unsafe_code)] - -mod account; -mod block; -mod common; -mod construction; -mod network; - -use crate::common::{ErrorWrapper, RosettaCliArgs}; -use aptos_logger::Level; -use clap::Parser; -use std::process::exit; - -#[tokio::main] -async fn main() { - let mut logger = aptos_logger::Logger::new(); - logger.channel_size(1000).is_async(false).level(Level::Warn); - logger.build(); - - let args: RosettaCliArgs = RosettaCliArgs::parse(); - - let result = args.execute().await; - - match result { - Ok(value) => println!("{}", value), - Err(error) => { - let error = ErrorWrapper { - error: error.to_string(), - }; - println!("{}", serde_json::to_string_pretty(&error).unwrap()); - exit(-1) - }, - } -} diff --git a/crates/aptos-rosetta-cli/src/network.rs b/crates/aptos-rosetta-cli/src/network.rs deleted file mode 100644 index 4d6710876115a..0000000000000 --- a/crates/aptos-rosetta-cli/src/network.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{format_output, NetworkArgs, UrlArgs}; -use aptos_rosetta::types::{NetworkListResponse, NetworkOptionsResponse, NetworkStatusResponse}; -use clap::{Parser, Subcommand}; - -/// Network APIs -/// -/// Used to get status of the current network and what is supported on the API -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html) -#[derive(Debug, Subcommand)] -pub enum NetworkCommand { - List(NetworkListCommand), - Options(NetworkOptionsCommand), - Status(NetworkStatusCommand), -} - -impl NetworkCommand { - pub async fn execute(self) -> anyhow::Result { - match self { - NetworkCommand::List(inner) => format_output(inner.execute().await), - NetworkCommand::Options(inner) => format_output(inner.execute().await), - NetworkCommand::Status(inner) => format_output(inner.execute().await), - } - } -} - -/// Get list of available networks -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networklist) -#[derive(Debug, Parser)] -pub struct NetworkListCommand { - #[clap(flatten)] - url_args: UrlArgs, -} - -impl NetworkListCommand { - pub async fn execute(self) -> anyhow::Result { - self.url_args.client().network_list().await - } -} - -/// Get network options -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) -#[derive(Debug, Parser)] -pub struct NetworkOptionsCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, -} - -impl NetworkOptionsCommand { - pub async fn execute(self) -> anyhow::Result { - let request = self.network_args.network_request(); - self.url_args.client().network_options(&request).await - } -} - -/// Get network status -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkstatus) -#[derive(Debug, Parser)] -pub struct NetworkStatusCommand { - #[clap(flatten)] - network_args: NetworkArgs, - #[clap(flatten)] - url_args: UrlArgs, -} - -impl NetworkStatusCommand { - pub async fn execute(self) -> anyhow::Result { - let request = self.network_args.network_request(); - self.url_args.client().network_status(&request).await - } -} diff --git a/crates/aptos-rosetta/Cargo.toml b/crates/aptos-rosetta/Cargo.toml deleted file mode 100644 index 1432866c5f281..0000000000000 --- a/crates/aptos-rosetta/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "aptos-rosetta" -description = "Aptos Rosetta" -version = "0.0.1" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-config = { workspace = true } -aptos-crypto = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-logger = { workspace = true } -aptos-node = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-sdk = { workspace = true } -aptos-types = { workspace = true } -aptos-warp-webserver = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -move-core-types = { workspace = true } -once_cell = { workspace = true } -percent-encoding = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } -warp = { workspace = true } diff --git a/crates/aptos-rosetta/README.md b/crates/aptos-rosetta/README.md deleted file mode 100644 index 2ad917da87827..0000000000000 --- a/crates/aptos-rosetta/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# Aptos Rosetta Implementation - -This implementation is built for running a local proxy against -a local fullnode. However, for testing purposes, this can be used -against an external REST endpoint. - -## Architecture - -[Rosetta](https://en.wikipedia.org/wiki/Rosetta_(software)) works as a sidecar to an Aptos fullnode. Rosetta then proxies the Rosetta standard -API calls to underlying Aptos REST API calls and builds the appropriate data. - - -## Running Rosetta - -The `aptos-rosetta` binary can run in three modes: -1. `online` -> This runs a local fullnode and blocks the Aptos REST API from outside access, using it only as a local proxy for Rosetta APIs. -2. `offline` -> This runs a Rosetta server that is not connected to the blockchain. Only commands listed as `offline` work with this mode. -3. `online-remote` -> This runs a Rosetta instance that connects to a remote fullnode e.g. a public fullnode. Please keep in mind that since this proxies APIs, it can fail due to throttling and network errors between the servers. - - -## Features supported - -### Balances -* Only the native `APT` is supported. -* Staking balances are also supported, with the sub-account with the name of `stake`, and only with `0x1::staking_contract` stake pools. -* Balances are loaded from the live API `get_account_resources`; and if the `block` has been pruned, it will error out. -* All balances are provided the balance at the end of a `block`. - - -### Blocks - -Blocks support reading the following operations: - - * `create_account` -> When an account is created. - * `withdraw` -> When a balance is withdrawn from an account. - * `deposit` -> When a balance is deposited to an account. - * `fee` -> The gas fee associated with running a transaction. - * `set_operator` -> Switching a `0x1::staking_contract` operator to a new operator. - * `set_voter` -> Switching a `0x1::staking_contract` voter to a new voter. - -Here are some exceptions: - - * Not all operators can be parsed from `failed transactions`. - * Set operator will have the stake balance in its metadata. - -All transactions are parsed from the events provided by the AptosFramework. There are a few exceptions to this that use the transaction payload, but only for errors. - -Block hash is `:` and not actually a hash. - -### Constructing transactions - -More specifics can be found here: https://www.rosetta-api.org/docs/flow.html#construction-api - -All inputs to the API must be done in the `ConstructionPreprocessRequest`. This allows you to set -the sequence number, expiry time, gas parameters, and the public keys to sign the transaction. - -Note: Currently only single signer is supported at this time. - -The general flow is that you provide these inputs and follow the flow of APIs. The Metadata call -will do a simulation of the transaction and tell you the estimated gas fee for that transaction. It -will also fail the transaction before paying gas if the transaction cannot work. Once all the payloads -are built and combined, you must sign the transaction with the Ed25519 key that matches the PublicKey -provided in the `ConstructinoPreProcessRequest`. - -#### Create Account -* Accounts can be created with just the `create_account` operation alone. - -#### Transfers -* Transfers occur as a combination of a `withdraw` and a `deposit`. This has the side effect of creating the receiver if it doesn't exist. -* Transfers support only APT at this moment. - -#### Set Operator -* A staking contract stake pool can change its operator. -* If no operator is provided, it will attempt to find the first operator in the stake pool. - -#### Set Voter -* A staking contract stake pool can chage its voter. -* If no operator is provided, it will attempt to find the first operator in the stake pool. - -## Data types -All data types must hide `null` values from the output JSON. Additionally, u64s must be -encoded as strings in any metadata fields. - -## Errors - -All errors are 500s and have error codes that are static and must not change. To add more errors, -add new codes and associated data. The error details must not show in the network options call and -are all provided as Option for that reason. - -## Time - -All timestamps are valid except for the first two timestamps. These are generally 0, so they are set to -January 1st 2000 if they're older than that. - -## Mempool APIs - -Mempool APIs are currently not supported. - -## CLI testing - -The [Rosetta CLI](https://www.rosetta-api.org/docs/rosetta_cli.html) can be run with the [rosetta_cli.json](./rosetta_cli.json) -file to run the automated checks. Additionally, the [aptos.ros](./aptos.ros) -file uses the Rosetta CLI DSL to describe the possible operations that -can be run. - -Additionally, we have our `aptos-rosetta-cli` crate for local testing. diff --git a/crates/aptos-rosetta/aptos.ros b/crates/aptos-rosetta/aptos.ros deleted file mode 100644 index e1d45ad24f443..0000000000000 --- a/crates/aptos-rosetta/aptos.ros +++ /dev/null @@ -1,146 +0,0 @@ -// A configuration script for the Rosetta CLI provided by the Rosetta Spec -// Create account workflow (only 1 at a time) -create_account(1){ - create_account{ - // Testing network - create_account.network = {"network": "TESTING", "blockchain": "aptos"}; - - // Generate key and derive the address, saving it locally - key = generate_key({"curve_type": "edwards25519"}); - account = derive({ - "network_identifier": {{create_account.network}}, - "public_key": {{key.public_key}} - }); - save_account({ - "account_identifier": {{account.account_identifier}}, - "keypair": {{key}} - }); - - // Find the "faucet" loaded account - currency = { - "symbol": "APT", - "decimals": 8, - "metadata": { - "move_type": "0x1::aptos_coin::AptosCoin" - } - }; - print_message({"Find faucet with at least balance to create accounts": "10000000"}); - loaded_account = find_balance({ - "minimum_balance": { - "value": "10000000", - "currency": {{currency}} - } - }); - - // Make a create account call - print_message({"Create account":{{account.account_identifier.address}}, "With account":{{loaded_account.account_identifier.address}}}); - create_account.operations = [ - { - "operation_identifier": {"index": 0}, - "type": "create_account", - "account": {{account.account_identifier}}, - "metadata":{ - "sender": {{loaded_account.account_identifier}} - } - } - ]; - create_account.confirmation_depth = 2; - } -} - -// Request funds from the "faucet" (only one at a time) -request_funds(1){ - find_account{ - currency = { - "symbol": "APT", - "decimals": 8, - "metadata": { - "move_type": "0x1::aptos_coin::AptosCoin" - } - }; - print_message({"Find faucet with at least balance": "0"}); - random_account = find_balance({ - "minimum_balance": { - "value": "0", - "currency": {{currency}} - }, - "create_limit":1 - }); - }, - request{ - print_message({"Requesting funds from":{{random_account.account_identifier.address}}}); - loaded_account = find_balance({ - "account_identifier": {{random_account.account_identifier}}, - "minimum_balance": { - "value": "10000000", - "currency": {{currency}} - } - }); - } -} - -// Transfer money between accounts -transfer(50){ - transfer{ - transfer.network = {"network":"TESTING", "blockchain":"aptos"}; - currency = { - "symbol": "APT", - "decimals": 8, - "metadata": { - "move_type": "0x1::aptos_coin::AptosCoin" - } - }; - - // Find someone who can send money - print_message({"Find someone to send money with balance minimum": "10000000"}); - sender = find_balance({ - "minimum_balance":{ - "value": "10000000", - "currency": {{currency}} - } - }); - - // Set the receiver_amount as some value <= sender.balance - max_fee - max_fee = "2200"; - available_amount = {{sender.balance.value}} - {{max_fee}}; - receiver_amount = random_number({"minimum": "1", "maximum": {{available_amount}}}); - sender_amount = "0" - {{receiver_amount}}; - - - // Find receiver and construct operations - print_message({"Find someone to receive money with balance minimum": "0"}); - receiver = find_balance({ - "not_account_identifier": [{{sender.account_identifier}}], - "minimum_balance": { - "value": "0", - "currency": {{currency}} - }, - "create_limit": 100, - "create_probability": 50 - }); - - print_message({"Transfer funds": {"sender":{{sender.account_identifier.address}}, "receiver":{{receiver.account_identifier.address}}, "amount":{{receiver_amount}}}}); - - transfer.operations = [ - { - "operation_identifier": {"index": 0}, - "type":"withdraw", - "account": {{sender.account_identifier}}, - "amount": { - "value": {{sender_amount}}, - "currency": {{currency}} - } - }, - { - "operation_identifier": {"index": 1}, - "type": "deposit", - "account": {{receiver.account_identifier}}, - "amount": { - "value": {{receiver_amount}}, - "currency": {{currency}} - } - } - ]; - transfer.confirmation_depth = 2; - } -} diff --git a/crates/aptos-rosetta/rosetta_cli.json b/crates/aptos-rosetta/rosetta_cli.json deleted file mode 100644 index 862fe002321cf..0000000000000 --- a/crates/aptos-rosetta/rosetta_cli.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "network": { - "blockchain": "aptos", - "network": "TESTING" - }, - "online_url": "http://localhost:8082", - "data_directory": "data", - "http_timeout": 30, - "max_retries": 5, - "retry_elapsed_time": 1, - "max_online_connections": 500, - "max_sync_concurrency": 10, - "tip_delay": 30, - "log_configuration": false, - "compression_disabled": false, - "memory_limit_disabled": false, - "error_stack_trace_disabled": false, - "coin_supported": false, - "construction": { - "offline_url": "http://localhost:8083", - "max_offline_connections": 500, - "stale_depth": 100, - "broadcast_limit": 5, - "ignore_broadcast_failures": false, - "clear_broadcasts": false, - "broadcast_behind_tip": false, - "block_broadcast_limit": 50, - "rebroadcast_all": false, - "constructor_dsl_file": "aptos.ros", - "status_port": 9090, - "force_retry": false, - "end_conditions": { - "create_account": 10, - "transfer": 20 - }, - "prefunded_accounts": [ - ] - }, - "data": { - "active_reconciliation_concurrency": 16, - "inactive_reconciliation_concurrency": 4, - "inactive_reconciliation_frequency": 250, - "log_blocks": false, - "log_transactions": false, - "log_balance_changes": false, - "log_reconciliations": false, - "ignore_reconciliation_error": false, - "historical_balance_disabled": false, - "exempt_accounts": "", - "bootstrap_balances": "", - "interesting_accounts": "", - "reconciliation_disabled": false, - "reconciliation_drain_disabled": false, - "inactive_discrepancy_search_disabled": false, - "balance_tracking_disabled": false, - "coin_tracking_disabled": false, - "status_port": 9090, - "results_output_file": "", - "pruning_disabled": false, - "initial_balance_fetch_disabled": false, - "end_conditions": { - "tip": true, - "reconciliation_coverage": { - "coverage": 0.95, - "tip": true - } - } - }, - "perf": null -} diff --git a/crates/aptos-rosetta/src/account.rs b/crates/aptos-rosetta/src/account.rs deleted file mode 100644 index 12d6d205994c5..0000000000000 --- a/crates/aptos-rosetta/src/account.rs +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Rosetta Account API -//! -//! See: [Account API Spec](https://www.rosetta-api.org/docs/AccountApi.html) -//! - -use crate::{ - common::{ - check_network, get_block_index_from_request, handle_request, native_coin, native_coin_tag, - with_context, - }, - error::{ApiError, ApiResult}, - types::{AccountBalanceRequest, AccountBalanceResponse, Amount, Currency, *}, - RosettaContext, -}; -use aptos_logger::{debug, trace, warn}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{AccountResource, CoinStoreResource}, -}; -use std::{collections::HashSet, str::FromStr}; -use warp::Filter; - -/// Account routes e.g. balance -pub fn routes( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::post().and( - warp::path!("account" / "balance") - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(account_balance)), - ) -} - -/// Account balance command -/// -/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html#accountbalance) -async fn account_balance( - request: AccountBalanceRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/account/balance"); - trace!( - request = ?request, - server_context = ?server_context, - "account_balance for [{}]", - request.account_identifier.address - ); - - let network_identifier = request.network_identifier; - - check_network(network_identifier, &server_context)?; - let rest_client = server_context.rest_client()?; - - // Retrieve the block index to read - let block_height = - get_block_index_from_request(&server_context, request.block_identifier.clone()).await?; - - // Version to grab is the last entry in the block (balance is at end of block) - let block_info = server_context - .block_cache()? - .get_block_info_by_height(block_height, server_context.chain_id) - .await?; - let balance_version = block_info.last_version; - - let (sequence_number, operators, balances, lockup_expiration) = get_balances( - &rest_client, - request.account_identifier, - balance_version, - request.currencies, - ) - .await?; - - Ok(AccountBalanceResponse { - block_identifier: block_info.block_id, - balances, - metadata: AccountBalanceMetadata { - sequence_number: sequence_number.into(), - operators, - lockup_expiration_time_utc: aptos_rest_client::aptos_api_types::U64(lockup_expiration), - }, - }) -} - -/// Retrieve the balances for an account -#[allow(clippy::manual_retain)] -async fn get_balances( - rest_client: &aptos_rest_client::Client, - account: AccountIdentifier, - version: u64, - maybe_filter_currencies: Option>, -) -> ApiResult<(u64, Option>, Vec, u64)> { - let owner_address = account.account_address()?; - let pool_address = account.pool_address()?; - - let mut balances = vec![]; - let mut lockup_expiration: u64 = 0; - let mut total_requested_balance: Option = None; - - if pool_address.is_some() { - match get_delegation_stake_balances( - rest_client, - &account, - owner_address, - pool_address.unwrap(), - version, - ) - .await - { - Ok(Some(balance_result)) => { - if let Some(balance) = balance_result.balance { - total_requested_balance = Some( - total_requested_balance.unwrap_or_default() - + u64::from_str(&balance.value).unwrap_or_default(), - ); - } - lockup_expiration = balance_result.lockup_expiration; - if let Some(balance) = total_requested_balance { - balances.push(Amount { - value: balance.to_string(), - currency: native_coin(), - }) - } - }, - result => { - warn!( - "Failed to retrieve requested balance for delegator_address: {}, pool_address: {}: {:?}", - owner_address, pool_address.unwrap(), result - ) - }, - } - } - - // Retrieve all account resources - if let Ok(response) = rest_client - .get_account_resources_at_version_bcs(owner_address, version) - .await - { - let resources = response.into_inner(); - let mut maybe_sequence_number = None; - let mut maybe_operators = None; - - // Iterate through resources, converting balances - for (struct_tag, bytes) in resources { - match ( - struct_tag.address, - struct_tag.module.as_str(), - struct_tag.name.as_str(), - ) { - (AccountAddress::ONE, ACCOUNT_MODULE, ACCOUNT_RESOURCE) => { - let account: AccountResource = bcs::from_bytes(&bytes)?; - maybe_sequence_number = Some(account.sequence_number()) - }, - (AccountAddress::ONE, COIN_MODULE, COIN_STORE_RESOURCE) => { - // Only show coins on the base account - if account.is_base_account() { - let coin_store: CoinStoreResource = bcs::from_bytes(&bytes)?; - if let Some(coin_type) = struct_tag.type_params.first() { - // Only display supported coins - if coin_type == &native_coin_tag() { - balances.push(Amount { - value: coin_store.coin().to_string(), - currency: native_coin(), - }); - } - } - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, STORE_RESOURCE) => { - if account.is_base_account() || pool_address.is_some() { - continue; - } - - let store: Store = bcs::from_bytes(&bytes)?; - maybe_operators = Some(vec![]); - for (operator, contract) in store.staking_contracts { - // Keep track of operators - maybe_operators.as_mut().unwrap().push(operator); - match get_stake_balances( - rest_client, - &account, - contract.pool_address, - version, - ) - .await - { - Ok(Some(balance_result)) => { - if let Some(balance) = balance_result.balance { - total_requested_balance = Some( - total_requested_balance.unwrap_or_default() - + u64::from_str(&balance.value).unwrap_or_default(), - ); - } - lockup_expiration = balance_result.lockup_expiration; - }, - result => { - warn!( - "Failed to retrieve requested balance for account: {}, address: {}: {:?}", - owner_address, contract.pool_address, result - ) - }, - } - } - if let Some(balance) = total_requested_balance { - balances.push(Amount { - value: balance.to_string(), - currency: native_coin(), - }) - } - - /* TODO: Right now operator stake is not supported - else if account.is_operator_stake() { - // For operator stake, filter on operator address - let operator_address = account.operator_address()?; - if let Some(contract) = store.staking_contracts.get(&operator_address) { - balances.push(get_total_stake( - rest_client, - &account, - contract.pool_address, - version, - ).await?); - } - }*/ - }, - _ => {}, - } - } - - let sequence_number = if let Some(sequence_number) = maybe_sequence_number { - sequence_number - } else { - return Err(ApiError::InternalError(Some( - "Failed to retrieve account sequence number".to_string(), - ))); - }; - - // Filter based on requested currencies - if let Some(currencies) = maybe_filter_currencies { - let mut currencies: HashSet = currencies.into_iter().collect(); - // Remove extra currencies not requested - balances = balances - .into_iter() - .filter(|balance| currencies.contains(&balance.currency)) - .collect(); - - for balance in balances.iter() { - currencies.remove(&balance.currency); - } - - for currency in currencies { - balances.push(Amount { - value: 0.to_string(), - currency, - }); - } - } - - // Retrieve balances - Ok(( - sequence_number, - maybe_operators, - balances, - lockup_expiration, - )) - } else { - Ok(( - 0, - None, - vec![Amount { - value: 0.to_string(), - currency: native_coin(), - }], - 0, - )) - } -} diff --git a/crates/aptos-rosetta/src/block.rs b/crates/aptos-rosetta/src/block.rs deleted file mode 100644 index 6ee61a6f218a4..0000000000000 --- a/crates/aptos-rosetta/src/block.rs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - check_network, get_block_index_from_request, get_timestamp, handle_request, with_context, - BlockHash, Y2K_MS, - }, - error::ApiResult, - types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction}, - RosettaContext, -}; -use aptos_logger::{debug, trace}; -use aptos_types::chain_id::ChainId; -use std::sync::Arc; -use warp::Filter; - -pub fn block_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("block") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(block)) -} - -/// Retrieves a block (in this case a single transaction) given it's identifier. -/// -/// Our implementation allows for by `index`, which is the ledger `version` or by -/// transaction `hash`. -/// -/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block) -async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult { - debug!("/block"); - trace!( - request = ?request, - server_context = ?server_context, - "/block", - ); - - check_network(request.network_identifier, &server_context)?; - - // Retrieve by block or by hash, both or neither is not allowed - let block_index = - get_block_index_from_request(&server_context, request.block_identifier).await?; - - let (parent_transaction, block) = get_block_by_index( - server_context.block_cache()?.as_ref(), - block_index, - server_context.chain_id, - ) - .await?; - - let keep_empty_transactions = request - .metadata - .as_ref() - .and_then(|inner| inner.keep_empty_transactions) - .unwrap_or_default(); - let block = build_block( - &server_context, - parent_transaction, - block, - server_context.chain_id, - keep_empty_transactions, - ) - .await?; - - Ok(BlockResponse { block }) -} - -/// Build up the transaction, which should contain the `operations` as the change set -async fn build_block( - server_context: &RosettaContext, - parent_block_identifier: BlockIdentifier, - block: aptos_rest_client::aptos_api_types::BcsBlock, - chain_id: ChainId, - keep_empty_transactions: bool, -) -> ApiResult { - // note: timestamps are in microseconds, so we convert to milliseconds - let timestamp = get_timestamp(block.block_timestamp); - let block_identifier = BlockIdentifier::from_block(&block, chain_id); - - // Convert the transactions and build the block - let mut transactions: Vec = Vec::new(); - // TODO: Parallelize these and then sort at end - if let Some(txns) = block.transactions { - for txn in txns { - let transaction = Transaction::from_transaction(server_context, txn).await?; - if keep_empty_transactions || !transaction.operations.is_empty() { - transactions.push(transaction) - } - } - } - - // Ensure the transactions are sorted in order - transactions.sort_by(|first, second| first.metadata.version.0.cmp(&second.metadata.version.0)); - - Ok(Block { - block_identifier, - parent_block_identifier, - timestamp, - transactions, - }) -} - -/// Retrieves a block by its index -async fn get_block_by_index( - block_cache: &BlockRetriever, - block_height: u64, - chain_id: ChainId, -) -> ApiResult<( - BlockIdentifier, - aptos_rest_client::aptos_api_types::BcsBlock, -)> { - let block = block_cache.get_block_by_height(block_height, true).await?; - - // For the genesis block, we populate parent_block_identifier with the - // same genesis block. Refer to - // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block - if block_height == 0 { - Ok((BlockIdentifier::from_block(&block, chain_id), block)) - } else { - // Retrieve the previous block's identifier - let prev_block = block_cache - .get_block_by_height(block_height - 1, false) - .await?; - let prev_block_id = BlockIdentifier::from_block(&prev_block, chain_id); - - // Retrieve the current block - Ok((prev_block_id, block)) - } -} - -#[derive(Clone, Debug)] -pub struct BlockInfo { - /// Block identifier (block hash & block height) - pub block_id: BlockIdentifier, - /// Milliseconds timestamp - pub timestamp: u64, - /// Last version in block for getting state - pub last_version: u64, -} - -impl BlockInfo { - pub fn from_block( - block: &aptos_rest_client::aptos_api_types::BcsBlock, - chain_id: ChainId, - ) -> BlockInfo { - BlockInfo { - block_id: BlockIdentifier::from_block(block, chain_id), - timestamp: get_timestamp(block.block_timestamp), - last_version: block.last_version, - } - } -} - -/// A cache of [`BlockInfo`] to allow us to keep track of the block boundaries -#[derive(Debug)] -pub struct BlockRetriever { - page_size: u16, - rest_client: Arc, -} - -impl BlockRetriever { - pub fn new(page_size: u16, rest_client: Arc) -> Self { - BlockRetriever { - page_size, - rest_client, - } - } - - pub async fn get_block_info_by_height( - &self, - height: u64, - chain_id: ChainId, - ) -> ApiResult { - // Genesis block is hardcoded - if height == 0 { - return Ok(BlockInfo { - block_id: BlockIdentifier { - index: 0, - hash: BlockHash::new(chain_id, 0).to_string(), - }, - timestamp: Y2K_MS, - last_version: 0, - }); - } - - let block = self.get_block_by_height(height, false).await?; - Ok(BlockInfo::from_block(&block, chain_id)) - } - - pub async fn get_block_by_height( - &self, - height: u64, - with_transactions: bool, - ) -> ApiResult { - if with_transactions { - Ok(self - .rest_client - .get_full_block_by_height_bcs(height, self.page_size) - .await? - .into_inner()) - } else { - Ok(self - .rest_client - .get_block_by_height_bcs(height, false) - .await? - .into_inner()) - } - } -} diff --git a/crates/aptos-rosetta/src/client.rs b/crates/aptos-rosetta/src/client.rs deleted file mode 100644 index ad7975f616047..0000000000000 --- a/crates/aptos-rosetta/src/client.rs +++ /dev/null @@ -1,909 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::native_coin, - types::{ - AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier, BlockRequest, - BlockResponse, ConstructionCombineRequest, ConstructionCombineResponse, - ConstructionDeriveRequest, ConstructionDeriveResponse, ConstructionHashRequest, - ConstructionMetadata, ConstructionMetadataRequest, ConstructionMetadataResponse, - ConstructionParseRequest, ConstructionParseResponse, ConstructionPayloadsRequest, - ConstructionPayloadsResponse, ConstructionPreprocessRequest, - ConstructionPreprocessResponse, ConstructionSubmitRequest, ConstructionSubmitResponse, - Error, MetadataRequest, NetworkIdentifier, NetworkListResponse, NetworkOptionsResponse, - NetworkRequest, NetworkStatusResponse, Operation, PreprocessMetadata, PublicKey, Signature, - SignatureType, TransactionIdentifier, TransactionIdentifierResponse, - }, -}; -use anyhow::anyhow; -use aptos_crypto::{ - ed25519::Ed25519PrivateKey, PrivateKey, SigningKey, ValidCryptoMaterialStringExt, -}; -use aptos_rest_client::aptos_api_types::mime_types::JSON; -use aptos_types::{account_address::AccountAddress, transaction::RawTransaction}; -use reqwest::{header::CONTENT_TYPE, Client as ReqwestClient}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{collections::HashMap, convert::TryInto, fmt::Debug, str::FromStr}; -use url::Url; - -/// Client for testing & interacting with a Rosetta service -#[derive(Debug, Clone)] -pub struct RosettaClient { - address: Url, - inner: ReqwestClient, -} - -impl RosettaClient { - pub fn new(address: Url) -> RosettaClient { - RosettaClient { - address, - inner: ReqwestClient::new(), - } - } - - pub async fn account_balance( - &self, - request: &AccountBalanceRequest, - ) -> anyhow::Result { - self.make_call("account/balance", request).await - } - - pub async fn block(&self, request: &BlockRequest) -> anyhow::Result { - self.make_call("block", request).await - } - - pub async fn combine( - &self, - request: &ConstructionCombineRequest, - ) -> anyhow::Result { - self.make_call("construction/combine", request).await - } - - pub async fn derive( - &self, - request: &ConstructionDeriveRequest, - ) -> anyhow::Result { - self.make_call("construction/derive", request).await - } - - pub async fn hash( - &self, - request: &ConstructionHashRequest, - ) -> anyhow::Result { - self.make_call("construction/hash", request).await - } - - pub async fn metadata( - &self, - request: &ConstructionMetadataRequest, - ) -> anyhow::Result { - self.make_call("construction/metadata", request).await - } - - pub async fn parse( - &self, - request: &ConstructionParseRequest, - ) -> anyhow::Result { - self.make_call("construction/parse", request).await - } - - pub async fn payloads( - &self, - request: &ConstructionPayloadsRequest, - ) -> anyhow::Result { - self.make_call("construction/payloads", request).await - } - - pub async fn preprocess( - &self, - request: &ConstructionPreprocessRequest, - ) -> anyhow::Result { - self.make_call("construction/preprocess", request).await - } - - pub async fn submit( - &self, - request: &ConstructionSubmitRequest, - ) -> anyhow::Result { - self.make_call("construction/submit", request).await - } - - pub async fn network_list(&self) -> anyhow::Result { - self.make_call("network/list", &MetadataRequest {}).await - } - - pub async fn network_options( - &self, - request: &NetworkRequest, - ) -> anyhow::Result { - self.make_call("network/options", request).await - } - - pub async fn network_status( - &self, - request: &NetworkRequest, - ) -> anyhow::Result { - self.make_call("network/status", request).await - } - - async fn make_call<'a, I: Serialize + Debug, O: DeserializeOwned>( - &'a self, - path: &'static str, - request: &'a I, - ) -> anyhow::Result { - let response = self - .inner - .post(self.address.join(path)?) - .header(CONTENT_TYPE, JSON) - .body(serde_json::to_string(request)?) - .send() - .await?; - if !response.status().is_success() { - let error: Error = response.json().await?; - return Err(anyhow!("Failed API with: {:?}", error)); - } - - Ok(response.json().await?) - } - - pub async fn create_account( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - new_account: AccountAddress, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A create account transaction is just a Create account operation - let operations = vec![Operation::create_account(0, None, new_account, sender)]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - false, - ) - .await - } - - pub async fn transfer( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - receiver: AccountAddress, - amount: u64, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A transfer operation is made up of a withdraw and a deposit - let operations = vec![ - Operation::withdraw( - 0, - None, - AccountIdentifier::base_account(sender), - native_coin(), - amount, - ), - Operation::deposit( - 1, - None, - AccountIdentifier::base_account(receiver), - native_coin(), - amount, - ), - ]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - false, - ) - .await - } - - pub async fn set_operator( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - old_operator: Option, - new_operator: AccountAddress, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A transfer operation is made up of a withdraw and a deposit - let operations = vec![Operation::set_operator( - 0, - None, - sender, - old_operator.map(AccountIdentifier::base_account), - AccountIdentifier::base_account(new_operator), - None, - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - old_operator.is_none(), - ) - .await - } - - pub async fn set_voter( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - operator: Option, - new_voter: AccountAddress, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A transfer operation is made up of a withdraw and a deposit - let operations = vec![Operation::set_voter( - 0, - None, - sender, - operator.map(AccountIdentifier::base_account), - AccountIdentifier::base_account(new_voter), - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - operator.is_none(), - ) - .await - } - - pub async fn reset_lockup( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - operator: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A transfer operation is made up of a withdraw and a deposit - let operations = vec![Operation::reset_lockup( - 0, - None, - sender, - operator.map(AccountIdentifier::base_account), - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - operator.is_none(), - ) - .await - } - - pub async fn update_commission( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - operator: Option, - new_commission_percentage: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - let operations = vec![Operation::update_commission( - 0, - None, - sender, - operator.map(AccountIdentifier::base_account), - new_commission_percentage, - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - operator.is_none(), - ) - .await - } - - pub async fn unlock_stake( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - operator: Option, - amount: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - let operations = vec![Operation::unlock_stake( - 0, - None, - sender, - operator.map(AccountIdentifier::base_account), - amount, - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - operator.is_none(), - ) - .await - } - - pub async fn distribute_staking_rewards( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - operator: AccountAddress, - staker: AccountAddress, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - let operations = vec![Operation::distribute_staking_rewards( - 0, - None, - sender, - AccountIdentifier::base_account(operator), - AccountIdentifier::base_account(staker), - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - false, - ) - .await - } - - pub async fn create_stake_pool( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - new_operator: Option, - new_voter: Option, - stake_amount: Option, - commission_percentage: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - // A transfer operation is made up of a withdraw and a deposit - - let operations = vec![Operation::create_stake_pool( - 0, - None, - sender, - new_operator, - new_voter, - stake_amount, - commission_percentage, - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - true, - ) - .await - } - - pub async fn add_delegated_stake( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let delegator = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - - let mut keys = HashMap::new(); - keys.insert(delegator, private_key); - - let operations = vec![Operation::add_delegated_stake( - 0, - None, - delegator, - AccountIdentifier::base_account(pool_address), - amount, - )]; - - self.submit_operations( - delegator, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - true, - ) - .await - } - - pub async fn unlock_delegated_stake( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let delegator = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(delegator, private_key); - - let operations = vec![Operation::unlock_delegated_stake( - 0, - None, - delegator, - AccountIdentifier::base_account(pool_address), - amount, - )]; - - self.submit_operations( - delegator, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - true, - ) - .await - } - - pub async fn withdraw_undelegated_stake( - &self, - network_identifier: &NetworkIdentifier, - private_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - ) -> anyhow::Result { - let sender = self - .get_account_address(network_identifier.clone(), private_key) - .await?; - let mut keys = HashMap::new(); - keys.insert(sender, private_key); - - let operations = vec![Operation::withdraw_undelegated_stake( - 0, - None, - sender, - AccountIdentifier::base_account(pool_address), - amount, - )]; - - self.submit_operations( - sender, - network_identifier.clone(), - &keys, - operations, - expiry_time_secs, - sequence_number, - max_gas, - gas_unit_price, - false, - ) - .await - } - - /// Retrieves the account address from the derivation path if there isn't an overriding account specified - async fn get_account_address( - &self, - network_identifier: NetworkIdentifier, - private_key: &Ed25519PrivateKey, - ) -> anyhow::Result { - Ok(self - .derive_account(network_identifier, private_key.public_key().try_into()?) - .await? - .account_address()?) - } - - /// Submits the operations to the blockchain - async fn submit_operations( - &self, - sender: AccountAddress, - network_identifier: NetworkIdentifier, - keys: &HashMap, - operations: Vec, - expiry_time_secs: u64, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, - // Parsed operations won't match given operations - parse_not_same: bool, - ) -> anyhow::Result { - // Retrieve txn metadata - let (metadata, public_keys) = self - .metadata_for_ops( - sender, - network_identifier.clone(), - operations.clone(), - max_gas, - gas_unit_price, - expiry_time_secs, - sequence_number, - keys, - ) - .await?; - - // Should have a fee in the native coin - let suggested_fee = metadata.suggested_fee.first().expect("Expected fee"); - let expected_fee = u64::from_str(&suggested_fee.value).expect("Expected u64 for fee"); - assert_eq!( - suggested_fee.currency, - native_coin(), - "Fee should always be the native coin" - ); - assert!( - metadata.metadata.max_gas_amount.0 * metadata.metadata.gas_price_per_unit.0 - >= expected_fee - ); - - // Build the transaction, sign it, and submit it - let response = self - .unsigned_transaction( - network_identifier.clone(), - operations.clone(), - metadata.metadata, - public_keys, - parse_not_same, - ) - .await?; - let signed_txn = self - .sign_transaction( - network_identifier.clone(), - keys, - response, - operations, - parse_not_same, - ) - .await?; - self.submit_transaction(network_identifier, signed_txn) - .await - } - - /// Derives an [`AccountAddress`] from the [`PublicKey`] - async fn derive_account( - &self, - network_identifier: NetworkIdentifier, - public_key: PublicKey, - ) -> anyhow::Result { - Ok(self - .derive(&ConstructionDeriveRequest { - network_identifier, - public_key, - }) - .await? - .account_identifier) - } - - /// Retrieves the metadata for the set of operations - async fn metadata_for_ops( - &self, - sender: AccountAddress, - network_identifier: NetworkIdentifier, - operations: Vec, - max_gas: Option, - gas_unit_price: Option, - expiry_time_secs: u64, - sequence_number: Option, - keys: &HashMap, - ) -> anyhow::Result<(ConstructionMetadataResponse, Vec)> { - // Request the given operation with the given gas constraints - let preprocess_response = self - .preprocess(&ConstructionPreprocessRequest { - network_identifier: network_identifier.clone(), - operations, - metadata: Some(PreprocessMetadata { - expiry_time_secs: Some(expiry_time_secs.into()), - sequence_number: sequence_number.map(|inner| inner.into()), - max_gas_amount: max_gas.map(|inner| inner.into()), - gas_price: gas_unit_price.map(|inner| inner.into()), - public_keys: Some(vec![keys - .get(&sender) - .unwrap() - .public_key() - .try_into() - .unwrap()]), - gas_price_multiplier: None, - gas_price_priority: None, - }), - }) - .await?; - - // Process the required public keys - let mut public_keys = Vec::new(); - for account in preprocess_response.required_public_keys { - if let Some(key) = keys.get(&account.account_address()?) { - public_keys.push(key.public_key().try_into()?); - } else { - return Err(anyhow!("No public key found for account")); - } - } - - // Request the metadata - self.metadata(&ConstructionMetadataRequest { - network_identifier, - options: preprocess_response.options, - }) - .await - .map(|response| (response, public_keys)) - } - - /// Build an unsigned transaction - async fn unsigned_transaction( - &self, - network_identifier: NetworkIdentifier, - operations: Vec, - metadata: ConstructionMetadata, - public_keys: Vec, - parse_not_same: bool, - ) -> anyhow::Result { - // Build the unsigned transaction - let payloads = self - .payloads(&ConstructionPayloadsRequest { - network_identifier: network_identifier.clone(), - operations: operations.clone(), - metadata: Some(metadata), - public_keys: Some(public_keys), - }) - .await?; - - // Verify that we can parse the transaction - let response = self - .parse(&ConstructionParseRequest { - network_identifier, - signed: false, - transaction: payloads.unsigned_transaction.clone(), - }) - .await?; - - if response.account_identifier_signers.is_some() { - Err(anyhow!("Signers were in the unsigned transaction!")) - } else if !parse_not_same && operations != response.operations { - Err(anyhow!( - "Operations were not parsed to be the same as input! Expected {:?} Got {:?}", - operations, - response.operations - )) - } else { - Ok(payloads) - } - } - - /// Signs a transaction and combines it with an unsigned transaction - async fn sign_transaction( - &self, - network_identifier: NetworkIdentifier, - keys: &HashMap, - unsigned_response: ConstructionPayloadsResponse, - operations: Vec, - parse_not_same: bool, - ) -> anyhow::Result { - let mut signatures = Vec::new(); - let mut signers: Vec = Vec::new(); - - // Sign the unsigned transaction - let unsigned_transaction: RawTransaction = bcs::from_bytes(&hex::decode( - unsigned_response.unsigned_transaction.clone(), - )?)?; - let signing_message = hex::encode(unsigned_transaction.signing_message().unwrap()); - - // Sign the payload if it matches the unsigned transaction - for payload in unsigned_response.payloads.into_iter() { - let account = &payload.account_identifier; - let private_key = keys - .get(&account.account_address()?) - .expect("Should have a private key"); - signers.push(account.clone()); - - assert_eq!(signing_message, payload.hex_bytes); - let txn_signature = private_key.sign(&unsigned_transaction).unwrap(); - signatures.push(Signature { - signing_payload: payload, - public_key: private_key.public_key().try_into()?, - signature_type: SignatureType::Ed25519, - hex_bytes: txn_signature.to_encoded_string()?, - }); - } - - // Build the signed transaction - let signed_response = self - .combine(&ConstructionCombineRequest { - network_identifier: network_identifier.clone(), - unsigned_transaction: unsigned_response.unsigned_transaction, - signatures, - }) - .await?; - - // Verify transaction can be parsed properly - let response = self - .parse(&ConstructionParseRequest { - network_identifier, - signed: true, - transaction: signed_response.signed_transaction.clone(), - }) - .await?; - - // Signers must match exactly - if let Some(parsed_signers) = response.account_identifier_signers { - if signers != parsed_signers { - return Err(anyhow!( - "Signers don't match Expected: {:?} Got: {:?}", - signers, - parsed_signers - )); - } - } else { - return Err(anyhow!("Signers were in the unsigned transaction!")); - } - - // Operations must match exactly - if !parse_not_same && operations != response.operations { - Err(anyhow!( - "Operations were not parsed to be the same as input! Expected {:?} Got {:?}", - operations, - response.operations - )) - } else { - Ok(signed_response.signed_transaction) - } - } - - /// Submit a transaction to the blockchain - async fn submit_transaction( - &self, - network_identifier: NetworkIdentifier, - signed_transaction: String, - ) -> anyhow::Result { - Ok(self - .submit(&ConstructionSubmitRequest { - network_identifier, - signed_transaction, - }) - .await? - .transaction_identifier) - } -} diff --git a/crates/aptos-rosetta/src/common.rs b/crates/aptos-rosetta/src/common.rs deleted file mode 100644 index 67c1422a99698..0000000000000 --- a/crates/aptos-rosetta/src/common.rs +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::{ApiError, ApiResult}, - types::{ - Currency, CurrencyMetadata, MetadataRequest, NetworkIdentifier, PartialBlockIdentifier, - APTOS_COIN_MODULE, APTOS_COIN_RESOURCE, - }, - RosettaContext, -}; -use aptos_crypto::{ValidCryptoMaterial, ValidCryptoMaterialStringExt}; -use aptos_logger::debug; -use aptos_rest_client::{Account, Response}; -use aptos_sdk::move_types::{ - ident_str, - language_storage::{StructTag, TypeTag}, -}; -use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; -use futures::future::BoxFuture; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{convert::Infallible, fmt::LowerHex, future::Future, str::FromStr}; -use warp::Filter; - -/// The year 2000 in milliseconds, as this is the lower limit for Rosetta API implementations -pub const Y2K_MS: u64 = 946713600000; -pub const BLOCKCHAIN: &str = "aptos"; - -/// Checks the request network matches the server network -pub fn check_network( - network_identifier: NetworkIdentifier, - server_context: &RosettaContext, -) -> ApiResult<()> { - if network_identifier.blockchain == BLOCKCHAIN - && ChainId::from_str(network_identifier.network.trim()) - .map_err(|_| ApiError::NetworkIdentifierMismatch)? - == server_context.chain_id - { - Ok(()) - } else { - Err(ApiError::NetworkIdentifierMismatch) - } -} - -/// Attaches RosettaContext to warp paths -pub fn with_context( - context: RosettaContext, -) -> impl Filter + Clone { - warp::any().map(move || context.clone()) -} - -pub fn with_empty_request() -> impl Filter + Clone -{ - warp::any().map(move || MetadataRequest {}) -} - -/// Handles a generic request to warp -pub fn handle_request<'a, F, R, Req, Resp>( - handler: F, -) -> impl Fn( - Req, - RosettaContext, -) -> BoxFuture<'static, Result, Infallible>> - + Clone -where - F: FnOnce(Req, RosettaContext) -> R + Clone + Copy + Send + 'static, - R: Future> + Send, - Req: Deserialize<'a> + Send + 'static, - Resp: std::fmt::Debug + Serialize, -{ - move |request, options| { - let fut = async move { - match handler(request, options).await { - Ok(response) => { - debug!("Response: {:?}", serde_json::to_string_pretty(&response)); - Ok(warp::reply::with_status( - warp::reply::json(&response), - warp::http::StatusCode::OK, - )) - }, - Err(api_error) => { - debug!("Error: {:?}", api_error); - let status = api_error.status_code(); - Ok(warp::reply::with_status( - warp::reply::json(&api_error.into_error()), - status, - )) - }, - } - }; - Box::pin(fut) - } -} - -pub async fn get_account( - rest_client: &aptos_rest_client::Client, - address: AccountAddress, -) -> ApiResult> { - rest_client - .get_account(address) - .await - .map_err(|_| ApiError::AccountNotFound(Some(address.to_string()))) -} - -/// Retrieve the timestamp according ot the Rosetta spec (milliseconds) -pub fn get_timestamp(timestamp_usecs: u64) -> u64 { - // note: timestamps are in microseconds, so we convert to milliseconds - let mut timestamp = timestamp_usecs / 1000; - - // Rosetta doesn't like timestamps before 2000 - if timestamp < Y2K_MS { - timestamp = Y2K_MS; - } - timestamp -} - -/// Strips the `0x` prefix on hex strings -pub fn strip_hex_prefix(str: &str) -> &str { - str.strip_prefix("0x").unwrap_or(str) -} - -pub fn encode_bcs(obj: &T) -> ApiResult { - let bytes = bcs::to_bytes(obj)?; - Ok(hex::encode(bytes)) -} - -pub fn decode_bcs(str: &str, type_name: &'static str) -> ApiResult { - let bytes = hex::decode(str)?; - bcs::from_bytes(&bytes).map_err(|_| ApiError::deserialization_failed(type_name)) -} - -pub fn decode_key( - str: &str, - type_name: &'static str, -) -> ApiResult { - T::from_encoded_string(str).map_err(|_| ApiError::deserialization_failed(type_name)) -} - -const DEFAULT_COIN: &str = "APT"; -const DEFAULT_DECIMALS: u8 = 8; - -pub fn native_coin() -> Currency { - Currency { - symbol: DEFAULT_COIN.to_string(), - decimals: DEFAULT_DECIMALS, - metadata: Some(CurrencyMetadata { - move_type: native_coin_tag().to_string(), - }), - } -} - -pub fn native_coin_tag() -> TypeTag { - TypeTag::Struct(Box::new(StructTag { - address: AccountAddress::ONE, - module: ident_str!(APTOS_COIN_MODULE).into(), - name: ident_str!(APTOS_COIN_RESOURCE).into(), - type_params: vec![], - })) -} - -pub fn is_native_coin(currency: &Currency) -> ApiResult<()> { - if currency == &native_coin() { - Ok(()) - } else { - Err(ApiError::UnsupportedCurrency(Some(currency.symbol.clone()))) - } -} - -/// Determines which block to pull for the request -pub async fn get_block_index_from_request( - server_context: &RosettaContext, - partial_block_identifier: Option, -) -> ApiResult { - Ok(match partial_block_identifier { - Some(PartialBlockIdentifier { - index: Some(block_index), - hash: Some(_), - }) => block_index, - // Lookup by block index - Some(PartialBlockIdentifier { - index: Some(block_index), - hash: None, - }) => block_index, - // Lookup by block hash - Some(PartialBlockIdentifier { - index: None, - hash: Some(hash), - }) => BlockHash::from_str(&hash)?.block_height(server_context.chain_id)?, - // Lookup latest version - _ => { - let response = server_context - .rest_client()? - .get_ledger_information() - .await?; - let state = response.state(); - - state.block_height - }, - }) -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct BlockHash { - chain_id: ChainId, - block_height: u64, -} - -impl BlockHash { - pub fn new(chain_id: ChainId, block_height: u64) -> Self { - BlockHash { - chain_id, - block_height, - } - } - - pub fn block_height(&self, expected_chain_id: ChainId) -> ApiResult { - if expected_chain_id != self.chain_id { - Err(ApiError::InvalidInput(Some(format!( - "Invalid chain id in block hash {} expected {}", - self.chain_id, expected_chain_id - )))) - } else { - Ok(self.block_height) - } - } -} - -impl FromStr for BlockHash { - type Err = ApiError; - - fn from_str(str: &str) -> Result { - let mut iter = str.split('-'); - - let chain_id = if let Some(maybe_chain_id) = iter.next() { - ChainId::from_str(maybe_chain_id).map_err(|_| { - ApiError::InvalidInput(Some(format!( - "Invalid block hash, chain-id is invalid {}", - str - ))) - })? - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Invalid block hash, missing chain-id or block height {}", - str - )))); - }; - - let block_height = if let Some(maybe_block_height) = iter.next() { - u64::from_str(maybe_block_height).map_err(|_| { - ApiError::InvalidInput(Some(format!( - "Invalid block hash, block height is invalid {}", - str - ))) - })? - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Invalid block hash, missing block height {}", - str - )))); - }; - - if iter.next().is_some() { - Err(ApiError::InvalidInput(Some(format!( - "Invalid block hash, too many hyphens {}", - str - )))) - } else { - Ok(BlockHash::new(chain_id, block_height)) - } - } -} - -impl std::fmt::Display for BlockHash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}-{}", self.chain_id, self.block_height) - } -} - -pub fn to_hex_lower(obj: &T) -> String { - format!("{:x}", obj) -} - -/// Retrieves the currency from the given parameters -/// TODO: What do do about the type params? -pub fn parse_currency(address: AccountAddress, module: &str, name: &str) -> ApiResult { - match (address, module, name) { - (AccountAddress::ONE, APTOS_COIN_MODULE, APTOS_COIN_RESOURCE) => Ok(native_coin()), - _ => Err(ApiError::TransactionParseError(Some(format!( - "Invalid coin for transfer {}::{}::{}", - address, module, name - )))), - } -} - -#[cfg(test)] -mod test { - use crate::common::BlockHash; - use aptos_types::chain_id::{ChainId, NamedChain}; - use std::str::FromStr; - - #[test] - pub fn chain_id_height_check() { - let block_hash = BlockHash::new(ChainId::test(), 0); - block_hash - .block_height(ChainId::test()) - .expect("Matching chain id should work"); - block_hash - .block_height(ChainId::new(NamedChain::MAINNET.id())) - .expect_err("Mismatch chain id should not work"); - } - - #[test] - pub fn chain_id_string_check() { - let block_hash = BlockHash::new(ChainId::test(), 0); - let parsed_block_hash = - BlockHash::from_str(&block_hash.to_string()).expect("Should parse string"); - assert_eq!(block_hash, parsed_block_hash); - } - - #[test] - pub fn valid_block_hashes() { - let valid_block_hashes: Vec<(&str, ChainId, u64)> = vec![ - ("testnet-0", ChainId::new(NamedChain::TESTNET.id()), 0), - ("mainnet-20", ChainId::new(NamedChain::MAINNET.id()), 20), - ("5-2", ChainId::new(5), 2), - ]; - for (str, chain_id, height) in valid_block_hashes { - let block_hash = BlockHash::from_str(str).expect("Valid block hash"); - assert_eq!(block_hash.block_height, height); - assert_eq!(block_hash.chain_id, chain_id); - } - } - - #[test] - pub fn invalid_block_hashes() { - let invalid_block_hashes: Vec<&str> = - vec!["testnet--1", "testnet", "1", "testnet-1-1", "1-mainnet"]; - for str in invalid_block_hashes { - BlockHash::from_str(str).expect_err("Invalid block hash"); - } - } -} diff --git a/crates/aptos-rosetta/src/construction.rs b/crates/aptos-rosetta/src/construction.rs deleted file mode 100644 index 2e52c5d5f991d..0000000000000 --- a/crates/aptos-rosetta/src/construction.rs +++ /dev/null @@ -1,1369 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Construction APIs -//! -//! The construction APIs break down transactions into composable parts that are -//! used to be generic across blockchains. A flow of operations can be found -//! in the [specifications](https://www.rosetta-api.org/docs/construction_api_introduction.html) -//! -//! This is broken down in the following flow: -//! -//! * Preprocess (based on operations) gets information to fetch from metadata (onchchain) -//! * Metadata fetches onchain information e.g. sequence number -//! * Payloads generates an unsigned transaction -//! * Application outside signs the payload from the transactino -//! * Combine puts the signed transaction payload with the unsigned transaction -//! * Submit submits the signed transaciton to the blockchain -//! -//! There are also 2 other sometimes used APIs -//! * Derive (get an account from the private key) -//! * Hash (get a hash of the transaction to lookup in mempool) -//! -//! Note: there is an "online" mode and an "offline" mode. The offline APIs can run without -//! a connection to a full node. The online ones need a connection to a full node. -//! - -use crate::{ - common::{ - check_network, decode_bcs, decode_key, encode_bcs, get_account, handle_request, - native_coin, parse_currency, with_context, - }, - error::{ApiError, ApiResult}, - types::{InternalOperation, *}, - RosettaContext, -}; -use aptos_crypto::{ - ed25519::{Ed25519PublicKey, Ed25519Signature}, - signing_message, ValidCryptoMaterialStringExt, -}; -use aptos_global_constants::adjust_gas_headroom; -use aptos_logger::debug; -use aptos_sdk::{ - move_types::language_storage::{StructTag, TypeTag}, - transaction_builder::TransactionFactory, -}; -use aptos_types::{ - account_address::AccountAddress, - chain_id::ChainId, - transaction::{ - authenticator::AuthenticationKey, RawTransaction, SignedTransaction, TransactionPayload, - }, -}; -use serde::de::DeserializeOwned; -use std::{ - convert::TryFrom, - time::{SystemTime, UNIX_EPOCH}, -}; -use warp::Filter; - -pub fn combine_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "combine") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_combine)) -} - -pub fn derive_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "derive") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_derive)) -} - -pub fn hash_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "hash") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_hash)) -} - -pub fn metadata_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "metadata") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_metadata)) -} - -pub fn parse_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "parse") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_parse)) -} - -pub fn payloads_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "payloads") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_payloads)) -} - -pub fn preprocess_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "preprocess") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_preprocess)) -} - -pub fn submit_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("construction" / "submit") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(construction_submit)) -} - -/// Construction combine command (OFFLINE) -/// -/// This combines signatures, and a raw txn -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructioncombine) -async fn construction_combine( - request: ConstructionCombineRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/combine {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let unsigned_txn: RawTransaction = - decode_bcs(&request.unsigned_transaction, "UnsignedTransaction")?; - - // Single signer only supported for now - // TODO: Support multi-agent / multi-signer? - if request.signatures.len() != 1 { - return Err(ApiError::UnsupportedSignatureCount(Some( - request.signatures.len(), - ))); - } - - let signature = &request.signatures[0]; - - if signature.signature_type != SignatureType::Ed25519 - || signature.public_key.curve_type != CurveType::Edwards25519 - { - return Err(ApiError::InvalidSignatureType); - } - - let public_key: Ed25519PublicKey = - decode_key(&signature.public_key.hex_bytes, "Ed25519PublicKey")?; - let signature: Ed25519Signature = decode_key(&signature.hex_bytes, "Ed25519Signature")?; - - let signed_txn = SignedTransaction::new(unsigned_txn, public_key, signature); - - Ok(ConstructionCombineResponse { - signed_transaction: encode_bcs(&signed_txn)?, - }) -} - -/// Construction derive command (OFFLINE) -/// -/// Derive account address from Public key -/// Note: This only works for new accounts. After the account is created, all APIs should provide -/// both account and key. -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionderive) -async fn construction_derive( - request: ConstructionDeriveRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/derive {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let public_key: Ed25519PublicKey = - decode_key(&request.public_key.hex_bytes, "Ed25519PublicKey")?; - let address = AuthenticationKey::ed25519(&public_key).account_address(); - - Ok(ConstructionDeriveResponse { - account_identifier: AccountIdentifier::base_account(address), - }) -} - -/// Construction hash command (OFFLINE) -/// -/// Hash a transaction to get it's identifier for lookup in mempool -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionhash) -async fn construction_hash( - request: ConstructionHashRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/hash {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let signed_transaction: SignedTransaction = - decode_bcs(&request.signed_transaction, "SignedTransaction")?; - - Ok(TransactionIdentifierResponse { - transaction_identifier: signed_transaction.committed_hash().into(), - }) -} - -/// Fills in the operator for actions that require it but don't have one -async fn fill_in_operator( - rest_client: &aptos_rest_client::Client, - mut internal_operation: InternalOperation, -) -> ApiResult { - match &mut internal_operation { - InternalOperation::SetOperator(op) => { - // If there was no old operator set, and there is only one, we should use that - if op.old_operator.is_none() { - let store = rest_client - .get_account_resource_bcs::(op.owner, "0x1::staking_contract::Store") - .await? - .into_inner(); - let staking_contracts = store.staking_contracts; - if staking_contracts.len() != 1 { - let operators: Vec<_> = staking_contracts - .iter() - .map(|(address, _)| *address) - .collect(); - return Err(ApiError::InvalidInput(Some(format!( - "Account has more than one operator, operator must be specified from: {:?}", - operators - )))); - } else { - // Take the only staking contract - op.old_operator = Some( - staking_contracts - .first() - .map(|(address, _)| *address) - .unwrap(), - ); - } - } - }, - InternalOperation::SetVoter(op) => { - // If there was no operator set, and there is only one, we should use that - if op.operator.is_none() { - let store = rest_client - .get_account_resource_bcs::(op.owner, "0x1::staking_contract::Store") - .await? - .into_inner(); - let staking_contracts = store.staking_contracts; - if staking_contracts.len() != 1 { - let operators: Vec<_> = staking_contracts - .iter() - .map(|(address, _)| address) - .collect(); - return Err(ApiError::InvalidInput(Some(format!( - "Account has more than one operator, operator must be specified from: {:?}", - operators - )))); - } else { - // Take the only staking contract - op.operator = Some( - staking_contracts - .first() - .map(|(address, _)| *address) - .unwrap(), - ); - } - } - }, - _ => {}, - } - - Ok(internal_operation) -} - -async fn simulate_transaction( - rest_client: &aptos_rest_client::Client, - chain_id: ChainId, - options: &MetadataOptions, - internal_operation: &InternalOperation, - sequence_number: u64, -) -> ApiResult<(Amount, u64, u64)> { - // If we have any missing fields, let's simulate! - let mut transaction_factory = TransactionFactory::new(chain_id); - - // If we have a gas unit price, let's not estimate - if let Some(gas_unit_price) = options.gas_price_per_unit.as_ref() { - transaction_factory = transaction_factory.with_gas_unit_price(gas_unit_price.0); - } else { - let gas_estimation = rest_client.estimate_gas_price().await?.into_inner(); - - // Get the priorities, for backwards compatibility, if the API doesn't have the prioritized ones, use the normal one - let mut gas_price = match options.gas_price_priority.unwrap_or_default() { - GasPricePriority::Low => gas_estimation - .deprioritized_gas_estimate - .unwrap_or(gas_estimation.gas_estimate), - GasPricePriority::Normal => gas_estimation.gas_estimate, - GasPricePriority::High => gas_estimation - .prioritized_gas_estimate - .unwrap_or(gas_estimation.gas_estimate), - }; - - // We can also provide the multiplier at this point, we mulitply times it, and divide by 100 - if let Some(gas_multiplier) = options.gas_price_multiplier { - let gas_multiplier = gas_multiplier as u64; - if let Some(multiplied_price) = gas_price.checked_mul(gas_multiplier) { - gas_price = multiplied_price.saturating_div(100) - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Gas price multiplier {} causes overflow on the price", - gas_multiplier - )))); - } - } - - transaction_factory = transaction_factory.with_gas_unit_price(gas_price); - } - - // Build up the transaction - let (txn_payload, sender) = internal_operation.payload()?; - let unsigned_transaction = transaction_factory - .payload(txn_payload) - .sender(sender) - .sequence_number(sequence_number) - .build(); - - // Read and fill in public key as necessary, this is required for simulation! - // TODO: Only single signer supported - let public_key = - if let Some(public_key) = options.public_keys.as_ref().and_then(|inner| inner.first()) { - Ed25519PublicKey::from_encoded_string(&public_key.hex_bytes).map_err(|err| { - ApiError::InvalidInput(Some(format!( - "Public key provided is not parsable {:?}", - err - ))) - })? - } else { - return Err(ApiError::InvalidInput(Some( - "Must provide public_keys for simulation otherwise it can't simulate!".to_string(), - ))); - }; - - // Sign the transaction with a dummy signature of all zeros as required by the API - let signed_transaction = SignedTransaction::new( - unsigned_transaction, - public_key, - Ed25519Signature::try_from([0u8; 64].as_ref()).expect("Zero signature should always work"), - ); - - // Simulate, filling in the fields that aren't being currently handled - // This API will always succeed unless 2 conditions - // 1. The API was going to fail anyways due to a bad transaction e.g. wrong signer, insufficient balance, etc. - // 2. The used gas price (provided or estimated) * the maximum possible gas is can't be paid e.g. there is no - // way for this user to ever pay for this transaction (at that gas price) - let response = rest_client - .simulate_bcs_with_gas_estimation(&signed_transaction, true, false) - .await?; - - let simulated_txn = response.inner(); - - // Check that we didn't go over the max gas provided by the API - if let Some(max_gas_amount) = options.max_gas_amount.as_ref() { - if max_gas_amount.0 < simulated_txn.info.gas_used() { - return Err(ApiError::MaxGasFeeTooLow(Some(format!( - "Max gas amount {} is less than number of actual gas units used {}", - max_gas_amount.0, - simulated_txn.info.gas_used() - )))); - } - } - - // Handle any other messages, including out of gas, which means the user has not enough - // funds to complete the transaction (e.g. the gas price is too high) - let simulation_status = simulated_txn.info.status(); - if !simulation_status.is_success() { - // TODO: Fix case for not enough gas to be a better message - return Err(ApiError::InvalidInput(Some(format!( - "Transaction failed to simulate with status: {:?}", - simulation_status - )))); - } - - if let Some(user_txn) = simulated_txn.transaction.try_as_signed_user_txn() { - // This gas price came from the simulation (would be the one from the input if provided) - let simulated_gas_unit_price = user_txn.gas_unit_price(); - - // These two will either be estimated or the original value, so we can just use them exactly - let max_gas_amount = if let Some(max_gas_amount) = options.max_gas_amount.as_ref() { - max_gas_amount.0 - } else { - // If estimating, we want to give headroom to ensure the transaction succeeds - adjust_gas_headroom(simulated_txn.info.gas_used(), user_txn.max_gas_amount()) - }; - - // Multiply the gas price times the max gas amount to use - let suggested_fee = Amount::suggested_gas_fee(simulated_gas_unit_price, max_gas_amount); - - Ok((suggested_fee, simulated_gas_unit_price, max_gas_amount)) - } else { - // This should never happen, because the underlying API can't run a non-user transaction - Err(ApiError::InternalError(Some(format!( - "Transaction returned by API was not a user transaction: {:?}", - simulated_txn.transaction - )))) - } -} - -/// Construction metadata command -/// -/// Retrieve sequence number for submitting transactions -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionmetadata) -async fn construction_metadata( - request: ConstructionMetadataRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/metadata {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let rest_client = server_context.rest_client()?; - let address = request.options.internal_operation.sender(); - let response = get_account(&rest_client, address).await?; - - // Ensure this network really is the one we expect it to be - if server_context.chain_id.id() != response.state().chain_id { - return Err(ApiError::ChainIdMismatch); - } - - let sequence_number = if let Some(sequence_number) = request.options.sequence_number { - sequence_number.0 - } else { - // Retrieve the sequence number from the rest server if one wasn't provided - response.inner().sequence_number - }; - - // We have to cheat the set operator and set voter operations right here - let internal_operation = fill_in_operator( - rest_client.as_ref(), - request.options.internal_operation.clone(), - ) - .await?; - - // If both are present, we skip simulation - let (suggested_fee, gas_unit_price, max_gas_amount) = simulate_transaction( - rest_client.as_ref(), - server_context.chain_id, - &request.options, - &internal_operation, - sequence_number, - ) - .await?; - - Ok(ConstructionMetadataResponse { - metadata: ConstructionMetadata { - sequence_number: sequence_number.into(), - max_gas_amount: max_gas_amount.into(), - gas_price_per_unit: gas_unit_price.into(), - expiry_time_secs: request.options.expiry_time_secs, - internal_operation, - }, - suggested_fee: vec![suggested_fee], - }) -} - -/// Construction parse command (OFFLINE) -/// -/// Parses operations from a transaction, used for verifying transaction construction -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionparse) -async fn construction_parse( - request: ConstructionParseRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/parse {:?}", request); - check_network(request.network_identifier, &server_context)?; - let metadata; - let (account_identifier_signers, unsigned_txn) = if request.signed { - let signed_txn: SignedTransaction = decode_bcs(&request.transaction, "SignedTransaction")?; - metadata = Some(ConstructionParseMetadata { - unsigned_transaction: None, - signed_transaction: Some(signed_txn.clone()), - }); - let mut account_identifier_signers: Vec<_> = - vec![AccountIdentifier::base_account(signed_txn.sender())]; - - for account in signed_txn.authenticator().secondary_signer_addresses() { - account_identifier_signers.push(AccountIdentifier::base_account(account)) - } - - ( - Some(account_identifier_signers), - signed_txn.into_raw_transaction(), - ) - } else { - let unsigned_txn: RawTransaction = decode_bcs(&request.transaction, "UnsignedTransaction")?; - metadata = Some(ConstructionParseMetadata { - unsigned_transaction: Some(unsigned_txn.clone()), - signed_transaction: None, - }); - (None, unsigned_txn) - }; - let sender = unsigned_txn.sender(); - - // This is messy, but all we can do - let operations = match unsigned_txn.into_payload() { - TransactionPayload::EntryFunction(inner) => { - let (module, function_name, type_args, args) = inner.into_inner(); - - match ( - *module.address(), - module.name().as_str(), - function_name.as_str(), - ) { - (AccountAddress::ONE, COIN_MODULE, TRANSFER_FUNCTION) => { - parse_transfer_operation(sender, &type_args, &args)? - }, - (AccountAddress::ONE, APTOS_ACCOUNT_MODULE, TRANSFER_FUNCTION) => { - parse_account_transfer_operation(sender, &type_args, &args)? - }, - (AccountAddress::ONE, APTOS_ACCOUNT_MODULE, CREATE_ACCOUNT_FUNCTION) => { - parse_create_account_operation(sender, &type_args, &args)? - }, - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION, - ) => parse_set_operator_operation(sender, &type_args, &args)?, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UPDATE_VOTER_FUNCTION) => { - parse_set_voter_operation(sender, &type_args, &args)? - }, - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - CREATE_STAKING_CONTRACT_FUNCTION, - ) => parse_create_stake_pool_operation(sender, &type_args, &args)?, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, RESET_LOCKUP_FUNCTION) => { - parse_reset_lockup_operation(sender, &type_args, &args)? - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UPDATE_COMMISSION_FUNCTION) => { - parse_update_commission_operation(sender, &type_args, &args)? - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UNLOCK_STAKE_FUNCTION) => { - parse_unlock_stake_operation(sender, &type_args, &args)? - }, - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - DISTRIBUTE_STAKING_REWARDS_FUNCTION, - ) => parse_distribute_staking_rewards_operation(sender, &type_args, &args)?, - ( - AccountAddress::ONE, - DELEGATION_POOL_MODULE, - DELEGATION_POOL_ADD_STAKE_FUNCTION, - ) => parse_delegation_pool_add_stake_operation(sender, &type_args, &args)?, - ( - AccountAddress::ONE, - DELEGATION_POOL_MODULE, - DELEGATION_POOL_WITHDRAW_FUNCTION, - ) => parse_delegation_pool_withdraw_operation(sender, &type_args, &args)?, - (AccountAddress::ONE, DELEGATION_POOL_MODULE, DELEGATION_POOL_UNLOCK_FUNCTION) => { - parse_delegation_pool_unlock_operation(sender, &type_args, &args)? - }, - _ => { - return Err(ApiError::TransactionParseError(Some(format!( - "Unsupported entry function type {:x}::{}::{}", - module.address(), - module.name(), - function_name - )))); - }, - } - }, - payload => { - return Err(ApiError::TransactionParseError(Some(format!( - "Unsupported transaction payload type {:?}", - payload - )))) - }, - }; - - Ok(ConstructionParseResponse { - operations, - account_identifier_signers, - metadata, - }) -} - -fn parse_create_account_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - // There are no typeargs for create account - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Create account should not have type arguments: {:?}", - type_args - )))); - } - - // Create account - if let Some(encoded_address) = args.first() { - let new_address: AccountAddress = bcs::from_bytes(encoded_address)?; - - Ok(vec![Operation::create_account( - 0, - None, - new_address, - sender, - )]) - } else { - Err(ApiError::InvalidOperations(Some( - "Create account doesn't have an address argument".to_string(), - ))) - } -} - -fn parse_transfer_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - let mut operations = Vec::new(); - - // Check coin is the native coin - - let currency = match type_args.first() { - Some(TypeTag::Struct(struct_tag)) => { - let StructTag { - address, - module, - name, - .. - } = &**struct_tag; - - parse_currency(*address, module.as_str(), name.as_str())? - }, - _ => { - return Err(ApiError::TransactionParseError(Some( - "No coin type in transfer".to_string(), - ))) - }, - }; - - // Retrieve the args for the operations - - let receiver: AccountAddress = if let Some(receiver) = args.first() { - bcs::from_bytes(receiver)? - } else { - return Err(ApiError::TransactionParseError(Some( - "No receiver in transfer".to_string(), - ))); - }; - let amount: u64 = if let Some(amount) = args.get(1) { - bcs::from_bytes(amount)? - } else { - return Err(ApiError::TransactionParseError(Some( - "No amount in transfer".to_string(), - ))); - }; - - operations.push(Operation::withdraw( - 0, - None, - AccountIdentifier::base_account(sender), - currency.clone(), - amount, - )); - operations.push(Operation::deposit( - 1, - None, - AccountIdentifier::base_account(receiver), - currency, - amount, - )); - Ok(operations) -} - -fn parse_account_transfer_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - // There are no typeargs for account transfer - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Account transfer should not have type arguments: {:?}", - type_args - )))); - } - let mut operations = Vec::new(); - - // Retrieve the args for the operations - - let receiver: AccountAddress = if let Some(receiver) = args.first() { - bcs::from_bytes(receiver)? - } else { - return Err(ApiError::TransactionParseError(Some( - "No receiver in account transfer".to_string(), - ))); - }; - let amount: u64 = if let Some(amount) = args.get(1) { - bcs::from_bytes(amount)? - } else { - return Err(ApiError::TransactionParseError(Some( - "No amount in account transfer".to_string(), - ))); - }; - - operations.push(Operation::withdraw( - 0, - None, - AccountIdentifier::base_account(sender), - native_coin(), - amount, - )); - operations.push(Operation::deposit( - 1, - None, - AccountIdentifier::base_account(receiver), - native_coin(), - amount, - )); - Ok(operations) -} - -pub fn parse_function_arg( - name: &str, - args: &[Vec], - index: usize, -) -> ApiResult { - if let Some(arg) = args.get(index) { - if let Ok(arg) = bcs::from_bytes::(arg) { - return Ok(arg); - } - } - - Err(ApiError::InvalidInput(Some(format!( - "Argument {} of {} failed to parse", - index, name - )))) -} - -pub fn parse_set_operator_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Set operator should not have type arguments: {:?}", - type_args - )))); - } - - let old_operator = parse_function_arg("set_operator", args, 0)?; - let new_operator = parse_function_arg("set_operator", args, 1)?; - Ok(vec![Operation::set_operator( - 0, - None, - sender, - Some(AccountIdentifier::base_account(old_operator)), - AccountIdentifier::base_account(new_operator), - None, - )]) -} - -pub fn parse_set_voter_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Set voter should not have type arguments: {:?}", - type_args - )))); - } - - let operator = parse_function_arg("set_voter", args, 0)?; - let new_voter = parse_function_arg("set_voter", args, 1)?; - Ok(vec![Operation::set_voter( - 0, - None, - sender, - Some(AccountIdentifier::base_account(operator)), - AccountIdentifier::base_account(new_voter), - )]) -} - -pub fn parse_create_stake_pool_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Create stake pool should not have type arguments: {:?}", - type_args - )))); - } - - let operator = parse_function_arg("create_stake_pool", args, 0)?; - let voter = parse_function_arg("create_stake_pool", args, 1)?; - let amount: u64 = parse_function_arg("create_stake_pool", args, 2)?; - let commission_percentage: u64 = parse_function_arg("create_stake_pool", args, 3)?; - Ok(vec![Operation::create_stake_pool( - 0, - None, - sender, - Some(operator), - Some(voter), - Some(amount), - Some(commission_percentage), - )]) -} - -pub fn parse_reset_lockup_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Reset lockup should not have type arguments: {:?}", - type_args - )))); - } - - let operator: AccountAddress = parse_function_arg("reset_lockup", args, 0)?; - Ok(vec![Operation::reset_lockup( - 0, - None, - sender, - Some(AccountIdentifier::base_account(operator)), - )]) -} - -pub fn parse_unlock_stake_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Unlock stake should not have type arguments: {:?}", - type_args - )))); - } - - let operator: AccountAddress = parse_function_arg("unlock_stake", args, 0)?; - let amount: u64 = parse_function_arg("unlock_stake", args, 1)?; - - Ok(vec![Operation::unlock_stake( - 0, - None, - sender, - Some(AccountIdentifier::base_account(operator)), - Some(amount), - )]) -} - -pub fn parse_update_commission_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Unlock stake should not have type arguments: {:?}", - type_args - )))); - } - - let operator: AccountAddress = parse_function_arg("update_commision", args, 0)?; - let new_commission_percentage: u64 = parse_function_arg("update_commision", args, 1)?; - - Ok(vec![Operation::update_commission( - 0, - None, - sender, - Some(AccountIdentifier::base_account(operator)), - Some(new_commission_percentage), - )]) -} - -pub fn parse_distribute_staking_rewards_operation( - sender: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Distribute should not have type arguments: {:?}", - type_args - )))); - } - - let staker: AccountAddress = parse_function_arg("distribute_staking_rewards", args, 0)?; - let operator: AccountAddress = parse_function_arg("distribute_staking_rewards", args, 1)?; - - Ok(vec![Operation::distribute_staking_rewards( - 0, - None, - sender, - AccountIdentifier::base_account(operator), - AccountIdentifier::base_account(staker), - )]) -} - -pub fn parse_delegation_pool_add_stake_operation( - delegator: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "add_delegated_stake should not have type arguments: {:?}", - type_args - )))); - } - - let pool_address: AccountAddress = parse_function_arg("add_delegated_stake", args, 0)?; - let amount: u64 = parse_function_arg("add_delegated_stake", args, 1)?; - - Ok(vec![Operation::add_delegated_stake( - 0, - None, - delegator, - AccountIdentifier::base_account(pool_address), - Some(amount), - )]) -} - -pub fn parse_delegation_pool_unlock_operation( - delegator: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "Unlock delegated stake should not have type arguments: {:?}", - type_args - )))); - } - - let pool_address: AccountAddress = parse_function_arg("unlock_delegated_stake", args, 0)?; - let amount: u64 = parse_function_arg("unlock_delegated_stake", args, 1)?; - - Ok(vec![Operation::unlock_delegated_stake( - 0, - None, - delegator, - AccountIdentifier::base_account(pool_address), - Some(amount), - )]) -} - -pub fn parse_delegation_pool_withdraw_operation( - delegator: AccountAddress, - type_args: &[TypeTag], - args: &[Vec], -) -> ApiResult> { - if !type_args.is_empty() { - return Err(ApiError::TransactionParseError(Some(format!( - "add_delegated_stake should not have type arguments: {:?}", - type_args - )))); - } - - let pool_address: AccountAddress = parse_function_arg("withdraw_undelegated", args, 0)?; - let amount: u64 = parse_function_arg("withdraw_undelegated", args, 1)?; - - Ok(vec![Operation::withdraw_undelegated_stake( - 0, - None, - delegator, - AccountIdentifier::base_account(pool_address), - Some(amount), - )]) -} - -/// Construction payloads command (OFFLINE) -/// -/// Constructs payloads for given known operations -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionpayloads) -async fn construction_payloads( - request: ConstructionPayloadsRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/payloads {:?}", request); - check_network(request.network_identifier, &server_context)?; - - // Retrieve the real operation we're doing - let mut operation = InternalOperation::extract(&request.operations)?; - let metadata = if let Some(ref metadata) = request.metadata { - metadata - } else { - return Err(ApiError::MissingPayloadMetadata); - }; - - // This is a hack to ensure that the payloads actually have overridden operators if not provided - match &mut operation { - InternalOperation::CreateAccount(_) => { - if operation != metadata.internal_operation { - return Err(ApiError::InvalidInput(Some(format!( - "CreateAccount operation doesn't match metadata {:?} vs {:?}", - operation, metadata.internal_operation - )))); - } - }, - InternalOperation::Transfer(_) => { - if operation != metadata.internal_operation { - return Err(ApiError::InvalidInput(Some(format!( - "Transfer operation doesn't match metadata {:?} vs {:?}", - operation, metadata.internal_operation - )))); - } - }, - InternalOperation::SetOperator(inner) => { - if let InternalOperation::SetOperator(ref metadata_op) = metadata.internal_operation { - if inner.owner == metadata_op.owner - && inner.new_operator == metadata_op.new_operator - { - if inner.old_operator.is_none() { - inner.old_operator = metadata_op.old_operator; - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Set operator operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Set operator operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::SetVoter(inner) => { - if let InternalOperation::SetVoter(ref metadata_op) = metadata.internal_operation { - if inner.owner == metadata_op.owner && inner.new_voter == metadata_op.new_voter { - if inner.operator.is_none() { - inner.operator = metadata_op.operator; - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Set voter operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Set voter operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::InitializeStakePool(_) => { - if operation != metadata.internal_operation { - return Err(ApiError::InvalidInput(Some(format!( - "Initialize stake pool doesn't match metadata {:?} vs {:?}", - operation, metadata.internal_operation - )))); - } - }, - InternalOperation::ResetLockup(inner) => { - if let InternalOperation::ResetLockup(ref metadata_op) = metadata.internal_operation { - if inner.owner != metadata_op.owner || inner.operator != metadata_op.operator { - return Err(ApiError::InvalidInput(Some(format!( - "Reset lockup operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Reset lockup operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::UnlockStake(inner) => { - if let InternalOperation::UnlockStake(ref metadata_op) = metadata.internal_operation { - if inner.owner != metadata_op.owner || inner.operator != metadata_op.operator { - return Err(ApiError::InvalidInput(Some(format!( - "Unlock stake operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Unlock stake operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::UpdateCommission(inner) => { - if let InternalOperation::UpdateCommission(ref metadata_op) = - metadata.internal_operation - { - if inner.owner != metadata_op.owner || inner.operator != metadata_op.operator { - return Err(ApiError::InvalidInput(Some(format!( - "Update commission operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Update commission operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::DistributeStakingRewards(inner) => { - if let InternalOperation::DistributeStakingRewards(ref metadata_op) = - metadata.internal_operation - { - if inner.operator != metadata_op.operator || inner.staker != metadata_op.staker { - return Err(ApiError::InvalidInput(Some(format!( - "Distribute staking rewards operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Distribute staking rewards operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::AddDelegatedStake(inner) => { - if let InternalOperation::AddDelegatedStake(ref metadata_op) = - metadata.internal_operation - { - if inner.delegator != metadata_op.delegator - || inner.pool_address != metadata_op.pool_address - { - return Err(ApiError::InvalidInput(Some(format!( - "AddDelegatedStake internal operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "InternalOperation::AddDelegatedStake doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::UnlockDelegatedStake(inner) => { - if let InternalOperation::UnlockDelegatedStake(ref metadata_op) = - metadata.internal_operation - { - if inner.delegator != metadata_op.delegator - || inner.pool_address != metadata_op.pool_address - { - return Err(ApiError::InvalidInput(Some(format!( - "Unlock delegated stake operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Unlock delegated stake operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - InternalOperation::WithdrawUndelegated(inner) => { - if let InternalOperation::WithdrawUndelegated(ref metadata_op) = - metadata.internal_operation - { - if inner.delegator != metadata_op.delegator - || inner.pool_address != metadata_op.pool_address - { - return Err(ApiError::InvalidInput(Some(format!( - "Withdraw undelegated operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - } else { - return Err(ApiError::InvalidInput(Some(format!( - "Withdraw undelegated operation doesn't match metadata {:?} vs {:?}", - inner, metadata.internal_operation - )))); - } - }, - } - - // Encode operation - let (txn_payload, sender) = operation.payload()?; - - // Build the transaction and make it ready for signing - let transaction_factory = TransactionFactory::new(server_context.chain_id) - .with_gas_unit_price(metadata.gas_price_per_unit.0) - .with_max_gas_amount(metadata.max_gas_amount.0); - - let mut txn_builder = transaction_factory - .payload(txn_payload) - .sender(sender) - .sequence_number(metadata.sequence_number.0); - - // Default expiry is 30 seconds from right now - if let Some(expiry_time_secs) = metadata.expiry_time_secs { - txn_builder = txn_builder.expiration_timestamp_secs(expiry_time_secs.0) - } - let unsigned_transaction = txn_builder.build(); - - let signing_message = hex::encode(signing_message(&unsigned_transaction).map_err(|err| { - ApiError::InvalidInput(Some(format!( - "Invalid transaction, can't build into a signing message {}", - err - ))) - })?); - let payload = SigningPayload { - account_identifier: AccountIdentifier::base_account(sender), - hex_bytes: signing_message, - signature_type: Some(SignatureType::Ed25519), - }; - - // Transaction is both the unsigned transaction and the payload - Ok(ConstructionPayloadsResponse { - unsigned_transaction: encode_bcs(&unsigned_transaction)?, - payloads: vec![payload], - }) -} - -/// Construction preprocess command (OFFLINE) -/// -/// This creates the request needed to fetch metadata -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionpreprocess) -async fn construction_preprocess( - request: ConstructionPreprocessRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/preprocess {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let internal_operation = InternalOperation::extract(&request.operations)?; - let required_public_keys = vec![AccountIdentifier::base_account(internal_operation.sender())]; - - if let Some(max_gas) = request - .metadata - .as_ref() - .and_then(|inner| inner.max_gas_amount) - { - if max_gas.0 < 1 { - return Err(ApiError::InvalidInput(Some( - "Cannot have a max gas amount less than 1".to_string(), - ))); - } - } - if let Some(expiry_time_secs) = request - .metadata - .as_ref() - .and_then(|inner| inner.expiry_time_secs) - { - if expiry_time_secs.0 - <= SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|err| { - ApiError::InternalError(Some(format!("Failed to get current time {}", err))) - })? - .as_secs() - { - return Err(ApiError::InvalidInput(Some( - "Expiry time secs is in the past, please provide a Unix timestamp in the future" - .to_string(), - ))); - } - } - - // Check gas input options - - let public_keys = request - .metadata - .as_ref() - .and_then(|inner| inner.public_keys.as_ref()); - - if request - .metadata - .as_ref() - .and_then(|inner| inner.max_gas_amount) - .is_none() - && public_keys - .as_ref() - .map(|inner| inner.is_empty()) - .unwrap_or(false) - { - return Err(ApiError::InvalidInput(Some( - "Must provide either max gas amount or public keys to estimate max gas amount" - .to_string(), - ))); - } - - Ok(ConstructionPreprocessResponse { - options: MetadataOptions { - internal_operation, - max_gas_amount: request - .metadata - .as_ref() - .and_then(|inner| inner.max_gas_amount), - gas_price_per_unit: request.metadata.as_ref().and_then(|inner| inner.gas_price), - expiry_time_secs: request - .metadata - .as_ref() - .and_then(|inner| inner.expiry_time_secs), - sequence_number: request - .metadata - .as_ref() - .and_then(|inner| inner.sequence_number), - public_keys: request - .metadata - .as_ref() - .and_then(|inner| inner.public_keys.clone()), - gas_price_multiplier: request - .metadata - .as_ref() - .and_then(|inner| inner.gas_price_multiplier), - gas_price_priority: request - .metadata - .as_ref() - .and_then(|inner| inner.gas_price_priority), - }, - required_public_keys, - }) -} - -/// Construction submit command (OFFLINE) -/// -/// Submits a transaction to the blockchain -/// -/// [API Spec](https://www.rosetta-api.org/docs/ConstructionApi.html#constructionsubmit) -async fn construction_submit( - request: ConstructionSubmitRequest, - server_context: RosettaContext, -) -> ApiResult { - debug!("/construction/submit {:?}", request); - check_network(request.network_identifier, &server_context)?; - - let rest_client = server_context.rest_client()?; - - let txn: SignedTransaction = decode_bcs(&request.signed_transaction, "SignedTransaction")?; - let hash = txn.clone().committed_hash(); - rest_client.submit_bcs(&txn).await?; - Ok(ConstructionSubmitResponse { - transaction_identifier: hash.into(), - }) -} diff --git a/crates/aptos-rosetta/src/error.rs b/crates/aptos-rosetta/src/error.rs deleted file mode 100644 index 3f0ca50462bee..0000000000000 --- a/crates/aptos-rosetta/src/error.rs +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{types, types::ErrorDetails}; -use aptos_rest_client::{aptos_api_types::AptosErrorCode, error::RestError}; -use hex::FromHexError; -use move_core_types::account_address::AccountAddressParseError; -use serde::{Deserialize, Serialize}; -use std::fmt::Formatter; -use warp::{http::StatusCode, reply::Reply}; - -pub type ApiResult = Result; - -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub enum ApiError { - TransactionIsPending, - NetworkIdentifierMismatch, - ChainIdMismatch, - DeserializationFailed(Option), - InvalidTransferOperations(Option<&'static str>), - InvalidSignatureType, - InvalidMaxGasFees, - MaxGasFeeTooLow(Option), - InvalidGasMultiplier, - GasEstimationFailed(Option), - InvalidOperations(Option), - MissingPayloadMetadata, - UnsupportedCurrency(Option), - UnsupportedSignatureCount(Option), - NodeIsOffline, - TransactionParseError(Option), - InternalError(Option), - CoinTypeFailedToBeFetched(Option), - - // Below here are codes directly from the REST API - AccountNotFound(Option), - ResourceNotFound(Option), - ModuleNotFound(Option), - StructFieldNotFound(Option), - VersionNotFound(Option), - TransactionNotFound(Option), - TableItemNotFound(Option), - BlockNotFound(Option), - StateValueNotFound(Option), - VersionPruned(Option), - BlockPruned(Option), - InvalidInput(Option), - InvalidTransactionUpdate(Option), - SequenceNumberTooOld(Option), - VmError(Option), - MempoolIsFull(Option), -} - -impl std::fmt::Display for ApiError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl std::error::Error for ApiError {} - -impl ApiError { - pub fn all() -> Vec { - use ApiError::*; - vec![ - TransactionIsPending, - NetworkIdentifierMismatch, - ChainIdMismatch, - DeserializationFailed(None), - InvalidTransferOperations(None), - InvalidSignatureType, - InvalidMaxGasFees, - MaxGasFeeTooLow(None), - InvalidGasMultiplier, - GasEstimationFailed(None), - InvalidOperations(None), - MissingPayloadMetadata, - UnsupportedCurrency(None), - UnsupportedSignatureCount(None), - NodeIsOffline, - TransactionParseError(None), - InternalError(None), - CoinTypeFailedToBeFetched(None), - AccountNotFound(None), - ResourceNotFound(None), - ModuleNotFound(None), - StructFieldNotFound(None), - VersionNotFound(None), - TransactionNotFound(None), - TableItemNotFound(None), - BlockNotFound(None), - StateValueNotFound(None), - VersionPruned(None), - BlockPruned(None), - InvalidInput(None), - InvalidTransactionUpdate(None), - SequenceNumberTooOld(None), - VmError(None), - MempoolIsFull(None), - ] - } - - pub fn code(&self) -> u32 { - use ApiError::*; - match self { - TransactionIsPending => 1, - NetworkIdentifierMismatch => 2, - ChainIdMismatch => 3, - DeserializationFailed(_) => 4, - InvalidTransferOperations(_) => 5, - InvalidSignatureType => 6, - InvalidMaxGasFees => 7, - MaxGasFeeTooLow(_) => 8, - InvalidGasMultiplier => 9, - InvalidOperations(_) => 10, - MissingPayloadMetadata => 11, - UnsupportedCurrency(_) => 12, - UnsupportedSignatureCount(_) => 13, - NodeIsOffline => 14, - TransactionParseError(_) => 15, - GasEstimationFailed(_) => 16, - InternalError(_) => 17, - AccountNotFound(_) => 18, - ResourceNotFound(_) => 19, - ModuleNotFound(_) => 20, - StructFieldNotFound(_) => 21, - VersionNotFound(_) => 22, - TransactionNotFound(_) => 23, - TableItemNotFound(_) => 24, - BlockNotFound(_) => 25, - VersionPruned(_) => 26, - BlockPruned(_) => 27, - InvalidInput(_) => 28, - InvalidTransactionUpdate(_) => 29, - SequenceNumberTooOld(_) => 30, - VmError(_) => 31, - MempoolIsFull(_) => 32, - CoinTypeFailedToBeFetched(_) => 33, - StateValueNotFound(_) => 34, - } - } - - pub fn retriable(&self) -> bool { - use ApiError::*; - matches!( - self, - AccountNotFound(_) - | BlockNotFound(_) - | MempoolIsFull(_) - | GasEstimationFailed(_) - | CoinTypeFailedToBeFetched(_) - ) - } - - pub fn status_code(&self) -> StatusCode { - // Per Rosetta guidelines, all errors are 500s - StatusCode::INTERNAL_SERVER_ERROR - } - - /// This value must be fixed, so it's all static strings - pub fn message(&self) -> &'static str { - match self { - ApiError::TransactionIsPending => "Transaction is pending", - ApiError::NetworkIdentifierMismatch => "Network identifier doesn't match", - ApiError::ChainIdMismatch => "Chain Id doesn't match", - ApiError::DeserializationFailed(_) => "Deserialization failed", - ApiError::InvalidTransferOperations(_) => "Invalid operations for a transfer", - ApiError::AccountNotFound(_) => "Account not found", - ApiError::InvalidSignatureType => "Invalid signature type", - ApiError::InvalidMaxGasFees => "Invalid max gas fee", - ApiError::MaxGasFeeTooLow(_) => "Max fee is lower than the estimated cost of the transaction", - ApiError::InvalidGasMultiplier => "Invalid gas multiplier", - ApiError::InvalidOperations(_) => "Invalid operations", - ApiError::MissingPayloadMetadata => "Payload metadata is missing", - ApiError::UnsupportedCurrency(_) => "Currency is unsupported", - ApiError::UnsupportedSignatureCount(_) => "Number of signatures is not supported", - ApiError::NodeIsOffline => "This API is unavailable for the node because he's offline", - ApiError::BlockNotFound(_) => "Block is missing events", - ApiError::StateValueNotFound(_) => "StateValue not found.", - ApiError::TransactionParseError(_) => "Transaction failed to parse", - ApiError::InternalError(_) => "Internal error", - ApiError::CoinTypeFailedToBeFetched(_) => "Faileed to retrieve the coin type information, please retry", - ApiError::ResourceNotFound(_) => "Resource not found", - ApiError::ModuleNotFound(_) => "Module not found", - ApiError::StructFieldNotFound(_) => "Struct field not found", - ApiError::VersionNotFound(_) => "Version not found", - ApiError::TransactionNotFound(_) => "Transaction not found", - ApiError::TableItemNotFound(_) => "Table item not found", - ApiError::VersionPruned(_) => "Version pruned", - ApiError::BlockPruned(_) => "Block pruned", - ApiError::InvalidInput(_) => "Invalid input", - ApiError::InvalidTransactionUpdate(_) => "Invalid transaction update. Can only update gas unit price", - ApiError::SequenceNumberTooOld(_) => "Sequence number too old. Please create a new transaction with an updated sequence number", - ApiError::VmError(_) => "Transaction submission failed due to VM error", - ApiError::MempoolIsFull(_) => "Mempool is full all accounts", - ApiError::GasEstimationFailed(_) => "Gas estimation failed", - } - } - - pub fn details(self) -> Option { - match self { - ApiError::DeserializationFailed(inner) => inner, - ApiError::InvalidTransferOperations(inner) => inner.map(|inner| inner.to_string()), - ApiError::UnsupportedCurrency(inner) => inner, - ApiError::UnsupportedSignatureCount(inner) => inner.map(|inner| inner.to_string()), - ApiError::TransactionParseError(inner) => inner, - ApiError::InvalidOperations(inner) => inner, - ApiError::InternalError(inner) => inner, - ApiError::CoinTypeFailedToBeFetched(inner) => inner, - ApiError::AccountNotFound(inner) => inner, - ApiError::ResourceNotFound(inner) => inner, - ApiError::ModuleNotFound(inner) => inner, - ApiError::StructFieldNotFound(inner) => inner, - ApiError::VersionNotFound(inner) => inner, - ApiError::TransactionNotFound(inner) => inner, - ApiError::TableItemNotFound(inner) => inner, - ApiError::BlockNotFound(inner) => inner, - ApiError::VersionPruned(inner) => inner, - ApiError::BlockPruned(inner) => inner, - ApiError::InvalidInput(inner) => inner, - ApiError::InvalidTransactionUpdate(inner) => inner, - ApiError::SequenceNumberTooOld(inner) => inner, - ApiError::VmError(inner) => inner, - ApiError::MempoolIsFull(inner) => inner, - ApiError::GasEstimationFailed(inner) => inner, - ApiError::MaxGasFeeTooLow(inner) => inner, - _ => None, - } - .map(|details| ErrorDetails { details }) - } - - pub fn deserialization_failed(type_: &str) -> ApiError { - ApiError::DeserializationFailed(Some(type_.to_string())) - } - - pub fn into_error(self) -> types::Error { - self.into() - } -} - -impl From for types::Error { - fn from(error: ApiError) -> Self { - let message = error.message().to_string(); - let code = error.code(); - let retriable = error.retriable(); - let details = error.details(); - types::Error { - message, - code, - retriable, - details, - } - } -} - -impl From for ApiError { - fn from(err: RestError) -> Self { - match err { - RestError::Api(err) => match err.error.error_code { - AptosErrorCode::AccountNotFound => { - ApiError::AccountNotFound(Some(err.error.message)) - }, - AptosErrorCode::ResourceNotFound => { - ApiError::ResourceNotFound(Some(err.error.message)) - }, - AptosErrorCode::ModuleNotFound => ApiError::ModuleNotFound(Some(err.error.message)), - AptosErrorCode::StructFieldNotFound => { - ApiError::StructFieldNotFound(Some(err.error.message)) - }, - AptosErrorCode::VersionNotFound => { - ApiError::VersionNotFound(Some(err.error.message)) - }, - AptosErrorCode::TransactionNotFound => { - ApiError::TransactionNotFound(Some(err.error.message)) - }, - AptosErrorCode::TableItemNotFound => { - ApiError::TableItemNotFound(Some(err.error.message)) - }, - AptosErrorCode::BlockNotFound => ApiError::BlockNotFound(Some(err.error.message)), - AptosErrorCode::StateValueNotFound => { - ApiError::StateValueNotFound(Some(err.error.message)) - }, - AptosErrorCode::VersionPruned => ApiError::VersionPruned(Some(err.error.message)), - AptosErrorCode::BlockPruned => ApiError::BlockPruned(Some(err.error.message)), - AptosErrorCode::InvalidInput => ApiError::InvalidInput(Some(err.error.message)), - AptosErrorCode::InvalidTransactionUpdate => { - ApiError::InvalidInput(Some(err.error.message)) - }, - AptosErrorCode::SequenceNumberTooOld => { - ApiError::SequenceNumberTooOld(Some(err.error.message)) - }, - AptosErrorCode::VmError => ApiError::VmError(Some(err.error.message)), - AptosErrorCode::HealthCheckFailed => { - ApiError::InternalError(Some(err.error.message)) - }, - AptosErrorCode::MempoolIsFull => ApiError::MempoolIsFull(Some(err.error.message)), - AptosErrorCode::WebFrameworkError => { - ApiError::InternalError(Some(err.error.message)) - }, - AptosErrorCode::BcsNotSupported => ApiError::InvalidInput(Some(err.error.message)), - AptosErrorCode::InternalError => ApiError::InternalError(Some(err.error.message)), - AptosErrorCode::ApiDisabled => ApiError::InternalError(Some(err.error.message)), - }, - RestError::Bcs(_) => ApiError::DeserializationFailed(None), - RestError::Json(_) => ApiError::DeserializationFailed(None), - RestError::Http(status_code, err) => ApiError::InternalError(Some(format!( - "Failed internal API call with HTTP code {}: {:#}", - status_code, err - ))), - RestError::UrlParse(err) => ApiError::InternalError(Some(err.to_string())), - RestError::Timeout(err) => ApiError::InternalError(Some(err.to_string())), - RestError::Unknown(err) => ApiError::InternalError(Some(err.to_string())), - } - } -} - -impl From for ApiError { - fn from(err: AccountAddressParseError) -> Self { - ApiError::DeserializationFailed(Some(err.to_string())) - } -} - -impl From for ApiError { - fn from(err: FromHexError) -> Self { - ApiError::DeserializationFailed(Some(err.to_string())) - } -} - -impl From for ApiError { - fn from(err: bcs::Error) -> Self { - ApiError::DeserializationFailed(Some(err.to_string())) - } -} - -impl From for ApiError { - fn from(err: anyhow::Error) -> Self { - ApiError::InternalError(Some(err.to_string())) - } -} - -impl From for ApiError { - fn from(err: std::num::ParseIntError) -> Self { - ApiError::DeserializationFailed(Some(err.to_string())) - } -} - -impl warp::reject::Reject for ApiError {} - -impl Reply for ApiError { - fn into_response(self) -> warp::reply::Response { - warp::reply::json(&self.into_error()).into_response() - } -} diff --git a/crates/aptos-rosetta/src/lib.rs b/crates/aptos-rosetta/src/lib.rs deleted file mode 100644 index a23693081e59f..0000000000000 --- a/crates/aptos-rosetta/src/lib.rs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Aptos Rosetta API -//! -//! [Rosetta API Spec](https://www.rosetta-api.org/docs/Reference.html) - -use crate::{ - block::BlockRetriever, - common::{handle_request, with_context}, - error::{ApiError, ApiResult}, - types::Store, -}; -use aptos_config::config::ApiConfig; -use aptos_logger::{debug, warn}; -use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; -use aptos_warp_webserver::{logger, Error, WebServer}; -use std::{collections::BTreeMap, convert::Infallible, sync::Arc}; -use tokio::task::JoinHandle; -use warp::{ - http::{HeaderValue, Method, StatusCode}, - reply, Filter, Rejection, Reply, -}; - -mod account; -mod block; -mod construction; -mod network; - -pub mod client; -pub mod common; -pub mod error; -pub mod types; - -pub const NODE_VERSION: &str = "0.1"; -pub const ROSETTA_VERSION: &str = "1.4.12"; - -/// Rosetta API context for use on all APIs -#[derive(Clone, Debug)] -pub struct RosettaContext { - /// A rest client to connect to a fullnode - rest_client: Option>, - /// ChainId of the chain to connect to - pub chain_id: ChainId, - /// Block index cache - pub block_cache: Option>, - pub owner_addresses: Vec, - pub pool_address_to_owner: BTreeMap, -} - -impl RosettaContext { - pub async fn new( - rest_client: Option>, - chain_id: ChainId, - block_cache: Option>, - owner_addresses: Vec, - ) -> Self { - let mut pool_address_to_owner = BTreeMap::new(); - if let Some(ref rest_client) = rest_client { - // We have to now fill in all of the mappings of owner to pool address - for owner_address in owner_addresses.iter() { - if let Ok(store) = rest_client - .get_account_resource_bcs::( - *owner_address, - "0x1::staking_contract::Store", - ) - .await - { - let store = store.into_inner(); - let pool_addresses: Vec<_> = store - .staking_contracts - .iter() - .map(|(_, pool)| pool.pool_address) - .collect(); - for pool_address in pool_addresses { - pool_address_to_owner.insert(pool_address, *owner_address); - } - } else { - warn!("Did not find a pool for owner: {}", owner_address); - } - } - } - - RosettaContext { - rest_client, - chain_id, - block_cache, - owner_addresses, - pool_address_to_owner, - } - } - - fn rest_client(&self) -> ApiResult> { - if let Some(ref client) = self.rest_client { - Ok(client.clone()) - } else { - Err(ApiError::NodeIsOffline) - } - } - - fn block_cache(&self) -> ApiResult> { - if let Some(ref block_cache) = self.block_cache { - Ok(block_cache.clone()) - } else { - Err(ApiError::NodeIsOffline) - } - } -} - -/// Creates HTTP server (warp-based) for Rosetta -pub fn bootstrap( - chain_id: ChainId, - api_config: ApiConfig, - rest_client: Option, - owner_addresses: Vec, -) -> anyhow::Result { - let runtime = aptos_runtimes::spawn_named_runtime("rosetta".into(), None); - - debug!("Starting up Rosetta server with {:?}", api_config); - - runtime.spawn(bootstrap_async( - chain_id, - api_config, - rest_client, - owner_addresses, - )); - Ok(runtime) -} - -/// Creates HTTP server for Rosetta in an async context -pub async fn bootstrap_async( - chain_id: ChainId, - api_config: ApiConfig, - rest_client: Option, - owner_addresses: Vec, -) -> anyhow::Result> { - debug!("Starting up Rosetta server with {:?}", api_config); - - if let Some(ref client) = rest_client { - assert_eq!( - chain_id.id(), - client - .get_ledger_information() - .await - .expect("Should successfully get ledger information from Rest API on bootstap") - .into_inner() - .chain_id, - "Failed to match Rosetta chain Id to upstream server" - ); - } - - let api = WebServer::from(api_config.clone()); - let handle = tokio::spawn(async move { - // If it's Online mode, add the block cache - let rest_client = rest_client.map(Arc::new); - let block_cache = rest_client.as_ref().map(|rest_client| { - Arc::new(BlockRetriever::new( - api_config.max_transactions_page_size, - rest_client.clone(), - )) - }); - - let context = - RosettaContext::new(rest_client.clone(), chain_id, block_cache, owner_addresses).await; - api.serve(routes(context)).await; - }); - Ok(handle) -} - -/// Collection of all routes for the server -pub fn routes( - context: RosettaContext, -) -> impl Filter + Clone { - account::routes(context.clone()) - .or(block::block_route(context.clone())) - .or(construction::combine_route(context.clone())) - .or(construction::derive_route(context.clone())) - .or(construction::hash_route(context.clone())) - .or(construction::metadata_route(context.clone())) - .or(construction::parse_route(context.clone())) - .or(construction::payloads_route(context.clone())) - .or(construction::preprocess_route(context.clone())) - .or(construction::submit_route(context.clone())) - .or(network::list_route(context.clone())) - .or(network::options_route(context.clone())) - .or(network::status_route(context.clone())) - .or(health_check_route(context)) - .with( - warp::cors() - .allow_any_origin() - .allow_methods(vec![Method::GET, Method::POST]) - .allow_headers(vec![warp::http::header::CONTENT_TYPE]), - ) - .with(logger()) - .recover(handle_rejection) -} - -/// Handle error codes from warp -async fn handle_rejection(err: Rejection) -> Result { - debug!("Failed with: {:?}", err); - let body = reply::json(&Error::new( - StatusCode::INTERNAL_SERVER_ERROR, - format!("unexpected error: {:?}", err), - )); - let mut rep = reply::with_status(body, StatusCode::INTERNAL_SERVER_ERROR).into_response(); - rep.headers_mut() - .insert("access-control-allow-origin", HeaderValue::from_static("*")); - Ok(rep) -} - -/// These parameters are directly passed onto the underlying rest server for a healthcheck -#[derive(serde::Deserialize)] -struct HealthCheckParams { - pub duration_secs: Option, -} - -/// Default amount of time the fullnode is accepted to be behind (arbitrarily it's 5 minutes) -const HEALTH_CHECK_DEFAULT_SECS: u64 = 300; - -pub fn health_check_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("-" / "healthy") - .and(warp::path::end()) - .and(warp::query().map(move |params: HealthCheckParams| params)) - .and(with_context(server_context)) - .and_then(handle_request(health_check)) -} - -/// Calls the underlying REST health check -async fn health_check( - params: HealthCheckParams, - server_context: RosettaContext, -) -> ApiResult<&'static str> { - let rest_client = server_context.rest_client()?; - let duration_secs = params.duration_secs.unwrap_or(HEALTH_CHECK_DEFAULT_SECS); - rest_client.health_check(duration_secs).await?; - - Ok("aptos-node:ok") -} diff --git a/crates/aptos-rosetta/src/main.rs b/crates/aptos-rosetta/src/main.rs deleted file mode 100644 index d267b0eabb65c..0000000000000 --- a/crates/aptos-rosetta/src/main.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use aptos_config::config::{ApiConfig, DEFAULT_MAX_PAGE_SIZE}; -use aptos_logger::prelude::*; -use aptos_node::AptosNodeArgs; -use aptos_rosetta::bootstrap; -use aptos_sdk::move_types::account_address::AccountAddress; -use aptos_types::chain_id::ChainId; -use clap::Parser; -use std::{ - fs::read_to_string, - net::SocketAddr, - path::PathBuf, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - thread, - time::Duration, -}; -use tokio::time::Instant; - -/// Poll every 100 ms -const DEFAULT_REST_API_WAIT_INTERVAL_MS: u64 = 100; -/// Log failures every 10 seconds -const LOG_INTERVAL_MS: u64 = 10_000; - -#[tokio::main] -async fn main() { - let args: CommandArgs = CommandArgs::parse(); - - match args { - CommandArgs::OnlineRemote(_) => { - println!("aptos-rosetta: Starting Rosetta in Online remote (no local full node) mode") - }, - CommandArgs::Online(_) => { - println!("aptos-rosetta: Starting Rosetta in Online (with local full node) mode") - }, - CommandArgs::Offline(_) => println!("aptos-rosetta: Starting Rosetta in Offline mode"), - } - - // If we're in online mode, we run a full node side by side, the fullnode sets up the logger - let _maybe_node = if let CommandArgs::Online(OnlineLocalArgs { - ref node_args, - ref online_args, - }) = args - { - println!("aptos-rosetta: Starting local full node"); - let node_args = node_args.clone(); - let runtime = thread::spawn(move || node_args.run()); - - // Wait and ensure the node is running on the URL - let client = aptos_rest_client::Client::new(online_args.rest_api_url.clone()); - let start = Instant::now(); - loop { - match client.get_index_bcs().await { - Ok(_) => { - break; - }, - Err(err) => { - sample!( - SampleRate::Duration(Duration::from_millis(LOG_INTERVAL_MS)), - println!( - "aptos-rosetta: Full node REST API isn't responding yet. You should check the node logs. It's been waiting {} seconds. Error: {:?}", - start.elapsed().as_secs(), - err - ) - ); - tokio::time::sleep(Duration::from_millis(DEFAULT_REST_API_WAIT_INTERVAL_MS)) - .await; - }, - } - } - - println!("aptos-rosetta: Local full node started successfully"); - Some(runtime) - } else { - // If we aren't running a full node, set up the logger now - aptos_logger::Logger::new().init(); - None - }; - - println!("aptos-rosetta: Starting rosetta"); - // Ensure runtime for Rosetta is up and running - let _rosetta = bootstrap( - args.chain_id(), - args.api_config(), - args.rest_client(), - args.owner_addresses(), - ) - .expect("aptos-rosetta: Should bootstrap rosetta server"); - - println!("aptos-rosetta: Rosetta started"); - // Run until there is an interrupt - let term = Arc::new(AtomicBool::new(false)); - while !term.load(Ordering::Acquire) { - std::thread::park(); - } -} - -/// A trait to provide common values from both online and offline mode -trait ServerArgs { - /// Retrieve the API config for the local server - fn api_config(&self) -> ApiConfig; - - /// Retrieve the optional rest client for the local server - fn rest_client(&self) -> Option; - - /// Retrieve the chain id - fn chain_id(&self) -> ChainId; - - /// Retrieve owner addresses - fn owner_addresses(&self) -> Vec; -} - -/// Aptos Rosetta API Server -/// -/// Provides an implementation of [Rosetta](https://www.rosetta-api.org/docs/Reference.html) on Aptos. -#[derive(Debug, Parser)] -#[clap(name = "aptos-rosetta", author, version, propagate_version = true)] -pub enum CommandArgs { - /// Run a local online server that connects to a fullnode endpoint - OnlineRemote(OnlineRemoteArgs), - /// Run a local full node in tandem with Rosetta - Online(OnlineLocalArgs), - /// Run a local online server that doesn't connect to a fullnode endpoint - Offline(OfflineArgs), -} - -impl ServerArgs for CommandArgs { - fn api_config(&self) -> ApiConfig { - match self { - CommandArgs::OnlineRemote(args) => args.api_config(), - CommandArgs::Offline(args) => args.api_config(), - CommandArgs::Online(args) => args.api_config(), - } - } - - fn rest_client(&self) -> Option { - match self { - CommandArgs::OnlineRemote(args) => args.rest_client(), - CommandArgs::Offline(args) => args.rest_client(), - CommandArgs::Online(args) => args.rest_client(), - } - } - - fn chain_id(&self) -> ChainId { - match self { - CommandArgs::OnlineRemote(args) => args.chain_id(), - CommandArgs::Offline(args) => args.chain_id(), - CommandArgs::Online(args) => args.chain_id(), - } - } - - fn owner_addresses(&self) -> Vec { - match self { - CommandArgs::OnlineRemote(args) => args.owner_addresses(), - CommandArgs::Offline(args) => args.owner_addresses(), - CommandArgs::Online(args) => args.owner_addresses(), - } - } -} - -#[derive(Debug, Parser)] -pub struct OfflineArgs { - /// Listen address for the server. e.g. 0.0.0.0:8082 - #[clap(long, default_value = "0.0.0.0:8082")] - listen_address: SocketAddr, - /// Path to TLS cert for HTTPS support - #[clap(long)] - tls_cert_path: Option, - /// Path to TLS key for HTTPS support - #[clap(long)] - tls_key_path: Option, - /// Limit to content length on all requests - #[clap(long)] - content_length_limit: Option, - /// ChainId to be used for the server e.g. TESTNET - #[clap(long, default_value_t = ChainId::test())] - chain_id: ChainId, - /// Page size for transactions APIs, must match the downstream node - /// - /// This can be configured to change performance characteristics - #[clap(long, default_value_t = DEFAULT_MAX_PAGE_SIZE)] - transactions_page_size: u16, -} - -impl ServerArgs for OfflineArgs { - fn api_config(&self) -> ApiConfig { - ApiConfig { - enabled: true, - address: self.listen_address, - tls_cert_path: self.tls_cert_path.clone(), - tls_key_path: self.tls_key_path.clone(), - content_length_limit: self.content_length_limit, - max_transactions_page_size: self.transactions_page_size, - ..Default::default() - } - } - - fn rest_client(&self) -> Option { - None - } - - fn chain_id(&self) -> ChainId { - self.chain_id - } - - fn owner_addresses(&self) -> Vec { - vec![] - } -} - -#[derive(Debug, Parser)] -pub struct OnlineRemoteArgs { - #[clap(flatten)] - offline_args: OfflineArgs, - /// URL for the Aptos REST API. e.g. https://fullnode.devnet.aptoslabs.com - #[clap(long, default_value = "http://localhost:8080")] - rest_api_url: url::Url, - /// Owner addresses file as a YAML file with a list - #[clap(long, value_parser)] - owner_address_file: Option, -} - -impl ServerArgs for OnlineRemoteArgs { - fn api_config(&self) -> ApiConfig { - self.offline_args.api_config() - } - - fn rest_client(&self) -> Option { - Some(aptos_rest_client::Client::new(self.rest_api_url.clone())) - } - - fn chain_id(&self) -> ChainId { - self.offline_args.chain_id - } - - fn owner_addresses(&self) -> Vec { - if let Some(ref path) = self.owner_address_file { - serde_yaml::from_str( - &read_to_string(path.as_path()).expect("Failed to read owner address file"), - ) - .expect("Owner address file is in an invalid format") - } else { - vec![] - } - } -} - -#[derive(Debug, Parser)] -pub struct OnlineLocalArgs { - #[clap(flatten)] - online_args: OnlineRemoteArgs, - #[clap(flatten)] - node_args: AptosNodeArgs, -} - -impl ServerArgs for OnlineLocalArgs { - fn api_config(&self) -> ApiConfig { - self.online_args.offline_args.api_config() - } - - fn rest_client(&self) -> Option { - Some(aptos_rest_client::Client::new( - self.online_args.rest_api_url.clone(), - )) - } - - fn chain_id(&self) -> ChainId { - self.online_args.offline_args.chain_id - } - - fn owner_addresses(&self) -> Vec { - self.online_args.owner_addresses() - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - CommandArgs::command().debug_assert() -} diff --git a/crates/aptos-rosetta/src/network.rs b/crates/aptos-rosetta/src/network.rs deleted file mode 100644 index 926ee55192c38..0000000000000 --- a/crates/aptos-rosetta/src/network.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{check_network, handle_request, with_context, with_empty_request}, - error::ApiError, - types::{ - Allow, MetadataRequest, NetworkListResponse, NetworkOptionsResponse, NetworkRequest, - NetworkStatusResponse, OperationStatusType, OperationType, Version, - }, - RosettaContext, NODE_VERSION, ROSETTA_VERSION, -}; -use aptos_logger::{debug, trace}; -use warp::Filter; - -pub fn list_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("network" / "list") - .and(warp::post()) - .and(with_empty_request()) - .and(with_context(server_context)) - .and_then(handle_request(network_list)) -} - -pub fn options_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("network" / "options") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(network_options)) -} - -pub fn status_route( - server_context: RosettaContext, -) -> impl Filter + Clone { - warp::path!("network" / "status") - .and(warp::post()) - .and(warp::body::json()) - .and(with_context(server_context)) - .and_then(handle_request(network_status)) -} - -/// List [`NetworkIdentifier`]s supported by this proxy aka [`ChainId`]s -/// -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networklist) -async fn network_list( - _empty: MetadataRequest, - server_context: RosettaContext, -) -> Result { - debug!("/network/list"); - trace!( - server_context = ?server_context, - "network_list", - ); - - let response = NetworkListResponse { - network_identifiers: vec![server_context.chain_id.into()], - }; - - Ok(response) -} - -/// Get Network options -/// -/// This lists out all errors, operations, and statuses, along with versioning information. -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) -async fn network_options( - request: NetworkRequest, - server_context: RosettaContext, -) -> Result { - debug!("/network/options"); - trace!( - request = ?request, - server_context = ?server_context, - "network_options", - ); - - check_network(request.network_identifier, &server_context)?; - - let version = Version { - rosetta_version: ROSETTA_VERSION.to_string(), - // TODO: Get from node via REST API - node_version: NODE_VERSION.to_string(), - middleware_version: "0.1.0".to_string(), - }; - - let operation_statuses = OperationStatusType::all() - .into_iter() - .map(|status| status.into()) - .collect(); - let operation_types = OperationType::all() - .into_iter() - .map(|op| op.to_string()) - .collect(); - let errors = ApiError::all() - .into_iter() - .map(|err| err.into_error()) - .collect(); - - let allow = Allow { - operation_statuses, - operation_types, - errors, - historical_balance_lookup: true, - timestamp_start_index: 2, - call_methods: vec![], - balance_exemptions: vec![], - mempool_coins: false, - }; - - let response = NetworkOptionsResponse { version, allow }; - - Ok(response) -} - -/// Get network status including the latest state -/// -/// This should respond with the latest ledger version, timestamp, and genesis information -/// -/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) -async fn network_status( - request: NetworkRequest, - server_context: RosettaContext, -) -> Result { - debug!("/network/status"); - trace!( - request = ?request, - server_context = ?server_context, - "network_status", - ); - - check_network(request.network_identifier, &server_context)?; - let chain_id = server_context.chain_id; - let rest_client = server_context.rest_client()?; - let block_cache = server_context.block_cache()?; - let genesis_block_identifier = block_cache - .get_block_info_by_height(0, chain_id) - .await? - .block_id; - let response = rest_client.get_ledger_information().await?; - let state = response.state(); - - // Get the oldest block - let oldest_block_identifier = block_cache - .get_block_info_by_height(state.oldest_block_height, chain_id) - .await? - .block_id; - - // Get the latest block - let current_block = block_cache - .get_block_info_by_height(state.block_height, chain_id) - .await?; - let current_block_identifier = current_block.block_id; - - let response = NetworkStatusResponse { - current_block_identifier, - current_block_timestamp: current_block.timestamp, - genesis_block_identifier, - oldest_block_identifier, - sync_status: None, - peers: vec![], - }; - - Ok(response) -} diff --git a/crates/aptos-rosetta/src/types/identifiers.rs b/crates/aptos-rosetta/src/types/identifiers.rs deleted file mode 100644 index e6ac9a79b908a..0000000000000 --- a/crates/aptos-rosetta/src/types/identifiers.rs +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Identifiers for the Rosetta spec -//! -//! [Spec](https://www.rosetta-api.org/docs/api_identifiers.html) - -use crate::{ - common::{to_hex_lower, BlockHash, BLOCKCHAIN}, - error::{ApiError, ApiResult}, -}; -use aptos_types::{ - account_address::AccountAddress, chain_id::ChainId, transaction::TransactionInfo, -}; -use serde::{Deserialize, Serialize}; -use std::{ - convert::{TryFrom, TryInto}, - str::FromStr, -}; - -/// Account identifier, specified as a hex encoded account address (with leading 0x) -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/AccountIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct AccountIdentifier { - /// Hex encoded AccountAddress beginning with 0x - pub address: String, - /// Sub account only used for staking - #[serde(skip_serializing_if = "Option::is_none")] - pub sub_account: Option, -} - -impl AccountIdentifier { - /// Convert [`AccountIdentifier`] to an [`AccountAddress`] - pub fn account_address(&self) -> ApiResult { - str_to_account_address(self.address.as_str()) - } - - pub fn pool_address(&self) -> ApiResult> { - if let Some(sub_account) = &self.sub_account { - if let Some(metadata) = &sub_account.metadata { - return str_to_account_address(metadata.pool_address.as_str()).map(Some); - } - } - - Ok(None) - } - - pub fn base_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: None, - } - } - - pub fn total_stake_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_total_stake()), - } - } - - pub fn pending_active_stake_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_pending_active_stake()), - } - } - - pub fn active_stake_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_active_stake()), - } - } - - pub fn pending_inactive_stake_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_pending_inactive_stake()), - } - } - - pub fn inactive_stake_account(address: AccountAddress) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_inactive_stake()), - } - } - - pub fn operator_stake_account( - address: AccountAddress, - operator_address: AccountAddress, - ) -> Self { - AccountIdentifier { - address: to_hex_lower(&address), - sub_account: Some(SubAccountIdentifier::new_operator_stake(operator_address)), - } - } - - pub fn is_base_account(&self) -> bool { - self.sub_account.is_none() - } - - pub fn is_total_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_total_stake() - } else { - false - } - } - - pub fn is_pending_active_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_pending_active_stake() - } else { - false - } - } - - pub fn is_active_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_active_stake() - } else { - false - } - } - - pub fn is_pending_inactive_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_pending_inactive_stake() - } else { - false - } - } - - pub fn is_inactive_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_inactive_stake() - } else { - false - } - } - - pub fn is_delegator_active_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_delegator_active_stake() - } else { - false - } - } - - pub fn is_delegator_inactive_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_delegator_inactive_stake() - } else { - false - } - } - - pub fn is_delegator_pending_inactive_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - inner.is_delegator_pending_inactive_stake() - } else { - false - } - } - - pub fn is_operator_stake(&self) -> bool { - if let Some(ref inner) = self.sub_account { - !(inner.is_total_stake() - || inner.is_active_stake() - || inner.is_pending_active_stake() - || inner.is_inactive_stake() - || inner.is_pending_inactive_stake()) - } else { - false - } - } - - pub fn operator_address(&self) -> ApiResult { - if let Some(ref inner) = self.sub_account { - inner.operator_address() - } else { - Err(ApiError::InternalError(Some( - "Can't get operator address of a non-operator stake account".to_string(), - ))) - } - } -} - -fn str_to_account_address(address: &str) -> Result { - AccountAddress::from_str(address) - .map_err(|_| ApiError::InvalidInput(Some("Invalid account address".to_string()))) -} - -/// There are two types of SubAccountIdentifiers -/// 1. `stake` which is the total stake -/// 2. `stake-` which is the stake on the operator -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SubAccountIdentifier { - /// Hex encoded AccountAddress beginning with 0x - pub address: String, - /// Metadata only used for delegated staking - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -const STAKE: &str = "stake"; -const PENDING_ACTIVE_STAKE: &str = "pending_active_stake"; -const ACTIVE_STAKE: &str = "active_stake"; -const PENDING_INACTIVE_STAKE: &str = "pending_inactive_stake"; -const INACTIVE_STAKE: &str = "inactive_stake"; -const ACCOUNT_SEPARATOR: char = '-'; - -impl SubAccountIdentifier { - pub fn new_total_stake() -> SubAccountIdentifier { - SubAccountIdentifier { - address: STAKE.to_string(), - metadata: None, - } - } - - pub fn new_pending_active_stake() -> SubAccountIdentifier { - SubAccountIdentifier { - address: PENDING_ACTIVE_STAKE.to_string(), - metadata: None, - } - } - - pub fn new_active_stake() -> SubAccountIdentifier { - SubAccountIdentifier { - address: ACTIVE_STAKE.to_string(), - metadata: None, - } - } - - pub fn new_pending_inactive_stake() -> SubAccountIdentifier { - SubAccountIdentifier { - address: PENDING_INACTIVE_STAKE.to_string(), - metadata: None, - } - } - - pub fn new_inactive_stake() -> SubAccountIdentifier { - SubAccountIdentifier { - address: INACTIVE_STAKE.to_string(), - metadata: None, - } - } - - pub fn new_delegated_total_stake(pool: &str) -> SubAccountIdentifier { - SubAccountIdentifier { - address: STAKE.to_string(), - metadata: Some(SubAccountIdentifierMetadata::new_pool_address( - AccountAddress::from_str(pool).unwrap(), - )), - } - } - - pub fn new_delegated_active_stake(pool: &str) -> SubAccountIdentifier { - SubAccountIdentifier { - address: ACTIVE_STAKE.to_string(), - metadata: Some(SubAccountIdentifierMetadata::new_pool_address( - AccountAddress::from_str(pool).unwrap(), - )), - } - } - - pub fn new_delegated_pending_inactive_stake(pool: &str) -> SubAccountIdentifier { - SubAccountIdentifier { - address: PENDING_INACTIVE_STAKE.to_string(), - metadata: Some(SubAccountIdentifierMetadata::new_pool_address( - AccountAddress::from_str(pool).unwrap(), - )), - } - } - - pub fn new_delegated_inactive_stake(pool: &str) -> SubAccountIdentifier { - SubAccountIdentifier { - address: INACTIVE_STAKE.to_string(), - metadata: Some(SubAccountIdentifierMetadata::new_pool_address( - AccountAddress::from_str(pool).unwrap(), - )), - } - } - - pub fn new_operator_stake(operator: AccountAddress) -> SubAccountIdentifier { - SubAccountIdentifier { - address: format!("{}-{}", STAKE, to_hex_lower(&operator)), - metadata: None, - } - } - - pub fn is_total_stake(&self) -> bool { - self.address.as_str() == STAKE - } - - pub fn is_pending_active_stake(&self) -> bool { - self.address.as_str() == PENDING_ACTIVE_STAKE - } - - pub fn is_active_stake(&self) -> bool { - self.address.as_str() == ACTIVE_STAKE && self.metadata.is_none() - } - - pub fn is_pending_inactive_stake(&self) -> bool { - self.address.as_str() == PENDING_INACTIVE_STAKE && self.metadata.is_none() - } - - pub fn is_inactive_stake(&self) -> bool { - self.address.as_str() == INACTIVE_STAKE && self.metadata.is_none() - } - - pub fn is_delegator_active_stake(&self) -> bool { - self.address.as_str() == ACTIVE_STAKE && self.metadata.is_some() - } - - pub fn is_delegator_inactive_stake(&self) -> bool { - self.address.as_str() == INACTIVE_STAKE && self.metadata.is_some() - } - - pub fn is_delegator_pending_inactive_stake(&self) -> bool { - self.address.as_str() == PENDING_INACTIVE_STAKE && self.metadata.is_some() - } - - pub fn operator_address(&self) -> ApiResult { - let mut parts = self.address.split(ACCOUNT_SEPARATOR); - - if let Some(stake) = parts.next() { - if stake == STAKE { - if let Some(operator) = parts.next() { - return str_to_account_address(operator); - } - } - } - - Err(ApiError::InvalidInput(Some(format!( - "Sub account isn't an operator address {:?}", - self - )))) - } -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SubAccountIdentifierMetadata { - /// Hex encoded Pool beginning with 0x - pub pool_address: String, -} - -impl SubAccountIdentifierMetadata { - pub fn new_pool_address(pool_address: AccountAddress) -> Self { - SubAccountIdentifierMetadata { - pool_address: to_hex_lower(&pool_address), - } - } -} - -/// Identifier for a "block". In aptos, we use a transaction model, so the index -/// represents multiple transactions in a "block" grouping of transactions -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/BlockIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BlockIdentifier { - /// Block index, which points to a txn at the beginning of a "block" - pub index: u64, - /// Accumulator hash at the beginning of the block - pub hash: String, -} - -impl BlockIdentifier { - pub fn from_block( - block: &aptos_rest_client::aptos_api_types::BcsBlock, - chain_id: ChainId, - ) -> BlockIdentifier { - BlockIdentifier { - index: block.block_height, - hash: BlockHash::new(chain_id, block.block_height).to_string(), - } - } -} - -/// Identifier for this specific network deployment -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct NetworkIdentifier { - /// Blockchain name, should always be `aptos` and be hardcoded - pub blockchain: String, - /// Network name which we use ChainId for it - pub network: String, -} - -impl NetworkIdentifier { - pub fn chain_id(&self) -> ApiResult { - self.try_into() - } -} - -impl TryFrom<&NetworkIdentifier> for ChainId { - type Error = ApiError; - - fn try_from(network_identifier: &NetworkIdentifier) -> Result { - ChainId::from_str(network_identifier.network.trim()) - .map_err(|err| ApiError::InvalidInput(Some(err.to_string()))) - } -} - -impl From for NetworkIdentifier { - fn from(chain_id: ChainId) -> Self { - NetworkIdentifier { - blockchain: BLOCKCHAIN.to_string(), - network: chain_id.to_string(), - } - } -} - -/// Identifies a specific [`crate::types::Operation`] within a `Transaction` -/// -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/OperationIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct OperationIdentifier { - /// The unique index of the operation within a transaction - /// - /// It must be 0 to n within the transaction. - pub index: u64, -} - -/// Partial block identifier for querying by version or by hash. Both should not be -/// provided at the same time. -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/PartialBlockIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct PartialBlockIdentifier { - #[serde(skip_serializing_if = "Option::is_none")] - pub index: Option, - /// Hash of the block - #[serde(skip_serializing_if = "Option::is_none")] - pub hash: Option, -} - -impl PartialBlockIdentifier { - pub fn latest() -> Self { - Self { - index: None, - hash: None, - } - } - - pub fn by_hash(hash: String) -> Self { - Self { - index: None, - hash: Some(hash), - } - } - - pub fn block_index(index: u64) -> Self { - Self { - index: Some(index), - hash: None, - } - } -} - -/// TransactionIdentifier to represent a transaction by hash -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/TransactionIdentifier.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct TransactionIdentifier { - /// The hash of the transaction so it can be looked up in mempool - pub hash: String, -} - -impl From<&TransactionInfo> for TransactionIdentifier { - fn from(txn: &TransactionInfo) -> Self { - TransactionIdentifier { - hash: to_hex_lower(&txn.transaction_hash()), - } - } -} - -impl From for TransactionIdentifier { - fn from(hash: aptos_crypto::HashValue) -> Self { - TransactionIdentifier { - hash: to_hex_lower(&hash), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_account_id() { - let account = AccountAddress::ONE; - let operator = AccountAddress::ZERO; - - let base_account = AccountIdentifier::base_account(account); - let total_stake_account = AccountIdentifier::total_stake_account(account); - let operator_stake_account = AccountIdentifier::operator_stake_account(account, operator); - let active_stake_account = AccountIdentifier::active_stake_account(account); - let pending_active_stake_account = AccountIdentifier::pending_active_stake_account(account); - let inactive_stake_account = AccountIdentifier::inactive_stake_account(account); - let pending_inactive_stake_account = - AccountIdentifier::pending_inactive_stake_account(account); - - assert!(base_account.is_base_account()); - assert!(!operator_stake_account.is_base_account()); - assert!(!total_stake_account.is_base_account()); - assert!(!active_stake_account.is_base_account()); - assert!(!pending_active_stake_account.is_base_account()); - assert!(!inactive_stake_account.is_base_account()); - assert!(!pending_inactive_stake_account.is_base_account()); - - assert!(!base_account.is_operator_stake()); - assert!(operator_stake_account.is_operator_stake()); - assert!(!total_stake_account.is_operator_stake()); - - assert!(!base_account.is_total_stake()); - assert!(!operator_stake_account.is_total_stake()); - assert!(total_stake_account.is_total_stake()); - - assert!(active_stake_account.is_active_stake()); - assert!(pending_active_stake_account.is_pending_active_stake()); - assert!(inactive_stake_account.is_inactive_stake()); - assert!(pending_inactive_stake_account.is_pending_inactive_stake()); - - assert_eq!(Ok(account), base_account.account_address()); - assert_eq!(Ok(account), operator_stake_account.account_address()); - assert_eq!(Ok(account), total_stake_account.account_address()); - assert_eq!(Ok(account), active_stake_account.account_address()); - assert_eq!(Ok(account), pending_active_stake_account.account_address()); - assert_eq!(Ok(account), inactive_stake_account.account_address()); - assert_eq!( - Ok(account), - pending_inactive_stake_account.account_address() - ); - - assert!(base_account.operator_address().is_err()); - assert_eq!(Ok(operator), operator_stake_account.operator_address()); - assert!(total_stake_account.operator_address().is_err()); - } - - #[test] - fn test_sub_account_id() { - let stake = SubAccountIdentifier::new_total_stake(); - assert!(stake.is_total_stake()); - - let operator_address = AccountAddress::ZERO; - let operator = SubAccountIdentifier::new_operator_stake(operator_address); - assert!(!operator.is_total_stake()); - assert_eq!(Ok(operator_address), operator.operator_address()); - - assert!(stake.operator_address().is_err()); - } -} diff --git a/crates/aptos-rosetta/src/types/misc.rs b/crates/aptos-rosetta/src/types/misc.rs deleted file mode 100644 index f97c3ed6ceb5b..0000000000000 --- a/crates/aptos-rosetta/src/types/misc.rs +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::native_coin, - error::ApiError, - types::{AccountIdentifier, Amount}, - AccountAddress, ApiResult, -}; -use aptos_rest_client::aptos_api_types::{EntryFunctionId, ViewRequest}; -use aptos_types::stake_pool::StakePool; -use once_cell::sync::Lazy; -use serde::{Deserialize, Serialize}; -use std::{ - convert::TryFrom, - fmt::{Display, Formatter}, - str::FromStr, -}; - -static DELEGATION_POOL_GET_STAKE_FUNCTION: Lazy = - Lazy::new(|| "0x1::delegation_pool::get_stake".parse().unwrap()); -static STAKE_GET_LOCKUP_SECS_FUNCTION: Lazy = - Lazy::new(|| "0x1::stake::get_lockup_secs".parse().unwrap()); - -/// Errors that can be returned by the API -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Error.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Error { - /// Error code - pub code: u32, - /// Message that always matches the error code - pub message: String, - /// Whether a call can retry on the error - pub retriable: bool, - /// Specific details of the error e.g. stack trace - #[serde(skip_serializing_if = "Option::is_none")] - pub details: Option, -} - -/// Error details that are specific to the instance -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ErrorDetails { - /// Related error details - pub details: String, -} - -/// Status of an operation -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/OperationStatus.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct OperationStatus { - pub status: String, - pub successful: bool, -} - -/// Represents a Peer, used for discovery -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Peer.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Peer { - peer_id: String, -} - -/// [API Spec](https://www.rosetta-api.org/docs/models/SyncStatus.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SyncStatus { - #[serde(skip_serializing_if = "Option::is_none")] - current_index: Option, - #[serde(skip_serializing_if = "Option::is_none")] - target_index: Option, - #[serde(skip_serializing_if = "Option::is_none")] - stage: Option, - synced: bool, -} - -/// Version information for the current deployment to handle software version matching -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Version.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Version { - /// Rosetta version, this should be hardcoded - pub rosetta_version: String, - /// Node version, this should come from the node - pub node_version: String, - /// Middleware version, this should be the version of this software - pub middleware_version: String, -} - -/// Represents the result of the balance retrieval -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BalanceResult { - pub balance: Option, - /// Time at which the lockup expires and pending_inactive balance becomes inactive - pub lockup_expiration: u64, -} - -/// An internal enum to support Operation typing -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub enum OperationType { - // Create must always be first for ordering - CreateAccount, - // Withdraw must come before deposit - Withdraw, - Deposit, - StakingReward, - SetOperator, - SetVoter, - InitializeStakePool, - ResetLockup, - UnlockStake, - UpdateCommission, - WithdrawUndelegatedFunds, - DistributeStakingRewards, - AddDelegatedStake, - UnlockDelegatedStake, - // Fee must always be last for ordering - Fee, -} - -impl OperationType { - const ADD_DELEGATED_STAKE: &'static str = "add_delegated_stake"; - const CREATE_ACCOUNT: &'static str = "create_account"; - const DEPOSIT: &'static str = "deposit"; - const DISTRIBUTE_STAKING_REWARDS: &'static str = "distribute_staking_rewards"; - const FEE: &'static str = "fee"; - const INITIALIZE_STAKE_POOL: &'static str = "initialize_stake_pool"; - const RESET_LOCKUP: &'static str = "reset_lockup"; - const SET_OPERATOR: &'static str = "set_operator"; - const SET_VOTER: &'static str = "set_voter"; - const STAKING_REWARD: &'static str = "staking_reward"; - const UNLOCK_DELEGATED_STAKE: &'static str = "unlock_delegated_stake"; - const UNLOCK_STAKE: &'static str = "unlock_stake"; - const UPDATE_COMMISSION: &'static str = "update_commission"; - const WITHDRAW: &'static str = "withdraw"; - const WITHDRAW_UNDELEGATED_FUNDS: &'static str = "withdraw_undelegated_funds"; - - pub fn all() -> Vec { - use OperationType::*; - vec![ - CreateAccount, - Withdraw, - Deposit, - Fee, - SetOperator, - SetVoter, - StakingReward, - InitializeStakePool, - ResetLockup, - UnlockStake, - WithdrawUndelegatedFunds, - DistributeStakingRewards, - AddDelegatedStake, - UnlockDelegatedStake, - ] - } -} - -impl FromStr for OperationType { - type Err = ApiError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - Self::CREATE_ACCOUNT => Ok(OperationType::CreateAccount), - Self::DEPOSIT => Ok(OperationType::Deposit), - Self::WITHDRAW => Ok(OperationType::Withdraw), - Self::FEE => Ok(OperationType::Fee), - Self::STAKING_REWARD => Ok(OperationType::StakingReward), - Self::SET_OPERATOR => Ok(OperationType::SetOperator), - Self::SET_VOTER => Ok(OperationType::SetVoter), - Self::INITIALIZE_STAKE_POOL => Ok(OperationType::InitializeStakePool), - Self::RESET_LOCKUP => Ok(OperationType::ResetLockup), - Self::UNLOCK_STAKE => Ok(OperationType::UnlockStake), - Self::UPDATE_COMMISSION => Ok(OperationType::UpdateCommission), - Self::DISTRIBUTE_STAKING_REWARDS => Ok(OperationType::DistributeStakingRewards), - Self::ADD_DELEGATED_STAKE => Ok(OperationType::AddDelegatedStake), - Self::UNLOCK_DELEGATED_STAKE => Ok(OperationType::UnlockDelegatedStake), - Self::WITHDRAW_UNDELEGATED_FUNDS => Ok(OperationType::WithdrawUndelegatedFunds), - _ => Err(ApiError::DeserializationFailed(Some(format!( - "Invalid OperationType: {}", - s - )))), - } - } -} - -impl Display for OperationType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use OperationType::*; - f.write_str(match self { - CreateAccount => Self::CREATE_ACCOUNT, - Deposit => Self::DEPOSIT, - Withdraw => Self::WITHDRAW, - StakingReward => Self::STAKING_REWARD, - SetOperator => Self::SET_OPERATOR, - SetVoter => Self::SET_VOTER, - InitializeStakePool => Self::INITIALIZE_STAKE_POOL, - ResetLockup => Self::RESET_LOCKUP, - UnlockStake => Self::UNLOCK_STAKE, - UpdateCommission => Self::UPDATE_COMMISSION, - DistributeStakingRewards => Self::DISTRIBUTE_STAKING_REWARDS, - AddDelegatedStake => Self::ADD_DELEGATED_STAKE, - UnlockDelegatedStake => Self::UNLOCK_DELEGATED_STAKE, - WithdrawUndelegatedFunds => Self::WITHDRAW_UNDELEGATED_FUNDS, - Fee => Self::FEE, - }) - } -} - -/// An internal type to support typing of Operation statuses -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub enum OperationStatusType { - /// Operation was part of a successfully committed transaction - Success, - /// Operation was not part of a successfully committed transaction - Failure, -} - -impl OperationStatusType { - const FAILURE: &'static str = "failure"; - const SUCCESS: &'static str = "success"; - - pub fn all() -> Vec { - vec![OperationStatusType::Success, OperationStatusType::Failure] - } -} - -impl From for OperationStatus { - fn from(status: OperationStatusType) -> Self { - let successful = match status { - OperationStatusType::Success => true, - OperationStatusType::Failure => false, - }; - - OperationStatus { - status: status.to_string(), - successful, - } - } -} - -impl TryFrom for OperationStatusType { - type Error = ApiError; - - fn try_from(status: OperationStatus) -> Result { - OperationStatusType::from_str(&status.status) - } -} - -impl FromStr for OperationStatusType { - type Err = ApiError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - Self::SUCCESS => Ok(OperationStatusType::Success), - Self::FAILURE => Ok(OperationStatusType::Failure), - _ => Err(ApiError::DeserializationFailed(Some(format!( - "Invalid OperationStatusType: {}", - s - )))), - } - } -} - -impl Display for OperationStatusType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - OperationStatusType::Success => Self::SUCCESS, - OperationStatusType::Failure => Self::FAILURE, - }) - } -} - -pub async fn get_stake_balances( - rest_client: &aptos_rest_client::Client, - owner_account: &AccountIdentifier, - pool_address: AccountAddress, - version: u64, -) -> ApiResult> { - const STAKE_POOL: &str = "0x1::stake::StakePool"; - if let Ok(response) = rest_client - .get_account_resource_at_version_bcs::(pool_address, STAKE_POOL, version) - .await - { - let stake_pool = response.into_inner(); - - // Stake isn't allowed for base accounts - if owner_account.is_base_account() { - return Err(ApiError::InvalidInput(Some( - "Stake pool not supported for base account".to_string(), - ))); - } - - // If the operator address is different, skip - if owner_account.is_operator_stake() - && owner_account.operator_address()? != stake_pool.operator_address - { - return Err(ApiError::InvalidInput(Some( - "Stake pool not for matching operator".to_string(), - ))); - } - - // Any stake pools that match, retrieve that. - let mut requested_balance: Option = None; - let lockup_expiration = stake_pool.locked_until_secs; - - if owner_account.is_active_stake() { - requested_balance = Some(stake_pool.active.to_string()); - } else if owner_account.is_pending_active_stake() { - requested_balance = Some(stake_pool.pending_active.to_string()); - } else if owner_account.is_inactive_stake() { - requested_balance = Some(stake_pool.inactive.to_string()); - } else if owner_account.is_pending_inactive_stake() { - requested_balance = Some(stake_pool.pending_inactive.to_string()); - } else if owner_account.is_total_stake() { - requested_balance = Some(stake_pool.get_total_staked_amount().to_string()); - } - - if let Some(balance) = requested_balance { - Ok(Some(BalanceResult { - balance: Some(Amount { - value: balance, - currency: native_coin(), - }), - lockup_expiration, - })) - } else { - Ok(None) - } - } else { - Ok(None) - } -} - -pub async fn get_delegation_stake_balances( - rest_client: &aptos_rest_client::Client, - account_identifier: &AccountIdentifier, - owner_address: AccountAddress, - pool_address: AccountAddress, - version: u64, -) -> ApiResult> { - // get requested_balance - let balances_response = rest_client - .view( - &ViewRequest { - function: DELEGATION_POOL_GET_STAKE_FUNCTION.clone(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(pool_address.to_string()), - serde_json::Value::String(owner_address.to_string()), - ], - }, - Some(version), - ) - .await?; - - let requested_balance = - parse_requested_balance(account_identifier, balances_response.into_inner()); - - // get lockup_secs - let lockup_secs_response = rest_client - .view( - &ViewRequest { - function: STAKE_GET_LOCKUP_SECS_FUNCTION.clone(), - type_arguments: vec![], - arguments: vec![serde_json::Value::String(pool_address.to_string())], - }, - Some(version), - ) - .await?; - let lockup_expiration = parse_lockup_expiration(lockup_secs_response.into_inner()); - - if let Some(balance) = requested_balance { - Ok(Some(BalanceResult { - balance: Some(Amount { - value: balance, - currency: native_coin(), - }), - lockup_expiration, - })) - } else { - Err(ApiError::InternalError(Some( - "Unable to construct BalanceResult instance".to_string(), - ))) - } -} - -fn parse_requested_balance( - account_identifier: &AccountIdentifier, - balances_result: Vec, -) -> Option { - if account_identifier.is_delegator_active_stake() { - return balances_result - .first() - .and_then(|v| v.as_str().map(|s| s.to_owned())); - } else if account_identifier.is_delegator_inactive_stake() { - return balances_result - .get(1) - .and_then(|v| v.as_str().map(|s| s.to_owned())); - } else if account_identifier.is_delegator_pending_inactive_stake() { - return balances_result - .get(2) - .and_then(|v| v.as_str().map(|s| s.to_owned())); - } else if account_identifier.is_total_stake() { - return Some( - balances_result - .iter() - .map(|v| { - v.as_str() - .map(|s| s.to_owned()) - .and_then(|s| s.parse::().ok()) - .unwrap_or(0) - }) - .sum::() - .to_string(), - ); - } - - None -} - -fn parse_lockup_expiration(lockup_secs_result: Vec) -> u64 { - return lockup_secs_result - .first() - .and_then(|v| v.as_str().and_then(|s| s.parse::().ok())) - .unwrap_or(0); -} - -#[cfg(test)] -mod test { - use super::*; - use crate::types::SubAccountIdentifier; - - #[test] - fn test_parse_requested_balance() { - let balances_result = vec![ - serde_json::Value::String("300".to_string()), - serde_json::Value::String("200".to_string()), - serde_json::Value::String("100".to_string()), - ]; - - // Total stake balance is sum of all 3 - assert_eq!( - Some("600".to_string()), - parse_requested_balance( - &AccountIdentifier { - address: "0x123".to_string(), - sub_account: Some(SubAccountIdentifier::new_delegated_total_stake("0xabc")), - }, - balances_result.clone() - ) - ); - - assert_eq!( - Some("300".to_string()), - parse_requested_balance( - &AccountIdentifier { - address: "0x123".to_string(), - sub_account: Some(SubAccountIdentifier::new_delegated_active_stake("0xabc")), - }, - balances_result.clone() - ) - ); - - assert_eq!( - Some("200".to_string()), - parse_requested_balance( - &AccountIdentifier { - address: "0x123".to_string(), - sub_account: Some(SubAccountIdentifier::new_delegated_inactive_stake("0xabc")), - }, - balances_result.clone() - ) - ); - - assert_eq!( - Some("100".to_string()), - parse_requested_balance( - &AccountIdentifier { - address: "0x123".to_string(), - sub_account: Some(SubAccountIdentifier::new_delegated_pending_inactive_stake( - "0xabc" - )), - }, - balances_result.clone() - ) - ); - - assert_eq!( - None, - parse_requested_balance( - &AccountIdentifier { - address: "0x123".to_string(), - sub_account: Some(SubAccountIdentifier::new_active_stake()), - }, - balances_result - ) - ); - } - - #[test] - fn test_parse_lockup_expiration() { - let lockup_secs_result = vec![serde_json::Value::String("123456".to_string())]; - assert_eq!(123456, parse_lockup_expiration(lockup_secs_result)); - } -} diff --git a/crates/aptos-rosetta/src/types/mod.rs b/crates/aptos-rosetta/src/types/mod.rs deleted file mode 100644 index 6d644695c34e3..0000000000000 --- a/crates/aptos-rosetta/src/types/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod identifiers; -mod misc; -mod move_types; -mod objects; -mod requests; - -pub use identifiers::*; -pub use misc::*; -pub use move_types::*; -pub use objects::*; -pub use requests::*; diff --git a/crates/aptos-rosetta/src/types/move_types.rs b/crates/aptos-rosetta/src/types/move_types.rs deleted file mode 100644 index 1701e5ceaa295..0000000000000 --- a/crates/aptos-rosetta/src/types/move_types.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Types and identifiers for parsing Move pub structs and types - -use crate::AccountAddress; -use aptos_types::event::EventHandle; -use serde::{Deserialize, Serialize}; - -pub const ACCOUNT_MODULE: &str = "account"; -pub const APTOS_ACCOUNT_MODULE: &str = "aptos_account"; -pub const APTOS_COIN_MODULE: &str = "aptos_coin"; -pub const COIN_MODULE: &str = "coin"; -pub const STAKE_MODULE: &str = "stake"; -pub const STAKING_PROXY_MODULE: &str = "staking_proxy"; -pub const STAKING_CONTRACT_MODULE: &str = "staking_contract"; -pub const VESTING_MODULE: &str = "vesting"; -pub const DELEGATION_POOL_MODULE: &str = "delegation_pool"; - -pub const ACCOUNT_RESOURCE: &str = "Account"; -pub const APTOS_COIN_RESOURCE: &str = "AptosCoin"; -pub const COIN_INFO_RESOURCE: &str = "CoinInfo"; -pub const COIN_STORE_RESOURCE: &str = "CoinStore"; -pub const STAKE_POOL_RESOURCE: &str = "StakePool"; -pub const STAKING_CONTRACT_RESOURCE: &str = "StakingContract"; -pub const STORE_RESOURCE: &str = "Store"; -pub const STAKING_GROUP_UPDATE_COMMISSION_RESOURCE: &str = "StakingGroupUpdateCommissionEvent"; -pub const VESTING_RESOURCE: &str = "Vesting"; -pub const DELEGATION_POOL_RESOURCE: &str = "DelegationPool"; -pub const WITHDRAW_STAKE_EVENT: &str = "WithdrawStakeEvent"; - -pub const CREATE_ACCOUNT_FUNCTION: &str = "create_account"; -pub const TRANSFER_FUNCTION: &str = "transfer"; - -// Staking Contract -pub const RESET_LOCKUP_FUNCTION: &str = "reset_lockup"; -pub const CREATE_STAKING_CONTRACT_FUNCTION: &str = "create_staking_contract"; -pub const SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION: &str = - "switch_operator_with_same_commission"; -pub const UPDATE_VOTER_FUNCTION: &str = "update_voter"; -pub const UNLOCK_STAKE_FUNCTION: &str = "unlock_stake"; -// TODO fix the typo in function name. commision -> commission -pub const UPDATE_COMMISSION_FUNCTION: &str = "update_commision"; -pub const DISTRIBUTE_STAKING_REWARDS_FUNCTION: &str = "distribute"; - -// Delegation Pool Contract -pub const DELEGATION_POOL_ADD_STAKE_FUNCTION: &str = "add_stake"; -pub const DELEGATION_POOL_UNLOCK_FUNCTION: &str = "unlock"; -pub const DELEGATION_POOL_WITHDRAW_FUNCTION: &str = "withdraw"; - -pub const DECIMALS_FIELD: &str = "decimal"; -pub const DEPOSIT_EVENTS_FIELD: &str = "deposit_events"; -pub const WITHDRAW_EVENTS_FIELD: &str = "withdraw_events"; -pub const SET_OPERATOR_EVENTS_FIELD: &str = "set_operator_events"; -pub const SEQUENCE_NUMBER_FIELD: &str = "sequence_number"; -pub const SYMBOL_FIELD: &str = "symbol"; - -// Staking Contract -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct StakingContract { - pub principal: u64, - pub pool_address: AccountAddress, - pub owner_cap: Capability, - pub commission_percentage: u64, - pub distribution_pool: Pool, - pub signer_cap: Capability, -} - -impl StakingContract { - pub fn get_balance(&self, account_address: &AccountAddress) -> Option { - self.distribution_pool.get_balance(account_address) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Store { - pub staking_contracts: Vec<(AccountAddress, StakingContract)>, - pub create_staking_contract_events: EventHandle, - pub update_voter_events: EventHandle, - pub reset_lockup_events: EventHandle, - pub add_stake_events: EventHandle, - pub request_commission_events: EventHandle, - pub unlock_stake_events: EventHandle, - pub switch_operator_events: EventHandle, - pub add_distribution_events: EventHandle, - pub distribute_events: EventHandle, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct StakingGroupUpdateCommissionEvent { - pub update_commission_events: EventHandle, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UpdateCommissionEvent { - pub staker: AccountAddress, - pub operator: AccountAddress, - pub old_commission_percentage: u64, - pub new_commission_percentage: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct CreateStakingContractEvent { - pub operator: AccountAddress, - pub voter: AccountAddress, - pub pool_address: AccountAddress, - pub principal: u64, - pub commission_percentage: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UpdateVoterEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub old_voter: AccountAddress, - pub new_voter: AccountAddress, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ResetLockupEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AddStakeEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub amount: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RequestCommissionEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub accumulated_rewards: u64, - pub commission_amount: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UnlockStakeEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub amount: u64, - pub commission_paid: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct SwitchOperatorEvent { - pub old_operator: AccountAddress, - pub new_operator: AccountAddress, - pub pool_address: AccountAddress, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AddDistributionEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub amount: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct DistributeEvent { - pub operator: AccountAddress, - pub pool_address: AccountAddress, - pub recipient: AccountAddress, - pub amount: u64, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Pool { - pub shareholders_limit: u64, - pub total_coins: u64, - pub total_shares: u64, - pub shares: Vec<(AccountAddress, u64)>, - pub shareholders: Vec, - pub scaling_factor: u64, -} - -impl Pool { - pub fn get_balance(&self, account_address: &AccountAddress) -> Option { - self.shares - .iter() - .find(|(address, _)| address == account_address) - .map(|(_, shares)| (*shares * self.total_coins) / self.total_shares) - } -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub struct Capability { - pub pool_address: AccountAddress, -} - -// Delegation Pool Contract -#[derive(Debug, Serialize, Deserialize)] -pub struct SharesPool { - pub shareholders_limit: u64, - pub total_coins: u64, - pub total_shares: u64, - pub shares: Vec<(AccountAddress, u64)>, - pub shareholders: Vec, - pub scaling_factor: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ObservedLockupCycle { - pub index: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct DelegationPool { - pub active_shares: SharesPool, - pub observed_lockup_cycle: ObservedLockupCycle, - pub inactive_shares: Vec<(ObservedLockupCycle, SharesPool)>, - pub pending_withdrawals: Vec<(AccountAddress, ObservedLockupCycle)>, - pub stake_pool_signer_cap: Capability, - pub total_coins_inactive: u64, - pub operator_commission_percentage: u64, - - pub add_stake_events: EventHandle, - pub reactivate_stake_events: EventHandle, - pub unlock_stake_events: EventHandle, - pub withdraw_stake_events: EventHandle, - pub distribute_commission_events: EventHandle, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AddDelegationEvent { - pub pool_address: AccountAddress, - pub delegator_address: AccountAddress, - pub amount_added: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UndelegationEvent { - pub pool_address: AccountAddress, - pub delegator_address: AccountAddress, - pub amount_unlocked: u64, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct WithdrawUndelegedEvent { - pub pool_address: AccountAddress, - pub delegator_address: AccountAddress, - pub amount_withdrawn: u64, -} diff --git a/crates/aptos-rosetta/src/types/objects.rs b/crates/aptos-rosetta/src/types/objects.rs deleted file mode 100644 index 5fb9b0e075831..0000000000000 --- a/crates/aptos-rosetta/src/types/objects.rs +++ /dev/null @@ -1,2473 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Objects of the Rosetta spec -//! -//! [Spec](https://www.rosetta-api.org/docs/api_objects.html) - -use crate::{ - common::{is_native_coin, native_coin, native_coin_tag}, - construction::{ - parse_create_stake_pool_operation, parse_delegation_pool_add_stake_operation, - parse_delegation_pool_unlock_operation, parse_delegation_pool_withdraw_operation, - parse_distribute_staking_rewards_operation, parse_reset_lockup_operation, - parse_set_operator_operation, parse_set_voter_operation, parse_unlock_stake_operation, - parse_update_commission_operation, - }, - error::ApiResult, - types::{ - move_types::*, AccountIdentifier, BlockIdentifier, Error, OperationIdentifier, - OperationStatus, OperationStatusType, OperationType, TransactionIdentifier, - }, - ApiError, RosettaContext, -}; -use anyhow::anyhow; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{ed25519::Ed25519PublicKey, ValidCryptoMaterialStringExt}; -use aptos_logger::warn; -use aptos_rest_client::aptos_api_types::{TransactionOnChainData, U64}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{AccountResource, CoinStoreResource, WithdrawEvent}, - contract_event::{ContractEvent, FEE_STATEMENT_EVENT_TYPE}, - event::EventKey, - fee_statement::FeeStatement, - stake_pool::{SetOperatorEvent, StakePool}, - state_store::state_key::{StateKey, StateKeyInner}, - transaction::{EntryFunction, TransactionPayload}, - write_set::{WriteOp, WriteSet}, -}; -use itertools::Itertools; -use move_core_types::language_storage::TypeTag; -use serde::{Deserialize, Serialize}; -use std::{ - cmp::Ordering, - collections::{BTreeMap, HashMap}, - convert::TryFrom, - fmt::{Display, Formatter}, - hash::Hash, - str::FromStr, -}; - -/// A description of all types used by the Rosetta implementation. -/// -/// This is used to verify correctness of the implementation and to check things like -/// operation names, and error names. -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Allow.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Allow { - /// List of all possible operation statuses - pub operation_statuses: Vec, - /// List of all possible writeset types - pub operation_types: Vec, - /// List of all possible errors - pub errors: Vec, - /// If the server is allowed to lookup historical transactions - pub historical_balance_lookup: bool, - /// All times after this are valid timestamps - pub timestamp_start_index: u64, - /// All call methods supported - pub call_methods: Vec, - /// A list of balance exemptions. These should be as minimal as possible, otherwise it becomes - /// more complicated for users - pub balance_exemptions: Vec, - /// Determines if mempool can change the balance on an account - /// This should be set to false - pub mempool_coins: bool, -} - -/// Amount of a [`Currency`] in atomic units -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Amount.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Amount { - /// Value of transaction as a String representation of an integer - pub value: String, - /// [`Currency`] - pub currency: Currency, -} - -impl Amount { - pub fn suggested_gas_fee(gas_unit_price: u64, max_gas_amount: u64) -> Amount { - Amount { - value: (gas_unit_price * max_gas_amount).to_string(), - currency: native_coin(), - } - } - - pub fn value(&self) -> ApiResult { - i128::from_str(&self.value) - .map_err(|_| ApiError::InvalidTransferOperations(Some("Withdraw amount is invalid"))) - } -} - -/// [API Spec](https://www.rosetta-api.org/docs/models/BalanceExemption.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BalanceExemption {} - -/// Representation of a Block for a blockchain. For aptos it is the version -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Block.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Block { - /// Block identifier of the current block - pub block_identifier: BlockIdentifier, - /// Block identifier of the previous block - pub parent_block_identifier: BlockIdentifier, - /// Timestamp in milliseconds to the block from the UNIX_EPOCH - pub timestamp: u64, - /// Transactions associated with the version. In aptos there should only be one transaction - pub transactions: Vec, -} - -/// A combination of a transaction and the block associated. In Aptos, this is just the same -/// as the version associated with the transaction -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/BlockTransaction.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BlockTransaction { - /// Block associated with transaction - block_identifier: BlockIdentifier, - /// Transaction associated with block - transaction: Transaction, -} - -/// Currency represented as atomic units including decimals -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Currency.html) -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Currency { - /// Symbol of currency - pub symbol: String, - /// Number of decimals to be considered in the currency - pub decimals: u8, - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct CurrencyMetadata { - pub move_type: String, -} - -/// Various signing curves supported by Rosetta. We only use [`CurveType::Edwards25519`] -/// [API Spec](https://www.rosetta-api.org/docs/models/CurveType.html) -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum CurveType { - Edwards25519, -} - -/// A representation of a single account change in a transaction -/// -/// This is known as a write set change within Aptos -/// [API Spec](https://www.rosetta-api.org/docs/models/Operation.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Operation { - /// Identifier of an operation within a transaction - pub operation_identifier: OperationIdentifier, - /// Type of operation - #[serde(rename = "type")] - pub operation_type: String, - /// Status of operation. Must be populated if the transaction is in the past. If submitting - /// new transactions, it must NOT be populated. - #[serde(skip_serializing_if = "Option::is_none")] - pub status: Option, - /// AccountIdentifier should be provided to point at which account the change is - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - /// Amount in the operation - #[serde(skip_serializing_if = "Option::is_none")] - pub amount: Option, - /// Operation specific metadata for any operation that's missing information it needs - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -impl Operation { - fn new( - operation_type: OperationType, - operation_index: u64, - status: Option, - account: AccountIdentifier, - amount: Option, - metadata: Option, - ) -> Operation { - Operation { - operation_identifier: OperationIdentifier { - index: operation_index, - }, - operation_type: operation_type.to_string(), - status: status.map(|inner| inner.to_string()), - account: Some(account), - amount, - metadata, - } - } - - pub fn create_stake_pool( - operation_index: u64, - status: Option, - owner: AccountAddress, - operator: Option, - voter: Option, - staked_balance: Option, - commission_percentage: Option, - ) -> Operation { - Operation::new( - OperationType::InitializeStakePool, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::create_stake_pool( - operator.map(AccountIdentifier::base_account), - voter.map(AccountIdentifier::base_account), - staked_balance, - commission_percentage, - )), - ) - } - - pub fn create_account( - operation_index: u64, - status: Option, - address: AccountAddress, - sender: AccountAddress, - ) -> Operation { - Operation::new( - OperationType::CreateAccount, - operation_index, - status, - AccountIdentifier::base_account(address), - None, - Some(OperationMetadata::create_account(sender)), - ) - } - - pub fn staking_reward( - operation_index: u64, - status: Option, - account: AccountIdentifier, - currency: Currency, - amount: u64, - ) -> Operation { - Operation::new( - OperationType::StakingReward, - operation_index, - status, - account, - Some(Amount { - value: amount.to_string(), - currency, - }), - None, - ) - } - - pub fn deposit( - operation_index: u64, - status: Option, - account: AccountIdentifier, - currency: Currency, - amount: u64, - ) -> Operation { - Operation::new( - OperationType::Deposit, - operation_index, - status, - account, - Some(Amount { - value: amount.to_string(), - currency, - }), - None, - ) - } - - pub fn withdraw( - operation_index: u64, - status: Option, - account: AccountIdentifier, - currency: Currency, - amount: u64, - ) -> Operation { - Operation::new( - OperationType::Withdraw, - operation_index, - status, - account, - Some(Amount { - value: format!("-{}", amount), - currency, - }), - None, - ) - } - - pub fn gas_fee( - operation_index: u64, - address: AccountAddress, - gas_used: u64, - gas_price_per_unit: u64, - ) -> Operation { - Operation::new( - OperationType::Fee, - operation_index, - Some(OperationStatusType::Success), - AccountIdentifier::base_account(address), - Some(Amount { - value: format!("-{}", gas_used.saturating_mul(gas_price_per_unit)), - currency: native_coin(), - }), - None, - ) - } - - pub fn set_operator( - operation_index: u64, - status: Option, - owner: AccountAddress, - old_operator: Option, - new_operator: AccountIdentifier, - staked_balance: Option, - ) -> Operation { - Operation::new( - OperationType::SetOperator, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::set_operator( - old_operator, - new_operator, - staked_balance, - )), - ) - } - - pub fn set_voter( - operation_index: u64, - status: Option, - owner: AccountAddress, - operator: Option, - new_voter: AccountIdentifier, - ) -> Operation { - Operation::new( - OperationType::SetVoter, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::set_voter(operator, new_voter)), - ) - } - - pub fn reset_lockup( - operation_index: u64, - status: Option, - owner: AccountAddress, - operator: Option, - ) -> Operation { - Operation::new( - OperationType::ResetLockup, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::reset_lockup(operator)), - ) - } - - pub fn unlock_stake( - operation_index: u64, - status: Option, - owner: AccountAddress, - operator: Option, - amount: Option, - ) -> Operation { - Operation::new( - OperationType::UnlockStake, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::unlock_stake(operator, amount)), - ) - } - - pub fn update_commission( - operation_index: u64, - status: Option, - owner: AccountAddress, - operator: Option, - new_commission_percentage: Option, - ) -> Operation { - Operation::new( - OperationType::UpdateCommission, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::update_commission( - operator, - new_commission_percentage, - )), - ) - } - - pub fn distribute_staking_rewards( - operation_index: u64, - status: Option, - account: AccountAddress, - operator: AccountIdentifier, - staker: AccountIdentifier, - ) -> Operation { - Operation::new( - OperationType::DistributeStakingRewards, - operation_index, - status, - AccountIdentifier::base_account(account), - None, - Some(OperationMetadata::distribute_staking_rewards( - operator, staker, - )), - ) - } - - pub fn account(&self) -> Option { - self.account - .as_ref() - .and_then(|inner| inner.account_address().ok()) - } - - pub fn currency(&self) -> Option<&Currency> { - self.amount.as_ref().map(|inner| &inner.currency) - } - - pub fn amount(&self) -> Option { - self.amount.as_ref().and_then(|inner| inner.value().ok()) - } - - pub fn status(&self) -> Option { - self.status - .as_ref() - .and_then(|inner| OperationStatusType::from_str(inner).ok()) - } - - pub fn operation_type(&self) -> Option { - OperationType::from_str(&self.operation_type).ok() - } - - pub fn operator(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .operator - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn old_operator(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .old_operator - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn new_operator(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .new_operator - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn sender(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .sender - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn staker(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .staker - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn new_voter(&self) -> Option { - self.metadata.as_ref().and_then(|inner| { - inner - .new_voter - .as_ref() - .and_then(|inner| inner.account_address().ok()) - }) - } - - pub fn metadata_amount(&self) -> Option { - self.metadata - .as_ref() - .and_then(|inner| inner.amount.map(|inner| inner.0)) - } - - pub fn staked_balance(&self) -> Option { - self.metadata - .as_ref() - .and_then(|inner| inner.staked_balance.map(|inner| inner.0)) - } - - pub fn commission_percentage(&self) -> Option { - self.metadata - .as_ref() - .and_then(|inner| inner.commission_percentage.map(|inner| inner.0)) - } - - pub fn add_delegated_stake( - operation_index: u64, - status: Option, - delegator: AccountAddress, - pool_address: AccountIdentifier, - amount: Option, - ) -> Operation { - Operation::new( - OperationType::AddDelegatedStake, - operation_index, - status, - AccountIdentifier::base_account(delegator), - None, - Some(OperationMetadata::add_delegated_stake(pool_address, amount)), - ) - } - - pub fn unlock_delegated_stake( - operation_index: u64, - status: Option, - delegator: AccountAddress, - pool_address: AccountIdentifier, - amount: Option, - ) -> Operation { - Operation::new( - OperationType::UnlockDelegatedStake, - operation_index, - status, - AccountIdentifier::base_account(delegator), - None, - Some(OperationMetadata::unlock_delegated_stake( - pool_address, - amount, - )), - ) - } - - pub fn withdraw_undelegated_stake( - operation_index: u64, - status: Option, - owner: AccountAddress, - pool_address: AccountIdentifier, - amount: Option, - ) -> Operation { - Operation::new( - OperationType::WithdrawUndelegatedFunds, - operation_index, - status, - AccountIdentifier::base_account(owner), - None, - Some(OperationMetadata::withdraw_undelegated_stake( - pool_address, - amount, - )), - ) - } -} - -impl std::cmp::PartialOrd for Operation { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl std::cmp::Ord for Operation { - fn cmp(&self, other: &Self) -> Ordering { - let self_op = OperationType::from_str(&self.operation_type).ok(); - let other_op = OperationType::from_str(&other.operation_type).ok(); - match (self_op, other_op) { - (Some(self_op), Some(other_op)) => { - match self_op.cmp(&other_op) { - // Keep the order stable if there's a difference - Ordering::Equal => self - .operation_identifier - .index - .cmp(&other.operation_identifier.index), - order => order, - } - }, - (Some(_), None) => Ordering::Less, - (None, Some(_)) => Ordering::Greater, - (None, None) => Ordering::Equal, - } - } -} - -/// This object is needed for flattening all the types into a -/// single json object used by Rosetta -#[derive(Clone, Default, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct OperationMetadata { - #[serde(skip_serializing_if = "Option::is_none")] - pub sender: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub operator: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub old_operator: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub new_operator: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub new_voter: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub staked_balance: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub commission_percentage: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub amount: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub staker: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub pool_address: Option, -} - -impl OperationMetadata { - pub fn create_account(sender: AccountAddress) -> Self { - OperationMetadata { - sender: Some(AccountIdentifier::base_account(sender)), - ..Default::default() - } - } - - pub fn set_operator( - old_operator: Option, - new_operator: AccountIdentifier, - staked_balance: Option, - ) -> Self { - OperationMetadata { - old_operator, - new_operator: Some(new_operator), - staked_balance: staked_balance.map(U64::from), - ..Default::default() - } - } - - pub fn set_voter(operator: Option, new_voter: AccountIdentifier) -> Self { - OperationMetadata { - operator, - new_voter: Some(new_voter), - ..Default::default() - } - } - - pub fn create_stake_pool( - new_operator: Option, - new_voter: Option, - staked_balance: Option, - commission_percentage: Option, - ) -> Self { - OperationMetadata { - new_operator, - new_voter, - staked_balance: staked_balance.map(U64::from), - commission_percentage: commission_percentage.map(U64::from), - ..Default::default() - } - } - - pub fn reset_lockup(operator: Option) -> Self { - OperationMetadata { - operator, - ..Default::default() - } - } - - pub fn unlock_stake(operator: Option, amount: Option) -> Self { - OperationMetadata { - operator, - amount: amount.map(U64::from), - ..Default::default() - } - } - - pub fn update_commission( - operator: Option, - new_commission_percentage: Option, - ) -> Self { - OperationMetadata { - operator, - commission_percentage: new_commission_percentage.map(U64::from), - ..Default::default() - } - } - - pub fn distribute_staking_rewards( - operator: AccountIdentifier, - staker: AccountIdentifier, - ) -> Self { - OperationMetadata { - operator: Some(operator), - staker: Some(staker), - ..Default::default() - } - } - - pub fn add_delegated_stake(pool_address: AccountIdentifier, amount: Option) -> Self { - OperationMetadata { - pool_address: Some(pool_address), - amount: amount.map(U64::from), - ..Default::default() - } - } - - pub fn unlock_delegated_stake(pool_address: AccountIdentifier, amount: Option) -> Self { - OperationMetadata { - pool_address: Some(pool_address), - amount: amount.map(U64::from), - ..Default::default() - } - } - - pub fn withdraw_undelegated_stake( - pool_address: AccountIdentifier, - amount: Option, - ) -> Self { - OperationMetadata { - pool_address: Some(pool_address), - amount: amount.map(U64::from), - ..Default::default() - } - } -} - -/// Public key used for the rosetta implementation. All private keys will never be handled -/// in the Rosetta implementation. -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/PublicKey.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct PublicKey { - /// Hex encoded public key bytes - pub hex_bytes: String, - /// Curve type associated with the key - pub curve_type: CurveType, -} - -impl TryFrom for PublicKey { - type Error = anyhow::Error; - - fn try_from(public_key: Ed25519PublicKey) -> Result { - Ok(PublicKey { - hex_bytes: public_key.to_encoded_string()?, - curve_type: CurveType::Edwards25519, - }) - } -} - -impl TryFrom for Ed25519PublicKey { - type Error = anyhow::Error; - - fn try_from(public_key: PublicKey) -> Result { - if public_key.curve_type != CurveType::Edwards25519 { - return Err(anyhow!("Invalid curve type")); - } - - Ok(Ed25519PublicKey::from_encoded_string( - &public_key.hex_bytes, - )?) - } -} - -/// Signature containing the signed payload and the encoded signed payload -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Signature.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Signature { - /// Payload to be signed - pub signing_payload: SigningPayload, - /// Public key related to the signature - pub public_key: PublicKey, - /// Cryptographic signature type - pub signature_type: SignatureType, - /// Hex bytes of the signature - pub hex_bytes: String, -} - -/// Cryptographic signature type used for signing transactions. Aptos only uses -/// [`SignatureType::Ed25519`] -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/SignatureType.html) -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SignatureType { - Ed25519, -} - -/// Signing payload should be signed by the client with their own private key -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/SigningPayload.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SigningPayload { - /// Account identifier of the signer - pub account_identifier: AccountIdentifier, - /// Hex encoded string of payload bytes to be signed - pub hex_bytes: String, - /// Signature type to sign with - pub signature_type: Option, -} - -/// A representation of a transaction by it's underlying operations (write set changes) -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/Transaction.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Transaction { - /// The identifying hash of the transaction - pub transaction_identifier: TransactionIdentifier, - /// Individual operations (write set changes) in a transaction - pub operations: Vec, - pub metadata: TransactionMetadata, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct TransactionMetadata { - pub transaction_type: TransactionType, - pub version: U64, - pub failed: bool, - pub vm_status: String, -} - -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub enum TransactionType { - User, - Genesis, - BlockMetadata, - BlockMetadataExt, - StateCheckpoint, - Validator, -} - -impl Display for TransactionType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use TransactionType::*; - f.write_str(match self { - User => "User", - Genesis => "Genesis", - BlockMetadata => "BlockResource", - BlockMetadataExt => "BlockResourceExt", - StateCheckpoint => "StateCheckpoint", - Validator => "Validator", - }) - } -} - -impl Transaction { - pub async fn from_transaction( - server_context: &RosettaContext, - txn: TransactionOnChainData, - ) -> ApiResult { - use aptos_types::transaction::Transaction::*; - let (txn_type, maybe_user_txn, txn_info, events) = match &txn.transaction { - UserTransaction(user_txn) => { - (TransactionType::User, Some(user_txn), txn.info, txn.events) - }, - GenesisTransaction(_) => (TransactionType::Genesis, None, txn.info, txn.events), - BlockMetadata(_) => (TransactionType::BlockMetadata, None, txn.info, txn.events), - BlockMetadataExt(_) => ( - TransactionType::BlockMetadataExt, - None, - txn.info, - txn.events, - ), - StateCheckpoint(_) => (TransactionType::StateCheckpoint, None, txn.info, vec![]), - ValidatorTransaction(_) => (TransactionType::Validator, None, txn.info, txn.events), - }; - - // Operations must be sequential and operation index must always be in the same order - // with no gaps - let successful = txn_info.status().is_success(); - let mut operations = vec![]; - let mut operation_index: u64 = 0; - if successful { - // Parse all operations from the writeset changes in a success - for (state_key, write_op) in &txn.changes { - let mut ops = parse_operations_from_write_set( - server_context, - state_key, - write_op, - &events, - maybe_user_txn.map(|inner| inner.sender()), - maybe_user_txn.map(|inner| inner.payload()), - txn.version, - operation_index, - &txn.changes, - ) - .await?; - operation_index += ops.len() as u64; - operations.append(&mut ops); - } - - // For storage fee refund - if let Some(user_txn) = maybe_user_txn { - let fee_events = get_fee_statement_from_event(&events); - for event in fee_events { - operations.push(Operation::deposit( - operation_index, - Some(OperationStatusType::Success), - AccountIdentifier::base_account(user_txn.sender()), - native_coin(), - event.storage_fee_refund(), - )); - operation_index += 1; - } - } - } else { - // Parse all failed operations from the payload - if let Some(user_txn) = maybe_user_txn { - let mut ops = parse_failed_operations_from_txn_payload( - operation_index, - user_txn.sender(), - user_txn.payload(), - ); - operation_index += ops.len() as u64; - operations.append(&mut ops); - } - }; - - // Reorder operations by type so that there's no invalid ordering - // (Create before transfer) (Withdraw before deposit) - operations.sort(); - for (i, operation) in operations.iter_mut().enumerate() { - operation.operation_identifier.index = i as u64; - } - - // Everything committed costs gas - if let Some(txn) = maybe_user_txn { - operations.push(Operation::gas_fee( - operation_index, - txn.sender(), - txn_info.gas_used(), - txn.gas_unit_price(), - )); - } - - Ok(Transaction { - transaction_identifier: (&txn_info).into(), - operations, - metadata: TransactionMetadata { - transaction_type: txn_type, - version: txn.version.into(), - failed: !successful, - vm_status: format!("{:?}", txn_info.status()), - }, - }) - } -} - -/// Parses operations from the transaction payload -/// -/// This case only occurs if the transaction failed, and that's because it's less accurate -/// than just following the state changes -fn parse_failed_operations_from_txn_payload( - operation_index: u64, - sender: AccountAddress, - payload: &TransactionPayload, -) -> Vec { - let mut operations = vec![]; - if let TransactionPayload::EntryFunction(inner) = payload { - match ( - *inner.module().address(), - inner.module().name().as_str(), - inner.function().as_str(), - ) { - (AccountAddress::ONE, COIN_MODULE, TRANSFER_FUNCTION) => { - // Only put the transfer in if we can understand the currency - if let Some(type_tag) = inner.ty_args().first() { - // We don't want to do lookups on failures for currencies that don't exist, - // so we only look up cached info not new info - // TODO: If other coins are supported, this will need to be updated to handle more coins - if type_tag == &native_coin_tag() { - operations = parse_transfer_from_txn_payload( - inner, - native_coin(), - sender, - operation_index, - ) - } - } - }, - (AccountAddress::ONE, APTOS_ACCOUNT_MODULE, TRANSFER_FUNCTION) => { - // We could add a create here as well, but we don't know if it will actually happen - operations = - parse_transfer_from_txn_payload(inner, native_coin(), sender, operation_index) - }, - (AccountAddress::ONE, ACCOUNT_MODULE, CREATE_ACCOUNT_FUNCTION) => { - if let Some(Ok(address)) = inner - .args() - .first() - .map(|encoded| bcs::from_bytes::(encoded)) - { - operations.push(Operation::create_account( - operation_index, - Some(OperationStatusType::Failure), - address, - sender, - )); - } else { - warn!("Failed to parse create account {:?}", inner); - } - }, - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION, - ) => { - if let Ok(mut ops) = - parse_set_operator_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse set operator {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UPDATE_VOTER_FUNCTION) => { - if let Ok(mut ops) = - parse_set_voter_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse set voter {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, RESET_LOCKUP_FUNCTION) => { - if let Ok(mut ops) = - parse_reset_lockup_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse reset lockup {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UPDATE_COMMISSION_FUNCTION) => { - if let Ok(mut ops) = - parse_update_commission_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse update commission {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, CREATE_STAKING_CONTRACT_FUNCTION) => { - if let Ok(mut ops) = - parse_create_stake_pool_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse create staking pool {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, UNLOCK_STAKE_FUNCTION) => { - if let Ok(mut ops) = - parse_unlock_stake_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse unlock stake {:?}", inner); - } - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, DISTRIBUTE_STAKING_REWARDS_FUNCTION) => { - if let Ok(mut ops) = parse_distribute_staking_rewards_operation( - sender, - inner.ty_args(), - inner.args(), - ) { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse distribute staking rewards {:?}", inner); - } - }, - (AccountAddress::ONE, DELEGATION_POOL_MODULE, DELEGATION_POOL_ADD_STAKE_FUNCTION) => { - if let Ok(mut ops) = - parse_delegation_pool_add_stake_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse delegation_pool::add_stake {:?}", inner); - } - }, - (AccountAddress::ONE, DELEGATION_POOL_MODULE, DELEGATION_POOL_WITHDRAW_FUNCTION) => { - if let Ok(mut ops) = - parse_delegation_pool_withdraw_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse delegation_pool::withdraw {:?}", inner); - } - }, - (AccountAddress::ONE, DELEGATION_POOL_MODULE, DELEGATION_POOL_UNLOCK_FUNCTION) => { - if let Ok(mut ops) = - parse_delegation_pool_unlock_operation(sender, inner.ty_args(), inner.args()) - { - if let Some(operation) = ops.get_mut(0) { - operation.status = Some(OperationStatusType::Failure.to_string()); - } - } else { - warn!("Failed to parse delegation_pool::unlock {:?}", inner); - } - }, - _ => { - // If we don't recognize the transaction payload, then we can't parse operations - }, - } - } - operations -} - -fn parse_transfer_from_txn_payload( - payload: &EntryFunction, - currency: Currency, - sender: AccountAddress, - operation_index: u64, -) -> Vec { - let mut operations = vec![]; - - let args = payload.args(); - let maybe_receiver = args - .first() - .map(|encoded| bcs::from_bytes::(encoded)); - let maybe_amount = args.get(1).map(|encoded| bcs::from_bytes::(encoded)); - - if let (Some(Ok(receiver)), Some(Ok(amount))) = (maybe_receiver, maybe_amount) { - operations.push(Operation::withdraw( - operation_index, - Some(OperationStatusType::Failure), - AccountIdentifier::base_account(sender), - currency.clone(), - amount, - )); - operations.push(Operation::deposit( - operation_index + 1, - Some(OperationStatusType::Failure), - AccountIdentifier::base_account(receiver), - currency, - amount, - )); - } else { - warn!( - "Failed to parse account's {} transfer {:?}", - sender, payload - ); - } - - operations -} - -/// Parses operations from the write set -/// -/// This can only be done during a successful transaction because there are actual state changes. -/// It is more accurate because untracked scripts are included in balance operations -async fn parse_operations_from_write_set( - server_context: &RosettaContext, - state_key: &StateKey, - write_op: &WriteOp, - events: &[ContractEvent], - maybe_sender: Option, - _maybe_payload: Option<&TransactionPayload>, - version: u64, - operation_index: u64, - changes: &WriteSet, -) -> ApiResult> { - let (struct_tag, address) = match state_key.inner() { - StateKeyInner::AccessPath(path) => { - if let Some(struct_tag) = path.get_struct_tag() { - (struct_tag, path.address) - } else { - return Ok(vec![]); - } - }, - _ => { - // Ignore all but access path - return Ok(vec![]); - }, - }; - - let bytes = match write_op.bytes() { - Some(bytes) => bytes, - None => return Ok(vec![]), - }; - let data = &bytes; - - // Determine operation - match ( - struct_tag.address, - struct_tag.module.as_str(), - struct_tag.name.as_str(), - struct_tag.type_params.len(), - ) { - (AccountAddress::ONE, ACCOUNT_MODULE, ACCOUNT_RESOURCE, 0) => { - parse_account_resource_changes(version, address, data, maybe_sender, operation_index) - }, - (AccountAddress::ONE, STAKE_MODULE, STAKE_POOL_RESOURCE, 0) => { - parse_stake_pool_resource_changes( - server_context, - version, - address, - data, - events, - operation_index, - ) - }, - (AccountAddress::ONE, STAKING_CONTRACT_MODULE, STORE_RESOURCE, 0) => { - parse_staking_contract_resource_changes(address, data, events, operation_index, changes) - .await - }, - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - STAKING_GROUP_UPDATE_COMMISSION_RESOURCE, - 0, - ) => parse_update_commission(address, data, events, operation_index, changes).await, - (AccountAddress::ONE, DELEGATION_POOL_MODULE, DELEGATION_POOL_RESOURCE, 0) => { - parse_delegation_pool_resource_changes(address, data, events, operation_index, changes) - .await - }, - (AccountAddress::ONE, COIN_MODULE, COIN_STORE_RESOURCE, 1) => { - if let Some(type_tag) = struct_tag.type_params.first() { - // TODO: This will need to be updated to support more coins - if type_tag == &native_coin_tag() { - parse_coinstore_changes( - native_coin(), - version, - address, - data, - events, - operation_index, - ) - .await - } else { - Ok(vec![]) - } - } else { - warn!( - "Failed to parse coinstore {} at version {}", - struct_tag, version - ); - Ok(vec![]) - } - }, - _ => { - // Any unknown type will just skip the operations - Ok(vec![]) - }, - } -} - -fn parse_account_resource_changes( - version: u64, - address: AccountAddress, - data: &[u8], - maybe_sender: Option, - operation_index: u64, -) -> ApiResult> { - // TODO: Handle key rotation - let mut operations = Vec::new(); - if let Ok(account) = bcs::from_bytes::(data) { - // Account sequence number increase (possibly creation) - // Find out if it's the 0th sequence number (creation) - if 0 == account.sequence_number() { - operations.push(Operation::create_account( - operation_index, - Some(OperationStatusType::Success), - address, - maybe_sender.unwrap_or(AccountAddress::ONE), - )); - } - } else { - warn!( - "Failed to parse AccountResource for {} at version {}", - address, version - ); - } - - Ok(operations) -} - -fn parse_stake_pool_resource_changes( - _server_context: &RosettaContext, - _version: u64, - _pool_address: AccountAddress, - _data: &[u8], - _events: &[ContractEvent], - _operation_index: u64, -) -> ApiResult> { - let operations = Vec::new(); - - // We at this point only care about balance changes from the stake pool - // TODO: Balance changes are not supported for staking at this time - /* if let Some(owner_address) = server_context.pool_address_to_owner.get(&pool_address) { - if let Ok(stakepool) = bcs::from_bytes::(data) { - let total_stake_account = AccountIdentifier::total_stake_account(*owner_address); - let operator_stake_account = AccountIdentifier::operator_stake_account( - *owner_address, - stakepool.operator_address, - ); - - // Retrieve add stake events - let add_stake_events = filter_events( - events, - stakepool.add_stake_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::( - event.event_data(), - ) { - Some(event) - } else { - warn!( - "Failed to parse add stake event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // For every stake event, we distribute to the two sub balances. The withdrawal from the account - // is handled in coin - for event in add_stake_events { - operations.push(Operation::deposit( - operation_index, - Some(OperationStatusType::Success), - total_stake_account.clone(), - native_coin(), - event.amount_added, - )); - operation_index += 1; - operations.push(Operation::deposit( - operation_index, - Some(OperationStatusType::Success), - operator_stake_account.clone(), - native_coin(), - event.amount_added, - )); - operation_index += 1; - } - - // Retrieve withdraw stake events - let withdraw_stake_events = filter_events( - events, - stakepool.withdraw_stake_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event) - } else { - warn!( - "Failed to parse withdraw stake event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // For every withdraw event, we have to remove the amounts from the stake pools - for event in withdraw_stake_events { - operations.push(Operation::withdraw( - operation_index, - Some(OperationStatusType::Success), - total_stake_account.clone(), - native_coin(), - event.amount_withdrawn, - )); - operation_index += 1; - operations.push(Operation::withdraw( - operation_index, - Some(OperationStatusType::Success), - operator_stake_account.clone(), - native_coin(), - event.amount_withdrawn, - )); - operation_index += 1; - } - - // Retrieve staking rewards events - let distribute_rewards_events = filter_events( - events, - stakepool.distribute_rewards_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) - { - Some(event) - } else { - warn!( - "Failed to parse distribute rewards event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // For every distribute rewards events, add to the staking pools - for event in distribute_rewards_events { - operations.push(Operation::staking_reward( - operation_index, - Some(OperationStatusType::Success), - total_stake_account.clone(), - native_coin(), - event.rewards_amount, - )); - operation_index += 1; - operations.push(Operation::staking_reward( - operation_index, - Some(OperationStatusType::Success), - operator_stake_account.clone(), - native_coin(), - event.rewards_amount, - )); - operation_index += 1; - } - - // Set voter has to be done at the `staking_contract` because there's no event for it here... - - // Handle set operator events - let set_operator_events = filter_events( - events, - stakepool.set_operator_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::( - event.event_data(), - ) { - Some(event) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse set operator event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // For every set operator event, change the operator, and transfer the money between them - // We do this after balance transfers so the balance changes are easier - let final_staked_amount = stakepool.get_total_staked_amount(); - for event in set_operator_events { - operations.push(Operation::set_operator( - operation_index, - Some(OperationStatusType::Success), - *owner_address, - Some(AccountIdentifier::base_account(event.old_operator)), - AccountIdentifier::base_account(event.new_operator), - )); - operation_index += 1; - - let old_operator_account = - AccountIdentifier::operator_stake_account(*owner_address, event.old_operator); - operations.push(Operation::withdraw( - operation_index, - Some(OperationStatusType::Success), - old_operator_account, - native_coin(), - final_staked_amount, - )); - operation_index += 1; - let new_operator_account = - AccountIdentifier::operator_stake_account(*owner_address, event.old_operator); - operations.push(Operation::deposit( - operation_index, - Some(OperationStatusType::Success), - new_operator_account, - native_coin(), - final_staked_amount, - )); - operation_index += 1; - } - } else { - warn!( - "Failed to parse stakepool for {} at version {}", - pool_address, version - ); - } - } - */ - Ok(operations) -} - -async fn parse_staking_contract_resource_changes( - owner_address: AccountAddress, - data: &[u8], - events: &[ContractEvent], - mut operation_index: u64, - changes: &WriteSet, -) -> ApiResult> { - let mut operations = Vec::new(); - - // This only handles the voter events from the staking contract - // If there are direct events on the pool, they will be ignored - if let Ok(store) = bcs::from_bytes::(data) { - // Collect all the stake pools that were created - let stake_pools: BTreeMap = changes - .iter() - .filter_map(|(state_key, write_op)| { - let data = write_op.bytes(); - - let mut ret = None; - if let (StateKeyInner::AccessPath(path), Some(data)) = (state_key.inner(), data) { - if let Some(struct_tag) = path.get_struct_tag() { - if let (AccountAddress::ONE, STAKE_MODULE, STAKE_POOL_RESOURCE) = ( - struct_tag.address, - struct_tag.module.as_str(), - struct_tag.name.as_str(), - ) { - if let Ok(pool) = bcs::from_bytes::(data) { - ret = Some((path.address, pool)) - } - } - } - } - - ret - }) - .collect(); - - // Collect all operator events for all the stake pools, and add the total stake - let mut set_operator_operations = vec![]; - let mut total_stake = 0; - for (operator, staking_contract) in store.staking_contracts { - if let Some(stake_pool) = stake_pools.get(&staking_contract.pool_address) { - // Skip mismatched operators - if operator != stake_pool.operator_address { - continue; - } - total_stake += stake_pool.get_total_staked_amount(); - - // Get all set operator events for this stake pool - let set_operator_events = filter_events( - events, - stake_pool.set_operator_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse set operator event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - for event in set_operator_events.iter() { - set_operator_operations.push(Operation::set_operator( - operation_index, - Some(OperationStatusType::Success), - owner_address, - Some(AccountIdentifier::base_account(event.old_operator)), - AccountIdentifier::base_account(event.new_operator), - None, - )); - operation_index += 1; - } - } - } - - // Handle set voter events, there are no events on the stake pool - let set_voter_events = filter_events( - events, - store.update_voter_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse update voter event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // Parse all set voter events - for event in set_voter_events { - operations.push(Operation::set_voter( - operation_index, - Some(OperationStatusType::Success), - owner_address, - Some(AccountIdentifier::base_account(event.operator)), - AccountIdentifier::base_account(event.new_voter), - )); - operation_index += 1; - } - - // Attach all set operators now, but with the total stake listed - for mut operation in set_operator_operations.into_iter() { - if let Some(inner) = operation.metadata.as_mut() { - inner.staked_balance = Some(total_stake.into()) - } - operations.push(operation); - } - - // Handle distribute events, there are no events on the stake pool - let distribute_staking_rewards_events = - filter_events(events, store.distribute_events.key(), |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse distribute event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }); - - // For every distribute events, add staking reward operation - for event in distribute_staking_rewards_events { - operations.push(Operation::staking_reward( - operation_index, - Some(OperationStatusType::Success), - AccountIdentifier::base_account(event.recipient), - native_coin(), - event.amount, - )); - operation_index += 1; - } - } - - Ok(operations) -} - -async fn parse_update_commission( - _owner_address: AccountAddress, - data: &[u8], - events: &[ContractEvent], - mut operation_index: u64, - _changes: &WriteSet, -) -> ApiResult> { - let mut operations = Vec::new(); - - // This only handles the voter events from the staking contract - // If there are direct events on the pool, they will be ignored - if let Ok(event_holder) = bcs::from_bytes::(data) { - let update_commission_events = filter_events( - events, - event_holder.update_commission_events.key(), - |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse update commission event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }, - ); - - // For every distribute events, add staking reward operation - for event in update_commission_events { - operations.push(Operation::update_commission( - operation_index, - Some(OperationStatusType::Success), - event.staker, - Some(AccountIdentifier::base_account(event.operator)), - Some(event.new_commission_percentage), - )); - operation_index += 1; - } - } - Ok(operations) -} - -async fn parse_delegation_pool_resource_changes( - _owner_address: AccountAddress, - _data: &[u8], - events: &[ContractEvent], - mut operation_index: u64, - _changes: &WriteSet, -) -> ApiResult> { - let mut operations = vec![]; - - for e in events { - let struct_tag = match e.type_tag() { - TypeTag::Struct(struct_tag) => struct_tag, - _ => continue, - }; - - match ( - struct_tag.address, - struct_tag.module.as_str(), - struct_tag.name.as_str(), - ) { - (AccountAddress::ONE, DELEGATION_POOL_MODULE, WITHDRAW_STAKE_EVENT) => { - let event: WithdrawUndelegedEvent = - if let Ok(event) = bcs::from_bytes(e.event_data()) { - event - } else { - warn!( - "Failed to parse withdraw undelegated event! Skipping for {}:{}", - e.v1()?.key().get_creator_address(), - e.v1()?.key().get_creation_number() - ); - continue; - }; - - operations.push(Operation::withdraw_undelegated_stake( - operation_index, - Some(OperationStatusType::Success), - event.delegator_address, - AccountIdentifier::base_account(event.pool_address), - Some(event.amount_withdrawn), - )); - operation_index += 1; - }, - _ => continue, - } - } - - Ok(operations) -} - -async fn parse_coinstore_changes( - currency: Currency, - version: u64, - address: AccountAddress, - data: &[u8], - events: &[ContractEvent], - mut operation_index: u64, -) -> ApiResult> { - let coin_store: CoinStoreResource = if let Ok(coin_store) = bcs::from_bytes(data) { - coin_store - } else { - warn!( - "Coin store failed to parse for coin type {:?} and address {} at version {}", - currency, address, version - ); - return Ok(vec![]); - }; - - let mut operations = vec![]; - - // Skip if there is no currency that can be found - let withdraw_amounts = get_amount_from_event(events, coin_store.withdraw_events().key()); - for amount in withdraw_amounts { - operations.push(Operation::withdraw( - operation_index, - Some(OperationStatusType::Success), - AccountIdentifier::base_account(address), - currency.clone(), - amount, - )); - operation_index += 1; - } - - let deposit_amounts = get_amount_from_event(events, coin_store.deposit_events().key()); - for amount in deposit_amounts { - operations.push(Operation::deposit( - operation_index, - Some(OperationStatusType::Success), - AccountIdentifier::base_account(address), - currency.clone(), - amount, - )); - operation_index += 1; - } - - Ok(operations) -} - -/// Pulls the balance change from a withdraw or deposit event -fn get_amount_from_event(events: &[ContractEvent], event_key: &EventKey) -> Vec { - filter_events(events, event_key, |event_key, event| { - if let Ok(event) = bcs::from_bytes::(event.event_data()) { - Some(event.amount()) - } else { - // If we can't parse the withdraw event, then there's nothing - warn!( - "Failed to parse coin store withdraw event! Skipping for {}:{}", - event_key.get_creator_address(), - event_key.get_creation_number() - ); - None - } - }) -} - -/// Filter v2 FeeStatement events with non-zero storage_fee_refund -fn get_fee_statement_from_event(events: &[ContractEvent]) -> Vec { - events - .iter() - .filter_map(|event| { - if let Ok(Some(fee_statement)) = event.try_v2_typed(&FEE_STATEMENT_EVENT_TYPE) { - Some(fee_statement) - } else { - None - } - }) - .collect() -} - -fn filter_events Option, T>( - events: &[ContractEvent], - event_key: &EventKey, - parser: F, -) -> Vec { - events - .iter() - .filter(|event| event.is_v1()) - .filter(|event| event.v1().unwrap().key() == event_key) - .sorted_by(|a, b| { - a.v1() - .unwrap() - .sequence_number() - .cmp(&b.v1().unwrap().sequence_number()) - }) - .filter_map(|event| parser(event_key, event)) - .collect() -} - -/// An enum for processing which operation is in a transaction -pub enum OperationDetails { - CreateAccount, - TransferCoin { - currency: Currency, - withdraw_event_key: Option, - deposit_event_key: Option, - }, -} - -/// A holder for all information related to a specific transaction -/// built from [`Operation`]s -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub enum InternalOperation { - CreateAccount(CreateAccount), - Transfer(Transfer), - SetOperator(SetOperator), - SetVoter(SetVoter), - InitializeStakePool(InitializeStakePool), - ResetLockup(ResetLockup), - UnlockStake(UnlockStake), - UpdateCommission(UpdateCommission), - WithdrawUndelegated(WithdrawUndelegated), - DistributeStakingRewards(DistributeStakingRewards), - AddDelegatedStake(AddDelegatedStake), - UnlockDelegatedStake(UnlockDelegatedStake), -} - -impl InternalOperation { - /// Pulls the [`InternalOperation`] from the set of [`Operation`] - pub fn extract(operations: &Vec) -> ApiResult { - match operations.len() { - 1 => { - if let Some(operation) = operations.first() { - match OperationType::from_str(&operation.operation_type) { - Ok(OperationType::InitializeStakePool) => { - if let ( - Some(OperationMetadata { - new_operator, - new_voter, - staked_balance, - commission_percentage, - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - let owner_address = account.account_address()?; - let operator_address = if let Some(address) = new_operator { - address.account_address()? - } else { - owner_address - }; - let voter_address = if let Some(address) = new_voter { - address.account_address()? - } else { - owner_address - }; - - return Ok(Self::InitializeStakePool(InitializeStakePool { - owner: owner_address, - operator: operator_address, - voter: voter_address, - amount: staked_balance.map(u64::from).unwrap_or_default(), - commission_percentage: commission_percentage - .map(u64::from) - .unwrap_or_default(), - seed: vec![], - })); - } - }, - Ok(OperationType::CreateAccount) => { - if let ( - Some(OperationMetadata { - sender: Some(sender), - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - return Ok(Self::CreateAccount(CreateAccount { - sender: sender.account_address()?, - new_account: account.account_address()?, - })); - } - }, - Ok(OperationType::SetOperator) => { - if let ( - Some(OperationMetadata { - old_operator, - new_operator: Some(new_operator), - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - let old_operator = if let Some(old_operator) = old_operator { - Some(old_operator.account_address()?) - } else { - None - }; - - return Ok(Self::SetOperator(SetOperator { - owner: account.account_address()?, - old_operator, - new_operator: new_operator.account_address()?, - })); - } - }, - Ok(OperationType::SetVoter) => { - if let ( - Some(OperationMetadata { - operator, - new_voter: Some(new_voter), - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - let operator = if let Some(operator) = operator { - Some(operator.account_address()?) - } else { - None - }; - return Ok(Self::SetVoter(SetVoter { - owner: account.account_address()?, - operator, - new_voter: new_voter.account_address()?, - })); - } - }, - Ok(OperationType::ResetLockup) => { - if let (Some(OperationMetadata { operator, .. }), Some(account)) = - (&operation.metadata, &operation.account) - { - let operator = if let Some(operator) = operator { - operator.account_address()? - } else { - return Err(ApiError::InvalidInput(Some( - "Reset lockup missing operator field".to_string(), - ))); - }; - return Ok(Self::ResetLockup(ResetLockup { - owner: account.account_address()?, - operator, - })); - } - }, - Ok(OperationType::UnlockStake) => { - if let ( - Some(OperationMetadata { - operator, amount, .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - let operator = if let Some(operator) = operator { - operator.account_address()? - } else { - return Err(ApiError::InvalidInput(Some( - "Unlock Stake missing operator field".to_string(), - ))); - }; - return Ok(Self::UnlockStake(UnlockStake { - owner: account.account_address()?, - operator, - amount: amount.map(u64::from).unwrap_or_default(), - })); - } - }, - Ok(OperationType::UpdateCommission) => { - if let ( - Some(OperationMetadata { - operator, - commission_percentage, - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - let operator = if let Some(operator) = operator { - operator.account_address()? - } else { - return Err(ApiError::InvalidInput(Some( - "Unlock Stake missing operator field".to_string(), - ))); - }; - return Ok(Self::UpdateCommission(UpdateCommission { - owner: account.account_address()?, - operator, - new_commission_percentage: commission_percentage - .map(u64::from) - .unwrap_or_default(), - })); - } - }, - Ok(OperationType::DistributeStakingRewards) => { - if let ( - Some(OperationMetadata { - operator: Some(operator), - staker: Some(staker), - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - return Ok(Self::DistributeStakingRewards( - DistributeStakingRewards { - sender: account.account_address()?, - operator: operator.account_address()?, - staker: staker.account_address()?, - }, - )); - } - }, - Ok(OperationType::AddDelegatedStake) => { - if let ( - Some(OperationMetadata { - pool_address: Some(pool_address), - amount, - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - return Ok(Self::AddDelegatedStake(AddDelegatedStake { - delegator: account.account_address()?, - pool_address: pool_address.account_address()?, - amount: amount.map(u64::from).unwrap_or_default(), - })); - } - }, - Ok(OperationType::UnlockDelegatedStake) => { - if let ( - Some(OperationMetadata { - pool_address: Some(pool_address), - amount, - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - return Ok(Self::UnlockDelegatedStake(UnlockDelegatedStake { - delegator: account.account_address()?, - pool_address: pool_address.account_address()?, - amount: amount.map(u64::from).unwrap_or_default(), - })); - } - }, - Ok(OperationType::WithdrawUndelegatedFunds) => { - if let ( - Some(OperationMetadata { - pool_address: Some(pool_address), - amount, - .. - }), - Some(account), - ) = (&operation.metadata, &operation.account) - { - return Ok(Self::WithdrawUndelegated(WithdrawUndelegated { - delegator: account.account_address()?, - amount_withdrawn: amount.map(u64::from).unwrap_or_default(), - pool_address: pool_address.account_address()?, - })); - } - }, - _ => {}, - } - } - - // Return invalid operations if for any reason parsing fails - Err(ApiError::InvalidOperations(Some(format!( - "Unrecognized single operation {:?}", - operations - )))) - }, - 2 => Ok(Self::Transfer(Transfer::extract_transfer(operations)?)), - _ => Err(ApiError::InvalidOperations(Some(format!( - "Unrecognized operation combination {:?}", - operations - )))), - } - } - - /// The sender of the transaction - pub fn sender(&self) -> AccountAddress { - match self { - Self::CreateAccount(inner) => inner.sender, - Self::Transfer(inner) => inner.sender, - Self::SetOperator(inner) => inner.owner, - Self::SetVoter(inner) => inner.owner, - Self::InitializeStakePool(inner) => inner.owner, - Self::ResetLockup(inner) => inner.owner, - Self::UnlockStake(inner) => inner.owner, - Self::UpdateCommission(inner) => inner.owner, - Self::WithdrawUndelegated(inner) => inner.delegator, - Self::DistributeStakingRewards(inner) => inner.sender, - Self::AddDelegatedStake(inner) => inner.delegator, - Self::UnlockDelegatedStake(inner) => inner.delegator, - } - } - - pub fn payload( - &self, - ) -> ApiResult<(aptos_types::transaction::TransactionPayload, AccountAddress)> { - Ok(match self { - InternalOperation::CreateAccount(create_account) => ( - aptos_stdlib::aptos_account_create_account(create_account.new_account), - create_account.sender, - ), - InternalOperation::Transfer(transfer) => { - is_native_coin(&transfer.currency)?; - ( - aptos_stdlib::aptos_account_transfer(transfer.receiver, transfer.amount.0), - transfer.sender, - ) - }, - InternalOperation::SetOperator(set_operator) => { - if set_operator.old_operator.is_none() { - return Err(ApiError::InvalidInput(Some( - "SetOperator doesn't have an old operator".to_string(), - ))); - } - ( - aptos_stdlib::staking_contract_switch_operator_with_same_commission( - set_operator.old_operator.unwrap(), - set_operator.new_operator, - ), - set_operator.owner, - ) - }, - InternalOperation::SetVoter(set_voter) => { - if set_voter.operator.is_none() { - return Err(ApiError::InvalidInput(Some( - "Set voter doesn't have an operator".to_string(), - ))); - } - ( - aptos_stdlib::staking_contract_update_voter( - set_voter.operator.unwrap(), - set_voter.new_voter, - ), - set_voter.owner, - ) - }, - InternalOperation::InitializeStakePool(init_stake_pool) => ( - aptos_stdlib::staking_contract_create_staking_contract( - init_stake_pool.operator, - init_stake_pool.voter, - init_stake_pool.amount, - init_stake_pool.commission_percentage, - init_stake_pool.seed.clone(), - ), - init_stake_pool.owner, - ), - InternalOperation::ResetLockup(reset_lockup) => ( - aptos_stdlib::staking_contract_reset_lockup(reset_lockup.operator), - reset_lockup.owner, - ), - InternalOperation::UnlockStake(unlock_stake) => ( - aptos_stdlib::staking_contract_unlock_stake( - unlock_stake.operator, - unlock_stake.amount, - ), - unlock_stake.owner, - ), - InternalOperation::UpdateCommission(update_commision) => ( - aptos_stdlib::staking_contract_update_commision( - update_commision.operator, - update_commision.new_commission_percentage, - ), - update_commision.owner, - ), - InternalOperation::DistributeStakingRewards(distribute_staking_rewards) => ( - aptos_stdlib::staking_contract_distribute( - distribute_staking_rewards.staker, - distribute_staking_rewards.operator, - ), - distribute_staking_rewards.sender, - ), - InternalOperation::AddDelegatedStake(add_delegated_stake) => ( - aptos_stdlib::delegation_pool_add_stake( - add_delegated_stake.pool_address, - add_delegated_stake.amount, - ), - add_delegated_stake.delegator, - ), - InternalOperation::UnlockDelegatedStake(unlock_delegated_stake) => ( - aptos_stdlib::delegation_pool_unlock( - unlock_delegated_stake.pool_address, - unlock_delegated_stake.amount, - ), - unlock_delegated_stake.delegator, - ), - InternalOperation::WithdrawUndelegated(withdraw_undelegated) => ( - aptos_stdlib::delegation_pool_withdraw( - withdraw_undelegated.pool_address, - withdraw_undelegated.amount_withdrawn, - ), - withdraw_undelegated.delegator, - ), - }) - } -} - -/// Operation to create an account -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct CreateAccount { - pub sender: AccountAddress, - pub new_account: AccountAddress, -} - -/// Operation to transfer coins between accounts -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct Transfer { - pub sender: AccountAddress, - pub receiver: AccountAddress, - pub amount: U64, - pub currency: Currency, -} - -impl Transfer { - pub fn extract_transfer(operations: &Vec) -> ApiResult { - // Only support 1:1 P2P transfer - // This is composed of a Deposit and a Withdraw operation - if operations.len() != 2 { - return Err(ApiError::InvalidTransferOperations(Some( - "Must have exactly 2 operations a withdraw and a deposit", - ))); - } - - let mut op_map = HashMap::new(); - for op in operations { - let op_type = OperationType::from_str(&op.operation_type)?; - op_map.insert(op_type, op); - } - - if !op_map.contains_key(&OperationType::Deposit) { - return Err(ApiError::InvalidTransferOperations(Some( - "Must have a deposit", - ))); - } - - // Verify accounts and amounts - let (sender, withdraw_amount) = if let Some(withdraw) = op_map.get(&OperationType::Withdraw) - { - if let (Some(account), Some(amount)) = (&withdraw.account, &withdraw.amount) { - if account.is_base_account() { - (account.account_address()?, amount) - } else { - return Err(ApiError::InvalidInput(Some( - "Transferring stake amounts is not supported".to_string(), - ))); - } - } else { - return Err(ApiError::InvalidTransferOperations(Some( - "Invalid withdraw account provided", - ))); - } - } else { - return Err(ApiError::InvalidTransferOperations(Some( - "Must have a withdraw", - ))); - }; - - let (receiver, deposit_amount) = if let Some(deposit) = op_map.get(&OperationType::Deposit) - { - if let (Some(account), Some(amount)) = (&deposit.account, &deposit.amount) { - if account.is_base_account() { - (account.account_address()?, amount) - } else { - return Err(ApiError::InvalidInput(Some( - "Transferring stake amounts is not supported".to_string(), - ))); - } - } else { - return Err(ApiError::InvalidTransferOperations(Some( - "Invalid deposit account provided", - ))); - } - } else { - return Err(ApiError::InvalidTransferOperations(Some( - "Must have a deposit", - ))); - }; - - // Currencies have to be the same - if withdraw_amount.currency != deposit_amount.currency { - return Err(ApiError::InvalidTransferOperations(Some( - "Currency mismatch between withdraw and deposit", - ))); - } - - // Check that the currency is supported - // TODO: in future use currency, since there's more than just 1 - is_native_coin(&withdraw_amount.currency)?; - - let withdraw_value = i128::from_str(&withdraw_amount.value) - .map_err(|_| ApiError::InvalidTransferOperations(Some("Withdraw amount is invalid")))?; - let deposit_value = i128::from_str(&deposit_amount.value) - .map_err(|_| ApiError::InvalidTransferOperations(Some("Deposit amount is invalid")))?; - - // We can't create or destroy coins, they must be negatives of each other - if -withdraw_value != deposit_value { - return Err(ApiError::InvalidTransferOperations(Some( - "Withdraw amount must be equal to negative of deposit amount", - ))); - } - - // We converted to u128 to ensure no loss of precision in comparison, - // but now we actually have to check it's a u64 - if deposit_value > u64::MAX as i128 { - return Err(ApiError::InvalidTransferOperations(Some( - "Transfer amount must not be greater than u64 max", - ))); - } - - let transfer_amount = deposit_value as u64; - - Ok(Transfer { - sender, - receiver, - amount: transfer_amount.into(), - currency: deposit_amount.currency.clone(), - }) - } -} - -/// Set operator -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SetOperator { - pub owner: AccountAddress, - pub old_operator: Option, - pub new_operator: AccountAddress, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct SetVoter { - pub owner: AccountAddress, - pub operator: Option, - pub new_voter: AccountAddress, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct InitializeStakePool { - pub owner: AccountAddress, - pub operator: AccountAddress, - pub voter: AccountAddress, - pub amount: u64, - pub commission_percentage: u64, - pub seed: Vec, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ResetLockup { - pub owner: AccountAddress, - pub operator: AccountAddress, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct UnlockStake { - pub owner: AccountAddress, - pub operator: AccountAddress, - pub amount: u64, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct UpdateCommission { - pub owner: AccountAddress, - pub operator: AccountAddress, - pub new_commission_percentage: u64, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct WithdrawUndelegated { - pub delegator: AccountAddress, - pub pool_address: AccountAddress, - pub amount_withdrawn: u64, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct DistributeStakingRewards { - pub sender: AccountAddress, - pub operator: AccountAddress, - pub staker: AccountAddress, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct AddDelegatedStake { - pub delegator: AccountAddress, - pub pool_address: AccountAddress, - pub amount: u64, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct UnlockDelegatedStake { - pub delegator: AccountAddress, - pub pool_address: AccountAddress, - pub amount: u64, -} diff --git a/crates/aptos-rosetta/src/types/requests.rs b/crates/aptos-rosetta/src/types/requests.rs deleted file mode 100644 index 32d74addcff11..0000000000000 --- a/crates/aptos-rosetta/src/types/requests.rs +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - types::{ - AccountIdentifier, Allow, Amount, Block, BlockIdentifier, Currency, InternalOperation, - NetworkIdentifier, Operation, PartialBlockIdentifier, Peer, PublicKey, Signature, - SigningPayload, SyncStatus, Transaction, TransactionIdentifier, Version, - }, - AccountAddress, ApiError, -}; -use aptos_rest_client::aptos_api_types::U64; -use aptos_types::{ - chain_id::ChainId, - transaction::{RawTransaction, SignedTransaction}, -}; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ - fmt::{Display, Formatter}, - str::FromStr, -}; - -/// Request for an account's currency balance either now, or historically -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/AccountBalanceRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct AccountBalanceRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Account identifier describing the account address - pub account_identifier: AccountIdentifier, - /// For historical balance lookups by either hash or version - #[serde(skip_serializing_if = "Option::is_none")] - pub block_identifier: Option, - /// For filtering which currencies to show - #[serde(skip_serializing_if = "Option::is_none")] - pub currencies: Option>, -} - -/// Response with the version associated and the balances of the account -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/AccountBalanceResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct AccountBalanceResponse { - /// Block containing the balance - pub block_identifier: BlockIdentifier, - /// Balances of all known currencies - pub balances: Vec, - /// Metadata of account, must have sequence number - pub metadata: AccountBalanceMetadata, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct AccountBalanceMetadata { - /// Sequence number of the account - pub sequence_number: U64, - #[serde(skip_serializing_if = "Option::is_none")] - pub operators: Option>, - pub lockup_expiration_time_utc: U64, -} -/// Reqyest a block (version) on the account -/// -/// With neither value for PartialBlockIdentifier, get the latest version -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/BlockRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BlockRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// A set of search parameters (latest, by hash, or by index) - #[serde(skip_serializing_if = "Option::is_none")] - pub block_identifier: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BlockRequestMetadata { - #[serde(skip_serializing_if = "Option::is_none")] - pub keep_empty_transactions: Option, -} - -impl BlockRequest { - fn new(chain_id: ChainId, block_identifier: Option) -> Self { - Self { - network_identifier: chain_id.into(), - block_identifier, - metadata: None, - } - } - - pub fn latest(chain_id: ChainId) -> Self { - Self::new(chain_id, None) - } - - pub fn by_hash(chain_id: ChainId, hash: String) -> Self { - Self::new(chain_id, Some(PartialBlockIdentifier::by_hash(hash))) - } - - pub fn by_index(chain_id: ChainId, index: u64) -> Self { - Self::new(chain_id, Some(PartialBlockIdentifier::block_index(index))) - } - - pub fn with_empty_transactions(mut self) -> Self { - self.metadata = Some(BlockRequestMetadata { - keep_empty_transactions: Some(true), - }); - self - } -} - -/// Response that will always have a valid block populated -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/BlockResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct BlockResponse { - /// The block requested. This should always be populated for a given valid version - pub block: Block, -} - -/// Request to combine signatures and an unsigned transaction for submission as a -/// [`aptos_types::transaction::SignedTransaction`] -/// -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionCombineRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionCombineRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// A hex encoded, BCS encoded, [`aptos_types::transaction::RawTransaction`] - pub unsigned_transaction: String, - /// Set of signatures with SigningPayloads to combine - pub signatures: Vec, -} - -/// Response of signed transaction for submission -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionCombineResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionCombineResponse { - /// A hex encoded, BCS encoded, [`aptos_types::transaction::SignedTransaction`] - pub signed_transaction: String, -} - -/// Request to derive an account from a public key -/// -/// This should be able to run without a running full node connection, but note that -/// this will not work with accounts that have rotated their public key. It should -/// only be used when an account is being created. -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionDeriveRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionDeriveRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Public key to derive an [`aptos_types::account_address::AccountAddress`] from - pub public_key: PublicKey, -} - -/// Response of derived account from a public key -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionDeriveResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionDeriveResponse { - /// The account identifier of the account if the [`aptos_types::account_address::AccountAddress`] can be derived. - /// - /// This will always return a value, though it might not match onchain information. - pub account_identifier: AccountIdentifier, -} - -/// Request to hash a transaction -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionHashRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionHashRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// A hex encoded, BCS encoded, [`aptos_types::transaction::SignedTransaction`] - pub signed_transaction: String, -} - -/// Request to retrieve all information needed for constructing a transaction from the blockchain -/// -/// A running full node is required for this API -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionMetadataRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionMetadataRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Information telling which metadata to lookup onchain - /// - /// This comes verbatim from a preprocess request - pub options: MetadataOptions, -} - -/// A set of operations to tell us which metadata to lookup onchain -/// -/// This is built from Preprocess, and is copied verbatim to the metadata request -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MetadataOptions { - /// The operation to run at a high level (e.g. CreateAccount/Transfer) - pub internal_operation: InternalOperation, - /// Maximum total gas units willing to pay for the transaction - #[serde(skip_serializing_if = "Option::is_none")] - pub max_gas_amount: Option, - /// Multiplier how much more willing to pay for the fees - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price_per_unit: Option, - /// Unix timestamp of expiry time - #[serde(skip_serializing_if = "Option::is_none")] - pub expiry_time_secs: Option, - /// Sequence number of the request - #[serde(skip_serializing_if = "Option::is_none")] - pub sequence_number: Option, - /// Public keys to sign simulated transaction. Must be present if max_gas_amount is not provided - #[serde(skip_serializing_if = "Option::is_none")] - pub public_keys: Option>, - /// Taking the estimated gas price, and multiplying it - /// times this number divided by 100 e.g. 120 is 120% - /// of the estimated price - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price_multiplier: Option, - /// Gas price priority. If the priority is low, it will - /// use a deprioritized price. If it's normal, it will use the estimated - /// price, and if it's high, it will use the prioritized price. - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price_priority: Option, -} - -/// Response with network specific data for constructing a transaction -/// -/// In this case, sequence number must be pulled from onchain. -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionMetadataResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionMetadataResponse { - /// Metadata that will be passed to Payloads to create a transaction - pub metadata: ConstructionMetadata, - /// A suggested gas fee based on the current state of the network - pub suggested_fee: Vec, -} - -/// Metadata required to construct a transaction -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionMetadata { - /// Sequence number of the sending account - pub sequence_number: U64, - /// Maximum gas willing to pay for the transaction - pub max_gas_amount: U64, - /// Multiplier e.g. how much each unit of gas is worth in the native coin - pub gas_price_per_unit: U64, - /// Unix timestamp of expiry time, defaults to 30 seconds from the payload request - #[serde(skip_serializing_if = "Option::is_none")] - pub expiry_time_secs: Option, - /// Because we need information from metadata to have the real operation - /// We don't have to parse any fields in the `Payloads` call - pub internal_operation: InternalOperation, -} - -/// Request to parse a signed or unsigned transaction into operations -/// -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionParseRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionParseRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Whether the transaction is a [`aptos_types::transaction::SignedTransaction`] - /// or a [`aptos_types::transaction::RawTransaction`] - pub signed: bool, - /// A hex encoded, BCS encoded [`aptos_types::transaction::SignedTransaction`] - /// or a [`aptos_types::transaction::RawTransaction`] - pub transaction: String, -} - -/// Response with operations in a transaction blob -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionParseResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionParseResponse { - /// The set of [`Operation`] that happened during the transaction - pub operations: Vec, - /// The signers of the transaction, if it was a [`aptos_types::transaction::SignedTransaction`] - #[serde(skip_serializing_if = "Option::is_none")] - pub account_identifier_signers: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionParseMetadata { - #[serde(skip_serializing_if = "Option::is_none")] - pub unsigned_transaction: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub signed_transaction: Option, -} - -/// Request to build payloads from the operations to sign -/// -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPayloadsRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionPayloadsRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// The set of [`Operation`] that describes the [`InternalOperation`] to execute - pub operations: Vec, - /// Required information for building a [`aptos_types::transaction::RawTransaction`] - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, - /// Public keys of those who will sign the eventual [`aptos_types::transaction::SignedTransaction`] - #[serde(skip_serializing_if = "Option::is_none")] - pub public_keys: Option>, -} - -/// Response with generated payloads to be signed -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPayloadsResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionPayloadsResponse { - /// A hex encoded, BCS encoded [`aptos_types::transaction::RawTransaction`] - /// containing the [`Operation`]s - pub unsigned_transaction: String, - /// Payloads describing who and what to sign - pub payloads: Vec, -} - -/// Request to get options for a [`ConstructionMetadataRequest`] -/// -/// This should be able to run without a running full node connection -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPreprocessRequest.html) -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub struct ConstructionPreprocessRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Operations that make up an `InternalOperation` - pub operations: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, -} - -/// This object holds all the possible "changes" to payloads -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub struct PreprocessMetadata { - /// Expiry time of the transaction in unix epoch seconds - #[serde(skip_serializing_if = "Option::is_none")] - pub expiry_time_secs: Option, - /// Sequence number to use for this transaction - #[serde(skip_serializing_if = "Option::is_none")] - pub sequence_number: Option, - /// Max gas amount for this transaction - #[serde(skip_serializing_if = "Option::is_none")] - pub max_gas_amount: Option, - /// Gas unit price for this transaction - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price: Option, - /// Public keys used for this transaction - #[serde(skip_serializing_if = "Option::is_none")] - pub public_keys: Option>, - /// Taking the estimated gas price, and multiplying it - /// times this number divided by 100 e.g. 120 is 120% - /// of the estimated price - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price_multiplier: Option, - /// Gas price priority. If the priority is low, it will - /// use a deprioritized price. If it's normal, it will use the estimated - /// price, and if it's high, it will use the prioritized price. - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_price_priority: Option, -} - -/// A gas price priority for what gas price to use -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] -pub enum GasPricePriority { - Low, - #[default] - Normal, - High, -} - -impl GasPricePriority { - pub fn as_str(&self) -> &'static str { - match self { - GasPricePriority::Low => "low", - GasPricePriority::Normal => "normal", - GasPricePriority::High => "high", - } - } -} - -impl Display for GasPricePriority { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.as_str()) - } -} - -impl FromStr for GasPricePriority { - type Err = ApiError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - "low" => Ok(Self::Low), - "normal" => Ok(Self::Normal), - "high" => Ok(Self::High), - _ => Err(ApiError::InvalidInput(Some(format!( - "{} is an invalid gas price priority", - s - )))), - } - } -} - -impl Serialize for GasPricePriority { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.as_str().serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for GasPricePriority { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let str = ::deserialize(deserializer)?; - Self::from_str(&str).map_err(|err| D::Error::custom(err.to_string())) - } -} - -/// Response for direct input into a [`ConstructionMetadataRequest`] -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPreprocessResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionPreprocessResponse { - /// Metadata to be sent verbatim to the Metadata API - pub options: MetadataOptions, - /// List of who needs to be signing this transaction - pub required_public_keys: Vec, -} - -/// Request to submit a signed transaction -/// -/// A running full node is required for this API -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionSubmitRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionSubmitRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// A hex encoded, BCS encoded [`aptos_types::transaction::SignedTransaction`] - pub signed_transaction: String, -} - -/// Response containing transaction identifier of submitted transaction -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionSubmitResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct ConstructionSubmitResponse { - /// Hash of the submitted [`aptos_types::transaction::SignedTransaction`] - pub transaction_identifier: TransactionIdentifier, -} - -/// Request for all transactions in mempool -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MempoolRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, -} - -/// Response of all transactions in mempool -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MempoolResponse { - /// Hash of the transactions in mempool - pub transaction_identifiers: Vec, -} - -/// Request for a transaction in mempool by hash -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolTransactionRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MempoolTransactionRequest { - /// Network identifier describing the blockchain and the chain id - pub network_identifier: NetworkIdentifier, - /// Hash of a transaction to lookup in mempool - pub transaction_identifier: TransactionIdentifier, -} - -/// Response of an estimate of the transaction in mempool -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolTransactionResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MempoolTransactionResponse { - /// The transaction in mempool - pub transaction: Transaction, -} - -/// Metadata request for a placeholder when no other fields exist -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/MetadataRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct MetadataRequest {} - -/// Response of all networks that this endpoint supports -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkListResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct NetworkListResponse { - /// List of networks supported by this Rosetta instance - pub network_identifiers: Vec, -} - -/// Response with all versioning and implementation specific fields -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkOptionsResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct NetworkOptionsResponse { - /// Software versions - pub version: Version, - /// Specifics about what is allowed on this server - pub allow: Allow, -} - -/// A generic request for network APIs -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkRequest.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct NetworkRequest { - pub network_identifier: NetworkIdentifier, -} - -/// Response with information about the current network state -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkStatusResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct NetworkStatusResponse { - /// Current block identifier - pub current_block_identifier: BlockIdentifier, - /// Current block timestamp in milliseconds - pub current_block_timestamp: u64, - /// Genesis block - pub genesis_block_identifier: BlockIdentifier, - /// Oldest version that is available after pruning. Assumed to be genesis block if not present - pub oldest_block_identifier: BlockIdentifier, - /// Sync status if a node needs to catch up - #[serde(skip_serializing_if = "Option::is_none")] - pub sync_status: Option, - /// Connected peers - pub peers: Vec, -} - -/// Response with a transaction that was hashed or submitted -/// -/// [API Spec](https://www.rosetta-api.org/docs/models/TransactionIdentifierResponse.html) -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub struct TransactionIdentifierResponse { - /// Hash of the transaction - pub transaction_identifier: TransactionIdentifier, -} diff --git a/crates/aptos-telemetry-service/Cargo.toml b/crates/aptos-telemetry-service/Cargo.toml deleted file mode 100644 index 2cd4184d99736..0000000000000 --- a/crates/aptos-telemetry-service/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "aptos-telemetry-service" -description = "Web service to receive telemetry from nodes and ingest it to backends" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-config = { workspace = true } -aptos-crypto = { workspace = true } -aptos-crypto-derive = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-types = { workspace = true } -base64 = { workspace = true } -bcs = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true } -debug-ignore = { workspace = true } -flate2 = { workspace = true } -futures = { workspace = true } -gcp-bigquery-client = { workspace = true } -hex = { workspace = true } -jsonwebtoken = { workspace = true } -once_cell = { workspace = true } -prometheus = { workspace = true } -rand = { workspace = true } -rand_core = { workspace = true } -reqwest = { workspace = true } -reqwest-middleware = { workspace = true } -reqwest-retry = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_repr = { workspace = true } -serde_yaml = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } -uuid = { workspace = true } -warp = { workspace = true } - -[dev-dependencies] -claims = { workspace = true } -httpmock = { workspace = true } diff --git a/crates/aptos-telemetry-service/src/auth.rs b/crates/aptos-telemetry-service/src/auth.rs deleted file mode 100644 index 1a16a4f0328ba..0000000000000 --- a/crates/aptos-telemetry-service/src/auth.rs +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - context::Context, - debug, error, - errors::{AuthError, ServiceError, ServiceErrorCode}, - jwt_auth::{authorize_jwt, create_jwt_token, jwt_from_header}, - types::{ - auth::{AuthRequest, AuthResponse, Claims}, - common::NodeType, - }, - warn, -}; -use anyhow::Result; -use aptos_config::config::{PeerRole, RoleType}; -use aptos_crypto::{noise, x25519}; -use aptos_types::{chain_id::ChainId, PeerId}; -use reqwest::header::AUTHORIZATION; -use warp::{filters::BoxedFilter, reject, reply, Filter, Rejection, Reply}; - -pub fn auth(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("auth") - .and(warp::post()) - .and(context.filter()) - .and(warp::body::json()) - .and_then(handle_auth) - .boxed() -} - -pub async fn handle_auth(context: Context, body: AuthRequest) -> Result { - debug!("received auth request: {:?}", body); - - let client_init_message = &body.handshake_msg; - - // Check whether the client (validator) is using the correct server's public key, which the - // client includes in its request body. - // This is useful for returning a refined error response to the client if it is using an - // invalid server public key. - if body.server_public_key != context.noise_config().public_key() { - return Err(reject::custom(ServiceError::bad_request( - ServiceErrorCode::AuthError(AuthError::InvalidServerPublicKey, body.chain_id), - ))); - } - - // build the prologue (chain_id | peer_id | public_key) - const CHAIN_ID_LENGTH: usize = 1; - const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; - const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; - let mut prologue = [0; PROLOGUE_SIZE]; - prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[body.chain_id.id()]); - prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(body.peer_id.as_ref()); - prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(body.server_public_key.as_slice()); - - let (remote_public_key, handshake_state, _payload) = context - .noise_config() - .parse_client_init_message(&prologue, client_init_message) - .map_err(|e| { - debug!("error performing noise handshake: {}", e); - reject::custom(ServiceError::bad_request(ServiceErrorCode::AuthError( - AuthError::NoiseHandshakeError(e), - body.chain_id, - ))) - })?; - - let cache = if body.role_type == RoleType::Validator { - context.peers().validators() - } else { - context.peers().validator_fullnodes() - }; - - let (epoch, peer_role) = match cache.read().get(&body.chain_id) { - Some((epoch, peer_set)) => { - match peer_set.get(&body.peer_id) { - Some(peer) => { - let remote_public_key = &remote_public_key; - if !peer.keys.contains(remote_public_key) { - warn!("peer found in peer set but public_key is not found. request body: {}, role_type: {}, peer_id: {}, received public_key: {}", body.chain_id, body.role_type, body.peer_id, remote_public_key); - return Err(reject::custom(ServiceError::forbidden( - ServiceErrorCode::AuthError( - AuthError::PeerPublicKeyNotFound, - body.chain_id, - ), - ))); - } - Ok((*epoch, peer.role)) - }, - None => { - // if not, verify that their peerid is constructed correctly from their public key - let derived_remote_peer_id = - aptos_types::account_address::from_identity_public_key(remote_public_key); - if derived_remote_peer_id != body.peer_id { - return Err(reject::custom(ServiceError::forbidden( - ServiceErrorCode::AuthError( - AuthError::PublicKeyMismatch, - body.chain_id, - ), - ))); - } else { - Ok((*epoch, PeerRole::Unknown)) - } - }, - } - }, - None => { - warn!( - "Validator set unavailable for Chain ID {}. Rejecting request.", - body.chain_id - ); - Err(reject::custom(ServiceError::unauthorized( - ServiceErrorCode::AuthError(AuthError::ValidatorSetUnavailable, body.chain_id), - ))) - }, - }?; - - let node_type = match peer_role { - PeerRole::Validator => NodeType::Validator, - PeerRole::ValidatorFullNode => NodeType::ValidatorFullNode, - PeerRole::Unknown => match body.role_type { - RoleType::Validator => NodeType::UnknownValidator, - RoleType::FullNode => context - .peers() - .public_fullnodes() - .get(&body.chain_id) - .and_then(|peer_set| { - if peer_set.contains_key(&body.peer_id) { - Some(NodeType::PublicFullNode) - } else { - None - } - }) - .unwrap_or(NodeType::UnknownFullNode), - }, - _ => NodeType::Unknown, - }; - - let token = create_jwt_token( - context.jwt_service(), - body.chain_id, - body.peer_id, - node_type, - epoch, - body.run_uuid, - ) - .map_err(|e| { - error!("unable to create jwt token: {}", e); - reject::custom(ServiceError::internal(ServiceErrorCode::AuthError( - AuthError::from(e), - body.chain_id, - ))) - })?; - - let mut rng = rand::rngs::OsRng; - let response_payload = token.as_bytes(); - let mut server_response = vec![0u8; noise::handshake_resp_msg_len(response_payload.len())]; - context - .noise_config() - .respond_to_client( - &mut rng, - handshake_state, - Some(response_payload), - &mut server_response, - ) - .map_err(|e| { - error!("unable to complete handshake {}", e); - ServiceError::internal(ServiceErrorCode::AuthError( - AuthError::NoiseHandshakeError(e), - body.chain_id, - )) - })?; - - Ok(reply::json(&AuthResponse { - handshake_msg: server_response, - })) -} - -pub fn with_auth( - context: Context, - roles: Vec, -) -> impl Filter + Clone { - warp::header::optional(AUTHORIZATION.as_str()) - .and_then(jwt_from_header) - .and( - warp::any() - .map(move || (context.clone(), roles.clone())) - .untuple_one(), - ) - .and_then(authorize_jwt) -} - -pub fn check_chain_access(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("chain-access" / ChainId) - .and(warp::get()) - .and(context.filter()) - .and_then(handle_check_chain_access) - .boxed() -} - -async fn handle_check_chain_access( - chain_id: ChainId, - context: Context, -) -> Result { - let present = context.chain_set().contains(&chain_id); - Ok(reply::json(&present)) -} diff --git a/crates/aptos-telemetry-service/src/clients/humio.rs b/crates/aptos-telemetry-service/src/clients/humio.rs deleted file mode 100644 index 935f20796fac3..0000000000000 --- a/crates/aptos-telemetry-service/src/clients/humio.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::types::humio::UnstructuredLog; -use anyhow::anyhow; -use flate2::{write::GzEncoder, Compression}; -use reqwest::{Client as ReqwestClient, Url}; -use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; -use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; - -pub const PEER_ID_FIELD_NAME: &str = "peer_id"; -pub const EPOCH_FIELD_NAME: &str = "epoch"; -pub const PEER_ROLE_TAG_NAME: &str = "peer_role"; -pub const CHAIN_ID_TAG_NAME: &str = "chain_id"; -pub const RUN_UUID_TAG_NAME: &str = "run_uuid"; - -#[derive(Clone)] -pub struct IngestClient { - inner: ClientWithMiddleware, - base_url: Url, - auth_token: String, -} - -impl IngestClient { - pub fn new(base_url: Url, auth_token: String) -> Self { - let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3); - let inner = ClientBuilder::new(ReqwestClient::new()) - .with(RetryTransientMiddleware::new_with_policy(retry_policy)) - .build(); - Self { - inner, - base_url, - auth_token, - } - } - - pub async fn ingest_unstructured_log( - &self, - unstructured_log: UnstructuredLog, - ) -> Result { - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - serde_json::to_writer(&mut gzip_encoder, &vec![unstructured_log]) - .map_err(|e| anyhow!("unable to serialize json: {}", e))?; - let compressed_bytes = gzip_encoder.finish()?; - - self.inner - .post(self.base_url.join("api/v1/ingest/humio-unstructured")?) - .bearer_auth(self.auth_token.clone()) - .header("Content-Encoding", "gzip") - .body(compressed_bytes) - .send() - .await - .map_err(|e| anyhow!("failed to post metrics: {}", e)) - } -} diff --git a/crates/aptos-telemetry-service/src/clients/mod.rs b/crates/aptos-telemetry-service/src/clients/mod.rs deleted file mode 100644 index cdfce66ce7531..0000000000000 --- a/crates/aptos-telemetry-service/src/clients/mod.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod humio; - -pub mod victoria_metrics_api { - - use anyhow::{anyhow, Result}; - use reqwest::{header::CONTENT_ENCODING, Client as ReqwestClient}; - use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; - use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; - use url::Url; - use warp::hyper::body::Bytes; - - /// Client implementation to export metrics to Victoria Metrics - #[derive(Clone)] - pub struct Client { - inner: ClientWithMiddleware, - base_url: Url, - auth_token: String, - } - - impl Client { - pub fn new(base_url: Url, auth_token: String) -> Self { - let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3); - let inner = ClientBuilder::new(ReqwestClient::new()) - .with(RetryTransientMiddleware::new_with_policy(retry_policy)) - .build(); - Self { - inner, - base_url, - auth_token, - } - } - - pub async fn post_prometheus_metrics( - &self, - raw_metrics_body: Bytes, - extra_labels: Vec, - encoding: String, - ) -> Result { - let labels: Vec<(String, String)> = extra_labels - .iter() - .map(|label| ("extra_label".into(), label.into())) - .collect(); - - self.inner - .post(format!("{}api/v1/import/prometheus", self.base_url)) - .bearer_auth(self.auth_token.clone()) - .header(CONTENT_ENCODING, encoding) - .query(&labels) - .body(raw_metrics_body) - .send() - .await - .map_err(|e| anyhow!("failed to post metrics: {}", e)) - } - } -} - -pub mod big_query { - use gcp_bigquery_client::{ - error::BQError, - model::{ - table_data_insert_all_request::TableDataInsertAllRequest, - table_data_insert_all_response::TableDataInsertAllResponse, - }, - Client as BigQueryClient, - }; - - #[derive(Clone)] - pub struct TableWriteClient { - client: BigQueryClient, - project_id: String, - dataset_id: String, - table_id: String, - } - - impl TableWriteClient { - pub fn new( - client: BigQueryClient, - project_id: String, - dataset_id: String, - table_id: String, - ) -> Self { - Self { - client, - project_id, - dataset_id, - table_id, - } - } - - pub async fn insert_all( - &self, - insert_request: TableDataInsertAllRequest, - ) -> Result { - self.client - .tabledata() - .insert_all( - &self.project_id, - &self.dataset_id, - &self.table_id, - insert_request, - ) - .await - } - } -} diff --git a/crates/aptos-telemetry-service/src/constants.rs b/crates/aptos-telemetry-service/src/constants.rs deleted file mode 100644 index eff0370e77348..0000000000000 --- a/crates/aptos-telemetry-service/src/constants.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -/// The maximum content length to accept in the http body. -pub const MAX_CONTENT_LENGTH: u64 = 1024 * 1024; - -/// GCP Header field for the current request's trace ID. -pub const GCP_CLOUD_TRACE_CONTEXT_HEADER: &str = "X-Cloud-Trace-Context"; - -/// GCP Cloud Run env variable for the current deployment revision -pub const GCP_CLOUD_RUN_REVISION_ENV: &str = "K_REVISION"; -/// GCP Cloud Run env variable for service name -pub const GCP_CLOUD_RUN_SERVICE_ENV: &str = "K_SERVICE"; -/// GCP Project within which this service is running. -/// This variable must be set by calling the metadata server -pub const GCP_SERVICE_PROJECT_ID_ENV: &str = "GCP_METADATA_PROJECT_ID"; -/// Environment variable with the container identifier for this cloud run revision -/// This variable must be set by calling the metadata server -pub const GCP_CLOUD_RUN_INSTANCE_ID_ENV: &str = "GCP_CLOUD_RUN_INSTANCE_ID"; diff --git a/crates/aptos-telemetry-service/src/context.rs b/crates/aptos-telemetry-service/src/context.rs deleted file mode 100644 index fe6d9d60d9a52..0000000000000 --- a/crates/aptos-telemetry-service/src/context.rs +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - clients::{big_query::TableWriteClient, humio, victoria_metrics_api::Client as MetricsClient}, - types::common::EpochedPeerStore, - LogIngestConfig, MetricsEndpointsConfig, -}; -use aptos_crypto::{noise, x25519}; -use aptos_infallible::RwLock; -use aptos_types::{chain_id::ChainId, PeerId}; -use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, TokenData, Validation}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - convert::Infallible, - sync::Arc, -}; -use warp::Filter; - -/// Container that holds various metric clients used for sending metrics from -/// various sources to appropriate backends. -#[derive(Clone, Default)] -pub struct GroupedMetricsClients { - /// Client(s) for exporting metrics of the running telemetry service - pub telemetry_service_metrics_clients: HashMap, - /// Clients for sending metrics from authenticated known nodes - pub ingest_metrics_client: HashMap, - /// Clients for sending metrics from authenticated unknown nodes - pub untrusted_ingest_metrics_clients: HashMap, -} - -impl GroupedMetricsClients { - #[cfg(test)] - pub fn new_empty() -> Self { - Self { - telemetry_service_metrics_clients: HashMap::new(), - ingest_metrics_client: HashMap::new(), - untrusted_ingest_metrics_clients: HashMap::new(), - } - } -} - -impl From for GroupedMetricsClients { - fn from(config: MetricsEndpointsConfig) -> GroupedMetricsClients { - GroupedMetricsClients { - telemetry_service_metrics_clients: config.telemetry_service_metrics.make_client(), - ingest_metrics_client: config.ingest_metrics.make_client(), - untrusted_ingest_metrics_clients: config.untrusted_ingest_metrics.make_client(), - } - } -} - -#[derive(Clone)] -pub struct LogIngestClients { - pub known_logs_ingest_client: humio::IngestClient, - pub unknown_logs_ingest_client: humio::IngestClient, - pub blacklist: Option>, -} - -impl From for LogIngestClients { - fn from(config: LogIngestConfig) -> Self { - Self { - known_logs_ingest_client: config.known_logs_endpoint.make_client(), - unknown_logs_ingest_client: config.unknown_logs_endpoint.make_client(), - blacklist: config.blacklist_peers, - } - } -} - -#[derive(Clone, Default)] -pub struct PeerStoreTuple { - validators: Arc>, - validator_fullnodes: Arc>, - public_fullnodes: HashMap>, -} - -impl PeerStoreTuple { - pub fn new( - validators: Arc>, - validator_fullnodes: Arc>, - public_fullnodes: HashMap>, - ) -> Self { - Self { - validators, - validator_fullnodes, - public_fullnodes, - } - } - - pub fn validators(&self) -> &Arc> { - &self.validators - } - - pub fn validator_fullnodes(&self) -> &Arc> { - &self.validator_fullnodes - } - - pub fn public_fullnodes(&self) -> &HashMap> { - &self.public_fullnodes - } -} - -#[derive(Clone)] -pub struct ClientTuple { - bigquery_client: Option, - victoria_metrics_clients: Option, - log_ingest_clients: Option, -} - -impl ClientTuple { - pub(crate) fn new( - bigquery_client: Option, - victoria_metrics_clients: Option, - log_ingest_clients: Option, - ) -> ClientTuple { - Self { - bigquery_client, - victoria_metrics_clients, - log_ingest_clients, - } - } -} - -#[derive(Clone)] -pub struct JsonWebTokenService { - encoding_key: EncodingKey, - decoding_key: DecodingKey, -} - -impl JsonWebTokenService { - pub fn from_base64_secret(secret: &str) -> Self { - let encoding_key = jsonwebtoken::EncodingKey::from_base64_secret(secret) - .expect("jsonwebtoken key should be in base64 format."); - let decoding_key = jsonwebtoken::DecodingKey::from_base64_secret(secret) - .expect("jsonwebtoken key should be in base64 format."); - Self { - encoding_key, - decoding_key, - } - } - - pub fn encode(&self, claims: T) -> Result { - let header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS512); - jsonwebtoken::encode(&header, &claims, &self.encoding_key) - } - - pub fn decode( - &self, - token: &str, - ) -> Result, jsonwebtoken::errors::Error> { - jsonwebtoken::decode::( - token, - &self.decoding_key, - &Validation::new(Algorithm::HS512), - ) - } -} - -#[derive(Clone)] -pub struct Context { - noise_config: Arc, - peers: PeerStoreTuple, - clients: ClientTuple, - jwt_service: JsonWebTokenService, - log_env_map: HashMap>, - peer_identities: HashMap>, -} - -impl Context { - pub fn new( - private_key: x25519::PrivateKey, - peers: PeerStoreTuple, - clients: ClientTuple, - jwt_service: JsonWebTokenService, - log_env_map: HashMap>, - peer_identities: HashMap>, - ) -> Self { - Self { - noise_config: Arc::new(noise::NoiseConfig::new(private_key)), - peers, - clients, - jwt_service, - log_env_map, - peer_identities, - } - } - - pub fn filter(self) -> impl Filter + Clone { - warp::any().map(move || self.clone()) - } - - pub fn noise_config(&self) -> Arc { - self.noise_config.clone() - } - - pub fn peers(&self) -> &PeerStoreTuple { - &self.peers - } - - pub fn jwt_service(&self) -> &JsonWebTokenService { - &self.jwt_service - } - - pub fn metrics_client(&self) -> &GroupedMetricsClients { - self.clients.victoria_metrics_clients.as_ref().unwrap() - } - - #[cfg(test)] - pub fn metrics_client_mut(&mut self) -> &mut GroupedMetricsClients { - self.clients.victoria_metrics_clients.as_mut().unwrap() - } - - pub fn log_ingest_clients(&self) -> &LogIngestClients { - self.clients.log_ingest_clients.as_ref().unwrap() - } - - pub(crate) fn bigquery_client(&self) -> Option<&TableWriteClient> { - self.clients.bigquery_client.as_ref() - } - - pub(crate) fn peer_identities(&self) -> &HashMap> { - &self.peer_identities - } - - pub fn chain_set(&self) -> HashSet { - self.peers.validators.read().keys().cloned().collect() - } - - pub fn log_env_map(&self) -> &HashMap> { - &self.log_env_map - } - - #[cfg(test)] - pub fn log_env_map_mut(&mut self) -> &mut HashMap> { - &mut self.log_env_map - } -} diff --git a/crates/aptos-telemetry-service/src/custom_event.rs b/crates/aptos-telemetry-service/src/custom_event.rs deleted file mode 100644 index e4ffba5f79796..0000000000000 --- a/crates/aptos-telemetry-service/src/custom_event.rs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - auth::with_auth, - context::Context, - debug, error, - errors::{CustomEventIngestError, ServiceError}, - metrics::BIG_QUERY_BACKEND_REQUEST_DURATION, - types::{ - auth::Claims, - common::{EventIdentity, NodeType}, - telemetry::{BigQueryRow, TelemetryDump}, - }, -}; -use anyhow::anyhow; -use aptos_types::PeerId; -use gcp_bigquery_client::model::table_data_insert_all_request::TableDataInsertAllRequest; -use serde_json::json; -use std::{str::FromStr, time::Duration}; -use tokio::time::Instant; -use warp::{filters::BoxedFilter, hyper::StatusCode, reject, reply, Filter, Rejection, Reply}; - -pub fn custom_event_ingest(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("ingest" / "custom-event") - .and(warp::post()) - .and(context.clone().filter()) - .and(with_auth(context, vec![ - NodeType::Validator, - NodeType::ValidatorFullNode, - NodeType::PublicFullNode, - NodeType::Unknown, - NodeType::UnknownValidator, - NodeType::UnknownFullNode, - ])) - .and(warp::body::json()) - .and_then(handle_custom_event) - .boxed() -} - -fn validate_custom_event_body( - claims: &Claims, - body: &TelemetryDump, -) -> anyhow::Result<(), Rejection> { - let body_peer_id = PeerId::from_str(&body.user_id).map_err(|_| { - reject::custom(ServiceError::bad_request( - CustomEventIngestError::InvalidEvent(body.user_id.clone(), claims.peer_id).into(), - )) - })?; - if body_peer_id != claims.peer_id { - return Err(reject::custom(ServiceError::bad_request( - CustomEventIngestError::InvalidEvent(body.user_id.clone(), claims.peer_id).into(), - ))); - } - - if body.events.is_empty() { - return Err(reject::custom(ServiceError::bad_request( - CustomEventIngestError::EmptyPayload.into(), - ))); - } - - Ok(()) -} - -pub(crate) async fn handle_custom_event( - context: Context, - claims: Claims, - body: TelemetryDump, -) -> anyhow::Result { - validate_custom_event_body(&claims, &body)?; - - let mut insert_request = TableDataInsertAllRequest::new(); - - let telemetry_event = &body.events[0]; - let event_params: Vec = telemetry_event - .params - .iter() - .map(|(k, v)| { - json!({ - "key": k, - "value": v - }) - }) - .collect(); - - let duration = - Duration::from_micros(body.timestamp_micros.as_str().parse::().map_err(|_| { - ServiceError::bad_request( - CustomEventIngestError::InvalidTimestamp(body.timestamp_micros).into(), - ) - })?); - - let row = BigQueryRow { - event_identity: EventIdentity::from(claims), - event_name: telemetry_event.name.clone(), - event_timestamp: duration.as_secs(), - event_params, - }; - - insert_request.add_row(None, &row).map_err(|e| { - error!("unable to create row: {}", e); - ServiceError::internal(CustomEventIngestError::from(e).into()) - })?; - - let start_timer = Instant::now(); - - context - .bigquery_client() - .ok_or_else(|| { - error!("big query client is not configured"); - ServiceError::internal( - CustomEventIngestError::from(anyhow!("BQ client is not configured")).into(), - ) - })? - .insert_all(insert_request) - .await - .map_err(|e| { - BIG_QUERY_BACKEND_REQUEST_DURATION - .with_label_values(&["request_error"]) - .observe(start_timer.elapsed().as_millis() as f64); - error!("unable to insert row into bigquery: {}", e); - ServiceError::internal(CustomEventIngestError::from(e).into()) - }) - .and_then(|result| { - if let Some(err) = result.insert_errors { - BIG_QUERY_BACKEND_REQUEST_DURATION - .with_label_values(&["insert_error"]) - .observe(start_timer.elapsed().as_secs_f64()); - Err(ServiceError::bad_request( - CustomEventIngestError::from(err[0].clone()).into(), - )) - } else { - BIG_QUERY_BACKEND_REQUEST_DURATION - .with_label_values(&["success"]) - .observe(start_timer.elapsed().as_secs_f64()); - Ok(result) - } - })?; - - debug!("row inserted succeefully: {:?}", &row); - - Ok(reply::with_status(reply::reply(), StatusCode::CREATED)) -} - -#[cfg(test)] -mod test { - use super::validate_custom_event_body; - use crate::types::{ - auth::Claims, - common::NodeType, - telemetry::{TelemetryDump, TelemetryEvent}, - }; - use aptos_types::{chain_id::ChainId, PeerId}; - use claims::assert_ok; - use std::collections::BTreeMap; - use uuid::Uuid; - - #[test] - fn test_validate_custom_event_body() { - let claims = Claims { - chain_id: ChainId::test(), - peer_id: PeerId::from_hex_literal("0x1234").unwrap(), - node_type: NodeType::Validator, - epoch: 1, - exp: 100, - iat: 200, - run_uuid: Uuid::new_v4(), - }; - - let body = TelemetryDump { - client_id: String::new(), - user_id: String::from("0x1"), - timestamp_micros: String::new(), - events: Vec::new(), - }; - assert_eq!(format!("{:?}", validate_custom_event_body(&claims, &body)), "Err(Rejection(ServiceError { http_code: 400, error_code: CustomEventIngestError(InvalidEvent(\"0x1\", 0000000000000000000000000000000000000000000000000000000000001234)) }))"); - - let body = TelemetryDump { - client_id: String::new(), - user_id: String::from("0x1234"), - timestamp_micros: String::new(), - events: vec![TelemetryEvent { - name: "test".into(), - params: BTreeMap::new(), - }], - }; - assert_ok!(validate_custom_event_body(&claims, &body)); - - let body = TelemetryDump { - client_id: String::new(), - user_id: String::from("1234"), - timestamp_micros: String::new(), - events: vec![TelemetryEvent { - name: "test".into(), - params: BTreeMap::new(), - }], - }; - assert_ok!(validate_custom_event_body(&claims, &body)); - - let body = TelemetryDump { - client_id: String::new(), - user_id: String::from("0x00001234"), - timestamp_micros: String::new(), - events: vec![TelemetryEvent { - name: "test".into(), - params: BTreeMap::new(), - }], - }; - assert_ok!(validate_custom_event_body(&claims, &body)); - - let body = TelemetryDump { - client_id: String::new(), - user_id: String::from("00001234"), - timestamp_micros: String::new(), - events: vec![TelemetryEvent { - name: "test".into(), - params: BTreeMap::new(), - }], - }; - assert_ok!(validate_custom_event_body(&claims, &body)); - } -} diff --git a/crates/aptos-telemetry-service/src/errors.rs b/crates/aptos-telemetry-service/src/errors.rs deleted file mode 100644 index 1237740576008..0000000000000 --- a/crates/aptos-telemetry-service/src/errors.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::noise::NoiseError; -use aptos_rest_client::error::RestError; -use aptos_types::{chain_id::ChainId, PeerId}; -use debug_ignore::DebugIgnore; -use gcp_bigquery_client::{ - error::BQError, - model::table_data_insert_all_response_insert_errors::TableDataInsertAllResponseInsertErrors, -}; -use std::fmt::{self, Debug}; -use thiserror::Error as ThisError; -use warp::{http::StatusCode, reject::Reject}; - -#[derive(Debug, ThisError)] -pub(crate) enum AuthError { - #[error("invalid public key")] - InvalidServerPublicKey, - #[error("error performing noise handshake")] - NoiseHandshakeError(NoiseError), - #[error("public key not found in peer keys")] - PeerPublicKeyNotFound, - #[error("public key does not match identity")] - PublicKeyMismatch, - #[error("validator set unavailable for chain")] - ValidatorSetUnavailable, - #[error("unable to authenticate")] - CreateJwtError(DebugIgnore), -} - -impl From for AuthError { - fn from(val: jsonwebtoken::errors::Error) -> Self { - Self::CreateJwtError(DebugIgnore(val)) - } -} - -#[derive(Debug, ThisError)] -pub(crate) enum JwtAuthError { - #[error("invalid authorization token")] - InvalidAuthToken, - #[error("expired authorization token")] - ExpiredAuthToken, - #[error("access denied to this resource")] - AccessDenied, - #[error("invalid authorization header: {0}")] - InvalidAuthHeader(DebugIgnore), -} - -impl From for JwtAuthError { - fn from(val: String) -> Self { - Self::InvalidAuthHeader(DebugIgnore(val)) - } -} - -#[derive(Debug, ThisError)] -pub(crate) enum CustomEventIngestError { - #[error("user_id {0} in event does not match peer_id {1}")] - InvalidEvent(String, PeerId), - #[error("no events in payload")] - EmptyPayload, - #[error("invalid payload timestamp: {0}")] - InvalidTimestamp(String), - #[error("unable to insert row into big query")] - BigQueryClientError(DebugIgnore), - #[error("invalid payload schema: {0}")] - BigQueryInsertError(DebugIgnore), - #[error("{0}")] - Other(DebugIgnore), -} - -impl From for CustomEventIngestError { - fn from(err: BQError) -> Self { - Self::BigQueryClientError(DebugIgnore(err)) - } -} - -impl From for CustomEventIngestError { - fn from(err: TableDataInsertAllResponseInsertErrors) -> Self { - Self::BigQueryInsertError(DebugIgnore(err)) - } -} - -impl From for CustomEventIngestError { - fn from(err: anyhow::Error) -> Self { - Self::Other(DebugIgnore(err)) - } -} - -#[derive(Debug, ThisError)] -pub(crate) enum LogIngestError { - #[error( - "unexpected payload body. Payload should be an array of strings possibly in gzip format" - )] - UnexpectedPayloadBody, - #[error("unexpected content encoding. Supported encodings are: gzip")] - UnexpectedContentEncoding, - #[error("unable to ingest logs")] - IngestionError, - #[error("peer id forbidden from posting logs")] - Forbidden(PeerId), -} - -#[derive(Debug, ThisError)] -pub(crate) enum MetricsIngestError { - #[error("unable to ingest metrics")] - IngestionError, -} - -#[derive(Debug, ThisError)] -pub(crate) enum ValidatorCacheUpdateError { - #[error("invalid url")] - InvalidUrl, - #[error("request error")] - RestError(#[source] RestError), - #[error("both peer set empty")] - BothPeerSetEmpty, - #[error("validator set empty")] - ValidatorSetEmpty, - #[error("vfn set empty")] - VfnSetEmpty, -} - -#[derive(Debug, ThisError)] -pub(crate) enum ServiceErrorCode { - #[error("authentication error: {0}")] - AuthError(AuthError, ChainId), - #[error("custom event ingest error: {0}")] - CustomEventIngestError(#[from] CustomEventIngestError), - #[error("authorization error: {0}")] - JwtAuthError(#[from] JwtAuthError), - #[error("log ingest error: {0}")] - LogIngestError(#[from] LogIngestError), - #[error("metrics ingest error: {0}")] - MetricsIngestError(#[from] MetricsIngestError), -} - -#[derive(Debug)] -pub(crate) struct ServiceError { - http_code: StatusCode, - error_code: ServiceErrorCode, -} - -impl ServiceError { - pub(crate) fn new(http_code: StatusCode, error_code: ServiceErrorCode) -> Self { - Self { - http_code, - error_code, - } - } - - pub(crate) fn bad_request(error_code: ServiceErrorCode) -> Self { - Self::new(StatusCode::BAD_REQUEST, error_code) - } - - pub(crate) fn unauthorized(error_code: ServiceErrorCode) -> Self { - Self::new(StatusCode::UNAUTHORIZED, error_code) - } - - pub(crate) fn forbidden(error_code: ServiceErrorCode) -> Self { - Self::new(StatusCode::FORBIDDEN, error_code) - } - - pub(crate) fn internal(error_code: ServiceErrorCode) -> Self { - Self::new(StatusCode::INTERNAL_SERVER_ERROR, error_code) - } - - pub(crate) fn http_status_code(&self) -> StatusCode { - self.http_code - } - - #[cfg(test)] - pub(crate) fn error_as_string(&self) -> String { - self.error_code.to_string() - } - - pub(crate) fn error_code(&self) -> &ServiceErrorCode { - &self.error_code - } -} - -impl Reject for ServiceError {} - -impl fmt::Display for ServiceError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.http_code, self.error_code)?; - Ok(()) - } -} diff --git a/crates/aptos-telemetry-service/src/gcp_logger.rs b/crates/aptos-telemetry-service/src/gcp_logger.rs deleted file mode 100644 index bde9f1cf1e226..0000000000000 --- a/crates/aptos-telemetry-service/src/gcp_logger.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::constants::GCP_SERVICE_PROJECT_ID_ENV; -use std::env; - -pub fn gcp_trace_id() -> Option { - let current_span = tracing::Span::current(); - current_span - .field("trace_id") - .zip(env::var(GCP_SERVICE_PROJECT_ID_ENV).ok()) - .map(|(trace_id, project_id)| format!("projects/{}/traces/{}", project_id, trace_id)) -} - -/// Log at the `trace` level -#[macro_export] -macro_rules! trace { - ($($arg:tt)+) => { - $crate::log!(aptos_logger::Level::Trace, $($arg)+) - }; -} - -/// Log at the `debug` level -#[macro_export] -macro_rules! debug { - ($($arg:tt)+) => { - $crate::log!(aptos_logger::Level::Debug, $($arg)+) - }; -} - -/// Log at the `info` level -#[macro_export] -macro_rules! info { - ($($arg:tt)+) => { - $crate::log!(aptos_logger::Level::Info, $($arg)+) - }; -} - -/// Log at the `warn` level -#[macro_export] -macro_rules! warn { - ($($arg:tt)+) => { - $crate::log!(aptos_logger::Level::Warn, $($arg)+) - }; -} - -/// Log at the `error` level -#[macro_export] -macro_rules! error { - ($($arg:tt)+) => { - $crate::log!(aptos_logger::Level::Error, $($arg)+) - }; -} - -/// Log at the given level, it's recommended to use a specific level macro instead -#[macro_export] -macro_rules! log { - // Entry, Log Level + stuff - ($level:expr, $($args:tt)+) => {{ - if let Some(trace_id) = $crate::gcp_logger::gcp_trace_id() { - aptos_logger::log!($level, "logging.googleapis.com/trace"=%trace_id, $($args)+) - } else { - aptos_logger::log!($level, $($args)+) - } - }}; -} diff --git a/crates/aptos-telemetry-service/src/index.rs b/crates/aptos-telemetry-service/src/index.rs deleted file mode 100644 index 5b273b9ed75fb..0000000000000 --- a/crates/aptos-telemetry-service/src/index.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - auth, - constants::GCP_CLOUD_TRACE_CONTEXT_HEADER, - context::Context, - custom_event, debug, - errors::ServiceError, - log_ingest, - metrics::SERVICE_ERROR_COUNTS, - prometheus_push_metrics, remote_config, - types::response::{ErrorResponse, IndexResponse}, -}; -use std::convert::Infallible; -use warp::{ - body::BodyDeserializeError, - filters::BoxedFilter, - http::StatusCode, - reject::{ - InvalidHeader, LengthRequired, MethodNotAllowed, PayloadTooLarge, UnsupportedMediaType, - }, - reply, Filter, Rejection, Reply, -}; - -pub fn routes( - context: Context, -) -> impl Filter + Clone { - let v1_api_prefix = warp::path!("api" / "v1" / ..); - - let v1_api = v1_api_prefix.and( - index(context.clone()) - .or(auth::check_chain_access(context.clone())) - .or(auth::auth(context.clone())) - .or(custom_event::custom_event_ingest(context.clone())) - .or(prometheus_push_metrics::metrics_ingest(context.clone())) - .or(log_ingest::log_ingest(context.clone())) - .or(remote_config::telemetry_log_env(context)), - ); - - v1_api - .recover(handle_rejection) - .with(warp::trace::trace(|info| { - let trace_id = info.request_headers() - .get(GCP_CLOUD_TRACE_CONTEXT_HEADER) - .and_then(|header_value| header_value.to_str().ok().and_then(|trace_value| trace_value.split_once('/').map(|parts| parts.0))) - .unwrap_or_default(); - let span = tracing::debug_span!("request", method=%info.method(), path=%info.path(), trace_id=trace_id); - span - })) -} - -fn index(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path::end() - .and(warp::get()) - .and(context.filter()) - .and_then(handle_index) - .boxed() -} - -async fn handle_index(context: Context) -> anyhow::Result { - let resp_payload = IndexResponse { - public_key: context.noise_config().public_key(), - }; - Ok(reply::json(&resp_payload)) -} - -pub async fn handle_rejection(err: Rejection) -> std::result::Result { - let code; - let body; - - if let Some(error) = err.find::() { - code = error.http_status_code(); - body = reply::json(&ErrorResponse::from(error)); - - SERVICE_ERROR_COUNTS - .with_label_values(&[&format!("{:?}", error.error_code())]) - .inc(); - } else if err.is_not_found() { - code = StatusCode::NOT_FOUND; - body = reply::json(&ErrorResponse::new(code, "Not Found".to_owned())); - } else if let Some(cause) = err.find::() { - code = StatusCode::BAD_REQUEST; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else if let Some(cause) = err.find::() { - code = StatusCode::BAD_REQUEST; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else if let Some(cause) = err.find::() { - code = StatusCode::LENGTH_REQUIRED; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else if let Some(cause) = err.find::() { - code = StatusCode::PAYLOAD_TOO_LARGE; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else if let Some(cause) = err.find::() { - code = StatusCode::UNSUPPORTED_MEDIA_TYPE; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else if let Some(cause) = err.find::() { - code = StatusCode::METHOD_NOT_ALLOWED; - body = reply::json(&ErrorResponse::new(code, cause.to_string())); - } else { - code = StatusCode::INTERNAL_SERVER_ERROR; - body = reply::json(&ErrorResponse::new( - code, - format!("unexpected error: {:?}", err), - )); - } - - debug!("returning an error with status code {}: {:?}", code, err); - - Ok(reply::with_status(body, code).into_response()) -} diff --git a/crates/aptos-telemetry-service/src/jwt_auth.rs b/crates/aptos-telemetry-service/src/jwt_auth.rs deleted file mode 100644 index ea445cd936843..0000000000000 --- a/crates/aptos-telemetry-service/src/jwt_auth.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - context::{Context, JsonWebTokenService}, - error, - errors::{JwtAuthError, ServiceError}, - types::{auth::Claims, common::NodeType}, -}; -use aptos_types::{chain_id::ChainId, PeerId}; -use chrono::Utc; -use jsonwebtoken::{errors::Error, TokenData}; -use uuid::Uuid; -use warp::{reject, Rejection}; - -const BEARER: &str = "BEARER "; - -pub fn create_jwt_token( - jwt_service: &JsonWebTokenService, - chain_id: ChainId, - peer_id: PeerId, - node_type: NodeType, - epoch: u64, - uuid: Uuid, -) -> Result { - let issued = Utc::now().timestamp(); - let expiration = Utc::now() - .checked_add_signed(chrono::Duration::minutes(60)) - .expect("valid timestamp") - .timestamp(); - - let claims = Claims { - chain_id, - peer_id, - node_type, - epoch, - exp: expiration as usize, - iat: issued as usize, - run_uuid: uuid, - }; - jwt_service.encode(claims) -} - -pub async fn authorize_jwt( - token: String, - context: Context, - allow_roles: Vec, -) -> anyhow::Result { - let decoded: TokenData = context.jwt_service().decode(&token).map_err(|e| { - error!("unable to authorize jwt token: {}", e); - reject::custom(ServiceError::unauthorized( - JwtAuthError::InvalidAuthToken.into(), - )) - })?; - let claims = decoded.claims; - - let current_epoch = match context.peers().validators().read().get(&claims.chain_id) { - Some(info) => info.0, - None => { - return Err(reject::custom(ServiceError::unauthorized( - JwtAuthError::ExpiredAuthToken.into(), - ))); - }, - }; - - if !allow_roles.contains(&claims.node_type) { - return Err(reject::custom(ServiceError::forbidden( - JwtAuthError::AccessDenied.into(), - ))); - } - - if claims.epoch == current_epoch && claims.exp > Utc::now().timestamp() as usize { - Ok(claims) - } else { - Err(reject::custom(ServiceError::unauthorized( - JwtAuthError::ExpiredAuthToken.into(), - ))) - } -} - -pub async fn jwt_from_header(auth_header: Option) -> anyhow::Result { - let auth_header = match auth_header { - Some(v) => v, - None => { - return Err(reject::custom(ServiceError::unauthorized( - JwtAuthError::from("bearer token missing".to_owned()).into(), - ))) - }, - }; - let auth_header = auth_header.split(',').next().unwrap_or_default(); - if !auth_header - .get(..BEARER.len()) - .unwrap_or_default() - .eq_ignore_ascii_case(BEARER) - { - return Err(reject::custom(ServiceError::unauthorized( - JwtAuthError::from("malformed bearer token".to_owned()).into(), - ))); - } - Ok(auth_header - .get(BEARER.len()..) - .unwrap_or_default() - .to_owned()) -} - -#[cfg(test)] -mod tests { - - use super::{super::tests::test_context, *}; - use std::collections::HashMap; - use warp::hyper::StatusCode; - - #[tokio::test] - async fn jwt_from_header_valid_bearer() { - assert_eq!( - jwt_from_header(Some("Bearer token".into())).await.unwrap(), - "token" - ); - - assert_eq!( - jwt_from_header(Some("bearer token".into())).await.unwrap(), - "token" - ); - - assert_eq!( - jwt_from_header(Some("BEARER token".into())).await.unwrap(), - "token" - ); - } - - #[tokio::test] - async fn jwt_from_header_invalid_bearer() { - let jwt = jwt_from_header(None).await; - assert!(jwt.is_err()); - - let jwt = jwt_from_header(Some("Bear token".into())).await; - assert!(jwt.is_err()); - - let jwt = jwt_from_header(Some("".into())).await; - assert!(jwt.is_err()); - - let jwt = jwt_from_header(Some("Bear".into())).await; - assert!(jwt.is_err()); - - let jwt = jwt_from_header(Some("BEARER: token".into())).await; - assert!(jwt.is_err()); - } - - #[tokio::test] - async fn test_authoize_jwt() { - let test_context = test_context::new_test_context().await; - { - test_context - .inner - .peers() - .validators() - .write() - .insert(ChainId::new(25), (10, HashMap::new())); - } - let token = create_jwt_token( - test_context.inner.jwt_service(), - ChainId::new(25), - PeerId::random(), - NodeType::Validator, - 10, - Uuid::default(), - ) - .unwrap(); - let result = - authorize_jwt(token, test_context.inner.clone(), vec![NodeType::Validator]).await; - assert!(result.is_ok()); - - let token = create_jwt_token( - test_context.inner.jwt_service(), - ChainId::new(25), - PeerId::random(), - NodeType::ValidatorFullNode, - 10, - Uuid::default(), - ) - .unwrap(); - let result = authorize_jwt(token, test_context.inner, vec![NodeType::Validator]).await; - assert!(result.is_err()); - - let rejection = result.err().unwrap(); - let service_error = rejection.find::().unwrap(); - assert_eq!(service_error.http_status_code(), StatusCode::FORBIDDEN); - assert_eq!( - service_error.error_as_string(), - "authorization error: access denied to this resource" - ); - } -} diff --git a/crates/aptos-telemetry-service/src/lib.rs b/crates/aptos-telemetry-service/src/lib.rs deleted file mode 100644 index 3128e5dbd547d..0000000000000 --- a/crates/aptos-telemetry-service/src/lib.rs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - clients::{big_query, humio, victoria_metrics_api::Client as MetricsClient}, - context::{ClientTuple, Context, JsonWebTokenService, LogIngestClients, PeerStoreTuple}, - index::routes, - metrics::PrometheusExporter, - validator_cache::PeerSetCacheUpdater, -}; -use aptos_crypto::{x25519, ValidCryptoMaterialStringExt}; -use aptos_types::{chain_id::ChainId, PeerId}; -use clap::Parser; -use context::GroupedMetricsClients; -use gcp_bigquery_client::Client as BigQueryClient; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - convert::Infallible, - env, - fs::File, - io::Read, - net::SocketAddr, - path::PathBuf, - sync::Arc, - time::Duration, -}; -use types::common::ChainCommonName; -use warp::{Filter, Reply}; - -mod auth; -mod clients; -mod constants; -mod context; -mod custom_event; -mod errors; -mod gcp_logger; -mod index; -mod jwt_auth; -mod log_ingest; -mod metrics; -mod prometheus_push_metrics; -mod remote_config; -#[cfg(test)] -pub(crate) mod tests; -pub mod types; -mod validator_cache; - -#[derive(Clone, Debug, Parser)] -#[clap(name = "Aptos Telemetry Service", author, version)] -pub struct AptosTelemetryServiceArgs { - #[clap(short = 'f', long, value_parser)] - config_path: PathBuf, -} - -impl AptosTelemetryServiceArgs { - pub async fn run(self) { - // Load the config file - let config = - TelemetryServiceConfig::load(self.config_path.clone()).unwrap_or_else(|error| { - panic!( - "Failed to load config file: {:?}. Error: {:?}", - self.config_path, error - ) - }); - info!("Using config {:?}", &config); - - let server_private_key = x25519::PrivateKey::from_encoded_string( - env::var("SERVER_PRIVATE_KEY") - .expect("environment variable SERVER_PRIVATE_KEY must be set") - .as_str(), - ) - .expect("unable to form x25519::Private key from environment variable SERVER_PRIVATE_KEY"); - - let bigquery_client = BigQueryClient::from_service_account_key_file( - env::var("GOOGLE_APPLICATION_CREDENTIALS") - .expect("environment variable GOOGLE_APPLICATION_CREDENTIALS must be set") - .as_str(), - ) - .await; - let bigquery_client = big_query::TableWriteClient::new( - bigquery_client, - config.custom_event_config.project_id.clone(), - config.custom_event_config.dataset_id.clone(), - config.custom_event_config.table_id.clone(), - ); - - let metrics_clients: GroupedMetricsClients = config.metrics_endpoints_config.clone().into(); - - let telemetry_metrics_client = metrics_clients - .telemetry_service_metrics_clients - .values() - .next() - .cloned() - .unwrap(); - - let log_ingest_clients: LogIngestClients = config.humio_ingest_config.clone().into(); - - let jwt_service = JsonWebTokenService::from_base64_secret( - env::var("JWT_SIGNING_KEY") - .expect("environment variable JWT_SIGNING_KEY must be set") - .as_str(), - ); - - let validators = Arc::new(aptos_infallible::RwLock::new(HashMap::new())); - let validator_fullnodes = Arc::new(aptos_infallible::RwLock::new(HashMap::new())); - let public_fullnodes = config.pfn_allowlist.clone(); - - let context = Context::new( - server_private_key, - PeerStoreTuple::new( - validators.clone(), - validator_fullnodes.clone(), - public_fullnodes, - ), - ClientTuple::new( - Some(bigquery_client), - Some(metrics_clients), - Some(log_ingest_clients), - ), - jwt_service, - config.log_env_map.clone(), - config.peer_identities.clone(), - ); - - PeerSetCacheUpdater::new( - validators, - validator_fullnodes, - config.trusted_full_node_addresses.clone(), - Duration::from_secs(config.update_interval), - ) - .run(); - - PrometheusExporter::new(telemetry_metrics_client).run(); - - Self::serve(&config, routes(context)).await; - } - - async fn serve(config: &TelemetryServiceConfig, routes: F) - where - F: Filter + Clone + Sync + Send + 'static, - F::Extract: Reply, - { - match &config.tls_cert_path { - None => warp::serve(routes).bind(config.address).await, - Some(cert_path) => { - warp::serve(routes) - .tls() - .cert_path(cert_path) - .key_path(config.tls_key_path.as_ref().unwrap()) - .bind(config.address) - .await - }, - }; - } -} - -/// Per metric endpoint configuration. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct MetricsEndpoint { - /// Map of endpoint canonical name to Urls - endpoint_urls: HashMap, - /// Environment variable that holds the secrets - keys_env_var: String, -} - -impl MetricsEndpoint { - #[cfg(test)] - fn default_for_test() -> Self { - Self { - endpoint_urls: HashMap::new(), - keys_env_var: "".into(), - } - } - - fn make_client(&self) -> HashMap { - let secrets: HashMap = - serde_json::from_str(&env::var(&self.keys_env_var).unwrap_or_else(|_| { - panic!( - "environment variable {} must be set and be a map of endpoint names to token", - self.keys_env_var.clone() - ) - })) - .unwrap_or_else(|_| { - panic!( - "environment variable {} must be a map of name to secret", - self.keys_env_var - ) - }); - - self.endpoint_urls - .iter() - .map(|(name, url)| { - let secret = secrets.get(name).unwrap_or_else(|| { - panic!( - "environment variable {} is missing secret for {}", - self.keys_env_var.clone(), - name, - ) - }); - ( - name.clone(), - MetricsClient::new(url.clone(), secret.clone()), - ) - }) - .collect() - } -} - -/// Metrics endpoints configuration for metrics from -/// different datasources. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct MetricsEndpointsConfig { - pub telemetry_service_metrics: MetricsEndpoint, - pub ingest_metrics: MetricsEndpoint, - pub untrusted_ingest_metrics: MetricsEndpoint, -} - -impl MetricsEndpointsConfig { - #[cfg(test)] - fn default_for_test() -> Self { - Self { - telemetry_service_metrics: MetricsEndpoint::default_for_test(), - ingest_metrics: MetricsEndpoint::default_for_test(), - untrusted_ingest_metrics: MetricsEndpoint::default_for_test(), - } - } -} - -/// A single log ingest endpoint config -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct LogIngestEndpoint { - pub endpoint_url: Url, - pub key_env_var: String, -} - -impl LogIngestEndpoint { - #[cfg(test)] - fn default_for_test() -> Self { - Self { - endpoint_url: Url::parse("test://test").unwrap(), - key_env_var: "".into(), - } - } - - fn make_client(&self) -> humio::IngestClient { - let secret = env::var(&self.key_env_var).unwrap_or_else(|_| { - panic!( - "environment variable {} must be set.", - self.key_env_var.clone() - ) - }); - - humio::IngestClient::new(self.endpoint_url.clone(), secret) - } -} - -/// Log ingest configuration for different sources -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct LogIngestConfig { - // Log endpoint for known nodes (nodes from validator set, whitelist, etc.) - pub known_logs_endpoint: LogIngestEndpoint, - // Log endpoint for unknown nodes - pub unknown_logs_endpoint: LogIngestEndpoint, - // Blacklisted peers from log ingestion - pub blacklist_peers: Option>, -} -impl LogIngestConfig { - #[cfg(test)] - pub(crate) fn default_for_test() -> LogIngestConfig { - Self { - known_logs_endpoint: LogIngestEndpoint::default_for_test(), - unknown_logs_endpoint: LogIngestEndpoint::default_for_test(), - blacklist_peers: None, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct TelemetryServiceConfig { - pub address: SocketAddr, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tls_cert_path: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tls_key_path: Option, - - pub trusted_full_node_addresses: HashMap, - pub update_interval: u64, - pub pfn_allowlist: HashMap>, - - pub custom_event_config: CustomEventConfig, - pub humio_ingest_config: LogIngestConfig, - - pub log_env_map: HashMap>, - pub peer_identities: HashMap>, - - pub metrics_endpoints_config: MetricsEndpointsConfig, -} - -impl TelemetryServiceConfig { - pub fn load(path: PathBuf) -> Result { - let mut file = File::open(&path).map_err(|e| { - anyhow::anyhow!( - "Unable to open file {}. Error: {}", - path.to_str().unwrap(), - e - ) - })?; - let mut contents = String::new(); - file.read_to_string(&mut contents).map_err(|e| { - anyhow::anyhow!( - "Unable to read file {}. Error: {}", - path.to_str().unwrap(), - e - ) - })?; - - serde_yaml::from_str(&contents).map_err(|e| { - anyhow::anyhow!( - "Unable to read yaml {}. Error: {}", - path.to_str().unwrap(), - e - ) - }) - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct CustomEventConfig { - pub project_id: String, - pub dataset_id: String, - pub table_id: String, -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - AptosTelemetryServiceArgs::command().debug_assert() -} diff --git a/crates/aptos-telemetry-service/src/log_ingest.rs b/crates/aptos-telemetry-service/src/log_ingest.rs deleted file mode 100644 index a0faaafdb753b..0000000000000 --- a/crates/aptos-telemetry-service/src/log_ingest.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - auth::with_auth, - clients::humio::{ - CHAIN_ID_TAG_NAME, EPOCH_FIELD_NAME, PEER_ID_FIELD_NAME, PEER_ROLE_TAG_NAME, - RUN_UUID_TAG_NAME, - }, - constants::MAX_CONTENT_LENGTH, - context::Context, - debug, error, - errors::{LogIngestError, ServiceError}, - metrics::LOG_INGEST_BACKEND_REQUEST_DURATION, - types::{auth::Claims, common::NodeType, humio::UnstructuredLog}, -}; -use flate2::bufread::GzDecoder; -use reqwest::{header::CONTENT_ENCODING, StatusCode}; -use std::collections::HashMap; -use tokio::time::Instant; -use warp::{filters::BoxedFilter, reject, reply, Buf, Filter, Rejection, Reply}; - -pub fn log_ingest(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("ingest" / "logs") - .and(warp::post()) - .and(context.clone().filter()) - .and(with_auth(context, vec![ - NodeType::Validator, - NodeType::ValidatorFullNode, - NodeType::PublicFullNode, - NodeType::UnknownFullNode, - NodeType::UnknownValidator, - ])) - .and(warp::header::optional(CONTENT_ENCODING.as_str())) - .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH)) - .and(warp::body::aggregate()) - .and_then(handle_log_ingest) - .boxed() -} - -pub async fn handle_log_ingest( - context: Context, - claims: Claims, - encoding: Option, - body: impl Buf, -) -> anyhow::Result { - debug!("handling log ingest"); - - if let Some(blacklist) = &context.log_ingest_clients().blacklist { - if blacklist.contains(&claims.peer_id) { - return Err(reject::custom(ServiceError::forbidden( - LogIngestError::Forbidden(claims.peer_id).into(), - ))); - } - } - - let client = match claims.node_type { - NodeType::Unknown | NodeType::UnknownValidator | NodeType::UnknownFullNode => { - &context.log_ingest_clients().unknown_logs_ingest_client - }, - _ => &context.log_ingest_clients().known_logs_ingest_client, - }; - - let log_messages: Vec = if let Some(encoding) = encoding { - if encoding.eq_ignore_ascii_case("gzip") { - let decoder = GzDecoder::new(body.reader()); - serde_json::from_reader(decoder).map_err(|e| { - debug!("unable to decode and deserialize body: {}", e); - ServiceError::bad_request(LogIngestError::UnexpectedPayloadBody.into()) - })? - } else { - return Err(reject::custom(ServiceError::bad_request( - LogIngestError::UnexpectedContentEncoding.into(), - ))); - } - } else { - serde_json::from_reader(body.reader()).map_err(|e| { - error!("unable to deserialize body: {}", e); - ServiceError::bad_request(LogIngestError::UnexpectedPayloadBody.into()) - })? - }; - - let mut fields = HashMap::new(); - fields.insert(PEER_ID_FIELD_NAME.into(), claims.peer_id.to_string()); - fields.insert(EPOCH_FIELD_NAME.into(), claims.epoch.to_string()); - - let mut tags = HashMap::new(); - let chain_name = if claims.chain_id.id() == 3 { - format!("{}", claims.chain_id.id()) - } else { - format!("{}", claims.chain_id) - }; - tags.insert(CHAIN_ID_TAG_NAME.into(), chain_name); - tags.insert(PEER_ROLE_TAG_NAME.into(), claims.node_type.to_string()); - tags.insert(RUN_UUID_TAG_NAME.into(), claims.run_uuid.to_string()); - - let unstructured_log = UnstructuredLog { - fields, - tags, - messages: log_messages, - }; - - debug!("ingesting to humio: {:?}", unstructured_log); - - let start_timer = Instant::now(); - - let res = client.ingest_unstructured_log(unstructured_log).await; - - match res { - Ok(res) => { - LOG_INGEST_BACKEND_REQUEST_DURATION - .with_label_values(&[res.status().as_str()]) - .observe(start_timer.elapsed().as_secs_f64()); - if res.status().is_success() { - debug!("log ingested into humio succeessfully"); - } else { - error!( - "humio log ingestion failed: {}", - res.error_for_status().err().unwrap() - ); - return Err(reject::custom(ServiceError::bad_request( - LogIngestError::IngestionError.into(), - ))); - } - }, - Err(err) => { - LOG_INGEST_BACKEND_REQUEST_DURATION - .with_label_values(&["Unknown"]) - .observe(start_timer.elapsed().as_secs_f64()); - error!("error sending log ingest request: {}", err); - return Err(reject::custom(ServiceError::bad_request( - LogIngestError::IngestionError.into(), - ))); - }, - } - - Ok(reply::with_status(reply::reply(), StatusCode::CREATED)) -} diff --git a/crates/aptos-telemetry-service/src/main.rs b/crates/aptos-telemetry-service/src/main.rs deleted file mode 100644 index 34e9a77099d4e..0000000000000 --- a/crates/aptos-telemetry-service/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![forbid(unsafe_code)] - -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_telemetry_service::AptosTelemetryServiceArgs; -use clap::Parser; - -#[tokio::main] -async fn main() { - aptos_logger::Logger::new().init(); - AptosTelemetryServiceArgs::parse().run().await; -} diff --git a/crates/aptos-telemetry-service/src/metrics.rs b/crates/aptos-telemetry-service/src/metrics.rs deleted file mode 100644 index 324bea3fe2f5b..0000000000000 --- a/crates/aptos-telemetry-service/src/metrics.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - clients::victoria_metrics_api, - constants::{ - GCP_CLOUD_RUN_INSTANCE_ID_ENV, GCP_CLOUD_RUN_REVISION_ENV, GCP_CLOUD_RUN_SERVICE_ENV, - GCP_SERVICE_PROJECT_ID_ENV, - }, - debug, error, -}; -use anyhow::anyhow; -use aptos_metrics_core::{ - register_histogram_vec, register_int_counter_vec, HistogramVec, IntCounterVec, -}; -use flate2::{write::GzEncoder, Compression}; -use once_cell::sync::Lazy; -use std::{env, io::Write, time::Duration}; -use tokio::time::{self, Instant}; -use warp::hyper::body::Bytes; - -const METRICS_EXPORT_FREQUENCY: Duration = Duration::from_secs(15); - -pub(crate) static SERVICE_ERROR_COUNTS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "telemetry_web_service_internal_error_counts", - "Service errors returned by the telemety web service by error_code", - &["error_code"] - ) - .unwrap() -}); - -pub(crate) static LOG_INGEST_BACKEND_REQUEST_DURATION: Lazy = Lazy::new(|| { - register_histogram_vec!( - "telemetry_web_service_log_ingest_backend_request_duration", - "Number of log ingest backend requests by response code", - &["response_code"] - ) - .unwrap() -}); - -pub(crate) static METRICS_INGEST_BACKEND_REQUEST_DURATION: Lazy = Lazy::new(|| { - register_histogram_vec!( - "telemetry_web_service_metrics_ingest_backend_request_duration", - "Number of metrics ingest backend requests by response code", - &["peer_id", "endpoint_name", "response_code"] - ) - .unwrap() -}); - -pub(crate) static BIG_QUERY_BACKEND_REQUEST_DURATION: Lazy = Lazy::new(|| { - register_histogram_vec!( - "telemetry_web_service_big_query_backend_request_duration", - "Number of big query backend requests by response kind", - &["kind"] - ) - .unwrap() -}); - -pub(crate) static METRICS_EXPORT_DURATION: Lazy = Lazy::new(|| { - register_histogram_vec!( - "telemetry_web_service_metrics_export_duration", - "Number of metrics export requests by response code", - &["response_code"] - ) - .unwrap() -}); - -pub(crate) static VALIDATOR_SET_UPDATE_SUCCESS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "telemetry_web_service_validator_set_update_success_count", - "Number of metrics validator set update successes", - &["chain_id"] - ) - .unwrap() -}); - -pub(crate) static VALIDATOR_SET_UPDATE_FAILED_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "telemetry_web_service_validator_set_update_failed_count", - "Number of metrics validator set update failures", - &["chain_id", "error_code"] - ) - .unwrap() -}); - -pub struct PrometheusExporter { - project_id: String, - service: String, - revision: String, - instance_id: String, - client: victoria_metrics_api::Client, -} - -impl PrometheusExporter { - pub fn new(client: victoria_metrics_api::Client) -> Self { - let service = env::var(GCP_CLOUD_RUN_SERVICE_ENV).unwrap_or_else(|_| "Unknown".into()); - let revision = env::var(GCP_CLOUD_RUN_REVISION_ENV).unwrap_or_else(|_| "Unknown".into()); - let instance_id = - env::var(GCP_CLOUD_RUN_INSTANCE_ID_ENV).unwrap_or_else(|_| "Unknown".into()); - let project_id = env::var(GCP_SERVICE_PROJECT_ID_ENV).unwrap_or_else(|_| "Unknown".into()); - - Self { - project_id, - service, - revision, - instance_id, - client, - } - } - - pub fn run(self) { - tokio::spawn(async move { - let mut interval = time::interval(METRICS_EXPORT_FREQUENCY); - loop { - interval.tick().await; - match self.gather_and_send().await { - Ok(()) => debug!("service metrics exported successfully"), - Err(err) => error!("error exporting metrics {}", err), - } - } - }); - } - - async fn gather_and_send(&self) -> Result<(), anyhow::Error> { - let scraped_metrics = prometheus::TextEncoder::new() - .encode_to_string(&prometheus::default_registry().gather()) - .map_err(|e| anyhow!("text encoding error {}", e))?; - - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - gzip_encoder - .write_all(scraped_metrics.as_bytes()) - .map_err(|e| anyhow!("gzip encoding error {}", e))?; - let metrics_body = gzip_encoder.finish()?; - - let extra_labels = vec![ - "namespace=telemetry-web-service".into(), - format!("cloud_run_revision={}", self.revision), - format!("cloud_run_service={}", self.service), - format!("cloud_run_container_id={}", self.instance_id), - format!("gcp_project_id={}", self.project_id), - ]; - - let start_timer = Instant::now(); - - let res = self - .client - .post_prometheus_metrics(Bytes::from(metrics_body), extra_labels, "gzip".into()) - .await; - - match res { - Ok(res) => { - METRICS_EXPORT_DURATION - .with_label_values(&[res.status().as_str()]) - .observe(start_timer.elapsed().as_millis() as f64); - if !res.status().is_success() { - return Err(anyhow!( - "remote write failed to victoria_metrics: {}", - res.error_for_status().err().unwrap() - )); - } - }, - Err(err) => { - METRICS_EXPORT_DURATION - .with_label_values(&["Unknown"]) - .observe(start_timer.elapsed().as_millis() as f64); - return Err(anyhow!("error sending remote write request: {}", err)); - }, - } - - Ok(()) - } -} diff --git a/crates/aptos-telemetry-service/src/prometheus_push_metrics.rs b/crates/aptos-telemetry-service/src/prometheus_push_metrics.rs deleted file mode 100644 index 0e17c6f9d67b3..0000000000000 --- a/crates/aptos-telemetry-service/src/prometheus_push_metrics.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - auth::with_auth, - constants::MAX_CONTENT_LENGTH, - context::Context, - debug, error, - errors::{MetricsIngestError, ServiceError}, - metrics::METRICS_INGEST_BACKEND_REQUEST_DURATION, - types::{auth::Claims, common::NodeType}, -}; -use reqwest::{header::CONTENT_ENCODING, StatusCode}; -use std::time::Duration; -use tokio::time::Instant; -use warp::{filters::BoxedFilter, hyper::body::Bytes, reject, reply, Filter, Rejection, Reply}; - -const MAX_METRICS_POST_WAIT_DURATION_SECS: u64 = 5; - -pub fn metrics_ingest(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("ingest" / "metrics") - .and(warp::post()) - .and(context.clone().filter()) - .and(with_auth(context, vec![ - NodeType::Validator, - NodeType::ValidatorFullNode, - NodeType::PublicFullNode, - NodeType::UnknownValidator, - NodeType::UnknownFullNode, - ])) - .and(warp::header::optional(CONTENT_ENCODING.as_str())) - .and(warp::body::content_length_limit(MAX_CONTENT_LENGTH)) - .and(warp::body::bytes()) - .and_then(handle_metrics_ingest) - .boxed() -} - -pub async fn handle_metrics_ingest( - context: Context, - claims: Claims, - encoding: Option, - metrics_body: Bytes, -) -> anyhow::Result { - debug!("handling prometheus metrics ingest"); - - let extra_labels = claims_to_extra_labels( - &claims, - context - .peer_identities() - .get(&claims.chain_id) - .and_then(|peers| peers.get(&claims.peer_id)), - ); - - let client = match claims.node_type { - NodeType::UnknownValidator | NodeType::UnknownFullNode => { - &context.metrics_client().untrusted_ingest_metrics_clients - }, - _ => &context.metrics_client().ingest_metrics_client, - }; - - let start_timer = Instant::now(); - - let post_futures = client.iter().map(|(name, client)| async { - let result = tokio::time::timeout( - Duration::from_secs(MAX_METRICS_POST_WAIT_DURATION_SECS), - client.post_prometheus_metrics( - metrics_body.clone(), - extra_labels.clone(), - encoding.clone().unwrap_or_default(), - ), - ) - .await; - - match result { - Ok(Ok(res)) => { - METRICS_INGEST_BACKEND_REQUEST_DURATION - .with_label_values(&[&claims.peer_id.to_string(), name, res.status().as_str()]) - .observe(start_timer.elapsed().as_secs_f64()); - if res.status().is_success() { - debug!("remote write to victoria metrics succeeded"); - } else { - error!( - "remote write failed to victoria_metrics for client {}: {}", - name.clone(), - res.error_for_status().err().unwrap() - ); - return Err(()); - } - }, - Ok(Err(err)) => { - METRICS_INGEST_BACKEND_REQUEST_DURATION - .with_label_values(&[&claims.peer_id.to_string(), name, "Unknown"]) - .observe(start_timer.elapsed().as_secs_f64()); - error!( - "error sending remote write request for client {}: {}", - name.clone(), - err - ); - return Err(()); - }, - Err(err) => { - error!( - "timed out sending remote write for client {}: {}", - name.clone(), - err - ); - return Err(()); - }, - } - Ok(()) - }); - - #[allow(clippy::unnecessary_fold)] - if futures::future::join_all(post_futures) - .await - .iter() - .all(|result| result.is_err()) - { - return Err(reject::custom(ServiceError::internal( - MetricsIngestError::IngestionError.into(), - ))); - } - - Ok(reply::with_status(reply::reply(), StatusCode::CREATED)) -} - -fn claims_to_extra_labels(claims: &Claims, common_name: Option<&String>) -> Vec { - let chain_name = if claims.chain_id.id() == 3 { - format!("chain_name={}", claims.chain_id.id()) - } else { - format!("chain_name={}", claims.chain_id) - }; - let pod_name = if let Some(common_name) = common_name { - format!( - "kubernetes_pod_name=peer_id:{}//{}", - common_name, - claims.peer_id.to_hex_literal() - ) - } else { - // for community nodes we cannot determine which pod name they run in (or whether they run in k8s at all), - // so we use the peer id as an approximation/replacement for pod_name - // This works well with our existing grafana dashboards - format!( - "kubernetes_pod_name=peer_id:{}", - claims.peer_id.to_hex_literal() - ) - }; - vec![ - format!("role={}", claims.node_type), - format!("metrics_source={}", "telemetry-service"), - chain_name, - format!("namespace={}", "telemetry-service"), - pod_name, - format!("run_uuid={}", claims.run_uuid), - ] -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{tests::test_context, MetricsClient}; - use aptos_types::{chain_id::ChainId, PeerId}; - use httpmock::MockServer; - use reqwest::Url; - use std::str::FromStr; - use uuid::Uuid; - - #[test] - fn verify_labels() { - let claims = claims_to_extra_labels( - &super::Claims { - chain_id: ChainId::new(25), - peer_id: PeerId::from_str("0x1").unwrap(), - node_type: NodeType::Validator, - epoch: 3, - exp: 123, - iat: 123, - run_uuid: Uuid::default(), - }, - Some(&String::from("test_name")), - ); - assert_eq!(claims, vec![ - "role=validator", - "metrics_source=telemetry-service", - "chain_name=25", - "namespace=telemetry-service", - "kubernetes_pod_name=peer_id:test_name//0x1", - &format!("run_uuid={}", Uuid::default()), - ]); - - let test_uuid = Uuid::new_v4(); - - let claims = claims_to_extra_labels( - &super::Claims { - chain_id: ChainId::new(25), - peer_id: PeerId::from_str("0x1").unwrap(), - node_type: NodeType::Validator, - epoch: 3, - exp: 123, - iat: 123, - run_uuid: test_uuid, - }, - None, - ); - assert_eq!(claims, vec![ - "role=validator", - "metrics_source=telemetry-service", - "chain_name=25", - "namespace=telemetry-service", - "kubernetes_pod_name=peer_id:0x1", - &format!("run_uuid={}", test_uuid), - ]); - } - - #[tokio::test] - async fn test_metrics_ingest_all_success() { - let mut test_context = test_context::new_test_context().await; - let claims = Claims::test(); - let body = Bytes::from_static(b"hello"); - - let server1 = MockServer::start(); - let mock1 = server1.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(200); - }); - - let server2 = MockServer::start(); - let mock2 = server2.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(200); - }); - - let clients = test_context.inner.metrics_client_mut(); - clients.ingest_metrics_client.insert( - "default1".into(), - MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()), - ); - clients.ingest_metrics_client.insert( - "default2".into(), - MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()), - ); - - let result = - handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await; - - mock1.assert(); - mock2.assert(); - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_metrics_ingest_partial_success() { - let mut test_context = test_context::new_test_context().await; - let claims = Claims::test(); - let body = Bytes::from_static(b"hello"); - - let server1 = MockServer::start(); - let mock1 = server1.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(200); - }); - - let server2 = MockServer::start(); - let mock2 = server2.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(500); - }); - - let clients = test_context.inner.metrics_client_mut(); - clients.ingest_metrics_client.insert( - "default1".into(), - MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()), - ); - clients.ingest_metrics_client.insert( - "default2".into(), - MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()), - ); - - let result = - handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await; - - mock1.assert(); - assert!(mock2.hits_async().await >= 1); - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_metrics_ingest_all_failure() { - let mut test_context = test_context::new_test_context().await; - let claims = Claims::test(); - let body = Bytes::from_static(b"hello"); - - let server1 = MockServer::start(); - let mock1 = server1.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(500); - }); - - let server2 = MockServer::start(); - let mock2 = server2.mock(|when, then| { - when.method("POST").path("/api/v1/import/prometheus"); - then.status(401); - }); - - let clients = test_context.inner.metrics_client_mut(); - clients.ingest_metrics_client.insert( - "default1".into(), - MetricsClient::new(Url::parse(&server1.base_url()).unwrap(), "token1".into()), - ); - clients.ingest_metrics_client.insert( - "default2".into(), - MetricsClient::new(Url::parse(&server2.base_url()).unwrap(), "token2".into()), - ); - - let result = - handle_metrics_ingest(test_context.inner, claims, Some("gzip".into()), body).await; - - assert!(mock1.hits_async().await >= 1); - mock2.assert(); - assert!(result.is_err()); - } -} diff --git a/crates/aptos-telemetry-service/src/remote_config.rs b/crates/aptos-telemetry-service/src/remote_config.rs deleted file mode 100644 index e2f256b9517e0..0000000000000 --- a/crates/aptos-telemetry-service/src/remote_config.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - auth::with_auth, - context::Context, - types::{auth::Claims, common::NodeType}, -}; -use warp::{filters::BoxedFilter, reply, Filter, Rejection, Reply}; - -pub fn telemetry_log_env(context: Context) -> BoxedFilter<(impl Reply,)> { - warp::path!("config" / "env" / "telemetry-log") - .and(warp::get()) - .and(with_auth(context.clone(), vec![ - NodeType::Validator, - NodeType::ValidatorFullNode, - NodeType::PublicFullNode, - ])) - .and(context.filter()) - .and_then(handle_telemetry_log_env) - .boxed() -} - -async fn handle_telemetry_log_env( - claims: Claims, - context: Context, -) -> Result { - let env: Option = context - .log_env_map() - .get(&claims.chain_id) - .and_then(|inner| inner.get(&claims.peer_id)) - .cloned(); - Ok(reply::json(&env)) -} - -#[cfg(test)] -mod tests { - use crate::{jwt_auth::create_jwt_token, tests::test_context, types::common::NodeType}; - use aptos_config::config::PeerSet; - use aptos_types::{chain_id::ChainId, PeerId}; - use std::collections::HashMap; - use uuid::Uuid; - - #[tokio::test] - async fn test_handle_telemetry_log_env() { - let log_level: String = String::from("debug,hyper=off"); - let peer_id = PeerId::random(); - let chain_id = ChainId::default(); - let epoch = 10; - let node_type = NodeType::Validator; - - let mut test_context = test_context::new_test_context().await; - test_context - .inner - .log_env_map_mut() - .insert(chain_id, HashMap::from([(peer_id, log_level.clone())])); - - test_context - .inner - .peers() - .validators() - .write() - .insert(chain_id, (epoch, PeerSet::default())); - - let jwt_token = create_jwt_token( - test_context.inner.jwt_service(), - chain_id, - peer_id, - node_type, - epoch, - Uuid::default(), - ) - .unwrap(); - - let value = test_context - .with_bearer_auth(jwt_token) - .get("/api/v1/config/env/telemetry-log") - .await; - - assert_eq!(value, log_level) - } -} diff --git a/crates/aptos-telemetry-service/src/tests/auth_test.rs b/crates/aptos-telemetry-service/src/tests/auth_test.rs deleted file mode 100644 index 42b3284ce3953..0000000000000 --- a/crates/aptos-telemetry-service/src/tests/auth_test.rs +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - context::JsonWebTokenService, - tests::test_context::new_test_context, - types::{ - auth::{AuthResponse, Claims}, - common::NodeType, - }, -}; -use aptos_config::config::{Peer, PeerRole, PeerSet, RoleType}; -use aptos_crypto::{ - noise, - noise::{InitiatorHandshakeState, NoiseConfig}, - x25519, Uniform, -}; -use aptos_types::{ - account_address, - chain_id::ChainId, - network_address::{ - DnsName, NetworkAddress, - Protocol::{Dns, Handshake, NoiseIK, Tcp}, - }, - PeerId, -}; -use serde_json::json; -use uuid::Uuid; - -fn init( - peer_role: PeerRole, -) -> ( - rand::rngs::ThreadRng, - NoiseConfig, - ChainId, - PeerId, - std::collections::HashMap, - Uuid, -) { - let mut rng = rand::thread_rng(); - let initiator_static = x25519::PrivateKey::generate(&mut rng); - let initiator_public_key = initiator_static.public_key(); - let initiator = noise::NoiseConfig::new(initiator_static); - let chain_id = ChainId::new(21); - let peer_id = account_address::from_identity_public_key(initiator_public_key); - let protocols = vec![ - Dns(DnsName::try_from("example.com".to_string()).unwrap()), - Tcp(1234), - NoiseIK(initiator_public_key), - Handshake(0), - ]; - let addr = NetworkAddress::from_protocols(protocols).unwrap(); - let peer = Peer::from_addrs(peer_role, vec![addr]); - let mut peer_set = PeerSet::new(); - peer_set.insert(peer_id, peer); - - let uuid = Uuid::new_v4(); - - (rng, initiator, chain_id, peer_id, peer_set, uuid) -} - -fn init_handshake( - rng: &mut (impl rand::RngCore + rand::CryptoRng), - chain_id: ChainId, - peer_id: PeerId, - server_public_key: x25519::PublicKey, - initiator: &NoiseConfig, -) -> (noise::InitiatorHandshakeState, Vec) { - // buffer to first noise handshake message - let mut client_noise_msg = vec![0; noise::handshake_init_msg_len(0)]; - - // build the prologue (chain_id | peer_id | public_key) - const CHAIN_ID_LENGTH: usize = 1; - const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; - const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; - let mut prologue = [0; PROLOGUE_SIZE]; - prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[chain_id.id()]); - prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(peer_id.as_ref()); - prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(server_public_key.as_slice()); - - // craft first handshake message (-> e, es, s, ss) - let initiator_state = initiator - .initiate_connection( - rng, - &prologue, - server_public_key, - None, - &mut client_noise_msg, - ) - .unwrap(); - - (initiator_state, client_noise_msg) -} - -fn finish_handshake( - jwt_service: &JsonWebTokenService, - initiator: &NoiseConfig, - initiator_state: InitiatorHandshakeState, - resp: serde_json::Value, -) -> jsonwebtoken::TokenData { - let resp: AuthResponse = serde_json::from_value(resp).unwrap(); - - let (response_payload, _) = initiator - .finalize_connection(initiator_state, resp.handshake_msg.as_slice()) - .unwrap(); - - let jwt = String::from_utf8(response_payload).unwrap(); - - jwt_service.decode(&jwt).unwrap() -} - -#[tokio::test] -async fn test_auth_validator_backwards_compat_uuid() { - let context = new_test_context().await; - let server_public_key = context.inner.noise_config().public_key(); - - let (mut rng, initiator, chain_id, peer_id, peer_set, _) = init(PeerRole::Validator); - - context - .inner - .peers() - .validators() - .write() - .insert(chain_id, (1, peer_set)); - - let (initiator_state, client_noise_msg) = - init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); - - let req = json!({ - "chain_id": chain_id, - "peer_id": peer_id, - "role_type": RoleType::Validator, - "server_public_key": server_public_key, - "handshake_msg": &client_noise_msg, - }); - let resp = context.post("/api/v1/auth", req).await; - - let decoded = finish_handshake( - context.inner.jwt_service(), - &initiator, - initiator_state, - resp, - ); - - assert_eq!(decoded.claims, Claims { - chain_id, - peer_id, - node_type: NodeType::Validator, - epoch: 1, - exp: decoded.claims.exp, - iat: decoded.claims.iat, - run_uuid: Uuid::default(), - },) -} - -#[tokio::test] -async fn test_auth_validator() { - let context = new_test_context().await; - let server_public_key = context.inner.noise_config().public_key(); - - let (mut rng, initiator, chain_id, peer_id, peer_set, run_uuid) = init(PeerRole::Validator); - - context - .inner - .peers() - .validators() - .write() - .insert(chain_id, (1, peer_set)); - - let (initiator_state, client_noise_msg) = - init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); - - let req = json!({ - "chain_id": chain_id, - "peer_id": peer_id, - "role_type": RoleType::Validator, - "server_public_key": server_public_key, - "handshake_msg": &client_noise_msg, - "run_uuid": run_uuid, - }); - let resp = context.post("/api/v1/auth", req).await; - - let decoded = finish_handshake( - context.inner.jwt_service(), - &initiator, - initiator_state, - resp, - ); - - assert_eq!(decoded.claims, Claims { - chain_id, - peer_id, - node_type: NodeType::Validator, - epoch: 1, - exp: decoded.claims.exp, - iat: decoded.claims.iat, - run_uuid, - },) -} - -#[tokio::test] -async fn test_auth_validatorfullnode() { - let context = new_test_context().await; - let server_public_key = context.inner.noise_config().public_key(); - - let (mut rng, initiator, chain_id, peer_id, peer_set, run_uuid) = - init(PeerRole::ValidatorFullNode); - - context - .inner - .peers() - .validator_fullnodes() - .write() - .insert(chain_id, (1, peer_set)); - - let (initiator_state, client_noise_msg) = - init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); - - let req = json!({ - "chain_id": chain_id, - "peer_id": peer_id, - "role_type": RoleType::FullNode, - "server_public_key": server_public_key, - "handshake_msg": &client_noise_msg, - "run_uuid": run_uuid, - }); - let resp = context.post("/api/v1/auth", req).await; - - let decoded = finish_handshake( - context.inner.jwt_service(), - &initiator, - initiator_state, - resp, - ); - - assert_eq!(decoded.claims, Claims { - chain_id, - peer_id, - node_type: NodeType::ValidatorFullNode, - epoch: 1, - exp: decoded.claims.exp, - iat: decoded.claims.iat, - run_uuid - },) -} - -#[tokio::test] -#[should_panic] -async fn test_auth_wrong_key() { - let context = new_test_context().await; - let server_public_key = context.inner.noise_config().public_key(); - - let mut rng = rand::thread_rng(); - let initiator_static = x25519::PrivateKey::generate(&mut rng); - let initiator_static2 = x25519::PrivateKey::generate(&mut rng); - let initiator_public_key = initiator_static.public_key(); - let initiator = noise::NoiseConfig::new(initiator_static); - let chain_id = ChainId::new(21); - let peer_id: PeerId = account_address::from_identity_public_key(initiator_public_key); - let protocols = vec![ - Dns(DnsName::try_from("example.com".to_string()).unwrap()), - Tcp(1234), - NoiseIK(initiator_static2.public_key()), - Handshake(0), - ]; - let addr = NetworkAddress::from_protocols(protocols).unwrap(); - let peer = Peer::from_addrs(PeerRole::Validator, vec![addr]); - let mut peer_set = PeerSet::new(); - peer_set.insert(peer_id, peer); - - context - .inner - .peers() - .validators() - .write() - .insert(chain_id, (1, peer_set)); - - let (initiator_state, client_noise_msg) = - init_handshake(&mut rng, chain_id, peer_id, server_public_key, &initiator); - - let req = json!({ - "chain_id": chain_id, - "peer_id": peer_id, - "role_type": RoleType::Validator, - "server_public_key": server_public_key, - "handshake_msg": client_noise_msg, - }); - let resp = context.post("/api/v1/auth", req).await; - - finish_handshake( - context.inner.jwt_service(), - &initiator, - initiator_state, - resp, - ); -} - -#[tokio::test] -async fn test_chain_access() { - let context = new_test_context().await; - let present_chain_id = ChainId::new(24); - let missing_chain_id = ChainId::new(32); - context - .inner - .peers() - .validators() - .write() - .insert(present_chain_id, (1, PeerSet::new())); - - let resp = context - .get(&format!("/api/v1/chain-access/{}", present_chain_id)) - .await; - assert!(resp.as_bool().unwrap()); - - let resp = context - .get(&format!("/api/v1/chain-access/{}", missing_chain_id)) - .await; - assert!(!resp.as_bool().unwrap()); -} diff --git a/crates/aptos-telemetry-service/src/tests/custom_event.rs b/crates/aptos-telemetry-service/src/tests/custom_event.rs deleted file mode 100644 index 61484abd82093..0000000000000 --- a/crates/aptos-telemetry-service/src/tests/custom_event.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::test_context::new_test_context; -use crate::{ - jwt_auth::create_jwt_token, - types::{ - common::NodeType, - telemetry::{TelemetryDump, TelemetryEvent}, - }, -}; -use aptos_config::config::PeerSet; -use aptos_types::{chain_id::ChainId, PeerId}; -use chrono::Utc; -use serde_json::json; -use std::collections::BTreeMap; -use uuid::Uuid; - -#[tokio::test] -async fn test_custom_event() { - let test_context = new_test_context().await; - let chain_id = ChainId::new(28); - let peer_id = PeerId::random(); - let node_type = NodeType::Validator; - let uuid = Uuid::new_v4(); - let epoch = 10; - - test_context - .inner - .peers() - .validators() - .write() - .insert(chain_id, (epoch, PeerSet::default())); - - let jwt_token = create_jwt_token( - test_context.inner.jwt_service(), - chain_id, - peer_id, - node_type, - epoch, - uuid, - ) - .unwrap(); - - let body = TelemetryDump { - client_id: "test-client".into(), - user_id: peer_id.to_string(), - timestamp_micros: Utc::now().timestamp_micros().to_string(), - events: vec![TelemetryEvent { - name: "sample-event".into(), - params: BTreeMap::new(), - }], - }; - test_context - .with_bearer_auth(jwt_token) - .expect_status_code(500) - .post("/api/v1/ingest/custom-event", json!(body)) - .await; -} diff --git a/crates/aptos-telemetry-service/src/tests/mod.rs b/crates/aptos-telemetry-service/src/tests/mod.rs deleted file mode 100644 index cf88484edcf28..0000000000000 --- a/crates/aptos-telemetry-service/src/tests/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod auth_test; -mod custom_event; -pub(crate) mod test_context; diff --git a/crates/aptos-telemetry-service/src/tests/test_context.rs b/crates/aptos-telemetry-service/src/tests/test_context.rs deleted file mode 100644 index dc7f93219b929..0000000000000 --- a/crates/aptos-telemetry-service/src/tests/test_context.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - context::{ClientTuple, Context, GroupedMetricsClients, JsonWebTokenService, PeerStoreTuple}, - index, CustomEventConfig, LogIngestConfig, MetricsEndpointsConfig, TelemetryServiceConfig, -}; -use aptos_crypto::{x25519, Uniform}; -use aptos_rest_client::aptos_api_types::mime_types; -use rand::SeedableRng; -use reqwest::header::AUTHORIZATION; -use serde_json::Value; -use std::collections::HashMap; -use warp::{ - http::{header::CONTENT_TYPE, Response}, - hyper::body::Bytes, -}; - -pub async fn new_test_context() -> TestContext { - let mut rng = ::rand::rngs::StdRng::from_seed([0u8; 32]); - let server_private_key = x25519::PrivateKey::generate(&mut rng); - - let config = TelemetryServiceConfig { - address: format!("{}:{}", "127.0.0.1", 80).parse().unwrap(), - tls_cert_path: None, - tls_key_path: None, - trusted_full_node_addresses: HashMap::new(), - update_interval: 60, - custom_event_config: CustomEventConfig { - project_id: String::from("1"), - dataset_id: String::from("2"), - table_id: String::from("3"), - }, - pfn_allowlist: HashMap::new(), - log_env_map: HashMap::new(), - peer_identities: HashMap::new(), - metrics_endpoints_config: MetricsEndpointsConfig::default_for_test(), - humio_ingest_config: LogIngestConfig::default_for_test(), - }; - - let peers = PeerStoreTuple::default(); - let jwt_service = JsonWebTokenService::from_base64_secret(&base64::encode("jwt_secret_key")); - - TestContext::new( - config, - Context::new( - server_private_key, - peers, - ClientTuple::new(None, Some(GroupedMetricsClients::new_empty()), None), - jwt_service, - HashMap::new(), - HashMap::new(), - ), - ) -} - -#[derive(Clone)] -pub struct TestContext { - pub config: TelemetryServiceConfig, - expect_status_code: u16, - pub inner: Context, - bearer_token: String, -} - -impl TestContext { - pub fn new(config: TelemetryServiceConfig, context: Context) -> Self { - Self { - config, - expect_status_code: 200, - inner: context, - bearer_token: "".into(), - } - } - - pub fn expect_status_code(&self, status_code: u16) -> Self { - let mut ret = self.clone(); - ret.expect_status_code = status_code; - ret - } - - pub fn with_bearer_auth(&self, token: String) -> Self { - let mut ret = self.clone(); - ret.bearer_token = token; - ret - } - - pub async fn get(&self, path: &str) -> Value { - self.execute( - warp::test::request() - .header(AUTHORIZATION, format!("Bearer {}", self.bearer_token)) - .method("GET") - .path(path), - ) - .await - } - - pub async fn post(&self, path: &str, body: Value) -> Value { - self.execute( - warp::test::request() - .header(AUTHORIZATION, format!("Bearer {}", self.bearer_token)) - .method("POST") - .path(path) - .json(&body), - ) - .await - } - - pub async fn reply(&self, req: warp::test::RequestBuilder) -> Response { - req.reply(&index::routes(self.inner.clone())).await - } - - pub async fn execute(&self, req: warp::test::RequestBuilder) -> Value { - let resp = self.reply(req).await; - - let headers = resp.headers(); - assert_eq!(headers[CONTENT_TYPE], mime_types::JSON); - - let body = serde_json::from_slice(resp.body()).expect("response body is JSON"); - assert_eq!( - self.expect_status_code, - resp.status(), - "\nresponse: {}", - pretty(&body) - ); - - body - } -} - -pub fn pretty(val: &Value) -> String { - serde_json::to_string_pretty(val).unwrap() + "\n" -} diff --git a/crates/aptos-telemetry-service/src/types/auth.rs b/crates/aptos-telemetry-service/src/types/auth.rs deleted file mode 100644 index abb8d53604ac7..0000000000000 --- a/crates/aptos-telemetry-service/src/types/auth.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::common::NodeType; -use aptos_config::config::RoleType; -use aptos_crypto::x25519; -use aptos_types::{chain_id::ChainId, PeerId}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -#[derive(Serialize, Deserialize, Debug)] -pub struct AuthRequest { - pub chain_id: ChainId, - pub peer_id: PeerId, - #[serde(default = "default_role_type")] - pub role_type: RoleType, - pub server_public_key: x25519::PublicKey, - pub handshake_msg: Vec, - #[serde(default = "default_uuid")] - pub run_uuid: Uuid, -} - -#[derive(Serialize, Deserialize)] -pub struct AuthResponse { - pub handshake_msg: Vec, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct Claims { - pub chain_id: ChainId, - pub peer_id: PeerId, - pub node_type: NodeType, - pub epoch: u64, - pub exp: usize, - pub iat: usize, - pub run_uuid: Uuid, -} - -fn default_role_type() -> RoleType { - RoleType::Validator -} - -fn default_uuid() -> Uuid { - Uuid::default() -} - -impl Claims { - #[cfg(test)] - pub(crate) fn test() -> Self { - use chrono::{Duration, Utc}; - - Self { - chain_id: ChainId::test(), - peer_id: PeerId::random(), - node_type: NodeType::Validator, - epoch: 10, - exp: Utc::now().timestamp() as usize, - iat: Utc::now() - .checked_add_signed(Duration::seconds(3600)) - .unwrap() - .timestamp() as usize, - run_uuid: Uuid::default(), - } - } -} diff --git a/crates/aptos-telemetry-service/src/types/mod.rs b/crates/aptos-telemetry-service/src/types/mod.rs deleted file mode 100644 index c8a9eab1d23a1..0000000000000 --- a/crates/aptos-telemetry-service/src/types/mod.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod auth; -pub mod telemetry; - -pub mod common { - - use crate::types::auth::Claims; - use aptos_config::config::PeerSet; - use aptos_types::{chain_id::ChainId, PeerId}; - use serde::{Deserialize, Serialize}; - use std::{collections::HashMap, fmt}; - use uuid::Uuid; - - pub type EpochNum = u64; - pub type EpochedPeerStore = HashMap; - pub type PeerStore = HashMap; - pub type ChainCommonName = String; - - #[derive(Debug, Serialize, Deserialize, Clone)] - pub struct EventIdentity { - pub peer_id: PeerId, - pub chain_id: ChainId, - pub role_type: NodeType, - pub epoch: u64, - pub uuid: Uuid, - } - - impl From for EventIdentity { - fn from(claims: Claims) -> Self { - Self { - peer_id: claims.peer_id, - chain_id: claims.chain_id, - role_type: claims.node_type, - epoch: claims.epoch, - uuid: claims.run_uuid, - } - } - } - - #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] - pub enum NodeType { - Validator, - ValidatorFullNode, - PublicFullNode, - Unknown, - UnknownValidator, - UnknownFullNode, - } - - impl NodeType { - pub fn as_str(self) -> &'static str { - match self { - NodeType::Validator => "validator", - NodeType::ValidatorFullNode => "validator_fullnode", - NodeType::PublicFullNode => "public_fullnode", - NodeType::Unknown => "unknown_peer", - NodeType::UnknownValidator => "unknown_validator", - NodeType::UnknownFullNode => "unknown_fullnode", - } - } - } - - impl fmt::Debug for NodeType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self) - } - } - - impl fmt::Display for NodeType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } - } -} - -pub mod response { - use crate::errors::ServiceError; - use aptos_crypto::x25519; - use reqwest::StatusCode; - use serde::{Deserialize, Serialize}; - - #[derive(Serialize, Deserialize)] - pub struct IndexResponse { - pub public_key: x25519::PublicKey, - } - - #[derive(Serialize, Deserialize)] - pub struct ErrorResponse { - code: u16, - message: String, - } - - impl ErrorResponse { - pub fn new(code: StatusCode, message: String) -> Self { - Self { - code: code.as_u16(), - message, - } - } - } - - impl From<&ServiceError> for ErrorResponse { - fn from(err: &ServiceError) -> Self { - Self::new(err.http_status_code(), err.to_string()) - } - } -} - -pub mod humio { - use serde::{Deserialize, Serialize}; - use std::collections::HashMap; - - #[derive(Deserialize, Serialize, Clone, Debug)] - pub struct UnstructuredLog { - pub fields: HashMap, - pub tags: HashMap, - pub messages: Vec, - } -} diff --git a/crates/aptos-telemetry-service/src/types/telemetry.rs b/crates/aptos-telemetry-service/src/types/telemetry.rs deleted file mode 100644 index adc8d1dfb667f..0000000000000 --- a/crates/aptos-telemetry-service/src/types/telemetry.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::types::common::EventIdentity; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// A useful struct for serialization a telemetry event -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct TelemetryEvent { - pub name: String, - pub params: BTreeMap, -} - -/// A useful struct for serializing a telemetry dump -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct TelemetryDump { - pub client_id: String, - pub user_id: String, - pub timestamp_micros: String, - pub events: Vec, -} - -#[derive(Debug, Serialize, Clone)] -pub(crate) struct BigQueryRow { - #[serde(flatten)] - pub event_identity: EventIdentity, - pub event_name: String, - pub event_timestamp: u64, - pub event_params: Vec, -} diff --git a/crates/aptos-telemetry-service/src/validator_cache.rs b/crates/aptos-telemetry-service/src/validator_cache.rs deleted file mode 100644 index 554e533d3b2d8..0000000000000 --- a/crates/aptos-telemetry-service/src/validator_cache.rs +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - debug, error, - errors::ValidatorCacheUpdateError, - metrics::{VALIDATOR_SET_UPDATE_FAILED_COUNT, VALIDATOR_SET_UPDATE_SUCCESS_COUNT}, - types::common::{ChainCommonName, EpochedPeerStore}, -}; -use aptos_config::config::{Peer, PeerRole, PeerSet}; -use aptos_infallible::RwLock; -use aptos_rest_client::Response; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, chain_id::ChainId, on_chain_config::ValidatorSet, PeerId, -}; -use std::{collections::HashMap, sync::Arc, time::Duration}; -use tokio::time; -use url::Url; - -#[derive(Clone)] -pub struct PeerSetCacheUpdater { - validators: Arc>, - validator_fullnodes: Arc>, - - query_addresses: Arc>, - update_interval: time::Duration, -} - -impl PeerSetCacheUpdater { - pub fn new( - validators: Arc>, - validator_fullnodes: Arc>, - trusted_full_node_addresses: HashMap, - update_interval: Duration, - ) -> Self { - Self { - validators, - validator_fullnodes, - query_addresses: Arc::new(trusted_full_node_addresses), - update_interval, - } - } - - pub fn run(self) { - let mut interval = time::interval(self.update_interval); - tokio::spawn(async move { - loop { - self.update().await; - interval.tick().await; - } - }); - } - - async fn update(&self) { - for (chain_name, url) in self.query_addresses.iter() { - match self.update_for_chain(chain_name, url).await { - Ok(_) => { - VALIDATOR_SET_UPDATE_SUCCESS_COUNT - .with_label_values(&[&chain_name.to_string()]) - .inc(); - debug!( - "validator set update successful for chain name {}", - chain_name - ); - }, - Err(err) => { - VALIDATOR_SET_UPDATE_FAILED_COUNT - .with_label_values(&[&chain_name.to_string(), &err.to_string()]) - .inc(); - error!( - "validator set update error for chain name {}: {:?}", - chain_name, err - ); - }, - } - } - } - - async fn update_for_chain( - &self, - chain_name: &ChainCommonName, - url: &str, - ) -> Result<(), ValidatorCacheUpdateError> { - let client = aptos_rest_client::Client::new(Url::parse(url).map_err(|e| { - error!("invalid url for chain_id {}: {}", chain_name, e); - ValidatorCacheUpdateError::InvalidUrl - })?); - let response: Response = client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await - .map_err(ValidatorCacheUpdateError::RestError)?; - - let (peer_addrs, state) = response.into_parts(); - - let chain_id = ChainId::new(state.chain_id); - - let mut validator_cache = self.validators.write(); - let mut vfn_cache = self.validator_fullnodes.write(); - - let validator_peers: PeerSet = peer_addrs - .clone() - .into_iter() - .filter_map(|validator_info| -> Option<(PeerId, Peer)> { - validator_info - .config() - .validator_network_addresses() - .map(|addresses| { - ( - *validator_info.account_address(), - Peer::from_addrs(PeerRole::Validator, addresses), - ) - }) - .map_err(|err| { - error!( - "unable to parse validator network address for validator info {} for chain name {}: {}", - validator_info, chain_name, err - ) - }) - .ok() - }) - .collect(); - - let vfn_peers: PeerSet = peer_addrs - .into_iter() - .filter_map(|validator_info| -> Option<(PeerId, Peer)> { - validator_info - .config() - .fullnode_network_addresses() - .map(|addresses| { - ( - *validator_info.account_address(), - Peer::from_addrs(PeerRole::ValidatorFullNode, addresses), - ) - }) - .map_err(|err| { - error!( - "unable to parse fullnode network address for validator info {} in chain name {}: {}", - validator_info, chain_name, err - ); - }) - .ok() - }) - .collect(); - - debug!( - "Validator peers for chain name {} (chain id {}) at epoch {}: {:?}", - chain_name, chain_id, state.epoch, validator_peers - ); - - let result = if validator_peers.is_empty() && vfn_peers.is_empty() { - Err(ValidatorCacheUpdateError::BothPeerSetEmpty) - } else if validator_peers.is_empty() { - Err(ValidatorCacheUpdateError::ValidatorSetEmpty) - } else if vfn_peers.is_empty() { - Err(ValidatorCacheUpdateError::VfnSetEmpty) - } else { - Ok(()) - }; - - if !validator_peers.is_empty() { - validator_cache.insert(chain_id, (state.epoch, validator_peers)); - } - - debug!( - "Validator fullnode peers for chain name {} (chain id {}) at epoch {}: {:?}", - chain_name, chain_id, state.epoch, vfn_peers - ); - - if !vfn_peers.is_empty() { - vfn_cache.insert(chain_id, (state.epoch, vfn_peers)); - } - - result - } -} - -#[cfg(test)] -mod tests { - use super::PeerSetCacheUpdater; - use aptos_crypto::{ - bls12381::{PrivateKey, PublicKey}, - test_utils::KeyPair, - Uniform, - }; - use aptos_infallible::RwLock; - use aptos_rest_client::aptos_api_types::*; - use aptos_types::{ - chain_id::ChainId, network_address::NetworkAddress, on_chain_config::ValidatorSet, - validator_config::ValidatorConfig, validator_info::ValidatorInfo, PeerId, - }; - use httpmock::MockServer; - use rand_core::OsRng; - use std::{collections::HashMap, str::FromStr, sync::Arc, time::Duration}; - - #[tokio::test] - async fn test_validator_cache_updater_with_invalid_address() { - let mut rng = OsRng; - let keypair = KeyPair::::generate(&mut rng); - let validator_info = ValidatorInfo::new( - PeerId::random(), - 10, - ValidatorConfig::new(keypair.public_key, vec![0, 0], vec![0, 0], 2), - ); - let validator_set = ValidatorSet::new(vec![validator_info]); - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("GET") - .path("/v1/accounts/0000000000000000000000000000000000000000000000000000000000000001/resource/0x1::stake::ValidatorSet"); - then.status(200) - .body(bcs::to_bytes(&validator_set).unwrap()) - .header(X_APTOS_CHAIN_ID, "25") - .header(X_APTOS_EPOCH, "10") - .header(X_APTOS_LEDGER_VERSION, "10") - .header(X_APTOS_LEDGER_OLDEST_VERSION, "2") - .header(X_APTOS_BLOCK_HEIGHT, "25") - .header(X_APTOS_OLDEST_BLOCK_HEIGHT, "10") - .header(X_APTOS_LEDGER_TIMESTAMP, "10"); - }); - - let mut fullnodes = HashMap::new(); - fullnodes.insert("testing".into(), server.base_url()); - - let updater = PeerSetCacheUpdater::new( - Arc::new(RwLock::new(HashMap::new())), - Arc::new(RwLock::new(HashMap::new())), - fullnodes, - Duration::from_secs(10), - ); - - updater.update().await; - - mock.assert(); - assert!(updater.validators.read().is_empty()); - assert!(updater.validator_fullnodes.read().is_empty()); - } - - #[tokio::test] - async fn test_validator_cache_updater_with_valid_address() { - let mut rng = OsRng; - let keypair = KeyPair::::generate(&mut rng); - let validator_info = ValidatorInfo::new( - PeerId::random(), - 10, - ValidatorConfig::new( - keypair.public_key, - bcs::to_bytes(&vec![NetworkAddress::from_str("/dns/a5f3d921730874389bb2f66275f163a5-8f14ad5b5e992c1c.elb.ap-southeast-1.amazonaws.com/tcp/6180/noise-ik/0xc5edf62233096df793b554e1013b07c83d01b3cf50c14ac83a0a7e0cfe340426/handshake/0").unwrap()]).unwrap(), - bcs::to_bytes(&vec![NetworkAddress::from_str("/dns/fullnode0.testnet.aptoslabs.com/tcp/6182/noise-ik/0xea19ab47ed9191865f15d85d751ed0663205c0b2f0f465714b1947c023715973/handshake/0").unwrap()]).unwrap(), - 2, - ), - ); - let validator_set = ValidatorSet::new(vec![validator_info.clone()]); - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("GET") - .path("/v1/accounts/0000000000000000000000000000000000000000000000000000000000000001/resource/0x1::stake::ValidatorSet"); - then.status(200) - .body(bcs::to_bytes(&validator_set).unwrap()) - .header(X_APTOS_CHAIN_ID, "25") - .header(X_APTOS_EPOCH, "10") - .header(X_APTOS_LEDGER_VERSION, "10") - .header(X_APTOS_LEDGER_OLDEST_VERSION, "2") - .header(X_APTOS_BLOCK_HEIGHT, "25") - .header(X_APTOS_OLDEST_BLOCK_HEIGHT, "10") - .header(X_APTOS_LEDGER_TIMESTAMP, "10"); - }); - - let mut fullnodes = HashMap::new(); - fullnodes.insert("testing".into(), server.base_url()); - - let updater = PeerSetCacheUpdater::new( - Arc::new(RwLock::new(HashMap::new())), - Arc::new(RwLock::new(HashMap::new())), - fullnodes, - Duration::from_secs(10), - ); - - updater.update().await; - - mock.assert(); - assert_eq!(updater.validators.read().len(), 1); - assert_eq!(updater.validator_fullnodes.read().len(), 1); - assert_eq!( - updater - .validators - .read() - .get(&ChainId::new(25)) - .unwrap() - .1 - .get(validator_info.account_address()) - .unwrap() - .addresses, - validator_info - .config() - .validator_network_addresses() - .unwrap() - ); - assert_eq!( - updater - .validator_fullnodes - .read() - .get(&ChainId::new(25)) - .unwrap() - .1 - .get(validator_info.account_address()) - .unwrap() - .addresses, - validator_info - .config() - .fullnode_network_addresses() - .unwrap() - ); - } -} diff --git a/crates/aptos-telemetry/Cargo.toml b/crates/aptos-telemetry/Cargo.toml deleted file mode 100644 index a4a6df87a2b51..0000000000000 --- a/crates/aptos-telemetry/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "aptos-telemetry" -description = "Aptos utilities to collect observability data from a node and send it to a remote destination" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-api = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus = { workspace = true } -aptos-crypto = { workspace = true } -aptos-db = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-mempool = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-network = { workspace = true } -aptos-node-resource-metrics = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-state-sync-driver = { workspace = true } -aptos-telemetry-service = { workspace = true } -aptos-types = { workspace = true } -flate2 = { workspace = true } -futures = { workspace = true } -once_cell = { workspace = true } -prometheus = { workspace = true } -rand = { workspace = true } -rand_core = { workspace = true } -reqwest = { workspace = true } -reqwest-middleware = { workspace = true } -reqwest-retry = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -sysinfo = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -tokio-retry = { workspace = true } -tokio-stream = { workspace = true } -url = { workspace = true } -uuid = { workspace = true } - -[dev-dependencies] -httpmock = { workspace = true } diff --git a/crates/aptos-telemetry/src/cli_metrics.rs b/crates/aptos-telemetry/src/cli_metrics.rs deleted file mode 100644 index 453149164280f..0000000000000 --- a/crates/aptos-telemetry/src/cli_metrics.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{service, utils}; -use aptos_logger::debug; -use aptos_telemetry_service::types::telemetry::TelemetryEvent; -use std::{collections::BTreeMap, time::Duration}; - -/// CLI metrics event name -const APTOS_CLI_METRICS: &str = "APTOS_CLI_METRICS"; - -/// Core metric keys -const COMMAND: &str = "command"; -const LATENCY: &str = "latency"; -const SUCCESS: &str = "success"; -const ERROR: &str = "error"; - -/// Collects and sends the build information via telemetry -pub async fn send_cli_telemetry_event( - mut build_information: BTreeMap, - command: String, - latency: Duration, - success: bool, - error: Option<&str>, -) { - // Collection information about the cli command - collect_cli_info(command, latency, success, error, &mut build_information); - - // Create a new telemetry event - let telemetry_event = TelemetryEvent { - name: APTOS_CLI_METRICS.into(), - params: build_information, - }; - - // TODO(joshlind): can we find a better way of identifying each CLI user? - let user_id = uuid::Uuid::new_v4().to_string(); - - // Send the event (we block on the join handle to ensure the - // event is processed before terminating the cli command). - let join_handle = - service::send_telemetry_event_with_ip(user_id, "NO_CHAIN".into(), None, telemetry_event) - .await; - if let Err(error) = join_handle.await { - debug!( - "Failed to send telemetry event with join error: {:?}", - error - ); - } -} - -/// Collects the cli info and appends it to the given map -pub(crate) fn collect_cli_info( - command: String, - latency: Duration, - success: bool, - error: Option<&str>, - build_information: &mut BTreeMap, -) { - build_information.insert(COMMAND.into(), command); - build_information.insert(LATENCY.into(), latency.as_millis().to_string()); - build_information.insert(SUCCESS.into(), success.to_string()); - utils::insert_optional_value( - build_information, - ERROR, - error.map(|inner| inner.to_string()), - ); -} diff --git a/crates/aptos-telemetry/src/constants.rs b/crates/aptos-telemetry/src/constants.rs deleted file mode 100644 index abadb85d847bc..0000000000000 --- a/crates/aptos-telemetry/src/constants.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -/// A collection of constants and default values for configuring telemetry components - -// Environment variables -pub(crate) const ENV_APTOS_DISABLE_TELEMETRY: &str = "APTOS_DISABLE_TELEMETRY"; -pub(crate) const ENV_APTOS_FORCE_ENABLE_TELEMETRY: &str = "APTOS_FORCE_ENABLE_TELEMETRY"; -pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_METRICS: &str = - "APTOS_DISABLE_TELEMETRY_PUSH_METRICS"; -pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_LOGS: &str = "APTOS_DISABLE_TELEMETRY_PUSH_LOGS"; -pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_EVENTS: &str = - "APTOS_DISABLE_TELEMETRY_PUSH_EVENTS"; -pub(crate) const ENV_APTOS_DISABLE_PROMETHEUS_NODE_METRICS: &str = - "APTOS_DISABLE_PROMETHEUS_NODE_METRICS"; -pub(crate) const ENV_APTOS_DISABLE_LOG_ENV_POLLING: &str = "APTOS_DISABLE_LOG_ENV_POLLING"; - -pub(crate) const ENV_GA_MEASUREMENT_ID: &str = "GA_MEASUREMENT_ID"; -pub(crate) const ENV_GA_API_SECRET: &str = "GA_API_SECRET"; -pub(crate) const ENV_TELEMETRY_SERVICE_URL: &str = "TELEMETRY_SERVICE_URL"; - -// Default Google Analytic values. -// TODO: Rotate these periodically. -pub(crate) const APTOS_GA_MEASUREMENT_ID: &str = "G-ZX4L6WPCFZ"; -pub(crate) const APTOS_GA_API_SECRET: &str = "ArtslKPTTjeiMi1n-IR39g"; - -// Useful URLS. -// Note: the measurement protocol requires HTTPS. -// See: https://developers.google.com/analytics/devguides/collection/protocol/v1/reference#transport -pub(crate) const GA4_URL: &str = "https://www.google-analytics.com/mp/collect"; -pub(crate) const HTTPBIN_URL: &str = "https://httpbin.org/ip"; -pub(crate) const TELEMETRY_SERVICE_URL: &str = "https://telemetry.aptoslabs.com"; -pub(crate) const MAINNET_TELEMETRY_SERVICE_URL: &str = "https://telemetry.mainnet.aptoslabs.com"; - -// Frequencies for the various metrics and pushes -pub(crate) const NODE_BUILD_INFO_FREQ_SECS: u64 = 60 * 60; // 60 minutes -pub(crate) const NODE_CORE_METRICS_FREQ_SECS: u64 = 30; // 30 seconds -pub(crate) const NODE_NETWORK_METRICS_FREQ_SECS: u64 = 60; // 1 minute -pub(crate) const NODE_SYS_INFO_FREQ_SECS: u64 = 5 * 60; // 5 minutes -pub(crate) const NODE_CONFIG_FREQ_SECS: u64 = 60 * 60; // 60 minutes - -// TODO: consider making this interval configurable -pub(crate) const PROMETHEUS_PUSH_METRICS_FREQ_SECS: u64 = 15; // 15 seconds -pub(crate) const CHAIN_ACCESS_CHECK_FREQ_SECS: u64 = 30 * 60; // 30 minutes -pub(crate) const LOG_ENV_POLL_FREQ_SECS: u64 = 5 * 60; // 5 minutes diff --git a/crates/aptos-telemetry/src/core_metrics.rs b/crates/aptos-telemetry/src/core_metrics.rs deleted file mode 100644 index b836b0ab1ece2..0000000000000 --- a/crates/aptos-telemetry/src/core_metrics.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{utils, utils::sum_all_histogram_counts}; -use aptos_config::config::NodeConfig; -use aptos_state_sync_driver::metrics::StorageSynchronizerOperations; -use aptos_telemetry_service::types::telemetry::TelemetryEvent; -use prometheus::core::Collector; -use std::collections::BTreeMap; - -/// Core metrics event name -const APTOS_NODE_CORE_METRICS: &str = "APTOS_NODE_CORE_METRICS"; - -/// Core metric keys -const CONSENSUS_LAST_COMMITTED_ROUND: &str = "consensus_last_committed_round"; -const CONSENSUS_PROPOSALS_COUNT: &str = "consensus_proposals_count"; -const CONSENSUS_TIMEOUT_COUNT: &str = "consensus_timeout_count"; -const MEMPOOL_CORE_MEMPOOL_INDEX_SIZE: &str = "mempool_core_mempool_index_size"; -const REST_RESPONSE_COUNT: &str = "rest_response_count"; -const ROLE_TYPE: &str = "role_type"; -const STATE_SYNC_BOOTSTRAP_MODE: &str = "state_sync_bootstrap_mode"; -const STATE_SYNC_CODE_VERSION: &str = "state_sync_code_version"; -const STATE_SYNC_CONTINUOUS_SYNC_MODE: &str = "state_sync_continuous_sync_mode"; -const STATE_SYNC_SYNCED_VERSION: &str = "state_sync_synced_version"; -const STATE_SYNC_SYNCED_EPOCH: &str = "state_sync_synced_epoch"; -const STORAGE_LEDGER_VERSION: &str = "storage_ledger_version"; -const STORAGE_MIN_READABLE_LEDGER_VERSION: &str = "storage_min_readable_ledger_version"; -const STORAGE_MIN_READABLE_STATE_MERKLE_VERSION: &str = "storage_min_readable_state_merkle_version"; -const STORAGE_MIN_READABLE_STATE_KV_VERSION: &str = "storage_min_readable_state_kv_version"; -const TELEMETRY_FAILURE_COUNT: &str = "telemetry_failure_count"; -const TELEMETRY_SUCCESS_COUNT: &str = "telemetry_success_count"; - -/// Collects and sends the build information via telemetry -pub(crate) async fn create_core_metric_telemetry_event(node_config: &NodeConfig) -> TelemetryEvent { - // Collect the core metrics - let core_metrics = get_core_metrics(node_config); - - // Create and return a new telemetry event - TelemetryEvent { - name: APTOS_NODE_CORE_METRICS.into(), - params: core_metrics, - } -} - -/// Used to expose core metrics for the node -pub fn get_core_metrics(node_config: &NodeConfig) -> BTreeMap { - let mut core_metrics: BTreeMap = BTreeMap::new(); - collect_core_metrics(&mut core_metrics, node_config); - core_metrics -} - -/// Collects the core metrics and appends them to the given map -fn collect_core_metrics(core_metrics: &mut BTreeMap, node_config: &NodeConfig) { - // Collect the core metrics for each component - collect_consensus_metrics(core_metrics); - collect_mempool_metrics(core_metrics); - collect_rest_metrics(core_metrics); - collect_state_sync_metrics(core_metrics, node_config); - collect_storage_metrics(core_metrics); - collect_telemetry_metrics(core_metrics); - - // Collect the node role - let node_role_type = node_config.base.role; - core_metrics.insert(ROLE_TYPE.into(), node_role_type.as_str().into()); -} - -/// Collects the consensus metrics and appends it to the given map -fn collect_consensus_metrics(core_metrics: &mut BTreeMap) { - core_metrics.insert( - CONSENSUS_PROPOSALS_COUNT.into(), - aptos_consensus::counters::PROPOSALS_COUNT.get().to_string(), - ); - core_metrics.insert( - CONSENSUS_LAST_COMMITTED_ROUND.into(), - aptos_consensus::counters::LAST_COMMITTED_ROUND - .get() - .to_string(), - ); - core_metrics.insert( - CONSENSUS_TIMEOUT_COUNT.into(), - aptos_consensus::counters::TIMEOUT_COUNT.get().to_string(), - ); - //TODO(joshlind): add block tracing and back pressure! -} - -/// Collects the mempool metrics and appends it to the given map -fn collect_mempool_metrics(core_metrics: &mut BTreeMap) { - core_metrics.insert( - MEMPOOL_CORE_MEMPOOL_INDEX_SIZE.into(), - aptos_mempool::counters::CORE_MEMPOOL_INDEX_SIZE - .with_label_values(&["system_ttl"]) - .get() - .to_string(), - ); -} - -/// Collects the REST metrics and appends it to the given map -fn collect_rest_metrics(core_metrics: &mut BTreeMap) { - let rest_response_metrics = aptos_api::metrics::RESPONSE_STATUS.collect(); - let rest_response_count = sum_all_histogram_counts(&rest_response_metrics); - core_metrics.insert(REST_RESPONSE_COUNT.into(), rest_response_count.to_string()); -} - -/// Collects the state sync metrics and appends it to the given map -fn collect_state_sync_metrics( - core_metrics: &mut BTreeMap, - node_config: &NodeConfig, -) { - let state_sync_driver_config = node_config.state_sync.state_sync_driver; - - // Get the state sync code version - core_metrics.insert(STATE_SYNC_CODE_VERSION.into(), "2".into()); - - core_metrics.insert( - STATE_SYNC_SYNCED_EPOCH.into(), - aptos_state_sync_driver::metrics::STORAGE_SYNCHRONIZER_OPERATIONS - .with_label_values(&[StorageSynchronizerOperations::SyncedEpoch.get_label()]) - .get() - .to_string(), - ); - core_metrics.insert( - STATE_SYNC_SYNCED_VERSION.into(), - aptos_state_sync_driver::metrics::STORAGE_SYNCHRONIZER_OPERATIONS - .with_label_values(&[StorageSynchronizerOperations::Synced.get_label()]) - .get() - .to_string(), - ); - core_metrics.insert( - STATE_SYNC_BOOTSTRAP_MODE.into(), - state_sync_driver_config - .bootstrapping_mode - .to_label() - .into(), - ); - core_metrics.insert( - STATE_SYNC_CONTINUOUS_SYNC_MODE.into(), - state_sync_driver_config - .continuous_syncing_mode - .to_label() - .into(), - ); -} - -/// Collects the storage metrics and appends it to the given map -fn collect_storage_metrics(core_metrics: &mut BTreeMap) { - core_metrics.insert( - STORAGE_LEDGER_VERSION.into(), - aptos_db::metrics::LEDGER_VERSION.get().to_string(), - ); - core_metrics.insert( - STORAGE_MIN_READABLE_LEDGER_VERSION.into(), - aptos_db::metrics::PRUNER_VERSIONS - .with_label_values(&["ledger_pruner", "min_readable"]) - .get() - .to_string(), - ); - core_metrics.insert( - STORAGE_MIN_READABLE_STATE_MERKLE_VERSION.into(), - aptos_db::metrics::PRUNER_VERSIONS - .with_label_values(&["state_merkle_pruner", "min_readable"]) - .get() - .to_string(), - ); - core_metrics.insert( - STORAGE_MIN_READABLE_STATE_KV_VERSION.into(), - aptos_db::metrics::PRUNER_VERSIONS - .with_label_values(&["state_kv_pruner", "min_readable"]) - .get() - .to_string(), - ); - // TODO(joshlind): add storage latencies! -} - -/// Collects the telemetry metrics and appends it to the given map -fn collect_telemetry_metrics(core_metrics: &mut BTreeMap) { - let telemetry_failure_metrics = crate::metrics::APTOS_TELEMETRY_FAILURE.collect(); - let telemetry_failure_count = utils::sum_all_gauges(&telemetry_failure_metrics); - core_metrics.insert( - TELEMETRY_FAILURE_COUNT.into(), - telemetry_failure_count.to_string(), - ); - - let telemetry_success_metrics = crate::metrics::APTOS_TELEMETRY_SUCCESS.collect(); - let telemetry_success_count = utils::sum_all_gauges(&telemetry_success_metrics); - core_metrics.insert( - TELEMETRY_SUCCESS_COUNT.into(), - telemetry_success_count.to_string(), - ); -} diff --git a/crates/aptos-telemetry/src/lib.rs b/crates/aptos-telemetry/src/lib.rs deleted file mode 100644 index 37c6e69295b3b..0000000000000 --- a/crates/aptos-telemetry/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -mod constants; -mod core_metrics; -mod metrics; -mod network_metrics; -mod sender; -mod telemetry_log_sender; - -pub mod cli_metrics; -pub mod service; -pub mod system_information; -pub mod utils; diff --git a/crates/aptos-telemetry/src/metrics.rs b/crates/aptos-telemetry/src/metrics.rs deleted file mode 100644 index 018ce6e706973..0000000000000 --- a/crates/aptos-telemetry/src/metrics.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - register_int_counter, register_int_counter_vec, IntCounter, IntCounterVec, -}; -use once_cell::sync::Lazy; - -/// Counter for successful telemetry events sent from Telemetry Sender to Telemetry Service -pub(crate) static APTOS_TELEMETRY_SERVICE_SUCCESS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_telemetry_service_success", - "Number of telemetry events successfully sent to telemetry service", - &["event_name"] - ) - .unwrap() -}); - -/// Counter for failed telemetry events sent from Telemetry Sender to Telemetry Service -pub(crate) static APTOS_TELEMETRY_SERVICE_FAILURE: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_telemetry_service_failure", - "Number of telemetry events that failed to send to telemetry service", - &["event_name"] - ) - .unwrap() -}); - -/// Counter for successful telemetry events sent to GA -/// /// TODO: Clean up when cleaning up telemetry exporter to GA -pub(crate) static APTOS_TELEMETRY_SUCCESS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_telemetry_success", - "Number of telemetry events successfully sent", - &["event_name"] - ) - .unwrap() -}); - -/// Counter for failed telemetry events sent to GA -/// TODO: Clean up when cleaning up telemetry exporter to GA -pub(crate) static APTOS_TELEMETRY_FAILURE: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_telemetry_failure", - "Number of telemetry events that failed to send", - &["event_name"] - ) - .unwrap() -}); - -/// Increments the number of successful telemetry events sent to GA -pub(crate) fn increment_telemetry_successes(event_name: &str) { - APTOS_TELEMETRY_SUCCESS - .with_label_values(&[event_name]) - .inc(); -} - -/// Increments the number of failed telemetry events sent to GA -pub(crate) fn increment_telemetry_failures(event_name: &str) { - APTOS_TELEMETRY_FAILURE - .with_label_values(&[event_name]) - .inc(); -} - -/// Increments the number of successful telemetry events sent to Telemetry service -pub(crate) fn increment_telemetry_service_successes(event_name: &str) { - APTOS_TELEMETRY_SERVICE_SUCCESS - .with_label_values(&[event_name]) - .inc(); -} - -/// Increments the number of failed telemetry events sent to Telemetry service -pub(crate) fn increment_telemetry_service_failures(event_name: &str) { - APTOS_TELEMETRY_SERVICE_FAILURE - .with_label_values(&[event_name]) - .inc(); -} - -/// Counter for successful log ingest events sent to Telemetry Service -pub(crate) static APTOS_LOG_INGEST_SUCCESS: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_log_ingest_success", - "Number of log ingest events successfully sent" - ) - .unwrap() -}); - -/// Counter for successful log ingest events sent to Telemetry Service -pub(crate) static APTOS_LOG_INGEST_TOO_LARGE: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_log_ingest_too_large", - "Number of log ingest events that were too large" - ) - .unwrap() -}); - -/// Counter for failed log ingest events sent to Telemetry Service -pub(crate) static APTOS_LOG_INGEST_FAILURE: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_log_ingest_failure", - "Number of log ingest events that failed to send" - ) - .unwrap() -}); - -/// Increments the number of successful log ingest events sent to Telemetry Service -pub(crate) fn increment_log_ingest_successes_by(v: u64) { - APTOS_LOG_INGEST_SUCCESS.inc_by(v); -} - -/// Increments the number of ignored log ingest events because too large -pub(crate) fn increment_log_ingest_too_large_by(v: u64) { - APTOS_LOG_INGEST_TOO_LARGE.inc_by(v); -} - -/// Increments the number of failed log ingest events -pub(crate) fn increment_log_ingest_failures_by(v: u64) { - APTOS_LOG_INGEST_FAILURE.inc_by(v); -} diff --git a/crates/aptos-telemetry/src/network_metrics.rs b/crates/aptos-telemetry/src/network_metrics.rs deleted file mode 100644 index 442d1655cdf28..0000000000000 --- a/crates/aptos-telemetry/src/network_metrics.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils; -use aptos_telemetry_service::types::telemetry::TelemetryEvent; -use prometheus::core::Collector; -use std::collections::BTreeMap; - -/// Network metrics event name -const APTOS_NODE_NETWORK_METRICS: &str = "APTOS_NODE_NETWORK_METRICS"; - -/// Network metric keys -const NETWORK_INBOUND_CONNECTIONS: &str = "network_inbound_connections"; -const NETWORK_INBOUND_MESSAGE_SUM: &str = "network_inbound_message_sum"; -const NETWORK_INBOUND_TRAFFIC_SUM: &str = "network_inbound_traffic_sum"; -const NETWORK_OUTBOUND_CONNECTIONS: &str = "network_outbound_connections"; -const NETWORK_OUTBOUND_MESSAGE_SUM: &str = "network_outbound_message_sum"; -const NETWORK_OUTBOUND_TRAFFIC_SUM: &str = "network_outbound_traffic_sum"; - -/// Collects and sends the build information via telemetry -pub(crate) async fn create_network_metric_telemetry_event() -> TelemetryEvent { - // Collect the network metrics - let network_metrics = get_network_metrics(); - - // Create and return a new telemetry event - TelemetryEvent { - name: APTOS_NODE_NETWORK_METRICS.into(), - params: network_metrics, - } -} - -/// Used to expose network metrics for the node -pub fn get_network_metrics() -> BTreeMap { - let mut network_metrics: BTreeMap = BTreeMap::new(); - collect_network_metrics(&mut network_metrics); - network_metrics -} - -/// Collects the network metrics and appends them to the given map -fn collect_network_metrics(network_metrics: &mut BTreeMap) { - collect_connection_metrics(network_metrics); - collect_message_and_traffic_metrics(network_metrics); -} - -/// Collects the connection metrics and appends them to the given map -fn collect_connection_metrics(network_metrics: &mut BTreeMap) { - // Calculate the number of inbound and outbound connections - let mut inbound_connection_count: f64 = 0.0; - let mut outbound_connection_count: f64 = 0.0; - for metric_family in aptos_network::counters::APTOS_CONNECTIONS.collect() { - for metric in metric_family.get_metric() { - // TODO(joshlind): avoid matching on strings that can change! - for label in metric.get_label() { - if label.get_name() == "direction" { - if label.get_value() == "inbound" { - inbound_connection_count += metric.get_gauge().get_value(); - } else if label.get_value() == "outbound" { - outbound_connection_count += metric.get_gauge().get_value(); - } - } - } - } - } - - // Update the connection metrics - network_metrics.insert( - NETWORK_INBOUND_CONNECTIONS.into(), - inbound_connection_count.to_string(), - ); - network_metrics.insert( - NETWORK_OUTBOUND_CONNECTIONS.into(), - outbound_connection_count.to_string(), - ); -} - -/// Collects the message and traffic metrics and appends them to the given map -fn collect_message_and_traffic_metrics(network_metrics: &mut BTreeMap) { - // Calculate the inbound messages and traffic - let inbound_metric_families = - aptos_network::counters::NETWORK_APPLICATION_INBOUND_METRIC.collect(); - let network_inbound_message_sum = utils::sum_all_histogram_counts(&inbound_metric_families); - let network_inbound_traffic_sum = utils::sum_all_histogram_sums(&inbound_metric_families); - - // Calculate the outbound messages and traffic - let outbound_metric_families = - aptos_network::counters::NETWORK_APPLICATION_OUTBOUND_METRIC.collect(); - let network_outbound_message_sum = utils::sum_all_histogram_counts(&outbound_metric_families); - let network_outbound_traffic_sum = utils::sum_all_histogram_sums(&outbound_metric_families); - - // Update the metrics - network_metrics.insert( - NETWORK_INBOUND_MESSAGE_SUM.into(), - network_inbound_message_sum.to_string(), - ); - network_metrics.insert( - NETWORK_INBOUND_TRAFFIC_SUM.into(), - network_inbound_traffic_sum.to_string(), - ); - network_metrics.insert( - NETWORK_OUTBOUND_MESSAGE_SUM.into(), - network_outbound_message_sum.to_string(), - ); - network_metrics.insert( - NETWORK_OUTBOUND_TRAFFIC_SUM.into(), - network_outbound_traffic_sum.to_string(), - ); -} diff --git a/crates/aptos-telemetry/src/sender.rs b/crates/aptos-telemetry/src/sender.rs deleted file mode 100644 index 2bcf89ed45e8c..0000000000000 --- a/crates/aptos-telemetry/src/sender.rs +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::metrics::{self, increment_log_ingest_failures_by, increment_log_ingest_successes_by}; -use anyhow::{anyhow, Error, Result}; -use aptos_config::config::{NodeConfig, RoleType}; -use aptos_crypto::{ - noise::{self, NoiseConfig}, - x25519, -}; -use aptos_infallible::{Mutex, RwLock}; -use aptos_logger::debug; -use aptos_telemetry_service::types::{ - auth::{AuthRequest, AuthResponse}, - response::IndexResponse, - telemetry::TelemetryDump, -}; -use aptos_types::{chain_id::ChainId, PeerId}; -use flate2::{write::GzEncoder, Compression}; -use prometheus::{default_registry, Registry}; -use reqwest::{header::CONTENT_ENCODING, Response, StatusCode, Url}; -use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, RequestBuilder}; -use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; -use std::{io::Write, sync::Arc, time::Duration}; -use uuid::Uuid; - -pub const DEFAULT_VERSION_PATH_BASE: &str = "api/v1/"; - -pub const PROMETHEUS_PUSH_METRICS_TIMEOUT_SECS: u64 = 8; -pub const TELEMETRY_SERVICE_TOTAL_RETRY_DURATION_SECS: u64 = 10; - -struct AuthContext { - noise_config: Option, - token: RwLock>, - server_public_key: Mutex>, -} - -impl AuthContext { - fn new(node_config: &NodeConfig) -> Self { - Self { - noise_config: node_config.get_identity_key().map(NoiseConfig::new), - token: RwLock::new(None), - server_public_key: Mutex::new(None), - } - } -} - -#[derive(Clone)] -pub(crate) struct TelemetrySender { - base_url: Url, - version_path_base: String, - chain_id: ChainId, - peer_id: PeerId, - role_type: RoleType, - client: ClientWithMiddleware, - auth_context: Arc, - uuid: Uuid, -} - -impl TelemetrySender { - pub fn new(base_url: Url, chain_id: ChainId, node_config: &NodeConfig) -> Self { - let retry_policy = ExponentialBackoff::builder().build_with_total_retry_duration( - Duration::from_secs(TELEMETRY_SERVICE_TOTAL_RETRY_DURATION_SECS), - ); - - let reqwest_client = reqwest::Client::new(); - let client = ClientBuilder::new(reqwest_client) - .with(RetryTransientMiddleware::new_with_policy(retry_policy)) - .build(); - - let version_path_base = match base_url.path() { - "/" => DEFAULT_VERSION_PATH_BASE.to_string(), - path => { - if !path.ends_with('/') { - format!("{}/", path) - } else { - path.to_string() - } - }, - }; - - Self { - base_url, - version_path_base, - chain_id, - peer_id: node_config.get_peer_id().unwrap_or(PeerId::ZERO), - role_type: node_config.base.role, - client, - auth_context: Arc::new(AuthContext::new(node_config)), - uuid: uuid::Uuid::new_v4(), - } - } - - pub fn build_path(&self, path: &str) -> Result { - Ok(self.base_url.join(&self.version_path_base)?.join(path)?) - } - - // sends an authenticated request to the telemetry service, automatically adding an auth token - // This function does not work with streaming bodies at the moment and will panic if you try so. - pub async fn send_authenticated_request( - &self, - request_builder: RequestBuilder, - ) -> Result { - let token = self.get_auth_token().await?; - - let request = request_builder - .try_clone() - .expect("Could not clone request_builder") - .bearer_auth(token) - .build()?; - - let mut response = self.client.execute(request).await?; - - // do 1 retry if the first attempt failed - if response.status() == StatusCode::UNAUTHORIZED { - // looks like request failed due to auth error. Let's get a new a fresh token. If this fails again we'll just return the error. - self.reset_token(); - let token = self.get_auth_token().await?; - let request = request_builder.bearer_auth(token).build()?; - response = self.client.execute(request).await?; - } - Ok(response) - } - - pub(crate) async fn push_prometheus_metrics( - &self, - registry: &Registry, - ) -> Result<(), anyhow::Error> { - debug!("Sending Prometheus Metrics"); - - let scraped_metrics = - prometheus::TextEncoder::new().encode_to_string(®istry.gather())?; - - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - gzip_encoder.write_all(scraped_metrics.as_bytes())?; - let compressed_bytes = gzip_encoder.finish()?; - - let response = self - .send_authenticated_request( - self.client - .post(self.build_path("ingest/metrics")?) - .header(CONTENT_ENCODING, "gzip") - .body(compressed_bytes) - .timeout(Duration::from_secs(PROMETHEUS_PUSH_METRICS_TIMEOUT_SECS)), - ) - .await; - - match response { - Err(e) => Err(anyhow!("Prometheus Metrics push failed: {}", e)), - Ok(response) => { - if response.status().is_success() { - Ok(()) - } else { - Err(anyhow!( - "Prometheus Metrics push failed with response: {}, body: {}", - response.status(), - response - .text() - .await - .unwrap_or_else(|_| "empty body".to_string()), - )) - } - }, - } - } - - pub(crate) async fn try_push_prometheus_metrics(&self) { - self.push_prometheus_metrics(default_registry()) - .await - .map_or_else( - |e| debug!("Failed to push Prometheus Metrics: {}", e), - |_| debug!("Prometheus Metrics pushed successfully."), - ); - } - - pub async fn try_send_logs(&self, batch: Vec) { - if let Ok(json) = serde_json::to_string(&batch) { - let len = json.len(); - - match self.post_logs(json.as_bytes()).await { - Ok(_) => { - increment_log_ingest_successes_by(batch.len() as u64); - debug!("Sent log of length: {}", len); - }, - Err(error) => { - increment_log_ingest_failures_by(batch.len() as u64); - debug!("Failed send log of length: {} with error: {}", len, error); - }, - } - } else { - debug!("Failed json serde of batch: {:?}", batch); - } - } - - async fn post_logs(&self, json: &[u8]) -> Result { - debug!("Sending logs"); - - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - gzip_encoder.write_all(json)?; - let compressed_bytes = gzip_encoder.finish()?; - - // Send the request and wait for a response - let response = self - .send_authenticated_request( - self.client - .post(self.build_path("ingest/logs")?) - .header(CONTENT_ENCODING, "gzip") - .body(compressed_bytes), - ) - .await?; - - // Process the result - error_for_status_with_body(response).await - } - - pub async fn try_send_custom_metrics(&self, event_name: String, telemetry_dump: TelemetryDump) { - match self.post_custom_metrics(&telemetry_dump.clone()).await { - Ok(_) => { - metrics::increment_telemetry_service_successes(&event_name); - debug!("Custom metrics with name {} sent successfully.", event_name); - }, - Err(e) => { - metrics::increment_telemetry_service_failures(&event_name); - debug!("Failed to send custom metrics: {}", e); - }, - } - } - - async fn post_custom_metrics( - &self, - telemetry_dump: &TelemetryDump, - ) -> Result { - // Send the request and wait for a response - let response = self - .send_authenticated_request( - self.client - .post(self.build_path("ingest/custom-event")?) - .json::(telemetry_dump), - ) - .await?; - - error_for_status_with_body(response).await - } - - async fn get_auth_token(&self) -> Result { - // Try to read the token holding a read lock - let token = { self.auth_context.token.read().as_ref().cloned() }; - match token { - Some(token) => Ok(token), - None => { - let token = self.authenticate().await?; - *self.auth_context.token.write() = Some(token.clone()); - Ok(token) - }, - } - } - - async fn get_public_key_from_server(&self) -> Result { - let response = self.client.get(self.build_path("")?).send().await?; - - match error_for_status_with_body(response).await { - Ok(response) => { - let response_payload = response.json::().await?; - Ok(response_payload.public_key) - }, - Err(err) => Err(anyhow!("Error getting server public key. {}", err)), - } - } - - async fn server_public_key(&self) -> Result { - let server_public_key = { *self.auth_context.server_public_key.lock() }; - match server_public_key { - Some(key) => Ok(key), - None => { - let public_key = self.get_public_key_from_server().await?; - *self.auth_context.server_public_key.lock() = Some(public_key); - Ok(public_key) - }, - } - } - - fn reset_token(&self) { - *self.auth_context.token.write() = None; - *self.auth_context.server_public_key.lock() = None; - } - - pub async fn authenticate(&self) -> Result { - let noise_config = match &self.auth_context.noise_config { - Some(config) => config, - None => return Err(anyhow!("Cannot send telemetry without private key")), - }; - let server_public_key = self.server_public_key().await?; - - // buffer to first noise handshake message - let mut client_noise_msg = vec![0; noise::handshake_init_msg_len(0)]; - - // build the prologue (chain_id | peer_id | server_public_key) - const CHAIN_ID_LENGTH: usize = 1; - const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; - const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; - let mut prologue = [0; PROLOGUE_SIZE]; - prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[self.chain_id.id()]); - prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(self.peer_id.as_ref()); - prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(server_public_key.as_slice()); - - let mut rng = rand::rngs::OsRng; - - // craft first handshake message (-> e, es, s, ss) - let initiator_state = noise_config - .initiate_connection( - &mut rng, - &prologue, - server_public_key, - None, - &mut client_noise_msg, - ) - .unwrap(); - - let auth_request = AuthRequest { - chain_id: self.chain_id, - peer_id: self.peer_id, - role_type: self.role_type, - server_public_key, - handshake_msg: client_noise_msg, - run_uuid: self.uuid, - }; - - let response = self - .client - .post(self.build_path("auth")?) - .json::(&auth_request) - .send() - .await?; - - let resp = match error_for_status_with_body(response).await { - Ok(response) => Ok(response.json::().await?), - Err(err) => { - debug!( - "[telemetry-client] Error sending authentication request: {}", - err, - ); - Err(anyhow!("error {}", err)) - }, - }?; - - let (response_payload, _) = noise_config - .finalize_connection(initiator_state, resp.handshake_msg.as_slice()) - .unwrap(); - - let jwt = String::from_utf8(response_payload)?; - - Ok(jwt) - } - - pub(crate) async fn check_chain_access(&self, chain_id: ChainId) -> bool { - debug!("checking chain access for chain id {}", chain_id); - - match self.try_check_chain_access(chain_id).await { - Ok(response) => match error_for_status_with_body(response).await { - Ok(response) => response.json::().await.unwrap_or(true), - Err(e) => { - debug!("Unable to check chain access {}", e); - true - }, - }, - Err(e) => { - debug!("Unable to check chain access {}", e); - true - }, - } - } - - async fn try_check_chain_access(&self, chain_id: ChainId) -> Result { - self.client - .get(self.build_path(&format!("chain-access/{}", chain_id))?) - .send() - .await - .map_err(|e| anyhow!("error sending request {}", e)) - } - - pub(crate) async fn get_telemetry_log_env(&self) -> Option { - let response = self - .send_authenticated_request( - self.client.get( - self.build_path("config/env/telemetry-log") - .expect("unable to build telemetry path for config/env/telemetry-log"), - ), - ) - .await; - - match response { - Ok(response) => match error_for_status_with_body(response).await { - Ok(response) => response.json::>().await.unwrap_or_default(), - Err(e) => { - debug!("Unable to get telemetry log env: {}", e); - None - }, - }, - Err(e) => { - debug!("Unable to check chain access {}", e); - None - }, - } - } -} - -async fn error_for_status_with_body(response: Response) -> Result { - if response.status().is_client_error() || response.status().is_server_error() { - Err(anyhow!( - "HTTP status error ({}) for url ({}): {}", - response.status(), - response.url().clone(), - response.text().await?, - )) - } else { - Ok(response) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::metrics::{APTOS_TELEMETRY_SERVICE_FAILURE, APTOS_TELEMETRY_SERVICE_SUCCESS}; - use aptos_crypto::Uniform; - use aptos_telemetry_service::types::telemetry::TelemetryEvent; - use httpmock::MockServer; - use prometheus::{register_int_counter_vec_with_registry, Registry}; - use std::{ - collections::BTreeMap, - time::{SystemTime, UNIX_EPOCH}, - }; - - #[tokio::test] - async fn test_server_public_key() { - let mut rng = rand::thread_rng(); - let private_key = x25519::PrivateKey::generate(&mut rng); - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("GET").path("/api/v1/"); - then.status(200).json_body_obj(&IndexResponse { - public_key: private_key.public_key(), - }); - }); - - let node_config = NodeConfig::default(); - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &node_config, - ); - - let result1 = client.server_public_key().await; - let result2 = client.server_public_key().await; - - mock.assert(); - - // Should call the server once and cache the key - assert_eq!(mock.hits(), 1); - assert!(result1.is_ok()); - assert_eq!(result1.unwrap(), private_key.public_key()); - assert!(result2.is_ok()); - assert_eq!(result2.unwrap(), private_key.public_key()); - - client.reset_token(); - - let result3 = client.server_public_key().await; - assert_eq!(mock.hits(), 2); - assert!(result3.is_ok()); - assert_eq!(result3.unwrap(), private_key.public_key()); - } - - #[tokio::test] - async fn test_post_custom_metrics() { - let mut telemetry_event = TelemetryEvent { - name: "sample-event".into(), - params: BTreeMap::new(), - }; - telemetry_event - .params - .insert("key-1".into(), "value-1".into()); - let telemetry_dump = TelemetryDump { - client_id: "client-1".into(), - user_id: "user-1".into(), - timestamp_micros: SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros() - .to_string(), - events: vec![], - }; - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("POST") - .header("Authorization", "Bearer SECRET_JWT_TOKEN") - .path("/api/v1/ingest/custom-event") - .json_body_obj(&telemetry_dump); - then.status(200); - }); - - let node_config = NodeConfig::default(); - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &node_config, - ); - { - *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); - } - - let result = client.post_custom_metrics(&telemetry_dump).await; - - mock.assert(); - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_try_send_metrics_retry_unauthorized() { - let event_name = "sample-event"; - let mut telemetry_event = TelemetryEvent { - name: event_name.into(), - params: BTreeMap::new(), - }; - telemetry_event - .params - .insert("key-1".into(), "value-1".into()); - let telemetry_dump = TelemetryDump { - client_id: "client-1".into(), - user_id: "user-1".into(), - timestamp_micros: SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros() - .to_string(), - events: vec![], - }; - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("POST").path("/api/v1/ingest/custom-event"); - then.status(401); - }); - - let node_config = NodeConfig::default(); - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &node_config, - ); - { - *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); - } - - client - .try_send_custom_metrics(event_name.into(), telemetry_dump) - .await; - - mock.assert_hits(1); - assert_eq!( - APTOS_TELEMETRY_SERVICE_SUCCESS - .with_label_values(&[event_name]) - .get(), - 0 - ); - assert_eq!( - APTOS_TELEMETRY_SERVICE_FAILURE - .with_label_values(&[event_name]) - .get(), - 1 - ); - } - - #[tokio::test] - async fn test_push_prometheus_metrics() { - // Initialize a local prometheus registry - // Using the global registry will conflict will other tests that increment counters - let test_registry = Registry::default(); - - let counter = register_int_counter_vec_with_registry!( - "aptos_telemetry_service_success", - "Number of telemetry events successfully sent to telemetry service", - &["event_name"], - test_registry - ) - .unwrap(); - - counter.with_label_values(&["test-event"]).inc(); - - let scraped_metrics = prometheus::TextEncoder::new() - .encode_to_string(&test_registry.gather()) - .unwrap(); - - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - gzip_encoder.write_all(scraped_metrics.as_bytes()).unwrap(); - let expected_compressed_bytes = gzip_encoder.finish().unwrap(); - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("POST") - .header("Authorization", "Bearer SECRET_JWT_TOKEN") - .path("/api/v1/ingest/metrics") - .body(String::from_utf8_lossy(&expected_compressed_bytes)); - then.status(200); - }); - - let node_config = NodeConfig::default(); - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &node_config, - ); - { - *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); - } - - let result = client.push_prometheus_metrics(&test_registry).await; - - mock.assert(); - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_post_logs() { - let batch = vec!["log1".to_string(), "log2".to_string()]; - let json = serde_json::to_string(&batch); - assert!(json.is_ok()); - - let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); - gzip_encoder.write_all(json.unwrap().as_bytes()).unwrap(); - let expected_compressed_bytes = gzip_encoder.finish().unwrap(); - - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("POST") - .header("Authorization", "Bearer SECRET_JWT_TOKEN") - .path("/api/v1/ingest/logs") - .body(String::from_utf8_lossy(&expected_compressed_bytes)); - then.status(200); - }); - - let node_config = NodeConfig::default(); - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &node_config, - ); - { - *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); - } - - client.try_send_logs(batch).await; - - mock.assert(); - } - - #[tokio::test] - async fn test_check_chain_access() { - let server = MockServer::start(); - let mock = server.mock(|when, then| { - when.method("GET").path("/api/v1/chain-access/24"); - then.status(200).json_body(true); - }); - - let client = TelemetrySender::new( - Url::parse(&server.base_url()).expect("unable to parse base url"), - ChainId::default(), - &NodeConfig::default(), - ); - assert!(client.check_chain_access(ChainId::new(24)).await); - - mock.assert(); - } -} diff --git a/crates/aptos-telemetry/src/service.rs b/crates/aptos-telemetry/src/service.rs deleted file mode 100644 index 8a2d064a3ec1f..0000000000000 --- a/crates/aptos-telemetry/src/service.rs +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use crate::{ - constants::*, core_metrics::create_core_metric_telemetry_event, metrics, - network_metrics::create_network_metric_telemetry_event, sender::TelemetrySender, - system_information::create_system_info_telemetry_event, - telemetry_log_sender::TelemetryLogSender, utils::create_build_info_telemetry_event, -}; -use aptos_config::config::NodeConfig; -use aptos_logger::{ - aptos_logger::RUST_LOG_TELEMETRY, prelude::*, telemetry_log_writer::TelemetryLog, - LoggerFilterUpdater, -}; -use aptos_telemetry_service::types::telemetry::{TelemetryDump, TelemetryEvent}; -use aptos_types::chain_id::ChainId; -use futures::channel::mpsc::{self, Receiver}; -use once_cell::sync::Lazy; -use rand::Rng; -use rand_core::OsRng; -use reqwest::Url; -use serde::Deserialize; -use std::{ - collections::BTreeMap, - env, - future::Future, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; -use tokio::{runtime::Runtime, task::JoinHandle, time}; -use uuid::Uuid; - -// The chain ID key -const CHAIN_ID_KEY: &str = "CHAIN_ID"; -// The IP address key -const IP_ADDRESS_KEY: &str = "IP_ADDRESS"; -// The telemetry token key -const TELEMETRY_TOKEN_KEY: &str = "TELEMETRY_TOKEN"; -// The default for unknown metric values -const UNKNOWN_METRIC_VALUE: &str = "UNKNOWN"; - -const APTOS_NODE_CONFIG_EVENT_NAME: &str = "APTOS_NODE_CONFIG"; - -/// The random token presented by the node to connect all -/// telemetry events. -/// TODO(joshlind): leverage real authentication! -static TELEMETRY_TOKEN: Lazy = Lazy::new(|| { - let mut rng = OsRng; - let token = rng.gen::(); - format!("TOKEN_{:?}", token) -}); - -/// Returns true iff telemetry is disabled -#[inline] -pub fn telemetry_is_disabled() -> bool { - env::var(ENV_APTOS_DISABLE_TELEMETRY).is_ok() -} - -/// Flag to force enabling/disabling of telemetry -#[inline] -fn force_enable_telemetry() -> bool { - env::var(ENV_APTOS_FORCE_ENABLE_TELEMETRY).is_ok() -} - -/// Flag to control enabling/disabling prometheus push metrics -#[inline] -fn enable_prometheus_push_metrics() -> bool { - force_enable_telemetry() - || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_METRICS).is_ok()) -} - -#[inline] -fn enable_prometheus_node_metrics() -> bool { - env::var(ENV_APTOS_DISABLE_PROMETHEUS_NODE_METRICS).is_err() -} - -/// Flag to control enabling/disabling push logs -#[inline] -fn enable_push_logs() -> bool { - force_enable_telemetry() - || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_LOGS).is_ok()) -} - -/// Flag to control enabling/disabling telemetry push events -#[inline] -fn enable_push_custom_events() -> bool { - force_enable_telemetry() - || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_EVENTS).is_ok()) -} - -#[inline] -fn enable_log_env_polling() -> bool { - force_enable_telemetry() - || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_LOG_ENV_POLLING).is_ok()) -} - -/// Starts the telemetry service and returns the execution runtime. -/// Note: The service will not be created if telemetry is disabled. -pub fn start_telemetry_service( - node_config: NodeConfig, - chain_id: ChainId, - build_info: BTreeMap, - remote_log_rx: Option>, - logger_filter_update_job: Option, -) -> Option { - if enable_prometheus_node_metrics() { - aptos_node_resource_metrics::register_node_metrics_collector(); - } - - // Don't start the service if telemetry has been disabled - if telemetry_is_disabled() { - warn!("Aptos telemetry is disabled!"); - return None; - } - - // Create the telemetry runtime - let telemetry_runtime = aptos_runtimes::spawn_named_runtime("telemetry".into(), None); - telemetry_runtime.handle().spawn(spawn_telemetry_service( - node_config, - chain_id, - build_info, - remote_log_rx, - logger_filter_update_job, - )); - - Some(telemetry_runtime) -} - -async fn spawn_telemetry_service( - node_config: NodeConfig, - chain_id: ChainId, - build_info: BTreeMap, - remote_log_rx: Option>, - logger_filter_update_job: Option, -) { - let telemetry_svc_url = env::var(ENV_TELEMETRY_SERVICE_URL).unwrap_or_else(|_| { - if chain_id == ChainId::mainnet() { - MAINNET_TELEMETRY_SERVICE_URL.into() - } else { - TELEMETRY_SERVICE_URL.into() - } - }); - - let base_url = Url::parse(&telemetry_svc_url).unwrap_or_else(|err| { - warn!( - "Unable to parse telemetry service URL {}. Make sure {} is unset or is set properly: {}. Defaulting to {}.", - telemetry_svc_url, - ENV_TELEMETRY_SERVICE_URL, err, TELEMETRY_SERVICE_URL - ); - Url::parse(TELEMETRY_SERVICE_URL) - .expect("unable to parse telemetry service default URL") - }); - - let telemetry_sender = TelemetrySender::new(base_url, chain_id, &node_config); - - if !force_enable_telemetry() && !telemetry_sender.check_chain_access(chain_id).await { - warn!( - "Aptos telemetry is not sent to the telemetry service because the service is not configured for chain ID {}", - chain_id - ); - // Spawn the custom event sender to send to GA4 only. - // This is a temporary workaround while we deprecate and remove GA4 completely. - let peer_id = fetch_peer_id(&node_config); - let handle = tokio::spawn(custom_event_sender( - None, - peer_id, - chain_id, - node_config.clone(), - build_info.clone(), - )); - info!("Telemetry service for GA4 started!"); - - // Check for chain access periodically in case the service is configured later - let mut interval = time::interval(Duration::from_secs(CHAIN_ACCESS_CHECK_FREQ_SECS)); - loop { - interval.tick().await; - if telemetry_sender.check_chain_access(chain_id).await { - handle.abort(); - info!("Aptos telemetry service is now configured for Chain ID {}. Starting telemetry service...", chain_id); - break; - } - } - } - - try_spawn_log_sender(telemetry_sender.clone(), remote_log_rx); - try_spawn_metrics_sender(telemetry_sender.clone()); - try_spawn_custom_event_sender(node_config, telemetry_sender.clone(), chain_id, build_info); - try_spawn_log_env_poll_task(telemetry_sender); - - // Run the logger filter update job within the telemetry runtime. - if let Some(job) = logger_filter_update_job { - tokio::spawn(job.run()); - } - - info!("Telemetry service started!"); -} - -fn try_spawn_log_env_poll_task(sender: TelemetrySender) { - if enable_log_env_polling() { - tokio::spawn(async move { - let original_value = env::var(RUST_LOG_TELEMETRY).ok(); - let mut interval = time::interval(Duration::from_secs(LOG_ENV_POLL_FREQ_SECS)); - loop { - interval.tick().await; - if let Some(env) = sender.get_telemetry_log_env().await { - info!( - "Updating {} env variable: previous value: {:?}, new value: {}", - RUST_LOG_TELEMETRY, - env::var(RUST_LOG_TELEMETRY).ok(), - env - ); - env::set_var(RUST_LOG_TELEMETRY, env) - } else if let Some(ref value) = original_value { - env::set_var(RUST_LOG_TELEMETRY, value) - } else { - env::remove_var(RUST_LOG_TELEMETRY) - } - } - }); - } -} - -fn try_spawn_custom_event_sender( - node_config: NodeConfig, - telemetry_sender: TelemetrySender, - chain_id: ChainId, - build_info: BTreeMap, -) { - if enable_push_custom_events() { - // Spawn the custom event sender - let peer_id = fetch_peer_id(&node_config); - tokio::spawn(custom_event_sender( - Some(telemetry_sender), - peer_id, - chain_id, - node_config, - build_info, - )); - } -} - -fn try_spawn_metrics_sender(telemetry_sender: TelemetrySender) { - if enable_prometheus_push_metrics() { - tokio::spawn(async move { - // Periodically send ALL prometheus metrics (This replaces the previous core and network metrics implementation) - let mut interval = - time::interval(Duration::from_secs(PROMETHEUS_PUSH_METRICS_FREQ_SECS)); - loop { - interval.tick().await; - telemetry_sender.try_push_prometheus_metrics().await; - } - }); - } -} - -fn try_spawn_log_sender( - telemetry_sender: TelemetrySender, - remote_log_rx: Option>, -) { - if enable_push_logs() { - if let Some(rx) = remote_log_rx { - let telemetry_log_sender = TelemetryLogSender::new(telemetry_sender); - tokio::spawn(telemetry_log_sender.start(rx)); - } - } -} - -/// Returns the peer id given the node config. -/// Returns UNKNOWN otherwise. -fn fetch_peer_id(node_config: &NodeConfig) -> String { - match node_config.get_peer_id() { - Some(peer_id) => peer_id.to_string(), - None => UNKNOWN_METRIC_VALUE.into(), - } -} - -async fn run_function_periodically(interval_seconds: u64, function_to_run: impl Fn() -> Fut) -where - Fut: Future, -{ - let mut interval = time::interval(Duration::from_secs(interval_seconds)); - loop { - interval.tick().await; - function_to_run().await; - } -} - -/// Spawns the dedicated telemetry service that operates periodically -async fn custom_event_sender( - telemetry_sender: Option, - peer_id: String, - chain_id: ChainId, - node_config: NodeConfig, - build_info: BTreeMap, -) { - futures::future::join5( - // Periodically send build information - run_function_periodically(NODE_BUILD_INFO_FREQ_SECS, || { - send_build_information( - peer_id.clone(), - chain_id.to_string(), - build_info.clone(), - telemetry_sender.clone(), - ) - }), - // Periodically send system information - run_function_periodically(NODE_SYS_INFO_FREQ_SECS, || { - send_system_information( - peer_id.clone(), - chain_id.to_string(), - telemetry_sender.clone(), - ) - }), - // Periodically send node core metrics - run_function_periodically(NODE_CORE_METRICS_FREQ_SECS, || { - send_node_core_metrics( - peer_id.clone(), - chain_id.to_string(), - &node_config, - telemetry_sender.clone(), - ) - }), - // Periodically send node network metrics - run_function_periodically(NODE_NETWORK_METRICS_FREQ_SECS, || { - send_node_network_metrics( - peer_id.clone(), - chain_id.to_string(), - telemetry_sender.clone(), - ) - }), - run_function_periodically(NODE_CONFIG_FREQ_SECS, || { - send_node_config( - peer_id.clone(), - chain_id.to_string(), - &node_config, - telemetry_sender.clone(), - ) - }), - ) - .await; -} - -/// Collects and sends the build information via telemetry -async fn send_build_information( - peer_id: String, - chain_id: String, - build_info: BTreeMap, - telemetry_sender: Option, -) { - let telemetry_event = create_build_info_telemetry_event(build_info).await; - send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; -} - -/// Collects and sends the core node metrics via telemetry -async fn send_node_config( - peer_id: String, - chain_id: String, - node_config: &NodeConfig, - telemetry_sender: Option, -) { - let node_config: BTreeMap = serde_json::to_value(node_config) - .map(|value| { - value - .as_object() - .map(|obj| { - obj.into_iter() - .map(|(k, v)| (k.clone(), v.to_string())) - .collect::>() - }) - .unwrap_or_default() - }) - .unwrap_or_default(); - - let telemetry_event = TelemetryEvent { - name: APTOS_NODE_CONFIG_EVENT_NAME.into(), - params: node_config, - }; - send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; -} - -/// Collects and sends the core node metrics via telemetry -async fn send_node_core_metrics( - peer_id: String, - chain_id: String, - node_config: &NodeConfig, - telemetry_sender: Option, -) { - let telemetry_event = create_core_metric_telemetry_event(node_config).await; - send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; -} - -/// Collects and sends the node network metrics via telemetry -async fn send_node_network_metrics( - peer_id: String, - chain_id: String, - telemetry_sender: Option, -) { - let telemetry_event = create_network_metric_telemetry_event().await; - send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; -} - -/// Collects and sends the system information via telemetry -async fn send_system_information( - peer_id: String, - chain_id: String, - telemetry_sender: Option, -) { - let telemetry_event = create_system_info_telemetry_event().await; - send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; -} - -/// Fetches the IP address and sends the given telemetry event -/// along with the IP address. Also sends a randomly generated -/// token to help correlate metrics across events. -pub(crate) async fn send_telemetry_event_with_ip( - peer_id: String, - chain_id: String, - telemetry_sender: Option, - telemetry_event: TelemetryEvent, -) -> JoinHandle<()> { - // Update the telemetry event with the ip address and random token - let TelemetryEvent { name, mut params } = telemetry_event; - params.insert(IP_ADDRESS_KEY.to_string(), get_origin_ip().await); - params.insert(TELEMETRY_TOKEN_KEY.to_string(), TELEMETRY_TOKEN.clone()); - params.insert(CHAIN_ID_KEY.into(), chain_id); - let telemetry_event = TelemetryEvent { name, params }; - - // Send the telemetry event - send_telemetry_event(peer_id, telemetry_sender, telemetry_event).await -} - -/// Gets the IP origin of the machine by pinging a url. -/// If none is found, returns UNKNOWN. -async fn get_origin_ip() -> String { - let resp = reqwest::get(HTTPBIN_URL).await; - match resp { - Ok(json) => match json.json::().await { - Ok(origin_ip) => origin_ip.origin, - Err(_) => UNKNOWN_METRIC_VALUE.into(), - }, - Err(_) => UNKNOWN_METRIC_VALUE.into(), - } -} - -/// Sends the given event and params to the telemetry endpoint -async fn send_telemetry_event( - peer_id: String, - telemetry_sender: Option, - telemetry_event: TelemetryEvent, -) -> JoinHandle<()> { - // Parse the Google analytics env variables - let api_secret = - env::var(ENV_GA_API_SECRET).unwrap_or_else(|_| APTOS_GA_API_SECRET.to_string()); - let measurement_id = - env::var(ENV_GA_MEASUREMENT_ID).unwrap_or_else(|_| APTOS_GA_MEASUREMENT_ID.to_string()); - - // Create and send the telemetry dump - let event_name = telemetry_event.name.clone(); - let timestamp_micros = match SystemTime::now().duration_since(UNIX_EPOCH) { - Ok(duration) => duration.as_micros().to_string(), - Err(_) => UNKNOWN_METRIC_VALUE.into(), - }; - let telemetry_dump = TelemetryDump { - client_id: Uuid::new_v4().to_string(), // We generate a random client id for each request - user_id: peer_id, - timestamp_micros, - events: vec![telemetry_event], - }; - if telemetry_sender.is_none() { - // telemetry_sender is None for Aptos CLI. - spawn_event_sender_to_google_analytics( - api_secret, - measurement_id, - event_name, - telemetry_dump, - ) - } else { - // Aptos nodes send their metrics to aptos-telemetry-service crate. - spawn_event_sender_to_telemetry_service(event_name, telemetry_sender, telemetry_dump) - } -} - -/// Spawns the telemetry event sender on a new thread to avoid blocking -fn spawn_event_sender_to_telemetry_service( - event_name: String, - telemetry_sender: Option, - telemetry_dump: TelemetryDump, -) -> JoinHandle<()> { - tokio::spawn(async move { - telemetry_sender - .unwrap() - .try_send_custom_metrics(event_name, telemetry_dump) - .await; - }) -} - -/// Spawns the telemetry event sender on a new thread to avoid blocking -fn spawn_event_sender_to_google_analytics( - api_secret: String, - measurement_id: String, - event_name: String, - telemetry_dump: TelemetryDump, -) -> JoinHandle<()> { - tokio::spawn(async move { - // Create a request client - let client = reqwest::Client::new(); - - // Send the request and wait for a response - let send_result = client - .post(format!( - "{}?&measurement_id={}&api_secret={}", - GA4_URL, measurement_id, api_secret - )) - .json::(&telemetry_dump) - .send() - .await; - - // Process the response - match send_result { - Ok(response) => { - let status_code = response.status(); - if status_code.is_success() { - debug!( - "Sent telemetry event {}, data: {:?}", - event_name, &telemetry_dump - ); - metrics::increment_telemetry_successes(&event_name); - } else { - debug!( - "Failed to send telemetry event! Status: {}, event: {}.", - response.status(), - event_name - ); - debug!("Failed telemetry response: {:?}", response.text().await); - metrics::increment_telemetry_failures(&event_name); - } - }, - Err(error) => { - debug!( - "Failed to send telemetry event: {}. Error: {:?}", - event_name, error - ); - metrics::increment_telemetry_failures(&event_name); - }, - } - }) -} - -/// A json struct useful for fetching the machine origin/IP -#[derive(Deserialize)] -struct OriginIP { - origin: String, -} diff --git a/crates/aptos-telemetry/src/system_information.rs b/crates/aptos-telemetry/src/system_information.rs deleted file mode 100644 index 62cd6a8b067ac..0000000000000 --- a/crates/aptos-telemetry/src/system_information.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::utils; -use aptos_infallible::Mutex; -use aptos_telemetry_service::types::telemetry::TelemetryEvent; -use once_cell::sync::Lazy; -use std::collections::BTreeMap; -use sysinfo::{CpuExt, DiskExt, System, SystemExt}; - -/// System information event name -const APTOS_NODE_SYSTEM_INFORMATION: &str = "APTOS_NODE_SYSTEM_INFORMATION"; - -/// System information keys -const CPU_BRAND: &str = "cpu_brand"; -const CPU_COUNT: &str = "cpu_count"; -const CPU_CORE_COUNT: &str = "cpu_core_count"; -const CPU_FREQUENCY: &str = "cpu_frequency"; -const CPU_NAME: &str = "cpu_name"; -const CPU_VENDOR_ID: &str = "cpu_vendor_id"; -const DISK_AVAILABLE_SPACE: &str = "disk_available_space"; -const DISK_COUNT: &str = "disk_count"; -const DISK_FILE_SYSTEM: &str = "disk_file_system"; -const DISK_NAME: &str = "disk_name"; -const DISK_TOTAL_SPACE: &str = "disk_total_space"; -const DISK_TYPE: &str = "disk_type"; -const MEMORY_AVAILABLE: &str = "memory_available"; -const MEMORY_TOTAL: &str = "memory_total"; -const MEMORY_USED: &str = "memory_used"; -const SYSTEM_HOST_NAME: &str = "system_host_name"; -const SYSTEM_KERNEL_VERSION: &str = "system_kernel_version"; -const SYSTEM_NAME: &str = "system_name"; -const SYSTEM_OS_VERSION: &str = "system_os_version"; - -/// Global system singleton (to avoid recreations) -pub static GLOBAL_SYSTEM: Lazy> = Lazy::new(|| Mutex::new(System::new_all())); - -/// Collects and sends the build information via telemetry -pub(crate) async fn create_system_info_telemetry_event() -> TelemetryEvent { - // Collect the system information - let system_information = get_system_information(); - - // Create and return a new telemetry event - TelemetryEvent { - name: APTOS_NODE_SYSTEM_INFORMATION.into(), - params: system_information, - } -} - -/// Used to expose system information -pub fn get_system_information() -> BTreeMap { - let mut system_information: BTreeMap = BTreeMap::new(); - collect_system_info(&mut system_information); - system_information -} - -/// Collects the system info and appends it to the given map -pub(crate) fn collect_system_info(system_information: &mut BTreeMap) { - // Note: this might be expensive, so it shouldn't be done often - GLOBAL_SYSTEM.lock().refresh_system(); - GLOBAL_SYSTEM.lock().refresh_disks(); - - // Collect relevant and available system information - collect_cpu_info(system_information, &GLOBAL_SYSTEM); - collect_disk_info(system_information, &GLOBAL_SYSTEM); - collect_memory_info(system_information, &GLOBAL_SYSTEM); - collect_sys_info(system_information, &GLOBAL_SYSTEM); -} - -/// Collects the cpu info and appends it to the given map -fn collect_cpu_info( - system_information: &mut BTreeMap, - system: &Lazy>, -) { - // Collect the number of CPUs and cores - let system_lock = system.lock(); - let cpus = system_lock.cpus(); - system_information.insert(CPU_COUNT.into(), cpus.len().to_string()); - utils::insert_optional_value( - system_information, - CPU_CORE_COUNT, - system_lock - .physical_core_count() - .map(|count| count.to_string()), - ); - - // Collect the overall CPU info - let global_cpu = system_lock.global_cpu_info(); - system_information.insert(CPU_BRAND.into(), global_cpu.brand().into()); - system_information.insert(CPU_FREQUENCY.into(), global_cpu.frequency().to_string()); - system_information.insert(CPU_NAME.into(), global_cpu.name().into()); - system_information.insert(CPU_VENDOR_ID.into(), global_cpu.vendor_id().into()); -} - -/// Collects the disk info and appends it to the given map -fn collect_disk_info( - system_information: &mut BTreeMap, - system: &Lazy>, -) { - // Collect the number of disks - let system_lock = system.lock(); - let disks = system_lock.disks(); - utils::insert_optional_value( - system_information, - DISK_COUNT, - Some(disks.len().to_string()), - ); - - // If there's no disks found, return. - if disks.is_empty() { - return; - } - - // Identify the index of the largest disk - let mut largest_disk_index = 0; - let mut largest_disk_size = 0; - for (index, disk) in disks.iter().enumerate() { - let disk_size = disk.total_space(); - if disk_size > largest_disk_size { - largest_disk_index = index; - largest_disk_size = disk_size; - } - } - - // Collect the information for the largest disk - let disk = &disks[largest_disk_index]; - system_information.insert( - DISK_AVAILABLE_SPACE.into(), - disk.available_space().to_string(), - ); - system_information.insert(DISK_FILE_SYSTEM.into(), format!("{:?}", disk.file_system())); - system_information.insert(DISK_NAME.into(), format!("{:?}", disk.name())); - system_information.insert(DISK_TOTAL_SPACE.into(), disk.total_space().to_string()); - system_information.insert(DISK_TYPE.into(), format!("{:?}", disk.type_())); -} - -/// Collects the memory info and appends it to the given map -fn collect_memory_info( - system_information: &mut BTreeMap, - system: &Lazy>, -) { - // Collect the information for the memory - let system_lock = system.lock(); - system_information.insert( - MEMORY_AVAILABLE.into(), - system_lock.available_memory().to_string(), - ); - system_information.insert(MEMORY_TOTAL.into(), system_lock.total_memory().to_string()); - system_information.insert(MEMORY_USED.into(), system_lock.used_memory().to_string()); -} - -/// Collects the sys info and appends it to the given map -fn collect_sys_info( - system_information: &mut BTreeMap, - system: &Lazy>, -) { - utils::insert_optional_value( - system_information, - SYSTEM_HOST_NAME, - system.lock().host_name(), - ); - utils::insert_optional_value( - system_information, - SYSTEM_KERNEL_VERSION, - system.lock().kernel_version(), - ); - utils::insert_optional_value(system_information, SYSTEM_NAME, system.lock().name()); - utils::insert_optional_value( - system_information, - SYSTEM_OS_VERSION, - system.lock().long_os_version(), - ); -} diff --git a/crates/aptos-telemetry/src/telemetry_log_sender.rs b/crates/aptos-telemetry/src/telemetry_log_sender.rs deleted file mode 100644 index ded83421fd298..0000000000000 --- a/crates/aptos-telemetry/src/telemetry_log_sender.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{metrics::increment_log_ingest_too_large_by, sender::TelemetrySender}; -use aptos_logger::{prelude::*, telemetry_log_writer::TelemetryLog}; -use futures::{channel::mpsc, StreamExt}; -use std::time::Duration; -use tokio::time::interval; -use tokio_stream::wrappers::IntervalStream; - -const MAX_BYTES: usize = 128 * 1024; -const MAX_BATCH_TIME: Duration = Duration::from_secs(5); - -/// Buffered -pub(crate) struct TelemetryLogSender { - sender: TelemetrySender, - batch: Vec, - max_bytes: usize, - current_bytes: usize, -} - -impl TelemetryLogSender { - pub fn new(sender: TelemetrySender) -> Self { - Self { - // TODO: use an existing sender? - sender, - batch: Vec::new(), - max_bytes: MAX_BYTES, - current_bytes: 0, - } - } - - fn drain_batch(&mut self) -> Vec { - let batch: Vec<_> = self.batch.drain(..).collect(); - self.current_bytes = 0; - batch - } - - pub(crate) fn add_to_batch(&mut self, log: String) -> Option> { - if log.len() > self.max_bytes { - warn!("Log ignored, size: {}", log.len()); - increment_log_ingest_too_large_by(1); - return None; - } - - self.current_bytes += log.len(); - self.batch.push(log); - - if self.current_bytes > self.max_bytes { - return Some(self.drain_batch()); - } - None - } - - pub async fn handle_next_log(&mut self, log: TelemetryLog) { - match log { - TelemetryLog::Log(log) => { - if let Some(batch) = self.add_to_batch(log) { - self.sender.try_send_logs(batch).await; - } - }, - TelemetryLog::Flush(tx) => { - self.flush_batch().await; - let _ = tx.send(()); - }, - } - } - - pub async fn flush_batch(&mut self) { - if !self.batch.is_empty() { - let drained = self.drain_batch(); - self.sender.try_send_logs(drained).await; - } - } - - pub async fn start(mut self, mut rx: mpsc::Receiver) { - debug!("Started Telemetry Log Sender"); - let mut interval = IntervalStream::new(interval(MAX_BATCH_TIME)).fuse(); - - loop { - ::futures::select! { - log = rx.select_next_some() => { - self.handle_next_log(log).await; - }, - _ = interval.select_next_some() => { - self.flush_batch().await; - }, - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - sender::TelemetrySender, - telemetry_log_sender::{TelemetryLogSender, MAX_BYTES}, - }; - use aptos_config::config::NodeConfig; - use aptos_types::chain_id::ChainId; - use reqwest::Url; - - #[tokio::test] - async fn test_add_to_batch() { - let telemetry_sender = TelemetrySender::new( - Url::parse("https://telemetry.svc").expect("unable to parse url"), - ChainId::test(), - &NodeConfig::default(), - ); - let mut sender = TelemetryLogSender::new(telemetry_sender); - - for _i in 0..2 { - // Large batch should not be allowed - let batch = sender.add_to_batch("a".repeat(MAX_BYTES + 1)); - assert!(batch.is_none()); - - // Batch is flushed before reaching size - let to_send = vec!["test"]; - let batch = sender.add_to_batch(to_send[0].to_string()); - assert!(batch.is_none()); - let batch = sender.drain_batch(); - assert_eq!(batch.len(), 1); - assert_eq!(batch, to_send); - - // Create batch that reaches max bytes - let bytes_per_string = 11; - let mut num_strings = (MAX_BYTES + 1) / bytes_per_string; - if (MAX_BYTES + 1) % bytes_per_string != 0 { - num_strings += 1; - } - let to_send: Vec<_> = (0..num_strings).map(|i| format!("{:11}", i)).collect(); - to_send.iter().enumerate().for_each(|(i, s)| { - // Large batch should not be allowed - let batch = sender.add_to_batch("a".repeat(MAX_BYTES + 1)); - assert!(batch.is_none()); - - let batch = sender.add_to_batch(s.clone()); - if i == (num_strings - 1) { - assert!(batch.is_some()); - assert_eq!(batch.unwrap(), to_send); - } else { - assert!(batch.is_none()); - } - }) - } - } -} diff --git a/crates/aptos-telemetry/src/utils.rs b/crates/aptos-telemetry/src/utils.rs deleted file mode 100644 index e7f9340353c70..0000000000000 --- a/crates/aptos-telemetry/src/utils.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use aptos_telemetry_service::types::telemetry::TelemetryEvent; -use prometheus::proto::MetricFamily; -use std::collections::BTreeMap; - -/// Build information event name -const APTOS_NODE_BUILD_INFORMATION: &str = "APTOS_NODE_BUILD_INFORMATION"; -/// Build information keys -pub const BUILD_CHAIN_ID: &str = "build_chain_id"; - -/// Collects and sends the build information via telemetry -pub(crate) async fn create_build_info_telemetry_event( - build_info: BTreeMap, -) -> TelemetryEvent { - // Create and return a new telemetry event - TelemetryEvent { - name: APTOS_NODE_BUILD_INFORMATION.into(), - params: build_info, - } -} - -/// Inserts an optional value into the given map iff the value exists -pub(crate) fn insert_optional_value( - map: &mut BTreeMap, - key: &str, - value: Option, -) { - if let Some(value) = value { - map.insert(key.to_string(), value); - } -} - -/// Sums all gauge counts in the given set of metric families -pub fn sum_all_gauges(metric_families: &Vec) -> f64 { - let mut gauge_sum = 0.0; - for metric_family in metric_families { - for metric in metric_family.get_metric() { - gauge_sum += metric.get_gauge().get_value(); - } - } - gauge_sum -} - -/// Sums all histogram sample counts in the given set of metric families -pub fn sum_all_histogram_counts(metric_families: &Vec) -> f64 { - let mut count_sum = 0.0; - for metric_family in metric_families { - for metric in metric_family.get_metric() { - count_sum += metric.get_histogram().get_sample_count() as f64 - } - } - count_sum -} - -/// Sums all histogram sample sums in the given set of metric families -pub fn sum_all_histogram_sums(metric_families: &Vec) -> f64 { - let mut count_sum = 0.0; - for metric_family in metric_families { - for metric in metric_family.get_metric() { - count_sum += metric.get_histogram().get_sample_sum() - } - } - count_sum -} diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md deleted file mode 100644 index c7727cb67cbef..0000000000000 --- a/crates/aptos/CHANGELOG.md +++ /dev/null @@ -1,171 +0,0 @@ -# Aptos CLI Changelog - -All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -## Unreleased -- Update `self_update` dependency to support situations where relevant directories (e.g. `/tmp`) exist on different filesystems. - -## [3.0.2] - 2024/03/12 -- Increased `max_connections` for postgres container created as part of local testnet to address occasional startup failures due to overloaded DB. - -## [3.0.1] - 2024/03/05 -- Fix bug in `aptos update revela` if default install directory doesn't exist. - -## [3.0.0] - 2024/03/05 -- **Breaking Change**: `aptos update` is now `aptos update aptos`. -- Added `aptos update revela`. This installs / updates the `revela` binary, which is needed for the new `aptos move decompile` subcommand. -- Extended `aptos move download` with an option `--bytecode` to also download the bytecode of a module -- Integrated the Revela decompiler which is now available via `aptos move decompile` -- Extended `aptos move disassemble` and the new `aptos move decompile` to also work on entire packages instead of only single files - -## [2.5.0] - 2024/02/27 -- Updated CLI source compilation to use rust toolchain version 1.75.0 (from 1.74.1). -- Upgraded indexer processors for local testnet from 9936ec73cef251fb01fd2c47412e064cad3975c2 to d44b2d209f57872ac593299c34751a5531b51352. Upgraded Hasura metadata accordingly. -- Added support for objects processor in local testnet and enabled it by default. - -## [2.4.0] - 2024/01/05 -- Hide the V2 compiler from input options until the V2 compiler is ready for release -- Updated CLI source compilation to use rust toolchain version 1.74.1 (from 1.72.1). -- Added `for` loop. - - Syntax: `for (iter in lower_bound..upper_bound) { loop_body }` with integer bounds. - - Documentation: https://aptos.dev/move/book/loops -- Upgraded indexer processors for local testnet from 2d5cb211a89a8705674e9e1e741c841dd899c558 to 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. Upgraded Hasura metadata accordingly. -- Upgraded Hasura GraphQL engine image from 2.35.0 to 2.36.1. - -## [2.3.2] - 2023/11/28 -- Services in the local testnet now bind to 127.0.0.1 by default (unless the CLI is running inside a container, which most users should not do) rather than 0.0.0.0. You can override this behavior with the `--bind-to` flag. This fixes an issue preventing the local testnet from working on Windows. - -## [2.3.1] - 2023/11/07 -### Updated -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to 2d5cb211a89a8705674e9e1e741c841dd899c558. -- Improved reliability of inter-container networking with local testnet. - -## [2.3.0] - 2023/10/25 -### Added -- Added `--node-api-key`. This lets you set an API key for the purpose of not being ratelimited. - -### Updated -- Made the local testnet exit more quickly if a service fails to start. -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to bcba94c26c8a6372056d2b69ce411c5719f98965. - -### Fixed -- Fixed an infrequent bug that caused startup failures for the local testnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage. -- Fixed an issue where the CLI could not find the Docker socket with some Docker Desktop configurations. - -## [2.2.2] - 2023/10/16 -### Updated -- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to d6f55d4baba32960ea7be60878552e73ffbe8b7e. - -## [2.2.1] - 2023/10/13 -### Fixed -- Fixed postgres data persistence between restarts when using `aptos node run-local-testnet --with-indexer-api`. - -## [2.2.0] - 2023/10/11 -### Added -- Added `--with-indexer-api` to `aptos node run-local-testnet`. With this flag you can run a full processor + indexer API stack as part of your local testnet. You must have Docker installed to use this feature. For more information, see https://aptos.dev/nodes/local-testnet/local-testnet-index. -### Updated -- Updated CLI source compilation to use rust toolchain version 1.72.1 (from 1.71.1). - -## [2.1.1] - 2023/09/27 -### Added -- Added an option `--print-metadata` to the command `aptos move download` to print out the metadata of the package to be downloaded. - - Example: `aptos move download --account 0x1 --package AptosFramework --url https://mainnet.aptoslabs.com/v1 --print-metadata` -### Updated -- The `--with-faucet` flag has been removed from `aptos node run-local-testnet`, we now run a faucet by default. To disable the faucet use the `--no-faucet` flag. -- **Breaking change**: When using `aptos node run-local-testnet` we now expose a transaction stream. Learn more about the transaction stream service here: https://aptos.dev/indexer/txn-stream/. Opt out of this with `--no-txn-stream`. This is marked as a breaking change since the CLI now uses a port (50051 by default) that it didn't used to. If you need this port, you can tell the CLI to use a different port with `--txn-stream-port`. - -## [2.1.0] - 2023/08/24 -### Updated -- Updated CLI source compilation to use rust toolchain version 1.71.1 (from 1.71.0). -### Added -- Added basic ledger support for CLI - - Example: `aptos init --ledger` to create a new profile from ledger. After this, you can use it the same way as other profiles. - - Note: `Ledger Nano s Plus` or `Ledger Nano X` is highly recommended. - -## [2.0.3] - 2023/08/04 -### Fixed -- Fixed the following input arguments issue when running `aptos move view` - - #8513: Fixed issue where CLI does not work with big numbers - - #8982: Fixed args issue when passing in u64/u128/u256 parameters -### Update -- CLI documentation refactor -- Updated CLI source compilation to use rust toolchain version 1.71.0 (from 1.70.0). -### Fixed -* Verify package now does not fail on a mismatched upgrade number - -## [2.0.2] - 2023/07/06 -### Added -- Added account lookup by authentication key - - Example: `account lookup-address --auth-key {your_auth_key}` -### Updated -- Updated CLI source compilation to use rust toolchain version 1.70.0 (from 1.66.1). -- Set 2 seconds timeout for telemetry -### Removed -- init command from config subcommand is removed. Please use init from the root command. - - Example: `aptos config init` -> `aptos init` -### Fixed -- Panic issue when running `aptos move test` is fixed - GitHub issue #8516 - -## [2.0.1] - 2023/06/05 -### Fixed -- Updated txn expiration configuration for the faucet built into the CLI to make local testnet startup more reliable. - -## [2.0.0] - 2023/06/01 -### Added -- Multisig v2 governance support -- JSON input file support -- Builder Pattern support for RestClient - - NOTE: Methods **new_with_timeout** and **new_with_timeout_and_user_agent** are no longer available. -- Added custom header *x-aptos-client* for analytic purpose - -## [1.0.14] - 2023/05/26 -- Updated DB bootstrap command with new DB restore features -- Nested vector arg support - - **Breaking change**: You can no longer pass in a vector like this: `--arg vector
    :0x1,0x2`, you must do it like this: `--arg 'address:["0x1", "0x2"]'` - -## [1.0.13] - 2023/04/27 -### Fixed -* Previously `--skip-fetch-latest-git-deps` would not actually do anything when used with `aptos move test`. This has been fixed. -* Fixed the issue of the hello_blockchain example where feature enable was missing - -## [1.0.12] - 2023/04/25 -### Added -* Support for creating and interacting with multisig accounts v2. More details can be found at [AIP 12](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-12.md). -* Added `disassemble` option to the CLI - This can be invoked using `aptos move disassemble` to disassemble the bytecode and save it to a file -* Fixed handling of `vector` as an entry function argument in `aptos move run` - -## [1.0.11] - 2023/04/14 -### Fixed -* Fixed creating a new test account with `aptos init` would fail if the account didn't already exist - -## [1.0.10] - 2023/04/13 -### Fixed -* If `aptos init` is run with a faucet URL specified (which happens by default when using the local, devnet, or testnet network options) and funding the account fails, the account creation is considered a failure and nothing is persisted. Previously it would report success despite the account not being created on chain. -* When specifying a profile where the `AuthenticationKey` has been rotated, now the `AccountAddress` is properly used from the config file -* Update `aptos init` to fix an incorrect account address issue, when trying to init with a rotated private key. Right now it does an actual account lookup instead of deriving from public key - -### Added -* Updates to prover and framework specs - -## [1.0.9] - 2023/03/29 -### Added -* `aptos move show abi` allows for viewing the ABI of a compiled move package -* Experimental gas profiler with the `--profile-gas` flag on any transaction submitting CLI command -* Updates to the prover and framework specs - -## [1.0.8] - 2023/03/16 -### Added -* Added an `aptos account derive-resource-account-address` command to add the ability to derive an address easily -* Added the ability for different input resource account seeds, to allow matching directly with onchain code -* Added beta support for coverage via `aptos move coverage` and `aptos move test --coverage` -* Added beta support for compiling with bytecode dependencies rather than source dependencies - -### Fixed -* All resource account commands can now use `string_seed` which will match the onchain representation of `b"string"` rather than always derive a different address -* Tests that go over the bytecode size limit can now compile -* `vector` inputs to now work for both `aptos move view` and `aptos move run` -* Governance proposal listing will now not crash on the latest on-chain format -* Move compiler will no longer use an environment variable to communicate between compiler and CLI for the bytecode version - -## [1.0.7] -* For logs earlier than 1.0.7, please check out the [releases on GitHub](https://github.com/aptos-labs/aptos-core/releases?q="Aptos+CLI+Release") diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml deleted file mode 100644 index 8a46484315491..0000000000000 --- a/crates/aptos/Cargo.toml +++ /dev/null @@ -1,114 +0,0 @@ -[package] -name = "aptos" -description = "Aptos tool for management of nodes and interacting with the blockchain" -version = "3.0.2" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-api-types = { workspace = true } -aptos-backup-cli = { workspace = true } -aptos-bitvec = { workspace = true } -aptos-build-info = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-cli-common = { workspace = true } -aptos-config = { workspace = true } -aptos-crypto = { workspace = true } -aptos-faucet-core = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas-profiling = { workspace = true } -aptos-gas-schedule = { workspace = true } -aptos-genesis = { workspace = true } -aptos-github-client = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-indexer-grpc-utils = { workspace = true } -aptos-keygen = { workspace = true } -aptos-ledger = { workspace = true } -aptos-logger = { workspace = true } -aptos-move-debugger = { workspace = true } -aptos-network-checker = { workspace = true } -aptos-node = { workspace = true } -aptos-protos = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-sdk = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-telemetry = { workspace = true } -aptos-temppath = { workspace = true } -aptos-types = { workspace = true } -aptos-vm = { workspace = true, features = ["testing"] } -aptos-vm-genesis = { workspace = true } -async-trait = { workspace = true } -base64 = { workspace = true } -bcs = { workspace = true } -bollard = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true, features = ["env", "unstable-styles"] } -clap_complete = { workspace = true } -codespan-reporting = { workspace = true } -dashmap = { workspace = true } -diesel = { workspace = true, features = [ - "postgres_backend", -] } -diesel-async = { workspace = true } -dirs = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -maplit = { workspace = true } -move-binary-format = { workspace = true } -move-bytecode-source-map = { workspace = true } -move-cli = { workspace = true } -move-command-line-common = { workspace = true } -move-compiler = { workspace = true } -move-core-types = { workspace = true } -move-coverage = { workspace = true } -move-disassembler = { workspace = true } -move-ir-types = { workspace = true } -move-package = { workspace = true } -move-symbol-pool = { workspace = true } -move-unit-test = { workspace = true, features = ["debugging"] } -move-vm-runtime = { workspace = true, features = ["testing"] } -once_cell = { workspace = true } -pathsearch = { workspace = true } -poem = { workspace = true } -processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "d44b2d209f57872ac593299c34751a5531b51352" } -rand = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -self_update = { git = "https://github.com/banool/self_update.git", rev = "8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b", features = ["archive-zip", "compression-zip-deflate"] } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "d44b2d209f57872ac593299c34751a5531b51352" } -tempfile = { workspace = true } -termcolor = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -toml = { workspace = true } -tonic = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } -version-compare = { workspace = true } -walkdir = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } - -[features] -default = [] -fuzzing = [] -no-upload-proposal = [] -indexer = ["aptos-node/indexer"] -cli-framework-test-move = [] - -[build-dependencies] -shadow-rs = { workspace = true } diff --git a/crates/aptos/README.md b/crates/aptos/README.md deleted file mode 100644 index e04ee0773aeca..0000000000000 --- a/crates/aptos/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Aptos Command Line Interface (CLI) Tool - -The `aptos` tool is a command line interface (CLI) for debugging, development, and node operation. - -See [Aptos CLI Documentation](https://aptos.dev/tools/aptos-cli/) for how to install the `aptos` CLI tool and how to use it. diff --git a/crates/aptos/build.rs b/crates/aptos/build.rs deleted file mode 100644 index 6cc52885b350d..0000000000000 --- a/crates/aptos/build.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -fn main() -> shadow_rs::SdResult<()> { - shadow_rs::new() -} diff --git a/crates/aptos/debug-move-example/Move.toml b/crates/aptos/debug-move-example/Move.toml deleted file mode 100644 index 80571522a0665..0000000000000 --- a/crates/aptos/debug-move-example/Move.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "DebugDemo" -version = "0.0.0" - -[addresses] -DebugDemo = "0x1" - -[dependencies] -AptosFramework = { local = "../../../aptos-move/framework/aptos-framework" } diff --git a/crates/aptos/debug-move-example/sources/DebugDemo.move b/crates/aptos/debug-move-example/sources/DebugDemo.move deleted file mode 100644 index 03a4254064da6..0000000000000 --- a/crates/aptos/debug-move-example/sources/DebugDemo.move +++ /dev/null @@ -1,32 +0,0 @@ -module DebugDemo::Message { - use std::string; - use std::signer; - use aptos_std::debug; - - struct MessageHolder has key { - message: string::String, - } - - - public entry fun set_message(account: signer, message_bytes: vector) - acquires MessageHolder { - debug::print_stack_trace(); - let message = string::utf8(message_bytes); - let account_addr = signer::address_of(&account); - if (!exists(account_addr)) { - move_to(&account, MessageHolder { - message, - }) - } else { - let old_message_holder = borrow_global_mut(account_addr); - old_message_holder.message = message; - } - } - - #[test(account = @0x1)] - public entry fun sender_can_set_message(account: signer) acquires MessageHolder { - let addr = signer::address_of(&account); - debug::print
    (&addr); - set_message(account, b"Hello, Blockchain"); - } -} diff --git a/crates/aptos/e2e/README.md b/crates/aptos/e2e/README.md deleted file mode 100644 index c3bcf71de0cc6..0000000000000 --- a/crates/aptos/e2e/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# CLI test suite -This directory contains Python code to help with running the CLI test suite. - -## Requirements -We use [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management: - -``` -curl -sSL https://install.python-poetry.org | python3 - -``` - -Once you have Poetry, you can install the dependencies for the testing framework like this: -``` -poetry config virtualenvs.in-project true # This helps with IDE integration -poetry install -``` - -To learn how to use the CLI testing framework, run this: -``` -poetry run python main.py -h -``` - -For example, using the CLI from an image: -``` -poetry run python main.py --base-network mainnet --test-cli-tag nightly -``` - -Using the CLI from a local path: -``` -poetry run python main.py -d --base-network mainnet --test-cli-path ~/aptos-core/target/debug/aptos -``` - -## Debugging -If you are get an error message similar to this: - -### CPU architecture -``` -docker: no matching manifest for linux/arm64/v8 in the manifest list entries. -``` - -Try running the poetry command with this env var: -``` -DOCKER_DEFAULT_PLATFORM=linux/amd64 poetry run python main.py --base-network testnet --test-cli-path ~/aptos-core/target/debug/aptos -``` -This makes the docker commands use the x86_64 images since we don't publish images for ARM. - -### CLI config type -If you see an error like this: -``` -Traceback (most recent call last): - File "/Users/dport/a/core/crates/aptos/e2e/main.py", line 194, in - if main(): - File "/Users/dport/a/core/crates/aptos/e2e/main.py", line 156, in main - run_helper.prepare() - File "/Users/dport/a/core/crates/aptos/e2e/test_helpers.py", line 155, in prepare - self.prepare_cli() - File "/Users/dport/a/core/crates/aptos/e2e/test_helpers.py", line 189, in prepare_cli - raise RuntimeError( -RuntimeError: When using --test-cli-path you must use workspace configuration, try running `aptos config set-global-config --config-type workspace` -``` - -It is because you are using the `--test-cli-path` flag but have configured the CLI to use the `global` config type. This is not currently compatible, you must switch the config type to workspace prior to running the E2E tests: -``` -aptos config set-global-config --config-type workspace -``` - -### My test doesn't work -You might be getting output like this: -``` -2023-06-22 20:27:07,533 - ERROR - These tests failed: -2023-06-22 20:27:07,533 - ERROR - test_move_compile_script ... -``` - -The best place to look is the test framework working directory, by default `/tmp/aptos-cli-tests`. - -In this directory you will find lots of useful output. For example, you can see the stdout of one of the commands in a test case like this: -``` -$ cat /tmp/aptos-cli-tests/out/007_test_move_compile_script.stdout -{ - "Error": "Move compilation failed: Unable to resolve packages for package 'TwoByTwoTransfer': While resolving dependency 'AptosFramework' in package 'TwoByTwoTransfer': While processing dependency 'AptosFramework': Unable to find package manifest for 'AptosFramework' at \"Move.toml/move/scripts/two_by_two_transfer/../../../framework/aptos-framework\"" -} -``` - -For each test there are the following files: -- test_name.command -- test_name.stdout -- test_name.stderr - -This file might also be present if the test threw an exception: -- test_name.exception - -## Writing new test cases -To write a new test case, follow these steps: -1. (Optional) Make a new file in [cases/](cases/) if none of the existing files seem appropriate. -1. Write a new function following these guidelines: - 1. Follow the naming scheme `test_*`. - 1. Decorate the function with the `test_case` decorator. - 1. If you want to assert something, do so by raising an exception (TestError has been provided for this purpose, but any old exception does the trick). - 1. Use the `RunHelper` to invoke CLI commands. Follow the example of other test cases. -1. Register the test in the `run_tests` function in [main.py](main.py). Note that the order matters here, later tests are allowed (and encouraged) to depend on the results of earlier tests. This way we can test truly end-to-end, beyond the span of a single invocation. - -## Formatting: -``` -poetry run isort . -poetry run black . -``` diff --git a/crates/aptos/e2e/cases/__init__.py b/crates/aptos/e2e/cases/__init__.py deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/crates/aptos/e2e/cases/account.py b/crates/aptos/e2e/cases/account.py deleted file mode 100644 index 34dba7d71a334..0000000000000 --- a/crates/aptos/e2e/cases/account.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json -import secrets - -from common import OTHER_ACCOUNT_ONE, TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_account_fund_with_faucet(run_helper: RunHelper, test_name=None): - amount_in_octa = 100000000000 - - # Fund the account. - run_helper.run_command( - test_name, - [ - "aptos", - "account", - "fund-with-faucet", - "--account", - run_helper.get_account_info().account_address, - "--amount", - str(amount_in_octa), - ], - ) - - # Assert it has the requested balance. - balance = int( - run_helper.api_client.account_balance( - run_helper.get_account_info().account_address - ) - ) - if balance == amount_in_octa: - raise TestError( - f"Account {run_helper.get_account_info().account_address} has balance {balance}, expected {amount_in_octa}" - ) - - -@test_case -def test_account_create_and_transfer(run_helper: RunHelper, test_name=None): - # Create the new account. - run_helper.run_command( - test_name, - [ - "aptos", - "account", - "create", - "--account", - OTHER_ACCOUNT_ONE.account_address, - "--assume-yes", - ], - ) - - # Assert it exists and has zero balance. - balance = int( - run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) - ) - if balance != 0: - raise TestError( - f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected 0" - ) - - transfer_amount = 1000 - - run_helper.run_command( - test_name, - [ - "aptos", - "account", - "transfer", - "--account", - OTHER_ACCOUNT_ONE.account_address, - "--amount", - str(transfer_amount), - "--assume-yes", - ], - ) - - balance = int( - run_helper.api_client.account_balance(OTHER_ACCOUNT_ONE.account_address) - ) - - if balance != transfer_amount: - raise TestError( - f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected {transfer_amount}" - ) - - -@test_case -def test_account_list(run_helper: RunHelper, test_name=None): - # List the created account - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "list", - "--account", - OTHER_ACCOUNT_ONE.account_address, - ], - ) - - json_result = json.loads(result.stdout) - found_account = False - - # Check if the resource account is in the list - for module in json_result["Result"]: - if module.get("0x1::account::Account") != None: - found_account = True - - if not found_account: - raise TestError( - "Cannot find the account in the account list after account creation" - ) - - -@test_case -def test_account_lookup_address(run_helper: RunHelper, test_name=None): - # Create the new account. - result_addr = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "lookup-address", - "--auth-key", - run_helper.get_account_info().account_address, # initially the account address is the auth key - ], - ) - - if run_helper.get_account_info().account_address not in result_addr.stdout: - raise TestError( - f"lookup-address result does not match {run_helper.get_account_info().account_address}" - ) - - -@test_case -def test_account_rotate_key(run_helper: RunHelper, test_name=None): - # Generate new private key - new_private_key = secrets.token_hex(32) - - # Current account info - old_profile = run_helper.get_account_info() - - # Rotate the key. - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "rotate-key", - "--new-private-key", - new_private_key, - "--assume-yes", - ], - input="no\n", - ) - - if '"success": true' not in result.stdout: - raise TestError( - f"[aptos account rotate-key --new-private-key {new_private_key} --assume-yes] failed" - ) - - new_profile = run_helper.get_account_info() - # Make sure new and old account addresses match - if old_profile.account_address != new_profile.account_address: - raise TestError( - f"Error: Account address changed after rotate-key: {old_profile.account_address} -> {new_profile.account_address}" - ) - - # lookup-address from old public key - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "lookup-address", - f"--public-key={old_profile.public_key}", - ], - ) - response = json.loads(result.stdout) - if response["Result"] != old_profile.account_address: - raise TestError( - f"lookup-address of old public key does not match original address: {old_profile.account_address}" - ) - - # lookup-address with new public key - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "lookup-address", - f"--public-key={new_profile.public_key}", - ], - ) - response = json.loads(result.stdout) - if response["Result"] != old_profile.account_address: - raise TestError( - f"lookup-address of new public key does not match original address: {old_profile.account_address}" - ) - - -@test_case -def test_account_resource_account(run_helper: RunHelper, test_name=None): - # Seed for the resource account - seed = "1" - - # Create the new resource account. - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "create-resource-account", - "--seed", - seed, - "--assume-yes", # assume yes to gas prompt - ], - ) - - result = json.loads(result.stdout) - sender = result["Result"].get("sender") - resource_account_address = result["Result"].get("resource_account") - - if resource_account_address == None or sender == None: - raise TestError("Resource account creation failed") - - # Derive the resource account - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "derive-resource-account-address", - "--seed", - seed, - "--address", - sender, - ], - ) - - if resource_account_address not in result.stdout: - raise TestError( - f"derive-resource-account-address result does not match expected: {resource_account_address}" - ) - - # List the resource account - result = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "list", - "--query=resources", - ], - ) - - json_result = json.loads(result.stdout) - found_resource = False - - # Check if the resource account is in the list - for module in json_result["Result"]: - if module.get("0x1::resource_account::Container") != None: - data = module["0x1::resource_account::Container"]["store"]["data"] - for resource in data: - if resource.get("key") == f"0x{resource_account_address}": - found_resource = True - break - - if not found_resource: - raise TestError( - "Cannot find the resource account in the account list after resource account creation" - ) diff --git a/crates/aptos/e2e/cases/config.py b/crates/aptos/e2e/cases/config.py deleted file mode 100644 index 1ec5aa12e950e..0000000000000 --- a/crates/aptos/e2e/cases/config.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json - -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_config_show_profiles(run_helper: RunHelper, test_name=None): - # Show the profile - response = run_helper.run_command( - test_name, - [ - "aptos", - "config", - "show-profiles", - ], - ) - - expected_profile = run_helper.get_account_info() - profile = json.loads(response.stdout)["Result"]["default"] - if ( - profile["has_private_key"] != True - or profile["public_key"] != expected_profile.public_key - or profile["account"] != expected_profile.account_address - ): - raise TestError( - f"[aptos config show-profiles] shows incorrect profile {profile} -- \n expected {expected_profile}" - ) diff --git a/crates/aptos/e2e/cases/init.py b/crates/aptos/e2e/cases/init.py deleted file mode 100644 index 096e628ada6ad..0000000000000 --- a/crates/aptos/e2e/cases/init.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import os - -import requests -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_init(run_helper: RunHelper, test_name=None): - # Inititalize a profile for the CLI to use. Note that we do not set the - # --skip-faucet flag. This means that in addition to creating a profile locally, - # it will use the faucet to create the account on chain. This will fund the - # account with the default amount of 100000000 OCTA. - run_helper.run_command( - test_name, - ["aptos", "init", "--assume-yes", "--network", "local"], - input="\n", - ) - - # Assert that the CLI config is there. - config_path = os.path.join( - run_helper.host_working_directory, ".aptos", "config.yaml" - ) - if not os.path.exists(config_path): - raise TestError( - f"{config_path} not found (in host working dir) after running aptos init" - ) - - # Assert that it contains info for the account that was created. - account_info = run_helper.get_account_info() - if not account_info: - raise TestError("Failed to read account info from newly created config file") - - # Confirm with the local testnet that it was created. - try: - run_helper.api_client.account(account_info.account_address) - except Exception as e: - raise TestError( - f"Failed to query local testnet for account {account_info.account_address}" - ) from e - - -@test_case -def test_metrics_accessible(run_helper: RunHelper, test_name=None): - # Assert that the metrics endpoint is accessible and returns valid json if - # requested. If the endpoint is not accessible or does not return valid - # JSON this will throw an exception which will be caught as a test failure. - metrics_url = run_helper.get_metrics_url(json=True) - requests.get(metrics_url).json() - - -@test_case -def test_aptos_header_included(run_helper: RunHelper, test_name=None): - # Make sure the aptos-cli header is included on the original request - response = requests.get(run_helper.get_metrics_url()) - - if 'request_source_client="aptos-cli' not in response.text: - raise TestError("Request should contain the correct aptos header: aptos-cli") diff --git a/crates/aptos/e2e/cases/move.py b/crates/aptos/e2e/cases/move.py deleted file mode 100644 index 712c6ce41f2bf..0000000000000 --- a/crates/aptos/e2e/cases/move.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json - -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_move_publish(run_helper: RunHelper, test_name=None): - # Prior to this function running the move/ directory was moved into the working - # directory in the host, which is then mounted into the container. The CLI is - # then run in this directory, meaning the move/ directory is in the same directory - # as the CLI is run from. This is why we can just refer to the package dir starting - # with move/ here. - package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" - - # Publish the module. - run_helper.run_command( - test_name, - [ - "aptos", - "move", - "publish", - "--assume-yes", - "--package-dir", - package_dir, - "--named-addresses", - f"addr={run_helper.get_account_info().account_address}", - ], - ) - - # Get what modules exist on chain. - response = run_helper.run_command( - test_name, - [ - "aptos", - "account", - "list", - "--account", - run_helper.get_account_info().account_address, - "--query", - "modules", - ], - ) - - # Confirm that the module exists on chain. - response = json.loads(response.stdout) - for module in response["Result"]: - if ( - module["abi"]["address"] - == f"0x{run_helper.get_account_info().account_address}" - and module["abi"]["name"] == "cli_e2e_tests" - ): - return - - raise TestError( - "Module apparently published successfully but it could not be found on chain" - ) - - -@test_case -def test_move_compile(run_helper: RunHelper, test_name=None): - package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" - account_info = run_helper.get_account_info() - - # Compile the module. - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "compile", - "--package-dir", - package_dir, - "--named-addresses", - f"addr={account_info.account_address}", - ], - ) - - if f"{account_info.account_address}::cli_e2e_tests" not in response.stdout: - raise TestError("Module did not compile successfully") - - -@test_case -def test_move_compile_dev_mode(run_helper: RunHelper, test_name=None): - package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" - account_info = run_helper.get_account_info() - - # Compile the module. Should not need an address passed in - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "compile", - "--dev", - "--package-dir", - package_dir, - ], - ) - - if f"{account_info.account_address}::cli_e2e_tests" not in response.stdout: - raise TestError("Module did not compile successfully") - - -@test_case -def test_move_compile_script(run_helper: RunHelper, test_name=None): - package_dir = f"move/cli-e2e-tests/{run_helper.base_network}" - account_info = run_helper.get_account_info() - - # Compile the script. - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "compile-script", - "--package-dir", - package_dir, - "--named-addresses", - f"addr={account_info.account_address}", - ], - ) - - if "script_hash" not in response.stdout: - raise TestError("Script did not compile successfully") - - -@test_case -def test_move_run(run_helper: RunHelper, test_name=None): - account_info = run_helper.get_account_info() - - # Run the min_hero entry function with default profile - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "run", - "--assume-yes", - "--function-id", - "default::cli_e2e_tests::mint_hero", - "--args", - "string:Boss", - "string:Male", - "string:Jin", - "string:Undead", - "string:", - ], - ) - - if '"success": true' not in response.stdout: - raise TestError("Move run did not execute successfully") - - # Get what modules exist on chain. - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "view", - "--assume-yes", - "--function-id", - f"0x{account_info.account_address}::cli_e2e_tests::view_hero", - "--args", - f"address:0x{account_info.account_address}", - "string:Hero Quest", - "string:Jin", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("gender") != "Male" and result[0].get("race") != "Undead": - raise TestError( - "Data on chain (view_hero) does not match expected data from (mint_hero)" - ) - - # Run test_move_run to entry function with default profile - # Make sure other parameters are able to be called using "move run" - # Notice the entry function is not running anything but just testing the parameters - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "run", - "--assume-yes", - "--function-id", - "default::cli_e2e_tests::test_move_run", - "--args", - "string:1234", # Notice this is testing u8 vector instead of actual string - "u16:[1,2]", - "u32:[1,2]", - "u64:[1,2]", - "u128:[1,2]", - "u256:[1,2]", - 'address:["0x123","0x456"]', - "bool:[true,false]", - 'string:["abc","efg"]', - ], - ) - - if '"success": true' not in response.stdout: - raise TestError("Move run did not execute successfully") - - -@test_case -def test_move_view(run_helper: RunHelper, test_name=None): - account_info = run_helper.get_account_info() - - # Run the view function - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "view", - "--function-id", - "0x1::account::exists_at", - "--args", - f"address:{account_info.account_address}", - ], - ) - - response = json.loads(response.stdout) - if response["Result"] == None or response["Result"][0] != True: - raise TestError("View function did not return correct result") - - # Test view function with with big number arguments - expected_u64 = 18446744073709551615 - expected_128 = 340282366920938463463374607431768211455 - expected_256 = ( - 115792089237316195423570985008687907853269984665640564039457584007913129639935 - ) - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "view", - "--assume-yes", - "--function-id", - "default::cli_e2e_tests::test_big_number", - "--args", - f"u64:{expected_u64}", - f"u128:{expected_128}", - f"u256:{expected_256}", # Important to test this big number - ], - ) - - response = json.loads(response.stdout) - if ( - response["Result"] == None - or response["Result"][0] != f"{expected_u64}" - or response["Result"][1] != f"{expected_128}" - or response["Result"][2] != f"{expected_256}" - ): - raise TestError( - f"View function [test_big_number] did not return correct result" - ) - - # Test view function with with vector arguments - # Follow 2 lines are for testing vector of u16-u256 - response = run_helper.run_command( - test_name, - [ - "aptos", - "move", - "view", - "--assume-yes", - "--function-id", - "default::cli_e2e_tests::test_vector", - "--args", - "string:1234", # Notice this is testing u8 vector instead of actual string - f"u16:[1,2]", - f"u32:[1,2]", - f"u64:[1,2]", - f"u128:[1,2]", - f"u256:[1,2]", - f'address:["0x123","0x456"]', - "bool:[true,false]", - 'string:["abc","efg"]', - ], - ) - - response = json.loads(response.stdout) - if response["Result"] == None or len(response["Result"]) != 9: - raise TestError(f"View function [test_vector] did not return correct result") diff --git a/crates/aptos/e2e/cases/node.py b/crates/aptos/e2e/cases/node.py deleted file mode 100644 index 5a7e40b3a91e3..0000000000000 --- a/crates/aptos/e2e/cases/node.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json - -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_node_show_validator_set(run_helper: RunHelper, test_name=None): - # run the show validator set command - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "show-validator-set", - "--profile", - "default", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("scheme") == None or result.get("active_validators") == None: - raise TestError( - "Node show validator set command failed: Did not return scheme and active_validators" - ) - - validator_0 = result.get("active_validators")[0] - validator_config = validator_0.get("config") - if ( - validator_0.get("account_address") == None - or validator_config.get("consensus_public_key") == None - or validator_config.get("validator_network_addresses") == None - or validator_config.get("fullnode_network_addresses") == None - ): - raise TestError( - "Node show validator set command failed: Did not return account_address, consensus_public_key, or config" - ) - - -@test_case -def test_node_update_consensus_key(run_helper: RunHelper, test_name=None): - # run init a new profile - run_helper.run_command( - test_name, - [ - "aptos", - "init", - "--assume-yes", - "--network", - "local", - "--profile", - "consensus", - ], - input="\n", - ) - - # run initialize stake to make sure stake pool created - run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "initialize-stake-owner", - "--profile", - "consensus", - "--initial-stake-amount", - "1", - "--assume-yes", - ], - ) - - # hardcode the bls12381 key - bls12381_public_key = "91067b73f284b4fa47141a023b4bbdc819e92a80058cbedfa2a39b43782c624eea1b281b0a8862aee23e77fb59a2ba75" - bls12381_pop = "857ddee36936599f98376ebc1a4d70a9ea9c066d27c03ff34300cc52ddea72760c3f8c8b8e423626cff585130d38370712945f48438ff24dcac0179dd4eb47389fd317395a36766490ce34c852b4b23fa8f0b55561275014ae56f79a86eff3ef" - - # run the update consensus key command - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "update-consensus-key", - "--profile", - "consensus", - "--consensus-public-key", - bls12381_public_key, - "--proof-of-possession", - bls12381_pop, - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") == None or result.get("success") != True: - raise TestError("update-consensus-key command failed: Did not return success") - - # Make sure consensus key is actually updated on chain - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--profile", - "consensus", - "--owner-address", - "consensus", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0] == None or result[0].get("consensus_public_key") == None: - raise TestError(f"Error: No consensus key on result") - - if result[0].get("consensus_public_key") != f"0x{bls12381_public_key}": - raise TestError( - f"Error: Expected consensus key 0x{bls12381_public_key} but got {result[0].get('consensus_public_key')}" - ) - - -@test_case -def test_node_update_validator_network_address(run_helper: RunHelper, test_name=None): - network_public_key = ( - "657dac6c023b39b53b8ae5fdc13a1e1222dacde98165324e16086a6766821628" - ) - network_host = "127.0.0.1" - network_port = "6180" - - # run the update command - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "update-validator-network-addresses", - "--profile", - "consensus", # Assume consensus profile is already created from test_node_update_consensus_key - "--validator-network-public-key", - network_public_key, - "--validator-host", - f"{network_host}:{network_port}", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") == None or result.get("success") != True: - raise TestError( - "update-validator-network-addresses command failed: Did not return success" - ) - - # Make sure network address is actually updated on chain - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--profile", - "consensus", - "--owner-address", - "consensus", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0] == None or result[0].get("validator_network_addresses") == None: - raise TestError(f"Error: No [validator_network_addresses] key found on result") - - expected_network_address = f"/ip4/{network_host}/tcp/{network_port}/noise-ik/0x{network_public_key}/handshake/0" - if result[0].get("validator_network_addresses")[0] != expected_network_address: - raise TestError( - f"Error: Expected validator network address [{expected_network_address}] but got [{result[0].get('validator_network_addresses')[0]}]" - ) diff --git a/crates/aptos/e2e/cases/stake.py b/crates/aptos/e2e/cases/stake.py deleted file mode 100644 index 2de1febab1088..0000000000000 --- a/crates/aptos/e2e/cases/stake.py +++ /dev/null @@ -1,418 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json - -from common import TestError -from test_helpers import RunHelper -from test_results import test_case - - -@test_case -def test_stake_initialize_stake_owner(run_helper: RunHelper, test_name=None): - # run the initialize-stake-owner command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "initialize-stake-owner", - "--initial-stake-amount", - "1000000", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") != True: - raise TestError("Did not initialize stake owner successfully") - - # make sure the the stake pool initialized on chain - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--profile", - "default", - "--owner-address", - "default", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0] == None or result[0].get("total_stake") != 1000000: - raise TestError("Did not initialize stake owner successfully") - - -@test_case -def test_stake_add_stake(run_helper: RunHelper, test_name=None): - # run the add-stake command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "add-stake", - "--amount", - "1000000", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not execute [add-stake] successfully") - - # verify that the stake was added - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--owner-address", - "default", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("total_stake") != 2000000: # initial 1M + added 1M - raise TestError( - f"Did not add stake successfully. Expected 2000000, got {result[0].get('total_stake')}" - ) - - -@test_case -def test_stake_withdraw_stake_before_unlock(run_helper: RunHelper, test_name=None): - # get the current stake amount - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--owner-address", - "default", - ], - ) - result = json.loads(response.stdout)["Result"] - current_stake = result[0].get("total_stake") - - # run the withdraw-stake command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "withdraw-stake", - "--amount", - "1000000", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not execute [withdraw-stake] successfully") - - # Get the current stake amount again - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--owner-address", - "default", - ], - ) - - # Total stake should not have change because the stake is still locked - result = json.loads(response.stdout)["Result"] - if result[0].get("total_stake") != current_stake: - raise TestError( - f"Total stake should not change before unlock. Expected {current_stake}, got {result[0].get('total_stake')}" - ) - - -@test_case -def test_stake_set_operator(run_helper: RunHelper, test_name=None): - # create a new operator account - run_helper.run_command( - test_name, - [ - "aptos", - "init", - "--profile", - "operator", - "--assume-yes", - "--network", - "local", - ], - input="\n", - ) - - # run the set-operator command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "set-operator", - "--operator-address", - "operator", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not set operator successfully") - - -@test_case -def test_stake_set_voter(run_helper: RunHelper, test_name=None): - # create a new voter account - run_helper.run_command( - test_name, - ["aptos", "init", "--profile", "voter", "--assume-yes", "--network", "local"], - input="\n", - ) - - # run the set-operator command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "set-delegated-voter", - "--voter-address", - "voter", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not set delegated-voter successfully") - - -@test_case -def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): - # run the set-operator command - # Note: This command has to run after set-operator and set-voter - # because it needs to know the operator and voter addresses - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "create-staking-contract", - "--operator", - "operator", - "--voter", - "voter", - "--amount", - "1000000", - "--commission-percentage", - "1", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") != True: - raise TestError("Did not set create staking contract successfully") - - -@test_case -def test_stake_create_staking_contract(run_helper: RunHelper, test_name=None): - # run the set-operator command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "create-staking-contract", - "--operator", - "operator", - "--voter", - "voter", - "--amount", - "1000000", - "--commission-percentage", - "1", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") != True: - raise TestError("Did not set create staking contract successfully") - - -@test_case -def test_stake_increase_lockup(run_helper: RunHelper, test_name=None): - # run the set-operator command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "increase-lockup", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not increase lockup successfully") - - -@test_case -def test_stake_unlock_stake(run_helper: RunHelper, test_name=None): - # run the unlock-stake command - response = run_helper.run_command( - test_name, - ["aptos", "stake", "unlock-stake", "--amount", "1000000", "--assume-yes"], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not unlock stake successfully") - - -@test_case -def test_stake_withdraw_stake_after_unlock(run_helper: RunHelper, test_name=None): - # get the current stake amount - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--owner-address", - "default", - ], - ) - result = json.loads(response.stdout)["Result"] - current_stake = result[0].get("total_stake") - - # run the unlock-stake command - amount_to_withdraw = 1000000 - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "unlock-stake", - "--amount", - f"{amount_to_withdraw}", - "--profile", - "default", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not unlock stake successfully") - - # run the withdraw-stake command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "withdraw-stake", - "--amount", - f"{amount_to_withdraw}", - "--profile", - "default", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("success") != True: - raise TestError("Did not execute [withdraw-stake] successfully") - - # verify that the stake was withdrawed - response = run_helper.run_command( - test_name, - [ - "aptos", - "node", - "get-stake-pool", - "--owner-address", - "default", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result[0].get("total_stake") != current_stake - amount_to_withdraw: - raise TestError( - f"The stake should be decreased by {amount_to_withdraw}. Expected {current_stake - amount_to_withdraw}, got {result[0].get('total_stake')}" - ) - - -@test_case -def test_stake_request_commission(run_helper: RunHelper, test_name=None): - # create a new account - run_helper.run_command( - test_name, - [ - "aptos", - "init", - "--profile", - "request_commission", - "--assume-yes", - "--network", - "local", - ], - input="\n", - ) - - # create staking contract - run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "create-staking-contract", - "--profile", - "request_commission", - "--operator", - "request_commission", - "--voter", - "request_commission", - "--amount", - "3", - "--commission-percentage", - "1", - "--assume-yes", - ], - ) - - # run the request-commission command - response = run_helper.run_command( - test_name, - [ - "aptos", - "stake", - "request-commission", - "--profile", - "request_commission", - "--owner-address", - "request_commission", - "--operator-address", - "request_commission", - "--assume-yes", - ], - ) - - result = json.loads(response.stdout)["Result"] - if result.get("success") != True: - raise TestError("Did not execute [request-commission] successfully") diff --git a/crates/aptos/e2e/common.py b/crates/aptos/e2e/common.py deleted file mode 100644 index d24369a15a895..0000000000000 --- a/crates/aptos/e2e/common.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import os -from dataclasses import dataclass -from enum import Enum - -NODE_PORT = 8080 -METRICS_PORT = 9101 -FAUCET_PORT = 8081 - - -class Network(Enum): - DEVNET = "devnet" - TESTNET = "testnet" - MAINNET = "mainnet" - - def __str__(self): - return self.value - - -# Information for some accounts we use for testing. -@dataclass -class AccountInfo: - private_key: str - public_key: str - account_address: str - - -# This is an account that use for testing, for example to create it with the init -# account, send funds to it, etc. This is not the account created by the `aptos init` -# test. To get details about that account use get_account_info on the RunHelper. -OTHER_ACCOUNT_ONE = AccountInfo( - private_key="0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221", - public_key="0x25caf00522e4d4664ec0a27166a69e8a32b5078959d0fc398da70d40d2893e8f", - account_address="0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82", -) - - -def build_image_name(image_repo_with_project: str, tag: str): - # If no repo is specified, leave it that way. Otherwise make sure we have a slash - # between the image repo and the image name. - image_repo_with_project = image_repo_with_project.rstrip("/") - if image_repo_with_project != "": - image_repo_with_project = f"{image_repo_with_project}/" - return f"{image_repo_with_project}tools:{tag}" - - -# Exception to use when a test fails, for the CLI did something unexpected, an -# expected output was missing, etc. This is just a convenience, the framework -# will still work if a different error is raised. -# -# For errors within the framework itself, use RuntimeError. -class TestError(Exception): - pass diff --git a/crates/aptos/e2e/linter.sh b/crates/aptos/e2e/linter.sh deleted file mode 100755 index 484eac198295c..0000000000000 --- a/crates/aptos/e2e/linter.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# This assumes you have already poetry installed. -# If not, please follow README.md instructions to install poetry and necessary dependencies. - -# Run the necesary linters and formatters for CLI e2e tests -echo "Start linter.sh..." - -# Run isort -echo "Running isort..." -poetry run isort . - -# Run black -echo "Running black..." -poetry run black . - -echo "linter.sh complete!" \ No newline at end of file diff --git a/crates/aptos/e2e/local_testnet.py b/crates/aptos/e2e/local_testnet.py deleted file mode 100644 index 941bef3da23fb..0000000000000 --- a/crates/aptos/e2e/local_testnet.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -# This file contains functions for running the local testnet. - -import logging -import subprocess -import time - -import requests -from common import FAUCET_PORT, METRICS_PORT, NODE_PORT, Network, build_image_name - -LOG = logging.getLogger(__name__) - -# Run a local testnet in a docker container. We choose to detach here and we'll -# stop running it later using the container name. -def run_node(network: Network, image_repo_with_project: str, pull=True): - image_name = build_image_name(image_repo_with_project, network) - container_name = f"aptos-tools-{network}" - LOG.info(f"Trying to run aptos CLI local testnet from image: {image_name}") - - # Confirm that the Docker daemon is running. - try: - subprocess.run( - ["docker", "container", "ls"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - check=True, - ) - except: - LOG.error("Failed to connect to Docker. Is it installed and running?") - raise - - # First delete the existing container if there is one with the same name. - subprocess.run( - ["docker", "rm", "-f", container_name], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - - # If debug logging is enabled show the output of the command to run the container. - kwargs = {"check": True} - if LOG.getEffectiveLevel() > 10: - kwargs = {**kwargs, **{"stdout": subprocess.PIPE, "stderr": subprocess.PIPE}} - - args = [ - "docker", - "run", - ] - - if pull: - args += ["--pull", "always"] - - args += [ - "--detach", - "--name", - container_name, - "-p", - f"{NODE_PORT}:{NODE_PORT}", - "-p", - f"{METRICS_PORT}:{METRICS_PORT}", - "-p", - f"{FAUCET_PORT}:{FAUCET_PORT}", - image_name, - "aptos", - "node", - "run-local-testnet", - "--with-faucet", - ] - - # Run the container. - subprocess.run( - args, - **kwargs, - ) - - LOG.info(f"Running aptos CLI local testnet from image: {image_name}") - return container_name - - -# Stop running the detached node. -def stop_node(container_name: str): - LOG.info(f"Stopping container: {container_name}") - subprocess.check_output(["docker", "stop", container_name]) - LOG.info(f"Stopped container: {container_name}") - - -# Query the node and faucet APIs until they start up or we timeout. -def wait_for_startup(container_name: str, timeout: int): - LOG.info(f"Waiting for node and faucet APIs for {container_name} to come up") - count = 0 - api_response = None - faucet_response = None - while True: - try: - api_response = requests.get(f"http://127.0.0.1:{NODE_PORT}/v1") - # Try to query the legacy faucet health endpoint first. TODO: Remove this - # once all local testnet images we use have the new faucet in them. - faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/health") - if faucet_response.status_code == 404: - # If that fails, try the new faucet health endpoint. - faucet_response = requests.get(f"http://127.0.0.1:{FAUCET_PORT}/") - if api_response.status_code != 200 or faucet_response.status_code != 200: - raise RuntimeError( - f"API or faucet not ready. API response: {api_response}. " - f"Faucet response: {faucet_response}" - ) - break - except Exception: - if count >= timeout: - LOG.error(f"Timeout while waiting for node / faucet to come up") - raise - count += 1 - time.sleep(1) - LOG.info(f"Node and faucet APIs for {container_name} came up") diff --git a/crates/aptos/e2e/main.py b/crates/aptos/e2e/main.py deleted file mode 100644 index 25668cdcda0ad..0000000000000 --- a/crates/aptos/e2e/main.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -""" -This script is how we orchestrate running a local testnet and then running CLI tests against it. There are two different CLIs used for this: - -1. Base: For running the local testnet. This is what the --base-network flag and all other flags starting with --base are for. -2. Test: The CLI that we're testing. This is what the --test-cli-tag / --test-cli-path and all other flags starting with --test are for. - -Example (testing CLI in image): - python3 main.py --base-network testnet --test-cli-tag mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 - -Example (testing locally built CLI binary): - python3 main.py --base-network devnet --test-cli-path ~/aptos-core/target/release/aptos - -This means, run the CLI test suite using a CLI built from mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3 against a local testnet built from the testnet branch of aptos-core. - -Example (using a different image repo): - See ~/.github/workflows/cli-e2e-tests.yaml - -When the test suite is complete, it will tell you which tests passed and which failed. To further debug a failed test, you can check the output in --working-directory, there will be files for each test containing the command run, stdout, stderr, and any exception. -""" - -import argparse -import logging -import os -import pathlib -import platform -import shutil -import sys - -from cases.account import ( - test_account_create_and_transfer, - test_account_list, - test_account_fund_with_faucet, - test_account_lookup_address, - test_account_resource_account, - test_account_rotate_key, -) -from cases.config import test_config_show_profiles -from cases.init import test_aptos_header_included, test_init, test_metrics_accessible -from cases.move import ( - test_move_compile, - test_move_compile_script, - test_move_publish, - test_move_run, - test_move_view, -) -from cases.node import ( - test_node_show_validator_set, - test_node_update_consensus_key, - test_node_update_validator_network_address, -) -from cases.stake import ( - test_stake_add_stake, - test_stake_create_staking_contract, - test_stake_increase_lockup, - test_stake_initialize_stake_owner, - test_stake_request_commission, - test_stake_set_operator, - test_stake_set_voter, - test_stake_unlock_stake, - test_stake_withdraw_stake_after_unlock, - test_stake_withdraw_stake_before_unlock, -) -from common import Network -from local_testnet import run_node, stop_node, wait_for_startup -from test_helpers import RunHelper -from test_results import test_results - -logging.basicConfig( - stream=sys.stderr, - format="%(asctime)s - %(levelname)s - %(message)s", - level=logging.INFO, -) - -LOG = logging.getLogger(__name__) - - -def parse_args(): - # You'll notice there are two argument "prefixes", base and test. These refer to - # cases 1 and 2 in the top-level comment. - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description=__doc__, - ) - parser.add_argument("-d", "--debug", action="store_true") - parser.add_argument( - "--image-repo-with-project", - default="aptoslabs", - help=( - "What docker image repo (+ project) to use for the local testnet. " - "By default we use Docker Hub: %(default)s (so, just aptoslabs for the " - "project since Docker Hub is the implied default repo). If you want to " - "specify a different repo, it might look like this: " - "docker.pkg.github.com/aptoslabs/aptos-core" - ), - ) - parser.add_argument( - "--base-network", - required=True, - type=Network, - choices=list(Network), - help="What branch the Aptos CLI used for the local testnet should be built from", - ) - parser.add_argument( - "--base-startup-timeout", - type=int, - default=30, - help="Timeout in seconds for waiting for node and faucet to start up", - ) - test_cli_args = parser.add_mutually_exclusive_group(required=True) - test_cli_args.add_argument( - "--test-cli-tag", - help="The image tag for the CLI we want to test, e.g. mainnet_0431e2251d0b42920d89a52c63439f7b9eda6ac3", - ) - test_cli_args.add_argument( - "--test-cli-path", - help="Path to CLI binary we want to test, e.g. /home/dport/aptos-core/target/release/aptos", - ) - parser.add_argument( - "--working-directory", - default="/tmp/aptos-cli-tests", - help="Where we'll run CLI commands from (in the host system). Default: %(default)s", - ) - parser.add_argument( - "--no-pull-always", - action="store_true", - help='If set, do not set "--pull always" when running the local testnet. Necessary for using local images.', - ) - args = parser.parse_args() - return args - - -def run_tests(run_helper): - # Make sure the metrics port is accessible. - test_metrics_accessible(run_helper) - - # Run init tests. We run these first to set up the CLI. - test_init(run_helper) - - # Run config tests. - test_config_show_profiles(run_helper) - - # Run account tests. - test_account_fund_with_faucet(run_helper) - test_account_create_and_transfer(run_helper) - test_account_list(run_helper) - test_account_lookup_address(run_helper) - test_account_resource_account(run_helper) - - # Make sure the aptos-cli header is included on the original request - test_aptos_header_included(run_helper) - - # Run move subcommand group tests. - test_move_compile(run_helper) - test_move_compile_script(run_helper) - test_move_publish(run_helper) - test_move_run(run_helper) - test_move_view(run_helper) - - # Run stake subcommand group tests. - test_stake_initialize_stake_owner(run_helper) - test_stake_add_stake(run_helper) - test_stake_withdraw_stake_before_unlock(run_helper) - test_stake_unlock_stake(run_helper) - test_stake_withdraw_stake_after_unlock(run_helper) - test_stake_increase_lockup(run_helper) - test_stake_set_operator(run_helper) - test_stake_set_voter(run_helper) - test_stake_create_staking_contract(run_helper) - test_stake_request_commission(run_helper) - - # Run node subcommand group tests. - test_node_show_validator_set(run_helper) - test_node_update_consensus_key(run_helper) - test_node_update_validator_network_address(run_helper) - - # WARNING: This has to stay at the end, else key will get rotated - test_account_rotate_key(run_helper) - - -def main(): - args = parse_args() - - if args.debug: - logging.getLogger().setLevel(logging.DEBUG) - LOG.debug("Debug logging enabled") - else: - logging.getLogger().setLevel(logging.INFO) - - # Create the dir the test CLI will run from. - shutil.rmtree(args.working_directory, ignore_errors=True) - pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True) - - # If we're on Mac and DOCKER_DEFAULT_PLATFORM is not already set, set it to - # linux/amd64 since we only publish images for that platform. - if platform.system().lower() == "darwin" and platform.processor().lower().startswith("arm"): - if not os.environ.get("DOCKER_DEFAULT_PLATFORM"): - os.environ["DOCKER_DEFAULT_PLATFORM"] = "linux/amd64" - LOG.info( - "Detected ARM Mac and DOCKER_DEFAULT_PLATFORM was not set, setting it " - "to linux/amd64" - ) - - # Run a node + faucet and wait for them to start up. - container_name = run_node( - args.base_network, args.image_repo_with_project, not args.no_pull_always - ) - - # We run these in a try finally so that if something goes wrong, such as the - # local testnet not starting up correctly or some unexpected error in the - # test framework, we still stop the node + faucet. - try: - wait_for_startup(container_name, args.base_startup_timeout) - - # Build the RunHelper object. - run_helper = RunHelper( - host_working_directory=args.working_directory, - image_repo_with_project=args.image_repo_with_project, - image_tag=args.test_cli_tag, - cli_path=args.test_cli_path, - base_network=args.base_network, - ) - - # Prepare the run helper. This ensures in advance that everything needed is there. - run_helper.prepare() - - # Run tests. - run_tests(run_helper) - finally: - # Stop the node + faucet. - stop_node(container_name) - - # Print out the results. - if test_results.passed: - LOG.info("These tests passed:") - for test_name in test_results.passed: - LOG.info(test_name) - - if test_results.failed: - LOG.error("These tests failed:") - for test_name, exception in test_results.failed: - LOG.error(f"{test_name}: {exception}") - LOG.info("---") - LOG.info( - f"Debug these tests by checking the command, stdout, stderr, and any " - f"exception information if relevant in {args.working_directory}/out" - ) - return False - - LOG.info("All tests passed!") - - return True - - -if __name__ == "__main__": - if main(): - sys.exit(0) - else: - sys.exit(1) diff --git a/crates/aptos/e2e/poetry.lock b/crates/aptos/e2e/poetry.lock deleted file mode 100644 index bd8def62ff7ce..0000000000000 --- a/crates/aptos/e2e/poetry.lock +++ /dev/null @@ -1,685 +0,0 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. - -[[package]] -name = "anyio" -version = "3.7.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, -] - -[package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "aptos-sdk" -version = "0.5.1" -description = "" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aptos_sdk-0.5.1.tar.gz", hash = "sha256:3711ad2bf1120fff463cd5f494162c4658f03dd6bfbf1f523ee9aea01a4cb0f0"}, -] - -[package.dependencies] -httpx = ">=0.23.0,<0.24.0" -mypy = ">=0.982,<0.983" -PyNaCl = ">=1.5.0,<2.0.0" - -[[package]] -name = "black" -version = "22.12.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.7" -files = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2023.11.17" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "httpcore" -version = "0.16.3" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.7" -files = [ - {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"}, - {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"}, -] - -[package.dependencies] -anyio = ">=3.0,<5.0" -certifi = "*" -h11 = ">=0.13,<0.15" -sniffio = "==1.*" - -[package.extras] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "httpx" -version = "0.23.3" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.7" -files = [ - {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"}, - {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"}, -] - -[package.dependencies] -certifi = "*" -httpcore = ">=0.15.0,<0.17.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "idna" -version = "3.6" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.7.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "isort" -version = "5.11.5" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "mypy" -version = "0.982" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, - {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, - {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, - {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, - {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, - {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, - {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, - {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, - {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, - {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, - {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, - {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, - {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, - {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, - {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, - {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, - {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, - {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, - {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, - {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, -] - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - -[[package]] -name = "platformdirs" -version = "4.0.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pynacl" -version = "1.5.0" -description = "Python binding to the Networking and Cryptography (NaCl) library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, - {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, - {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, - {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, - {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, -] - -[package.dependencies] -cffi = ">=1.4.1" - -[package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -optional = false -python-versions = "*" -files = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "urllib3" -version = "2.0.7" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.7 <=4" -content-hash = "a521ce7dc5692587334cf5116fa19b834a12ab556c1ab32cd8f6793dd5d5568c" diff --git a/crates/aptos/e2e/pyproject.toml b/crates/aptos/e2e/pyproject.toml deleted file mode 100644 index 23b3274f6d7f0..0000000000000 --- a/crates/aptos/e2e/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[tool.poetry] -name = "aptos-cli-e2e-tests" -version = "0.1.0" -description = "Aptos CLI E2E tests" -authors = ["Aptos Labs "] -license = "Apache-2.0" - -[tool.poetry.dependencies] -python = ">=3.7 <=4" -aptos-sdk = "^0.5.1" -requests = "^2.31.0" - -[tool.poetry.dev-dependencies] -black = "^22.6.0" -isort = "^5.10.1" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/crates/aptos/e2e/test_helpers.py b/crates/aptos/e2e/test_helpers.py deleted file mode 100644 index dbca11238b8f0..0000000000000 --- a/crates/aptos/e2e/test_helpers.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import json -import logging -import os -import pathlib -import shutil -import subprocess -import traceback -from dataclasses import dataclass - -from aptos_sdk.client import RestClient -from common import METRICS_PORT, NODE_PORT, AccountInfo, Network, build_image_name - -LOG = logging.getLogger(__name__) - -WORKING_DIR_IN_CONTAINER = "/tmp" - - -# We pass this class into all test functions to help with calling the CLI, -# collecting output, and accessing common info. -@dataclass -class RunHelper: - host_working_directory: str - image_repo_with_project: str - image_tag: str - cli_path: str - base_network: Network - - test_count: int - - # This can be used by the tests to query the local testnet node. - api_client: RestClient - - def __init__( - self, - host_working_directory, - image_repo_with_project, - image_tag, - cli_path, - base_network, - ): - if image_tag and cli_path: - raise RuntimeError("Cannot specify both image_tag and cli_path") - if not (image_tag or cli_path): - raise RuntimeError("Must specify one of image_tag and cli_path") - self.host_working_directory = host_working_directory - self.image_repo_with_project = image_repo_with_project - self.image_tag = image_tag - self.base_network = base_network - self.cli_path = os.path.abspath(cli_path) if cli_path else cli_path - self.base_network = base_network - self.test_count = 0 - self.api_client = RestClient(f"http://127.0.0.1:{NODE_PORT}/v1") - - def build_image_name(self): - return build_image_name(self.image_repo_with_project, self.image_tag) - - # This function lets you pass call the CLI like you would normally, but really it is - # calling the CLI in a docker container and mounting the host working directory such - # that the container will write it results out to that directory. That way the CLI - # state / configuration is preserved between test cases. - def run_command(self, test_name, command, *args, **kwargs): - file_name = f"{self.test_count:03}_{test_name}" - self.test_count += 1 - - # If we're in a CI environment it is necessary to set the --user, otherwise it - # is not possible to interact with the files in the bindmount. For more details - # see here: https://github.com/community/community/discussions/44243. - if os.environ.get("CI"): - user_args = ["--user", f"{os.getuid()}:{os.getgid()}"] - else: - user_args = [] - - # Build command. - if self.image_tag: - full_command = ( - [ - "docker", - "run", - ] - + user_args - + [ - "-e", - # This is necessary to force the CLI to place the `.move` directory - # inside the bindmount dir, which is the only writeable directory - # inside the container when in CI. It's fine to do it outside of CI - # as well. - f"HOME={WORKING_DIR_IN_CONTAINER}", - "--rm", - "--network", - "host", - "-i", - "-v", - f"{self.host_working_directory}:{WORKING_DIR_IN_CONTAINER}", - "--workdir", - WORKING_DIR_IN_CONTAINER, - self.build_image_name(), - ] - + command - ) - else: - full_command = [self.cli_path] + command[1:] - LOG.debug(f"Running command: {full_command}") - - # Create the output directory if necessary. - out_path = os.path.join(self.host_working_directory, "out") - pathlib.Path(out_path).mkdir(exist_ok=True) - - # Write the command we're going to run to file. - with open(os.path.join(out_path, f"{file_name}.command"), "w") as f: - f.write(" ".join(command)) - - # Run command. - try: - # If we're using a local CLI, set the working directory for subprocess.run. - if self.cli_path: - kwargs["cwd"] = self.host_working_directory - result = subprocess.run( - full_command, - *args, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - **kwargs, - ) - LOG.debug(f"Subcommand succeeded: {test_name}") - - write_subprocess_out(out_path, file_name, result) - - return result - except subprocess.CalledProcessError as e: - LOG.warn(f"Subcommand failed: {test_name}") - - # Write the exception to file. - with open(os.path.join(out_path, f"{file_name}.exception"), "w") as f: - f.write( - "".join( - traceback.format_exception( - etype=type(e), value=e, tb=e.__traceback__ - ) - ) - ) - - # Fortunately the result and exception of subprocess.run both have the - # stdout and stderr attributes on them. - write_subprocess_out(out_path, file_name, e) - - raise - - # Top level function to run any preparation. - def prepare(self): - self.prepare_move() - self.prepare_cli() - - # Move any Move files into the working directory. - def prepare_move(self): - shutil.copytree( - "../../../aptos-move/move-examples/cli-e2e-tests", - os.path.join(self.host_working_directory, "move/cli-e2e-tests"), - ignore=shutil.ignore_patterns("build"), - ) - - # If image_Tag is set, pull the test CLI image. We don't technically have to do - # this separately but it makes the steps clearer. Otherwise, cli_path must be - # set, in which case we ensure the file is there. - def prepare_cli(self): - if self.image_tag: - image_name = self.build_image_name() - LOG.info(f"Pre-pulling image for CLI we're testing: {image_name}") - command = ["docker", "pull", image_name] - LOG.debug(f"Running command: {command}") - output = subprocess.check_output(command) - LOG.debug(f"Output: {output}") - else: - if not os.path.isfile(self.cli_path): - raise RuntimeError(f"CLI not found at path: {self.cli_path}") - - # If we're testing a CLI in the host system, i.e. from the --test-cli-path flag, - # make sure we're using "workspace" configuration and not "global" configuration. - response = self.run_command( - "check_workspace_config", - ["aptos", "config", "show-global-config"], - ) - response = json.loads(response.stdout) - if response["Result"]["config_type"].lower() != "workspace": - raise RuntimeError( - "When using --test-cli-path you must use workspace configuration, " - "try running `aptos config set-global-config --config-type workspace`" - ) - - # Get the account info of the account created by test_init. - def get_account_info(self): - path = os.path.join(self.host_working_directory, ".aptos", "config.yaml") - with open(path) as f: - content = f.read().splitlines() - # To avoid using external deps we parse the file manually. - private_key = None - public_key = None - account_address = None - for line in content: - if "private_key: " in line: - private_key = line.split("private_key: ")[1].replace('"', "") - if "public_key: " in line: - public_key = line.split("public_key: ")[1].replace('"', "") - if "account: " in line: - account_address = line.split("account: ")[1].replace('"', "") - if not private_key or not public_key or not account_address: - raise RuntimeError(f"Failed to parse {path} to get account info") - return AccountInfo( - private_key=private_key, - public_key=public_key, - account_address=account_address, - ) - - def get_metrics_url(self, json=False): - path = "metrics" if not json else "json_metrics" - return f"http://127.0.0.1:{METRICS_PORT}/{path}" - - -# This function helps with writing the stdout / stderr of a subprocess to files. -def write_subprocess_out(out_path, file_name, command_output): - LOG.debug(f"Stdout: {command_output.stdout}") - LOG.debug(f"Stderr: {command_output.stderr}") - - # Write stdout and stderr to file. - with open(os.path.join(out_path, f"{file_name}.stdout"), "w") as f: - f.write(command_output.stdout) - with open(os.path.join(out_path, f"{file_name}.stderr"), "w") as f: - f.write(command_output.stderr) diff --git a/crates/aptos/e2e/test_results.py b/crates/aptos/e2e/test_results.py deleted file mode 100644 index c206d5a60ad40..0000000000000 --- a/crates/aptos/e2e/test_results.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright © Aptos Foundation -# SPDX-License-Identifier: Apache-2.0 - -import logging -import typing -from dataclasses import dataclass, field -from functools import wraps - -LOG = logging.getLogger(__name__) - - -# This class holds info about passed / failed tests. -@dataclass(init=True) -class TestResults: - passed: typing.List[str] = field(default_factory=list) - failed: typing.List[typing.Tuple[str, Exception]] = field(default_factory=list) - - -# This is a decorator that you put above every test case. It handles capturing test -# success / failure so it can be reported at the end of the test suite. It also handles -# passing in test_name based on the name of the function so the caller doesn't have to. -def build_test_case_decorator(test_results: TestResults): - def test_case_inner(f): - @wraps(f) - def wrapper(*args, **kwds): - LOG.info(f"Running test: {f.__name__}") - try: - result = f(*args, test_name=f.__name__, **kwds) - test_results.passed.append(f.__name__) - return result - except Exception as e: - test_results.failed.append((f.__name__, e)) - return None - - return wrapper - - return test_case_inner - - -# We now define one TestResults that we'll use for every test case. This is a bit of a -# hack but it is the only way to then be able to provide a decorator that works out of -# the box. The alternative was to use a context manager and wrap every function call in -# it, but not only is that more verbose, but you'd have to provide the name of each test -# case manually to the context manager, whereas with this approach the name can be -# inferred from the function being decorated directly. -test_results = TestResults() - -# Then we define an instance of the decorator that uses that TestResults instance. -test_case = build_test_case_decorator(test_results) diff --git a/crates/aptos/homebrew/README.md b/crates/aptos/homebrew/README.md deleted file mode 100644 index 391ab0acac85e..0000000000000 --- a/crates/aptos/homebrew/README.md +++ /dev/null @@ -1,228 +0,0 @@ -# Homebrew Aptos - -Homebrew is a package manager that works for MacOS Silicon and Intel chips as well as Linux distributions like Debian -and Ubuntu. - -The [Aptos command line interface (CLI)](https://aptos.dev/tools/aptos-cli/install-cli/) may be installed -via [Homebrew](https://brew.sh/) for simplicity. This is an in-depth overview of Homebrew and the Aptos formula. In this -guide, we go over each section of the Homebrew formula and steps to implement changes in the future. - -## Quick guide - -- [Formula in Homebrew GitHub](https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb) -- [Aptos 1.0.3 New Formula PR for GitHub](https://github.com/Homebrew/homebrew-core/pull/119832) -- [Aptos Formula Fix PR to use build_cli_release.sh](https://github.com/Homebrew/homebrew-core/pull/120051) - -## Getting started - -To begin, first ensure that homebrew is correctly installed on your computer. Visit [brew.sh](https://brew.sh/) to learn -how you can set it up! - -To test that it works correctly, try - -```bash -brew help -``` - -Once homebrew is installed, run - -```bash -brew install aptos -``` - -to test that it installed correctly, try - -```bash -aptos --help - -# This should return something like - -# aptos 1.0.5 -# Aptos Labs -# Command Line Interface (CLI) for developing and interacting with the Aptos blockchain -# ... -``` - -## Change guide - -Note: This guide is for developers who are trying to update the Aptos homebrew formula. - -You can get the latest formula here: https://github.com/Homebrew/homebrew-core/blob/master/Formula/aptos.rb - -Copy the `aptos.rb` file to your `homebrew` `formula` directory. For example, on macOS with an M1, this will likely be: - -```bash -/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula -``` - -### Development - -After you've copied `aptos.rb` to your local `homebrew` `formula` directory, you can modify it and use the commands -below for testing. - -```bash -# On Mac M1, homebrew formulas are located locally at -/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula - -# Before submitting changes run -brew audit --new-formula aptos # For new formula -brew audit aptos --strict --online -brew install aptos -brew test aptos - -# For debugging issues during the installation process you can do -brew install aptos --interactive # Interactive, gives you access to the shell -brew install aptos -d # Debug mode - -# Livecheck -brew livecheck --debug aptos -``` - -### Committing changes - -Once you have audited and tested your brew formula using the commands above, make sure you: - -1. Commit your changes to `aptos-core` in `crates/aptos/homebrew`. -2. Fork the Homebrew Core repository - per [How to Open a Homebrew Pull Request](https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request#formulae-related-pull-request). -3. Create a PR on the [Homebrew Core](https://github.com/Homebrew/homebrew-core/pulls) repo with your changes. - -## Aptos.rb structure overview - -### Header - -```ruby -class Aptos < Formula - desc "Layer 1 blockchain built to support fair access to decentralized assets for all" - homepage "https://aptoslabs.com/" - url "https://github.com/aptos-labs/aptos-core/archive/refs/tags/aptos-cli-v1.0.3.tar.gz" - sha256 "670bb6cb841cb8a65294878af9a4f03d4cba2a598ab4550061fed3a4b1fe4e98" - license "Apache-2.0" - ... -``` - -### Bottles - -[Bottles](https://docs.brew.sh/Bottles#pour-bottle-pour_bottle) are precompiled binaries. This way people don't need to -compile from source every time. - -> Bottles for homebrew/core formulae are created by [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) when a pull -> request is submitted. If the formula builds successfully on each supported platform and a maintainer approves the -> change, [Brew Test Bot](https://docs.brew.sh/Brew-Test-Bot) updates its bottle do block and uploads each bottle to -> GitHub Packages. - -```ruby - ... - # IMPORTANT: These are automatically generated, you DO NOT need to add these manually, I'm adding them here as an example - bottle do - sha256 cellar: :any_skip_relocation, arm64_ventura: "40434b61e99cf9114a3715851d01c09edaa94b814f89864d57a18d00a8e0c4e9" - sha256 cellar: :any_skip_relocation, arm64_monterey: "edd6dcf9d627746a910d324422085eb4b06cdab654789a03b37133cd4868633c" - sha256 cellar: :any_skip_relocation, arm64_big_sur: "d9568107514168afc41e73bd3fd0fc45a6a9891a289857831f8ee027fb339676" - sha256 cellar: :any_skip_relocation, ventura: "d7289b5efca029aaa95328319ccf1d8a4813c7828f366314e569993eeeaf0003" - sha256 cellar: :any_skip_relocation, monterey: "ba58e1eb3398c725207ce9d6251d29b549cde32644c3d622cd286b86c7896576" - sha256 cellar: :any_skip_relocation, big_sur: "3e2431a6316b8f0ffa4db75758fcdd9dea162fdfb3dbff56f5e405bcbea4fedc" - sha256 cellar: :any_skip_relocation, x86_64_linux: "925113b4967ed9d3da78cd12745b1282198694a7f8c11d75b8c41451f8eff4b5" - end - ... -``` - -### Livecheck - -[Brew livecheck](https://docs.brew.sh/Brew-Livecheck) uses strategies to find the newest version of a formula or cask’s -software by checking upstream. The strategy used below checks for all `aptos-cli-v` tags for `aptos-core`. The -regex ensures that releases for other, non-CLI builds are not factored into livecheck. - -Livecheck is run on a schedule with BrewTestBot and will update the bottles automatically on a schedule to ensure -they're up to date. For more info on how BrewTestBot and brew livecheck works, please see -the [How does BrewTestBot work and when does it update formulae?](https://github.com/Homebrew/discussions/discussions/3083) -discussion. - -```ruby -... - # This livecheck scans the releases folder and looks for all releases - # with matching regex of href="/tag/aptos-cli-v". This - # is done to automatically check for new release versions of the CLI. - livecheck do - url :stable - regex(/^aptos-cli[._-]v?(\d+(?:\.\d+)+)$/i) - end -... -``` - -To run livecheck for testing, we recommend including the `--debug` argument: - -```bash -brew livecheck --debug aptos -``` - -### Depends on and installation - -- `depends_on` is for specifying - other [homebrew formulas as dependencies](https://docs.brew.sh/Formula-Cookbook#specifying-other-formulae-as-dependencies). -- Currently, we use v1.64 of Rust, as specified in the `Cargo.toml` file of the project. If we were to use the latest - stable build of Rust - going forward, we would modify the formula slightly. See the comments below for more details. - -```ruby - # Installs listed homebrew dependencies before Aptos installation - # Dependencies needed: https://aptos.dev/cli-tools/build-aptos-cli - # See scripts/dev_setup.sh in aptos-core for more info - depends_on "cmake" => :build - depends_on "rustup-init" => :build - uses_from_macos "llvm" => :build - - on_linux do - depends_on "pkg-config" => :build - depends_on "zip" => :build - depends_on "openssl@3" - depends_on "systemd" - end - - # Currently must compile with the same rustc version specified in the - # root Cargo.toml file of aptos-core (currently it is pegged to Rust - # v1.64). In the future if it becomes compatible with the latest Rust - # toolchain, we can remove the use of rustup-init, replacing it with a - # depends_on "rust" => :build - # above and build the binary without rustup as a dependency - # - # Uses build_cli_release.sh for creating the compiled binaries. - # This drastically reduces their size (ie. 2.2 GB on Linux for release - # build becomes 40 MB when run with opt-level = "z", strip, lto, etc). - # See cargo.toml [profile.cli] section for more details - def install - system "#{Formula["rustup-init"].bin}/rustup-init", - "-qy", "--no-modify-path", "--default-toolchain", "1.64" - ENV.prepend_path "PATH", HOMEBREW_CACHE/"cargo_cache/bin" - system "./scripts/cli/build_cli_release.sh", "homebrew" - bin.install "target/cli/aptos" - end -``` - -### Tests - -To conduct tests, run: - -```bash -brew test aptos -``` - -The current test generates a new key via the Aptos CLI and ensures the shell output matches the filename(s) for that -key. - -```ruby - ... - test do - assert_match(/output.pub/i, shell_output("#{bin}/aptos key generate --output-file output")) - end - ... -``` - -## Supporting resources - -- To view other Homebrew-related FAQs or ask questions yourself, visit - the [discussions board](https://github.com/orgs/Homebrew/discussions). -- For similar Rust-related build examples, we recommend: - - [`rustfmt.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/rustfmt.rb) -- Finally, note these key Homebew guides: - - [Homebrew Formula Cookbook](https://docs.brew.sh/Formula-Cookbook) - - [Creating and Running Your Own Homebrew Tap - Rust Runbook](https://publishing-project.rivendellweb.net/creating-and-running-your-own-homebrew-tap/) diff --git a/crates/aptos/scripts/check_dynamic_deps.sh b/crates/aptos/scripts/check_dynamic_deps.sh deleted file mode 100755 index f5dc6683b79b2..0000000000000 --- a/crates/aptos/scripts/check_dynamic_deps.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -# This script checks if the CLI depends on external deps that it shouldn't. We run this -# in CI to make sure we don't accidentally reintroduce deps that would make the CLI -# unusable on most systems. -# -# While it would be more reliable to actually build the CLI and check what libraries it -# links to, e.g. with otool, it is much cheaper to use cargo tree. As far as I can tell -# the entire Rust ecosystem makes use of these `x-sys` libraries to depend on external -# dynamically linked libraries. -# -# We can almost use cargo deny but it doesn't support checking specific build paths. We -# don't care if openssl-sys for example is used at build time (which it is, indirectly -# by shadow-rs), only at run time. See more here: -# https://github.com/EmbarkStudios/cargo-deny/issues/563 -# -# It assumes cargo and friends are available. -# -# Run this from the root of the repo. - -declare -a deps=("pq-sys" "openssl-sys") - -for dep in "${deps[@]}"; do - echo "Checking for banned dependency $dep..." - - # Check for deps. As you can see, we only check for MacOS right now. - out=`cargo tree -e features,no-build,no-dev --target aarch64-apple-darwin -p aptos -i "$dep"` - - # If the exit status was non-zero, great, the dep couldn't be found. - if [ $? -ne 0 ]; then - continue - fi - - # If the exit status was zero we have to check the output to see if the dep is in - # use. If it is in the output, it is in use. - if [[ $out != *"$dep"* ]]; then - continue - fi - - echo "Banned dependency $dep found!" - exit 1 -done - -echo -echo "None of the banned dependencies are in use, great!" -exit 0 diff --git a/crates/aptos/src/account/create.rs b/crates/aptos/src/account/create.rs deleted file mode 100644 index cbabbf1c6045a..0000000000000 --- a/crates/aptos/src/account/create.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}; -use aptos_cached_packages::aptos_stdlib; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; - -// 1 APT -pub const DEFAULT_FUNDED_COINS: u64 = 100_000_000; - -/// Create a new account on-chain -/// -/// An account can be created by transferring coins, or by making an explicit -/// call to create an account. This will create an account with no coins, and -/// any coins will have to transferred afterwards. -#[derive(Debug, Parser)] -pub struct CreateAccount { - /// Address of the new account - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateAccount { - fn command_name(&self) -> &'static str { - "CreateAccount" - } - - async fn execute(self) -> CliTypedResult { - let address = self.account; - self.txn_options - .submit_transaction(aptos_stdlib::aptos_account_create_account(address)) - .await - .map(TransactionSummary::from) - } -} diff --git a/crates/aptos/src/account/create_resource_account.rs b/crates/aptos/src/account/create_resource_account.rs deleted file mode 100644 index 54af8e2bbae83..0000000000000 --- a/crates/aptos/src/account/create_resource_account.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::derive_resource_account::ResourceAccountSeed, - common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary}, -}; -use aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account; -use aptos_rest_client::{ - aptos_api_types::{WriteResource, WriteSetChange}, - Transaction, -}; -use aptos_types::{account_address::AccountAddress, transaction::authenticator::AuthenticationKey}; -use async_trait::async_trait; -use clap::Parser; -use serde::Serialize; -use std::str::FromStr; - -/// Create a resource account on-chain -/// -/// This will create a resource account which can be used as an autonomous account -/// not controlled directly by one account. -#[derive(Debug, Parser)] -pub struct CreateResourceAccount { - /// Optional Resource Account authentication key. - #[clap(long, value_parser = AuthenticationKey::from_str)] - pub(crate) authentication_key: Option, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -/// A shortened create resource account output -#[derive(Clone, Debug, Serialize)] -pub struct CreateResourceAccountSummary { - pub resource_account: Option, - #[serde(flatten)] - pub transaction_summary: TransactionSummary, -} - -impl From for CreateResourceAccountSummary { - fn from(transaction: Transaction) -> Self { - let transaction_summary = TransactionSummary::from(&transaction); - - let mut summary = CreateResourceAccountSummary { - transaction_summary, - resource_account: None, - }; - - if let Transaction::UserTransaction(txn) = transaction { - summary.resource_account = txn.info.changes.iter().find_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if data.typ.name.as_str() == "Account" - && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() - { - Some(*address.inner()) - } else { - None - } - }, - _ => None, - }); - } - - summary - } -} - -#[async_trait] -impl CliCommand for CreateResourceAccount { - fn command_name(&self) -> &'static str { - "CreateResourceAccount" - } - - async fn execute(self) -> CliTypedResult { - let authentication_key: Vec = if let Some(key) = self.authentication_key { - bcs::to_bytes(&key)? - } else { - vec![] - }; - self.txn_options - .submit_transaction(resource_account_create_resource_account( - self.seed_args.seed()?, - authentication_key, - )) - .await - .map(CreateResourceAccountSummary::from) - } -} diff --git a/crates/aptos/src/account/derive_resource_account.rs b/crates/aptos/src/account/derive_resource_account.rs deleted file mode 100644 index e2e3815668c7b..0000000000000 --- a/crates/aptos/src/account/derive_resource_account.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliError, CliTypedResult}; -use aptos_sdk::rest_client::aptos_api_types::HexEncodedBytes; -use aptos_types::account_address::{create_resource_address, AccountAddress}; -use async_trait::async_trait; -use clap::Parser; -use std::{fmt::Formatter, str::FromStr}; - -/// Encoding for the Resource account seed -#[derive(Debug, Default, Clone, Copy)] -pub enum SeedEncoding { - #[default] - Bcs, - Hex, - Utf8, -} - -const BCS: &str = "bcs"; -const UTF_8: &str = "utf8"; -const HEX: &str = "hex"; - -impl std::fmt::Display for SeedEncoding { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - SeedEncoding::Bcs => BCS, - SeedEncoding::Hex => HEX, - SeedEncoding::Utf8 => UTF_8, - }) - } -} - -impl FromStr for SeedEncoding { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - BCS => Ok(Self::Bcs), - HEX => Ok(Self::Hex), - UTF_8 | "utf-8" | "utf_8" => Ok(Self::Utf8), - _ => Err(CliError::UnableToParse( - "seed-encoding", - "For --seed-encoding please provide one of ['bcs','hex', 'utf8']".to_string(), - )), - } - } -} - -/// A generic interface for allowing for different types of seed phrase inputs -/// -/// The easiest to use is `string_seed` as it will match directly with the b"string" notation in Move. -#[derive(Debug, Parser)] -pub struct ResourceAccountSeed { - /// Resource account seed - /// - /// Seed used in generation of the AccountId of the resource account - /// The seed will be converted to bytes using the encoding from `--seed-encoding`, defaults to `BCS` - #[clap(long)] - pub(crate) seed: String, - - /// Resource account seed encoding - /// - /// The encoding can be one of `Bcs`, `Utf8`, and `Hex`. - /// - /// - Bcs is the legacy functionality of the CLI, it will BCS encode the string, but can be confusing for users e.g. `"ab" -> vector[0x2, 0x61, 0x62]` - /// - Utf8 will encode the string as raw UTF-8 bytes, similar to in Move `b"string"` e.g. `"ab" -> vector[0x61, 0x62]` - /// - Hex will encode the string as raw hex encoded bytes e.g. `"0x6162" -> vector[0x61, 0x62]` - #[clap(long, default_value_t = SeedEncoding::Bcs)] - pub(crate) seed_encoding: SeedEncoding, -} - -impl ResourceAccountSeed { - pub fn seed(self) -> CliTypedResult> { - match self.seed_encoding { - SeedEncoding::Bcs => Ok(bcs::to_bytes(self.seed.as_str())?), - SeedEncoding::Utf8 => Ok(self.seed.as_bytes().to_vec()), - SeedEncoding::Hex => HexEncodedBytes::from_str(self.seed.as_str()) - .map(|inner| inner.0) - .map_err(|err| CliError::UnableToParse("seed", err.to_string())), - } - } -} - -/// Derive the address for a resource account -/// -/// This will not create a resource account, but instead give the deterministic address given -/// a source address and seed. -#[derive(Debug, Parser)] -pub struct DeriveResourceAccount { - /// Address of the creator's account - #[clap(long, alias = "account", value_parser = crate::common::types::load_account_arg)] - pub(crate) address: AccountAddress, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, -} - -#[async_trait] -impl CliCommand for DeriveResourceAccount { - fn command_name(&self) -> &'static str { - "DeriveResourceAccountAddress" - } - - async fn execute(self) -> CliTypedResult { - let seed = self.seed_args.seed()?; - Ok(create_resource_address(self.address, &seed)) - } -} diff --git a/crates/aptos/src/account/fund.rs b/crates/aptos/src/account/fund.rs deleted file mode 100644 index 5bc6be9df1105..0000000000000 --- a/crates/aptos/src/account/fund.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::create::DEFAULT_FUNDED_COINS, - common::types::{CliCommand, CliTypedResult, FaucetOptions, ProfileOptions, RestOptions}, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; - -/// Fund an account with tokens from a faucet -/// -/// This will create an account if it doesn't exist with the faucet. This is mostly useful -/// for local development and devnet. -#[derive(Debug, Parser)] -pub struct FundWithFaucet { - /// Address to fund - /// - /// If the account wasn't previously created, it will be created when being funded - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: Option, - - /// Number of Octas to fund the account from the faucet - /// - /// The amount added to the account may be limited by the faucet, and may be less - /// than the amount requested. - #[clap(long, default_value_t = DEFAULT_FUNDED_COINS)] - pub(crate) amount: u64, - - #[clap(flatten)] - pub(crate) faucet_options: FaucetOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand for FundWithFaucet { - fn command_name(&self) -> &'static str { - "FundWithFaucet" - } - - async fn execute(self) -> CliTypedResult { - let address = if let Some(account) = self.account { - account - } else { - self.profile_options.account_address()? - }; - let client = self.rest_options.client(&self.profile_options)?; - self.faucet_options - .fund_account(client, &self.profile_options, self.amount, address) - .await?; - return Ok(format!( - "Added {} Octas to account {}", - self.amount, address - )); - } -} diff --git a/crates/aptos/src/account/key_rotation.rs b/crates/aptos/src/account/key_rotation.rs deleted file mode 100644 index e964d4e93ee4c..0000000000000 --- a/crates/aptos/src/account/key_rotation.rs +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{ - account_address_from_auth_key, account_address_from_public_key, - AuthenticationKeyInputOptions, CliCommand, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EncodingOptions, ExtractPublicKey, ParsePrivateKey, ProfileConfig, - ProfileOptions, PublicKeyInputOptions, RestOptions, TransactionOptions, TransactionSummary, - }, - utils::{prompt_yes, prompt_yes_with_override, read_line}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - encoding_type::EncodingType, - PrivateKey, SigningKey, -}; -use aptos_rest_client::{ - aptos_api_types::{AptosError, AptosErrorCode}, - error::{AptosErrorResponse, RestError}, - Client, -}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{RotationProofChallenge, CORE_CODE_ADDRESS}, - transaction::authenticator::AuthenticationKey, -}; -use async_trait::async_trait; -use clap::Parser; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, path::PathBuf}; - -/// Rotate an account's authentication key -/// -/// Rotating the account's authentication key allows you to use a new -/// private key. You must provide a new private key. Once it is -/// rotated you will need to use the original account address, with the -/// new private key. There is an interactive prompt to help you add it -/// to a new profile. -#[derive(Debug, Parser)] -pub struct RotateKey { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - - /// File name that contains the new private key encoded in the type from `--encoding` - #[clap(long, group = "new_private_key_inputs", value_parser)] - pub(crate) new_private_key_file: Option, - - /// New private key encoded in the type from `--encoding` - #[clap(long, group = "new_private_key_inputs")] - pub(crate) new_private_key: Option, - - /// Name of the profile to save the new private key - /// - /// If not provided, it will interactively have you save a profile, - /// unless `--skip_saving_profile` is provided - #[clap(long)] - pub(crate) save_to_profile: Option, - - /// Skip saving profile - /// - /// This skips the interactive profile saving after rotating the authentication key - #[clap(long)] - pub(crate) skip_saving_profile: bool, -} - -impl ParsePrivateKey for RotateKey {} - -impl RotateKey { - /// Extract private key from CLI args - pub fn extract_private_key( - &self, - encoding: EncodingType, - ) -> CliTypedResult> { - self.parse_private_key( - encoding, - self.new_private_key_file.clone(), - self.new_private_key.clone(), - ) - } -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct RotateSummary { - message: Option, - transaction: TransactionSummary, -} - -#[async_trait] -impl CliCommand for RotateKey { - fn command_name(&self) -> &'static str { - "RotateKey" - } - - async fn execute(self) -> CliTypedResult { - let new_private_key = self - .extract_private_key(self.txn_options.encoding_options.encoding)? - .ok_or_else(|| { - CliError::CommandArgumentError( - "One of ['--new-private-key', '--new-private-key-file'] must be used" - .to_string(), - ) - })?; - - let (current_private_key, sender_address) = self.txn_options.get_key_and_address()?; - - if new_private_key == current_private_key { - return Err(CliError::CommandArgumentError( - "New private key cannot be the same as the current private key".to_string(), - )); - } - - // Get sequence number for account - let sequence_number = self.txn_options.sequence_number(sender_address).await?; - let auth_key = self.txn_options.auth_key(sender_address).await?; - - let rotation_proof = RotationProofChallenge { - account_address: CORE_CODE_ADDRESS, - module_name: "account".to_string(), - struct_name: "RotationProofChallenge".to_string(), - sequence_number, - originator: sender_address, - current_auth_key: AccountAddress::from_bytes(auth_key) - .map_err(|err| CliError::UnableToParse("auth_key", err.to_string()))?, - new_public_key: new_private_key.public_key().to_bytes().to_vec(), - }; - - let rotation_msg = - bcs::to_bytes(&rotation_proof).map_err(|err| CliError::BCS("rotation_proof", err))?; - - // Signs the struct using both the current private key and the next private key - let rotation_proof_signed_by_current_private_key = - current_private_key.sign_arbitrary_message(&rotation_msg.clone()); - let rotation_proof_signed_by_new_private_key = - new_private_key.sign_arbitrary_message(&rotation_msg); - - let txn_summary = self - .txn_options - .submit_transaction(aptos_stdlib::account_rotate_authentication_key( - 0, - // Existing public key - current_private_key.public_key().to_bytes().to_vec(), - 0, - // New public key - new_private_key.public_key().to_bytes().to_vec(), - rotation_proof_signed_by_current_private_key - .to_bytes() - .to_vec(), - rotation_proof_signed_by_new_private_key.to_bytes().to_vec(), - )) - .await - .map(TransactionSummary::from)?; - - let string = serde_json::to_string_pretty(&txn_summary) - .map_err(|err| CliError::UnableToParse("transaction summary", err.to_string()))?; - - eprintln!("{}", string); - - if let Some(txn_success) = txn_summary.success { - if !txn_success { - return Err(CliError::ApiError( - "Transaction was not executed successfully".to_string(), - )); - } - } else { - return Err(CliError::UnexpectedError( - "Malformed transaction response".to_string(), - )); - } - - let mut profile_name: String; - - if self.save_to_profile.is_none() { - if self.skip_saving_profile - || !prompt_yes("Do you want to create a profile for the new key?") - { - return Ok(RotateSummary { - transaction: txn_summary, - message: None, - }); - } - - eprintln!("Enter the name for the profile"); - profile_name = read_line("Profile name")?.trim().to_string(); - } else { - // We can safely unwrap here - profile_name = self.save_to_profile.unwrap(); - } - - // Check if profile name exists - let mut config = CliConfig::load(ConfigSearchMode::CurrentDirAndParents)?; - - if let Some(ref profiles) = config.profiles { - if profiles.contains_key(&profile_name) { - if let Err(cli_err) = prompt_yes_with_override( - format!( - "Profile {} exits. Do you want to provide a new profile name?", - profile_name - ) - .as_str(), - self.txn_options.prompt_options, - ) { - match cli_err { - CliError::AbortedError => { - return Ok(RotateSummary { - transaction: txn_summary, - message: None, - }); - }, - _ => { - return Err(cli_err); - }, - } - } - - eprintln!("Enter the name for the profile"); - profile_name = read_line("Profile name")?.trim().to_string(); - } - } - - if profile_name.is_empty() { - return Err(CliError::AbortedError); - } - - let mut profile_config = ProfileConfig { - private_key: Some(new_private_key.clone()), - public_key: Some(new_private_key.public_key()), - account: Some(sender_address), - ..self.txn_options.profile_options.profile()? - }; - - if let Some(url) = self.txn_options.rest_options.url { - profile_config.rest_url = Some(url.into()); - } - - if config.profiles.is_none() { - config.profiles = Some(BTreeMap::new()); - } - - config - .profiles - .as_mut() - .unwrap() - .insert(profile_name.clone(), profile_config); - config.save()?; - - eprintln!("Profile {} is saved.", profile_name); - - Ok(RotateSummary { - transaction: txn_summary, - message: Some(format!("Profile {} is saved.", profile_name)), - }) - } -} - -/// Lookup the account address through the on-chain lookup table -/// -/// If the account is rotated, it will provide the address accordingly. If the account was not -/// rotated, it will provide the derived address only if the account exists onchain. -#[derive(Debug, Parser)] -pub struct LookupAddress { - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, - - #[clap(flatten)] - pub(crate) public_key_options: PublicKeyInputOptions, - - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - - #[clap(flatten)] - pub(crate) authentication_key_options: AuthenticationKeyInputOptions, -} - -impl LookupAddress { - pub(crate) fn public_key(&self) -> CliTypedResult { - self.public_key_options - .extract_public_key(self.encoding_options.encoding, &self.profile_options) - } - - pub(crate) fn auth_key(&self) -> CliTypedResult> { - self.authentication_key_options - .extract_auth_key(self.encoding_options.encoding) - } - - /// Builds a rest client - fn rest_client(&self) -> CliTypedResult { - self.rest_options.client(&self.profile_options) - } -} - -#[async_trait] -impl CliCommand for LookupAddress { - fn command_name(&self) -> &'static str { - "LookupAddress" - } - - async fn execute(self) -> CliTypedResult { - let rest_client = self.rest_client()?; - - // TODO: Support arbitrary auth key to support other types like multie25519 - let address = match self.auth_key()? { - Some(key) => account_address_from_auth_key(&key), - None => account_address_from_public_key(&self.public_key()?), - }; - Ok(lookup_address(&rest_client, address, true).await?) - } -} - -pub async fn lookup_address( - rest_client: &Client, - address_key: AccountAddress, - must_exist: bool, -) -> Result { - let originating_resource: OriginatingResource = rest_client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::account::OriginatingAddress") - .await? - .into_inner(); - - let table_handle = originating_resource.address_map.handle; - - // The derived address that can be used to look up the original address - match rest_client - .get_table_item_bcs( - table_handle, - "address", - "address", - address_key.to_hex_literal(), - ) - .await - { - Ok(inner) => Ok(inner.into_inner()), - Err(RestError::Api(AptosErrorResponse { - error: - AptosError { - error_code: AptosErrorCode::TableItemNotFound, - .. - }, - .. - })) => { - // If the table item wasn't found, we may check if the account exists - if !must_exist { - Ok(address_key) - } else { - rest_client - .get_account_bcs(address_key) - .await - .map(|_| address_key) - } - }, - Err(err) => Err(err), - } -} - -#[derive(Deserialize)] -pub struct OriginatingResource { - pub address_map: Table, -} - -#[derive(Deserialize)] -pub struct Table { - pub handle: AccountAddress, -} diff --git a/crates/aptos/src/account/list.rs b/crates/aptos/src/account/list.rs deleted file mode 100644 index 75cf1af3b7472..0000000000000 --- a/crates/aptos/src/account/list.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{ - CliCommand, CliConfig, CliError, CliTypedResult, ConfigSearchMode, ProfileOptions, RestOptions, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::{Parser, ValueEnum}; -use serde_json::json; -use std::{ - fmt::{Display, Formatter}, - str::FromStr, -}; - -#[derive(ValueEnum, Clone, Copy, Debug)] -pub enum ListQuery { - Balance, - Modules, - Resources, -} - -impl Display for ListQuery { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - ListQuery::Balance => "balance", - ListQuery::Modules => "modules", - ListQuery::Resources => "resources", - }; - write!(f, "{}", str) - } -} - -impl FromStr for ListQuery { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "balance" => Ok(ListQuery::Balance), - "modules" => Ok(ListQuery::Modules), - "resources" => Ok(ListQuery::Resources), - _ => Err("Invalid query. Valid values are balance, modules, resources"), - } - } -} - -/// List resources, modules, or balance owned by an address -/// -/// This allows you to list the current resources at the time of query. This can change due to -/// any transactions that have occurred after the request. -#[derive(Debug, Parser)] -pub struct ListAccount { - /// Address of the account you want to list resources/modules/balance for - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: Option, - - /// Type of items to list: [balance, resources, modules] - #[clap(long, value_enum, ignore_case = true, default_value_t = ListQuery::Resources)] - pub(crate) query: ListQuery, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for ListAccount { - fn command_name(&self) -> &'static str { - "ListAccount" - } - - async fn execute(self) -> CliTypedResult> { - let account = if let Some(account) = self.account { - account - } else if let Some(Some(account)) = CliConfig::load_profile( - self.profile_options.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.account) - { - account - } else { - return Err(CliError::CommandArgumentError( - "Please provide an account using --account or run aptos init".to_string(), - )); - }; - - let client = self.rest_options.client(&self.profile_options)?; - let response = match self.query { - ListQuery::Balance => vec![ - client - .get_account_resource( - account, - "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>", - ) - .await? - .into_inner() - .unwrap() - .data, - ], - ListQuery::Modules => client - .get_account_modules(account) - .await? - .into_inner() - .into_iter() - .map(|module| json!(module.try_parse_abi().unwrap())) - .collect::>(), - ListQuery::Resources => client - .get_account_resources(account) - .await? - .into_inner() - .into_iter() - .map(|resource| { - let mut map = serde_json::Map::new(); - map.insert(resource.resource_type.to_string(), resource.data); - serde_json::Value::Object(map) - }) - .collect::>(), - }; - - Ok(response) - } -} diff --git a/crates/aptos/src/account/mod.rs b/crates/aptos/src/account/mod.rs deleted file mode 100644 index 2421a74327360..0000000000000 --- a/crates/aptos/src/account/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliResult}; -use clap::Subcommand; - -pub mod create; -pub mod create_resource_account; -pub mod derive_resource_account; -pub mod fund; -pub mod key_rotation; -pub mod list; -pub mod multisig_account; -pub mod transfer; - -/// Tool for interacting with accounts -/// -/// This tool is used to create accounts, get information about the -/// account's resources, and transfer resources between accounts. -#[derive(Debug, Subcommand)] -pub enum AccountTool { - Create(create::CreateAccount), - CreateResourceAccount(create_resource_account::CreateResourceAccount), - DeriveResourceAccountAddress(derive_resource_account::DeriveResourceAccount), - FundWithFaucet(fund::FundWithFaucet), - List(list::ListAccount), - LookupAddress(key_rotation::LookupAddress), - RotateKey(key_rotation::RotateKey), - Transfer(transfer::TransferCoins), -} - -impl AccountTool { - pub async fn execute(self) -> CliResult { - match self { - AccountTool::Create(tool) => tool.execute_serialized().await, - AccountTool::CreateResourceAccount(tool) => tool.execute_serialized().await, - AccountTool::DeriveResourceAccountAddress(tool) => tool.execute_serialized().await, - AccountTool::FundWithFaucet(tool) => tool.execute_serialized().await, - AccountTool::List(tool) => tool.execute_serialized().await, - AccountTool::LookupAddress(tool) => tool.execute_serialized().await, - AccountTool::RotateKey(tool) => tool.execute_serialized().await, - AccountTool::Transfer(tool) => tool.execute_serialized().await, - } - } -} - -/// Tool for interacting with multisig accounts -#[derive(Debug, Subcommand)] -pub enum MultisigAccountTool { - Approve(multisig_account::Approve), - Create(multisig_account::Create), - CreateTransaction(multisig_account::CreateTransaction), - Execute(multisig_account::Execute), - ExecuteReject(multisig_account::ExecuteReject), - ExecuteWithPayload(multisig_account::ExecuteWithPayload), - Reject(multisig_account::Reject), - VerifyProposal(multisig_account::VerifyProposal), -} - -impl MultisigAccountTool { - pub async fn execute(self) -> CliResult { - match self { - MultisigAccountTool::Approve(tool) => tool.execute_serialized().await, - MultisigAccountTool::Create(tool) => tool.execute_serialized().await, - MultisigAccountTool::CreateTransaction(tool) => tool.execute_serialized().await, - MultisigAccountTool::Execute(tool) => tool.execute_serialized().await, - MultisigAccountTool::ExecuteReject(tool) => tool.execute_serialized().await, - MultisigAccountTool::ExecuteWithPayload(tool) => tool.execute_serialized().await, - MultisigAccountTool::Reject(tool) => tool.execute_serialized().await, - MultisigAccountTool::VerifyProposal(tool) => tool.execute_serialized().await, - } - } -} diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs deleted file mode 100644 index e37664e9992a7..0000000000000 --- a/crates/aptos/src/account/multisig_account.rs +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{ - types::{ - CliCommand, CliError, CliTypedResult, EntryFunctionArguments, MultisigAccount, - MultisigAccountWithSequenceNumber, TransactionOptions, TransactionSummary, - }, - utils::view_json_option_str, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::HashValue; -use aptos_rest_client::{ - aptos_api_types::{ - EntryFunctionId, HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange, - }, - Transaction, -}; -use aptos_types::{ - account_address::AccountAddress, - transaction::{Multisig, MultisigTransactionPayload, TransactionPayload}, -}; -use async_trait::async_trait; -use bcs::to_bytes; -use clap::Parser; -use once_cell::sync::Lazy; -use serde::Serialize; -use serde_json::json; - -static GET_TRANSACTION_ENTRY_FUNCTION: Lazy = - Lazy::new(|| "0x1::multisig_account::get_transaction".parse().unwrap()); - -/// Create a new multisig account (v2) on-chain. -/// -/// This will create a new multisig account and make the sender one of the owners. -#[derive(Debug, Parser)] -pub struct Create { - /// Addresses of additional owners for the new multisig, beside the transaction sender. - #[clap(long, num_args = 0.., value_parser = crate::common::types::load_account_arg)] - pub(crate) additional_owners: Vec, - /// The number of signatures (approvals or rejections) required to execute or remove a proposed - /// transaction. - #[clap(long)] - pub(crate) num_signatures_required: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -/// A shortened create multisig account output -#[derive(Clone, Debug, Serialize)] -pub struct CreateSummary { - #[serde(flatten)] - pub multisig_account: Option, - #[serde(flatten)] - pub transaction_summary: TransactionSummary, -} - -impl From for CreateSummary { - fn from(transaction: Transaction) -> Self { - let transaction_summary = TransactionSummary::from(&transaction); - - let mut summary = CreateSummary { - transaction_summary, - multisig_account: None, - }; - - if let Transaction::UserTransaction(txn) = transaction { - summary.multisig_account = txn.info.changes.iter().find_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if data.typ.name.as_str() == "Account" - && *address.inner().to_hex() != *txn.request.sender.inner().to_hex() - { - Some(MultisigAccount { - multisig_address: *address.inner(), - }) - } else { - None - } - }, - _ => None, - }); - } - - summary - } -} - -#[async_trait] -impl CliCommand for Create { - fn command_name(&self) -> &'static str { - "CreateMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_create_with_owners( - self.additional_owners, - self.num_signatures_required, - // TODO: Support passing in custom metadata. - vec![], - vec![], - )) - .await - .map(CreateSummary::from) - } -} - -/// Propose a new multisig transaction. -/// -/// As one of the owners of the multisig, propose a new transaction. This also implicitly approves -/// the created transaction so it has one approval initially. In order for the transaction to be -/// executed, it needs as many approvals as the number of signatures required. -#[derive(Debug, Parser)] -pub struct CreateTransaction { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, - /// Pass this flag if only storing transaction hash on-chain. Else full payload is stored - #[clap(long)] - pub(crate) store_hash_only: bool, -} - -#[async_trait] -impl CliCommand for CreateTransaction { - fn command_name(&self) -> &'static str { - "CreateTransactionMultisig" - } - - async fn execute(self) -> CliTypedResult { - let multisig_transaction_payload_bytes = - to_bytes::(&self.entry_function_args.try_into()?)?; - let transaction_payload = if self.store_hash_only { - aptos_stdlib::multisig_account_create_transaction_with_hash( - self.multisig_account.multisig_address, - HashValue::sha3_256_of(&multisig_transaction_payload_bytes).to_vec(), - ) - } else { - aptos_stdlib::multisig_account_create_transaction( - self.multisig_account.multisig_address, - multisig_transaction_payload_bytes, - ) - }; - self.txn_options - .submit_transaction(transaction_payload) - .await - .map(|inner| inner.into()) - } -} - -/// Verify entry function matches on-chain transaction proposal. -#[derive(Debug, Parser)] -pub struct VerifyProposal { - #[clap(flatten)] - pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, -} - -#[async_trait] -impl CliCommand for VerifyProposal { - fn command_name(&self) -> &'static str { - "VerifyProposalMultisig" - } - - async fn execute(self) -> CliTypedResult { - // Get multisig transaction via view function. - let multisig_transaction = &self - .txn_options - .view(ViewRequest { - function: GET_TRANSACTION_ENTRY_FUNCTION.clone(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(String::from( - &self - .multisig_account_with_sequence_number - .multisig_account - .multisig_address, - )), - serde_json::Value::String( - self.multisig_account_with_sequence_number - .sequence_number - .to_string(), - ), - ], - }) - .await?[0]; - // Get expected multisig transaction payload hash hex from provided entry function. - let expected_payload_hash = HashValue::sha3_256_of( - &to_bytes::(&self.entry_function_args.try_into()?)?, - ) - .to_hex_literal(); - // Get on-chain payload hash. If full payload provided on-chain: - let actual_payload_hash = - if let Some(actual_payload) = view_json_option_str(&multisig_transaction["payload"])? { - // Actual payload hash is the hash of the on-chain payload. - HashValue::sha3_256_of(actual_payload.parse::()?.inner()) - .to_hex_literal() - // If full payload not provided, get payload hash directly from transaction proposal: - } else { - view_json_option_str(&multisig_transaction["payload_hash"])?.ok_or( - CliError::UnexpectedError( - "Neither payload nor payload hash provided on-chain".to_string(), - ), - )? - }; - // Get verification result based on if expected and actual payload hashes match. - if expected_payload_hash.eq(&actual_payload_hash) { - Ok(json!({ - "Status": "Transaction match", - "Multisig transaction": multisig_transaction - })) - } else { - Err(CliError::UnexpectedError(format!( - "Transaction mismatch: The transaction you provided has a payload hash of \ - {expected_payload_hash}, but the on-chain transaction proposal you specified has \ - a payload hash of {actual_payload_hash}. For more info, see \ - https://aptos.dev/move/move-on-aptos/cli#multisig-governance" - ))) - } - } -} - -/// Approve a multisig transaction. -/// -/// As one of the owners of the multisig, approve a transaction proposed for the multisig. -/// With enough approvals (as many as the number of signatures required), the transaction can be -/// executed (See Execute). -#[derive(Debug, Parser)] -pub struct Approve { - #[clap(flatten)] - pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Approve { - fn command_name(&self) -> &'static str { - "ApproveMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_approve_transaction( - self.multisig_account_with_sequence_number - .multisig_account - .multisig_address, - self.multisig_account_with_sequence_number.sequence_number, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Reject a multisig transaction. -/// -/// As one of the owners of the multisig, reject a transaction proposed for the multisig. -/// With enough rejections (as many as the number of signatures required), the transaction can be -/// completely removed (See ExecuteReject). -#[derive(Debug, Parser)] -pub struct Reject { - #[clap(flatten)] - pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Reject { - fn command_name(&self) -> &'static str { - "RejectMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_reject_transaction( - self.multisig_account_with_sequence_number - .multisig_account - .multisig_address, - self.multisig_account_with_sequence_number.sequence_number, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Execute a proposed multisig transaction that has a full payload stored on-chain. -#[derive(Debug, Parser)] -pub struct Execute { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for Execute { - fn command_name(&self) -> &'static str { - "ExecuteMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(TransactionPayload::Multisig(Multisig { - multisig_address: self.multisig_account.multisig_address, - transaction_payload: None, - })) - .await - .map(|inner| inner.into()) - } -} - -/// Execute a proposed multisig transaction that has only a payload hash stored on-chain. -#[derive(Debug, Parser)] -pub struct ExecuteWithPayload { - #[clap(flatten)] - pub(crate) execute: Execute, - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, -} - -#[async_trait] -impl CliCommand for ExecuteWithPayload { - fn command_name(&self) -> &'static str { - "ExecuteWithPayloadMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.execute - .txn_options - .submit_transaction(TransactionPayload::Multisig(Multisig { - multisig_address: self.execute.multisig_account.multisig_address, - transaction_payload: Some(self.entry_function_args.try_into()?), - })) - .await - .map(|inner| inner.into()) - } -} - -/// Remove a proposed multisig transaction. -/// -/// The transaction to be removed needs to have as many rejections as the number of signatures -/// required. -#[derive(Debug, Parser)] -pub struct ExecuteReject { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for ExecuteReject { - fn command_name(&self) -> &'static str { - "ExecuteRejectMultisig" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_execute_rejected_transaction( - self.multisig_account.multisig_address, - )) - .await - .map(|inner| inner.into()) - } -} diff --git a/crates/aptos/src/account/transfer.rs b/crates/aptos/src/account/transfer.rs deleted file mode 100644 index 68023e355b43f..0000000000000 --- a/crates/aptos/src/account/transfer.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliTypedResult, TransactionOptions}; -use aptos_cached_packages::aptos_stdlib; -use aptos_rest_client::{ - aptos_api_types::{HashValue, WriteResource, WriteSetChange}, - Transaction, -}; -use aptos_types::account_address::AccountAddress; -use async_trait::async_trait; -use clap::Parser; -use serde::Serialize; -use std::collections::BTreeMap; - -// TODO: Add ability to transfer non-APT coins -// TODO: Add ability to not create account by default -/// Transfer APT between accounts -/// -#[derive(Debug, Parser)] -pub struct TransferCoins { - /// Address of account to send APT to - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - /// Amount of Octas (10^-8 APT) to transfer - #[clap(long)] - pub(crate) amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for TransferCoins { - fn command_name(&self) -> &'static str { - "TransferCoins" - } - - async fn execute(self) -> CliTypedResult { - self.txn_options - .submit_transaction(aptos_stdlib::aptos_account_transfer( - self.account, - self.amount, - )) - .await - .map(TransferSummary::from) - } -} - -const SUPPORTED_COINS: [&str; 1] = ["0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"]; - -/// A shortened transaction output -#[derive(Clone, Debug, Serialize)] -pub struct TransferSummary { - pub gas_unit_price: u64, - pub gas_used: u64, - pub balance_changes: BTreeMap, - pub sender: AccountAddress, - pub success: bool, - pub version: u64, - pub vm_status: String, - pub transaction_hash: HashValue, -} - -impl TransferSummary { - pub fn octa_spent(&self) -> u64 { - self.gas_unit_price * self.gas_used - } -} - -impl From for TransferSummary { - fn from(transaction: Transaction) -> Self { - if let Transaction::UserTransaction(txn) = transaction { - let vm_status = txn.info.vm_status; - let success = txn.info.success; - let sender = *txn.request.sender.inner(); - let gas_unit_price = txn.request.gas_unit_price.0; - let gas_used = txn.info.gas_used.0; - let transaction_hash = txn.info.hash; - let version = txn.info.version.0; - let balance_changes = txn - .info - .changes - .into_iter() - .filter_map(|change| match change { - WriteSetChange::WriteResource(WriteResource { address, data, .. }) => { - if SUPPORTED_COINS.contains(&data.typ.to_string().as_str()) { - Some(( - *address.inner(), - serde_json::to_value(data.data).unwrap_or_default(), - )) - } else { - None - } - }, - _ => None, - }) - .collect(); - - TransferSummary { - gas_unit_price, - gas_used, - balance_changes, - sender, - success, - version, - vm_status, - transaction_hash, - } - } else { - panic!("Can't call From for a non UserTransaction") - } - } -} diff --git a/crates/aptos/src/common/init.rs b/crates/aptos/src/common/init.rs deleted file mode 100644 index ae9c12a040489..0000000000000 --- a/crates/aptos/src/common/init.rs +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::key_rotation::lookup_address, - common::{ - types::{ - account_address_from_public_key, CliCommand, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EncodingOptions, HardwareWalletOptions, PrivateKeyInputOptions, - ProfileConfig, ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE, - }, - utils::{fund_account, prompt_yes_with_override, read_line}, - }, -}; -use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt}; -use aptos_ledger; -use aptos_rest_client::{ - aptos_api_types::{AptosError, AptosErrorCode}, - error::{AptosErrorResponse, RestError}, -}; -use async_trait::async_trait; -use clap::Parser; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, str::FromStr}; - -/// 1 APT (might not actually get that much, depending on the faucet) -const NUM_DEFAULT_OCTAS: u64 = 100000000; - -/// Tool to initialize current directory for the aptos tool -/// -/// Configuration will be pushed into .aptos/config.yaml -#[derive(Debug, Parser)] -pub struct InitTool { - /// Network to use for default settings - /// - /// If custom `rest_url` and `faucet_url` are wanted, use `custom` - #[clap(long)] - pub network: Option, - - /// URL to a fullnode on the network - #[clap(long)] - pub rest_url: Option, - - /// URL for the Faucet endpoint - #[clap(long)] - pub faucet_url: Option, - - /// Auth token, if we're using the faucet. This is only used this time, we don't - /// store it. - #[clap(long, env)] - pub faucet_auth_token: Option, - - /// Whether to skip the faucet for a non-faucet endpoint - #[clap(long)] - pub skip_faucet: bool, - - /// Whether you want to create a profile from your ledger account - /// - /// Make sure that you have your Ledger device connected and unlocked, with the Aptos app installed and opened. - /// You must also enable "Blind Signing" on your device to sign transactions from the CLI. - #[clap(long)] - pub ledger: bool, - - #[clap(flatten)] - pub(crate) hardware_wallet_options: HardwareWalletOptions, - - #[clap(flatten)] - pub rng_args: RngArgs, - #[clap(flatten)] - pub(crate) private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -#[async_trait] -impl CliCommand<()> for InitTool { - fn command_name(&self) -> &'static str { - "AptosInit" - } - - async fn execute(self) -> CliTypedResult<()> { - let mut config = if CliConfig::config_exists(ConfigSearchMode::CurrentDir) { - CliConfig::load(ConfigSearchMode::CurrentDir)? - } else { - CliConfig::default() - }; - - let profile_name = self - .profile_options - .profile_name() - .unwrap_or(DEFAULT_PROFILE); - - // Select profile we're using - let mut profile_config = if let Some(profile_config) = config.remove_profile(profile_name) { - prompt_yes_with_override(&format!("Aptos already initialized for profile {}, do you want to overwrite the existing config?", profile_name), self.prompt_options)?; - profile_config - } else { - ProfileConfig::default() - }; - eprintln!("Configuring for profile {}", profile_name); - - // Choose a network - let network = if let Some(network) = self.network { - eprintln!("Configuring for network {:?}", network); - network - } else { - eprintln!( - "Choose network from [devnet, testnet, mainnet, local, custom | defaults to devnet]" - ); - let input = read_line("network")?; - let input = input.trim(); - if input.is_empty() { - eprintln!("No network given, using devnet..."); - Network::Devnet - } else { - Network::from_str(input)? - } - }; - - // Ensure that there is at least a REST URL set for the network - match network { - Network::Mainnet => { - profile_config.rest_url = - Some("https://fullnode.mainnet.aptoslabs.com".to_string()); - profile_config.faucet_url = None; - }, - Network::Testnet => { - profile_config.rest_url = - Some("https://fullnode.testnet.aptoslabs.com".to_string()); - profile_config.faucet_url = - Some("https://faucet.testnet.aptoslabs.com".to_string()); - }, - Network::Devnet => { - profile_config.rest_url = Some("https://fullnode.devnet.aptoslabs.com".to_string()); - profile_config.faucet_url = Some("https://faucet.devnet.aptoslabs.com".to_string()); - }, - Network::Local => { - profile_config.rest_url = Some("http://localhost:8080".to_string()); - profile_config.faucet_url = Some("http://localhost:8081".to_string()); - }, - Network::Custom => self.custom_network(&mut profile_config)?, - } - - // Check if any ledger flag is set - let derivation_path = if let Some(deri_path) = - self.hardware_wallet_options.extract_derivation_path()? - { - Some(deri_path) - } else if self.ledger { - // Fetch the top 5 (index 0-4) accounts from Ledger - let account_map = aptos_ledger::fetch_batch_accounts(Some(0..5))?; - eprintln!( - "Please choose an index from the following {} ledger accounts, or choose an arbitrary index that you want to use:", - account_map.len() - ); - - // Iterate through the accounts and print them out - for (index, (derivation_path, account)) in account_map.iter().enumerate() { - eprintln!( - "[{}] Derivation path: {} (Address: {})", - index, derivation_path, account - ); - } - let input_index = read_line("derivation_index")?; - let input_index = input_index.trim(); - let path = aptos_ledger::DERIVATION_PATH.replace("{index}", input_index); - - // Validate the path - if !aptos_ledger::validate_derivation_path(&path) { - return Err(CliError::UnexpectedError( - "Invalid index input. Please make sure the input is a valid number index" - .to_owned(), - )); - } - Some(path) - } else { - None - }; - - // Set the derivation_path to the one user chose - profile_config.derivation_path = derivation_path.clone(); - - // Private key - let private_key = if self.is_hardware_wallet() { - // Private key stays in ledger - None - } else { - let ed25519_private_key = if let Some(key) = self - .private_key_options - .extract_private_key_cli(self.encoding_options.encoding)? - { - eprintln!("Using command line argument for private key"); - key - } else { - eprintln!("Enter your private key as a hex literal (0x...) [Current: {} | No input: Generate new key (or keep one if present)]", profile_config.private_key.as_ref().map(|_| "Redacted").unwrap_or("None")); - let input = read_line("Private key")?; - let input = input.trim(); - if input.is_empty() { - if let Some(key) = profile_config.private_key { - eprintln!("No key given, keeping existing key..."); - key - } else { - eprintln!("No key given, generating key..."); - self.rng_args - .key_generator()? - .generate_ed25519_private_key() - } - } else { - Ed25519PrivateKey::from_encoded_string(input).map_err(|err| { - CliError::UnableToParse("Ed25519PrivateKey", err.to_string()) - })? - } - }; - - Some(ed25519_private_key) - }; - - // Public key - let public_key = if self.is_hardware_wallet() { - let pub_key = match aptos_ledger::get_public_key( - derivation_path - .ok_or(CliError::UnexpectedError( - "Invalid derivation path".to_string(), - ))? - .as_str(), - false, - ) { - Ok(pub_key_str) => pub_key_str, - Err(err) => { - return Err(CliError::UnexpectedError(format!( - "Unexpected Ledger Error: {:?}", - err.to_string() - ))) - }, - }; - pub_key - } else { - private_key.clone().unwrap().public_key() - }; - - let rest_url = Url::parse( - profile_config - .rest_url - .as_ref() - .expect("Must have rest client as created above"), - ) - .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?; - let client = aptos_rest_client::Client::new(rest_url); - - // lookup the address from onchain instead of deriving it - // if this is the rotated key, deriving it will outputs an incorrect address - let derived_address = account_address_from_public_key(&public_key); - let address = lookup_address(&client, derived_address, false).await?; - - profile_config.private_key = private_key; - profile_config.public_key = Some(public_key); - profile_config.account = Some(address); - - // Create account if it doesn't exist (and there's a faucet) - // Check if account exists - let account_exists = match client.get_account(address).await { - Ok(_) => true, - Err(err) => { - if let RestError::Api(AptosErrorResponse { - error: - AptosError { - error_code: AptosErrorCode::ResourceNotFound, - .. - }, - .. - }) - | RestError::Api(AptosErrorResponse { - error: - AptosError { - error_code: AptosErrorCode::AccountNotFound, - .. - }, - .. - }) = err - { - false - } else { - return Err(CliError::UnexpectedError(format!( - "Failed to check if account exists: {:?}", - err - ))); - } - }, - }; - - // If you want to create a private key, but not fund the account, skipping the faucet is still possible - let maybe_faucet_url = if self.skip_faucet { - None - } else { - profile_config.faucet_url.as_ref() - }; - - if let Some(faucet_url) = maybe_faucet_url { - if account_exists { - eprintln!("Account {} has been already found onchain", address); - } else { - eprintln!( - "Account {} doesn't exist, creating it and funding it with {} Octas", - address, NUM_DEFAULT_OCTAS - ); - fund_account( - client, - Url::parse(faucet_url) - .map_err(|err| CliError::UnableToParse("rest_url", err.to_string()))?, - self.faucet_auth_token.as_deref(), - address, - NUM_DEFAULT_OCTAS, - ) - .await?; - eprintln!("Account {} funded successfully", address); - } - } else if account_exists { - eprintln!("Account {} has been already found onchain", address); - } else if network == Network::Mainnet { - eprintln!("Account {} does not exist, you will need to create and fund the account by transferring funds from another account", address); - } else { - eprintln!("Account {} has been initialized locally, but you must transfer coins to it to create the account onchain", address); - } - - // Ensure the loaded config has profiles setup for a possible empty file - if config.profiles.is_none() { - config.profiles = Some(BTreeMap::new()); - } - config - .profiles - .as_mut() - .expect("Must have profiles, as created above") - .insert(profile_name.to_string(), profile_config); - config.save()?; - eprintln!("\n---\nAptos CLI is now set up for account {} as profile {}! Run `aptos --help` for more information about commands", address, self.profile_options.profile_name().unwrap_or(DEFAULT_PROFILE)); - Ok(()) - } -} - -impl InitTool { - /// Custom network created, which requires a REST URL - fn custom_network(&self, profile_config: &mut ProfileConfig) -> CliTypedResult<()> { - // Rest Endpoint - let rest_url = if let Some(ref rest_url) = self.rest_url { - eprintln!("Using command line argument for rest URL {}", rest_url); - Some(rest_url.to_string()) - } else { - let current = profile_config.rest_url.as_deref(); - eprintln!( - "Enter your rest endpoint [Current: {} | No input: Exit (or keep the existing if present)]", - current.unwrap_or("None"), - ); - let input = read_line("Rest endpoint")?; - let input = input.trim(); - if input.is_empty() { - if let Some(current) = current { - eprintln!("No rest url given, keeping the existing url..."); - Some(current.to_string()) - } else { - eprintln!("No rest url given, exiting..."); - return Err(CliError::AbortedError); - } - } else { - Some( - reqwest::Url::parse(input) - .map_err(|err| CliError::UnableToParse("Rest Endpoint", err.to_string()))? - .to_string(), - ) - } - }; - profile_config.rest_url = rest_url; - - // Faucet Endpoint - let faucet_url = if self.skip_faucet { - eprintln!("Not configuring a faucet because --skip-faucet was provided"); - None - } else if let Some(ref faucet_url) = self.faucet_url { - eprintln!("Using command line argument for faucet URL {}", faucet_url); - Some(faucet_url.to_string()) - } else { - let current = profile_config.faucet_url.as_deref(); - eprintln!( - "Enter your faucet endpoint [Current: {} | No input: Skip (or keep the existing one if present) | 'skip' to not use a faucet]", - current - .unwrap_or("None"), - ); - let input = read_line("Faucet endpoint")?; - let input = input.trim(); - if input.is_empty() { - if let Some(current) = current { - eprintln!("No faucet url given, keeping the existing url..."); - Some(current.to_string()) - } else { - eprintln!("No faucet url given, skipping faucet..."); - None - } - } else if input.to_lowercase() == "skip" { - eprintln!("Skipping faucet..."); - None - } else { - Some( - reqwest::Url::parse(input) - .map_err(|err| CliError::UnableToParse("Faucet Endpoint", err.to_string()))? - .to_string(), - ) - } - }; - profile_config.faucet_url = faucet_url; - Ok(()) - } - - fn is_hardware_wallet(&self) -> bool { - self.hardware_wallet_options.is_hardware_wallet() || self.ledger - } -} - -/// A simplified list of all networks supported by the CLI -/// -/// Any command using this, will be simpler to setup as profiles -#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] -pub enum Network { - Mainnet, - Testnet, - Devnet, - Local, - Custom, -} - -impl FromStr for Network { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(match s.to_lowercase().trim() { - "mainnet" => Self::Mainnet, - "testnet" => Self::Testnet, - "devnet" => Self::Devnet, - "local" => Self::Local, - "custom" => Self::Custom, - str => { - return Err(CliError::CommandArgumentError(format!( - "Invalid network {}. Must be one of [devnet, testnet, mainnet, local, custom]", - str - ))); - }, - }) - } -} - -impl Default for Network { - fn default() -> Self { - Self::Devnet - } -} diff --git a/crates/aptos/src/common/mod.rs b/crates/aptos/src/common/mod.rs deleted file mode 100644 index 1be94da1d100b..0000000000000 --- a/crates/aptos/src/common/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod init; -pub mod types; -pub mod utils; diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs deleted file mode 100644 index 46ce57ca21234..0000000000000 --- a/crates/aptos/src/common/types.rs +++ /dev/null @@ -1,2152 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::utils::fund_account; -use crate::{ - common::{ - init::Network, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, - get_account_with_state, get_auth_key, get_sequence_number, parse_json_file, - prompt_yes_with_override, read_from_file, start_logger, to_common_result, - to_common_success_result, write_to_file, write_to_file_with_opts, - write_to_user_only_file, - }, - }, - config::GlobalConfig, - genesis::git::from_yaml, - move_tool::{ArgWithType, FunctionArgType, MemberId}, -}; -use anyhow::Context; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, - encoding_type::{EncodingError, EncodingType}, - x25519, PrivateKey, ValidCryptoMaterialStringExt, -}; -use aptos_gas_profiling::FrameName; -use aptos_global_constants::adjust_gas_headroom; -use aptos_keygen::KeyGen; -use aptos_logger::Level; -use aptos_move_debugger::aptos_debugger::AptosDebugger; -use aptos_rest_client::{ - aptos_api_types::{EntryFunctionId, HashValue, MoveType, ViewRequest}, - error::RestError, - AptosBaseUrl, Client, Transaction, -}; -use aptos_sdk::{ - transaction_builder::TransactionFactory, - types::{HardwareWalletAccount, HardwareWalletType, LocalAccount, TransactionSigner}, -}; -use aptos_types::{ - chain_id::ChainId, - transaction::{ - authenticator::AuthenticationKey, EntryFunction, MultisigTransactionPayload, Script, - SignedTransaction, TransactionArgument, TransactionPayload, TransactionStatus, - }, -}; -use async_trait::async_trait; -use clap::{Parser, ValueEnum}; -use hex::FromHexError; -use move_core_types::{account_address::AccountAddress, language_storage::TypeTag}; -use move_package::CompilerVersion; -use serde::{Deserialize, Serialize}; -#[cfg(unix)] -use std::os::unix::fs::OpenOptionsExt; -use std::{ - collections::BTreeMap, - convert::TryFrom, - fmt::{Debug, Display, Formatter}, - fs::OpenOptions, - path::{Path, PathBuf}, - str::FromStr, - time::{Duration, Instant, SystemTime, UNIX_EPOCH}, -}; -use thiserror::Error; - -pub const USER_AGENT: &str = concat!("aptos-cli/", env!("CARGO_PKG_VERSION")); -const US_IN_SECS: u64 = 1_000_000; -const ACCEPTED_CLOCK_SKEW_US: u64 = 5 * US_IN_SECS; -pub const DEFAULT_EXPIRATION_SECS: u64 = 30; -pub const DEFAULT_PROFILE: &str = "default"; - -// Custom header value to identify the client -const X_APTOS_CLIENT_VALUE: &str = concat!("aptos-cli/", env!("CARGO_PKG_VERSION")); - -/// A common result to be returned to users -pub type CliResult = Result; - -/// A common result to remove need for typing `Result` -pub type CliTypedResult = Result; - -/// CLI Errors for reporting through telemetry and outputs -#[derive(Debug, Error)] -pub enum CliError { - #[error("Aborted command")] - AbortedError, - #[error("API error: {0}")] - ApiError(String), - #[error("Error (de)serializing '{0}': {1}")] - BCS(&'static str, #[source] bcs::Error), - #[error("Invalid arguments: {0}")] - CommandArgumentError(String), - #[error("Unable to load config: {0} {1}")] - ConfigLoadError(String, String), - #[error("Unable to find config {0}, have you run `aptos init`?")] - ConfigNotFoundError(String), - #[error("Error accessing '{0}': {1}")] - IO(String, #[source] std::io::Error), - #[error("Move compilation failed: {0}")] - MoveCompilationError(String), - #[error("Move unit tests failed")] - MoveTestError, - #[error("Move Prover failed: {0}")] - MoveProverError(String), - #[error("Unable to parse '{0}': error: {1}")] - UnableToParse(&'static str, String), - #[error("Unable to read file '{0}', error: {1}")] - UnableToReadFile(String, String), - #[error("Unexpected error: {0}")] - UnexpectedError(String), - #[error("Simulation failed with status: {0}")] - SimulationError(String), - #[error("Coverage failed with status: {0}")] - CoverageError(String), -} - -impl CliError { - pub fn to_str(&self) -> &'static str { - match self { - CliError::AbortedError => "AbortedError", - CliError::ApiError(_) => "ApiError", - CliError::BCS(_, _) => "BCS", - CliError::CommandArgumentError(_) => "CommandArgumentError", - CliError::ConfigLoadError(_, _) => "ConfigLoadError", - CliError::ConfigNotFoundError(_) => "ConfigNotFoundError", - CliError::IO(_, _) => "IO", - CliError::MoveCompilationError(_) => "MoveCompilationError", - CliError::MoveTestError => "MoveTestError", - CliError::MoveProverError(_) => "MoveProverError", - CliError::UnableToParse(_, _) => "UnableToParse", - CliError::UnableToReadFile(_, _) => "UnableToReadFile", - CliError::UnexpectedError(_) => "UnexpectedError", - CliError::SimulationError(_) => "SimulationError", - CliError::CoverageError(_) => "CoverageError", - } - } -} - -impl From for CliError { - fn from(e: RestError) -> Self { - CliError::ApiError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_config::config::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_github_client::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: serde_yaml::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: base64::DecodeError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: std::string::FromUtf8Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_crypto::CryptoMaterialError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: FromHexError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: anyhow::Error) -> Self { - CliError::UnexpectedError(format!("{:#}", e)) - } -} - -impl From for CliError { - fn from(e: bcs::Error) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: aptos_ledger::AptosLedgerError) -> Self { - CliError::UnexpectedError(e.to_string()) - } -} - -impl From for CliError { - fn from(e: EncodingError) -> Self { - match e { - EncodingError::BCS(s, e) => CliError::BCS(s, e), - EncodingError::UnableToParse(s, e) => CliError::UnableToParse(s, e), - EncodingError::UnableToReadFile(s, e) => CliError::UnableToReadFile(s, e), - EncodingError::UTF8(s) => CliError::UnexpectedError(s), - } - } -} - -/// Config saved to `.aptos/config.yaml` -#[derive(Debug, Serialize, Deserialize)] -pub struct CliConfig { - /// Map of profile configs - #[serde(skip_serializing_if = "Option::is_none")] - pub profiles: Option>, -} - -const CONFIG_FILE: &str = "config.yaml"; -const LEGACY_CONFIG_FILE: &str = "config.yml"; -pub const CONFIG_FOLDER: &str = ".aptos"; - -/// An individual profile -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct ProfileConfig { - #[serde(skip_serializing_if = "Option::is_none")] - pub network: Option, - /// Private key for commands. - #[serde(skip_serializing_if = "Option::is_none")] - pub private_key: Option, - /// Public key for commands - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, - /// Account for commands - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - /// URL for the Aptos rest endpoint - #[serde(skip_serializing_if = "Option::is_none")] - pub rest_url: Option, - /// URL for the Faucet endpoint (if applicable) - #[serde(skip_serializing_if = "Option::is_none")] - pub faucet_url: Option, - /// Derivation path index of the account on ledger - #[serde(skip_serializing_if = "Option::is_none")] - pub derivation_path: Option, -} - -/// ProfileConfig but without the private parts -#[derive(Debug, Serialize)] -pub struct ProfileSummary { - pub has_private_key: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub rest_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub faucet_url: Option, -} - -impl From<&ProfileConfig> for ProfileSummary { - fn from(config: &ProfileConfig) -> Self { - ProfileSummary { - has_private_key: config.private_key.is_some(), - public_key: config.public_key.clone(), - account: config.account, - rest_url: config.rest_url.clone(), - faucet_url: config.faucet_url.clone(), - } - } -} - -impl Default for CliConfig { - fn default() -> Self { - CliConfig { - profiles: Some(BTreeMap::new()), - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] -pub enum ConfigSearchMode { - CurrentDir, - CurrentDirAndParents, -} - -impl CliConfig { - /// Checks if the config exists in the current working directory - pub fn config_exists(mode: ConfigSearchMode) -> bool { - if let Ok(folder) = Self::aptos_folder(mode) { - let config_file = folder.join(CONFIG_FILE); - let old_config_file = folder.join(LEGACY_CONFIG_FILE); - config_file.exists() || old_config_file.exists() - } else { - false - } - } - - /// Loads the config from the current working directory or one of its parents. - pub fn load(mode: ConfigSearchMode) -> CliTypedResult { - let folder = Self::aptos_folder(mode)?; - - let config_file = folder.join(CONFIG_FILE); - let old_config_file = folder.join(LEGACY_CONFIG_FILE); - if config_file.exists() { - from_yaml( - &String::from_utf8(read_from_file(config_file.as_path())?) - .map_err(CliError::from)?, - ) - } else if old_config_file.exists() { - from_yaml( - &String::from_utf8(read_from_file(old_config_file.as_path())?) - .map_err(CliError::from)?, - ) - } else { - Err(CliError::ConfigNotFoundError(format!( - "{}", - config_file.display() - ))) - } - } - - pub fn load_profile( - profile: Option<&str>, - mode: ConfigSearchMode, - ) -> CliTypedResult> { - let mut config = Self::load(mode)?; - - // If no profile was given, use `default` - if let Some(profile) = profile { - if let Some(account_profile) = config.remove_profile(profile) { - Ok(Some(account_profile)) - } else { - Err(CliError::CommandArgumentError(format!( - "Profile {} not found", - profile - ))) - } - } else { - Ok(config.remove_profile(DEFAULT_PROFILE)) - } - } - - pub fn remove_profile(&mut self, profile: &str) -> Option { - if let Some(ref mut profiles) = self.profiles { - profiles.remove(&profile.to_string()) - } else { - None - } - } - - /// Saves the config to ./.aptos/config.yaml - pub fn save(&self) -> CliTypedResult<()> { - let aptos_folder = Self::aptos_folder(ConfigSearchMode::CurrentDir)?; - - // Create if it doesn't exist - create_dir_if_not_exist(aptos_folder.as_path())?; - - // Save over previous config file - let config_file = aptos_folder.join(CONFIG_FILE); - let config_bytes = serde_yaml::to_string(&self).map_err(|err| { - CliError::UnexpectedError(format!("Failed to serialize config {}", err)) - })?; - write_to_user_only_file(&config_file, CONFIG_FILE, config_bytes.as_bytes())?; - - // As a cleanup, delete the old if it exists - let legacy_config_file = aptos_folder.join(LEGACY_CONFIG_FILE); - if legacy_config_file.exists() { - eprintln!("Removing legacy config file {}", LEGACY_CONFIG_FILE); - let _ = std::fs::remove_file(legacy_config_file); - } - Ok(()) - } - - /// Finds the current directory's .aptos folder - fn aptos_folder(mode: ConfigSearchMode) -> CliTypedResult { - let global_config = GlobalConfig::load()?; - global_config.get_config_location(mode) - } -} - -/// Types of Keys used by the blockchain -#[derive(ValueEnum, Clone, Copy, Debug)] -pub enum KeyType { - /// Ed25519 key used for signing - Ed25519, - /// X25519 key used for network handshakes and identity - X25519, - /// A BLS12381 key for consensus - Bls12381, -} - -impl Display for KeyType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = match self { - KeyType::Ed25519 => "ed25519", - KeyType::X25519 => "x25519", - KeyType::Bls12381 => "bls12381", - }; - write!(f, "{}", str) - } -} - -impl FromStr for KeyType { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "ed25519" => Ok(KeyType::Ed25519), - "x25519" => Ok(KeyType::X25519), - "bls12381" => Ok(KeyType::Bls12381), - _ => Err("Invalid key type: Must be one of [ed25519, x25519]"), - } - } -} - -#[derive(Debug, Default, Parser)] -pub struct ProfileOptions { - /// Profile to use from the CLI config - /// - /// This will be used to override associated settings such as - /// the REST URL, the Faucet URL, and the private key arguments. - /// - /// Defaults to "default" - #[clap(long)] - pub profile: Option, -} - -impl ProfileOptions { - pub fn account_address(&self) -> CliTypedResult { - let profile = self.profile()?; - if let Some(account) = profile.account { - return Ok(account); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } - - pub fn derivation_path(&self) -> CliTypedResult> { - let profile = self.profile()?; - Ok(profile.derivation_path) - } - - pub fn public_key(&self) -> CliTypedResult { - let profile = self.profile()?; - if let Some(public_key) = profile.public_key { - return Ok(public_key); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } - - pub fn profile_name(&self) -> Option<&str> { - self.profile.as_ref().map(|inner| inner.trim()) - } - - pub fn profile(&self) -> CliTypedResult { - if let Some(profile) = - CliConfig::load_profile(self.profile_name(), ConfigSearchMode::CurrentDirAndParents)? - { - return Ok(profile); - } - - Err(CliError::ConfigNotFoundError( - self.profile - .clone() - .unwrap_or_else(|| DEFAULT_PROFILE.to_string()), - )) - } -} - -#[derive(Clone, Debug, Parser)] -pub struct RngArgs { - /// The seed used for key generation, should be a 64 character hex string and only used for testing - /// - /// If a predictable random seed is used, the key that is produced will be insecure and easy - /// to reproduce. Please do not use this unless sufficient randomness is put into the random - /// seed. - #[clap(long)] - random_seed: Option, -} - -impl RngArgs { - pub fn from_seed(seed: [u8; 32]) -> RngArgs { - RngArgs { - random_seed: Some(hex::encode(seed)), - } - } - - pub fn from_string_seed(str: &str) -> RngArgs { - assert!(str.len() < 32); - - let mut seed = [0u8; 32]; - for (i, byte) in str.bytes().enumerate() { - seed[i] = byte; - } - - RngArgs { - random_seed: Some(hex::encode(seed)), - } - } - - /// Returns a key generator with the seed if given - pub fn key_generator(&self) -> CliTypedResult { - if let Some(ref seed) = self.random_seed { - // Strip 0x - let seed = seed.strip_prefix("0x").unwrap_or(seed); - let mut seed_slice = [0u8; 32]; - - hex::decode_to_slice(seed, &mut seed_slice)?; - Ok(KeyGen::from_seed(seed_slice)) - } else { - Ok(KeyGen::from_os_rng()) - } - } -} - -/// An insertable option for use with prompts. -#[derive(Clone, Copy, Debug, Default, Parser, PartialEq, Eq)] -pub struct PromptOptions { - /// Assume yes for all yes/no prompts - #[clap(long, group = "prompt_options")] - pub assume_yes: bool, - /// Assume no for all yes/no prompts - #[clap(long, group = "prompt_options")] - pub assume_no: bool, -} - -impl PromptOptions { - pub fn yes() -> Self { - Self { - assume_yes: true, - assume_no: false, - } - } - - pub fn no() -> Self { - Self { - assume_yes: false, - assume_no: true, - } - } -} - -/// An insertable option for use with encodings. -#[derive(Debug, Default, Parser)] -pub struct EncodingOptions { - /// Encoding of data as one of [base64, bcs, hex] - #[clap(long, default_value_t = EncodingType::Hex)] - pub encoding: EncodingType, -} - -#[derive(Debug, Parser)] -pub struct AuthenticationKeyInputOptions { - /// Authentication Key file input - #[clap(long, group = "authentication_key_input", value_parser)] - auth_key_file: Option, - - /// Authentication key input - #[clap(long, group = "authentication_key_input")] - auth_key: Option, -} - -impl AuthenticationKeyInputOptions { - pub fn extract_auth_key( - &self, - encoding: EncodingType, - ) -> CliTypedResult> { - if let Some(ref file) = self.auth_key_file { - Ok(Some(encoding.load_key("--auth-key-file", file.as_path())?)) - } else if let Some(ref key) = self.auth_key { - let key = key.as_bytes().to_vec(); - Ok(Some(encoding.decode_key("--auth-key", key)?)) - } else { - Ok(None) - } - } - - pub fn from_public_key(key: &Ed25519PublicKey) -> AuthenticationKeyInputOptions { - let auth_key = AuthenticationKey::ed25519(key); - AuthenticationKeyInputOptions { - auth_key: Some(auth_key.to_encoded_string().unwrap()), - auth_key_file: None, - } - } -} - -#[derive(Debug, Parser)] -pub struct PublicKeyInputOptions { - /// Ed25519 Public key input file name - /// - /// Mutually exclusive with `--public-key` - #[clap(long, group = "public_key_input", value_parser)] - public_key_file: Option, - /// Ed25519 Public key encoded in a type as shown in `encoding` - /// - /// Mutually exclusive with `--public-key-file` - #[clap(long, group = "public_key_input")] - public_key: Option, -} - -impl PublicKeyInputOptions { - pub fn from_key(key: &Ed25519PublicKey) -> PublicKeyInputOptions { - PublicKeyInputOptions { - public_key: Some(key.to_encoded_string().unwrap()), - public_key_file: None, - } - } -} - -impl ExtractPublicKey for PublicKeyInputOptions { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - if let Some(ref file) = self.public_key_file { - Ok(encoding.load_key("--public-key-file", file.as_path())?) - } else if let Some(ref key) = self.public_key { - let key = key.as_bytes().to_vec(); - Ok(encoding.decode_key("--public-key", key)?) - } else if let Some(Some(public_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.public_key) - { - Ok(public_key) - } else { - Err(CliError::CommandArgumentError( - "One of ['--public-key', '--public-key-file', '--profile'] must be used" - .to_string(), - )) - } - } -} - -pub trait ParsePrivateKey { - fn parse_private_key( - &self, - encoding: EncodingType, - private_key_file: Option, - private_key: Option, - ) -> CliTypedResult> { - if let Some(ref file) = private_key_file { - Ok(Some( - encoding.load_key("--private-key-file", file.as_path())?, - )) - } else if let Some(ref key) = private_key { - let key = key.as_bytes().to_vec(); - Ok(Some(encoding.decode_key("--private-key", key)?)) - } else { - Ok(None) - } - } -} - -#[derive(Debug, Default, Parser)] -pub struct HardwareWalletOptions { - /// Derivation Path of your account in hardware wallet - /// - /// e.g format - m/44\'/637\'/0\'/0\'/0\' - /// Make sure your wallet is unlocked and have Aptos opened - #[clap(long)] - pub derivation_path: Option, - - /// Index of your account in hardware wallet - /// - /// This is the simpler version of derivation path e.g format - [0] - /// we will translate this index into [m/44'/637'/0'/0'/0] - #[clap(long)] - pub derivation_index: Option, -} - -impl HardwareWalletOptions { - pub fn extract_derivation_path(&self) -> CliTypedResult> { - if let Some(derivation_path) = &self.derivation_path { - Ok(Some(derivation_path.clone())) - } else if let Some(derivation_index) = &self.derivation_index { - let derivation_path = format!("m/44'/637'/{}'/0'/0'", derivation_index); - Ok(Some(derivation_path)) - } else { - Ok(None) - } - } - - pub fn is_hardware_wallet(&self) -> bool { - self.derivation_path.is_some() || self.derivation_index.is_some() - } -} - -#[derive(Debug, Default, Parser)] -pub struct PrivateKeyInputOptions { - /// Signing Ed25519 private key file path - /// - /// Encoded with type from `--encoding` - /// Mutually exclusive with `--private-key` - #[clap(long, group = "private_key_input", value_parser)] - private_key_file: Option, - /// Signing Ed25519 private key - /// - /// Encoded with type from `--encoding` - /// Mutually exclusive with `--private-key-file` - #[clap(long, group = "private_key_input")] - private_key: Option, -} - -impl ParsePrivateKey for PrivateKeyInputOptions {} - -impl PrivateKeyInputOptions { - pub fn from_private_key(private_key: &Ed25519PrivateKey) -> CliTypedResult { - Ok(PrivateKeyInputOptions { - private_key: Some( - private_key - .to_encoded_string() - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ), - private_key_file: None, - }) - } - - pub fn from_x25519_private_key(private_key: &x25519::PrivateKey) -> CliTypedResult { - Ok(PrivateKeyInputOptions { - private_key: Some( - private_key - .to_encoded_string() - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ), - private_key_file: None, - }) - } - - pub fn from_file(file: PathBuf) -> Self { - PrivateKeyInputOptions { - private_key: None, - private_key_file: Some(file), - } - } - - /// Extract public key from CLI args with fallback to config - /// This will first try to extract public key from private_key from CLI args - /// With fallback to profile - /// NOTE: Use this function instead of 'extract_private_key_and_address' if this is HardwareWallet profile - /// HardwareWallet profile does not have private key in config - pub fn extract_public_key_and_address( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - maybe_address: Option, - ) -> CliTypedResult<(Ed25519PublicKey, AccountAddress)> { - // Order of operations - // 1. CLI inputs - // 2. Profile - // 3. Derived - if let Some(private_key) = self.extract_private_key_cli(encoding)? { - // If we use the CLI inputs, then we should derive or use the address from the input - if let Some(address) = maybe_address { - Ok((private_key.public_key(), address)) - } else { - let address = account_address_from_public_key(&private_key.public_key()); - Ok((private_key.public_key(), address)) - } - } else if let Some((Some(public_key), maybe_config_address)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| (p.public_key, p.account)) - { - match (maybe_address, maybe_config_address) { - (Some(address), _) => Ok((public_key, address)), - (_, Some(address)) => Ok((public_key, address)), - (None, None) => { - let address = account_address_from_public_key(&public_key); - Ok((public_key, address)) - }, - } - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'], or ['public_key'] must present in profile".to_string(), - )) - } - } - - /// Extract private key from CLI args with fallback to config - pub fn extract_private_key_and_address( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - maybe_address: Option, - ) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { - // Order of operations - // 1. CLI inputs - // 2. Profile - // 3. Derived - if let Some(key) = self.extract_private_key_cli(encoding)? { - // If we use the CLI inputs, then we should derive or use the address from the input - if let Some(address) = maybe_address { - Ok((key, address)) - } else { - let address = account_address_from_public_key(&key.public_key()); - Ok((key, address)) - } - } else if let Some((Some(key), maybe_config_address)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| (p.private_key, p.account)) - { - match (maybe_address, maybe_config_address) { - (Some(address), _) => Ok((key, address)), - (_, Some(address)) => Ok((key, address)), - (None, None) => { - let address = account_address_from_public_key(&key.public_key()); - Ok((key, address)) - }, - } - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'] must be used".to_string(), - )) - } - } - - /// Extract private key from CLI args with fallback to config - pub fn extract_private_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - if let Some(key) = self.extract_private_key_cli(encoding)? { - Ok(key) - } else if let Some(Some(private_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.private_key) - { - Ok(private_key) - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'] must be used".to_string(), - )) - } - } - - /// Extract private key from CLI args - pub fn extract_private_key_cli( - &self, - encoding: EncodingType, - ) -> CliTypedResult> { - self.parse_private_key( - encoding, - self.private_key_file.clone(), - self.private_key.clone(), - ) - } -} - -// Extract the public key by deriving private key, fall back to public key from profile -// Order of operations -// 1. Get the private key (either from CLI input or profile), and derive the public key from it -// 2. Else get the public key directly from the config profile -// 3. Else error -impl ExtractPublicKey for PrivateKeyInputOptions { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult { - // 1. Get the private key, and derive the public key - let private_key = if let Some(key) = self.extract_private_key_cli(encoding)? { - Some(key) - } else if let Some(Some(private_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.private_key) - { - Some(private_key) - } else { - None - }; - - // 2. Get the public key from the config profile - // 3. Else error - if let Some(key) = private_key { - Ok(key.public_key()) - } else if let Some(Some(public_key)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.public_key) - { - Ok(public_key) - } else { - Err(CliError::CommandArgumentError( - "Unable to extract public key from Private Key input nor Profile".to_string(), - )) - } - } -} - -pub trait ExtractPublicKey { - fn extract_public_key( - &self, - encoding: EncodingType, - profile: &ProfileOptions, - ) -> CliTypedResult; -} - -pub fn account_address_from_public_key(public_key: &Ed25519PublicKey) -> AccountAddress { - let auth_key = AuthenticationKey::ed25519(public_key); - account_address_from_auth_key(&auth_key) -} - -pub fn account_address_from_auth_key(auth_key: &AuthenticationKey) -> AccountAddress { - AccountAddress::new(*auth_key.account_address()) -} - -#[derive(Debug, Parser)] -pub struct SaveFile { - /// Output file path - #[clap(long, value_parser)] - pub output_file: PathBuf, - - #[clap(flatten)] - pub prompt_options: PromptOptions, -} - -impl SaveFile { - /// Check if the `output_file` exists already - pub fn check_file(&self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options) - } - - /// Save to the `output_file` - pub fn save_to_file(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - write_to_file(self.output_file.as_path(), name, bytes) - } - - /// Save to the `output_file` with restricted permissions (mode 0600) - pub fn save_to_file_confidential(&self, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - let mut opts = OpenOptions::new(); - #[cfg(unix)] - opts.mode(0o600); - write_to_file_with_opts(self.output_file.as_path(), name, bytes, &mut opts) - } -} - -/// Options specific to using the Rest endpoint -#[derive(Debug, Default, Parser)] -pub struct RestOptions { - /// URL to a fullnode on the network - /// - /// Defaults to the URL in the `default` profile - #[clap(long)] - pub(crate) url: Option, - - /// Connection timeout in seconds, used for the REST endpoint of the fullnode - #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS, alias = "connection-timeout-s")] - pub connection_timeout_secs: u64, - - /// Key to use for ratelimiting purposes with the node API. This value will be used - /// as `Authorization: Bearer `. You may also set this with the NODE_API_KEY - /// environment variable. - #[clap(long, env)] - pub node_api_key: Option, -} - -impl RestOptions { - pub fn new(url: Option, connection_timeout_secs: Option) -> Self { - RestOptions { - url, - connection_timeout_secs: connection_timeout_secs.unwrap_or(DEFAULT_EXPIRATION_SECS), - node_api_key: None, - } - } - - /// Retrieve the URL from the profile or the command line - pub fn url(&self, profile: &ProfileOptions) -> CliTypedResult { - if let Some(ref url) = self.url { - Ok(url.clone()) - } else if let Some(Some(url)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.rest_url) - { - reqwest::Url::parse(&url) - .map_err(|err| CliError::UnableToParse("Rest URL", err.to_string())) - } else { - Err(CliError::CommandArgumentError("No rest url given. Please add --url or add a rest_url to the .aptos/config.yaml for the current profile".to_string())) - } - } - - pub fn client(&self, profile: &ProfileOptions) -> CliTypedResult { - let mut client = Client::builder(AptosBaseUrl::Custom(self.url(profile)?)) - .timeout(Duration::from_secs(self.connection_timeout_secs)) - .header(aptos_api_types::X_APTOS_CLIENT, X_APTOS_CLIENT_VALUE)?; - if let Some(node_api_key) = &self.node_api_key { - client = client.api_key(node_api_key)?; - } - Ok(client.build()) - } -} - -/// Options for compiling a move package dir -#[derive(Debug, Clone, Parser)] -pub struct MovePackageDir { - /// Enables dev mode, which uses all dev-addresses and dev-dependencies - /// - /// Dev mode allows for changing dependencies and addresses to the preset [dev-addresses] and - /// [dev-dependencies] fields. This works both inside and out of tests for using preset values. - /// - /// Currently, it also additionally pulls in all test compilation artifacts - #[clap(long)] - pub dev: bool, - /// Path to a move package (the folder with a Move.toml file) - #[clap(long, value_parser)] - pub package_dir: Option, - /// Path to save the compiled move package - /// - /// Defaults to `/build` - #[clap(long, value_parser)] - pub output_dir: Option, - /// Named addresses for the move binary - /// - /// Example: alice=0x1234, bob=0x5678 - /// - /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap(long, value_parser = crate::common::utils::parse_map::, default_value = "")] - pub(crate) named_addresses: BTreeMap, - - /// Skip pulling the latest git dependencies - /// - /// If you don't have a network connection, the compiler may fail due - /// to no ability to pull git dependencies. This will allow overriding - /// this for local development. - #[clap(long)] - pub(crate) skip_fetch_latest_git_deps: bool, - - /// Specify the version of the bytecode the compiler is going to emit. - #[clap(long)] - pub bytecode_version: Option, - - /// Specify the version of the compiler. - /// - /// Currently hidden until the official launch of Compiler V2 - #[clap(long, hide = true)] - pub compiler_version: Option, - - /// Do not complain about unknown attributes in Move code. - #[clap(long)] - pub skip_attribute_checks: bool, - - /// Do apply extended checks for Aptos (e.g. `#[view]` attribute) also on test code. - /// NOTE: this behavior will become the default in the future. - /// See https://github.com/aptos-labs/aptos-core/issues/10335 - #[clap(long, env = "APTOS_CHECK_TEST_CODE")] - pub check_test_code: bool, -} - -impl MovePackageDir { - pub fn new(package_dir: PathBuf) -> Self { - Self { - dev: false, - package_dir: Some(package_dir), - output_dir: None, - named_addresses: Default::default(), - skip_fetch_latest_git_deps: true, - bytecode_version: None, - compiler_version: None, - skip_attribute_checks: false, - check_test_code: false, - } - } - - pub fn get_package_path(&self) -> CliTypedResult { - dir_default_to_current(self.package_dir.clone()) - } - - /// Retrieve the NamedAddresses, resolving all the account addresses accordingly - pub fn named_addresses(&self) -> BTreeMap { - self.named_addresses - .clone() - .into_iter() - .map(|(key, value)| (key, value.account_address)) - .collect() - } - - pub fn add_named_address(&mut self, key: String, value: String) { - self.named_addresses - .insert(key, AccountAddressWrapper::from_str(&value).unwrap()); - } -} - -/// A wrapper around `AccountAddress` to be more flexible from strings than AccountAddress -#[derive(Clone, Copy, Debug)] -pub struct AccountAddressWrapper { - pub account_address: AccountAddress, -} - -impl FromStr for AccountAddressWrapper { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(AccountAddressWrapper { - account_address: load_account_arg(s)?, - }) - } -} - -/// Loads an account arg and allows for naming based on profiles -pub fn load_account_arg(str: &str) -> Result { - if let Ok(account_address) = AccountAddress::from_str(str) { - Ok(account_address) - } else if let Some(Some(account_address)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.account) - { - Ok(account_address) - } else if let Some(Some(private_key)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.private_key) - { - let public_key = private_key.public_key(); - Ok(account_address_from_public_key(&public_key)) - } else { - Err(CliError::CommandArgumentError( - "'--account' or '--profile' after using aptos init must be provided".to_string(), - )) - } -} - -/// A wrapper around `AccountAddress` to allow for "_" -#[derive(Clone, Copy, Debug)] -pub struct MoveManifestAccountWrapper { - pub account_address: Option, -} - -impl FromStr for MoveManifestAccountWrapper { - type Err = CliError; - - fn from_str(s: &str) -> Result { - Ok(MoveManifestAccountWrapper { - account_address: load_manifest_account_arg(s)?, - }) - } -} - -/// Loads an account arg and allows for naming based on profiles and "_" -pub fn load_manifest_account_arg(str: &str) -> Result, CliError> { - if str == "_" { - Ok(None) - } else if let Ok(account_address) = AccountAddress::from_str(str) { - Ok(Some(account_address)) - } else if let Some(Some(private_key)) = - CliConfig::load_profile(Some(str), ConfigSearchMode::CurrentDirAndParents)? - .map(|p| p.private_key) - { - let public_key = private_key.public_key(); - Ok(Some(account_address_from_public_key(&public_key))) - } else { - Err(CliError::CommandArgumentError( - "Invalid Move manifest account address".to_string(), - )) - } -} - -/// A common trait for all CLI commands to have consistent outputs -#[async_trait] -pub trait CliCommand: Sized + Send { - /// Returns a name for logging purposes - fn command_name(&self) -> &'static str; - - /// Returns whether the error should be JSONifyed. - fn jsonify_error_output(&self) -> bool { - true - } - - /// Executes the command, returning a command specific type - async fn execute(self) -> CliTypedResult; - - /// Executes the command, and serializes it to the common JSON output type - async fn execute_serialized(self) -> CliResult { - self.execute_serialized_with_logging_level(Level::Warn) - .await - } - - /// Execute the command with customized logging level - async fn execute_serialized_with_logging_level(self, level: Level) -> CliResult { - let command_name = self.command_name(); - start_logger(level); - let start_time = Instant::now(); - let jsonify_error_output = self.jsonify_error_output(); - to_common_result( - command_name, - start_time, - self.execute().await, - jsonify_error_output, - ) - .await - } - - /// Same as execute serialized without setting up logging - async fn execute_serialized_without_logger(self) -> CliResult { - let command_name = self.command_name(); - let start_time = Instant::now(); - let jsonify_error_output = self.jsonify_error_output(); - to_common_result( - command_name, - start_time, - self.execute().await, - jsonify_error_output, - ) - .await - } - - /// Executes the command, and throws away Ok(result) for the string Success - async fn execute_serialized_success(self) -> CliResult { - start_logger(Level::Warn); - let command_name = self.command_name(); - let start_time = Instant::now(); - let jsonify_error_output = self.jsonify_error_output(); - to_common_success_result( - command_name, - start_time, - self.execute().await, - jsonify_error_output, - ) - .await - } -} - -/// A shortened transaction output -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct TransactionSummary { - pub transaction_hash: HashValue, - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_used: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub gas_unit_price: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub pending: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sender: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sequence_number: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub success: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub timestamp_us: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub version: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub vm_status: Option, -} - -impl From for TransactionSummary { - fn from(transaction: Transaction) -> Self { - TransactionSummary::from(&transaction) - } -} -impl From<&Transaction> for TransactionSummary { - fn from(transaction: &Transaction) -> Self { - match transaction { - Transaction::PendingTransaction(txn) => TransactionSummary { - transaction_hash: txn.hash, - pending: Some(true), - sender: Some(*txn.request.sender.inner()), - sequence_number: Some(txn.request.sequence_number.0), - gas_used: None, - gas_unit_price: None, - success: None, - version: None, - vm_status: None, - timestamp_us: None, - }, - Transaction::UserTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - sender: Some(*txn.request.sender.inner()), - gas_used: Some(txn.info.gas_used.0), - gas_unit_price: Some(txn.request.gas_unit_price.0), - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - sequence_number: Some(txn.request.sequence_number.0), - timestamp_us: Some(txn.timestamp.0), - pending: None, - }, - Transaction::GenesisTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - timestamp_us: None, - }, - Transaction::BlockMetadataTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - timestamp_us: Some(txn.timestamp.0), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - }, - Transaction::StateCheckpointTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - success: Some(txn.info.success), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - timestamp_us: Some(txn.timestamp.0), - sender: None, - gas_used: None, - gas_unit_price: None, - pending: None, - sequence_number: None, - }, - Transaction::ValidatorTransaction(txn) => TransactionSummary { - transaction_hash: txn.info.hash, - gas_used: None, - gas_unit_price: None, - pending: None, - sender: None, - sequence_number: None, - success: Some(txn.info.success), - timestamp_us: Some(txn.timestamp.0), - version: Some(txn.info.version.0), - vm_status: Some(txn.info.vm_status.clone()), - }, - } - } -} - -/// A summary of a `WriteSetChange` for easy printing -#[derive(Clone, Debug, Default, Serialize)] -pub struct ChangeSummary { - #[serde(skip_serializing_if = "Option::is_none")] - address: Option, - #[serde(skip_serializing_if = "Option::is_none")] - data: Option, - event: &'static str, - #[serde(skip_serializing_if = "Option::is_none")] - handle: Option, - #[serde(skip_serializing_if = "Option::is_none")] - key: Option, - #[serde(skip_serializing_if = "Option::is_none")] - module: Option, - #[serde(skip_serializing_if = "Option::is_none")] - resource: Option, - #[serde(skip_serializing_if = "Option::is_none")] - value: Option, -} - -#[derive(Debug, Default, Parser)] -pub struct FaucetOptions { - /// URL for the faucet endpoint e.g. `https://faucet.devnet.aptoslabs.com` - #[clap(long)] - faucet_url: Option, - - /// Auth token to bypass faucet ratelimits. You can also set this as an environment - /// variable with FAUCET_AUTH_TOKEN. - #[clap(long, env)] - faucet_auth_token: Option, -} - -impl FaucetOptions { - pub fn new(faucet_url: Option, faucet_auth_token: Option) -> Self { - FaucetOptions { - faucet_url, - faucet_auth_token, - } - } - - fn faucet_url(&self, profile: &ProfileOptions) -> CliTypedResult { - if let Some(ref faucet_url) = self.faucet_url { - Ok(faucet_url.clone()) - } else if let Some(Some(url)) = CliConfig::load_profile( - profile.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|profile| profile.faucet_url) - { - reqwest::Url::parse(&url) - .map_err(|err| CliError::UnableToParse("config faucet_url", err.to_string())) - } else { - Err(CliError::CommandArgumentError("No faucet given. Please add --faucet-url or add a faucet URL to the .aptos/config.yaml for the current profile".to_string())) - } - } - - /// Fund an account with the faucet. - pub async fn fund_account( - &self, - rest_client: Client, - profile: &ProfileOptions, - num_octas: u64, - address: AccountAddress, - ) -> CliTypedResult<()> { - fund_account( - rest_client, - self.faucet_url(profile)?, - self.faucet_auth_token.as_deref(), - address, - num_octas, - ) - .await - } -} - -/// Gas price options for manipulating how to prioritize transactions -#[derive(Debug, Eq, Parser, PartialEq)] -pub struct GasOptions { - /// Gas multiplier per unit of gas - /// - /// The amount of Octas (10^-8 APT) used for a transaction is equal - /// to (gas unit price * gas used). The gas_unit_price can - /// be used as a multiplier for the amount of Octas willing - /// to be paid for a transaction. This will prioritize the - /// transaction with a higher gas unit price. - /// - /// Without a value, it will determine the price based on the current estimated price - #[clap(long)] - pub gas_unit_price: Option, - /// Maximum amount of gas units to be used to send this transaction - /// - /// The maximum amount of gas units willing to pay for the transaction. - /// This is the (max gas in Octas / gas unit price). - /// - /// For example if I wanted to pay a maximum of 100 Octas, I may have the - /// max gas set to 100 if the gas unit price is 1. If I want it to have a - /// gas unit price of 2, the max gas would need to be 50 to still only have - /// a maximum price of 100 Octas. - /// - /// Without a value, it will determine the price based on simulating the current transaction - #[clap(long)] - pub max_gas: Option, - /// Number of seconds to expire the transaction - /// - /// This is the number of seconds from the current local computer time. - #[clap(long, default_value_t = DEFAULT_EXPIRATION_SECS)] - pub expiration_secs: u64, -} - -impl Default for GasOptions { - fn default() -> Self { - GasOptions { - gas_unit_price: None, - max_gas: None, - expiration_secs: DEFAULT_EXPIRATION_SECS, - } - } -} - -#[derive(Debug)] -pub enum AccountType { - Local, - HardwareWallet, -} - -/// Common options for interacting with an account for a validator -#[derive(Debug, Default, Parser)] -pub struct TransactionOptions { - /// Sender account address - /// - /// This allows you to override the account address from the derived account address - /// in the event that the authentication key was rotated or for a resource account - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) sender_account: Option, - - #[clap(flatten)] - pub(crate) private_key_options: PrivateKeyInputOptions, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) gas_options: GasOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - - /// If this option is set, simulate the transaction locally using the debugger and generate - /// flamegraphs that reflect the gas usage. - #[clap(long)] - pub(crate) profile_gas: bool, -} - -impl TransactionOptions { - /// Builds a rest client - fn rest_client(&self) -> CliTypedResult { - self.rest_options.client(&self.profile_options) - } - - pub fn get_transaction_account_type(&self) -> CliTypedResult { - if self.private_key_options.private_key.is_some() - || self.private_key_options.private_key_file.is_some() - { - Ok(AccountType::Local) - } else if let Some(profile) = CliConfig::load_profile( - self.profile_options.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? { - if profile.private_key.is_some() { - Ok(AccountType::Local) - } else { - Ok(AccountType::HardwareWallet) - } - } else { - Err(CliError::CommandArgumentError( - "One of ['--private-key', '--private-key-file'] or profile must be used" - .to_string(), - )) - } - } - - /// Retrieves the private key and the associated address - /// TODO: Cache this information - pub fn get_key_and_address(&self) -> CliTypedResult<(Ed25519PrivateKey, AccountAddress)> { - self.private_key_options.extract_private_key_and_address( - self.encoding_options.encoding, - &self.profile_options, - self.sender_account, - ) - } - - pub fn get_public_key_and_address(&self) -> CliTypedResult<(Ed25519PublicKey, AccountAddress)> { - self.private_key_options.extract_public_key_and_address( - self.encoding_options.encoding, - &self.profile_options, - self.sender_account, - ) - } - - pub fn sender_address(&self) -> CliTypedResult { - Ok(self.get_key_and_address()?.1) - } - - pub fn get_public_key(&self) -> CliTypedResult { - self.private_key_options - .extract_public_key(self.encoding_options.encoding, &self.profile_options) - } - - /// Gets the auth key by account address. We need to fetch the auth key from Rest API rather than creating an - /// auth key out of the public key. - pub(crate) async fn auth_key( - &self, - sender_address: AccountAddress, - ) -> CliTypedResult { - let client = self.rest_client()?; - get_auth_key(&client, sender_address).await - } - - pub async fn sequence_number(&self, sender_address: AccountAddress) -> CliTypedResult { - let client = self.rest_client()?; - get_sequence_number(&client, sender_address).await - } - - pub async fn view(&self, payload: ViewRequest) -> CliTypedResult> { - let client = self.rest_client()?; - Ok(client.view(&payload, None).await?.into_inner()) - } - - /// Submit a transaction - pub async fn submit_transaction( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - let client = self.rest_client()?; - let (sender_public_key, sender_address) = self.get_public_key_and_address()?; - - // Ask to confirm price if the gas unit price is estimated above the lowest value when - // it is automatically estimated - let ask_to_confirm_price; - let gas_unit_price = if let Some(gas_unit_price) = self.gas_options.gas_unit_price { - ask_to_confirm_price = false; - gas_unit_price - } else { - let gas_unit_price = client.estimate_gas_price().await?.into_inner().gas_estimate; - - ask_to_confirm_price = true; - gas_unit_price - }; - - // Get sequence number for account - let (account, state) = get_account_with_state(&client, sender_address).await?; - let sequence_number = account.sequence_number; - - // Retrieve local time, and ensure it's within an expected skew of the blockchain - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|err| CliError::UnexpectedError(err.to_string()))? - .as_secs(); - let now_usecs = now * US_IN_SECS; - - // Warn local user that clock is skewed behind the blockchain. - // There will always be a little lag from real time to blockchain time - if now_usecs < state.timestamp_usecs - ACCEPTED_CLOCK_SKEW_US { - eprintln!("Local clock is is skewed from blockchain clock. Clock is more than {} seconds behind the blockchain {}", ACCEPTED_CLOCK_SKEW_US, state.timestamp_usecs / US_IN_SECS ); - } - let expiration_time_secs = now + self.gas_options.expiration_secs; - - let chain_id = ChainId::new(state.chain_id); - // TODO: Check auth key against current private key and provide a better message - - let max_gas = if let Some(max_gas) = self.gas_options.max_gas { - // If the gas unit price was estimated ask, but otherwise you've chosen hwo much you want to spend - if ask_to_confirm_price { - let message = format!("Do you want to submit transaction for a maximum of {} Octas at a gas unit price of {} Octas?", max_gas * gas_unit_price, gas_unit_price); - prompt_yes_with_override(&message, self.prompt_options)?; - } - max_gas - } else { - let transaction_factory = - TransactionFactory::new(chain_id).with_gas_unit_price(gas_unit_price); - - let unsigned_transaction = transaction_factory - .payload(payload.clone()) - .sender(sender_address) - .sequence_number(sequence_number) - .expiration_timestamp_secs(expiration_time_secs) - .build(); - - let signed_transaction = SignedTransaction::new( - unsigned_transaction, - sender_public_key.clone(), - Ed25519Signature::try_from([0u8; 64].as_ref()).unwrap(), - ); - - let txns = client - .simulate_with_gas_estimation(&signed_transaction, true, false) - .await? - .into_inner(); - let simulated_txn = txns.first().unwrap(); - - // Check if the transaction will pass, if it doesn't then fail - if !simulated_txn.info.success { - return Err(CliError::SimulationError( - simulated_txn.info.vm_status.clone(), - )); - } - - // Take the gas used and use a headroom factor on it - let gas_used = simulated_txn.info.gas_used.0; - let adjusted_max_gas = - adjust_gas_headroom(gas_used, simulated_txn.request.max_gas_amount.0); - - // Ask if you want to accept the estimate amount - let upper_cost_bound = adjusted_max_gas * gas_unit_price; - let lower_cost_bound = gas_used * gas_unit_price; - let message = format!( - "Do you want to submit a transaction for a range of [{} - {}] Octas at a gas unit price of {} Octas?", - lower_cost_bound, - upper_cost_bound, - gas_unit_price); - prompt_yes_with_override(&message, self.prompt_options)?; - adjusted_max_gas - }; - - // Sign and submit transaction - let transaction_factory = TransactionFactory::new(chain_id) - .with_gas_unit_price(gas_unit_price) - .with_max_gas_amount(max_gas) - .with_transaction_expiration_time(self.gas_options.expiration_secs); - - match self.get_transaction_account_type() { - Ok(AccountType::Local) => { - let (private_key, _) = self.get_key_and_address()?; - let sender_account = - &mut LocalAccount::new(sender_address, private_key, sequence_number); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload)); - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) - }, - Ok(AccountType::HardwareWallet) => { - let sender_account = &mut HardwareWalletAccount::new( - sender_address, - sender_public_key, - self.profile_options - .derivation_path() - .expect("derivative path is missing from profile") - .unwrap(), - HardwareWalletType::Ledger, - sequence_number, - ); - let transaction = sender_account - .sign_with_transaction_builder(transaction_factory.payload(payload))?; - let response = client - .submit_and_wait(&transaction) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - - Ok(response.into_inner()) - }, - Err(err) => Err(err), - } - } - - /// Simulate the transaction locally using the debugger, with the gas profiler enabled. - pub async fn profile_gas( - &self, - payload: TransactionPayload, - ) -> CliTypedResult { - println!(); - println!("Simulating transaction locally with the gas profiler..."); - - let client = self.rest_client()?; - - // Fetch the chain states required for the simulation - // TODO(Gas): get the following from the chain - const DEFAULT_GAS_UNIT_PRICE: u64 = 100; - const DEFAULT_MAX_GAS: u64 = 2_000_000; - - let (sender_key, sender_address) = self.get_key_and_address()?; - let gas_unit_price = self - .gas_options - .gas_unit_price - .unwrap_or(DEFAULT_GAS_UNIT_PRICE); - let (account, state) = get_account_with_state(&client, sender_address).await?; - let version = state.version; - let chain_id = ChainId::new(state.chain_id); - let sequence_number = account.sequence_number; - - let balance = client - .get_account_balance_at_version(sender_address, version) - .await - .map_err(|err| CliError::ApiError(err.to_string()))? - .into_inner(); - - let max_gas = self.gas_options.max_gas.unwrap_or_else(|| { - if gas_unit_price == 0 { - DEFAULT_MAX_GAS - } else { - std::cmp::min(balance.coin.value.0 / gas_unit_price, DEFAULT_MAX_GAS) - } - }); - - // Create and sign the transaction - let transaction_factory = TransactionFactory::new(chain_id) - .with_gas_unit_price(gas_unit_price) - .with_max_gas_amount(max_gas) - .with_transaction_expiration_time(self.gas_options.expiration_secs); - let sender_account = &mut LocalAccount::new(sender_address, sender_key, sequence_number); - let transaction = - sender_account.sign_with_transaction_builder(transaction_factory.payload(payload)); - let hash = transaction.clone().committed_hash(); - - // Execute the transaction using the debugger - let debugger = AptosDebugger::rest_client(client).unwrap(); - let res = debugger.execute_transaction_at_version_with_gas_profiler(version, transaction); - let (vm_status, output, gas_log) = res.map_err(|err| { - CliError::UnexpectedError(format!("failed to simulate txn with gas profiler: {}", err)) - })?; - - // Generate a humen-readable name for the report - let entry_point = gas_log.entry_point(); - - let human_readable_name = match entry_point { - FrameName::Script => "script".to_string(), - FrameName::Function { - module_id, name, .. - } => { - let addr_short = module_id.address().short_str_lossless(); - let addr_truncated = if addr_short.len() > 4 { - &addr_short[..4] - } else { - addr_short.as_str() - }; - format!("0x{}-{}-{}", addr_truncated, module_id.name(), name) - }, - }; - let raw_file_name = format!("txn-{}-{}", hash, human_readable_name); - - // Generate the report - let path = Path::new("gas-profiling").join(raw_file_name); - gas_log.generate_html_report(path, format!("Gas Report - {}", human_readable_name))?; - - // Generate the transaction summary - - // TODO(Gas): double check if this is correct. - let success = match output.status() { - TransactionStatus::Keep(exec_status) => Some(exec_status.is_success()), - TransactionStatus::Discard(_) | TransactionStatus::Retry => None, - }; - - Ok(TransactionSummary { - transaction_hash: hash.into(), - gas_used: Some(output.gas_used()), - gas_unit_price: Some(gas_unit_price), - pending: None, - sender: Some(sender_address), - sequence_number: None, // The transaction is not comitted so there is no new sequence number. - success, - timestamp_us: None, - version: Some(version), // The transaction is not comitted so there is no new version. - vm_status: Some(vm_status.to_string()), - }) - } - - pub async fn estimate_gas_price(&self) -> CliTypedResult { - let client = self.rest_client()?; - client - .estimate_gas_price() - .await - .map(|inner| inner.into_inner().gas_estimate) - .map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to retrieve gas price estimate {:?}", - err - )) - }) - } -} - -#[derive(Parser)] -pub struct OptionalPoolAddressArgs { - /// Address of the Staking pool - /// - /// Defaults to the profile's `AccountAddress` - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) pool_address: Option, -} - -#[derive(Parser)] -pub struct PoolAddressArgs { - /// Address of the Staking pool - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) pool_address: AccountAddress, -} - -/// Common options for interactions with a multisig account. -#[derive(Clone, Debug, Parser, Serialize)] -pub struct MultisigAccount { - /// The address of the multisig account to interact with - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) multisig_address: AccountAddress, -} - -#[derive(Clone, Debug, Parser, Serialize)] -pub struct MultisigAccountWithSequenceNumber { - #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - /// Multisig account sequence number to interact with - #[clap(long)] - pub(crate) sequence_number: u64, -} - -#[derive(Debug, Parser)] -pub struct TypeArgVec { - /// TypeTag arguments separated by spaces. - /// - /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` - #[clap(long, num_args = 0..)] - pub(crate) type_args: Vec, -} - -impl TryFrom<&Vec> for TypeArgVec { - type Error = CliError; - - fn try_from(value: &Vec) -> Result { - let mut type_args = vec![]; - for string_ref in value { - type_args.push( - MoveType::from_str(string_ref) - .map_err(|err| CliError::UnableToParse("type argument", err.to_string()))?, - ); - } - Ok(TypeArgVec { type_args }) - } -} - -impl TryInto> for TypeArgVec { - type Error = CliError; - - fn try_into(self) -> Result, Self::Error> { - let mut type_tags: Vec = vec![]; - for type_arg in self.type_args { - type_tags.push( - TypeTag::try_from(type_arg) - .map_err(|err| CliError::UnableToParse("type argument", err.to_string()))?, - ); - } - Ok(type_tags) - } -} - -#[derive(Clone, Debug, Parser)] -pub struct ArgWithTypeVec { - /// Arguments combined with their type separated by spaces. - /// - /// Supported types [address, bool, hex, string, u8, u16, u32, u64, u128, u256, raw] - /// - /// Vectors may be specified using JSON array literal syntax (you may need to escape this with - /// quotes based on your shell interpreter) - /// - /// Example: `address:0x1 bool:true u8:0 u256:1234 "bool:[true, false]" 'address:[["0xace", "0xbee"], []]'` - #[clap(long, num_args = 0..)] - pub(crate) args: Vec, -} - -impl TryFrom<&Vec> for ArgWithTypeVec { - type Error = CliError; - - fn try_from(value: &Vec) -> Result { - let mut args = vec![]; - for arg_json_ref in value { - let function_arg_type = FunctionArgType::from_str(&arg_json_ref.arg_type)?; - args.push(function_arg_type.parse_arg_json(&arg_json_ref.value)?); - } - Ok(ArgWithTypeVec { args }) - } -} - -impl TryInto> for ArgWithTypeVec { - type Error = CliError; - - fn try_into(self) -> Result, Self::Error> { - let mut args = vec![]; - for arg in self.args { - args.push( - (&arg) - .try_into() - .context(format!("Failed to parse arg {:?}", arg)) - .map_err(|err| CliError::CommandArgumentError(err.to_string()))?, - ); - } - Ok(args) - } -} - -impl TryInto>> for ArgWithTypeVec { - type Error = CliError; - - fn try_into(self) -> Result>, Self::Error> { - Ok(self - .args - .into_iter() - .map(|arg_with_type| arg_with_type.arg) - .collect()) - } -} - -impl TryInto> for ArgWithTypeVec { - type Error = CliError; - - fn try_into(self) -> Result, Self::Error> { - let mut args = vec![]; - for arg in self.args { - args.push(arg.to_json()?); - } - Ok(args) - } -} - -/// Common options for constructing an entry function transaction payload. -#[derive(Debug, Parser)] -pub struct EntryFunctionArguments { - /// Function name as `
    ::::` - /// - /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` - #[clap(long, required_unless_present = "json_file")] - pub function_id: Option, - - #[clap(flatten)] - pub(crate) type_arg_vec: TypeArgVec, - #[clap(flatten)] - pub(crate) arg_vec: ArgWithTypeVec, - - /// JSON file specifying public entry function ID, type arguments, and arguments. - #[clap(long, value_parser, conflicts_with_all = &["function_id", "args", "type_args"])] - pub(crate) json_file: Option, -} - -impl EntryFunctionArguments { - /// Get instance as if all fields passed from command line, parsing JSON input file if needed. - fn check_input_style(self) -> CliTypedResult { - if let Some(json_path) = self.json_file { - Ok(parse_json_file::(&json_path)?.try_into()?) - } else { - Ok(self) - } - } -} - -impl TryInto for EntryFunctionArguments { - type Error = CliError; - - fn try_into(self) -> Result { - let entry_function_args = self.check_input_style()?; - let function_id: MemberId = (&entry_function_args).try_into()?; - Ok(EntryFunction::new( - function_id.module_id, - function_id.member_id, - entry_function_args.type_arg_vec.try_into()?, - entry_function_args.arg_vec.try_into()?, - )) - } -} - -impl TryInto for EntryFunctionArguments { - type Error = CliError; - - fn try_into(self) -> Result { - Ok(MultisigTransactionPayload::EntryFunction(self.try_into()?)) - } -} - -impl TryInto for &EntryFunctionArguments { - type Error = CliError; - - fn try_into(self) -> Result { - self.function_id - .clone() - .ok_or(CliError::CommandArgumentError( - "No function ID provided".to_string(), - )) - } -} - -impl TryInto for EntryFunctionArguments { - type Error = CliError; - - fn try_into(self) -> Result { - let entry_function_args = self.check_input_style()?; - let function_id: MemberId = (&entry_function_args).try_into()?; - Ok(ViewRequest { - function: EntryFunctionId { - module: function_id.module_id.into(), - name: function_id.member_id.into(), - }, - type_arguments: entry_function_args.type_arg_vec.type_args, - arguments: entry_function_args.arg_vec.try_into()?, - }) - } -} - -/// Common options for constructing a script payload -#[derive(Debug, Parser)] -pub struct ScriptFunctionArguments { - #[clap(flatten)] - pub(crate) type_arg_vec: TypeArgVec, - #[clap(flatten)] - pub(crate) arg_vec: ArgWithTypeVec, - - /// JSON file specifying type arguments and arguments. - #[clap(long, value_parser, conflicts_with_all = &["args", "type_args"])] - pub(crate) json_file: Option, -} - -impl ScriptFunctionArguments { - /// Get instance as if all fields passed from command line, parsing JSON input file if needed. - fn check_input_style(self) -> CliTypedResult { - if let Some(json_path) = self.json_file { - Ok(parse_json_file::(&json_path)?.try_into()?) - } else { - Ok(self) - } - } - - pub fn create_script_payload(self, bytecode: Vec) -> CliTypedResult { - let script_function_args = self.check_input_style()?; - Ok(TransactionPayload::Script(Script::new( - bytecode, - script_function_args.type_arg_vec.try_into()?, - script_function_args.arg_vec.try_into()?, - ))) - } -} - -#[derive(Deserialize, Serialize)] -/// JSON file format for function arguments. -pub struct ArgWithTypeJSON { - #[serde(rename = "type")] - pub(crate) arg_type: String, - pub(crate) value: serde_json::Value, -} - -#[derive(Deserialize, Serialize)] -/// JSON file format for entry function arguments. -pub struct EntryFunctionArgumentsJSON { - pub(crate) function_id: String, - pub(crate) type_args: Vec, - pub(crate) args: Vec, -} - -impl TryInto for EntryFunctionArgumentsJSON { - type Error = CliError; - - fn try_into(self) -> Result { - Ok(EntryFunctionArguments { - function_id: Some(MemberId::from_str(&self.function_id)?), - type_arg_vec: TypeArgVec::try_from(&self.type_args)?, - arg_vec: ArgWithTypeVec::try_from(&self.args)?, - json_file: None, - }) - } -} - -#[derive(Deserialize)] -/// JSON file format for script function arguments. -struct ScriptFunctionArgumentsJSON { - type_args: Vec, - args: Vec, -} - -impl TryInto for ScriptFunctionArgumentsJSON { - type Error = CliError; - - fn try_into(self) -> Result { - Ok(ScriptFunctionArguments { - type_arg_vec: TypeArgVec::try_from(&self.type_args)?, - arg_vec: ArgWithTypeVec::try_from(&self.args)?, - json_file: None, - }) - } -} - -#[derive(Parser)] -pub struct OverrideSizeCheckOption { - /// Whether to override the check for maximal size of published data - /// - /// This won't bypass on chain checks, so if you are not allowed to go over the size check, it - /// will still be blocked from publishing. - #[clap(long)] - pub(crate) value: bool, -} diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs deleted file mode 100644 index 0eee2c5ea3ad9..0000000000000 --- a/crates/aptos/src/common/utils.rs +++ /dev/null @@ -1,555 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::types::{ - account_address_from_public_key, CliError, CliTypedResult, PromptOptions, - TransactionOptions, TransactionSummary, - }, - config::GlobalConfig, - CliResult, -}; -use aptos_build_info::build_information; -use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; -use aptos_keygen::KeyGen; -use aptos_logger::{debug, Level}; -use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, FaucetClient, State}; -use aptos_telemetry::service::telemetry_is_disabled; -use aptos_types::{ - account_address::create_multisig_account_address, - chain_id::ChainId, - on_chain_config::{FeatureFlag, Features}, - transaction::{authenticator::AuthenticationKey, TransactionPayload}, -}; -use itertools::Itertools; -use move_core_types::{account_address::AccountAddress, language_storage::CORE_CODE_ADDRESS}; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -#[cfg(unix)] -use std::os::unix::fs::OpenOptionsExt; -use std::{ - collections::BTreeMap, - env, - fs::OpenOptions, - io::Write, - path::{Path, PathBuf}, - str::FromStr, - time::{Duration, Instant, SystemTime}, -}; -use tokio::time::timeout; - -/// Prompts for confirmation until a yes or no is given explicitly -pub fn prompt_yes(prompt: &str) -> bool { - let mut result: Result = Err(()); - - // Read input until a yes or a no is given - while result.is_err() { - println!("{} [yes/no] >", prompt); - let mut input = String::new(); - if std::io::stdin().read_line(&mut input).is_err() { - continue; - } - result = match input.trim().to_lowercase().as_str() { - "yes" | "y" => Ok(true), - "no" | "n" => Ok(false), - _ => Err(()), - }; - } - result.unwrap() -} - -/// Convert any successful response to Success. If there is an error, show it as JSON -/// unless `jsonify_error` is false. -pub async fn to_common_success_result( - command: &str, - start_time: Instant, - result: CliTypedResult, - jsonify_error: bool, -) -> CliResult { - to_common_result( - command, - start_time, - result.map(|_| "Success"), - jsonify_error, - ) - .await -} - -/// For pretty printing outputs in JSON. You can opt out of printing the error as -/// JSON by setting `jsonify_error` to false. -pub async fn to_common_result( - command: &str, - start_time: Instant, - result: CliTypedResult, - jsonify_error: bool, -) -> CliResult { - let latency = start_time.elapsed(); - - if !telemetry_is_disabled() { - let error = if let Err(ref error) = result { - // Only print the error type - Some(error.to_str()) - } else { - None - }; - - if let Err(err) = timeout( - Duration::from_millis(2000), - send_telemetry_event(command, latency, error), - ) - .await - { - debug!("send_telemetry_event timeout from CLI: {}", err.to_string()) - } - } - - // Return early with a non JSON error if requested. - if let Err(err) = &result { - if !jsonify_error { - return Err(format!("{:#}", err)); - } - } - - let is_err = result.is_err(); - let result = ResultWrapper::::from(result); - let string = serde_json::to_string_pretty(&result).unwrap(); - if is_err { - Err(string) - } else { - Ok(string) - } -} - -pub fn cli_build_information() -> BTreeMap { - build_information!() -} - -/// Sends a telemetry event about the CLI build, command and result -async fn send_telemetry_event(command: &str, latency: Duration, error: Option<&str>) { - // Collect the build information - let build_information = cli_build_information(); - - // Send the event - aptos_telemetry::cli_metrics::send_cli_telemetry_event( - build_information, - command.into(), - latency, - error.is_none(), - error, - ) - .await; -} - -/// A result wrapper for displaying either a correct execution result or an error. -/// -/// The purpose of this is to have a pretty easy to recognize JSON output format e.g. -/// -/// { -/// "Result":{ -/// "encoded":{ ... } -/// } -/// } -/// -/// { -/// "Error":"Failed to run command" -/// } -/// -#[derive(Debug, Serialize)] -enum ResultWrapper { - Result(T), - Error(String), -} - -impl From> for ResultWrapper { - fn from(result: CliTypedResult) -> Self { - match result { - Ok(inner) => ResultWrapper::Result(inner), - Err(inner) => ResultWrapper::Error(format!("{:#}", inner)), - } - } -} - -/// Checks if a file exists, being overridden by `PromptOptions` -pub fn check_if_file_exists(file: &Path, prompt_options: PromptOptions) -> CliTypedResult<()> { - if file.exists() { - prompt_yes_with_override( - &format!( - "{:?} already exists, are you sure you want to overwrite it?", - file.as_os_str(), - ), - prompt_options, - )? - } - - Ok(()) -} - -pub fn prompt_yes_with_override(prompt: &str, prompt_options: PromptOptions) -> CliTypedResult<()> { - if prompt_options.assume_no { - return Err(CliError::AbortedError); - } else if prompt_options.assume_yes { - return Ok(()); - } - - let is_yes = if let Some(response) = GlobalConfig::load()?.get_default_prompt_response() { - response - } else { - prompt_yes(prompt) - }; - - if is_yes { - Ok(()) - } else { - Err(CliError::AbortedError) - } -} - -pub fn read_from_file(path: &Path) -> CliTypedResult> { - std::fs::read(path) - .map_err(|e| CliError::UnableToReadFile(format!("{}", path.display()), e.to_string())) -} - -/// Write a `&[u8]` to a file -pub fn write_to_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - write_to_file_with_opts(path, name, bytes, &mut OpenOptions::new()) -} - -/// Write a User only read / write file -pub fn write_to_user_only_file(path: &Path, name: &str, bytes: &[u8]) -> CliTypedResult<()> { - let mut opts = OpenOptions::new(); - #[cfg(unix)] - opts.mode(0o600); - write_to_file_with_opts(path, name, bytes, &mut opts) -} - -/// Write a `&[u8]` to a file with the given options -pub fn write_to_file_with_opts( - path: &Path, - name: &str, - bytes: &[u8], - opts: &mut OpenOptions, -) -> CliTypedResult<()> { - let mut file = opts - .write(true) - .create(true) - .truncate(true) - .open(path) - .map_err(|e| CliError::IO(name.to_string(), e))?; - file.write_all(bytes) - .map_err(|e| CliError::IO(name.to_string(), e)) -} - -/// Appends a file extension to a `Path` without overwriting the original extension. -pub fn append_file_extension( - file: &Path, - appended_extension: &'static str, -) -> CliTypedResult { - let extension = file - .extension() - .map(|extension| extension.to_str().unwrap_or_default()); - if let Some(extension) = extension { - Ok(file.with_extension(extension.to_owned() + "." + appended_extension)) - } else { - Ok(file.with_extension(appended_extension)) - } -} - -/// Retrieves account resource from the rest client -pub async fn get_account( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - let account_response = client - .get_account(address) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - Ok(account_response.into_inner()) -} - -/// Retrieves account resource from the rest client -pub async fn get_account_with_state( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult<(Account, State)> { - let account_response = client - .get_account(address) - .await - .map_err(|err| CliError::ApiError(err.to_string()))?; - Ok(account_response.into_parts()) -} - -/// Retrieves sequence number from the rest client -pub async fn get_sequence_number( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - Ok(get_account(client, address).await?.sequence_number) -} - -/// Retrieves the auth key from the rest client -pub async fn get_auth_key( - client: &aptos_rest_client::Client, - address: AccountAddress, -) -> CliTypedResult { - Ok(get_account(client, address).await?.authentication_key) -} - -/// Retrieves the value of the specified feature flag from the rest client -pub async fn get_feature_flag(client: &Client, flag: FeatureFlag) -> CliTypedResult { - let features = client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::features::Features") - .await? - .into_inner(); - Ok(features.is_enabled(flag)) -} - -/// Retrieves the chain id from the rest client -pub async fn chain_id(rest_client: &Client) -> CliTypedResult { - let state = rest_client - .get_ledger_information() - .await - .map_err(|err| CliError::ApiError(err.to_string()))? - .into_inner(); - Ok(ChainId::new(state.chain_id)) -} -/// Error message for parsing a map -const PARSE_MAP_SYNTAX_MSG: &str = "Invalid syntax for map. Example: Name=Value,Name2=Value"; - -/// Parses an inline map of values -/// -/// Example: Name=Value,Name2=Value -pub fn parse_map(str: &str) -> anyhow::Result> -where - K::Err: 'static + std::error::Error + Send + Sync, - V::Err: 'static + std::error::Error + Send + Sync, -{ - let mut map = BTreeMap::new(); - - // Split pairs by commas - for pair in str.split_terminator(',') { - // Split pairs by = then trim off any spacing - let (first, second): (&str, &str) = pair - .split_terminator('=') - .collect_tuple() - .ok_or_else(|| anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG))?; - let first = first.trim(); - let second = second.trim(); - if first.is_empty() || second.is_empty() { - return Err(anyhow::Error::msg(PARSE_MAP_SYNTAX_MSG)); - } - - // At this point, we just give error messages appropriate to parsing - let key: K = K::from_str(first)?; - let value: V = V::from_str(second)?; - map.insert(key, value); - } - Ok(map) -} - -/// Generate a vanity account for Ed25519 single signer scheme, either standard or multisig. -/// -/// The default authentication key for an Ed25519 account is the same as the account address. Hence -/// for a standard account, this function generates Ed25519 private keys until finding one that has -/// an authentication key (account address) that begins with the given vanity prefix. -/// -/// For a multisig account, this function generates private keys until finding one that can create -/// a multisig account with the given vanity prefix as its first transaction (sequence number 0). -/// -/// Note that while a valid hex string must have an even number of characters, a vanity prefix can -/// have an odd number of characters since account addresses are human-readable. -/// -/// `vanity_prefix_ref` is a reference to a hex string vanity prefix, optionally prefixed with "0x". -/// For example "0xaceface" or "d00d". -pub fn generate_vanity_account_ed25519( - vanity_prefix_ref: &str, - multisig: bool, -) -> CliTypedResult { - let vanity_prefix_ref = vanity_prefix_ref - .strip_prefix("0x") - .unwrap_or(vanity_prefix_ref); // Optionally strip leading 0x from input string. - let mut to_check_if_is_hex = String::from(vanity_prefix_ref); - // If an odd number of characters append a 0 for verifying that prefix contains valid hex. - if to_check_if_is_hex.len() % 2 != 0 { - to_check_if_is_hex += "0" - }; - hex::decode(to_check_if_is_hex). // Check that the vanity prefix can be decoded into hex. - map_err(|error| CliError::CommandArgumentError(format!( - "The vanity prefix could not be decoded to hex: {}", error)))?; - let mut key_generator = KeyGen::from_os_rng(); // Get random key generator. - loop { - // Generate new keys until finding a match against the vanity prefix. - let private_key = key_generator.generate_ed25519_private_key(); - let mut account_address = - account_address_from_public_key(&Ed25519PublicKey::from(&private_key)); - if multisig { - account_address = create_multisig_account_address(account_address, 0) - }; - if account_address - .short_str_lossless() - .starts_with(vanity_prefix_ref) - { - return Ok(private_key); - }; - } -} - -pub fn current_dir() -> CliTypedResult { - env::current_dir().map_err(|err| { - CliError::UnexpectedError(format!("Failed to get current directory {}", err)) - }) -} - -pub fn dir_default_to_current(maybe_dir: Option) -> CliTypedResult { - if let Some(dir) = maybe_dir { - Ok(dir) - } else { - current_dir() - } -} - -pub fn create_dir_if_not_exist(dir: &Path) -> CliTypedResult<()> { - // Check if the directory exists, if it's not a dir, it will also fail here - if !dir.exists() || !dir.is_dir() { - std::fs::create_dir_all(dir).map_err(|e| CliError::IO(dir.display().to_string(), e))?; - debug!("Created {} folder", dir.display()); - } else { - debug!("{} folder already exists", dir.display()); - } - Ok(()) -} - -/// Reads a line from input -pub fn read_line(input_name: &'static str) -> CliTypedResult { - let mut input_buf = String::new(); - let _ = std::io::stdin() - .read_line(&mut input_buf) - .map_err(|err| CliError::IO(input_name.to_string(), err))?; - - Ok(input_buf) -} - -/// Lists the content of a directory -pub fn read_dir_files( - path: &Path, - predicate: impl Fn(&Path) -> bool, -) -> CliTypedResult> { - let to_cli_err = |err| CliError::IO(path.display().to_string(), err); - let mut result = vec![]; - for entry in std::fs::read_dir(path).map_err(to_cli_err)? { - let path = entry.map_err(to_cli_err)?.path(); - if predicate(path.as_path()) { - result.push(path) - } - } - Ok(result) -} - -/// Fund account (and possibly create it) from a faucet. This function waits for the -/// transaction on behalf of the caller. -pub async fn fund_account( - rest_client: Client, - faucet_url: Url, - faucet_auth_token: Option<&str>, - address: AccountAddress, - num_octas: u64, -) -> CliTypedResult<()> { - let mut client = FaucetClient::new_from_rest_client(faucet_url, rest_client); - if let Some(token) = faucet_auth_token { - client = client.with_auth_token(token.to_string()); - } - client - .fund(address, num_octas) - .await - .map_err(|err| CliError::ApiError(format!("Faucet issue: {:#}", err))) -} - -/// Wait for transactions, returning an error if any of them fail. -pub async fn wait_for_transactions( - client: &aptos_rest_client::Client, - hashes: Vec, -) -> CliTypedResult<()> { - let sys_time = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map_err(|e| CliError::UnexpectedError(e.to_string()))? - .as_secs() - + 30; - for hash in hashes { - client - .wait_for_transaction_by_hash( - hash.into(), - sys_time, - Some(Duration::from_secs(60)), - None, - ) - .await?; - } - Ok(()) -} - -pub fn start_logger(level: Level) { - let mut logger = aptos_logger::Logger::new(); - logger.channel_size(1000).is_async(false).level(level); - logger.build(); -} - -/// For transaction payload and options, either get gas profile or submit for execution. -pub async fn profile_or_submit( - payload: TransactionPayload, - txn_options_ref: &TransactionOptions, -) -> CliTypedResult { - // Profile gas if needed. - if txn_options_ref.profile_gas { - txn_options_ref.profile_gas(payload).await - } else { - // Otherwise submit the transaction. - txn_options_ref - .submit_transaction(payload) - .await - .map(TransactionSummary::from) - } -} - -/// Try parsing JSON in file at path into a specified type. -pub fn parse_json_file Deserialize<'a>>(path_ref: &Path) -> CliTypedResult { - serde_json::from_slice::(&read_from_file(path_ref)?).map_err(|err| { - CliError::UnableToReadFile(format!("{}", path_ref.display()), err.to_string()) - }) -} - -/// Convert a view function JSON field into a string option. -/// -/// A view function JSON return represents an option via an inner JSON array titled `vec`. -pub fn view_json_option_str(option_ref: &serde_json::Value) -> CliTypedResult> { - if let Some(vec_field) = option_ref.get("vec") { - if let Some(vec_array) = vec_field.as_array() { - if vec_array.is_empty() { - Ok(None) - } else if vec_array.len() > 1 { - Err(CliError::UnexpectedError(format!( - "JSON `vec` array has more than one element: {:?}", - vec_array - ))) - } else { - let option_val_ref = &vec_array[0]; - if let Some(inner_str) = option_val_ref.as_str() { - Ok(Some(inner_str.to_string())) - } else { - Err(CliError::UnexpectedError(format!( - "JSON option is not a string: {}", - option_val_ref - ))) - } - } - } else { - Err(CliError::UnexpectedError(format!( - "JSON `vec` field is not an array: {}", - vec_field - ))) - } - } else { - Err(CliError::UnexpectedError(format!( - "JSON field does not have an inner `vec` field: {}", - option_ref - ))) - } -} diff --git a/crates/aptos/src/config/mod.rs b/crates/aptos/src/config/mod.rs deleted file mode 100644 index 00e8fc401b81a..0000000000000 --- a/crates/aptos/src/config/mod.rs +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - CliCommand, CliConfig, CliError, CliResult, CliTypedResult, ConfigSearchMode, - ProfileSummary, CONFIG_FOLDER, - }, - utils::{create_dir_if_not_exist, current_dir, read_from_file, write_to_user_only_file}, - }, - genesis::git::{from_yaml, to_yaml}, - Tool, -}; -use aptos_cli_common::generate_cli_completions; -use async_trait::async_trait; -use clap::{Parser, ValueEnum}; -use clap_complete::Shell; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Formatter, path::PathBuf, str::FromStr}; - -/// Tool for interacting with configuration of the Aptos CLI tool -/// -/// This tool handles the global configuration of the CLI tool for -/// default configuration, and user specific settings. -#[derive(Parser)] -pub enum ConfigTool { - GenerateShellCompletions(GenerateShellCompletions), - SetGlobalConfig(SetGlobalConfig), - ShowGlobalConfig(ShowGlobalConfig), - ShowProfiles(ShowProfiles), -} - -impl ConfigTool { - pub async fn execute(self) -> CliResult { - match self { - ConfigTool::GenerateShellCompletions(tool) => tool.execute_serialized_success().await, - ConfigTool::SetGlobalConfig(tool) => tool.execute_serialized().await, - ConfigTool::ShowGlobalConfig(tool) => tool.execute_serialized().await, - ConfigTool::ShowProfiles(tool) => tool.execute_serialized().await, - } - } -} - -/// Generate shell completion files -/// -/// First generate the completion file, then follow the shell specific directions on how -/// to install the completion file. -#[derive(Parser)] -pub struct GenerateShellCompletions { - /// Shell to generate completions - #[clap(long, value_enum, ignore_case = true)] - shell: Shell, - - /// File to output shell completions to - #[clap(long, value_parser)] - output_file: PathBuf, -} - -#[async_trait] -impl CliCommand<()> for GenerateShellCompletions { - fn command_name(&self) -> &'static str { - "GenerateShellCompletions" - } - - async fn execute(self) -> CliTypedResult<()> { - generate_cli_completions::("aptos", self.shell, self.output_file.as_path()) - .map_err(|err| CliError::IO(self.output_file.display().to_string(), err)) - } -} - -/// Set global configuration settings -/// -/// Any configuration flags that are not provided will not be changed -#[derive(Parser, Debug)] -pub struct SetGlobalConfig { - /// A configuration for where to place and use the config - /// - /// `Workspace` will put the `.aptos/` folder in the current directory, where - /// `Global` will put the `.aptos/` folder in your home directory - #[clap(long)] - config_type: Option, - /// A configuration for how to expect the prompt response - /// - /// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where - /// "no" runs cli with "--assume-no", default: "prompt" - #[clap(long)] - default_prompt_response: Option, -} - -#[async_trait] -impl CliCommand for SetGlobalConfig { - fn command_name(&self) -> &'static str { - "SetGlobalConfig" - } - - async fn execute(self) -> CliTypedResult { - // Load the global config - let mut config = GlobalConfig::load()?; - - // Enable all features that are actually listed - if let Some(config_type) = self.config_type { - config.config_type = Some(config_type); - } - - if let Some(default_prompt_response) = self.default_prompt_response { - config.default_prompt_response = default_prompt_response; - } - - config.save()?; - config.display() - } -} - -/// Shows the current profiles available -/// -/// This will only show public information and will not show -/// private information -#[derive(Parser, Debug)] -pub struct ShowProfiles { - /// Which profile to show - /// - /// If provided, show only this profile - #[clap(long)] - profile: Option, -} - -#[async_trait] -impl CliCommand> for ShowProfiles { - fn command_name(&self) -> &'static str { - "ShowProfiles" - } - - async fn execute(self) -> CliTypedResult> { - // Load the profile config - let config = CliConfig::load(ConfigSearchMode::CurrentDir)?; - Ok(config - .profiles - .unwrap_or_default() - .into_iter() - .filter(|(key, _)| { - if let Some(ref profile) = self.profile { - profile == key - } else { - true - } - }) - .map(|(key, profile)| (key, ProfileSummary::from(&profile))) - .collect()) - } -} - -/// Shows the properties in the global config -#[derive(Parser, Debug)] -pub struct ShowGlobalConfig {} - -#[async_trait] -impl CliCommand for ShowGlobalConfig { - fn command_name(&self) -> &'static str { - "ShowGlobalConfig" - } - - async fn execute(self) -> CliTypedResult { - // Load the global config - let config = GlobalConfig::load()?; - - config.display() - } -} - -const GLOBAL_CONFIG_FILE: &str = "global_config.yaml"; - -/// A global configuration for global settings related to a user -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct GlobalConfig { - /// Whether to be using Global or Workspace mode - #[serde(skip_serializing_if = "Option::is_none")] - pub config_type: Option, - /// Prompt response type - #[serde(default)] - pub default_prompt_response: PromptResponseType, -} - -impl GlobalConfig { - /// Fill in defaults for display via the CLI - pub fn display(mut self) -> CliTypedResult { - if self.config_type.is_none() { - self.config_type = Some(ConfigType::default()); - } - - Ok(self) - } - - pub fn load() -> CliTypedResult { - let path = global_folder()?.join(GLOBAL_CONFIG_FILE); - if path.exists() { - from_yaml(&String::from_utf8(read_from_file(path.as_path())?)?) - } else { - // If we don't have a config, let's load the default - // Let's create the file if it doesn't exist - let config = GlobalConfig::default(); - config.save()?; - Ok(config) - } - } - - /// Get the config location based on the type - pub fn get_config_location(&self, mode: ConfigSearchMode) -> CliTypedResult { - match self.config_type.unwrap_or_default() { - ConfigType::Global => global_folder(), - ConfigType::Workspace => find_workspace_config(current_dir()?, mode), - } - } - - /// Get the prompt options from global config - pub fn get_default_prompt_response(&self) -> Option { - match self.default_prompt_response { - PromptResponseType::Prompt => None, // prompt - PromptResponseType::Yes => Some(true), // assume_yes - PromptResponseType::No => Some(false), // assume_no - } - } - - fn save(&self) -> CliTypedResult<()> { - let global_folder = global_folder()?; - create_dir_if_not_exist(global_folder.as_path())?; - - write_to_user_only_file( - global_folder.join(GLOBAL_CONFIG_FILE).as_path(), - "Global Config", - &to_yaml(&self)?.into_bytes(), - ) - } -} - -fn global_folder() -> CliTypedResult { - if let Some(dir) = dirs::home_dir() { - Ok(dir.join(CONFIG_FOLDER)) - } else { - Err(CliError::UnexpectedError( - "Unable to retrieve home directory".to_string(), - )) - } -} - -fn find_workspace_config( - starting_path: PathBuf, - mode: ConfigSearchMode, -) -> CliTypedResult { - match mode { - ConfigSearchMode::CurrentDir => Ok(starting_path.join(CONFIG_FOLDER)), - ConfigSearchMode::CurrentDirAndParents => { - let mut current_path = starting_path.clone(); - loop { - current_path.push(CONFIG_FOLDER); - if current_path.is_dir() { - break Ok(current_path); - } else if !(current_path.pop() && current_path.pop()) { - // If we aren't able to find the folder, we'll create a new one right here - break Ok(starting_path.join(CONFIG_FOLDER)); - } - } - }, - } -} - -const GLOBAL: &str = "global"; -const WORKSPACE: &str = "workspace"; - -/// A configuration for where to place and use the config -/// -/// Workspace allows for multiple configs based on location, where -/// Global allows for one config for every part of the code -#[derive(Debug, Copy, Clone, Serialize, Deserialize, ValueEnum)] -pub enum ConfigType { - /// Per system user configuration put in `/.aptos` - Global, - /// Per directory configuration put in `/.aptos` - Workspace, -} - -impl Default for ConfigType { - fn default() -> Self { - // TODO: When we version up, we can change this to global - Self::Workspace - } -} - -impl std::fmt::Display for ConfigType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - ConfigType::Global => GLOBAL, - ConfigType::Workspace => WORKSPACE, - }) - } -} - -impl FromStr for ConfigType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - GLOBAL => Ok(Self::Global), - WORKSPACE => Ok(Self::Workspace), - _ => Err(CliError::CommandArgumentError( - "Invalid config type, must be one of [global, workspace]".to_string(), - )), - } - } -} - -const PROMPT: &str = "prompt"; -const ASSUME_YES: &str = "yes"; -const ASSUME_NO: &str = "no"; - -/// A configuration for how to expect the prompt response -/// -/// Option can be one of ["yes", "no", "prompt"], "yes" runs cli with "--assume-yes", where -/// "no" runs cli with "--assume-no", default: "prompt" -#[derive(Debug, Copy, Clone, Serialize, Deserialize, ValueEnum)] -pub enum PromptResponseType { - /// normal prompt - Prompt, - /// `--assume-yes` - Yes, - /// `--assume-no` - No, -} - -impl Default for PromptResponseType { - fn default() -> Self { - Self::Prompt - } -} - -impl std::fmt::Display for PromptResponseType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - PromptResponseType::Prompt => PROMPT, - PromptResponseType::Yes => ASSUME_YES, - PromptResponseType::No => ASSUME_NO, - }) - } -} - -impl FromStr for PromptResponseType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().trim() { - PROMPT => Ok(Self::Prompt), - ASSUME_YES => Ok(Self::Yes), - ASSUME_NO => Ok(Self::No), - _ => Err(CliError::CommandArgumentError( - "Invalid prompt response type, must be one of [yes, no, prompt]".to_string(), - )), - } - } -} diff --git a/crates/aptos/src/ffi.rs b/crates/aptos/src/ffi.rs deleted file mode 100644 index 41964747f5149..0000000000000 --- a/crates/aptos/src/ffi.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![allow(unsafe_code)] - -use crate::Tool; -use clap::Parser; -use std::{ - ffi::{c_char, CStr, CString}, - thread, -}; -use tokio::runtime::Runtime; - -/// # Safety -/// -/// Run the aptos CLI synchronously -/// Note: This function should only be called from other SDK (i.g Typescript) -/// -/// Return: the pointer to CLIResult c string -#[no_mangle] -pub unsafe extern "C" fn run_aptos_sync(s: *const c_char) -> *const c_char { - let c_str = unsafe { - assert!(!s.is_null()); - CStr::from_ptr(s) - }; - - // split string by spaces - let input_string = c_str.to_str().unwrap().split_whitespace(); - - // Create a new Tokio runtime and block on the execution of `cli.execute()` - let result_string = Runtime::new().unwrap().block_on(async move { - let cli = Tool::parse_from(input_string); - cli.execute().await - }); - - let res_cstr = CString::new(result_string.unwrap()).unwrap(); - - // Return a pointer to the C string - res_cstr.into_raw() -} - -/// # Safety -/// -/// Run the aptos CLI async; Use this function if you are expecting the aptos CLI command -/// to run in the background, or different thread -/// Note: This function should only be called from other SDK (i.g Typescript) -/// -/// Return: the pointer to c string: 'true' -#[no_mangle] -pub unsafe extern "C" fn run_aptos_async(s: *mut c_char) -> *mut c_char { - println!("Running aptos..."); - let c_str = unsafe { - assert!(!s.is_null()); - CStr::from_ptr(s) - }; - - // Spawn a new thread to run the CLI - thread::spawn(move || { - let rt = Runtime::new().unwrap(); - let input_string = c_str.to_str().unwrap().split_whitespace(); - let cli = Tool::parse_from(input_string); - - // Run the CLI once - rt.block_on(async { cli.execute().await }) - .expect("Failed to run CLI"); - }); - - // Return pointer - CString::new("true").unwrap().into_raw() -} - -/// # Safety -/// -/// After running the aptos CLI using FFI. Make sure to invoke this method to free up or -/// deallocate the memory -#[no_mangle] -pub unsafe extern "C" fn free_cstring(s: *mut c_char) { - unsafe { - if s.is_null() { - return; - } - let _ = CString::from_raw(s); - }; -} diff --git a/crates/aptos/src/genesis/git.rs b/crates/aptos/src/genesis/git.rs deleted file mode 100644 index baf4e278c25c3..0000000000000 --- a/crates/aptos/src/genesis/git.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{CliError, CliTypedResult}, - utils::{create_dir_if_not_exist, write_to_file}, - }, - CliCommand, -}; -use aptos_config::config::Token; -use aptos_framework::ReleaseBundle; -use aptos_genesis::config::Layout; -use aptos_github_client::Client as GithubClient; -use async_trait::async_trait; -use clap::Parser; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - fmt::Debug, - io::Read, - path::{Path, PathBuf}, - str::FromStr, -}; - -pub const LAYOUT_FILE: &str = "layout.yaml"; -pub const OPERATOR_FILE: &str = "operator.yaml"; -pub const OWNER_FILE: &str = "owner.yaml"; -pub const FRAMEWORK_NAME: &str = "framework.mrb"; -pub const BALANCES_FILE: &str = "balances.yaml"; -pub const EMPLOYEE_VESTING_ACCOUNTS_FILE: &str = "employee_vesting_accounts.yaml"; - -/// Setup a shared Git repository for Genesis -/// -/// This will setup a folder or an online Github repository to be used -/// for Genesis. If it's the local, it will create the folders but not -/// set up a Git repository. -#[derive(Parser)] -pub struct SetupGit { - #[clap(flatten)] - pub(crate) git_options: GitOptions, - - /// Path to the `Layout` file which defines where all the files are - #[clap(long, value_parser)] - pub(crate) layout_file: PathBuf, -} - -#[async_trait] -impl CliCommand<()> for SetupGit { - fn command_name(&self) -> &'static str { - "SetupGit" - } - - async fn execute(self) -> CliTypedResult<()> { - let layout = Layout::from_disk(&self.layout_file)?; - - // Upload layout file to ensure we can read later - let client = self.git_options.get_client()?; - client.put(Path::new(LAYOUT_FILE), &layout)?; - - Ok(()) - } -} - -#[derive(Clone, Debug, Default)] -pub struct GithubRepo { - owner: String, - repository: String, -} - -impl FromStr for GithubRepo { - type Err = CliError; - - fn from_str(s: &str) -> Result { - let parts: Vec<_> = s.split('/').collect(); - if parts.len() != 2 { - Err(CliError::CommandArgumentError("Invalid repository must be of the form 'owner/repository` e.g. 'aptos-labs/aptos-core'".to_string())) - } else { - Ok(GithubRepo { - owner: parts.first().unwrap().to_string(), - repository: parts.get(1).unwrap().to_string(), - }) - } - } -} - -#[derive(Clone, Default, Parser)] -pub struct GitOptions { - /// Github repository e.g. 'aptos-labs/aptos-core' - /// - /// Mutually exclusive with `--local-repository-dir` - #[clap(long)] - pub(crate) github_repository: Option, - - /// Github repository branch e.g. main - #[clap(long, default_value = "main")] - pub(crate) github_branch: String, - - /// Path to Github API token. Token must have repo:* permissions - #[clap(long, value_parser)] - pub(crate) github_token_file: Option, - - /// Path to local git repository - /// - /// Mutually exclusive with `--github-repository` - #[clap(long, value_parser)] - pub(crate) local_repository_dir: Option, -} - -impl GitOptions { - pub fn get_client(self) -> CliTypedResult { - if self.github_repository.is_none() - && self.github_token_file.is_none() - && self.local_repository_dir.is_some() - { - Ok(Client::local(self.local_repository_dir.unwrap())) - } else if self.github_repository.is_some() - && self.github_token_file.is_some() - && self.local_repository_dir.is_none() - { - Client::github( - self.github_repository.unwrap(), - self.github_branch, - self.github_token_file.unwrap(), - ) - } else { - Err(CliError::CommandArgumentError("Must provide either only --local-repository-dir or both --github-repository and --github-token-path".to_string())) - } - } -} - -/// A client for abstracting away local vs Github storage -/// -/// Note: Writes do not commit locally -pub enum Client { - Local(PathBuf), - Github(GithubClient), -} - -impl Client { - pub fn local(path: PathBuf) -> Client { - Client::Local(path) - } - - pub fn github( - repository: GithubRepo, - branch: String, - token_path: PathBuf, - ) -> CliTypedResult { - let token = Token::FromDisk(token_path).read_token()?; - Ok(Client::Github(GithubClient::new( - repository.owner, - repository.repository, - branch, - token, - ))) - } - - /// Retrieves an object as a YAML encoded file from the appropriate storage - pub fn get(&self, path: &Path) -> CliTypedResult { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(path); - - if !path.exists() { - return Err(CliError::UnableToReadFile( - path.display().to_string(), - "File not found".to_string(), - )); - } - - eprintln!("Reading {}", path.display()); - let mut file = std::fs::File::open(path.as_path()) - .map_err(|e| CliError::IO(path.display().to_string(), e))?; - - let mut contents = String::new(); - file.read_to_string(&mut contents) - .map_err(|e| CliError::IO(path.display().to_string(), e))?; - from_yaml(&contents) - }, - Client::Github(client) => { - from_base64_encoded_yaml(&client.get_file(&path.display().to_string())?) - }, - } - } - - /// Puts an object as a YAML encoded file to the appropriate storage - pub fn put(&self, name: &Path, input: &T) -> CliTypedResult<()> { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(name); - - // Create repository path and any sub-directories - if let Some(dir) = path.parent() { - self.create_dir(dir)?; - } else { - return Err(CliError::UnexpectedError(format!( - "Path should always have a parent {}", - path.display() - ))); - } - write_to_file( - path.as_path(), - &path.display().to_string(), - to_yaml(input)?.as_bytes(), - )?; - }, - Client::Github(client) => { - client.put(&name.display().to_string(), &to_base64_encoded_yaml(input)?)?; - }, - } - - Ok(()) - } - - pub fn create_dir(&self, dir: &Path) -> CliTypedResult<()> { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(dir); - create_dir_if_not_exist(path.as_path())?; - }, - Client::Github(_) => { - // There's no such thing as an empty directory in Git, so do nothing - }, - } - - Ok(()) - } - - /// Retrieve framework release bundle. - pub fn get_framework(&self) -> CliTypedResult { - match self { - Client::Local(local_repository_path) => { - let path = local_repository_path.join(FRAMEWORK_NAME); - if !path.exists() { - return Err(CliError::UnableToReadFile( - path.display().to_string(), - "File not found".to_string(), - )); - } - Ok(ReleaseBundle::read(path)?) - }, - Client::Github(client) => { - let bytes = base64::decode(client.get_file(FRAMEWORK_NAME)?)?; - Ok(bcs::from_bytes::(&bytes)?) - }, - } - } -} - -pub fn to_yaml(input: &T) -> CliTypedResult { - Ok(serde_yaml::to_string(input)?) -} - -pub fn from_yaml(input: &str) -> CliTypedResult { - Ok(serde_yaml::from_str(input)?) -} - -pub fn to_base64_encoded_yaml(input: &T) -> CliTypedResult { - Ok(base64::encode(to_yaml(input)?)) -} - -pub fn from_base64_encoded_yaml(input: &str) -> CliTypedResult { - from_yaml(&String::from_utf8(base64::decode(input)?)?) -} diff --git a/crates/aptos/src/genesis/keys.rs b/crates/aptos/src/genesis/keys.rs deleted file mode 100644 index d4b619f6d04e5..0000000000000 --- a/crates/aptos/src/genesis/keys.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{CliError, CliTypedResult, OptionalPoolAddressArgs, PromptOptions, RngArgs}, - utils::{ - check_if_file_exists, create_dir_if_not_exist, current_dir, dir_default_to_current, - read_from_file, write_to_user_only_file, - }, - }, - genesis::git::{from_yaml, to_yaml, GitOptions, LAYOUT_FILE, OPERATOR_FILE, OWNER_FILE}, - governance::CompileScriptFunction, - CliCommand, -}; -use aptos_genesis::{ - config::{HostAndPort, Layout, OperatorConfiguration, OwnerConfiguration}, - keys::{generate_key_objects, PublicIdentity}, -}; -use aptos_types::{ - account_address::AccountAddress, - transaction::{Script, Transaction, WriteSetPayload}, -}; -use async_trait::async_trait; -use clap::Parser; -use std::path::{Path, PathBuf}; - -const PRIVATE_KEYS_FILE: &str = "private-keys.yaml"; -pub const PUBLIC_KEYS_FILE: &str = "public-keys.yaml"; -const VALIDATOR_FILE: &str = "validator-identity.yaml"; -const VFN_FILE: &str = "validator-full-node-identity.yaml"; - -/// Generate keys for a new validator -/// -/// Generates account key, consensus key, and network key for a validator -/// These keys are used for running a validator or operator in a network -#[derive(Parser)] -pub struct GenerateKeys { - /// Output directory for the key files - #[clap(long, value_parser)] - pub(crate) output_dir: Option, - - #[clap(flatten)] - pub(crate) pool_address_args: OptionalPoolAddressArgs, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - #[clap(flatten)] - pub rng_args: RngArgs, -} - -#[async_trait] -impl CliCommand> for GenerateKeys { - fn command_name(&self) -> &'static str { - "GenerateKeys" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - - let private_keys_file = output_dir.join(PRIVATE_KEYS_FILE); - let public_keys_file = output_dir.join(PUBLIC_KEYS_FILE); - let validator_file = output_dir.join(VALIDATOR_FILE); - let vfn_file = output_dir.join(VFN_FILE); - check_if_file_exists(private_keys_file.as_path(), self.prompt_options)?; - check_if_file_exists(public_keys_file.as_path(), self.prompt_options)?; - check_if_file_exists(validator_file.as_path(), self.prompt_options)?; - check_if_file_exists(vfn_file.as_path(), self.prompt_options)?; - - let mut key_generator = self.rng_args.key_generator()?; - let (mut validator_blob, mut vfn_blob, private_identity, public_identity) = - generate_key_objects(&mut key_generator)?; - - // Allow for the owner to be different than the operator - if let Some(pool_address) = self.pool_address_args.pool_address { - validator_blob.account_address = Some(pool_address); - vfn_blob.account_address = Some(pool_address); - } - - // Create the directory if it doesn't exist - create_dir_if_not_exist(output_dir.as_path())?; - - write_to_user_only_file( - private_keys_file.as_path(), - PRIVATE_KEYS_FILE, - to_yaml(&private_identity)?.as_bytes(), - )?; - write_to_user_only_file( - public_keys_file.as_path(), - PUBLIC_KEYS_FILE, - to_yaml(&public_identity)?.as_bytes(), - )?; - write_to_user_only_file( - validator_file.as_path(), - VALIDATOR_FILE, - to_yaml(&validator_blob)?.as_bytes(), - )?; - write_to_user_only_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?; - Ok(vec![ - public_keys_file, - private_keys_file, - validator_file, - vfn_file, - ]) - } -} - -/// Set validator configuration for a single validator -/// -/// This will set the validator configuration for a single validator in the git repository. -/// It will have to be run for each validator expected at genesis. -#[derive(Parser)] -pub struct SetValidatorConfiguration { - /// Name of the validator - #[clap(long)] - pub(crate) username: String, - - /// Host and port pair for the validator e.g. 127.0.0.1:6180 or aptoslabs.com:6180 - #[clap(long)] - pub(crate) validator_host: HostAndPort, - - /// Host and port pair for the fullnode e.g. 127.0.0.1:6180 or aptoslabs.com:6180 - #[clap(long)] - pub(crate) full_node_host: Option, - - /// Stake amount for stake distribution - #[clap(long, default_value_t = 1)] - pub(crate) stake_amount: u64, - - /// Commission rate to pay operator - /// - /// This is a percentage between 0% and 100% - #[clap(long, default_value_t = 0)] - pub(crate) commission_percentage: u64, - - /// Whether the validator will be joining the genesis validator set - /// - /// If set this validator will already be in the validator set at genesis - #[clap(long)] - pub(crate) join_during_genesis: bool, - - /// Path to private identity generated from GenerateKeys - #[clap(long, value_parser)] - pub(crate) owner_public_identity_file: Option, - - /// Path to operator public identity, defaults to owner identity - #[clap(long, value_parser)] - pub(crate) operator_public_identity_file: Option, - - /// Path to voter public identity, defaults to owner identity - #[clap(long, value_parser)] - pub(crate) voter_public_identity_file: Option, - - #[clap(flatten)] - pub(crate) git_options: GitOptions, -} - -#[async_trait] -impl CliCommand<()> for SetValidatorConfiguration { - fn command_name(&self) -> &'static str { - "SetValidatorConfiguration" - } - - async fn execute(self) -> CliTypedResult<()> { - // Load owner - let owner_keys_file = if let Some(owner_keys_file) = self.owner_public_identity_file { - owner_keys_file - } else { - current_dir()?.join(PUBLIC_KEYS_FILE) - }; - let owner_identity = read_public_identity_file(owner_keys_file.as_path())?; - - // Load voter - let voter_identity = if let Some(voter_keys_file) = self.voter_public_identity_file { - read_public_identity_file(voter_keys_file.as_path())? - } else { - owner_identity.clone() - }; - - // Load operator - let (operator_identity, operator_keys_file) = - if let Some(operator_keys_file) = self.operator_public_identity_file { - ( - read_public_identity_file(operator_keys_file.as_path())?, - operator_keys_file, - ) - } else { - (owner_identity.clone(), owner_keys_file) - }; - - // Extract the possible optional fields - let consensus_public_key = - if let Some(consensus_public_key) = operator_identity.consensus_public_key { - consensus_public_key - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read consensus public key from public identity file {}", - operator_keys_file.display() - ))); - }; - - let validator_network_public_key = if let Some(validator_network_public_key) = - operator_identity.validator_network_public_key - { - validator_network_public_key - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read validator network public key from public identity file {}", - operator_keys_file.display() - ))); - }; - - let consensus_proof_of_possession = if let Some(consensus_proof_of_possession) = - operator_identity.consensus_proof_of_possession - { - consensus_proof_of_possession - } else { - return Err(CliError::CommandArgumentError(format!( - "Failed to read consensus proof of possession from public identity file {}", - operator_keys_file.display() - ))); - }; - - // Only add the public key if there is a full node - let full_node_network_public_key = if self.full_node_host.is_some() { - operator_identity.full_node_network_public_key - } else { - None - }; - - // Build operator configuration file - let operator_config = OperatorConfiguration { - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key.clone(), - consensus_public_key, - consensus_proof_of_possession, - validator_network_public_key, - validator_host: self.validator_host, - full_node_network_public_key, - full_node_host: self.full_node_host, - }; - - let owner_config = OwnerConfiguration { - owner_account_address: owner_identity.account_address.into(), - owner_account_public_key: owner_identity.account_public_key, - voter_account_address: voter_identity.account_address.into(), - voter_account_public_key: voter_identity.account_public_key, - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key, - stake_amount: self.stake_amount, - commission_percentage: self.commission_percentage, - join_during_genesis: self.join_during_genesis, - }; - - let directory = PathBuf::from(&self.username); - let operator_file = directory.join(OPERATOR_FILE); - let owner_file = directory.join(OWNER_FILE); - - let git_client = self.git_options.get_client()?; - git_client.put(operator_file.as_path(), &operator_config)?; - git_client.put(owner_file.as_path(), &owner_config) - } -} - -pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult { - let bytes = read_from_file(public_identity_file)?; - from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?) -} - -/// Generate a Layout template file -/// -/// This will generate a layout template file for genesis with some default values. To start a -/// new chain, these defaults should be carefully thought through and chosen. -#[derive(Parser)] -pub struct GenerateLayoutTemplate { - /// Path of the output layout template - #[clap(long, value_parser, default_value = LAYOUT_FILE)] - pub(crate) output_file: PathBuf, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<()> for GenerateLayoutTemplate { - fn command_name(&self) -> &'static str { - "GenerateLayoutTemplate" - } - - async fn execute(self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; - let layout = Layout::default(); - - write_to_user_only_file( - self.output_file.as_path(), - &self.output_file.display().to_string(), - to_yaml(&layout)?.as_bytes(), - ) - } -} - -/// Generate a WriteSet genesis -/// -/// This will compile a Move script and generate a writeset from that script. -#[derive(Parser)] -pub struct GenerateAdminWriteSet { - /// Path of the output genesis file - #[clap(long, value_parser)] - pub(crate) output_file: PathBuf, - - /// Address of the account which execute this script. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) execute_as: AccountAddress, - - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<()> for GenerateAdminWriteSet { - fn command_name(&self) -> &'static str { - "GenerateAdminWriteSet" - } - - async fn execute(self) -> CliTypedResult<()> { - check_if_file_exists(self.output_file.as_path(), self.prompt_options)?; - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("GenerateAdminWriteSet", self.prompt_options)?; - - let txn = Transaction::GenesisTransaction(WriteSetPayload::Script { - execute_as: self.execute_as, - script: Script::new(bytecode, vec![], vec![]), - }); - - write_to_user_only_file( - self.output_file.as_path(), - &self.output_file.display().to_string(), - &bcs::to_bytes(&txn).map_err(CliError::from)?, - ) - } -} diff --git a/crates/aptos/src/genesis/mod.rs b/crates/aptos/src/genesis/mod.rs deleted file mode 100644 index 1ca3e34297d74..0000000000000 --- a/crates/aptos/src/genesis/mod.rs +++ /dev/null @@ -1,932 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod git; -pub mod keys; -#[cfg(test)] -mod tests; -pub mod tools; - -use crate::{ - common::{ - types::{CliError, CliTypedResult, PromptOptions}, - utils::{check_if_file_exists, dir_default_to_current, write_to_file}, - }, - genesis::git::{ - Client, GitOptions, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE, - OPERATOR_FILE, OWNER_FILE, - }, - CliCommand, CliResult, -}; -use aptos_crypto::{ - bls12381, ed25519::ED25519_PUBLIC_KEY_LENGTH, x25519, ValidCryptoMaterial, - ValidCryptoMaterialStringExt, -}; -use aptos_genesis::{ - builder::GenesisConfiguration, - config::{ - AccountBalanceMap, EmployeePoolMap, HostAndPort, Layout, StringOperatorConfiguration, - StringOwnerConfiguration, ValidatorConfiguration, - }, - mainnet::MainnetGenesisInfo, - GenesisInfo, -}; -use aptos_logger::info; -use aptos_types::{ - account_address::{AccountAddress, AccountAddressWithChecks}, - on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig}, -}; -use aptos_vm_genesis::{default_gas_schedule, AccountBalance, EmployeePool}; -use async_trait::async_trait; -use clap::Parser; -use std::{ - cmp::Ordering, - collections::{BTreeMap, BTreeSet, HashSet}, - path::{Path, PathBuf}, - str::FromStr, -}; - -const WAYPOINT_FILE: &str = "waypoint.txt"; -const GENESIS_FILE: &str = "genesis.blob"; - -/// Tool for setting up an Aptos chain Genesis transaction -/// -/// This tool sets up a space for multiple initial "validator" -/// accounts to build a genesis transaction for a new chain. -#[derive(Parser)] -pub enum GenesisTool { - GenerateAdminWriteSet(keys::GenerateAdminWriteSet), - GenerateGenesis(GenerateGenesis), - GetPoolAddresses(tools::PoolAddresses), - GenerateKeys(keys::GenerateKeys), - GenerateLayoutTemplate(keys::GenerateLayoutTemplate), - SetupGit(git::SetupGit), - SetValidatorConfiguration(keys::SetValidatorConfiguration), -} - -impl GenesisTool { - pub async fn execute(self) -> CliResult { - match self { - GenesisTool::GenerateAdminWriteSet(tool) => tool.execute_serialized_success().await, - GenesisTool::GenerateGenesis(tool) => tool.execute_serialized().await, - GenesisTool::GetPoolAddresses(tool) => tool.execute_serialized().await, - GenesisTool::GenerateKeys(tool) => tool.execute_serialized().await, - GenesisTool::GenerateLayoutTemplate(tool) => tool.execute_serialized_success().await, - GenesisTool::SetupGit(tool) => tool.execute_serialized_success().await, - GenesisTool::SetValidatorConfiguration(tool) => tool.execute_serialized_success().await, - } - } -} - -/// Generate genesis from a git repository -/// -/// This will create a genesis.blob and a waypoint.txt to be used for -/// running a network -#[derive(Parser)] -pub struct GenerateGenesis { - /// Output directory for Genesis file and waypoint - #[clap(long, value_parser)] - output_dir: Option, - /// Whether this is mainnet genesis. - /// - /// Default is false - #[clap(long)] - mainnet: bool, - - #[clap(flatten)] - prompt_options: PromptOptions, - #[clap(flatten)] - git_options: GitOptions, -} - -#[async_trait] -impl CliCommand> for GenerateGenesis { - fn command_name(&self) -> &'static str { - "GenerateGenesis" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let genesis_file = output_dir.join(GENESIS_FILE); - let waypoint_file = output_dir.join(WAYPOINT_FILE); - check_if_file_exists(genesis_file.as_path(), self.prompt_options)?; - check_if_file_exists(waypoint_file.as_path(), self.prompt_options)?; - - // Generate genesis and waypoint files - let (genesis_bytes, waypoint) = if self.mainnet { - let mut mainnet_genesis = fetch_mainnet_genesis_info(self.git_options)?; - let genesis_bytes = bcs::to_bytes(mainnet_genesis.clone().get_genesis()) - .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; - (genesis_bytes, mainnet_genesis.generate_waypoint()?) - } else { - let mut test_genesis = fetch_genesis_info(self.git_options)?; - let genesis_bytes = bcs::to_bytes(test_genesis.clone().get_genesis()) - .map_err(|e| CliError::BCS(GENESIS_FILE, e))?; - (genesis_bytes, test_genesis.generate_waypoint()?) - }; - write_to_file(genesis_file.as_path(), GENESIS_FILE, &genesis_bytes)?; - write_to_file( - waypoint_file.as_path(), - WAYPOINT_FILE, - waypoint.to_string().as_bytes(), - )?; - Ok(vec![genesis_file, waypoint_file]) - } -} - -/// Retrieves all information for mainnet genesis from the Git repository -pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult { - let client = git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - - if layout.root_key.is_some() { - return Err(CliError::UnexpectedError( - "Root key must not be set for mainnet.".to_string(), - )); - } - - let total_supply = layout.total_supply.ok_or_else(|| { - CliError::UnexpectedError("Layout file does not have `total_supply`".to_string()) - })?; - - let account_balance_map: AccountBalanceMap = client.get(Path::new(BALANCES_FILE))?; - let accounts: Vec = account_balance_map.try_into()?; - - // Check that the supply matches the total - let total_balance_supply: u64 = accounts.iter().map(|inner| inner.balance).sum(); - if total_supply != total_balance_supply { - return Err(CliError::UnexpectedError(format!( - "Total supply seen {} doesn't match expected total supply {}", - total_balance_supply, total_supply - ))); - } - - // Check that the user has a reasonable amount of APT, since below the minimum gas amount is - // not useful 1 APT minimally - const MIN_USEFUL_AMOUNT: u64 = 200000000; - let ten_percent_of_total = total_supply / 10; - for account in accounts.iter() { - if account.balance != 0 && account.balance < MIN_USEFUL_AMOUNT { - return Err(CliError::UnexpectedError(format!( - "Account {} has an initial supply below expected amount {} < {}", - account.account_address, account.balance, MIN_USEFUL_AMOUNT - ))); - } else if account.balance > ten_percent_of_total { - return Err(CliError::UnexpectedError(format!( - "Account {} has an more than 10% of the total balance {} > {}", - account.account_address, account.balance, ten_percent_of_total - ))); - } - } - - // Keep track of accounts for later lookup of balances - let initialized_accounts: BTreeMap = accounts - .iter() - .map(|inner| (inner.account_address, inner.balance)) - .collect(); - - let employee_vesting_accounts: EmployeePoolMap = - client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; - - let employee_validators: Vec<_> = employee_vesting_accounts - .inner - .iter() - .map(|inner| inner.validator.clone()) - .collect(); - let employee_vesting_accounts: Vec = employee_vesting_accounts.try_into()?; - let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; - let mut unique_accounts = BTreeSet::new(); - let mut unique_network_keys = HashSet::new(); - let mut unique_consensus_keys = HashSet::new(); - let mut unique_consensus_pop = HashSet::new(); - let mut unique_hosts = HashSet::new(); - - validate_employee_accounts( - &employee_vesting_accounts, - &initialized_accounts, - &mut unique_accounts, - )?; - - let mut seen_owners = BTreeMap::new(); - validate_validators( - &layout, - &employee_validators, - &initialized_accounts, - &mut unique_accounts, - &mut unique_network_keys, - &mut unique_consensus_keys, - &mut unique_consensus_pop, - &mut unique_hosts, - &mut seen_owners, - true, - )?; - validate_validators( - &layout, - &validators, - &initialized_accounts, - &mut unique_accounts, - &mut unique_network_keys, - &mut unique_consensus_keys, - &mut unique_consensus_pop, - &mut unique_hosts, - &mut seen_owners, - false, - )?; - - let framework = client.get_framework()?; - Ok(MainnetGenesisInfo::new( - layout.chain_id, - accounts, - employee_vesting_accounts, - validators, - framework, - &GenesisConfiguration { - allow_new_validators: true, - epoch_duration_secs: layout.epoch_duration_secs, - is_test: false, - min_stake: layout.min_stake, - min_voting_threshold: layout.min_voting_threshold, - max_stake: layout.max_stake, - recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, - required_proposer_stake: layout.required_proposer_stake, - rewards_apy_percentage: layout.rewards_apy_percentage, - voting_duration_secs: layout.voting_duration_secs, - voting_power_increase_limit: layout.voting_power_increase_limit, - employee_vesting_start: layout.employee_vesting_start, - employee_vesting_period_duration: layout.employee_vesting_period_duration, - consensus_config: OnChainConsensusConfig::default_for_genesis(), - execution_config: OnChainExecutionConfig::default_for_genesis(), - gas_schedule: default_gas_schedule(), - initial_features_override: None, - randomness_config_override: None, - jwk_consensus_config_override: None, - }, - )?) -} - -/// Retrieves all information for genesis from the Git repository -pub fn fetch_genesis_info(git_options: GitOptions) -> CliTypedResult { - let client = git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - - if layout.root_key.is_none() { - return Err(CliError::UnexpectedError( - "Layout field root_key was not set. Please provide a hex encoded Ed25519PublicKey." - .to_string(), - )); - } - - let validators = get_validator_configs(&client, &layout, false).map_err(parse_error)?; - let framework = client.get_framework()?; - Ok(GenesisInfo::new( - layout.chain_id, - layout.root_key.unwrap(), - validators, - framework, - &GenesisConfiguration { - allow_new_validators: layout.allow_new_validators, - epoch_duration_secs: layout.epoch_duration_secs, - is_test: layout.is_test, - min_stake: layout.min_stake, - min_voting_threshold: layout.min_voting_threshold, - max_stake: layout.max_stake, - recurring_lockup_duration_secs: layout.recurring_lockup_duration_secs, - required_proposer_stake: layout.required_proposer_stake, - rewards_apy_percentage: layout.rewards_apy_percentage, - voting_duration_secs: layout.voting_duration_secs, - voting_power_increase_limit: layout.voting_power_increase_limit, - employee_vesting_start: layout.employee_vesting_start, - employee_vesting_period_duration: layout.employee_vesting_period_duration, - consensus_config: layout.on_chain_consensus_config, - execution_config: layout.on_chain_execution_config, - gas_schedule: default_gas_schedule(), - initial_features_override: None, - randomness_config_override: None, - jwk_consensus_config_override: None, - }, - )?) -} - -fn parse_error(errors: Vec) -> CliError { - eprintln!( - "Failed to parse genesis inputs:\n{}", - serde_yaml::to_string(&errors).unwrap() - ); - CliError::UnexpectedError("Failed to parse genesis inputs".to_string()) -} - -fn get_validator_configs( - client: &Client, - layout: &Layout, - is_mainnet: bool, -) -> Result, Vec> { - let mut validators = Vec::new(); - let mut errors = Vec::new(); - for user in &layout.users { - match get_config(client, user, is_mainnet) { - Ok(validator) => { - validators.push(validator); - }, - Err(failure) => { - if let CliError::UnexpectedError(failure) = failure { - errors.push(format!("{}: {}", user, failure)); - } else { - errors.push(format!("{}: {:?}", user, failure)); - } - }, - } - } - - if errors.is_empty() { - Ok(validators) - } else { - Err(errors) - } -} - -/// Do proper parsing so more information is known about failures -fn get_config( - client: &Client, - user: &str, - is_mainnet: bool, -) -> CliTypedResult { - // Load a user's configuration files - let dir = PathBuf::from(user); - let owner_file = dir.join(OWNER_FILE); - let owner_file = owner_file.as_path(); - let owner_config = client.get::(owner_file)?; - - // Check and convert fields in owner file - let owner_account_address: AccountAddress = parse_required_option( - &owner_config.owner_account_address, - owner_file, - "owner_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let owner_account_public_key = parse_required_option( - &owner_config.owner_account_public_key, - owner_file, - "owner_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let operator_account_address: AccountAddress = parse_required_option( - &owner_config.operator_account_address, - owner_file, - "operator_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let operator_account_public_key = parse_required_option( - &owner_config.operator_account_public_key, - owner_file, - "operator_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let voter_account_address: AccountAddress = parse_required_option( - &owner_config.voter_account_address, - owner_file, - "voter_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let voter_account_public_key = parse_required_option( - &owner_config.voter_account_public_key, - owner_file, - "voter_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - let stake_amount = parse_required_option( - &owner_config.stake_amount, - owner_file, - "stake_amount", - u64::from_str, - )?; - - // Default to 0 for commission percentage if missing. - let commission_percentage = parse_optional_option( - &owner_config.commission_percentage, - owner_file, - "commission_percentage", - u64::from_str, - )? - .unwrap_or(0); - - // Default to true for whether the validator should be joining during genesis. - let join_during_genesis = parse_optional_option( - &owner_config.join_during_genesis, - owner_file, - "join_during_genesis", - bool::from_str, - )? - .unwrap_or(true); - - // We don't require the operator file if the validator is not joining during genesis. - if is_mainnet && !join_during_genesis { - return Ok(ValidatorConfiguration { - owner_account_address: owner_account_address.into(), - owner_account_public_key, - operator_account_address: operator_account_address.into(), - operator_account_public_key, - voter_account_address: voter_account_address.into(), - voter_account_public_key, - consensus_public_key: None, - proof_of_possession: None, - validator_network_public_key: None, - validator_host: None, - full_node_network_public_key: None, - full_node_host: None, - stake_amount, - commission_percentage, - join_during_genesis, - }); - }; - - let operator_file = dir.join(OPERATOR_FILE); - let operator_file = operator_file.as_path(); - let operator_config = client.get::(operator_file)?; - - // Check and convert fields in operator file - let operator_account_address_from_file: AccountAddress = parse_required_option( - &operator_config.operator_account_address, - operator_file, - "operator_account_address", - AccountAddressWithChecks::from_str, - )? - .into(); - let operator_account_public_key_from_file = parse_required_option( - &operator_config.operator_account_public_key, - operator_file, - "operator_account_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - let consensus_public_key = parse_required_option( - &operator_config.consensus_public_key, - operator_file, - "consensus_public_key", - |str| parse_key(bls12381::PublicKey::LENGTH, str), - )?; - let consensus_proof_of_possession = parse_required_option( - &operator_config.consensus_proof_of_possession, - operator_file, - "consensus_proof_of_possession", - |str| parse_key(bls12381::ProofOfPossession::LENGTH, str), - )?; - let validator_network_public_key = parse_required_option( - &operator_config.validator_network_public_key, - operator_file, - "validator_network_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - let full_node_network_public_key = parse_optional_option( - &operator_config.full_node_network_public_key, - operator_file, - "full_node_network_public_key", - |str| parse_key(ED25519_PUBLIC_KEY_LENGTH, str), - )?; - - // Verify owner & operator agree on operator - if operator_account_address != operator_account_address_from_file { - return Err( - CliError::CommandArgumentError( - format!("Operator account {} in owner file {} does not match operator account {} in operator file {}", - operator_account_address, - owner_file.display(), - operator_account_address_from_file, - operator_file.display() - ))); - } - if operator_account_public_key != operator_account_public_key_from_file { - return Err( - CliError::CommandArgumentError( - format!("Operator public key {} in owner file {} does not match operator public key {} in operator file {}", - operator_account_public_key, - owner_file.display(), - operator_account_public_key_from_file, - operator_file.display() - ))); - } - - // Build Validator configuration - Ok(ValidatorConfiguration { - owner_account_address: owner_account_address.into(), - owner_account_public_key, - operator_account_address: operator_account_address.into(), - operator_account_public_key, - voter_account_address: voter_account_address.into(), - voter_account_public_key, - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(consensus_proof_of_possession), - validator_network_public_key: Some(validator_network_public_key), - validator_host: Some(operator_config.validator_host), - full_node_network_public_key, - full_node_host: operator_config.full_node_host, - stake_amount, - commission_percentage, - join_during_genesis, - }) -} - -// TODO: Move into the Crypto libraries -fn parse_key(num_bytes: usize, str: &str) -> anyhow::Result { - let num_chars: usize = num_bytes * 2; - let mut working = str.trim(); - - // Checks if it has a 0x at the beginning, which is okay - if working.starts_with("0x") { - working = &working[2..]; - } - - match working.len().cmp(&num_chars) { - Ordering::Less => { - anyhow::bail!( - "Key {} is too short {} must be {} hex characters", - str, - working.len(), - num_chars - ) - }, - Ordering::Greater => { - anyhow::bail!( - "Key {} is too long {} must be {} hex characters with or without a 0x in front", - str, - working.len(), - num_chars - ) - }, - Ordering::Equal => {}, - } - - if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { - anyhow::bail!("Key {} contains a non-hex character", str) - } - - Ok(T::from_encoded_string(str.trim())?) -} - -fn parse_required_option Result, T, E: std::fmt::Display>( - option: &Option, - file: &Path, - field_name: &'static str, - parse: F, -) -> Result { - if let Some(ref field) = option { - parse(field).map_err(|err| { - CliError::CommandArgumentError(format!( - "Field {} is invalid in file {}. Err: {}", - field_name, - file.display(), - err - )) - }) - } else { - Err(CliError::CommandArgumentError(format!( - "File {} is missing {}", - file.display(), - field_name - ))) - } -} - -fn parse_optional_option Result, T, E: std::fmt::Display>( - option: &Option, - file: &Path, - field_name: &'static str, - parse: F, -) -> Result, CliError> { - if let Some(ref field) = option { - parse(field) - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Field {} is invalid in file {}. Err: {}", - field_name, - file.display(), - err - )) - }) - .map(Some) - } else { - Ok(None) - } -} - -fn validate_validators( - layout: &Layout, - validators: &[ValidatorConfiguration], - initialized_accounts: &BTreeMap, - unique_accounts: &mut BTreeSet, - unique_network_keys: &mut HashSet, - unique_consensus_keys: &mut HashSet, - unique_consensus_pops: &mut HashSet, - unique_hosts: &mut HashSet, - seen_owners: &mut BTreeMap, - is_pooled_validator: bool, -) -> CliTypedResult<()> { - // check accounts for validators - let mut errors = vec![]; - - for (i, validator) in validators.iter().enumerate() { - let name = if is_pooled_validator { - format!("Employee Pool #{}", i) - } else { - layout.users.get(i).unwrap().to_string() - }; - - if !initialized_accounts.contains_key(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} is is not in the balances.yaml file", - validator.owner_account_address, name - ))); - } - if !initialized_accounts.contains_key(&validator.operator_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Operator {} in validator {} is is not in the balances.yaml file", - validator.operator_account_address, name - ))); - } - if !initialized_accounts.contains_key(&validator.voter_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Voter {} in validator {} is is not in the balances.yaml file", - validator.voter_account_address, name - ))); - } - - let owner_balance = initialized_accounts - .get(&validator.owner_account_address.into()) - .unwrap(); - - if seen_owners.contains_key(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} has been seen before as an owner of validator {}", - validator.owner_account_address, - name, - seen_owners - .get(&validator.owner_account_address.into()) - .unwrap() - ))); - } - seen_owners.insert(validator.owner_account_address.into(), i); - - if unique_accounts.contains(&validator.owner_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Owner '{}' in validator {} has already been seen elsewhere", - validator.owner_account_address, name - ))); - } - unique_accounts.insert(validator.owner_account_address.into()); - - if unique_accounts.contains(&validator.operator_account_address.into()) { - errors.push(CliError::UnexpectedError(format!( - "Operator '{}' in validator {} has already been seen elsewhere", - validator.operator_account_address, name - ))); - } - unique_accounts.insert(validator.operator_account_address.into()); - - // Pooled validators have a combined balance - // TODO: Make this field optional but checked - if !is_pooled_validator && *owner_balance < validator.stake_amount { - errors.push(CliError::UnexpectedError(format!( - "Owner {} in validator {} has less in it's balance {} than the stake amount for the validator {}", - validator.owner_account_address, name, owner_balance, validator.stake_amount - ))); - } - if validator.stake_amount < layout.min_stake { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has stake {} under the min stake {}", - name, validator.stake_amount, layout.min_stake - ))); - } - if validator.stake_amount > layout.max_stake { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has stake {} over the max stake {}", - name, validator.stake_amount, layout.max_stake - ))); - } - - // Ensure that the validator is setup correctly if it's joining in genesis - if validator.join_during_genesis { - if validator.validator_network_public_key.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a validator network public key, though it's joining during genesis", - name - ))); - } - if !unique_network_keys.insert(validator.validator_network_public_key.unwrap()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated validator network key{}", - name, - validator.validator_network_public_key.unwrap() - ))); - } - - if validator.validator_host.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a validator host, though it's joining during genesis", - name - ))); - } - if !unique_hosts.insert(validator.validator_host.as_ref().unwrap().clone()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated validator host {:?}", - name, - validator.validator_host.as_ref().unwrap() - ))); - } - - if validator.consensus_public_key.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a consensus public key, though it's joining during genesis", - name - ))); - } - if !unique_consensus_keys - .insert(validator.consensus_public_key.as_ref().unwrap().clone()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated a consensus public key {}", - name, - validator.consensus_public_key.as_ref().unwrap() - ))); - } - - if validator.proof_of_possession.is_none() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} does not have a consensus proof of possession, though it's joining during genesis", - name - ))); - } - if !unique_consensus_pops - .insert(validator.proof_of_possession.as_ref().unwrap().clone()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated a consensus proof of possessions {}", - name, - validator.proof_of_possession.as_ref().unwrap() - ))); - } - - match ( - validator.full_node_host.as_ref(), - validator.full_node_network_public_key.as_ref(), - ) { - (None, None) => { - info!("Validator {} does not have a full node setup", name); - }, - (Some(_), None) | (None, Some(_)) => { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node host or public key but not both", - name - ))); - }, - (Some(full_node_host), Some(full_node_network_public_key)) => { - // Ensure that the validator and the full node aren't the same - let validator_host = validator.validator_host.as_ref().unwrap(); - let validator_network_public_key = - validator.validator_network_public_key.as_ref().unwrap(); - if validator_host == full_node_host { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator and a full node host that are the same {:?}", - name, - validator_host - ))); - } - if !unique_hosts.insert(validator.full_node_host.as_ref().unwrap().clone()) { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated full node host {:?}", - name, - validator.full_node_host.as_ref().unwrap() - ))); - } - - if validator_network_public_key == full_node_network_public_key { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator and a full node network public key that are the same {}", - name, - validator_network_public_key - ))); - } - if !unique_network_keys.insert(validator.full_node_network_public_key.unwrap()) - { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a repeated full node network key {}", - name, - validator.full_node_network_public_key.unwrap() - ))); - } - }, - } - } else { - if validator.validator_network_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator network public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.validator_host.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a validator host, but it is *NOT* joining during genesis", - name - ))); - } - if validator.consensus_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a consensus public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.proof_of_possession.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a consensus proof of possession, but it is *NOT* joining during genesis", - name - ))); - } - if validator.full_node_network_public_key.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node public key, but it is *NOT* joining during genesis", - name - ))); - } - if validator.full_node_host.is_some() { - errors.push(CliError::UnexpectedError(format!( - "Validator {} has a full node host, but it is *NOT* joining during genesis", - name - ))); - } - } - } - - if errors.is_empty() { - Ok(()) - } else { - eprintln!("{:#?}", errors); - - Err(CliError::UnexpectedError( - "Failed to validate validators".to_string(), - )) - } -} - -fn validate_employee_accounts( - employee_vesting_accounts: &[EmployeePool], - initialized_accounts: &BTreeMap, - unique_accounts: &mut BTreeSet, -) -> CliTypedResult<()> { - // Check accounts for employee accounts - for (i, pool) in employee_vesting_accounts.iter().enumerate() { - let mut total_stake_pool_amount = 0; - for (j, account) in pool.accounts.iter().enumerate() { - if !initialized_accounts.contains_key(account) { - return Err(CliError::UnexpectedError(format!( - "Account #{} '{}' in employee pool #{} is not in the balances.yaml file", - j, account, i - ))); - } - if unique_accounts.contains(account) { - return Err(CliError::UnexpectedError(format!( - "Account #{} '{}' in employee pool #{} has already been seen elsewhere", - j, account, i - ))); - } - unique_accounts.insert(*account); - - total_stake_pool_amount += initialized_accounts.get(account).unwrap(); - } - - if total_stake_pool_amount != pool.validator.validator.stake_amount { - return Err(CliError::UnexpectedError(format!( - "Stake amount {} in employee pool #{} does not match combined of accounts {}", - pool.validator.validator.stake_amount, i, total_stake_pool_amount - ))); - } - - if !initialized_accounts.contains_key(&pool.validator.validator.owner_address) { - return Err(CliError::UnexpectedError(format!( - "Owner address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.owner_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.validator.validator.operator_address) { - return Err(CliError::UnexpectedError(format!( - "Operator address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.operator_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.validator.validator.voter_address) { - return Err(CliError::UnexpectedError(format!( - "Voter address {} in employee pool #{} is is not in the balances.yaml file", - pool.validator.validator.voter_address, i - ))); - } - if !initialized_accounts.contains_key(&pool.beneficiary_resetter) { - return Err(CliError::UnexpectedError(format!( - "Beneficiary resetter {} in employee pool #{} is is not in the balances.yaml file", - pool.beneficiary_resetter, i - ))); - } - } - Ok(()) -} diff --git a/crates/aptos/src/genesis/tests.rs b/crates/aptos/src/genesis/tests.rs deleted file mode 100644 index bb270e6ef8527..0000000000000 --- a/crates/aptos/src/genesis/tests.rs +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{OptionalPoolAddressArgs, PromptOptions, RngArgs}, - utils::{read_from_file, write_to_file}, - }, - genesis::{ - git::{ - from_yaml, GitOptions, SetupGit, BALANCES_FILE, EMPLOYEE_VESTING_ACCOUNTS_FILE, - FRAMEWORK_NAME, - }, - keys::{GenerateKeys, GenerateLayoutTemplate, SetValidatorConfiguration, PUBLIC_KEYS_FILE}, - GenerateGenesis, - }, - CliCommand, -}; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - PrivateKey, -}; -use aptos_genesis::{ - config::{ - AccountBalanceMap, EmployeePoolConfig, EmployeePoolMap, HostAndPort, Layout, - ValidatorConfiguration, - }, - keys::PublicIdentity, -}; -use aptos_keygen::KeyGen; -use aptos_temppath::TempPath; -use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; -use aptos_vm_genesis::{AccountBalance, TestValidator}; -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - str::FromStr, -}; - -const INITIAL_BALANCE: u64 = 100_000_000_000_000; - -/// Test the E2E genesis flow since it doesn't require a node to run -#[tokio::test] -async fn test_genesis_e2e_flow() { - let is_mainnet = false; - let dir = TempPath::new(); - dir.create_as_dir().unwrap(); - let git_options = create_users(2, 0, &dir, &mut vec![], is_mainnet).await; - - // Now generate genesis - let output_dir = TempPath::new(); - output_dir.create_as_dir().unwrap(); - let output_dir = PathBuf::from(output_dir.path()); - generate_genesis(git_options, output_dir.clone(), is_mainnet).await; - - // TODO: Verify that these are good - let waypoint_file = output_dir.join("waypoint.txt"); - assert!(waypoint_file.exists()); - let genesis_file = output_dir.join("genesis.blob"); - assert!(genesis_file.exists()); -} - -#[tokio::test] -async fn test_mainnet_genesis_e2e_flow() { - let is_mainnet = true; - let dir = TempPath::new(); - dir.create_as_dir().unwrap(); - let git_options = create_users(2, 4, &dir, &mut vec![10, 1], is_mainnet).await; - let account_1 = AccountAddress::from_hex_literal("0x101").unwrap(); - let account_2 = AccountAddress::from_hex_literal("0x102").unwrap(); - let employee_1 = AccountAddress::from_hex_literal("0x201").unwrap(); - let employee_2 = AccountAddress::from_hex_literal("0x202").unwrap(); - let employee_3 = AccountAddress::from_hex_literal("0x203").unwrap(); - let employee_4 = AccountAddress::from_hex_literal("0x204").unwrap(); - - let owner_identity1 = load_identity(dir.path(), "owner-0"); - let owner_identity2 = load_identity(dir.path(), "owner-1"); - let operator_identity1 = load_identity(dir.path(), "operator-0"); - let operator_identity2 = load_identity(dir.path(), "operator-1"); - let voter_identity1 = load_identity(dir.path(), "voter-0"); - let voter_identity2 = load_identity(dir.path(), "voter-1"); - let admin_identity1 = load_identity(dir.path(), "other-0"); - let admin_identity2 = load_identity(dir.path(), "other-1"); - let employee_operator_identity1 = load_identity(dir.path(), "other-2"); - let employee_operator_identity2 = load_identity(dir.path(), "other-3"); - - // Create initial balances and employee vesting account files. - let git_dir = git_options.local_repository_dir.as_ref().unwrap().as_path(); - - create_account_balances_file(PathBuf::from(git_dir), vec![ - owner_identity1.account_address, - owner_identity2.account_address, - operator_identity1.account_address, - operator_identity2.account_address, - voter_identity1.account_address, - voter_identity2.account_address, - account_1, - account_2, - employee_1, - employee_2, - employee_3, - employee_4, - admin_identity1.account_address, - admin_identity2.account_address, - employee_operator_identity1.account_address, - employee_operator_identity2.account_address, - ]) - .await; - create_employee_vesting_accounts_file( - PathBuf::from(git_dir), - &[admin_identity1, admin_identity2], - &[employee_operator_identity1, employee_operator_identity2], - &[vec![employee_1, employee_2], vec![employee_3, employee_4]], - &[true, false], - ) - .await; - - // Now generate genesis - let output_dir = TempPath::new(); - output_dir.create_as_dir().unwrap(); - let output_dir = PathBuf::from(output_dir.path()); - generate_genesis(git_options, output_dir.clone(), is_mainnet).await; - - // TODO: Verify that these are good - let waypoint_file = output_dir.join("waypoint.txt"); - assert!(waypoint_file.exists()); - let genesis_file = output_dir.join("genesis.blob"); - assert!(genesis_file.exists()); -} - -pub fn load_identity(base_dir: &Path, name: &str) -> PublicIdentity { - let path = base_dir.join(name).join(PUBLIC_KEYS_FILE); - from_yaml(&String::from_utf8(read_from_file(path.as_path()).unwrap()).unwrap()).unwrap() -} - -async fn create_users( - num_validators: u8, - num_other_users: u8, - dir: &TempPath, - commission_rates: &mut Vec, - is_mainnet: bool, -) -> GitOptions { - let mut users: HashMap = HashMap::new(); - for i in 0..num_validators { - let name = format!("owner-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - - let name = format!("operator-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - - let name = format!("voter-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - } - for i in 0..num_other_users { - let name = format!("other-{}", i); - let output_dir = generate_keys(dir.path(), &name).await; - users.insert(name, output_dir); - } - - // Get the validator's names - let validator_names = users - .keys() - .map(|key| key.to_string()) - .filter(|name| name.starts_with("owner")) - .collect(); - let mut key_gen = KeyGen::from_seed([num_validators.saturating_add(1); 32]); - - // First step is setup the local git repo - let root_private_key = if !is_mainnet { - Some(key_gen.generate_ed25519_private_key()) - } else { - None - }; - let git_options = - setup_git_dir(root_private_key.as_ref(), validator_names, ChainId::test()).await; - - // Only write validators to folders - for i in 0..num_validators { - let owner_name = format!("owner-{}", i); - let owner_identity = users.get(&owner_name).unwrap().join(PUBLIC_KEYS_FILE); - let operator_identity = users - .get(&format!("operator-{}", i)) - .unwrap() - .join(PUBLIC_KEYS_FILE); - let voter_identity = users - .get(&format!("voter-{}", i)) - .unwrap() - .join(PUBLIC_KEYS_FILE); - let commission_rate = if commission_rates.is_empty() { - 0 - } else { - commission_rates.remove(0) - }; - set_validator_config( - owner_name, - git_options.clone(), - owner_identity.as_path(), - operator_identity.as_path(), - voter_identity.as_path(), - commission_rate, - i as u16, - ) - .await; - } - git_options -} - -/// Generate genesis and waypoint -async fn generate_genesis(git_options: GitOptions, output_dir: PathBuf, mainnet: bool) { - let command = GenerateGenesis { - prompt_options: PromptOptions::yes(), - git_options, - output_dir: Some(output_dir), - mainnet, - }; - let _ = command.execute().await.unwrap(); -} - -/// Setup a temporary repo location and add all required pieces -async fn setup_git_dir( - root_private_key: Option<&Ed25519PrivateKey>, - users: Vec, - chain_id: ChainId, -) -> GitOptions { - let git_options = git_options(); - let layout_file = TempPath::new(); - layout_file.create_as_file().unwrap(); - let layout_file = layout_file.path(); - - create_layout_file( - layout_file, - root_private_key.map(|inner| inner.public_key()), - users, - chain_id, - ) - .await; - let setup_command = SetupGit { - git_options: git_options.clone(), - layout_file: PathBuf::from(layout_file), - }; - - setup_command - .execute() - .await - .expect("Should not fail creating repo folder"); - - // Add framework - add_framework_to_dir(git_options.local_repository_dir.as_ref().unwrap().as_path()); - git_options -} - -/// Add framework to git directory -fn add_framework_to_dir(git_dir: &Path) { - aptos_cached_packages::head_release_bundle() - .write(git_dir.join(FRAMEWORK_NAME)) - .unwrap() -} - -/// Local git options for testing -fn git_options() -> GitOptions { - let temp_path = TempPath::new(); - let path = PathBuf::from(temp_path.path()); - GitOptions { - local_repository_dir: Some(path), - ..Default::default() - } -} - -/// Create a layout file for the repo -async fn create_layout_file( - file: &Path, - root_public_key: Option, - users: Vec, - chain_id: ChainId, -) { - GenerateLayoutTemplate { - output_file: PathBuf::from(file), - prompt_options: PromptOptions::yes(), - } - .execute() - .await - .expect("Expected to create layout template"); - - // Update layout file - let mut layout: Layout = - from_yaml(&String::from_utf8(read_from_file(file).unwrap()).unwrap()).unwrap(); - layout.root_key = root_public_key; - layout.users = users; - layout.chain_id = chain_id; - layout.is_test = true; - layout.total_supply = Some(INITIAL_BALANCE * 16); - - write_to_file( - file, - "Layout file", - serde_yaml::to_string(&layout).unwrap().as_bytes(), - ) - .unwrap(); -} - -/// Generate keys for a "user" -async fn generate_keys(dir: &Path, name: &str) -> PathBuf { - let output_dir = dir.join(name); - let command = GenerateKeys { - pool_address_args: OptionalPoolAddressArgs { pool_address: None }, - rng_args: RngArgs::from_string_seed(name), - prompt_options: PromptOptions::yes(), - output_dir: Some(output_dir.clone()), - }; - let _ = command.execute().await.unwrap(); - output_dir -} - -/// Set validator configuration for a user -async fn set_validator_config( - username: String, - git_options: GitOptions, - owner_identity_file: &Path, - operator_identity_file: &Path, - voter_identity_file: &Path, - commission_percentage: u64, - port: u16, -) { - let command = SetValidatorConfiguration { - username, - git_options, - owner_public_identity_file: Some(owner_identity_file.to_path_buf()), - validator_host: HostAndPort::from_str(&format!("localhost:{}", port)).unwrap(), - stake_amount: 100_000_000_000_000, - full_node_host: None, - operator_public_identity_file: Some(operator_identity_file.to_path_buf()), - voter_public_identity_file: Some(voter_identity_file.to_path_buf()), - commission_percentage, - join_during_genesis: true, - }; - - command.execute().await.unwrap() -} - -async fn create_account_balances_file(path: PathBuf, addresses: Vec) { - let account_balances: Vec = addresses - .iter() - .map(|account_address| AccountBalance { - account_address: *account_address, - balance: INITIAL_BALANCE, - }) - .collect(); - - let balance_map = AccountBalanceMap::try_from(account_balances).unwrap(); - - write_to_file( - &path.join(BALANCES_FILE), - BALANCES_FILE, - serde_yaml::to_string(&balance_map).unwrap().as_bytes(), - ) - .unwrap(); -} - -async fn create_employee_vesting_accounts_file( - path: PathBuf, - admin_identities: &[PublicIdentity], - operator_identities: &[PublicIdentity], - employee_groups: &[Vec], - join_during_genesis: &[bool], -) { - TestValidator::new_test_set(Some(employee_groups.len()), Some(INITIAL_BALANCE)); - let employee_vesting_accounts: Vec<_> = employee_groups - .iter() - .enumerate() - .map(|(index, accounts)| { - let admin_identity = admin_identities[index].clone(); - let operator_identity = operator_identities[index].clone(); - let validator_config = if *join_during_genesis.get(index).unwrap() { - ValidatorConfiguration { - owner_account_address: admin_identity.account_address.into(), - owner_account_public_key: admin_identity.account_public_key.clone(), - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key.clone(), - voter_account_address: admin_identity.account_address.into(), - voter_account_public_key: admin_identity.account_public_key, - consensus_public_key: operator_identity.consensus_public_key, - proof_of_possession: operator_identity.consensus_proof_of_possession, - validator_network_public_key: operator_identity.validator_network_public_key, - validator_host: Some(HostAndPort::from_str("localhost:8080").unwrap()), - full_node_network_public_key: operator_identity.full_node_network_public_key, - full_node_host: Some(HostAndPort::from_str("localhost:8081").unwrap()), - stake_amount: 2 * INITIAL_BALANCE, - commission_percentage: 0, - join_during_genesis: true, - } - } else { - ValidatorConfiguration { - owner_account_address: admin_identity.account_address.into(), - owner_account_public_key: admin_identity.account_public_key.clone(), - operator_account_address: operator_identity.account_address.into(), - operator_account_public_key: operator_identity.account_public_key, - voter_account_address: admin_identity.account_address.into(), - voter_account_public_key: admin_identity.account_public_key, - consensus_public_key: None, - proof_of_possession: None, - validator_network_public_key: None, - validator_host: None, - full_node_network_public_key: None, - full_node_host: None, - stake_amount: 2 * INITIAL_BALANCE, - commission_percentage: 0, - join_during_genesis: false, - } - }; - - EmployeePoolConfig { - accounts: accounts.iter().map(|addr| addr.into()).collect(), - validator: validator_config, - vesting_schedule_numerators: vec![3, 3, 3, 3, 1], - vesting_schedule_denominator: 48, - beneficiary_resetter: AccountAddress::from_hex_literal("0x101").unwrap().into(), - } - }) - .collect(); - let employee_vesting_map = EmployeePoolMap { - inner: employee_vesting_accounts, - }; - write_to_file( - &path.join(EMPLOYEE_VESTING_ACCOUNTS_FILE), - EMPLOYEE_VESTING_ACCOUNTS_FILE, - serde_yaml::to_string(&employee_vesting_map) - .unwrap() - .as_bytes(), - ) - .unwrap(); -} diff --git a/crates/aptos/src/genesis/tools.rs b/crates/aptos/src/genesis/tools.rs deleted file mode 100644 index d9d00125cca11..0000000000000 --- a/crates/aptos/src/genesis/tools.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::PromptOptions, - utils::{dir_default_to_current, write_to_file}, - }, - genesis::{ - get_validator_configs, - git::{GitOptions, EMPLOYEE_VESTING_ACCOUNTS_FILE, LAYOUT_FILE}, - parse_error, - }, - CliCommand, CliTypedResult, -}; -use aptos_genesis::config::{EmployeePoolMap, Layout}; -use aptos_sdk::move_types::account_address::AccountAddress; -use aptos_types::account_address::{create_vesting_pool_address, default_stake_pool_address}; -use async_trait::async_trait; -use clap::Parser; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, -}; - -const POOL_ADDRESSES: &str = "pool-addresses.yaml"; -const EMPLOYEE_POOL_ADDRESSES: &str = "employee-pool-addresses.yaml"; - -/// Get pool addresses from a mainnet genesis setup -/// -/// Outputs all pool addresses to a file from the genesis files -#[derive(Parser)] -pub struct PoolAddresses { - /// Output directory for pool addresses - #[clap(long, value_parser)] - output_dir: Option, - - #[clap(flatten)] - prompt_options: PromptOptions, - #[clap(flatten)] - git_options: GitOptions, -} - -#[async_trait] -impl CliCommand> for PoolAddresses { - fn command_name(&self) -> &'static str { - "GetPoolAddresses" - } - - async fn execute(self) -> CliTypedResult> { - let output_dir = dir_default_to_current(self.output_dir.clone())?; - let client = self.git_options.get_client()?; - let layout: Layout = client.get(Path::new(LAYOUT_FILE))?; - let employee_vesting_accounts: EmployeePoolMap = - client.get(Path::new(EMPLOYEE_VESTING_ACCOUNTS_FILE))?; - let validators = get_validator_configs(&client, &layout, true).map_err(parse_error)?; - - let mut address_to_pool = BTreeMap::::new(); - - for validator in validators { - let stake_pool_address = default_stake_pool_address( - validator.owner_account_address.into(), - validator.operator_account_address.into(), - ); - address_to_pool.insert(validator.owner_account_address.into(), stake_pool_address); - } - - let mut employee_address_to_pool = BTreeMap::::new(); - - for employee_pool in employee_vesting_accounts.inner.iter() { - let stake_pool_address = create_vesting_pool_address( - employee_pool.validator.owner_account_address.into(), - employee_pool.validator.operator_account_address.into(), - 0, - &[], - ); - - employee_address_to_pool.insert( - employee_pool.validator.owner_account_address.into(), - stake_pool_address, - ); - } - - let pool_addresses_file = output_dir.join(POOL_ADDRESSES); - let employee_pool_addresses_file = output_dir.join(EMPLOYEE_POOL_ADDRESSES); - - write_to_file( - pool_addresses_file.as_path(), - POOL_ADDRESSES, - serde_yaml::to_string(&address_to_pool)?.as_bytes(), - )?; - write_to_file( - employee_pool_addresses_file.as_path(), - EMPLOYEE_POOL_ADDRESSES, - serde_yaml::to_string(&employee_address_to_pool)?.as_bytes(), - )?; - - Ok(vec![pool_addresses_file, employee_pool_addresses_file]) - } -} diff --git a/crates/aptos/src/governance/delegation_pool.rs b/crates/aptos/src/governance/delegation_pool.rs deleted file mode 100644 index a2263a941043e..0000000000000 --- a/crates/aptos/src/governance/delegation_pool.rs +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::governance::{utils::*, *}; -use clap::Subcommand; - -/// Tool for on-chain governance from delegation pools -/// -/// This tool allows voters that have stake in a delegation pool to submit proposals or vote on -/// a proposal. -#[derive(Subcommand)] -pub enum DelegationPoolTool { - Propose(SubmitProposal), - Vote(SubmitVote), -} - -impl DelegationPoolTool { - pub async fn execute(self) -> CliResult { - use DelegationPoolTool::*; - match self { - Propose(tool) => tool.execute_serialized().await, - Vote(tool) => tool.execute_serialized().await, - } - } -} - -/// Submit a governance proposal -/// -/// You can only submit a proposal when the remaining lockup period of this delegation pool is -/// longer than a proposal duration and you have enough voting power to meet the minimum proposing -/// threshold. If you are voting with a delegation pool which hasn't enabled partial governance -/// voting yet, this command will enable it for you. -#[derive(Parser)] -pub struct SubmitProposal { - /// The address of the delegation pool to propose. - #[clap(long)] - delegation_pool_address: AccountAddress, - #[clap(flatten)] - pub(crate) args: SubmitProposalArgs, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ProposalSubmissionSummary { - proposal_id: Option, - txn_summaries: Vec, -} - -#[async_trait] -impl CliCommand for SubmitProposal { - fn command_name(&self) -> &'static str { - "SubmitDelegationPoolProposal" - } - - async fn execute(mut self) -> CliTypedResult { - let mut summaries = vec![]; - if let Some(txn_summary) = delegation_pool_governance_precheck( - &self.args.txn_options, - self.delegation_pool_address, - ) - .await? - { - summaries.push(txn_summary); - }; - // Validate the proposal metadata - let (script_hash, metadata_hash) = self.args.compile_proposals().await?; - prompt_yes_with_override( - "Do you want to submit this proposal?", - self.args.txn_options.prompt_options, - )?; - - let txn: Transaction = self - .args - .txn_options - .submit_transaction(aptos_stdlib::delegation_pool_create_proposal( - self.delegation_pool_address, - script_hash.to_vec(), - self.args.metadata_url.to_string().as_bytes().to_vec(), - metadata_hash.to_hex().as_bytes().to_vec(), - self.args.is_multi_step, - )) - .await?; - let proposal_id = extract_proposal_id(&txn)?; - summaries.push(TransactionSummary::from(&txn)); - Ok(ProposalSubmissionSummary { - proposal_id, - txn_summaries: summaries, - }) - } -} - -/// Submit a vote on a proposal -/// -/// Votes can only be given on proposals that are currently open for voting. You can vote -/// with `--yes` for a yes vote, and `--no` for a no vote. If you are voting with a delegation pool -/// which hasn't enabled partial governance voting yet, this command will enable it for you. -#[derive(Parser)] -pub struct SubmitVote { - /// The address of the delegation pool to vote. - #[clap(long)] - delegation_pool_address: AccountAddress, - - #[clap(flatten)] - pub(crate) args: SubmitVoteArgs, -} - -#[async_trait] -impl CliCommand> for SubmitVote { - fn command_name(&self) -> &'static str { - "SubmitDelegationPoolVote" - } - - async fn execute(mut self) -> CliTypedResult> { - // The vote option is a group, so only one of yes and no must be true. - let vote = self.args.yes; - let mut summaries: Vec = vec![]; - if let Some(txn_summary) = delegation_pool_governance_precheck( - &self.args.txn_options, - self.delegation_pool_address, - ) - .await? - { - summaries.push(txn_summary); - }; - - let client = &self - .args - .txn_options - .rest_options - .client(&self.args.txn_options.profile_options)?; - let voter_address = self.args.txn_options.profile_options.account_address()?; - let remaining_voting_power = get_remaining_voting_power( - client, - self.delegation_pool_address, - voter_address, - self.args.proposal_id, - ) - .await?; - if remaining_voting_power == 0 { - return Err(CliError::CommandArgumentError( - "Voter has no voting power left on this proposal".to_string(), - )); - }; - let voting_power = - check_remaining_voting_power(remaining_voting_power, self.args.voting_power); - prompt_yes_with_override( - &format!( - "Vote {} with voting power = {} from stake pool {} on proposal {}?", - vote_to_string(vote), - voting_power, - self.delegation_pool_address, - self.args.proposal_id, - ), - self.args.txn_options.prompt_options, - )?; - summaries.push( - self.args - .txn_options - .submit_transaction(aptos_stdlib::delegation_pool_vote( - self.delegation_pool_address, - self.args.proposal_id, - voting_power, - vote, - )) - .await - .map(TransactionSummary::from)?, - ); - - Ok(summaries) - } -} - -/// Precheck before any delegation pool governance operations. Check if feature flags are enabled. -/// Also check if partial governance voting is enabled for delegation pool. If not, send a -/// transaction to enable it. -async fn delegation_pool_governance_precheck( - txn_options: &TransactionOptions, - pool_address: AccountAddress, -) -> CliTypedResult> { - let client = &txn_options - .rest_options - .client(&txn_options.profile_options)?; - if !is_partial_governance_voting_enabled(client).await? { - return Err(CliError::CommandArgumentError( - "Partial governance voting feature flag is not enabled".to_string(), - )); - }; - if !is_delegation_pool_partial_governance_voting_enabled(client).await? { - return Err(CliError::CommandArgumentError( - "Delegation pool partial governance voting feature flag is not enabled".to_string(), - )); - }; - if is_partial_governance_voting_enabled_for_delegation_pool(client, pool_address).await? { - Ok(None) - } else { - println!("Partial governance voting for delegation pool {} hasn't been enabled yet. Enabling it now...", - pool_address); - let txn_summary = txn_options - .submit_transaction( - aptos_stdlib::delegation_pool_enable_partial_governance_voting(pool_address), - ) - .await - .map(TransactionSummary::from)?; - Ok(Some(txn_summary)) - } -} - -async fn is_partial_governance_voting_enabled_for_delegation_pool( - client: &Client, - pool_address: AccountAddress, -) -> CliTypedResult { - let response = client - .view( - &ViewRequest { - function: "0x1::delegation_pool::partial_governance_voting_enabled" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![serde_json::Value::String(pool_address.to_string())], - }, - None, - ) - .await?; - response.inner()[0] - .as_bool() - .ok_or(CliError::UnexpectedError( - "Unexpected response from node when checking if partial governance_voting is \ - enabled for delegation pool" - .to_string(), - )) -} - -async fn get_remaining_voting_power( - client: &Client, - pool_address: AccountAddress, - voter_address: AccountAddress, - proposal_id: u64, -) -> CliTypedResult { - let response = client - .view( - &ViewRequest { - function: "0x1::delegation_pool::calculate_and_update_remaining_voting_power" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(pool_address.to_string()), - serde_json::Value::String(voter_address.to_string()), - serde_json::Value::String(proposal_id.to_string()), - ], - }, - None, - ) - .await?; - let remaining_voting_power_str = - response.inner()[0] - .as_str() - .ok_or(CliError::UnexpectedError(format!( - "Unexpected response from node when getting remaining voting power of {}\ - in delegation pool {}", - pool_address, voter_address - )))?; - remaining_voting_power_str.parse().map_err(|err| { - CliError::UnexpectedError(format!( - "Unexpected response from node when getting remaining voting power of {}\ - in delegation pool {}: {}", - pool_address, voter_address, err - )) - }) -} diff --git a/crates/aptos/src/governance/mod.rs b/crates/aptos/src/governance/mod.rs deleted file mode 100644 index a781f12dbee32..0000000000000 --- a/crates/aptos/src/governance/mod.rs +++ /dev/null @@ -1,1227 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod delegation_pool; -pub mod utils; - -#[cfg(feature = "no-upload-proposal")] -use crate::common::utils::read_from_file; -use crate::{ - common::{ - types::{ - CliError, CliTypedResult, MovePackageDir, PoolAddressArgs, ProfileOptions, - PromptOptions, RestOptions, TransactionOptions, TransactionSummary, - }, - utils::prompt_yes_with_override, - }, - governance::utils::*, - move_tool::{FrameworkPackageArgs, IncludedArtifacts}, - CliCommand, CliResult, -}; -use aptos_api_types::ViewRequest; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::HashValue; -use aptos_framework::{BuildOptions, BuiltPackage, ReleasePackage}; -use aptos_logger::warn; -use aptos_rest_client::{ - aptos_api_types::{Address, HexEncodedBytes, U128, U64}, - Client, Transaction, -}; -use aptos_sdk::move_types::language_storage::CORE_CODE_ADDRESS; -use aptos_types::{ - account_address::AccountAddress, - event::EventHandle, - governance::VotingRecords, - stake_pool::StakePool, - state_store::table::TableHandle, - transaction::{Script, TransactionPayload}, -}; -use async_trait::async_trait; -use clap::Parser; -use move_core_types::transaction_argument::TransactionArgument; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - fmt::Formatter, - fs, - path::{Path, PathBuf}, -}; -use tempfile::TempDir; - -/// Tool for on-chain governance -/// -/// This tool allows voters that have stake to vote the ability to -/// propose changes to the chain, as well as vote and execute these -/// proposals. -#[derive(Parser)] -pub enum GovernanceTool { - Propose(SubmitProposal), - Vote(SubmitVote), - ShowProposal(ViewProposal), - ListProposals(ListProposals), - VerifyProposal(VerifyProposal), - ExecuteProposal(ExecuteProposal), - GenerateUpgradeProposal(GenerateUpgradeProposal), - ApproveExecutionHash(ApproveExecutionHash), - #[clap(subcommand)] - DelegationPool(delegation_pool::DelegationPoolTool), -} - -impl GovernanceTool { - pub async fn execute(self) -> CliResult { - use GovernanceTool::*; - match self { - Propose(tool) => tool.execute_serialized().await, - Vote(tool) => tool.execute_serialized().await, - ExecuteProposal(tool) => tool.execute_serialized().await, - GenerateUpgradeProposal(tool) => tool.execute_serialized_success().await, - ShowProposal(tool) => tool.execute_serialized().await, - ListProposals(tool) => tool.execute_serialized().await, - VerifyProposal(tool) => tool.execute_serialized().await, - ApproveExecutionHash(tool) => tool.execute_serialized().await, - DelegationPool(tool) => tool.execute().await, - } - } -} - -/// View a known on-chain governance proposal -/// -/// This command will return the proposal requested as well as compute -/// the hash of the metadata to determine whether it was verified or not. -#[derive(Parser)] -pub struct ViewProposal { - /// The identifier of the onchain governance proposal - #[clap(long)] - proposal_id: u64, - - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - profile: ProfileOptions, -} - -#[async_trait] -impl CliCommand for ViewProposal { - fn command_name(&self) -> &'static str { - "ViewProposal" - } - - async fn execute(mut self) -> CliTypedResult { - // Get proposal - let client = self.rest_options.client(&self.profile)?; - let forum = client - .get_account_resource_bcs::( - AccountAddress::ONE, - "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", - ) - .await? - .into_inner(); - let voting_table = forum.table_handle.0; - - let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) - .await? - .into(); - - let metadata_hash = proposal.metadata.get("metadata_hash").unwrap(); - let metadata_url = proposal.metadata.get("metadata_location").unwrap(); - - // Compute the hash and verify accordingly - let mut metadata_verified = false; - let mut actual_metadata_hash = "Unable to fetch metadata url".to_string(); - let mut actual_metadata = None; - if let Ok(url) = Url::parse(metadata_url) { - if let Ok(bytes) = get_metadata_from_url(&url).await { - let hash = HashValue::sha3_256_of(&bytes); - metadata_verified = metadata_hash == &hash.to_hex(); - actual_metadata_hash = hash.to_hex(); - if let Ok(metadata) = String::from_utf8(bytes) { - actual_metadata = Some(metadata); - } - } - } - - Ok(VerifiedProposal { - metadata_verified, - actual_metadata_hash, - actual_metadata, - proposal, - }) - } -} - -/// List the last 100 visible onchain proposals -/// -/// Note, if the full node you are talking to is pruning data, it may not have some of the -/// proposals show here -#[derive(Parser)] -pub struct ListProposals { - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - profile: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for ListProposals { - fn command_name(&self) -> &'static str { - "ListProposals" - } - - async fn execute(mut self) -> CliTypedResult> { - // List out known proposals based on events - let client = self.rest_options.client(&self.profile)?; - - let events = client - .get_account_events_bcs( - AccountAddress::ONE, - "0x1::aptos_governance::GovernanceEvents", - "create_proposal_events", - None, - Some(100), - ) - .await? - .into_inner(); - let mut proposals = vec![]; - - for event in &events { - match bcs::from_bytes::(event.event.event_data()) { - Ok(valid_event) => proposals.push(valid_event.into()), - Err(err) => { - eprintln!( - "Event: {:?} cannot be parsed as a proposal: {:?}", - event, err - ) - }, - } - } - - // TODO: Show more information about proposal? - Ok(proposals) - } -} - -/// Verify a proposal given the source code of the script -/// -/// The script's bytecode or source can be provided and it will -/// verify whether the hash matches the onchain hash -#[derive(Parser)] -pub struct VerifyProposal { - /// The id of the onchain proposal - #[clap(long)] - pub(crate) proposal_id: u64, - - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile: ProfileOptions, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand for VerifyProposal { - fn command_name(&self) -> &'static str { - "VerifyProposal" - } - - async fn execute(mut self) -> CliTypedResult { - // Compile local first to get the hash - let (_, hash) = self - .compile_proposal_args - .compile("SubmitProposal", self.prompt_options)?; - - // Retrieve the onchain proposal - let client = self.rest_options.client(&self.profile)?; - let forum = client - .get_account_resource_bcs::( - AccountAddress::ONE, - "0x1::voting::VotingForum<0x1::governance_proposal::GovernanceProposal>", - ) - .await? - .into_inner(); - let voting_table = forum.table_handle.0; - - let proposal: Proposal = get_proposal(&client, voting_table, self.proposal_id) - .await? - .into(); - - // Compare the hashes - let computed_hash = hash.to_hex(); - let onchain_hash = proposal.execution_hash; - - Ok(VerifyProposalResponse { - verified: computed_hash == onchain_hash, - computed_hash, - onchain_hash, - }) - } -} - -async fn get_proposal( - client: &aptos_rest_client::Client, - voting_table: AccountAddress, - proposal_id: u64, -) -> CliTypedResult { - let json = client - .get_table_item( - voting_table, - "u64", - "0x1::voting::Proposal<0x1::governance_proposal::GovernanceProposal>", - format!("{}", proposal_id), - ) - .await? - .into_inner(); - serde_json::from_value(json) - .map_err(|err| CliError::CommandArgumentError(format!("Failed to parse proposal {}", err))) -} - -/// Submit a governance proposal -#[derive(Parser)] -pub struct SubmitProposal { - #[clap(flatten)] - pub(crate) pool_address_args: PoolAddressArgs, - #[clap(flatten)] - pub(crate) args: SubmitProposalArgs, -} - -#[derive(Parser)] -pub struct SubmitProposalArgs { - /// Location of the JSON metadata of the proposal - /// - /// If this location does not keep the metadata in the exact format, it will be less likely - /// that voters will approve this proposal, as they won't be able to verify it. - #[clap(long)] - pub(crate) metadata_url: Url, - - #[cfg(feature = "no-upload-proposal")] - /// A JSON file to be uploaded later at the metadata URL - /// - /// If this does not match properly, voters may choose to vote no. For real proposals, - /// it is better to already have it uploaded at the URL. - #[clap(long)] - pub(crate) metadata_path: Option, - - #[clap(long)] - pub(crate) is_multi_step: bool, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, -} - -impl SubmitProposalArgs { - /// Compile the proposal and return the script hash and metadata hash. - pub async fn compile_proposals(&self) -> CliTypedResult<(HashValue, HashValue)> { - let (_bytecode, script_hash) = self - .compile_proposal_args - .compile("SubmitProposal", self.txn_options.prompt_options)?; - - // Validate the proposal metadata - let (metadata, metadata_hash) = self.get_metadata().await?; - - println!( - "{}\n\tMetadata Hash: {}\n\tScript Hash: {}", - metadata, metadata_hash, script_hash - ); - Ok((script_hash, metadata_hash)) - } - - /// Retrieve metadata and validate it - async fn get_metadata(&self) -> CliTypedResult<(ProposalMetadata, HashValue)> { - #[cfg(feature = "no-upload-proposal")] - let bytes = if let Some(ref path) = self.metadata_path { - read_from_file(path)? - } else { - get_metadata_from_url(&self.metadata_url).await? - }; - #[cfg(not(feature = "no-upload-proposal"))] - let bytes = get_metadata_from_url(&self.metadata_url).await?; - - let metadata: ProposalMetadata = serde_json::from_slice(&bytes).map_err(|err| { - CliError::CommandArgumentError(format!( - "Metadata is not in a proper JSON format: {}", - err - )) - })?; - Url::parse(&metadata.source_code_url).map_err(|err| { - CliError::CommandArgumentError(format!( - "Source code URL {} is invalid {}", - metadata.source_code_url, err - )) - })?; - Url::parse(&metadata.discussion_url).map_err(|err| { - CliError::CommandArgumentError(format!( - "Discussion URL {} is invalid {}", - metadata.discussion_url, err - )) - })?; - let metadata_hash = HashValue::sha3_256_of(&bytes); - Ok((metadata, metadata_hash)) - } -} - -#[async_trait] -impl CliCommand for SubmitProposal { - fn command_name(&self) -> &'static str { - "SubmitProposal" - } - - async fn execute(mut self) -> CliTypedResult { - // Validate the proposal metadata - let (script_hash, metadata_hash) = self.args.compile_proposals().await?; - prompt_yes_with_override( - "Do you want to submit this proposal?", - self.args.txn_options.prompt_options, - )?; - - let txn: Transaction = if self.args.is_multi_step { - self.args - .txn_options - .submit_transaction(aptos_stdlib::aptos_governance_create_proposal_v2( - self.pool_address_args.pool_address, - script_hash.to_vec(), - self.args.metadata_url.to_string().as_bytes().to_vec(), - metadata_hash.to_hex().as_bytes().to_vec(), - true, - )) - .await? - } else { - self.args - .txn_options - .submit_transaction(aptos_stdlib::aptos_governance_create_proposal( - self.pool_address_args.pool_address, - script_hash.to_vec(), - self.args.metadata_url.to_string().as_bytes().to_vec(), - metadata_hash.to_hex().as_bytes().to_vec(), - )) - .await? - }; - let txn_summary = TransactionSummary::from(&txn); - let proposal_id = extract_proposal_id(&txn)?; - Ok(ProposalSubmissionSummary { - proposal_id, - transaction: txn_summary, - }) - } -} - -/// Retrieve the Metadata from the given URL -async fn get_metadata_from_url(metadata_url: &Url) -> CliTypedResult> { - let client = reqwest::ClientBuilder::default() - .tls_built_in_root_certs(true) - .build() - .map_err(|err| CliError::UnexpectedError(format!("Failed to build HTTP client {}", err)))?; - client - .get(metadata_url.clone()) - .send() - .await - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Failed to fetch metadata url {}: {}", - metadata_url, err - )) - })? - .bytes() - .await - .map(|b| b.to_vec()) - .map_err(|err| { - CliError::CommandArgumentError(format!( - "Failed to fetch metadata url {}: {}", - metadata_url, err - )) - }) -} - -/// Extract the proposal id from the events of a proposal creation transaction. -fn extract_proposal_id(txn: &Transaction) -> CliTypedResult> { - if let Transaction::UserTransaction(inner) = txn { - // Find event with proposal id - let proposal_id = if let Some(event) = inner.events.iter().find(|event| { - event.typ.to_string().as_str() == "0x1::aptos_governance::CreateProposalEvent" - }) { - let data: CreateProposalEvent = - serde_json::from_value(event.data.clone()).map_err(|_| { - CliError::UnexpectedError( - "Failed to parse Proposal event to get ProposalId".to_string(), - ) - })?; - Some(data.proposal_id.0) - } else { - warn!("No proposal event found to find proposal id"); - None - }; - - return Ok(proposal_id); - } - Err(CliError::UnexpectedError( - "Unable to find parse proposal transaction output".to_string(), - )) -} - -#[derive(Debug, Deserialize, Serialize)] -struct CreateProposalEvent { - proposal_id: U64, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct ProposalSubmissionSummary { - proposal_id: Option, - #[serde(flatten)] - transaction: TransactionSummary, -} - -/// Submit a vote on a proposal -/// -/// Votes can only be given on proposals that are currently open for voting. You can vote -/// with `--yes` for a yes vote, and `--no` for a no vote. -#[derive(Parser)] -pub struct SubmitVote { - /// Space separated list of pool addresses. - #[clap(long, num_args = 0.., value_parser = crate::common::types::load_account_arg)] - pub(crate) pool_addresses: Vec, - - #[clap(flatten)] - pub(crate) args: SubmitVoteArgs, -} - -#[derive(Parser)] -#[group(id = "vote", required = true, multiple = false)] -pub struct SubmitVoteArgs { - /// Id of the proposal to vote on - #[clap(long)] - pub(crate) proposal_id: u64, - - /// Vote to accept the proposal - #[clap(long, group = "vote")] - pub(crate) yes: bool, - - /// Vote to reject the proposal - #[clap(long, group = "vote")] - pub(crate) no: bool, - - /// Voting power to use for the vote. If not specified, all the voting power will be used. - #[clap(long)] - pub(crate) voting_power: Option, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -impl SubmitVote { - // Partial governance voting is controlled by a feature flag. If the feature flag is on, the way - // to check voting power will be different. - async fn vote_before_partial_governance_voting( - &self, - client: &Client, - vote: bool, - ) -> CliTypedResult> { - if self.args.voting_power.is_some() { - return Err(CliError::CommandArgumentError( - "Specifying voting power is not supported before partial governance voting feature flag is enabled".to_string(), - )); - }; - - let proposal_id = self.args.proposal_id; - let voting_records = client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::aptos_governance::VotingRecords", - ) - .await - .unwrap() - .into_inner() - .votes; - - let mut summaries: Vec = vec![]; - for pool_address in &self.pool_addresses { - let voting_record = client - .get_table_item( - voting_records, - "0x1::aptos_governance::RecordKey", - "bool", - VotingRecord { - proposal_id: proposal_id.to_string(), - stake_pool: *pool_address, - }, - ) - .await; - let voted = if let Ok(voting_record) = voting_record { - voting_record.into_inner().as_bool().unwrap() - } else { - false - }; - if voted { - println!("Stake pool {} already voted", *pool_address); - continue; - } - - let stake_pool = client - .get_account_resource_bcs::(*pool_address, "0x1::stake::StakePool") - .await? - .into_inner(); - let voting_power = stake_pool.get_governance_voting_power(); - - prompt_yes_with_override( - &format!( - "Vote {} with voting power = {} from stake pool {}?", - vote_to_string(vote), - voting_power, - pool_address - ), - self.args.txn_options.prompt_options, - )?; - - summaries.push( - self.args - .txn_options - .submit_transaction(aptos_stdlib::aptos_governance_vote( - *pool_address, - proposal_id, - vote, - )) - .await - .map(TransactionSummary::from)?, - ); - } - Ok(summaries) - } - - // Partial governance voting is controlled by a feature flag. If the feature flag is on, the way - // to check voting power will be different. - async fn vote_after_partial_governance_voting( - &self, - vote: bool, - ) -> CliTypedResult> { - if self.args.voting_power.is_some() && self.pool_addresses.len() > 1 { - return Err(CliError::CommandArgumentError( - "Only 1 pool address can be provided when voting power is specified".to_string(), - )); - }; - let proposal_id = self.args.proposal_id; - let is_proposal_closed = self - .args - .txn_options - .view(ViewRequest { - function: "0x1::voting::is_voting_closed".parse().unwrap(), - type_arguments: vec!["0x1::governance_proposal::GovernanceProposal" - .parse() - .unwrap()], - arguments: vec![ - serde_json::Value::String("0x1".to_string()), - serde_json::Value::String(proposal_id.to_string()), - ], - }) - .await?[0] - .as_bool() - .unwrap(); - if is_proposal_closed { - return Err(CliError::CommandArgumentError(format!( - "Proposal {} is closed.", - proposal_id - ))); - }; - - let mut summaries: Vec = vec![]; - for pool_address in &self.pool_addresses { - let remaining_voting_power = self - .args - .txn_options - .view(ViewRequest { - function: "0x1::aptos_governance::get_remaining_voting_power" - .parse() - .unwrap(), - type_arguments: vec![], - arguments: vec![ - serde_json::Value::String(pool_address.to_string()), - serde_json::Value::String(proposal_id.to_string()), - ], - }) - .await?[0] - .as_str() - .unwrap() - .parse() - .unwrap(); - if remaining_voting_power == 0 { - println!( - "Stake pool {} has no voting power on proposal {}. This is because the \ - stake pool has already voted before enabling partial governance voting, or the \ - stake pool has already used all its voting power.", - *pool_address, proposal_id - ); - continue; - } - let voting_power = - check_remaining_voting_power(remaining_voting_power, self.args.voting_power); - - prompt_yes_with_override( - &format!( - "Vote {} with voting power = {} from stake pool {}?", - vote_to_string(vote), - voting_power, - pool_address - ), - self.args.txn_options.prompt_options, - )?; - - summaries.push( - self.args - .txn_options - .submit_transaction(aptos_stdlib::aptos_governance_partial_vote( - *pool_address, - proposal_id, - voting_power, - vote, - )) - .await - .map(TransactionSummary::from)?, - ); - } - Ok(summaries) - } -} - -#[async_trait] -impl CliCommand> for SubmitVote { - fn command_name(&self) -> &'static str { - "SubmitVote" - } - - async fn execute(mut self) -> CliTypedResult> { - // The vote option is a group, so only one of yes and no must be true. - let vote = self.args.yes; - - let client: &Client = &self - .args - .txn_options - .rest_options - .client(&self.args.txn_options.profile_options)?; - - if is_partial_governance_voting_enabled(client).await? { - self.vote_after_partial_governance_voting(vote).await - } else { - return self - .vote_before_partial_governance_voting(client, vote) - .await; - } - } -} - -/// Submit a transaction to approve a proposal's script hash to bypass the transaction size limit. -/// This is needed for upgrading large packages such as aptos-framework. -#[derive(Parser)] -pub struct ApproveExecutionHash { - /// Id of the proposal to vote on - #[clap(long)] - pub(crate) proposal_id: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for ApproveExecutionHash { - fn command_name(&self) -> &'static str { - "ApproveExecutionHash" - } - - async fn execute(mut self) -> CliTypedResult { - Ok(self - .txn_options - .submit_transaction( - aptos_stdlib::aptos_governance_add_approved_script_hash_script(self.proposal_id), - ) - .await - .map(TransactionSummary::from)?) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct VotingRecord { - proposal_id: String, - stake_pool: AccountAddress, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ProposalMetadata { - title: String, - description: String, - source_code_url: String, - discussion_url: String, -} - -impl std::fmt::Display for ProposalMetadata { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Proposal:\n\tTitle:{}\n\tDescription:{}\n\tSource code URL:{}\n\tDiscussion URL:{}", - self.title, self.description, self.source_code_url, self.discussion_url - ) - } -} - -fn compile_in_temp_dir( - script_name: &str, - script_path: &Path, - framework_package_args: &FrameworkPackageArgs, - prompt_options: PromptOptions, - bytecode_version: Option, -) -> CliTypedResult<(Vec, HashValue)> { - // Make a temporary directory for compilation - let temp_dir = TempDir::new().map_err(|err| { - CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) - })?; - - // Initialize a move directory - let package_dir = temp_dir.path(); - framework_package_args.init_move_dir( - package_dir, - script_name, - BTreeMap::new(), - prompt_options, - )?; - - // Insert the new script - let sources_dir = package_dir.join("sources"); - let new_script_path = if let Some(file_name) = script_path.file_name() { - sources_dir.join(file_name) - } else { - // If for some reason we can't get the move file - sources_dir.join("script.move") - }; - fs::copy(script_path, new_script_path.as_path()).map_err(|err| { - CliError::IO( - format!( - "Failed to copy {} to {}", - script_path.display(), - new_script_path.display() - ), - err, - ) - })?; - - // Compile the script - compile_script( - framework_package_args.skip_fetch_latest_git_deps, - package_dir, - bytecode_version, - ) -} - -fn compile_script( - skip_fetch_latest_git_deps: bool, - package_dir: &Path, - bytecode_version: Option, -) -> CliTypedResult<(Vec, HashValue)> { - let build_options = BuildOptions { - with_srcs: false, - with_abis: false, - with_source_maps: false, - with_error_map: false, - skip_fetch_latest_git_deps, - bytecode_version, - ..BuildOptions::default() - }; - - let pack = BuiltPackage::build(package_dir.to_path_buf(), build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - let scripts_count = pack.script_count(); - - if scripts_count != 1 { - return Err(CliError::UnexpectedError(format!( - "Only one script can be prepared a time. Make sure one and only one script file \ - is included in the Move package. Found {} scripts.", - scripts_count - ))); - } - - let bytes = pack.extract_script_code().pop().unwrap(); - let hash = HashValue::sha3_256_of(bytes.as_slice()); - Ok((bytes, hash)) -} - -/// Execute a proposal that has passed voting requirements -#[derive(Parser)] -pub struct ExecuteProposal { - /// Proposal Id being executed - #[clap(long)] - pub(crate) proposal_id: u64, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, -} - -#[async_trait] -impl CliCommand for ExecuteProposal { - fn command_name(&self) -> &'static str { - "ExecuteProposal" - } - - async fn execute(mut self) -> CliTypedResult { - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("ExecuteProposal", self.txn_options.prompt_options)?; - // TODO: Check hash so we don't do a failed roundtrip? - - let args = vec![TransactionArgument::U64(self.proposal_id)]; - let txn = TransactionPayload::Script(Script::new(bytecode, vec![], args)); - - self.txn_options - .submit_transaction(txn) - .await - .map(TransactionSummary::from) - } -} - -/// Compile a specified script. -#[derive(Parser)] -pub struct CompileScriptFunction { - /// Path to the Move script for the proposal - #[clap(long, group = "script", value_parser)] - pub script_path: Option, - - /// Path to the Move script for the proposal - #[clap(long, group = "script", value_parser)] - pub compiled_script_path: Option, - - #[clap(flatten)] - pub(crate) framework_package_args: FrameworkPackageArgs, - - #[clap(long)] - pub(crate) bytecode_version: Option, -} - -impl CompileScriptFunction { - pub(crate) fn compile( - &self, - script_name: &str, - prompt_options: PromptOptions, - ) -> CliTypedResult<(Vec, HashValue)> { - if let Some(compiled_script_path) = &self.compiled_script_path { - let bytes = std::fs::read(compiled_script_path).map_err(|e| { - CliError::IO(format!("Unable to read {:?}", self.compiled_script_path), e) - })?; - let hash = HashValue::sha3_256_of(bytes.as_slice()); - return Ok((bytes, hash)); - } - - // Check script file - let script_path = self - .script_path - .as_ref() - .ok_or_else(|| { - CliError::CommandArgumentError( - "Must choose either --compiled-script-path or --script-path".to_string(), - ) - })? - .as_path(); - if !script_path.exists() { - return Err(CliError::CommandArgumentError(format!( - "{} does not exist", - script_path.display() - ))); - } else if script_path.is_dir() { - return Err(CliError::CommandArgumentError(format!( - "{} is a directory", - script_path.display() - ))); - } - - // Compile script - compile_in_temp_dir( - script_name, - script_path, - &self.framework_package_args, - prompt_options, - self.bytecode_version, - ) - } -} - -/// Generates a package upgrade proposal script. -#[derive(Parser)] -pub struct GenerateUpgradeProposal { - /// Address of the account which the proposal addresses. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - /// Where to store the generated proposal - #[clap(long, value_parser, default_value = "proposal.move")] - pub(crate) output: PathBuf, - - /// What artifacts to include in the package. This can be one of `none`, `sparse`, and - /// `all`. `none` is the most compact form and does not allow to reconstruct a source - /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct - /// a source package; `all` includes all available artifacts. The choice of included - /// artifacts heavily influences the size and therefore gas cost of publishing: `none` - /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 - /// as much. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, - - /// Generate the script for mainnet governance proposal by default or generate the upgrade script for testnet. - #[clap(long)] - pub(crate) testnet: bool, - - #[clap(long, default_value = "")] - pub(crate) next_execution_hash: String, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for GenerateUpgradeProposal { - fn command_name(&self) -> &'static str { - "GenerateUpgradeProposal" - } - - async fn execute(self) -> CliTypedResult<()> { - let GenerateUpgradeProposal { - move_options, - account, - included_artifacts, - output, - testnet, - next_execution_hash, - } = self; - let package_path = move_options.get_package_path()?; - let options = included_artifacts.build_options( - move_options.dev, - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - move_options.compiler_version, - move_options.skip_attribute_checks, - move_options.check_test_code, - ); - let package = BuiltPackage::build(package_path, options)?; - let release = ReleasePackage::new(package)?; - - // If we're generating a single-step proposal on testnet - if testnet && next_execution_hash.is_empty() { - release.generate_script_proposal_testnet(account, output)?; - // If we're generating a single-step proposal on mainnet - } else if next_execution_hash.is_empty() { - release.generate_script_proposal(account, output)?; - // If we're generating a multi-step proposal - } else { - let next_execution_hash_bytes = hex::decode(next_execution_hash)?; - release.generate_script_proposal_multi_step( - account, - output, - next_execution_hash_bytes, - )?; - }; - Ok(()) - } -} - -/// Generate execution hash for a specified script. -#[derive(Parser)] -pub struct GenerateExecutionHash { - #[clap(long)] - pub script_path: Option, - #[clap(long)] - pub framework_local_dir: Option, -} - -impl GenerateExecutionHash { - pub fn generate_hash(&self) -> CliTypedResult<(Vec, HashValue)> { - let framework_local_dir = if self.framework_local_dir.is_some() { - self.framework_local_dir.clone() - } else { - Option::from({ - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.pop(); - path.pop(); - path.join("aptos-move") - .join("framework") - .join("aptos-framework") - .canonicalize() - .map_err(|err| { - CliError::IO( - format!("Failed to canonicalize aptos framework path: {:?}", path), - err, - ) - })? - }) - }; - CompileScriptFunction { - script_path: self.script_path.clone(), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir, - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - } - .compile("execution_hash", PromptOptions::yes()) - } -} - -/// Response for `verify proposal` -#[derive(Serialize, Deserialize, Debug)] -pub struct VerifyProposalResponse { - pub verified: bool, - pub computed_hash: String, - pub onchain_hash: String, -} - -/// Voting forum onchain type -/// -/// TODO: Move to a shared location -#[derive(Serialize, Deserialize, Debug)] -pub struct VotingForum { - table_handle: TableHandle, - events: VotingEvents, - next_proposal_id: u64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct VotingEvents { - create_proposal_events: EventHandle, - register_forum_events: EventHandle, - resolve_proposal_events: EventHandle, - vote_events: EventHandle, -} - -/// Summary of proposal from the listing events for `ListProposals` -#[derive(Serialize, Deserialize, Debug)] -struct ProposalSummary { - proposer: AccountAddress, - stake_pool: AccountAddress, - proposal_id: u64, - execution_hash: String, - proposal_metadata: BTreeMap, -} - -impl From for ProposalSummary { - fn from(event: CreateProposalFullEvent) -> Self { - let proposal_metadata = event - .proposal_metadata - .into_iter() - .map(|(key, value)| (key, String::from_utf8(value).unwrap())) - .collect(); - ProposalSummary { - proposer: event.proposer, - stake_pool: event.stake_pool, - proposal_id: event.proposal_id, - execution_hash: hex::encode(event.execution_hash), - proposal_metadata, - } - } -} - -#[derive(Deserialize)] -struct CreateProposalFullEvent { - proposer: AccountAddress, - stake_pool: AccountAddress, - proposal_id: u64, - execution_hash: Vec, - proposal_metadata: Vec<(String, Vec)>, -} - -/// A proposal and the verified information about it -#[derive(Serialize, Deserialize, Debug)] -pub struct VerifiedProposal { - metadata_verified: bool, - actual_metadata_hash: String, - actual_metadata: Option, - proposal: Proposal, -} - -/// A reformatted type that has human readable version of the proposal onchain -#[derive(Serialize, Deserialize, Debug)] -pub struct Proposal { - proposer: AccountAddress, - metadata: BTreeMap, - creation_time_secs: u64, - execution_hash: String, - min_vote_threshold: u128, - expiration_secs: u64, - early_resolution_vote_threshold: Option, - yes_votes: u128, - no_votes: u128, - is_resolved: bool, - resolution_time_secs: u64, -} - -impl From for Proposal { - fn from(proposal: JsonProposal) -> Self { - let metadata = proposal - .metadata - .data - .into_iter() - .map(|pair| { - let value = match pair.key.as_str() { - "metadata_hash" => String::from_utf8(pair.value.0) - .unwrap_or_else(|_| "Failed to parse utf8".to_string()), - "metadata_location" => String::from_utf8(pair.value.0) - .unwrap_or_else(|_| "Failed to parse utf8".to_string()), - "RESOLVABLE_TIME_METADATA_KEY" => bcs::from_bytes::(pair.value.inner()) - .map(|inner| inner.to_string()) - .unwrap_or_else(|_| "Failed to parse u64".to_string()), - _ => pair.value.to_string(), - }; - (pair.key, value) - }) - .collect(); - - Proposal { - proposer: proposal.proposer.into(), - metadata, - creation_time_secs: proposal.creation_time_secs.into(), - execution_hash: format!("{:x}", proposal.execution_hash), - min_vote_threshold: proposal.min_vote_threshold.into(), - expiration_secs: proposal.expiration_secs.into(), - early_resolution_vote_threshold: proposal - .early_resolution_vote_threshold - .vec - .first() - .map(|inner| inner.0), - yes_votes: proposal.yes_votes.into(), - no_votes: proposal.no_votes.into(), - is_resolved: proposal.is_resolved, - resolution_time_secs: proposal.resolution_time_secs.into(), - } - } -} - -/// An ugly JSON parsing version for from the JSON API -#[derive(Serialize, Deserialize, Debug)] -struct JsonProposal { - creation_time_secs: U64, - early_resolution_vote_threshold: JsonEarlyResolutionThreshold, - execution_hash: aptos_rest_client::aptos_api_types::HashValue, - expiration_secs: U64, - is_resolved: bool, - min_vote_threshold: U128, - no_votes: U128, - resolution_time_secs: U64, - yes_votes: U128, - proposer: Address, - metadata: JsonMetadata, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonEarlyResolutionThreshold { - vec: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonMetadata { - data: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct JsonMetadataPair { - key: String, - value: HexEncodedBytes, -} diff --git a/crates/aptos/src/governance/utils.rs b/crates/aptos/src/governance/utils.rs deleted file mode 100644 index c936778595da0..0000000000000 --- a/crates/aptos/src/governance/utils.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{governance::*, *}; -use aptos_types::on_chain_config::FeatureFlag; - -pub fn vote_to_string(vote: bool) -> &'static str { - if vote { - "Yes" - } else { - "No" - } -} - -pub fn check_remaining_voting_power( - remaining_voting_power: u64, - specified_voting_power: Option, -) -> u64 { - let mut voting_power = remaining_voting_power; - if let Some(specified_voting_power) = specified_voting_power { - if specified_voting_power > voting_power { - println!( - "Stake pool only has {} voting power on proposal.", - voting_power - ); - } else { - voting_power = specified_voting_power; - }; - }; - voting_power -} - -pub async fn is_partial_governance_voting_enabled(client: &Client) -> CliTypedResult { - common::utils::get_feature_flag(client, FeatureFlag::PARTIAL_GOVERNANCE_VOTING).await -} - -pub async fn is_delegation_pool_partial_governance_voting_enabled( - client: &Client, -) -> CliTypedResult { - common::utils::get_feature_flag( - client, - FeatureFlag::DELEGATION_POOL_PARTIAL_GOVERNANCE_VOTING, - ) - .await -} diff --git a/crates/aptos/src/lib.rs b/crates/aptos/src/lib.rs deleted file mode 100644 index 49a0d3c79816b..0000000000000 --- a/crates/aptos/src/lib.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![deny(unsafe_code)] - -pub mod account; -pub mod common; -pub mod config; -pub mod ffi; -pub mod genesis; -pub mod governance; -pub mod move_tool; -pub mod node; -pub mod op; -pub mod stake; -#[cfg(any(test, feature = "fuzzing"))] -pub mod test; -pub mod update; - -use crate::common::{ - types::{CliCommand, CliResult, CliTypedResult}, - utils::cli_build_information, -}; -use async_trait::async_trait; -use clap::Parser; -use std::collections::BTreeMap; - -/// Command Line Interface (CLI) for developing and interacting with the Aptos blockchain -#[derive(Parser)] -#[clap(name = "aptos", author, version, propagate_version = true, styles = aptos_cli_common::aptos_cli_style())] -pub enum Tool { - #[clap(subcommand)] - Account(account::AccountTool), - #[clap(subcommand)] - Config(config::ConfigTool), - #[clap(subcommand)] - Genesis(genesis::GenesisTool), - #[clap(subcommand)] - Governance(governance::GovernanceTool), - Info(InfoTool), - Init(common::init::InitTool), - #[clap(subcommand)] - Key(op::key::KeyTool), - #[clap(subcommand)] - Move(move_tool::MoveTool), - #[clap(subcommand)] - Multisig(account::MultisigAccountTool), - #[clap(subcommand)] - Node(node::NodeTool), - #[clap(subcommand)] - Stake(stake::StakeTool), - #[clap(subcommand)] - Update(update::UpdateTool), -} - -impl Tool { - pub async fn execute(self) -> CliResult { - use Tool::*; - match self { - Account(tool) => tool.execute().await, - Config(tool) => tool.execute().await, - Genesis(tool) => tool.execute().await, - Governance(tool) => tool.execute().await, - Info(tool) => tool.execute_serialized().await, - // TODO: Replace entirely with config init - Init(tool) => tool.execute_serialized_success().await, - Key(tool) => tool.execute().await, - Move(tool) => tool.execute().await, - Multisig(tool) => tool.execute().await, - Node(tool) => tool.execute().await, - Stake(tool) => tool.execute().await, - Update(tool) => tool.execute().await, - } - } -} - -/// Show build information about the CLI -/// -/// This is useful for debugging as well as determining what versions are compatible with the CLI -#[derive(Parser)] -pub struct InfoTool {} - -#[async_trait] -impl CliCommand> for InfoTool { - fn command_name(&self) -> &'static str { - "GetCLIInfo" - } - - async fn execute(self) -> CliTypedResult> { - Ok(cli_build_information()) - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Tool::command().debug_assert() -} diff --git a/crates/aptos/src/main.rs b/crates/aptos/src/main.rs deleted file mode 100644 index e2f1730baec41..0000000000000 --- a/crates/aptos/src/main.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Aptos is a one stop tool for operations, debugging, and other operations with the blockchain - -#![forbid(unsafe_code)] - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -use aptos::{move_tool, Tool}; -use clap::Parser; -use std::{process::exit, time::Duration}; - -fn main() { - // Register hooks. - move_tool::register_package_hooks(); - - // Create a runtime. - let runtime = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - - // Run the corresponding tool. - let result = runtime.block_on(Tool::parse().execute()); - - // Shutdown the runtime with a timeout. We do this to make sure that we don't sit - // here waiting forever waiting for tasks that sometimes don't want to exit on - // their own (e.g. telemetry, containers spawned by the local testnet, etc). - runtime.shutdown_timeout(Duration::from_millis(50)); - - match result { - Ok(inner) => println!("{}", inner), - Err(inner) => { - println!("{}", inner); - exit(1); - }, - } -} diff --git a/crates/aptos/src/move_tool/aptos_debug_natives.rs b/crates/aptos/src/move_tool/aptos_debug_natives.rs deleted file mode 100644 index 926f1c48826ca..0000000000000 --- a/crates/aptos/src/move_tool/aptos_debug_natives.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_framework::extended_checks; -use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters, LATEST_GAS_FEATURE_VERSION}; -use aptos_types::on_chain_config::{Features, TimedFeaturesBuilder}; -use aptos_vm::natives; -use move_vm_runtime::native_functions::NativeFunctionTable; - -// move_stdlib has the testing feature enabled to include debug native functions -pub fn aptos_debug_natives( - native_gas_parameters: NativeGasParameters, - misc_gas_params: MiscGasParameters, -) -> NativeFunctionTable { - // As a side effect, also configure for unit testing - natives::configure_for_unit_test(); - extended_checks::configure_extended_checks_for_unit_test(); - // Return all natives -- build with the 'testing' feature, therefore containing - // debug related functions. - natives::aptos_natives( - LATEST_GAS_FEATURE_VERSION, - native_gas_parameters, - misc_gas_params, - TimedFeaturesBuilder::enable_all().build(), - Features::default(), - ) -} diff --git a/crates/aptos/src/move_tool/aptos_dep_example/README.md b/crates/aptos/src/move_tool/aptos_dep_example/README.md deleted file mode 100644 index 89d702218476a..0000000000000 --- a/crates/aptos/src/move_tool/aptos_dep_example/README.md +++ /dev/null @@ -1,24 +0,0 @@ -This is a small example of using the new `aptos` dependency. This shall be removed once we have -documentation/tests. - -`pack2` contains a package which is used by `pack1` as follows: - -``` -[dependencies] -Pack2 = { aptos = "http://localhost:8080", address = "default" } -``` - -To see it working: - -```shell -# Start a node with an account -aptos node run-local-testnet & -aptos account create --account default --use-faucet -# Compile and publish pack2 -cd pack2 -aptos move compile --named-addresses project=default -aptos move publish --named-addresses project=default -# Compile pack1 agains the published pack2 -cd ../pack1 -aptos move compile --named-addresses project=default -``` diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml b/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml deleted file mode 100644 index dab5d3fe70e9e..0000000000000 --- a/crates/aptos/src/move_tool/aptos_dep_example/pack1/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "Pack1" -version = "0.0.0" - -[dependencies] -Pack2 = { aptos = "http://localhost:8080", address = "default" } diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move b/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move deleted file mode 100644 index fe091952d3dda..0000000000000 --- a/crates/aptos/src/move_tool/aptos_dep_example/pack1/sources/hello.move +++ /dev/null @@ -1,7 +0,0 @@ -module project::test { - use project::m; - - public entry fun test(_sender: &signer) { - assert!(m::add(1, 2) == 1 + 2, 1); - } -} diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml b/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml deleted file mode 100644 index c5e6c7d008c72..0000000000000 --- a/crates/aptos/src/move_tool/aptos_dep_example/pack2/Move.toml +++ /dev/null @@ -1,3 +0,0 @@ -[package] -name = "Pack2" -version = "0.0.0" diff --git a/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move b/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move deleted file mode 100644 index ade22fadb5cfe..0000000000000 --- a/crates/aptos/src/move_tool/aptos_dep_example/pack2/sources/m.move +++ /dev/null @@ -1,3 +0,0 @@ -module project::m { - public fun add(x: u64, y: u64): u64 { x + y } -} diff --git a/crates/aptos/src/move_tool/bytecode.rs b/crates/aptos/src/move_tool/bytecode.rs deleted file mode 100644 index c883783f02a51..0000000000000 --- a/crates/aptos/src/move_tool/bytecode.rs +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{CliCommand, CliError, CliTypedResult, PromptOptions}, - utils::{ - check_if_file_exists, create_dir_if_not_exist, read_dir_files, read_from_file, - write_to_user_only_file, - }, - }, - update::get_revela_path, -}; -use anyhow::Context; -use async_trait::async_trait; -use clap::{Args, Parser}; -use itertools::Itertools; -use move_binary_format::{ - binary_views::BinaryIndexedView, file_format::CompiledScript, CompiledModule, -}; -use move_bytecode_source_map::{mapping::SourceMapping, utils::source_map_from_file}; -use move_command_line_common::files::{ - MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, -}; -use move_coverage::coverage_map::CoverageMap; -use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; -use move_ir_types::location::Spanned; -use std::{ - fs, - path::{Path, PathBuf}, - process::Command, -}; - -const DISASSEMBLER_EXTENSION: &str = "mv.asm"; -const DECOMPILER_EXTENSION: &str = "mv.move"; - -/// Disassemble the Move bytecode pointed to in the textual representation -/// of Move bytecode. -/// -/// For example, if you want to disassemble an on-chain package `PackName` at account `0x42`: -/// 1. Download the package with `aptos move download --account 0x42 --package PackName --bytecode` -/// 2. Disassemble the package bytecode with `aptos disassemble --package-path PackName/bytecode_modules` -#[derive(Debug, Parser)] -pub struct Disassemble { - #[clap(flatten)] - pub command: BytecodeCommand, -} - -/// Decompile the Move bytecode pointed to into Move source code. -/// -/// For example, if you want to decompile an on-chain package `PackName` at account `0x42`: -/// 1. Download the package with `aptos move download --account 0x42 --package PackName --bytecode` -/// 2. Decompile the package bytecode with `aptos decompile --package-path PackName/bytecode_modules` -#[derive(Debug, Parser)] -pub struct Decompile { - #[clap(flatten)] - pub command: BytecodeCommand, -} - -#[derive(Debug, Args)] -pub struct BytecodeCommand { - /// Treat input file as a script (default is to treat file as a module) - #[clap(long)] - pub is_script: bool, - - #[clap(flatten)] - input: BytecodeCommandInput, - - /// (Optional) Currently only for disassemble: path to a coverage file for the VM in order - /// to print trace information in the disassembled output. - #[clap(long)] - pub code_coverage_path: Option, - - /// Output directory for the generated file. Defaults to the directory of the - /// `path/module.mv` file if not provided. The disassembled output is stored - /// at `output_dir/module.mv.asm`, and decompiled output at - /// `output_dir/module.mv.move`. - #[clap(long, value_parser)] - pub(crate) output_dir: Option, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -/// Allows to ensure that either one of both is selected (via the `group` attribute). -#[derive(Debug, Args)] -#[group(required = true, multiple = false)] -pub struct BytecodeCommandInput { - /// The path to a directory containing Move bytecode files with the extension `.mv`. - /// The tool will process all files find in this directory - /// - /// If present, a source map at the same location ending in `.mvsm` and the source - /// file itself ending in`.move` will be processed by the tool. - #[clap(long)] - pub package_path: Option, - - /// Alternatively to a package path, path to a single bytecode file which should be processed. - #[clap(long)] - pub bytecode_path: Option, -} - -#[derive(Debug, Clone, Copy)] -enum BytecodeCommandType { - Disassemble, - Decompile, -} - -#[async_trait] -impl CliCommand for Disassemble { - fn command_name(&self) -> &'static str { - "Disassemble" - } - - async fn execute(mut self) -> CliTypedResult { - self.command.execute(BytecodeCommandType::Disassemble).await - } -} - -#[async_trait] -impl CliCommand for Decompile { - fn command_name(&self) -> &'static str { - "Decompile" - } - - async fn execute(mut self) -> CliTypedResult { - self.command.execute(BytecodeCommandType::Decompile).await - } -} - -impl BytecodeCommand { - async fn execute(self, command_type: BytecodeCommandType) -> CliTypedResult { - let inputs = if let Some(path) = self.input.bytecode_path.clone() { - vec![path] - } else if let Some(path) = self.input.package_path.clone() { - read_dir_files(path.as_path(), |p| { - p.extension() - .map(|s| s == MOVE_COMPILED_EXTENSION) - .unwrap_or_default() - })? - } else { - unreachable!("arguments required by clap") - }; - - let mut report = vec![]; - let mut last_out_dir = String::new(); - for bytecode_path in inputs { - let bytecode_path = bytecode_path.as_path(); - let extension = bytecode_path - .extension() - .context("Missing file extension for bytecode file")?; - if extension != MOVE_COMPILED_EXTENSION { - return Err(CliError::UnexpectedError(format!( - "Bad source file extension {:?}; expected {}", - extension, MOVE_COMPILED_EXTENSION - ))); - } - - let (output, extension) = match command_type { - BytecodeCommandType::Disassemble => { - (self.disassemble(bytecode_path)?, DISASSEMBLER_EXTENSION) - }, - BytecodeCommandType::Decompile => { - (self.decompile(bytecode_path)?, DECOMPILER_EXTENSION) - }, - }; - - let output_dir = if let Some(dir) = self.output_dir.clone() { - dir - } else { - bytecode_path.parent().expect("has parent dir").to_owned() - }; - last_out_dir = output_dir.display().to_string(); - - let output_file = output_dir - .join(bytecode_path.file_name().expect("file name")) - .with_extension(extension); - check_if_file_exists(output_file.as_path(), self.prompt_options)?; - - // Create the directory if it doesn't exist - create_dir_if_not_exist(output_dir.as_path())?; - - // write to file - write_to_user_only_file( - output_file.as_path(), - &output_file.display().to_string(), - output.as_bytes(), - )?; - report.push( - output_file - .file_name() - .expect("file name") - .to_string_lossy() - .to_string(), - ); - } - - Ok(match report.len() { - 0 => "no bytecode modules found".to_owned(), - 1 => format!("{}/{}", last_out_dir, report[0]), - _ => format!("{}/{{{}}}", last_out_dir, report.into_iter().join(",")), - }) - } - - fn disassemble(&self, bytecode_path: &Path) -> Result { - let bytecode_bytes = read_from_file(bytecode_path)?; - let move_path = bytecode_path.with_extension(MOVE_EXTENSION); - let source_map_path = bytecode_path.with_extension(SOURCE_MAP_EXTENSION); - - let source = fs::read_to_string(move_path).ok(); - let source_map = source_map_from_file(&source_map_path).ok(); - - let disassembler_options = DisassemblerOptions { - print_code: true, - only_externally_visible: false, - print_basic_blocks: true, - print_locals: true, - }; - let no_loc = Spanned::unsafe_no_loc(()).loc; - let module: CompiledModule; - let script: CompiledScript; - let bytecode = if self.is_script { - script = CompiledScript::deserialize(&bytecode_bytes) - .context("Script blob can't be deserialized")?; - BinaryIndexedView::Script(&script) - } else { - module = CompiledModule::deserialize(&bytecode_bytes) - .context("Module blob can't be deserialized")?; - BinaryIndexedView::Module(&module) - }; - - let mut source_mapping = if let Some(s) = source_map { - SourceMapping::new(s, bytecode) - } else { - SourceMapping::new_from_view(bytecode, no_loc) - .context("Unable to build dummy source mapping")? - }; - - if let Some(source_code) = source { - source_mapping.with_source_code((bytecode_path.display().to_string(), source_code)); - } - - let mut disassembler = Disassembler::new(source_mapping, disassembler_options); - - if let Some(file_path) = &self.code_coverage_path { - disassembler.add_coverage_map( - CoverageMap::from_binary_file(file_path) - .map_err(|_err| { - CliError::UnexpectedError("Unable to read from file_path".to_string()) - })? - .to_unified_exec_map(), - ); - } - - disassembler - .disassemble() - .map_err(|err| CliError::UnexpectedError(format!("Unable to disassemble: {}", err))) - } - - fn decompile(&self, bytecode_path: &Path) -> Result { - let exe = get_revela_path()?; - let to_cli_error = |e| CliError::IO(exe.display().to_string(), e); - let mut cmd = Command::new(exe.as_path()); - cmd.arg(format!("--bytecode={}", bytecode_path.display())); - if self.is_script { - cmd.arg("--script"); - } - let out = cmd.output().map_err(to_cli_error)?; - if out.status.success() { - String::from_utf8(out.stdout).map_err(|err| { - CliError::UnexpectedError(format!( - "output generated by decompiler is not valid utf8: {}", - err - )) - }) - } else { - Err(CliError::UnexpectedError(format!( - "decompiler exited with status {}: {}", - out.status, - String::from_utf8(out.stderr).unwrap_or_default() - ))) - } - } -} diff --git a/crates/aptos/src/move_tool/coverage.rs b/crates/aptos/src/move_tool/coverage.rs deleted file mode 100644 index 102a22e3c9054..0000000000000 --- a/crates/aptos/src/move_tool/coverage.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; -use aptos_framework::extended_checks; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; -use move_compiler::compiled_unit::{CompiledUnit, NamedCompiledModule}; -use move_coverage::{ - coverage_map::CoverageMap, format_csv_summary, format_human_summary, - source_coverage::SourceCoverageBuilder, summary::summarize_inst_cov, -}; -use move_disassembler::disassembler::Disassembler; -use move_package::{compilation::compiled_package::CompiledPackage, BuildConfig, CompilerConfig}; - -/// Display a coverage summary for all modules in a package -/// -#[derive(Debug, Parser)] -pub struct SummaryCoverage { - /// Display function coverage summaries - /// - /// When provided, it will include coverage on a function level - #[clap(long)] - pub summarize_functions: bool, - /// Output CSV data of coverage - #[clap(long = "csv")] - pub output_csv: bool, - /// A filter string to determine which unit tests to compute coverage on - #[clap(long, short)] - pub filter: Option, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -impl SummaryCoverage { - pub fn coverage(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let modules: Vec<_> = package - .root_modules() - .filter_map(|unit| { - let mut retain = true; - if let Some(filter_str) = &self.filter { - if !&unit.unit.name().as_str().contains(filter_str.as_str()) { - retain = false; - } - } - match &unit.unit { - CompiledUnit::Module(NamedCompiledModule { module, .. }) if retain => { - Some(module.clone()) - }, - _ => None, - } - }) - .collect(); - let coverage_map = coverage_map.to_unified_exec_map(); - if self.output_csv { - format_csv_summary( - modules.as_slice(), - &coverage_map, - summarize_inst_cov, - &mut std::io::stdout(), - ) - } else { - format_human_summary( - modules.as_slice(), - &coverage_map, - summarize_inst_cov, - &mut std::io::stdout(), - self.summarize_functions, - ) - } - Ok(()) - } -} - -#[async_trait] -impl CliCommand<()> for SummaryCoverage { - fn command_name(&self) -> &'static str { - "SummaryCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - self.coverage() - } -} - -/// Display coverage information about the module against source code -#[derive(Debug, Parser)] -pub struct SourceCoverage { - #[clap(long = "module")] - pub module_name: String, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for SourceCoverage { - fn command_name(&self) -> &'static str { - "SourceCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let unit = package.get_module_by_name_from_root(&self.module_name)?; - let source_path = &unit.source_path; - let (module, source_map) = match &unit.unit { - CompiledUnit::Module(NamedCompiledModule { - module, source_map, .. - }) => (module, source_map), - _ => panic!("Should all be modules"), - }; - let source_coverage = SourceCoverageBuilder::new(module, &coverage_map, source_map); - source_coverage - .compute_source_coverage(source_path) - .output_source_coverage(&mut std::io::stdout()) - .map_err(|err| CliError::UnexpectedError(format!("Failed to get coverage {}", err))) - } -} - -/// Display coverage information about the module against disassembled bytecode -#[derive(Debug, Parser)] -pub struct BytecodeCoverage { - #[clap(long = "module")] - pub module_name: String, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand<()> for BytecodeCoverage { - fn command_name(&self) -> &'static str { - "BytecodeCoverage" - } - - async fn execute(self) -> CliTypedResult<()> { - let (coverage_map, package) = compile_coverage(self.move_options)?; - let unit = package.get_module_by_name_from_root(&self.module_name)?; - let mut disassembler = Disassembler::from_unit(&unit.unit); - disassembler.add_coverage_map(coverage_map.to_unified_exec_map()); - println!("{}", disassembler.disassemble()?); - Ok(()) - } -} - -fn compile_coverage( - move_options: MovePackageDir, -) -> CliTypedResult<(CoverageMap, CompiledPackage)> { - let config = BuildConfig { - dev_mode: move_options.dev, - additional_named_addresses: move_options.named_addresses(), - test_mode: false, - install_dir: move_options.output_dir.clone(), - compiler_config: CompilerConfig { - known_attributes: extended_checks::get_all_attribute_names().clone(), - skip_attribute_checks: false, - ..Default::default() - }, - ..Default::default() - }; - let path = move_options.get_package_path()?; - let coverage_map = - CoverageMap::from_binary_file(path.join(".coverage_map.mvcov")).map_err(|err| { - CliError::UnexpectedError(format!("Failed to retrieve coverage map {}", err)) - })?; - let package = config - .compile_package(path.as_path(), &mut Vec::new()) - .map_err(|err| CliError::MoveCompilationError(err.to_string()))?; - - Ok((coverage_map, package)) -} - -/// Computes coverage for a package -/// -/// Computes coverage on a previous unit test run for a package. Coverage input must -/// first be built with `aptos move test --coverage` -#[derive(Subcommand)] -pub enum CoveragePackage { - Summary(SummaryCoverage), - Source(SourceCoverage), - Bytecode(BytecodeCoverage), -} - -impl CoveragePackage { - pub async fn execute(self) -> CliResult { - match self { - Self::Summary(tool) => tool.execute_serialized_success().await, - Self::Source(tool) => tool.execute_serialized_success().await, - Self::Bytecode(tool) => tool.execute_serialized_success().await, - } - } -} diff --git a/crates/aptos/src/move_tool/manifest.rs b/crates/aptos/src/move_tool/manifest.rs deleted file mode 100644 index 5850562e26400..0000000000000 --- a/crates/aptos/src/move_tool/manifest.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::types::load_manifest_account_arg; -use aptos_types::account_address::AccountAddress; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::collections::BTreeMap; - -/// A Rust representation of the Move package manifest -/// -/// Note: The original Move package manifest object used by the package system -/// can't be serialized because it uses a symbol mapping -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MovePackageManifest { - pub package: PackageInfo, - pub addresses: BTreeMap, - #[serde(rename = "dev-addresses")] - pub dev_addresses: BTreeMap, - pub dependencies: BTreeMap, - #[serde(rename = "dev-dependencies")] - pub dev_dependencies: BTreeMap, -} -/// Representation of an option address so we can print it as "_" -#[derive(Debug, Clone)] -pub struct ManifestNamedAddress { - pub address: Option, -} - -impl From> for ManifestNamedAddress { - fn from(opt: Option) -> Self { - ManifestNamedAddress { address: opt } - } -} - -impl From for Option { - fn from(addr: ManifestNamedAddress) -> Self { - addr.address - } -} - -impl Serialize for ManifestNamedAddress { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if let Some(address) = self.address { - serializer.serialize_str(&address.to_hex_literal()) - } else { - serializer.serialize_str("_") - } - } -} - -impl<'de> Deserialize<'de> for ManifestNamedAddress { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let str = ::deserialize(deserializer)?; - Ok(ManifestNamedAddress { - // TODO: Cleanup unwrap - address: load_manifest_account_arg(&str).unwrap(), - }) - } -} - -/// A Rust representation of a move dependency -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Dependency { - #[serde(skip_serializing_if = "Option::is_none")] - pub local: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub git: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub rev: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub subdir: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub aptos: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub address: Option, -} - -/// A Rust representation of the package info -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PackageInfo { - pub name: String, - pub version: String, - pub authors: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub license: Option, -} diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs deleted file mode 100644 index d02f076e61f77..0000000000000 --- a/crates/aptos/src/move_tool/mod.rs +++ /dev/null @@ -1,1940 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::derive_resource_account::ResourceAccountSeed, - common::{ - types::{ - load_account_arg, ArgWithTypeJSON, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EntryFunctionArguments, EntryFunctionArgumentsJSON, - MoveManifestAccountWrapper, MovePackageDir, OverrideSizeCheckOption, ProfileOptions, - PromptOptions, RestOptions, SaveFile, ScriptFunctionArguments, TransactionOptions, - TransactionSummary, - }, - utils::{ - check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, - profile_or_submit, prompt_yes_with_override, write_to_file, - }, - }, - governance::CompileScriptFunction, - move_tool::{ - bytecode::{Decompile, Disassemble}, - coverage::SummaryCoverage, - manifest::{Dependency, ManifestNamedAddress, MovePackageManifest, PackageInfo}, - }, - CliCommand, CliResult, -}; -use aptos_crypto::HashValue; -use aptos_framework::{ - docgen::DocgenOptions, extended_checks, natives::code::UpgradePolicy, prover::ProverOptions, - BuildOptions, BuiltPackage, -}; -use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters}; -use aptos_rest_client::aptos_api_types::{ - EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, -}; -use aptos_types::{ - account_address::{create_resource_address, AccountAddress}, - object_address::create_object_code_deployment_address, - on_chain_config::aptos_test_feature_flags_genesis, - transaction::{TransactionArgument, TransactionPayload}, -}; -use async_trait::async_trait; -use clap::{Parser, Subcommand, ValueEnum}; -use itertools::Itertools; -use move_cli::{self, base::test::UnitTestResult}; -use move_command_line_common::env::MOVE_HOME; -use move_core_types::{identifier::Identifier, language_storage::ModuleId, u256::U256}; -use move_package::{ - source_package::layout::SourcePackageLayout, BuildConfig, CompilerConfig, CompilerVersion, -}; -use move_unit_test::UnitTestingConfig; -pub use package_hooks::*; -use serde::{Deserialize, Serialize}; -use serde_json::json; -use std::{ - collections::BTreeMap, - fmt::{Display, Formatter}, - path::{Path, PathBuf}, - str::FromStr, -}; -pub use stored_package::*; -use tokio::task; - -mod aptos_debug_natives; -mod bytecode; -pub mod coverage; -mod manifest; -pub mod package_hooks; -mod show; -pub mod stored_package; - -/// Tool for Move related operations -/// -/// This tool lets you compile, test, and publish Move code, in addition -/// to run any other tools that help run, verify, or provide information -/// about this code. -#[derive(Subcommand)] -pub enum MoveTool { - BuildPublishPayload(BuildPublishPayload), - Clean(CleanPackage), - Compile(CompilePackage), - CompileScript(CompileScript), - #[clap(subcommand)] - Coverage(coverage::CoveragePackage), - CreateObjectAndPublishPackage(CreateObjectAndPublishPackage), - UpgradeObjectPackage(UpgradeObjectPackage), - CreateResourceAccountAndPublishPackage(CreateResourceAccountAndPublishPackage), - Disassemble(Disassemble), - Decompile(Decompile), - Document(DocumentPackage), - Download(DownloadPackage), - Init(InitPackage), - List(ListPackage), - Prove(ProvePackage), - Publish(PublishPackage), - Run(RunFunction), - RunScript(RunScript), - #[clap(subcommand, hide = true)] - Show(show::ShowTool), - Test(TestPackage), - VerifyPackage(VerifyPackage), - View(ViewFunction), -} - -impl MoveTool { - pub async fn execute(self) -> CliResult { - match self { - MoveTool::BuildPublishPayload(tool) => tool.execute_serialized().await, - MoveTool::Clean(tool) => tool.execute_serialized().await, - MoveTool::Compile(tool) => tool.execute_serialized().await, - MoveTool::CompileScript(tool) => tool.execute_serialized().await, - MoveTool::Coverage(tool) => tool.execute().await, - MoveTool::CreateObjectAndPublishPackage(tool) => { - tool.execute_serialized_success().await - }, - MoveTool::UpgradeObjectPackage(tool) => tool.execute_serialized_success().await, - MoveTool::CreateResourceAccountAndPublishPackage(tool) => { - tool.execute_serialized_success().await - }, - MoveTool::Disassemble(tool) => tool.execute_serialized().await, - MoveTool::Decompile(tool) => tool.execute_serialized().await, - MoveTool::Document(tool) => tool.execute_serialized().await, - MoveTool::Download(tool) => tool.execute_serialized().await, - MoveTool::Init(tool) => tool.execute_serialized_success().await, - MoveTool::List(tool) => tool.execute_serialized().await, - MoveTool::Prove(tool) => tool.execute_serialized().await, - MoveTool::Publish(tool) => tool.execute_serialized().await, - MoveTool::Run(tool) => tool.execute_serialized().await, - MoveTool::RunScript(tool) => tool.execute_serialized().await, - MoveTool::Show(tool) => tool.execute_serialized().await, - MoveTool::Test(tool) => tool.execute_serialized().await, - MoveTool::VerifyPackage(tool) => tool.execute_serialized().await, - MoveTool::View(tool) => tool.execute_serialized().await, - } - } -} - -#[derive(Parser)] -pub struct FrameworkPackageArgs { - /// Git revision or branch for the Aptos framework - /// - /// This is mutually exclusive with `--framework-local-dir` - #[clap(long, group = "framework_package_args")] - pub(crate) framework_git_rev: Option, - - /// Local framework directory for the Aptos framework - /// - /// This is mutually exclusive with `--framework-git-rev` - #[clap(long, value_parser, group = "framework_package_args")] - pub(crate) framework_local_dir: Option, - - /// Skip pulling the latest git dependencies - /// - /// If you don't have a network connection, the compiler may fail due - /// to no ability to pull git dependencies. This will allow overriding - /// this for local development. - #[clap(long)] - pub(crate) skip_fetch_latest_git_deps: bool, -} - -impl FrameworkPackageArgs { - pub fn init_move_dir( - &self, - package_dir: &Path, - name: &str, - addresses: BTreeMap, - prompt_options: PromptOptions, - ) -> CliTypedResult<()> { - const APTOS_FRAMEWORK: &str = "AptosFramework"; - const APTOS_GIT_PATH: &str = "https://github.com/aptos-labs/aptos-core.git"; - const SUBDIR_PATH: &str = "aptos-move/framework/aptos-framework"; - const DEFAULT_BRANCH: &str = "mainnet"; - - let move_toml = package_dir.join(SourcePackageLayout::Manifest.path()); - check_if_file_exists(move_toml.as_path(), prompt_options)?; - create_dir_if_not_exist( - package_dir - .join(SourcePackageLayout::Sources.path()) - .as_path(), - )?; - create_dir_if_not_exist( - package_dir - .join(SourcePackageLayout::Tests.path()) - .as_path(), - )?; - create_dir_if_not_exist( - package_dir - .join(SourcePackageLayout::Scripts.path()) - .as_path(), - )?; - - // Add the framework dependency if it's provided - let mut dependencies = BTreeMap::new(); - if let Some(ref path) = self.framework_local_dir { - dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { - local: Some(path.display().to_string()), - git: None, - rev: None, - subdir: None, - aptos: None, - address: None, - }); - } else { - let git_rev = self.framework_git_rev.as_deref().unwrap_or(DEFAULT_BRANCH); - dependencies.insert(APTOS_FRAMEWORK.to_string(), Dependency { - local: None, - git: Some(APTOS_GIT_PATH.to_string()), - rev: Some(git_rev.to_string()), - subdir: Some(SUBDIR_PATH.to_string()), - aptos: None, - address: None, - }); - } - - let manifest = MovePackageManifest { - package: PackageInfo { - name: name.to_string(), - version: "1.0.0".to_string(), - license: None, - authors: vec![], - }, - addresses, - dependencies, - dev_addresses: Default::default(), - dev_dependencies: Default::default(), - }; - - write_to_file( - move_toml.as_path(), - SourcePackageLayout::Manifest.location_str(), - toml::to_string_pretty(&manifest) - .map_err(|err| CliError::UnexpectedError(err.to_string()))? - .as_bytes(), - ) - } -} - -/// Creates a new Move package at the given location -/// -/// This will create a directory for a Move package and a corresponding -/// `Move.toml` file. -#[derive(Parser)] -pub struct InitPackage { - /// Name of the new Move package - #[clap(long)] - pub(crate) name: String, - - /// Directory to create the new Move package - #[clap(long, value_parser)] - pub(crate) package_dir: Option, - - /// Named addresses for the move binary - /// - /// Allows for an address to be put into the Move.toml, or a placeholder `_` - /// - /// Example: alice=0x1234,bob=0x5678,greg=_ - /// - /// Note: This will fail if there are duplicates in the Move.toml file remove those first. - #[clap( - long, - value_parser = crate::common::utils::parse_map::, - default_value = "" - )] - pub(crate) named_addresses: BTreeMap, - - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, - - #[clap(flatten)] - pub(crate) framework_package_args: FrameworkPackageArgs, -} - -#[async_trait] -impl CliCommand<()> for InitPackage { - fn command_name(&self) -> &'static str { - "InitPackage" - } - - async fn execute(self) -> CliTypedResult<()> { - let package_dir = dir_default_to_current(self.package_dir.clone())?; - let addresses = self - .named_addresses - .into_iter() - .map(|(key, value)| (key, value.account_address.into())) - .collect(); - - self.framework_package_args.init_move_dir( - package_dir.as_path(), - &self.name, - addresses, - self.prompt_options, - ) - } -} - -/// Compiles a package and returns the associated ModuleIds -#[derive(Parser)] -pub struct CompilePackage { - /// Save the package metadata in the package's build directory - /// - /// If set, package metadata should be generated and stored in the package's build directory. - /// This metadata can be used to construct a transaction to publish a package. - #[clap(long)] - pub(crate) save_metadata: bool, - - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand> for CompilePackage { - fn command_name(&self) -> &'static str { - "CompilePackage" - } - - async fn execute(self) -> CliTypedResult> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - ..self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ) - }; - let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - if self.save_metadata { - pack.extract_metadata_and_save()?; - } - let ids = pack - .modules() - .map(|m| m.self_id().to_string()) - .collect::>(); - // TODO: Also say how many scripts are compiled - Ok(ids) - } -} - -/// Compiles a Move script into bytecode -/// -/// Compiles a script into bytecode and provides a hash of the bytecode. -/// This can then be run with `aptos move run-script` -#[derive(Parser)] -pub struct CompileScript { - #[clap(long, value_parser)] - pub output_file: Option, - #[clap(flatten)] - pub move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand for CompileScript { - fn command_name(&self) -> &'static str { - "CompileScript" - } - - async fn execute(self) -> CliTypedResult { - let (bytecode, script_hash) = self.compile_script().await?; - let script_location = self.output_file.unwrap_or_else(|| { - self.move_options - .get_package_path() - .unwrap() - .join("script.mv") - }); - write_to_file(script_location.as_path(), "Script", bytecode.as_slice())?; - Ok(CompileScriptOutput { - script_location, - script_hash, - }) - } -} - -impl CompileScript { - async fn compile_script(&self) -> CliTypedResult<(Vec, HashValue)> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - ..IncludedArtifacts::None.build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ) - }; - let package_dir = self.move_options.get_package_path()?; - let pack = BuiltPackage::build(package_dir, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - let scripts_count = pack.script_count(); - if scripts_count != 1 { - return Err(CliError::UnexpectedError(format!( - "Only one script can be prepared a time. Make sure one and only one script file \ - is included in the Move package. Found {} scripts.", - scripts_count - ))); - } - - let bytecode = pack.extract_script_code().pop().unwrap(); - let script_hash = HashValue::sha3_256_of(bytecode.as_slice()); - Ok((bytecode, script_hash)) - } -} - -#[derive(Debug, Serialize)] -pub struct CompileScriptOutput { - pub script_location: PathBuf, - pub script_hash: HashValue, -} - -/// Runs Move unit tests for a package -/// -/// This will run Move unit tests against a package with debug mode -/// turned on. Note, that move code warnings currently block tests from running. -#[derive(Parser)] -pub struct TestPackage { - /// A filter string to determine which unit tests to run - #[clap(long, short)] - pub filter: Option, - - /// A boolean value to skip warnings. - #[clap(long)] - pub ignore_compile_warnings: bool, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - - /// The maximum number of instructions that can be executed by a test - /// - /// If set, the number of instructions executed by one test will be bounded - // TODO: Remove short, it's against the style guidelines, and update the name here - #[clap( - name = "instructions", - default_value_t = 100000, - short = 'i', - long = "instructions" - )] - pub instruction_execution_bound: u64, - - /// Collect coverage information for later use with the various `aptos move coverage` subcommands - #[clap(long = "coverage")] - pub compute_coverage: bool, - - /// Dump storage state on failure. - #[clap(long = "dump")] - pub dump_state: bool, -} - -#[async_trait] -impl CliCommand<&'static str> for TestPackage { - fn command_name(&self) -> &'static str { - "TestPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let known_attributes = extended_checks::get_all_attribute_names(); - let mut config = BuildConfig { - dev_mode: self.move_options.dev, - additional_named_addresses: self.move_options.named_addresses(), - test_mode: true, - full_model_generation: self.move_options.check_test_code, - install_dir: self.move_options.output_dir.clone(), - skip_fetch_latest_git_deps: self.move_options.skip_fetch_latest_git_deps, - compiler_config: CompilerConfig { - known_attributes: known_attributes.clone(), - skip_attribute_checks: self.move_options.skip_attribute_checks, - compiler_version: self.move_options.compiler_version, - ..Default::default() - }, - ..Default::default() - }; - - let path = self.move_options.get_package_path()?; - let result = move_cli::base::test::run_move_unit_tests( - path.as_path(), - config.clone(), - UnitTestingConfig { - filter: self.filter.clone(), - report_stacktrace_on_abort: true, - report_storage_on_error: self.dump_state, - ignore_compile_warnings: self.ignore_compile_warnings, - ..UnitTestingConfig::default_with_bound(None) - }, - // TODO(Gas): we may want to switch to non-zero costs in the future - aptos_debug_natives::aptos_debug_natives( - NativeGasParameters::zeros(), - MiscGasParameters::zeros(), - ), - aptos_test_feature_flags_genesis(), - None, - self.compute_coverage, - &mut std::io::stdout(), - ) - .map_err(|err| CliError::UnexpectedError(format!("Failed to run tests: {:#}", err)))?; - - // Print coverage summary if --coverage is set - if self.compute_coverage { - // TODO: config seems to be dead here. - config.test_mode = false; - let summary = SummaryCoverage { - summarize_functions: false, - output_csv: false, - filter: self.filter, - move_options: self.move_options, - }; - summary.coverage()?; - - println!("Please use `aptos move coverage -h` for more detailed source or bytecode test coverage of this package"); - } - - match result { - UnitTestResult::Success => Ok("Success"), - UnitTestResult::Failure => Err(CliError::MoveTestError), - } - } -} - -/// Proves a Move package -/// -/// This is a tool for formal verification of a Move package using -/// the Move prover -#[derive(Parser)] -pub struct ProvePackage { - #[clap(flatten)] - move_options: MovePackageDir, - - #[clap(flatten)] - prover_options: ProverOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for ProvePackage { - fn command_name(&self) -> &'static str { - "ProvePackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let ProvePackage { - move_options, - prover_options, - } = self; - - let result = task::spawn_blocking(move || { - prover_options.prove( - move_options.dev, - move_options.get_package_path()?.as_path(), - move_options.named_addresses(), - move_options.bytecode_version, - move_options.skip_attribute_checks, - extended_checks::get_all_attribute_names(), - ) - }) - .await - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - match result { - Ok(_) => Ok("Success"), - Err(e) => Err(CliError::MoveProverError(format!("{:#}", e))), - } - } -} - -/// Documents a Move package -/// -/// This converts the content of the package into markdown for documentation. -#[derive(Parser)] -pub struct DocumentPackage { - #[clap(flatten)] - move_options: MovePackageDir, - - #[clap(flatten)] - docgen_options: DocgenOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for DocumentPackage { - fn command_name(&self) -> &'static str { - "DocumentPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let DocumentPackage { - move_options, - docgen_options, - } = self; - let build_options = BuildOptions { - dev: move_options.dev, - with_srcs: false, - with_abis: false, - with_source_maps: false, - with_error_map: false, - with_docs: true, - install_dir: None, - named_addresses: move_options.named_addresses(), - docgen_options: Some(docgen_options), - skip_fetch_latest_git_deps: move_options.skip_fetch_latest_git_deps, - bytecode_version: move_options.bytecode_version, - compiler_version: move_options.compiler_version, - skip_attribute_checks: move_options.skip_attribute_checks, - check_test_code: move_options.check_test_code, - known_attributes: extended_checks::get_all_attribute_names().clone(), - }; - BuiltPackage::build(move_options.get_package_path()?, build_options)?; - Ok("succeeded") - } -} - -#[derive(Parser)] -pub struct IncludedArtifactsArgs { - /// Artifacts to be generated when building the package - /// - /// Which artifacts to include in the package. This can be one of `none`, `sparse`, and - /// `all`. `none` is the most compact form and does not allow to reconstruct a source - /// package from chain; `sparse` is the minimal set of artifacts needed to reconstruct - /// a source package; `all` includes all available artifacts. The choice of included - /// artifacts heavily influences the size and therefore gas cost of publishing: `none` - /// is the size of bytecode alone; `sparse` is roughly 2 times as much; and `all` 3-4 - /// as much. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, -} - -/// Publishes the modules in a Move package to the Aptos blockchain -#[derive(Parser)] -pub struct PublishPackage { - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -struct PackagePublicationData { - metadata_serialized: Vec, - compiled_units: Vec>, - payload: TransactionPayload, -} - -/// Build a publication transaction payload and store it in a JSON output file. -#[derive(Parser)] -pub struct BuildPublishPayload { - #[clap(flatten)] - publish_package: PublishPackage, - /// JSON output file to write publication transaction to - #[clap(long, value_parser)] - pub(crate) json_output_file: PathBuf, -} - -impl TryInto for &PublishPackage { - type Error = CliError; - - fn try_into(self) -> Result { - let package_path = self.move_options.get_package_path()?; - let options = self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ); - let package = BuiltPackage::build(package_path, options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - let compiled_units = package.extract_code(); - let metadata_serialized = - bcs::to_bytes(&package.extract_metadata()?).expect("PackageMetadata has BCS"); - let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( - metadata_serialized.clone(), - compiled_units.clone(), - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - if !self.override_size_check_option.value && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include fewer artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - Ok(PackagePublicationData { - metadata_serialized, - compiled_units, - payload, - }) - } -} - -#[derive(ValueEnum, Clone, Copy, Debug)] -pub enum IncludedArtifacts { - None, - Sparse, - All, -} - -impl Display for IncludedArtifacts { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use IncludedArtifacts::*; - match self { - None => f.write_str("none"), - Sparse => f.write_str("sparse"), - All => f.write_str("all"), - } - } -} - -impl FromStr for IncludedArtifacts { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - use IncludedArtifacts::*; - match s { - "none" => Ok(None), - "sparse" => Ok(Sparse), - "all" => Ok(All), - _ => Err("unknown variant"), - } - } -} - -impl IncludedArtifacts { - pub(crate) fn build_options( - self, - dev: bool, - skip_fetch_latest_git_deps: bool, - named_addresses: BTreeMap, - bytecode_version: Option, - compiler_version: Option, - skip_attribute_checks: bool, - check_test_code: bool, - ) -> BuildOptions { - use IncludedArtifacts::*; - match self { - None => BuildOptions { - dev, - with_srcs: false, - with_abis: false, - with_source_maps: false, - // Always enable error map bytecode injection - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - compiler_version, - skip_attribute_checks, - check_test_code, - known_attributes: extended_checks::get_all_attribute_names().clone(), - ..BuildOptions::default() - }, - Sparse => BuildOptions { - dev, - with_srcs: true, - with_abis: false, - with_source_maps: false, - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - compiler_version, - skip_attribute_checks, - check_test_code, - known_attributes: extended_checks::get_all_attribute_names().clone(), - ..BuildOptions::default() - }, - All => BuildOptions { - dev, - with_srcs: true, - with_abis: true, - with_source_maps: true, - with_error_map: true, - named_addresses, - skip_fetch_latest_git_deps, - bytecode_version, - compiler_version, - skip_attribute_checks, - check_test_code, - known_attributes: extended_checks::get_all_attribute_names().clone(), - ..BuildOptions::default() - }, - } - } -} - -pub const MAX_PUBLISH_PACKAGE_SIZE: usize = 60_000; - -#[async_trait] -impl CliCommand for PublishPackage { - fn command_name(&self) -> &'static str { - "PublishPackage" - } - - async fn execute(self) -> CliTypedResult { - let package_publication_data: PackagePublicationData = (&self).try_into()?; - profile_or_submit(package_publication_data.payload, &self.txn_options).await - } -} - -#[async_trait] -impl CliCommand for BuildPublishPayload { - fn command_name(&self) -> &'static str { - "BuildPublishPayload" - } - - async fn execute(self) -> CliTypedResult { - let package_publication_data: PackagePublicationData = - (&self.publish_package).try_into()?; - // Extract entry function data from publication payload. - let entry_function = package_publication_data.payload.into_entry_function(); - let entry_function_id = EntryFunctionId { - module: MoveModuleId::from(entry_function.module().clone()), - name: IdentifierWrapper::from(entry_function.function()), - }; - let package_metadata_hex = - HexEncodedBytes(package_publication_data.metadata_serialized).to_string(); - let package_code_hex_vec: Vec = package_publication_data - .compiled_units - .into_iter() - .map(|element| HexEncodedBytes(element).to_string()) - .collect(); - // Construct entry function JSON file representation from entry function data. - let json = EntryFunctionArgumentsJSON { - function_id: entry_function_id.to_string(), - type_args: vec![], - args: vec![ - ArgWithTypeJSON { - arg_type: "hex".to_string(), - value: serde_json::Value::String(package_metadata_hex), - }, - ArgWithTypeJSON { - arg_type: "hex".to_string(), - value: json!(package_code_hex_vec), - }, - ], - }; - // Create save file options for checking and saving file to disk. - let save_file = SaveFile { - output_file: self.json_output_file, - prompt_options: self.publish_package.txn_options.prompt_options, - }; - save_file.check_file()?; - save_file.save_to_file( - "Publication entry function JSON file", - serde_json::to_string_pretty(&json) - .map_err(|err| CliError::UnexpectedError(format!("{}", err)))? - .as_bytes(), - )?; - Ok(format!( - "Publication payload entry function JSON file saved to {}", - save_file.output_file.display() - )) - } -} - -/// Publishes the modules in a Move package to the Aptos blockchain, under an object. -#[derive(Parser)] -pub struct CreateObjectAndPublishPackage { - /// The named address for compiling and using in the contract - /// - /// This will take the derived account address for the object and put it in this location - #[clap(long)] - pub(crate) address_name: String, - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateObjectAndPublishPackage { - fn command_name(&self) -> &'static str { - "CreateObjectAndPublishPackage" - } - - async fn execute(mut self) -> CliTypedResult { - let sender_address = self.txn_options.get_public_key_and_address()?.1; - let sequence_number = self.txn_options.sequence_number(sender_address).await? + 1; - let object_address = create_object_code_deployment_address(sender_address, sequence_number); - - self.move_options - .add_named_address(self.address_name, object_address.to_string()); - - let options = self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ); - let package = BuiltPackage::build(self.move_options.get_package_path()?, options)?; - let message = format!( - "Do you want to publish this package at object address {}", - object_address - ); - prompt_yes_with_override(&message, self.txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::object_code_deployment_publish( - bcs::to_bytes(&package.extract_metadata()?) - .expect("Failed to serialize PackageMetadata"), - package.extract_code(), - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - - if !self.override_size_check_option.value && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - let result = self - .txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from); - - if result.is_ok() { - println!( - "Code was successfully deployed to object address {}.", - object_address - ); - } - result - } -} - -#[derive(Parser)] -pub struct UpgradeObjectPackage { - /// Address of the object the package was deployed to - /// - /// This must be an already deployed object containing the package - /// if the package is not already created, it will fail. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) object_address: AccountAddress, - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for UpgradeObjectPackage { - fn command_name(&self) -> &'static str { - "UpgradeObjectPackage" - } - - async fn execute(self) -> CliTypedResult { - let options = self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ); - let built_package = BuiltPackage::build(self.move_options.get_package_path()?, options)?; - let url = self - .txn_options - .rest_options - .url(&self.txn_options.profile_options)?; - - // Get the `PackageRegistry` at the given object address. - let registry = CachedPackageRegistry::create(url, self.object_address, false).await?; - let package = registry - .get_package(built_package.name()) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - - if package.upgrade_policy() == UpgradePolicy::immutable() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `immutable` cannot be upgraded".to_owned(), - )); - } - - let message = format!( - "Do you want to upgrade the package '{}' at object address {}", - package.name(), - self.object_address - ); - prompt_yes_with_override(&message, self.txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::object_code_deployment_upgrade( - bcs::to_bytes(&built_package.extract_metadata()?) - .expect("Failed to serialize PackageMetadata"), - built_package.extract_code(), - self.object_address, - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - - if !self.override_size_check_option.value && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - let result = self - .txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from); - - if result.is_ok() { - println!( - "Code was successfully upgraded at object address {}.", - self.object_address - ); - } - result - } -} - -/// Publishes the modules in a Move package to the Aptos blockchain under a resource account -#[derive(Parser)] -pub struct CreateResourceAccountAndPublishPackage { - /// The named address for compiling and using in the contract - /// - /// This will take the derived account address for the resource account and put it in this location - #[clap(long)] - pub(crate) address_name: String, - - #[clap(flatten)] - pub(crate) override_size_check_option: OverrideSizeCheckOption, - - #[clap(flatten)] - pub(crate) seed_args: ResourceAccountSeed, - #[clap(flatten)] - pub(crate) included_artifacts_args: IncludedArtifactsArgs, - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateResourceAccountAndPublishPackage { - fn command_name(&self) -> &'static str { - "ResourceAccountPublishPackage" - } - - async fn execute(self) -> CliTypedResult { - let CreateResourceAccountAndPublishPackage { - address_name, - mut move_options, - txn_options, - override_size_check_option, - included_artifacts_args, - seed_args, - } = self; - - let account = if let Some(Some(account)) = CliConfig::load_profile( - txn_options.profile_options.profile_name(), - ConfigSearchMode::CurrentDirAndParents, - )? - .map(|p| p.account) - { - account - } else { - return Err(CliError::CommandArgumentError( - "Please provide an account using --profile or run aptos init".to_string(), - )); - }; - let seed = seed_args.seed()?; - - let resource_address = create_resource_address(account, &seed); - move_options.add_named_address(address_name, resource_address.to_string()); - - let package_path = move_options.get_package_path()?; - let options = included_artifacts_args.included_artifacts.build_options( - move_options.dev, - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - move_options.compiler_version, - move_options.skip_attribute_checks, - move_options.check_test_code, - ); - let package = BuiltPackage::build(package_path, options)?; - let compiled_units = package.extract_code(); - - // Send the compiled module and metadata using the code::publish_package_txn. - let metadata = package.extract_metadata()?; - - let message = format!( - "Do you want to publish this package under the resource account's address {}?", - resource_address - ); - prompt_yes_with_override(&message, txn_options.prompt_options)?; - - let payload = aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account_and_publish_package( - seed, - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - compiled_units, - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - if !override_size_check_option.value && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - txn_options - .submit_transaction(payload) - .await - .map(TransactionSummary::from) - } -} - -/// Downloads a package and stores it in a directory named after the package -/// -/// This lets you retrieve packages directly from the blockchain for inspection -/// and use as a local dependency in testing. -#[derive(Parser)] -pub struct DownloadPackage { - /// Address of the account containing the package - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - /// Name of the package - #[clap(long)] - pub package: String, - - /// Directory to store downloaded package. Defaults to the current directory. - #[clap(long, value_parser)] - pub output_dir: Option, - - /// Whether to download the bytecode of the package. - #[clap(long, short)] - pub bytecode: bool, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - /// Print metadata of the package - #[clap(long)] - pub print_metadata: bool, -} - -#[async_trait] -impl CliCommand<&'static str> for DownloadPackage { - fn command_name(&self) -> &'static str { - "DownloadPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, self.bytecode).await?; - let output_dir = dir_default_to_current(self.output_dir)?; - - let package = registry - .get_package(self.package) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - if package.upgrade_policy() == UpgradePolicy::arbitrary() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `arbitrary` cannot be downloaded \ - since it is not safe to depend on such packages." - .to_owned(), - )); - } - if self.print_metadata { - println!("{}", package); - } - let package_path = output_dir.join(package.name()); - package - .save_package_to_disk(package_path.as_path()) - .map_err(|e| CliError::UnexpectedError(format!("Failed to save package: {}", e)))?; - if self.bytecode { - for module in package.module_names() { - if let Some(bytecode) = registry.get_bytecode(module).await? { - package.save_bytecode_to_disk(package_path.as_path(), module, bytecode)? - } - } - }; - println!( - "Saved package with {} module(s) to `{}`", - package.module_names().len(), - package_path.display() - ); - Ok("Download succeeded") - } -} - -/// Downloads a package and verifies the bytecode -/// -/// Downloads the package from onchain and verifies the bytecode matches a local compilation of the Move code -#[derive(Parser)] -pub struct VerifyPackage { - /// Address of the account containing the package - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - /// Artifacts to be generated when building this package. - #[clap(long, default_value_t = IncludedArtifacts::Sparse)] - pub(crate) included_artifacts: IncludedArtifacts, - - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for VerifyPackage { - fn command_name(&self) -> &'static str { - "VerifyPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - // First build the package locally to get the package metadata - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - bytecode_version: self.move_options.bytecode_version, - ..self.included_artifacts.build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ) - }; - let pack = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - let compiled_metadata = pack.extract_metadata()?; - - // Now pull the compiled package - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, false).await?; - let package = registry - .get_package(pack.name()) - .await - .map_err(|s| CliError::CommandArgumentError(s.to_string()))?; - - // We can't check the arbitrary, because it could change on us - if package.upgrade_policy() == UpgradePolicy::arbitrary() { - return Err(CliError::CommandArgumentError( - "A package with upgrade policy `arbitrary` cannot be downloaded \ - since it is not safe to depend on such packages." - .to_owned(), - )); - } - - // Verify that the source digest matches - package.verify(&compiled_metadata)?; - - Ok("Successfully verified source of package") - } -} - -/// Lists information about packages and modules on-chain for an account -#[derive(Parser)] -pub struct ListPackage { - /// Address of the account for which to list packages. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) account: AccountAddress, - - /// Type of items to query - /// - /// Current supported types `[packages]` - #[clap(long, default_value_t = MoveListQuery::Packages)] - query: MoveListQuery, - - #[clap(flatten)] - rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[derive(ValueEnum, Clone, Copy, Debug)] -pub enum MoveListQuery { - Packages, -} - -impl Display for MoveListQuery { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - MoveListQuery::Packages => "packages", - }) - } -} - -impl FromStr for MoveListQuery { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "packages" => Ok(MoveListQuery::Packages), - _ => Err("Invalid query. Valid values are modules, packages"), - } - } -} - -#[async_trait] -impl CliCommand<&'static str> for ListPackage { - fn command_name(&self) -> &'static str { - "ListPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let url = self.rest_options.url(&self.profile_options)?; - let registry = CachedPackageRegistry::create(url, self.account, false).await?; - match self.query { - MoveListQuery::Packages => { - for name in registry.package_names() { - let data = registry.get_package(name).await?; - println!("package {}", data.name()); - println!(" upgrade_policy: {}", data.upgrade_policy()); - println!(" upgrade_number: {}", data.upgrade_number()); - println!(" source_digest: {}", data.source_digest()); - println!(" modules: {}", data.module_names().into_iter().join(", ")); - } - }, - } - Ok("list succeeded") - } -} - -/// Cleans derived artifacts of a package. -#[derive(Parser)] -pub struct CleanPackage { - #[clap(flatten)] - pub(crate) move_options: MovePackageDir, - #[clap(flatten)] - pub(crate) prompt_options: PromptOptions, -} - -#[async_trait] -impl CliCommand<&'static str> for CleanPackage { - fn command_name(&self) -> &'static str { - "CleanPackage" - } - - async fn execute(self) -> CliTypedResult<&'static str> { - let path = self.move_options.get_package_path()?; - let build_dir = path.join("build"); - // Only remove the build dir if it exists, allowing for users to still clean their cache - if build_dir.exists() { - std::fs::remove_dir_all(build_dir.as_path()) - .map_err(|e| CliError::IO(build_dir.display().to_string(), e))?; - } - - let move_dir = PathBuf::from(MOVE_HOME.as_str()); - if move_dir.exists() - && prompt_yes_with_override( - &format!( - "Do you also want to delete the local package download cache at `{}`?", - move_dir.display() - ), - self.prompt_options, - ) - .is_ok() - { - std::fs::remove_dir_all(move_dir.as_path()) - .map_err(|e| CliError::IO(move_dir.display().to_string(), e))?; - } - Ok("succeeded") - } -} - -/// Run a Move function -#[derive(Parser)] -pub struct RunFunction { - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for RunFunction { - fn command_name(&self) -> &'static str { - "RunFunction" - } - - async fn execute(self) -> CliTypedResult { - profile_or_submit( - TransactionPayload::EntryFunction(self.entry_function_args.try_into()?), - &self.txn_options, - ) - .await - } -} - -/// Run a view function -#[derive(Parser)] -pub struct ViewFunction { - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for ViewFunction { - fn command_name(&self) -> &'static str { - "RunViewFunction" - } - - async fn execute(self) -> CliTypedResult> { - self.txn_options - .view(self.entry_function_args.try_into()?) - .await - } -} - -/// Run a Move script -#[derive(Parser)] -pub struct RunScript { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) compile_proposal_args: CompileScriptFunction, - #[clap(flatten)] - pub(crate) script_function_args: ScriptFunctionArguments, -} - -#[async_trait] -impl CliCommand for RunScript { - fn command_name(&self) -> &'static str { - "RunScript" - } - - async fn execute(self) -> CliTypedResult { - let (bytecode, _script_hash) = self - .compile_proposal_args - .compile("RunScript", self.txn_options.prompt_options)?; - - profile_or_submit( - self.script_function_args.create_script_payload(bytecode)?, - &self.txn_options, - ) - .await - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum FunctionArgType { - Address, - Bool, - Hex, - String, - U8, - U16, - U32, - U64, - U128, - U256, - Raw, -} - -impl Display for FunctionArgType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - FunctionArgType::Address => write!(f, "address"), - FunctionArgType::Bool => write!(f, "bool"), - FunctionArgType::Hex => write!(f, "hex"), - FunctionArgType::String => write!(f, "string"), - FunctionArgType::U8 => write!(f, "u8"), - FunctionArgType::U16 => write!(f, "u16"), - FunctionArgType::U32 => write!(f, "u32"), - FunctionArgType::U64 => write!(f, "u64"), - FunctionArgType::U128 => write!(f, "u128"), - FunctionArgType::U256 => write!(f, "u256"), - FunctionArgType::Raw => write!(f, "raw"), - } - } -} - -impl FunctionArgType { - /// Parse a standalone argument (not a vector) from string slice into BCS representation. - fn parse_arg_str(&self, arg: &str) -> CliTypedResult> { - match self { - FunctionArgType::Address => bcs::to_bytes( - &load_account_arg(arg) - .map_err(|err| CliError::UnableToParse("address", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Bool => bcs::to_bytes( - &bool::from_str(arg) - .map_err(|err| CliError::UnableToParse("bool", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Hex => bcs::to_bytes( - HexEncodedBytes::from_str(arg) - .map_err(|err| CliError::UnableToParse("hex", err.to_string()))? - .inner(), - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::String => bcs::to_bytes(arg).map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U8 => bcs::to_bytes( - &u8::from_str(arg).map_err(|err| CliError::UnableToParse("u8", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U16 => bcs::to_bytes( - &u16::from_str(arg) - .map_err(|err| CliError::UnableToParse("u16", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U32 => bcs::to_bytes( - &u32::from_str(arg) - .map_err(|err| CliError::UnableToParse("u32", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U64 => bcs::to_bytes( - &u64::from_str(arg) - .map_err(|err| CliError::UnableToParse("u64", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U128 => bcs::to_bytes( - &u128::from_str(arg) - .map_err(|err| CliError::UnableToParse("u128", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::U256 => bcs::to_bytes( - &U256::from_str(arg) - .map_err(|err| CliError::UnableToParse("u256", err.to_string()))?, - ) - .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Raw => Ok(HexEncodedBytes::from_str(arg) - .map_err(|err| CliError::UnableToParse("raw", err.to_string()))? - .inner() - .to_vec()), - } - } - - /// Recursively parse argument JSON into BCS representation. - pub fn parse_arg_json(&self, arg: &serde_json::Value) -> CliTypedResult { - match arg { - serde_json::Value::Bool(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.to_string().as_str())?, - }), - serde_json::Value::Number(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.to_string().as_str())?, - }), - serde_json::Value::String(value) => Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: 0, - arg: self.parse_arg_str(value.as_str())?, - }), - serde_json::Value::Array(_) => { - let mut bcs: Vec = vec![]; // BCS representation of argument. - let mut common_sub_arg_depth = None; - // Prepend argument sequence length to BCS bytes vector. - write_u64_as_uleb128(&mut bcs, arg.as_array().unwrap().len()); - // Loop over all of the vector's sub-arguments, which may also be vectors: - for sub_arg in arg.as_array().unwrap() { - let ArgWithType { - _ty: _, - _vector_depth: sub_arg_depth, - arg: mut sub_arg_bcs, - } = self.parse_arg_json(sub_arg)?; - // Verify all sub-arguments have same depth. - if let Some(check_depth) = common_sub_arg_depth { - if check_depth != sub_arg_depth { - return Err(CliError::CommandArgumentError( - "Variable vector depth".to_string(), - )); - } - }; - common_sub_arg_depth = Some(sub_arg_depth); - bcs.append(&mut sub_arg_bcs); // Append sub-argument BCS. - } - // Default sub-argument depth is 0 for when no sub-arguments were looped over. - Ok(ArgWithType { - _ty: self.clone(), - _vector_depth: common_sub_arg_depth.unwrap_or(0) + 1, - arg: bcs, - }) - }, - serde_json::Value::Null => { - Err(CliError::CommandArgumentError("Null argument".to_string())) - }, - serde_json::Value::Object(_) => Err(CliError::CommandArgumentError( - "JSON object argument".to_string(), - )), - } - } -} - -// TODO use from move_binary_format::file_format_common if it is made public. -fn write_u64_as_uleb128(binary: &mut Vec, mut val: usize) { - loop { - let cur = val & 0x7F; - if cur != val { - binary.push((cur | 0x80) as u8); - val >>= 7; - } else { - binary.push(cur as u8); - break; - } - } -} - -impl FromStr for FunctionArgType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "address" => Ok(FunctionArgType::Address), - "bool" => Ok(FunctionArgType::Bool), - "hex" => Ok(FunctionArgType::Hex), - "string" => Ok(FunctionArgType::String), - "u8" => Ok(FunctionArgType::U8), - "u16" => Ok(FunctionArgType::U16), - "u32" => Ok(FunctionArgType::U32), - "u64" => Ok(FunctionArgType::U64), - "u128" => Ok(FunctionArgType::U128), - "u256" => Ok(FunctionArgType::U256), - "raw" => Ok(FunctionArgType::Raw), - str => { - Err(CliError::CommandArgumentError(format!( - "Invalid arg type '{}'. Must be one of: ['{}','{}','{}','{}','{}','{}','{}','{}','{}','{}','{}']", - str, - FunctionArgType::Address, - FunctionArgType::Bool, - FunctionArgType::Hex, - FunctionArgType::String, - FunctionArgType::U8, - FunctionArgType::U16, - FunctionArgType::U32, - FunctionArgType::U64, - FunctionArgType::U128, - FunctionArgType::U256, - FunctionArgType::Raw))) - } - } - } -} - -/// A parseable arg with a type separated by a colon -#[derive(Clone, Debug)] -pub struct ArgWithType { - pub(crate) _ty: FunctionArgType, - pub(crate) _vector_depth: u8, - pub(crate) arg: Vec, -} - -impl ArgWithType { - pub fn address(account_address: AccountAddress) -> Self { - ArgWithType { - _ty: FunctionArgType::Address, - _vector_depth: 0, - arg: bcs::to_bytes(&account_address).unwrap(), - } - } - - pub fn u64(arg: u64) -> Self { - ArgWithType { - _ty: FunctionArgType::U64, - _vector_depth: 0, - arg: bcs::to_bytes(&arg).unwrap(), - } - } - - pub fn bytes(arg: Vec) -> Self { - ArgWithType { - _ty: FunctionArgType::Raw, - _vector_depth: 0, - arg: bcs::to_bytes(&arg).unwrap(), - } - } - - pub fn raw(arg: Vec) -> Self { - ArgWithType { - _ty: FunctionArgType::Raw, - _vector_depth: 0, - arg, - } - } - - pub fn bcs_value_to_json<'a, T: Deserialize<'a> + Serialize>( - &'a self, - ) -> CliTypedResult { - match self._vector_depth { - 0 => match self._ty.clone() { - FunctionArgType::U64 => { - serde_json::to_value(bcs::from_bytes::(&self.arg)?.to_string()) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::U128 => { - serde_json::to_value(bcs::from_bytes::(&self.arg)?.to_string()) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::U256 => { - serde_json::to_value(bcs::from_bytes::(&self.arg)?.to_string()) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::Raw => serde_json::to_value(&self.arg) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - _ => serde_json::to_value(bcs::from_bytes::(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - }, - 1 => match self._ty.clone() { - FunctionArgType::U64 => { - let u64_vector: Vec = bcs::from_bytes::>(&self.arg)?; - let string_vector: Vec = - u64_vector.iter().map(ToString::to_string).collect(); - serde_json::to_value(string_vector) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::U128 => { - let u128_vector: Vec = bcs::from_bytes::>(&self.arg)?; - let string_vector: Vec = - u128_vector.iter().map(ToString::to_string).collect(); - serde_json::to_value(string_vector) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::U256 => { - let u256_vector: Vec = bcs::from_bytes::>(&self.arg)?; - let string_vector: Vec = - u256_vector.iter().map(ToString::to_string).collect(); - serde_json::to_value(string_vector) - .map_err(|err| CliError::UnexpectedError(err.to_string())) - }, - FunctionArgType::Raw => serde_json::to_value(&self.arg) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - _ => serde_json::to_value(bcs::from_bytes::>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - }, - - 2 => serde_json::to_value(bcs::from_bytes::>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - - 3 => serde_json::to_value(bcs::from_bytes::>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - - 4 => serde_json::to_value(bcs::from_bytes::>>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 5 => serde_json::to_value(bcs::from_bytes::>>>>>(&self.arg)?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 6 => serde_json::to_value(bcs::from_bytes::>>>>>>( - &self.arg, - )?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - 7 => serde_json::to_value(bcs::from_bytes::>>>>>>>( - &self.arg, - )?) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - depth => Err(CliError::UnexpectedError(format!( - "Vector of depth {depth} is overly nested" - ))), - } - } - - pub fn to_json(&self) -> CliTypedResult { - match self._ty { - FunctionArgType::Address => self.bcs_value_to_json::(), - FunctionArgType::Bool => self.bcs_value_to_json::(), - FunctionArgType::Hex => self.bcs_value_to_json::>(), - FunctionArgType::String => self.bcs_value_to_json::(), - FunctionArgType::U8 => self.bcs_value_to_json::(), - FunctionArgType::U16 => self.bcs_value_to_json::(), - FunctionArgType::U32 => self.bcs_value_to_json::(), - FunctionArgType::U64 => self.bcs_value_to_json::(), - FunctionArgType::U128 => self.bcs_value_to_json::(), - FunctionArgType::U256 => self.bcs_value_to_json::(), - FunctionArgType::Raw => serde_json::to_value(&self.arg) - .map_err(|err| CliError::UnexpectedError(err.to_string())), - } - .map_err(|err| { - CliError::UnexpectedError(format!("Failed to parse argument to JSON {}", err)) - }) - } -} - -/// Does not support string arguments that contain the following characters: -/// -/// * `,` -/// * `[` -/// * `]` -impl FromStr for ArgWithType { - type Err = CliError; - - fn from_str(s: &str) -> Result { - // Splits on the first colon, returning at most `2` elements - // This is required to support args that contain a colon - let parts: Vec<_> = s.splitn(2, ':').collect(); - if parts.len() != 2 { - return Err(CliError::CommandArgumentError( - "Arguments must be pairs of : e.g. bool:true".to_string(), - )); - } - let ty = FunctionArgType::from_str(parts.first().unwrap())?; - let mut arg = String::from(*parts.last().unwrap()); - // May need to surround with quotes if not an array, so arg can be parsed into JSON. - if !arg.starts_with('[') { - if let FunctionArgType::Address - | FunctionArgType::Hex - | FunctionArgType::String - | FunctionArgType::Raw = ty - { - arg = format!("\"{}\"", arg); - } - } - let json = serde_json::from_str::(arg.as_str()) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - ty.parse_arg_json(&json) - } -} - -impl TryInto for &ArgWithType { - type Error = CliError; - - fn try_into(self) -> Result { - if self._vector_depth > 0 && self._ty != FunctionArgType::U8 { - return Err(CliError::UnexpectedError( - "Unable to parse non-u8 vector to transaction argument".to_string(), - )); - } - match self._ty { - FunctionArgType::Address => Ok(TransactionArgument::Address(txn_arg_parser( - &self.arg, "address", - )?)), - FunctionArgType::Bool => Ok(TransactionArgument::Bool(txn_arg_parser( - &self.arg, "bool", - )?)), - FunctionArgType::Hex => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "hex", - )?)), - FunctionArgType::String => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "string", - )?)), - FunctionArgType::U8 => match self._vector_depth { - 0 => Ok(TransactionArgument::U8(txn_arg_parser(&self.arg, "u8")?)), - 1 => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, - "vector", - )?)), - depth => Err(CliError::UnexpectedError(format!( - "Unable to parse u8 vector of depth {} to transaction argument", - depth - ))), - }, - FunctionArgType::U16 => Ok(TransactionArgument::U16(txn_arg_parser(&self.arg, "u16")?)), - FunctionArgType::U32 => Ok(TransactionArgument::U32(txn_arg_parser(&self.arg, "u32")?)), - FunctionArgType::U64 => Ok(TransactionArgument::U64(txn_arg_parser(&self.arg, "u64")?)), - FunctionArgType::U128 => Ok(TransactionArgument::U128(txn_arg_parser( - &self.arg, "u128", - )?)), - FunctionArgType::U256 => Ok(TransactionArgument::U256(txn_arg_parser( - &self.arg, "u256", - )?)), - FunctionArgType::Raw => Ok(TransactionArgument::U8Vector(txn_arg_parser( - &self.arg, "raw", - )?)), - } - } -} - -fn txn_arg_parser( - data: &[u8], - label: &'static str, -) -> Result { - bcs::from_bytes(data).map_err(|err| CliError::UnableToParse(label, err.to_string())) -} - -/// Identifier of a module member (function or struct). -/// Duplicated from aptos_types, as we also need to load_account_arg from the CLI. -#[derive(Debug, Clone)] -pub struct MemberId { - pub module_id: ModuleId, - pub member_id: Identifier, -} - -fn parse_member_id(function_id: &str) -> CliTypedResult { - let ids: Vec<&str> = function_id.split_terminator("::").collect(); - if ids.len() != 3 { - return Err(CliError::CommandArgumentError( - "FunctionId is not well formed. Must be of the form
    ::::" - .to_string(), - )); - } - let address = load_account_arg(ids.first().unwrap())?; - let module = Identifier::from_str(ids.get(1).unwrap()) - .map_err(|err| CliError::UnableToParse("Module Name", err.to_string()))?; - let member_id = Identifier::from_str(ids.get(2).unwrap()) - .map_err(|err| CliError::UnableToParse("Member Name", err.to_string()))?; - let module_id = ModuleId::new(address, module); - Ok(MemberId { - module_id, - member_id, - }) -} - -impl FromStr for MemberId { - type Err = CliError; - - fn from_str(s: &str) -> Result { - parse_member_id(s) - } -} diff --git a/crates/aptos/src/move_tool/package_hooks.rs b/crates/aptos/src/move_tool/package_hooks.rs deleted file mode 100644 index c6b140fe86c2a..0000000000000 --- a/crates/aptos/src/move_tool/package_hooks.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{common::types::load_account_arg, move_tool::CachedPackageRegistry}; -use aptos_framework::UPGRADE_POLICY_CUSTOM_FIELD; -use futures::executor::block_on; -use move_package::{ - compilation::package_layout::CompiledPackageLayout, package_hooks::PackageHooks, - source_package::parsed_manifest::CustomDepInfo, -}; -use move_symbol_pool::Symbol; -use reqwest::Url; - -pub fn register_package_hooks() { - move_package::package_hooks::register_package_hooks(Box::new(AptosPackageHooks {})) -} - -struct AptosPackageHooks {} - -impl PackageHooks for AptosPackageHooks { - fn custom_package_info_fields(&self) -> Vec { - vec![UPGRADE_POLICY_CUSTOM_FIELD.to_string()] - } - - fn custom_dependency_key(&self) -> Option { - Some("aptos".to_string()) - } - - fn resolve_custom_dependency( - &self, - _dep_name: Symbol, - info: &CustomDepInfo, - ) -> anyhow::Result<()> { - block_on(maybe_download_package(info)) - } -} - -async fn maybe_download_package(info: &CustomDepInfo) -> anyhow::Result<()> { - if !info - .download_to - .join(CompiledPackageLayout::BuildInfo.path()) - .exists() - { - let registry = CachedPackageRegistry::create( - Url::parse(info.node_url.as_str())?, - load_account_arg(info.package_address.as_str())?, - false, - ) - .await?; - let package = registry.get_package(info.package_name).await?; - package.save_package_to_disk(info.download_to.as_path()) - } else { - Ok(()) - } -} diff --git a/crates/aptos/src/move_tool/show.rs b/crates/aptos/src/move_tool/show.rs deleted file mode 100644 index 22e49f0848a3c..0000000000000 --- a/crates/aptos/src/move_tool/show.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::IncludedArtifactsArgs; -use crate::common::types::{CliCommand, CliError, CliResult, CliTypedResult, MovePackageDir}; -use anyhow::Context; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_types::transaction::EntryABI; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; - -#[derive(Subcommand)] -pub enum ShowTool { - Abi(ShowAbi), -} - -impl ShowTool { - pub async fn execute_serialized(self) -> CliResult { - match self { - Self::Abi(tool) => tool.execute_serialized().await, - } - } -} - -/// Compile the package and show information about the ABIs of the compiled modules. -/// -/// For example, this would show the function `transfer` in the module `coin`: -/// -/// aptos move show abi --modules coin --names transfer -/// -#[derive(Parser)] -pub struct ShowAbi { - /// If provided, only show items from the given Move modules. These should be module - /// names, not file paths. For example, `coin`. - #[clap(long, num_args = 0..)] - modules: Vec, - - /// If provided, only show items with the given names. For example, `transfer`. - #[clap(long, num_args = 0..)] - names: Vec, - - #[clap(flatten)] - included_artifacts_args: IncludedArtifactsArgs, - - #[clap(flatten)] - move_options: MovePackageDir, -} - -#[async_trait] -impl CliCommand> for ShowAbi { - fn command_name(&self) -> &'static str { - "ShowAbi" - } - - async fn execute(self) -> CliTypedResult> { - let build_options = BuildOptions { - install_dir: self.move_options.output_dir.clone(), - with_abis: true, - ..self - .included_artifacts_args - .included_artifacts - .build_options( - self.move_options.dev, - self.move_options.skip_fetch_latest_git_deps, - self.move_options.named_addresses(), - self.move_options.bytecode_version, - self.move_options.compiler_version, - self.move_options.skip_attribute_checks, - self.move_options.check_test_code, - ) - }; - - // Build the package. - let package = BuiltPackage::build(self.move_options.get_package_path()?, build_options) - .map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?; - - // Get ABIs from the package. - let abis = package - .extract_abis() - .context("No ABIs found after compilation")?; - - // Filter the ABIs based on the filters passed in. - let abis = abis - .into_iter() - .filter(|abi| { - let name = abi.name().to_string(); - if !self.names.is_empty() && !self.names.contains(&name) { - return false; - } - match &abi { - EntryABI::EntryFunction(func) => { - if !self.modules.is_empty() - && !self - .modules - .contains(&func.module_name().name().to_string()) - { - return false; - } - }, - EntryABI::TransactionScript(_) => { - // If there were any modules specified we ignore scripts. - if !self.modules.is_empty() { - return false; - } - }, - } - true - }) - .collect(); - - Ok(abis) - } -} diff --git a/crates/aptos/src/move_tool/stored_package.rs b/crates/aptos/src/move_tool/stored_package.rs deleted file mode 100644 index 084c366322bc9..0000000000000 --- a/crates/aptos/src/move_tool/stored_package.rs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::bail; -use aptos_framework::{ - natives::code::{ModuleMetadata, PackageMetadata, PackageRegistry, UpgradePolicy}, - unzip_metadata_str, -}; -use aptos_rest_client::Client; -use aptos_types::account_address::AccountAddress; -use move_package::compilation::package_layout::CompiledPackageLayout; -use reqwest::Url; -use std::{collections::BTreeMap, fmt, fs, path::Path}; - -// TODO: this is a first naive implementation of the package registry. Before mainnet -// we need to use tables for the package registry. - -/// Represents the package registry at a given account. -pub struct CachedPackageRegistry { - inner: PackageRegistry, - bytecode: BTreeMap>, -} - -/// Represents the package metadata found in an registry. -pub struct CachedPackageMetadata<'a> { - metadata: &'a PackageMetadata, -} - -/// Represents the package metadata found in an registry. -pub struct CachedModuleMetadata<'a> { - metadata: &'a ModuleMetadata, -} - -impl fmt::Display for CachedPackageMetadata<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "{}", self.metadata)?; - Ok(()) - } -} - -impl CachedPackageRegistry { - /// Creates a new registry. - pub async fn create( - url: Url, - addr: AccountAddress, - with_bytecode: bool, - ) -> anyhow::Result { - let client = Client::new(url); - // Need to use a different type to deserialize JSON - let inner = client - .get_account_resource_bcs::(addr, "0x1::code::PackageRegistry") - .await? - .into_inner(); - let mut bytecode = BTreeMap::new(); - if with_bytecode { - for pack in &inner.packages { - for module in &pack.modules { - let bytes = client - .get_account_module(addr, &module.name) - .await? - .into_inner() - .bytecode - .0; - bytecode.insert(module.name.clone(), bytes); - } - } - } - Ok(Self { inner, bytecode }) - } - - /// Returns the list of packages in this registry by name. - pub fn package_names(&self) -> Vec<&str> { - self.inner - .packages - .iter() - .map(|p| p.name.as_str()) - .collect() - } - - /// Finds the metadata for the given module in the registry by its unique name. - pub async fn get_module<'a>( - &self, - name: impl AsRef, - ) -> anyhow::Result> { - let name = name.as_ref(); - for package in &self.inner.packages { - for module in &package.modules { - if module.name == name { - return Ok(CachedModuleMetadata { metadata: module }); - } - } - } - bail!("module `{}` not found", name) - } - - /// Finds the metadata for the given package in the registry by its unique name. - pub async fn get_package<'a>( - &self, - name: impl AsRef, - ) -> anyhow::Result> { - let name = name.as_ref(); - for package in &self.inner.packages { - if package.name == name { - return Ok(CachedPackageMetadata { metadata: package }); - } - } - bail!("package `{}` not found", name) - } - - /// Gets the bytecode associated with the module. - pub async fn get_bytecode( - &self, - module_name: impl AsRef, - ) -> anyhow::Result> { - Ok(self - .bytecode - .get(module_name.as_ref()) - .map(|v| v.as_slice())) - } -} - -impl<'a> CachedPackageMetadata<'a> { - pub fn name(&self) -> &str { - &self.metadata.name - } - - pub fn upgrade_policy(&self) -> UpgradePolicy { - self.metadata.upgrade_policy - } - - pub fn upgrade_number(&self) -> u64 { - self.metadata.upgrade_number - } - - pub fn source_digest(&self) -> &str { - &self.metadata.source_digest - } - - pub fn manifest(&self) -> anyhow::Result { - unzip_metadata_str(&self.metadata.manifest) - } - - pub fn module_names(&self) -> Vec<&str> { - self.metadata - .modules - .iter() - .map(|s| s.name.as_str()) - .collect() - } - - pub fn module(&self, name: impl AsRef) -> anyhow::Result> { - let name = name.as_ref(); - for module in &self.metadata.modules { - if module.name == name { - return Ok(CachedModuleMetadata { metadata: module }); - } - } - bail!("module `{}` not found", name) - } - - pub fn save_package_to_disk(&self, path: &Path) -> anyhow::Result<()> { - fs::create_dir_all(path)?; - fs::write( - path.join("Move.toml"), - unzip_metadata_str(&self.metadata.manifest)?, - )?; - let sources_dir = path.join(CompiledPackageLayout::Sources.path()); - fs::create_dir_all(&sources_dir)?; - for module in &self.metadata.modules { - let source = match module.source.is_empty() { - true => { - println!("module without code: {}", module.name); - "".into() - }, - false => unzip_metadata_str(&module.source)?, - }; - fs::write(sources_dir.join(format!("{}.move", module.name)), source)?; - } - Ok(()) - } - - pub fn save_bytecode_to_disk( - &self, - path: &Path, - module_name: &str, - bytecode: &[u8], - ) -> anyhow::Result<()> { - let bytecode_dir = path.join(CompiledPackageLayout::CompiledModules.path()); - fs::create_dir_all(&bytecode_dir)?; - fs::write(bytecode_dir.join(format!("{}.mv", module_name)), bytecode)?; - Ok(()) - } - - pub fn verify(&self, package_metadata: &PackageMetadata) -> anyhow::Result<()> { - let self_metadata = self.metadata; - - if self_metadata.name != package_metadata.name { - bail!( - "Package name doesn't match {} : {}", - package_metadata.name, - self_metadata.name - ) - } else if self_metadata.deps != package_metadata.deps { - bail!( - "Dependencies don't match {:?} : {:?}", - package_metadata.deps, - self_metadata.deps - ) - } else if self_metadata.modules != package_metadata.modules { - bail!( - "Modules don't match {:?} : {:?}", - package_metadata.modules, - self_metadata.modules - ) - } else if self_metadata.manifest != package_metadata.manifest { - bail!( - "Manifest doesn't match {:?} : {:?}", - package_metadata.manifest, - self_metadata.manifest - ) - } else if self_metadata.upgrade_policy != package_metadata.upgrade_policy { - bail!( - "Upgrade policy doesn't match {:?} : {:?}", - package_metadata.upgrade_policy, - self_metadata.upgrade_policy - ) - } else if self_metadata.extension != package_metadata.extension { - bail!( - "Extensions doesn't match {:?} : {:?}", - package_metadata.extension, - self_metadata.extension - ) - } else if self_metadata.source_digest != package_metadata.source_digest { - bail!( - "Source digests doesn't match {:?} : {:?}", - package_metadata.source_digest, - self_metadata.source_digest - ) - } - - Ok(()) - } -} - -impl<'a> CachedModuleMetadata<'a> { - pub fn name(&self) -> &str { - &self.metadata.name - } - - pub fn zipped_source(&self) -> &[u8] { - &self.metadata.source - } - - pub fn zipped_source_map_raw(&self) -> &[u8] { - &self.metadata.source_map - } -} diff --git a/crates/aptos/src/node/analyze/analyze_validators.rs b/crates/aptos/src/node/analyze/analyze_validators.rs deleted file mode 100644 index 26deff503040c..0000000000000 --- a/crates/aptos/src/node/analyze/analyze_validators.rs +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::fetch_metadata::ValidatorInfo; -use anyhow::Result; -use aptos_bitvec::BitVec; -use aptos_rest_client::VersionedNewBlockEvent; -use aptos_storage_interface::{DbReader, Order}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{new_block_event_key, NewBlockEvent}, -}; -use itertools::Itertools; -use std::{cmp::Ordering, collections::HashMap, convert::TryFrom, ops::Add}; - -/// Single validator stats -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ValidatorStats { - /// Number of successful proposals - pub proposal_successes: u32, - /// Number of failed proposals - pub proposal_failures: u32, - /// Number of votes proposals - pub votes: u32, - /// Number of transactions in a block - pub transactions: u32, - /// Voting power - pub voting_power: u64, -} - -impl ValidatorStats { - /// Proposal failure rate - pub fn failure_rate(&self) -> f32 { - (self.proposal_failures as f32) / (self.proposal_failures + self.proposal_successes) as f32 - } - - /// Whether node is proposing well enough - pub fn is_reliable(&self) -> bool { - (self.proposal_successes > 0) && (self.failure_rate() < 0.1) - } - - // Whether node is voting well enough - pub fn is_voting_enough(&self, rounds: u32) -> bool { - self.votes as f32 > rounds as f32 * 0.3 - } -} - -#[derive(Debug, Eq, PartialEq, Hash)] -pub enum NodeState { - // Proposal failure < 10%, >30% votes - Reliable, - // Proposal failure < 10%, <30% votes - ReliableLowVotes, - // Has successful proposals, but proposal failure > 10% - AliveUnreliable, - // No successful proposals, but voting - OnlyVoting, - // Not participating in consensus - NotParticipatingInConsensus, - // Not in ValidatorSet - Absent, -} - -impl NodeState { - pub fn to_char(&self) -> &str { - match self { - Self::Reliable => "+", - Self::ReliableLowVotes => "P", - Self::AliveUnreliable => "~", - Self::OnlyVoting => "V", - Self::NotParticipatingInConsensus => "X", - Self::Absent => " ", - } - } - - // Large the value, the worse the node is performing. - pub fn to_order_weight(&self) -> usize { - match self { - Self::Reliable => 0, - Self::ReliableLowVotes => 100, - Self::AliveUnreliable => 10000, - Self::OnlyVoting => 1000000, - Self::NotParticipatingInConsensus => 100000000, - Self::Absent => 1, - } - } -} - -impl Add for ValidatorStats { - type Output = Self; - - fn add(self, other: Self) -> Self { - Self { - proposal_successes: self.proposal_successes + other.proposal_successes, - proposal_failures: self.proposal_failures + other.proposal_failures, - votes: self.votes + other.votes, - transactions: self.transactions + other.transactions, - voting_power: 0, // cannot aggregate voting power. - } - } -} - -/// Statistics for all validators -#[derive(Clone)] -pub struct EpochStats { - /// Statistics for each of the validators - pub validator_stats: HashMap, - /// Total rounds in an epoch - pub total_rounds: u32, - /// Total transactions in an epoch - pub total_transactions: u32, - /// Successful rounds in an epoch - pub round_successes: u32, - /// Failed rounds in an epoch - pub round_failures: u32, - /// Nil blocks in an epoch - pub nil_blocks: u32, - /// Total voting power - pub total_voting_power: u128, -} - -impl EpochStats { - pub fn to_state(&self, validator: &AccountAddress) -> NodeState { - self.validator_stats - .get(validator) - .map(|b| { - if b.is_reliable() { - if b.is_voting_enough(self.total_rounds) { - NodeState::Reliable - } else { - NodeState::ReliableLowVotes - } - } else if b.proposal_successes > 0 { - NodeState::AliveUnreliable - } else if b.votes > 0 { - NodeState::OnlyVoting - } else { - NodeState::NotParticipatingInConsensus - } - }) - .unwrap_or(NodeState::Absent) - } - - pub fn to_votes(&self, validator: &AccountAddress) -> u32 { - self.validator_stats - .get(validator) - .map(|s| s.votes) - .unwrap_or(0) - } - - pub fn to_voting_power(&self, validator: &AccountAddress) -> u64 { - self.validator_stats - .get(validator) - .map(|s| s.voting_power) - .unwrap_or(0) - } -} - -impl Add for EpochStats { - type Output = Self; - - fn add(self, other: Self) -> Self { - let mut validator_stats = self.validator_stats; - for (key, other_validator_stats) in other.validator_stats.into_iter() { - validator_stats.insert( - key, - other_validator_stats - + *validator_stats.get(&key).unwrap_or(&ValidatorStats { - proposal_failures: 0, - proposal_successes: 0, - votes: 0, - transactions: 0, - voting_power: 0, - }), - ); - } - Self { - validator_stats, - total_rounds: self.total_rounds + other.total_rounds, - round_successes: self.round_successes + other.round_successes, - round_failures: self.round_failures + other.round_failures, - nil_blocks: self.nil_blocks + other.nil_blocks, - total_transactions: self.total_transactions + other.total_transactions, - total_voting_power: 0, - } - } -} - -/// Analyze validator performance -pub struct AnalyzeValidators {} - -impl AnalyzeValidators { - /// Fetch all events from a single epoch from DB. - pub fn fetch_epoch(epoch: u64, aptos_db: &dyn DbReader) -> Result> { - let batch = 100; - - let mut cursor = u64::max_value(); - let mut result: Vec = vec![]; - let ledger_version = aptos_db.get_latest_ledger_info()?.ledger_info().version(); - - loop { - let raw_events = aptos_db.get_events( - &new_block_event_key(), - cursor, - Order::Descending, - batch as u64, - ledger_version, - )?; - let end = raw_events.len() < batch; - for raw_event in raw_events { - if cursor <= raw_event.event.v1()?.sequence_number() { - println!( - "Duplicate event found for {} : {:?}", - cursor, - raw_event.event.v1()?.sequence_number() - ); - } else { - cursor = raw_event.event.v1()?.sequence_number(); - let event = bcs::from_bytes::(raw_event.event.event_data())?; - - match epoch.cmp(&event.epoch()) { - Ordering::Equal => { - result.push(VersionedNewBlockEvent { - event, - version: raw_event.transaction_version, - sequence_number: raw_event.event.v1()?.sequence_number(), - }); - }, - Ordering::Greater => { - return Ok(result); - }, - Ordering::Less => {}, - }; - } - } - - if end { - return Ok(result); - } - } - } - - /// Analyze single epoch - pub fn analyze(blocks: &[VersionedNewBlockEvent], validators: &[ValidatorInfo]) -> EpochStats { - assert!( - validators.iter().as_slice().windows(2).all(|w| { - w[0].validator_index - .partial_cmp(&w[1].validator_index) - .map(|o| o != Ordering::Greater) - .unwrap_or(false) - }), - "Validators need to be sorted" - ); - assert!( - blocks.iter().as_slice().windows(2).all(|w| { - w[0].event - .round() - .partial_cmp(&w[1].event.round()) - .map(|o| o != Ordering::Greater) - .unwrap_or(false) - }), - "Blocks need to be sorted" - ); - - let mut successes = HashMap::::new(); - let mut failures = HashMap::::new(); - let mut votes = HashMap::::new(); - let mut transactions = HashMap::::new(); - - let mut trimmed_rounds = 0; - let mut nil_blocks = 0; - let mut previous_round = 0; - for (pos, block) in blocks.iter().enumerate() { - let event = &block.event; - let is_nil = event.proposer() == AccountAddress::ZERO; - if is_nil { - nil_blocks += 1; - } - let expected_round = - previous_round + u64::from(!is_nil) + event.failed_proposer_indices().len() as u64; - if event.round() != expected_round { - println!( - "Missing failed AccountAddresss : {} {:?}", - previous_round, &event - ); - assert!(expected_round < event.round()); - trimmed_rounds += event.round() - expected_round; - } - previous_round = event.round(); - - if !is_nil { - *successes.entry(event.proposer()).or_insert(0) += 1; - } - - for failed_proposer_index in event.failed_proposer_indices() { - *failures - .entry(validators[*failed_proposer_index as usize].address) - .or_insert(0) += 1; - } - - let previous_block_votes_bitvec: BitVec = - event.previous_block_votes_bitvec().clone().into(); - assert_eq!( - BitVec::required_buckets(validators.len() as u16), - previous_block_votes_bitvec.num_buckets() - ); - for (i, validator) in validators.iter().enumerate() { - if previous_block_votes_bitvec.is_set(i as u16) { - *votes.entry(validator.address).or_insert(0) += 1; - } - } - - let cur_transactions_option = blocks - .get(pos + 1) - .map(|next| u32::try_from(next.version - block.version - 2).unwrap()); - if let Some(cur_transactions) = cur_transactions_option { - if is_nil { - assert_eq!( - cur_transactions, - 0, - "{} {:?}", - block.version, - blocks.get(pos + 1) - ); - } - *transactions.entry(event.proposer()).or_insert(0) += cur_transactions; - } - } - let total_successes: u32 = successes.values().sum(); - let total_failures: u32 = failures.values().sum(); - let total_transactions: u32 = transactions.values().sum(); - let total_rounds = total_successes + total_failures; - assert_eq!( - total_rounds + u32::try_from(trimmed_rounds).unwrap(), - previous_round as u32, - "{} success + {} failures + {} trimmed != {}", - total_successes, - total_failures, - trimmed_rounds, - previous_round - ); - - return EpochStats { - validator_stats: validators - .iter() - .map(|validator| { - (validator.address, ValidatorStats { - proposal_successes: *successes.get(&validator.address).unwrap_or(&0), - proposal_failures: *failures.get(&validator.address).unwrap_or(&0), - votes: *votes.get(&validator.address).unwrap_or(&0), - transactions: *transactions.get(&validator.address).unwrap_or(&0), - voting_power: validator.voting_power, - }) - }) - .collect(), - total_rounds, - total_transactions, - round_successes: total_successes, - round_failures: total_failures, - nil_blocks, - total_voting_power: validators - .iter() - .map(|validator| validator.voting_power as u128) - .sum(), - }; - } - - /// Print validator stats in a table - pub fn print_detailed_epoch_table( - epoch_stats: &EpochStats, - extra: Option<(&str, &HashMap)>, - sort_by_health: bool, - ) { - println!( - "Rounds: {} successes, {} failures, {} NIL blocks, failure rate: {}%, nil block rate: {}%", - epoch_stats.round_successes, epoch_stats.round_failures, epoch_stats.nil_blocks, - 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, - 100.0 * epoch_stats.nil_blocks as f32 / epoch_stats.total_rounds as f32, - ); - println!( - "{: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <30}", - "elected", - "% rounds", - "% failed", - "succeded", - "failed", - "voted", - "transact", - extra.map(|(column, _)| column).unwrap_or("") - ); - - let mut validator_order: Vec<&AccountAddress> = - epoch_stats.validator_stats.keys().collect(); - if sort_by_health { - validator_order.sort_by_cached_key(|v| { - epoch_stats - .validator_stats - .get(v) - .map(|s| { - ( - if s.proposal_successes > 0 { - (s.failure_rate() * 100000.0) as u32 - } else { - 200000 - }, - -((s.proposal_failures + s.proposal_successes) as i32), - *v, - ) - }) - .unwrap() - }); - } else { - validator_order.sort(); - } - - for validator in validator_order { - let cur_stats = epoch_stats.validator_stats.get(validator).unwrap(); - println!( - "{: <10} | {:5.2}% | {:7.3}% | {: <10} | {: <10} | {: <10} | {: <10} | {}", - cur_stats.proposal_failures + cur_stats.proposal_successes, - 100.0 * (cur_stats.proposal_failures + cur_stats.proposal_successes) as f32 - / (epoch_stats.total_rounds as f32), - 100.0 * cur_stats.failure_rate(), - cur_stats.proposal_successes, - cur_stats.proposal_failures, - cur_stats.votes, - cur_stats.transactions, - if let Some((_, extra_map)) = extra { - format!( - "{: <30} | {}", - extra_map.get(validator).unwrap_or(&"".to_string()), - validator - ) - } else { - format!("{}", validator) - } - ); - } - } - - pub fn print_validator_health_over_time( - stats: &HashMap, - validators: &[AccountAddress], - extra: Option<&HashMap>, - ) { - let epochs: Vec<_> = stats.keys().sorted().collect(); - - let mut sorted_validators = validators.to_vec(); - sorted_validators.sort_by_cached_key(|validator| { - ( - epochs - .iter() - .map(|cur_epoch| { - stats - .get(cur_epoch) - .unwrap() - .to_state(validator) - .to_order_weight() - }) - .sum::(), - *validator, - ) - }); - - for validator in sorted_validators { - print!( - "{}: ", - if let Some(extra_map) = extra { - format!( - "{: <30} | {}", - extra_map.get(&validator).unwrap_or(&""), - validator - ) - } else { - format!("{}", validator) - } - ); - for cur_epoch in epochs.iter() { - print!( - "{}", - stats.get(cur_epoch).unwrap().to_state(&validator).to_char() - ); - } - println!(); - } - } - - pub fn print_network_health_over_time( - stats: &HashMap, - validators: &[AccountAddress], - ) { - let epochs = stats.keys().sorted(); - - println!( - "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10}", - "epoch", - "reliable", - "r low vote", - "unreliable", - "only vote", - "down(cons)", - "rounds", - "#r failed", - "% failure", - "% stake has >10% of votes", - ); - for cur_epoch in epochs { - let epoch_stats = stats.get(cur_epoch).unwrap(); - - let counts = validators.iter().map(|v| epoch_stats.to_state(v)).counts(); - - let voted_voting_power: u128 = validators - .iter() - .flat_map(|v| { - if epoch_stats.to_votes(v) > epoch_stats.round_successes / 10 { - Some(epoch_stats.to_voting_power(v) as u128) - } else { - None - } - }) - .sum(); - - println!( - "{: <8} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {: <10} | {:10.2} | {:10.2}", - cur_epoch, - counts.get(&NodeState::Reliable).unwrap_or(&0), - counts.get(&NodeState::ReliableLowVotes).unwrap_or(&0), - counts.get(&NodeState::AliveUnreliable).unwrap_or(&0), - counts.get(&NodeState::OnlyVoting).unwrap_or(&0), - counts - .get(&NodeState::NotParticipatingInConsensus) - .unwrap_or(&0), - epoch_stats.total_rounds, - epoch_stats.round_failures, - 100.0 * epoch_stats.round_failures as f32 / epoch_stats.total_rounds as f32, - 100.0 * voted_voting_power as f32 / epoch_stats.total_voting_power as f32, - ); - } - } -} diff --git a/crates/aptos/src/node/analyze/fetch_metadata.rs b/crates/aptos/src/node/analyze/fetch_metadata.rs deleted file mode 100644 index e9ab24e8b9c15..0000000000000 --- a/crates/aptos/src/node/analyze/fetch_metadata.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{anyhow, Result}; -use aptos_rest_client::{ - aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange}, - Client as RestClient, Transaction, VersionedNewBlockEvent, -}; -use aptos_types::account_address::AccountAddress; -use std::str::FromStr; - -const MAX_FETCH_BATCH_SIZE: u16 = 1000; - -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -pub struct ValidatorInfo { - pub address: AccountAddress, - pub voting_power: u64, - pub validator_index: u16, -} - -pub struct EpochInfo { - pub epoch: u64, - pub blocks: Vec, - pub validators: Vec, - pub partial: bool, -} - -pub struct FetchMetadata {} - -impl FetchMetadata { - fn get_validator_addresses( - data: &MoveResource, - field_name: &str, - ) -> Result> { - fn extract_validator_address(validator: &serde_json::Value) -> Result { - Ok(ValidatorInfo { - address: AccountAddress::from_hex_literal( - validator.get("addr").unwrap().as_str().unwrap(), - ) - .map_err(|e| anyhow!("Cannot parse address {:?}", e))?, - voting_power: validator - .get("voting_power") - .unwrap() - .as_str() - .unwrap() - .parse() - .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?, - validator_index: validator - .get("config") - .unwrap() - .get("validator_index") - .unwrap() - .as_str() - .unwrap() - .parse() - .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?, - }) - } - - let validators_json = data - .data - .0 - .get(&IdentifierWrapper::from_str(field_name).unwrap()) - .unwrap(); - if let serde_json::Value::Array(validators_array) = validators_json { - let mut validators: Vec = vec![]; - for validator in validators_array { - validators.push(extract_validator_address(validator)?); - } - Ok(validators) - } else { - Err(anyhow!("{} validators not in json", field_name)) - } - } - - async fn get_transactions_in_range( - client: &RestClient, - start: u64, - last: u64, - ) -> Result> { - let mut result = Vec::new(); - let mut cursor = start; - while cursor < last { - let limit = std::cmp::min(MAX_FETCH_BATCH_SIZE as u64, last - cursor) as u16; - let mut current = client - .get_transactions(Some(cursor), Some(limit)) - .await? - .into_inner(); - if current.is_empty() { - return Err(anyhow!( - "No transactions returned with start={} and limit={}", - cursor, - limit - )); - } - cursor += current.len() as u64; - result.append(&mut current); - } - Ok(result) - } - - fn get_validators_from_transaction(transaction: &Transaction) -> Result> { - if let Ok(info) = transaction.transaction_info() { - for change in &info.changes { - if let WriteSetChange::WriteResource(resource) = change { - if resource.data.typ.name.0.as_str() == "ValidatorSet" { - // No pending at epoch change - assert_eq!( - Vec::::new(), - FetchMetadata::get_validator_addresses( - &resource.data, - "pending_inactive" - )? - ); - assert_eq!( - Vec::::new(), - FetchMetadata::get_validator_addresses( - &resource.data, - "pending_active" - )? - ); - return FetchMetadata::get_validator_addresses( - &resource.data, - "active_validators", - ); - } - } - } - } - Err(anyhow!("Couldn't find ValidatorSet in the transaction")) - } - - pub async fn fetch_new_block_events( - client: &RestClient, - start_epoch: Option, - end_epoch: Option, - ) -> Result> { - let (last_events, state) = client - .get_new_block_events_bcs(None, Some(1)) - .await? - .into_parts(); - let mut start_seq_num = state.oldest_block_height; - assert_eq!(last_events.len(), 1, "{:?}", last_events); - let last_event = last_events.first().unwrap(); - let last_seq_num = last_event.sequence_number; - - let wanted_start_epoch = { - let mut wanted_start_epoch = start_epoch.unwrap_or(2); - if wanted_start_epoch < 0 { - wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1; - } - - let oldest_event = client - .get_new_block_events_bcs(Some(start_seq_num), Some(1)) - .await? - .into_inner() - .into_iter() - .next() - .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?; - let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2); - if oldest_fetchable_epoch > wanted_start_epoch as u64 { - println!( - "Oldest full epoch that can be retreived is {} ", - oldest_fetchable_epoch - ); - oldest_fetchable_epoch - } else { - wanted_start_epoch as u64 - } - }; - let wanted_end_epoch = { - let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX); - if wanted_end_epoch < 0 { - wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1; - } - std::cmp::min( - last_event.event.epoch() + 1, - std::cmp::max(2, wanted_end_epoch) as u64, - ) - }; - - if wanted_start_epoch > 2 { - let mut search_end = last_seq_num; - - // Stop when search is close enough, and we can then linearly - // proceed from there. - // Since we are ignoring results we are fetching during binary search - // we want to stop when we are close. - while start_seq_num + 20 < search_end { - let mid = (start_seq_num + search_end) / 2; - - let mid_epoch = client - .get_new_block_events_bcs(Some(mid), Some(1)) - .await? - .into_inner() - .first() - .unwrap() - .event - .epoch(); - - if mid_epoch < wanted_start_epoch { - start_seq_num = mid; - } else { - search_end = mid; - } - } - } - - let mut batch_index = 0; - - println!( - "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}", - start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch, - ); - let mut result: Vec = vec![]; - if wanted_start_epoch >= wanted_end_epoch { - return Ok(result); - } - - let mut validators: Vec = vec![]; - let mut current: Vec = vec![]; - let mut epoch = 0; - - let mut cursor = start_seq_num; - loop { - let response = client - .get_new_block_events_bcs(Some(cursor), Some(MAX_FETCH_BATCH_SIZE)) - .await; - - if response.is_err() { - println!( - "Failed to read new_block_events beyond {}, stopping. {:?}", - cursor, - response.unwrap_err() - ); - assert!(!validators.is_empty()); - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: true, - }); - return Ok(result); - } - let events = response.unwrap().into_inner(); - - if events.is_empty() { - return Err(anyhow!( - "No transactions returned with start={} and limit={}", - cursor, - MAX_FETCH_BATCH_SIZE - )); - } - - cursor += events.len() as u64; - batch_index += 1; - - for event in events { - if event.event.epoch() > epoch { - if epoch == 0 { - epoch = event.event.epoch(); - current = vec![]; - } else { - let last = current.last().cloned(); - if let Some(last) = last { - let transactions = FetchMetadata::get_transactions_in_range( - client, - last.version, - event.version, - ) - .await?; - assert_eq!( - transactions.first().unwrap().version().unwrap(), - last.version - ); - for transaction in transactions { - if let Ok(new_validators) = - FetchMetadata::get_validators_from_transaction(&transaction) - { - if epoch >= wanted_start_epoch { - assert!(!validators.is_empty()); - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: false, - }); - } - current = vec![]; - - validators = new_validators; - validators.sort_by_key(|v| v.validator_index); - assert_eq!(epoch + 1, event.event.epoch()); - epoch = event.event.epoch(); - if epoch >= wanted_end_epoch { - return Ok(result); - } - break; - } - } - assert!( - current.is_empty(), - "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}", - last.version, - event.version - last.version, - event.event.epoch(), - ); - } - } - } - current.push(event); - } - - if batch_index % 100 == 0 { - println!( - "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents", - result.len(), - epoch, - current.len(), - cursor - ); - } - - if cursor > last_seq_num { - if !validators.is_empty() { - result.push(EpochInfo { - epoch, - blocks: current, - validators: validators.clone(), - partial: true, - }); - } - return Ok(result); - } - } - } -} diff --git a/crates/aptos/src/node/analyze/mod.rs b/crates/aptos/src/node/analyze/mod.rs deleted file mode 100644 index ac8c7016375d3..0000000000000 --- a/crates/aptos/src/node/analyze/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod analyze_validators; -pub mod fetch_metadata; diff --git a/crates/aptos/src/node/local_testnet/docker.rs b/crates/aptos/src/node/local_testnet/docker.rs deleted file mode 100644 index 11128b3ac84cd..0000000000000 --- a/crates/aptos/src/node/local_testnet/docker.rs +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::traits::ShutdownStep; -use anyhow::{Context, Result}; -use async_trait::async_trait; -#[cfg(unix)] -use bollard::API_DEFAULT_VERSION; -use bollard::{ - container::{RemoveContainerOptions, StopContainerOptions}, - errors::Error as BollardError, - image::CreateImageOptions, - network::CreateNetworkOptions, - volume::{CreateVolumeOptions, RemoveVolumeOptions}, - Docker, -}; -use futures::TryStreamExt; -use std::{fs::create_dir_all, path::Path}; -use tracing::{info, warn}; -use version_compare::Version; - -const ERROR_MESSAGE: &str = "Docker is not available, confirm it is installed and running. See https://aptos.dev/guides/local-development-network#faq for assistance."; - -pub const CONTAINER_NETWORK_NAME: &str = "aptos-local-testnet-network"; - -/// This function returns a Docker client. Before returning, it confirms that it can -/// actually query the API and checks that the API version is sufficient. It first -/// tries to connect at the default socket location and if that fails, it tries to find -/// a socket in the user's home directory. On Windows NT it doesn't try that since -/// there no second location, there is just the one named pipe. -pub async fn get_docker() -> Result { - let docker = Docker::connect_with_local_defaults() - .context(format!("{} (init_default)", ERROR_MESSAGE))?; - - // We have to specify the type because the compiler can't figure out the error - // in the case where the system is Unix. - let out: Result<(Docker, bollard::system::Version), bollard::errors::Error> = - match docker.version().await { - Ok(version) => Ok((docker, version)), - Err(err) => { - warn!( - "Received this error trying to use default Docker socket location: {:#}", - err - ); - // Look for the socket in ~/.docker/run - // We don't have to do this if this issue gets addressed: - // https://github.com/fussybeaver/bollard/issues/345 - #[cfg(unix)] - { - let path = dirs::home_dir() - .context(format!("{} (home_dir)", ERROR_MESSAGE))? - .join(".docker") - .join("run") - .join("docker.sock"); - info!("Looking for Docker socket at {}", path.display()); - let path = path.to_str().context(format!("{} (path)", ERROR_MESSAGE))?; - let docker = Docker::connect_with_socket(path, 120, API_DEFAULT_VERSION) - .context(format!("{} (init_home)", ERROR_MESSAGE))?; - let version = docker - .version() - .await - .context(format!("{} (version_home)", ERROR_MESSAGE))?; - Ok((docker, version)) - } - // Just return the original error. - #[cfg(not(unix))] - Err(err) - }, - }; - let (docker, version) = out?; - - // Try to warn the user about their Docker version being too old. We don't error - // out if the version is too old in case we're wrong about the minimum version - // for their particular system. We just print a warning. - match version.api_version { - Some(current_api_version) => match Version::from(¤t_api_version) { - Some(current_api_version) => { - let minimum_api_version = Version::from("1.42").unwrap(); - if current_api_version < minimum_api_version { - eprintln!( - "WARNING: Docker API version {} is too old, minimum required version is {}. Please update Docker!", - current_api_version, - minimum_api_version, - ); - } else { - info!("Docker version is sufficient: {}", current_api_version); - } - }, - None => { - eprintln!( - "WARNING: Failed to parse Docker API version: {}", - current_api_version - ); - }, - }, - None => { - eprintln!( - "WARNING: Failed to determine Docker version, confirm your Docker is up to date!" - ); - }, - } - - Ok(docker) -} - -/// Delete a container. If the container doesn't exist, that's fine, just move on. -pub async fn delete_container(container_name: &str) -> Result<()> { - info!( - "Removing container with name {} (if it exists)", - container_name - ); - - let docker = get_docker().await?; - - let options = Some(RemoveContainerOptions { - force: true, - ..Default::default() - }); - - // Ignore any error, it'll be because the container doesn't exist. - let result = docker.remove_container(container_name, options).await; - - match result { - Ok(_) => info!("Succesfully removed container {}", container_name), - Err(err) => warn!( - "Failed to remove container {}: {:#} (it probably didn't exist)", - container_name, err - ), - } - - Ok(()) -} - -/// Stop a container. If the container doesn't exist, that's fine, just move on. -pub async fn stop_container(container_name: &str) -> Result<()> { - info!( - "Stopping container with name {} (if it exists)", - container_name - ); - - let docker = get_docker().await?; - - let options = Some(StopContainerOptions { - // Timeout in seconds before we kill the container. - t: 1, - }); - - // Ignore any error, it'll be because the container doesn't exist. - let result = docker.stop_container(container_name, options).await; - - match result { - Ok(_) => info!("Succesfully stopped container {}", container_name), - Err(err) => warn!( - "Failed to stop container {}: {:#} (it probably didn't exist)", - container_name, err - ), - } - - Ok(()) -} - -pub async fn pull_docker_image(image_name: &str) -> Result<()> { - info!("Checking if we have to pull docker image {}", image_name); - - let docker = get_docker().await?; - - let options = Some(CreateImageOptions { - from_image: image_name, - ..Default::default() - }); - - // Check if the image is there. If it is, exit early, the user can update any - // images we've already pulled manually if they want. - if docker.inspect_image(image_name).await.is_ok() { - info!( - "Image {} found locally, not attempting to pull it", - image_name - ); - return Ok(()); - } - - // The image is not present, let the user know we'll pull it. - eprintln!("Image {} not found, pulling it now...", image_name); - - // The docker pull CLI command is just sugar around this API. - docker - .create_image(options, None, None) - // Just wait for the whole stream, we don't need to do other things in parallel. - .try_collect::>() - .await - .with_context(|| format!("Failed to pull image {}", image_name))?; - - info!("Pulled docker image {}", image_name); - - Ok(()) -} - -/// Create a network. If the network already exists, that's fine, just move on. -pub async fn create_network(network_name: &str) -> Result<()> { - let docker = get_docker().await?; - - info!("Creating network {}", network_name); - - let config = CreateNetworkOptions { - name: network_name, - internal: false, - check_duplicate: true, - ..Default::default() - }; - let response = docker.create_network(config).await; - - match response { - Ok(_) => { - info!("Created volume {}", network_name); - Ok(()) - }, - Err(err) => match err { - BollardError::DockerResponseServerError { status_code, .. } => { - if status_code == 409 { - info!("Network {} already exists, not creating it", network_name); - Ok(()) - } else { - Err(err.into()) - } - }, - wildcard => Err(wildcard.into()), - }, - } -} - -pub async fn create_volume(volume_name: &str) -> Result<()> { - let docker = get_docker().await?; - - info!("Creating volume {}", volume_name); - - let config = CreateVolumeOptions { - name: volume_name, - ..Default::default() - }; - docker.create_volume(config).await?; - - info!("Created volume {}", volume_name); - - Ok(()) -} - -pub async fn delete_volume(volume_name: &str) -> Result<()> { - let docker = get_docker().await?; - - info!("Removing volume {}", volume_name); - - let config = RemoveVolumeOptions { force: true }; - - // Delete the volume. This returns Ok even if the volume didn't exist, unlike the - // other "remove_x" endpoints, so we just use ? here. - docker - .remove_volume(volume_name, Some(config)) - .await - .context(format!("Failed to remove volume {}", volume_name))?; - - info!( - "Succesfully removed volume {} (if it existed in the first place)", - volume_name - ); - - Ok(()) -} - -/// This function creates a directory called `dir_name` under `test_dir` and writes a -/// file called README.md that tells the user where to go to see logs. We do this since -/// having the user use `docker logs` is the preferred approach, rather than writing -/// logs to files (which is complex and can slow down the container). -pub fn setup_docker_logging(test_dir: &Path, dir_name: &str, container_name: &str) -> Result<()> { - // Create dir. - let log_dir = test_dir.join(dir_name); - create_dir_all(log_dir.as_path()).context(format!("Failed to create {}", log_dir.display()))?; - - // Write README. - let data = format!( - "To see logs for {} run the following command:\n\ndocker logs {}\n", - dir_name, container_name - ); - std::fs::write(log_dir.join("README.md"), data).context("Unable to write README file")?; - - Ok(()) -} - -/// This shutdown step stops a container with the given name. If no container is found -/// we continue without error. We choose to stop the container on shutdown rather than -/// totally delete it so the user can check the logs if it was an unexpected shutdown. -/// When the local testnet is started again, any leftover container will be deleted. -#[derive(Clone, Debug)] -pub struct StopContainerShutdownStep { - container_name: &'static str, -} - -impl StopContainerShutdownStep { - pub fn new(container_name: &'static str) -> Self { - Self { container_name } - } -} - -#[async_trait] -impl ShutdownStep for StopContainerShutdownStep { - async fn run(self: Box) -> Result<()> { - stop_container(self.container_name).await?; - Ok(()) - } -} diff --git a/crates/aptos/src/node/local_testnet/faucet.rs b/crates/aptos/src/node/local_testnet/faucet.rs deleted file mode 100644 index 065bfb6633288..0000000000000 --- a/crates/aptos/src/node/local_testnet/faucet.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; -use anyhow::Result; -use aptos_faucet_core::server::{FunderKeyEnum, RunConfig}; -use async_trait::async_trait; -use clap::Parser; -use maplit::hashset; -use reqwest::Url; -use std::{collections::HashSet, net::Ipv4Addr, path::PathBuf}; - -/// Args related to running a faucet in the local testnet. -#[derive(Debug, Parser)] -pub struct FaucetArgs { - /// Do not run a faucet alongside the node. - /// - /// Running a faucet alongside the node allows you to create and fund accounts - /// for testing. - #[clap(long)] - pub no_faucet: bool, - - /// This does nothing, we already run a faucet by default. We only keep this here - /// for backwards compatibility with tests. We will remove this once the commit - /// that added --no-faucet makes its way to the testnet branch. - #[clap(long, hide = true)] - pub with_faucet: bool, - - /// Port to run the faucet on. - /// - /// When running, you'll be able to use the faucet at `http://127.0.0.1:/mint` e.g. - /// `http//127.0.0.1:8081/mint` - #[clap(long, default_value_t = 8081)] - pub faucet_port: u16, - - /// Disable the delegation of faucet minting to a dedicated account. - #[clap(long)] - pub do_not_delegate: bool, -} - -#[derive(Clone, Debug)] -pub struct FaucetManager { - config: RunConfig, - prerequisite_health_checkers: HashSet, -} - -impl FaucetManager { - pub fn new( - args: &RunLocalTestnet, - prerequisite_health_checkers: HashSet, - bind_to: Ipv4Addr, - test_dir: PathBuf, - node_api_url: Url, - ) -> Result { - Ok(Self { - config: RunConfig::build_for_cli( - node_api_url.clone(), - bind_to.to_string(), - args.faucet_args.faucet_port, - FunderKeyEnum::KeyFile(test_dir.join("mint.key")), - args.faucet_args.do_not_delegate, - None, - ), - prerequisite_health_checkers, - }) - } -} - -#[async_trait] -impl ServiceManager for FaucetManager { - fn get_name(&self) -> String { - "Faucet".to_string() - } - - fn get_health_checkers(&self) -> HashSet { - hashset! {HealthChecker::http_checker_from_port( - self.config.server_config.listen_port, - self.get_name(), - )} - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - self.prerequisite_health_checkers.iter().collect() - } - - async fn run_service(self: Box) -> Result<()> { - self.config.run().await - } -} diff --git a/crates/aptos/src/node/local_testnet/hasura_metadata.json b/crates/aptos/src/node/local_testnet/hasura_metadata.json deleted file mode 100644 index fa6f9669101a7..0000000000000 --- a/crates/aptos/src/node/local_testnet/hasura_metadata.json +++ /dev/null @@ -1,2158 +0,0 @@ -{ - "resource_version": 385, - "metadata": { - "version": 3, - "sources": [ - { - "name": "indexer-v2", - "kind": "postgres", - "tables": [ - { - "table": { - "name": "account_transactions", - "schema": "public" - }, - "array_relationships": [ - { - "name": "coin_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "coin_activities", - "schema": "public" - } - } - } - }, - { - "name": "delegated_staking_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "delegated_staking_activities", - "schema": "public" - } - } - } - }, - { - "name": "fungible_asset_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "fungible_asset_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities_v2", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "account_address", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "address_events_summary", - "schema": "public" - }, - "object_relationships": [ - { - "name": "block_metadata", - "using": { - "manual_configuration": { - "column_mapping": { - "min_block_height": "block_height" - }, - "insertion_order": null, - "remote_table": { - "name": "block_metadata_transactions", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "min_block_height", - "num_distinct_versions", - "account_address" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "address_version_from_events", - "schema": "public" - }, - "array_relationships": [ - { - "name": "coin_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "coin_activities", - "schema": "public" - } - } - } - }, - { - "name": "delegated_staking_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "delegated_staking_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities_v2", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "account_address", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "address_version_from_move_resources", - "schema": "public" - }, - "array_relationships": [ - { - "name": "coin_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "coin_activities", - "schema": "public" - } - } - } - }, - { - "name": "delegated_staking_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "delegated_staking_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities", - "schema": "public" - } - } - } - }, - { - "name": "token_activities_v2", - "using": { - "manual_configuration": { - "column_mapping": { - "transaction_version": "transaction_version" - }, - "insertion_order": null, - "remote_table": { - "name": "token_activities_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "address", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "block_metadata_transactions", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "block_height", - "epoch", - "failed_proposer_indices", - "id", - "previous_block_votes_bitvec", - "proposer", - "round", - "timestamp", - "version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "coin_activities", - "schema": "public" - }, - "object_relationships": [ - { - "name": "coin_info", - "using": { - "manual_configuration": { - "column_mapping": { - "coin_type": "coin_type" - }, - "insertion_order": null, - "remote_table": { - "name": "coin_infos", - "schema": "public" - } - } - } - } - ], - "array_relationships": [ - { - "name": "aptos_names", - "using": { - "manual_configuration": { - "column_mapping": { - "owner_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "activity_type", - "amount", - "block_height", - "coin_type", - "entry_function_id_str", - "event_account_address", - "event_creation_number", - "event_index", - "event_sequence_number", - "is_gas_fee", - "is_transaction_success", - "owner_address", - "storage_refund_amount", - "transaction_timestamp", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "coin_balances", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "coin_type", - "coin_type_hash", - "owner_address", - "transaction_timestamp", - "transaction_version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "coin_infos", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "coin_type", - "coin_type_hash", - "creator_address", - "decimals", - "name", - "supply_aggregator_table_handle", - "supply_aggregator_table_key", - "symbol", - "transaction_created_timestamp", - "transaction_version_created" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "coin_supply", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "coin_type", - "coin_type_hash", - "supply", - "transaction_epoch", - "transaction_timestamp", - "transaction_version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "collection_datas", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_data_id_hash", - "collection_name", - "creator_address", - "description", - "description_mutable", - "maximum", - "maximum_mutable", - "metadata_uri", - "supply", - "table_handle", - "transaction_timestamp", - "transaction_version", - "uri_mutable" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_ans_lookup", - "schema": "public" - }, - "array_relationships": [ - { - "name": "all_token_ownerships", - "using": { - "manual_configuration": { - "column_mapping": { - "token_name": "name" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_ownerships", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "domain", - "expiration_timestamp", - "is_deleted", - "last_transaction_version", - "registered_address", - "subdomain", - "token_name" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_ans_lookup_v2", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "domain", - "expiration_timestamp", - "is_deleted", - "last_transaction_version", - "registered_address", - "subdomain", - "token_name", - "token_standard" - ], - "filter": {}, - "limit": 100 - }, - "comment": "" - } - ] - }, - { - "table": { - "name": "current_aptos_names", - "schema": "public" - }, - "object_relationships": [ - { - "name": "is_domain_owner", - "using": { - "manual_configuration": { - "column_mapping": { - "domain_with_suffix": "token_name", - "owner_address": "owner_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "last_transaction_version", - "is_active", - "is_primary", - "domain", - "owner_address", - "registered_address", - "subdomain", - "token_name", - "token_standard", - "domain_with_suffix", - "expiration_timestamp" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - }, - "comment": "" - } - ] - }, - { - "table": { - "name": "current_coin_balances", - "schema": "public" - }, - "object_relationships": [ - { - "name": "coin_info", - "using": { - "manual_configuration": { - "column_mapping": { - "coin_type_hash": "coin_type_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "coin_infos", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "coin_type", - "coin_type_hash", - "last_transaction_timestamp", - "last_transaction_version", - "owner_address" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_collection_datas", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_data_id_hash", - "collection_name", - "creator_address", - "description", - "description_mutable", - "last_transaction_timestamp", - "last_transaction_version", - "maximum", - "maximum_mutable", - "metadata_uri", - "supply", - "table_handle", - "uri_mutable" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_collection_ownership_v2_view", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_collection", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_id": "collection_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collections_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "distinct_tokens", - "last_transaction_version", - "collection_id", - "collection_name", - "creator_address", - "owner_address", - "collection_uri", - "single_token_uri" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "current_collections_v2", - "schema": "public" - }, - "object_relationships": [], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_id", - "collection_name", - "creator_address", - "current_supply", - "description", - "last_transaction_timestamp", - "last_transaction_version", - "max_supply", - "mutable_description", - "mutable_uri", - "table_handle_v1", - "token_standard", - "total_minted_v2", - "uri" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_delegated_staking_pool_balances", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "active_table_handle", - "inactive_table_handle", - "last_transaction_version", - "operator_commission_percentage", - "staking_pool_address", - "total_coins", - "total_shares" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_delegated_voter", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "delegation_pool_address", - "delegator_address", - "last_transaction_timestamp", - "last_transaction_version", - "pending_voter", - "table_handle", - "voter" - ], - "filter": {}, - "limit": 100 - }, - "comment": "" - } - ] - }, - { - "table": { - "name": "current_delegator_balances", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_pool_balance", - "using": { - "manual_configuration": { - "column_mapping": { - "pool_address": "staking_pool_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_delegated_staking_pool_balances", - "schema": "public" - } - } - } - }, - { - "name": "staking_pool_metadata", - "using": { - "manual_configuration": { - "column_mapping": { - "pool_address": "staking_pool_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_staking_pool_voter", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "delegator_address", - "last_transaction_version", - "parent_table_handle", - "pool_address", - "pool_type", - "shares", - "table_handle" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_fungible_asset_balances", - "schema": "public" - }, - "object_relationships": [ - { - "name": "metadata", - "using": { - "manual_configuration": { - "column_mapping": { - "asset_type": "asset_type" - }, - "insertion_order": null, - "remote_table": { - "name": "fungible_asset_metadata", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "asset_type", - "is_frozen", - "is_primary", - "last_transaction_timestamp", - "last_transaction_version", - "owner_address", - "storage_id", - "token_standard" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "current_objects", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "allow_ungated_transfer", - "is_deleted", - "last_guid_creation_num", - "last_transaction_version", - "object_address", - "owner_address", - "state_key_hash" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_staking_pool_voter", - "schema": "public" - }, - "array_relationships": [ - { - "name": "operator_aptos_name", - "using": { - "manual_configuration": { - "column_mapping": { - "operator_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "last_transaction_version", - "operator_address", - "staking_pool_address", - "voter_address" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_table_items", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "decoded_key", - "decoded_value", - "is_deleted", - "key", - "key_hash", - "last_transaction_version", - "table_handle" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_token_datas", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_collection_data", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_data_id_hash": "collection_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collection_datas", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_data_id_hash", - "collection_name", - "creator_address", - "default_properties", - "description", - "description_mutable", - "largest_property_version", - "last_transaction_timestamp", - "last_transaction_version", - "maximum", - "maximum_mutable", - "metadata_uri", - "name", - "payee_address", - "properties_mutable", - "royalty_mutable", - "royalty_points_denominator", - "royalty_points_numerator", - "supply", - "token_data_id_hash", - "uri_mutable" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_token_datas_v2", - "schema": "public" - }, - "object_relationships": [ - { - "name": "aptos_name", - "using": { - "manual_configuration": { - "column_mapping": { - "token_name": "token_name" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - }, - { - "name": "current_collection", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_id": "collection_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collections_v2", - "schema": "public" - } - } - } - }, - { - "name": "current_token_ownership", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id": "token_data_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_ownerships_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_id", - "description", - "is_fungible_v2", - "largest_property_version_v1", - "last_transaction_timestamp", - "last_transaction_version", - "maximum", - "supply", - "token_data_id", - "token_name", - "token_properties", - "token_standard", - "token_uri" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "current_token_ownerships", - "schema": "public" - }, - "object_relationships": [ - { - "name": "aptos_name", - "using": { - "manual_configuration": { - "column_mapping": { - "name": "token_name" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - }, - { - "name": "current_collection_data", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_data_id_hash": "collection_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collection_datas", - "schema": "public" - } - } - } - }, - { - "name": "current_token_data", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id_hash": "token_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "collection_data_id_hash", - "collection_name", - "creator_address", - "last_transaction_timestamp", - "last_transaction_version", - "name", - "owner_address", - "property_version", - "table_type", - "token_data_id_hash", - "token_properties" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "current_token_ownerships_v2", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_token_data", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id": "token_data_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas_v2", - "schema": "public" - } - } - } - } - ], - "array_relationships": [ - { - "name": "composed_nfts", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id": "owner_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_ownerships_v2", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "is_fungible_v2", - "is_soulbound_v2", - "last_transaction_timestamp", - "last_transaction_version", - "owner_address", - "property_version_v1", - "storage_id", - "table_type_v1", - "token_data_id", - "token_properties_mutated_v1", - "token_standard" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "current_token_pending_claims", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_collection_data", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_data_id_hash": "collection_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collection_datas", - "schema": "public" - } - } - } - }, - { - "name": "current_collection_v2", - "using": { - "manual_configuration": { - "column_mapping": { - "collection_id": "collection_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_collections_v2", - "schema": "public" - } - } - } - }, - { - "name": "current_token_data", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id_hash": "token_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas", - "schema": "public" - } - } - } - }, - { - "name": "current_token_data_v2", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id": "token_data_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas_v2", - "schema": "public" - } - } - } - }, - { - "name": "token", - "using": { - "manual_configuration": { - "column_mapping": { - "last_transaction_version": "transaction_version", - "property_version": "property_version", - "token_data_id_hash": "token_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "tokens", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "collection_data_id_hash", - "collection_id", - "collection_name", - "creator_address", - "from_address", - "last_transaction_timestamp", - "last_transaction_version", - "name", - "property_version", - "table_handle", - "to_address", - "token_data_id", - "token_data_id_hash" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "delegated_staking_activities", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "delegator_address", - "event_index", - "event_type", - "pool_address", - "transaction_version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "delegated_staking_pool_balances", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "active_table_handle", - "inactive_table_handle", - "operator_commission_percentage", - "staking_pool_address", - "total_coins", - "total_shares", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - }, - "comment": "" - } - ] - }, - { - "table": { - "name": "delegated_staking_pools", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_staking_pool", - "using": { - "manual_configuration": { - "column_mapping": { - "staking_pool_address": "staking_pool_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_staking_pool_voter", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "first_transaction_version", - "staking_pool_address" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "delegator_distinct_pool", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_pool_balance", - "using": { - "manual_configuration": { - "column_mapping": { - "pool_address": "staking_pool_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_delegated_staking_pool_balances", - "schema": "public" - } - } - } - }, - { - "name": "staking_pool_metadata", - "using": { - "manual_configuration": { - "column_mapping": { - "pool_address": "staking_pool_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_staking_pool_voter", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "delegator_address", - "pool_address" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "events", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "account_address", - "creation_number", - "data", - "event_index", - "sequence_number", - "transaction_block_height", - "transaction_version", - "type", - "indexed_type" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "fungible_asset_activities", - "schema": "public" - }, - "object_relationships": [ - { - "name": "metadata", - "using": { - "manual_configuration": { - "column_mapping": { - "asset_type": "asset_type" - }, - "insertion_order": null, - "remote_table": { - "name": "fungible_asset_metadata", - "schema": "public" - } - } - } - } - ], - "array_relationships": [ - { - "name": "owner_aptos_names", - "using": { - "manual_configuration": { - "column_mapping": { - "owner_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "asset_type", - "block_height", - "entry_function_id_str", - "event_index", - "gas_fee_payer_address", - "is_frozen", - "is_gas_fee", - "is_transaction_success", - "owner_address", - "storage_id", - "storage_refund_amount", - "token_standard", - "transaction_timestamp", - "transaction_version", - "type" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "fungible_asset_metadata", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "asset_type", - "creator_address", - "decimals", - "icon_uri", - "last_transaction_timestamp", - "last_transaction_version", - "name", - "project_uri", - "supply_aggregator_table_handle_v1", - "supply_aggregator_table_key_v1", - "symbol", - "token_standard" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "indexer_status", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "db", - "is_indexer_up" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "ledger_infos", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "chain_id" - ], - "filter": {} - } - } - ] - }, - { - "table": { - "name": "move_resources", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "address", - "transaction_version" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "num_active_delegator_per_pool", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "num_active_delegator", - "pool_address" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "processor_status", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "last_success_version", - "last_transaction_timestamp", - "last_updated", - "processor" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "proposal_votes", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "num_votes", - "proposal_id", - "should_pass", - "staking_pool_address", - "transaction_timestamp", - "transaction_version", - "voter_address" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "table_items", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "decoded_key", - "decoded_value", - "key", - "table_handle", - "transaction_version", - "write_set_change_index" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "table_metadatas", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "handle", - "key_type", - "value_type" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "token_activities", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_token_data", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id_hash": "token_data_id_hash" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas", - "schema": "public" - } - } - } - } - ], - "array_relationships": [ - { - "name": "aptos_names_owner", - "using": { - "manual_configuration": { - "column_mapping": { - "event_account_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - }, - { - "name": "aptos_names_to", - "using": { - "manual_configuration": { - "column_mapping": { - "to_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "coin_amount", - "coin_type", - "collection_data_id_hash", - "collection_name", - "creator_address", - "event_account_address", - "event_creation_number", - "event_index", - "event_sequence_number", - "from_address", - "name", - "property_version", - "to_address", - "token_amount", - "token_data_id_hash", - "transaction_timestamp", - "transaction_version", - "transfer_type" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "token_activities_v2", - "schema": "public" - }, - "object_relationships": [ - { - "name": "current_token_data", - "using": { - "manual_configuration": { - "column_mapping": { - "token_data_id": "token_data_id" - }, - "insertion_order": null, - "remote_table": { - "name": "current_token_datas_v2", - "schema": "public" - } - } - } - } - ], - "array_relationships": [ - { - "name": "aptos_names_from", - "using": { - "manual_configuration": { - "column_mapping": { - "from_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - }, - { - "name": "aptos_names_to", - "using": { - "manual_configuration": { - "column_mapping": { - "to_address": "registered_address" - }, - "insertion_order": null, - "remote_table": { - "name": "current_aptos_names", - "schema": "public" - } - } - } - } - ], - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "after_value", - "before_value", - "entry_function_id_str", - "event_account_address", - "event_index", - "from_address", - "is_fungible_v2", - "property_version_v1", - "to_address", - "token_amount", - "token_data_id", - "token_standard", - "transaction_timestamp", - "transaction_version", - "type" - ], - "filter": {}, - "limit": 100, - "allow_aggregations": true - } - } - ] - }, - { - "table": { - "name": "token_datas", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_data_id_hash", - "collection_name", - "creator_address", - "default_properties", - "description", - "description_mutable", - "largest_property_version", - "maximum", - "maximum_mutable", - "metadata_uri", - "name", - "payee_address", - "properties_mutable", - "royalty_mutable", - "royalty_points_denominator", - "royalty_points_numerator", - "supply", - "token_data_id_hash", - "transaction_timestamp", - "transaction_version", - "uri_mutable" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "token_ownerships", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "amount", - "collection_data_id_hash", - "collection_name", - "creator_address", - "name", - "owner_address", - "property_version", - "table_handle", - "table_type", - "token_data_id_hash", - "transaction_timestamp", - "transaction_version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "tokens", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "collection_data_id_hash", - "collection_name", - "creator_address", - "name", - "property_version", - "token_data_id_hash", - "token_properties", - "transaction_timestamp", - "transaction_version" - ], - "filter": {}, - "limit": 100 - } - } - ] - }, - { - "table": { - "name": "user_transactions", - "schema": "public" - }, - "select_permissions": [ - { - "role": "anonymous", - "permission": { - "columns": [ - "block_height", - "entry_function_id_str", - "epoch", - "expiration_timestamp_secs", - "gas_unit_price", - "max_gas_amount", - "parent_signature_type", - "sender", - "sequence_number", - "timestamp", - "version" - ], - "filter": {}, - "limit": 100 - } - } - ] - } - ], - "configuration": { - "connection_info": { - "database_url": { - "from_env": "INDEXER_V2_POSTGRES_URL" - }, - "isolation_level": "read-committed", - "use_prepared_statements": false - } - } - } - ], - "query_collections": [ - { - "name": "allowed-queries", - "definition": { - "queries": [ - { - "name": "Latest Processor Status", - "query": "query LatestProcessorStatus {\n processor_status {\n processor\n last_updated\n last_success_version\n last_transaction_timestamp\n }\n}" - } - ] - } - } - ], - "allowlist": [ - { - "collection": "allowed-queries", - "scope": { - "global": true - } - } - ], - "rest_endpoints": [ - { - "comment": "", - "definition": { - "query": { - "collection_name": "allowed-queries", - "query_name": "Latest Processor Status" - } - }, - "methods": [ - "GET" - ], - "name": "Latest Processor Status", - "url": "get_lastest_processor_status" - } - ], - "api_limits": { - "depth_limit": { - "global": 5, - "per_role": {} - }, - "disabled": false, - "time_limit": { - "global": 10, - "per_role": {} - } - }, - "metrics_config": { - "analyze_query_variables": true, - "analyze_response_body": true - } - } -} diff --git a/crates/aptos/src/node/local_testnet/health_checker.rs b/crates/aptos/src/node/local_testnet/health_checker.rs deleted file mode 100644 index ec22495b3c07e..0000000000000 --- a/crates/aptos/src/node/local_testnet/health_checker.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::indexer_api::confirm_metadata_applied; -use anyhow::{anyhow, Context, Result}; -use aptos_protos::indexer::v1::GetTransactionsRequest; -use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; -use diesel_async::{pg::AsyncPgConnection, AsyncConnection, RunQueryDsl}; -use futures::StreamExt; -use processor::schema::processor_status; -use reqwest::Url; -use serde::Serialize; -use std::time::Duration; -use tokio::time::Instant; -use tracing::info; - -const MAX_WAIT_S: u64 = 60; -const WAIT_INTERVAL_MS: u64 = 200; - -/// This provides a single place to define a variety of different healthchecks. In -/// cases where the name of the service being checked isn't obvious, the enum will take -/// a string arg that names it. -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] -pub enum HealthChecker { - /// Check that a HTTP API is up. The second param is the name of the HTTP service. - Http(Url, String), - /// Check that the node API is up. This is just a specific case of Http for extra - /// guarantees around liveliness. - NodeApi(Url), - /// Check that a data service GRPC stream is up. - DataServiceGrpc(Url), - /// Check that a postgres instance is up. - Postgres(String), - /// Check that a processor is successfully processing txns. The first value is the - /// postgres connection string. The second is the name of the processor. We check - /// the that last_success_version in the processor_status table is present and > 0. - Processor(String, String), - /// Check that the indexer API is up and the metadata has been applied. We only use - /// this one in the ready server. - IndexerApiMetadata(Url), -} - -impl HealthChecker { - pub async fn check(&self) -> Result<()> { - match self { - HealthChecker::Http(url, _) => { - reqwest::get(Url::clone(url)) - .await - .with_context(|| format!("Failed to GET {}", url))?; - Ok(()) - }, - HealthChecker::NodeApi(url) => { - aptos_rest_client::Client::new(Url::clone(url)) - .get_index() - .await?; - Ok(()) - }, - HealthChecker::DataServiceGrpc(url) => { - let mut client = aptos_indexer_grpc_utils::create_data_service_grpc_client( - url.clone(), - Some(Duration::from_secs(5)), - ) - .await?; - let request = tonic::Request::new(GetTransactionsRequest { - starting_version: Some(0), - ..Default::default() - }); - // Make sure we can stream the first message from the stream. - client - .get_transactions(request) - .await - .context("GRPC connection error")? - .into_inner() - .next() - .await - .context("Did not receive init signal from data service GRPC stream")? - .context("Error processing first message from GRPC stream")?; - Ok(()) - }, - HealthChecker::Postgres(connection_string) => { - AsyncPgConnection::establish(connection_string) - .await - .context("Failed to connect to postgres to check DB liveness")?; - Ok(()) - }, - HealthChecker::Processor(connection_string, processor_name) => { - let mut connection = AsyncPgConnection::establish(connection_string) - .await - .context("Failed to connect to postgres to check processor status")?; - let result = processor_status::table - .select((processor_status::last_success_version,)) - .filter(processor_status::processor.eq(processor_name)) - .first::<(i64,)>(&mut connection) - .await - .optional() - .context("Failed to look up processor status")?; - match result { - Some(result) => { - // This is last_success_version. - if result.0 > 0 { - info!( - "Processor {} started processing successfully (currently at version {})", - processor_name, result.0 - ); - Ok(()) - } else { - Err(anyhow!( - "Processor {} found in DB but last_success_version is zero", - processor_name - )) - } - }, - None => Err(anyhow!( - "Processor {} has not processed any transactions", - processor_name - )), - } - }, - HealthChecker::IndexerApiMetadata(url) => { - confirm_metadata_applied(url.clone()).await?; - Ok(()) - }, - } - } - - /// Wait up to MAX_WAIT_S seconds for a service to start up. - pub async fn wait( - &self, - // The service, if any, waiting for this service to start up. - waiting_service: Option<&str>, - ) -> Result<()> { - let prefix = self.to_string(); - wait_for_startup(|| self.check(), match waiting_service { - Some(waiting_service) => { - format!( - "{} at {} did not start up before {}", - prefix, - self.address_str(), - waiting_service, - ) - }, - None => format!("{} at {} did not start up", prefix, self.address_str()), - }) - .await - } - - /// This is only ever used for display purposes. If possible, this should be the - /// endpoint of the service that this HealthChecker is checking. - pub fn address_str(&self) -> &str { - match self { - HealthChecker::Http(url, _) => url.as_str(), - HealthChecker::NodeApi(url) => url.as_str(), - HealthChecker::DataServiceGrpc(url) => url.as_str(), - HealthChecker::Postgres(url) => url.as_str(), - HealthChecker::Processor(_, processor_name) => processor_name.as_str(), - HealthChecker::IndexerApiMetadata(url) => url.as_str(), - } - } - - /// Given a port, make an instance of HealthChecker::Http targeting 127.0.0.1. - pub fn http_checker_from_port(port: u16, name: String) -> Self { - Self::Http( - Url::parse(&format!("http://127.0.0.1:{}", port,)).unwrap(), - name, - ) - } -} - -impl std::fmt::Display for HealthChecker { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - HealthChecker::Http(_, name) => write!(f, "{}", name), - HealthChecker::NodeApi(_) => write!(f, "Node API"), - HealthChecker::DataServiceGrpc(_) => write!(f, "Transaction stream"), - HealthChecker::Postgres(_) => write!(f, "Postgres"), - HealthChecker::Processor(_, processor_name) => write!(f, "{}", processor_name), - HealthChecker::IndexerApiMetadata(_) => write!(f, "Indexer API with metadata applied"), - } - } -} - -async fn wait_for_startup(check_fn: F, error_message: String) -> Result<()> -where - F: Fn() -> Fut, - Fut: futures::Future>, -{ - let max_wait = Duration::from_secs(MAX_WAIT_S); - let wait_interval = Duration::from_millis(WAIT_INTERVAL_MS); - - let start = Instant::now(); - let mut started_successfully = false; - - let mut last_error_message = None; - while start.elapsed() < max_wait { - match check_fn().await { - Ok(_) => { - started_successfully = true; - break; - }, - Err(err) => { - last_error_message = Some(format!("{:#}", err)); - }, - } - tokio::time::sleep(wait_interval).await - } - - if !started_successfully { - let error_message = match last_error_message { - Some(last_error_message) => format!("{}: {}", error_message, last_error_message), - None => error_message, - }; - return Err(anyhow!(error_message)); - } - - Ok(()) -} diff --git a/crates/aptos/src/node/local_testnet/indexer_api.rs b/crates/aptos/src/node/local_testnet/indexer_api.rs deleted file mode 100644 index 3440003aa53ae..0000000000000 --- a/crates/aptos/src/node/local_testnet/indexer_api.rs +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - docker::{ - delete_container, get_docker, pull_docker_image, setup_docker_logging, - StopContainerShutdownStep, CONTAINER_NETWORK_NAME, - }, - health_checker::HealthChecker, - traits::{PostHealthyStep, ServiceManager, ShutdownStep}, - RunLocalTestnet, -}; -use anyhow::{anyhow, Context, Result}; -use async_trait::async_trait; -use bollard::{ - container::{Config, CreateContainerOptions, StartContainerOptions, WaitContainerOptions}, - models::{HostConfig, PortBinding}, -}; -use clap::Parser; -use futures::TryStreamExt; -use maplit::{hashmap, hashset}; -use reqwest::Url; -use std::{collections::HashSet, path::PathBuf}; -use tracing::{info, warn}; - -const INDEXER_API_CONTAINER_NAME: &str = "local-testnet-indexer-api"; -const HASURA_IMAGE: &str = "hasura/graphql-engine:v2.36.1"; - -/// This Hasura metadata originates from the aptos-indexer-processors repo. -/// -/// This metadata is from revision: 4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf. -/// -/// The metadata file is not taken verbatim, it is currently edited by hand to remove -/// any references to tables that aren't created by the Rust processor migrations. -/// -/// To arrive at the final edited file I normally start with the new metadata file, -/// try to start the local testnet, and check .aptos/testnet/main/tracing.log to -/// see what error Hasura returned. Remove the culprit from the metadata, which is -/// generally a few tables and relations to those tables, and try again. Repeat until -/// it accepts the metadata. -/// -/// This works fine today since all the key processors you'd need in a local testnet -/// are in the set of processors written in Rust. If this changes, we can explore -/// alternatives, e.g. running processors in other languages using containers. -const HASURA_METADATA: &str = include_str!("hasura_metadata.json"); - -/// Args related to running an indexer API for the local testnet. -#[derive(Debug, Parser)] -pub struct IndexerApiArgs { - /// If set, we will run a postgres DB using Docker (unless - /// --use-host-postgres is set), run the standard set of indexer processors (see - /// --processors), and configure them to write to this DB, and run an API that lets - /// you access the data they write to storage. This is opt in because it requires - /// Docker to be installed on the host system. - #[clap(long, conflicts_with = "no_txn_stream")] - pub with_indexer_api: bool, - - /// The port at which to run the indexer API. - #[clap(long, default_value_t = 8090)] - pub indexer_api_port: u16, -} - -#[derive(Clone, Debug)] -pub struct IndexerApiManager { - indexer_api_port: u16, - prerequisite_health_checkers: HashSet, - test_dir: PathBuf, - postgres_connection_string: String, -} - -impl IndexerApiManager { - pub fn new( - args: &RunLocalTestnet, - prerequisite_health_checkers: HashSet, - test_dir: PathBuf, - postgres_connection_string: String, - ) -> Result { - Ok(Self { - indexer_api_port: args.indexer_api_args.indexer_api_port, - prerequisite_health_checkers, - test_dir, - postgres_connection_string, - }) - } - - pub fn get_url(&self) -> Url { - Url::parse(&format!("http://127.0.0.1:{}", self.indexer_api_port)).unwrap() - } -} - -#[async_trait] -impl ServiceManager for IndexerApiManager { - fn get_name(&self) -> String { - "Indexer API".to_string() - } - - async fn pre_run(&self) -> Result<()> { - // Confirm Docker is available. - get_docker().await?; - - // Delete any existing indexer API container we find. - delete_container(INDEXER_API_CONTAINER_NAME).await?; - - // Pull the image here so it is not subject to the 30 second startup timeout. - pull_docker_image(HASURA_IMAGE).await?; - - // Warn the user about DOCKER_DEFAULT_PLATFORM. - if let Ok(var) = std::env::var("DOCKER_DEFAULT_PLATFORM") { - eprintln!( - "WARNING: DOCKER_DEFAULT_PLATFORM is set to {}. This may cause problems \ - with running the indexer API. If it fails to start up, try unsetting \ - this env var.\n", - var - ); - } - - Ok(()) - } - - /// In this case we we return two HealthCheckers, one for whether the Hasura API - /// is up at all and one for whether the metadata is applied. - fn get_health_checkers(&self) -> HashSet { - hashset! { - // This first one just checks if the API is up at all. - HealthChecker::Http(self.get_url(), "Indexer API".to_string()), - // This second one checks if the metadata is applied. - HealthChecker::IndexerApiMetadata(self.get_url()), - } - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - self.prerequisite_health_checkers.iter().collect() - } - - async fn run_service(self: Box) -> Result<()> { - setup_docker_logging(&self.test_dir, "indexer-api", INDEXER_API_CONTAINER_NAME)?; - - // This is somewhat hard to maintain. If it requires any further maintenance we - // should just delete support for using Postgres on the host system. - let (postgres_connection_string, network_mode) = - // When connecting to postgres on the host via an IP from inside a - // container, we need to instead connect to host.docker.internal. - // There is no need to bind to a Docker network in this case. - if self.postgres_connection_string.contains("127.0.0.1") { - ( - self.postgres_connection_string - .replace("127.0.0.1", "host.docker.internal"), - None, - ) - } else { - // Otherwise we use the standard connection string (containing the name - // of the container) and bind to the Docker network we created earlier - // in the Postgres pre_run steps. - ( - self.postgres_connection_string, - Some(CONTAINER_NETWORK_NAME.to_string()), - ) - }; - - let exposed_ports = Some(hashmap! {self.indexer_api_port.to_string() => hashmap!{}}); - let host_config = HostConfig { - // Connect the container to the network we made in the postgres pre_run. - // This allows the indexer API to access the postgres container without - // routing through the host network. - network_mode, - // This is necessary so connecting to the host postgres works on Linux. - extra_hosts: Some(vec!["host.docker.internal:host-gateway".to_string()]), - port_bindings: Some(hashmap! { - self.indexer_api_port.to_string() => Some(vec![PortBinding { - host_ip: Some("127.0.0.1".to_string()), - host_port: Some(self.indexer_api_port.to_string()), - }]), - }), - ..Default::default() - }; - - let docker = get_docker().await?; - - info!( - "Using postgres connection string: {}", - postgres_connection_string - ); - - let config = Config { - image: Some(HASURA_IMAGE.to_string()), - tty: Some(true), - exposed_ports, - host_config: Some(host_config), - env: Some(vec![ - format!("PG_DATABASE_URL={}", postgres_connection_string), - format!( - "HASURA_GRAPHQL_METADATA_DATABASE_URL={}", - postgres_connection_string - ), - format!("INDEXER_V2_POSTGRES_URL={}", postgres_connection_string), - "HASURA_GRAPHQL_DEV_MODE=true".to_string(), - "HASURA_GRAPHQL_ENABLE_CONSOLE=true".to_string(), - // See the docs for the image, this is a magic path inside the - // container where they have already bundled in the UI assets. - "HASURA_GRAPHQL_CONSOLE_ASSETS_DIR=/srv/console-assets".to_string(), - format!("HASURA_GRAPHQL_SERVER_PORT={}", self.indexer_api_port), - ]), - ..Default::default() - }; - - let options = Some(CreateContainerOptions { - name: INDEXER_API_CONTAINER_NAME, - ..Default::default() - }); - - info!("Starting indexer API with this config: {:?}", config); - - let id = docker.create_container(options, config).await?.id; - - info!("Created container for indexer API with this ID: {}", id); - - docker - .start_container(&id, None::>) - .await - .context("Failed to start indexer API container")?; - - info!("Started indexer API container {}", id); - - // Wait for the container to stop (which it shouldn't). - let wait = docker - .wait_container( - &id, - Some(WaitContainerOptions { - condition: "not-running", - }), - ) - .try_collect::>() - .await - .context("Failed to wait on indexer API container")?; - - warn!("Indexer API stopped: {:?}", wait.last()); - - Ok(()) - } - - fn get_post_healthy_steps(&self) -> Vec> { - /// There is no good way to apply Hasura metadata (the JSON format, anyway) to - /// an instance of Hasura in a container at startup: - /// - /// https://github.com/hasura/graphql-engine/issues/8423 - /// - /// As such, the only way to do it is to apply it via the API after startup. - /// That is what this post healthy step does. - #[derive(Debug)] - struct PostMetdataPostHealthyStep { - pub indexer_api_url: Url, - } - - #[async_trait] - impl PostHealthyStep for PostMetdataPostHealthyStep { - async fn run(self: Box) -> Result<()> { - post_metadata(self.indexer_api_url, HASURA_METADATA) - .await - .context("Failed to apply Hasura metadata for Indexer API")?; - Ok(()) - } - } - - vec![Box::new(PostMetdataPostHealthyStep { - indexer_api_url: self.get_url(), - })] - } - - fn get_shutdown_steps(&self) -> Vec> { - // Unfortunately the Hasura container does not shut down when the CLI does and - // there doesn't seem to be a good way to make it do so. To work around this, - // we register a step that will stop the container on shutdown. - // Read more here: https://stackoverflow.com/q/77171786/3846032. - vec![Box::new(StopContainerShutdownStep::new( - INDEXER_API_CONTAINER_NAME, - ))] - } -} - -/// This submits a POST request to apply metadata to a Hasura API. -async fn post_metadata(url: Url, metadata_content: &str) -> Result<()> { - // Parse the metadata content as JSON. - let metadata_json: serde_json::Value = serde_json::from_str(metadata_content)?; - - // Make the request. - info!("Submitting request to apply Hasura metadata"); - let response = - make_hasura_metadata_request(url, "replace_metadata", Some(metadata_json)).await?; - info!( - "Received response for applying Hasura metadata: {:?}", - response - ); - - // Confirm that the metadata was applied successfully and there is no inconsistency - // between the schema and the underlying DB schema. - if let Some(obj) = response.as_object() { - if let Some(is_consistent_val) = obj.get("is_consistent") { - if is_consistent_val.as_bool() == Some(true) { - return Ok(()); - } - } - } - - Err(anyhow!( - "Something went wrong applying the Hasura metadata, perhaps it is not consistent with the DB. Response: {:#?}", - response - )) -} - -/// This confirms that the metadata has been applied. We use this in the health -/// checker. -pub async fn confirm_metadata_applied(url: Url) -> Result<()> { - // Make the request. - info!("Confirming Hasura metadata applied..."); - let response = make_hasura_metadata_request(url, "export_metadata", None).await?; - info!( - "Received response for confirming Hasura metadata applied: {:?}", - response - ); - - // If the sources field is set it means the metadata was applied successfully. - if let Some(obj) = response.as_object() { - if let Some(sources) = obj.get("sources") { - if let Some(sources) = sources.as_array() { - if !sources.is_empty() { - return Ok(()); - } - } - } - } - - Err(anyhow!( - "The Hasura metadata has not been applied yet. Response: {:#?}", - response - )) -} - -/// The /v1/metadata endpoint supports a few different operations based on the `type` -/// field in the request body. All requests have a similar format, with these `type` -/// and `args` fields. -async fn make_hasura_metadata_request( - mut url: Url, - typ: &str, - args: Option, -) -> Result { - let client = reqwest::Client::new(); - - // Update the query path. - url.set_path("/v1/metadata"); - - // Construct the payload. - let mut payload = serde_json::Map::new(); - payload.insert( - "type".to_string(), - serde_json::Value::String(typ.to_string()), - ); - - // If args is provided, use that. Otherwise use an empty object. We have to set it - // no matter what because the API expects the args key to be set. - let args = match args { - Some(args) => args, - None => serde_json::Value::Object(serde_json::Map::new()), - }; - payload.insert("args".to_string(), args); - - // Send the POST request. - let response = client.post(url).json(&payload).send().await?; - - // Return the response as a JSON value. - response - .json() - .await - .context("Failed to parse response as JSON") -} diff --git a/crates/aptos/src/node/local_testnet/logging.rs b/crates/aptos/src/node/local_testnet/logging.rs deleted file mode 100644 index 3752582070d94..0000000000000 --- a/crates/aptos/src/node/local_testnet/logging.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use dashmap::DashMap; -use std::{ - fs::{create_dir_all, File}, - path::PathBuf, - sync::{Arc, Mutex}, -}; -use tracing_subscriber::fmt::MakeWriter; - -/// This struct impls MakeWriter, a trait that returns writers for using by the -/// tracing_subscriber library. It returns a custom logger that logs to different -/// files based on the name of the worker (thread). -/// -/// To be as efficient as possible and only create + open each file once, we keep -/// track of file handles in a DashMap with this FileLock struct as the values. -/// Learn more about FileLock in the doc comment there. -pub struct ThreadNameMakeWriter { - /// The base directory to output logs to. - base_dir: PathBuf, - /// We keep open file handles here to avoid making them every time. They key is the - /// name of the file (thread name without the number). - file_handles: DashMap, -} - -impl ThreadNameMakeWriter { - pub fn new(base_dir: PathBuf) -> Self { - Self { - base_dir, - file_handles: DashMap::new(), - } - } -} - -impl<'a> MakeWriter<'a> for ThreadNameMakeWriter { - type Writer = Box; - - fn make_writer(&'a self) -> Self::Writer { - let base_dir = self.base_dir.clone(); - let thread_name = std::thread::current() - .name() - .unwrap_or("no-thread-name") - .to_string(); - let thread_name_no_number = truncate_last_segment(&thread_name, '-'); - let log_file = self - .file_handles - .entry(thread_name_no_number.clone()) - .or_insert_with(|| FileLock::new(create_file(base_dir, thread_name_no_number))) - .value() - .clone(); - Box::new(log_file) - } -} - -fn create_file(base_dir: PathBuf, thread_name_no_number: String) -> File { - let dir_path = base_dir.join(thread_name_no_number); - create_dir_all(&dir_path).expect("Failed to create log directory"); - let log_path = dir_path.join("tracing.log"); - std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(log_path) - .unwrap() -} - -fn truncate_last_segment(s: &str, delimiter: char) -> String { - s.rsplit_once(delimiter) - .map(|x| x.0) - .unwrap_or(s) - .to_string() -} - -/// This struct protects access to an open file handle. Using a Mutex is necessary -/// because we cannot allow concurrent access to the file. The Arc ensures that -/// every time we hand out a reference to the handle, it is the same handle. -#[derive(Clone)] -pub struct FileLock { - file: Arc>, -} - -impl FileLock { - pub fn new(file: File) -> Self { - Self { - file: Arc::new(Mutex::new(file)), - } - } -} - -impl std::io::Write for FileLock { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.file.lock().unwrap().write(buf) - } - - fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - self.file.lock().unwrap().write_all(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.file.lock().unwrap().flush() - } -} diff --git a/crates/aptos/src/node/local_testnet/mod.rs b/crates/aptos/src/node/local_testnet/mod.rs deleted file mode 100644 index ad270ba2f1c69..0000000000000 --- a/crates/aptos/src/node/local_testnet/mod.rs +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod docker; -mod faucet; -mod health_checker; -mod indexer_api; -mod logging; -mod node; -mod postgres; -mod processors; -mod ready_server; -mod traits; -mod utils; - -use self::{ - faucet::FaucetArgs, - health_checker::HealthChecker, - indexer_api::IndexerApiArgs, - logging::ThreadNameMakeWriter, - node::NodeArgs, - postgres::PostgresArgs, - processors::ProcessorArgs, - ready_server::ReadyServerArgs, - traits::{PostHealthyStep, ServiceManager}, -}; -use crate::{ - common::{ - types::{CliCommand, CliError, CliTypedResult, ConfigSearchMode, PromptOptions}, - utils::prompt_yes_with_override, - }, - config::GlobalConfig, - node::local_testnet::{ - faucet::FaucetManager, indexer_api::IndexerApiManager, node::NodeManager, - processors::ProcessorManager, ready_server::ReadyServerManager, traits::ShutdownStep, - }, -}; -use anyhow::{Context, Result}; -use aptos_indexer_grpc_server_framework::setup_logging; -use async_trait::async_trait; -use clap::Parser; -use std::{ - collections::HashSet, - fs::{create_dir_all, remove_dir_all}, - net::Ipv4Addr, - path::{Path, PathBuf}, - pin::Pin, -}; -use tokio::task::JoinSet; -use tracing::{info, warn}; -use tracing_subscriber::fmt::MakeWriter; - -const TESTNET_FOLDER: &str = "testnet"; - -/// Run a local testnet -/// -/// This local testnet will run it's own genesis and run as a single node network -/// locally. A faucet and grpc transaction stream will run alongside the node unless -/// you specify otherwise with --no-faucet and --no-txn-stream respectively. -#[derive(Parser)] -pub struct RunLocalTestnet { - /// The directory to save all files for the node - /// - /// Defaults to .aptos/testnet - #[clap(long, value_parser)] - test_dir: Option, - - /// Clean the state and start with a new chain at genesis - /// - /// This will wipe the aptosdb in `--test-dir` to remove any incompatible changes, and start - /// the chain fresh. Note, that you will need to publish the module again and distribute funds - /// from the faucet accordingly. - #[clap(long)] - force_restart: bool, - - #[clap(flatten)] - node_args: NodeArgs, - - #[clap(flatten)] - faucet_args: FaucetArgs, - - #[clap(flatten)] - postgres_args: PostgresArgs, - - #[clap(flatten)] - processor_args: ProcessorArgs, - - #[clap(flatten)] - indexer_api_args: IndexerApiArgs, - - #[clap(flatten)] - ready_server_args: ReadyServerArgs, - - #[clap(flatten)] - prompt_options: PromptOptions, - - /// By default all services running on the host system will be bound to 127.0.0.1, - /// unless you're running the CLI inside a container, in which case it will run - /// them on 0.0.0.0. You can use this flag to override this behavior in both cases. - #[clap(long, hide = true)] - bind_to: Option, - - /// By default, tracing output goes to files. With this set, it goes to stdout. - #[clap(long, hide = true)] - log_to_stdout: bool, -} - -impl RunLocalTestnet { - /// Wait for many services to start up. This prints a message like "X is starting, - /// please wait..." for each service and then "X is ready. Endpoint: " - /// when it's ready. - async fn wait_for_startup<'a>( - &self, - health_checkers: &HashSet, - test_dir: &Path, - ) -> CliTypedResult<()> { - let mut futures: Vec> + Send>>> = - Vec::new(); - - for health_checker in health_checkers { - let silent = match health_checker { - HealthChecker::NodeApi(_) => false, - HealthChecker::Http(_, _) => false, - HealthChecker::DataServiceGrpc(_) => false, - HealthChecker::Postgres(_) => false, - // We don't want to print anything for the processors, it'd be too spammy. - HealthChecker::Processor(_, _) => true, - // We don't want to actually wait on this health checker here because - // it will never return true since we apply the metadata in a post - // healthy step (which comes after we call this function). So we move - // on. This is a bit of a leaky abstraction that we can solve with more - // lifecycle hooks down the line. - HealthChecker::IndexerApiMetadata(_) => continue, - }; - if !silent { - eprintln!("{} is starting, please wait...", health_checker); - } else { - info!("[silent] {} is starting, please wait...", health_checker); - } - let fut = async move { - health_checker.wait(None).await?; - if !silent { - eprintln!( - "{} is ready. Endpoint: {}", - health_checker, - health_checker.address_str() - ); - } else { - info!( - "[silent] {} is ready. Endpoint: {}", - health_checker, - health_checker.address_str() - ); - } - Ok(()) - }; - futures.push(Box::pin(fut)); - } - - eprintln!(); - - // We use join_all because we expect all of these to return. - for f in futures::future::join_all(futures).await { - f.map_err(|err| { - CliError::UnexpectedError(format!( - "One of the services failed to start up: {:?}. \ - Please check the logs at {} for more information.", - err, - test_dir.display(), - )) - })?; - } - - Ok(()) - } -} - -#[async_trait] -impl CliCommand<()> for RunLocalTestnet { - fn command_name(&self) -> &'static str { - "RunLocalTestnet" - } - - fn jsonify_error_output(&self) -> bool { - false - } - - async fn execute(mut self) -> CliTypedResult<()> { - if self.log_to_stdout { - setup_logging(None); - } - - let global_config = GlobalConfig::load().context("Failed to load global config")?; - let test_dir = match &self.test_dir { - Some(test_dir) => test_dir.clone(), - None => global_config - .get_config_location(ConfigSearchMode::CurrentDirAndParents)? - .join(TESTNET_FOLDER), - }; - - // If asked, remove the current test directory and start with a new node. - if self.force_restart && test_dir.exists() { - prompt_yes_with_override( - "Are you sure you want to delete the existing local testnet data?", - self.prompt_options, - )?; - remove_dir_all(test_dir.as_path()).map_err(|err| { - CliError::IO(format!("Failed to delete {}", test_dir.display()), err) - })?; - info!("Deleted test directory at: {:?}", test_dir); - } - - if !test_dir.exists() { - info!("Test directory does not exist, creating it: {:?}", test_dir); - create_dir_all(test_dir.as_path()).map_err(|err| { - CliError::IO(format!("Failed to create {}", test_dir.display()), err) - })?; - info!("Created test directory: {:?}", test_dir); - } - - // We set up directory based logging after we have created test_dir. - if !self.log_to_stdout { - // Set up logging for anything that uses tracing. These logs will go to - // different directories based on the name of the runtime. - let td = test_dir.clone(); - let make_writer = move || { - ThreadNameMakeWriter::new(td.clone()).make_writer() as Box - }; - setup_logging(Some(Box::new(make_writer))); - } - - // If the CLI is running inside a container, bind services not running inside a - // container to 0.0.0.0 (so they can be accessed from outside the container). - // Otherwise bind them to 127.0.0.1. This is necessary because Windows - // complains about services binding to 0.0.0.0 sometimes. - let running_inside_container = Path::new(".dockerenv").exists(); - let bind_to = match self.bind_to { - Some(bind_to) => bind_to, - None => { - if running_inside_container { - Ipv4Addr::new(0, 0, 0, 0) - } else { - Ipv4Addr::new(127, 0, 0, 1) - } - }, - }; - info!("Binding host services to {}", bind_to); - - let mut managers: Vec> = Vec::new(); - - // Build the node manager. We do this unconditionally. - let node_manager = NodeManager::new(&self, bind_to, test_dir.clone()) - .context("Failed to build node service manager")?; - let node_health_checkers = node_manager.get_health_checkers(); - - // If configured to do so, build the faucet manager. - if !self.faucet_args.no_faucet { - let faucet_manager = FaucetManager::new( - &self, - node_health_checkers.clone(), - bind_to, - test_dir.clone(), - node_manager.get_node_api_url(), - ) - .context("Failed to build faucet service manager")?; - managers.push(Box::new(faucet_manager)); - } - - if self.indexer_api_args.with_indexer_api { - let postgres_manager = postgres::PostgresManager::new(&self, test_dir.clone()) - .context("Failed to build postgres service manager")?; - let postgres_health_checkers = postgres_manager.get_health_checkers(); - managers.push(Box::new(postgres_manager)); - - let processor_preqrequisite_healthcheckers = - [node_health_checkers, postgres_health_checkers] - .into_iter() - .flatten() - .collect(); - let processor_managers = ProcessorManager::many_new( - &self, - processor_preqrequisite_healthcheckers, - node_manager.get_data_service_url(), - self.postgres_args.get_connection_string(None, true), - ) - .context("Failed to build processor service managers")?; - - let processor_health_checkers = processor_managers - .iter() - .flat_map(|m| m.get_health_checkers()) - .collect(); - - let mut processor_managers = processor_managers - .into_iter() - .map(|m| Box::new(m) as Box) - .collect(); - managers.append(&mut processor_managers); - - let indexer_api_manager = IndexerApiManager::new( - &self, - processor_health_checkers, - test_dir.clone(), - self.postgres_args.get_connection_string(None, false), - ) - .context("Failed to build indexer API service manager")?; - managers.push(Box::new(indexer_api_manager)); - } - - // We put the node manager into managers at the end just so we have access to - // it before this so we can call things like `node_manager.get_node_api_url()`. - managers.push(Box::new(node_manager)); - - // Get the healthcheckers from all the managers. We'll pass to this - // `wait_for_startup`. - let health_checkers: HashSet = managers - .iter() - .flat_map(|m| m.get_health_checkers()) - .collect(); - - // The final manager we add is the ready server. This must happen last since - // it use the health checkers from all the other services. - managers.push(Box::new(ReadyServerManager::new( - &self, - bind_to, - health_checkers.clone(), - )?)); - - // Collect steps to run on shutdown. We run these in reverse. This is somewhat - // arbitrary, each shutdown step should work no matter the order it is run in. - let shutdown_steps: Vec> = managers - .iter() - .flat_map(|m| m.get_shutdown_steps()) - .rev() - .collect(); - - // Run any pre-run steps. - for manager in &managers { - manager.pre_run().await.with_context(|| { - format!("Failed to apply pre run steps for {}", manager.get_name()) - })?; - } - - eprintln!( - "\nReadiness endpoint: http://{}:{}/\n", - bind_to, self.ready_server_args.ready_server_listen_port, - ); - - // Collect post healthy steps to run after the services start. - let post_healthy_steps: Vec> = managers - .iter() - .flat_map(|m| m.get_post_healthy_steps()) - .collect(); - - let mut join_set = JoinSet::new(); - - // Start each of the services. - for manager in managers.into_iter() { - join_set.spawn(manager.run()); - } - - // Wait for all the services to start up. While doing so we also wait for any - // of the services to end. This is not meant to ever happen (except for ctrl-c, - // which we don't catch yet, so the process will just abort). So if it does - // happen, it means one of the services failed to start up, in which case we - // stop waiting for the rest of the services and error out. - tokio::select! { - res = self.wait_for_startup(&health_checkers, &test_dir) => { - res? - }, - res = join_set.join_next() => { - eprintln!("\nOne of the services failed to start up, running shutdown steps..."); - run_shutdown_steps(shutdown_steps).await?; - eprintln!("Ran shutdown steps"); - return Err(CliError::UnexpectedError(format!( - "\nOne of the services crashed on startup:\n{:#?}\nPlease check the logs in {}", - // We can unwrap because we know for certain that the JoinSet is - // not empty. - res.unwrap(), - test_dir.display(), - ))); - } - } - - eprintln!("\nApplying post startup steps..."); - - // Run any post healthy steps. - for post_healthy_step in post_healthy_steps { - post_healthy_step - .run() - .await - .context("Failed to run post startup step")?; - } - - eprintln!("\nSetup is complete, you can now use the local testnet!"); - - // Create a task that listens for ctrl-c. We want to intercept it so we can run - // the shutdown steps before properly exiting. This is of course best effort, - // see `ShutdownStep` for more info. In particular, to speak to how "best effort" - // this really is, to make sure ctrl-c happens more or less instantly, we only - // register this handler after all the services have started. - let abort_handle = join_set.spawn(async move { - tokio::signal::ctrl_c() - .await - .expect("Failed to register ctrl-c hook"); - Ok(()) - }); - let ctrl_c_task_id = abort_handle.id(); - - // Wait for one of the tasks to end. We should never get past this point unless - // something goes goes wrong or the user signals for the process to end. We - // unwrap once because we know for certain the set is not empty and that's the - // only condition in which this can return `None`. - let result = join_set.join_next_with_id().await.unwrap(); - - // We want to print a different message depending on which task ended. We can - // determine if the task that ended was the ctrl-c task based on the ID of the - // task. - let finished_task_id = match &result { - Ok((id, _)) => *id, - Err(err) => err.id(), - }; - - let was_ctrl_c = finished_task_id == ctrl_c_task_id; - if was_ctrl_c { - eprintln!("\nReceived ctrl-c, running shutdown steps..."); - } else { - eprintln!("\nOne of the services exited unexpectedly, running shutdown steps..."); - } - - // At this point register another ctrl-c handler so the user can kill the CLI - // instantly if they send the signal twice. - tokio::spawn(async move { - tokio::signal::ctrl_c() - .await - .expect("Failed to register ctrl-c hook"); - warn!("Received ctrl-c twice and exited immediately"); - eprintln!(); - std::process::exit(1); - }); - - // Run post shutdown steps, if any. - run_shutdown_steps(shutdown_steps).await?; - - eprintln!("Done, goodbye!"); - - match was_ctrl_c { - true => Ok(()), - false => Err(CliError::UnexpectedError(format!( - "One of the services stopped unexpectedly.\nPlease check the logs in {}", - test_dir.display() - ))), - } - } -} - -async fn run_shutdown_steps(shutdown_steps: Vec>) -> Result<()> { - for shutdown_step in shutdown_steps { - shutdown_step - .run() - .await - .context("Failed to run shutdown step")?; - } - Ok(()) -} diff --git a/crates/aptos/src/node/local_testnet/node.rs b/crates/aptos/src/node/local_testnet/node.rs deleted file mode 100644 index f90181c3d3d4a..0000000000000 --- a/crates/aptos/src/node/local_testnet/node.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; -use crate::node::local_testnet::utils::socket_addr_to_url; -use anyhow::{anyhow, Context, Result}; -use aptos_config::config::{NodeConfig, DEFAULT_GRPC_STREAM_PORT}; -use aptos_node::{load_node_config, start_test_environment_node}; -use async_trait::async_trait; -use clap::Parser; -use maplit::hashset; -use rand::{rngs::StdRng, SeedableRng}; -use reqwest::Url; -use std::{ - collections::HashSet, - net::{IpAddr, Ipv4Addr}, - path::PathBuf, - thread, - time::Duration, -}; - -/// Args specific to running a node (and its components, e.g. the txn stream) in the -/// local testnet. -#[derive(Debug, Parser)] -pub struct NodeArgs { - /// An overridable config template for the test node - /// - /// If provided, the config will be used, and any needed configuration for the local testnet - /// will override the config's values - #[clap(long, value_parser)] - pub config_path: Option, - - /// Path to node configuration file override for local test mode. - /// - /// If provided, the default node config will be overridden by the config in the given file. - /// Cannot be used with --config-path - #[clap(long, value_parser, conflicts_with("config_path"))] - pub test_config_override: Option, - - /// Random seed for key generation in test mode - /// - /// This allows you to have deterministic keys for testing - #[clap(long, value_parser = aptos_node::load_seed)] - pub seed: Option<[u8; 32]>, - - /// Do not run a transaction stream service alongside the node. - /// - /// Note: In reality this is not the same as running a Transaction Stream Service, - /// it is just using the stream directly on the node, but in practice this - /// distinction shouldn't matter. - #[clap(long)] - no_txn_stream: bool, - - /// The port at which to expose the grpc transaction stream. - #[clap(long, default_value_t = DEFAULT_GRPC_STREAM_PORT)] - txn_stream_port: u16, - - /// If set we won't run the node at all. - // - // Note: I decided that since running multiple partial local testnets is a rare - // case that only core devs would ever really want, it wasn't worth making the code - // much more complex to support that case "first class". Instead, we have this flag - // that does everything else to set up running the node, but never actually runs - // it. This is useful if you want to invoke the CLI once to run a node + txn stream - // and invoke it again to run processors + indexer API. You might want to do this - // for compatibility testing. If you use this flag and there _isn't_ a node already - // running at the expected port, the processors will fail to connect to the txn - // stream (since there isn't one) and the local testnet will crash. - // - // If we do change our minds on this one day, the correct way to do this would be - // to let the user instead pass in a bunch of flags that declare where an existing - // node is running, separate service configs from their manager, return a config - // instead of a manager, etc. - // - // Because this flag is a bit of a footgun we hide it from regular users. - #[clap(long, hide = true)] - pub no_node: bool, -} - -#[derive(Clone, Debug)] -pub struct NodeManager { - config: NodeConfig, - test_dir: PathBuf, - no_node: bool, -} - -impl NodeManager { - pub fn new(args: &RunLocalTestnet, bind_to: Ipv4Addr, test_dir: PathBuf) -> Result { - let rng = args - .node_args - .seed - .map(StdRng::from_seed) - .unwrap_or_else(StdRng::from_entropy); - - // If there is a config on disk, this function will use that. If not, it will - // create a new one, taking the config_path and test_config_override arguments - // into account. - let mut node_config = load_node_config( - &args.node_args.config_path, - &args.node_args.test_config_override, - &test_dir, - false, - false, - aptos_cached_packages::head_release_bundle(), - rng, - ) - .context("Failed to load / create config for node")?; - - eprintln!(); - - // Enable the grpc stream on the node if we will run a txn stream service. - let run_txn_stream = !args.node_args.no_txn_stream; - node_config.indexer_grpc.enabled = run_txn_stream; - node_config.indexer_grpc.use_data_service_interface = run_txn_stream; - node_config - .indexer_grpc - .address - .set_port(args.node_args.txn_stream_port); - - // So long as the indexer relies on storage indexing tables, this must be set - // for the indexer GRPC stream on the node to work. - node_config.storage.enable_indexer = run_txn_stream; - - // Bind to the requested address. - node_config.api.address.set_ip(IpAddr::V4(bind_to)); - node_config.indexer_grpc.address.set_ip(IpAddr::V4(bind_to)); - node_config.admin_service.address = bind_to.to_string(); - node_config.inspection_service.address = bind_to.to_string(); - - Ok(NodeManager { - config: node_config, - test_dir, - no_node: args.node_args.no_node, - }) - } - - pub fn get_node_api_url(&self) -> Url { - socket_addr_to_url(&self.config.api.address, "http").unwrap() - } - - pub fn get_data_service_url(&self) -> Url { - socket_addr_to_url(&self.config.indexer_grpc.address, "http").unwrap() - } -} - -#[async_trait] -impl ServiceManager for NodeManager { - fn get_name(&self) -> String { - "Node API".to_string() - } - - /// We return health checkers for both the Node API and the txn stream (if enabled). - /// As it is now, it is fine to make downstream services wait for both but if that - /// changes we can refactor. - fn get_health_checkers(&self) -> HashSet { - let node_api_url = self.get_node_api_url(); - let mut checkers = HashSet::new(); - checkers.insert(HealthChecker::NodeApi(node_api_url)); - if self.config.indexer_grpc.enabled { - let data_service_url = - socket_addr_to_url(&self.config.indexer_grpc.address, "http").unwrap(); - checkers.insert(HealthChecker::DataServiceGrpc(data_service_url)); - } - checkers - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - // The node doesn't depend on anything, we start it first. - hashset! {} - } - - /// Spawn the node on a thread and then create a future that just waits for it to - /// exit (which should never happen) forever. This is necessary because there is - /// no async function we can use to run the node. - async fn run_service(self: Box) -> Result<()> { - // Don't actually run the node, just idle. - if self.no_node { - loop { - tokio::time::sleep(Duration::from_millis(10000)).await; - } - } - - let node_thread_handle = thread::spawn(move || { - let result = start_test_environment_node(self.config, self.test_dir, false); - eprintln!("Node stopped unexpectedly {:#?}", result); - }); - - // This just waits for the node thread forever. - loop { - if node_thread_handle.is_finished() { - return Err(anyhow!("Node thread finished unexpectedly")); - } - tokio::time::sleep(Duration::from_millis(500)).await; - } - } -} diff --git a/crates/aptos/src/node/local_testnet/postgres.rs b/crates/aptos/src/node/local_testnet/postgres.rs deleted file mode 100644 index 74937ad6e5cee..0000000000000 --- a/crates/aptos/src/node/local_testnet/postgres.rs +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - docker::{ - create_network, create_volume, delete_container, delete_volume, get_docker, - pull_docker_image, setup_docker_logging, StopContainerShutdownStep, CONTAINER_NETWORK_NAME, - }, - health_checker::HealthChecker, - traits::{ServiceManager, ShutdownStep}, - RunLocalTestnet, -}; -use anyhow::{bail, Context, Result}; -use async_trait::async_trait; -use bollard::{ - container::{Config, CreateContainerOptions, StartContainerOptions, WaitContainerOptions}, - models::{HostConfig, PortBinding}, -}; -use clap::Parser; -use diesel_async::{pg::AsyncPgConnection, AsyncConnection, RunQueryDsl}; -use futures::TryStreamExt; -use maplit::{hashmap, hashset}; -use std::{collections::HashSet, path::PathBuf}; -use tracing::{info, warn}; - -pub const POSTGRES_CONTAINER_NAME: &str = "local-testnet-postgres"; -const POSTGRES_VOLUME_NAME: &str = "local-testnet-postgres-data"; -const POSTGRES_IMAGE: &str = "postgres:14.11"; -const DATA_PATH_IN_CONTAINER: &str = "/var/lib/mydata"; -const POSTGRES_DEFAULT_PORT: u16 = 5432; - -/// Args related to running postgres in the local testnet. -#[derive(Clone, Debug, Parser)] -pub struct PostgresArgs { - /// This is the database to connect to, both when --use-host-postgres is set - /// and when it is not (when postgres is running in a container). - #[clap(long, default_value = "local_testnet")] - pub postgres_database: String, - - /// The user to connect as. If --use-host-postgres is set, we expect this user to - /// exist already. - #[clap(long, default_value = "postgres")] - pub postgres_user: String, - - /// This is the port to use for the postgres instance when --use-host-postgres - /// is not set (i.e. we are running a postgres instance in a container). - #[clap(long, default_value_t = 5433)] - pub postgres_port: u16, - - /// If set, connect to the postgres instance specified by the rest of the - /// `postgres_args` (e.g. --host-postgres-port) rather than running an instance - /// with Docker. This can be used to connect to an existing postgres instance - /// running on the host system. - /// - /// WARNING: Any existing database it finds (based on --postgres-database) will be - /// dropped and recreated. - #[clap(long, requires = "with_indexer_api")] - pub use_host_postgres: bool, - - /// When --use-host-postgres is set, this is the port to connect to. - #[clap(long, default_value_t = 5432)] - pub host_postgres_port: u16, - - /// When --use-host-postgres is set, this is the password to connect with. - #[clap(long)] - pub host_postgres_password: Option, -} - -impl PostgresArgs { - pub fn get_postgres_port(&self, external: bool) -> u16 { - match external { - true => match self.use_host_postgres { - true => self.host_postgres_port, - false => self.postgres_port, - }, - // If connecting from inside the container network, just use the default - // postgres port, since we run postgres on 5432 inside the container. - false => POSTGRES_DEFAULT_PORT, - } - } - - /// Get the connection string for the postgres database. If `database` is specified - /// we will use that rather than `self.postgres_database`. If `external` is true, - /// it will give you the string for connecting from the host. If it is false, it - /// will give you the string for connecting from another container in the network - /// we create for all containers in the local testnet. - pub fn get_connection_string(&self, database: Option<&str>, external: bool) -> String { - let password = match self.use_host_postgres { - true => match &self.host_postgres_password { - Some(password) => format!(":{}", password), - None => "".to_string(), - }, - false => "".to_string(), - }; - let port = self.get_postgres_port(external); - let database = match database { - Some(database) => database, - None => &self.postgres_database, - }; - let host = match self.use_host_postgres { - true => "127.0.0.1", - false => match external { - true => "127.0.0.1", - false => POSTGRES_CONTAINER_NAME, - }, - }; - format!( - "postgres://{}{}@{}:{}/{}", - self.postgres_user, password, host, port, database, - ) - } -} - -#[derive(Clone, Debug)] -pub struct PostgresManager { - args: PostgresArgs, - test_dir: PathBuf, - force_restart: bool, -} - -impl PostgresManager { - pub fn new(args: &RunLocalTestnet, test_dir: PathBuf) -> Result { - if args.postgres_args.use_host_postgres - && args.postgres_args.postgres_database == "postgres" - { - bail!("The postgres database cannot be named postgres if --use-host-postgres is set"); - } - Ok(Self { - args: args.postgres_args.clone(), - test_dir, - force_restart: args.force_restart, - }) - } - - /// Drop and recreate the database specified by `self.args.postgres_database`. - /// This is only necessary when --force-restart and --use-host-postgres are set. - /// For this we connect to the `postgres` database so we can drop the database - /// we'll actually use (since you can't drop a database you're connected to). - async fn recreate_host_database(&self) -> Result<()> { - info!("Dropping database {}", self.args.postgres_database); - let connection_string = self.args.get_connection_string(Some("postgres"), true); - - // Open a connection to the DB. - let mut connection = AsyncPgConnection::establish(&connection_string) - .await - .with_context(|| format!("Failed to connect to postgres at {}", connection_string))?; - - // Drop the DB. - diesel::sql_query(format!( - "DROP DATABASE IF EXISTS {}", - self.args.postgres_database - )) - .execute(&mut connection) - .await?; - info!("Dropped database {}", self.args.postgres_database); - - // Create DB again. - diesel::sql_query(format!("CREATE DATABASE {}", self.args.postgres_database)) - .execute(&mut connection) - .await?; - info!("Created database {}", self.args.postgres_database); - - Ok(()) - } -} - -#[async_trait] -impl ServiceManager for PostgresManager { - fn get_name(&self) -> String { - "Postgres".to_string() - } - - async fn pre_run(&self) -> Result<()> { - if self.args.use_host_postgres { - if self.force_restart { - // If we're using a DB outside of Docker, drop and recreate the database. - self.recreate_host_database().await?; - } - } else { - // Confirm Docker is available. - get_docker().await?; - - // Kill any existing container we find. - delete_container(POSTGRES_CONTAINER_NAME).await?; - - // Pull the image here so it is not subject to the startup timeout for - // `run_service`. - pull_docker_image(POSTGRES_IMAGE).await?; - - // Create a network for the containers to talk to each other. - create_network(CONTAINER_NETWORK_NAME).await?; - } - - Ok(()) - } - - fn get_health_checkers(&self) -> HashSet { - hashset! {HealthChecker::Postgres( - self.args.get_connection_string(None, true), - )} - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - hashset! {} - } - - async fn run_service(self: Box) -> Result<()> { - // If we're using postgres on the host just do nothing forever. - if self.args.use_host_postgres { - return std::future::pending().await; - } - - // Let the user know where to go to see logs for postgres. - setup_docker_logging(&self.test_dir, "postgres", POSTGRES_CONTAINER_NAME)?; - - let docker = get_docker().await?; - - // If we're starting afresh, delete any existing volume. - if self.force_restart { - delete_volume(POSTGRES_VOLUME_NAME) - .await - .context("Failed to delete volume for postgres")?; - } - - // Create a volume for the postgres instance to use. - create_volume(POSTGRES_VOLUME_NAME) - .await - .context("Failed to create volume for postgres")?; - - let options = Some(CreateContainerOptions { - name: POSTGRES_CONTAINER_NAME, - ..Default::default() - }); - - let port = self.args.get_postgres_port(true).to_string(); - let exposed_ports = Some(hashmap! {POSTGRES_DEFAULT_PORT.to_string() => hashmap!{}}); - let host_config = Some(HostConfig { - // Bind the container to the network we created in the pre_run. This does - // not prevent the binary in the container from exposing itself to the host - // on 127.0.0.1. See more here: https://stackoverflow.com/a/77432636/3846032. - network_mode: Some(CONTAINER_NETWORK_NAME.to_string()), - port_bindings: Some(hashmap! { - POSTGRES_DEFAULT_PORT.to_string() => Some(vec![PortBinding { - host_ip: Some("127.0.0.1".to_string()), - host_port: Some(port), - }]), - }), - // Mount the volume in to the container. We use a volume because they are - // more performant and easier to manage via the Docker API. - binds: Some(vec![format!( - "{}:{}", - POSTGRES_VOLUME_NAME, DATA_PATH_IN_CONTAINER, - )]), - ..Default::default() - }); - - let config = Config { - image: Some(POSTGRES_IMAGE.to_string()), - // We set this to false so the container keeps running after the CLI - // shuts down by default. We manually kill the container if applicable, - // for example if the user set --force-restart. - tty: Some(false), - exposed_ports, - host_config, - env: Some(vec![ - // We run postgres without any auth + no password. - "POSTGRES_HOST_AUTH_METHOD=trust".to_string(), - format!("POSTGRES_USER={}", self.args.postgres_user), - format!("POSTGRES_DB={}", self.args.postgres_database), - // This tells where postgres to store the DB data on disk. This is the - // directory inside the container that is mounted from the host system. - format!("PGDATA={}", DATA_PATH_IN_CONTAINER), - ]), - cmd: Some( - vec![ - "postgres", - "-c", - // The default is 100 as of Postgres 14.11. Given the local testnet - // can be composed of many different processors all with their own - // connection pools, 100 is insufficient. - "max_connections=200", - "-c", - // The default is 128MB as of Postgres 14.11. We 2x that value to - // match the fact that we 2x'd max_connections. - "shared_buffers=256MB", - ] - .into_iter() - .map(|s| s.to_string()) - .collect(), - ), - ..Default::default() - }; - - info!("Starting postgres with this config: {:?}", config); - - let id = docker - .create_container(options, config) - .await - .context("Failed to create postgres container")? - .id; - - info!("Created container for postgres with this ID: {}", id); - - docker - .start_container(&id, None::>) - .await - .context("Failed to start postgres container")?; - - info!("Started postgres container {}", id); - - // Wait for the container to stop (which it shouldn't). - let wait = docker - .wait_container( - &id, - Some(WaitContainerOptions { - condition: "not-running", - }), - ) - .try_collect::>() - .await - .context("Failed to wait on postgres container")?; - - warn!("Postgres container stopped: {:?}", wait.last()); - - Ok(()) - } - - fn get_shutdown_steps(&self) -> Vec> { - if self.args.use_host_postgres { - vec![] - } else { - // Stop the container. Note, stopping and even deleting the container is - // fine because we store the data in a directory mounted in from the host - // system, so it will persist. - vec![Box::new(StopContainerShutdownStep::new( - POSTGRES_CONTAINER_NAME, - ))] - } - } -} diff --git a/crates/aptos/src/node/local_testnet/processors.rs b/crates/aptos/src/node/local_testnet/processors.rs deleted file mode 100644 index 0196058261006..0000000000000 --- a/crates/aptos/src/node/local_testnet/processors.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; -use anyhow::{bail, Context, Result}; -use async_trait::async_trait; -use clap::Parser; -use diesel_async::{pg::AsyncPgConnection, AsyncConnection}; -use maplit::hashset; -use processor::{ - processors::{token_processor::TokenProcessorConfig, ProcessorConfig, ProcessorName}, - utils::database::run_pending_migrations, - IndexerGrpcProcessorConfig, -}; -use reqwest::Url; -use server_framework::RunnableConfig; -use std::collections::HashSet; -use tokio::sync::OnceCell; -use tracing::info; - -static RUN_MIGRATIONS_ONCE: OnceCell = OnceCell::const_new(); - -/// This struct is used to parse the command line arguments for the processors. -#[derive(Debug, Parser)] -pub struct ProcessorArgs { - /// The value of this flag determines which processors we will run if - /// --with-indexer-api is set. Note that some processors are not supported in the - /// local testnet (e.g. ANS). If you try to set those an error will be thrown - /// immediately. - #[clap( - long, - value_enum, - default_values_t = vec![ - ProcessorName::AccountTransactionsProcessor, - ProcessorName::CoinProcessor, - ProcessorName::DefaultProcessor, - ProcessorName::EventsProcessor, - ProcessorName::FungibleAssetProcessor, - ProcessorName::ObjectsProcessor, - ProcessorName::StakeProcessor, - ProcessorName::TokenProcessor, - ProcessorName::TokenV2Processor, - ProcessorName::UserTransactionProcessor, - ], - requires = "with_indexer_api" - )] - processors: Vec, -} - -#[derive(Debug)] -pub struct ProcessorManager { - config: IndexerGrpcProcessorConfig, - prerequisite_health_checkers: HashSet, -} - -impl ProcessorManager { - fn new( - processor_name: &ProcessorName, - prerequisite_health_checkers: HashSet, - data_service_url: Url, - postgres_connection_string: String, - ) -> Result { - let processor_config = match processor_name { - ProcessorName::AccountTransactionsProcessor => { - ProcessorConfig::AccountTransactionsProcessor - }, - ProcessorName::AnsProcessor => { - bail!("ANS processor is not supported in the local testnet") - }, - ProcessorName::CoinProcessor => ProcessorConfig::CoinProcessor, - ProcessorName::DefaultProcessor => ProcessorConfig::DefaultProcessor, - ProcessorName::EventsProcessor => ProcessorConfig::EventsProcessor, - ProcessorName::FungibleAssetProcessor => ProcessorConfig::FungibleAssetProcessor, - ProcessorName::NftMetadataProcessor => { - bail!("NFT Metadata processor is not supported in the local testnet") - }, - ProcessorName::StakeProcessor => ProcessorConfig::StakeProcessor, - ProcessorName::TokenProcessor => { - ProcessorConfig::TokenProcessor(TokenProcessorConfig { - // This NFT points contract doesn't exist on local testnets. - nft_points_contract: None, - }) - }, - ProcessorName::TokenV2Processor => ProcessorConfig::TokenV2Processor, - ProcessorName::UserTransactionProcessor => ProcessorConfig::UserTransactionProcessor, - ProcessorName::MonitoringProcessor => { - bail!("Monitoring processor is not supported in the local testnet") - }, - ProcessorName::ObjectsProcessor => ProcessorConfig::ObjectsProcessor, - }; - let config = IndexerGrpcProcessorConfig { - processor_config, - postgres_connection_string, - indexer_grpc_data_service_address: data_service_url, - auth_token: "notused".to_string(), - grpc_http2_config: Default::default(), - starting_version: None, - ending_version: None, - number_concurrent_processing_tasks: None, - enable_verbose_logging: None, - // The default at the time of writing is 30 but we don't need that - // many in a local testnet environment. - db_pool_size: Some(8), - gap_detection_batch_size: 50, - }; - let manager = Self { - config, - prerequisite_health_checkers, - }; - Ok(manager) - } - - /// This function returns many new ProcessorManagers, one for each processor. - pub fn many_new( - args: &RunLocalTestnet, - prerequisite_health_checkers: HashSet, - data_service_url: Url, - postgres_connection_string: String, - ) -> Result> { - if args.processor_args.processors.is_empty() { - bail!("Must specify at least one processor to run"); - } - let mut managers = Vec::new(); - for processor_name in &args.processor_args.processors { - managers.push(Self::new( - processor_name, - prerequisite_health_checkers.clone(), - data_service_url.clone(), - postgres_connection_string.clone(), - )?); - } - Ok(managers) - } - - /// Create the necessary tables in the DB for the processors to work. - async fn run_migrations(&self) -> Result<()> { - let connection_string = &self.config.postgres_connection_string; - let mut connection = AsyncPgConnection::establish(connection_string) - .await - .with_context(|| format!("Failed to connect to postgres at {}", connection_string))?; - run_pending_migrations(&mut connection).await; - Ok(()) - } -} - -#[async_trait] -impl ServiceManager for ProcessorManager { - fn get_name(&self) -> String { - format!("processor_{}", self.config.processor_config.name()) - } - - fn get_health_checkers(&self) -> HashSet { - hashset! {HealthChecker::Processor( - self.config.postgres_connection_string.to_string(), - self.config.processor_config.name().to_string(), - ) } - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - self.prerequisite_health_checkers.iter().collect() - } - - async fn run_service(self: Box) -> Result<()> { - // By default, when a processor starts up (specifically in Worker.run) it runs - // any pending migrations. Unfortunately, if you start multiple processors at - // the same time, they can sometimes clash with errors like this: - // - // https://stackoverflow.com/q/54351783/3846032 - // - // To fix this, we run the migrations ourselves here in the CLI first. We use - // OnceCell to make sure we only run the migration once. When all the processor - // ServiceManagers reach this point, one of them will run the code and the rest - // will wait. Doing it at this point in the code is safer than relying on - // coordiation outside of this manager. - RUN_MIGRATIONS_ONCE - .get_or_init(|| async { - info!("Running DB migrations for the indexer processors"); - self.run_migrations() - .await - .expect("Failed to run DB migrations"); - info!("Ran DB migrations for the indexer processors"); - true - }) - .await; - - // Run the processor. - self.config.run().await - } -} diff --git a/crates/aptos/src/node/local_testnet/ready_server.rs b/crates/aptos/src/node/local_testnet/ready_server.rs deleted file mode 100644 index 3acdc0a656920..0000000000000 --- a/crates/aptos/src/node/local_testnet/ready_server.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet}; -use anyhow::Result; -use async_trait::async_trait; -use clap::Parser; -use maplit::hashset; -use poem::{ - get, handler, - http::StatusCode, - listener::TcpListener, - middleware::Tracing, - web::{Data, Json}, - EndpointExt, IntoResponse, Route, Server, -}; -use serde::Serialize; -use std::{ - collections::HashSet, - net::{Ipv4Addr, SocketAddrV4}, - time::Duration, -}; -use tokio::time::timeout; - -/// Args related to running a ready server in the local testnet. The ready server lets -/// users / clients check that if all the services in the local testnet are ready -/// without having to ping each service individually. -#[derive(Debug, Clone, Parser)] -pub struct ReadyServerArgs { - /// The port to run the ready server. This exposes an endpoint at `/` that you can - /// use to check if the entire local testnet is ready. - #[clap(long, default_value_t = 8070)] - pub ready_server_listen_port: u16, -} - -#[derive(Clone, Debug)] -pub struct ReadyServerManager { - config: ReadyServerArgs, - bind_to: Ipv4Addr, - health_checkers: HashSet, -} - -impl ReadyServerManager { - pub fn new( - args: &RunLocalTestnet, - bind_to: Ipv4Addr, - health_checkers: HashSet, - ) -> Result { - Ok(ReadyServerManager { - config: args.ready_server_args.clone(), - bind_to, - health_checkers, - }) - } -} - -#[async_trait] -impl ServiceManager for ReadyServerManager { - fn get_name(&self) -> String { - "Ready Server".to_string() - } - - fn get_health_checkers(&self) -> HashSet { - // We don't health check the service that exposes health checks. - hashset! {} - } - - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> { - // This service should start before the other services are ready. - hashset! {} - } - - async fn run_service(self: Box) -> Result<()> { - run_ready_server(self.health_checkers, self.config, self.bind_to).await - } -} - -/// This returns a future that runs a web server that exposes a single unified health -/// checking port. Clients can use this to check if all the services are ready. -pub async fn run_ready_server( - health_checkers: HashSet, - config: ReadyServerArgs, - bind_to: Ipv4Addr, -) -> Result<()> { - let app = Route::new() - .at("/", get(root)) - .data(HealthCheckers { health_checkers }) - .with(Tracing); - Server::new(TcpListener::bind(SocketAddrV4::new( - bind_to, - config.ready_server_listen_port, - ))) - .name("ready-server") - .run(app) - .await?; - Err(anyhow::anyhow!("Ready server exited unexpectedly")) -} - -#[derive(Clone, Debug)] -struct HealthCheckers { - pub health_checkers: HashSet, -} - -#[derive(Serialize)] -struct ReadyData { - pub ready: Vec, - pub not_ready: Vec, -} - -#[handler] -async fn root(health_checkers: Data<&HealthCheckers>) -> impl IntoResponse { - let mut ready = vec![]; - let mut not_ready = vec![]; - for health_checker in &health_checkers.health_checkers { - // Use timeout since some of these checks can take quite a while if the - // underlying service is not ready. This is best effort of course, see the docs - // for tokio::time::timeout for more information. - match timeout(Duration::from_secs(3), health_checker.check()).await { - Ok(Ok(())) => ready.push(health_checker.clone()), - _ => { - not_ready.push(health_checker.clone()); - }, - } - } - let status_code = if not_ready.is_empty() { - StatusCode::OK - } else { - StatusCode::SERVICE_UNAVAILABLE - }; - Json(ReadyData { ready, not_ready }).with_status(status_code) -} diff --git a/crates/aptos/src/node/local_testnet/traits.rs b/crates/aptos/src/node/local_testnet/traits.rs deleted file mode 100644 index 45b552b6cdad3..0000000000000 --- a/crates/aptos/src/node/local_testnet/traits.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::health_checker::HealthChecker; -use anyhow::{Context, Result}; -use async_trait::async_trait; -use std::{collections::HashSet, fmt::Debug}; -use tracing::warn; - -#[async_trait] -pub trait ServiceManager: Debug + Send + Sync + 'static { - /// Pretty name that we will show to the user for updates about this service. - fn get_name(&self) -> String; - - /// This is called before the service is run. This is a good place to do any - /// setup that needs to be done before the service is run. - async fn pre_run(&self) -> Result<()> { - Ok(()) - } - - /// All services should expose some way to check if they are healthy. This function - /// returns HealthCheckers, a struct that serves this purpose, that later services - /// can use to make sure prerequisite services have started. These are also used - /// by the "ready server", a server that exposes a unified endpoint for checking - /// if all services are ready. - fn get_health_checkers(&self) -> HashSet; - - /// Whereas get_health_checkers returns healthchecks that other downstream services - /// can use, this should return health checkers for services that this service is - /// waiting to start. - // - // Note: If we were using an object oriented language, we'd just make the - // constructor of the superclass require a vec of health checkers. Unfortunately - // we can't do that here, hence this runaround where the trait implementer must - // individually handle accepting health checkers and exposing them here. Similarly, - // if we could make this function private we would, but we can't since right now - // all functions in a trait must be pub. - fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker>; - - /// This is the function we use from the outside to start the service. It makes - /// sure all the prerequisite services have started and then calls the inner - /// function to run the service. The user should never need to override this - /// implementation. - async fn run(self: Box) -> Result<()> { - // We make a new function here so that each task waits for its prereqs within - // its own run function. This way we can start each service in any order. - let name = self.get_name(); - let name_clone = name.to_string(); - for health_checker in self.get_prerequisite_health_checkers() { - health_checker - .wait(Some(&self.get_name())) - .await - .context("Prerequisite service did not start up successfully")?; - } - self.run_service() - .await - .context("Service ended with an error")?; - warn!( - "Service {} ended unexpectedly without any error", - name_clone - ); - Ok(()) - } - - /// The ServiceManager may return PostHealthySteps. The tool will run these after - /// the service is started. See `PostHealthyStep` for more information. - // - // You might ask, why not just have a `post_healthy` function? The problem is we - // want `run` to take `self` so the implementer doesn't have to worry about making - // their config Clone, so after that point the ServiceManager won't exist. Hence - // this model. - fn get_post_healthy_steps(&self) -> Vec> { - vec![] - } - - /// The ServiceManager may return ShutdownSteps. The tool will run these on shutdown. - /// This is best effort, there is nothing we can do if part of the code aborts or - /// the process receives something like SIGKILL. - /// - /// See `ShutdownStep` for more information. - fn get_shutdown_steps(&self) -> Vec> { - vec![] - } - - /// This function is responsible for running the service. It should return an error - /// if the service ends unexpectedly. It gets called by `run`. - async fn run_service(self: Box) -> Result<()>; -} - -/// If a service wants to do something after it is healthy, it can define a struct, -/// implement this trait for it, and return an instance of it. -/// -/// For more information see `get_post_healthy_steps` in `ServiceManager`. -#[async_trait] -pub trait PostHealthyStep: Debug + Send + Sync + 'static { - async fn run(self: Box) -> Result<()>; -} - -/// If a service wants to do something on shutdown, it can define a struct, -/// implement this trait for it, and return an instance of it. -/// -/// For more information see `get_shutdown_steps` in `ServiceManager`. -#[async_trait] -pub trait ShutdownStep: Debug + Send + Sync + 'static { - async fn run(self: Box) -> Result<()>; -} diff --git a/crates/aptos/src/node/local_testnet/utils.rs b/crates/aptos/src/node/local_testnet/utils.rs deleted file mode 100644 index 58e0a7808c24e..0000000000000 --- a/crates/aptos/src/node/local_testnet/utils.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use reqwest::Url; -use std::net::SocketAddr; - -pub fn socket_addr_to_url(socket_addr: &SocketAddr, scheme: &str) -> Result { - let host = match socket_addr { - SocketAddr::V4(v4) => format!("{}", v4.ip()), - SocketAddr::V6(v6) => format!("[{}]", v6.ip()), - }; - let full_url = format!("{}://{}:{}", scheme, host, socket_addr.port()); - Ok(Url::parse(&full_url)?) -} diff --git a/crates/aptos/src/node/mod.rs b/crates/aptos/src/node/mod.rs deleted file mode 100644 index 83d32e501d010..0000000000000 --- a/crates/aptos/src/node/mod.rs +++ /dev/null @@ -1,1504 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod analyze; -pub mod local_testnet; - -use self::local_testnet::RunLocalTestnet; -use crate::{ - common::{ - types::{ - CliCommand, CliError, CliResult, CliTypedResult, OptionalPoolAddressArgs, - PoolAddressArgs, ProfileOptions, RestOptions, TransactionOptions, TransactionSummary, - }, - utils::read_from_file, - }, - genesis::git::from_yaml, - node::analyze::{ - analyze_validators::{AnalyzeValidators, ValidatorStats}, - fetch_metadata::FetchMetadata, - }, -}; -use aptos_backup_cli::{ - coordinators::restore::{RestoreCoordinator, RestoreCoordinatorOpt}, - storage::DBToolStorageOpt, - utils::GlobalRestoreOpt, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{bls12381, bls12381::PublicKey, x25519, ValidCryptoMaterialStringExt}; -use aptos_genesis::config::{HostAndPort, OperatorConfiguration}; -use aptos_logger::Level; -use aptos_network_checker::args::{ - validate_address, CheckEndpointArgs, HandshakeArgs, NodeAddressArgs, -}; -use aptos_rest_client::{aptos_api_types::VersionedEvent, Client, State}; -use aptos_types::{ - account_address::AccountAddress, - account_config::{BlockResource, CORE_CODE_ADDRESS}, - chain_id::ChainId, - network_address::NetworkAddress, - on_chain_config::{ConfigurationResource, ConsensusScheme, ValidatorSet}, - stake_pool::StakePool, - staking_contract::StakingContractStore, - validator_info::ValidatorInfo, - validator_performances::ValidatorPerformances, - vesting::VestingAdminStore, -}; -use async_trait::async_trait; -use bcs::Result; -use chrono::{DateTime, NaiveDateTime, Utc}; -use clap::Parser; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - convert::{TryFrom, TryInto}, - path::PathBuf, - time::Duration, -}; - -const SECS_TO_MICROSECS: u64 = 1_000_000; - -/// Tool for operations related to nodes -/// -/// This tool allows you to run a local test node for testing, -/// identify issues with nodes, and show related information. -#[derive(Parser)] -pub enum NodeTool { - AnalyzeValidatorPerformance(AnalyzeValidatorPerformance), - BootstrapDb(BootstrapDb), - CheckNetworkConnectivity(CheckNetworkConnectivity), - GetPerformance(GetPerformance), - GetStakePool(GetStakePool), - InitializeValidator(InitializeValidator), - JoinValidatorSet(JoinValidatorSet), - LeaveValidatorSet(LeaveValidatorSet), - ShowEpochInfo(ShowEpochInfo), - ShowValidatorConfig(ShowValidatorConfig), - ShowValidatorSet(ShowValidatorSet), - ShowValidatorStake(ShowValidatorStake), - RunLocalTestnet(RunLocalTestnet), - UpdateConsensusKey(UpdateConsensusKey), - UpdateValidatorNetworkAddresses(UpdateValidatorNetworkAddresses), -} - -impl NodeTool { - pub async fn execute(self) -> CliResult { - use NodeTool::*; - match self { - AnalyzeValidatorPerformance(tool) => tool.execute_serialized().await, - BootstrapDb(tool) => { - tool.execute_serialized_with_logging_level(Level::Info) - .await - }, - CheckNetworkConnectivity(tool) => tool.execute_serialized().await, - GetPerformance(tool) => tool.execute_serialized().await, - GetStakePool(tool) => tool.execute_serialized().await, - InitializeValidator(tool) => tool.execute_serialized().await, - JoinValidatorSet(tool) => tool.execute_serialized().await, - LeaveValidatorSet(tool) => tool.execute_serialized().await, - ShowEpochInfo(tool) => tool.execute_serialized().await, - ShowValidatorSet(tool) => tool.execute_serialized().await, - ShowValidatorStake(tool) => tool.execute_serialized().await, - ShowValidatorConfig(tool) => tool.execute_serialized().await, - RunLocalTestnet(tool) => tool - .execute_serialized_without_logger() - .await - .map(|_| "".to_string()), - UpdateConsensusKey(tool) => tool.execute_serialized().await, - UpdateValidatorNetworkAddresses(tool) => tool.execute_serialized().await, - } - } -} - -#[derive(Parser)] -pub struct OperatorConfigFileArgs { - /// Operator Configuration file - /// - /// Config file created from the `genesis set-validator-configuration` command - #[clap(long, value_parser)] - pub(crate) operator_config_file: Option, -} - -impl OperatorConfigFileArgs { - fn load(&self) -> CliTypedResult> { - if let Some(ref file) = self.operator_config_file { - Ok(from_yaml( - &String::from_utf8(read_from_file(file)?).map_err(CliError::from)?, - )?) - } else { - Ok(None) - } - } -} - -#[derive(Parser)] -pub struct ValidatorConsensusKeyArgs { - /// Hex encoded Consensus public key - /// - /// The key should be a BLS12-381 public key - #[clap(long, value_parser = bls12381::PublicKey::from_encoded_string)] - pub(crate) consensus_public_key: Option, - - /// Hex encoded Consensus proof of possession - /// - /// The key should be a BLS12-381 proof of possession - #[clap(long, value_parser = bls12381::ProofOfPossession::from_encoded_string)] - pub(crate) proof_of_possession: Option, -} - -impl ValidatorConsensusKeyArgs { - fn get_consensus_public_key<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<&'a bls12381::PublicKey> { - let consensus_public_key = if let Some(ref consensus_public_key) = self.consensus_public_key - { - consensus_public_key - } else if let Some(ref operator_config) = operator_config { - &operator_config.consensus_public_key - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --consensus-public-key".to_string(), - )); - }; - Ok(consensus_public_key) - } - - fn get_consensus_proof_of_possession<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<&'a bls12381::ProofOfPossession> { - let proof_of_possession = if let Some(ref proof_of_possession) = self.proof_of_possession { - proof_of_possession - } else if let Some(ref operator_config) = operator_config { - &operator_config.consensus_proof_of_possession - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --proof-of-possession".to_string(), - )); - }; - Ok(proof_of_possession) - } -} - -#[derive(Parser)] -pub struct ValidatorNetworkAddressesArgs { - /// Host and port pair for the validator - /// - /// e.g. 127.0.0.1:6180 - #[clap(long)] - pub(crate) validator_host: Option, - - /// Validator x25519 public network key - #[clap(long, value_parser = x25519::PublicKey::from_encoded_string)] - pub(crate) validator_network_public_key: Option, - - /// Host and port pair for the fullnode - /// - /// e.g. 127.0.0.1:6180. Optional - #[clap(long)] - pub(crate) full_node_host: Option, - - /// Full node x25519 public network key - #[clap(long, value_parser = x25519::PublicKey::from_encoded_string)] - pub(crate) full_node_network_public_key: Option, -} - -impl ValidatorNetworkAddressesArgs { - fn get_network_configs<'a>( - &'a self, - operator_config: &'a Option, - ) -> CliTypedResult<( - x25519::PublicKey, - Option, - &'a HostAndPort, - Option<&'a HostAndPort>, - )> { - let validator_network_public_key = - if let Some(public_key) = self.validator_network_public_key { - public_key - } else if let Some(ref operator_config) = operator_config { - operator_config.validator_network_public_key - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --validator-network-public-key" - .to_string(), - )); - }; - - let full_node_network_public_key = - if let Some(public_key) = self.full_node_network_public_key { - Some(public_key) - } else if let Some(ref operator_config) = operator_config { - operator_config.full_node_network_public_key - } else { - None - }; - - let validator_host = if let Some(ref host) = self.validator_host { - host - } else if let Some(ref operator_config) = operator_config { - &operator_config.validator_host - } else { - return Err(CliError::CommandArgumentError( - "Must provide either --operator-config-file or --validator-host".to_string(), - )); - }; - - let full_node_host = if let Some(ref host) = self.full_node_host { - Some(host) - } else if let Some(ref operator_config) = operator_config { - operator_config.full_node_host.as_ref() - } else { - None - }; - - Ok(( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - )) - } -} - -#[derive(Copy, Clone, Debug, Serialize)] -pub enum StakePoolType { - Direct, - StakingContract, - Vesting, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] -pub enum StakePoolState { - Active, - Inactive, - PendingActive, - PendingInactive, -} - -#[derive(Debug, Serialize)] -pub struct StakePoolResult { - pub state: StakePoolState, - pub pool_address: AccountAddress, - pub operator_address: AccountAddress, - pub voter_address: AccountAddress, - pub pool_type: StakePoolType, - pub total_stake: u64, - pub commission_percentage: u64, - pub commission_not_yet_unlocked: u64, - pub lockup_expiration_utc_time: DateTime, - pub consensus_public_key: String, - pub validator_network_addresses: Vec, - pub fullnode_network_addresses: Vec, - pub epoch_info: EpochInfo, - #[serde(skip_serializing_if = "Option::is_none")] - pub vesting_contract: Option, -} - -/// Show the stake pool -/// -/// Retrieves the associated stake pool from the multiple types for the given owner address -#[derive(Parser)] -pub struct GetStakePool { - /// The owner address of the stake pool - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub(crate) owner_address: AccountAddress, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand> for GetStakePool { - fn command_name(&self) -> &'static str { - "GetStakePool" - } - - async fn execute(mut self) -> CliTypedResult> { - let owner_address = self.owner_address; - let client = &self.rest_options.client(&self.profile_options)?; - get_stake_pools(client, owner_address).await - } -} - -#[derive(Debug, Serialize)] -pub struct StakePoolPerformance { - current_epoch_successful_proposals: u64, - current_epoch_failed_proposals: u64, - previous_epoch_rewards: Vec, - epoch_info: EpochInfo, -} - -/// Show staking performance of the given staking pool -#[derive(Parser)] -pub struct GetPerformance { - #[clap(flatten)] - pub(crate) pool_address_args: PoolAddressArgs, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[async_trait] -impl CliCommand for GetPerformance { - fn command_name(&self) -> &'static str { - "GetPerformance" - } - - async fn execute(mut self) -> CliTypedResult { - let client = &self.rest_options.client(&self.profile_options)?; - let pool_address = self.pool_address_args.pool_address; - let validator_set = &client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - - let mut current_epoch_successful_proposals = 0; - let mut current_epoch_failed_proposals = 0; - let state = get_stake_pool_state(validator_set, &pool_address); - if state == StakePoolState::Active || state == StakePoolState::PendingInactive { - let validator_config = client - .get_account_resource_bcs::( - pool_address, - "0x1::stake::ValidatorConfig", - ) - .await? - .into_inner(); - let validator_performances = &client - .get_account_resource_bcs::( - CORE_CODE_ADDRESS, - "0x1::stake::ValidatorPerformance", - ) - .await? - .into_inner(); - let validator_index = validator_config.validator_index as usize; - current_epoch_successful_proposals = - validator_performances.validators[validator_index].successful_proposals; - current_epoch_failed_proposals = - validator_performances.validators[validator_index].failed_proposals; - }; - - let previous_epoch_rewards = client - .get_account_events( - pool_address, - "0x1::stake::StakePool", - "distribute_rewards_events", - Some(0), - Some(10), - ) - .await - .unwrap() - .into_inner() - .into_iter() - .map(|e: VersionedEvent| { - e.data - .get("rewards_amount") - .unwrap() - .as_str() - .unwrap() - .into() - }) - .collect(); - - Ok(StakePoolPerformance { - current_epoch_successful_proposals, - current_epoch_failed_proposals, - previous_epoch_rewards, - epoch_info: get_epoch_info(client).await?, - }) - } -} - -/// Retrieves all stake pools associated with an account -pub async fn get_stake_pools( - client: &Client, - owner_address: AccountAddress, -) -> CliTypedResult> { - let epoch_info = get_epoch_info(client).await?; - let validator_set = &client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - let mut stake_pool_results: Vec = vec![]; - // Add direct stake pool if any. - let direct_stake_pool = get_stake_pool_info( - client, - owner_address, - StakePoolType::Direct, - 0, - 0, - epoch_info.clone(), - validator_set, - None, - ) - .await; - if let Ok(direct_stake_pool) = direct_stake_pool { - stake_pool_results.push(direct_stake_pool); - }; - - // Fetch all stake pools managed via staking contracts. - let staking_contract_pools = get_staking_contract_pools( - client, - owner_address, - StakePoolType::StakingContract, - epoch_info.clone(), - validator_set, - None, - ) - .await; - if let Ok(mut staking_contract_pools) = staking_contract_pools { - stake_pool_results.append(&mut staking_contract_pools); - }; - - // Fetch all stake pools managed via employee vesting accounts. - let vesting_admin_store = client - .get_account_resource_bcs::(owner_address, "0x1::vesting::AdminStore") - .await; - if let Ok(vesting_admin_store) = vesting_admin_store { - let vesting_contracts = vesting_admin_store.into_inner().vesting_contracts; - for vesting_contract in vesting_contracts { - let mut staking_contract_pools = get_staking_contract_pools( - client, - vesting_contract, - StakePoolType::Vesting, - epoch_info.clone(), - validator_set, - Some(vesting_contract), - ) - .await - .unwrap(); - stake_pool_results.append(&mut staking_contract_pools); - } - }; - - Ok(stake_pool_results) -} - -/// Retrieve 0x1::staking_contract related pools -pub async fn get_staking_contract_pools( - client: &Client, - staker_address: AccountAddress, - pool_type: StakePoolType, - epoch_info: EpochInfo, - validator_set: &ValidatorSet, - vesting_contract: Option, -) -> CliTypedResult> { - let mut stake_pool_results: Vec = vec![]; - let staking_contract_store = client - .get_account_resource_bcs::( - staker_address, - "0x1::staking_contract::Store", - ) - .await?; - let staking_contracts = staking_contract_store.into_inner().staking_contracts; - for staking_contract in staking_contracts { - let stake_pool_address = get_stake_pool_info( - client, - staking_contract.value.pool_address, - pool_type, - staking_contract.value.principal, - staking_contract.value.commission_percentage, - epoch_info.clone(), - validator_set, - vesting_contract, - ) - .await - .unwrap(); - stake_pool_results.push(stake_pool_address); - } - Ok(stake_pool_results) -} - -pub async fn get_stake_pool_info( - client: &Client, - pool_address: AccountAddress, - pool_type: StakePoolType, - principal: u64, - commission_percentage: u64, - epoch_info: EpochInfo, - validator_set: &ValidatorSet, - vesting_contract: Option, -) -> CliTypedResult { - let stake_pool = client - .get_account_resource_bcs::(pool_address, "0x1::stake::StakePool") - .await? - .into_inner(); - let validator_config = client - .get_account_resource_bcs::(pool_address, "0x1::stake::ValidatorConfig") - .await? - .into_inner(); - let total_stake = stake_pool.get_total_staked_amount(); - let commission_not_yet_unlocked = (total_stake - principal) * commission_percentage / 100; - let state = get_stake_pool_state(validator_set, &pool_address); - - let consensus_public_key = if validator_config.consensus_public_key.is_empty() { - "".into() - } else { - PublicKey::try_from(&validator_config.consensus_public_key[..]) - .unwrap() - .to_encoded_string() - .unwrap() - }; - Ok(StakePoolResult { - state, - pool_address, - operator_address: stake_pool.operator_address, - voter_address: stake_pool.delegated_voter, - pool_type, - total_stake, - commission_percentage, - commission_not_yet_unlocked, - lockup_expiration_utc_time: Time::new_seconds(stake_pool.locked_until_secs).utc_time, - consensus_public_key, - validator_network_addresses: validator_config - .validator_network_addresses() - .unwrap_or_default(), - fullnode_network_addresses: validator_config - .fullnode_network_addresses() - .unwrap_or_default(), - epoch_info, - vesting_contract, - }) -} - -fn get_stake_pool_state( - validator_set: &ValidatorSet, - pool_address: &AccountAddress, -) -> StakePoolState { - if validator_set.active_validators().contains(pool_address) { - StakePoolState::Active - } else if validator_set - .pending_active_validators() - .contains(pool_address) - { - StakePoolState::PendingActive - } else if validator_set - .pending_inactive_validators() - .contains(pool_address) - { - StakePoolState::PendingInactive - } else { - StakePoolState::Inactive - } -} - -/// Register the current account as a validator -/// -/// This will create a new stake pool for the given account. The voter and operator fields will be -/// defaulted to the stake pool account if not provided. -#[derive(Parser)] -pub struct InitializeValidator { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, - #[clap(flatten)] - pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, -} - -#[async_trait] -impl CliCommand for InitializeValidator { - fn command_name(&self) -> &'static str { - "InitializeValidator" - } - - async fn execute(mut self) -> CliTypedResult { - let operator_config = self.operator_config_file_args.load()?; - let consensus_public_key = self - .validator_consensus_key_args - .get_consensus_public_key(&operator_config)?; - let consensus_proof_of_possession = self - .validator_consensus_key_args - .get_consensus_proof_of_possession(&operator_config)?; - let ( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - ) = self - .validator_network_addresses_args - .get_network_configs(&operator_config)?; - let validator_network_addresses = - vec![validator_host.as_network_address(validator_network_public_key)?]; - let full_node_network_addresses = - match (full_node_host.as_ref(), full_node_network_public_key) { - (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], - (None, None) => vec![], - _ => { - return Err(CliError::CommandArgumentError( - "If specifying fullnode addresses, both host and public key are required." - .to_string(), - )) - }, - }; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_initialize_validator( - consensus_public_key.to_bytes().to_vec(), - consensus_proof_of_possession.to_bytes().to_vec(), - // BCS encode, so that we can hide the original type - bcs::to_bytes(&validator_network_addresses)?, - bcs::to_bytes(&full_node_network_addresses)?, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Arguments used for operator of the staking pool -#[derive(Parser)] -pub struct OperatorArgs { - #[clap(flatten)] - pub(crate) pool_address_args: OptionalPoolAddressArgs, -} - -impl OperatorArgs { - fn address_fallback_to_profile( - &self, - profile_options: &ProfileOptions, - ) -> CliTypedResult { - if let Some(address) = self.pool_address_args.pool_address { - Ok(address) - } else { - profile_options.account_address() - } - } - - fn address_fallback_to_txn( - &self, - transaction_options: &TransactionOptions, - ) -> CliTypedResult { - if let Some(address) = self.pool_address_args.pool_address { - Ok(address) - } else { - transaction_options.sender_address() - } - } -} - -/// Join the validator set after meeting staking requirements -/// -/// Joining the validator set requires sufficient stake. Once the transaction -/// succeeds, you will join the validator set in the next epoch. -#[derive(Parser)] -pub struct JoinValidatorSet { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for JoinValidatorSet { - fn command_name(&self) -> &'static str { - "JoinValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_join_validator_set(address)) - .await - .map(|inner| inner.into()) - } -} - -/// Leave the validator set -/// -/// Leaving the validator set will require you to have unlocked and withdrawn all stake. After this -/// transaction is successful, you will leave the validator set in the next epoch. -#[derive(Parser)] -pub struct LeaveValidatorSet { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for LeaveValidatorSet { - fn command_name(&self) -> &'static str { - "LeaveValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_leave_validator_set(address)) - .await - .map(|inner| inner.into()) - } -} - -/// Show validator stake information for a specific validator -/// -/// This will show information about a specific validator, given its -/// `--pool-address`. -#[derive(Parser)] -pub struct ShowValidatorStake { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for ShowValidatorStake { - fn command_name(&self) -> &'static str { - "ShowValidatorStake" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let address = self - .operator_args - .address_fallback_to_profile(&self.profile_options)?; - let response = client - .get_resource(address, "0x1::stake::StakePool") - .await?; - Ok(response.into_inner()) - } -} - -/// Show validator configuration for a specific validator -/// -/// This will show information about a specific validator, given its -/// `--pool-address`. -#[derive(Parser)] -pub struct ShowValidatorConfig { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, -} - -#[async_trait] -impl CliCommand for ShowValidatorConfig { - fn command_name(&self) -> &'static str { - "ShowValidatorConfig" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let address = self - .operator_args - .address_fallback_to_profile(&self.profile_options)?; - let validator_config: ValidatorConfig = client - .get_account_resource_bcs(address, "0x1::stake::ValidatorConfig") - .await? - .into_inner(); - Ok((&validator_config) - .try_into() - .map_err(|err| CliError::BCS("Validator config", err))?) - } -} - -/// Show validator details of the validator set -/// -/// This will show information about the validators including their voting power, addresses, and -/// public keys. -#[derive(Parser)] -pub struct ShowValidatorSet { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, -} - -#[async_trait] -impl CliCommand for ShowValidatorSet { - fn command_name(&self) -> &'static str { - "ShowValidatorSet" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self.rest_options.client(&self.profile_options)?; - let validator_set: ValidatorSet = client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await? - .into_inner(); - - ValidatorSetSummary::try_from(&validator_set) - .map_err(|err| CliError::BCS("Validator Set", err)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorSetSummary { - pub scheme: ConsensusScheme, - pub active_validators: Vec, - pub pending_inactive: Vec, - pub pending_active: Vec, - pub total_voting_power: u128, - pub total_joining_power: u128, -} - -impl ValidatorSetSummary { - fn convert_to_summary_vec( - validator_info: Vec, - ) -> Result, bcs::Error> { - let mut validators: Vec = vec![]; - for validator in validator_info.iter() { - match validator.try_into() { - Ok(validator) => validators.push(validator), - Err(err) => return Err(err), - } - } - Ok(validators) - } -} - -impl TryFrom<&ValidatorSet> for ValidatorSetSummary { - type Error = bcs::Error; - - fn try_from(set: &ValidatorSet) -> Result { - let active_validators: Vec = - Self::convert_to_summary_vec(set.active_validators.clone())?; - let pending_inactive: Vec = - Self::convert_to_summary_vec(set.pending_inactive.clone())?; - let pending_active: Vec = - Self::convert_to_summary_vec(set.pending_active.clone())?; - Ok(ValidatorSetSummary { - scheme: set.scheme, - active_validators, - pending_inactive, - pending_active, - total_voting_power: set.total_voting_power, - total_joining_power: set.total_joining_power, - }) - } -} - -impl From<&ValidatorSetSummary> for ValidatorSet { - fn from(summary: &ValidatorSetSummary) -> Self { - ValidatorSet { - scheme: summary.scheme, - active_validators: summary - .active_validators - .iter() - .map(|validator| validator.into()) - .collect(), - pending_inactive: summary - .pending_inactive - .iter() - .map(|validator| validator.into()) - .collect(), - pending_active: summary - .pending_active - .iter() - .map(|validator| validator.into()) - .collect(), - total_voting_power: summary.total_voting_power, - total_joining_power: summary.total_joining_power, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorInfoSummary { - // The validator's account address. AccountAddresses are initially derived from the account - // auth pubkey; however, the auth key can be rotated, so one should not rely on this - // initial property. - pub account_address: AccountAddress, - // Voting power of this validator - consensus_voting_power: u64, - // Validator config - config: ValidatorConfigSummary, -} - -impl TryFrom<&ValidatorInfo> for ValidatorInfoSummary { - type Error = bcs::Error; - - fn try_from(info: &ValidatorInfo) -> Result { - let config = info.config(); - let config = ValidatorConfig { - consensus_public_key: config.consensus_public_key.to_bytes().to_vec(), - validator_network_addresses: config.validator_network_addresses.clone(), - fullnode_network_addresses: config.fullnode_network_addresses.clone(), - validator_index: config.validator_index, - }; - Ok(ValidatorInfoSummary { - account_address: info.account_address, - consensus_voting_power: info.consensus_voting_power(), - config: ValidatorConfigSummary::try_from(&config)?, - }) - } -} - -impl From<&ValidatorInfoSummary> for ValidatorInfo { - fn from(summary: &ValidatorInfoSummary) -> Self { - let config = &summary.config; - ValidatorInfo::new( - summary.account_address, - summary.consensus_voting_power, - aptos_types::validator_config::ValidatorConfig::new( - PublicKey::from_encoded_string(&config.consensus_public_key).unwrap(), - bcs::to_bytes(&config.validator_network_addresses).unwrap(), - bcs::to_bytes(&config.fullnode_network_addresses).unwrap(), - config.validator_index, - ), - ) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub struct ValidatorConfig { - pub consensus_public_key: Vec, - pub validator_network_addresses: Vec, - pub fullnode_network_addresses: Vec, - pub validator_index: u64, -} - -impl ValidatorConfig { - pub fn new( - consensus_public_key: Vec, - validator_network_addresses: Vec, - fullnode_network_addresses: Vec, - validator_index: u64, - ) -> Self { - ValidatorConfig { - consensus_public_key, - validator_network_addresses, - fullnode_network_addresses, - validator_index, - } - } - - pub fn fullnode_network_addresses(&self) -> Result, bcs::Error> { - match &self.validator_network_addresses.is_empty() { - true => Ok(vec![]), - false => bcs::from_bytes(&self.fullnode_network_addresses), - } - } - - pub fn validator_network_addresses(&self) -> Result, bcs::Error> { - match &self.validator_network_addresses.is_empty() { - true => Ok(vec![]), - false => bcs::from_bytes(&self.validator_network_addresses), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct ValidatorConfigSummary { - pub consensus_public_key: String, - /// This is an bcs serialized `Vec` - pub validator_network_addresses: Vec, - /// This is an bcs serialized `Vec` - pub fullnode_network_addresses: Vec, - pub validator_index: u64, -} - -impl TryFrom<&ValidatorConfig> for ValidatorConfigSummary { - type Error = bcs::Error; - - fn try_from(config: &ValidatorConfig) -> Result { - let consensus_public_key = if config.consensus_public_key.is_empty() { - "".into() - } else { - PublicKey::try_from(&config.consensus_public_key[..]) - .unwrap() - .to_encoded_string() - .unwrap() - }; - Ok(ValidatorConfigSummary { - consensus_public_key, - validator_network_addresses: config.validator_network_addresses()?, - fullnode_network_addresses: config.fullnode_network_addresses()?, - validator_index: config.validator_index, - }) - } -} - -impl From<&ValidatorConfigSummary> for ValidatorConfig { - fn from(summary: &ValidatorConfigSummary) -> Self { - let consensus_public_key = if summary.consensus_public_key.is_empty() { - vec![] - } else { - summary.consensus_public_key.as_bytes().to_vec() - }; - ValidatorConfig { - consensus_public_key, - validator_network_addresses: bcs::to_bytes(&summary.validator_network_addresses) - .unwrap(), - fullnode_network_addresses: bcs::to_bytes(&summary.fullnode_network_addresses).unwrap(), - validator_index: summary.validator_index, - } - } -} - -/// Update consensus key for the validator node -/// -/// This will take effect in the next epoch -#[derive(Parser)] -pub struct UpdateConsensusKey { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_consensus_key_args: ValidatorConsensusKeyArgs, -} - -#[async_trait] -impl CliCommand for UpdateConsensusKey { - fn command_name(&self) -> &'static str { - "UpdateConsensusKey" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - let operator_config = self.operator_config_file_args.load()?; - let consensus_public_key = self - .validator_consensus_key_args - .get_consensus_public_key(&operator_config)?; - let consensus_proof_of_possession = self - .validator_consensus_key_args - .get_consensus_proof_of_possession(&operator_config)?; - self.txn_options - .submit_transaction(aptos_stdlib::stake_rotate_consensus_key( - address, - consensus_public_key.to_bytes().to_vec(), - consensus_proof_of_possession.to_bytes().to_vec(), - )) - .await - .map(|inner| inner.into()) - } -} - -/// Update the current validator's network and fullnode network addresses -/// -/// This will take effect in the next epoch -#[derive(Parser)] -pub struct UpdateValidatorNetworkAddresses { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) operator_args: OperatorArgs, - #[clap(flatten)] - pub(crate) operator_config_file_args: OperatorConfigFileArgs, - #[clap(flatten)] - pub(crate) validator_network_addresses_args: ValidatorNetworkAddressesArgs, -} - -#[async_trait] -impl CliCommand for UpdateValidatorNetworkAddresses { - fn command_name(&self) -> &'static str { - "UpdateValidatorNetworkAddresses" - } - - async fn execute(mut self) -> CliTypedResult { - let address = self - .operator_args - .address_fallback_to_txn(&self.txn_options)?; - - let validator_config = self.operator_config_file_args.load()?; - let ( - validator_network_public_key, - full_node_network_public_key, - validator_host, - full_node_host, - ) = self - .validator_network_addresses_args - .get_network_configs(&validator_config)?; - let validator_network_addresses = - vec![validator_host.as_network_address(validator_network_public_key)?]; - let full_node_network_addresses = - match (full_node_host.as_ref(), full_node_network_public_key) { - (Some(host), Some(public_key)) => vec![host.as_network_address(public_key)?], - (None, None) => vec![], - _ => { - return Err(CliError::CommandArgumentError( - "If specifying fullnode addresses, both host and public key are required." - .to_string(), - )) - }, - }; - - self.txn_options - .submit_transaction(aptos_stdlib::stake_update_network_and_fullnode_addresses( - address, - // BCS encode, so that we can hide the original type - bcs::to_bytes(&validator_network_addresses)?, - bcs::to_bytes(&full_node_network_addresses)?, - )) - .await - .map(|inner| inner.into()) - } -} - -/// Analyze the performance of one or more validators -#[derive(Parser)] -pub struct AnalyzeValidatorPerformance { - /// First epoch to analyze - /// - /// Defaults to the first epoch - #[clap(long, default_value_t = -2)] - pub start_epoch: i64, - - /// Last epoch to analyze - /// - /// Defaults to the latest epoch - #[clap(long)] - pub end_epoch: Option, - - /// Analyze mode for the validator: [All, DetailedEpochTable, ValidatorHealthOverTime, NetworkHealthOverTime] - #[clap(value_enum, ignore_case = true, long)] - pub(crate) analyze_mode: AnalyzeMode, - - /// Filter of stake pool addresses to analyze - /// - /// Defaults to all stake pool addresses - #[clap(long, num_args = 0.., value_parser = crate::common::types::load_account_arg)] - pub pool_addresses: Vec, - - #[clap(flatten)] - pub(crate) rest_options: RestOptions, - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, -} - -#[derive(PartialEq, Eq, clap::ValueEnum, Clone)] -pub enum AnalyzeMode { - /// Print all other modes simultaneously - All, - /// For each epoch, print a detailed table containing performance - /// of each of the validators. - DetailedEpochTable, - /// For each validator, summarize it's performance in an epoch into - /// one of the predefined reliability buckets, - /// and prints it's performance across epochs. - ValidatorHealthOverTime, - /// For each epoch summarize how many validators were in - /// each of the reliability buckets. - NetworkHealthOverTime, -} - -#[async_trait] -impl CliCommand<()> for AnalyzeValidatorPerformance { - fn command_name(&self) -> &'static str { - "AnalyzeValidatorPerformance" - } - - async fn execute(mut self) -> CliTypedResult<()> { - let client = self.rest_options.client(&self.profile_options)?; - - let epochs = - FetchMetadata::fetch_new_block_events(&client, Some(self.start_epoch), self.end_epoch) - .await?; - let mut stats = HashMap::new(); - - let print_detailed = self.analyze_mode == AnalyzeMode::DetailedEpochTable - || self.analyze_mode == AnalyzeMode::All; - for epoch_info in epochs { - let mut epoch_stats = - AnalyzeValidators::analyze(&epoch_info.blocks, &epoch_info.validators); - if !self.pool_addresses.is_empty() { - let mut filtered_stats: HashMap = HashMap::new(); - for pool_address in &self.pool_addresses { - filtered_stats.insert( - *pool_address, - *epoch_stats.validator_stats.get(pool_address).unwrap(), - ); - } - epoch_stats.validator_stats = filtered_stats; - } - if print_detailed { - println!( - "Detailed table for {}epoch {}:", - if epoch_info.partial { "partial " } else { "" }, - epoch_info.epoch - ); - AnalyzeValidators::print_detailed_epoch_table( - &epoch_stats, - Some(( - "voting_power", - &epoch_info - .validators - .iter() - .map(|v| (v.address, v.voting_power.to_string())) - .collect::>(), - )), - true, - ); - } - if !epoch_info.partial { - stats.insert(epoch_info.epoch, epoch_stats); - } - } - - if stats.is_empty() { - println!("No data found for given input"); - return Ok(()); - } - let total_stats = stats.values().cloned().reduce(|a, b| a + b).unwrap(); - if print_detailed { - println!( - "Detailed table for all epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_detailed_epoch_table(&total_stats, None, true); - } - let all_validators: Vec<_> = total_stats.validator_stats.keys().cloned().collect(); - if self.analyze_mode == AnalyzeMode::ValidatorHealthOverTime - || self.analyze_mode == AnalyzeMode::All - { - println!( - "Validator health over epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_validator_health_over_time(&stats, &all_validators, None); - } - if self.analyze_mode == AnalyzeMode::NetworkHealthOverTime - || self.analyze_mode == AnalyzeMode::All - { - println!( - "Network health over epochs [{}, {}]:", - stats.keys().min().unwrap(), - stats.keys().max().unwrap() - ); - AnalyzeValidators::print_network_health_over_time(&stats, &all_validators); - } - Ok(()) - } -} - -/// Bootstrap AptosDB from a backup -/// -/// Enables users to load from a backup to catch their node's DB up to a known state. -#[derive(Parser)] -pub struct BootstrapDb { - #[clap(flatten)] - storage: DBToolStorageOpt, - #[clap(flatten)] - opt: RestoreCoordinatorOpt, - #[clap(flatten)] - global: GlobalRestoreOpt, -} - -#[async_trait] -impl CliCommand<()> for BootstrapDb { - fn command_name(&self) -> &'static str { - "BootstrapDb" - } - - async fn execute(self) -> CliTypedResult<()> { - let storage = self.storage.init_storage().await?; - // hack: get around this error, related to use of `async_trait`: - // error: higher-ranked lifetime error - // ... - // = note: could not prove for<'r, 's> Pin>>>: CoerceUnsized> + std::marker::Send + 's)>>> - tokio::task::spawn_blocking(|| { - let runtime = tokio::runtime::Runtime::new().unwrap(); - runtime - .block_on(RestoreCoordinator::new(self.opt, self.global.try_into()?, storage).run()) - }) - .await - .unwrap()?; - Ok(()) - } -} - -/// Checks the network connectivity of a node -/// -/// Checks network connectivity by dialing the node and attempting -/// to establish a connection with a noise handshake. -#[derive(Parser)] -pub struct CheckNetworkConnectivity { - /// `NetworkAddress` of remote server interface. - /// Examples include: - /// - `/dns/example.com/tcp/6180/noise-ik//handshake/1` - /// - `/ip4//tcp/6182/noise-ik//handshake/0` - #[clap(long, value_parser = validate_address)] - pub address: NetworkAddress, - - /// `ChainId` of remote server. - /// Examples include: - /// - Chain numbers, e.g., `2`, `3` and `25`. - /// - Chain names, e.g., `devnet`, `testnet`, `mainnet` and `testing` (for local test networks). - #[clap(long)] - pub chain_id: ChainId, - - #[clap(flatten)] - pub handshake_args: HandshakeArgs, -} - -#[async_trait] -impl CliCommand for CheckNetworkConnectivity { - fn command_name(&self) -> &'static str { - "CheckNetworkConnectivity" - } - - async fn execute(self) -> CliTypedResult { - // Create the check endpoint args for the checker - let node_address_args = NodeAddressArgs { - address: self.address, - chain_id: self.chain_id, - }; - let check_endpoint_args = CheckEndpointArgs { - node_address_args, - handshake_args: self.handshake_args, - }; - - // Check the endpoint - aptos_network_checker::check_endpoint(&check_endpoint_args, None) - .await - .map_err(|error| CliError::UnexpectedError(error.to_string())) - } -} - -/// Show epoch information -/// -/// Displays the current epoch, the epoch length, and the estimated time of the next epoch -#[derive(Parser)] -pub struct ShowEpochInfo { - #[clap(flatten)] - pub(crate) profile_options: ProfileOptions, - #[clap(flatten)] - pub(crate) rest_options: RestOptions, -} - -#[async_trait] -impl CliCommand for ShowEpochInfo { - fn command_name(&self) -> &'static str { - "ShowEpochInfo" - } - - async fn execute(self) -> CliTypedResult { - let client = &self.rest_options.client(&self.profile_options)?; - get_epoch_info(client).await - } -} - -async fn get_epoch_info(client: &Client) -> CliTypedResult { - let (block_resource, state): (BlockResource, State) = client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::block::BlockResource") - .await? - .into_parts(); - let reconfig_resource: ConfigurationResource = client - .get_account_resource_at_version_bcs( - CORE_CODE_ADDRESS, - "0x1::reconfiguration::Configuration", - state.version, - ) - .await? - .into_inner(); - - let epoch_interval = block_resource.epoch_interval(); - let epoch_interval_secs = epoch_interval / SECS_TO_MICROSECS; - let last_reconfig = reconfig_resource.last_reconfiguration_time(); - Ok(EpochInfo { - epoch: reconfig_resource.epoch(), - epoch_interval_secs, - current_epoch_start_time: Time::new_micros(last_reconfig), - next_epoch_start_time: Time::new_micros(last_reconfig + epoch_interval), - }) -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EpochInfo { - epoch: u64, - epoch_interval_secs: u64, - current_epoch_start_time: Time, - next_epoch_start_time: Time, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Time { - unix_time: u128, - utc_time: DateTime, -} - -impl Time { - pub fn new(time: Duration) -> Self { - let date_time = - NaiveDateTime::from_timestamp_opt(time.as_secs() as i64, time.subsec_nanos()).unwrap(); - #[allow(deprecated)] - let utc_time = DateTime::from_utc(date_time, Utc); - // TODO: Allow configurable time zone - Self { - unix_time: time.as_micros(), - utc_time, - } - } - - pub fn new_micros(microseconds: u64) -> Self { - Self::new(Duration::from_micros(microseconds)) - } - - pub fn new_seconds(seconds: u64) -> Self { - Self::new(Duration::from_secs(seconds)) - } -} - -#[cfg(test)] -mod tests { - use crate::{CliResult, Tool}; - use clap::Parser; - - // TODO: there have to be cleaner ways to test things. Maybe a CLI test framework? - - // FIXME: Remove this test, it's very fragile and move to E2E CLI test framework - #[tokio::test] - // Verifies basic properties about the network connectivity checker - async fn test_check_network_connectivity() { - // Verify that an invalid address will return an error - let args = &[ - "aptos", - "node", - "check-network-connectivity", - "--address", - "invalid-address", - "--chain-id", - "mainnet", - ]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "Invalid address"); - - // Verify that an invalid chain-id will return an error - let args = &["aptos", "node", "check-network-connectivity", "--address", "/ip4/34.70.116.169/tcp/6182/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "invalid-chain"]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "invalid value"); - - // Verify that a failure to connect will return a timeout - let args = &["aptos", "node", "check-network-connectivity", "--address", "/ip4/31.71.116.169/tcp/0001/noise-ik/0x249f3301db104705652e0a0c471b46d13172b2baf14e31f007413f3baee46b0c/handshake/0", "--chain-id", "testnet"]; - let error_message = run_tool_with_args(args).await.unwrap_err(); - assert_contains(error_message, "Timed out while checking endpoint"); - } - - async fn run_tool_with_args(args: &[&str]) -> CliResult { - let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; - tool.execute().await - } - - fn assert_contains(message: String, expected_string: &str) { - if !message.contains(expected_string) { - panic!( - "Expected message to contain {:?}, but it did not! Message: {:?}", - expected_string, message - ); - } - } -} diff --git a/crates/aptos/src/op/key.rs b/crates/aptos/src/op/key.rs deleted file mode 100644 index 07a65a21db2da..0000000000000 --- a/crates/aptos/src/op/key.rs +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - account_address_from_public_key, CliError, CliTypedResult, EncodingOptions, KeyType, - RngArgs, SaveFile, - }, - utils::{ - append_file_extension, check_if_file_exists, generate_vanity_account_ed25519, - write_to_file, - }, - }, - CliCommand, CliResult, -}; -use aptos_config::config::{Peer, PeerRole}; -use aptos_crypto::{ - bls12381, ed25519, encoding_type::EncodingType, x25519, PrivateKey, ValidCryptoMaterial, -}; -use aptos_genesis::config::HostAndPort; -use aptos_types::account_address::{ - create_multisig_account_address, from_identity_public_key, AccountAddress, -}; -use async_trait::async_trait; -use clap::{Parser, Subcommand}; -use std::{ - collections::{HashMap, HashSet}, - path::{Path, PathBuf}, -}; - -pub const PUBLIC_KEY_EXTENSION: &str = "pub"; - -/// Tool for generating, inspecting, and interacting with keys -/// -/// This tool allows users to generate and extract related information -/// with all key types used on the Aptos blockchain. -#[derive(Debug, Subcommand)] -pub enum KeyTool { - Generate(GenerateKey), - ExtractPeer(ExtractPeer), -} - -impl KeyTool { - pub async fn execute(self) -> CliResult { - match self { - KeyTool::Generate(tool) => tool.execute_serialized().await, - KeyTool::ExtractPeer(tool) => tool.execute_serialized().await, - } - } -} - -/// Extract full peer information for an upstream peer -/// -/// This command builds a YAML blob that can be copied into a user's network configuration. -/// A host is required to build the network address used for the connection, and the -/// network key is required to identify the peer. -/// -/// A `private-network-key` or `public-network-key` can be given encoded on the command line, or -/// a `private-network-key-file` or a `public-network-key-file` can be given to read from. -/// The `output-file` will be a YAML serialized peer information for use in network config. -#[derive(Debug, Parser)] -pub struct ExtractPeer { - /// Host and port of the full node - /// - /// e.g. 127.0.0.1:6180 or my-awesome-dns.com:6180 - #[clap(long)] - pub(crate) host: HostAndPort, - - #[clap(flatten)] - pub(crate) network_key_input_options: NetworkKeyInputOptions, - #[clap(flatten)] - pub(crate) output_file_options: SaveFile, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -#[async_trait] -impl CliCommand> for ExtractPeer { - fn command_name(&self) -> &'static str { - "ExtractPeer" - } - - async fn execute(self) -> CliTypedResult> { - // Load key based on public or private - let public_key = self - .network_key_input_options - .extract_public_network_key(self.encoding_options.encoding)?; - - // Check output file exists - self.output_file_options.check_file()?; - - // Build peer info - let peer_id = from_identity_public_key(public_key); - let mut public_keys = HashSet::new(); - public_keys.insert(public_key); - - let address = self.host.as_network_address(public_key).map_err(|err| { - CliError::UnexpectedError(format!("Failed to build network address: {}", err)) - })?; - - let peer = Peer::new(vec![address], public_keys, PeerRole::Upstream); - - let mut map = HashMap::new(); - map.insert(peer_id, peer); - - // Save to file - let yaml = serde_yaml::to_string(&map) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?; - self.output_file_options - .save_to_file("Extracted peer", yaml.as_bytes())?; - Ok(map) - } -} - -#[derive(Debug, Default, Parser)] -pub struct NetworkKeyInputOptions { - /// x25519 Private key input file name - #[clap(long, group = "network_key_input", value_parser)] - private_network_key_file: Option, - - /// x25519 Private key encoded in a type as shown in `encoding` - #[clap(long, group = "network_key_input")] - private_network_key: Option, - - /// x25519 Public key input file name - #[clap(long, group = "network_key_input", value_parser)] - public_network_key_file: Option, - - /// x25519 Public key encoded in a type as shown in `encoding` - #[clap(long, group = "network_key_input")] - public_network_key: Option, -} - -impl NetworkKeyInputOptions { - pub fn from_private_key_file(file: PathBuf) -> Self { - Self { - private_network_key_file: Some(file), - private_network_key: None, - public_network_key_file: None, - public_network_key: None, - } - } - - pub fn extract_public_network_key( - self, - encoding: EncodingType, - ) -> CliTypedResult { - // The grouping above prevents there from being more than one, but just in case - match (self.public_network_key, self.public_network_key_file, self.private_network_key, self.private_network_key_file){ - (Some(public_network_key), None, None, None) => Ok(encoding.decode_key("--public-network-key", public_network_key.as_bytes().to_vec())?), - (None, Some(public_network_key_file),None, None) => Ok(encoding.load_key("--public-network-key-file", public_network_key_file.as_path())?), - (None, None, Some(private_network_key), None) => { - let private_network_key: x25519::PrivateKey = encoding.decode_key("--private-network-key", private_network_key.as_bytes().to_vec())?; - Ok(private_network_key.public_key()) - }, - (None, None, None, Some(private_network_key_file)) => { - let private_network_key: x25519::PrivateKey = encoding.load_key("--private-network-key-file", private_network_key_file.as_path())?; - Ok(private_network_key.public_key()) - }, - _ => Err(CliError::CommandArgumentError("Must provide exactly one of [--public-network-key, --public-network-key-file, --private-network-key, --private-network-key-file]".to_string())) - } - } -} - -/// Generates a `x25519` or `ed25519` key. -/// -/// This can be used for generating an identity. Two files will be created -/// `output_file` and `output_file.pub`. `output_file` will contain the private -/// key encoded with the `encoding` and `output_file.pub` will contain the public -/// key encoded with the `encoding`. -#[derive(Debug, Parser)] -pub struct GenerateKey { - /// Key type to generate. Must be one of [x25519, ed25519, bls12381] - #[clap(long, default_value_t = KeyType::Ed25519)] - pub(crate) key_type: KeyType, - /// Vanity prefix that resultant account address should start with, e.g. 0xaceface or d00d. Each - /// additional character multiplies by a factor of 16 the computational difficulty associated - /// with generating an address, so try out shorter prefixes first and be prepared to wait for - /// longer ones - #[clap(long)] - pub vanity_prefix: Option, - /// Use this flag when vanity prefix is for a multisig account. This mines a private key for - /// a single signer account that can, as its first transaction, create a multisig account with - /// the given vanity prefix - #[clap(long)] - pub vanity_multisig: bool, - #[clap(flatten)] - pub rng_args: RngArgs, - #[clap(flatten)] - pub(crate) save_params: SaveKey, -} - -#[async_trait] -impl CliCommand> for GenerateKey { - fn command_name(&self) -> &'static str { - "GenerateKey" - } - - async fn execute(self) -> CliTypedResult> { - if self.vanity_prefix.is_some() && !matches!(self.key_type, KeyType::Ed25519) { - return Err(CliError::CommandArgumentError(format!( - "Vanity prefixes are only accepted for {} keys", - KeyType::Ed25519 - ))); - } - if self.vanity_multisig && self.vanity_prefix.is_none() { - return Err(CliError::CommandArgumentError( - "No vanity prefix provided".to_string(), - )); - } - self.save_params.check_key_file()?; - let mut keygen = self.rng_args.key_generator()?; - match self.key_type { - KeyType::X25519 => { - let private_key = keygen.generate_x25519_private_key().map_err(|err| { - CliError::UnexpectedError(format!( - "Failed to convert ed25519 to x25519 {:?}", - err - )) - })?; - self.save_params.save_key(&private_key, "x25519") - }, - KeyType::Ed25519 => { - // If no vanity prefix specified, generate a standard Ed25519 private key. - let private_key = if self.vanity_prefix.is_none() { - keygen.generate_ed25519_private_key() - } else { - // If a vanity prefix is specified, generate vanity Ed25519 account from it. - generate_vanity_account_ed25519( - self.vanity_prefix.clone().unwrap().as_str(), - self.vanity_multisig, - )? - }; - // Store CLI result from key save operation, to append vanity address(es) if needed. - let mut result_map = self.save_params.save_key(&private_key, "ed25519").unwrap(); - if self.vanity_prefix.is_some() { - let account_address = account_address_from_public_key( - &ed25519::Ed25519PublicKey::from(&private_key), - ); - // Store account address in a PathBuf so it can be displayed in CLI result. - result_map.insert( - "Account Address:", - PathBuf::from(account_address.to_hex_literal()), - ); - if self.vanity_multisig { - let multisig_account_address = - create_multisig_account_address(account_address, 0); - result_map.insert( - "Multisig Account Address:", - PathBuf::from(multisig_account_address.to_hex_literal()), - ); - } - } - return Ok(result_map); - }, - KeyType::Bls12381 => { - let private_key = keygen.generate_bls12381_private_key(); - self.save_params.save_bls_key(&private_key, "bls12381") - }, - } - } -} - -impl GenerateKey { - /// A test friendly typed key generation for x25519 keys. - pub async fn generate_x25519( - encoding: EncodingType, - key_file: &Path, - ) -> CliTypedResult<(x25519::PrivateKey, x25519::PublicKey)> { - let args = format!( - "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", - key_type = KeyType::X25519, - key_file = key_file.display(), - encoding = encoding, - ); - let command = GenerateKey::parse_from(args.split_whitespace()); - command.execute().await?; - Ok(( - encoding.load_key("private_key", key_file)?, - encoding.load_key( - "public_key", - &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, - )?, - )) - } - - /// A test friendly typed key generation for e25519 keys. - pub async fn generate_ed25519( - encoding: EncodingType, - key_file: &Path, - ) -> CliTypedResult<(ed25519::Ed25519PrivateKey, ed25519::Ed25519PublicKey)> { - let args = format!( - "generate --key-type {key_type:?} --output-file {key_file} --encoding {encoding:?} --assume-yes", - key_type = KeyType::Ed25519, - key_file = key_file.display(), - encoding = encoding, - ); - let command = GenerateKey::parse_from(args.split_whitespace()); - command.execute().await?; - Ok(( - encoding.load_key("private_key", key_file)?, - encoding.load_key( - "public_key", - &append_file_extension(key_file, PUBLIC_KEY_EXTENSION)?, - )?, - )) - } -} - -#[derive(Debug, Parser)] -pub struct SaveKey { - #[clap(flatten)] - pub(crate) file_options: SaveFile, - #[clap(flatten)] - pub(crate) encoding_options: EncodingOptions, -} - -impl SaveKey { - /// Public key file name - fn public_key_file(&self) -> CliTypedResult { - append_file_extension( - self.file_options.output_file.as_path(), - PUBLIC_KEY_EXTENSION, - ) - } - - /// Public key file name - fn proof_of_possession_file(&self) -> CliTypedResult { - append_file_extension(self.file_options.output_file.as_path(), "pop") - } - - /// Check if the key file exists already - pub fn check_key_file(&self) -> CliTypedResult<()> { - // Check if file already exists - self.file_options.check_file()?; - check_if_file_exists(&self.public_key_file()?, self.file_options.prompt_options) - } - - /// Saves a key to a file encoded in a string - pub fn save_key( - self, - key: &Key, - key_name: &'static str, - ) -> CliTypedResult> { - let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; - let encoded_public_key = self - .encoding_options - .encoding - .encode_key(key_name, &key.public_key())?; - - // Write private and public keys to files - let public_key_file = self.public_key_file()?; - self.file_options - .save_to_file_confidential(key_name, &encoded_private_key)?; - write_to_file(&public_key_file, key_name, &encoded_public_key)?; - - let mut map = HashMap::new(); - map.insert("PrivateKey Path", self.file_options.output_file); - map.insert("PublicKey Path", public_key_file); - Ok(map) - } - - /// Saves a key to a file encoded in a string - pub fn save_bls_key( - self, - key: &bls12381::PrivateKey, - key_name: &'static str, - ) -> CliTypedResult> { - let encoded_private_key = self.encoding_options.encoding.encode_key(key_name, key)?; - let encoded_public_key = self - .encoding_options - .encoding - .encode_key(key_name, &key.public_key())?; - let encoded_proof_of_posession = self - .encoding_options - .encoding - .encode_key(key_name, &bls12381::ProofOfPossession::create(key))?; - - // Write private and public keys to files - let public_key_file = self.public_key_file()?; - let proof_of_possession_file = self.proof_of_possession_file()?; - self.file_options - .save_to_file_confidential(key_name, &encoded_private_key)?; - write_to_file(&public_key_file, key_name, &encoded_public_key)?; - write_to_file( - &proof_of_possession_file, - key_name, - &encoded_proof_of_posession, - )?; - - let mut map = HashMap::new(); - map.insert("PrivateKey Path", self.file_options.output_file); - map.insert("PublicKey Path", public_key_file); - map.insert("Proof of possession Path", proof_of_possession_file); - Ok(map) - } -} diff --git a/crates/aptos/src/op/mod.rs b/crates/aptos/src/op/mod.rs deleted file mode 100644 index 989a2bb8e32fb..0000000000000 --- a/crates/aptos/src/op/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod key; diff --git a/crates/aptos/src/stake/mod.rs b/crates/aptos/src/stake/mod.rs deleted file mode 100644 index c2fcd8a37957a..0000000000000 --- a/crates/aptos/src/stake/mod.rs +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - common::{ - types::{ - CliCommand, CliError, CliResult, CliTypedResult, TransactionOptions, TransactionSummary, - }, - utils::prompt_yes_with_override, - }, - node::{get_stake_pools, StakePoolType}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_types::{ - account_address::{ - create_vesting_contract_address, default_stake_pool_address, AccountAddress, - }, - vesting::VestingAdminStore, -}; -use async_trait::async_trait; -use clap::Parser; - -/// Tool for manipulating stake and stake pools -/// -#[derive(Parser)] -pub enum StakeTool { - AddStake(AddStake), - CreateStakingContract(CreateStakingContract), - DistributeVestedCoins(DistributeVestedCoins), - IncreaseLockup(IncreaseLockup), - InitializeStakeOwner(InitializeStakeOwner), - RequestCommission(RequestCommission), - SetDelegatedVoter(SetDelegatedVoter), - SetOperator(SetOperator), - UnlockStake(UnlockStake), - UnlockVestedCoins(UnlockVestedCoins), - WithdrawStake(WithdrawStake), -} - -impl StakeTool { - pub async fn execute(self) -> CliResult { - use StakeTool::*; - match self { - AddStake(tool) => tool.execute_serialized().await, - CreateStakingContract(tool) => tool.execute_serialized().await, - DistributeVestedCoins(tool) => tool.execute_serialized().await, - IncreaseLockup(tool) => tool.execute_serialized().await, - InitializeStakeOwner(tool) => tool.execute_serialized().await, - RequestCommission(tool) => tool.execute_serialized().await, - SetDelegatedVoter(tool) => tool.execute_serialized().await, - SetOperator(tool) => tool.execute_serialized().await, - UnlockStake(tool) => tool.execute_serialized().await, - UnlockVestedCoins(tool) => tool.execute_serialized().await, - WithdrawStake(tool) => tool.execute_serialized().await, - } - } -} - -/// Add APT to a stake pool -/// -/// This command allows stake pool owners to add APT to their stake. -#[derive(Parser)] -pub struct AddStake { - /// Amount of Octas (10^-8 APT) to add to stake - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for AddStake { - fn command_name(&self) -> &'static str { - "AddStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let amount = self.amount; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_add_stake(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_add_stake( - stake_pool.operator_address, - amount, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Adding stake is not supported for vesting contracts".into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Unlock staked APT in a stake pool -/// -/// APT coins can only be unlocked if they no longer have an applied lockup period -#[derive(Parser)] -pub struct UnlockStake { - /// Amount of Octas (10^-8 APT) to unlock - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for UnlockStake { - fn command_name(&self) -> &'static str { - "UnlockStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let amount = self.amount; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_unlock(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_unlock_stake( - stake_pool.operator_address, - amount, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Unlocking stake is not supported for vesting contracts".into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Withdraw unlocked staked APT from a stake pool -/// -/// This allows users to withdraw stake back into their CoinStore. -/// Before calling `WithdrawStake`, `UnlockStake` must be called first. -#[derive(Parser)] -pub struct WithdrawStake { - /// Amount of Octas (10^-8 APT) to withdraw. - /// This only applies to stake pools owned directly by the owner account, instead of via - /// a staking contract. In the latter case, when withdrawal is issued, all coins are distributed - #[clap(long)] - pub amount: u64, - - #[clap(flatten)] - pub(crate) node_op_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for WithdrawStake { - fn command_name(&self) -> &'static str { - "WithdrawStake" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .node_op_options - .rest_options - .client(&self.node_op_options.profile_options)?; - let amount = self.amount; - let owner_address = self.node_op_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.node_op_options - .submit_transaction(aptos_stdlib::stake_withdraw(amount)) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.node_op_options - .submit_transaction(aptos_stdlib::staking_contract_distribute( - owner_address, - stake_pool.operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - return Err(CliError::UnexpectedError( - "Stake withdrawal from vesting contract should use distribute-vested-coins" - .into(), - )) - }, - } - } - Ok(transaction_summaries) - } -} - -/// Increase lockup of all staked APT in a stake pool -/// -/// Lockup may need to be increased in order to vote on a proposal. -#[derive(Parser)] -pub struct IncreaseLockup { - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for IncreaseLockup { - fn command_name(&self) -> &'static str { - "IncreaseLockup" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_increase_lockup()) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_reset_lockup( - stake_pool.operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::vesting_reset_lockup( - stake_pool.vesting_contract.unwrap(), - )) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Initialize a stake pool owner -/// -/// Initializing stake owner adds the capability to delegate the -/// stake pool to an operator, or delegate voting to a different account. -#[derive(Parser)] -pub struct InitializeStakeOwner { - /// Initial amount of Octas (10^-8 APT) to be staked - #[clap(long)] - pub initial_stake_amount: u64, - - /// Account Address of delegated operator - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub operator_address: Option, - - /// Account address of delegated voter - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub voter_address: Option, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for InitializeStakeOwner { - fn command_name(&self) -> &'static str { - "InitializeStakeOwner" - } - - async fn execute(mut self) -> CliTypedResult { - let owner_address = self.txn_options.sender_address()?; - self.txn_options - .submit_transaction(aptos_stdlib::stake_initialize_stake_owner( - self.initial_stake_amount, - self.operator_address.unwrap_or(owner_address), - self.voter_address.unwrap_or(owner_address), - )) - .await - .map(|inner| inner.into()) - } -} - -/// Delegate operator capability to another account -/// -/// This changes teh operator capability from its current operator to a different operator. -/// By default, the operator of a stake pool is the owner of the stake pool -#[derive(Parser)] -pub struct SetOperator { - /// Account Address of delegated operator - /// - /// If not specified, it will be the same as the owner - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub operator_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for SetOperator { - fn command_name(&self) -> &'static str { - "SetOperator" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let new_operator_address = self.operator_address; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_set_operator( - new_operator_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction( - aptos_stdlib::staking_contract_switch_operator_with_same_commission( - stake_pool.operator_address, - new_operator_address, - ), - ) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction( - aptos_stdlib::vesting_update_operator_with_same_commission( - stake_pool.vesting_contract.unwrap(), - new_operator_address, - ), - ) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Delegate voting capability to another account -/// -/// Delegates voting capability from its current voter to a different voter. -/// By default, the voter of a stake pool is the owner of the stake pool -#[derive(Parser)] -pub struct SetDelegatedVoter { - /// Account Address of delegated voter - /// - /// If not specified, it will be the same as the owner - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub voter_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand> for SetDelegatedVoter { - fn command_name(&self) -> &'static str { - "SetDelegatedVoter" - } - - async fn execute(mut self) -> CliTypedResult> { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - let owner_address = self.txn_options.sender_address()?; - let new_voter_address = self.voter_address; - let mut transaction_summaries: Vec = vec![]; - - let stake_pool_results = get_stake_pools(&client, owner_address).await?; - for stake_pool in stake_pool_results { - match stake_pool.pool_type { - StakePoolType::Direct => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::stake_set_delegated_voter( - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::StakingContract => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_update_voter( - stake_pool.operator_address, - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - StakePoolType::Vesting => { - transaction_summaries.push( - self.txn_options - .submit_transaction(aptos_stdlib::vesting_update_voter( - stake_pool.vesting_contract.unwrap(), - new_voter_address, - )) - .await - .map(|inner| inner.into())?, - ); - }, - } - } - Ok(transaction_summaries) - } -} - -/// Create a staking contract stake pool -/// -/// -#[derive(Parser)] -pub struct CreateStakingContract { - /// Account Address of operator - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub operator: AccountAddress, - - /// Account Address of delegated voter - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub voter: AccountAddress, - - /// Amount to create the staking contract with - #[clap(long)] - pub amount: u64, - - /// Percentage of accumulated rewards to pay the operator as commission - #[clap(long)] - pub commission_percentage: u64, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for CreateStakingContract { - fn command_name(&self) -> &'static str { - "CreateStakingContract" - } - - async fn execute(mut self) -> CliTypedResult { - let pool_address = default_stake_pool_address( - self.txn_options.profile_options.account_address()?, - self.operator, - ); - prompt_yes_with_override( - &format!( - "Creating a new staking contract with pool address 0x{}. Confirm?", - pool_address - ), - self.txn_options.prompt_options, - )?; - - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_create_staking_contract( - self.operator, - self.voter, - self.amount, - self.commission_percentage, - vec![], - )) - .await - .map(|inner| inner.into()) - } -} - -/// Distribute fully unlocked coins from vesting -/// -/// Distribute fully unlocked coins (rewards and/or vested coins) from the vesting contract -/// to shareholders. -#[derive(Parser)] -pub struct DistributeVestedCoins { - /// Address of the vesting contract's admin. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub admin_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for DistributeVestedCoins { - fn command_name(&self) -> &'static str { - "DistributeVestedCoins" - } - - async fn execute(mut self) -> CliTypedResult { - let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); - self.txn_options - .submit_transaction(aptos_stdlib::vesting_distribute(vesting_contract_address)) - .await - .map(|inner| inner.into()) - } -} - -/// Unlock vested coins -/// -/// Unlock vested coins according to the vesting contract's schedule. -/// This also unlocks any accumulated staking rewards and pays commission to the operator of the -/// vesting contract's stake pool first. -/// -/// The unlocked vested tokens and staking rewards are still subject to the staking lockup and -/// cannot be withdrawn until after the lockup expires. -#[derive(Parser)] -pub struct UnlockVestedCoins { - /// Address of the vesting contract's admin. - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub admin_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for UnlockVestedCoins { - fn command_name(&self) -> &'static str { - "UnlockVestedCoins" - } - - async fn execute(mut self) -> CliTypedResult { - let vesting_contract_address = create_vesting_contract_address(self.admin_address, 0, &[]); - self.txn_options - .submit_transaction(aptos_stdlib::vesting_vest(vesting_contract_address)) - .await - .map(|inner| inner.into()) - } -} - -/// Request commission from running a stake pool -/// -/// Allows operators or owners to request commission from running a stake pool (only if there's a -/// staking contract set up with the staker). The commission will be withdrawable at the end of the -/// stake pool's current lockup period. -#[derive(Parser)] -pub struct RequestCommission { - /// Address of the owner of the stake pool - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub owner_address: AccountAddress, - - /// Address of the operator of the stake pool - #[clap(long, value_parser = crate::common::types::load_account_arg)] - pub operator_address: AccountAddress, - - #[clap(flatten)] - pub(crate) txn_options: TransactionOptions, -} - -#[async_trait] -impl CliCommand for RequestCommission { - fn command_name(&self) -> &'static str { - "RequestCommission" - } - - async fn execute(mut self) -> CliTypedResult { - let client = self - .txn_options - .rest_options - .client(&self.txn_options.profile_options)?; - - // If this is a vesting stake pool, retrieve the associated vesting contract - let vesting_admin_store = client - .get_account_resource_bcs::( - self.owner_address, - "0x1::vesting::AdminStore", - ) - .await; - - // Note: this only works if the vesting contract has exactly one staking contract - // associated - let staker_address = if let Ok(vesting_admin_store) = vesting_admin_store { - vesting_admin_store.into_inner().vesting_contracts[0] - } else { - self.owner_address - }; - self.txn_options - .submit_transaction(aptos_stdlib::staking_contract_request_commission( - staker_address, - self.operator_address, - )) - .await - .map(|inner| inner.into()) - } -} diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs deleted file mode 100644 index e936b8dea8f36..0000000000000 --- a/crates/aptos/src/test/mod.rs +++ /dev/null @@ -1,1261 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - account::{ - create::{CreateAccount, DEFAULT_FUNDED_COINS}, - fund::FundWithFaucet, - key_rotation::{LookupAddress, RotateKey, RotateSummary}, - list::{ListAccount, ListQuery}, - transfer::{TransferCoins, TransferSummary}, - }, - common::{ - init::{InitTool, Network}, - types::{ - account_address_from_public_key, AccountAddressWrapper, ArgWithTypeVec, - AuthenticationKeyInputOptions, CliError, CliTypedResult, EncodingOptions, - EntryFunctionArguments, FaucetOptions, GasOptions, KeyType, MoveManifestAccountWrapper, - MovePackageDir, OptionalPoolAddressArgs, OverrideSizeCheckOption, PoolAddressArgs, - PrivateKeyInputOptions, PromptOptions, PublicKeyInputOptions, RestOptions, RngArgs, - SaveFile, ScriptFunctionArguments, TransactionOptions, TransactionSummary, TypeArgVec, - }, - utils::write_to_file, - }, - governance::{ - CompileScriptFunction, ProposalSubmissionSummary, SubmitProposal, SubmitProposalArgs, - SubmitVote, SubmitVoteArgs, VerifyProposal, VerifyProposalResponse, - }, - move_tool::{ - ArgWithType, CompilePackage, DownloadPackage, FrameworkPackageArgs, IncludedArtifacts, - IncludedArtifactsArgs, InitPackage, MemberId, PublishPackage, RunFunction, RunScript, - TestPackage, - }, - node::{ - AnalyzeMode, AnalyzeValidatorPerformance, GetStakePool, InitializeValidator, - JoinValidatorSet, LeaveValidatorSet, OperatorArgs, OperatorConfigFileArgs, - ShowValidatorConfig, ShowValidatorSet, ShowValidatorStake, StakePoolResult, - UpdateConsensusKey, UpdateValidatorNetworkAddresses, ValidatorConfig, - ValidatorConsensusKeyArgs, ValidatorNetworkAddressesArgs, - }, - op::key::{ExtractPeer, GenerateKey, NetworkKeyInputOptions, SaveKey}, - stake::{ - AddStake, IncreaseLockup, InitializeStakeOwner, SetDelegatedVoter, SetOperator, - UnlockStake, WithdrawStake, - }, - CliCommand, -}; -use aptos_config::config::Peer; -use aptos_crypto::{ - bls12381, - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - x25519, PrivateKey, -}; -use aptos_genesis::config::HostAndPort; -use aptos_keygen::KeyGen; -use aptos_logger::warn; -use aptos_rest_client::{ - aptos_api_types::{MoveStructTag, MoveType}, - Transaction, -}; -use aptos_sdk::move_types::{account_address::AccountAddress, language_storage::ModuleId}; -use aptos_temppath::TempPath; -use aptos_types::on_chain_config::ValidatorSet; -use move_core_types::ident_str; -use reqwest::Url; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::{ - collections::{BTreeMap, HashMap}, - mem, - path::PathBuf, - str::FromStr, - time::Duration, -}; -use tempfile::TempDir; -use thiserror::__private::AsDisplay; -use tokio::time::{sleep, Instant}; - -#[cfg(test)] -mod tests; - -pub const INVALID_ACCOUNT: &str = "0xDEADBEEFCAFEBABE"; - -pub const FIRST_MOVE_FILE: &str = " -module NamedAddress0::store { - use std::string; - use aptos_framework::coin::{Self}; - - struct CoolCoin has key {} - - public entry fun init( - account: &signer, - decimals: u64, - monitor_supply: bool - ) { - let (_, _) = coin::initialize(account, string::utf8(b\"CoolCoin\"), string::utf8(b\"COOL\"), decimals, monitor_supply); - coin::register(account); - } -}"; - -/// A framework for testing the CLI -pub struct CliTestFramework { - account_addresses: Vec, - account_keys: Vec, - endpoint: Url, - faucet_endpoint: Url, - move_dir: Option, -} - -impl CliTestFramework { - pub fn local_new(num_accounts: usize) -> CliTestFramework { - let dummy_url = Url::parse("http://localhost").unwrap(); - let mut framework = CliTestFramework { - account_addresses: Vec::new(), - account_keys: Vec::new(), - endpoint: dummy_url.clone(), - faucet_endpoint: dummy_url, - move_dir: None, - }; - let mut keygen = KeyGen::from_seed([0; 32]); - for _ in 0..num_accounts { - let key = keygen.generate_ed25519_private_key(); - framework.add_account_to_cli(key); - } - framework - } - - pub async fn new(endpoint: Url, faucet_endpoint: Url, num_accounts: usize) -> CliTestFramework { - let mut framework = CliTestFramework { - account_addresses: Vec::new(), - account_keys: Vec::new(), - endpoint, - faucet_endpoint, - move_dir: None, - }; - let mut keygen = KeyGen::from_seed([0; 32]); - - for _ in 0..num_accounts { - framework - .create_cli_account_from_faucet(keygen.generate_ed25519_private_key(), None) - .await - .unwrap(); - } - - framework - } - - pub fn addresses(&self) -> Vec { - self.account_addresses.clone() - } - - async fn check_account_exists(&self, index: usize) -> bool { - // Create account if it doesn't exist (and there's a faucet) - let client = aptos_rest_client::Client::new(self.endpoint.clone()); - let address = self.account_id(index); - client.get_account(address).await.is_ok() - } - - pub fn add_account_to_cli(&mut self, private_key: Ed25519PrivateKey) -> usize { - let address = account_address_from_public_key(&private_key.public_key()); - self.account_addresses.push(address); - self.account_keys.push(private_key); - println!( - "Account: {} (index: {})", - address.to_hex_literal(), - self.account_keys.len() - 1 - ); - self.account_keys.len() - 1 - } - - pub fn add_account_with_address_to_cli( - &mut self, - private_key: Ed25519PrivateKey, - address: AccountAddress, - ) -> usize { - self.account_addresses.push(address); - self.account_keys.push(private_key); - self.account_keys.len() - 1 - } - - pub async fn create_cli_account( - &mut self, - private_key: Ed25519PrivateKey, - sender_index: usize, - ) -> CliTypedResult { - let index = self.add_account_to_cli(private_key); - if self.check_account_exists(index).await { - return Err(CliError::UnexpectedError( - "Account already exists".to_string(), - )); - } - CreateAccount { - txn_options: self.transaction_options(sender_index, None), - account: self.account_id(index), - } - .execute() - .await?; - - Ok(index) - } - - pub async fn create_cli_account_from_faucet( - &mut self, - private_key: Ed25519PrivateKey, - amount: Option, - ) -> CliTypedResult { - let index = self.add_account_to_cli(private_key); - if self.check_account_exists(index).await { - return Err(CliError::UnexpectedError( - "Account already exists".to_string(), - )); - } - - self.fund_account(index, amount).await?; - warn!( - "Funded account {:?} with {:?} OCTA", - self.account_id(index), - amount.unwrap_or(DEFAULT_FUNDED_COINS) - ); - Ok(index) - } - - pub async fn fund_account(&self, index: usize, amount: Option) -> CliTypedResult { - FundWithFaucet { - profile_options: Default::default(), - account: Some(self.account_id(index)), - faucet_options: self.faucet_options(), - amount: amount.unwrap_or(DEFAULT_FUNDED_COINS), - rest_options: self.rest_options(), - } - .execute() - .await - } - - pub async fn lookup_address( - &self, - public_key: &Ed25519PublicKey, - ) -> CliTypedResult { - LookupAddress { - public_key_options: PublicKeyInputOptions::from_key(public_key), - rest_options: self.rest_options(), - encoding_options: Default::default(), - profile_options: Default::default(), - authentication_key_options: AuthenticationKeyInputOptions::from_public_key(public_key), - } - .execute() - .await - } - - pub async fn rotate_key( - &mut self, - index: usize, - new_private_key: String, - gas_options: Option, - ) -> CliTypedResult { - let response = RotateKey { - txn_options: TransactionOptions { - private_key_options: PrivateKeyInputOptions::from_private_key( - self.private_key(index), - ) - .unwrap(), - sender_account: Some(self.account_id(index)), - rest_options: self.rest_options(), - gas_options: gas_options.unwrap_or_default(), - prompt_options: PromptOptions::yes(), - ..Default::default() - }, - new_private_key: Some(new_private_key), - save_to_profile: None, - new_private_key_file: None, - skip_saving_profile: true, - } - .execute() - .await?; - - Ok(response) - } - - pub async fn list_account(&self, index: usize, query: ListQuery) -> CliTypedResult> { - ListAccount { - rest_options: self.rest_options(), - profile_options: Default::default(), - account: Some(self.account_id(index)), - query, - } - .execute() - .await - } - - pub async fn transfer_coins( - &self, - sender_index: usize, - receiver_index: usize, - amount: u64, - gas_options: Option, - ) -> CliTypedResult { - TransferCoins { - txn_options: self.transaction_options(sender_index, gas_options), - account: self.account_id(receiver_index), - amount, - } - .execute() - .await - } - - pub async fn transfer_invalid_addr( - &self, - sender_index: usize, - amount: u64, - gas_options: Option, - ) -> CliTypedResult { - RunFunction { - entry_function_args: EntryFunctionArguments { - function_id: Some(MemberId { - module_id: ModuleId::new(AccountAddress::ONE, ident_str!("coin").into()), - member_id: ident_str!("transfer").into(), - }), - arg_vec: ArgWithTypeVec { - args: vec![ - ArgWithType::from_str("address:0xdeadbeefcafebabe").unwrap(), - ArgWithType::from_str(&format!("u64:{}", amount)).unwrap(), - ], - }, - type_arg_vec: TypeArgVec { - type_args: vec![MoveType::Struct(MoveStructTag::new( - AccountAddress::ONE.into(), - ident_str!("aptos_coin").into(), - ident_str!("AptosCoin").into(), - vec![], - ))], - }, - json_file: None, - }, - txn_options: self.transaction_options(sender_index, gas_options), - } - .execute() - .await - } - - pub async fn show_validator_config( - &self, - pool_index: usize, - ) -> CliTypedResult { - ShowValidatorConfig { - rest_options: self.rest_options(), - profile_options: Default::default(), - operator_args: self.operator_args(Some(pool_index)), - } - .execute() - .await - .map(|v| (&v).into()) - } - - pub async fn show_validator_set(&self) -> CliTypedResult { - ShowValidatorSet { - rest_options: self.rest_options(), - profile_options: Default::default(), - } - .execute() - .await - .map(|v| (&v).into()) - } - - pub async fn show_validator_stake(&self, pool_index: usize) -> CliTypedResult { - ShowValidatorStake { - rest_options: self.rest_options(), - profile_options: Default::default(), - operator_args: self.operator_args(Some(pool_index)), - } - .execute() - .await - } - - pub async fn initialize_validator( - &self, - index: usize, - consensus_public_key: bls12381::PublicKey, - proof_of_possession: bls12381::ProofOfPossession, - validator_host: HostAndPort, - validator_network_public_key: x25519::PublicKey, - ) -> CliTypedResult { - InitializeValidator { - txn_options: self.transaction_options(index, None), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_consensus_key_args: ValidatorConsensusKeyArgs { - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(proof_of_possession), - }, - validator_network_addresses_args: ValidatorNetworkAddressesArgs { - validator_host: Some(validator_host), - validator_network_public_key: Some(validator_network_public_key), - full_node_host: None, - full_node_network_public_key: None, - }, - } - .execute() - .await - } - - pub async fn add_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - AddStake { - txn_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn unlock_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - UnlockStake { - txn_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn withdraw_stake( - &self, - index: usize, - amount: u64, - ) -> CliTypedResult> { - WithdrawStake { - node_op_options: self.transaction_options(index, None), - amount, - } - .execute() - .await - } - - pub async fn increase_lockup(&self, index: usize) -> CliTypedResult> { - IncreaseLockup { - txn_options: self.transaction_options(index, None), - } - .execute() - .await - } - - pub async fn join_validator_set( - &self, - operator_index: usize, - pool_index: Option, - ) -> CliTypedResult { - JoinValidatorSet { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - } - .execute() - .await - } - - pub async fn leave_validator_set( - &self, - operator_index: usize, - pool_index: Option, - ) -> CliTypedResult { - LeaveValidatorSet { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - } - .execute() - .await - } - - pub fn add_file_in_package(&self, rel_path: &str, content: String) { - let source_path = self.move_dir().join(rel_path); - write_to_file( - source_path.as_path(), - &source_path.as_display().to_string(), - content.as_bytes(), - ) - .unwrap(); - } - - pub async fn update_validator_network_addresses( - &self, - operator_index: usize, - pool_index: Option, - validator_host: HostAndPort, - validator_network_public_key: x25519::PublicKey, - ) -> CliTypedResult { - UpdateValidatorNetworkAddresses { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_network_addresses_args: ValidatorNetworkAddressesArgs { - validator_host: Some(validator_host), - validator_network_public_key: Some(validator_network_public_key), - full_node_host: None, - full_node_network_public_key: None, - }, - } - .execute() - .await - } - - pub async fn analyze_validator_performance( - &self, - start_epoch: Option, - end_epoch: Option, - ) -> CliTypedResult<()> { - AnalyzeValidatorPerformance { - start_epoch: start_epoch.unwrap_or(-2), - end_epoch, - rest_options: self.rest_options(), - profile_options: Default::default(), - analyze_mode: AnalyzeMode::All, - pool_addresses: vec![], - } - .execute() - .await - } - - pub async fn update_consensus_key( - &self, - operator_index: usize, - pool_index: Option, - consensus_public_key: bls12381::PublicKey, - proof_of_possession: bls12381::ProofOfPossession, - ) -> CliTypedResult { - UpdateConsensusKey { - txn_options: self.transaction_options(operator_index, None), - operator_args: self.operator_args(pool_index), - operator_config_file_args: OperatorConfigFileArgs { - operator_config_file: None, - }, - validator_consensus_key_args: ValidatorConsensusKeyArgs { - consensus_public_key: Some(consensus_public_key), - proof_of_possession: Some(proof_of_possession), - }, - } - .execute() - .await - } - - pub async fn init(&self, private_key: &Ed25519PrivateKey) -> CliTypedResult<()> { - InitTool { - network: Some(Network::Custom), - rest_url: Some(self.endpoint.clone()), - faucet_url: Some(self.faucet_endpoint.clone()), - faucet_auth_token: None, - rng_args: RngArgs::from_seed([0; 32]), - private_key_options: PrivateKeyInputOptions::from_private_key(private_key)?, - profile_options: Default::default(), - prompt_options: PromptOptions::yes(), - encoding_options: EncodingOptions::default(), - skip_faucet: false, - ledger: false, - hardware_wallet_options: Default::default(), - } - .execute() - .await - } - - pub async fn get_pool_address( - &self, - owner_index: usize, - ) -> CliTypedResult> { - GetStakePool { - owner_address: self.account_id(owner_index), - rest_options: self.rest_options(), - profile_options: Default::default(), - } - .execute() - .await - } - - pub async fn initialize_stake_owner( - &self, - owner_index: usize, - initial_stake_amount: u64, - voter_index: Option, - operator_index: Option, - ) -> CliTypedResult { - InitializeStakeOwner { - txn_options: self.transaction_options(owner_index, None), - initial_stake_amount, - operator_address: operator_index.map(|idx| self.account_id(idx)), - voter_address: voter_index.map(|idx| self.account_id(idx)), - } - .execute() - .await - } - - pub async fn create_stake_pool( - &self, - owner_index: usize, - operator_index: usize, - voter_index: usize, - amount: u64, - commission_percentage: u64, - ) -> CliTypedResult { - RunFunction { - entry_function_args: EntryFunctionArguments { - function_id: Some( - MemberId::from_str("0x1::staking_contract::create_staking_contract").unwrap(), - ), - arg_vec: ArgWithTypeVec { - args: vec![ - ArgWithType::address(self.account_id(operator_index)), - ArgWithType::address(self.account_id(voter_index)), - ArgWithType::u64(amount), - ArgWithType::u64(commission_percentage), - ArgWithType::bytes(vec![]), - ], - }, - type_arg_vec: TypeArgVec { type_args: vec![] }, - json_file: None, - }, - txn_options: self.transaction_options(owner_index, None), - } - .execute() - .await - } - - pub async fn set_operator( - &self, - owner_index: usize, - operator_index: usize, - ) -> CliTypedResult> { - SetOperator { - txn_options: self.transaction_options(owner_index, None), - operator_address: self.account_id(operator_index), - } - .execute() - .await - } - - pub async fn set_delegated_voter( - &self, - owner_index: usize, - voter_index: usize, - ) -> CliTypedResult> { - SetDelegatedVoter { - txn_options: self.transaction_options(owner_index, None), - voter_address: self.account_id(voter_index), - } - .execute() - .await - } - - /// Wait for an account to exist - pub async fn wait_for_account(&self, index: usize) -> CliTypedResult> { - let mut result = self.list_account(index, ListQuery::Balance).await; - let start = Instant::now(); - while start.elapsed() < Duration::from_secs(10) { - match result { - Ok(_) => return result, - _ => { - sleep(Duration::from_millis(500)).await; - result = self.list_account(index, ListQuery::Balance).await; - }, - }; - } - - result - } - - pub async fn account_balance_now(&self, index: usize) -> CliTypedResult { - let result = self.list_account(index, ListQuery::Balance).await?; - Ok(json_account_to_balance(result.first().unwrap())) - } - - pub async fn assert_account_balance_now(&self, index: usize, expected: u64) { - let result = self.list_account(index, ListQuery::Balance).await; - assert!( - result.is_ok(), - "Account {} not yet created, {}, last 10 transactions: {}", - self.account_id(index), - result.unwrap_err(), - self.last_n_transactions_details(10).await - ); - let accounts = result.unwrap(); - let account = accounts.first().unwrap(); - let coin = json_account_to_balance(account); - assert_eq!( - coin, - expected, - "Account {} with state: {:?}, last 10 transactions: {}", - self.account_id(index), - account, - self.last_n_transactions_details(10).await - ); - } - - async fn last_n_transactions_details(&self, count: u16) -> String { - let result = aptos_rest_client::Client::new(self.endpoint.clone()) - .get_transactions(None, Some(count)) - .await; - if let Err(e) = result { - return format!("Err({:?})", e); - } - let lines = result - .unwrap() - .inner() - .iter() - .map(|t| { - if let Transaction::UserTransaction(u) = t { - format!( - " * [{}] {}: sender={}, payload={:?}", - t.version().unwrap_or(0), - t.vm_status(), - u.request.sender, - u.request.payload - ) - } else { - format!( - " * [{}] {}: {}", - t.version().unwrap_or(0), - t.vm_status(), - t.type_str() - ) - } - }) - .collect::>(); - format!("\n{}\n", lines.join("\n")) - } - - pub async fn generate_x25519_key( - &self, - output_file: PathBuf, - seed: [u8; 32], - ) -> CliTypedResult> { - GenerateKey { - key_type: KeyType::X25519, - rng_args: RngArgs::from_seed(seed), - save_params: SaveKey { - file_options: SaveFile { - output_file, - prompt_options: PromptOptions::yes(), - }, - encoding_options: Default::default(), - }, - vanity_prefix: None, - vanity_multisig: false, - } - .execute() - .await - } - - pub async fn extract_peer( - &self, - host: HostAndPort, - private_key_file: PathBuf, - output_file: PathBuf, - ) -> CliTypedResult> { - ExtractPeer { - host, - network_key_input_options: NetworkKeyInputOptions::from_private_key_file( - private_key_file, - ), - output_file_options: SaveFile { - output_file, - prompt_options: PromptOptions::yes(), - }, - encoding_options: Default::default(), - } - .execute() - .await - } - - pub fn init_move_dir(&mut self) { - let move_dir = TempPath::new(); - move_dir - .create_as_dir() - .expect("Expected to be able to create move temp dir"); - self.move_dir = Some(move_dir.path().to_path_buf()); - } - - #[cfg(feature = "cli-framework-test-move")] - pub fn add_move_files(&self) { - let move_dir = self.move_dir(); - let sources_dir = move_dir.join("sources"); - - let hello_blockchain_contents = include_str!( - "../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain.move" - ); - let source_path = sources_dir.join("hello_blockchain.move"); - write_to_file( - source_path.as_path(), - &source_path.display().to_string(), - hello_blockchain_contents.as_bytes(), - ) - .unwrap(); - - let hello_blockchain_test_contents = include_str!("../../../../aptos-move/move-examples/hello_blockchain/sources/hello_blockchain_test.move"); - let test_path = sources_dir.join("hello_blockchain_test.move"); - write_to_file( - test_path.as_path(), - &test_path.display().to_string(), - hello_blockchain_test_contents.as_bytes(), - ) - .unwrap(); - } - - pub fn move_dir(&self) -> PathBuf { - assert!(self.move_dir.is_some(), "Must have initialized the temp move directory with `CliTestFramework::init_move_dir()` first"); - self.move_dir.as_ref().cloned().unwrap() - } - - pub async fn init_package( - &self, - name: String, - account_strs: BTreeMap<&str, &str>, - framework_dir: Option, - ) -> CliTypedResult<()> { - InitPackage { - name, - package_dir: Some(self.move_dir()), - named_addresses: Self::move_manifest_named_addresses(account_strs), - prompt_options: PromptOptions { - assume_yes: false, - assume_no: true, - }, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: framework_dir, - skip_fetch_latest_git_deps: false, - }, - } - .execute() - .await - } - - pub async fn compile_package( - &self, - account_strs: BTreeMap<&str, &str>, - included_artifacts: Option, - ) -> CliTypedResult> { - CompilePackage { - move_options: self.move_options(account_strs), - save_metadata: false, - included_artifacts_args: IncludedArtifactsArgs { - included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), - }, - } - .execute() - .await - } - - pub async fn test_package( - &self, - account_strs: BTreeMap<&str, &str>, - filter: Option<&str>, - ) -> CliTypedResult<&'static str> { - TestPackage { - instruction_execution_bound: 100_000, - move_options: self.move_options(account_strs), - filter: filter.map(|str| str.to_string()), - ignore_compile_warnings: false, - compute_coverage: false, - dump_state: false, - } - .execute() - .await - } - - pub async fn publish_package( - &self, - index: usize, - gas_options: Option, - account_strs: BTreeMap<&str, &str>, - included_artifacts: Option, - ) -> CliTypedResult { - PublishPackage { - move_options: self.move_options(account_strs), - txn_options: self.transaction_options(index, gas_options), - override_size_check_option: OverrideSizeCheckOption { value: false }, - included_artifacts_args: IncludedArtifactsArgs { - included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), - }, - } - .execute() - .await - } - - pub async fn download_package( - &self, - index: usize, - package: String, - output_dir: PathBuf, - ) -> CliTypedResult<&'static str> { - DownloadPackage { - rest_options: self.rest_options(), - profile_options: Default::default(), - account: self.account_id(index), - package, - output_dir: Some(output_dir), - print_metadata: false, - bytecode: true, - } - .execute() - .await - } - - pub async fn run_function( - &self, - index: usize, - gas_options: Option, - function_id: MemberId, - args: Vec<&str>, - type_args: Vec<&str>, - ) -> CliTypedResult { - let mut parsed_args = Vec::new(); - for arg in args { - parsed_args.push( - ArgWithType::from_str(arg) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ) - } - - let mut parsed_type_args = Vec::new(); - for arg in type_args { - parsed_type_args.push( - MoveType::from_str(arg) - .map_err(|err| CliError::UnexpectedError(err.to_string()))?, - ) - } - - RunFunction { - entry_function_args: EntryFunctionArguments { - function_id: Some(function_id), - arg_vec: ArgWithTypeVec { args: parsed_args }, - type_arg_vec: TypeArgVec { - type_args: parsed_type_args, - }, - json_file: None, - }, - txn_options: self.transaction_options(index, gas_options), - } - .execute() - .await - } - - /// Runs the given script contents using the local aptos_framework directory. - pub async fn run_script( - &self, - index: usize, - script_contents: &str, - ) -> CliTypedResult { - self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }) - .await - } - - /// Runs the given script contents using the aptos_framework from aptos-core git repository. - pub async fn run_script_with_default_framework( - &self, - index: usize, - script_contents: &str, - ) -> CliTypedResult { - self.run_script_with_framework_package(index, script_contents, FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: None, - skip_fetch_latest_git_deps: false, - }) - .await - } - - /// Runs the given script with the provided framework package arguments - pub async fn run_script_with_framework_package( - &self, - index: usize, - script_contents: &str, - framework_package_args: FrameworkPackageArgs, - ) -> CliTypedResult { - // Make a temporary directory for compilation - let temp_dir = TempDir::new().map_err(|err| { - CliError::UnexpectedError(format!("Failed to create temporary directory {}", err)) - })?; - - let source_path = temp_dir.path().join("script.move"); - write_to_file( - source_path.as_path(), - &source_path.display().to_string(), - script_contents.as_bytes(), - ) - .unwrap(); - - RunScript { - txn_options: self.transaction_options(index, None), - compile_proposal_args: CompileScriptFunction { - script_path: Some(source_path), - compiled_script_path: None, - framework_package_args, - bytecode_version: None, - }, - script_function_args: ScriptFunctionArguments { - type_arg_vec: TypeArgVec { type_args: vec![] }, - arg_vec: ArgWithTypeVec { args: vec![] }, - json_file: None, - }, - } - .execute() - .await - } - - pub async fn run_script_with_script_path( - &self, - index: usize, - script_path: &str, - args: Vec, - type_args: Vec, - ) -> CliTypedResult { - RunScript { - txn_options: self.transaction_options(index, None), - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path.parse().unwrap()), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - script_function_args: ScriptFunctionArguments { - type_arg_vec: TypeArgVec { type_args }, - arg_vec: ArgWithTypeVec { args }, - json_file: None, - }, - } - .execute() - .await - } - - pub fn aptos_framework_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework") - } - - pub fn move_options(&self, account_strs: BTreeMap<&str, &str>) -> MovePackageDir { - MovePackageDir { - dev: true, - package_dir: Some(self.move_dir()), - output_dir: None, - named_addresses: Self::named_addresses(account_strs), - skip_fetch_latest_git_deps: true, - bytecode_version: None, - compiler_version: None, - skip_attribute_checks: false, - check_test_code: false, - } - } - - pub fn move_manifest_named_addresses( - account_strs: BTreeMap<&str, &str>, - ) -> BTreeMap { - account_strs - .iter() - .map(|(key, value)| { - ( - key.to_string(), - MoveManifestAccountWrapper::from_str(value).unwrap(), - ) - }) - .collect() - } - - pub fn named_addresses( - account_strs: BTreeMap<&str, &str>, - ) -> BTreeMap { - account_strs - .iter() - .map(|(key, value)| { - ( - key.to_string(), - AccountAddressWrapper::from_str(value).unwrap(), - ) - }) - .collect() - } - - pub fn rest_options(&self) -> RestOptions { - RestOptions::new(Some(self.endpoint.clone()), None) - } - - pub fn faucet_options(&self) -> FaucetOptions { - FaucetOptions::new(Some(self.faucet_endpoint.clone()), None) - } - - fn transaction_options( - &self, - index: usize, - gas_options: Option, - ) -> TransactionOptions { - TransactionOptions { - private_key_options: PrivateKeyInputOptions::from_private_key(self.private_key(index)) - .unwrap(), - sender_account: Some(self.account_id(index)), - rest_options: self.rest_options(), - gas_options: gas_options.unwrap_or_default(), - prompt_options: PromptOptions::yes(), - ..Default::default() - } - } - - fn operator_args(&self, pool_index: Option) -> OperatorArgs { - OperatorArgs { - pool_address_args: OptionalPoolAddressArgs { - pool_address: pool_index.map(|idx| self.account_id(idx)), - }, - } - } - - pub fn private_key(&self, index: usize) -> &Ed25519PrivateKey { - self.account_keys.get(index).unwrap() - } - - pub fn set_private_key( - &mut self, - index: usize, - new_key: Ed25519PrivateKey, - ) -> Ed25519PrivateKey { - // Insert the new private key into the test framework, returning the old one - mem::replace(&mut self.account_keys[index], new_key) - } - - pub fn account_id(&self, index: usize) -> AccountAddress { - *self.account_addresses.get(index).unwrap() - } - - pub async fn create_proposal( - &mut self, - index: usize, - metadata_url: &str, - script_path: PathBuf, - pool_address: AccountAddress, - is_multi_step: bool, - ) -> CliTypedResult { - SubmitProposal { - pool_address_args: PoolAddressArgs { pool_address }, - args: SubmitProposalArgs { - #[cfg(feature = "no-upload-proposal")] - metadata_path: None, - metadata_url: Url::parse(metadata_url).unwrap(), - txn_options: self.transaction_options(index, None), - is_multi_step, - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - }, - } - .execute() - .await - } - - pub async fn vote( - &self, - index: usize, - proposal_id: u64, - yes: bool, - no: bool, - pool_addresses: Vec, - ) { - SubmitVote { - pool_addresses, - args: SubmitVoteArgs { - proposal_id, - yes, - no, - voting_power: None, - txn_options: self.transaction_options(index, None), - }, - } - .execute() - .await - .expect("Successfully voted."); - } - - pub async fn verify_proposal( - &self, - proposal_id: u64, - script_path: &str, - ) -> CliTypedResult { - VerifyProposal { - proposal_id, - compile_proposal_args: CompileScriptFunction { - script_path: Some(script_path.parse().unwrap()), - compiled_script_path: None, - framework_package_args: FrameworkPackageArgs { - framework_git_rev: None, - framework_local_dir: Some(Self::aptos_framework_dir()), - skip_fetch_latest_git_deps: false, - }, - bytecode_version: None, - }, - rest_options: self.rest_options(), - profile: Default::default(), - prompt_options: PromptOptions::yes(), - } - .execute() - .await - } -} - -// ValidatorConfig/ValidatorSet doesn't match Move ValidatorSet struct, -// and json is serialized with different types from both, so hardcoding deserialization. - -fn json_account_to_balance(value: &Value) -> u64 { - u64::from_str( - value - .as_object() - .unwrap() - .get("coin") - .unwrap() - .as_object() - .unwrap() - .get("value") - .unwrap() - .as_str() - .unwrap(), - ) - .unwrap() -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct IndividualValidatorPerformance { - successful_proposals: String, - failed_proposals: String, -} - -impl IndividualValidatorPerformance { - pub fn successful_proposals(&self) -> u32 { - self.successful_proposals.parse().unwrap() - } - - pub fn failed_proposals(&self) -> u32 { - self.failed_proposals.parse().unwrap() - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ValidatorPerformance { - pub validators: Vec, -} diff --git a/crates/aptos/src/test/tests.rs b/crates/aptos/src/test/tests.rs deleted file mode 100644 index 006f5e15c3218..0000000000000 --- a/crates/aptos/src/test/tests.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - move_tool::{ArgWithType, FunctionArgType}, - CliResult, Tool, -}; -use clap::Parser; -use std::str::FromStr; - -/// In order to ensure that there aren't duplicate input arguments for untested CLI commands, -/// we call help on every command to ensure it at least runs -#[tokio::test] -async fn ensure_every_command_args_work() { - assert_cmd_not_panic(&["aptos"]).await; - - assert_cmd_not_panic(&["aptos", "account"]).await; - assert_cmd_not_panic(&["aptos", "account", "create", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "create-resource-account", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "fund-with-faucet", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "list", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "lookup-address", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "rotate-key", "--help"]).await; - assert_cmd_not_panic(&["aptos", "account", "transfer", "--help"]).await; - - assert_cmd_not_panic(&["aptos", "config"]).await; - assert_cmd_not_panic(&["aptos", "config", "generate-shell-completions", "--help"]).await; - assert_cmd_not_panic(&["aptos", "config", "init", "--help"]).await; - assert_cmd_not_panic(&["aptos", "config", "set-global-config", "--help"]).await; - assert_cmd_not_panic(&["aptos", "config", "show-global-config"]).await; - assert_cmd_not_panic(&["aptos", "config", "show-profiles"]).await; - - assert_cmd_not_panic(&["aptos", "genesis"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "generate-genesis", "--help"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "generate-keys", "--help"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "generate-layout-template", "--help"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "set-validator-configuration", "--help"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "setup-git", "--help"]).await; - assert_cmd_not_panic(&["aptos", "genesis", "generate-admin-write-set", "--help"]).await; - - assert_cmd_not_panic(&["aptos", "governance"]).await; - assert_cmd_not_panic(&["aptos", "governance", "execute-proposal", "--help"]).await; - assert_cmd_not_panic(&["aptos", "governance", "generate-upgrade-proposal", "--help"]).await; - assert_cmd_not_panic(&["aptos", "governance", "propose", "--help"]).await; - assert_cmd_not_panic(&["aptos", "governance", "vote", "--help"]).await; - assert_cmd_not_panic(&["aptos", "governance", "delegation_pool", "--help"]).await; - assert_cmd_not_panic(&["aptos", "governance", "delegation_pool", "vote", "--help"]).await; - assert_cmd_not_panic(&[ - "aptos", - "governance", - "delegation_pool", - "propose", - "--help", - ]) - .await; - - assert_cmd_not_panic(&["aptos", "info"]).await; - - assert_cmd_not_panic(&["aptos", "init", "--help"]).await; - - assert_cmd_not_panic(&["aptos", "key"]).await; - assert_cmd_not_panic(&["aptos", "key", "generate", "--help"]).await; - assert_cmd_not_panic(&["aptos", "key", "extract-peer", "--help"]).await; - - assert_cmd_not_panic(&["aptos", "move"]).await; - assert_cmd_not_panic(&["aptos", "move", "clean", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "compile", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "compile-script", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "decompile", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "disassemble", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "download", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "init", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "list", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "prove", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "publish", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "run", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "run-script", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "test", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "transactional-test", "--help"]).await; - assert_cmd_not_panic(&["aptos", "move", "view", "--help"]).await; - - assert_cmd_not_panic(&["aptos", "node"]).await; - assert_cmd_not_panic(&["aptos", "node", "check-network-connectivity", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "get-stake-pool", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "analyze-validator-performance", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "bootstrap-db-from-backup", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "initialize-validator", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "join-validator-set", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "leave-validator-set", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "run-local-testnet", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "show-validator-config", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "show-validator-set", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "show-validator-stake", "--help"]).await; - assert_cmd_not_panic(&["aptos", "node", "update-consensus-key", "--help"]).await; - assert_cmd_not_panic(&[ - "aptos", - "node", - "update-validator-network-addresses", - "--help", - ]) - .await; - - assert_cmd_not_panic(&["aptos", "stake"]).await; - assert_cmd_not_panic(&["aptos", "stake", "add-stake", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "increase-lockup", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "initialize-stake-owner", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "set-delegated-voter", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "set-operator", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "unlock-stake", "--help"]).await; - assert_cmd_not_panic(&["aptos", "stake", "withdraw-stake", "--help"]).await; -} - -/// Ensure we can parse URLs for args -#[tokio::test] -async fn ensure_can_parse_args_with_urls() { - let result = ArgWithType::from_str("string:https://aptoslabs.com").unwrap(); - matches!(result._ty, FunctionArgType::String); - assert_eq!( - result.arg, - bcs::to_bytes(&"https://aptoslabs.com".to_string()).unwrap() - ); -} - -async fn assert_cmd_not_panic(args: &[&str]) { - // When a command fails, it will have a panic in it due to an improperly setup command - // thread 'main' panicked at 'Command propose: Argument names must be unique, but 'assume-yes' is - // in use by more than one argument or group', ... - - match run_cmd(args).await { - Ok(inner) => assert!( - !inner.contains("panic"), - "Failed to not panic cmd {}: {}", - args.join(" "), - inner - ), - Err(inner) => assert!( - !inner.contains("panic"), - "Failed to not panic cmd {}: {}", - args.join(" "), - inner - ), - } -} - -async fn run_cmd(args: &[&str]) -> CliResult { - let tool: Tool = Tool::try_parse_from(args).map_err(|msg| msg.to_string())?; - tool.execute().await -} diff --git a/crates/aptos/src/update/aptos.rs b/crates/aptos/src/update/aptos.rs deleted file mode 100644 index fe8f5881f33b3..0000000000000 --- a/crates/aptos/src/update/aptos.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -// Out of the box the self_update crate assumes that you have releases named a -// specific way with the crate name, version, and target triple in a specific -// format. We don't do this with our releases, we have other GitHub releases beyond -// just the CLI, and we don't build for all major target triples, so we have to do -// some of the work ourselves first to figure out what the latest version of the -// CLI is and which binary to download based on the current OS. Then we can plug -// that into the library which takes care of the rest. - -use super::{BinaryUpdater, UpdateRequiredInfo}; -use crate::common::{ - types::{CliCommand, CliTypedResult}, - utils::cli_build_information, -}; -use anyhow::{anyhow, Context, Result}; -use aptos_build_info::BUILD_OS; -use async_trait::async_trait; -use clap::Parser; -use self_update::{ - backends::github::{ReleaseList, Update}, - cargo_crate_version, - update::ReleaseUpdate, -}; -use std::process::Command; - -/// Update the CLI itself -/// -/// This can be used to update the CLI to the latest version. This is useful if you -/// installed the CLI via the install script / by downloading the binary directly. -#[derive(Debug, Parser)] -pub struct AptosUpdateTool { - /// The owner of the repo to download the binary from. - #[clap(long, default_value = "aptos-labs")] - repo_owner: String, - - /// The name of the repo to download the binary from. - #[clap(long, default_value = "aptos-core")] - repo_name: String, -} - -impl BinaryUpdater for AptosUpdateTool { - fn pretty_name(&self) -> &'static str { - "Aptos CLI" - } - - /// Return information about whether an update is required. - fn get_update_info(&self) -> Result { - // Build a configuration for determining the latest release. - let config = ReleaseList::configure() - .repo_owner(&self.repo_owner) - .repo_name(&self.repo_name) - .build() - .map_err(|e| anyhow!("Failed to build configuration to fetch releases: {:#}", e))?; - - // Get the most recent releases. - let releases = config - .fetch() - .map_err(|e| anyhow!("Failed to fetch releases: {:#}", e))?; - - // Find the latest release of the CLI, in which we filter for the CLI tag. - // If the release isn't in the last 30 items (the default API page size) - // this will fail. See https://github.com/aptos-labs/aptos-core/issues/6411. - let mut releases = releases.into_iter(); - let latest_release = loop { - let release = match releases.next() { - Some(release) => release, - None => return Err(anyhow!("Failed to find latest CLI release")), - }; - if release.version.starts_with("aptos-cli-") { - break release; - } - }; - let target_version = latest_release.version.split("-v").last().unwrap(); - - // Return early if we're up to date already. - let current_version = cargo_crate_version!(); - - Ok(UpdateRequiredInfo { - current_version: Some(current_version.to_string()), - target_version: target_version.to_string(), - }) - } - - fn build_updater(&self, info: &UpdateRequiredInfo) -> Result> { - let installation_method = - InstallationMethod::from_env().context("Failed to determine installation method")?; - match installation_method { - InstallationMethod::Source => { - return Err(anyhow!( - "Detected this CLI was built from source, refusing to update" - )); - }, - InstallationMethod::Homebrew => { - return Err(anyhow!( - "Detected this CLI comes from homebrew, use `brew upgrade aptos` instead" - )); - }, - InstallationMethod::Other => {}, - } - - // Determine the target we should download. This is necessary because we don't - // name our binary releases using the target triples nor do we build specifically - // for all major triples, so we have to generalize to one of the binaries we do - // happen to build. We figure this out based on what system the CLI was built on. - let build_info = cli_build_information(); - let target = match build_info.get(BUILD_OS).context("Failed to determine build info of current CLI")?.as_str() { - "linux-x86_64" => { - // In the case of Linux, which build to use depends on the OpenSSL - // library on the host machine. So we try to determine that here. - // This code below parses the output of the `openssl version` command, - // where the version string is the 1th (0-indexing) item in the string - // when split by whitespace. - let output = Command::new("openssl") - .args(["version"]) - .output(); - let version = match output { - Ok(output) => { - let stdout = String::from_utf8(output.stdout).unwrap(); - stdout.split_whitespace().collect::>()[1].to_string() - }, - Err(e) => { - println!("Failed to determine OpenSSL version, assuming an older version: {:#}", e); - "1.0.0".to_string() - } - }; - // On Ubuntu < 22.04 the bundled OpenSSL is version 1.x.x, whereas on - // 22.04+ it is 3.x.x. Unfortunately if you build the CLI on a system - // with one major version of OpenSSL, you cannot use it on a system - // with a different version. Accordingly, if the current system uses - // OpenSSL 3.x.x, we use the version of the CLI built on a system with - // OpenSSL 3.x.x, meaning Ubuntu 22.04. Otherwise we use the one built - // on 20.04. - if version.starts_with('3') { - "Ubuntu-22.04-x86_64" - } else { - "Ubuntu-x86_64" - } - }, - "macos-x86_64" => "MacOSX-x86_64", - "windows-x86_64" => "Windows-x86_64", - wildcard => return Err(anyhow!("Self-updating is not supported on your OS ({}) right now, please download the binary manually", wildcard)), - }; - - let current_version = match &info.current_version { - Some(version) => version, - None => unreachable!("current_version should always be Some at this point"), - }; - - // Build a new configuration that will direct the library to download the - // binary with the target version tag and target that we determined above. - Update::configure() - .repo_owner(&self.repo_owner) - .repo_name(&self.repo_name) - .bin_name("aptos") - .current_version(current_version) - .target_version_tag(&format!("aptos-cli-v{}", info.target_version)) - .target(target) - .build() - .map_err(|e| anyhow!("Failed to build self-update configuration: {:#}", e)) - } -} - -pub enum InstallationMethod { - Source, - Homebrew, - Other, -} - -impl InstallationMethod { - pub fn from_env() -> Result { - // Determine update instructions based on what we detect about the installation. - let exe_path = std::env::current_exe()?; - let installation_method = if exe_path.to_string_lossy().contains("brew") { - InstallationMethod::Homebrew - } else if exe_path.to_string_lossy().contains("target") { - InstallationMethod::Source - } else { - InstallationMethod::Other - }; - Ok(installation_method) - } -} - -#[async_trait] -impl CliCommand for AptosUpdateTool { - fn command_name(&self) -> &'static str { - "UpdateAptos" - } - - async fn execute(self) -> CliTypedResult { - tokio::task::spawn_blocking(move || self.update()) - .await - .context("Failed to self-update Aptos CLI")? - } -} diff --git a/crates/aptos/src/update/helpers.rs b/crates/aptos/src/update/helpers.rs deleted file mode 100644 index 1bcfa27fc6cfd..0000000000000 --- a/crates/aptos/src/update/helpers.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use std::path::PathBuf; - -/// Some functionality of the Aptos CLI relies on some additional binaries. This is -/// where we install them by default. These paths align with the installation script, -/// which is generally how the Linux and Windows users install the CLI. -pub fn get_additional_binaries_dir() -> PathBuf { - #[cfg(windows)] - { - let home_dir = std::env::var("USERPROFILE").unwrap_or_else(|_| "".into()); - PathBuf::from(home_dir).join(".aptoscli/bin") - } - - #[cfg(not(windows))] - { - let home_dir = std::env::var("HOME").unwrap_or_else(|_| "".into()); - PathBuf::from(home_dir).join(".local/bin") - } -} diff --git a/crates/aptos/src/update/mod.rs b/crates/aptos/src/update/mod.rs deleted file mode 100644 index 560fd22dbb8d8..0000000000000 --- a/crates/aptos/src/update/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -// Note: We make use of the self_update crate, but as you can see in the case of -// Revela, this can also be used to install / update other binaries. - -mod aptos; -mod helpers; -mod revela; -mod tool; - -use crate::common::types::CliTypedResult; -use anyhow::{anyhow, Context, Result}; -pub use helpers::get_additional_binaries_dir; -pub use revela::get_revela_path; -use self_update::{update::ReleaseUpdate, version::bump_is_greater, Status}; -pub use tool::UpdateTool; - -/// Things that implement this trait are able to update a binary. -trait BinaryUpdater { - /// Only used for messages we print to the user. - fn pretty_name(&self) -> &'static str; - - /// Return information about whether an update is required. - fn get_update_info(&self) -> Result; - - /// Build the updater from the self_update crate. - fn build_updater(&self, info: &UpdateRequiredInfo) -> Result>; - - /// Update the binary. Install if not present, in the case of additional binaries - /// such as Revela. - fn update(&self) -> CliTypedResult { - // Confirm that we need to update. - let info = self - .get_update_info() - .context("Failed to check if we need to update")?; - if !info.update_required()? { - return Ok(format!("Already up to date (v{})", info.target_version)); - } - - // Build the updater. - let updater = self.build_updater(&info)?; - - // Update the binary. - let result = updater - .update() - .map_err(|e| anyhow!("Failed to update {}: {:#}", self.pretty_name(), e))?; - - let message = match result { - Status::UpToDate(_) => unreachable!("We should have caught this already"), - Status::Updated(_) => match info.current_version { - Some(current_version) => format!( - "Successfully updated {} from v{} to v{}", - self.pretty_name(), - current_version, - info.target_version - ), - None => { - format!( - "Successfully installed {} v{}", - self.pretty_name(), - info.target_version - ) - }, - }, - }; - - Ok(message) - } -} - -/// Information used to determine if an update is required. The versions given to this -/// struct should not have any prefix, it should just be the version. e.g. 2.5.0 rather -/// than aptos-cli-v2.5.0. -#[derive(Debug)] -pub struct UpdateRequiredInfo { - pub current_version: Option, - pub target_version: String, -} - -impl UpdateRequiredInfo { - pub fn update_required(&self) -> Result { - match self.current_version { - Some(ref current_version) => bump_is_greater(current_version, &self.target_version) - .context( - "Failed to compare current and latest CLI versions, please update manually", - ), - None => Ok(true), - } - } -} diff --git a/crates/aptos/src/update/revela.rs b/crates/aptos/src/update/revela.rs deleted file mode 100644 index 3612ec5db0940..0000000000000 --- a/crates/aptos/src/update/revela.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{get_additional_binaries_dir, BinaryUpdater, UpdateRequiredInfo}; -use crate::common::{ - types::{CliCommand, CliTypedResult}, - utils::cli_build_information, -}; -use anyhow::{anyhow, bail, Context, Result}; -use aptos_build_info::BUILD_OS; -use async_trait::async_trait; -use clap::Parser; -use self_update::{backends::github::Update, update::ReleaseUpdate}; -use std::path::PathBuf; - -const REVELA_BINARY_NAME: &str = "revela"; -const TARGET_REVELA_VERSION: &str = "1.0.0"; - -const REVELA_EXE_ENV: &str = "REVELA_EXE"; -#[cfg(target_os = "windows")] -const REVELA_EXE: &str = "revela.exe"; -#[cfg(not(target_os = "windows"))] -const REVELA_EXE: &str = "revela"; - -/// Update Revela, the tool used for decompilation. -#[derive(Debug, Parser)] -pub struct RevelaUpdateTool { - /// The owner of the repo to download the binary from. - #[clap(long, default_value = "verichains")] - repo_owner: String, - - /// The name of the repo to download the binary from. - #[clap(long, default_value = "revela")] - repo_name: String, - - /// The version to install, e.g. 1.0.1. Use with caution, the default value is a - /// version that is tested for compatibility with the version of the CLI you are - /// using. - #[clap(long, default_value = TARGET_REVELA_VERSION)] - target_version: String, - - /// Where to install the binary. Make sure this directory is on your PATH. If not - /// given we will put it in a standard location for your OS that the CLI will use - /// later when the tool is required. - #[clap(long)] - install_dir: Option, -} - -impl BinaryUpdater for RevelaUpdateTool { - fn pretty_name(&self) -> &'static str { - "Revela" - } - - /// Return information about whether an update is required. - fn get_update_info(&self) -> Result { - // Get the current version, if any. - let revela_path = get_revela_path(); - let current_version = match revela_path { - Ok(path) => { - let output = std::process::Command::new(path) - .arg("--version") - .output() - .context("Failed to get current version of Revela")?; - let stdout = String::from_utf8(output.stdout) - .context("Failed to parse current version of Revela as UTF-8")?; - let current_version = stdout - .split_whitespace() - .nth(1) - .map(|s| s.to_string()) - .context("Failed to extract version number from command output")?; - Some(current_version.trim_start_matches('v').to_string()) - }, - Err(_) => None, - }; - - // Strip v prefix from target version if present. - let target_version = self.target_version.trim_start_matches('v').to_string(); - - Ok(UpdateRequiredInfo { - current_version, - target_version, - }) - } - - fn build_updater(&self, info: &UpdateRequiredInfo) -> Result> { - // Determine the target we should download based on how the CLI itself was built. - let arch_str = get_arch(); - let build_info = cli_build_information(); - let target = match build_info.get(BUILD_OS).context("Failed to determine build info of current CLI")?.as_str() { - "linux-aarch64" | "linux-x86_64" => "unknown-linux-gnu", - "macos-aarch64" | "macos-x86" => "apple-darwin", - "windows-x86_64" => "pc-windows-gnu", - wildcard => bail!("Self-updating is not supported on your OS ({}) right now, please download the binary manually", wildcard), - }; - - let target = format!("{}-{}", arch_str, target); - - let install_dir = match self.install_dir.clone() { - Some(dir) => dir, - None => { - let dir = get_additional_binaries_dir(); - // Make the directory if it doesn't already exist. - std::fs::create_dir_all(&dir) - .with_context(|| format!("Failed to create directory: {:?}", dir))?; - dir - }, - }; - - let current_version = match &info.current_version { - Some(version) => version, - None => "0.0.0", - }; - - Update::configure() - .bin_install_dir(install_dir) - .bin_name(REVELA_BINARY_NAME) - .repo_owner(&self.repo_owner) - .repo_name(&self.repo_name) - .current_version(current_version) - .target_version_tag(&format!("v{}", info.target_version)) - .target(&target) - .build() - .map_err(|e| anyhow!("Failed to build self-update configuration: {:#}", e)) - } -} - -#[async_trait] -impl CliCommand for RevelaUpdateTool { - fn command_name(&self) -> &'static str { - "UpdateRevela" - } - - async fn execute(self) -> CliTypedResult { - tokio::task::spawn_blocking(move || self.update()) - .await - .context("Failed to install / update Revela")? - } -} - -#[cfg(target_arch = "x86_64")] -fn get_arch() -> &'static str { - "x86_64" -} - -#[cfg(target_arch = "aarch64")] -fn get_arch() -> &'static str { - "aarch64" -} - -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] -fn get_arch() -> &'static str { - unimplemented!("Self-updating is not supported on your CPU architecture right now, please download the binary manually") -} - -pub fn get_revela_path() -> Result { - // Look at the environment variable first. - if let Ok(path) = std::env::var(REVELA_EXE_ENV) { - return Ok(PathBuf::from(path)); - } - - // See if it is present in the path where we usually install additional binaries. - let path = get_additional_binaries_dir().join(REVELA_BINARY_NAME); - if path.exists() && path.is_file() { - return Ok(path); - } - - // See if we can find the binary in the PATH. - if let Some(path) = pathsearch::find_executable_in_path(REVELA_EXE) { - return Ok(path); - } - - Err(anyhow!( - "Cannot locate the decompiler executable. \ - Environment variable `{}` is not set, and `{}` is not in the PATH. \ - Try running `aptos update revela` to download it.", - REVELA_EXE_ENV, - REVELA_EXE - )) -} diff --git a/crates/aptos/src/update/tool.rs b/crates/aptos/src/update/tool.rs deleted file mode 100644 index 60c0c2a5e6b08..0000000000000 --- a/crates/aptos/src/update/tool.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{aptos::AptosUpdateTool, revela::RevelaUpdateTool}; -use crate::common::types::{CliCommand, CliResult}; -use clap::Subcommand; - -/// Update the CLI or other tools it depends on. -#[derive(Subcommand)] -pub enum UpdateTool { - Aptos(AptosUpdateTool), - Revela(RevelaUpdateTool), -} - -impl UpdateTool { - pub async fn execute(self) -> CliResult { - match self { - UpdateTool::Aptos(tool) => tool.execute_serialized().await, - UpdateTool::Revela(tool) => tool.execute_serialized().await, - } - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/Cargo.toml b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/Cargo.toml deleted file mode 100644 index a411a9740e6e4..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "aptos-indexer-grpc-integration-tests" -version = "0.1.0" -edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = { workspace = true } -aptos-config = { workspace = true } -aptos-indexer-grpc-cache-worker = { workspace = true } -aptos-indexer-grpc-data-service = { workspace = true } -aptos-indexer-grpc-file-store = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-indexer-grpc-utils = { workspace = true } -aptos-inspection-service = { workspace = true } -aptos-logger = { workspace = true } -aptos-protos = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-transaction-emitter-lib = { workspace = true } -aptos-transaction-generator-lib = { workspace = true } -aptos-types = { workspace = true } -async-trait = { workspace = true } -backoff = { workspace = true } -base64 = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -futures-core = { workspace = true } -futures-util = { workspace = true } -itertools = { workspace = true } -once_cell = { workspace = true } -prometheus = { workspace = true } -prost = { workspace = true } -redis = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -tempfile = { workspace = true } -tokio = { workspace = true } -tonic = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } -warp = { workspace = true } - -[features] -integration-tests = [] diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs deleted file mode 100644 index 41eeab05b7c79..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -// We hide these tests behind a feature flag because these are not standard unit tests, -// these are integration tests that rely on a variety of outside pieces such as a local -// testnet and a running Redis instance. -#[cfg(feature = "integration-tests")] -mod tests; diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/fullnode_tests.rs b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/fullnode_tests.rs deleted file mode 100644 index ec2a88c23b0e4..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/fullnode_tests.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright © Aptos Foundation - -use anyhow::{bail, Context, Result}; -use aptos_indexer_grpc_cache_worker::IndexerGrpcCacheWorkerConfig; -use aptos_indexer_grpc_file_store::IndexerGrpcFileStoreWorkerConfig; -use aptos_indexer_grpc_server_framework::{ - run_server_with_config, setup_logging, setup_panic_handler, GenericConfig, RunnableConfig, -}; -use aptos_indexer_grpc_utils::{ - cache_operator::CacheOperator, - config::{IndexerGrpcFileStoreConfig, LocalFileStore}, - constants::BLOB_STORAGE_SIZE, - file_store_operator::{FileStoreOperator, LocalFileStoreOperator}, - types::RedisUrl, -}; -use aptos_transaction_emitter_lib::{emit_transactions, ClusterArgs, CoinSourceArgs, EmitArgs}; -use aptos_transaction_generator_lib::args::TransactionTypeArg; -use aptos_types::chain_id::ChainId; -use once_cell::sync::Lazy; -use regex::Regex; -use std::{fs::File, io::Write, path::PathBuf, str::FromStr}; -use tempfile::TempDir; -use tokio::task::JoinHandle; -use tracing::info; -use url::Url; - -static TESTNET_REST_API_URL: Lazy = - Lazy::new(|| Url::from_str("http://127.0.0.1:8080").unwrap()); -static TESTNET_FULLNODE_GRPC_URL: Lazy = - Lazy::new(|| Url::from_str("http://127.0.0.1:50051").unwrap()); -static REDIS_PRIMARY_URL: Lazy = - Lazy::new(|| RedisUrl::from_str("redis://127.0.0.1:6379").unwrap()); - -static MINT_KEY_FILE_NAME: &str = "mint.key"; - -/// Get the name of docker containers that match the given regex -/// This works around different docker compose v1 and v2 naming conventions -fn get_container_by_name_regex(name_regex: Regex) -> Result> { - let containers = std::process::Command::new("docker") - .args(&["ps", "--format", "{{.Names}}"]) - .output()?; - let containers = String::from_utf8(containers.stdout)?; - let ret = containers - .split("\n") - .map(|x| x.to_string()) - .filter(|x| name_regex.is_match(x)) - .collect::>(); - Ok(ret) -} - -/// Connects to the local redis running in docker and resets it -async fn reset_redis() -> Result<()> { - let redis_containers = get_container_by_name_regex(Regex::new(r".*redis.*")?)?; - for container in redis_containers { - let _ = std::process::Command::new("docker") - .args(&["exec", &container, "redis-cli", "FLUSHALL"]) - .output()?; - } - - let conn = redis::Client::open(REDIS_PRIMARY_URL.0.clone()) - .expect("Create redis client failed.") - .get_async_connection() - .await - .expect("Create redis connection failed."); - let mut cache_operator = CacheOperator::new(conn); - match cache_operator.get_latest_version().await { - Ok(x) => { - bail!( - "Redis did not scale down properly. There's still stuff in the cache. Latest version: {}", - x - ); - }, - Err(_) => info!("Redis scaled down properly"), - } - Ok(()) -} - -/// Fetch the mint key from the running local testnet and dump it into the path specified -async fn dump_mint_key_to_file(path: &PathBuf) -> Result { - let validator_containers = - get_container_by_name_regex(Regex::new(r"validator-testnet.*validator.*")?)?; - if validator_containers.len() != 1 { - bail!( - "Expected 1 validator container, found {}", - validator_containers.len() - ); - } - let validator = &validator_containers[0]; - let output = std::process::Command::new("docker") - .args(&["exec", validator, "cat", "/opt/aptos/var/mint.key"]) - .output()?; - let output_stdout = output.stdout; - info!("Mint key: {:?}", output_stdout); - let mint_key_path = path.join(MINT_KEY_FILE_NAME); - let mint_key_path_string = mint_key_path.display().to_string(); - let mut file = File::create(mint_key_path).context("Could not create mint key in path")?; - file.write_all(&output_stdout) - .context("Could not write mint key to file")?; - Ok(mint_key_path_string) -} - -/// Emit transactions to the local testnet to invoke certain indexer actions, such as writing -/// to filestore -async fn emit_transactions_for_test() -> Result<()> { - // dump the key to a tempfile - let path_buf = TempDir::new() - .context("Could not create temp dir")? - .into_path(); - let mint_key_file_path = dump_mint_key_to_file(&path_buf) - .await - .expect("Failed to fetch mint key"); - info!("Mint key file path: {}", mint_key_file_path); - - // emit some transactions - let duration = 10; - let target_tps = BLOB_STORAGE_SIZE / duration; - let cluster_args = ClusterArgs { - targets: Some(vec![(*TESTNET_REST_API_URL).clone()]), - targets_file: None, - reuse_accounts: false, - chain_id: ChainId::test(), - coin_source_args: CoinSourceArgs { - mint_file: Some(mint_key_file_path), - ..CoinSourceArgs::default() - }, - }; - let emit_args = EmitArgs { - // mempool_backlog: None, - target_tps: Some(target_tps), - txn_expiration_time_secs: 30, - duration: duration.try_into().unwrap(), - transaction_type: vec![TransactionTypeArg::default()], - ..EmitArgs::default() - }; - - info!( - "Emitting transactions: {} tps for {} seconds...", - target_tps, duration - ); - - let stats = emit_transactions(&cluster_args, &emit_args) - .await - .map_err(|e| panic!("Emit transactions failed {:?}", e)) - .unwrap(); - info!("Total stats: {}", stats); - info!("Average rate: {}", stats.rate()); - Ok(()) -} - -async fn start_server( - server_config: T, -) -> Result<(u16, JoinHandle>)> { - let health_check_port = aptos_config::utils::get_available_port(); - let config = GenericConfig { - health_check_port, - server_config, - }; - let server_name = config.server_config.get_server_name(); - info!( - "Starting server {} with healtheck port {}", - server_name, health_check_port - ); - let runtime_handle = tokio::runtime::Handle::current(); - - // runs the component's run, but we need the server run - let join_handle = runtime_handle.spawn(async move { run_server_with_config(config).await }); - let startup_timeout_secs = 30; - for i in 0..startup_timeout_secs { - match reqwest::get(format!("http://localhost:{}/metrics", health_check_port)).await { - Ok(_) => break, - Err(e) => { - if i == startup_timeout_secs - 1 { - let msg = if join_handle.is_finished() { - format!("Server failed on startup: {:#?}", join_handle.await) - } else { - "Server was still starting up".to_string() - }; - bail!( - "Server didn't come up within given timeout: {:#?} {}", - e, - msg - ); - } - }, - } - if join_handle.is_finished() { - bail!( - "Server returned error while starting up: {:#?}", - join_handle.await - ); - } - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - Ok((health_check_port, join_handle)) -} - -async fn setup_test() { - // we're going to run both cache worker and file store worker in the same process - // so we need to centrally set up logging and panic handler, whereas they are usually done in the same service - setup_logging(); - setup_panic_handler(); - reset_redis().await.expect("Failed to reset redis for test"); - - // aptos_logger too - aptos_logger::Logger::init_for_testing(); -} - -// These tests expect that the local environment has a running fullnode -// This can be done by using the docker-compose -// We will then simulate chaos by using (1) docker exec (2) docker-compose scale = -#[tokio::test] -pub async fn verify_docker_compose_setup() { - let url = format!("{}v1", *TESTNET_REST_API_URL); - reqwest::get(&url) - .await - .unwrap() - .error_for_status() - .unwrap(); // we just want a good status code -} - -/// Test that the cache worker can start from scratch and make progress. -/// This is a cold start because there is no existing cache and also it is unable to read from the file store -/// about the latest state prior to starting. -#[tokio::test] -async fn test_cold_start_cache_worker_progress() { - setup_test().await; - - let tmp_dir = TempDir::new().expect("Could not create temp dir"); // start with a new file store each time - let cache_worker_config = IndexerGrpcCacheWorkerConfig { - fullnode_grpc_address: (*TESTNET_FULLNODE_GRPC_URL).clone(), - file_store_config: IndexerGrpcFileStoreConfig::LocalFileStore(LocalFileStore { - local_file_store_path: tmp_dir.path().to_path_buf(), - }), - redis_main_instance_address: (*REDIS_PRIMARY_URL).clone(), - }; - - let (_cache_worker_port, _cache_worker_handle) = - start_server::(cache_worker_config) - .await - .expect("Failed to start CacheWorker"); - - let conn = redis::Client::open((*REDIS_PRIMARY_URL).0.clone()) - .expect("Create redis client failed.") - .get_async_connection() - .await - .expect("Create redis connection failed."); - - let check_cache_secs = 30; - let check_cache_frequency_secs = 5; - let tries = check_cache_secs / check_cache_frequency_secs; - - // check that the cache was written to - let mut cache_operator = CacheOperator::new(conn); - let mut chain_id = 0; - for _ in 0..tries { - match cache_operator.get_chain_id().await { - Ok(x) => { - chain_id = x; - info!("Chain id: {}", x); - break; - }, - Err(_) => { - tokio::time::sleep(std::time::Duration::from_secs(check_cache_frequency_secs)) - .await; - }, - } - } - assert!(chain_id == 4); - - // check that the cache worker is making progress - let mut latest_version = 0; - let mut new_latest_version; - for _ in 0..tries { - tokio::time::sleep(std::time::Duration::from_secs(check_cache_frequency_secs)).await; - new_latest_version = cache_operator.get_latest_version().await.unwrap(); - info!( - "Processed {} versions since last check {}s ago...", - new_latest_version - latest_version, - check_cache_frequency_secs - ); - assert!(new_latest_version > latest_version); - latest_version = new_latest_version; - } -} - -/// Test that the file store worker can start from scratch and make progress. -/// This is a cold start since the file store and the cache start as empty. And the file store is generally the source of truth -/// between the two. We expect the file store to be written to -#[tokio::test] -async fn test_cold_start_file_store_worker_progress() { - setup_test().await; - - let tmp_dir = TempDir::new().expect("Could not create temp dir"); // start with a new file store each time - - let cache_worker_config = IndexerGrpcCacheWorkerConfig { - fullnode_grpc_address: (*TESTNET_FULLNODE_GRPC_URL).clone(), - file_store_config: IndexerGrpcFileStoreConfig::LocalFileStore(LocalFileStore { - local_file_store_path: tmp_dir.path().to_path_buf(), - }), - redis_main_instance_address: (*REDIS_PRIMARY_URL).clone(), - }; - - let file_store_worker_config = IndexerGrpcFileStoreWorkerConfig { - redis_main_instance_address: (*REDIS_PRIMARY_URL).clone(), - file_store_config: IndexerGrpcFileStoreConfig::LocalFileStore(LocalFileStore { - local_file_store_path: tmp_dir.path().to_path_buf(), - }), - }; - - let (_cache_worker_port, _cache_worker_handle) = - start_server::(cache_worker_config.clone()) - .await - .expect("Failed to start CacheWorker"); - - // XXX: wait some time before file store starts up. we should resolve the boot dependency cycle - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - - let (_file_store_port, _file_store_handle) = - start_server::(file_store_worker_config) - .await - .expect("Failed to start FileStoreWorker"); - - // wait until file store writes its first metadata - let file_store_operator = LocalFileStoreOperator::new(tmp_dir.path().to_path_buf()); - let tries = 6; - for _ in 0..tries { - match file_store_operator.get_file_store_metadata().await { - Some(_) => { - info!("File store metadata found"); - break; - }, - None => { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - }, - } - } - - // inspect the files at boot. there should at least be the metadata file specifying that it has 0 versions processed - info!( - "Expecting file store to have files {}", - tmp_dir.path().display() - ); - let file_store_metadata = file_store_operator.get_file_store_metadata().await; - assert!(file_store_metadata.is_some()); - - // emit transactions, enough to write to file store - emit_transactions_for_test() - .await - .expect("Emit transactions failed"); - - // after a while, expect the metadata file to be updated with the latest version - let file_store_metadata = file_store_operator - .get_file_store_metadata() - .await - .expect("Failed to get file store metadata"); - info!( - "[Indexer Cache] File store metadata: {:?}", - file_store_metadata - ); - assert!(file_store_metadata.version > 0); -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs deleted file mode 100644 index b102b93ad164a..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -mod fullnode_tests; diff --git a/testsuite/forge-cli/Cargo.toml b/testsuite/forge-cli/Cargo.toml deleted file mode 100644 index 847cffe0f22aa..0000000000000 --- a/testsuite/forge-cli/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "aptos-forge-cli" -description = "Aptos end to end test framework CLI" -version = "0.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-config = { workspace = true } -aptos-forge = { workspace = true } -aptos-framework = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-logger = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-sdk = { workspace = true } -aptos-testcases = { workspace = true } -async-trait = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -once_cell = { workspace = true } -rand = { workspace = true } -random_word = { workspace = true } -reqwest = { workspace = true } -serde_yaml = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } - -[[bin]] -name = "forge" -path = "src/main.rs" diff --git a/testsuite/forge-cli/src/README.md b/testsuite/forge-cli/src/README.md deleted file mode 100644 index c4598ebc4f70b..0000000000000 --- a/testsuite/forge-cli/src/README.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -id: forge cli -title: Forge CLI -custom_edit_url: https://github.com/aptos-labs/aptos-core/edit/main/testsuite/forge-cli/README.md ---- - -# Forge CLI - -This crate contains the Forge command line interface (CLI) tool. This enables users to -run local and remote Aptos swarms (i.e., networks of validators and validator fullnodes). For -example, to deploy a local validator swarm, run: - -``` -cargo run -p aptos-forge-cli -- --suite "run_forever" --num-validators 4 test local-swarm -``` - -This will start a local network of 4 validators, each running in their own process. The -network will run forever, unless manually killed. The output will display the locations -of the validator files (e.g., the genesis files, logs, node configurations, etc.) and the -commands that were run to start each node. The process id (PID) of each node and -server addresses (e.g., REST APIs) are also displayed when it starts. For example, if you -run the above command you should see: - -``` -... -2022-09-01T15:41:27.228289Z [main] INFO crates/aptos-genesis/src/builder.rs:462 Building genesis with 4 validators. Directory of output: "/private/var/folders/dx/c0l2rrkn0656gfx6v5_dy_p80000gn/T/.tmpq9uPMJ" -... -2022-09-01T15:41:28.090606Z [main] INFO testsuite/forge/src/backend/local/swarm.rs:207 The root (or mint) key for the swarm is: 0xf9f... -... -2022-09-01T15:41:28.094800Z [main] INFO testsuite/forge/src/backend/local/node.rs:129 Started node 0 (PID: 78939) with command: ".../aptos-core/target/debug/aptos-node" "-f" "/private/var/folders/dx/c0l2rrkn0656gfx6v5_dy_p80000gn/T/.tmpq9uPMJ/0/node.yaml" -2022-09-01T15:41:28.094825Z [main] INFO testsuite/forge/src/backend/local/node.rs:137 Node 0: REST API is listening at: http://127.0.0.1:64566 -2022-09-01T15:41:28.094838Z [main] INFO testsuite/forge/src/backend/local/node.rs:142 Node 0: Inspection service is listening at http://127.0.0.1:64568 -... -``` - -Using the information from this output, you could stop a single node and restart -it, e.g., stop and restart node `0`: - -``` -kill -9 -cargo run -p aptos-node -- -f -``` - -## Faucet and minting - -In order to mint coins in this test network you need to run a faucet. You can do that with this command: - -```bash -cargo run -p aptos-faucet-service -- run-simple --key --node-url -``` - -You can get the values above like this: -- `key`: When you started the swarm, there was output like this: `The root (or mint) key for the swarm is: 0xf9f...`. This is the `key`. -- `node_url`: When you started the swarm, there was output like this: `REST API is listening at: http://127.0.0.1:64566`. This is the `node_url`. - -The above command will run a faucet locally, listening on port `8081`. Using this faucet, you can then mint tokens to your test accounts, for example: - -```bash -curl -X POST http://127.0.0.1:8081/mint?amount=&pub_key= -``` - -As an alternative to using the faucet service, you may use the faucet CLI directly: -``` -cargo run -p aptos-faucet-cli -- --amount 10 --accounts --key -``` - -:::tip Faucet and Aptos CLI -See more on how the faucet works in the [README](https://github.com/aptos-labs/aptos-core/tree/main/crates/aptos-faucet). - -Also see how to use the [Aptos CLI](../cli-tools/aptos-cli/use-cli/use-aptos-cli.md#account-examples) with an existing faucet. -::: - -## Validator fullnodes - -To also run validator fullnodes inside the network, use the `--num-validator-fullnodes` flag, e.g.,: -``` -cargo run -p aptos-forge-cli -- --suite "run_forever" --num-validators 3 --num-validator-fullnodes 1 test local-swarm -``` - -## Additional usage - -To see all tool usage options, run: -``` -cargo run -p aptos-forge-cli --help -``` diff --git a/testsuite/forge-cli/src/main.rs b/testsuite/forge-cli/src/main.rs deleted file mode 100644 index 951164b0fdc13..0000000000000 --- a/testsuite/forge-cli/src/main.rs +++ /dev/null @@ -1,2749 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![allow(clippy::field_reassign_with_default)] - -use anyhow::{format_err, Context, Result}; -use aptos_config::config::{ - BootstrappingMode, ConsensusConfig, ContinuousSyncingMode, MempoolConfig, NetbenchConfig, - NodeConfig, StateSyncConfig, -}; -use aptos_forge::{ - args::TransactionTypeArg, - emitter::NumAccountsMode, - prometheus_metrics::LatencyBreakdownSlice, - success_criteria::{ - LatencyBreakdownThreshold, LatencyType, MetricsThreshold, StateProgressThreshold, - SuccessCriteria, SystemMetricsThreshold, - }, - ForgeConfig, Options, *, -}; -use aptos_logger::{info, Level}; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::{ - move_types::account_address::AccountAddress, - transaction_builder::aptos_stdlib, - types::on_chain_config::{ - BlockGasLimitType, OnChainConsensusConfig, OnChainExecutionConfig, TransactionShufflerType, - }, -}; -use aptos_testcases::{ - compatibility_test::SimpleValidatorUpgrade, - consensus_reliability_tests::ChangingWorkingQuorumTest, - forge_setup_test::ForgeSetupTest, - framework_upgrade::FrameworkUpgrade, - fullnode_reboot_stress_test::FullNodeRebootStressTest, - generate_traffic, - load_vs_perf_benchmark::{ - ContinuousTraffic, LoadVsPerfBenchmark, TransactionWorkload, Workloads, - }, - modifiers::{CpuChaosTest, ExecutionDelayConfig, ExecutionDelayTest}, - multi_region_network_test::{ - MultiRegionNetworkEmulationConfig, MultiRegionNetworkEmulationTest, - }, - network_bandwidth_test::NetworkBandwidthTest, - network_loss_test::NetworkLossTest, - network_partition_test::NetworkPartitionTest, - performance_test::PerformanceBenchmark, - public_fullnode_performance::PFNPerformance, - quorum_store_onchain_enable_test::QuorumStoreOnChainEnableTest, - reconfiguration_test::ReconfigurationTest, - state_sync_performance::{ - StateSyncFullnodeFastSyncPerformance, StateSyncFullnodePerformance, - StateSyncValidatorPerformance, - }, - three_region_simulation_test::ThreeRegionSameCloudSimulationTest, - twin_validator_test::TwinValidatorTest, - two_traffics_test::TwoTrafficsTest, - validator_join_leave_test::ValidatorJoinLeaveTest, - validator_reboot_stress_test::ValidatorRebootStressTest, - CompositeNetworkTest, -}; -use clap::{Parser, Subcommand}; -use futures::stream::{FuturesUnordered, StreamExt}; -use once_cell::sync::Lazy; -use rand::{rngs::ThreadRng, seq::SliceRandom, Rng}; -use std::{ - env, - num::NonZeroUsize, - path::{Path, PathBuf}, - process, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - thread, - time::Duration, -}; -use suites::dag::get_dag_test; -use tokio::{runtime::Runtime, select}; -use url::Url; - -mod suites; - -// Useful constants -const KILOBYTE: usize = 1000; -const MEGABYTE: usize = KILOBYTE * 1000; - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[derive(Parser, Debug)] -struct Args { - #[clap(long, default_value_t = 300)] - duration_secs: usize, - #[clap(flatten)] - options: Options, - #[clap(long)] - num_validators: Option, - #[clap(long)] - num_validator_fullnodes: Option, - #[clap( - long, - help = "Specify a test suite to run", - default_value = "land_blocking" - )] - suite: String, - #[clap(long, num_args = 0..)] - changelog: Option>, - - // subcommand groups - #[clap(subcommand)] - cli_cmd: CliCommand, -} - -#[derive(Subcommand, Debug)] -enum CliCommand { - /// Subcommands to run forge tests - #[clap(subcommand)] - Test(TestCommand), - /// Subcommands to set up or manage running forge networks - #[clap(subcommand)] - Operator(OperatorCommand), -} - -#[derive(Subcommand, Debug)] -enum TestCommand { - /// Run tests using the local swarm backend - LocalSwarm(LocalSwarm), - /// Run tests in cluster using the remote kubernetes backend - K8sSwarm(K8sSwarm), -} - -#[derive(Subcommand, Debug)] -enum OperatorCommand { - /// Set the image tag for a node in the cluster - SetNodeImageTag(SetNodeImageTag), - /// Clean up an existing cluster - CleanUp(CleanUp), - /// Resize an existing cluster - Resize(Resize), -} - -#[derive(Parser, Debug)] -struct LocalSwarm { - #[clap(long, help = "directory to build local swarm under")] - swarmdir: Option, -} - -#[derive(Parser, Debug)] -struct K8sSwarm { - #[clap(long, help = "The kubernetes namespace to use for test")] - namespace: Option, - #[clap( - long, - help = "The image tag currently is used for validators", - default_value = "devnet" - )] - image_tag: String, - #[clap( - long, - help = "For supported tests, the image tag for validators to upgrade to", - default_value = "devnet" - )] - upgrade_image_tag: String, - #[clap( - long, - help = "Path to flattened directory containing compiled Move modules" - )] - move_modules_dir: Option, - #[clap( - long, - help = "If set, uses kubectl port-forward instead of assuming k8s DNS access" - )] - port_forward: bool, - #[clap( - long, - help = "If set, reuse the forge testnet active in the specified namespace" - )] - reuse: bool, - #[clap( - long, - help = "If set, keeps the forge testnet active in the specified namespace" - )] - keep: bool, - #[clap(long, help = "If set, enables HAProxy for each of the validators")] - enable_haproxy: bool, -} - -#[derive(Parser, Debug)] -struct SetNodeImageTag { - #[clap(long, help = "The name of the node StatefulSet to update")] - stateful_set_name: String, - #[clap(long, help = "The name of the container to update")] - container_name: String, - #[clap(long, help = "The docker image tag to use for the node")] - image_tag: String, - #[clap(long, help = "The kubernetes namespace to clean up")] - namespace: String, -} - -#[derive(Parser, Debug)] -struct CleanUp { - #[clap( - long, - help = "The kubernetes namespace to clean up. If unset, attemps to cleanup all by using forge-management configmaps" - )] - namespace: Option, -} - -#[derive(Parser, Debug)] -struct Resize { - #[clap(long, help = "The kubernetes namespace to resize")] - namespace: String, - #[clap(long, default_value_t = 30)] - num_validators: usize, - #[clap(long, default_value_t = 1)] - num_fullnodes: usize, - #[clap( - long, - help = "Override the image tag used for validators", - default_value = "devnet" - )] - validator_image_tag: String, - #[clap( - long, - help = "Override the image tag used for testnet-specific components", - default_value = "devnet" - )] - testnet_image_tag: String, - #[clap( - long, - help = "Path to flattened directory containing compiled Move modules" - )] - move_modules_dir: Option, - #[clap( - long, - help = "If set, dont use kubectl port forward to access the cluster" - )] - connect_directly: bool, - #[clap(long, help = "If set, enables HAProxy for each of the validators")] - enable_haproxy: bool, -} - -// common metrics thresholds: -static SYSTEM_12_CORES_5GB_THRESHOLD: Lazy = Lazy::new(|| { - SystemMetricsThreshold::new( - // Check that we don't use more than 12 CPU cores for 30% of the time. - MetricsThreshold::new(12.0, 30), - // Check that we don't use more than 5 GB of memory for 30% of the time. - MetricsThreshold::new_gb(5.0, 30), - ) -}); -static SYSTEM_12_CORES_10GB_THRESHOLD: Lazy = Lazy::new(|| { - SystemMetricsThreshold::new( - // Check that we don't use more than 12 CPU cores for 30% of the time. - MetricsThreshold::new(12.0, 30), - // Check that we don't use more than 10 GB of memory for 30% of the time. - MetricsThreshold::new_gb(10.0, 30), - ) -}); - -/// Make an easy to remember random namespace for your testnet -fn random_namespace(dictionary: Vec, rng: &mut R) -> Result { - // Pick four random words - let random_words = dictionary - .choose_multiple(rng, 4) - .cloned() - .collect::>(); - Ok(format!("forge-{}", random_words.join("-"))) -} - -fn main() -> Result<()> { - let mut logger = aptos_logger::Logger::new(); - logger.channel_size(1000).is_async(false).level(Level::Info); - logger.build(); - - let args = Args::parse(); - let duration = Duration::from_secs(args.duration_secs as u64); - let suite_name: &str = args.suite.as_ref(); - - let runtime = Runtime::new()?; - match args.cli_cmd { - // cmd input for test - CliCommand::Test(ref test_cmd) => { - // Identify the test suite to run - let mut test_suite = get_test_suite(suite_name, duration, test_cmd)?; - - // Identify the number of validators and fullnodes to run - // (if overriding what test has specified) - if let Some(num_validators) = args.num_validators { - let num_validators_non_zero = NonZeroUsize::new(num_validators) - .context("--num-validators must be positive!")?; - test_suite = test_suite.with_initial_validator_count(num_validators_non_zero); - - // Verify the number of fullnodes is less than the validators - if let Some(num_validator_fullnodes) = args.num_validator_fullnodes { - if num_validator_fullnodes > num_validators { - return Err(format_err!( - "Cannot have more fullnodes than validators! Fullnodes: {:?}, validators: {:?}.", - num_validator_fullnodes, num_validators - )); - } - } - } - if let Some(num_validator_fullnodes) = args.num_validator_fullnodes { - test_suite = test_suite.with_initial_fullnode_count(num_validator_fullnodes) - } - - // Run the test suite - match test_cmd { - TestCommand::LocalSwarm(local_cfg) => { - // Loosen all criteria for local runs - test_suite.get_success_criteria_mut().min_avg_tps = 400; - let previous_emit_job = test_suite.get_emit_job().clone(); - let test_suite = - test_suite.with_emit_job(previous_emit_job.mode(EmitJobMode::MaxLoad { - mempool_backlog: 5000, - })); - let swarm_dir = local_cfg.swarmdir.clone(); - run_forge( - duration, - test_suite, - LocalFactory::from_workspace(swarm_dir)?, - &args.options, - args.changelog.clone(), - ) - }, - TestCommand::K8sSwarm(k8s) => { - if let Some(move_modules_dir) = &k8s.move_modules_dir { - test_suite = test_suite.with_genesis_modules_path(move_modules_dir.clone()); - } - let namespace = if k8s.namespace.is_none() { - let mut rng: ThreadRng = rand::thread_rng(); - // Lets pick some four letter words ;) - let words = random_word::all_len(4) - .ok_or_else(|| { - format_err!( - "Failed to get namespace, rerun with --namespace " - ) - })? - .to_vec() - .iter() - .map(|s| s.to_string()) - .collect::>(); - random_namespace(words, &mut rng)? - } else { - k8s.namespace.clone().unwrap() - }; - let forge_runner_mode = - ForgeRunnerMode::try_from_env().unwrap_or(ForgeRunnerMode::K8s); - run_forge( - duration, - test_suite, - K8sFactory::new( - namespace, - k8s.image_tag.clone(), - k8s.upgrade_image_tag.clone(), - // We want to port forward if we're running locally because local means we're not in cluster - k8s.port_forward || forge_runner_mode == ForgeRunnerMode::Local, - k8s.reuse, - k8s.keep, - k8s.enable_haproxy, - ) - .unwrap(), - &args.options, - args.changelog, - )?; - Ok(()) - }, - } - }, - // cmd input for cluster operations - CliCommand::Operator(op_cmd) => match op_cmd { - OperatorCommand::SetNodeImageTag(set_stateful_set_image_tag_config) => { - runtime.block_on(set_stateful_set_image_tag( - set_stateful_set_image_tag_config.stateful_set_name, - set_stateful_set_image_tag_config.container_name, - set_stateful_set_image_tag_config.image_tag, - set_stateful_set_image_tag_config.namespace, - ))?; - Ok(()) - }, - OperatorCommand::CleanUp(cleanup) => { - if let Some(namespace) = cleanup.namespace { - runtime.block_on(uninstall_testnet_resources(namespace))?; - } else { - runtime.block_on(cleanup_cluster_with_management())?; - } - Ok(()) - }, - OperatorCommand::Resize(resize) => { - runtime.block_on(install_testnet_resources( - resize.namespace, - resize.num_validators, - resize.num_fullnodes, - resize.validator_image_tag, - resize.testnet_image_tag, - resize.move_modules_dir, - !resize.connect_directly, - resize.enable_haproxy, - None, - None, - ))?; - Ok(()) - }, - }, - } -} - -pub fn run_forge( - global_duration: Duration, - tests: ForgeConfig, - factory: F, - options: &Options, - logs: Option>, -) -> Result<()> { - let forge = Forge::new(options, tests, global_duration, factory); - - if options.list { - forge.list()?; - - return Ok(()); - } - - match forge.run() { - Ok(report) => { - if let Some(mut changelog) = logs { - if changelog.len() != 2 { - println!("Use: changelog "); - process::exit(1); - } - let to_commit = changelog.remove(1); - let from_commit = Some(changelog.remove(0)); - send_changelog_message(&report.to_string(), &from_commit, &to_commit); - } - Ok(()) - }, - Err(e) => { - eprintln!("Failed to run tests:\n{}", e); - Err(e) - }, - } -} - -pub fn send_changelog_message(perf_msg: &str, from_commit: &Option, to_commit: &str) { - println!( - "Generating changelog from {:?} to {}", - from_commit, to_commit - ); - let changelog = get_changelog(from_commit.as_ref(), to_commit); - let msg = format!("{}\n\n{}", changelog, perf_msg); - let slack_url: Option = env::var("SLACK_URL") - .map(|u| u.parse().expect("Failed to parse SLACK_URL")) - .ok(); - if let Some(ref slack_url) = slack_url { - let slack_client = SlackClient::new(); - if let Err(e) = slack_client.send_message(slack_url, &msg) { - println!("Failed to send slack message: {}", e); - } - } -} - -fn get_changelog(prev_commit: Option<&String>, upstream_commit: &str) -> String { - let github_client = GitHub::new(); - let commits = github_client.get_commits("aptos-labs/aptos-core", upstream_commit); - match commits { - Err(e) => { - println!("Failed to get github commits: {:?}", e); - format!("*Revision upstream_{}*", upstream_commit) - }, - Ok(commits) => { - let mut msg = format!("*Revision {}*", upstream_commit); - for commit in commits { - if let Some(prev_commit) = prev_commit { - if commit.sha.starts_with(prev_commit) { - break; - } - } - let commit_lines: Vec<_> = commit.commit.message.split('\n').collect(); - let commit_head = commit_lines[0]; - let commit_head = commit_head.replace("[breaking]", "*[breaking]*"); - let short_sha = &commit.sha[..6]; - let email_parts: Vec<_> = commit.commit.author.email.split('@').collect(); - let author = email_parts[0]; - let line = format!("\n>\u{2022} {} _{}_ {}", short_sha, author, commit_head); - msg.push_str(&line); - } - msg - }, - } -} - -// TODO: can we clean this function up? -/// Returns the test suite for the given test name -fn get_test_suite( - test_name: &str, - duration: Duration, - test_cmd: &TestCommand, -) -> Result { - // Check the test name against the multi-test suites - match test_name { - "local_test_suite" => return Ok(local_test_suite()), - "pre_release" => return Ok(pre_release_suite()), - "run_forever" => return Ok(run_forever()), - // TODO(rustielin): verify each test suite - "k8s_suite" => return Ok(k8s_test_suite()), - "chaos" => return Ok(chaos_test_suite(duration)), - _ => {}, // No multi-test suite matches! - }; - - // Otherwise, check the test name against the grouped test suites - if let Some(test_suite) = get_land_blocking_test(test_name, duration, test_cmd) { - return Ok(test_suite); - } else if let Some(test_suite) = get_multi_region_test(test_name) { - return Ok(test_suite); - } else if let Some(test_suite) = get_netbench_test(test_name) { - return Ok(test_suite); - } else if let Some(test_suite) = get_pfn_test(test_name, duration) { - return Ok(test_suite); - } else if let Some(test_suite) = get_realistic_env_test(test_name, duration, test_cmd) { - return Ok(test_suite); - } else if let Some(test_suite) = get_state_sync_test(test_name) { - return Ok(test_suite); - } else if let Some(test_suite) = get_dag_test(test_name, duration, test_cmd) { - return Ok(test_suite); - } - - // Otherwise, check the test name against the ungrouped test suites - let ungrouped_test_suite = match test_name { - "epoch_changer_performance" => epoch_changer_performance(), - "validators_join_and_leave" => validators_join_and_leave(), - "config" => ForgeConfig::default().add_network_test(ReconfigurationTest), - "network_partition" => network_partition(), - "network_bandwidth" => network_bandwidth(), - "setup_test" => setup_test(), - "single_vfn_perf" => single_vfn_perf(), - "validator_reboot_stress_test" => validator_reboot_stress_test(), - "fullnode_reboot_stress_test" => fullnode_reboot_stress_test(), - "workload_mix" => workload_mix_test(), - "account_creation" | "nft_mint" | "publishing" | "module_loading" - | "write_new_resource" => individual_workload_tests(test_name.into()), - "graceful_overload" => graceful_overload(), - // not scheduled on continuous - "load_vs_perf_benchmark" => load_vs_perf_benchmark(), - "workload_vs_perf_benchmark" => workload_vs_perf_benchmark(), - // maximizing number of rounds and epochs within a given time, to stress test consensus - // so using small constant traffic, small blocks and fast rounds, and short epochs. - // reusing changing_working_quorum_test just for invariants/asserts, but with max_down_nodes = 0. - "consensus_stress_test" => consensus_stress_test(), - "changing_working_quorum_test" => changing_working_quorum_test(), - "changing_working_quorum_test_high_load" => changing_working_quorum_test_high_load(), - // not scheduled on continuous - "large_test_only_few_nodes_down" => large_test_only_few_nodes_down(), - "different_node_speed_and_reliability_test" => different_node_speed_and_reliability_test(), - "state_sync_slow_processing_catching_up" => state_sync_slow_processing_catching_up(), - "state_sync_failures_catching_up" => state_sync_failures_catching_up(), - "twin_validator_test" => twin_validator_test(), - "large_db_simple_test" => large_db_simple_test(), - "consensus_only_realistic_env_max_tps" => run_consensus_only_realistic_env_max_tps(), - "quorum_store_reconfig_enable_test" => quorum_store_reconfig_enable_test(), - "mainnet_like_simulation_test" => mainnet_like_simulation_test(), - "gather_metrics" => gather_metrics(), - _ => return Err(format_err!("Invalid --suite given: {:?}", test_name)), - }; - - Ok(ungrouped_test_suite) -} - -/// Provides a forge config that runs the swarm forever (unless killed) -fn run_forever() -> ForgeConfig { - ForgeConfig::default() - .add_admin_test(GetMetadata) - .with_genesis_module_bundle(aptos_cached_packages::head_release_bundle().clone()) - .add_aptos_test(RunForever) -} - -fn local_test_suite() -> ForgeConfig { - ForgeConfig::default() - .add_aptos_test(FundAccount) - .add_aptos_test(TransferCoins) - .add_admin_test(GetMetadata) - .add_network_test(RestartValidator) - .add_network_test(EmitTransaction) - .with_genesis_module_bundle(aptos_cached_packages::head_release_bundle().clone()) -} - -fn k8s_test_suite() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(30).unwrap()) - .add_aptos_test(FundAccount) - .add_aptos_test(TransferCoins) - .add_admin_test(GetMetadata) - .add_network_test(EmitTransaction) - .add_network_test(SimpleValidatorUpgrade) - .add_network_test(PerformanceBenchmark) -} - -/// Attempts to match the test name to a land-blocking test -fn get_land_blocking_test( - test_name: &str, - duration: Duration, - test_cmd: &TestCommand, -) -> Option { - let test = match test_name { - "land_blocking" => land_blocking_test_suite(duration), // TODO: remove land_blocking, superseded by below - "realistic_env_max_load" => realistic_env_max_load_test(duration, test_cmd, 7, 5), - "compat" => compat(), - "framework_upgrade" => framework_upgrade(), - _ => return None, // The test name does not match a land-blocking test - }; - Some(test) -} - -/// Attempts to match the test name to a network benchmark test -fn get_netbench_test(test_name: &str) -> Option { - let test = match test_name { - // Network tests without chaos - "net_bench_no_chaos_1000" => net_bench_no_chaos(MEGABYTE, 1000), - "net_bench_no_chaos_900" => net_bench_no_chaos(MEGABYTE, 900), - "net_bench_no_chaos_800" => net_bench_no_chaos(MEGABYTE, 800), - "net_bench_no_chaos_700" => net_bench_no_chaos(MEGABYTE, 700), - "net_bench_no_chaos_600" => net_bench_no_chaos(MEGABYTE, 600), - "net_bench_no_chaos_500" => net_bench_no_chaos(MEGABYTE, 500), - "net_bench_no_chaos_300" => net_bench_no_chaos(MEGABYTE, 300), - "net_bench_no_chaos_200" => net_bench_no_chaos(MEGABYTE, 200), - "net_bench_no_chaos_100" => net_bench_no_chaos(MEGABYTE, 100), - "net_bench_no_chaos_50" => net_bench_no_chaos(MEGABYTE, 50), - "net_bench_no_chaos_20" => net_bench_no_chaos(MEGABYTE, 20), - "net_bench_no_chaos_10" => net_bench_no_chaos(MEGABYTE, 10), - "net_bench_no_chaos_1" => net_bench_no_chaos(MEGABYTE, 1), - - // Network tests with chaos - "net_bench_two_region_chaos_1000" => net_bench_two_region_chaos(MEGABYTE, 1000), - "net_bench_two_region_chaos_500" => net_bench_two_region_chaos(MEGABYTE, 500), - "net_bench_two_region_chaos_300" => net_bench_two_region_chaos(MEGABYTE, 300), - "net_bench_two_region_chaos_200" => net_bench_two_region_chaos(MEGABYTE, 200), - "net_bench_two_region_chaos_100" => net_bench_two_region_chaos(MEGABYTE, 100), - "net_bench_two_region_chaos_50" => net_bench_two_region_chaos(MEGABYTE, 50), - "net_bench_two_region_chaos_30" => net_bench_two_region_chaos(MEGABYTE, 30), - "net_bench_two_region_chaos_20" => net_bench_two_region_chaos(MEGABYTE, 20), - "net_bench_two_region_chaos_15" => net_bench_two_region_chaos(MEGABYTE, 15), - "net_bench_two_region_chaos_10" => net_bench_two_region_chaos(MEGABYTE, 10), - "net_bench_two_region_chaos_1" => net_bench_two_region_chaos(MEGABYTE, 1), - - // Network tests with small messages - "net_bench_two_region_chaos_small_messages_5" => { - net_bench_two_region_chaos(100 * KILOBYTE, 50) - }, - "net_bench_two_region_chaos_small_messages_1" => { - net_bench_two_region_chaos(100 * KILOBYTE, 10) - }, - - _ => return None, // The test name does not match a network benchmark test - }; - Some(test) -} - -/// Attempts to match the test name to a PFN test -fn get_pfn_test(test_name: &str, duration: Duration) -> Option { - let test = match test_name { - "pfn_const_tps" => pfn_const_tps(duration, false, false, true), - "pfn_const_tps_with_network_chaos" => pfn_const_tps(duration, false, true, false), - "pfn_const_tps_with_realistic_env" => pfn_const_tps(duration, true, true, false), - "pfn_performance" => pfn_performance(duration, false, false, true), - "pfn_performance_with_network_chaos" => pfn_performance(duration, false, true, false), - "pfn_performance_with_realistic_env" => pfn_performance(duration, true, true, false), - _ => return None, // The test name does not match a PFN test - }; - Some(test) -} - -/// Attempts to match the test name to a realistic-env test -fn get_realistic_env_test( - test_name: &str, - duration: Duration, - test_cmd: &TestCommand, -) -> Option { - let test = match test_name { - "realistic_env_max_load_large" => realistic_env_max_load_test(duration, test_cmd, 20, 10), - "realistic_env_load_sweep" => realistic_env_load_sweep_test(), - "realistic_env_workload_sweep" => realistic_env_workload_sweep_test(), - "realistic_env_graceful_workload_sweep" => realistic_env_graceful_workload_sweep(), - "realistic_env_graceful_overload" => realistic_env_graceful_overload(), - "realistic_network_tuned_for_throughput" => realistic_network_tuned_for_throughput_test(), - _ => return None, // The test name does not match a realistic-env test - }; - Some(test) -} - -/// Attempts to match the test name to a state sync test -fn get_state_sync_test(test_name: &str) -> Option { - let test = match test_name { - "state_sync_perf_fullnodes_apply_outputs" => state_sync_perf_fullnodes_apply_outputs(), - "state_sync_perf_fullnodes_execute_transactions" => { - state_sync_perf_fullnodes_execute_transactions() - }, - "state_sync_perf_fullnodes_fast_sync" => state_sync_perf_fullnodes_fast_sync(), - "state_sync_perf_validators" => state_sync_perf_validators(), - _ => return None, // The test name does not match a state sync test - }; - Some(test) -} - -/// Attempts to match the test name to a multi-region test -fn get_multi_region_test(test_name: &str) -> Option { - let test = match test_name { - "multiregion_benchmark_test" => multiregion_benchmark_test(), - "three_region_simulation" => three_region_simulation(), - "three_region_simulation_with_different_node_speed" => { - three_region_simulation_with_different_node_speed() - }, - _ => return None, // The test name does not match a multi-region test - }; - Some(test) -} - -fn wrap_with_realistic_env(test: T) -> CompositeNetworkTest { - CompositeNetworkTest::new_with_two_wrappers( - MultiRegionNetworkEmulationTest::default(), - CpuChaosTest::default(), - test, - ) -} - -fn mempool_config_practically_non_expiring(mempool_config: &mut MempoolConfig) { - mempool_config.capacity = 3_000_000; - mempool_config.capacity_bytes = (3_u64 * 1024 * 1024 * 1024) as usize; - mempool_config.capacity_per_user = 100_000; - mempool_config.system_transaction_timeout_secs = 5 * 60 * 60; - mempool_config.system_transaction_gc_interval_ms = 5 * 60 * 60_000; -} - -fn state_sync_config_execute_transactions(state_sync_config: &mut StateSyncConfig) { - state_sync_config.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - state_sync_config.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; -} - -fn state_sync_config_apply_transaction_outputs(state_sync_config: &mut StateSyncConfig) { - state_sync_config.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - state_sync_config.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; -} - -fn state_sync_config_fast_sync(state_sync_config: &mut StateSyncConfig) { - state_sync_config.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - state_sync_config.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; -} - -fn wrap_with_two_region_env(test: T) -> CompositeNetworkTest { - CompositeNetworkTest::new( - MultiRegionNetworkEmulationTest::new_with_config( - MultiRegionNetworkEmulationConfig::two_region(), - ), - test, - ) -} - -fn run_consensus_only_realistic_env_max_tps() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 300000, - }) - .txn_expiration_time_secs(5 * 60), - ) - .add_network_test(CompositeNetworkTest::new( - MultiRegionNetworkEmulationTest::default(), - CpuChaosTest::default(), - )) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - optimize_for_maximum_throughput(config, 20_000, 4_500, 3.0); - })) - // TODO(ibalajiarun): tune these success critiera after we have a better idea of the test behavior - .with_success_criteria( - SuccessCriteria::new(10000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn quorum_store_backlog_txn_limit_count( - config: &mut NodeConfig, - target_tps: usize, - vn_latency: f64, -) { - config - .consensus - .quorum_store - .back_pressure - .backlog_txn_limit_count = (target_tps as f64 * vn_latency) as u64; - config - .consensus - .quorum_store - .back_pressure - .dynamic_max_txn_per_s = 4000; -} - -fn optimize_for_maximum_throughput( - config: &mut NodeConfig, - target_tps: usize, - max_txns_per_block: usize, - vn_latency: f64, -) { - mempool_config_practically_non_expiring(&mut config.mempool); - - config.consensus.max_sending_block_txns = max_txns_per_block as u64; - config.consensus.max_receiving_block_txns = (max_txns_per_block as f64 * 4.0 / 3.0) as u64; - config.consensus.max_sending_block_bytes = 10 * 1024 * 1024; - config.consensus.max_receiving_block_bytes = 12 * 1024 * 1024; - config.consensus.pipeline_backpressure = vec![]; - config.consensus.chain_health_backoff = vec![]; - - quorum_store_backlog_txn_limit_count(config, target_tps, vn_latency); - - config.consensus.quorum_store.sender_max_batch_txns = 500; - config.consensus.quorum_store.sender_max_batch_bytes = 4 * 1024 * 1024; - config.consensus.quorum_store.sender_max_num_batches = 100; - config.consensus.quorum_store.sender_max_total_txns = 4000; - config.consensus.quorum_store.sender_max_total_bytes = 8 * 1024 * 1024; - config.consensus.quorum_store.receiver_max_batch_txns = 1000; - config.consensus.quorum_store.receiver_max_batch_bytes = 8 * 1024 * 1024; - config.consensus.quorum_store.receiver_max_num_batches = 200; - config.consensus.quorum_store.receiver_max_total_txns = 8000; - config.consensus.quorum_store.receiver_max_total_bytes = 16 * 1024 * 1024; -} - -fn large_db_simple_test() -> ForgeConfig { - large_db_test(10, 500, 300, "10-validators".to_string()) -} - -fn twin_validator_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(5) - .add_network_test(TwinValidatorTest) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 300.into(); - })) - .with_success_criteria( - SuccessCriteria::new(5500) - .add_no_restarts() - .add_wait_for_catchup_s(60) - .add_system_metrics_threshold(SYSTEM_12_CORES_5GB_THRESHOLD.clone()) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -fn state_sync_failures_catching_up() -> ForgeConfig { - changing_working_quorum_test_helper( - 7, - 300, - 3000, - 2500, - true, - false, - ChangingWorkingQuorumTest { - min_tps: 1500, - always_healthy_nodes: 2, - max_down_nodes: 1, - num_large_validators: 2, - add_execution_delay: false, - check_period_s: 27, - }, - ) -} - -fn state_sync_slow_processing_catching_up() -> ForgeConfig { - changing_working_quorum_test_helper(7, 300, 3000, 2500, true, true, ChangingWorkingQuorumTest { - min_tps: 750, - always_healthy_nodes: 2, - max_down_nodes: 0, - num_large_validators: 2, - add_execution_delay: true, - check_period_s: 57, - }) -} - -fn different_node_speed_and_reliability_test() -> ForgeConfig { - changing_working_quorum_test_helper(20, 120, 70, 50, true, false, ChangingWorkingQuorumTest { - min_tps: 30, - always_healthy_nodes: 6, - max_down_nodes: 5, - num_large_validators: 3, - add_execution_delay: true, - check_period_s: 27, - }) -} - -fn large_test_only_few_nodes_down() -> ForgeConfig { - changing_working_quorum_test_helper(60, 120, 100, 70, false, false, ChangingWorkingQuorumTest { - min_tps: 50, - always_healthy_nodes: 40, - max_down_nodes: 10, - num_large_validators: 0, - add_execution_delay: false, - check_period_s: 27, - }) -} - -fn changing_working_quorum_test_high_load() -> ForgeConfig { - changing_working_quorum_test_helper(16, 120, 500, 300, true, true, ChangingWorkingQuorumTest { - min_tps: 50, - always_healthy_nodes: 0, - max_down_nodes: 16, - num_large_validators: 0, - add_execution_delay: false, - // Use longer check duration, as we are bringing enough nodes - // to require state-sync to catch up to have consensus. - check_period_s: 53, - }) -} - -fn changing_working_quorum_test() -> ForgeConfig { - changing_working_quorum_test_helper(16, 120, 100, 70, true, true, ChangingWorkingQuorumTest { - min_tps: 15, - always_healthy_nodes: 0, - max_down_nodes: 16, - num_large_validators: 0, - add_execution_delay: false, - // Use longer check duration, as we are bringing enough nodes - // to require state-sync to catch up to have consensus. - check_period_s: 53, - }) -} - -fn consensus_stress_test() -> ForgeConfig { - changing_working_quorum_test_helper(10, 60, 100, 80, true, false, ChangingWorkingQuorumTest { - min_tps: 50, - always_healthy_nodes: 10, - max_down_nodes: 0, - num_large_validators: 0, - add_execution_delay: false, - check_period_s: 27, - }) -} - -fn realistic_env_sweep_wrap( - num_validators: usize, - num_fullnodes: usize, - test: LoadVsPerfBenchmark, -) -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(num_validators).unwrap()) - .with_initial_fullnode_count(num_fullnodes) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.execution.processed_transactions_detailed_counters = true; - })) - .add_network_test(wrap_with_realistic_env(test)) - // Test inherits the main EmitJobRequest, so update here for more precise latency measurements - .with_emit_job( - EmitJobRequest::default().latency_polling_interval(Duration::from_millis(100)), - ) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) - .with_success_criteria( - SuccessCriteria::new(0) - .add_no_restarts() - .add_wait_for_catchup_s(60) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 30.0, - max_round_gap: 10, - }), - ) -} - -fn realistic_env_load_sweep_test() -> ForgeConfig { - realistic_env_sweep_wrap(20, 10, LoadVsPerfBenchmark { - test: Box::new(PerformanceBenchmark), - workloads: Workloads::TPS(vec![10, 100, 1000, 3000, 5000]), - criteria: [ - (9, 1.5, 3., 4., 0), - (95, 1.5, 3., 4., 0), - (950, 2., 3., 4., 0), - (2750, 2.5, 3.5, 4.5, 0), - (4600, 3., 4., 6., 10), // Allow some expired transactions (high-load) - ] - .into_iter() - .map( - |(min_tps, max_lat_p50, max_lat_p90, max_lat_p99, max_expired_tps)| { - SuccessCriteria::new(min_tps) - .add_max_expired_tps(max_expired_tps) - .add_max_failed_submission_tps(0) - .add_latency_threshold(max_lat_p50, LatencyType::P50) - .add_latency_threshold(max_lat_p90, LatencyType::P90) - .add_latency_threshold(max_lat_p99, LatencyType::P99) - }, - ) - .collect(), - continuous_traffic: None, - }) -} - -fn realistic_env_workload_sweep_test() -> ForgeConfig { - realistic_env_sweep_wrap(7, 3, LoadVsPerfBenchmark { - test: Box::new(PerformanceBenchmark), - workloads: Workloads::TRANSACTIONS(vec![ - TransactionWorkload { - transaction_type: TransactionTypeArg::CoinTransfer, - num_modules: 1, - unique_senders: false, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::NoOp, - num_modules: 100, - unique_senders: false, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::ModifyGlobalResource, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::TokenV2AmbassadorMint, - num_modules: 1, - unique_senders: true, - mempool_backlog: 30000, - }, - // transactions get rejected, to fix. - // TransactionWorkload { - // transaction_type: TransactionTypeArg::PublishPackage, - // num_modules: 1, - // unique_senders: true, - // mempool_backlog: 1000, - // }, - ]), - // Investigate/improve to make latency more predictable on different workloads - criteria: [ - (5500, 100, 0.3, 0.3, 0.8, 0.65), - (4500, 100, 0.3, 0.4, 1.0, 2.0), - (2000, 300, 0.3, 0.3, 0.8, 2.0), - (600, 500, 0.3, 0.3, 0.8, 2.0), - // (150, 0.5, 1.0, 1.5, 0.65), - ] - .into_iter() - .map( - |( - min_tps, - max_expired, - batch_to_pos, - pos_to_proposal, - proposal_to_ordered, - ordered_to_commit, - )| { - SuccessCriteria::new(min_tps) - .add_max_expired_tps(max_expired) - .add_max_failed_submission_tps(200) - .add_latency_breakdown_threshold(LatencyBreakdownThreshold::new_strict(vec![ - (LatencyBreakdownSlice::QsBatchToPos, batch_to_pos), - (LatencyBreakdownSlice::QsPosToProposal, pos_to_proposal), - ( - LatencyBreakdownSlice::ConsensusProposalToOrdered, - proposal_to_ordered, - ), - ( - LatencyBreakdownSlice::ConsensusOrderedToCommit, - ordered_to_commit, - ), - ])) - }, - ) - .collect(), - continuous_traffic: None, - }) -} - -fn realistic_env_graceful_workload_sweep() -> ForgeConfig { - realistic_env_sweep_wrap(7, 3, LoadVsPerfBenchmark { - test: Box::new(PerformanceBenchmark), - workloads: Workloads::TRANSACTIONS(vec![ - // do account generation first, to fill up a storage a bit. - TransactionWorkload { - transaction_type: TransactionTypeArg::AccountGeneration, - num_modules: 1, - unique_senders: false, - mempool_backlog: 100000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::CoinTransfer, - num_modules: 1, - unique_senders: false, - mempool_backlog: 100000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::ModifyGlobalResource, - num_modules: 1, - unique_senders: false, - mempool_backlog: 50000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::CreateObjects10WithPayload10k, - num_modules: 1, - unique_senders: false, - mempool_backlog: 10000, - }, - // very low gas/s - TransactionWorkload { - transaction_type: TransactionTypeArg::CreateObjectsConflict100WithPayload10k, - num_modules: 1, - unique_senders: false, - mempool_backlog: 2000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::TokenV2AmbassadorMint, - num_modules: 1, - unique_senders: false, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::VectorPicture40, - num_modules: 1, - unique_senders: false, - mempool_backlog: 50000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::VectorPictureRead40, - num_modules: 1, - unique_senders: false, - mempool_backlog: 50000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::VectorPicture30k, - num_modules: 1, - unique_senders: false, - mempool_backlog: 10000, - }, - // very high gas/s - TransactionWorkload { - transaction_type: TransactionTypeArg::VectorPicture30k, - num_modules: 20, - unique_senders: false, - mempool_backlog: 10000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::SmartTablePicture30KWith200Change, - num_modules: 1, - unique_senders: false, - mempool_backlog: 2000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::SmartTablePicture1MWith256Change, - num_modules: 1, - unique_senders: false, - mempool_backlog: 2000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::SmartTablePicture1MWith1KChangeExceedsLimit, - num_modules: 1, - unique_senders: false, - mempool_backlog: 2000, - }, - // publishing package - executes sequentially, but conflict_multiplier is 1 - TransactionWorkload { - transaction_type: TransactionTypeArg::PublishPackage, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - // module loading - TransactionWorkload { - transaction_type: TransactionTypeArg::NoOp, - num_modules: 1000, - unique_senders: false, - mempool_backlog: 50000, - }, - ]), - criteria: Vec::new(), - continuous_traffic: Some(ContinuousTraffic { - traffic: EmitJobRequest::default() - .num_accounts_mode(NumAccountsMode::TransactionsPerAccount(1)) - .mode(EmitJobMode::ConstTps { tps: 10 }) - .gas_price(5 * aptos_global_constants::GAS_UNIT_PRICE), - criteria: Some(SuccessCriteria::new(8)), - }), - }) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) -} - -fn load_vs_perf_benchmark() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_initial_fullnode_count(10) - .add_network_test(LoadVsPerfBenchmark { - test: Box::new(PerformanceBenchmark), - workloads: Workloads::TPS(vec![ - 200, 1000, 3000, 5000, 7000, 7500, 8000, 9000, 10000, 12000, 15000, - ]), - criteria: Vec::new(), - continuous_traffic: None, - }) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) - .with_success_criteria( - SuccessCriteria::new(0) - .add_no_restarts() - .add_wait_for_catchup_s(60) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 30.0, - max_round_gap: 10, - }), - ) -} - -fn workload_vs_perf_benchmark() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(7) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.execution.processed_transactions_detailed_counters = true; - })) - .add_network_test(LoadVsPerfBenchmark { - test: Box::new(PerformanceBenchmark), - workloads: Workloads::TRANSACTIONS(vec![ - TransactionWorkload { - transaction_type: TransactionTypeArg::NoOp, - num_modules: 1, - unique_senders: false, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::NoOp, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::NoOp, - num_modules: 1000, - unique_senders: false, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::CoinTransfer, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::CoinTransfer, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::AccountResource32B, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::AccountResource1KB, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - TransactionWorkload { - transaction_type: TransactionTypeArg::PublishPackage, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }, - ]), - criteria: Vec::new(), - continuous_traffic: None, - }) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) - .with_success_criteria( - SuccessCriteria::new(0) - .add_no_restarts() - .add_wait_for_catchup_s(60) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 30.0, - max_round_gap: 10, - }), - ) -} - -fn graceful_overload() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(10).unwrap()) - // if we have full nodes for subset of validators, TPS drops. - // Validators without VFN are not creating batches, - // as no useful transaction reach their mempool. - // something to potentially improve upon. - // So having VFNs for all validators - .with_initial_fullnode_count(10) - .add_network_test(TwoTrafficsTest { - inner_traffic: EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 10000 }) - .init_gas_price_multiplier(20), - - // Additionally - we are not really gracefully handling overlaods, - // setting limits based on current reality, to make sure they - // don't regress, but something to investigate - inner_success_criteria: SuccessCriteria::new(3400), - }) - // First start non-overload (higher gas-fee) traffic, - // to not cause issues with TxnEmitter setup - account creation - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 1000 }) - .gas_price(5 * aptos_global_constants::GAS_UNIT_PRICE), - ) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 300.into(); - })) - .with_success_criteria( - SuccessCriteria::new(900) - .add_no_restarts() - .add_wait_for_catchup_s(120) - .add_system_metrics_threshold(SystemMetricsThreshold::new( - // Check that we don't use more than 12 CPU cores for 30% of the time. - MetricsThreshold::new(12.0, 40), - // Check that we don't use more than 5 GB of memory for 30% of the time. - MetricsThreshold::new_gb(5.0, 30), - )) - .add_latency_threshold(10.0, LatencyType::P50) - .add_latency_threshold(30.0, LatencyType::P90) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 30.0, - max_round_gap: 10, - }), - ) -} - -fn realistic_env_graceful_overload() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_initial_fullnode_count(20) - .add_network_test(wrap_with_realistic_env(TwoTrafficsTest { - inner_traffic: EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 15000 }) - .init_gas_price_multiplier(20), - // Additionally - we are not really gracefully handling overlaods, - // setting limits based on current reality, to make sure they - // don't regress, but something to investigate - inner_success_criteria: SuccessCriteria::new(3400), - })) - // First start higher gas-fee traffic, to not cause issues with TxnEmitter setup - account creation - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 1000 }) - .gas_price(5 * aptos_global_constants::GAS_UNIT_PRICE), - ) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.execution.processed_transactions_detailed_counters = true; - })) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 300.into(); - })) - .with_success_criteria( - SuccessCriteria::new(900) - .add_no_restarts() - .add_wait_for_catchup_s(180) // 3 minutes - .add_system_metrics_threshold(SystemMetricsThreshold::new( - // overload test uses more CPUs than others, so increase the limit - // Check that we don't use more than 18 CPU cores for 30% of the time. - MetricsThreshold::new(18.0, 40), - // Check that we don't use more than 5 GB of memory for 30% of the time. - MetricsThreshold::new_gb(5.0, 30), - )) - .add_latency_threshold(10.0, LatencyType::P50) - .add_latency_threshold(30.0, LatencyType::P90) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 30.0, - max_round_gap: 10, - }), - ) -} - -fn workload_mix_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(5).unwrap()) - .with_initial_fullnode_count(3) - .add_network_test(PerformanceBenchmark) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.execution.processed_transactions_detailed_counters = true; - })) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 10000, - }) - .transaction_mix(vec![ - // To test both variants, make module publish with such frequency, so that there are - // similar number of sequential and parallel blocks. - // For other transactions, make more expensive transactions somewhat rarer. - ( - TransactionTypeArg::AccountGeneration.materialize_default(), - 10000, - ), - ( - TransactionTypeArg::CoinTransfer.materialize_default(), - 10000, - ), - (TransactionTypeArg::PublishPackage.materialize_default(), 3), - ( - TransactionTypeArg::Batch100Transfer.materialize_default(), - 100, - ), - ( - TransactionTypeArg::VectorPicture30k.materialize_default(), - 100, - ), - ( - TransactionTypeArg::SmartTablePicture30KWith200Change.materialize( - 1, - true, - WorkflowProgress::when_done_default(), - ), - 100, - ), - ( - TransactionTypeArg::TokenV2AmbassadorMint.materialize_default(), - 10000, - ), - ( - TransactionTypeArg::ModifyGlobalResource.materialize_default(), - 1000, - ), - ( - TransactionTypeArg::ModifyGlobalResourceAggV2.materialize_default(), - 1000, - ), - ( - TransactionTypeArg::ModifyGlobalFlagAggV2.materialize_default(), - 1000, - ), - ( - TransactionTypeArg::ModifyGlobalBoundedAggV2.materialize_default(), - 1000, - ), - ( - TransactionTypeArg::ResourceGroupsGlobalWriteTag1KB.materialize_default(), - 1000, - ), - ( - TransactionTypeArg::ResourceGroupsGlobalWriteAndReadTag1KB - .materialize_default(), - 1000, - ), - ( - TransactionTypeArg::TokenV1NFTMintAndTransferSequential - .materialize_default(), - 1000, - ), - ( - TransactionTypeArg::TokenV1FTMintAndTransfer.materialize_default(), - 10000, - ), - ]), - ) - .with_success_criteria( - SuccessCriteria::new(3000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn individual_workload_tests(test_name: String) -> ForgeConfig { - let job = EmitJobRequest::default().mode(EmitJobMode::MaxLoad { - mempool_backlog: 30000, - }); - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(5).unwrap()) - .with_initial_fullnode_count(3) - .add_network_test(PerformanceBenchmark) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 600.into(); - })) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.execution.processed_transactions_detailed_counters = true; - })) - .with_emit_job( - if test_name == "write_new_resource" { - let account_creation_type = TransactionType::AccountGeneration { - add_created_accounts_to_pool: true, - max_account_working_set: 20_000_000, - creation_balance: 200_000_000, - }; - let write_type = TransactionType::CallCustomModules { - entry_point: EntryPoints::BytesMakeOrChange { - data_length: Some(32), - }, - num_modules: 1, - use_account_pool: true, - }; - job.transaction_mix_per_phase(vec![ - // warmup - vec![(account_creation_type, 1)], - vec![(account_creation_type, 1)], - vec![(write_type, 1)], - // cooldown - vec![(write_type, 1)], - ]) - } else { - job.transaction_type(match test_name.as_str() { - "account_creation" => { - TransactionTypeArg::AccountGeneration.materialize_default() - }, - "publishing" => TransactionTypeArg::PublishPackage.materialize_default(), - "module_loading" => TransactionTypeArg::NoOp.materialize( - 1000, - false, - WorkflowProgress::when_done_default(), - ), - _ => unreachable!("{}", test_name), - }) - }, - ) - .with_success_criteria( - SuccessCriteria::new(match test_name.as_str() { - "account_creation" => 3600, - "publishing" => 60, - "write_new_resource" => 3700, - "module_loading" => 1800, - _ => unreachable!("{}", test_name), - }) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn fullnode_reboot_stress_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(7) - .add_network_test(FullNodeRebootStressTest) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::ConstTps { tps: 5000 })) - .with_success_criteria(SuccessCriteria::new(2000).add_wait_for_catchup_s(600)) -} - -fn validator_reboot_stress_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(1) - .add_network_test(ValidatorRebootStressTest { - num_simultaneously: 2, - down_time_secs: 5.0, - pause_secs: 5.0, - }) - .with_success_criteria(SuccessCriteria::new(2000).add_wait_for_catchup_s(600)) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 120.into(); - })) -} - -fn apply_config_for_quorum_store_single_node(config: &mut NodeConfig) { - config - .consensus - .quorum_store - .back_pressure - .dynamic_max_txn_per_s = 5500; -} - -fn single_vfn_perf() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(1).unwrap()) - .with_initial_fullnode_count(1) - .add_network_test(PerformanceBenchmark) - .with_success_criteria( - SuccessCriteria::new(5000) - .add_no_restarts() - .add_wait_for_catchup_s(240), - ) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - apply_config_for_quorum_store_single_node(config); - })) -} - -fn setup_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(1).unwrap()) - .with_initial_fullnode_count(1) - .add_network_test(ForgeSetupTest) -} - -fn network_bandwidth() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(8).unwrap()) - .add_network_test(NetworkBandwidthTest) -} - -fn gather_metrics() -> ForgeConfig { - ForgeConfig::default() - .add_network_test(GatherMetrics) - .add_network_test(Delay::new(180)) - .add_network_test(GatherMetrics) -} - -/// Creates a netbench configuration for direct send using -/// the specified message size and frequency. -fn create_direct_send_netbench_config( - message_size: usize, - message_frequency: u64, -) -> NetbenchConfig { - // Create the netbench config - let mut netbench_config = NetbenchConfig::default(); - - // Enable direct send network benchmarking - netbench_config.enabled = true; - netbench_config.enable_direct_send_testing = true; - - // Configure the message sizes and frequency - netbench_config.direct_send_data_size = message_size; - netbench_config.direct_send_per_second = message_frequency; - netbench_config.max_network_channel_size = message_frequency * 2; // Double the channel size for an additional buffer - - netbench_config -} - -/// Performs direct send network benchmarking between 2 validators -/// using the specified message size and frequency. -fn net_bench_no_chaos(message_size: usize, message_frequency: u64) -> ForgeConfig { - ForgeConfig::default() - .add_network_test(Delay::new(180)) - .with_initial_validator_count(NonZeroUsize::new(2).unwrap()) - .with_validator_override_node_config_fn(Arc::new(move |config, _| { - let netbench_config = - create_direct_send_netbench_config(message_size, message_frequency); - config.netbench = Some(netbench_config); - })) -} - -/// Performs direct send network benchmarking between 2 validators -/// using the specified message size and frequency, with two-region chaos. -fn net_bench_two_region_chaos(message_size: usize, message_frequency: u64) -> ForgeConfig { - net_bench_two_region_inner(create_direct_send_netbench_config( - message_size, - message_frequency, - )) -} - -/// A simple utility function for creating a ForgeConfig with a -/// two-region environment using the specified netbench config. -fn net_bench_two_region_inner(netbench_config: NetbenchConfig) -> ForgeConfig { - ForgeConfig::default() - .add_network_test(wrap_with_two_region_env(Delay::new(180))) - .with_initial_validator_count(NonZeroUsize::new(2).unwrap()) - .with_validator_override_node_config_fn(Arc::new(move |config, _| { - config.netbench = Some(netbench_config); - })) -} - -fn three_region_simulation_with_different_node_speed() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(30).unwrap()) - .with_initial_fullnode_count(30) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::ConstTps { tps: 5000 })) - .add_network_test(CompositeNetworkTest::new( - ExecutionDelayTest { - add_execution_delay: ExecutionDelayConfig { - inject_delay_node_fraction: 0.5, - inject_delay_max_transaction_percentage: 40, - inject_delay_per_transaction_ms: 2, - }, - }, - ThreeRegionSameCloudSimulationTest, - )) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.api.failpoints_enabled = true; - })) - .with_fullnode_override_node_config_fn(Arc::new(|config, _| { - state_sync_config_execute_transactions(&mut config.state_sync); - })) - .with_success_criteria( - SuccessCriteria::new(1000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn three_region_simulation() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(12).unwrap()) - .with_initial_fullnode_count(12) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::ConstTps { tps: 5000 })) - .add_network_test(ThreeRegionSameCloudSimulationTest) - // TODO(rustielin): tune these success criteria after we have a better idea of the test behavior - .with_success_criteria( - SuccessCriteria::new(3000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn network_partition() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(10).unwrap()) - .add_network_test(NetworkPartitionTest) - .with_success_criteria( - SuccessCriteria::new(2500) - .add_no_restarts() - .add_wait_for_catchup_s(240), - ) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - apply_config_for_quorum_store_single_node(config); - })) -} - -fn compat() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(4).unwrap()) - .add_network_test(SimpleValidatorUpgrade) - .with_success_criteria(SuccessCriteria::new(5000).add_wait_for_catchup_s(240)) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 30.into(); - })) -} - -fn framework_upgrade() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(4).unwrap()) - .add_network_test(FrameworkUpgrade) - .with_success_criteria(SuccessCriteria::new(5000).add_wait_for_catchup_s(240)) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 30.into(); - })) -} - -fn epoch_changer_performance() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(5).unwrap()) - .with_initial_fullnode_count(2) - .add_network_test(PerformanceBenchmark) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 60.into(); - })) -} - -/// A default config for running various state sync performance tests -fn state_sync_perf_fullnodes_config() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(4).unwrap()) - .with_initial_fullnode_count(4) -} - -/// The config for running a state sync performance test when applying -/// transaction outputs in fullnodes. -fn state_sync_perf_fullnodes_apply_outputs() -> ForgeConfig { - state_sync_perf_fullnodes_config() - .add_network_test(StateSyncFullnodePerformance) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 600.into(); - })) - .with_fullnode_override_node_config_fn(Arc::new(|config, _| { - state_sync_config_apply_transaction_outputs(&mut config.state_sync); - })) - .with_success_criteria(SuccessCriteria::new(9000)) -} - -/// The config for running a state sync performance test when executing -/// transactions in fullnodes. -fn state_sync_perf_fullnodes_execute_transactions() -> ForgeConfig { - state_sync_perf_fullnodes_config() - .add_network_test(StateSyncFullnodePerformance) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 600.into(); - })) - .with_fullnode_override_node_config_fn(Arc::new(|config, _| { - state_sync_config_execute_transactions(&mut config.state_sync); - })) - .with_success_criteria(SuccessCriteria::new(5000)) -} - -/// The config for running a state sync performance test when fast syncing -/// to the latest epoch. -fn state_sync_perf_fullnodes_fast_sync() -> ForgeConfig { - state_sync_perf_fullnodes_config() - .add_network_test(StateSyncFullnodeFastSyncPerformance) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 180.into(); // Frequent epochs - })) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 30000, - }) - .transaction_type(TransactionTypeArg::AccountGeneration.materialize_default()), // Create many state values - ) - .with_fullnode_override_node_config_fn(Arc::new(|config, _| { - state_sync_config_fast_sync(&mut config.state_sync); - })) -} - -/// The config for running a state sync performance test when applying -/// transaction outputs in failed validators. -fn state_sync_perf_validators() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 600.into(); - })) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - state_sync_config_apply_transaction_outputs(&mut config.state_sync); - })) - .add_network_test(StateSyncValidatorPerformance) - .with_success_criteria(SuccessCriteria::new(5000)) -} - -/// The config for running a validator join and leave test. -fn validators_join_and_leave() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 60.into(); - helm_values["chain"]["allow_new_validators"] = true.into(); - })) - .add_network_test(ValidatorJoinLeaveTest) - .with_success_criteria( - SuccessCriteria::new(5000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_system_metrics_threshold(SYSTEM_12_CORES_10GB_THRESHOLD.clone()) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -fn land_blocking_test_suite(duration: Duration) -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_initial_fullnode_count(10) - .add_network_test(PerformanceBenchmark) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // Have single epoch change in land blocking - helm_values["chain"]["epoch_duration_secs"] = 300.into(); - })) - .with_success_criteria( - SuccessCriteria::new( - if duration.as_secs() > 1200 { - 4500 - } else { - 5000 - }, - ) - .add_no_restarts() - .add_wait_for_catchup_s( - // Give at least 60s for catchup, give 10% of the run for longer durations. - (duration.as_secs() / 10).max(60), - ) - .add_system_metrics_threshold(SYSTEM_12_CORES_10GB_THRESHOLD.clone()) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -// TODO: Replace land_blocking when performance reaches on par with current land_blocking -fn realistic_env_max_load_test( - duration: Duration, - test_cmd: &TestCommand, - num_validators: usize, - num_fullnodes: usize, -) -> ForgeConfig { - // Check if HAProxy is enabled - let ha_proxy = if let TestCommand::K8sSwarm(k8s) = test_cmd { - k8s.enable_haproxy - } else { - false - }; - - // Determine if this is a long running test - let duration_secs = duration.as_secs(); - let long_running = duration_secs >= 2400; - - // Create the test - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(num_validators).unwrap()) - .with_initial_fullnode_count(num_fullnodes) - .add_network_test(wrap_with_realistic_env(TwoTrafficsTest { - inner_traffic: EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 40000, - }) - .init_gas_price_multiplier(20), - inner_success_criteria: SuccessCriteria::new( - if ha_proxy { - 4600 - } else if long_running { - // This is for forge stable - 7000 - } else { - // During land time we want to be less strict, otherwise we flaky fail - 6500 - }, - ), - })) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - // Have single epoch change in land blocking, and a few on long-running - helm_values["chain"]["epoch_duration_secs"] = - (if long_running { 600 } else { 300 }).into(); - helm_values["chain"]["on_chain_consensus_config"] = - serde_yaml::to_value(OnChainConsensusConfig::default_for_genesis()) - .expect("must serialize"); - helm_values["chain"]["on_chain_execution_config"] = - serde_yaml::to_value(OnChainExecutionConfig::default_for_genesis()) - .expect("must serialize"); - })) - // First start higher gas-fee traffic, to not cause issues with TxnEmitter setup - account creation - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 100 }) - .gas_price(5 * aptos_global_constants::GAS_UNIT_PRICE) - .latency_polling_interval(Duration::from_millis(100)), - ) - .with_success_criteria( - SuccessCriteria::new(95) - .add_no_restarts() - .add_wait_for_catchup_s( - // Give at least 60s for catchup, give 10% of the run for longer durations. - (duration.as_secs() / 10).max(60), - ) - .add_latency_threshold(3.4, LatencyType::P50) - .add_latency_threshold(4.5, LatencyType::P90) - .add_latency_breakdown_threshold(LatencyBreakdownThreshold::new_with_breach_pct( - vec![ - (LatencyBreakdownSlice::QsBatchToPos, 0.35), - // only reaches close to threshold during epoch change - ( - LatencyBreakdownSlice::QsPosToProposal, - if ha_proxy { 0.7 } else { 0.6 }, - ), - // can be adjusted down if less backpressure - (LatencyBreakdownSlice::ConsensusProposalToOrdered, 0.85), - // can be adjusted down if less backpressure - ( - LatencyBreakdownSlice::ConsensusOrderedToCommit, - if ha_proxy { 1.3 } else { 0.75 }, - ), - ], - 5, - )) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 15.0, - max_round_gap: 4, - }), - ) -} - -fn realistic_network_tuned_for_throughput_test() -> ForgeConfig { - // THE MOST COMMONLY USED TUNE-ABLES: - const USE_CRAZY_MACHINES: bool = false; - const ENABLE_VFNS: bool = true; - const VALIDATOR_COUNT: usize = 12; - - // Config is based on these values. The target TPS should be a slight overestimate of - // the actual throughput to be able to have reasonable queueing but also so throughput - // will improve as performance improves. - // Overestimate: causes mempool and/or batch queueing. Underestimate: not enough txns in blocks. - const TARGET_TPS: usize = 15_000; - // Overestimate: causes blocks to be too small. Underestimate: causes blocks that are too large. - // Ideally, want the block size to take 200-250ms of execution time to match broadcast RTT. - const MAX_TXNS_PER_BLOCK: usize = 3500; - // Overestimate: causes batch queueing. Underestimate: not enough txns in quorum store. - // This is validator latency, minus mempool queueing time. - const VN_LATENCY_S: f64 = 2.5; - // Overestimate: causes mempool queueing. Underestimate: not enough txns incoming. - const VFN_LATENCY_S: f64 = 4.0; - - let mut forge_config = ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(VALIDATOR_COUNT).unwrap()) - .add_network_test(MultiRegionNetworkEmulationTest::default()) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::MaxLoad { - mempool_backlog: (TARGET_TPS as f64 * VFN_LATENCY_S) as usize, - })) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - // Increase the state sync chunk sizes (consensus blocks are much larger than 1k) - optimize_state_sync_for_throughput(config); - - optimize_for_maximum_throughput(config, TARGET_TPS, MAX_TXNS_PER_BLOCK, VN_LATENCY_S); - - // Other consensus / Quroum store configs - config.consensus.quorum_store_pull_timeout_ms = 200; - - // Experimental storage optimizations - config.storage.rocksdb_configs.enable_storage_sharding = true; - - // Increase the concurrency level - if USE_CRAZY_MACHINES { - config.execution.concurrency_level = 48; - } - })) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - let mut on_chain_execution_config = OnChainExecutionConfig::default_for_genesis(); - // Need to update if the default changes - match &mut on_chain_execution_config { - OnChainExecutionConfig::Missing - | OnChainExecutionConfig::V1(_) - | OnChainExecutionConfig::V2(_) - | OnChainExecutionConfig::V3(_) => { - unreachable!("Unexpected on-chain execution config type, if OnChainExecutionConfig::default_for_genesis() has been updated, this test must be updated too.") - } - OnChainExecutionConfig::V4(config_v4) => { - config_v4.block_gas_limit_type = BlockGasLimitType::NoLimit; - config_v4.transaction_shuffler_type = TransactionShufflerType::Fairness { - sender_conflict_window_size: 256, - module_conflict_window_size: 2, - entry_fun_conflict_window_size: 3, - }; - } - } - helm_values["chain"]["on_chain_execution_config"] = - serde_yaml::to_value(on_chain_execution_config).expect("must serialize"); - })); - - if ENABLE_VFNS { - forge_config = forge_config - .with_initial_fullnode_count(VALIDATOR_COUNT) - .with_fullnode_override_node_config_fn(Arc::new(|config, _| { - // Increase the state sync chunk sizes (consensus blocks are much larger than 1k) - optimize_state_sync_for_throughput(config); - - // Experimental storage optimizations - config.storage.rocksdb_configs.enable_storage_sharding = true; - - // Increase the concurrency level - if USE_CRAZY_MACHINES { - config.execution.concurrency_level = 48; - } - })); - } - - if USE_CRAZY_MACHINES { - forge_config = forge_config - .with_validator_resource_override(NodeResourceOverride { - cpu_cores: Some(58), - memory_gib: Some(200), - }) - .with_fullnode_resource_override(NodeResourceOverride { - cpu_cores: Some(58), - memory_gib: Some(200), - }) - .with_success_criteria( - SuccessCriteria::new(25000) - .add_no_restarts() - /* This test runs at high load, so we need more catchup time */ - .add_wait_for_catchup_s(120), - /* Doesn't work without event indices - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - */ - ); - } else { - forge_config = forge_config.with_success_criteria( - SuccessCriteria::new(12000) - .add_no_restarts() - /* This test runs at high load, so we need more catchup time */ - .add_wait_for_catchup_s(120), - /* Doesn't work without event indices - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - */ - ); - } - - forge_config -} - -/// Optimizes the state sync configs for throughput -fn optimize_state_sync_for_throughput(node_config: &mut NodeConfig) { - let max_chunk_size = 15_000; // This allows state sync to match consensus block sizes - let max_chunk_bytes = 40 * 1024 * 1024; // 10x the current limit (to prevent execution fallback) - - // Update the chunk sizes for the data client - let data_client_config = &mut node_config.state_sync.aptos_data_client; - data_client_config.max_transaction_chunk_size = max_chunk_size; - data_client_config.max_transaction_output_chunk_size = max_chunk_size; - // Update the chunk sizes for the storage service - let storage_service_config = &mut node_config.state_sync.storage_service; - storage_service_config.max_transaction_chunk_size = max_chunk_size; - storage_service_config.max_transaction_output_chunk_size = max_chunk_size; - - // Update the chunk bytes for the storage service - storage_service_config.max_network_chunk_bytes = max_chunk_bytes; -} - -fn pre_release_suite() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(30).unwrap()) - .add_network_test(NetworkBandwidthTest) -} - -fn chaos_test_suite(duration: Duration) -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(30).unwrap()) - .add_network_test(NetworkBandwidthTest) - .add_network_test(ThreeRegionSameCloudSimulationTest) - .add_network_test(NetworkLossTest) - .with_success_criteria( - SuccessCriteria::new( - if duration > Duration::from_secs(1200) { - 100 - } else { - 1000 - }, - ) - .add_no_restarts() - .add_system_metrics_threshold(SYSTEM_12_CORES_5GB_THRESHOLD.clone()), - ) -} - -pub fn changing_working_quorum_test_helper( - num_validators: usize, - epoch_duration: usize, - target_tps: usize, - min_avg_tps: usize, - apply_txn_outputs: bool, - use_chain_backoff: bool, - test: ChangingWorkingQuorumTest, -) -> ForgeConfig { - let config = ForgeConfig::default(); - let num_large_validators = test.num_large_validators; - let max_down_nodes = test.max_down_nodes; - config - .with_initial_validator_count(NonZeroUsize::new(num_validators).unwrap()) - .with_initial_fullnode_count( - if max_down_nodes == 0 { - 0 - } else { - std::cmp::max(2, target_tps / 1000) - }, - ) - .add_network_test(test) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - helm_values["chain"]["epoch_duration_secs"] = epoch_duration.into(); - helm_values["genesis"]["validator"]["num_validators_with_larger_stake"] = - num_large_validators.into(); - })) - .with_validator_override_node_config_fn(Arc::new(move |config, _| { - config.api.failpoints_enabled = true; - let block_size = (target_tps / 4) as u64; - - config.consensus.max_sending_block_txns = block_size; - config.consensus.max_receiving_block_txns = block_size; - config.consensus.round_initial_timeout_ms = 500; - config.consensus.round_timeout_backoff_exponent_base = 1.0; - config.consensus.quorum_store_poll_time_ms = 100; - - let mut min_block_txns = block_size; - let mut chain_health_backoff = ConsensusConfig::default().chain_health_backoff; - if use_chain_backoff { - // Generally if we are stress testing the consensus, we don't want to slow it down. - chain_health_backoff = vec![]; - } else { - for (i, item) in chain_health_backoff.iter_mut().enumerate() { - // as we have lower TPS, make limits smaller - item.max_sending_block_txns_override = - (block_size / 2_u64.pow(i as u32 + 1)).max(2); - min_block_txns = min_block_txns.min(item.max_sending_block_txns_override); - // as we have fewer nodes, make backoff triggered earlier: - item.backoff_if_below_participating_voting_power_percentage = 90 - i * 5; - } - } - config.consensus.quorum_store.sender_max_batch_txns = min_block_txns as usize; - config.consensus.quorum_store.receiver_max_batch_txns = min_block_txns as usize; - - config.consensus.chain_health_backoff = chain_health_backoff; - - // Override the syncing mode of all nodes to use transaction output syncing. - // TODO(joshlind): remove me once we move back to output syncing by default. - if apply_txn_outputs { - state_sync_config_apply_transaction_outputs(&mut config.state_sync); - } - })) - .with_fullnode_override_node_config_fn(Arc::new(move |config, _| { - // Override the syncing mode of all nodes to use transaction output syncing. - // TODO(joshlind): remove me once we move back to output syncing by default. - if apply_txn_outputs { - state_sync_config_apply_transaction_outputs(&mut config.state_sync); - } - })) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: target_tps }) - .transaction_mix(vec![ - (TransactionTypeArg::CoinTransfer.materialize_default(), 80), - ( - TransactionTypeArg::AccountGeneration.materialize_default(), - 20, - ), - ]), - ) - .with_success_criteria( - SuccessCriteria::new(min_avg_tps) - .add_no_restarts() - .add_wait_for_catchup_s(30) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: if max_down_nodes == 0 { - // very aggressive if no nodes are expected to be down - 3.0 - } else if max_down_nodes * 3 + 1 + 2 < num_validators { - // number of down nodes is at least 2 below the quorum limit, so - // we can still be reasonably aggressive - 15.0 - } else { - // number of down nodes is close to the quorum limit, so - // make a check a bit looser, as state sync might be required - // to get the quorum back. - 40.0 - }, - max_round_gap: 6, - }), - ) -} - -fn large_db_test( - num_validators: usize, - target_tps: usize, - min_avg_tps: usize, - existing_db_tag: String, -) -> ForgeConfig { - let config = ForgeConfig::default(); - config - .with_initial_validator_count(NonZeroUsize::new(num_validators).unwrap()) - .with_initial_fullnode_count(std::cmp::max(2, target_tps / 1000)) - .add_network_test(PerformanceBenchmark) - .with_existing_db(existing_db_tag.clone()) - .with_validator_override_node_config_fn(Arc::new(move |config, _| { - config.base.working_dir = Some(PathBuf::from("/opt/aptos/data/checkpoint")); - })) - .with_fullnode_override_node_config_fn(Arc::new(move |config, _| { - config.base.working_dir = Some(PathBuf::from("/opt/aptos/data/checkpoint")); - })) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: target_tps }) - .transaction_mix(vec![ - (TransactionTypeArg::CoinTransfer.materialize_default(), 75), - ( - TransactionTypeArg::AccountGeneration.materialize_default(), - 20, - ), - ( - TransactionTypeArg::TokenV1NFTMintAndTransferSequential - .materialize_default(), - 5, - ), - ]), - ) - .with_success_criteria( - SuccessCriteria::new(min_avg_tps) - .add_no_restarts() - .add_wait_for_catchup_s(30) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -fn quorum_store_reconfig_enable_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_initial_fullnode_count(20) - .add_network_test(QuorumStoreOnChainEnableTest {}) - .with_success_criteria( - SuccessCriteria::new(5000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_system_metrics_threshold(SYSTEM_12_CORES_10GB_THRESHOLD.clone()) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -fn mainnet_like_simulation_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 200_000, - }) - .txn_expiration_time_secs(5 * 60), - ) - .add_network_test(CompositeNetworkTest::new( - MultiRegionNetworkEmulationTest::default(), - CpuChaosTest::default(), - )) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // no epoch change. - helm_values["chain"]["epoch_duration_secs"] = (24 * 3600).into(); - })) - // TODO(ibalajiarun): tune these success critiera after we have a better idea of the test behavior - .with_success_criteria( - SuccessCriteria::new(10000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 6, - }), - ) -} - -/// This test runs a network test in a real multi-region setup. It configures -/// genesis and node helm values to enable certain configurations needed to run in -/// the multiregion forge cluster. -fn multiregion_benchmark_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .add_network_test(PerformanceBenchmark) - .with_genesis_helm_config_fn(Arc::new(|helm_values| { - // Have single epoch change in land blocking - helm_values["chain"]["epoch_duration_secs"] = 300.into(); - - helm_values["genesis"]["multicluster"]["enabled"] = true.into(); - })) - .with_multi_region_config() - .with_success_criteria( - SuccessCriteria::new(4500) - .add_no_restarts() - .add_wait_for_catchup_s( - // Give at least 60s for catchup, give 10% of the run for longer durations. - 180, - ) - .add_system_metrics_threshold(SYSTEM_12_CORES_10GB_THRESHOLD.clone()) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -/// This test runs a constant-TPS benchmark where the network includes -/// PFNs, and the transactions are submitted to the PFNs. This is useful -/// for measuring latencies when the system is not saturated. -/// -/// Note: If `add_cpu_chaos` is true, CPU chaos is enabled on the entire swarm. -/// Likewise, if `add_network_emulation` is true, network chaos is enabled. -fn pfn_const_tps( - duration: Duration, - add_cpu_chaos: bool, - add_network_emulation: bool, - epoch_changes: bool, -) -> ForgeConfig { - let epoch_duration_secs = if epoch_changes { - 300 // 5 minutes - } else { - 60 * 60 * 2 // 2 hours; avoid epoch changes which can introduce noise - }; - - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(7) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::ConstTps { tps: 100 })) - .add_network_test(PFNPerformance::new(7, add_cpu_chaos, add_network_emulation)) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - helm_values["chain"]["epoch_duration_secs"] = epoch_duration_secs.into(); - })) - .with_success_criteria( - SuccessCriteria::new(95) - .add_no_restarts() - .add_max_expired_tps(0) - .add_max_failed_submission_tps(0) - // Percentile thresholds are set to +1 second of non-PFN tests. Should be revisited. - .add_latency_threshold(2.5, LatencyType::P50) - .add_latency_threshold(4., LatencyType::P90) - .add_latency_threshold(5., LatencyType::P99) - .add_wait_for_catchup_s( - // Give at least 60s for catchup and at most 10% of the run - (duration.as_secs() / 10).max(60), - ) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -/// This test runs a performance benchmark where the network includes -/// PFNs, and the transactions are submitted to the PFNs. This is useful -/// for measuring maximum throughput and latencies. -/// -/// Note: If `add_cpu_chaos` is true, CPU chaos is enabled on the entire swarm. -/// Likewise, if `add_network_emulation` is true, network chaos is enabled. -fn pfn_performance( - duration: Duration, - add_cpu_chaos: bool, - add_network_emulation: bool, - epoch_changes: bool, -) -> ForgeConfig { - // Determine the minimum expected TPS - let min_expected_tps = 4500; - let epoch_duration_secs = if epoch_changes { - 300 // 5 minutes - } else { - 60 * 60 * 2 // 2 hours; avoid epoch changes which can introduce noise - }; - - // Create the forge config - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) - .with_initial_fullnode_count(7) - .add_network_test(PFNPerformance::new(7, add_cpu_chaos, add_network_emulation)) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - helm_values["chain"]["epoch_duration_secs"] = epoch_duration_secs.into(); - })) - .with_success_criteria( - SuccessCriteria::new(min_expected_tps) - .add_no_restarts() - .add_wait_for_catchup_s( - // Give at least 60s for catchup and at most 10% of the run - (duration.as_secs() / 10).max(60), - ) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 10.0, - max_round_gap: 4, - }), - ) -} - -/// A simple test that runs the swarm forever. This is useful for -/// local testing (e.g., deploying a local swarm and interacting -/// with it). -#[derive(Debug)] -struct RunForever; - -impl Test for RunForever { - fn name(&self) -> &'static str { - "run_forever" - } -} - -#[async_trait::async_trait] -impl AptosTest for RunForever { - async fn run<'t>(&self, _ctx: &mut AptosContext<'t>) -> Result<()> { - println!("The network has been deployed. Hit Ctrl+C to kill this, otherwise it will run forever."); - let keep_running = Arc::new(AtomicBool::new(true)); - while keep_running.load(Ordering::Acquire) { - thread::park(); - } - Ok(()) - } -} - -//TODO Make public test later -#[derive(Debug)] -struct GetMetadata; - -impl Test for GetMetadata { - fn name(&self) -> &'static str { - "get_metadata" - } -} - -impl AdminTest for GetMetadata { - fn run(&self, ctx: &mut AdminContext<'_>) -> Result<()> { - let client = ctx.rest_client(); - let runtime = Runtime::new().unwrap(); - runtime.block_on(client.get_aptos_version()).unwrap(); - runtime.block_on(client.get_ledger_information()).unwrap(); - - Ok(()) - } -} - -pub async fn check_account_balance( - client: &RestClient, - account_address: AccountAddress, - expected: u64, -) -> Result<()> { - let balance = client - .get_account_balance(account_address) - .await? - .into_inner(); - assert_eq!(balance.get(), expected); - - Ok(()) -} - -#[derive(Debug)] -struct FundAccount; - -impl Test for FundAccount { - fn name(&self) -> &'static str { - "fund_account" - } -} - -#[async_trait::async_trait] -impl AptosTest for FundAccount { - async fn run<'t>(&self, ctx: &mut AptosContext<'t>) -> Result<()> { - let client = ctx.client(); - - let account = ctx.random_account(); - let amount = 1000; - ctx.create_user_account(account.public_key()).await?; - ctx.mint(account.address(), amount).await?; - check_account_balance(&client, account.address(), amount).await?; - - Ok(()) - } -} - -#[derive(Debug)] -struct TransferCoins; - -impl Test for TransferCoins { - fn name(&self) -> &'static str { - "transfer_coins" - } -} - -#[async_trait::async_trait] -impl AptosTest for TransferCoins { - async fn run<'t>(&self, ctx: &mut AptosContext<'t>) -> Result<()> { - let client = ctx.client(); - let payer = ctx.random_account(); - let payee = ctx.random_account(); - ctx.create_user_account(payer.public_key()).await?; - ctx.create_user_account(payee.public_key()).await?; - ctx.mint(payer.address(), 10000).await?; - check_account_balance(&client, payer.address(), 10000).await?; - - let transfer_txn = payer.sign_with_transaction_builder( - ctx.aptos_transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(payee.address(), 10)), - ); - client.submit_and_wait(&transfer_txn).await?; - check_account_balance(&client, payee.address(), 10).await?; - - Ok(()) - } -} - -#[derive(Debug)] -struct RestartValidator; - -impl Test for RestartValidator { - fn name(&self) -> &'static str { - "restart_validator" - } -} - -impl NetworkTest for RestartValidator { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = Runtime::new()?; - runtime.block_on(async { - let node = ctx.swarm().validators_mut().next().unwrap(); - node.health_check().await.expect("node health check failed"); - node.stop().await.unwrap(); - println!("Restarting node {}", node.peer_id()); - node.start().await.unwrap(); - tokio::time::sleep(Duration::from_secs(1)).await; - node.health_check().await.expect("node health check failed"); - }); - Ok(()) - } -} - -#[derive(Debug)] -struct EmitTransaction; - -impl Test for EmitTransaction { - fn name(&self) -> &'static str { - "emit_transaction" - } -} - -impl NetworkTest for EmitTransaction { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let duration = Duration::from_secs(10); - let all_validators = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - let stats = generate_traffic(ctx, &all_validators, duration).unwrap(); - ctx.report.report_txn_stats(self.name().to_string(), &stats); - - Ok(()) - } -} - -#[derive(Debug)] -struct Delay { - seconds: u64, -} - -impl Delay { - fn new(seconds: u64) -> Self { - Self { seconds } - } -} - -impl Test for Delay { - fn name(&self) -> &'static str { - "delay" - } -} - -impl NetworkTest for Delay { - fn run(&self, _ctx: &mut NetworkContext<'_>) -> Result<()> { - info!("forge sleep {}", self.seconds); - std::thread::sleep(Duration::from_secs(self.seconds)); - Ok(()) - } -} - -#[derive(Debug)] -struct GatherMetrics; - -impl Test for GatherMetrics { - fn name(&self) -> &'static str { - "gather_metrics" - } -} - -impl NetworkTest for GatherMetrics { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = ctx.runtime.handle(); - runtime.block_on(gather_metrics_one(ctx)); - Ok(()) - } -} - -async fn gather_metrics_one(ctx: &NetworkContext<'_>) { - let handle = ctx.runtime.handle(); - let outdir = Path::new("/tmp"); - let mut gets = FuturesUnordered::new(); - let now = chrono::prelude::Utc::now() - .format("%Y%m%d_%H%M%S") - .to_string(); - for val in ctx.swarm.validators() { - let mut url = val.inspection_service_endpoint(); - let valname = val.peer_id().to_string(); - url.set_path("metrics"); - let fname = format!("{}.{}.metrics", now, valname); - let outpath: PathBuf = outdir.join(fname); - let th = handle.spawn(gather_metrics_to_file(url, outpath)); - gets.push(th); - } - // join all the join handles - while !gets.is_empty() { - select! { - _ = gets.next() => {} - } - } -} - -async fn gather_metrics_to_file(url: Url, outpath: PathBuf) { - let client = reqwest::Client::new(); - match client.get(url).send().await { - Ok(response) => { - let url = response.url().clone(); - let status = response.status(); - if status.is_success() { - match response.text().await { - Ok(text) => match std::fs::write(outpath, text) { - Ok(_) => {}, - Err(err) => { - info!("could not write metrics: {}", err); - }, - }, - Err(err) => { - info!("bad metrics GET: {} -> {}", url, err); - }, - } - } else { - info!("bad metrics GET: {} -> {}", url, status); - } - }, - Err(err) => { - info!("bad metrics GET: {}", err); - }, - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_random_namespace() { - let mut rng = rand::rngs::mock::StepRng::new(100, 1); - let words = ["apple", "banana", "carrot", "durian", "eggplant", "fig"] - .to_vec() - .iter() - .map(|s| s.to_string()) - .collect::>(); - let namespace = random_namespace(words, &mut rng).unwrap(); - assert_eq!(namespace, "forge-durian-eggplant-fig-apple"); - } - - #[test] - fn verify_tool() { - use clap::CommandFactory; - Args::command().debug_assert() - } -} diff --git a/testsuite/forge-cli/src/suites/dag.rs b/testsuite/forge-cli/src/suites/dag.rs deleted file mode 100644 index 73d4c747af28b..0000000000000 --- a/testsuite/forge-cli/src/suites/dag.rs +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{changing_working_quorum_test_helper, wrap_with_realistic_env, TestCommand}; -use aptos_forge::{ - success_criteria::{LatencyType, StateProgressThreshold, SuccessCriteria}, - EmitJobMode, EmitJobRequest, ForgeConfig, -}; -use aptos_sdk::types::on_chain_config::{ - BlockGasLimitType, ConsensusAlgorithmConfig, DagConsensusConfigV1, OnChainConsensusConfig, - OnChainExecutionConfig, TransactionShufflerType, ValidatorTxnConfig, -}; -use aptos_testcases::{ - consensus_reliability_tests::ChangingWorkingQuorumTest, - dag_onchain_enable_test::DagOnChainEnableTest, two_traffics_test::TwoTrafficsTest, -}; -use std::{num::NonZeroUsize, sync::Arc, time::Duration}; - -pub fn get_dag_test( - test_name: &str, - duration: Duration, - test_cmd: &TestCommand, -) -> Option { - get_dag_on_realistic_env_test(test_name, duration, test_cmd) -} - -/// Attempts to match the test name to a dag-realistic-env test -fn get_dag_on_realistic_env_test( - test_name: &str, - duration: Duration, - test_cmd: &TestCommand, -) -> Option { - let test = match test_name { - "dag_realistic_env_max_load" => dag_realistic_env_max_load_test(duration, test_cmd, 20, 0), - "dag_changing_working_quorum_test" => dag_changing_working_quorum_test(), - "dag_reconfig_enable_test" => dag_reconfig_enable_test(), - _ => return None, // The test name does not match a dag realistic-env test - }; - Some(test) -} - -fn dag_realistic_env_max_load_test( - duration: Duration, - test_cmd: &TestCommand, - num_validators: usize, - num_fullnodes: usize, -) -> ForgeConfig { - // Check if HAProxy is enabled - let ha_proxy = if let TestCommand::K8sSwarm(k8s) = test_cmd { - k8s.enable_haproxy - } else { - false - }; - - // Determine if this is a long running test - let duration_secs = duration.as_secs(); - let long_running = duration_secs >= 2400; - - // Create the test - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(num_validators).unwrap()) - .with_initial_fullnode_count(num_fullnodes) - .add_network_test(wrap_with_realistic_env(TwoTrafficsTest { - inner_traffic: EmitJobRequest::default() - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 50000, - }) - .init_gas_price_multiplier(20), - inner_success_criteria: SuccessCriteria::new( - if ha_proxy { - 2000 - } else if long_running { - // This is for forge stable - 2500 - } else { - // During land time we want to be less strict, otherwise we flaky fail - 2800 - }, - ), - })) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.consensus.max_sending_block_txns = 4000; - config.consensus.max_sending_block_bytes = 6 * 1024 * 1024; - config.consensus.max_receiving_block_txns = 10000; - config.consensus.max_receiving_block_bytes = 7 * 1024 * 1024; - })) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - // Have single epoch change in land blocking, and a few on long-running - helm_values["chain"]["epoch_duration_secs"] = - (if long_running { 600 } else { 300 }).into(); - - let onchain_consensus_config = OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::DAG(DagConsensusConfigV1::default()), - vtxn: ValidatorTxnConfig::default_for_genesis(), - }; - - helm_values["chain"]["on_chain_consensus_config"] = - serde_yaml::to_value(onchain_consensus_config).expect("must serialize"); - - let mut on_chain_execution_config = OnChainExecutionConfig::default_for_genesis(); - // Need to update if the default changes - match &mut on_chain_execution_config { - OnChainExecutionConfig::Missing - | OnChainExecutionConfig::V1(_) - | OnChainExecutionConfig::V2(_) - | OnChainExecutionConfig::V3(_) => { - unreachable!("Unexpected on-chain execution config type, if OnChainExecutionConfig::default_for_genesis() has been updated, this test must be updated too.") - } - OnChainExecutionConfig::V4(config_v4) => { - config_v4.block_gas_limit_type = BlockGasLimitType::NoLimit; - config_v4.transaction_shuffler_type = TransactionShufflerType::Fairness { - sender_conflict_window_size: 256, - module_conflict_window_size: 2, - entry_fun_conflict_window_size: 3, - }; - } - } - helm_values["chain"]["on_chain_execution_config"] = - serde_yaml::to_value(on_chain_execution_config).expect("must serialize"); - })) - // First start higher gas-fee traffic, to not cause issues with TxnEmitter setup - account creation - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 100 }) - .gas_price(5 * aptos_global_constants::GAS_UNIT_PRICE) - .latency_polling_interval(Duration::from_millis(100)), - ) - .with_success_criteria( - SuccessCriteria::new(95) - .add_no_restarts() - .add_wait_for_catchup_s( - // Give at least 60s for catchup, give 10% of the run for longer durations. - (duration.as_secs() / 10).max(60), - ) - .add_latency_threshold(4.0, LatencyType::P50) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 15.0, - max_round_gap: 8, - }), - ) -} - -fn dag_changing_working_quorum_test() -> ForgeConfig { - let epoch_duration = 120; - let num_large_validators = 0; - let base_config = changing_working_quorum_test_helper( - 16, - epoch_duration, - 100, - 70, - true, - true, - ChangingWorkingQuorumTest { - min_tps: 15, - always_healthy_nodes: 0, - max_down_nodes: 16, - num_large_validators, - add_execution_delay: false, - // Use longer check duration, as we are bringing enough nodes - // to require state-sync to catch up to have consensus. - check_period_s: 53, - }, - ); - - base_config - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.consensus.max_sending_block_txns = 4000; - config.consensus.max_sending_block_bytes = 6 * 1024 * 1024; - config.consensus.max_receiving_block_txns = 10000; - config.consensus.max_receiving_block_bytes = 7 * 1024 * 1024; - })) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - helm_values["chain"]["epoch_duration_secs"] = epoch_duration.into(); - helm_values["genesis"]["validator"]["num_validators_with_larger_stake"] = - num_large_validators.into(); - - let onchain_consensus_config = OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::DAG(DagConsensusConfigV1::default()), - vtxn: ValidatorTxnConfig::default_for_genesis(), - }; - - helm_values["chain"]["on_chain_consensus_config"] = - serde_yaml::to_value(onchain_consensus_config).expect("must serialize"); - helm_values["chain"]["on_chain_execution_config"] = - serde_yaml::to_value(OnChainExecutionConfig::default_for_genesis()) - .expect("must serialize"); - })) -} - -fn dag_reconfig_enable_test() -> ForgeConfig { - ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(20).unwrap()) - .with_initial_fullnode_count(20) - .add_network_test(DagOnChainEnableTest {}) - .with_validator_override_node_config_fn(Arc::new(|config, _| { - config.consensus.max_sending_block_txns = 4000; - config.consensus.max_sending_block_bytes = 6 * 1024 * 1024; - config.consensus.max_receiving_block_txns = 10000; - config.consensus.max_receiving_block_bytes = 7 * 1024 * 1024; - })) - .with_genesis_helm_config_fn(Arc::new(move |helm_values| { - let mut on_chain_execution_config = OnChainExecutionConfig::default_for_genesis(); - // Need to update if the default changes - match &mut on_chain_execution_config { - OnChainExecutionConfig::Missing - | OnChainExecutionConfig::V1(_) - | OnChainExecutionConfig::V2(_) - | OnChainExecutionConfig::V3(_) => { - unreachable!("Unexpected on-chain execution config type, if OnChainExecutionConfig::default_for_genesis() has been updated, this test must be updated too.") - } - OnChainExecutionConfig::V4(config_v4) => { - config_v4.block_gas_limit_type = BlockGasLimitType::NoLimit; - config_v4.transaction_shuffler_type = TransactionShufflerType::Fairness { - sender_conflict_window_size: 256, - module_conflict_window_size: 2, - entry_fun_conflict_window_size: 3, - }; - } - } - helm_values["chain"]["on_chain_execution_config"] = - serde_yaml::to_value(on_chain_execution_config).expect("must serialize"); - })) - .with_success_criteria( - SuccessCriteria::new(1000) - .add_no_restarts() - .add_wait_for_catchup_s(240) - .add_chain_progress(StateProgressThreshold { - max_no_progress_secs: 20.0, - max_round_gap: 20, - }), - ) -} diff --git a/testsuite/forge-cli/src/suites/mod.rs b/testsuite/forge-cli/src/suites/mod.rs deleted file mode 100644 index 8929b73df1561..0000000000000 --- a/testsuite/forge-cli/src/suites/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright © Aptos Foundation - -pub mod dag; diff --git a/testsuite/forge/Cargo.toml b/testsuite/forge/Cargo.toml deleted file mode 100644 index c423fe8c5a70e..0000000000000 --- a/testsuite/forge/Cargo.toml +++ /dev/null @@ -1,69 +0,0 @@ -[package] -name = "aptos-forge" -description = "Aptos end to end test framework" -version = "0.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -again = { workspace = true } -anyhow = { workspace = true, features = ["backtrace"] } -aptos = { workspace = true } -aptos-cached-packages = { workspace = true } -aptos-cli-common = { workspace = true } -aptos-config = { workspace = true } -aptos-db = { workspace = true } -aptos-framework = { workspace = true } -aptos-genesis = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-infallible = { workspace = true } -aptos-inspection-service = { workspace = true } -aptos-logger = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-retrier = { workspace = true } -aptos-sdk = { workspace = true } -aptos-secure-storage = { workspace = true } -aptos-short-hex-str = { workspace = true } -aptos-state-sync-driver = { workspace = true } -aptos-transaction-emitter-lib = { workspace = true } -aptos-transaction-generator-lib = { workspace = true } -async-trait = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true, features = ["env", "unstable-styles"] } -either = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -hyper = { workspace = true } -hyper-tls = { workspace = true } -itertools = { workspace = true } -json-patch = { workspace = true } -k8s-openapi = { version = "0.13.1", default-features = false, features = [ - "v1_22", -] } -kube = { version = "0.65.0", default-features = false, features = ["jsonpatch", "client", "rustls-tls", "derive"] } -num_cpus = { workspace = true } -once_cell = { workspace = true } -prometheus-http-query = { workspace = true } -rand = { workspace = true } -rayon = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -tempfile = { workspace = true } -termcolor = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } - -[features] -default = [] -testing = ["aptos-global-constants/testing"] diff --git a/testsuite/forge/src/backend/k8s/chaos.rs b/testsuite/forge/src/backend/k8s/chaos.rs deleted file mode 100644 index 3dfb3b3a4ef48..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - dump_string_to_file, K8sSwarm, Result, Swarm, SwarmChaos, SwarmCpuStress, SwarmNetEm, - SwarmNetworkBandwidth, SwarmNetworkDelay, SwarmNetworkLoss, SwarmNetworkPartition, KUBECTL_BIN, -}; -use anyhow::bail; -use aptos_logger::info; -use aptos_sdk::{move_types::account_address::AccountAddress, types::PeerId}; -use std::process::{Command, Stdio}; -use tempfile::TempDir; - -macro_rules! DELAY_NETWORK_CHAOS_TEMPLATE { - () => { - "chaos/network_delay.yaml" - }; -} -macro_rules! PARTITION_NETWORK_CHAOS_TEMPLATE { - () => { - "chaos/network_partition.yaml" - }; -} -macro_rules! BANDWIDTH_NETWORK_CHAOS_TEMPLATE { - () => { - "chaos/network_bandwidth.yaml" - }; -} - -macro_rules! NETWORK_LOSS_CHAOS_TEMPLATE { - () => { - "chaos/network_loss.yaml" - }; -} - -macro_rules! NETEM_CHAOS_TEMPLATE { - () => { - "chaos/netem.yaml" - }; -} - -macro_rules! CPU_STRESS_CHAOS_TEMPLATE { - () => { - "chaos/cpu_stress.yaml" - }; -} - -// The node name for an address that could not be found in the swarm -const INVALID_NODE_STRING: &str = "invalid-node"; - -impl K8sSwarm { - /// Injects the SwarmChaos into the specified namespace - pub fn inject_swarm_chaos(&self, chaos: &SwarmChaos) -> Result<()> { - let template = self.create_chaos_template(chaos)?; - info!("Injecting chaos: {}", template); - self.inject_chaos_template(template) - } - - /// Removes the SwarmChaos from the specified namespace, if it exists - /// Most types of Chaos are represented by a single NetworkChaos CRD, so we can just reconstruct - /// it and kubectl delete -f it. However, Delay Chaos is represented by however many pairwise delays there - /// are (GroupNetworkDelay ie region), so we need to delete each one individually. - pub fn remove_swarm_chaos(&self, chaos: &SwarmChaos) -> Result<()> { - match chaos { - SwarmChaos::Delay(network_delay) => { - for group in &network_delay.group_network_delays { - let delete_networkchaos = [ - "-n", - &self.kube_namespace, - "delete", - "networkchaos", - &group.name, - ]; - info!("{:?}", delete_networkchaos); - let delete_networkchaos_output = Command::new(KUBECTL_BIN) - .stdout(Stdio::inherit()) - .args(delete_networkchaos) - .output() - .expect("failed to delete all NetworkChaos"); - if !delete_networkchaos_output.status.success() { - bail!( - "{}", - String::from_utf8(delete_networkchaos_output.stderr).unwrap() - ); - } - } - Ok(()) - }, - _ => { - let template = self.create_chaos_template(chaos)?; - self.remove_chaos_template(template) - }, - } - } - - fn create_network_delay_template( - &self, - swarm_network_delay: &SwarmNetworkDelay, - ) -> Result { - let mut network_chaos_specs = vec![]; - - for group_network_delay in &swarm_network_delay.group_network_delays { - let source_instance_labels = - self.get_instance_labels(&group_network_delay.source_nodes); - let target_instance_labels = - self.get_instance_labels(&group_network_delay.target_nodes); - - network_chaos_specs.push(format!( - include_str!(DELAY_NETWORK_CHAOS_TEMPLATE!()), - name = &group_network_delay.name, - namespace = self.kube_namespace, - latency_ms = group_network_delay.latency_ms, - jitter_ms = group_network_delay.jitter_ms, - correlation_percentage = group_network_delay.correlation_percentage, - instance_labels = &source_instance_labels, - target_instance_labels = &target_instance_labels, - )); - } - Ok(network_chaos_specs.join("\n---\n")) - } - - fn create_network_partition_template( - &self, - swarm_network_partition: &SwarmNetworkPartition, - ) -> Result { - Ok(format!( - include_str!(PARTITION_NETWORK_CHAOS_TEMPLATE!()), - namespace = self.kube_namespace, - partition_percentage = swarm_network_partition.partition_percentage - )) - } - - fn create_network_bandwidth_template( - &self, - swarm_network_bandwidth: &SwarmNetworkBandwidth, - ) -> Result { - let mut network_chaos_specs = vec![]; - - for group_network_bandwidth in &swarm_network_bandwidth.group_network_bandwidths { - network_chaos_specs.push(format!( - include_str!(BANDWIDTH_NETWORK_CHAOS_TEMPLATE!()), - namespace = self.kube_namespace, - rate = group_network_bandwidth.rate, - limit = group_network_bandwidth.limit, - buffer = group_network_bandwidth.buffer, - )); - } - - Ok(network_chaos_specs.join("\n---\n")) - } - - fn create_network_loss_template( - &self, - swarm_network_loss: &SwarmNetworkLoss, - ) -> Result { - Ok(format!( - include_str!(NETWORK_LOSS_CHAOS_TEMPLATE!()), - namespace = self.kube_namespace, - loss_percentage = swarm_network_loss.loss_percentage, - correlation_percentage = swarm_network_loss.correlation_percentage, - )) - } - - fn create_netem_template(&self, swarm_netem: &SwarmNetEm) -> Result { - let mut network_chaos_specs = vec![]; - - for group_netem in &swarm_netem.group_netems { - let source_instance_labels = self.get_instance_labels(&group_netem.source_nodes); - let target_instance_labels = self.get_instance_labels(&group_netem.target_nodes); - - network_chaos_specs.push(format!( - include_str!(NETEM_CHAOS_TEMPLATE!()), - name = &group_netem.name, - namespace = self.kube_namespace, - delay_latency_ms = group_netem.delay_latency_ms, - delay_jitter_ms = group_netem.delay_jitter_ms, - delay_correlation_percentage = group_netem.delay_correlation_percentage, - loss_percentage = group_netem.loss_percentage, - loss_correlation_percentage = group_netem.loss_correlation_percentage, - instance_labels = &source_instance_labels, - target_instance_labels = &target_instance_labels, - rate = group_netem.rate_in_mbps, - )); - } - - Ok(network_chaos_specs.join("\n---\n")) - } - - /// Creates the CPU stress template, which can be used to inject CPU stress into a pod. - /// This can be used to simulate nodes with different available CPU resource even though the - /// nodes have identical hardware. For example, a node with 4 cores can be simulated as a node - /// with 2 cores by setting num_workers to 2. - fn create_cpu_stress_template(&self, swarm_cpu_stress: &SwarmCpuStress) -> Result { - let mut cpu_stress_specs = vec![]; - - for group_cpu_stress in &swarm_cpu_stress.group_cpu_stresses { - let instance_labels = self.get_instance_labels(&group_cpu_stress.target_nodes); - - cpu_stress_specs.push(format!( - include_str!(CPU_STRESS_CHAOS_TEMPLATE!()), - name = &group_cpu_stress.name, - namespace = self.kube_namespace, - num_workers = group_cpu_stress.num_workers, - load_per_worker = group_cpu_stress.load_per_worker, - instance_labels = &instance_labels, - )); - } - - Ok(cpu_stress_specs.join("\n---\n")) - } - - fn create_chaos_template(&self, chaos: &SwarmChaos) -> Result { - match chaos { - SwarmChaos::Delay(c) => self.create_network_delay_template(c), - SwarmChaos::Partition(c) => self.create_network_partition_template(c), - SwarmChaos::Bandwidth(c) => self.create_network_bandwidth_template(c), - SwarmChaos::Loss(c) => self.create_network_loss_template(c), - SwarmChaos::NetEm(c) => self.create_netem_template(c), - SwarmChaos::CpuStress(c) => self.create_cpu_stress_template(c), - } - } - - /// Creates and applies the NetworkChaos CRD - fn inject_chaos_template(&self, chaos_template: String) -> Result<()> { - let tmp_dir = TempDir::new().expect("Could not create temp dir"); - let latency_network_chaos_file_path = dump_string_to_file( - format!("{}-chaos.yaml", self.kube_namespace), - chaos_template, - &tmp_dir, - )?; - Command::new(KUBECTL_BIN) - .args([ - "-n", - &self.kube_namespace, - "apply", - "-f", - &latency_network_chaos_file_path, - ]) - .status() - .expect("Failed to submit chaos template"); - - Ok(()) - } - - /// Removes the NetworkChaos CRD - fn remove_chaos_template(&self, chaos_template: String) -> Result<()> { - let tmp_dir = TempDir::new().expect("Could not create temp dir"); - let latency_network_chaos_file_path = dump_string_to_file( - format!("{}-chaos.yaml", self.kube_namespace), - chaos_template, - &tmp_dir, - )?; - Command::new(KUBECTL_BIN) - .args([ - "-n", - &self.kube_namespace, - "delete", - "-f", - &latency_network_chaos_file_path, - ]) - .status() - .expect("Failed to delete chaos by template"); - - Ok(()) - } - - /// Returns the instance labels for the given peers - /// as a string (separated by commas). - fn get_instance_labels(&self, peers: &[PeerId]) -> String { - peers - .iter() - .map(|node| self.get_node_name(node)) - .collect::>() - .join(",") - } - - /// Returns the name of the node associated with the given account address - fn get_node_name(&self, node: &AccountAddress) -> &str { - if let Some(validator) = self.validator(*node) { - validator.name() - } else if let Some(fullnode) = self.full_node(*node) { - fullnode.name() - } else { - // TODO: should we throw an error here instead of failing silently? - INVALID_NODE_STRING - } - } -} diff --git a/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml b/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml deleted file mode 100644 index 64d771ce5a0ee..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: chaos-mesh.org/v1alpha1 -kind: StressChaos -metadata: - namespace: {namespace} - name: {name} -spec: - mode: all - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [{instance_labels}] }} - stressors: - cpu: - workers: {num_workers} - load: {load_per_worker} \ No newline at end of file diff --git a/testsuite/forge/src/backend/k8s/chaos/netem.yaml b/testsuite/forge/src/backend/k8s/chaos/netem.yaml deleted file mode 100644 index 1957df33f898e..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/netem.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: chaos-mesh.org/v1alpha1 -kind: NetworkChaos -metadata: - namespace: {namespace} - name: {name} -spec: - action: netem - mode: all - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [{instance_labels}] }} - delay: - latency: "{delay_latency_ms}ms" - correlation: "{delay_correlation_percentage}" - jitter: "{delay_jitter_ms}ms" - loss: - loss: "{loss_percentage}" - correlation: "{loss_correlation_percentage}" - bandwidth: - rate: "{rate}mbps" - limit: 20971520 # placeholder value. not supported by tc netem - buffer: 10000 # placeholder value. not supported by tc netem - direction: both - target: - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [{target_instance_labels}] }} - mode: all diff --git a/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml b/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml deleted file mode 100644 index 5c81f759e38b8..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml +++ /dev/null @@ -1,17 +0,0 @@ -kind: NetworkChaos -apiVersion: chaos-mesh.org/v1alpha1 -metadata: - namespace: {namespace} - name: forge-namespace-{rate}mbps-bandwidth -spec: - action: bandwidth - mode: all - selector: - namespaces: - - {namespace} - labelSelectors: - app.kubernetes.io/name: validator - bandwidth: - rate: "{rate}mbps" - limit: {limit} - buffer: {buffer} diff --git a/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml b/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml deleted file mode 100644 index 20571fbcd6840..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml +++ /dev/null @@ -1,35 +0,0 @@ -kind: NetworkChaos -apiVersion: chaos-mesh.org/v1alpha1 -metadata: - namespace: {namespace} - name: {name} -spec: - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [{instance_labels}] }} - mode: all - action: delay - delay: - latency: "{latency_ms}ms" - correlation: "{correlation_percentage}" - jitter: "{jitter_ms}ms" - # Indicates the direction of target packets. Available values include: - # from (the packets from target) - # to (the packets to target) - # both ( the packets from or to target) - # This parameter makes Chaos only take effect for a specific direction of packets. - direction: both - target: - # For delay NetworkChaos, always use "from" direction and always target an entire namespace - # This is because Forge submits API requests to k8s Services, whereas Chaos Mesh applies the chaos - # via netem on the PodIP directly. - # This also hinges on the fact that the Forge workloads (txn-emitter and overall test runner) exist - # in a separate k8s Namespace, so they are not affected by the below selector. - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [{target_instance_labels}] }} - mode: all diff --git a/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml b/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml deleted file mode 100644 index 9914efe8dbb98..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml +++ /dev/null @@ -1,25 +0,0 @@ - -kind: NetworkChaos -apiVersion: chaos-mesh.org/v1alpha1 -metadata: - namespace: {namespace} - name: forge-namespace-{loss_percentage}loss-{correlation_percentage}correlation -spec: - selector: - namespaces: - - {namespace} - labelSelectors: - app.kubernetes.io/name: validator - mode: all - action: loss - loss: - loss: "{loss_percentage}" - correlation: "{correlation_percentage}" - direction: both - target: - selector: - namespaces: - - {namespace} - labelSelectors: - app.kubernetes.io/name: validator - mode: all diff --git a/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml b/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml deleted file mode 100644 index 2f30ea92ccb76..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml +++ /dev/null @@ -1,22 +0,0 @@ -kind: NetworkChaos -apiVersion: chaos-mesh.org/v1alpha1 -metadata: - namespace: {namespace} - name: forge-namespace-{partition_percentage}-percent-partition -spec: - selector: - namespaces: - - {namespace} - labelSelectors: - app.kubernetes.io/name: validator - mode: all - action: partition - direction: both - target: - selector: - namespaces: - - {namespace} - expressionSelectors: - - {{ key: app.kubernetes.io/instance, operator: In, values: [validator-0, validator-1, validator-2] }} - mode: fixed-percent - value: "{partition_percentage}" diff --git a/testsuite/forge/src/backend/k8s/chaos_schema.rs b/testsuite/forge/src/backend/k8s/chaos_schema.rs deleted file mode 100644 index c628fbc7b2cb9..0000000000000 --- a/testsuite/forge/src/backend/k8s/chaos_schema.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use kube::CustomResource; -use serde::{Deserialize, Serialize}; - -pub enum Chaos { - Network(NetworkChaos), - Stress(StressChaos), -} - -#[derive(CustomResource, Deserialize, Default, Serialize, Clone, Debug)] -#[kube( - group = "chaos-mesh.org", - version = "v1alpha1", - kind = "NetworkChaos", - status = "ChaosStatus", - plural = "networkchaos", - namespaced, - schema = "disabled" -)] -pub struct NetworkChaosSpec {} - -#[derive(CustomResource, Default, Serialize, Deserialize, Clone, Debug)] -#[kube( - group = "chaos-mesh.org", - version = "v1alpha1", - kind = "StressChaos", - status = "ChaosStatus", - plural = "stresschaos", - namespaced, - schema = "disabled" -)] -pub struct StressChaosSpec {} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct ChaosStatus { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub conditions: Option>, -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct ChaosCondition { - #[serde(rename = "type")] - pub r#type: ChaosConditionType, - - pub status: ConditionStatus, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub enum ConditionStatus { - False, - True, - Unknown, -} - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum ChaosConditionType { - Selected, - AllInjected, - AllRecovered, - Paused, -} diff --git a/testsuite/forge/src/backend/k8s/cluster_helper.rs b/testsuite/forge/src/backend/k8s/cluster_helper.rs deleted file mode 100644 index 28588cd2ba3fd..0000000000000 --- a/testsuite/forge/src/backend/k8s/cluster_helper.rs +++ /dev/null @@ -1,1224 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - get_fullnodes, get_validators, k8s_wait_genesis_strategy, k8s_wait_nodes_strategy, - nodes_healthcheck, wait_stateful_set, ForgeRunnerMode, GenesisConfigFn, K8sApi, K8sNode, - NodeConfigFn, ReadWrite, Result, APTOS_NODE_HELM_CHART_PATH, APTOS_NODE_HELM_RELEASE_NAME, - DEFAULT_ROOT_KEY, DEFAULT_TEST_SUITE_NAME, DEFAULT_USERNAME, FORGE_KEY_SEED, - FULLNODE_HAPROXY_SERVICE_SUFFIX, FULLNODE_SERVICE_SUFFIX, GENESIS_HELM_CHART_PATH, - GENESIS_HELM_RELEASE_NAME, HELM_BIN, KUBECTL_BIN, MANAGEMENT_CONFIGMAP_PREFIX, - NAMESPACE_CLEANUP_THRESHOLD_SECS, POD_CLEANUP_THRESHOLD_SECS, VALIDATOR_HAPROXY_SERVICE_SUFFIX, - VALIDATOR_SERVICE_SUFFIX, -}; -use again::RetryPolicy; -use anyhow::{anyhow, bail, format_err}; -use aptos_logger::info; -use aptos_sdk::types::PeerId; -use k8s_openapi::api::{ - apps::v1::{Deployment, StatefulSet}, - batch::v1::Job, - core::v1::{ConfigMap, Namespace, PersistentVolume, PersistentVolumeClaim, Pod}, -}; -use kube::{ - api::{Api, DeleteParams, ListParams, ObjectMeta, Patch, PatchParams, PostParams}, - client::Client as K8sClient, - config::{KubeConfigOptions, Kubeconfig}, - Config, Error as KubeError, ResourceExt, -}; -use rand::Rng; -use serde::de::DeserializeOwned; -use serde_json::Value; -use std::{ - collections::{BTreeMap, HashMap}, - convert::TryFrom, - env, - fmt::Debug, - fs, - fs::File, - io::Write, - net::TcpListener, - path::Path, - process::{Command, Stdio}, - str, - sync::Arc, - time::{SystemTime, UNIX_EPOCH}, -}; -use tempfile::TempDir; -use thiserror::Error; -use tokio::time::Duration; - -/// Gets a free port -pub fn get_free_port() -> u32 { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - listener.local_addr().unwrap().port() as u32 -} - -/// Waits for the testnet's genesis job to complete, while tailing the job's logs -async fn wait_genesis_job(kube_client: &K8sClient, era: &str, kube_namespace: &str) -> Result<()> { - aptos_retrier::retry_async(k8s_wait_genesis_strategy(), || { - let jobs: Api = Api::namespaced(kube_client.clone(), kube_namespace); - Box::pin(async move { - let job_name = format!("{}-aptos-genesis-e{}", GENESIS_HELM_RELEASE_NAME, era); - - let genesis_job = jobs.get_status(&job_name).await.unwrap(); - - let status = genesis_job.status.unwrap(); - info!("Genesis status: {:?}", status); - match status.active { - Some(_) => { - // try tailing the logs of the genesis job - // by the time this is done, we can re-evalulate its status - Command::new(KUBECTL_BIN) - .args([ - "-n", - kube_namespace, - "logs", - "-f", - format!("job/{}", &job_name).as_str(), - ]) - .status() - .expect("Failed to tail genesis logs"); - }, - None => info!("Genesis completed running"), - } - info!("Genesis status: {:?}", status); - match status.succeeded { - Some(_) => { - info!("Genesis done"); - Ok(()) - }, - None => bail!("Genesis did not succeed"), - } - }) - }) - .await -} - -/// Waits for a given number of HAProxy K8s Deployments to be ready -async fn wait_node_haproxy( - kube_client: &K8sClient, - kube_namespace: &str, - num_haproxy: usize, -) -> Result<()> { - aptos_retrier::retry_async(k8s_wait_nodes_strategy(), || { - let deployments_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); - Box::pin(async move { - for i in 0..num_haproxy { - let haproxy_deployment_name = - format!("{}-{}-haproxy", APTOS_NODE_HELM_RELEASE_NAME, i); - match deployments_api.get_status(&haproxy_deployment_name).await { - Ok(s) => { - let deployment_name = s.name(); - if let Some(deployment_status) = s.status { - let ready_replicas = deployment_status.ready_replicas.unwrap_or(0); - info!( - "Deployment {} has {} ready_replicas", - deployment_name, ready_replicas - ); - if ready_replicas > 0 { - info!("Deployment {} ready", deployment_name); - continue; - } - } - info!("Deployment {} has no status", deployment_name); - bail!("Deployment not ready"); - }, - Err(e) => { - info!("Failed to get deployment: {}", e); - bail!("Failed to get deployment: {}", e); - }, - } - } - Ok(()) - }) - }) - .await -} - -/// Waits for all given K8sNodes to be ready. Called when the testnet is first started, so we may have to wait a while for -/// machines to be provisioned by the cloud provider. -async fn wait_nodes_stateful_set( - kube_client: &K8sClient, - kube_namespace: &str, - nodes: &HashMap, -) -> Result<()> { - // wait for all nodes healthy - for node in nodes.values() { - // retry every 10 seconds for 20 minutes - let retry_policy = RetryPolicy::fixed(Duration::from_secs(10)).with_max_retries(120); - wait_stateful_set( - kube_client, - kube_namespace, - node.stateful_set_name(), - 1, - retry_policy, - ) - .await? - } - Ok(()) -} - -/// Deletes a collection of resources in k8s as part of aptos-node -async fn delete_k8s_collection( - api: Api, - name: &'static str, - label_selector: &str, -) -> Result<()> -where - T: Clone + DeserializeOwned + Debug, - ::DynamicType: Default, -{ - match api - .delete_collection( - &DeleteParams::default(), - &ListParams::default().labels(label_selector), - ) - .await? - { - either::Left(list) => { - let names: Vec<_> = list.iter().map(ResourceExt::name).collect(); - info!("Deleting collection of {}: {:?}", name, names); - }, - either::Right(status) => { - info!("Deleted collection of {}: status={:?}", name, status); - }, - } - - Ok(()) -} - -/// Delete existing k8s resources in the namespace. This is essentially helm uninstall but lighter weight -pub(crate) async fn delete_k8s_resources(client: K8sClient, kube_namespace: &str) -> Result<()> { - // selector for the helm chart - let aptos_node_helm_selector = "app.kubernetes.io/part-of=aptos-node"; - let testnet_addons_helm_selector = "app.kubernetes.io/part-of=testnet-addons"; - let genesis_helm_selector = "app.kubernetes.io/part-of=aptos-genesis"; - - // selector for manually created resources from Forge - let forge_pfn_selector = "app.kubernetes.io/part-of=forge-pfn"; - - // delete all deployments and statefulsets - // cross this with all the compute resources created by aptos-node helm chart - let deployments: Api = Api::namespaced(client.clone(), kube_namespace); - let stateful_sets: Api = Api::namespaced(client.clone(), kube_namespace); - let pvcs: Api = Api::namespaced(client.clone(), kube_namespace); - let jobs: Api = Api::namespaced(client.clone(), kube_namespace); - // service deletion by label selector is not supported in this version of k8s api - // let services: Api = Api::namespaced(client.clone(), kube_namespace); - - for selector in &[ - aptos_node_helm_selector, - testnet_addons_helm_selector, - genesis_helm_selector, - forge_pfn_selector, - ] { - info!("Deleting k8s resources with selector: {}", selector); - delete_k8s_collection(deployments.clone(), "Deployments", selector).await?; - delete_k8s_collection(stateful_sets.clone(), "StatefulSets", selector).await?; - delete_k8s_collection(pvcs.clone(), "PersistentVolumeClaims", selector).await?; - delete_k8s_collection(jobs.clone(), "Jobs", selector).await?; - // This is causing problem on gcp forge for some reason?! - // HACK remove to unblock - // delete_k8s_collection(cronjobs.clone(), "CronJobs", selector).await?; - // delete_k8s_collection(services.clone(), "Services", selector).await?; - } - - delete_all_chaos(kube_namespace)?; - - Ok(()) -} - -pub(crate) fn delete_all_chaos(kube_namespace: &str) -> Result<()> { - // clear everything manually, in case there are some dangling - let delete_networkchaos = ["-n", kube_namespace, "delete", "networkchaos", "--all"]; - info!("{:?}", delete_networkchaos); - let delete_networkchaos_output = Command::new(KUBECTL_BIN) - .stdout(Stdio::inherit()) - .args(delete_networkchaos) - .output() - .expect("failed to delete all NetworkChaos"); - if !delete_networkchaos_output.status.success() { - bail!( - "{}", - String::from_utf8(delete_networkchaos_output.stderr).unwrap() - ); - } - Ok(()) -} - -/// Deletes all Forge resources from the given namespace. If the namespace is "default", delete the management configmap -/// as well as all compute resources. If the namespace is a Forge namespace (has the "forge-*" prefix), then simply delete -/// the entire namespace -async fn delete_k8s_cluster(kube_namespace: String) -> Result<()> { - let client: K8sClient = create_k8s_client().await?; - - // if operating on the default namespace, - match kube_namespace.as_str() { - "default" => { - // delete the management configmap - let configmap: Api = Api::namespaced(client.clone(), &kube_namespace); - let management_configmap_name = - format!("{}-{}", MANAGEMENT_CONFIGMAP_PREFIX, &kube_namespace); - match configmap - .delete(&management_configmap_name, &DeleteParams::default()) - .await - { - Ok(_) => info!( - "Deleted default management configmap: {}", - &management_configmap_name - ), - // if configmap not found, assume it's already been deleted and make clean-up idempotent - Err(KubeError::Api(api_err)) => { - if api_err.code == 404 { - info!( - "Could not find configmap {}, continuing", - &management_configmap_name - ); - } else { - bail!(api_err); - } - }, - Err(e) => bail!(e), - }; - delete_k8s_resources(client, "default").await?; - }, - s if s.starts_with("forge") => { - let namespaces: Api = Api::all(client); - namespaces - .delete(&kube_namespace, &DeleteParams::default()) - .await? - .map_left(|namespace| info!("Deleting namespace {}: {:?}", s, namespace.status)) - .map_right(|status| info!("Deleted namespace {}: {:?}", s, status)); - }, - _ => { - bail!( - "Invalid kubernetes namespace provided: {}. Use forge-*", - kube_namespace - ); - }, - } - - Ok(()) -} - -fn upgrade_helm_release( - release_name: String, - helm_chart: String, - options: &[String], - kube_namespace: String, -) -> Result<()> { - // Check to make sure helm_chart exists - let helm_chart_path = Path::new(&helm_chart); - if !helm_chart_path.exists() { - bail!( - "Helm chart {} does not exist, try running from the repo root", - helm_chart - ); - } - - // only create cluster-level resources once - let psp_values = match kube_namespace.as_str() { - "default" => "podSecurityPolicy=true", - _ => "podSecurityPolicy=false", - }; - let upgrade_base_args = [ - "upgrade".to_string(), - // "--debug".to_string(), - "--install".to_string(), - // // force replace if necessary - // "--force".to_string(), - // in a new namespace - "--create-namespace".to_string(), - "--namespace".to_string(), - kube_namespace, - // upgrade - release_name.clone(), - helm_chart.clone(), - // reuse old values - "--reuse-values".to_string(), - "--history-max".to_string(), - "2".to_string(), - ]; - let upgrade_override_args = ["--set".to_string(), psp_values.to_string()]; - let upgrade_args = [&upgrade_base_args, options, &upgrade_override_args].concat(); - info!("{:?}", upgrade_args); - let upgrade_output = Command::new(HELM_BIN) - .stdout(Stdio::inherit()) - .args(&upgrade_args) - .output() - .unwrap_or_else(|_| { - panic!( - "failed to helm upgrade release {} with chart {}", - release_name, helm_chart - ) - }); - if !upgrade_output.status.success() { - bail!(format!( - "Upgrade not completed: {}", - String::from_utf8(upgrade_output.stderr).unwrap() - )); - } - - Ok(()) -} - -// TODO: upgrade via kube api -#[allow(dead_code)] -fn upgrade_validator( - _validator_name: String, - _options: &[String], - _kube_namespace: String, -) -> Result<()> { - todo!() -} - -fn upgrade_aptos_node_helm(options: &[String], kube_namespace: String) -> Result<()> { - upgrade_helm_release( - APTOS_NODE_HELM_RELEASE_NAME.to_string(), - APTOS_NODE_HELM_CHART_PATH.to_string(), - options, - kube_namespace, - ) -} - -// runs helm upgrade on the installed aptos-genesis release named "genesis" -// if a new "era" is specified, a new genesis will be created, and old resources will be destroyed -fn upgrade_genesis_helm(options: &[String], kube_namespace: String) -> Result<()> { - upgrade_helm_release( - GENESIS_HELM_RELEASE_NAME.to_string(), - GENESIS_HELM_CHART_PATH.to_string(), - options, - kube_namespace, - ) -} - -pub async fn uninstall_testnet_resources(kube_namespace: String) -> Result<()> { - // delete kubernetes resources - delete_k8s_cluster(kube_namespace.clone()).await?; - info!( - "aptos-node resources for Forge removed in namespace: {}", - kube_namespace - ); - - Ok(()) -} - -fn generate_new_era() -> String { - let mut rng = rand::thread_rng(); - let r: u8 = rng.gen(); - format!("forge{}", r) -} - -fn get_node_default_helm_path() -> String { - match ForgeRunnerMode::try_from_env().unwrap_or(ForgeRunnerMode::K8s) { - ForgeRunnerMode::Local => { - "testsuite/forge/src/backend/k8s/helm-values/aptos-node-default-values.yaml" - }, - ForgeRunnerMode::K8s => "/aptos/terraform/aptos-node-default-values.yaml", - } - .to_string() -} - -pub async fn reset_persistent_volumes(kube_client: &K8sClient) -> Result<()> { - let pv_api: Api = Api::all(kube_client.clone()); - let pvs = pv_api - .list(&ListParams::default()) - .await? - .items - .into_iter() - .filter(|pv| { - if let Some(status) = &pv.status { - if let Some(phase) = &status.phase { - if phase == "Released" { - return true; - } - } - } - false - }) - .collect::>(); - - for pv in &pvs { - let name = pv.metadata.name.clone().expect("Must have name!"); - info!("Changing pv {} from Released to Available.", name); - let patch = serde_json::json!({ - "spec": { - "claimRef": null - } - }); - pv_api - .patch(&name, &PatchParams::default(), &Patch::Merge(&patch)) - .await?; - } - - Ok(()) -} - -pub async fn check_persistent_volumes( - kube_client: K8sClient, - num_requested_pvs: usize, - existing_db_tag: String, -) -> Result<()> { - info!("Trying to get {} PVs.", num_requested_pvs); - let pv_api: Api = Api::all(kube_client.clone()); - let list_params = ListParams::default(); - let pvs = pv_api - .list(&list_params) - .await? - .items - .into_iter() - .filter(|pv| { - if let Some(labels) = &pv.metadata.labels { - if let Some(tag) = labels.get(&"tag".to_string()) { - if tag == &existing_db_tag { - if let Some(status) = &pv.status { - if let Some(phase) = &status.phase { - if phase == "Available" { - return true; - } - } - } - } - } - } - false - }) - .collect::>(); - - if pvs.len() < num_requested_pvs { - return Err(anyhow!( - "Could not find enough PVs, requested: {}, available: {}.", - num_requested_pvs, - pvs.len() - )); - } - - info!("Found enough PVs."); - - Ok(()) -} - -/// Installs a testnet in a k8s namespace by first running genesis, and the installing the aptos-nodes via helm -/// Returns the current era, as well as a mapping of validators and fullnodes -pub async fn install_testnet_resources( - kube_namespace: String, - num_validators: usize, - num_fullnodes: usize, - node_image_tag: String, - genesis_image_tag: String, - genesis_modules_path: Option, - use_port_forward: bool, - enable_haproxy: bool, - genesis_helm_config_fn: Option, - node_helm_config_fn: Option, -) -> Result<(String, HashMap, HashMap)> { - let kube_client = create_k8s_client().await?; - - // get deployment-specific helm values and cache it - let tmp_dir = TempDir::new().expect("Could not create temp dir"); - let aptos_node_values_file = dump_helm_values_to_file(APTOS_NODE_HELM_RELEASE_NAME, &tmp_dir)?; - let genesis_values_file = dump_helm_values_to_file(GENESIS_HELM_RELEASE_NAME, &tmp_dir)?; - - // generate a random era to wipe the network state - let new_era = generate_new_era(); - - // get forge override helm values and cache it - let aptos_node_forge_helm_values_yaml = construct_node_helm_values( - node_helm_config_fn, - fs::read_to_string(get_node_default_helm_path()) - .expect("Not able to read default value file"), - kube_namespace.clone(), - new_era.clone(), - num_validators, - num_fullnodes, - node_image_tag, - enable_haproxy, - )?; - - let aptos_node_forge_values_file = dump_string_to_file( - "aptos-node-values.yaml".to_string(), - aptos_node_forge_helm_values_yaml, - &tmp_dir, - )?; - - let genesis_forge_helm_values_yaml = construct_genesis_helm_values( - genesis_helm_config_fn, - kube_namespace.clone(), - new_era.clone(), - num_validators, - genesis_image_tag, - enable_haproxy, - )?; - let genesis_forge_values_file = dump_string_to_file( - "genesis-values.yaml".to_string(), - genesis_forge_helm_values_yaml, - &tmp_dir, - )?; - - // combine all helm values - let aptos_node_upgrade_options = vec![ - // use the old values - "-f".to_string(), - aptos_node_values_file, - "-f".to_string(), - aptos_node_forge_values_file, - ]; - - let mut genesis_upgrade_options = vec![ - // use the old values - "-f".to_string(), - genesis_values_file, - "-f".to_string(), - genesis_forge_values_file, - ]; - - // run genesis from the directory in aptos/init image - if let Some(genesis_modules_path) = genesis_modules_path { - genesis_upgrade_options.extend([ - "--set".to_string(), - format!("genesis.moveModulesDir={}", genesis_modules_path), - ]); - } - - // upgrade genesis - upgrade_genesis_helm(genesis_upgrade_options.as_slice(), kube_namespace.clone())?; - - // wait for genesis to run again, and get the updated validators - wait_genesis_job(&kube_client, &new_era, &kube_namespace).await?; - - // TODO(rustielin): get the helm releases to be consistent - upgrade_aptos_node_helm( - aptos_node_upgrade_options.as_slice(), - kube_namespace.clone(), - )?; - - let (validators, fullnodes) = collect_running_nodes( - &kube_client, - kube_namespace, - use_port_forward, - enable_haproxy, - ) - .await?; - - Ok((new_era.clone(), validators, fullnodes)) -} - -pub fn construct_node_helm_values( - node_helm_config_fn: Option, - base_helm_values: String, - kube_namespace: String, - era: String, - num_validators: usize, - num_fullnodes: usize, - image_tag: String, - enable_haproxy: bool, -) -> Result { - let mut value: serde_yaml::Value = serde_yaml::from_str(&base_helm_values)?; - value["numValidators"] = num_validators.into(); - value["numFullnodeGroups"] = num_fullnodes.into(); - value["imageTag"] = image_tag.clone().into(); - value["chain"]["era"] = era.into(); - value["haproxy"]["enabled"] = enable_haproxy.into(); - value["labels"]["forge-namespace"] = make_k8s_label(kube_namespace).into(); - value["labels"]["forge-image-tag"] = make_k8s_label(image_tag).into(); - - // if present, tag the node with the test suite name and username - let suite_name = env::var("FORGE_TEST_SUITE").unwrap_or(DEFAULT_TEST_SUITE_NAME.to_string()); - value["labels"]["forge-test-suite"] = make_k8s_label(suite_name).into(); - let username = env::var("FORGE_USERNAME").unwrap_or(DEFAULT_USERNAME.to_string()); - value["labels"]["forge-username"] = make_k8s_label(username).into(); - - if let Some(config_fn) = node_helm_config_fn { - (config_fn)(&mut value); - } - serde_yaml::to_string(&value).map_err(|e| anyhow::anyhow!("{:?}", e)) -} - -pub fn construct_genesis_helm_values( - genesis_helm_config_fn: Option, - kube_namespace: String, - era: String, - num_validators: usize, - genesis_image_tag: String, - enable_haproxy: bool, -) -> Result { - let validator_internal_host_suffix = if enable_haproxy { - VALIDATOR_HAPROXY_SERVICE_SUFFIX - } else { - VALIDATOR_SERVICE_SUFFIX - }; - let fullnode_internal_host_suffix = if enable_haproxy { - FULLNODE_HAPROXY_SERVICE_SUFFIX - } else { - FULLNODE_SERVICE_SUFFIX - }; - let mut value: serde_yaml::Value = serde_yaml::Value::default(); - value["imageTag"] = genesis_image_tag.clone().into(); - value["chain"]["era"] = era.into(); - value["chain"]["root_key"] = DEFAULT_ROOT_KEY.into(); - value["genesis"]["numValidators"] = num_validators.into(); - value["genesis"]["validator"]["internal_host_suffix"] = validator_internal_host_suffix.into(); - value["genesis"]["validator"]["key_seed"] = FORGE_KEY_SEED.into(); - value["genesis"]["fullnode"]["internal_host_suffix"] = fullnode_internal_host_suffix.into(); - value["labels"]["forge-namespace"] = make_k8s_label(kube_namespace).into(); - value["labels"]["forge-image-tag"] = make_k8s_label(genesis_image_tag).into(); - - // if present, tag the node with the test suite name and username - let suite_name = env::var("FORGE_TEST_SUITE").unwrap_or(DEFAULT_TEST_SUITE_NAME.to_string()); - value["labels"]["forge-test-suite"] = make_k8s_label(suite_name).into(); - let username = env::var("FORGE_USERNAME").unwrap_or(DEFAULT_USERNAME.to_string()); - value["labels"]["forge-username"] = make_k8s_label(username).into(); - - if let Some(config_fn) = genesis_helm_config_fn { - (config_fn)(&mut value); - } - - serde_yaml::to_string(&value).map_err(|e| anyhow::anyhow!("{:?}", e)) -} - -/// Collect the running nodes in the network into K8sNodes -pub async fn collect_running_nodes( - kube_client: &K8sClient, - kube_namespace: String, - use_port_forward: bool, - enable_haproxy: bool, -) -> Result<(HashMap, HashMap)> { - // get all validators - let validators = get_validators( - kube_client.clone(), - &kube_namespace, - use_port_forward, - enable_haproxy, - ) - .await - .unwrap(); - - // wait for all validator STS to spin up - wait_nodes_stateful_set(kube_client, &kube_namespace, &validators).await?; - - if enable_haproxy { - wait_node_haproxy(kube_client, &kube_namespace, validators.len()).await?; - } - - // get all fullnodes - let fullnodes = get_fullnodes( - kube_client.clone(), - &kube_namespace, - use_port_forward, - enable_haproxy, - ) - .await - .unwrap(); - - wait_nodes_stateful_set(kube_client, &kube_namespace, &fullnodes).await?; - - let nodes = validators - .values() - .chain(fullnodes.values()) - .collect::>(); - - // start port-forward for each of the nodes - if use_port_forward { - for node in nodes.iter() { - node.port_forward_rest_api()?; - // assume this will always succeed??? - } - } - - nodes_healthcheck(nodes).await?; - Ok((validators, fullnodes)) -} - -/// Returns a [Config] object reading the KUBECONFIG environment variable or infering from the -/// environment. Differently from [`Config::infer()`], this will look at the -/// `KUBECONFIG` env var first, and only then infer from the environment. -async fn make_kube_client_config() -> Result { - match Config::from_kubeconfig(&KubeConfigOptions::default()).await { - Ok(config) => Ok(config), - Err(kubeconfig_err) => { - Config::infer() - .await - .map_err(|infer_err| - anyhow::anyhow!("Unable to construct Config. Failed to infer config {:?}. Failed to read KUBECONFIG {:?}", infer_err, kubeconfig_err) - ) - } - } -} - -pub async fn create_k8s_client() -> Result { - let mut config = make_kube_client_config().await?; - - let cluster_name = Kubeconfig::read() - .map(|k| k.current_context.unwrap_or_default()) - .unwrap_or_else(|_| config.cluster_url.to_string()); - - config.accept_invalid_certs = true; - - let client = K8sClient::try_from(config)?; - - // Test the connection, fail if request fails - client.apiserver_version().await.map_err(|_| { - if !cluster_name.contains("forge") { - format_err!( - "Failed to connect to kubernetes cluster {}, \ - please make sure you have the right kubeconfig", - cluster_name - ) - } else { - format_err!("Failed to connect to kubernetes cluster {}", cluster_name) - } - })?; - Ok(client) -} - -/// Gets the result of helm status command as JSON -fn get_helm_status(helm_release_name: &str) -> Result { - let status_args = [ - "status", - helm_release_name, - "--namespace", - "default", - "-o", - "json", - ]; - info!("{:?}", status_args); - let raw_helm_values = Command::new(HELM_BIN) - .args(status_args) - .output() - .unwrap_or_else(|_| panic!("Failed to helm status {}", helm_release_name)); - - let helm_values = String::from_utf8(raw_helm_values.stdout).unwrap(); - serde_json::from_str(&helm_values).map_err(|e| { - format_err!( - "Failed to deserialize helm values. Check if release {} exists: {}", - helm_release_name, - e - ) - }) -} - -/// Dumps the given String contents into a file at the given temp directory -pub fn dump_string_to_file( - file_name: String, - content: String, - tmp_dir: &TempDir, -) -> Result { - let file_path = tmp_dir.path().join(file_name.clone()); - info!("Wrote content to: {:?}", &file_path); - let mut file = File::create(file_path).expect("Could not create file in temp dir"); - file.write_all(&content.into_bytes()) - .expect("Could not write to file"); - let file_path_str = tmp_dir.path().join(file_name).display().to_string(); - Ok(file_path_str) -} - -fn dump_helm_values_to_file(helm_release_name: &str, tmp_dir: &TempDir) -> Result { - // get aptos-node values - let v: Value = get_helm_status(helm_release_name).unwrap(); - let config = &v["config"]; - let content = config.to_string(); - let file_name = format!("{}_status.json", helm_release_name); - - dump_string_to_file(file_name, content, tmp_dir) -} - -#[derive(Error, Debug)] -#[error("{0}")] -enum ApiError { - RetryableError(String), - FinalError(String), -} - -async fn create_namespace( - namespace_api: Arc>, - kube_namespace: String, -) -> Result<(), ApiError> { - let kube_namespace_name = kube_namespace.clone(); - let namespace = Namespace { - metadata: ObjectMeta { - name: Some(kube_namespace_name.clone()), - ..ObjectMeta::default() - }, - spec: None, - status: None, - }; - if let Err(KubeError::Api(api_err)) = namespace_api - .create(&PostParams::default(), &namespace) - .await - { - if api_err.code == 409 { - info!( - "Namespace {} already exists, continuing with it", - &kube_namespace_name - ); - } else if api_err.code == 401 { - return Err(ApiError::FinalError( - "Unauthorized, did you authorize with kubernetes? \ - Try running `kubectl get current-context`" - .to_string(), - )); - } else { - return Err(ApiError::RetryableError(format!( - "Failed to use existing namespace {}: {:?}", - &kube_namespace_name, api_err - ))); - } - } - Ok(()) -} - -pub async fn create_management_configmap( - kube_namespace: String, - keep: bool, - cleanup_duration: Duration, -) -> Result<()> { - let kube_client = create_k8s_client().await?; - let namespaces_api = Arc::new(K8sApi::::from_client(kube_client.clone(), None)); - let other_kube_namespace = kube_namespace.clone(); - - // try to create a new namespace - // * if it errors with 409, the namespace exists already and we should use it - // * if it errors with 403, the namespace is likely in the process of being terminated, so try again - RetryPolicy::exponential(Duration::from_millis(1000)) - .with_max_delay(Duration::from_millis(10 * 60 * 1000)) - .retry_if( - move || create_namespace(namespaces_api.clone(), other_kube_namespace.clone()), - |e: &ApiError| matches!(e, ApiError::RetryableError(_)), - ) - .await?; - - let configmap_api = Arc::new(K8sApi::::from_client( - kube_client.clone(), - Some(kube_namespace.clone()), - )); - - let management_configmap_name = format!("{}-{}", MANAGEMENT_CONFIGMAP_PREFIX, &kube_namespace); - let mut data: BTreeMap = BTreeMap::new(); - let start = SystemTime::now(); - let cleanup_time = (start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - + cleanup_duration) - .as_secs(); - data.insert("keep".to_string(), keep.to_string()); - data.insert("cleanup".to_string(), cleanup_time.to_string()); - - let config = ConfigMap { - binary_data: None, - data: Some(data.clone()), - metadata: ObjectMeta { - name: Some(management_configmap_name.clone()), - ..ObjectMeta::default() - }, - immutable: None, - }; - if let Err(KubeError::Api(api_err)) = - configmap_api.create(&PostParams::default(), &config).await - { - if api_err.code == 409 { - info!( - "Configmap {} already exists, continuing with it", - &management_configmap_name - ); - } else { - bail!( - "Failed to use existing management configmap {}: {:?}", - &kube_namespace, - api_err - ); - } - } else { - info!( - "Created configmap {} with data {:?}", - management_configmap_name, data - ); - } - - Ok(()) -} - -pub async fn cleanup_cluster_with_management() -> Result<()> { - let kube_client = create_k8s_client().await?; - let start = SystemTime::now(); - let time_since_the_epoch = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - - let pods_api: Api = Api::namespaced(kube_client.clone(), "default"); - let lp = ListParams::default().labels("app.kubernetes.io/name=forge"); - - // delete all forge test pods over a threshold age - let pods = pods_api - .list(&lp) - .await? - .items - .into_iter() - .filter(|pod| { - let pod_name = pod.name(); - info!("Got pod {}", pod_name); - if let Some(time) = &pod.metadata.creation_timestamp { - let pod_creation_time = time.0.timestamp() as u64; - let pod_uptime = time_since_the_epoch - pod_creation_time; - info!( - "Pod {} has lived for {}/{} seconds", - pod_name, pod_uptime, POD_CLEANUP_THRESHOLD_SECS - ); - if pod_uptime > POD_CLEANUP_THRESHOLD_SECS { - return true; - } - } - false - }) - .collect::>(); - for pod in pods { - let pod_name = pod.name(); - info!("Deleting pod {}", pod_name); - pods_api.delete(&pod_name, &DeleteParams::default()).await?; - } - - // delete all forge testnets over a threshold age using their management configmaps - // unless they are explicitly set with "keep = true" - let configmaps_api: Api = Api::all(kube_client.clone()); - let lp = ListParams::default(); - let configmaps = configmaps_api - .list(&lp) - .await? - .items - .into_iter() - .filter(|configmap| { - let configmap_name = configmap.name(); - let configmap_namespace = configmap.namespace().unwrap(); - if !configmap_name.contains(MANAGEMENT_CONFIGMAP_PREFIX) { - return false; - } - if let Some(data) = &configmap.data { - info!("Got configmap {} with data: {:?}", &configmap_name, data); - return check_namespace_for_cleanup( - data, - configmap_namespace, - time_since_the_epoch, - ); - } - false - }) - .collect::>(); - for configmap in configmaps { - let namespace = configmap.namespace().unwrap(); - uninstall_testnet_resources(namespace).await?; - } - - Ok(()) -} - -fn check_namespace_for_cleanup( - data: &BTreeMap, - namespace: String, - time_since_the_epoch: u64, -) -> bool { - let keep: bool = data.get("keep").unwrap().parse().unwrap(); - if keep { - info!("Explicitly keeping namespace {}", namespace); - return false; - } - if data.get("cleanup").is_none() { - // This is needed for backward compatibility where older namespaces created - // don't have "cleanup" time set. Delete this code once we roll out the cleanup - // feature fully - let start: u64 = data.get("start").unwrap().parse().unwrap(); - let namespace_uptime = time_since_the_epoch - start; - info!( - "Namespace {} has lived for {}/{} seconds", - namespace, namespace_uptime, NAMESPACE_CLEANUP_THRESHOLD_SECS - ); - if keep { - info!("Explicitly keeping namespace {}", namespace); - return false; - } - if namespace_uptime > NAMESPACE_CLEANUP_THRESHOLD_SECS { - return true; - } - } else { - // TODO(rustielin): come up with some sane values for namespaces - let cleanup_time_since_epoch: u64 = data.get("cleanup").unwrap().parse().unwrap(); - - if cleanup_time_since_epoch <= time_since_the_epoch { - info!("Namespace {} will be cleaned up", namespace,); - return true; - } else { - info!( - "Namespace {} has remaining {} seconds before cleanup", - namespace, - cleanup_time_since_epoch - time_since_the_epoch - ); - } - } - false -} - -/// Ensures that the label is at most 64 characters to meet k8s -/// label length requirements. -pub fn make_k8s_label(value: String) -> String { - value.get(..63).unwrap_or(&value).to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::FailedNamespacesApi; - - #[tokio::test] - async fn test_create_namespace_final_error() { - let namespace_creator = Arc::new(FailedNamespacesApi::from_status_code(401)); - let result = create_namespace(namespace_creator, "banana".to_string()).await; - match result { - Err(ApiError::FinalError(_)) => {}, - _ => panic!("Expected final error"), - } - } - - #[tokio::test] - async fn test_construct_node_helm_values() { - let node_helm_values = construct_node_helm_values( - None, - "{}".to_string(), - "forge-123".to_string(), - "era".to_string(), - 5, - 6, - "image".to_string(), - true, - ) - .unwrap(); - - let expected_helm_values = "--- -numValidators: 5 -numFullnodeGroups: 6 -imageTag: image -chain: - era: era -haproxy: - enabled: true -labels: - forge-namespace: forge-123 - forge-image-tag: image - forge-test-suite: unknown-testsuite - forge-username: unknown-username -"; - assert_eq!(node_helm_values, expected_helm_values); - } - - #[tokio::test] - async fn test_construct_genesis_helm_values() { - let genesis_helm_values = construct_genesis_helm_values( - Some(Arc::new(|helm_values| { - helm_values["chain"]["epoch_duration_secs"] = 60.into(); - })), - "forge-123".to_string(), - "era".to_string(), - 5, - "genesis_image".to_string(), - true, - ) - .unwrap(); - let expected_helm_values = "--- -imageTag: genesis_image -chain: - era: era - root_key: 48136DF3174A3DE92AFDB375FFE116908B69FF6FAB9B1410E548A33FEA1D159D - epoch_duration_secs: 60 -genesis: - numValidators: 5 - validator: - internal_host_suffix: validator-lb - key_seed: \"80000\" - fullnode: - internal_host_suffix: fullnode-lb -labels: - forge-namespace: forge-123 - forge-image-tag: genesis_image - forge-test-suite: unknown-testsuite - forge-username: unknown-username -"; - assert_eq!(genesis_helm_values, expected_helm_values); - println!("{}", genesis_helm_values); - } - - #[tokio::test] - async fn test_create_namespace_retryable_error() { - let namespace_creator = Arc::new(FailedNamespacesApi::from_status_code(403)); - let result = create_namespace(namespace_creator, "banana".to_string()).await; - match result { - Err(ApiError::RetryableError(_)) => {}, - _ => panic!("Expected retryable error"), - } - } - - #[tokio::test] - async fn test_check_namespace_for_cleanup() { - let start = SystemTime::now(); - let time_since_the_epoch = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - - let mut data = BTreeMap::new(); - - // Ensure very old run without keep is cleaned up. - data.insert("keep".to_string(), "false".to_string()); - data.insert("start".to_string(), "0".to_string()); - - assert!(check_namespace_for_cleanup( - &data, - "foo".to_string(), - time_since_the_epoch - )); - - // Ensure old run with keep is not cleaned up. - data.insert("keep".to_string(), "true".to_string()); - data.insert("start".to_string(), "0".to_string()); - - assert!(!check_namespace_for_cleanup( - &data, - "foo".to_string(), - time_since_the_epoch - )); - - // Ensure very old run without keep is cleaned up. - data.insert("keep".to_string(), "false".to_string()); - data.insert("cleanup".to_string(), "20".to_string()); - - assert!(check_namespace_for_cleanup( - &data, - "foo".to_string(), - time_since_the_epoch - )); - - // Ensure old run with keep is not cleaned up. - data.insert("keep".to_string(), "true".to_string()); - data.insert("cleanup".to_string(), "20".to_string()); - - assert!(!check_namespace_for_cleanup( - &data, - "foo".to_string(), - time_since_the_epoch - )); - - // Ensure a run with clean up some time in future is not cleaned up. - data.insert("keep".to_string(), "false".to_string()); - let cleanup_time = (start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - + Duration::from_secs(3600)) - .as_secs(); - data.insert("cleanup".to_string(), cleanup_time.to_string()); - - assert!(!check_namespace_for_cleanup( - &data, - "foo".to_string(), - time_since_the_epoch - )); - } -} diff --git a/testsuite/forge/src/backend/k8s/constants.rs b/testsuite/forge/src/backend/k8s/constants.rs deleted file mode 100644 index 4457483f9d03b..0000000000000 --- a/testsuite/forge/src/backend/k8s/constants.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -/// A collection of constants and default values for configuring various Forge components. - -// These are test keys for forge ephemeral networks. Do not use these elsewhere! -pub const DEFAULT_ROOT_KEY: &str = - "48136DF3174A3DE92AFDB375FFE116908B69FF6FAB9B1410E548A33FEA1D159D"; -pub const DEFAULT_ROOT_PRIV_KEY: &str = - "E25708D90C72A53B400B27FC7602C4D546C7B7469FA6E12544F0EBFB2F16AE19"; - -// Seed to generate keys for forge tests. -pub const FORGE_KEY_SEED: &str = "80000"; - -// binaries expected to be present on test runner -pub const HELM_BIN: &str = "helm"; -pub const KUBECTL_BIN: &str = "kubectl"; - -// helm release names and helm chart paths -pub const APTOS_NODE_HELM_RELEASE_NAME: &str = "aptos-node"; -pub const GENESIS_HELM_RELEASE_NAME: &str = "genesis"; -pub const APTOS_NODE_HELM_CHART_PATH: &str = "terraform/helm/aptos-node"; -pub const GENESIS_HELM_CHART_PATH: &str = "terraform/helm/genesis"; - -// cleanup namespaces after 30 minutes unless "keep = true" -pub const NAMESPACE_CLEANUP_THRESHOLD_SECS: u64 = 1800; -// Leave a buffer of around 20 minutes for test provisioning and cleanup to be done before cleaning -// up underlying resources. -pub const NAMESPACE_CLEANUP_DURATION_BUFFER_SECS: u64 = 1200; -pub const POD_CLEANUP_THRESHOLD_SECS: u64 = 86400; -pub const MANAGEMENT_CONFIGMAP_PREFIX: &str = "forge-management"; - -// this is the port on the validator service itself, as opposed to 80 on the validator haproxy service -pub const NODE_METRIC_PORT: u32 = 9101; -pub const REST_API_SERVICE_PORT: u32 = 8080; -pub const REST_API_HAPROXY_SERVICE_PORT: u32 = 80; -// when we interact with the node over port-forward -pub const LOCALHOST: &str = "127.0.0.1"; - -// kubernetes service names -pub const VALIDATOR_SERVICE_SUFFIX: &str = "validator"; -pub const FULLNODE_SERVICE_SUFFIX: &str = "fullnode"; -pub const VALIDATOR_HAPROXY_SERVICE_SUFFIX: &str = "validator-lb"; -pub const FULLNODE_HAPROXY_SERVICE_SUFFIX: &str = "fullnode-lb"; -pub const HAPROXY_SERVICE_SUFFIX: &str = "lb"; - -// kubernetes resource names for validator 0, which may be used for templating -pub const VALIDATOR_0_STATEFUL_SET_NAME: &str = "aptos-node-0-validator"; -pub const VALIDATOR_0_GENESIS_SECRET_PREFIX: &str = "aptos-node-0-genesis"; -pub const VALIDATOR_0_DATA_PERSISTENT_VOLUME_CLAIM_PREFIX: &str = "aptos-node-0-validator"; - -// metadata about the cluster -pub const DEFAULT_TEST_SUITE_NAME: &str = "unknown-testsuite"; -pub const DEFAULT_USERNAME: &str = "unknown-username"; diff --git a/testsuite/forge/src/backend/k8s/fullnode.rs b/testsuite/forge/src/backend/k8s/fullnode.rs deleted file mode 100644 index f7537e3a492cb..0000000000000 --- a/testsuite/forge/src/backend/k8s/fullnode.rs +++ /dev/null @@ -1,778 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - get_stateful_set_image, make_k8s_label, K8sNode, ReadWrite, Result, Version, - DEFAULT_TEST_SUITE_NAME, DEFAULT_USERNAME, REST_API_SERVICE_PORT, - VALIDATOR_0_DATA_PERSISTENT_VOLUME_CLAIM_PREFIX, VALIDATOR_0_GENESIS_SECRET_PREFIX, - VALIDATOR_0_STATEFUL_SET_NAME, -}; -use anyhow::Context; -use aptos_config::{ - config::{ - ApiConfig, BaseConfig, DiscoveryMethod, ExecutionConfig, NetworkConfig, NodeConfig, - OverrideNodeConfig, RoleType, WaypointConfig, - }, - network_id::NetworkId, -}; -use aptos_logger::info; -use aptos_sdk::types::PeerId; -use aptos_short_hex_str::AsShortHexStr; -use k8s_openapi::{ - api::{ - apps::v1::{StatefulSet, StatefulSetSpec}, - core::v1::{ - ConfigMap, ConfigMapVolumeSource, Container, PersistentVolumeClaim, - PersistentVolumeClaimSpec, PodSpec, PodTemplateSpec, ResourceRequirements, - SecretVolumeSource, Service, ServicePort, ServiceSpec, Volume, VolumeMount, - }, - }, - apimachinery::pkg::apis::meta::v1::LabelSelector, -}; -use kube::api::{ObjectMeta, PostParams}; -use std::{ - collections::BTreeMap, - env, - net::{Ipv4Addr, SocketAddr, SocketAddrV4}, - path::PathBuf, - sync::Arc, -}; -use tempfile::TempDir; - -// these are constants given by the aptos-node helm chart -// see terraform/helm/aptos-node/templates/validator.yaml - -// the name of the NodeConfig for the PFN, as well as the key in the k8s ConfigMap -// where the NodeConfig is stored -const FULLNODE_CONFIG_MAP_KEY: &str = "fullnode.yaml"; - -// the path where the genesis is mounted in the validator -const GENESIS_CONFIG_VOLUME_NAME: &str = "genesis-config"; -const GENESIS_CONFIG_VOLUME_PATH: &str = "/opt/aptos/genesis"; -const GENESIS_CONFIG_WRITABLE_VOLUME_NAME: &str = "writable-genesis"; - -// the path where the config file is mounted in the fullnode -const APTOS_CONFIG_VOLUME_NAME: &str = "aptos-config"; -const APTOS_CONFIG_VOLUME_PATH: &str = "/opt/aptos/etc"; - -// the path where the data volume is mounted in the fullnode -const APTOS_DATA_VOLUME_NAME: &str = "aptos-data"; -const APTOS_DATA_VOLUME_PATH: &str = "/opt/aptos/data"; - -/// Derive the fullnode image from the validator image. They will share the same image repo (validator), but not necessarily the version (image tag) -fn get_fullnode_image_from_validator_image( - validator_stateful_set: &StatefulSet, - version: &Version, -) -> Result { - let fullnode_kube_image = get_stateful_set_image(validator_stateful_set)?; - let fullnode_image_repo = fullnode_kube_image.name; - - // fullnode uses the validator image, with a different image tag - Ok(format!("{}:{}", fullnode_image_repo, version)) -} - -/// Create a ConfigMap with the given NodeConfig, with a constant key -async fn create_node_config_configmap( - node_config_config_map_name: String, - node_config: &OverrideNodeConfig, -) -> Result { - let mut data: BTreeMap = BTreeMap::new(); - data.insert( - FULLNODE_CONFIG_MAP_KEY.to_string(), - serde_yaml::to_string(&node_config.get_yaml()?)?, - ); - let node_config_config_map = ConfigMap { - binary_data: None, - data: Some(data.clone()), - metadata: ObjectMeta { - name: Some(node_config_config_map_name), - ..ObjectMeta::default() - }, - immutable: None, - }; - Ok(node_config_config_map) -} - -/// Create a PFN data volume by using the validator data volume as a template -fn create_fullnode_persistent_volume_claim( - validator_data_volume: PersistentVolumeClaim, -) -> Result { - let volume_requests = validator_data_volume - .spec - .as_ref() - .expect("Could not get volume spec from validator data volume") - .resources - .as_ref() - .expect("Could not get volume resources from validator data volume") - .requests - .clone(); - - Ok(PersistentVolumeClaim { - metadata: ObjectMeta { - name: Some(APTOS_DATA_VOLUME_NAME.to_string()), - ..ObjectMeta::default() - }, - spec: Some(PersistentVolumeClaimSpec { - access_modes: Some(vec!["ReadWriteOnce".to_string()]), - resources: Some(ResourceRequirements { - requests: volume_requests, - ..ResourceRequirements::default() - }), - ..PersistentVolumeClaimSpec::default() - }), - ..PersistentVolumeClaim::default() - }) -} - -fn create_fullnode_labels(fullnode_name: String) -> BTreeMap { - // if present, tag the node with the test suite name and username - let suite_name = env::var("FORGE_TEST_SUITE").unwrap_or(DEFAULT_TEST_SUITE_NAME.to_string()); - let username = env::var("FORGE_USERNAME").unwrap_or(DEFAULT_USERNAME.to_string()); - - [ - ("app.kubernetes.io/name".to_string(), "fullnode".to_string()), - ("app.kubernetes.io/instance".to_string(), fullnode_name), - ("forge-test-suite".to_string(), make_k8s_label(suite_name)), - ("forge-username".to_string(), make_k8s_label(username)), - ( - "app.kubernetes.io/part-of".to_string(), - "forge-pfn".to_string(), - ), - ] - .iter() - .cloned() - .collect() -} - -fn create_fullnode_service(fullnode_name: String) -> Result { - Ok(Service { - metadata: ObjectMeta { - name: Some(fullnode_name.clone()), - ..ObjectMeta::default() - }, - spec: Some(ServiceSpec { - selector: Some(create_fullnode_labels(fullnode_name)), - // for now, only expose the REST API - ports: Some(vec![ServicePort { - port: REST_API_SERVICE_PORT as i32, - ..ServicePort::default() - }]), - ..ServiceSpec::default() - }), - ..Service::default() - }) -} - -fn create_fullnode_container( - fullnode_image: String, - validator_container: &Container, -) -> Result { - Ok(Container { - image: Some(fullnode_image), - command: Some(vec![ - "/usr/local/bin/aptos-node".to_string(), - "-f".to_string(), - format!("/opt/aptos/etc/{}", FULLNODE_CONFIG_MAP_KEY), - ]), - volume_mounts: Some(vec![ - VolumeMount { - mount_path: APTOS_CONFIG_VOLUME_PATH.to_string(), - name: APTOS_CONFIG_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - VolumeMount { - mount_path: APTOS_DATA_VOLUME_PATH.to_string(), - name: APTOS_DATA_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - VolumeMount { - mount_path: GENESIS_CONFIG_VOLUME_PATH.to_string(), - name: GENESIS_CONFIG_WRITABLE_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - ]), - name: "fullnode".to_string(), - // specifically, inherit resources, env,ports, securityContext from the validator's container - ..validator_container.clone() - }) -} - -fn create_fullnode_volumes( - fullnode_genesis_secret_name: String, - fullnode_node_config_config_map_name: String, -) -> Vec { - vec![ - Volume { - name: GENESIS_CONFIG_VOLUME_NAME.to_string(), - secret: Some(SecretVolumeSource { - secret_name: Some(fullnode_genesis_secret_name), - ..SecretVolumeSource::default() - }), - ..Volume::default() - }, - Volume { - name: APTOS_CONFIG_VOLUME_NAME.to_string(), - config_map: Some(ConfigMapVolumeSource { - name: Some(fullnode_node_config_config_map_name), - ..ConfigMapVolumeSource::default() - }), - ..Volume::default() - }, - Volume { - name: GENESIS_CONFIG_WRITABLE_VOLUME_NAME.to_string(), - empty_dir: Some(Default::default()), - ..Volume::default() - }, - ] -} - -/// Create a fullnode StatefulSet given some templates from the validator -fn create_fullnode_stateful_set( - fullnode_name: String, - fullnode_image: String, - fullnode_genesis_secret_name: String, - fullnode_node_config_config_map_name: String, - validator_stateful_set: StatefulSet, - validator_data_volume: PersistentVolumeClaim, -) -> Result { - // extract some useful structs from the validator - let validator_stateful_set_spec = validator_stateful_set - .spec - .as_ref() - .context("Validator StatefulSet does not have spec")? - .clone(); - let validator_stateful_set_pod_spec = validator_stateful_set_spec - .template - .spec - .as_ref() - .context("Validator StatefulSet does not have spec.template.spec")? - .clone(); - - let validator_container = validator_stateful_set_pod_spec - .containers - .first() - .context("Validator StatefulSet does not have any containers")?; - - // common labels - let labels_map: BTreeMap = create_fullnode_labels(fullnode_name.clone()); - - // create the fullnode data volume - let data_volume = create_fullnode_persistent_volume_claim(validator_data_volume)?; - - // create the fullnode container - let fullnode_container = create_fullnode_container(fullnode_image, validator_container)?; - - // create the fullnode volumes - let fullnode_volumes = create_fullnode_volumes( - fullnode_genesis_secret_name, - fullnode_node_config_config_map_name, - ); - - // build the fullnode stateful set - let mut fullnode_stateful_set = StatefulSet::default(); - fullnode_stateful_set.metadata.name = Some(fullnode_name.clone()); - fullnode_stateful_set.metadata.labels = Some(labels_map.clone()); - fullnode_stateful_set.spec = Some(StatefulSetSpec { - service_name: fullnode_name, // the name of the service is the same as that of the fullnode - selector: LabelSelector { - match_labels: Some(labels_map.clone()), - ..LabelSelector::default() - }, - volume_claim_templates: Some(vec![data_volume]), // a PVC that is created directly by the StatefulSet, and owned by it - template: PodTemplateSpec { - metadata: Some(ObjectMeta { - labels: Some(labels_map), - ..ObjectMeta::default() - }), - spec: Some(PodSpec { - containers: vec![fullnode_container], - volumes: Some(fullnode_volumes), - // specifically, inherit nodeSelector, affinity, tolerations, securityContext, serviceAccountName from the validator's PodSpec - ..validator_stateful_set_pod_spec.clone() - }), - }, - ..validator_stateful_set_spec - }); - Ok(fullnode_stateful_set) -} - -/// Create a default PFN NodeConfig that uses the genesis, waypoint, and data paths expected in k8s -pub fn get_default_pfn_node_config() -> NodeConfig { - let mut waypoint_path = PathBuf::from(GENESIS_CONFIG_VOLUME_PATH); - waypoint_path.push("waypoint.txt"); - - let mut genesis_path = PathBuf::from(GENESIS_CONFIG_VOLUME_PATH); - genesis_path.push("genesis.blob"); - - NodeConfig { - base: BaseConfig { - role: RoleType::FullNode, - data_dir: PathBuf::from(APTOS_DATA_VOLUME_PATH), - waypoint: WaypointConfig::FromFile(waypoint_path), - ..BaseConfig::default() - }, - execution: ExecutionConfig { - genesis_file_location: genesis_path, - ..ExecutionConfig::default() - }, - full_node_networks: vec![NetworkConfig { - network_id: NetworkId::Public, - discovery_method: DiscoveryMethod::Onchain, - // defaults to listening on "/ip4/0.0.0.0/tcp/6180" - ..NetworkConfig::default() - }], - api: ApiConfig { - // API defaults to listening on "127.0.0.1:8080". Override with 0.0.0.0:8080 - address: SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new(0, 0, 0, 0), - REST_API_SERVICE_PORT as u16, - )), - ..ApiConfig::default() - }, - ..NodeConfig::default() - } -} - -/// Create a PFN stateful set workload -/// This function assumes that the swarm has already been set up (e.g. there are already validators running) as it borrows -/// some artifacts such as genesis from the 0th validator -/// The given NodeConfig will be merged with the default PFN NodeConfig for Forge -pub async fn install_public_fullnode<'a>( - stateful_set_api: Arc>, - configmap_api: Arc>, - persistent_volume_claim_api: Arc>, - service_api: Arc>, - version: &'a Version, - node_config: &'a OverrideNodeConfig, - era: String, - namespace: String, - use_port_forward: bool, - index: usize, -) -> Result<(PeerId, K8sNode)> { - let node_peer_id = node_config - .override_config() - .get_peer_id() - .unwrap_or_else(PeerId::random); - let fullnode_name = format!("public-fullnode-{}-{}", index, node_peer_id.short_str()); - - // create the NodeConfig configmap - let fullnode_node_config_config_map_name = format!("{}-config", fullnode_name.clone()); - let fullnode_node_config_config_map = - create_node_config_configmap(fullnode_node_config_config_map_name.clone(), node_config) - .await?; - configmap_api - .create(&PostParams::default(), &fullnode_node_config_config_map) - .await?; - - // assume that the validator workload (val0) has already been created (not necessarily running yet) - // get its spec so we can inherit some of its properties - let validator_stateful_set = stateful_set_api.get(VALIDATOR_0_STATEFUL_SET_NAME).await?; - - // get the fullnode image - let fullnode_image_full = - get_fullnode_image_from_validator_image(&validator_stateful_set, version)?; - - // borrow genesis secret from the first validator. it follows this naming convention - let fullnode_genesis_secret_name = format!("{}-e{}", VALIDATOR_0_GENESIS_SECRET_PREFIX, era); - let validator_data_persistent_volume_claim_name = format!( - "{}-e{}", - VALIDATOR_0_DATA_PERSISTENT_VOLUME_CLAIM_PREFIX, era - ); - - // create the data volume - let validator_data_volume = persistent_volume_claim_api - .get(validator_data_persistent_volume_claim_name.as_str()) - .await - .map_err(|e| { - anyhow::anyhow!( - "Could not get validator data volume to inherit from {:?}: {:?}", - validator_data_persistent_volume_claim_name, - e - ) - })?; - - let fullnode_stateful_set = create_fullnode_stateful_set( - fullnode_name.clone(), - fullnode_image_full, - fullnode_genesis_secret_name, - fullnode_node_config_config_map_name, - validator_stateful_set, - validator_data_volume, - )?; - - // check that all the labels are the same - let fullnode_metadata_labels = fullnode_stateful_set - .metadata - .labels - .as_ref() - .context("Validator StatefulSet does not have metadata.labels")?; - let fullnode_spec_selector_match_labels = fullnode_stateful_set - .spec - .as_ref() - .context("Validator StatefulSet does not have spec")? - .selector - .match_labels - .as_ref() - .context("Validator StatefulSet does not have spec.selector.match_labels")?; - let fullnode_spec_template_metadata_labels = fullnode_stateful_set - .spec - .as_ref() - .context("Validator StatefulSet does not have spec")? - .template - .metadata - .as_ref() - .context("Validator StatefulSet does not have spec.template.metadata")? - .labels - .as_ref() - .context("Validator StatefulSet does not have spec.template.metadata.labels")?; - - let labels = [ - fullnode_metadata_labels, - fullnode_spec_selector_match_labels, - fullnode_spec_template_metadata_labels, - ]; - for label1 in labels.into_iter() { - for label2 in labels.into_iter() { - assert_eq!(label1, label2); - } - } - - let fullnode_service = create_fullnode_service(fullnode_name.clone())?; - - // write the spec to file - let tmp_dir = TempDir::new().expect("Could not create temp dir"); - let fullnode_config_path = tmp_dir.path().join("fullnode.yaml"); - let fullnode_config_file = std::fs::File::create(&fullnode_config_path) - .with_context(|| format!("Could not create file {:?}", fullnode_config_path))?; - serde_yaml::to_writer(fullnode_config_file, &fullnode_stateful_set)?; - - let fullnode_service_path = tmp_dir.path().join("service.yaml"); - let fullnode_service_file = std::fs::File::create(&fullnode_service_path) - .with_context(|| format!("Could not create file {:?}", fullnode_service_path))?; - serde_yaml::to_writer(fullnode_service_file, &fullnode_service)?; - info!("Wrote fullnode k8s specs to path: {:?}", &tmp_dir); - - // create the StatefulSet - let sts = stateful_set_api - .create(&PostParams::default(), &fullnode_stateful_set) - .await?; - let fullnode_stateful_set_str = serde_yaml::to_string(&fullnode_stateful_set)?; - info!( - "Created fullnode StatefulSet:\n---{}\n---", - &fullnode_stateful_set_str - ); - // and its service - service_api - .create(&PostParams::default(), &fullnode_service) - .await?; - let fullnode_service_str = serde_yaml::to_string(&fullnode_service)?; - info!( - "Created fullnode Service:\n---{}\n---", - fullnode_service_str - ); - - let service_name = &fullnode_service - .metadata - .name - .context("Fullnode Service does not have metadata.name")?; - - let full_service_name = format!("{}.{}.svc", service_name, &namespace); // this is the full name that includes the namespace - - // Append the cluster name if its a multi-cluster deployment - let full_service_name = if let Some(target_cluster_name) = sts - .metadata - .labels - .as_ref() - .and_then(|labels| labels.get("multicluster/targetcluster")) - { - format!("{}.{}", &full_service_name, &target_cluster_name) - } else { - full_service_name - }; - - let ret_node = K8sNode { - name: fullnode_name.clone(), - stateful_set_name: fullnode_stateful_set - .metadata - .name - .context("Fullnode StatefulSet does not have metadata.name")?, - peer_id: node_peer_id, - index, - service_name: full_service_name, - version: version.clone(), - namespace, - haproxy_enabled: false, - - port_forward_enabled: use_port_forward, - rest_api_port: REST_API_SERVICE_PORT, // in the case of port-forward, this port will be changed at runtime - }; - - Ok((node_peer_id, ret_node)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - MockConfigMapApi, MockPersistentVolumeClaimApi, MockServiceApi, MockStatefulSetApi, - }; - use aptos_config::config::Identity; - use aptos_sdk::crypto::{x25519::PrivateKey, Uniform}; - use k8s_openapi::apimachinery::pkg::api::resource::Quantity; - - /// Get a dummy validator persistent volume claim that looks like one created by terraform/helm/aptos-node/templates/validator.yaml - fn get_dummy_validator_persistent_volume_claim() -> PersistentVolumeClaim { - PersistentVolumeClaim { - metadata: ObjectMeta { - name: Some("aptos-node-0-validator-e42069".to_string()), - ..ObjectMeta::default() - }, - spec: Some(PersistentVolumeClaimSpec { - access_modes: Some(vec!["ReadWriteOnce".to_string()]), - resources: Some(ResourceRequirements { - requests: Some( - [ - ("storage".to_string(), Quantity("1Gi".to_string())), - ("storage2".to_string(), Quantity("2Gi".to_string())), - ] - .iter() - .cloned() - .collect(), - ), - ..ResourceRequirements::default() - }), - ..PersistentVolumeClaimSpec::default() - }), - ..PersistentVolumeClaim::default() - } - } - - /// Get a dummy validator stateful set that looks like one created by terraform/helm/aptos-node/templates/validator.yaml - fn get_dummy_validator_stateful_set() -> StatefulSet { - let labels: BTreeMap = [ - ( - "app.kubernetes.io/name".to_string(), - "validator".to_string(), - ), - ( - "app.kubernetes.io/instance".to_string(), - "aptos-node-0-validator-0".to_string(), - ), - ( - "app.kubernetes.io/part-of".to_string(), - "forge-pfn".to_string(), - ), - ] - .iter() - .cloned() - .collect(); - StatefulSet { - metadata: ObjectMeta { - name: Some("aptos-node-0-validator".to_string()), - labels: Some(labels.clone()), - ..ObjectMeta::default() - }, - spec: Some(StatefulSetSpec { - replicas: Some(1), - template: PodTemplateSpec { - metadata: Some(ObjectMeta { - labels: Some(labels), - ..ObjectMeta::default() - }), - spec: Some(PodSpec { - containers: vec![Container { - name: "validator".to_string(), - image: Some( - "banana.fruit.aptos/potato/validator:banana_image_tag".to_string(), - ), - command: Some(vec![ - "/usr/local/bin/aptos-node".to_string(), - "-f".to_string(), - "/opt/aptos/etc/validator.yaml".to_string(), - ]), - volume_mounts: Some(vec![ - VolumeMount { - mount_path: APTOS_CONFIG_VOLUME_PATH.to_string(), - name: APTOS_CONFIG_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - VolumeMount { - mount_path: APTOS_DATA_VOLUME_PATH.to_string(), - name: APTOS_DATA_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - VolumeMount { - mount_path: GENESIS_CONFIG_VOLUME_PATH.to_string(), - name: GENESIS_CONFIG_WRITABLE_VOLUME_NAME.to_string(), - ..VolumeMount::default() - }, - ]), - ..Container::default() - }], - ..PodSpec::default() - }), - }, - ..StatefulSetSpec::default() - }), - ..StatefulSet::default() - } - } - - #[tokio::test] - /// Test that we can create a node config configmap and that it contains the node config at a known data key - async fn test_create_node_config_map() { - let config_map_name = "aptos-node-0-validator-0-config".to_string(); - let node_config = NodeConfig::default(); - let override_config = OverrideNodeConfig::new_with_default_base(node_config.clone()); - - // expect that the one we get is the same as the one we created - let created_config_map = - create_node_config_configmap(config_map_name.clone(), &override_config) - .await - .unwrap(); - - let regenerated_node_config = serde_yaml::from_str::( - created_config_map - .data - .unwrap() - .get(FULLNODE_CONFIG_MAP_KEY) - .unwrap(), - ) - .unwrap(); - assert_eq!(regenerated_node_config, node_config); - } - - #[test] - /// Test that we can create a data volume from an existing validator data volume, and that we inherit the resource requests - fn test_create_persistent_volume_claim() { - let requests = Some( - [ - ("storage".to_string(), Quantity("1Gi".to_string())), - ("storage2".to_string(), Quantity("2Gi".to_string())), - ] - .iter() - .cloned() - .collect(), - ); - let pvc = PersistentVolumeClaim { - metadata: ObjectMeta { - name: Some(APTOS_DATA_VOLUME_NAME.to_string()), - ..ObjectMeta::default() - }, - spec: Some(PersistentVolumeClaimSpec { - access_modes: Some(vec!["ReadWriteOnce".to_string()]), - resources: Some(ResourceRequirements { - requests, - ..ResourceRequirements::default() - }), - ..PersistentVolumeClaimSpec::default() - }), - ..PersistentVolumeClaim::default() - }; - let created_pvc = create_fullnode_persistent_volume_claim(pvc.clone()); - - // assert that the resources are the same - assert_eq!( - created_pvc.unwrap().spec.unwrap().resources, - pvc.spec.unwrap().resources - ); - } - - #[test] - /// Test that the created StatefulSet and Service are connected - fn test_create_fullnode_stateful_set_and_service_connected() { - // top level args - let era = 42069; - let peer_id = PeerId::random(); - let fullnode_name = "fullnode-".to_string() + &peer_id.to_string(); // everything should be keyed on this - let fullnode_image = "fruit.com/banana:latest".to_string(); - let fullnode_genesis_secret_name = format!("aptos-node-0-genesis-e{}", era); - let fullnode_node_config_config_map_name = format!("{}-config", fullnode_name); - - let fullnode_stateful_set = create_fullnode_stateful_set( - fullnode_name.clone(), - fullnode_image, - fullnode_genesis_secret_name, - fullnode_node_config_config_map_name, - get_dummy_validator_stateful_set(), - get_dummy_validator_persistent_volume_claim(), - ) - .unwrap(); - - let fullnode_service = create_fullnode_service(fullnode_name.clone()).unwrap(); - - // assert that the StatefulSet has the correct name - assert_eq!( - fullnode_stateful_set.metadata.name, - Some(fullnode_name.clone()) - ); - // assert that the Service has the correct name - assert_eq!(fullnode_service.metadata.name, Some(fullnode_name.clone())); - // assert that the StatefulSet has a serviceName that matches the Service - assert_eq!( - fullnode_stateful_set.spec.unwrap().service_name, - fullnode_name - ); - // assert that the labels in the Service match the StatefulSet - assert_eq!( - fullnode_service.spec.unwrap().selector, - fullnode_stateful_set.metadata.labels - ); - } - - #[tokio::test] - /// Full PFN installation test, checking that the resulting resources created are as expected - async fn test_install_public_fullnode() { - // top level args - let peer_id = PeerId::random(); - let version = Version::new(0, "banana".to_string()); - - // create APIs - let stateful_set_api = Arc::new(MockStatefulSetApi::from_stateful_set( - get_dummy_validator_stateful_set(), - )); - let configmap_api = Arc::new(MockConfigMapApi::from_config_map(ConfigMap::default())); - let persistent_volume_claim_api = - Arc::new(MockPersistentVolumeClaimApi::from_persistent_volume_claim( - get_dummy_validator_persistent_volume_claim(), - )); - let service_api = Arc::new(MockServiceApi::from_service(Service::default())); - - // get the base config and mutate it - let mut node_config = get_default_pfn_node_config(); - node_config.full_node_networks[0].identity = - Identity::from_config(PrivateKey::generate_for_testing(), peer_id); - let override_config = OverrideNodeConfig::new_with_default_base(node_config); - - let era = "42069".to_string(); - let namespace = "forge42069".to_string(); - - let (created_peer_id, created_node) = install_public_fullnode( - stateful_set_api, - configmap_api, - persistent_volume_claim_api, - service_api, - &version, - &override_config, - era, - namespace, - false, - 7, - ) - .await - .unwrap(); - - // assert the created resources match some patterns - assert_eq!(created_peer_id, peer_id); - assert_eq!( - created_node.name, - format!( - "public-fullnode-{}-{}", - created_node.index, - &peer_id.short_str() - ) - ); - assert!(created_node.name.len() < 64); // This is a k8s limit - assert_eq!(created_node.index, 7); - } -} diff --git a/testsuite/forge/src/backend/k8s/helm-values/aptos-node-default-values.yaml b/testsuite/forge/src/backend/k8s/helm-values/aptos-node-default-values.yaml deleted file mode 100644 index 027696a5ed843..0000000000000 --- a/testsuite/forge/src/backend/k8s/helm-values/aptos-node-default-values.yaml +++ /dev/null @@ -1,38 +0,0 @@ -validator: - enableNetworkPolicy: false - rust_log: debug,hyper=off - # force enable the telemetry service to try to send telemetry - force_enable_telemetry: true - -fullnode: - # at most one VFN per validator, depending on numFullnodeGroups - groups: - - name: fullnode - replicas: 1 - rust_log: debug,hyper=off - # force enable the telemetry service to try to send telemetry - force_enable_telemetry: true - -# Make all services internal NodePort and open all ports -# NodePort is required for ChaosMesh to function correctly: https://github.com/chaos-mesh/chaos-mesh/issues/3278#issuecomment-1134248492 -service: - validator: - external: - type: "NodePort" - internal: - type: "NodePort" - enableRestApi: true - enableMetricsPort: true - - fullnode: - external: - type: "NodePort" - internal: - type: "NodePort" - enableRestApi: true - enableMetricsPort: true - -# always assume we're spinning up a testnet and doing genesis rather than using the single validator test mode -loadTestGenesis: false -# always run forge in privileged mode for extra testing capability -enablePrivilegedMode: true diff --git a/testsuite/forge/src/backend/k8s/kube_api.rs b/testsuite/forge/src/backend/k8s/kube_api.rs deleted file mode 100644 index 3ff3103fdff28..0000000000000 --- a/testsuite/forge/src/backend/k8s/kube_api.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use kube::{ - api::{Api, PostParams}, - client::Client as K8sClient, - Error as KubeError, Resource as ApiResource, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::fmt::Debug; - -// Create kube API wrapper traits such that they are testable - -#[derive(Clone)] -pub struct K8sApi { - api: Api, -} - -impl K8sApi -where - K: ApiResource, -{ - pub fn from_client(kube_client: K8sClient, kube_namespace: Option) -> Self - where - ::DynamicType: Default, - { - if let Some(kube_namespace) = kube_namespace { - K8sApi { - api: Api::namespaced(kube_client, &kube_namespace), - } - } else { - K8sApi { - api: Api::all(kube_client), - } - } - } -} - -#[async_trait] -pub trait ReadWrite: Send + Sync { - async fn get(&self, name: &str) -> Result; - async fn create(&self, pp: &PostParams, k: &K) -> Result; -} - -// Implement the traits for K8sApi - -#[async_trait] -impl ReadWrite for K8sApi -where - K: k8s_openapi::Resource + Send + Sync + Clone + DeserializeOwned + Serialize + Debug, -{ - async fn get(&self, name: &str) -> Result { - self.api.get(name).await - } - - async fn create(&self, pp: &PostParams, k: &K) -> Result { - self.api.create(pp, k).await - } -} - -#[cfg(test)] -pub mod mocks { - use super::*; - use crate::Result; - use async_trait::async_trait; - use hyper::StatusCode; - use k8s_openapi::api::{ - apps::v1::StatefulSet, - core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Secret, Service}, - }; - use kube::{api::PostParams, error::ErrorResponse, Error as KubeError}; - - // Mock StatefulSet API - - pub struct MockStatefulSetApi { - stateful_set: StatefulSet, - } - - impl MockStatefulSetApi { - pub fn from_stateful_set(stateful_set: StatefulSet) -> Self { - MockStatefulSetApi { stateful_set } - } - } - - #[async_trait] - impl ReadWrite for MockStatefulSetApi { - async fn get(&self, name: &str) -> Result { - if self.stateful_set.metadata.name == Some(name.to_string()) { - return Ok(self.stateful_set.clone()); - } - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "StatefulSet with name {} could not be found in {:?}", - name, self.stateful_set - ), - reason: "not_found".to_string(), - code: 404, - })); - } - - async fn create( - &self, - _pp: &PostParams, - stateful_set: &StatefulSet, - ) -> Result { - if self.stateful_set.metadata.name == stateful_set.metadata.name { - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "StatefulSet with same name already exists in {:?}", - self.stateful_set - ), - reason: "already_exists".to_string(), - code: 409, - })); - } - Ok(self.stateful_set.clone()) - } - } - - // Mock Pod API - - pub struct MockPodApi { - pod: Pod, - } - - impl MockPodApi { - pub fn from_pod(pod: Pod) -> Self { - MockPodApi { pod } - } - } - - #[async_trait] - impl ReadWrite for MockPodApi { - async fn get(&self, _name: &str) -> Result { - Ok(self.pod.clone()) - } - - async fn create(&self, _pp: &PostParams, _pod: &Pod) -> Result { - Ok(self.pod.clone()) - } - } - - // Mock ConfigMap API - - pub struct MockConfigMapApi { - config_map: ConfigMap, - } - - impl MockConfigMapApi { - pub fn from_config_map(config_map: ConfigMap) -> Self { - MockConfigMapApi { config_map } - } - } - - #[async_trait] - impl ReadWrite for MockConfigMapApi { - async fn get(&self, name: &str) -> Result { - if self.config_map.metadata.name == Some(name.to_string()) { - return Ok(self.config_map.clone()); - } - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "ConfigMap with name {} could not be found in {:?}", - name, self.config_map - ), - reason: "not_found".to_string(), - code: 404, - })); - } - - async fn create( - &self, - _pp: &PostParams, - config_map: &ConfigMap, - ) -> Result { - if self.config_map.metadata.name == config_map.metadata.name { - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "ConfigMap with same name already exists in {:?}", - self.config_map - ), - reason: "already_exists".to_string(), - code: 409, - })); - } - Ok(self.config_map.clone()) - } - } - - // Mock PersistentVolumeClaim API - - pub struct MockPersistentVolumeClaimApi { - persistent_volume_claim: PersistentVolumeClaim, - } - - impl MockPersistentVolumeClaimApi { - pub fn from_persistent_volume_claim( - persistent_volume_claim: PersistentVolumeClaim, - ) -> Self { - MockPersistentVolumeClaimApi { - persistent_volume_claim, - } - } - } - - #[async_trait] - impl ReadWrite for MockPersistentVolumeClaimApi { - async fn get(&self, name: &str) -> Result { - if self.persistent_volume_claim.metadata.name == Some(name.to_string()) { - return Ok(self.persistent_volume_claim.clone()); - } - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "PersistentVolumeClaim with name {} could not be found in {:?}", - name, self.persistent_volume_claim - ), - reason: "not_found".to_string(), - code: 404, - })); - } - - async fn create( - &self, - _pp: &PostParams, - persistent_volume_claim: &PersistentVolumeClaim, - ) -> Result { - if self.persistent_volume_claim.metadata.name == persistent_volume_claim.metadata.name { - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "PersistentVolumeClaim with same name already exists in {:?}", - self.persistent_volume_claim - ), - reason: "already_exists".to_string(), - code: 409, - })); - } - Ok(self.persistent_volume_claim.clone()) - } - } - - // Mock Service API - - pub struct MockServiceApi { - service: Service, - } - - impl MockServiceApi { - pub fn from_service(service: Service) -> Self { - MockServiceApi { service } - } - } - - #[async_trait] - impl ReadWrite for MockServiceApi { - async fn get(&self, name: &str) -> Result { - if self.service.metadata.name == Some(name.to_string()) { - return Ok(self.service.clone()); - } - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "Service with name {} could not be found in {:?}", - name, self.service - ), - reason: "not_found".to_string(), - code: 404, - })); - } - - async fn create(&self, _pp: &PostParams, service: &Service) -> Result { - if self.service.metadata.name == service.metadata.name { - return Err(KubeError::Api(ErrorResponse { - status: "failed".to_string(), - message: format!( - "Service with same name already exists in {:?}", - self.service - ), - reason: "already_exists".to_string(), - code: 409, - })); - } - Ok(self.service.clone()) - } - } - - // Mock Service API - pub struct MockSecretApi { - secret: Option, - } - - impl MockSecretApi { - pub fn from_secret(secret: Option) -> Self { - MockSecretApi { secret } - } - } - - #[async_trait] - impl ReadWrite for MockSecretApi { - async fn get(&self, _name: &str) -> Result { - match self.secret { - Some(ref s) => Ok(s.clone()), - None => Err(KubeError::Api(ErrorResponse { - status: "status".to_string(), - message: "message".to_string(), - reason: "reason".to_string(), - code: 404, - })), - } - } - - async fn create(&self, _pp: &PostParams, secret: &Secret) -> Result { - return Ok(secret.clone()); - } - } - - // Mock API that always fails to create a new Namespace - - pub struct FailedNamespacesApi { - status_code: u16, - } - - impl FailedNamespacesApi { - pub fn from_status_code(status_code: u16) -> Self { - FailedNamespacesApi { status_code } - } - } - - #[async_trait] - impl ReadWrite for FailedNamespacesApi { - async fn get(&self, _name: &str) -> Result { - let status = StatusCode::from_u16(self.status_code).unwrap(); - Err(KubeError::Api(ErrorResponse { - status: status.to_string(), - code: status.as_u16(), - message: "Failed to get namespace".to_string(), - reason: "Failed to parse error data".into(), - })) - } - - async fn create( - &self, - _pp: &PostParams, - _namespace: &Namespace, - ) -> Result { - let status = StatusCode::from_u16(self.status_code).unwrap(); - Err(KubeError::Api(ErrorResponse { - status: status.to_string(), - code: status.as_u16(), - message: "Failed to create namespace".to_string(), - reason: "Failed to parse error data".into(), - })) - } - } -} diff --git a/testsuite/forge/src/backend/k8s/mod.rs b/testsuite/forge/src/backend/k8s/mod.rs deleted file mode 100644 index 105495ca66f68..0000000000000 --- a/testsuite/forge/src/backend/k8s/mod.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{Factory, GenesisConfig, GenesisConfigFn, NodeConfigFn, Result, Swarm, Version}; -use anyhow::bail; -use aptos_logger::info; -use rand::rngs::StdRng; -use std::{convert::TryInto, num::NonZeroUsize, time::Duration}; - -pub mod chaos; -pub mod chaos_schema; -mod cluster_helper; -pub mod constants; -mod fullnode; -pub mod kube_api; -pub mod node; -pub mod prometheus; -mod stateful_set; -mod swarm; - -use aptos_sdk::crypto::ed25519::ED25519_PRIVATE_KEY_LENGTH; -pub use cluster_helper::*; -pub use constants::*; -pub use fullnode::*; -#[cfg(test)] -pub use kube_api::mocks::*; -pub use kube_api::*; -pub use node::K8sNode; -pub use stateful_set::*; -pub use swarm::*; - -pub struct K8sFactory { - root_key: [u8; ED25519_PRIVATE_KEY_LENGTH], - image_tag: String, - upgrade_image_tag: String, - kube_namespace: String, - use_port_forward: bool, - reuse: bool, - keep: bool, - enable_haproxy: bool, -} - -impl K8sFactory { - pub fn new( - kube_namespace: String, - image_tag: String, - upgrade_image_tag: String, - use_port_forward: bool, - reuse: bool, - keep: bool, - enable_haproxy: bool, - ) -> Result { - let root_key: [u8; ED25519_PRIVATE_KEY_LENGTH] = - hex::decode(DEFAULT_ROOT_PRIV_KEY)?.try_into().unwrap(); - - match kube_namespace.as_str() { - "default" => { - info!("Using the default kubernetes namespace"); - }, - s if s.starts_with("forge") => { - info!("Using forge namespace: {}", s); - }, - _ => { - bail!( - "Invalid kubernetes namespace provided: {}. Use forge-*", - kube_namespace - ); - }, - } - - Ok(Self { - root_key, - image_tag, - upgrade_image_tag, - kube_namespace, - use_port_forward, - reuse, - keep, - enable_haproxy, - }) - } -} - -#[async_trait::async_trait] -impl Factory for K8sFactory { - fn versions<'a>(&'a self) -> Box + 'a> { - let version = vec![ - Version::new(0, self.image_tag.clone()), - Version::new(1, self.upgrade_image_tag.clone()), - ]; - Box::new(version.into_iter()) - } - - async fn launch_swarm( - &self, - _rng: &mut StdRng, - num_validators: NonZeroUsize, - num_fullnodes: usize, - init_version: &Version, - genesis_version: &Version, - genesis_config: Option<&GenesisConfig>, - cleanup_duration: Duration, - genesis_config_fn: Option, - node_config_fn: Option, - existing_db_tag: Option, - ) -> Result> { - let genesis_modules_path = match genesis_config { - Some(config) => match config { - GenesisConfig::Bundle(_) => { - bail!("k8s forge backend does not support raw bytes as genesis modules. please specify a path instead") - }, - GenesisConfig::Path(path) => Some(path.clone()), - }, - None => None, - }; - - let kube_client = create_k8s_client().await?; - let (new_era, validators, fullnodes) = if self.reuse { - let (validators, fullnodes) = match collect_running_nodes( - &kube_client, - self.kube_namespace.clone(), - self.use_port_forward, - self.enable_haproxy, - ) - .await - { - Ok(res) => res, - Err(e) => { - bail!(e); - }, - }; - let new_era = None; // TODO: get the actual era - (new_era, validators, fullnodes) - } else { - // clear the cluster of resources - delete_k8s_resources(kube_client.clone(), &self.kube_namespace).await?; - // create the forge-management configmap before installing anything - create_management_configmap(self.kube_namespace.clone(), self.keep, cleanup_duration) - .await?; - if let Some(existing_db_tag) = existing_db_tag { - // TODO(prod-eng): For now we are managing PVs out of forge, and bind them manually - // with the volume. Going forward we should consider automate this process. - - // The previously claimed PVs are in Released stage once the corresponding PVC is - // gone. We reset its status to Available so they can be reused later. - reset_persistent_volumes(&kube_client).await?; - - // We return early here if there are not enough PVs to claim. - check_persistent_volumes( - kube_client, - num_validators.get() + num_fullnodes, - existing_db_tag, - ) - .await?; - } - // try installing testnet resources, but clean up if it fails - match install_testnet_resources( - self.kube_namespace.clone(), - num_validators.get(), - num_fullnodes, - format!("{}", init_version), - format!("{}", genesis_version), - genesis_modules_path, - self.use_port_forward, - self.enable_haproxy, - genesis_config_fn, - node_config_fn, - ) - .await - { - Ok(res) => (Some(res.0), res.1, res.2), - Err(e) => { - uninstall_testnet_resources(self.kube_namespace.clone()).await?; - bail!(e); - }, - } - }; - - let swarm = K8sSwarm::new( - &self.root_key, - &self.image_tag, - &self.upgrade_image_tag, - &self.kube_namespace, - validators, - fullnodes, - self.keep, - new_era, - self.use_port_forward, - ) - .await - .unwrap(); - Ok(Box::new(swarm)) - } -} diff --git a/testsuite/forge/src/backend/k8s/node.rs b/testsuite/forge/src/backend/k8s/node.rs deleted file mode 100644 index 8fd0fec22badf..0000000000000 --- a/testsuite/forge/src/backend/k8s/node.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - backend::k8s::stateful_set, get_free_port, scale_stateful_set_replicas, FullNode, - HealthCheckError, Node, NodeExt, Result, Validator, Version, KUBECTL_BIN, LOCALHOST, - NODE_METRIC_PORT, REST_API_HAPROXY_SERVICE_PORT, REST_API_SERVICE_PORT, -}; -use anyhow::{anyhow, format_err}; -use aptos_config::config::NodeConfig; -use aptos_db::common::{LEDGER_DB_NAME, STATE_MERKLE_DB_NAME}; -use aptos_logger::info; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::types::PeerId; -use aptos_state_sync_driver::metadata_storage::STATE_SYNC_DB_NAME; -use reqwest::Url; -use serde_json::Value; -use std::{ - fmt::{Debug, Formatter}, - process::{Command, Stdio}, - str::FromStr, - thread, - time::{Duration, Instant}, -}; - -const APTOS_DATA_DIR: &str = "/opt/aptos/data"; - -pub struct K8sNode { - pub(crate) name: String, - pub(crate) stateful_set_name: String, - pub(crate) peer_id: PeerId, - pub(crate) index: usize, - pub(crate) service_name: String, - pub(crate) rest_api_port: u32, - pub version: Version, - pub namespace: String, - // whether this node has HAProxy in front of it - pub haproxy_enabled: bool, - // whether we should try using port-forward on the Service to reach this node - pub port_forward_enabled: bool, -} - -impl K8sNode { - fn rest_api_port(&self) -> u32 { - self.rest_api_port - } - - fn service_name(&self) -> String { - self.service_name.clone() - } - - pub(crate) fn rest_client(&self) -> RestClient { - RestClient::new(self.rest_api_endpoint()) - } - - pub fn stateful_set_name(&self) -> &str { - &self.stateful_set_name - } - - fn namespace(&self) -> &str { - &self.namespace - } - - /// Start a port-forward to the node's Service - fn port_forward(&self, port: u32, remote_port: u32) -> Result<()> { - let port_forward_args = [ - "port-forward", - "-n", - self.namespace(), - &format!("svc/{}", self.service_name()), - &format!("{}:{}", port, remote_port), - ]; - // spawn a port-forward child process - let cmd = Command::new(KUBECTL_BIN) - .args(port_forward_args) - .stdout(Stdio::null()) - // .stderr(Stdio::null()) - .spawn(); - match cmd { - Ok(mut child) => { - // sleep a bit and check if port-forward failed for some reason - let timeout = Duration::from_secs(1); - thread::sleep(timeout); - match child.try_wait() { - Ok(Some(status)) => { - info!("Port-forward may have started already: exit {}", status); - Ok(()) - }, - Ok(None) => { - info!( - "Port-forward started for {:?} from {} --> {}", - self, port, remote_port - ); - Ok(()) - }, - Err(err) => Err(anyhow!( - "Port-forward did not work: {:?} error {}", - port_forward_args, - err - )), - } - }, - Err(err) => Err(anyhow!( - "Port-forward did not start: {:?} error {}", - port_forward_args, - err - )), - } - } - - pub fn port_forward_rest_api(&self) -> Result<()> { - let remote_rest_api_port = if self.haproxy_enabled { - REST_API_HAPROXY_SERVICE_PORT - } else { - REST_API_SERVICE_PORT - }; - self.port_forward(self.rest_api_port(), remote_rest_api_port) - } -} - -#[async_trait::async_trait] -impl Node for K8sNode { - fn name(&self) -> &str { - &self.name - } - - fn index(&self) -> usize { - self.index - } - - fn peer_id(&self) -> PeerId { - self.peer_id - } - - async fn start(&mut self) -> Result<()> { - scale_stateful_set_replicas(self.stateful_set_name(), self.namespace(), 1).await?; - // need to port-forward again since the node is coming back - // note that we will get a new port - if self.port_forward_enabled { - self.rest_api_port = get_free_port(); - self.port_forward_rest_api()?; - } - self.wait_until_healthy(Instant::now() + Duration::from_secs(60)) - .await - } - - async fn stop(&mut self) -> Result<()> { - info!("going to stop node {}", self.stateful_set_name()); - scale_stateful_set_replicas(self.stateful_set_name(), self.namespace(), 0).await - } - - fn version(&self) -> Version { - self.version.clone() - } - - fn rest_api_endpoint(&self) -> Url { - let host = if self.port_forward_enabled { - LOCALHOST - } else { - &self.service_name - }; - Url::from_str(&format!("http://{}:{}/v1", host, self.rest_api_port())) - .expect("Invalid URL.") - } - - async fn clear_storage(&mut self) -> Result<()> { - // Remove all storage files - let ledger_db_path = format!("{}/db/{}", APTOS_DATA_DIR, LEDGER_DB_NAME); - let state_db_path = format!("{}/db/{}", APTOS_DATA_DIR, STATE_MERKLE_DB_NAME); - let state_sync_db_path = format!("{}/db/{}", APTOS_DATA_DIR, STATE_SYNC_DB_NAME); - - let delete_storage_paths = [ - "-n", - self.namespace(), - "exec", - &format!("sts/{}", self.stateful_set_name()), - "--", - "rm", - "-rf", - &ledger_db_path, - &state_db_path, - &state_sync_db_path, - ]; - info!("{:?}", delete_storage_paths); - let cleanup_output = Command::new(KUBECTL_BIN) - .stdout(Stdio::inherit()) - .args(delete_storage_paths) - .output() - .expect("failed to clear node storage"); - assert!( - cleanup_output.status.success(), - "{}", - String::from_utf8(cleanup_output.stderr).unwrap() - ); - - // Stop the node to clear buffers - // This step must be done after removing the storage files, since clearing storage involves exec into the (running) node - self.stop().await?; - - Ok(()) - } - - fn config(&self) -> &NodeConfig { - todo!() - } - - // TODO: replace this with prometheus query? - fn counter(&self, counter: &str, port: u64) -> Result { - let response: Value = - reqwest::blocking::get(format!("http://{}:{}/counters", LOCALHOST, port))?.json()?; - if let Value::Number(ref response) = response[counter] { - if let Some(response) = response.as_f64() { - Ok(response) - } else { - Err(format_err!( - "Failed to parse counter({}) as f64: {:?}", - counter, - response - )) - } - } else { - Err(format_err!( - "Counter({}) was not a Value::Number: {:?}", - counter, - response[counter] - )) - } - } - - // TODO: verify this still works - fn expose_metric(&self) -> Result { - let port = get_free_port(); - self.port_forward(port, NODE_METRIC_PORT)?; - - Ok(port as u64) - } - - async fn health_check(&mut self) -> Result<(), HealthCheckError> { - self.rest_client() - .get_ledger_information() - .await - .map(|_| ()) - .map_err(|e| { - HealthCheckError::Failure(format_err!("K8s node health_check failed: {}", e)) - }) - } - - // TODO: verify this still works - fn inspection_service_endpoint(&self) -> Url { - Url::parse(&format!( - "http://{}:{}", - &self.service_name(), - self.rest_api_port() - )) - .unwrap() - } - - async fn get_identity(&mut self) -> Result { - stateful_set::get_identity(self.stateful_set_name(), self.namespace()).await - } - - async fn set_identity(&mut self, k8s_secret_name: String) -> Result<()> { - stateful_set::set_identity( - self.stateful_set_name(), - self.namespace(), - k8s_secret_name.as_str(), - ) - .await - } -} - -impl Validator for K8sNode {} - -impl FullNode for K8sNode {} - -impl Debug for K8sNode { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - let host = if self.port_forward_enabled { - LOCALHOST - } else { - &self.service_name - }; - write!(f, "{} @ {}", self.name, host) - } -} diff --git a/testsuite/forge/src/backend/k8s/prometheus.rs b/testsuite/forge/src/backend/k8s/prometheus.rs deleted file mode 100644 index d0d28b7f729df..0000000000000 --- a/testsuite/forge/src/backend/k8s/prometheus.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{create_k8s_client, K8sApi, ReadWrite, Result}; -use again::RetryPolicy; -use anyhow::{anyhow, bail}; -use aptos_logger::info; -use k8s_openapi::api::core::v1::Secret; -use once_cell::sync::Lazy; -use prometheus_http_query::{ - response::{PromqlResult, Sample}, - Client as PrometheusClient, -}; -use reqwest::{header, Client as HttpClient}; -use std::{collections::BTreeMap, sync::Arc, time::Duration}; - -static PROMETHEUS_RETRY_POLICY: Lazy = Lazy::new(|| { - RetryPolicy::exponential(Duration::from_millis(125)) - .with_max_retries(3) - .with_jitter(true) -}); - -pub async fn get_prometheus_client() -> Result { - // read from the environment - let kube_client = create_k8s_client().await?; - let secrets_api = Arc::new(K8sApi::::from_client( - kube_client, - Some("default".to_string()), - )); - create_prometheus_client_from_environment(secrets_api).await -} - -async fn create_prometheus_client_from_environment( - secrets_api: Arc>, -) -> Result { - let prom_url_env = std::env::var("PROMETHEUS_URL"); - let prom_token_env = std::env::var("PROMETHEUS_TOKEN"); - - let (prom_url, prom_token) = match (prom_url_env.clone(), prom_token_env) { - // if both variables are provided, use them, otherwise try inferring from environment - (Ok(url), Ok(token)) => { - info!("Creating prometheus client from environment variables"); - (url, Some(token)) - }, - _ => { - // try reading a cluster-local secret - match secrets_api.get("prometheus-read-only").await { - Ok(secret) => { - if let Some(data) = secret.data { - let prom_url_k8s_secret = data.get("url"); - let prom_token_k8s_secret = data.get("token"); - match (prom_url_k8s_secret, prom_token_k8s_secret) { - (Some(url), Some(token)) => { - info!("Creating prometheus client from kubernetes secret"); - ( - String::from_utf8(url.0.clone()).unwrap(), - Some(String::from_utf8(token.0.clone()).unwrap()), - ) - }, - _ => { - bail!("Failed to read prometheus-read-only url and token"); - }, - } - } else { - bail!("Failed to read prometheus-read-only secret data"); - } - }, - Err(e) => { - // There's no remote prometheus secret setup. Try reading from a local prometheus backend - info!("Failed to get prometheus-read-only secret: {}", e); - info!("Creating prometheus client from local"); - // Try reading from remote prometheus first, otherwise assume it's local - if let Ok(prom_url_env) = prom_url_env { - (prom_url_env, None) - } else { - ("http://127.0.0.1:9090".to_string(), None) - } - }, - } - }, - }; - - // add auth header if specified - let mut headers = header::HeaderMap::new(); - if let Some(token) = prom_token { - if let Ok(mut auth_value) = - header::HeaderValue::from_str(format!("Bearer {}", token.as_str()).as_str()) - { - auth_value.set_sensitive(true); - headers.insert(header::AUTHORIZATION, auth_value); - } else { - bail!("Invalid prometheus token"); - } - } - - let client = HttpClient::builder().default_headers(headers).build()?; - match PrometheusClient::from(client, &prom_url) { - Ok(c) => Ok(c), - Err(e) => bail!("Failed to create client {}", e), - } -} - -pub fn construct_query_with_extra_labels( - query: &str, - labels_map: &BTreeMap, -) -> String { - // edit the query string to insert swarm metadata - let mut new_query = "".to_string(); - - let mut labels_strs = vec![]; - for (k, v) in labels_map { - labels_strs.push(format!(r#"{}="{}""#, k, v)); - } - let labels = labels_strs.join(","); - - let parts: Vec<&str> = query.split_inclusive('{').collect(); - if parts.len() == 1 { - // no labels in query - format!("{}{{{}}}", query, labels) - } else { - let mut parts_iter = parts.into_iter(); - let prev = parts_iter.next(); - new_query.push_str(prev.unwrap()); - - for part in parts_iter { - if part.starts_with('}') { - // assume no collisions in Forge namespace - new_query.push_str(&labels); - } else { - // assume no collisions in Forge namespace - new_query.push_str(&labels); - new_query.push(','); - } - new_query.push_str(part); - } - new_query - } -} - -pub async fn query_with_metadata( - prom_client: &PrometheusClient, - query: &str, - time: Option, - timeout: Option, - labels_map: &BTreeMap, -) -> Result { - let new_query = construct_query_with_extra_labels(query, labels_map); - let new_query_ref = &new_query; - PROMETHEUS_RETRY_POLICY - .retry(move || prom_client.query(new_query_ref, time, timeout)) - .await - .map_err(|e| anyhow!("Failed to query prometheus for {}: {}", query, e)) -} - -pub async fn query_range_with_metadata( - prom_client: &PrometheusClient, - query: &str, - start_time: i64, - end_time: i64, - internal_secs: f64, - timeout: Option, - labels_map: &BTreeMap, -) -> Result> { - let new_query = construct_query_with_extra_labels(query, labels_map); - let new_query_ref = &new_query; - let r = PROMETHEUS_RETRY_POLICY - .retry(move || { - prom_client.query_range(new_query_ref, start_time, end_time, internal_secs, timeout) - }) - .await - .map_err(|e| { - anyhow!( - "Failed to query prometheus {}. start={}, end={}, query={}", - e, - start_time, - end_time, - new_query - ) - })?; - let range = r.as_range().ok_or_else(|| { - anyhow!( - "Failed to get range from prometheus response. start={}, end={}, query={}", - start_time, - end_time, - new_query - ) - })?; - if range.len() != 1 { - bail!( - "Expected only one range vector from prometheus, recieved {} ({:?}). start={}, end={}, query={}", - range.len(), - range, - start_time, - end_time, - new_query - ); - } - Ok(range - .first() - .unwrap() // safe because we checked length above - .samples() - .to_vec()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::MockSecretApi; - use k8s_openapi::ByteString; - use kube::api::ObjectMeta; - use prometheus_http_query::Error as PrometheusError; - use std::{ - env, - time::{SystemTime, UNIX_EPOCH}, - }; - - #[tokio::test] - async fn test_create_client_secret() { - let secret_api = Arc::new(MockSecretApi::from_secret(Some(Secret { - metadata: ObjectMeta { - name: Some("prometheus-read-only".to_string()), - ..ObjectMeta::default() - }, - data: Some(BTreeMap::from([ - ( - "url".to_string(), - ByteString("http://prometheus.site".to_string().into_bytes()), - ), - ( - "token".to_string(), - ByteString("token".to_string().into_bytes()), - ), - ])), - string_data: None, - type_: None, - immutable: None, - }))); - - create_prometheus_client_from_environment(secret_api) - .await - .unwrap(); - } - - #[tokio::test] - async fn test_create_client_none() { - let secret_api = Arc::new(MockSecretApi::from_secret(None)); - - create_prometheus_client_from_environment(secret_api) - .await - .unwrap(); - } - - #[tokio::test] - async fn test_create_client_env() { - let secret_api = Arc::new(MockSecretApi::from_secret(None)); - - env::set_var("PROMETHEUS_URL", "http://prometheus.site"); - - // this is the worst case and will default to local (and not panic) - create_prometheus_client_from_environment(secret_api.clone()) - .await - .unwrap(); - - env::set_var("PROMETHEUS_TOKEN", "token"); - - // this should use the envs - create_prometheus_client_from_environment(secret_api) - .await - .unwrap(); - - // cleanup - env::remove_var("PROMETHEUS_URL"); - env::remove_var("PROMETHEUS_TOKEN"); - } - - #[tokio::test] - async fn test_query_prometheus() { - let client_result = get_prometheus_client().await; - - // Currently this test tries to connect to the internet... and doesnt - // require success so it is likely to be skipped - // We should come back with some abstractions to make this more testable - let client = if let Ok(client) = client_result { - client - } else { - println!("Skipping test. Failed to create prometheus client"); - return; - }; - - // try a simple instant query - // if it fails to connect to a prometheus instance, skip the test - let query = r#"container_cpu_usage_seconds_total{chain_name=~".*forge.*", pod="aptos-node-0-validator-0", container="validator"}"#; - let response = client.query(query, None, None).await; - match response { - Ok(pres) => { - println!("{:?}", pres); - }, - Err(PrometheusError::Client(e)) => { - println!("Skipping test. Failed to create prometheus client: {}", e); - return; - }, - Err(e) => panic!("Expected PromqlResult: {}", e), - } - - // try a range query - let start = SystemTime::now(); - let since_the_epoch = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - let start_timestamp: i64 = (since_the_epoch - 60) as i64; - let end_timestamp: i64 = since_the_epoch as i64; - let step = 15.0; - - let response = client - .query_range(query, start_timestamp, end_timestamp, step, None) - .await; - match response { - Ok(pres) => println!("{:?}", pres), - _ => panic!("Expected PromqlResult"), - } - } - - #[test] - fn test_create_query() { - let mut labels_map = BTreeMap::new(); - labels_map.insert("a".to_string(), "a".to_string()); - labels_map.insert("some_label".to_string(), "blabla".to_string()); - - // test when no existing labels - let original_query = "aptos_connections"; - let expected_query = r#"aptos_connections{a="a",some_label="blabla"}"#; - let new_query = construct_query_with_extra_labels(original_query, &labels_map); - assert_eq!(expected_query, new_query); - - // test when empty labels - let original_query = "aptos_connections{}"; - let expected_query = r#"aptos_connections{a="a",some_label="blabla"}"#; - let new_query = construct_query_with_extra_labels(original_query, &labels_map); - assert_eq!(expected_query, new_query); - - // test when existing labels - let original_query = r#"aptos_connections{abc="123",def="456"}"#; - let expected_query = r#"aptos_connections{a="a",some_label="blabla",abc="123",def="456"}"#; - let new_query = construct_query_with_extra_labels(original_query, &labels_map); - assert_eq!(expected_query, new_query); - - // test when multiple queries - let original_query = r#"aptos_connections{abc="123",def="456"} - aptos_disconnects{abc="123"} / aptos_count{}"#; - let expected_query = r#"aptos_connections{a="a",some_label="blabla",abc="123",def="456"} - aptos_disconnects{a="a",some_label="blabla",abc="123"} / aptos_count{a="a",some_label="blabla"}"#; - let new_query = construct_query_with_extra_labels(original_query, &labels_map); - assert_eq!(expected_query, new_query); - } -} diff --git a/testsuite/forge/src/backend/k8s/stateful_set.rs b/testsuite/forge/src/backend/k8s/stateful_set.rs deleted file mode 100644 index 46079e451162d..0000000000000 --- a/testsuite/forge/src/backend/k8s/stateful_set.rs +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{create_k8s_client, k8s_wait_nodes_strategy, K8sApi, ReadWrite, Result, KUBECTL_BIN}; -use again::RetryPolicy; -use anyhow::bail; -use aptos_logger::info; -use json_patch::{Patch as JsonPatch, PatchOperation, ReplaceOperation}; -use k8s_openapi::api::{apps::v1::StatefulSet, core::v1::Pod}; -use kube::{ - api::{Api, Patch, PatchParams}, - client::Client as K8sClient, - ResourceExt, -}; -use serde_json::{json, Value}; -use std::{process::Command, sync::Arc, time::Duration}; -use thiserror::Error; - -#[derive(Error, Debug)] -#[error("{0}")] -enum WorkloadScalingError { - RetryableError(String), - FinalError(String), -} - -pub struct KubeImage { - pub name: String, - pub tag: String, -} - -pub fn get_stateful_set_image(stateful_set: &StatefulSet) -> Result { - let s = stateful_set - .spec - .as_ref() - .expect("Failed to get StatefulSet spec") - .template - .spec - .as_ref() - .expect("Failed to get StatefulSet pod spec") - .containers[0] - .image - .as_ref() - .expect("Failed to get StatefulSet image") - .split(':') - .collect::>(); - - Ok(KubeImage { - name: s[0].to_string(), - tag: s[1].to_string(), - }) -} - -/// Waits for a single K8s StatefulSet to be ready -pub async fn wait_stateful_set( - kube_client: &K8sClient, - kube_namespace: &str, - sts_name: &str, - desired_replicas: u64, - retry_policy: RetryPolicy, -) -> Result<()> { - let stateful_set_api = Arc::new(K8sApi::::from_client( - kube_client.clone(), - Some(kube_namespace.to_string()), - )); - let pod_api = Arc::new(K8sApi::::from_client( - kube_client.clone(), - Some(kube_namespace.to_string()), - )); - retry_policy - .retry_if( - move || { - check_stateful_set_status( - stateful_set_api.clone(), - pod_api.clone(), - sts_name, - desired_replicas, - ) - }, - |e: &WorkloadScalingError| matches!(e, WorkloadScalingError::RetryableError(_)), - ) - .await?; - - Ok(()) -} - -/// Checks the status of a single K8s StatefulSet. Also inspects the pods to make sure they are all ready. -async fn check_stateful_set_status( - stateful_set_api: Arc>, - pod_api: Arc>, - sts_name: &str, - desired_replicas: u64, -) -> Result<(), WorkloadScalingError> { - match stateful_set_api.get(sts_name).await { - Ok(s) => { - let sts_name = &s.name(); - // get the StatefulSet status - if let Some(sts_status) = s.status { - let ready_replicas = sts_status.ready_replicas.unwrap_or(0) as u64; - let replicas = sts_status.replicas as u64; - if ready_replicas == replicas && replicas == desired_replicas { - info!( - "StatefulSet {} has scaled to {}", - sts_name, desired_replicas - ); - return Ok(()); - } - info!( - "StatefulSet {} has {}/{} replicas", - sts_name, ready_replicas, desired_replicas - ); - } - let pod_name = format!("{}-0", sts_name); - // Get the StatefulSet's Pod status - if let Some(status) = pod_api - .get(&pod_name) - .await - .map_err(|e| WorkloadScalingError::RetryableError(e.to_string()))? - .status - { - if let Some(ref container_statuses) = status.container_statuses { - if let Some(container_status) = container_statuses.last() { - if let Some(state) = &container_status.state { - if let Some(waiting) = &state.waiting { - if let Some(waiting_reason) = &waiting.reason { - match waiting_reason.as_str() { - "ImagePullBackOff" => { - info!("Pod {} has ImagePullBackOff", &pod_name); - return Err(WorkloadScalingError::FinalError( - "ImagePullBackOff".to_string(), - )); - }, - "CrashLoopBackOff" => { - info!("Pod {} has CrashLoopBackOff", &pod_name); - return Err(WorkloadScalingError::FinalError( - "CrashLoopBackOff".to_string(), - )); - }, - "ErrImagePull" => { - info!("Pod {} has ErrImagePull", &pod_name); - return Err(WorkloadScalingError::FinalError( - "ErrImagePull".to_string(), - )); - }, - _ => { - info!("Waiting for pod {}", &pod_name); - return Err(WorkloadScalingError::RetryableError( - format!("Waiting for pod {}", &pod_name), - )); - }, - } - } - } - } - } - } - if let Some(phase) = status.phase.as_ref() { - info!("Pod {} at phase {}", &pod_name, phase) - } - Err(WorkloadScalingError::RetryableError(format!( - "Retry due to pod {} status {:?}", - &pod_name, status - ))) - } else { - Err(WorkloadScalingError::FinalError(format!( - "Pod {} status not found", - &pod_name - ))) - } - }, - Err(e) => { - info!("Failed to get StatefulSet: {}", e); - Err(WorkloadScalingError::RetryableError(format!( - "Failed to get StatefulSet: {}", - e - ))) - }, - } -} - -/// Given the name of a node's StatefulSet, sets the node's image tag. Assumes that the StatefulSet has only one container -/// Note that this function will not wait for the StatefulSet to be ready. -pub async fn set_stateful_set_image_tag( - stateful_set_name: String, - container_name: String, - image_tag: String, - kube_namespace: String, -) -> Result<()> { - let kube_client: K8sClient = create_k8s_client().await?; - let sts_api: Api = Api::namespaced(kube_client.clone(), &kube_namespace); - let sts = sts_api.get(&stateful_set_name).await?; - let image_repo = get_stateful_set_image(&sts)?.name; - - // replace the image tag - let new_image = format!("{}:{}", &image_repo, &image_tag); - - // set the image using kubectl - // patching the node spec may not work - Command::new(KUBECTL_BIN) - .args([ - "-n", - &kube_namespace, - "set", - "image", - &format!("statefulset/{}", &stateful_set_name), - &format!("{}={}", &container_name, &new_image), - ]) - .status() - .expect("Failed to set image for StatefulSet"); - - Ok(()) -} - -/// Scales the given StatefulSet to the given number of replicas -pub async fn scale_stateful_set_replicas( - sts_name: &str, - kube_namespace: &str, - replica_num: u64, -) -> Result<()> { - let kube_client = create_k8s_client().await?; - let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); - let pp = PatchParams::apply("forge").force(); - let patch = serde_json::json!({ - "apiVersion": "apps/v1", - "kind": "StatefulSet", - "metadata": { - "name": sts_name, - }, - "spec": { - "replicas": replica_num, - } - }); - let patch = Patch::Apply(&patch); - stateful_set_api.patch(sts_name, &pp, &patch).await?; - // retry for ~5 min at a fixed interval - let retry_policy = RetryPolicy::fixed(Duration::from_secs(10)).with_max_retries(6 * 5); - wait_stateful_set( - &kube_client, - kube_namespace, - sts_name, - replica_num, - retry_policy, - ) - .await?; - - Ok(()) -} - -pub async fn set_identity( - sts_name: &str, - kube_namespace: &str, - k8s_secret_name: &str, -) -> Result<()> { - let kube_client = create_k8s_client().await?; - let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); - let patch_op = PatchOperation::Replace(ReplaceOperation { - // The json path below should match `terraform/helm/aptos-node/templates/validator.yaml`. - path: "/spec/template/spec/volumes/1/secret/secretName".to_string(), - value: json!(k8s_secret_name), - }); - let patch: Patch = Patch::Json(JsonPatch(vec![patch_op])); - let pp = PatchParams::apply("forge"); - stateful_set_api.patch(sts_name, &pp, &patch).await?; - Ok(()) -} - -pub async fn get_identity(sts_name: &str, kube_namespace: &str) -> Result { - let kube_client = create_k8s_client().await?; - let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); - let sts = stateful_set_api.get(sts_name).await?; - // The json path below should match `terraform/helm/aptos-node/templates/validator.yaml`. - let secret_name = sts.spec.unwrap().template.spec.unwrap().volumes.unwrap()[1] - .secret - .clone() - .unwrap() - .secret_name - .unwrap(); - Ok(secret_name) -} - -pub async fn check_for_container_restart( - kube_client: &K8sClient, - kube_namespace: &str, - sts_name: &str, -) -> Result<()> { - aptos_retrier::retry_async(k8s_wait_nodes_strategy(), || { - let pod_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); - Box::pin(async move { - // Get the StatefulSet's Pod status - let pod_name = format!("{}-0", sts_name); - if let Some(status) = pod_api.get_status(&pod_name).await?.status { - if let Some(container_statuses) = status.container_statuses { - for container_status in container_statuses { - if container_status.restart_count > 0 { - bail!( - "Container {} in pod {} restarted {} times ", - container_status.name, - &pod_name, - container_status.restart_count - ); - } - } - return Ok(()); - } - // In case of no restarts, k8 apis returns no container statuses - Ok(()) - } else { - bail!("Can't query the pod status for {}", sts_name) - } - }) - }) - .await -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{MockPodApi, MockStatefulSetApi}; - use k8s_openapi::{ - api::{ - apps::v1::{StatefulSet, StatefulSetSpec, StatefulSetStatus}, - core::v1::{ContainerState, ContainerStateWaiting, ContainerStatus, PodStatus}, - }, - apimachinery::pkg::apis::meta::v1::ObjectMeta, - }; - - #[tokio::test] - async fn test_check_stateful_set_status() { - // mock a StatefulSet with 0/1 replicas - // this should then mean we check the underlying pod to see what's up - let stateful_set_api = Arc::new(MockStatefulSetApi::from_stateful_set(StatefulSet { - metadata: ObjectMeta { - name: Some("test-stateful-set".to_string()), - ..ObjectMeta::default() - }, - spec: Some(StatefulSetSpec { - replicas: Some(1), - ..StatefulSetSpec::default() - }), - status: Some(StatefulSetStatus { - replicas: 1, - ready_replicas: Some(0), - ..StatefulSetStatus::default() - }), - })); - - // we should retry if the pod status is not explicitly bad - let pod_default_api = Arc::new(MockPodApi::from_pod(Pod { - status: Some(PodStatus::default()), - ..Pod::default() - })); - let ret = check_stateful_set_status( - stateful_set_api.clone(), - pod_default_api.clone(), - "test-stateful-set", - 1, - ) - .await; - assert!(matches!( - ret.err(), - Some(WorkloadScalingError::RetryableError(_)) - )); - - // the pod explicitly has a bad status, so we should fail fast - let pod_default_api = Arc::new(MockPodApi::from_pod(Pod { - metadata: ObjectMeta { - name: Some("test-stateful-set-0".to_string()), - ..ObjectMeta::default() - }, - status: Some(PodStatus { - container_statuses: Some(vec![ContainerStatus { - name: "test-container".to_string(), - restart_count: 0, - state: Some(ContainerState { - waiting: Some(ContainerStateWaiting { - reason: Some("CrashLoopBackOff".to_string()), - ..ContainerStateWaiting::default() - }), - ..ContainerState::default() - }), - ..ContainerStatus::default() - }]), - ..PodStatus::default() - }), - ..Pod::default() - })); - let ret = check_stateful_set_status( - stateful_set_api.clone(), - pod_default_api.clone(), - "test-stateful-set", - 1, - ) - .await; - assert!(matches!( - ret.err(), - Some(WorkloadScalingError::FinalError(_)) - )); - } -} diff --git a/testsuite/forge/src/backend/k8s/swarm.rs b/testsuite/forge/src/backend/k8s/swarm.rs deleted file mode 100644 index d4cc6512f2a53..0000000000000 --- a/testsuite/forge/src/backend/k8s/swarm.rs +++ /dev/null @@ -1,889 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - chaos_schema::{ - Chaos, ChaosConditionType, ChaosStatus, ConditionStatus, NetworkChaos, StressChaos, - }, - check_for_container_restart, create_k8s_client, delete_all_chaos, get_default_pfn_node_config, - get_free_port, get_stateful_set_image, install_public_fullnode, - node::K8sNode, - prometheus::{self, query_range_with_metadata, query_with_metadata}, - query_sequence_number, set_stateful_set_image_tag, uninstall_testnet_resources, ChainInfo, - FullNode, K8sApi, Node, Result, Swarm, SwarmChaos, Validator, Version, HAPROXY_SERVICE_SUFFIX, - REST_API_HAPROXY_SERVICE_PORT, REST_API_SERVICE_PORT, -}; -use ::aptos_logger::*; -use anyhow::{anyhow, bail, format_err}; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_retrier::fixed_retry_strategy; -use aptos_sdk::{ - crypto::ed25519::Ed25519PrivateKey, - move_types::account_address::AccountAddress, - types::{chain_id::ChainId, AccountKey, LocalAccount, PeerId}, -}; -use k8s_openapi::api::{ - apps::v1::StatefulSet, - core::v1::{ConfigMap, PersistentVolumeClaim, Service}, -}; -use kube::{ - api::{Api, ListParams}, - client::Client as K8sClient, -}; -use prometheus_http_query::{ - response::{PromqlResult, Sample}, - Client as PrometheusClient, -}; -use regex::Regex; -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - convert::TryFrom, - env, str, - sync::Arc, -}; -use tokio::{runtime::Runtime, time::Duration}; - -pub struct K8sSwarm { - validators: HashMap, - fullnodes: HashMap, - root_account: LocalAccount, - kube_client: K8sClient, - versions: Arc>, - pub chain_id: ChainId, - pub kube_namespace: String, - keep: bool, - chaoses: HashSet, - prom_client: Option, - era: Option, - use_port_forward: bool, - chaos_experiment_ops: Box, -} - -impl K8sSwarm { - pub async fn new<'b>( - root_key: &[u8], - image_tag: &str, - upgrade_image_tag: &str, - kube_namespace: &str, - validators: HashMap, - fullnodes: HashMap, - keep: bool, - era: Option, - use_port_forward: bool, - ) -> Result { - let kube_client = create_k8s_client().await?; - - let client = validators.values().next().unwrap().rest_client(); - let key = load_root_key(root_key); - let account_key = AccountKey::from_private_key(key); - let address = aptos_sdk::types::account_config::aptos_test_root_address(); - let sequence_number = query_sequence_number(&client, address).await.map_err(|e| { - format_err!( - "query_sequence_number on {:?} for dd account failed: {}", - client, - e - ) - })?; - let root_account = LocalAccount::new(address, account_key, sequence_number); - - let mut versions = HashMap::new(); - let cur_version = Version::new(0, image_tag.to_string()); - let upgrade_version = Version::new(1, upgrade_image_tag.to_string()); - versions.insert(upgrade_version, upgrade_image_tag.to_string()); - versions.insert(cur_version, image_tag.to_string()); - - let prom_client = match prometheus::get_prometheus_client().await { - Ok(p) => Some(p), - Err(e) => { - // Fail fast if prometheus is not configured. A test is meaningless if we do not have observability - bail!("Could not build prometheus client: {}", e); - }, - }; - - let swarm = K8sSwarm { - validators, - fullnodes, - root_account, - kube_client: kube_client.clone(), - chain_id: ChainId::new(4), - versions: Arc::new(versions), - kube_namespace: kube_namespace.to_string(), - keep, - chaoses: HashSet::new(), - prom_client, - era, - use_port_forward, - chaos_experiment_ops: Box::new(RealChaosExperimentOps { - kube_client: kube_client.clone(), - kube_namespace: kube_namespace.to_string(), - }), - }; - - // test hitting the configured prometheus endpoint - let query = "container_memory_usage_bytes{pod=\"aptos-node-0-validator-0\"}"; - let r = swarm.query_metrics(query, None, None).await?; - let ivs = r.as_instant().unwrap(); - for iv in ivs { - info!("container_memory_usage_bytes: {}", iv.sample().value()); - } - - Ok(swarm) - } - - fn get_rest_api_url(&self, idx: usize) -> String { - self.validators - .values() - .nth(idx) - .unwrap() - .rest_api_endpoint() - .to_string() - } - - fn get_inspection_service_url(&self, idx: usize) -> String { - self.validators - .values() - .nth(idx) - .unwrap() - .inspection_service_endpoint() - .to_string() - } - - #[allow(dead_code)] - fn get_kube_client(&self) -> K8sClient { - self.kube_client.clone() - } - - /// Installs a PFN with the given version and node config - async fn install_public_fullnode_resources<'a>( - &mut self, - version: &'a Version, - node_config: &'a OverrideNodeConfig, - ) -> Result<(PeerId, K8sNode)> { - // create APIs - let stateful_set_api: Arc> = Arc::new(K8sApi::::from_client( - self.get_kube_client(), - Some(self.kube_namespace.clone()), - )); - let configmap_api: Arc> = Arc::new(K8sApi::::from_client( - self.get_kube_client(), - Some(self.kube_namespace.clone()), - )); - let persistent_volume_claim_api: Arc> = - Arc::new(K8sApi::::from_client( - self.get_kube_client(), - Some(self.kube_namespace.clone()), - )); - let service_api: Arc> = Arc::new(K8sApi::::from_client( - self.get_kube_client(), - Some(self.kube_namespace.clone()), - )); - let (peer_id, mut k8snode) = install_public_fullnode( - stateful_set_api, - configmap_api, - persistent_volume_claim_api, - service_api, - version, - node_config, - self.era - .as_ref() - .expect("Installing PFN requires acquiring the current chain era") - .clone(), - self.kube_namespace.clone(), - self.use_port_forward, - self.fullnodes.len(), - ) - .await?; - k8snode.start().await?; // actually start the node. if port-forward is enabled, this is when it gets its ephemeral port - Ok((peer_id, k8snode)) - } -} - -#[async_trait::async_trait] -impl Swarm for K8sSwarm { - async fn health_check(&mut self) -> Result<()> { - let nodes = self.validators.values().collect(); - let unhealthy_nodes = nodes_healthcheck(nodes).await.unwrap(); - if !unhealthy_nodes.is_empty() { - bail!("Unhealthy nodes: {:?}", unhealthy_nodes) - } - - Ok(()) - } - - fn validators<'a>(&'a self) -> Box + 'a> { - let mut validators: Vec<_> = self - .validators - .values() - .map(|v| v as &'a dyn Validator) - .collect(); - validators.sort_by_key(|v| v.index()); - Box::new(validators.into_iter()) - } - - fn validators_mut<'a>(&'a mut self) -> Box + 'a> { - let mut validators: Vec<_> = self - .validators - .values_mut() - .map(|v| v as &'a mut dyn Validator) - .collect(); - validators.sort_by_key(|v| v.index()); - Box::new(validators.into_iter()) - } - - fn validator(&self, id: PeerId) -> Option<&dyn Validator> { - self.validators.get(&id).map(|v| v as &dyn Validator) - } - - fn validator_mut(&mut self, id: PeerId) -> Option<&mut dyn Validator> { - self.validators - .get_mut(&id) - .map(|v| v as &mut dyn Validator) - } - - /// TODO: this should really be a method on Node rather than Swarm - async fn upgrade_validator(&mut self, id: PeerId, version: &Version) -> Result<()> { - let validator = self - .validators - .get_mut(&id) - .ok_or_else(|| anyhow!("Invalid id: {}", id))?; - let version = self - .versions - .get(version) - .cloned() - .ok_or_else(|| anyhow!("Invalid version: {:?}", version))?; - // stop the validator first so there is no race on the upgrade - validator.stop().await?; - // set the image tag of the StatefulSet spec while there are 0 replicas - set_stateful_set_image_tag( - validator.stateful_set_name().to_string(), - // the container name for the validator in its StatefulSet is "validator" - "validator".to_string(), - // extract the image tag from the "version" - version.to_string(), - self.kube_namespace.clone(), - ) - .await?; - - // To ensure that the validator is fully spun back up - // If port-forward is enabled, this ensures that the pod is back before attempting a port-forward - validator.start().await?; - Ok(()) - } - - fn full_nodes<'a>(&'a self) -> Box + 'a> { - let mut full_nodes: Vec<_> = self - .fullnodes - .values() - .map(|n| n as &'a dyn FullNode) - .collect(); - full_nodes.sort_by_key(|n| n.index()); - Box::new(full_nodes.into_iter()) - } - - fn full_nodes_mut<'a>(&'a mut self) -> Box + 'a> { - let mut full_nodes: Vec<_> = self - .fullnodes - .values_mut() - .map(|n| n as &'a mut dyn FullNode) - .collect(); - full_nodes.sort_by_key(|n| n.index()); - Box::new(full_nodes.into_iter()) - } - - fn full_node(&self, id: PeerId) -> Option<&dyn FullNode> { - self.fullnodes.get(&id).map(|v| v as &dyn FullNode) - } - - fn full_node_mut(&mut self, id: PeerId) -> Option<&mut dyn FullNode> { - self.fullnodes.get_mut(&id).map(|v| v as &mut dyn FullNode) - } - - fn add_validator(&mut self, _version: &Version, _template: NodeConfig) -> Result { - todo!() - } - - fn remove_validator(&mut self, _id: PeerId) -> Result<()> { - todo!() - } - - fn add_validator_full_node( - &mut self, - _version: &Version, - _config: OverrideNodeConfig, - _id: PeerId, - ) -> Result { - todo!() - } - - async fn add_full_node( - &mut self, - version: &Version, - config: OverrideNodeConfig, - ) -> Result { - self.install_public_fullnode_resources(version, &config) - .await - .map(|(peer_id, node)| { - self.fullnodes.insert(peer_id, node); - peer_id - }) - } - - fn remove_full_node(&mut self, _id: PeerId) -> Result<()> { - todo!() - } - - fn versions<'a>(&'a self) -> Box + 'a> { - Box::new(self.versions.keys().cloned()) - } - - fn chain_info(&mut self) -> ChainInfo<'_> { - let rest_api_url = self.get_rest_api_url(0); - let inspection_service_url = self.get_inspection_service_url(0); - ChainInfo::new( - &mut self.root_account, - rest_api_url, - inspection_service_url, - self.chain_id, - ) - } - - // returns a kubectl logs command to retrieve the logs manually - // and instructions to check the actual live logs location from fgi - fn logs_location(&mut self) -> String { - "See fgi output for more information.".to_string() - } - - async fn inject_chaos(&mut self, chaos: SwarmChaos) -> Result<()> { - self.inject_swarm_chaos(&chaos)?; - self.chaoses.insert(chaos); - self.chaos_experiment_ops - .ensure_chaos_experiments_active() - .await?; - - Ok(()) - } - - async fn remove_chaos(&mut self, chaos: SwarmChaos) -> Result<()> { - self.chaos_experiment_ops - .ensure_chaos_experiments_active() - .await?; - - if self.chaoses.remove(&chaos) { - self.remove_swarm_chaos(&chaos)?; - } else { - bail!("Chaos {:?} not found", chaos); - } - Ok(()) - } - - async fn remove_all_chaos(&mut self) -> Result<()> { - self.chaos_experiment_ops - .ensure_chaos_experiments_active() - .await?; - - // try removing all existing chaoses - for chaos in self.chaoses.clone() { - self.remove_swarm_chaos(&chaos)?; - } - // force remove all others - delete_all_chaos(&self.kube_namespace)?; - - self.chaoses.clear(); - Ok(()) - } - - async fn ensure_no_validator_restart(&self) -> Result<()> { - for validator in &self.validators { - check_for_container_restart( - &self.kube_client, - &self.kube_namespace.clone(), - validator.1.stateful_set_name(), - ) - .await?; - } - info!("Found no validator restarts"); - Ok(()) - } - - async fn ensure_no_fullnode_restart(&self) -> Result<()> { - for fullnode in &self.fullnodes { - check_for_container_restart( - &self.kube_client, - &self.kube_namespace.clone(), - fullnode.1.stateful_set_name(), - ) - .await?; - } - info!("Found no fullnode restarts"); - Ok(()) - } - - async fn query_metrics( - &self, - query: &str, - time: Option, - timeout: Option, - ) -> Result { - if let Some(c) = &self.prom_client { - let mut labels_map = BTreeMap::new(); - labels_map.insert("namespace".to_string(), self.kube_namespace.clone()); - return query_with_metadata(c, query, time, timeout, &labels_map).await; - } - bail!("No prom client"); - } - - async fn query_range_metrics( - &self, - query: &str, - start_time: i64, - end_time: i64, - timeout: Option, - ) -> Result> { - if let Some(c) = &self.prom_client { - let mut labels_map = BTreeMap::new(); - labels_map.insert("namespace".to_string(), self.kube_namespace.clone()); - return query_range_with_metadata( - c, - query, - start_time, - end_time, - 30.0, - timeout, - &labels_map, - ) - .await; - } - bail!("No prom client"); - } - - fn chain_info_for_node(&mut self, idx: usize) -> ChainInfo<'_> { - let rest_api_url = self.get_rest_api_url(idx); - let inspection_service_url = self.get_inspection_service_url(idx); - ChainInfo::new( - &mut self.root_account, - rest_api_url, - inspection_service_url, - self.chain_id, - ) - } - - fn get_default_pfn_node_config(&self) -> NodeConfig { - get_default_pfn_node_config() - } -} - -/// Amount of time to wait for genesis to complete -pub fn k8s_wait_genesis_strategy() -> impl Iterator { - // retry every 10 seconds for 10 minutes - fixed_retry_strategy(10 * 1000, 60) -} - -/// Amount of time to wait for nodes to spin up, from provisioning to API ready -pub fn k8s_wait_nodes_strategy() -> impl Iterator { - // retry every 10 seconds for 20 minutes - fixed_retry_strategy(10 * 1000, 120) -} - -async fn list_stateful_sets(client: K8sClient, kube_namespace: &str) -> Result> { - let stateful_set_api: Api = Api::namespaced(client, kube_namespace); - let lp = ListParams::default(); - let stateful_sets = stateful_set_api.list(&lp).await?.items; - Ok(stateful_sets) -} - -fn stateful_set_name_matches(sts: &StatefulSet, suffix: &str) -> bool { - if let Some(s) = sts.metadata.name.as_ref() { - s.contains(suffix) - } else { - false - } -} - -fn parse_service_name_from_stateful_set_name( - stateful_set_name: &str, - enable_haproxy: bool, -) -> String { - let re = Regex::new(r"(aptos-node-\d+)-(validator|fullnode)").unwrap(); - let cap = re.captures(stateful_set_name).unwrap(); - let service_base_name = format!("{}-{}", &cap[1], &cap[2]); - if enable_haproxy { - format!("{}-{}", &service_base_name, HAPROXY_SERVICE_SUFFIX) - } else { - service_base_name - } -} - -fn get_k8s_node_from_stateful_set( - sts: &StatefulSet, - enable_haproxy: bool, - use_port_forward: bool, -) -> K8sNode { - let stateful_set_name = sts.metadata.name.as_ref().unwrap(); - // If HAProxy is enabled, use its Service name. Otherwise the Service name matches the StatefulSet name - let mut service_name = - parse_service_name_from_stateful_set_name(stateful_set_name, enable_haproxy); - - // the full service name includes the namespace - let namespace = sts.metadata.namespace.as_ref().unwrap(); - - // if we're not using port-forward and expecting to hit the service directly, we should use the full service name - // since the test runner may be in a separate namespace - if !use_port_forward { - service_name = format!("{}.{}.svc", &service_name, &namespace); - } - - // Append the cluster name if its a multi-cluster deployment - let service_name = if let Some(target_cluster_name) = sts - .metadata - .labels - .as_ref() - .and_then(|labels| labels.get("multicluster/targetcluster")) - { - format!("{}.{}", &service_name, &target_cluster_name) - } else { - service_name - }; - - // If HAProxy is enabled, use the port on its Service. Otherwise use the port on the validator Service - let mut rest_api_port = if enable_haproxy { - REST_API_HAPROXY_SERVICE_PORT - } else { - REST_API_SERVICE_PORT - }; - - if use_port_forward { - rest_api_port = get_free_port(); - } - let index = parse_node_index(stateful_set_name).expect("error to parse node index"); - let node_type = parse_node_type(stateful_set_name); - - // Extract the image tag from the StatefulSet spec - let image_tag = get_stateful_set_image(sts) - .expect("Failed to get StatefulSet image") - .tag; - - K8sNode { - name: format!("{}-{}", &node_type, index), - stateful_set_name: stateful_set_name.clone(), - // TODO: fetch this from running node - peer_id: PeerId::random(), - index, - service_name, - rest_api_port, - version: Version::new(0, image_tag), - namespace: namespace.to_string(), - haproxy_enabled: enable_haproxy, - port_forward_enabled: use_port_forward, - } -} - -pub(crate) async fn get_validators( - client: K8sClient, - kube_namespace: &str, - use_port_forward: bool, - enable_haproxy: bool, -) -> Result> { - let stateful_sets = list_stateful_sets(client, kube_namespace).await?; - let validators = stateful_sets - .into_iter() - .filter(|sts| stateful_set_name_matches(sts, "validator")) - .map(|sts| { - let node = get_k8s_node_from_stateful_set(&sts, enable_haproxy, use_port_forward); - (node.peer_id(), node) - }) - .collect::>(); - - Ok(validators) -} - -pub(crate) async fn get_fullnodes( - client: K8sClient, - kube_namespace: &str, - use_port_forward: bool, - enable_haproxy: bool, -) -> Result> { - let stateful_sets = list_stateful_sets(client, kube_namespace).await?; - let fullnodes = stateful_sets - .into_iter() - .filter(|sts| stateful_set_name_matches(sts, "fullnode")) - .map(|sts| { - let node = get_k8s_node_from_stateful_set(&sts, enable_haproxy, use_port_forward); - (node.peer_id(), node) - }) - .collect::>(); - - Ok(fullnodes) -} - -/// Given a string like the StatefulSet name or Service name, parse the node type, -/// whether it's a validator or fullnode -fn parse_node_type(s: &str) -> String { - let re = Regex::new(r"(validator|fullnode)").unwrap(); - let cap = re.captures(s).unwrap(); - cap[1].to_string() -} - -// gets the node index based on its associated statefulset name -// e.g. aptos-node--validator -// e.g. aptos-node--fullnode-e -fn parse_node_index(s: &str) -> Result { - // first get rid of the prefixes - let v = s.split("aptos-node-").collect::>(); - if v.len() < 2 { - return Err(format_err!("Failed to parse {:?} node id format", s)); - } - // then get rid of the node type suffix - let v = v[1].split('-').collect::>(); - let idx: usize = v[0].parse().unwrap(); - Ok(idx) -} - -fn load_root_key(root_key_bytes: &[u8]) -> Ed25519PrivateKey { - Ed25519PrivateKey::try_from(root_key_bytes).unwrap() -} - -pub async fn nodes_healthcheck(nodes: Vec<&K8sNode>) -> Result> { - let mut unhealthy_nodes = vec![]; - - // TODO(rustielin): do all nodes healthchecks in parallel - for node in nodes { - // perform healthcheck with retry, returning unhealthy - let node_name = node.name().to_string(); - let check = aptos_retrier::retry_async(k8s_wait_nodes_strategy(), || { - Box::pin(async move { - match node.rest_client().get_ledger_information().await { - Ok(res) => { - let version = res.inner().version; - // ensure a threshold liveness for each node - // we want to guarantee node is making progress without spinning too long - if version > 100 { - info!("Node {} healthy @ version {} > 100", node.name(), version); - return Ok(()); - } - info!("Node {} @ version {}", node.name(), version); - bail!( - "Node {} unhealthy: REST API returned version 0", - node.name() - ); - }, - Err(err) => { - let err = anyhow::Error::from(err); - info!("Node {} unhealthy: {}", node.name(), &err); - Err(err) - }, - } - }) - }) - .await; - if check.is_err() { - unhealthy_nodes.push(node_name); - } - } - if !unhealthy_nodes.is_empty() { - debug!("Unhealthy validators after cleanup: {:?}", unhealthy_nodes); - } - - Ok(unhealthy_nodes) -} - -impl Drop for K8sSwarm { - fn drop(&mut self) { - let runtime = Runtime::new().unwrap(); - if !self.keep { - runtime - .block_on(uninstall_testnet_resources(self.kube_namespace.clone())) - .unwrap(); - } else { - println!("Keeping kube_namespace {}", self.kube_namespace); - } - } -} - -#[async_trait::async_trait] -trait ChaosExperimentOps { - async fn list_network_chaos(&self) -> Result>; - async fn list_stress_chaos(&self) -> Result>; - - async fn ensure_chaos_experiments_active(&self) -> Result<()> { - let timeout_duration = Duration::from_secs(300); // 5 minutes - let polling_interval = Duration::from_secs(5); - - tokio::time::timeout(timeout_duration, async { - loop { - match self.are_chaos_experiments_active().await { - Ok(true) => { - info!("Chaos experiments are active"); - return Ok(()); - }, - Ok(false) => { - info!("Chaos experiments are not active, retrying..."); - }, - Err(e) => { - warn!( - "Error while checking chaos experiments status: {}. Retrying...", - e - ); - }, - } - tokio::time::sleep(polling_interval).await; - } - }) - .await - .map_err(|e| { - anyhow!( - "Timed out waiting for chaos experiments to be active: {}", - e - ) - })? - } - - /// Checks if all chaos experiments are active - async fn are_chaos_experiments_active(&self) -> Result { - let (network_chaoses, stress_chaoses) = - tokio::join!(self.list_network_chaos(), self.list_stress_chaos()); - - let chaoses: Vec = network_chaoses? - .into_iter() - .map(Chaos::Network) - .chain(stress_chaoses?.into_iter().map(Chaos::Stress)) - .collect(); - - Ok(!chaoses.is_empty() - && chaoses.iter().all(|chaos| match chaos { - Chaos::Network(network_chaos) => check_all_injected(&network_chaos.status), - Chaos::Stress(stress_chaos) => check_all_injected(&stress_chaos.status), - })) - } -} - -fn check_all_injected(status: &Option) -> bool { - status - .as_ref() - .and_then(|status| status.conditions.as_ref()) - .map_or(false, |conditions| { - conditions.iter().any(|c| { - c.r#type == ChaosConditionType::AllInjected && c.status == ConditionStatus::True - }) - }) -} - -struct MockChaosExperimentOps { - network_chaos: Vec, - stress_chaos: Vec, -} - -#[async_trait::async_trait] -impl ChaosExperimentOps for MockChaosExperimentOps { - async fn list_network_chaos(&self) -> Result> { - Ok(self.network_chaos.clone()) - } - - async fn list_stress_chaos(&self) -> Result> { - Ok(self.stress_chaos.clone()) - } -} - -struct RealChaosExperimentOps { - kube_client: K8sClient, - kube_namespace: String, -} - -#[async_trait::async_trait] -impl ChaosExperimentOps for RealChaosExperimentOps { - async fn list_network_chaos(&self) -> Result> { - let network_chaos_api: Api = - Api::namespaced(self.kube_client.clone(), &self.kube_namespace); - let lp = ListParams::default(); - let network_chaoses = network_chaos_api.list(&lp).await?.items; - Ok(network_chaoses) - } - - async fn list_stress_chaos(&self) -> Result> { - let stress_chaos_api: Api = - Api::namespaced(self.kube_client.clone(), &self.kube_namespace); - let lp = ListParams::default(); - let stress_chaoses = stress_chaos_api.list(&lp).await?.items; - Ok(stress_chaoses) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::chaos_schema::ChaosCondition; - - #[test] - fn test_parse_service_name_from_stateful_set_name() { - let validator_sts_name = "aptos-node-19-validator"; - let validator_service_name = - parse_service_name_from_stateful_set_name(validator_sts_name, false); - assert_eq!("aptos-node-19-validator", &validator_service_name); - // with haproxy - let validator_service_name = - parse_service_name_from_stateful_set_name(validator_sts_name, true); - assert_eq!("aptos-node-19-validator-lb", &validator_service_name); - - let fullnode_sts_name = "aptos-node-0-fullnode-eforge195"; - let fullnode_service_name = - parse_service_name_from_stateful_set_name(fullnode_sts_name, false); - assert_eq!("aptos-node-0-fullnode", &fullnode_service_name); - // with haproxy - let fullnode_service_name = - parse_service_name_from_stateful_set_name(fullnode_sts_name, true); - assert_eq!("aptos-node-0-fullnode-lb", &fullnode_service_name); - } - - async fn create_chaos_experiments( - network_status: ConditionStatus, - stress_status: ConditionStatus, - ) -> (Vec, Vec) { - let network_chaos = NetworkChaos { - status: Some(ChaosStatus { - conditions: Some(vec![ChaosCondition { - r#type: ChaosConditionType::AllInjected, - status: network_status, - }]), - }), - ..NetworkChaos::new("test", Default::default()) - }; - let stress_chaos = StressChaos { - status: Some(ChaosStatus { - conditions: Some(vec![ChaosCondition { - r#type: ChaosConditionType::AllInjected, - status: stress_status, - }]), - }), - ..StressChaos::new("test", Default::default()) - }; - (vec![network_chaos], vec![stress_chaos]) - } - - #[tokio::test] - async fn test_chaos_experiments_active() { - // No experiments active - let chaos_ops = MockChaosExperimentOps { - network_chaos: vec![], - stress_chaos: vec![], - }; - assert!(!chaos_ops.are_chaos_experiments_active().await.unwrap()); - - // Only network chaos active - let (network_chaos, stress_chaos) = - create_chaos_experiments(ConditionStatus::True, ConditionStatus::False).await; - let chaos_ops = MockChaosExperimentOps { - network_chaos, - stress_chaos, - }; - assert!(!chaos_ops.are_chaos_experiments_active().await.unwrap()); - - // Both network and stress chaos active - let (network_chaos, stress_chaos) = - create_chaos_experiments(ConditionStatus::True, ConditionStatus::True).await; - let chaos_ops = MockChaosExperimentOps { - network_chaos, - stress_chaos, - }; - assert!(chaos_ops.are_chaos_experiments_active().await.unwrap()); - } -} diff --git a/testsuite/forge/src/backend/local/cargo.rs b/testsuite/forge/src/backend/local/cargo.rs deleted file mode 100644 index 93af06ac2d8c7..0000000000000 --- a/testsuite/forge/src/backend/local/cargo.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::Result; -use anyhow::{bail, Context}; -use aptos_logger::info; -use serde::Deserialize; -use std::{ - env, fs, - io::{self, Write}, - path::{Path, PathBuf}, - process::Command, -}; -use tempfile::NamedTempFile; - -#[derive(Deserialize)] -pub struct Metadata { - pub target_directory: PathBuf, - pub workspace_root: PathBuf, -} - -/// at _forge_ compile time, decide what kind of build we will use for `aptos-node` -pub fn use_release() -> bool { - option_env!("LOCAL_SWARM_NODE_RELEASE").is_some() -} - -/// at _forge_ compile time, decide to build `aptos-node` only for consensus perf tests -pub fn build_consensus_only_node() -> bool { - option_env!("CONSENSUS_ONLY_PERF_TEST").is_some() -} - -/// at _forge_ compile time, decide to build `aptos-node` with extra network perf tests -pub fn build_network_perf_test() -> bool { - option_env!("NETWORK_PERF_TEST").is_some() -} - -/// at forge _run_ time, compile `aptos-node` without indexer -pub fn build_aptos_node_without_indexer() -> bool { - std::env::var("FORGE_BUILD_WITHOUT_INDEXER").is_ok() -} - -pub fn metadata() -> Result { - let output = Command::new("cargo") - .arg("metadata") - .arg("--no-deps") - .arg("--format-version=1") - .output() - .context("Failed to query cargo metadata")?; - - serde_json::from_slice(&output.stdout).map_err(Into::into) -} - -/// Get the aptos node binary from the current working directory -pub fn get_aptos_node_binary_from_worktree() -> Result<(String, PathBuf)> { - let metadata = metadata()?; - let mut revision = git_rev_parse(&metadata, "HEAD")?; - if git_is_worktree_dirty()? { - revision.push_str("-dirty"); - } - - let bin_path = cargo_build_aptos_node(&metadata.workspace_root, &metadata.target_directory)?; - - Ok((revision, bin_path)) -} - -/// This function will attempt to build the aptos-node binary at an arbitrary revision. -/// Using the `target/forge` as a working directory it will do the following: -/// 1. Look for a binary named `aptos-node--`, if it already exists return it -/// 2. If the binary doesn't exist check out the revision to `target/forge/revision` by doing -/// `git archive --format=tar | tar x` -/// 3. Using the `target/forge/target` directory as a cargo artifact directory, build the -/// binary and then move it to `target/forge/aptos-node--` -pub fn get_aptos_node_binary_at_revision(revision: &str) -> Result<(String, PathBuf)> { - let metadata = metadata()?; - let forge_directory = metadata.target_directory.join("forge"); - let revision = git_rev_parse(&metadata, format!("{}^{{commit}}", revision))?; - let checkout_dir = forge_directory.join(&revision); - let forge_target_directory = forge_directory.join("target"); - let aptos_node_bin = forge_directory.join(format!( - "aptos-node--{}{}", - revision, - env::consts::EXE_SUFFIX - )); - - if aptos_node_bin.exists() { - return Ok((revision, aptos_node_bin)); - } - - fs::create_dir_all(&forge_target_directory)?; - - checkout_revision(&metadata, &revision, &checkout_dir)?; - - fs::rename( - cargo_build_aptos_node(&checkout_dir, &forge_target_directory)?, - &aptos_node_bin, - )?; - - let _ = fs::remove_dir_all(&checkout_dir); - - Ok((revision, aptos_node_bin)) -} - -fn git_rev_parse>(metadata: &Metadata, rev: R) -> Result { - let rev = rev.as_ref(); - let output = Command::new("git") - .current_dir(&metadata.workspace_root) - .arg("rev-parse") - .arg(rev) - .output() - .context("Failed to parse revision")?; - if output.status.success() { - String::from_utf8(output.stdout) - .map(|s| s.trim().to_owned()) - .map_err(Into::into) - } else { - bail!("Failed to parse revision: {}", rev); - } -} - -// Determine if the worktree is dirty -fn git_is_worktree_dirty() -> Result { - Command::new("git") - .args(["diff-index", "--name-only", "HEAD", "--"]) - .output() - .context("Failed to determine if the worktree is dirty") - .map(|output| !output.stdout.is_empty()) -} - -/// Attempt to query the local git repository's remotes for the one that points to the upstream -/// aptos-labs/aptos-core repository, falling back to "origin" if unable to locate the remote -pub fn git_get_upstream_remote() -> Result { - let output = Command::new("sh") - .arg("-c") - .arg( - "git remote -v | grep \"https://github.com/aptos-labs/aptos-core.* (fetch)\" | cut -f1", - ) - .output() - .context("Failed to get upstream remote")?; - - if output.status.success() { - let remote = String::from_utf8(output.stdout).map(|s| s.trim().to_owned())?; - - // If its empty, fall back to "origin" - if remote.is_empty() { - Ok("origin".into()) - } else { - Ok(remote) - } - } else { - Ok("origin".into()) - } -} - -pub fn git_merge_base>(rev: R) -> Result { - let rev = rev.as_ref(); - let output = Command::new("git") - .arg("merge-base") - .arg("HEAD") - .arg(rev) - .output() - .context("Failed to find merge base")?; - if output.status.success() { - String::from_utf8(output.stdout) - .map(|s| s.trim().to_owned()) - .map_err(Into::into) - } else { - bail!("Failed to find merge base between: {} and HEAD", rev); - } -} - -pub fn cargo_build_common_args() -> Vec<&'static str> { - let mut args = if build_aptos_node_without_indexer() { - vec!["build", "--features=failpoints,smoke-test"] - } else { - vec!["build", "--features=failpoints,indexer,smoke-test"] - }; - if build_consensus_only_node() { - args.push("--features=consensus-only-perf-test"); - } - if build_network_perf_test() { - args.push("--features=network-perf-test"); - } - if use_release() { - args.push("--release"); - } - args -} - -fn cargo_build_aptos_node(directory: D, target_directory: T) -> Result -where - D: AsRef, - T: AsRef, -{ - let target_directory = target_directory.as_ref(); - let directory = directory.as_ref(); - - let mut args = cargo_build_common_args(); - // build the aptos-node package directly to avoid feature unification issues - args.push("--package=aptos-node"); - info!("Compiling with cargo args: {:?}", args); - let output = Command::new("cargo") - .current_dir(directory) - .env("CARGO_TARGET_DIR", target_directory) - .args(&args) - .output() - .context("Failed to build aptos-node")?; - - if output.status.success() { - let bin_path = target_directory.join(format!( - "{}/{}{}", - if use_release() { "release" } else { "debug" }, - "aptos-node", - env::consts::EXE_SUFFIX - )); - if !bin_path.exists() { - bail!( - "Can't find binary aptos-node at expected path {:?}", - bin_path - ); - } - info!("Local swarm node binary path: {:?}", bin_path); - Ok(bin_path) - } else { - io::stderr().write_all(&output.stderr)?; - - bail!( - "Failed to build aptos-node: 'cd {} && CARGO_TARGET_DIR={} cargo build --bin=aptos-node", - directory.display(), - target_directory.display(), - ); - } -} - -fn checkout_revision(metadata: &Metadata, revision: &str, to: &Path) -> Result<()> { - fs::create_dir_all(to)?; - - let archive_file = NamedTempFile::new()?.into_temp_path(); - - let output = Command::new("git") - .current_dir(&metadata.workspace_root) - .arg("archive") - .arg("--format=tar") - .arg("--output") - .arg(&archive_file) - .arg(revision) - .output() - .context("Failed to run git archive")?; - if !output.status.success() { - bail!("Failed to run git archive"); - } - - let output = Command::new("tar") - .current_dir(to) - .arg("xf") - .arg(&archive_file) - .output() - .context("Failed to run tar")?; - - if !output.status.success() { - bail!("Failed to run tar"); - } - - Ok(()) -} diff --git a/testsuite/forge/src/backend/local/mod.rs b/testsuite/forge/src/backend/local/mod.rs deleted file mode 100644 index ae533eb1d8162..0000000000000 --- a/testsuite/forge/src/backend/local/mod.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{Factory, GenesisConfig, GenesisConfigFn, NodeConfigFn, Result, Swarm, Version}; -use anyhow::{bail, Context}; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_framework::ReleaseBundle; -use aptos_genesis::builder::{InitConfigFn, InitGenesisConfigFn, InitGenesisStakeFn}; -use aptos_infallible::Mutex; -use rand::rngs::StdRng; -use std::{ - collections::HashMap, - num::NonZeroUsize, - path::{Path, PathBuf}, - sync::Arc, - time::Duration, -}; - -mod cargo; -mod node; -mod swarm; -pub use self::swarm::ActiveNodesGuard; -pub use cargo::cargo_build_common_args; -pub use node::LocalNode; -pub use swarm::{LocalSwarm, SwarmDirectory}; - -#[derive(Clone, Debug)] -pub struct LocalVersion { - bin: PathBuf, - version: Version, -} - -impl LocalVersion { - pub fn new(bin: PathBuf, version: Version) -> Self { - Self { bin, version } - } - - pub fn bin(&self) -> &Path { - &self.bin - } - - pub fn version(&self) -> Version { - self.version.clone() - } -} - -pub struct LocalFactory { - versions: Arc>, - swarm_dir: Option, -} - -impl LocalFactory { - pub fn new(versions: HashMap, swarm_dir: Option) -> Self { - Self { - versions: Arc::new(versions), - swarm_dir, - } - } - - pub fn from_workspace(swarm_dir: Option) -> Result { - let mut versions = HashMap::new(); - let new_version = cargo::get_aptos_node_binary_from_worktree().map(|(revision, bin)| { - let version = Version::new(usize::max_value(), revision); - LocalVersion { bin, version } - })?; - - versions.insert(new_version.version.clone(), new_version); - Ok(Self::new(versions, swarm_dir)) - } - - pub fn from_revision(revision: &str) -> Result { - let mut versions = HashMap::new(); - let new_version = - cargo::get_aptos_node_binary_at_revision(revision).map(|(revision, bin)| { - let version = Version::new(usize::max_value(), revision); - LocalVersion { bin, version } - })?; - - versions.insert(new_version.version.clone(), new_version); - Ok(Self::new(versions, None)) - } - - pub fn with_revision_and_workspace(revision: &str) -> Result { - let workspace = cargo::get_aptos_node_binary_from_worktree().map(|(revision, bin)| { - let version = Version::new(usize::max_value(), revision); - LocalVersion { bin, version } - })?; - let revision = - cargo::get_aptos_node_binary_at_revision(revision).map(|(revision, bin)| { - let version = Version::new(usize::min_value(), revision); - LocalVersion { bin, version } - })?; - - let mut versions = HashMap::new(); - versions.insert(workspace.version(), workspace); - versions.insert(revision.version(), revision); - Ok(Self::new(versions, None)) - } - - /// Create a LocalFactory with a aptos-node version built at the tip of upstream/main and the - /// current workspace, suitable for compatibility testing. - pub fn with_upstream_and_workspace() -> Result { - let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?; - Self::with_revision_and_workspace(&upstream_main) - } - - /// Create a LocalFactory with a aptos-node version built at merge-base of upstream/main and the - /// current workspace, suitable for compatibility testing. - pub fn with_upstream_merge_base_and_workspace() -> Result { - let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?; - let merge_base = cargo::git_merge_base(upstream_main)?; - Self::with_revision_and_workspace(&merge_base) - } - - pub async fn new_swarm_with_version( - &self, - rng: R, - number_of_validators: NonZeroUsize, - number_of_fullnodes: usize, - version: &Version, - genesis_framework: Option, - init_config: Option, - vfn_config: Option, - init_genesis_stake: Option, - init_genesis_config: Option, - guard: ActiveNodesGuard, - ) -> Result - where - R: ::rand::RngCore + ::rand::CryptoRng, - { - let swarmdir = self.swarm_dir.clone().map(|sd| PathBuf::from(sd.as_str())); - // Build the swarm - let mut swarm = LocalSwarm::build( - rng, - number_of_validators, - self.versions.clone(), - Some(version.clone()), - init_config, - init_genesis_stake, - init_genesis_config, - swarmdir, - genesis_framework, - guard, - )?; - - // Launch the swarm - swarm - .launch() - .await - .with_context(|| format!("Swarm logs can be found here: {}", swarm.logs_location()))?; - - let vfn_config = vfn_config.unwrap_or_else(NodeConfig::get_default_vfn_config); - let vfn_override_config = OverrideNodeConfig::new_with_default_base(vfn_config); - - // Add and launch the fullnodes - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - for validator_peer_id in validator_peer_ids.iter().take(number_of_fullnodes) { - let _ = swarm - .add_validator_fullnode(version, vfn_override_config.clone(), *validator_peer_id) - .unwrap(); - } - swarm.wait_all_alive(Duration::from_secs(60)).await?; - - Ok(swarm) - } -} - -#[async_trait::async_trait] -impl Factory for LocalFactory { - fn versions<'a>(&'a self) -> Box + 'a> { - Box::new(self.versions.keys().cloned()) - } - - async fn launch_swarm( - &self, - rng: &mut StdRng, - num_validators: NonZeroUsize, - num_fullnodes: usize, - version: &Version, - _genesis_version: &Version, - genesis_config: Option<&GenesisConfig>, - _cleanup_duration: Duration, - _genesis_config_fn: Option, - _node_config_fn: Option, - _existing_db_tag: Option, - ) -> Result> { - let framework = match genesis_config { - Some(config) => match config { - GenesisConfig::Bundle(bundle) => Some(bundle.clone()), - GenesisConfig::Path(_) => { - bail!("local forge backend does not support flattened dir for genesis") - }, - }, - None => None, - }; - - // no guarding, as this code path is not used in parallel - let guard = ActiveNodesGuard::grab(1, Arc::new(Mutex::new(0))).await; - - let swarm = self - .new_swarm_with_version( - rng, - num_validators, - num_fullnodes, - version, - framework, - None, - None, - None, - None, - guard, - ) - .await?; - - Ok(Box::new(swarm)) - } -} diff --git a/testsuite/forge/src/backend/local/node.rs b/testsuite/forge/src/backend/local/node.rs deleted file mode 100644 index 9487e40180e60..0000000000000 --- a/testsuite/forge/src/backend/local/node.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{FullNode, HealthCheckError, LocalVersion, Node, NodeExt, Validator, Version}; -use anyhow::{anyhow, ensure, Context, Result}; -use aptos_config::{ - config::{NodeConfig, SECURE_STORAGE_FILENAME}, - keys::ConfigKey, -}; -use aptos_db::{ - common::{LEDGER_DB_NAME, STATE_MERKLE_DB_NAME}, - fast_sync_storage_wrapper::SECONDARY_DB_DIR, -}; -use aptos_logger::{debug, info}; -use aptos_sdk::{ - crypto::ed25519::Ed25519PrivateKey, - types::{account_address::AccountAddress, PeerId}, -}; -use aptos_state_sync_driver::metadata_storage::STATE_SYNC_DB_NAME; -use std::{ - env, - fs::{self, OpenOptions}, - path::PathBuf, - process::{Child, Command}, - str::FromStr, -}; -use url::Url; - -#[derive(Debug)] -struct Process(Child); - -impl Drop for Process { - // When the Process struct goes out of scope we need to kill the child process - fn drop(&mut self) { - // check if the process has already been terminated - match self.0.try_wait() { - // The child process has already terminated, perhaps due to a crash - Ok(Some(_)) => {}, - - // The process is still running so we need to attempt to kill it - _ => { - self.0.kill().expect("Process wasn't running"); - self.0.wait().unwrap(); - }, - } - } -} - -#[derive(Debug)] -pub struct LocalNode { - version: LocalVersion, - process: Option, - name: String, - index: usize, - account_private_key: Option>, - peer_id: AccountAddress, - directory: PathBuf, - config: NodeConfig, -} - -impl LocalNode { - pub fn new( - version: LocalVersion, - name: String, - index: usize, - directory: PathBuf, - account_private_key: Option>, - ) -> Result { - let config_path = directory.join("node.yaml"); - let config = NodeConfig::load_from_path(&config_path).map_err(|error| { - anyhow!( - "Failed to load NodeConfig from file: {:?}. Error: {:?}", - config_path, - error - ) - })?; - let peer_id = config - .get_peer_id() - .ok_or_else(|| anyhow!("unable to retrieve PeerId from config"))?; - - Ok(Self { - version, - process: None, - name, - index, - account_private_key, - peer_id, - directory, - config, - }) - } - - pub fn base_dir(&self) -> PathBuf { - self.directory.clone() - } - - pub fn config_path(&self) -> PathBuf { - self.directory.join("node.yaml") - } - - pub fn log_path(&self) -> PathBuf { - self.directory.join("log") - } - - pub fn peer_id(&self) -> PeerId { - self.peer_id - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn account_private_key(&self) -> &Option> { - &self.account_private_key - } - - pub fn start(&mut self) -> Result<()> { - ensure!(self.process.is_none(), "node {} already running", self.name); - - // Ensure log file exists - let log_file = OpenOptions::new() - .create(true) - .write(true) - .append(true) - .open(self.log_path())?; - - // Start node process - let mut node_command = Command::new(self.version.bin()); - node_command - .current_dir(&self.directory) - .arg("-f") - .arg(self.config_path()); - if env::var("RUST_LOG").is_err() { - // Only set our RUST_LOG if its not present in environment - node_command.env("RUST_LOG", "debug"); - } - node_command.stdout(log_file.try_clone()?).stderr(log_file); - let process = node_command.spawn().with_context(|| { - format!( - "Error launching node process with binary: {:?}", - self.version.bin() - ) - })?; - - // We print out the commands and PIDs for debugging of local swarms - info!( - "Started node {} (PID: {}) with command: {:?}, log_path: {:?}", - self.name, - process.id(), - node_command, - self.log_path(), - ); - - // We print out the API endpoints of each node for local debugging - info!( - "Node {}: REST API is listening at: http://127.0.0.1:{}", - self.name, - self.config.api.address.port() - ); - info!( - "Node {}: Inspection service is listening at http://127.0.0.1:{}", - self.name, self.config.inspection_service.port - ); - info!( - "Node {}: Admin service is listening at http://127.0.0.1:{}", - self.name, self.config.admin_service.port - ); - info!( - "Node {}: Backup service is listening at http://127.0.0.1:{}", - self.name, - self.config.storage.backup_service_address.port() - ); - - self.process = Some(Process(process)); - - Ok(()) - } - - pub fn stop(&mut self) { - self.process = None; - } - - pub fn port(&self) -> u16 { - self.config.api.address.port() - } - - pub fn inspection_service_port(&self) -> u16 { - self.config.inspection_service.port - } - - pub fn config(&self) -> &NodeConfig { - &self.config - } - - pub(crate) fn config_mut(&mut self) -> &mut NodeConfig { - &mut self.config - } - - pub fn upgrade(&mut self, version: LocalVersion) -> Result<()> { - self.stop(); - self.version = version; - self.start() - } - - pub fn get_log_contents(&self) -> Result { - fs::read_to_string(self.log_path()).map_err(Into::into) - } - - pub async fn health_check(&mut self) -> Result<(), HealthCheckError> { - debug!("Health check on node '{}'", self.name); - - if let Some(p) = &mut self.process { - match p.0.try_wait() { - // This would mean the child process has crashed - Ok(Some(status)) => { - let error = format!("Node '{}' crashed with: {}", self.name, status); - return Err(HealthCheckError::NotRunning(error)); - }, - - // This is the case where the node is still running - Ok(None) => {}, - - // Some other unknown error - Err(e) => { - return Err(HealthCheckError::Unknown(e.into())); - }, - } - } else { - let error = format!("Node '{}' is stopped", self.name); - return Err(HealthCheckError::NotRunning(error)); - } - - self.inspection_client() - .get_forge_metrics() - .await - .map(|_| ()) - .map_err(HealthCheckError::Failure)?; - - self.rest_client() - .get_ledger_information() - .await - .map(|_| ()) - .map_err(|err| HealthCheckError::Failure(err.into())) - } -} - -#[async_trait::async_trait] -impl Node for LocalNode { - fn peer_id(&self) -> PeerId { - self.peer_id() - } - - fn index(&self) -> usize { - self.index - } - - fn name(&self) -> &str { - self.name() - } - - fn version(&self) -> Version { - self.version.version() - } - - fn rest_api_endpoint(&self) -> Url { - let ip = self.config().api.address.ip(); - let port = self.config().api.address.port(); - Url::from_str(&format!("http://{}:{}/v1", ip, port)).expect("Invalid URL.") - } - - fn inspection_service_endpoint(&self) -> Url { - Url::parse(&format!( - "http://localhost:{}", - self.inspection_service_port() - )) - .unwrap() - } - - fn config(&self) -> &NodeConfig { - self.config() - } - - async fn start(&mut self) -> Result<()> { - self.start() - } - - async fn stop(&mut self) -> Result<()> { - self.stop(); - Ok(()) - } - - async fn get_identity(&mut self) -> Result { - todo!() - } - - async fn set_identity(&mut self, _k8s_secret_name: String) -> Result<()> { - todo!() - } - - async fn clear_storage(&mut self) -> Result<()> { - // Remove all storage files (i.e., blockchain data, consensus data and state sync data) - let node_config = self.config(); - let ledger_db_path = node_config.storage.dir().join(LEDGER_DB_NAME); - let state_db_path = node_config.storage.dir().join(STATE_MERKLE_DB_NAME); - let secure_storage_path = node_config.get_working_dir().join(SECURE_STORAGE_FILENAME); - let state_sync_db_path = node_config.storage.dir().join(STATE_SYNC_DB_NAME); - let secondary_db_path = node_config.storage.dir().join(SECONDARY_DB_DIR); - - debug!( - "Deleting ledger, state, secure and state sync db paths ({:?}, {:?}, {:?}, {:?}, {:?}) for node {:?}", - ledger_db_path.as_path(), - state_db_path.as_path(), - secure_storage_path.as_path(), - state_sync_db_path.as_path(), - secondary_db_path.as_path(), - self.name - ); - - // Verify the files exist - assert!(ledger_db_path.as_path().exists() && state_db_path.as_path().exists()); - assert!(state_sync_db_path.as_path().exists()); - if self.config.base.role.is_validator() { - assert!(secure_storage_path.as_path().exists()); - } - - // Remove the primary DB files - fs::remove_dir_all(ledger_db_path) - .map_err(anyhow::Error::from) - .context("Failed to delete ledger_db_path")?; - fs::remove_dir_all(state_db_path) - .map_err(anyhow::Error::from) - .context("Failed to delete state_db_path")?; - fs::remove_dir_all(state_sync_db_path) - .map_err(anyhow::Error::from) - .context("Failed to delete state_sync_db_path")?; - - // Remove the secondary DB files - if secondary_db_path.as_path().exists() { - fs::remove_dir_all(secondary_db_path) - .map_err(anyhow::Error::from) - .context("Failed to delete secondary_db_path")?; - } - - // Remove the secure storage file - if self.config.base.role.is_validator() { - fs::remove_file(secure_storage_path) - .map_err(anyhow::Error::from) - .context("Failed to delete secure_storage_db_path")?; - } - - // Stop the node to clear buffers - self.stop(); - - Ok(()) - } - - async fn health_check(&mut self) -> Result<(), HealthCheckError> { - self.health_check().await - } - - fn counter(&self, _counter: &str, _port: u64) -> Result { - todo!() - } - - // local node does not need to expose metric end point - fn expose_metric(&self) -> Result { - Ok(0) - } -} - -impl Validator for LocalNode {} -impl FullNode for LocalNode {} diff --git a/testsuite/forge/src/backend/local/swarm.rs b/testsuite/forge/src/backend/local/swarm.rs deleted file mode 100644 index 4e8905c2a3305..0000000000000 --- a/testsuite/forge/src/backend/local/swarm.rs +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - ChainInfo, FullNode, HealthCheckError, LocalNode, LocalVersion, Node, Swarm, SwarmChaos, - SwarmExt, Validator, Version, -}; -use anyhow::{anyhow, bail, Result}; -use aptos_config::{ - config::{NetworkConfig, NodeConfig, OverrideNodeConfig, PersistableConfig}, - keys::ConfigKey, - network_id::NetworkId, -}; -use aptos_framework::ReleaseBundle; -use aptos_genesis::builder::{ - FullnodeNodeConfig, InitConfigFn, InitGenesisConfigFn, InitGenesisStakeFn, -}; -use aptos_infallible::Mutex; -use aptos_logger::{info, warn}; -use aptos_sdk::{ - crypto::{ed25519::Ed25519PrivateKey, encoding_type::EncodingType}, - types::{ - chain_id::ChainId, transaction::Transaction, waypoint::Waypoint, AccountKey, LocalAccount, - PeerId, - }, -}; -use prometheus_http_query::response::{PromqlResult, Sample}; -use std::{ - collections::HashMap, - fs, - fs::File, - io::Write, - mem, - num::NonZeroUsize, - ops, - path::{Path, PathBuf}, - sync::Arc, - time::{Duration, Instant}, -}; -use tempfile::TempDir; - -#[derive(Debug)] -pub enum SwarmDirectory { - Persistent(PathBuf), - Temporary(TempDir), -} - -impl SwarmDirectory { - pub fn persist(&mut self) { - match self { - SwarmDirectory::Persistent(_) => {}, - SwarmDirectory::Temporary(_) => { - let mut temp = SwarmDirectory::Persistent(PathBuf::new()); - mem::swap(self, &mut temp); - let _ = mem::replace(self, temp.into_persistent()); - }, - } - } - - pub fn into_persistent(self) -> Self { - match self { - SwarmDirectory::Temporary(tempdir) => SwarmDirectory::Persistent(tempdir.into_path()), - SwarmDirectory::Persistent(dir) => SwarmDirectory::Persistent(dir), - } - } -} - -impl ops::Deref for SwarmDirectory { - type Target = Path; - - fn deref(&self) -> &Self::Target { - match self { - SwarmDirectory::Persistent(dir) => dir.deref(), - SwarmDirectory::Temporary(dir) => dir.path(), - } - } -} - -impl AsRef for SwarmDirectory { - fn as_ref(&self) -> &Path { - match self { - SwarmDirectory::Persistent(dir) => dir.as_ref(), - SwarmDirectory::Temporary(dir) => dir.as_ref(), - } - } -} - -#[derive(Debug)] -pub struct LocalSwarm { - node_name_counter: usize, - genesis: Transaction, - genesis_waypoint: Waypoint, - versions: Arc>, - validators: HashMap, - fullnodes: HashMap, - public_networks: HashMap, - dir: SwarmDirectory, - root_account: LocalAccount, - chain_id: ChainId, - root_key: ConfigKey, - - launched: bool, - #[allow(dead_code)] - guard: ActiveNodesGuard, -} - -impl LocalSwarm { - pub fn build( - rng: R, - number_of_validators: NonZeroUsize, - versions: Arc>, - initial_version: Option, - init_config: Option, - init_genesis_stake: Option, - init_genesis_config: Option, - dir: Option, - genesis_framework: Option, - guard: ActiveNodesGuard, - ) -> Result - where - R: ::rand::RngCore + ::rand::CryptoRng, - { - info!("Building a new swarm"); - let dir_actual = if let Some(dir_) = dir { - if dir_.exists() { - fs::remove_dir_all(&dir_)?; - } - fs::create_dir_all(&dir_)?; - SwarmDirectory::Persistent(dir_) - } else { - SwarmDirectory::Temporary(TempDir::new()?) - }; - - let (root_key, genesis, genesis_waypoint, validators) = - aptos_genesis::builder::Builder::new( - &dir_actual, - genesis_framework - .unwrap_or_else(|| aptos_cached_packages::head_release_bundle().clone()), - )? - .with_num_validators(number_of_validators) - .with_init_config(Some(Arc::new(move |index, config, base| { - // for local tests, turn off parallel execution: - config.execution.concurrency_level = 1; - - // Single node orders blocks too fast which would trigger backpressure and stall for 1 sec - // which cause flakiness in tests. - if number_of_validators.get() == 1 { - // this delays empty block by (30-1) * 30ms - config.consensus.quorum_store_poll_time_ms = 900; - config - .state_sync - .state_sync_driver - .enable_auto_bootstrapping = true; - config - .state_sync - .state_sync_driver - .max_connection_deadline_secs = 1; - } - - if let Some(init_config) = &init_config { - (init_config)(index, config, base); - } - }))) - .with_init_genesis_stake(init_genesis_stake) - .with_init_genesis_config(init_genesis_config) - .build(rng)?; - - // Get the initial version to start the nodes with, either the one provided or fallback to - // using the latest version - let initial_version_actual = initial_version.unwrap_or_else(|| { - versions - .iter() - .max_by(|v1, v2| v1.0.cmp(v2.0)) - .unwrap() - .0 - .clone() - }); - let version = versions.get(&initial_version_actual).unwrap(); - - let mut validators = validators - .into_iter() - .map(|v| { - let node = LocalNode::new( - version.to_owned(), - v.name, - v.index, - v.dir, - v.account_private_key, - )?; - Ok((node.peer_id(), node)) - }) - .collect::>>()?; - - // After genesis, remove public network from validator and add to public_networks - let public_networks = validators - .values_mut() - .map(|validator| { - let mut validator_override_config = - OverrideNodeConfig::load_config(validator.config_path())?; - let validator_config = validator_override_config.override_config_mut(); - - // Grab the public network config from the validator and insert it into the VFN's config - // The validator's public network identity is the same as the VFN's public network identity - // We remove it from the validator so the VFN can hold it - let public_network = { - let (i, _) = validator_config - .full_node_networks - .iter() - .enumerate() - .find(|(_i, config)| config.network_id == NetworkId::Public) - .expect("Validator should have a public network"); - validator_config.full_node_networks.remove(i) - }; - validator_config.set_data_dir(validator.base_dir()); - *validator.config_mut() = validator_config.clone(); - // Since the validator's config has changed we need to save it - validator_override_config.save_config(validator.config_path())?; - - Ok((validator.peer_id(), public_network)) - }) - .collect::>>()?; - - // We print out the root key to make it easy for users to deploy a local faucet - let encoded_root_key = EncodingType::Hex.encode_key("root_key", &root_key)?; - info!( - "The root (or mint) key for the swarm is: 0x{}", - String::from_utf8_lossy(encoded_root_key.as_slice()) - ); - let root_key_path = dir_actual.as_ref().join("root_key"); - if let Ok(mut out) = File::create(root_key_path.clone()) { - out.write_all(encoded_root_key.as_slice())?; - info!("Wrote root (or mint) key to: {}", root_key_path.display()); - } - let encoded_root_key = EncodingType::BCS.encode_key("root_key", &root_key)?; - let root_key_path = dir_actual.as_ref().join("root_key.bin"); - if let Ok(mut out) = File::create(root_key_path.clone()) { - out.write_all(encoded_root_key.as_slice())?; - info!("Wrote root (or mint) key to: {}", root_key_path.display()); - } - - let root_key = ConfigKey::new(root_key); - let root_account = LocalAccount::new( - aptos_sdk::types::account_config::aptos_test_root_address(), - AccountKey::from_private_key(root_key.private_key()), - 0, - ); - - Ok(LocalSwarm { - node_name_counter: validators.len(), - genesis, - genesis_waypoint, - versions, - validators, - fullnodes: HashMap::new(), - public_networks, - dir: dir_actual, - root_account, - chain_id: ChainId::test(), - root_key, - launched: false, - guard, - }) - } - - pub async fn launch(&mut self) -> Result<()> { - if self.launched { - return Err(anyhow!("Swarm already launched")); - } - self.launched = true; - - // Start all the validators - for validator in self.validators.values_mut() { - validator.start()?; - } - - self.wait_all_alive(Duration::from_secs(60)).await?; - info!("Swarm launched successfully."); - Ok(()) - } - - pub async fn wait_all_alive(&mut self, timeout: Duration) -> Result<()> { - // Wait for all of them to startup - let deadline = Instant::now() + timeout; - self.wait_for_startup().await?; - self.wait_for_connectivity(deadline).await?; - self.liveness_check(deadline).await?; - info!("Swarm alive."); - Ok(()) - } - - pub async fn wait_for_startup(&mut self) -> Result<()> { - let num_attempts = 30; - let mut done = vec![false; self.validators.len()]; - for i in 0..num_attempts { - info!("Wait for startup attempt: {} of {}", i, num_attempts); - for (node, done) in self.validators.values_mut().zip(done.iter_mut()) { - if *done { - continue; - } - match node.health_check().await { - Ok(()) => *done = true, - - Err(HealthCheckError::Unknown(e)) => { - return Err(anyhow!( - "Node '{}' is not running! Error: {}", - node.name(), - e - )); - }, - Err(HealthCheckError::NotRunning(error)) => { - return Err(anyhow!( - "Node '{}' is not running! Error: {:?}", - node.name(), - error - )); - }, - Err(HealthCheckError::Failure(e)) => { - warn!("health check failure: {}", e); - break; - }, - } - } - - // Check if all the nodes have been successfully launched - if done.iter().all(|status| *status) { - return Ok(()); - } - - tokio::time::sleep(::std::time::Duration::from_millis(1000)).await; - } - - Err(anyhow!("Launching Swarm timed out")) - } - - pub fn add_validator_fullnode( - &mut self, - version: &Version, - config: OverrideNodeConfig, - validator_peer_id: PeerId, - ) -> Result { - let validator = self - .validators - .get(&validator_peer_id) - .ok_or_else(|| anyhow!("no validator with peer_id: {}", validator_peer_id))?; - - let public_network = self - .public_networks - .get(&validator_peer_id) - .ok_or_else(|| anyhow!("no public network with peer_id: {}", validator_peer_id))?; - - if self.fullnodes.contains_key(&validator_peer_id) { - bail!("VFN for validator {} already configured", validator_peer_id); - } - - let name = self.node_name_counter.to_string(); - let index = self.node_name_counter; - self.node_name_counter += 1; - let fullnode_config = FullnodeNodeConfig::validator_fullnode( - name, - self.dir.as_ref(), - config, - validator.config(), - &self.genesis_waypoint, - &self.genesis, - public_network, - )?; - - let version = self.versions.get(version).unwrap(); - let mut fullnode = LocalNode::new( - version.to_owned(), - fullnode_config.name, - index, - fullnode_config.dir, - None, - )?; - - let peer_id = fullnode.peer_id(); - assert_eq!(peer_id, validator_peer_id); - fullnode.start()?; - - self.fullnodes.insert(peer_id, fullnode); - - Ok(peer_id) - } - - fn add_fullnode(&mut self, version: &Version, config: OverrideNodeConfig) -> Result { - let name = self.node_name_counter.to_string(); - let index = self.node_name_counter; - self.node_name_counter += 1; - let fullnode_config = FullnodeNodeConfig::public_fullnode( - name, - self.dir.as_ref(), - config, - &self.genesis_waypoint, - &self.genesis, - )?; - - let version = self.versions.get(version).unwrap(); - let mut fullnode = LocalNode::new( - version.to_owned(), - fullnode_config.name, - index, - fullnode_config.dir, - None, - )?; - - let peer_id = fullnode.peer_id(); - fullnode.start()?; - - self.fullnodes.insert(peer_id, fullnode); - - Ok(peer_id) - } - - pub fn root_key(&self) -> Ed25519PrivateKey { - self.root_key.private_key() - } - - pub fn chain_id(&self) -> ChainId { - self.chain_id - } - - pub fn validator(&self, peer_id: PeerId) -> Option<&LocalNode> { - self.validators.get(&peer_id) - } - - pub fn validator_mut(&mut self, peer_id: PeerId) -> Option<&mut LocalNode> { - self.validators.get_mut(&peer_id) - } - - pub fn validators(&self) -> impl Iterator { - let mut validators: Vec<&LocalNode> = self.validators.values().collect(); - validators.sort_by_key(|v| v.index()); // Sort by index for consistent ordering - validators.into_iter() - } - - pub fn validators_mut(&mut self) -> impl Iterator { - let mut validators: Vec<&mut LocalNode> = self.validators.values_mut().collect(); - validators.sort_by_key(|v| v.index()); // Sort by index for consistent ordering - validators.into_iter() - } - - pub fn fullnode(&self, peer_id: PeerId) -> Option<&LocalNode> { - self.fullnodes.get(&peer_id) - } - - pub fn fullnode_mut(&mut self, peer_id: PeerId) -> Option<&mut LocalNode> { - self.fullnodes.get_mut(&peer_id) - } - - pub fn fullnodes(&self) -> impl Iterator { - let mut fullnodes: Vec<&LocalNode> = self.fullnodes.values().collect(); - fullnodes.sort_by_key(|v| v.index()); // Sort by index for consistent ordering - fullnodes.into_iter() - } - - pub fn fullnodes_mut(&mut self) -> impl Iterator { - let mut fullnodes: Vec<&mut LocalNode> = self.fullnodes.values_mut().collect(); - fullnodes.sort_by_key(|v| v.index()); // Sort by index for consistent ordering - fullnodes.into_iter() - } - - pub fn dir(&self) -> &Path { - self.dir.as_ref() - } -} - -impl Drop for LocalSwarm { - fn drop(&mut self) { - // If panicking, persist logs - if std::env::var("LOCAL_SWARM_SAVE_LOGS").is_ok() || std::thread::panicking() { - eprintln!("Logs located at {}", self.logs_location()); - } - } -} - -#[async_trait::async_trait] -impl Swarm for LocalSwarm { - async fn health_check(&mut self) -> Result<()> { - Ok(()) - } - - fn validators<'a>(&'a self) -> Box + 'a> { - let mut validators: Vec<_> = self - .validators - .values() - .map(|v| v as &'a dyn Validator) - .collect(); - validators.sort_by_key(|v| v.index()); - Box::new(validators.into_iter()) - } - - fn validators_mut<'a>(&'a mut self) -> Box + 'a> { - let mut validators: Vec<_> = self - .validators - .values_mut() - .map(|v| v as &'a mut dyn Validator) - .collect(); - validators.sort_by_key(|v| v.index()); - Box::new(validators.into_iter()) - } - - fn validator(&self, id: PeerId) -> Option<&dyn Validator> { - self.validators.get(&id).map(|v| v as &dyn Validator) - } - - fn validator_mut(&mut self, id: PeerId) -> Option<&mut dyn Validator> { - self.validators - .get_mut(&id) - .map(|v| v as &mut dyn Validator) - } - - async fn upgrade_validator(&mut self, id: PeerId, version: &Version) -> Result<()> { - let version = self - .versions - .get(version) - .cloned() - .ok_or_else(|| anyhow!("Invalid version: {:?}", version))?; - let validator = self - .validators - .get_mut(&id) - .ok_or_else(|| anyhow!("Invalid id: {}", id))?; - validator.upgrade(version) - } - - fn full_nodes<'a>(&'a self) -> Box + 'a> { - let mut full_nodes: Vec<_> = self - .fullnodes - .values() - .map(|v| v as &'a dyn FullNode) - .collect(); - full_nodes.sort_by_key(|n| n.index()); - Box::new(full_nodes.into_iter()) - } - - fn full_nodes_mut<'a>(&'a mut self) -> Box + 'a> { - let mut full_nodes: Vec<_> = self - .fullnodes - .values_mut() - .map(|v| v as &'a mut dyn FullNode) - .collect(); - full_nodes.sort_by_key(|n| n.index()); - Box::new(full_nodes.into_iter()) - } - - fn full_node(&self, id: PeerId) -> Option<&dyn FullNode> { - self.fullnodes.get(&id).map(|v| v as &dyn FullNode) - } - - fn full_node_mut(&mut self, id: PeerId) -> Option<&mut dyn FullNode> { - self.fullnodes.get_mut(&id).map(|v| v as &mut dyn FullNode) - } - - fn add_validator(&mut self, _version: &Version, _template: NodeConfig) -> Result { - todo!() - } - - fn remove_validator(&mut self, _id: PeerId) -> Result<()> { - todo!() - } - - fn add_validator_full_node( - &mut self, - version: &Version, - config: OverrideNodeConfig, - id: PeerId, - ) -> Result { - self.add_validator_fullnode(version, config, id) - } - - async fn add_full_node( - &mut self, - version: &Version, - config: OverrideNodeConfig, - ) -> Result { - self.add_fullnode(version, config) - } - - fn remove_full_node(&mut self, id: PeerId) -> Result<()> { - if let Some(mut fullnode) = self.fullnodes.remove(&id) { - fullnode.stop(); - } - - Ok(()) - } - - fn versions<'a>(&'a self) -> Box + 'a> { - Box::new(self.versions.keys().cloned()) - } - - fn chain_info(&mut self) -> ChainInfo<'_> { - let rest_api_url = self - .validators() - .next() - .unwrap() - .rest_api_endpoint() - .to_string(); - let inspection_service_url = self - .validators() - .next() - .unwrap() - .inspection_service_endpoint() - .to_string(); - - ChainInfo::new( - &mut self.root_account, - rest_api_url, - inspection_service_url, - self.chain_id, - ) - } - - fn logs_location(&mut self) -> String { - self.dir.persist(); - self.dir.display().to_string() - } - - async fn inject_chaos(&mut self, _chaos: SwarmChaos) -> Result<()> { - todo!() - } - - async fn remove_chaos(&mut self, _chaos: SwarmChaos) -> Result<()> { - todo!() - } - - async fn remove_all_chaos(&mut self) -> Result<()> { - todo!() - } - - async fn ensure_no_validator_restart(&self) -> Result<()> { - todo!() - } - - async fn ensure_no_fullnode_restart(&self) -> Result<()> { - todo!() - } - - async fn query_metrics( - &self, - _query: &str, - _time: Option, - _timeout: Option, - ) -> Result { - todo!() - } - - async fn query_range_metrics( - &self, - _query: &str, - _start_time: i64, - _end_time: i64, - _timeout: Option, - ) -> Result> { - todo!() - } - - fn chain_info_for_node(&mut self, idx: usize) -> ChainInfo<'_> { - let rest_api_url = self - .validators() - .nth(idx) - .unwrap() - .rest_api_endpoint() - .to_string(); - let inspection_service_url = self - .validators() - .nth(idx) - .unwrap() - .inspection_service_endpoint() - .to_string(); - ChainInfo::new( - &mut self.root_account, - rest_api_url, - inspection_service_url, - self.chain_id, - ) - } - - fn get_default_pfn_node_config(&self) -> NodeConfig { - todo!() - } -} - -#[derive(Debug)] -pub struct ActiveNodesGuard { - counter: Arc>, - slots: usize, -} - -impl ActiveNodesGuard { - pub async fn grab(slots: usize, counter: Arc>) -> Self { - let max = num_cpus::get(); - let mut idx = 0; - loop { - { - let mut guard = counter.lock(); - // first check is so that if test needs more slots than cores, - // we still allow it to run (during low contention) - if *guard <= 2 || *guard + slots <= max { - info!( - "Grabbed {} node slots to start test, already active {} swarm nodes", - slots, *guard - ); - *guard += slots; - drop(guard); - return Self { counter, slots }; - } - idx += 1; - // log only if idx is power of two, to reduce logs - if (idx & (idx - 1)) == 0 { - info!( - "Too many active swarm nodes ({}), max allowed is {}, waiting to start {} new ones", - *guard, max, slots, - ); - } - } - tokio::time::sleep(Duration::from_secs(2)).await; - } - } -} - -impl Drop for ActiveNodesGuard { - fn drop(&mut self) { - let mut guard = self.counter.lock(); - *guard -= self.slots; - } -} diff --git a/testsuite/forge/src/backend/mod.rs b/testsuite/forge/src/backend/mod.rs deleted file mode 100644 index 24b996e75ca45..0000000000000 --- a/testsuite/forge/src/backend/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -mod local; -pub use local::{LocalNode, *}; - -mod k8s; -pub use k8s::{K8sNode, *}; diff --git a/testsuite/forge/src/github.rs b/testsuite/forge/src/github.rs deleted file mode 100644 index 300419dfb33b8..0000000000000 --- a/testsuite/forge/src/github.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use anyhow::{anyhow, format_err, Result}; -use reqwest::{header::USER_AGENT, Url}; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct CommitInfo { - pub sha: String, - pub commit: GitCommitInfo, -} - -#[derive(Debug, Deserialize)] -pub struct GitCommitInfo { - pub author: Author, - pub message: String, -} - -#[derive(Debug, Deserialize)] -pub struct Author { - pub name: String, - pub email: String, -} - -pub struct GitHub { - client: reqwest::blocking::Client, -} - -impl GitHub { - pub fn new() -> GitHub { - let client = reqwest::blocking::Client::new(); - GitHub { client } - } - - /// repo in format owner/repo_name - /// sha can be long or short hash, or branch name - /// Paging is not implemented yet - pub fn get_commits(&self, repo: &str, sha: &str) -> Result> { - let url = format!("https://api.github.com/repos/{}/commits?sha={}", repo, sha); - let url: Url = url.parse().map_err(|e| { - anyhow!( - "Failed to parse github url: {:?}\n, resulted in Error:{}", - url, - e - ) - })?; - let request = self.client.get(url); - let response = request - .header(USER_AGENT, "aptos-forge") - .send() - .map_err(|e| format_err!("Failed to query github: {:?}", e))?; - let response: Vec = response - .json() - .map_err(|e| format_err!("Failed to parse github response: {:?}", e))?; - Ok(response) - } -} - -impl Default for GitHub { - fn default() -> Self { - Self::new() - } -} diff --git a/testsuite/forge/src/interface/admin.rs b/testsuite/forge/src/interface/admin.rs deleted file mode 100644 index 50d726ced5a6a..0000000000000 --- a/testsuite/forge/src/interface/admin.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::{ChainInfo, CoreContext, Test}; -use crate::{Result, TestReport}; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::types::LocalAccount; -use reqwest::Url; - -/// The testing interface which defines a test written from the perspective of the Admin of the -/// network. This means that the test will have access to the Root account but do not control any -/// of the validators or full nodes running on the network. -pub trait AdminTest: Test { - /// Executes the test against the given context. - fn run(&self, ctx: &mut AdminContext<'_>) -> Result<()>; -} - -#[derive(Debug)] -pub struct AdminContext<'t> { - core: CoreContext, - - chain_info: ChainInfo<'t>, - pub report: &'t mut TestReport, -} - -impl<'t> AdminContext<'t> { - pub fn new(core: CoreContext, chain_info: ChainInfo<'t>, report: &'t mut TestReport) -> Self { - Self { - core, - chain_info, - report, - } - } - - pub fn core(&self) -> &CoreContext { - &self.core - } - - pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng { - self.core.rng() - } - - pub fn rest_client(&self) -> RestClient { - RestClient::new(Url::parse(self.chain_info.rest_api()).unwrap()) - } - - pub fn chain_info(&mut self) -> &mut ChainInfo<'t> { - &mut self.chain_info - } - - pub fn random_account(&mut self) -> LocalAccount { - LocalAccount::generate(self.core.rng()) - } -} diff --git a/testsuite/forge/src/interface/aptos.rs b/testsuite/forge/src/interface/aptos.rs deleted file mode 100644 index 26a37aec9c668..0000000000000 --- a/testsuite/forge/src/interface/aptos.rs +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::Test; -use crate::{CoreContext, Result, TestReport}; -use aptos_cached_packages::aptos_stdlib; -use aptos_logger::info; -use aptos_rest_client::{Client as RestClient, PendingTransaction, State, Transaction}; -use aptos_sdk::{ - crypto::ed25519::Ed25519PublicKey, - move_types::identifier::Identifier, - transaction_builder::TransactionFactory, - types::{ - account_address::AccountAddress, - account_config::CORE_CODE_ADDRESS, - chain_id::ChainId, - transaction::{ - authenticator::{AnyPublicKey, AuthenticationKey}, - SignedTransaction, - }, - LocalAccount, - }, -}; -use rand::{rngs::OsRng, Rng, SeedableRng}; -use reqwest::Url; -use serde::{Deserialize, Serialize}; - -#[async_trait::async_trait] -pub trait AptosTest: Test { - /// Executes the test against the given context. - async fn run<'t>(&self, ctx: &mut AptosContext<'t>) -> Result<()>; -} - -pub struct AptosContext<'t> { - core: CoreContext, - public_info: AptosPublicInfo<'t>, - pub report: &'t mut TestReport, -} - -impl<'t> AptosContext<'t> { - pub fn new( - core: CoreContext, - public_info: AptosPublicInfo<'t>, - report: &'t mut TestReport, - ) -> Self { - Self { - core, - public_info, - report, - } - } - - pub fn client(&self) -> RestClient { - RestClient::new(self.public_info.rest_api_url.clone()) - } - - pub fn url(&self) -> &str { - self.public_info.rest_api_url.as_str() - } - - pub fn core(&self) -> &CoreContext { - &self.core - } - - pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng { - self.core.rng() - } - - pub fn random_account(&mut self) -> LocalAccount { - LocalAccount::generate(self.core.rng()) - } - - pub fn chain_id(&self) -> ChainId { - self.public_info.chain_id - } - - pub fn transaction_factory(&self) -> TransactionFactory { - let unit_price = std::cmp::max(aptos_global_constants::GAS_UNIT_PRICE, 1); - TransactionFactory::new(self.chain_id()).with_gas_unit_price(unit_price) - } - - pub fn aptos_transaction_factory(&self) -> TransactionFactory { - self.public_info.transaction_factory() - } - - pub async fn create_user_account(&mut self, pubkey: &Ed25519PublicKey) -> Result<()> { - self.public_info.create_user_account(pubkey).await - } - - pub async fn mint(&mut self, addr: AccountAddress, amount: u64) -> Result<()> { - self.public_info.mint(addr, amount).await - } - - pub async fn transfer( - &self, - from_account: &mut LocalAccount, - to_account: &LocalAccount, - amount: u64, - ) -> Result { - self.public_info - .transfer(from_account, to_account, amount) - .await - } - - pub async fn get_balance(&self, address: AccountAddress) -> Option { - self.public_info.get_balance(address).await - } - - pub fn root_account(&mut self) -> &mut LocalAccount { - self.public_info.root_account - } -} - -pub struct AptosPublicInfo<'t> { - chain_id: ChainId, - inspection_service_url: Url, - rest_api_url: Url, - rest_client: RestClient, - root_account: &'t mut LocalAccount, - rng: ::rand::rngs::StdRng, -} - -impl<'t> AptosPublicInfo<'t> { - pub fn new( - chain_id: ChainId, - inspection_service_url_str: String, - rest_api_url_str: String, - root_account: &'t mut LocalAccount, - ) -> Self { - let rest_api_url = Url::parse(&rest_api_url_str).unwrap(); - let inspection_service_url = Url::parse(&inspection_service_url_str).unwrap(); - Self { - inspection_service_url, - rest_client: RestClient::new(rest_api_url.clone()), - rest_api_url, - chain_id, - root_account, - rng: ::rand::rngs::StdRng::from_seed(OsRng.gen()), - } - } - - pub fn client(&self) -> &RestClient { - &self.rest_client - } - - pub fn url(&self) -> &str { - self.rest_api_url.as_str() - } - - pub fn inspection_service_url(&self) -> &str { - self.inspection_service_url.as_str() - } - - pub fn root_account(&mut self) -> &mut LocalAccount { - self.root_account - } - - pub async fn create_user_account(&mut self, pubkey: &Ed25519PublicKey) -> Result<()> { - let auth_key = AuthenticationKey::ed25519(pubkey); - let create_account_txn = - self.root_account - .sign_with_transaction_builder(self.transaction_factory().payload( - aptos_stdlib::aptos_account_create_account(auth_key.account_address()), - )); - self.rest_client - .submit_and_wait(&create_account_txn) - .await?; - Ok(()) - } - - pub async fn create_user_account_with_any_key( - &mut self, - pubkey: &AnyPublicKey, - ) -> Result { - let auth_key = AuthenticationKey::any_key(pubkey.clone()); - let create_account_txn = - self.root_account - .sign_with_transaction_builder(self.transaction_factory().payload( - aptos_stdlib::aptos_account_create_account(auth_key.account_address()), - )); - self.rest_client - .submit_and_wait(&create_account_txn) - .await?; - Ok(auth_key.account_address()) - } - - pub async fn mint(&mut self, addr: AccountAddress, amount: u64) -> Result<()> { - let mint_txn = self.root_account.sign_with_transaction_builder( - self.transaction_factory() - .payload(aptos_stdlib::aptos_coin_mint(addr, amount)), - ); - self.rest_client.submit_and_wait(&mint_txn).await?; - Ok(()) - } - - pub async fn transfer_non_blocking( - &self, - from_account: &mut LocalAccount, - to_account: &LocalAccount, - amount: u64, - ) -> Result { - let tx = from_account.sign_with_transaction_builder(self.transaction_factory().payload( - aptos_stdlib::aptos_coin_transfer(to_account.address(), amount), - )); - let pending_txn = self.rest_client.submit(&tx).await?.into_inner(); - Ok(pending_txn) - } - - pub async fn transfer( - &self, - from_account: &mut LocalAccount, - to_account: &LocalAccount, - amount: u64, - ) -> Result { - let pending_txn = self - .transfer_non_blocking(from_account, to_account, amount) - .await?; - self.rest_client.wait_for_transaction(&pending_txn).await?; - Ok(pending_txn) - } - - pub fn transaction_factory(&self) -> TransactionFactory { - let unit_price = std::cmp::max(aptos_global_constants::GAS_UNIT_PRICE, 1); - TransactionFactory::new(self.chain_id).with_gas_unit_price(unit_price) - } - - pub async fn get_approved_execution_hash_at_aptos_governance( - &self, - proposal_id: u64, - ) -> Vec { - let approved_execution_hashes = self - .rest_client - .get_account_resource_bcs::>>( - CORE_CODE_ADDRESS, - "0x1::aptos_governance::ApprovedExecutionHashes", - ) - .await; - let hashes = approved_execution_hashes.unwrap().into_inner().data; - let mut execution_hash = vec![]; - for hash in hashes { - if hash.key == proposal_id { - execution_hash = hash.value; - break; - } - } - execution_hash - } - - pub async fn get_balance(&self, address: AccountAddress) -> Option { - let module = Identifier::new("coin".to_string()).unwrap(); - let name = Identifier::new("CoinStore".to_string()).unwrap(); - self.rest_client - .get_account_resources(address) - .await - .unwrap() - .into_inner() - .into_iter() - .find(|r| r.resource_type.name == name && r.resource_type.module == module) - .and_then(|coin| { - coin.data - .get("coin") - .unwrap() - .get("value") - .unwrap() - .as_str() - .and_then(|s| s.parse::().ok()) - }) - } - - pub fn random_account(&mut self) -> LocalAccount { - LocalAccount::generate(&mut self.rng) - } - - pub async fn create_and_fund_user_account(&mut self, amount: u64) -> Result { - let account = self.random_account(); - self.create_user_account(account.public_key()).await?; - self.mint(account.address(), amount).await?; - Ok(account) - } - - pub async fn reconfig(&mut self) -> State { - // dedupe with smoke-test::test_utils::reconfig - reconfig( - &self.rest_client, - &self.transaction_factory(), - self.root_account, - ) - .await - } - - /// Syncs the root account to it's sequence number in the event that a faucet changed it's value - pub async fn sync_root_account_sequence_number(&mut self) { - let root_address = self.root_account().address(); - let root_sequence_number = self - .client() - .get_account_bcs(root_address) - .await - .unwrap() - .into_inner() - .sequence_number(); - self.root_account() - .set_sequence_number(root_sequence_number); - } -} - -pub async fn reconfig( - client: &RestClient, - transaction_factory: &TransactionFactory, - root_account: &mut LocalAccount, -) -> State { - let aptos_version = client.get_aptos_version().await.unwrap(); - let current = aptos_version.into_inner(); - let current_version = *current.major.inner(); - let txns = vec![ - root_account.sign_with_transaction_builder(transaction_factory.clone().payload( - aptos_stdlib::version_set_for_next_epoch(current_version + 1), - )), - root_account.sign_with_transaction_builder( - transaction_factory - .clone() - .payload(aptos_stdlib::aptos_governance_force_end_epoch_test_only()), - ), - ]; - - submit_and_wait_reconfig(client, txns).await -} - -pub async fn submit_and_wait_reconfig( - client: &RestClient, - mut txns: Vec, -) -> State { - let state = client.get_ledger_information().await.unwrap().into_inner(); - let last_txn = txns.pop().unwrap(); - for txn in txns { - let _ = client.submit(&txn).await; - } - let result = client.submit_and_wait(&last_txn).await; - if let Err(e) = result { - let last_transactions = client - .get_account_transactions(last_txn.sender(), None, None) - .await - .map(|result| { - result - .into_inner() - .iter() - .map(|t| { - if let Transaction::UserTransaction(ut) = t { - format!( - "user seq={}, payload={:?}", - ut.request.sequence_number, ut.request.payload - ) - } else { - t.type_str().to_string() - } - }) - .collect::>() - }); - - panic!( - "Couldn't execute {:?}, for account {:?}, error {:?}, last account transactions: {:?}", - last_txn, - last_txn.sender(), - e, - last_transactions.unwrap_or_default() - ) - } - - let transaction = result.unwrap(); - // Next transaction after reconfig should be a new epoch. - let new_state = client - .wait_for_version(transaction.inner().version().unwrap() + 1) - .await - .unwrap(); - - info!( - "Applied reconfig from (epoch={}, ledger_v={}), to (epoch={}, ledger_v={})", - state.epoch, state.version, new_state.epoch, new_state.version - ); - assert_ne!(state.epoch, new_state.epoch); - - new_state -} - -#[derive(Serialize, Deserialize)] -struct SimpleMap { - data: Vec>, -} - -#[derive(Serialize, Deserialize)] -struct Element { - key: K, - value: V, -} diff --git a/testsuite/forge/src/interface/chain_info.rs b/testsuite/forge/src/interface/chain_info.rs deleted file mode 100644 index 9baa8c22ac9f1..0000000000000 --- a/testsuite/forge/src/interface/chain_info.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::AptosPublicInfo; -use anyhow::Result; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::{ - transaction_builder::TransactionFactory, - types::{chain_id::ChainId, LocalAccount}, -}; -use reqwest::Url; - -#[derive(Debug)] -pub struct ChainInfo<'t> { - pub root_account: &'t mut LocalAccount, - pub rest_api_url: String, - pub inspection_service_url: String, - pub chain_id: ChainId, -} - -impl<'t> ChainInfo<'t> { - pub fn new( - root_account: &'t mut LocalAccount, - rest_api_url: String, - inspection_service_url: String, - chain_id: ChainId, - ) -> Self { - Self { - root_account, - rest_api_url, - inspection_service_url, - chain_id, - } - } - - pub fn root_account(&mut self) -> &mut LocalAccount { - self.root_account - } - - pub async fn resync_root_account_seq_num(&mut self, client: &RestClient) -> Result<()> { - let account = client - .get_account(self.root_account.address()) - .await? - .into_inner(); - self.root_account - .set_sequence_number(account.sequence_number); - Ok(()) - } - - pub fn rest_api(&self) -> &str { - &self.rest_api_url - } - - pub fn rest_client(&self) -> RestClient { - RestClient::new(Url::parse(self.rest_api()).unwrap()) - } - - pub fn chain_id(&self) -> ChainId { - self.chain_id - } - - pub fn transaction_factory(&self) -> TransactionFactory { - TransactionFactory::new(self.chain_id()) - } - - pub fn into_aptos_public_info(self) -> AptosPublicInfo<'t> { - AptosPublicInfo::new( - self.chain_id, - self.inspection_service_url.clone(), - self.rest_api_url.clone(), - self.root_account, - ) - } -} diff --git a/testsuite/forge/src/interface/chaos.rs b/testsuite/forge/src/interface/chaos.rs deleted file mode 100644 index cb2375f643182..0000000000000 --- a/testsuite/forge/src/interface/chaos.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_sdk::types::PeerId; -use std::fmt::{Display, Formatter}; - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub enum SwarmChaos { - Delay(SwarmNetworkDelay), - Partition(SwarmNetworkPartition), - Bandwidth(SwarmNetworkBandwidth), - Loss(SwarmNetworkLoss), - NetEm(SwarmNetEm), - CpuStress(SwarmCpuStress), -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmNetworkDelay { - pub group_network_delays: Vec, -} - -impl Display for SwarmNetworkDelay { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "Delay nodes {:?}", self.group_network_delays) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct GroupNetworkDelay { - pub name: String, - pub source_nodes: Vec, - pub target_nodes: Vec, - pub latency_ms: u64, - pub jitter_ms: u64, - pub correlation_percentage: u64, -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmNetworkPartition { - pub partition_percentage: u64, -} - -impl Display for SwarmNetworkPartition { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "Partition {} nodes", self.partition_percentage) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmNetworkBandwidth { - pub group_network_bandwidths: Vec, -} - -impl Display for SwarmNetworkBandwidth { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "Bandwidth nodes {:?}", self.group_network_bandwidths) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct GroupNetworkBandwidth { - pub name: String, - /// Rate in megabytes per second - pub rate: u64, - pub limit: u64, - pub buffer: u64, -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmNetworkLoss { - pub loss_percentage: u64, - pub correlation_percentage: u64, -} - -impl Display for SwarmNetworkLoss { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!( - f, - "Loss on all nodes: loss {}, correlation {},", - self.loss_percentage, self.correlation_percentage, - ) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmNetEm { - pub group_netems: Vec, -} - -impl Display for SwarmNetEm { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "NetEm nodes {:?}", self.group_netems) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct GroupNetEm { - pub name: String, - pub source_nodes: Vec, - pub target_nodes: Vec, - pub delay_latency_ms: u64, - pub delay_jitter_ms: u64, - pub delay_correlation_percentage: u64, - pub loss_percentage: u64, - pub loss_correlation_percentage: u64, - pub rate_in_mbps: u64, -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct SwarmCpuStress { - pub group_cpu_stresses: Vec, -} - -impl Display for SwarmCpuStress { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "CpuStress nodes {:?}", self.group_cpu_stresses) - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub struct GroupCpuStress { - pub name: String, - pub target_nodes: Vec, - pub num_workers: u64, - pub load_per_worker: u64, -} diff --git a/testsuite/forge/src/interface/factory.rs b/testsuite/forge/src/interface/factory.rs deleted file mode 100644 index 8af6ab1de8b4b..0000000000000 --- a/testsuite/forge/src/interface/factory.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::{GenesisConfig, Swarm, Version}; -use crate::{GenesisConfigFn, NodeConfigFn, Result}; -use rand::rngs::StdRng; -use std::{num::NonZeroUsize, time::Duration}; - -/// Trait used to represent a interface for constructing a launching new networks -#[async_trait::async_trait] -pub trait Factory { - fn versions<'a>(&'a self) -> Box + 'a>; - - async fn launch_swarm( - &self, - rng: &mut StdRng, - num_validators: NonZeroUsize, - num_fullnodes: usize, - version: &Version, - genesis_version: &Version, - genesis_modules: Option<&GenesisConfig>, - cleanup_duration: Duration, - genesis_config_fn: Option, - node_config_fn: Option, - existing_db_tag: Option, - ) -> Result>; -} diff --git a/testsuite/forge/src/interface/mod.rs b/testsuite/forge/src/interface/mod.rs deleted file mode 100644 index 5a6507219e0ef..0000000000000 --- a/testsuite/forge/src/interface/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -mod admin; -pub use admin::*; -mod aptos; -pub use self::aptos::*; -mod network; -pub use network::*; -mod test; -pub use test::*; -mod factory; -pub use factory::*; -mod swarm; -pub use swarm::*; -mod chaos; -pub use chaos::*; -mod node; -pub use node::*; -mod chain_info; -pub mod prometheus_metrics; - -use aptos_framework::ReleaseBundle; -pub use chain_info::*; - -/// A wrapper around a usize in order to represent an opaque version of a Node. -/// -/// It is intended that backends will be able to take this opaque version identifier and lookup the -/// appropriate version information internally to be able to determine the version of node software -/// to use. -/// -/// It's expected that `Version`s returned by querying a `Factory` or a `Swarm` will be sort-able -/// such that they'll be ordered with older versions first, e.g. older -> newer. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Version(usize, String); - -impl Version { - pub fn new(version: usize, display_string: String) -> Self { - Self(version, display_string) - } -} - -impl std::fmt::Display for Version { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.1) - } -} - -#[derive(Clone)] -pub enum GenesisConfig { - Bundle(ReleaseBundle), - Path(String), -} diff --git a/testsuite/forge/src/interface/network.rs b/testsuite/forge/src/interface/network.rs deleted file mode 100644 index 1d4f87fc2a9f4..0000000000000 --- a/testsuite/forge/src/interface/network.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::Test; -use crate::{ - prometheus_metrics::LatencyBreakdown, - success_criteria::{SuccessCriteria, SuccessCriteriaChecker}, - CoreContext, Result, Swarm, TestReport, -}; -use aptos_transaction_emitter_lib::{EmitJobRequest, TxnStats}; -use std::time::Duration; -use tokio::runtime::Runtime; - -/// The testing interface which defines a test written with full control over an existing network. -/// Tests written against this interface will have access to both the Root account as well as the -/// nodes which comprise the network. -pub trait NetworkTest: Test { - /// Executes the test against the given context. - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()>; -} - -pub struct NetworkContext<'t> { - core: CoreContext, - pub swarm: &'t mut dyn Swarm, - pub report: &'t mut TestReport, - pub global_duration: Duration, - pub emit_job: EmitJobRequest, - pub success_criteria: SuccessCriteria, - pub runtime: Runtime, -} - -impl<'t> NetworkContext<'t> { - pub fn new( - core: CoreContext, - swarm: &'t mut dyn Swarm, - report: &'t mut TestReport, - global_duration: Duration, - emit_job: EmitJobRequest, - success_criteria: SuccessCriteria, - ) -> Self { - Self { - core, - swarm, - report, - global_duration, - emit_job, - success_criteria, - runtime: Runtime::new().unwrap(), - } - } - - pub fn swarm(&mut self) -> &mut dyn Swarm { - self.swarm - } - - pub fn core(&mut self) -> &mut CoreContext { - &mut self.core - } - - pub fn check_for_success( - &mut self, - stats: &TxnStats, - window: Duration, - latency_breakdown: &LatencyBreakdown, - start_time: i64, - end_time: i64, - start_version: u64, - end_version: u64, - ) -> Result<()> { - self.runtime - .block_on(SuccessCriteriaChecker::check_for_success( - &self.success_criteria, - self.swarm, - self.report, - stats, - window, - latency_breakdown, - start_time, - end_time, - start_version, - end_version, - )) - } -} diff --git a/testsuite/forge/src/interface/node.rs b/testsuite/forge/src/interface/node.rs deleted file mode 100644 index 02dd405b97c59..0000000000000 --- a/testsuite/forge/src/interface/node.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{Result, Version}; -use anyhow::anyhow; -use aptos_config::{config::NodeConfig, network_id::NetworkId}; -use aptos_inspection_service::inspection_client::InspectionClient; -use aptos_rest_client::{AptosBaseUrl, Client as RestClient}; -use aptos_sdk::types::PeerId; -use std::{ - collections::HashMap, - time::{Duration, Instant}, -}; -use url::Url; - -#[derive(Debug)] -pub enum HealthCheckError { - NotRunning(String), - Failure(anyhow::Error), - Unknown(anyhow::Error), -} - -impl std::fmt::Display for HealthCheckError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl std::error::Error for HealthCheckError {} - -/// Trait used to represent a running Validator or FullNode -#[async_trait::async_trait] -pub trait Node: Send + Sync { - /// Return the PeerId of this Node - fn peer_id(&self) -> PeerId; - - /// Return index of the node - fn index(&self) -> usize; - - /// Return the human readable name of this Node - fn name(&self) -> &str; - - /// Return the version this node is running - fn version(&self) -> Version; - - /// Return the URL for the REST API endpoint of this Node - fn rest_api_endpoint(&self) -> Url; - - /// Return the URL for the debug-interface for this Node - fn inspection_service_endpoint(&self) -> Url; - - /// Return a reference to the Config this Node is using - fn config(&self) -> &NodeConfig; - - /// Start this Node. - /// This should be a noop if the Node is already running. - async fn start(&mut self) -> Result<()>; - - /// Stop this Node. - /// This should be a noop if the Node isn't running. - async fn stop(&mut self) -> Result<()>; - - async fn get_identity(&mut self) -> Result; - - async fn set_identity(&mut self, k8s_secret_name: String) -> Result<()>; - /// Clears this Node's Storage. This stops the node as well - async fn clear_storage(&mut self) -> Result<()>; - - async fn health_check(&mut self) -> Result<(), HealthCheckError>; - - fn counter(&self, counter: &str, port: u64) -> Result; - - fn expose_metric(&self) -> Result; -} - -/// Trait used to represent a running Validator -#[async_trait::async_trait] -pub trait Validator: Node + Sync { - async fn check_connectivity( - &self, - network_id: NetworkId, - expected_peers: usize, - ) -> Result { - if expected_peers == 0 { - return Ok(true); - } - - self.get_connected_peers(network_id, None) - .await - .map(|maybe_n| maybe_n.map(|n| n >= expected_peers as i64).unwrap_or(false)) - } - - async fn wait_for_connectivity( - &self, - network_id: NetworkId, - expected_peers: usize, - deadline: Instant, - ) -> Result<()> { - while !self.check_connectivity(network_id, expected_peers).await? { - if Instant::now() > deadline { - return Err(anyhow!("waiting for connectivity timed out")); - } - - tokio::time::sleep(Duration::from_millis(500)).await; - } - - Ok(()) - } -} - -/// Trait used to represent a running FullNode -#[async_trait::async_trait] -pub trait FullNode: Node + Sync { - //TODO handle VFNs querying if they are connected to a validator - async fn check_connectivity(&self) -> Result { - const DIRECTION: Option<&str> = Some("outbound"); - const EXPECTED_PEERS: usize = 1; - - for &network_id in &[NetworkId::Public, NetworkId::Vfn] { - let r = self - .get_connected_peers(network_id, DIRECTION) - .await - .map(|maybe_n| maybe_n.map(|n| n >= EXPECTED_PEERS as i64).unwrap_or(false)); - if let Ok(true) = r { - return Ok(true); - } - } - Ok(false) - } - - async fn wait_for_connectivity(&self, deadline: Instant) -> Result<()> { - while !self.check_connectivity().await? { - if Instant::now() > deadline { - return Err(anyhow!("waiting for connectivity timed out")); - } - - tokio::time::sleep(Duration::from_millis(500)).await; - } - - Ok(()) - } -} - -impl NodeExt for T where T: Node {} - -#[async_trait::async_trait] -pub trait NodeExt: Node { - /// Return REST API client of this Node - fn rest_client(&self) -> RestClient { - RestClient::new(self.rest_api_endpoint()) - } - - /// Return REST API client of this Node - fn rest_client_with_timeout(&self, timeout: Duration) -> RestClient { - RestClient::builder(AptosBaseUrl::Custom(self.rest_api_endpoint())) - .timeout(timeout) - .build() - } - - /// Return an InspectionClient for this Node - fn inspection_client(&self) -> InspectionClient { - InspectionClient::new(self.inspection_service_endpoint()) - } - - /// Restarts this Node by calling Node::Stop followed by Node::Start - async fn restart(&mut self) -> Result<()> { - self.stop().await?; - self.start().await - } - - /// Query a Metric for from this Node - async fn get_metric_i64(&self, metric_name: &str) -> Result> { - self.inspection_client() - .get_node_metric_i64(metric_name) - .await - } - - async fn get_metric_with_fields_i64( - &self, - metric_name: &str, - fields: HashMap, - ) -> Result> { - let filtered: Vec<_> = self - .inspection_client() - .get_node_metric_with_name(metric_name) - .await? - .into_iter() - .flat_map(|map| map.into_iter()) - .filter_map(|(metric, metric_value)| { - if fields - .iter() - .all(|(key, value)| metric.contains(&format!("{}={}", key, value))) - { - Some(metric_value) - } else { - None - } - }) - .collect(); - - Ok(if filtered.is_empty() { - None - } else { - let checked: Result> = filtered.into_iter().map(|v| v.to_i64()).collect(); - Some(checked?.into_iter().sum()) - }) - } - - async fn get_connected_peers( - &self, - network_id: NetworkId, - direction: Option<&str>, - ) -> Result> { - let mut map = HashMap::new(); - map.insert("network_id".to_string(), network_id.to_string()); - if let Some(direction) = direction { - map.insert("direction".to_string(), direction.to_string()); - } - self.get_metric_with_fields_i64("aptos_connections", map) - .await - } - - async fn liveness_check(&self, seconds: u64) -> Result<()> { - Ok(self.rest_client().health_check(seconds).await?) - } - - async fn wait_until_healthy(&mut self, deadline: Instant) -> Result<()> { - let mut healthcheck_error = - HealthCheckError::Unknown(anyhow::anyhow!("No healthcheck performed yet")); - while Instant::now() < deadline { - healthcheck_error = match self.health_check().await { - Ok(()) => return Ok(()), - Err(HealthCheckError::NotRunning(error)) => { - return Err(anyhow::anyhow!( - "Node {}:{} not running! Error: {:?}", - self.name(), - self.peer_id(), - error, - )) - }, - Err(e) => e, // For other errors we'll retry - }; - - tokio::time::sleep(Duration::from_millis(500)).await; - } - - Err(anyhow::anyhow!( - "Timed out waiting for Node {}:{} to be healthy: Error: {:?}", - self.name(), - self.peer_id(), - healthcheck_error - )) - } -} diff --git a/testsuite/forge/src/interface/prometheus_metrics.rs b/testsuite/forge/src/interface/prometheus_metrics.rs deleted file mode 100644 index 207b8f47d6045..0000000000000 --- a/testsuite/forge/src/interface/prometheus_metrics.rs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::Swarm; -use prometheus_http_query::response::Sample; -use std::{collections::BTreeMap, fmt}; - -#[derive(Clone)] -pub struct MetricSamples(Vec); - -impl MetricSamples { - pub fn new(samples: Vec) -> Self { - Self(samples) - } - - pub fn max_sample(&self) -> f64 { - self.0 - .iter() - .map(|s| s.value()) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap_or_default() - } - - pub fn avg_sample(&self) -> f64 { - self.0.iter().map(|s| s.value()).sum::() / self.0.len() as f64 - } - - pub fn get(&self) -> &Vec { - &self.0 - } -} - -impl fmt::Debug for MetricSamples { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:?}", - self.0 - .iter() - .map(|s| (s.value(), s.timestamp())) - .collect::>() - ) - } -} - -#[derive(Clone, Debug)] -pub struct SystemMetrics { - pub cpu_core_metrics: MetricSamples, - pub memory_bytes_metrics: MetricSamples, -} - -impl SystemMetrics { - pub fn new(cpu_metrics: Vec, memory_metrics: Vec) -> Self { - Self { - cpu_core_metrics: MetricSamples::new(cpu_metrics), - memory_bytes_metrics: MetricSamples::new(memory_metrics), - } - } -} - -pub async fn fetch_system_metrics( - swarm: &dyn Swarm, - start_time: i64, - end_time: i64, -) -> anyhow::Result { - let cpu_query = r#"avg(rate(container_cpu_usage_seconds_total{container=~"validator"}[30s]))"#; - let memory_query = r#"avg(container_memory_rss{container=~"validator"})"#; - - let cpu_samples = swarm - .query_range_metrics(cpu_query, start_time, end_time, None) - .await?; - - let memory_samples = swarm - .query_range_metrics(memory_query, start_time, end_time, None) - .await?; - - Ok(SystemMetrics::new(cpu_samples, memory_samples)) -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub enum LatencyBreakdownSlice { - QsBatchToPos, - QsPosToProposal, - ConsensusProposalToOrdered, - ConsensusOrderedToCommit, - ConsensusProposalToCommit, -} - -#[derive(Clone, Debug)] -pub struct LatencyBreakdown(BTreeMap); - -impl LatencyBreakdown { - pub fn new(latency: BTreeMap) -> Self { - Self(latency) - } - - pub fn keys(&self) -> Vec { - self.0.keys().cloned().collect() - } - - pub fn get_samples(&self, slice: &LatencyBreakdownSlice) -> &MetricSamples { - self.0 - .get(slice) - .unwrap_or_else(|| panic!("Missing latency breakdown for {:?}", slice)) - } -} - -pub async fn fetch_latency_breakdown( - swarm: &dyn Swarm, - start_time: u64, - end_time: u64, -) -> anyhow::Result { - // Averaging over 1m, and skipping data points at the start that would take averages outside of the interval. - let start_time_adjusted = start_time + 60; - let consensus_proposal_to_ordered_query = r#"quantile(0.67, rate(aptos_consensus_block_tracing_sum{role=~"validator", stage="ordered"}[1m]) / rate(aptos_consensus_block_tracing_count{role=~"validator", stage="ordered"}[1m]))"#; - let consensus_proposal_to_commit_query = r#"quantile(0.67, rate(aptos_consensus_block_tracing_sum{role=~"validator", stage="committed"}[1m]) / rate(aptos_consensus_block_tracing_count{role=~"validator", stage="committed"}[1m]))"#; - - let qs_batch_to_pos_query = r#"sum(rate(quorum_store_batch_to_PoS_duration_sum{role=~"validator"}[1m])) / sum(rate(quorum_store_batch_to_PoS_duration_count{role=~"validator"}[1m]))"#; - let qs_pos_to_proposal_query = r#"sum(rate(quorum_store_pos_to_pull_sum{role=~"validator"}[1m])) / sum(rate(quorum_store_pos_to_pull_count{role=~"validator"}[1m]))"#; - - let consensus_proposal_to_ordered_samples = swarm - .query_range_metrics( - consensus_proposal_to_ordered_query, - start_time_adjusted as i64, - end_time as i64, - None, - ) - .await?; - - let consensus_proposal_to_commit_samples = swarm - .query_range_metrics( - consensus_proposal_to_commit_query, - start_time_adjusted as i64, - end_time as i64, - None, - ) - .await?; - - let consensus_ordered_to_commit_samples = swarm - .query_range_metrics( - &format!( - "{} - {}", - consensus_proposal_to_commit_query, consensus_proposal_to_ordered_query - ), - start_time_adjusted as i64, - end_time as i64, - None, - ) - .await?; - - let qs_batch_to_pos_samples = swarm - .query_range_metrics( - qs_batch_to_pos_query, - start_time_adjusted as i64, - end_time as i64, - None, - ) - .await?; - - let qs_pos_to_proposal_samples = swarm - .query_range_metrics( - qs_pos_to_proposal_query, - start_time_adjusted as i64, - end_time as i64, - None, - ) - .await?; - - let mut samples = BTreeMap::new(); - samples.insert( - LatencyBreakdownSlice::QsBatchToPos, - MetricSamples::new(qs_batch_to_pos_samples), - ); - samples.insert( - LatencyBreakdownSlice::QsPosToProposal, - MetricSamples::new(qs_pos_to_proposal_samples), - ); - samples.insert( - LatencyBreakdownSlice::ConsensusProposalToOrdered, - MetricSamples::new(consensus_proposal_to_ordered_samples), - ); - samples.insert( - LatencyBreakdownSlice::ConsensusOrderedToCommit, - MetricSamples::new(consensus_ordered_to_commit_samples), - ); - samples.insert( - LatencyBreakdownSlice::ConsensusProposalToCommit, - MetricSamples::new(consensus_proposal_to_commit_samples), - ); - - Ok(LatencyBreakdown::new(samples)) -} diff --git a/testsuite/forge/src/interface/swarm.rs b/testsuite/forge/src/interface/swarm.rs deleted file mode 100644 index 5b548cbe6ce20..0000000000000 --- a/testsuite/forge/src/interface/swarm.rs +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - AptosPublicInfo, ChainInfo, FullNode, NodeExt, Result, SwarmChaos, Validator, Version, -}; -use anyhow::{anyhow, bail}; -use aptos_config::{ - config::{NodeConfig, OverrideNodeConfig}, - network_id::NetworkId, -}; -use aptos_logger::info; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::types::PeerId; -use futures::future::{join_all, try_join_all}; -use prometheus_http_query::response::{PromqlResult, Sample}; -use std::time::{Duration, Instant}; -use tokio::runtime::Runtime; - -/// Trait used to represent a running network comprised of Validators and FullNodes -#[async_trait::async_trait] -pub trait Swarm: Sync { - /// Performs a health check on the entire swarm, ensuring all Nodes are Live and that no forks - /// have occurred - async fn health_check(&mut self) -> Result<()>; - - /// Returns an Iterator of references to all the Validators in the Swarm - fn validators<'a>(&'a self) -> Box + 'a>; - - /// Returns an Iterator of mutable references to all the Validators in the Swarm - fn validators_mut<'a>(&'a mut self) -> Box + 'a>; - - /// Returns a reference to the Validator with the provided PeerId - fn validator(&self, id: PeerId) -> Option<&dyn Validator>; - - /// Returns a mutable reference to the Validator with the provided PeerId - fn validator_mut(&mut self, id: PeerId) -> Option<&mut dyn Validator>; - - /// Upgrade a Validator to run specified `Version` - async fn upgrade_validator(&mut self, id: PeerId, version: &Version) -> Result<()>; - - /// Returns an Iterator of references to all the FullNodes in the Swarm - fn full_nodes<'a>(&'a self) -> Box + 'a>; - - /// Returns an Iterator of mutable references to all the FullNodes in the Swarm - fn full_nodes_mut<'a>(&'a mut self) -> Box + 'a>; - - /// Returns a reference to the FullNode with the provided PeerId - fn full_node(&self, id: PeerId) -> Option<&dyn FullNode>; - - /// Returns a mutable reference to the FullNode with the provided PeerId - fn full_node_mut(&mut self, id: PeerId) -> Option<&mut dyn FullNode>; - - /// Adds a Validator to the swarm and returns the PeerId - fn add_validator(&mut self, version: &Version, template: NodeConfig) -> Result; - - /// Removes the Validator with the provided PeerId - fn remove_validator(&mut self, id: PeerId) -> Result<()>; - - fn add_validator_full_node( - &mut self, - version: &Version, - config: OverrideNodeConfig, - id: PeerId, - ) -> Result; - - /// Adds a FullNode to the swarm and returns the PeerId - async fn add_full_node( - &mut self, - version: &Version, - config: OverrideNodeConfig, - ) -> Result; - - /// Removes the FullNode with the provided PeerId - fn remove_full_node(&mut self, id: PeerId) -> Result<()>; - - /// Return a list of supported Versions - fn versions<'a>(&'a self) -> Box + 'a>; - - /// Construct a ChainInfo from this Swarm - fn chain_info(&mut self) -> ChainInfo<'_>; - - fn logs_location(&mut self) -> String; - - /// Injects all types of chaos - async fn inject_chaos(&mut self, chaos: SwarmChaos) -> Result<()>; - async fn remove_chaos(&mut self, chaos: SwarmChaos) -> Result<()>; - async fn remove_all_chaos(&mut self) -> Result<()>; - - async fn ensure_no_validator_restart(&self) -> Result<()>; - async fn ensure_no_fullnode_restart(&self) -> Result<()>; - - // Get prometheus metrics from the swarm - async fn query_metrics( - &self, - query: &str, - time: Option, - timeout: Option, - ) -> Result; - - async fn query_range_metrics( - &self, - query: &str, - start_time: i64, - end_time: i64, - timeout: Option, - ) -> Result>; - - fn aptos_public_info(&mut self) -> AptosPublicInfo<'_> { - self.chain_info().into_aptos_public_info() - } - - fn chain_info_for_node(&mut self, idx: usize) -> ChainInfo<'_>; - - fn aptos_public_info_for_node(&mut self, idx: usize) -> AptosPublicInfo<'_> { - self.chain_info_for_node(idx).into_aptos_public_info() - } - - fn get_default_pfn_node_config(&self) -> NodeConfig; -} - -impl SwarmExt for T where T: Swarm {} - -#[async_trait::async_trait] -pub trait SwarmExt: Swarm { - async fn liveness_check(&self, deadline: Instant) -> Result<()> { - let liveness_check_seconds = 10; - let validators = self.validators().collect::>(); - let full_nodes = self.full_nodes().collect::>(); - - while try_join_all( - validators - .iter() - .map(|node| node.liveness_check(liveness_check_seconds)) - .chain( - full_nodes - .iter() - .map(|node| node.liveness_check(liveness_check_seconds)), - ), - ) - .await - .is_err() - { - if Instant::now() > deadline { - return Err(anyhow!("Swarm liveness check timed out")); - } - - tokio::time::sleep(Duration::from_millis(500)).await; - } - info!("Swarm liveness check passed"); - Ok(()) - } - - /// Waits for the swarm to achieve connectivity - async fn wait_for_connectivity(&self, deadline: Instant) -> Result<()> { - let validators = self.validators().collect::>(); - let full_nodes = self.full_nodes().collect::>(); - - while !try_join_all( - validators - .iter() - .map(|node| node.check_connectivity(NetworkId::Validator, validators.len() - 1)) - .chain(full_nodes.iter().map(|node| node.check_connectivity())), - ) - .await - .map(|v| v.iter().all(|r| *r)) - .unwrap_or(false) - { - if Instant::now() > deadline { - return Err(anyhow!("waiting for swarm connectivity timed out")); - } - - tokio::time::sleep(Duration::from_millis(500)).await; - } - info!("Swarm connectivity check passed"); - Ok(()) - } - - /// Perform a safety check, ensuring that no forks have occurred in the network. - fn fork_check(&self) -> Result<()> { - // Checks if root_hashes are equal across all nodes at a given version - async fn are_root_hashes_equal_at_version( - clients: &[RestClient], - version: u64, - ) -> Result { - let root_hashes = try_join_all( - clients - .iter() - .map(|node| node.get_transaction_by_version(version)) - .collect::>(), - ) - .await? - .into_iter() - .map(|r| { - r.into_inner() - .transaction_info() - .unwrap() - .accumulator_root_hash - }) - .collect::>(); - - Ok(root_hashes.windows(2).all(|w| w[0] == w[1])) - } - - let runtime = Runtime::new().unwrap(); - - let clients = self - .validators() - .map(|node| node.rest_client()) - .chain(self.full_nodes().map(|node| node.rest_client())) - .collect::>(); - - let versions = runtime - .block_on(try_join_all( - clients - .iter() - .map(|node| node.get_ledger_information()) - .collect::>(), - ))? - .into_iter() - .map(|resp| resp.into_inner().version) - .collect::>(); - let min_version = versions - .iter() - .min() - .copied() - .ok_or_else(|| anyhow!("Unable to query nodes for their latest version"))?; - let max_version = versions - .iter() - .max() - .copied() - .ok_or_else(|| anyhow!("Unable to query nodes for their latest version"))?; - - if !runtime.block_on(are_root_hashes_equal_at_version(&clients, min_version))? { - return Err(anyhow!("Fork check failed")); - } - - runtime.block_on( - self.wait_for_all_nodes_to_catchup_to_version(max_version, Duration::from_secs(10)), - )?; - - if !runtime.block_on(are_root_hashes_equal_at_version(&clients, max_version))? { - return Err(anyhow!("Fork check failed")); - } - - Ok(()) - } - - /// Waits for all nodes to have caught up to the specified `target_version`. - async fn wait_for_all_nodes_to_catchup_to_version( - &self, - target_version: u64, - timeout: Duration, - ) -> Result<()> { - wait_for_all_nodes_to_catchup_to_version( - &self.get_all_nodes_clients_with_names(), - target_version, - timeout, - ) - .await - } - - /// Waits for all nodes to have caught up to the specified `target_epoch`. - async fn wait_for_all_nodes_to_catchup_to_epoch( - &self, - target_epoch: u64, - timeout: Duration, - ) -> Result<()> { - wait_for_all_nodes_to_catchup_to_epoch( - &self.get_all_nodes_clients_with_names(), - target_epoch, - timeout, - ) - .await - } - - /// Wait for all nodes in the network to be caught up. This is done by first querying each node - /// for its current version, selects the max version, then waits for all nodes to catch up to - /// that version. Once done, we can guarantee that all transactions committed before invocation - /// of this function are available at all the nodes in the swarm - async fn wait_for_all_nodes_to_catchup(&self, timeout: Duration) -> Result<()> { - wait_for_all_nodes_to_catchup(&self.get_all_nodes_clients_with_names(), timeout).await - } - - /// Wait for all nodes in the network to change epochs. This is done by first querying each node - /// for its current epoch, selecting the max epoch, then waiting for all nodes to sync to max - /// epoch + 1. - async fn wait_for_all_nodes_to_change_epoch(&self, timeout: Duration) -> Result<()> { - let clients = &self.get_all_nodes_clients_with_names(); - if clients.is_empty() { - bail!("No nodes are available!") - } - - let highest_synced_epoch = get_highest_synced_epoch(clients).await?; - wait_for_all_nodes_to_catchup_to_epoch(clients, highest_synced_epoch + 1, timeout).await - } - - async fn wait_for_all_nodes_to_catchup_to_next(&self, timeout: Duration) -> Result<()> { - let clients = self.get_all_nodes_clients_with_names(); - let highest_synced_version = get_highest_synced_version(&clients).await?; - wait_for_all_nodes_to_catchup_to_version(&clients, highest_synced_version + 1, timeout) - .await - } - - fn get_validator_clients_with_names(&self) -> Vec<(String, RestClient)> { - self.validators() - .map(|node| (node.name().to_string(), node.rest_client())) - .collect() - } - - fn get_all_nodes_clients_with_names(&self) -> Vec<(String, RestClient)> { - self.validators() - .map(|node| (node.name().to_string(), node.rest_client())) - .chain( - self.full_nodes() - .map(|node| (node.name().to_string(), node.rest_client())), - ) - .collect() - } - - fn get_clients_for_peers(&self, peers: &[PeerId], client_timeout: Duration) -> Vec { - peers - .iter() - .map(|peer| { - self.validator(*peer) - .map(|n| n.rest_client_with_timeout(client_timeout)) - .unwrap_or_else(|| { - self.full_node(*peer) - .unwrap() - .rest_client_with_timeout(client_timeout) - }) - }) - .collect() - } - - async fn get_client_with_newest_ledger_version(&self) -> Option<(u64, RestClient)> { - let clients = self.get_all_nodes_clients_with_names(); - let ledger_infos = join_all(clients.iter().map(|(_name, client)| async { - let start = Instant::now(); - let result = client.get_ledger_information().await; - - info!( - "Fetch from {:?} took {}ms, at version: {}", - client.path_prefix_string(), - start.elapsed().as_millis(), - result - .as_ref() - .map(|r| r.inner().version as i64) - .unwrap_or(-1) - ); - result - })) - .await; - ledger_infos - .into_iter() - .zip(clients) - .flat_map(|(resp, (_, client))| resp.map(|r| (r.into_inner().version, client))) - .max_by_key(|(v, _c)| *v) - } -} - -/// Waits for all nodes to have caught up to the specified `target_version`. -pub async fn wait_for_all_nodes_to_catchup_to_version( - clients: &[(String, RestClient)], - target_version: u64, - timeout: Duration, -) -> Result<()> { - wait_for_all_nodes_to_catchup_to_target_version_or_epoch( - clients, - Some(target_version), - None, - timeout, - ) - .await -} - -/// Waits for all nodes to have caught up to the specified `target_epoch`. -pub async fn wait_for_all_nodes_to_catchup_to_epoch( - clients: &[(String, RestClient)], - target_epoch: u64, - timeout: Duration, -) -> Result<()> { - wait_for_all_nodes_to_catchup_to_target_version_or_epoch( - clients, - None, - Some(target_epoch), - timeout, - ) - .await -} - -/// Waits for all nodes to have caught up to the specified `target_version` or `target_epoch`. -async fn wait_for_all_nodes_to_catchup_to_target_version_or_epoch( - clients: &[(String, RestClient)], - target_version: Option, - target_epoch: Option, - timeout: Duration, -) -> Result<()> { - if target_version.is_none() && target_epoch.is_none() { - bail!("No target version or epoch was specified!") - } - - let start_time = Instant::now(); - loop { - // Fetch the current versions and epochs of all nodes - let version_and_epoch_results: Result> = - try_join_all(clients.iter().map(|(node_name, node)| async move { - let node_ledger_info_response = node.get_ledger_information().await?.into_inner(); - Ok(( - node_name, - node_ledger_info_response.version, - node_ledger_info_response.epoch, - )) - })) - .await; - let node_versions_and_epochs = - version_and_epoch_results.map(|results| results.into_iter().collect::>()); - - // Check if all nodes are caught up to the target version - let all_caught_up_to_version = target_version - .map(|target_version| { - node_versions_and_epochs - .as_ref() - .map(|responses| { - responses - .iter() - .all(|(_, version, _)| *version >= target_version) - }) - .unwrap_or(false) // No version found - }) - .unwrap_or(true); // No target version was specified - - // Check if all nodes are caught up to the target epoch - let all_caught_up_to_epoch = target_epoch - .map(|target_epoch| { - node_versions_and_epochs - .as_ref() - .map(|responses| responses.iter().all(|(_, _, epoch)| *epoch >= target_epoch)) - .unwrap_or(false) // No epoch found - }) - .unwrap_or(true); // No target epoch was specified - - // Check if all targets have been met - if all_caught_up_to_version && all_caught_up_to_epoch { - info!( - "All nodes caught up to target version and epoch ({:?}, {:?}) successfully, in {} seconds", - target_version, - target_epoch, - start_time.elapsed().as_secs() - ); - return Ok(()); - } - - // Check if we've timed out while waiting - if start_time.elapsed() > timeout { - return Err(anyhow!( - "Waiting for nodes to catch up to target version and epoch ({:?}, {:?}) timed out after {} seconds, current status: {:?}", - target_version, - target_epoch, - start_time.elapsed().as_secs(), - node_versions_and_epochs - )); - } - - tokio::time::sleep(Duration::from_millis(500)).await; - } -} - -/// Wait for all nodes in the network to be caught up. This is done by first querying each node -/// for its current version, selects the max version, then waits for all nodes to catch up to -/// that version. Once done, we can guarantee that all transactions committed before invocation -/// of this function are available at all the nodes in the swarm -pub async fn wait_for_all_nodes_to_catchup( - clients: &[(String, RestClient)], - timeout: Duration, -) -> Result<()> { - if clients.is_empty() { - bail!("No nodes are available!") - } - let highest_synced_version = get_highest_synced_version(clients).await?; - wait_for_all_nodes_to_catchup_to_version(clients, highest_synced_version, timeout).await -} - -/// Returns the highest synced version of the given clients -pub async fn get_highest_synced_version(clients: &[(String, RestClient)]) -> Result { - let (highest_synced_version, _) = get_highest_synced_version_and_epoch(clients).await?; - Ok(highest_synced_version) -} - -/// Returns the highest synced epoch of the given clients -pub async fn get_highest_synced_epoch(clients: &[(String, RestClient)]) -> Result { - let (_, highest_synced_epoch) = get_highest_synced_version_and_epoch(clients).await?; - Ok(highest_synced_epoch) -} - -/// Returns the highest synced version and epoch of the given clients -pub async fn get_highest_synced_version_and_epoch( - clients: &[(String, RestClient)], -) -> Result<(u64, u64)> { - let mut latest_version_and_epoch = (0, 0); - for (_, client) in clients { - latest_version_and_epoch = latest_version_and_epoch.max( - client - .get_ledger_information() - .await - .map(|r| (r.inner().version, r.inner().epoch)) - .unwrap_or((0, 0)), - ); - } - Ok(latest_version_and_epoch) -} diff --git a/testsuite/forge/src/interface/test.rs b/testsuite/forge/src/interface/test.rs deleted file mode 100644 index 72c78e6a64514..0000000000000 --- a/testsuite/forge/src/interface/test.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use rand::SeedableRng; - -/// Whether a test is expected to fail or not -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum ShouldFail { - No, - Yes, - YesWithMessage(&'static str), -} - -/// Represents a Test in Forge -/// -/// This is meant to be a super trait of the other test interfaces. -pub trait Test: Send + Sync { - /// Returns the name of the Test - fn name(&self) -> &'static str; - - /// Indicates if the Test should be ignored - fn ignored(&self) -> bool { - false - } - - /// Indicates if the Test should fail - fn should_fail(&self) -> ShouldFail { - ShouldFail::No - } -} - -impl Test for &T { - fn name(&self) -> &'static str { - (**self).name() - } - - fn ignored(&self) -> bool { - (**self).ignored() - } - - fn should_fail(&self) -> ShouldFail { - (**self).should_fail() - } -} - -#[derive(Debug)] -pub struct CoreContext { - rng: ::rand::rngs::StdRng, -} - -impl CoreContext { - pub fn new(rng: ::rand::rngs::StdRng) -> Self { - Self { rng } - } - - pub fn from_rng(rng: R) -> Self { - Self { - rng: ::rand::rngs::StdRng::from_rng(rng).unwrap(), - } - } - - pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng { - &mut self.rng - } -} diff --git a/testsuite/forge/src/lib.rs b/testsuite/forge/src/lib.rs deleted file mode 100644 index bdd8ec3cc6eeb..0000000000000 --- a/testsuite/forge/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! Forge is a framework for writing and running end-to-end tests in Aptos - -pub use anyhow::Result; - -mod interface; -pub use interface::*; - -mod runner; -pub use runner::*; - -mod backend; -pub use aptos_transaction_emitter_lib::*; -pub use aptos_transaction_generator_lib::*; -pub use backend::*; - -mod report; -pub use report::*; - -mod github; -pub use github::*; - -mod slack; -pub use slack::*; - -pub mod success_criteria; - -pub mod test_utils; diff --git a/testsuite/forge/src/report.rs b/testsuite/forge/src/report.rs deleted file mode 100644 index aabaa849a09ec..0000000000000 --- a/testsuite/forge/src/report.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_logger::info; -use aptos_transaction_emitter_lib::emitter::stats::TxnStats; -use serde::Serialize; -use std::fmt; - -#[derive(Default, Debug, Serialize)] -pub struct TestReport { - metrics: Vec, - text: String, -} - -#[derive(Debug, Serialize)] -pub struct ReportedMetric { - pub test_name: String, - pub metric: String, - pub value: f64, -} - -impl TestReport { - pub fn new() -> Self { - Default::default() - } - - pub fn report_metric(&mut self, test: E, metric: M, value: f64) { - self.metrics.push(ReportedMetric { - test_name: test.to_string(), - metric: metric.to_string(), - value, - }); - } - - pub fn report_text(&mut self, text: String) { - if !self.text.is_empty() { - self.text.push('\n'); - } - self.text.push_str(&text); - info!("{}", text); - } - - pub fn report_txn_stats(&mut self, test_name: String, stats: &TxnStats) { - let rate = stats.rate(); - self.report_metric(test_name.clone(), "submitted_txn", stats.submitted as f64); - self.report_metric(test_name.clone(), "expired_txn", stats.expired as f64); - self.report_metric(test_name.clone(), "avg_tps", rate.committed as f64); - self.report_metric(test_name.clone(), "avg_latency", rate.latency as f64); - self.report_metric(test_name.clone(), "p50_latency", rate.p50_latency as f64); - self.report_metric(test_name.clone(), "p90_latency", rate.p90_latency as f64); - self.report_metric(test_name.clone(), "p99_latency", rate.p99_latency as f64); - self.report_text(format!("{} : {}", test_name, rate)); - } - - pub fn print_report(&self) { - println!("Test Statistics: "); - println!("{}", self); - let json_report = - serde_json::to_string_pretty(&self).expect("Failed to serialize report to json"); - println!( - "\n====json-report-begin===\n{}\n====json-report-end===", - json_report - ); - } -} - -impl fmt::Display for TestReport { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.text) - } -} diff --git a/testsuite/forge/src/runner.rs b/testsuite/forge/src/runner.rs deleted file mode 100644 index f4b15c9facdac..0000000000000 --- a/testsuite/forge/src/runner.rs +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// TODO going to remove random seed once cluster deployment supports re-run genesis -use crate::{ - success_criteria::{MetricsThreshold, SuccessCriteria, SystemMetricsThreshold}, - *, -}; -use anyhow::{bail, format_err, Error, Result}; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_framework::ReleaseBundle; -use clap::{Parser, ValueEnum}; -use rand::{rngs::OsRng, Rng, SeedableRng}; -use std::{ - fmt::{Display, Formatter}, - io::{self, Write}, - num::NonZeroUsize, - process, - str::FromStr, - sync::Arc, - time::Duration, -}; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use tokio::runtime::Runtime; - -const KUBERNETES_SERVICE_HOST: &str = "KUBERNETES_SERVICE_HOST"; -pub const FORGE_RUNNER_MODE: &str = "FORGE_RUNNER_MODE"; - -#[derive(Debug, Parser)] -#[clap(about = "Forged in Fire", styles = aptos_cli_common::aptos_cli_style())] -pub struct Options { - /// The FILTER string is tested against the name of all tests, and only those tests whose names - /// contain the filter are run. - filter: Option, - #[clap(long = "exact")] - /// Exactly match filters rather than by substring - filter_exact: bool, - #[allow(dead_code)] - #[clap(long, default_value = "1", env = "RUST_TEST_THREADS")] - /// NO-OP: unsupported option, exists for compatibility with the default test harness - /// Number of threads used for running tests in parallel - test_threads: NonZeroUsize, - #[allow(dead_code)] - #[clap(short = 'q', long)] - /// NO-OP: unsupported option, exists for compatibility with the default test harness - quiet: bool, - #[allow(dead_code)] - #[clap(long)] - /// NO-OP: unsupported option, exists for compatibility with the default test harness - nocapture: bool, - #[clap(long)] - /// List all tests - pub list: bool, - #[clap(long)] - /// List or run ignored tests - ignored: bool, - #[clap(long)] - /// Include ignored tests when listing or running tests - include_ignored: bool, - /// Configure formatting of output: - /// pretty = Print verbose output; - /// terse = Display one character per test; - /// (json is unsupported, exists for compatibility with the default test harness) - #[clap(long, value_enum, ignore_case = true, default_value_t = Format::Pretty)] - format: Format, - #[allow(dead_code)] - #[clap(short = 'Z')] - /// NO-OP: unsupported option, exists for compatibility with the default test harness - /// -Z unstable-options Enable nightly-only flags: - /// unstable-options = Allow use of experimental features - z_unstable_options: Option, - #[allow(dead_code)] - #[clap(long)] - /// NO-OP: unsupported option, exists for compatibility with the default test harness - /// Show captured stdout of successful tests - show_output: bool, -} - -impl Options { - pub fn parse() -> Self { - Parser::parse() - } -} - -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ValueEnum)] -pub enum Format { - #[default] - Pretty, - Terse, - Json, -} - -pub fn forge_main(tests: ForgeConfig, factory: F, options: &Options) -> Result<()> { - let forge = Forge::new(options, tests, Duration::from_secs(30), factory); - - if options.list { - forge.list()?; - - return Ok(()); - } - - match forge.run() { - Ok(..) => Ok(()), - Err(e) => { - eprintln!("Failed to run tests:\n{}", e); - process::exit(101); // Exit with a non-zero exit code if tests failed - }, - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum InitialVersion { - Oldest, - Newest, -} - -pub type NodeConfigFn = Arc; -pub type GenesisConfigFn = Arc; -/// override_config, base_config (see OverrideNodeConfig) -pub type OverrideNodeConfigFn = Arc; - -#[derive(Clone, Copy, Default)] -pub struct NodeResourceOverride { - pub cpu_cores: Option, - pub memory_gib: Option, -} - -pub struct ForgeConfig { - aptos_tests: Vec>, - admin_tests: Vec>, - network_tests: Vec>, - - /// The initial number of validators to spawn when the test harness creates a swarm - initial_validator_count: NonZeroUsize, - - /// The initial number of fullnodes to spawn when the test harness creates a swarm - initial_fullnode_count: usize, - - /// The initial version to use when the test harness creates a swarm - initial_version: InitialVersion, - - /// The initial genesis modules to use when starting a network - genesis_config: Option, - - /// Optional genesis helm values init function - genesis_helm_config_fn: Option, - - /// Optional validator node config override function - validator_override_node_config_fn: Option, - - /// Optional fullnode node config override function - fullnode_override_node_config_fn: Option, - - multi_region_config: bool, - - /// Transaction workload to run on the swarm - emit_job_request: EmitJobRequest, - - /// Success criteria - success_criteria: SuccessCriteria, - - /// The label of existing DBs to use, if None, will create new db. - existing_db_tag: Option, - - validator_resource_override: NodeResourceOverride, - - fullnode_resource_override: NodeResourceOverride, -} - -impl ForgeConfig { - pub fn new() -> Self { - Self::default() - } - - pub fn add_aptos_test(mut self, aptos_test: T) -> Self { - self.aptos_tests.push(Box::new(aptos_test)); - self - } - - pub fn with_aptos_tests(mut self, aptos_tests: Vec>) -> Self { - self.aptos_tests = aptos_tests; - self - } - - pub fn add_admin_test(mut self, admin_test: T) -> Self { - self.admin_tests.push(Box::new(admin_test)); - self - } - - pub fn with_admin_tests(mut self, admin_tests: Vec>) -> Self { - self.admin_tests = admin_tests; - self - } - - pub fn add_network_test(mut self, network_test: T) -> Self { - self.network_tests.push(Box::new(network_test)); - self - } - - pub fn with_network_tests(mut self, network_tests: Vec>) -> Self { - self.network_tests = network_tests; - self - } - - pub fn with_initial_validator_count(mut self, initial_validator_count: NonZeroUsize) -> Self { - self.initial_validator_count = initial_validator_count; - self - } - - pub fn with_initial_fullnode_count(mut self, initial_fullnode_count: usize) -> Self { - self.initial_fullnode_count = initial_fullnode_count; - self - } - - pub fn with_genesis_helm_config_fn(mut self, genesis_helm_config_fn: GenesisConfigFn) -> Self { - self.genesis_helm_config_fn = Some(genesis_helm_config_fn); - self - } - - pub fn with_validator_override_node_config_fn(mut self, f: OverrideNodeConfigFn) -> Self { - self.validator_override_node_config_fn = Some(f); - self - } - - pub fn with_fullnode_override_node_config_fn(mut self, f: OverrideNodeConfigFn) -> Self { - self.fullnode_override_node_config_fn = Some(f); - self - } - - pub fn with_multi_region_config(mut self) -> Self { - self.multi_region_config = true; - self - } - - pub fn with_validator_resource_override( - mut self, - resource_override: NodeResourceOverride, - ) -> Self { - self.validator_resource_override = resource_override; - self - } - - pub fn with_fullnode_resource_override( - mut self, - resource_override: NodeResourceOverride, - ) -> Self { - self.fullnode_resource_override = resource_override; - self - } - - fn override_node_config_from_fn(config_fn: OverrideNodeConfigFn) -> OverrideNodeConfig { - let mut override_config = NodeConfig::default(); - let mut base_config = NodeConfig::default(); - config_fn(&mut override_config, &mut base_config); - OverrideNodeConfig::new(override_config, base_config) - } - - pub fn build_node_helm_config_fn(&self) -> Option { - let validator_override_node_config = self - .validator_override_node_config_fn - .clone() - .map(|config_fn| Self::override_node_config_from_fn(config_fn)); - let fullnode_override_node_config = self - .fullnode_override_node_config_fn - .clone() - .map(|config_fn| Self::override_node_config_from_fn(config_fn)); - let multi_region_config = self.multi_region_config; - let existing_db_tag = self.existing_db_tag.clone(); - let validator_resource_override = self.validator_resource_override; - let fullnode_resource_override = self.fullnode_resource_override; - - Some(Arc::new(move |helm_values: &mut serde_yaml::Value| { - if let Some(override_config) = &validator_override_node_config { - helm_values["validator"]["config"] = override_config.get_yaml().unwrap(); - } - if let Some(override_config) = &fullnode_override_node_config { - helm_values["fullnode"]["config"] = override_config.get_yaml().unwrap(); - } - if multi_region_config { - helm_values["multicluster"]["enabled"] = true.into(); - // Create headless services for validators and fullnodes. - // Note: chaos-mesh will not work with clusterIP services. - helm_values["service"]["validator"]["internal"]["type"] = "ClusterIP".into(); - helm_values["service"]["validator"]["internal"]["headless"] = true.into(); - helm_values["service"]["fullnode"]["internal"]["type"] = "ClusterIP".into(); - helm_values["service"]["fullnode"]["internal"]["headless"] = true.into(); - } - if let Some(existing_db_tag) = &existing_db_tag { - helm_values["validator"]["storage"]["labels"]["tag"] = - existing_db_tag.clone().into(); - helm_values["fullnode"]["storage"]["labels"]["tag"] = - existing_db_tag.clone().into(); - } - - // validator resource overrides - if let Some(cpu_cores) = validator_resource_override.cpu_cores { - helm_values["validator"]["resources"]["requests"]["cpu"] = cpu_cores.into(); - helm_values["validator"]["resources"]["limits"]["cpu"] = cpu_cores.into(); - } - if let Some(memory_gib) = validator_resource_override.memory_gib { - helm_values["validator"]["resources"]["requests"]["memory"] = - format!("{}Gi", memory_gib).into(); - helm_values["validator"]["resources"]["limits"]["memory"] = - format!("{}Gi", memory_gib).into(); - } - // fullnode resource overrides - if let Some(cpu_cores) = fullnode_resource_override.cpu_cores { - helm_values["fullnode"]["resources"]["requests"]["cpu"] = cpu_cores.into(); - helm_values["fullnode"]["resources"]["limits"]["cpu"] = cpu_cores.into(); - } - if let Some(memory_gib) = fullnode_resource_override.memory_gib { - helm_values["fullnode"]["resources"]["requests"]["memory"] = - format!("{}Gi", memory_gib).into(); - helm_values["fullnode"]["resources"]["limits"]["memory"] = - format!("{}Gi", memory_gib).into(); - } - })) - } - - pub fn with_initial_version(mut self, initial_version: InitialVersion) -> Self { - self.initial_version = initial_version; - self - } - - pub fn with_genesis_module_bundle(mut self, bundle: ReleaseBundle) -> Self { - self.genesis_config = Some(GenesisConfig::Bundle(bundle)); - self - } - - pub fn with_genesis_modules_path(mut self, genesis_modules: String) -> Self { - self.genesis_config = Some(GenesisConfig::Path(genesis_modules)); - self - } - - pub fn with_emit_job(mut self, emit_job_request: EmitJobRequest) -> Self { - self.emit_job_request = emit_job_request; - self - } - - pub fn get_emit_job(&self) -> &EmitJobRequest { - &self.emit_job_request - } - - pub fn with_success_criteria(mut self, success_criteria: SuccessCriteria) -> Self { - self.success_criteria = success_criteria; - self - } - - pub fn get_success_criteria_mut(&mut self) -> &mut SuccessCriteria { - &mut self.success_criteria - } - - pub fn with_existing_db(mut self, tag: String) -> Self { - self.existing_db_tag = Some(tag); - self - } - - pub fn number_of_tests(&self) -> usize { - self.admin_tests.len() + self.network_tests.len() + self.aptos_tests.len() - } - - pub fn all_tests(&self) -> Vec>> { - self.admin_tests - .iter() - .map(|t| Box::new(AnyTestRef::Admin(t.as_ref()))) - .chain( - self.network_tests - .iter() - .map(|t| Box::new(AnyTestRef::Network(t.as_ref()))), - ) - .chain( - self.aptos_tests - .iter() - .map(|t| Box::new(AnyTestRef::Aptos(t.as_ref()))), - ) - .collect() - } -} - -// Workaround way to implement all_tests, for: -// error[E0658]: cannot cast `dyn interface::admin::AdminTest` to `dyn interface::test::Test`, trait upcasting coercion is experimental -pub enum AnyTestRef<'a> { - Aptos(&'a dyn AptosTest), - Admin(&'a dyn AdminTest), - Network(&'a dyn NetworkTest), -} - -impl<'a> Test for AnyTestRef<'a> { - fn name(&self) -> &'static str { - match self { - AnyTestRef::Aptos(t) => t.name(), - AnyTestRef::Admin(t) => t.name(), - AnyTestRef::Network(t) => t.name(), - } - } - - fn ignored(&self) -> bool { - match self { - AnyTestRef::Aptos(t) => t.ignored(), - AnyTestRef::Admin(t) => t.ignored(), - AnyTestRef::Network(t) => t.ignored(), - } - } - - fn should_fail(&self) -> ShouldFail { - match self { - AnyTestRef::Aptos(t) => t.should_fail(), - AnyTestRef::Admin(t) => t.should_fail(), - AnyTestRef::Network(t) => t.should_fail(), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ForgeRunnerMode { - Local, - K8s, -} - -impl FromStr for ForgeRunnerMode { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s { - "local" => Ok(ForgeRunnerMode::Local), - "k8s" => Ok(ForgeRunnerMode::K8s), - _ => Err(format_err!("Invalid runner mode: {}", s)), - } - } -} - -impl ForgeRunnerMode { - pub fn try_from_env() -> Result { - if let Ok(runner_mode) = std::env::var(FORGE_RUNNER_MODE) { - Ok(ForgeRunnerMode::from_str(&runner_mode)?) - } else if std::env::var(KUBERNETES_SERVICE_HOST).is_ok() { - Ok(ForgeRunnerMode::K8s) - } else { - Ok(ForgeRunnerMode::Local) - } - } -} - -impl Default for ForgeConfig { - fn default() -> Self { - let forge_run_mode = ForgeRunnerMode::try_from_env().unwrap_or(ForgeRunnerMode::K8s); - let success_criteria = if forge_run_mode == ForgeRunnerMode::Local { - SuccessCriteria::new(600).add_no_restarts() - } else { - SuccessCriteria::new(3500) - .add_no_restarts() - .add_system_metrics_threshold(SystemMetricsThreshold::new( - // Check that we don't use more than 12 CPU cores for 30% of the time. - MetricsThreshold::new(12.0, 30), - // Check that we don't use more than 10 GB of memory for 30% of the time. - MetricsThreshold::new_gb(10.0, 30), - )) - }; - Self { - aptos_tests: vec![], - admin_tests: vec![], - network_tests: vec![], - initial_validator_count: NonZeroUsize::new(1).unwrap(), - initial_fullnode_count: 0, - initial_version: InitialVersion::Oldest, - genesis_config: None, - genesis_helm_config_fn: None, - validator_override_node_config_fn: None, - fullnode_override_node_config_fn: None, - multi_region_config: false, - emit_job_request: EmitJobRequest::default().mode(EmitJobMode::MaxLoad { - mempool_backlog: 40000, - }), - success_criteria, - existing_db_tag: None, - validator_resource_override: NodeResourceOverride::default(), - fullnode_resource_override: NodeResourceOverride::default(), - } - } -} - -pub struct Forge<'cfg, F> { - options: &'cfg Options, - tests: ForgeConfig, - global_duration: Duration, - factory: F, -} - -impl<'cfg, F: Factory> Forge<'cfg, F> { - pub fn new( - options: &'cfg Options, - tests: ForgeConfig, - global_duration: Duration, - factory: F, - ) -> Self { - Self { - options, - tests, - global_duration, - factory, - } - } - - pub fn list(&self) -> Result<()> { - for test in self.filter_tests(&self.tests.all_tests()) { - println!("{}: test", test.name()); - } - - if self.options.format == Format::Pretty { - println!(); - println!( - "{} tests", - self.filter_tests(&self.tests.all_tests()).count() - ); - } - - Ok(()) - } - - /// Get the initial version based on test configuration - pub fn initial_version(&self) -> Version { - let versions = self.factory.versions(); - match self.tests.initial_version { - InitialVersion::Oldest => versions.min(), - InitialVersion::Newest => versions.max(), - } - .expect("There has to be at least 1 version") - } - - pub fn run(&self) -> Result { - let test_count = self.filter_tests(&self.tests.all_tests()).count(); - let filtered_out = test_count.saturating_sub(self.tests.all_tests().len()); - - let mut report = TestReport::new(); - let mut summary = TestSummary::new(test_count, filtered_out); - summary.write_starting_msg()?; - - if test_count > 0 { - println!( - "Starting Swarm with supported versions: {:?}", - self.factory - .versions() - .map(|v| v.to_string()) - .collect::>() - ); - let initial_version = self.initial_version(); - // The genesis version should always match the initial node version - let genesis_version = initial_version.clone(); - let runtime = Runtime::new().unwrap(); - let mut rng = ::rand::rngs::StdRng::from_seed(OsRng.gen()); - let mut swarm = runtime.block_on(self.factory.launch_swarm( - &mut rng, - self.tests.initial_validator_count, - self.tests.initial_fullnode_count, - &initial_version, - &genesis_version, - self.tests.genesis_config.as_ref(), - self.global_duration + Duration::from_secs(NAMESPACE_CLEANUP_DURATION_BUFFER_SECS), - self.tests.genesis_helm_config_fn.clone(), - self.tests.build_node_helm_config_fn(), - self.tests.existing_db_tag.clone(), - ))?; - - // Run AptosTests - for test in self.filter_tests(&self.tests.aptos_tests) { - let mut aptos_ctx = AptosContext::new( - CoreContext::from_rng(&mut rng), - swarm.chain_info().into_aptos_public_info(), - &mut report, - ); - let result = run_test(|| runtime.block_on(test.run(&mut aptos_ctx))); - report.report_text(result.to_string()); - summary.handle_result(test.name().to_owned(), result)?; - } - - // Run AdminTests - for test in self.filter_tests(&self.tests.admin_tests) { - let mut admin_ctx = AdminContext::new( - CoreContext::from_rng(&mut rng), - swarm.chain_info(), - &mut report, - ); - let result = run_test(|| test.run(&mut admin_ctx)); - report.report_text(result.to_string()); - summary.handle_result(test.name().to_owned(), result)?; - } - - for test in self.filter_tests(&self.tests.network_tests) { - let mut network_ctx = NetworkContext::new( - CoreContext::from_rng(&mut rng), - &mut *swarm, - &mut report, - self.global_duration, - self.tests.emit_job_request.clone(), - self.tests.success_criteria.clone(), - ); - let result = run_test(|| test.run(&mut network_ctx)); - report.report_text(result.to_string()); - summary.handle_result(test.name().to_owned(), result)?; - } - - report.print_report(); - - io::stdout().flush()?; - io::stderr().flush()?; - if !summary.success() { - println!(); - println!("Swarm logs can be found here: {}", swarm.logs_location()); - } - } - - summary.write_summary()?; - - if summary.success() { - Ok(report) - } else { - bail!("Tests Failed") - } - } - - fn filter_tests<'a, T: Test + ?Sized>( - &'a self, - tests: &'a [Box], - ) -> impl Iterator> { - tests - .iter() - // Filter by ignored - .filter( - move |test| match (self.options.include_ignored, self.options.ignored) { - (true, _) => true, // Don't filter anything - (false, true) => test.ignored(), - (false, false) => !test.ignored(), - }, - ) - // Filter by test name - .filter(move |test| { - if let Some(filter) = &self.options.filter { - if self.options.filter_exact { - test.name() == &filter[..] - } else { - test.name().contains(&filter[..]) - } - } else { - true - } - }) - } -} - -enum TestResult { - Ok, - FailedWithMsg(String), -} - -impl Display for TestResult { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - match self { - TestResult::Ok => write!(f, "Test Ok"), - TestResult::FailedWithMsg(msg) => write!(f, "Test Failed: {}", msg), - } - } -} - -fn run_test Result<()>>(f: F) -> TestResult { - match f() { - Ok(()) => TestResult::Ok, - Err(e) => { - let is_triggerd_by_github_actions = - std::env::var("FORGE_TRIGGERED_BY").unwrap_or_default() == "github-actions"; - if is_triggerd_by_github_actions { - // ::error:: is github specific syntax to set an error on the job that is highlighted as described here https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message - println!("::error::{:?}", e); - } - TestResult::FailedWithMsg(format!("{:?}", e)) - }, - } -} - -struct TestSummary { - stdout: StandardStream, - total: usize, - filtered_out: usize, - passed: usize, - failed: Vec, -} - -impl TestSummary { - fn new(total: usize, filtered_out: usize) -> Self { - Self { - stdout: StandardStream::stdout(ColorChoice::Auto), - total, - filtered_out, - passed: 0, - failed: Vec::new(), - } - } - - fn handle_result(&mut self, name: String, result: TestResult) -> io::Result<()> { - write!(self.stdout, "test {} ... ", name)?; - match result { - TestResult::Ok => { - self.passed += 1; - self.write_ok()?; - }, - TestResult::FailedWithMsg(msg) => { - self.failed.push(name); - self.write_failed()?; - writeln!(self.stdout)?; - - write!(self.stdout, "Error: {}", msg)?; - }, - } - writeln!(self.stdout)?; - Ok(()) - } - - fn write_ok(&mut self) -> io::Result<()> { - self.stdout - .set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; - write!(self.stdout, "ok")?; - self.stdout.reset()?; - Ok(()) - } - - fn write_failed(&mut self) -> io::Result<()> { - self.stdout - .set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - write!(self.stdout, "FAILED")?; - self.stdout.reset()?; - Ok(()) - } - - fn write_starting_msg(&mut self) -> io::Result<()> { - writeln!(self.stdout)?; - writeln!( - self.stdout, - "running {} tests", - self.total - self.filtered_out - )?; - Ok(()) - } - - fn write_summary(&mut self) -> io::Result<()> { - // Print out the failing tests - if !self.failed.is_empty() { - writeln!(self.stdout)?; - writeln!(self.stdout, "failures:")?; - for name in &self.failed { - writeln!(self.stdout, " {}", name)?; - } - } - - writeln!(self.stdout)?; - write!(self.stdout, "test result: ")?; - if self.failed.is_empty() { - self.write_ok()?; - } else { - self.write_failed()?; - } - writeln!( - self.stdout, - ". {} passed; {} failed; {} filtered out", - self.passed, - self.failed.len(), - self.filtered_out - )?; - writeln!(self.stdout)?; - Ok(()) - } - - fn success(&self) -> bool { - self.failed.is_empty() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_forge_runner_mode_from_env() { - // HACK we really should not be setting env variables in test - - // Store the env variables before we mutate them - let original_forge_runner_mode = std::env::var(FORGE_RUNNER_MODE); - let original_kubernetes_service_host = std::env::var(KUBERNETES_SERVICE_HOST); - - // Test the default locally - std::env::remove_var(FORGE_RUNNER_MODE); - std::env::remove_var(KUBERNETES_SERVICE_HOST); - let default_local_runner_mode = ForgeRunnerMode::try_from_env(); - - std::env::remove_var(FORGE_RUNNER_MODE); - std::env::set_var(KUBERNETES_SERVICE_HOST, "1.1.1.1"); - let default_kubernetes_runner_mode = ForgeRunnerMode::try_from_env(); - - std::env::set_var(FORGE_RUNNER_MODE, "local"); - std::env::set_var(KUBERNETES_SERVICE_HOST, "1.1.1.1"); - let local_runner_mode = ForgeRunnerMode::try_from_env(); - - std::env::set_var(FORGE_RUNNER_MODE, "k8s"); - std::env::remove_var(KUBERNETES_SERVICE_HOST); - let k8s_runner_mode = ForgeRunnerMode::try_from_env(); - - std::env::set_var(FORGE_RUNNER_MODE, "durian"); - std::env::remove_var(KUBERNETES_SERVICE_HOST); - let invalid_runner_mode = ForgeRunnerMode::try_from_env(); - - // Reset the env variables after running - match original_forge_runner_mode { - Ok(mode) => std::env::set_var(FORGE_RUNNER_MODE, mode), - Err(_) => std::env::remove_var(FORGE_RUNNER_MODE), - } - match original_kubernetes_service_host { - Ok(service_host) => std::env::set_var(KUBERNETES_SERVICE_HOST, service_host), - Err(_) => std::env::remove_var(KUBERNETES_SERVICE_HOST), - } - - assert_eq!(default_local_runner_mode.unwrap(), ForgeRunnerMode::Local); - assert_eq!( - default_kubernetes_runner_mode.unwrap(), - ForgeRunnerMode::K8s - ); - assert_eq!(local_runner_mode.unwrap(), ForgeRunnerMode::Local); - assert_eq!(k8s_runner_mode.unwrap(), ForgeRunnerMode::K8s); - assert_eq!( - invalid_runner_mode.unwrap_err().to_string(), - "Invalid runner mode: durian" - ); - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Options::command().debug_assert() -} diff --git a/testsuite/forge/src/slack.rs b/testsuite/forge/src/slack.rs deleted file mode 100644 index 45b97dfc764b3..0000000000000 --- a/testsuite/forge/src/slack.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use anyhow::{bail, format_err, Result}; -use reqwest::{self, Url}; -use serde_json::{self, json}; - -pub struct SlackClient { - client: reqwest::blocking::Client, -} - -impl SlackClient { - pub fn new() -> Self { - let client = reqwest::blocking::Client::new(); - Self { client } - } - - pub fn send_message(&self, url: &Url, msg: &str) -> Result<()> { - let msg = json!({ "text": msg }); - let msg = serde_json::to_string(&msg) - .map_err(|e| format_err!("Failed to serialize message for slack: {:?}", e))?; - let request = self.client.post(url.clone()).body(msg); - let response = request - .send() - .map_err(|e| format_err!("Failed to send slack message: {:?}", e))?; - if !response.status().is_success() { - bail!("Slack service returned error code: {}", response.status()) - } - Ok(()) - } -} - -impl Default for SlackClient { - fn default() -> Self { - Self::new() - } -} diff --git a/testsuite/forge/src/success_criteria.rs b/testsuite/forge/src/success_criteria.rs deleted file mode 100644 index 690f0de4c8b15..0000000000000 --- a/testsuite/forge/src/success_criteria.rs +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - prometheus_metrics::{ - fetch_system_metrics, LatencyBreakdown, LatencyBreakdownSlice, SystemMetrics, - }, - Swarm, SwarmExt, TestReport, -}; -use anyhow::{bail, Context}; -use aptos::node::analyze::fetch_metadata::FetchMetadata; -use aptos_sdk::types::PeerId; -use aptos_transaction_emitter_lib::{TxnStats, TxnStatsRate}; -use prometheus_http_query::response::Sample; -use std::{collections::BTreeMap, time::Duration}; - -#[derive(Clone, Debug)] -pub struct StateProgressThreshold { - pub max_no_progress_secs: f32, - pub max_round_gap: u64, -} - -#[derive(Clone, Debug)] -pub enum LatencyType { - Average, - P50, - P90, - P99, -} - -#[derive(Default, Clone, Debug)] -pub struct MetricsThreshold { - max: f64, - // % of the data point that can breach the max threshold - max_breach_pct: usize, -} - -impl MetricsThreshold { - pub fn new(max: f64, max_breach_pct: usize) -> Self { - Self { - max, - max_breach_pct, - } - } - - pub fn new_gb(max: f64, max_breach_pct: usize) -> Self { - Self { - max: max * 1024.0 * 1024.0 * 1024.0, - max_breach_pct, - } - } - - pub fn ensure_metrics_threshold( - &self, - metrics_name: &str, - metrics: &Vec, - ) -> anyhow::Result<()> { - if metrics.is_empty() { - bail!("Empty metrics provided"); - } - let breach_count = metrics - .iter() - .filter(|sample| sample.value() > self.max) - .count(); - let breach_pct = (breach_count * 100) / metrics.len(); - if breach_pct > self.max_breach_pct { - bail!( - "{:?} metric violated threshold of {:?}, max_breach_pct: {:?}, breach_pct: {:?} ", - metrics_name, - self.max, - self.max_breach_pct, - breach_pct - ); - } - Ok(()) - } -} - -#[derive(Default, Clone, Debug)] -pub struct SystemMetricsThreshold { - cpu_threshold: MetricsThreshold, - memory_threshold: MetricsThreshold, -} - -impl SystemMetricsThreshold { - pub fn ensure_threshold(&self, metrics: &SystemMetrics) -> anyhow::Result<()> { - self.cpu_threshold - .ensure_metrics_threshold("cpu", metrics.cpu_core_metrics.get())?; - self.memory_threshold - .ensure_metrics_threshold("memory", metrics.memory_bytes_metrics.get())?; - Ok(()) - } - - pub fn new(cpu_threshold: MetricsThreshold, memory_threshold: MetricsThreshold) -> Self { - Self { - cpu_threshold, - memory_threshold, - } - } -} - -#[derive(Clone, Debug)] -pub struct LatencyBreakdownThreshold { - pub thresholds: BTreeMap, -} - -impl LatencyBreakdownThreshold { - pub fn new_strict(thresholds: Vec<(LatencyBreakdownSlice, f64)>) -> Self { - Self::new_with_breach_pct(thresholds, 0) - } - - pub fn new_with_breach_pct( - thresholds: Vec<(LatencyBreakdownSlice, f64)>, - max_breach_pct: usize, - ) -> Self { - Self { - thresholds: thresholds - .into_iter() - .map(|(k, v)| (k, MetricsThreshold::new(v, max_breach_pct))) - .collect(), - } - } - - pub fn ensure_threshold( - &self, - metrics: &LatencyBreakdown, - traffic_name_addition: &String, - ) -> anyhow::Result<()> { - for (slice, threshold) in &self.thresholds { - let samples = metrics.get_samples(slice); - threshold.ensure_metrics_threshold( - &format!("{:?}{}", slice, traffic_name_addition), - samples.get(), - )?; - } - Ok(()) - } -} - -#[derive(Default, Clone, Debug)] -pub struct SuccessCriteria { - pub min_avg_tps: usize, - latency_thresholds: Vec<(Duration, LatencyType)>, - latency_breakdown_thresholds: Option, - check_no_restarts: bool, - max_expired_tps: Option, - max_failed_submission_tps: Option, - wait_for_all_nodes_to_catchup: Option, - // Maximum amount of CPU cores and memory bytes used by the nodes. - system_metrics_threshold: Option, - chain_progress_check: Option, -} - -impl SuccessCriteria { - pub fn new(min_avg_tps: usize) -> Self { - Self { - min_avg_tps, - latency_thresholds: Vec::new(), - latency_breakdown_thresholds: None, - check_no_restarts: false, - max_expired_tps: None, - max_failed_submission_tps: None, - wait_for_all_nodes_to_catchup: None, - system_metrics_threshold: None, - chain_progress_check: None, - } - } - - pub fn add_no_restarts(mut self) -> Self { - self.check_no_restarts = true; - self - } - - pub fn add_max_expired_tps(mut self, max_expired_tps: usize) -> Self { - self.max_expired_tps = Some(max_expired_tps); - self - } - - pub fn add_max_failed_submission_tps(mut self, max_failed_submission_tps: usize) -> Self { - self.max_failed_submission_tps = Some(max_failed_submission_tps); - self - } - - pub fn add_wait_for_catchup_s(mut self, duration_secs: u64) -> Self { - self.wait_for_all_nodes_to_catchup = Some(Duration::from_secs(duration_secs)); - self - } - - pub fn add_system_metrics_threshold(mut self, threshold: SystemMetricsThreshold) -> Self { - self.system_metrics_threshold = Some(threshold); - self - } - - pub fn add_chain_progress(mut self, threshold: StateProgressThreshold) -> Self { - self.chain_progress_check = Some(threshold); - self - } - - pub fn add_latency_threshold(mut self, threshold_s: f32, latency_type: LatencyType) -> Self { - self.latency_thresholds - .push((Duration::from_secs_f32(threshold_s), latency_type)); - self - } - - pub fn add_latency_breakdown_threshold(mut self, threshold: LatencyBreakdownThreshold) -> Self { - self.latency_breakdown_thresholds = Some(threshold); - self - } -} - -pub struct SuccessCriteriaChecker {} - -impl SuccessCriteriaChecker { - pub fn check_core_for_success( - success_criteria: &SuccessCriteria, - _report: &mut TestReport, - stats_rate: &TxnStatsRate, - latency_breakdown: Option<&LatencyBreakdown>, - traffic_name: Option, - ) -> anyhow::Result<()> { - let traffic_name_addition = traffic_name - .map(|n| format!(" for {}", n)) - .unwrap_or_default(); - Self::check_throughput( - success_criteria.min_avg_tps, - success_criteria.max_expired_tps, - success_criteria.max_failed_submission_tps, - stats_rate, - &traffic_name_addition, - )?; - Self::check_latency( - &success_criteria.latency_thresholds, - stats_rate, - &traffic_name_addition, - )?; - if let Some(latency_breakdown_thresholds) = &success_criteria.latency_breakdown_thresholds { - latency_breakdown_thresholds - .ensure_threshold(latency_breakdown.unwrap(), &traffic_name_addition)?; - } - Ok(()) - } - - pub async fn check_for_success( - success_criteria: &SuccessCriteria, - swarm: &mut dyn Swarm, - report: &mut TestReport, - stats: &TxnStats, - window: Duration, - latency_breakdown: &LatencyBreakdown, - start_time: i64, - end_time: i64, - start_version: u64, - end_version: u64, - ) -> anyhow::Result<()> { - println!( - "End to end duration: {}s, performance measured for: {}s", - window.as_secs(), - stats.lasted.as_secs() - ); - let stats_rate = stats.rate(); - - let no_traffic_name_addition = "".to_string(); - Self::check_throughput( - success_criteria.min_avg_tps, - success_criteria.max_expired_tps, - success_criteria.max_failed_submission_tps, - &stats_rate, - &no_traffic_name_addition, - )?; - - Self::check_latency( - &success_criteria.latency_thresholds, - &stats_rate, - &no_traffic_name_addition, - )?; - - if let Some(latency_breakdown_thresholds) = &success_criteria.latency_breakdown_thresholds { - latency_breakdown_thresholds - .ensure_threshold(latency_breakdown, &no_traffic_name_addition)?; - } - - if let Some(timeout) = success_criteria.wait_for_all_nodes_to_catchup { - swarm - .wait_for_all_nodes_to_catchup_to_next(timeout) - .await - .context("Failed waiting for all nodes to catchup to next version")?; - } - - if success_criteria.check_no_restarts { - swarm - .ensure_no_validator_restart() - .await - .context("Failed ensuring no validator restarted")?; - swarm - .ensure_no_fullnode_restart() - .await - .context("Failed ensuring no fullnode restarted")?; - } - - if let Some(system_metrics_threshold) = success_criteria.system_metrics_threshold.clone() { - Self::check_system_metrics(swarm, start_time, end_time, system_metrics_threshold) - .await?; - } - - if let Some(chain_progress_threshold) = &success_criteria.chain_progress_check { - Self::check_chain_progress( - swarm, - report, - chain_progress_threshold, - start_version, - end_version, - ) - .await - .context("Failed check chain progress")?; - } - - Ok(()) - } - - async fn check_chain_progress( - swarm: &mut dyn Swarm, - report: &mut TestReport, - chain_progress_threshold: &StateProgressThreshold, - start_version: u64, - end_version: u64, - ) -> anyhow::Result<()> { - // Choose client with newest ledger version to fetch NewBlockEvents from: - let (_max_v, client) = swarm - .get_client_with_newest_ledger_version() - .await - .context("No clients replied in check_chain_progress")?; - - let epochs = FetchMetadata::fetch_new_block_events(&client, None, None) - .await - .unwrap(); - - let mut max_round_gap = 0; - let mut max_round_gap_version = 0; - let mut max_time_gap = 0; - let mut max_time_gap_version = 0; - - let mut prev_block = None; - let mut prev_ts = 0; - let mut failed_from_nil = 0; - let mut previous_epooch = 0; - let mut previous_round = 0; - for block in epochs - .iter() - .flat_map(|epoch| epoch.blocks.iter()) - .filter(|b| b.version > start_version && b.version < end_version) - { - let is_nil = block.event.proposer() == PeerId::ZERO; - - let current_gap = if previous_epooch == block.event.epoch() { - block.event.round() - previous_round - 1 - } else { - u64::from(!is_nil) + block.event.failed_proposer_indices().len() as u64 - }; - - if is_nil { - failed_from_nil += current_gap; - } else { - if prev_ts > 0 { - let round_gap = current_gap + failed_from_nil; - let time_gap = block.event.proposed_time() as i64 - prev_ts as i64; - - if time_gap < 0 { - println!( - "Clock went backwards? {}, {:?}, {:?}", - time_gap, block, prev_block - ); - } - - if round_gap > max_round_gap { - max_round_gap = round_gap; - max_round_gap_version = block.version; - } - if time_gap > max_time_gap as i64 { - max_time_gap = time_gap as u64; - max_time_gap_version = block.version; - } - } - - failed_from_nil = 0; - prev_ts = block.event.proposed_time(); - prev_block = Some(block); - } - - previous_epooch = block.event.epoch(); - previous_round = block.event.round(); - } - - let max_time_gap_secs = Duration::from_micros(max_time_gap).as_secs_f32(); - - let gap_text = format!( - "Max round gap was {} [limit {}] at version {}. Max no progress secs was {} [limit {}] at version {}.", - max_round_gap, - chain_progress_threshold.max_round_gap, - max_round_gap_version, - max_time_gap_secs, - chain_progress_threshold.max_no_progress_secs, - max_time_gap_version, - ); - - if max_round_gap > chain_progress_threshold.max_round_gap - || max_time_gap_secs > chain_progress_threshold.max_no_progress_secs - { - bail!("Failed chain progress check. {}", gap_text); - } else { - println!("Passed progress check. {}", gap_text); - report.report_text(gap_text); - } - - Ok(()) - } - - pub fn check_tps( - min_avg_tps: usize, - stats_rate: &TxnStatsRate, - traffic_name_addition: &String, - ) -> anyhow::Result<()> { - let avg_tps = stats_rate.committed; - if avg_tps < min_avg_tps as u64 { - bail!( - "TPS requirement{} failed. Average TPS {}, minimum TPS requirement {}. Full stats: {}", - traffic_name_addition, - avg_tps, - min_avg_tps, - stats_rate, - ) - } else { - println!( - "TPS is {} and is within limit of {}", - stats_rate.committed, min_avg_tps - ); - Ok(()) - } - } - - fn check_max_value( - max_config: Option, - stats_rate: &TxnStatsRate, - value: u64, - value_desc: &str, - traffic_name_addition: &String, - ) -> anyhow::Result<()> { - if let Some(max) = max_config { - if value > max as u64 { - bail!( - "{} requirement{} failed. {} TPS: average {}, maximum requirement {}. Full stats: {}", - value_desc, - traffic_name_addition, - value_desc, - value, - max, - stats_rate, - ) - } else { - println!( - "{} TPS is {} and is below max limit of {}", - value_desc, value, max - ); - Ok(()) - } - } else { - Ok(()) - } - } - - pub fn check_throughput( - min_avg_tps: usize, - max_expired_config: Option, - max_failed_submission_config: Option, - stats_rate: &TxnStatsRate, - traffic_name_addition: &String, - ) -> anyhow::Result<()> { - Self::check_tps(min_avg_tps, stats_rate, traffic_name_addition)?; - Self::check_max_value( - max_expired_config, - stats_rate, - stats_rate.expired, - "expired", - traffic_name_addition, - )?; - Self::check_max_value( - max_failed_submission_config, - stats_rate, - stats_rate.failed_submission, - "submission", - traffic_name_addition, - )?; - Ok(()) - } - - pub fn check_latency( - latency_thresholds: &[(Duration, LatencyType)], - stats_rate: &TxnStatsRate, - traffic_name_addition: &String, - ) -> anyhow::Result<()> { - let mut failures = Vec::new(); - for (latency_threshold, latency_type) in latency_thresholds { - let latency = Duration::from_millis(match latency_type { - LatencyType::Average => stats_rate.latency, - LatencyType::P50 => stats_rate.p50_latency, - LatencyType::P90 => stats_rate.p90_latency, - LatencyType::P99 => stats_rate.p99_latency, - }); - - if latency > *latency_threshold { - failures.push( - format!( - "{:?} latency{} is {}s and exceeds limit of {}s", - latency_type, - traffic_name_addition, - latency.as_secs_f32(), - latency_threshold.as_secs_f32() - ) - .to_string(), - ); - } else { - println!( - "{:?} latency{} is {}s and is within limit of {}s", - latency_type, - traffic_name_addition, - latency.as_secs_f32(), - latency_threshold.as_secs_f32() - ); - } - } - if !failures.is_empty() { - bail!("Failed latency check, for {:?}", failures); - } else { - Ok(()) - } - } - - async fn check_system_metrics( - swarm: &mut dyn Swarm, - start_time: i64, - end_time: i64, - threshold: SystemMetricsThreshold, - ) -> anyhow::Result<()> { - let system_metrics = fetch_system_metrics(swarm, start_time, end_time).await?; - threshold.ensure_threshold(&system_metrics) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - #[tokio::test] - async fn test_empty_metrics_threshold() { - let cpu_threshold = MetricsThreshold::new(10.0, 30); - let memory_threshold = MetricsThreshold::new(100.0, 40); - let threshold = SystemMetricsThreshold::new(cpu_threshold, memory_threshold); - let metrics = SystemMetrics::new(vec![], vec![]); - threshold.ensure_threshold(&metrics).unwrap_err(); - } -} diff --git a/testsuite/forge/src/test_utils/consensus_utils.rs b/testsuite/forge/src/test_utils/consensus_utils.rs deleted file mode 100644 index 9eba7b81fdb60..0000000000000 --- a/testsuite/forge/src/test_utils/consensus_utils.rs +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{wait_for_all_nodes_to_catchup_to_version, Swarm, SwarmExt}; -use anyhow::{bail, Context, Result}; -use aptos_config::config::DEFAULT_MAX_PAGE_SIZE; -use aptos_rest_client::Client as RestClient; -use async_trait::async_trait; -use chrono::Utc; -use core::time; -use futures::future::join_all; -use itertools::Itertools; -use std::{ - collections::HashSet, - time::{Duration, Instant}, -}; - -#[derive(Clone, Debug)] -pub struct NodeState { - pub version: u64, - pub epoch: u64, - pub round: u64, -} - -// TODO: check if we can fetch consensus round, not just committed round. -async fn get_node_state(validator_client: &RestClient) -> NodeState { - let (events, state) = validator_client - .get_new_block_events_bcs(None, Some(1)) - .await - .unwrap() - .into_parts(); - let event = events.first().unwrap(); - assert!(event.version <= state.version); - NodeState { - version: state.version, - epoch: event.event.epoch(), - round: event.event.round(), - } -} - -/// Run a test, where we spin up a set of validators, and then -/// check a reliability scenario. -/// After scenario finishes, we always return reliability to 100%, -/// and we confirm that chain recouperates, makes progress, and -/// all nodes agree on ledger. -/// -/// Scenario is performed via two nested loops: -/// outer cycles, and within each cycle over parts. -/// Scenario can specify failpoint changes on every part, -/// and can check the performance of the network after every cycle. -/// -/// Transaction can be inserted on every part, to control the throughput. -/// I.e. if part is shorter than how long it takes for empty block to be -/// generated, we can make sure one block gets created on every part. -pub async fn test_consensus_fault_tolerance( - swarm: &mut dyn Swarm, - cycles: usize, - cycle_duration_s: f32, - parts_in_cycle: usize, - mut failure_injection: Box, - // (cycle, executed_epochs, executed_rounds, executed_transactions, current_state, previous_state) - mut check_cycle: Box< - dyn FnMut(usize, u64, u64, u64, Vec, Vec) -> Result<()>, - >, - new_epoch_on_cycle: bool, - // Instead of failing on first check, we check the full run, - // and then fail if any checks failed during the run. - // Can allow us to better see if state would've gotten resolved by itself, etc. - raise_check_error_at_the_end: bool, -) -> Result<()> { - let validator_clients = swarm.get_validator_clients_with_names(); - - async fn get_all_states(validator_clients: &[(String, RestClient)]) -> Vec { - join_all( - validator_clients - .iter() - .cloned() - .map(move |(_, v)| async move { get_node_state(&v).await }), - ) - .await - } - - let mut errors = Vec::new(); - - for cycle in 0..cycles { - let previous = get_all_states(&validator_clients).await; - - let now = Instant::now(); - for part in 0..parts_in_cycle { - failure_injection - .inject(&validator_clients, cycle, part) - .await; - let elapsed = now.elapsed().as_secs_f32(); - let wanted = (1 + part) as f32 * cycle_duration_s / (parts_in_cycle as f32); - if elapsed < wanted { - tokio::time::sleep(time::Duration::from_secs_f32(wanted - elapsed)).await; - } - } - - let cur = get_all_states(&validator_clients).await; - - let epochs = cur.iter().map(|s| s.epoch).max().unwrap() - - previous.iter().map(|s| s.epoch).max().unwrap(); - let rounds = cur - .iter() - .map(|s| s.round) - .max() - .unwrap() - .saturating_sub(previous.iter().map(|s| s.round).max().unwrap()); - let transactions = cur.iter().map(|s| s.version).max().unwrap() - - previous.iter().map(|s| s.version).max().unwrap(); - - println!( - "cycle {} lasted {:.3} with {} epochs, {} rounds and {} transactions", - cycle, - now.elapsed().as_secs_f32(), - epochs, - rounds, - transactions, - ); - println!( - "All at epochs: {:?}, from {:?}", - cur.iter().map(|s| s.epoch).collect::>(), - previous.iter().map(|s| s.epoch).collect::>(), - ); - println!( - "All at rounds: {:?}, from {:?}", - cur.iter().map(|s| s.round).collect::>(), - previous.iter().map(|s| s.round).collect::>(), - ); - println!( - "All at versions: {:?}, from {:?}", - cur.iter().map(|s| s.version).collect::>(), - previous.iter().map(|s| s.version).collect::>(), - ); - - let check_result = check_cycle(cycle, epochs, rounds, transactions, cur.clone(), previous); - if raise_check_error_at_the_end { - if let Err(error) = check_result { - println!("Failed check {}", error); - errors.push((error, cycle, Utc::now())); - } - } else { - check_result?; - } - - if new_epoch_on_cycle { - swarm.aptos_public_info().reconfig().await; - } - } - - failure_injection.clear(&validator_clients).await; - - let cur = get_all_states(&validator_clients).await; - println!( - "All at versions: {:?}", - cur.iter().map(|s| s.version).collect::>() - ); - let largest_v = cur.iter().map(|s| s.version).max().unwrap(); - println!("Largest version {}", largest_v); - let target_v = largest_v + 10; - - wait_for_all_nodes_to_catchup_to_version(&validator_clients, target_v, Duration::from_secs(30)) - .await - .context("catchup failed")?; - - let transactions: Vec<_> = - join_all(validator_clients.iter().cloned().map(move |v| async move { - let mut txns = - v.1.get_transactions_bcs( - Some(target_v.saturating_sub(DEFAULT_MAX_PAGE_SIZE as u64)), - Some(DEFAULT_MAX_PAGE_SIZE), - ) - .await - .unwrap() - .into_inner(); - txns.retain(|t| t.version <= target_v); - >>::Ok(txns) - })) - .await; - - let txns_a = transactions.first().unwrap().as_ref().unwrap(); - - for i in 1..transactions.len() { - let txns_b = transactions.get(i).unwrap().as_ref().unwrap(); - assert_eq!( - txns_a.len(), - txns_b.len(), - "Fetched length of transactions for target_v {} doesn't match: from {} to {} vs from {} to {}", - target_v, - txns_a.first().map(|t| t.version).unwrap_or(0), - txns_a.last().map(|t| t.version).unwrap_or(0), - txns_b.first().map(|t| t.version).unwrap_or(0), - txns_b.last().map(|t| t.version).unwrap_or(0), - ); - for i in 0..txns_a.len() { - assert_eq!( - txns_a[i], txns_b[i], - "Transaction at index {} after target version {}, doesn't match", - i, target_v - ); - } - } - - if !errors.is_empty() { - bail!( - "There were {} check failures during the run: {}", - errors.len(), - errors - .iter() - .map(|(err, cycle, ts)| format!( - "cycle {} at {}: {:?} ", - cycle, - ts.to_rfc3339(), - err - )) - .join("\n") - ); - } - Ok(()) -} - -#[async_trait] -pub trait FailureInjection { - async fn inject( - &mut self, - validator_clients: &[(String, RestClient)], - cycle: usize, - part: usize, - ); - async fn clear(&mut self, validator_clients: &[(String, RestClient)]); -} - -pub struct NoFailureInjection {} - -#[async_trait] -impl FailureInjection for NoFailureInjection { - async fn inject(&mut self, _: &[(String, RestClient)], _: usize, _: usize) {} - - async fn clear(&mut self, _: &[(String, RestClient)]) {} -} - -pub fn no_failure_injection() -> Box { - Box::new(NoFailureInjection {}) -} - -pub struct FailPointFailureInjection { - modified_failpoints: HashSet<(usize, String)>, - // (cycle, part) -> (Vec(validator_index, name, action), reset_old_enpoints) - get_fail_points_to_set: - Box (Vec<(usize, String, String)>, bool) + Send>, -} - -impl FailPointFailureInjection { - pub fn new( - get_fail_points_to_set: Box< - dyn FnMut(usize, usize) -> (Vec<(usize, String, String)>, bool) + Send, - >, - ) -> Self { - Self { - modified_failpoints: HashSet::new(), - get_fail_points_to_set, - } - } -} - -pub fn fail_point_injection( - get_fail_points_to_set: Box< - dyn FnMut(usize, usize) -> (Vec<(usize, String, String)>, bool) + Send, - >, -) -> Box { - Box::new(FailPointFailureInjection::new(get_fail_points_to_set)) -} - -#[async_trait] -impl FailureInjection for FailPointFailureInjection { - async fn inject( - &mut self, - validator_clients: &[(String, RestClient)], - cycle: usize, - part: usize, - ) { - let (fail_points_to_set, reset_old_failpoints) = (self.get_fail_points_to_set)(cycle, part); - if reset_old_failpoints { - let new_set = fail_points_to_set - .iter() - .map(|(validator_idx, name, _actions)| (validator_idx, name)) - .collect::>(); - for (validator_idx, name) in self.modified_failpoints.iter() { - // we don't want to clear failpoints we are setting later, - // as it can cause race conditions. - if !new_set.contains(&(validator_idx, name)) { - validator_clients[*validator_idx] - .1 - .set_failpoint(name.clone(), "off".to_string()) - .await - .context(validator_clients[*validator_idx].0.clone()) - .unwrap(); - } - } - self.modified_failpoints = HashSet::new(); - } - for (validator_idx, name, actions) in fail_points_to_set { - validator_clients[validator_idx] - .1 - .set_failpoint(name.clone(), actions.clone()) - .await - .context(validator_clients[validator_idx].0.clone()) - .unwrap(); - self.modified_failpoints.insert((validator_idx, name)); - } - } - - async fn clear(&mut self, validator_clients: &[(String, RestClient)]) { - for (validator_idx, name) in self.modified_failpoints.iter() { - validator_clients[*validator_idx] - .1 - .set_failpoint(name.clone(), "off".to_string()) - .await - .context(validator_clients[*validator_idx].0.clone()) - .unwrap(); - } - } -} diff --git a/testsuite/forge/src/test_utils/mod.rs b/testsuite/forge/src/test_utils/mod.rs deleted file mode 100644 index 6a09962387803..0000000000000 --- a/testsuite/forge/src/test_utils/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod consensus_utils; diff --git a/testsuite/smoke-test/Cargo.toml b/testsuite/smoke-test/Cargo.toml deleted file mode 100644 index 501e639fa553b..0000000000000 --- a/testsuite/smoke-test/Cargo.toml +++ /dev/null @@ -1,82 +0,0 @@ -[package] -name = "smoke-test" -description = "Aptos end to end tests" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true, features = ["fuzzing"] } -aptos-bitvec = { path = "../../crates/aptos-bitvec" } -aptos-cached-packages = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus = { workspace = true } -aptos-crypto = { workspace = true } -aptos-db = { workspace = true } -aptos-debugger = { workspace = true } -aptos-dkg = { workspace = true } -aptos-faucet-core = { workspace = true } -aptos-forge = { workspace = true } -aptos-framework = { workspace = true } -aptos-gas-algebra = { workspace = true } -aptos-gas-schedule = { workspace = true, features = ["testing"] } -aptos-global-constants = { workspace = true } -aptos-indexer = { workspace = true } -aptos-inspection-service = { workspace = true } -aptos-keygen = { workspace = true } -aptos-move-debugger = { workspace = true } -aptos-release-builder = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-rosetta = { workspace = true } -aptos-sdk = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-temppath = { workspace = true } -aptos-types = { workspace = true } -aptos-vm = { workspace = true } -aptos-vm-genesis = { workspace = true } -async-trait = { workspace = true } -bcs = { workspace = true } -diesel = { workspace = true, features = [ - "chrono", - "postgres", - "r2d2", - "numeric", - "serde_json", -] } -digest = { workspace = true } -hex = { workspace = true } -hyper = { workspace = true } -move-core-types = { workspace = true } -num-traits = { workspace = true } -proptest = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } -walkdir = { workspace = true } - -[dev-dependencies] -aptos-backup-cli = { workspace = true } -aptos-genesis = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-secure-storage = { workspace = true } -aptos-time-service = { workspace = true } -aptos-vault-client = { workspace = true } -base64 = { workspace = true } -futures = { workspace = true } -num_cpus = { workspace = true } -once_cell = { workspace = true } -rand = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -serde_yaml = { workspace = true } diff --git a/testsuite/smoke-test/src/aptos/account_creation.rs b/testsuite/smoke-test/src/aptos/account_creation.rs deleted file mode 100644 index 7f1658608ae14..0000000000000 --- a/testsuite/smoke-test/src/aptos/account_creation.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 diff --git a/testsuite/smoke-test/src/aptos/error_report.rs b/testsuite/smoke-test/src/aptos/error_report.rs deleted file mode 100644 index d3bee2ad9e6ba..0000000000000 --- a/testsuite/smoke-test/src/aptos/error_report.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::new_local_swarm_with_aptos; -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::{AptosPublicInfo, Swarm}; -use aptos_sdk::{transaction_builder::TransactionBuilder, types::LocalAccount}; -use aptos_types::{ - account_address::AccountAddress, account_config::aptos_test_root_address, chain_id::ChainId, -}; - -async fn submit_and_check_err TransactionBuilder>( - local_account: &LocalAccount, - info: &mut AptosPublicInfo<'_>, - f: F, - expected: &str, -) { - let payload = info - .transaction_factory() - .payload(aptos_stdlib::aptos_coin_claim_mint_capability()) - .sequence_number(0); - let txn = local_account.sign_transaction(f(payload).build()); - let err = format!( - "{:?}", - info.client().submit_and_wait(&txn).await.unwrap_err() - ); - assert!( - err.contains(expected), - "expected = {}, err = {}", - expected, - err - ) -} - -#[tokio::test] -async fn test_error_report() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - let local_account = info.random_account(); - let address = local_account.address(); - info.create_user_account(local_account.public_key()) - .await - .unwrap(); - submit_and_check_err( - &local_account, - &mut info, - |t| t.sender(address), - "INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE", - ) - .await; - // TODO(Gas): re-enable this - /*submit_and_check_err( - &local_account, - ctx, - |t| t.sender(address).gas_unit_price(0), - "GAS_UNIT_PRICE_BELOW_MIN_BOUND", - ) - .await;*/ - submit_and_check_err( - &local_account, - &mut info, - |t| t.sender(address).chain_id(ChainId::new(100)), - "BAD_CHAIN_ID", - ) - .await; - submit_and_check_err( - &local_account, - &mut info, - |t| t.sender(AccountAddress::random()), - "SENDING_ACCOUNT_DOES_NOT_EXIST", - ) - .await; - submit_and_check_err( - &local_account, - &mut info, - |t| t.sender(aptos_test_root_address()), - "SEQUENCE_NUMBER_TOO_OLD", - ) - .await; - let root_account_sequence_number = info.root_account().sequence_number(); - submit_and_check_err( - &local_account, - &mut info, - |t| { - t.sender(aptos_test_root_address()) - .sequence_number(root_account_sequence_number) - }, - "INVALID_AUTH_KEY", - ) - .await; - info.mint(address, 100000).await.unwrap(); - submit_and_check_err( - &local_account, - &mut info, - |t| t.sender(address).expiration_timestamp_secs(0), - "TRANSACTION_EXPIRED", - ) - .await; -} diff --git a/testsuite/smoke-test/src/aptos/gas_check.rs b/testsuite/smoke-test/src/aptos/gas_check.rs deleted file mode 100644 index 68f7d79659ff8..0000000000000 --- a/testsuite/smoke-test/src/aptos/gas_check.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::new_local_swarm_with_aptos; -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::Swarm; -use std::time::Duration; - -// TODO: This test should be moved to e2e-move-tests as only 0x1 can make changes to gas schedule -// and there's no easy way to do this in a smoke test without going through the full governance -// flow. -#[ignore] -#[tokio::test] -async fn test_gas_check() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - let account1 = info.random_account(); - info.create_user_account(account1.public_key()) - .await - .unwrap(); - let account2 = info.random_account(); - info.create_user_account(account2.public_key()) - .await - .unwrap(); - - let transfer_txn = account1.sign_with_transaction_builder( - info.transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(account2.address(), 100)), - ); - // fail due to not enough gas - let err = info - .client() - .submit_and_wait(&transfer_txn) - .await - .unwrap_err(); - assert!(format!("{:?}", err).contains("INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE")); - - // TODO(Gas): double check this - info.mint(account1.address(), 1_000).await.unwrap(); - info.mint(account2.address(), 1_000).await.unwrap(); - - let transfer_too_much = account2.sign_with_transaction_builder( - // TODO(Gas): double check this - info.transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(account1.address(), 1_000)), - ); - - let err = info - .client() - .submit_and_wait(&transfer_too_much) - .await - .unwrap_err(); - assert!(format!("{:?}", err).contains("execution failed")); - - // succeed with enough gas - info.client().submit_and_wait(&transfer_txn).await.unwrap(); - - /* - // update to allow 0 gas unit price - let mut gas_params = AptosGasParameters::initial(); - gas_params.txn.min_price_per_gas_unit = 0.into(); - let gas_schedule_blob = bcs::to_bytes(&gas_params.to_on_chain_gas_schedule()) - .expect("failed to serialize gas parameters"); - - let txn_factory = info.transaction_factory(); - - // This is disabled as set_gas_schedule is no longer an entry function and thus not accessible - // via aptos_stdlib. - - let update_txn = info - .root_account() - .sign_with_transaction_builder(txn_factory.payload( - aptos_stdlib::gas_schedule_set_gas_schedule(gas_schedule_blob), - )); - info.client().submit_and_wait(&update_txn).await.unwrap(); - */ - - let zero_gas_txn = account1.sign_with_transaction_builder( - info.transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(account2.address(), 100)) - .gas_unit_price(0), - ); - while info - .client() - .get_ledger_information() - .await - .unwrap() - .inner() - .epoch - < 2 - { - tokio::time::sleep(Duration::from_millis(50)).await; - } - info.client().submit_and_wait(&zero_gas_txn).await.unwrap(); -} diff --git a/testsuite/smoke-test/src/aptos/mint_transfer.rs b/testsuite/smoke-test/src/aptos/mint_transfer.rs deleted file mode 100644 index dbc194f1da6a1..0000000000000 --- a/testsuite/smoke-test/src/aptos/mint_transfer.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::new_local_swarm_with_aptos; -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::Swarm; -use aptos_move_debugger::aptos_debugger::AptosDebugger; -use aptos_types::transaction::{ExecutionStatus, TransactionStatus}; - -#[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_mint_transfer() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - let account1 = info.random_account(); - info.create_user_account(account1.public_key()) - .await - .unwrap(); - let account2 = info.random_account(); - info.create_user_account(account2.public_key()) - .await - .unwrap(); - - // NOTE(Gas): For some reason, there needs to be a lot of funds in the account in order for the - // test to pass. - // Is this caused by us increasing the default max gas amount in - // testsuite/forge/src/interface/aptos.rs? - info.mint(account1.address(), 100_000_000_000) - .await - .unwrap(); - - let transfer_txn = account1.sign_with_transaction_builder( - info.transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(account2.address(), 40000)), - ); - info.client().submit_and_wait(&transfer_txn).await.unwrap(); - assert_eq!( - info.client() - .get_account_balance(account2.address()) - .await - .unwrap() - .into_inner() - .get(), - 40000 - ); - - // test delegation - let txn_factory = info.transaction_factory(); - let delegate_txn1 = info - .root_account() - .sign_with_transaction_builder(txn_factory.payload( - aptos_stdlib::aptos_coin_delegate_mint_capability(account1.address()), - )); - info.client().submit_and_wait(&delegate_txn1).await.unwrap(); - - // Test delegating more than one at a time: faucet startup stampeding herd - let delegate_txn2 = info - .root_account() - .sign_with_transaction_builder(txn_factory.payload( - aptos_stdlib::aptos_coin_delegate_mint_capability(account2.address()), - )); - info.client().submit_and_wait(&delegate_txn2).await.unwrap(); - - let claim_txn = account1.sign_with_transaction_builder( - txn_factory.payload(aptos_stdlib::aptos_coin_claim_mint_capability()), - ); - info.client().submit_and_wait(&claim_txn).await.unwrap(); - let mint_txn = account1.sign_with_transaction_builder( - txn_factory.payload(aptos_stdlib::aptos_coin_mint(account1.address(), 10000)), - ); - info.client().submit_and_wait(&mint_txn).await.unwrap(); - - // Testing the AptosDebugger by reexecuting the transaction that has been published. - println!("Testing...."); - let debugger = AptosDebugger::rest_client(info.client().clone()).unwrap(); - - let txn_ver = debugger - .get_version_by_account_sequence(account1.address(), 0) - .await - .unwrap() - .unwrap(); - - let output = debugger - .execute_past_transactions(txn_ver, 1) - .await - .unwrap() - .pop() - .unwrap(); - - assert_eq!( - output.status(), - &TransactionStatus::Keep(ExecutionStatus::Success) - ); -} diff --git a/testsuite/smoke-test/src/aptos/mod.rs b/testsuite/smoke-test/src/aptos/mod.rs deleted file mode 100644 index 64fa8f9359e16..0000000000000 --- a/testsuite/smoke-test/src/aptos/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod account_creation; -mod error_report; -mod gas_check; -mod mint_transfer; -pub(crate) mod move_test_helpers; -mod package_publish; diff --git a/testsuite/smoke-test/src/aptos/move_test_helpers.rs b/testsuite/smoke-test/src/aptos/move_test_helpers.rs deleted file mode 100644 index 013bff5e3874b..0000000000000 --- a/testsuite/smoke-test/src/aptos/move_test_helpers.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! Helpers for writing Move tests - -use anyhow::Result; -use aptos_forge::AptosPublicInfo; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_sdk::transaction_builder::TransactionFactory; -use std::path::PathBuf; - -/// New style publishing via `code::publish_package` -pub async fn publish_package( - info: &mut AptosPublicInfo<'_>, - move_dir: PathBuf, -) -> Result { - let package = BuiltPackage::build(move_dir, BuildOptions::default())?; - let blobs = package.extract_code(); - let metadata = package.extract_metadata()?; - let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - blobs, - ); - let txn_factory = info.transaction_factory(); - let publish_txn = info - .root_account() - .sign_with_transaction_builder(txn_factory.payload(payload)); - info.client().submit_and_wait(&publish_txn).await?; - Ok(txn_factory) -} diff --git a/testsuite/smoke-test/src/aptos/package_publish.rs b/testsuite/smoke-test/src/aptos/package_publish.rs deleted file mode 100644 index 7d1b48285c1cc..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{aptos::move_test_helpers, smoke_test_environment::new_local_swarm_with_aptos}; -use aptos_forge::Swarm; - -#[tokio::test] -async fn test_package_publish() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - let base_path_v1 = base_dir.join("src/aptos/package_publish_modules_v1/"); - let base_path_v2 = base_dir.join("src/aptos/package_publish_modules_v2/"); - let base_path_v3 = base_dir.join("src/aptos/package_publish_modules_v3/"); - - move_test_helpers::publish_package(&mut info, base_path_v1) - .await - .unwrap(); - // v2 is downwards compatible to v1 - move_test_helpers::publish_package(&mut info, base_path_v2) - .await - .unwrap(); - // v3 is not downwards compatible to v2 - move_test_helpers::publish_package(&mut info, base_path_v3) - .await - .unwrap_err(); -} diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v1/Move.toml b/testsuite/smoke-test/src/aptos/package_publish_modules_v1/Move.toml deleted file mode 100644 index 70189b41ee665..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v1/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "hello" -version = "0.0.0" - -[dependencies] -AptosFramework = { local = "../../../../../aptos-move/framework/aptos-framework" } diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v1/sources/hello.move b/testsuite/smoke-test/src/aptos/package_publish_modules_v1/sources/hello.move deleted file mode 100644 index fbc45e82a92fc..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v1/sources/hello.move +++ /dev/null @@ -1,5 +0,0 @@ -module 0xA550C18::TestPackagePublish { - - public entry fun hello(_owner: &signer) { - } -} diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v2/Move.toml b/testsuite/smoke-test/src/aptos/package_publish_modules_v2/Move.toml deleted file mode 100644 index 70189b41ee665..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v2/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "hello" -version = "0.0.0" - -[dependencies] -AptosFramework = { local = "../../../../../aptos-move/framework/aptos-framework" } diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v2/sources/hello.move b/testsuite/smoke-test/src/aptos/package_publish_modules_v2/sources/hello.move deleted file mode 100644 index 2862c8e684c03..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v2/sources/hello.move +++ /dev/null @@ -1,11 +0,0 @@ -module 0xA550C18::TestPackagePublish { - - struct S { x: u64 } - - public entry fun hello(_owner: &signer) { - } - - // Adding new entry - public entry fun hello2(_owner: &signer) { - } -} diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v3/Move.toml b/testsuite/smoke-test/src/aptos/package_publish_modules_v3/Move.toml deleted file mode 100644 index 70189b41ee665..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v3/Move.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "hello" -version = "0.0.0" - -[dependencies] -AptosFramework = { local = "../../../../../aptos-move/framework/aptos-framework" } diff --git a/testsuite/smoke-test/src/aptos/package_publish_modules_v3/sources/hello.move b/testsuite/smoke-test/src/aptos/package_publish_modules_v3/sources/hello.move deleted file mode 100644 index 2dfa42f4baa2e..0000000000000 --- a/testsuite/smoke-test/src/aptos/package_publish_modules_v3/sources/hello.move +++ /dev/null @@ -1,10 +0,0 @@ -module 0xA550C18::TestPackagePublish { - - struct S { x: u64, y: u64 } // changing layout - - public entry fun hello(_owner: &signer) { - } - - public entry fun hello2(_owner: &signer) { - } -} diff --git a/testsuite/smoke-test/src/aptos_cli/account.rs b/testsuite/smoke-test/src/aptos_cli/account.rs deleted file mode 100644 index 0cbfa2b493e91..0000000000000 --- a/testsuite/smoke-test/src/aptos_cli/account.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::SwarmBuilder; -use aptos::{account::create::DEFAULT_FUNDED_COINS, common::types::GasOptions}; -use aptos_crypto::{PrivateKey, ValidCryptoMaterialStringExt}; -use aptos_keygen::KeyGen; - -#[tokio::test] -async fn test_account_flow() { - let (_swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(2) - .await; - - cli.assert_account_balance_now(0, DEFAULT_FUNDED_COINS) - .await; - cli.assert_account_balance_now(1, DEFAULT_FUNDED_COINS) - .await; - - let transfer_amount = 100; - let response = cli - .transfer_coins(0, 1, transfer_amount, None) - .await - .unwrap(); - let expected_sender_amount = DEFAULT_FUNDED_COINS - (response.octa_spent()) - transfer_amount; - let expected_receiver_amount = DEFAULT_FUNDED_COINS + transfer_amount; - - // transfer_coins already waits for transaction to be committed - cli.assert_account_balance_now(0, expected_sender_amount) - .await; - cli.assert_account_balance_now(1, expected_receiver_amount) - .await; - - let expected_sender_amount = expected_sender_amount + DEFAULT_FUNDED_COINS; - let _ = cli.fund_account(0, None).await.unwrap(); - // fund_account already waits for transaction to be committed - cli.assert_account_balance_now(0, expected_sender_amount) - .await; - - // Create another cli account: - cli.create_cli_account_from_faucet(KeyGen::from_os_rng().generate_ed25519_private_key(), None) - .await - .unwrap(); - cli.assert_account_balance_now(2, DEFAULT_FUNDED_COINS) - .await; - - // Test gas options - // Override gas unit price should use it instead of the estimated one - let summary = cli - .transfer_coins( - 2, - 1, - 5, - Some(GasOptions { - gas_unit_price: Some(2), - max_gas: None, - expiration_secs: 30, - }), - ) - .await - .unwrap(); - assert_eq!(2, summary.gas_unit_price); - - let new_expected_balance = DEFAULT_FUNDED_COINS - summary.octa_spent() - 5; - - cli.assert_account_balance_now(2, new_expected_balance) - .await; - - // Setting max gas skips simulation (this should fail for too little gas units, but be charged gas) - // If it was simulated, it wouldn't charge gas, and it would need to be caught by the VM. Mempool - // submission doesn't check max gas is correct, just that the user has enough to pay it - cli.transfer_coins( - 2, - 1, - 5, - Some(GasOptions { - gas_unit_price: None, - // NOTE(Gas): This should be equal to the min gas amount allowed. - // Read the comment above to understand why. - max_gas: Some(2), - expiration_secs: 30, - }), - ) - .await - .unwrap_err(); - - // The previous transaction resulted in gas spent, so the balance of the account - // should be lower than it was before (<), unless the gas price is zero, in which - // case it will be the same (hence <=). - assert!(cli.account_balance_now(2).await.unwrap() <= new_expected_balance); -} - -#[tokio::test] -async fn test_account_key_rotation() { - let (_swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(2) - .await; - let account_id = cli.account_id(0); - let original_public_key = cli.private_key(0).public_key(); - assert_eq!( - cli.lookup_address(&original_public_key).await.unwrap(), - account_id - ); - - let mut keygen = KeyGen::from_seed([9u8; 32]); - let new_private_key = keygen.generate_ed25519_private_key(); - cli.rotate_key(0, new_private_key.to_encoded_string().unwrap(), None) - .await - .unwrap(); - // Ensure account id in framework is still the same - assert_eq!(account_id, cli.account_id(0)); - - // Original should still work - assert_eq!( - cli.lookup_address(&original_public_key).await.unwrap(), - account_id - ); - // And new one should work - assert_eq!( - cli.lookup_address(&new_private_key.public_key()) - .await - .unwrap(), - account_id - ); - - // And now a transfer with the old key should not work - cli.transfer_coins(0, 1, 5, None) - .await - .expect_err("Old key should not be able to transfer"); - - // But the new one should - cli.set_private_key(0, new_private_key); - cli.transfer_coins(0, 1, 5, None) - .await - .expect("New key should be able to transfer"); -} diff --git a/testsuite/smoke-test/src/aptos_cli/mod.rs b/testsuite/smoke-test/src/aptos_cli/mod.rs deleted file mode 100644 index 7c576cf7e4b05..0000000000000 --- a/testsuite/smoke-test/src/aptos_cli/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod account; -#[cfg(feature = "cli-framework-test-move")] -mod r#move; -pub mod validator; diff --git a/testsuite/smoke-test/src/aptos_cli/move.rs b/testsuite/smoke-test/src/aptos_cli/move.rs deleted file mode 100644 index 3d5278034acce..0000000000000 --- a/testsuite/smoke-test/src/aptos_cli/move.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::SwarmBuilder; -use aptos::test::CliTestFramework; -use aptos_framework::{BuildOptions, BuiltPackage}; -use aptos_logger::info; -use aptos_types::move_utils::MemberId; -use move_core_types::account_address::AccountAddress; -use move_package::source_package::manifest_parser::parse_move_manifest_from_file; -use std::{collections::BTreeMap, path::PathBuf, str::FromStr}; - -const PACKAGE_NAME: &str = "AwesomePackage"; -const HELLO_BLOCKCHAIN: &str = "hello_blockchain"; - -fn aptos_framework_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework") -} - -#[tokio::test] -async fn test_move_compile_flow() { - let mut cli = CliTestFramework::local_new(1); - - cli.init_move_dir(); - let move_dir = cli.move_dir(); - let account = cli.account_id(0).to_hex_literal(); - - let mut package_addresses = BTreeMap::new(); - package_addresses.insert(HELLO_BLOCKCHAIN, "_"); - - cli.init_package( - PACKAGE_NAME.to_string(), - package_addresses, - Some(aptos_framework_dir()), - ) - .await - .expect("Should succeed"); - - // The manifest should work to compile - let mut named_addresses = BTreeMap::new(); - named_addresses.insert(HELLO_BLOCKCHAIN, account.as_str()); - match cli.compile_package(named_addresses.clone(), None).await { - Ok(modules) => assert!(modules.is_empty()), - Err(err) => panic!("Error compiling: {:?}", err), - } - - // Let's check that it's setup correctly - let manifest = parse_move_manifest_from_file(move_dir.join("Move.toml").as_path()) - .expect("Expect a Move.toml file"); - assert_eq!(manifest.package.name.as_str(), PACKAGE_NAME); - // Expect "1.0.0" - assert_eq!(manifest.package.version.0, 1); - assert_eq!(manifest.package.version.1, 0); - assert_eq!(manifest.package.version.2, 0); - - let addresses = manifest.addresses.expect("Expect some addresses"); - assert_eq!(addresses.len(), 1); - let (key, value) = addresses.iter().next().expect("Expect an address"); - assert_eq!(key.as_str(), HELLO_BLOCKCHAIN); - assert!(value.is_none()); - - assert_eq!(manifest.dependencies.len(), 1); - - let dependency = manifest.dependencies.iter().next().unwrap(); - assert_eq!("AptosFramework", dependency.0.to_string()); - - // Now try to compile real code - cli.add_move_files(); - - match cli.compile_package(named_addresses.clone(), None).await { - Ok(modules) => assert!(!modules.is_empty()), - Err(err) => panic!("Error compiling: {:?}", err), - } - - // Run tests to ensure they work too - match cli.test_package(named_addresses.clone(), None).await { - Ok(result) => assert_eq!("Success", result), - Err(err) => panic!("Error testing: {:?}", err), - } -} - -#[tokio::test] -async fn test_move_publish_flow() { - let (_swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(2) - .await; - - let account = cli.account_id(0).to_hex_literal(); - // Setup move package - cli.init_move_dir(); - let mut package_addresses = BTreeMap::new(); - package_addresses.insert(HELLO_BLOCKCHAIN, "_"); - cli.init_package( - PACKAGE_NAME.to_string(), - package_addresses, - Some(aptos_framework_dir()), - ) - .await - .expect("Should succeed"); - cli.add_move_files(); - - cli.wait_for_account(0) - .await - .expect("Should create account"); - info!("Move package dir: {}", cli.move_dir().display()); - - // Let's publish it - let mut named_addresses = BTreeMap::new(); - named_addresses.insert(HELLO_BLOCKCHAIN, account.as_str()); - let _ = match cli.publish_package(0, None, named_addresses, None).await { - Ok(response) => response, - Err(err) => panic!("Should not have failed to publish package {:?}", err), - }; - - // TODO: Verify transaction summary - - // Wrong number of args will definitely fail - let function_id = MemberId::from_str(&format!("{}::message::set_message", account)).unwrap(); - - assert!(cli - .run_function(0, None, function_id.clone(), vec![], vec![]) - .await - .is_err()); - - assert!(cli - .run_function(0, None, function_id, vec!["string:hello_world"], vec![]) - .await - .is_ok()); - - // Now download the package. It will be stored in a directory PACKAGE_NAME inside move_dir. - let _ = match cli - .download_package(0, PACKAGE_NAME.to_owned(), cli.move_dir()) - .await - { - Ok(response) => response, - Err(err) => panic!("Should not have failed to download package {:?}", err), - }; - - // Ensure the downloaded package can build. This is a test that the information is correctly - // roundtripped. - let _ = match BuiltPackage::build(cli.move_dir().join(PACKAGE_NAME), BuildOptions { - named_addresses: std::iter::once(( - HELLO_BLOCKCHAIN.to_owned(), - AccountAddress::from_hex_literal(&account).expect("account address parsable"), - )) - .collect(), - ..BuildOptions::default() - }) { - Ok(response) => response, - Err(err) => panic!( - "Should not have failed to build downloaded package {:?}", - err - ), - }; -} diff --git a/testsuite/smoke-test/src/aptos_cli/validator.rs b/testsuite/smoke-test/src/aptos_cli/validator.rs deleted file mode 100644 index 31e030e99b1c0..0000000000000 --- a/testsuite/smoke-test/src/aptos_cli/validator.rs +++ /dev/null @@ -1,1541 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{create_and_fund_account, MAX_CATCH_UP_WAIT_SECS}, -}; -use aptos::{ - account::create::DEFAULT_FUNDED_COINS, - common::types::TransactionSummary, - node::analyze::{ - analyze_validators::{AnalyzeValidators, EpochStats}, - fetch_metadata::FetchMetadata, - }, - test::{CliTestFramework, ValidatorPerformance}, -}; -use aptos_bitvec::BitVec; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{bls12381, ed25519::Ed25519PrivateKey, x25519, ValidCryptoMaterialStringExt}; -use aptos_forge::{reconfig, wait_for_all_nodes_to_catchup, LocalSwarm, NodeExt, Swarm, SwarmExt}; -use aptos_genesis::config::HostAndPort; -use aptos_keygen::KeyGen; -use aptos_logger::info; -use aptos_rest_client::{Client, State}; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, - network_address::DnsName, - on_chain_config::{ - ConsensusAlgorithmConfig, ConsensusConfigV1, ExecutionConfigV1, LeaderReputationType, - OnChainConsensusConfig, OnChainExecutionConfig, ProposerAndVoterConfig, - ProposerElectionType, TransactionShufflerType, ValidatorSet, - }, - PeerId, -}; -use std::{ - collections::{HashMap, HashSet}, - convert::TryFrom, - fmt::Write, - sync::Arc, - time::Duration, -}; - -#[tokio::test] -async fn test_analyze_validators() { - let (mut swarm, cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_genesis_stake(Arc::new(|_i, genesis_stake_amount| { - *genesis_stake_amount = 100000; - })) - .build_with_cli(0) - .await; - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - tokio::time::sleep(Duration::from_secs(3)).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - tokio::time::sleep(Duration::from_secs(3)).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - cli.analyze_validator_performance(None, None).await.unwrap(); -} - -#[tokio::test] -async fn test_show_validator_set() { - let (swarm, cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(1) - .await; - let validator_set = cli.show_validator_set().await.unwrap(); - - assert_eq!(1, validator_set.active_validators.len()); - assert_eq!(0, validator_set.pending_inactive.len()); - assert_eq!(0, validator_set.pending_active.len()); - assert_eq!( - validator_set - .active_validators - .first() - .unwrap() - .account_address(), - &swarm.validators().next().unwrap().peer_id() - ); -} - -/// One effect of updated leader election config, is that new node/node that was down before -/// will be elected much sooner than before. -/// This function checks for a node that is down and not being elected, how long it takes for it -/// to start voting (after being up), and then how long it takes for it to be elected. -async fn check_vote_to_elected(swarm: &mut LocalSwarm) -> (Option, Option) { - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let (address_off, rest_client_off) = swarm - .validators() - .nth(1) - .map(|v| (v.peer_id(), v.rest_client())) - .unwrap(); - - let (_address_after_off, rest_client_after_off) = swarm - .validators() - .nth(2) - .map(|v| (v.peer_id(), v.rest_client())) - .unwrap(); - - rest_client_off - .set_failpoint("consensus::send::any".to_string(), "100%return".to_string()) - .await - .unwrap(); - - // clear leader reputation history, so we stop electing down node altogether - for _ in 0..5 { - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - } - - tokio::time::sleep(Duration::from_secs(10)).await; - rest_client_off - .set_failpoint("consensus::send::any".to_string(), "off".to_string()) - .await - .unwrap(); - - let epoch = reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await - .epoch; - - // Turn off a different node, to force votes from recently down node being required for forming consnensus - rest_client_after_off - .set_failpoint("consensus::send::any".to_string(), "100%return".to_string()) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_secs(60)).await; - - rest_client_after_off - .set_failpoint("consensus::send::any".to_string(), "off".to_string()) - .await - .unwrap(); - - let events = FetchMetadata::fetch_new_block_events(&rest_client, Some(epoch as i64), None) - .await - .unwrap(); - assert_eq!(events.len(), 1); - - let info = events.first().unwrap(); - let off_index = info - .validators - .iter() - .find(|v| v.address == address_off) - .unwrap() - .validator_index; - let mut first_vote = None; - let mut first_elected = None; - for event in info.blocks.iter() { - let previous_block_votes_bitvec: BitVec = - event.event.previous_block_votes_bitvec().clone().into(); - if first_vote.is_none() && previous_block_votes_bitvec.is_set(off_index) { - first_vote = Some(event.event.round()); - } - - if first_elected.is_none() && event.event.proposer() == address_off { - first_elected = Some(event.event.round()); - } - } - (first_vote, first_elected) -} - -#[tokio::test] -#[ignore] -async fn test_onchain_config_change() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_init_config(Arc::new(|_, conf, _| { - // reduce timeout, as we will have dead node during rounds - conf.consensus.round_initial_timeout_ms = 400; - conf.consensus.quorum_store_poll_time_ms = 100; - conf.api.failpoints_enabled = true; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - let inner = match genesis_config.consensus_config.clone() { - OnChainConsensusConfig::V1(inner) => inner, - OnChainConsensusConfig::V2(inner) => inner, - OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::Jolteon { main, .. }, - .. - } => main, - _ => unimplemented!(), - }; - - let leader_reputation_type = - if let ProposerElectionType::LeaderReputation(leader_reputation_type) = - inner.proposer_election_type - { - leader_reputation_type - } else { - panic!() - }; - let proposer_and_voter_config = match &leader_reputation_type { - LeaderReputationType::ProposerAndVoter(_) => panic!(), - LeaderReputationType::ProposerAndVoterV2(proposer_and_voter_config) => { - proposer_and_voter_config - }, - }; - let new_consensus_config = OnChainConsensusConfig::V1(ConsensusConfigV1 { - proposer_election_type: ProposerElectionType::LeaderReputation( - LeaderReputationType::ProposerAndVoter(ProposerAndVoterConfig { - proposer_window_num_validators_multiplier: 20, - // reduce max epoch history to speed up the test. - use_history_from_previous_epoch_max_count: 2, - // make test not flaky, by making unlikely selections extremely unlikely: - active_weight: 1000000, - ..*proposer_and_voter_config - }), - ), - ..inner - }); - genesis_config.consensus_config = new_consensus_config; - })) - .with_aptos() - .build_with_cli(0) - .await; - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let current_consensus_config: OnChainConsensusConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - let inner = match current_consensus_config { - OnChainConsensusConfig::V1(inner) => inner, - OnChainConsensusConfig::V2(inner) => inner, - _ => unimplemented!(), - }; - let leader_reputation_type = - if let ProposerElectionType::LeaderReputation(leader_reputation_type) = - inner.proposer_election_type - { - leader_reputation_type - } else { - panic!() - }; - let proposer_and_voter_config = match &leader_reputation_type { - LeaderReputationType::ProposerAndVoterV2(_) => panic!(), - LeaderReputationType::ProposerAndVoter(proposer_and_voter_config) => { - proposer_and_voter_config - }, - }; - let new_consensus_config = OnChainConsensusConfig::V1(ConsensusConfigV1 { - proposer_election_type: ProposerElectionType::LeaderReputation( - LeaderReputationType::ProposerAndVoterV2(*proposer_and_voter_config), - ), - ..inner - }); - - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_blob(&bcs::to_bytes(&new_consensus_config).unwrap()) - ); - - // confirm with old configs, validator will need to wait quite a bit from voting to being elected - let (first_vote_old, first_elected_old) = check_vote_to_elected(&mut swarm).await; - println!( - "With old config: {:?} to {:?}", - first_vote_old, first_elected_old - ); - - println!( - "Epoch before : {}", - rest_client - .get_ledger_information() - .await - .unwrap() - .into_inner() - .epoch - ); - cli.run_script(root_cli_index, &update_consensus_config_script) - .await - .unwrap(); - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - swarm - .wait_for_all_nodes_to_catchup_to_next(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - println!( - "Epoch after : {}", - rest_client - .get_ledger_information() - .await - .unwrap() - .into_inner() - .epoch - ); - - // confirm with new configs, validator doesn't wait much from voting to being elected - let (first_vote_new, first_elected_new) = check_vote_to_elected(&mut swarm).await; - println!( - "With new config: {:?} to {:?}", - first_vote_new, first_elected_new - ); - - cli.analyze_validator_performance(Some(0), None) - .await - .unwrap(); - - // Node that is down, should start voting very fast - assert!(first_vote_old.unwrap() < 20); - assert!(first_vote_new.unwrap() < 20); - // In old config, we expect there to be a lot of rounds before node gets elected as leader - assert!(first_elected_old.unwrap() > 80); - // In updated config, we expect it to be elected pretty fast. - // There is necessary 20 rounds delay due to exclude_round, and then only a few more rounds. - assert!(first_elected_new.unwrap() < 40); -} - -#[tokio::test] -#[ignore] -// This test is ignored because it is very long running -async fn test_onchain_shuffling_change() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(2) - .with_aptos() - .build_with_cli(0) - .await; - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let current_execution_config: OnChainExecutionConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::execution_config::ExecutionConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - assert_eq!( - current_execution_config.transaction_shuffler_type(), - TransactionShufflerType::SenderAwareV2(32), - ); - - assert_reordering(&mut swarm, true).await; - - let execution_config_with_shuffling = OnChainExecutionConfig::V1(ExecutionConfigV1 { - transaction_shuffler_type: TransactionShufflerType::NoShuffling, - }); - - let update_execution_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::execution_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - execution_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_blob(&bcs::to_bytes(&execution_config_with_shuffling).unwrap()) - ); - - cli.run_script(root_cli_index, &update_execution_config_script) - .await - .unwrap(); - - let updated_execution_config: OnChainExecutionConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::execution_config::ExecutionConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - assert_eq!( - updated_execution_config.transaction_shuffler_type(), - TransactionShufflerType::NoShuffling, - ); - - assert_reordering(&mut swarm, false).await; -} - -async fn assert_reordering(swarm: &mut dyn Swarm, expected_reordering: bool) { - swarm - .aptos_public_info() - .sync_root_account_sequence_number() - .await; - let transaction_factory = swarm.aptos_public_info().transaction_factory(); - - let clients = swarm.get_all_nodes_clients_with_names(); - - let dst = create_and_fund_account(swarm, 10000000000).await; - - let mut accounts = vec![]; - let mut txns = vec![]; - for _ in 0..2 { - let account = create_and_fund_account(swarm, 10000000000).await; - - for _ in 0..5 { - let txn = account.sign_with_transaction_builder( - transaction_factory.payload(aptos_stdlib::aptos_coin_transfer(dst.address(), 10)), - ); - txns.push(txn); - } - accounts.push(account); - } - - let result = clients[0] - .1 - .submit_batch_bcs(&txns) - .await - .unwrap() - .into_inner(); - info!("result: {:?}", result); - - for txn in &txns { - clients[0].1.wait_for_signed_transaction(txn).await.unwrap(); - } - - wait_for_all_nodes_to_catchup(&clients, Duration::from_secs(30)) - .await - .unwrap(); - - let committed_order = clients[0] - .1 - .get_transactions_bcs(None, Some(1000)) - .await - .unwrap() - .into_inner(); - - info!( - "dst: {}, senders: {:?}", - dst.address(), - accounts.iter().map(|a| a.address()).collect::>() - ); - let mut block_txns = vec![]; - for txn in committed_order { - match txn.transaction { - aptos_types::transaction::Transaction::UserTransaction(txn) => { - info!("from {}, seq_num {}", txn.sender(), txn.sequence_number()); - block_txns.push(txn); - }, - aptos_types::transaction::Transaction::BlockMetadata(b) => { - info!("block metadata {}", b.round()); - - let senders = accounts.iter().map(|a| a.address()).collect::>(); - let mut changes = 0; - for i in 1..block_txns.len() { - if block_txns[i - 1].sender() != block_txns[i].sender() - && senders.contains(&block_txns[i].sender()) - { - changes += 1; - } - } - info!("block_txns.len: {}, changes: {}", block_txns.len(), changes); - - if changes > 1 { - assert!(expected_reordering, "changes: {}", changes); - } - if block_txns.len() >= 4 && changes == 1 { - assert!(!expected_reordering, "changes: {}", changes) - } - block_txns.clear(); - }, - _ => {}, - } - } -} - -pub(crate) fn generate_blob(data: &[u8]) -> String { - let mut buf = String::new(); - - write!(buf, "vector[").unwrap(); - for (i, b) in data.iter().enumerate() { - if i % 20 == 0 { - if i > 0 { - writeln!(buf).unwrap(); - } - } else { - write!(buf, " ").unwrap(); - } - write!(buf, "{}u8,", b).unwrap(); - } - write!(buf, "]").unwrap(); - buf -} - -#[tokio::test] -async fn test_large_total_stake() { - // just barelly below u64::MAX - const BASE: u64 = 10_000_000_000_000_000_000; - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_init_genesis_stake(Arc::new(|_, genesis_stake_amount| { - // make sure we have quorum - *genesis_stake_amount = BASE; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.epoch_duration_secs = 4; - genesis_config.recurring_lockup_duration_secs = 4; - genesis_config.voting_duration_secs = 3; - })) - .build_with_cli(0) - .await; - - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut keygen = KeyGen::from_os_rng(); - let (validator_cli_index, keys) = init_validator_account(&mut cli, &mut keygen, None).await; - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - - cli.initialize_validator( - validator_cli_index, - keys.consensus_public_key(), - keys.consensus_proof_of_possession(), - HostAndPort { - host: dns_name("0.0.0.0"), - port: 1234, - }, - keys.network_public_key(), - ) - .await - .unwrap(); - - cli.join_validator_set(validator_cli_index, None) - .await - .unwrap(); - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_eq!( - get_validator_state(&cli, validator_cli_index).await, - ValidatorState::ACTIVE - ); - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_nodes_rewards() { - // with 10% APY, BASE amount gives 100 rewards per second - const BASE: u64 = 3600u64 * 24 * 365 * 10 * 100; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_init_config(Arc::new(|_, conf, _| { - // reduce timeout, as we will have dead node during rounds - conf.consensus.round_initial_timeout_ms = 200; - conf.consensus.quorum_store_poll_time_ms = 100; - conf.api.failpoints_enabled = true; - })) - .with_init_genesis_stake(Arc::new(|i, genesis_stake_amount| { - // make sure we have quorum - *genesis_stake_amount = if i < 2 { 10 * BASE } else { BASE }; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.epoch_duration_secs = 4; - genesis_config.recurring_lockup_duration_secs = 4; - genesis_config.voting_duration_secs = 3; - genesis_config.rewards_apy_percentage = 10; - })) - .build_with_cli(0) - .await; - - let transaction_factory = swarm.chain_info().transaction_factory(); - - let mut validators: Vec<_> = swarm.validators().collect(); - validators.sort_by_key(|v| v.name()); - - let validator_cli_indices = validators - .iter() - .map(|validator| { - cli.add_account_to_cli( - validator - .account_private_key() - .as_ref() - .unwrap() - .private_key(), - ) - }) - .collect::>(); - let rest_clients = validators - .iter() - .map(|validator| validator.rest_client()) - .collect::>(); - let addresses = validators - .iter() - .map(|validator| validator.peer_id()) - .collect::>(); - - println!("{:?}", addresses.iter().enumerate().collect::>()); - - async fn get_state_and_validator_set(rest_client: &Client) -> (State, HashMap) { - let (validator_set, state): (ValidatorSet, State) = rest_client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::stake::ValidatorSet") - .await - .unwrap() - .into_parts(); - let validator_to_voting_power = validator_set - .active_validators - .iter() - .chain(validator_set.pending_inactive.iter()) - .map(|v| (v.account_address, v.consensus_voting_power())) - .collect::>(); - (state, validator_to_voting_power) - } - - println!( - "{:?}", - get_state_and_validator_set(&rest_clients[0]).await.1 - ); - - rest_clients[2] - .set_failpoint( - "consensus::send::broadcast_proposal".to_string(), - "100%return".to_string(), - ) - .await - .unwrap(); - - reconfig( - &rest_clients[0], - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - let (start_2_failures_state, start_2_failures_validator_set) = - get_state_and_validator_set(&rest_clients[0]).await; - println!( - "Node 2 ({}) starts failing: at epoch {} and version {}, set: {:?}", - addresses[2], - start_2_failures_state.epoch, - start_2_failures_state.version, - start_2_failures_validator_set - ); - - tokio::time::sleep(Duration::from_secs(5)).await; - - reconfig( - &rest_clients[0], - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - cli.fund_account(validator_cli_indices[3], Some(30000)) - .await - .unwrap(); - - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_clients[3]) - .await - .unwrap(); - - cli.leave_validator_set(validator_cli_indices[3], None) - .await - .unwrap(); - - reconfig( - &rest_clients[0], - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - let (start_3_left_state, start_3_left_validator_set) = - get_state_and_validator_set(&rest_clients[0]).await; - println!( - "Node 3 ({}) leaves validator set: at epoch {} and version {}, set: {:?}", - addresses[3], - start_3_left_state.epoch, - start_3_left_state.version, - start_3_left_validator_set - ); - let end_2_failures_epoch = start_3_left_state.epoch; - rest_clients[2] - .set_failpoint( - "consensus::send::broadcast_proposal".to_string(), - "20%return".to_string(), - ) - .await - .unwrap(); - - tokio::time::sleep(Duration::from_secs(5)).await; - reconfig( - &rest_clients[0], - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - tokio::time::sleep(Duration::from_secs(3)).await; - - reconfig( - &rest_clients[0], - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - let (end_state, end_validator_set) = get_state_and_validator_set(&rest_clients[0]).await; - println!( - "END: at epoch {} and version {} set: {:?}", - end_state.epoch, end_state.version, end_validator_set - ); - - cli.analyze_validator_performance(None, None).await.unwrap(); - - let epochs = FetchMetadata::fetch_new_block_events(&rest_clients[0], None, None) - .await - .unwrap(); - - let mut previous_stats: Option = None; - for epoch_info in epochs { - println!( - "Processing epoch {} for versions [{}, {}]", - epoch_info.epoch, - epoch_info.blocks.first().unwrap().version, - epoch_info.blocks.last().unwrap().version - ); - if let Some(previous) = previous_stats { - let mut estimates = Vec::new(); - for cur_validator in &epoch_info.validators { - let prev_stats = previous - .validator_stats - .get(&cur_validator.address) - .unwrap(); - if prev_stats.proposal_successes == 0 { - assert_eq!(cur_validator.voting_power, prev_stats.voting_power); - } else { - assert!(cur_validator.voting_power > prev_stats.voting_power, "in epoch {} voting power for {} didn't increase with successful proposals (from {} to {})", epoch_info.epoch - 1, cur_validator.address, prev_stats.voting_power, cur_validator.voting_power); - let earning = (cur_validator.voting_power - prev_stats.voting_power) as f64 - / prev_stats.voting_power as f64; - let failure_rate = prev_stats.failure_rate() as f64; - let epoch_reward_estimate = earning / (1.0 - failure_rate); - println!( - "{}: {} / {} = {}, prev_voting_power = {}", - cur_validator.address, - earning, - failure_rate, - epoch_reward_estimate, - prev_stats.voting_power - ); - estimates.push(epoch_reward_estimate); - } - } - if !estimates.is_empty() { - assert!( - estimates.iter().copied().fold(f64::NEG_INFINITY, f64::max) - / estimates.iter().copied().fold(f64::INFINITY, f64::min) - < 1.1, - "in epoch {}, estimated epoch reward rate differs: {:?}", - epoch_info.epoch - 1, - estimates - ); - } - } - - let last_version = epoch_info.blocks.iter().map(|b| b.version).max().unwrap(); - let epoch_stats = AnalyzeValidators::analyze(&epoch_info.blocks, &epoch_info.validators); - - if epoch_info.epoch >= start_3_left_state.epoch { - assert!( - !epoch_stats.validator_stats.contains_key(&addresses[3]), - "Epoch {}, node {} shouldn't be present", - epoch_info.epoch, - addresses[3] - ); - } - - if epoch_info.epoch >= start_2_failures_state.epoch - && epoch_info.epoch < end_2_failures_epoch - { - assert_eq!( - 0, - epoch_stats - .validator_stats - .get(&addresses[2]) - .unwrap() - .proposal_successes, - "Epoch {}, node {} shouldn't have any successful proposals", - epoch_info.epoch, - addresses[2] - ); - } - - let mut epoch_perf = serde_json::from_value::( - rest_clients[0] - .get_account_resource_at_version( - PeerId::ONE, - "0x1::stake::ValidatorPerformance", - last_version, - ) - .await - .unwrap() - .into_inner() - .unwrap() - .data, - ) - .unwrap(); - - println!( - "ValidatorPerformance for epoch {} at version {}: {:?}", - epoch_info.epoch, last_version, epoch_perf - ); - - // If epoch change happens with the BlockMetadata block, we don't have the last ValidatorPerformance - // for that epoch, so we take one before it. - let target_stats = if epoch_perf - .validators - .iter() - .map(|v| v.successful_proposals() + v.failed_proposals()) - .sum::() - == 0 - { - println!( - "Don't have latest perf, doing one before, at version {}", - last_version - 1 - ); - epoch_perf = serde_json::from_value::( - rest_clients[0] - .get_account_resource_at_version( - PeerId::ONE, - "0x1::stake::ValidatorPerformance", - last_version - 1, - ) - .await - .unwrap() - .into_inner() - .unwrap() - .data, - ) - .unwrap(); - AnalyzeValidators::analyze( - &epoch_info.blocks[..epoch_info.blocks.len() - 1], - &epoch_info.validators, - ) - } else { - epoch_stats.clone() - }; - - for info in epoch_info.validators { - let v_stats = target_stats.validator_stats.get(&info.address).unwrap(); - let v_perf = epoch_perf - .validators - .get(info.validator_index as usize) - .unwrap(); - assert_eq!( - v_stats.proposal_successes, - v_perf.successful_proposals(), - "Epoch {}\n info {:?}\n stats {:?}\n perf {:?}", - epoch_info.epoch, - info, - epoch_stats.validator_stats, - epoch_perf.validators - ); - assert_eq!( - v_stats.proposal_failures, - v_perf.failed_proposals(), - "Epoch {}\n info {:?}\n stats {:?}\n perf {:?}", - epoch_info.epoch, - info, - epoch_stats.validator_stats, - epoch_perf.validators - ); - } - - previous_stats = Some(epoch_stats); - } -} - -#[tokio::test] -async fn test_register_and_update_validator() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(0) - .await; - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut keygen = KeyGen::from_os_rng(); - let (validator_cli_index, keys) = init_validator_account(&mut cli, &mut keygen, None).await; - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - - assert!(cli - .show_validator_config(validator_cli_index) - .await - .is_err()); // validator not registered yet - - let port = 1234; - cli.initialize_validator( - validator_cli_index, - keys.consensus_public_key(), - keys.consensus_proof_of_possession(), - HostAndPort { - host: dns_name("0.0.0.0"), - port, - }, - keys.network_public_key(), - ) - .await - .unwrap(); - - let validator_config = cli - .show_validator_config(validator_cli_index) - .await - .unwrap(); - assert_eq!( - validator_config.consensus_public_key, - keys.consensus_public_key() - .to_encoded_string() - .unwrap() - .as_bytes() - .to_vec() - ); - - let new_port = 5678; - let new_network_private_key = keygen.generate_x25519_private_key().unwrap(); - - cli.update_validator_network_addresses( - validator_cli_index, - None, - HostAndPort { - host: dns_name("0.0.0.0"), - port: new_port, - }, - new_network_private_key.public_key(), - ) - .await - .unwrap(); - - let validator_config = cli - .show_validator_config(validator_cli_index) - .await - .unwrap(); - - let address_new = validator_config - .validator_network_addresses() - .unwrap() - .into_iter() - .next() - .unwrap(); - assert_eq!( - address_new.find_noise_proto().unwrap(), - new_network_private_key.public_key() - ); - assert_eq!(address_new.find_port().unwrap(), new_port); - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - // because we haven't joined the validator set yet, we shouldn't be there - let validator_set = cli.show_validator_set().await.unwrap(); - assert_eq!(1, validator_set.active_validators.len()); - assert_eq!(0, validator_set.pending_inactive.len()); - assert_eq!(0, validator_set.pending_active.len()); -} - -#[tokio::test] -async fn test_join_and_leave_validator() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_i, conf, _| { - // reduce timeout, as we will have dead node during rounds - conf.consensus.round_initial_timeout_ms = 200; - conf.consensus.quorum_store_poll_time_ms = 100; - })) - .with_init_genesis_stake(Arc::new(|_i, genesis_stake_amount| { - *genesis_stake_amount = 100000; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.epoch_duration_secs = 5; - genesis_config.recurring_lockup_duration_secs = 10; - genesis_config.voting_duration_secs = 5; - })) - .build_with_cli(0) - .await; - - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut keygen = KeyGen::from_os_rng(); - let (validator_cli_index, keys) = - init_validator_account(&mut cli, &mut keygen, Some(DEFAULT_FUNDED_COINS * 3)).await; - let mut gas_used = 0; - - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - - let port = 1234; - gas_used += get_gas( - cli.initialize_validator( - validator_cli_index, - keys.consensus_public_key(), - keys.consensus_proof_of_possession(), - HostAndPort { - host: dns_name("0.0.0.0"), - port, - }, - keys.network_public_key(), - ) - .await - .unwrap(), - ); - - assert_validator_set_sizes(&cli, 1, 0, 0).await; - - cli.assert_account_balance_now(validator_cli_index, (3 * DEFAULT_FUNDED_COINS) - gas_used) - .await; - - let stake_coins = 7; - gas_used += get_gas( - cli.add_stake(validator_cli_index, stake_coins) - .await - .unwrap()[0] - .clone(), - ); - - cli.assert_account_balance_now( - validator_cli_index, - (3 * DEFAULT_FUNDED_COINS) - stake_coins - gas_used, - ) - .await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_validator_set_sizes(&cli, 1, 0, 0).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_validator_set_sizes(&cli, 1, 0, 0).await; - - gas_used += get_gas( - cli.join_validator_set(validator_cli_index, None) - .await - .unwrap(), - ); - - assert_validator_set_sizes(&cli, 1, 1, 0).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_validator_set_sizes(&cli, 2, 0, 0).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - gas_used += get_gas( - cli.leave_validator_set(validator_cli_index, None) - .await - .unwrap(), - ); - - assert_validator_set_sizes(&cli, 1, 0, 1).await; - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_validator_set_sizes(&cli, 1, 0, 0).await; - - cli.assert_account_balance_now( - validator_cli_index, - (3 * DEFAULT_FUNDED_COINS) - stake_coins - gas_used, - ) - .await; - - let unlock_stake = 3; - - // Unlock stake. - gas_used += get_gas( - cli.unlock_stake(validator_cli_index, unlock_stake) - .await - .unwrap()[0] - .clone(), - ); - - // Conservatively wait until the recurring lockup is over. - tokio::time::sleep(Duration::from_secs(10)).await; - - let withdraw_stake = 2; - gas_used += get_gas( - cli.withdraw_stake(validator_cli_index, withdraw_stake) - .await - .unwrap() - .remove(0), - ); - - cli.assert_account_balance_now( - validator_cli_index, - (3 * DEFAULT_FUNDED_COINS) - stake_coins + withdraw_stake - gas_used, - ) - .await; -} - -#[tokio::test] -async fn test_owner_create_and_delegate_flow() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_i, conf, _| { - // reduce timeout, as we will have dead node during rounds - conf.consensus.round_initial_timeout_ms = 200; - conf.consensus.quorum_store_poll_time_ms = 100; - })) - .with_init_genesis_stake(Arc::new(|_i, genesis_stake_amount| { - // enough for quorum - *genesis_stake_amount = 5000000; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.epoch_duration_secs = 5; - genesis_config.recurring_lockup_duration_secs = 10; - genesis_config.voting_duration_secs = 5; - genesis_config.min_stake = 500000 - })) - .build_with_cli(0) - .await; - - let transaction_factory = swarm.chain_info().transaction_factory(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut keygen = KeyGen::from_os_rng(); - - let owner_initial_coins = 20000000; - let voter_initial_coins = 1000000; - let operator_initial_coins = 1000000; - - // Owner of the coins receives coins - let owner_cli_index = cli - .create_cli_account_from_faucet( - keygen.generate_ed25519_private_key(), - Some(owner_initial_coins), - ) - .await - .unwrap(); - println!("owner CLI index: {}", owner_cli_index); - - cli.assert_account_balance_now(owner_cli_index, owner_initial_coins) - .await; - - // Faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - - let operator_keys = ValidatorNodeKeys::new(&mut keygen); - let voter_cli_index = cli - .create_cli_account(keygen.generate_ed25519_private_key(), owner_cli_index) - .await - .unwrap(); - let operator_cli_index = cli - .create_cli_account(operator_keys.account_private_key.clone(), owner_cli_index) - .await - .unwrap(); - - // Fetch amount of gas used for the above account creations - let mut owner_gas = - owner_initial_coins - cli.account_balance_now(owner_cli_index).await.unwrap(); - println!("owner_gas1: {}", owner_gas); - - // Voter and operator start with no coins - // Owner needs to send small amount of coins to operator and voter, to create their accounts and so they have enough for gas fees. - owner_gas += cli - .transfer_coins(owner_cli_index, voter_cli_index, voter_initial_coins, None) - .await - .unwrap() - .octa_spent(); - owner_gas += cli - .transfer_coins( - owner_cli_index, - operator_cli_index, - operator_initial_coins, - None, - ) - .await - .unwrap() - .octa_spent(); - - cli.assert_account_balance_now( - owner_cli_index, - owner_initial_coins - voter_initial_coins - operator_initial_coins - owner_gas, - ) - .await; - cli.assert_account_balance_now(voter_cli_index, voter_initial_coins) - .await; - cli.assert_account_balance_now(operator_cli_index, operator_initial_coins) - .await; - - let stake_amount = 1000000; - let mut operator_gas = 0; - owner_gas += get_gas( - cli.initialize_stake_owner( - owner_cli_index, - stake_amount, - Some(voter_cli_index), - Some(operator_cli_index), - ) - .await - .unwrap(), - ); - - println!("before4"); - cli.assert_account_balance_now( - owner_cli_index, - owner_initial_coins - - voter_initial_coins - - operator_initial_coins - - stake_amount - - owner_gas, - ) - .await; - println!("after4"); - - assert_validator_set_sizes(&cli, 1, 0, 0).await; - assert_eq!( - get_validator_state(&cli, owner_cli_index).await, - ValidatorState::NONE - ); - - let port = 6543; - - operator_gas += get_gas( - cli.update_consensus_key( - operator_cli_index, - Some(owner_cli_index), - operator_keys.consensus_public_key(), - operator_keys.consensus_proof_of_possession(), - ) - .await - .unwrap(), - ); - - operator_gas += get_gas( - cli.update_validator_network_addresses( - operator_cli_index, - Some(owner_cli_index), - HostAndPort { - host: dns_name("0.0.0.0"), - port, - }, - operator_keys.network_public_key(), - ) - .await - .unwrap(), - ); - - println!("before5"); - cli.assert_account_balance_now(operator_cli_index, operator_initial_coins - operator_gas) - .await; - println!("after5"); - - cli.join_validator_set(operator_cli_index, Some(owner_cli_index)) - .await - .unwrap(); - - let owner_state = get_validator_state(&cli, owner_cli_index).await; - if owner_state == ValidatorState::JOINING { - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_eq!( - get_validator_state(&cli, owner_cli_index).await, - ValidatorState::ACTIVE - ); - } else { - assert_eq!(owner_state, ValidatorState::ACTIVE); - } - - let new_operator_keys = ValidatorNodeKeys::new(&mut keygen); - let new_voter_cli_index = cli.add_account_to_cli(keygen.generate_ed25519_private_key()); - let new_operator_cli_index = cli - .create_cli_account( - new_operator_keys.account_private_key.clone(), - owner_cli_index, - ) - .await - .unwrap(); - - cli.set_delegated_voter(owner_cli_index, new_voter_cli_index) - .await - .unwrap(); - cli.set_operator(owner_cli_index, new_operator_cli_index) - .await - .unwrap(); - - cli.transfer_coins( - owner_cli_index, - new_operator_cli_index, - operator_initial_coins, - None, - ) - .await - .unwrap(); - - cli.leave_validator_set(new_operator_cli_index, Some(owner_cli_index)) - .await - .unwrap(); - - let owner_state = get_validator_state(&cli, owner_cli_index).await; - if owner_state == ValidatorState::LEAVING { - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - assert_eq!( - get_validator_state(&cli, owner_cli_index).await, - ValidatorState::NONE - ); - } else { - assert_eq!(owner_state, ValidatorState::NONE); - } - - cli.join_validator_set(operator_cli_index, Some(owner_cli_index)) - .await - .unwrap_err(); - assert_eq!( - get_validator_state(&cli, owner_cli_index).await, - ValidatorState::NONE - ); -} - -fn dns_name(addr: &str) -> DnsName { - DnsName::try_from(addr.to_string()).unwrap() -} - -pub struct ValidatorNodeKeys { - account_private_key: Ed25519PrivateKey, - network_private_key: x25519::PrivateKey, - consensus_private_key: bls12381::PrivateKey, -} - -impl ValidatorNodeKeys { - pub fn new(keygen: &mut KeyGen) -> Self { - Self { - account_private_key: keygen.generate_ed25519_private_key(), - network_private_key: keygen.generate_x25519_private_key().unwrap(), - consensus_private_key: keygen.generate_bls12381_private_key(), - } - } - - pub fn network_public_key(&self) -> x25519::PublicKey { - self.network_private_key.public_key() - } - - pub fn consensus_public_key(&self) -> bls12381::PublicKey { - bls12381::PublicKey::from(&self.consensus_private_key) - } - - pub fn consensus_proof_of_possession(&self) -> bls12381::ProofOfPossession { - bls12381::ProofOfPossession::create(&self.consensus_private_key) - } -} - -pub async fn init_validator_account( - cli: &mut CliTestFramework, - keygen: &mut KeyGen, - amount: Option, -) -> (usize, ValidatorNodeKeys) { - let validator_node_keys = ValidatorNodeKeys::new(keygen); - let validator_cli_index = cli - .create_cli_account_from_faucet(validator_node_keys.account_private_key.clone(), amount) - .await - .unwrap(); - - cli.assert_account_balance_now(validator_cli_index, amount.unwrap_or(DEFAULT_FUNDED_COINS)) - .await; - (validator_cli_index, validator_node_keys) -} - -async fn assert_validator_set_sizes( - cli: &CliTestFramework, - active: usize, - joining: usize, - leaving: usize, -) { - let validator_set = cli.show_validator_set().await.unwrap(); - assert_eq!( - active, - validator_set.active_validators.len(), - "{:?}", - validator_set - ); - assert_eq!( - joining, - validator_set.pending_active.len(), - "{:?}", - validator_set - ); - assert_eq!( - leaving, - validator_set.pending_inactive.len(), - "{:?}", - validator_set - ); -} - -#[derive(Debug, PartialEq, Eq)] -enum ValidatorState { - ACTIVE, - JOINING, - LEAVING, - NONE, -} - -async fn get_validator_state(cli: &CliTestFramework, pool_index: usize) -> ValidatorState { - let validator_set = cli.show_validator_set().await.unwrap(); - let pool_address = cli.account_id(pool_index); - - for (state, list) in [ - (ValidatorState::ACTIVE, &validator_set.active_validators), - (ValidatorState::JOINING, &validator_set.pending_active), - (ValidatorState::LEAVING, &validator_set.pending_inactive), - ] { - if list.iter().any(|info| info.account_address == pool_address) { - return state; - } - } - ValidatorState::NONE -} - -fn get_gas(transaction: TransactionSummary) -> u64 { - transaction.gas_used.unwrap() * transaction.gas_unit_price.unwrap() -} diff --git a/testsuite/smoke-test/src/client.rs b/testsuite/smoke-test/src/client.rs deleted file mode 100644 index 3c3f7b512fc98..0000000000000 --- a/testsuite/smoke-test/src/client.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::new_local_swarm_with_aptos, - test_utils::{ - assert_balance, check_create_mint_transfer, create_and_fund_account, transfer_coins, - MAX_HEALTHY_WAIT_SECS, - }, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::{NodeExt, Swarm}; -use std::time::{Duration, Instant}; - -#[tokio::test] -async fn test_create_mint_transfer_block_metadata() { - let mut swarm = new_local_swarm_with_aptos(1).await; - // This script does 4 transactions - check_create_mint_transfer(&mut swarm).await; - - // Test if we commit not only user transactions but also block metadata transactions, - // assert committed version > # of user transactions - let client = swarm.validators().next().unwrap().rest_client(); - let version = client - .get_ledger_information() - .await - .unwrap() - .into_inner() - .version; - assert!( - version > 4, - "BlockMetadata txn not produced, current version: {}", - version - ); -} - -#[tokio::test] -async fn test_basic_fault_tolerance() { - // A configuration with 4 validators should tolerate single node failure. - let mut swarm = new_local_swarm_with_aptos(4).await; - swarm.validators_mut().nth(3).unwrap().stop(); - check_create_mint_transfer(&mut swarm).await; -} - -#[tokio::test] -async fn test_basic_restartability() { - let mut swarm = new_local_swarm_with_aptos(4).await; - let client = swarm.validators().next().unwrap().rest_client(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - let mut account_0 = create_and_fund_account(&mut swarm, 100).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - transfer_coins( - &client, - &transaction_factory, - &mut account_0, - &account_1, - 10, - ) - .await; - assert_balance(&client, &account_0, 90).await; - assert_balance(&client, &account_1, 20).await; - - let validator = swarm.validators_mut().next().unwrap(); - validator.restart().await.unwrap(); - validator - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - - assert_balance(&client, &account_0, 90).await; - assert_balance(&client, &account_1, 20).await; - - transfer_coins( - &client, - &transaction_factory, - &mut account_0, - &account_1, - 10, - ) - .await; - assert_balance(&client, &account_0, 80).await; - assert_balance(&client, &account_1, 30).await; -} - -#[tokio::test] -async fn test_concurrent_transfers_single_node() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let client = swarm.validators().next().unwrap().rest_client(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - let mut account_0 = create_and_fund_account(&mut swarm, 100).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - assert_balance(&client, &account_0, 100).await; - assert_balance(&client, &account_1, 10).await; - - for _ in 0..20 { - let txn = account_0.sign_with_transaction_builder( - transaction_factory.payload(aptos_stdlib::aptos_coin_transfer(account_1.address(), 1)), - ); - client.submit_and_wait(&txn).await.unwrap(); - } - transfer_coins(&client, &transaction_factory, &mut account_0, &account_1, 1).await; - // assert_balance(&client, &account_0, 79).await; - assert_balance(&client, &account_1, 31).await; -} - -#[tokio::test] -async fn test_latest_events_and_transactions() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let client = swarm.validators().next().unwrap().rest_client(); - let start_events = client - .get_new_block_events_bcs(None, Some(2)) - .await - .unwrap() - .into_inner(); - let start_transations = client - .get_transactions(None, Some(2)) - .await - .unwrap() - .into_inner(); - - create_and_fund_account(&mut swarm, 100).await; - let cur_events = client - .get_new_block_events_bcs(None, Some(2)) - .await - .unwrap() - .into_inner(); - let (cur_transations, cur_ledger) = client - .get_transactions(None, Some(2)) - .await - .unwrap() - .into_parts(); - - assert!(start_events[0].event.round() < cur_events[0].event.round()); - assert!(cur_events[0].event.round() < cur_events[1].event.round()); - assert_eq!(cur_events.len(), 2); - - assert!(start_transations[0].version() < cur_transations[0].version()); - assert!(cur_transations[0].version() < cur_transations[1].version()); - assert_eq!(cur_transations.len(), 2); - assert_eq!(cur_transations[1].version().unwrap(), cur_ledger.version); -} diff --git a/testsuite/smoke-test/src/consensus/consensus_fault_tolerance.rs b/testsuite/smoke-test/src/consensus/consensus_fault_tolerance.rs deleted file mode 100644 index dff8acfb8f0e7..0000000000000 --- a/testsuite/smoke-test/src/consensus/consensus_fault_tolerance.rs +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{create_and_fund_account, transfer_coins_non_blocking}, -}; -use aptos_forge::{ - test_utils::consensus_utils::{ - no_failure_injection, test_consensus_fault_tolerance, FailPointFailureInjection, NodeState, - }, - LocalSwarm, Swarm, SwarmExt, -}; -use aptos_logger::info; -use rand::{self, rngs::SmallRng, Rng, SeedableRng}; -use std::{ - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::{Duration, Instant}, -}; - -pub async fn create_swarm(num_nodes: usize, max_block_txns: u64) -> LocalSwarm { - let swarm = SwarmBuilder::new_local(num_nodes) - .with_init_config(Arc::new(move |_, config, _| { - config.api.failpoints_enabled = true; - config.consensus.max_sending_block_txns = max_block_txns; - config.consensus.quorum_store.sender_max_batch_txns = config - .consensus - .quorum_store - .sender_max_batch_txns - .min(max_block_txns as usize); - config.consensus.quorum_store.receiver_max_batch_txns = config - .consensus - .quorum_store - .receiver_max_batch_txns - .min(max_block_txns as usize); - config.consensus.round_initial_timeout_ms = 1000; - // no increase in timeout, to have stable round/second rate. - config.consensus.round_timeout_backoff_exponent_base = 1.0; - // empty block generated automatically every ~half a second - config.consensus.quorum_store_poll_time_ms = 500; - config - .state_sync - .state_sync_driver - .enable_auto_bootstrapping = true; - config - .state_sync - .state_sync_driver - .max_connection_deadline_secs = 3; - })) - .build() - .await; - - println!( - "Validators {:?}", - swarm.validators().map(|v| v.peer_id()).collect::>() - ); - swarm -} - -pub struct ActiveTrafficGuard { - pub finish_traffic: Arc, -} - -impl Drop for ActiveTrafficGuard { - fn drop(&mut self) { - self.finish_traffic.store(true, Ordering::Relaxed); - } -} - -pub async fn start_traffic( - num_accounts: usize, - tps: f32, - swarm: &mut dyn Swarm, -) -> ActiveTrafficGuard { - let validator_clients = swarm.get_all_nodes_clients_with_names(); - - let finish = Arc::new(AtomicBool::new(false)); - let finish_copy = finish.clone(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - info!("Preparing accounts"); - let mut accounts = vec![]; - for _ in 0..num_accounts { - accounts.push(create_and_fund_account(swarm, 10000000).await); - } - - info!("Starting traffic"); - tokio::spawn(async move { - let mut small_rng = SmallRng::from_entropy(); - - let now = Instant::now(); - let mut index = 0; - while !finish.load(Ordering::Relaxed) { - let sender = small_rng.gen_range(0usize, accounts.len() - 1); - let (a, b) = accounts.split_at_mut(sender + 1); - transfer_coins_non_blocking( - &validator_clients[small_rng.gen_range(0usize, validator_clients.len())].1, - &transaction_factory, - &mut a[sender], - &b[small_rng.gen_range(0, b.len())], - 1, - ) - .await; - - index += 1; - - let elapsed = now.elapsed().as_secs_f32(); - let wanted = (1 + index) as f32 / tps; - if elapsed < wanted { - tokio::time::sleep(Duration::from_secs_f32(wanted - elapsed)).await; - } else if elapsed > wanted + 1.0 { - info!("Traffic is running {}s behind", elapsed - wanted); - } - } - }); - ActiveTrafficGuard { - finish_traffic: finish_copy, - } -} - -async fn run_fail_point_test( - num_validators: usize, - cycles: usize, - cycle_duration_s: f32, - parts_in_cycle: usize, - traffic_tps: f32, - max_block_size: u64, - // (cycle, part) -> (Vec(validator_index, name, action), reset_old_enpoints) - get_fail_points_to_set: Box< - dyn FnMut(usize, usize) -> (Vec<(usize, String, String)>, bool) + Send, - >, - // (cycle, executed_epochs, executed_rounds, executed_transactions, current_state, previous_state) - check_cycle: Box< - dyn FnMut(usize, u64, u64, u64, Vec, Vec) -> anyhow::Result<()>, - >, -) { - let mut swarm = create_swarm(num_validators, max_block_size).await; - let _active_traffic = if traffic_tps > 0.0 { - start_traffic(5, traffic_tps, &mut swarm).await - } else { - ActiveTrafficGuard { - finish_traffic: Arc::new(AtomicBool::new(false)), - } - }; - test_consensus_fault_tolerance( - &mut swarm, - cycles, - cycle_duration_s, - parts_in_cycle, - Box::new(FailPointFailureInjection::new(get_fail_points_to_set)), - check_cycle, - false, - false, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_no_failures() { - let num_validators = 3; - - let mut swarm = create_swarm(num_validators, 1).await; - - test_consensus_fault_tolerance( - &mut swarm, - 3, - 5.0, - 1, - no_failure_injection(), - Box::new(move |_, _, executed_rounds, executed_transactions, _, _| { - assert!( - executed_transactions >= 4, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 2, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - Ok(()) - }), - true, - false, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_fault_tolerance_of_network_send() { - // Randomly increase network failure rate, until network halts, and check that it comes back afterwards. - let mut small_rng = SmallRng::from_entropy(); - let num_validators = 3; - let num_cycles = 4; - run_fail_point_test( - num_validators, - num_cycles, - 2.5, - 5, - 1.0, - 1, - Box::new(move |cycle, _part| { - let max = 10 * (10 - num_cycles + cycle + 1); - let rand: usize = small_rng.gen_range(0, 1000); - let rand_reliability = ((rand as f32 / 1000.0).powf(0.5) * max as f32) as i32; - let wanted_client = small_rng.gen_range(0usize, num_validators); - - ( - vec![( - wanted_client, - "consensus::send::any".to_string(), - format!("{}%return", rand_reliability), - )], - false, - ) - }), - Box::new(|_, _, _, _, _, _| Ok(())), - ) - .await; -} - -#[tokio::test] -async fn test_fault_tolerance_of_network_receive() { - // Randomly increase network failure rate, until network halts, and check that it comes back afterwards. - let mut small_rng = SmallRng::from_entropy(); - let num_validators = 3; - let num_cycles = 4; - run_fail_point_test( - num_validators, - num_cycles, - 2.5, - 5, - 1.0, - 1, - Box::new(move |cycle, _part| { - let max = 10 * (10 - num_cycles + cycle + 1); - let rand: usize = small_rng.gen_range(0, 1000); - let rand_reliability = ((rand as f32 / 1000.0).powf(0.5) * max as f32) as i32; - let wanted_client = small_rng.gen_range(0usize, num_validators); - - ( - vec![( - wanted_client, - "consensus::process::any".to_string(), - format!("{}%return", rand_reliability), - )], - false, - ) - }), - Box::new(|_, _, _, _, _, _| Ok(())), - ) - .await; -} - -#[tokio::test] -async fn test_changing_working_consensus() { - // with 7 nodes, consensus needs 5 to operate. - // we rotate in each cycle, which 2 nodes are down. - // we should consisnently be seeing progress. - let num_validators = 7; - run_fail_point_test( - num_validators, - 6, - 5.0, - 5, - 1.0, - 1, - Box::new(move |cycle, part| { - if part == 0 { - let client_1 = (cycle * 2) % num_validators; - let client_2 = (cycle * 2 + 1) % num_validators; - ( - vec![ - ( - client_1, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_1, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ], - true, - ) - } else { - (vec![], false) - } - }), - Box::new(|_, _, executed_rounds, executed_transactions, _, _| { - assert!( - executed_transactions >= 5, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 2, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - Ok(()) - }), - ) - .await; -} - -#[ignore] -#[tokio::test] -async fn test_changing_working_consensus_fast() { - // with 7 nodes, consensus needs 5 to operate. - // we rotate in each part, which 2 nodes are down. - // we should consisnently be seeing progress. - let mut rng = SmallRng::from_seed([5u8; 16]); - let num_validators = 7; - run_fail_point_test( - num_validators, - 4, - 5.0, - 5, - 1.0, - 1, - Box::new(move |_, _| { - let client_1 = rng.gen_range(0, num_validators); - let client_2 = rng.gen_range(0, num_validators); - ( - vec![ - ( - client_1, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_1, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ], - true, - ) - }), - Box::new(|_, _, executed_rounds, executed_transactions, _, _| { - assert!( - executed_transactions >= 4, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 2, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - Ok(()) - }), - ) - .await; -} - -#[ignore] -#[tokio::test] -async fn test_alternating_having_consensus() { - // with 5 nodes, consensus needs 4 to operate. - // we alternate between 1 and 2 nodes being down, - // and checking progress or no progress - let num_validators = 5; - run_fail_point_test( - num_validators, - 8, - 4.0, - 1, - 1.0, - 1, - Box::new(move |cycle, part| { - if part == 0 { - let client_1 = (cycle * 2) % num_validators; - let mut res = vec![ - ( - client_1, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_1, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ]; - if cycle % 2 == 1 { - let client_2 = (cycle * 2 + 1) % num_validators; - res.push(( - client_2, - "consensus::send::any".to_string(), - "return".to_string(), - )); - res.push(( - client_2, - "consensus::process::any".to_string(), - "return".to_string(), - )); - } - (res, true) - } else { - (vec![], false) - } - }), - Box::new(|cycle, _, executed_rounds, executed_transactions, _, _| { - if cycle % 2 == 1 { - // allow 1 round / 3 transactions, in case anything was leftover in the pipeline - assert!( - executed_transactions <= 3, - "progress with active consensus, {} transactions", - executed_transactions - ); - assert!( - executed_rounds <= 1, - "progress with active consensus, {} rounds", - executed_rounds - ); - } else { - assert!( - executed_transactions >= 5, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 2, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - } - Ok(()) - }), - ) - .await; -} diff --git a/testsuite/smoke-test/src/consensus/consensus_only.rs b/testsuite/smoke-test/src/consensus/consensus_only.rs deleted file mode 100644 index c0163fa9a2ce3..0000000000000 --- a/testsuite/smoke-test/src/consensus/consensus_only.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{smoke_test_environment::new_local_swarm_with_aptos, txn_emitter::generate_traffic}; -use aptos_forge::args::TransactionTypeArg; -use std::time::Duration; - -#[ignore] -#[tokio::test] -// Assumes that the consensus-only-perf-test feature is enabled. -async fn test_consensus_only_with_txn_emitter() { - let mut swarm = new_local_swarm_with_aptos(1).await; - - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - - let txn_stat = generate_traffic( - &mut swarm, - &all_validators, - Duration::from_secs(10), - 1, - vec![vec![ - (TransactionTypeArg::CoinTransfer.materialize_default(), 70), - ( - TransactionTypeArg::AccountGeneration.materialize_default(), - 20, - ), - ]], - ) - .await - .unwrap(); - println!("{:?}", txn_stat.rate()); - // assert some much smaller number than expected, so it doesn't fail under contention - assert!(txn_stat.submitted > 30); - assert!(txn_stat.committed > 30); -} diff --git a/testsuite/smoke-test/src/consensus/consensusdb_recovery.rs b/testsuite/smoke-test/src/consensus/consensusdb_recovery.rs deleted file mode 100644 index 608987c95b777..0000000000000 --- a/testsuite/smoke-test/src/consensus/consensusdb_recovery.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::new_local_swarm_with_aptos, - test_utils::{assert_balance, create_and_fund_account, transfer_coins, MAX_HEALTHY_WAIT_SECS}, -}; -use aptos_consensus::CONSENSUS_DB_NAME; -use aptos_forge::{HealthCheckError, NodeExt, Swarm}; -use std::{ - fs, - time::{Duration, Instant}, -}; - -#[tokio::test] -async fn test_consensusdb_recovery() { - let mut swarm = new_local_swarm_with_aptos(4).await; - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let client_1 = swarm - .validator(validator_peer_ids[1]) - .unwrap() - .rest_client(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - let mut account_0 = create_and_fund_account(&mut swarm, 100).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - let txn = transfer_coins( - &client_1, - &transaction_factory, - &mut account_0, - &account_1, - 10, - ) - .await; - assert_balance(&client_1, &account_0, 90).await; - assert_balance(&client_1, &account_1, 20).await; - - // Stop a node - let node_to_restart = validator_peer_ids[0]; - let node_config = swarm.validator(node_to_restart).unwrap().config().clone(); - let node = swarm.validator_mut(node_to_restart).unwrap(); - node.stop(); - let consensus_db_path = node_config.storage.dir().join(CONSENSUS_DB_NAME); - // Verify that consensus db exists and - // we are not deleting a non-existent directory - assert!(consensus_db_path.as_path().exists()); - // Delete the consensus db to simulate consensus db is nuked - fs::remove_dir_all(consensus_db_path).unwrap(); - node.start().unwrap(); - let deadline = Instant::now() + Duration::from_secs(10); - while Instant::now() < deadline { - // after the node recovers, it'll exit with 0 - if let Err(HealthCheckError::NotRunning(_)) = node.health_check().await { - break; - } - } - - node.restart().await.unwrap(); - node.wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - - let client_0 = swarm.validator(node_to_restart).unwrap().rest_client(); - // Wait for the txn to by synced to the restarted node - client_0.wait_for_signed_transaction(&txn).await.unwrap(); - assert_balance(&client_0, &account_0, 90).await; - assert_balance(&client_0, &account_1, 20).await; - - let txn = transfer_coins( - &client_1, - &transaction_factory, - &mut account_0, - &account_1, - 10, - ) - .await; - client_0.wait_for_signed_transaction(&txn).await.unwrap(); - - assert_balance(&client_0, &account_0, 80).await; - assert_balance(&client_0, &account_1, 30).await; -} diff --git a/testsuite/smoke-test/src/consensus/dag/dag_fault_tolerance.rs b/testsuite/smoke-test/src/consensus/dag/dag_fault_tolerance.rs deleted file mode 100644 index 3277308b3b152..0000000000000 --- a/testsuite/smoke-test/src/consensus/dag/dag_fault_tolerance.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - consensus::consensus_fault_tolerance::{start_traffic, ActiveTrafficGuard}, - smoke_test_environment::SwarmBuilder, -}; -use aptos_config::config::DagFetcherConfig; -use aptos_forge::{ - test_utils::consensus_utils::{ - no_failure_injection, test_consensus_fault_tolerance, FailPointFailureInjection, NodeState, - }, - LocalSwarm, -}; -use aptos_types::on_chain_config::{ - ConsensusAlgorithmConfig, DagConsensusConfigV1, OnChainConsensusConfig, ValidatorTxnConfig, -}; -use rand::{rngs::SmallRng, Rng, SeedableRng}; -use std::sync::{atomic::AtomicBool, Arc}; - -pub async fn create_dag_swarm(num_nodes: usize) -> LocalSwarm { - let swarm = SwarmBuilder::new_local(num_nodes) - .with_init_config(Arc::new(move |_, config, _| { - config.api.failpoints_enabled = true; - config - .state_sync - .state_sync_driver - .enable_auto_bootstrapping = true; - config - .state_sync - .state_sync_driver - .max_connection_deadline_secs = 3; - config.dag_consensus.fetcher_config = DagFetcherConfig { - retry_interval_ms: 30, - rpc_timeout_ms: 500, - min_concurrent_responders: 2, - max_concurrent_responders: 7, - max_concurrent_fetches: 4, - } - })) - .with_init_genesis_config(Arc::new(move |genesis_config| { - let onchain_consensus_config = OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::DAG(DagConsensusConfigV1::default()), - vtxn: ValidatorTxnConfig::default_for_genesis(), - }; - - genesis_config.consensus_config = onchain_consensus_config; - })) - .build() - .await; - - println!( - "Validators {:?}", - swarm.validators().map(|v| v.peer_id()).collect::>() - ); - swarm -} - -#[tokio::test] -async fn test_no_failures() { - let num_validators = 3; - - let mut swarm = create_dag_swarm(num_validators).await; - - test_consensus_fault_tolerance( - &mut swarm, - 3, - 5.0, - 1, - no_failure_injection(), - Box::new(move |_, _, executed_rounds, executed_transactions, _, _| { - assert!( - executed_transactions >= 4, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 2, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - Ok(()) - }), - true, - false, - ) - .await - .unwrap(); -} - -async fn run_dag_fail_point_test( - num_validators: usize, - cycles: usize, - cycle_duration_s: f32, - parts_in_cycle: usize, - traffic_tps: f32, - _max_block_size: u64, - // (cycle, part) -> (Vec(validator_index, name, action), reset_old_enpoints) - get_fail_points_to_set: Box< - dyn FnMut(usize, usize) -> (Vec<(usize, String, String)>, bool) + Send, - >, - // (cycle, executed_epochs, executed_rounds, executed_transactions, current_state, previous_state) - check_cycle: Box< - dyn FnMut(usize, u64, u64, u64, Vec, Vec) -> anyhow::Result<()>, - >, -) { - let mut swarm = create_dag_swarm(num_validators).await; - let _active_traffic = if traffic_tps > 0.0 { - start_traffic(5, traffic_tps, &mut swarm).await - } else { - ActiveTrafficGuard { - finish_traffic: Arc::new(AtomicBool::new(false)), - } - }; - test_consensus_fault_tolerance( - &mut swarm, - cycles, - cycle_duration_s, - parts_in_cycle, - Box::new(FailPointFailureInjection::new(get_fail_points_to_set)), - check_cycle, - false, - false, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_fault_tolerance_of_network_send() { - // Randomly increase network failure rate, until network halts, and check that it comes back afterwards. - let mut small_rng = SmallRng::from_entropy(); - let num_validators = 3; - let num_cycles = 4; - run_dag_fail_point_test( - num_validators, - num_cycles, - 2.5, - 5, - 1.0, - 1, - Box::new(move |cycle, _part| { - let max = 10 * (10 - num_cycles + cycle + 1); - let rand: usize = small_rng.gen_range(0, 1000); - let rand_reliability = ((rand as f32 / 1000.0).powf(0.5) * max as f32) as i32; - let wanted_client = small_rng.gen_range(0usize, num_validators); - - ( - vec![( - wanted_client, - "consensus::send::any".to_string(), - format!("{}%return", rand_reliability), - )], - false, - ) - }), - Box::new(|_, _, _, _, _, _| Ok(())), - ) - .await; -} - -#[tokio::test] -async fn test_fault_tolerance_of_network_receive() { - // Randomly increase network failure rate, until network halts, and check that it comes back afterwards. - let mut small_rng = SmallRng::from_entropy(); - let num_validators = 3; - let num_cycles = 4; - run_dag_fail_point_test( - num_validators, - num_cycles, - 2.5, - 5, - 1.0, - 1, - Box::new(move |cycle, _part| { - let max = 10 * (10 - num_cycles + cycle + 1); - let rand: usize = small_rng.gen_range(0, 1000); - let rand_reliability = ((rand as f32 / 1000.0).powf(0.5) * max as f32) as i32; - let wanted_client = small_rng.gen_range(0usize, num_validators); - - ( - vec![( - wanted_client, - "consensus::process::any".to_string(), - format!("{}%return", rand_reliability), - )], - false, - ) - }), - Box::new(|_, _, _, _, _, _| Ok(())), - ) - .await; -} - -#[tokio::test] -async fn test_changing_working_consensus() { - // with 7 nodes, consensus needs 5 to operate. - // we rotate in each cycle, which 2 nodes are down. - // we should consisnently be seeing progress. - let num_validators = 7; - run_dag_fail_point_test( - num_validators, - 6, - 10.0, - 2, - 1.0, - num_validators as u64, - Box::new(move |cycle, part| { - if part == 0 { - let client_1 = (cycle * 2) % num_validators; - let client_2 = (cycle * 2 + 1) % num_validators; - ( - vec![ - ( - client_1, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_1, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::send::any".to_string(), - "return".to_string(), - ), - ( - client_2, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ], - true, - ) - } else { - (vec![], false) - } - }), - Box::new(|_, _, executed_rounds, executed_transactions, _, _| { - assert!( - executed_transactions >= 1, - "no progress with active consensus, only {} transactions", - executed_transactions - ); - assert!( - executed_rounds >= 1, - "no progress with active consensus, only {} rounds", - executed_rounds - ); - Ok(()) - }), - ) - .await; -} diff --git a/testsuite/smoke-test/src/consensus/dag/mod.rs b/testsuite/smoke-test/src/consensus/dag/mod.rs deleted file mode 100644 index 2e15d3cf1ed1f..0000000000000 --- a/testsuite/smoke-test/src/consensus/dag/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright © Aptos Foundation - -mod dag_fault_tolerance; diff --git a/testsuite/smoke-test/src/consensus/mod.rs b/testsuite/smoke-test/src/consensus/mod.rs deleted file mode 100644 index ad1368b8f84d2..0000000000000 --- a/testsuite/smoke-test/src/consensus/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod consensus_fault_tolerance; -mod consensus_only; -mod consensusdb_recovery; -mod dag; -mod quorum_store_fault_tolerance; diff --git a/testsuite/smoke-test/src/consensus/quorum_store_fault_tolerance.rs b/testsuite/smoke-test/src/consensus/quorum_store_fault_tolerance.rs deleted file mode 100644 index e1ad0f22ec38c..0000000000000 --- a/testsuite/smoke-test/src/consensus/quorum_store_fault_tolerance.rs +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - aptos_cli::validator::generate_blob, smoke_test_environment::SwarmBuilder, - txn_emitter::generate_traffic, -}; -use aptos::test::CliTestFramework; -use aptos_consensus::QUORUM_STORE_DB_NAME; -use aptos_forge::{ - args::TransactionTypeArg, reconfig, wait_for_all_nodes_to_catchup, NodeExt, Swarm, SwarmExt, - TransactionType, -}; -use aptos_logger::info; -use aptos_rest_client::Client; -use aptos_types::{ - on_chain_config::{ConsensusConfigV1, OnChainConsensusConfig}, - PeerId, -}; -use std::{fs, sync::Arc, time::Duration}; - -const MAX_WAIT_SECS: u64 = 60; - -async fn generate_traffic_and_assert_committed( - swarm: &mut dyn Swarm, - nodes: &[PeerId], - duration: Duration, -) { - let rest_client = swarm.validator(nodes[0]).unwrap().rest_client(); - - // faucet can make our root LocalAccount sequence number get out of sync. - swarm - .chain_info() - .resync_root_account_seq_num(&rest_client) - .await - .unwrap(); - - let txn_stat = generate_traffic(swarm, nodes, duration, 100, vec![vec![ - ( - TransactionType::CoinTransfer { - invalid_transaction_ratio: 0, - sender_use_account_pool: false, - }, - 70, - ), - ( - TransactionType::AccountGeneration { - add_created_accounts_to_pool: true, - max_account_working_set: 1_000_000, - creation_balance: 1_000_000, - }, - 20, - ), - ]]) - .await - .unwrap(); - println!("{:?}", txn_stat.rate()); - // assert some much smaller number than expected, so it doesn't fail under contention - assert!(txn_stat.submitted > 30); - assert!(txn_stat.committed > 30); -} - -async fn update_consensus_config( - cli: &CliTestFramework, - root_cli_index: usize, - new_consensus_config: OnChainConsensusConfig, -) { - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set_for_next_epoch(&framework_signer, config_bytes); - aptos_governance::force_end_epoch(&framework_signer); - }} - }} - "#, - generate_blob(&bcs::to_bytes(&new_consensus_config).unwrap()) - ); - cli.run_script(root_cli_index, &update_consensus_config_script) - .await - .unwrap(); -} - -// TODO: remove when quorum store becomes the in-code default -#[tokio::test] -async fn test_onchain_config_quorum_store_enabled_and_disabled() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_aptos() - // Start with V1 - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.consensus_config = - OnChainConsensusConfig::V1(ConsensusConfigV1::default()) - })) - .build_with_cli(0) - .await; - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - - generate_traffic_and_assert_committed(&mut swarm, &validator_peer_ids, Duration::from_secs(5)) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - for _ in 0..5 { - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let current_consensus_config = - crate::utils::get_current_consensus_config(&rest_client).await; - let inner = match current_consensus_config { - OnChainConsensusConfig::V1(inner) => inner, - OnChainConsensusConfig::V2(_) => panic!("Unexpected V2 config"), - _ => unimplemented!(), - }; - // Change to V2 - let new_consensus_config = OnChainConsensusConfig::V2(ConsensusConfigV1 { ..inner }); - update_consensus_config(&cli, root_cli_index, new_consensus_config).await; - - generate_traffic_and_assert_committed( - &mut swarm, - &validator_peer_ids, - Duration::from_secs(5), - ) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - let current_consensus_config = - crate::utils::get_current_consensus_config(&rest_client).await; - let inner = match current_consensus_config { - OnChainConsensusConfig::V1(_) => panic!("Unexpected V1 config"), - OnChainConsensusConfig::V2(inner) => inner, - _ => unimplemented!(), - }; - - // Disaster rollback to V1 - let new_consensus_config = OnChainConsensusConfig::V1(ConsensusConfigV1 { ..inner }); - update_consensus_config(&cli, root_cli_index, new_consensus_config).await; - - generate_traffic_and_assert_committed( - &mut swarm, - &validator_peer_ids, - Duration::from_secs(5), - ) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - } -} - -/// Checks progress even if half the nodes are not actually writing batches to the DB. -/// Shows that remote reading of batches is working. -/// Note this is more than expected (f) byzantine behavior. -#[tokio::test] -async fn test_remote_batch_reads() { - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - })) - // TODO: remove when quorum store becomes the in-code default - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.consensus_config = - OnChainConsensusConfig::V2(ConsensusConfigV1::default()) - })) - .build() - .await; - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - for peer_id in validator_peer_ids.iter().take(2) { - let validator_client = swarm.validator(*peer_id).unwrap().rest_client(); - validator_client - .set_failpoint("quorum_store::save".to_string(), "return".to_string()) - .await - .unwrap(); - } - - generate_traffic_and_assert_committed( - &mut swarm, - &[validator_peer_ids[2]], - Duration::from_secs(20), - ) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); -} - -async fn test_batch_id_on_restart(do_wipe_db: bool) { - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - // TODO: remove when quorum store becomes the in-code default - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.consensus_config = - OnChainConsensusConfig::V2(ConsensusConfigV1::default()) - })) - .build() - .await; - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let node_to_restart = validator_peer_ids[0]; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - generate_traffic_and_assert_committed(&mut swarm, &[node_to_restart], Duration::from_secs(20)) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - info!("restart node 0, db intact"); - swarm - .validator_mut(node_to_restart) - .unwrap() - .restart() - .await - .unwrap(); - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - generate_traffic_and_assert_committed(&mut swarm, &[node_to_restart], Duration::from_secs(20)) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - info!("stop node 0"); - swarm.validator_mut(node_to_restart).unwrap().stop(); - if do_wipe_db { - let node0_config = swarm.validator(node_to_restart).unwrap().config().clone(); - let db_dir = node0_config.storage.dir(); - let quorum_store_db_dir = db_dir.join(QUORUM_STORE_DB_NAME); - info!( - "wipe only quorum store db: {}", - quorum_store_db_dir.display() - ); - fs::remove_dir_all(quorum_store_db_dir).unwrap(); - } else { - info!("don't do anything to quorum store db"); - } - info!("start node 0"); - swarm - .validator_mut(node_to_restart) - .unwrap() - .start() - .unwrap(); - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - info!("generate traffic"); - generate_traffic_and_assert_committed(&mut swarm, &[node_to_restart], Duration::from_secs(20)) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); -} - -/// Checks that a validator can still get signatures on batches on restart when the db is intact. -#[tokio::test] -async fn test_batch_id_on_restart_same_db() { - test_batch_id_on_restart(false).await; -} - -/// Checks that a validator can still get signatures on batches even if its db is reset (e.g., -/// the disk failed, or the validator had to be moved to another node). -#[tokio::test] -async fn test_batch_id_on_restart_wiped_db() { - test_batch_id_on_restart(true).await; -} - -#[tokio::test] -async fn test_swarm_with_bad_non_qs_node() { - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - })) - // TODO: remove when quorum store becomes the in-code default - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.consensus_config = - OnChainConsensusConfig::V2(ConsensusConfigV1::default()) - })) - .build() - .await; - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let dishonest_peer_id = validator_peer_ids[0]; - let honest_peers: Vec<(String, Client)> = swarm - .validators() - .skip(1) - .map(|node| (node.name().to_string(), node.rest_client())) - .collect(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - let rest_client = swarm - .validator(validator_peer_ids[1]) - .unwrap() - .rest_client(); - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - generate_traffic_and_assert_committed( - &mut swarm, - &validator_peer_ids[1..], - Duration::from_secs(20), - ) - .await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - let non_qs_validator_client = swarm.validator(dishonest_peer_id).unwrap().rest_client(); - non_qs_validator_client - .set_failpoint( - "consensus::start_new_epoch::disable_qs".to_string(), - "return".to_string(), - ) - .await - .unwrap(); - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - - wait_for_all_nodes_to_catchup(&honest_peers, Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); - - info!("generate traffic"); - let tx_stat = generate_traffic( - &mut swarm, - &[dishonest_peer_id], - Duration::from_secs(20), - 1, - vec![vec![ - (TransactionTypeArg::CoinTransfer.materialize_default(), 70), - ( - TransactionTypeArg::AccountGeneration.materialize_default(), - 20, - ), - ]], - ) - .await; - assert!(tx_stat.is_err()); - - generate_traffic_and_assert_committed( - &mut swarm, - &validator_peer_ids[1..], - Duration::from_secs(20), - ) - .await; - - wait_for_all_nodes_to_catchup(&honest_peers, Duration::from_secs(MAX_WAIT_SECS)) - .await - .unwrap(); -} diff --git a/testsuite/smoke-test/src/execution.rs b/testsuite/smoke-test/src/execution.rs deleted file mode 100644 index c037c597ce29f..0000000000000 --- a/testsuite/smoke-test/src/execution.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{smoke_test_environment::SwarmBuilder, utils::get_current_version}; -use aptos_forge::{NodeExt, SwarmExt}; -use std::{sync::Arc, time::Duration}; - -#[tokio::test] -async fn fallback_test() { - let swarm = SwarmBuilder::new_local(1) - .with_init_config(Arc::new(|_, config, _| { - config.api.failpoints_enabled = true; - config.execution.discard_failed_blocks = true; - })) - .with_aptos() - .build() - .await; - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(60)) - .await - .expect("Epoch 2 taking too long to come!"); - - let client = swarm.validators().next().unwrap().rest_client(); - - client - .set_failpoint( - "aptos_vm::vm_wrapper::execute_transaction".to_string(), - "100%return".to_string(), - ) - .await - .unwrap(); - - for _i in 0..1 { - let version_milestone_0 = get_current_version(&client).await; - let version_milestone_1 = version_milestone_0 + 5; - println!("Current version: {}, the chain should tolerate discarding failed blocks, waiting for {}.", version_milestone_0, version_milestone_1); - swarm - .wait_for_all_nodes_to_catchup_to_version(version_milestone_1, Duration::from_secs(30)) - .await - .expect("milestone 1 taking too long"); - } -} diff --git a/testsuite/smoke-test/src/full_nodes.rs b/testsuite/smoke-test/src/full_nodes.rs deleted file mode 100644 index 2360c8a5cb78c..0000000000000 --- a/testsuite/smoke-test/src/full_nodes.rs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{ - assert_balance, create_and_fund_account, transfer_coins, MAX_CATCH_UP_WAIT_SECS, - MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS, - }, -}; -use aptos_config::{ - config::{DiscoveryMethod, NodeConfig, OverrideNodeConfig, Peer, PeerRole, HANDSHAKE_VERSION}, - network_id::NetworkId, -}; -use aptos_forge::{LocalSwarm, NodeExt, Swarm, SwarmExt}; -use aptos_types::network_address::{NetworkAddress, Protocol}; -use std::{ - collections::HashSet, - net::Ipv4Addr, - time::{Duration, Instant}, -}; - -#[tokio::test] -async fn test_full_node_basic_flow() { - let mut swarm = local_swarm_with_fullnodes(1, 1).await; - let validator_peer_id = swarm.validators().next().unwrap().peer_id(); - let vfn_peer_id = swarm.full_nodes().next().unwrap().peer_id(); - let version = swarm.versions().max().unwrap(); - let pfn_peer_id = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(NodeConfig::get_default_pfn_config()), - ) - .await - .unwrap(); - for fullnode in swarm.full_nodes_mut() { - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - } - let transaction_factory = swarm.chain_info().transaction_factory(); - - // create clients for all nodes - let validator_client = swarm.validator(validator_peer_id).unwrap().rest_client(); - let vfn_client = swarm.full_node(vfn_peer_id).unwrap().rest_client(); - let pfn_client = swarm.full_node(pfn_peer_id).unwrap().rest_client(); - - let mut account_0 = create_and_fund_account(&mut swarm, 10).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - // Send txn to PFN - let _txn = transfer_coins( - &pfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&validator_client, &account_0, 9).await; - assert_balance(&validator_client, &account_1, 11).await; - assert_balance(&vfn_client, &account_0, 9).await; - assert_balance(&vfn_client, &account_1, 11).await; - assert_balance(&pfn_client, &account_0, 9).await; - assert_balance(&pfn_client, &account_1, 11).await; - - // Send txn to VFN - let txn = transfer_coins( - &vfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&validator_client, &account_0, 8).await; - assert_balance(&validator_client, &account_1, 12).await; - assert_balance(&vfn_client, &account_0, 8).await; - assert_balance(&vfn_client, &account_1, 12).await; - - pfn_client.wait_for_signed_transaction(&txn).await.unwrap(); - assert_balance(&pfn_client, &account_0, 8).await; - assert_balance(&pfn_client, &account_1, 12).await; - - // Send txn to Validator - let txn = transfer_coins( - &vfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&validator_client, &account_0, 7).await; - assert_balance(&validator_client, &account_1, 13).await; - - vfn_client.wait_for_signed_transaction(&txn).await.unwrap(); - assert_balance(&vfn_client, &account_0, 7).await; - assert_balance(&vfn_client, &account_1, 13).await; - - pfn_client.wait_for_signed_transaction(&txn).await.unwrap(); - assert_balance(&pfn_client, &account_0, 7).await; - assert_balance(&pfn_client, &account_1, 13).await; -} - -#[tokio::test] -async fn test_vfn_failover() { - // VFN failover happens when validator is down even for default_failovers = 0 - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.mempool.default_failovers = 0; - let mut swarm = SwarmBuilder::new_local(4) - .with_num_fullnodes(4) - .with_aptos() - .with_vfn_config(vfn_config) - .build() - .await; - let transaction_factory = swarm.chain_info().transaction_factory(); - - for fullnode in swarm.full_nodes_mut() { - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - fullnode - .wait_for_connectivity(Instant::now() + Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS)) - .await - .unwrap(); - } - - // Setup accounts - let mut account_0 = create_and_fund_account(&mut swarm, 10).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - // set up client - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let vfn_peer_ids = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - let validator = validator_peer_ids[1]; - let vfn_client = swarm.full_node(vfn_peer_ids[1]).unwrap().rest_client(); - - // submit client requests directly to VFN of dead V - swarm.validator_mut(validator).unwrap().stop(); - - transfer_coins( - &vfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&vfn_client, &account_0, 9).await; - assert_balance(&vfn_client, &account_1, 11).await; - - transfer_coins( - &vfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&vfn_client, &account_0, 8).await; - assert_balance(&vfn_client, &account_1, 12).await; -} - -#[tokio::test] -async fn test_private_full_node() { - let mut swarm = local_swarm_with_fullnodes(4, 1).await; - let vfn_peer_id = swarm.full_nodes().next().unwrap().peer_id(); - - let transaction_factory = swarm.chain_info().transaction_factory(); - - // Here we want to add two swarms, a private full node, followed by a user full node connected to it - let mut private_config = NodeConfig::get_default_pfn_config(); - let private_network = private_config.full_node_networks.first_mut().unwrap(); - // Disallow public connections - private_network.max_inbound_connections = 0; - // Also, we only want it to purposely connect to 1 VFN - private_network.max_outbound_connections = 1; - - let mut user_config = NodeConfig::get_default_pfn_config(); - let user_network = user_config.full_node_networks.first_mut().unwrap(); - // Disallow fallbacks to VFNs - user_network.max_outbound_connections = 1; - user_network.discovery_method = DiscoveryMethod::None; - - // The secret sauce, add the user as a downstream to the seeds - add_node_to_seeds( - &mut private_config, - &user_config, - NetworkId::Public, - PeerRole::Downstream, - ); - - // Now we need to connect the VFNs to the private swarm - let version = swarm.versions().max().unwrap(); - add_node_to_seeds( - &mut private_config, - swarm.fullnode(vfn_peer_id).unwrap().config(), - NetworkId::Public, - PeerRole::PreferredUpstream, - ); - let private = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(private_config), - ) - .await - .unwrap(); - - // And connect the user to the private swarm - add_node_to_seeds( - &mut user_config, - swarm.full_node(private).unwrap().config(), - NetworkId::Public, - PeerRole::PreferredUpstream, - ); - let user = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(user_config), - ) - .await - .unwrap(); - - swarm - .wait_for_connectivity(Instant::now() + Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS)) - .await - .unwrap(); - - // Ensure that User node is connected to private node and only the private node - { - let user_node = swarm.full_node(user).unwrap(); - assert_eq!( - 1, - user_node - .get_connected_peers(NetworkId::Public, None) - .await - .unwrap() - .unwrap_or(0), - "User node is connected to more than one peer" - ); - } - - // read state from full node client - let validator_client = swarm.validators().next().unwrap().rest_client(); - let user_client = swarm.full_node(user).unwrap().rest_client(); - - let mut account_0 = create_and_fund_account(&mut swarm, 100).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - // send txn from user node and check both validator and user node have correct balance - transfer_coins( - &user_client, - &transaction_factory, - &mut account_0, - &account_1, - 10, - ) - .await; - assert_balance(&user_client, &account_0, 90).await; - assert_balance(&user_client, &account_1, 20).await; - assert_balance(&validator_client, &account_0, 90).await; - assert_balance(&validator_client, &account_1, 20).await; -} - -fn add_node_to_seeds( - dest_config: &mut NodeConfig, - seed_config: &NodeConfig, - network_id: NetworkId, - peer_role: PeerRole, -) { - let dest_network_config = dest_config - .full_node_networks - .iter_mut() - .find(|network| network.network_id == network_id) - .unwrap(); - let seed_network_config = seed_config - .full_node_networks - .iter() - .find(|network| network.network_id == network_id) - .unwrap(); - - let seed_peer_id = seed_network_config.peer_id(); - let seed_key = seed_network_config.identity_key().public_key(); - - let seed_peer = if peer_role != PeerRole::Downstream { - // For upstreams, we know the address, but so don't duplicate the keys in the config (lazy way) - // TODO: This is ridiculous, we need a better way to manipulate these `NetworkAddress`s - let address = seed_network_config.listen_address.clone(); - let port_protocol = address - .as_slice() - .iter() - .find(|protocol| matches!(protocol, Protocol::Tcp(_))) - .unwrap(); - let address = NetworkAddress::from_protocols(vec![ - Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)), - port_protocol.clone(), - Protocol::NoiseIK(seed_key), - Protocol::Handshake(HANDSHAKE_VERSION), - ]) - .unwrap(); - - Peer::new(vec![address], HashSet::new(), peer_role) - } else { - // For downstreams, we don't know the address, but we know the keys - let mut seed_keys = HashSet::new(); - seed_keys.insert(seed_key); - Peer::new(vec![], seed_keys, peer_role) - }; - - dest_network_config.seeds.insert(seed_peer_id, seed_peer); -} - -async fn local_swarm_with_fullnodes(num_validators: usize, num_fullnodes: usize) -> LocalSwarm { - SwarmBuilder::new_local(num_validators) - .with_num_fullnodes(num_fullnodes) - .with_aptos() - .build() - .await -} diff --git a/testsuite/smoke-test/src/fullnode.rs b/testsuite/smoke-test/src/fullnode.rs deleted file mode 100644 index 3cc1081ed3a8d..0000000000000 --- a/testsuite/smoke-test/src/fullnode.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::new_local_swarm_with_aptos, test_utils::MAX_HEALTHY_WAIT_SECS, -}; -use anyhow::bail; -use aptos_cached_packages::aptos_stdlib; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_forge::{NodeExt, Result, Swarm}; -use aptos_rest_client::Client as RestClient; -use aptos_types::account_address::AccountAddress; -use std::time::{Duration, Instant}; - -#[tokio::test] -async fn test_indexer() { - let mut swarm = new_local_swarm_with_aptos(1).await; - - let version = swarm.versions().max().unwrap(); - let fullnode_peer_id = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(NodeConfig::get_default_pfn_config()), - ) - .await - .unwrap(); - let validator_peer_id = swarm.validators().next().unwrap().peer_id(); - let _vfn_peer_id = swarm - .add_validator_full_node( - &version, - OverrideNodeConfig::new_with_default_base(NodeConfig::get_default_vfn_config()), - validator_peer_id, - ) - .unwrap(); - - let fullnode = swarm.full_node_mut(fullnode_peer_id).unwrap(); - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - - let client = fullnode.rest_client(); - - let account1 = swarm.aptos_public_info().random_account(); - let account2 = swarm.aptos_public_info().random_account(); - - let mut chain_info = swarm.chain_info().into_aptos_public_info(); - let factory = chain_info.transaction_factory(); - chain_info - .create_user_account(account1.public_key()) - .await - .unwrap(); - // TODO(Gas): double check if this is correct - chain_info - .mint(account1.address(), 10_000_000_000) - .await - .unwrap(); - chain_info - .create_user_account(account2.public_key()) - .await - .unwrap(); - - wait_for_account(&client, account1.address()).await.unwrap(); - - let txn = account1.sign_with_transaction_builder( - factory.payload(aptos_stdlib::aptos_coin_transfer(account2.address(), 10)), - ); - - client.submit_and_wait(&txn).await.unwrap(); - let balance = client - .get_account_balance(account2.address()) - .await - .unwrap() - .into_inner(); - - assert_eq!(balance.get(), 10); -} - -async fn wait_for_account(client: &RestClient, address: AccountAddress) -> Result<()> { - const DEFAULT_WAIT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(120); - let start = std::time::Instant::now(); - while start.elapsed() < DEFAULT_WAIT_TIMEOUT { - if client.get_account(address).await.is_ok() { - return Ok(()); - } - tokio::time::sleep(std::time::Duration::from_millis(10)).await; - } - bail!("wait for account(address={}) timeout", address,) -} diff --git a/testsuite/smoke-test/src/genesis.rs b/testsuite/smoke-test/src/genesis.rs deleted file mode 100644 index c7ae8c1e6b09f..0000000000000 --- a/testsuite/smoke-test/src/genesis.rs +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - storage::{db_backup, db_restore}, - test_utils::{ - check_create_mint_transfer_node, create_test_accounts, execute_transactions, - execute_transactions_and_wait, swarm_utils::insert_waypoint, MAX_CATCH_UP_WAIT_SECS, - MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS, - }, - workspace_builder, - workspace_builder::workspace_root, -}; -use anyhow::anyhow; -use aptos_config::{ - config::{AdminServiceConfig, InitialSafetyRulesConfig, NodeConfig}, - network_id::NetworkId, -}; -use aptos_forge::{ - get_highest_synced_version, get_highest_synced_version_and_epoch, - wait_for_all_nodes_to_catchup, LocalNode, LocalSwarm, Node, NodeExt, SwarmExt, Validator, -}; -use aptos_temppath::TempPath; -use aptos_types::{transaction::Transaction, waypoint::Waypoint}; -use move_core_types::language_storage::CORE_CODE_ADDRESS; -use regex::Regex; -use reqwest::Client; -use std::{ - fs, - path::PathBuf, - process::Command, - str::FromStr, - time::{Duration, Instant}, -}; - -#[ignore] // TODO(joshlind): revisit the flakes once we update state sync to handle forks automatically. -#[tokio::test] -/// This test verifies: -/// 1. The behaviour of the consensus sync_only mode (to emulate a network halt). -/// 2. The flow of a genesis write-set transaction for fullnodes (after the validators have forked). -/// -/// The test does the following: -/// 1. Start a 4 node validator network, including 2 VFNs. -/// 2. Use consensus `sync_only` mode to force all nodes to stop at the same version (i.e., emulate a halt). -/// 3. Use the aptos CLI to generate a genesis transaction that removes the last validator from the set. -/// 4. Use the aptos-debugger to manually apply the genesis transaction to all remaining validators. -/// 5. Verify that the network is able to resume consensus and that the last validator is no longer in the set. -/// 6. Use the aptos-debugger to manually apply the genesis transaction to all VFNs. -/// 7. Verify that the VFNs are able to sync with the rest of the network. -async fn test_fullnode_genesis_transaction_flow() { - println!("0. Building the Aptos CLI and debugger!"); - let aptos_debugger = workspace_builder::get_bin("aptos-debugger"); - let aptos_cli = workspace_builder::get_bin("aptos"); - - println!("1. Starting a 4 node validator network with 2 VFNs!"); - let num_validators = 4; - let num_fullnodes = 2; - let (mut swarm, cli_test_framework, _) = SwarmBuilder::new_local(num_validators) - .with_num_fullnodes(num_fullnodes) - .with_aptos() - .build_with_cli(0) - .await; - - println!("2. Executing a number of test transactions"); - let validator = swarm.validators_mut().next().unwrap(); - let validator_client = validator.rest_client(); - let (mut account_0, account_1) = create_test_accounts(&mut swarm).await; - execute_transactions_and_wait( - &mut swarm, - &validator_client, - &mut account_0, - &account_1, - true, - ) - .await; - - println!("3. Enabling `sync_only` mode for every validator!"); - for validator in swarm.validators_mut() { - enable_sync_only_mode(num_validators, validator).await; - } - - println!("4. Fetching the halt version and epoch, and stopping all validators!"); - let (halt_version, halt_epoch) = - get_highest_synced_version_and_epoch(&swarm.get_all_nodes_clients_with_names()) - .await - .unwrap(); - for node in swarm.validators_mut() { - node.stop(); - } - - println!("5. Generating a genesis transaction that removes the last validator from the set!"); - let (genesis_blob_path, genesis_transaction) = - generate_genesis_transaction(&mut swarm, aptos_cli); - - println!("6. Applying the genesis transaction to the first validator!"); - let first_validator_config = swarm.validators_mut().next().unwrap().config().clone(); - let first_validator_storage_dir = first_validator_config.storage.dir(); - let output = Command::new(aptos_debugger.as_path()) - .current_dir(workspace_root()) - .args(&vec![ - "aptos-db", - "bootstrap", - first_validator_storage_dir.to_str().unwrap(), - "--genesis-txn-file", - genesis_blob_path.path().to_str().unwrap(), - ]) - .output() - .unwrap(); - - println!("7. Parsing the output to get the waypoint: {:?}", output); - let output_string = std::str::from_utf8(&output.stdout).unwrap(); - let waypoint = parse_waypoint(output_string); - - println!( - "8. Applying the genesis transaction to validators 0, 1 and 2. Waypoint: {:?}", - waypoint - ); - for (num_expected_peers, validator) in swarm.validators_mut().take(3).enumerate() { - apply_genesis_to_node( - validator, - genesis_transaction.clone(), - waypoint, - num_expected_peers, - false, // Don't test the admin service on the validators - ) - .await; - } - - println!("9. Verifying that we're able to resume consensus and execute transactions!"); - execute_transactions( - &mut swarm, - &validator_client, - &mut account_0, - &account_1, - true, - ) - .await; - - println!("10. Verifying that the last validator is no longer in the validator set!"); - let validator_set = cli_test_framework.show_validator_set().await.unwrap(); - assert_eq!(validator_set.active_validators.len(), num_validators - 1); - - println!("11. Verifying that the VFNs are stuck at the network halt! Expected epoch: {}, version: {}", halt_epoch, halt_version); - for fullnode in swarm.fullnodes_mut() { - // Get the current epoch and version for the fullnode - let (current_epoch, current_version) = get_current_epoch_and_version(fullnode).await; - - // Verify that the fullnode is stuck at the network halt - assert_eq!(current_epoch, halt_epoch); - assert_eq!(current_version, halt_version); - } - - println!( - "12. Applying the genesis transaction to the VFNs. Waypoint: {:?}", - waypoint - ); - for (fullnode_index, fullnode) in swarm.fullnodes_mut().enumerate() { - apply_genesis_to_node( - fullnode, - genesis_transaction.clone(), - waypoint, - 1, // Number of expected peers - fullnode_index == 0, // Test admin service on the first fullnode - ) - .await; - } - - println!("13. Verifying that the VFNs are able to sync with the validators!"); - let all_nodes = swarm.validators().take(3).chain(swarm.fullnodes()); - let all_node_clients: Vec<_> = all_nodes - .map(|node| (node.name().to_string(), node.rest_client())) - .collect(); - wait_for_all_nodes_to_catchup( - &all_node_clients, - Duration::from_secs(MAX_CATCH_UP_WAIT_SECS), - ) - .await - .unwrap(); -} - -#[tokio::test] -/// This test verifies: -/// 1. The behaviour of the consensus sync_only mode. -/// 2. The flow of a genesis write-set transaction after the chain has halted. -/// 3. That db-restore is able to restore a failed validator node. -/// -/// The test does the following: -/// 1. Start a 5 node validator network. -/// 2. Enable consensus `sync_only` mode for the last validator and verify that it can sync. -/// 3. Use consensus `sync_only` mode to force all nodes to stop at the same version (i.e., emulate a halt). -/// 4. Use the aptos CLI to generate a genesis transaction that removes the last validator from the set. -/// 5. Use the aptos-debugger to manually apply the genesis transaction to all remaining validators. -/// 6. Verify that the network is able to resume consensus and that the last validator is no longer in the set. -/// 7. Verify that a failed validator node is able to db-restore and rejoin the network. -async fn test_validator_genesis_transaction_and_db_restore_flow() { - println!("0. Building the Aptos CLI and debugger!"); - let aptos_debugger = workspace_builder::get_bin("aptos-debugger"); - let aptos_cli = workspace_builder::get_bin("aptos"); - - println!("1. Starting a 5 node validator network!"); - let num_validators = 5; - let (mut swarm, cli_test_framework, _) = SwarmBuilder::new_local(num_validators) - .with_aptos() - .build_with_cli(0) - .await; - - println!("2. Enabling `sync_only` mode for the last validator and verifying that it can sync!"); - let last_validator = swarm.validators_mut().last().unwrap(); - enable_sync_only_mode(num_validators, last_validator).await; - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - println!("3. Enabling `sync_only` mode for every validator!"); - for validator in swarm.validators_mut() { - enable_sync_only_mode(num_validators, validator).await; - } - - println!("4. Deleting one validator's DB and verifying that it can still catch up!"); - delete_storage_and_wait_for_catchup(&mut swarm, 3).await; - - println!("5. Stopping three validator nodes!"); - for node in swarm.validators_mut().take(3) { - node.stop(); - } - - println!("6. Generating a genesis transaction that removes the last validator from the set!"); - let (genesis_blob_path, genesis_transaction) = - generate_genesis_transaction(&mut swarm, aptos_cli); - - println!("7. Applying the genesis transaction to the first validator!"); - let first_validator_config = swarm.validators_mut().next().unwrap().config().clone(); - let first_validator_storage_dir = first_validator_config.storage.dir(); - let output = Command::new(aptos_debugger.as_path()) - .current_dir(workspace_root()) - .args(&vec![ - "aptos-db", - "bootstrap", - first_validator_storage_dir.to_str().unwrap(), - "--genesis-txn-file", - genesis_blob_path.path().to_str().unwrap(), - ]) - .output() - .unwrap(); - - println!("8. Parsing the output to get the waypoint: {:?}", output); - let output_string = std::str::from_utf8(&output.stdout).unwrap(); - let waypoint = parse_waypoint(output_string); - - println!( - "9. Applying the genesis transaction to validators 0, 1 and 2. Waypoint: {:?}", - waypoint - ); - for (num_expected_peers, validator) in swarm.validators_mut().take(3).enumerate() { - apply_genesis_to_node( - validator, - genesis_transaction.clone(), - waypoint, - num_expected_peers, - num_expected_peers == 0, // Test admin service on the first validator - ) - .await; - } - - println!("10. Verifying that we're able to resume consensus and execute transactions!"); - swarm.wait_for_startup().await.unwrap(); - check_create_mint_transfer_node(&mut swarm, 0).await; - - println!("11. Verifying that the last validator is no longer in the validator set!"); - let validator_set = cli_test_framework.show_validator_set().await.unwrap(); - assert_eq!(validator_set.active_validators.len(), num_validators - 1); - - println!("12. Deleting the DB on validator 3 and verifying that it can still catch up via db-restore!"); - delete_db_and_execute_restore(&mut swarm, 3, waypoint, num_validators).await; - - println!("13. Verifying that we're able to execute transactions on validator 3!"); - check_create_mint_transfer_node(&mut swarm, 3).await; -} - -/// Applies the genesis transaction to the specified node and waits for it to become healthy -async fn apply_genesis_to_node( - node: &mut LocalNode, - genesis_transaction: Transaction, - waypoint: Waypoint, - num_expected_peers: usize, - test_admin_service: bool, -) { - // Insert the waypoint into the node's config - let mut node_config = node.config().clone(); - insert_waypoint(&mut node_config, waypoint); - - // Update the genesis transaction - node_config.execution.genesis = Some(genesis_transaction.clone()); - - // If the node is a validator, reset the initial safety rules config and the sync_only flag - if node_config.base.role.is_validator() { - // Reset the initial safety rules config - node_config - .consensus - .safety_rules - .initial_safety_rules_config = InitialSafetyRulesConfig::None; - - // Reset the sync_only flag to false (so the validator can participate in consensus) - node_config.consensus.sync_only = false; - } - - // Remove the admin service override (the config optimizer should run and set the config) - if test_admin_service { - // TODO: is there a way we can verify the config optimizer without doing this? - node_config.admin_service = AdminServiceConfig::default(); - } - - // Update the config and restart the node - update_node_config_and_restart(node, node_config.clone()); - - // Wait for the node to become healthy - let network_id = if node_config.base.role.is_validator() { - NetworkId::Validator - } else { - NetworkId::Vfn - }; - wait_for_health_and_connectivity(node, network_id, num_expected_peers).await; - - // Verify that the config optimizer ran and started the admin service at the default port - if test_admin_service { - verify_admin_service_is_running().await; - } -} - -/// Deletes the DB on the specified validator and verifies that it can still catch up via db-restore -async fn delete_db_and_execute_restore( - env: &mut LocalSwarm, - validator_index: usize, - waypoint: Waypoint, - num_nodes: usize, -) { - // Get the current epoch and version from the first validator - let first_validator = env.validators_mut().next().unwrap(); - let (current_epoch, current_version) = get_current_epoch_and_version(first_validator).await; - - // Perform a DB backup on the first validator - let first_validator_backup_port = first_validator - .config() - .storage - .backup_service_address - .port(); - let previous_epoch = current_epoch.checked_sub(1).unwrap(); - let (backup_path, _) = db_backup( - first_validator_backup_port, - previous_epoch, // target epoch: most recently closed epoch - current_version, // target version - current_version as usize, // txn batch size (version 0 is in its own batch) - previous_epoch as usize, // state snapshot interval - &[waypoint], - ); - - // Stop the specified validator - let validator = env.validators_mut().nth(validator_index).unwrap(); - validator.stop(); - - // Disable sync_only mode on the specified validator - let mut validator_config = validator.config().clone(); - validator_config.consensus.sync_only = false; - validator_config - .save_to_path(validator.config_path()) - .unwrap(); - - // Delete the DB on the specified validator - let db_dir = validator.config().storage.dir(); - fs::remove_dir_all(&db_dir).unwrap(); - - // Perform a DB restore on the specified validator - let enable_storage_sharding = validator - .config() - .storage - .rocksdb_configs - .enable_storage_sharding; - db_restore( - backup_path.path(), - db_dir.as_path(), - &[waypoint], - enable_storage_sharding, - None, - ); - - // Restart the validator and wait for it to become healthy - validator.start().unwrap(); - wait_for_health_and_connectivity(validator, NetworkId::Validator, num_nodes - 2).await; - - // Wait until the validator catches up to the current version - let client = validator.rest_client(); - let version = get_highest_synced_version(&env.get_all_nodes_clients_with_names()) - .await - .unwrap(); - loop { - if let Ok(resp) = client.get_ledger_information().await { - if resp.into_inner().version > version { - println!("Node 3 catches up on {}", version); - break; - } - } - tokio::time::sleep(Duration::from_secs(1)).await; - } -} - -/// Deletes the DB of the specified validator and waits for it to catch up -async fn delete_storage_and_wait_for_catchup(env: &mut LocalSwarm, validator_index: usize) { - // Stop the validator and delete the DB - let validator = env.validators_mut().nth(validator_index).unwrap(); - validator.stop(); - validator.clear_storage().await.unwrap(); - - // Restart the validator and wait for it to catch up - validator.start().unwrap(); - env.wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); -} - -/// Enables sync_only mode for the specified validator and wait for it to become healthy -async fn enable_sync_only_mode(num_nodes: usize, validator_node: &mut LocalNode) { - // Update the validator's config to enable sync_only mode - let mut validator_config = validator_node.config().clone(); - validator_config.consensus.sync_only = true; - update_node_config_and_restart(validator_node, validator_config.clone()); - - // Wait for the validator to become healthy - wait_for_health_and_connectivity(validator_node, NetworkId::Validator, num_nodes - 1).await; -} - -/// Generates a genesis write-set transaction that removes the last validator from the set -fn generate_genesis_transaction( - env: &mut LocalSwarm, - aptos_cli: PathBuf, -) -> (TempPath, Transaction) { - // Get the address of the last validator - let last_validator_address = env - .validators() - .last() - .unwrap() - .config() - .get_peer_id() - .unwrap(); - - // Create a write-set transaction that removes the last validator from the set - let script = format!( - r#" - script {{ - use aptos_framework::stake; - use aptos_framework::aptos_governance; - use aptos_framework::block; - - fun main(vm_signer: &signer, framework_signer: &signer) {{ - stake::remove_validators(framework_signer, &vector[@0x{}]); - block::emit_writeset_block_event(vm_signer, @0x1); - aptos_governance::force_end_epoch(framework_signer); - }} - }} - "#, - last_validator_address.to_hex() - ); - - // Write the transaction to a temporary file - let temp_script_path = TempPath::new(); - let mut move_script_path = temp_script_path.path().to_path_buf(); - move_script_path.set_extension("move"); - fs::write(move_script_path.as_path(), script).unwrap(); - - // Determine the framework path - let framework_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework"); - - // Create a temporary file to hold the genesis blob - let genesis_blob_path = TempPath::new(); - genesis_blob_path.create_as_file().unwrap(); - - // Generate the genesis write-set transaction - Command::new(aptos_cli.as_path()) - .current_dir(workspace_root()) - .args(&vec![ - "genesis", - "generate-admin-write-set", - "--output-file", - genesis_blob_path.path().to_str().unwrap(), - "--execute-as", - CORE_CODE_ADDRESS.clone().to_hex().as_str(), - "--script-path", - move_script_path.as_path().to_str().unwrap(), - "--framework-local-dir", - framework_path.as_os_str().to_str().unwrap(), - "--assume-yes", - ]) - .output() - .unwrap(); - - // Read the genesis transaction from the temporary file - let genesis_transaction = { - let buf = fs::read(genesis_blob_path.as_ref()).unwrap(); - bcs::from_bytes::(&buf).unwrap() - }; - - (genesis_blob_path, genesis_transaction) -} - -/// Returns the current epoch and version of the specified node -/// by querying the node's REST API. -async fn get_current_epoch_and_version(node: &mut LocalNode) -> (u64, u64) { - // Get current ledger info from the rest client - let rest_client = node.rest_client(); - let current_ledger_info = rest_client.get_ledger_information().await.unwrap(); - - // Return the current epoch and version - let current_epoch = current_ledger_info.inner().epoch; - let current_version = current_ledger_info.inner().version; - (current_epoch, current_version) -} - -/// Parses the waypoint from the output of the bootstrap command -fn parse_waypoint(bootstrap_command_output: &str) -> Waypoint { - let waypoint = Regex::new(r"Got waypoint: (\d+:\w+)") - .unwrap() - .captures(bootstrap_command_output) - .ok_or_else(|| { - anyhow!("Failed to parse `aptos-debugger aptos-db bootstrap` waypoint output!") - }); - Waypoint::from_str(waypoint.unwrap()[1].into()).unwrap() -} - -/// Update the specified node's config and restart the node -fn update_node_config_and_restart(node: &mut LocalNode, mut config: NodeConfig) { - // Stop the node - node.stop(); - - // Update the node's config - let node_path = node.config_path(); - config.save_to_path(node_path).unwrap(); - - // Restart the node - node.start().unwrap(); -} - -/// Verifies that the admin service is running on the default port -/// and that it returns the expected response when the endpoints are disabled. -async fn verify_admin_service_is_running() { - // Create a simple REST client - let rest_client = Client::new(); - - // Send a request to the admin service - let default_admin_service_port = AdminServiceConfig::default().port; - let admin_service_url = format!("http://127.0.0.1:{}", default_admin_service_port); - let request = rest_client.get(admin_service_url.clone()); - - // Verify that the admin service receives the request, and responds - // with a message indicating that the endpoint is disabled. - let response = request.send().await.unwrap(); - let response_string = response.text().await.unwrap(); - assert_eq!(response_string, "AdminService is not enabled."); -} - -/// Wait for the specified node to become healthy and for the -/// node to connect to the specified number of peers. -async fn wait_for_health_and_connectivity( - node: &mut LocalNode, - network_id: NetworkId, - num_expected_peers: usize, -) { - // Wait for the node to become healthy - let healthy_deadline = Instant::now() - .checked_add(Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .unwrap(); - node.wait_until_healthy(healthy_deadline) - .await - .unwrap_or_else(|err| { - let lsof_output = Command::new("lsof").arg("-i").output().unwrap(); - panic!( - "wait_until_healthy failed. lsof -i: {:?}: {}", - lsof_output, err - ); - }); - - // Wait for the node to connect to the expected number of peers - let connectivity_deadline = Instant::now() - .checked_add(Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS)) - .unwrap(); - node.wait_for_connectivity(network_id, num_expected_peers, connectivity_deadline) - .await - .unwrap(); -} diff --git a/testsuite/smoke-test/src/indexer.rs b/testsuite/smoke-test/src/indexer.rs deleted file mode 100644 index 9bb854d44df71..0000000000000 --- a/testsuite/smoke-test/src/indexer.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_cached_packages::aptos_stdlib::aptos_token_stdlib; -use aptos_forge::{AptosPublicInfo, Result, Swarm}; -use aptos_indexer::{ - database::{new_db_pool, PgDbPool, PgPoolConnection}, - models::transactions::TransactionQuery, -}; -use aptos_sdk::types::LocalAccount; -use diesel::RunQueryDsl; -use std::sync::Arc; - -pub fn wipe_database(conn: &mut PgPoolConnection) { - for command in [ - "DROP SCHEMA public CASCADE", - "CREATE SCHEMA public", - "GRANT ALL ON SCHEMA public TO postgres", - "GRANT ALL ON SCHEMA public TO public", - ] { - diesel::sql_query(command).execute(conn).unwrap(); - } -} - -pub fn get_database_url() -> String { - std::env::var("INDEXER_DATABASE_URL").expect("must set 'INDEXER_DATABASE_URL' to run tests!") -} - -pub fn setup_indexer() -> anyhow::Result { - let conn_pool = new_db_pool(get_database_url().as_str())?; - wipe_database(&mut conn_pool.get()?); - Ok(conn_pool) -} - -pub async fn execute_nft_txns<'t>( - creator: LocalAccount, - info: &mut AptosPublicInfo<'t>, -) -> Result<()> { - let collection_name = "collection name".to_owned().into_bytes(); - let token_name = "token name".to_owned().into_bytes(); - let collection_builder = - info.transaction_factory() - .payload(aptos_token_stdlib::token_create_collection_script( - collection_name.clone(), - "description".to_owned().into_bytes(), - "uri".to_owned().into_bytes(), - 20_000_000, - vec![false, false, false], - )); - - let collection_txn = creator.sign_with_transaction_builder(collection_builder); - info.client().submit_and_wait(&collection_txn).await?; - - let token_builder = - info.transaction_factory() - .payload(aptos_token_stdlib::token_create_token_script( - collection_name.clone(), - token_name.clone(), - "collection description".to_owned().into_bytes(), - 3, - 4, - "uri".to_owned().into_bytes(), - creator.address(), - 1, - 0, - vec![false, false, false, false, true], - vec!["age".as_bytes().to_vec()], - vec!["3".as_bytes().to_vec()], - vec!["int".as_bytes().to_vec()], - )); - - let token_txn = creator.sign_with_transaction_builder(token_builder); - info.client().submit_and_wait(&token_txn).await?; - - let token_mutator = - info.transaction_factory() - .payload(aptos_token_stdlib::token_mutate_token_properties( - creator.address(), - creator.address(), - collection_name.clone(), - token_name.clone(), - 0, - 2, - vec!["age".as_bytes().to_vec()], - vec!["2".as_bytes().to_vec()], - vec!["int".as_bytes().to_vec()], - )); - let mutate_txn = creator.sign_with_transaction_builder(token_mutator); - info.client().submit_and_wait(&mutate_txn).await?; - Ok(()) -} - -#[tokio::test] -async fn test_old_indexer() { - if aptos_indexer::should_skip_pg_tests() { - return; - } - - let conn_pool = setup_indexer().unwrap(); - - let mut swarm = crate::smoke_test_environment::SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.storage.enable_indexer = true; - - config.indexer.enabled = true; - config.indexer.postgres_uri = Some(get_database_url()); - config.indexer.processor = - Some(aptos_indexer::processors::default_processor::NAME.to_string()); - })) - .build() - .await; - - let mut info = swarm.aptos_public_info(); - - let ledger = info - .client() - .get_ledger_information() - .await - .unwrap() - .into_inner(); - - println!("ledger state: {:?}", ledger); - - // Set up accounts, generate some traffic - // TODO(Gas): double check this - let mut account1 = info - .create_and_fund_user_account(50_000_000_000) - .await - .unwrap(); - let account2 = info - .create_and_fund_user_account(50_000_000_000) - .await - .unwrap(); - // This transfer should emit events - let t_tx = info.transfer(&mut account1, &account2, 717).await.unwrap(); - // test NFT creation event indexing - execute_nft_txns(account1, &mut info).await.unwrap(); - - // Let the test complete! Yes, this does suck. - tokio::time::sleep(std::time::Duration::from_secs(3)).await; - - // Get them into the array and sort by type in order to prevent ordering from breaking tests - let mut transactions = vec![]; - for v in 0..2 { - transactions - .push(TransactionQuery::get_by_version(v, &mut conn_pool.get().unwrap()).unwrap()); - } - transactions.sort_by(|a, b| a.0.type_.partial_cmp(&b.0.type_).unwrap()); - - // This is a block metadata transaction - let (tx1, ut1, bmt1, events1, wsc1) = &transactions[0]; - assert_eq!(tx1.type_, "block_metadata_transaction"); - assert!(ut1.is_none()); - assert!(bmt1.is_some()); - assert!(!events1.is_empty()); - assert!(!wsc1.is_empty()); - - // This is the genesis transaction - let (tx0, ut0, bmt0, events0, wsc0) = &transactions[1]; - assert_eq!(tx0.type_, "genesis_transaction"); - assert!(ut0.is_none()); - assert!(bmt0.is_none()); - assert!(!events0.is_empty()); - assert!(wsc0.len() > 10); - - // This is the transfer - let (tx2, ut2, bmt2, events2, wsc2) = TransactionQuery::get_by_hash( - t_tx.hash.to_string().as_str(), - &mut conn_pool.get().unwrap(), - ) - .unwrap(); - - assert_eq!(tx2.type_, "user_transaction"); - assert_eq!(tx2.hash, t_tx.hash.to_string()); - - // This is a user transaction, so the bmt should be None - assert!(ut2.is_some()); - assert!(bmt2.is_none()); - assert!(wsc2.len() > 1); - assert_eq!(events2.len(), 2); - assert_eq!(events2.first().unwrap().type_, "0x1::coin::WithdrawEvent"); - assert_eq!(events2.get(1).unwrap().type_, "0x1::coin::DepositEvent"); -} diff --git a/testsuite/smoke-test/src/inspection_service.rs b/testsuite/smoke-test/src/inspection_service.rs deleted file mode 100644 index 13341406c0fe3..0000000000000 --- a/testsuite/smoke-test/src/inspection_service.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::new_local_swarm_with_aptos; -use aptos_forge::Swarm; - -#[tokio::test] -async fn test_inspection_service_connection() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let info = swarm.aptos_public_info(); - // Ping the inspection service index page and verify we get a successful response - let resp = reqwest::get(info.inspection_service_url().to_owned()) - .await - .unwrap(); - assert_eq!(reqwest::StatusCode::OK, resp.status()); -} diff --git a/testsuite/smoke-test/src/jwks/dummy_provider/mod.rs b/testsuite/smoke-test/src/jwks/dummy_provider/mod.rs deleted file mode 100644 index f82a6b9bee95c..0000000000000 --- a/testsuite/smoke-test/src/jwks/dummy_provider/mod.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_infallible::RwLock; -use hyper::{ - service::{make_service_fn, service_fn}, - Body, Request, Response, Server, -}; -use request_handler::RequestHandler; -use std::{convert::Infallible, mem, net::SocketAddr, sync::Arc}; -use tokio::{ - sync::{ - oneshot, - oneshot::{Receiver, Sender}, - }, - task::JoinHandle, -}; - -pub(crate) mod request_handler; - -/// A dummy OIDC provider. -pub struct DummyProvider { - close_tx: Sender<()>, - open_id_config_url: String, - handler_holder: Arc>>>, - server_join_handle: JoinHandle<()>, -} - -impl DummyProvider { - pub(crate) async fn spawn() -> Self { - let addr = SocketAddr::from(([127, 0, 0, 1], 0)); - let handler_holder = Arc::new(RwLock::new(None)); - let (port_tx, port_rx) = oneshot::channel::(); - let (close_tx, close_rx) = oneshot::channel::<()>(); - let server_join_handle = tokio::spawn(Self::run_server( - addr, - handler_holder.clone(), - port_tx, - close_rx, - )); - let actual_port = port_rx.await.unwrap(); - let open_id_config_url = format!("http://127.0.0.1:{}", actual_port); - Self { - close_tx, - open_id_config_url, - handler_holder, - server_join_handle, - } - } - - pub fn open_id_config_url(&self) -> String { - self.open_id_config_url.clone() - } - - pub fn update_request_handler( - &self, - handler: Option>, - ) -> Option> { - mem::replace(&mut *self.handler_holder.write(), handler) - } - - pub async fn shutdown(self) { - let DummyProvider { - close_tx, - server_join_handle, - .. - } = self; - close_tx.send(()).unwrap(); - server_join_handle.await.unwrap(); - } -} - -// Private functions. -impl DummyProvider { - async fn run_server( - addr: SocketAddr, - handler_holder: Arc>>>, - port_tx: Sender, - close_rx: Receiver<()>, - ) { - let make_svc = make_service_fn(move |_| { - let handler_holder_clone = handler_holder.clone(); - async move { - Ok::<_, Infallible>(service_fn(move |req| { - Self::handle_request(req, handler_holder_clone.clone()) - })) - } - }); - - let server = Server::bind(&addr).serve(make_svc); - let actual_addr = server.local_addr(); - port_tx.send(actual_addr.port()).unwrap(); - - // Graceful shutdown - let graceful = server.with_graceful_shutdown(async { - close_rx.await.unwrap(); - }); - - graceful.await.unwrap(); - } - - async fn handle_request( - request: Request, - handler_holder: Arc>>>, - ) -> Result, Infallible> { - let handler = handler_holder.write(); - let raw_response = handler.as_ref().unwrap().handle(request); - Ok(Response::new(Body::from(raw_response))) - } -} diff --git a/testsuite/smoke-test/src/jwks/dummy_provider/request_handler.rs b/testsuite/smoke-test/src/jwks/dummy_provider/request_handler.rs deleted file mode 100644 index 5310931ef1af7..0000000000000 --- a/testsuite/smoke-test/src/jwks/dummy_provider/request_handler.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_infallible::Mutex; -use hyper::{Body, Request}; -use move_core_types::account_address::AccountAddress; -use std::{collections::HashSet, str::FromStr}; - -/// A handler that handles JWK requests from a validator, -/// assuming the validator account address is written as the COOKIE. -pub trait RequestHandler: Send + Sync { - fn handle(&self, request: Request) -> Vec; -} - -pub struct StaticContentServer { - content: Vec, -} - -impl StaticContentServer { - pub fn new(content: Vec) -> Self { - Self { content } - } - - pub fn new_str(content: &str) -> Self { - Self::new(content.as_bytes().to_vec()) - } -} - -impl RequestHandler for StaticContentServer { - fn handle(&self, _origin: Request) -> Vec { - self.content.clone() - } -} - -fn origin_from_cookie(request: &Request) -> AccountAddress { - let cookie = request - .headers() - .get(hyper::header::COOKIE) - .unwrap() - .to_str() - .unwrap(); - AccountAddress::from_str(cookie).unwrap() -} - -/// The first `k` requesters will get content A forever, the rest will get content B forever. -pub struct EquivocatingServer { - content_a: Vec, - content_b: Vec, - k: usize, - requesters_observed: Mutex>, -} - -impl EquivocatingServer { - pub fn new(content_a: Vec, content_b: Vec, k: usize) -> Self { - Self { - content_a, - content_b, - k, - requesters_observed: Mutex::new(HashSet::new()), - } - } -} - -impl RequestHandler for EquivocatingServer { - fn handle(&self, request: Request) -> Vec { - let mut requesters_observed = self.requesters_observed.lock(); - let origin = origin_from_cookie(&request); - if requesters_observed.len() < self.k { - requesters_observed.insert(origin); - } - - if requesters_observed.contains(&origin) { - self.content_a.clone() - } else { - self.content_b.clone() - } - } -} - -/// This server first replies with `initial_thoughts`. -/// After enough audience receives it for at least once, it switches its reply to `second_thoughts`. -/// -/// This behavior simulates the situation where a provider performs a 2nd key rotation right after the 1st. -pub struct MindChangingServer { - initial_thoughts: Vec, - second_thoughts: Vec, - change_mind_threshold: usize, - requesters_observed: Mutex>, -} - -impl MindChangingServer { - pub fn new( - initial_thoughts: Vec, - second_thoughts: Vec, - change_mind_threshold: usize, - ) -> Self { - Self { - initial_thoughts, - second_thoughts, - change_mind_threshold, - requesters_observed: Mutex::new(HashSet::new()), - } - } -} - -impl RequestHandler for MindChangingServer { - fn handle(&self, request: Request) -> Vec { - let mut requesters_observed = self.requesters_observed.lock(); - let origin = origin_from_cookie(&request); - if requesters_observed.contains(&origin) - || requesters_observed.len() >= self.change_mind_threshold - { - self.second_thoughts.clone() - } else { - requesters_observed.insert(origin); - self.initial_thoughts.clone() - } - } -} diff --git a/testsuite/smoke-test/src/jwks/jwk_consensus_basic.rs b/testsuite/smoke-test/src/jwks/jwk_consensus_basic.rs deleted file mode 100644 index fe1f79ef3d304..0000000000000 --- a/testsuite/smoke-test/src/jwks/jwk_consensus_basic.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - jwks::{ - dummy_provider::{ - request_handler::{EquivocatingServer, StaticContentServer}, - DummyProvider, - }, - get_patched_jwks, update_jwk_consensus_config, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{ - jwks::{jwk::JWK, rsa::RSA_JWK, unsupported::UnsupportedJWK, AllProvidersJWKs, ProviderJWKs}, - on_chain_config::{JWKConsensusConfigV1, OIDCProvider, OnChainJWKConsensusConfig}, -}; -use std::{sync::Arc, time::Duration}; -use tokio::time::sleep; - -/// The validators should agree on the JWK after provider set is changed/JWK is rotated. -#[tokio::test] -async fn jwk_consensus_basic() { - let epoch_duration_secs = 30; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - })) - .build_with_cli(0) - .await; - let client = swarm.validators().next().unwrap().rest_client(); - let root_idx = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 2 taking too long to arrive!"); - - info!("Initially the provider set is empty. So should be the JWK map."); - - sleep(Duration::from_secs(10)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert!(patched_jwks.jwks.entries.is_empty()); - - info!("Adding some providers."); - let (provider_alice, provider_bob) = - tokio::join!(DummyProvider::spawn(), DummyProvider::spawn()); - - provider_alice.update_request_handler(Some(Arc::new(StaticContentServer::new_str( - r#" -{ - "keys": [ - {"kid":"kid1", "kty":"RSA", "e":"AQAB", "n":"n1", "alg":"RS384", "use":"sig"}, - {"n":"n0", "kty":"RSA", "use":"sig", "alg":"RS256", "e":"AQAB", "kid":"kid0"} - ] -} -"#, - )))); - provider_bob.update_request_handler(Some(Arc::new(StaticContentServer::new( - r#"{"keys": ["BOB_JWK_V0"]}"#.as_bytes().to_vec(), - )))); - let config = OnChainJWKConsensusConfig::V1(JWKConsensusConfigV1 { - oidc_providers: vec![ - OIDCProvider { - name: "https://alice.io".to_string(), - config_url: provider_alice.open_id_config_url(), - }, - OIDCProvider { - name: "https://bob.dev".to_string(), - config_url: provider_bob.open_id_config_url(), - }, - ], - }); - - let txn_summary = update_jwk_consensus_config(cli, root_idx, &config).await; - debug!("txn_summary={:?}", txn_summary); - - info!("Waiting for an on-chain update. 10 sec should be enough."); - sleep(Duration::from_secs(10)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert_eq!( - AllProvidersJWKs { - entries: vec![ - ProviderJWKs { - issuer: b"https://alice.io".to_vec(), - version: 1, - jwks: vec![ - JWK::RSA(RSA_JWK::new_256_aqab("kid0", "n0")).into(), - JWK::RSA(RSA_JWK::new_from_strs("kid1", "RSA", "RS384", "AQAB", "n1")) - .into(), - ], - }, - ProviderJWKs { - issuer: b"https://bob.dev".to_vec(), - version: 1, - jwks: vec![JWK::Unsupported(UnsupportedJWK::new_with_payload( - "\"BOB_JWK_V0\"" - )) - .into()], - }, - ] - }, - patched_jwks.jwks - ); - - info!("Rotating Alice keys. Also making https://alice.io gently equivocate."); - provider_alice.update_request_handler(Some(Arc::new(EquivocatingServer::new( - r#"{"keys": ["ALICE_JWK_V1A"]}"#.as_bytes().to_vec(), - r#"{"keys": ["ALICE_JWK_V1B"]}"#.as_bytes().to_vec(), - 1, - )))); - - info!("Waiting for an on-chain update. 30 sec should be enough."); - sleep(Duration::from_secs(30)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert_eq!( - AllProvidersJWKs { - entries: vec![ - ProviderJWKs { - issuer: b"https://alice.io".to_vec(), - version: 2, - jwks: vec![JWK::Unsupported(UnsupportedJWK::new_with_payload( - "\"ALICE_JWK_V1B\"" - )) - .into()], - }, - ProviderJWKs { - issuer: b"https://bob.dev".to_vec(), - version: 1, - jwks: vec![JWK::Unsupported(UnsupportedJWK::new_with_payload( - "\"BOB_JWK_V0\"" - )) - .into()], - }, - ] - }, - patched_jwks.jwks - ); - - info!("Tear down."); - provider_alice.shutdown().await; -} diff --git a/testsuite/smoke-test/src/jwks/jwk_consensus_per_issuer.rs b/testsuite/smoke-test/src/jwks/jwk_consensus_per_issuer.rs deleted file mode 100644 index bec556798bb96..0000000000000 --- a/testsuite/smoke-test/src/jwks/jwk_consensus_per_issuer.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - jwks::{ - dummy_provider::{ - request_handler::{EquivocatingServer, StaticContentServer}, - DummyProvider, - }, - get_patched_jwks, update_jwk_consensus_config, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{ - jwks::{jwk::JWK, unsupported::UnsupportedJWK, AllProvidersJWKs, ProviderJWKs}, - on_chain_config::{JWKConsensusConfigV1, OIDCProvider, OnChainJWKConsensusConfig}, -}; -use std::{sync::Arc, time::Duration}; -use tokio::time::sleep; - -/// The validators should do JWK consensus per issuer: -/// one problematic issuer should not block valid updates of other issuers. -#[tokio::test] -async fn jwk_consensus_per_issuer() { - let epoch_duration_secs = 30; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - })) - .build_with_cli(0) - .await; - let client = swarm.validators().next().unwrap().rest_client(); - let root_idx = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 2 taking too long to arrive!"); - - info!("Initially the provider set is empty. So should be the JWK map."); - - sleep(Duration::from_secs(10)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert!(patched_jwks.jwks.entries.is_empty()); - - info!("Adding some providers, one seriously equivocating, the other well behaving."); - let (provider_alice, provider_bob) = - tokio::join!(DummyProvider::spawn(), DummyProvider::spawn()); - provider_alice.update_request_handler(Some(Arc::new(EquivocatingServer::new( - r#"{"keys": ["ALICE_JWK_V1A"]}"#.as_bytes().to_vec(), - r#"{"keys": ["ALICE_JWK_V1B"]}"#.as_bytes().to_vec(), - 2, - )))); - provider_bob.update_request_handler(Some(Arc::new(StaticContentServer::new( - r#"{"keys": ["BOB_JWK_V0"]}"#.as_bytes().to_vec(), - )))); - let config = OnChainJWKConsensusConfig::V1(JWKConsensusConfigV1 { - oidc_providers: vec![ - OIDCProvider { - name: "https://alice.io".to_string(), - config_url: provider_alice.open_id_config_url(), - }, - OIDCProvider { - name: "https://bob.dev".to_string(), - config_url: provider_bob.open_id_config_url(), - }, - ], - }); - - let txn_summary = update_jwk_consensus_config(cli, root_idx, &config).await; - debug!("txn_summary={:?}", txn_summary); - - info!("Wait for 60 secs and there should only update for Bob, not Alice."); - sleep(Duration::from_secs(60)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert_eq!( - AllProvidersJWKs { - entries: vec![ProviderJWKs { - issuer: b"https://bob.dev".to_vec(), - version: 1, - jwks: vec![ - JWK::Unsupported(UnsupportedJWK::new_with_payload("\"BOB_JWK_V0\"")).into() - ], - }] - }, - patched_jwks.jwks - ); - - info!("Tear down."); - provider_alice.shutdown().await; -} diff --git a/testsuite/smoke-test/src/jwks/jwk_consensus_provider_change_mind.rs b/testsuite/smoke-test/src/jwks/jwk_consensus_provider_change_mind.rs deleted file mode 100644 index 1db5d8976ba64..0000000000000 --- a/testsuite/smoke-test/src/jwks/jwk_consensus_provider_change_mind.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - jwks::{ - dummy_provider::{ - request_handler::{MindChangingServer, StaticContentServer}, - DummyProvider, - }, - get_patched_jwks, update_jwk_consensus_config, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{ - jwks::{jwk::JWK, unsupported::UnsupportedJWK, AllProvidersJWKs, ProviderJWKs}, - on_chain_config::{JWKConsensusConfigV1, OIDCProvider, OnChainJWKConsensusConfig}, -}; -use std::{sync::Arc, time::Duration}; -use tokio::time::sleep; - -/// The validators should be able to reach JWK consensus -/// even if a provider double-rotates its key in a very short period of time. -/// First rotation may have been observed by some validators. -#[tokio::test] -async fn jwk_consensus_provider_change_mind() { - // Big epoch duration to ensure epoch change does not help reset validators if they are stuck. - let epoch_duration_secs = 1800; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - })) - .build_with_cli(0) - .await; - let client = swarm.validators().next().unwrap().rest_client(); - let root_idx = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 2 taking too long to arrive!"); - - info!("Initially the provider set is empty. So should be the ObservedJWKs."); - - sleep(Duration::from_secs(10)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert!(patched_jwks.jwks.entries.is_empty()); - - info!("Adding some providers."); - let (provider_alice, provider_bob) = - tokio::join!(DummyProvider::spawn(), DummyProvider::spawn()); - provider_alice.update_request_handler(Some(Arc::new(StaticContentServer::new( - r#"{"keys": ["ALICE_JWK_V0"]}"#.as_bytes().to_vec(), - )))); - provider_bob.update_request_handler(Some(Arc::new(MindChangingServer::new( - r#"{"keys": ["BOB_JWK_V0"]}"#.as_bytes().to_vec(), - r#"{"keys": ["BOB_JWK_V0_1"]}"#.as_bytes().to_vec(), - 2, - )))); - let config = OnChainJWKConsensusConfig::V1(JWKConsensusConfigV1 { - oidc_providers: vec![ - OIDCProvider { - name: "https://alice.io".to_string(), - config_url: provider_alice.open_id_config_url(), - }, - OIDCProvider { - name: "https://bob.dev".to_string(), - config_url: provider_bob.open_id_config_url(), - }, - ], - }); - - let txn_summary = update_jwk_consensus_config(cli, root_idx, &config).await; - debug!("txn_summary={:?}", txn_summary); - - info!("Waiting for an on-chain update. 30 secs should be enough."); - sleep(Duration::from_secs(30)).await; - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - assert_eq!( - AllProvidersJWKs { - entries: vec![ - ProviderJWKs { - issuer: b"https://alice.io".to_vec(), - version: 1, - jwks: vec![JWK::Unsupported(UnsupportedJWK::new_with_payload( - "\"ALICE_JWK_V0\"" - )) - .into()], - }, - ProviderJWKs { - issuer: b"https://bob.dev".to_vec(), - version: 1, - jwks: vec![JWK::Unsupported(UnsupportedJWK::new_with_payload( - "\"BOB_JWK_V0_1\"" - )) - .into()], - }, - ] - }, - patched_jwks.jwks - ); -} diff --git a/testsuite/smoke-test/src/jwks/mod.rs b/testsuite/smoke-test/src/jwks/mod.rs deleted file mode 100644 index b59f70ba2787a..0000000000000 --- a/testsuite/smoke-test/src/jwks/mod.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright © Aptos Foundation - -mod dummy_provider; -mod jwk_consensus_basic; -mod jwk_consensus_per_issuer; -mod jwk_consensus_provider_change_mind; - -use crate::smoke_test_environment::SwarmBuilder; -use aptos::{common::types::TransactionSummary, test::CliTestFramework}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_rest_client::Client; -use aptos_types::{ - jwks::{ - jwk::{JWKMoveStruct, JWK}, - unsupported::UnsupportedJWK, - AllProvidersJWKs, PatchedJWKs, ProviderJWKs, - }, - on_chain_config::OnChainJWKConsensusConfig, -}; -use move_core_types::account_address::AccountAddress; -use std::time::Duration; - -pub async fn update_jwk_consensus_config( - cli: CliTestFramework, - account_idx: usize, - config: &OnChainJWKConsensusConfig, -) -> TransactionSummary { - let script = match config { - OnChainJWKConsensusConfig::Off => r#" -script { - use aptos_framework::aptos_governance; - use aptos_framework::jwk_consensus_config; - fun main(core_resources: &signer) { - let framework = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let config = jwk_consensus_config::new_off(); - jwk_consensus_config::set_for_next_epoch(&framework, config); - aptos_governance::reconfigure(&framework); - } -} -"# - .to_string(), - OnChainJWKConsensusConfig::V1(config_v1) => { - let provider_lines = config_v1 - .oidc_providers - .iter() - .map(|provider| { - format!( - "jwk_consensus_config::new_oidc_provider(utf8(b\"{}\"), utf8(b\"{}\")),", - provider.name, provider.config_url - ) - }) - .collect::>() - .join("\n "); - format!( - r#" -script {{ - use aptos_framework::aptos_governance; - use aptos_framework::jwk_consensus_config; - use std::string::utf8; - - fun main(core_resources: &signer) {{ - let framework = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let config = jwk_consensus_config::new_v1(vector[ - {provider_lines} - ]); - jwk_consensus_config::set_for_next_epoch(&framework, config); - aptos_governance::reconfigure(&framework); - }} -}} -"# - ) - }, - }; - println!("script={script}"); - - cli.run_script(account_idx, script.as_str()).await.unwrap() -} - -async fn get_patched_jwks(rest_client: &Client) -> PatchedJWKs { - let maybe_response = rest_client - .get_account_resource_bcs::(AccountAddress::ONE, "0x1::jwks::PatchedJWKs") - .await; - let response = maybe_response.unwrap(); - response.into_inner() -} - -/// Patch the JWK with governance proposal and see it is effective. -#[tokio::test] -async fn jwk_patching() { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_aptos() - .build_with_cli(0) - .await; - let client = swarm.validators().next().unwrap().rest_client(); - let root_idx = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(60)) - .await - .expect("Epoch 2 taking too long to come!"); - - info!("Insert a JWK."); - let jwk_patch_script = r#" -script { - use aptos_framework::jwks; - use aptos_framework::aptos_governance; - fun main(core_resources: &signer) { - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let alice_jwk_0 = jwks::new_unsupported_jwk(b"alice_jwk_id_0", b"alice_jwk_payload_0"); - let patches = vector[ - jwks::new_patch_remove_all(), - jwks::new_patch_upsert_jwk(b"https://alice.com", alice_jwk_0), - ]; - jwks::set_patches(&framework_signer, patches); - } -} -"#; - - let txn_summary = cli.run_script(root_idx, jwk_patch_script).await.unwrap(); - debug!("txn_summary={:?}", txn_summary); - - info!("Use resource API to check the patch result."); - let patched_jwks = get_patched_jwks(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - - let expected_providers_jwks = AllProvidersJWKs { - entries: vec![ProviderJWKs { - issuer: b"https://alice.com".to_vec(), - version: 0, - jwks: vec![JWKMoveStruct::from(JWK::Unsupported(UnsupportedJWK { - id: b"alice_jwk_id_0".to_vec(), - payload: b"alice_jwk_payload_0".to_vec(), - }))], - }], - }; - assert_eq!(expected_providers_jwks, patched_jwks.jwks); -} diff --git a/testsuite/smoke-test/src/keyless.rs b/testsuite/smoke-test/src/keyless.rs deleted file mode 100644 index 84513df225af6..0000000000000 --- a/testsuite/smoke-test/src/keyless.rs +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::smoke_test_environment::SwarmBuilder; -use aptos::test::CliTestFramework; -use aptos_cached_packages::aptos_stdlib; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - poseidon_bn254::fr_to_bytes_le, - SigningKey, Uniform, -}; -use aptos_forge::{AptosPublicInfo, LocalSwarm, NodeExt, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_rest_client::Client; -use aptos_types::{ - jwks::{ - jwk::{JWKMoveStruct, JWK}, - rsa::RSA_JWK, - AllProvidersJWKs, PatchedJWKs, ProviderJWKs, - }, - keyless::{ - get_public_inputs_hash, test_utils, - test_utils::{ - get_sample_esk, get_sample_groth16_sig_and_pk, get_sample_iss, get_sample_jwk, - get_sample_openid_sig_and_pk, - }, - Configuration, EphemeralCertificate, Groth16ProofAndStatement, Groth16VerificationKey, - KeylessPublicKey, KeylessSignature, TransactionAndProof, KEYLESS_ACCOUNT_MODULE_NAME, - }, - transaction::{ - authenticator::{ - AccountAuthenticator, AnyPublicKey, AnySignature, EphemeralSignature, - TransactionAuthenticator, - }, - SignedTransaction, - }, -}; -use move_core_types::account_address::AccountAddress; -use rand::thread_rng; -use std::time::Duration; -// TODO(keyless): Test the override aud_val path - -#[tokio::test] -async fn test_keyless_oidc_txn_verifies() { - let (_, _, mut swarm, signed_txn) = get_transaction(get_sample_openid_sig_and_pk).await; - - info!("Submit OpenID transaction"); - let result = swarm - .aptos_public_info() - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if let Err(e) = result { - panic!("Error with OpenID TXN verification: {:?}", e) - } -} - -#[tokio::test] -async fn test_keyless_oidc_txn_with_bad_jwt_sig() { - let (tw_sk, config, jwk, mut swarm) = setup_local_net().await; - let (mut sig, pk) = get_sample_openid_sig_and_pk(); - - match &mut sig.cert { - EphemeralCertificate::ZeroKnowledgeSig(_) => panic!("Internal inconsistency"), - EphemeralCertificate::OpenIdSig(openid_sig) => { - openid_sig.jwt_sig = vec![0u8; 16] // Mauling the signature - }, - } - - let mut info = swarm.aptos_public_info(); - let signed_txn = sign_transaction(&mut info, sig, pk, &jwk, &config, &tw_sk).await; - - info!("Submit OpenID transaction with bad JWT signature"); - let result = info - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if result.is_ok() { - panic!("OpenID TXN with bad JWT signature should have failed verification") - } -} - -#[tokio::test] -async fn test_keyless_oidc_txn_with_expired_epk() { - let (tw_sk, config, jwk, mut swarm) = setup_local_net().await; - let (mut sig, pk) = get_sample_openid_sig_and_pk(); - - sig.exp_date_secs = 1; // This should fail the verification since the expiration date is way in the past - - let mut info = swarm.aptos_public_info(); - let signed_txn = sign_transaction(&mut info, sig, pk, &jwk, &config, &tw_sk).await; - - info!("Submit OpenID transaction with expired EPK"); - let result = info - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if result.is_ok() { - panic!("OpenID TXN with expired EPK should have failed verification") - } -} - -#[tokio::test] -async fn test_keyless_groth16_verifies() { - let (_, _, mut swarm, signed_txn) = get_transaction(get_sample_groth16_sig_and_pk).await; - - info!("Submit keyless Groth16 transaction"); - let result = swarm - .aptos_public_info() - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if let Err(e) = result { - panic!("Error with keyless Groth16 TXN verification: {:?}", e) - } -} - -#[tokio::test] -async fn test_keyless_groth16_with_mauled_proof() { - let (tw_sk, config, jwk, mut swarm) = setup_local_net().await; - let (sig, pk) = get_sample_groth16_sig_and_pk(); - - let mut info = swarm.aptos_public_info(); - let signed_txn = sign_transaction(&mut info, sig, pk, &jwk, &config, &tw_sk).await; - let signed_txn = maul_groth16_zkp_signature(signed_txn); - - info!("Submit keyless Groth16 transaction"); - let result = info - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if result.is_ok() { - panic!("Keyless Groth16 TXN with mauled proof should have failed verification") - } -} - -#[tokio::test] -async fn test_keyless_groth16_with_bad_tw_signature() { - let (_tw_sk, config, jwk, mut swarm) = setup_local_net().await; - let (sig, pk) = get_sample_groth16_sig_and_pk(); - - let mut info = swarm.aptos_public_info(); - - // using the sample ESK rather than the TW SK to get a bad training wheels signature - let signed_txn = sign_transaction(&mut info, sig, pk, &jwk, &config, &get_sample_esk()).await; - - info!("Submit keyless Groth16 transaction"); - let result = info - .client() - .submit_without_serializing_response(&signed_txn) - .await; - - if result.is_ok() { - panic!( - "Keyless Groth16 TXN with bad training wheels signature should have failed verification" - ) - } -} - -async fn sign_transaction<'a>( - info: &mut AptosPublicInfo<'a>, - mut sig: KeylessSignature, - pk: KeylessPublicKey, - jwk: &RSA_JWK, - config: &Configuration, - tw_sk: &Ed25519PrivateKey, -) -> SignedTransaction { - let addr = info - .create_user_account_with_any_key(&AnyPublicKey::keyless(pk.clone())) - .await - .unwrap(); - info.mint(addr, 10_000_000_000).await.unwrap(); - - let recipient = info - .create_and_fund_user_account(20_000_000_000) - .await - .unwrap(); - - let raw_txn = info - .transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(recipient.address(), 100)) - .sender(addr) - .sequence_number(1) - .build(); - - let esk = get_sample_esk(); - - let public_inputs_hash: Option<[u8; 32]> = - if let EphemeralCertificate::ZeroKnowledgeSig(_) = &sig.cert { - // This will only calculate the hash if it's needed, avoiding unnecessary computation. - Some(fr_to_bytes_le( - &get_public_inputs_hash(&sig, &pk, jwk, config).unwrap(), - )) - } else { - None - }; - - let mut txn_and_zkp = TransactionAndProof { - message: raw_txn.clone(), - proof: None, - }; - - // Compute the training wheels signature if not present - match &mut sig.cert { - EphemeralCertificate::ZeroKnowledgeSig(proof) => { - let proof_and_statement = Groth16ProofAndStatement { - proof: proof.proof.into(), - public_inputs_hash: public_inputs_hash.unwrap(), - }; - - proof.training_wheels_signature = Some(EphemeralSignature::ed25519( - tw_sk.sign(&proof_and_statement).unwrap(), - )); - - txn_and_zkp.proof = Some(proof.proof); - }, - EphemeralCertificate::OpenIdSig(_) => {}, - } - - sig.ephemeral_signature = EphemeralSignature::ed25519(esk.sign(&txn_and_zkp).unwrap()); - - SignedTransaction::new_keyless(raw_txn, pk, sig) -} - -fn maul_groth16_zkp_signature(txn: SignedTransaction) -> SignedTransaction { - // extract the keyless PK and signature - let (pk, sig) = match txn.authenticator() { - TransactionAuthenticator::SingleSender { - sender: AccountAuthenticator::SingleKey { authenticator }, - } => match (authenticator.public_key(), authenticator.signature()) { - (AnyPublicKey::Keyless { public_key }, AnySignature::Keyless { signature }) => { - (public_key.clone(), signature.clone()) - }, - _ => panic!("Expected keyless authenticator"), - }, - _ => panic!("Expected keyless authenticator"), - }; - - // disassemble the txn - let raw_txn = txn.into_raw_transaction(); - - test_utils::maul_raw_groth16_txn(pk, sig, raw_txn) -} - -async fn get_transaction( - get_pk_and_sig_func: fn() -> (KeylessSignature, KeylessPublicKey), -) -> ( - KeylessSignature, - KeylessPublicKey, - LocalSwarm, - SignedTransaction, -) { - let (tw_sk, config, jwk, mut swarm) = setup_local_net().await; - - let (sig, pk) = get_pk_and_sig_func(); - - let mut info = swarm.aptos_public_info(); - let signed_txn = - sign_transaction(&mut info, sig.clone(), pk.clone(), &jwk, &config, &tw_sk).await; - - (sig, pk, swarm, signed_txn) -} - -async fn setup_local_net() -> (Ed25519PrivateKey, Configuration, RSA_JWK, LocalSwarm) { - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(1) - .with_aptos() - .build_with_cli(0) - .await; - - let (tw_sk, config, jwk) = spawn_network_and_execute_gov_proposals(&mut swarm, &mut cli).await; - (tw_sk, config, jwk, swarm) -} - -async fn spawn_network_and_execute_gov_proposals( - swarm: &mut LocalSwarm, - cli: &mut CliTestFramework, -) -> (Ed25519PrivateKey, Configuration, RSA_JWK) { - let client = swarm.validators().next().unwrap().rest_client(); - let root_idx = cli.add_account_with_address_to_cli( - swarm.root_key(), - swarm.chain_info().root_account().address(), - ); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(60)) - .await - .expect("Epoch 2 taking too long to come!"); - - let vk_type_tag = format!( - "0x1::{}::Groth16VerificationKey", - KEYLESS_ACCOUNT_MODULE_NAME - ); - let maybe_response = client - .get_account_resource_bcs::( - AccountAddress::ONE, - vk_type_tag.as_str(), - ) - .await; - let vk = maybe_response.unwrap().into_inner(); - println!("Groth16 VK: {:?}", vk); - - let configuration_type_tag = format!("0x1::{}::Configuration", KEYLESS_ACCOUNT_MODULE_NAME); - let maybe_response = client - .get_account_resource_bcs::( - AccountAddress::ONE, - configuration_type_tag.as_str(), - ) - .await; - let config = maybe_response.unwrap().into_inner(); - println!("Keyless configuration before: {:?}", config); - - let iss = get_sample_iss(); - let jwk = get_sample_jwk(); - - let training_wheels_sk = Ed25519PrivateKey::generate(&mut thread_rng()); - let training_wheels_pk = Ed25519PublicKey::from(&training_wheels_sk); - - info!("Insert a JWK."); - let jwk_patch_script = format!( - r#" -script {{ -use aptos_framework::jwks; -use aptos_framework::{}; -use aptos_framework::aptos_governance; -use std::string::utf8; -use std::option; -fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let jwk_0 = jwks::new_rsa_jwk( - utf8(b"{}"), - utf8(b"{}"), - utf8(b"{}"), - utf8(b"{}") - ); - let patches = vector[ - jwks::new_patch_remove_all(), - jwks::new_patch_upsert_jwk(b"{}", jwk_0), - ]; - jwks::set_patches(&framework_signer, patches); - - {}::update_max_exp_horizon(&framework_signer, {}); - {}::update_training_wheels(&framework_signer, option::some(x"{}")); -}} -}} -"#, - KEYLESS_ACCOUNT_MODULE_NAME, - jwk.kid, - jwk.alg, - jwk.e, - jwk.n, - iss, - KEYLESS_ACCOUNT_MODULE_NAME, - Configuration::new_for_testing().max_exp_horizon_secs, - KEYLESS_ACCOUNT_MODULE_NAME, - hex::encode(training_wheels_pk.to_bytes()) - ); - - let txn_summary = cli.run_script(root_idx, &jwk_patch_script).await.unwrap(); - debug!("txn_summary={:?}", txn_summary); - - info!("Use resource API to check the patch result."); - let patched_jwks = get_latest_jwkset(&client).await; - debug!("patched_jwks={:?}", patched_jwks); - - let expected_providers_jwks = AllProvidersJWKs { - entries: vec![ProviderJWKs { - issuer: iss.into_bytes(), - version: 0, - jwks: vec![JWKMoveStruct::from(JWK::RSA(jwk.clone()))], - }], - }; - assert_eq!(expected_providers_jwks, patched_jwks.jwks); - - let maybe_response = client - .get_account_resource_bcs::( - AccountAddress::ONE, - configuration_type_tag.as_str(), - ) - .await; - let config = maybe_response.unwrap().into_inner(); - println!("Keyless configuration after: {:?}", config); - - let mut info = swarm.aptos_public_info(); - - // Increment sequence number since we patched a JWK - info.root_account().increment_sequence_number(); - - (training_wheels_sk, config, jwk) -} - -async fn get_latest_jwkset(rest_client: &Client) -> PatchedJWKs { - let maybe_response = rest_client - .get_account_resource_bcs::(AccountAddress::ONE, "0x1::jwks::PatchedJWKs") - .await; - let response = maybe_response.unwrap(); - response.into_inner() -} diff --git a/testsuite/smoke-test/src/lib.rs b/testsuite/smoke-test/src/lib.rs deleted file mode 100644 index 1bc84980668a5..0000000000000 --- a/testsuite/smoke-test/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -extern crate core; - -#[cfg(test)] -mod aptos; -#[cfg(test)] -mod aptos_cli; -#[cfg(test)] -mod client; -#[cfg(test)] -mod consensus; -#[cfg(test)] -mod execution; -#[cfg(test)] -mod full_nodes; -#[cfg(test)] -mod fullnode; -#[cfg(test)] -mod genesis; -#[cfg(test)] -mod indexer; -#[cfg(test)] -mod inspection_service; -#[cfg(test)] -mod jwks; -#[cfg(test)] -mod keyless; -#[cfg(test)] -mod network; -#[cfg(test)] -mod randomness; -#[cfg(test)] -mod rest_api; -#[cfg(test)] -mod rosetta; -#[cfg(test)] -mod state_sync; -#[cfg(test)] -mod storage; -#[cfg(test)] -mod test_smoke_tests; -#[cfg(test)] -mod transaction; -#[cfg(test)] -mod txn_broadcast; -#[cfg(test)] -mod txn_emitter; -#[cfg(test)] -mod upgrade; - -#[cfg(test)] -mod smoke_test_environment; - -#[cfg(test)] -mod test_utils; - -#[cfg(test)] -mod validator_txns; - -#[cfg(test)] -mod workspace_builder; - -#[cfg(test)] -pub(crate) mod utils; diff --git a/testsuite/smoke-test/src/network.rs b/testsuite/smoke-test/src/network.rs deleted file mode 100644 index 96f56e44707c3..0000000000000 --- a/testsuite/smoke-test/src/network.rs +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::{new_local_swarm_with_aptos, SwarmBuilder}, - state_sync::test_all_validator_failures, - test_utils::{MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS}, -}; -use aptos::test::CliTestFramework; -use aptos_config::{ - config::{ - DiscoveryMethod, FileDiscovery, Identity, NetworkConfig, NodeConfig, OverrideNodeConfig, - Peer, PeerSet, RestDiscovery, - }, - network_id::NetworkId, -}; -use aptos_crypto::{encoding_type::EncodingType, x25519, x25519::PrivateKey}; -use aptos_forge::{FullNode, Node, NodeExt, Swarm}; -use aptos_genesis::config::HostAndPort; -use aptos_sdk::move_types::account_address::AccountAddress; -use aptos_temppath::TempPath; -use std::{ - collections::HashMap, - path::Path, - sync::Arc, - time::{Duration, Instant}, -}; - -#[tokio::test] -async fn test_connection_limiting() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let version = swarm.versions().max().unwrap(); - let validator_peer_id = swarm.validators().next().unwrap().peer_id(); - - // Only allow file based discovery, disallow other nodes - let cli = CliTestFramework::local_new(0); - let host = HostAndPort::local(swarm.validators().next().unwrap().port()).unwrap(); - let (private_key, peer_set) = - generate_private_key_and_peer(&cli, host.clone(), [1u8; 32]).await; - let discovery_file = create_discovery_file(peer_set.clone()); - let mut full_node_config = NodeConfig::get_default_vfn_config(); - modify_network_config(&mut full_node_config, &NetworkId::Public, |network| { - network.discovery_method = DiscoveryMethod::None; - network.discovery_methods = vec![ - DiscoveryMethod::Onchain, - DiscoveryMethod::File(FileDiscovery { - path: discovery_file.path().to_path_buf(), - interval_secs: 1, - }), - ]; - network.max_inbound_connections = 0; - }); - - let vfn_peer_id = swarm - .add_validator_fullnode( - &version, - OverrideNodeConfig::new_with_default_base(full_node_config), - validator_peer_id, - ) - .unwrap(); - - // Wait till nodes are healthy - swarm - .fullnode_mut(vfn_peer_id) - .unwrap() - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - - // This node should be able to connect - let pfn_peer_id = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(add_identity_to_config( - NodeConfig::get_default_pfn_config(), - &NetworkId::Public, - private_key, - peer_set, - )), - ) - .await - .unwrap(); - swarm - .fullnode_mut(pfn_peer_id) - .unwrap() - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - // This node should connect - FullNode::wait_for_connectivity( - swarm.fullnode(pfn_peer_id).unwrap(), - Instant::now() + Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS), - ) - .await - .unwrap(); - assert_eq!( - 1, - swarm - .fullnode(vfn_peer_id) - .unwrap() - .get_connected_peers(NetworkId::Public, Some("inbound")) - .await - .unwrap() - .unwrap_or(0) - ); - - // And not be able to connect with an arbitrary one, limit is 0 - // TODO: Improve network checker to keep connection alive so we can test connection limits without nodes - let cli = CliTestFramework::local_new(0); - let (private_key, peer_set) = - generate_private_key_and_peer(&cli, host.clone(), [2u8; 32]).await; - let pfn_peer_id_fail = swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(add_identity_to_config( - NodeConfig::get_default_pfn_config(), - &NetworkId::Public, - private_key, - peer_set, - )), - ) - .await - .unwrap(); - - // This node should fail to connect - swarm - .fullnode_mut(pfn_peer_id_fail) - .unwrap() - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - tokio::time::sleep(Duration::from_secs(5)).await; - assert_eq!( - 1, - swarm - .fullnode(vfn_peer_id) - .unwrap() - .get_connected_peers(NetworkId::Public, Some("inbound")) - .await - .unwrap() - .unwrap_or(0) - ); -} - -#[tokio::test] -async fn test_rest_discovery() { - let mut swarm = SwarmBuilder::new_local(1).with_aptos().build().await; - - // Point to an already existing node - let (version, rest_endpoint) = { - let validator = swarm.validators().next().unwrap(); - (validator.version(), validator.rest_api_endpoint()) - }; - let mut full_node_config = NodeConfig::get_default_pfn_config(); - let network_config = full_node_config.full_node_networks.first_mut().unwrap(); - network_config.discovery_method = DiscoveryMethod::Rest(RestDiscovery { - url: rest_endpoint, - interval_secs: 1, - }); - - // Start a new node that should connect to the previous node only via REST - // The startup wait time should check if it connects successfully - swarm - .add_full_node( - &version, - OverrideNodeConfig::new_with_default_base(full_node_config), - ) - .await - .unwrap(); -} - -// Currently this test seems flaky: https://github.com/aptos-labs/aptos-core/issues/670 -#[ignore] -#[tokio::test] -async fn test_file_discovery() { - let cli = CliTestFramework::local_new(0); - // TODO: This host needs to be set properly - let host = HostAndPort::local(6180).unwrap(); - let (_, peer_set) = generate_private_key_and_peer(&cli, host, [0u8; 32]).await; - let discovery_file = Arc::new(create_discovery_file(peer_set)); - let discovery_file_for_closure = discovery_file.clone(); - let swarm = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(move |_, config, _| { - let discovery_file_for_closure2 = discovery_file_for_closure.clone(); - modify_network_config(config, &NetworkId::Validator, move |network| { - network.discovery_method = DiscoveryMethod::None; - network.discovery_methods = vec![ - DiscoveryMethod::Onchain, - DiscoveryMethod::File(FileDiscovery { - path: discovery_file_for_closure2.path().to_path_buf(), - interval_secs: 1, - }), - ]; - }); - })) - .build() - .await; - let _validator_peer_id = swarm.validators().next().unwrap().peer_id(); - - // At first we should be able to connect - // TODO: Check connection - - // Now when we clear the file, we shouldn't be able to connect - write_peerset_to_file((*discovery_file).as_ref(), HashMap::new()); - tokio::time::sleep(Duration::from_millis(300)).await; - - // TODO: Check connection -} - -// TODO: add more complex tests for the peer monitoring service. -// TODO: move the state sync functions to a utility file (instead of importing directly). - -#[tokio::test] -async fn test_peer_monitoring_service_enabled() { - // Create a swarm of 4 validators with peer monitoring enabled - let swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.peer_monitoring_service.enable_peer_monitoring_client = true; - })) - .build() - .await; - - // Test the ability of the validators to sync - test_all_validator_failures(swarm).await; -} - -#[ignore] -#[tokio::test] -// Requires that the network-perf-test feature is enabled -async fn test_network_performance_monitoring() { - // Create a swarm of 4 validators with peer monitoring enabled - let swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.peer_monitoring_service.enable_peer_monitoring_client = true; - config - .peer_monitoring_service - .performance_monitoring - .enable_rpc_testing = true; - config - .peer_monitoring_service - .performance_monitoring - .rpc_interval_usec = 1_000_000; // 1 sec - config - .peer_monitoring_service - .performance_monitoring - .rpc_data_size = 1024; // 1 KB - })) - .build() - .await; - - // Test the ability of the validators to sync - test_all_validator_failures(swarm).await; -} - -/// Creates a discovery file with the given `PeerSet` -fn create_discovery_file(peer_set: PeerSet) -> TempPath { - let discovery_file = TempPath::new(); - discovery_file.create_as_file().unwrap(); - write_peerset_to_file(discovery_file.as_ref(), peer_set); - discovery_file -} - -/// Generates `PrivateKey` and `Peer` information for a client / node -async fn generate_private_key_and_peer( - cli: &CliTestFramework, - host: HostAndPort, - seed: [u8; 32], -) -> (x25519::PrivateKey, HashMap) { - let temp_folder = TempPath::new(); - temp_folder.create_as_dir().unwrap(); - let private_key_path = temp_folder.path().join("private_key.txt"); - let extract_peer_path = temp_folder.path().join("extract_peer.txt"); - cli.generate_x25519_key(private_key_path.clone(), seed) - .await - .unwrap(); - - let private_key: x25519::PrivateKey = EncodingType::Hex - .load_key("test-key", private_key_path.as_path()) - .unwrap(); - let peer_set = cli - .extract_peer(host, private_key_path, extract_peer_path) - .await - .unwrap(); - // Check that public key matches peer - assert_eq!( - peer_set - .iter() - .next() - .unwrap() - .1 - .keys - .iter() - .next() - .unwrap(), - &private_key.public_key() - ); - // Check that peer id matches public key - assert_eq!( - private_key.public_key().as_slice(), - peer_set.iter().next().unwrap().0.as_slice() - ); - (private_key, peer_set) -} - -fn modify_network_config( - node_config: &mut NodeConfig, - network_id: &NetworkId, - modifier: F, -) { - let network = match network_id { - NetworkId::Validator => node_config.validator_network.as_mut().unwrap(), - _ => node_config - .full_node_networks - .iter_mut() - .find(|network| &network.network_id == network_id) - .unwrap(), - }; - - modifier(network); -} - -fn add_identity_to_config( - mut config: NodeConfig, - network_id: &NetworkId, - private_key: PrivateKey, - peer_set: PeerSet, -) -> NodeConfig { - let (peer_id, _) = peer_set.iter().next().unwrap(); - modify_network_config(&mut config, network_id, |network| { - network.identity = Identity::from_config(private_key, *peer_id); - }); - config -} - -pub fn write_peerset_to_file(path: &Path, peers: PeerSet) { - let file_contents = serde_yaml::to_vec(&peers).unwrap(); - std::fs::write(path, file_contents).unwrap(); -} diff --git a/testsuite/smoke-test/src/randomness/disable_feature_0.rs b/testsuite/smoke-test/src/randomness/disable_feature_0.rs deleted file mode 100644 index 5d394d3d4c51c..0000000000000 --- a/testsuite/smoke-test/src/randomness/disable_feature_0.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_on_chain_resource, script_to_disable_main_logic, verify_dkg_transcript, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{Node, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{ - dkg::DKGState, on_chain_config::OnChainRandomnessConfig, randomness::PerBlockRandomness, -}; -use std::{sync::Arc, time::Duration}; - -/// Disable on-chain randomness by only disabling randomness main logic. -#[tokio::test] -async fn disable_feature_0() { - let epoch_duration_secs = 20; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build_with_cli(0) - .await; - - let root_addr = swarm.chain_info().root_account().address(); - let root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_addr); - - let decrypt_key_map = decrypt_key_map(&swarm); - - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 3."); - - info!("Now in epoch 3. Disabling randomness main logic."); - let txn_summary = cli - .run_script(root_idx, script_to_disable_main_logic().as_str()) - .await - .expect("Txn execution error."); - debug!("txn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(4, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 4."); - - info!("Now in epoch 4. DKG transcript should still be available. Randomness seed should be unavailable."); - let dkg_session = get_on_chain_resource::(&client) - .await - .last_completed - .expect("dkg result for epoch 4 should be present"); - assert_eq!(4, dkg_session.target_epoch()); - assert!(verify_dkg_transcript(&dkg_session, &decrypt_key_map).is_ok()); - - let randomness_seed = get_on_chain_resource::(&client).await; - assert!(randomness_seed.seed.is_none()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(5, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 5."); - - info!("Now in epoch 5. DKG transcript should be unavailable. Randomness seed should be unavailable."); - let maybe_last_complete = get_on_chain_resource::(&client) - .await - .last_completed; - assert!( - maybe_last_complete.is_none() || maybe_last_complete.as_ref().unwrap().target_epoch() != 5 - ); - - let randomness_seed = get_on_chain_resource::(&client).await; - assert!(randomness_seed.seed.is_none()); -} diff --git a/testsuite/smoke-test/src/randomness/disable_feature_1.rs b/testsuite/smoke-test/src/randomness/disable_feature_1.rs deleted file mode 100644 index 86bb6f3dfc002..0000000000000 --- a/testsuite/smoke-test/src/randomness/disable_feature_1.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_on_chain_resource, script_to_update_consensus_config, - verify_dkg_transcript, - }, - smoke_test_environment::SwarmBuilder, - utils::get_current_consensus_config, -}; -use aptos_forge::{Node, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{ - dkg::DKGState, on_chain_config::OnChainRandomnessConfig, randomness::PerBlockRandomness, -}; -use std::{sync::Arc, time::Duration}; - -/// Disable on-chain randomness by only disabling validator transactions. -#[tokio::test] -async fn disable_feature_1() { - let epoch_duration_secs = 20; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build_with_cli(0) - .await; - - let root_addr = swarm.chain_info().root_account().address(); - let root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_addr); - - let decrypt_key_map = decrypt_key_map(&swarm); - - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 3."); - - info!("Now in epoch 3. Disabling validator transactions."); - let mut config = get_current_consensus_config(&client).await; - assert!(config.is_vtxn_enabled()); - config.disable_validator_txns(); - let disable_vtxn_script = script_to_update_consensus_config(&config); - debug!("disable_vtxn_script={}", disable_vtxn_script); - let txn_summary = cli - .run_script(root_idx, disable_vtxn_script.as_str()) - .await - .expect("Txn execution error."); - debug!("disabling_vtxn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(4, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 4."); - - info!("Now in epoch 4. DKG transcript should still be available. Randomness seed should be unavailable."); - let dkg_session = get_on_chain_resource::(&client) - .await - .last_completed - .expect("dkg result for epoch 4 should be present"); - assert_eq!(4, dkg_session.target_epoch()); - assert!(verify_dkg_transcript(&dkg_session, &decrypt_key_map).is_ok()); - - let randomness_seed = get_on_chain_resource::(&client).await; - assert!(randomness_seed.seed.is_none()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(5, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 5."); - - info!("Now in epoch 5. DKG transcript should be unavailable. Randomness seed should be unavailable."); - let maybe_last_complete = get_on_chain_resource::(&client) - .await - .last_completed; - assert!( - maybe_last_complete.is_none() || maybe_last_complete.as_ref().unwrap().target_epoch() != 5 - ); - - let randomness_seed = get_on_chain_resource::(&client).await; - assert!(randomness_seed.seed.is_none()); -} diff --git a/testsuite/smoke-test/src/randomness/dkg_with_validator_down.rs b/testsuite/smoke-test/src/randomness/dkg_with_validator_down.rs deleted file mode 100644 index 8791b49b5a90e..0000000000000 --- a/testsuite/smoke-test/src/randomness/dkg_with_validator_down.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{decrypt_key_map, verify_dkg_transcript, wait_for_dkg_finish}, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::NodeExt; -use aptos_types::on_chain_config::OnChainRandomnessConfig; -use std::sync::Arc; - -#[tokio::test] -async fn dkg_with_validator_down() { - let epoch_duration_secs = 10; - let estimated_dkg_latency_secs = 20; - let time_limit_secs = epoch_duration_secs + estimated_dkg_latency_secs; - - let mut swarm = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(|conf| { - conf.epoch_duration_secs = 10; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build() - .await; - let decrypt_key_map = decrypt_key_map(&swarm); - - let client = swarm.validators().last().unwrap().rest_client(); - println!("Wait for an epoch start."); - let dkg_session_1 = wait_for_dkg_finish(&client, None, time_limit_secs).await; - - println!("Current epoch is {}.", dkg_session_1.target_epoch()); - - println!("Take one validator down."); - swarm.validators_mut().take(1).for_each(|v| { - v.stop(); - }); - - println!( - "Wait until we fully entered epoch {}.", - dkg_session_1.target_epoch() + 1 - ); - - let dkg_session_2 = wait_for_dkg_finish( - &client, - Some(dkg_session_1.target_epoch() + 1), - time_limit_secs, - ) - .await; - - assert!(verify_dkg_transcript(&dkg_session_2, &decrypt_key_map).is_ok()); -} diff --git a/testsuite/smoke-test/src/randomness/dkg_with_validator_join_leave.rs b/testsuite/smoke-test/src/randomness/dkg_with_validator_join_leave.rs deleted file mode 100644 index 5747713141996..0000000000000 --- a/testsuite/smoke-test/src/randomness/dkg_with_validator_join_leave.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{decrypt_key_map, num_validators, verify_dkg_transcript, wait_for_dkg_finish}, - smoke_test_environment::SwarmBuilder, -}; -use aptos::test::CliTestFramework; -use aptos_forge::{Node, Swarm}; -use aptos_types::on_chain_config::OnChainRandomnessConfig; -use std::sync::Arc; - -#[tokio::test] -async fn dkg_with_validator_join_leave() { - let epoch_duration_secs = 40; - let estimated_dkg_latency_secs = 80; - let time_limit_secs = epoch_duration_secs + estimated_dkg_latency_secs; - - let mut swarm = SwarmBuilder::new_local(7) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build() - .await; - - let decrypt_key_map = decrypt_key_map(&swarm); - - println!("Wait for a moment when DKG is not running."); - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - let dkg_session_1 = wait_for_dkg_finish(&client, None, time_limit_secs).await; - println!( - "Current epoch is {}. Number of validators: {}.", - dkg_session_1.target_epoch(), - num_validators(&dkg_session_1) - ); - - println!( - "Wait until we fully entered epoch {}.", - dkg_session_1.target_epoch() + 1 - ); - let dkg_session_2 = wait_for_dkg_finish( - &client, - Some(dkg_session_1.target_epoch() + 1), - time_limit_secs, - ) - .await; - - println!( - "Current epoch is {}. Number of validators: {}.", - dkg_session_2.target_epoch(), - num_validators(&dkg_session_2) - ); - - println!("Letting one of the validators leave."); - let (victim_validator_sk, victim_validator_addr) = { - let victim_validator = swarm.validators().next().unwrap(); - let sk = victim_validator - .account_private_key() - .clone() - .unwrap() - .private_key(); - let addr = victim_validator.peer_id(); - (sk, addr) - }; - - println!("Give the victim some money so it can first send transactions."); - let mut public_info = swarm.chain_info().into_aptos_public_info(); - public_info - .mint(victim_validator_addr, 100000000000000) - .await - .unwrap(); - - println!("Send the txn to request leave."); - let faucet_endpoint: reqwest::Url = "http://localhost:8081".parse().unwrap(); - let mut cli = CliTestFramework::new( - client_endpoint, - faucet_endpoint, - /*num_cli_accounts=*/ 0, - ) - .await; - let idx = cli.add_account_to_cli(victim_validator_sk); - let txn_result = cli.leave_validator_set(idx, None).await.unwrap(); - println!("Txn result: {:?}", txn_result); - - println!( - "Wait until we fully entered epoch {}.", - dkg_session_2.target_epoch() + 1 - ); - let dkg_session_3 = wait_for_dkg_finish( - &client, - Some(dkg_session_2.target_epoch() + 1), - time_limit_secs, - ) - .await; - - println!( - "Current epoch is {}. Number of validators: {}.", - dkg_session_3.target_epoch(), - num_validators(&dkg_session_3) - ); - - assert!(verify_dkg_transcript(&dkg_session_3, &decrypt_key_map).is_ok()); - assert_eq!( - num_validators(&dkg_session_3), - num_validators(&dkg_session_2) - 1 - ); - - println!("Now re-join."); - let txn_result = cli.join_validator_set(idx, None).await; - println!("Txn result: {:?}", txn_result); - println!( - "Wait until we fully entered epoch {}.", - dkg_session_3.target_epoch() + 1 - ); - let dkg_session_4 = wait_for_dkg_finish( - &client, - Some(dkg_session_3.target_epoch() + 1), - time_limit_secs, - ) - .await; - - println!( - "Current epoch is {}. Number of validators: {}.", - dkg_session_4.target_epoch(), - num_validators(&dkg_session_4) - ); - - assert!(verify_dkg_transcript(&dkg_session_4, &decrypt_key_map).is_ok()); - assert_eq!( - num_validators(&dkg_session_4), - num_validators(&dkg_session_3) + 1 - ); -} diff --git a/testsuite/smoke-test/src/randomness/e2e_basic_consumption.rs b/testsuite/smoke-test/src/randomness/e2e_basic_consumption.rs deleted file mode 100644 index 9244062963b80..0000000000000 --- a/testsuite/smoke-test/src/randomness/e2e_basic_consumption.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::smoke_test_environment::SwarmBuilder; -use aptos::{move_tool::MemberId, test::CliTestFramework}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use aptos_logger::info; -use aptos_types::on_chain_config::OnChainRandomnessConfig; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; - -/// Publish the `on-chain-dice` example module, -/// run its function that consume on-chain randomness, and -/// print out the random results. -#[tokio::test] -async fn e2e_basic_consumption() { - let epoch_duration_secs = 20; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build_with_cli(0) - .await; - - let rest_client = swarm.validators().next().unwrap().rest_client(); - - info!("Wait for epoch 2. Epoch 1 does not have randomness."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 2 taking too long to arrive!"); - - let root_address = swarm.chain_info().root_account().address(); - info!("Root account: {}", root_address); - let _root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_address); - - info!("Publishing OnChainDice module."); - publish_on_chain_dice_module(&mut cli, 0).await; - - info!("Rolling the dice."); - let account = cli.account_id(0).to_hex_literal(); - let roll_func_id = MemberId::from_str(&format!("{}::dice::roll", account)).unwrap(); - for _ in 0..10 { - let txn_summary = cli - .run_function(0, None, roll_func_id.clone(), vec![], vec![]) - .await - .unwrap(); - info!("Roll txn summary: {:?}", txn_summary); - } - - info!("Collecting roll history."); - let dice_roll_history = rest_client - .get_account_resource_bcs::( - root_address, - format!("{}::dice::DiceRollHistory", account).as_str(), - ) - .await - .unwrap() - .into_inner(); - - info!("Roll history: {:?}", dice_roll_history.rolls); -} - -#[derive(Deserialize, Serialize)] -struct DiceRollHistory { - rolls: Vec, -} - -async fn publish_on_chain_dice_module(cli: &mut CliTestFramework, publisher_account_idx: usize) { - cli.init_move_dir(); - let mut package_addresses = BTreeMap::new(); - package_addresses.insert("module_owner", "_"); - - cli.init_package( - "OnChainDice".to_string(), - package_addresses, - Some(CliTestFramework::aptos_framework_dir()), - ) - .await - .unwrap(); - - let content = - include_str!("../../../../aptos-move/move-examples/on_chain_dice/sources/dice.move") - .to_string(); - cli.add_file_in_package("sources/dice.move", content); - - cli.wait_for_account(publisher_account_idx).await.unwrap(); - - info!("Move package dir: {}", cli.move_dir().display()); - - let mut named_addresses = BTreeMap::new(); - let account_str = cli.account_id(publisher_account_idx).to_string(); - named_addresses.insert("module_owner", account_str.as_str()); - cli.publish_package(0, None, named_addresses, None) - .await - .unwrap(); -} diff --git a/testsuite/smoke-test/src/randomness/e2e_correctness.rs b/testsuite/smoke-test/src/randomness/e2e_correctness.rs deleted file mode 100644 index f1f0d962ef548..0000000000000 --- a/testsuite/smoke-test/src/randomness/e2e_correctness.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_current_version, get_on_chain_resource, verify_dkg_transcript, - verify_randomness, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{NodeExt, SwarmExt}; -use aptos_logger::info; -use aptos_types::{dkg::DKGState, on_chain_config::OnChainRandomnessConfig}; -use std::{sync::Arc, time::Duration}; - -/// Verify the correctness of DKG transcript and block-level randomness seed. -#[tokio::test] -async fn randomness_correctness() { - let epoch_duration_secs = 20; - - let (swarm, _cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build_with_cli(0) - .await; - - let decrypt_key_map = decrypt_key_map(&swarm); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - info!("Wait for epoch 2. Epoch 1 does not have randomness."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 2 taking too long to arrive!"); - - info!("Verify DKG correctness for epoch 2."); - let dkg_session = get_on_chain_resource::(&rest_client).await; - assert!(verify_dkg_transcript(dkg_session.last_complete(), &decrypt_key_map).is_ok()); - - // Verify the randomness in 5 versions. - for _ in 0..5 { - let cur_txn_version = get_current_version(&rest_client).await; - info!("Verifying WVUF output for version {}.", cur_txn_version); - let wvuf_verify_result = - verify_randomness(&decrypt_key_map, &rest_client, cur_txn_version).await; - println!("wvuf_verify_result={:?}", wvuf_verify_result); - assert!(wvuf_verify_result.is_ok()); - } - - info!("Wait for epoch 3."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Epoch 3 taking too long to arrive!"); - - info!("Verify DKG correctness for epoch 3."); - let dkg_session = get_on_chain_resource::(&rest_client).await; - assert!(verify_dkg_transcript(dkg_session.last_complete(), &decrypt_key_map).is_ok()); - - // Again, verify the randomness in 5 versions. - for _ in 0..5 { - let cur_txn_version = get_current_version(&rest_client).await; - info!("Verifying WVUF output for version {}.", cur_txn_version); - let wvuf_verify_result = - verify_randomness(&decrypt_key_map, &rest_client, cur_txn_version).await; - println!("wvuf_verify_result={:?}", wvuf_verify_result); - assert!(wvuf_verify_result.is_ok()); - } -} diff --git a/testsuite/smoke-test/src/randomness/enable_feature_0.rs b/testsuite/smoke-test/src/randomness/enable_feature_0.rs deleted file mode 100644 index 12a80d4a0c9a3..0000000000000 --- a/testsuite/smoke-test/src/randomness/enable_feature_0.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_on_chain_resource, script_to_enable_main_logic, - script_to_update_consensus_config, verify_dkg_transcript, - }, - smoke_test_environment::SwarmBuilder, - utils::get_current_consensus_config, -}; -use aptos_forge::{Node, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{dkg::DKGState, on_chain_config::OnChainRandomnessConfig}; -use std::{sync::Arc, time::Duration}; - -/// Enable on-chain randomness in the following steps. -/// - Enable randomness main logic in epoch `e`. -/// - Enable validator transactions in consensus config in epoch `e + 1`. -#[tokio::test] -async fn enable_feature_0() { - let epoch_duration_secs = 20; - let estimated_dkg_latency_secs = 40; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // start with vtxn disabled and randomness off. - conf.consensus_config.disable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_disabled()); - })) - .build_with_cli(0) - .await; - - let root_addr = swarm.chain_info().root_account().address(); - let root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_addr); - - let decrypt_key_map = decrypt_key_map(&swarm); - - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 3."); - - info!("Now in epoch 3. Enabling randomness main logic."); - let enable_dkg_script = script_to_enable_main_logic(); - - let txn_summary = cli - .run_script(root_idx, enable_dkg_script.as_str()) - .await - .expect("Txn execution error."); - debug!("enabling_dkg_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(4, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 4."); - - info!("Now in epoch 4. Enabling validator transactions."); - let mut config = get_current_consensus_config(&client).await; - config.enable_validator_txns(); - let enable_vtxn_script = script_to_update_consensus_config(&config); - debug!("enable_vtxn_script={}", enable_vtxn_script); - let txn_summary = cli - .run_script(root_idx, enable_vtxn_script.as_str()) - .await - .expect("Txn execution error."); - debug!("enabling_vtxn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(5, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 5."); - - info!("Now in epoch 5. Both DKG and vtxn are enabled. There should be no randomness since DKG did not happen at the end of last epoch."); - let maybe_last_complete = get_on_chain_resource::(&client) - .await - .last_completed; - assert!( - maybe_last_complete.is_none() || maybe_last_complete.as_ref().unwrap().target_epoch() != 5 - ); - - info!("Waiting for epoch 6."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch( - 6, - Duration::from_secs(epoch_duration_secs + estimated_dkg_latency_secs), - ) - .await - .expect("Waited too long for epoch 6."); - - let dkg_session = get_on_chain_resource::(&client) - .await - .last_completed - .expect("dkg result for epoch 6 should be present"); - assert_eq!(6, dkg_session.target_epoch()); - assert!(verify_dkg_transcript(&dkg_session, &decrypt_key_map).is_ok()); -} diff --git a/testsuite/smoke-test/src/randomness/enable_feature_1.rs b/testsuite/smoke-test/src/randomness/enable_feature_1.rs deleted file mode 100644 index 45cbe928d62ed..0000000000000 --- a/testsuite/smoke-test/src/randomness/enable_feature_1.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_on_chain_resource, script_to_enable_main_logic, - script_to_update_consensus_config, verify_dkg_transcript, - }, - smoke_test_environment::SwarmBuilder, - utils::get_current_consensus_config, -}; -use aptos_forge::{Node, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{dkg::DKGState, on_chain_config::OnChainRandomnessConfig}; -use std::{sync::Arc, time::Duration}; - -/// Enable on-chain randomness in the following steps. -/// - Enable validator transactions in consensus config in epoch `e`. -/// - Enable randomness main logic in epoch `e + 1`. -#[tokio::test] -async fn enable_feature_1() { - let epoch_duration_secs = 20; - let estimated_dkg_latency_secs = 40; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // start with vtxn disabled and randomness off. - conf.consensus_config.disable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_disabled()); - })) - .build_with_cli(0) - .await; - - let root_addr = swarm.chain_info().root_account().address(); - let root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_addr); - - let decrypt_key_map = decrypt_key_map(&swarm); - - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 3."); - - info!("Now in epoch 3. Enabling validator transactions."); - let mut config = get_current_consensus_config(&client).await; - config.enable_validator_txns(); - let enable_vtxn_script = script_to_update_consensus_config(&config); - - debug!("enable_vtxn_script={}", enable_vtxn_script); - let txn_summary = cli - .run_script(root_idx, enable_vtxn_script.as_str()) - .await - .expect("Txn execution error."); - debug!("enabling_vtxn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(4, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 4."); - - info!("Now in epoch 4. Enabling randomness main logic."); - let enable_main_logic_script = script_to_enable_main_logic(); - let txn_summary = cli - .run_script(root_idx, enable_main_logic_script.as_str()) - .await - .expect("Txn execution error."); - debug!("txn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(5, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 5."); - - info!("Now in epoch 5. Both DKG and vtxn are enabled. There should be no randomness since DKG did not happen at the end of last epoch."); - let maybe_last_complete = get_on_chain_resource::(&client) - .await - .last_completed; - assert!( - maybe_last_complete.is_none() || maybe_last_complete.as_ref().unwrap().target_epoch() != 5 - ); - - info!("Waiting for epoch 6."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch( - 6, - Duration::from_secs(epoch_duration_secs + estimated_dkg_latency_secs), - ) - .await - .expect("Waited too long for epoch 6."); - - let dkg_session = get_on_chain_resource::(&client) - .await - .last_completed - .expect("dkg result for epoch 6 should be present"); - assert_eq!(6, dkg_session.target_epoch()); - assert!(verify_dkg_transcript(&dkg_session, &decrypt_key_map).is_ok()); -} diff --git a/testsuite/smoke-test/src/randomness/enable_feature_2.rs b/testsuite/smoke-test/src/randomness/enable_feature_2.rs deleted file mode 100644 index 65330b9ac2ca3..0000000000000 --- a/testsuite/smoke-test/src/randomness/enable_feature_2.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{decrypt_key_map, get_on_chain_resource, verify_dkg_transcript}, - smoke_test_environment::SwarmBuilder, - utils::get_current_consensus_config, -}; -use aptos_forge::{Node, Swarm, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_types::{dkg::DKGState, on_chain_config::OnChainRandomnessConfig}; -use std::{sync::Arc, time::Duration}; - -/// Enable on-chain randomness by enabling validator transactions and randomness main logic. -#[tokio::test] -async fn enable_feature_2() { - let epoch_duration_secs = 20; - let estimated_dkg_latency_secs = 40; - - let (mut swarm, mut cli, _faucet) = SwarmBuilder::new_local(4) - .with_num_fullnodes(1) - .with_aptos() - .with_init_genesis_config(Arc::new(move |conf| { - conf.epoch_duration_secs = epoch_duration_secs; - conf.allow_new_validators = true; - - // start with vtxn disabled and randomness off. - conf.consensus_config.disable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_disabled()); - })) - .build_with_cli(0) - .await; - - let root_addr = swarm.chain_info().root_account().address(); - let root_idx = cli.add_account_with_address_to_cli(swarm.root_key(), root_addr); - - let decrypt_key_map = decrypt_key_map(&swarm); - - let client_endpoint = swarm.validators().nth(1).unwrap().rest_api_endpoint(); - let client = aptos_rest_client::Client::new(client_endpoint.clone()); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(3, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 3."); - - info!("Now in epoch 3. Enabling all the dependencies at the same time."); - let mut config = get_current_consensus_config(&client).await; - config.enable_validator_txns(); - let config_bytes = bcs::to_bytes(&config).unwrap(); - let script = format!( - r#" -script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - use aptos_framework::randomness_config; - use aptos_std::fixed_point64; - - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let consensus_config_bytes = vector{:?}; - consensus_config::set_for_next_epoch(&framework_signer, consensus_config_bytes); - let randomness_config = randomness_config::new_v1( - fixed_point64::create_from_rational(1, 2), - fixed_point64::create_from_rational(2, 3) - ); - randomness_config::set_for_next_epoch(&framework_signer, randomness_config); - aptos_governance::reconfigure(&framework_signer); - }} -}} -"#, - config_bytes - ); - - debug!("script={}", script); - let txn_summary = cli - .run_script(root_idx, script.as_str()) - .await - .expect("Txn execution error."); - debug!("txn_summary={:?}", txn_summary); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(4, Duration::from_secs(epoch_duration_secs * 2)) - .await - .expect("Waited too long for epoch 4."); - - info!("Now in epoch 4. Both DKG and vtxn are enabled. There should be no randomness since DKG did not happen at the end of last epoch."); - let maybe_last_complete = get_on_chain_resource::(&client) - .await - .last_completed; - assert!( - maybe_last_complete.is_none() || maybe_last_complete.as_ref().unwrap().target_epoch() != 4 - ); - - info!("Waiting for epoch 5."); - swarm - .wait_for_all_nodes_to_catchup_to_epoch( - 5, - Duration::from_secs(epoch_duration_secs + estimated_dkg_latency_secs), - ) - .await - .expect("Waited too long for epoch 5."); - - let dkg_session = get_on_chain_resource::(&client) - .await - .last_completed - .expect("dkg result for epoch 5 should be present"); - assert_eq!(5, dkg_session.target_epoch()); - assert!(verify_dkg_transcript(&dkg_session, &decrypt_key_map).is_ok()); -} diff --git a/testsuite/smoke-test/src/randomness/mod.rs b/testsuite/smoke-test/src/randomness/mod.rs deleted file mode 100644 index 2b67ccbb26dd1..0000000000000 --- a/testsuite/smoke-test/src/randomness/mod.rs +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright © Aptos Foundation - -use anyhow::{anyhow, ensure, Result}; -use aptos_crypto::{compat::Sha3_256, Uniform}; -use aptos_dkg::weighted_vuf::traits::WeightedVUF; -use aptos_forge::LocalSwarm; -use aptos_logger::info; -use aptos_rest_client::Client; -use aptos_types::{ - dkg::{DKGSessionState, DKGState, DKGTrait, DefaultDKG}, - on_chain_config::{OnChainConfig, OnChainConsensusConfig}, - randomness::{PerBlockRandomness, RandMetadataToSign, WVUF}, - validator_verifier::ValidatorConsensusInfo, -}; -use digest::Digest; -use move_core_types::{account_address::AccountAddress, language_storage::CORE_CODE_ADDRESS}; -use rand::{prelude::StdRng, SeedableRng}; -use std::{collections::HashMap, time::Duration}; -use tokio::time::Instant; - -mod disable_feature_0; -mod disable_feature_1; -mod dkg_with_validator_down; -mod dkg_with_validator_join_leave; -mod e2e_basic_consumption; -mod e2e_correctness; -mod enable_feature_0; -mod enable_feature_1; -mod enable_feature_2; -mod validator_restart_during_dkg; - -#[allow(dead_code)] -async fn get_current_version(rest_client: &Client) -> u64 { - rest_client - .get_ledger_information() - .await - .unwrap() - .inner() - .version -} - -async fn get_on_chain_resource(rest_client: &Client) -> T { - let maybe_response = rest_client - .get_account_resource_bcs::(CORE_CODE_ADDRESS, T::struct_tag().to_string().as_str()) - .await; - let response = maybe_response.unwrap(); - response.into_inner() -} - -#[allow(dead_code)] -async fn get_on_chain_resource_at_version( - rest_client: &Client, - version: u64, -) -> T { - let maybe_response = rest_client - .get_account_resource_at_version_bcs::( - CORE_CODE_ADDRESS, - T::struct_tag().to_string().as_str(), - version, - ) - .await; - let response = maybe_response.unwrap(); - response.into_inner() -} - -/// Poll the on-chain state until we see a DKG session finishes. -/// Return a `DKGSessionState` of the DKG session seen. -#[allow(dead_code)] -async fn wait_for_dkg_finish( - client: &Client, - target_epoch: Option, - time_limit_secs: u64, -) -> DKGSessionState { - let mut dkg_state = get_on_chain_resource::(client).await; - let timer = Instant::now(); - while timer.elapsed().as_secs() < time_limit_secs - && !(dkg_state.in_progress.is_none() - && dkg_state.last_completed.is_some() - && (target_epoch.is_none() - || dkg_state - .last_completed - .as_ref() - .map(|session| session.metadata.dealer_epoch + 1) - == target_epoch)) - { - tokio::time::sleep(Duration::from_secs(1)).await; - dkg_state = get_on_chain_resource::(client).await; - } - assert!(timer.elapsed().as_secs() < time_limit_secs); - dkg_state.last_complete().clone() -} - -/// Verify that DKG transcript of epoch i (stored in `new_dkg_state`) is correctly generated -/// by the validator set in epoch i-1 (stored in `new_dkg_state`). -fn verify_dkg_transcript( - dkg_session: &DKGSessionState, - decrypt_key_map: &HashMap::NewValidatorDecryptKey>, -) -> Result<()> { - info!( - "Verifying the transcript generated in epoch {}.", - dkg_session.metadata.dealer_epoch, - ); - let pub_params = DefaultDKG::new_public_params(&dkg_session.metadata); - let transcript = bcs::from_bytes(dkg_session.transcript.as_slice()).map_err(|e| { - anyhow!("DKG transcript verification failed with transcript deserialization error: {e}") - })?; - println!("transcript={:?}", transcript); - DefaultDKG::verify_transcript(&pub_params, &transcript)?; - - info!("Double-verifying by reconstructing the dealt secret."); - let dealt_secret_from_shares = dealt_secret_from_shares( - dkg_session - .metadata - .target_validator_consensus_infos_cloned(), - decrypt_key_map, - &pub_params, - &transcript, - ); - - println!("dealt_secret_from_shares={:?}", dealt_secret_from_shares); - - let dealt_secret_from_inputs = dealt_secret_from_input( - &transcript, - &pub_params, - &pub_params.session_metadata.dealer_consensus_infos_cloned(), - ); - println!("dealt_secret_from_inputs={:?}", dealt_secret_from_inputs); - - ensure!( - dealt_secret_from_shares == dealt_secret_from_inputs, - "dkg transcript verification failed with final check failure" - ); - Ok(()) -} - -fn dealt_secret_from_shares( - target_validator_set: Vec, - decrypt_key_map: &HashMap::NewValidatorDecryptKey>, - pub_params: &::PublicParams, - transcript: &::Transcript, -) -> ::DealtSecret { - let player_share_pairs = target_validator_set - .iter() - .enumerate() - .map(|(idx, validator_info)| { - let dk = decrypt_key_map.get(&validator_info.address).unwrap(); - let (secret_share, _pub_key_share) = DefaultDKG::decrypt_secret_share_from_transcript( - pub_params, transcript, idx as u64, dk, - ) - .unwrap(); - (idx as u64, secret_share) - }) - .collect(); - - DefaultDKG::reconstruct_secret_from_shares(pub_params, player_share_pairs).unwrap() -} - -fn dealt_secret_from_input( - trx: &::Transcript, - pub_params: &::PublicParams, - dealer_validator_infos: &[ValidatorConsensusInfo], -) -> ::DealtSecret { - let dealers = DefaultDKG::get_dealers(trx); - println!("dealers={:?}", dealers); - let input_secrets = dealers - .into_iter() - .map(|dealer_idx| { - let cur_addr = dealer_validator_infos[dealer_idx as usize].address; - // Same seed is used in `DKGManager::setup_deal_broadcast` for smoke tests. - let mut rng = StdRng::from_seed(cur_addr.into_bytes()); - ::InputSecret::generate(&mut rng) - }) - .collect(); - - let aggregated_input_secret = DefaultDKG::aggregate_input_secret(input_secrets); - DefaultDKG::dealt_secret_from_input(pub_params, &aggregated_input_secret) -} - -#[allow(dead_code)] -fn num_validators(dkg_state: &DKGSessionState) -> usize { - dkg_state.metadata.target_validator_set.len() -} - -fn decrypt_key_map( - swarm: &LocalSwarm, -) -> HashMap::NewValidatorDecryptKey> { - swarm - .validators() - .map(|validator| { - let dk = validator - .config() - .consensus - .safety_rules - .initial_safety_rules_config - .identity_blob() - .unwrap() - .try_into_dkg_new_validator_decrypt_key() - .unwrap(); - (validator.peer_id(), dk) - }) - .collect::>() -} - -/// Fetch the DKG result and the block randomness (from aggregation) for a specific version. -/// Derive the distributed secret from DKG result. -/// Verify that the randomness from aggregation (the actual one store on chain) equals to -/// the randomness from direct evaluation using the distributed secret (the expected one). -async fn verify_randomness( - decrypt_key_map: &HashMap::NewValidatorDecryptKey>, - rest_client: &Client, - version: u64, -) -> Result<()> { - // Fetch resources. - let (dkg_state, on_chain_block_randomness) = tokio::join!( - get_on_chain_resource_at_version::(rest_client, version), - get_on_chain_resource_at_version::(rest_client, version) - ); - - ensure!( - on_chain_block_randomness.seed.is_some(), - "randomness verification failed with seed missing" - ); - - // Derive the shared secret. - let dkg_session = dkg_state - .last_completed - .ok_or_else(|| anyhow!("randomness verification failed with missing dkg result"))?; - let dkg_pub_params = DefaultDKG::new_public_params(&dkg_session.metadata); - let transcript = - bcs::from_bytes::<::Transcript>(dkg_session.transcript.as_slice()) - .map_err(|_| { - anyhow!( - "randomness verification failed with on-chain dkg transcript deserialization error" - ) - })?; - let dealt_secret = dealt_secret_from_shares( - dkg_session - .metadata - .target_validator_consensus_infos_cloned(), - decrypt_key_map, - &dkg_pub_params, - &transcript, - ); - - // Compare the outputs from 2 paths. - let rand_metadata = RandMetadataToSign { - epoch: on_chain_block_randomness.epoch, - round: on_chain_block_randomness.round, - }; - let input = bcs::to_bytes(&rand_metadata).unwrap(); - let output = WVUF::eval(&dealt_secret, input.as_slice()); - let output_serialized = bcs::to_bytes(&output).unwrap(); - let expected_randomness_seed = Sha3_256::digest(output_serialized.as_slice()).to_vec(); - - ensure!( - expected_randomness_seed == on_chain_block_randomness.seed.clone().unwrap(), - "randomness verification failed with final check failure" - ); - Ok(()) -} - -fn script_to_enable_main_logic() -> String { - r#" -script { - use aptos_framework::aptos_governance; - use aptos_framework::randomness_config; - use aptos_std::fixed_point64; - - fun main(core_resources: &signer) { - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let config = randomness_config::new_v1( - fixed_point64::create_from_rational(1, 2), - fixed_point64::create_from_rational(2, 3) - ); - randomness_config::set_for_next_epoch(&framework_signer, config); - aptos_governance::reconfigure(&framework_signer); - } -} -"# - .to_string() -} - -fn script_to_disable_main_logic() -> String { - r#" -script { - use aptos_framework::aptos_governance; - use aptos_framework::randomness_config; - fun main(core_resources: &signer) { - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let config = randomness_config::new_off(); - randomness_config::set_for_next_epoch(&framework_signer, config); - aptos_governance::reconfigure(&framework_signer); - } -} -"# - .to_string() -} - -fn script_to_update_consensus_config(config: &OnChainConsensusConfig) -> String { - let config_bytes = bcs::to_bytes(config).unwrap(); - format!( - r#" -script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0x1); - let config_bytes = vector{:?}; - consensus_config::set_for_next_epoch(&framework_signer, config_bytes); - aptos_governance::reconfigure(&framework_signer); - }} -}} - "#, - config_bytes - ) -} diff --git a/testsuite/smoke-test/src/randomness/validator_restart_during_dkg.rs b/testsuite/smoke-test/src/randomness/validator_restart_during_dkg.rs deleted file mode 100644 index dd8f5fa29a2e2..0000000000000 --- a/testsuite/smoke-test/src/randomness/validator_restart_during_dkg.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - randomness::{ - decrypt_key_map, get_on_chain_resource, verify_dkg_transcript, wait_for_dkg_finish, - }, - smoke_test_environment::SwarmBuilder, -}; -use aptos_forge::{NodeExt, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_rest_client::Client; -use aptos_types::{dkg::DKGState, on_chain_config::OnChainRandomnessConfig}; -use futures::future::join_all; -use std::{sync::Arc, time::Duration}; - -#[tokio::test] -async fn validator_restart_during_dkg() { - let epoch_duration_secs = 30; - let estimated_dkg_latency_secs = 30; - let time_limit_secs = epoch_duration_secs + estimated_dkg_latency_secs; - let num_validators = 4; - let num_validators_to_restart = 3; - let mut swarm = SwarmBuilder::new_local(num_validators) - .with_num_fullnodes(1) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - })) - .with_init_genesis_config(Arc::new(|conf| { - conf.epoch_duration_secs = 30; - - // Ensure randomness is enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .build() - .await; - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(epoch_duration_secs * 10)) - .await - .unwrap(); - - let decrypt_key_map = decrypt_key_map(&swarm); - - info!("Wait for an epoch start."); - let validator_clients: Vec = - swarm.validators().map(|node| node.rest_client()).collect(); - let dkg_session_1 = wait_for_dkg_finish(&validator_clients[3], None, time_limit_secs).await; - - info!( - "Current epoch is {}.", - dkg_session_1.metadata.dealer_epoch + 1 - ); - - info!("Inject fault to all validators so they get stuck upon the first DKG message received."); - let tasks = validator_clients - .iter() - .take(num_validators_to_restart) - .map(|client| { - client.set_failpoint( - "dkg::process_dkg_start_event".to_string(), - "panic".to_string(), - ) - }) - .collect::>(); - let aptos_results = join_all(tasks).await; - debug!("aptos_results={:?}", aptos_results); - - info!("Restart nodes after they panic."); - for (node_idx, node) in swarm - .validators_mut() - .enumerate() - .take(num_validators_to_restart) - { - while node.health_check().await.is_ok() { - tokio::time::sleep(Duration::from_secs(1)).await; - } - info!("node {} panicked", node_idx); - node.restart().await.unwrap(); - info!("node {} restarted", node_idx); - } - - info!( - "DKG should be able to continue. Wait until we fully entered epoch {}.", - dkg_session_1.target_epoch() + 1 - ); - - swarm - .wait_for_all_nodes_to_catchup_to_epoch( - dkg_session_1.target_epoch() + 1, - Duration::from_secs(time_limit_secs), - ) - .await - .unwrap(); - let dkg_session_2 = get_on_chain_resource::(&validator_clients[3]) - .await - .last_completed - .clone() - .unwrap(); - assert!(verify_dkg_transcript(&dkg_session_2, &decrypt_key_map).is_ok()); -} diff --git a/testsuite/smoke-test/src/rest_api.rs b/testsuite/smoke-test/src/rest_api.rs deleted file mode 100644 index c546068bd4fba..0000000000000 --- a/testsuite/smoke-test/src/rest_api.rs +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::{new_local_swarm_with_aptos, SwarmBuilder}, - txn_emitter::generate_traffic, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_config::config::GasEstimationConfig; -use aptos_crypto::ed25519::Ed25519Signature; -use aptos_forge::{LocalSwarm, NodeExt, Swarm, TransactionType}; -use aptos_global_constants::{DEFAULT_BUCKETS, GAS_UNIT_PRICE}; -use aptos_rest_client::{ - aptos_api_types::{MoveModuleId, TransactionData, ViewFunction, ViewRequest}, - Client, -}; -use aptos_sdk::move_types::language_storage::StructTag; -use aptos_types::{ - account_address::AccountAddress, - account_config::{AccountResource, CORE_CODE_ADDRESS}, - on_chain_config::{ExecutionConfigV2, OnChainExecutionConfig, TransactionShufflerType}, - transaction::{authenticator::AuthenticationKey, SignedTransaction, Transaction}, -}; -use move_core_types::{ - ident_str, - language_storage::{ModuleId, TypeTag}, -}; -use std::{convert::TryFrom, str::FromStr, sync::Arc, time::Duration}; - -#[tokio::test] -async fn test_get_index() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let info = swarm.aptos_public_info(); - - let resp = reqwest::get(info.url().to_owned()).await.unwrap(); - assert_eq!(reqwest::StatusCode::OK, resp.status()); -} - -#[tokio::test] -async fn test_basic_client() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - info.client().get_ledger_information().await.unwrap(); - - // NOTE(Gas): For some reason, there needs to be a lot of funds in the account in order for the - // test to pass. - // Is this caused by us increasing the default max gas amount in - // testsuite/forge/src/interface/aptos.rs? - let account1 = info - .create_and_fund_user_account(10_000_000_000) - .await - .unwrap(); - let account2 = info - .create_and_fund_user_account(10_000_000_000) - .await - .unwrap(); - - let tx = account1.sign_with_transaction_builder( - info.transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer(account2.address(), 1)), - ); - let pending_txn = info.client().submit(&tx).await.unwrap().into_inner(); - - info.client() - .wait_for_transaction(&pending_txn) - .await - .unwrap(); - - info.client() - .get_transaction_by_hash(pending_txn.hash.into()) - .await - .unwrap(); - - info.client() - .get_account_resources(CORE_CODE_ADDRESS) - .await - .unwrap(); - - info.client().get_transactions(None, None).await.unwrap(); -} - -fn next_bucket(gas_unit_price: u64) -> u64 { - *DEFAULT_BUCKETS - .iter() - .find(|bucket| **bucket > gas_unit_price) - .unwrap() -} - -async fn block_height(client: &Client) -> u64 { - client - .get_ledger_information() - .await - .unwrap() - .into_inner() - .block_height -} - -async fn test_gas_estimation_inner(swarm: &mut LocalSwarm) { - let client = swarm.validators().next().unwrap().rest_client(); - let estimation = match client.estimate_gas_price().await { - Ok(res) => res.into_inner(), - Err(e) => panic!("Client error: {:?}", e), - }; - println!("{:?}", estimation); - // Note: in testing GAS_UNIT_PRICE = 0 - assert_eq!(Some(GAS_UNIT_PRICE), estimation.deprioritized_gas_estimate); - assert_eq!(GAS_UNIT_PRICE, estimation.gas_estimate); - assert_eq!( - Some(next_bucket(GAS_UNIT_PRICE)), - estimation.prioritized_gas_estimate - ); - - let txn_gas_price = 100; - let all_validators: Vec<_> = swarm.validators().map(|v| v.peer_id()).collect(); - let txn_stat = generate_traffic( - swarm, - &all_validators, - Duration::from_secs(20), - txn_gas_price, - vec![vec![( - TransactionType::CoinTransfer { - invalid_transaction_ratio: 0, - sender_use_account_pool: false, - }, - 100, - )]], - ) - .await - .unwrap(); - println!("{:?}", txn_stat.rate()); - - let estimation = match client.estimate_gas_price().await { - Ok(res) => res.into_inner(), - Err(e) => panic!("Client error: {:?}", e), - }; - println!("{:?}", estimation); - // Note: it's quite hard to get deprioritized_gas_estimate higher in smoke tests - assert_eq!(next_bucket(txn_gas_price), estimation.gas_estimate); - assert_eq!( - Some(next_bucket(next_bucket(txn_gas_price))), - estimation.prioritized_gas_estimate - ); - - // Wait for enough empty blocks to reset the prices - let num_blocks_to_reset = GasEstimationConfig::default().aggressive_block_history as u64; - let base_height = block_height(&client).await; - loop { - let num_blocks_passed = block_height(&client).await - base_height; - if num_blocks_passed > num_blocks_to_reset { - println!("{} blocks passed, done sleeping", num_blocks_passed); - break; - } - println!("{} blocks passed, sleeping 10 secs...", num_blocks_passed); - // Exercise cache - client.estimate_gas_price().await.unwrap(); - std::thread::sleep(Duration::from_secs(10)); - } - - // Multiple times, to exercise cache - for _i in 0..2 { - let estimation = match client.estimate_gas_price().await { - Ok(res) => res.into_inner(), - Err(e) => panic!("Client error: {:?}", e), - }; - println!("{:?}", estimation); - // Note: in testing GAS_UNIT_PRICE = 0 - assert_eq!(Some(GAS_UNIT_PRICE), estimation.deprioritized_gas_estimate); - assert_eq!(GAS_UNIT_PRICE, estimation.gas_estimate); - assert_eq!( - Some(next_bucket(GAS_UNIT_PRICE)), - estimation.prioritized_gas_estimate - ); - } -} - -#[tokio::test] -async fn test_gas_estimation_txns_limit() { - let mut swarm = SwarmBuilder::new_local(1) - .with_init_config(Arc::new(|_, conf, _| { - let max_block_txns = 3; - conf.api.gas_estimation.enabled = true; - // Use a small full block threshold to make gas estimates update sooner. - conf.api.gas_estimation.full_block_txns = max_block_txns as usize; - // Wait for full blocks with small block size to advance consensus at a fast rate. - conf.consensus.quorum_store_poll_time_ms = 200; - conf.consensus.wait_for_full_blocks_above_pending_blocks = 0; - conf.consensus.max_sending_block_txns = max_block_txns; - conf.consensus.quorum_store.sender_max_batch_txns = conf - .consensus - .quorum_store - .sender_max_batch_txns - .min(max_block_txns as usize); - conf.consensus.quorum_store.receiver_max_batch_txns = conf - .consensus - .quorum_store - .receiver_max_batch_txns - .min(max_block_txns as usize); - })) - .build() - .await; - - test_gas_estimation_inner(&mut swarm).await; -} - -#[tokio::test] -#[ignore] -// This test is ignored because after enabling gas limit, the txn emitter fails. -// TODO (bchocho): Fix this test. -async fn test_gas_estimation_gas_used_limit() { - let mut swarm = SwarmBuilder::new_local(1) - .with_init_genesis_config(Arc::new(|conf| { - conf.execution_config = OnChainExecutionConfig::V2(ExecutionConfigV2 { - transaction_shuffler_type: TransactionShufflerType::NoShuffling, - block_gas_limit: Some(1), - }); - })) - .with_init_config(Arc::new(|_, conf, _| { - let max_block_txns = 3; - conf.api.gas_estimation.enabled = true; - // The full block threshold will never be hit - conf.api.gas_estimation.full_block_txns = (max_block_txns * 2) as usize; - // Wait for full blocks with small block size to advance consensus at a fast rate. - conf.consensus.quorum_store_poll_time_ms = 200; - conf.consensus.wait_for_full_blocks_above_pending_blocks = 0; - conf.consensus.max_sending_block_txns = max_block_txns; - conf.consensus.quorum_store.sender_max_batch_txns = conf - .consensus - .quorum_store - .sender_max_batch_txns - .min(max_block_txns as usize); - conf.consensus.quorum_store.receiver_max_batch_txns = conf - .consensus - .quorum_store - .receiver_max_batch_txns - .min(max_block_txns as usize); - })) - .build() - .await; - - test_gas_estimation_inner(&mut swarm).await; -} - -#[tokio::test] -async fn test_bcs() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - // Create accounts - let mut local_account = info - .create_and_fund_user_account(100_000_000_000) - .await - .unwrap(); - let account = local_account.address(); - let public_key = local_account.public_key(); - let other_local_account = info - .create_and_fund_user_account(100_000_000_000) - .await - .unwrap(); - - let client = info.client(); - // Check get account - let account_resource = client.get_account_bcs(account).await.unwrap().into_inner(); - let expected_auth_key = AuthenticationKey::ed25519(public_key); - let onchain_auth_key = - AuthenticationKey::try_from(account_resource.authentication_key()).unwrap(); - assert_eq!(expected_auth_key, onchain_auth_key); - assert_eq!(0, account_resource.sequence_number()); - - // Check get resources - let resources = client - .get_account_resources_bcs(account) - .await - .unwrap() - .into_inner(); - let bytes = resources - .get(&StructTag::from_str("0x1::account::Account").unwrap()) - .unwrap(); - let account_resource: AccountResource = bcs::from_bytes(bytes).unwrap(); - assert_eq!(0, account_resource.sequence_number()); - - let single_account_resource: AccountResource = client - .get_account_resource_bcs(account, "0x1::account::Account") - .await - .unwrap() - .into_inner(); - assert_eq!(account_resource, single_account_resource); - - // Check Modules align - let modules = client - .get_account_modules(AccountAddress::ONE) - .await - .unwrap() - .into_inner(); - let bcs_modules = client - .get_account_modules_bcs(AccountAddress::ONE) - .await - .unwrap() - .into_inner(); - - assert_eq!(modules.len(), bcs_modules.len()); - let module_bytecode = modules.first().unwrap().clone().try_parse_abi().unwrap(); - let module_abi = module_bytecode.abi.as_ref().unwrap(); - let module_id = MoveModuleId { - address: module_abi.address, - name: module_abi.name.clone(), - }; - assert_eq!( - &module_bytecode.bytecode.0, - bcs_modules.get(&module_id).unwrap() - ); - - let json_module = client - .get_account_module(AccountAddress::ONE, module_id.name.as_str()) - .await - .unwrap() - .into_inner(); - let bcs_module = client - .get_account_module_bcs(AccountAddress::ONE, module_id.name.as_str()) - .await - .unwrap() - .into_inner(); - assert_eq!(json_module.bytecode.0, bcs_module); - assert_eq!(module_bytecode.bytecode.0, bcs_module); - - // Transfer money to make a transaction - let pending_transaction = info - .transfer(&mut local_account, &other_local_account, 500) - .await - .unwrap(); - let expected_txn_hash = pending_transaction.hash.into(); - let expected_txn = client - .wait_for_transaction_bcs(&pending_transaction) - .await - .unwrap() - .into_inner(); - let expected_txn_version = expected_txn.version; - - // Check transactions on an account - let transactions = client - .get_account_transactions(account, Some(0), Some(2)) - .await - .unwrap() - .into_inner(); - let transactions_bcs = client - .get_account_transactions_bcs(account, Some(0), Some(2)) - .await - .unwrap() - .into_inner(); - - // Should only have the transfer up there - assert!(transactions_bcs.contains(&expected_txn)); - assert_eq!(1, transactions_bcs.len()); - assert_eq!(transactions.len(), transactions_bcs.len()); - - for (i, expected_transaction) in transactions.iter().enumerate() { - let bcs_txn = transactions_bcs.get(i).unwrap(); - assert_eq!(bcs_txn.version, expected_transaction.version().unwrap()); - let expected_hash = - aptos_crypto::HashValue::from(expected_transaction.transaction_info().unwrap().hash); - - let bcs_hash = if let Transaction::UserTransaction(ref txn) = bcs_txn.transaction { - txn.clone().committed_hash() - } else { - panic!("BCS transaction is not a user transaction! {:?}", bcs_txn); - }; - assert_eq!(expected_hash, bcs_hash); - } - - // Check that the transaction is able to be looked up by hash and version - let expected_txn_data = TransactionData::OnChain(expected_txn); - - assert_eq!( - expected_txn_data, - client - .get_transaction_by_hash_bcs(expected_txn_hash) - .await - .unwrap() - .into_inner() - ); - assert_eq!( - expected_txn_data, - client - .get_transaction_by_version_bcs(expected_txn_version) - .await - .unwrap() - .into_inner() - ); - - // Check that the first 5 transactions match - let json_txns = client - .get_transactions(Some(0), Some(5)) - .await - .unwrap() - .into_inner(); - let bcs_txns = client - .get_transactions_bcs(Some(0), Some(5)) - .await - .unwrap() - .into_inner(); - assert_eq!(5, json_txns.len()); - assert_eq!(json_txns.len(), bcs_txns.len()); - - // Ensure same hashes and versions for each transaction - for (i, json_txn) in json_txns.iter().enumerate() { - let bcs_txn = bcs_txns.get(i).unwrap(); - - assert_eq!(json_txn.version().unwrap(), bcs_txn.version); - assert_eq!( - aptos_crypto::HashValue::from(json_txn.transaction_info().unwrap().hash), - bcs_txn.info.transaction_hash() - ); - } - - // Test simulation of a transaction should be the same in BCS & JSON - let transfer_txn = info - .transaction_factory() - .transfer(other_local_account.address(), 500) - .sender(local_account.address()) - .sequence_number(local_account.sequence_number()) - .build(); - let signed_txn = SignedTransaction::new( - transfer_txn, - local_account.public_key().clone(), - Ed25519Signature::dummy_signature(), - ); - - let json_txns = client.simulate(&signed_txn).await.unwrap().into_inner(); - let json_txn = json_txns.first().unwrap(); - - let bcs_txn = client.simulate_bcs(&signed_txn).await.unwrap().into_inner(); - assert_eq!( - aptos_crypto::HashValue::from(json_txn.info.hash), - bcs_txn.info.transaction_hash() - ); - - // Actually submit the transaction, and ensure it submits and succeeds - // TODO: check failure case? - let transfer_txn = local_account.sign_with_transaction_builder( - info.transaction_factory() - .transfer(other_local_account.address(), 500), - ); - - let txn = client - .submit_and_wait_bcs(&transfer_txn) - .await - .unwrap() - .into_inner(); - let txn_version = txn.version; - assert_eq!( - txn.transaction.try_as_signed_user_txn().unwrap(), - &transfer_txn - ); - - // Check blocks - let json_block = client - .get_block_by_version(txn_version, true) - .await - .unwrap() - .into_inner(); - let bcs_block = client - .get_block_by_version_bcs(txn_version, true) - .await - .unwrap() - .into_inner(); - - assert_eq!(json_block.block_height.0, bcs_block.block_height); - assert_eq!( - aptos_crypto::HashValue::from(json_block.block_hash), - bcs_block.block_hash - ); - - let json_txns = json_block.transactions.unwrap(); - let first_json_txn = json_txns.first().unwrap(); - let bcs_txns = bcs_block.transactions.unwrap(); - let first_bcs_txn = bcs_txns.first().unwrap(); - assert_eq!(first_json_txn.version().unwrap(), first_bcs_txn.version); - assert_eq!( - aptos_crypto::HashValue::from(first_json_txn.transaction_info().unwrap().hash), - first_bcs_txn.info.transaction_hash() - ); - - let json_block_by_height = client - .get_block_by_height(bcs_block.block_height, true) - .await - .unwrap() - .into_inner(); - let bcs_block_by_height = client - .get_block_by_height_bcs(bcs_block.block_height, true) - .await - .unwrap() - .into_inner(); - assert_eq!( - json_block_by_height.block_height.0, - bcs_block_by_height.block_height - ); - assert_eq!(bcs_block.block_height, bcs_block_by_height.block_height); - assert_eq!(bcs_block.block_hash, bcs_block_by_height.block_hash); - assert_eq!( - aptos_crypto::HashValue::from(json_block_by_height.block_hash), - bcs_block_by_height.block_hash - ); - - let json_events = client - .get_account_events( - AccountAddress::ONE, - "0x1::block::BlockResource", - "new_block_events", - Some(0), - Some(1), - ) - .await - .unwrap() - .into_inner(); - let bcs_events = client - .get_account_events_bcs( - AccountAddress::ONE, - "0x1::block::BlockResource", - "new_block_events", - Some(0), - Some(1), - ) - .await - .unwrap() - .into_inner(); - assert_eq!( - json_events.first().unwrap().version.0, - bcs_events.first().unwrap().transaction_version - ); - - // Test that more than 25 transactions can be retrieved - let json_txns = client - .get_transactions(Some(0), Some(30)) - .await - .unwrap() - .into_inner(); - let bcs_txns = client - .get_transactions_bcs(Some(0), Some(30)) - .await - .unwrap() - .into_inner(); - assert_eq!(json_txns.len(), 30); - assert_eq!(json_txns.len(), bcs_txns.len()); -} - -#[tokio::test] -async fn test_view_function() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let info = swarm.aptos_public_info(); - let client: &Client = info.client(); - - let address = AccountAddress::ONE; - - // Non-BCS - let view_request = ViewRequest { - function: "0x1::coin::is_account_registered".parse().unwrap(), - type_arguments: vec!["0x1::aptos_coin::AptosCoin".parse().unwrap()], - arguments: vec![serde_json::Value::String(address.to_hex_literal())], - }; - - // Balance should be 0 and there should only be one return value - let json_ret_values = client.view(&view_request, None).await.unwrap().into_inner(); - assert_eq!(json_ret_values.len(), 1); - assert!(!json_ret_values[0].as_bool().unwrap()); - - // BCS - let bcs_view_request = ViewFunction { - module: ModuleId::new(address, ident_str!("coin").into()), - function: ident_str!("is_account_registered").into(), - ty_args: vec![TypeTag::Struct(Box::new( - StructTag::from_str("0x1::aptos_coin::AptosCoin").unwrap(), - ))], - args: vec![bcs::to_bytes(&address).unwrap()], - }; - - // Balance should be 0 and there should only be one return value - let bcs_ret_values: Vec = client - .view_bcs(&bcs_view_request, None) - .await - .unwrap() - .into_inner(); - assert_eq!(bcs_ret_values.len(), 1); - assert!(!bcs_ret_values[0]); -} diff --git a/testsuite/smoke-test/src/rosetta.rs b/testsuite/smoke-test/src/rosetta.rs deleted file mode 100644 index 76f5bbfd6aa87..0000000000000 --- a/testsuite/smoke-test/src/rosetta.rs +++ /dev/null @@ -1,2703 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::SwarmBuilder; -use anyhow::anyhow; -use aptos::{ - account::create::DEFAULT_FUNDED_COINS, - common::types::GasOptions, - test::{CliTestFramework, INVALID_ACCOUNT}, -}; -use aptos_cached_packages::aptos_stdlib; -use aptos_config::{config::ApiConfig, utils::get_available_port}; -use aptos_crypto::{ - ed25519::{Ed25519PrivateKey, Ed25519Signature}, - HashValue, PrivateKey, -}; -use aptos_forge::{AptosPublicInfo, LocalSwarm, Node, NodeExt, Swarm}; -use aptos_gas_schedule::{AptosGasParameters, FromOnChainGasSchedule}; -use aptos_genesis::builder::InitConfigFn; -use aptos_global_constants::GAS_UNIT_PRICE; -use aptos_rest_client::{ - aptos_api_types::{TransactionOnChainData, UserTransaction}, - Response, Transaction, -}; -use aptos_rosetta::{ - client::RosettaClient, - common::{native_coin, BlockHash, BLOCKCHAIN, Y2K_MS}, - types::{ - AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier, BlockIdentifier, - BlockRequest, BlockResponse, NetworkIdentifier, NetworkRequest, Operation, - OperationStatusType, OperationType, PartialBlockIdentifier, TransactionIdentifier, - TransactionType, STAKING_CONTRACT_MODULE, SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION, - }, - ROSETTA_VERSION, -}; -use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; -use aptos_types::{ - account_address::AccountAddress, account_config::CORE_CODE_ADDRESS, chain_id::ChainId, - on_chain_config::GasScheduleV2, transaction::SignedTransaction, -}; -use serde_json::json; -use std::{ - collections::{BTreeMap, HashSet}, - future::Future, - str::FromStr, - sync::Arc, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; -use tokio::{task::JoinHandle, time::Instant}; - -const EPOCH_DURATION_S: u64 = 5; -const DEFAULT_TRANSFER_AMOUNT: u64 = 20; -const DEFAULT_MAX_WAIT_S: u64 = 5; -const DEFAULT_INTERVAL_MS: u64 = 100; -static DEFAULT_MAX_WAIT_DURATION: Duration = Duration::from_secs(DEFAULT_MAX_WAIT_S); -static DEFAULT_INTERVAL_DURATION: Duration = Duration::from_millis(DEFAULT_INTERVAL_MS); - -async fn setup_simple_test( - num_accounts: usize, -) -> ( - LocalSwarm, - CliTestFramework, - JoinHandle>, - RosettaClient, -) { - setup_test(num_accounts, Arc::new(|_, _, _| {})).await -} - -async fn setup_test( - num_accounts: usize, - config_fn: InitConfigFn, -) -> ( - LocalSwarm, - CliTestFramework, - JoinHandle>, - RosettaClient, -) { - // Start the validator - let (swarm, cli, faucet) = SwarmBuilder::new_local(1) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.epoch_duration_secs = EPOCH_DURATION_S; - })) - .with_init_config(config_fn) - .with_aptos() - .build_with_cli(num_accounts) - .await; - let validator = swarm.validators().next().unwrap(); - - // And the rosetta server - let rosetta_port = get_available_port(); - let rosetta_socket_addr = format!("127.0.0.1:{}", rosetta_port); - let rosetta_url = format!("http://{}", rosetta_socket_addr.clone()) - .parse() - .unwrap(); - let rosetta_client = RosettaClient::new(rosetta_url); - let api_config = ApiConfig { - enabled: true, - address: rosetta_socket_addr.parse().unwrap(), - tls_cert_path: None, - tls_key_path: None, - content_length_limit: None, - ..Default::default() - }; - - // Start the server - let _rosetta = aptos_rosetta::bootstrap_async( - swarm.chain_id(), - api_config, - Some(aptos_rest_client::Client::new( - validator.rest_api_endpoint(), - )), - cli.addresses(), - ) - .await - .unwrap(); - - // Ensure rosetta can take requests - try_until_ok_default(|| rosetta_client.network_list()) - .await - .unwrap(); - - (swarm, cli, faucet, rosetta_client) -} - -#[tokio::test] -async fn test_block_transactions() { - const NUM_TXNS_PER_PAGE: u16 = 2; - - let (swarm, cli, _faucet, rosetta_client) = setup_test( - 2, - Arc::new(|_, config, _| config.api.max_transactions_page_size = NUM_TXNS_PER_PAGE), - ) - .await; - - let account_1 = cli.account_id(0); - let chain_id = swarm.chain_id(); - - // At time 0, there should be 0 balance - let response = get_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_1), - Some(0), - ) - .await - .unwrap(); - assert_eq!(response.block_identifier, BlockIdentifier { - index: 0, - hash: BlockHash::new(chain_id, 0).to_string(), - }); - - // First fund account 1 with lots more gas - cli.fund_account(0, Some(DEFAULT_FUNDED_COINS * 10)) - .await - .unwrap(); - let response = cli.transfer_coins(0, 1, 100, None).await.unwrap(); - - let validator = swarm.validators().next().unwrap(); - let rest_client = validator.rest_client(); - let height = rest_client - .get_block_by_version_bcs(response.version, false) - .await - .unwrap() - .into_inner() - .block_height; - - let response = rosetta_client - .block(&BlockRequest::by_index(swarm.chain_id(), height)) - .await - .unwrap(); - - // There is only one user transaction, so the other one should be dropped - assert_eq!(1, response.block.transactions.len()); -} - -#[tokio::test] -async fn test_network() { - let (swarm, _, _, rosetta_client) = setup_simple_test(1).await; - let chain_id = swarm.chain_id(); - - // We only support one network, this network - let networks = try_until_ok_default(|| rosetta_client.network_list()) - .await - .unwrap(); - assert_eq!(1, networks.network_identifiers.len()); - let network_id = networks.network_identifiers.first().unwrap(); - assert_eq!(BLOCKCHAIN, network_id.blockchain); - assert_eq!(chain_id.to_string(), network_id.network); - - let request = NetworkRequest { - network_identifier: NetworkIdentifier::from(chain_id), - }; - let options = rosetta_client.network_options(&request).await.unwrap(); - assert_eq!(ROSETTA_VERSION, options.version.rosetta_version); - - // TODO: Check other options - - let request = NetworkRequest { - network_identifier: NetworkIdentifier::from(chain_id), - }; - let status = try_until_ok_default(|| rosetta_client.network_status(&request)) - .await - .unwrap(); - assert!(status.current_block_timestamp >= Y2K_MS); - assert_eq!( - BlockIdentifier { - index: 0, - hash: BlockHash::new(chain_id, 0).to_string(), - }, - status.genesis_block_identifier - ); - assert_eq!( - status.genesis_block_identifier, - status.oldest_block_identifier, - ); - - // Wrong blockchain should fail - let request = NetworkRequest { - network_identifier: NetworkIdentifier { - blockchain: "eth".to_string(), - network: chain_id.to_string(), - }, - }; - rosetta_client - .network_status(&request) - .await - .expect_err("Should not work with wrong blockchain name"); - - // Wrong network should fail - let request = NetworkRequest { - network_identifier: NetworkIdentifier::from(ChainId::new(22)), - }; - rosetta_client - .network_status(&request) - .await - .expect_err("Should not work with wrong network chain id"); -} - -#[tokio::test] -async fn test_account_balance() { - let (mut swarm, cli, _faucet, rosetta_client) = setup_simple_test(3).await; - - let account_1 = cli.account_id(0); - let account_2 = cli.account_id(1); - let account_3 = cli.account_id(2); - let chain_id = swarm.chain_id(); - swarm - .aptos_public_info() - .sync_root_account_sequence_number() - .await; - - let mut account_4 = swarm - .aptos_public_info() - .create_and_fund_user_account(10_000_000_000) - .await - .unwrap(); - - // At time 0, there should be no balance - let response = get_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_1), - Some(0), - ) - .await - .unwrap(); - assert_eq!(response.block_identifier, BlockIdentifier { - index: 0, - hash: BlockHash::new(chain_id, 0).to_string(), - }); - - // First fund account 1 with lots more gas - cli.fund_account(0, Some(DEFAULT_FUNDED_COINS * 2)) - .await - .unwrap(); - - let mut account_1_balance = DEFAULT_FUNDED_COINS * 3; - let mut account_2_balance = DEFAULT_FUNDED_COINS; - // At some time both accounts should exist with initial amounts - try_until_ok(DEFAULT_MAX_WAIT_DURATION, DEFAULT_INTERVAL_DURATION, || { - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_1), - account_1_balance, - 0, - ) - }) - .await - .unwrap(); - try_until_ok_default(|| { - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_2), - account_2_balance, - 0, - ) - }) - .await - .unwrap(); - - // Send money, and expect the gas and fees to show up accordingly - const TRANSFER_AMOUNT: u64 = 5000; - let response = cli - .transfer_coins(0, 1, TRANSFER_AMOUNT, None) - .await - .unwrap(); - account_1_balance -= TRANSFER_AMOUNT + response.gas_used * response.gas_unit_price; - account_2_balance += TRANSFER_AMOUNT; - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_1), - account_1_balance, - 1, - ) - .await - .unwrap(); - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_2), - account_2_balance, - 0, - ) - .await - .unwrap(); - - // Failed transaction spends gas - let _ = cli - .transfer_invalid_addr( - 0, - TRANSFER_AMOUNT, - Some(GasOptions { - gas_unit_price: None, - max_gas: Some(1000), - expiration_secs: 30, - }), - ) - .await - .unwrap_err(); - - // Make a bad transaction, which will cause gas to be spent but no transfer - let validator = swarm.validators().next().unwrap(); - let rest_client = validator.rest_client(); - let txns = rest_client - .get_account_transactions(account_1, None, None) - .await - .unwrap() - .into_inner(); - let failed_txn = txns.last().unwrap(); - if let Transaction::UserTransaction(txn) = failed_txn { - account_1_balance -= txn.request.gas_unit_price.0 * txn.info.gas_used.0; - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(account_1), - account_1_balance, - 2, - ) - .await - .unwrap(); - } - - // Check that the balance hasn't changed (and should be 0) in the invalid account - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::base_account(AccountAddress::from_hex_literal(INVALID_ACCOUNT).unwrap()), - 0, - 0, - ) - .await - .unwrap(); - - // Let's now check the staking balance with the original staking contract, it should not be supported - cli.fund_account(2, Some(10_000_000)).await.unwrap(); - cli.initialize_stake_owner(2, 1_000_000, None, None) - .await - .unwrap(); - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::total_stake_account(account_3), - 1_000_000, - 1, - ) - .await - .expect_err("Original staking contract is not supported"); - - create_staking_contract( - &swarm.aptos_public_info(), - &mut account_4, - account_1, - account_2, - 1_000_000, - 10, - 1, - ) - .await; - - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::total_stake_account(account_4.address()), - 1_000_000, - 1, - ) - .await - .unwrap(); - - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::active_stake_account(account_4.address()), - 1_000_000, - 1, - ) - .await - .unwrap(); - - unlock_stake( - &swarm.aptos_public_info(), - &mut account_4, - account_1, - 1_000, - 2, - ) - .await; - - // Since unlock_stake was initiated, 1000 APT should be in pending inactive state until lockup ends - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::pending_inactive_stake_account(account_4.address()), - 1_000, - 2, - ) - .await - .unwrap(); - - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::inactive_stake_account(account_4.address()), - 0, - 2, - ) - .await - .unwrap(); - - /* TODO: Support operator stake account in the future - account_has_balance( - &rosetta_client, - chain_id, - AccountIdentifier::operator_stake_account(account_4.address(), account_1), - 1_000_000, - 1, - ) - .await - .unwrap();*/ -} - -async fn create_staking_contract( - info: &AptosPublicInfo<'_>, - account: &mut LocalAccount, - operator: AccountAddress, - voter: AccountAddress, - amount: u64, - commission_percentage: u64, - sequence_number: u64, -) -> Response { - let staking_contract_creation = info - .transaction_factory() - .payload(aptos_stdlib::staking_contract_create_staking_contract( - operator, - voter, - amount, - commission_percentage, - vec![], - )) - .sequence_number(sequence_number); - - let txn = account.sign_with_transaction_builder(staking_contract_creation); - info.client().submit_and_wait(&txn).await.unwrap() -} - -async fn unlock_stake( - info: &AptosPublicInfo<'_>, - account: &mut LocalAccount, - operator: AccountAddress, - amount: u64, - sequence_number: u64, -) -> Response { - let unlock_stake = info - .transaction_factory() - .payload(aptos_stdlib::staking_contract_unlock_stake( - operator, amount, - )) - .sequence_number(sequence_number); - - let txn = account.sign_with_transaction_builder(unlock_stake); - info.client().submit_and_wait(&txn).await.unwrap() -} - -async fn create_delegation_pool( - info: &AptosPublicInfo<'_>, - account: &mut LocalAccount, - commission_percentage: u64, - sequence_number: u64, -) -> Response { - let delegation_pool_creation = info - .transaction_factory() - .payload(aptos_stdlib::delegation_pool_initialize_delegation_pool( - commission_percentage, - vec![], - )) - .sequence_number(sequence_number); - - let txn = account.sign_with_transaction_builder(delegation_pool_creation); - info.client().submit_and_wait(&txn).await.unwrap() -} - -async fn account_has_balance( - rosetta_client: &RosettaClient, - chain_id: ChainId, - account_identifier: AccountIdentifier, - expected_balance: u64, - expected_sequence_number: u64, -) -> anyhow::Result { - let response = get_balance(rosetta_client, chain_id, account_identifier.clone(), None).await?; - assert_eq!( - expected_sequence_number, - response.metadata.sequence_number.0 - ); - - if response.balances.iter().any(|amount| { - amount.currency == native_coin() && amount.value == expected_balance.to_string() - }) { - Ok(response.block_identifier.index) - } else { - Err(anyhow!( - "Failed to find account {:?} with {} {:?}, received {:?}", - account_identifier, - expected_balance, - native_coin(), - response - )) - } -} - -async fn get_balance( - rosetta_client: &RosettaClient, - chain_id: ChainId, - account_identifier: AccountIdentifier, - index: Option, -) -> anyhow::Result { - let request = AccountBalanceRequest { - network_identifier: chain_id.into(), - account_identifier, - block_identifier: Some(PartialBlockIdentifier { index, hash: None }), - currencies: Some(vec![native_coin()]), - }; - try_until_ok_default(|| rosetta_client.account_balance(&request)).await -} - -async fn wait_for_rosetta_block(node_clients: &NodeClients<'_>, block_height: u64) { - // Wait until the Rosetta service is ready - let request = NetworkRequest { - network_identifier: node_clients.network.clone(), - }; - - loop { - let status = try_until_ok_default(|| node_clients.rosetta_client.network_status(&request)) - .await - .unwrap(); - if status.current_block_identifier.index >= block_height { - break; - } - } -} - -#[tokio::test] -async fn test_transfer() { - let (mut swarm, cli, _faucet, rosetta_client) = setup_simple_test(1).await; - let chain_id = swarm.chain_id(); - let client = swarm.aptos_public_info().client().clone(); - let sender = cli.account_id(0); - let receiver = AccountAddress::from_hex_literal("0xBEEF").unwrap(); - let sender_private_key = cli.private_key(0); - let sender_balance = client - .get_account_balance(sender) - .await - .unwrap() - .into_inner() - .coin - .value - .0; - let network = NetworkIdentifier::from(chain_id); - let node_clients = NodeClients { - rosetta_client: &rosetta_client, - rest_client: &client, - network: &network, - }; - wait_for_rosetta_block(&node_clients, 2).await; - - // Attempt to transfer more than balance to another user (should fail) - simple_transfer_and_wait( - &node_clients, - sender_private_key, - receiver, - sender_balance + 200, - ) - .await - .expect_err("Should fail simulation since we can't transfer more than balance coins"); - - // Attempt to transfer more than balance to another user (should fail) - let transaction_factory = TransactionFactory::new(chain_id) - // We purposely don't set gas unit price here so the builder uses the default. - // Note that the default is different in tests. See here: - // config/global-constants/src/lib.rs - .with_gas_unit_price(GAS_UNIT_PRICE) - .with_max_gas_amount(1000); - let txn_payload = aptos_stdlib::aptos_account_transfer(receiver, 100); - let unsigned_transaction = transaction_factory - .payload(txn_payload) - .sender(sender) - .sequence_number(0) - .build(); - let signed_transaction = SignedTransaction::new( - unsigned_transaction, - sender_private_key.public_key(), - Ed25519Signature::dummy_signature(), - ); - - let simulation_txn = client - .simulate_bcs(&signed_transaction) - .await - .expect("Should succeed getting gas estimate") - .into_inner(); - let gas_usage = simulation_txn.info.gas_used() * GAS_UNIT_PRICE; - let max_sent = sender_balance - gas_usage; - - // Attempt to transfer more than balance - gas to another user (should fail) - simple_transfer_and_wait(&node_clients, sender_private_key, receiver, max_sent + 1) - .await - .expect_err("Should fail simulation since we can't transfer more than balance + gas coins"); - - // Attempt to transfer more than balance - gas to another user (should fail) - let node_clients = NodeClients { - rosetta_client: &rosetta_client, - rest_client: &client, - network: &network, - }; - simple_transfer_and_wait(&node_clients, sender_private_key, receiver, max_sent) - .await - .expect("Should succeed transfer"); - - // Sender balance should be 0 - assert_eq!( - client - .get_account_balance(sender) - .await - .unwrap() - .into_inner() - .coin - .value - .0, - 0 - ); - // Receiver should be sent coins - assert_eq!( - client - .get_account_balance(receiver) - .await - .unwrap() - .into_inner() - .coin - .value - .0, - max_sent - ); -} - -/// This test tests all of Rosetta's functionality from the read side in one go. Since -/// it's block based and it needs time to run, we do all the checks in a single test. -#[tokio::test] -async fn test_block() { - let (swarm, cli, _faucet, rosetta_client) = setup_simple_test(5).await; - let chain_id = swarm.chain_id(); - let validator = swarm.validators().next().unwrap(); - let rest_client = validator.rest_client(); - - // Mapping of account to block and balance mappings - let mut balances = BTreeMap::>::new(); - - // Wait until the Rosetta service is ready - let network = NetworkIdentifier::from(chain_id); - let node_clients = NodeClients { - rosetta_client: &rosetta_client, - rest_client: &rest_client, - network: &network, - }; - wait_for_rosetta_block(&node_clients, 2).await; - - // Do some transfers - let account_id_0 = cli.account_id(0); - let account_id_1 = cli.account_id(1); - let account_id_2 = cli.account_id(2); - let account_id_3 = cli.account_id(3); - - // TODO(greg): revisit after fixing gas estimation - cli.fund_account(0, Some(100000000)).await.unwrap(); - cli.fund_account(1, Some(6500000)).await.unwrap(); - cli.fund_account(2, Some(500000)).await.unwrap(); - cli.fund_account(3, Some(200000)).await.unwrap(); - - // Get minimum gas price - let gas_schedule: GasScheduleV2 = rest_client - .get_account_resource_bcs(CORE_CODE_ADDRESS, "0x1::gas_schedule::GasScheduleV2") - .await - .unwrap() - .into_inner(); - let feature_version = gas_schedule.feature_version; - let gas_params = AptosGasParameters::from_on_chain_gas_schedule( - &gas_schedule.to_btree_map(), - feature_version, - ) - .unwrap(); - let min_gas_price = u64::from(gas_params.vm.txn.min_price_per_gas_unit); - - let private_key_0 = cli.private_key(0); - let private_key_1 = cli.private_key(1); - let private_key_2 = cli.private_key(2); - let private_key_3 = cli.private_key(3); - - let seq_no_0 = simple_transfer_and_wait( - &node_clients, - private_key_0, - account_id_1, - DEFAULT_TRANSFER_AMOUNT, - ) - .await - .unwrap() - .request - .sequence_number - .0; - simple_transfer_and_wait( - &node_clients, - private_key_1, - account_id_0, - DEFAULT_TRANSFER_AMOUNT, - ) - .await - .unwrap(); - transfer_and_wait( - &node_clients, - private_key_0, - account_id_0, - DEFAULT_TRANSFER_AMOUNT, - None, - Some(seq_no_0 + 1), - None, - None, - ) - .await - .unwrap(); - // Create a new account via transfer - simple_transfer_and_wait( - &node_clients, - private_key_2, - AccountAddress::from_hex_literal(INVALID_ACCOUNT).unwrap(), - DEFAULT_TRANSFER_AMOUNT, - ) - .await - .unwrap(); - let seq_no_3 = transfer_and_wait( - &node_clients, - private_key_3, - account_id_0, - DEFAULT_TRANSFER_AMOUNT, - None, - None, - Some(2000000), - Some(min_gas_price), - ) - .await - .unwrap() - .request - .sequence_number - .0; - - // Create another account via command - create_account_and_wait( - &node_clients, - private_key_3, - AccountAddress::from_hex_literal("0x99").unwrap(), - None, - Some(seq_no_3 + 1), - None, - None, - ) - .await - .unwrap(); - - transfer_and_wait( - &node_clients, - private_key_1, - account_id_3, - DEFAULT_TRANSFER_AMOUNT, - None, - // Test the default behavior - None, - None, - Some(min_gas_price + 1), - ) - .await - .unwrap(); - - // This one will fail because expiration is in the past - transfer_and_wait( - &node_clients, - private_key_3, - AccountAddress::ONE, - DEFAULT_TRANSFER_AMOUNT, - Some(Duration::from_secs(0)), - None, - None, - None, - ) - .await - .unwrap_err(); - - // This one will fail because gas is too low - transfer_and_wait( - &node_clients, - private_key_3, - AccountAddress::ONE, - DEFAULT_TRANSFER_AMOUNT, - None, - None, - Some(1), - None, - ) - .await - .unwrap_err(); - - // Add a ton of coins, and set an operator - cli.fund_account(3, Some(10_000_000)).await.unwrap(); - cli.create_stake_pool(3, 3, 1, 1_000_000, 0).await.unwrap(); - - // Set the operator - set_operator_and_wait( - &node_clients, - private_key_3, - Some(account_id_3), - account_id_1, - ) - .await - .expect("Set operator should work!"); - - // Also fail to set an operator (since the operator already changed) - set_operator_and_wait( - &node_clients, - private_key_3, - Some(account_id_3), - account_id_1, - ) - .await - .unwrap_err(); - - // Test native stake pool and reset lockup support - const MIL_APT: u64 = 100000000000000; - cli.fund_account(2, Some(10 * MIL_APT)).await.unwrap(); - create_stake_pool_and_wait( - &node_clients, - private_key_2, - Some(account_id_3), - Some(account_id_2), - Some(MIL_APT), - Some(5), - ) - .await - .expect("Should successfully create stake pool"); - - // TODO: Verify lockup time changes - - // Reset lockup - reset_lockup_and_wait(&node_clients, private_key_2, Some(account_id_3)) - .await - .expect("Should successfully reset lockup"); - - // Update commission - update_commission_and_wait(&node_clients, private_key_2, Some(account_id_3), Some(50)) - .await - .expect("Should successfully update commission"); - - // Successfully, and fail setting a voter - set_voter_and_wait( - &node_clients, - private_key_3, - Some(account_id_3), - account_id_1, - ) - .await - .expect_err("Set voter shouldn't work with the wrong operator!"); - set_voter_and_wait( - &node_clients, - private_key_3, - Some(account_id_1), - account_id_1, - ) - .await - .expect("Set voter should work!"); - - // Unlock stake - unlock_stake_and_wait(&node_clients, private_key_2, Some(account_id_3), Some(10)) - .await - .expect("Should successfully unlock stake"); - - // Failed distribution with wrong staker - distribute_staking_rewards_and_wait(&node_clients, private_key_3, account_id_2, account_id_3) - .await - .expect_err("Staker has no staking contracts."); - - let final_txn = distribute_staking_rewards_and_wait( - &node_clients, - private_key_3, - account_id_3, - account_id_2, - ) - .await - .expect("Distribute staking rewards should work!"); - - let final_block_to_check = rest_client - .get_block_by_version(final_txn.info.version.0, false) - .await - .expect("Should be able to get block info for completed txns"); - - // Check a couple blocks past the final transaction to check more txns - let final_block_height = final_block_to_check.into_inner().block_height.0 + 2; - - // TODO: Track total supply? - // TODO: Check account balance block hashes? - // TODO: Handle multiple coin types - - // Wait until the Rosetta service is ready - wait_for_rosetta_block(&node_clients, final_block_height).await; - - // Now we have to watch all the changes - let mut current_version = 0; - let mut previous_block_index = 0; - let mut block_hashes = HashSet::new(); - for block_height in 0..final_block_height { - let request = BlockRequest::by_index(chain_id, block_height); - let response: BlockResponse = rosetta_client - .block(&request) - .await - .expect("Should be able to get blocks that are already known"); - let block = response.block; - let actual_block = rest_client - .get_block_by_height_bcs(block_height, true) - .await - .expect("Should be able to get block for a known block") - .into_inner(); - - assert_eq!( - block.block_identifier.index, block_height, - "The block should match the requested block" - ); - assert_eq!( - block.block_identifier.hash, - BlockHash::new(chain_id, block_height).to_string(), - "Block hash should match chain_id-block_height" - ); - assert_eq!( - block.parent_block_identifier.index, previous_block_index, - "Parent block index should be previous block" - ); - assert_eq!( - block.parent_block_identifier.hash, - BlockHash::new(chain_id, previous_block_index).to_string(), - "Parent block hash should be previous block chain_id-block_height" - ); - assert!( - block_hashes.insert(block.block_identifier.hash.clone()), - "Block hash was repeated {}", - block.block_identifier.hash - ); - - // It's only greater or equal because microseconds are cut off - let expected_timestamp = if block_height == 0 { - Y2K_MS - } else { - actual_block.block_timestamp.saturating_div(1000) - }; - assert_eq!( - expected_timestamp, block.timestamp, - "Block timestamp should match actual timestamp but in ms" - ); - - // TODO: double check that all transactions do show with the flag, and that all expected txns - // are shown without the flag - - let actual_txns = actual_block - .transactions - .as_ref() - .expect("Every actual block should have transactions"); - parse_block_transactions(&block, &mut balances, actual_txns, &mut current_version).await; - - // Keep track of the previous - previous_block_index = block_height; - } - - // Reconcile and ensure all balances are calculated correctly - check_balances(&rosetta_client, chain_id, balances).await; -} - -/// Parse the transactions in each block -async fn parse_block_transactions( - block: &aptos_rosetta::types::Block, - balances: &mut BTreeMap>, - actual_txns: &[TransactionOnChainData], - current_version: &mut u64, -) { - let mut txn_hashes = HashSet::new(); - for transaction in block.transactions.iter() { - let txn_metadata = &transaction.metadata; - let txn_version = txn_metadata.version.0; - let cur_version = *current_version; - assert!( - txn_version >= cur_version, - "Transaction version {} must be greater than previous {}", - txn_version, - cur_version - ); - - let actual_txn = actual_txns - .iter() - .find(|txn| txn.version == txn_version) - .expect("There should be the transaction in the actual block"); - let actual_txn_info = &actual_txn.info; - - // Ensure transaction identifier is correct - let txn_hash = transaction.transaction_identifier.hash.clone(); - assert_eq!( - format!("{:x}", actual_txn_info.transaction_hash()), - txn_hash, - "Transaction hash should match the actual hash" - ); - - assert!( - txn_hashes.insert(txn_hash.clone()), - "Transaction hash was repeated {}", - txn_hash - ); - - // Ensure the status is correct - assert_eq!(txn_metadata.failed, !actual_txn_info.status().is_success()); - assert_eq!( - txn_metadata.vm_status, - format!("{:?}", actual_txn_info.status()) - ); - - // Type specific checks - match txn_metadata.transaction_type { - TransactionType::Genesis => { - // For this test, there should only be one genesis - assert_eq!(0, cur_version); - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::GenesisTransaction(_) - )); - }, - TransactionType::User => { - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::UserTransaction(_) - )); - // Must have a gas fee - assert!(!transaction.operations.is_empty()); - }, - TransactionType::BlockMetadata => { - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::BlockMetadata(_) - )); - assert!(transaction.operations.is_empty()); - }, - TransactionType::BlockMetadataExt => { - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::BlockMetadataExt(_) - )); - assert!(transaction.operations.is_empty()); - }, - TransactionType::StateCheckpoint => { - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::StateCheckpoint(_) - )); - assert!(transaction.operations.is_empty()); - }, - TransactionType::Validator => { - assert!(matches!( - actual_txn.transaction, - aptos_types::transaction::Transaction::ValidatorTransaction(_) - )); - assert!(transaction.operations.is_empty()); - }, - } - - parse_operations( - block.block_identifier.index, - balances, - transaction, - actual_txn, - ) - .await; - - for (_, account_balance) in balances.iter() { - if let Some(amount) = account_balance.get(&cur_version) { - assert!(*amount >= 0, "Amount shouldn't be negative!") - } - } - - // Increment to next version - *current_version = txn_version + 1; - } -} - -/// Parse the individual operations in a transaction -async fn parse_operations( - block_height: u64, - balances: &mut BTreeMap>, - transaction: &aptos_rosetta::types::Transaction, - actual_txn: &TransactionOnChainData, -) { - // If there are no operations, then there is no gas operation - let mut has_gas_op = false; - for (expected_index, operation) in transaction.operations.iter().enumerate() { - assert_eq!(expected_index as u64, operation.operation_identifier.index); - - // Gas transaction is always last - let status = operation.status().expect("Should have an operation status"); - let operation_type = operation - .operation_type() - .expect("Operation type should be known"); - let actual_successful = actual_txn.info.status().is_success(); - - // Iterate through every operation, keeping track of balances - match operation_type { - OperationType::CreateAccount => { - // Initialize state for a new account - let account = operation - .account() - .expect("Account address should be parsable"); - - if actual_successful { - assert_eq!(OperationStatusType::Success, status); - let account_balances = balances.entry(account).or_default(); - - if account_balances.is_empty() { - account_balances.insert(block_height, 0i128); - } else { - panic!("Account already has a balance when being created!"); - } - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed create account operation" - ); - } - }, - OperationType::Deposit => { - let account = operation - .account() - .expect("Account address should be parsable"); - - if actual_successful { - assert_eq!(OperationStatusType::Success, status); - let account_balances = balances.entry(account).or_insert_with(|| { - let mut map = BTreeMap::new(); - map.insert(block_height, 0); - map - }); - let (_, latest_balance) = account_balances.iter().last().unwrap(); - assert_eq!( - operation.currency().unwrap(), - &native_coin(), - "Balance should be the native coin" - ); - let delta = operation.amount().unwrap(); - - // Add with panic on overflow in case of too high of a balance - let new_balance = *latest_balance + delta; - account_balances.insert(block_height, new_balance); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed deposit operation" - ); - } - }, - OperationType::Withdraw => { - // Gas is always successful - if actual_successful { - assert_eq!(OperationStatusType::Success, status); - let account = operation - .account() - .expect("Account address should be parsable"); - - let account_balances = balances.entry(account).or_insert_with(|| { - let mut map = BTreeMap::new(); - map.insert(block_height, 0); - map - }); - let (_, latest_balance) = account_balances.iter().last().unwrap(); - assert_eq!( - operation.currency().unwrap(), - &native_coin(), - "Balance should be the native coin" - ); - let delta = operation.amount().unwrap(); - let new_balance = *latest_balance + delta; - account_balances.insert(block_height, new_balance); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed withdraw operation" - ); - } - }, - OperationType::StakingReward => { - let account = operation - .account() - .expect("Account address should be parsable"); - - if actual_successful { - assert_eq!(OperationStatusType::Success, status); - let account_balances = balances.entry(account).or_insert_with(|| { - let mut map = BTreeMap::new(); - map.insert(block_height, 0); - map - }); - let (_, latest_balance) = account_balances.iter().last().unwrap(); - assert_eq!( - operation.currency().unwrap(), - &native_coin(), - "Balance should be the native coin" - ); - - // Add with panic on overflow in case of too high of a balance - let delta = operation.amount().unwrap(); - let new_balance = *latest_balance + delta; - account_balances.insert(block_height, new_balance); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed stake reward operation" - ); - } - }, - OperationType::SetOperator => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful set operator operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed set operator operation" - ); - } - - // Check that operator was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_operator_address: AccountAddress = match ( - *payload.module().address(), - payload.module().name().as_str(), - payload.function().as_str(), - ) { - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION, - ) => bcs::from_bytes(payload.args().last().unwrap()).unwrap(), - ( - AccountAddress::ONE, - STAKING_CONTRACT_MODULE, - "create_staking_contract", - ) => bcs::from_bytes(payload.args().first().unwrap()).unwrap(), - _ => panic!("Unsupported entry function for set operator! {:?}", txn), - }; - - let operator = operation.new_operator().unwrap(); - assert_eq!(actual_operator_address, operator) - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::SetVoter => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful set voter operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed set voter operation" - ); - } - - // Check that voter was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_voter_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let voter = operation.new_voter().unwrap(); - assert_eq!(actual_voter_address, voter) - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::Fee => { - has_gas_op = true; - assert_eq!(OperationStatusType::Success, status); - let account = operation - .account() - .expect("There should be an account in a fee operation"); - - let account_balances = balances.entry(account).or_insert_with(|| { - let mut map = BTreeMap::new(); - map.insert(block_height, 0); - map - }); - let (_, latest_balance) = account_balances.iter().last().unwrap(); - assert_eq!( - operation.currency().unwrap(), - &native_coin(), - "Balance should be the native coin" - ); - let delta = operation.amount().unwrap(); - - // Subtract with panic on overflow in case of a negative balance - let new_balance = *latest_balance + delta; - account_balances.insert(block_height, new_balance); - match actual_txn.transaction { - aptos_types::transaction::Transaction::UserTransaction(ref txn) => { - assert_eq!( - actual_txn - .info - .gas_used() - .saturating_mul(txn.gas_unit_price()), - delta.unsigned_abs() as u64, - "Gas operation should always match gas used * gas unit price" - ) - }, - _ => { - panic!("Gas transactions should be user transactions!") - }, - }; - }, - OperationType::ResetLockup => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful reset lockup operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed reset lockup operation" - ); - } - - // Check that reset lockup was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_operator_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let operator = operation.operator().unwrap(); - assert_eq!(actual_operator_address, operator) - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::UpdateCommission => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful update commission operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed update commission operation" - ); - } - - // Check that update commmission was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_operator_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let operator = operation - .metadata - .as_ref() - .unwrap() - .operator - .as_ref() - .unwrap() - .account_address() - .unwrap(); - assert_eq!(actual_operator_address, operator); - - let new_commission = operation - .metadata - .as_ref() - .unwrap() - .commission_percentage - .as_ref() - .unwrap() - .0; - let actual_new_commission: u64 = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - assert_eq!(actual_new_commission, new_commission); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::InitializeStakePool => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful initialize stake pool operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed initialize stake pool operation" - ); - } - - // Check that reset lockup was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_operator_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let operator = operation.new_operator().unwrap(); - assert_eq!(actual_operator_address, operator); - - let actual_voter_address: AccountAddress = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - let voter = operation.new_voter().unwrap(); - assert_eq!(actual_voter_address, voter); - - let actual_stake_amount: u64 = - bcs::from_bytes(payload.args().get(2).unwrap()).unwrap(); - let stake = operation.staked_balance().unwrap(); - assert_eq!(actual_stake_amount, stake); - - let commission_percentage = operation.commission_percentage().unwrap(); - let actual_commission: u64 = - bcs::from_bytes(payload.args().get(3).unwrap()).unwrap(); - assert_eq!(actual_commission, commission_percentage); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::UnlockStake => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful unlock stake operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed unlock stake operation" - ); - } - - // Check that unlock stake was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_operator_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let operator = operation.operator().unwrap(); - assert_eq!(actual_operator_address, operator); - - let actual_amount: u64 = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - let amount = operation.metadata_amount().unwrap(); - assert_eq!(actual_amount, amount); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::DistributeStakingRewards => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful distribute operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed distribute operation" - ); - } - - // Check that distribute was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_staker_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - let staker = operation.staker().unwrap(); - assert_eq!(actual_staker_address, staker); - - let actual_operator_address: AccountAddress = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - let operator = operation.operator().unwrap(); - assert_eq!(actual_operator_address, operator); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::AddDelegatedStake => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful add_delegated_stake operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed add_delegated_stake operation" - ); - } - - // Check that add stake was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_pool_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - - let pool_address = operation - .metadata - .as_ref() - .unwrap() - .pool_address - .as_ref() - .unwrap() - .account_address() - .unwrap(); - - assert_eq!(actual_pool_address, pool_address); - - let actual_amount: u64 = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - - let amount = operation - .metadata - .as_ref() - .unwrap() - .amount - .as_ref() - .unwrap() - .0; - - assert_eq!(actual_amount, amount); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::UnlockDelegatedStake => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful unlock_delegated_stake operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed unlock_delegated_stake operation" - ); - } - - // Check that unlock stake was set the same - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_pool_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - - let pool_address = operation - .metadata - .as_ref() - .unwrap() - .pool_address - .as_ref() - .unwrap() - .account_address() - .unwrap(); - - assert_eq!(actual_pool_address, pool_address); - - let actual_amount: u64 = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - - let amount = operation - .metadata - .as_ref() - .unwrap() - .amount - .as_ref() - .unwrap() - .0; - - assert_eq!(actual_amount, amount); - } else { - panic!("Not an entry function"); - } - } else { - panic!("Not a user transaction"); - } - }, - OperationType::WithdrawUndelegatedFunds => { - if actual_successful { - assert_eq!( - OperationStatusType::Success, - status, - "Successful transaction should have successful distribute operation" - ); - } else { - assert_eq!( - OperationStatusType::Failure, - status, - "Failed transaction should have failed distribute operation" - ); - } - if let aptos_types::transaction::Transaction::UserTransaction(ref txn) = - actual_txn.transaction - { - if let aptos_types::transaction::TransactionPayload::EntryFunction( - ref payload, - ) = txn.payload() - { - let actual_pool_address: AccountAddress = - bcs::from_bytes(payload.args().first().unwrap()).unwrap(); - - let pool_address = operation - .metadata - .as_ref() - .unwrap() - .pool_address - .as_ref() - .unwrap() - .account_address() - .unwrap(); - - assert_eq!(actual_pool_address, pool_address); - - let actual_amount: u64 = - bcs::from_bytes(payload.args().get(1).unwrap()).unwrap(); - - let amount = operation - .metadata - .as_ref() - .unwrap() - .amount - .as_ref() - .unwrap() - .0; - - assert_eq!(actual_amount, amount); - } else { - panic!("Not an entry function"); - } - } - }, - } - } - - assert!( - has_gas_op - || transaction.metadata.transaction_type == TransactionType::Genesis - || transaction.operations.is_empty(), - "Must have a gas operation at least in a transaction except for Genesis", - ); -} - -/// Check that all balances are correct with the account balance command from the blocks -async fn check_balances( - rosetta_client: &RosettaClient, - chain_id: ChainId, - balances: BTreeMap>, -) { - // TODO: Check some random times that arent on changes? - for (account, account_balances) in balances { - for (block_height, expected_balance) in account_balances { - // Block should match it's calculated balance - let response = rosetta_client - .account_balance(&AccountBalanceRequest { - network_identifier: NetworkIdentifier::from(chain_id), - account_identifier: AccountIdentifier::base_account(account), - block_identifier: Some(PartialBlockIdentifier { - index: Some(block_height), - hash: None, - }), - currencies: Some(vec![native_coin()]), - }) - .await - .unwrap(); - assert_eq!( - block_height, response.block_identifier.index, - "Block should be the one expected" - ); - - let balance = response.balances.first().unwrap(); - assert_eq!( - balance.currency, - native_coin(), - "Balance should be the native coin" - ); - assert_eq!( - expected_balance, - balance - .value - .parse::() - .expect("Should have a balance from account balance") as i128, - "Expected {} to have a balance of {}, but was {} at block {}", - account, - expected_balance, - balance.value, - block_height - ); - } - } -} - -#[tokio::test] -async fn test_invalid_transaction_gas_charged() { - let (swarm, cli, _faucet, rosetta_client) = setup_simple_test(1).await; - let chain_id = swarm.chain_id(); - - // Make sure first that there's money to transfer - cli.assert_account_balance_now(0, DEFAULT_FUNDED_COINS) - .await; - - // Now let's see some transfers - const TRANSFER_AMOUNT: u64 = 5000; - let _ = cli - .transfer_invalid_addr( - 0, - TRANSFER_AMOUNT, - Some(GasOptions { - gas_unit_price: None, - max_gas: Some(1000), - expiration_secs: 30, - }), - ) - .await - .unwrap_err(); - - let sender = cli.account_id(0); - - // Find failed transaction - let validator = swarm.validators().next().unwrap(); - let rest_client = validator.rest_client(); - let txns = rest_client - .get_account_transactions(sender, None, None) - .await - .unwrap() - .into_inner(); - let actual_txn = txns.iter().find(|txn| !txn.success()).unwrap(); - let actual_txn = if let Transaction::UserTransaction(txn) = actual_txn { - txn - } else { - panic!("Not a user transaction"); - }; - let txn_version = actual_txn.info.version.0; - - let block_info = rest_client - .get_block_by_version(txn_version, false) - .await - .unwrap() - .into_inner(); - - let block_with_transfer = rosetta_client - .block(&BlockRequest::by_index(chain_id, block_info.block_height.0)) - .await - .unwrap(); - let block_with_transfer = block_with_transfer.block; - // Verify failed txn - let rosetta_txn = block_with_transfer - .transactions - .iter() - .find(|txn| txn.metadata.version.0 == txn_version) - .unwrap(); - - assert_failed_transfer_transaction( - sender, - AccountAddress::from_hex_literal(INVALID_ACCOUNT).unwrap(), - TRANSFER_AMOUNT, - actual_txn, - rosetta_txn, - ); -} - -fn assert_failed_transfer_transaction( - sender: AccountAddress, - receiver: AccountAddress, - transfer_amount: u64, - actual_txn: &UserTransaction, - rosetta_txn: &aptos_rosetta::types::Transaction, -) { - // Check the transaction - assert_eq!( - format!("{:x}", actual_txn.info.hash), - rosetta_txn.transaction_identifier.hash - ); - - let rosetta_txn_metadata = &rosetta_txn.metadata; - assert_eq!(TransactionType::User, rosetta_txn_metadata.transaction_type); - assert_eq!(actual_txn.info.version.0, rosetta_txn_metadata.version.0); - // This should have 3, the deposit, withdraw, and fee - assert_eq!(rosetta_txn.operations.len(), 3); - - // Check the operations - let mut seen_deposit = false; - let mut seen_withdraw = false; - for (i, operation) in rosetta_txn.operations.iter().enumerate() { - assert_eq!(i as u64, operation.operation_identifier.index); - if !seen_deposit && !seen_withdraw { - match OperationType::from_str(&operation.operation_type).unwrap() { - OperationType::Deposit => { - seen_deposit = true; - assert_deposit( - operation, - transfer_amount, - receiver, - actual_txn.info.success, - ); - }, - OperationType::Withdraw => { - seen_withdraw = true; - assert_withdraw(operation, transfer_amount, sender, actual_txn.info.success); - }, - _ => panic!("Shouldn't get any other operations"), - } - } else if !seen_deposit { - seen_deposit = true; - assert_deposit( - operation, - transfer_amount, - receiver, - actual_txn.info.success, - ); - } else if !seen_withdraw { - seen_withdraw = true; - assert_withdraw(operation, transfer_amount, sender, actual_txn.info.success); - } else { - // Gas is always last - assert_gas( - operation, - actual_txn.request.gas_unit_price.0 * actual_txn.info.gas_used.0, - sender, - true, - ); - } - } -} - -fn assert_deposit( - operation: &Operation, - expected_amount: u64, - account: AccountAddress, - success: bool, -) { - assert_transfer( - operation, - OperationType::Deposit, - expected_amount.to_string(), - account, - success, - ); -} - -fn assert_withdraw( - operation: &Operation, - expected_amount: u64, - account: AccountAddress, - success: bool, -) { - assert_transfer( - operation, - OperationType::Withdraw, - format!("-{}", expected_amount), - account, - success, - ); -} - -fn assert_gas(operation: &Operation, expected_amount: u64, account: AccountAddress, success: bool) { - assert_transfer( - operation, - OperationType::Fee, - format!("-{}", expected_amount), - account, - success, - ); -} - -fn assert_transfer( - operation: &Operation, - expected_type: OperationType, - expected_amount: String, - account: AccountAddress, - success: bool, -) { - assert_eq!(expected_type.to_string(), operation.operation_type); - let amount = operation.amount.as_ref().unwrap(); - assert_eq!(native_coin(), amount.currency); - assert_eq!(expected_amount, amount.value); - assert_eq!( - &AccountIdentifier::base_account(account), - operation.account.as_ref().unwrap() - ); - let expected_status = if success { - OperationStatusType::Success - } else { - OperationStatusType::Failure - } - .to_string(); - assert_eq!(&expected_status, operation.status.as_ref().unwrap()); -} - -/// Try for 2 seconds to get a response. This handles the fact that it's starting async -async fn try_until_ok_default(function: F) -> anyhow::Result -where - F: Fn() -> Fut, - Fut: Future>, -{ - try_until_ok( - DEFAULT_MAX_WAIT_DURATION, - DEFAULT_INTERVAL_DURATION, - function, - ) - .await -} - -async fn try_until_ok( - total_wait: Duration, - interval: Duration, - function: F, -) -> anyhow::Result -where - F: Fn() -> Fut, - Fut: Future>, -{ - let mut result = Err(anyhow::Error::msg("Failed to get response")); - let start = Instant::now(); - while start.elapsed() < total_wait { - result = function().await; - if result.is_ok() { - break; - } - tokio::time::sleep(interval).await; - } - - result -} - -struct NodeClients<'a> { - pub rosetta_client: &'a RosettaClient, - pub rest_client: &'a aptos_rest_client::Client, - pub network: &'a NetworkIdentifier, -} - -async fn create_account_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - new_account: AccountAddress, - txn_expiry_duration: Option, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - txn_expiry_duration.unwrap_or(DEFAULT_MAX_WAIT_DURATION), - |expiry_time| { - node_clients.rosetta_client.create_account( - node_clients.network, - sender_key, - new_account, - expiry_time, - sequence_number, - max_gas, - gas_unit_price, - ) - }, - ) - .await -} - -async fn simple_transfer_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - receiver: AccountAddress, - amount: u64, -) -> Result, ErrorWrapper> { - transfer_and_wait( - node_clients, - sender_key, - receiver, - amount, - None, - None, - None, - None, - ) - .await -} - -async fn transfer_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - receiver: AccountAddress, - amount: u64, - txn_expiry_duration: Option, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - txn_expiry_duration.unwrap_or(DEFAULT_MAX_WAIT_DURATION), - |expiry_time| { - node_clients.rosetta_client.transfer( - node_clients.network, - sender_key, - receiver, - amount, - expiry_time, - sequence_number, - max_gas, - gas_unit_price, - ) - }, - ) - .await -} - -async fn set_operator_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - old_operator: Option, - new_operator: AccountAddress, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.set_operator( - node_clients.network, - sender_key, - old_operator, - new_operator, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn set_voter_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: Option, - new_voter: AccountAddress, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.set_voter( - node_clients.network, - sender_key, - operator, - new_voter, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn create_stake_pool_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: Option, - voter: Option, - stake_amount: Option, - commission_percentage: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.create_stake_pool( - node_clients.network, - sender_key, - operator, - voter, - stake_amount, - commission_percentage, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn reset_lockup_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.reset_lockup( - node_clients.network, - sender_key, - operator, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn update_commission_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: Option, - new_commission_percentage: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.update_commission( - node_clients.network, - sender_key, - operator, - new_commission_percentage, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn unlock_stake_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: Option, - amount: Option, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.unlock_stake( - node_clients.network, - sender_key, - operator, - amount, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn distribute_staking_rewards_and_wait( - node_clients: &NodeClients<'_>, - sender_key: &Ed25519PrivateKey, - operator: AccountAddress, - staker: AccountAddress, -) -> Result, ErrorWrapper> { - submit_transaction( - node_clients.rest_client, - DEFAULT_MAX_WAIT_DURATION, - |expiry_time| { - node_clients.rosetta_client.distribute_staking_rewards( - node_clients.network, - sender_key, - operator, - staker, - expiry_time, - None, - None, - None, - ) - }, - ) - .await -} - -async fn submit_transaction< - Fut: Future>, - F: FnOnce(u64) -> Fut, ->( - rest_client: &aptos_rest_client::Client, - txn_expiry_duration: Duration, - transaction_builder: F, -) -> Result, ErrorWrapper> { - let expiry_time = expiry_time(txn_expiry_duration); - - let txn_hash = transaction_builder(expiry_time.as_secs()) - .await - .map_err(ErrorWrapper::BeforeSubmission)? - .hash; - wait_for_transaction(rest_client, expiry_time, txn_hash) - .await - .map_err(ErrorWrapper::AfterSubmission) -} - -async fn wait_for_transaction( - rest_client: &aptos_rest_client::Client, - expiry_time: Duration, - txn_hash: String, -) -> Result, Box> { - let hash_value = HashValue::from_str(&txn_hash).unwrap(); - let response = rest_client - .wait_for_transaction_by_hash( - hash_value, - expiry_time.as_secs(), - Some(DEFAULT_MAX_WAIT_DURATION), - None, - ) - .await; - match response { - Ok(response) => { - if let Transaction::UserTransaction(txn) = response.into_inner() { - Ok(txn) - } else { - panic!("Transaction is supposed to be a UserTransaction!") - } - }, - Err(_) => { - if let Transaction::UserTransaction(txn) = rest_client - .get_transaction_by_hash(hash_value) - .await - .unwrap() - .into_inner() - { - Err(txn) - } else { - panic!("Failed transaction is supposed to be a UserTransaction!"); - } - }, - } -} - -fn expiry_time(txn_expiry_duration: Duration) -> Duration { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .saturating_add(txn_expiry_duration) -} - -async fn add_delegated_stake_and_wait( - rosetta_client: &RosettaClient, - rest_client: &aptos_rest_client::Client, - network_identifier: &NetworkIdentifier, - sender_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - txn_expiry_duration: Duration, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, -) -> Result, ErrorWrapper> { - let expiry_time = expiry_time(txn_expiry_duration); - let txn_hash = rosetta_client - .add_delegated_stake( - network_identifier, - sender_key, - pool_address, - amount, - expiry_time.as_secs(), - sequence_number, - max_gas, - gas_unit_price, - ) - .await - .map_err(ErrorWrapper::BeforeSubmission)? - .hash; - - wait_for_transaction(rest_client, expiry_time, txn_hash) - .await - .map_err(ErrorWrapper::AfterSubmission) -} - -async fn unlock_delegated_stake_and_wait( - rosetta_client: &RosettaClient, - rest_client: &aptos_rest_client::Client, - network_identifier: &NetworkIdentifier, - sender_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - txn_expiry_duration: Duration, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, -) -> Result, ErrorWrapper> { - let expiry_time = expiry_time(txn_expiry_duration); - let txn_hash = rosetta_client - .unlock_delegated_stake( - network_identifier, - sender_key, - pool_address, - amount, - expiry_time.as_secs(), - sequence_number, - max_gas, - gas_unit_price, - ) - .await - .map_err(ErrorWrapper::BeforeSubmission)? - .hash; - wait_for_transaction(rest_client, expiry_time, txn_hash) - .await - .map_err(ErrorWrapper::AfterSubmission) -} - -async fn withdraw_undelegated_stake_and_wait( - rosetta_client: &RosettaClient, - rest_client: &aptos_rest_client::Client, - network_identifier: &NetworkIdentifier, - sender_key: &Ed25519PrivateKey, - pool_address: AccountAddress, - amount: Option, - txn_expiry_duration: Duration, - sequence_number: Option, - max_gas: Option, - gas_unit_price: Option, -) -> Result, ErrorWrapper> { - let expiry_time = expiry_time(txn_expiry_duration); - let txn_hash = rosetta_client - .withdraw_undelegated_stake( - network_identifier, - sender_key, - pool_address, - amount, - expiry_time.as_secs(), - sequence_number, - max_gas, - gas_unit_price, - ) - .await - .map_err(ErrorWrapper::BeforeSubmission)? - .hash; - - wait_for_transaction(rest_client, expiry_time, txn_hash) - .await - .map_err(ErrorWrapper::AfterSubmission) -} - -#[tokio::test] -async fn test_delegation_pool_operations() { - const NUM_TXNS_PER_PAGE: u16 = 2; - - let (mut swarm, cli, _, rosetta_client) = setup_test( - 2, - Arc::new(|_, config, _| config.api.max_transactions_page_size = NUM_TXNS_PER_PAGE), - ) - .await; - - // 20 APT - let delegate_initial_balance = 20 * u64::pow(10, 8); - cli.fund_account(0, Some(delegate_initial_balance)) - .await - .unwrap(); - let delegate_account_private_key = cli.private_key(0); - - let chain_id = swarm.chain_id(); - let validator = swarm.validators().next().unwrap(); - let rest_client = validator.rest_client(); - let _request = NetworkRequest { - network_identifier: NetworkIdentifier::from(chain_id), - }; - let network_identifier = chain_id.into(); - - let root_address = swarm.aptos_public_info().root_account().address(); - let root_sequence_number = swarm - .aptos_public_info() - .client() - .get_account_bcs(root_address) - .await - .unwrap() - .into_inner() - .sequence_number(); - - swarm - .aptos_public_info() - .root_account() - .set_sequence_number(root_sequence_number); - - let mut delegation_pool_creator_account = swarm - .aptos_public_info() - .create_and_fund_user_account(1_000_000_000_000_000) - .await - .unwrap(); - - let res = create_delegation_pool( - &swarm.aptos_public_info(), - &mut delegation_pool_creator_account, - 10, - 1, - ) - .await; - - let (tx, _) = res.into_parts(); - let tx_serialized = json!(tx); - let mut pool_address_str = ""; - - if let Some(changes) = tx_serialized["changes"].as_array() { - for change in changes { - if change["data"]["type"] == "0x1::delegation_pool::DelegationPool" { - pool_address_str = change["address"].as_str().unwrap(); - break; - } - } - } - let pool_address = AccountAddress::from_hex_literal(pool_address_str).unwrap(); - - // Must stake at least 11 APT - let staked_amount = 11 * u64::pow(10, 8); - - add_delegated_stake_and_wait( - &rosetta_client, - &rest_client, - &network_identifier, - delegate_account_private_key, - pool_address, - Some(staked_amount), - Duration::from_secs(5), - None, - None, - None, - ) - .await - .expect("Should successfully add delegated stake"); - - unlock_delegated_stake_and_wait( - &rosetta_client, - &rest_client, - &network_identifier, - delegate_account_private_key, - pool_address, - Some(staked_amount), - Duration::from_secs(5), - None, - None, - None, - ) - .await - .expect("Should successfully unlock delegated stake"); - - let final_txn = withdraw_undelegated_stake_and_wait( - &rosetta_client, - &rest_client, - &network_identifier, - delegate_account_private_key, - pool_address, - Some(staked_amount), - Duration::from_secs(5), - None, - None, - None, - ) - .await - .expect("Should successfully withdraw undelegated"); - - let final_block_to_check = rest_client - .get_block_by_version(final_txn.info.version.0, false) - .await - .expect("Should be able to get block info for completed txns"); - - // Check a couple blocks past the final transaction to check more txns - let final_block_height = final_block_to_check.into_inner().block_height.0 + 2; - - // TODO: Track total supply? - // TODO: Check account balance block hashes? - // TODO: Handle multiple coin types - - // Wait until the Rosetta service is ready - let request = NetworkRequest { - network_identifier: NetworkIdentifier::from(chain_id), - }; - - loop { - let status = try_until_ok_default(|| rosetta_client.network_status(&request)) - .await - .unwrap(); - if status.current_block_identifier.index >= final_block_height { - break; - } - } - - // Now we have to watch all the changes - let mut current_version = 0; - let mut balances = BTreeMap::>::new(); - let mut previous_block_index = 0; - let mut block_hashes = HashSet::new(); - for block_height in 0..final_block_height { - let request = BlockRequest::by_index(chain_id, block_height); - let response: BlockResponse = rosetta_client - .block(&request) - .await - .expect("Should be able to get blocks that are already known"); - let block = response.block; - let actual_block = rest_client - .get_block_by_height_bcs(block_height, true) - .await - .expect("Should be able to get block for a known block") - .into_inner(); - - assert_eq!( - block.block_identifier.index, block_height, - "The block should match the requested block" - ); - assert_eq!( - block.block_identifier.hash, - BlockHash::new(chain_id, block_height).to_string(), - "Block hash should match chain_id-block_height" - ); - assert_eq!( - block.parent_block_identifier.index, previous_block_index, - "Parent block index should be previous block" - ); - assert_eq!( - block.parent_block_identifier.hash, - BlockHash::new(chain_id, previous_block_index).to_string(), - "Parent block hash should be previous block chain_id-block_height" - ); - assert!( - block_hashes.insert(block.block_identifier.hash.clone()), - "Block hash was repeated {}", - block.block_identifier.hash - ); - - // It's only greater or equal because microseconds are cut off - let expected_timestamp = if block_height == 0 { - Y2K_MS - } else { - actual_block.block_timestamp.saturating_div(1000) - }; - assert_eq!( - expected_timestamp, block.timestamp, - "Block timestamp should match actual timestamp but in ms" - ); - - // TODO: double check that all transactions do show with the flag, and that all expected txns - // are shown without the flag - - let actual_txns = actual_block - .transactions - .as_ref() - .expect("Every actual block should have transactions"); - - parse_block_transactions(&block, &mut balances, actual_txns, &mut current_version).await; - - // Keep track of the previous - previous_block_index = block_height; - } -} - -#[derive(Debug)] -pub enum ErrorWrapper { - BeforeSubmission(anyhow::Error), - AfterSubmission(Box), -} diff --git a/testsuite/smoke-test/src/smoke_test_environment.rs b/testsuite/smoke-test/src/smoke_test_environment.rs deleted file mode 100644 index f2c32fa25ad99..0000000000000 --- a/testsuite/smoke-test/src/smoke_test_environment.rs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos::test::CliTestFramework; -use aptos_config::{config::NodeConfig, keys::ConfigKey, utils::get_available_port}; -use aptos_crypto::ed25519::Ed25519PrivateKey; -use aptos_faucet_core::server::{FunderKeyEnum, RunConfig}; -use aptos_forge::{ActiveNodesGuard, Factory, LocalFactory, LocalSwarm, Node}; -use aptos_framework::ReleaseBundle; -use aptos_genesis::builder::{InitConfigFn, InitGenesisConfigFn, InitGenesisStakeFn}; -use aptos_infallible::Mutex; -use aptos_logger::prelude::*; -use aptos_types::chain_id::ChainId; -use once_cell::sync::Lazy; -use rand::rngs::OsRng; -use std::{num::NonZeroUsize, sync::Arc}; -use tokio::task::JoinHandle; - -const SWARM_BUILD_NUM_RETRIES: u8 = 3; - -#[derive(Clone)] -pub struct SwarmBuilder { - local: bool, - num_validators: NonZeroUsize, - num_fullnodes: usize, - genesis_framework: Option, - init_config: Option, - vfn_config: Option, - init_genesis_stake: Option, - init_genesis_config: Option, -} - -impl SwarmBuilder { - pub fn new(local: bool, num_validators: usize) -> Self { - Self { - local, - num_validators: NonZeroUsize::new(num_validators).unwrap(), - num_fullnodes: 0, - genesis_framework: None, - init_config: None, - vfn_config: None, - init_genesis_stake: None, - init_genesis_config: None, - } - } - - pub fn new_local(num_validators: usize) -> Self { - Self::new(true, num_validators) - } - - pub fn with_aptos(mut self) -> Self { - self.genesis_framework = Some(aptos_cached_packages::head_release_bundle().clone()); - self - } - - pub fn with_aptos_testnet(mut self) -> Self { - self.genesis_framework = Some(aptos_framework::testnet_release_bundle().clone()); - self - } - - pub fn with_init_config(mut self, init_config: InitConfigFn) -> Self { - self.init_config = Some(init_config); - self - } - - pub fn with_vfn_config(mut self, config: NodeConfig) -> Self { - self.vfn_config = Some(config); - self - } - - pub fn with_init_genesis_stake(mut self, init_genesis_stake: InitGenesisStakeFn) -> Self { - self.init_genesis_stake = Some(init_genesis_stake); - self - } - - pub fn with_init_genesis_config(mut self, init_genesis_config: InitGenesisConfigFn) -> Self { - self.init_genesis_config = Some(init_genesis_config); - self - } - - pub fn with_num_fullnodes(mut self, num_fullnodes: usize) -> Self { - self.num_fullnodes = num_fullnodes; - self - } - - // Gas is not enabled with this setup, it's enabled via forge instance. - pub async fn build_inner(&mut self) -> anyhow::Result { - ::aptos_logger::Logger::new().init(); - info!("Preparing to finish compiling"); - // TODO change to return Swarm trait - // Add support for forge - assert!(self.local); - static FACTORY: Lazy = - Lazy::new(|| LocalFactory::from_workspace(None).unwrap()); - let version = FACTORY.versions().max().unwrap(); - info!("Node finished compiling"); - - let slots = self.num_validators.get() * 2; - - static ACTIVE_NODES: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(0))); - let guard = ActiveNodesGuard::grab(slots, ACTIVE_NODES.clone()).await; - - let builder = self.clone(); - let init_genesis_config = builder.init_genesis_config; - FACTORY - .new_swarm_with_version( - OsRng, - builder.num_validators, - builder.num_fullnodes, - &version, - builder.genesis_framework, - builder.init_config, - builder.vfn_config, - builder.init_genesis_stake, - Some(Arc::new(move |genesis_config| { - if let Some(init_genesis_config) = &init_genesis_config { - (init_genesis_config)(genesis_config); - } - })), - guard, - ) - .await - } - - // Gas is not enabled with this setup, it's enabled via forge instance. - // Local swarm spin-up can fail due to port issues. So we retry SWARM_BUILD_NUM_RETRIES times. - pub async fn build(&mut self) -> LocalSwarm { - let num_retries = SWARM_BUILD_NUM_RETRIES; - let mut attempt = 0; - loop { - if attempt > num_retries { - panic!("Exhausted retries: {} / {}", attempt, num_retries); - } - match self.build_inner().await { - Ok(swarm) => { - return swarm; - }, - Err(err) => warn!("Attempt {} / {} failed with: {}", attempt, num_retries, err), - } - attempt += 1; - } - } - - pub async fn build_with_cli( - &mut self, - num_cli_accounts: usize, - ) -> (LocalSwarm, CliTestFramework, JoinHandle>) { - let swarm = self.build().await; - let chain_id = swarm.chain_id(); - let validator = swarm.validators().next().unwrap(); - let root_key = swarm.root_key(); - let faucet_port = get_available_port(); - let faucet = launch_faucet( - validator.rest_api_endpoint(), - root_key, - chain_id, - faucet_port, - ); - let faucet_endpoint: reqwest::Url = - format!("http://localhost:{}", faucet_port).parse().unwrap(); - // Connect the operator tool to the node's JSON RPC API - let tool = CliTestFramework::new( - validator.rest_api_endpoint(), - faucet_endpoint, - num_cli_accounts, - ) - .await; - println!( - "Created CLI with {} accounts for LocalSwarm", - num_cli_accounts - ); - (swarm, tool, faucet) - } -} - -// Gas is not enabled with this setup, it's enabled via forge instance. -pub async fn new_local_swarm_with_aptos(num_validators: usize) -> LocalSwarm { - SwarmBuilder::new_local(num_validators) - .with_aptos() - .build() - .await -} - -#[tokio::test] -async fn test_prevent_starting_nodes_twice() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - assert!(swarm.launch().await.is_err()); - let validator = swarm.validators_mut().next().unwrap(); - assert!(validator.start().is_err()); - validator.stop(); - assert!(validator.start().is_ok()); - assert!(validator.start().is_err()); -} - -pub fn launch_faucet( - endpoint: reqwest::Url, - mint_key: Ed25519PrivateKey, - chain_id: ChainId, - port: u16, -) -> JoinHandle> { - let faucet_config = RunConfig::build_for_cli( - endpoint, - "0.0.0.0".to_string(), - port, - FunderKeyEnum::Key(ConfigKey::new(mint_key)), - true, - Some(chain_id), - ); - tokio::spawn(faucet_config.run()) -} diff --git a/testsuite/smoke-test/src/state_sync.rs b/testsuite/smoke-test/src/state_sync.rs deleted file mode 100644 index 92dc5929c9fc3..0000000000000 --- a/testsuite/smoke-test/src/state_sync.rs +++ /dev/null @@ -1,1022 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::{new_local_swarm_with_aptos, SwarmBuilder}, - test_utils::{ - create_test_accounts, execute_transactions, execute_transactions_and_wait, - wait_for_all_nodes, MAX_CATCH_UP_WAIT_SECS, MAX_HEALTHY_WAIT_SECS, - }, -}; -use aptos_config::config::{ - BootstrappingMode, ContinuousSyncingMode, NodeConfig, OverrideNodeConfig, -}; -use aptos_db::AptosDB; -use aptos_forge::{LocalNode, LocalSwarm, Node, NodeExt, Swarm}; -use aptos_inspection_service::inspection_client::InspectionClient; -use aptos_rest_client::Client as RestClient; -use aptos_storage_interface::DbReader; -use aptos_types::{ - account_address::AccountAddress, - on_chain_config::{ - ConsensusConfigV1, LeaderReputationType, OnChainConsensusConfig, ProposerAndVoterConfig, - ProposerElectionType, - }, - PeerId, -}; -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; - -// TODO: Go through the existing tests, identify any gaps, and clean up the rest. - -#[tokio::test] -async fn test_fullnode_fast_sync_epoch_changes() { - // Test fast syncing in the presence of epoch changes - test_fullnode_fast_sync(true).await; -} - -#[tokio::test] -async fn test_fullnode_fast_sync_no_epoch_changes() { - // Test fast syncing without epoch changes - test_fullnode_fast_sync(false).await; -} - -#[tokio::test] -async fn test_fullnode_output_sync_epoch_changes() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transaction outputs to sync - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs; - vfn_config.state_sync.aptos_data_client.use_compression = true; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_output_sync_no_compression() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transaction outputs to sync (without compression) - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs; - vfn_config.state_sync.aptos_data_client.use_compression = false; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_output_sync_exponential_backoff() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transaction outputs to sync with a small timeout - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs; - vfn_config.state_sync.aptos_data_client.response_timeout_ms = 1; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_intelligent_sync_epoch_changes() { - // Create a validator swarm of 1 validator node with a small network limit - let mut swarm = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.storage_service.max_network_chunk_bytes = 5 * 1024; - })) - .build() - .await; - - // Create a fullnode config that uses transactions or outputs to sync - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteOrApplyFromGenesis; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactionsOrApplyOutputs; - vfn_config - .state_sync - .aptos_data_client - .max_num_output_reductions = 1; - vfn_config.state_sync.aptos_data_client.response_timeout_ms = 1; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_fast_and_intelligent_sync_epoch_changes() { - // Create a validator swarm of 1 validator node with a small network limit - let mut swarm = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.storage_service.max_network_chunk_bytes = 500 * 1024; - })) - .build() - .await; - - // Create a fullnode config that uses fast and intelligent syncing - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactionsOrApplyOutputs; - vfn_config - .state_sync - .aptos_data_client - .max_num_output_reductions = 2; - vfn_config.state_sync.aptos_data_client.response_timeout_ms = 1; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_execution_sync_epoch_changes() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transactions to sync - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions; - vfn_config.state_sync.aptos_data_client.use_compression = true; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, true, false).await; -} - -#[tokio::test] -async fn test_fullnode_output_sync_no_epoch_changes() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transaction outputs to sync - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ApplyTransactionOutputs; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, false, false).await; -} - -#[tokio::test] -async fn test_fullnode_execution_sync_no_epoch_changes() { - // Create a validator swarm of 1 validator node - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Create a fullnode config that uses transactions to sync - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config - .state_sync - .state_sync_driver - .continuous_syncing_mode = ContinuousSyncingMode::ExecuteTransactions; - - // Create the fullnode and test its ability to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, false, false).await; -} - -#[tokio::test] -async fn test_single_validator_reboot() { - // Create a swarm of 1 validator - let mut swarm = new_local_swarm_with_aptos(1).await; - - // Execute multiple transactions - let validator = swarm.validators_mut().next().unwrap(); - let validator_client = validator.rest_client(); - let (mut account_0, mut account_1) = create_test_accounts(&mut swarm).await; - execute_transactions( - &mut swarm, - &validator_client, - &mut account_0, - &account_1, - true, - ) - .await; - - // Restart the validator - let validator = swarm.validators_mut().next().unwrap(); - validator.stop(); - validator.start().unwrap(); - swarm - .wait_all_alive(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - // Execute more transactions - execute_transactions_and_wait( - &mut swarm, - &validator_client, - &mut account_1, - &account_0, - true, - ) - .await; -} - -#[tokio::test] -async fn test_validator_output_sync_epoch_changes() { - // Create a swarm of 4 validators using output syncing - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_sync_and_participate_epoch_changes() { - // Test the default syncing method with epoch changes - test_validator_sync_and_participate(false, true).await; -} - -#[tokio::test] -async fn test_validator_sync_and_participate_no_epoch_changes() { - // Test the default syncing method without epoch changes - test_validator_sync_and_participate(false, false).await; -} - -#[tokio::test] -async fn test_validator_fast_sync_and_participate_epoch_changes() { - // Test fast syncing with epoch changes - test_validator_sync_and_participate(true, true).await; -} - -#[tokio::test] -async fn test_validator_fast_sync_and_participate_no_epoch_changes() { - // Test fast syncing without epoch changes - test_validator_sync_and_participate(true, false).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_output_sync_small_network_limit() { - // Create a swarm of 4 validators using output syncing and an aggressive network limit - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - config.state_sync.storage_service.max_network_chunk_bytes = 100 * 1024; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_output_sync_unrealistic_network_limit() { - // Create a swarm of 4 validators using output syncing and an unrealistic network limit. - // This forces all chunks to be of size 1. - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - config.state_sync.storage_service.max_network_chunk_bytes = 1; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_fast_sync_no_compression() { - // Create a swarm of 4 validators using fast syncing and no compression - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - config.state_sync.aptos_data_client.use_compression = false; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_fast_sync_small_network_limit() { - // Create a swarm of 4 validators using fast sync and an aggressive network limit - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - config.state_sync.storage_service.max_network_chunk_bytes = 200 * 1024; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_fast_sync_unrealistic_network_limit() { - // Create a swarm of 4 validators using fast sync and an unrealistic network limit. - // This forces all chunks to be of size 1. - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - config.state_sync.storage_service.max_network_chunk_bytes = 1; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_fast_sync_exponential_backoff_epoch_changes() { - // Test fast syncing with exponential backoff and epoch changes - test_validator_sync_exponential_backoff(true).await; -} - -#[tokio::test] -async fn test_validator_fast_sync_exponential_backoff_no_epoch_changes() { - // Test fast syncing without exponential backoff and no epoch changes - test_validator_sync_exponential_backoff(false).await; -} - -#[tokio::test] -async fn test_validator_execution_sync_epoch_changes() { - // Create a swarm of 4 validators using transaction syncing - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_intelligent_sync_epoch_changes() { - // Create a swarm of 4 validators using transaction or output syncing - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteOrApplyFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactionsOrApplyOutputs; - config.state_sync.storage_service.max_network_chunk_bytes = 10 * 1024; - config - .state_sync - .aptos_data_client - .max_num_output_reductions = 1; - config.state_sync.aptos_data_client.response_timeout_ms = 1; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_execution_sync_small_network_limits() { - // Create a swarm of 4 validators using transaction syncing and an aggressive network limit - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - config.state_sync.storage_service.max_network_chunk_bytes = 100 * 1024; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_validator_execution_sync_unrealistic_network_limits() { - // Create a swarm of 4 validators using transaction syncing and an unrealistic network limit. - // This forces all chunks to be of size 1. - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - config.state_sync.storage_service.max_network_chunk_bytes = 1; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_output_sync_exponential_backoff() { - // Create a swarm of 4 validators using output syncing and a small response timeout - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ApplyTransactionOutputsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - config.state_sync.aptos_data_client.response_timeout_ms = 1; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[tokio::test] -async fn test_validator_execution_sync_no_compression() { - // Create a swarm of 4 validators using transaction syncing and no compression - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::ExecuteTransactionsFromGenesis; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - config.state_sync.aptos_data_client.use_compression = false; - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, true).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_all_validators_fast_and_output_sync() { - // Create a swarm of 4 validators with fast and output syncing - let swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - })) - .build() - .await; - - // Test the ability of all validators to sync - test_all_validator_failures(swarm).await; -} - -#[ignore] // Ignore this test because it takes a long time. But, it works so it shouldn't be removed. -#[tokio::test] -async fn test_all_validators_fast_and_execution_sync() { - // Create a swarm of 4 validators with fast and execution syncing - let swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ExecuteTransactions; - })) - .build() - .await; - - // Test the ability of all validators to sync - test_all_validator_failures(swarm).await; -} - -/// Creates a new full node using the given config and swarm -async fn create_fullnode(full_node_config: NodeConfig, swarm: &mut LocalSwarm) -> PeerId { - let validator_peer_id = swarm.validators().next().unwrap().peer_id(); - let vfn_peer_id = swarm - .add_validator_fullnode( - &swarm.versions().max().unwrap(), - OverrideNodeConfig::new_with_default_base(full_node_config), - validator_peer_id, - ) - .unwrap(); - for fullnode in swarm.full_nodes_mut() { - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - } - vfn_peer_id -} - -/// A test method that verifies that a fullnode can fast sync from -/// a validator after a data wipe. If `epoch_changes` are enabled -/// then epoch changes can occur during test execution and fullnode syncing. -async fn test_fullnode_fast_sync(epoch_changes: bool) { - // Create a swarm with 2 validators - let mut swarm = SwarmBuilder::new_local(2) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.epoch_duration_secs = 10_000; // Prevent epoch changes from occurring unnecessarily - })) - .build() - .await; - - // Verify the oldest ledger info and pruning metrics for the validators - for validator in swarm.validators_mut() { - verify_fast_sync_version_and_metrics(validator, true).await; - } - - // Create a fullnode config that uses fast syncing - let mut vfn_config = NodeConfig::get_default_vfn_config(); - vfn_config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - - // Test the ability of a fullnode to sync - let vfn_peer_id = create_fullnode(vfn_config, &mut swarm).await; - test_fullnode_sync(vfn_peer_id, &mut swarm, epoch_changes, true).await; - - // Verify the oldest ledger info and pruning metrics for the fullnode - if epoch_changes { - let fullnode = swarm.fullnode_mut(vfn_peer_id).unwrap(); - verify_fast_sync_version_and_metrics(fullnode, false).await; - } -} - -/// A test method that verifies that a validator can fast sync from other -/// validators after a data wipe and while requiring exponential backoff. -/// If `epoch_changes` are enabled then epoch changes can occur during -/// test execution and validator syncing. -async fn test_validator_sync_exponential_backoff(epoch_changes: bool) { - // Create a swarm of 4 validators using fast sync and a small response timeout - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, config, _| { - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.state_sync_driver.continuous_syncing_mode = - ContinuousSyncingMode::ApplyTransactionOutputs; - config.state_sync.aptos_data_client.use_compression = false; - config.state_sync.aptos_data_client.response_timeout_ms = 1; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.epoch_duration_secs = 10_000; // Prevent epoch changes from occurring unnecessarily - })) - .build() - .await; - - // Test the ability of the validator to sync - test_validator_sync(&mut swarm, 1, epoch_changes).await; -} - -/// A helper method that tests that a full node can sync from a validator after -/// a failure and continue to stay up-to-date. -async fn test_fullnode_sync( - vfn_peer_id: PeerId, - swarm: &mut LocalSwarm, - epoch_changes: bool, - clear_storage: bool, -) { - // Stop the fullnode and potentially clear storage - if clear_storage { - stop_fullnode_and_delete_storage(swarm, vfn_peer_id).await; - } else { - swarm.fullnode_mut(vfn_peer_id).unwrap().stop(); - } - - // Execute a number of transactions on the validator - let validator_peer_id = swarm.validators().next().unwrap().peer_id(); - let validator_client = swarm.validator(validator_peer_id).unwrap().rest_client(); - let (mut account_0, mut account_1) = create_test_accounts(swarm).await; - execute_transactions( - swarm, - &validator_client, - &mut account_0, - &account_1, - epoch_changes, - ) - .await; - - // Restart the fullnode and verify it can sync - swarm - .fullnode_mut(vfn_peer_id) - .unwrap() - .restart() - .await - .unwrap(); - wait_for_all_nodes(swarm).await; - - // Execute more transactions on the validator and verify the fullnode catches up - execute_transactions_and_wait( - swarm, - &validator_client, - &mut account_1, - &account_0, - epoch_changes, - ) - .await; -} - -/// A helper method that tests that a validator can sync after a failure and -/// continue to stay up-to-date. -async fn test_validator_sync( - swarm: &mut LocalSwarm, - validator_index_to_test: usize, - epoch_changes: bool, -) { - // Execute multiple transactions through validator 0 - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let validator_client_0 = swarm - .validator(validator_peer_ids[0]) - .unwrap() - .rest_client(); - let (mut account_0, mut account_1) = create_test_accounts(swarm).await; - execute_transactions_and_wait( - swarm, - &validator_client_0, - &mut account_0, - &account_1, - epoch_changes, - ) - .await; - - // Stop the specified validator and delete the storage - let validator = validator_peer_ids[validator_index_to_test]; - stop_validator_and_delete_storage(swarm, validator).await; - - // Execute more transactions - execute_transactions( - swarm, - &validator_client_0, - &mut account_1, - &account_0, - epoch_changes, - ) - .await; - - // Restart the validator and wait for all nodes to catchup - swarm.validator_mut(validator).unwrap().start().unwrap(); - wait_for_all_nodes(swarm).await; - - // Execute multiple transactions and verify the validator - // can sync and that consensus is still running. - execute_transactions_and_wait( - swarm, - &validator_client_0, - &mut account_0, - &account_1, - epoch_changes, - ) - .await; -} - -/// A test method that verifies that a validator can sync after a data wipe -/// and begin to participate in consensus. If `epoch_changes` are enabled -/// then epoch changes can occur during test execution and validator syncing. -/// If `fast_sync` is true, then the validator will use fast (snapshot) -/// syncing. Otherwise, it will use the default syncing method. -async fn test_validator_sync_and_participate(fast_sync: bool, epoch_changes: bool) { - // Create a swarm of 4 validators - let num_validators = 4; - let mut swarm = SwarmBuilder::new_local(num_validators) - .with_aptos() - .with_init_config(Arc::new(move |_, config, _| { - if fast_sync { - // Set the bootstrapping mode to fast syncing - config.state_sync.state_sync_driver.bootstrapping_mode = - BootstrappingMode::DownloadLatestStates; - config.state_sync.storage_service.max_state_chunk_size = 30; - } - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - // Shorten the required proposer history to speed up the test - let consensus_config = match genesis_config.consensus_config.clone() { - OnChainConsensusConfig::V1(consensus_config) => consensus_config, - OnChainConsensusConfig::V2(consensus_config) => consensus_config, - OnChainConsensusConfig::V3 { alg, .. } => alg.unwrap_jolteon_config_v1().clone(), - }; - let leader_reputation_type = match &consensus_config.proposer_election_type { - ProposerElectionType::LeaderReputation(leader_reputation_type) => { - leader_reputation_type - }, - proposer_election_type => panic!( - "This test requires a leader reputation proposer election, but got: {:?}", - proposer_election_type - ), - }; - let proposer_and_voter_config = match &leader_reputation_type { - LeaderReputationType::ProposerAndVoterV2(proposer_and_voter_config) => { - proposer_and_voter_config - }, - leader_reputation_type => panic!( - "This test requires a proposer and voter V2 leader reputation, but got: {:?}", - leader_reputation_type - ), - }; - genesis_config.consensus_config = OnChainConsensusConfig::V1(ConsensusConfigV1 { - proposer_election_type: ProposerElectionType::LeaderReputation( - LeaderReputationType::ProposerAndVoter(ProposerAndVoterConfig { - proposer_window_num_validators_multiplier: 1, - voter_window_num_validators_multiplier: 1, - use_history_from_previous_epoch_max_count: 1, - ..*proposer_and_voter_config - }), - ), - ..Default::default() - }); - - // Prevent epoch changes from occurring unnecessarily - genesis_config.epoch_duration_secs = 10_000; - })) - .build() - .await; - - // Test the ability of the second validator to sync - let validator_index_to_test = 1; - test_validator_sync(&mut swarm, validator_index_to_test, epoch_changes).await; - - // Verify the oldest ledger info and pruning metrics for the second validator - if fast_sync && epoch_changes { - let validator = swarm.validators_mut().nth(validator_index_to_test).unwrap(); - verify_fast_sync_version_and_metrics(validator, false).await; - } - - // Execute multiple transactions through the first validator - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let validator_client = swarm - .validator(*validator_peer_ids.first().unwrap()) - .unwrap() - .rest_client(); - let (mut account_0, account_1) = create_test_accounts(&mut swarm).await; - execute_transactions_and_wait( - &mut swarm, - &validator_client, - &mut account_0, - &account_1, - false, - ) - .await; - - // Stop the last validator (to prevent it from participating in consensus) - let last_validator = swarm - .validator_mut(*validator_peer_ids.last().unwrap()) - .unwrap(); - last_validator.stop(); - - // Verify that consensus is progressing (the second validator should participate after syncing) - execute_transactions( - &mut swarm, - &validator_client, - &mut account_0, - &account_1, - false, - ) - .await; -} - -/// A helper method that tests that all validators can sync after a failure and -/// continue to stay up-to-date. -pub async fn test_all_validator_failures(mut swarm: LocalSwarm) { - // Execute multiple transactions through validator 0 - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let validator_0 = validator_peer_ids[0]; - let validator_client_0 = swarm.validator(validator_0).unwrap().rest_client(); - let (mut account_0, mut account_1) = create_test_accounts(&mut swarm).await; - execute_transactions_and_wait( - &mut swarm, - &validator_client_0, - &mut account_0, - &account_1, - true, - ) - .await; - - // Go through each validator, stop the node, delete the storage and wait for it to come back - for validator in validator_peer_ids.clone() { - stop_validator_and_delete_storage(&mut swarm, validator).await; - swarm.validator_mut(validator).unwrap().start().unwrap(); - wait_for_all_nodes(&mut swarm).await; - } - - // Execute multiple transactions (no epoch changes) and verify validator 0 can sync - execute_transactions_and_wait( - &mut swarm, - &validator_client_0, - &mut account_1, - &account_0, - false, - ) - .await; - - // Go through each validator, stop the node, delete the storage and wait for it to come back - for validator in validator_peer_ids.clone() { - stop_validator_and_delete_storage(&mut swarm, validator).await; - swarm.validator_mut(validator).unwrap().start().unwrap(); - wait_for_all_nodes(&mut swarm).await; - } - - // Execute multiple transactions (with epoch changes) and verify validator 0 can sync - let validator_client_1 = swarm - .validator(validator_peer_ids[0]) - .unwrap() - .rest_client(); - execute_transactions_and_wait( - &mut swarm, - &validator_client_1, - &mut account_1, - &account_0, - true, - ) - .await; -} - -/// Stops the specified fullnode and deletes storage -async fn stop_fullnode_and_delete_storage(swarm: &mut LocalSwarm, fullnode: AccountAddress) { - let fullnode = swarm.full_node_mut(fullnode).unwrap(); - - // The fullnode is stopped during the clear_storage() call - fullnode.clear_storage().await.unwrap(); -} - -/// Stops the specified validator and deletes storage -async fn stop_validator_and_delete_storage(swarm: &mut LocalSwarm, validator: AccountAddress) { - let validator = swarm.validator_mut(validator).unwrap(); - - // The validator is stopped during the clear_storage() call - validator.clear_storage().await.unwrap(); -} - -/// Verifies that the oldest ledger info, pruning metrics and first -/// ledger info are all correctly aligned after a fast sync. -async fn verify_fast_sync_version_and_metrics(node: &mut LocalNode, sync_to_genesis: bool) { - // Verify the oldest ledger info for the node - verify_oldest_version_after_fast_sync(node.rest_client(), sync_to_genesis).await; - - // Verify the node's pruning metrics - let inspection_client = node.inspection_client(); - verify_pruning_metrics_after_fast_sync(inspection_client, sync_to_genesis).await; - - // Verify that the ledger info exists at version 0 - verify_first_ledger_info(node); -} - -/// Verifies that the ledger info at version 0 exists in the given node's DB -fn verify_first_ledger_info(node: &mut LocalNode) { - // Get the DB path for the node - let db_path = node.config().base.data_dir.as_path(); - let mut db_path_buf = db_path.to_path_buf(); - db_path_buf.push("db"); - - // Stop the node to prevent any DB contention - node.stop(); - - // Verify that the ledger info exists at version 0 - let aptos_db = AptosDB::new_for_test(db_path_buf.as_path()); - aptos_db.get_epoch_ending_ledger_info(0).unwrap(); - - // Restart the node - node.start().unwrap(); -} - -/// Verifies the oldest ledger version on a node after fast syncing -async fn verify_oldest_version_after_fast_sync( - node_rest_client: RestClient, - sync_to_genesis: bool, -) { - // Fetch the oldest ledger version from the node - let ledger_information = node_rest_client.get_ledger_information().await.unwrap(); - let oldest_ledger_version = ledger_information.inner().oldest_ledger_version; - - // Verify the oldest ledger version after fast syncing - if sync_to_genesis { - // The node should have fast synced to genesis - assert_eq!(oldest_ledger_version, 0); - } else { - // The node should have fast synced to the latest epoch - assert!(oldest_ledger_version > 0); - } -} - -/// Verifies the pruning metrics on a node after fast syncing -async fn verify_pruning_metrics_after_fast_sync( - node_inspection_client: InspectionClient, - sync_to_genesis: bool, -) { - // Fetch the pruning metrics from the node - let state_merkle_pruner_version = node_inspection_client - .get_node_metric_i64( - "aptos_pruner_versions{pruner_name=state_merkle_pruner,tag=min_readable}", - ) - .await - .unwrap() - .unwrap(); - let epoch_snapshot_pruner_version = node_inspection_client - .get_node_metric_i64( - "aptos_pruner_versions{pruner_name=epoch_snapshot_pruner,tag=min_readable}", - ) - .await - .unwrap() - .unwrap(); - let ledger_pruner_version = node_inspection_client - .get_node_metric_i64("aptos_pruner_versions{pruner_name=ledger_pruner,tag=min_readable}") - .await - .unwrap() - .unwrap(); - - // Verify that the pruning metrics are valid - if sync_to_genesis { - // The node should have fast synced to genesis - assert_eq!(state_merkle_pruner_version, 0); - assert_eq!(epoch_snapshot_pruner_version, 0); - assert_eq!(ledger_pruner_version, 0); - } else { - // The node should have fast synced to the latest epoch - assert!(state_merkle_pruner_version > 0); - assert!(epoch_snapshot_pruner_version > 0); - assert!(ledger_pruner_version > 0); - } -} diff --git a/testsuite/smoke-test/src/storage.rs b/testsuite/smoke-test/src/storage.rs deleted file mode 100644 index d60437909cc76..0000000000000 --- a/testsuite/smoke-test/src/storage.rs +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{ - assert_balance, create_and_fund_account, swarm_utils::insert_waypoint, - transfer_and_maybe_reconfig, transfer_coins, MAX_CATCH_UP_WAIT_SECS, MAX_HEALTHY_WAIT_SECS, - }, - workspace_builder, - workspace_builder::workspace_root, -}; -use anyhow::{bail, Result}; -use aptos_backup_cli::metadata::view::BackupStorageState; -use aptos_forge::{reconfig, NodeExt, Swarm, SwarmExt}; -use aptos_logger::info; -use aptos_temppath::TempPath; -use aptos_types::{transaction::Version, waypoint::Waypoint}; -use std::{ - fs, - path::Path, - process::Command, - time::{Duration, Instant}, -}; - -#[tokio::test] -async fn test_db_restore() { - // pre-build tools - ::aptos_logger::Logger::new().init(); - info!("---------- 0. test_db_restore started."); - workspace_builder::get_bin("aptos-debugger"); - info!("---------- 1. pre-building finished."); - - let mut swarm = SwarmBuilder::new_local(4).with_aptos().build().await; - info!("---------- 1.1 swarm built, sending some transactions."); - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let client_1 = swarm - .validator(validator_peer_ids[1]) - .unwrap() - .rest_client(); - let transaction_factory = swarm.chain_info().transaction_factory(); - - // set up: two accounts, a lot of money - let mut account_0 = create_and_fund_account(&mut swarm, 1000000).await; - let account_1 = create_and_fund_account(&mut swarm, 1000000).await; - - info!("---------- 1.2 wait for nodes to catch up."); - // we need to wait for all nodes to see it, as client_1 is different node from the - // one creating accounts above - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - info!("---------- 1.3 caught up."); - - assert_balance(&client_1, &account_0, 1000000).await; - assert_balance(&client_1, &account_1, 1000000).await; - - let mut expected_balance_0 = 999999; - let mut expected_balance_1 = 1000001; - - transfer_coins( - &client_1, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&client_1, &account_0, expected_balance_0).await; - assert_balance(&client_1, &account_1, expected_balance_1).await; - - expected_balance_0 -= 10; - expected_balance_1 += 10; - - transfer_and_maybe_reconfig( - &client_1, - &transaction_factory, - swarm.chain_info().root_account, - &mut account_0, - &account_1, - 5, - ) - .await; - // explicit reconfigs: we are at least at epoch 5 - for _ in 0..4 { - reconfig( - &client_1, - &transaction_factory, - swarm.chain_info().root_account, - ) - .await; - } - // some more reconfigs to complicate things by putting in multiple epoch boundaries - // in a transaction backup - transfer_and_maybe_reconfig( - &client_1, - &transaction_factory, - swarm.chain_info().root_account, - &mut account_0, - &account_1, - 5, - ) - .await; - assert_balance(&client_1, &account_0, expected_balance_0).await; - assert_balance(&client_1, &account_1, expected_balance_1).await; - - info!("---------- 2. reached at least epoch 5, starting backup coordinator."); - // make a backup from node 1 - let node1_config = swarm.validator(validator_peer_ids[1]).unwrap().config(); - let port = node1_config.storage.backup_service_address.port(); - let (backup_path, _) = db_backup(port, 5, 400, 200, 5, &[]); - // take down node 0 - let node_to_restart = validator_peer_ids[0]; - swarm.validator_mut(node_to_restart).unwrap().stop(); - - // nuke db - let node0_config_path = swarm.validator(node_to_restart).unwrap().config_path(); - let mut node0_config = swarm.validator(node_to_restart).unwrap().config().clone(); - let genesis_waypoint = node0_config.base.waypoint.genesis_waypoint(); - insert_waypoint(&mut node0_config, genesis_waypoint); - node0_config.save_to_path(node0_config_path).unwrap(); - let db_dir = node0_config.storage.dir(); - fs::remove_dir_all(db_dir.clone()).unwrap(); - - info!("---------- 3. stopped node 0, gonna restore DB."); - // restore db from backup - db_restore( - backup_path.path(), - db_dir.as_path(), - &[], - node0_config.storage.rocksdb_configs.enable_storage_sharding, - None, - ); - - expected_balance_0 -= 3; - expected_balance_1 += 3; - - transfer_and_maybe_reconfig( - &client_1, - &transaction_factory, - swarm.chain_info().root_account, - &mut account_0, - &account_1, - 3, - ) - .await; - - assert_balance(&client_1, &account_0, expected_balance_0).await; - assert_balance(&client_1, &account_1, expected_balance_1).await; - - info!("---------- 4. Gonna restart node 0."); - // start node 0 on top of restored db - swarm - .validator_mut(node_to_restart) - .unwrap() - .start() - .unwrap(); - swarm - .validator_mut(node_to_restart) - .unwrap() - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - info!("---------- 5. Node 0 is healthy, verify it's caught up."); - // verify it's caught up - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - let client_0 = swarm.validator(node_to_restart).unwrap().rest_client(); - - assert_balance(&client_0, &account_0, expected_balance_0).await; - assert_balance(&client_0, &account_1, expected_balance_1).await; - info!("6. Done"); -} - -fn db_backup_verify(backup_path: &Path, trusted_waypoints: &[Waypoint]) { - info!("---------- running aptos-debugger aptos-db backup-verify"); - let now = Instant::now(); - let bin_path = workspace_builder::get_bin("aptos-debugger"); - let metadata_cache_path = TempPath::new(); - - metadata_cache_path.create_as_dir().unwrap(); - - let mut cmd = Command::new(bin_path.as_path()); - cmd.args(["aptos-db", "backup", "verify"]); - trusted_waypoints.iter().for_each(|w| { - cmd.arg("--trust-waypoint"); - cmd.arg(&w.to_string()); - }); - - let status = cmd - .args([ - "--metadata-cache-dir", - metadata_cache_path.path().to_str().unwrap(), - "--concurrent-downloads", - "4", - "--local-fs-dir", - backup_path.to_str().unwrap(), - ]) - .current_dir(workspace_root()) - .status() - .unwrap(); - assert!(status.success(), "{}", status); - info!("Backup verified in {} seconds.", now.elapsed().as_secs()); -} - -fn replay_verify(backup_path: &Path, trusted_waypoints: &[Waypoint]) { - info!("---------- running replay-verify"); - let now = Instant::now(); - let bin_path = workspace_builder::get_bin("aptos-debugger"); - let metadata_cache_path = TempPath::new(); - let target_db_dir = TempPath::new(); - - metadata_cache_path.create_as_dir().unwrap(); - - let mut cmd = Command::new(bin_path.as_path()); - cmd.args(["aptos-db", "replay-verify"]); - trusted_waypoints.iter().for_each(|w| { - cmd.arg("--trust-waypoint"); - cmd.arg(&w.to_string()); - }); - - let replay = cmd - .args([ - "--metadata-cache-dir", - metadata_cache_path.path().to_str().unwrap(), - "--concurrent-downloads", - "4", - "--target-db-dir", - target_db_dir.path().to_str().unwrap(), - "--local-fs-dir", - backup_path.to_str().unwrap(), - ]) - .current_dir(workspace_root()) - .output() - .unwrap(); - assert!( - replay.status.success(), - "{}, {}", - std::str::from_utf8(&replay.stderr).unwrap(), - std::str::from_utf8(&replay.stdout).unwrap(), - ); - - info!( - "Backup replay-verified in {} seconds.", - now.elapsed().as_secs() - ); -} - -fn wait_for_backups( - target_epoch: u64, - target_version: u64, - now: Instant, - bin_path: &Path, - metadata_cache_path: &Path, - backup_path: &Path, - trusted_waypoints: &[Waypoint], -) -> Result { - for i in 0..120 { - info!( - "{}th wait for the backup to reach epoch {}, version {}.", - i, target_epoch, target_version, - ); - let state = get_backup_storage_state(bin_path, metadata_cache_path, backup_path)?; - if state.latest_epoch_ending_epoch.is_some() - && state.latest_transaction_version.is_some() - && state.latest_state_snapshot_epoch.is_some() - && state.latest_state_snapshot_epoch.is_some() - && state.latest_epoch_ending_epoch.unwrap() >= target_epoch - && state.latest_transaction_version.unwrap() >= target_version - && state.latest_transaction_version.unwrap() - >= state.latest_state_snapshot_version.unwrap() - { - info!( - "Backup created in {} seconds. backup storage state: {}", - now.elapsed().as_secs(), - state - ); - return Ok(state.latest_state_snapshot_version.unwrap()); - } - info!("Backup storage state: {}", state); - if state.latest_transaction_version.is_some() { - // the verify should always succeed unless backup storage is completely empty. - db_backup_verify(backup_path, trusted_waypoints); - } - std::thread::sleep(Duration::from_secs(1)); - } - - bail!("Failed to create backup."); -} - -fn get_backup_storage_state( - bin_path: &Path, - metadata_cache_path: &Path, - backup_path: &Path, -) -> Result { - let output = Command::new(bin_path) - .current_dir(workspace_root()) - .args([ - "aptos-db", - "backup", - "query", - "backup-storage-state", - "--metadata-cache-dir", - metadata_cache_path.to_str().unwrap(), - "--concurrent-downloads", - "4", - "--local-fs-dir", - backup_path.to_str().unwrap(), - ]) - .output()? - .stdout; - std::str::from_utf8(&output)?.parse() -} - -pub(crate) fn db_backup( - backup_service_port: u16, - target_epoch: u64, - target_version: Version, - transaction_batch_size: usize, - state_snapshot_interval_epochs: usize, - trusted_waypoints: &[Waypoint], -) -> (TempPath, Version) { - info!("---------- running aptos db tool backup"); - let now = Instant::now(); - let bin_path = workspace_builder::get_bin("aptos-debugger"); - let metadata_cache_path1 = TempPath::new(); - let metadata_cache_path2 = TempPath::new(); - let backup_path = TempPath::new(); - - metadata_cache_path1.create_as_dir().unwrap(); - metadata_cache_path2.create_as_dir().unwrap(); - backup_path.create_as_dir().unwrap(); - - // Initialize backup storage, avoid race between the coordinator and wait_for_backups to create - // the identity file. - get_backup_storage_state(&bin_path, metadata_cache_path2.path(), backup_path.path()).unwrap(); - - // spawn the backup coordinator - let mut backup_coordinator = Command::new(bin_path.as_path()) - .current_dir(workspace_root()) - .args([ - "aptos-db", - "backup", - "continuously", - "--backup-service-address", - &format!("http://localhost:{}", backup_service_port), - "--transaction-batch-size", - &transaction_batch_size.to_string(), - "--state-snapshot-interval-epochs", - &state_snapshot_interval_epochs.to_string(), - "--metadata-cache-dir", - metadata_cache_path1.path().to_str().unwrap(), - "--concurrent-downloads", - "4", - "--local-fs-dir", - backup_path.path().to_str().unwrap(), - ]) - .spawn() - .unwrap(); - - // watch the backup storage, wait for it to reach target epoch and version - let wait_res = wait_for_backups( - target_epoch, - target_version, - now, - bin_path.as_path(), - metadata_cache_path2.path(), - backup_path.path(), - trusted_waypoints, - ); - - // start the backup compaction - let compaction = Command::new(bin_path.as_path()) - .current_dir(workspace_root()) - .args([ - "aptos-db", - "backup-maintenance", - "compact", - "--epoch-ending-file-compact-factor", - "2", - "--state-snapshot-file-compact-factor", - "2", - "--transaction-file-compact-factor", - "2", - "--metadata-cache-dir", - metadata_cache_path1.path().to_str().unwrap(), - "--concurrent-downloads", - "4", - "--local-fs-dir", - backup_path.path().to_str().unwrap(), - ]) - .output() - .unwrap(); - assert!( - compaction.status.success(), - "{}", - std::str::from_utf8(&compaction.stderr).unwrap() - ); - backup_coordinator.kill().unwrap(); - let snapshot_ver = wait_res.unwrap(); - replay_verify(backup_path.path(), trusted_waypoints); - (backup_path, snapshot_ver) -} - -pub(crate) fn db_restore( - backup_path: &Path, - db_path: &Path, - trusted_waypoints: &[Waypoint], - enable_storage_sharding: bool, - target_verion: Option, /* target version should be same as epoch ending version to start a node */ -) { - let now = Instant::now(); - let bin_path = workspace_builder::get_bin("aptos-debugger"); - let metadata_cache_path = TempPath::new(); - - metadata_cache_path.create_as_dir().unwrap(); - - let mut cmd = Command::new(bin_path.as_path()); - cmd.args(["aptos-db", "restore", "bootstrap-db"]); - trusted_waypoints.iter().for_each(|w| { - cmd.arg("--trust-waypoint"); - cmd.arg(&w.to_string()); - }); - - if enable_storage_sharding { - cmd.arg("--enable_storage_sharding"); - } - if let Some(version) = target_verion { - cmd.arg("--target-version"); - cmd.arg(&version.to_string()); - } - - let status = cmd - .args([ - "--target-db-dir", - db_path.to_str().unwrap(), - "--concurrent-downloads", - "4", - "--metadata-cache-dir", - metadata_cache_path.path().to_str().unwrap(), - "--local-fs-dir", - backup_path.to_str().unwrap(), - ]) - .current_dir(workspace_root()) - .status() - .unwrap(); - assert!(status.success(), "{}", status); - info!("Backup restored in {} seconds.", now.elapsed().as_secs()); -} diff --git a/testsuite/smoke-test/src/test_smoke_tests.rs b/testsuite/smoke-test/src/test_smoke_tests.rs deleted file mode 100644 index 6c071fd581f52..0000000000000 --- a/testsuite/smoke-test/src/test_smoke_tests.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS}, -}; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_forge::{NodeExt, Swarm}; -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; - -/// Bring up a swarm normally, then run get_bin, and bring up a VFN. -/// Previously get_bin triggered a rebuild of aptos-node, which caused issues that were only seen -/// during parallel execution of tests. -/// This test should make regressions obvious. -#[tokio::test] -async fn test_aptos_node_after_get_bin() { - let mut swarm = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - })) - .build() - .await; - let version = swarm.versions().max().unwrap(); - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - - // Before #5308 this re-compiled aptos-node and caused a panic on the vfn. - let _aptos_cli = crate::workspace_builder::get_bin("aptos"); - - let validator = validator_peer_ids[0]; - let _vfn = swarm - .add_validator_fullnode( - &version, - OverrideNodeConfig::new_with_default_base(NodeConfig::get_default_vfn_config()), - validator, - ) - .unwrap(); - - for fullnode in swarm.full_nodes_mut() { - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - fullnode - .wait_for_connectivity(Instant::now() + Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS)) - .await - .unwrap(); - } -} diff --git a/testsuite/smoke-test/src/test_utils.rs b/testsuite/smoke-test/src/test_utils.rs deleted file mode 100644 index da2258ab9ff71..0000000000000 --- a/testsuite/smoke-test/src/test_utils.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::{reconfig, LocalSwarm, NodeExt, Swarm, SwarmExt}; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::{ - transaction_builder::TransactionFactory, - types::{transaction::SignedTransaction, LocalAccount}, -}; -use rand::random; -use std::time::Duration; - -pub const MAX_CATCH_UP_WAIT_SECS: u64 = 180; // The max time we'll wait for nodes to catch up -pub const MAX_CONNECTIVITY_WAIT_SECS: u64 = 180; // The max time we'll wait for nodes to gain connectivity -pub const MAX_HEALTHY_WAIT_SECS: u64 = 120; // The max time we'll wait for nodes to become healthy - -pub async fn create_and_fund_account(swarm: &'_ mut dyn Swarm, amount: u64) -> LocalAccount { - let mut info = swarm.aptos_public_info(); - info.create_and_fund_user_account(amount).await.unwrap() -} - -/// Creates and funds two test accounts -pub async fn create_test_accounts(swarm: &mut LocalSwarm) -> (LocalAccount, LocalAccount) { - let token_amount = 1000; - let account_0 = create_and_fund_account(swarm, token_amount).await; - let account_1 = create_and_fund_account(swarm, token_amount).await; - (account_0, account_1) -} - -/// Executes transactions using the given transaction factory, client and -/// accounts. If `execute_epoch_changes` is true, also execute transactions to -/// force reconfigurations. -pub async fn execute_transactions( - swarm: &mut LocalSwarm, - client: &RestClient, - sender: &mut LocalAccount, - receiver: &LocalAccount, - execute_epoch_changes: bool, -) { - // Execute several transactions - let num_transfers = 10; - let transaction_factory = swarm.chain_info().transaction_factory(); - if execute_epoch_changes { - transfer_and_maybe_reconfig( - client, - &transaction_factory, - swarm.chain_info().root_account, - sender, - receiver, - num_transfers, - ) - .await; - } else { - for _ in 0..num_transfers { - // Execute simple transfer transactions - transfer_coins(client, &transaction_factory, sender, receiver, 1).await; - } - } - - // Always ensure that at least one reconfiguration transaction is executed - if !execute_epoch_changes { - aptos_forge::reconfig( - client, - &transaction_factory, - swarm.chain_info().root_account, - ) - .await; - } -} - -/// Executes transactions and waits for all nodes to catch up -pub async fn execute_transactions_and_wait( - swarm: &mut LocalSwarm, - client: &RestClient, - sender: &mut LocalAccount, - receiver: &LocalAccount, - epoch_changes: bool, -) { - execute_transactions(swarm, client, sender, receiver, epoch_changes).await; - wait_for_all_nodes(swarm).await; -} - -pub async fn transfer_coins_non_blocking( - client: &RestClient, - transaction_factory: &TransactionFactory, - sender: &mut LocalAccount, - receiver: &LocalAccount, - amount: u64, -) -> SignedTransaction { - let txn = sender.sign_with_transaction_builder(transaction_factory.payload( - aptos_stdlib::aptos_coin_transfer(receiver.address(), amount), - )); - - client.submit(&txn).await.unwrap(); - txn -} - -pub async fn transfer_coins( - client: &RestClient, - transaction_factory: &TransactionFactory, - sender: &mut LocalAccount, - receiver: &LocalAccount, - amount: u64, -) -> SignedTransaction { - let txn = - transfer_coins_non_blocking(client, transaction_factory, sender, receiver, amount).await; - - client.wait_for_signed_transaction(&txn).await.unwrap(); - - txn -} - -pub async fn transfer_and_maybe_reconfig( - client: &RestClient, - transaction_factory: &TransactionFactory, - root_account: &mut LocalAccount, - sender: &mut LocalAccount, - receiver: &LocalAccount, - num_transfers: usize, -) { - for _ in 0..num_transfers { - // Reconfigurations have a 20% chance of being executed - if random::() % 5 == 0 { - reconfig(client, transaction_factory, root_account).await; - } - - transfer_coins(client, transaction_factory, sender, receiver, 1).await; - } -} - -pub async fn assert_balance(client: &RestClient, account: &LocalAccount, balance: u64) { - let on_chain_balance = client - .get_account_balance(account.address()) - .await - .unwrap() - .into_inner(); - - assert_eq!(on_chain_balance.get(), balance); -} - -/// This helper function creates 3 new accounts, mints funds, transfers funds -/// between the accounts and verifies that these operations succeed. -pub async fn check_create_mint_transfer(swarm: &mut LocalSwarm) { - check_create_mint_transfer_node(swarm, 0).await; -} - -/// This helper function creates 3 new accounts, mints funds, transfers funds -/// between the accounts and verifies that these operations succeed on one specific validator. -pub async fn check_create_mint_transfer_node(swarm: &mut LocalSwarm, idx: usize) { - let client = swarm.validators().nth(idx).unwrap().rest_client(); - - // Create account 0, mint 10 coins and check balance - let transaction_factory = TransactionFactory::new(swarm.chain_id()); - let mut info = swarm.aptos_public_info_for_node(idx); - let mut account_0 = info.create_and_fund_user_account(10).await.unwrap(); - assert_balance(&client, &account_0, 10).await; - - // Create account 1, mint 1 coin, transfer 3 coins from account 0 to 1, check balances - let account_1 = info.create_and_fund_user_account(1).await.unwrap(); - transfer_coins(&client, &transaction_factory, &mut account_0, &account_1, 3).await; - - assert_balance(&client, &account_0, 7).await; - assert_balance(&client, &account_1, 4).await; - - // Create account 2, mint 15 coins and check balance - let account_2 = info.create_and_fund_user_account(15).await.unwrap(); - assert_balance(&client, &account_2, 15).await; -} - -/// Waits for all nodes to catch up -pub async fn wait_for_all_nodes(swarm: &mut LocalSwarm) { - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); -} - -/// This module provides useful functions for operating, handling and managing -/// AptosSwarm instances. It is particularly useful for working with tests that -/// require a SmokeTestEnvironment, as it provides a generic interface across -/// AptosSwarms, regardless of if the swarm is a validator swarm, validator full -/// node swarm, or a public full node swarm. -#[cfg(test)] -pub mod swarm_utils { - use aptos_config::config::{NodeConfig, SecureBackend, WaypointConfig}; - use aptos_secure_storage::{KVStorage, Storage}; - use aptos_types::waypoint::Waypoint; - - pub fn insert_waypoint(node_config: &mut NodeConfig, waypoint: Waypoint) { - node_config.base.waypoint = WaypointConfig::FromConfig(waypoint); - - let f = |backend: &SecureBackend| { - let mut storage: Storage = backend.into(); - storage - .set(aptos_global_constants::WAYPOINT, waypoint) - .expect("Unable to write waypoint"); - storage - .set(aptos_global_constants::GENESIS_WAYPOINT, waypoint) - .expect("Unable to write waypoint"); - }; - let backend = &node_config.consensus.safety_rules.backend; - f(backend); - } -} diff --git a/testsuite/smoke-test/src/transaction.rs b/testsuite/smoke-test/src/transaction.rs deleted file mode 100644 index c58e36400dc65..0000000000000 --- a/testsuite/smoke-test/src/transaction.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::smoke_test_environment::new_local_swarm_with_aptos; -use aptos_cached_packages::aptos_stdlib; -use aptos_forge::Swarm; -use aptos_keygen::KeyGen; -use aptos_rest_client::{ - aptos_api_types::{EntryFunctionPayload, TransactionPayload}, - Transaction, -}; -use aptos_sdk::{ - crypto::{PrivateKey, SigningKey}, - types::transaction::{authenticator::AuthenticationKey, SignedTransaction}, -}; - -// TODO: debug me and re-enable the test! -#[ignore] -#[tokio::test] -async fn test_external_transaction_signer() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let mut info = swarm.aptos_public_info(); - - // generate key pair - let mut key_gen = KeyGen::from_os_rng(); - let private_key = key_gen.generate_ed25519_private_key(); - let public_key = private_key.public_key(); - - // create transfer parameters - let sender_auth_key = AuthenticationKey::ed25519(&public_key); - let sender_address = sender_auth_key.account_address(); - info.create_user_account(&public_key).await.unwrap(); - // TODO(Gas): double check if this is correct - info.mint(sender_address, 10_000_000).await.unwrap(); - - let receiver = info.random_account(); - info.create_user_account(receiver.public_key()) - .await - .unwrap(); - // TODO(Gas): double check if this is correct - info.mint(receiver.address(), 1_000_000).await.unwrap(); - - let amount = 1_000_000; - let test_gas_unit_price = 1; - // TODO(Gas): double check if this is correct - let test_max_gas_amount = 1_000_000; - - // prepare transfer transaction - let test_sequence_number = info - .client() - .get_account(sender_address) - .await - .unwrap() - .into_inner() - .sequence_number; - - let unsigned_txn = info - .transaction_factory() - .payload(aptos_stdlib::aptos_coin_transfer( - receiver.address(), - amount, - )) - .sender(sender_address) - .sequence_number(test_sequence_number) - .max_gas_amount(test_max_gas_amount) - .gas_unit_price(test_gas_unit_price) - .build(); - - assert_eq!(unsigned_txn.sender(), sender_address); - - // sign the transaction with the private key - let signature = private_key.sign(&unsigned_txn).unwrap(); - - // submit the transaction - let txn = SignedTransaction::new(unsigned_txn.clone(), public_key, signature); - info.client().submit_and_wait(&txn).await.unwrap(); - - // query the transaction and check it contains the same values as requested - let txn = info - .client() - .get_account_transactions(sender_address, Some(test_sequence_number), Some(1)) - .await - .unwrap() - .into_inner() - .into_iter() - .next() - .unwrap(); - - match txn { - Transaction::UserTransaction(user_txn) => { - assert_eq!(*user_txn.request.sender.inner(), sender_address); - assert_eq!(user_txn.request.sequence_number.0, test_sequence_number); - assert_eq!(user_txn.request.gas_unit_price.0, test_gas_unit_price); - assert_eq!(user_txn.request.max_gas_amount.0, test_max_gas_amount); - - if let TransactionPayload::EntryFunctionPayload(EntryFunctionPayload { - function: _, - type_arguments: _, - arguments, - }) = user_txn.request.payload - { - assert_eq!( - arguments - .into_iter() - .map(|arg| arg.as_str().unwrap().to_owned()) - .collect::>(), - vec![receiver.address().to_hex_literal(), amount.to_string(),] - ); - } else { - panic!("unexpected transaction playload") - } - }, - _ => panic!("Query should get user transaction"), - } -} diff --git a/testsuite/smoke-test/src/txn_broadcast.rs b/testsuite/smoke-test/src/txn_broadcast.rs deleted file mode 100644 index 816ba1f989aa4..0000000000000 --- a/testsuite/smoke-test/src/txn_broadcast.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::SwarmBuilder, - test_utils::{ - assert_balance, create_and_fund_account, transfer_coins, MAX_CATCH_UP_WAIT_SECS, - MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS, - }, -}; -use aptos_config::config::{NodeConfig, OverrideNodeConfig}; -use aptos_forge::{NodeExt, Swarm, SwarmExt}; -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; - -/// Checks txn goes through consensus even if the local validator is not creating proposals. -/// This behavior should be true with both mempool and quorum store. -#[tokio::test] -async fn test_txn_broadcast() { - let mut swarm = SwarmBuilder::new_local(4) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - })) - .build() - .await; - let transaction_factory = swarm.chain_info().transaction_factory(); - let version = swarm.versions().max().unwrap(); - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - - let validator = validator_peer_ids[1]; - let vfn = swarm - .add_validator_fullnode( - &version, - OverrideNodeConfig::new_with_default_base(NodeConfig::get_default_vfn_config()), - validator, - ) - .unwrap(); - - for fullnode in swarm.full_nodes_mut() { - fullnode - .wait_until_healthy(Instant::now() + Duration::from_secs(MAX_HEALTHY_WAIT_SECS)) - .await - .unwrap(); - fullnode - .wait_for_connectivity(Instant::now() + Duration::from_secs(MAX_CONNECTIVITY_WAIT_SECS)) - .await - .unwrap(); - } - - // Setup accounts - let mut account_0 = create_and_fund_account(&mut swarm, 10).await; - let account_1 = create_and_fund_account(&mut swarm, 10).await; - - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_CATCH_UP_WAIT_SECS)) - .await - .unwrap(); - - // set up vfn_client - let vfn_client = swarm.full_node(vfn).unwrap().rest_client(); - - // set up validator_client. proposals not sent from this validator. txn should still go through. - let validator_client = swarm.validator(validator).unwrap().rest_client(); - validator_client - .set_failpoint( - "consensus::send::proposal".to_string(), - "return".to_string(), - ) - .await - .unwrap(); - - // send to validator_client - let txn = transfer_coins( - &validator_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&validator_client, &account_0, 9).await; - assert_balance(&validator_client, &account_1, 11).await; - vfn_client.wait_for_signed_transaction(&txn).await.unwrap(); - assert_balance(&vfn_client, &account_0, 9).await; - assert_balance(&vfn_client, &account_1, 11).await; - - // send to vfn_client - transfer_coins( - &vfn_client, - &transaction_factory, - &mut account_0, - &account_1, - 1, - ) - .await; - - assert_balance(&validator_client, &account_0, 8).await; - assert_balance(&validator_client, &account_1, 12).await; - assert_balance(&vfn_client, &account_0, 8).await; - assert_balance(&vfn_client, &account_1, 12).await; -} diff --git a/testsuite/smoke-test/src/txn_emitter.rs b/testsuite/smoke-test/src/txn_emitter.rs deleted file mode 100644 index 3023f89c70cff..0000000000000 --- a/testsuite/smoke-test/src/txn_emitter.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - smoke_test_environment::{new_local_swarm_with_aptos, SwarmBuilder}, - test_utils::create_and_fund_account, -}; -use anyhow::ensure; -use aptos_forge::{ - args::TransactionTypeArg, emitter::NumAccountsMode, EmitJobMode, EmitJobRequest, EntryPoints, - NodeExt, Result, Swarm, TransactionType, TxnEmitter, TxnStats, WorkflowProgress, -}; -use aptos_sdk::{transaction_builder::TransactionFactory, types::PeerId}; -use rand::{rngs::OsRng, SeedableRng}; -use std::{sync::Arc, time::Duration}; - -pub async fn generate_traffic( - swarm: &mut dyn Swarm, - nodes: &[PeerId], - duration: Duration, - gas_price: u64, - transaction_mix_per_phase: Vec>, -) -> Result { - ensure!(gas_price > 0, "gas_price is required to be non zero"); - - let rng = SeedableRng::from_rng(OsRng)?; - let validator_clients = swarm - .validators() - .filter(|v| nodes.contains(&v.peer_id())) - .map(|n| n.rest_client()) - .collect::>(); - let mut emit_job_request = EmitJobRequest::default(); - let chain_info = swarm.chain_info(); - let transaction_factory = - TransactionFactory::new(chain_info.chain_id).with_gas_unit_price(gas_price); - let emitter = TxnEmitter::new(transaction_factory, rng); - - emit_job_request = emit_job_request - .rest_clients(validator_clients) - .gas_price(gas_price) - .expected_gas_per_txn(1000000) - .max_gas_per_txn(2000000) - .coordination_delay_between_instances(Duration::from_secs(1)) - .transaction_mix_per_phase(transaction_mix_per_phase) - .mode(EmitJobMode::ConstTps { tps: 20 }); - emitter - .emit_txn_for_with_stats(chain_info.root_account, emit_job_request, duration, 3) - .await -} - -#[ignore] -#[tokio::test] -async fn test_txn_emmitter() { - let mut swarm = new_local_swarm_with_aptos(1).await; - - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - - let txn_stat = generate_traffic( - &mut swarm, - &all_validators, - Duration::from_secs(20), - 100, - vec![ - // vec![( - // TransactionType::AccountGeneration { - // add_created_accounts_to_pool: true, - // max_account_working_set: 1_000_000, - // creation_balance: 1_000_000, - // }, - // 20, - // )], - // vec![ - // (TransactionTypeArg::CoinTransfer.materialize_default(), 20), - // // // commenting this out given it consistently fails smoke test - // // // and it seems to be called only from `test_txn_emmitter` - // ( - // TransactionType::PublishPackage { - // use_account_pool: false, - // }, - // 20, - // ), - // ], - vec![ - ( - TransactionTypeArg::NoOp.materialize( - 100, - false, - WorkflowProgress::when_done_default(), - ), - 20, - ), - ( - TransactionType::CallCustomModules { - entry_point: EntryPoints::MakeOrChangeTable { - offset: 0, - count: 60, - }, - num_modules: 1, - use_account_pool: false, - }, - 20, - ), - ], - // vec![( - // TransactionType::CallCustomModules { - // entry_point: EntryPoints::TokenV1MintAndStoreNFTSequential, - // num_modules: 1, - // use_account_pool: false, - // }, - // 20, - // )], - // vec![( - // TransactionType::CallCustomModules { - // entry_point: EntryPoints::TokenV1MintAndTransferNFTParallel, - // num_modules: 1, - // use_account_pool: false, - // }, - // 20, - // )], - // vec![( - // TransactionType::CallCustomModules { - // entry_point: EntryPoints::TokenV1MintAndTransferNFTSequential, - // num_modules: 1, - // use_account_pool: false, - // }, - // 20, - // )], - ], - ) - .await - .unwrap(); - println!("{:?}", txn_stat.rate()); - // assert some much smaller number than expected, so it doesn't fail under contention - assert!(txn_stat.submitted > 30); - assert!(txn_stat.committed > 30); -} - -#[tokio::test] -async fn test_txn_emmitter_with_high_pending_latency() { - let mut swarm = SwarmBuilder::new_local(1) - .with_aptos() - .with_init_config(Arc::new(|_, conf, _| { - conf.api.failpoints_enabled = true; - conf.consensus.pipeline_backpressure.truncate(1); - conf.consensus.pipeline_backpressure[0].max_txns_from_block_to_execute = Some(2); - conf.consensus.pipeline_backpressure[0].back_pressure_pipeline_latency_limit_ms = 0; - })) - .build() - .await; - - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - - let txn_stat = generate_traffic( - &mut swarm, - &all_validators, - Duration::from_secs(20), - 100, - vec![vec![( - TransactionType::CallCustomModules { - entry_point: EntryPoints::SmartTablePicture { - length: 128 * 1024, - num_points_per_txn: 256, - }, - num_modules: 1, - use_account_pool: false, - }, - 1, - )]], - ) - .await - .unwrap(); - assert!(txn_stat.submitted > 30); -} - -#[tokio::test] -async fn test_txn_emmitter_low_funds() { - let mut swarm = new_local_swarm_with_aptos(1).await; - let account_1 = create_and_fund_account(&mut swarm, 5705100).await; - - let transaction_type = TransactionType::CallCustomModules { - entry_point: EntryPoints::Nop, - num_modules: 1, - use_account_pool: false, - }; - - let rng = SeedableRng::from_rng(OsRng).unwrap(); - let validator_clients = swarm - .validators() - .map(|n| n.rest_client()) - .collect::>(); - let chain_info = swarm.chain_info(); - let transaction_factory = TransactionFactory::new(chain_info.chain_id).with_gas_unit_price(100); - let emitter = TxnEmitter::new(transaction_factory, rng); - - let emit_job_request = EmitJobRequest::default() - .rest_clients(validator_clients) - .gas_price(100) - .expected_max_txns(2000) - .expected_gas_per_txn(3) - .init_gas_price_multiplier(1) - .init_max_gas_per_txn(20000) - .max_gas_per_txn(3) - .num_accounts_mode(NumAccountsMode::TransactionsPerAccount(5)) - .transaction_type(transaction_type) - .mode(EmitJobMode::MaxLoad { - mempool_backlog: 10, - }); - - let txn_stat = emitter - .emit_txn_for_with_stats(&account_1, emit_job_request, Duration::from_secs(10), 3) - .await - .unwrap(); - - assert!(txn_stat.submitted > 30); -} diff --git a/testsuite/smoke-test/src/upgrade.rs b/testsuite/smoke-test/src/upgrade.rs deleted file mode 100644 index 8668c47775661..0000000000000 --- a/testsuite/smoke-test/src/upgrade.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - aptos::move_test_helpers, smoke_test_environment::SwarmBuilder, - test_utils::check_create_mint_transfer, workspace_builder, workspace_builder::workspace_root, -}; -use aptos_crypto::ValidCryptoMaterialStringExt; -use aptos_forge::Swarm; -use aptos_gas_algebra::GasQuantity; -use aptos_gas_schedule::{AptosGasParameters, InitialGasSchedule, ToOnChainGasSchedule}; -use aptos_release_builder::{ - components::{ - feature_flags::{FeatureFlag, Features}, - framework::FrameworkReleaseConfig, - gas::generate_gas_upgrade_proposal, - ExecutionMode, Proposal, ProposalMetadata, - }, - ReleaseEntry, -}; -use aptos_temppath::TempPath; -use aptos_types::on_chain_config::{FeatureFlag as AptosFeatureFlag, OnChainConsensusConfig}; -use std::{fs, path::PathBuf, process::Command, sync::Arc}; - -// Ignored. This is redundant with the forge compat test but this test is easier to run locally and -// could help debug much faster -#[ignore] -// TODO: currently fails when quorum store is enabled by hard-coding. Investigate why. -#[tokio::test] -/// This test verifies the flow of aptos framework upgrade process. -/// i.e: The network will be alive after applying the new aptos framework release. -async fn test_upgrade_flow() { - // prebuild tools. - let aptos_cli = workspace_builder::get_bin("aptos"); - - let num_nodes = 5; - let (mut env, _cli, _) = SwarmBuilder::new_local(num_nodes) - .with_aptos_testnet() - .build_with_cli(0) - .await; - - let url = env.aptos_public_info().url().to_string(); - let private_key = env - .aptos_public_info() - .root_account() - .private_key() - .to_encoded_string() - .unwrap(); - - // Bump the limit in gas schedule - // TODO: Replace this logic with aptos-gas - let mut gas_parameters = AptosGasParameters::initial(); - gas_parameters.vm.txn.max_transaction_size_in_bytes = GasQuantity::new(100_000_000); - - let gas_schedule = aptos_types::on_chain_config::GasScheduleV2 { - feature_version: aptos_gas_schedule::LATEST_GAS_FEATURE_VERSION, - entries: gas_parameters - .to_on_chain_gas_schedule(aptos_gas_schedule::LATEST_GAS_FEATURE_VERSION), - }; - - let (_, update_gas_script) = - generate_gas_upgrade_proposal(true, &gas_schedule, true, "".to_owned().into_bytes()) - .unwrap() - .pop() - .unwrap(); - - let gas_script_path = TempPath::new(); - let mut gas_script_path = gas_script_path.path().to_path_buf(); - gas_script_path.set_extension("move"); - fs::write(gas_script_path.as_path(), update_gas_script).unwrap(); - - let framework_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework"); - - assert!(Command::new(aptos_cli.as_path()) - .current_dir(workspace_root()) - .args(&vec![ - "move", - "run-script", - "--script-path", - gas_script_path.to_str().unwrap(), - "--framework-local-dir", - framework_path.as_os_str().to_str().unwrap(), - "--sender-account", - "0xA550C18", - "--url", - url.as_str(), - "--private-key", - private_key.as_str(), - "--assume-yes", - ]) - .output() - .unwrap() - .status - .success()); - env.aptos_public_info() - .root_account() - .increment_sequence_number(); - - let upgrade_scripts_folder = TempPath::new(); - upgrade_scripts_folder.create_as_dir().unwrap(); - - let config = aptos_release_builder::ReleaseConfig { - name: "Default".to_string(), - remote_endpoint: None, - proposals: vec![ - Proposal { - execution_mode: ExecutionMode::RootSigner, - name: "framework".to_string(), - metadata: ProposalMetadata::default(), - update_sequence: vec![ReleaseEntry::Framework(FrameworkReleaseConfig { - bytecode_version: 6, // TODO: remove explicit bytecode version from sources - git_hash: None, - })], - }, - Proposal { - execution_mode: ExecutionMode::RootSigner, - name: "gas".to_string(), - metadata: ProposalMetadata::default(), - update_sequence: vec![ReleaseEntry::DefaultGas], - }, - Proposal { - execution_mode: ExecutionMode::RootSigner, - name: "feature_flags".to_string(), - metadata: ProposalMetadata::default(), - update_sequence: vec![ - ReleaseEntry::FeatureFlag(Features { - enabled: AptosFeatureFlag::default_features() - .into_iter() - .map(FeatureFlag::from) - .collect(), - disabled: vec![], - }), - ReleaseEntry::Consensus(OnChainConsensusConfig::default()), - ], - }, - ], - }; - - config - .generate_release_proposal_scripts(upgrade_scripts_folder.path()) - .unwrap(); - let mut scripts = walkdir::WalkDir::new(upgrade_scripts_folder.path()) - .sort_by_file_name() - .into_iter() - .filter_map(|path| match path { - Ok(path) => { - if path.path().ends_with("move") { - Some(path.path().to_path_buf()) - } else { - None - } - }, - Err(_) => None, - }) - .collect::>(); - - scripts.sort(); - - let framework_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("aptos-move") - .join("framework") - .join("aptos-framework"); - - for path in scripts.iter() { - assert!(Command::new(aptos_cli.as_path()) - .current_dir(workspace_root()) - .args(&vec![ - "move", - "run-script", - "--script-path", - path.to_str().unwrap(), - "--framework-local-dir", - framework_path.as_os_str().to_str().unwrap(), - "--sender-account", - "0xA550C18", - "--url", - url.as_str(), - "--private-key", - private_key.as_str(), - "--assume-yes", - ]) - .output() - .unwrap() - .status - .success()); - - env.aptos_public_info() - .root_account() - .increment_sequence_number(); - } - - //TODO: Make sure gas schedule is indeed updated by the tool. - - // Test the module publishing workflow - let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - let base_path_v1 = base_dir.join("src/aptos/package_publish_modules_v1/"); - - move_test_helpers::publish_package(&mut env.aptos_public_info(), base_path_v1) - .await - .unwrap(); - - check_create_mint_transfer(&mut env).await; -} - -// This test is intentionally disabled because it's taking ~500s to execute right now. -// The main reason is that compilation of scripts takes a bit too long, as the Move compiler will need -// to repeatedly compile all the aptos framework pacakges as dependency -// -#[ignore] -#[tokio::test(flavor = "multi_thread")] -async fn test_release_validate_tool_multi_step() { - let (mut env, _, _) = SwarmBuilder::new_local(1) - .with_init_genesis_stake(Arc::new(|_, genesis_stake_amount| { - // make sure we have quorum - *genesis_stake_amount = 2000000000000000; - })) - .with_init_genesis_config(Arc::new(|genesis_config| { - genesis_config.allow_new_validators = true; - genesis_config.voting_duration_secs = 30; - genesis_config.voting_power_increase_limit = 50; - genesis_config.epoch_duration_secs = 4; - })) - .build_with_cli(2) - .await; - let config = aptos_release_builder::ReleaseConfig::default(); - - let root_key = TempPath::new(); - root_key.create_as_file().unwrap(); - let mut root_key_path = root_key.path().to_path_buf(); - root_key_path.set_extension("key"); - - std::fs::write( - root_key_path.as_path(), - bcs::to_bytes(&env.chain_info().root_account().private_key()).unwrap(), - ) - .unwrap(); - - let network_config = aptos_release_builder::validate::NetworkConfig { - endpoint: url::Url::parse(&env.chain_info().rest_api_url).unwrap(), - root_key_path, - validator_account: env.validators().last().unwrap().peer_id(), - validator_key: env - .validators() - .last() - .unwrap() - .account_private_key() - .as_ref() - .unwrap() - .private_key(), - framework_git_rev: None, - }; - - network_config.mint_to_validator().await.unwrap(); - - aptos_release_builder::validate::validate_config(config, network_config) - .await - .unwrap(); - - let root_account = env.aptos_public_info().root_account().address(); - // Test the module publishing workflow - let sequence_number = env - .aptos_public_info() - .client() - .get_account(root_account) - .await - .unwrap() - .inner() - .sequence_number; - env.aptos_public_info() - .root_account() - .set_sequence_number(sequence_number); - - let base_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - let base_path_v1 = base_dir.join("src/aptos/package_publish_modules_v1/"); - - move_test_helpers::publish_package(&mut env.aptos_public_info(), base_path_v1) - .await - .unwrap(); - - check_create_mint_transfer(&mut env).await; -} diff --git a/testsuite/smoke-test/src/upgrade_multi_step_test_metadata.txt b/testsuite/smoke-test/src/upgrade_multi_step_test_metadata.txt deleted file mode 100644 index b29deaaa23477..0000000000000 --- a/testsuite/smoke-test/src/upgrade_multi_step_test_metadata.txt +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title":"Reduce min stake required by 1 APT", - "description":"Reduce min stake required to join the validator set by 1 APT", - "source_code_url":"https://github.com/aptos-labs/ait3-governance/proposals/sources/0-update-min-stake.move", - "discussion_url":"https://forum.aptoslabs.com/proposals/upgrade-potato-module-v3" -} diff --git a/testsuite/smoke-test/src/utils.rs b/testsuite/smoke-test/src/utils.rs deleted file mode 100644 index 26dd623295a37..0000000000000 --- a/testsuite/smoke-test/src/utils.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_rest_client::Client; -use aptos_types::on_chain_config::OnChainConsensusConfig; -use move_core_types::language_storage::CORE_CODE_ADDRESS; - -pub(crate) async fn get_current_version(rest_client: &Client) -> u64 { - rest_client - .get_ledger_information() - .await - .unwrap() - .inner() - .version -} - -pub(crate) async fn get_current_consensus_config(rest_client: &Client) -> OnChainConsensusConfig { - bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap() -} diff --git a/testsuite/smoke-test/src/validator_txns.rs b/testsuite/smoke-test/src/validator_txns.rs deleted file mode 100644 index 78774a0db1ec8..0000000000000 --- a/testsuite/smoke-test/src/validator_txns.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - smoke_test_environment::SwarmBuilder, - utils::{get_current_consensus_config, get_current_version}, -}; -use aptos_forge::{NodeExt, SwarmExt}; -use aptos_logger::{debug, info}; -use aptos_rest_client::Client; -use aptos_types::on_chain_config::OnChainRandomnessConfig; -use futures::future::join_all; -use std::{sync::Arc, time::Duration}; - -/// Chain should not be blocked by failing validator txns. -#[tokio::test] -async fn dummy_validator_txns() { - let swarm = SwarmBuilder::new_local(4) - .with_init_config(Arc::new(|_, config, _| { - config.api.failpoints_enabled = true; - })) - .with_init_genesis_config(Arc::new(move |conf| { - // start with randomness enabled. - conf.consensus_config.enable_validator_txns(); - conf.randomness_config_override = Some(OnChainRandomnessConfig::default_enabled()); - })) - .with_aptos() - .build() - .await; - - swarm - .wait_for_all_nodes_to_catchup_to_epoch(2, Duration::from_secs(60)) - .await - .expect("Epoch 2 taking too long to come!"); - - let validator_clients: Vec = - swarm.validators().map(|node| node.rest_client()).collect(); - - let consensus_config = get_current_consensus_config(&validator_clients[0]).await; - debug!("consensus_config={:?}", consensus_config); - - info!("Update all validators to start proposing a ValidatorTransaction::Dummy1 in their proposals."); - let tasks = validator_clients - .iter() - .map(|client| { - client.set_failpoint( - "mixed_payload_client::extra_test_only_vtxns".to_string(), - "return".to_string(), - ) - }) - .collect::>(); - let aptos_results = join_all(tasks).await; - println!("aptos_results={:?}", aptos_results); - - let version_milestone_0 = get_current_version(&validator_clients[0]).await; - let version_milestone_1 = version_milestone_0 + 10; - info!("Current version: {}, the chain should tolerate potentially invalid vtxns and survive until version {}.", version_milestone_0, version_milestone_1); - swarm - .wait_for_all_nodes_to_catchup_to_version(version_milestone_1, Duration::from_secs(60)) - .await - .expect("milestone 1 taking too long"); -} diff --git a/testsuite/smoke-test/src/workspace_builder.rs b/testsuite/smoke-test/src/workspace_builder.rs deleted file mode 100644 index 68dd69768c8eb..0000000000000 --- a/testsuite/smoke-test/src/workspace_builder.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! DO NOT USE OUTSIDE OF SMOKE_TEST CRATE -//! -//! This utility is to only be used inside of smoke test. - -use aptos_forge::cargo_build_common_args; -use aptos_logger::prelude::*; -use once_cell::sync::Lazy; -use std::{env, path::PathBuf, process::Command}; - -const WORKSPACE_BUILD_ERROR_MSG: &str = r#" - Unable to build all workspace binaries. Cannot continue running tests. - - Try running 'cargo build --release --all --bins --exclude aptos-node' yourself. -"#; - -// Global flag indicating if all binaries in the workspace have been built. -static WORKSPACE_BUILT: Lazy = Lazy::new(|| { - info!("Building project binaries"); - let mut args = cargo_build_common_args(); - args.append(&mut vec!["--all", "--bins", "--exclude", "aptos-node"]); - - let cargo_build = Command::new("cargo") - .current_dir(workspace_root()) - .args(&args) - .output() - .expect(WORKSPACE_BUILD_ERROR_MSG); - if cargo_build.status.success() { - info!("Finished building project binaries"); - true - } else { - error!("Output: {:?}", cargo_build); - false - } -}); - -// Path to top level workspace -pub fn workspace_root() -> PathBuf { - let mut path = build_dir(); - while !path.ends_with("target") { - path.pop(); - } - path.pop(); - path -} - -// Path to the directory where build artifacts live. -//TODO maybe add an Environment Variable which points to built binaries -fn build_dir() -> PathBuf { - env::current_exe() - .ok() - .map(|mut path| { - path.pop(); - if path.ends_with("deps") { - path.pop(); - } - path - }) - .expect("Can't find the build directory. Cannot continue running tests") -} - -// Path to a specified binary -pub fn get_bin>(bin_name: S) -> PathBuf { - assert_ne!( - "aptos-node", - bin_name.as_ref(), - "aptos-node must be built and used via local swarm cargo_build_aptos_node" - ); - - // We have to check to see if the workspace is built first to ensure that the binaries we're - // testing are up to date. - if !*WORKSPACE_BUILT { - panic!("{}", WORKSPACE_BUILD_ERROR_MSG); - } - - let bin_name = bin_name.as_ref(); - let bin_path = build_dir().join(format!("{}{}", bin_name, env::consts::EXE_SUFFIX)); - - // If the binary doesn't exist then either building them failed somehow or the supplied binary - // name doesn't match any binaries this workspace can produce. - if !bin_path.exists() { - panic!( - "Can't find binary '{}' in expected path {:?}", - bin_name, bin_path - ); - } - - bin_path -} diff --git a/testsuite/testcases/Cargo.toml b/testsuite/testcases/Cargo.toml deleted file mode 100644 index ac3b806041b58..0000000000000 --- a/testsuite/testcases/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -[package] -name = "aptos-testcases" -description = "Aptos compatibility tests" -version = "0.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos = { workspace = true, features = ["fuzzing"] } -aptos-config = { workspace = true } -aptos-forge = { workspace = true } -aptos-genesis = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-keygen = { workspace = true } -aptos-logger = { workspace = true } -aptos-move-examples = { workspace = true } -aptos-release-builder = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-sdk = { workspace = true } -aptos-temppath = { workspace = true } -aptos-types = { workspace = true } -bcs = { workspace = true } -csv = { workspace = true } -futures = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -rand = { workspace = true } -reqwest = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } - -[dev-dependencies] -assert_approx_eq = { workspace = true } - -[[test]] -name = "forge-local-compatibility" -harness = false - -[[test]] -name = "forge-local-performance" -harness = false diff --git a/testsuite/testcases/src/compatibility_test.rs b/testsuite/testcases/src/compatibility_test.rs deleted file mode 100644 index d27ff4392b0f8..0000000000000 --- a/testsuite/testcases/src/compatibility_test.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{batch_update, generate_traffic}; -use anyhow::bail; -use aptos_forge::{NetworkContext, NetworkTest, Result, SwarmExt, Test}; -use aptos_logger::info; -use tokio::{runtime::Runtime, time::Duration}; - -pub struct SimpleValidatorUpgrade; - -impl Test for SimpleValidatorUpgrade { - fn name(&self) -> &'static str { - "compatibility::simple-validator-upgrade" - } -} - -impl NetworkTest for SimpleValidatorUpgrade { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = Runtime::new()?; - - // Get the different versions we're testing with - let (old_version, new_version) = { - let mut versions = ctx.swarm().versions().collect::>(); - versions.sort(); - if versions.len() != 2 { - bail!("exactly two different versions needed to run compat test"); - } - - (versions[0].clone(), versions[1].clone()) - }; - - let msg = format!( - "Compatibility test results for {} ==> {} (PR)", - old_version, new_version - ); - info!("{}", msg); - ctx.report.report_text(msg); - - // Split the swarm into 2 parts - if ctx.swarm().validators().count() < 4 { - bail!("compat test requires >= 4 validators"); - } - let all_validators = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - let mut first_batch = all_validators.clone(); - let second_batch = first_batch.split_off(first_batch.len() / 2); - let first_node = first_batch.pop().unwrap(); - let duration = Duration::from_secs(30); - - let msg = format!( - "1. Check liveness of validators at old version: {}", - old_version - ); - info!("{}", msg); - ctx.report.report_text(msg); - - // Generate some traffic - let txn_stat = generate_traffic(ctx, &all_validators, duration)?; - ctx.report - .report_txn_stats(format!("{}::liveness-check", self.name()), &txn_stat); - - // Update the first Validator - let msg = format!( - "2. Upgrading first Validator to new version: {}", - new_version - ); - info!("{}", msg); - ctx.report.report_text(msg); - runtime.block_on(batch_update(ctx, &[first_node], &new_version))?; - - // Generate some traffic - let txn_stat = generate_traffic(ctx, &[first_node], duration)?; - ctx.report.report_txn_stats( - format!("{}::single-validator-upgrade", self.name()), - &txn_stat, - ); - - // Update the rest of the first batch - let msg = format!( - "3. Upgrading rest of first batch to new version: {}", - new_version - ); - info!("{}", msg); - ctx.report.report_text(msg); - runtime.block_on(batch_update(ctx, &first_batch, &new_version))?; - - // Generate some traffic - let txn_stat = generate_traffic(ctx, &first_batch, duration)?; - ctx.report.report_txn_stats( - format!("{}::half-validator-upgrade", self.name()), - &txn_stat, - ); - - ctx.swarm().fork_check()?; - - // Update the second batch - let msg = format!("4. upgrading second batch to new version: {}", new_version); - info!("{}", msg); - ctx.report.report_text(msg); - runtime.block_on(batch_update(ctx, &second_batch, &new_version))?; - - // Generate some traffic - let txn_stat = generate_traffic(ctx, &second_batch, duration)?; - ctx.report.report_txn_stats( - format!("{}::rest-validator-upgrade", self.name()), - &txn_stat, - ); - - let msg = "5. check swarm health".to_string(); - info!("{}", msg); - ctx.report.report_text(msg); - ctx.swarm().fork_check()?; - ctx.report.report_text(format!( - "Compatibility test for {} ==> {} passed", - old_version, new_version - )); - - Ok(()) - } -} diff --git a/testsuite/testcases/src/consensus_reliability_tests.rs b/testsuite/testcases/src/consensus_reliability_tests.rs deleted file mode 100644 index 99a8d6f18ed41..0000000000000 --- a/testsuite/testcases/src/consensus_reliability_tests.rs +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use anyhow::{anyhow, bail, Context}; -use aptos_forge::{ - test_utils::consensus_utils::{ - test_consensus_fault_tolerance, FailPointFailureInjection, NodeState, - }, - NetworkContext, NetworkTest, Result, Swarm, SwarmExt, Test, TestReport, -}; -use aptos_logger::{info, warn}; -use rand::Rng; -use std::{collections::HashSet, time::Duration}; -use tokio::runtime::Runtime; - -pub struct ChangingWorkingQuorumTest { - pub min_tps: usize, - pub always_healthy_nodes: usize, - pub max_down_nodes: usize, - pub num_large_validators: usize, - pub add_execution_delay: bool, - /// Check that every given number of seconds all nodes make progress, without any failures. - /// It is good to make epoch length and this duration not be multiples of one another, - /// to test different timings - pub check_period_s: usize, -} - -impl Test for ChangingWorkingQuorumTest { - fn name(&self) -> &'static str { - "changing working quorum test" - } -} - -impl NetworkLoadTest for ChangingWorkingQuorumTest { - fn setup(&self, ctx: &mut NetworkContext) -> Result { - // because we are doing failure testing, we should be sending - // traffic to nodes that are alive. - if ctx.swarm().full_nodes().count() > 0 { - Ok(LoadDestination::AllFullnodes) - } else if self.always_healthy_nodes > 0 { - Ok(LoadDestination::Peers( - ctx.swarm() - .validators() - .take(self.always_healthy_nodes) - .map(|v| v.peer_id()) - .collect(), - )) - } else { - Ok(LoadDestination::AllValidators) - } - } - - fn test( - &self, - swarm: &mut dyn Swarm, - _report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - let runtime = Runtime::new().unwrap(); - - let validators = swarm.get_validator_clients_with_names(); - - let num_validators = validators.len(); - - let num_always_healthy = self.always_healthy_nodes; - // largest number of (small) nodes that can fail simultaneously, while we have enough for quorum - let can_fail_for_quorum = - (self.num_large_validators * 10 + (num_validators - self.num_large_validators) - 1) / 3; - // In our test, maximum number of nodes that we will fail simultaneously. - let max_fail_in_test = std::cmp::min( - std::cmp::min(self.max_down_nodes, num_validators - num_always_healthy), - can_fail_for_quorum, - ); - // On every cycle, we will fail this many next nodes, and make this many previous nodes healthy again. - let cycle_offset = max_fail_in_test / 4 + 1; - let num_destinations = if swarm.full_nodes().count() > 0 { - swarm.full_nodes().count() - } else if num_always_healthy > 0 { - num_always_healthy - } else { - swarm.validators().count() - }; - // Function that returns set of down nodes in a given cycle. - let down_indices_f = move |cycle: usize| -> HashSet { - let mut down_indices: HashSet<_> = (0..max_fail_in_test) - .map(|i| { - num_always_healthy - + (cycle * cycle_offset + i) % (num_validators - num_always_healthy) - }) - .collect(); - // If there is a limited number of destinations and they may fail, we ensure at least one is up. - if num_always_healthy == 0 - && max_fail_in_test >= num_destinations - && down_indices.contains(&0) - && down_indices.contains(&(num_destinations - 1)) - { - // Replace one of the destinations with the next sequential index. - down_indices.remove(&((cycle * cycle_offset) % num_destinations)); - // Notice the check will never pass with num_always_healthy > 0, so we don't consider it. - down_indices.insert((cycle * cycle_offset + max_fail_in_test) % num_validators); - }; - down_indices - }; - info!( - "Always healthy {} nodes, every cycle having {} nodes out of {} down, rotating {} each cycle, expecting first {} validators to have 10x larger stake", - num_always_healthy, max_fail_in_test, num_validators, cycle_offset, self.num_large_validators); - - let slow_allowed_lagging = if self.add_execution_delay { - runtime.block_on(async { - let mut rng = rand::thread_rng(); - let mut slow_allowed_lagging = HashSet::new(); - for (index, (name, validator)) in - validators.iter().enumerate().skip(num_always_healthy) - { - let sleep_time = rng.gen_range(20, 500); - if sleep_time > 100 { - slow_allowed_lagging.insert(index); - } - let name = name.clone(); - - validator - .set_failpoint( - "aptos_vm::execution::block_metadata".to_string(), - format!("sleep({})", sleep_time), - ) - .await - .map_err(|e| { - anyhow!( - "set_failpoint to remove execution delay on {} failed, {:?}", - name, - e - ) - })?; - } - Ok::, anyhow::Error>(slow_allowed_lagging) - })? - } else { - HashSet::new() - }; - - let min_tps = self.min_tps; - let check_period_s = self.check_period_s; - - runtime.block_on(test_consensus_fault_tolerance( - swarm, - duration.as_secs() as usize / self.check_period_s, - self.check_period_s as f32, - 1, - Box::new(FailPointFailureInjection::new(Box::new(move |cycle, part| { - if part == 0 { - let down_indices = down_indices_f(cycle); - info!("For cycle {} down nodes: {:?}", cycle, down_indices); - // For all down nodes, we are going to drop all messages we receive. - ( - down_indices.iter().flat_map(|i| { - [ - ( - *i, - "consensus::process::any".to_string(), - "return".to_string(), - ), - ] - }).collect(), - true, - ) - } else { - (vec![], false) - } - }))), - Box::new(move |cycle, _, _, _, cycle_end, cycle_start| { - // we group nodes into 3 groups: - // - active - nodes we expect to be making progress, and doing so together. we check wery strict rule of min(cycle_end) vs max(cycle_start) - // - allowed_lagging - nodes that are allowed to not be up-to-date to the tip of the chain, but are required to be making individual progress. - // We treat all nodes that were recently down as those (while state-sync is given time to catch-up), or nodes that - // were added slowness into execution via add_execution_delay param. - // - down - nodes that are cut-off from the rest of the nodes, and so shouldn't be seeing any progress. There should be no progress - // on the ordered certificates, but since we are only seeing committed ones, we allow for only minimal progress there, for - // what they already have in the buffer. - - let down_indices = down_indices_f(cycle); - let recently_down_indices = if cycle > 0 { down_indices_f(cycle - 1) } else { HashSet::new() }; - fn split(all: Vec, down_indices: &HashSet, allowed_lagging_indices: &HashSet) -> (Vec<(usize, NodeState)>, Vec<(usize, NodeState)>, Vec) { - let (down, not_down): (Vec<_>, Vec<_>) = all.into_iter().enumerate().partition(|(idx, _state)| down_indices.contains(idx)); - let (allowed_lagging, active) = not_down.into_iter().partition(|(idx, _state)| allowed_lagging_indices.contains(idx)); - (down, allowed_lagging, active.into_iter().map(|(_idx, state)| state).collect()) - } - - let allowed_lagging = recently_down_indices.union(&slow_allowed_lagging).cloned().collect::>(); - let (cycle_end_down, cycle_end_allowed_lagging, cycle_end_active) = split(cycle_end, &down_indices, &allowed_lagging); - let (cycle_start_down, cycle_start_allowed_lagging, cycle_start_active) = split(cycle_start, &down_indices, &allowed_lagging); - - // Make sure that every active node is making progress, so we compare min(cycle_end) vs max(cycle_start) - let (cycle_end_min_epoch, cycle_end_min_round) = cycle_end_active.iter().map(|s| (s.epoch, s.round)).min().unwrap(); - let (cycle_start_max_epoch, cycle_start_max_round) = cycle_start_active.iter().map(|s| (s.epoch, s.round)).max().unwrap(); - - let epochs_progress = cycle_end_min_epoch as i64 - cycle_start_max_epoch as i64; - let round_progress = cycle_end_min_round as i64 - cycle_start_max_round as i64; - - let transaction_progress = cycle_end_active.iter().map(|s| s.version).min().unwrap() as i64 - - cycle_start_active.iter().map(|s| s.version).max().unwrap() as i64; - - if transaction_progress < (min_tps * check_period_s) as i64 { - bail!( - "not enough progress with active consensus, only {} transactions, expected >= {} ({} TPS). Down indices {:?}, cycle start active: {:?}. cycle end active: {:?}", - transaction_progress, - min_tps * check_period_s, - min_tps, - down_indices, - cycle_start_active, - cycle_end_active, - ); - } - if epochs_progress < 0 || (epochs_progress == 0 && round_progress < (check_period_s / 2) as i64) { - bail!( - "not enough progress with active consensus, only {} epochs and {} rounds, expectd >= {}", - epochs_progress, - round_progress, - check_period_s / 2, - ); - } - - // Make sure that allowed_lagging nodes are making progress - for ((node_idx, cycle_end_state), (node_idx_p, cycle_start_state)) in cycle_end_allowed_lagging.iter().zip(cycle_start_allowed_lagging.iter()) { - assert_eq!(node_idx, node_idx_p, "{:?} {:?}", cycle_end_allowed_lagging, cycle_start_allowed_lagging); - let transaction_progress = cycle_end_state.version as i64 - cycle_start_state.version as i64; - if transaction_progress < (min_tps * check_period_s) as i64 { - bail!( - "not enough individual progress on allowed lagging node ({}), only {} transactions, expected >= {} ({} TPS)", - node_idx, - transaction_progress, - min_tps * check_period_s, - min_tps, - ); - } - - let epochs_progress = cycle_end_state.epoch as i64 - cycle_start_state.epoch as i64; - let round_progress = cycle_end_state.round as i64 - cycle_start_state.round as i64; - if epochs_progress < 0 || (epochs_progress == 0 && round_progress < (check_period_s / 2) as i64) { - bail!( - "not enough individual progress on allowed lagging node ({}), only {} epochs and {} rounds, expectd >= {}. Transaction progress was {}.", - node_idx, - epochs_progress, - round_progress, - check_period_s / 2, - transaction_progress, - ); - } - } - - // Make sure down nodes don't make progress: - for ((node_idx, cycle_end_state), (node_idx_p, cycle_start_state)) in cycle_end_down.iter().zip(cycle_start_down.iter()) { - assert_eq!(node_idx, node_idx_p, "{:?} {:?}", cycle_end_down, cycle_start_down); - if cycle_end_state.round > cycle_start_state.round + 3 { - // if we just failed the node, some progress can happen due to pipeline in consensus, - // or buffer of received messages in state sync - if recently_down_indices.contains(node_idx) { - bail!("progress on down node {} from ({}, {}) to ({}, {})", node_idx, cycle_start_state.epoch, cycle_start_state.round, cycle_end_state.epoch, cycle_end_state.round); - } else { - warn!("progress on down node {} immediatelly after turning off from ({}, {}) to ({}, {})", node_idx, cycle_start_state.epoch, cycle_start_state.round, cycle_end_state.epoch, cycle_end_state.round) - } - } - } - - Ok(()) - }), - false, - true, - )).context("test_consensus_fault_tolerance failed")?; - - // undo slowing down. - if self.add_execution_delay { - runtime.block_on(async { - for (name, validator) in validators.iter().skip(num_always_healthy) { - let name = name.clone(); - - validator - .set_failpoint( - "aptos_vm::execution::block_metadata".to_string(), - "off".to_string(), - ) - .await - .map_err(|e| { - anyhow!( - "set_failpoint to remove execution delay on {} failed, {:?}", - name, - e - ) - })?; - } - Ok::<(), anyhow::Error>(()) - })?; - } - Ok(()) - } -} - -impl NetworkTest for ChangingWorkingQuorumTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/dag_onchain_enable_test.rs b/testsuite/testcases/src/dag_onchain_enable_test.rs deleted file mode 100644 index 09af1aee62ba4..0000000000000 --- a/testsuite/testcases/src/dag_onchain_enable_test.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{generate_onchain_config_blob, NetworkLoadTest}; -use anyhow::Ok; -use aptos::test::CliTestFramework; -use aptos_forge::{NetworkTest, NodeExt, SwarmExt, Test}; -use aptos_logger::info; -use aptos_sdk::bcs; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, - on_chain_config::{ - ConsensusAlgorithmConfig, DagConsensusConfigV1, OnChainConsensusConfig, ValidatorTxnConfig, - }, -}; -use std::time::Duration; -use tokio::runtime::Runtime; - -const MAX_NODE_LAG_SECS: u64 = 360; - -pub struct DagOnChainEnableTest {} - -impl Test for DagOnChainEnableTest { - fn name(&self) -> &'static str { - "dag reconfig enable test" - } -} - -impl NetworkLoadTest for DagOnChainEnableTest { - fn test( - &self, - swarm: &mut dyn aptos_forge::Swarm, - _report: &mut aptos_forge::TestReport, - duration: std::time::Duration, - ) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - - let faucet_endpoint: reqwest::Url = "http://localhost:8081".parse().unwrap(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut cli = runtime.block_on(async { - CliTestFramework::new( - swarm.validators().next().unwrap().rest_api_endpoint(), - faucet_endpoint, - /*num_cli_accounts=*/ 0, - ) - .await - }); - - std::thread::sleep(duration / 3); - - runtime.block_on(async { - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.chain_info().root_account().private_key().clone(), - swarm.chain_info().root_account().address(), - ); - - let current_consensus_config: OnChainConsensusConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - assert!(matches!(current_consensus_config, OnChainConsensusConfig::V3 { .. })); - - // Change to V2 - let new_consensus_config = OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::DAG(DagConsensusConfigV1::default()), - vtxn: ValidatorTxnConfig::default_disabled(), - }; - - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_onchain_config_blob(&bcs::to_bytes(&new_consensus_config).unwrap()) - ); - - cli.run_script_with_default_framework(root_cli_index, &update_consensus_config_script) - .await - })?; - - std::thread::sleep(duration / 3); - - let initial_consensus_config = runtime.block_on(async { - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.chain_info().root_account().private_key().clone(), - swarm.chain_info().root_account().address(), - ); - - let current_consensus_config: OnChainConsensusConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - assert!(matches!(current_consensus_config, OnChainConsensusConfig::V3 { .. })); - - // Change to DAG - let new_consensus_config = OnChainConsensusConfig::V3 { - alg: ConsensusAlgorithmConfig::DAG(DagConsensusConfigV1::default()), - vtxn: ValidatorTxnConfig::default_disabled(), - }; - - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_onchain_config_blob(&bcs::to_bytes(&new_consensus_config).unwrap()) - ); - - cli.run_script_with_default_framework(root_cli_index, &update_consensus_config_script) - .await?; - - Ok(current_consensus_config) - })?; - - std::thread::sleep(duration / 3); - - runtime.block_on(async { - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.chain_info().root_account().private_key().clone(), - swarm.chain_info().root_account().address(), - ); - - let current_consensus_config: OnChainConsensusConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - assert!(matches!(current_consensus_config, OnChainConsensusConfig::V3 { .. })); - - // Change back to initial - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_onchain_config_blob(&bcs::to_bytes(&initial_consensus_config).unwrap()) - ); - - cli.run_script_with_default_framework(root_cli_index, &update_consensus_config_script) - .await - })?; - - // Wait for all nodes to synchronize and stabilize. - info!("Waiting for the validators to be synchronized."); - runtime.block_on(async { - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_NODE_LAG_SECS)) - .await - })?; - - Ok(()) - } -} - -impl NetworkTest for DagOnChainEnableTest { - fn run(&self, ctx: &mut aptos_forge::NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/data/four_region_link_stats.csv b/testsuite/testcases/src/data/four_region_link_stats.csv deleted file mode 100644 index 64175bc2a8eca..0000000000000 --- a/testsuite/testcases/src/data/four_region_link_stats.csv +++ /dev/null @@ -1,13 +0,0 @@ -sending_region,receiving_region,bitrate_bps,avgrtt -gcp--us-central1,aws--eu-west-1,300000000,103.435 -gcp--us-central1,aws--ap-northeast-1,300000000,133.996 -gcp--us-central1,aws--sa-east-1,300000000,145.483 -aws--sa-east-1,gcp--us-central1,300000000,145.703 -aws--sa-east-1,aws--eu-west-1,300000000,176.894 -aws--sa-east-1,aws--ap-northeast-1,300000000,255.289 -aws--eu-west-1,gcp--us-central1,300000000,104.169 -aws--eu-west-1,aws--sa-east-1,300000000,176.813 -aws--eu-west-1,aws--ap-northeast-1,300000000,198.555 -aws--ap-northeast-1,gcp--us-central1,300000000,128.999 -aws--ap-northeast-1,aws--eu-west-1,300000000,198.539 -aws--ap-northeast-1,aws--sa-east-1,300000000,255.323 \ No newline at end of file diff --git a/testsuite/testcases/src/data/two_region_link_stats.csv b/testsuite/testcases/src/data/two_region_link_stats.csv deleted file mode 100644 index cc22397cf27f1..0000000000000 --- a/testsuite/testcases/src/data/two_region_link_stats.csv +++ /dev/null @@ -1,3 +0,0 @@ -sending_region,receiving_region,bitrate_bps,avgrtt -aws--sa-east-1,aws--ap-northeast-1,300000000,255.289 -aws--ap-northeast-1,aws--sa-east-1,300000000,255.323 \ No newline at end of file diff --git a/testsuite/testcases/src/forge_setup_test.rs b/testsuite/testcases/src/forge_setup_test.rs deleted file mode 100644 index 6a69224bb1bc4..0000000000000 --- a/testsuite/testcases/src/forge_setup_test.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::generate_traffic; -use anyhow::Context; -use aptos_config::config::OverrideNodeConfig; -use aptos_forge::{NetworkContext, NetworkTest, Result, Test}; -use aptos_logger::info; -use rand::{ - rngs::{OsRng, StdRng}, - seq::IteratorRandom, - Rng, SeedableRng, -}; -use std::{thread, time::Duration}; -use tokio::runtime::Runtime; - -const STATE_SYNC_VERSION_COUNTER_NAME: &str = "aptos_state_sync_version"; - -pub struct ForgeSetupTest; - -impl Test for ForgeSetupTest { - fn name(&self) -> &'static str { - "verify_forge_setup" - } -} - -impl NetworkTest for ForgeSetupTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let mut rng = StdRng::from_seed(OsRng.gen()); - let runtime = Runtime::new().unwrap(); - - let swarm = ctx.swarm(); - - let all_fullnodes = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - let fullnode_id = all_fullnodes.iter().choose(&mut rng).unwrap(); - - info!("Pick one fullnode to stop and wipe"); - let fullnode = swarm.full_node_mut(*fullnode_id).unwrap(); - runtime.block_on(fullnode.clear_storage())?; - runtime.block_on(fullnode.start())?; - - let fullnode = swarm.full_node(*fullnode_id).unwrap(); - let fullnode_name = fullnode.name(); - - for _ in 0..10 { - let query = format!( - "{}{{instance=\"{}\",type=\"synced\"}}", - STATE_SYNC_VERSION_COUNTER_NAME, &fullnode_name - ); - info!("PromQL Query {}", query); - let r = runtime.block_on(swarm.query_metrics(&query, None, None))?; - let ivs = r.as_instant().unwrap(); - for iv in ivs { - info!( - "{}: {}", - STATE_SYNC_VERSION_COUNTER_NAME, - iv.sample().value() - ); - } - thread::sleep(std::time::Duration::from_secs(5)); - } - - // add some PFNs and send load to them - let mut pfns = Vec::new(); - let num_pfns = 5; - for _ in 0..num_pfns { - let pfn_version = swarm.versions().max().unwrap(); - let pfn_node_config = - OverrideNodeConfig::new_with_default_base(swarm.get_default_pfn_node_config()); - let pfn_peer_id = - runtime.block_on(swarm.add_full_node(&pfn_version, pfn_node_config))?; - - let _pfn = swarm.full_node(pfn_peer_id).context("pfn not found")?; - pfns.push(pfn_peer_id); - } - - let duration = Duration::from_secs(10 * num_pfns); - let txn_stat = generate_traffic(ctx, &pfns, duration)?; - - ctx.report - .report_txn_stats(self.name().to_string(), &txn_stat); - - Ok(()) - } -} diff --git a/testsuite/testcases/src/framework_upgrade.rs b/testsuite/testcases/src/framework_upgrade.rs deleted file mode 100644 index 4cbe6e4f5c01c..0000000000000 --- a/testsuite/testcases/src/framework_upgrade.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{batch_update, generate_traffic}; -use anyhow::bail; -use aptos_forge::{ - NetworkContext, NetworkTest, Result, SwarmExt, Test, DEFAULT_ROOT_PRIV_KEY, FORGE_KEY_SEED, -}; -use aptos_keygen::KeyGen; -use aptos_logger::info; -use aptos_sdk::crypto::{ed25519::Ed25519PrivateKey, PrivateKey}; -use aptos_temppath::TempPath; -use aptos_types::transaction::authenticator::AuthenticationKey; -use tokio::{runtime::Runtime, time::Duration}; - -pub struct FrameworkUpgrade; - -impl Test for FrameworkUpgrade { - fn name(&self) -> &'static str { - "framework_upgrade::framework-upgrade" - } -} - -impl NetworkTest for FrameworkUpgrade { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = Runtime::new()?; - - // Get the different versions we're testing with - let (old_version, new_version) = { - let mut versions = ctx.swarm().versions().collect::>(); - versions.sort(); - if versions.len() != 2 { - bail!("exactly two different versions needed to run compat test"); - } - - (versions[0].clone(), versions[1].clone()) - }; - - let all_validators = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - - let msg = format!( - "Compatibility test results for {} ==> {} (PR)", - old_version, new_version - ); - info!("{}", msg); - ctx.report.report_text(msg); - - // Update the validators to latest version. - let msg = format!("Upgrade the nodes to version: {}", new_version); - info!("{}", msg); - ctx.report.report_text(msg); - runtime.block_on(batch_update(ctx, &all_validators, &new_version))?; - - ctx.swarm().fork_check()?; - - // Apply the framework release bundle. - let root_key_path = TempPath::new(); - root_key_path.create_as_file()?; - std::fs::write( - root_key_path.path(), - bcs::to_bytes(&Ed25519PrivateKey::try_from( - hex::decode(DEFAULT_ROOT_PRIV_KEY)?.as_ref(), - )?)?, - )?; - - let starting_seed_in_decimal = i64::from_str_radix(FORGE_KEY_SEED, 16)?; - - // Initialize keyGen to get validator private keys. We uses the same seed in the test - // driver as in the genesis script so that the validator keys are deterministic. - let mut seed_slice = [0u8; 32]; - let seed_in_decimal = starting_seed_in_decimal; - let seed_in_hex_string = format!("{seed_in_decimal:0>64x}"); - - hex::decode_to_slice(seed_in_hex_string, &mut seed_slice)?; - - let mut keygen = KeyGen::from_seed(seed_slice); - let validator_key = keygen.generate_ed25519_private_key(); - let validator_account = - AuthenticationKey::ed25519(&validator_key.public_key()).account_address(); - - let network_info = aptos_release_builder::validate::NetworkConfig { - endpoint: ctx.swarm().validators().last().unwrap().rest_api_endpoint(), - root_key_path: root_key_path.path().to_path_buf(), - validator_account, - validator_key, - framework_git_rev: None, - }; - - runtime.block_on(network_info.mint_to_validator())?; - - let release_config = aptos_release_builder::current_release_config(); - - runtime.block_on(aptos_release_builder::validate::validate_config( - release_config.clone(), - network_info, - ))?; - - // Update the sequence number for the root account - let root_account = ctx.swarm().chain_info().root_account().address(); - // Test the module publishing workflow - let sequence_number = runtime - .block_on( - ctx.swarm() - .chain_info() - .rest_client() - .get_account(root_account), - ) - .unwrap() - .inner() - .sequence_number; - ctx.swarm() - .chain_info() - .root_account() - .set_sequence_number(sequence_number); - - // Generate some traffic - let duration = Duration::from_secs(30); - let txn_stat = generate_traffic(ctx, &all_validators, duration)?; - ctx.report.report_txn_stats( - format!("{}::full-framework-upgrade", self.name()), - &txn_stat, - ); - - ctx.swarm().fork_check()?; - - let msg = "5. check swarm health".to_string(); - info!("{}", msg); - ctx.report.report_text(msg); - ctx.swarm().fork_check()?; - ctx.report.report_text(format!( - "Compatibility test for {} ==> {} passed", - old_version, new_version - )); - - Ok(()) - } -} diff --git a/testsuite/testcases/src/fullnode_reboot_stress_test.rs b/testsuite/testcases/src/fullnode_reboot_stress_test.rs deleted file mode 100644 index a2d8702e402a8..0000000000000 --- a/testsuite/testcases/src/fullnode_reboot_stress_test.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{NetworkContext, NetworkTest, Result, Swarm, Test, TestReport}; -use rand::{seq::SliceRandom, thread_rng}; -use std::time::Duration; -use tokio::{runtime::Runtime, time::Instant}; - -pub struct FullNodeRebootStressTest; - -impl Test for FullNodeRebootStressTest { - fn name(&self) -> &'static str { - "fullnode reboot stress test" - } -} - -impl NetworkLoadTest for FullNodeRebootStressTest { - fn setup(&self, _ctx: &mut NetworkContext) -> Result { - Ok(LoadDestination::AllFullnodes) - } - - fn test( - &self, - swarm: &mut dyn Swarm, - _report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - let start = Instant::now(); - let runtime = Runtime::new().unwrap(); - - let all_fullnodes = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - - let mut rng = thread_rng(); - - while start.elapsed() < duration { - let fullnode_to_reboot = swarm - .full_node_mut(*all_fullnodes.choose(&mut rng).unwrap()) - .unwrap(); - runtime.block_on(async { fullnode_to_reboot.stop().await })?; - runtime.block_on(async { fullnode_to_reboot.start().await })?; - std::thread::sleep(Duration::from_secs(10)); - } - - Ok(()) - } -} - -impl NetworkTest for FullNodeRebootStressTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/lib.rs b/testsuite/testcases/src/lib.rs deleted file mode 100644 index a620211693382..0000000000000 --- a/testsuite/testcases/src/lib.rs +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub mod compatibility_test; -pub mod consensus_reliability_tests; -pub mod dag_onchain_enable_test; -pub mod forge_setup_test; -pub mod framework_upgrade; -pub mod fullnode_reboot_stress_test; -pub mod load_vs_perf_benchmark; -pub mod modifiers; -pub mod multi_region_network_test; -pub mod network_bandwidth_test; -pub mod network_loss_test; -pub mod network_partition_test; -pub mod partial_nodes_down_test; -pub mod performance_test; -pub mod public_fullnode_performance; -pub mod quorum_store_onchain_enable_test; -pub mod reconfiguration_test; -pub mod state_sync_performance; -pub mod three_region_simulation_test; -pub mod twin_validator_test; -pub mod two_traffics_test; -pub mod validator_join_leave_test; -pub mod validator_reboot_stress_test; - -use anyhow::Context; -use aptos_forge::{ - prometheus_metrics::{fetch_latency_breakdown, LatencyBreakdown}, - EmitJobRequest, NetworkContext, NetworkTest, NodeExt, Result, Swarm, SwarmExt, Test, - TestReport, TxnEmitter, TxnStats, Version, -}; -use aptos_logger::info; -use aptos_rest_client::Client as RestClient; -use aptos_sdk::{transaction_builder::TransactionFactory, types::PeerId}; -use futures::future::join_all; -use rand::{rngs::StdRng, SeedableRng}; -use std::{ - fmt::Write, - time::{Duration, Instant, SystemTime, UNIX_EPOCH}, -}; -use tokio::runtime::Runtime; - -const WARMUP_DURATION_FRACTION: f32 = 0.07; -const COOLDOWN_DURATION_FRACTION: f32 = 0.04; - -async fn batch_update( - ctx: &mut NetworkContext<'_>, - validators_to_update: &[PeerId], - version: &Version, -) -> Result<()> { - for validator in validators_to_update { - ctx.swarm().upgrade_validator(*validator, version).await?; - } - - ctx.swarm().health_check().await?; - let deadline = Instant::now() + Duration::from_secs(60); - for validator in validators_to_update { - ctx.swarm() - .validator_mut(*validator) - .unwrap() - .wait_until_healthy(deadline) - .await?; - } - - Ok(()) -} - -pub fn create_emitter_and_request( - swarm: &mut dyn Swarm, - mut emit_job_request: EmitJobRequest, - nodes: &[PeerId], - rng: StdRng, -) -> Result<(TxnEmitter, EmitJobRequest)> { - // as we are loading nodes, use higher client timeout - let client_timeout = Duration::from_secs(30); - - let chain_info = swarm.chain_info(); - let transaction_factory = TransactionFactory::new(chain_info.chain_id); - let emitter = TxnEmitter::new(transaction_factory, rng); - - emit_job_request = - emit_job_request.rest_clients(swarm.get_clients_for_peers(nodes, client_timeout)); - Ok((emitter, emit_job_request)) -} - -pub fn traffic_emitter_runtime() -> Result { - let runtime = aptos_runtimes::spawn_named_runtime("emitter".into(), Some(64)); - Ok(runtime) -} - -pub fn generate_traffic( - ctx: &mut NetworkContext<'_>, - nodes: &[PeerId], - duration: Duration, -) -> Result { - let emit_job_request = ctx.emit_job.clone(); - let rng = SeedableRng::from_rng(ctx.core().rng())?; - let (emitter, emit_job_request) = - create_emitter_and_request(ctx.swarm(), emit_job_request, nodes, rng)?; - - let rt = traffic_emitter_runtime()?; - let stats = rt.block_on(emitter.emit_txn_for( - ctx.swarm().chain_info().root_account, - emit_job_request, - duration, - ))?; - - Ok(stats) -} - -pub enum LoadDestination { - AllNodes, - AllValidators, - AllFullnodes, - // Send to AllFullnodes, if any exist, otherwise to AllValidators - FullnodesOtherwiseValidators, - Peers(Vec), -} - -impl LoadDestination { - fn get_destination_nodes(self, swarm: &mut dyn Swarm) -> Vec { - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - let all_fullnodes = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - - match self { - LoadDestination::AllNodes => [&all_validators[..], &all_fullnodes[..]].concat(), - LoadDestination::AllValidators => all_validators, - LoadDestination::AllFullnodes => all_fullnodes, - LoadDestination::FullnodesOtherwiseValidators => { - if all_fullnodes.is_empty() { - all_validators - } else { - all_fullnodes - } - }, - LoadDestination::Peers(peers) => peers, - } - } -} - -pub trait NetworkLoadTest: Test { - fn setup(&self, _ctx: &mut NetworkContext) -> Result { - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - // Load is started before this function is called, and stops after this function returns. - // Expected duration is passed into this function, expecting this function to take that much - // time to finish. How long this function takes will dictate how long the actual test lasts. - fn test( - &self, - _swarm: &mut dyn Swarm, - _report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - std::thread::sleep(duration); - Ok(()) - } - - fn finish(&self, _ctx: &mut NetworkContext) -> Result<()> { - Ok(()) - } -} - -impl NetworkTest for dyn NetworkLoadTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = Runtime::new().unwrap(); - let start_timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - let (start_version, _) = runtime - .block_on(ctx.swarm().get_client_with_newest_ledger_version()) - .context("no clients replied for start version")?; - let emit_job_request = ctx.emit_job.clone(); - let rng = SeedableRng::from_rng(ctx.core().rng())?; - let duration = ctx.global_duration; - let stats_by_phase = self.network_load_test( - ctx, - emit_job_request, - duration, - WARMUP_DURATION_FRACTION, - COOLDOWN_DURATION_FRACTION, - rng, - )?; - - let phased = stats_by_phase.len() > 1; - for (phase, phase_stats) in stats_by_phase.iter().enumerate() { - let test_name = if phased { - format!("{}_phase_{}", self.name(), phase) - } else { - self.name().to_string() - }; - ctx.report - .report_txn_stats(test_name, &phase_stats.emitter_stats); - ctx.report.report_text(format!( - "Latency breakdown for phase {}: {:?}", - phase, - phase_stats - .latency_breakdown - .keys() - .into_iter() - .map(|slice| { - let slice_samples = phase_stats.latency_breakdown.get_samples(&slice); - format!( - "{:?}: max: {:.3}, avg: {:.3}", - slice, - slice_samples.max_sample(), - slice_samples.avg_sample() - ) - }) - .collect::>() - )); - } - - let end_timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - let (end_version, _) = runtime - .block_on(ctx.swarm().get_client_with_newest_ledger_version()) - .context("no clients replied for end version")?; - - self.finish(ctx).context("finish NetworkLoadTest ")?; - - for phase_stats in stats_by_phase.into_iter() { - ctx.check_for_success( - &phase_stats.emitter_stats, - phase_stats.actual_duration, - &phase_stats.latency_breakdown, - start_timestamp as i64, - end_timestamp as i64, - start_version, - end_version, - ) - .context("check for success")?; - } - - Ok(()) - } -} - -impl dyn NetworkLoadTest { - pub fn network_load_test( - &self, - ctx: &mut NetworkContext, - emit_job_request: EmitJobRequest, - duration: Duration, - warmup_duration_fraction: f32, - cooldown_duration_fraction: f32, - rng: StdRng, - ) -> Result> { - let destination = self.setup(ctx).context("setup NetworkLoadTest")?; - let nodes_to_send_load_to = destination.get_destination_nodes(ctx.swarm()); - - // Generate some traffic - - let (mut emitter, emit_job_request) = - create_emitter_and_request(ctx.swarm(), emit_job_request, &nodes_to_send_load_to, rng) - .context("create emitter")?; - - let rt = traffic_emitter_runtime()?; - let clients = ctx - .swarm() - .get_clients_for_peers(&nodes_to_send_load_to, Duration::from_secs(10)); - - let mut stats_tracking_phases = emit_job_request.get_num_phases(); - assert!(stats_tracking_phases > 0 && stats_tracking_phases != 2); - if stats_tracking_phases == 1 { - stats_tracking_phases = 3; - } - - info!("Starting emitting txns for {}s", duration.as_secs()); - let mut job = rt - .block_on(emitter.start_job( - ctx.swarm().chain_info().root_account, - emit_job_request, - stats_tracking_phases, - )) - .context("start emitter job")?; - - let total_start = PhaseTimingStart::now(); - - let warmup_duration = duration.mul_f32(warmup_duration_fraction); - let cooldown_duration = duration.mul_f32(cooldown_duration_fraction); - let test_duration = duration - warmup_duration - cooldown_duration; - let phase_duration = test_duration.div_f32((stats_tracking_phases - 2) as f32); - - job = rt.block_on(job.periodic_stat_forward(warmup_duration, 60)); - info!("{}s warmup finished", warmup_duration.as_secs()); - - let mut phase_timing = Vec::new(); - let mut phase_start_network_state = Vec::new(); - let test_start = Instant::now(); - for i in 0..stats_tracking_phases - 2 { - phase_start_network_state.push(rt.block_on(NetworkState::new(&clients))); - job.start_next_phase(); - - if i > 0 { - info!( - "Starting test phase {} out of {}", - i, - stats_tracking_phases - 2, - ); - } - let phase_start = PhaseTimingStart::now(); - - let join_stats = rt.spawn(job.periodic_stat_forward(phase_duration, 60)); - self.test(ctx.swarm, ctx.report, phase_duration) - .context("test NetworkLoadTest")?; - job = rt.block_on(join_stats).context("join stats")?; - phase_timing.push(phase_start.elapsed()); - } - let actual_test_duration = test_start.elapsed(); - info!( - "{}s test finished after {}s", - test_duration.as_secs(), - actual_test_duration.as_secs() - ); - - phase_start_network_state.push(rt.block_on(NetworkState::new(&clients))); - job.start_next_phase(); - let cooldown_start = Instant::now(); - - let cooldown_used = cooldown_start.elapsed(); - if cooldown_used < cooldown_duration { - job = rt.block_on(job.periodic_stat_forward(cooldown_duration - cooldown_used, 60)); - } - info!("{}s cooldown finished", cooldown_duration.as_secs()); - - let total_timing = total_start.elapsed(); - info!( - "Emitting txns ran for {} secs(from {} to {}), stopping job...", - duration.as_secs(), - total_timing.start_unixtime_s, - total_timing.end_unixtime_s, - ); - let stats_by_phase = rt.block_on(job.stop_job()); - - info!("Stopped job"); - info!("Warmup stats: {}", stats_by_phase[0].rate()); - - let mut stats: Option = None; - let mut stats_by_phase_filtered = Vec::new(); - for i in 0..stats_tracking_phases - 2 { - let next_i = i + 1; - let cur = &stats_by_phase[next_i]; - info!("Test stats [test phase {}]: {}", i, cur.rate()); - stats = if let Some(previous) = stats { - Some(&previous + cur) - } else { - Some(cur.clone()) - }; - let latency_breakdown = rt.block_on(fetch_latency_breakdown( - ctx.swarm(), - phase_timing[i].start_unixtime_s, - phase_timing[i].end_unixtime_s, - ))?; - info!( - "Latency breakdown details for phase {}: from {} to {}: {:?}", - i, - phase_timing[i].start_unixtime_s, - phase_timing[i].end_unixtime_s, - latency_breakdown - ); - stats_by_phase_filtered.push(LoadTestPhaseStats { - emitter_stats: cur.clone(), - actual_duration: phase_timing[i].duration, - phase_start_unixtime_s: phase_timing[i].start_unixtime_s, - phase_end_unixtime_s: phase_timing[i].end_unixtime_s, - ledger_transactions: NetworkState::ledger_transactions( - &phase_start_network_state[i], - &phase_start_network_state[next_i], - ), - latency_breakdown, - }); - } - info!("Cooldown stats: {}", stats_by_phase.last().unwrap().rate()); - - Ok(stats_by_phase_filtered) - } -} - -struct PhaseTimingStart { - now: Instant, - unixtime_s: u64, -} - -impl PhaseTimingStart { - fn now() -> PhaseTimingStart { - let now = Instant::now(); - let unixtime_s = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(); - PhaseTimingStart { now, unixtime_s } - } - - fn elapsed(&self) -> PhaseTiming { - PhaseTiming { - duration: self.now.elapsed(), - start_unixtime_s: self.unixtime_s, - end_unixtime_s: SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(), - } - } -} - -struct PhaseTiming { - duration: Duration, - start_unixtime_s: u64, - end_unixtime_s: u64, -} - -pub(crate) struct NetworkState { - max_version_and_height: Option<(u64, u64)>, -} - -impl NetworkState { - pub async fn new(clients: &[RestClient]) -> NetworkState { - let max_version_and_height = - join_all(clients.iter().map(|client| client.get_ledger_information())) - .await - .into_iter() - .filter(|r| r.is_ok()) - .map(|r| r.unwrap().into_inner()) - .map(|s| (s.version, s.block_height)) - .max(); - NetworkState { - max_version_and_height, - } - } - - pub fn ledger_transactions(start: &NetworkState, end: &NetworkState) -> u64 { - if let (Some((end_version, end_height)), Some((start_version, start_height))) = - (end.max_version_and_height, start.max_version_and_height) - { - (end_version - end_height * 2) - (start_version - start_height * 2) - } else { - 0 - } - } -} - -pub struct LoadTestPhaseStats { - pub emitter_stats: TxnStats, - pub actual_duration: Duration, - pub phase_start_unixtime_s: u64, - pub phase_end_unixtime_s: u64, - pub ledger_transactions: u64, - pub latency_breakdown: LatencyBreakdown, -} - -pub struct CompositeNetworkTest { - // Wrapper tests - their setup and finish methods are called, before the test ones. - // TODO don't know how to make this array, and have forge/main.rs work - pub wrappers: Vec>, - // This is the main test, return values from this test are used in setup, and - // only it's test function is called. - pub test: Box, -} - -impl CompositeNetworkTest { - pub fn new( - wrapper: W, - test: T, - ) -> CompositeNetworkTest { - CompositeNetworkTest { - wrappers: vec![Box::new(wrapper)], - test: Box::new(test), - } - } - - pub fn new_with_two_wrappers< - T1: NetworkLoadTest + 'static, - T2: NetworkLoadTest + 'static, - W: NetworkTest + 'static, - >( - wrapper1: T1, - wrapper2: T2, - test: W, - ) -> CompositeNetworkTest { - CompositeNetworkTest { - wrappers: vec![Box::new(wrapper1), Box::new(wrapper2)], - test: Box::new(test), - } - } -} - -impl NetworkTest for CompositeNetworkTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - for wrapper in &self.wrappers { - wrapper.setup(ctx)?; - } - self.test.run(ctx)?; - for wrapper in &self.wrappers { - wrapper.finish(ctx)?; - } - Ok(()) - } -} - -impl Test for CompositeNetworkTest { - fn name(&self) -> &'static str { - "CompositeNetworkTest" - } -} - -pub(crate) fn generate_onchain_config_blob(data: &[u8]) -> String { - let mut buf = String::new(); - - write!(buf, "vector[").unwrap(); - for (i, b) in data.iter().enumerate() { - if i % 20 == 0 { - if i > 0 { - writeln!(buf).unwrap(); - } - } else { - write!(buf, " ").unwrap(); - } - write!(buf, "{}u8,", b).unwrap(); - } - write!(buf, "]").unwrap(); - buf -} diff --git a/testsuite/testcases/src/load_vs_perf_benchmark.rs b/testsuite/testcases/src/load_vs_perf_benchmark.rs deleted file mode 100644 index 60d794375fcd3..0000000000000 --- a/testsuite/testcases/src/load_vs_perf_benchmark.rs +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{create_emitter_and_request, LoadDestination, NetworkLoadTest}; -use anyhow::Context; -use aptos_forge::{ - args::TransactionTypeArg, - prometheus_metrics::{LatencyBreakdown, LatencyBreakdownSlice}, - success_criteria::{SuccessCriteria, SuccessCriteriaChecker}, - EmitJobMode, EmitJobRequest, NetworkContext, NetworkTest, Result, Test, TxnStats, - WorkflowProgress, -}; -use aptos_logger::info; -use rand::SeedableRng; -use std::{fmt::Debug, time::Duration}; -use tokio::runtime::Runtime; - -// add larger warmup, as when we are exceeding the max load, -// it takes more time to fill mempool. -const PER_TEST_WARMUP_DURATION_FRACTION: f32 = 0.2; -const PER_TEST_COOLDOWN_DURATION_FRACTION: f32 = 0.05; - -pub struct SingleRunStats { - name: String, - stats: TxnStats, - latency_breakdown: LatencyBreakdown, - ledger_transactions: u64, - actual_duration: Duration, -} - -#[derive(Debug)] -pub enum Workloads { - TPS(Vec), - TRANSACTIONS(Vec), -} - -impl Workloads { - fn len(&self) -> usize { - match self { - Self::TPS(tpss) => tpss.len(), - Self::TRANSACTIONS(workloads) => workloads.len(), - } - } - - fn type_name(&self) -> String { - match self { - Self::TPS(_) => "Load (TPS)".to_string(), - Self::TRANSACTIONS(_) => "Workload".to_string(), - } - } - - fn num_phases(&self, index: usize) -> usize { - match self { - Self::TPS(_) => 1, - Self::TRANSACTIONS(workloads) => { - if workloads[index].is_phased() { - 2 - } else { - 1 - } - }, - } - } - - fn phase_name(&self, index: usize, phase: usize) -> String { - match self { - Self::TPS(tpss) => { - assert_eq!(phase, 0); - format!("{}", tpss[index]) - }, - Self::TRANSACTIONS(workloads) => format!( - "{}{}: {}", - index, - if workloads[index].is_phased() { - format!(": ph{}", phase) - } else { - "".to_string() - }, - workloads[index].phase_name(phase) - ), - } - } - - fn configure(&self, index: usize, request: EmitJobRequest) -> EmitJobRequest { - match self { - Self::TPS(tpss) => request.mode(EmitJobMode::ConstTps { tps: tpss[index] }), - Self::TRANSACTIONS(workloads) => workloads[index].configure(request), - } - } - - fn split_duration(&self, global_duration: Duration) -> (Duration, Duration) { - let total_phases: usize = (0..self.len()).map(|index| self.num_phases(index)).sum(); - let phase_duration = global_duration.div_f32( - total_phases as f32 + PER_TEST_WARMUP_DURATION_FRACTION * (self.len() - 1) as f32, - ); - let buffer = phase_duration.mul_f32(PER_TEST_WARMUP_DURATION_FRACTION); - (phase_duration, buffer) - } -} - -#[derive(Debug, Copy, Clone)] -pub struct TransactionWorkload { - pub transaction_type: TransactionTypeArg, - pub num_modules: usize, - pub unique_senders: bool, - pub mempool_backlog: usize, -} - -impl TransactionWorkload { - fn is_phased(&self) -> bool { - self.unique_senders - } - - fn configure(&self, request: EmitJobRequest) -> EmitJobRequest { - let account_creation_type = - TransactionTypeArg::AccountGenerationLargePool.materialize_default(); - - let request = request.mode(EmitJobMode::MaxLoad { - mempool_backlog: self.mempool_backlog, - }); - - if self.is_phased() { - let write_type = self.transaction_type.materialize( - self.num_modules, - true, - WorkflowProgress::when_done_default(), - ); - request.transaction_mix_per_phase(vec![ - // warmup - vec![(account_creation_type, 1)], - vec![(account_creation_type, 1)], - vec![(write_type, 1)], - // cooldown - vec![(write_type, 1)], - ]) - } else { - request.transaction_type(self.transaction_type.materialize( - self.num_modules, - false, - WorkflowProgress::when_done_default(), - )) - } - } - - fn phase_name(&self, phase: usize) -> String { - format!( - "{}{}[{:.1}k]", - match (self.is_phased(), phase) { - (true, 0) => "CreateBurnerAccounts".to_string(), - (true, 1) => format!("{:?}", self.transaction_type), - (false, 0) => format!("{:?}", self.transaction_type), - _ => unreachable!(), - }, - if self.num_modules > 1 { - format!("({} modules)", self.num_modules) - } else { - "".to_string() - }, - self.mempool_backlog as f32 / 1000.0, - ) - } -} - -pub struct ContinuousTraffic { - pub traffic: EmitJobRequest, - pub criteria: Option, -} - -pub struct LoadVsPerfBenchmark { - pub test: Box, - pub workloads: Workloads, - pub criteria: Vec, - - pub continuous_traffic: Option, -} - -impl Test for LoadVsPerfBenchmark { - fn name(&self) -> &'static str { - "continuous progress test" - } -} - -impl LoadVsPerfBenchmark { - fn evaluate_single( - &self, - ctx: &mut NetworkContext<'_>, - workloads: &Workloads, - index: usize, - duration: Duration, - ) -> Result> { - let rng = SeedableRng::from_rng(ctx.core().rng())?; - let emit_job_request = workloads.configure(index, ctx.emit_job.clone()); - let stats_by_phase = self.test.network_load_test( - ctx, - emit_job_request, - duration, - PER_TEST_WARMUP_DURATION_FRACTION, - PER_TEST_COOLDOWN_DURATION_FRACTION, - rng, - )?; - - let mut result = vec![]; - for (phase, phase_stats) in stats_by_phase.into_iter().enumerate() { - result.push(SingleRunStats { - name: workloads.phase_name(index, phase), - stats: phase_stats.emitter_stats, - latency_breakdown: phase_stats.latency_breakdown, - ledger_transactions: phase_stats.ledger_transactions, - actual_duration: phase_stats.actual_duration, - }); - } - - Ok(result) - } -} - -impl NetworkTest for LoadVsPerfBenchmark { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - assert!( - self.criteria.is_empty() || self.criteria.len() == self.workloads.len(), - "Invalid config, {} criteria and {} workloads given", - self.criteria.len(), - self.workloads.len(), - ); - - let rt = Runtime::new().unwrap(); - - let mut continous_job = if let Some(continuous_traffic) = &self.continuous_traffic { - let nodes_to_send_load_to = - LoadDestination::FullnodesOtherwiseValidators.get_destination_nodes(ctx.swarm()); - let rng = SeedableRng::from_rng(ctx.core().rng())?; - let (mut emitter, emit_job_request) = create_emitter_and_request( - ctx.swarm(), - continuous_traffic.traffic.clone(), - &nodes_to_send_load_to, - rng, - ) - .context("create emitter")?; - - let job = rt - .block_on(emitter.start_job( - ctx.swarm().chain_info().root_account, - emit_job_request, - 1 + 2 * self.workloads.len(), - )) - .context("start emitter job")?; - Some(job) - } else { - None - }; - - let (phase_duration, buffer) = self.workloads.split_duration(ctx.global_duration); - - let mut results = Vec::new(); - for index in 0..self.workloads.len() { - if index != 0 { - info!("Sleeping in between loadtests, for {}s", buffer.as_secs()); - std::thread::sleep(buffer); - } - - if let Some(job) = continous_job.as_mut() { - job.start_next_phase() - } - - info!("Starting for {:?}", self.workloads); - results.push( - self.evaluate_single( - ctx, - &self.workloads, - index, - phase_duration - .checked_mul(self.workloads.num_phases(index) as u32) - .unwrap(), - )?, - ); - - if let Some(job) = continous_job.as_mut() { - job.start_next_phase() - } - - // Note: uncomment below to perform reconfig during a test - // let mut aptos_info = ctx.swarm().aptos_public_info(); - // runtime.block_on(aptos_info.reconfig()); - - let table = to_table(self.workloads.type_name(), &results); - for line in table { - info!("{}", line); - } - } - - let table = to_table(self.workloads.type_name(), &results); - for line in table { - ctx.report.report_text(line); - } - - let continuous_results = continous_job.map(|job| { - let stats_by_phase = rt.block_on(job.stop_job()); - - let mut result = vec![]; - for (phase, phase_stats) in stats_by_phase.into_iter().enumerate() { - if phase % 2 != 0 { - result.push(( - format!("continuous with traffic {}", phase / 2), - phase_stats, - )); - } - } - - let table = to_table_continuous("continuous traffic".to_string(), &result); - for line in table { - ctx.report.report_text(line); - } - - result - }); - - for (index, result) in results.iter().enumerate() { - // always take last phase for success criteria - let target_result = &result[result.len() - 1]; - let rate = target_result.stats.rate(); - if let Some(criteria) = self.criteria.get(index) { - SuccessCriteriaChecker::check_core_for_success( - criteria, - ctx.report, - &rate, - Some(&target_result.latency_breakdown), - Some(target_result.name.clone()), - )?; - } - } - - if let Some(results) = continuous_results { - for (name, stats) in results { - let rate = stats.rate(); - if let Some(criteria) = &self.continuous_traffic.as_ref().unwrap().criteria { - SuccessCriteriaChecker::check_core_for_success( - criteria, - ctx.report, - &rate, - None, - Some(name), - )?; - } - } - } - - Ok(()) - } -} - -fn to_table(type_name: String, results: &[Vec]) -> Vec { - let mut table = Vec::new(); - table.push(format!( - "{: <40} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12}", - type_name, - "submitted/s", - "committed/s", - "expired/s", - "rejected/s", - "chain txn/s", - "latency", - "p50 lat", - "p90 lat", - "p99 lat", - "batch->pos", - "pos->prop", - "prop->order", - "order->commit", - "actual dur" - )); - - for run_results in results { - for result in run_results { - let rate = result.stats.rate(); - table.push(format!( - "{: <40} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12.3} | {: <12.3} | {: <12.3} | {: <12.3} | {: <12}", - result.name, - rate.submitted, - rate.committed, - rate.expired, - rate.failed_submission, - result.ledger_transactions / result.actual_duration.as_secs(), - rate.latency, - rate.p50_latency, - rate.p90_latency, - rate.p99_latency, - result.latency_breakdown.get_samples(&LatencyBreakdownSlice::QsBatchToPos).max_sample(), - result.latency_breakdown.get_samples(&LatencyBreakdownSlice::QsPosToProposal).max_sample(), - result.latency_breakdown.get_samples(&LatencyBreakdownSlice::ConsensusProposalToOrdered).max_sample(), - result.latency_breakdown.get_samples(&LatencyBreakdownSlice::ConsensusOrderedToCommit).max_sample(), - result.actual_duration.as_secs() - )); - } - } - - table -} - -fn to_table_continuous(type_name: String, results: &[(String, TxnStats)]) -> Vec { - let mut table = Vec::new(); - table.push(format!( - "{: <40} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12}", - type_name, - "submitted/s", - "committed/s", - "expired/s", - "rejected/s", - "latency", - "p50 lat", - "p90 lat", - "p99 lat", - )); - - for (name, stats) in results { - let rate = stats.rate(); - table.push(format!( - "{: <40} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12} | {: <12}", - name, - rate.submitted, - rate.committed, - rate.expired, - rate.failed_submission, - rate.latency, - rate.p50_latency, - rate.p90_latency, - rate.p99_latency, - )); - } - - table -} - -#[test] -fn test_phases_duration() { - use assert_approx_eq::assert_approx_eq; - use std::ops::{Add, Mul}; - - let one_phase = TransactionWorkload { - transaction_type: TransactionTypeArg::CoinTransfer, - num_modules: 1, - unique_senders: false, - mempool_backlog: 20000, - }; - let two_phase = TransactionWorkload { - transaction_type: TransactionTypeArg::ModifyGlobalResource, - num_modules: 1, - unique_senders: true, - mempool_backlog: 20000, - }; - - { - let workload = Workloads::TRANSACTIONS(vec![one_phase]); - let (phase, _buffer) = workload.split_duration(Duration::from_secs(1)); - assert_approx_eq!(phase.as_secs_f32(), 1.0); - } - - { - let workload = Workloads::TRANSACTIONS(vec![one_phase, one_phase]); - let (phase, buffer) = workload.split_duration(Duration::from_secs(1)); - assert_approx_eq!(phase.as_secs_f32(), 1.0 / 2.2); - assert_approx_eq!(buffer.as_secs_f32(), 1.0 / 2.2 * 0.2); - assert_approx_eq!(phase.add(phase).add(buffer).as_secs_f32(), 1.0); - } - - { - let workload = Workloads::TRANSACTIONS(vec![two_phase]); - let (phase, _buffer) = workload.split_duration(Duration::from_secs(1)); - assert_approx_eq!(phase.as_secs_f32(), 0.5); - } - - { - let workload = - Workloads::TRANSACTIONS(vec![one_phase, one_phase, two_phase, two_phase, two_phase]); - let (phase, buffer) = workload.split_duration(Duration::from_secs(1)); - assert_approx_eq!(phase.as_secs_f32(), 1.0 / 8.8); - assert_approx_eq!(buffer.as_secs_f32(), 1.0 / 8.8 * 0.2); - assert_approx_eq!(phase.mul(8).add(buffer.mul(4)).as_secs_f32(), 1.0); - } -} diff --git a/testsuite/testcases/src/modifiers.rs b/testsuite/testcases/src/modifiers.rs deleted file mode 100644 index 2cdda468a1418..0000000000000 --- a/testsuite/testcases/src/modifiers.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{multi_region_network_test::chunk_peers, LoadDestination, NetworkLoadTest}; -use aptos_forge::{ - GroupCpuStress, NetworkContext, NetworkTest, Swarm, SwarmChaos, SwarmCpuStress, SwarmExt, Test, -}; -use aptos_logger::info; -use aptos_types::PeerId; -use rand::Rng; -use tokio::runtime::Runtime; - -fn add_execution_delay(swarm: &mut dyn Swarm, config: &ExecutionDelayConfig) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - let validators = swarm.get_validator_clients_with_names(); - - runtime.block_on(async { - let mut rng = rand::thread_rng(); - for (name, validator) in validators { - let sleep_percentage = if rng.gen_bool(config.inject_delay_node_fraction) { - rng.gen_range(1_u32, config.inject_delay_max_transaction_percentage) - } else { - 0 - }; - info!( - "Validator {} adding {}% of transactions with {}ms execution delay", - name, sleep_percentage, config.inject_delay_per_transaction_ms - ); - validator - .set_failpoint( - "aptos_vm::execution::user_transaction".to_string(), - format!( - "{}%delay({})", - sleep_percentage, config.inject_delay_per_transaction_ms - ), - ) - .await - .map_err(|e| { - anyhow::anyhow!( - "set_failpoint to add execution delay on {} failed, {:?}", - name, - e - ) - })?; - } - Ok(()) - }) -} - -fn remove_execution_delay(swarm: &mut dyn Swarm) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - let validators = swarm.get_validator_clients_with_names(); - - runtime.block_on(async { - for (name, validator) in validators { - validator - .set_failpoint( - "aptos_vm::execution::block_metadata".to_string(), - "off".to_string(), - ) - .await - .map_err(|e| { - anyhow::anyhow!( - "set_failpoint to remove execution delay on {} failed, {:?}", - name, - e - ) - })?; - } - Ok(()) - }) -} - -/// Config for adding variable processing overhead/delay into -/// execution, to make different nodes have different processing speed. -pub struct ExecutionDelayConfig { - /// Fraction (0.0 - 1.0) of nodes on which any delay will be introduced - pub inject_delay_node_fraction: f64, - /// For nodes with delay, what percentage (0-100) of transaction will be delayed. - /// (this is needed because delay that can be introduced is integer number of ms) - /// Different node speed come from this setting, each node is selected a number - /// between 1 and given max. - pub inject_delay_max_transaction_percentage: u32, - /// Fixed busy-loop delay applied to each transaction that is delayed, - /// before it is executed. - pub inject_delay_per_transaction_ms: u32, -} - -pub struct ExecutionDelayTest { - pub add_execution_delay: ExecutionDelayConfig, -} - -impl NetworkLoadTest for ExecutionDelayTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - add_execution_delay(ctx.swarm(), &self.add_execution_delay)?; - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - remove_execution_delay(ctx.swarm()) - } -} - -impl NetworkTest for ExecutionDelayTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} - -impl Test for ExecutionDelayTest { - fn name(&self) -> &'static str { - "ExecutionDelayWrapper" - } -} - -pub struct NetworkUnreliabilityConfig { - pub inject_unreliability_fraction: f64, - pub inject_max_unreliability_percentage: f32, -} - -pub struct NetworkUnreliabilityTest { - pub config: NetworkUnreliabilityConfig, -} - -impl NetworkLoadTest for NetworkUnreliabilityTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - let swarm = ctx.swarm(); - let runtime = Runtime::new().unwrap(); - let validators = swarm.get_validator_clients_with_names(); - - runtime.block_on(async { - let mut rng = rand::thread_rng(); - for (name, validator) in validators { - let drop_percentage = if rng.gen_bool(self.config.inject_unreliability_fraction) { - rng.gen_range( - 1_u32, - (self.config.inject_max_unreliability_percentage * 1000.0) as u32, - ) as f32 - / 1000.0 - } else { - 0.0 - }; - info!( - "Validator {} dropping {}% of messages", - name, drop_percentage - ); - validator - .set_failpoint( - "consensus::send::any".to_string(), - format!("{}%return", drop_percentage), - ) - .await - .map_err(|e| { - anyhow::anyhow!( - "set_failpoint to add unreliability on {} failed, {:?}", - name, - e - ) - })?; - } - Ok::<(), anyhow::Error>(()) - })?; - - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - let validators = ctx.swarm().get_validator_clients_with_names(); - - runtime.block_on(async { - for (name, validator) in validators { - validator - .set_failpoint("consensus::send::any".to_string(), "off".to_string()) - .await - .map_err(|e| { - anyhow::anyhow!( - "set_failpoint to remove unreliability on {} failed, {:?}", - name, - e - ) - })?; - } - Ok(()) - }) - } -} - -impl NetworkTest for NetworkUnreliabilityTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} - -impl Test for NetworkUnreliabilityTest { - fn name(&self) -> &'static str { - "NetworkUnreliabilityWrapper" - } -} - -#[derive(Clone)] -pub struct CpuChaosConfig { - pub num_groups: usize, - pub load_per_worker: u64, -} - -impl Default for CpuChaosConfig { - fn default() -> Self { - Self { - num_groups: 4, - load_per_worker: 100, - } - } -} - -#[derive(Default)] -pub struct CpuChaosTest { - cpu_chaos_config: CpuChaosConfig, -} - -impl CpuChaosTest { - pub fn new_with_config(cpu_chaos_config: CpuChaosConfig) -> Self { - Self { cpu_chaos_config } - } - - /// Creates a new SwarmCpuStress to be injected via chaos. Note: - /// CPU chaos is only done for the validators in the swarm (and - /// not the fullnodes). - fn create_cpu_chaos(&self, swarm: &mut dyn Swarm) -> SwarmCpuStress { - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - let cpu_chaos_config = self.cpu_chaos_config.clone(); - create_swarm_cpu_stress(all_validators, Some(cpu_chaos_config)) - } -} - -impl Test for CpuChaosTest { - fn name(&self) -> &'static str { - "CpuChaosWrapper" - } -} - -/// Creates a SwarmCpuStress to be injected via chaos. CPU chaos -/// is added to all the given peers using the specified config. -pub fn create_swarm_cpu_stress( - all_peers: Vec, - cpu_chaos_config: Option, -) -> SwarmCpuStress { - // Determine the CPU chaos config to use - let cpu_chaos_config = cpu_chaos_config.unwrap_or_default(); - - // Chunk the peers into groups and create a GroupCpuStress for each group - let all_peers = all_peers.iter().map(|id| vec![*id]).collect(); - let peer_chunks = chunk_peers(all_peers, cpu_chaos_config.num_groups); - let group_cpu_stresses = peer_chunks - .into_iter() - .enumerate() - .map(|(idx, chunk)| { - // Lower bound the number of workers - let num_workers = if cpu_chaos_config.num_groups > idx { - (cpu_chaos_config.num_groups - idx) as u64 - } else { - 1 - }; - - // Create the cpu stress for the group - info!( - "Creating CPU stress for group {} with {} workers", - idx, num_workers - ); - GroupCpuStress { - name: format!("group-{}-cpu-stress", idx), - target_nodes: chunk, - num_workers, - load_per_worker: cpu_chaos_config.load_per_worker, - } - }) - .collect(); - - SwarmCpuStress { group_cpu_stresses } -} - -impl NetworkLoadTest for CpuChaosTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - let swarm_cpu_stress = self.create_cpu_chaos(ctx.swarm()); - - ctx.runtime.block_on( - ctx.swarm - .inject_chaos(SwarmChaos::CpuStress(swarm_cpu_stress)), - )?; - - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - let swarm_cpu_stress = self.create_cpu_chaos(ctx.swarm()); - - ctx.runtime.block_on( - ctx.swarm - .remove_chaos(SwarmChaos::CpuStress(swarm_cpu_stress)), - ) - } -} - -impl NetworkTest for CpuChaosTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/multi_region_network_test.rs b/testsuite/testcases/src/multi_region_network_test.rs deleted file mode 100644 index a4a60a062f818..0000000000000 --- a/testsuite/testcases/src/multi_region_network_test.rs +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{GroupNetEm, NetworkContext, NetworkTest, Swarm, SwarmChaos, SwarmNetEm, Test}; -use aptos_logger::info; -use aptos_types::PeerId; -use itertools::{self, EitherOrBoth, Itertools}; -use std::collections::BTreeMap; - -/// The link stats are obtained from https://github.com/doitintl/intercloud-throughput/blob/master/results_202202/results.csv -/// The four regions were hand-picked from the dataset to simulate a multi-region setup -/// with high latencies. -/// Note, we restrict bandwidth to 300 Mbps between all regions. The reasoning is that the dataset -/// is measuring TCP bandwidth only which is primarily affected by RTT, and not the actual bandwidth -/// across the regions, which would vary according to competing traffic, etc. -const FOUR_REGION_LINK_STATS: &[u8] = include_bytes!("data/four_region_link_stats.csv"); -/// The two regions were chosen as the most distant regions among the four regions set. -const TWO_REGION_LINK_STATS: &[u8] = include_bytes!("data/two_region_link_stats.csv"); - -fn get_link_stats_table(csv: &[u8]) -> BTreeMap> { - let mut stats_table = BTreeMap::new(); - - let mut rdr = csv::Reader::from_reader(csv); - rdr.deserialize() - .for_each(|result: Result<(String, String, u64, f64), _>| { - if let Ok((from, to, bitrate, latency)) = result { - stats_table - .entry(from) - .or_insert_with(BTreeMap::new) - .insert(to, (bitrate, latency)); - } - }); - stats_table -} - -fn div_ceil(dividend: usize, divisor: usize) -> usize { - if dividend % divisor == 0 { - dividend / divisor - } else { - dividend / divisor + 1 - } -} - -/// Chunks the given set of peers into the specified number of chunks. The difference between the -/// largest chunk and smallest chunk is at most one. -pub(crate) fn chunk_peers(mut peers: Vec>, num_chunks: usize) -> Vec> { - let mut chunks = vec![]; - let mut chunks_remaining = num_chunks; - while chunks_remaining > 0 { - let chunk_size = div_ceil(peers.len(), chunks_remaining); - let remaining = peers.split_off(chunk_size); - chunks.push(peers.iter().flatten().cloned().collect()); - peers = remaining; - - chunks_remaining -= 1; - } - chunks -} - -/// Creates a table of peers grouped by region. The peers are divided into N groups, where N is the -/// number of regions provided in the link stats table. Any remaining peers are added to the first -/// group. -fn create_link_stats_table_with_peer_groups( - peers: Vec>, - link_stats_table: &LinkStatsTable, -) -> LinkStatsTableWithPeerGroups { - // Verify that we have enough grouped peers to simulate the link stats table - assert!(peers.len() >= link_stats_table.len()); - - // Verify that we have the correct number of regions to simulate the link stats table - let number_of_regions = link_stats_table.len(); - assert!( - number_of_regions >= 2, - "At least 2 regions are required for inter-region network chaos." - ); - assert!( - number_of_regions <= 4, - "ChaosMesh only supports simulating up to 4 regions." - ); - - // Create the link stats table with peer groups - let peer_chunks = chunk_peers(peers, number_of_regions); - let peer_groups = peer_chunks - .into_iter() - .zip(link_stats_table.iter()) - .map(|(chunk, (from_region, stats))| (from_region.clone(), chunk, stats.clone())) - .collect(); - - peer_groups -} - -// A map of "source" regions to a map of "destination" region to (bandwidth, latency) -type LinkStatsTable = BTreeMap>; -// A map of "source" regions to a tuple of (list of peers, map of "destination" region to (bandwidth, latency)) -type LinkStatsTableWithPeerGroups = Vec<(String, Vec, BTreeMap)>; - -#[derive(Clone)] -pub struct InterRegionNetEmConfig { - delay_jitter_ms: u64, - delay_correlation_percentage: u64, - loss_percentage: u64, - loss_correlation_percentage: u64, -} - -impl Default for InterRegionNetEmConfig { - fn default() -> Self { - Self { - delay_jitter_ms: 0, - delay_correlation_percentage: 50, - loss_percentage: 3, - loss_correlation_percentage: 50, - } - } -} - -impl InterRegionNetEmConfig { - // Creates GroupNetEm for inter-region network chaos - fn build(&self, peer_groups: &LinkStatsTableWithPeerGroups) -> Vec { - let group_netems: Vec = peer_groups - .iter() - .combinations(2) - .map(|comb| { - let (from_region, from_chunk, stats) = &comb[0]; - let (to_region, to_chunk, _) = &comb[1]; - - let (bandwidth, latency) = stats.get(to_region).unwrap(); - let netem = GroupNetEm { - name: format!("{}-to-{}-netem", from_region, to_region), - source_nodes: from_chunk.to_vec(), - target_nodes: to_chunk.to_vec(), - delay_latency_ms: *latency as u64, - delay_jitter_ms: self.delay_jitter_ms, - delay_correlation_percentage: self.delay_correlation_percentage, - loss_percentage: self.loss_percentage, - loss_correlation_percentage: self.loss_correlation_percentage, - rate_in_mbps: *bandwidth / 1e6 as u64, - }; - info!("inter-region netem {:?}", netem); - - netem - }) - .collect(); - - group_netems - } -} - -#[derive(Clone)] -pub struct IntraRegionNetEmConfig { - bandwidth_rate_mbps: u64, - delay_latency_ms: u64, - delay_jitter_ms: u64, - delay_correlation_percentage: u64, - loss_percentage: u64, - loss_correlation_percentage: u64, -} - -impl Default for IntraRegionNetEmConfig { - fn default() -> Self { - Self { - bandwidth_rate_mbps: 10 * 1000, // 10 Gbps - delay_latency_ms: 50, - delay_jitter_ms: 0, - delay_correlation_percentage: 50, - loss_percentage: 1, - loss_correlation_percentage: 50, - } - } -} - -impl IntraRegionNetEmConfig { - fn build(&self, peer_groups: LinkStatsTableWithPeerGroups) -> Vec { - let group_netems: Vec = peer_groups - .iter() - .map(|(region, chunk, _)| { - let netem = GroupNetEm { - name: format!("{}-self-netem", region), - source_nodes: chunk.to_vec(), - target_nodes: chunk.to_vec(), - delay_latency_ms: self.delay_latency_ms, - delay_jitter_ms: self.delay_jitter_ms, - delay_correlation_percentage: self.delay_correlation_percentage, - loss_percentage: self.loss_percentage, - loss_correlation_percentage: self.loss_correlation_percentage, - rate_in_mbps: self.bandwidth_rate_mbps, - }; - info!("intra-region netem {:?}", netem); - - netem - }) - .collect(); - - group_netems - } -} - -#[derive(Clone)] -pub struct MultiRegionNetworkEmulationConfig { - pub link_stats_table: LinkStatsTable, - pub inter_region_config: InterRegionNetEmConfig, - pub intra_region_config: Option, -} - -impl Default for MultiRegionNetworkEmulationConfig { - fn default() -> Self { - Self { - link_stats_table: get_link_stats_table(FOUR_REGION_LINK_STATS), - inter_region_config: InterRegionNetEmConfig::default(), - intra_region_config: Some(IntraRegionNetEmConfig::default()), - } - } -} - -impl MultiRegionNetworkEmulationConfig { - pub fn two_region() -> Self { - Self { - link_stats_table: get_link_stats_table(TWO_REGION_LINK_STATS), - ..Default::default() - } - } -} - -/// A test to emulate network conditions for a multi-region setup. -#[derive(Default)] -pub struct MultiRegionNetworkEmulationTest { - network_emulation_config: MultiRegionNetworkEmulationConfig, -} - -impl MultiRegionNetworkEmulationTest { - pub fn new_with_config(network_emulation_config: MultiRegionNetworkEmulationConfig) -> Self { - Self { - network_emulation_config, - } - } - - /// Creates a new SwarmNetEm to be injected via chaos. Note: network - /// emulation is only done for the validators in the swarm (and not - /// the fullnodes). - fn create_netem_chaos(&self, swarm: &mut dyn Swarm) -> SwarmNetEm { - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - let all_vfns = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - - let all_pairs: Vec<_> = all_validators - .iter() - .zip_longest(all_vfns) - .map(|either_or_both| match either_or_both { - EitherOrBoth::Both(validator, vfn) => vec![*validator, vfn], - EitherOrBoth::Left(validator) => vec![*validator], - EitherOrBoth::Right(_) => { - panic!("Number of validators must be >= number of VFNs") - }, - }) - .collect(); - - let network_emulation_config = self.network_emulation_config.clone(); - create_multi_region_swarm_network_chaos(all_pairs, Some(network_emulation_config)) - } -} - -impl Test for MultiRegionNetworkEmulationTest { - fn name(&self) -> &'static str { - "network:multi-region-network-emulation" - } -} - -/// Creates a SwarmNetEm to be injected via chaos. Network emulation is added to all the given -/// peers using the specified config. Peers that must be colocated should be grouped in the same -/// inner vector. They are treated as a single group. -pub fn create_multi_region_swarm_network_chaos( - all_peers: Vec>, - network_emulation_config: Option, -) -> SwarmNetEm { - // Determine the network emulation config to use - let network_emulation_config = network_emulation_config.unwrap_or_default(); - - // Create the link stats table for the peer groups - let peer_groups = create_link_stats_table_with_peer_groups( - all_peers, - &network_emulation_config.link_stats_table, - ); - - // Create the inter and intra network emulation configs - let inter_region_netem = network_emulation_config - .inter_region_config - .build(&peer_groups); - let intra_region_netem = network_emulation_config - .intra_region_config - .as_ref() - .map(|config| config.build(peer_groups)) - .unwrap_or_default(); - - SwarmNetEm { - group_netems: itertools::concat(vec![intra_region_netem, inter_region_netem]), - } -} - -impl NetworkLoadTest for MultiRegionNetworkEmulationTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - let chaos = self.create_netem_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.inject_chaos(SwarmChaos::NetEm(chaos)))?; - - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - let chaos = self.create_netem_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.remove_chaos(SwarmChaos::NetEm(chaos)))?; - Ok(()) - } -} - -impl NetworkTest for MultiRegionNetworkEmulationTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use aptos_types::account_address::AccountAddress; - use std::vec; - - #[test] - fn test_create_multi_region_swarm_network_chaos() { - aptos_logger::Logger::new().init(); - - // Create a config with 8 peers and multiple regions - let all_peers: Vec<_> = (0..8).map(|_| vec![PeerId::random()]).collect(); - let netem = create_multi_region_swarm_network_chaos(all_peers, None); - - // Verify the number of group netems - assert_eq!(netem.group_netems.len(), 10); - - // Create a config with 10 peers and multiple regions - let all_peers: Vec<_> = (0..10).map(|_| vec![PeerId::random()]).collect(); - let netem = create_multi_region_swarm_network_chaos(all_peers.clone(), None); - - // Verify the resulting group netems - assert_eq!(netem.group_netems.len(), 10); - assert_eq!(netem.group_netems[0].source_nodes.len(), 4); - assert_eq!(netem.group_netems[0].target_nodes.len(), 4); - assert_eq!(netem.group_netems[0], GroupNetEm { - name: "aws--ap-northeast-1-self-netem".to_owned(), - rate_in_mbps: 10000, - source_nodes: vec![ - all_peers[0][0], - all_peers[1][0], - all_peers[8][0], - all_peers[9][0], - ], - target_nodes: vec![ - all_peers[0][0], - all_peers[1][0], - all_peers[8][0], - all_peers[9][0], - ], - delay_latency_ms: 50, - delay_jitter_ms: 5, - delay_correlation_percentage: 50, - loss_percentage: 1, - loss_correlation_percentage: 50 - }) - } - - #[test] - fn test_chunk_peers() { - let peers: Vec<_> = (0..3).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 1); - assert_eq!(chunks[1].len(), 1); - assert_eq!(chunks[2].len(), 1); - assert_eq!(chunks[3].len(), 0); - - let peers: Vec<_> = (0..4).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 1); - assert_eq!(chunks[1].len(), 1); - assert_eq!(chunks[2].len(), 1); - assert_eq!(chunks[3].len(), 1); - - let peers: Vec<_> = (0..5).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 2); - assert_eq!(chunks[1].len(), 1); - assert_eq!(chunks[2].len(), 1); - assert_eq!(chunks[3].len(), 1); - - let peers: Vec<_> = (0..6).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 2); - assert_eq!(chunks[1].len(), 2); - assert_eq!(chunks[2].len(), 1); - assert_eq!(chunks[3].len(), 1); - - let peers: Vec<_> = (0..7).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 2); - assert_eq!(chunks[1].len(), 2); - assert_eq!(chunks[2].len(), 2); - assert_eq!(chunks[3].len(), 1); - - let peers: Vec<_> = (0..8).map(|_| vec![AccountAddress::random()]).collect(); - let chunks = chunk_peers(peers, 4); - assert_eq!(chunks[0].len(), 2); - assert_eq!(chunks[1].len(), 2); - assert_eq!(chunks[2].len(), 2); - assert_eq!(chunks[3].len(), 2); - } -} diff --git a/testsuite/testcases/src/network_bandwidth_test.rs b/testsuite/testcases/src/network_bandwidth_test.rs deleted file mode 100644 index eb8f701565440..0000000000000 --- a/testsuite/testcases/src/network_bandwidth_test.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{ - GroupNetworkBandwidth, NetworkContext, NetworkTest, SwarmChaos, SwarmNetworkBandwidth, Test, -}; - -pub struct NetworkBandwidthTest; - -// Bandwidth -// Indicates the rate of bandwidth limit -pub const RATE_MBPS: u64 = 100; -// Indicates the number of bytes waiting in queue -pub const LIMIT_BYTES: u64 = 20971520; -// Indicates the maximum number of bytes that can be sent instantaneously -pub const BUFFER_BYTES: u64 = 10000; - -impl Test for NetworkBandwidthTest { - fn name(&self) -> &'static str { - "network::bandwidth-test" - } -} - -impl NetworkLoadTest for NetworkBandwidthTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - ctx.runtime - .block_on( - ctx.swarm - .inject_chaos(SwarmChaos::Bandwidth(SwarmNetworkBandwidth { - group_network_bandwidths: vec![GroupNetworkBandwidth { - name: format!("forge-namespace-{}mbps-bandwidth", RATE_MBPS), - rate: RATE_MBPS, - limit: LIMIT_BYTES, - buffer: BUFFER_BYTES, - }], - })), - )?; - - let msg = format!( - "Limited bandwidth to {}mbps with limit {} and buffer {} to namespace", - RATE_MBPS, LIMIT_BYTES, BUFFER_BYTES - ); - println!("{}", msg); - ctx.report.report_text(msg); - - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - ctx.runtime - .block_on( - ctx.swarm - .remove_chaos(SwarmChaos::Bandwidth(SwarmNetworkBandwidth { - group_network_bandwidths: vec![GroupNetworkBandwidth { - name: format!("forge-namespace-{}mbps-bandwidth", RATE_MBPS), - rate: RATE_MBPS, - limit: LIMIT_BYTES, - buffer: BUFFER_BYTES, - }], - })), - )?; - Ok(()) - } -} - -impl NetworkTest for NetworkBandwidthTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/network_loss_test.rs b/testsuite/testcases/src/network_loss_test.rs deleted file mode 100644 index f7f175555f7aa..0000000000000 --- a/testsuite/testcases/src/network_loss_test.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{NetworkContext, NetworkTest, SwarmChaos, SwarmNetworkLoss, Test}; - -pub struct NetworkLossTest; - -// Loss parameters -pub const LOSS_PERCENTAGE: u64 = 20; -pub const CORRELATION_PERCENTAGE: u64 = 10; - -impl Test for NetworkLossTest { - fn name(&self) -> &'static str { - "network::loss-test" - } -} - -impl NetworkLoadTest for NetworkLossTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - ctx.runtime - .block_on(ctx.swarm.inject_chaos(SwarmChaos::Loss(SwarmNetworkLoss { - loss_percentage: LOSS_PERCENTAGE, - correlation_percentage: CORRELATION_PERCENTAGE, - })))?; - - let msg = format!( - "Injected {}% loss with {}% correlation loss to namespace", - LOSS_PERCENTAGE, CORRELATION_PERCENTAGE, - ); - println!("{}", msg); - ctx.report.report_text(msg); - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - ctx.runtime - .block_on(ctx.swarm.remove_chaos(SwarmChaos::Loss(SwarmNetworkLoss { - loss_percentage: LOSS_PERCENTAGE, - correlation_percentage: CORRELATION_PERCENTAGE, - })))?; - Ok(()) - } -} - -impl NetworkTest for NetworkLossTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/network_partition_test.rs b/testsuite/testcases/src/network_partition_test.rs deleted file mode 100644 index 41659cf5c8468..0000000000000 --- a/testsuite/testcases/src/network_partition_test.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{NetworkContext, NetworkTest, SwarmChaos, SwarmNetworkPartition, Test}; - -pub struct NetworkPartitionTest; - -// Partition -pub const PARTITION_PERCENTAGE: u64 = 30; - -impl Test for NetworkPartitionTest { - fn name(&self) -> &'static str { - "network::partition-test" - } -} - -impl NetworkLoadTest for NetworkPartitionTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - ctx.runtime - .block_on( - ctx.swarm - .inject_chaos(SwarmChaos::Partition(SwarmNetworkPartition { - partition_percentage: PARTITION_PERCENTAGE, - })), - )?; - - let msg = format!( - "Partitioned {}% validators in namespace", - PARTITION_PERCENTAGE - ); - println!("{}", msg); - ctx.report.report_text(msg); - // Just send the load to last validator which is not included in the partition - Ok(LoadDestination::Peers(vec![ctx - .swarm - .validators() - .last() - .map(|v| v.peer_id()) - .unwrap()])) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - ctx.runtime - .block_on( - ctx.swarm - .remove_chaos(SwarmChaos::Partition(SwarmNetworkPartition { - partition_percentage: PARTITION_PERCENTAGE, - })), - )?; - Ok(()) - } -} - -impl NetworkTest for NetworkPartitionTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/partial_nodes_down_test.rs b/testsuite/testcases/src/partial_nodes_down_test.rs deleted file mode 100644 index 2d6c126907cc4..0000000000000 --- a/testsuite/testcases/src/partial_nodes_down_test.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::generate_traffic; -use aptos_forge::{NetworkContext, NetworkTest, Result, Test}; -use std::thread; -use tokio::{runtime::Runtime, time::Duration}; - -pub struct PartialNodesDown; - -impl Test for PartialNodesDown { - fn name(&self) -> &'static str { - "10%-down" - } -} - -impl NetworkTest for PartialNodesDown { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let runtime = Runtime::new()?; - let duration = Duration::from_secs(120); - let all_validators = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - let mut down_nodes = all_validators.clone(); - let up_nodes = down_nodes.split_off(all_validators.len() / 10); - for n in &down_nodes { - let node = ctx.swarm().validator_mut(*n).unwrap(); - println!("Node {} is going to stop", node.name()); - runtime.block_on(node.stop())?; - } - thread::sleep(Duration::from_secs(5)); - - // Generate some traffic - let txn_stat = generate_traffic(ctx, &up_nodes, duration)?; - ctx.report - .report_txn_stats(self.name().to_string(), &txn_stat); - for n in &down_nodes { - let node = ctx.swarm().validator_mut(*n).unwrap(); - println!("Node {} is going to restart", node.name()); - runtime.block_on(node.start())?; - } - - Ok(()) - } -} diff --git a/testsuite/testcases/src/performance_test.rs b/testsuite/testcases/src/performance_test.rs deleted file mode 100644 index f602ede7d437f..0000000000000 --- a/testsuite/testcases/src/performance_test.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::NetworkLoadTest; -use aptos_forge::{NetworkContext, NetworkTest, Result, Test}; - -pub struct PerformanceBenchmark; - -impl Test for PerformanceBenchmark { - fn name(&self) -> &'static str { - "performance benchmark" - } -} - -impl NetworkLoadTest for PerformanceBenchmark {} - -impl NetworkTest for PerformanceBenchmark { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/public_fullnode_performance.rs b/testsuite/testcases/src/public_fullnode_performance.rs deleted file mode 100644 index 1cfa3433f7281..0000000000000 --- a/testsuite/testcases/src/public_fullnode_performance.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - modifiers::create_swarm_cpu_stress, - multi_region_network_test::create_multi_region_swarm_network_chaos, LoadDestination, - NetworkLoadTest, -}; -use anyhow::Error; -use aptos_config::config::OverrideNodeConfig; -use aptos_forge::{ - NetworkContext, NetworkTest, Result, Swarm, SwarmChaos, SwarmCpuStress, SwarmNetEm, Test, -}; -use aptos_logger::info; -use aptos_sdk::move_types::account_address::AccountAddress; -use aptos_types::PeerId; -use itertools::{EitherOrBoth, Itertools}; -use rand::{ - rngs::{OsRng, StdRng}, - seq::SliceRandom, - Rng, SeedableRng, -}; -use std::iter::once; -use tokio::runtime::Runtime; - -/// A simple test that adds multiple public fullnodes (PFNs) to the swarm -/// and submits transactions through them. Network emulation chaos can also -/// be configured for all nodes in the swarm. -#[derive(Default)] -pub struct PFNPerformance { - num_pfns: u64, - add_cpu_chaos: bool, - add_network_emulation: bool, - shuffle_rng_seed: [u8; 32], -} - -impl PFNPerformance { - pub fn new(num_pfns: u64, add_cpu_chaos: bool, add_network_emulation: bool) -> Self { - // Create a random seed for the shuffle RNG - let shuffle_rng_seed: [u8; 32] = OsRng.gen(); - - Self { - num_pfns, - add_cpu_chaos, - add_network_emulation, - shuffle_rng_seed, - } - } - - /// Creates CPU chaos for the swarm. Note: CPU chaos is added - /// to all validators, VFNs and PFNs in the swarm. - fn create_cpu_chaos(&self, swarm: &mut dyn Swarm) -> SwarmCpuStress { - // Gather and shuffle all peers IDs (so that we get random CPU chaos) - let shuffled_peer_ids = self.gather_and_shuffle_peer_ids(swarm); - - // Create CPU chaos for the swarm - create_swarm_cpu_stress(shuffled_peer_ids, None) - } - - /// Creates network emulation chaos for the swarm. Note: network chaos - /// is added to all validators, VFNs and PFNs in the swarm. - fn create_network_emulation_chaos(&self, swarm: &mut dyn Swarm) -> SwarmNetEm { - // Gather and shuffle all peers IDs (so that we get random network emulation) - let shuffled_peer_ids = self.gather_and_shuffle_peer_ids_with_colocation(swarm); - - // Create network emulation chaos for the swarm - create_multi_region_swarm_network_chaos(shuffled_peer_ids, None) - } - - /// Gathers and shuffles all peer IDs in the swarm - fn gather_and_shuffle_peer_ids(&self, swarm: &mut dyn Swarm) -> Vec { - // Identify the validators and fullnodes in the swarm - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let fullnode_peer_ids = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - - // Gather and shuffle all peers IDs - let mut all_peer_ids = validator_peer_ids - .iter() - .chain(fullnode_peer_ids.iter()) - .cloned() - .collect::>(); - all_peer_ids.shuffle(&mut StdRng::from_seed(self.shuffle_rng_seed)); - - all_peer_ids - } - - /// Gathers and shuffles all peer IDs in the swarm, colocating VFNs with their validator - fn gather_and_shuffle_peer_ids_with_colocation( - &self, - swarm: &mut dyn Swarm, - ) -> Vec> { - // Identify the validators and fullnodes in the swarm - let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); - let fullnode_peer_ids = swarm.full_nodes().map(|v| v.peer_id()).collect::>(); - let (vfn_peer_ids, pfn_peer_ids) = - fullnode_peer_ids.split_at(fullnode_peer_ids.len() - self.num_pfns as usize); - let mut vfn_and_vn_ids: Vec<_> = validator_peer_ids - .iter() - .zip_longest(vfn_peer_ids) - .map(|either_or_both| match either_or_both { - EitherOrBoth::Both(validator, vfn) => vec![*validator, *vfn], - EitherOrBoth::Left(validator) => vec![*validator], - EitherOrBoth::Right(_) => panic!("Unexpected"), - }) - .collect(); - vfn_and_vn_ids.shuffle(&mut StdRng::from_seed(self.shuffle_rng_seed)); - - // All PFNs in the first region - once(pfn_peer_ids.to_vec()).chain(vfn_and_vn_ids).collect() - } -} - -impl Test for PFNPerformance { - fn name(&self) -> &'static str { - "PFNPerformance" - } -} - -impl NetworkTest for PFNPerformance { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} - -impl NetworkLoadTest for PFNPerformance { - /// We must override the setup function to: (i) create PFNs in - /// the swarm; and (ii) use those PFNs as the load destination. - fn setup(&self, ctx: &mut NetworkContext) -> Result { - // Add the PFNs to the swarm - let pfn_peer_ids = create_and_add_pfns(ctx, self.num_pfns)?; - - // Add CPU chaos to the swarm - if self.add_cpu_chaos { - let cpu_chaos = self.create_cpu_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.inject_chaos(SwarmChaos::CpuStress(cpu_chaos)))?; - } - - // Add network emulation to the swarm - if self.add_network_emulation { - let network_chaos = self.create_network_emulation_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.inject_chaos(SwarmChaos::NetEm(network_chaos)))?; - } - - // Use the PFNs as the load destination - Ok(LoadDestination::Peers(pfn_peer_ids)) - } - - fn finish(&self, ctx: &mut NetworkContext) -> Result<()> { - // Remove CPU chaos from the swarm - if self.add_cpu_chaos { - let cpu_chaos = self.create_cpu_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.remove_chaos(SwarmChaos::CpuStress(cpu_chaos)))?; - } - - // Remove network emulation from the swarm - if self.add_network_emulation { - let network_chaos = self.create_network_emulation_chaos(ctx.swarm); - ctx.runtime - .block_on(ctx.swarm.remove_chaos(SwarmChaos::NetEm(network_chaos)))?; - } - - Ok(()) - } -} - -/// Adds a number of PFNs to the network and returns the peer IDs -fn create_and_add_pfns(ctx: &mut NetworkContext, num_pfns: u64) -> Result, Error> { - info!("Creating {} public fullnodes!", num_pfns); - - // Identify the version for the PFNs - let swarm = ctx.swarm(); - let pfn_version = swarm.versions().max().unwrap(); - - // Create the PFN swarm - let runtime = Runtime::new().unwrap(); - let pfn_peer_ids: Vec = (0..num_pfns) - .map(|i| { - // Create a config for the PFN. Note: this needs to be done here - // because the config will generate a unique peer ID for the PFN. - let pfn_config = swarm.get_default_pfn_node_config(); - let pfn_override_config = OverrideNodeConfig::new_with_default_base(pfn_config); - - // Add the PFN to the swarm - let peer_id = runtime - .block_on(swarm.add_full_node(&pfn_version, pfn_override_config)) - .unwrap(); - - // Verify the PFN was added - if swarm.full_node(peer_id).is_none() { - panic!( - "Failed to locate PFN {:?} in the swarm! Peer ID: {:?}", - i, peer_id - ); - } - - // Return the peer ID - info!("Created PFN {:?} with peer ID: {:?}", i, peer_id); - peer_id - }) - .collect(); - - Ok(pfn_peer_ids) -} diff --git a/testsuite/testcases/src/quorum_store_onchain_enable_test.rs b/testsuite/testcases/src/quorum_store_onchain_enable_test.rs deleted file mode 100644 index 63c8e6e505fa6..0000000000000 --- a/testsuite/testcases/src/quorum_store_onchain_enable_test.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{generate_onchain_config_blob, NetworkLoadTest}; -use anyhow::Ok; -use aptos::test::CliTestFramework; -use aptos_forge::{NetworkTest, NodeExt, SwarmExt, Test}; -use aptos_logger::info; -use aptos_sdk::bcs; -use aptos_types::{ - account_config::CORE_CODE_ADDRESS, - on_chain_config::{ConsensusConfigV1, OnChainConsensusConfig}, -}; -use std::time::Duration; -use tokio::runtime::Runtime; - -const MAX_NODE_LAG_SECS: u64 = 360; - -pub struct QuorumStoreOnChainEnableTest {} - -impl Test for QuorumStoreOnChainEnableTest { - fn name(&self) -> &'static str { - "quorum-store reconfig enable test" - } -} - -impl NetworkLoadTest for QuorumStoreOnChainEnableTest { - fn test( - &self, - swarm: &mut dyn aptos_forge::Swarm, - _report: &mut aptos_forge::TestReport, - duration: std::time::Duration, - ) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - - let faucet_endpoint: reqwest::Url = "http://localhost:8081".parse().unwrap(); - let rest_client = swarm.validators().next().unwrap().rest_client(); - - let mut cli = runtime.block_on(async { - CliTestFramework::new( - swarm.validators().next().unwrap().rest_api_endpoint(), - faucet_endpoint, - /*num_cli_accounts=*/ 0, - ) - .await - }); - - std::thread::sleep(duration / 2); - - runtime.block_on(async { - - let root_cli_index = cli.add_account_with_address_to_cli( - swarm.chain_info().root_account().private_key().clone(), - swarm.chain_info().root_account().address(), - ); - - let current_consensus_config: OnChainConsensusConfig = bcs::from_bytes( - &rest_client - .get_account_resource_bcs::>( - CORE_CODE_ADDRESS, - "0x1::consensus_config::ConsensusConfig", - ) - .await - .unwrap() - .into_inner(), - ) - .unwrap(); - - let inner = match current_consensus_config { - OnChainConsensusConfig::V1(inner) => inner, - OnChainConsensusConfig::V2(_) => panic!("Unexpected V2 config"), - _ => unimplemented!() - }; - - // Change to V2 - let new_consensus_config = OnChainConsensusConfig::V2(ConsensusConfigV1 { ..inner }); - - let update_consensus_config_script = format!( - r#" - script {{ - use aptos_framework::aptos_governance; - use aptos_framework::consensus_config; - fun main(core_resources: &signer) {{ - let framework_signer = aptos_governance::get_signer_testnet_only(core_resources, @0000000000000000000000000000000000000000000000000000000000000001); - let config_bytes = {}; - consensus_config::set(&framework_signer, config_bytes); - }} - }} - "#, - generate_onchain_config_blob(&bcs::to_bytes(&new_consensus_config).unwrap()) - ); - - cli.run_script_with_default_framework(root_cli_index, &update_consensus_config_script) - .await - })?; - - std::thread::sleep(duration / 2); - - // Wait for all nodes to synchronize and stabilize. - info!("Waiting for the validators to be synchronized."); - runtime.block_on(async { - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_NODE_LAG_SECS)) - .await - })?; - - Ok(()) - } -} - -impl NetworkTest for QuorumStoreOnChainEnableTest { - fn run(&self, ctx: &mut aptos_forge::NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/reconfiguration_test.rs b/testsuite/testcases/src/reconfiguration_test.rs deleted file mode 100644 index 57f99767eb194..0000000000000 --- a/testsuite/testcases/src/reconfiguration_test.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::anyhow; -use aptos_forge::{NetworkContext, NetworkTest, Result, Test}; - -pub struct ReconfigurationTest; - -impl Test for ReconfigurationTest { - fn name(&self) -> &'static str { - "reconfiguration-test" - } -} - -impl NetworkTest for ReconfigurationTest { - fn run(&self, _ctx: &mut NetworkContext<'_>) -> Result<()> { - Err(anyhow!("Not supported in aptos-framework yet")) - } - // TODO(https://github.com/aptos-labs/aptos-core/issues/317): add back after support those transactions in aptos-framework - // let rt = Runtime::new()?; - // - // let mut rng = StdRng::from_seed(OsRng.gen()); - // let client = OperationalTool::new(ctx.swarm().chain_info().rest_api().to_owned()); - // let validator_info = rt - // .block_on(client.validator_set(None)) - // .expect("Unable to fetch validator set"); - // let affected_peer_id = *validator_info[0].account_address(); - // let validator_config = rt - // .block_on(client.validator_config(affected_peer_id)) - // .expect("Unable to fetch validator config"); - // let affected_pod_name = std::str::from_utf8(&validator_config.human_name) - // .unwrap() - // .to_string(); - // let validator_clients = ctx - // .swarm() - // .validators() - // .map(|n| n.rest_client()) - // .collect::>(); - // let tx_factory = TransactionFactory::new(ctx.swarm().chain_info().chain_id); - // let mut aptos_root_account = ctx.swarm().chain_info().root_account; - // let allowed_nonce = 0; - // let full_node_client = validator_clients.iter().choose(&mut rng).unwrap(); - // let timer = Instant::now(); - // let count = 101; - // - // rt.block_on(async { - // expect_epoch(full_node_client, 1).await.unwrap(); - // { - // println!("Remove and add back {}.", affected_pod_name); - // let validator_name = affected_pod_name.as_bytes().to_vec(); - // let remove_txn = aptos_root_account.sign_with_transaction_builder( - // tx_factory.remove_validator_and_reconfigure( - // allowed_nonce, - // validator_name.clone(), - // affected_peer_id, - // ), - // ); - // execute_and_wait_transactions( - // full_node_client, - // &mut aptos_root_account, - // vec![remove_txn], - // ) - // .await - // .unwrap(); - // expect_epoch(full_node_client, 2).await.unwrap(); - // let add_txn = aptos_root_account.sign_with_transaction_builder( - // tx_factory.add_validator_and_reconfigure( - // allowed_nonce, - // validator_name.clone(), - // affected_peer_id, - // ), - // ); - // execute_and_wait_transactions( - // full_node_client, - // &mut aptos_root_account, - // vec![add_txn], - // ) - // .await - // .unwrap(); - // expect_epoch(full_node_client, 3).await.unwrap(); - // } - // - // { - // println!("Switch decoupled-execution on and off repetitively."); - // let upgrade_config = OnChainConsensusConfig::V2(ConsensusConfigV2 { - // two_chain: true, - // decoupled_execution: true, - // back_pressure_limit: 10, - // exclude_round: 20, - // }); - // let downgrade_config = OnChainConsensusConfig::default(); - // for i in 1..count / 2 { - // let upgrade_txn = aptos_root_account.sign_with_transaction_builder( - // tx_factory.update_aptos_consensus_config( - // allowed_nonce, - // bcs::to_bytes(&upgrade_config).unwrap(), - // ), - // ); - // execute_and_wait_transactions( - // full_node_client, - // &mut aptos_root_account, - // vec![upgrade_txn], - // ) - // .await - // .unwrap(); - // expect_epoch(full_node_client, (i + 1) * 2).await.unwrap(); - // let downgrade_txn = aptos_root_account.sign_with_transaction_builder( - // tx_factory.update_aptos_consensus_config( - // allowed_nonce, - // bcs::to_bytes(&downgrade_config).unwrap(), - // ), - // ); - // execute_and_wait_transactions( - // full_node_client, - // &mut aptos_root_account, - // vec![downgrade_txn], - // ) - // .await - // .unwrap(); - // expect_epoch(full_node_client, (i + 1) * 2 + 1) - // .await - // .unwrap(); - // } - // } - // - // if count % 2 == 1 { - // let magic_number = 42; - // println!("Bump Version to {}", magic_number); - // let update_txn = aptos_root_account.sign_with_transaction_builder( - // tx_factory.update_aptos_version(allowed_nonce, magic_number), - // ); - // execute_and_wait_transactions( - // full_node_client, - // &mut aptos_root_account, - // vec![update_txn], - // ) - // .await - // .unwrap(); - // expect_epoch(full_node_client, count + 1).await.unwrap(); - // } - // }); - // - // let elapsed = timer.elapsed(); - // ctx.report.report_text(format!( - // "Reconfiguration: total epoch: {} finished in {} seconds", - // count, - // elapsed.as_secs() - // )); - // - // Ok(()) - // } -} - -// async fn expect_epoch(client: &RestClient, expected_epoch: u64) -> anyhow::Result<()> { -// let config = client.get_epoch_configuration().await?.into_inner(); -// let next_block_epoch = *config.next_block_epoch.inner(); -// ensure!( -// next_block_epoch == expected_epoch, -// "Expect next block epoch {}, actual {}", -// expected_epoch, -// next_block_epoch -// ); -// Ok(()) -// } diff --git a/testsuite/testcases/src/state_sync_performance.rs b/testsuite/testcases/src/state_sync_performance.rs deleted file mode 100644 index 22f43cc7a1569..0000000000000 --- a/testsuite/testcases/src/state_sync_performance.rs +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::generate_traffic; -use anyhow::bail; -use aptos_forge::{ - get_highest_synced_epoch, get_highest_synced_version, NetworkContext, NetworkTest, Result, - SwarmExt, Test, -}; -use aptos_logger::info; -use aptos_sdk::move_types::account_address::AccountAddress; -use std::time::Instant; -use tokio::{runtime::Runtime, time::Duration}; - -const MAX_EPOCH_CHANGE_SECS: u64 = 300; // Max amount of time (in seconds) to wait for an epoch change -const MAX_NODE_LAG_SECS: u64 = 30; // Max amount of lag (in seconds) that nodes should adhere to -const NUM_STATE_VALUE_COUNTER_NAME: &str = "aptos_jellyfish_leaf_count"; // The metric to fetch for the number of state values - -/// A state sync performance test that measures fullnode sync performance. -/// In the test, all fullnodes are wiped, restarted and timed to synchronize. -pub struct StateSyncFullnodePerformance; - -impl Test for StateSyncFullnodePerformance { - fn name(&self) -> &'static str { - "StateSyncFullnodePerformance" - } -} - -impl NetworkTest for StateSyncFullnodePerformance { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let all_fullnodes = get_fullnodes_and_check_setup(ctx, self.name())?; - - // Emit a lot of traffic and ensure the fullnodes can all sync - emit_traffic_and_ensure_bounded_sync(ctx, &all_fullnodes)?; - - // Stop and reset the fullnodes so they start syncing from genesis - stop_and_reset_nodes(ctx, &all_fullnodes, &[])?; - - // Wait for all nodes to catch up to the highest synced version - // then calculate and display the throughput results. - ensure_state_sync_transaction_throughput(ctx, self.name()) - } -} - -/// A state sync performance test that measures fast sync performance. -/// In the test, all fullnodes are wiped, restarted and timed to synchronize. -pub struct StateSyncFullnodeFastSyncPerformance; - -impl Test for StateSyncFullnodeFastSyncPerformance { - fn name(&self) -> &'static str { - "StateSyncFullnodeFastSyncPerformance" - } -} - -impl NetworkTest for StateSyncFullnodeFastSyncPerformance { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - let all_fullnodes = get_fullnodes_and_check_setup(ctx, self.name())?; - - // Emit a lot of traffic and ensure the fullnodes can all sync - emit_traffic_and_ensure_bounded_sync(ctx, &all_fullnodes)?; - - // Wait for an epoch change to ensure fast sync can download all the latest states - info!("Waiting for an epoch change."); - let runtime = Runtime::new().unwrap(); - runtime.block_on(async { - ctx.swarm() - .wait_for_all_nodes_to_change_epoch(Duration::from_secs(MAX_EPOCH_CHANGE_SECS)) - .await - })?; - - // Get the highest known epoch in the chain - let highest_synced_epoch = runtime.block_on(async { - get_highest_synced_epoch(&ctx.swarm().get_all_nodes_clients_with_names()) - .await - .unwrap_or(0) - }); - if highest_synced_epoch == 0 { - return Err(anyhow::format_err!( - "The swarm has synced 0 epochs! Something has gone wrong!" - )); - } - - // Fetch the number of state values held on-chain - let fullnode_name = ctx.swarm().full_nodes().next().unwrap().name(); - let prom_query = format!( - "{}{{instance=\"{}\"}}", - NUM_STATE_VALUE_COUNTER_NAME, &fullnode_name - ); - let promql_result = runtime.block_on(ctx.swarm().query_metrics(&prom_query, None, None))?; - let number_of_state_values = match promql_result.as_instant().unwrap().first() { - Some(instant_vector) => instant_vector.sample().value() as u64, - None => { - return Err(anyhow::format_err!( - "No instant vectors found for prom query {}", - prom_query - )); - }, - }; - info!( - "Number of reported state values found on-chain is: {}", - number_of_state_values - ); - - // Stop and reset the fullnodes so they start syncing from genesis - stop_and_reset_nodes(ctx, &all_fullnodes, &[])?; - - // Wait for all nodes to catch up to the highest synced epoch - // then calculate and display the throughput results. - display_state_sync_state_throughput( - ctx, - self.name(), - highest_synced_epoch, - number_of_state_values, - )?; - - // TODO: add a minimum expected throughput that could fail the test - Ok(()) - } -} - -/// A state sync performance test that measures validator sync performance. -/// In the test, 2 validators are wiped, restarted and timed to synchronize. -pub struct StateSyncValidatorPerformance; - -impl Test for StateSyncValidatorPerformance { - fn name(&self) -> &'static str { - "StateSyncValidatorPerformance" - } -} - -impl NetworkTest for StateSyncValidatorPerformance { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - // Verify we have at least 7 validators (i.e., 3f+1, where f is 2) - // so we can kill 2 validators but still make progress. - let all_validators = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - let num_validators = all_validators.len(); - if num_validators < 7 { - return Err(anyhow::format_err!( - "State sync validator performance tests require at least 7 validators! Given: {:?} \ - This is to ensure the chain can still make progress when 2 validators are killed.", - num_validators - )); - } - - // Log the test setup - info!( - "Running state sync test {:?} with {:?} validators.", - self.name(), - num_validators, - ); - - // Generate some traffic through the validators. - emit_traffic_and_ensure_bounded_sync(ctx, &all_validators)?; - - // Stop and reset two validators so they start syncing from genesis - info!("Deleting data for two validators!"); - let validators_to_reset = &all_validators[0..2]; - stop_and_reset_nodes(ctx, &[], validators_to_reset)?; - - // Wait for all nodes to catch up to the highest synced version - // then calculate and display the throughput results. - ensure_state_sync_transaction_throughput(ctx, self.name()) - } -} - -/// Verifies the setup for the given fullnode test and returns the -/// set of fullnodes. -fn get_fullnodes_and_check_setup( - ctx: &mut NetworkContext, - test_name: &'static str, -) -> Result> { - // Verify we have at least 1 fullnode - let all_fullnodes = ctx - .swarm() - .full_nodes() - .map(|v| v.peer_id()) - .collect::>(); - if all_fullnodes.is_empty() { - return Err(anyhow::format_err!( - "Fullnode test {} requires at least 1 fullnode!", - test_name - )); - } - - // Log the test setup - info!( - "Running state sync test {:?} with {:?} validators and {:?} fullnodes.", - test_name, - ctx.swarm().validators().count(), - all_fullnodes.len() - ); - - Ok(all_fullnodes) -} - -/// Emits traffic through all specified nodes and ensures all nodes can -/// sync within a reasonable time bound. -fn emit_traffic_and_ensure_bounded_sync( - ctx: &mut NetworkContext, - nodes_to_send_traffic: &[AccountAddress], -) -> Result<()> { - // Generate some traffic through the specified nodes. - // We do this for half the test time. - let emit_txn_duration = ctx.global_duration.checked_div(2).unwrap(); - info!( - "Generating the initial traffic for {:?} seconds.", - emit_txn_duration.as_secs() - ); - let _txn_stat = generate_traffic(ctx, nodes_to_send_traffic, emit_txn_duration)?; - - // Wait for all nodes to synchronize. We time bound this to ensure - // nodes don't fall too far behind. - info!("Waiting for the validators and fullnodes to be synchronized."); - Runtime::new().unwrap().block_on(async { - ctx.swarm() - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_NODE_LAG_SECS)) - .await - })?; - - Ok(()) -} - -/// Stops and resets all specified nodes -fn stop_and_reset_nodes( - ctx: &mut NetworkContext, - fullnodes_to_reset: &[AccountAddress], - validators_to_reset: &[AccountAddress], -) -> Result<()> { - let runtime = Runtime::new().unwrap(); - - // Stop and reset all fullnodes - info!("Deleting all fullnode data!"); - for fullnode_id in fullnodes_to_reset { - let fullnode = ctx.swarm().full_node_mut(*fullnode_id).unwrap(); - runtime.block_on(async { fullnode.clear_storage().await })?; - } - - // Stop and reset all validators - info!("Deleting all validator data!"); - for valdiator_id in validators_to_reset { - let validator = ctx.swarm().validator_mut(*valdiator_id).unwrap(); - runtime.block_on(async { validator.clear_storage().await })?; - } - - // Restart the fullnodes so they start syncing from a fresh state - for fullnode_id in fullnodes_to_reset { - let fullnode = ctx.swarm().full_node_mut(*fullnode_id).unwrap(); - runtime.block_on(async { fullnode.start().await })?; - } - - // Restart the validators so they start syncing from a fresh state - for valdiator_id in validators_to_reset { - let validator = ctx.swarm().validator_mut(*valdiator_id).unwrap(); - runtime.block_on(async { validator.start().await })?; - } - - Ok(()) -} - -/// Calculates and displays the state sync state value throughput -/// when fast syncing to the latest epoch. -fn display_state_sync_state_throughput( - ctx: &mut NetworkContext<'_>, - test_name: &str, - highest_synced_epoch: u64, - number_of_state_values: u64, -) -> Result<()> { - // Start the timer - let timer = Instant::now(); - let runtime = Runtime::new().unwrap(); - - // Wait for all nodes to catch up to the same epoch (that is when fast sync completes). - // We allow up to half the test time to do this. - let node_sync_duration = ctx.global_duration.checked_div(2).unwrap(); - runtime.block_on(async { - ctx.swarm() - .wait_for_all_nodes_to_catchup_to_epoch(highest_synced_epoch, node_sync_duration) - .await - })?; - - // Stop the syncing timer - let seconds_to_sync = timer.elapsed().as_secs(); - if seconds_to_sync == 0 { - return Err(anyhow::format_err!( - "The time taken to state sync was 0 seconds! Something has gone wrong!" - )); - } - - // Calculate and report the syncing throughput - let state_sync_throughput = number_of_state_values / seconds_to_sync; - let throughput_message = format!( - "State sync throughput : {} state values / sec", - state_sync_throughput - ); - info!("Measured state sync throughput: {:?}", throughput_message); - ctx.report.report_text(throughput_message); - ctx.report.report_metric( - test_name, - "state_sync_throughput", - state_sync_throughput as f64, - ); - - Ok(()) -} - -/// Calculates, enforces and displays the state sync transaction -/// throughput using the synced version and sync duration. -fn ensure_state_sync_transaction_throughput( - ctx: &mut NetworkContext<'_>, - test_name: &str, -) -> Result<()> { - // Start the timer - let timer = Instant::now(); - - // Get the highest synced version for the chain - let runtime = Runtime::new().unwrap(); - let highest_synced_version = runtime.block_on(async { - get_highest_synced_version(&ctx.swarm().get_all_nodes_clients_with_names()) - .await - .unwrap_or(0) - }); - if highest_synced_version == 0 { - return Err(anyhow::format_err!( - "The swarm has synced 0 versions! Something has gone wrong!" - )); - } - - // Wait for all nodes to catch up to the same synced version. - // We allow up to half the test time to do this. - let node_sync_duration = ctx.global_duration.checked_div(2).unwrap(); - runtime.block_on(async { - ctx.swarm() - .wait_for_all_nodes_to_catchup(node_sync_duration) - .await - })?; - - // Calculate the state sync throughput - let seconds_to_sync = timer.elapsed().as_secs(); - if seconds_to_sync == 0 { - return Err(anyhow::format_err!( - "The time taken to state sync was 0 seconds! Something has gone wrong!" - )); - } - let state_sync_throughput = highest_synced_version / seconds_to_sync; - - // Report the state sync results - let throughput_message = format!("State sync throughput : {} txn/sec", state_sync_throughput); - info!("Measured state sync throughput: {:?}", throughput_message); - ctx.report.report_text(throughput_message); - ctx.report.report_metric( - test_name, - "state_sync_throughput", - state_sync_throughput as f64, - ); - - // TODO: we fetch the TPS requirement from the given success criteria. - // But, we should probably make it more generic to avoid this. - // Ensure we meet the success criteria. - let min_expected_tps = ctx.success_criteria.min_avg_tps as u64; - if state_sync_throughput < min_expected_tps { - let error_message = format!( - "State sync TPS requirement failed. Average TPS: {}, minimum required TPS: {}", - state_sync_throughput, min_expected_tps - ); - bail!(error_message) - } - - Ok(()) -} diff --git a/testsuite/testcases/src/three_region_simulation_test.rs b/testsuite/testcases/src/three_region_simulation_test.rs deleted file mode 100644 index 916bbcc86c322..0000000000000 --- a/testsuite/testcases/src/three_region_simulation_test.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos_forge::{ - GroupNetworkBandwidth, GroupNetworkDelay, NetworkContext, NetworkTest, Swarm, SwarmChaos, - SwarmNetworkBandwidth, SwarmNetworkDelay, Test, -}; -use aptos_logger::info; - -/// Represents a test that simulates a network with 3 regions, all in the same cloud. -pub struct ThreeRegionSameCloudSimulationTest; - -impl Test for ThreeRegionSameCloudSimulationTest { - fn name(&self) -> &'static str { - "network::three-region-simulation" - } -} - -/// Create a SwarmNetworkDelay with the following topology: -/// 1. 3 equal size group of nodes, each in a different region -/// 2. Each region has minimal network delay amongst its nodes -/// 3. Each region has a network delay to the other two regions, as estimated by https://www.cloudping.co/grid -/// 4. Currently simulating a 50 percentile network delay between us-west <--> af-south <--> eu-north -fn create_three_region_swarm_network_delay(swarm: &dyn Swarm) -> SwarmNetworkDelay { - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - - // each region has 1/3 of the validators - let region_size = all_validators.len() / 3; - let mut us_west = all_validators; - let mut af_south = us_west.split_off(region_size); - let eu_north = af_south.split_off(region_size); - - let group_network_delays = vec![ - GroupNetworkDelay { - name: "us-west-to-af-south".to_string(), - source_nodes: us_west.clone(), - target_nodes: af_south.clone(), - latency_ms: 300, - jitter_ms: 50, - correlation_percentage: 50, - }, - GroupNetworkDelay { - name: "us-west-to-eu-north".to_string(), - source_nodes: us_west.clone(), - target_nodes: eu_north.clone(), - latency_ms: 150, - jitter_ms: 50, - correlation_percentage: 50, - }, - GroupNetworkDelay { - name: "eu-north-to-af-south".to_string(), - source_nodes: eu_north.clone(), - target_nodes: af_south.clone(), - latency_ms: 200, - jitter_ms: 50, - correlation_percentage: 50, - }, - ]; - - info!("US_WEST: {:?}", us_west); - info!("AF_SOUTH B: {:?}", af_south); - info!("EU_NORTH C: {:?}", eu_north); - - SwarmNetworkDelay { - group_network_delays, - } -} - -/// 1000 mbps network bandwidth simulation between all regions within -/// the same cloud with dedicated backbone like GCP -fn create_bandwidth_limit() -> SwarmNetworkBandwidth { - SwarmNetworkBandwidth { - group_network_bandwidths: vec![GroupNetworkBandwidth { - name: "forge-namespace-1000mbps-bandwidth".to_owned(), - rate: 1000, // 1000 megabytes per second - limit: 20971520, - buffer: 10000, - }], - } -} - -impl NetworkLoadTest for ThreeRegionSameCloudSimulationTest { - fn setup(&self, ctx: &mut NetworkContext) -> anyhow::Result { - // inject network delay - let delay = create_three_region_swarm_network_delay(ctx.swarm()); - let chaos = SwarmChaos::Delay(delay); - ctx.runtime.block_on(ctx.swarm.inject_chaos(chaos))?; - - // inject bandwidth limit - let bandwidth = create_bandwidth_limit(); - let chaos = SwarmChaos::Bandwidth(bandwidth); - ctx.runtime.block_on(ctx.swarm.inject_chaos(chaos))?; - - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn finish(&self, ctx: &mut NetworkContext) -> anyhow::Result<()> { - ctx.runtime.block_on(ctx.swarm.remove_all_chaos())?; - Ok(()) - } -} - -impl NetworkTest for ThreeRegionSameCloudSimulationTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/twin_validator_test.rs b/testsuite/testcases/src/twin_validator_test.rs deleted file mode 100644 index 53905027f22b5..0000000000000 --- a/testsuite/testcases/src/twin_validator_test.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::NetworkLoadTest; -use anyhow::Context; -use aptos_forge::{NetworkContext, NetworkTest, NodeExt, Test}; -use aptos_sdk::move_types::account_address::AccountAddress; -use std::time::{Duration, Instant}; -use tokio::runtime::Runtime; - -pub struct TwinValidatorTest; - -impl Test for TwinValidatorTest { - fn name(&self) -> &'static str { - "twin validator" - } -} - -impl NetworkLoadTest for TwinValidatorTest {} - -impl NetworkTest for TwinValidatorTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> anyhow::Result<()> { - let runtime = Runtime::new().unwrap(); - - let all_validators_ids = ctx - .swarm() - .validators() - .map(|v| v.peer_id()) - .collect::>(); - let validator_count = all_validators_ids.len(); - let twin_count = 2; - runtime.block_on(async { - for i in 0..twin_count { - let main_id: AccountAddress = all_validators_ids[i]; - let twin_id = all_validators_ids[i + validator_count - twin_count]; - ctx.swarm() - .validator_mut(twin_id) - .unwrap() - .clear_storage() - .await - .context(format!( - "Error while clearing storage and stopping {twin_id}" - ))?; - let main_identity = ctx - .swarm() - .validator_mut(main_id) - .unwrap() - .get_identity() - .await - .context(format!("Error while getting identity for {main_id}"))?; - ctx.swarm() - .validator_mut(twin_id) - .unwrap() - .set_identity(main_identity) - .await - .context(format!("Error while setting identity for {twin_id}"))?; - ctx.swarm() - .validator_mut(twin_id) - .unwrap() - .start() - .await - .context(format!("Error while starting {twin_id}"))?; - ctx.swarm() - .validator_mut(twin_id) - .unwrap() - .wait_until_healthy(Instant::now() + Duration::from_secs(300)) - .await - .context(format!("Error while waiting for {twin_id}"))?; - } - Ok::<(), anyhow::Error>(()) - })?; - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/two_traffics_test.rs b/testsuite/testcases/src/two_traffics_test.rs deleted file mode 100644 index dd824174a4ee9..0000000000000 --- a/testsuite/testcases/src/two_traffics_test.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - create_emitter_and_request, traffic_emitter_runtime, LoadDestination, NetworkLoadTest, -}; -use aptos_forge::{ - success_criteria::{SuccessCriteria, SuccessCriteriaChecker}, - EmitJobRequest, NetworkContext, NetworkTest, Result, Swarm, Test, TestReport, -}; -use aptos_logger::info; -use rand::{rngs::OsRng, Rng, SeedableRng}; -use std::time::{Duration, Instant}; - -pub struct TwoTrafficsTest { - pub inner_traffic: EmitJobRequest, - pub inner_success_criteria: SuccessCriteria, -} - -impl Test for TwoTrafficsTest { - fn name(&self) -> &'static str { - "two traffics test" - } -} - -impl NetworkLoadTest for TwoTrafficsTest { - fn test( - &self, - swarm: &mut dyn Swarm, - report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - info!( - "Running TwoTrafficsTest test for duration {}s", - duration.as_secs_f32() - ); - let nodes_to_send_load_to = - LoadDestination::FullnodesOtherwiseValidators.get_destination_nodes(swarm); - let rng = ::rand::rngs::StdRng::from_seed(OsRng.gen()); - - let (emitter, emit_job_request) = create_emitter_and_request( - swarm, - self.inner_traffic.clone(), - &nodes_to_send_load_to, - rng, - )?; - - let rt = traffic_emitter_runtime()?; - - let test_start = Instant::now(); - - let stats = rt.block_on(emitter.emit_txn_for( - swarm.chain_info().root_account, - emit_job_request, - duration, - ))?; - - let actual_test_duration = test_start.elapsed(); - info!( - "End to end duration: {}s, while txn emitter lasted: {}s", - actual_test_duration.as_secs(), - stats.lasted.as_secs() - ); - - let rate = stats.rate(); - - report.report_txn_stats(format!("{}: inner traffic", self.name()), &stats); - - SuccessCriteriaChecker::check_core_for_success( - &self.inner_success_criteria, - report, - &rate, - None, - Some("inner traffic".to_string()), - )?; - Ok(()) - } -} - -impl NetworkTest for TwoTrafficsTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/src/validator_join_leave_test.rs b/testsuite/testcases/src/validator_join_leave_test.rs deleted file mode 100644 index 092871293febc..0000000000000 --- a/testsuite/testcases/src/validator_join_leave_test.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{LoadDestination, NetworkLoadTest}; -use aptos::{account::create::DEFAULT_FUNDED_COINS, test::CliTestFramework}; -use aptos_forge::{ - reconfig, NetworkContext, NetworkTest, NodeExt, Result, Swarm, SwarmExt, Test, TestReport, - FORGE_KEY_SEED, -}; -use aptos_keygen::KeyGen; -use aptos_logger::info; -use aptos_sdk::crypto::{ed25519::Ed25519PrivateKey, PrivateKey}; -use aptos_types::{account_address::AccountAddress, transaction::authenticator::AuthenticationKey}; -use std::time::Duration; -use tokio::runtime::Runtime; - -const MAX_NODE_LAG_SECS: u64 = 360; - -pub struct ValidatorJoinLeaveTest; - -impl Test for ValidatorJoinLeaveTest { - fn name(&self) -> &'static str { - "validator join and leave sets" - } -} - -impl NetworkLoadTest for ValidatorJoinLeaveTest { - fn setup(&self, _ctx: &mut NetworkContext) -> Result { - Ok(LoadDestination::FullnodesOtherwiseValidators) - } - - fn test( - &self, - swarm: &mut dyn Swarm, - _report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - // Verify we have at least 7 validators (i.e., 3f+1, where f is 2) - // so we can lose 2 validators but still make progress. - let num_validators = swarm.validators().count(); - if num_validators < 7 { - return Err(anyhow::format_err!( - "ValidatorSet leaving and rejoining test require at least 7 validators! Given: {:?}.", - num_validators - )); - } - - let faucet_endpoint: reqwest::Url = "http://localhost:8081".parse().unwrap(); - // Connect the operator tool to the node's JSON RPC API - let rest_client = swarm.validators().next().unwrap().rest_client(); - let transaction_factory = swarm.chain_info().transaction_factory(); - let runtime = Runtime::new().unwrap(); - - let mut cli = runtime.block_on(async { - CliTestFramework::new( - swarm.validators().next().unwrap().rest_api_endpoint(), - faucet_endpoint, - /*num_cli_accounts=*/ 0, - ) - .await - }); - - let mut public_info = swarm.chain_info().into_aptos_public_info(); - - let mut validator_cli_indices = Vec::new(); - - let starting_seed_in_decimal = i64::from_str_radix(FORGE_KEY_SEED, 16)?; - - for i in 0..num_validators { - // Initialize keyGen to get validator private keys. We uses the same seed in the test - // driver as in the genesis script so that the validator keys are deterministic. - let mut seed_slice = [0u8; 32]; - let seed_in_decimal = starting_seed_in_decimal + (i as i64); - let seed_in_hex_string = format!("{seed_in_decimal:0>64x}"); - - hex::decode_to_slice(seed_in_hex_string, &mut seed_slice)?; - - let mut keygen = KeyGen::from_seed(seed_slice); - - let (validator_cli_index, _keys, account_balance) = runtime.block_on(async { - let (validator_cli_index, keys) = - init_validator_account(&mut cli, &mut keygen).await; - - let auth_key = AuthenticationKey::ed25519(&keys.account_private_key.public_key()); - let validator_account_address = AccountAddress::new(*auth_key.account_address()); - - public_info - .mint(validator_account_address, DEFAULT_FUNDED_COINS) - .await - .unwrap(); - - let account_balance = public_info - .get_balance(validator_account_address) - .await - .unwrap(); - - (validator_cli_index, keys, account_balance) - }); - assert_eq!(account_balance, DEFAULT_FUNDED_COINS); - validator_cli_indices.push(validator_cli_index); - - assert_eq!( - runtime.block_on(get_validator_state(&cli, validator_cli_index)), - ValidatorState::ACTIVE - ); - } - - // Log the test setup - info!( - "Running validator join and leave test {:?} with {:?} validators.", - self.name(), - num_validators, - ); - - // Wait for all nodes to synchronize and stabilize. - info!("Waiting for the validators to be synchronized."); - runtime.block_on(async { - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_NODE_LAG_SECS)) - .await - })?; - - // Wait for 1/3 of the test duration. - std::thread::sleep(duration / 3); - - runtime.block_on(async { - // 1/3 validators leave the validator set. - info!("Make the last 1/3 validators leave the validator set!"); - for operator_index in validator_cli_indices.iter().rev().take(num_validators / 3) { - cli.leave_validator_set(*operator_index, None) - .await - .unwrap(); - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - } - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - }); - - // Wait for 1/3 of the test duration. - std::thread::sleep(duration / 3); - - runtime.block_on(async { - // Rejoining validator set. - info!("Make the last 1/3 validators rejoin the validator set!"); - for operator_index in validator_cli_indices.iter().rev().take(num_validators / 3) { - cli.join_validator_set(*operator_index, None).await.unwrap(); - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - } - - reconfig( - &rest_client, - &transaction_factory, - swarm.chain_info().root_account(), - ) - .await; - }); - - // Wait for all nodes to synchronize and stabilize. - info!("Waiting for the validators to be synchronized."); - runtime.block_on(async { - swarm - .wait_for_all_nodes_to_catchup(Duration::from_secs(MAX_NODE_LAG_SECS)) - .await - })?; - - Ok(()) - } -} - -impl NetworkTest for ValidatorJoinLeaveTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} - -#[derive(Debug, PartialEq, Eq)] -enum ValidatorState { - ACTIVE, - JOINING, - LEAVING, - NONE, -} - -struct ValidatorNodeKeys { - account_private_key: Ed25519PrivateKey, -} - -impl ValidatorNodeKeys { - pub fn new(keygen: &mut KeyGen) -> Self { - Self { - account_private_key: keygen.generate_ed25519_private_key(), - } - } -} - -async fn init_validator_account( - cli: &mut CliTestFramework, - keygen: &mut KeyGen, -) -> (usize, ValidatorNodeKeys) { - let validator_node_keys = ValidatorNodeKeys::new(keygen); - let validator_cli_index = - cli.add_account_to_cli(validator_node_keys.account_private_key.clone()); - (validator_cli_index, validator_node_keys) -} - -async fn get_validator_state(cli: &CliTestFramework, pool_index: usize) -> ValidatorState { - let validator_set = cli.show_validator_set().await.unwrap(); - let pool_address = cli.account_id(pool_index); - - for (state, list) in [ - (ValidatorState::ACTIVE, &validator_set.active_validators), - (ValidatorState::JOINING, &validator_set.pending_active), - (ValidatorState::LEAVING, &validator_set.pending_inactive), - ] { - if list.iter().any(|info| info.account_address == pool_address) { - return state; - } - } - ValidatorState::NONE -} diff --git a/testsuite/testcases/src/validator_reboot_stress_test.rs b/testsuite/testcases/src/validator_reboot_stress_test.rs deleted file mode 100644 index 9355ac97a99d8..0000000000000 --- a/testsuite/testcases/src/validator_reboot_stress_test.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::NetworkLoadTest; -use aptos_forge::{NetworkContext, NetworkTest, Result, Swarm, Test, TestReport}; -use rand::{seq::SliceRandom, thread_rng}; -use std::time::Duration; -use tokio::{runtime::Runtime, time::Instant}; - -pub struct ValidatorRebootStressTest { - pub num_simultaneously: usize, - pub down_time_secs: f32, - pub pause_secs: f32, -} - -impl Test for ValidatorRebootStressTest { - fn name(&self) -> &'static str { - "validator reboot stress test" - } -} - -impl NetworkLoadTest for ValidatorRebootStressTest { - fn test( - &self, - swarm: &mut dyn Swarm, - _report: &mut TestReport, - duration: Duration, - ) -> Result<()> { - let start = Instant::now(); - let runtime = Runtime::new().unwrap(); - - let all_validators = swarm.validators().map(|v| v.peer_id()).collect::>(); - - let mut rng = thread_rng(); - - while start.elapsed() < duration { - let addresses: Vec<_> = all_validators - .choose_multiple(&mut rng, self.num_simultaneously) - .cloned() - .collect(); - for adr in &addresses { - let validator_to_reboot = swarm.validator_mut(*adr).unwrap(); - runtime.block_on(async { validator_to_reboot.stop().await })?; - } - if self.down_time_secs > 0.0 { - std::thread::sleep(Duration::from_secs_f32(self.down_time_secs)); - } - - for adr in &addresses { - let validator_to_reboot = swarm.validator_mut(*adr).unwrap(); - runtime.block_on(async { validator_to_reboot.start().await })?; - } - - if self.pause_secs > 0.0 { - std::thread::sleep(Duration::from_secs_f32(self.pause_secs)); - } - } - - Ok(()) - } -} - -impl NetworkTest for ValidatorRebootStressTest { - fn run(&self, ctx: &mut NetworkContext<'_>) -> Result<()> { - ::run(self, ctx) - } -} diff --git a/testsuite/testcases/tests/forge-local-compatibility.rs b/testsuite/testcases/tests/forge-local-compatibility.rs deleted file mode 100644 index 6b45328d6cba4..0000000000000 --- a/testsuite/testcases/tests/forge-local-compatibility.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_forge::{forge_main, ForgeConfig, InitialVersion, LocalFactory, Options, Result}; -use aptos_testcases::compatibility_test::SimpleValidatorUpgrade; -use std::num::NonZeroUsize; - -fn main() -> Result<()> { - ::aptos_logger::Logger::init_for_testing(); - - let tests = ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(4).unwrap()) - .with_initial_version(InitialVersion::Oldest) - .add_network_test(SimpleValidatorUpgrade); - - let options = Options::parse(); - forge_main( - tests, - LocalFactory::with_upstream_merge_base_and_workspace()?, - &options, - ) -} diff --git a/testsuite/testcases/tests/forge-local-performance.rs b/testsuite/testcases/tests/forge-local-performance.rs deleted file mode 100644 index df5e682180b00..0000000000000 --- a/testsuite/testcases/tests/forge-local-performance.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_forge::{ - forge_main, - success_criteria::{StateProgressThreshold, SuccessCriteria}, - EmitJobMode, EmitJobRequest, ForgeConfig, InitialVersion, LocalFactory, Options, Result, -}; -use aptos_testcases::performance_test::PerformanceBenchmark; -use std::num::NonZeroUsize; - -fn main() -> Result<()> { - ::aptos_logger::Logger::init_for_testing(); - - let tests = ForgeConfig::default() - .with_initial_validator_count(NonZeroUsize::new(2).unwrap()) - .with_initial_version(InitialVersion::Newest) - .add_network_test(PerformanceBenchmark) - .with_emit_job( - EmitJobRequest::default() - .mode(EmitJobMode::ConstTps { tps: 30 }) - .gas_price(aptos_global_constants::GAS_UNIT_PRICE), - ) - .with_success_criteria(SuccessCriteria::new(20).add_chain_progress( - StateProgressThreshold { - max_no_progress_secs: 0.0, - max_round_gap: 0, - }, - )); - - let options = Options::parse(); - forge_main(tests, LocalFactory::from_workspace(None)?, &options) -} From d78124550562a757b2e4899610a62815d6931037 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 24 Apr 2024 11:24:34 +0300 Subject: [PATCH 036/147] aptos-crypto: remove mentions of noise From README --- crates/aptos-crypto/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/aptos-crypto/README.md b/crates/aptos-crypto/README.md index 57e5aa1d73315..b470897ac7526 100644 --- a/crates/aptos-crypto/README.md +++ b/crates/aptos-crypto/README.md @@ -23,8 +23,6 @@ Aptos makes use of several cryptographic algorithms: - **Boneh-Shacham-Lynn (BLS) multisignatures and aggregate signatures** + Based on the [blst](https://docs.rs/blst/) crate + Implemented on top of Barreto-Lynn-Scott BLS12-381 elliptic curves -- The **[Noise Protocol Framework](http://www.noiseprotocol.org/)** - - Used to create authenticated and encrypted communications channels between validators - **X25519** key exchange + Based on the [x25519-dalek](https://docs.rs/x25519-dalek) crate + Used in our implementation of the [Noise Protocol Framework](http://www.noiseprotocol.org/) @@ -43,7 +41,6 @@ Before implementing a cryptographic primitive, be sure to read [`traits.rs`](src ├── hash.rs # Hash function (SHA-3) ├── hkdf.rs # HKDF implementation ├── multi_ed25519.rs # MultiEd25519 implementation of the signing/verification API in traits.rs - ├── noise.rs # Noise Protocol Framework implementation ├── test_utils.rs ├── traits.rs # Traits for safer implementations of signature schemes ├── validatable.rs # Traits for deferring validation of group elements (e.g., public keys, signatures) From e0ab0803630f484316695a03cde1caed4da89403 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 13:35:49 -0700 Subject: [PATCH 037/147] fix: visibility for testing. --- api/src/transactions.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index cf7a46db2a49c..e6684edd007cd 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -150,7 +150,7 @@ impl TransactionsApi { operation_id = "get_transactions", tag = "ApiTags::Transactions" )] - async fn get_transactions( + pub async fn get_transactions( &self, accept_type: AcceptType, /// Ledger version to start list of transactions @@ -195,7 +195,7 @@ impl TransactionsApi { operation_id = "get_transaction_by_hash", tag = "ApiTags::Transactions" )] - async fn get_transaction_by_hash( + pub async fn get_transaction_by_hash( &self, accept_type: AcceptType, /// Hash of transaction to retrieve @@ -219,7 +219,7 @@ impl TransactionsApi { operation_id = "get_transaction_by_version", tag = "ApiTags::Transactions" )] - async fn get_transaction_by_version( + pub async fn get_transaction_by_version( &self, accept_type: AcceptType, /// Version of transaction to retrieve @@ -249,7 +249,7 @@ impl TransactionsApi { operation_id = "get_account_transactions", tag = "ApiTags::Transactions" )] - async fn get_accounts_transactions( + pub async fn get_accounts_transactions( &self, accept_type: AcceptType, /// Address of account with or without a `0x` prefix @@ -300,7 +300,7 @@ impl TransactionsApi { operation_id = "submit_transaction", tag = "ApiTags::Transactions" )] - async fn submit_transaction( + pub async fn submit_transaction( &self, accept_type: AcceptType, data: SubmitTransactionPost, @@ -353,7 +353,7 @@ impl TransactionsApi { operation_id = "submit_batch_transactions", tag = "ApiTags::Transactions" )] - async fn submit_transactions_batch( + pub async fn submit_transactions_batch( &self, accept_type: AcceptType, data: SubmitTransactionsBatchPost, @@ -408,7 +408,7 @@ impl TransactionsApi { operation_id = "simulate_transaction", tag = "ApiTags::Transactions" )] - async fn simulate_transaction( + pub async fn simulate_transaction( &self, accept_type: AcceptType, /// If set to true, the max gas value in the transaction will be ignored @@ -555,7 +555,7 @@ impl TransactionsApi { operation_id = "encode_submission", tag = "ApiTags::Transactions" )] - async fn encode_submission( + pub async fn encode_submission( &self, accept_type: AcceptType, data: Json, @@ -615,7 +615,7 @@ impl TransactionsApi { operation_id = "estimate_gas_price", tag = "ApiTags::Transactions" )] - async fn estimate_gas_price(&self, accept_type: AcceptType) -> BasicResult { + pub async fn estimate_gas_price(&self, accept_type: AcceptType) -> BasicResult { fail_point_poem("endpoint_encode_submission")?; self.context .check_api_output_enabled("Estimate gas price", &accept_type)?; From 73358c18a992131752d56ddd8be89289eb0d1509 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:33:56 -0700 Subject: [PATCH 038/147] fix: playing with deadlock issues. --- api/src/transactions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index e6684edd007cd..9e4ecc3fe22c3 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -305,6 +305,7 @@ impl TransactionsApi { accept_type: AcceptType, data: SubmitTransactionPost, ) -> SubmitTransactionResult { + println!("submit_transaction"); data.verify() .context("Submitted transaction invalid'") .map_err(|err| { From ea38cf23dd7ef05ad0933e42861b9af630e8b27f Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:40:31 -0700 Subject: [PATCH 039/147] fix: playing with deadlock issues. --- api/src/transactions.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 9e4ecc3fe22c3..906541efd874f 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -314,16 +314,22 @@ impl TransactionsApi { AptosErrorCode::InvalidInput, ) })?; + println!("transaction verified") fail_point_poem("endpoint_submit_transaction")?; if !self.context.node_config.api.transaction_submission_enabled { return Err(api_disabled("Submit transaction")); } + println!("api enabled"); self.context .check_api_output_enabled("Submit transaction", &accept_type)?; let ledger_info = self.context.get_latest_ledger_info()?; + println!("ledger_info: {:?}", ledger_info); let signed_transaction = self.get_signed_transaction(&ledger_info, data)?; - self.create(&accept_type, &ledger_info, signed_transaction) - .await + panic!("signed_transaction: {:?}", signed_transaction); + let res = self.create(&accept_type, &ledger_info, signed_transaction) + .await; + println!("res: {:?}", res); + res } /// Submit batch transactions From 1506052f475e564642c8362d7dba426b43932d6d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:42:18 -0700 Subject: [PATCH 040/147] fix: playing with deadlock issues. --- api/src/transactions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 906541efd874f..9baec0964b362 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -314,7 +314,7 @@ impl TransactionsApi { AptosErrorCode::InvalidInput, ) })?; - println!("transaction verified") + println!("transaction verified"); fail_point_poem("endpoint_submit_transaction")?; if !self.context.node_config.api.transaction_submission_enabled { return Err(api_disabled("Submit transaction")); From 6ae321ea81569428b15107c12cf2b0b5e7930575 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:45:17 -0700 Subject: [PATCH 041/147] fix: playing with deadlock issues. --- api/src/transactions.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 9baec0964b362..c00ce6010d6da 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -327,9 +327,9 @@ impl TransactionsApi { let signed_transaction = self.get_signed_transaction(&ledger_info, data)?; panic!("signed_transaction: {:?}", signed_transaction); let res = self.create(&accept_type, &ledger_info, signed_transaction) - .await; - println!("res: {:?}", res); - res + .await?; + println!("sumitted transaction"); + Ok(res) } /// Submit batch transactions From 55d48c3ca14e6510935fbd110eec877e247d88eb Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:54:05 -0700 Subject: [PATCH 042/147] fix: playing with deadlock issues. --- api/src/transactions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index c00ce6010d6da..36e5a78e02be7 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -325,7 +325,7 @@ impl TransactionsApi { let ledger_info = self.context.get_latest_ledger_info()?; println!("ledger_info: {:?}", ledger_info); let signed_transaction = self.get_signed_transaction(&ledger_info, data)?; - panic!("signed_transaction: {:?}", signed_transaction); + println!("signed_transaction: {:?}", signed_transaction); let res = self.create(&accept_type, &ledger_info, signed_transaction) .await?; println!("sumitted transaction"); From ce6424de284b601d47a860d26473a726190acf12 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 15:59:36 -0700 Subject: [PATCH 043/147] fix: playing with deadlock issues. --- api/src/context.rs | 2 +- api/src/transactions.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/context.rs b/api/src/context.rs index faaea4e9298bf..38eb32ec18b50 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -209,7 +209,7 @@ impl Context { .clone() .send(MempoolClientRequest::SubmitTransaction(txn, req_sender)) .await?; - + println!("Sent to mp_sender"); callback.await? } diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 36e5a78e02be7..6502f9069ecb9 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -1065,6 +1065,7 @@ impl TransactionsApi { /// Submits a single transaction, and converts mempool codes to errors async fn create_internal(&self, txn: SignedTransaction) -> Result<(), AptosError> { + println!("create_internal"); let (mempool_status, vm_status_opt) = self .context .submit_transaction(txn) From a1e7cba7b6860baa619dc5c3691eee83074d12e8 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 16:04:27 -0700 Subject: [PATCH 044/147] fix: playing with deadlock issues. --- api/src/context.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/context.rs b/api/src/context.rs index 38eb32ec18b50..7de8a43625374 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -210,7 +210,9 @@ impl Context { .send(MempoolClientRequest::SubmitTransaction(txn, req_sender)) .await?; println!("Sent to mp_sender"); - callback.await? + let res = callback.await?; + println!("Received from mp_sender"); + res } // For use from external crates where they don't want to handle From 2027a153efa4fda4a2d0abe5e4ded8f95ce49ee7 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 24 Apr 2024 17:59:49 -0700 Subject: [PATCH 045/147] fix: cleanup. --- api/src/context.rs | 5 +---- api/src/transactions.rs | 12 ++---------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/api/src/context.rs b/api/src/context.rs index 7de8a43625374..1fff25068c081 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -209,10 +209,7 @@ impl Context { .clone() .send(MempoolClientRequest::SubmitTransaction(txn, req_sender)) .await?; - println!("Sent to mp_sender"); - let res = callback.await?; - println!("Received from mp_sender"); - res + callback.await? } // For use from external crates where they don't want to handle diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 6502f9069ecb9..e6684edd007cd 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -305,7 +305,6 @@ impl TransactionsApi { accept_type: AcceptType, data: SubmitTransactionPost, ) -> SubmitTransactionResult { - println!("submit_transaction"); data.verify() .context("Submitted transaction invalid'") .map_err(|err| { @@ -314,22 +313,16 @@ impl TransactionsApi { AptosErrorCode::InvalidInput, ) })?; - println!("transaction verified"); fail_point_poem("endpoint_submit_transaction")?; if !self.context.node_config.api.transaction_submission_enabled { return Err(api_disabled("Submit transaction")); } - println!("api enabled"); self.context .check_api_output_enabled("Submit transaction", &accept_type)?; let ledger_info = self.context.get_latest_ledger_info()?; - println!("ledger_info: {:?}", ledger_info); let signed_transaction = self.get_signed_transaction(&ledger_info, data)?; - println!("signed_transaction: {:?}", signed_transaction); - let res = self.create(&accept_type, &ledger_info, signed_transaction) - .await?; - println!("sumitted transaction"); - Ok(res) + self.create(&accept_type, &ledger_info, signed_transaction) + .await } /// Submit batch transactions @@ -1065,7 +1058,6 @@ impl TransactionsApi { /// Submits a single transaction, and converts mempool codes to errors async fn create_internal(&self, txn: SignedTransaction) -> Result<(), AptosError> { - println!("create_internal"); let (mempool_status, vm_status_opt) = self .context .submit_transaction(txn) From 29555a2cfa53c588c310d721fa47871b1e628f65 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 25 Apr 2024 16:16:44 +0100 Subject: [PATCH 046/147] revert nth commit --- storage/aptosdb/src/db/aptosdb_test.rs | 92 ++++++++++++++++--- .../aptosdb/src/db/include/aptosdb_writer.rs | 53 ++++++----- types/src/block_info.rs | 4 + types/src/ledger_info.rs | 4 + 4 files changed, 118 insertions(+), 35 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 95336cec3085a..e4dc4b41cf871 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -3,10 +3,10 @@ use crate::{ db::{ - get_first_seq_num_and_limit, test_helper, + get_first_seq_num_and_limit, test_helper::{ - arb_blocks_to_commit, put_as_state_root, put_transaction_auxiliary_data, - put_transaction_infos, update_in_memory_state, + self, arb_blocks_to_commit, arb_blocks_to_commit_with_block_nums, put_as_state_root, + put_transaction_auxiliary_data, put_transaction_infos, update_in_memory_state, }, AptosDB, }, @@ -277,10 +277,11 @@ fn test_revert_last_commit() { .executed_state_id(); // Revert the last commit - db.revert_last_commit( + db.revert_commit( last_committed_version, - root_hash, - &latest_ledger_info_before_revert, + last_committed_version.clone(), // In this case the last commit and version to commit are same + root_hash, // the hash will also be the same as lastest + latest_ledger_info_before_revert, ) .unwrap(); @@ -288,12 +289,75 @@ fn test_revert_last_commit() { let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); assert_eq!( latest_ledger_info_after_revert.ledger_info().version(), - last_committed_version + last_committed_version, + ); +} + +#[test] +fn test_revert_nth_commit() { + aptos_logger::Logger::new().init(); + + let tmp_dir = TempPath::new(); + let db = AptosDB::new_for_test(&tmp_dir); + let mut cur_ver: Version = 0; + let mut in_memory_state = db.buffered_state().lock().current_state().clone(); + let _ancestor = in_memory_state.base.clone(); + let mut val_generator = ValueGenerator::new(); + let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(1, 5)); + + struct CommitValue { + hash: HashValue, + info: LedgerInfoWithSignatures, + } + + let mut commits = HashMap::new(); + for (txns_to_commit, ledger_info_with_sigs) in &blocks { + update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); + db.save_transactions_for_test( + txns_to_commit, + cur_ver, /* first_version */ + cur_ver.checked_sub(1), + Some(ledger_info_with_sigs), + true, /* sync_commit */ + in_memory_state.clone(), + ) + .unwrap(); + commits.insert(cur_ver, CommitValue { + hash: ledger_info_with_sigs.commit_info().executed_state_id(), + info: ledger_info_with_sigs.clone(), + }); + cur_ver += txns_to_commit.len() as u64; + } + + // Check the latest version is expected before the revert + let latest_version = db.get_latest_ledger_info().unwrap().ledger_info().version(); + let expected_commit_version = commits.keys().max().unwrap() + 1; + + // Get the version and root hash for the commit we want to revert (commit 3) + let revert_version = *commits.keys().nth(2).unwrap(); + let v = commits.get(&revert_version).unwrap(); + + // Revert commit 3 + db.revert_commit( + revert_version, + latest_version, + v.hash.clone(), + v.info.clone(), + ) + .unwrap(); + + // Check that the latest ledger info is one less than the version reverted. + let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); + let expected_version = revert_version + 1; + //let expected_version = revert_version - 1; + assert_eq!( + latest_ledger_info_after_revert.ledger_info().version(), + expected_version, ); } #[test] -fn test_revert_last_commit_should_fail_with_wrong_hash() { +fn test_revert_commit_should_fail_with_wrong_hash() { aptos_logger::Logger::new().init(); let tmp_dir = TempPath::new(); @@ -321,15 +385,15 @@ fn test_revert_last_commit_should_fail_with_wrong_hash() { // Get the latest ledger info before revert let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); - + // Revert the last commit - let result = db.revert_last_commit( + let result = db.revert_commit( last_committed_version, - HashValue::random(), // A wrong hash - &latest_ledger_info_before_revert, + last_committed_version.clone(), // In this case the last commit and version to commit are the same + HashValue::random(), // A wrong hash + latest_ledger_info_before_revert, ); - assert!(result.is_err()); - + assert!(result.is_err()); } pub fn test_state_merkle_pruning_impl( diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 982b0646163eb..386242a50888f 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -633,22 +633,24 @@ impl AptosDB { Ok(()) } - pub fn revert_last_commit( + /// Revert a commit. The latest version will always be one less than the + /// `target_version`. + pub fn revert_commit( &self, - last_version: Version, + version_to_revert: Version, + latest_version: Version, new_root_hash: HashValue, - ledger_info_with_sigs: &LedgerInfoWithSignatures, + mut ledger_info_with_sigs: LedgerInfoWithSignatures, ) -> Result<()> { + // The state and version after commit will always be one less than the version to revert let _timer = OTHER_TIMERS_SECONDS - .with_label_values(&["revert_last_commit"]) + .with_label_values(&["revert_commit"]) .start_timer(); - // Revert the ledger commit progress let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::LedgerCommitProgress, - // Even though this is a revert operation, we still want to increment the version. - &DbMetadataValue::Version(last_version + 1), + &DbMetadataValue::Version(version_to_revert - 1), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -656,17 +658,17 @@ impl AptosDB { let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(last_version + 1), + &DbMetadataValue::Version(version_to_revert - 1), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - let temp_position = Position::from_postorder_index(last_version)?; + let temp_position = Position::from_postorder_index(latest_version)?; // Revert the transaction accumulator let batch = SchemaBatch::new(); self.ledger_db .transaction_accumulator_db() - .revert_transaction_accumulator(last_version, &batch, temp_position)?; + .revert_transaction_accumulator(version_to_revert - 1, &batch, temp_position)?; self.ledger_db .transaction_accumulator_db() .write_schemas(batch)?; @@ -675,8 +677,7 @@ impl AptosDB { let batch = SchemaBatch::new(); self.ledger_db .transaction_info_db() - .delete_transaction_info(last_version, &batch)?; - + .delete_transaction_info(version_to_revert, &batch)?; let batch = SchemaBatch::new(); self.ledger_db.transaction_info_db().write_schemas(batch)?; @@ -684,13 +685,12 @@ impl AptosDB { let batch = SchemaBatch::new(); self.ledger_db .event_db() - .delete_events(last_version, &batch)?; + .delete_events(version_to_revert, &batch)?; self.ledger_db.event_db().write_schemas(batch)?; // Revert the transaction auxiliary data let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(last_version, last_version, &batch)?; - + TransactionAuxiliaryDataDb::prune(version_to_revert - 1, latest_version, &batch)?; let batch = SchemaBatch::new(); self.ledger_db .transaction_auxiliary_data_db() @@ -698,20 +698,31 @@ impl AptosDB { // Revert the write set let batch = SchemaBatch::new(); - WriteSetDb::prune(last_version - 1, last_version, &batch)?; + WriteSetDb::prune(version_to_revert - 1, latest_version, &batch)?; self.ledger_db.transaction_db().prune_transactions( - last_version -1, - last_version, + version_to_revert - 1, + latest_version, &batch, )?; + // Revert the state kv and ledger metadata not yet implemented self.state_store .state_kv_db - .revert_state_kv_and_ledger_metadata(last_version)?; + .revert_state_kv_and_ledger_metadata(version_to_revert)?; - // Update the latest ledger info if provided - self.commit_ledger_info(last_version + 1, new_root_hash, Some(ledger_info_with_sigs))?; + // The latest version will be the version_to_revert + //Update the version of `ledger_info_with_sigs` + let _ledger_info = ledger_info_with_sigs + .ledger_info() + .to_owned() + .set_version(version_to_revert); + // Update the latest ledger info if provided + self.commit_ledger_info( + version_to_revert, + new_root_hash, + Some(&ledger_info_with_sigs), + )?; Ok(()) } } diff --git a/types/src/block_info.rs b/types/src/block_info.rs index 26a934f11250d..b0a8a3a612b9c 100644 --- a/types/src/block_info.rs +++ b/types/src/block_info.rs @@ -183,6 +183,10 @@ impl BlockInfo { self.version } + pub fn set_version(&mut self, version: Version) { + self.version = version; + } + /// This function checks if the current BlockInfo has /// exactly the same values in those fields that will not change /// after execution, compared to a given BlockInfo diff --git a/types/src/ledger_info.rs b/types/src/ledger_info.rs index 47ce8c620b1d3..b98153faf31ac 100644 --- a/types/src/ledger_info.rs +++ b/types/src/ledger_info.rs @@ -117,6 +117,10 @@ impl LedgerInfo { self.commit_info.version() } + pub fn set_version(&mut self, version: Version) { + self.commit_info.set_version(version) + } + pub fn timestamp_usecs(&self) -> u64 { self.commit_info.timestamp_usecs() } From ec570266062b079f8d8bcb7123d28a0920dae5d0 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 25 Apr 2024 17:14:59 +0100 Subject: [PATCH 047/147] use with_block_nums --- storage/aptosdb/src/db/aptosdb_test.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index e4dc4b41cf871..8837c7b4d5e33 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -254,7 +254,7 @@ fn test_revert_last_commit() { let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); let mut val_generator = ValueGenerator::new(); - let blocks = val_generator.generate(arb_blocks_to_commit()); + let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(1, 3)); for (txns_to_commit, ledger_info_with_sigs) in &blocks { update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); db.save_transactions_for_test( @@ -303,7 +303,7 @@ fn test_revert_nth_commit() { let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); let mut val_generator = ValueGenerator::new(); - let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(1, 5)); + let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(3, 10)); struct CommitValue { hash: HashValue, @@ -332,6 +332,7 @@ fn test_revert_nth_commit() { // Check the latest version is expected before the revert let latest_version = db.get_latest_ledger_info().unwrap().ledger_info().version(); let expected_commit_version = commits.keys().max().unwrap() + 1; + assert_eq!(latest_version, expected_commit_version); // Get the version and root hash for the commit we want to revert (commit 3) let revert_version = *commits.keys().nth(2).unwrap(); From df70fa4b513cfc7056e4f4ed2836bc078b4061ac Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 25 Apr 2024 18:12:10 +0100 Subject: [PATCH 048/147] make better assertions --- storage/aptosdb/src/db/aptosdb_test.rs | 32 +++++++++++-------- .../aptosdb/src/db/include/aptosdb_writer.rs | 6 ++-- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 8837c7b4d5e33..b1c379af4e99c 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -254,7 +254,7 @@ fn test_revert_last_commit() { let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); let mut val_generator = ValueGenerator::new(); - let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(1, 3)); + let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(3, 3)); for (txns_to_commit, ledger_info_with_sigs) in &blocks { update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); db.save_transactions_for_test( @@ -267,30 +267,33 @@ fn test_revert_last_commit() { ) .unwrap(); cur_ver += txns_to_commit.len() as u64; + + println!("next version: {}", cur_ver); } // Get the latest ledger info before revert let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); - let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); - let root_hash = latest_ledger_info_before_revert + let root_hash = db + .get_latest_ledger_info() + .unwrap() + .ledger_info() .commit_info() .executed_state_id(); + let expected_version = cur_ver - 1; + assert_eq!(db.get_latest_version().unwrap(), expected_version); + // Revert the last commit db.revert_commit( - last_committed_version, - last_committed_version.clone(), // In this case the last commit and version to commit are same - root_hash, // the hash will also be the same as lastest + db.get_latest_version().unwrap(), + db.get_latest_version().unwrap(), // In this case the last commit and version to commit are same + root_hash, // the hash will also be the same as lastest latest_ledger_info_before_revert, ) .unwrap(); - // Check that the latest ledger info is updated correctly after the revert - let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); - assert_eq!( - latest_ledger_info_after_revert.ledger_info().version(), - last_committed_version, - ); + let exepcted_version = cur_ver - 2; + assert_eq!(db.get_latest_version().unwrap(), exepcted_version); } #[test] @@ -303,7 +306,8 @@ fn test_revert_nth_commit() { let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); let mut val_generator = ValueGenerator::new(); - let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(3, 10)); + // set range of min and max blocks to 5 to always gen 5 blocks + let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(5, 5)); struct CommitValue { hash: HashValue, @@ -327,6 +331,7 @@ fn test_revert_nth_commit() { info: ledger_info_with_sigs.clone(), }); cur_ver += txns_to_commit.len() as u64; + println!("Commit at version: {}", cur_ver); } // Check the latest version is expected before the revert @@ -337,6 +342,7 @@ fn test_revert_nth_commit() { // Get the version and root hash for the commit we want to revert (commit 3) let revert_version = *commits.keys().nth(2).unwrap(); let v = commits.get(&revert_version).unwrap(); + println!("Reverting commit at version: {}", revert_version); // Revert commit 3 db.revert_commit( diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 386242a50888f..781ba8b7cf792 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -640,7 +640,7 @@ impl AptosDB { version_to_revert: Version, latest_version: Version, new_root_hash: HashValue, - mut ledger_info_with_sigs: LedgerInfoWithSignatures, + ledger_info_with_sigs: LedgerInfoWithSignatures, ) -> Result<()> { // The state and version after commit will always be one less than the version to revert let _timer = OTHER_TIMERS_SECONDS @@ -715,11 +715,11 @@ impl AptosDB { let _ledger_info = ledger_info_with_sigs .ledger_info() .to_owned() - .set_version(version_to_revert); + .set_version(version_to_revert - 1); // Update the latest ledger info if provided self.commit_ledger_info( - version_to_revert, + version_to_revert - 1, new_root_hash, Some(&ledger_info_with_sigs), )?; From 25bc25eba16277587446588c5bf8fce426d70363 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Thu, 25 Apr 2024 18:48:04 +0100 Subject: [PATCH 049/147] stop last test flakiness --- storage/aptosdb/src/db/aptosdb_test.rs | 35 +++++++++++--------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index b1c379af4e99c..77d65776c2e7d 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -267,10 +267,12 @@ fn test_revert_last_commit() { ) .unwrap(); cur_ver += txns_to_commit.len() as u64; - - println!("next version: {}", cur_ver); } + // Check expected before revert commit + let expected_version = cur_ver - 1; + assert_eq!(db.get_latest_version().unwrap(), expected_version); + // Get the latest ledger info before revert let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); let root_hash = db @@ -280,9 +282,6 @@ fn test_revert_last_commit() { .commit_info() .executed_state_id(); - let expected_version = cur_ver - 1; - assert_eq!(db.get_latest_version().unwrap(), expected_version); - // Revert the last commit db.revert_commit( db.get_latest_version().unwrap(), @@ -309,12 +308,13 @@ fn test_revert_nth_commit() { // set range of min and max blocks to 5 to always gen 5 blocks let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(5, 5)); - struct CommitValue { + struct Commit { hash: HashValue, info: LedgerInfoWithSignatures, } let mut commits = HashMap::new(); + let mut commit_versions = Vec::new(); for (txns_to_commit, ledger_info_with_sigs) in &blocks { update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); db.save_transactions_for_test( @@ -326,41 +326,36 @@ fn test_revert_nth_commit() { in_memory_state.clone(), ) .unwrap(); - commits.insert(cur_ver, CommitValue { + commits.insert(cur_ver, Commit { hash: ledger_info_with_sigs.commit_info().executed_state_id(), info: ledger_info_with_sigs.clone(), }); cur_ver += txns_to_commit.len() as u64; + commit_versions.push(cur_ver); println!("Commit at version: {}", cur_ver); } - // Check the latest version is expected before the revert - let latest_version = db.get_latest_ledger_info().unwrap().ledger_info().version(); - let expected_commit_version = commits.keys().max().unwrap() + 1; - assert_eq!(latest_version, expected_commit_version); + // Check expected before revert commit + let expected_version = cur_ver - 1; + assert_eq!(db.get_latest_version().unwrap(), expected_version); // Get the version and root hash for the commit we want to revert (commit 3) - let revert_version = *commits.keys().nth(2).unwrap(); + let revert_version = commit_versions[2]; let v = commits.get(&revert_version).unwrap(); println!("Reverting commit at version: {}", revert_version); // Revert commit 3 db.revert_commit( revert_version, - latest_version, + db.get_latest_version().unwrap(), v.hash.clone(), v.info.clone(), ) .unwrap(); // Check that the latest ledger info is one less than the version reverted. - let latest_ledger_info_after_revert = db.get_latest_ledger_info().unwrap(); - let expected_version = revert_version + 1; - //let expected_version = revert_version - 1; - assert_eq!( - latest_ledger_info_after_revert.ledger_info().version(), - expected_version, - ); + let expected_version = revert_version; + assert_eq!(db.get_latest_version().unwrap(), expected_version); } #[test] From 65f814775c5883c9662cd2761a233189ad2ce68a Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 25 Apr 2024 15:48:26 -0700 Subject: [PATCH 050/147] fix: make get_transaction_by_hash_inner public. --- api/src/transactions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index e6684edd007cd..9c45c7b4a6dae 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -689,7 +689,7 @@ impl TransactionsApi { } } - async fn get_transaction_by_hash_inner( + pub async fn get_transaction_by_hash_inner( &self, accept_type: &AcceptType, hash: HashValue, From afa2d381377eb249814c594bec71ade4f836f582 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 25 Apr 2024 16:03:21 -0700 Subject: [PATCH 051/147] fix: debugging transaction_by hash issue. --- api/src/transactions.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 9c45c7b4a6dae..a7ed0ce861c50 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -698,6 +698,7 @@ impl TransactionsApi { let accept_type = accept_type.clone(); let ledger_info = api_spawn_blocking(move || context.get_latest_ledger_info()).await?; + println!("ledger_info: {:?}", ledger_info); let txn_data = self .get_by_hash(hash.into(), &ledger_info) @@ -712,7 +713,7 @@ impl TransactionsApi { })? .context(format!("Failed to find transaction with hash: {}", hash)) .map_err(|_| transaction_not_found_by_hash(hash, &ledger_info))?; - + println!("txn_data: {:?}", txn_data); let api = self.clone(); api_spawn_blocking(move || api.get_transaction_inner(&accept_type, txn_data, &ledger_info)) .await From 846428325a6faf86fac478c9bb50996f308c4c58 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 25 Apr 2024 16:13:16 -0700 Subject: [PATCH 052/147] fix: debugging transaction_by hash issue. --- api/src/transactions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index a7ed0ce861c50..7a0e086eafc40 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -838,6 +838,7 @@ impl TransactionsApi { .await .context("Failed to join task to read transaction by hash")? .context("Failed to read transaction by hash from DB")?; + println!("from_db: {:?}", from_db); Ok(match from_db { None => self .context From 7f3e0e5c93d2b208f421445e3fd7ecdad8d0a4dd Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 25 Apr 2024 16:18:49 -0700 Subject: [PATCH 053/147] fix: debugging transaction_by hash issue. --- api/src/transactions.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 7a0e086eafc40..5db4876e11979 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -840,11 +840,15 @@ impl TransactionsApi { .context("Failed to read transaction by hash from DB")?; println!("from_db: {:?}", from_db); Ok(match from_db { - None => self + None => { + let res = self .context .get_pending_transaction_by_hash(hash) .await? - .map(|t| t.into()), + .map(|t| t.into()); + println!("pending_transaction: {:?}", res); + res + }, _ => from_db.map(|t| t.into()), }) } From d60b3ebea23fb1e9fd2e02d9e9afbcd5d966dd7f Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 26 Apr 2024 00:22:39 +0100 Subject: [PATCH 054/147] debugging --- storage/aptosdb/src/db/aptosdb_test.rs | 50 ++++++++++++------- .../aptosdb/src/db/include/aptosdb_writer.rs | 3 +- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 77d65776c2e7d..767062b7b8f1e 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -286,7 +286,7 @@ fn test_revert_last_commit() { db.revert_commit( db.get_latest_version().unwrap(), db.get_latest_version().unwrap(), // In this case the last commit and version to commit are same - root_hash, // the hash will also be the same as lastest + root_hash, // the hash is also the lastest latest_ledger_info_before_revert, ) .unwrap(); @@ -298,25 +298,33 @@ fn test_revert_last_commit() { #[test] fn test_revert_nth_commit() { aptos_logger::Logger::new().init(); - let tmp_dir = TempPath::new(); let db = AptosDB::new_for_test(&tmp_dir); let mut cur_ver: Version = 0; let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); + let mut val_generator = ValueGenerator::new(); // set range of min and max blocks to 5 to always gen 5 blocks let (blocks, _) = val_generator.generate(arb_blocks_to_commit_with_block_nums(5, 5)); + #[derive(Debug)] struct Commit { hash: HashValue, info: LedgerInfoWithSignatures, + first_version: Version, + last_version: Version, + txs: usize, } - let mut commits = HashMap::new(); + let mut committed_blocks = HashMap::new(); let mut commit_versions = Vec::new(); + let mut blockheight = 0; + for (txns_to_commit, ledger_info_with_sigs) in &blocks { + let first_version = cur_ver; update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); + db.save_transactions_for_test( txns_to_commit, cur_ver, /* first_version */ @@ -326,36 +334,42 @@ fn test_revert_nth_commit() { in_memory_state.clone(), ) .unwrap(); - commits.insert(cur_ver, Commit { + + let last_version = cur_ver + txns_to_commit.len() as u64 - 1; + committed_blocks.insert(blockheight, Commit { hash: ledger_info_with_sigs.commit_info().executed_state_id(), info: ledger_info_with_sigs.clone(), + first_version, + last_version, + txs: txns_to_commit.len(), }); - cur_ver += txns_to_commit.len() as u64; + commit_versions.push(cur_ver); - println!("Commit at version: {}", cur_ver); + cur_ver += txns_to_commit.len() as u64; + blockheight += 1; } // Check expected before revert commit let expected_version = cur_ver - 1; assert_eq!(db.get_latest_version().unwrap(), expected_version); - // Get the version and root hash for the commit we want to revert (commit 3) - let revert_version = commit_versions[2]; - let v = commits.get(&revert_version).unwrap(); - println!("Reverting commit at version: {}", revert_version); + // Get the 3rd block back from the latest block + let revert_block_num = blockheight - 3; + let revert = committed_blocks.get(&revert_block_num).unwrap(); + + println!("Reverting block: {:?}", revert); + + let version_to_revert = revert.first_version; - // Revert commit 3 db.revert_commit( - revert_version, - db.get_latest_version().unwrap(), - v.hash.clone(), - v.info.clone(), + version_to_revert, + revert.last_version, + revert.hash.clone(), + revert.info.clone(), ) .unwrap(); - // Check that the latest ledger info is one less than the version reverted. - let expected_version = revert_version; - assert_eq!(db.get_latest_version().unwrap(), expected_version); + assert_eq!(db.get_latest_version().unwrap(), version_to_revert - 1); } #[test] diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 781ba8b7cf792..d194c77fd12d2 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -633,8 +633,7 @@ impl AptosDB { Ok(()) } - /// Revert a commit. The latest version will always be one less than the - /// `target_version`. + /// Revert a commit. pub fn revert_commit( &self, version_to_revert: Version, From 06443b81f6b8b8742c4aa47eba9e315b5e6502ff Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 25 Apr 2024 16:25:19 -0700 Subject: [PATCH 055/147] fix: debugging transaction_by hash issue. --- api/src/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/context.rs b/api/src/context.rs index 1fff25068c081..0c8726b1b7b94 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -781,6 +781,7 @@ impl Context { hash: HashValue, ledger_version: u64, ) -> Result> { + println!("get_transaction_by_hash hash: {:?} {:?}", hash, ledger_version); self.db .get_transaction_by_hash(hash, ledger_version, true)? .map(|t| self.convert_into_transaction_on_chain_data(t)) From 723959f87340103b7a34e697a4d920c822123747 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 26 Apr 2024 11:19:03 +0100 Subject: [PATCH 056/147] fix bug --- storage/aptosdb/src/db/aptosdb_test.rs | 15 +++------------ storage/aptosdb/src/db/include/aptosdb_writer.rs | 4 ++-- storage/aptosdb/src/state_kv_db.rs | 9 ++------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 767062b7b8f1e..6393dd40ca878 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -313,8 +313,6 @@ fn test_revert_nth_commit() { hash: HashValue, info: LedgerInfoWithSignatures, first_version: Version, - last_version: Version, - txs: usize, } let mut committed_blocks = HashMap::new(); @@ -324,7 +322,6 @@ fn test_revert_nth_commit() { for (txns_to_commit, ledger_info_with_sigs) in &blocks { let first_version = cur_ver; update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); - db.save_transactions_for_test( txns_to_commit, cur_ver, /* first_version */ @@ -334,16 +331,11 @@ fn test_revert_nth_commit() { in_memory_state.clone(), ) .unwrap(); - - let last_version = cur_ver + txns_to_commit.len() as u64 - 1; committed_blocks.insert(blockheight, Commit { hash: ledger_info_with_sigs.commit_info().executed_state_id(), info: ledger_info_with_sigs.clone(), first_version, - last_version, - txs: txns_to_commit.len(), }); - commit_versions.push(cur_ver); cur_ver += txns_to_commit.len() as u64; blockheight += 1; @@ -357,13 +349,12 @@ fn test_revert_nth_commit() { let revert_block_num = blockheight - 3; let revert = committed_blocks.get(&revert_block_num).unwrap(); - println!("Reverting block: {:?}", revert); - - let version_to_revert = revert.first_version; + // Get the version to revert to + let version_to_revert = revert.first_version - 1; db.revert_commit( version_to_revert, - revert.last_version, + db.get_latest_version().unwrap(), revert.hash.clone(), revert.info.clone(), ) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index d194c77fd12d2..40e978fa04f8f 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -703,13 +703,13 @@ impl AptosDB { latest_version, &batch, )?; + println!("Reverted write set"); - // Revert the state kv and ledger metadata not yet implemented + // Revert the state kv and ledger metadata self.state_store .state_kv_db .revert_state_kv_and_ledger_metadata(version_to_revert)?; - // The latest version will be the version_to_revert //Update the version of `ledger_info_with_sigs` let _ledger_info = ledger_info_with_sigs .ledger_info() diff --git a/storage/aptosdb/src/state_kv_db.rs b/storage/aptosdb/src/state_kv_db.rs index f105bcf4cf669..83a5f107fd61d 100644 --- a/storage/aptosdb/src/state_kv_db.rs +++ b/storage/aptosdb/src/state_kv_db.rs @@ -211,7 +211,7 @@ impl StateKvDb { pub(crate) fn revert_state_kv_and_ledger_metadata(&self, version: Version) -> Result<()> { // Revert the state KV metadata DB to the given version - let mut metadata_batch = SchemaBatch::new(); + let metadata_batch = SchemaBatch::new(); metadata_batch.put::( &DbMetadataKey::StateKvCommitProgress, &DbMetadataValue::Version(version), @@ -224,7 +224,7 @@ impl StateKvDb { // Revert each state KV DB shard to the corresponding version for shard_id in 0..NUM_STATE_SHARDS { - let mut shard_batch = SchemaBatch::new(); + let shard_batch = SchemaBatch::new(); shard_batch.put::( &DbMetadataKey::StateKvShardCommitProgress(shard_id as usize), &DbMetadataValue::Version(version), @@ -232,11 +232,6 @@ impl StateKvDb { self.state_kv_db_shards[shard_id as usize].write_schemas(shard_batch)?; } - // Truncate the state KV DB shards if necessary - if let Some(overall_kv_commit_progress) = get_state_kv_commit_progress(self)? { - truncate_state_kv_db_shards(self, overall_kv_commit_progress, Some(version))?; - } - Ok(()) } From 6239f63ede6939d146a053a5eb165f943e464713 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 26 Apr 2024 11:50:55 +0100 Subject: [PATCH 057/147] fmt --- storage/aptosdb/src/db/aptosdb_test.rs | 1 + storage/aptosdb/src/db/include/aptosdb_writer.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 6393dd40ca878..761e7dd6be61f 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -331,6 +331,7 @@ fn test_revert_nth_commit() { in_memory_state.clone(), ) .unwrap(); + committed_blocks.insert(blockheight, Commit { hash: ledger_info_with_sigs.commit_info().executed_state_id(), info: ledger_info_with_sigs.clone(), diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 40e978fa04f8f..1d0134cda70cf 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -703,7 +703,6 @@ impl AptosDB { latest_version, &batch, )?; - println!("Reverted write set"); // Revert the state kv and ledger metadata self.state_store From 8767d921d9c13053034ad4e0e300f0d3abeada40 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 18 Apr 2024 20:08:37 +0300 Subject: [PATCH 058/147] consensus-types: remove request_response Eliminate the async dependency in consensus-types. --- consensus/consensus-types/src/lib.rs | 1 - .../consensus-types/src/request_response.rs | 54 ------------------- 2 files changed, 55 deletions(-) delete mode 100644 consensus/consensus-types/src/request_response.rs diff --git a/consensus/consensus-types/src/lib.rs b/consensus/consensus-types/src/lib.rs index 1ae492531755c..fee7fd2e755c8 100644 --- a/consensus/consensus-types/src/lib.rs +++ b/consensus/consensus-types/src/lib.rs @@ -17,7 +17,6 @@ pub mod proposal_ext; pub mod proposal_msg; pub mod quorum_cert; pub mod randomness; -pub mod request_response; pub mod safety_data; pub mod sync_info; pub mod timeout_2chain; diff --git a/consensus/consensus-types/src/request_response.rs b/consensus/consensus-types/src/request_response.rs deleted file mode 100644 index a28082b6c0c7f..0000000000000 --- a/consensus/consensus-types/src/request_response.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::common::{Payload, PayloadFilter}; -use anyhow::Result; -use futures::channel::oneshot; -use std::{fmt, fmt::Formatter}; - -pub enum GetPayloadCommand { - /// Request to pull block to submit to consensus. - GetPayloadRequest( - // max block size - u64, - // max byte size - u64, - // max number of inline transactions (transactions without a proof of store) - u64, - // max byte size of inline transactions (transactions without a proof of store) - u64, - // return non full - bool, - // block payloads to exclude from the requested block - PayloadFilter, - // callback to respond to - oneshot::Sender>, - ), -} - -impl fmt::Display for GetPayloadCommand { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - GetPayloadCommand::GetPayloadRequest( - max_txns, - max_bytes, - max_inline_txns, - max_inline_bytes, - return_non_full, - excluded, - _, - ) => { - write!( - f, - "GetPayloadRequest [max_txns: {}, max_bytes: {}, max_inline_txns: {}, max_inline_bytes:{}, return_non_full: {}, excluded: {}]", - max_txns, max_bytes, max_inline_txns, max_inline_bytes, return_non_full, excluded - ) - }, - } - } -} - -#[derive(Debug)] -pub enum GetPayloadResponse { - GetPayloadResponse(Payload), -} From b116b2e1eb534364029e0b9c082a0fa847730905 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 19 Apr 2024 18:12:14 +0300 Subject: [PATCH 059/147] cut out tokio from consensus-types Stub out the status field in ProofWithData with the vector that's evenutally supposed to be retrieved, until we figure out if we need the field and the whole structure at all. --- Cargo.lock | 2 - consensus/consensus-types/Cargo.toml | 2 - consensus/consensus-types/src/common.rs | 51 +++---------------------- 3 files changed, 5 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d604ef464dce0..a866a4addae44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -817,7 +817,6 @@ dependencies = [ "aptos-short-hex-str", "aptos-types", "bcs 0.1.4", - "futures", "itertools 0.10.5", "mirai-annotations", "once_cell", @@ -826,7 +825,6 @@ dependencies = [ "rayon", "serde", "serde_json", - "tokio", ] [[package]] diff --git a/consensus/consensus-types/Cargo.toml b/consensus/consensus-types/Cargo.toml index ed7fe3256b1ee..aa7d1f19ca0c7 100644 --- a/consensus/consensus-types/Cargo.toml +++ b/consensus/consensus-types/Cargo.toml @@ -23,7 +23,6 @@ aptos-logger = { workspace = true } aptos-short-hex-str = { workspace = true } aptos-types = { workspace = true } bcs = { workspace = true } -futures = { workspace = true } itertools = { workspace = true } mirai-annotations = { workspace = true } once_cell = { workspace = true } @@ -31,7 +30,6 @@ proptest = { workspace = true, optional = true } rand = { workspace = true } rayon = { workspace = true } serde = { workspace = true } -tokio = { workspace = true } [dev-dependencies] aptos-types = { workspace = true, features = ["fuzzing"] } diff --git a/consensus/consensus-types/src/common.rs b/consensus/consensus-types/src/common.rs index 3f6fba7695929..f7504ab762d42 100644 --- a/consensus/consensus-types/src/common.rs +++ b/consensus/consensus-types/src/common.rs @@ -8,8 +8,6 @@ use aptos_crypto::{ HashValue, }; use aptos_crypto_derive::CryptoHasher; -use aptos_executor_types::ExecutorResult; -use aptos_infallible::Mutex; use aptos_logger::prelude::*; use aptos_types::{ account_address::AccountAddress, transaction::SignedTransaction, @@ -18,8 +16,7 @@ use aptos_types::{ use once_cell::sync::OnceCell; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use std::{cmp::min, collections::HashSet, fmt, fmt::Write, sync::Arc}; -use tokio::sync::oneshot; +use std::{cmp::min, collections::HashSet, fmt, fmt::Write}; /// The round of a block is a consensus-internal counter, which starts with 0 and increases /// monotonically. It is used for the protocol safety and liveness (please see the detailed @@ -86,62 +83,24 @@ pub struct RejectedTransactionSummary { pub reason: DiscardedVMStatus, } -#[derive(Debug)] -pub enum DataStatus { - Cached(Vec), - Requested( - Vec<( - HashValue, - oneshot::Receiver>>, - )>, - ), -} - -impl DataStatus { - pub fn extend(&mut self, other: DataStatus) { - match (self, other) { - (DataStatus::Requested(v1), DataStatus::Requested(v2)) => v1.extend(v2), - (_, _) => unreachable!(), - } - } - - pub fn take(&mut self) -> DataStatus { - std::mem::replace(self, DataStatus::Requested(vec![])) - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub struct ProofWithData { pub proofs: Vec, #[serde(skip)] - pub status: Arc>>, -} - -impl PartialEq for ProofWithData { - fn eq(&self, other: &Self) -> bool { - self.proofs == other.proofs && Arc::as_ptr(&self.status) == Arc::as_ptr(&other.status) - } + pub status: Vec, } -impl Eq for ProofWithData {} - impl ProofWithData { pub fn new(proofs: Vec) -> Self { Self { proofs, - status: Arc::new(Mutex::new(None)), + status: vec![], } } pub fn extend(&mut self, other: ProofWithData) { - let other_data_status = other.status.lock().as_mut().unwrap().take(); self.proofs.extend(other.proofs); - let mut status = self.status.lock(); - if status.is_none() { - *status = Some(other_data_status); - } else { - status.as_mut().unwrap().extend(other_data_status); - } + self.status.extend(other.status); } pub fn len(&self) -> usize { From 2c3f7f026bdccaaf3155c802e3103e310645b48d Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 22 Apr 2024 11:55:56 +0300 Subject: [PATCH 060/147] aptos-vm: cut out use of futures --- Cargo.lock | 1 - aptos-move/aptos-vm/Cargo.toml | 1 - .../sharded_executor_service.rs | 15 +++++---------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a866a4addae44..099d576029ba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3736,7 +3736,6 @@ dependencies = [ "dashmap", "derive_more", "fail 0.5.1", - "futures", "hex", "jsonwebtoken 8.3.0", "move-binary-format", diff --git a/aptos-move/aptos-vm/Cargo.toml b/aptos-move/aptos-vm/Cargo.toml index 4949892ed9324..e32fb9741dee9 100644 --- a/aptos-move/aptos-vm/Cargo.toml +++ b/aptos-move/aptos-vm/Cargo.toml @@ -45,7 +45,6 @@ crossbeam-channel = { workspace = true } dashmap = { workspace = true } derive_more = { workspace = true } fail = { workspace = true } -futures = { workspace = true } hex = { workspace = true } jsonwebtoken = { workspace = true } move-binary-format = { workspace = true } diff --git a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs index 2ad3b8d3d5cc9..2186a92c24407 100644 --- a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs +++ b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs @@ -29,7 +29,6 @@ use aptos_types::{ }, }; use aptos_vm_logging::disable_speculative_logging; -use futures::{channel::oneshot, executor::block_on}; use move_core_types::vm_status::VMStatus; use std::sync::Arc; @@ -104,8 +103,6 @@ impl ShardedExecutorService { state_view: &S, config: BlockExecutorConfig, ) -> Result, VMStatus> { - let (callback, callback_receiver) = oneshot::channel(); - let cross_shard_state_view = Arc::new(CrossShardStateView::create_cross_shard_state_view( state_view, &transactions, @@ -123,8 +120,8 @@ impl ShardedExecutorService { .into_iter() .map(|txn| txn.into_txn().into_txn()) .collect(); - let executor_thread_pool_clone = executor_thread_pool.clone(); + let mut outputs = Ok(vec![]); executor_thread_pool.clone().scope(|s| { s.spawn(move |_| { CrossShardCommitReceiver::start( @@ -133,8 +130,8 @@ impl ShardedExecutorService { round, ); }); - s.spawn(move |_| { - let ret = BlockAptosVM::execute_block( + s.spawn(|s| { + outputs = BlockAptosVM::execute_block( executor_thread_pool, &signature_verified_transactions, aggr_overridden_state_view.as_ref(), @@ -159,15 +156,13 @@ impl ShardedExecutorService { // Send a self message to stop the cross-shard commit receiver. cross_shard_client_clone.send_global_msg(CrossShardMsg::StopMsg); } - callback.send(ret).unwrap(); - executor_thread_pool_clone.spawn(move || { + s.spawn(move |_| { // Explicit async drop drop(signature_verified_transactions); }); }); }); - - block_on(callback_receiver).unwrap() + outputs } fn execute_block( From 61bd4ee2d569b09ca05c685caebaf7612c51d8c0 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 24 Apr 2024 16:11:54 +0300 Subject: [PATCH 061/147] Cut out aptos-consensus and dependent crates aptos-consensus had code that provided consensus-types data in asynchronous sends. Since we are not likely to need this crate anyway, cut it out of the workspace. Also remove any dependent crates. --- Cargo.lock | 563 +--- Cargo.toml | 20 - aptos-move/aptos-debugger/Cargo.toml | 47 - aptos-move/aptos-debugger/example-txn.txt | 1 - .../aptos-debugger/src/aptos_debugger.rs | 294 --- .../aptos-debugger/src/bcs_txn_decoder.rs | 81 - .../src/bin/remote-gas-profiler.rs | 75 - aptos-move/aptos-debugger/src/common.rs | 53 - .../src/execute_past_transactions.rs | 48 - .../src/execute_pending_block.rs | 92 - aptos-move/aptos-debugger/src/lib.rs | 8 - consensus/Cargo.toml | 112 - consensus/README.md | 59 - consensus/src/block_preparer.rs | 65 - consensus/src/block_storage/block_store.rs | 617 ----- .../src/block_storage/block_store_test.rs | 491 ---- consensus/src/block_storage/block_tree.rs | 478 ---- consensus/src/block_storage/mod.rs | 66 - consensus/src/block_storage/sync_manager.rs | 629 ----- consensus/src/block_storage/tracing.rs | 38 - consensus/src/consensus_provider.rs | 106 - consensus/src/consensusdb/consensusdb_test.rs | 118 - consensus/src/consensusdb/mod.rs | 213 -- consensus/src/consensusdb/schema/block/mod.rs | 47 - .../src/consensusdb/schema/block/test.rs | 14 - consensus/src/consensusdb/schema/dag/mod.rs | 97 - consensus/src/consensusdb/schema/mod.rs | 41 - .../schema/quorum_certificate/mod.rs | 47 - .../schema/quorum_certificate/test.rs | 15 - .../consensusdb/schema/single_entry/mod.rs | 71 - .../consensusdb/schema/single_entry/test.rs | 14 - consensus/src/counters.rs | 969 ------- consensus/src/dag/adapter.rs | 448 ---- .../leader_reputation_adapter.rs | 145 -- consensus/src/dag/anchor_election/mod.rs | 21 - .../src/dag/anchor_election/round_robin.rs | 33 - consensus/src/dag/bootstrap.rs | 778 ------ consensus/src/dag/commit_signer.rs | 31 - consensus/src/dag/dag_driver.rs | 401 --- consensus/src/dag/dag_fetcher.rs | 438 ---- consensus/src/dag/dag_handler.rs | 292 --- consensus/src/dag/dag_network.rs | 207 -- consensus/src/dag/dag_state_sync.rs | 344 --- consensus/src/dag/dag_store.rs | 548 ---- consensus/src/dag/errors.rs | 80 - consensus/src/dag/health/backoff.rs | 85 - consensus/src/dag/health/chain_health.rs | 96 - consensus/src/dag/health/mod.rs | 13 - consensus/src/dag/health/pipeline_health.rs | 80 - consensus/src/dag/mod.rs | 32 - consensus/src/dag/observability/counters.rs | 69 - consensus/src/dag/observability/logging.rs | 44 - consensus/src/dag/observability/mod.rs | 6 - consensus/src/dag/observability/tracing.rs | 40 - consensus/src/dag/order_rule.rs | 261 -- consensus/src/dag/rb_handler.rs | 263 -- consensus/src/dag/round_state.rs | 205 -- consensus/src/dag/storage.rs | 72 - consensus/src/dag/tests/dag_driver_tests.rs | 233 -- consensus/src/dag/tests/dag_network_test.rs | 135 - .../src/dag/tests/dag_state_sync_tests.rs | 233 -- consensus/src/dag/tests/dag_test.rs | 317 --- consensus/src/dag/tests/fetcher_test.rs | 63 - consensus/src/dag/tests/helpers.rs | 103 - consensus/src/dag/tests/integration_tests.rs | 248 -- consensus/src/dag/tests/mod.rs | 13 - consensus/src/dag/tests/order_rule_tests.rs | 264 -- consensus/src/dag/tests/rb_handler_tests.rs | 258 -- consensus/src/dag/tests/types_test.rs | 184 -- consensus/src/dag/types.rs | 1003 ------- consensus/src/epoch_manager.rs | 1657 ------------ consensus/src/error.rs | 108 - consensus/src/execution_pipeline.rs | 272 -- consensus/src/lib.rs | 101 - .../src/liveness/cached_proposer_election.rs | 69 - .../liveness/cached_proposer_election_test.rs | 69 - consensus/src/liveness/leader_reputation.rs | 813 ------ .../src/liveness/leader_reputation_test.rs | 763 ------ consensus/src/liveness/mod.rs | 25 - consensus/src/liveness/proposal_generator.rs | 504 ---- .../src/liveness/proposal_generator_test.rs | 231 -- consensus/src/liveness/proposer_election.rs | 85 - .../liveness/rotating_proposer_election.rs | 41 - .../src/liveness/rotating_proposer_test.rs | 59 - .../src/liveness/round_proposer_election.rs | 34 - consensus/src/liveness/round_proposer_test.rs | 41 - consensus/src/liveness/round_state.rs | 382 --- consensus/src/liveness/round_state_test.rs | 159 -- .../liveness/unequivocal_proposer_election.rs | 88 - .../unequivocal_proposer_election_test.rs | 106 - consensus/src/logging.rs | 67 - consensus/src/metrics_safety_rules.rs | 265 -- consensus/src/network.rs | 844 ------ consensus/src/network_interface.rs | 178 -- consensus/src/network_tests.rs | 857 ------ consensus/src/payload_client/mixed.rs | 284 -- consensus/src/payload_client/mod.rs | 33 - consensus/src/payload_client/user/mod.rs | 83 - .../user/quorum_store_client.rs | 152 -- consensus/src/payload_client/validator.rs | 79 - consensus/src/payload_manager.rs | 294 --- consensus/src/pending_votes.rs | 579 ---- consensus/src/persistent_liveness_storage.rs | 454 ---- consensus/src/pipeline/buffer.rs | 256 -- consensus/src/pipeline/buffer_item.rs | 469 ---- consensus/src/pipeline/buffer_manager.rs | 794 ------ .../src/pipeline/commit_reliable_broadcast.rs | 113 - .../src/pipeline/decoupled_execution_utils.rs | 124 - consensus/src/pipeline/errors.rs | 20 - consensus/src/pipeline/execution_client.rs | 479 ---- consensus/src/pipeline/execution_phase.rs | 92 - .../src/pipeline/execution_schedule_phase.rs | 110 - .../src/pipeline/execution_wait_phase.rs | 55 - consensus/src/pipeline/hashable.rs | 9 - consensus/src/pipeline/linkedlist.rs | 322 --- consensus/src/pipeline/mod.rs | 40 - consensus/src/pipeline/persisting_phase.rs | 75 - consensus/src/pipeline/pipeline_phase.rs | 99 - consensus/src/pipeline/signing_phase.rs | 84 - .../pipeline/tests/buffer_manager_tests.rs | 421 --- .../pipeline/tests/execution_phase_tests.rs | 171 -- .../src/pipeline/tests/integration_tests.rs | 5 - consensus/src/pipeline/tests/mod.rs | 11 - .../tests/ordering_state_computer_tests.rs | 8 - consensus/src/pipeline/tests/phase_tester.rs | 103 - .../src/pipeline/tests/signing_phase_tests.rs | 146 -- consensus/src/pipeline/tests/test_utils.rs | 162 -- consensus/src/qc_aggregator.rs | 181 -- .../src/quorum_store/batch_coordinator.rs | 158 -- consensus/src/quorum_store/batch_generator.rs | 492 ---- consensus/src/quorum_store/batch_requester.rs | 202 -- consensus/src/quorum_store/batch_store.rs | 459 ---- consensus/src/quorum_store/counters.rs | 715 ----- .../direct_mempool_quorum_store.rs | 173 -- consensus/src/quorum_store/mod.rs | 23 - .../src/quorum_store/network_listener.rs | 92 - .../src/quorum_store/proof_coordinator.rs | 373 --- consensus/src/quorum_store/proof_manager.rs | 344 --- .../src/quorum_store/quorum_store_builder.rs | 446 ---- .../quorum_store/quorum_store_coordinator.rs | 158 -- consensus/src/quorum_store/quorum_store_db.rs | 163 -- consensus/src/quorum_store/schema.rs | 74 - .../tests/batch_generator_test.rs | 748 ------ .../tests/batch_requester_test.rs | 258 -- .../quorum_store/tests/batch_store_test.rs | 283 -- .../tests/direct_mempool_quorum_store_test.rs | 80 - consensus/src/quorum_store/tests/mod.rs | 12 - .../tests/proof_coordinator_test.rs | 85 - .../quorum_store/tests/proof_manager_test.rs | 293 --- .../tests/quorum_store_db_test.rs | 86 - .../src/quorum_store/tests/types_test.rs | 42 - consensus/src/quorum_store/tests/utils.rs | 95 - consensus/src/quorum_store/types.rs | 303 --- consensus/src/quorum_store/utils.rs | 415 --- consensus/src/rand/dkg/mod.rs | 2 - consensus/src/rand/mod.rs | 7 - consensus/src/rand/rand_gen/aug_data_store.rs | 125 - consensus/src/rand/rand_gen/block_queue.rs | 225 -- consensus/src/rand/rand_gen/mod.rs | 15 - .../src/rand/rand_gen/network_messages.rs | 115 - consensus/src/rand/rand_gen/rand_manager.rs | 418 --- consensus/src/rand/rand_gen/rand_store.rs | 505 ---- .../rand/rand_gen/reliable_broadcast_state.rs | 144 - consensus/src/rand/rand_gen/storage/db.rs | 121 - .../src/rand/rand_gen/storage/in_memory.rs | 78 - .../src/rand/rand_gen/storage/interface.rs | 23 - consensus/src/rand/rand_gen/storage/mod.rs | 6 - consensus/src/rand/rand_gen/storage/schema.rs | 96 - consensus/src/rand/rand_gen/test_utils.rs | 68 - consensus/src/rand/rand_gen/types.rs | 557 ---- consensus/src/recovery_manager.rs | 160 -- consensus/src/round_manager.rs | 1220 --------- consensus/src/round_manager_fuzzing.rs | 264 -- consensus/src/round_manager_test.rs | 2320 ----------------- consensus/src/state_computer.rs | 565 ---- consensus/src/state_computer_tests.rs | 263 -- consensus/src/state_replication.rs | 84 - .../src/test_utils/mock_execution_client.rs | 170 -- .../src/test_utils/mock_payload_manager.rs | 77 - .../test_utils/mock_quorum_store_sender.rs | 85 - .../src/test_utils/mock_state_computer.rs | 161 -- consensus/src/test_utils/mock_storage.rs | 312 --- consensus/src/test_utils/mod.rs | 261 -- consensus/src/transaction_deduper.rs | 33 - consensus/src/transaction_filter/mod.rs | 290 --- .../fairness/conflict_key/entry_fun.rs | 45 - .../fairness/conflict_key/entry_fun_module.rs | 39 - .../fairness/conflict_key/mod.rs | 117 - .../fairness/conflict_key/test_utils.rs | 190 -- .../fairness/conflict_key/txn_sender.rs | 19 - .../fairness/conflict_zone.rs | 69 - .../src/transaction_shuffler/fairness/mod.rs | 220 -- .../fairness/pending_zone.rs | 70 - .../fairness/selection_tracker.rs | 43 - .../fairness/tests/manual.rs | 169 -- .../fairness/tests/mod.rs | 5 - .../fairness/tests/proptests.rs | 104 - consensus/src/transaction_shuffler/mod.rs | 69 - .../src/transaction_shuffler/sender_aware.rs | 532 ---- consensus/src/twins/basic_twins_test.rs | 315 --- consensus/src/twins/mod.rs | 6 - consensus/src/twins/twins_node.rs | 304 --- .../src/txn_hash_and_authenticator_deduper.rs | 363 --- consensus/src/txn_notifier.rs | 92 - consensus/src/util/db_tool.rs | 107 - consensus/src/util/mock_time_service.rs | 138 - consensus/src/util/mod.rs | 24 - consensus/src/util/time_service.rs | 148 -- crates/aptos-admin-service/Cargo.toml | 42 - crates/aptos-admin-service/src/lib.rs | 6 - .../src/server/consensus/mod.rs | 241 -- crates/aptos-admin-service/src/server/mod.rs | 217 -- .../src/server/profiling.rs | 132 - .../src/server/thread_dump.rs | 173 -- .../aptos-admin-service/src/server/utils.rs | 55 - crates/aptos-debugger/Cargo.toml | 23 - crates/aptos-debugger/src/lib.rs | 35 - crates/aptos-debugger/src/main.rs | 16 - .../indexer-grpc-cache-worker/Cargo.toml | 49 - .../indexer-grpc-cache-worker/README.md | 22 - .../indexer-grpc-cache-worker/src/lib.rs | 66 - .../indexer-grpc-cache-worker/src/main.rs | 20 - .../indexer-grpc-cache-worker/src/metrics.rs | 63 - .../indexer-grpc-cache-worker/src/worker.rs | 517 ---- .../indexer-grpc-data-service/Cargo.toml | 44 - .../indexer-grpc-data-service/README.md | 69 - .../indexer-grpc-data-service/src/config.rs | 241 -- .../src/grpc_response_stream.rs | 62 - .../indexer-grpc-data-service/src/lib.rs | 10 - .../indexer-grpc-data-service/src/main.rs | 17 - .../indexer-grpc-data-service/src/metrics.rs | 99 - .../grpc_response_dispatcher.rs | 404 --- .../src/response_dispatcher/mod.rs | 34 - .../indexer-grpc-data-service/src/service.rs | 978 ------- .../indexer-grpc-file-store/Cargo.toml | 38 - .../indexer-grpc-file-store/README.md | 56 - .../indexer-grpc-file-store/src/lib.rs | 67 - .../indexer-grpc-file-store/src/main.rs | 20 - .../indexer-grpc-file-store/src/metrics.rs | 41 - .../indexer-grpc-file-store/src/processor.rs | 298 --- .../indexer-grpc-server-framework/Cargo.toml | 34 - .../indexer-grpc-server-framework/README.md | 29 - .../indexer-grpc-server-framework/src/lib.rs | 290 --- .../nft-metadata-crawler-parser/.gitignore | 2 - .../nft-metadata-crawler-parser/Cargo.toml | 48 - .../nft-metadata-crawler-parser/diesel.toml | 10 - .../down.sql | 2 - .../up.sql | 18 - .../2023-09-08-001532_create_tables/down.sql | 3 - .../2023-09-08-001532_create_tables/up.sql | 20 - .../down.sql | 1 - .../up.sql | 1 - .../down.sql | 1 - .../up.sql | 1 - .../nft-metadata-crawler-parser/src/config.rs | 281 -- .../nft-metadata-crawler-parser/src/lib.rs | 36 - .../nft-metadata-crawler-parser/src/main.rs | 10 - .../src/models/ledger_info.rs | 26 - .../src/models/mod.rs | 5 - .../src/models/nft_metadata_crawler_uris.rs | 190 -- .../models/nft_metadata_crawler_uris_query.rs | 127 - .../nft-metadata-crawler-parser/src/schema.rs | 30 - .../src/utils/constants.rs | 28 - .../src/utils/counters.rs | 234 -- .../src/utils/database.rs | 106 - .../src/utils/gcs.rs | 132 - .../src/utils/image_optimizer.rs | 164 -- .../src/utils/json_parser.rs | 97 - .../src/utils/mod.rs | 9 - .../src/utils/uri_parser.rs | 140 - .../nft-metadata-crawler-parser/src/worker.rs | 433 --- testsuite/generate-format/Cargo.toml | 35 - testsuite/generate-format/README.md | 170 -- testsuite/generate-format/src/api.rs | 140 - testsuite/generate-format/src/aptos.rs | 120 - testsuite/generate-format/src/compute.rs | 46 - testsuite/generate-format/src/consensus.rs | 125 - testsuite/generate-format/src/lib.rs | 112 - testsuite/generate-format/src/linter.rs | 60 - testsuite/generate-format/src/move_abi.rs | 28 - testsuite/generate-format/src/network.rs | 56 - .../tests/detect_format_change.rs | 124 - testsuite/generate-format/tests/linter.rs | 37 - .../generate-format/tests/staged/api.yaml | 821 ------ .../generate-format/tests/staged/aptos.yaml | 703 ----- .../tests/staged/consensus.yaml | 1135 -------- .../tests/staged/move_abi.yaml | 94 - .../generate-format/tests/staged/network.yaml | 199 -- 288 files changed, 2 insertions(+), 56996 deletions(-) delete mode 100644 aptos-move/aptos-debugger/Cargo.toml delete mode 100644 aptos-move/aptos-debugger/example-txn.txt delete mode 100644 aptos-move/aptos-debugger/src/aptos_debugger.rs delete mode 100644 aptos-move/aptos-debugger/src/bcs_txn_decoder.rs delete mode 100644 aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs delete mode 100644 aptos-move/aptos-debugger/src/common.rs delete mode 100644 aptos-move/aptos-debugger/src/execute_past_transactions.rs delete mode 100644 aptos-move/aptos-debugger/src/execute_pending_block.rs delete mode 100644 aptos-move/aptos-debugger/src/lib.rs delete mode 100644 consensus/Cargo.toml delete mode 100644 consensus/README.md delete mode 100644 consensus/src/block_preparer.rs delete mode 100644 consensus/src/block_storage/block_store.rs delete mode 100644 consensus/src/block_storage/block_store_test.rs delete mode 100644 consensus/src/block_storage/block_tree.rs delete mode 100644 consensus/src/block_storage/mod.rs delete mode 100644 consensus/src/block_storage/sync_manager.rs delete mode 100644 consensus/src/block_storage/tracing.rs delete mode 100644 consensus/src/consensus_provider.rs delete mode 100644 consensus/src/consensusdb/consensusdb_test.rs delete mode 100644 consensus/src/consensusdb/mod.rs delete mode 100644 consensus/src/consensusdb/schema/block/mod.rs delete mode 100644 consensus/src/consensusdb/schema/block/test.rs delete mode 100644 consensus/src/consensusdb/schema/dag/mod.rs delete mode 100644 consensus/src/consensusdb/schema/mod.rs delete mode 100644 consensus/src/consensusdb/schema/quorum_certificate/mod.rs delete mode 100644 consensus/src/consensusdb/schema/quorum_certificate/test.rs delete mode 100644 consensus/src/consensusdb/schema/single_entry/mod.rs delete mode 100644 consensus/src/consensusdb/schema/single_entry/test.rs delete mode 100644 consensus/src/counters.rs delete mode 100644 consensus/src/dag/adapter.rs delete mode 100644 consensus/src/dag/anchor_election/leader_reputation_adapter.rs delete mode 100644 consensus/src/dag/anchor_election/mod.rs delete mode 100644 consensus/src/dag/anchor_election/round_robin.rs delete mode 100644 consensus/src/dag/bootstrap.rs delete mode 100644 consensus/src/dag/commit_signer.rs delete mode 100644 consensus/src/dag/dag_driver.rs delete mode 100644 consensus/src/dag/dag_fetcher.rs delete mode 100644 consensus/src/dag/dag_handler.rs delete mode 100644 consensus/src/dag/dag_network.rs delete mode 100644 consensus/src/dag/dag_state_sync.rs delete mode 100644 consensus/src/dag/dag_store.rs delete mode 100644 consensus/src/dag/errors.rs delete mode 100644 consensus/src/dag/health/backoff.rs delete mode 100644 consensus/src/dag/health/chain_health.rs delete mode 100644 consensus/src/dag/health/mod.rs delete mode 100644 consensus/src/dag/health/pipeline_health.rs delete mode 100644 consensus/src/dag/mod.rs delete mode 100644 consensus/src/dag/observability/counters.rs delete mode 100644 consensus/src/dag/observability/logging.rs delete mode 100644 consensus/src/dag/observability/mod.rs delete mode 100644 consensus/src/dag/observability/tracing.rs delete mode 100644 consensus/src/dag/order_rule.rs delete mode 100644 consensus/src/dag/rb_handler.rs delete mode 100644 consensus/src/dag/round_state.rs delete mode 100644 consensus/src/dag/storage.rs delete mode 100644 consensus/src/dag/tests/dag_driver_tests.rs delete mode 100644 consensus/src/dag/tests/dag_network_test.rs delete mode 100644 consensus/src/dag/tests/dag_state_sync_tests.rs delete mode 100644 consensus/src/dag/tests/dag_test.rs delete mode 100644 consensus/src/dag/tests/fetcher_test.rs delete mode 100644 consensus/src/dag/tests/helpers.rs delete mode 100644 consensus/src/dag/tests/integration_tests.rs delete mode 100644 consensus/src/dag/tests/mod.rs delete mode 100644 consensus/src/dag/tests/order_rule_tests.rs delete mode 100644 consensus/src/dag/tests/rb_handler_tests.rs delete mode 100644 consensus/src/dag/tests/types_test.rs delete mode 100644 consensus/src/dag/types.rs delete mode 100644 consensus/src/epoch_manager.rs delete mode 100644 consensus/src/error.rs delete mode 100644 consensus/src/execution_pipeline.rs delete mode 100644 consensus/src/lib.rs delete mode 100644 consensus/src/liveness/cached_proposer_election.rs delete mode 100644 consensus/src/liveness/cached_proposer_election_test.rs delete mode 100644 consensus/src/liveness/leader_reputation.rs delete mode 100644 consensus/src/liveness/leader_reputation_test.rs delete mode 100644 consensus/src/liveness/mod.rs delete mode 100644 consensus/src/liveness/proposal_generator.rs delete mode 100644 consensus/src/liveness/proposal_generator_test.rs delete mode 100644 consensus/src/liveness/proposer_election.rs delete mode 100644 consensus/src/liveness/rotating_proposer_election.rs delete mode 100644 consensus/src/liveness/rotating_proposer_test.rs delete mode 100644 consensus/src/liveness/round_proposer_election.rs delete mode 100644 consensus/src/liveness/round_proposer_test.rs delete mode 100644 consensus/src/liveness/round_state.rs delete mode 100644 consensus/src/liveness/round_state_test.rs delete mode 100644 consensus/src/liveness/unequivocal_proposer_election.rs delete mode 100644 consensus/src/liveness/unequivocal_proposer_election_test.rs delete mode 100644 consensus/src/logging.rs delete mode 100644 consensus/src/metrics_safety_rules.rs delete mode 100644 consensus/src/network.rs delete mode 100644 consensus/src/network_interface.rs delete mode 100644 consensus/src/network_tests.rs delete mode 100644 consensus/src/payload_client/mixed.rs delete mode 100644 consensus/src/payload_client/mod.rs delete mode 100644 consensus/src/payload_client/user/mod.rs delete mode 100644 consensus/src/payload_client/user/quorum_store_client.rs delete mode 100644 consensus/src/payload_client/validator.rs delete mode 100644 consensus/src/payload_manager.rs delete mode 100644 consensus/src/pending_votes.rs delete mode 100644 consensus/src/persistent_liveness_storage.rs delete mode 100644 consensus/src/pipeline/buffer.rs delete mode 100644 consensus/src/pipeline/buffer_item.rs delete mode 100644 consensus/src/pipeline/buffer_manager.rs delete mode 100644 consensus/src/pipeline/commit_reliable_broadcast.rs delete mode 100644 consensus/src/pipeline/decoupled_execution_utils.rs delete mode 100644 consensus/src/pipeline/errors.rs delete mode 100644 consensus/src/pipeline/execution_client.rs delete mode 100644 consensus/src/pipeline/execution_phase.rs delete mode 100644 consensus/src/pipeline/execution_schedule_phase.rs delete mode 100644 consensus/src/pipeline/execution_wait_phase.rs delete mode 100644 consensus/src/pipeline/hashable.rs delete mode 100644 consensus/src/pipeline/linkedlist.rs delete mode 100644 consensus/src/pipeline/mod.rs delete mode 100644 consensus/src/pipeline/persisting_phase.rs delete mode 100644 consensus/src/pipeline/pipeline_phase.rs delete mode 100644 consensus/src/pipeline/signing_phase.rs delete mode 100644 consensus/src/pipeline/tests/buffer_manager_tests.rs delete mode 100644 consensus/src/pipeline/tests/execution_phase_tests.rs delete mode 100644 consensus/src/pipeline/tests/integration_tests.rs delete mode 100644 consensus/src/pipeline/tests/mod.rs delete mode 100644 consensus/src/pipeline/tests/ordering_state_computer_tests.rs delete mode 100644 consensus/src/pipeline/tests/phase_tester.rs delete mode 100644 consensus/src/pipeline/tests/signing_phase_tests.rs delete mode 100644 consensus/src/pipeline/tests/test_utils.rs delete mode 100644 consensus/src/qc_aggregator.rs delete mode 100644 consensus/src/quorum_store/batch_coordinator.rs delete mode 100644 consensus/src/quorum_store/batch_generator.rs delete mode 100644 consensus/src/quorum_store/batch_requester.rs delete mode 100644 consensus/src/quorum_store/batch_store.rs delete mode 100644 consensus/src/quorum_store/counters.rs delete mode 100644 consensus/src/quorum_store/direct_mempool_quorum_store.rs delete mode 100644 consensus/src/quorum_store/mod.rs delete mode 100644 consensus/src/quorum_store/network_listener.rs delete mode 100644 consensus/src/quorum_store/proof_coordinator.rs delete mode 100644 consensus/src/quorum_store/proof_manager.rs delete mode 100644 consensus/src/quorum_store/quorum_store_builder.rs delete mode 100644 consensus/src/quorum_store/quorum_store_coordinator.rs delete mode 100644 consensus/src/quorum_store/quorum_store_db.rs delete mode 100644 consensus/src/quorum_store/schema.rs delete mode 100644 consensus/src/quorum_store/tests/batch_generator_test.rs delete mode 100644 consensus/src/quorum_store/tests/batch_requester_test.rs delete mode 100644 consensus/src/quorum_store/tests/batch_store_test.rs delete mode 100644 consensus/src/quorum_store/tests/direct_mempool_quorum_store_test.rs delete mode 100644 consensus/src/quorum_store/tests/mod.rs delete mode 100644 consensus/src/quorum_store/tests/proof_coordinator_test.rs delete mode 100644 consensus/src/quorum_store/tests/proof_manager_test.rs delete mode 100644 consensus/src/quorum_store/tests/quorum_store_db_test.rs delete mode 100644 consensus/src/quorum_store/tests/types_test.rs delete mode 100644 consensus/src/quorum_store/tests/utils.rs delete mode 100644 consensus/src/quorum_store/types.rs delete mode 100644 consensus/src/quorum_store/utils.rs delete mode 100644 consensus/src/rand/dkg/mod.rs delete mode 100644 consensus/src/rand/mod.rs delete mode 100644 consensus/src/rand/rand_gen/aug_data_store.rs delete mode 100644 consensus/src/rand/rand_gen/block_queue.rs delete mode 100644 consensus/src/rand/rand_gen/mod.rs delete mode 100644 consensus/src/rand/rand_gen/network_messages.rs delete mode 100644 consensus/src/rand/rand_gen/rand_manager.rs delete mode 100644 consensus/src/rand/rand_gen/rand_store.rs delete mode 100644 consensus/src/rand/rand_gen/reliable_broadcast_state.rs delete mode 100644 consensus/src/rand/rand_gen/storage/db.rs delete mode 100644 consensus/src/rand/rand_gen/storage/in_memory.rs delete mode 100644 consensus/src/rand/rand_gen/storage/interface.rs delete mode 100644 consensus/src/rand/rand_gen/storage/mod.rs delete mode 100644 consensus/src/rand/rand_gen/storage/schema.rs delete mode 100644 consensus/src/rand/rand_gen/test_utils.rs delete mode 100644 consensus/src/rand/rand_gen/types.rs delete mode 100644 consensus/src/recovery_manager.rs delete mode 100644 consensus/src/round_manager.rs delete mode 100644 consensus/src/round_manager_fuzzing.rs delete mode 100644 consensus/src/round_manager_test.rs delete mode 100644 consensus/src/state_computer.rs delete mode 100644 consensus/src/state_computer_tests.rs delete mode 100644 consensus/src/state_replication.rs delete mode 100644 consensus/src/test_utils/mock_execution_client.rs delete mode 100644 consensus/src/test_utils/mock_payload_manager.rs delete mode 100644 consensus/src/test_utils/mock_quorum_store_sender.rs delete mode 100644 consensus/src/test_utils/mock_state_computer.rs delete mode 100644 consensus/src/test_utils/mock_storage.rs delete mode 100644 consensus/src/test_utils/mod.rs delete mode 100644 consensus/src/transaction_deduper.rs delete mode 100644 consensus/src/transaction_filter/mod.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/mod.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/conflict_zone.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/mod.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/pending_zone.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/selection_tracker.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/tests/manual.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/tests/mod.rs delete mode 100644 consensus/src/transaction_shuffler/fairness/tests/proptests.rs delete mode 100644 consensus/src/transaction_shuffler/mod.rs delete mode 100644 consensus/src/transaction_shuffler/sender_aware.rs delete mode 100644 consensus/src/twins/basic_twins_test.rs delete mode 100644 consensus/src/twins/mod.rs delete mode 100644 consensus/src/twins/twins_node.rs delete mode 100644 consensus/src/txn_hash_and_authenticator_deduper.rs delete mode 100644 consensus/src/txn_notifier.rs delete mode 100644 consensus/src/util/db_tool.rs delete mode 100644 consensus/src/util/mock_time_service.rs delete mode 100644 consensus/src/util/mod.rs delete mode 100644 consensus/src/util/time_service.rs delete mode 100644 crates/aptos-admin-service/Cargo.toml delete mode 100644 crates/aptos-admin-service/src/lib.rs delete mode 100644 crates/aptos-admin-service/src/server/consensus/mod.rs delete mode 100644 crates/aptos-admin-service/src/server/mod.rs delete mode 100644 crates/aptos-admin-service/src/server/profiling.rs delete mode 100644 crates/aptos-admin-service/src/server/thread_dump.rs delete mode 100644 crates/aptos-admin-service/src/server/utils.rs delete mode 100644 crates/aptos-debugger/Cargo.toml delete mode 100644 crates/aptos-debugger/src/lib.rs delete mode 100644 crates/aptos-debugger/src/main.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/Cargo.toml delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/metrics.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/worker.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/Cargo.toml delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/README.md delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/config.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/grpc_response_stream.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/lib.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/main.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/metrics.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/grpc_response_dispatcher.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/mod.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-data-service/src/service.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/Cargo.toml delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/README.md delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/metrics.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/processor.rs delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-server-framework/Cargo.toml delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md delete mode 100644 ecosystem/indexer-grpc/indexer-grpc-server-framework/src/lib.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/.gitignore delete mode 100644 ecosystem/nft-metadata-crawler-parser/Cargo.toml delete mode 100644 ecosystem/nft-metadata-crawler-parser/diesel.toml delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/down.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/up.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/down.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/up.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/down.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/up.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/down.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/up.sql delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/config.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/lib.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/main.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/ledger_info.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/mod.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/schema.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/database.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs delete mode 100644 ecosystem/nft-metadata-crawler-parser/src/worker.rs delete mode 100644 testsuite/generate-format/Cargo.toml delete mode 100644 testsuite/generate-format/README.md delete mode 100644 testsuite/generate-format/src/api.rs delete mode 100644 testsuite/generate-format/src/aptos.rs delete mode 100644 testsuite/generate-format/src/compute.rs delete mode 100644 testsuite/generate-format/src/consensus.rs delete mode 100644 testsuite/generate-format/src/lib.rs delete mode 100644 testsuite/generate-format/src/linter.rs delete mode 100644 testsuite/generate-format/src/move_abi.rs delete mode 100644 testsuite/generate-format/src/network.rs delete mode 100644 testsuite/generate-format/tests/detect_format_change.rs delete mode 100644 testsuite/generate-format/tests/linter.rs delete mode 100644 testsuite/generate-format/tests/staged/api.yaml delete mode 100644 testsuite/generate-format/tests/staged/aptos.yaml delete mode 100644 testsuite/generate-format/tests/staged/consensus.yaml delete mode 100644 testsuite/generate-format/tests/staged/move_abi.yaml delete mode 100644 testsuite/generate-format/tests/staged/network.yaml diff --git a/Cargo.lock b/Cargo.lock index 099d576029ba7..dce1af3b5f30f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,12 +202,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "antidote" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" - [[package]] name = "anyhow" version = "1.0.82" @@ -237,37 +231,6 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "aptos-admin-service" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-config", - "aptos-consensus", - "aptos-consensus-types", - "aptos-crypto", - "aptos-infallible", - "aptos-logger", - "aptos-profiler", - "aptos-runtimes", - "aptos-storage-interface", - "aptos-types", - "async-mutex", - "bcs 0.1.4", - "futures", - "http 0.2.12", - "hyper", - "lazy_static", - "mime", - "pprof", - "regex", - "rstack-self", - "sha256", - "tokio", - "tokio-scoped", - "url", -] - [[package]] name = "aptos-aggregator" version = "0.1.0" @@ -706,87 +669,6 @@ dependencies = [ "url", ] -[[package]] -name = "aptos-consensus" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-bitvec", - "aptos-bounded-executor", - "aptos-cached-packages", - "aptos-channels", - "aptos-collections", - "aptos-config", - "aptos-consensus-notifications", - "aptos-consensus-types", - "aptos-crypto", - "aptos-crypto-derive", - "aptos-dkg", - "aptos-enum-conversion-derive", - "aptos-event-notifications", - "aptos-executor", - "aptos-executor-test-helpers", - "aptos-executor-types", - "aptos-experimental-runtimes", - "aptos-fallible", - "aptos-global-constants", - "aptos-infallible", - "aptos-keygen", - "aptos-logger", - "aptos-mempool", - "aptos-metrics-core", - "aptos-network", - "aptos-reliable-broadcast", - "aptos-runtimes", - "aptos-safety-rules", - "aptos-schemadb", - "aptos-secure-storage", - "aptos-short-hex-str", - "aptos-storage-interface", - "aptos-temppath", - "aptos-time-service", - "aptos-types", - "aptos-validator-transaction-pool", - "aptos-vm", - "aptos-vm-validator", - "arc-swap", - "async-trait", - "bcs 0.1.4", - "byteorder", - "bytes", - "chrono", - "claims", - "clap 4.5.4", - "dashmap", - "enum_dispatch", - "fail 0.5.1", - "futures", - "futures-channel", - "hex", - "itertools 0.10.5", - "maplit", - "mirai-annotations", - "move-core-types", - "num-derive", - "num-traits", - "once_cell", - "proptest", - "rand 0.7.3", - "rayon", - "scopeguard", - "serde", - "serde_bytes", - "serde_json", - "serde_yaml 0.8.26", - "sha3 0.9.1", - "strum_macros 0.24.3", - "tempfile", - "thiserror", - "tokio", - "tokio-metrics", - "tokio-retry", -] - [[package]] name = "aptos-consensus-notifications" version = "0.1.0" @@ -1077,20 +959,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "aptos-debugger" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-consensus", - "aptos-db-tool", - "aptos-logger", - "aptos-move-debugger", - "aptos-push-metrics", - "clap 4.5.4", - "tokio", -] - [[package]] name = "aptos-dkg" version = "0.1.0" @@ -1781,39 +1649,6 @@ dependencies = [ "url", ] -[[package]] -name = "aptos-indexer-grpc-cache-worker" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-config", - "aptos-indexer-grpc-server-framework", - "aptos-indexer-grpc-utils", - "aptos-metrics-core", - "aptos-moving-average", - "aptos-protos", - "aptos-runtimes", - "async-trait", - "backoff", - "base64 0.13.1", - "clap 4.5.4", - "futures", - "futures-core", - "jemallocator", - "once_cell", - "prost 0.12.4", - "redis", - "reqwest", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tempfile", - "tokio", - "tonic 0.11.0", - "tracing", - "url", -] - [[package]] name = "aptos-indexer-grpc-data-access" version = "1.0.0" @@ -1854,65 +1689,6 @@ dependencies = [ "warp", ] -[[package]] -name = "aptos-indexer-grpc-data-service" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-indexer-grpc-data-access", - "aptos-indexer-grpc-server-framework", - "aptos-indexer-grpc-utils", - "aptos-logger", - "aptos-metrics-core", - "aptos-moving-average", - "aptos-protos", - "aptos-runtimes", - "async-trait", - "base64 0.13.1", - "clap 4.5.4", - "cloud-storage", - "futures", - "jemallocator", - "once_cell", - "prost 0.12.4", - "redis", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tonic 0.11.0", - "tonic-reflection", - "tracing", - "uuid", -] - -[[package]] -name = "aptos-indexer-grpc-file-store" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-indexer-grpc-server-framework", - "aptos-indexer-grpc-utils", - "aptos-metrics-core", - "aptos-moving-average", - "aptos-protos", - "aptos-runtimes", - "async-trait", - "base64 0.13.1", - "clap 4.5.4", - "cloud-storage", - "futures", - "futures-util", - "jemallocator", - "once_cell", - "prost 0.12.4", - "redis", - "serde", - "serde_json", - "tokio", - "tracing", -] - [[package]] name = "aptos-indexer-grpc-fullnode" version = "1.0.0" @@ -1971,29 +1747,6 @@ dependencies = [ "tonic-reflection", ] -[[package]] -name = "aptos-indexer-grpc-server-framework" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-admin-service", - "aptos-metrics-core", - "aptos-runtimes", - "async-trait", - "backtrace", - "clap 4.5.4", - "futures", - "prometheus", - "serde", - "serde_yaml 0.8.26", - "tempfile", - "tokio", - "toml 0.7.8", - "tracing", - "tracing-subscriber 0.3.18", - "warp", -] - [[package]] name = "aptos-indexer-grpc-table-info" version = "1.0.0" @@ -2418,41 +2171,6 @@ dependencies = [ "prometheus", ] -[[package]] -name = "aptos-move-debugger" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-consensus", - "aptos-crypto", - "aptos-gas-meter", - "aptos-gas-profiling", - "aptos-gas-schedule", - "aptos-logger", - "aptos-memory-usage-tracker", - "aptos-resource-viewer", - "aptos-rest-client", - "aptos-table-natives", - "aptos-types", - "aptos-validator-interface", - "aptos-vm", - "aptos-vm-logging", - "aptos-vm-types", - "bcs 0.1.4", - "clap 4.5.4", - "move-binary-format", - "move-cli", - "move-compiler", - "move-core-types", - "move-resource-viewer", - "move-vm-runtime", - "move-vm-test-utils", - "regex", - "reqwest", - "tokio", - "url", -] - [[package]] name = "aptos-move-e2e-benchmark" version = "0.1.0" @@ -2719,39 +2437,6 @@ dependencies = [ "url", ] -[[package]] -name = "aptos-nft-metadata-crawler-parser" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-indexer-grpc-server-framework", - "aptos-metrics-core", - "aptos-runtimes", - "async-trait", - "backoff", - "base64 0.13.1", - "bytes", - "chrono", - "clap 4.5.4", - "csv", - "diesel", - "diesel_migrations", - "field_count", - "futures", - "google-cloud-storage", - "image", - "once_cell", - "regex", - "reqwest", - "serde", - "serde_json", - "sha256", - "tokio", - "tracing", - "url", - "warp", -] - [[package]] name = "aptos-node-checker" version = "0.1.1" @@ -4303,15 +3988,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-object-pool" version = "0.1.4" @@ -4783,12 +4459,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -6427,27 +6097,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "dw" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef0ed82b765c2ab79fb48e4bf2c95bd583202f4078a702bc714cc6e6f3ca80c3" -dependencies = [ - "dw-sys", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "dw-sys" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14eb35c87ff6626cd1021bb32bc7d9a5372ea72547e1eaf0343a841d9d55a973" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "e2e-move-tests" version = "0.1.0" @@ -6990,22 +6639,6 @@ dependencies = [ "sha3 0.10.8", ] -[[package]] -name = "exr" -version = "1.72.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" -dependencies = [ - "bit_field", - "flume", - "half 2.4.1", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - [[package]] name = "fail" version = "0.4.0" @@ -7215,15 +6848,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin 0.9.8", -] - [[package]] name = "fnv" version = "1.0.7" @@ -7236,28 +6860,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "foreign-types-shared", ] [[package]] @@ -7266,12 +6869,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -7480,27 +7077,6 @@ dependencies = [ "yup-oauth2", ] -[[package]] -name = "generate-format" -version = "0.1.0" -dependencies = [ - "aptos-api-types", - "aptos-config", - "aptos-consensus", - "aptos-consensus-types", - "aptos-crypto", - "aptos-crypto-derive", - "aptos-network", - "aptos-types", - "bcs 0.1.4", - "clap 4.5.4", - "move-core-types", - "rand 0.7.3", - "serde", - "serde-reflection", - "serde_yaml 0.8.26", -] - [[package]] name = "generic-array" version = "0.12.4" @@ -7579,16 +7155,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.28.1" @@ -8352,13 +7918,8 @@ dependencies = [ "bytemuck", "byteorder", "color_quant", - "exr", - "gif", - "jpeg-decoder", "num-traits", "png", - "qoi", - "tiff", ] [[package]] @@ -8690,15 +8251,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -8847,12 +8399,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "ledger-apdu" version = "0.10.0" @@ -10848,7 +10394,7 @@ checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.5.0", "cfg-if", - "foreign-types 0.3.2", + "foreign-types", "libc", "once_cell", "openssl-macros", @@ -12137,15 +11683,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "qstring" version = "0.7.2" @@ -12743,34 +12280,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rstack" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7df9d3ebd4f17b52e6134efe2fa20021c80688cbe823d481a729a993b730493" -dependencies = [ - "cfg-if", - "dw", - "lazy_static", - "libc", - "log", -] - -[[package]] -name = "rstack-self" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd5030da3aba0ec731502f74ec38e63798eea6bc8b8ba5972129afe3eababd2" -dependencies = [ - "antidote", - "backtrace", - "bincode", - "lazy_static", - "libc", - "rstack", - "serde", -] - [[package]] name = "rstest" version = "0.15.0" @@ -13436,19 +12945,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha256" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2 0.10.8", - "tokio", -] - [[package]] name = "sha3" version = "0.8.2" @@ -13768,9 +13264,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -14281,17 +13774,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - [[package]] name = "time" version = "0.3.36" @@ -14419,18 +13901,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "tokio-metrics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", - "tokio-stream", -] - [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -14484,16 +13954,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-scoped" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4beb8ba13bc53ac53ce1d52b42f02e5d8060f0f42138862869beb769722b256" -dependencies = [ - "tokio", - "tokio-stream", -] - [[package]] name = "tokio-stream" version = "0.1.15" @@ -15241,10 +14701,6 @@ name = "uuid" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom 0.2.14", - "serde", -] [[package]] name = "valuable" @@ -15509,12 +14965,6 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "which" version = "4.4.2" @@ -15983,12 +15433,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index d9bf2a1150463..66c6ce779e090 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "api/types", "aptos-move/aptos-abstract-gas-usage", "aptos-move/aptos-aggregator", - "aptos-move/aptos-debugger", "aptos-move/aptos-gas-algebra", "aptos-move/aptos-gas-meter", "aptos-move/aptos-gas-profiling", @@ -41,10 +40,8 @@ members = [ "aptos-utils", "config", "config/global-constants", - "consensus", "consensus/consensus-types", "consensus/safety-rules", - "crates/aptos-admin-service", "crates/aptos-api-tester", "crates/aptos-bcs-utils", "crates/aptos-bitvec", @@ -53,7 +50,6 @@ members = [ "crates/aptos-compression", "crates/aptos-crypto", "crates/aptos-crypto-derive", - "crates/aptos-debugger", "crates/aptos-dkg", "crates/aptos-drop-helper", "crates/aptos-enum-conversion-derive", @@ -101,15 +97,10 @@ members = [ "crates/validator-transaction-pool", "devtools/aptos-cargo-cli", "dkg", - "ecosystem/indexer-grpc/indexer-grpc-cache-worker", "ecosystem/indexer-grpc/indexer-grpc-data-access", - "ecosystem/indexer-grpc/indexer-grpc-data-service", - "ecosystem/indexer-grpc/indexer-grpc-file-store", "ecosystem/indexer-grpc/indexer-grpc-fullnode", - "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/indexer-grpc-table-info", "ecosystem/indexer-grpc/indexer-grpc-utils", - "ecosystem/nft-metadata-crawler-parser", "ecosystem/node-checker", "ecosystem/node-checker/fn-check-client", "execution/block-partitioner", @@ -163,7 +154,6 @@ members = [ "testsuite/dos/sender", "testsuite/fuzzer", "testsuite/fuzzer/fuzz", - "testsuite/generate-format", "testsuite/module-publish", # third_party/move "third_party/move/extensions/async/move-async-vm", @@ -232,7 +222,6 @@ members = [ # For more, see the "Conditional compilation for tests" section in documentation/coding_guidelines.md. default-members = [ "consensus/safety-rules", - "crates/aptos-debugger", "crates/aptos-faucet/service", "crates/aptos-keygen", "crates/aptos-rate-limiter", @@ -257,7 +246,6 @@ rust-version = "1.75.0" # Internal crate dependencies. # Please do not add any test features here: they should be declared by the individual crate. aptos-accumulator = { path = "storage/accumulator" } -aptos-admin-service = { path = "crates/aptos-admin-service" } aptos-aggregator = { path = "aptos-move/aptos-aggregator" } aptos-api = { path = "api" } aptos-api-test-context = { path = "api/test-context" } @@ -274,7 +262,6 @@ aptos-channels = { path = "crates/channel" } aptos-cli-common = { path = "crates/aptos-cli-common" } aptos-collections = { path = "crates/aptos-collections" } aptos-compression = { path = "crates/aptos-compression" } -aptos-consensus = { path = "consensus" } aptos-consensus-notifications = { path = "state-sync/inter-component/consensus-notifications" } aptos-consensus-types = { path = "consensus/consensus-types" } aptos-config = { path = "config" } @@ -286,7 +273,6 @@ aptos-data-streaming-service = { path = "state-sync/data-streaming-service" } aptos-db = { path = "storage/aptosdb" } aptos-db-indexer = { path = "storage/indexer" } aptos-db-tool = { path = "storage/db-tool" } -aptos-debugger = { path = "crates/aptos-debugger" } aptos-dkg = { path = "crates/aptos-dkg" } aptos-dkg-runtime = { path = "dkg" } aptos-drop-helper = { path = "crates/aptos-drop-helper" } @@ -318,14 +304,10 @@ aptos-github-client = { path = "crates/aptos-github-client" } aptos-global-constants = { path = "config/global-constants" } aptos-id-generator = { path = "crates/aptos-id-generator" } aptos-indexer = { path = "crates/indexer" } -aptos-indexer-grpc-cache-worker = { path = "ecosystem/indexer-grpc/indexer-grpc-cache-worker" } -aptos-indexer-grpc-data-service = { path = "ecosystem/indexer-grpc/indexer-grpc-data-service" } aptos-indexer-grpc-data-access = { path = "ecosystem/indexer-grpc/indexer-grpc-data-access" } -aptos-indexer-grpc-file-store = { path = "ecosystem/indexer-grpc/indexer-grpc-file-store" } aptos-indexer-grpc-fullnode = { path = "ecosystem/indexer-grpc/indexer-grpc-fullnode" } aptos-indexer-grpc-table-info = { path = "ecosystem/indexer-grpc/indexer-grpc-table-info" } aptos-indexer-grpc-utils = { path = "ecosystem/indexer-grpc/indexer-grpc-utils" } -aptos-indexer-grpc-server-framework = { path = "ecosystem/indexer-grpc/indexer-grpc-server-framework" } aptos-infallible = { path = "crates/aptos-infallible" } aptos-jellyfish-merkle = { path = "storage/jellyfish-merkle" } aptos-jwk-consensus = { path = "crates/aptos-jwk-consensus" } @@ -339,7 +321,6 @@ aptos-mempool = { path = "mempool" } aptos-mempool-notifications = { path = "state-sync/inter-component/mempool-notifications" } aptos-memsocket = { path = "network/memsocket" } aptos-metrics-core = { path = "crates/aptos-metrics-core" } -aptos-move-debugger = { path = "aptos-move/aptos-debugger" } aptos-move-examples = { path = "aptos-move/move-examples" } aptos-move-e2e-benchmark = { path = "aptos-move/e2e-benchmark" } aptos-mvhashmap = { path = "aptos-move/mvhashmap" } @@ -350,7 +331,6 @@ aptos-network-benchmark = { path = "network/benchmark" } aptos-network-builder = { path = "network/builder" } aptos-network-checker = { path = "crates/aptos-network-checker" } aptos-network-discovery = { path = "network/discovery" } -aptos-nft-metadata-crawler-parser = { path = "ecosystem/nft-metadata-crawler-parser" } aptos-node-checker = { path = "ecosystem/node-checker" } aptos-node-identity = { path = "crates/aptos-node-identity" } aptos-node-resource-metrics = { path = "crates/node-resource-metrics" } diff --git a/aptos-move/aptos-debugger/Cargo.toml b/aptos-move/aptos-debugger/Cargo.toml deleted file mode 100644 index cc7f9b6e0a46c..0000000000000 --- a/aptos-move/aptos-debugger/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "aptos-move-debugger" -version = "0.1.0" -description = "A tool to replay transactions on chain and to execute transactions locally." - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-consensus = { workspace = true } -aptos-crypto = { workspace = true } -aptos-gas-meter = { workspace = true } -aptos-gas-profiling = { workspace = true } -aptos-gas-schedule = { workspace = true } -aptos-logger = { workspace = true } -aptos-memory-usage-tracker = { workspace = true } -aptos-resource-viewer = { workspace = true } -aptos-rest-client = { workspace = true } -aptos-table-natives = { workspace = true } -aptos-types = { workspace = true } -aptos-validator-interface = { workspace = true } -aptos-vm = { workspace = true } -aptos-vm-logging = { workspace = true } -aptos-vm-types = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -move-binary-format = { workspace = true } -move-cli = { workspace = true } -move-compiler = { workspace = true } -move-core-types = { workspace = true } -move-resource-viewer = { workspace = true } -move-vm-runtime = { workspace = true } -move-vm-test-utils = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -tokio = { workspace = true } -url = { workspace = true } - -[[bin]] -name = "remote-gas-profiler" diff --git a/aptos-move/aptos-debugger/example-txn.txt b/aptos-move/aptos-debugger/example-txn.txt deleted file mode 100644 index 64e4acd6c7e20..0000000000000 --- a/aptos-move/aptos-debugger/example-txn.txt +++ /dev/null @@ -1 +0,0 @@ -[65, 2, 62, 205, 111, 255, 182, 248, 10, 156, 99, 251, 214, 36, 49, 107, 225, 63, 1, 93, 30, 222, 207, 61, 178, 136, 229, 64, 145, 48, 55, 131, 4, 1, 0, 0, 0, 0, 0, 0, 2, 253, 249, 242, 150, 39, 16, 224, 114, 46, 38, 148, 6, 20, 25, 220, 21, 148, 64, 90, 94, 71, 138, 29, 35, 35, 80, 6, 154, 50, 83, 255, 148, 7, 119, 97, 114, 107, 97, 100, 101, 12, 109, 105, 110, 116, 95, 119, 97, 114, 107, 97, 100, 101, 0, 0, 160, 134, 1, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 190, 30, 118, 100, 0, 0, 0, 0, 2, 0, 32, 243, 52, 118, 7, 1, 74, 17, 119, 172, 46, 175, 211, 29, 153, 62, 59, 25, 207, 198, 210, 25, 238, 108, 2, 67, 153, 41, 80, 108, 107, 247, 63, 64, 69, 72, 73, 1, 249, 2, 60, 227, 143, 119, 179, 185, 250, 22, 94, 11, 27, 241, 3, 223, 177, 205, 77, 207, 121, 199, 94, 239, 117, 96, 102, 42, 169, 227, 80, 250, 182, 132, 103, 45, 174, 196, 87, 101, 7, 178, 68, 153, 241, 207, 83, 160, 220, 4, 0, 19, 38, 244, 109, 43, 113, 212, 12, 9] diff --git a/aptos-move/aptos-debugger/src/aptos_debugger.rs b/aptos-move/aptos-debugger/src/aptos_debugger.rs deleted file mode 100644 index 65c2dc5c35148..0000000000000 --- a/aptos-move/aptos-debugger/src/aptos_debugger.rs +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{format_err, Result}; -use aptos_gas_meter::{StandardGasAlgebra, StandardGasMeter}; -use aptos_gas_profiling::{GasProfiler, TransactionGasLog}; -use aptos_gas_schedule::{MiscGasParameters, NativeGasParameters, LATEST_GAS_FEATURE_VERSION}; -use aptos_memory_usage_tracker::MemoryTrackedGasMeter; -use aptos_resource_viewer::{AnnotatedAccountStateBlob, AptosValueAnnotator}; -use aptos_rest_client::Client; -use aptos_types::{ - account_address::AccountAddress, - chain_id::ChainId, - on_chain_config::{Features, OnChainConfig, TimedFeaturesBuilder}, - state_store::TStateView, - transaction::{ - signature_verified_transaction::SignatureVerifiedTransaction, SignedTransaction, - Transaction, TransactionInfo, TransactionOutput, TransactionPayload, Version, - }, - vm_status::VMStatus, -}; -use aptos_validator_interface::{ - AptosValidatorInterface, DBDebuggerInterface, DebuggerStateView, RestDebuggerInterface, -}; -use aptos_vm::{ - data_cache::AsMoveResolver, - move_vm_ext::{MoveVmExt, SessionExt, SessionId}, - AptosVM, VMExecutor, -}; -use aptos_vm_logging::log_schema::AdapterLogSchema; -use aptos_vm_types::{ - change_set::VMChangeSet, output::VMOutput, storage::change_set_configs::ChangeSetConfigs, -}; -use move_binary_format::errors::VMResult; -use std::{path::Path, sync::Arc}; - -pub struct AptosDebugger { - debugger: Arc, -} - -impl AptosDebugger { - pub fn new(debugger: Arc) -> Self { - Self { debugger } - } - - pub fn rest_client(rest_client: Client) -> Result { - Ok(Self::new(Arc::new(RestDebuggerInterface::new(rest_client)))) - } - - pub fn db + Clone>(db_root_path: P) -> Result { - Ok(Self::new(Arc::new(DBDebuggerInterface::open( - db_root_path, - )?))) - } - - pub fn execute_transactions_at_version( - &self, - version: Version, - txns: Vec, - ) -> Result> { - let sig_verified_txns: Vec = - txns.into_iter().map(|x| x.into()).collect::>(); - let state_view = DebuggerStateView::new(self.debugger.clone(), version); - AptosVM::execute_block_no_limit(&sig_verified_txns, &state_view) - .map_err(|err| format_err!("Unexpected VM Error: {:?}", err)) - } - - pub fn execute_transaction_at_version_with_gas_profiler( - &self, - version: Version, - txn: SignedTransaction, - ) -> Result<(VMStatus, VMOutput, TransactionGasLog)> { - let state_view = DebuggerStateView::new(self.debugger.clone(), version); - let log_context = AdapterLogSchema::new(state_view.id(), 0); - let txn = txn - .check_signature() - .map_err(|err| format_err!("Unexpected VM Error: {:?}", err))?; - - // TODO(Gas): revisit this. - let resolver = state_view.as_move_resolver(); - let vm = AptosVM::new( - &resolver, - /*override_is_delayed_field_optimization_capable=*/ Some(false), - ); - - // Module bundle is deprecated! - if let TransactionPayload::ModuleBundle(_) = txn.payload() { - anyhow::bail!("Module bundle payload has been removed") - } - - let (status, output, gas_profiler) = vm.execute_user_transaction_with_custom_gas_meter( - &resolver, - &txn, - &log_context, - |gas_feature_version, gas_params, storage_gas_params, balance| { - let gas_meter = - MemoryTrackedGasMeter::new(StandardGasMeter::new(StandardGasAlgebra::new( - gas_feature_version, - gas_params, - storage_gas_params, - balance, - ))); - let gas_profiler = match txn.payload() { - TransactionPayload::Script(_) => GasProfiler::new_script(gas_meter), - TransactionPayload::EntryFunction(entry_func) => GasProfiler::new_function( - gas_meter, - entry_func.module().clone(), - entry_func.function().to_owned(), - entry_func.ty_args().to_vec(), - ), - TransactionPayload::Multisig(..) => unimplemented!("not supported yet"), - - // Deprecated. - TransactionPayload::ModuleBundle(..) => { - unreachable!("Module bundle payload has already been checked") - }, - }; - Ok(gas_profiler) - }, - )?; - - Ok((status, output, gas_profiler.finish())) - } - - pub async fn execute_past_transactions( - &self, - mut begin: Version, - mut limit: u64, - ) -> Result> { - let (mut txns, mut txn_infos) = self - .debugger - .get_committed_transactions(begin, limit) - .await?; - - let mut ret = vec![]; - while limit != 0 { - println!( - "Starting epoch execution at {:?}, {:?} transactions remaining", - begin, limit - ); - let mut epoch_result = self - .execute_transactions_by_epoch(begin, txns.clone()) - .await?; - begin += epoch_result.len() as u64; - limit -= epoch_result.len() as u64; - txns = txns.split_off(epoch_result.len()); - let epoch_txn_infos = txn_infos.drain(0..epoch_result.len()).collect::>(); - Self::print_mismatches(&epoch_result, &epoch_txn_infos, begin); - - ret.append(&mut epoch_result); - } - Ok(ret) - } - - fn print_mismatches( - txn_outputs: &[TransactionOutput], - expected_txn_infos: &[TransactionInfo], - first_version: Version, - ) { - for idx in 0..txn_outputs.len() { - let txn_output = &txn_outputs[idx]; - let txn_info = &expected_txn_infos[idx]; - let version = first_version + idx as Version; - txn_output - .ensure_match_transaction_info(version, txn_info, None, None) - .unwrap_or_else(|err| println!("{}", err)) - } - } - - pub async fn execute_transactions_by_epoch( - &self, - begin: Version, - txns: Vec, - ) -> Result> { - let results = self.execute_transactions_at_version(begin, txns)?; - let mut ret = vec![]; - let mut is_reconfig = false; - - for result in results.into_iter() { - if is_reconfig { - continue; - } - if is_reconfiguration(&result) { - is_reconfig = true; - } - ret.push(result) - } - Ok(ret) - } - - pub async fn annotate_account_state_at_version( - &self, - account: AccountAddress, - version: Version, - ) -> Result> { - let state_view = DebuggerStateView::new(self.debugger.clone(), version); - let remote_storage = state_view.as_move_resolver(); - let annotator = AptosValueAnnotator::new(&remote_storage); - Ok( - match self - .debugger - .get_account_state_by_version(account, version) - .await? - { - Some(account_state) => Some(annotator.view_account_state(&account_state)?), - None => None, - }, - ) - } - - pub async fn annotate_key_accounts_at_version( - &self, - version: Version, - ) -> Result> { - let accounts = self.debugger.get_admin_accounts(version).await?; - let state_view = DebuggerStateView::new(self.debugger.clone(), version); - let remote_storage = state_view.as_move_resolver(); - let annotator = AptosValueAnnotator::new(&remote_storage); - - let mut result = vec![]; - for (addr, state) in accounts.into_iter() { - result.push((addr, annotator.view_account_state(&state)?)); - } - Ok(result) - } - - pub async fn get_latest_version(&self) -> Result { - self.debugger.get_latest_version().await - } - - pub async fn get_version_by_account_sequence( - &self, - account: AccountAddress, - seq: u64, - ) -> Result> { - self.debugger - .get_version_by_account_sequence(account, seq) - .await - } - - pub async fn get_committed_transaction_at_version( - &self, - version: Version, - ) -> Result<(Transaction, TransactionInfo)> { - let (mut txns, mut info) = self.debugger.get_committed_transactions(version, 1).await?; - - let txn = txns.pop().expect("there must be exactly 1 txn in the vec"); - let info = info - .pop() - .expect("there must be exactly 1 txn info in the vec"); - - Ok((txn, info)) - } - - pub fn state_view_at_version(&self, version: Version) -> DebuggerStateView { - DebuggerStateView::new(self.debugger.clone(), version) - } - - pub fn run_session_at_version(&self, version: Version, f: F) -> Result - where - F: FnOnce(&mut SessionExt) -> VMResult<()>, - { - let state_view = DebuggerStateView::new(self.debugger.clone(), version); - let state_view_storage = state_view.as_move_resolver(); - let features = Features::fetch_config(&state_view_storage).unwrap_or_default(); - let move_vm = MoveVmExt::new( - NativeGasParameters::zeros(), - MiscGasParameters::zeros(), - LATEST_GAS_FEATURE_VERSION, - ChainId::test().id(), - features, - TimedFeaturesBuilder::enable_all().build(), - &state_view_storage, - /*aggregator_v2_type_tagging*/ false, - ) - .unwrap(); - let mut session = move_vm.new_session(&state_view_storage, SessionId::Void); - f(&mut session).map_err(|err| format_err!("Unexpected VM Error: {:?}", err))?; - let change_set = session - .finish(&ChangeSetConfigs::unlimited_at_gas_feature_version( - LATEST_GAS_FEATURE_VERSION, - )) - .map_err(|err| format_err!("Unexpected VM Error: {:?}", err))?; - Ok(change_set) - } -} - -fn is_reconfiguration(vm_output: &TransactionOutput) -> bool { - let new_epoch_event_key = aptos_types::on_chain_config::new_epoch_event_key(); - vm_output - .events() - .iter() - .any(|event| event.event_key() == Some(&new_epoch_event_key)) -} diff --git a/aptos-move/aptos-debugger/src/bcs_txn_decoder.rs b/aptos-move/aptos-debugger/src/bcs_txn_decoder.rs deleted file mode 100644 index 9a4cc2a76051f..0000000000000 --- a/aptos-move/aptos-debugger/src/bcs_txn_decoder.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::aptos_debugger::AptosDebugger; -use anyhow::Result; -use aptos_rest_client::Client; -use aptos_types::transaction::SignedTransaction; -use aptos_vm::AptosVM; -use clap::Parser; -use regex::Regex; -use std::io; -use url::Url; - -#[derive(Parser)] -pub struct Command { - #[clap(long, default_value_t = false)] - execute: bool, - - #[clap(long, default_value_t = 1)] - concurrency_level: usize, -} - -impl Command { - pub async fn run(self) -> Result<()> { - let mut buffer = String::new(); - io::stdin().read_line(&mut buffer)?; - let re = Regex::new(r"\d+").unwrap(); - let bytes = re - .find_iter(&buffer) - .filter_map(|m| m.as_str().parse::().ok()) - .collect::>(); - - let txn: SignedTransaction = bcs::from_bytes::(&bytes)?; - let chain_id = txn.chain_id(); - println!("==================="); - println!("Transaction Summary"); - println!("==================="); - println!("Sender: {:?}", txn.sender()); - println!("Sequence number: {:?}", txn.sequence_number()); - - let network = if chain_id.is_mainnet() { - "mainnet".to_string() - } else if chain_id.is_testnet() { - "testnet".to_string() - } else { - "devnet".to_string() - }; - println!("Chain ID: {}", chain_id.id()); - println!("Network: {}", network); - - let endpoint = format!("https://{}.aptoslabs.com/v1", network); - let debugger = AptosDebugger::rest_client(Client::new(Url::parse(&endpoint)?))?; - let version = debugger - .get_version_by_account_sequence(txn.sender(), txn.sequence_number()) - .await? - .unwrap(); - println!("Version: {:?}", version); - println!( - "Overview: https://explorer.aptoslabs.com/txn/{:?}/userTxnOverview?network={}", - version, network - ); - println!( - "Payload: https://explorer.aptoslabs.com/txn/{:?}/payload?network={}", - version, network - ); - - if self.execute { - AptosVM::set_concurrency_level_once(self.concurrency_level); - println!(); - println!("==============================="); - println!("Transaction re-execution result"); - println!("==============================="); - println!( - "{:#?}", - debugger.execute_past_transactions(version, 1).await? - ); - } - - Ok(()) - } -} diff --git a/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs b/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs deleted file mode 100644 index cc4c02aee3608..0000000000000 --- a/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{bail, Result}; -use aptos_move_debugger::aptos_debugger::AptosDebugger; -use aptos_rest_client::Client; -use aptos_types::transaction::Transaction; -use aptos_vm::AptosVM; -use clap::{Parser, Subcommand}; -use std::path::{Path, PathBuf}; -use url::Url; - -#[derive(Subcommand)] -pub enum Target { - /// Use full node's rest api as query endpoint. - Rest { endpoint: String }, - /// Use a local db instance to serve as query endpoint. - DB { path: PathBuf }, -} - -#[derive(Parser)] -pub struct Args { - #[clap(subcommand)] - target: Target, - - #[clap(long)] - version: u64, -} - -#[tokio::main] -async fn main() -> Result<()> { - // Parse the commandline args - let args = Args::parse(); - let version = args.version; - - // Initialize the debugger - aptos_logger::Logger::new().init(); - AptosVM::set_concurrency_level_once(1); - - let debugger = match args.target { - Target::Rest { endpoint } => { - AptosDebugger::rest_client(Client::new(Url::parse(&endpoint)?))? - }, - Target::DB { path } => AptosDebugger::db(path)?, - }; - - // Execute the transaction w/ the gas profiler - let (txn, _txn_info) = debugger - .get_committed_transaction_at_version(version) - .await?; - - let txn = match txn { - Transaction::UserTransaction(txn) => txn, - _ => bail!("not a user transaction"), - }; - - let (_status, output, gas_log) = - debugger.execute_transaction_at_version_with_gas_profiler(version, txn)?; - - let txn_output = - output.try_materialize_into_transaction_output(&debugger.state_view_at_version(version))?; - - // Show results to the user - println!("{:#?}", txn_output); - - let report_path = Path::new("gas-profiling").join(format!("txn-{}", version)); - gas_log.generate_html_report( - &report_path, - format!("Gas Report - Transaction {}", version), - )?; - - println!("Gas profiling report saved to {}.", report_path.display()); - - Ok(()) -} diff --git a/aptos-move/aptos-debugger/src/common.rs b/aptos-move/aptos-debugger/src/common.rs deleted file mode 100644 index 6b250b8108b42..0000000000000 --- a/aptos-move/aptos-debugger/src/common.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{execute_past_transactions, execute_pending_block}; -use anyhow::Result; -use clap::Parser; -use std::path::PathBuf; - -#[derive(Parser)] -#[clap(group(clap::ArgGroup::new("target") - .required(true) - .multiple(false) - .args(&["rest_endpoint", "db_path"]), -))] -pub struct Target { - /// Use full node's rest api as query endpoint. - #[clap(long, group = "target")] - pub(crate) rest_endpoint: Option, - - /// Use a local db instance to serve as query endpoint. - #[clap(long, group = "target")] - pub(crate) db_path: Option, -} - -#[derive(Parser)] -pub struct Opts { - #[clap(flatten)] - pub(crate) target: Target, - - #[clap(long, default_value_t = 1)] - pub(crate) concurrency_level: usize, -} - -#[derive(Parser)] -pub enum Command { - ExecutePastTransactions(execute_past_transactions::Command), - ExecutePendingBlock(execute_pending_block::Command), -} - -impl Command { - pub async fn run(self) -> Result<()> { - match self { - Command::ExecutePastTransactions(cmd) => cmd.run().await, - Command::ExecutePendingBlock(cmd) => cmd.run().await, - } - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Command::command().debug_assert() -} diff --git a/aptos-move/aptos-debugger/src/execute_past_transactions.rs b/aptos-move/aptos-debugger/src/execute_past_transactions.rs deleted file mode 100644 index 7905e46f8a593..0000000000000 --- a/aptos-move/aptos-debugger/src/execute_past_transactions.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{aptos_debugger::AptosDebugger, common::Opts}; -use anyhow::Result; -use aptos_rest_client::Client; -use aptos_vm::AptosVM; -use clap::Parser; -use url::Url; - -#[derive(Parser)] -pub struct Command { - #[clap(flatten)] - opts: Opts, - - #[clap(long)] - begin_version: u64, - - #[clap(long)] - limit: u64, - - #[clap(long)] - skip_result: bool, -} - -impl Command { - pub async fn run(self) -> Result<()> { - AptosVM::set_concurrency_level_once(self.opts.concurrency_level); - - let debugger = if let Some(rest_endpoint) = self.opts.target.rest_endpoint { - AptosDebugger::rest_client(Client::new(Url::parse(&rest_endpoint)?))? - } else if let Some(db_path) = self.opts.target.db_path { - AptosDebugger::db(db_path)? - } else { - unreachable!("Must provide one target."); - }; - - let result = debugger - .execute_past_transactions(self.begin_version, self.limit) - .await?; - - if !self.skip_result { - println!("{result:#?}",); - } - - Ok(()) - } -} diff --git a/aptos-move/aptos-debugger/src/execute_pending_block.rs b/aptos-move/aptos-debugger/src/execute_pending_block.rs deleted file mode 100644 index dce2487c2269e..0000000000000 --- a/aptos-move/aptos-debugger/src/execute_pending_block.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{aptos_debugger::AptosDebugger, common::Opts}; -use anyhow::Result; -use aptos_crypto::HashValue; -use aptos_logger::info; -use aptos_rest_client::Client; -use aptos_vm::AptosVM; -use clap::Parser; -use std::path::PathBuf; -use url::Url; - -#[derive(Parser)] -#[clap(group(clap::ArgGroup::new("block_target") - .required(true) - .multiple(false) - .args(&["block_rest_endpoint", "consensus_db_path"]), -))] -pub struct Command { - #[clap(flatten)] - opts: Opts, - - #[clap(long, group = "block_target")] - block_rest_endpoint: Option, - - #[clap(long, group = "block_target")] - consensus_db_path: Option, - - #[clap(long)] - begin_version: u64, - - #[clap(long)] - block_id: Option, - - #[clap(long)] - add_system_txns: bool, -} - -impl Command { - pub async fn run(self) -> Result<()> { - AptosVM::set_concurrency_level_once(self.opts.concurrency_level); - - let debugger = if let Some(rest_endpoint) = self.opts.target.rest_endpoint { - AptosDebugger::rest_client(Client::new(Url::parse(&rest_endpoint)?))? - } else if let Some(db_path) = self.opts.target.db_path { - AptosDebugger::db(db_path)? - } else { - unreachable!("Must provide one target."); - }; - - let user_txns = if let Some(block_rest_endpoint) = self.block_rest_endpoint { - info!( - "Getting block {:?} from {block_rest_endpoint:?}.", - self.block_id - ); - let base_url = - Url::parse(&block_rest_endpoint)?.join("/debug/consensus/block?bcs=true")?; - let url = if let Some(block_id) = self.block_id { - base_url.join(&format!("&block_id={block_id:?}"))? - } else { - base_url - }; - info!("GET {url:?}..."); - let body = reqwest::get(url).await?.bytes().await?; - bcs::from_bytes(&body)? - } else if let Some(consensus_db_path) = self.consensus_db_path { - info!( - "Getting block {:?} from {consensus_db_path:?}.", - self.block_id - ); - let cmd = aptos_consensus::util::db_tool::Command { - db_dir: consensus_db_path, - block_id: self.block_id, - }; - cmd.dump_pending_txns()? - } else { - unreachable!("Must provide one target."); - }; - - let block = if self.add_system_txns { - todo!("Add block metadata txn and state checkpoint txn if necessary."); - } else { - user_txns - }; - - let txn_outputs = debugger.execute_transactions_at_version(self.begin_version, block)?; - println!("{txn_outputs:#?}"); - - Ok(()) - } -} diff --git a/aptos-move/aptos-debugger/src/lib.rs b/aptos-move/aptos-debugger/src/lib.rs deleted file mode 100644 index 1079cd1ab8e38..0000000000000 --- a/aptos-move/aptos-debugger/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod aptos_debugger; -pub mod bcs_txn_decoder; -pub mod common; -pub mod execute_past_transactions; -pub mod execute_pending_block; diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml deleted file mode 100644 index 3b3c689e51a50..0000000000000 --- a/consensus/Cargo.toml +++ /dev/null @@ -1,112 +0,0 @@ -[package] -name = "aptos-consensus" -description = "Aptos consensus" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-bitvec = { workspace = true } -aptos-bounded-executor = { workspace = true } -aptos-channels = { workspace = true } -aptos-collections = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus-notifications = { workspace = true } -aptos-consensus-types = { workspace = true } -aptos-crypto = { workspace = true } -aptos-crypto-derive = { workspace = true } -aptos-dkg = { workspace = true } -aptos-enum-conversion-derive = { workspace = true } -aptos-event-notifications = { workspace = true } -aptos-executor = { workspace = true } -aptos-executor-types = { workspace = true } -aptos-experimental-runtimes = { workspace = true } -aptos-fallible = { workspace = true } -aptos-global-constants = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-mempool = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-network = { workspace = true } -aptos-reliable-broadcast = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-safety-rules = { workspace = true } -aptos-schemadb = { workspace = true } -aptos-secure-storage = { workspace = true } -aptos-short-hex-str = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-temppath = { workspace = true } -aptos-time-service = { workspace = true } -aptos-types = { workspace = true } -aptos-validator-transaction-pool = { workspace = true } -aptos-vm = { workspace = true } -arc-swap = { workspace = true } -async-trait = { workspace = true } -bcs = { workspace = true } -byteorder = { workspace = true } -bytes = { workspace = true } -chrono = { workspace = true } -claims = { workspace = true } -clap = { workspace = true } -dashmap = { workspace = true } -enum_dispatch = { workspace = true } -fail = { workspace = true } -futures = { workspace = true } -futures-channel = { workspace = true } -hex = { workspace = true } -itertools = { workspace = true } -maplit = { workspace = true } -mirai-annotations = { workspace = true } -move-core-types = { workspace = true } -num-derive = { workspace = true } -num-traits = { workspace = true } -once_cell = { workspace = true } -rand = { workspace = true } -rayon = { workspace = true } -scopeguard = { workspace = true } -serde = { workspace = true } -serde_bytes = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -sha3 = { workspace = true } -strum_macros = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } -tokio-metrics = { workspace = true } -tokio-retry = { workspace = true } - -[dev-dependencies] -aptos-cached-packages = { workspace = true } -aptos-config = { workspace = true, features = ["fuzzing"] } -aptos-consensus-types = { workspace = true, features = ["fuzzing"] } -aptos-executor-test-helpers = { workspace = true } -aptos-keygen = { workspace = true } -aptos-mempool = { workspace = true, features = ["fuzzing"] } -aptos-network = { workspace = true, features = ["fuzzing"] } -aptos-safety-rules = { workspace = true, features = ["testing"] } -aptos-vm = { workspace = true, features = ["fuzzing"] } -aptos-vm-validator = { workspace = true } -claims = { workspace = true } -move-core-types = { workspace = true } -proptest = { workspace = true } -tempfile = { workspace = true } - -[features] -default = [] -fuzzing = [ - "aptos-consensus-types/fuzzing", - "aptos-config/fuzzing", - "aptos-crypto/fuzzing", - "aptos-mempool/fuzzing", - "aptos-types/fuzzing", - "aptos-safety-rules/testing", -] -failpoints = ["fail/failpoints"] diff --git a/consensus/README.md b/consensus/README.md deleted file mode 100644 index 8d2b91569ede0..0000000000000 --- a/consensus/README.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -id: consensus -title: Consensus -custom_edit_url: https://github.com/aptos-labs/aptos-core/edit/main/consensus/README.md ---- - - -The consensus component supports state machine replication using the AptosBFT consensus protocol. - -## Overview - -A consensus protocol allows a set of validators to create the logical appearance of a single database. The consensus protocol replicates submitted transactions among the validators, executes potential transactions against the current database, and then agrees on a binding commitment to the ordering of transactions and resulting execution. As a result, all validators can maintain an identical database for a given version number following the [state machine replication paradigm](https://dl.acm.org/citation.cfm?id=98167). The Aptos protocol uses a variant of the [Jolteon consensus protocol](https://arxiv.org/pdf/2106.10362.pdf), a recent Byzantine fault-tolerant ([BFT](https://en.wikipedia.org/wiki/Byzantine_fault)) consensus protocol, called AptosBFT. It provides safety (all honest validators agree on commits and execution) and liveness (commits are continually produced) in the partial synchrony model defined in the paper "Consensus in the Presence of Partial Synchrony" by Dwork, Lynch, and Stockmeyer ([DLS](https://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf)) and mentioned in the paper ["Practical Byzantine Fault Tolerance" (PBFT)](http://pmg.csail.mit.edu/papers/osdi99.pdf) by Castro and Liskov, as well as newer protocols such as [Tendermint](https://arxiv.org/abs/1807.04938). In this document, we present a high-level description of the AptosBFT protocol and discuss how the code is organized. For details on the specifications and proofs of AptosBFT, read the full [technical report](../developer-docs-site/static/papers/aptos-consensus-state-machine-replication-in-the-aptos-blockchain/). - -Agreement on the database state must be reached between validators, even if -there are Byzantine faults. The Byzantine failures model allows some validators -to arbitrarily deviate from the protocol without constraint, with the exception -of being computationally bound (and thus not able to break cryptographic assumptions). Byzantine faults are worst-case errors where validators collude and behave maliciously to try to sabotage system behavior. A consensus protocol that tolerates Byzantine faults caused by malicious or hacked validators can also mitigate arbitrary hardware and software failures. - -AptosBFT assumes that a set of 3f + 1 votes is distributed among a set of validators that may be honest or Byzantine. AptosBFT remains safe, preventing attacks such as double spends and forks when at most f votes are controlled by Byzantine validators — also implying that at least 2f+1 votes are honest. AptosBFT remains live, committing transactions from clients, as long as there exists a global stabilization time (GST), after which all messages between honest validators are delivered to other honest validators within a maximal network delay $\Delta$ (this is the partial synchrony model introduced in [DLS](https://groups.csail.mit.edu/tds/papers/Lynch/jacm88.pdf)). In addition to traditional guarantees, AptosBFT maintains safety when validators crash and restart — even if all validators restart at the same time. - -### AptosBFT Overview - -In AptosBFT, validators receive transactions from clients and share them with each other through a shared mempool protocol. The AptosBFT protocol then proceeds in a sequence of rounds. In each round, a validator takes the role of leader and proposes a block of transactions to extend a certified sequence of blocks (see quorum certificates below) that contain the full previous transaction history. A validator receives the proposed block and checks their voting rules to determine if it should vote for certifying this block. These simple rules ensure the safety of AptosBFT — and their implementation can be cleanly separated and audited. If the validator intends to vote for this block, it executes the block’s transactions speculatively and without external effect. This results in the computation of an authenticator for the database that results from the execution of the block. The validator then sends a signed vote for the block and the database authenticator to the leader. The leader gathers these votes to form a quorum certificate that provides evidence of $\ge$ 2f + 1 votes for this block and broadcasts the quorum certificate to all validators. - -A block is committed when a contiguous 3-chain commit rule is met. A block at round k is committed if it has a quorum certificate and is confirmed by two more blocks and quorum certificates at rounds k + 1 and k + 2. The commit rule eventually allows honest validators to commit a block. AptosBFT guarantees that all honest validators will eventually commit the block (and proceeding sequence of blocks linked from it). Once a sequence of blocks has committed, the state resulting from executing their transactions can be persisted and forms a replicated database. - -### Advantages of Jolteon - -We evaluated several BFT-based protocols against the dimensions of performance, reliability, security, ease of robust implementation, and operational overhead for validators. Our goal was to choose a protocol that would initially support at least 100 validators and would be able to evolve over time to support 500–1,000 validators. The initial AptosBFT protocol was based on HotStuff for the following reasons: (i) simplicity and modularity; (ii) ability to easily integrate consensus with execution; and (iii) promising performance in early experiments. Later we switched to Jolteon as it reduces latecny by 33% without sacrificing throughput. - -The AptosBFT protocol decomposes into modules for safety (voting and commit rules) and liveness (round_state). This decoupling provides the ability to develop and experiment independently and on different modules in parallel. Due to the simple voting and commit rules, protocol safety is easy to implement and verify. It is straightforward to integrate execution as a part of consensus to avoid forking issues that arise from non-deterministic execution in a leader-based protocol. We did not consider proof-of-work based protocols, such as [Bitcoin](https://bitcoin.org/bitcoin.pdf), due to their poor performance and high energy (and environmental) costs. - -### Extensions and Modifications - -We reformulate the safety conditions and provide extended proofs of safety, liveness, and optimistic responsiveness. We also implement a number of additional features. First, we make the protocol more resistant to non-determinism bugs, by having validators collectively sign the resulting state of a block rather than just the sequence of transactions. This also allows clients to use quorum certificates to authenticate reads from the database. Second, we design a round_state that emits explicit timeouts, and validators rely on a quorum of those to move to the next round — without requiring synchronized clocks. Third, we intend to design an unpredictable leader election mechanism in which the leader of a round is determined by the proposer of the latest committed block using a verifiable random function [VRF](https://people.csail.mit.edu/silvio/Selected%20Scientific%20Papers/Pseudo%20Randomness/Verifiable_Random_Functions.pdf). This mechanism limits the window of time in which an adversary can launch an effective denial-of-service attack against a leader. Fourth, we use aggregate signatures that preserve the identity of validators who sign quorum certificates. This allows us to provide incentives to validators that contribute to quorum certificates. Aggregate signatures also do not require a complex [threshold key setup](https://www.cypherpunks.ca/~iang/pubs/DKG.pdf). - -## Implementation Details - -The consensus component is mostly implemented in the [Actor](https://en.wikipedia.org/wiki/Actor_model) programming model — i.e., it uses message-passing to communicate between different subcomponents with the [tokio](https://tokio.rs/) framework used as the task runtime. The primary exception to the actor model (as it is accessed in parallel by several subcomponents) is the consensus data structure *BlockStore* which manages the blocks, execution, quorum certificates, and other shared data structures. The major subcomponents in the consensus component are: - -* **PayloadClient** is the interface to the mempool component and supports the pulling of transactions as well as removing committed transactions. A proposer uses on-demand pull transactions from mempool to form a proposal block. -* **StateComputer** is the interface for accessing the execution component. It can execute blocks, commit blocks, and can synchronize state. -* **BlockStore** maintains the tree of proposal blocks, block execution, votes, quorum certificates, and persistent storage. It is responsible for maintaining the consistency of the combination of these data structures and can be concurrently accessed by other subcomponents. -* **RoundManager** is responsible for processing the individual events (e.g., process_new_round, process_proposal, process_vote). It exposes the async processing functions for each event type and drives the protocol. -* **RoundState** is responsible for the liveness of the consensus protocol. It changes rounds due to timeout certificates or quorum certificates and proposes blocks when it is the proposer for the current round. -* **SafetyRules** is responsible for the safety of the consensus protocol. It processes quorum certificates and LedgerInfo to learn about new commits and guarantees that the two voting rules are followed — even in the case of restart (since all safety data is persisted to local storage). - -All consensus messages are signed by their creators and verified by their receivers. Message verification occurs closest to the network layer to avoid invalid or unnecessary data from entering the consensus protocol. - -## How is this module organized? - - consensus - ├── src - │   ├── block_storage # In-memory storage of blocks and related data structures - │   ├── consensusdb # Database interaction to persist consensus data for safety and liveness - │   ├── liveness # RoundState, proposer, and other liveness related code - │   └── test_utils # Mock implementations that are used for testing only - ├── consensus-types # Consensus data types (i.e. quorum certificates) - └── safety-rules # Safety (voting) rules diff --git a/consensus/src/block_preparer.rs b/consensus/src/block_preparer.rs deleted file mode 100644 index ca92f1b835e15..0000000000000 --- a/consensus/src/block_preparer.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters::{MAX_TXNS_FROM_BLOCK_TO_EXECUTE, TXN_SHUFFLE_SECONDS}, - payload_manager::PayloadManager, - transaction_deduper::TransactionDeduper, - transaction_filter::TransactionFilter, - transaction_shuffler::TransactionShuffler, -}; -use aptos_consensus_types::block::Block; -use aptos_executor_types::ExecutorResult; -use aptos_types::transaction::SignedTransaction; -use std::sync::Arc; - -pub struct BlockPreparer { - payload_manager: Arc, - txn_filter: Arc, - txn_deduper: Arc, - txn_shuffler: Arc, -} - -impl BlockPreparer { - pub fn new( - payload_manager: Arc, - txn_filter: Arc, - txn_deduper: Arc, - txn_shuffler: Arc, - ) -> Self { - Self { - payload_manager, - txn_filter, - txn_deduper, - txn_shuffler, - } - } - - pub async fn prepare_block(&self, block: &Block) -> ExecutorResult> { - let (txns, max_txns_from_block_to_execute) = - self.payload_manager.get_transactions(block).await?; - let txn_filter = self.txn_filter.clone(); - let txn_deduper = self.txn_deduper.clone(); - let txn_shuffler = self.txn_shuffler.clone(); - let block_id = block.id(); - let block_timestamp_usecs = block.timestamp_usecs(); - // Transaction filtering, deduplication and shuffling are CPU intensive tasks, so we run them in a blocking task. - tokio::task::spawn_blocking(move || { - let filtered_txns = txn_filter.filter(block_id, block_timestamp_usecs, txns); - let deduped_txns = txn_deduper.dedup(filtered_txns); - let mut shuffled_txns = { - let _timer = TXN_SHUFFLE_SECONDS.start_timer(); - - txn_shuffler.shuffle(deduped_txns) - }; - - if let Some(max_txns_from_block_to_execute) = max_txns_from_block_to_execute { - shuffled_txns.truncate(max_txns_from_block_to_execute); - } - MAX_TXNS_FROM_BLOCK_TO_EXECUTE.observe(shuffled_txns.len() as f64); - Ok(shuffled_txns) - }) - .await - .expect("Failed to spawn blocking task for transaction generation") - } -} diff --git a/consensus/src/block_storage/block_store.rs b/consensus/src/block_storage/block_store.rs deleted file mode 100644 index 84611d65a46cb..0000000000000 --- a/consensus/src/block_storage/block_store.rs +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{ - block_tree::BlockTree, - tracing::{observe_block, BlockStage}, - BlockReader, - }, - counters, - payload_manager::PayloadManager, - persistent_liveness_storage::{ - PersistentLivenessStorage, RecoveryData, RootInfo, RootMetadata, - }, - pipeline::execution_client::TExecutionClient, - util::time_service::TimeService, -}; -use anyhow::{bail, ensure, format_err, Context}; -use aptos_consensus_types::{ - block::Block, common::Round, pipelined_block::PipelinedBlock, quorum_cert::QuorumCert, - sync_info::SyncInfo, timeout_2chain::TwoChainTimeoutCertificate, -}; -use aptos_crypto::{hash::ACCUMULATOR_PLACEHOLDER_HASH, HashValue}; -use aptos_executor_types::StateComputeResult; -use aptos_infallible::RwLock; -use aptos_logger::prelude::*; -use aptos_types::ledger_info::LedgerInfoWithSignatures; -use futures::executor::block_on; -#[cfg(test)] -use std::collections::VecDeque; -#[cfg(any(test, feature = "fuzzing"))] -use std::sync::atomic::{AtomicBool, Ordering}; -use std::{sync::Arc, time::Duration}; - -#[cfg(test)] -#[path = "block_store_test.rs"] -mod block_store_test; - -#[path = "sync_manager.rs"] -pub mod sync_manager; - -fn update_counters_for_ordered_blocks(ordered_blocks: &[Arc]) { - for block in ordered_blocks { - observe_block(block.block().timestamp_usecs(), BlockStage::ORDERED); - } -} - -/// Responsible for maintaining all the blocks of payload and the dependencies of those blocks -/// (parent and previous QC links). It is expected to be accessed concurrently by multiple threads -/// and is thread-safe. -/// -/// Example tree block structure based on parent links. -/// ╭--> A3 -/// Genesis--> B0--> B1--> B2--> B3 -/// ╰--> C1--> C2 -/// ╰--> D3 -/// -/// Example corresponding tree block structure for the QC links (must follow QC constraints). -/// ╭--> A3 -/// Genesis--> B0--> B1--> B2--> B3 -/// ├--> C1 -/// ├--------> C2 -/// ╰--------------> D3 -pub struct BlockStore { - inner: Arc>, - execution_client: Arc, - /// The persistent storage backing up the in-memory data structure, every write should go - /// through this before in-memory tree. - storage: Arc, - /// Used to ensure that any block stored will have a timestamp < the local time - time_service: Arc, - // consistent with round type - vote_back_pressure_limit: Round, - payload_manager: Arc, - #[cfg(any(test, feature = "fuzzing"))] - back_pressure_for_test: AtomicBool, -} - -impl BlockStore { - pub fn new( - storage: Arc, - initial_data: RecoveryData, - execution_client: Arc, - max_pruned_blocks_in_mem: usize, - time_service: Arc, - vote_back_pressure_limit: Round, - payload_manager: Arc, - ) -> Self { - let highest_2chain_tc = initial_data.highest_2chain_timeout_certificate(); - let (root, root_metadata, blocks, quorum_certs) = initial_data.take(); - let block_store = block_on(Self::build( - root, - root_metadata, - blocks, - quorum_certs, - highest_2chain_tc, - execution_client, - storage, - max_pruned_blocks_in_mem, - time_service, - vote_back_pressure_limit, - payload_manager, - )); - block_on(block_store.try_send_for_execution()); - block_store - } - - async fn try_send_for_execution(&self) { - // reproduce the same batches (important for the commit phase) - - let mut certs = self.inner.read().get_all_quorum_certs_with_commit_info(); - certs.sort_unstable_by_key(|qc| qc.commit_info().round()); - - for qc in certs { - if qc.commit_info().round() > self.commit_root().round() { - info!( - "trying to commit to round {} with ledger info {}", - qc.commit_info().round(), - qc.ledger_info() - ); - - if let Err(e) = self.send_for_execution(qc.clone()).await { - error!("Error in try-committing blocks. {}", e.to_string()); - } - } - } - } - - async fn build( - root: RootInfo, - root_metadata: RootMetadata, - blocks: Vec, - quorum_certs: Vec, - highest_2chain_timeout_cert: Option, - execution_client: Arc, - storage: Arc, - max_pruned_blocks_in_mem: usize, - time_service: Arc, - vote_back_pressure_limit: Round, - payload_manager: Arc, - ) -> Self { - let RootInfo(root_block, root_qc, root_ordered_cert, root_commit_cert) = root; - - //verify root is correct - assert!( - // decoupled execution allows dummy versions - root_qc.certified_block().version() == 0 - || root_qc.certified_block().version() == root_metadata.version(), - "root qc version {} doesn't match committed trees {}", - root_qc.certified_block().version(), - root_metadata.version(), - ); - assert!( - // decoupled execution allows dummy executed_state_id - root_qc.certified_block().executed_state_id() == *ACCUMULATOR_PLACEHOLDER_HASH - || root_qc.certified_block().executed_state_id() == root_metadata.accu_hash, - "root qc state id {} doesn't match committed trees {}", - root_qc.certified_block().executed_state_id(), - root_metadata.accu_hash, - ); - - let result = StateComputeResult::new( - root_metadata.accu_hash, - root_metadata.frozen_root_hashes, - root_metadata.num_leaves, /* num_leaves */ - vec![], /* parent_root_hashes */ - 0, /* parent_num_leaves */ - None, /* epoch_state */ - vec![], /* compute_status */ - vec![], /* txn_infos */ - vec![], /* reconfig_events */ - ); - - let pipelined_root_block = PipelinedBlock::new( - *root_block, - vec![], - // Create a dummy state_compute_result with necessary fields filled in. - result, - ); - - let tree = BlockTree::new( - pipelined_root_block, - root_qc, - root_ordered_cert, - root_commit_cert, - max_pruned_blocks_in_mem, - highest_2chain_timeout_cert.map(Arc::new), - ); - - let block_store = Self { - inner: Arc::new(RwLock::new(tree)), - execution_client, - storage, - time_service, - vote_back_pressure_limit, - payload_manager, - #[cfg(any(test, feature = "fuzzing"))] - back_pressure_for_test: AtomicBool::new(false), - }; - - for block in blocks { - block_store - .insert_ordered_block(block) - .await - .unwrap_or_else(|e| { - panic!("[BlockStore] failed to insert block during build {:?}", e) - }); - } - for qc in quorum_certs { - block_store - .insert_single_quorum_cert(qc) - .unwrap_or_else(|e| { - panic!("[BlockStore] failed to insert quorum during build{:?}", e) - }); - } - - counters::LAST_COMMITTED_ROUND.set(block_store.ordered_root().round() as i64); - block_store - } - - /// Send an ordered block id with the proof for execution, returns () on success or error - pub async fn send_for_execution(&self, finality_proof: QuorumCert) -> anyhow::Result<()> { - let block_id_to_commit = finality_proof.commit_info().id(); - let block_to_commit = self - .get_block(block_id_to_commit) - .ok_or_else(|| format_err!("Committed block id not found"))?; - - // First make sure that this commit is new. - ensure!( - block_to_commit.round() > self.ordered_root().round(), - "Committed block round lower than root" - ); - - let blocks_to_commit = self - .path_from_ordered_root(block_id_to_commit) - .unwrap_or_default(); - - assert!(!blocks_to_commit.is_empty()); - - let block_tree = self.inner.clone(); - let storage = self.storage.clone(); - - // This callback is invoked synchronously with and could be used for multiple batches of blocks. - self.execution_client - .finalize_order( - &blocks_to_commit, - finality_proof.ledger_info().clone(), - Box::new( - move |committed_blocks: &[Arc], - commit_decision: LedgerInfoWithSignatures| { - block_tree.write().commit_callback( - storage, - committed_blocks, - finality_proof, - commit_decision, - ); - }, - ), - ) - .await - .expect("Failed to persist commit"); - - self.inner.write().update_ordered_root(block_to_commit.id()); - update_counters_for_ordered_blocks(&blocks_to_commit); - - Ok(()) - } - - pub async fn rebuild( - &self, - root: RootInfo, - root_metadata: RootMetadata, - blocks: Vec, - quorum_certs: Vec, - ) { - let max_pruned_blocks_in_mem = self.inner.read().max_pruned_blocks_in_mem(); - // Rollover the previous highest TC from the old tree to the new one. - let prev_2chain_htc = self - .highest_2chain_timeout_cert() - .map(|tc| tc.as_ref().clone()); - let BlockStore { inner, .. } = Self::build( - root, - root_metadata, - blocks, - quorum_certs, - prev_2chain_htc, - self.execution_client.clone(), - Arc::clone(&self.storage), - max_pruned_blocks_in_mem, - Arc::clone(&self.time_service), - self.vote_back_pressure_limit, - self.payload_manager.clone(), - ) - .await; - - // Unwrap the new tree and replace the existing tree. - *self.inner.write() = Arc::try_unwrap(inner) - .unwrap_or_else(|_| panic!("New block tree is not shared")) - .into_inner(); - self.try_send_for_execution().await; - } - - /// Insert a block if it passes all validation tests. - /// Returns the Arc to the block kept in the block store after persisting it to storage - /// - /// This function assumes that the ancestors are present (returns MissingParent otherwise). - /// - /// Duplicate inserts will return the previously inserted block ( - /// note that it is considered a valid non-error case, for example, it can happen if a validator - /// receives a certificate for a block that is currently being added). - pub async fn insert_ordered_block(&self, block: Block) -> anyhow::Result> { - if let Some(existing_block) = self.get_block(block.id()) { - return Ok(existing_block); - } - ensure!( - self.inner.read().ordered_root().round() < block.round(), - "Block with old round" - ); - - let pipelined_block = PipelinedBlock::new_ordered(block.clone()); - // ensure local time past the block time - let block_time = Duration::from_micros(pipelined_block.timestamp_usecs()); - let current_timestamp = self.time_service.get_current_timestamp(); - if let Some(t) = block_time.checked_sub(current_timestamp) { - if t > Duration::from_secs(1) { - warn!( - "Long wait time {}ms for block {}", - t.as_millis(), - pipelined_block.block() - ); - } - self.time_service.wait_until(block_time).await; - } - if let Some(payload) = pipelined_block.block().payload() { - self.payload_manager - .prefetch_payload_data(payload, pipelined_block.block().timestamp_usecs()); - } - self.storage - .save_tree(vec![pipelined_block.block().clone()], vec![]) - .context("Insert block failed when saving block")?; - self.inner.write().insert_block(pipelined_block) - } - - /// Validates quorum certificates and inserts it into block tree assuming dependencies exist. - pub fn insert_single_quorum_cert(&self, qc: QuorumCert) -> anyhow::Result<()> { - // If the parent block is not the root block (i.e not None), ensure the executed state - // of a block is consistent with its QuorumCert, otherwise persist the QuorumCert's - // state and on restart, a new execution will agree with it. A new execution will match - // the QuorumCert's state on the next restart will work if there is a memory - // corruption, for example. - match self.get_block(qc.certified_block().id()) { - Some(pipelined_block) => { - ensure!( - // decoupled execution allows dummy block infos - pipelined_block - .block_info() - .match_ordered_only(qc.certified_block()), - "QC for block {} has different {:?} than local {:?}", - qc.certified_block().id(), - qc.certified_block(), - pipelined_block.block_info() - ); - observe_block( - pipelined_block.block().timestamp_usecs(), - BlockStage::QC_ADDED, - ); - }, - None => bail!("Insert {} without having the block in store first", qc), - }; - - self.storage - .save_tree(vec![], vec![qc.clone()]) - .context("Insert block failed when saving quorum")?; - self.inner.write().insert_quorum_cert(qc) - } - - /// Replace the highest 2chain timeout certificate in case the given one has a higher round. - /// In case a timeout certificate is updated, persist it to storage. - pub fn insert_2chain_timeout_certificate( - &self, - tc: Arc, - ) -> anyhow::Result<()> { - let cur_tc_round = self - .highest_2chain_timeout_cert() - .map_or(0, |tc| tc.round()); - if tc.round() <= cur_tc_round { - return Ok(()); - } - self.storage - .save_highest_2chain_timeout_cert(tc.as_ref()) - .context("Timeout certificate insert failed when persisting to DB")?; - self.inner.write().replace_2chain_timeout_cert(tc); - Ok(()) - } - - /// Prune the tree up to next_root_id (keep next_root_id's block). Any branches not part of - /// the next_root_id's tree should be removed as well. - /// - /// For example, root = B0 - /// B0--> B1--> B2 - /// ╰--> B3--> B4 - /// - /// prune_tree(B3) should be left with - /// B3--> B4, root = B3 - /// - /// Returns the block ids of the blocks removed. - #[cfg(test)] - fn prune_tree(&self, next_root_id: HashValue) -> VecDeque { - let id_to_remove = self.inner.read().find_blocks_to_prune(next_root_id); - if let Err(e) = self - .storage - .prune_tree(id_to_remove.clone().into_iter().collect()) - { - // it's fine to fail here, as long as the commit succeeds, the next restart will clean - // up dangling blocks, and we need to prune the tree to keep the root consistent with - // executor. - warn!(error = ?e, "fail to delete block"); - } - - // synchronously update both root_id and commit_root_id - let mut wlock = self.inner.write(); - wlock.update_ordered_root(next_root_id); - wlock.update_commit_root(next_root_id); - wlock.process_pruned_blocks(id_to_remove.clone()); - id_to_remove - } - - #[cfg(any(test, feature = "fuzzing"))] - pub fn set_back_pressure_for_test(&self, back_pressure: bool) { - self.back_pressure_for_test - .store(back_pressure, Ordering::Relaxed) - } - - /// Return if the consensus is backpressured - fn vote_back_pressure(&self) -> bool { - #[cfg(any(test, feature = "fuzzing"))] - { - if self.back_pressure_for_test.load(Ordering::Relaxed) { - return true; - } - } - let commit_round = self.commit_root().round(); - let ordered_round = self.ordered_root().round(); - counters::OP_COUNTERS - .gauge("back_pressure") - .set((ordered_round - commit_round) as i64); - ordered_round > self.vote_back_pressure_limit + commit_round - } - - pub fn pipeline_pending_latency(&self, proposal_timestamp: Duration) -> Duration { - let ordered_root = self.ordered_root(); - let commit_root = self.commit_root(); - let pending_path = self - .path_from_commit_root(self.ordered_root().id()) - .unwrap_or_default(); - let pending_rounds = pending_path.len(); - let oldest_not_committed = pending_path.into_iter().min_by_key(|b| b.round()); - - let oldest_not_committed_spent_in_pipeline = oldest_not_committed - .as_ref() - .and_then(|b| b.elapsed_in_pipeline()) - .unwrap_or(Duration::ZERO); - - let ordered_round = ordered_root.round(); - let oldest_not_committed_round = oldest_not_committed.as_ref().map_or(0, |b| b.round()); - let commit_round = commit_root.round(); - let ordered_timestamp = Duration::from_micros(ordered_root.timestamp_usecs()); - let oldest_not_committed_timestamp = oldest_not_committed - .as_ref() - .map(|b| Duration::from_micros(b.timestamp_usecs())) - .unwrap_or(Duration::ZERO); - let committed_timestamp = Duration::from_micros(commit_root.timestamp_usecs()); - let commit_cert_timestamp = - Duration::from_micros(self.highest_commit_cert().commit_info().timestamp_usecs()); - - fn latency_from_proposal(proposal_timestamp: Duration, timestamp: Duration) -> Duration { - if timestamp.is_zero() { - // latency not known without non-genesis blocks - Duration::ZERO - } else { - proposal_timestamp.checked_sub(timestamp).unwrap() - } - } - - let latency_to_committed = latency_from_proposal(proposal_timestamp, committed_timestamp); - let latency_to_oldest_not_committed = - latency_from_proposal(proposal_timestamp, oldest_not_committed_timestamp); - let latency_to_ordered = latency_from_proposal(proposal_timestamp, ordered_timestamp); - - info!( - pending_rounds = pending_rounds, - ordered_round = ordered_round, - oldest_not_committed_round = oldest_not_committed_round, - commit_round = commit_round, - oldest_not_committed_spent_in_pipeline = - oldest_not_committed_spent_in_pipeline.as_millis() as u64, - latency_to_ordered_ms = latency_to_ordered.as_millis() as u64, - latency_to_oldest_not_committed = latency_to_oldest_not_committed.as_millis() as u64, - latency_to_committed_ms = latency_to_committed.as_millis() as u64, - latency_to_commit_cert_ms = - latency_from_proposal(proposal_timestamp, commit_cert_timestamp).as_millis() as u64, - "Pipeline pending latency on proposal creation", - ); - - counters::CONSENSUS_PROPOSAL_PENDING_ROUNDS.observe(pending_rounds as f64); - counters::CONSENSUS_PROPOSAL_PENDING_DURATION - .observe(oldest_not_committed_spent_in_pipeline.as_secs_f64()); - - if pending_rounds > 1 { - // TODO cleanup - // previous logic was using difference between committed and ordered. - // keeping it until we test out the new logic. - // latency_to_oldest_not_committed - // .saturating_sub(latency_to_ordered.min(MAX_ORDERING_PIPELINE_LATENCY_REDUCTION)) - - oldest_not_committed_spent_in_pipeline - } else { - Duration::ZERO - } - } -} - -impl BlockReader for BlockStore { - fn block_exists(&self, block_id: HashValue) -> bool { - self.inner.read().block_exists(&block_id) - } - - fn get_block(&self, block_id: HashValue) -> Option> { - self.inner.read().get_block(&block_id) - } - - fn ordered_root(&self) -> Arc { - self.inner.read().ordered_root() - } - - fn commit_root(&self) -> Arc { - self.inner.read().commit_root() - } - - fn get_quorum_cert_for_block(&self, block_id: HashValue) -> Option> { - self.inner.read().get_quorum_cert_for_block(&block_id) - } - - fn path_from_ordered_root(&self, block_id: HashValue) -> Option>> { - self.inner.read().path_from_ordered_root(block_id) - } - - fn path_from_commit_root(&self, block_id: HashValue) -> Option>> { - self.inner.read().path_from_commit_root(block_id) - } - - fn highest_certified_block(&self) -> Arc { - self.inner.read().highest_certified_block() - } - - fn highest_quorum_cert(&self) -> Arc { - self.inner.read().highest_quorum_cert() - } - - fn highest_ordered_cert(&self) -> Arc { - self.inner.read().highest_ordered_cert() - } - - fn highest_commit_cert(&self) -> Arc { - self.inner.read().highest_commit_cert() - } - - fn highest_2chain_timeout_cert(&self) -> Option> { - self.inner.read().highest_2chain_timeout_cert() - } - - fn sync_info(&self) -> SyncInfo { - SyncInfo::new_decoupled( - self.highest_quorum_cert().as_ref().clone(), - self.highest_ordered_cert().as_ref().clone(), - self.highest_commit_cert().as_ref().clone(), - self.highest_2chain_timeout_cert() - .map(|tc| tc.as_ref().clone()), - ) - } - - fn vote_back_pressure(&self) -> bool { - self.vote_back_pressure() - } - - fn pipeline_pending_latency(&self, proposal_timestamp: Duration) -> Duration { - self.pipeline_pending_latency(proposal_timestamp) - } -} - -#[cfg(any(test, feature = "fuzzing"))] -impl BlockStore { - /// Returns the number of blocks in the tree - pub(crate) fn len(&self) -> usize { - self.inner.read().len() - } - - /// Returns the number of child links in the tree - pub(crate) fn child_links(&self) -> usize { - self.inner.read().child_links() - } - - /// The number of pruned blocks that are still available in memory - pub(super) fn pruned_blocks_in_mem(&self) -> usize { - self.inner.read().pruned_blocks_in_mem() - } - - /// Helper function to insert the block with the qc together - pub async fn insert_block_with_qc(&self, block: Block) -> anyhow::Result> { - self.insert_single_quorum_cert(block.quorum_cert().clone())?; - if self.ordered_root().round() < block.quorum_cert().commit_info().round() { - self.send_for_execution(block.quorum_cert().clone()).await?; - } - self.insert_ordered_block(block).await - } -} diff --git a/consensus/src/block_storage/block_store_test.rs b/consensus/src/block_storage/block_store_test.rs deleted file mode 100644 index 3943aba4c9ea3..0000000000000 --- a/consensus/src/block_storage/block_store_test.rs +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{block_store::sync_manager::NeedFetchResult, BlockReader}, - pending_votes::{PendingVotes, VoteReceptionResult}, - test_utils::{ - build_empty_tree, build_simple_tree, consensus_runtime, timed_block_on, TreeInserter, - }, - util::mock_time_service::SimulatedTimeService, -}; -use aptos_config::config::QcAggregatorType; -use aptos_consensus_types::{ - block::{ - block_test_utils::{ - self, certificate_for_genesis, gen_test_certificate, placeholder_certificate_for_block, - placeholder_ledger_info, - }, - Block, - }, - common::{Author, Payload}, - vote::Vote, - vote_data::VoteData, -}; -use aptos_crypto::{HashValue, PrivateKey}; -use aptos_types::{ - validator_signer::ValidatorSigner, validator_verifier::random_validator_verifier, -}; -use futures_channel::mpsc::unbounded; -use proptest::prelude::*; -use std::{cmp::min, collections::HashSet, sync::Arc}; - -#[tokio::test] -async fn test_highest_block_and_quorum_cert() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - assert_eq!( - block_store.highest_certified_block().block(), - &Block::make_genesis_block() - ); - assert_eq!( - block_store.highest_quorum_cert().as_ref(), - &certificate_for_genesis() - ); - - let genesis = block_store.ordered_root(); - - // Genesis block and quorum certificate is still the highest - let block_round_1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - assert_eq!( - block_store.highest_certified_block().block(), - &Block::make_genesis_block() - ); - assert_eq!( - block_store.highest_quorum_cert().as_ref(), - &certificate_for_genesis() - ); - - // block_round_1 block and quorum certificate is now the highest - let block_round_3 = inserter.insert_block(&block_round_1, 3, None).await; - assert_eq!(block_store.highest_certified_block(), block_round_1); - assert_eq!( - block_store.highest_quorum_cert().as_ref(), - block_store - .get_block(block_round_3.id()) - .expect("block_round_1 should exist") - .quorum_cert() - ); - - // block_round_1 block and quorum certificate is still the highest, since block_round_4 - // also builds on block_round_1 - let block_round_4 = inserter.insert_block(&block_round_1, 4, None).await; - assert_eq!(block_store.highest_certified_block(), block_round_1); - assert_eq!( - block_store.highest_quorum_cert().as_ref(), - block_store - .get_block(block_round_4.id()) - .expect("block_round_1 should exist") - .quorum_cert() - ); -} - -#[tokio::test] -async fn test_qc_ancestry() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let genesis = block_store.ordered_root(); - let block_a_1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - let block_a_2 = inserter.insert_block(&block_a_1, 2, None).await; - - assert_eq!( - block_store.get_block(genesis.quorum_cert().certified_block().id()), - None - ); - assert_eq!( - block_store.get_block(block_a_1.quorum_cert().certified_block().id()), - Some(genesis) - ); - assert_eq!( - block_store.get_block(block_a_2.quorum_cert().certified_block().id()), - Some(block_a_1) - ); -} - -// This test should be continuously extended to eventually become the -// single-page spec for the logic of our block storage. -proptest! { - - #[test] - fn test_block_store_insert( - (private_keys, blocks) in block_test_utils::block_forest_and_its_keys( - // quorum size - 10, - // recursion depth - 50) - ){ - let authors: HashSet = private_keys.iter().map( - // match the signer_strategy in validator_signer.rs - |key| Author::from_bytes(&key.public_key().to_bytes()[0..32]).unwrap() - ).collect(); - let runtime = consensus_runtime(); - let block_store = build_empty_tree(); - for block in blocks { - if block.round() > 0 && authors.contains(&block.author().unwrap()) { - let known_parent = block_store.block_exists(block.parent_id()); - let certified_parent = block.quorum_cert().certified_block().id() == block.parent_id(); - let verify_res = block.verify_well_formed(); - let res = timed_block_on(&runtime, block_store.insert_ordered_block(block.clone())); - if !certified_parent { - prop_assert!(verify_res.is_err()); - } else if !known_parent { - // We cannot really bring blocks in this test because the block retrieval - // functionality invokes event processing, which is not setup here. - assert!(res.is_err()); - } - else { - // The parent must be present if we get to this line. - let parent = block_store.get_block(block.parent_id()).unwrap(); - if block.round() <= parent.round() { - prop_assert!(res.is_err()); - } else { - let executed_block = res.unwrap(); - prop_assert_eq!(executed_block.block(), - &block, - "expected ok on block: {:#?}, got {:#?}", block, executed_block.block()); - } - } - } - } - } -} - -#[tokio::test] -async fn test_block_store_prune() { - let (blocks, block_store) = build_simple_tree().await; - // Attempt to prune genesis block (should be no-op) - assert_eq!(block_store.prune_tree(blocks[0].id()).len(), 0); - assert_eq!(block_store.len(), 7); - assert_eq!(block_store.child_links(), block_store.len() - 1); - assert_eq!(block_store.pruned_blocks_in_mem(), 0); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block A1 - assert_eq!(block_store.prune_tree(blocks[1].id()).len(), 4); - assert_eq!(block_store.len(), 3); - assert_eq!(block_store.child_links(), block_store.len() - 1); - assert_eq!(block_store.pruned_blocks_in_mem(), 4); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block A2 - assert_eq!(block_store.prune_tree(blocks[2].id()).len(), 5); - assert_eq!(block_store.len(), 2); - assert_eq!(block_store.child_links(), block_store.len() - 1); - assert_eq!(block_store.pruned_blocks_in_mem(), 5); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block A3 - assert_eq!(block_store.prune_tree(blocks[3].id()).len(), 6); - assert_eq!(block_store.len(), 1); - assert_eq!(block_store.child_links(), block_store.len() - 1); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block B1 - assert_eq!(block_store.prune_tree(blocks[4].id()).len(), 4); - assert_eq!(block_store.len(), 3); - assert_eq!(block_store.child_links(), block_store.len() - 1); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block B2 - assert_eq!(block_store.prune_tree(blocks[5].id()).len(), 6); - assert_eq!(block_store.len(), 1); - assert_eq!(block_store.child_links(), block_store.len() - 1); - - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block C1 - assert_eq!(block_store.prune_tree(blocks[6].id()).len(), 6); - assert_eq!(block_store.len(), 1); - assert_eq!(block_store.child_links(), block_store.len() - 1); - - // Prune the chain of Genesis -> B1 -> B2 - let (blocks, block_store) = build_simple_tree().await; - // Prune up to block B1 - assert_eq!(block_store.prune_tree(blocks[4].id()).len(), 4); - assert_eq!(block_store.len(), 3); - assert_eq!(block_store.child_links(), block_store.len() - 1); - // Prune up to block B2 - assert_eq!(block_store.prune_tree(blocks[5].id()).len(), 2); - assert_eq!(block_store.len(), 1); - assert_eq!(block_store.child_links(), block_store.len() - 1); -} - -#[tokio::test] -async fn test_block_tree_gc() { - // build a tree with 100 nodes, max_pruned_nodes_in_mem = 10 - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let genesis = block_store.ordered_root(); - let mut cur_node = block_store.get_block(genesis.id()).unwrap(); - let mut added_blocks = vec![]; - - for round in 1..100 { - if round == 1 { - cur_node = inserter - .insert_block_with_qc(certificate_for_genesis(), &cur_node, round) - .await; - } else { - cur_node = inserter.insert_block(&cur_node, round, None).await; - } - added_blocks.push(cur_node.clone()); - } - - for (i, block) in added_blocks.iter().enumerate() { - assert_eq!(block_store.len(), 100 - i); - assert_eq!(block_store.pruned_blocks_in_mem(), min(i, 10)); - block_store.prune_tree(block.id()); - } -} - -#[tokio::test] -async fn test_path_from_root() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let genesis = block_store - .get_block(block_store.ordered_root().id()) - .unwrap(); - let b1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - let b2 = inserter.insert_block(&b1, 2, None).await; - let b3 = inserter.insert_block(&b2, 3, None).await; - - assert_eq!( - block_store.path_from_ordered_root(b3.id()), - Some(vec![b1, b2.clone(), b3.clone()]) - ); - assert_eq!( - block_store.path_from_ordered_root(genesis.id()), - Some(vec![]) - ); - - block_store.prune_tree(b2.id()); - - assert_eq!( - block_store.path_from_ordered_root(b3.id()), - Some(vec![b3.clone()]) - ); - assert_eq!(block_store.path_from_ordered_root(genesis.id()), None); -} - -#[tokio::test] -async fn test_insert_vote() { - ::aptos_logger::Logger::init_for_testing(); - // Set up enough different authors to support different votes for the same block. - let (signers, validator_verifier) = random_validator_verifier(11, Some(10), false); - let my_signer = signers[10].clone(); - let mut inserter = TreeInserter::new(my_signer); - let block_store = inserter.block_store(); - let genesis = block_store.ordered_root(); - let block = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - let time_service = Arc::new(SimulatedTimeService::new()); - let (delayed_qc_tx, _) = unbounded(); - - let mut pending_votes = - PendingVotes::new(time_service, delayed_qc_tx, QcAggregatorType::NoDelay); - - assert!(block_store.get_quorum_cert_for_block(block.id()).is_none()); - for (i, voter) in signers.iter().enumerate().take(10).skip(1) { - let vote = Vote::new( - VoteData::new( - block.block().gen_block_info( - block.compute_result().root_hash(), - block.compute_result().version(), - block.compute_result().epoch_state().clone(), - ), - block.quorum_cert().certified_block().clone(), - ), - voter.author(), - placeholder_ledger_info(), - voter, - ) - .unwrap(); - let vote_res = pending_votes.insert_vote(&vote, &validator_verifier); - - // first vote of an author is accepted - assert_eq!(vote_res, VoteReceptionResult::VoteAdded(i as u128)); - // filter out duplicates - assert_eq!( - pending_votes.insert_vote(&vote, &validator_verifier), - VoteReceptionResult::DuplicateVote, - ); - // qc is still not there - assert!(block_store.get_quorum_cert_for_block(block.id()).is_none()); - } - - // Add the final vote to form a QC - let final_voter = &signers[0]; - let vote = Vote::new( - VoteData::new( - block.block().gen_block_info( - block.compute_result().root_hash(), - block.compute_result().version(), - block.compute_result().epoch_state().clone(), - ), - block.quorum_cert().certified_block().clone(), - ), - final_voter.author(), - placeholder_ledger_info(), - final_voter, - ) - .unwrap(); - match pending_votes.insert_vote(&vote, &validator_verifier) { - VoteReceptionResult::NewQuorumCertificate(qc) => { - assert_eq!(qc.certified_block().id(), block.id()); - block_store - .insert_single_quorum_cert(qc.as_ref().clone()) - .unwrap(); - }, - _ => { - panic!("QC not formed!"); - }, - } - - let block_qc = block_store.get_quorum_cert_for_block(block.id()).unwrap(); - assert_eq!(block_qc.certified_block().id(), block.id()); -} - -#[tokio::test] -async fn test_illegal_timestamp() { - let signer = ValidatorSigner::random(None); - let block_store = build_empty_tree(); - let genesis = block_store.ordered_root(); - let block_with_illegal_timestamp = Block::new_proposal( - Payload::empty(false, true), - 0, - // This timestamp is illegal, it is the same as genesis - genesis.timestamp_usecs(), - certificate_for_genesis(), - &signer, - Vec::new(), - ) - .unwrap(); - let result = block_store - .insert_ordered_block(block_with_illegal_timestamp) - .await; - assert!(result.is_err()); -} - -#[tokio::test] -async fn test_highest_qc() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - - // build a tree of the following form - // genesis <- a1 <- a2 <- a3 - let genesis = block_store.ordered_root(); - let a1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - assert_eq!(block_store.highest_certified_block(), genesis); - let a2 = inserter.insert_block(&a1, 2, None).await; - assert_eq!(block_store.highest_certified_block(), a1); - let _a3 = inserter.insert_block(&a2, 3, None).await; - assert_eq!(block_store.highest_certified_block(), a2); -} - -#[tokio::test] -async fn test_need_fetch_for_qc() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - - // build a tree of the following form - // genesis <- a1 <- a2 <- a3 - let genesis = block_store.ordered_root(); - let a1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - let a2 = inserter.insert_block(&a1, 2, None).await; - let a3 = inserter.insert_block(&a2, 3, None).await; - block_store.prune_tree(a2.id()); - let need_fetch_qc = placeholder_certificate_for_block( - &[inserter.signer().clone()], - HashValue::zero(), - a3.round() + 1, - HashValue::zero(), - a3.round(), - ); - let too_old_qc = certificate_for_genesis(); - let can_insert_qc = placeholder_certificate_for_block( - &[inserter.signer().clone()], - a3.id(), - a3.round(), - a2.id(), - a2.round(), - ); - let duplicate_qc = block_store.get_quorum_cert_for_block(a2.id()).unwrap(); - assert_eq!( - block_store.need_fetch_for_quorum_cert(&need_fetch_qc), - NeedFetchResult::NeedFetch - ); - assert_eq!( - block_store.need_fetch_for_quorum_cert(&too_old_qc), - NeedFetchResult::QCRoundBeforeRoot, - ); - assert_eq!( - block_store.need_fetch_for_quorum_cert(&can_insert_qc), - NeedFetchResult::QCBlockExist, - ); - assert_eq!( - block_store.need_fetch_for_quorum_cert(duplicate_qc.as_ref()), - NeedFetchResult::QCAlreadyExist, - ); -} - -#[tokio::test] -async fn test_need_sync_for_ledger_info() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - - let mut prev = block_store.ordered_root(); - for i in 1..=30 { - prev = inserter.insert_block(&prev, i, None).await; - } - inserter - .insert_block( - &prev, - 31, - Some(prev.block().gen_block_info(HashValue::zero(), 1, None)), - ) - .await; - assert_eq!(block_store.ordered_root().round(), 30); - assert_eq!(block_store.commit_root().round(), 0); - - let create_ledger_info = |round: u64| { - let future_block = inserter.create_block_with_qc( - certificate_for_genesis(), - 1, - round, - Payload::empty(false, true), - vec![], - ); - gen_test_certificate( - &[inserter.signer().clone()], - future_block.gen_block_info(HashValue::zero(), 0, None), - future_block.quorum_cert().parent_block().clone(), - Some(future_block.gen_block_info(HashValue::zero(), 0, None)), - ) - .ledger_info() - .clone() - }; - // it's larger and the block doesn't exist in the tree - let ordered_round_too_far = block_store.ordered_root().round() + 1; - let ordered_too_far = create_ledger_info(ordered_round_too_far); - assert!(block_store.need_sync_for_ledger_info(&ordered_too_far)); - - let committed_round_too_far = - block_store.commit_root().round() + 30.max(block_store.vote_back_pressure_limit * 2) + 1; - let committed_too_far = create_ledger_info(committed_round_too_far); - assert!(block_store.need_sync_for_ledger_info(&committed_too_far)); - - let round_not_too_far = - block_store.commit_root().round() + block_store.vote_back_pressure_limit + 1; - let not_too_far = create_ledger_info(round_not_too_far); - assert!(!block_store.need_sync_for_ledger_info(¬_too_far)); -} diff --git a/consensus/src/block_storage/block_tree.rs b/consensus/src/block_storage/block_tree.rs deleted file mode 100644 index 7c16b0946dc35..0000000000000 --- a/consensus/src/block_storage/block_tree.rs +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - counters::update_counters_for_committed_blocks, - logging::{LogEvent, LogSchema}, - persistent_liveness_storage::PersistentLivenessStorage, -}; -use anyhow::bail; -use aptos_consensus_types::{ - pipelined_block::PipelinedBlock, quorum_cert::QuorumCert, - timeout_2chain::TwoChainTimeoutCertificate, -}; -use aptos_crypto::HashValue; -use aptos_logger::prelude::*; -use aptos_types::{block_info::BlockInfo, ledger_info::LedgerInfoWithSignatures}; -use mirai_annotations::{checked_verify_eq, precondition}; -use std::{ - collections::{vec_deque::VecDeque, HashMap, HashSet}, - sync::Arc, -}; - -/// This structure is a wrapper of [`ExecutedBlock`](aptos_consensus_types::pipelined_block::PipelinedBlock) -/// that adds `children` field to know the parent-child relationship between blocks. -struct LinkableBlock { - /// Executed block that has raw block data and execution output. - executed_block: Arc, - /// The set of children for cascading pruning. Note: a block may have multiple children. - children: HashSet, -} - -impl LinkableBlock { - pub fn new(block: PipelinedBlock) -> Self { - Self { - executed_block: Arc::new(block), - children: HashSet::new(), - } - } - - pub fn executed_block(&self) -> &Arc { - &self.executed_block - } - - pub fn children(&self) -> &HashSet { - &self.children - } - - pub fn add_child(&mut self, child_id: HashValue) { - assert!( - self.children.insert(child_id), - "Block {:x} already existed.", - child_id, - ); - } -} - -impl LinkableBlock { - pub fn id(&self) -> HashValue { - self.executed_block().id() - } -} - -/// This structure maintains a consistent block tree of parent and children links. Blocks contain -/// parent links and are immutable. For all parent links, a child link exists. This structure -/// should only be used internally in BlockStore. -pub struct BlockTree { - /// All the blocks known to this replica (with parent links) - id_to_block: HashMap, - /// Root of the tree. This is the root of ordering phase - ordered_root_id: HashValue, - /// Commit Root id: this is the root of commit phase - commit_root_id: HashValue, - /// A certified block id with highest round - highest_certified_block_id: HashValue, - - /// The quorum certificate of highest_certified_block - highest_quorum_cert: Arc, - /// The highest 2-chain timeout certificate (if any). - highest_2chain_timeout_cert: Option>, - /// The quorum certificate that has highest commit info. - highest_ordered_cert: Arc, - /// The quorum certificate that has highest commit decision info. - highest_commit_cert: Arc, - /// Map of block id to its completed quorum certificate (2f + 1 votes) - id_to_quorum_cert: HashMap>, - /// To keep the IDs of the elements that have been pruned from the tree but not cleaned up yet. - pruned_block_ids: VecDeque, - /// Num pruned blocks to keep in memory. - max_pruned_blocks_in_mem: usize, -} - -impl BlockTree { - pub(super) fn new( - root: PipelinedBlock, - root_quorum_cert: QuorumCert, - root_ordered_cert: QuorumCert, - root_commit_cert: QuorumCert, - max_pruned_blocks_in_mem: usize, - highest_2chain_timeout_cert: Option>, - ) -> Self { - assert_eq!( - root.id(), - root_ordered_cert.commit_info().id(), - "inconsistent root and ledger info" - ); - let root_id = root.id(); - - let mut id_to_block = HashMap::new(); - id_to_block.insert(root_id, LinkableBlock::new(root)); - counters::NUM_BLOCKS_IN_TREE.set(1); - - let root_quorum_cert = Arc::new(root_quorum_cert); - let mut id_to_quorum_cert = HashMap::new(); - id_to_quorum_cert.insert( - root_quorum_cert.certified_block().id(), - Arc::clone(&root_quorum_cert), - ); - - let pruned_block_ids = VecDeque::with_capacity(max_pruned_blocks_in_mem); - - BlockTree { - id_to_block, - ordered_root_id: root_id, - commit_root_id: root_id, // initially we set commit_root_id = root_id - highest_certified_block_id: root_id, - highest_quorum_cert: Arc::clone(&root_quorum_cert), - highest_ordered_cert: Arc::new(root_ordered_cert), - highest_commit_cert: Arc::new(root_commit_cert), - id_to_quorum_cert, - pruned_block_ids, - max_pruned_blocks_in_mem, - highest_2chain_timeout_cert, - } - } - - // This method will only be used in this module. - fn get_linkable_block(&self, block_id: &HashValue) -> Option<&LinkableBlock> { - self.id_to_block.get(block_id) - } - - // This method will only be used in this module. - fn get_linkable_block_mut(&mut self, block_id: &HashValue) -> Option<&mut LinkableBlock> { - self.id_to_block.get_mut(block_id) - } - - /// fetch all the quorum certs with non-empty commit info - pub fn get_all_quorum_certs_with_commit_info(&self) -> Vec { - return self - .id_to_quorum_cert - .values() - .filter(|qc| qc.commit_info() != &BlockInfo::empty()) - .map(|qc| (**qc).clone()) - .collect::>(); - } - - // This method will only be used in this module. - // This method is used in pruning and length query, - // to reflect the actual root, we use commit root - fn linkable_root(&self) -> &LinkableBlock { - self.get_linkable_block(&self.commit_root_id) - .expect("Root must exist") - } - - fn remove_block(&mut self, block_id: HashValue) { - // Remove the block from the store - self.id_to_block.remove(&block_id); - self.id_to_quorum_cert.remove(&block_id); - } - - pub(super) fn block_exists(&self, block_id: &HashValue) -> bool { - self.id_to_block.contains_key(block_id) - } - - pub(super) fn get_block(&self, block_id: &HashValue) -> Option> { - self.get_linkable_block(block_id) - .map(|lb| Arc::clone(lb.executed_block())) - } - - pub(super) fn ordered_root(&self) -> Arc { - self.get_block(&self.ordered_root_id) - .expect("Root must exist") - } - - pub(super) fn commit_root(&self) -> Arc { - self.get_block(&self.commit_root_id) - .expect("Commit root must exist") - } - - pub(super) fn highest_certified_block(&self) -> Arc { - self.get_block(&self.highest_certified_block_id) - .expect("Highest cerfified block must exist") - } - - pub(super) fn highest_quorum_cert(&self) -> Arc { - Arc::clone(&self.highest_quorum_cert) - } - - pub(super) fn highest_2chain_timeout_cert(&self) -> Option> { - self.highest_2chain_timeout_cert.clone() - } - - /// Replace highest timeout cert with the given value. - pub(super) fn replace_2chain_timeout_cert(&mut self, tc: Arc) { - self.highest_2chain_timeout_cert.replace(tc); - } - - pub(super) fn highest_ordered_cert(&self) -> Arc { - Arc::clone(&self.highest_ordered_cert) - } - - pub(super) fn highest_commit_cert(&self) -> Arc { - Arc::clone(&self.highest_commit_cert) - } - - pub(super) fn get_quorum_cert_for_block( - &self, - block_id: &HashValue, - ) -> Option> { - self.id_to_quorum_cert.get(block_id).cloned() - } - - pub(super) fn insert_block( - &mut self, - block: PipelinedBlock, - ) -> anyhow::Result> { - let block_id = block.id(); - if let Some(existing_block) = self.get_block(&block_id) { - debug!("Already had block {:?} for id {:?} when trying to add another block {:?} for the same id", - existing_block, - block_id, - block); - checked_verify_eq!(existing_block.compute_result(), block.compute_result()); - Ok(existing_block) - } else { - match self.get_linkable_block_mut(&block.parent_id()) { - Some(parent_block) => parent_block.add_child(block_id), - None => bail!("Parent block {} not found", block.parent_id()), - }; - let linkable_block = LinkableBlock::new(block); - let arc_block = Arc::clone(linkable_block.executed_block()); - assert!(self.id_to_block.insert(block_id, linkable_block).is_none()); - counters::NUM_BLOCKS_IN_TREE.inc(); - Ok(arc_block) - } - } - - fn update_highest_commit_cert(&mut self, new_commit_cert: QuorumCert) { - if new_commit_cert.commit_info().round() > self.highest_commit_cert.commit_info().round() { - self.highest_commit_cert = Arc::new(new_commit_cert); - self.update_commit_root(self.highest_commit_cert.commit_info().id()); - } - } - - pub(super) fn insert_quorum_cert(&mut self, qc: QuorumCert) -> anyhow::Result<()> { - let block_id = qc.certified_block().id(); - let qc = Arc::new(qc); - - // Safety invariant: For any two quorum certificates qc1, qc2 in the block store, - // qc1 == qc2 || qc1.round != qc2.round - // The invariant is quadratic but can be maintained in linear time by the check - // below. - precondition!({ - let qc_round = qc.certified_block().round(); - self.id_to_quorum_cert.values().all(|x| { - (*(*x).ledger_info()).ledger_info().consensus_data_hash() - == (*(*qc).ledger_info()).ledger_info().consensus_data_hash() - || x.certified_block().round() != qc_round - }) - }); - - match self.get_block(&block_id) { - Some(block) => { - if block.round() > self.highest_certified_block().round() { - self.highest_certified_block_id = block.id(); - self.highest_quorum_cert = Arc::clone(&qc); - } - }, - None => bail!("Block {} not found", block_id), - } - - self.id_to_quorum_cert - .entry(block_id) - .or_insert_with(|| Arc::clone(&qc)); - - if self.highest_ordered_cert.commit_info().round() < qc.commit_info().round() { - self.highest_ordered_cert = qc; - } - - Ok(()) - } - - /// Find the blocks to prune up to next_root_id (keep next_root_id's block). Any branches not - /// part of the next_root_id's tree should be removed as well. - /// - /// For example, root = B0 - /// B0--> B1--> B2 - /// ╰--> B3--> B4 - /// - /// prune_tree(B_3) should be left with - /// B3--> B4, root = B3 - /// - /// Note this function is read-only, use with process_pruned_blocks to do the actual prune. - pub(super) fn find_blocks_to_prune(&self, next_root_id: HashValue) -> VecDeque { - // Nothing to do if this is the commit root - if next_root_id == self.commit_root_id { - return VecDeque::new(); - } - - let mut blocks_pruned = VecDeque::new(); - let mut blocks_to_be_pruned = vec![self.linkable_root()]; - while let Some(block_to_remove) = blocks_to_be_pruned.pop() { - // Add the children to the blocks to be pruned (if any), but stop when it reaches the - // new root - for child_id in block_to_remove.children() { - if next_root_id == *child_id { - continue; - } - blocks_to_be_pruned.push( - self.get_linkable_block(child_id) - .expect("Child must exist in the tree"), - ); - } - // Track all the block ids removed - blocks_pruned.push_back(block_to_remove.id()); - } - blocks_pruned - } - - pub(super) fn update_ordered_root(&mut self, root_id: HashValue) { - assert!(self.block_exists(&root_id)); - self.ordered_root_id = root_id; - } - - pub(super) fn update_commit_root(&mut self, root_id: HashValue) { - assert!(self.block_exists(&root_id)); - self.commit_root_id = root_id; - } - - /// Process the data returned by the prune_tree, they're separated because caller might - /// be interested in doing extra work e.g. delete from persistent storage. - /// Note that we do not necessarily remove the pruned blocks: they're kept in a separate buffer - /// for some time in order to enable other peers to retrieve the blocks even after they've - /// been committed. - pub(super) fn process_pruned_blocks(&mut self, mut newly_pruned_blocks: VecDeque) { - counters::NUM_BLOCKS_IN_TREE.sub(newly_pruned_blocks.len() as i64); - // The newly pruned blocks are pushed back to the deque pruned_block_ids. - // In case the overall number of the elements is greater than the predefined threshold, - // the oldest elements (in the front of the deque) are removed from the tree. - self.pruned_block_ids.append(&mut newly_pruned_blocks); - if self.pruned_block_ids.len() > self.max_pruned_blocks_in_mem { - let num_blocks_to_remove = self.pruned_block_ids.len() - self.max_pruned_blocks_in_mem; - for _ in 0..num_blocks_to_remove { - if let Some(id) = self.pruned_block_ids.pop_front() { - self.remove_block(id); - } - } - } - } - - /// Returns all the blocks between the commit root and the given block, including the given block - /// but excluding the root. - /// In case a given block is not the successor of the root, return None. - /// While generally the provided blocks should always belong to the active tree, there might be - /// a race, in which the root of the tree is propagated forward between retrieving the block - /// and getting its path from root (e.g., at proposal generator). Hence, we don't want to panic - /// and prefer to return None instead. - pub(super) fn path_from_root_to_block( - &self, - block_id: HashValue, - root_id: HashValue, - root_round: u64, - ) -> Option>> { - let mut res = vec![]; - let mut cur_block_id = block_id; - loop { - match self.get_block(&cur_block_id) { - Some(ref block) if block.round() <= root_round => { - break; - }, - Some(block) => { - cur_block_id = block.parent_id(); - res.push(block); - }, - None => return None, - } - } - // At this point cur_block.round() <= self.root.round() - if cur_block_id != root_id { - return None; - } - // Called `.reverse()` to get the chronically increased order. - res.reverse(); - Some(res) - } - - pub(super) fn path_from_ordered_root( - &self, - block_id: HashValue, - ) -> Option>> { - self.path_from_root_to_block(block_id, self.ordered_root_id, self.ordered_root().round()) - } - - pub(super) fn path_from_commit_root( - &self, - block_id: HashValue, - ) -> Option>> { - self.path_from_root_to_block(block_id, self.commit_root_id, self.commit_root().round()) - } - - pub(super) fn max_pruned_blocks_in_mem(&self) -> usize { - self.max_pruned_blocks_in_mem - } - - /// Update the counters for committed blocks and prune them from the in-memory and persisted store. - pub fn commit_callback( - &mut self, - storage: Arc, - blocks_to_commit: &[Arc], - finality_proof: QuorumCert, - commit_decision: LedgerInfoWithSignatures, - ) { - let commit_proof = finality_proof - .create_merged_with_executed_state(commit_decision) - .expect("Inconsistent commit proof and evaluation decision, cannot commit block"); - - let block_to_commit = blocks_to_commit.last().unwrap().clone(); - update_counters_for_committed_blocks(blocks_to_commit); - let current_round = self.commit_root().round(); - let committed_round = block_to_commit.round(); - debug!( - LogSchema::new(LogEvent::CommitViaBlock).round(current_round), - committed_round = committed_round, - block_id = block_to_commit.id(), - ); - - let id_to_remove = self.find_blocks_to_prune(block_to_commit.id()); - if let Err(e) = storage.prune_tree(id_to_remove.clone().into_iter().collect()) { - // it's fine to fail here, as long as the commit succeeds, the next restart will clean - // up dangling blocks, and we need to prune the tree to keep the root consistent with - // executor. - warn!(error = ?e, "fail to delete block"); - } - self.process_pruned_blocks(id_to_remove); - self.update_highest_commit_cert(commit_proof); - } -} - -#[cfg(any(test, feature = "fuzzing"))] -impl BlockTree { - /// Returns the number of blocks in the tree - pub(super) fn len(&self) -> usize { - // BFS over the tree to find the number of blocks in the tree. - let mut res = 0; - let mut to_visit = vec![self.linkable_root()]; - while let Some(block) = to_visit.pop() { - res += 1; - for child_id in block.children() { - to_visit.push( - self.get_linkable_block(child_id) - .expect("Child must exist in the tree"), - ); - } - } - res - } - - /// Returns the number of child links in the tree - pub(super) fn child_links(&self) -> usize { - self.len() - 1 - } - - /// The number of pruned blocks that are still available in memory - pub(super) fn pruned_blocks_in_mem(&self) -> usize { - self.pruned_block_ids.len() - } -} diff --git a/consensus/src/block_storage/mod.rs b/consensus/src/block_storage/mod.rs deleted file mode 100644 index 8b553706127b8..0000000000000 --- a/consensus/src/block_storage/mod.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_consensus_types::{ - pipelined_block::PipelinedBlock, quorum_cert::QuorumCert, sync_info::SyncInfo, - timeout_2chain::TwoChainTimeoutCertificate, -}; -use aptos_crypto::HashValue; -pub use block_store::{sync_manager::BlockRetriever, BlockStore}; -use std::{sync::Arc, time::Duration}; - -mod block_store; -mod block_tree; -pub mod tracing; - -pub trait BlockReader: Send + Sync { - /// Check if a block with the block_id exist in the BlockTree. - fn block_exists(&self, block_id: HashValue) -> bool; - - /// Try to get a block with the block_id, return an Arc of it if found. - fn get_block(&self, block_id: HashValue) -> Option>; - - /// Get the current ordered root block of the BlockTree. - fn ordered_root(&self) -> Arc; - - /// Get the current commit root block of the BlockTree. - fn commit_root(&self) -> Arc; - - fn get_quorum_cert_for_block(&self, block_id: HashValue) -> Option>; - - /// Returns all the blocks between the ordered/commit root and the given block, including the given block - /// but excluding the root. - /// In case a given block is not the successor of the root, return None. - /// For example if a tree is b0 <- b1 <- b2 <- b3, then - /// path_from_root(b2) -> Some([b2, b1]) - /// path_from_root(b0) -> Some([]) - /// path_from_root(a) -> None - fn path_from_ordered_root(&self, block_id: HashValue) -> Option>>; - - fn path_from_commit_root(&self, block_id: HashValue) -> Option>>; - - /// Return the certified block with the highest round. - fn highest_certified_block(&self) -> Arc; - - /// Return the quorum certificate with the highest round - fn highest_quorum_cert(&self) -> Arc; - - /// Return the quorum certificate that carries ledger info with the highest round - fn highest_ordered_cert(&self) -> Arc; - - /// Return the highest timeout certificate if available. - fn highest_2chain_timeout_cert(&self) -> Option>; - - /// Return the highest commit decision quorum certificate. - fn highest_commit_cert(&self) -> Arc; - - /// Return the combination of highest quorum cert, timeout cert and commit cert. - fn sync_info(&self) -> SyncInfo; - - /// Return if the consensus is backpressured - fn vote_back_pressure(&self) -> bool; - - // Return time difference between last committed block and new proposal - fn pipeline_pending_latency(&self, proposal_timestamp: Duration) -> Duration; -} diff --git a/consensus/src/block_storage/sync_manager.rs b/consensus/src/block_storage/sync_manager.rs deleted file mode 100644 index f7849a943b5fb..0000000000000 --- a/consensus/src/block_storage/sync_manager.rs +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{BlockReader, BlockStore}, - epoch_manager::LivenessStorageData, - logging::{LogEvent, LogSchema}, - monitor, - network::{IncomingBlockRetrievalRequest, NetworkSender}, - network_interface::ConsensusMsg, - payload_manager::PayloadManager, - persistent_liveness_storage::{LedgerRecoveryData, PersistentLivenessStorage, RecoveryData}, - pipeline::execution_client::TExecutionClient, -}; -use anyhow::{bail, Context}; -use aptos_consensus_types::{ - block::Block, - block_retrieval::{ - BlockRetrievalRequest, BlockRetrievalResponse, BlockRetrievalStatus, NUM_PEERS_PER_RETRY, - NUM_RETRIES, RETRY_INTERVAL_MSEC, RPC_TIMEOUT_MSEC, - }, - common::Author, - quorum_cert::QuorumCert, - sync_info::SyncInfo, -}; -use aptos_crypto::HashValue; -use aptos_logger::prelude::*; -use aptos_types::{ - account_address::AccountAddress, epoch_change::EpochChangeProof, - ledger_info::LedgerInfoWithSignatures, -}; -use fail::fail_point; -use futures::{stream::FuturesUnordered, StreamExt}; -use rand::{prelude::*, Rng}; -use std::{clone::Clone, cmp::min, sync::Arc, time::Duration}; -use tokio::time; - -#[derive(Debug, PartialEq, Eq)] -/// Whether we need to do block retrieval if we want to insert a Quorum Cert. -pub enum NeedFetchResult { - QCRoundBeforeRoot, - QCAlreadyExist, - QCBlockExist, - NeedFetch, -} - -impl BlockStore { - /// Check if we're far away from this ledger info and need to sync. - /// This ensures that the block referred by the ledger info is not in buffer manager. - pub fn need_sync_for_ledger_info(&self, li: &LedgerInfoWithSignatures) -> bool { - // TODO move min gap to fallback (30) to config. - (self.ordered_root().round() < li.commit_info().round() - && !self.block_exists(li.commit_info().id())) - || self.commit_root().round() + 30.max(2 * self.vote_back_pressure_limit) - < li.commit_info().round() - } - - /// Checks if quorum certificate can be inserted in block store without RPC - /// Returns the enum to indicate the detailed status. - pub fn need_fetch_for_quorum_cert(&self, qc: &QuorumCert) -> NeedFetchResult { - if qc.certified_block().round() < self.ordered_root().round() { - return NeedFetchResult::QCRoundBeforeRoot; - } - if self - .get_quorum_cert_for_block(qc.certified_block().id()) - .is_some() - { - return NeedFetchResult::QCAlreadyExist; - } - if self.block_exists(qc.certified_block().id()) { - return NeedFetchResult::QCBlockExist; - } - NeedFetchResult::NeedFetch - } - - /// Fetches dependencies for given sync_info.quorum_cert - /// If gap is large, performs state sync using sync_to_highest_ordered_cert - /// Inserts sync_info.quorum_cert into block store as the last step - pub async fn add_certs( - &self, - sync_info: &SyncInfo, - mut retriever: BlockRetriever, - ) -> anyhow::Result<()> { - self.sync_to_highest_commit_cert( - sync_info.highest_commit_cert().ledger_info(), - &retriever.network, - ) - .await; - self.sync_to_highest_ordered_cert( - sync_info.highest_ordered_cert().clone(), - sync_info.highest_commit_cert().clone(), - &mut retriever, - ) - .await?; - - self.insert_quorum_cert(sync_info.highest_ordered_cert(), &mut retriever) - .await?; - - self.insert_quorum_cert(sync_info.highest_quorum_cert(), &mut retriever) - .await?; - - if let Some(tc) = sync_info.highest_2chain_timeout_cert() { - self.insert_2chain_timeout_certificate(Arc::new(tc.clone()))?; - } - Ok(()) - } - - pub async fn insert_quorum_cert( - &self, - qc: &QuorumCert, - retriever: &mut BlockRetriever, - ) -> anyhow::Result<()> { - match self.need_fetch_for_quorum_cert(qc) { - NeedFetchResult::NeedFetch => self.fetch_quorum_cert(qc.clone(), retriever).await?, - NeedFetchResult::QCBlockExist => self.insert_single_quorum_cert(qc.clone())?, - NeedFetchResult::QCAlreadyExist => return Ok(()), - _ => (), - } - if self.ordered_root().round() < qc.commit_info().round() { - self.send_for_execution(qc.clone()).await?; - if qc.ends_epoch() { - retriever - .network - .broadcast_epoch_change(EpochChangeProof::new( - vec![qc.ledger_info().clone()], - /* more = */ false, - )) - .await; - } - } - Ok(()) - } - - /// Insert the quorum certificate separately from the block, used to split the processing of - /// updating the consensus state(with qc) and deciding whether to vote(with block) - /// The missing ancestors are going to be retrieved from the given peer. If a given peer - /// fails to provide the missing ancestors, the qc is not going to be added. - async fn fetch_quorum_cert( - &self, - qc: QuorumCert, - retriever: &mut BlockRetriever, - ) -> anyhow::Result<()> { - let mut pending = vec![]; - let mut retrieve_qc = qc.clone(); - loop { - if self.block_exists(retrieve_qc.certified_block().id()) { - break; - } - let mut blocks = retriever - .retrieve_block_for_qc(&retrieve_qc, 1, retrieve_qc.certified_block().id()) - .await?; - // retrieve_block_for_qc guarantees that blocks has exactly 1 element - let block = blocks.remove(0); - retrieve_qc = block.quorum_cert().clone(); - pending.push(block); - } - // insert the qc <- block pair - while let Some(block) = pending.pop() { - let block_qc = block.quorum_cert().clone(); - self.insert_single_quorum_cert(block_qc)?; - self.insert_ordered_block(block).await?; - } - self.insert_single_quorum_cert(qc) - } - - /// Check the highest ordered cert sent by peer to see if we're behind and start a fast - /// forward sync if the committed block doesn't exist in our tree. - /// It works as follows: - /// 1. request the gap blocks from the peer (from highest_ledger_info to highest_ordered_cert) - /// 2. We persist the gap blocks to storage before start sync to ensure we could restart if we - /// crash in the middle of the sync. - /// 3. We prune the old tree and replace with a new tree built with the 3-chain. - async fn sync_to_highest_ordered_cert( - &self, - highest_ordered_cert: QuorumCert, - highest_commit_cert: QuorumCert, - retriever: &mut BlockRetriever, - ) -> anyhow::Result<()> { - if !self.need_sync_for_ledger_info(highest_commit_cert.ledger_info()) { - return Ok(()); - } - let (root, root_metadata, blocks, quorum_certs) = Self::fast_forward_sync( - &highest_ordered_cert, - &highest_commit_cert, - retriever, - self.storage.clone(), - self.execution_client.clone(), - self.payload_manager.clone(), - ) - .await? - .take(); - info!( - LogSchema::new(LogEvent::CommitViaSync).round(self.ordered_root().round()), - committed_round = root.0.round(), - block_id = root.0.id(), - ); - self.rebuild(root, root_metadata, blocks, quorum_certs) - .await; - - if highest_commit_cert.ledger_info().ledger_info().ends_epoch() { - retriever - .network - .send_epoch_change(EpochChangeProof::new( - vec![highest_ordered_cert.ledger_info().clone()], - /* more = */ false, - )) - .await; - } - Ok(()) - } - - pub async fn fast_forward_sync<'a>( - highest_ordered_cert: &'a QuorumCert, - highest_commit_cert: &'a QuorumCert, - retriever: &'a mut BlockRetriever, - storage: Arc, - execution_client: Arc, - payload_manager: Arc, - ) -> anyhow::Result { - info!( - LogSchema::new(LogEvent::StateSync).remote_peer(retriever.preferred_peer), - "Start state sync to commit cert: {}, ordered cert: {}", - highest_commit_cert, - highest_ordered_cert, - ); - - // we fetch the blocks from - let num_blocks = highest_ordered_cert.certified_block().round() - - highest_commit_cert.ledger_info().ledger_info().round() - + 1; - - // although unlikely, we might wrap num_blocks around on a 32-bit machine - assert!(num_blocks < std::usize::MAX as u64); - - let mut blocks = retriever - .retrieve_block_for_qc( - highest_ordered_cert, - num_blocks, - highest_commit_cert.commit_info().id(), - ) - .await?; - - assert_eq!( - blocks.first().expect("blocks are empty").id(), - highest_ordered_cert.certified_block().id(), - "Expecting in the retrieval response, first block should be {}, but got {}", - highest_ordered_cert.certified_block().id(), - blocks.first().expect("blocks are empty").id(), - ); - - // Confirm retrieval ended when it hit the last block we care about, even if it didn't reach all num_blocks blocks. - assert_eq!( - blocks.last().expect("blocks are empty").id(), - highest_commit_cert.commit_info().id() - ); - - let mut quorum_certs = vec![highest_ordered_cert.clone()]; - quorum_certs.extend( - blocks - .iter() - .take(blocks.len() - 1) - .map(|block| block.quorum_cert().clone()), - ); - - // check if highest_commit_cert comes from a fork - // if so, we need to fetch it's block as well, to have a proof of commit. - if !blocks - .iter() - .any(|block| block.id() == highest_commit_cert.certified_block().id()) - { - info!( - "Found forked QC {}, fetching it as well", - highest_commit_cert - ); - let mut additional_blocks = retriever - .retrieve_block_for_qc( - highest_commit_cert, - 1, - highest_commit_cert.certified_block().id(), - ) - .await?; - - assert_eq!(additional_blocks.len(), 1); - let block = additional_blocks.pop().expect("blocks are empty"); - assert_eq!( - block.id(), - highest_commit_cert.certified_block().id(), - "Expecting in the retrieval response, for commit certificate fork, first block should be {}, but got {}", - highest_commit_cert.certified_block().id(), - block.id(), - ); - - blocks.push(block); - quorum_certs.push(highest_commit_cert.clone()); - } - - assert_eq!(blocks.len(), quorum_certs.len()); - for (i, block) in blocks.iter().enumerate() { - assert_eq!(block.id(), quorum_certs[i].certified_block().id()); - if let Some(payload) = block.payload() { - payload_manager.prefetch_payload_data(payload, block.timestamp_usecs()); - } - } - - // Check early that recovery will succeed, and return before corrupting our state in case it will not. - LedgerRecoveryData::new(highest_commit_cert.ledger_info().clone()) - .find_root(&mut blocks.clone(), &mut quorum_certs.clone()) - .with_context(|| { - // for better readability - quorum_certs.sort_by_key(|qc| qc.certified_block().round()); - format!( - "\nRoot: {:?}\nBlocks in db: {}\nQuorum Certs in db: {}\n", - highest_commit_cert.commit_info(), - blocks - .iter() - .map(|b| format!("\n\t{}", b)) - .collect::>() - .concat(), - quorum_certs - .iter() - .map(|qc| format!("\n\t{}", qc)) - .collect::>() - .concat(), - ) - })?; - - storage.save_tree(blocks.clone(), quorum_certs.clone())?; - - execution_client - .sync_to(highest_commit_cert.ledger_info().clone()) - .await?; - - // we do not need to update block_tree.highest_commit_decision_ledger_info here - // because the block_tree is going to rebuild itself. - - let recovery_data = match storage.start() { - LivenessStorageData::FullRecoveryData(recovery_data) => recovery_data, - _ => panic!("Failed to construct recovery data after fast forward sync"), - }; - - Ok(recovery_data) - } - - /// Fast forward in the decoupled-execution pipeline if the block exists there - async fn sync_to_highest_commit_cert( - &self, - ledger_info: &LedgerInfoWithSignatures, - network: &Arc, - ) { - // if the block exists between commit root and ordered root - if self.commit_root().round() < ledger_info.commit_info().round() - && self.block_exists(ledger_info.commit_info().id()) - && self.ordered_root().round() >= ledger_info.commit_info().round() - { - network.send_commit_proof(ledger_info.clone()).await - } - } - - /// Retrieve a n chained blocks from the block store starting from - /// an initial parent id, returning with anyhow::Result<()> { - fail_point!("consensus::process_block_retrieval", |_| { - Err(anyhow::anyhow!("Injected error in process_block_retrieval")) - }); - let mut blocks = vec![]; - let mut status = BlockRetrievalStatus::Succeeded; - let mut id = request.req.block_id(); - while (blocks.len() as u64) < request.req.num_blocks() { - if let Some(executed_block) = self.get_block(id) { - blocks.push(executed_block.block().clone()); - if request.req.match_target_id(id) { - status = BlockRetrievalStatus::SucceededWithTarget; - break; - } - id = executed_block.parent_id(); - } else { - status = BlockRetrievalStatus::NotEnoughBlocks; - break; - } - } - - if blocks.is_empty() { - status = BlockRetrievalStatus::IdNotFound; - } - - let response = Box::new(BlockRetrievalResponse::new(status, blocks)); - let response_bytes = request - .protocol - .to_bytes(&ConsensusMsg::BlockRetrievalResponse(response))?; - request - .response_sender - .send(Ok(response_bytes.into())) - .map_err(|_| anyhow::anyhow!("Failed to send block retrieval response")) - } -} - -/// BlockRetriever is used internally to retrieve blocks -pub struct BlockRetriever { - network: Arc, - preferred_peer: Author, - validator_addresses: Vec, - max_blocks_to_request: u64, -} - -impl BlockRetriever { - pub fn new( - network: Arc, - preferred_peer: Author, - validator_addresses: Vec, - max_blocks_to_request: u64, - ) -> Self { - Self { - network, - preferred_peer, - validator_addresses, - max_blocks_to_request, - } - } - - async fn retrieve_block_for_id_chunk( - &mut self, - block_id: HashValue, - target_block_id: HashValue, - retrieve_batch_size: u64, - mut peers: Vec, - ) -> anyhow::Result { - let mut failed_attempt = 0_u32; - let mut cur_retry = 0; - - let num_retries = NUM_RETRIES; - let request_num_peers = NUM_PEERS_PER_RETRY; - let retry_interval = Duration::from_millis(RETRY_INTERVAL_MSEC); - let rpc_timeout = Duration::from_millis(RPC_TIMEOUT_MSEC); - - monitor!("retrieve_block_for_id_chunk", { - let mut interval = time::interval(retry_interval); - let mut futures = FuturesUnordered::new(); - let request = BlockRetrievalRequest::new_with_target_block_id( - block_id, - retrieve_batch_size, - target_block_id, - ); - loop { - tokio::select! { - _ = interval.tick() => { - // send batch request to a set of peers of size request_num_peers (or 1 for the first time) - let next_peers = if cur_retry < num_retries { - let first_atempt = cur_retry == 0; - cur_retry += 1; - self.pick_peers( - first_atempt, - &mut peers, - if first_atempt { 1 } else {request_num_peers} - ) - } else { - Vec::new() - }; - - if next_peers.is_empty() && futures.is_empty() { - bail!("Couldn't fetch block") - } - - for peer in next_peers { - debug!( - LogSchema::new(LogEvent::RetrieveBlock).remote_peer(peer), - block_id = block_id, - "Fetching {} blocks, retry {}, failed attempts {}", - retrieve_batch_size, - cur_retry, - failed_attempt - ); - let remote_peer = peer; - let future = self.network.request_block( - request.clone(), - peer, - rpc_timeout, - ); - futures.push(async move { (remote_peer, future.await) }); - } - } - Some((peer, response)) = futures.next() => { - match response { - Ok(result) => return Ok(result), - e => { - warn!( - remote_peer = peer, - block_id = block_id, - "{:?}, Failed to fetch block", - e, - ); - failed_attempt += 1; - }, - } - }, - } - } - }) - } - - /// Retrieve n blocks for given block_id from peers - /// - /// Returns Result with Vec that if succeeded. This method will - /// continue until the quorum certificate members all fail to return the missing chain. - /// - /// The first attempt of block retrieval will always be sent to preferred_peer to allow the - /// leader to drive quorum certificate creation The other peers from the quorum certificate - /// will be randomly tried next. If all members of the quorum certificate are exhausted, an - /// error is returned - async fn retrieve_block_for_id( - &mut self, - block_id: HashValue, - target_block_id: HashValue, - peers: Vec, - num_blocks: u64, - ) -> anyhow::Result> { - info!( - "Retrieving {} blocks starting from {}", - num_blocks, block_id - ); - let mut progress = 0; - let mut last_block_id = block_id; - let mut result_blocks: Vec = vec![]; - let mut retrieve_batch_size = self.max_blocks_to_request; - if peers.is_empty() { - bail!("Failed to fetch block {}: no peers available", block_id); - } - while progress < num_blocks { - // in case this is the last retrieval - retrieve_batch_size = min(retrieve_batch_size, num_blocks - progress); - - info!( - "Retrieving chunk: {} blocks starting from {}, original start {}", - retrieve_batch_size, last_block_id, block_id - ); - - let response = self - .retrieve_block_for_id_chunk( - last_block_id, - target_block_id, - retrieve_batch_size, - peers.clone(), - ) - .await; - match response { - Ok(result) if matches!(result.status(), BlockRetrievalStatus::Succeeded) => { - // extend the result blocks - let batch = result.blocks().clone(); - progress += batch.len() as u64; - last_block_id = batch.last().unwrap().parent_id(); - result_blocks.extend(batch); - }, - Ok(result) - if matches!(result.status(), BlockRetrievalStatus::SucceededWithTarget) => - { - // if we found the target, end the loop - let batch = result.blocks().clone(); - result_blocks.extend(batch); - break; - }, - _e => { - bail!( - "Failed to fetch block {}, for original start {}", - last_block_id, - block_id, - ); - }, - } - } - assert_eq!(result_blocks.last().unwrap().id(), target_block_id); - Ok(result_blocks) - } - - /// Retrieve chain of n blocks for given QC - async fn retrieve_block_for_qc<'a>( - &'a mut self, - qc: &'a QuorumCert, - num_blocks: u64, - target_block_id: HashValue, - ) -> anyhow::Result> { - let peers = qc.ledger_info().get_voters(&self.validator_addresses); - self.retrieve_block_for_id( - qc.certified_block().id(), - target_block_id, - peers, - num_blocks, - ) - .await - } - - fn pick_peer(&self, first_atempt: bool, peers: &mut Vec) -> AccountAddress { - assert!(!peers.is_empty(), "pick_peer on empty peer list"); - - if first_atempt { - // remove preferred_peer if its in list of peers - // (strictly speaking it is not required to be there) - for i in 0..peers.len() { - if peers[i] == self.preferred_peer { - peers.remove(i); - break; - } - } - return self.preferred_peer; - } - - let peer_idx = thread_rng().gen_range(0, peers.len()); - peers.remove(peer_idx) - } - - fn pick_peers( - &self, - first_atempt: bool, - peers: &mut Vec, - request_num_peers: usize, - ) -> Vec { - let mut result = Vec::new(); - while !peers.is_empty() && result.len() < request_num_peers { - result.push(self.pick_peer(first_atempt && result.is_empty(), peers)); - } - result - } -} diff --git a/consensus/src/block_storage/tracing.rs b/consensus/src/block_storage/tracing.rs deleted file mode 100644 index 302fc7e336877..0000000000000 --- a/consensus/src/block_storage/tracing.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::counters; -use aptos_infallible::duration_since_epoch; -use std::time::Duration; - -pub struct BlockStage; - -impl BlockStage { - pub const COMMITTED: &'static str = "committed"; - pub const COMMIT_CERTIFIED: &'static str = "commit_certified"; - pub const EPOCH_MANAGER_RECEIVED: &'static str = "epoch_manager_received"; - pub const EPOCH_MANAGER_VERIFIED: &'static str = "epoch_manager_verified"; - pub const EXECUTED: &'static str = "executed"; - pub const NETWORK_RECEIVED: &'static str = "network_received"; - pub const ORDERED: &'static str = "ordered"; - pub const QC_ADDED: &'static str = "qc_added"; - pub const QC_AGGREGATED: &'static str = "qc_aggregated"; - pub const RAND_ADD_DECISION: &'static str = "rand_add_decision"; - pub const RAND_ADD_ENOUGH_SHARE: &'static str = "rand_add_enough_share"; - pub const RAND_ENTER: &'static str = "rand_enter"; - pub const RAND_READY: &'static str = "rand_ready"; - pub const ROUND_MANAGER_RECEIVED: &'static str = "round_manager_received"; - pub const SIGNED: &'static str = "signed"; - pub const SYNCED: &'static str = "synced"; - pub const VOTED: &'static str = "voted"; -} - -/// Record the time during each stage of a block. -pub fn observe_block(timestamp: u64, stage: &'static str) { - if let Some(t) = duration_since_epoch().checked_sub(Duration::from_micros(timestamp)) { - counters::BLOCK_TRACING - .with_label_values(&[stage]) - .observe(t.as_secs_f64()); - } -} diff --git a/consensus/src/consensus_provider.rs b/consensus/src/consensus_provider.rs deleted file mode 100644 index 63032c74c0ffb..0000000000000 --- a/consensus/src/consensus_provider.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - epoch_manager::EpochManager, - network::NetworkTask, - network_interface::{ConsensusMsg, ConsensusNetworkClient}, - persistent_liveness_storage::StorageWriteProxy, - pipeline::execution_client::ExecutionProxyClient, - quorum_store::quorum_store_db::QuorumStoreDB, - rand::rand_gen::storage::db::RandDb, - state_computer::ExecutionProxy, - transaction_filter::TransactionFilter, - txn_notifier::MempoolNotifier, - util::time_service::ClockTimeService, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_config::config::NodeConfig; -use aptos_consensus_notifications::ConsensusNotificationSender; -use aptos_event_notifications::{DbBackedOnChainConfig, ReconfigNotificationListener}; -use aptos_executor::block_executor::BlockExecutor; -use aptos_logger::prelude::*; -use aptos_mempool::QuorumStoreRequest; -use aptos_network::application::interface::{NetworkClient, NetworkServiceEvents}; -use aptos_storage_interface::DbReaderWriter; -use aptos_validator_transaction_pool::VTxnPoolState; -use aptos_vm::AptosVM; -use futures::channel::mpsc; -use std::sync::Arc; -use tokio::runtime::Runtime; - -/// Helper function to start consensus based on configuration and return the runtime -pub fn start_consensus( - node_config: &NodeConfig, - network_client: NetworkClient, - network_service_events: NetworkServiceEvents, - state_sync_notifier: Arc, - consensus_to_mempool_sender: mpsc::Sender, - aptos_db: DbReaderWriter, - reconfig_events: ReconfigNotificationListener, - vtxn_pool: VTxnPoolState, -) -> (Runtime, Arc, Arc) { - let runtime = aptos_runtimes::spawn_named_runtime("consensus".into(), None); - let storage = Arc::new(StorageWriteProxy::new(node_config, aptos_db.reader.clone())); - let quorum_store_db = Arc::new(QuorumStoreDB::new(node_config.storage.dir())); - - let txn_notifier = Arc::new(MempoolNotifier::new( - consensus_to_mempool_sender.clone(), - node_config.consensus.mempool_executed_txn_timeout_ms, - )); - - let execution_proxy = ExecutionProxy::new( - Arc::new(BlockExecutor::::new(aptos_db)), - txn_notifier, - state_sync_notifier, - runtime.handle(), - TransactionFilter::new(node_config.execution.transaction_filter.clone()), - ); - - let time_service = Arc::new(ClockTimeService::new(runtime.handle().clone())); - - let (timeout_sender, timeout_receiver) = - aptos_channels::new(1_024, &counters::PENDING_ROUND_TIMEOUTS); - let (self_sender, self_receiver) = - aptos_channels::new_unbounded(&counters::PENDING_SELF_MESSAGES); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - let bounded_executor = BoundedExecutor::new(8, runtime.handle().clone()); - let rand_storage = Arc::new(RandDb::new(node_config.storage.dir())); - - let execution_client = Arc::new(ExecutionProxyClient::new( - node_config.consensus.clone(), - Arc::new(execution_proxy), - node_config.validator_network.as_ref().unwrap().peer_id(), - self_sender.clone(), - consensus_network_client.clone(), - bounded_executor.clone(), - rand_storage.clone(), - )); - - let epoch_mgr = EpochManager::new( - node_config, - time_service, - self_sender, - consensus_network_client, - timeout_sender, - consensus_to_mempool_sender, - execution_client, - storage.clone(), - quorum_store_db.clone(), - reconfig_events, - bounded_executor, - aptos_time_service::TimeService::real(), - vtxn_pool, - rand_storage, - ); - - let (network_task, network_receiver) = NetworkTask::new(network_service_events, self_receiver); - - runtime.spawn(network_task.start()); - runtime.spawn(epoch_mgr.start(timeout_receiver, network_receiver)); - - debug!("Consensus started."); - (runtime, storage, quorum_store_db) -} diff --git a/consensus/src/consensusdb/consensusdb_test.rs b/consensus/src/consensusdb/consensusdb_test.rs deleted file mode 100644 index 13b8fd0711590..0000000000000 --- a/consensus/src/consensusdb/consensusdb_test.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use self::schema::dag::NodeSchema; -use super::*; -use crate::dag::{CertifiedNode, Extensions, Node, Vote}; -use aptos_consensus_types::{ - block::block_test_utils::certificate_for_genesis, - common::{Author, Payload}, -}; -use aptos_crypto::bls12381::Signature; -use aptos_temppath::TempPath; -use aptos_types::aggregate_signature::AggregateSignature; -use std::{collections::HashMap, hash::Hash}; - -#[test] -fn test_put_get() { - let tmp_dir = TempPath::new(); - let db = ConsensusDB::new(&tmp_dir); - - let block = Block::make_genesis_block(); - let blocks = vec![block]; - - assert_eq!(db.get_all::().unwrap().len(), 0); - assert_eq!(db.get_all::().unwrap().len(), 0); - - let qcs = vec![certificate_for_genesis()]; - db.save_blocks_and_quorum_certificates(blocks.clone(), qcs.clone()) - .unwrap(); - - assert_eq!(db.get_all::().unwrap().len(), 1); - assert_eq!(db.get_all::().unwrap().len(), 1); - - let tc = vec![0u8, 1, 2]; - db.save_highest_2chain_timeout_certificate(tc.clone()) - .unwrap(); - - let vote = vec![2u8, 1, 0]; - db.save_vote(vote.clone()).unwrap(); - - let (vote_1, tc_1, blocks_1, qc_1) = db.get_data().unwrap(); - assert_eq!(blocks, blocks_1); - assert_eq!(qcs, qc_1); - assert_eq!(Some(tc), tc_1); - assert_eq!(Some(vote), vote_1); - - db.delete_highest_2chain_timeout_certificate().unwrap(); - db.delete_last_vote_msg().unwrap(); - assert!(db - .get_highest_2chain_timeout_certificate() - .unwrap() - .is_none()); - assert!(db.get_last_vote().unwrap().is_none()); -} - -#[test] -fn test_delete_block_and_qc() { - let tmp_dir = TempPath::new(); - let db = ConsensusDB::new(&tmp_dir); - - assert_eq!(db.get_all::().unwrap().len(), 0); - assert_eq!(db.get_all::().unwrap().len(), 0); - - let blocks = vec![Block::make_genesis_block()]; - let block_id = blocks[0].id(); - - let qcs = vec![certificate_for_genesis()]; - let qc_id = qcs[0].certified_block().id(); - - db.save_blocks_and_quorum_certificates(blocks, qcs).unwrap(); - assert_eq!(db.get_all::().unwrap().len(), 1); - assert_eq!(db.get_all::().unwrap().len(), 1); - - // Start to delete - db.delete_blocks_and_quorum_certificates(vec![block_id, qc_id]) - .unwrap(); - assert_eq!(db.get_all::().unwrap().len(), 0); - assert_eq!(db.get_all::().unwrap().len(), 0); -} - -fn test_dag_type, K: Eq + Hash>(key: S::Key, value: S::Value, db: &ConsensusDB) { - db.put::(&key, &value).unwrap(); - let mut from_db: HashMap = db.get_all::().unwrap().into_iter().collect(); - assert_eq!(from_db.len(), 1); - let value_from_db = from_db.remove(&key).unwrap(); - assert_eq!(value, value_from_db); - db.delete::(vec![key]).unwrap(); - assert_eq!(db.get_all::().unwrap().len(), 0); -} - -#[test] -fn test_dag() { - let tmp_dir = TempPath::new(); - let db = ConsensusDB::new(&tmp_dir); - - let node = Node::new( - 1, - 1, - Author::random(), - 123, - vec![], - Payload::empty(false, true), - vec![], - Extensions::empty(), - ); - test_dag_type::::Key>((), node.clone(), &db); - - let certified_node = CertifiedNode::new(node.clone(), AggregateSignature::empty()); - test_dag_type::::Key>( - certified_node.digest(), - certified_node, - &db, - ); - - let vote = Vote::new(node.metadata().clone(), Signature::dummy_signature()); - test_dag_type::::Key>(node.id(), vote, &db); -} diff --git a/consensus/src/consensusdb/mod.rs b/consensus/src/consensusdb/mod.rs deleted file mode 100644 index 5f5947adb8322..0000000000000 --- a/consensus/src/consensusdb/mod.rs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -mod consensusdb_test; -mod schema; - -use crate::error::DbError; -use anyhow::Result; -use aptos_consensus_types::{block::Block, quorum_cert::QuorumCert}; -use aptos_crypto::HashValue; -use aptos_logger::prelude::*; -use aptos_schemadb::{ - schema::Schema, Options, ReadOptions, SchemaBatch, DB, DEFAULT_COLUMN_FAMILY_NAME, -}; -use aptos_storage_interface::AptosDbError; -pub use schema::{ - block::BlockSchema, - dag::{CertifiedNodeSchema, DagVoteSchema, NodeSchema}, - quorum_certificate::QCSchema, -}; -use schema::{ - single_entry::{SingleEntryKey, SingleEntrySchema}, - BLOCK_CF_NAME, CERTIFIED_NODE_CF_NAME, DAG_VOTE_CF_NAME, NODE_CF_NAME, QC_CF_NAME, - SINGLE_ENTRY_CF_NAME, -}; -use std::{iter::Iterator, path::Path, time::Instant}; - -/// The name of the consensus db file -pub const CONSENSUS_DB_NAME: &str = "consensus_db"; - -/// Creates new physical DB checkpoint in directory specified by `checkpoint_path`. -pub fn create_checkpoint + Clone>(db_path: P, checkpoint_path: P) -> Result<()> { - let start = Instant::now(); - let consensus_db_checkpoint_path = checkpoint_path.as_ref().join(CONSENSUS_DB_NAME); - std::fs::remove_dir_all(&consensus_db_checkpoint_path).unwrap_or(()); - ConsensusDB::new(db_path) - .db - .create_checkpoint(&consensus_db_checkpoint_path)?; - info!( - path = consensus_db_checkpoint_path, - time_ms = %start.elapsed().as_millis(), - "Made ConsensusDB checkpoint." - ); - Ok(()) -} - -pub struct ConsensusDB { - db: DB, -} - -impl ConsensusDB { - pub fn new + Clone>(db_root_path: P) -> Self { - let column_families = vec![ - /* UNUSED CF = */ DEFAULT_COLUMN_FAMILY_NAME, - BLOCK_CF_NAME, - QC_CF_NAME, - SINGLE_ENTRY_CF_NAME, - NODE_CF_NAME, - CERTIFIED_NODE_CF_NAME, - DAG_VOTE_CF_NAME, - "ordered_anchor_id", // deprecated CF - ]; - - let path = db_root_path.as_ref().join(CONSENSUS_DB_NAME); - let instant = Instant::now(); - let mut opts = Options::default(); - opts.create_if_missing(true); - opts.create_missing_column_families(true); - let db = DB::open(path.clone(), "consensus", column_families, &opts) - .expect("ConsensusDB open failed; unable to continue"); - - info!( - "Opened ConsensusDB at {:?} in {} ms", - path, - instant.elapsed().as_millis() - ); - - Self { db } - } - - pub fn get_data( - &self, - ) -> Result<( - Option>, - Option>, - Vec, - Vec, - )> { - let last_vote = self.get_last_vote()?; - let highest_2chain_timeout_certificate = self.get_highest_2chain_timeout_certificate()?; - let consensus_blocks = self - .get_all::()? - .into_iter() - .map(|(_, block)| block) - .collect(); - let consensus_qcs = self - .get_all::()? - .into_iter() - .map(|(_, qc)| qc) - .collect(); - Ok(( - last_vote, - highest_2chain_timeout_certificate, - consensus_blocks, - consensus_qcs, - )) - } - - pub fn save_highest_2chain_timeout_certificate(&self, tc: Vec) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.put::(&SingleEntryKey::Highest2ChainTimeoutCert, &tc)?; - self.commit(batch)?; - Ok(()) - } - - pub fn save_vote(&self, last_vote: Vec) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.put::(&SingleEntryKey::LastVote, &last_vote)?; - self.commit(batch) - } - - pub fn save_blocks_and_quorum_certificates( - &self, - block_data: Vec, - qc_data: Vec, - ) -> Result<(), DbError> { - if block_data.is_empty() && qc_data.is_empty() { - return Err(anyhow::anyhow!("Consensus block and qc data is empty!").into()); - } - let batch = SchemaBatch::new(); - block_data - .iter() - .try_for_each(|block| batch.put::(&block.id(), block))?; - qc_data - .iter() - .try_for_each(|qc| batch.put::(&qc.certified_block().id(), qc))?; - self.commit(batch) - } - - pub fn delete_blocks_and_quorum_certificates( - &self, - block_ids: Vec, - ) -> Result<(), DbError> { - if block_ids.is_empty() { - return Err(anyhow::anyhow!("Consensus block ids is empty!").into()); - } - let batch = SchemaBatch::new(); - block_ids.iter().try_for_each(|hash| { - batch.delete::(hash)?; - batch.delete::(hash) - })?; - self.commit(batch) - } - - /// Write the whole schema batch including all data necessary to mutate the ledger - /// state of some transaction by leveraging rocksdb atomicity support. - fn commit(&self, batch: SchemaBatch) -> Result<(), DbError> { - self.db.write_schemas(batch)?; - Ok(()) - } - - /// Get latest timeout certificates (we only store the latest highest timeout certificates). - fn get_highest_2chain_timeout_certificate(&self) -> Result>, DbError> { - Ok(self - .db - .get::(&SingleEntryKey::Highest2ChainTimeoutCert)?) - } - - pub fn delete_highest_2chain_timeout_certificate(&self) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.delete::(&SingleEntryKey::Highest2ChainTimeoutCert)?; - self.commit(batch) - } - - /// Get serialized latest vote (if available) - fn get_last_vote(&self) -> Result>, DbError> { - Ok(self - .db - .get::(&SingleEntryKey::LastVote)?) - } - - pub fn delete_last_vote_msg(&self) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.delete::(&SingleEntryKey::LastVote)?; - self.commit(batch)?; - Ok(()) - } - - pub fn put(&self, key: &S::Key, value: &S::Value) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.put::(key, value)?; - self.commit(batch)?; - Ok(()) - } - - pub fn delete(&self, keys: Vec) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - keys.iter().try_for_each(|key| batch.delete::(key))?; - self.commit(batch) - } - - pub fn get_all(&self) -> Result, DbError> { - let mut iter = self.db.iter::(ReadOptions::default())?; - iter.seek_to_first(); - Ok(iter.collect::, AptosDbError>>()?) - } - - pub fn get(&self, key: &S::Key) -> Result, DbError> { - Ok(self.db.get::(key)?) - } -} diff --git a/consensus/src/consensusdb/schema/block/mod.rs b/consensus/src/consensusdb/schema/block/mod.rs deleted file mode 100644 index 5ac486a9ebdde..0000000000000 --- a/consensus/src/consensusdb/schema/block/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! This module defines physical storage schema for consensus block. -//! -//! Serialized block bytes identified by block_hash. -//! ```text -//! |<---key---->|<---value--->| -//! | block_hash | block | -//! ``` - -use crate::define_schema; -use anyhow::Result; -use aptos_consensus_types::block::Block; -use aptos_crypto::HashValue; -use aptos_schemadb::{ - schema::{KeyCodec, ValueCodec}, - ColumnFamilyName, -}; - -pub const BLOCK_CF_NAME: ColumnFamilyName = "block"; - -define_schema!(BlockSchema, HashValue, Block, BLOCK_CF_NAME); - -impl KeyCodec for HashValue { - fn encode_key(&self) -> Result> { - Ok(self.to_vec()) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(HashValue::from_slice(data)?) - } -} - -impl ValueCodec for Block { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -#[cfg(test)] -mod test; diff --git a/consensus/src/consensusdb/schema/block/test.rs b/consensus/src/consensusdb/schema/block/test.rs deleted file mode 100644 index e2c03259a5a0e..0000000000000 --- a/consensus/src/consensusdb/schema/block/test.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::*; -use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; - -#[test] -fn test_encode_decode() { - let block = Block::make_genesis_block(); - assert_encode_decode::(&block.id(), &block); -} - -test_no_panic_decoding!(BlockSchema); diff --git a/consensus/src/consensusdb/schema/dag/mod.rs b/consensus/src/consensusdb/schema/dag/mod.rs deleted file mode 100644 index 22b7ac69f42dc..0000000000000 --- a/consensus/src/consensusdb/schema/dag/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -//! This module defines physical storage schemas for DAG. -//! -//! ``` - -use crate::{ - consensusdb::schema::ensure_slice_len_eq, - dag::{CertifiedNode, Node, NodeId, Vote}, - define_schema, -}; -use anyhow::Result; -use aptos_crypto::HashValue; -use aptos_schemadb::{ - schema::{KeyCodec, ValueCodec}, - ColumnFamilyName, -}; -use std::mem::size_of; - -pub const NODE_CF_NAME: ColumnFamilyName = "node"; - -define_schema!(NodeSchema, (), Node, NODE_CF_NAME); - -impl KeyCodec for () { - fn encode_key(&self) -> Result> { - Ok(vec![]) - } - - fn decode_key(data: &[u8]) -> Result { - ensure_slice_len_eq(data, size_of::())?; - Ok(()) - } -} - -impl ValueCodec for Node { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -pub const DAG_VOTE_CF_NAME: ColumnFamilyName = "dag_vote"; - -define_schema!(DagVoteSchema, NodeId, Vote, DAG_VOTE_CF_NAME); - -impl KeyCodec for NodeId { - fn encode_key(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -impl ValueCodec for Vote { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -pub const CERTIFIED_NODE_CF_NAME: ColumnFamilyName = "certified_node"; - -define_schema!( - CertifiedNodeSchema, - HashValue, - CertifiedNode, - CERTIFIED_NODE_CF_NAME -); - -impl KeyCodec for HashValue { - fn encode_key(&self) -> Result> { - Ok(self.to_vec()) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(HashValue::from_slice(data)?) - } -} - -impl ValueCodec for CertifiedNode { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} diff --git a/consensus/src/consensusdb/schema/mod.rs b/consensus/src/consensusdb/schema/mod.rs deleted file mode 100644 index 3b2b511de9ab8..0000000000000 --- a/consensus/src/consensusdb/schema/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub(crate) mod block; -pub(crate) mod dag; -pub(crate) mod quorum_certificate; -pub(crate) mod single_entry; - -use anyhow::{ensure, Result}; - -pub(crate) fn ensure_slice_len_eq(data: &[u8], len: usize) -> Result<()> { - ensure!( - data.len() == len, - "Unexpected data len {}, expected {}.", - data.len(), - len, - ); - Ok(()) -} - -/// Copied from aptos-schemdadb to define pub struct instead of pub(crate) -#[macro_export] -macro_rules! define_schema { - ($schema_type:ident, $key_type:ty, $value_type:ty, $cf_name:expr) => { - #[derive(Debug)] - pub struct $schema_type; - - impl aptos_schemadb::schema::Schema for $schema_type { - type Key = $key_type; - type Value = $value_type; - - const COLUMN_FAMILY_NAME: ColumnFamilyName = $cf_name; - } - }; -} - -pub use block::BLOCK_CF_NAME; -pub use dag::{CERTIFIED_NODE_CF_NAME, DAG_VOTE_CF_NAME, NODE_CF_NAME}; -pub use quorum_certificate::QC_CF_NAME; -pub use single_entry::SINGLE_ENTRY_CF_NAME; diff --git a/consensus/src/consensusdb/schema/quorum_certificate/mod.rs b/consensus/src/consensusdb/schema/quorum_certificate/mod.rs deleted file mode 100644 index 63416c43daf6d..0000000000000 --- a/consensus/src/consensusdb/schema/quorum_certificate/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! This module defines physical storage schema for consensus quorum certificate (of a block). -//! -//! Serialized quorum certificate bytes identified by block_hash. -//! ```text -//! |<---key---->|<----value--->| -//! | block_hash | QuorumCert | -//! ``` - -use crate::define_schema; -use anyhow::Result; -use aptos_consensus_types::quorum_cert::QuorumCert; -use aptos_crypto::HashValue; -use aptos_schemadb::{ - schema::{KeyCodec, ValueCodec}, - ColumnFamilyName, -}; - -pub const QC_CF_NAME: ColumnFamilyName = "quorum_certificate"; - -define_schema!(QCSchema, HashValue, QuorumCert, QC_CF_NAME); - -impl KeyCodec for HashValue { - fn encode_key(&self) -> Result> { - Ok(self.to_vec()) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(HashValue::from_slice(data)?) - } -} - -impl ValueCodec for QuorumCert { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -#[cfg(test)] -mod test; diff --git a/consensus/src/consensusdb/schema/quorum_certificate/test.rs b/consensus/src/consensusdb/schema/quorum_certificate/test.rs deleted file mode 100644 index 4b2cf5423dcb4..0000000000000 --- a/consensus/src/consensusdb/schema/quorum_certificate/test.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::*; -use aptos_consensus_types::block::block_test_utils::certificate_for_genesis; -use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; - -#[test] -fn test_encode_decode() { - let qc = certificate_for_genesis(); - assert_encode_decode::(&qc.certified_block().id(), &qc); -} - -test_no_panic_decoding!(QCSchema); diff --git a/consensus/src/consensusdb/schema/single_entry/mod.rs b/consensus/src/consensusdb/schema/single_entry/mod.rs deleted file mode 100644 index 6a66609cf9fa4..0000000000000 --- a/consensus/src/consensusdb/schema/single_entry/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! This module defines physical storage schema for any single-entry data. -//! -//! There will be only one row in this column family for each type of data. -//! The key will be a serialized enum type designating the data type and should not have any meaning -//! and be used. -//! -//! ```text -//! |<-------key------->|<-----value----->| -//! | single entry key | raw value bytes | -//! ``` - -use super::ensure_slice_len_eq; -use crate::define_schema; -use anyhow::{format_err, Result}; -use aptos_schemadb::{ - schema::{KeyCodec, ValueCodec}, - ColumnFamilyName, -}; -use byteorder::ReadBytesExt; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive, ToPrimitive}; -use std::mem::size_of; - -pub const SINGLE_ENTRY_CF_NAME: ColumnFamilyName = "single_entry"; - -define_schema!( - SingleEntrySchema, - SingleEntryKey, - Vec, - SINGLE_ENTRY_CF_NAME -); - -#[derive(Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)] -#[repr(u8)] -pub enum SingleEntryKey { - // Used to store the last vote - LastVote = 0, - // Two chain timeout cert - Highest2ChainTimeoutCert = 1, -} - -impl KeyCodec for SingleEntryKey { - fn encode_key(&self) -> Result> { - Ok(vec![self - .to_u8() - .ok_or_else(|| format_err!("ToPrimitive failed."))?]) - } - - fn decode_key(mut data: &[u8]) -> Result { - ensure_slice_len_eq(data, size_of::())?; - let key = data.read_u8()?; - SingleEntryKey::from_u8(key).ok_or_else(|| format_err!("FromPrimitive failed.")) - } -} - -impl ValueCodec for Vec { - fn encode_value(&self) -> Result> { - Ok(self.clone()) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(data.to_vec()) - } -} - -#[cfg(test)] -mod test; diff --git a/consensus/src/consensusdb/schema/single_entry/test.rs b/consensus/src/consensusdb/schema/single_entry/test.rs deleted file mode 100644 index 0fdc37a571b76..0000000000000 --- a/consensus/src/consensusdb/schema/single_entry/test.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::*; -use aptos_schemadb::{schema::fuzzing::assert_encode_decode, test_no_panic_decoding}; - -// Tests that the DB can encode / decode data -#[test] -fn test_single_entry_schema() { - assert_encode_decode::(&SingleEntryKey::LastVote, &vec![1u8, 2u8, 3u8]); -} - -test_no_panic_decoding!(SingleEntrySchema); diff --git a/consensus/src/counters.rs b/consensus/src/counters.rs deleted file mode 100644 index dadbe0219a07b..0000000000000 --- a/consensus/src/counters.rs +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - quorum_store, -}; -use aptos_consensus_types::pipelined_block::PipelinedBlock; -use aptos_metrics_core::{ - exponential_buckets, op_counters::DurationHistogram, register_avg_counter, register_counter, - register_gauge, register_gauge_vec, register_histogram, register_histogram_vec, - register_int_counter, register_int_counter_vec, register_int_gauge, register_int_gauge_vec, - Counter, Gauge, GaugeVec, Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, - IntGaugeVec, -}; -use aptos_types::transaction::TransactionStatus; -use move_core_types::vm_status::DiscardedVMStatus; -use once_cell::sync::Lazy; -use std::sync::Arc; - -/// Transaction commit was successful -pub const TXN_COMMIT_SUCCESS_LABEL: &str = "success"; -/// Transaction commit failed (will not be retried) -pub const TXN_COMMIT_FAILED_LABEL: &str = "failed"; -/// Transaction commit failed (will not be retried) because of a duplicate -pub const TXN_COMMIT_FAILED_DUPLICATE_LABEL: &str = "failed_duplicate"; -/// Transaction commit was unsuccessful, but will be retried -pub const TXN_COMMIT_RETRY_LABEL: &str = "retry"; - -////////////////////// -// HEALTH COUNTERS -////////////////////// - -/// Monitor counters, used by monitor! macro -pub static OP_COUNTERS: Lazy = - Lazy::new(|| aptos_metrics_core::op_counters::OpMetrics::new_and_registered("consensus")); - -/// Counts the total number of errors -pub static ERROR_COUNT: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_error_count", - "Total number of errors in main loop" - ) - .unwrap() -}); - -/// This counter is set to the round of the highest committed block. -pub static LAST_COMMITTED_ROUND: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_last_committed_round", - "This counter is set to the round of the highest committed block." - ) - .unwrap() -}); - -/// The counter corresponds to the version of the last committed ledger info. -pub static LAST_COMMITTED_VERSION: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_last_committed_version", - "The counter corresponds to the version of the last committed ledger info." - ) - .unwrap() -}); - -/// Count of the committed failed rounds since last restart. -pub static COMMITTED_FAILED_ROUNDS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_committed_failed_rounds_count", - "Count of the committed failed rounds since last restart." - ) - .unwrap() -}); - -/// Count of the committed blocks since last restart. -pub static COMMITTED_BLOCKS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_committed_blocks_count", - "Count of the committed blocks since last restart." - ) - .unwrap() -}); - -/// Count of the committed transactions since last restart. -pub static COMMITTED_TXNS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_committed_txns_count", - "Count of the transactions since last restart. state is success or failed", - &["state"] - ) - .unwrap() -}); - -////////////////////// -// PROPOSAL ELECTION -////////////////////// - -/// Count of the block proposals sent by this validator since last restart -/// (both primary and secondary) -pub static PROPOSALS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!("aptos_consensus_proposals_count", "Count of the block proposals sent by this validator since last restart (both primary and secondary)").unwrap() -}); - -/// Count the number of times a validator voted for a nil block since last restart. -pub static VOTE_NIL_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_vote_nil_count", - "Count the number of times a validator voted for a nil block since last restart." - ) - .unwrap() -}); - -/// Total voting power of validators in validator set -pub static TOTAL_VOTING_POWER: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_total_voting_power", - "Total voting power of validators in validator set" - ) - .unwrap() -}); - -/// Number of distinct senders in a block -pub static NUM_SENDERS_IN_BLOCK: Lazy = Lazy::new(|| { - register_gauge!("num_senders_in_block", "Total number of senders in a block").unwrap() -}); - -/// Transaction shuffling call latency -pub static TXN_SHUFFLE_SECONDS: Lazy = Lazy::new(|| { - register_histogram!( - // metric name - "aptos_execution_transaction_shuffle_seconds", - // metric description - "The time spent in seconds in shuffle of transactions", - exponential_buckets(/*start=*/ 1e-6, /*factor=*/ 2.0, /*count=*/ 30).unwrap(), - ) - .unwrap() -}); - -/// Transaction dedup call latency -pub static TXN_DEDUP_SECONDS: Lazy = Lazy::new(|| { - register_histogram!( - // metric name - "aptos_execution_transaction_dedup_seconds", - // metric description - "The time spent in seconds in dedup of transaction", - exponential_buckets(/*start=*/ 1e-6, /*factor=*/ 2.0, /*count=*/ 30).unwrap(), - ) - .unwrap() -}); - -/// Transaction dedup number of filtered -pub static TXN_DEDUP_FILTERED: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_execution_transaction_dedup_filtered", - "The number of duplicates filtered per block", - ) -}); - -/// Number of rounds we were collecting votes for proposer -/// (similar to PROPOSALS_COUNT, but can be larger, if we failed in creating/sending of the proposal) -pub static PROPOSER_COLLECTED_ROUND_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_proposer_collecting_round_count", - "Total voting power of all votes collected for the round this node was proposer", - ) - .unwrap() -}); - -/// Total voting power of all votes collected for the same ledger info -/// for the rounds this node was a proposer (cumulative) -pub static PROPOSER_COLLECTED_MOST_VOTING_POWER: Lazy = Lazy::new(|| { - register_counter!( - "aptos_proposer_collected_most_voting_power_sum", - "Total voting power of all votes collected for the same ledger info for the rounds this node was a proposer", - ) - .unwrap() -}); - -/// Total voting power of all votes collected for all other ledger info -/// for the rounds this node was a proposer -pub static PROPOSER_COLLECTED_CONFLICTING_VOTING_POWER: Lazy = Lazy::new(|| { - register_counter!( - "aptos_proposer_collected_conflicting_voting_power_sum", - "Total voting power of all votes collected for all other ledger info for the rounds this node was a proposer", - ) - .unwrap() -}); - -/// Total voting power of all votes collected for all other ledger info -/// for the rounds this node was a proposer -pub static PROPOSER_COLLECTED_TIMEOUT_VOTING_POWER: Lazy = Lazy::new(|| { - register_counter!( - "aptos_proposer_collected_timeout_voting_power_sum", - "Total voting power of all votes collected for the same ledger info for the rounds this node was a proposer", - ) - .unwrap() -}); - -/// Committed proposals map when using LeaderReputation as the ProposerElection -pub static COMMITTED_PROPOSALS_IN_WINDOW: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_committed_proposals_in_window", - "Total number committed proposals in the current reputation window", - ) - .unwrap() -}); - -/// Failed proposals map when using LeaderReputation as the ProposerElection -pub static FAILED_PROPOSALS_IN_WINDOW: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_failed_proposals_in_window", - "Total number of failed proposals in the current reputation window", - ) - .unwrap() -}); - -/// Committed votes map when using LeaderReputation as the ProposerElection -pub static COMMITTED_VOTES_IN_WINDOW: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_committed_votes_in_window", - "Total number of committed votes in the current reputation window", - ) - .unwrap() -}); - -/// The number of block events the LeaderReputation uses -pub static LEADER_REPUTATION_ROUND_HISTORY_SIZE: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_leader_reputation_round_history_size", - "Total number of new block events in the current reputation window" - ) - .unwrap() -}); - -/// Counts when chain_health backoff is triggered -pub static CONSENSUS_WITHOLD_VOTE_BACKPRESSURE_TRIGGERED: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_consensus_withold_vote_backpressure_triggered", - "Counts when consensus vote_backpressure is triggered", - ) -}); - -/// Counts when chain_health backoff is triggered -pub static CHAIN_HEALTH_BACKOFF_TRIGGERED: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_chain_health_backoff_triggered", - "Counts when chain_health backoff is triggered", - ) -}); - -/// Counts when waiting for full blocks is triggered -pub static WAIT_FOR_FULL_BLOCKS_TRIGGERED: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_wait_for_full_blocks_triggered", - "Counts when waiting for full blocks is triggered", - ) -}); - -/// Counts when chain_health backoff is triggered -pub static PIPELINE_BACKPRESSURE_ON_PROPOSAL_TRIGGERED: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_pipeline_backpressure_on_proposal_triggered", - "Counts when chain_health backoff is triggered", - ) -}); - -/// number of rounds pending when creating proposal -pub static CONSENSUS_PROPOSAL_PENDING_ROUNDS: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_consensus_proposal_pending_rounds", - "number of rounds pending when creating proposal", - ) -}); - -/// duration pending when creating proposal -pub static CONSENSUS_PROPOSAL_PENDING_DURATION: Lazy = Lazy::new(|| { - register_avg_counter( - "aptos_consensus_proposal_pending_duration", - "duration pending when creating proposal", - ) -}); - -/// Amount of time (in seconds) proposal is delayed due to backpressure/backoff -pub static PROPOSER_DELAY_PROPOSAL: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_proposer_delay_proposal", - "Amount of time (in seconds) proposal is delayed due to backpressure/backoff", - ) - .unwrap() -}); - -/// How many pending blocks are there, when we make a proposal -pub static PROPOSER_PENDING_BLOCKS_COUNT: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_proposer_pending_blocks_count", - "How many pending blocks are there, when we make a proposal", - ) - .unwrap() -}); - -/// How full is a largest pending block, as a fraction of max len/bytes (between 0 and 1) -pub static PROPOSER_PENDING_BLOCKS_FILL_FRACTION: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_proposer_pending_blocks_fill_fraction", - "How full is a largest recent pending block, as a fraction of max len/bytes (between 0 and 1)", - ) - .unwrap() -}); - -/// Next set of counters are computed at leader election time, with some delay. - -/// Current voting power fraction that participated in consensus -/// (voted or proposed) in the reputation window, used for chain-health -/// based backoff -pub static CHAIN_HEALTH_REPUTATION_PARTICIPATING_VOTING_POWER_FRACTION: Lazy = - Lazy::new(|| { - register_gauge!( - "aptos_chain_health_participating_voting_power_fraction_last_reputation_rounds", - "Total voting power of validators in validator set" - ) - .unwrap() - }); - -/// Window sizes for which to measure chain health. -pub static CHAIN_HEALTH_WINDOW_SIZES: [usize; 4] = [10, 30, 100, 300]; - -/// Current (with some delay) total voting power -pub static CHAIN_HEALTH_TOTAL_VOTING_POWER: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_chain_health_total_voting_power", - "Total voting power of validators in validator set" - ) - .unwrap() -}); - -/// Current (with some delay) total number of validators -pub static CHAIN_HEALTH_TOTAL_NUM_VALIDATORS: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_chain_health_total_num_validators", - "Total number of validators in validator set" - ) - .unwrap() -}); - -/// Current (with some delay) voting power that participated in consensus -/// (voted or proposed) in the given window. -pub static CHAIN_HEALTH_PARTICIPATING_VOTING_POWER: Lazy> = Lazy::new(|| { - CHAIN_HEALTH_WINDOW_SIZES - .iter() - .map(|i| { - register_gauge!( - format!( - "aptos_chain_health_participating_voting_power_last_{}_rounds", - i - ), - "Current (with some delay) voting power that participated in consensus (voted or proposed) in the given window." - ) - .unwrap() - }) - .collect() -}); - -/// Current (with some delay) number of validators that participated in consensus -/// (voted or proposed) in the given window. -pub static CHAIN_HEALTH_PARTICIPATING_NUM_VALIDATORS: Lazy> = Lazy::new(|| { - CHAIN_HEALTH_WINDOW_SIZES - .iter() - .map(|i| { - register_int_gauge!( - format!( - "aptos_chain_health_participating_num_validators_last_{}_rounds", - i - ), - "Current (with some delay) number of validators that participated in consensus (voted or proposed) in the given window." - ) - .unwrap() - }) - .collect() -}); - -/// Emits consensus participation status for all peers, 0 means no participation in the window -/// 1 otherwise. -pub static CONSENSUS_PARTICIPATION_STATUS: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_participation_status", - "Counter for consensus participation status, 0 means no participation and 1 otherwise", - &["peer_id"] - ) - .unwrap() -}); - -/// Voting power of the validator -pub static VALIDATOR_VOTING_POWER: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_validator_voting_power", - "Voting power of the validator" - ) - .unwrap() -}); - -/// Emits voting power for all validators in the current epoch. -pub static ALL_VALIDATORS_VOTING_POWER: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_all_validators_voting_power", - "Voting power for all validators in current epoch", - &["peer_id"] - ) - .unwrap() -}); - -/// For the current ordering round, voting power needed for quorum. -pub static CONSENSUS_CURRENT_ROUND_QUORUM_VOTING_POWER: Lazy = Lazy::new(|| { - register_gauge!( - "aptos_consensus_current_round_quorum_voting_power", - "Counter for consensus participation status, 0 means no participation and 1 otherwise", - ) - .unwrap() -}); - -/// For the current ordering round, for each peer, whether they have voted, and for which hash_index -pub static CONSENSUS_CURRENT_ROUND_VOTED_POWER: Lazy = Lazy::new(|| { - register_gauge_vec!( - "aptos_consensus_current_round_voted_power", - "Counter for consensus participation status, 0 means no participation and 1 otherwise", - &["peer_id", "hash_index"] - ) - .unwrap() -}); - -/// For the current ordering round, for each peer, whether they have voted for a timeout -pub static CONSENSUS_CURRENT_ROUND_TIMEOUT_VOTED_POWER: Lazy = Lazy::new(|| { - register_gauge_vec!( - "aptos_consensus_current_round_timeout_voted_power", - "Counter for consensus participation status, 0 means no participation and 1 otherwise", - &["peer_id"] - ) - .unwrap() -}); - -/// Last vote seen for each of the peers -pub static CONSENSUS_LAST_VOTE_EPOCH: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_last_voted_epoch", - "for each peer_id, last epoch we've seen consensus vote", - &["peer_id"] - ) - .unwrap() -}); - -/// Last vote seen for each of the peers -pub static CONSENSUS_LAST_VOTE_ROUND: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_last_voted_round", - "for each peer_id, last round we've seen consensus vote", - &["peer_id"] - ) - .unwrap() -}); - -/// Last timeout vote seen for each of the peers -pub static CONSENSUS_LAST_TIMEOUT_VOTE_EPOCH: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_last_timeout_voted_epoch", - "for each peer_id, last epoch we've seen consensus timeout vote", - &["peer_id"] - ) - .unwrap() -}); - -/// Last timeout vote seen for each of the peers -pub static CONSENSUS_LAST_TIMEOUT_VOTE_ROUND: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_last_timeout_voted_round", - "for each peer_id, last round we've seen consensus timeout vote", - &["peer_id"] - ) - .unwrap() -}); - -////////////////////// -// RoundState COUNTERS -////////////////////// -/// This counter is set to the last round reported by the local round_state. -pub static CURRENT_ROUND: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_current_round", - "This counter is set to the last round reported by the local round_state." - ) - .unwrap() -}); - -/// Count of the rounds that gathered QC since last restart. -pub static QC_ROUNDS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_qc_rounds_count", - "Count of the rounds that gathered QC since last restart." - ) - .unwrap() -}); - -/// Count of the timeout rounds since last restart (close to 0 in happy path). -pub static TIMEOUT_ROUNDS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_timeout_rounds_count", - "Count of the timeout rounds since last restart (close to 0 in happy path)." - ) - .unwrap() -}); - -/// Count the number of timeouts a node experienced since last restart (close to 0 in happy path). -/// This count is different from `TIMEOUT_ROUNDS_COUNT`, because not every time a node has -/// a timeout there is an ultimate decision to move to the next round (it might take multiple -/// timeouts to get the timeout certificate). -pub static TIMEOUT_COUNT: Lazy = Lazy::new(|| { - register_int_counter!("aptos_consensus_timeout_count", "Count the number of timeouts a node experienced since last restart (close to 0 in happy path).").unwrap() -}); - -/// The timeout of the current round. -pub static ROUND_TIMEOUT_MS: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_round_timeout_s", - "The timeout of the current round." - ) - .unwrap() -}); - -//////////////////////// -// SYNC MANAGER COUNTERS -//////////////////////// -/// Counts the number of times the sync info message has been set since last restart. -pub static SYNC_INFO_MSGS_SENT_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_sync_info_msg_sent_count", - "Counts the number of times the sync info message has been set since last restart." - ) - .unwrap() -}); - -////////////////////// -// RECONFIGURATION COUNTERS -////////////////////// -/// Current epoch num -pub static EPOCH: Lazy = - Lazy::new(|| register_int_gauge!("aptos_consensus_epoch", "Current epoch num").unwrap()); - -/// The number of validators in the current epoch -pub static CURRENT_EPOCH_VALIDATORS: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_current_epoch_validators", - "The number of validators in the current epoch" - ) - .unwrap() -}); - -////////////////////// -// BLOCK STORE COUNTERS -////////////////////// -/// Counter for the number of blocks in the block tree (including the root). -/// In a "happy path" with no collisions and timeouts, should be equal to 3 or 4. -pub static NUM_BLOCKS_IN_TREE: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_num_blocks_in_tree", - "Counter for the number of blocks in the block tree (including the root)." - ) - .unwrap() -}); - -/// Counter for the number of blocks in the pipeline broken down by stage. -pub static NUM_BLOCKS_IN_PIPELINE: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "aptos_consensus_num_blocks_in_pipeline", - "Counter for the number of blocks in the pipeline", - &["stage"] - ) - .unwrap() -}); - -////////////////////// -// PERFORMANCE COUNTERS -////////////////////// -// TODO Consider reintroducing this counter -// pub static UNWRAPPED_PROPOSAL_SIZE_BYTES: Lazy = Lazy::new(|| { -// register_histogram!( -// "aptos_consensus_unwrapped_proposal_size_bytes", -// "Histogram of proposal size after BCS but before wrapping with GRPC and aptos net." -// ) -// .unwrap() -// }); - -const NUM_CONSENSUS_TRANSACTIONS_BUCKETS: [f64; 24] = [ - 5.0, 10.0, 20.0, 40.0, 75.0, 100.0, 200.0, 400.0, 800.0, 1200.0, 1800.0, 2500.0, 3300.0, - 4000.0, 5000.0, 6500.0, 8000.0, 10000.0, 12500.0, 15000.0, 18000.0, 21000.0, 25000.0, 30000.0, -]; - -/// Histogram for the number of txns per (committed) blocks. -pub static NUM_TXNS_PER_BLOCK: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_num_txns_per_block", - "Histogram for the number of txns per (committed) blocks.", - NUM_CONSENSUS_TRANSACTIONS_BUCKETS.to_vec() - ) - .unwrap() -}); - -// Histogram buckets that expand DEFAULT_BUCKETS with more granularity: -// * 0.3 to 2.0: step 0.1 -// * 2.0 to 4.0: step 0.2 -// * 4.0 to 7.5: step 0.5 -const BLOCK_TRACING_BUCKETS: &[f64] = &[ - 0.005, 0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, - 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, - 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 10.0, -]; - -/// Traces block movement throughout the node -pub static BLOCK_TRACING: Lazy = Lazy::new(|| { - register_histogram_vec!( - "aptos_consensus_block_tracing", - "Histogram for different stages of a block", - &["stage"], - BLOCK_TRACING_BUCKETS.to_vec() - ) - .unwrap() -}); - -const CONSENSUS_WAIT_DURATION_BUCKETS: [f64; 19] = [ - 0.005, 0.01, 0.015, 0.02, 0.04, 0.06, 0.08, 0.10, 0.125, 0.15, 0.175, 0.2, 0.225, 0.25, 0.3, - 0.4, 0.6, 0.8, 2.0, -]; - -/// Histogram of the time it requires to wait before inserting blocks into block store. -/// Measured as the block's timestamp minus local timestamp. -pub static WAIT_DURATION_S: Lazy = Lazy::new(|| { - DurationHistogram::new(register_histogram!("aptos_consensus_wait_duration_s", - "Histogram of the time it requires to wait before inserting blocks into block store. Measured as the block's timestamp minus the local timestamp.", - CONSENSUS_WAIT_DURATION_BUCKETS.to_vec()).unwrap()) -}); - -const VERIFY_BUCKETS: &[f64] = &[ - 0.0001, 0.00025, 0.0005, 0.001, 0.0015, 0.002, 0.0025, 0.003, 0.0035, 0.004, 0.005, 0.006, - 0.007, 0.008, 0.009, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, -]; - -pub static VERIFY_MSG: Lazy = Lazy::new(|| { - register_histogram_vec!( - "aptos_consensus_verify_msg", - "Histogram of the time it takes to verify a message", - &["msg"], - VERIFY_BUCKETS.to_vec() - ) - .unwrap() -}); - -/////////////////// -// CHANNEL COUNTERS -/////////////////// -/// Count of the pending messages sent to itself in the channel -pub static PENDING_SELF_MESSAGES: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_pending_self_messages", - "Count of the pending messages sent to itself in the channel" - ) - .unwrap() -}); - -/// Count of the pending outbound round timeouts -pub static PENDING_ROUND_TIMEOUTS: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_pending_round_timeouts", - "Count of the pending outbound round timeouts" - ) - .unwrap() -}); - -/// Counter of pending network events to Consensus -pub static PENDING_CONSENSUS_NETWORK_EVENTS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_pending_network_events", - "Counters(queued,dequeued,dropped) related to pending network notifications to Consensus", - &["state"] - ) - .unwrap() -}); - -/// Count of the pending state sync notification. -pub static PENDING_STATE_SYNC_NOTIFICATION: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_pending_state_sync_notification", - "Count of the pending state sync notification" - ) - .unwrap() -}); - -/// Count of the pending quorum store commit notification. -pub static PENDING_QUORUM_STORE_COMMIT_NOTIFICATION: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_pending_quorum_store_commit_notification", - "Count of the pending quorum store commit notification" - ) - .unwrap() -}); - -/// Counters related to pending commit votes -pub static BUFFER_MANAGER_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_buffer_manager_msgs_count", - "Counters(queued,dequeued,dropped) related to pending commit votes", - &["state"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to consensus channel -pub static CONSENSUS_CHANNEL_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_channel_msgs_count", - "Counters(queued,dequeued,dropped) related to consensus channel", - &["state"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to buffer manager channel -pub static BUFFER_MANAGER_CHANNEL_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_buffer_manager_channel_msgs_count", - "Counters(queued,dequeued,dropped) related to buffer manager channel", - &["state"] - ) - .unwrap() -}); - -/// Counters for received consensus messages broken down by type -pub static CONSENSUS_RECEIVED_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_received_msgs_count", - "Counters for received consensus messages broken down by type", - &["type"] - ) - .unwrap() -}); - -/// Counters for sent consensus messages broken down by type -pub static CONSENSUS_SENT_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_sent_msgs_count", - "Counters for received consensus messages broken down by type", - &["type"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to consensus round manager channel -pub static ROUND_MANAGER_CHANNEL_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_round_manager_msgs_count", - "Counters(queued,dequeued,dropped) related to consensus round manager channel", - &["state"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to quorum store channel -pub static QUORUM_STORE_CHANNEL_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_quorum_store_channel_msgs_count", - "Counters(queued,dequeued,dropped) related to quorum store channel", - &["state"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to rpc request channel -pub static RPC_CHANNEL_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_rpc_channel_msgs_count", - "Counters(queued,dequeued,dropped) related to rpc request channel", - &["state"] - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to block retrieval per epoch task -pub static BLOCK_RETRIEVAL_TASK_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_block_retrieval_task_msgs_count", - "Counters(queued,dequeued,dropped) related to block retrieval task", - &["state"] - ) - .unwrap() -}); - -/// Count of the buffer manager retry requests since last restart. -pub static BUFFER_MANAGER_RETRY_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_buffer_manager_retry_count", - "Count of the buffer manager retry requests since last restart" - ) - .unwrap() -}); - -const PROPSER_ELECTION_DURATION_BUCKETS: [f64; 17] = [ - 0.001, 0.002, 0.003, 0.004, 0.006, 0.008, 0.01, 0.012, 0.014, 0.0175, 0.02, 0.025, 0.05, 0.25, - 0.5, 1.0, 2.0, -]; - -/// Time it takes for proposer election to compute proposer (when not cached) -pub static PROPOSER_ELECTION_DURATION: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_proposer_election_duration", - "Time it takes for proposer election to compute proposer (when not cached)", - PROPSER_ELECTION_DURATION_BUCKETS.to_vec() - ) - .unwrap() -}); - -/// Count of the number of blocks that have ready batches to execute. -pub static QUORUM_BATCH_READY_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_quorum_store_batch_ready_count", - "Count of the number of blocks that have ready batches to execute" - ) - .unwrap() -}); - -/// Histogram of the time durations waiting for batch when executing. -pub static BATCH_WAIT_DURATION: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "aptos_consensus_batch_wait_duration", - "Histogram of the time durations for waiting batches.", - // exponential_buckets(/*start=*/ 100.0, /*factor=*/ 1.1, /*count=*/ 100).unwrap(), - ) - .unwrap(), - ) -}); - -/// Histogram of timers for each of the buffer manager phase processors. -pub static BUFFER_MANAGER_PHASE_PROCESS_SECONDS: Lazy = Lazy::new(|| { - register_histogram_vec!( - // metric name - "aptos_consensus_buffer_manager_phase_process_seconds", - // metric description - "Timer for buffer manager PipelinePhase::process()", - // metric labels (dimensions) - &["name"], - exponential_buckets(/*start=*/ 1e-6, /*factor=*/ 2.0, /*count=*/ 22).unwrap(), - ) - .unwrap() -}); - -/// Count of the number of `ProposalExt` blocks received while the feature is disabled. -pub static UNEXPECTED_PROPOSAL_EXT_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_unexpected_proposal_ext_count", - "Count of the number of `ProposalExt` blocks received while the feature is disabled." - ) - .unwrap() -}); - -/// Histogram for the number of txns to be executed in a block. -pub static MAX_TXNS_FROM_BLOCK_TO_EXECUTE: Lazy = Lazy::new(|| { - register_histogram!( - "max_txns_from_block_to_execute", - "Histogram for the number of txns to be executed in a block.", - exponential_buckets(/*start=*/ 1.5, /*factor=*/ 1.5, /*count=*/ 25).unwrap(), - ) - .unwrap() -}); - -/// Count of the number of `DKG` validator transactions received while the feature is disabled. -pub static UNEXPECTED_DKG_VTXN_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_consensus_unexpected_dkg_vtxn_count", - "Count of the number of `DKG` validator transactions received while the feature is disabled." - ) - .unwrap() -}); - -/// Histogram of the time durations for fetching commit history. -pub static FETCH_COMMIT_HISTORY_DURATION: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "aptos_consensus_fetch_commit_history_duration", - "Histogram of the time durations for fetching commit history.", - // exponential_buckets(/*start=*/ 100.0, /*factor=*/ 1.1, /*count=*/ 100).unwrap(), - ) - .unwrap(), - ) -}); - -/// Update various counters for committed blocks -pub fn update_counters_for_committed_blocks(blocks_to_commit: &[Arc]) { - for block in blocks_to_commit { - observe_block(block.block().timestamp_usecs(), BlockStage::COMMITTED); - let txn_status = block.compute_result().compute_status_for_input_txns(); - NUM_TXNS_PER_BLOCK.observe(txn_status.len() as f64); - COMMITTED_BLOCKS_COUNT.inc(); - LAST_COMMITTED_ROUND.set(block.round() as i64); - LAST_COMMITTED_VERSION.set(block.compute_result().num_leaves() as i64); - - let failed_rounds = block - .block() - .block_data() - .failed_authors() - .map(|v| v.len()) - .unwrap_or(0); - if failed_rounds > 0 { - COMMITTED_FAILED_ROUNDS_COUNT.inc_by(failed_rounds as u64); - } - - // Quorum store metrics - quorum_store::counters::NUM_BATCH_PER_BLOCK.observe(block.block().payload_size() as f64); - - for status in txn_status.iter() { - let commit_status = match status { - TransactionStatus::Keep(_) => TXN_COMMIT_SUCCESS_LABEL, - TransactionStatus::Discard(reason) => { - if *reason == DiscardedVMStatus::SEQUENCE_NUMBER_TOO_NEW { - TXN_COMMIT_RETRY_LABEL - } else if *reason == DiscardedVMStatus::SEQUENCE_NUMBER_TOO_OLD { - TXN_COMMIT_FAILED_DUPLICATE_LABEL - } else { - TXN_COMMIT_FAILED_LABEL - } - }, - TransactionStatus::Retry => TXN_COMMIT_RETRY_LABEL, - }; - COMMITTED_TXNS_COUNT - .with_label_values(&[commit_status]) - .inc(); - } - } -} - -pub static EPOCH_MANAGER_ISSUES_DETAILS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_consensus_epoch_manager_issues", - "Count of occurences of different epoch manager processing issues.", - &["kind"] - ) - .unwrap() -}); - -pub static PROPOSED_VTXN_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_proposed_vtxn_count", - "Number of validator transactions proposed", - &["proposer"] - ) - .unwrap() -}); - -pub static PROPOSED_VTXN_BYTES: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_proposed_vtxn_bytes", - "The total size in bytes of validator transactions proposed", - &["proposer"] - ) - .unwrap() -}); - -pub static RAND_QUEUE_SIZE: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_rand_queue_size", - "Number of randomness-pending blocks." - ) - .unwrap() -}); diff --git a/consensus/src/dag/adapter.rs b/consensus/src/dag/adapter.rs deleted file mode 100644 index 6b983f8503d1e..0000000000000 --- a/consensus/src/dag/adapter.rs +++ /dev/null @@ -1,448 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - dag_store::DagStore, - observability::counters::{NUM_NODES_PER_BLOCK, NUM_ROUNDS_PER_BLOCK}, -}; -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - consensusdb::{CertifiedNodeSchema, ConsensusDB, DagVoteSchema, NodeSchema}, - counters, - counters::update_counters_for_committed_blocks, - dag::{ - storage::{CommitEvent, DAGStorage}, - CertifiedNode, Node, NodeId, Vote, - }, - pipeline::buffer_manager::OrderedBlocks, -}; -use anyhow::{anyhow, bail, format_err}; -use aptos_bitvec::BitVec; -use aptos_consensus_types::{ - block::Block, - common::{Author, Payload, Round}, - pipelined_block::PipelinedBlock, - quorum_cert::QuorumCert, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::StateComputeResult; -use aptos_infallible::RwLock; -use aptos_logger::{error, info}; -use aptos_storage_interface::DbReader; -use aptos_types::{ - account_config::NewBlockEvent, - aggregate_signature::AggregateSignature, - block_info::BlockInfo, - epoch_change::EpochChangeProof, - epoch_state::EpochState, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - on_chain_config::{CommitHistoryResource, OnChainConfig}, - state_store::state_key::StateKey, -}; -use async_trait::async_trait; -use futures_channel::mpsc::UnboundedSender; -use std::{ - collections::{BTreeMap, HashMap}, - sync::Arc, - time::{Duration, Instant}, -}; - -pub trait OrderedNotifier: Send + Sync { - fn send_ordered_nodes( - &self, - ordered_nodes: Vec>, - failed_author: Vec<(Round, Author)>, - ); -} - -#[async_trait] -pub trait ProofNotifier: Send + Sync { - async fn send_epoch_change(&self, proof: EpochChangeProof); - - async fn send_commit_proof(&self, ledger_info: LedgerInfoWithSignatures); -} - -pub(crate) fn compute_initial_block_and_ledger_info( - ledger_info_from_storage: LedgerInfoWithSignatures, -) -> (BlockInfo, LedgerInfoWithSignatures) { - // We start from the block that storage's latest ledger info, if storage has end-epoch - // LedgerInfo, we generate the virtual genesis block - if ledger_info_from_storage.ledger_info().ends_epoch() { - let genesis = - Block::make_genesis_block_from_ledger_info(ledger_info_from_storage.ledger_info()); - - let ledger_info = ledger_info_from_storage.ledger_info(); - let genesis_qc = QuorumCert::certificate_for_genesis_from_ledger_info( - ledger_info_from_storage.ledger_info(), - genesis.id(), - ); - let genesis_ledger_info = genesis_qc.ledger_info().clone(); - ( - genesis.gen_block_info( - ledger_info.transaction_accumulator_hash(), - ledger_info.version(), - ledger_info.next_epoch_state().cloned(), - ), - genesis_ledger_info, - ) - } else { - ( - ledger_info_from_storage.ledger_info().commit_info().clone(), - ledger_info_from_storage, - ) - } -} - -pub(super) struct OrderedNotifierAdapter { - executor_channel: UnboundedSender, - dag: Arc, - parent_block_info: Arc>, - epoch_state: Arc, - ledger_info_provider: Arc>, - block_ordered_ts: Arc>>, - allow_batches_without_pos_in_proposal: bool, -} - -impl OrderedNotifierAdapter { - pub(super) fn new( - executor_channel: UnboundedSender, - dag: Arc, - epoch_state: Arc, - parent_block_info: BlockInfo, - ledger_info_provider: Arc>, - allow_batches_without_pos_in_proposal: bool, - ) -> Self { - Self { - executor_channel, - dag, - parent_block_info: Arc::new(RwLock::new(parent_block_info)), - epoch_state, - ledger_info_provider, - block_ordered_ts: Arc::new(RwLock::new(BTreeMap::new())), - allow_batches_without_pos_in_proposal, - } - } - - pub(super) fn pipeline_pending_latency(&self) -> Duration { - match self.block_ordered_ts.read().first_key_value() { - Some((round, timestamp)) => { - let latency = timestamp.elapsed(); - info!(round = round, latency = latency, "pipeline pending latency"); - latency - }, - None => Duration::ZERO, - } - } -} - -impl OrderedNotifier for OrderedNotifierAdapter { - fn send_ordered_nodes( - &self, - ordered_nodes: Vec>, - failed_author: Vec<(Round, Author)>, - ) { - let anchor = ordered_nodes.last().unwrap(); - let epoch = anchor.epoch(); - let round = anchor.round(); - let timestamp = anchor.metadata().timestamp(); - let author = *anchor.author(); - let mut validator_txns = vec![]; - let mut payload = Payload::empty( - !anchor.payload().is_direct(), - self.allow_batches_without_pos_in_proposal, - ); - let mut node_digests = vec![]; - for node in &ordered_nodes { - validator_txns.extend(node.validator_txns().clone()); - payload = payload.extend(node.payload().clone()); - node_digests.push(node.digest()); - } - let parent_block_id = self.parent_block_info.read().id(); - // construct the bitvec that indicates which nodes present in the previous round in CommitEvent - let mut parents_bitvec = BitVec::with_num_bits(self.epoch_state.verifier.len() as u16); - for parent in anchor.parents().iter() { - if let Some(idx) = self - .epoch_state - .verifier - .address_to_validator_index() - .get(parent.metadata().author()) - { - parents_bitvec.set(*idx as u16); - } - } - let parent_timestamp = self.parent_block_info.read().timestamp_usecs(); - let block_timestamp = timestamp.max(parent_timestamp.checked_add(1).expect("must add")); - - NUM_NODES_PER_BLOCK.observe(ordered_nodes.len() as f64); - let rounds_between = { - let lowest_round_node = ordered_nodes.first().map_or(0, |node| node.round()); - round.saturating_sub(lowest_round_node) - }; - NUM_ROUNDS_PER_BLOCK.observe((rounds_between + 1) as f64); - - let block = PipelinedBlock::new( - Block::new_for_dag( - epoch, - round, - block_timestamp, - validator_txns, - payload, - author, - failed_author, - parent_block_id, - parents_bitvec, - node_digests, - ), - vec![], - StateComputeResult::new_dummy(), - ); - let block_info = block.block_info(); - let ledger_info_provider = self.ledger_info_provider.clone(); - let dag = self.dag.clone(); - *self.parent_block_info.write() = block_info.clone(); - - self.block_ordered_ts - .write() - .insert(block_info.round(), Instant::now()); - let block_created_ts = self.block_ordered_ts.clone(); - - observe_block(block.block().timestamp_usecs(), BlockStage::ORDERED); - - let blocks_to_send = OrderedBlocks { - ordered_blocks: vec![block], - ordered_proof: LedgerInfoWithSignatures::new( - LedgerInfo::new(block_info, anchor.digest()), - AggregateSignature::empty(), - ), - callback: Box::new( - move |committed_blocks: &[Arc], - commit_decision: LedgerInfoWithSignatures| { - block_created_ts - .write() - .retain(|&round, _| round > commit_decision.commit_info().round()); - dag.commit_callback(commit_decision.commit_info().round()); - ledger_info_provider - .write() - .notify_commit_proof(commit_decision); - update_counters_for_committed_blocks(committed_blocks); - }, - ), - }; - if self - .executor_channel - .unbounded_send(blocks_to_send) - .is_err() - { - error!("[DAG] execution pipeline closed"); - } - } -} - -pub struct StorageAdapter { - epoch: u64, - epoch_to_validators: HashMap>, - consensus_db: Arc, - aptos_db: Arc, -} - -impl StorageAdapter { - pub fn new( - epoch: u64, - epoch_to_validators: HashMap>, - consensus_db: Arc, - aptos_db: Arc, - ) -> Self { - Self { - epoch, - epoch_to_validators, - consensus_db, - aptos_db, - } - } - - pub fn bitvec_to_validators( - validators: &[Author], - bitvec: &BitVec, - ) -> anyhow::Result> { - if BitVec::required_buckets(validators.len() as u16) != bitvec.num_buckets() { - bail!( - "bitvec bucket {} does not match validators len {}", - bitvec.num_buckets(), - validators.len() - ); - } - - Ok(validators - .iter() - .enumerate() - .filter_map(|(index, validator)| { - if bitvec.is_set(index as u16) { - Some(*validator) - } else { - None - } - }) - .collect()) - } - - pub fn indices_to_validators( - validators: &[Author], - indices: &[u64], - ) -> anyhow::Result> { - indices - .iter() - .map(|index| { - usize::try_from(*index) - .map_err(|_err| anyhow!("index {} out of bounds", index)) - .and_then(|index| { - validators.get(index).cloned().ok_or(anyhow!( - "index {} is larger than number of validators {}", - index, - validators.len() - )) - }) - }) - .collect() - } - - fn convert(&self, new_block_event: NewBlockEvent) -> anyhow::Result { - let validators = &self.epoch_to_validators[&new_block_event.epoch()]; - Ok(CommitEvent::new( - NodeId::new( - new_block_event.epoch(), - new_block_event.round(), - new_block_event.proposer(), - ), - Self::bitvec_to_validators( - validators, - &new_block_event.previous_block_votes_bitvec().clone().into(), - )?, - Self::indices_to_validators(validators, new_block_event.failed_proposer_indices())?, - )) - } - - fn get_commit_history_resource( - &self, - latest_version: u64, - ) -> anyhow::Result { - Ok(bcs::from_bytes( - self.aptos_db - .get_state_value_by_version( - &StateKey::access_path(CommitHistoryResource::access_path().unwrap()), - latest_version, - )? - .ok_or_else(|| format_err!("Resource doesn't exist"))? - .bytes(), - )?) - } -} - -impl DAGStorage for StorageAdapter { - fn save_pending_node(&self, node: &Node) -> anyhow::Result<()> { - Ok(self.consensus_db.put::(&(), node)?) - } - - fn get_pending_node(&self) -> anyhow::Result> { - Ok(self.consensus_db.get::(&())?) - } - - fn delete_pending_node(&self) -> anyhow::Result<()> { - Ok(self.consensus_db.delete::(vec![()])?) - } - - fn save_vote(&self, node_id: &NodeId, vote: &Vote) -> anyhow::Result<()> { - Ok(self.consensus_db.put::(node_id, vote)?) - } - - fn get_votes(&self) -> anyhow::Result> { - Ok(self.consensus_db.get_all::()?) - } - - fn delete_votes(&self, node_ids: Vec) -> anyhow::Result<()> { - Ok(self.consensus_db.delete::(node_ids)?) - } - - fn save_certified_node(&self, node: &CertifiedNode) -> anyhow::Result<()> { - Ok(self - .consensus_db - .put::(&node.digest(), node)?) - } - - fn get_certified_nodes(&self) -> anyhow::Result> { - Ok(self.consensus_db.get_all::()?) - } - - fn delete_certified_nodes(&self, digests: Vec) -> anyhow::Result<()> { - Ok(self.consensus_db.delete::(digests)?) - } - - fn get_latest_k_committed_events(&self, k: u64) -> anyhow::Result> { - let timer = counters::FETCH_COMMIT_HISTORY_DURATION.start_timer(); - let version = self.aptos_db.get_latest_version()?; - let resource = self.get_commit_history_resource(version)?; - let handle = resource.table_handle(); - let mut commit_events = vec![]; - for i in 1..=std::cmp::min(k, resource.length()) { - let idx = (resource.next_idx() + resource.max_capacity() - i as u32) - % resource.max_capacity(); - let new_block_event = bcs::from_bytes::( - self.aptos_db - .get_state_value_by_version( - &StateKey::table_item(*handle, bcs::to_bytes(&idx).unwrap()), - version, - )? - .ok_or_else(|| format_err!("Table item doesn't exist"))? - .bytes(), - )?; - if self - .epoch_to_validators - .contains_key(&new_block_event.epoch()) - { - commit_events.push(self.convert(new_block_event)?); - } - } - let duration = timer.stop_and_record(); - info!("[DAG] fetch commit history duration: {} sec", duration); - commit_events.reverse(); - Ok(commit_events) - } - - fn get_latest_ledger_info(&self) -> anyhow::Result { - // TODO: use callback from notifier to cache the latest ledger info - Ok(self.aptos_db.get_latest_ledger_info()?) - } - - fn get_epoch_to_proposers(&self) -> HashMap> { - self.epoch_to_validators.clone() - } -} - -pub(crate) trait TLedgerInfoProvider: Send + Sync { - fn get_latest_ledger_info(&self) -> LedgerInfoWithSignatures; - - fn get_highest_committed_anchor_round(&self) -> Round; -} - -pub(super) struct LedgerInfoProvider { - latest_ledger_info: LedgerInfoWithSignatures, -} - -impl LedgerInfoProvider { - pub(super) fn new(latest_ledger_info: LedgerInfoWithSignatures) -> Self { - Self { latest_ledger_info } - } - - pub(super) fn notify_commit_proof(&mut self, ledger_info: LedgerInfoWithSignatures) { - self.latest_ledger_info = ledger_info; - } -} - -impl TLedgerInfoProvider for RwLock { - fn get_latest_ledger_info(&self) -> LedgerInfoWithSignatures { - self.read().latest_ledger_info.clone() - } - - fn get_highest_committed_anchor_round(&self) -> Round { - self.read().latest_ledger_info.ledger_info().round() - } -} diff --git a/consensus/src/dag/anchor_election/leader_reputation_adapter.rs b/consensus/src/dag/anchor_election/leader_reputation_adapter.rs deleted file mode 100644 index 3d58d5cd31b47..0000000000000 --- a/consensus/src/dag/anchor_election/leader_reputation_adapter.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - dag::{ - anchor_election::{AnchorElection, CommitHistory}, - storage::CommitEvent, - }, - liveness::{ - leader_reputation::{ - LeaderReputation, MetadataBackend, ReputationHeuristic, VotingPowerRatio, - }, - proposer_election::ProposerElection, - }, -}; -use aptos_bitvec::BitVec; -use aptos_collections::BoundedVecDeque; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::HashValue; -use aptos_infallible::Mutex; -use aptos_types::account_config::NewBlockEvent; -use move_core_types::account_address::AccountAddress; -use std::{collections::HashMap, sync::Arc}; - -pub struct MetadataBackendAdapter { - epoch_to_validators: HashMap>, - window_size: usize, - sliding_window: Mutex>, -} - -impl MetadataBackendAdapter { - pub fn new( - window_size: usize, - epoch_to_validators: HashMap>, - ) -> Self { - Self { - epoch_to_validators, - window_size, - sliding_window: Mutex::new(BoundedVecDeque::new(window_size)), - } - } - - pub fn push(&self, event: CommitEvent) { - if !self.epoch_to_validators.contains_key(&event.epoch()) { - return; - } - self.sliding_window.lock().push_front(event); - } - - // TODO: we should change NewBlockEvent on LeaderReputation to take a trait - fn convert(&self, event: CommitEvent) -> NewBlockEvent { - let validators = self.epoch_to_validators.get(&event.epoch()).unwrap(); - let mut bitvec = BitVec::with_num_bits(validators.len() as u16); - for author in event.parents() { - bitvec.set(*validators.get(author).unwrap() as u16); - } - let mut failed_authors = vec![]; - for author in event.failed_authors() { - failed_authors.push(*validators.get(author).unwrap() as u64); - } - NewBlockEvent::new( - AccountAddress::ZERO, - event.epoch(), - event.round(), - 0, - bitvec.into(), - *event.author(), - failed_authors, - 0, - ) - } -} - -impl MetadataBackend for MetadataBackendAdapter { - fn get_block_metadata( - &self, - _target_epoch: u64, - _target_round: Round, - ) -> (Vec, HashValue) { - let events: Vec<_> = self - .sliding_window - .lock() - .clone() - .into_iter() - .map(|event| self.convert(event)) - .collect(); - ( - events, - // TODO: fill in the hash value - HashValue::zero(), - ) - } -} - -pub struct LeaderReputationAdapter { - reputation: LeaderReputation, - data_source: Arc, -} - -impl LeaderReputationAdapter { - pub fn new( - epoch: u64, - epoch_to_proposers: HashMap>, - voting_powers: Vec, - backend: Arc, - heuristic: Box, - window_for_chain_health: usize, - ) -> Self { - Self { - reputation: LeaderReputation::new( - epoch, - epoch_to_proposers, - voting_powers, - backend.clone(), - heuristic, - 0, - true, - window_for_chain_health, - ), - data_source: backend, - } - } -} - -impl AnchorElection for LeaderReputationAdapter { - fn get_anchor(&self, round: Round) -> Author { - self.reputation.get_valid_proposer(round) - } - - fn update_reputation(&self, commit_event: CommitEvent) { - self.data_source.push(commit_event) - } -} - -impl CommitHistory for LeaderReputationAdapter { - fn get_voting_power_participation_ratio(&self, round: Round) -> VotingPowerRatio { - let mut voting_power_ratio = self.reputation.get_voting_power_participation_ratio(round); - // TODO: fix this once leader reputation is fixed - if voting_power_ratio < 0.67 { - voting_power_ratio = 1.0; - } - - voting_power_ratio - } -} diff --git a/consensus/src/dag/anchor_election/mod.rs b/consensus/src/dag/anchor_election/mod.rs deleted file mode 100644 index 53db6db738250..0000000000000 --- a/consensus/src/dag/anchor_election/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{dag::storage::CommitEvent, liveness::leader_reputation::VotingPowerRatio}; -use aptos_consensus_types::common::{Author, Round}; - -pub trait AnchorElection: Send + Sync { - fn get_anchor(&self, round: Round) -> Author; - - fn update_reputation(&self, commit_event: CommitEvent); -} - -pub trait CommitHistory: Send + Sync { - fn get_voting_power_participation_ratio(&self, round: Round) -> VotingPowerRatio; -} - -mod leader_reputation_adapter; -mod round_robin; - -pub use leader_reputation_adapter::{LeaderReputationAdapter, MetadataBackendAdapter}; -pub use round_robin::RoundRobinAnchorElection; diff --git a/consensus/src/dag/anchor_election/round_robin.rs b/consensus/src/dag/anchor_election/round_robin.rs deleted file mode 100644 index ee1b6e5f53442..0000000000000 --- a/consensus/src/dag/anchor_election/round_robin.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::CommitHistory; -use crate::{ - dag::{anchor_election::AnchorElection, storage::CommitEvent}, - liveness::leader_reputation::VotingPowerRatio, -}; -use aptos_consensus_types::common::{Author, Round}; - -pub struct RoundRobinAnchorElection { - validators: Vec, -} - -impl RoundRobinAnchorElection { - pub fn new(validators: Vec) -> Self { - Self { validators } - } -} - -impl AnchorElection for RoundRobinAnchorElection { - fn get_anchor(&self, round: Round) -> Author { - self.validators[(round / 2) as usize % self.validators.len()] - } - - fn update_reputation(&self, _event: CommitEvent) {} -} - -impl CommitHistory for RoundRobinAnchorElection { - fn get_voting_power_participation_ratio(&self, _round: Round) -> VotingPowerRatio { - 1.0 - } -} diff --git a/consensus/src/dag/bootstrap.rs b/consensus/src/dag/bootstrap.rs deleted file mode 100644 index a70c089e1da15..0000000000000 --- a/consensus/src/dag/bootstrap.rs +++ /dev/null @@ -1,778 +0,0 @@ -// Copyright © Aptos Foundation - -use super::{ - adapter::{OrderedNotifierAdapter, TLedgerInfoProvider}, - anchor_election::{AnchorElection, CommitHistory, RoundRobinAnchorElection}, - dag_driver::DagDriver, - dag_fetcher::{DagFetcher, DagFetcherService, FetchRequestHandler}, - dag_handler::NetworkHandler, - dag_network::TDAGNetworkSender, - dag_state_sync::{DagStateSynchronizer, StateSyncTrigger}, - dag_store::DagStore, - health::{ChainHealthBackoff, HealthBackoff, PipelineLatencyBasedBackpressure, TChainHealth}, - order_rule::OrderRule, - rb_handler::NodeBroadcastHandler, - storage::{CommitEvent, DAGStorage}, - types::{CertifiedNodeMessage, DAGMessage}, - DAGRpcResult, ProofNotifier, -}; -use crate::{ - dag::{ - adapter::{compute_initial_block_and_ledger_info, LedgerInfoProvider}, - anchor_election::{LeaderReputationAdapter, MetadataBackendAdapter}, - dag_state_sync::{SyncModeMessageHandler, SyncOutcome}, - observability::logging::{LogEvent, LogSchema}, - round_state::{AdaptiveResponsive, RoundState}, - }, - liveness::{ - leader_reputation::{ProposerAndVoterHeuristic, ReputationHeuristic}, - proposal_generator::{ChainHealthBackoffConfig, PipelineBackpressureConfig}, - }, - monitor, - network::IncomingDAGRequest, - payload_client::PayloadClient, - payload_manager::PayloadManager, - pipeline::{buffer_manager::OrderedBlocks, execution_client::TExecutionClient}, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::{ - aptos_channel::{self, Receiver}, - message_queues::QueueStyle, -}; -use aptos_config::config::DagConsensusConfig; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::{Mutex, RwLock}; -use aptos_logger::{debug, info}; -use aptos_reliable_broadcast::{RBNetworkSender, ReliableBroadcast}; -use aptos_types::{ - epoch_state::EpochState, - on_chain_config::{ - AnchorElectionMode, DagConsensusConfigV1, - LeaderReputationType::{ProposerAndVoter, ProposerAndVoterV2}, - OnChainJWKConsensusConfig, OnChainRandomnessConfig, ProposerAndVoterConfig, - ValidatorTxnConfig, - }, - validator_signer::ValidatorSigner, -}; -use async_trait::async_trait; -use enum_dispatch::enum_dispatch; -use futures_channel::{ - mpsc::{UnboundedReceiver, UnboundedSender}, - oneshot, -}; -use std::{fmt, ops::Deref, sync::Arc, time::Duration}; -use tokio::{ - runtime::Handle, - select, - task::{block_in_place, JoinHandle}, -}; -use tokio_retry::strategy::ExponentialBackoff; - -#[derive(Clone)] -struct BootstrapBaseState { - dag_store: Arc, - order_rule: Arc>, - ledger_info_provider: Arc, - ordered_notifier: Arc, - commit_history: Arc, -} - -#[enum_dispatch(TDagMode)] -enum Mode { - Active(ActiveMode), - Sync(SyncMode), -} - -impl fmt::Display for Mode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Mode::Active(_) => write!(f, "Active"), - Mode::Sync(_) => write!(f, "Sync"), - } - } -} - -#[async_trait] -#[enum_dispatch] -trait TDagMode { - async fn run( - self, - dag_rpc_rx: &mut Receiver, - bootstrapper: &DagBootstrapper, - ) -> Option; -} - -struct ActiveMode { - handler: NetworkHandler, - fetch_service: DagFetcherService, - base_state: BootstrapBaseState, - buffer: Vec, -} - -#[async_trait] -impl TDagMode for ActiveMode { - async fn run( - self, - dag_rpc_rx: &mut Receiver, - bootstrapper: &DagBootstrapper, - ) -> Option { - monitor!( - "dag_active_mode", - self.run_internal(dag_rpc_rx, bootstrapper).await - ) - } -} - -impl ActiveMode { - async fn run_internal( - self, - dag_rpc_rx: &mut Receiver, - bootstrapper: &DagBootstrapper, - ) -> Option { - info!( - LogSchema::new(LogEvent::ActiveMode) - .round(self.base_state.dag_store.deref().read().highest_round()), - highest_committed_round = self - .base_state - .ledger_info_provider - .get_latest_ledger_info() - .commit_info() - .round(), - highest_ordered_round = self - .base_state - .dag_store - .read() - .highest_ordered_anchor_round(), - ); - - // Spawn the fetch service - let handle = tokio::spawn(self.fetch_service.start()); - defer!({ - // Signal and stop the fetch service - debug!("aborting fetch service"); - handle.abort(); - let _ = block_in_place(move || Handle::current().block_on(handle)); - debug!("aborting fetch service complete"); - }); - - // Run the network handler until it returns with state sync status. - let sync_outcome = self - .handler - .run(dag_rpc_rx, bootstrapper.executor.clone(), self.buffer) - .await; - - info!( - LogSchema::new(LogEvent::SyncOutcome), - sync_outcome = %sync_outcome, - ); - - match sync_outcome { - SyncOutcome::NeedsSync(certified_node_msg) => Some(Mode::Sync(SyncMode { - certified_node_msg, - base_state: self.base_state, - })), - SyncOutcome::EpochEnds => None, - _ => unreachable!(), - } - } -} - -struct SyncMode { - certified_node_msg: CertifiedNodeMessage, - base_state: BootstrapBaseState, -} - -#[async_trait] -impl TDagMode for SyncMode { - async fn run( - self, - dag_rpc_rx: &mut Receiver, - bootstrapper: &DagBootstrapper, - ) -> Option { - monitor!( - "dag_sync_mode", - self.run_internal(dag_rpc_rx, bootstrapper).await - ) - } -} - -impl SyncMode { - async fn run_internal( - self, - dag_rpc_rx: &mut Receiver, - bootstrapper: &DagBootstrapper, - ) -> Option { - let sync_manager = DagStateSynchronizer::new( - bootstrapper.epoch_state.clone(), - bootstrapper.time_service.clone(), - bootstrapper.execution_client.clone(), - bootstrapper.storage.clone(), - bootstrapper.payload_manager.clone(), - bootstrapper - .onchain_config - .dag_ordering_causal_history_window as Round, - ); - - let highest_committed_anchor_round = self - .base_state - .ledger_info_provider - .get_highest_committed_anchor_round(); - - info!( - LogSchema::new(LogEvent::SyncMode) - .round(self.base_state.dag_store.read().highest_round()), - target_round = self.certified_node_msg.round(), - local_ordered_round = self - .base_state - .dag_store - .read() - .highest_ordered_anchor_round(), - local_committed_round = highest_committed_anchor_round - ); - let dag_fetcher = DagFetcher::new( - bootstrapper.epoch_state.clone(), - bootstrapper.dag_network_sender.clone(), - bootstrapper.time_service.clone(), - bootstrapper.config.fetcher_config.clone(), - ); - - let (request, responders, sync_dag_store) = sync_manager.build_request( - &self.certified_node_msg, - self.base_state.dag_store.clone(), - highest_committed_anchor_round, - ); - - let commit_li = self.certified_node_msg.ledger_info().clone(); - - let network_handle = SyncModeMessageHandler::new( - bootstrapper.epoch_state.clone(), - request.start_round(), - request.target_round(), - bootstrapper - .onchain_config - .dag_ordering_causal_history_window as u64, - ); - - let (res_tx, res_rx) = oneshot::channel(); - let handle = tokio::spawn(async move { - let result = sync_manager - .sync_dag_to(dag_fetcher, request, responders, sync_dag_store, commit_li) - .await; - let _ = res_tx.send(result); - }); - defer!({ - debug!("aborting dag synchronizer"); - handle.abort(); - let _ = block_in_place(move || Handle::current().block_on(handle)); - debug!("aborting dag synchronizer complete"); - }); - - let mut buffer = Vec::new(); - - select! { - biased; - res = res_rx => { - match res { - Ok(sync_result) => { - if sync_result.is_ok() { - info!("sync succeeded. running full bootstrap."); - // If the sync task finishes successfully, we can transition to Active mode by - // rebootstrapping all components starting from the DAG store. - let (new_state, new_handler, new_fetch_service) = bootstrapper.full_bootstrap(); - Some(Mode::Active(ActiveMode { - handler: new_handler, - fetch_service: new_fetch_service, - base_state: new_state, - buffer, - })) - } else { - info!("sync failed. resuming with current DAG state."); - // If the sync task fails, then continue the DAG in Active Mode with existing state. - let (new_handler, new_fetch_service) = - bootstrapper.bootstrap_components(&self.base_state); - Some(Mode::Active(ActiveMode { - handler: new_handler, - fetch_service: new_fetch_service, - base_state: self.base_state, - buffer, - })) - } - }, - Err(_) => unreachable!("sender won't be dropped without sending"), - } - }, - res = network_handle.run(dag_rpc_rx, &mut buffer) => { - // The network handle returns if the sender side of dag_rpc_rx closes, - // or network handle found a future CertifiedNodeMessage to cancel the - // current sync. - if let Some(msg) = res { - Some(Mode::Sync(SyncMode { - certified_node_msg: msg, - base_state: self.base_state, - })) - } else { - unreachable!("remote mustn't drop the network message sender until bootstrapper returns"); - } - } - } - } -} - -pub struct DagBootstrapper { - self_peer: Author, - config: DagConsensusConfig, - onchain_config: DagConsensusConfigV1, - signer: Arc, - epoch_state: Arc, - storage: Arc, - rb_network_sender: Arc>, - dag_network_sender: Arc, - proof_notifier: Arc, - time_service: aptos_time_service::TimeService, - payload_manager: Arc, - payload_client: Arc, - ordered_nodes_tx: UnboundedSender, - execution_client: Arc, - quorum_store_enabled: bool, - vtxn_config: ValidatorTxnConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - executor: BoundedExecutor, - allow_batches_without_pos_in_proposal: bool, -} - -impl DagBootstrapper { - #[allow(clippy::too_many_arguments)] - pub fn new( - self_peer: Author, - config: DagConsensusConfig, - onchain_config: DagConsensusConfigV1, - signer: Arc, - epoch_state: Arc, - storage: Arc, - rb_network_sender: Arc>, - dag_network_sender: Arc, - proof_notifier: Arc, - time_service: aptos_time_service::TimeService, - payload_manager: Arc, - payload_client: Arc, - ordered_nodes_tx: UnboundedSender, - execution_client: Arc, - quorum_store_enabled: bool, - vtxn_config: ValidatorTxnConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - executor: BoundedExecutor, - allow_batches_without_pos_in_proposal: bool, - ) -> Self { - Self { - self_peer, - config, - onchain_config, - signer, - epoch_state, - storage, - rb_network_sender, - dag_network_sender, - proof_notifier, - time_service, - payload_manager, - payload_client, - ordered_nodes_tx, - execution_client, - quorum_store_enabled, - vtxn_config, - randomness_config, - jwk_consensus_config, - executor, - allow_batches_without_pos_in_proposal, - } - } - - fn build_leader_reputation_components( - &self, - config: &ProposerAndVoterConfig, - ) -> Arc { - let num_validators = self.epoch_state.verifier.len(); - let epoch_to_validators_vec = self.storage.get_epoch_to_proposers(); - let epoch_to_validator_map = epoch_to_validators_vec - .iter() - .map(|(key, value)| { - ( - *key, - value - .iter() - .enumerate() - .map(|(idx, author)| (*author, idx)) - .collect(), - ) - }) - .collect(); - let metadata_adapter = Arc::new(MetadataBackendAdapter::new( - num_validators - * std::cmp::max( - config.proposer_window_num_validators_multiplier, - config.voter_window_num_validators_multiplier, - ), - epoch_to_validator_map, - )); - let heuristic: Box = Box::new(ProposerAndVoterHeuristic::new( - self.self_peer, - config.active_weight, - config.inactive_weight, - config.failed_weight, - config.failure_threshold_percent, - num_validators * config.voter_window_num_validators_multiplier, - num_validators * config.proposer_window_num_validators_multiplier, - false, - )); - - let voting_power: Vec = self - .epoch_state - .verifier - .get_ordered_account_addresses_iter() - .map(|p| self.epoch_state.verifier.get_voting_power(&p).unwrap()) - .collect(); - - Arc::new(LeaderReputationAdapter::new( - self.epoch_state.epoch, - epoch_to_validators_vec, - voting_power, - metadata_adapter, - heuristic, - 100, - )) - } - - fn build_anchor_election( - &self, - ) -> ( - Arc, - Arc, - Option>, - ) { - match &self.onchain_config.anchor_election_mode { - AnchorElectionMode::RoundRobin => { - let election = Arc::new(RoundRobinAnchorElection::new( - self.epoch_state.verifier.get_ordered_account_addresses(), - )); - (election.clone(), election, None) - }, - AnchorElectionMode::LeaderReputation(reputation_type) => { - let (commit_events, leader_reputation) = match reputation_type { - ProposerAndVoterV2(config) => { - let commit_events = self - .storage - .get_latest_k_committed_events( - std::cmp::max( - config.proposer_window_num_validators_multiplier, - config.voter_window_num_validators_multiplier, - ) as u64 - * self.epoch_state.verifier.len() as u64, - ) - .expect("Failed to read commit events from storage"); - ( - commit_events, - self.build_leader_reputation_components(config), - ) - }, - ProposerAndVoter(_) => unreachable!("unsupported mode"), - }; - - ( - leader_reputation.clone(), - leader_reputation, - Some(commit_events), - ) - }, - } - } - - fn bootstrap_dag_store( - &self, - anchor_election: Arc, - commit_history: Arc, - commit_events: Option>, - dag_window_size_config: u64, - ) -> BootstrapBaseState { - let ledger_info_from_storage = self - .storage - .get_latest_ledger_info() - .expect("latest ledger info must exist"); - let (parent_block_info, ledger_info) = - compute_initial_block_and_ledger_info(ledger_info_from_storage); - - let ledger_info_provider = Arc::new(RwLock::new(LedgerInfoProvider::new(ledger_info))); - - let initial_ledger_info = ledger_info_provider - .get_latest_ledger_info() - .ledger_info() - .clone(); - let commit_round = initial_ledger_info.round(); - let initial_round = std::cmp::max( - 1, - initial_ledger_info - .round() - .saturating_sub(dag_window_size_config), - ); - - let dag = Arc::new(DagStore::new( - self.epoch_state.clone(), - self.storage.clone(), - self.payload_manager.clone(), - initial_round, - dag_window_size_config, - )); - - let ordered_notifier = Arc::new(OrderedNotifierAdapter::new( - self.ordered_nodes_tx.clone(), - dag.clone(), - self.epoch_state.clone(), - parent_block_info, - ledger_info_provider.clone(), - self.allow_batches_without_pos_in_proposal, - )); - - let order_rule = Arc::new(Mutex::new(OrderRule::new( - self.epoch_state.clone(), - commit_round + 1, - dag.clone(), - anchor_election.clone(), - ordered_notifier.clone(), - self.onchain_config.dag_ordering_causal_history_window as Round, - commit_events, - ))); - - BootstrapBaseState { - dag_store: dag, - order_rule, - ledger_info_provider, - ordered_notifier, - commit_history, - } - } - - fn bootstrap_components( - &self, - base_state: &BootstrapBaseState, - ) -> (NetworkHandler, DagFetcherService) { - let validators = self.epoch_state.verifier.get_ordered_account_addresses(); - let rb_config = self.config.rb_config.clone(); - let round_state_config = self.config.round_state_config.clone(); - - // A backoff policy that starts at _base_*_factor_ ms and multiplies by _base_ each iteration. - let rb_backoff_policy = ExponentialBackoff::from_millis(rb_config.backoff_policy_base_ms) - .factor(rb_config.backoff_policy_factor) - .max_delay(Duration::from_millis(rb_config.backoff_policy_max_delay_ms)); - let rb = Arc::new(ReliableBroadcast::new( - validators.clone(), - self.rb_network_sender.clone(), - rb_backoff_policy, - self.time_service.clone(), - Duration::from_millis(rb_config.rpc_timeout_ms), - self.executor.clone(), - )); - - let BootstrapBaseState { - dag_store, - ledger_info_provider, - order_rule, - ordered_notifier, - commit_history, - } = base_state; - - let state_sync_trigger = StateSyncTrigger::new( - self.epoch_state.clone(), - ledger_info_provider.clone(), - dag_store.clone(), - self.proof_notifier.clone(), - self.onchain_config.dag_ordering_causal_history_window as Round, - ); - - let (dag_fetcher, fetch_requester, node_fetch_waiter, certified_node_fetch_waiter) = - DagFetcherService::new( - self.epoch_state.clone(), - self.dag_network_sender.clone(), - dag_store.clone(), - self.time_service.clone(), - self.config.fetcher_config.clone(), - ); - let fetch_requester = Arc::new(fetch_requester); - let (new_round_tx, new_round_rx) = tokio::sync::mpsc::unbounded_channel(); - let round_state = RoundState::new( - new_round_tx.clone(), - Box::new(AdaptiveResponsive::new( - new_round_tx, - self.epoch_state.clone(), - Duration::from_millis(round_state_config.adaptive_responsive_minimum_wait_time_ms), - )), - ); - - let chain_health: Arc = ChainHealthBackoff::new( - ChainHealthBackoffConfig::new(self.config.health_config.chain_backoff_config.clone()), - commit_history.clone(), - ); - let pipeline_health = PipelineLatencyBasedBackpressure::new( - Duration::from_millis(self.config.health_config.voter_pipeline_latency_limit_ms), - PipelineBackpressureConfig::new( - self.config - .health_config - .pipeline_backpressure_config - .clone(), - ), - ordered_notifier.clone(), - ); - let health_backoff = - HealthBackoff::new(self.epoch_state.clone(), chain_health, pipeline_health); - let dag_driver = DagDriver::new( - self.self_peer, - self.epoch_state.clone(), - dag_store.clone(), - self.payload_client.clone(), - rb, - self.time_service.clone(), - self.storage.clone(), - order_rule.clone(), - fetch_requester.clone(), - ledger_info_provider.clone(), - round_state, - self.onchain_config.dag_ordering_causal_history_window as Round, - self.config.node_payload_config.clone(), - health_backoff.clone(), - self.quorum_store_enabled, - self.allow_batches_without_pos_in_proposal, - ); - let rb_handler = NodeBroadcastHandler::new( - dag_store.clone(), - order_rule.clone(), - self.signer.clone(), - self.epoch_state.clone(), - self.storage.clone(), - fetch_requester, - self.config.node_payload_config.clone(), - self.vtxn_config.clone(), - self.randomness_config.clone(), - self.jwk_consensus_config.clone(), - health_backoff, - ); - let fetch_handler = FetchRequestHandler::new(dag_store.clone(), self.epoch_state.clone()); - - let dag_handler = NetworkHandler::new( - self.epoch_state.clone(), - rb_handler, - dag_driver, - fetch_handler, - node_fetch_waiter, - certified_node_fetch_waiter, - state_sync_trigger, - new_round_rx, - ); - - (dag_handler, dag_fetcher) - } - - fn full_bootstrap(&self) -> (BootstrapBaseState, NetworkHandler, DagFetcherService) { - let (anchor_election, commit_history, commit_events) = self.build_anchor_election(); - - let base_state = self.bootstrap_dag_store( - anchor_election.clone(), - commit_history, - commit_events, - self.onchain_config.dag_ordering_causal_history_window as u64, - ); - - let (handler, fetch_service) = self.bootstrap_components(&base_state); - (base_state, handler, fetch_service) - } - - pub async fn start( - self, - mut dag_rpc_rx: Receiver, - mut shutdown_rx: oneshot::Receiver>, - ) { - info!( - LogSchema::new(LogEvent::EpochStart), - epoch = self.epoch_state.epoch, - ); - - let (base_state, handler, fetch_service) = self.full_bootstrap(); - - let mut mode = Mode::Active(ActiveMode { - handler, - fetch_service, - base_state, - buffer: Vec::new(), - }); - loop { - select! { - biased; - Ok(ack_tx) = &mut shutdown_rx => { - let _ = ack_tx.send(()); - info!(LogSchema::new(LogEvent::Shutdown), epoch = self.epoch_state.epoch); - return; - }, - Some(next_mode) = mode.run(&mut dag_rpc_rx, &self) => { - info!(LogSchema::new(LogEvent::ModeTransition), next_mode = %next_mode); - mode = next_mode; - } - } - } - } -} - -pub(super) fn bootstrap_dag_for_test( - self_peer: Author, - signer: ValidatorSigner, - epoch_state: Arc, - storage: Arc, - rb_network_sender: Arc>, - dag_network_sender: Arc, - proof_notifier: Arc, - time_service: aptos_time_service::TimeService, - payload_manager: Arc, - payload_client: Arc, - execution_client: Arc, -) -> ( - JoinHandle, - JoinHandle<()>, - aptos_channel::Sender, - UnboundedReceiver, -) { - let (ordered_nodes_tx, ordered_nodes_rx) = futures_channel::mpsc::unbounded(); - let bootstraper = DagBootstrapper::new( - self_peer, - DagConsensusConfig::default(), - DagConsensusConfigV1::default(), - signer.into(), - epoch_state.clone(), - storage.clone(), - rb_network_sender, - dag_network_sender, - proof_notifier.clone(), - time_service, - payload_manager, - payload_client, - ordered_nodes_tx, - execution_client, - false, - ValidatorTxnConfig::default_enabled(), - OnChainRandomnessConfig::default_enabled(), - OnChainJWKConsensusConfig::default_enabled(), - BoundedExecutor::new(2, Handle::current()), - true, - ); - - let (_base_state, handler, fetch_service) = bootstraper.full_bootstrap(); - - let (dag_rpc_tx, dag_rpc_rx) = aptos_channel::new(QueueStyle::FIFO, 64, None); - - let dh_handle = tokio::spawn(async move { - let mut dag_rpc_rx = dag_rpc_rx; - handler - .run(&mut dag_rpc_rx, bootstraper.executor.clone(), Vec::new()) - .await - }); - let df_handle = tokio::spawn(fetch_service.start()); - - (dh_handle, df_handle, dag_rpc_tx, ordered_nodes_rx) -} diff --git a/consensus/src/dag/commit_signer.rs b/consensus/src/dag/commit_signer.rs deleted file mode 100644 index 6aa06d19d7ae3..0000000000000 --- a/consensus/src/dag/commit_signer.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::pipeline::signing_phase::CommitSignerProvider; -use aptos_crypto::bls12381; -use aptos_types::validator_signer::ValidatorSigner; -use std::sync::Arc; - -pub struct DagCommitSigner { - signer: Arc, -} - -impl DagCommitSigner { - pub fn new(signer: Arc) -> Self { - Self { signer } - } -} - -impl CommitSignerProvider for DagCommitSigner { - fn sign_commit_vote( - &self, - _ledger_info: aptos_types::ledger_info::LedgerInfoWithSignatures, - new_ledger_info: aptos_types::ledger_info::LedgerInfo, - ) -> Result { - let signature = self - .signer - .sign(&new_ledger_info) - .map_err(|err| aptos_safety_rules::Error::SerializationError(err.to_string()))?; - - Ok(signature) - } -} diff --git a/consensus/src/dag/dag_driver.rs b/consensus/src/dag/dag_driver.rs deleted file mode 100644 index 278f76248bb3e..0000000000000 --- a/consensus/src/dag/dag_driver.rs +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{dag_store::DagStore, health::HealthBackoff, types::NodeCertificate}; -use crate::{ - dag::{ - adapter::TLedgerInfoProvider, - dag_fetcher::TFetchRequester, - errors::DagDriverError, - observability::{ - counters::{self, NODE_PAYLOAD_SIZE, NUM_TXNS_PER_NODE}, - logging::{LogEvent, LogSchema}, - tracing::{observe_node, observe_round, NodeStage, RoundStage}, - }, - order_rule::OrderRule, - round_state::RoundState, - storage::DAGStorage, - types::{ - CertificateAckState, CertifiedAck, CertifiedNode, CertifiedNodeMessage, DAGMessage, - Extensions, Node, SignatureBuilder, - }, - DAGRpcResult, RpcHandler, - }, - payload_client::PayloadClient, -}; -use anyhow::{bail, ensure}; -use aptos_collections::BoundedVecDeque; -use aptos_config::config::DagPayloadConfig; -use aptos_consensus_types::common::{Author, Payload, PayloadFilter}; -use aptos_crypto::hash::CryptoHash; -use aptos_infallible::Mutex; -use aptos_logger::{debug, error}; -use aptos_reliable_broadcast::{DropGuard, ReliableBroadcast}; -use aptos_time_service::{TimeService, TimeServiceTrait}; -use aptos_types::{block_info::Round, epoch_state::EpochState}; -use aptos_validator_transaction_pool as vtxn_pool; -use async_trait::async_trait; -use futures::{ - executor::block_on, - future::{join, AbortHandle, Abortable}, -}; -use futures_channel::oneshot; -use std::{collections::HashSet, sync::Arc, time::Duration}; -use tokio_retry::strategy::ExponentialBackoff; - -pub(crate) struct DagDriver { - author: Author, - epoch_state: Arc, - dag: Arc, - payload_client: Arc, - reliable_broadcast: Arc>, - time_service: TimeService, - rb_handles: Mutex>, - storage: Arc, - order_rule: Arc>, - fetch_requester: Arc, - ledger_info_provider: Arc, - round_state: RoundState, - window_size_config: Round, - payload_config: DagPayloadConfig, - health_backoff: HealthBackoff, - quorum_store_enabled: bool, - allow_batches_without_pos_in_proposal: bool, -} - -impl DagDriver { - #[allow(clippy::too_many_arguments)] - pub fn new( - author: Author, - epoch_state: Arc, - dag: Arc, - payload_client: Arc, - reliable_broadcast: Arc>, - time_service: TimeService, - storage: Arc, - order_rule: Arc>, - fetch_requester: Arc, - ledger_info_provider: Arc, - round_state: RoundState, - window_size_config: Round, - payload_config: DagPayloadConfig, - health_backoff: HealthBackoff, - quorum_store_enabled: bool, - allow_batches_without_pos_in_proposal: bool, - ) -> Self { - let pending_node = storage - .get_pending_node() - .expect("should be able to read dag storage"); - let highest_strong_links_round = - dag.read().highest_strong_links_round(&epoch_state.verifier); - - let driver = Self { - author, - epoch_state, - dag, - payload_client, - reliable_broadcast, - time_service, - rb_handles: Mutex::new(BoundedVecDeque::new(window_size_config as usize)), - storage, - order_rule, - fetch_requester, - ledger_info_provider, - round_state, - window_size_config, - payload_config, - health_backoff, - quorum_store_enabled, - allow_batches_without_pos_in_proposal, - }; - - // If we were broadcasting the node for the round already, resume it - if let Some(node) = - pending_node.filter(|node| node.round() == highest_strong_links_round + 1) - { - debug!( - LogSchema::new(LogEvent::NewRound).round(node.round()), - "Resume round" - ); - driver - .round_state - .set_current_round(node.round()) - .expect("must succeed"); - driver.broadcast_node(node); - } else { - // kick start a new round - if !driver.dag.read().is_empty() { - block_on(driver.enter_new_round(highest_strong_links_round + 1)); - } - } - driver - } - - fn add_node(&self, node: CertifiedNode) -> anyhow::Result<()> { - { - let dag_reader = self.dag.read(); - - // Ensure the window hasn't moved, so we don't request fetch unnecessarily. - ensure!(node.round() >= dag_reader.lowest_round(), "stale node"); - - if !dag_reader.all_exists(node.parents_metadata()) { - if let Err(err) = self.fetch_requester.request_for_certified_node(node) { - error!("request to fetch failed: {}", err); - } - bail!(DagDriverError::MissingParents); - } - } - - // Note on concurrency: it is possible that a prune operation kicks in here and - // moves the window forward making the `node` stale, but we guarantee that the - // order rule only visits `window` length rounds, so having node around should - // be fine. Any stale node inserted due to this race will be cleaned up with - // the next prune operation. - - self.dag.add_node(node)?; - - self.check_new_round(); - Ok(()) - } - - fn check_new_round(&self) { - let (highest_strong_link_round, strong_links) = self.get_highest_strong_links_round(); - - let minimum_delay = self - .health_backoff - .backoff_duration(highest_strong_link_round + 1); - self.round_state.check_for_new_round( - highest_strong_link_round, - strong_links, - minimum_delay, - ); - } - - fn get_highest_strong_links_round(&self) -> (Round, Vec) { - let dag_reader = self.dag.read(); - let highest_strong_links_round = - dag_reader.highest_strong_links_round(&self.epoch_state.verifier); - ( - highest_strong_links_round, - // unwrap is for round 0 - dag_reader - .get_strong_links_for_round(highest_strong_links_round, &self.epoch_state.verifier) - .unwrap_or_default(), - ) - } - - pub async fn enter_new_round(&self, new_round: Round) { - if let Err(e) = self.round_state.set_current_round(new_round) { - debug!(error=?e, "cannot enter round"); - return; - } - - let (strong_links, sys_payload_filter, payload_filter) = { - let dag_reader = self.dag.read(); - - let highest_strong_links_round = - dag_reader.highest_strong_links_round(&self.epoch_state.verifier); - if new_round.saturating_sub(highest_strong_links_round) == 0 { - debug!( - new_round = new_round, - highest_strong_link_round = highest_strong_links_round, - "new round too stale to enter" - ); - return; - } - - debug!(LogSchema::new(LogEvent::NewRound).round(new_round)); - counters::CURRENT_ROUND.set(new_round as i64); - - let strong_links = dag_reader - .get_strong_links_for_round(new_round - 1, &self.epoch_state.verifier) - .unwrap_or_else(|| { - assert_eq!(new_round, 1, "Only expect empty strong links for round 1"); - vec![] - }); - - if strong_links.is_empty() { - ( - strong_links, - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - ) - } else { - let highest_commit_round = self - .ledger_info_provider - .get_highest_committed_anchor_round(); - - let nodes = dag_reader - .reachable( - strong_links.iter().map(|node| node.metadata()), - Some(highest_commit_round.saturating_sub(self.window_size_config)), - |_| true, - ) - .map(|node_status| node_status.as_node()) - .collect::>(); - - let payload_filter = - PayloadFilter::from(&nodes.iter().map(|node| node.payload()).collect()); - let validator_txn_hashes = nodes - .iter() - .flat_map(|node| node.validator_txns()) - .map(|txn| txn.hash()); - let validator_payload_filter = vtxn_pool::TransactionFilter::PendingTxnHashSet( - HashSet::from_iter(validator_txn_hashes), - ); - - (strong_links, validator_payload_filter, payload_filter) - } - }; - - let (max_txns, max_size_bytes) = self - .health_backoff - .calculate_payload_limits(new_round, &self.payload_config); - - let (validator_txns, payload) = match self - .payload_client - .pull_payload( - Duration::from_millis(self.payload_config.payload_pull_max_poll_time_ms), - max_txns, - max_size_bytes, - // TODO: Set max_inline_items and max_inline_bytes correctly - 100, - 100 * 1024, - sys_payload_filter, - payload_filter, - Box::pin(async {}), - false, - 0, - 0.0, - ) - .await - { - Ok(payload) => payload, - Err(e) => { - error!("error pulling payload: {}", e); - ( - vec![], - Payload::empty( - self.quorum_store_enabled, - self.allow_batches_without_pos_in_proposal, - ), - ) - }, - }; - - // TODO: need to wait to pass median of parents timestamp - let highest_parent_timestamp = strong_links - .iter() - .map(|node| node.metadata().timestamp()) - .max() - .unwrap_or(0); - let timestamp = std::cmp::max( - self.time_service.now_unix_time().as_micros() as u64, - highest_parent_timestamp + 1, - ); - let new_node = Node::new( - self.epoch_state.epoch, - new_round, - self.author, - timestamp, - validator_txns, - payload, - strong_links, - Extensions::empty(), - ); - self.storage - .save_pending_node(&new_node) - .expect("node must be saved"); - self.broadcast_node(new_node); - } - - fn broadcast_node(&self, node: Node) { - let rb = self.reliable_broadcast.clone(); - let rb2 = self.reliable_broadcast.clone(); - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - let (tx, rx) = oneshot::channel(); - let signature_builder = - SignatureBuilder::new(node.metadata().clone(), self.epoch_state.clone(), tx); - let cert_ack_set = CertificateAckState::new(self.epoch_state.verifier.len()); - let latest_ledger_info = self.ledger_info_provider.clone(); - - let round = node.round(); - let node_clone = node.clone(); - let timestamp = node.timestamp(); - let node_broadcast = async move { - debug!(LogSchema::new(LogEvent::BroadcastNode), id = node.id()); - - defer!( observe_round(timestamp, RoundStage::NodeBroadcasted); ); - rb.broadcast(node, signature_builder).await - }; - let certified_broadcast = async move { - let Ok(certificate) = rx.await else { - error!("channel closed before receiving ceritifcate"); - return; - }; - - debug!( - LogSchema::new(LogEvent::BroadcastCertifiedNode), - id = node_clone.id() - ); - - defer!( observe_round(timestamp, RoundStage::CertifiedNodeBroadcasted); ); - let certified_node = - CertifiedNode::new(node_clone, certificate.signatures().to_owned()); - let certified_node_msg = CertifiedNodeMessage::new( - certified_node, - latest_ledger_info.get_latest_ledger_info(), - ); - rb2.broadcast(certified_node_msg, cert_ack_set).await - }; - let core_task = join(node_broadcast, certified_broadcast); - let author = self.author; - let task = async move { - debug!("{} Start reliable broadcast for round {}", author, round); - core_task.await; - debug!("Finish reliable broadcast for round {}", round); - }; - tokio::spawn(Abortable::new(task, abort_registration)); - // TODO: a bounded vec queue can hold more than window rounds, but we want to limit - // by number of rounds. - if let Some((_handle, prev_round_timestamp)) = self - .rb_handles - .lock() - .push_back((DropGuard::new(abort_handle), timestamp)) - { - // TODO: this observation is inaccurate. - observe_round(prev_round_timestamp, RoundStage::Finished); - } - } - - pub fn fetch_callback(&self) { - self.order_rule.lock().process_all(); - self.check_new_round(); - } -} - -#[async_trait] -impl RpcHandler for DagDriver { - type Request = CertifiedNode; - type Response = CertifiedAck; - - async fn process(&self, certified_node: Self::Request) -> anyhow::Result { - let epoch = certified_node.metadata().epoch(); - debug!(LogSchema::new(LogEvent::ReceiveCertifiedNode) - .remote_peer(*certified_node.author()) - .round(certified_node.round())); - if self.dag.read().exists(certified_node.metadata()) { - return Ok(CertifiedAck::new(epoch)); - } - - observe_node(certified_node.timestamp(), NodeStage::CertifiedNodeReceived); - NUM_TXNS_PER_NODE.observe(certified_node.payload().len() as f64); - NODE_PAYLOAD_SIZE.observe(certified_node.payload().size() as f64); - - let node_metadata = certified_node.metadata().clone(); - self.add_node(certified_node) - .map(|_| self.order_rule.lock().process_new_node(&node_metadata))?; - - Ok(CertifiedAck::new(epoch)) - } -} diff --git a/consensus/src/dag/dag_fetcher.rs b/consensus/src/dag/dag_fetcher.rs deleted file mode 100644 index 43f7b456e16f0..0000000000000 --- a/consensus/src/dag/dag_fetcher.rs +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{dag_store::DagStore, errors::DagFetchError, DAGRpcResult}; -use crate::dag::{ - dag_network::{RpcResultWithResponder, TDAGNetworkSender}, - errors::FetchRequestHandleError, - observability::logging::{LogEvent, LogSchema}, - types::{CertifiedNode, FetchResponse, Node, NodeMetadata, RemoteFetchRequest}, - RpcHandler, RpcWithFallback, -}; -use anyhow::{bail, ensure}; -use aptos_bitvec::BitVec; -use aptos_config::config::DagFetcherConfig; -use aptos_consensus_types::common::{Author, Round}; -use aptos_logger::{debug, error, info}; -use aptos_time_service::TimeService; -use aptos_types::epoch_state::EpochState; -use async_trait::async_trait; -use futures::{future::Shared, stream::FuturesUnordered, Future, FutureExt, Stream, StreamExt}; -use std::{ - collections::HashMap, - pin::Pin, - sync::Arc, - task::{Context, Poll}, - time::Duration, -}; -use tokio::{ - select, - sync::{ - mpsc::{Receiver, Sender}, - oneshot, - }, -}; - -pub struct FetchWaiter { - rx: Receiver>, - futures: Pin>>>, -} - -impl FetchWaiter { - fn new(rx: Receiver>) -> Self { - Self { - rx, - futures: Box::pin(FuturesUnordered::new()), - } - } -} - -impl Stream for FetchWaiter { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Poll::Ready(Some(rx)) = self.rx.poll_recv(cx) { - self.futures.push(rx); - } - - self.futures.as_mut().poll_next(cx) - } -} - -pub trait TFetchRequester: Send + Sync { - fn request_for_node(&self, node: Node) -> anyhow::Result<()>; - fn request_for_certified_node(&self, node: CertifiedNode) -> anyhow::Result<()>; -} - -pub struct FetchRequester { - request_tx: Sender, - node_waiter_tx: Sender>, - certified_node_waiter_tx: Sender>, -} - -impl TFetchRequester for FetchRequester { - fn request_for_node(&self, node: Node) -> anyhow::Result<()> { - let (res_tx, res_rx) = oneshot::channel(); - let fetch_req = LocalFetchRequest::Node(node, res_tx); - self.request_tx - .try_send(fetch_req) - .map_err(|e| anyhow::anyhow!("unable to send node fetch request to channel: {}", e))?; - self.node_waiter_tx.try_send(res_rx)?; - Ok(()) - } - - fn request_for_certified_node(&self, node: CertifiedNode) -> anyhow::Result<()> { - let (res_tx, res_rx) = oneshot::channel(); - let fetch_req = LocalFetchRequest::CertifiedNode(node, res_tx); - self.request_tx.try_send(fetch_req).map_err(|e| { - anyhow::anyhow!( - "unable to send certified node fetch request to channel: {}", - e - ) - })?; - self.certified_node_waiter_tx.try_send(res_rx)?; - Ok(()) - } -} - -#[derive(Debug)] -pub enum LocalFetchRequest { - Node(Node, oneshot::Sender), - CertifiedNode(CertifiedNode, oneshot::Sender), -} - -impl LocalFetchRequest { - pub fn responders(&self, validators: &[Author]) -> Vec { - match self { - LocalFetchRequest::Node(node, _) => vec![*node.author()], - LocalFetchRequest::CertifiedNode(node, _) => { - node.signatures().get_signers_addresses(validators) - }, - } - } - - pub fn notify(self) { - if match self { - LocalFetchRequest::Node(node, sender) => sender.send(node).map_err(|_| ()), - LocalFetchRequest::CertifiedNode(node, sender) => sender.send(node).map_err(|_| ()), - } - .is_err() - { - error!("Failed to send node back"); - } - } - - pub fn node(&self) -> &Node { - match self { - LocalFetchRequest::Node(node, _) => node, - LocalFetchRequest::CertifiedNode(node, _) => node, - } - } -} - -pub struct DagFetcherService { - inner: Arc, - dag: Arc, - request_rx: Receiver, - ordered_authors: Vec, - inflight_requests: HashMap< - (Round, BitVec), - Shared> + Send>>>, - >, - futures: - FuturesUnordered> + Send>>>, - max_concurrent_fetches: usize, -} - -impl DagFetcherService { - pub fn new( - epoch_state: Arc, - network: Arc, - dag: Arc, - time_service: TimeService, - config: DagFetcherConfig, - ) -> ( - Self, - FetchRequester, - FetchWaiter, - FetchWaiter, - ) { - let (request_tx, request_rx) = tokio::sync::mpsc::channel(16); - let (node_tx, node_rx) = tokio::sync::mpsc::channel(100); - let (certified_node_tx, certified_node_rx) = tokio::sync::mpsc::channel(100); - let ordered_authors = epoch_state.verifier.get_ordered_account_addresses(); - ( - Self { - max_concurrent_fetches: config.max_concurrent_fetches, - inner: Arc::new(DagFetcher::new(epoch_state, network, time_service, config)), - dag, - request_rx, - ordered_authors, - inflight_requests: HashMap::new(), - futures: FuturesUnordered::new(), - }, - FetchRequester { - request_tx, - node_waiter_tx: node_tx, - certified_node_waiter_tx: certified_node_tx, - }, - FetchWaiter::new(node_rx), - FetchWaiter::new(certified_node_rx), - ) - } - - pub async fn start(mut self) { - loop { - select! { - Some(result) = self.futures.next() => { - match result { - Ok(local_request) => local_request.notify(), - Err(err) => error!("unable to complete fetch successfully: {}", err), - } - }, - // TODO: Configure concurrency - Some(local_request) = self.request_rx.recv(), if self.futures.len() < self.max_concurrent_fetches => { - match self.fetch(local_request.node(), local_request.responders(&self.ordered_authors)) { - Ok(fut) => { - self.futures.push(async move { - fut.await?; - Ok(local_request) - }.boxed()) - }, - Err(err) => error!("unable to initiate fetch successfully: {}", err), - } - }, - else => { - info!("Dag Fetch Service exiting."); - return; - } - } - } - } - - pub(super) fn fetch( - &mut self, - node: &Node, - responders: Vec, - ) -> anyhow::Result>>> { - let remote_request = { - let dag_reader = self.dag.read(); - ensure!( - node.round() >= dag_reader.lowest_incomplete_round(), - "Already synced beyond requested round {}, lowest incomplete round {}", - node.round(), - dag_reader.lowest_incomplete_round() - ); - - let missing_parents: Vec = dag_reader - .filter_missing(node.parents_metadata()) - .cloned() - .collect(); - - if missing_parents.is_empty() { - return Ok(async { Ok(()) }.boxed().shared()); - } - - RemoteFetchRequest::new( - node.metadata().epoch(), - missing_parents, - dag_reader.bitmask(node.round().saturating_sub(1)), - ) - }; - - let target_round = remote_request.target_round(); - let Some(bitmap) = remote_request.exists_bitmask().bitvec(target_round) else { - bail!( - "cannot get bitmap for target_round {} in {:?}", - target_round, - remote_request.exists_bitmask() - ); - }; - - let future = self - .inflight_requests - .entry((target_round, bitmap)) - .or_insert_with(|| { - let fetcher = self.inner.clone(); - let dag_clone = self.dag.clone(); - async move { fetcher.fetch(remote_request, responders, dag_clone).await } - .boxed() - .shared() - }) - .clone(); - - Ok(future) - } -} - -#[async_trait] -pub trait TDagFetcher: Send { - async fn fetch( - &self, - remote_request: RemoteFetchRequest, - responders: Vec, - dag: Arc, - ) -> Result<(), DagFetchError>; -} - -pub(crate) struct DagFetcher { - network: Arc, - time_service: TimeService, - epoch_state: Arc, - config: DagFetcherConfig, -} - -impl DagFetcher { - pub(crate) fn new( - epoch_state: Arc, - network: Arc, - time_service: TimeService, - config: DagFetcherConfig, - ) -> Self { - Self { - network, - time_service, - epoch_state, - config, - } - } -} - -#[async_trait] -impl TDagFetcher for DagFetcher { - async fn fetch( - &self, - remote_request: RemoteFetchRequest, - responders: Vec, - dag: Arc, - ) -> Result<(), DagFetchError> { - debug!( - LogSchema::new(LogEvent::FetchNodes), - start_round = remote_request.start_round(), - target_round = remote_request.target_round(), - lens = remote_request.exists_bitmask().len(), - missing_nodes = remote_request.exists_bitmask().num_missing(), - ); - let mut rpc = RpcWithFallback::new( - responders, - remote_request.clone().into(), - Duration::from_millis(self.config.retry_interval_ms), - Duration::from_millis(self.config.rpc_timeout_ms), - self.network.clone(), - self.time_service.clone(), - self.config.min_concurrent_responders, - self.config.max_concurrent_responders, - ); - - while let Some(RpcResultWithResponder { responder, result }) = rpc.next().await { - match result { - Ok(DAGRpcResult(Ok(response))) => { - match FetchResponse::try_from(response).and_then(|response| { - response.verify(&remote_request, &self.epoch_state.verifier) - }) { - Ok(fetch_response) => { - let certified_nodes = fetch_response.certified_nodes(); - // TODO: support chunk response or fallback to state sync - { - for node in certified_nodes.into_iter().rev() { - if let Err(e) = dag.add_node(node) { - error!(error = ?e, "failed to add node"); - } - } - } - - if dag.read().all_exists(remote_request.targets()) { - return Ok(()); - } - }, - Err(err) => { - info!(error = ?err, "failure parsing/verifying fetch response from {}", responder); - }, - }; - }, - Ok(DAGRpcResult(Err(dag_rpc_error))) => { - info!(error = ?dag_rpc_error, responder = responder, "fetch failure: target {} returned error", responder); - }, - Err(err) => { - info!(error = ?err, responder = responder, "rpc failed to {}", responder); - }, - } - } - Err(DagFetchError::Failed) - } -} - -pub struct FetchRequestHandler { - dag: Arc, - author_to_index: HashMap, -} - -impl FetchRequestHandler { - pub fn new(dag: Arc, epoch_state: Arc) -> Self { - Self { - dag, - author_to_index: epoch_state.verifier.address_to_validator_index().clone(), - } - } -} - -#[async_trait] -impl RpcHandler for FetchRequestHandler { - type Request = RemoteFetchRequest; - type Response = FetchResponse; - - async fn process(&self, message: Self::Request) -> anyhow::Result { - let dag_reader = self.dag.read(); - - // `Certified Node`: In the good case, there should exist at least one honest validator that - // signed the Certified Node that has the all the parents to fulfil this - // request. - // `Node`: In the good case, the sender of the Node should have the parents in its local DAG - // to satisfy this request. - debug!( - LogSchema::new(LogEvent::ReceiveFetchNodes).round(dag_reader.highest_round()), - start_round = message.start_round(), - target_round = message.target_round(), - ); - ensure!( - dag_reader.lowest_round() <= message.start_round(), - FetchRequestHandleError::GarbageCollected( - message.start_round(), - dag_reader.lowest_round() - ), - ); - - let missing_targets: BitVec = message - .targets() - .map(|node| !dag_reader.exists(node)) - .collect(); - ensure!( - missing_targets.all_zeros(), - FetchRequestHandleError::TargetsMissing(missing_targets) - ); - - let certified_nodes: Vec<_> = dag_reader - .reachable( - message.targets(), - Some(message.exists_bitmask().first_round()), - |_| true, - ) - .filter_map(|node_status| { - let arc_node = node_status.as_node(); - self.author_to_index - .get(arc_node.author()) - .and_then(|author_idx| { - if !message.exists_bitmask().has(arc_node.round(), *author_idx) { - Some(arc_node.as_ref().clone()) - } else { - None - } - }) - }) - .collect(); - - // TODO: decide if the response is too big and act accordingly. - - Ok(FetchResponse::new(message.epoch(), certified_nodes)) - } -} diff --git a/consensus/src/dag/dag_handler.rs b/consensus/src/dag/dag_handler.rs deleted file mode 100644 index dc085d627db06..0000000000000 --- a/consensus/src/dag/dag_handler.rs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - dag::{ - dag_driver::DagDriver, - dag_fetcher::{FetchRequestHandler, FetchWaiter}, - dag_network::RpcHandler, - dag_state_sync::{StateSyncTrigger, SyncOutcome}, - errors::{ - DAGError, DAGRpcError, DagDriverError, FetchRequestHandleError, - NodeBroadcastHandleError, - }, - rb_handler::NodeBroadcastHandler, - types::{DAGMessage, DAGRpcResult}, - CertifiedNode, Node, - }, - monitor, - network::{IncomingDAGRequest, RpcResponder}, -}; -use aptos_bounded_executor::{concurrent_map, BoundedExecutor}; -use aptos_channels::aptos_channel; -use aptos_consensus_types::common::{Author, Round}; -use aptos_logger::{debug, error, warn}; -use aptos_types::epoch_state::EpochState; -use futures::{stream::FuturesUnordered, StreamExt}; -use std::sync::Arc; -use tokio::{runtime::Handle, select}; - -pub(crate) struct NetworkHandler { - epoch_state: Arc, - node_receiver: Arc, - dag_driver: Arc, - node_fetch_waiter: FetchWaiter, - certified_node_fetch_waiter: FetchWaiter, - new_round_event: tokio::sync::mpsc::UnboundedReceiver, - verified_msg_processor: Arc, -} - -impl NetworkHandler { - pub(super) fn new( - epoch_state: Arc, - node_receiver: NodeBroadcastHandler, - dag_driver: DagDriver, - fetch_receiver: FetchRequestHandler, - node_fetch_waiter: FetchWaiter, - certified_node_fetch_waiter: FetchWaiter, - state_sync_trigger: StateSyncTrigger, - new_round_event: tokio::sync::mpsc::UnboundedReceiver, - ) -> Self { - let node_receiver = Arc::new(node_receiver); - let dag_driver = Arc::new(dag_driver); - Self { - epoch_state: epoch_state.clone(), - node_receiver: node_receiver.clone(), - dag_driver: dag_driver.clone(), - node_fetch_waiter, - certified_node_fetch_waiter, - new_round_event, - verified_msg_processor: Arc::new(VerifiedMessageProcessor { - node_receiver, - dag_driver, - fetch_receiver, - state_sync_trigger, - epoch_state, - }), - } - } - - pub async fn run( - self, - dag_rpc_rx: &mut aptos_channel::Receiver, - executor: BoundedExecutor, - _buffer: Vec, - ) -> SyncOutcome { - // TODO: process buffer - let NetworkHandler { - epoch_state, - node_receiver, - dag_driver, - mut node_fetch_waiter, - mut certified_node_fetch_waiter, - mut new_round_event, - verified_msg_processor, - .. - } = self; - - // TODO: feed in the executor based on verification Runtime - let mut verified_msg_stream = concurrent_map( - dag_rpc_rx, - executor.clone(), - move |rpc_request: IncomingDAGRequest| { - let epoch_state = epoch_state.clone(); - async move { - let epoch = rpc_request.req.epoch(); - let result = rpc_request - .req - .try_into() - .and_then(|dag_message: DAGMessage| { - monitor!( - "dag_message_verify", - dag_message.verify(rpc_request.sender, &epoch_state.verifier) - )?; - Ok(dag_message) - }); - (result, epoch, rpc_request.sender, rpc_request.responder) - } - }, - ); - - let dag_driver_clone = dag_driver.clone(); - let node_receiver_clone = node_receiver.clone(); - let handle = tokio::spawn(async move { - while let Some(new_round) = new_round_event.recv().await { - monitor!("dag_on_new_round_event", { - dag_driver_clone.enter_new_round(new_round).await; - node_receiver_clone.gc(); - }); - } - }); - defer!(handle.abort()); - - let mut futures = FuturesUnordered::new(); - // A separate executor to ensure the message verification sender (above) and receiver (below) are - // not blocking each other. - // TODO: make this configurable - let executor = BoundedExecutor::new(8, Handle::current()); - loop { - select! { - Some((msg, epoch, author, responder)) = verified_msg_stream.next() => { - let verified_msg_processor = verified_msg_processor.clone(); - let f = executor.spawn(async move { - monitor!("dag_on_verified_msg", { - match verified_msg_processor.process_verified_message(msg, epoch, author, responder).await { - Ok(sync_status) => { - if matches!( - sync_status, - SyncOutcome::NeedsSync(_) | SyncOutcome::EpochEnds - ) { - return Some(sync_status); - } - }, - Err(e) => { - warn!(error = ?e, "error processing rpc"); - }, - }; - None - }) - }).await; - futures.push(f); - }, - Some(status) = futures.next() => { - if let Some(status) = status.expect("future must not panic") { - return status; - } - }, - Some(result) = certified_node_fetch_waiter.next() => { - let dag_driver_clone = dag_driver.clone(); - executor.spawn(async move { - monitor!("dag_on_cert_node_fetch", match result { - Ok(certified_node) => { - if let Err(e) = dag_driver_clone.process(certified_node).await { - warn!(error = ?e, "error processing certified node fetch notification"); - } else { - dag_driver_clone.fetch_callback(); - } - }, - Err(e) => { - debug!("sender dropped channel: {}", e); - }, - }); - }).await; - }, - Some(result) = node_fetch_waiter.next() => { - let node_receiver_clone = node_receiver.clone(); - let dag_driver_clone = dag_driver.clone(); - executor.spawn(async move { - monitor!("dag_on_node_fetch", match result { - Ok(node) => { - if let Err(e) = node_receiver_clone.process(node).await { - warn!(error = ?e, "error processing node fetch notification"); - } else { - dag_driver_clone.fetch_callback(); - } - }, - Err(e) => { - debug!("sender dropped channel: {}", e); - }, - }); - }).await; - }, - } - } - } -} - -struct VerifiedMessageProcessor { - node_receiver: Arc, - dag_driver: Arc, - fetch_receiver: FetchRequestHandler, - state_sync_trigger: StateSyncTrigger, - epoch_state: Arc, -} - -impl VerifiedMessageProcessor { - async fn process_verified_message( - &self, - dag_message_result: anyhow::Result, - epoch: u64, - author: Author, - responder: RpcResponder, - ) -> anyhow::Result { - let response: Result = { - match dag_message_result { - Ok(dag_message) => { - debug!( - epoch = epoch, - author = author, - message = dag_message, - "Verified DAG message" - ); - match dag_message { - DAGMessage::NodeMsg(node) => monitor!( - "dag_on_node_msg", - self.node_receiver - .process(node) - .await - .map(|r| r.into()) - .map_err(|err| { - err.downcast::() - .map_or(DAGError::Unknown, |err| { - DAGError::NodeBroadcastHandleError(err) - }) - }) - ), - DAGMessage::CertifiedNodeMsg(certified_node_msg) => { - monitor!("dag_on_cert_node_msg", { - match self.state_sync_trigger.check(certified_node_msg).await? { - SyncOutcome::Synced(Some(certified_node_msg)) => self - .dag_driver - .process(certified_node_msg.certified_node()) - .await - .map(|r| r.into()) - .map_err(|err| { - err.downcast::() - .map_or(DAGError::Unknown, |err| { - DAGError::DagDriverError(err) - }) - }), - status @ (SyncOutcome::NeedsSync(_) - | SyncOutcome::EpochEnds) => return Ok(status), - _ => unreachable!(), - } - }) - }, - DAGMessage::FetchRequest(request) => monitor!( - "dag_on_fetch_request", - self.fetch_receiver - .process(request) - .await - .map(|r| r.into()) - .map_err(|err| { - err.downcast::().map_or( - DAGError::Unknown, - DAGError::FetchRequestHandleError, - ) - }) - ), - _ => unreachable!("verification must catch this error"), - } - }, - Err(err) => { - error!(error = ?err, "DAG message verification failed"); - Err(DAGError::MessageVerificationError) - }, - } - }; - - debug!( - epoch = epoch, - sender = author, - response = response.as_ref().map(|r| r.name()), - "RPC response" - ); - - let response: DAGRpcResult = response - .map_err(|e| DAGRpcError::new(self.epoch_state.epoch, e)) - .into(); - responder.respond(response)?; - - Ok(SyncOutcome::Synced(None)) - } -} diff --git a/consensus/src/dag/dag_network.rs b/consensus/src/dag/dag_network.rs deleted file mode 100644 index d3c1ce5d2b11c..0000000000000 --- a/consensus/src/dag/dag_network.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright © Aptos Foundation - -use super::{types::DAGMessage, DAGRpcResult}; -use aptos_consensus_types::common::Author; -use aptos_reliable_broadcast::RBNetworkSender; -use aptos_time_service::{Interval, TimeService, TimeServiceTrait}; -use async_trait::async_trait; -use futures::{ - stream::{FusedStream, FuturesUnordered}, - Future, Stream, -}; -use rand::seq::SliceRandom; -use std::{ - pin::Pin, - sync::Arc, - task::{Context, Poll}, - time::Duration, -}; - -#[async_trait] -pub trait RpcHandler { - type Request; - type Response; - - async fn process(&self, message: Self::Request) -> anyhow::Result; -} - -#[async_trait] -pub trait TDAGNetworkSender: Send + Sync + RBNetworkSender { - async fn send_rpc( - &self, - receiver: Author, - message: DAGMessage, - timeout: Duration, - ) -> anyhow::Result; - - /// Given a list of potential responders, sending rpc to get response from any of them and could - /// fallback to more in case of failures. - async fn send_rpc_with_fallbacks( - self: Arc, - responders: Vec, - message: DAGMessage, - retry_interval: Duration, - rpc_timeout: Duration, - min_concurrent_responders: u32, - max_concurrent_responders: u32, - ) -> RpcWithFallback; -} - -struct Responders { - peers: Vec, - generator: ExponentialNumberGenerator, -} - -impl Responders { - fn new(mut peers: Vec, initial_request_count: u32, max_request_count: u32) -> Self { - peers.shuffle(&mut rand::thread_rng()); - Self { - peers, - generator: ExponentialNumberGenerator::new(initial_request_count, 2, max_request_count), - } - } - - fn next_to_request(&mut self) -> Option> { - let count = self.generator.next().expect("should return a number"); - - if self.peers.is_empty() { - return None; - } - Some( - self.peers - .split_off(self.peers.len().saturating_sub(count as usize)), - ) - } -} - -pub struct RpcResultWithResponder { - pub result: anyhow::Result, - pub responder: Author, -} - -pub struct RpcWithFallback { - responders: Responders, - message: DAGMessage, - rpc_timeout: Duration, - - terminated: bool, - futures: - Pin + Send>>>>>, - sender: Arc, - interval: Pin>, -} - -impl RpcWithFallback { - pub fn new( - responders: Vec, - message: DAGMessage, - retry_interval: Duration, - rpc_timeout: Duration, - sender: Arc, - time_service: TimeService, - min_concurrent_responders: u32, - max_concurrent_responders: u32, - ) -> Self { - Self { - responders: Responders::new( - responders, - min_concurrent_responders, - max_concurrent_responders, - ), - message, - rpc_timeout, - - terminated: false, - futures: Box::pin(FuturesUnordered::new()), - sender, - interval: Box::pin(time_service.interval(retry_interval)), - } - } -} - -async fn send_rpc( - sender: Arc, - peer: Author, - message: DAGMessage, - timeout: Duration, -) -> RpcResultWithResponder { - RpcResultWithResponder { - responder: peer, - result: sender.send_rpc(peer, message, timeout).await, - } -} - -impl Stream for RpcWithFallback { - type Item = RpcResultWithResponder; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if !self.futures.is_empty() { - // Check if any of the futures is ready - if let Poll::Ready(result) = self.futures.as_mut().poll_next(cx) { - return Poll::Ready(result); - } - } - - // Check if the timeout has happened - let timeout = matches!(self.interval.as_mut().poll_next(cx), Poll::Ready(_)); - - if self.futures.is_empty() || timeout { - // try to find more responders and queue futures - if let Some(peers) = Pin::new(&mut self.responders).next_to_request() { - for peer in peers { - let future = Box::pin(send_rpc( - self.sender.clone(), - peer, - self.message.clone(), - self.rpc_timeout, - )); - self.futures.push(future); - } - } else if self.futures.is_empty() { - self.terminated = true; - return Poll::Ready(None); - } - } - - self.futures.as_mut().poll_next(cx) - } -} - -impl FusedStream for RpcWithFallback { - fn is_terminated(&self) -> bool { - self.terminated - } -} - -struct ExponentialNumberGenerator { - current: u32, - factor: u32, - max_limit: u32, -} - -impl ExponentialNumberGenerator { - fn new(starting_value: u32, factor: u32, max_limit: u32) -> Self { - Self { - current: starting_value, - factor, - max_limit, - } - } -} - -impl Iterator for ExponentialNumberGenerator { - type Item = u32; - - fn next(&mut self) -> Option { - let result = self.current; - if self.current < self.max_limit { - self.current = self - .current - .checked_mul(self.factor) - .unwrap_or(self.max_limit) - .min(self.max_limit) - } - - Some(result) - } -} diff --git a/consensus/src/dag/dag_state_sync.rs b/consensus/src/dag/dag_state_sync.rs deleted file mode 100644 index 25eb605aad564..0000000000000 --- a/consensus/src/dag/dag_state_sync.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright © Aptos Foundation - -use super::{ - adapter::TLedgerInfoProvider, - dag_fetcher::TDagFetcher, - dag_store::DagStore, - storage::DAGStorage, - types::{CertifiedNodeMessage, RemoteFetchRequest}, - ProofNotifier, -}; -use crate::{ - dag::DAGMessage, network::IncomingDAGRequest, payload_manager::TPayloadManager, - pipeline::execution_client::TExecutionClient, -}; -use anyhow::{bail, ensure}; -use aptos_channels::aptos_channel; -use aptos_consensus_types::common::{Author, Round}; -use aptos_logger::{debug, error}; -use aptos_time_service::TimeService; -use aptos_types::{ - epoch_change::EpochChangeProof, epoch_state::EpochState, ledger_info::LedgerInfoWithSignatures, -}; -use core::fmt; -use futures::StreamExt; -use std::sync::Arc; - -#[derive(Debug)] -pub enum SyncOutcome { - NeedsSync(CertifiedNodeMessage), - Synced(Option), - EpochEnds, -} - -impl fmt::Display for SyncOutcome { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SyncOutcome::NeedsSync(_) => write!(f, "NeedsSync"), - SyncOutcome::Synced(_) => write!(f, "Synced"), - SyncOutcome::EpochEnds => write!(f, "EpochEnds"), - } - } -} - -pub(super) struct StateSyncTrigger { - epoch_state: Arc, - ledger_info_provider: Arc, - dag_store: Arc, - proof_notifier: Arc, - dag_window_size_config: Round, -} - -impl StateSyncTrigger { - pub(super) fn new( - epoch_state: Arc, - ledger_info_provider: Arc, - dag_store: Arc, - proof_notifier: Arc, - dag_window_size_config: Round, - ) -> Self { - Self { - epoch_state, - ledger_info_provider, - dag_store, - proof_notifier, - dag_window_size_config, - } - } - - fn verify_ledger_info(&self, ledger_info: &LedgerInfoWithSignatures) -> anyhow::Result<()> { - ensure!(ledger_info.commit_info().epoch() == self.epoch_state.epoch); - - if ledger_info.commit_info().round() > 0 { - ledger_info - .verify_signatures(&self.epoch_state.verifier) - .map_err(|e| anyhow::anyhow!("unable to verify ledger info: {}", e))?; - } - - Ok(()) - } - - /// This method checks if a state sync is required - pub(super) async fn check(&self, node: CertifiedNodeMessage) -> anyhow::Result { - let ledger_info_with_sigs = node.ledger_info(); - - self.notify_commit_proof(ledger_info_with_sigs).await; - - if !self.need_sync_for_ledger_info(ledger_info_with_sigs) { - return Ok(SyncOutcome::Synced(Some(node))); - } - - // Only verify the certificate if we need to sync - self.verify_ledger_info(ledger_info_with_sigs)?; - - if ledger_info_with_sigs.ledger_info().ends_epoch() { - self.proof_notifier - .send_epoch_change(EpochChangeProof::new( - vec![ledger_info_with_sigs.clone()], - /* more = */ false, - )) - .await; - return Ok(SyncOutcome::EpochEnds); - } - - Ok(SyncOutcome::NeedsSync(node)) - } - - /// Fast forward in the decoupled-execution pipeline if the block exists there - async fn notify_commit_proof(&self, ledger_info: &LedgerInfoWithSignatures) { - // if the anchor exists between ledger info round and highest ordered round - // Note: ledger info round <= highest ordered round - if self - .ledger_info_provider - .get_highest_committed_anchor_round() - < ledger_info.commit_info().round() - && self - .dag_store - .read() - .highest_ordered_anchor_round() - .unwrap_or_default() - >= ledger_info.commit_info().round() - { - self.proof_notifier - .send_commit_proof(ledger_info.clone()) - .await - } - } - - /// Check if we're far away from this ledger info and need to sync. - /// This ensures that the block referred by the ledger info is not in buffer manager. - fn need_sync_for_ledger_info(&self, li: &LedgerInfoWithSignatures) -> bool { - if li.commit_info().round() - <= self - .ledger_info_provider - .get_highest_committed_anchor_round() - { - return false; - } - - let dag_reader = self.dag_store.read(); - // check whether if DAG order round is behind the given ledger info committed round - // (meaning consensus is behind) or - // the local highest committed anchor round is 2*DAG_WINDOW behind the given ledger info round - // (meaning execution is behind the DAG window) - - // fetch can't work since nodes are garbage collected - dag_reader.is_empty() - || dag_reader.highest_round() + 1 + self.dag_window_size_config - < li.commit_info().round() - || self - .ledger_info_provider - .get_highest_committed_anchor_round() - + 2 * self.dag_window_size_config - < li.commit_info().round() - } -} - -pub(super) struct DagStateSynchronizer { - epoch_state: Arc, - time_service: TimeService, - execution_client: Arc, - storage: Arc, - payload_manager: Arc, - dag_window_size_config: Round, -} - -impl DagStateSynchronizer { - pub fn new( - epoch_state: Arc, - time_service: TimeService, - execution_client: Arc, - storage: Arc, - payload_manager: Arc, - dag_window_size_config: Round, - ) -> Self { - Self { - epoch_state, - time_service, - execution_client, - storage, - payload_manager, - dag_window_size_config, - } - } - - pub(crate) fn build_request( - &self, - node: &CertifiedNodeMessage, - current_dag_store: Arc, - highest_committed_anchor_round: Round, - ) -> (RemoteFetchRequest, Vec, Arc) { - let commit_li = node.ledger_info(); - - { - let dag_reader = current_dag_store.read(); - assert!( - dag_reader - .highest_ordered_anchor_round() - .unwrap_or_default() - < commit_li.commit_info().round() - || highest_committed_anchor_round + self.dag_window_size_config - < commit_li.commit_info().round() - ); - } - - // TODO: there is a case where DAG fetches missing nodes in window and a crash happens and when we restart, - // we end up with a gap between the DAG and we need to be smart enough to clean up the DAG before the gap. - - // Create a new DAG store and Fetch blocks - let target_round = node.round(); - let start_round = commit_li - .commit_info() - .round() - .saturating_sub(self.dag_window_size_config); - let sync_dag_store = Arc::new(DagStore::new_empty( - self.epoch_state.clone(), - self.storage.clone(), - self.payload_manager.clone(), - start_round, - self.dag_window_size_config, - )); - let bitmask = { sync_dag_store.read().bitmask(target_round) }; - let request = RemoteFetchRequest::new( - self.epoch_state.epoch, - vec![node.metadata().clone()], - bitmask, - ); - - let responders = node - .certificate() - .signatures() - .get_signers_addresses(&self.epoch_state.verifier.get_ordered_account_addresses()); - - (request, responders, sync_dag_store) - } - - /// Note: Assumes that the sync checks have been done - pub async fn sync_dag_to( - &self, - dag_fetcher: impl TDagFetcher, - request: RemoteFetchRequest, - responders: Vec, - sync_dag_store: Arc, - commit_li: LedgerInfoWithSignatures, - ) -> anyhow::Result { - match dag_fetcher - .fetch(request, responders, sync_dag_store.clone()) - .await - { - Ok(_) => {}, - Err(err) => { - error!("error fetching nodes {}", err); - bail!(err) - }, - } - - self.execution_client.sync_to(commit_li).await?; - - Ok(Arc::into_inner(sync_dag_store).unwrap()) - } -} - -pub(crate) struct SyncModeMessageHandler { - epoch_state: Arc, - start_round: Round, - target_round: Round, - window: u64, -} - -impl SyncModeMessageHandler { - pub(crate) fn new( - epoch_state: Arc, - start_round: Round, - target_round: Round, - window: u64, - ) -> Self { - Self { - epoch_state, - start_round, - target_round, - window, - } - } - - pub(crate) async fn run( - mut self, - dag_rpc_rx: &mut aptos_channel::Receiver, - buffer: &mut Vec, - ) -> Option { - while let Some(msg) = dag_rpc_rx.next().await { - match self.process_rpc(msg, buffer) { - Ok(may_be_cert_node) => { - if let Some(next_sync_msg) = may_be_cert_node { - return Some(next_sync_msg); - } - }, - Err(err) => { - error!("error processing {}", err); - }, - } - } - None - } - - fn process_rpc( - &mut self, - rpc_request: IncomingDAGRequest, - buffer: &mut Vec, - ) -> anyhow::Result> { - let dag_message: DAGMessage = rpc_request.req.try_into()?; - - debug!( - "processing rpc message {} from {}", - dag_message.name(), - rpc_request.sender - ); - - match dag_message.verify(rpc_request.sender, &self.epoch_state.verifier) { - Ok(_) => match dag_message { - DAGMessage::NodeMsg(_) => { - debug!("ignoring node msg"); - }, - DAGMessage::CertifiedNodeMsg(ref cert_node_msg) => { - if cert_node_msg.round() < self.start_round { - debug!("ignoring stale certified node msg"); - } else if cert_node_msg.round() > self.target_round + (2 * self.window) { - debug!("cancelling current sync"); - return Ok(Some(cert_node_msg.clone())); - } else { - buffer.push(dag_message); - } - }, - DAGMessage::FetchRequest(_) => { - debug!("ignoring fetch msg"); - }, - _ => unreachable!("verification must catch this error"), - }, - Err(err) => { - error!(error = ?err, "error verifying message"); - return Err(err); - }, - }; - Ok(None) - } -} diff --git a/consensus/src/dag/dag_store.rs b/consensus/src/dag/dag_store.rs deleted file mode 100644 index e22b23f762ae8..0000000000000 --- a/consensus/src/dag/dag_store.rs +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - types::{DagSnapshotBitmask, NodeMetadata}, - Node, -}; -use crate::{ - dag::{ - storage::DAGStorage, - types::{CertifiedNode, NodeCertificate}, - }, - payload_manager::TPayloadManager, -}; -use anyhow::{anyhow, ensure}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::HashValue; -use aptos_infallible::RwLock; -use aptos_logger::{debug, error, warn}; -use aptos_types::{epoch_state::EpochState, validator_verifier::ValidatorVerifier}; -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - ops::Deref, - sync::Arc, -}; - -#[derive(Clone)] -pub enum NodeStatus { - Unordered { - node: Arc, - aggregated_weak_voting_power: u128, - aggregated_strong_voting_power: u128, - }, - Ordered(Arc), -} - -impl NodeStatus { - pub fn as_node(&self) -> &Arc { - match self { - NodeStatus::Unordered { node, .. } | NodeStatus::Ordered(node) => node, - } - } - - pub fn mark_as_ordered(&mut self) { - assert!(matches!(self, NodeStatus::Unordered { .. })); - *self = NodeStatus::Ordered(self.as_node().clone()); - } -} -/// Data structure that stores the in-memory DAG representation, it maintains round based index. -#[derive(Clone)] -pub struct InMemDag { - nodes_by_round: BTreeMap>>, - /// Map between peer id to vector index - author_to_index: HashMap, - start_round: Round, - epoch_state: Arc, - /// The window we maintain between highest committed round and initial round - window_size: u64, -} - -impl InMemDag { - pub fn new_empty(epoch_state: Arc, start_round: Round, window_size: u64) -> Self { - let author_to_index = epoch_state.verifier.address_to_validator_index().clone(); - let nodes_by_round = BTreeMap::new(); - Self { - nodes_by_round, - author_to_index, - start_round, - epoch_state, - window_size, - } - } - - pub(crate) fn lowest_round(&self) -> Round { - self.start_round - } - - pub fn highest_round(&self) -> Round { - // If stale nodes exist on the BTreeMap, ignore their rounds when calculating - // the highest round. - *self - .nodes_by_round - .last_key_value() - .map(|(round, _)| round) - .unwrap_or(&self.start_round) - .max(&self.start_round) - } - - /// The highest strong links round is either the highest round or the highest round - 1 - /// because we ensure all parents (strong links) exist for any nodes in the store - pub fn highest_strong_links_round(&self, validator_verifier: &ValidatorVerifier) -> Round { - let highest_round = self.highest_round(); - self.get_strong_links_for_round(highest_round, validator_verifier) - .map_or_else(|| highest_round.saturating_sub(1), |_| highest_round) - } - - #[cfg(test)] - pub fn add_node_for_test(&mut self, node: CertifiedNode) -> anyhow::Result<()> { - self.validate_new_node(&node)?; - self.add_validated_node(node) - } - - fn add_validated_node(&mut self, node: CertifiedNode) -> anyhow::Result<()> { - let round = node.round(); - ensure!( - round >= self.lowest_round(), - "dag was pruned. given round: {}, lowest round: {}", - round, - self.lowest_round() - ); - - let node = Arc::new(node); - let round_ref = self - .get_node_ref_mut(node.round(), node.author()) - .expect("must be present"); - ensure!(round_ref.is_none(), "race during insertion"); - *round_ref = Some(NodeStatus::Unordered { - node: node.clone(), - aggregated_weak_voting_power: 0, - aggregated_strong_voting_power: 0, - }); - self.update_votes(&node, true); - Ok(()) - } - - fn validate_new_node(&mut self, node: &CertifiedNode) -> anyhow::Result<()> { - ensure!( - node.epoch() == self.epoch_state.epoch, - "different epoch {}, current {}", - node.epoch(), - self.epoch_state.epoch - ); - let author = node.metadata().author(); - let index = *self - .author_to_index - .get(author) - .ok_or_else(|| anyhow!("unknown author"))?; - let round = node.metadata().round(); - ensure!( - round >= self.lowest_round(), - "round too low {}, lowest in dag {}", - round, - self.lowest_round() - ); - ensure!( - round <= self.highest_round() + 1, - "round too high {}, highest in dag {}", - round, - self.highest_round() - ); - if round > self.lowest_round() { - for parent in node.parents() { - ensure!(self.exists(parent.metadata()), "parent not exist"); - } - } - let round_ref = self - .nodes_by_round - .entry(round) - .or_insert_with(|| vec![None; self.author_to_index.len()]); - ensure!(round_ref[index].is_none(), "duplicate node"); - Ok(()) - } - - pub fn update_votes(&mut self, node: &Node, update_link_power: bool) { - if node.round() <= self.lowest_round() { - return; - } - - let voting_power = self - .epoch_state - .verifier - .get_voting_power(node.author()) - .expect("must exist"); - - for parent in node.parents_metadata() { - let node_status = self - .get_node_ref_mut(parent.round(), parent.author()) - .expect("must exist"); - match node_status { - Some(NodeStatus::Unordered { - aggregated_weak_voting_power, - aggregated_strong_voting_power, - .. - }) => { - if update_link_power { - *aggregated_strong_voting_power += voting_power as u128; - } else { - *aggregated_weak_voting_power += voting_power as u128; - } - }, - Some(NodeStatus::Ordered(_)) => {}, - None => unreachable!("parents must exist before voting for a node"), - } - } - } - - pub fn exists(&self, metadata: &NodeMetadata) -> bool { - self.get_node_ref_by_metadata(metadata).is_some() - } - - pub fn all_exists<'a>(&self, nodes: impl Iterator) -> bool { - self.filter_missing(nodes).next().is_none() - } - - pub fn all_exists_by_round_author<'a>( - &self, - mut nodes: impl Iterator, - ) -> bool { - nodes.all(|(round, author)| self.get_node_ref(*round, author).is_some()) - } - - pub fn filter_missing<'a, 'b>( - &'b self, - nodes: impl Iterator + 'b, - ) -> impl Iterator + 'b { - nodes.filter(|node_metadata| !self.exists(node_metadata)) - } - - fn get_node_ref_by_metadata(&self, metadata: &NodeMetadata) -> Option<&NodeStatus> { - self.get_node_ref(metadata.round(), metadata.author()) - } - - pub fn get_node_ref(&self, round: Round, author: &Author) -> Option<&NodeStatus> { - let index = self.author_to_index.get(author)?; - let round_ref = self.nodes_by_round.get(&round)?; - round_ref[*index].as_ref() - } - - fn get_node_ref_mut( - &mut self, - round: Round, - author: &Author, - ) -> Option<&mut Option> { - let index = self.author_to_index.get(author)?; - let round_ref = self.nodes_by_round.get_mut(&round)?; - Some(&mut round_ref[*index]) - } - - fn get_round_iter(&self, round: Round) -> Option> { - self.nodes_by_round - .get(&round) - .map(|round_ref| round_ref.iter().flatten()) - } - - pub fn get_node(&self, metadata: &NodeMetadata) -> Option> { - self.get_node_ref_by_metadata(metadata) - .map(|node_status| node_status.as_node().clone()) - } - - pub fn get_node_by_round_author( - &self, - round: Round, - author: &Author, - ) -> Option<&Arc> { - self.get_node_ref(round, author) - .map(|node_status| node_status.as_node()) - } - - pub fn check_votes_for_node( - &self, - metadata: &NodeMetadata, - validator_verifier: &ValidatorVerifier, - ) -> bool { - self.get_node_ref_by_metadata(metadata) - .map(|node_status| match node_status { - NodeStatus::Unordered { - aggregated_weak_voting_power, - aggregated_strong_voting_power, - .. - } => { - validator_verifier - .check_aggregated_voting_power(*aggregated_weak_voting_power, true) - .is_ok() - || validator_verifier - .check_aggregated_voting_power(*aggregated_strong_voting_power, false) - .is_ok() - }, - NodeStatus::Ordered(_) => { - error!("checking voting power for Ordered node"); - true - }, - }) - .unwrap_or(false) - } - - fn reachable_filter(start: Vec) -> impl FnMut(&Arc) -> bool { - let mut reachable: HashSet = HashSet::from_iter(start); - move |node| { - if reachable.contains(&node.digest()) { - for parent in node.parents() { - reachable.insert(*parent.metadata().digest()); - } - true - } else { - false - } - } - } - - pub fn reachable_mut( - &mut self, - from: &Arc, - until: Option, - ) -> impl Iterator { - let until = until.unwrap_or(self.lowest_round()); - let mut reachable_filter = Self::reachable_filter(vec![from.digest()]); - self.nodes_by_round - .range_mut(until..=from.round()) - .rev() - .flat_map(|(_, round_ref)| round_ref.iter_mut()) - .flatten() - .filter(move |node_status| { - matches!(node_status, NodeStatus::Unordered { .. }) - && reachable_filter(node_status.as_node()) - }) - } - - pub fn reachable<'a>( - &self, - targets: impl Iterator + Clone, - until: Option, - // TODO: replace filter with bool to filter unordered - filter: impl Fn(&NodeStatus) -> bool, - ) -> impl Iterator { - let until = until.unwrap_or(self.lowest_round()); - let initial_round = targets.clone().map(|t| t.round()).max().unwrap(); - let initial = targets.map(|t| *t.digest()).collect(); - - let mut reachable_filter = Self::reachable_filter(initial); - self.nodes_by_round - .range(until..=initial_round) - .rev() - .flat_map(|(_, round_ref)| round_ref.iter()) - .flatten() - .filter(move |node_status| { - filter(node_status) && reachable_filter(node_status.as_node()) - }) - } - - pub fn get_strong_links_for_round( - &self, - round: Round, - validator_verifier: &ValidatorVerifier, - ) -> Option> { - if validator_verifier - .check_voting_power( - self.get_round_iter(round)? - .map(|node_status| node_status.as_node().metadata().author()), - true, - ) - .is_ok() - { - Some( - self.get_round_iter(round)? - .map(|node_status| node_status.as_node().certificate()) - .collect(), - ) - } else { - None - } - } - - pub fn lowest_incomplete_round(&self) -> Round { - if self.nodes_by_round.is_empty() { - return self.lowest_round(); - } - - for (round, round_nodes) in &self.nodes_by_round { - if round_nodes.iter().any(|node| node.is_none()) { - return *round; - } - } - - self.highest_round() + 1 - } - - pub fn bitmask(&self, target_round: Round) -> DagSnapshotBitmask { - let from_round = if self.is_empty() { - self.lowest_round() - } else { - target_round - .saturating_sub(self.window_size) - .max(self.lowest_incomplete_round()) - .max(self.lowest_round()) - }; - let mut bitmask: Vec<_> = self - .nodes_by_round - .range(from_round..=target_round) - .map(|(_, round_nodes)| round_nodes.iter().map(|node| node.is_some()).collect()) - .collect(); - - bitmask.resize( - (target_round - from_round + 1) as usize, - vec![false; self.author_to_index.len()], - ); - - DagSnapshotBitmask::new(from_round, bitmask) - } - - pub(super) fn prune(&mut self) -> BTreeMap>> { - let to_keep = self.nodes_by_round.split_off(&self.start_round); - let to_prune = std::mem::replace(&mut self.nodes_by_round, to_keep); - debug!( - "pruning dag. start round {}. pruning from {}", - self.start_round, - to_prune.first_key_value().map(|v| v.0).unwrap() - ); - to_prune - } - - fn commit_callback( - &mut self, - commit_round: Round, - ) -> Option>>> { - let new_start_round = commit_round.saturating_sub(3 * self.window_size); - if new_start_round > self.start_round { - self.start_round = new_start_round; - return Some(self.prune()); - } - None - } - - pub(super) fn highest_ordered_anchor_round(&self) -> Option { - for (round, round_nodes) in self.nodes_by_round.iter().rev() { - for maybe_node_status in round_nodes { - if matches!(maybe_node_status, Some(NodeStatus::Ordered(_))) { - return Some(*round); - } - } - } - None - } - - pub fn is_empty(&self) -> bool { - self.nodes_by_round.is_empty() && self.start_round > 1 - } -} - -pub struct DagStore { - dag: RwLock, - storage: Arc, - payload_manager: Arc, -} - -impl DagStore { - pub fn new( - epoch_state: Arc, - storage: Arc, - payload_manager: Arc, - start_round: Round, - window_size: u64, - ) -> Self { - let mut all_nodes = storage.get_certified_nodes().unwrap_or_default(); - all_nodes.sort_unstable_by_key(|(_, node)| node.round()); - let mut to_prune = vec![]; - // Reconstruct the continuous dag starting from start_round and gc unrelated nodes - let dag = Self::new_empty( - epoch_state, - storage.clone(), - payload_manager, - start_round, - window_size, - ); - for (digest, certified_node) in all_nodes { - // TODO: save the storage call in this case - if let Err(e) = dag.add_node(certified_node) { - debug!("Delete node after bootstrap due to {}", e); - to_prune.push(digest); - } - } - if let Err(e) = storage.delete_certified_nodes(to_prune) { - error!("Error deleting expired nodes: {:?}", e); - } - if dag.read().is_empty() { - warn!( - "[DAG] Start with empty DAG store at {}, need state sync", - start_round - ); - } - dag - } - - pub fn new_empty( - epoch_state: Arc, - storage: Arc, - payload_manager: Arc, - start_round: Round, - window_size: u64, - ) -> Self { - let dag = InMemDag::new_empty(epoch_state, start_round, window_size); - Self { - dag: RwLock::new(dag), - storage, - payload_manager, - } - } - - pub fn new_for_test( - dag: InMemDag, - storage: Arc, - payload_manager: Arc, - ) -> Self { - Self { - dag: RwLock::new(dag), - storage, - payload_manager, - } - } - - pub fn add_node(&self, node: CertifiedNode) -> anyhow::Result<()> { - self.dag.write().validate_new_node(&node)?; - - // Note on concurrency: it is possible that a prune operation kicks in here and - // moves the window forward making the `node` stale. Any stale node inserted - // due to this race will be cleaned up with the next prune operation. - - // mutate after all checks pass - self.storage.save_certified_node(&node)?; - - debug!("Added node {}", node.id()); - self.payload_manager - .prefetch_payload_data(node.payload(), node.metadata().timestamp()); - - self.dag.write().add_validated_node(node) - } - - pub fn commit_callback(&self, commit_round: Round) { - let to_prune = self.dag.write().commit_callback(commit_round); - if let Some(to_prune) = to_prune { - let digests = to_prune - .iter() - .flat_map(|(_, round_ref)| round_ref.iter().flatten()) - .map(|node_status| *node_status.as_node().metadata().digest()) - .collect(); - if let Err(e) = self.storage.delete_certified_nodes(digests) { - error!("Error deleting expired nodes: {:?}", e); - } - } - } -} - -impl Deref for DagStore { - type Target = RwLock; - - fn deref(&self) -> &Self::Target { - &self.dag - } -} diff --git a/consensus/src/dag/errors.rs b/consensus/src/dag/errors.rs deleted file mode 100644 index e7032f65d32c1..0000000000000 --- a/consensus/src/dag/errors.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_bitvec::BitVec; -use aptos_consensus_types::common::Round; -use serde::{Deserialize, Serialize}; -use std::ops::Deref; -use thiserror::Error as ThisError; - -#[derive(Clone, ThisError, Debug, Serialize, Deserialize)] -pub enum DagFetchError { - #[error("fetch failed")] - Failed, - #[error("already exists")] - AlreadyExists, -} - -#[derive(Clone, ThisError, Debug, Serialize, Deserialize)] -pub enum NodeBroadcastHandleError { - #[error("invalid parent in node")] - InvalidParent, - #[error("missing parents")] - MissingParents, - #[error("stale round number")] - StaleRound(Round), - #[error("refused to vote")] - VoteRefused, -} - -#[derive(Clone, Debug, ThisError, Serialize, Deserialize)] -pub enum DagDriverError { - #[error("missing parents")] - MissingParents, -} - -#[derive(Clone, Debug, ThisError, Serialize, Deserialize)] -pub enum FetchRequestHandleError { - #[error("target nodes are missing, missing {}", .0.count_ones())] - TargetsMissing(BitVec), - #[error("garbage collected, request round {0}, lowest round {1}")] - GarbageCollected(Round, Round), -} - -#[derive(Clone, Debug, ThisError, Serialize, Deserialize)] -pub enum DAGError { - #[error(transparent)] - NodeBroadcastHandleError(NodeBroadcastHandleError), - #[error(transparent)] - DagDriverError(DagDriverError), - #[error(transparent)] - FetchRequestHandleError(FetchRequestHandleError), - #[error("unable to verify message")] - MessageVerificationError, - #[error("unknown error")] - Unknown, -} - -#[derive(Clone, Debug, ThisError, Serialize, Deserialize)] -#[error("{error}")] -pub struct DAGRpcError { - error: DAGError, - epoch: u64, -} - -impl DAGRpcError { - pub fn new(epoch: u64, error: DAGError) -> Self { - Self { epoch, error } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } -} - -impl Deref for DAGRpcError { - type Target = DAGError; - - fn deref(&self) -> &Self::Target { - &self.error - } -} diff --git a/consensus/src/dag/health/backoff.rs b/consensus/src/dag/health/backoff.rs deleted file mode 100644 index 262d196aeb7e3..0000000000000 --- a/consensus/src/dag/health/backoff.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation - -use super::{pipeline_health::TPipelineHealth, TChainHealth}; -use aptos_config::config::DagPayloadConfig; -use aptos_consensus_types::common::Round; -use aptos_types::epoch_state::EpochState; -use std::{sync::Arc, time::Duration}; - -#[derive(Clone)] -pub struct HealthBackoff { - epoch_state: Arc, - chain_health: Arc, - pipeline_health: Arc, -} - -impl HealthBackoff { - pub fn new( - epoch_state: Arc, - chain_health: Arc, - pipeline_health: Arc, - ) -> Self { - Self { - epoch_state, - chain_health, - pipeline_health, - } - } - - pub fn calculate_payload_limits( - &self, - round: Round, - payload_config: &DagPayloadConfig, - ) -> (u64, u64) { - let chain_backoff = self - .chain_health - .get_round_payload_limits(round) - .unwrap_or((u64::MAX, u64::MAX)); - let pipeline_backoff = self - .pipeline_health - .get_payload_limits() - .unwrap_or((u64::MAX, u64::MAX)); - let voting_power_ratio = self.chain_health.voting_power_ratio(round); - - let max_txns_per_round = [ - payload_config.max_sending_txns_per_round, - chain_backoff.0, - pipeline_backoff.0, - ] - .into_iter() - .min() - .expect("must not be empty"); - - let max_size_per_round_bytes = [ - payload_config.max_sending_size_per_round_bytes, - chain_backoff.1, - pipeline_backoff.1, - ] - .into_iter() - .min() - .expect("must not be empty"); - - // TODO: figure out receiver side checks - let max_txns = max_txns_per_round.saturating_div( - (self.epoch_state.verifier.len() as f64 * voting_power_ratio).ceil() as u64, - ); - let max_txn_size_bytes = max_size_per_round_bytes.saturating_div( - (self.epoch_state.verifier.len() as f64 * voting_power_ratio).ceil() as u64, - ); - - (max_txns, max_txn_size_bytes) - } - - pub fn backoff_duration(&self, round: Round) -> Duration { - let chain_backoff = self.chain_health.get_round_backoff(round); - let pipeline_backoff = self.pipeline_health.get_backoff(); - - chain_backoff - .unwrap_or_default() - .max(pipeline_backoff.unwrap_or_default()) - } - - pub fn stop_voting(&self) -> bool { - self.pipeline_health.stop_voting() - } -} diff --git a/consensus/src/dag/health/chain_health.rs b/consensus/src/dag/health/chain_health.rs deleted file mode 100644 index bcbe8a2d88f2f..0000000000000 --- a/consensus/src/dag/health/chain_health.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - counters::CHAIN_HEALTH_BACKOFF_TRIGGERED, - dag::anchor_election::CommitHistory, - liveness::{leader_reputation::VotingPowerRatio, proposal_generator::ChainHealthBackoffConfig}, -}; -use aptos_config::config::ChainHealthBackoffValues; -use aptos_consensus_types::common::Round; -use std::{sync::Arc, time::Duration}; - -pub trait TChainHealth: Send + Sync { - fn get_round_backoff(&self, round: Round) -> Option; - - fn get_round_payload_limits(&self, round: Round) -> Option<(u64, u64)>; - - fn voting_power_ratio(&self, round: Round) -> VotingPowerRatio; -} - -pub struct NoChainHealth {} - -impl NoChainHealth { - pub fn new() -> Arc { - Arc::new(Self {}) - } -} - -impl TChainHealth for NoChainHealth { - fn get_round_backoff(&self, _round: Round) -> Option { - None - } - - fn get_round_payload_limits(&self, _round: Round) -> Option<(u64, u64)> { - None - } - - fn voting_power_ratio(&self, _round: Round) -> VotingPowerRatio { - 1.0 - } -} - -pub struct ChainHealthBackoff { - config: ChainHealthBackoffConfig, - commit_history: Arc, -} - -impl ChainHealthBackoff { - pub fn new( - config: ChainHealthBackoffConfig, - commit_history: Arc, - ) -> Arc { - Arc::new(Self { - commit_history, - config, - }) - } - - fn get_chain_health_backoff(&self, round: Round) -> Option<&ChainHealthBackoffValues> { - let voting_power_ratio = self - .commit_history - .get_voting_power_participation_ratio(round); - let chain_health_backoff = self.config.get_backoff(voting_power_ratio); - - chain_health_backoff - } -} - -impl TChainHealth for ChainHealthBackoff { - fn get_round_backoff(&self, round: Round) -> Option { - let chain_health_backoff = self.get_chain_health_backoff(round); - - if let Some(value) = chain_health_backoff { - CHAIN_HEALTH_BACKOFF_TRIGGERED.observe(1.0); - Some(Duration::from_millis(value.backoff_proposal_delay_ms)) - } else { - CHAIN_HEALTH_BACKOFF_TRIGGERED.observe(0.0); - None - } - } - - fn get_round_payload_limits(&self, round: Round) -> Option<(u64, u64)> { - let chain_health_backoff = self.get_chain_health_backoff(round); - - chain_health_backoff.map(|value| { - ( - value.max_sending_block_txns_override, - value.max_sending_block_bytes_override, - ) - }) - } - - fn voting_power_ratio(&self, round: Round) -> VotingPowerRatio { - self.commit_history - .get_voting_power_participation_ratio(round) - } -} diff --git a/consensus/src/dag/health/mod.rs b/consensus/src/dag/health/mod.rs deleted file mode 100644 index 462763ec77f9d..0000000000000 --- a/consensus/src/dag/health/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © Aptos Foundation - -mod backoff; -mod chain_health; -mod pipeline_health; - -pub use backoff::HealthBackoff; -#[cfg(test)] -pub use chain_health::NoChainHealth; -pub use chain_health::{ChainHealthBackoff, TChainHealth}; -#[cfg(test)] -pub use pipeline_health::NoPipelineBackpressure; -pub use pipeline_health::PipelineLatencyBasedBackpressure; diff --git a/consensus/src/dag/health/pipeline_health.rs b/consensus/src/dag/health/pipeline_health.rs deleted file mode 100644 index d3c59c6ead75d..0000000000000 --- a/consensus/src/dag/health/pipeline_health.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - dag::adapter::OrderedNotifierAdapter, liveness::proposal_generator::PipelineBackpressureConfig, -}; -use std::{sync::Arc, time::Duration}; - -pub trait TPipelineHealth: Send + Sync { - fn get_backoff(&self) -> Option; - - fn get_payload_limits(&self) -> Option<(u64, u64)>; - - fn stop_voting(&self) -> bool; -} - -pub struct NoPipelineBackpressure {} - -impl NoPipelineBackpressure { - pub fn new() -> Arc { - Arc::new(Self {}) - } -} - -impl TPipelineHealth for NoPipelineBackpressure { - fn get_backoff(&self) -> Option { - None - } - - fn get_payload_limits(&self) -> Option<(u64, u64)> { - None - } - - fn stop_voting(&self) -> bool { - false - } -} - -pub struct PipelineLatencyBasedBackpressure { - voter_pipeline_latency_limit: Duration, - pipeline_config: PipelineBackpressureConfig, - adapter: Arc, -} - -impl PipelineLatencyBasedBackpressure { - pub(in crate::dag) fn new( - voter_pipeline_latency_limit: Duration, - pipeline_config: PipelineBackpressureConfig, - adapter: Arc, - ) -> Arc { - Arc::new(Self { - voter_pipeline_latency_limit, - pipeline_config, - adapter, - }) - } -} - -impl TPipelineHealth for PipelineLatencyBasedBackpressure { - fn get_backoff(&self) -> Option { - let latency = self.adapter.pipeline_pending_latency(); - self.pipeline_config - .get_backoff(latency) - .map(|config| Duration::from_millis(config.backpressure_proposal_delay_ms)) - } - - fn get_payload_limits(&self) -> Option<(u64, u64)> { - let latency = self.adapter.pipeline_pending_latency(); - self.pipeline_config.get_backoff(latency).map(|config| { - ( - config.max_sending_block_txns_override, - config.max_sending_block_bytes_override, - ) - }) - } - - fn stop_voting(&self) -> bool { - let latency = self.adapter.pipeline_pending_latency(); - latency > self.voter_pipeline_latency_limit - } -} diff --git a/consensus/src/dag/mod.rs b/consensus/src/dag/mod.rs deleted file mode 100644 index 4b5611d84e9ca..0000000000000 --- a/consensus/src/dag/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 -#![allow(dead_code)] - -mod adapter; -mod anchor_election; -mod bootstrap; -mod commit_signer; -mod dag_driver; -mod dag_fetcher; -mod dag_handler; -mod dag_network; -mod dag_state_sync; -mod dag_store; -mod errors; -mod health; -mod observability; -mod order_rule; -mod rb_handler; -mod round_state; -mod storage; -#[cfg(test)] -mod tests; -mod types; - -pub use adapter::{ProofNotifier, StorageAdapter}; -pub use bootstrap::DagBootstrapper; -pub use commit_signer::DagCommitSigner; -pub use dag_network::{RpcHandler, RpcWithFallback, TDAGNetworkSender}; -#[cfg(test)] -pub use types::Extensions; -pub use types::{CertifiedNode, DAGMessage, DAGNetworkMessage, DAGRpcResult, Node, NodeId, Vote}; diff --git a/consensus/src/dag/observability/counters.rs b/consensus/src/dag/observability/counters.rs deleted file mode 100644 index 2abdc9ec41a15..0000000000000 --- a/consensus/src/dag/observability/counters.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - register_histogram, register_histogram_vec, register_int_gauge, Histogram, HistogramVec, - IntGauge, -}; -use once_cell::sync::Lazy; - -/// Traces node latency movement throughout the DAG -pub static NODE_TRACING: Lazy = Lazy::new(|| { - register_histogram_vec!( - "aptos_consensus_dag_node_tracing", - "Histogram for different stages of a node", - &["stage"] - ) - .unwrap() -}); - -/// Traces round latency movement throughout the DAG -pub static ROUND_TRACING: Lazy = Lazy::new(|| { - register_histogram_vec!( - "aptos_consensus_dag_round_tracing", - "Histogram for different stages of a round", - &["stage"] - ) - .unwrap() -}); - -/// This counter is set to the last round reported by the local round_state. -pub static CURRENT_ROUND: Lazy = Lazy::new(|| { - register_int_gauge!( - "aptos_consensus_dag_current_round", - "This counter is set to the last round reported by the dag driver." - ) - .unwrap() -}); - -pub static NUM_TXNS_PER_NODE: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_dag_num_txns_per_node", - "Histogram counting the number of transactions per node", - ) - .unwrap() -}); - -pub static NODE_PAYLOAD_SIZE: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_dag_node_payload_size", - "Histogram counting the size of the node payload", - ) - .unwrap() -}); - -pub static NUM_NODES_PER_BLOCK: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_dag_num_nodes_per_block", - "Histogram counting the number of nodes per block", - ) - .unwrap() -}); - -pub static NUM_ROUNDS_PER_BLOCK: Lazy = Lazy::new(|| { - register_histogram!( - "aptos_consensus_dag_num_rounds_per_block", - "Histogram counting the number of rounds per block", - ) - .unwrap() -}); diff --git a/consensus/src/dag/observability/logging.rs b/consensus/src/dag/observability/logging.rs deleted file mode 100644 index 51ee8d47b0086..0000000000000 --- a/consensus/src/dag/observability/logging.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_consensus_types::common::{Author, Round}; -use aptos_logger::Schema; -use serde::Serialize; - -#[derive(Schema)] -pub struct LogSchema { - event: LogEvent, - remote_peer: Option, - round: Option, -} - -#[derive(Serialize)] -pub enum LogEvent { - EpochStart, - ModeTransition, - BroadcastNode, - ReceiveNode, - Vote, - ReceiveVote, - BroadcastCertifiedNode, - ReceiveCertifiedNode, - ReceiveAck, - OrderedAnchor, - NewRound, - FetchNodes, - ReceiveFetchNodes, - ActiveMode, - SyncMode, - SyncOutcome, - Shutdown, -} - -impl LogSchema { - pub fn new(event: LogEvent) -> Self { - Self { - event, - remote_peer: None, - round: None, - } - } -} diff --git a/consensus/src/dag/observability/mod.rs b/consensus/src/dag/observability/mod.rs deleted file mode 100644 index 899b950ea22ad..0000000000000 --- a/consensus/src/dag/observability/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod counters; -pub mod logging; -pub mod tracing; diff --git a/consensus/src/dag/observability/tracing.rs b/consensus/src/dag/observability/tracing.rs deleted file mode 100644 index 839ad37109789..0000000000000 --- a/consensus/src/dag/observability/tracing.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::dag::observability::counters; -use aptos_infallible::duration_since_epoch; -use aptos_metrics_core::HistogramVec; -use std::time::Duration; - -#[derive(strum_macros::AsRefStr)] -pub enum NodeStage { - NodeReceived, - CertAggregated, - CertifiedNodeReceived, - AnchorOrdered, - NodeOrdered, -} - -#[derive(strum_macros::AsRefStr)] -pub enum RoundStage { - NodeBroadcasted, - CertifiedNodeBroadcasted, - StrongLinkReceived, - Finished, -} - -fn observe(counter: &HistogramVec, timestamp: u64, name: &str) { - if let Some(t) = duration_since_epoch().checked_sub(Duration::from_micros(timestamp)) { - counter.with_label_values(&[name]).observe(t.as_secs_f64()); - } -} - -/// Record the time during each stage of a node. -pub fn observe_node(timestamp: u64, stage: NodeStage) { - observe(&counters::NODE_TRACING, timestamp, stage.as_ref()); -} - -/// Record the time during each stage of a round. -pub fn observe_round(timestamp: u64, stage: RoundStage) { - observe(&counters::ROUND_TRACING, timestamp, stage.as_ref()); -} diff --git a/consensus/src/dag/order_rule.rs b/consensus/src/dag/order_rule.rs deleted file mode 100644 index 4cbbedec80529..0000000000000 --- a/consensus/src/dag/order_rule.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::dag_store::DagStore; -use crate::dag::{ - adapter::OrderedNotifier, - anchor_election::AnchorElection, - dag_store::NodeStatus, - observability::{ - logging::{LogEvent, LogSchema}, - tracing::{observe_node, NodeStage}, - }, - storage::CommitEvent, - types::NodeMetadata, - CertifiedNode, -}; -use aptos_consensus_types::common::Round; -use aptos_infallible::Mutex; -use aptos_logger::debug; -use aptos_types::epoch_state::EpochState; -use std::sync::Arc; - -pub trait TOrderRule: Send + Sync { - fn process_new_node(&self, node_metadata: &NodeMetadata); - - fn process_all(&self); -} - -pub struct OrderRule { - epoch_state: Arc, - lowest_unordered_anchor_round: Round, - dag: Arc, - anchor_election: Arc, - notifier: Arc, - dag_window_size_config: Round, -} - -impl OrderRule { - pub fn new( - epoch_state: Arc, - lowest_unordered_anchor_round: Round, - dag: Arc, - anchor_election: Arc, - notifier: Arc, - dag_window_size_config: Round, - commit_events: Option>, - ) -> Self { - if let Some(commit_events) = commit_events { - // make sure it's sorted - assert!(commit_events - .windows(2) - .all(|w| (w[0].epoch(), w[0].round()) < (w[1].epoch(), w[1].round()))); - for event in commit_events { - if event.epoch() == epoch_state.epoch { - let maybe_anchor = dag - .read() - .get_node_by_round_author(event.round(), event.author()) - .cloned(); - if let Some(anchor) = maybe_anchor { - dag.write() - .reachable_mut(&anchor, None) - .for_each(|node_status| node_status.mark_as_ordered()); - } - } - anchor_election.update_reputation(event); - } - } - let mut order_rule = Self { - epoch_state, - lowest_unordered_anchor_round, - dag, - anchor_election, - notifier, - dag_window_size_config, - }; - // re-check if anything can be ordered to recover pending anchors - order_rule.process_all(); - order_rule - } - - /// Check if two rounds have the same parity - fn check_parity(r1: Round, r2: Round) -> bool { - (r1 ^ r2) & 1 == 0 - } - - /// Find if there's anchors that can be ordered start from `start_round` until `round`, - /// if so find next one until nothing can be ordered. - fn check_ordering_between(&mut self, mut start_round: Round, round: Round) { - while start_round <= round { - if let Some(direct_anchor) = - self.find_first_anchor_with_enough_votes(start_round, round) - { - let ordered_anchor = self.find_first_anchor_to_order(direct_anchor); - self.finalize_order(ordered_anchor); - // if there's any anchor being ordered, the loop continues to check if new anchor can be ordered as well. - start_round = self.lowest_unordered_anchor_round; - } else { - break; - } - } - } - - /// From the start round until the target_round, try to find if there's any anchor has enough votes to trigger ordering - fn find_first_anchor_with_enough_votes( - &self, - mut start_round: Round, - target_round: Round, - ) -> Option> { - let dag_reader = self.dag.read(); - while start_round < target_round { - let anchor_author = self.anchor_election.get_anchor(start_round); - // I "think" it's impossible to get ordered/committed node here but to double check - if let Some(anchor_node) = - dag_reader.get_node_by_round_author(start_round, &anchor_author) - { - // f+1 or 2f+1? - if dag_reader - .check_votes_for_node(anchor_node.metadata(), &self.epoch_state.verifier) - { - return Some(anchor_node.clone()); - } - } else { - debug!( - anchor = anchor_author, - "Anchor not found for round {}", start_round - ); - } - start_round += 2; - } - None - } - - /// Follow an anchor with enough votes to find the first anchor that's recursively reachable by its suffix anchor - fn find_first_anchor_to_order( - &self, - mut current_anchor: Arc, - ) -> Arc { - let dag_reader = self.dag.read(); - let anchor_round = current_anchor.round(); - let is_anchor = |metadata: &NodeMetadata| -> bool { - Self::check_parity(metadata.round(), anchor_round) - && *metadata.author() == self.anchor_election.get_anchor(metadata.round()) - }; - while let Some(prev_anchor) = dag_reader - .reachable( - Some(current_anchor.metadata().clone()).iter(), - Some(self.lowest_unordered_anchor_round), - |node_status| matches!(node_status, NodeStatus::Unordered { .. }), - ) - // skip the current anchor itself - .skip(1) - .map(|node_status| node_status.as_node()) - .find(|node| is_anchor(node.metadata())) - { - current_anchor = prev_anchor.clone(); - } - current_anchor - } - - /// Finalize the ordering with the given anchor node, update anchor election and construct blocks for execution. - fn finalize_order(&mut self, anchor: Arc) { - // Check we're in the expected instance - assert!(Self::check_parity( - self.lowest_unordered_anchor_round, - anchor.round(), - )); - let lowest_round_to_reach = anchor.round().saturating_sub(self.dag_window_size_config); - - // Ceil it to the closest unordered anchor round - let lowest_anchor_round = std::cmp::max( - self.lowest_unordered_anchor_round, - lowest_round_to_reach - + !Self::check_parity(lowest_round_to_reach, anchor.round()) as u64, - ); - assert!(Self::check_parity(lowest_anchor_round, anchor.round())); - - let failed_authors_and_rounds: Vec<_> = (lowest_anchor_round..anchor.round()) - .step_by(2) - .map(|failed_round| (failed_round, self.anchor_election.get_anchor(failed_round))) - .collect(); - let parents = anchor - .parents() - .iter() - .map(|cert| *cert.metadata().author()) - .collect(); - let event = CommitEvent::new( - anchor.id(), - parents, - failed_authors_and_rounds - .iter() - .map(|(_, author)| *author) - .collect(), - ); - self.anchor_election.update_reputation(event); - - let mut dag_writer = self.dag.write(); - let mut ordered_nodes: Vec<_> = dag_writer - .reachable_mut(&anchor, Some(lowest_round_to_reach)) - .map(|node_status| { - node_status.mark_as_ordered(); - node_status.as_node().clone() - }) - .collect(); - - observe_node(anchor.timestamp(), NodeStage::AnchorOrdered); - for node in ordered_nodes.iter().skip(1) { - observe_node(node.timestamp(), NodeStage::NodeOrdered); - } - ordered_nodes.reverse(); - - debug!( - LogSchema::new(LogEvent::OrderedAnchor), - id = anchor.id(), - lowest_unordered_anchor_round = self.lowest_unordered_anchor_round, - "Reached round {} with {} nodes", - lowest_anchor_round, - ordered_nodes.len() - ); - - self.lowest_unordered_anchor_round = anchor.round() + 1; - self.notifier - .send_ordered_nodes(ordered_nodes, failed_authors_and_rounds); - } - - /// Check if this node can trigger anchors to be ordered - pub fn process_new_node(&mut self, node_metadata: &NodeMetadata) { - let round = node_metadata.round(); - - debug!( - lowest_unordered_round = self.lowest_unordered_anchor_round, - node_round = round, - "Trigger Ordering" - ); - // If the node comes from the proposal round in the current instance, it can't trigger any ordering - if round <= self.lowest_unordered_anchor_round - || Self::check_parity(round, self.lowest_unordered_anchor_round) - { - return; - } - // This node's votes can trigger an anchor from previous round to be ordered. - let start_round = round - 1; - self.check_ordering_between(start_round, round) - } - - /// Check the whole dag to see if anything can be ordered. - pub fn process_all(&mut self) { - let start_round = self.lowest_unordered_anchor_round; - let round = self.dag.read().highest_round(); - self.check_ordering_between(start_round, round); - } -} - -impl TOrderRule for Mutex { - fn process_new_node(&self, node_metadata: &NodeMetadata) { - self.lock().process_new_node(node_metadata) - } - - fn process_all(&self) { - self.lock().process_all() - } -} diff --git a/consensus/src/dag/rb_handler.rs b/consensus/src/dag/rb_handler.rs deleted file mode 100644 index b6948adbfbd93..0000000000000 --- a/consensus/src/dag/rb_handler.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{dag_store::DagStore, health::HealthBackoff, order_rule::TOrderRule}; -use crate::{ - dag::{ - dag_fetcher::TFetchRequester, - dag_network::RpcHandler, - errors::NodeBroadcastHandleError, - observability::{ - logging::{LogEvent, LogSchema}, - tracing::{observe_node, NodeStage}, - }, - storage::DAGStorage, - types::{Node, NodeCertificate, Vote}, - NodeId, - }, - util::is_vtxn_expected, -}; -use anyhow::{bail, ensure}; -use aptos_config::config::DagPayloadConfig; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::Mutex; -use aptos_logger::{debug, error}; -use aptos_types::{ - epoch_state::EpochState, - on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorTxnConfig}, - validator_signer::ValidatorSigner, - validator_txn::ValidatorTransaction, -}; -use async_trait::async_trait; -use claims::assert_some; -use dashmap::DashSet; -use std::{collections::BTreeMap, mem, sync::Arc}; - -pub(crate) struct NodeBroadcastHandler { - dag: Arc, - order_rule: Arc, - /// Note: The mutex around BTreeMap is to work around Rust Sync semantics. - /// Fine grained concurrency is implemented by the DashSet below. - votes_by_round_peer: Mutex>>, - votes_fine_grained_lock: DashSet<(Round, Author)>, - signer: Arc, - epoch_state: Arc, - storage: Arc, - fetch_requester: Arc, - payload_config: DagPayloadConfig, - vtxn_config: ValidatorTxnConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - health_backoff: HealthBackoff, -} - -impl NodeBroadcastHandler { - pub fn new( - dag: Arc, - order_rule: Arc, - signer: Arc, - epoch_state: Arc, - storage: Arc, - fetch_requester: Arc, - payload_config: DagPayloadConfig, - vtxn_config: ValidatorTxnConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - health_backoff: HealthBackoff, - ) -> Self { - let epoch = epoch_state.epoch; - let votes_by_round_peer = read_votes_from_storage(&storage, epoch); - - Self { - dag, - order_rule, - votes_by_round_peer: Mutex::new(votes_by_round_peer), - votes_fine_grained_lock: DashSet::with_capacity(epoch_state.verifier.len() * 10), - signer, - epoch_state, - storage, - fetch_requester, - payload_config, - vtxn_config, - randomness_config, - jwk_consensus_config, - health_backoff, - } - } - - pub fn gc(&self) { - let lowest_round = self.dag.read().lowest_round(); - if let Err(e) = self.gc_before_round(lowest_round) { - error!("Error deleting votes: {}", e); - } - } - - pub fn gc_before_round(&self, min_round: Round) -> anyhow::Result<()> { - let mut votes_by_round_peer_guard = self.votes_by_round_peer.lock(); - let to_retain = votes_by_round_peer_guard.split_off(&min_round); - let to_delete = mem::replace(&mut *votes_by_round_peer_guard, to_retain); - drop(votes_by_round_peer_guard); - - let to_delete = to_delete - .iter() - .flat_map(|(r, peer_and_digest)| { - peer_and_digest - .iter() - .map(|(author, _)| NodeId::new(self.epoch_state.epoch, *r, *author)) - }) - .collect(); - self.storage.delete_votes(to_delete) - } - - fn validate(&self, node: Node) -> anyhow::Result { - ensure!( - node.epoch() == self.epoch_state.epoch, - "different epoch {}, current {}", - node.epoch(), - self.epoch_state.epoch - ); - - let num_vtxns = node.validator_txns().len() as u64; - ensure!(num_vtxns <= self.vtxn_config.per_block_limit_txn_count()); - for vtxn in node.validator_txns() { - ensure!( - is_vtxn_expected(&self.randomness_config, &self.jwk_consensus_config, vtxn), - "unexpected validator transaction: {:?}", - vtxn.topic() - ); - } - let vtxn_total_bytes = node - .validator_txns() - .iter() - .map(ValidatorTransaction::size_in_bytes) - .sum::() as u64; - ensure!(vtxn_total_bytes <= self.vtxn_config.per_block_limit_total_bytes()); - - let num_txns = num_vtxns + node.payload().len() as u64; - let txn_bytes = vtxn_total_bytes + node.payload().size() as u64; - ensure!(num_txns <= self.payload_config.max_receiving_txns_per_round); - ensure!(txn_bytes <= self.payload_config.max_receiving_size_per_round_bytes); - - let current_round = node.metadata().round(); - - let dag_reader = self.dag.read(); - let lowest_round = dag_reader.lowest_round(); - - ensure!( - current_round >= lowest_round, - NodeBroadcastHandleError::StaleRound(current_round) - ); - - // check which parents are missing in the DAG - let missing_parents: Vec = node - .parents() - .iter() - .filter(|parent| !dag_reader.exists(parent.metadata())) - .cloned() - .collect(); - drop(dag_reader); // Drop the DAG store early as it is no longer required - - if !missing_parents.is_empty() { - // For each missing parent, verify their signatures and voting power. - // Otherwise, a malicious node can send bad nodes with fake parents - // and cause this peer to issue unnecessary fetch requests. - ensure!( - missing_parents - .iter() - .all(|parent| { parent.verify(&self.epoch_state.verifier).is_ok() }), - NodeBroadcastHandleError::InvalidParent - ); - - // Don't issue fetch requests for parents of the lowest round in the DAG - // because they are already GC'ed - if current_round > lowest_round { - if let Err(err) = self.fetch_requester.request_for_node(node) { - error!("request to fetch failed: {}", err); - } - bail!(NodeBroadcastHandleError::MissingParents); - } - } - - Ok(node) - } -} - -fn read_votes_from_storage( - storage: &Arc, - epoch: u64, -) -> BTreeMap> { - let mut votes_by_round_peer = BTreeMap::new(); - - let all_votes = storage.get_votes().unwrap_or_default(); - let mut to_delete = vec![]; - for (node_id, vote) in all_votes { - if node_id.epoch() == epoch { - votes_by_round_peer - .entry(node_id.round()) - .or_insert_with(BTreeMap::new) - .insert(*node_id.author(), vote); - } else { - to_delete.push(node_id); - } - } - if let Err(err) = storage.delete_votes(to_delete) { - error!("unable to clear old signatures: {}", err); - } - - votes_by_round_peer -} - -#[async_trait] -impl RpcHandler for NodeBroadcastHandler { - type Request = Node; - type Response = Vote; - - async fn process(&self, node: Self::Request) -> anyhow::Result { - ensure!( - !self.health_backoff.stop_voting(), - NodeBroadcastHandleError::VoteRefused - ); - - let key = (node.round(), *node.author()); - ensure!( - self.votes_fine_grained_lock.insert(key), - "concurrent insertion" - ); - defer!({ - assert_some!(self.votes_fine_grained_lock.remove(&key)); - }); - - let node = self.validate(node)?; - observe_node(node.timestamp(), NodeStage::NodeReceived); - debug!(LogSchema::new(LogEvent::ReceiveNode) - .remote_peer(*node.author()) - .round(node.round())); - - if let Some(ack) = self - .votes_by_round_peer - .lock() - .entry(node.round()) - .or_default() - .get(node.author()) - { - return Ok(ack.clone()); - } - - let signature = node.sign_vote(&self.signer)?; - let vote = Vote::new(node.metadata().clone(), signature); - self.storage.save_vote(&node.id(), &vote)?; - self.votes_by_round_peer - .lock() - .get_mut(&node.round()) - .expect("must exist") - .insert(*node.author(), vote.clone()); - - self.dag.write().update_votes(&node, false); - self.order_rule.process_new_node(node.metadata()); - - debug!(LogSchema::new(LogEvent::Vote) - .remote_peer(*node.author()) - .round(node.round())); - Ok(vote) - } -} diff --git a/consensus/src/dag/round_state.rs b/consensus/src/dag/round_state.rs deleted file mode 100644 index 77a4afd9ca3e4..0000000000000 --- a/consensus/src/dag/round_state.rs +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::dag::{ - observability::tracing::{observe_round, RoundStage}, - types::NodeCertificate, -}; -use anyhow::ensure; -use aptos_consensus_types::common::Round; -use aptos_infallible::{duration_since_epoch, Mutex}; -use aptos_types::epoch_state::EpochState; -use std::{cmp::Ordering, sync::Arc, time::Duration}; -use tokio::task::JoinHandle; - -pub struct RoundState { - current_round: Mutex, - event_sender: tokio::sync::mpsc::UnboundedSender, - responsive_check: Box, -} - -impl RoundState { - pub fn new( - event_sender: tokio::sync::mpsc::UnboundedSender, - responsive_check: Box, - ) -> Self { - Self { - current_round: Mutex::new(0), - event_sender, - responsive_check, - } - } - - pub fn check_for_new_round( - &self, - highest_strong_links_round: Round, - strong_links: Vec, - minimum_delay: Duration, - ) { - let current_round = *self.current_round.lock(); - match current_round.cmp(&highest_strong_links_round) { - // we're behind, move forward immediately - Ordering::Less => { - // the receiver can be dropped if we move to a new epoch - let _ = self.event_sender.send(highest_strong_links_round + 1); - }, - Ordering::Equal => self.responsive_check.check_for_new_round( - highest_strong_links_round, - strong_links, - minimum_delay, - ), - Ordering::Greater => (), - } - } - - pub fn current_round(&self) -> Round { - *self.current_round.lock() - } - - pub fn set_current_round(&self, new_round: Round) -> anyhow::Result<()> { - let mut current_round = self.current_round.lock(); - ensure!( - *current_round < new_round, - "current round {} is newer than new round {}", - current_round, - new_round - ); - *current_round = new_round; - self.responsive_check.reset(); - Ok(()) - } -} - -/// Interface to decide if we should move forward to a new round -pub trait ResponsiveCheck: Send + Sync { - fn check_for_new_round( - &self, - highest_strong_links_round: Round, - strong_links: Vec, - health_backoff_delay: Duration, - ); - - fn reset(&self); -} - -/// Move as fast as 2f+1 -pub struct OptimisticResponsive { - event_sender: tokio::sync::mpsc::UnboundedSender, -} - -impl OptimisticResponsive { - pub fn new(event_sender: tokio::sync::mpsc::UnboundedSender) -> Self { - Self { event_sender } - } -} - -impl ResponsiveCheck for OptimisticResponsive { - fn check_for_new_round( - &self, - highest_strong_links_round: Round, - _strong_links: Vec, - _health_backoff_delay: Duration, - ) { - let new_round = highest_strong_links_round + 1; - let _ = self.event_sender.send(new_round); - } - - fn reset(&self) {} -} - -enum State { - Initial, - Scheduled(JoinHandle<()>), - Sent, -} - -struct AdaptiveResponsiveInner { - start_time: Duration, - state: State, -} - -/// More sophisticated strategy to move round forward given 2f+1 strong links -/// Delay if backpressure is triggered. -/// Move as soon as 3f+1 is ready. (TODO: make it configurable) -/// Move if minimal wait time is reached. -pub struct AdaptiveResponsive { - inner: Mutex, - epoch_state: Arc, - minimal_wait_time: Duration, - event_sender: tokio::sync::mpsc::UnboundedSender, -} - -impl AdaptiveResponsive { - pub fn new( - event_sender: tokio::sync::mpsc::UnboundedSender, - epoch_state: Arc, - minimal_wait_time: Duration, - ) -> Self { - Self { - inner: Mutex::new(AdaptiveResponsiveInner { - start_time: duration_since_epoch(), - state: State::Initial, - }), - epoch_state, - minimal_wait_time, - event_sender, - } - } -} - -impl ResponsiveCheck for AdaptiveResponsive { - fn check_for_new_round( - &self, - highest_strong_links_round: Round, - strong_links: Vec, - health_backoff_delay: Duration, - ) { - let mut inner = self.inner.lock(); - if matches!(inner.state, State::Sent) { - return; - } - let new_round = highest_strong_links_round + 1; - observe_round( - inner.start_time.as_micros() as u64, - RoundStage::StrongLinkReceived, - ); - let voting_power = self - .epoch_state - .verifier - .sum_voting_power(strong_links.iter().map(|cert| cert.metadata().author())) - .expect("Unable to sum voting power from strong links"); - - let (wait_time, is_health_backoff) = if self.minimal_wait_time < health_backoff_delay { - (health_backoff_delay, true) - } else { - (self.minimal_wait_time, false) - }; - - // voting power == 3f+1 and pass wait time if health backoff - let duration_since_start = duration_since_epoch().saturating_sub(inner.start_time); - if voting_power == self.epoch_state.verifier.total_voting_power() - && (duration_since_start >= wait_time || !is_health_backoff) - { - let _ = self.event_sender.send(new_round); - if let State::Scheduled(handle) = std::mem::replace(&mut inner.state, State::Sent) { - handle.abort(); - } - } else if matches!(inner.state, State::Initial) { - // wait until minimal time reaches before sending - let sender = self.event_sender.clone(); - let wait_time = wait_time.saturating_sub(duration_since_start); - let handle = tokio::spawn(async move { - tokio::time::sleep(wait_time).await; - let _ = sender.send(new_round); - }); - inner.state = State::Scheduled(handle); - } - } - - fn reset(&self) { - let mut inner = self.inner.lock(); - - inner.start_time = duration_since_epoch(); - inner.state = State::Initial; - } -} diff --git a/consensus/src/dag/storage.rs b/consensus/src/dag/storage.rs deleted file mode 100644 index e090b9d9f17b9..0000000000000 --- a/consensus/src/dag/storage.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::{types::Vote, NodeId}; -use crate::dag::{CertifiedNode, Node}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::HashValue; -use aptos_types::ledger_info::LedgerInfoWithSignatures; -use std::collections::HashMap; - -#[derive(Clone)] -pub struct CommitEvent { - node_id: NodeId, - parents: Vec, - failed_authors: Vec, -} - -impl CommitEvent { - pub fn new(node_id: NodeId, parents: Vec, failed_authors: Vec) -> Self { - CommitEvent { - node_id, - parents, - failed_authors, - } - } - - pub fn epoch(&self) -> u64 { - self.node_id.epoch() - } - - pub fn round(&self) -> Round { - self.node_id.round() - } - - pub fn author(&self) -> &Author { - self.node_id.author() - } - - pub fn parents(&self) -> &[Author] { - &self.parents - } - - pub fn failed_authors(&self) -> &[Author] { - &self.failed_authors - } -} - -pub trait DAGStorage: Send + Sync { - fn save_pending_node(&self, node: &Node) -> anyhow::Result<()>; - - fn get_pending_node(&self) -> anyhow::Result>; - - fn delete_pending_node(&self) -> anyhow::Result<()>; - - fn save_vote(&self, node_id: &NodeId, vote: &Vote) -> anyhow::Result<()>; - - fn get_votes(&self) -> anyhow::Result>; - - fn delete_votes(&self, node_ids: Vec) -> anyhow::Result<()>; - - fn save_certified_node(&self, node: &CertifiedNode) -> anyhow::Result<()>; - - fn get_certified_nodes(&self) -> anyhow::Result>; - - fn delete_certified_nodes(&self, digests: Vec) -> anyhow::Result<()>; - - fn get_latest_k_committed_events(&self, k: u64) -> anyhow::Result>; - - fn get_latest_ledger_info(&self) -> anyhow::Result; - - fn get_epoch_to_proposers(&self) -> HashMap>; -} diff --git a/consensus/src/dag/tests/dag_driver_tests.rs b/consensus/src/dag/tests/dag_driver_tests.rs deleted file mode 100644 index 364a11b19db3d..0000000000000 --- a/consensus/src/dag/tests/dag_driver_tests.rs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - dag::{ - adapter::TLedgerInfoProvider, - anchor_election::RoundRobinAnchorElection, - dag_driver::DagDriver, - dag_fetcher::TFetchRequester, - dag_network::{RpcWithFallback, TDAGNetworkSender}, - dag_store::DagStore, - errors::DagDriverError, - health::{HealthBackoff, NoChainHealth, NoPipelineBackpressure}, - order_rule::OrderRule, - round_state::{OptimisticResponsive, RoundState}, - tests::{ - dag_test::MockStorage, - helpers::{new_certified_node, MockPayloadManager, TEST_DAG_WINDOW}, - order_rule_tests::TestNotifier, - }, - types::{CertifiedAck, DAGMessage, TestAck}, - DAGRpcResult, RpcHandler, - }, - test_utils::MockPayloadManager as MockPayloadClient, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_config::config::DagPayloadConfig; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::Mutex; -use aptos_reliable_broadcast::{RBNetworkSender, ReliableBroadcast}; -use aptos_time_service::TimeService; -use aptos_types::{ - epoch_state::EpochState, - ledger_info::{generate_ledger_info_with_sig, LedgerInfo, LedgerInfoWithSignatures}, - validator_signer::ValidatorSigner, - validator_verifier::{random_validator_verifier, ValidatorVerifier}, -}; -use async_trait::async_trait; -use claims::{assert_ok, assert_ok_eq}; -use futures_channel::mpsc::unbounded; -use std::{sync::Arc, time::Duration}; -use tokio::{runtime::Handle, sync::oneshot}; -use tokio_retry::strategy::ExponentialBackoff; - -struct MockNetworkSender { - _drop_notifier: Option>, -} - -#[async_trait] -impl RBNetworkSender for MockNetworkSender { - async fn send_rb_rpc( - &self, - _receiver: Author, - _messagee: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - Ok(DAGRpcResult(Ok(DAGMessage::TestAck(TestAck(Vec::new()))))) - } -} - -#[async_trait] -impl TDAGNetworkSender for MockNetworkSender { - async fn send_rpc( - &self, - _receiver: Author, - _message: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - unimplemented!() - } - - /// Given a list of potential responders, sending rpc to get response from any of them and could - /// fallback to more in case of failures. - async fn send_rpc_with_fallbacks( - self: Arc, - _responders: Vec, - _message: DAGMessage, - _retry_interval: Duration, - _rpc_timeout: Duration, - _min_concurrent_responders: u32, - _max_concurrent_responders: u32, - ) -> RpcWithFallback { - unimplemented!() - } -} - -struct MockLedgerInfoProvider { - latest_ledger_info: LedgerInfoWithSignatures, -} - -impl TLedgerInfoProvider for MockLedgerInfoProvider { - fn get_latest_ledger_info(&self) -> LedgerInfoWithSignatures { - self.latest_ledger_info.clone() - } - - fn get_highest_committed_anchor_round(&self) -> Round { - self.latest_ledger_info.ledger_info().round() - } -} - -struct MockFetchRequester {} - -impl TFetchRequester for MockFetchRequester { - fn request_for_node(&self, _node: crate::dag::Node) -> anyhow::Result<()> { - unimplemented!() - } - - fn request_for_certified_node(&self, _node: crate::dag::CertifiedNode) -> anyhow::Result<()> { - Ok(()) - } -} - -fn setup( - signers: &[ValidatorSigner], - validator_verifier: ValidatorVerifier, - network_sender: Arc, -) -> DagDriver { - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - - let mock_ledger_info = LedgerInfo::mock_genesis(None); - let mock_ledger_info = generate_ledger_info_with_sig(signers, mock_ledger_info); - let storage = Arc::new(MockStorage::new_with_ledger_info( - mock_ledger_info.clone(), - epoch_state.clone(), - )); - let dag = Arc::new(DagStore::new( - epoch_state.clone(), - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - )); - - let rb = Arc::new(ReliableBroadcast::new( - signers.iter().map(|s| s.author()).collect(), - network_sender.clone(), - ExponentialBackoff::from_millis(10), - aptos_time_service::TimeService::mock(), - Duration::from_millis(500), - BoundedExecutor::new(2, Handle::current()), - )); - let time_service = TimeService::mock(); - let validators = signers.iter().map(|vs| vs.author()).collect(); - let (tx, _) = unbounded(); - let order_rule = Arc::new(Mutex::new(OrderRule::new( - epoch_state.clone(), - 1, - dag.clone(), - Arc::new(RoundRobinAnchorElection::new(validators)), - Arc::new(TestNotifier { tx }), - TEST_DAG_WINDOW as Round, - None, - ))); - - let fetch_requester = Arc::new(MockFetchRequester {}); - - let ledger_info_provider = Arc::new(MockLedgerInfoProvider { - latest_ledger_info: mock_ledger_info, - }); - let (round_tx, _round_rx) = tokio::sync::mpsc::unbounded_channel(); - let round_state = RoundState::new( - round_tx.clone(), - Box::new(OptimisticResponsive::new(round_tx)), - ); - - DagDriver::new( - signers[0].author(), - epoch_state.clone(), - dag, - Arc::new(MockPayloadClient::new(None)), - rb, - time_service, - storage, - order_rule, - fetch_requester, - ledger_info_provider, - round_state, - TEST_DAG_WINDOW as Round, - DagPayloadConfig::default(), - HealthBackoff::new( - epoch_state, - NoChainHealth::new(), - NoPipelineBackpressure::new(), - ), - false, - true, - ) -} - -#[tokio::test] -async fn test_certified_node_handler() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let network_sender = Arc::new(MockNetworkSender { - _drop_notifier: None, - }); - let driver = setup(&signers, validator_verifier, network_sender); - - let first_round_node = new_certified_node(1, signers[0].author(), vec![]); - // expect an ack for a valid message - assert_ok!(driver.process(first_round_node.clone()).await); - // expect an ack if the same message is sent again - assert_ok_eq!(driver.process(first_round_node).await, CertifiedAck::new(1)); - - let parent_node = new_certified_node(1, signers[1].author(), vec![]); - let invalid_node = new_certified_node(2, signers[0].author(), vec![parent_node.certificate()]); - assert_eq!( - driver.process(invalid_node).await.unwrap_err().to_string(), - DagDriverError::MissingParents.to_string() - ); -} - -#[tokio::test] -async fn test_dag_driver_drop() { - aptos_logger::Logger::init_for_testing(); - - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let (tx, rx) = oneshot::channel(); - let network_sender = Arc::new(MockNetworkSender { - _drop_notifier: Some(tx), - }); - let driver = setup(&signers, validator_verifier, network_sender); - - driver.enter_new_round(1).await; - - tokio::spawn(async move { - tokio::time::sleep(Duration::from_secs(5)).await; - drop(driver); - }); - - let _ = rx.await; -} diff --git a/consensus/src/dag/tests/dag_network_test.rs b/consensus/src/dag/tests/dag_network_test.rs deleted file mode 100644 index 7310bb8948e59..0000000000000 --- a/consensus/src/dag/tests/dag_network_test.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::dag::{ - dag_network::{RpcWithFallback, TDAGNetworkSender}, - types::{DAGMessage, TestAck, TestMessage}, - DAGRpcResult, -}; -use anyhow::{anyhow, bail}; -use aptos_consensus_types::common::Author; -use aptos_infallible::Mutex; -use aptos_reliable_broadcast::RBNetworkSender; -use aptos_time_service::{TimeService, TimeServiceTrait}; -use aptos_types::validator_verifier::random_validator_verifier; -use async_trait::async_trait; -use claims::{assert_err, assert_ok}; -use futures::StreamExt; -use std::{collections::HashMap, sync::Arc, time::Duration}; - -#[derive(Clone)] -enum TestPeerState { - Fast, - Slow(Duration), - FailSlow(Duration), -} - -#[derive(Clone)] -struct MockDAGNetworkSender { - time_service: TimeService, - test_peer_state: Arc>>, -} - -#[async_trait] -impl RBNetworkSender for MockDAGNetworkSender { - async fn send_rb_rpc( - &self, - _receiver: Author, - _message: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - unimplemented!() - } -} - -#[async_trait] -impl TDAGNetworkSender for MockDAGNetworkSender { - async fn send_rpc( - &self, - receiver: Author, - message: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - let message: TestMessage = message.try_into()?; - let state = { - self.test_peer_state - .lock() - .get(&receiver) - .ok_or_else(|| anyhow!("lookup failed"))? - .clone() - }; - match state { - TestPeerState::Fast => Ok(Ok(TestAck(message.0).into()).into()), - TestPeerState::Slow(duration) => { - self.time_service.sleep(duration).await; - Ok(Ok(TestAck(message.0).into()).into()) - }, - TestPeerState::FailSlow(duration) => { - self.time_service.sleep(duration).await; - bail!("failed to respond"); - }, - } - } - - async fn send_rpc_with_fallbacks( - self: Arc, - responders: Vec, - message: DAGMessage, - retry_interval: Duration, - rpc_timeout: Duration, - min_concurrent_responders: u32, - max_concurrent_responders: u32, - ) -> RpcWithFallback { - RpcWithFallback::new( - responders, - message, - retry_interval, - rpc_timeout, - self.clone(), - self.time_service.clone(), - min_concurrent_responders, - max_concurrent_responders, - ) - } -} - -#[tokio::test] -async fn test_send_rpc_with_fallback() { - let (_, validator_verifier) = random_validator_verifier(5, None, false); - let validators = validator_verifier.get_ordered_account_addresses(); - let time_service = TimeService::real(); - - let sender = MockDAGNetworkSender { - time_service: time_service.clone(), - test_peer_state: Arc::new(Mutex::new(HashMap::from([ - (validators[0], TestPeerState::Fast), - ( - validators[1], - TestPeerState::FailSlow(Duration::from_secs(1)), - ), - (validators[2], TestPeerState::Slow(Duration::from_secs(5))), - ( - validators[3], - TestPeerState::FailSlow(Duration::from_secs(3)), - ), - (validators[4], TestPeerState::Slow(Duration::from_secs(2))), - ]))), - }; - - let message = TestMessage(vec![42; validators.len() - 1]); - let mut rpc = Arc::new(sender) - .send_rpc_with_fallbacks( - validators, - message.into(), - Duration::from_millis(100), - Duration::from_secs(5), - 1, - 4, - ) - .await; - - assert_ok!(rpc.next().await.unwrap().result.unwrap().0); - assert_err!(rpc.next().await.unwrap().result); - assert_ok!(rpc.next().await.unwrap().result.unwrap().0); - assert_err!(rpc.next().await.unwrap().result); - assert_ok!(rpc.next().await.unwrap().result.unwrap().0); -} diff --git a/consensus/src/dag/tests/dag_state_sync_tests.rs b/consensus/src/dag/tests/dag_state_sync_tests.rs deleted file mode 100644 index 53c65a3db9010..0000000000000 --- a/consensus/src/dag/tests/dag_state_sync_tests.rs +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright © Aptos Foundation - -use super::helpers::TEST_DAG_WINDOW; -use crate::{ - dag::{ - adapter::OrderedNotifier, - dag_fetcher::{FetchRequestHandler, TDagFetcher}, - dag_state_sync::DagStateSynchronizer, - dag_store::DagStore, - errors::DagFetchError, - storage::DAGStorage, - tests::{ - dag_test::MockStorage, - helpers::{generate_dag_nodes, MockPayloadManager}, - }, - types::{CertifiedNodeMessage, RemoteFetchRequest}, - CertifiedNode, DAGMessage, DAGRpcResult, RpcHandler, RpcWithFallback, TDAGNetworkSender, - }, - pipeline::execution_client::DummyExecutionClient, -}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::HashValue; -use aptos_reliable_broadcast::RBNetworkSender; -use aptos_time_service::TimeService; -use aptos_types::{ - aggregate_signature::AggregateSignature, - block_info::BlockInfo, - epoch_state::EpochState, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - validator_verifier::random_validator_verifier, -}; -use async_trait::async_trait; -use claims::assert_none; -use std::{sync::Arc, time::Duration}; - -struct MockDAGNetworkSender {} - -#[async_trait] -impl RBNetworkSender for MockDAGNetworkSender { - async fn send_rb_rpc( - &self, - _receiver: Author, - _message: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - unimplemented!() - } -} - -#[async_trait] -impl TDAGNetworkSender for MockDAGNetworkSender { - async fn send_rpc( - &self, - _receiver: Author, - _message: DAGMessage, - _timeout: Duration, - ) -> anyhow::Result { - unimplemented!() - } - - /// Given a list of potential responders, sending rpc to get response from any of them and could - /// fallback to more in case of failures. - async fn send_rpc_with_fallbacks( - self: Arc, - _responders: Vec, - _message: DAGMessage, - _retry_interval: Duration, - _rpc_timeout: Duration, - _min_concurrent_responders: u32, - _max_concurrent_responders: u32, - ) -> RpcWithFallback { - unimplemented!() - } -} - -struct MockDagFetcher { - target_dag: Arc, - epoch_state: Arc, -} - -#[async_trait] -impl TDagFetcher for MockDagFetcher { - async fn fetch( - &self, - remote_request: RemoteFetchRequest, - _responders: Vec, - new_dag: Arc, - ) -> Result<(), DagFetchError> { - let response = FetchRequestHandler::new(self.target_dag.clone(), self.epoch_state.clone()) - .process(remote_request) - .await - .unwrap(); - - for node in response.certified_nodes().into_iter().rev() { - new_dag.write().add_node_for_test(node).unwrap() - } - - Ok(()) - } -} - -struct MockNotifier {} - -#[async_trait] -impl OrderedNotifier for MockNotifier { - fn send_ordered_nodes( - &self, - _ordered_nodes: Vec>, - _failed_author: Vec<(Round, Author)>, - ) { - } -} - -fn setup(epoch_state: Arc, storage: Arc) -> DagStateSynchronizer { - let time_service = TimeService::mock(); - let execution_client = Arc::new(DummyExecutionClient {}); - let payload_manager = Arc::new(MockPayloadManager {}); - - DagStateSynchronizer::new( - epoch_state, - time_service, - execution_client, - storage, - payload_manager, - TEST_DAG_WINDOW as Round, - ) -} - -#[tokio::test] -async fn test_dag_state_sync() { - const NUM_ROUNDS: u64 = 90; - const LI_ROUNDS: u64 = NUM_ROUNDS * 2 / 3; - const SLOW_DAG_ROUNDS: u64 = NUM_ROUNDS / 3; - - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let validators = validator_verifier.get_ordered_account_addresses(); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - let storage = Arc::new(MockStorage::new()); - - let virtual_dag = (0..NUM_ROUNDS) - .map(|_| { - signers - .iter() - .map(|_| Some(vec![true; signers.len() * 2 / 3 + 1])) - .collect() - }) - .collect::>(); - let nodes = generate_dag_nodes(&virtual_dag, &validators); - - let fast_dag = Arc::new(DagStore::new( - epoch_state.clone(), - Arc::new(MockStorage::new()), - Arc::new(MockPayloadManager {}), - 1, - 0, - )); - for round_nodes in &nodes { - for node in round_nodes.iter().flatten() { - fast_dag.write().add_node_for_test(node.clone()).unwrap(); - } - } - - let slow_dag = Arc::new(DagStore::new( - epoch_state.clone(), - Arc::new(MockStorage::new()), - Arc::new(MockPayloadManager {}), - 1, - 0, - )); - for round_nodes in nodes.iter().take(SLOW_DAG_ROUNDS as usize) { - for node in round_nodes.iter().flatten() { - slow_dag.write().add_node_for_test(node.clone()).unwrap(); - } - } - - let li_node = nodes[LI_ROUNDS as usize - 1] - .first() - .unwrap() - .clone() - .unwrap(); - let sync_to_li = LedgerInfoWithSignatures::new( - LedgerInfo::new( - BlockInfo::new( - epoch_state.epoch, - li_node.round(), - HashValue::zero(), - HashValue::zero(), - 0, - 0, - None, - ), - li_node.digest(), - ), - AggregateSignature::empty(), - ); - let sync_to_node = nodes[NUM_ROUNDS as usize - 1] - .first() - .unwrap() - .clone() - .unwrap(); - - let sync_node_li = CertifiedNodeMessage::new(sync_to_node, sync_to_li); - - let state_sync = setup(epoch_state.clone(), storage.clone()); - let dag_fetcher = MockDagFetcher { - target_dag: fast_dag.clone(), - epoch_state: epoch_state.clone(), - }; - - let (request, responders, sync_dag_store) = - state_sync.build_request(&sync_node_li, slow_dag.clone(), 0); - - let sync_result = state_sync - .sync_dag_to( - dag_fetcher, - request, - responders, - sync_dag_store, - sync_node_li.ledger_info().clone(), - ) - .await; - let new_dag = sync_result.unwrap(); - - assert_eq!( - new_dag.read().lowest_round(), - (LI_ROUNDS - TEST_DAG_WINDOW) as Round - ); - assert_eq!(new_dag.read().highest_round(), NUM_ROUNDS as Round); - assert_none!(new_dag.read().highest_ordered_anchor_round(),); -} diff --git a/consensus/src/dag/tests/dag_test.rs b/consensus/src/dag/tests/dag_test.rs deleted file mode 100644 index c050b81c55cbf..0000000000000 --- a/consensus/src/dag/tests/dag_test.rs +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::helpers::MockPayloadManager; -use crate::dag::{ - dag_store::DagStore, - storage::{CommitEvent, DAGStorage}, - tests::helpers::{new_certified_node, TEST_DAG_WINDOW}, - types::{CertifiedNode, DagSnapshotBitmask, Node}, - NodeId, Vote, -}; -use aptos_consensus_types::common::Author; -use aptos_crypto::HashValue; -use aptos_infallible::Mutex; -use aptos_types::{ - epoch_state::EpochState, ledger_info::LedgerInfoWithSignatures, - validator_signer::ValidatorSigner, validator_verifier::random_validator_verifier, -}; -use std::{collections::HashMap, sync::Arc}; - -pub struct MockStorage { - node_data: Mutex>, - vote_data: Mutex>, - certified_node_data: Mutex>, - latest_ledger_info: Option, - epoch_state: Option>, -} - -impl MockStorage { - pub fn new() -> Self { - Self { - node_data: Mutex::new(None), - vote_data: Mutex::new(HashMap::new()), - certified_node_data: Mutex::new(HashMap::new()), - latest_ledger_info: None, - epoch_state: None, - } - } - - pub fn new_with_ledger_info( - ledger_info: LedgerInfoWithSignatures, - epoch_state: Arc, - ) -> Self { - Self { - node_data: Mutex::new(None), - vote_data: Mutex::new(HashMap::new()), - certified_node_data: Mutex::new(HashMap::new()), - latest_ledger_info: Some(ledger_info), - epoch_state: Some(epoch_state), - } - } -} - -impl DAGStorage for MockStorage { - fn save_pending_node(&self, node: &Node) -> anyhow::Result<()> { - self.node_data.lock().replace(node.clone()); - Ok(()) - } - - fn get_pending_node(&self) -> anyhow::Result> { - Ok(self.node_data.lock().clone()) - } - - fn delete_pending_node(&self) -> anyhow::Result<()> { - self.node_data.lock().take(); - Ok(()) - } - - fn save_vote(&self, node_id: &NodeId, vote: &Vote) -> anyhow::Result<()> { - self.vote_data.lock().insert(node_id.clone(), vote.clone()); - Ok(()) - } - - fn get_votes(&self) -> anyhow::Result> { - Ok(self.vote_data.lock().clone().into_iter().collect()) - } - - fn delete_votes(&self, node_ids: Vec) -> anyhow::Result<()> { - for node_id in node_ids { - self.vote_data.lock().remove(&node_id); - } - Ok(()) - } - - fn save_certified_node(&self, node: &CertifiedNode) -> anyhow::Result<()> { - self.certified_node_data - .lock() - .insert(node.digest(), node.clone()); - Ok(()) - } - - fn get_certified_nodes(&self) -> anyhow::Result> { - Ok(self - .certified_node_data - .lock() - .clone() - .into_iter() - .collect()) - } - - fn delete_certified_nodes(&self, digests: Vec) -> anyhow::Result<()> { - for digest in digests { - self.certified_node_data.lock().remove(&digest); - } - Ok(()) - } - - fn get_latest_k_committed_events(&self, _k: u64) -> anyhow::Result> { - Ok(vec![]) - } - - fn get_latest_ledger_info(&self) -> anyhow::Result { - self.latest_ledger_info - .clone() - .ok_or_else(|| anyhow::anyhow!("ledger info not set")) - } - - fn get_epoch_to_proposers(&self) -> HashMap> { - self.epoch_state - .as_ref() - .map(|epoch_state| { - [( - epoch_state.epoch, - epoch_state.verifier.get_ordered_account_addresses(), - )] - .into() - }) - .unwrap_or_default() - } -} - -fn setup() -> ( - Vec, - Arc, - DagStore, - Arc, -) { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - let storage = Arc::new(MockStorage::new()); - let payload_manager = Arc::new(MockPayloadManager {}); - let dag = DagStore::new( - epoch_state.clone(), - storage.clone(), - payload_manager, - 1, - TEST_DAG_WINDOW, - ); - (signers, epoch_state, dag, storage) -} - -#[test] -fn test_dag_insertion_succeed() { - let (signers, epoch_state, dag, _) = setup(); - - // Round 1 - nodes 0, 1, 2 links to vec![] - for signer in &signers[0..3] { - let node = new_certified_node(1, signer.author(), vec![]); - assert!(dag.write().add_node_for_test(node).is_ok()); - } - let parents = dag - .read() - .get_strong_links_for_round(1, &epoch_state.verifier) - .unwrap(); - - // Round 2 nodes 0, 1, 2 links to 0, 1, 2 - for signer in &signers[0..3] { - let node = new_certified_node(2, signer.author(), parents.clone()); - assert!(dag.write().add_node_for_test(node).is_ok()); - } - - // Round 3 nodes 1, 2 links to 0, 1, 2 - let parents = dag - .read() - .get_strong_links_for_round(2, &epoch_state.verifier) - .unwrap(); - - for signer in &signers[1..3] { - let node = new_certified_node(3, signer.author(), parents.clone()); - assert!(dag.write().add_node_for_test(node).is_ok()); - } - - // not enough strong links - assert!(dag - .read() - .get_strong_links_for_round(3, &epoch_state.verifier) - .is_none()); -} - -#[test] -fn test_dag_insertion_failure() { - let (signers, epoch_state, dag, _) = setup(); - - // Round 1 - nodes 0, 1, 2 links to vec![] - for signer in &signers[0..3] { - let node = new_certified_node(1, signer.author(), vec![]); - assert!(dag.write().add_node_for_test(node.clone()).is_ok()); - // duplicate node - assert!(dag.write().add_node_for_test(node).is_err()); - } - - let missing_node = new_certified_node(1, signers[3].author(), vec![]); - let mut parents = dag - .read() - .get_strong_links_for_round(1, &epoch_state.verifier) - .unwrap(); - parents.push(missing_node.certificate()); - - let node = new_certified_node(2, signers[0].author(), parents.clone()); - // parents not exist - assert!(dag.write().add_node_for_test(node).is_err()); - - let node = new_certified_node(3, signers[0].author(), vec![]); - // round too high - assert!(dag.write().add_node_for_test(node).is_err()); - - let node = new_certified_node(2, signers[0].author(), parents[0..3].to_vec()); - assert!(dag.write().add_node_for_test(node).is_ok()); - let node = new_certified_node(2, signers[0].author(), vec![]); - // equivocation node - assert!(dag.write().add_node_for_test(node).is_err()); -} - -#[test] -fn test_dag_recover_from_storage() { - let (signers, epoch_state, dag, storage) = setup(); - - let mut metadatas = vec![]; - - for round in 1..10 { - let parents = dag - .read() - .get_strong_links_for_round(round, &epoch_state.verifier) - .unwrap_or_default(); - for signer in &signers[0..3] { - let node = new_certified_node(round, signer.author(), parents.clone()); - metadatas.push(node.metadata().clone()); - assert!(dag.add_node(node).is_ok()); - } - } - let new_dag = DagStore::new( - epoch_state.clone(), - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - ); - - for metadata in &metadatas { - assert!(new_dag.read().exists(metadata)); - } - - let new_epoch_state = Arc::new(EpochState { - epoch: 2, - verifier: epoch_state.verifier.clone(), - }); - - let _new_epoch_dag = DagStore::new( - new_epoch_state, - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - ); - assert!(storage.certified_node_data.lock().is_empty()); -} - -#[test] -fn test_dag_bitmask() { - let (signers, epoch_state, dag, _) = setup(); - - assert_eq!( - dag.read().bitmask(TEST_DAG_WINDOW), - DagSnapshotBitmask::new(1, vec![vec![false; 4]; TEST_DAG_WINDOW as usize]) - ); - - for round in 1..5 { - let parents = dag - .read() - .get_strong_links_for_round(round - 1, &epoch_state.verifier) - .unwrap_or_default(); - if round > 1 { - assert!(!parents.is_empty()); - } - for signer in &signers[0..3] { - let node = new_certified_node(round, signer.author(), parents.clone()); - assert!(dag.write().add_node_for_test(node).is_ok()); - } - } - let mut bitmask = vec![vec![true, true, true, false]; 2]; - bitmask.resize(TEST_DAG_WINDOW as usize + 1, vec![false; 4]); - assert_eq!(dag.read().bitmask(8), DagSnapshotBitmask::new(3, bitmask)); - - // Populate the fourth author for all rounds - for round in 1..5 { - let parents = dag - .read() - .get_strong_links_for_round(round - 1, &epoch_state.verifier) - .unwrap_or_default(); - if round > 1 { - assert!(!parents.is_empty()); - } - let node = new_certified_node(round, signers[3].author(), parents.clone()); - assert!(dag.write().add_node_for_test(node).is_ok()); - } - assert_eq!( - dag.read().bitmask(10), - DagSnapshotBitmask::new(5, vec![vec![false; 4]; 6]) - ); - assert_eq!( - dag.read().bitmask(6), - DagSnapshotBitmask::new(5, vec![vec![false; 4]; 2]) - ); -} diff --git a/consensus/src/dag/tests/fetcher_test.rs b/consensus/src/dag/tests/fetcher_test.rs deleted file mode 100644 index 9941ed04b103f..0000000000000 --- a/consensus/src/dag/tests/fetcher_test.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright © Aptos Foundation - -use super::dag_test::MockStorage; -use crate::dag::{ - dag_fetcher::FetchRequestHandler, - dag_store::DagStore, - tests::helpers::{new_certified_node, MockPayloadManager, TEST_DAG_WINDOW}, - types::{DagSnapshotBitmask, FetchResponse, RemoteFetchRequest}, - RpcHandler, -}; -use aptos_types::{epoch_state::EpochState, validator_verifier::random_validator_verifier}; -use claims::assert_ok_eq; -use std::sync::Arc; - -#[tokio::test] -async fn test_dag_fetcher_receiver() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - let storage = Arc::new(MockStorage::new()); - let dag = Arc::new(DagStore::new( - epoch_state.clone(), - storage, - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - )); - - let fetcher = FetchRequestHandler::new(dag.clone(), epoch_state); - - let mut first_round_nodes = vec![]; - - // Round 1 - nodes 0, 1, 2 links to vec![] - for signer in &signers[0..3] { - let node = new_certified_node(1, signer.author(), vec![]); - assert!(dag.add_node(node.clone()).is_ok()); - first_round_nodes.push(node); - } - - // Round 2 - node 0 - let target_node = new_certified_node(2, signers[0].author(), vec![ - first_round_nodes[0].certificate(), - first_round_nodes[1].certificate(), - ]); - - let request = RemoteFetchRequest::new( - target_node.epoch(), - target_node - .parents() - .iter() - .map(|parent| parent.metadata().clone()) - .collect(), - DagSnapshotBitmask::new(1, vec![vec![true, false]]), - ); - assert_ok_eq!( - fetcher.process(request).await, - FetchResponse::new(1, vec![first_round_nodes[1].clone()]) - ); -} - -// TODO: add more tests after commit rule tests diff --git a/consensus/src/dag/tests/helpers.rs b/consensus/src/dag/tests/helpers.rs deleted file mode 100644 index dbcd86b9dd033..0000000000000 --- a/consensus/src/dag/tests/helpers.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - dag::{ - order_rule::TOrderRule, - types::{CertifiedNode, Extensions, Node, NodeCertificate, NodeMetadata}, - }, - payload_manager::TPayloadManager, -}; -use aptos_consensus_types::common::{Author, Payload, Round}; -use aptos_types::aggregate_signature::AggregateSignature; - -pub(super) const TEST_DAG_WINDOW: u64 = 5; - -pub(super) struct MockPayloadManager {} - -impl TPayloadManager for MockPayloadManager { - fn prefetch_payload_data(&self, _payload: &Payload, _timestamp: u64) {} -} - -pub(super) struct MockOrderRule {} - -impl TOrderRule for MockOrderRule { - fn process_new_node(&self, _node_metadata: &NodeMetadata) {} - - fn process_all(&self) {} -} - -pub(crate) fn new_certified_node( - round: Round, - author: Author, - parents: Vec, -) -> CertifiedNode { - let node = Node::new( - 1, - round, - author, - 0, - vec![], - Payload::empty(false, true), - parents, - Extensions::empty(), - ); - CertifiedNode::new(node, AggregateSignature::empty()) -} - -pub(crate) fn new_node( - round: Round, - timestamp: u64, - author: Author, - parents: Vec, -) -> Node { - Node::new( - 1, - round, - author, - timestamp, - vec![], - Payload::empty(false, true), - parents, - Extensions::empty(), - ) -} - -/// Generate certified nodes for dag given the virtual dag -pub(crate) fn generate_dag_nodes( - dag: &[Vec>>], - validators: &[Author], -) -> Vec>> { - let mut nodes = vec![]; - let mut previous_round: Vec> = vec![]; - for (round, round_nodes) in dag.iter().enumerate() { - let mut nodes_at_round = vec![]; - for (idx, author) in validators.iter().enumerate() { - if let Some(bitmask) = &round_nodes[idx] { - // the bitmask is compressed (without the holes), we need to flatten the previous round nodes - // to match the index - let parents: Vec<_> = previous_round - .iter() - .flatten() - .enumerate() - .filter(|(idx, _)| *bitmask.get(*idx).unwrap_or(&false)) - .map(|(_, node)| { - NodeCertificate::new(node.metadata().clone(), AggregateSignature::empty()) - }) - .collect(); - if round > 1 { - assert_eq!(parents.len(), validators.len() * 2 / 3 + 1); - } - nodes_at_round.push(Some(new_certified_node( - (round + 1) as u64, - *author, - parents, - ))); - } else { - nodes_at_round.push(None); - } - } - previous_round = nodes_at_round.clone(); - nodes.push(nodes_at_round); - } - nodes -} diff --git a/consensus/src/dag/tests/integration_tests.rs b/consensus/src/dag/tests/integration_tests.rs deleted file mode 100644 index 7abcf9919712b..0000000000000 --- a/consensus/src/dag/tests/integration_tests.rs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright © Aptos Foundation - -use super::dag_test; -use crate::{ - dag::{bootstrap::bootstrap_dag_for_test, dag_state_sync::SyncOutcome}, - network::{IncomingDAGRequest, NetworkSender, RpcResponder}, - network_interface::{ConsensusMsg, ConsensusNetworkClient, DIRECT_SEND, RPC}, - network_tests::{NetworkPlayground, TwinId}, - payload_manager::PayloadManager, - pipeline::{buffer_manager::OrderedBlocks, execution_client::DummyExecutionClient}, - test_utils::{consensus_runtime, MockPayloadManager, MockStorage}, -}; -use aptos_channels::{aptos_channel, message_queues::QueueStyle}; -use aptos_config::network_id::{NetworkId, PeerNetworkId}; -use aptos_consensus_types::common::Author; -use aptos_logger::debug; -use aptos_network::{ - application::interface::NetworkClient, - peer_manager::{conn_notifs_channel, ConnectionRequestSender, PeerManagerRequestSender}, - protocols::{ - network::{self, Event, NetworkEvents, NewNetworkEvents, NewNetworkSender}, - wire::handshake::v1::ProtocolIdSet, - }, - transport::ConnectionMetadata, - ProtocolId, -}; -use aptos_time_service::TimeService; -use aptos_types::{ - epoch_state::EpochState, - ledger_info::generate_ledger_info_with_sig, - validator_signer::ValidatorSigner, - validator_verifier::{random_validator_verifier, ValidatorVerifier}, -}; -use claims::assert_gt; -use futures::{ - stream::{select, Select}, - StreamExt, -}; -use futures_channel::mpsc::UnboundedReceiver; -use maplit::hashmap; -use std::sync::Arc; -use tokio::task::JoinHandle; - -struct DagBootstrapUnit { - nh_task_handle: JoinHandle, - df_task_handle: JoinHandle<()>, - dag_rpc_tx: aptos_channel::Sender, - network_events: Box< - Select, aptos_channels::UnboundedReceiver>>, - >, -} - -impl DagBootstrapUnit { - fn make( - self_peer: Author, - epoch: u64, - signer: ValidatorSigner, - storage: Arc, - network: NetworkSender, - time_service: TimeService, - network_events: Box< - Select< - NetworkEvents, - aptos_channels::UnboundedReceiver>, - >, - >, - all_signers: Vec, - ) -> (Self, UnboundedReceiver) { - let epoch_state = Arc::new(EpochState { - epoch, - verifier: storage.get_validator_set().into(), - }); - let ledger_info = generate_ledger_info_with_sig(&all_signers, storage.get_ledger_info()); - let dag_storage = - dag_test::MockStorage::new_with_ledger_info(ledger_info, epoch_state.clone()); - - let network = Arc::new(network); - - let payload_client = Arc::new(MockPayloadManager::new(None)); - let payload_manager = Arc::new(PayloadManager::DirectMempool); - - let execution_client = Arc::new(DummyExecutionClient); - - let (nh_abort_handle, df_abort_handle, dag_rpc_tx, ordered_nodes_rx) = - bootstrap_dag_for_test( - self_peer, - signer, - epoch_state, - Arc::new(dag_storage), - network.clone(), - network.clone(), - network.clone(), - time_service, - payload_manager, - payload_client, - execution_client, - ); - - ( - Self { - nh_task_handle: nh_abort_handle, - df_task_handle: df_abort_handle, - dag_rpc_tx, - network_events, - }, - ordered_nodes_rx, - ) - } - - async fn start(mut self) { - loop { - match self.network_events.next().await.unwrap() { - Event::RpcRequest(sender, msg, protocol, response_sender) => match msg { - ConsensusMsg::DAGMessage(msg) => { - debug!("handling RPC..."); - self.dag_rpc_tx.push(sender, IncomingDAGRequest { - req: msg, - sender, - responder: RpcResponder { - protocol, - response_sender, - }, - }) - }, - _ => unreachable!("expected only DAG-related messages"), - }, - _ => panic!("Unexpected Network Event"), - } - .unwrap() - } - } -} - -fn create_network( - playground: &mut NetworkPlayground, - id: usize, - author: Author, - validators: ValidatorVerifier, -) -> ( - NetworkSender, - Box< - Select, aptos_channels::UnboundedReceiver>>, - >, -) { - let (network_reqs_tx, network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (consensus_tx, consensus_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = aptos_channels::new_test(8); - let (_, conn_status_rx) = conn_notifs_channel::new(); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - playground.peer_protocols(), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - let network_events = NetworkEvents::new(consensus_rx, conn_status_rx, None); - - let (self_sender, self_receiver) = aptos_channels::new_unbounded_test(); - let network = NetworkSender::new(author, consensus_network_client, self_sender, validators); - - let twin_id = TwinId { id, author }; - - playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx); - - let all_network_events = Box::new(select(network_events, self_receiver)); - - (network, all_network_events) -} - -fn bootstrap_nodes( - playground: &mut NetworkPlayground, - signers: Vec, - validators: ValidatorVerifier, -) -> (Vec, Vec>) { - let peers_and_metadata = playground.peer_protocols(); - let (nodes, ordered_node_receivers) = signers - .iter() - .enumerate() - .map(|(id, signer)| { - let peer_id = signer.author(); - let mut conn_meta = ConnectionMetadata::mock(peer_id); - conn_meta.application_protocols = ProtocolIdSet::from_iter([ - ProtocolId::ConsensusDirectSendJson, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusRpcBcs, - ]); - let peer_network_id = PeerNetworkId::new(NetworkId::Validator, peer_id); - peers_and_metadata - .insert_connection_metadata(peer_network_id, conn_meta) - .unwrap(); - - let (_, storage) = MockStorage::start_for_testing((&validators).into()); - let (network, network_events) = - create_network(playground, id, signer.author(), validators.clone()); - - DagBootstrapUnit::make( - signer.author(), - 1, - signer.clone(), - storage, - network, - aptos_time_service::TimeService::real(), - network_events, - signers.clone(), - ) - }) - .unzip(); - - (nodes, ordered_node_receivers) -} - -#[tokio::test] -async fn test_dag_e2e() { - let num_nodes = 7; - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let (signers, validators) = random_validator_verifier(num_nodes, None, false); - let (nodes, mut ordered_node_receivers) = bootstrap_nodes(&mut playground, signers, validators); - let tasks: Vec<_> = nodes - .into_iter() - .map(|node| runtime.spawn(node.start())) - .collect(); - runtime.spawn(playground.start()); - - for _ in 1..10 { - let mut all_ordered = vec![]; - for receiver in &mut ordered_node_receivers { - let block = receiver.next().await.unwrap(); - all_ordered.push(block.ordered_blocks) - } - let first = all_ordered.first().unwrap(); - assert_gt!(first.len(), 0, "must order nodes"); - for a in all_ordered.iter() { - assert_eq!(a.len(), first.len(), "length should match"); - assert_eq!(a, first); - } - } - for task in tasks { - task.abort(); - let _ = task.await; - } - runtime.shutdown_background(); -} diff --git a/consensus/src/dag/tests/mod.rs b/consensus/src/dag/tests/mod.rs deleted file mode 100644 index eee0c463dede8..0000000000000 --- a/consensus/src/dag/tests/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod dag_driver_tests; -mod dag_network_test; -mod dag_state_sync_tests; -mod dag_test; -mod fetcher_test; -mod helpers; -mod integration_tests; -mod order_rule_tests; -mod rb_handler_tests; -mod types_test; diff --git a/consensus/src/dag/tests/order_rule_tests.rs b/consensus/src/dag/tests/order_rule_tests.rs deleted file mode 100644 index fcd6dac5d0c16..0000000000000 --- a/consensus/src/dag/tests/order_rule_tests.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::dag::{ - adapter::OrderedNotifier, - anchor_election::RoundRobinAnchorElection, - dag_store::{DagStore, InMemDag}, - order_rule::OrderRule, - tests::{ - dag_test::MockStorage, - helpers::{generate_dag_nodes, MockPayloadManager, TEST_DAG_WINDOW}, - }, - types::NodeMetadata, - CertifiedNode, -}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::Mutex; -use aptos_types::{epoch_state::EpochState, validator_verifier::random_validator_verifier}; -use async_trait::async_trait; -use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use proptest::prelude::*; -use std::sync::Arc; - -/// Generate a virtual dag that first layer represents round -/// second layer represents nodes, Some => node exist, None => not exist -/// third layer is a bitmask that represents compressed strong links (true => linked, false => not linked), -/// the bitmask ignores non-existing nodes -fn generate_virtual_dag( - num_nodes: usize, - num_holes: usize, - round: u64, -) -> impl Strategy>>>> { - let num_strong_links = num_nodes * 2 / 3 + 1; - assert!(num_holes <= num_nodes - num_strong_links); - // This only has length of num_nodes - num_holes which ignores holes - let strong_links: Vec = std::iter::repeat(false) - .take(num_nodes - num_holes - num_strong_links) - .chain(std::iter::repeat(true).take(num_strong_links)) - .collect(); - // This has length of num_nodes - let nodes: Vec = std::iter::repeat(false) - .take(num_holes) - .chain(std::iter::repeat(true).take(num_nodes - num_holes)) - .collect(); - // For every round, we shuffle the nodes bitmask to generate holes - // For every node, we shuffle the compressed strong links if the node is not a hole - proptest::collection::vec( - Just(nodes).prop_shuffle().prop_flat_map(move |nodes| { - nodes - .into_iter() - .map(|exist| { - if exist { - Just(strong_links.clone()) - .prop_shuffle() - .prop_map(Some) - .boxed() - } else { - Just(None).boxed() - } - }) - .collect::>() - }), - round as usize, - ) -} - -/// Generate `num_perm` random permutations of how nodes are processed by the order rule -/// Imagine we have 4 nodes, this generates `num_perm` permutations of [0, 1, 2, 3] -fn generate_permutations( - num_perm: usize, - total_number: usize, -) -> impl Strategy>> { - proptest::collection::vec( - Just((0..total_number).collect::>()).prop_shuffle(), - num_perm, - ) -} - -pub struct TestNotifier { - pub tx: UnboundedSender>>, -} - -#[async_trait] -impl OrderedNotifier for TestNotifier { - fn send_ordered_nodes( - &self, - ordered_nodes: Vec>, - _failed_authors: Vec<(Round, Author)>, - ) { - self.tx.unbounded_send(ordered_nodes).unwrap() - } -} - -fn create_order_rule( - epoch_state: Arc, - dag: Arc, -) -> (OrderRule, UnboundedReceiver>>) { - let anchor_election = Arc::new(RoundRobinAnchorElection::new( - epoch_state.verifier.get_ordered_account_addresses(), - )); - let (tx, rx) = unbounded(); - ( - OrderRule::new( - epoch_state, - 1, - dag, - anchor_election, - Arc::new(TestNotifier { tx }), - TEST_DAG_WINDOW as Round, - None, - ), - rx, - ) -} - -const NUM_HOLES: usize = 1; -const NUM_VALIDATORS: usize = 5; -const NUM_ROUNDS: u64 = 50; -const NUM_PERMUTATION: usize = 100; - -proptest! { - #[test] - fn test_order_rule_safety( - mut dag_with_holes in generate_virtual_dag(NUM_VALIDATORS, NUM_HOLES, NUM_ROUNDS), - mut dag in generate_virtual_dag(NUM_VALIDATORS, 0, NUM_ROUNDS), - sequences in generate_permutations(NUM_PERMUTATION, (NUM_VALIDATORS - NUM_HOLES) * NUM_ROUNDS as usize) - ) { - let (_, validator_verifier) = random_validator_verifier(NUM_VALIDATORS, None, false); - let validators = validator_verifier.get_ordered_account_addresses(); - let author_indexes = validator_verifier.address_to_validator_index().clone(); - dag.append(&mut dag_with_holes); - let nodes = generate_dag_nodes(&dag, &validators); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - let mut dag = InMemDag::new_empty(epoch_state.clone(), 0, TEST_DAG_WINDOW); - for round_nodes in &nodes { - for node in round_nodes.iter().flatten() { - dag.add_node_for_test(node.clone()).unwrap(); - } - } - let flatten_nodes: Vec<_> = nodes.into_iter().flatten().flatten().collect(); - let all_ordered = Arc::new(Mutex::new(vec![])); - rayon::scope(|s| { - for seq in sequences { - s.spawn(|_| { - let dag = Arc::new(DagStore::new_for_test(dag.clone(),Arc::new(MockStorage::new()), Arc::new(MockPayloadManager {}))); - let (mut order_rule, mut receiver) = create_order_rule(epoch_state.clone(), dag); - for idx in seq { - order_rule.process_new_node(flatten_nodes[idx].metadata()); - } - let mut ordered = vec![]; - while let Ok(Some(mut ordered_nodes)) = receiver.try_next() { - ordered.append(&mut ordered_nodes); - } - all_ordered.lock().push(ordered); - }); - } - }); - // order produced by process_all - let dag = Arc::new(DagStore::new_for_test(dag.clone(),Arc::new(MockStorage::new()), Arc::new(MockPayloadManager {}))); - let (mut order_rule, mut receiver) = create_order_rule(epoch_state.clone(), dag); - order_rule.process_all(); - let mut ordered = vec![]; - while let Ok(Some(mut ordered_nodes)) = receiver.try_next() { - ordered.append(&mut ordered_nodes); - } - let display = |node: &Arc| { - (node.metadata().round(), *author_indexes.get(node.metadata().author()).unwrap()) - }; - let longest: Vec<_> = ordered.iter().map(display).collect(); - - for ordered in all_ordered.lock().iter() { - let a: Vec<_> = ordered.iter().map(display).collect(); - assert_eq!(a, longest[..a.len()]); - } - } -} - -#[test] -fn test_order_rule_basic() { - let dag = vec![ - vec![Some(vec![]), Some(vec![]), Some(vec![]), Some(vec![])], - vec![ - Some(vec![false, true, true, true]), - Some(vec![true, true, true, false]), - Some(vec![false, true, true, true]), - None, - ], - vec![ - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - ], - vec![ - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - Some(vec![true, false, true, true]), - None, - ], - vec![ - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - None, - ], - vec![ - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - Some(vec![true, true, true, false]), - None, - ], - ]; - let (_, validator_verifier) = random_validator_verifier(4, None, false); - let validators = validator_verifier.get_ordered_account_addresses(); - let author_indexes = validator_verifier.address_to_validator_index().clone(); - let nodes = generate_dag_nodes(&dag, &validators); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - let mut dag = InMemDag::new_empty(epoch_state.clone(), 0, TEST_DAG_WINDOW); - for round_nodes in &nodes { - for node in round_nodes.iter().flatten() { - dag.add_node_for_test(node.clone()).unwrap(); - } - } - let display = |node: &NodeMetadata| (node.round(), *author_indexes.get(node.author()).unwrap()); - let dag = Arc::new(DagStore::new_for_test( - dag.clone(), - Arc::new(MockStorage::new()), - Arc::new(MockPayloadManager {}), - )); - let (mut order_rule, mut receiver): (OrderRule, UnboundedReceiver>>) = - create_order_rule(epoch_state, dag); - for node in nodes.iter().flatten().flatten() { - order_rule.process_new_node(node.metadata()); - } - let expected_order = vec![ - // anchor (1, 0) has 1 votes, anchor (3, 1) has 2 votes and a path to (1, 0) - vec![(1, 0)], - // anchor (2, 1) has 3 votes - vec![(1, 2), (1, 1), (2, 1)], - // anchor (3, 1) has 2 votes - vec![(1, 3), (2, 2), (2, 0), (3, 1)], - // anchor (4, 2) has 3 votes - vec![(3, 3), (3, 2), (3, 0), (4, 2)], - // anchor (5, 2) has 3 votes - vec![(4, 1), (4, 0), (5, 2)], - ]; - let mut batch = 0; - while let Ok(Some(ordered_nodes)) = receiver.try_next() { - assert_eq!( - ordered_nodes - .iter() - .map(|node| display(node.metadata())) - .collect::>(), - expected_order[batch] - ); - batch += 1; - } -} diff --git a/consensus/src/dag/tests/rb_handler_tests.rs b/consensus/src/dag/tests/rb_handler_tests.rs deleted file mode 100644 index 87729b7078316..0000000000000 --- a/consensus/src/dag/tests/rb_handler_tests.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::dag::{ - dag_fetcher::TFetchRequester, - dag_store::DagStore, - errors::NodeBroadcastHandleError, - health::{HealthBackoff, NoChainHealth, NoPipelineBackpressure}, - rb_handler::NodeBroadcastHandler, - storage::DAGStorage, - tests::{ - dag_test::MockStorage, - helpers::{new_node, MockOrderRule, MockPayloadManager, TEST_DAG_WINDOW}, - }, - types::NodeCertificate, - NodeId, RpcHandler, Vote, -}; -use aptos_config::config::DagPayloadConfig; -use aptos_types::{ - aggregate_signature::PartialSignatures, - epoch_state::EpochState, - on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorTxnConfig}, - validator_verifier::random_validator_verifier, -}; -use claims::{assert_ok, assert_ok_eq}; -use futures::executor::block_on; -use std::{collections::BTreeMap, sync::Arc}; - -struct MockFetchRequester {} - -impl TFetchRequester for MockFetchRequester { - fn request_for_node(&self, _node: crate::dag::Node) -> anyhow::Result<()> { - Ok(()) - } - - fn request_for_certified_node(&self, _node: crate::dag::CertifiedNode) -> anyhow::Result<()> { - Ok(()) - } -} - -#[tokio::test] -async fn test_node_broadcast_receiver_succeed() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier.clone(), - }); - let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); - - // Scenario: Start DAG from beginning - let storage = Arc::new(MockStorage::new()); - let dag = Arc::new(DagStore::new( - epoch_state.clone(), - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - )); - let order_rule = Arc::new(MockOrderRule {}); - - let health_backoff = HealthBackoff::new( - epoch_state.clone(), - NoChainHealth::new(), - NoPipelineBackpressure::new(), - ); - - let wellformed_node = new_node(1, 10, signers[0].author(), vec![]); - let equivocating_node = new_node(1, 20, signers[0].author(), vec![]); - - assert_ne!(wellformed_node.digest(), equivocating_node.digest()); - - let rb_receiver = NodeBroadcastHandler::new( - dag, - order_rule, - signers[3].clone(), - epoch_state.clone(), - storage.clone(), - Arc::new(MockFetchRequester {}), - DagPayloadConfig::default(), - ValidatorTxnConfig::default_disabled(), - OnChainRandomnessConfig::default_disabled(), - OnChainJWKConsensusConfig::default_disabled(), - health_backoff, - ); - - let expected_result = Vote::new( - wellformed_node.metadata().clone(), - wellformed_node.sign_vote(&signers[3]).unwrap(), - ); - // expect an ack for a valid message - assert_ok_eq!(rb_receiver.process(wellformed_node).await, expected_result); - // expect the original ack for any future message from same author - assert_ok_eq!( - rb_receiver.process(equivocating_node).await, - expected_result - ); -} - -// TODO: Unit test node broad receiver with a pruned DAG store. Possibly need a validator verifier trait. - -#[tokio::test] -async fn test_node_broadcast_receiver_failure() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier.clone(), - }); - let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); - - let mut rb_receivers: Vec<_> = signers - .iter() - .map(|signer| { - let storage = Arc::new(MockStorage::new()); - let dag = Arc::new(DagStore::new( - epoch_state.clone(), - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - )); - let order_rule = Arc::new(MockOrderRule {}); - - NodeBroadcastHandler::new( - dag, - order_rule, - signer.clone(), - epoch_state.clone(), - storage, - Arc::new(MockFetchRequester {}), - DagPayloadConfig::default(), - ValidatorTxnConfig::default_disabled(), - OnChainRandomnessConfig::default_disabled(), - OnChainJWKConsensusConfig::default_disabled(), - HealthBackoff::new( - epoch_state.clone(), - NoChainHealth::new(), - NoPipelineBackpressure::new(), - ), - ) - }) - .collect(); - - // Round 1 - let node = new_node(1, 10, signers[0].author(), vec![]); - let vote = rb_receivers[1].process(node.clone()).await.unwrap(); - - // Round 2 with invalid parent - let partial_sigs = PartialSignatures::new(BTreeMap::from([( - signers[1].author(), - vote.signature().clone(), - )])); - let node_cert = NodeCertificate::new( - node.metadata().clone(), - validator_verifier - .aggregate_signatures(&partial_sigs) - .unwrap(), - ); - let node = new_node(2, 20, signers[0].author(), vec![node_cert]); - assert_eq!( - rb_receivers[1].process(node).await.unwrap_err().to_string(), - NodeBroadcastHandleError::InvalidParent.to_string(), - ); - - // Round 1 - add all nodes - let node_certificates: Vec<_> = signers - .iter() - .map(|signer| { - let node = new_node(1, 10, signer.author(), vec![]); - let mut partial_sigs = PartialSignatures::empty(); - rb_receivers - .iter_mut() - .zip(&signers) - .for_each(|(rb_receiver, signer)| { - let sig = block_on(rb_receiver.process(node.clone())).unwrap(); - partial_sigs.add_signature(signer.author(), sig.signature().clone()) - }); - NodeCertificate::new( - node.metadata().clone(), - validator_verifier - .aggregate_signatures(&partial_sigs) - .unwrap(), - ) - }) - .collect(); - - // Add Round 2 node with proper certificates - let node = new_node(2, 20, signers[0].author(), node_certificates); - assert_eq!( - rb_receivers[0].process(node).await.unwrap_err().to_string(), - NodeBroadcastHandleError::MissingParents.to_string() - ); -} - -#[tokio::test] -async fn test_node_broadcast_receiver_storage() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let signers: Vec<_> = signers.into_iter().map(Arc::new).collect(); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: validator_verifier, - }); - - let storage = Arc::new(MockStorage::new()); - let dag = Arc::new(DagStore::new( - epoch_state.clone(), - storage.clone(), - Arc::new(MockPayloadManager {}), - 0, - TEST_DAG_WINDOW, - )); - let order_rule = Arc::new(MockOrderRule {}); - - let node = new_node(1, 10, signers[0].author(), vec![]); - - let rb_receiver = NodeBroadcastHandler::new( - dag.clone(), - order_rule.clone(), - signers[3].clone(), - epoch_state.clone(), - storage.clone(), - Arc::new(MockFetchRequester {}), - DagPayloadConfig::default(), - ValidatorTxnConfig::default_disabled(), - OnChainRandomnessConfig::default_disabled(), - OnChainJWKConsensusConfig::default_disabled(), - HealthBackoff::new( - epoch_state.clone(), - NoChainHealth::new(), - NoPipelineBackpressure::new(), - ), - ); - let sig = rb_receiver.process(node).await.expect("must succeed"); - - assert_ok_eq!(storage.get_votes(), vec![( - NodeId::new(1, 1, signers[0].author()), - sig - )],); - - let rb_receiver = NodeBroadcastHandler::new( - dag, - order_rule.clone(), - signers[3].clone(), - epoch_state.clone(), - storage.clone(), - Arc::new(MockFetchRequester {}), - DagPayloadConfig::default(), - ValidatorTxnConfig::default_disabled(), - OnChainRandomnessConfig::default_disabled(), - OnChainJWKConsensusConfig::default_disabled(), - HealthBackoff::new( - epoch_state, - NoChainHealth::new(), - NoPipelineBackpressure::new(), - ), - ); - assert_ok!(rb_receiver.gc_before_round(2)); - assert_eq!(storage.get_votes().unwrap().len(), 0); -} diff --git a/consensus/src/dag/tests/types_test.rs b/consensus/src/dag/tests/types_test.rs deleted file mode 100644 index 64b854cbf92aa..0000000000000 --- a/consensus/src/dag/tests/types_test.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright © Aptos Foundation - -use super::helpers::new_node; -use crate::dag::{ - tests::helpers::new_certified_node, - types::{ - CertifiedNode, DAGNetworkMessage, DagSnapshotBitmask, Extensions, Node, NodeCertificate, - NodeMetadata, RemoteFetchRequest, - }, -}; -use aptos_consensus_types::common::Payload; -use aptos_crypto::HashValue; -use aptos_types::{ - aggregate_signature::AggregateSignature, validator_verifier::random_validator_verifier, -}; -use claims::assert_ok; -use std::vec; - -#[test] -fn test_node_verify() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - let sender = signers[0].author(); - - let invalid_node = Node::new_for_test( - NodeMetadata::new_for_test(0, 0, signers[0].author(), 0, HashValue::random()), - Payload::empty(false, true), - vec![], - Extensions::empty(), - ); - assert_eq!( - invalid_node - .verify(sender, &validator_verifier) - .unwrap_err() - .to_string(), - "invalid digest" - ); - - // Well-formed round 1 node - let first_round_node = new_node(1, 10, signers[0].author(), vec![]); - assert_ok!(first_round_node.verify(sender, &validator_verifier)); - // Mismatch sender - first_round_node - .verify(signers[1].author(), &validator_verifier) - .unwrap_err(); - - // Round 2 node without parents - let node = new_node(2, 20, signers[0].author(), vec![]); - assert_eq!( - node.verify(sender, &validator_verifier) - .unwrap_err() - .to_string(), - "not enough parents to satisfy voting power", - ); - - // Round 1 cert - let parent_cert = NodeCertificate::new( - first_round_node.metadata().clone(), - AggregateSignature::empty(), - ); - let node = new_node(3, 20, signers[0].author(), vec![parent_cert]); - assert_eq!( - node.verify(sender, &validator_verifier) - .unwrap_err() - .to_string(), - "invalid parent round" - ); -} - -#[test] -fn test_certified_node_verify() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - - let invalid_node = Node::new_for_test( - NodeMetadata::new_for_test(0, 0, signers[0].author(), 0, HashValue::random()), - Payload::empty(false, true), - vec![], - Extensions::empty(), - ); - let invalid_certified_node = CertifiedNode::new(invalid_node, AggregateSignature::empty()); - assert_eq!( - invalid_certified_node - .verify(&validator_verifier) - .unwrap_err() - .to_string(), - "invalid digest" - ); - - let certified_node = new_certified_node(0, signers[0].author(), vec![]); - - assert_eq!( - certified_node - .verify(&validator_verifier) - .unwrap_err() - .to_string(), - "Invalid bitvec from the multi-signature" - ); -} - -#[test] -fn test_remote_fetch_request() { - let (signers, validator_verifier) = random_validator_verifier(4, None, false); - - let parents: Vec<_> = (0..3) - .map(|idx| { - NodeMetadata::new_for_test(1, 3, signers[idx].author(), 100, HashValue::random()) - }) - .collect(); - - let request = RemoteFetchRequest::new( - 1, - parents.clone(), - DagSnapshotBitmask::new(1, vec![vec![false; 5]]), - ); - assert_eq!( - request.verify(&validator_verifier).unwrap_err().to_string(), - "invalid bitmask: each round length is not equal to validator count" - ); - - let request = RemoteFetchRequest::new( - 1, - vec![parents[0].clone()], - DagSnapshotBitmask::new(1, vec![vec![false; signers.len()]]), - ); - assert!(request - .verify(&validator_verifier) - .unwrap_err() - .to_string() - .contains("Bitmask length doesn't match")); - - let request = RemoteFetchRequest::new( - 1, - vec![parents[0].clone()], - DagSnapshotBitmask::new(1, vec![vec![false; signers.len()]; 3]), - ); - assert_ok!(request.verify(&validator_verifier)); - - let request = RemoteFetchRequest::new( - 1, - parents, - DagSnapshotBitmask::new(1, vec![vec![false; signers.len()]; 3]), - ); - assert_ok!(request.verify(&validator_verifier)); -} - -#[test] -fn test_dag_snapshot_bitmask() { - let bitmask = DagSnapshotBitmask::new(1, vec![vec![false, false, false, true]]); - - assert!(!bitmask.has(1, 0)); - assert!(bitmask.has(1, 3)); - assert!(!bitmask.has(2, 0)); - assert_eq!(bitmask.first_round(), 1); - - let bitmask = DagSnapshotBitmask::new(1, vec![vec![false, true, true, true], vec![ - false, true, false, false, - ]]); - - assert!(!bitmask.has(1, 0)); - assert!(bitmask.has(1, 3)); - assert!(!bitmask.has(2, 0)); - assert!(bitmask.has(2, 1)); - assert!(!bitmask.has(10, 10)); - assert_eq!(bitmask.first_round(), 1); -} - -#[test] -fn test_dag_network_message() { - let short_data = vec![10; 10]; - let long_data = vec![20; 30]; - - let short_message = DAGNetworkMessage::new(1, short_data); - - assert_eq!( - format!("{:?}", short_message), - "DAGNetworkMessage { epoch: 1, data: \"0a0a0a0a0a0a0a0a0a0a\" }" - ); - - let long_message = DAGNetworkMessage::new(2, long_data); - - assert_eq!( - format!("{:?}", long_message), - "DAGNetworkMessage { epoch: 2, data: \"1414141414141414141414141414141414141414\" }" - ); -} diff --git a/consensus/src/dag/types.rs b/consensus/src/dag/types.rs deleted file mode 100644 index 154af7d4f56c5..0000000000000 --- a/consensus/src/dag/types.rs +++ /dev/null @@ -1,1003 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::errors::DAGRpcError; -use crate::{ - dag::observability::{ - logging::{LogEvent, LogSchema}, - tracing::{observe_node, NodeStage}, - }, - network::TConsensusMsg, - network_interface::ConsensusMsg, -}; -use anyhow::{bail, ensure}; -use aptos_bitvec::BitVec; -use aptos_consensus_types::common::{Author, Payload, Round}; -use aptos_crypto::{ - bls12381::Signature, - hash::{CryptoHash, CryptoHasher}, - CryptoMaterialError, HashValue, -}; -use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; -use aptos_enum_conversion_derive::EnumConversion; -use aptos_infallible::Mutex; -use aptos_logger::debug; -use aptos_reliable_broadcast::{BroadcastStatus, RBMessage}; -use aptos_types::{ - aggregate_signature::{AggregateSignature, PartialSignatures}, - epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, - validator_signer::ValidatorSigner, - validator_txn::ValidatorTransaction, - validator_verifier::ValidatorVerifier, -}; -use futures_channel::oneshot; -use serde::{Deserialize, Serialize}; -use std::{ - cmp::min, - collections::HashSet, - fmt::{Display, Formatter}, - ops::{Deref, DerefMut}, - sync::Arc, -}; - -#[derive(Clone, Serialize, Deserialize, CryptoHasher, Debug, PartialEq)] -pub enum Extensions { - Empty, - // Reserved for future extensions such as randomness shares -} - -impl Extensions { - pub fn empty() -> Self { - Self::Empty - } -} - -#[derive(Serialize)] -struct NodeWithoutDigest<'a> { - epoch: u64, - round: Round, - author: Author, - timestamp: u64, - validator_txns: &'a Vec, - payload: &'a Payload, - parents: &'a Vec, - extensions: &'a Extensions, -} - -impl<'a> CryptoHash for NodeWithoutDigest<'a> { - type Hasher = NodeHasher; - - fn hash(&self) -> HashValue { - let mut state = Self::Hasher::new(); - let bytes = bcs::to_bytes(&self).expect("Unable to serialize node"); - state.update(&bytes); - state.finish() - } -} - -impl<'a> From<&'a Node> for NodeWithoutDigest<'a> { - fn from(node: &'a Node) -> Self { - Self { - epoch: node.metadata.epoch, - round: node.metadata.round, - author: node.metadata.author, - timestamp: node.metadata.timestamp, - validator_txns: &node.validator_txns, - payload: &node.payload, - parents: &node.parents, - extensions: &node.extensions, - } - } -} - -/// Represents the metadata about the node, without payload and parents from Node -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, CryptoHasher, BCSCryptoHash)] -pub struct NodeMetadata { - node_id: NodeId, - timestamp: u64, - digest: HashValue, -} - -impl NodeMetadata { - #[cfg(test)] - pub fn new_for_test( - epoch: u64, - round: Round, - author: Author, - timestamp: u64, - digest: HashValue, - ) -> Self { - Self { - node_id: NodeId { - epoch, - round, - author, - }, - timestamp, - digest, - } - } - - pub fn digest(&self) -> &HashValue { - &self.digest - } - - pub fn round(&self) -> Round { - self.round - } - - pub fn author(&self) -> &Author { - &self.author - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn timestamp(&self) -> u64 { - self.timestamp - } -} - -impl Deref for NodeMetadata { - type Target = NodeId; - - fn deref(&self) -> &Self::Target { - &self.node_id - } -} - -/// Node representation in the DAG, parents contain 2f+1 strong links (links to previous round) -#[derive(Clone, Serialize, Deserialize, CryptoHasher, Debug, PartialEq)] -pub struct Node { - metadata: NodeMetadata, - validator_txns: Vec, - payload: Payload, - parents: Vec, - extensions: Extensions, -} - -impl Node { - pub fn new( - epoch: u64, - round: Round, - author: Author, - timestamp: u64, - validator_txns: Vec, - payload: Payload, - parents: Vec, - extensions: Extensions, - ) -> Self { - let digest = Self::calculate_digest_internal( - epoch, - round, - author, - timestamp, - &validator_txns, - &payload, - &parents, - &extensions, - ); - - Self { - metadata: NodeMetadata { - node_id: NodeId { - epoch, - round, - author, - }, - timestamp, - digest, - }, - validator_txns, - payload, - parents, - extensions, - } - } - - #[cfg(test)] - pub fn new_for_test( - metadata: NodeMetadata, - payload: Payload, - parents: Vec, - extensions: Extensions, - ) -> Self { - Self { - metadata, - validator_txns: vec![], - payload, - parents, - extensions, - } - } - - /// Calculate the node digest based on all fields in the node - fn calculate_digest_internal( - epoch: u64, - round: Round, - author: Author, - timestamp: u64, - validator_txns: &Vec, - payload: &Payload, - parents: &Vec, - extensions: &Extensions, - ) -> HashValue { - let node_with_out_digest = NodeWithoutDigest { - epoch, - round, - author, - timestamp, - validator_txns, - payload, - parents, - extensions, - }; - node_with_out_digest.hash() - } - - fn calculate_digest(&self) -> HashValue { - Self::calculate_digest_internal( - self.metadata.epoch, - self.metadata.round, - self.metadata.author, - self.metadata.timestamp, - &self.validator_txns, - &self.payload, - &self.parents, - &self.extensions, - ) - } - - pub fn digest(&self) -> HashValue { - self.metadata.digest - } - - pub fn metadata(&self) -> &NodeMetadata { - &self.metadata - } - - pub fn parents(&self) -> &[NodeCertificate] { - &self.parents - } - - pub fn timestamp(&self) -> u64 { - self.metadata.timestamp - } - - pub fn parents_metadata(&self) -> impl Iterator { - self.parents().iter().map(|cert| &cert.metadata) - } - - pub fn author(&self) -> &Author { - self.metadata.author() - } - - pub fn epoch(&self) -> u64 { - self.metadata.epoch - } - - pub fn id(&self) -> NodeId { - NodeId::new(self.epoch(), self.round(), *self.author()) - } - - pub fn sign_vote(&self, signer: &ValidatorSigner) -> Result { - signer.sign(self.metadata()) - } - - pub fn round(&self) -> Round { - self.metadata.round - } - - pub fn payload(&self) -> &Payload { - &self.payload - } - - pub fn validator_txns(&self) -> &Vec { - &self.validator_txns - } - - pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - ensure!( - sender == *self.author(), - "Author {} doesn't match sender {}", - self.author(), - sender - ); - // TODO: move this check to rpc process logic to delay it as much as possible for performance - ensure!(self.digest() == self.calculate_digest(), "invalid digest"); - - let node_round = self.metadata().round(); - - ensure!(node_round > 0, "current round cannot be zero"); - - if node_round == 1 { - ensure!(self.parents().is_empty(), "invalid parents for round 1"); - return Ok(()); - } - - let prev_round = node_round - 1; - // check if the parents' round is the node's round - 1 - ensure!( - self.parents() - .iter() - .all(|parent| parent.metadata().round() == prev_round), - "invalid parent round" - ); - - // Verification of the certificate is delayed until we need to fetch it - ensure!( - verifier - .check_voting_power( - self.parents() - .iter() - .map(|parent| parent.metadata().author()), - true, - ) - .is_ok(), - "not enough parents to satisfy voting power" - ); - - // TODO: validate timestamp - - Ok(()) - } -} - -#[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Hash, Clone, PartialOrd, Ord)] -pub struct NodeId { - epoch: u64, - round: Round, - author: Author, -} - -impl NodeId { - pub fn new(epoch: u64, round: Round, author: Author) -> Self { - Self { - epoch, - round, - author, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn round(&self) -> Round { - self.round - } - - pub fn author(&self) -> &Author { - &self.author - } -} - -impl Display for NodeId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "NodeId: [epoch: {}, round: {}, author: {}]", - self.epoch, self.round, self.author - ) - } -} - -/// Quorum signatures over the node digest -#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] -pub struct NodeCertificate { - metadata: NodeMetadata, - signatures: AggregateSignature, -} - -impl NodeCertificate { - pub fn new(metadata: NodeMetadata, signatures: AggregateSignature) -> Self { - Self { - metadata, - signatures, - } - } - - pub fn metadata(&self) -> &NodeMetadata { - &self.metadata - } - - pub fn signers(&self, validators: &[Author]) -> Vec { - self.signatures.get_signers_addresses(validators) - } - - pub fn signatures(&self) -> &AggregateSignature { - &self.signatures - } - - pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - Ok(verifier.verify_multi_signatures(self.metadata(), self.signatures())?) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct CertifiedNode { - node: Node, - signatures: AggregateSignature, -} - -impl CertifiedNode { - pub fn new(node: Node, signatures: AggregateSignature) -> Self { - Self { node, signatures } - } - - pub fn signatures(&self) -> &AggregateSignature { - &self.signatures - } - - pub fn certificate(&self) -> NodeCertificate { - NodeCertificate::new(self.node.metadata.clone(), self.signatures.clone()) - } - - pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - ensure!(self.digest() == self.calculate_digest(), "invalid digest"); - - Ok(verifier.verify_multi_signatures(self.metadata(), self.signatures())?) - } -} - -impl Deref for CertifiedNode { - type Target = Node; - - fn deref(&self) -> &Self::Target { - &self.node - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct CertifiedNodeMessage { - certified_node: CertifiedNode, - ledger_info: LedgerInfoWithSignatures, -} - -impl CertifiedNodeMessage { - pub fn new(certified_node: CertifiedNode, ledger_info: LedgerInfoWithSignatures) -> Self { - Self { - certified_node, - ledger_info, - } - } - - pub fn certified_node(self) -> CertifiedNode { - self.certified_node - } - - pub fn ledger_info(&self) -> &LedgerInfoWithSignatures { - &self.ledger_info - } - - pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - ensure!( - *self.certified_node.author() == sender, - "Author {} doesn't match sender {}", - self.certified_node.author(), - sender - ); - ensure!( - self.certified_node.epoch() == self.ledger_info.commit_info().epoch(), - "Epoch {} from node doesn't match epoch {} from ledger info", - self.certified_node.epoch(), - self.ledger_info().commit_info().epoch() - ); - self.certified_node.verify(verifier) - } -} - -impl Deref for CertifiedNodeMessage { - type Target = CertifiedNode; - - fn deref(&self) -> &Self::Target { - &self.certified_node - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct Vote { - metadata: NodeMetadata, - signature: Signature, -} - -impl Vote { - pub(crate) fn new(metadata: NodeMetadata, signature: Signature) -> Self { - Self { - metadata, - signature, - } - } - - pub fn signature(&self) -> &Signature { - &self.signature - } - - pub fn verify(&self, author: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - Ok(verifier.verify(author, &self.metadata, self.signature())?) - } -} - -impl From for DAGRpcResult { - fn from(vote: Vote) -> Self { - DAGRpcResult(Ok(DAGMessage::VoteMsg(vote))) - } -} - -impl TryFrom for Vote { - type Error = anyhow::Error; - - fn try_from(result: DAGRpcResult) -> Result { - result.0?.try_into() - } -} - -pub struct SignatureBuilder { - metadata: NodeMetadata, - inner: Mutex<(PartialSignatures, Option>)>, - epoch_state: Arc, -} - -impl SignatureBuilder { - pub fn new( - metadata: NodeMetadata, - epoch_state: Arc, - tx: oneshot::Sender, - ) -> Arc { - Arc::new(Self { - metadata, - inner: Mutex::new((PartialSignatures::empty(), Some(tx))), - epoch_state, - }) - } -} - -impl BroadcastStatus for Arc { - type Aggregated = (); - type Message = Node; - type Response = Vote; - - /// Processes the [Vote]s received for a given [Node]. Once a supermajority voting power - /// is reached, this method sends [NodeCertificate] into a channel. It will only return - /// successfully when [Vote]s are received from all the peers. - fn add(&self, peer: Author, ack: Self::Response) -> anyhow::Result> { - ensure!(self.metadata == ack.metadata, "Digest mismatch"); - ack.verify(peer, &self.epoch_state.verifier)?; - debug!(LogSchema::new(LogEvent::ReceiveVote) - .remote_peer(peer) - .round(self.metadata.round())); - let mut guard = self.inner.lock(); - let (partial_signatures, tx) = guard.deref_mut(); - partial_signatures.add_signature(peer, ack.signature); - - if tx.is_some() - && self - .epoch_state - .verifier - .check_voting_power(partial_signatures.signatures().keys(), true) - .is_ok() - { - let aggregated_signature = self - .epoch_state - .verifier - .aggregate_signatures(partial_signatures) - .expect("Signature aggregation should succeed"); - observe_node(self.metadata.timestamp(), NodeStage::CertAggregated); - let certificate = NodeCertificate::new(self.metadata.clone(), aggregated_signature); - - _ = tx.take().expect("must exist").send(certificate); - } - - if partial_signatures.signatures().len() == self.epoch_state.verifier.len() { - Ok(Some(())) - } else { - Ok(None) - } - } -} - -pub struct CertificateAckState { - num_validators: usize, - received: Mutex>, -} - -impl CertificateAckState { - pub fn new(num_validators: usize) -> Arc { - Arc::new(Self { - num_validators, - received: Mutex::new(HashSet::new()), - }) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct CertifiedAck { - epoch: u64, -} - -impl CertifiedAck { - pub fn new(epoch: u64) -> Self { - Self { epoch } - } -} - -impl From for DAGRpcResult { - fn from(ack: CertifiedAck) -> Self { - DAGRpcResult(Ok(DAGMessage::CertifiedAckMsg(ack))) - } -} - -impl TryFrom for CertifiedAck { - type Error = anyhow::Error; - - fn try_from(result: DAGRpcResult) -> Result { - result.0?.try_into() - } -} - -impl BroadcastStatus for Arc { - type Aggregated = (); - type Message = CertifiedNodeMessage; - type Response = CertifiedAck; - - fn add(&self, peer: Author, _ack: Self::Response) -> anyhow::Result> { - debug!(LogSchema::new(LogEvent::ReceiveAck).remote_peer(peer)); - let mut received = self.received.lock(); - received.insert(peer); - if received.len() == self.num_validators { - Ok(Some(())) - } else { - Ok(None) - } - } -} - -/// Represents a request to fetch missing dependencies for `target`, `start_round` represents -/// the first round we care about in the DAG, `exists_bitmask` is a two dimensional bitmask represents -/// if a node exist at [start_round + index][validator_index]. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct RemoteFetchRequest { - epoch: u64, - targets: Vec, - exists_bitmask: DagSnapshotBitmask, -} - -impl RemoteFetchRequest { - pub fn new(epoch: u64, targets: Vec, exists_bitmask: DagSnapshotBitmask) -> Self { - Self { - epoch, - targets, - exists_bitmask, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn targets(&self) -> impl Iterator + Clone { - self.targets.iter() - } - - pub fn exists_bitmask(&self) -> &DagSnapshotBitmask { - &self.exists_bitmask - } - - pub fn start_round(&self) -> Round { - self.exists_bitmask.first_round() - } - - pub fn target_round(&self) -> Round { - self.targets[0].round - } - - pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - ensure!( - self.exists_bitmask - .bitmask - .iter() - .all(|round| round.len() == verifier.len()), - "invalid bitmask: each round length is not equal to validator count" - ); - ensure!(!self.targets.is_empty(), "Targets is empty"); - let target_round = self.targets[0].round(); - ensure!( - self.targets().all(|node| node.round() == target_round), - "Target round is not consistent" - ); - ensure!( - self.exists_bitmask.first_round() + self.exists_bitmask.bitmask.len() as u64 - 1 - == target_round, - "Bitmask length doesn't match, first_round {}, length {}, target {}", - self.exists_bitmask.first_round(), - self.exists_bitmask.bitmask.len(), - target_round - ); - - Ok(()) - } -} - -/// Represents a response to FetchRequest, `certified_nodes` are indexed by [round][validator_index] -/// It should fill in gaps from the `exists_bitmask` according to the parents from the `target_digest` node. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct FetchResponse { - epoch: u64, - certified_nodes: Vec, -} - -impl FetchResponse { - pub fn new(epoch: u64, certified_nodes: Vec) -> Self { - Self { - epoch, - certified_nodes, - } - } - - pub fn certified_nodes(self) -> Vec { - self.certified_nodes - } - - pub fn verify( - self, - request: &RemoteFetchRequest, - validator_verifier: &ValidatorVerifier, - ) -> anyhow::Result { - ensure!( - self.certified_nodes.iter().all(|node| { - let round = node.round(); - let author = node.author(); - if let Some(author_idx) = - validator_verifier.address_to_validator_index().get(author) - { - !request.exists_bitmask.has(round, *author_idx) - } else { - false - } - }), - "nodes don't match requested bitmask" - ); - ensure!( - self.certified_nodes - .iter() - .all(|node| node.verify(validator_verifier).is_ok()), - "unable to verify certified nodes" - ); - - Ok(self) - } -} - -#[derive(Serialize, Deserialize, Clone)] -pub struct DAGNetworkMessage { - epoch: u64, - #[serde(with = "serde_bytes")] - data: Vec, -} - -impl DAGNetworkMessage { - pub fn new(epoch: u64, data: Vec) -> Self { - Self { epoch, data } - } - - pub fn data(&self) -> &[u8] { - &self.data - } - - pub fn epoch(&self) -> u64 { - self.epoch - } -} - -impl core::fmt::Debug for DAGNetworkMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DAGNetworkMessage") - .field("epoch", &self.epoch) - .field("data", &hex::encode(&self.data[..min(20, self.data.len())])) - .finish() - } -} - -#[derive(Clone, Serialize, Deserialize, Debug, EnumConversion)] -pub enum DAGMessage { - NodeMsg(Node), - VoteMsg(Vote), - CertifiedNodeMsg(CertifiedNodeMessage), - CertifiedAckMsg(CertifiedAck), - FetchRequest(RemoteFetchRequest), - FetchResponse(FetchResponse), - - #[cfg(test)] - TestMessage(TestMessage), - #[cfg(test)] - TestAck(TestAck), -} - -impl DAGMessage { - pub fn name(&self) -> &str { - match self { - DAGMessage::NodeMsg(_) => "NodeMsg", - DAGMessage::VoteMsg(_) => "VoteMsg", - DAGMessage::CertifiedNodeMsg(_) => "CertifiedNodeMsg", - DAGMessage::CertifiedAckMsg(_) => "CertifiedAckMsg", - DAGMessage::FetchRequest(_) => "FetchRequest", - DAGMessage::FetchResponse(_) => "FetchResponse", - #[cfg(test)] - DAGMessage::TestMessage(_) => "TestMessage", - #[cfg(test)] - DAGMessage::TestAck(_) => "TestAck", - } - } - - pub fn author(&self) -> anyhow::Result { - match self { - DAGMessage::NodeMsg(node) => Ok(node.metadata.author), - DAGMessage::CertifiedNodeMsg(node) => Ok(node.metadata.author), - _ => bail!("message does not support author field"), - } - } - - pub fn verify(&self, sender: Author, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - match self { - DAGMessage::NodeMsg(node) => node.verify(sender, verifier), - DAGMessage::CertifiedNodeMsg(certified_node) => certified_node.verify(sender, verifier), - DAGMessage::FetchRequest(fetch_request) => fetch_request.verify(verifier), - DAGMessage::VoteMsg(_) - | DAGMessage::CertifiedAckMsg(_) - | DAGMessage::FetchResponse(_) => { - bail!("Unexpected to verify {} in rpc handler", self.name()) - }, - #[cfg(test)] - DAGMessage::TestMessage(_) | DAGMessage::TestAck(_) => { - bail!("Unexpected to verify {}", self.name()) - }, - } - } -} - -impl RBMessage for DAGMessage {} - -impl TConsensusMsg for DAGMessage { - fn epoch(&self) -> u64 { - match self { - DAGMessage::NodeMsg(node) => node.metadata.epoch, - DAGMessage::VoteMsg(vote) => vote.metadata.epoch, - DAGMessage::CertifiedNodeMsg(node) => node.metadata.epoch, - DAGMessage::CertifiedAckMsg(ack) => ack.epoch, - DAGMessage::FetchRequest(req) => req.epoch, - DAGMessage::FetchResponse(res) => res.epoch, - #[cfg(test)] - DAGMessage::TestMessage(_) => 1, - #[cfg(test)] - DAGMessage::TestAck(_) => 1, - } - } - - fn from_network_message(msg: ConsensusMsg) -> anyhow::Result { - match msg { - ConsensusMsg::DAGMessage(msg) => Ok(bcs::from_bytes(&msg.data)?), - _ => bail!("unexpected consensus message type {:?}", msg), - } - } - - fn into_network_message(self) -> ConsensusMsg { - ConsensusMsg::DAGMessage(DAGNetworkMessage { - epoch: self.epoch(), - data: bcs::to_bytes(&self).unwrap(), - }) - } -} - -impl TryFrom for DAGMessage { - type Error = anyhow::Error; - - fn try_from(msg: DAGNetworkMessage) -> Result { - Ok(bcs::from_bytes(&msg.data)?) - } -} - -impl TryFrom for DAGMessage { - type Error = anyhow::Error; - - fn try_from(msg: ConsensusMsg) -> Result { - TConsensusMsg::from_network_message(msg) - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct DAGRpcResult(pub Result); - -impl TConsensusMsg for DAGRpcResult { - fn epoch(&self) -> u64 { - match &self.0 { - Ok(dag_message) => dag_message.epoch(), - Err(error) => error.epoch(), - } - } - - fn from_network_message(msg: ConsensusMsg) -> anyhow::Result { - match msg { - ConsensusMsg::DAGMessage(msg) => Ok(bcs::from_bytes(&msg.data)?), - _ => bail!("unexpected consensus message type {:?}", msg), - } - } - - fn into_network_message(self) -> ConsensusMsg { - ConsensusMsg::DAGMessage(DAGNetworkMessage { - epoch: self.epoch(), - data: bcs::to_bytes(&self).unwrap(), - }) - } -} - -impl RBMessage for DAGRpcResult {} - -impl Deref for DAGRpcResult { - type Target = Result; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From> for DAGRpcResult { - fn from(result: Result) -> Self { - Self(result) - } -} - -#[cfg(test)] -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)] -pub struct TestMessage(pub Vec); - -#[cfg(test)] -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct TestAck(pub Vec); - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct DagSnapshotBitmask { - bitmask: Vec>, - first_round: Round, -} - -impl DagSnapshotBitmask { - pub fn new(first_round: Round, bitmask: Vec>) -> Self { - Self { - bitmask, - first_round, - } - } - - pub fn has(&self, round: Round, author_idx: usize) -> bool { - let round_idx = match round.checked_sub(self.first_round) { - Some(idx) => idx as usize, - None => return false, - }; - self.bitmask - .get(round_idx) - .and_then(|round| round.get(author_idx).cloned()) - .unwrap_or(false) - } - - pub fn num_missing(&self) -> usize { - self.bitmask - .iter() - .map(|round| round.iter().map(|exist| !*exist as usize).sum::()) - .sum::() - } - - pub fn first_round(&self) -> Round { - self.first_round - } - - pub fn len(&self) -> usize { - self.bitmask.len() - } - - pub fn bitvec(&self, round: Round) -> Option { - let round_idx = round.checked_sub(self.first_round)? as usize; - self.bitmask.get(round_idx).map(|bitvec| bitvec.into()) - } -} diff --git a/consensus/src/epoch_manager.rs b/consensus/src/epoch_manager.rs deleted file mode 100644 index f768fb5000494..0000000000000 --- a/consensus/src/epoch_manager.rs +++ /dev/null @@ -1,1657 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{ - tracing::{observe_block, BlockStage}, - BlockStore, - }, - counters, - dag::{DagBootstrapper, DagCommitSigner, StorageAdapter}, - error::{error_kind, DbError}, - liveness::{ - cached_proposer_election::CachedProposerElection, - leader_reputation::{ - extract_epoch_to_proposers, AptosDBBackend, LeaderReputation, - ProposerAndVoterHeuristic, ReputationHeuristic, - }, - proposal_generator::{ - ChainHealthBackoffConfig, PipelineBackpressureConfig, ProposalGenerator, - }, - proposer_election::ProposerElection, - rotating_proposer_election::{choose_leader, RotatingProposer}, - round_proposer_election::RoundProposer, - round_state::{ExponentialTimeInterval, RoundState}, - }, - logging::{LogEvent, LogSchema}, - metrics_safety_rules::MetricsSafetyRules, - monitor, - network::{ - IncomingBatchRetrievalRequest, IncomingBlockRetrievalRequest, IncomingDAGRequest, - IncomingRandGenRequest, IncomingRpcRequest, NetworkReceivers, NetworkSender, - }, - network_interface::{ConsensusMsg, ConsensusNetworkClient}, - payload_client::{ - mixed::MixedPayloadClient, user::quorum_store_client::QuorumStoreClient, PayloadClient, - }, - payload_manager::PayloadManager, - persistent_liveness_storage::{LedgerRecoveryData, PersistentLivenessStorage, RecoveryData}, - pipeline::execution_client::TExecutionClient, - quorum_store::{ - quorum_store_builder::{DirectMempoolInnerBuilder, InnerBuilder, QuorumStoreBuilder}, - quorum_store_coordinator::CoordinatorCommand, - quorum_store_db::QuorumStoreStorage, - }, - rand::rand_gen::{ - storage::interface::RandStorage, - types::{AugmentedData, RandConfig}, - }, - recovery_manager::RecoveryManager, - round_manager::{RoundManager, UnverifiedEvent, VerifiedEvent}, - util::time_service::TimeService, -}; -use anyhow::{anyhow, bail, ensure, Context}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::{aptos_channel, message_queues::QueueStyle}; -use aptos_config::config::{ - ConsensusConfig, DagConsensusConfig, ExecutionConfig, NodeConfig, QcAggregatorType, - SafetyRulesConfig, SecureBackend, -}; -use aptos_consensus_types::{ - common::{Author, Round}, - delayed_qc_msg::DelayedQcMsg, - epoch_retrieval::EpochRetrievalRequest, -}; -use aptos_crypto::bls12381; -use aptos_dkg::{ - pvss::{traits::Transcript, Player}, - weighted_vuf::traits::WeightedVUF, -}; -use aptos_event_notifications::ReconfigNotificationListener; -use aptos_global_constants::CONSENSUS_KEY; -use aptos_infallible::{duration_since_epoch, Mutex}; -use aptos_logger::prelude::*; -use aptos_mempool::QuorumStoreRequest; -use aptos_network::{application::interface::NetworkClient, protocols::network::Event}; -use aptos_safety_rules::SafetyRulesManager; -use aptos_secure_storage::{KVStorage, Storage}; -use aptos_types::{ - account_address::AccountAddress, - dkg::{real_dkg::maybe_dk_from_bls_sk, DKGState, DKGTrait, DefaultDKG}, - epoch_change::EpochChangeProof, - epoch_state::EpochState, - jwks::SupportedOIDCProviders, - on_chain_config::{ - Features, LeaderReputationType, OnChainConfigPayload, OnChainConfigProvider, - OnChainConsensusConfig, OnChainExecutionConfig, OnChainJWKConsensusConfig, - OnChainRandomnessConfig, ProposerElectionType, RandomnessConfigMoveStruct, ValidatorSet, - }, - randomness::{RandKeys, WvufPP, WVUF}, - validator_signer::ValidatorSigner, -}; -use aptos_validator_transaction_pool::VTxnPoolState; -use fail::fail_point; -use futures::{ - channel::{ - mpsc, - mpsc::{unbounded, Sender, UnboundedSender}, - oneshot, - }, - SinkExt, StreamExt, -}; -use itertools::Itertools; -use rand::{prelude::StdRng, thread_rng, SeedableRng}; -use std::{ - cmp::Ordering, - collections::HashMap, - hash::Hash, - mem::{discriminant, Discriminant}, - sync::Arc, - time::Duration, -}; - -/// Range of rounds (window) that we might be calling proposer election -/// functions with at any given time, in addition to the proposer history length. -const PROPOSER_ELECTION_CACHING_WINDOW_ADDITION: usize = 3; -/// Number of rounds we expect storage to be ahead of the proposer round, -/// used for fetching data from DB. -const PROPOSER_ROUND_BEHIND_STORAGE_BUFFER: usize = 10; - -#[allow(clippy::large_enum_variant)] -pub enum LivenessStorageData { - FullRecoveryData(RecoveryData), - PartialRecoveryData(LedgerRecoveryData), -} - -// Manager the components that shared across epoch and spawn per-epoch RoundManager with -// epoch-specific input. -pub struct EpochManager { - author: Author, - config: ConsensusConfig, - #[allow(unused)] - execution_config: ExecutionConfig, - time_service: Arc, - self_sender: aptos_channels::UnboundedSender>, - network_sender: ConsensusNetworkClient>, - timeout_sender: aptos_channels::Sender, - quorum_store_enabled: bool, - quorum_store_to_mempool_sender: Sender, - execution_client: Arc, - storage: Arc, - safety_rules_manager: SafetyRulesManager, - vtxn_pool: VTxnPoolState, - reconfig_events: ReconfigNotificationListener

    , - // channels to rand manager - rand_manager_msg_tx: Option>, - // channels to round manager - round_manager_tx: Option< - aptos_channel::Sender<(Author, Discriminant), (Author, VerifiedEvent)>, - >, - buffered_proposal_tx: Option>, - round_manager_close_tx: Option>>, - epoch_state: Option>, - block_retrieval_tx: - Option>, - quorum_store_msg_tx: Option>, - quorum_store_coordinator_tx: Option>, - quorum_store_storage: Arc, - batch_retrieval_tx: - Option>, - bounded_executor: BoundedExecutor, - // recovery_mode is set to true when the recovery manager is spawned - recovery_mode: bool, - - aptos_time_service: aptos_time_service::TimeService, - dag_rpc_tx: Option>, - dag_shutdown_tx: Option>>, - dag_config: DagConsensusConfig, - payload_manager: Arc, - rand_storage: Arc>, -} - -impl EpochManager

    { - pub(crate) fn new( - node_config: &NodeConfig, - time_service: Arc, - self_sender: aptos_channels::UnboundedSender>, - network_sender: ConsensusNetworkClient>, - timeout_sender: aptos_channels::Sender, - quorum_store_to_mempool_sender: Sender, - execution_client: Arc, - storage: Arc, - quorum_store_storage: Arc, - reconfig_events: ReconfigNotificationListener

    , - bounded_executor: BoundedExecutor, - aptos_time_service: aptos_time_service::TimeService, - vtxn_pool: VTxnPoolState, - rand_storage: Arc>, - ) -> Self { - let author = node_config.validator_network.as_ref().unwrap().peer_id(); - let config = node_config.consensus.clone(); - let execution_config = node_config.execution.clone(); - let dag_config = node_config.dag_consensus.clone(); - let sr_config = &node_config.consensus.safety_rules; - let safety_rules_manager = SafetyRulesManager::new(sr_config); - Self { - author, - config, - execution_config, - time_service, - self_sender, - network_sender, - timeout_sender, - // This default value is updated at epoch start - quorum_store_enabled: false, - quorum_store_to_mempool_sender, - execution_client, - storage, - safety_rules_manager, - vtxn_pool, - reconfig_events, - rand_manager_msg_tx: None, - round_manager_tx: None, - round_manager_close_tx: None, - buffered_proposal_tx: None, - epoch_state: None, - block_retrieval_tx: None, - quorum_store_msg_tx: None, - quorum_store_coordinator_tx: None, - quorum_store_storage, - batch_retrieval_tx: None, - bounded_executor, - recovery_mode: false, - dag_rpc_tx: None, - dag_shutdown_tx: None, - aptos_time_service, - dag_config, - payload_manager: Arc::new(PayloadManager::DirectMempool), - rand_storage, - } - } - - fn epoch_state(&self) -> &EpochState { - self.epoch_state - .as_ref() - .expect("EpochManager not started yet") - } - - fn epoch(&self) -> u64 { - self.epoch_state().epoch - } - - fn create_round_state( - &self, - time_service: Arc, - timeout_sender: aptos_channels::Sender, - delayed_qc_tx: UnboundedSender, - qc_aggregator_type: QcAggregatorType, - ) -> RoundState { - let time_interval = Box::new(ExponentialTimeInterval::new( - Duration::from_millis(self.config.round_initial_timeout_ms), - self.config.round_timeout_backoff_exponent_base, - self.config.round_timeout_backoff_max_exponent, - )); - RoundState::new( - time_interval, - time_service, - timeout_sender, - delayed_qc_tx, - qc_aggregator_type, - ) - } - - /// Create a proposer election handler based on proposers - fn create_proposer_election( - &self, - epoch_state: &EpochState, - onchain_config: &OnChainConsensusConfig, - ) -> Arc { - let proposers = epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect::>(); - match &onchain_config.proposer_election_type() { - ProposerElectionType::RotatingProposer(contiguous_rounds) => { - Arc::new(RotatingProposer::new(proposers, *contiguous_rounds)) - }, - // We don't really have a fixed proposer! - ProposerElectionType::FixedProposer(contiguous_rounds) => { - let proposer = choose_leader(proposers); - Arc::new(RotatingProposer::new(vec![proposer], *contiguous_rounds)) - }, - ProposerElectionType::LeaderReputation(leader_reputation_type) => { - let ( - heuristic, - window_size, - weight_by_voting_power, - use_history_from_previous_epoch_max_count, - ) = match &leader_reputation_type { - LeaderReputationType::ProposerAndVoter(proposer_and_voter_config) - | LeaderReputationType::ProposerAndVoterV2(proposer_and_voter_config) => { - let proposer_window_size = proposers.len() - * proposer_and_voter_config.proposer_window_num_validators_multiplier; - let voter_window_size = proposers.len() - * proposer_and_voter_config.voter_window_num_validators_multiplier; - let heuristic: Box = - Box::new(ProposerAndVoterHeuristic::new( - self.author, - proposer_and_voter_config.active_weight, - proposer_and_voter_config.inactive_weight, - proposer_and_voter_config.failed_weight, - proposer_and_voter_config.failure_threshold_percent, - voter_window_size, - proposer_window_size, - leader_reputation_type.use_reputation_window_from_stale_end(), - )); - ( - heuristic, - std::cmp::max(proposer_window_size, voter_window_size), - proposer_and_voter_config.weight_by_voting_power, - proposer_and_voter_config.use_history_from_previous_epoch_max_count, - ) - }, - }; - - let seek_len = onchain_config.leader_reputation_exclude_round() as usize - + onchain_config.max_failed_authors_to_store() - + PROPOSER_ROUND_BEHIND_STORAGE_BUFFER; - - let backend = Arc::new(AptosDBBackend::new( - window_size, - seek_len, - self.storage.aptos_db(), - )); - let voting_powers: Vec<_> = if weight_by_voting_power { - proposers - .iter() - .map(|p| epoch_state.verifier.get_voting_power(p).unwrap()) - .collect() - } else { - vec![1; proposers.len()] - }; - - let epoch_to_proposers = self.extract_epoch_proposers( - epoch_state, - use_history_from_previous_epoch_max_count, - proposers, - (window_size + seek_len) as u64, - ); - - info!( - "Starting epoch {}: proposers across epochs for leader election: {:?}", - epoch_state.epoch, - epoch_to_proposers - .iter() - .map(|(epoch, proposers)| (epoch, proposers.len())) - .sorted() - .collect::>() - ); - - let proposer_election = Box::new(LeaderReputation::new( - epoch_state.epoch, - epoch_to_proposers, - voting_powers, - backend, - heuristic, - onchain_config.leader_reputation_exclude_round(), - leader_reputation_type.use_root_hash_for_seed(), - self.config.window_for_chain_health, - )); - // LeaderReputation is not cheap, so we can cache the amount of rounds round_manager needs. - Arc::new(CachedProposerElection::new( - epoch_state.epoch, - proposer_election, - onchain_config.max_failed_authors_to_store() - + PROPOSER_ELECTION_CACHING_WINDOW_ADDITION, - )) - }, - ProposerElectionType::RoundProposer(round_proposers) => { - // Hardcoded to the first proposer - let default_proposer = proposers.first().unwrap(); - Arc::new(RoundProposer::new( - round_proposers.clone(), - *default_proposer, - )) - }, - } - } - - fn extract_epoch_proposers( - &self, - epoch_state: &EpochState, - use_history_from_previous_epoch_max_count: u32, - proposers: Vec, - needed_rounds: u64, - ) -> HashMap> { - // Genesis is epoch=0 - // First block (after genesis) is epoch=1, and is the only block in that epoch. - // It has no votes, so we skip it unless we are in epoch 1, as otherwise it will - // skew leader elections for exclude_round number of rounds. - let first_epoch_to_consider = std::cmp::max( - if epoch_state.epoch == 1 { 1 } else { 2 }, - epoch_state - .epoch - .saturating_sub(use_history_from_previous_epoch_max_count as u64), - ); - // If we are considering beyond the current epoch, we need to fetch validators for those epochs - if epoch_state.epoch > first_epoch_to_consider { - self.storage - .aptos_db() - .get_epoch_ending_ledger_infos(first_epoch_to_consider - 1, epoch_state.epoch) - .map_err(Into::into) - .and_then(|proof| { - ensure!( - proof.ledger_info_with_sigs.len() as u64 - == (epoch_state.epoch - (first_epoch_to_consider - 1)) - ); - extract_epoch_to_proposers(proof, epoch_state.epoch, &proposers, needed_rounds) - }) - .unwrap_or_else(|err| { - error!( - "Couldn't create leader reputation with history across epochs, {:?}", - err - ); - HashMap::from([(epoch_state.epoch, proposers)]) - }) - } else { - HashMap::from([(epoch_state.epoch, proposers)]) - } - } - - fn process_epoch_retrieval( - &mut self, - request: EpochRetrievalRequest, - peer_id: AccountAddress, - ) -> anyhow::Result<()> { - debug!( - LogSchema::new(LogEvent::ReceiveEpochRetrieval) - .remote_peer(peer_id) - .epoch(self.epoch()), - "[EpochManager] receive {}", request, - ); - let proof = self - .storage - .aptos_db() - .get_epoch_ending_ledger_infos(request.start_epoch, request.end_epoch) - .map_err(DbError::from) - .context("[EpochManager] Failed to get epoch proof")?; - let msg = ConsensusMsg::EpochChangeProof(Box::new(proof)); - if let Err(err) = self.network_sender.send_to(peer_id, msg) { - warn!( - "[EpochManager] Failed to send epoch proof to {}, with error: {:?}", - peer_id, err, - ); - } - Ok(()) - } - - fn process_different_epoch( - &mut self, - different_epoch: u64, - peer_id: AccountAddress, - ) -> anyhow::Result<()> { - debug!( - LogSchema::new(LogEvent::ReceiveMessageFromDifferentEpoch) - .remote_peer(peer_id) - .epoch(self.epoch()), - remote_epoch = different_epoch, - ); - match different_epoch.cmp(&self.epoch()) { - Ordering::Less => { - if self - .epoch_state() - .verifier - .get_voting_power(&self.author) - .is_some() - { - // Ignore message from lower epoch if we're part of the validator set, the node would eventually see messages from - // higher epoch and request a proof - sample!( - SampleRate::Duration(Duration::from_secs(1)), - debug!("Discard message from lower epoch {} from {}", different_epoch, peer_id); - ); - Ok(()) - } else { - // reply back the epoch change proof if we're not part of the validator set since we won't broadcast - // timeout in this epoch - monitor!( - "process_epoch_retrieval", - self.process_epoch_retrieval( - EpochRetrievalRequest { - start_epoch: different_epoch, - end_epoch: self.epoch(), - }, - peer_id - ) - ) - } - }, - // We request proof to join higher epoch - Ordering::Greater => { - let request = EpochRetrievalRequest { - start_epoch: self.epoch(), - end_epoch: different_epoch, - }; - let msg = ConsensusMsg::EpochRetrievalRequest(Box::new(request)); - if let Err(err) = self.network_sender.send_to(peer_id, msg) { - warn!( - "[EpochManager] Failed to send epoch retrieval to {}, {:?}", - peer_id, err - ); - counters::EPOCH_MANAGER_ISSUES_DETAILS - .with_label_values(&["failed_to_send_epoch_retrieval"]) - .inc(); - } - - Ok(()) - }, - Ordering::Equal => { - bail!("[EpochManager] Same epoch should not come to process_different_epoch"); - }, - } - } - - async fn initiate_new_epoch(&mut self, proof: EpochChangeProof) -> anyhow::Result<()> { - let ledger_info = proof - .verify(self.epoch_state()) - .context("[EpochManager] Invalid EpochChangeProof")?; - info!( - LogSchema::new(LogEvent::NewEpoch).epoch(ledger_info.ledger_info().next_block_epoch()), - "Received verified epoch change", - ); - - // shutdown existing processor first to avoid race condition with state sync. - self.shutdown_current_processor().await; - // make sure storage is on this ledger_info too, it should be no-op if it's already committed - // panic if this doesn't succeed since the current processors are already shutdown. - self.execution_client - .sync_to(ledger_info.clone()) - .await - .context(format!( - "[EpochManager] State sync to new epoch {}", - ledger_info - )) - .expect("Failed to sync to new epoch"); - - monitor!("reconfig", self.await_reconfig_notification().await); - Ok(()) - } - - fn spawn_block_retrieval_task( - &mut self, - epoch: u64, - block_store: Arc, - max_blocks_allowed: u64, - ) { - let (request_tx, mut request_rx) = aptos_channel::new::<_, IncomingBlockRetrievalRequest>( - QueueStyle::LIFO, - 1, - Some(&counters::BLOCK_RETRIEVAL_TASK_MSGS), - ); - let task = async move { - info!(epoch = epoch, "Block retrieval task starts"); - while let Some(request) = request_rx.next().await { - if request.req.num_blocks() > max_blocks_allowed { - warn!( - "Ignore block retrieval with too many blocks: {}", - request.req.num_blocks() - ); - continue; - } - if let Err(e) = monitor!( - "process_block_retrieval", - block_store.process_block_retrieval(request).await - ) { - warn!(epoch = epoch, error = ?e, kind = error_kind(&e)); - } - } - info!(epoch = epoch, "Block retrieval task stops"); - }; - self.block_retrieval_tx = Some(request_tx); - tokio::spawn(task); - } - - async fn shutdown_current_processor(&mut self) { - if let Some(close_tx) = self.round_manager_close_tx.take() { - // Release the previous RoundManager, especially the SafetyRule client - let (ack_tx, ack_rx) = oneshot::channel(); - close_tx - .send(ack_tx) - .expect("[EpochManager] Fail to drop round manager"); - ack_rx - .await - .expect("[EpochManager] Fail to drop round manager"); - } - self.round_manager_tx = None; - - if let Some(close_tx) = self.dag_shutdown_tx.take() { - // Release the previous RoundManager, especially the SafetyRule client - let (ack_tx, ack_rx) = oneshot::channel(); - close_tx - .send(ack_tx) - .expect("[EpochManager] Fail to drop DAG bootstrapper"); - ack_rx - .await - .expect("[EpochManager] Fail to drop DAG bootstrapper"); - } - self.dag_shutdown_tx = None; - - // Shutdown the previous rand manager - self.rand_manager_msg_tx = None; - - // Shutdown the previous buffer manager, to release the SafetyRule client - self.execution_client.end_epoch().await; - - // Shutdown the block retrieval task by dropping the sender - self.block_retrieval_tx = None; - self.batch_retrieval_tx = None; - - if let Some(mut quorum_store_coordinator_tx) = self.quorum_store_coordinator_tx.take() { - let (ack_tx, ack_rx) = oneshot::channel(); - quorum_store_coordinator_tx - .send(CoordinatorCommand::Shutdown(ack_tx)) - .await - .expect("Could not send shutdown indicator to QuorumStore"); - ack_rx.await.expect("Failed to stop QuorumStore"); - } - } - - async fn start_recovery_manager( - &mut self, - ledger_data: LedgerRecoveryData, - onchain_consensus_config: OnChainConsensusConfig, - epoch_state: Arc, - network_sender: Arc, - ) { - let (recovery_manager_tx, recovery_manager_rx) = aptos_channel::new( - QueueStyle::LIFO, - 1, - Some(&counters::ROUND_MANAGER_CHANNEL_MSGS), - ); - self.round_manager_tx = Some(recovery_manager_tx); - let (close_tx, close_rx) = oneshot::channel(); - self.round_manager_close_tx = Some(close_tx); - let recovery_manager = RecoveryManager::new( - epoch_state, - network_sender, - self.storage.clone(), - self.execution_client.clone(), - ledger_data.committed_round(), - self.config - .max_blocks_per_sending_request(onchain_consensus_config.quorum_store_enabled()), - self.payload_manager.clone(), - ); - tokio::spawn(recovery_manager.start(recovery_manager_rx, close_rx)); - } - - async fn init_payload_provider( - &mut self, - epoch_state: &EpochState, - network_sender: NetworkSender, - consensus_config: &OnChainConsensusConfig, - ) -> (Arc, QuorumStoreClient, QuorumStoreBuilder) { - // Start QuorumStore - let (consensus_to_quorum_store_tx, consensus_to_quorum_store_rx) = - mpsc::channel(self.config.intra_consensus_channel_buffer_size); - - let quorum_store_config = if consensus_config.is_dag_enabled() { - self.dag_config.quorum_store.clone() - } else { - self.config.quorum_store.clone() - }; - - let mut quorum_store_builder = if self.quorum_store_enabled { - info!("Building QuorumStore"); - QuorumStoreBuilder::QuorumStore(InnerBuilder::new( - self.epoch(), - self.author, - epoch_state.verifier.len() as u64, - quorum_store_config, - consensus_to_quorum_store_rx, - self.quorum_store_to_mempool_sender.clone(), - self.config.mempool_txn_pull_timeout_ms, - self.storage.aptos_db().clone(), - network_sender, - epoch_state.verifier.clone(), - self.config.safety_rules.backend.clone(), - self.quorum_store_storage.clone(), - !consensus_config.is_dag_enabled(), - )) - } else { - info!("Building DirectMempool"); - QuorumStoreBuilder::DirectMempool(DirectMempoolInnerBuilder::new( - consensus_to_quorum_store_rx, - self.quorum_store_to_mempool_sender.clone(), - self.config.mempool_txn_pull_timeout_ms, - )) - }; - - let (payload_manager, quorum_store_msg_tx) = quorum_store_builder.init_payload_manager(); - self.quorum_store_msg_tx = quorum_store_msg_tx; - self.payload_manager = payload_manager.clone(); - - let payload_client = QuorumStoreClient::new( - consensus_to_quorum_store_tx, - self.config.quorum_store_pull_timeout_ms, - self.config.wait_for_full_blocks_above_recent_fill_threshold, - self.config.wait_for_full_blocks_above_pending_blocks, - ); - (payload_manager, payload_client, quorum_store_builder) - } - - fn set_epoch_start_metrics(&self, epoch_state: &EpochState) { - counters::EPOCH.set(epoch_state.epoch as i64); - counters::CURRENT_EPOCH_VALIDATORS.set(epoch_state.verifier.len() as i64); - - counters::TOTAL_VOTING_POWER.set(epoch_state.verifier.total_voting_power() as f64); - counters::VALIDATOR_VOTING_POWER.set( - epoch_state - .verifier - .get_voting_power(&self.author) - .unwrap_or(0) as f64, - ); - epoch_state - .verifier - .get_ordered_account_addresses_iter() - .for_each(|peer_id| { - counters::ALL_VALIDATORS_VOTING_POWER - .with_label_values(&[&peer_id.to_string()]) - .set(epoch_state.verifier.get_voting_power(&peer_id).unwrap_or(0) as i64) - }); - } - - async fn start_round_manager( - &mut self, - recovery_data: RecoveryData, - epoch_state: Arc, - onchain_consensus_config: OnChainConsensusConfig, - onchain_execution_config: OnChainExecutionConfig, - onchain_randomness_config: OnChainRandomnessConfig, - onchain_jwk_consensus_config: OnChainJWKConsensusConfig, - network_sender: Arc, - payload_client: Arc, - payload_manager: Arc, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ) { - let epoch = epoch_state.epoch; - info!( - epoch = epoch_state.epoch, - validators = epoch_state.verifier.to_string(), - root_block = %recovery_data.root_block(), - "Starting new epoch", - ); - - info!(epoch = epoch, "Update SafetyRules"); - - let mut safety_rules = - MetricsSafetyRules::new(self.safety_rules_manager.client(), self.storage.clone()); - if let Err(error) = safety_rules.perform_initialize() { - error!( - epoch = epoch, - error = error, - "Unable to initialize safety rules.", - ); - } - let (delayed_qc_tx, delayed_qc_rx) = unbounded(); - - info!(epoch = epoch, "Create RoundState"); - let round_state = self.create_round_state( - self.time_service.clone(), - self.timeout_sender.clone(), - delayed_qc_tx, - self.config.qc_aggregator_type.clone(), - ); - - info!(epoch = epoch, "Create ProposerElection"); - let proposer_election = - self.create_proposer_election(&epoch_state, &onchain_consensus_config); - let chain_health_backoff_config = - ChainHealthBackoffConfig::new(self.config.chain_health_backoff.clone()); - let pipeline_backpressure_config = - PipelineBackpressureConfig::new(self.config.pipeline_backpressure.clone()); - - let safety_rules_container = Arc::new(Mutex::new(safety_rules)); - - self.execution_client - .start_epoch( - epoch_state.clone(), - safety_rules_container.clone(), - payload_manager.clone(), - &onchain_consensus_config, - &onchain_execution_config, - &onchain_randomness_config, - rand_config, - rand_msg_rx, - ) - .await; - - info!(epoch = epoch, "Create BlockStore"); - // Read the last vote, before "moving" `recovery_data` - let last_vote = recovery_data.last_vote(); - let block_store = Arc::new(BlockStore::new( - Arc::clone(&self.storage), - recovery_data, - self.execution_client.clone(), - self.config.max_pruned_blocks_in_mem, - Arc::clone(&self.time_service), - self.config.vote_back_pressure_limit, - payload_manager, - )); - - info!(epoch = epoch, "Create ProposalGenerator"); - // txn manager is required both by proposal generator (to pull the proposers) - // and by event processor (to update their status). - let proposal_generator = ProposalGenerator::new( - self.author, - block_store.clone(), - payload_client, - self.time_service.clone(), - Duration::from_millis(self.config.quorum_store_poll_time_ms), - self.config.max_sending_block_txns, - self.config.max_sending_block_bytes, - self.config.max_sending_inline_txns, - self.config.max_sending_inline_bytes, - onchain_consensus_config.max_failed_authors_to_store(), - pipeline_backpressure_config, - chain_health_backoff_config, - self.quorum_store_enabled, - onchain_consensus_config.effective_validator_txn_config(), - self.config - .quorum_store - .allow_batches_without_pos_in_proposal, - ); - let (round_manager_tx, round_manager_rx) = aptos_channel::new( - QueueStyle::LIFO, - 1, - Some(&counters::ROUND_MANAGER_CHANNEL_MSGS), - ); - - let (buffered_proposal_tx, buffered_proposal_rx) = aptos_channel::new( - QueueStyle::KLAST, - 5, - Some(&counters::ROUND_MANAGER_CHANNEL_MSGS), - ); - self.round_manager_tx = Some(round_manager_tx.clone()); - self.buffered_proposal_tx = Some(buffered_proposal_tx.clone()); - let max_blocks_allowed = self - .config - .max_blocks_per_receiving_request(onchain_consensus_config.quorum_store_enabled()); - - let mut round_manager = RoundManager::new( - epoch_state, - block_store.clone(), - round_state, - proposer_election, - proposal_generator, - safety_rules_container, - network_sender, - self.storage.clone(), - onchain_consensus_config, - buffered_proposal_tx, - self.config.clone(), - onchain_randomness_config, - onchain_jwk_consensus_config, - ); - - round_manager.init(last_vote).await; - - let (close_tx, close_rx) = oneshot::channel(); - self.round_manager_close_tx = Some(close_tx); - tokio::spawn(round_manager.start( - round_manager_rx, - buffered_proposal_rx, - delayed_qc_rx, - close_rx, - )); - - self.spawn_block_retrieval_task(epoch, block_store, max_blocks_allowed); - } - - fn start_quorum_store(&mut self, quorum_store_builder: QuorumStoreBuilder) { - if let Some((quorum_store_coordinator_tx, batch_retrieval_rx)) = - quorum_store_builder.start() - { - self.quorum_store_coordinator_tx = Some(quorum_store_coordinator_tx); - self.batch_retrieval_tx = Some(batch_retrieval_rx); - } - } - - fn create_network_sender(&mut self, epoch_state: &EpochState) -> NetworkSender { - NetworkSender::new( - self.author, - self.network_sender.clone(), - self.self_sender.clone(), - epoch_state.verifier.clone(), - ) - } - - fn try_get_rand_config_for_new_epoch( - &self, - new_epoch_state: &EpochState, - onchain_randomness_config: &OnChainRandomnessConfig, - maybe_dkg_state: anyhow::Result, - consensus_config: &OnChainConsensusConfig, - ) -> Result { - if !consensus_config.is_vtxn_enabled() { - return Err(NoRandomnessReason::VTxnDisabled); - } - if !onchain_randomness_config.randomness_enabled() { - return Err(NoRandomnessReason::FeatureDisabled); - } - let new_epoch = new_epoch_state.epoch; - - let dkg_state = maybe_dkg_state.map_err(NoRandomnessReason::DKGStateResourceMissing)?; - let dkg_session = dkg_state - .last_completed - .ok_or_else(|| NoRandomnessReason::DKGCompletedSessionResourceMissing)?; - if dkg_session.metadata.dealer_epoch + 1 != new_epoch_state.epoch { - return Err(NoRandomnessReason::CompletedSessionTooOld); - } - let dkg_pub_params = DefaultDKG::new_public_params(&dkg_session.metadata); - let my_index = new_epoch_state - .verifier - .address_to_validator_index() - .get(&self.author) - .copied() - .ok_or_else(|| NoRandomnessReason::NotInValidatorSet)?; - - let dkg_decrypt_key = load_dkg_decrypt_key(&self.config.safety_rules) - .ok_or_else(|| NoRandomnessReason::DKGDecryptKeyUnavailable)?; - let transcript = bcs::from_bytes::<::Transcript>( - dkg_session.transcript.as_slice(), - ) - .map_err(NoRandomnessReason::TranscriptDeserializationError)?; - - let vuf_pp = WvufPP::from(&dkg_pub_params.pvss_config.pp); - - // No need to verify the transcript. - - // keys for randomness generation - let (sk, pk) = DefaultDKG::decrypt_secret_share_from_transcript( - &dkg_pub_params, - &transcript, - my_index as u64, - &dkg_decrypt_key, - ) - .map_err(NoRandomnessReason::SecretShareDecryptionFailed)?; - - let pk_shares = (0..new_epoch_state.verifier.len()) - .map(|id| { - transcript.get_public_key_share(&dkg_pub_params.pvss_config.wconfig, &Player { id }) - }) - .collect::>(); - - // Recover existing augmented key pair or generate a new one - let (ask, apk) = if let Some((_, key_pair)) = self - .rand_storage - .get_key_pair_bytes() - .map_err(NoRandomnessReason::RandDbNotAvailable)? - .filter(|(epoch, _)| *epoch == new_epoch) - { - bcs::from_bytes(&key_pair).map_err(NoRandomnessReason::KeyPairDeserializationError)? - } else { - let mut rng = - StdRng::from_rng(thread_rng()).map_err(NoRandomnessReason::RngCreationError)?; - let augmented_key_pair = WVUF::augment_key_pair(&vuf_pp, sk, pk, &mut rng); - self.rand_storage - .save_key_pair_bytes( - new_epoch, - bcs::to_bytes(&augmented_key_pair) - .map_err(NoRandomnessReason::KeyPairSerializationError)?, - ) - .map_err(NoRandomnessReason::KeyPairPersistError)?; - augmented_key_pair - }; - - let keys = RandKeys::new(ask, apk, pk_shares, new_epoch_state.verifier.len()); - - let rand_config = RandConfig::new( - self.author, - new_epoch, - new_epoch_state.verifier.clone(), - vuf_pp, - keys, - dkg_pub_params.pvss_config.wconfig.clone(), - ); - - Ok(rand_config) - } - - async fn start_new_epoch(&mut self, payload: OnChainConfigPayload

    ) { - let validator_set: ValidatorSet = payload - .get() - .expect("failed to get ValidatorSet from payload"); - let epoch_state = Arc::new(EpochState { - epoch: payload.epoch(), - verifier: (&validator_set).into(), - }); - - self.epoch_state = Some(epoch_state.clone()); - - let onchain_consensus_config: anyhow::Result = payload.get(); - let onchain_execution_config: anyhow::Result = payload.get(); - let onchain_randomness_config: anyhow::Result = payload.get(); - let onchain_jwk_consensus_config: anyhow::Result = payload.get(); - let dkg_state = payload.get::(); - - if let Err(error) = &onchain_consensus_config { - error!("Failed to read on-chain consensus config {}", error); - } - - if let Err(error) = &onchain_execution_config { - error!("Failed to read on-chain execution config {}", error); - } - - if let Err(error) = &onchain_randomness_config { - error!("Failed to read on-chain randomness config {}", error); - } - - self.epoch_state = Some(epoch_state.clone()); - - let consensus_config = onchain_consensus_config.unwrap_or_default(); - let execution_config = onchain_execution_config - .unwrap_or_else(|_| OnChainExecutionConfig::default_if_missing()); - let onchain_randomness_config = onchain_randomness_config - .and_then(OnChainRandomnessConfig::try_from) - .unwrap_or_else(|_| OnChainRandomnessConfig::default_if_missing()); - let jwk_consensus_config = onchain_jwk_consensus_config.unwrap_or_else(|_| { - // `jwk_consensus_config` not yet initialized, falling back to the old configs. - Self::equivalent_jwk_consensus_config_from_deprecated_resources(&payload) - }); - let rand_config = self.try_get_rand_config_for_new_epoch( - &epoch_state, - &onchain_randomness_config, - dkg_state, - &consensus_config, - ); - info!( - "[Randomness] start_new_epoch: epoch={}, rand_config={:?}, ", - epoch_state.epoch, rand_config - ); // The sk inside has `SlientDebug`. - let rand_config = rand_config.ok(); - - let (network_sender, payload_client, payload_manager) = self - .initialize_shared_component(&epoch_state, &consensus_config) - .await; - - let (rand_msg_tx, rand_msg_rx) = aptos_channel::new::( - QueueStyle::FIFO, - 100, - None, - ); - - self.rand_manager_msg_tx = Some(rand_msg_tx); - - if consensus_config.is_dag_enabled() { - self.start_new_epoch_with_dag( - epoch_state, - consensus_config, - execution_config, - onchain_randomness_config, - jwk_consensus_config, - network_sender, - payload_client, - payload_manager, - rand_config, - rand_msg_rx, - ) - .await - } else { - self.start_new_epoch_with_joltean( - epoch_state, - consensus_config, - execution_config, - onchain_randomness_config, - jwk_consensus_config, - network_sender, - payload_client, - payload_manager, - rand_config, - rand_msg_rx, - ) - .await - } - } - - async fn initialize_shared_component( - &mut self, - epoch_state: &EpochState, - consensus_config: &OnChainConsensusConfig, - ) -> (NetworkSender, Arc, Arc) { - self.set_epoch_start_metrics(epoch_state); - self.quorum_store_enabled = self.enable_quorum_store(consensus_config); - let network_sender = self.create_network_sender(epoch_state); - let (payload_manager, quorum_store_client, quorum_store_builder) = self - .init_payload_provider(epoch_state, network_sender.clone(), consensus_config) - .await; - let effective_vtxn_config = consensus_config.effective_validator_txn_config(); - debug!("effective_vtxn_config={:?}", effective_vtxn_config); - let mixed_payload_client = MixedPayloadClient::new( - effective_vtxn_config, - Arc::new(self.vtxn_pool.clone()), - Arc::new(quorum_store_client), - ); - self.start_quorum_store(quorum_store_builder); - ( - network_sender, - Arc::new(mixed_payload_client), - payload_manager, - ) - } - - async fn start_new_epoch_with_joltean( - &mut self, - epoch_state: Arc, - consensus_config: OnChainConsensusConfig, - execution_config: OnChainExecutionConfig, - onchain_randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - network_sender: NetworkSender, - payload_client: Arc, - payload_manager: Arc, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ) { - match self.storage.start() { - LivenessStorageData::FullRecoveryData(initial_data) => { - self.recovery_mode = false; - self.start_round_manager( - initial_data, - epoch_state, - consensus_config, - execution_config, - onchain_randomness_config, - jwk_consensus_config, - Arc::new(network_sender), - payload_client, - payload_manager, - rand_config, - rand_msg_rx, - ) - .await - }, - LivenessStorageData::PartialRecoveryData(ledger_data) => { - self.recovery_mode = true; - self.start_recovery_manager( - ledger_data, - consensus_config, - epoch_state, - Arc::new(network_sender), - ) - .await - }, - } - } - - async fn start_new_epoch_with_dag( - &mut self, - epoch_state: Arc, - onchain_consensus_config: OnChainConsensusConfig, - on_chain_execution_config: OnChainExecutionConfig, - onchain_randomness_config: OnChainRandomnessConfig, - onchain_jwk_consensus_config: OnChainJWKConsensusConfig, - network_sender: NetworkSender, - payload_client: Arc, - payload_manager: Arc, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ) { - let epoch = epoch_state.epoch; - let consensus_key = new_consensus_key_from_storage(&self.config.safety_rules.backend) - .expect("unable to get private key"); - let signer = Arc::new(ValidatorSigner::new(self.author, consensus_key)); - let commit_signer = Arc::new(DagCommitSigner::new(signer.clone())); - - assert!( - onchain_consensus_config.decoupled_execution(), - "decoupled execution must be enabled" - ); - - self.execution_client - .start_epoch( - epoch_state.clone(), - commit_signer, - payload_manager.clone(), - &onchain_consensus_config, - &on_chain_execution_config, - &onchain_randomness_config, - rand_config, - rand_msg_rx, - ) - .await; - - let onchain_dag_consensus_config = onchain_consensus_config.unwrap_dag_config_v1(); - let epoch_to_validators = self.extract_epoch_proposers( - &epoch_state, - onchain_dag_consensus_config.dag_ordering_causal_history_window as u32, - epoch_state.verifier.get_ordered_account_addresses(), - onchain_dag_consensus_config.dag_ordering_causal_history_window as u64, - ); - let dag_storage = Arc::new(StorageAdapter::new( - epoch, - epoch_to_validators, - self.storage.consensus_db(), - self.storage.aptos_db(), - )); - - let network_sender_arc = Arc::new(network_sender); - - let bootstrapper = DagBootstrapper::new( - self.author, - self.dag_config.clone(), - onchain_dag_consensus_config.clone(), - signer, - epoch_state.clone(), - dag_storage, - network_sender_arc.clone(), - network_sender_arc.clone(), - network_sender_arc, - self.aptos_time_service.clone(), - payload_manager, - payload_client, - self.execution_client.get_execution_channel().unwrap(), - self.execution_client.clone(), - onchain_consensus_config.quorum_store_enabled(), - onchain_consensus_config.effective_validator_txn_config(), - onchain_randomness_config, - onchain_jwk_consensus_config, - self.bounded_executor.clone(), - self.config - .quorum_store - .allow_batches_without_pos_in_proposal, - ); - - let (dag_rpc_tx, dag_rpc_rx) = aptos_channel::new(QueueStyle::FIFO, 10, None); - self.dag_rpc_tx = Some(dag_rpc_tx); - let (dag_shutdown_tx, dag_shutdown_rx) = oneshot::channel(); - self.dag_shutdown_tx = Some(dag_shutdown_tx); - - tokio::spawn(bootstrapper.start(dag_rpc_rx, dag_shutdown_rx)); - } - - fn enable_quorum_store(&mut self, onchain_config: &OnChainConsensusConfig) -> bool { - fail_point!("consensus::start_new_epoch::disable_qs", |_| false); - onchain_config.quorum_store_enabled() - } - - async fn process_message( - &mut self, - peer_id: AccountAddress, - consensus_msg: ConsensusMsg, - ) -> anyhow::Result<()> { - fail_point!("consensus::process::any", |_| { - Err(anyhow::anyhow!("Injected error in process_message")) - }); - - if let ConsensusMsg::ProposalMsg(proposal) = &consensus_msg { - observe_block( - proposal.proposal().timestamp_usecs(), - BlockStage::EPOCH_MANAGER_RECEIVED, - ); - } - // we can't verify signatures from a different epoch - let maybe_unverified_event = self.check_epoch(peer_id, consensus_msg).await?; - - if let Some(unverified_event) = maybe_unverified_event { - // filter out quorum store messages if quorum store has not been enabled - match self.filter_quorum_store_events(peer_id, &unverified_event) { - Ok(true) => {}, - Ok(false) => return Ok(()), // This occurs when the quorum store is not enabled, but the recovery mode is enabled. We filter out the messages, but don't raise any error. - Err(err) => return Err(err), - } - // same epoch -> run well-formedness + signature check - let epoch_state = self.epoch_state.clone().unwrap(); - let quorum_store_enabled = self.quorum_store_enabled; - let quorum_store_msg_tx = self.quorum_store_msg_tx.clone(); - let buffered_proposal_tx = self.buffered_proposal_tx.clone(); - let round_manager_tx = self.round_manager_tx.clone(); - let my_peer_id = self.author; - let max_num_batches = self.config.quorum_store.receiver_max_num_batches; - let max_batch_expiry_gap_usecs = - self.config.quorum_store.batch_expiry_gap_when_init_usecs; - let payload_manager = self.payload_manager.clone(); - self.bounded_executor - .spawn(async move { - match monitor!( - "verify_message", - unverified_event.clone().verify( - peer_id, - &epoch_state.verifier, - quorum_store_enabled, - peer_id == my_peer_id, - max_num_batches, - max_batch_expiry_gap_usecs, - ) - ) { - Ok(verified_event) => { - Self::forward_event( - quorum_store_msg_tx, - round_manager_tx, - buffered_proposal_tx, - peer_id, - verified_event, - payload_manager, - ); - }, - Err(e) => { - error!( - SecurityEvent::ConsensusInvalidMessage, - remote_peer = peer_id, - error = ?e, - unverified_event = unverified_event - ); - }, - } - }) - .await; - } - Ok(()) - } - - async fn check_epoch( - &mut self, - peer_id: AccountAddress, - msg: ConsensusMsg, - ) -> anyhow::Result> { - match msg { - ConsensusMsg::ProposalMsg(_) - | ConsensusMsg::SyncInfo(_) - | ConsensusMsg::VoteMsg(_) - | ConsensusMsg::CommitVoteMsg(_) - | ConsensusMsg::CommitDecisionMsg(_) - | ConsensusMsg::BatchMsg(_) - | ConsensusMsg::BatchRequestMsg(_) - | ConsensusMsg::SignedBatchInfo(_) - | ConsensusMsg::ProofOfStoreMsg(_) => { - let event: UnverifiedEvent = msg.into(); - if event.epoch()? == self.epoch() { - return Ok(Some(event)); - } else { - monitor!( - "process_different_epoch_consensus_msg", - self.process_different_epoch(event.epoch()?, peer_id) - )?; - } - }, - ConsensusMsg::EpochChangeProof(proof) => { - let msg_epoch = proof.epoch()?; - debug!( - LogSchema::new(LogEvent::ReceiveEpochChangeProof) - .remote_peer(peer_id) - .epoch(self.epoch()), - "Proof from epoch {}", msg_epoch, - ); - if msg_epoch == self.epoch() { - monitor!("process_epoch_proof", self.initiate_new_epoch(*proof).await)?; - } else { - info!( - remote_peer = peer_id, - "[EpochManager] Unexpected epoch proof from epoch {}, local epoch {}", - msg_epoch, - self.epoch() - ); - counters::EPOCH_MANAGER_ISSUES_DETAILS - .with_label_values(&["epoch_proof_wrong_epoch"]) - .inc(); - } - }, - ConsensusMsg::EpochRetrievalRequest(request) => { - ensure!( - request.end_epoch <= self.epoch(), - "[EpochManager] Received EpochRetrievalRequest beyond what we have locally" - ); - monitor!( - "process_epoch_retrieval", - self.process_epoch_retrieval(*request, peer_id) - )?; - }, - _ => { - bail!("[EpochManager] Unexpected messages: {:?}", msg); - }, - } - Ok(None) - } - - fn filter_quorum_store_events( - &mut self, - peer_id: AccountAddress, - event: &UnverifiedEvent, - ) -> anyhow::Result { - match event { - UnverifiedEvent::BatchMsg(_) - | UnverifiedEvent::SignedBatchInfo(_) - | UnverifiedEvent::ProofOfStoreMsg(_) => { - if self.quorum_store_enabled { - Ok(true) // This states that we shouldn't filter out the event - } else if self.recovery_mode { - Ok(false) // This states that we should filter out the event, but without an error - } else { - Err(anyhow::anyhow!( - "Quorum store is not enabled locally, but received msg from sender: {}", - peer_id, - )) - } - }, - _ => Ok(true), // This states that we shouldn't filter out the event - } - } - - fn forward_event_to( - mut maybe_tx: Option>, - key: K, - value: V, - ) -> anyhow::Result<()> { - if let Some(tx) = &mut maybe_tx { - tx.push(key, value) - } else { - bail!("channel not initialized"); - } - } - - fn forward_event( - quorum_store_msg_tx: Option>, - round_manager_tx: Option< - aptos_channel::Sender<(Author, Discriminant), (Author, VerifiedEvent)>, - >, - buffered_proposal_tx: Option>, - peer_id: AccountAddress, - event: VerifiedEvent, - payload_manager: Arc, - ) { - if let VerifiedEvent::ProposalMsg(proposal) = &event { - observe_block( - proposal.proposal().timestamp_usecs(), - BlockStage::EPOCH_MANAGER_VERIFIED, - ); - } - if let Err(e) = match event { - quorum_store_event @ (VerifiedEvent::SignedBatchInfo(_) - | VerifiedEvent::ProofOfStoreMsg(_) - | VerifiedEvent::BatchMsg(_)) => { - Self::forward_event_to(quorum_store_msg_tx, peer_id, quorum_store_event) - .context("quorum store sender") - }, - proposal_event @ VerifiedEvent::ProposalMsg(_) => { - if let VerifiedEvent::ProposalMsg(p) = &proposal_event { - if let Some(payload) = p.proposal().payload() { - payload_manager - .prefetch_payload_data(payload, p.proposal().timestamp_usecs()); - } - } - Self::forward_event_to(buffered_proposal_tx, peer_id, proposal_event) - .context("proposal precheck sender") - }, - round_manager_event => Self::forward_event_to( - round_manager_tx, - (peer_id, discriminant(&round_manager_event)), - (peer_id, round_manager_event), - ) - .context("round manager sender"), - } { - warn!("Failed to forward event: {}", e); - } - } - - fn process_rpc_request( - &mut self, - peer_id: Author, - request: IncomingRpcRequest, - ) -> anyhow::Result<()> { - fail_point!("consensus::process::any", |_| { - Err(anyhow::anyhow!("Injected error in process_rpc_request")) - }); - - match request.epoch() { - Some(epoch) if epoch != self.epoch() => { - monitor!( - "process_different_epoch_rpc_request", - self.process_different_epoch(epoch, peer_id) - )?; - return Ok(()); - }, - None => { - ensure!(matches!(request, IncomingRpcRequest::BlockRetrieval(_))); - }, - _ => {}, - } - - match request { - IncomingRpcRequest::BlockRetrieval(request) => { - if let Some(tx) = &self.block_retrieval_tx { - tx.push(peer_id, request) - } else { - error!("Round manager not started"); - Ok(()) - } - }, - IncomingRpcRequest::BatchRetrieval(request) => { - if let Some(tx) = &self.batch_retrieval_tx { - tx.push(peer_id, request) - } else { - Err(anyhow::anyhow!("Quorum store not started")) - } - }, - IncomingRpcRequest::DAGRequest(request) => { - if let Some(tx) = &self.dag_rpc_tx { - tx.push(peer_id, request) - } else { - Err(anyhow::anyhow!("DAG not bootstrapped")) - } - }, - IncomingRpcRequest::CommitRequest(request) => { - self.execution_client.send_commit_msg(peer_id, request) - }, - IncomingRpcRequest::RandGenRequest(request) => { - if let Some(tx) = &self.rand_manager_msg_tx { - tx.push(peer_id, request) - } else { - bail!("Rand manager not started"); - } - }, - } - } - - fn process_local_timeout(&mut self, round: u64) { - let Some(sender) = self.round_manager_tx.as_mut() else { - warn!( - "Received local timeout for round {} without Round Manager", - round - ); - return; - }; - - let peer_id = self.author; - let event = VerifiedEvent::LocalTimeout(round); - if let Err(e) = sender.push((peer_id, discriminant(&event)), (peer_id, event)) { - error!("Failed to send event to round manager {:?}", e); - } - } - - async fn await_reconfig_notification(&mut self) { - let reconfig_notification = self - .reconfig_events - .next() - .await - .expect("Reconfig sender dropped, unable to start new epoch"); - self.start_new_epoch(reconfig_notification.on_chain_configs) - .await; - } - - pub async fn start( - mut self, - mut round_timeout_sender_rx: aptos_channels::Receiver, - mut network_receivers: NetworkReceivers, - ) { - // initial start of the processor - self.await_reconfig_notification().await; - loop { - tokio::select! { - (peer, msg) = network_receivers.consensus_messages.select_next_some() => { - monitor!("epoch_manager_process_consensus_messages", - if let Err(e) = self.process_message(peer, msg).await { - error!(epoch = self.epoch(), error = ?e, kind = error_kind(&e)); - }); - }, - (peer, msg) = network_receivers.quorum_store_messages.select_next_some() => { - monitor!("epoch_manager_process_quorum_store_messages", - if let Err(e) = self.process_message(peer, msg).await { - error!(epoch = self.epoch(), error = ?e, kind = error_kind(&e)); - }); - }, - (peer, request) = network_receivers.rpc_rx.select_next_some() => { - monitor!("epoch_manager_process_rpc", - if let Err(e) = self.process_rpc_request(peer, request) { - error!(epoch = self.epoch(), error = ?e, kind = error_kind(&e)); - }); - }, - round = round_timeout_sender_rx.select_next_some() => { - monitor!("epoch_manager_process_round_timeout", - self.process_local_timeout(round)); - }, - } - // Continually capture the time of consensus process to ensure that clock skew between - // validators is reasonable and to find any unusual (possibly byzantine) clock behavior. - counters::OP_COUNTERS - .gauge("time_since_epoch_ms") - .set(duration_since_epoch().as_millis() as i64); - } - } - - /// Before `JWKConsensusConfig` is initialized, convert from `Features` and `SupportedOIDCProviders` instead. - fn equivalent_jwk_consensus_config_from_deprecated_resources( - payload: &OnChainConfigPayload

    , - ) -> OnChainJWKConsensusConfig { - let features = payload.get::().ok(); - let oidc_providers = payload.get::().ok(); - OnChainJWKConsensusConfig::from((features, oidc_providers)) - } -} - -fn new_consensus_key_from_storage(backend: &SecureBackend) -> anyhow::Result { - let storage: Storage = backend.into(); - storage - .available() - .map_err(|e| anyhow!("Storage is not available: {e}"))?; - storage - .get(CONSENSUS_KEY) - .map(|v| v.value) - .map_err(|e| anyhow!("storage get and map err: {e}")) -} - -fn load_dkg_decrypt_key_from_identity_blob( - config: &SafetyRulesConfig, -) -> anyhow::Result<::NewValidatorDecryptKey> { - let identity_blob = config.initial_safety_rules_config.identity_blob()?; - identity_blob.try_into_dkg_new_validator_decrypt_key() -} - -fn load_dkg_decrypt_key_from_secure_storage( - config: &SafetyRulesConfig, -) -> anyhow::Result<::NewValidatorDecryptKey> { - let consensus_key = new_consensus_key_from_storage(&config.backend)?; - maybe_dk_from_bls_sk(&consensus_key) -} - -fn load_dkg_decrypt_key( - config: &SafetyRulesConfig, -) -> Option<::NewValidatorDecryptKey> { - match load_dkg_decrypt_key_from_secure_storage(config) { - Ok(dk) => { - return Some(dk); - }, - Err(e) => { - warn!("{e}"); - }, - } - - match load_dkg_decrypt_key_from_identity_blob(config) { - Ok(dk) => { - return Some(dk); - }, - Err(e) => { - warn!("{e}"); - }, - } - - None -} - -#[derive(Debug)] -enum NoRandomnessReason { - VTxnDisabled, - FeatureDisabled, - DKGStateResourceMissing(anyhow::Error), - DKGCompletedSessionResourceMissing, - CompletedSessionTooOld, - NotInValidatorSet, - DKGDecryptKeyUnavailable, - TranscriptDeserializationError(bcs::Error), - SecretShareDecryptionFailed(anyhow::Error), - RngCreationError(rand::Error), - RandDbNotAvailable(anyhow::Error), - KeyPairDeserializationError(bcs::Error), - KeyPairSerializationError(bcs::Error), - KeyPairPersistError(anyhow::Error), -} diff --git a/consensus/src/error.rs b/consensus/src/error.rs deleted file mode 100644 index d77acce745694..0000000000000 --- a/consensus/src/error.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::pipeline; -use thiserror::Error; - -#[derive(Debug, Error)] -#[error(transparent)] -pub struct DbError { - #[from] - inner: anyhow::Error, -} - -impl From for DbError { - fn from(e: aptos_storage_interface::AptosDbError) -> Self { - DbError { inner: e.into() } - } -} - -#[derive(Debug, Error)] -#[error(transparent)] -pub struct StateSyncError { - #[from] - inner: anyhow::Error, -} - -impl From for StateSyncError { - fn from(e: pipeline::errors::Error) -> Self { - StateSyncError { inner: e.into() } - } -} - -impl From for StateSyncError { - fn from(e: aptos_executor_types::ExecutorError) -> Self { - StateSyncError { inner: e.into() } - } -} - -#[derive(Debug, Error)] -#[error(transparent)] -pub struct MempoolError { - #[from] - inner: anyhow::Error, -} - -#[derive(Debug, Error)] -#[error(transparent)] -pub struct QuorumStoreError { - #[from] - inner: anyhow::Error, -} - -#[derive(Debug, Error)] -#[error(transparent)] -pub struct VerifyError { - #[from] - inner: anyhow::Error, -} - -pub fn error_kind(e: &anyhow::Error) -> &'static str { - if e.downcast_ref::() - .is_some() - { - return "Execution"; - } - if let Some(e) = e.downcast_ref::() { - if e.inner - .downcast_ref::() - .is_some() - { - return "Execution"; - } - return "StateSync"; - } - if e.downcast_ref::().is_some() { - return "Mempool"; - } - if e.downcast_ref::().is_some() { - return "QuorumStore"; - } - if e.downcast_ref::().is_some() { - return "ConsensusDb"; - } - if e.downcast_ref::().is_some() { - return "SafetyRules"; - } - if e.downcast_ref::().is_some() { - return "VerifyError"; - } - "InternalError" -} - -#[cfg(test)] -mod tests { - use crate::error::{error_kind, StateSyncError}; - use anyhow::Context; - - #[test] - fn conversion_and_downcast() { - let error = aptos_executor_types::ExecutorError::InternalError { - error: "lalala".to_string(), - }; - let typed_error: StateSyncError = error.into(); - let upper: anyhow::Result<()> = Err(typed_error).context("Context!"); - assert_eq!(error_kind(&upper.unwrap_err()), "Execution"); - } -} diff --git a/consensus/src/execution_pipeline.rs b/consensus/src/execution_pipeline.rs deleted file mode 100644 index 53a50f01a4659..0000000000000 --- a/consensus/src/execution_pipeline.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -use crate::{ - block_preparer::BlockPreparer, - monitor, - state_computer::{PipelineExecutionResult, StateComputeResultFut}, -}; -use aptos_consensus_types::block::Block; -use aptos_crypto::HashValue; -use aptos_executor_types::{ - state_checkpoint_output::StateCheckpointOutput, BlockExecutorTrait, ExecutorError, - ExecutorResult, -}; -use aptos_experimental_runtimes::thread_manager::optimal_min_len; -use aptos_logger::{debug, error}; -use aptos_types::{ - block_executor::{config::BlockExecutorConfigFromOnchain, partitioner::ExecutableBlock}, - block_metadata_ext::BlockMetadataExt, - transaction::{ - signature_verified_transaction::SignatureVerifiedTransaction, SignedTransaction, - }, -}; -use fail::fail_point; -use once_cell::sync::Lazy; -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; -use std::sync::Arc; -use tokio::sync::{mpsc, oneshot}; - -pub static SIG_VERIFY_POOL: Lazy> = Lazy::new(|| { - Arc::new( - rayon::ThreadPoolBuilder::new() - .num_threads(8) // More than 8 threads doesn't seem to help much - .thread_name(|index| format!("signature-checker-{}", index)) - .build() - .unwrap(), - ) -}); - -pub struct ExecutionPipeline { - prepare_block_tx: mpsc::UnboundedSender, -} - -impl ExecutionPipeline { - pub fn spawn(executor: Arc, runtime: &tokio::runtime::Handle) -> Self { - let (prepare_block_tx, prepare_block_rx) = mpsc::unbounded_channel(); - let (execute_block_tx, execute_block_rx) = mpsc::unbounded_channel(); - let (ledger_apply_tx, ledger_apply_rx) = mpsc::unbounded_channel(); - runtime.spawn(Self::prepare_block_stage( - prepare_block_rx, - execute_block_tx, - )); - runtime.spawn(Self::execute_stage( - execute_block_rx, - ledger_apply_tx, - executor.clone(), - )); - runtime.spawn(Self::ledger_apply_stage(ledger_apply_rx, executor)); - Self { prepare_block_tx } - } - - pub async fn queue( - &self, - block: Block, - metadata: BlockMetadataExt, - parent_block_id: HashValue, - txn_generator: BlockPreparer, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - ) -> StateComputeResultFut { - let (result_tx, result_rx) = oneshot::channel(); - let block_id = block.id(); - self.prepare_block_tx - .send(PrepareBlockCommand { - block, - metadata, - block_executor_onchain_config, - parent_block_id, - block_preparer: txn_generator, - result_tx, - }) - .expect("Failed to send block to execution pipeline."); - - Box::pin(async move { - result_rx - .await - .map_err(|err| ExecutorError::InternalError { - error: format!( - "Failed to receive execution result for block {}: {:?}.", - block_id, err - ), - })? - }) - } - - async fn prepare_block( - execute_block_tx: mpsc::UnboundedSender, - command: PrepareBlockCommand, - ) { - let PrepareBlockCommand { - block, - metadata, - block_executor_onchain_config, - parent_block_id, - block_preparer, - result_tx, - } = command; - - debug!("prepare_block received block {}.", block.id()); - let input_txns = block_preparer.prepare_block(&block).await; - if let Err(e) = input_txns { - result_tx.send(Err(e)).unwrap_or_else(|err| { - error!( - block_id = block.id(), - "Failed to send back execution result for block {}: {:?}.", - block.id(), - err, - ); - }); - return; - } - let validator_txns = block.validator_txns().cloned().unwrap_or_default(); - let input_txns = input_txns.unwrap(); - tokio::task::spawn_blocking(move || { - let txns_to_execute = - Block::combine_to_input_transactions(validator_txns, input_txns.clone(), metadata); - let sig_verified_txns: Vec = - SIG_VERIFY_POOL.install(|| { - let num_txns = txns_to_execute.len(); - txns_to_execute - .into_par_iter() - .with_min_len(optimal_min_len(num_txns, 32)) - .map(|t| t.into()) - .collect::>() - }); - execute_block_tx - .send(ExecuteBlockCommand { - input_txns, - block: (block.id(), sig_verified_txns).into(), - parent_block_id, - block_executor_onchain_config, - result_tx, - }) - .expect("Failed to send block to execution pipeline."); - }) - .await - .expect("Failed to spawn_blocking."); - } - - async fn prepare_block_stage( - mut prepare_block_rx: mpsc::UnboundedReceiver, - execute_block_tx: mpsc::UnboundedSender, - ) { - while let Some(command) = prepare_block_rx.recv().await { - monitor!( - "prepare_block", - Self::prepare_block(execute_block_tx.clone(), command).await - ); - } - debug!("prepare_block_stage quitting."); - } - - async fn execute_stage( - mut block_rx: mpsc::UnboundedReceiver, - ledger_apply_tx: mpsc::UnboundedSender, - executor: Arc, - ) { - while let Some(ExecuteBlockCommand { - input_txns, - block, - parent_block_id, - block_executor_onchain_config, - result_tx, - }) = block_rx.recv().await - { - let block_id = block.block_id; - debug!("execute_stage received block {}.", block_id); - let executor = executor.clone(); - let state_checkpoint_output = monitor!( - "execute_block", - tokio::task::spawn_blocking(move || { - fail_point!("consensus::compute", |_| { - Err(ExecutorError::InternalError { - error: "Injected error in compute".into(), - }) - }); - executor.execute_and_state_checkpoint( - block, - parent_block_id, - block_executor_onchain_config, - ) - }) - .await - ) - .expect("Failed to spawn_blocking."); - - ledger_apply_tx - .send(LedgerApplyCommand { - input_txns, - block_id, - parent_block_id, - state_checkpoint_output, - result_tx, - }) - .expect("Failed to send block to ledger_apply stage."); - } - debug!("execute_stage quitting."); - } - - async fn ledger_apply_stage( - mut block_rx: mpsc::UnboundedReceiver, - executor: Arc, - ) { - while let Some(LedgerApplyCommand { - input_txns, - block_id, - parent_block_id, - state_checkpoint_output, - result_tx, - }) = block_rx.recv().await - { - debug!("ledger_apply stage received block {}.", block_id); - let res = async { - let executor = executor.clone(); - monitor!( - "ledger_apply", - tokio::task::spawn_blocking(move || { - executor.ledger_update(block_id, parent_block_id, state_checkpoint_output?) - }) - ) - .await - .expect("Failed to spawn_blocking().") - } - .await; - let pipe_line_res = res.map(|output| PipelineExecutionResult::new(input_txns, output)); - result_tx.send(pipe_line_res).unwrap_or_else(|err| { - error!( - block_id = block_id, - "Failed to send back execution result for block {}: {:?}", block_id, err, - ); - }); - } - debug!("ledger_apply stage quitting."); - } -} - -struct PrepareBlockCommand { - block: Block, - metadata: BlockMetadataExt, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - // The parent block id. - parent_block_id: HashValue, - block_preparer: BlockPreparer, - result_tx: oneshot::Sender>, -} - -struct ExecuteBlockCommand { - input_txns: Vec, - block: ExecutableBlock, - parent_block_id: HashValue, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - result_tx: oneshot::Sender>, -} - -struct LedgerApplyCommand { - input_txns: Vec, - block_id: HashValue, - parent_block_id: HashValue, - state_checkpoint_output: ExecutorResult, - result_tx: oneshot::Sender>, -} diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs deleted file mode 100644 index dbc5cb5ec5e5c..0000000000000 --- a/consensus/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -//! Consensus for the Aptos Core blockchain -//! -//! The consensus protocol implemented is AptosBFT (based on -//! [DiemBFT](https://developers.diem.com/papers/diem-consensus-state-machine-replication-in-the-diem-blockchain/2021-08-17.pdf)). - -#![cfg_attr(feature = "fuzzing", allow(dead_code))] -#![recursion_limit = "512"] - -#[macro_use(defer)] -extern crate scopeguard; - -extern crate core; - -mod block_storage; -mod consensusdb; -mod dag; -mod epoch_manager; -mod error; -mod liveness; -mod logging; -mod metrics_safety_rules; -mod network; -#[cfg(test)] -mod network_tests; -mod payload_client; -mod pending_votes; -pub mod persistent_liveness_storage; -mod pipeline; -pub mod quorum_store; -mod rand; -mod recovery_manager; -mod round_manager; -mod state_computer; -#[cfg(test)] -mod state_computer_tests; -mod state_replication; -#[cfg(any(test, feature = "fuzzing"))] -mod test_utils; -#[cfg(test)] -mod twins; -mod txn_notifier; -pub mod util; - -mod block_preparer; -/// AptosBFT implementation -pub mod consensus_provider; -/// Required by the telemetry service -pub mod counters; -mod execution_pipeline; -/// AptosNet interface. -pub mod network_interface; -mod payload_manager; -mod qc_aggregator; -mod transaction_deduper; -mod transaction_filter; -mod transaction_shuffler; -mod txn_hash_and_authenticator_deduper; - -use aptos_metrics_core::IntGauge; -pub use consensusdb::create_checkpoint; -/// Required by the smoke tests -pub use consensusdb::CONSENSUS_DB_NAME; -pub use quorum_store::quorum_store_db::QUORUM_STORE_DB_NAME; -#[cfg(feature = "fuzzing")] -pub use round_manager::round_manager_fuzzing; - -struct IntGaugeGuard { - gauge: IntGauge, -} - -impl IntGaugeGuard { - fn new(gauge: IntGauge) -> Self { - gauge.inc(); - Self { gauge } - } -} - -impl Drop for IntGaugeGuard { - fn drop(&mut self) { - self.gauge.dec(); - } -} - -/// Helper function to record metrics for external calls. -/// Include call counts, time, and whether it's inside or not (1 or 0). -/// It assumes a OpMetrics defined as OP_COUNTERS in crate::counters; -#[macro_export] -macro_rules! monitor { - ($name:literal, $fn:expr) => {{ - use $crate::{counters::OP_COUNTERS, IntGaugeGuard}; - let _timer = OP_COUNTERS.timer($name); - let _guard = IntGaugeGuard::new(OP_COUNTERS.gauge(concat!($name, "_running"))); - $fn - }}; -} diff --git a/consensus/src/liveness/cached_proposer_election.rs b/consensus/src/liveness/cached_proposer_election.rs deleted file mode 100644 index bb53e196d157a..0000000000000 --- a/consensus/src/liveness/cached_proposer_election.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::proposer_election::ProposerElection; -use crate::counters::PROPOSER_ELECTION_DURATION; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::Mutex; -use aptos_logger::prelude::info; -use std::collections::BTreeMap; - -// Wrapper around ProposerElection. -// -// Function get_valid_proposer can be expensive, and we want to make sure -// it is computed only once for a given round. -pub struct CachedProposerElection { - epoch: u64, - proposer_election: Box, - // We use BTreeMap since we want a fixed window of cached elements - // to look back (and caller knows how big of a window it needs). - // LRU cache wouldn't work as well, as access order of the elements - // would define eviction, and could lead to evicting still needed elements. - recent_elections: Mutex>, - window: usize, -} - -impl CachedProposerElection { - pub fn new( - epoch: u64, - proposer_election: Box, - window: usize, - ) -> Self { - Self { - epoch, - proposer_election, - recent_elections: Mutex::new(BTreeMap::new()), - window, - } - } - - pub fn get_or_compute_entry(&self, round: Round) -> (Author, f64) { - let mut recent_elections = self.recent_elections.lock(); - - if round > self.window as u64 { - *recent_elections = recent_elections.split_off(&(round - self.window as u64)); - } - - *recent_elections.entry(round).or_insert_with(|| { - let _timer = PROPOSER_ELECTION_DURATION.start_timer(); - let result = self - .proposer_election - .get_valid_proposer_and_voting_power_participation_ratio(round); - info!( - "ProposerElection for epoch {} and round {}: {:?}", - self.epoch, round, result - ); - result - }) - } -} - -impl ProposerElection for CachedProposerElection { - fn get_valid_proposer(&self, round: Round) -> Author { - self.get_or_compute_entry(round).0 - } - - fn get_voting_power_participation_ratio(&self, round: Round) -> f64 { - self.get_or_compute_entry(round).1 - } -} diff --git a/consensus/src/liveness/cached_proposer_election_test.rs b/consensus/src/liveness/cached_proposer_election_test.rs deleted file mode 100644 index 0cc059f1da5d3..0000000000000 --- a/consensus/src/liveness/cached_proposer_election_test.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::proposer_election::ProposerElection; -use crate::liveness::cached_proposer_election::CachedProposerElection; -use aptos_consensus_types::common::{Author, Round}; -use aptos_infallible::Mutex; -use std::{cell::Cell, sync::Arc}; - -struct MockProposerElection { - proposers: Vec, - asked: Arc>>, -} - -impl MockProposerElection { - pub fn new(proposers: Vec, asked: Arc>>) -> Self { - Self { proposers, asked } - } -} - -impl ProposerElection for MockProposerElection { - fn get_valid_proposer(&self, round: Round) -> Author { - let round_uszie = round as usize; - let asked = self.asked.lock(); - asked.replace(asked.get() + 1); - self.proposers[round_uszie % self.proposers.len()] - } -} - -#[test] -fn test_get_valid_proposer_caching() { - let asked = Arc::new(Mutex::new(Cell::new(0))); - let authors: Vec = (0..4).map(|_| Author::random()).collect(); - let cpe = CachedProposerElection::new( - 1, - Box::new(MockProposerElection::new(authors.clone(), asked.clone())), - 10, - ); - - assert_eq!(asked.lock().get(), 0); - - assert_eq!(cpe.get_valid_proposer(0), authors[0]); - assert_eq!(asked.lock().get(), 1); - assert!(cpe.is_valid_proposer(authors[0], 0)); - assert!(!cpe.is_valid_proposer(authors[1], 0)); - assert_eq!(asked.lock().get(), 1); - - assert_eq!(cpe.get_valid_proposer(1), authors[1]); - assert_eq!(asked.lock().get(), 2); - assert!(cpe.is_valid_proposer(authors[1], 1)); - assert!(!cpe.is_valid_proposer(authors[0], 1)); - assert_eq!(asked.lock().get(), 2); - - assert_eq!(cpe.get_valid_proposer(0), authors[0]); - assert_eq!(asked.lock().get(), 2); - - assert_eq!(cpe.get_valid_proposer(11), authors[3]); - assert_eq!(asked.lock().get(), 3); - assert!(cpe.is_valid_proposer(authors[3], 11)); - assert!(!cpe.is_valid_proposer(authors[0], 11)); - assert_eq!(asked.lock().get(), 3); - - // round=0 is outside the caching window, and round=1 is still inside - assert_eq!(cpe.get_valid_proposer(0), authors[0]); - assert_eq!(asked.lock().get(), 4); - - assert_eq!(cpe.get_valid_proposer(1), authors[1]); - assert_eq!(asked.lock().get(), 4); -} diff --git a/consensus/src/liveness/leader_reputation.rs b/consensus/src/liveness/leader_reputation.rs deleted file mode 100644 index 9351789e5100c..0000000000000 --- a/consensus/src/liveness/leader_reputation.rs +++ /dev/null @@ -1,813 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters::{ - CHAIN_HEALTH_PARTICIPATING_NUM_VALIDATORS, CHAIN_HEALTH_PARTICIPATING_VOTING_POWER, - CHAIN_HEALTH_REPUTATION_PARTICIPATING_VOTING_POWER_FRACTION, - CHAIN_HEALTH_TOTAL_NUM_VALIDATORS, CHAIN_HEALTH_TOTAL_VOTING_POWER, - CHAIN_HEALTH_WINDOW_SIZES, COMMITTED_PROPOSALS_IN_WINDOW, COMMITTED_VOTES_IN_WINDOW, - CONSENSUS_PARTICIPATION_STATUS, FAILED_PROPOSALS_IN_WINDOW, - LEADER_REPUTATION_ROUND_HISTORY_SIZE, - }, - liveness::proposer_election::{choose_index, ProposerElection}, -}; -use anyhow::{ensure, Result}; -use aptos_bitvec::BitVec; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::HashValue; -use aptos_infallible::{Mutex, MutexGuard}; -use aptos_logger::prelude::*; -use aptos_storage_interface::DbReader; -use aptos_types::{ - account_config::NewBlockEvent, epoch_change::EpochChangeProof, epoch_state::EpochState, -}; -use std::{ - cmp::max, - collections::{HashMap, HashSet}, - convert::TryFrom, - sync::Arc, -}; - -pub type VotingPowerRatio = f64; - -/// Interface to query committed NewBlockEvent. -pub trait MetadataBackend: Send + Sync { - /// Return a contiguous NewBlockEvent window in which last one is at target_round or - /// latest committed, return all previous one if not enough. - fn get_block_metadata( - &self, - target_epoch: u64, - target_round: Round, - ) -> (Vec, HashValue); -} - -#[derive(Debug, Clone)] -pub struct VersionedNewBlockEvent { - /// event - pub event: NewBlockEvent, - /// version - pub version: u64, -} - -pub struct AptosDBBackend { - window_size: usize, - seek_len: usize, - aptos_db: Arc, - db_result: Mutex<(Vec, u64, bool)>, -} - -impl AptosDBBackend { - pub fn new(window_size: usize, seek_len: usize, aptos_db: Arc) -> Self { - Self { - window_size, - seek_len, - aptos_db, - db_result: Mutex::new((vec![], 0u64, true)), - } - } - - fn refresh_db_result( - &self, - mut locked: MutexGuard<'_, (Vec, u64, bool)>, - latest_db_version: u64, - ) -> Result<(Vec, u64, bool)> { - // assumes target round is not too far from latest commit - let limit = self.window_size + self.seek_len; - - let events = self.aptos_db.get_latest_block_events(limit)?; - - let max_returned_version = events.first().map_or(0, |first| first.transaction_version); - - let new_block_events = events - .into_iter() - .map(|event| { - Ok(VersionedNewBlockEvent { - event: bcs::from_bytes::(event.event.event_data())?, - version: event.transaction_version, - }) - }) - .collect::, bcs::Error>>()?; - - let hit_end = new_block_events.len() < limit; - - let result = ( - new_block_events, - std::cmp::max(latest_db_version, max_returned_version), - hit_end, - ); - *locked = result.clone(); - Ok(result) - } - - fn get_from_db_result( - &self, - target_epoch: u64, - target_round: Round, - events: &Vec, - hit_end: bool, - ) -> (Vec, HashValue) { - // Do not warn when round==0, because check will always be unsure of whether we have - // all events from the previous epoch. If there is an actual issue, next round will log it. - if target_round != 0 { - let has_larger = events.first().map_or(false, |e| { - (e.event.epoch(), e.event.round()) >= (target_epoch, target_round) - }); - if !has_larger { - // error, and not a fatal, in an unlikely scenario that we have many failed consecutive rounds, - // and nobody has any newer successful blocks. - warn!( - "Local history is too old, asking for {} epoch and {} round, and latest from db is {} epoch and {} round! Elected proposers are unlikely to match!!", - target_epoch, target_round, events.first().map_or(0, |e| e.event.epoch()), events.first().map_or(0, |e| e.event.round())) - } - } - - let mut max_version = 0; - let mut result = vec![]; - for event in events { - if (event.event.epoch(), event.event.round()) <= (target_epoch, target_round) - && result.len() < self.window_size - { - max_version = std::cmp::max(max_version, event.version); - result.push(event.event.clone()); - } - } - - if result.len() < self.window_size && !hit_end { - error!( - "We are not fetching far enough in history, we filtered from {} to {}, but asked for {}. Target ({}, {}), received from {:?} to {:?}.", - events.len(), - result.len(), - self.window_size, - target_epoch, - target_round, - events.last().map_or((0, 0), |e| (e.event.epoch(), e.event.round())), - events.first().map_or((0, 0), |e| (e.event.epoch(), e.event.round())), - ); - } - - if result.is_empty() { - warn!("No events in the requested window could be found"); - (result, HashValue::zero()) - } else { - let root_hash = self - .aptos_db - .get_accumulator_root_hash(max_version) - .unwrap_or_else(|_| { - error!( - "We couldn't fetch accumulator hash for the {} version, for {} epoch, {} round", - max_version, target_epoch, target_round, - ); - HashValue::zero() - }); - (result, root_hash) - } - } -} - -impl MetadataBackend for AptosDBBackend { - // assume the target_round only increases - fn get_block_metadata( - &self, - target_epoch: u64, - target_round: Round, - ) -> (Vec, HashValue) { - let locked = self.db_result.lock(); - let events = &locked.0; - let version = locked.1; - let hit_end = locked.2; - - let has_larger = events.first().map_or(false, |e| { - (e.event.epoch(), e.event.round()) >= (target_epoch, target_round) - }); - let latest_db_version = self.aptos_db.get_latest_version().unwrap_or(0); - // check if fresher data has potential to give us different result - if !has_larger && version < latest_db_version { - let fresh_db_result = self.refresh_db_result(locked, latest_db_version); - match fresh_db_result { - Ok((events, _version, hit_end)) => { - self.get_from_db_result(target_epoch, target_round, &events, hit_end) - }, - Err(e) => { - // fails if requested events were pruned / or we never backfil them. - warn!( - error = ?e, "[leader reputation] Fail to refresh window", - ); - (vec![], HashValue::zero()) - }, - } - } else { - self.get_from_db_result(target_epoch, target_round, events, hit_end) - } - } -} - -/// Interface to calculate weights for proposers based on history. -pub trait ReputationHeuristic: Send + Sync { - /// Return the weights of all candidates based on the history. - fn get_weights( - &self, - epoch: u64, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - ) -> Vec; -} - -pub struct NewBlockEventAggregation { - // Window sizes are in number of succesfull blocks, not number of rounds. - // i.e. we can be looking at different number of rounds for the same window, - // dependig on how many failures we have. - voter_window_size: usize, - proposer_window_size: usize, - reputation_window_from_stale_end: bool, -} - -impl NewBlockEventAggregation { - pub fn new( - voter_window_size: usize, - proposer_window_size: usize, - reputation_window_from_stale_end: bool, - ) -> Self { - Self { - voter_window_size, - proposer_window_size, - reputation_window_from_stale_end, - } - } - - pub fn bitvec_to_voters<'a>( - validators: &'a [Author], - bitvec: &BitVec, - ) -> Result, String> { - if BitVec::required_buckets(validators.len() as u16) != bitvec.num_buckets() { - return Err(format!( - "bitvec bucket {} does not match validators len {}", - bitvec.num_buckets(), - validators.len() - )); - } - - Ok(validators - .iter() - .enumerate() - .filter_map(|(index, validator)| { - if bitvec.is_set(index as u16) { - Some(validator) - } else { - None - } - }) - .collect()) - } - - pub fn indices_to_validators<'a>( - validators: &'a [Author], - indices: &[u64], - ) -> Result, String> { - indices - .iter() - .map(|index| { - usize::try_from(*index) - .map_err(|_err| format!("index {} out of bounds", index)) - .and_then(|index| { - validators.get(index).ok_or(format!( - "index {} is larger than number of validators {}", - index, - validators.len() - )) - }) - }) - .collect() - } - - fn history_iter<'a>( - history: &'a [NewBlockEvent], - epoch_to_candidates: &'a HashMap>, - window_size: usize, - from_stale_end: bool, - ) -> impl Iterator { - let sub_history = if from_stale_end { - let start = if history.len() > window_size { - history.len() - window_size - } else { - 0 - }; - - &history[start..] - } else { - if !history.is_empty() { - assert!( - ( - history.first().unwrap().epoch(), - history.first().unwrap().round() - ) >= ( - history.last().unwrap().epoch(), - history.last().unwrap().round() - ) - ); - } - let end = if history.len() > window_size { - window_size - } else { - history.len() - }; - - &history[..end] - }; - sub_history - .iter() - .filter(move |&meta| epoch_to_candidates.contains_key(&meta.epoch())) - } - - pub fn get_aggregated_metrics( - &self, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - author: &Author, - ) -> ( - HashMap, - HashMap, - HashMap, - ) { - let votes = self.count_votes(epoch_to_candidates, history); - let proposals = self.count_proposals(epoch_to_candidates, history); - let failed_proposals = self.count_failed_proposals(epoch_to_candidates, history); - - COMMITTED_PROPOSALS_IN_WINDOW.set(*proposals.get(author).unwrap_or(&0) as i64); - FAILED_PROPOSALS_IN_WINDOW.set(*failed_proposals.get(author).unwrap_or(&0) as i64); - COMMITTED_VOTES_IN_WINDOW.set(*votes.get(author).unwrap_or(&0) as i64); - - LEADER_REPUTATION_ROUND_HISTORY_SIZE.set( - proposals.values().sum::() as i64 + failed_proposals.values().sum::() as i64, - ); - - (votes, proposals, failed_proposals) - } - - pub fn count_votes( - &self, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - ) -> HashMap { - Self::count_votes_custom( - epoch_to_candidates, - history, - self.voter_window_size, - self.reputation_window_from_stale_end, - ) - } - - pub fn count_votes_custom( - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - window_size: usize, - from_stale_end: bool, - ) -> HashMap { - Self::history_iter(history, epoch_to_candidates, window_size, from_stale_end).fold( - HashMap::new(), - |mut map, meta| { - match Self::bitvec_to_voters( - &epoch_to_candidates[&meta.epoch()], - &meta.previous_block_votes_bitvec().clone().into(), - ) { - Ok(voters) => { - for &voter in voters { - let count = map.entry(voter).or_insert(0); - *count += 1; - } - }, - Err(msg) => { - error!( - "Voter conversion from bitmap failed at epoch {}, round {}: {}", - meta.epoch(), - meta.round(), - msg - ) - }, - } - map - }, - ) - } - - pub fn count_proposals( - &self, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - ) -> HashMap { - Self::count_proposals_custom( - epoch_to_candidates, - history, - self.proposer_window_size, - self.reputation_window_from_stale_end, - ) - } - - pub fn count_proposals_custom( - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - window_size: usize, - from_stale_end: bool, - ) -> HashMap { - Self::history_iter(history, epoch_to_candidates, window_size, from_stale_end).fold( - HashMap::new(), - |mut map, meta| { - let count = map.entry(meta.proposer()).or_insert(0); - *count += 1; - map - }, - ) - } - - pub fn count_failed_proposals( - &self, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - ) -> HashMap { - Self::history_iter( - history, - epoch_to_candidates, - self.proposer_window_size, - self.reputation_window_from_stale_end, - ) - .fold(HashMap::new(), |mut map, meta| { - match Self::indices_to_validators( - &epoch_to_candidates[&meta.epoch()], - meta.failed_proposer_indices(), - ) { - Ok(failed_proposers) => { - for &failed_proposer in failed_proposers { - let count = map.entry(failed_proposer).or_insert(0); - *count += 1; - } - }, - Err(msg) => { - error!( - "Failed proposer conversion from indices failed at epoch {}, round {}: {}", - meta.epoch(), - meta.round(), - msg - ) - }, - } - map - }) - } -} - -/// Heuristic that looks at successful and failed proposals, as well as voting history, -/// to define node reputation, used for leader selection. -/// -/// We want to optimize leader selection to primarily maximize network's throughput, -/// but we also, in combinatoin with staking rewards logic, need to be reasonably fair. -/// -/// Logic is: -/// * if proposer round failure rate within the proposer window is strictly above threshold, use failed_weight (default 1). -/// * otherwise, if node had no proposal rounds and no successful votes, use inactive_weight (default 10). -/// * otherwise, use the default active_weight (default 100). -/// -/// We primarily want to avoid failed rounds, as they have a largest negative effect on the network. -/// So if we see a node having failures to propose, when it was the leader, we want to avoid that node. -/// We add a threshold (instead of penalizing on a single failure), so that transient issues in the network, -/// or malicious behaviour of the next leader is avoided. In general, we expect there to be -/// proposer_window_size/num_validators opportunities for a node to be a leader, so a single failure, or a -/// subset of following leaders being malicious will not be enough to exclude a node. -/// On the other hand, single failure, without any successes before will exclude the note. -/// Threshold probably makes the most sense to be between: -/// * 10% (aggressive exclusion with 1 failure in 10 proposals being enough for exclusion) -/// * and 33% (much less aggressive exclusion, with 1 failure for every 2 successes, should still reduce failed -/// rounds by at least 66%, and is enough to avoid byzantine attacks as well as the rest of the protocol) -pub struct ProposerAndVoterHeuristic { - author: Author, - active_weight: u64, - inactive_weight: u64, - failed_weight: u64, - failure_threshold_percent: u32, - aggregation: NewBlockEventAggregation, -} - -impl ProposerAndVoterHeuristic { - pub fn new( - author: Author, - active_weight: u64, - inactive_weight: u64, - failed_weight: u64, - failure_threshold_percent: u32, - voter_window_size: usize, - proposer_window_size: usize, - reputation_window_from_stale_end: bool, - ) -> Self { - Self { - author, - active_weight, - inactive_weight, - failed_weight, - failure_threshold_percent, - aggregation: NewBlockEventAggregation::new( - voter_window_size, - proposer_window_size, - reputation_window_from_stale_end, - ), - } - } -} - -impl ReputationHeuristic for ProposerAndVoterHeuristic { - fn get_weights( - &self, - epoch: u64, - epoch_to_candidates: &HashMap>, - history: &[NewBlockEvent], - ) -> Vec { - assert!(epoch_to_candidates.contains_key(&epoch)); - - let (votes, proposals, failed_proposals) = - self.aggregation - .get_aggregated_metrics(epoch_to_candidates, history, &self.author); - - epoch_to_candidates[&epoch] - .iter() - .map(|author| { - let cur_votes = *votes.get(author).unwrap_or(&0); - let cur_proposals = *proposals.get(author).unwrap_or(&0); - let cur_failed_proposals = *failed_proposals.get(author).unwrap_or(&0); - - if cur_failed_proposals * 100 - > (cur_proposals + cur_failed_proposals) * self.failure_threshold_percent - { - self.failed_weight - } else if cur_proposals > 0 || cur_votes > 0 { - self.active_weight - } else { - self.inactive_weight - } - }) - .collect() - } -} - -/// Committed history based proposer election implementation that could help bias towards -/// successful leaders to help improve performance. -pub struct LeaderReputation { - epoch: u64, - epoch_to_proposers: HashMap>, - voting_powers: Vec, - backend: Arc, - heuristic: Box, - exclude_round: u64, - use_root_hash: bool, - window_for_chain_health: usize, -} - -impl LeaderReputation { - pub fn new( - epoch: u64, - epoch_to_proposers: HashMap>, - voting_powers: Vec, - backend: Arc, - heuristic: Box, - exclude_round: u64, - use_root_hash: bool, - window_for_chain_health: usize, - ) -> Self { - assert!(epoch_to_proposers.contains_key(&epoch)); - assert_eq!(epoch_to_proposers[&epoch].len(), voting_powers.len()); - - Self { - epoch, - epoch_to_proposers, - voting_powers, - backend, - heuristic, - exclude_round, - use_root_hash, - window_for_chain_health, - } - } - - // Compute chain health metrics, and - // - return participating voting power percentage for the window_for_chain_health - // - update metric counters for different windows - fn compute_chain_health_and_add_metrics( - &self, - history: &[NewBlockEvent], - round: Round, - ) -> VotingPowerRatio { - let candidates = self.epoch_to_proposers.get(&self.epoch).unwrap(); - // use f64 counter, as total voting power is u128 - let total_voting_power = self.voting_powers.iter().map(|v| *v as f64).sum(); - CHAIN_HEALTH_TOTAL_VOTING_POWER.set(total_voting_power); - CHAIN_HEALTH_TOTAL_NUM_VALIDATORS.set(candidates.len() as i64); - - let mut result = None; - - for (counter_index, participants_window_size) in - CHAIN_HEALTH_WINDOW_SIZES.iter().enumerate() - { - let chosen = self.window_for_chain_health == *participants_window_size; - let sample_fraction = participants_window_size / 10; - // Sample longer durations - if chosen || sample_fraction <= 1 || (round % sample_fraction as u64) == 1 { - let participants: HashSet<_> = NewBlockEventAggregation::count_votes_custom( - &self.epoch_to_proposers, - history, - *participants_window_size, - false, - ) - .into_keys() - .chain( - NewBlockEventAggregation::count_proposals_custom( - &self.epoch_to_proposers, - history, - *participants_window_size, - false, - ) - .into_keys(), - ) - .collect(); - - let participating_voting_power = candidates - .iter() - .zip(self.voting_powers.iter()) - .filter(|(c, _vp)| participants.contains(c)) - .map(|(_c, vp)| *vp as f64) - .sum(); - - if counter_index == max(CHAIN_HEALTH_WINDOW_SIZES.len() - 2, 0) { - // Only emit this for one window value. Currently defaults to 100 - candidates.iter().for_each(|author| { - if participants.contains(author) { - CONSENSUS_PARTICIPATION_STATUS - .with_label_values(&[&author.to_hex()]) - .set(1_i64) - } else { - CONSENSUS_PARTICIPATION_STATUS - .with_label_values(&[&author.to_hex()]) - .set(0_i64) - } - }); - } - - CHAIN_HEALTH_PARTICIPATING_VOTING_POWER[counter_index] - .set(participating_voting_power); - CHAIN_HEALTH_PARTICIPATING_NUM_VALIDATORS[counter_index] - .set(participants.len() as i64); - - if chosen { - // do not treat chain as unhealthy, if chain just started, and we don't have enough history to decide. - let voting_power_participation_ratio: VotingPowerRatio = - if history.len() < *participants_window_size && self.epoch <= 2 { - 1.0 - } else if total_voting_power >= 1.0 { - participating_voting_power / total_voting_power - } else { - error!( - "Total voting power is {}, should never happen", - total_voting_power - ); - 1.0 - }; - CHAIN_HEALTH_REPUTATION_PARTICIPATING_VOTING_POWER_FRACTION - .set(voting_power_participation_ratio); - result = Some(voting_power_participation_ratio); - } - } - } - - result.unwrap_or_else(|| { - panic!( - "asked window size {} not found in predefined window sizes: {:?}", - self.window_for_chain_health, CHAIN_HEALTH_WINDOW_SIZES - ) - }) - } -} - -impl ProposerElection for LeaderReputation { - fn get_valid_proposer_and_voting_power_participation_ratio( - &self, - round: Round, - ) -> (Author, VotingPowerRatio) { - let target_round = round.saturating_sub(self.exclude_round); - let (sliding_window, root_hash) = self.backend.get_block_metadata(self.epoch, target_round); - let voting_power_participation_ratio = - self.compute_chain_health_and_add_metrics(&sliding_window, round); - let mut weights = - self.heuristic - .get_weights(self.epoch, &self.epoch_to_proposers, &sliding_window); - let proposers = &self.epoch_to_proposers[&self.epoch]; - assert_eq!(weights.len(), proposers.len()); - - // Multiply weights by voting power: - let stake_weights: Vec = weights - .iter_mut() - .enumerate() - .map(|(i, w)| *w as u128 * self.voting_powers[i] as u128) - .collect(); - - let state = if self.use_root_hash { - [ - root_hash.to_vec(), - self.epoch.to_le_bytes().to_vec(), - round.to_le_bytes().to_vec(), - ] - .concat() - } else { - [ - self.epoch.to_le_bytes().to_vec(), - round.to_le_bytes().to_vec(), - ] - .concat() - }; - - let chosen_index = choose_index(stake_weights, state); - (proposers[chosen_index], voting_power_participation_ratio) - } - - fn get_valid_proposer(&self, round: Round) -> Author { - self.get_valid_proposer_and_voting_power_participation_ratio(round) - .0 - } - - fn get_voting_power_participation_ratio(&self, round: Round) -> VotingPowerRatio { - self.get_valid_proposer_and_voting_power_participation_ratio(round) - .1 - } -} - -pub(crate) fn extract_epoch_to_proposers_impl( - next_epoch_states_and_cur_epoch_rounds: &[(&EpochState, u64)], - epoch: u64, - proposers: &[Author], - needed_rounds: u64, -) -> Result>> { - let last_index = next_epoch_states_and_cur_epoch_rounds.len() - 1; - let mut num_rounds = 0; - let mut result = HashMap::new(); - for (index, (next_epoch_state, cur_epoch_rounds)) in next_epoch_states_and_cur_epoch_rounds - .iter() - .enumerate() - .rev() - { - let next_epoch_proposers = next_epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect::>(); - if index == last_index { - ensure!( - epoch == next_epoch_state.epoch, - "fetched epoch_ending ledger_infos are for a wrong epoch {} vs {}", - epoch, - next_epoch_state.epoch - ); - ensure!( - proposers == next_epoch_proposers, - "proposers from state and fetched epoch_ending ledger_infos are missaligned" - ); - } - result.insert(next_epoch_state.epoch, next_epoch_proposers); - - if num_rounds > needed_rounds { - break; - } - // LI contains validator set for next epoch (i.e. in next_epoch_state) - // so after adding the number of rounds in the epoch, we need to process the - // corresponding ValidatorSet on the previous ledger info, - // before checking if we are done. - num_rounds += cur_epoch_rounds; - } - - ensure!( - result.contains_key(&epoch), - "Current epoch ({}) not in fetched map ({:?})", - epoch, - result.keys().collect::>() - ); - Ok(result) -} - -pub fn extract_epoch_to_proposers( - proof: EpochChangeProof, - epoch: u64, - proposers: &[Author], - needed_rounds: u64, -) -> Result>> { - extract_epoch_to_proposers_impl( - &proof - .ledger_info_with_sigs - .iter() - .map::, _>(|ledger_info| { - let cur_epoch_rounds = ledger_info.ledger_info().round(); - let next_epoch_state = ledger_info - .ledger_info() - .next_epoch_state() - .ok_or_else(|| anyhow::anyhow!("no cur_epoch_state"))?; - Ok((next_epoch_state, cur_epoch_rounds)) - }) - .collect::, _>>()?, - epoch, - proposers, - needed_rounds, - ) -} diff --git a/consensus/src/liveness/leader_reputation_test.rs b/consensus/src/liveness/leader_reputation_test.rs deleted file mode 100644 index 89739eb208fa5..0000000000000 --- a/consensus/src/liveness/leader_reputation_test.rs +++ /dev/null @@ -1,763 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::leader_reputation::{ - extract_epoch_to_proposers_impl, AptosDBBackend, ProposerAndVoterHeuristic, -}; -use crate::liveness::{ - leader_reputation::{ - LeaderReputation, MetadataBackend, NewBlockEventAggregation, ReputationHeuristic, - }, - proposer_election::{choose_index, ProposerElection}, -}; -use aptos_bitvec::BitVec; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::{bls12381, HashValue}; -use aptos_infallible::Mutex; -use aptos_keygen::KeyGen; -use aptos_storage_interface::DbReader; -use aptos_types::{ - account_address::AccountAddress, - account_config::{new_block_event_key, NewBlockEvent}, - contract_event::{ContractEvent, EventWithVersion}, - epoch_state::EpochState, - transaction::Version, - validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier}, -}; -use claims::assert_err; -use itertools::Itertools; -use move_core_types::{language_storage::TypeTag, move_resource::MoveStructType}; -use num_traits::Pow; -use std::{collections::HashMap, sync::Arc}; - -/// #### NewBlockEventAggregation tests #### - -#[test] -fn test_aggregation_bitmap_to_voters() { - let validators: Vec<_> = (0..4).map(|_| Author::random()).collect(); - let bitmap = vec![true, true, false, true]; - - if let Ok(voters) = NewBlockEventAggregation::bitvec_to_voters(&validators, &bitmap.into()) { - assert_eq!(&validators[0], voters[0]); - assert_eq!(&validators[1], voters[1]); - assert_eq!(&validators[3], voters[2]); - } else { - unreachable!(); - } -} - -#[test] -fn test_aggregation_bitmap_to_voters_mismatched_lengths() { - let validators: Vec<_> = (0..8) // size of 8 with one u8 in bitvec - .map(|_| Author::random()) - .collect(); - let bitmap_too_long = vec![true; 9]; // 2 bytes in bitvec - assert!( - NewBlockEventAggregation::bitvec_to_voters(&validators, &bitmap_too_long.into()).is_err() - ); - let bitmap_too_short: Vec = vec![]; // 0 bytes in bitvec - assert!( - NewBlockEventAggregation::bitvec_to_voters(&validators, &bitmap_too_short.into()).is_err() - ); -} - -#[test] -fn test_aggregation_indices_to_authors() { - let validators: Vec<_> = (0..4).map(|_| Author::random()).collect(); - let indices = vec![2u64, 2, 0, 3]; - - if let Ok(authors) = NewBlockEventAggregation::indices_to_validators(&validators, &indices) { - assert_eq!(&validators[2], authors[0]); - assert_eq!(&validators[2], authors[1]); - assert_eq!(&validators[0], authors[2]); - assert_eq!(&validators[3], authors[3]); - } else { - unreachable!(); - } -} - -#[test] -fn test_aggregation_indices_to_authors_out_of_index() { - let validators: Vec<_> = (0..4).map(|_| Author::random()).collect(); - let indices = vec![0, 0, 4, 0]; - assert!(NewBlockEventAggregation::indices_to_validators(&validators, &indices).is_err()); -} - -struct Example1 { - validators0: Vec, - validators1: Vec, - aptos_db: Arc, - backend: AptosDBBackend, -} - -impl Example1 { - fn new(window_size: usize) -> Self { - let mut sorted_validators: Vec = (0..5).map(|_| Author::random()).collect(); - sorted_validators.sort(); - // same first 3 validators, different 4th validator (index 3). - let mut validators0: Vec = sorted_validators[..3].to_vec(); - validators0.push(sorted_validators[3]); - let mut validators1: Vec = validators0[..3].to_vec(); - validators1.push(sorted_validators[4]); - - let aptos_db = Arc::new(MockDbReader::new()); - let backend = AptosDBBackend::new(window_size, 0, aptos_db.clone()); - - Self { - validators0, - validators1, - aptos_db, - backend, - } - } - - fn history(&self) -> Vec { - self.backend.get_block_metadata(5, 0).0 - } - - fn step1(&mut self) { - self.aptos_db - .add_event_with_data(self.validators0[0], vec![1, 2], vec![3]); - self.aptos_db - .add_event_with_data(self.validators0[0], vec![1, 2], vec![]); - self.aptos_db - .add_event_with_data(self.validators0[1], vec![0, 2], vec![2]); - self.aptos_db - .add_event_with_data(self.validators0[2], vec![0, 1], vec![]); - } - - fn step2(&mut self) { - self.aptos_db - .add_event_with_data(self.validators0[3], vec![0, 1], vec![1]); - self.aptos_db - .add_event_with_data(self.validators0[3], vec![0, 1], vec![1]); - } - - fn step3(&mut self) { - self.aptos_db.new_epoch(); - self.aptos_db - .add_event_with_data(self.validators1[3], vec![0, 1], vec![0]); - } -} - -#[test] -fn test_aggregation_counting() { - let mut example1 = Example1::new(5); - let validators0 = example1.validators0.clone(); - let epoch_to_validators = HashMap::from([(0u64, validators0.clone())]); - let aggregation = NewBlockEventAggregation::new(2, 5, false); - - example1.step1(); - - assert_eq!( - aggregation.count_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([ - (validators0[0], 2), - (validators0[1], 1), - (validators0[2], 1), - ]) - ); - assert_eq!( - aggregation.count_failed_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([(validators0[2], 1), (validators0[3], 1),]) - ); - assert_eq!( - aggregation.count_votes(&epoch_to_validators, &example1.history()), - HashMap::from([ - (validators0[0], 2), - (validators0[1], 1), - (validators0[2], 1), - ]) - ); - - example1.step2(); - - assert_eq!( - aggregation.count_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([ - (validators0[0], 1), - (validators0[1], 1), - (validators0[2], 1), - (validators0[3], 2), - ]) - ); - assert_eq!( - aggregation.count_failed_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([(validators0[2], 1), (validators0[1], 2),]) - ); - assert_eq!( - aggregation.count_votes(&epoch_to_validators, &example1.history()), - HashMap::from([(validators0[0], 2), (validators0[1], 2),]) - ); - - example1.step3(); - - let validators1 = example1.validators1.clone(); - let epoch_to_validators = HashMap::from([(1u64, validators1.clone())]); - - assert_eq!( - aggregation.count_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([(validators1[3], 1),]) - ); - assert_eq!( - aggregation.count_failed_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([(validators1[0], 1),]) - ); - assert_eq!( - aggregation.count_votes(&epoch_to_validators, &example1.history()), - HashMap::from([(validators1[0], 1), (validators1[1], 1),]) - ); - - let epoch_to_validators = - HashMap::from([(0u64, validators0.clone()), (1u64, validators1.clone())]); - - assert_ne!(validators0[3], validators1[3]); - - assert_eq!( - aggregation.count_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([ - (validators1[1], 1), - (validators1[2], 1), - (validators0[3], 2), - (validators1[3], 1), - ]) - ); - assert_eq!( - aggregation.count_failed_proposals(&epoch_to_validators, &example1.history()), - HashMap::from([ - (validators1[0], 1), - (validators1[2], 1), - (validators1[1], 2), - ]) - ); - assert_eq!( - aggregation.count_votes(&epoch_to_validators, &example1.history()), - HashMap::from([(validators1[0], 2), (validators1[1], 2),]) - ); -} - -/// #### - -#[test] -fn test_proposer_and_voter_heuristic() { - let mut example1 = Example1::new(5); - let validators0 = example1.validators0.clone(); - let epoch_to_validators0 = HashMap::from([(0u64, validators0.clone())]); - let heuristic = - ProposerAndVoterHeuristic::new(example1.validators0[0], 100, 10, 1, 49, 2, 5, false); - - example1.step1(); - assert_eq!( - heuristic.get_weights(0, &epoch_to_validators0, &example1.history()), - vec![100, 100, 1, 1] - ); - - example1.step2(); - assert_eq!( - heuristic.get_weights(0, &epoch_to_validators0, &example1.history()), - vec![100, 1, 1, 100] - ); - - example1.step3(); - - let validators1 = example1.validators1.clone(); - let epoch_to_validators1 = HashMap::from([(1u64, validators1.clone())]); - assert_eq!( - heuristic.get_weights(1, &epoch_to_validators1, &example1.history()), - vec![1, 100, 10, 100] - ); - - let epoch_to_validators01 = HashMap::from([(0u64, validators0), (1u64, validators1)]); - assert_eq!( - heuristic.get_weights(1, &epoch_to_validators01, &example1.history()), - vec![1, 1, 1, 100] - ); -} - -/// #### LeaderReputation test #### - -#[test] -fn test_api_v1() { - test_api(false); -} - -#[test] -fn test_api_v2() { - test_api(true); -} - -fn test_api(use_root_hash: bool) { - let active_weight: u64 = 9; - let inactive_weight: u64 = 1; - let proposers: Vec = - (0..5).map(|_| AccountAddress::random()).sorted().collect(); - - // 5 * base_stake just below u64::MAX - let base_stake: u64 = 3_000_000_000_000_000_000; - - let voting_powers: Vec = (0..5).map(|i| base_stake * (i + 1)).collect(); - - // first metadata is ignored because of window size 1 - let expected_weights = vec![ - active_weight as u128 * base_stake as u128, - inactive_weight as u128 * (2 * base_stake) as u128, - inactive_weight as u128 * (3 * base_stake) as u128, - active_weight as u128 * (4 * base_stake) as u128, - inactive_weight as u128 * (5 * base_stake) as u128, - ]; - let total_weights: u128 = expected_weights.iter().sum(); - - let mut selected = [0; 5].to_vec(); - let aptos_db = Arc::new(MockDbReader::new()); - - for epoch in 1..1000 { - aptos_db.new_epoch(); - assert_eq!( - (epoch, 1), - aptos_db.add_event_with_data(proposers[0], vec![1, 2], vec![]) - ); - assert_eq!( - (epoch, 2), - aptos_db.add_event_with_data(proposers[0], vec![3], vec![]) - ); - let backend = Arc::new(AptosDBBackend::new(1, 4, aptos_db.clone())); - let leader_reputation = LeaderReputation::new( - epoch, - HashMap::from([(epoch, proposers.clone())]), - voting_powers.clone(), - backend, - Box::new(ProposerAndVoterHeuristic::new( - proposers[0], - active_weight, - inactive_weight, - 0, - 10, - proposers.len(), - proposers.len(), - false, - )), - 4, - use_root_hash, - 30, - ); - let round = 42u64; - - let state = if use_root_hash { - [ - aptos_db.get_accumulator_root_hash(0).unwrap().to_vec(), - epoch.to_le_bytes().to_vec(), - round.to_le_bytes().to_vec(), - ] - .concat() - } else { - [epoch.to_le_bytes().to_vec(), round.to_le_bytes().to_vec()].concat() - }; - - let expected_index = choose_index(expected_weights.clone(), state); - selected[expected_index] += 1; - let unexpected_index = (expected_index + 1) % proposers.len(); - let output = leader_reputation.get_valid_proposer(round); - assert_eq!(output, proposers[expected_index]); - assert!(leader_reputation.is_valid_proposer(proposers[expected_index], round)); - assert!(!leader_reputation.is_valid_proposer(proposers[unexpected_index], round)); - } - - for i in 0..5 { - let p = expected_weights[i] as f32 / total_weights as f32; - let expected = (1000.0 * p) as i32; - let std_dev = (1000.0 * p * (1.0 - p)).pow(0.5); - // We've run the election enough times, to expect occurances to be close to the average - // (each test is independent, as seed is different for every cycle) - // We check that difference from average is below 3 standard deviations, - // which will approximately be true in 99.7% of cases. - // (as we can approximate each selection with normal distribution) - // - // Test is deterministic, as all seeds are, so if it passes once, shouldn't ever fail. - // Meaning, wheen we change the selection formula, there is 0.3% chance this test will fail - // unnecessarily. - assert!( - expected.abs_diff(selected[i]) as f32 <= 3.0 * std_dev, - "{}: expected={} selected={}, std_dev: {}", - i, - expected, - selected[i], - std_dev - ); - } -} - -struct MockDbReader { - events: Mutex>, - random_address: Author, - last_timestamp: Mutex, - idx: Mutex, - epoch: Mutex, - round: Mutex, - to_add_event_after_call: Mutex>, - - fetched: Mutex, -} - -impl MockDbReader { - pub fn new() -> MockDbReader { - Self { - events: Mutex::new(vec![]), - random_address: Author::random(), - last_timestamp: Mutex::new(100000), - idx: Mutex::new(0), - epoch: Mutex::new(0), - round: Mutex::new(0), - to_add_event_after_call: Mutex::new(None), - fetched: Mutex::new(0), - } - } - - pub fn add_event(&self, expected_epoch: u64, expected_round: Round) { - let (epoch, round) = self.add_event_with_data(self.random_address, vec![0], vec![]); - assert_eq!((epoch, round), (expected_epoch, expected_round)) - } - - pub fn add_event_with_data( - &self, - proposer: Author, - votes: Vec, - failed_proposers: Vec, - ) -> (u64, u64) { - let mut idx = self.idx.lock(); - *idx += 1; - - let mut round = self.round.lock(); - *round += 1 + failed_proposers.len() as u64; - - let epoch = self.epoch.lock(); - - let mut votes_bitvec = BitVec::with_num_bits(1); - for vote in votes { - votes_bitvec.set(vote); - } - - self.events.lock().push(EventWithVersion::new( - *idx, - ContractEvent::new_v1( - new_block_event_key(), - *idx, - TypeTag::Struct(Box::new(NewBlockEvent::struct_tag())), - bcs::to_bytes(&NewBlockEvent::new( - AccountAddress::random(), - *epoch, - *round, - *round, - votes_bitvec.into(), - proposer, - failed_proposers, - *self.last_timestamp.lock(), - )) - .unwrap(), - ), - )); - *self.last_timestamp.lock() += 100; - (*epoch, *round) - } - - pub fn new_epoch(&self) { - *self.epoch.lock() += 1; - *self.round.lock() = 0; - } - - pub fn skip_rounds(&self, to_skip: u64) { - *self.round.lock() += to_skip; - } - - pub fn add_another_transaction(&self) { - *self.idx.lock() += 1; - } - - pub fn add_event_after_call(&self, epoch: u64, round: Round) { - *self.to_add_event_after_call.lock() = Some((epoch, round)); - } - - fn fetched(&self) -> usize { - *self.fetched.lock() - } -} - -impl DbReader for MockDbReader { - fn get_latest_block_events( - &self, - num_events: usize, - ) -> aptos_storage_interface::Result> { - *self.fetched.lock() += 1; - let events = self.events.lock(); - // println!("Events {:?}", *events); - Ok(events - .iter() - .skip(events.len().saturating_sub(num_events)) - .rev() - .cloned() - .collect()) - } - - /// Returns the latest version, error on on non-bootstrapped DB. - fn get_latest_version(&self) -> aptos_storage_interface::Result { - let version = *self.idx.lock(); - let mut to_add = self.to_add_event_after_call.lock(); - if let Some((epoch, round)) = *to_add { - self.add_event(epoch, round); - *to_add = None; - } - Ok(version) - } - - /// Gets the transaction accumulator root hash at specified version. - /// Caller must guarantee the version is not greater than the latest version. - fn get_accumulator_root_hash( - &self, - _version: Version, - ) -> aptos_storage_interface::Result { - Ok(HashValue::zero()) - } -} - -#[test] -fn backend_wrapper_test() { - let aptos_db = Arc::new(MockDbReader::new()); - let backend = AptosDBBackend::new(3, 3, aptos_db.clone()); - - aptos_db.add_event(0, 1); - aptos_db.new_epoch(); - aptos_db.skip_rounds(1); - for i in 2..6 { - aptos_db.add_event(1, i); - } - let mut fetch_count = 0; - - let mut assert_history = |round, expected_history: Vec, to_fetch| { - let history: Vec = backend - .get_block_metadata(1, round) - .0 - .iter() - .map(|e| e.round()) - .collect(); - assert_eq!(expected_history, history, "At round {}", round); - if to_fetch { - fetch_count += 1; - } - assert_eq!(fetch_count, aptos_db.fetched(), "At round {}", round); - }; - - assert_history(6, vec![5, 4, 3], true); - // while history doesn't change, no need to refetch, no matter the round - assert_history(5, vec![5, 4, 3], false); - assert_history(4, vec![4, 3, 2], false); - assert_history(3, vec![3, 2, 1], false); - assert_history(5, vec![5, 4, 3], false); - assert_history(6, vec![5, 4, 3], false); - - // as soon as history change, we fetch again - aptos_db.add_event(1, 6); - assert_history(6, vec![6, 5, 4], true); - aptos_db.add_event(1, 7); - assert_history(6, vec![6, 5, 4], false); - aptos_db.add_event(1, 8); - assert_history(6, vec![6, 5, 4], false); - - assert_history(9, vec![8, 7, 6], true); - aptos_db.skip_rounds(1); - aptos_db.add_event(1, 10); - // we need to refetch, as we don't know if round that arrived is for 9 or not. - assert_history(9, vec![8, 7, 6], true); - assert_history(9, vec![8, 7, 6], false); - aptos_db.add_event(1, 11); - // since we already saw round 10, and are asking for round 9, no need to fetch again. - assert_history(9, vec![8, 7, 6], false); - aptos_db.add_event(1, 12); - assert_history(9, vec![8, 7, 6], false); - - // last time we fetched, we saw 10, so we don't need to fetch for 10 - // but need to fetch for 11. - assert_history(10, vec![10, 8, 7], false); - assert_history(11, vec![11, 10, 8], true); - assert_history(12, vec![12, 11, 10], false); - - // since history include target round, unrelated transaction don't require refresh - aptos_db.add_another_transaction(); - assert_history(12, vec![12, 11, 10], false); - - // since history doesn't include target round, any unrelated transaction requires refresh - assert_history(13, vec![12, 11, 10], true); - aptos_db.add_another_transaction(); - assert_history(13, vec![12, 11, 10], true); - assert_history(13, vec![12, 11, 10], false); - aptos_db.add_another_transaction(); - assert_history(13, vec![12, 11, 10], true); - assert_history(13, vec![12, 11, 10], false); - - // check for race condition - aptos_db.add_another_transaction(); - aptos_db.add_event_after_call(1, 13); - // in the first we add event after latest_db_version is fetched, as a race. - // Second one should know that there is nothing new. - assert_history(14, vec![13, 12, 11], true); - assert_history(14, vec![13, 12, 11], false); -} - -#[test] -fn backend_test_cross_epoch() { - let aptos_db = Arc::new(MockDbReader::new()); - let backend = AptosDBBackend::new(3, 3, aptos_db.clone()); - - aptos_db.add_event(0, 1); - aptos_db.new_epoch(); - aptos_db.add_event(1, 1); - aptos_db.add_event(1, 2); - aptos_db.add_event(1, 3); - aptos_db.new_epoch(); - aptos_db.add_event(2, 1); - aptos_db.add_event(2, 2); - - let mut fetch_count = 0; - - let mut assert_history = |epoch, round, expected_history: Vec<(u64, Round)>, to_fetch| { - let history: Vec<(u64, Round)> = backend - .get_block_metadata(epoch, round) - .0 - .iter() - .map(|e| (e.epoch(), e.round())) - .collect(); - assert_eq!(expected_history, history, "At round {}", round); - if to_fetch { - fetch_count += 1; - } - assert_eq!(fetch_count, aptos_db.fetched(), "At round {}", round); - }; - - assert_history(2, 2, vec![(2, 2), (2, 1), (1, 3)], true); - assert_history(2, 1, vec![(2, 1), (1, 3), (1, 2)], false); - - aptos_db.new_epoch(); - aptos_db.add_event(3, 1); - - assert_history(3, 2, vec![(3, 1), (2, 2), (2, 1)], true); -} - -#[test] -fn test_extract_epoch_to_proposers_impl() { - fn create_epoch_state( - epoch: u64, - authors: &[Author], - public_key: &bls12381::PublicKey, - ) -> EpochState { - EpochState { - epoch, - verifier: ValidatorVerifier::new( - authors - .iter() - .map(|author| ValidatorConsensusInfo::new(*author, public_key.clone(), 1)) - .collect::>(), - ), - } - } - - let private_key = KeyGen::from_os_rng().generate_bls12381_private_key(); - let public_key = bls12381::PublicKey::from(&private_key); - let authors: Vec = (0..7).map(|_| AccountAddress::random()).sorted().collect(); - - let epoch_states = (0..7) - .map(|i| create_epoch_state(i as u64, &[authors[i]], &public_key)) - .collect::>(); - - // last EpochState needs to be for current epoch: - assert_err!(extract_epoch_to_proposers_impl( - &[(&epoch_states[1], 100u64)], - 2, - &[authors[2]], - 1000 - )); - assert_err!(extract_epoch_to_proposers_impl( - &[(&epoch_states[2], 100u64), (&epoch_states[3], 100u64)], - 2, - &[authors[2]], - 1000 - )); - - assert_eq!( - HashMap::from([(2, vec![authors[2]])]), - extract_epoch_to_proposers_impl(&[(&epoch_states[2], 100u64)], 2, &[authors[2]], 1000) - .unwrap() - ); - assert_eq!( - HashMap::from([(2, vec![authors[2]])]), - extract_epoch_to_proposers_impl(&[(&epoch_states[2], 10000u64)], 2, &[authors[2]], 1000) - .unwrap() - ); - - assert_eq!( - HashMap::from([(2, vec![authors[2]]), (3, vec![authors[3]])]), - extract_epoch_to_proposers_impl( - &[(&epoch_states[2], 100u64), (&epoch_states[3], 10000u64)], - 3, - &[authors[3]], - 1000 - ) - .unwrap() - ); - assert_eq!( - HashMap::from([(2, vec![authors[2]]), (3, vec![authors[3]])]), - extract_epoch_to_proposers_impl( - &[ - (&epoch_states[1], 100u64), - (&epoch_states[2], 100u64), - (&epoch_states[3], 10000u64) - ], - 3, - &[authors[3]], - 1000 - ) - .unwrap() - ); - assert_eq!( - HashMap::from([ - (1, vec![authors[1]]), - (2, vec![authors[2]]), - (3, vec![authors[3]]), - (4, vec![authors[4]]), - (5, vec![authors[5]]) - ]), - extract_epoch_to_proposers_impl( - &[ - (&epoch_states[1], 1u64), - (&epoch_states[2], 1u64), - (&epoch_states[3], 1u64), - (&epoch_states[4], 1u64), - (&epoch_states[5], 1u64) - ], - 5, - &[authors[5]], - 1000 - ) - .unwrap() - ); - - assert_eq!( - HashMap::from([ - (2, vec![authors[2]]), - (3, vec![authors[3]]), - (4, vec![authors[4]]), - (5, vec![authors[5]]) - ]), - extract_epoch_to_proposers_impl( - &[ - (&epoch_states[1], 400u64), - (&epoch_states[2], 400u64), - (&epoch_states[3], 400u64), - (&epoch_states[4], 400u64), - (&epoch_states[5], 400u64) - ], - 5, - &[authors[5]], - 1000 - ) - .unwrap() - ); -} diff --git a/consensus/src/liveness/mod.rs b/consensus/src/liveness/mod.rs deleted file mode 100644 index f7e8f11bceb05..0000000000000 --- a/consensus/src/liveness/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -pub(crate) mod cached_proposer_election; -pub(crate) mod leader_reputation; -pub(crate) mod proposal_generator; -pub(crate) mod proposer_election; -pub(crate) mod rotating_proposer_election; -pub(crate) mod round_proposer_election; -pub(crate) mod round_state; -pub(crate) mod unequivocal_proposer_election; - -#[cfg(test)] -mod cached_proposer_election_test; -#[cfg(test)] -mod leader_reputation_test; -#[cfg(test)] -mod rotating_proposer_test; -#[cfg(test)] -mod round_proposer_test; -#[cfg(test)] -mod round_state_test; -#[cfg(test)] -mod unequivocal_proposer_election_test; diff --git a/consensus/src/liveness/proposal_generator.rs b/consensus/src/liveness/proposal_generator.rs deleted file mode 100644 index f2bed3abb1b69..0000000000000 --- a/consensus/src/liveness/proposal_generator.rs +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - proposer_election::ProposerElection, unequivocal_proposer_election::UnequivocalProposerElection, -}; -use crate::{ - block_storage::BlockReader, - counters::{ - CHAIN_HEALTH_BACKOFF_TRIGGERED, PIPELINE_BACKPRESSURE_ON_PROPOSAL_TRIGGERED, - PROPOSER_DELAY_PROPOSAL, PROPOSER_PENDING_BLOCKS_COUNT, - PROPOSER_PENDING_BLOCKS_FILL_FRACTION, - }, - payload_client::PayloadClient, - util::time_service::TimeService, -}; -use anyhow::{bail, ensure, format_err, Context}; -use aptos_config::config::{ChainHealthBackoffValues, PipelineBackpressureValues}; -use aptos_consensus_types::{ - block::Block, - block_data::BlockData, - common::{Author, Payload, PayloadFilter, Round}, - quorum_cert::QuorumCert, -}; -use aptos_crypto::{hash::CryptoHash, HashValue}; -use aptos_logger::{error, sample, sample::SampleRate, warn}; -use aptos_types::{on_chain_config::ValidatorTxnConfig, validator_txn::ValidatorTransaction}; -use aptos_validator_transaction_pool as vtxn_pool; -use futures::future::BoxFuture; -use std::{ - collections::{BTreeMap, HashSet}, - sync::Arc, - time::Duration, -}; - -#[cfg(test)] -#[path = "proposal_generator_test.rs"] -mod proposal_generator_test; - -#[derive(Clone)] -pub struct ChainHealthBackoffConfig { - backoffs: BTreeMap, -} - -impl ChainHealthBackoffConfig { - pub fn new(backoffs: Vec) -> Self { - let original_len = backoffs.len(); - let backoffs = backoffs - .into_iter() - .map(|v| (v.backoff_if_below_participating_voting_power_percentage, v)) - .collect::>(); - assert_eq!(original_len, backoffs.len()); - Self { backoffs } - } - - #[allow(dead_code)] - pub fn new_no_backoff() -> Self { - Self { - backoffs: BTreeMap::new(), - } - } - - pub fn get_backoff(&self, voting_power_ratio: f64) -> Option<&ChainHealthBackoffValues> { - if self.backoffs.is_empty() { - return None; - } - - if voting_power_ratio < 2.0 / 3.0 { - error!("Voting power ratio {} is below 2f + 1", voting_power_ratio); - } - let voting_power_percentage = (voting_power_ratio * 100.0).floor() as usize; - if voting_power_percentage > 100 { - error!( - "Voting power participation percentatge {} is > 100, before rounding {}", - voting_power_percentage, voting_power_ratio - ); - } - self.backoffs - .range(voting_power_percentage..) - .next() - .map(|(_, v)| { - sample!( - SampleRate::Duration(Duration::from_secs(10)), - warn!( - "Using chain health backoff config for {} voting power percentage: {:?}", - voting_power_percentage, v - ) - ); - v - }) - } -} - -#[derive(Clone)] -pub struct PipelineBackpressureConfig { - backoffs: BTreeMap, -} - -impl PipelineBackpressureConfig { - pub fn new(backoffs: Vec) -> Self { - let original_len = backoffs.len(); - let backoffs = backoffs - .into_iter() - .map(|v| (v.back_pressure_pipeline_latency_limit_ms, v)) - .collect::>(); - assert_eq!(original_len, backoffs.len()); - Self { backoffs } - } - - #[allow(dead_code)] - pub fn new_no_backoff() -> Self { - Self { - backoffs: BTreeMap::new(), - } - } - - pub fn get_backoff( - &self, - pipeline_pending_latency: Duration, - ) -> Option<&PipelineBackpressureValues> { - if self.backoffs.is_empty() { - return None; - } - - self.backoffs - .range(..(pipeline_pending_latency.as_millis() as u64)) - .last() - .map(|(_, v)| { - sample!( - SampleRate::Duration(Duration::from_secs(10)), - warn!( - "Using consensus backpressure config for {}ms pending duration: {:?}", - pipeline_pending_latency.as_millis(), - v - ) - ); - v - }) - } -} - -/// ProposalGenerator is responsible for generating the proposed block on demand: it's typically -/// used by a validator that believes it's a valid candidate for serving as a proposer at a given -/// round. -/// ProposalGenerator is the one choosing the branch to extend: -/// - round is given by the caller (typically determined by RoundState). -/// The transactions for the proposed block are delivered by PayloadClient. -/// -/// PayloadClient should be aware of the pending transactions in the branch that it is extending, -/// such that it will filter them out to avoid transaction duplication. -pub struct ProposalGenerator { - // The account address of this validator - author: Author, - // Block store is queried both for finding the branch to extend and for generating the - // proposed block. - block_store: Arc, - // ProofOfStore manager is delivering the ProofOfStores. - payload_client: Arc, - // Transaction manager is delivering the transactions. - // Time service to generate block timestamps - time_service: Arc, - // Max time for preparation of the proposal - quorum_store_poll_time: Duration, - // Max number of transactions to be added to a proposed block. - max_block_txns: u64, - // Max number of bytes to be added to a proposed block. - max_block_bytes: u64, - // Max number of inline transactions to be added to a proposed block. - max_inline_txns: u64, - // Max number of inline bytes to be added to a proposed block. - max_inline_bytes: u64, - // Max number of failed authors to be added to a proposed block. - max_failed_authors_to_store: usize, - - pipeline_backpressure_config: PipelineBackpressureConfig, - chain_health_backoff_config: ChainHealthBackoffConfig, - - // Last round that a proposal was generated - last_round_generated: Round, - quorum_store_enabled: bool, - vtxn_config: ValidatorTxnConfig, - - allow_batches_without_pos_in_proposal: bool, -} - -impl ProposalGenerator { - #[allow(clippy::too_many_arguments)] - pub fn new( - author: Author, - block_store: Arc, - payload_client: Arc, - time_service: Arc, - quorum_store_poll_time: Duration, - max_block_txns: u64, - max_block_bytes: u64, - max_inline_txns: u64, - max_inline_bytes: u64, - max_failed_authors_to_store: usize, - pipeline_backpressure_config: PipelineBackpressureConfig, - chain_health_backoff_config: ChainHealthBackoffConfig, - quorum_store_enabled: bool, - vtxn_config: ValidatorTxnConfig, - allow_batches_without_pos_in_proposal: bool, - ) -> Self { - Self { - author, - block_store, - payload_client, - time_service, - quorum_store_poll_time, - max_block_txns, - max_block_bytes, - max_inline_txns, - max_inline_bytes, - max_failed_authors_to_store, - pipeline_backpressure_config, - chain_health_backoff_config, - last_round_generated: 0, - quorum_store_enabled, - vtxn_config, - allow_batches_without_pos_in_proposal, - } - } - - pub fn author(&self) -> Author { - self.author - } - - /// Creates a NIL block proposal extending the highest certified block from the block store. - pub fn generate_nil_block( - &self, - round: Round, - proposer_election: &mut UnequivocalProposerElection, - ) -> anyhow::Result { - let hqc = self.ensure_highest_quorum_cert(round)?; - let quorum_cert = hqc.as_ref().clone(); - let failed_authors = self.compute_failed_authors( - round, // to include current round, as that is what failed - quorum_cert.certified_block().round(), - true, - proposer_election, - ); - Ok(Block::new_nil(round, quorum_cert, failed_authors)) - } - - /// The function generates a new proposal block: the returned future is fulfilled when the - /// payload is delivered by the PayloadClient implementation. At most one proposal can be - /// generated per round (no proposal equivocation allowed). - /// Errors returned by the PayloadClient implementation are propagated to the caller. - /// The logic for choosing the branch to extend is as follows: - /// 1. The function gets the highest head of a one-chain from block tree. - /// The new proposal must extend hqc to ensure optimistic responsiveness. - /// 2. The round is provided by the caller. - /// 3. In case a given round is not greater than the calculated parent, return an OldRound - /// error. - pub async fn generate_proposal( - &mut self, - round: Round, - proposer_election: &mut UnequivocalProposerElection, - wait_callback: BoxFuture<'static, ()>, - ) -> anyhow::Result { - if self.last_round_generated < round { - self.last_round_generated = round; - } else { - bail!("Already proposed in the round {}", round); - } - - let hqc = self.ensure_highest_quorum_cert(round)?; - - let (validator_txns, payload, timestamp) = if hqc.certified_block().has_reconfiguration() { - // Reconfiguration rule - we propose empty blocks with parents' timestamp - // after reconfiguration until it's committed - ( - vec![], - Payload::empty( - self.quorum_store_enabled, - self.allow_batches_without_pos_in_proposal, - ), - hqc.certified_block().timestamp_usecs(), - ) - } else { - // One needs to hold the blocks with the references to the payloads while get_block is - // being executed: pending blocks vector keeps all the pending ancestors of the extended branch. - let mut pending_blocks = self - .block_store - .path_from_commit_root(hqc.certified_block().id()) - .ok_or_else(|| format_err!("HQC {} already pruned", hqc.certified_block().id()))?; - // Avoid txn manager long poll if the root block has txns, so that the leader can - // deliver the commit proof to others without delay. - pending_blocks.push(self.block_store.commit_root()); - - // Exclude all the pending transactions: these are all the ancestors of - // parent (including) up to the root (including). - let exclude_payload: Vec<_> = pending_blocks - .iter() - .flat_map(|block| block.payload()) - .collect(); - let payload_filter = PayloadFilter::from(&exclude_payload); - - let pending_ordering = self - .block_store - .path_from_ordered_root(hqc.certified_block().id()) - .ok_or_else(|| format_err!("HQC {} already pruned", hqc.certified_block().id()))? - .iter() - .any(|block| !block.payload().map_or(true, |txns| txns.is_empty())); - - // All proposed blocks in a branch are guaranteed to have increasing timestamps - // since their predecessor block will not be added to the BlockStore until - // the local time exceeds it. - let timestamp = self.time_service.get_current_timestamp(); - - let voting_power_ratio = proposer_election.get_voting_power_participation_ratio(round); - - let (max_block_txns, max_block_bytes, max_txns_from_block_to_execute, proposal_delay) = - self.calculate_max_block_sizes(voting_power_ratio, timestamp, round) - .await; - - PROPOSER_DELAY_PROPOSAL.set(proposal_delay.as_secs_f64()); - if !proposal_delay.is_zero() { - tokio::time::sleep(proposal_delay).await; - } - - let max_pending_block_len = pending_blocks - .iter() - .map(|block| block.payload().map_or(0, |p| p.len())) - .max() - .unwrap_or(0); - let max_pending_block_bytes = pending_blocks - .iter() - .map(|block| block.payload().map_or(0, |p| p.size())) - .max() - .unwrap_or(0); - // Use non-backpressure reduced values for computing fill_fraction - let max_fill_fraction = (max_pending_block_len as f32 / self.max_block_txns as f32) - .max(max_pending_block_bytes as f32 / self.max_block_bytes as f32); - PROPOSER_PENDING_BLOCKS_COUNT.set(pending_blocks.len() as i64); - PROPOSER_PENDING_BLOCKS_FILL_FRACTION.set(max_fill_fraction as f64); - - let pending_validator_txn_hashes: HashSet = pending_blocks - .iter() - .filter_map(|block| block.validator_txns()) - .flatten() - .map(ValidatorTransaction::hash) - .collect(); - let validator_txn_filter = - vtxn_pool::TransactionFilter::PendingTxnHashSet(pending_validator_txn_hashes); - let (validator_txns, mut payload) = self - .payload_client - .pull_payload( - self.quorum_store_poll_time.saturating_sub(proposal_delay), - max_block_txns, - max_block_bytes, - // TODO: Set max_inline_txns and max_inline_bytes correctly - self.max_inline_txns, - self.max_inline_bytes, - validator_txn_filter, - payload_filter, - wait_callback, - pending_ordering, - pending_blocks.len(), - max_fill_fraction, - ) - .await - .context("Fail to retrieve payload")?; - - if !payload.is_direct() - && max_txns_from_block_to_execute.is_some() - && payload.len() > max_txns_from_block_to_execute.unwrap() - { - payload = payload.transform_to_quorum_store_v2(max_txns_from_block_to_execute); - } - (validator_txns, payload, timestamp.as_micros() as u64) - }; - - let quorum_cert = hqc.as_ref().clone(); - let failed_authors = self.compute_failed_authors( - round, - quorum_cert.certified_block().round(), - false, - proposer_election, - ); - - let block = if self.vtxn_config.enabled() { - BlockData::new_proposal_ext( - validator_txns, - payload, - self.author, - failed_authors, - round, - timestamp, - quorum_cert, - ) - } else { - BlockData::new_proposal( - payload, - self.author, - failed_authors, - round, - timestamp, - quorum_cert, - ) - }; - - Ok(block) - } - - async fn calculate_max_block_sizes( - &mut self, - voting_power_ratio: f64, - timestamp: Duration, - round: Round, - ) -> (u64, u64, Option, Duration) { - let mut values_max_block_txns = vec![self.max_block_txns]; - let mut values_max_block_bytes = vec![self.max_block_bytes]; - let mut values_proposal_delay = vec![Duration::ZERO]; - let mut max_txns_from_block_to_execute = None; - - let chain_health_backoff = self - .chain_health_backoff_config - .get_backoff(voting_power_ratio); - if let Some(value) = chain_health_backoff { - values_max_block_txns.push(value.max_sending_block_txns_override); - values_max_block_bytes.push(value.max_sending_block_bytes_override); - values_proposal_delay.push(Duration::from_millis(value.backoff_proposal_delay_ms)); - CHAIN_HEALTH_BACKOFF_TRIGGERED.observe(1.0); - } else { - CHAIN_HEALTH_BACKOFF_TRIGGERED.observe(0.0); - } - - let pipeline_backpressure = self - .pipeline_backpressure_config - .get_backoff(self.block_store.pipeline_pending_latency(timestamp)); - if let Some(value) = pipeline_backpressure { - values_max_block_txns.push(value.max_sending_block_txns_override); - values_max_block_bytes.push(value.max_sending_block_bytes_override); - max_txns_from_block_to_execute = value.max_txns_from_block_to_execute; - values_proposal_delay.push(Duration::from_millis(value.backpressure_proposal_delay_ms)); - PIPELINE_BACKPRESSURE_ON_PROPOSAL_TRIGGERED.observe(1.0); - } else { - PIPELINE_BACKPRESSURE_ON_PROPOSAL_TRIGGERED.observe(0.0); - }; - - let max_block_txns = values_max_block_txns.into_iter().min().unwrap(); - let max_block_bytes = values_max_block_bytes.into_iter().min().unwrap(); - let proposal_delay = values_proposal_delay.into_iter().max().unwrap(); - - if pipeline_backpressure.is_some() || chain_health_backoff.is_some() { - warn!( - "Generating proposal: reducing limits to {} txns and {} bytes, due to pipeline_backpressure: {}, chain health backoff: {}. Delaying sending proposal by {}ms. Round: {}", - max_block_txns, - max_block_bytes, - pipeline_backpressure.is_some(), - chain_health_backoff.is_some(), - proposal_delay.as_millis(), - round, - ); - } - ( - max_block_txns, - max_block_bytes, - max_txns_from_block_to_execute, - proposal_delay, - ) - } - - fn ensure_highest_quorum_cert(&self, round: Round) -> anyhow::Result> { - let hqc = self.block_store.highest_quorum_cert(); - ensure!( - hqc.certified_block().round() < round, - "Given round {} is lower than hqc round {}", - round, - hqc.certified_block().round() - ); - ensure!( - !hqc.ends_epoch(), - "The epoch has already ended,a proposal is not allowed to generated" - ); - - Ok(hqc) - } - - /// Compute the list of consecutive proposers from the - /// immediately preceeding rounds that didn't produce a successful block - pub fn compute_failed_authors( - &self, - round: Round, - previous_round: Round, - include_cur_round: bool, - proposer_election: &mut UnequivocalProposerElection, - ) -> Vec<(Round, Author)> { - let end_round = round + u64::from(include_cur_round); - let mut failed_authors = Vec::new(); - let start = std::cmp::max( - previous_round + 1, - end_round.saturating_sub(self.max_failed_authors_to_store as u64), - ); - for i in start..end_round { - failed_authors.push((i, proposer_election.get_valid_proposer(i))); - } - - failed_authors - } -} diff --git a/consensus/src/liveness/proposal_generator_test.rs b/consensus/src/liveness/proposal_generator_test.rs deleted file mode 100644 index ab4102b7a39f3..0000000000000 --- a/consensus/src/liveness/proposal_generator_test.rs +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::BlockReader, - liveness::{ - proposal_generator::{ - ChainHealthBackoffConfig, PipelineBackpressureConfig, ProposalGenerator, - }, - rotating_proposer_election::RotatingProposer, - unequivocal_proposer_election::UnequivocalProposerElection, - }, - test_utils::{build_empty_tree, MockPayloadManager, TreeInserter}, - util::mock_time_service::SimulatedTimeService, -}; -use aptos_consensus_types::{ - block::{block_test_utils::certificate_for_genesis, Block}, - common::Author, -}; -use aptos_types::{on_chain_config::ValidatorTxnConfig, validator_signer::ValidatorSigner}; -use futures::{future::BoxFuture, FutureExt}; -use std::{sync::Arc, time::Duration}; - -fn empty_callback() -> BoxFuture<'static, ()> { - async move {}.boxed() -} - -#[tokio::test] -async fn test_proposal_generation_empty_tree() { - let signer = ValidatorSigner::random(None); - let block_store = build_empty_tree(); - let mut proposal_generator = ProposalGenerator::new( - signer.author(), - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - Arc::new(SimulatedTimeService::new()), - Duration::ZERO, - 1, - 10, - 1, - 10, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - ValidatorTxnConfig::default_disabled(), - true, - ); - let mut proposer_election = - UnequivocalProposerElection::new(Arc::new(RotatingProposer::new(vec![signer.author()], 1))); - let genesis = block_store.ordered_root(); - - // Generate proposals for an empty tree. - let proposal_data = proposal_generator - .generate_proposal(1, &mut proposer_election, empty_callback()) - .await - .unwrap(); - let proposal = Block::new_proposal_from_block_data(proposal_data, &signer).unwrap(); - assert_eq!(proposal.parent_id(), genesis.id()); - assert_eq!(proposal.round(), 1); - assert_eq!(proposal.quorum_cert().certified_block().id(), genesis.id()); - assert_eq!(proposal.block_data().failed_authors().unwrap().len(), 0); - - // Duplicate proposals on the same round are not allowed - let proposal_err = proposal_generator - .generate_proposal(1, &mut proposer_election, empty_callback()) - .await - .err(); - assert!(proposal_err.is_some()); -} - -#[tokio::test] -async fn test_proposal_generation_parent() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let mut proposal_generator = ProposalGenerator::new( - inserter.signer().author(), - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - Arc::new(SimulatedTimeService::new()), - Duration::ZERO, - 1, - 1000, - 1, - 500, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - ValidatorTxnConfig::default_disabled(), - true, - ); - let mut proposer_election = UnequivocalProposerElection::new(Arc::new(RotatingProposer::new( - vec![inserter.signer().author()], - 1, - ))); - let genesis = block_store.ordered_root(); - let a1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - let b1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 2) - .await; - - let original_res = proposal_generator - .generate_proposal(10, &mut proposer_election, empty_callback()) - .await - .unwrap(); - // With no certifications the parent is genesis - // generate proposals for an empty tree. - assert_eq!(original_res.parent_id(), genesis.id()); - // test that we have authors for the skipped rounds (1, 2, .. 9) - assert_eq!(original_res.failed_authors().unwrap().len(), 9); - assert_eq!(original_res.failed_authors().unwrap().first().unwrap().0, 1); - assert_eq!(original_res.failed_authors().unwrap().last().unwrap().0, 9); - - // Once a1 is certified, it should be the one to choose from - inserter.insert_qc_for_block(a1.as_ref(), None); - let a1_child_res = proposal_generator - .generate_proposal(11, &mut proposer_election, empty_callback()) - .await - .unwrap(); - assert_eq!(a1_child_res.parent_id(), a1.id()); - assert_eq!(a1_child_res.round(), 11); - assert_eq!(a1_child_res.quorum_cert().certified_block().id(), a1.id()); - - // test that we have authors for the skipped rounds (2, 3, .. 10) - assert_eq!(a1_child_res.failed_authors().unwrap().len(), 9); - assert_eq!(a1_child_res.failed_authors().unwrap().first().unwrap().0, 2); - assert_eq!(a1_child_res.failed_authors().unwrap().last().unwrap().0, 10); - - // Once b1 is certified, it should be the one to choose from - inserter.insert_qc_for_block(b1.as_ref(), None); - let b1_child_res = proposal_generator - .generate_proposal(15, &mut proposer_election, empty_callback()) - .await - .unwrap(); - assert_eq!(b1_child_res.parent_id(), b1.id()); - assert_eq!(b1_child_res.round(), 15); - assert_eq!(b1_child_res.quorum_cert().certified_block().id(), b1.id()); - - // test that we have authors for the skipped rounds (5, .. 14), as the limit of 10 has been reached - assert_eq!(b1_child_res.failed_authors().unwrap().len(), 10); - assert_eq!(b1_child_res.failed_authors().unwrap().first().unwrap().0, 5); - assert_eq!(b1_child_res.failed_authors().unwrap().last().unwrap().0, 14); -} - -#[tokio::test] -async fn test_old_proposal_generation() { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let mut proposal_generator = ProposalGenerator::new( - inserter.signer().author(), - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - Arc::new(SimulatedTimeService::new()), - Duration::ZERO, - 1, - 1000, - 1, - 500, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - ValidatorTxnConfig::default_disabled(), - true, - ); - let mut proposer_election = UnequivocalProposerElection::new(Arc::new(RotatingProposer::new( - vec![inserter.signer().author()], - 1, - ))); - let genesis = block_store.ordered_root(); - let a1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis, 1) - .await; - inserter.insert_qc_for_block(a1.as_ref(), None); - - let proposal_err = proposal_generator - .generate_proposal(1, &mut proposer_election, empty_callback()) - .await - .err(); - assert!(proposal_err.is_some()); -} - -#[tokio::test] -async fn test_correct_failed_authors() { - let inserter = TreeInserter::default(); - let author = inserter.signer().author(); - let peer1 = Author::random(); - let peer2 = Author::random(); - let block_store = inserter.block_store(); - let mut proposal_generator = ProposalGenerator::new( - author, - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - Arc::new(SimulatedTimeService::new()), - Duration::ZERO, - 1, - 1000, - 1, - 500, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - ValidatorTxnConfig::default_disabled(), - true, - ); - let mut proposer_election = UnequivocalProposerElection::new(Arc::new(RotatingProposer::new( - vec![author, peer1, peer2], - 1, - ))); - let genesis = block_store.ordered_root(); - - let result = proposal_generator - .generate_proposal(6, &mut proposer_election, empty_callback()) - .await - .unwrap(); - // With no certifications the parent is genesis - // generate proposals for an empty tree. - assert_eq!(result.parent_id(), genesis.id()); - // test that we have authors for the skipped rounds (1, 2, .. 5) - assert_eq!(result.failed_authors().unwrap().len(), 5); - assert_eq!(result.failed_authors().unwrap()[0], (1, peer1)); - assert_eq!(result.failed_authors().unwrap()[1], (2, peer2)); - assert_eq!(result.failed_authors().unwrap()[2], (3, author)); - assert_eq!(result.failed_authors().unwrap()[3], (4, peer1)); - assert_eq!(result.failed_authors().unwrap()[4], (5, peer2)); -} diff --git a/consensus/src/liveness/proposer_election.rs b/consensus/src/liveness/proposer_election.rs deleted file mode 100644 index d9935bd731bac..0000000000000 --- a/consensus/src/liveness/proposer_election.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_consensus_types::common::{Author, Round}; -use aptos_fallible::copy_from_slice::copy_slice_to_vec; -use num_traits::CheckedAdd; -use std::cmp::Ordering; - -/// ProposerElection incorporates the logic of choosing a leader among multiple candidates. -pub trait ProposerElection { - /// If a given author is a valid candidate for being a proposer, generate the info, - /// otherwise return None. - /// Note that this function is synchronous. - fn is_valid_proposer(&self, author: Author, round: Round) -> bool { - self.get_valid_proposer(round) == author - } - - /// Return the valid proposer for a given round (this information can be - /// used by e.g., voters for choosing the destinations for sending their votes to). - fn get_valid_proposer(&self, round: Round) -> Author; - - /// Return the chain health: a ratio of voting power participating in the consensus. - fn get_voting_power_participation_ratio(&self, _round: Round) -> f64 { - 1.0 - } - - fn get_valid_proposer_and_voting_power_participation_ratio( - &self, - round: Round, - ) -> (Author, f64) { - ( - self.get_valid_proposer(round), - self.get_voting_power_participation_ratio(round), - ) - } -} - -// next consumes seed and returns random deterministic u64 value in [0, max) range -fn next_in_range(state: Vec, max: u128) -> u128 { - // hash = SHA-3-256(state) - let hash = aptos_crypto::HashValue::sha3_256_of(&state).to_vec(); - let mut temp = [0u8; 16]; - copy_slice_to_vec(&hash[..16], &mut temp).expect("next failed"); - // return hash[0..16] - u128::from_le_bytes(temp) % max -} - -// chose index randomly, with given weight distribution -pub(crate) fn choose_index(mut weights: Vec, state: Vec) -> usize { - let mut total_weight = 0; - // Create cumulative weights vector - // Since we own the vector, we can safely modify it in place - for w in &mut weights { - total_weight = total_weight - .checked_add(w) - .expect("Total stake shouldn't exceed u128::MAX"); - *w = total_weight; - } - let chosen_weight = next_in_range(state, total_weight); - weights - .binary_search_by(|w| { - if *w <= chosen_weight { - Ordering::Less - } else { - Ordering::Greater - } - }) - .unwrap_err() -} - -#[test] -fn test_bounds() { - // check that bounds are correct, and both first and last weight can be selected. - let mut selected = [0, 0]; - let weights = [u64::MAX as u128 * 1000, u64::MAX as u128 * 1000].to_vec(); - // 10 is enough to get one of each. - for i in 0i32..10 { - let state = i.to_le_bytes().to_vec(); - selected[choose_index(weights.clone(), state)] += 1; - } - - assert!(selected[0] >= 1); - assert!(selected[1] >= 1); -} diff --git a/consensus/src/liveness/rotating_proposer_election.rs b/consensus/src/liveness/rotating_proposer_election.rs deleted file mode 100644 index db2b2c4dfbe2b..0000000000000 --- a/consensus/src/liveness/rotating_proposer_election.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::liveness::proposer_election::ProposerElection; -use aptos_consensus_types::common::{Author, Round}; - -/// The rotating proposer maps a round to an author according to a round-robin rotation. -/// A fixed proposer strategy loses liveness when the fixed proposer is down. Rotating proposers -/// won't gather quorum certificates to machine loss/byzantine behavior on f/n rounds. -pub struct RotatingProposer { - // Ordering of proposers to rotate through (all honest replicas must agree on this) - proposers: Vec, - // Number of contiguous rounds (i.e. round numbers increase by 1) a proposer is active - // in a row - contiguous_rounds: u32, -} - -/// Choose a proposer that is going to be the single leader (relevant for a mock fixed proposer -/// election only). -pub fn choose_leader(peers: Vec) -> Author { - // As it is just a tmp hack function, pick the min PeerId to be a proposer. - peers.into_iter().min().expect("No trusted peers found!") -} - -impl RotatingProposer { - /// With only one proposer in the vector, it behaves the same as a fixed proposer strategy. - pub fn new(proposers: Vec, contiguous_rounds: u32) -> Self { - Self { - proposers, - contiguous_rounds, - } - } -} - -impl ProposerElection for RotatingProposer { - fn get_valid_proposer(&self, round: Round) -> Author { - self.proposers - [((round / u64::from(self.contiguous_rounds)) % self.proposers.len() as u64) as usize] - } -} diff --git a/consensus/src/liveness/rotating_proposer_test.rs b/consensus/src/liveness/rotating_proposer_test.rs deleted file mode 100644 index f2dfc774d7f41..0000000000000 --- a/consensus/src/liveness/rotating_proposer_test.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::liveness::{ - proposer_election::ProposerElection, rotating_proposer_election::RotatingProposer, -}; -use aptos_types::account_address::AccountAddress; - -#[test] -fn test_rotating_proposer() { - let chosen_author = AccountAddress::random(); - let another_author = AccountAddress::random(); - let proposers = vec![chosen_author, another_author]; - let pe = RotatingProposer::new(proposers, 1); - - // Send a proposal from both chosen author and another author, the only winning proposals - // follow the round-robin rotation. - - assert!(!pe.is_valid_proposer(chosen_author, 1)); - assert!(pe.is_valid_proposer(another_author, 1),); - assert!(pe.is_valid_proposer(chosen_author, 2)); - assert!(!pe.is_valid_proposer(another_author, 2)); - assert_eq!(pe.get_valid_proposer(1), another_author); - assert_eq!(pe.get_valid_proposer(2), chosen_author); -} - -#[test] -fn test_rotating_proposer_with_three_contiguous_rounds() { - let chosen_author = AccountAddress::random(); - let another_author = AccountAddress::random(); - let proposers = vec![chosen_author, another_author]; - let pe = RotatingProposer::new(proposers, 3); - - // Send a proposal from both chosen author and another author, the only winning proposals - // follow the round-robin rotation with 3 contiguous rounds. - - assert!(!pe.is_valid_proposer(another_author, 1)); - assert!(pe.is_valid_proposer(chosen_author, 1)); - assert!(pe.is_valid_proposer(chosen_author, 2)); - assert!(!pe.is_valid_proposer(another_author, 2)); - assert_eq!(pe.get_valid_proposer(1), chosen_author); - assert_eq!(pe.get_valid_proposer(2), chosen_author); -} - -#[test] -fn test_fixed_proposer() { - let chosen_author = AccountAddress::random(); - let another_author = AccountAddress::random(); - let pe = RotatingProposer::new(vec![chosen_author], 1); - - // Send a proposal from both chosen author and another author, the only winning proposal is - // from the chosen author. - - assert!(pe.is_valid_proposer(chosen_author, 1)); - assert!(!pe.is_valid_proposer(another_author, 1)); - assert_eq!(pe.get_valid_proposer(1), chosen_author); - assert!(pe.is_valid_proposer(chosen_author, 2)); -} diff --git a/consensus/src/liveness/round_proposer_election.rs b/consensus/src/liveness/round_proposer_election.rs deleted file mode 100644 index 653852b91278b..0000000000000 --- a/consensus/src/liveness/round_proposer_election.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::liveness::proposer_election::ProposerElection; -use aptos_consensus_types::common::{Author, Round}; -use std::collections::HashMap; - -/// The round proposer maps a round to author -pub struct RoundProposer { - // A pre-defined map specifying proposers per round - proposers: HashMap, - // Default proposer to use if proposer for a round is unspecified. - // We hardcode this to the first proposer - default_proposer: Author, -} - -impl RoundProposer { - pub fn new(proposers: HashMap, default_proposer: Author) -> Self { - Self { - proposers, - default_proposer, - } - } -} - -impl ProposerElection for RoundProposer { - fn get_valid_proposer(&self, round: Round) -> Author { - match self.proposers.get(&round) { - None => self.default_proposer, - Some(round_proposer) => *round_proposer, - } - } -} diff --git a/consensus/src/liveness/round_proposer_test.rs b/consensus/src/liveness/round_proposer_test.rs deleted file mode 100644 index 27b603b799dd0..0000000000000 --- a/consensus/src/liveness/round_proposer_test.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::liveness::{ - proposer_election::ProposerElection, round_proposer_election::RoundProposer, -}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_types::account_address::AccountAddress; -use std::collections::HashMap; - -#[test] -fn test_round_proposer() { - let chosen_author_round1 = AccountAddress::random(); - let chosen_author_round2 = AccountAddress::random(); - let another_author = AccountAddress::random(); - - // A map that specifies the proposer per round - let mut round_proposers: HashMap = HashMap::new(); - round_proposers.insert(1, chosen_author_round1); - round_proposers.insert(2, chosen_author_round2); - - let pe = RoundProposer::new(round_proposers, chosen_author_round1); - - // Send a proposal from both chosen author and another author, the only winning proposals - // follow the round-proposers mapping - - // In round 3, send a proposal from chosen_author_round1 (which is also the default proposer). - // The proposal should win because the map doesn't specify proposer for round 3 hence - // falling back on the default proposer - - assert!(pe.is_valid_proposer(chosen_author_round1, 1),); - assert!(!pe.is_valid_proposer(another_author, 1)); - assert!(pe.is_valid_proposer(chosen_author_round2, 2)); - assert!(!pe.is_valid_proposer(another_author, 2)); - assert!(pe.is_valid_proposer(chosen_author_round1, 3)); - assert!(!pe.is_valid_proposer(another_author, 3)); - assert_eq!(pe.get_valid_proposer(1), chosen_author_round1); - assert_eq!(pe.get_valid_proposer(2), chosen_author_round2); - assert_eq!(pe.get_valid_proposer(3), chosen_author_round1); -} diff --git a/consensus/src/liveness/round_state.rs b/consensus/src/liveness/round_state.rs deleted file mode 100644 index 12cb7f5061e04..0000000000000 --- a/consensus/src/liveness/round_state.rs +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - pending_votes::{PendingVotes, VoteReceptionResult}, - util::time_service::{SendTask, TimeService}, -}; -use aptos_config::config::QcAggregatorType; -use aptos_consensus_types::{ - common::Round, delayed_qc_msg::DelayedQcMsg, sync_info::SyncInfo, - timeout_2chain::TwoChainTimeoutWithPartialSignatures, vote::Vote, -}; -use aptos_crypto::HashValue; -use aptos_logger::{prelude::*, Schema}; -use aptos_types::{ - ledger_info::LedgerInfoWithPartialSignatures, validator_verifier::ValidatorVerifier, -}; -use futures::future::AbortHandle; -use futures_channel::mpsc::UnboundedSender; -use serde::Serialize; -use std::{fmt, sync::Arc, time::Duration}; - -/// A reason for starting a new round: introduced for monitoring / debug purposes. -#[derive(Serialize, Debug, PartialEq, Eq)] -pub enum NewRoundReason { - QCReady, - Timeout, -} - -impl fmt::Display for NewRoundReason { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - NewRoundReason::QCReady => write!(f, "QCReady"), - NewRoundReason::Timeout => write!(f, "TCReady"), - } - } -} - -/// NewRoundEvents produced by RoundState are guaranteed to be monotonically increasing. -/// NewRoundEvents are consumed by the rest of the system: they can cause sending new proposals -/// or voting for some proposals that wouldn't have been voted otherwise. -/// The duration is populated for debugging and testing -#[derive(Debug)] -pub struct NewRoundEvent { - pub round: Round, - pub reason: NewRoundReason, - pub timeout: Duration, - pub prev_round_votes: Vec<(HashValue, LedgerInfoWithPartialSignatures)>, - pub prev_round_timeout_votes: Option, -} - -impl fmt::Display for NewRoundEvent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "NewRoundEvent: [round: {}, reason: {}, timeout: {:?}]", - self.round, self.reason, self.timeout, - ) - } -} - -/// Determines the maximum round duration based on the round difference between the current -/// round and the committed round -pub trait RoundTimeInterval: Send + Sync + 'static { - /// Use the index of the round after the highest quorum certificate to commit a block and - /// return the duration for this round - /// - /// Round indices start at 0 (round index = 0 is the first round after the round that led - /// to the highest committed round). Given that round r is the highest round to commit a - /// block, then round index 0 is round r+1. Note that for genesis does not follow the - /// 3-chain rule for commits, so round 1 has round index 0. For example, if one wants - /// to calculate the round duration of round 6 and the highest committed round is 3 (meaning - /// the highest round to commit a block is round 5, then the round index is 0. - fn get_round_duration(&self, round_index_after_committed_qc: usize) -> Duration; -} - -/// Round durations increase exponentially -/// Basically time interval is base * mul^power -/// Where power=max(rounds_since_qc, max_exponent) -#[derive(Clone)] -pub struct ExponentialTimeInterval { - // Initial time interval duration after a successful quorum commit. - base_ms: u64, - // By how much we increase interval every time - exponent_base: f64, - // Maximum time interval won't exceed base * mul^max_pow. - // Theoretically, setting it means - // that we rely on synchrony assumptions when the known max messaging delay is - // max_interval. Alternatively, we can consider using max_interval to meet partial synchrony - // assumptions where while delta is unknown, it is <= max_interval. - max_exponent: usize, -} - -impl ExponentialTimeInterval { - #[cfg(any(test, feature = "fuzzing"))] - pub fn fixed(duration: Duration) -> Self { - Self::new(duration, 1.0, 0) - } - - pub fn new(base: Duration, exponent_base: f64, max_exponent: usize) -> Self { - assert!( - max_exponent < 32, - "max_exponent for RoundStateTimeInterval should be <32" - ); - assert!( - exponent_base.powf(max_exponent as f64).ceil() < f64::from(std::u32::MAX), - "Maximum interval multiplier should be less then u32::Max" - ); - ExponentialTimeInterval { - base_ms: base.as_millis() as u64, // any reasonable ms timeout fits u64 perfectly - exponent_base, - max_exponent, - } - } -} - -impl RoundTimeInterval for ExponentialTimeInterval { - fn get_round_duration(&self, round_index_after_committed_qc: usize) -> Duration { - let pow = round_index_after_committed_qc.min(self.max_exponent) as u32; - let base_multiplier = self.exponent_base.powf(f64::from(pow)); - let duration_ms = ((self.base_ms as f64) * base_multiplier).ceil() as u64; - Duration::from_millis(duration_ms) - } -} - -/// `RoundState` contains information about a specific round and moves forward when -/// receives new certificates. -/// -/// A round `r` starts in the following cases: -/// * there is a QuorumCert for round `r-1`, -/// * there is a TimeoutCertificate for round `r-1`. -/// -/// Round interval calculation is the responsibility of the RoundStateTimeoutInterval trait. It -/// depends on the delta between the current round and the highest committed round (the intuition is -/// that we want to exponentially grow the interval the further the current round is from the last -/// committed round). -/// -/// Whenever a new round starts a local timeout is set following the round interval. This local -/// timeout is going to send the timeout events once in interval until the new round starts. -pub struct RoundState { - // Determines the time interval for a round given the number of non-committed rounds since - // last commit. - time_interval: Box, - // Highest known committed round as reported by the caller. The caller might choose not to - // inform the RoundState about certain committed rounds (e.g., NIL blocks): in this case the - // committed round in RoundState might lag behind the committed round of a block tree. - highest_committed_round: Round, - // Current round is max{highest_qc, highest_tc} + 1. - current_round: Round, - // The deadline for the next local timeout event. It is reset every time a new round start, or - // a previous deadline expires. - // Represents as Duration since UNIX_EPOCH. - current_round_deadline: Duration, - // Service for timer - time_service: Arc, - // To send local timeout events to the subscriber (e.g., SMR) - timeout_sender: aptos_channels::Sender, - // Votes received fot the current round. - pending_votes: PendingVotes, - // Vote sent locally for the current round. - vote_sent: Option, - // The handle to cancel previous timeout task when moving to next round. - abort_handle: Option, - // Self sender to send delayed QC aggregation events to the round manager. - delayed_qc_tx: UnboundedSender, - qc_aggregator_type: QcAggregatorType, -} - -#[derive(Default, Schema)] -pub struct RoundStateLogSchema<'a> { - round: Option, - committed_round: Option, - #[schema(display)] - pending_votes: Option<&'a PendingVotes>, - #[schema(display)] - self_vote: Option<&'a Vote>, -} - -impl<'a> RoundStateLogSchema<'a> { - pub fn new(state: &'a RoundState) -> Self { - Self { - round: Some(state.current_round), - committed_round: Some(state.highest_committed_round), - pending_votes: Some(&state.pending_votes), - self_vote: state.vote_sent.as_ref(), - } - } -} - -impl RoundState { - pub fn new( - time_interval: Box, - time_service: Arc, - timeout_sender: aptos_channels::Sender, - delayed_qc_tx: UnboundedSender, - qc_aggregator_type: QcAggregatorType, - ) -> Self { - // Our counters are initialized lazily, so they're not going to appear in - // Prometheus if some conditions never happen. Invoking get() function enforces creation. - counters::QC_ROUNDS_COUNT.get(); - counters::TIMEOUT_ROUNDS_COUNT.get(); - counters::TIMEOUT_COUNT.get(); - - let pending_votes = PendingVotes::new( - time_service.clone(), - delayed_qc_tx.clone(), - qc_aggregator_type.clone(), - ); - - Self { - time_interval, - highest_committed_round: 0, - current_round: 0, - current_round_deadline: time_service.get_current_timestamp(), - time_service, - timeout_sender, - pending_votes, - vote_sent: None, - abort_handle: None, - delayed_qc_tx, - qc_aggregator_type, - } - } - - /// Return if already voted for timeout - pub fn is_vote_timeout(&self) -> bool { - self.vote_sent.as_ref().map_or(false, |v| v.is_timeout()) - } - - /// Return the current round. - pub fn current_round(&self) -> Round { - self.current_round - } - - /// Returns deadline for current round - pub fn current_round_deadline(&self) -> Duration { - self.current_round_deadline - } - - /// In case the local timeout corresponds to the current round, reset the timeout and - /// return true. Otherwise ignore and return false. - pub fn process_local_timeout(&mut self, round: Round) -> bool { - if round != self.current_round { - return false; - } - warn!(round = round, "Local timeout"); - counters::TIMEOUT_COUNT.inc(); - self.setup_timeout(1); - true - } - - /// Notify the RoundState about the potentially new QC, TC, and highest committed round. - /// Note that some of these values might not be available by the caller. - pub fn process_certificates(&mut self, sync_info: SyncInfo) -> Option { - if sync_info.highest_ordered_round() > self.highest_committed_round { - self.highest_committed_round = sync_info.highest_ordered_round(); - } - let new_round = sync_info.highest_round() + 1; - if new_round > self.current_round { - let (prev_round_votes, prev_round_timeout_votes) = self.pending_votes.drain_votes(); - - // Start a new round. - self.current_round = new_round; - self.pending_votes = PendingVotes::new( - self.time_service.clone(), - self.delayed_qc_tx.clone(), - self.qc_aggregator_type.clone(), - ); - self.vote_sent = None; - let timeout = self.setup_timeout(1); - // The new round reason is QCReady in case both QC.round + 1 == new_round, otherwise - // it's Timeout and TC.round + 1 == new_round. - let new_round_reason = if sync_info.highest_certified_round() + 1 == new_round { - NewRoundReason::QCReady - } else { - NewRoundReason::Timeout - }; - let new_round_event = NewRoundEvent { - round: self.current_round, - reason: new_round_reason, - timeout, - prev_round_votes, - prev_round_timeout_votes, - }; - info!(round = new_round, "Starting new round: {}", new_round_event); - return Some(new_round_event); - } - None - } - - pub fn insert_vote( - &mut self, - vote: &Vote, - verifier: &ValidatorVerifier, - ) -> VoteReceptionResult { - if vote.vote_data().proposed().round() == self.current_round { - self.pending_votes.insert_vote(vote, verifier) - } else { - VoteReceptionResult::UnexpectedRound( - vote.vote_data().proposed().round(), - self.current_round, - ) - } - } - - pub fn record_vote(&mut self, vote: Vote) { - if vote.vote_data().proposed().round() == self.current_round { - self.vote_sent = Some(vote); - } - } - - pub async fn process_delayed_qc_msg( - &mut self, - validator_verifier: &ValidatorVerifier, - msg: DelayedQcMsg, - ) -> VoteReceptionResult { - let DelayedQcMsg { vote } = msg; - self.pending_votes - .process_delayed_qc(validator_verifier, vote) - } - - pub fn vote_sent(&self) -> Option { - self.vote_sent.clone() - } - - /// Setup a longer timeout task for leader because it enters the round earlier. - pub fn setup_leader_timeout(&mut self) { - self.setup_timeout(2); - } - - /// Setup the timeout task and return the duration of the current timeout - fn setup_timeout(&mut self, multiplier: u32) -> Duration { - let timeout_sender = self.timeout_sender.clone(); - let timeout = self.setup_deadline(multiplier); - trace!( - "Scheduling timeout of {} ms for round {}", - timeout.as_millis(), - self.current_round - ); - let abort_handle = self - .time_service - .run_after(timeout, SendTask::make(timeout_sender, self.current_round)); - if let Some(handle) = self.abort_handle.replace(abort_handle) { - handle.abort(); - } - timeout - } - - /// Setup the current round deadline and return the duration of the current round - fn setup_deadline(&mut self, multiplier: u32) -> Duration { - let round_index_after_committed_round = { - if self.highest_committed_round == 0 { - // Genesis doesn't require the 3-chain rule for commit, hence start the index at - // the round after genesis. - self.current_round - 1 - } else if self.current_round < self.highest_committed_round + 3 { - 0 - } else { - self.current_round - self.highest_committed_round - 3 - } - } as usize; - let timeout = self - .time_interval - .get_round_duration(round_index_after_committed_round) - * multiplier; - let now = self.time_service.get_current_timestamp(); - debug!( - round = self.current_round, - "{:?} passed since the previous deadline.", - now.checked_sub(self.current_round_deadline) - .map_or("0 ms".to_string(), |v| format!("{:?}", v)) - ); - debug!( - round = self.current_round, - "Set round deadline to {:?} from now", timeout - ); - self.current_round_deadline = now + timeout; - timeout - } -} diff --git a/consensus/src/liveness/round_state_test.rs b/consensus/src/liveness/round_state_test.rs deleted file mode 100644 index c29179131a8a6..0000000000000 --- a/consensus/src/liveness/round_state_test.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - liveness::round_state::{ - ExponentialTimeInterval, NewRoundEvent, NewRoundReason, RoundState, RoundTimeInterval, - }, - util::mock_time_service::SimulatedTimeService, -}; -use aptos_config::config::QcAggregatorType; -use aptos_consensus_types::{ - common::Round, - quorum_cert::QuorumCert, - sync_info::SyncInfo, - timeout_2chain::{TwoChainTimeout, TwoChainTimeoutCertificate}, - vote_data::VoteData, -}; -use aptos_crypto::HashValue; -use aptos_types::{ - aggregate_signature::AggregateSignature, - block_info::BlockInfo, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, -}; -use futures::StreamExt; -use futures_channel::mpsc::unbounded; -use std::{sync::Arc, time::Duration}; - -#[test] -fn test_round_time_interval() { - let interval = ExponentialTimeInterval::new(Duration::from_millis(3000), 1.5, 2); - assert_eq!(3000, interval.get_round_duration(0).as_millis()); - assert_eq!(4500, interval.get_round_duration(1).as_millis()); - assert_eq!( - 6750, /* 4500*1.5 */ - interval.get_round_duration(2).as_millis() - ); - // Test that there is no integer overflow - assert_eq!(6750, interval.get_round_duration(1000).as_millis()); -} - -#[tokio::test] -/// Verify that RoundState properly outputs local timeout events upon timeout -async fn test_basic_timeout() { - let (mut pm, mut timeout_rx) = make_round_state(); - - // jump start the round_state - pm.process_certificates(generate_sync_info(Some(0), None, None)); - for _ in 0..2 { - let round = timeout_rx.next().await.unwrap(); - // Here we just test timeout send retry, - // round for timeout is not changed as no timeout certificate was gathered at this point - assert_eq!(1, round); - pm.process_local_timeout(round); - } -} - -#[test] -fn test_round_event_generation() { - let (mut pm, _) = make_round_state(); - // Happy path with new QC - expect_qc( - 2, - pm.process_certificates(generate_sync_info(Some(1), None, None)), - ); - // Old QC does not generate anything - assert!(pm - .process_certificates(generate_sync_info(Some(1), None, None)) - .is_none()); - // A TC for a higher round - expect_timeout( - 3, - pm.process_certificates(generate_sync_info(None, Some(2), None)), - ); - // In case both QC and TC are present choose the one with the higher value - expect_timeout( - 4, - pm.process_certificates(generate_sync_info(Some(2), Some(3), None)), - ); - // In case both QC and TC are present with the same value, choose QC - expect_qc( - 5, - pm.process_certificates(generate_sync_info(Some(4), Some(4), None)), - ); -} - -fn make_round_state() -> (RoundState, aptos_channels::Receiver) { - let time_interval = Box::new(ExponentialTimeInterval::fixed(Duration::from_millis(2))); - let simulated_time = SimulatedTimeService::auto_advance_until(Duration::from_millis(4)); - let (timeout_tx, timeout_rx) = aptos_channels::new_test(1_024); - let (delayed_qc_tx, _) = unbounded(); - ( - RoundState::new( - time_interval, - Arc::new(simulated_time), - timeout_tx, - delayed_qc_tx, - QcAggregatorType::NoDelay, - ), - timeout_rx, - ) -} - -fn expect_qc(round: Round, event: Option) { - let event = event.unwrap(); - assert_eq!(round, event.round); - assert_eq!(event.reason, NewRoundReason::QCReady); -} - -fn expect_timeout(round: Round, event: Option) { - let event = event.unwrap(); - assert_eq!(round, event.round); - assert_eq!(event.reason, NewRoundReason::Timeout); -} - -fn generate_sync_info( - quorum_round: Option, - timeout_round: Option, - commit_round: Option, -) -> SyncInfo { - let quorum_round = quorum_round.unwrap_or(0); - let timeout_round = timeout_round.unwrap_or(0); - let commit_round = commit_round.unwrap_or(0); - let commit_block = BlockInfo::new( - 1, - commit_round, - HashValue::zero(), - HashValue::zero(), - 0, - 0, - None, - ); - let ledger_info = LedgerInfoWithSignatures::new( - LedgerInfo::new(commit_block, HashValue::zero()), - AggregateSignature::empty(), - ); - let quorum_cert = QuorumCert::new( - VoteData::new( - BlockInfo::new( - 1, - quorum_round, - HashValue::zero(), - HashValue::zero(), - 0, - 0, - None, - ), - BlockInfo::empty(), - ), - ledger_info, - ); - let commit_cert = quorum_cert.clone(); - let tc = TwoChainTimeoutCertificate::new(TwoChainTimeout::new( - 1, - timeout_round, - quorum_cert.clone(), - )); - SyncInfo::new(quorum_cert, commit_cert, Some(tc)) -} diff --git a/consensus/src/liveness/unequivocal_proposer_election.rs b/consensus/src/liveness/unequivocal_proposer_election.rs deleted file mode 100644 index 5e4be2bfb372e..0000000000000 --- a/consensus/src/liveness/unequivocal_proposer_election.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::proposer_election::ProposerElection; -use aptos_consensus_types::{ - block::Block, - common::{Author, Round}, -}; -use aptos_crypto::HashValue; -use aptos_infallible::Mutex; -use aptos_logger::{error, warn, SecurityEvent}; -use std::{cmp::Ordering, sync::Arc}; - -// Wrapper around ProposerElection. -// -// Provides is_valid_proposal that remembers, and rejects if -// the same leader proposes multiple blocks. -pub struct UnequivocalProposerElection { - proposer_election: Arc, - already_proposed: Mutex<(Round, HashValue)>, -} - -impl ProposerElection for UnequivocalProposerElection { - fn get_valid_proposer(&self, round: Round) -> Author { - self.proposer_election.get_valid_proposer(round) - } - - fn get_voting_power_participation_ratio(&self, round: Round) -> f64 { - self.proposer_election - .get_voting_power_participation_ratio(round) - } -} - -impl UnequivocalProposerElection { - pub fn new(proposer_election: Arc) -> Self { - Self { - proposer_election, - already_proposed: Mutex::new((0, HashValue::zero())), - } - } - - // Return if a given proposed block is valid: - // - if a given author is a valid candidate for being a proposer - // - if this is the first block proposer has submitted in this round - // - if it is not old proposal - pub fn is_valid_proposal(&self, block: &Block) -> bool { - block.author().map_or(false, |author| { - let valid_author = self.is_valid_proposer(author, block.round()); - if !valid_author { - warn!( - SecurityEvent::InvalidConsensusProposal, - "Proposal is not from valid author {}, expected {} for round {} and id {}", - author, - self.get_valid_proposer(block.round()), - block.round(), - block.id() - ); - - return false; - } - let mut already_proposed = self.already_proposed.lock(); - // detect if the leader proposes more than once in this round - match block.round().cmp(&already_proposed.0) { - Ordering::Greater => { - already_proposed.0 = block.round(); - already_proposed.1 = block.id(); - true - }, - Ordering::Equal => { - if already_proposed.1 != block.id() { - error!( - SecurityEvent::InvalidConsensusProposal, - "Multiple proposals from {} for round {}: {} and {}", - author, - block.round(), - already_proposed.1, - block.id() - ); - false - } else { - true - } - }, - Ordering::Less => false, - } - }) - } -} diff --git a/consensus/src/liveness/unequivocal_proposer_election_test.rs b/consensus/src/liveness/unequivocal_proposer_election_test.rs deleted file mode 100644 index 41aa3f1a458b7..0000000000000 --- a/consensus/src/liveness/unequivocal_proposer_election_test.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::proposer_election::ProposerElection; -use crate::liveness::unequivocal_proposer_election::UnequivocalProposerElection; -use aptos_consensus_types::{ - block::{block_test_utils::certificate_for_genesis, Block}, - common::{Author, Payload, Round}, -}; -use aptos_types::validator_signer::ValidatorSigner; -use std::{collections::HashMap, sync::Arc}; - -struct MockProposerElection { - proposers: HashMap, -} - -impl MockProposerElection { - pub fn new(proposers: HashMap) -> Self { - Self { proposers } - } -} - -impl ProposerElection for MockProposerElection { - fn get_valid_proposer(&self, round: Round) -> Author { - *self.proposers.get(&round).unwrap() - } -} - -#[test] -fn test_is_valid_proposal() { - let chosen_validator_signer = ValidatorSigner::random([0u8; 32]); - let chosen_author = chosen_validator_signer.author(); - let another_validator_signer = ValidatorSigner::random([1u8; 32]); - // let another_author = another_validator_signer.author(); - - // Test genesis and the next block - let quorum_cert = certificate_for_genesis(); - - let good_proposal = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - quorum_cert.clone(), - &chosen_validator_signer, - Vec::new(), - ) - .unwrap(); - let bad_author_proposal = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - quorum_cert.clone(), - &another_validator_signer, - Vec::new(), - ) - .unwrap(); - let bad_duplicate_proposal = Block::new_proposal( - Payload::empty(false, true), - 1, - 2, - quorum_cert.clone(), - &chosen_validator_signer, - Vec::new(), - ) - .unwrap(); - let next_good_proposal = Block::new_proposal( - Payload::empty(false, true), - 2, - 3, - quorum_cert.clone(), - &chosen_validator_signer, - Vec::new(), - ) - .unwrap(); - let next_bad_duplicate_proposal = Block::new_proposal( - Payload::empty(false, true), - 2, - 4, - quorum_cert, - &chosen_validator_signer, - Vec::new(), - ) - .unwrap(); - - let pe = - UnequivocalProposerElection::new(Arc::new(MockProposerElection::new(HashMap::from([ - (1, chosen_author), - (2, chosen_author), - ])))); - - assert!(pe.is_valid_proposer(chosen_author, 1)); - assert!(pe.is_valid_proposal(&good_proposal)); - assert!(!pe.is_valid_proposal(&bad_author_proposal)); - - // another proposal from the valid proposer should fail - assert!(!pe.is_valid_proposal(&bad_duplicate_proposal)); - // good proposal still passes - assert!(pe.is_valid_proposal(&good_proposal)); - - // going to the next round: - assert!(pe.is_valid_proposal(&next_good_proposal)); - assert!(!pe.is_valid_proposal(&next_bad_duplicate_proposal)); - - // Proposal from previous round is not valid any more: - assert!(!pe.is_valid_proposal(&good_proposal)); -} diff --git a/consensus/src/logging.rs b/consensus/src/logging.rs deleted file mode 100644 index 0e04adb3343d6..0000000000000 --- a/consensus/src/logging.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_consensus_types::common::Author; -use aptos_crypto::HashValue; -use aptos_logger::Schema; -use aptos_types::block_info::Round; -use serde::Serialize; - -#[derive(Schema)] -pub struct LogSchema { - event: LogEvent, - author: Option, - remote_peer: Option, - epoch: Option, - round: Option, - id: Option, -} - -#[derive(Serialize)] -pub enum LogEvent { - CommitViaBlock, - CommitViaSync, - IncrementalProofExpired, - NetworkReceiveProposal, - NewEpoch, - NewRound, - ProofOfStoreInit, - ProofOfStoreReady, - Propose, - ReceiveBatchRetrieval, - ReceiveBlockRetrieval, - ReceiveEpochChangeProof, - ReceiveEpochRetrieval, - ReceiveMessageFromDifferentEpoch, - ReceiveNewCertificate, - ReceiveProposal, - ReceiveSyncInfo, - ReceiveVote, - RetrieveBlock, - StateSync, - Timeout, - Vote, - VoteNIL, - // log events related to randomness generation - BroadcastRandShare, - ReceiveProactiveRandShare, - ReceiveReactiveRandShare, - BroadcastAugData, - ReceiveAugData, - BroadcastCertifiedAugData, - ReceiveCertifiedAugData, -} - -impl LogSchema { - pub fn new(event: LogEvent) -> Self { - Self { - event, - author: None, - remote_peer: None, - epoch: None, - round: None, - id: None, - } - } -} diff --git a/consensus/src/metrics_safety_rules.rs b/consensus/src/metrics_safety_rules.rs deleted file mode 100644 index d49d2dbf12465..0000000000000 --- a/consensus/src/metrics_safety_rules.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - monitor, persistent_liveness_storage::PersistentLivenessStorage, - pipeline::signing_phase::CommitSignerProvider, -}; -use aptos_consensus_types::{ - block_data::BlockData, - timeout_2chain::{TwoChainTimeout, TwoChainTimeoutCertificate}, - vote::Vote, - vote_proposal::VoteProposal, -}; -use aptos_crypto::bls12381; -use aptos_infallible::Mutex; -use aptos_logger::prelude::info; -use aptos_safety_rules::{ConsensusState, Error, TSafetyRules}; -use aptos_types::{ - epoch_change::EpochChangeProof, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, -}; -use std::sync::Arc; - -/// Wrap safety rules with counters. -pub struct MetricsSafetyRules { - inner: Box, - storage: Arc, -} - -impl MetricsSafetyRules { - pub fn new( - inner: Box, - storage: Arc, - ) -> Self { - Self { inner, storage } - } - - pub fn perform_initialize(&mut self) -> Result<(), Error> { - let consensus_state = self.consensus_state()?; - let mut waypoint_version = consensus_state.waypoint().version(); - loop { - let proofs = self - .storage - .retrieve_epoch_change_proof(waypoint_version) - .map_err(|e| { - Error::InternalError(format!( - "Unable to retrieve Waypoint state from storage, encountered Error:{}", - e - )) - })?; - // We keep initializing safety rules as long as the waypoint continues to increase. - // This is due to limits in the number of epoch change proofs that storage can provide. - match self.initialize(&proofs) { - Err(Error::WaypointOutOfDate( - prev_version, - curr_version, - current_epoch, - provided_epoch, - )) if prev_version < curr_version => { - waypoint_version = curr_version; - info!("Previous waypoint version {}, updated version {}, current epoch {}, provided epoch {}", prev_version, curr_version, current_epoch, provided_epoch); - continue; - }, - result => return result, - } - } - } - - fn retry) -> Result>( - &mut self, - mut f: F, - ) -> Result { - let result = f(&mut self.inner); - match result { - Err(Error::NotInitialized(_)) - | Err(Error::IncorrectEpoch(_, _)) - | Err(Error::WaypointOutOfDate(_, _, _, _)) => { - self.perform_initialize()?; - f(&mut self.inner) - }, - _ => result, - } - } -} - -impl TSafetyRules for MetricsSafetyRules { - fn consensus_state(&mut self) -> Result { - monitor!("safety_rules", self.inner.consensus_state()) - } - - fn initialize(&mut self, proof: &EpochChangeProof) -> Result<(), Error> { - monitor!("safety_rules", self.inner.initialize(proof)) - } - - fn sign_proposal(&mut self, block_data: &BlockData) -> Result { - self.retry(|inner| monitor!("safety_rules", inner.sign_proposal(block_data))) - } - - fn sign_timeout_with_qc( - &mut self, - timeout: &TwoChainTimeout, - timeout_cert: Option<&TwoChainTimeoutCertificate>, - ) -> Result { - self.retry(|inner| { - monitor!( - "safety_rules", - inner.sign_timeout_with_qc(timeout, timeout_cert) - ) - }) - } - - fn construct_and_sign_vote_two_chain( - &mut self, - vote_proposal: &VoteProposal, - timeout_cert: Option<&TwoChainTimeoutCertificate>, - ) -> Result { - self.retry(|inner| { - monitor!( - "safety_rules", - inner.construct_and_sign_vote_two_chain(vote_proposal, timeout_cert) - ) - }) - } - - fn sign_commit_vote( - &mut self, - ledger_info: LedgerInfoWithSignatures, - new_ledger_info: LedgerInfo, - ) -> Result { - self.retry(|inner| { - monitor!( - "safety_rules", - inner.sign_commit_vote(ledger_info.clone(), new_ledger_info.clone()) - ) - }) - } -} - -impl CommitSignerProvider for Mutex { - fn sign_commit_vote( - &self, - ledger_info: LedgerInfoWithSignatures, - new_ledger_info: LedgerInfo, - ) -> Result { - self.lock().sign_commit_vote(ledger_info, new_ledger_info) - } -} - -#[cfg(test)] -mod tests { - use crate::{metrics_safety_rules::MetricsSafetyRules, test_utils::EmptyStorage}; - use aptos_consensus_types::{ - block_data::BlockData, - timeout_2chain::{TwoChainTimeout, TwoChainTimeoutCertificate}, - vote::Vote, - vote_proposal::VoteProposal, - }; - use aptos_crypto::bls12381; - use aptos_safety_rules::{ConsensusState, Error, TSafetyRules}; - use aptos_types::{ - epoch_change::EpochChangeProof, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - }; - use claims::{assert_matches, assert_ok}; - - pub struct MockSafetyRules { - // number of initialize() calls - init_calls: i32, - - // max initialize() calls to complete perform_initialize() - max_init_calls: i32, - - // last initialize() returns Ok() or any error != WaypointOutOfDate - last_init_result: Result<(), Error>, - } - - impl MockSafetyRules { - pub fn new( - init_calls: i32, - max_init_calls: i32, - last_init_result: Result<(), Error>, - ) -> Self { - Self { - init_calls, - max_init_calls, - last_init_result, - } - } - } - - impl TSafetyRules for MockSafetyRules { - fn consensus_state(&mut self) -> Result { - Ok(ConsensusState::default()) - } - - fn initialize(&mut self, _: &EpochChangeProof) -> Result<(), Error> { - self.init_calls += 1; - if self.init_calls < self.max_init_calls { - return Err(Error::WaypointOutOfDate( - (self.init_calls - 1) as u64, - self.init_calls as u64, - self.max_init_calls as u64, - self.init_calls as u64, - )); - } - self.last_init_result.clone() - } - - fn sign_proposal(&mut self, _: &BlockData) -> Result { - unimplemented!() - } - - fn sign_timeout_with_qc( - &mut self, - _: &TwoChainTimeout, - _: Option<&TwoChainTimeoutCertificate>, - ) -> Result { - unimplemented!() - } - - fn construct_and_sign_vote_two_chain( - &mut self, - _: &VoteProposal, - _: Option<&TwoChainTimeoutCertificate>, - ) -> Result { - unimplemented!() - } - - fn sign_commit_vote( - &mut self, - _: LedgerInfoWithSignatures, - _: LedgerInfo, - ) -> Result { - unimplemented!() - } - } - - #[test] - fn test_perform_initialize_ok() { - ::aptos_logger::Logger::init_for_testing(); - let (_, mock_storage) = EmptyStorage::start_for_testing(); - let mock_safety_rules = MockSafetyRules::new(0, 10, Ok(())); - let mut metric_safety_rules = - MetricsSafetyRules::new(Box::new(mock_safety_rules), mock_storage); - assert_ok!(metric_safety_rules.perform_initialize()); - } - - #[test] - fn test_perform_initialize_error() { - ::aptos_logger::Logger::init_for_testing(); - let (_, mock_storage) = EmptyStorage::start_for_testing(); - let mock_safety_rules = MockSafetyRules::new( - 0, - 10, - Err(Error::InvalidEpochChangeProof(String::from("Error"))), - ); - let mut metric_safety_rules = - MetricsSafetyRules::new(Box::new(mock_safety_rules), mock_storage); - assert_matches!( - metric_safety_rules.perform_initialize(), - Err(Error::InvalidEpochChangeProof(_)) - ); - } -} diff --git a/consensus/src/network.rs b/consensus/src/network.rs deleted file mode 100644 index 5da56774b06a7..0000000000000 --- a/consensus/src/network.rs +++ /dev/null @@ -1,844 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - counters, - dag::{ - DAGMessage, DAGNetworkMessage, DAGRpcResult, ProofNotifier, RpcWithFallback, - TDAGNetworkSender, - }, - logging::{LogEvent, LogSchema}, - monitor, - network_interface::{ConsensusMsg, ConsensusNetworkClient, RPC}, - pipeline::commit_reliable_broadcast::CommitMessage, - quorum_store::types::{Batch, BatchMsg, BatchRequest, BatchResponse}, - rand::rand_gen::network_messages::RandGenMessage, -}; -use anyhow::{anyhow, bail, ensure}; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::network_id::NetworkId; -use aptos_consensus_types::{ - block_retrieval::{BlockRetrievalRequest, BlockRetrievalResponse}, - common::Author, - pipeline::{commit_decision::CommitDecision, commit_vote::CommitVote}, - proof_of_store::{ProofOfStore, ProofOfStoreMsg, SignedBatchInfo, SignedBatchInfoMsg}, - proposal_msg::ProposalMsg, - sync_info::SyncInfo, - vote_msg::VoteMsg, -}; -use aptos_logger::prelude::*; -use aptos_network::{ - application::interface::{NetworkClient, NetworkServiceEvents}, - protocols::{network::Event, rpc::error::RpcError}, - ProtocolId, -}; -use aptos_reliable_broadcast::{RBMessage, RBNetworkSender}; -use aptos_types::{ - account_address::AccountAddress, epoch_change::EpochChangeProof, - ledger_info::LedgerInfoWithSignatures, validator_verifier::ValidatorVerifier, -}; -use async_trait::async_trait; -use bytes::Bytes; -use fail::fail_point; -use futures::{ - channel::oneshot, - stream::{select, select_all}, - SinkExt, Stream, StreamExt, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - mem::{discriminant, Discriminant}, - sync::Arc, - time::Duration, -}; -use tokio::time::timeout; - -pub trait TConsensusMsg: Sized + Serialize + DeserializeOwned { - fn epoch(&self) -> u64; - - fn from_network_message(msg: ConsensusMsg) -> anyhow::Result; - - fn into_network_message(self) -> ConsensusMsg; -} - -#[derive(Debug)] -pub struct RpcResponder { - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} - -impl RpcResponder { - pub fn respond(self, response: R) -> anyhow::Result<()> - where - R: TConsensusMsg, - { - let rpc_response = self - .protocol - .to_bytes(&response.into_network_message()) - .map(Bytes::from) - .map_err(RpcError::Error); - - self.response_sender - .send(rpc_response) - .map_err(|_| anyhow::anyhow!("unable to respond to rpc")) - } -} - -/// The block retrieval request is used internally for implementing RPC: the callback is executed -/// for carrying the response -#[derive(Debug)] -pub struct IncomingBlockRetrievalRequest { - pub req: BlockRetrievalRequest, - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} - -#[derive(Debug)] -pub struct IncomingBatchRetrievalRequest { - pub req: BatchRequest, - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} - -#[derive(Debug)] -pub struct IncomingDAGRequest { - pub req: DAGNetworkMessage, - pub sender: Author, - pub responder: RpcResponder, -} - -#[derive(Debug)] -pub struct IncomingCommitRequest { - pub req: CommitMessage, - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} - -#[derive(Debug)] -pub struct IncomingRandGenRequest { - pub req: RandGenMessage, - pub sender: Author, - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} - -#[derive(Debug)] -pub enum IncomingRpcRequest { - BlockRetrieval(IncomingBlockRetrievalRequest), - BatchRetrieval(IncomingBatchRetrievalRequest), - DAGRequest(IncomingDAGRequest), - CommitRequest(IncomingCommitRequest), - RandGenRequest(IncomingRandGenRequest), -} - -impl IncomingRpcRequest { - pub fn epoch(&self) -> Option { - match self { - IncomingRpcRequest::BatchRetrieval(req) => Some(req.req.epoch()), - IncomingRpcRequest::DAGRequest(req) => Some(req.req.epoch()), - IncomingRpcRequest::RandGenRequest(req) => Some(req.req.epoch()), - IncomingRpcRequest::CommitRequest(req) => req.req.epoch(), - IncomingRpcRequest::BlockRetrieval(_) => None, - } - } -} - -/// Just a convenience struct to keep all the network proxy receiving queues in one place. -/// Will be returned by the NetworkTask upon startup. -pub struct NetworkReceivers { - /// Provide a LIFO buffer for each (Author, MessageType) key - pub consensus_messages: aptos_channel::Receiver< - (AccountAddress, Discriminant), - (AccountAddress, ConsensusMsg), - >, - pub quorum_store_messages: aptos_channel::Receiver< - (AccountAddress, Discriminant), - (AccountAddress, ConsensusMsg), - >, - pub rpc_rx: aptos_channel::Receiver< - (AccountAddress, Discriminant), - (AccountAddress, IncomingRpcRequest), - >, -} - -#[async_trait::async_trait] -pub trait QuorumStoreSender: Send + Clone { - async fn send_batch_request(&self, request: BatchRequest, recipients: Vec); - - async fn request_batch( - &self, - request: BatchRequest, - recipient: Author, - timeout: Duration, - ) -> anyhow::Result; - - async fn send_batch(&self, batch: Batch, recipients: Vec); - - async fn send_signed_batch_info_msg( - &self, - signed_batch_infos: Vec, - recipients: Vec, - ); - - async fn broadcast_batch_msg(&mut self, batches: Vec); - - async fn broadcast_proof_of_store_msg(&mut self, proof_of_stores: Vec); - - async fn send_proof_of_store_msg_to_self(&mut self, proof_of_stores: Vec); -} - -/// Implements the actual networking support for all consensus messaging. -#[derive(Clone)] -pub struct NetworkSender { - author: Author, - consensus_network_client: ConsensusNetworkClient>, - // Self sender and self receivers provide a shortcut for sending the messages to itself. - // (self sending is not supported by the networking API). - self_sender: aptos_channels::UnboundedSender>, - validators: ValidatorVerifier, - time_service: aptos_time_service::TimeService, -} - -impl NetworkSender { - pub fn new( - author: Author, - consensus_network_client: ConsensusNetworkClient>, - self_sender: aptos_channels::UnboundedSender>, - validators: ValidatorVerifier, - ) -> Self { - NetworkSender { - author, - consensus_network_client, - self_sender, - validators, - time_service: aptos_time_service::TimeService::real(), - } - } - - /// Tries to retrieve num of blocks backwards starting from id from the given peer: the function - /// returns a future that is fulfilled with BlockRetrievalResponse. - pub async fn request_block( - &self, - retrieval_request: BlockRetrievalRequest, - from: Author, - timeout: Duration, - ) -> anyhow::Result { - fail_point!("consensus::send::any", |_| { - Err(anyhow::anyhow!("Injected error in request_block")) - }); - fail_point!("consensus::send::block_retrieval", |_| { - Err(anyhow::anyhow!("Injected error in request_block")) - }); - - ensure!(from != self.author, "Retrieve block from self"); - let msg = ConsensusMsg::BlockRetrievalRequest(Box::new(retrieval_request.clone())); - counters::CONSENSUS_SENT_MSGS - .with_label_values(&[msg.name()]) - .inc(); - let response_msg = monitor!( - "block_retrieval", - self.consensus_network_client - .send_rpc(from, msg, timeout) - .await - )?; - let response = match response_msg { - ConsensusMsg::BlockRetrievalResponse(resp) => *resp, - _ => return Err(anyhow!("Invalid response to request")), - }; - response - .verify(retrieval_request, &self.validators) - .map_err(|e| { - error!( - SecurityEvent::InvalidRetrievedBlock, - request_block_response = response, - error = ?e, - ); - e - })?; - - Ok(response) - } - - pub async fn send_rpc( - &self, - receiver: Author, - msg: ConsensusMsg, - timeout_duration: Duration, - ) -> anyhow::Result { - fail_point!("consensus::send::any", |_| { - Err(anyhow::anyhow!("Injected error in send_rpc")) - }); - counters::CONSENSUS_SENT_MSGS - .with_label_values(&[msg.name()]) - .inc(); - if receiver == self.author() { - let (tx, rx) = oneshot::channel(); - let protocol = RPC[0]; - let self_msg = Event::RpcRequest(receiver, msg.clone(), RPC[0], tx); - self.self_sender.clone().send(self_msg).await?; - if let Ok(Ok(Ok(bytes))) = timeout(timeout_duration, rx).await { - Ok(protocol.from_bytes(&bytes)?) - } else { - bail!("self rpc failed"); - } - } else { - Ok(monitor!( - "send_rpc", - self.consensus_network_client - .send_rpc(receiver, msg, timeout_duration) - .await - )?) - } - } - - /// Tries to send the given msg to all the participants. - /// - /// The future is fulfilled as soon as the message is put into the mpsc channel to network - /// internal (to provide back pressure), it does not indicate the message is delivered or sent - /// out. - async fn broadcast(&self, msg: ConsensusMsg) { - fail_point!("consensus::send::any", |_| ()); - // Directly send the message to ourself without going through network. - let self_msg = Event::Message(self.author, msg.clone()); - let mut self_sender = self.self_sender.clone(); - if let Err(err) = self_sender.send(self_msg).await { - error!("Error broadcasting to self: {:?}", err); - } - - // Get the list of validators excluding our own account address. Note the - // ordering is not important in this case. - let self_author = self.author; - let other_validators: Vec<_> = self - .validators - .get_ordered_account_addresses_iter() - .filter(|author| author != &self_author) - .collect(); - - counters::CONSENSUS_SENT_MSGS - .with_label_values(&[msg.name()]) - .inc_by(other_validators.len() as u64); - // Broadcast message over direct-send to all other validators. - if let Err(err) = self - .consensus_network_client - .send_to_many(other_validators.into_iter(), msg) - { - warn!(error = ?err, "Error broadcasting message"); - } - } - - pub fn broadcast_without_self(&self, msg: ConsensusMsg) { - let self_author = self.author; - let other_validators: Vec<_> = self - .validators - .get_ordered_account_addresses_iter() - .filter(|author| author != &self_author) - .collect(); - - counters::CONSENSUS_SENT_MSGS - .with_label_values(&[msg.name()]) - .inc_by(other_validators.len() as u64); - // Broadcast message over direct-send to all other validators. - if let Err(err) = self - .consensus_network_client - .send_to_many(other_validators.into_iter(), msg) - { - warn!(error = ?err, "Error broadcasting message"); - } - } - - /// Tries to send msg to given recipients. - async fn send(&self, msg: ConsensusMsg, recipients: Vec) { - fail_point!("consensus::send::any", |_| ()); - let network_sender = self.consensus_network_client.clone(); - let mut self_sender = self.self_sender.clone(); - for peer in recipients { - if self.author == peer { - let self_msg = Event::Message(self.author, msg.clone()); - if let Err(err) = self_sender.send(self_msg).await { - error!(error = ?err, "Error delivering a self msg"); - } - continue; - } - counters::CONSENSUS_SENT_MSGS - .with_label_values(&[msg.name()]) - .inc(); - if let Err(e) = network_sender.send_to(peer, msg.clone()) { - warn!( - remote_peer = peer, - error = ?e, "Failed to send a msg to peer", - ); - } - } - } - - pub async fn broadcast_proposal(&self, proposal_msg: ProposalMsg) { - fail_point!("consensus::send::broadcast_proposal", |_| ()); - let msg = ConsensusMsg::ProposalMsg(Box::new(proposal_msg)); - self.broadcast(msg).await - } - - pub async fn broadcast_sync_info(&self, sync_info_msg: SyncInfo) { - fail_point!("consensus::send::broadcast_sync_info", |_| ()); - let msg = ConsensusMsg::SyncInfo(Box::new(sync_info_msg)); - self.broadcast(msg).await - } - - pub async fn broadcast_timeout_vote(&self, timeout_vote_msg: VoteMsg) { - fail_point!("consensus::send::broadcast_timeout_vote", |_| ()); - let msg = ConsensusMsg::VoteMsg(Box::new(timeout_vote_msg)); - self.broadcast(msg).await - } - - pub async fn broadcast_epoch_change(&self, epoch_change_proof: EpochChangeProof) { - fail_point!("consensus::send::broadcast_epoch_change", |_| ()); - let msg = ConsensusMsg::EpochChangeProof(Box::new(epoch_change_proof)); - self.broadcast(msg).await - } - - pub async fn send_commit_vote( - &self, - commit_vote: CommitVote, - recipient: Author, - ) -> anyhow::Result<()> { - fail_point!("consensus::send::commit_vote", |_| Ok(())); - let msg = ConsensusMsg::CommitMessage(Box::new(CommitMessage::Vote(commit_vote))); - self.send_rpc(recipient, msg, Duration::from_millis(500)) - .await - .map(|_| ()) - } - - pub async fn broadcast_vote(&self, vote_msg: VoteMsg) { - fail_point!("consensus::send::vote", |_| ()); - let msg = ConsensusMsg::VoteMsg(Box::new(vote_msg)); - self.broadcast(msg).await - } - - /// Sends the vote to the chosen recipients (typically that would be the recipients that - /// we believe could serve as proposers in the next round). The recipients on the receiving - /// end are going to be notified about a new vote in the vote queue. - /// - /// The future is fulfilled as soon as the message put into the mpsc channel to network - /// internal(to provide back pressure), it does not indicate the message is delivered or sent - /// out. It does not give indication about when the message is delivered to the recipients, - /// as well as there is no indication about the network failures. - pub async fn send_vote(&self, vote_msg: VoteMsg, recipients: Vec) { - fail_point!("consensus::send::vote", |_| ()); - let msg = ConsensusMsg::VoteMsg(Box::new(vote_msg)); - self.send(msg, recipients).await - } - - #[cfg(feature = "failpoints")] - pub async fn send_proposal(&self, proposal_msg: ProposalMsg, recipients: Vec) { - fail_point!("consensus::send::proposal", |_| ()); - let msg = ConsensusMsg::ProposalMsg(Box::new(proposal_msg)); - self.send(msg, recipients).await - } - - pub async fn send_epoch_change(&self, proof: EpochChangeProof) { - fail_point!("consensus::send::epoch_change", |_| ()); - let msg = ConsensusMsg::EpochChangeProof(Box::new(proof)); - self.send(msg, vec![self.author]).await - } - - /// Sends the ledger info to self buffer manager - pub async fn send_commit_proof(&self, ledger_info: LedgerInfoWithSignatures) { - fail_point!("consensus::send::commit_decision", |_| ()); - let msg = ConsensusMsg::CommitMessage(Box::new(CommitMessage::Decision( - CommitDecision::new(ledger_info), - ))); - let _ = self - .send_rpc(self.author, msg, Duration::from_millis(500)) - .await; - } - - pub fn author(&self) -> Author { - self.author - } -} - -#[async_trait::async_trait] -impl QuorumStoreSender for NetworkSender { - async fn send_batch_request(&self, request: BatchRequest, recipients: Vec) { - fail_point!("consensus::send::batch_request", |_| ()); - let msg = ConsensusMsg::BatchRequestMsg(Box::new(request)); - self.send(msg, recipients).await - } - - async fn request_batch( - &self, - request: BatchRequest, - recipient: Author, - timeout: Duration, - ) -> anyhow::Result { - let request_digest = request.digest(); - let msg = ConsensusMsg::BatchRequestMsg(Box::new(request)); - let response = self - .consensus_network_client - .send_rpc(recipient, msg, timeout) - .await?; - match response { - // TODO: deprecated, remove after another release (likely v1.11) - ConsensusMsg::BatchResponse(batch) => { - batch.verify_with_digest(request_digest)?; - Ok(BatchResponse::Batch(*batch)) - }, - ConsensusMsg::BatchResponseV2(maybe_batch) => { - if let BatchResponse::Batch(batch) = maybe_batch.as_ref() { - batch.verify_with_digest(request_digest)?; - } - // Note BatchResponse::NotFound(ledger_info) is verified later with a ValidatorVerifier - Ok(*maybe_batch) - }, - _ => Err(anyhow!("Invalid batch response")), - } - } - - async fn send_batch(&self, batch: Batch, recipients: Vec) { - fail_point!("consensus::send::batch", |_| ()); - let msg = ConsensusMsg::BatchResponse(Box::new(batch)); - self.send(msg, recipients).await - } - - async fn send_signed_batch_info_msg( - &self, - signed_batch_infos: Vec, - recipients: Vec, - ) { - fail_point!("consensus::send::signed_batch_info", |_| ()); - let msg = - ConsensusMsg::SignedBatchInfo(Box::new(SignedBatchInfoMsg::new(signed_batch_infos))); - self.send(msg, recipients).await - } - - async fn broadcast_batch_msg(&mut self, batches: Vec) { - fail_point!("consensus::send::broadcast_batch", |_| ()); - let msg = ConsensusMsg::BatchMsg(Box::new(BatchMsg::new(batches))); - self.broadcast(msg).await - } - - async fn broadcast_proof_of_store_msg(&mut self, proofs: Vec) { - fail_point!("consensus::send::proof_of_store", |_| ()); - let msg = ConsensusMsg::ProofOfStoreMsg(Box::new(ProofOfStoreMsg::new(proofs))); - self.broadcast(msg).await - } - - async fn send_proof_of_store_msg_to_self(&mut self, proofs: Vec) { - fail_point!("consensus::send::proof_of_store", |_| ()); - let msg = ConsensusMsg::ProofOfStoreMsg(Box::new(ProofOfStoreMsg::new(proofs))); - self.send(msg, vec![self.author]).await - } -} - -#[async_trait] -impl TDAGNetworkSender for NetworkSender { - async fn send_rpc( - &self, - receiver: Author, - message: DAGMessage, - timeout: Duration, - ) -> anyhow::Result { - self.send_rpc(receiver, message.into_network_message(), timeout) - .await - .map_err(|e| anyhow!("invalid rpc response: {}", e)) - .and_then(TConsensusMsg::from_network_message) - } - - /// Given a list of potential responders, sending rpc to get response from any of them and could - /// fallback to more in case of failures. - async fn send_rpc_with_fallbacks( - self: Arc, - responders: Vec, - message: DAGMessage, - retry_interval: Duration, - rpc_timeout: Duration, - min_concurrent_responders: u32, - max_concurrent_responders: u32, - ) -> RpcWithFallback { - RpcWithFallback::new( - responders, - message, - retry_interval, - rpc_timeout, - self.clone(), - self.time_service.clone(), - min_concurrent_responders, - max_concurrent_responders, - ) - } -} - -#[async_trait] -impl - RBNetworkSender for NetworkSender -{ - async fn send_rb_rpc( - &self, - receiver: Author, - message: Req, - timeout: Duration, - ) -> anyhow::Result { - self.send_rpc(receiver, message.into_network_message(), timeout) - .await - .map_err(|e| anyhow!("invalid rpc response: {}", e)) - .and_then(TConsensusMsg::from_network_message) - } -} - -#[async_trait] -impl ProofNotifier for NetworkSender { - async fn send_epoch_change(&self, proof: EpochChangeProof) { - self.send_epoch_change(proof).await - } - - async fn send_commit_proof(&self, ledger_info: LedgerInfoWithSignatures) { - self.send_commit_proof(ledger_info).await - } -} - -pub struct NetworkTask { - consensus_messages_tx: aptos_channel::Sender< - (AccountAddress, Discriminant), - (AccountAddress, ConsensusMsg), - >, - quorum_store_messages_tx: aptos_channel::Sender< - (AccountAddress, Discriminant), - (AccountAddress, ConsensusMsg), - >, - rpc_tx: aptos_channel::Sender< - (AccountAddress, Discriminant), - (AccountAddress, IncomingRpcRequest), - >, - all_events: Box> + Send + Unpin>, -} - -impl NetworkTask { - /// Establishes the initial connections with the peers and returns the receivers. - pub fn new( - network_service_events: NetworkServiceEvents, - self_receiver: aptos_channels::UnboundedReceiver>, - ) -> (NetworkTask, NetworkReceivers) { - let (consensus_messages_tx, consensus_messages) = aptos_channel::new( - QueueStyle::FIFO, - 10, - Some(&counters::CONSENSUS_CHANNEL_MSGS), - ); - let (quorum_store_messages_tx, quorum_store_messages) = aptos_channel::new( - QueueStyle::FIFO, - // TODO: tune this value based on quorum store messages with backpressure - 50, - Some(&counters::QUORUM_STORE_CHANNEL_MSGS), - ); - let (rpc_tx, rpc_rx) = - aptos_channel::new(QueueStyle::FIFO, 10, Some(&counters::RPC_CHANNEL_MSGS)); - - // Verify the network events have been constructed correctly - let network_and_events = network_service_events.into_network_and_events(); - if (network_and_events.values().len() != 1) - || !network_and_events.contains_key(&NetworkId::Validator) - { - panic!("The network has not been setup correctly for consensus!"); - } - - // Collect all the network events into a single stream - let network_events: Vec<_> = network_and_events.into_values().collect(); - let network_events = select_all(network_events).fuse(); - let all_events = Box::new(select(network_events, self_receiver)); - - ( - NetworkTask { - consensus_messages_tx, - quorum_store_messages_tx, - rpc_tx, - all_events, - }, - NetworkReceivers { - consensus_messages, - quorum_store_messages, - rpc_rx, - }, - ) - } - - fn push_msg( - peer_id: AccountAddress, - msg: ConsensusMsg, - tx: &aptos_channel::Sender< - (AccountAddress, Discriminant), - (AccountAddress, ConsensusMsg), - >, - ) { - if let Err(e) = tx.push((peer_id, discriminant(&msg)), (peer_id, msg)) { - warn!( - remote_peer = peer_id, - error = ?e, "Error pushing consensus msg", - ); - } - } - - pub async fn start(mut self) { - while let Some(message) = self.all_events.next().await { - monitor!("network_main_loop", match message { - Event::Message(peer_id, msg) => { - counters::CONSENSUS_RECEIVED_MSGS - .with_label_values(&[msg.name()]) - .inc(); - match msg { - quorum_store_msg @ (ConsensusMsg::SignedBatchInfo(_) - | ConsensusMsg::BatchMsg(_) - | ConsensusMsg::ProofOfStoreMsg(_)) => { - Self::push_msg( - peer_id, - quorum_store_msg, - &self.quorum_store_messages_tx, - ); - }, - // Remove after migration to use rpc. - ConsensusMsg::CommitVoteMsg(commit_vote) => { - let (tx, _rx) = oneshot::channel(); - let req_with_callback = - IncomingRpcRequest::CommitRequest(IncomingCommitRequest { - req: CommitMessage::Vote(*commit_vote), - protocol: RPC[0], - response_sender: tx, - }); - if let Err(e) = self.rpc_tx.push( - (peer_id, discriminant(&req_with_callback)), - (peer_id, req_with_callback), - ) { - warn!(error = ?e, "aptos channel closed"); - }; - }, - ConsensusMsg::CommitDecisionMsg(commit_decision) => { - let (tx, _rx) = oneshot::channel(); - let req_with_callback = - IncomingRpcRequest::CommitRequest(IncomingCommitRequest { - req: CommitMessage::Decision(*commit_decision), - protocol: RPC[0], - response_sender: tx, - }); - if let Err(e) = self.rpc_tx.push( - (peer_id, discriminant(&req_with_callback)), - (peer_id, req_with_callback), - ) { - warn!(error = ?e, "aptos channel closed"); - }; - }, - consensus_msg @ (ConsensusMsg::ProposalMsg(_) - | ConsensusMsg::VoteMsg(_) - | ConsensusMsg::SyncInfo(_) - | ConsensusMsg::EpochRetrievalRequest(_) - | ConsensusMsg::EpochChangeProof(_)) => { - if let ConsensusMsg::ProposalMsg(proposal) = &consensus_msg { - observe_block( - proposal.proposal().timestamp_usecs(), - BlockStage::NETWORK_RECEIVED, - ); - info!( - LogSchema::new(LogEvent::NetworkReceiveProposal) - .remote_peer(peer_id), - block_round = proposal.proposal().round(), - block_hash = proposal.proposal().id(), - ); - } - Self::push_msg(peer_id, consensus_msg, &self.consensus_messages_tx); - }, - // TODO: get rid of the rpc dummy value - ConsensusMsg::RandGenMessage(req) => { - let (tx, _rx) = oneshot::channel(); - let req_with_callback = - IncomingRpcRequest::RandGenRequest(IncomingRandGenRequest { - req, - sender: peer_id, - protocol: RPC[0], - response_sender: tx, - }); - if let Err(e) = self.rpc_tx.push( - (peer_id, discriminant(&req_with_callback)), - (peer_id, req_with_callback), - ) { - warn!(error = ?e, "aptos channel closed"); - }; - }, - _ => { - warn!(remote_peer = peer_id, "Unexpected direct send msg"); - continue; - }, - } - }, - Event::RpcRequest(peer_id, msg, protocol, callback) => { - counters::CONSENSUS_RECEIVED_MSGS - .with_label_values(&[msg.name()]) - .inc(); - let req = match msg { - ConsensusMsg::BlockRetrievalRequest(request) => { - debug!( - remote_peer = peer_id, - event = LogEvent::ReceiveBlockRetrieval, - "{}", - request - ); - IncomingRpcRequest::BlockRetrieval(IncomingBlockRetrievalRequest { - req: *request, - protocol, - response_sender: callback, - }) - }, - ConsensusMsg::BatchRequestMsg(request) => { - debug!( - remote_peer = peer_id, - event = LogEvent::ReceiveBatchRetrieval, - "{}", - request - ); - IncomingRpcRequest::BatchRetrieval(IncomingBatchRetrievalRequest { - req: *request, - protocol, - response_sender: callback, - }) - }, - ConsensusMsg::DAGMessage(req) => { - IncomingRpcRequest::DAGRequest(IncomingDAGRequest { - req, - sender: peer_id, - responder: RpcResponder { - protocol, - response_sender: callback, - }, - }) - }, - ConsensusMsg::CommitMessage(req) => { - IncomingRpcRequest::CommitRequest(IncomingCommitRequest { - req: *req, - protocol, - response_sender: callback, - }) - }, - ConsensusMsg::RandGenMessage(req) => { - IncomingRpcRequest::RandGenRequest(IncomingRandGenRequest { - req, - sender: peer_id, - protocol, - response_sender: callback, - }) - }, - _ => { - warn!(remote_peer = peer_id, "Unexpected msg: {:?}", msg); - continue; - }, - }; - if let Err(e) = self - .rpc_tx - .push((peer_id, discriminant(&req)), (peer_id, req)) - { - warn!(error = ?e, "aptos channel closed"); - }; - }, - _ => { - // Ignore `NewPeer` and `LostPeer` events - }, - }); - } - } -} diff --git a/consensus/src/network_interface.rs b/consensus/src/network_interface.rs deleted file mode 100644 index 16f7d06d3587d..0000000000000 --- a/consensus/src/network_interface.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! Interface between Consensus and Network layers. - -use crate::{ - dag::DAGNetworkMessage, - pipeline, - quorum_store::types::{Batch, BatchMsg, BatchRequest, BatchResponse}, - rand::rand_gen::network_messages::RandGenMessage, -}; -use aptos_config::network_id::{NetworkId, PeerNetworkId}; -use aptos_consensus_types::{ - block_retrieval::{BlockRetrievalRequest, BlockRetrievalResponse}, - epoch_retrieval::EpochRetrievalRequest, - pipeline::{commit_decision::CommitDecision, commit_vote::CommitVote}, - proof_of_store::{ProofOfStoreMsg, SignedBatchInfoMsg}, - proposal_msg::ProposalMsg, - sync_info::SyncInfo, - vote_msg::VoteMsg, -}; -use aptos_network::{ - application::{error::Error, interface::NetworkClientInterface}, - ProtocolId, -}; -use aptos_types::{epoch_change::EpochChangeProof, PeerId}; -pub use pipeline::commit_reliable_broadcast::CommitMessage; -use serde::{Deserialize, Serialize}; -use std::time::Duration; - -/// Network type for consensus -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum ConsensusMsg { - /// RPC to get a chain of block of the given length starting from the given block id. - BlockRetrievalRequest(Box), - /// Carries the returned blocks and the retrieval status. - BlockRetrievalResponse(Box), - /// Request to get a EpochChangeProof from current_epoch to target_epoch - EpochRetrievalRequest(Box), - /// ProposalMsg contains the required information for the proposer election protocol to make - /// its choice (typically depends on round and proposer info). - ProposalMsg(Box), - /// This struct describes basic synchronization metadata. - SyncInfo(Box), - /// A vector of LedgerInfo with contiguous increasing epoch numbers to prove a sequence of - /// epoch changes from the first LedgerInfo's epoch. - EpochChangeProof(Box), - /// VoteMsg is the struct that is ultimately sent by the voter in response for receiving a - /// proposal. - VoteMsg(Box), - /// CommitProposal is the struct that is sent by the validator after execution to propose - /// on the committed state hash root. - CommitVoteMsg(Box), - /// CommitDecision is the struct that is sent by the validator after collecting no fewer - /// than 2f + 1 signatures on the commit proposal. This part is not on the critical path, but - /// it can save slow machines to quickly confirm the execution result. - CommitDecisionMsg(Box), - /// Quorum Store: Send a Batch of transactions. - BatchMsg(Box), - /// Quorum Store: Request the payloads of a completed batch. - BatchRequestMsg(Box), - /// Quorum Store: Response to the batch request. - BatchResponse(Box), - /// Quorum Store: Send a signed batch digest. This is a vote for the batch and a promise that - /// the batch of transactions was received and will be persisted until batch expiration. - SignedBatchInfo(Box), - /// Quorum Store: Broadcast a certified proof of store (a digest that received 2f+1 votes). - ProofOfStoreMsg(Box), - /// DAG protocol message - DAGMessage(DAGNetworkMessage), - /// Commit message - CommitMessage(Box), - /// Randomness generation message - RandGenMessage(RandGenMessage), - /// Quorum Store: Response to the batch request. - BatchResponseV2(Box), -} - -/// Network type for consensus -impl ConsensusMsg { - /// ConsensusMsg type in string - /// - pub fn name(&self) -> &str { - match self { - ConsensusMsg::BlockRetrievalRequest(_) => "BlockRetrievalRequest", - ConsensusMsg::BlockRetrievalResponse(_) => "BlockRetrievalResponse", - ConsensusMsg::EpochRetrievalRequest(_) => "EpochRetrievalRequest", - ConsensusMsg::ProposalMsg(_) => "ProposalMsg", - ConsensusMsg::SyncInfo(_) => "SyncInfo", - ConsensusMsg::EpochChangeProof(_) => "EpochChangeProof", - ConsensusMsg::VoteMsg(_) => "VoteMsg", - ConsensusMsg::CommitVoteMsg(_) => "CommitVoteMsg", - ConsensusMsg::CommitDecisionMsg(_) => "CommitDecisionMsg", - ConsensusMsg::BatchMsg(_) => "BatchMsg", - ConsensusMsg::BatchRequestMsg(_) => "BatchRequestMsg", - ConsensusMsg::BatchResponse(_) => "BatchResponse", - ConsensusMsg::SignedBatchInfo(_) => "SignedBatchInfo", - ConsensusMsg::ProofOfStoreMsg(_) => "ProofOfStoreMsg", - ConsensusMsg::DAGMessage(_) => "DAGMessage", - ConsensusMsg::CommitMessage(_) => "CommitMessage", - ConsensusMsg::RandGenMessage(_) => "RandGenMessage", - ConsensusMsg::BatchResponseV2(_) => "BatchResponseV2", - } - } -} - -/// The interface from Consensus to Networking layer. -/// -/// This is a thin wrapper around a `NetworkClient`, so it is easy -/// to clone and send off to a separate task. For example, the rpc requests -/// return Futures that encapsulate the whole flow, from sending the request to -/// remote, to finally receiving the response and deserializing. It therefore -/// makes the most sense to make the rpc call on a separate async task, which -/// requires the `ConsensusNetworkClient` to be `Clone` and `Send`. -#[derive(Clone)] -pub struct ConsensusNetworkClient { - network_client: NetworkClient, -} - -/// Supported protocols in preferred order (from highest priority to lowest). -pub const RPC: &[ProtocolId] = &[ - ProtocolId::ConsensusRpcCompressed, - ProtocolId::ConsensusRpcBcs, - ProtocolId::ConsensusRpcJson, -]; - -/// Supported protocols in preferred order (from highest priority to lowest). -pub const DIRECT_SEND: &[ProtocolId] = &[ - ProtocolId::ConsensusDirectSendCompressed, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusDirectSendJson, -]; - -impl> ConsensusNetworkClient { - /// Returns a new consensus network client - pub fn new(network_client: NetworkClient) -> Self { - Self { network_client } - } - - /// Send a single message to the destination peer - pub fn send_to(&self, peer: PeerId, message: ConsensusMsg) -> Result<(), Error> { - let peer_network_id = self.get_peer_network_id_for_peer(peer); - self.network_client.send_to_peer(message, peer_network_id) - } - - /// Send a single message to the destination peers - pub fn send_to_many( - &self, - peers: impl Iterator, - message: ConsensusMsg, - ) -> Result<(), Error> { - let peer_network_ids: Vec = peers - .map(|peer| self.get_peer_network_id_for_peer(peer)) - .collect(); - self.network_client - .send_to_peers(message, &peer_network_ids) - } - - /// Send a RPC to the destination peer - pub async fn send_rpc( - &self, - peer: PeerId, - message: ConsensusMsg, - rpc_timeout: Duration, - ) -> Result { - let peer_network_id = self.get_peer_network_id_for_peer(peer); - self.network_client - .send_to_peer_rpc(message, rpc_timeout, peer_network_id) - .await - } - - // TODO: we shouldn't need to expose this. Migrate the code to handle - // peer and network ids. - fn get_peer_network_id_for_peer(&self, peer: PeerId) -> PeerNetworkId { - PeerNetworkId::new(NetworkId::Validator, peer) - } -} diff --git a/consensus/src/network_tests.rs b/consensus/src/network_tests.rs deleted file mode 100644 index db037975aa4dd..0000000000000 --- a/consensus/src/network_tests.rs +++ /dev/null @@ -1,857 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::{NetworkReceivers, NetworkSender}, - network_interface::{ConsensusMsg, ConsensusNetworkClient}, - test_utils::{self, consensus_runtime, placeholder_ledger_info, timed_block_on}, -}; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::network_id::NetworkId; -use aptos_consensus_types::{ - block::{block_test_utils::certificate_for_genesis, Block}, - common::Author, - proposal_msg::ProposalMsg, - sync_info::SyncInfo, - vote::Vote, - vote_data::VoteData, - vote_msg::VoteMsg, -}; -use aptos_infallible::{Mutex, RwLock}; -use aptos_network::{ - application::storage::PeersAndMetadata, - peer_manager::{ - conn_notifs_channel, ConnectionRequestSender, PeerManagerNotification, PeerManagerRequest, - PeerManagerRequestSender, - }, - protocols::{ - network::{NewNetworkEvents, SerializedRequest}, - rpc::InboundRpcRequest, - wire::handshake::v1::ProtocolIdSet, - }, - ProtocolId, -}; -use aptos_types::{block_info::BlockInfo, PeerId}; -use futures::{channel::mpsc, SinkExt, StreamExt}; -use std::{ - collections::{HashMap, HashSet}, - iter::FromIterator, - sync::Arc, - time::Duration, -}; -use tokio::runtime::Handle; - -/// `TwinId` is used by the NetworkPlayground to uniquely identify -/// nodes, even if they have the same `AccountAddress` (e.g. for Twins) -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct TwinId { - /// Node's ID - pub id: usize, - /// Author (AccountAddress) - pub author: Author, -} - -/// `NetworkPlayground` mocks the network implementation and provides convenience -/// methods for testing. Test clients can use `wait_for_messages` or -/// `deliver_messages` to inspect the direct-send messages sent between peers. -/// They can also configure network messages to be dropped between specific peers. -/// -/// Currently, RPC messages are delivered immediately and are not controlled by -/// `wait_for_messages` or `deliver_messages` for delivery. They are also not -/// currently dropped according to the `NetworkPlayground`'s drop config. -pub struct NetworkPlayground { - /// Maps each Author to a Sender of their inbound network notifications. - /// These events will usually be handled by the event loop spawned in - /// `ConsensusNetworkImpl`. - /// - node_consensus_txs: Arc< - Mutex< - HashMap>, - >, - >, - /// Nodes' outbound handlers forward their outbound non-rpc messages to this - /// queue. - outbound_msgs_tx: mpsc::Sender<(TwinId, PeerManagerRequest)>, - /// NetworkPlayground reads all nodes' outbound messages through this queue. - outbound_msgs_rx: mpsc::Receiver<(TwinId, PeerManagerRequest)>, - /// Allow test code to drop direct-send messages between peers. - drop_config: Arc>, - /// Allow test code to drop direct-send messages between peers per round. - drop_config_round: DropConfigRound, - /// An executor for spawning node outbound network event handlers - executor: Handle, - /// Maps authors to twins IDs - /// An author may have multiple twin IDs for Twins - author_to_twin_ids: Arc>, - /// Information about connections - peers_and_metadata: Arc, -} - -impl NetworkPlayground { - pub fn new(executor: Handle) -> Self { - let (outbound_msgs_tx, outbound_msgs_rx) = mpsc::channel(1_024); - - NetworkPlayground { - node_consensus_txs: Arc::new(Mutex::new(HashMap::new())), - outbound_msgs_tx, - outbound_msgs_rx, - drop_config: Arc::new(RwLock::new(DropConfig::default())), - drop_config_round: DropConfigRound::default(), - executor, - author_to_twin_ids: Arc::new(RwLock::new(AuthorToTwinIds::default())), - peers_and_metadata: PeersAndMetadata::new(&[NetworkId::Validator]), - } - } - - pub fn handle(&self) -> Handle { - self.executor.clone() - } - - /// HashMap of supported protocols to initialize ConsensusNetworkClient. - pub fn peer_protocols(&self) -> Arc { - self.peers_and_metadata.clone() - } - - /// Create a new async task that handles outbound messages sent by a node. - /// - /// All non-rpc messages are forwarded to the NetworkPlayground's - /// `outbound_msgs_rx` queue, which controls delivery through the - /// `deliver_messages` and `wait_for_messages` API's. - /// - /// Rpc messages are immediately sent to the destination for handling, so - /// they don't block. - async fn start_node_outbound_handler( - drop_config: Arc>, - src_twin_id: TwinId, - mut network_reqs_rx: aptos_channel::Receiver<(PeerId, ProtocolId), PeerManagerRequest>, - mut outbound_msgs_tx: mpsc::Sender<(TwinId, PeerManagerRequest)>, - node_consensus_txs: Arc< - Mutex< - HashMap< - TwinId, - aptos_channel::Sender<(PeerId, ProtocolId), PeerManagerNotification>, - >, - >, - >, - author_to_twin_ids: Arc>, - ) { - while let Some(net_req) = network_reqs_rx.next().await { - match net_req { - // Immediately forward rpc requests for handling. Unfortunately, - // we can't handle rpc requests in `deliver_messages` due to - // blocking issues, e.g., I want to write: - // ``` - // let block = sender.request_block(peer_id, block_id).await.unwrap(); - // playground.wait_for_messages(1).await; - // ``` - // but because the rpc call blocks and depends on the message - // delivery, we'd have to spawn the sending behaviour on a - // separate task, which is inconvenient. - PeerManagerRequest::SendRpc(dst, outbound_req) => { - let dst_twin_ids = author_to_twin_ids.read().get_twin_ids(dst); - - let dst_twin_id = match dst_twin_ids.iter().find(|dst_twin_id| { - !drop_config - .read() - .is_message_dropped(&src_twin_id, dst_twin_id) - }) { - Some(id) => id, - None => continue, // drop rpc - }; - - let node_consensus_tx = - node_consensus_txs.lock().get(dst_twin_id).unwrap().clone(); - - let inbound_req = InboundRpcRequest { - protocol_id: outbound_req.protocol_id, - data: outbound_req.data, - res_tx: outbound_req.res_tx, - }; - - node_consensus_tx - .push( - (src_twin_id.author, ProtocolId::ConsensusRpcBcs), - PeerManagerNotification::RecvRpc(src_twin_id.author, inbound_req), - ) - .unwrap(); - }, - // Other PeerManagerRequest get buffered for `deliver_messages` to - // synchronously drain. - net_req => { - let _ = outbound_msgs_tx.send((src_twin_id, net_req)).await; - }, - } - } - } - - /// Add a new node to the NetworkPlayground. - pub fn add_node( - &mut self, - twin_id: TwinId, - consensus_tx: aptos_channel::Sender<(PeerId, ProtocolId), PeerManagerNotification>, - network_reqs_rx: aptos_channel::Receiver<(PeerId, ProtocolId), PeerManagerRequest>, - conn_mgr_reqs_rx: aptos_channels::Receiver, - ) { - self.node_consensus_txs.lock().insert(twin_id, consensus_tx); - self.drop_config.write().add_node(twin_id); - - self.extend_author_to_twin_ids(twin_id.author, twin_id); - - let fut1 = NetworkPlayground::start_node_outbound_handler( - Arc::clone(&self.drop_config), - twin_id, - network_reqs_rx, - self.outbound_msgs_tx.clone(), - self.node_consensus_txs.clone(), - self.author_to_twin_ids.clone(), - ); - let fut2 = conn_mgr_reqs_rx.map(Ok).forward(::futures::sink::drain()); - self.executor.spawn(futures::future::join(fut1, fut2)); - } - - /// Deliver a `PeerManagerRequest` from peer `src` to the destination peer. - /// Returns a copy of the delivered message and the sending peer id, and - /// whether the message was successfully delivered - async fn deliver_message( - &mut self, - src_twin_id: TwinId, - dst_twin_id: TwinId, - msg_notif: PeerManagerNotification, - ) -> (Author, ConsensusMsg) { - let node_consensus_tx = self - .node_consensus_txs - .lock() - .get(&dst_twin_id) - .unwrap() - .clone(); - - // copy message data - let msg_copy = match &msg_notif { - PeerManagerNotification::RecvMessage(src, msg) => { - let msg: ConsensusMsg = msg.to_message().unwrap(); - (*src, msg) - }, - msg_notif => panic!( - "[network playground] Unexpected PeerManagerNotification: {:?}", - msg_notif - ), - }; - let _ = node_consensus_tx.push( - (src_twin_id.author, ProtocolId::ConsensusDirectSendBcs), - msg_notif, - ); - msg_copy - } - - /// Wait for exactly `num_messages` to be enqueued and delivered. Return a - /// copy of all messages for verification. - /// While all the sent messages are delivered (except those configured to be dropped), - /// only the messages that satisfy the given msg inspector are counted. - pub async fn wait_for_messages( - &mut self, - num_messages: usize, - msg_inspector: F, - ) -> Vec<(Author, ConsensusMsg)> - where - F: Fn(&(Author, ConsensusMsg)) -> bool, - { - let mut msg_copies = vec![]; - while msg_copies.len() < num_messages { - // Take the next queued message - let (src_twin_id, net_req) = self.outbound_msgs_rx.next().await - .expect("[network playground] waiting for messages, but message queue has shutdown unexpectedly"); - - // Convert PeerManagerRequest to corresponding PeerManagerNotification, - // and extract destination peer - let (dst, msg) = match &net_req { - PeerManagerRequest::SendDirectSend(dst_inner, msg_inner) => { - (*dst_inner, msg_inner.clone()) - }, - msg_inner => panic!( - "[network playground] Unexpected PeerManagerRequest: {:?}", - msg_inner - ), - }; - - let dst_twin_ids = self.get_twin_ids(dst); - for (idx, dst_twin_id) in dst_twin_ids.iter().enumerate() { - let consensus_msg = msg.to_message().unwrap(); - - // Deliver and copy message if it's not dropped - if !self.is_message_dropped(&src_twin_id, dst_twin_id, consensus_msg) { - let msg_notif = - PeerManagerNotification::RecvMessage(src_twin_id.author, msg.clone()); - let msg_copy = self - .deliver_message(src_twin_id, *dst_twin_id, msg_notif) - .await; - - // Only insert msg_copy once for twins (if delivered) - if idx == 0 && msg_inspector(&msg_copy) { - msg_copies.push(msg_copy); - } - } - } - } - assert_eq!(msg_copies.len(), num_messages); - msg_copies - } - - /// Return the round of a given message - fn get_message_round(msg: ConsensusMsg) -> Option { - match msg { - ConsensusMsg::ProposalMsg(proposal_msg) => Some(proposal_msg.proposal().round()), - ConsensusMsg::VoteMsg(vote_msg) => Some(vote_msg.vote().vote_data().proposed().round()), - ConsensusMsg::SyncInfo(sync_info) => Some(sync_info.highest_certified_round()), - ConsensusMsg::CommitVoteMsg(commit_vote) => Some(commit_vote.commit_info().round()), - _ => None, - } - } - - /// Returns true for any message - pub fn take_all(_msg_copy: &(Author, ConsensusMsg)) -> bool { - true - } - - /// Returns true for proposal messages only. - pub fn proposals_only(msg: &(Author, ConsensusMsg)) -> bool { - matches!(&msg.1, ConsensusMsg::ProposalMsg(_)) - } - - /// Returns true for vote messages only. - pub fn votes_only(msg: &(Author, ConsensusMsg)) -> bool { - matches!(&msg.1, ConsensusMsg::VoteMsg(_)) - } - - pub fn extend_author_to_twin_ids(&mut self, author: Author, twin_id: TwinId) { - self.author_to_twin_ids - .write() - .extend_author_to_twin_ids(author, twin_id); - } - - pub fn get_twin_ids(&self, author: Author) -> Vec { - self.author_to_twin_ids.read().get_twin_ids(author) - } - - fn is_message_dropped(&self, src: &TwinId, dst: &TwinId, msg: ConsensusMsg) -> bool { - self.drop_config.read().is_message_dropped(src, dst) - || Self::get_message_round(msg).map_or(false, |r| { - self.drop_config_round.is_message_dropped(src, dst, r) - }) - } - - pub fn split_network( - &self, - partition_first: Vec, - partition_second: Vec, - ) -> bool { - self.drop_config - .write() - .split_network(&partition_first, &partition_second) - } - - /// Check if the message from 'src_twin_id' to 'dst_twin_id' should be dropped in the given round - pub fn is_message_dropped_round(&self, src: &TwinId, dst: &TwinId, round: u64) -> bool { - self.drop_config_round.is_message_dropped(src, dst, round) - } - - /// Creates the given per round network partitions - pub fn split_network_round( - &mut self, - round_partitions: &HashMap>>, - ) -> bool { - let mut ret = true; - - for (round, partitions) in round_partitions.iter() { - partitions.iter().enumerate().for_each(|(i, p1)| { - partitions.iter().skip(i + 1).for_each(|p2| { - ret &= self - .drop_config_round - .drop_message_for_round(*round, p1, p2) - }) - }) - } - ret - } - - pub async fn start(mut self) { - // Take the next queued message - while let Some((src_twin_id, net_req)) = self.outbound_msgs_rx.next().await { - // Convert PeerManagerRequest to corresponding PeerManagerNotification, - // and extract destination peer - let (dst, msg) = match &net_req { - PeerManagerRequest::SendDirectSend(dst_inner, msg_inner) => { - (*dst_inner, msg_inner.clone()) - }, - msg_inner => panic!( - "[network playground] Unexpected PeerManagerRequest: {:?}", - msg_inner - ), - }; - - let dst_twin_ids = self.get_twin_ids(dst); - - for dst_twin_id in dst_twin_ids.iter() { - let msg_notif = - PeerManagerNotification::RecvMessage(src_twin_id.author, msg.clone()); - let consensus_msg = msg.to_message().unwrap(); - - // Deliver and copy message it if it's not dropped - if !self.is_message_dropped(&src_twin_id, dst_twin_id, consensus_msg) { - self.deliver_message(src_twin_id, *dst_twin_id, msg_notif) - .await; - } - } - } - } -} - -#[derive(Default)] -struct AuthorToTwinIds(HashMap>); - -impl AuthorToTwinIds { - pub fn extend_author_to_twin_ids(&mut self, author: Author, twin_id: TwinId) { - self.0.entry(author).or_default(); - - self.0.get_mut(&author).unwrap().push(twin_id) - } - - pub fn get_twin_ids(&self, author: Author) -> Vec { - self.0.get(&author).unwrap().clone() - } -} - -#[derive(Default)] -struct DropConfig(HashMap>); - -impl DropConfig { - pub fn is_message_dropped(&self, src: &TwinId, dst: &TwinId) -> bool { - self.0.get(src).map_or(false, |set| set.contains(dst)) - } - - pub fn drop_message_for(&mut self, src: &TwinId, dst: &TwinId) -> bool { - self.0.entry(*src).or_default().insert(*dst) - } - - pub fn split_network( - &mut self, - partition_first: &[TwinId], - partition_second: &[TwinId], - ) -> bool { - partition_first - .iter() - .flat_map(move |n1| partition_second.iter().map(move |n2| (n1, n2))) - .fold(true, |mut done, (n1, n2)| { - done &= self.drop_message_for(n1, n2); - done &= self.drop_message_for(n2, n1); - done - }) - } - - fn add_node(&mut self, src: TwinId) { - self.0.insert(src, HashSet::new()); - } -} - -/// Table of per round message dropping rules -#[derive(Default)] -struct DropConfigRound(HashMap); - -impl DropConfigRound { - /// Check if the message from 'src' to 'dst' should be dropped in the given round - fn is_message_dropped(&self, src: &TwinId, dst: &TwinId, round: u64) -> bool { - self.0 - .get(&round) - .map_or(false, |config| config.is_message_dropped(src, dst)) - } - - /// Create partition for the round - fn drop_message_for_round( - &mut self, - round: u64, - partition_first: &[TwinId], - partition_second: &[TwinId], - ) -> bool { - let config = self.0.entry(round).or_default(); - config.split_network(partition_first, partition_second) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - network::{IncomingRpcRequest, NetworkTask}, - network_interface::{DIRECT_SEND, RPC}, - }; - use aptos_config::network_id::{NetworkId, PeerNetworkId}; - use aptos_consensus_types::{ - block_retrieval::{BlockRetrievalRequest, BlockRetrievalResponse, BlockRetrievalStatus}, - common::Payload, - }; - use aptos_crypto::HashValue; - use aptos_network::{ - application::{ - interface::{NetworkClient, NetworkServiceEvents}, - storage::PeersAndMetadata, - }, - protocols::{ - direct_send::Message, - network, - network::{NetworkEvents, NewNetworkSender}, - }, - transport::ConnectionMetadata, - }; - use aptos_types::validator_verifier::random_validator_verifier; - use bytes::Bytes; - use futures::{channel::oneshot, future}; - use maplit::hashmap; - - #[test] - fn test_split_network_round() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - - let num_nodes = 5; - let (signers, _validator_verifier) = random_validator_verifier(num_nodes, None, false); - - let mut nodes = Vec::new(); - for (i, signer) in signers.iter().enumerate() { - nodes.push(TwinId { - id: i, - author: signer.author(), - }); - } - - // Create per round partitions - let mut round_partitions: HashMap>> = HashMap::new(); - // Round 1 partitions: [0], [1,2] - round_partitions.insert(1, vec![vec![nodes[0]], vec![nodes[1], nodes[2]]]); - // Round 2 partitions: [1], [2], [3,4] - round_partitions.insert(2, vec![vec![nodes[1]], vec![nodes[2]], vec![ - nodes[3], nodes[4], - ]]); - assert!(playground.split_network_round(&round_partitions)); - - // Round 1 checks (partitions: [0], [1,2]) - // Messages from 0 to 1 should be dropped - assert!(playground.is_message_dropped_round(&nodes[0], &nodes[1], 1)); - // Messages from 1 to 0 should also be dropped - assert!(playground.is_message_dropped_round(&nodes[1], &nodes[0], 1)); - // Messages from 1 to 2 should not be dropped - assert!(!playground.is_message_dropped_round(&nodes[1], &nodes[2], 1)); - // Messages from 3 to 1 should not be dropped - assert!(!playground.is_message_dropped_round(&nodes[3], &nodes[0], 1)); - - // Round 2 checks (partitions: [1], [2], [3,4]) - // Messages from 2 to 4 should be dropped - assert!(playground.is_message_dropped_round(&nodes[2], &nodes[4], 2)); - // Messages from 1 to 2 should be dropped - assert!(playground.is_message_dropped_round(&nodes[1], &nodes[2], 2)); - // Messages from 3 to 4 should not be dropped - assert!(!playground.is_message_dropped_round(&nodes[3], &nodes[4], 2)); - // Messages from 0 to 3 should not be dropped - assert!(!playground.is_message_dropped_round(&nodes[0], &nodes[3], 2)); - } - - fn add_peer_to_storage( - peers_and_metadata: &PeersAndMetadata, - peer: &PeerId, - protocols: &[ProtocolId], - ) { - let peer_network_id = PeerNetworkId::new(NetworkId::Validator, *peer); - let mut conn_meta = ConnectionMetadata::mock(*peer); - conn_meta.application_protocols = ProtocolIdSet::from_iter(protocols); - peers_and_metadata - .insert_connection_metadata(peer_network_id, conn_meta) - .unwrap(); - } - - #[test] - fn test_network_api() { - let runtime = consensus_runtime(); - let _entered_runtime = runtime.enter(); - - let num_nodes = 5; - let mut receivers: Vec = Vec::new(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = Vec::new(); - let (signers, validator_verifier) = random_validator_verifier(num_nodes, None, false); - let peers: Vec<_> = signers.iter().map(|signer| signer.author()).collect(); - let peers_and_metadata = PeersAndMetadata::new(&[NetworkId::Validator]); - - for (peer_id, peer) in peers.iter().enumerate() { - let (network_reqs_tx, network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (consensus_tx, consensus_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = aptos_channels::new_test(1024); - let (_, conn_status_rx) = conn_notifs_channel::new(); - - add_peer_to_storage(&peers_and_metadata, peer, &[ - ProtocolId::ConsensusDirectSendJson, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusRpcBcs, - ]); - - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - peers_and_metadata.clone(), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - - let twin_id = TwinId { - id: peer_id, - author: *peer, - }; - playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx); - - let (self_sender, self_receiver) = aptos_channels::new_unbounded_test(); - let node = NetworkSender::new( - *peer, - consensus_network_client, - self_sender, - validator_verifier.clone(), - ); - - let network_events = NetworkEvents::new(consensus_rx, conn_status_rx, None); - let network_service_events = - NetworkServiceEvents::new(hashmap! {NetworkId::Validator => network_events}); - let (task, receiver) = NetworkTask::new(network_service_events, self_receiver); - - receivers.push(receiver); - runtime.handle().spawn(task.start()); - nodes.push(node); - } - let vote_msg = VoteMsg::new( - Vote::new( - VoteData::new(BlockInfo::random(1), BlockInfo::random(0)), - peers[0], - placeholder_ledger_info(), - &signers[0], - ) - .unwrap(), - test_utils::placeholder_sync_info(), - ); - let previous_qc = certificate_for_genesis(); - let proposal = ProposalMsg::new( - Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - previous_qc.clone(), - &signers[0], - Vec::new(), - ) - .unwrap(), - SyncInfo::new(previous_qc.clone(), previous_qc, None), - ); - timed_block_on(&runtime, async { - nodes[0] - .send_vote(vote_msg.clone(), peers[2..5].to_vec()) - .await; - playground - .wait_for_messages(3, NetworkPlayground::take_all) - .await; - for r in receivers.iter_mut().take(5).skip(2) { - let (_, msg) = r.consensus_messages.next().await.unwrap(); - match msg { - ConsensusMsg::VoteMsg(v) => assert_eq!(*v, vote_msg), - _ => panic!("unexpected messages"), - } - } - nodes[0].broadcast_proposal(proposal.clone()).await; - playground - .wait_for_messages(4, NetworkPlayground::take_all) - .await; - for r in receivers.iter_mut().take(num_nodes - 1) { - let (_, msg) = r.consensus_messages.next().await.unwrap(); - match msg { - ConsensusMsg::ProposalMsg(p) => assert_eq!(*p, proposal), - _ => panic!("unexpected messages"), - } - } - }); - } - - #[test] - fn test_rpc() { - let runtime = consensus_runtime(); - let _entered_runtime = runtime.enter(); - - let num_nodes = 2; - let mut senders = Vec::new(); - let mut receivers: Vec = Vec::new(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = Vec::new(); - let (signers, validator_verifier) = random_validator_verifier(num_nodes, None, false); - let peers: Vec<_> = signers.iter().map(|signer| signer.author()).collect(); - let peers_and_metadata = PeersAndMetadata::new(&[NetworkId::Validator]); - - for (peer_id, peer) in peers.iter().enumerate() { - let (network_reqs_tx, network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (consensus_tx, consensus_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = aptos_channels::new_test(1024); - let (_, conn_status_rx) = conn_notifs_channel::new(); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - peers_and_metadata.clone(), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - - add_peer_to_storage(&peers_and_metadata, peer, &[ - ProtocolId::ConsensusDirectSendJson, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusRpcJson, - ]); - - let twin_id = TwinId { - id: peer_id, - author: *peer, - }; - playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx); - - let (self_sender, self_receiver) = aptos_channels::new_unbounded_test(); - let node = NetworkSender::new( - *peer, - consensus_network_client.clone(), - self_sender, - validator_verifier.clone(), - ); - - let network_events = NetworkEvents::new(consensus_rx, conn_status_rx, None); - let network_service_events = - NetworkServiceEvents::new(hashmap! {NetworkId::Validator => network_events}); - let (task, receiver) = NetworkTask::new(network_service_events, self_receiver); - - senders.push(consensus_network_client); - receivers.push(receiver); - runtime.handle().spawn(task.start()); - nodes.push(node); - } - let receiver_1 = receivers.remove(1); - let node0 = nodes[0].clone(); - let peer1 = peers[1]; - let vote_msg = VoteMsg::new( - Vote::new( - VoteData::new(BlockInfo::random(1), BlockInfo::random(0)), - peers[0], - placeholder_ledger_info(), - &signers[0], - ) - .unwrap(), - test_utils::placeholder_sync_info(), - ); - - // verify request block rpc - let mut rpc_rx = receiver_1.rpc_rx; - let on_request_block = async move { - while let Some((_, request)) = rpc_rx.next().await { - // make sure the network task is not blocked during RPC - // we limit the network notification queue size to 1 so if it's blocked, - // we can not process 2 votes and the test will timeout - node0.send_vote(vote_msg.clone(), vec![peer1]).await; - node0.send_vote(vote_msg.clone(), vec![peer1]).await; - playground - .wait_for_messages(2, NetworkPlayground::votes_only) - .await; - let response = - BlockRetrievalResponse::new(BlockRetrievalStatus::IdNotFound, vec![]); - let response = ConsensusMsg::BlockRetrievalResponse(Box::new(response)); - let bytes = Bytes::from(serde_json::to_vec(&response).unwrap()); - match request { - IncomingRpcRequest::BlockRetrieval(request) => { - request.response_sender.send(Ok(bytes)).unwrap() - }, - _ => panic!("unexpected message"), - } - } - }; - runtime.handle().spawn(on_request_block); - let peer = peers[1]; - timed_block_on(&runtime, async { - let response = nodes[0] - .request_block( - BlockRetrievalRequest::new(HashValue::zero(), 1), - peer, - Duration::from_secs(5), - ) - .await - .unwrap(); - assert_eq!(response.status(), BlockRetrievalStatus::IdNotFound); - }); - } - - #[test] - fn test_bad_message() { - let runtime = consensus_runtime(); - let _entered_runtime = runtime.enter(); - - let (peer_mgr_notifs_tx, peer_mgr_notifs_rx) = - aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_notifs_tx, connection_notifs_rx) = - aptos_channel::new(QueueStyle::FIFO, 8, None); - let network_events = NetworkEvents::new(peer_mgr_notifs_rx, connection_notifs_rx, None); - let network_service_events = - NetworkServiceEvents::new(hashmap! {NetworkId::Validator => network_events}); - let (self_sender, self_receiver) = aptos_channels::new_unbounded_test(); - - let (network_task, mut network_receivers) = - NetworkTask::new(network_service_events, self_receiver); - - let peer_id = PeerId::random(); - let protocol_id = ProtocolId::ConsensusDirectSendBcs; - let bad_msg = PeerManagerNotification::RecvMessage(peer_id, Message { - protocol_id, - mdata: Bytes::from_static(b"\xde\xad\xbe\xef"), - }); - - peer_mgr_notifs_tx - .push((peer_id, protocol_id), bad_msg) - .unwrap(); - - let liveness_check_msg = ConsensusMsg::BlockRetrievalRequest(Box::new( - BlockRetrievalRequest::new(HashValue::random(), 1), - )); - - let protocol_id = ProtocolId::ConsensusRpcJson; - let (res_tx, _res_rx) = oneshot::channel(); - let liveness_check_msg = PeerManagerNotification::RecvRpc(peer_id, InboundRpcRequest { - protocol_id, - data: Bytes::from(serde_json::to_vec(&liveness_check_msg).unwrap()), - res_tx, - }); - - peer_mgr_notifs_tx - .push((peer_id, protocol_id), liveness_check_msg) - .unwrap(); - - let f_check = async move { - assert!(network_receivers.rpc_rx.next().await.is_some()); - - drop(peer_mgr_notifs_tx); - drop(connection_notifs_tx); - drop(self_sender); - - assert!(network_receivers.rpc_rx.next().await.is_none()); - assert!(network_receivers.consensus_messages.next().await.is_none()); - }; - let f_network_task = network_task.start(); - - let runtime = consensus_runtime(); - timed_block_on(&runtime, future::join(f_network_task, f_check)); - } -} diff --git a/consensus/src/payload_client/mixed.rs b/consensus/src/payload_client/mixed.rs deleted file mode 100644 index fdd7bacd9f729..0000000000000 --- a/consensus/src/payload_client/mixed.rs +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright © Aptos Foundation - -#[cfg(test)] -use crate::payload_client::user; -#[cfg(test)] -use crate::payload_client::validator::DummyValidatorTxnClient; -use crate::{ - error::QuorumStoreError, - payload_client::{user::UserPayloadClient, PayloadClient}, -}; -use aptos_consensus_types::common::{Payload, PayloadFilter}; -use aptos_logger::debug; -use aptos_types::{ - dkg::{DKGTranscript, DKGTranscriptMetadata}, - on_chain_config::ValidatorTxnConfig, - validator_txn::ValidatorTransaction, -}; -use aptos_validator_transaction_pool as vtxn_pool; -use fail::fail_point; -use futures::future::BoxFuture; -use move_core_types::account_address::AccountAddress; -#[cfg(test)] -use std::collections::HashSet; -use std::{ - cmp::min, - sync::Arc, - time::{Duration, Instant}, -}; - -pub struct MixedPayloadClient { - validator_txn_config: ValidatorTxnConfig, - validator_txn_pool_client: Arc, - user_payload_client: Arc, -} - -impl MixedPayloadClient { - pub fn new( - validator_txn_config: ValidatorTxnConfig, - validator_txn_pool_client: Arc< - dyn crate::payload_client::validator::ValidatorTxnPayloadClient, - >, - user_payload_client: Arc, - ) -> Self { - Self { - validator_txn_config, - validator_txn_pool_client, - user_payload_client, - } - } - - /// When enabled in smoke tests, generate 2 random validator transactions, 1 valid, 1 invalid. - fn extra_test_only_vtxns(&self) -> Vec { - fail_point!("mixed_payload_client::extra_test_only_vtxns", |_| vec![ - ValidatorTransaction::DKGResult(DKGTranscript { - metadata: DKGTranscriptMetadata { - epoch: 999, - author: AccountAddress::ZERO, - }, - transcript_bytes: vec![], - }), - ]); - vec![] - } -} - -#[async_trait::async_trait] -impl PayloadClient for MixedPayloadClient { - async fn pull_payload( - &self, - mut max_poll_time: Duration, - mut max_items: u64, - mut max_bytes: u64, - max_inline_items: u64, - max_inline_bytes: u64, - validator_txn_filter: vtxn_pool::TransactionFilter, - user_txn_filter: PayloadFilter, - wait_callback: BoxFuture<'static, ()>, - pending_ordering: bool, - pending_uncommitted_blocks: usize, - recent_max_fill_fraction: f32, - ) -> anyhow::Result<(Vec, Payload), QuorumStoreError> { - // Pull validator txns first. - let validator_txn_pull_timer = Instant::now(); - let mut validator_txns = self - .validator_txn_pool_client - .pull( - max_poll_time, - min( - max_items, - self.validator_txn_config.per_block_limit_txn_count(), - ), - min( - max_bytes, - self.validator_txn_config.per_block_limit_total_bytes(), - ), - validator_txn_filter, - ) - .await; - - validator_txns.extend(self.extra_test_only_vtxns()); - - debug!("num_validator_txns={}", validator_txns.len()); - // Update constraints with validator txn pull results. - max_items -= validator_txns.len() as u64; - max_bytes -= validator_txns - .iter() - .map(|txn| txn.size_in_bytes()) - .sum::() as u64; - max_poll_time = max_poll_time.saturating_sub(validator_txn_pull_timer.elapsed()); - - // Pull user payload. - let user_payload = self - .user_payload_client - .pull( - max_poll_time, - max_items, - max_bytes, - max_inline_items, - max_inline_bytes, - user_txn_filter, - wait_callback, - pending_ordering, - pending_uncommitted_blocks, - recent_max_fill_fraction, - ) - .await?; - - Ok((validator_txns, user_payload)) - } -} - -#[tokio::test] -async fn mixed_payload_client_should_prioritize_validator_txns() { - let all_validator_txns = vec![ - ValidatorTransaction::dummy(b"1".to_vec()), - ValidatorTransaction::dummy(b"22".to_vec()), - ValidatorTransaction::dummy(b"333".to_vec()), - ]; - - let all_user_txns = crate::test_utils::create_vec_signed_transactions(10); - let client = MixedPayloadClient { - validator_txn_config: ValidatorTxnConfig::V1 { - per_block_limit_txn_count: 99, - per_block_limit_total_bytes: 1048576, - }, - validator_txn_pool_client: Arc::new(DummyValidatorTxnClient::new( - all_validator_txns.clone(), - )), - user_payload_client: Arc::new(user::DummyClient::new(all_user_txns.clone())), - }; - - let (pulled_validator_txns, Payload::DirectMempool(pulled_user_txns)) = client - .pull_payload( - Duration::from_secs(1), // max_poll_time - 99, // max_items - 1048576, // size limit: 1MB - 50, - 500000, // inline limit: 500KB - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - Box::pin(async {}), - false, - 0, - 0., - ) - .await - .unwrap() - else { - unreachable!() - }; - - assert_eq!(3, pulled_validator_txns.len()); - assert_eq!(10, pulled_user_txns.len()); - - let (pulled_validator_txns, Payload::DirectMempool(pulled_user_txns)) = client - .pull_payload( - Duration::from_micros(500), // max_poll_time - 99, // max_items - 1048576, // size limit: 1MB - 50, - 500000, // inline limit: 500KB - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - Box::pin(async {}), - false, - 0, - 0., - ) - .await - .unwrap() - else { - unreachable!() - }; - - assert_eq!(1, pulled_validator_txns.len()); - assert_eq!(0, pulled_user_txns.len()); - - let (pulled_validator_txns, Payload::DirectMempool(pulled_user_txns)) = client - .pull_payload( - Duration::from_secs(1), // max_poll_time - 1, // max_items - 1048576, // size limit: 1MB - 0, - 0, // inline limit: 0 - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - Box::pin(async {}), - false, - 0, - 0., - ) - .await - .unwrap() - else { - unreachable!() - }; - - assert_eq!(1, pulled_validator_txns.len()); - assert_eq!(0, pulled_user_txns.len()); - - let (pulled_validator_txns, Payload::DirectMempool(pulled_user_txns)) = client - .pull_payload( - Duration::from_secs(1), // max_poll_time - 99, // max_items - all_validator_txns[0].size_in_bytes() as u64, - 50, - all_validator_txns[0].size_in_bytes() as u64, - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - Box::pin(async {}), - false, - 0, - 0., - ) - .await - .unwrap() - else { - unreachable!() - }; - - assert_eq!(1, pulled_validator_txns.len()); - assert_eq!(0, pulled_user_txns.len()); -} - -#[tokio::test] -async fn mixed_payload_client_should_respect_validator_txn_feature_flag() { - let all_validator_txns = vec![ - ValidatorTransaction::dummy(b"1".to_vec()), - ValidatorTransaction::dummy(b"22".to_vec()), - ValidatorTransaction::dummy(b"333".to_vec()), - ]; - - let all_user_txns = crate::test_utils::create_vec_signed_transactions(10); - let client = MixedPayloadClient { - validator_txn_config: ValidatorTxnConfig::default_disabled(), - validator_txn_pool_client: Arc::new(DummyValidatorTxnClient::new( - all_validator_txns.clone(), - )), - user_payload_client: Arc::new(user::DummyClient::new(all_user_txns.clone())), - }; - - let (pulled_validator_txns, Payload::DirectMempool(pulled_user_txns)) = client - .pull_payload( - Duration::from_millis(50), // max_poll_time - 99, // max_items - 1048576, // size limit: 1MB - 50, - 500000, // inline limit: 500KB - vtxn_pool::TransactionFilter::PendingTxnHashSet(HashSet::new()), - PayloadFilter::Empty, - Box::pin(async {}), - false, - 0, - 0., - ) - .await - .unwrap() - else { - unreachable!() - }; - - assert_eq!(0, pulled_validator_txns.len()); - assert_eq!(10, pulled_user_txns.len()); -} diff --git a/consensus/src/payload_client/mod.rs b/consensus/src/payload_client/mod.rs deleted file mode 100644 index d37cbfbbdb5ac..0000000000000 --- a/consensus/src/payload_client/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::error::QuorumStoreError; -use aptos_consensus_types::common::{Payload, PayloadFilter}; -use aptos_types::validator_txn::ValidatorTransaction; -use aptos_validator_transaction_pool::TransactionFilter; -use futures::future::BoxFuture; -use std::time::Duration; - -pub mod mixed; -pub mod user; -pub mod validator; - -#[async_trait::async_trait] -pub trait PayloadClient: Send + Sync { - async fn pull_payload( - &self, - max_poll_time: Duration, - max_items: u64, - max_bytes: u64, - max_inline_items: u64, - max_inline_bytes: u64, - validator_txn_filter: TransactionFilter, - user_txn_filter: PayloadFilter, - wait_callback: BoxFuture<'static, ()>, - pending_ordering: bool, - pending_uncommitted_blocks: usize, - recent_max_fill_fraction: f32, - ) -> anyhow::Result<(Vec, Payload), QuorumStoreError>; - - fn trace_payloads(&self) {} -} diff --git a/consensus/src/payload_client/user/mod.rs b/consensus/src/payload_client/user/mod.rs deleted file mode 100644 index 8e52508252903..0000000000000 --- a/consensus/src/payload_client/user/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::error::QuorumStoreError; -use aptos_consensus_types::common::{Payload, PayloadFilter}; -#[cfg(test)] -use aptos_types::transaction::SignedTransaction; -use futures::future::BoxFuture; -use std::time::Duration; -#[cfg(test)] -use std::time::Instant; - -/// Clients can pull information about transactions from the mempool and return -/// the retrieved information as a `Payload`. -#[async_trait::async_trait] -pub trait UserPayloadClient: Send + Sync { - async fn pull( - &self, - max_poll_time: Duration, - max_items: u64, - max_bytes: u64, - max_inline_items: u64, - max_inline_bytes: u64, - exclude: PayloadFilter, - wait_callback: BoxFuture<'static, ()>, - pending_ordering: bool, - pending_uncommitted_blocks: usize, - recent_max_fill_fraction: f32, - ) -> anyhow::Result; -} - -/// A dummy user payload client that pull hardcoded txns one by one. -#[cfg(test)] -pub struct DummyClient { - pub(crate) txns: Vec, -} - -#[cfg(test)] -impl DummyClient { - pub fn new(txns: Vec) -> Self { - Self { txns } - } -} - -#[cfg(test)] -#[async_trait::async_trait] -impl UserPayloadClient for DummyClient { - async fn pull( - &self, - max_poll_time: Duration, - mut max_items: u64, - mut max_bytes: u64, - _max_inline_items: u64, - _max_inline_bytes: u64, - _exclude: PayloadFilter, - _wait_callback: BoxFuture<'static, ()>, - _pending_ordering: bool, - _pending_uncommitted_blocks: usize, - _recent_max_fill_fraction: f32, - ) -> anyhow::Result { - let timer = Instant::now(); - let mut nxt_txn_idx = 0; - let mut txns = vec![]; - while timer.elapsed() < max_poll_time - && max_items >= 1 - && max_bytes >= 1 - && nxt_txn_idx < self.txns.len() - { - tokio::time::sleep(Duration::from_millis(1)).await; - let txn = self.txns[nxt_txn_idx].clone(); - let txn_size = txn.raw_txn_bytes_len() as u64; - if txn_size > max_bytes { - break; - } - max_items -= 1; - max_bytes -= txn_size; - nxt_txn_idx += 1; - txns.push(txn); - } - Ok(Payload::DirectMempool(txns)) - } -} - -pub mod quorum_store_client; diff --git a/consensus/src/payload_client/user/quorum_store_client.rs b/consensus/src/payload_client/user/quorum_store_client.rs deleted file mode 100644 index f8e378a8cb957..0000000000000 --- a/consensus/src/payload_client/user/quorum_store_client.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - counters::WAIT_FOR_FULL_BLOCKS_TRIGGERED, error::QuorumStoreError, monitor, - payload_client::user::UserPayloadClient, -}; -use aptos_consensus_types::{ - common::{Payload, PayloadFilter}, - request_response::{GetPayloadCommand, GetPayloadResponse}, -}; -use aptos_logger::info; -use fail::fail_point; -use futures::future::BoxFuture; -use futures_channel::{mpsc, oneshot}; -use std::time::{Duration, Instant}; -use tokio::time::{sleep, timeout}; - -const NO_TXN_DELAY: u64 = 30; - -/// Client that pulls blocks from Quorum Store -#[derive(Clone)] -pub struct QuorumStoreClient { - consensus_to_quorum_store_sender: mpsc::Sender, - /// Timeout for consensus to pull transactions from quorum store and get a response (in milliseconds) - pull_timeout_ms: u64, - wait_for_full_blocks_above_recent_fill_threshold: f32, - wait_for_full_blocks_above_pending_blocks: usize, -} - -impl QuorumStoreClient { - pub fn new( - consensus_to_quorum_store_sender: mpsc::Sender, - pull_timeout_ms: u64, - wait_for_full_blocks_above_recent_fill_threshold: f32, - wait_for_full_blocks_above_pending_blocks: usize, - ) -> Self { - Self { - consensus_to_quorum_store_sender, - pull_timeout_ms, - wait_for_full_blocks_above_recent_fill_threshold, - wait_for_full_blocks_above_pending_blocks, - } - } - - async fn pull_internal( - &self, - max_items: u64, - max_bytes: u64, - max_inline_items: u64, - max_inline_bytes: u64, - return_non_full: bool, - exclude_payloads: PayloadFilter, - ) -> anyhow::Result { - let (callback, callback_rcv) = oneshot::channel(); - let req = GetPayloadCommand::GetPayloadRequest( - max_items, - max_bytes, - max_inline_items, - max_inline_bytes, - return_non_full, - exclude_payloads.clone(), - callback, - ); - // send to shared mempool - self.consensus_to_quorum_store_sender - .clone() - .try_send(req) - .map_err(anyhow::Error::from)?; - // wait for response - match monitor!( - "pull_payload", - timeout(Duration::from_millis(self.pull_timeout_ms), callback_rcv).await - ) { - Err(_) => { - Err(anyhow::anyhow!("[consensus] did not receive GetBlockResponse on time").into()) - }, - Ok(resp) => match resp.map_err(anyhow::Error::from)?? { - GetPayloadResponse::GetPayloadResponse(payload) => Ok(payload), - }, - } - } -} - -#[async_trait::async_trait] -impl UserPayloadClient for QuorumStoreClient { - async fn pull( - &self, - max_poll_time: Duration, - max_items: u64, - max_bytes: u64, - max_inline_items: u64, - max_inline_bytes: u64, - exclude: PayloadFilter, - wait_callback: BoxFuture<'static, ()>, - pending_ordering: bool, - pending_uncommitted_blocks: usize, - recent_max_fill_fraction: f32, - ) -> anyhow::Result { - let return_non_full = recent_max_fill_fraction - < self.wait_for_full_blocks_above_recent_fill_threshold - && pending_uncommitted_blocks < self.wait_for_full_blocks_above_pending_blocks; - let return_empty = pending_ordering && return_non_full; - - WAIT_FOR_FULL_BLOCKS_TRIGGERED.observe(if !return_non_full { 1.0 } else { 0.0 }); - - fail_point!("consensus::pull_payload", |_| { - Err(anyhow::anyhow!("Injected error in pull_payload").into()) - }); - let mut callback_wrapper = Some(wait_callback); - // keep polling QuorumStore until there's payloads available or there's still pending payloads - let start_time = Instant::now(); - - let payload = loop { - // Make sure we don't wait more than expected, due to thread scheduling delays/processing time consumed - let done = start_time.elapsed() >= max_poll_time; - let payload = self - .pull_internal( - max_items, - max_bytes, - max_inline_items, - max_inline_bytes, - return_non_full || return_empty || done, - exclude.clone(), - ) - .await?; - if payload.is_empty() && !return_empty && !done { - if let Some(callback) = callback_wrapper.take() { - callback.await; - } - sleep(Duration::from_millis(NO_TXN_DELAY)).await; - continue; - } - break payload; - }; - info!( - elapsed_time_ms = start_time.elapsed().as_millis() as u64, - max_poll_time_ms = max_poll_time.as_millis() as u64, - payload_len = payload.len(), - max_items = max_items, - max_bytes = max_bytes, - max_inline_items = max_inline_items, - max_inline_bytes = max_inline_bytes, - pending_ordering = pending_ordering, - return_empty = return_empty, - return_non_full = return_non_full, - duration_ms = start_time.elapsed().as_millis() as u64, - "Pull payloads from QuorumStore: proposal" - ); - - Ok(payload) - } -} diff --git a/consensus/src/payload_client/validator.rs b/consensus/src/payload_client/validator.rs deleted file mode 100644 index 91b6b8cb2fd39..0000000000000 --- a/consensus/src/payload_client/validator.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_types::validator_txn::ValidatorTransaction; -use aptos_validator_transaction_pool as vtxn_pool; -use aptos_validator_transaction_pool::VTxnPoolState; -use std::{ - ops::Add, - time::{Duration, Instant}, -}; - -#[async_trait::async_trait] -pub trait ValidatorTxnPayloadClient: Send + Sync { - async fn pull( - &self, - max_time: Duration, - max_items: u64, - max_bytes: u64, - exclude: vtxn_pool::TransactionFilter, - ) -> Vec; -} - -#[cfg(test)] -pub struct DummyValidatorTxnClient { - txns: Vec, -} - -#[cfg(test)] -impl DummyValidatorTxnClient { - pub fn new(txns: Vec) -> Self { - Self { txns } - } -} - -#[cfg(test)] -#[async_trait::async_trait] -impl ValidatorTxnPayloadClient for DummyValidatorTxnClient { - async fn pull( - &self, - max_time: Duration, - mut max_items: u64, - mut max_bytes: u64, - _exclude: vtxn_pool::TransactionFilter, - ) -> Vec { - let timer = Instant::now(); - let mut nxt_txn_idx = 0; - let mut ret = vec![]; - while timer.elapsed() < max_time - && max_items > 0 - && max_bytes > 0 - && nxt_txn_idx < self.txns.len() - { - tokio::time::sleep(Duration::from_millis(1)).await; - let txn = self.txns[nxt_txn_idx].clone(); - let txn_size = txn.size_in_bytes() as u64; - if txn_size > max_bytes { - break; - } - ret.push(txn); - max_items -= 1; - max_bytes -= txn_size; - nxt_txn_idx += 1; - } - ret - } -} - -#[async_trait::async_trait] -impl ValidatorTxnPayloadClient for VTxnPoolState { - async fn pull( - &self, - max_time: Duration, - max_items: u64, - max_bytes: u64, - filter: vtxn_pool::TransactionFilter, - ) -> Vec { - let deadline = Instant::now().add(max_time); - self.pull(deadline, max_items, max_bytes, filter) - } -} diff --git a/consensus/src/payload_manager.rs b/consensus/src/payload_manager.rs deleted file mode 100644 index b5e9f62920ee0..0000000000000 --- a/consensus/src/payload_manager.rs +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - quorum_store::{batch_store::BatchReader, quorum_store_coordinator::CoordinatorCommand}, -}; -use aptos_consensus_types::{ - block::Block, - common::{DataStatus, Payload, ProofWithData}, - proof_of_store::ProofOfStore, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::{ExecutorError::DataNotFound, *}; -use aptos_logger::prelude::*; -use aptos_types::transaction::SignedTransaction; -use futures::channel::mpsc::Sender; -use std::sync::Arc; -use tokio::sync::oneshot; - -pub trait TPayloadManager: Send + Sync { - fn prefetch_payload_data(&self, payload: &Payload, timestamp: u64); -} - -/// Responsible to extract the transactions out of the payload and notify QuorumStore about commits. -/// If QuorumStore is enabled, has to ask BatchReader for the transaction behind the proofs of availability in the payload. -pub enum PayloadManager { - DirectMempool, - InQuorumStore(Arc, Sender), -} - -impl TPayloadManager for PayloadManager { - fn prefetch_payload_data(&self, payload: &Payload, timestamp: u64) { - self.prefetch_payload_data(payload, timestamp); - } -} - -impl PayloadManager { - fn request_transactions( - proofs: Vec, - block_timestamp: u64, - batch_reader: Arc, - ) -> Vec<( - HashValue, - oneshot::Receiver>>, - )> { - let mut receivers = Vec::new(); - for pos in proofs { - trace!( - "QSE: requesting pos {:?}, digest {}, time = {}", - pos, - pos.digest(), - block_timestamp - ); - if block_timestamp <= pos.expiration() { - receivers.push((*pos.digest(), batch_reader.get_batch(pos))); - } else { - debug!("QSE: skipped expired pos {}", pos.digest()); - } - } - receivers - } - - ///Pass commit information to BatchReader and QuorumStore wrapper for their internal cleanups. - pub fn notify_commit(&self, block_timestamp: u64, payloads: Vec) { - match self { - PayloadManager::DirectMempool => {}, - PayloadManager::InQuorumStore(batch_reader, coordinator_tx) => { - batch_reader.update_certified_timestamp(block_timestamp); - - let batches: Vec<_> = payloads - .into_iter() - .flat_map(|payload| match payload { - Payload::DirectMempool(_) => { - unreachable!("InQuorumStore should be used"); - }, - Payload::InQuorumStore(proof_with_status) => proof_with_status - .proofs - .iter() - .map(|proof| proof.info().clone()) - .collect::>(), - Payload::InQuorumStoreWithLimit(proof_with_status) => proof_with_status - .proof_with_data - .proofs - .iter() - .map(|proof| proof.info().clone()) - .collect::>(), - Payload::QuorumStoreInlineHybrid(inline_batches, proof_with_data, _) => { - inline_batches - .iter() - .map(|(batch_info, _)| batch_info.clone()) - .chain( - proof_with_data - .proofs - .iter() - .map(|proof| proof.info().clone()), - ) - .collect::>() - }, - }) - .collect(); - - let mut tx = coordinator_tx.clone(); - - if let Err(e) = tx.try_send(CoordinatorCommand::CommitNotification( - block_timestamp, - batches, - )) { - warn!( - "CommitNotification failed. Is the epoch shutting down? error: {}", - e - ); - } - }, - } - } - - /// Called from consensus to pre-fetch the transaction behind the batches in the block. - pub fn prefetch_payload_data(&self, payload: &Payload, timestamp: u64) { - let request_txns_and_update_status = - move |proof_with_status: &ProofWithData, batch_reader: Arc| { - let receivers = PayloadManager::request_transactions( - proof_with_status.proofs.clone(), - timestamp, - batch_reader.clone(), - ); - proof_with_status - .status - .lock() - .replace(DataStatus::Requested(receivers)); - }; - - match self { - PayloadManager::DirectMempool => {}, - PayloadManager::InQuorumStore(batch_reader, _) => match payload { - Payload::InQuorumStore(proof_with_status) => { - request_txns_and_update_status(proof_with_status, batch_reader.clone()); - }, - Payload::InQuorumStoreWithLimit(proof_with_data) => { - request_txns_and_update_status( - &proof_with_data.proof_with_data, - batch_reader.clone(), - ); - }, - Payload::QuorumStoreInlineHybrid(_, proof_with_data, _) => { - request_txns_and_update_status(proof_with_data, batch_reader.clone()); - }, - Payload::DirectMempool(_) => { - unreachable!() - }, - }, - } - } - - /// Extract transaction from a given block - /// Assumes it is never called for the same block concurrently. Otherwise status can be None. - pub async fn get_transactions( - &self, - block: &Block, - ) -> ExecutorResult<(Vec, Option)> { - let payload = match block.payload() { - Some(p) => p, - None => return Ok((Vec::new(), None)), - }; - - async fn process_payload( - proof_with_data: &ProofWithData, - batch_reader: Arc, - block: &Block, - ) -> ExecutorResult> { - let status = proof_with_data.status.lock().take(); - match status.expect("Should have been updated before.") { - DataStatus::Cached(data) => { - counters::QUORUM_BATCH_READY_COUNT.inc(); - proof_with_data - .status - .lock() - .replace(DataStatus::Cached(data.clone())); - Ok(data) - }, - DataStatus::Requested(receivers) => { - let _timer = counters::BATCH_WAIT_DURATION.start_timer(); - let mut vec_ret = Vec::new(); - if !receivers.is_empty() { - debug!( - "QSE: waiting for data on {} receivers, block_round {}", - receivers.len(), - block.round() - ); - } - for (digest, rx) in receivers { - match rx.await { - Err(e) => { - // We probably advanced epoch already. - warn!( - "Oneshot channel to get a batch was dropped with error {:?}", - e - ); - let new_receivers = PayloadManager::request_transactions( - proof_with_data.proofs.clone(), - block.timestamp_usecs(), - batch_reader.clone(), - ); - // Could not get all data so requested again - proof_with_data - .status - .lock() - .replace(DataStatus::Requested(new_receivers)); - return Err(DataNotFound(digest)); - }, - Ok(Ok(data)) => { - vec_ret.push(data); - }, - Ok(Err(e)) => { - let new_receivers = PayloadManager::request_transactions( - proof_with_data.proofs.clone(), - block.timestamp_usecs(), - batch_reader.clone(), - ); - // Could not get all data so requested again - proof_with_data - .status - .lock() - .replace(DataStatus::Requested(new_receivers)); - return Err(e); - }, - } - } - let ret: Vec = vec_ret.into_iter().flatten().collect(); - // execution asks for the data twice, so data is cached here for the second time. - proof_with_data - .status - .lock() - .replace(DataStatus::Cached(ret.clone())); - Ok(ret) - }, - } - } - - match (self, payload) { - (PayloadManager::DirectMempool, Payload::DirectMempool(txns)) => { - Ok((txns.clone(), None)) - }, - ( - PayloadManager::InQuorumStore(batch_reader, _), - Payload::InQuorumStore(proof_with_data), - ) => Ok(( - process_payload(proof_with_data, batch_reader.clone(), block).await?, - None, - )), - ( - PayloadManager::InQuorumStore(batch_reader, _), - Payload::InQuorumStoreWithLimit(proof_with_data), - ) => Ok(( - process_payload( - &proof_with_data.proof_with_data, - batch_reader.clone(), - block, - ) - .await?, - proof_with_data.max_txns_to_execute, - )), - ( - PayloadManager::InQuorumStore(batch_reader, _), - Payload::QuorumStoreInlineHybrid( - inline_batches, - proof_with_data, - max_txns_to_execute, - ), - ) => Ok(( - { - let mut all_txns = - process_payload(proof_with_data, batch_reader.clone(), block).await?; - all_txns.append( - &mut inline_batches - .iter() - // TODO: Can clone be avoided here? - .flat_map(|(_batch_info, txns)| txns.clone()) - .collect(), - ); - all_txns - }, - *max_txns_to_execute, - )), - (_, _) => unreachable!( - "Wrong payload {} epoch {}, round {}, id {}", - payload, - block.block_data().epoch(), - block.block_data().round(), - block.id() - ), - } - } -} diff --git a/consensus/src/pending_votes.rs b/consensus/src/pending_votes.rs deleted file mode 100644 index 56af5416a3a20..0000000000000 --- a/consensus/src/pending_votes.rs +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! PendingVotes store pending votes observed for a fixed epoch and round. -//! It is meant to be used inside of a RoundState. -//! The module takes care of creating a QC or a TC -//! when enough votes (or timeout votes) have been observed. -//! Votes are automatically dropped when the structure goes out of scope. - -use crate::{ - counters, - qc_aggregator::{create_qc_aggregator, QcAggregator}, - util::time_service::TimeService, -}; -use aptos_config::config::QcAggregatorType; -use aptos_consensus_types::{ - common::Author, - delayed_qc_msg::DelayedQcMsg, - quorum_cert::QuorumCert, - timeout_2chain::{TwoChainTimeoutCertificate, TwoChainTimeoutWithPartialSignatures}, - vote::Vote, - vote_data::VoteData, -}; -use aptos_crypto::{hash::CryptoHash, HashValue}; -use aptos_logger::prelude::*; -use aptos_types::{ - aggregate_signature::PartialSignatures, - ledger_info::LedgerInfoWithPartialSignatures, - validator_verifier::{ValidatorVerifier, VerifyError}, -}; -use futures_channel::mpsc::UnboundedSender; -use std::{ - collections::{BTreeMap, HashMap}, - fmt, - sync::Arc, -}; - -/// Result of the vote processing. The failure case (Verification error) is returned -/// as the Error part of the result. -#[derive(Debug, PartialEq, Eq)] -pub enum VoteReceptionResult { - /// The vote has been added but QC has not been formed yet. Return the amount of voting power - /// QC currently has. - VoteAdded(u128), - /// The vote has been added and we have gather enough voting power to form the QC but we have - /// delayed the QC to aggregate as many signatures as possible. - VoteAddedQCDelayed(u128), - /// The very same vote message has been processed in past. - DuplicateVote, - /// The very same author has already voted for another proposal in this round (equivocation). - EquivocateVote, - /// This block has just been certified after adding the vote. - NewQuorumCertificate(Arc), - /// The vote completes a new TwoChainTimeoutCertificate - New2ChainTimeoutCertificate(Arc), - /// There might be some issues adding a vote - ErrorAddingVote(VerifyError), - /// Error happens when aggregating signature - ErrorAggregatingSignature(VerifyError), - /// Error happens when aggregating timeout certificated - ErrorAggregatingTimeoutCertificate(VerifyError), - /// The vote is not for the current round. - UnexpectedRound(u64, u64), - /// Receive f+1 timeout to trigger a local timeout, return the amount of voting power TC currently has. - EchoTimeout(u128), -} - -/// A PendingVotes structure keep track of votes -pub struct PendingVotes { - /// Maps LedgerInfo digest to associated signatures (contained in a partial LedgerInfoWithSignatures). - /// This might keep multiple LedgerInfos for the current round: either due to different proposals (byzantine behavior) - /// or due to different NIL proposals (clients can have a different view of what block to extend). - li_digest_to_votes: - HashMap, - /// Tracks all the signatures of the 2-chain timeout for the given round. - maybe_partial_2chain_tc: Option, - /// Map of Author to (vote, li_digest). This is useful to discard multiple votes. - author_to_vote: HashMap, - /// Whether we have echoed timeout for this round. - echo_timeout: bool, - - qc_aggregator: Box, -} - -impl PendingVotes { - /// Creates an empty PendingVotes structure for a specific epoch and round - pub fn new( - time_service: Arc, - delayed_qc_tx: UnboundedSender, - qc_aggregator_type: QcAggregatorType, - ) -> Self { - PendingVotes { - li_digest_to_votes: HashMap::new(), - maybe_partial_2chain_tc: None, - author_to_vote: HashMap::new(), - echo_timeout: false, - qc_aggregator: create_qc_aggregator(qc_aggregator_type, time_service, delayed_qc_tx), - } - } - - /// Insert a vote and if the vote is valid, return a QuorumCertificate preferentially over a - /// TimeoutCertificate if either can can be formed - pub fn insert_vote( - &mut self, - vote: &Vote, - validator_verifier: &ValidatorVerifier, - ) -> VoteReceptionResult { - // derive data from vote - let li_digest = vote.ledger_info().hash(); - - // - // 1. Has the author already voted for this round? - // - - if let Some((previously_seen_vote, previous_li_digest)) = - self.author_to_vote.get(&vote.author()) - { - // is it the same vote? - if &li_digest == previous_li_digest { - // we've already seen an equivalent vote before - let new_timeout_vote = vote.is_timeout() && !previously_seen_vote.is_timeout(); - if !new_timeout_vote { - // it's not a new timeout vote - return VoteReceptionResult::DuplicateVote; - } - } else { - // we have seen a different vote for the same round - error!( - SecurityEvent::ConsensusEquivocatingVote, - remote_peer = vote.author(), - vote = vote, - previous_vote = previously_seen_vote - ); - - return VoteReceptionResult::EquivocateVote; - } - } - - // - // 2. Store new vote (or update, in case it's a new timeout vote) - // - - self.author_to_vote - .insert(vote.author(), (vote.clone(), li_digest)); - - // - // 3. Let's check if we can create a QC - // - - let len = self.li_digest_to_votes.len() + 1; - // obtain the ledger info with signatures associated to the vote's ledger info - let (hash_index, li_with_sig) = - self.li_digest_to_votes.entry(li_digest).or_insert_with(|| { - // if the ledger info with signatures doesn't exist yet, create it - ( - len, - LedgerInfoWithPartialSignatures::new( - vote.ledger_info().clone(), - PartialSignatures::empty(), - ), - ) - }); - - let validator_voting_power = validator_verifier - .get_voting_power(&vote.author()) - .unwrap_or(0); - if validator_voting_power == 0 { - warn!("Received vote with no voting power, from {}", vote.author()); - } - let cur_epoch = vote.vote_data().proposed().epoch() as i64; - let cur_round = vote.vote_data().proposed().round() as i64; - counters::CONSENSUS_CURRENT_ROUND_QUORUM_VOTING_POWER - .set(validator_verifier.quorum_voting_power() as f64); - - if !vote.is_timeout() { - counters::CONSENSUS_CURRENT_ROUND_VOTED_POWER - .with_label_values(&[&vote.author().to_string(), &hash_index_to_str(*hash_index)]) - .set(validator_voting_power as f64); - counters::CONSENSUS_LAST_VOTE_EPOCH - .with_label_values(&[&vote.author().to_string()]) - .set(cur_epoch); - counters::CONSENSUS_LAST_VOTE_ROUND - .with_label_values(&[&vote.author().to_string()]) - .set(cur_round); - } - - // add this vote to the ledger info with signatures - li_with_sig.add_signature(vote.author(), vote.signature().clone()); - - // check if we have enough signatures to create a QC - let voting_power = - match validator_verifier.check_voting_power(li_with_sig.signatures().keys(), true) { - // a quorum of signature was reached, a new QC is formed - Ok(aggregated_voting_power) => { - return self.qc_aggregator.handle_aggregated_qc( - validator_verifier, - aggregated_voting_power, - vote, - li_with_sig, - ); - }, - - // not enough votes - Err(VerifyError::TooLittleVotingPower { voting_power, .. }) => voting_power, - - // error - Err(error) => { - error!( - "MUST_FIX: vote received could not be added: {}, vote: {}", - error, vote - ); - return VoteReceptionResult::ErrorAddingVote(error); - }, - }; - - // - // 4. We couldn't form a QC, let's check if we can create a TC - // - - if let Some((timeout, signature)) = vote.two_chain_timeout() { - counters::CONSENSUS_CURRENT_ROUND_TIMEOUT_VOTED_POWER - .with_label_values(&[&vote.author().to_string()]) - .set(validator_voting_power as f64); - counters::CONSENSUS_LAST_TIMEOUT_VOTE_EPOCH - .with_label_values(&[&vote.author().to_string()]) - .set(cur_epoch); - counters::CONSENSUS_LAST_TIMEOUT_VOTE_ROUND - .with_label_values(&[&vote.author().to_string()]) - .set(cur_round); - - let partial_tc = self - .maybe_partial_2chain_tc - .get_or_insert_with(|| TwoChainTimeoutWithPartialSignatures::new(timeout.clone())); - partial_tc.add(vote.author(), timeout.clone(), signature.clone()); - let tc_voting_power = - match validator_verifier.check_voting_power(partial_tc.signers(), true) { - Ok(_) => { - return match partial_tc.aggregate_signatures(validator_verifier) { - Ok(tc_with_sig) => VoteReceptionResult::New2ChainTimeoutCertificate( - Arc::new(tc_with_sig), - ), - Err(e) => VoteReceptionResult::ErrorAggregatingTimeoutCertificate(e), - }; - }, - Err(VerifyError::TooLittleVotingPower { voting_power, .. }) => voting_power, - Err(error) => { - error!( - "MUST_FIX: 2-chain timeout vote received could not be added: {}, vote: {}", - error, vote - ); - return VoteReceptionResult::ErrorAddingVote(error); - }, - }; - - // Echo timeout if receive f+1 timeout message. - if !self.echo_timeout { - let f_plus_one = validator_verifier.total_voting_power() - - validator_verifier.quorum_voting_power() - + 1; - if tc_voting_power >= f_plus_one { - self.echo_timeout = true; - return VoteReceptionResult::EchoTimeout(tc_voting_power); - } - } - } - - // - // 5. No QC (or TC) could be formed, return the QC's voting power - // - - VoteReceptionResult::VoteAdded(voting_power) - } - - pub fn aggregate_qc_now( - validator_verifier: &ValidatorVerifier, - li_with_sig: &LedgerInfoWithPartialSignatures, - vote_data: &VoteData, - ) -> VoteReceptionResult { - match li_with_sig.aggregate_signatures(validator_verifier) { - Ok(ledger_info_with_sig) => VoteReceptionResult::NewQuorumCertificate(Arc::new( - QuorumCert::new(vote_data.clone(), ledger_info_with_sig), - )), - Err(e) => VoteReceptionResult::ErrorAggregatingSignature(e), - } - } - - pub fn process_delayed_qc( - &mut self, - validator_verifier: &ValidatorVerifier, - vote: Vote, - ) -> VoteReceptionResult { - let li_digest = vote.ledger_info().hash(); - let (_, li_with_sig) = self.li_digest_to_votes.get_mut(&li_digest).unwrap(); - match validator_verifier.check_voting_power(li_with_sig.signatures().keys(), true) { - // a quorum of signature was reached, a new QC is formed - Ok(_) => Self::aggregate_qc_now(validator_verifier, li_with_sig, vote.vote_data()), - - // not enough votes - Err(VerifyError::TooLittleVotingPower { .. }) => { - panic!("Delayed QC aggregation should not be triggered if we don't have enough votes to form a QC"); - }, - - // error - Err(error) => { - error!( - "MUST_FIX: vote received could not be added: {}, vote: {}", - error, vote - ); - VoteReceptionResult::ErrorAddingVote(error) - }, - } - } - - pub fn drain_votes( - &mut self, - ) -> ( - Vec<(HashValue, LedgerInfoWithPartialSignatures)>, - Option, - ) { - for (hash_index, _) in self.li_digest_to_votes.values() { - let hash_index_str = hash_index_to_str(*hash_index); - for author in self.author_to_vote.keys() { - counters::CONSENSUS_CURRENT_ROUND_VOTED_POWER - .with_label_values(&[&author.to_string(), &hash_index_str]) - .set(0_f64); - } - } - if let Some(partial_tc) = &self.maybe_partial_2chain_tc { - for author in partial_tc.signers() { - counters::CONSENSUS_CURRENT_ROUND_TIMEOUT_VOTED_POWER - .with_label_values(&[&author.to_string()]) - .set(0_f64); - } - } - - ( - self.li_digest_to_votes - .drain() - .map(|(key, (_, li))| (key, li)) - .collect(), - self.maybe_partial_2chain_tc.take(), - ) - } -} - -fn hash_index_to_str(hash_index: usize) -> String { - if hash_index <= 2 { - hash_index.to_string() - } else { - "other".to_string() - } -} - -// -// Helpful trait implementation -// - -impl fmt::Display for PendingVotes { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // collect votes per ledger info - let votes = self - .li_digest_to_votes - .iter() - .map(|(li_digest, (_, li))| (li_digest, li.signatures().keys().collect::>())) - .collect::>(); - - // collect timeout votes - let timeout_votes = self - .maybe_partial_2chain_tc - .as_ref() - .map(|partial_tc| partial_tc.signers().collect::>()); - - // write - write!(f, "PendingVotes: [")?; - - for (hash, authors) in votes { - write!(f, "LI {} has {} votes {:?} ", hash, authors.len(), authors)?; - } - - if let Some(authors) = timeout_votes { - write!(f, "{} timeout {:?}", authors.len(), authors)?; - } - - write!(f, "]") - } -} - -// -// Tests -// - -#[cfg(test)] -mod tests { - use super::{PendingVotes, VoteReceptionResult}; - use crate::util::mock_time_service::SimulatedTimeService; - use aptos_config::config::QcAggregatorType; - use aptos_consensus_types::{ - block::block_test_utils::certificate_for_genesis, vote::Vote, vote_data::VoteData, - }; - use aptos_crypto::HashValue; - use aptos_types::{ - block_info::BlockInfo, ledger_info::LedgerInfo, - validator_verifier::random_validator_verifier, - }; - use futures_channel::mpsc::unbounded; - use itertools::Itertools; - use std::sync::Arc; - - /// Creates a random ledger info for epoch 1 and round 1. - fn random_ledger_info() -> LedgerInfo { - LedgerInfo::new( - BlockInfo::new(1, 0, HashValue::random(), HashValue::random(), 0, 0, None), - HashValue::random(), - ) - } - - /// Creates a random VoteData for epoch 1 and round 1, - /// extending a random block at epoch1 and round 0. - fn random_vote_data() -> VoteData { - VoteData::new(BlockInfo::random(1), BlockInfo::random(0)) - } - - #[test] - /// Verify that votes are properly aggregated to QC based on their LedgerInfo digest - fn test_qc_aggregation() { - ::aptos_logger::Logger::init_for_testing(); - - // set up 4 validators - let (signers, validator) = random_validator_verifier(4, Some(2), false); - let (delayed_qc_tx, _) = unbounded(); - let mut pending_votes = PendingVotes::new( - Arc::new(SimulatedTimeService::new()), - delayed_qc_tx, - QcAggregatorType::NoDelay, - ); - - // create random vote from validator[0] - let li1 = random_ledger_info(); - let vote_data_1 = random_vote_data(); - let vote_data_1_author_0 = - Vote::new(vote_data_1, signers[0].author(), li1, &signers[0]).unwrap(); - - // first time a new vote is added -> VoteAdded - assert_eq!( - pending_votes.insert_vote(&vote_data_1_author_0, &validator), - VoteReceptionResult::VoteAdded(1) - ); - - // same author voting for the same thing -> DuplicateVote - assert_eq!( - pending_votes.insert_vote(&vote_data_1_author_0, &validator), - VoteReceptionResult::DuplicateVote - ); - - // same author voting for a different result -> EquivocateVote - let li2 = random_ledger_info(); - let vote_data_2 = random_vote_data(); - let vote_data_2_author_0 = Vote::new( - vote_data_2.clone(), - signers[0].author(), - li2.clone(), - &signers[0], - ) - .unwrap(); - assert_eq!( - pending_votes.insert_vote(&vote_data_2_author_0, &validator), - VoteReceptionResult::EquivocateVote - ); - - // a different author voting for a different result -> VoteAdded - let vote_data_2_author_1 = Vote::new( - vote_data_2.clone(), - signers[1].author(), - li2.clone(), - &signers[1], - ) - .unwrap(); - assert_eq!( - pending_votes.insert_vote(&vote_data_2_author_1, &validator), - VoteReceptionResult::VoteAdded(1) - ); - - // two votes for the ledger info -> NewQuorumCertificate - let vote_data_2_author_2 = - Vote::new(vote_data_2, signers[2].author(), li2, &signers[2]).unwrap(); - match pending_votes.insert_vote(&vote_data_2_author_2, &validator) { - VoteReceptionResult::NewQuorumCertificate(qc) => { - assert!(qc.ledger_info().check_voting_power(&validator).is_ok()); - }, - _ => { - panic!("No QC formed."); - }, - }; - } - - #[test] - fn test_2chain_tc_aggregation() { - ::aptos_logger::Logger::init_for_testing(); - - // set up 4 validators - let (signers, validator) = random_validator_verifier(4, None, false); - let (delayed_qc_tx, _) = unbounded(); - let mut pending_votes = PendingVotes::new( - Arc::new(SimulatedTimeService::new()), - delayed_qc_tx, - QcAggregatorType::NoDelay, - ); - - // submit a new vote from validator[0] -> VoteAdded - let li0 = random_ledger_info(); - let vote0 = random_vote_data(); - let mut vote0_author_0 = Vote::new(vote0, signers[0].author(), li0, &signers[0]).unwrap(); - - assert_eq!( - pending_votes.insert_vote(&vote0_author_0, &validator), - VoteReceptionResult::VoteAdded(1) - ); - - // submit the same vote but enhanced with a timeout -> VoteAdded - let timeout = vote0_author_0.generate_2chain_timeout(certificate_for_genesis()); - let signature = timeout.sign(&signers[0]).unwrap(); - vote0_author_0.add_2chain_timeout(timeout, signature); - - assert_eq!( - pending_votes.insert_vote(&vote0_author_0, &validator), - VoteReceptionResult::VoteAdded(1) - ); - - // another vote for a different block cannot form a TC if it doesn't have a timeout signature - let li1 = random_ledger_info(); - let vote1 = random_vote_data(); - let mut vote1_author_1 = Vote::new(vote1, signers[1].author(), li1, &signers[1]).unwrap(); - assert_eq!( - pending_votes.insert_vote(&vote1_author_1, &validator), - VoteReceptionResult::VoteAdded(1) - ); - - // if that vote is now enhanced with a timeout signature -> EchoTimeout. - let timeout = vote1_author_1.generate_2chain_timeout(certificate_for_genesis()); - let signature = timeout.sign(&signers[1]).unwrap(); - vote1_author_1.add_2chain_timeout(timeout, signature); - match pending_votes.insert_vote(&vote1_author_1, &validator) { - VoteReceptionResult::EchoTimeout(voting_power) => { - assert_eq!(voting_power, 2); - }, - _ => { - panic!("Should echo timeout"); - }, - }; - - let li2 = random_ledger_info(); - let vote2 = random_vote_data(); - let mut vote2_author_2 = Vote::new(vote2, signers[2].author(), li2, &signers[2]).unwrap(); - - // if that vote is now enhanced with a timeout signature -> NewTimeoutCertificate. - let timeout = vote2_author_2.generate_2chain_timeout(certificate_for_genesis()); - let signature = timeout.sign(&signers[2]).unwrap(); - vote2_author_2.add_2chain_timeout(timeout, signature); - - match pending_votes.insert_vote(&vote2_author_2, &validator) { - VoteReceptionResult::New2ChainTimeoutCertificate(tc) => { - assert!(validator - .check_voting_power( - tc.signatures_with_rounds() - .get_voters( - &validator.get_ordered_account_addresses_iter().collect_vec() - ) - .iter(), - true - ) - .is_ok()); - }, - _ => { - panic!("Should form TC"); - }, - }; - } -} diff --git a/consensus/src/persistent_liveness_storage.rs b/consensus/src/persistent_liveness_storage.rs deleted file mode 100644 index d00f86852d473..0000000000000 --- a/consensus/src/persistent_liveness_storage.rs +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{consensusdb::ConsensusDB, epoch_manager::LivenessStorageData, error::DbError}; -use anyhow::{format_err, Context, Result}; -use aptos_config::config::NodeConfig; -use aptos_consensus_types::{ - block::Block, quorum_cert::QuorumCert, timeout_2chain::TwoChainTimeoutCertificate, vote::Vote, -}; -use aptos_crypto::HashValue; -use aptos_logger::prelude::*; -use aptos_storage_interface::DbReader; -use aptos_types::{ - block_info::Round, epoch_change::EpochChangeProof, ledger_info::LedgerInfoWithSignatures, - proof::TransactionAccumulatorSummary, transaction::Version, -}; -use std::{cmp::max, collections::HashSet, sync::Arc}; - -/// PersistentLivenessStorage is essential for maintaining liveness when a node crashes. Specifically, -/// upon a restart, a correct node will recover. Even if all nodes crash, liveness is -/// guaranteed. -/// Blocks persisted are proposed but not yet committed. The committed state is persisted -/// via StateComputer. -pub trait PersistentLivenessStorage: Send + Sync { - /// Persist the blocks and quorum certs into storage atomically. - fn save_tree(&self, blocks: Vec, quorum_certs: Vec) -> Result<()>; - - /// Delete the corresponding blocks and quorum certs atomically. - fn prune_tree(&self, block_ids: Vec) -> Result<()>; - - /// Persist consensus' state - fn save_vote(&self, vote: &Vote) -> Result<()>; - - /// Construct data that can be recovered from ledger - fn recover_from_ledger(&self) -> LedgerRecoveryData; - - /// Construct necessary data to start consensus. - fn start(&self) -> LivenessStorageData; - - /// Persist the highest 2chain timeout certificate for improved liveness - proof for other replicas - /// to jump to this round - fn save_highest_2chain_timeout_cert( - &self, - highest_timeout_cert: &TwoChainTimeoutCertificate, - ) -> Result<()>; - - /// Retrieve a epoch change proof for SafetyRules so it can instantiate its - /// ValidatorVerifier. - fn retrieve_epoch_change_proof(&self, version: u64) -> Result; - - /// Returns a handle of the aptosdb. - fn aptos_db(&self) -> Arc; - - // Returns a handle of the consensus db - fn consensus_db(&self) -> Arc; -} - -#[derive(Clone)] -pub struct RootInfo( - pub Box, - pub QuorumCert, - pub QuorumCert, - pub QuorumCert, -); - -/// LedgerRecoveryData is a subset of RecoveryData that we can get solely from ledger info. -#[derive(Clone)] -pub struct LedgerRecoveryData { - storage_ledger: LedgerInfoWithSignatures, -} - -impl LedgerRecoveryData { - pub fn new(storage_ledger: LedgerInfoWithSignatures) -> Self { - LedgerRecoveryData { storage_ledger } - } - - pub fn committed_round(&self) -> Round { - self.storage_ledger.commit_info().round() - } - - /// Finds the root (last committed block) and returns the root block, the QC to the root block - /// and the ledger info for the root block, return an error if it can not be found. - /// - /// We guarantee that the block corresponding to the storage's latest ledger info always exists. - pub fn find_root( - &self, - blocks: &mut Vec, - quorum_certs: &mut Vec, - ) -> Result { - info!( - "The last committed block id as recorded in storage: {}", - self.storage_ledger - ); - - // We start from the block that storage's latest ledger info, if storage has end-epoch - // LedgerInfo, we generate the virtual genesis block - let (root_id, latest_ledger_info_sig) = if self.storage_ledger.ledger_info().ends_epoch() { - let genesis = - Block::make_genesis_block_from_ledger_info(self.storage_ledger.ledger_info()); - let genesis_qc = QuorumCert::certificate_for_genesis_from_ledger_info( - self.storage_ledger.ledger_info(), - genesis.id(), - ); - let genesis_ledger_info = genesis_qc.ledger_info().clone(); - let genesis_id = genesis.id(); - blocks.push(genesis); - quorum_certs.push(genesis_qc); - (genesis_id, genesis_ledger_info) - } else { - ( - self.storage_ledger.ledger_info().consensus_block_id(), - self.storage_ledger.clone(), - ) - }; - - // sort by (epoch, round) to guarantee the topological order of parent <- child - blocks.sort_by_key(|b| (b.epoch(), b.round())); - - let root_idx = blocks - .iter() - .position(|block| block.id() == root_id) - .ok_or_else(|| format_err!("unable to find root: {}", root_id))?; - let root_block = blocks.remove(root_idx); - let root_quorum_cert = quorum_certs - .iter() - .find(|qc| qc.certified_block().id() == root_block.id()) - .ok_or_else(|| format_err!("No QC found for root: {}", root_id))? - .clone(); - let root_ordered_cert = quorum_certs - .iter() - .find(|qc| qc.commit_info().id() == root_block.id()) - .ok_or_else(|| format_err!("No LI found for root: {}", root_id))? - .clone(); - - info!("Consensus root block is {}", root_block); - - let root_commit_cert = root_ordered_cert - .create_merged_with_executed_state(latest_ledger_info_sig) - .expect("Inconsistent commit proof and evaluation decision, cannot commit block"); - - Ok(RootInfo( - Box::new(root_block), - root_quorum_cert, - root_ordered_cert, - root_commit_cert, - )) - } -} - -pub struct RootMetadata { - pub accu_hash: HashValue, - pub frozen_root_hashes: Vec, - pub num_leaves: Version, -} - -impl RootMetadata { - pub fn version(&self) -> Version { - max(self.num_leaves, 1) - 1 - } - - #[cfg(any(test, feature = "fuzzing"))] - pub fn new_empty() -> Self { - Self { - accu_hash: *aptos_crypto::hash::ACCUMULATOR_PLACEHOLDER_HASH, - frozen_root_hashes: vec![], - num_leaves: 0, - } - } -} - -impl From for RootMetadata { - fn from(summary: TransactionAccumulatorSummary) -> Self { - Self { - accu_hash: summary.0.root_hash, - frozen_root_hashes: summary.0.frozen_subtree_roots, - num_leaves: summary.0.num_leaves, - } - } -} - -/// The recovery data constructed from raw consensusdb data, it'll find the root value and -/// blocks that need cleanup or return error if the input data is inconsistent. -pub struct RecoveryData { - // The last vote message sent by this validator. - last_vote: Option, - root: RootInfo, - root_metadata: RootMetadata, - // 1. the blocks guarantee the topological ordering - parent <- child. - // 2. all blocks are children of the root. - blocks: Vec, - quorum_certs: Vec, - blocks_to_prune: Option>, - - // Liveness data - highest_2chain_timeout_certificate: Option, -} - -impl RecoveryData { - pub fn new( - last_vote: Option, - ledger_recovery_data: LedgerRecoveryData, - mut blocks: Vec, - root_metadata: RootMetadata, - mut quorum_certs: Vec, - highest_2chain_timeout_cert: Option, - ) -> Result { - let root = ledger_recovery_data - .find_root(&mut blocks, &mut quorum_certs) - .with_context(|| { - // for better readability - blocks.sort_by_key(|block| block.round()); - quorum_certs.sort_by_key(|qc| qc.certified_block().round()); - format!( - "\nRoot: {}\nBlocks in db: {}\nQuorum Certs in db: {}\n", - ledger_recovery_data.storage_ledger.ledger_info(), - blocks - .iter() - .map(|b| format!("\n{}", b)) - .collect::>() - .concat(), - quorum_certs - .iter() - .map(|qc| format!("\n{}", qc)) - .collect::>() - .concat(), - ) - })?; - - let blocks_to_prune = Some(Self::find_blocks_to_prune( - root.0.id(), - &mut blocks, - &mut quorum_certs, - )); - let epoch = root.0.epoch(); - Ok(RecoveryData { - last_vote: match last_vote { - Some(v) if v.epoch() == epoch => Some(v), - _ => None, - }, - root, - root_metadata, - blocks, - quorum_certs, - blocks_to_prune, - highest_2chain_timeout_certificate: match highest_2chain_timeout_cert { - Some(tc) if tc.epoch() == epoch => Some(tc), - _ => None, - }, - }) - } - - pub fn root_block(&self) -> &Block { - &self.root.0 - } - - pub fn last_vote(&self) -> Option { - self.last_vote.clone() - } - - pub fn take(self) -> (RootInfo, RootMetadata, Vec, Vec) { - ( - self.root, - self.root_metadata, - self.blocks, - self.quorum_certs, - ) - } - - pub fn take_blocks_to_prune(&mut self) -> Vec { - self.blocks_to_prune - .take() - .expect("blocks_to_prune already taken") - } - - pub fn highest_2chain_timeout_certificate(&self) -> Option { - self.highest_2chain_timeout_certificate.clone() - } - - fn find_blocks_to_prune( - root_id: HashValue, - blocks: &mut Vec, - quorum_certs: &mut Vec, - ) -> Vec { - // prune all the blocks that don't have root as ancestor - let mut tree = HashSet::new(); - let mut to_remove = HashSet::new(); - tree.insert(root_id); - // assume blocks are sorted by round already - blocks.retain(|block| { - if tree.contains(&block.parent_id()) { - tree.insert(block.id()); - true - } else { - to_remove.insert(block.id()); - false - } - }); - quorum_certs.retain(|qc| { - if tree.contains(&qc.certified_block().id()) { - true - } else { - to_remove.insert(qc.certified_block().id()); - false - } - }); - to_remove.into_iter().collect() - } -} - -/// The proxy we use to persist data in db storage service via grpc. -pub struct StorageWriteProxy { - db: Arc, - aptos_db: Arc, -} - -impl StorageWriteProxy { - pub fn new(config: &NodeConfig, aptos_db: Arc) -> Self { - let db = Arc::new(ConsensusDB::new(config.storage.dir())); - StorageWriteProxy { db, aptos_db } - } -} - -impl PersistentLivenessStorage for StorageWriteProxy { - fn save_tree(&self, blocks: Vec, quorum_certs: Vec) -> Result<()> { - Ok(self - .db - .save_blocks_and_quorum_certificates(blocks, quorum_certs)?) - } - - fn prune_tree(&self, block_ids: Vec) -> Result<()> { - if !block_ids.is_empty() { - // quorum certs that certified the block_ids will get removed - self.db.delete_blocks_and_quorum_certificates(block_ids)?; - } - Ok(()) - } - - fn save_vote(&self, vote: &Vote) -> Result<()> { - Ok(self.db.save_vote(bcs::to_bytes(vote)?)?) - } - - fn recover_from_ledger(&self) -> LedgerRecoveryData { - let latest_ledger_info = self - .aptos_db - .get_latest_ledger_info() - .expect("Failed to get latest ledger info."); - LedgerRecoveryData::new(latest_ledger_info) - } - - fn start(&self) -> LivenessStorageData { - info!("Start consensus recovery."); - let raw_data = self - .db - .get_data() - .expect("unable to recover consensus data"); - - let last_vote = raw_data - .0 - .map(|bytes| bcs::from_bytes(&bytes[..]).expect("unable to deserialize last vote")); - - let highest_2chain_timeout_cert = raw_data.1.map(|b| { - bcs::from_bytes(&b).expect("unable to deserialize highest 2-chain timeout cert") - }); - let blocks = raw_data.2; - let quorum_certs: Vec<_> = raw_data.3; - let blocks_repr: Vec = blocks.iter().map(|b| format!("\n\t{}", b)).collect(); - info!( - "The following blocks were restored from ConsensusDB : {}", - blocks_repr.concat() - ); - let qc_repr: Vec = quorum_certs - .iter() - .map(|qc| format!("\n\t{}", qc)) - .collect(); - info!( - "The following quorum certs were restored from ConsensusDB: {}", - qc_repr.concat() - ); - - // find the block corresponding to storage latest ledger info - let latest_ledger_info = self - .aptos_db - .get_latest_ledger_info() - .expect("Failed to get latest ledger info."); - let accumulator_summary = self - .aptos_db - .get_accumulator_summary(latest_ledger_info.ledger_info().version()) - .expect("Failed to get accumulator summary."); - let ledger_recovery_data = LedgerRecoveryData::new(latest_ledger_info); - - match RecoveryData::new( - last_vote, - ledger_recovery_data.clone(), - blocks, - accumulator_summary.into(), - quorum_certs, - highest_2chain_timeout_cert, - ) { - Ok(mut initial_data) => { - (self as &dyn PersistentLivenessStorage) - .prune_tree(initial_data.take_blocks_to_prune()) - .expect("unable to prune dangling blocks during restart"); - if initial_data.last_vote.is_none() { - self.db - .delete_last_vote_msg() - .expect("unable to cleanup last vote"); - } - if initial_data.highest_2chain_timeout_certificate.is_none() { - self.db - .delete_highest_2chain_timeout_certificate() - .expect("unable to cleanup highest 2-chain timeout cert"); - } - info!( - "Starting up the consensus state machine with recovery data - [last_vote {}], [highest timeout certificate: {}]", - initial_data.last_vote.as_ref().map_or("None".to_string(), |v| v.to_string()), - initial_data.highest_2chain_timeout_certificate().as_ref().map_or("None".to_string(), |v| v.to_string()), - ); - - LivenessStorageData::FullRecoveryData(initial_data) - }, - Err(e) => { - error!(error = ?e, "Failed to construct recovery data"); - LivenessStorageData::PartialRecoveryData(ledger_recovery_data) - }, - } - } - - fn save_highest_2chain_timeout_cert( - &self, - highest_timeout_cert: &TwoChainTimeoutCertificate, - ) -> Result<()> { - Ok(self - .db - .save_highest_2chain_timeout_certificate(bcs::to_bytes(highest_timeout_cert)?)?) - } - - fn retrieve_epoch_change_proof(&self, version: u64) -> Result { - let (_, proofs) = self - .aptos_db - .get_state_proof(version) - .map_err(DbError::from)? - .into_inner(); - Ok(proofs) - } - - fn aptos_db(&self) -> Arc { - self.aptos_db.clone() - } - - fn consensus_db(&self) -> Arc { - self.db.clone() - } -} diff --git a/consensus/src/pipeline/buffer.rs b/consensus/src/pipeline/buffer.rs deleted file mode 100644 index 29ead7aba94d8..0000000000000 --- a/consensus/src/pipeline/buffer.rs +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::pipeline::hashable::Hashable; -use aptos_crypto::HashValue; -use std::collections::HashMap; - -pub struct LinkedItem { - // use option so we don't need T to be cloneable - elem: Option, - // index is for find_element_by_key to have a starting position (similar to find_element) - index: u64, - next: Option, -} - -pub type Cursor = Option; - -/// Buffer implementes an ordered dictionary -/// It supports push_back, pop_front, and lookup by HashValue -pub struct Buffer { - map: HashMap>, - count: u64, - head: Cursor, - tail: Cursor, -} - -impl Buffer { - pub fn new() -> Self { - Self { - map: HashMap::new(), - count: 0, - head: None, - tail: None, - } - } - - pub fn len(&self) -> usize { - self.map.len() - } - - pub fn head_cursor(&self) -> &Cursor { - &self.head - } - - #[cfg(test)] - pub fn tail_cursor(&self) -> &Cursor { - &self.tail - } - - pub fn push_back(&mut self, elem: T) { - self.count = self.count.checked_add(1).unwrap(); - let t_hash = elem.hash(); - self.map.insert(t_hash, LinkedItem { - elem: Some(elem), - index: self.count, - next: None, - }); - if let Some(tail) = self.tail { - self.map.get_mut(&tail).unwrap().next = Some(t_hash); - } - self.tail = Some(t_hash); - self.head.get_or_insert(t_hash); - } - - pub fn pop_front(&mut self) -> Option { - self.head.take().map(|head| { - let mut item = self.map.remove(&head).unwrap(); - let elem = item.elem.take(); - self.head = item.next; - if self.head.is_none() { - // empty - self.tail = None; - } - elem.unwrap() - }) - } - - // utils - assuming item is not None - pub fn get_next(&self, cursor: &Cursor) -> Cursor { - self.map.get(cursor.as_ref().unwrap()).unwrap().next - } - - pub fn get(&self, cursor: &Cursor) -> &T { - self.map - .get(cursor.as_ref().unwrap()) - .unwrap() - .elem - .as_ref() - .unwrap() - } - - pub fn set(&mut self, cursor: &Cursor, new_val: T) { - self.map - .get_mut(cursor.as_ref().unwrap()) - .unwrap() - .elem - .replace(new_val); - } - - pub fn take(&mut self, cursor: &Cursor) -> T { - self.map - .get_mut(cursor.as_ref().unwrap()) - .unwrap() - .elem - .take() - .unwrap() - } - - pub fn exist(&self, cursor: &Cursor) -> bool { - cursor.map_or(false, |key| self.map.contains_key(&key)) - } - - /// find_elem returns the first item non-prior to `cursor` that compare(item) is true - /// if no such item exists, the function returns None - pub fn find_elem_from bool>(&self, cursor: Cursor, compare: F) -> Cursor { - let mut current = cursor; - if !self.exist(&cursor) { - return None; - } - while current.is_some() { - if compare(self.get(¤t)) { - return current; - } - current = self.get_next(¤t); - } - None - } - - /// we make sure that the element found by the key is after `cursor` - /// if `cursor` is None, this function returns None (same as find_elem) - pub fn find_elem_by_key(&self, cursor: Cursor, key: HashValue) -> Cursor { - let cursor_order = self.map.get(cursor.as_ref()?).unwrap().index; - let item = self.map.get(&key)?; - if item.index >= cursor_order { - Some(key) - } else { - None - } - } -} - -// tests -#[cfg(test)] -mod test { - use super::Buffer; - use crate::pipeline::hashable::Hashable; - use aptos_crypto::HashValue; - use std::fmt::{Debug, Formatter}; - - #[derive(PartialEq, Eq)] - pub struct HashWrapper { - inner: HashValue, - } - - impl Debug for HashWrapper { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "HashWrapper: [{}]", self.inner) - } - } - - impl From for HashWrapper { - fn from(val: u64) -> Self { - Self { - inner: HashValue::from_u64(val), - } - } - } - - impl Hashable for HashWrapper { - fn hash(&self) -> HashValue { - self.inner - } - } - - #[test] - fn basics() { - let mut buffer = Buffer::::new(); - - // Check empty list behaves right - assert_eq!(buffer.pop_front(), None); - - // Populate list - buffer.push_back(HashWrapper::from(1)); - buffer.push_back(HashWrapper::from(2)); - buffer.push_back(HashWrapper::from(3)); - - // Check normal removal - assert_eq!(buffer.pop_front(), Some(HashWrapper::from(1))); - assert_eq!(buffer.pop_front(), Some(HashWrapper::from(2))); - - // Push some more just to make sure nothing's corrupted - buffer.push_back(HashWrapper::from(4)); - buffer.push_back(HashWrapper::from(5)); - - // Check normal removal - assert_eq!(buffer.pop_front(), Some(HashWrapper::from(3))); - assert_eq!(buffer.pop_front(), Some(HashWrapper::from(4))); - - // Check exhaustion - assert_eq!(buffer.pop_front(), Some(HashWrapper::from(5))); - assert_eq!(buffer.pop_front(), None); - } - - #[test] - fn find() { - let mut buffer = Buffer::::new(); - buffer.push_back(HashWrapper::from(1)); - buffer.push_back(HashWrapper::from(2)); - buffer.push_back(HashWrapper::from(3)); - - // look for 1 (succeed) - let res_1 = buffer.find_elem_by_key(*buffer.head_cursor(), HashValue::from_u64(1)); - assert_eq!(buffer.get(&res_1), &HashWrapper::from(1)); - // look for 4 (fail) - let res_no_4 = buffer.find_elem_by_key(*buffer.head_cursor(), HashValue::from_u64(4)); - assert!(res_no_4.is_none()); - // look for 1 after (or on) the tail (fail) - let res_no_1 = buffer.find_elem_by_key(*buffer.tail_cursor(), HashValue::from_u64(1)); - assert!(res_no_1.is_none()); - - // look for 2 (succeed) - let res_2 = - buffer.find_elem_from(*buffer.head_cursor(), |item| item == &HashWrapper::from(2)); - assert_eq!(buffer.get(&res_2), &HashWrapper::from(2)); - // look for 5 (fail) - let res_no_5 = - buffer.find_elem_from(*buffer.head_cursor(), |item| item == &HashWrapper::from(5)); - assert!(res_no_5.is_none()); - // look for 2 after (or on) the tail (fail) - let res_no_2 = - buffer.find_elem_from(*buffer.tail_cursor(), |item| item == &HashWrapper::from(2)); - assert!(res_no_2.is_none()); - } - - #[test] - fn get_set_take() { - let mut buffer = Buffer::::new(); - buffer.push_back(HashWrapper::from(1)); - buffer.push_back(HashWrapper::from(2)); - buffer.push_back(HashWrapper::from(3)); - - // test get - assert_eq!(buffer.get(buffer.head_cursor()), &HashWrapper::from(1)); - assert_eq!(buffer.get(buffer.tail_cursor()), &HashWrapper::from(3)); - - // test set - let tail = *buffer.tail_cursor(); - buffer.set(&tail, HashWrapper::from(5)); - assert_eq!(buffer.get(buffer.tail_cursor()), &HashWrapper::from(5)); - - // test take - let head = *buffer.head_cursor(); - assert_eq!(buffer.take(&head), HashWrapper::from(1)); - } -} diff --git a/consensus/src/pipeline/buffer_item.rs b/consensus/src/pipeline/buffer_item.rs deleted file mode 100644 index d251e97e80fa0..0000000000000 --- a/consensus/src/pipeline/buffer_item.rs +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{pipeline::hashable::Hashable, state_replication::StateComputerCommitCallBackType}; -use anyhow::anyhow; -use aptos_consensus_types::{ - common::Author, pipeline::commit_vote::CommitVote, pipelined_block::PipelinedBlock, -}; -use aptos_crypto::{bls12381, HashValue}; -use aptos_executor_types::ExecutorResult; -use aptos_logger::prelude::*; -use aptos_reliable_broadcast::DropGuard; -use aptos_types::{ - aggregate_signature::PartialSignatures, - block_info::BlockInfo, - ledger_info::{LedgerInfo, LedgerInfoWithPartialSignatures, LedgerInfoWithSignatures}, - validator_verifier::ValidatorVerifier, -}; -use futures::future::BoxFuture; -use itertools::zip_eq; -use tokio::time::Instant; - -fn generate_commit_ledger_info( - commit_info: &BlockInfo, - ordered_proof: &LedgerInfoWithSignatures, -) -> LedgerInfo { - LedgerInfo::new( - commit_info.clone(), - ordered_proof.ledger_info().consensus_data_hash(), - ) -} - -fn verify_signatures( - unverified_signatures: PartialSignatures, - validator: &ValidatorVerifier, - commit_ledger_info: &LedgerInfo, -) -> PartialSignatures { - // Returns a valid partial signature from a set of unverified signatures. - // TODO: Validating individual signatures in expensive. Replace this with optimistic signature - // verification for BLS. Here, we can implement a tree-based batch verification technique that - // filters out invalid signature shares much faster when there are only a few of them - // (e.g., [LM07]: Finding Invalid Signatures in Pairing-Based Batches, - // by Law, Laurie and Matt, Brian J., in Cryptography and Coding, 2007). - PartialSignatures::new( - unverified_signatures - .signatures() - .iter() - .filter(|(author, sig)| validator.verify(**author, commit_ledger_info, sig).is_ok()) - .map(|(author, sig)| (*author, sig.clone())) - .collect(), - ) -} - -fn generate_executed_item_from_ordered( - commit_info: BlockInfo, - executed_blocks: Vec, - verified_signatures: PartialSignatures, - callback: StateComputerCommitCallBackType, - ordered_proof: LedgerInfoWithSignatures, -) -> BufferItem { - debug!("{} advance to executed from ordered", commit_info); - let partial_commit_proof = LedgerInfoWithPartialSignatures::new( - generate_commit_ledger_info(&commit_info, &ordered_proof), - verified_signatures, - ); - BufferItem::Executed(Box::new(ExecutedItem { - executed_blocks, - partial_commit_proof, - callback, - commit_info, - ordered_proof, - })) -} - -fn aggregate_commit_proof( - commit_ledger_info: &LedgerInfo, - verified_signatures: &PartialSignatures, - validator: &ValidatorVerifier, -) -> LedgerInfoWithSignatures { - let aggregated_sig = validator - .aggregate_signatures(verified_signatures) - .expect("Failed to generate aggregated signature"); - LedgerInfoWithSignatures::new(commit_ledger_info.clone(), aggregated_sig) -} - -// we differentiate buffer items at different stages -// for better code readability -pub struct OrderedItem { - pub unverified_signatures: PartialSignatures, - // This can happen in the fast forward sync path, where we can receive the commit proof - // from peers. - pub commit_proof: Option, - pub callback: StateComputerCommitCallBackType, - pub ordered_blocks: Vec, - pub ordered_proof: LedgerInfoWithSignatures, -} - -pub struct ExecutedItem { - pub executed_blocks: Vec, - pub partial_commit_proof: LedgerInfoWithPartialSignatures, - pub callback: StateComputerCommitCallBackType, - pub commit_info: BlockInfo, - pub ordered_proof: LedgerInfoWithSignatures, -} - -pub struct SignedItem { - pub executed_blocks: Vec, - pub partial_commit_proof: LedgerInfoWithPartialSignatures, - pub callback: StateComputerCommitCallBackType, - pub commit_vote: CommitVote, - pub rb_handle: Option<(Instant, DropGuard)>, -} - -pub struct AggregatedItem { - pub executed_blocks: Vec, - pub commit_proof: LedgerInfoWithSignatures, - pub callback: StateComputerCommitCallBackType, -} - -pub enum BufferItem { - Ordered(Box), - Executed(Box), - Signed(Box), - Aggregated(Box), -} - -impl Hashable for BufferItem { - fn hash(&self) -> HashValue { - self.block_id() - } -} - -pub type ExecutionFut = BoxFuture<'static, ExecutorResult>>; - -impl BufferItem { - pub fn new_ordered( - ordered_blocks: Vec, - ordered_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> Self { - Self::Ordered(Box::new(OrderedItem { - unverified_signatures: PartialSignatures::empty(), - commit_proof: None, - callback, - ordered_blocks, - ordered_proof, - })) - } - - // pipeline functions - pub fn advance_to_executed_or_aggregated( - self, - executed_blocks: Vec, - validator: &ValidatorVerifier, - epoch_end_timestamp: Option, - ) -> Self { - match self { - Self::Ordered(ordered_item) => { - let OrderedItem { - ordered_blocks, - commit_proof, - unverified_signatures, - callback, - ordered_proof, - } = *ordered_item; - for (b1, b2) in zip_eq(ordered_blocks.iter(), executed_blocks.iter()) { - assert_eq!(b1.id(), b2.id()); - } - let mut commit_info = executed_blocks.last().unwrap().block_info(); - match epoch_end_timestamp { - Some(timestamp) if commit_info.timestamp_usecs() != timestamp => { - assert!(executed_blocks.last().unwrap().is_reconfiguration_suffix()); - commit_info.change_timestamp(timestamp); - }, - _ => (), - } - if let Some(commit_proof) = commit_proof { - // We have already received the commit proof in fast forward sync path, - // we can just use that proof and proceed to aggregated - assert_eq!(commit_proof.commit_info().clone(), commit_info); - debug!( - "{} advance to aggregated from ordered", - commit_proof.commit_info() - ); - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks, - commit_proof, - callback, - })) - } else { - let commit_ledger_info = - generate_commit_ledger_info(&commit_info, &ordered_proof); - - let verified_signatures = - verify_signatures(unverified_signatures, validator, &commit_ledger_info); - if (validator.check_voting_power(verified_signatures.signatures().keys(), true)) - .is_ok() - { - let commit_proof = aggregate_commit_proof( - &commit_ledger_info, - &verified_signatures, - validator, - ); - debug!( - "{} advance to aggregated from ordered", - commit_proof.commit_info() - ); - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks, - commit_proof, - callback, - })) - } else { - generate_executed_item_from_ordered( - commit_info, - executed_blocks, - verified_signatures, - callback, - ordered_proof, - ) - } - } - }, - _ => { - panic!("Only ordered blocks can advance to executed blocks.") - }, - } - } - - pub fn advance_to_signed(self, author: Author, signature: bls12381::Signature) -> Self { - match self { - Self::Executed(executed_item) => { - let ExecutedItem { - executed_blocks, - callback, - partial_commit_proof, - .. - } = *executed_item; - - // we don't add the signature here, it'll be added when receiving the commit vote from self - let commit_vote = CommitVote::new_with_signature( - author, - partial_commit_proof.ledger_info().clone(), - signature, - ); - debug!("{} advance to signed", partial_commit_proof.commit_info()); - - Self::Signed(Box::new(SignedItem { - executed_blocks, - callback, - partial_commit_proof, - commit_vote, - rb_handle: None, - })) - }, - _ => { - panic!("Only executed buffer items can advance to signed blocks.") - }, - } - } - - /// this function assumes block id matches and the validity of ledger_info and that it has the voting power - /// it returns an updated item - pub fn try_advance_to_aggregated_with_ledger_info( - self, - commit_proof: LedgerInfoWithSignatures, - ) -> Self { - match self { - Self::Signed(signed_item) => { - let SignedItem { - executed_blocks, - callback, - partial_commit_proof: local_commit_proof, - .. - } = *signed_item; - assert_eq!(local_commit_proof.commit_info(), commit_proof.commit_info(),); - debug!( - "{} advance to aggregated with commit decision", - commit_proof.commit_info() - ); - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks, - callback, - commit_proof, - })) - }, - Self::Executed(executed_item) => { - let ExecutedItem { - executed_blocks, - callback, - commit_info, - .. - } = *executed_item; - assert_eq!(commit_info, *commit_proof.commit_info()); - debug!( - "{} advance to aggregated with commit decision", - commit_proof.commit_info() - ); - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks, - callback, - commit_proof, - })) - }, - Self::Ordered(ordered_item) => { - let ordered = *ordered_item; - assert!(ordered - .ordered_proof - .commit_info() - .match_ordered_only(commit_proof.commit_info())); - // can't aggregate it without execution, only store the signatures - debug!( - "{} received commit decision in ordered stage", - commit_proof.commit_info() - ); - Self::Ordered(Box::new(OrderedItem { - commit_proof: Some(commit_proof), - ..ordered - })) - }, - Self::Aggregated(_) => { - unreachable!("Found aggregated buffer item but any aggregated buffer item should get dequeued right away."); - }, - } - } - - pub fn try_advance_to_aggregated(self, validator: &ValidatorVerifier) -> Self { - match self { - Self::Signed(signed_item) => { - if validator - .check_voting_power(signed_item.partial_commit_proof.signatures().keys(), true) - .is_ok() - { - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks: signed_item.executed_blocks, - commit_proof: aggregate_commit_proof( - signed_item.partial_commit_proof.ledger_info(), - signed_item.partial_commit_proof.partial_sigs(), - validator, - ), - callback: signed_item.callback, - })) - } else { - Self::Signed(signed_item) - } - }, - Self::Executed(executed_item) => { - if validator - .check_voting_power( - executed_item.partial_commit_proof.signatures().keys(), - true, - ) - .is_ok() - { - Self::Aggregated(Box::new(AggregatedItem { - executed_blocks: executed_item.executed_blocks, - commit_proof: aggregate_commit_proof( - executed_item.partial_commit_proof.ledger_info(), - executed_item.partial_commit_proof.partial_sigs(), - validator, - ), - callback: executed_item.callback, - })) - } else { - Self::Executed(executed_item) - } - }, - _ => self, - } - } - - // generic functions - pub fn get_blocks(&self) -> &Vec { - match self { - Self::Ordered(ordered) => &ordered.ordered_blocks, - Self::Executed(executed) => &executed.executed_blocks, - Self::Signed(signed) => &signed.executed_blocks, - Self::Aggregated(aggregated) => &aggregated.executed_blocks, - } - } - - pub fn block_id(&self) -> HashValue { - self.get_blocks().last().unwrap().id() - } - - pub fn add_signature_if_matched(&mut self, vote: CommitVote) -> anyhow::Result<()> { - let target_commit_info = vote.commit_info(); - let author = vote.author(); - let signature = vote.signature().clone(); - match self { - Self::Ordered(ordered) => { - if ordered - .ordered_proof - .commit_info() - .match_ordered_only(target_commit_info) - { - // we optimistically assume the vote will be valid in the future. - // when advancing to executed item, we will check if the sigs are valid. - // each author at most stores a single sig for each item, - // so an adversary will not be able to flood our memory. - ordered - .unverified_signatures - .add_signature(author, signature); - return Ok(()); - } - }, - Self::Executed(executed) => { - if executed.commit_info == *target_commit_info { - executed - .partial_commit_proof - .add_signature(author, signature); - return Ok(()); - } - }, - Self::Signed(signed) => { - if signed.partial_commit_proof.commit_info() == target_commit_info { - signed.partial_commit_proof.add_signature(author, signature); - return Ok(()); - } - }, - Self::Aggregated(aggregated) => { - // we do not need to do anything for aggregated - // but return true is helpful to stop the outer loop early - if aggregated.commit_proof.commit_info() == target_commit_info { - return Ok(()); - } - }, - } - Err(anyhow!("Inconsistent commit info.")) - } - - pub fn is_ordered(&self) -> bool { - matches!(self, Self::Ordered(_)) - } - - pub fn is_executed(&self) -> bool { - matches!(self, Self::Executed(_)) - } - - pub fn is_signed(&self) -> bool { - matches!(self, Self::Signed(_)) - } - - pub fn is_aggregated(&self) -> bool { - matches!(self, Self::Aggregated(_)) - } - - pub fn unwrap_signed_mut(&mut self) -> &mut SignedItem { - match self { - BufferItem::Signed(item) => item.as_mut(), - _ => panic!("Not signed item"), - } - } - - pub fn unwrap_executed_ref(&self) -> &ExecutedItem { - match self { - BufferItem::Executed(item) => item.as_ref(), - _ => panic!("Not executed item"), - } - } - - pub fn unwrap_aggregated(self) -> AggregatedItem { - match self { - BufferItem::Aggregated(item) => *item, - _ => panic!("Not aggregated item"), - } - } -} diff --git a/consensus/src/pipeline/buffer_manager.rs b/consensus/src/pipeline/buffer_manager.rs deleted file mode 100644 index e2fdf442eadaf..0000000000000 --- a/consensus/src/pipeline/buffer_manager.rs +++ /dev/null @@ -1,794 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - counters, monitor, - network::{IncomingCommitRequest, NetworkSender}, - network_interface::ConsensusMsg, - pipeline::{ - buffer::{Buffer, Cursor}, - buffer_item::BufferItem, - commit_reliable_broadcast::{AckState, CommitMessage}, - execution_schedule_phase::ExecutionRequest, - execution_wait_phase::{ExecutionResponse, ExecutionWaitRequest}, - persisting_phase::PersistingRequest, - pipeline_phase::CountedRequest, - signing_phase::{SigningRequest, SigningResponse}, - }, - state_replication::StateComputerCommitCallBackType, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_consensus_types::{ - common::Author, pipeline::commit_decision::CommitDecision, pipelined_block::PipelinedBlock, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorError; -use aptos_logger::prelude::*; -use aptos_network::protocols::{rpc::error::RpcError, wire::handshake::v1::ProtocolId}; -use aptos_reliable_broadcast::{DropGuard, ReliableBroadcast}; -use aptos_time_service::TimeService; -use aptos_types::{ - account_address::AccountAddress, epoch_change::EpochChangeProof, epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, -}; -use bytes::Bytes; -use futures::{ - channel::{ - mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, - oneshot, - }, - future::{AbortHandle, Abortable}, - FutureExt, SinkExt, StreamExt, -}; -use once_cell::sync::OnceCell; -use std::sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, - Arc, -}; -use tokio::time::{Duration, Instant}; -use tokio_retry::strategy::ExponentialBackoff; - -pub const COMMIT_VOTE_BROADCAST_INTERVAL_MS: u64 = 1500; -pub const COMMIT_VOTE_REBROADCAST_INTERVAL_MS: u64 = 30000; -pub const LOOP_INTERVAL_MS: u64 = 1500; - -#[derive(Debug, Default)] -pub struct ResetAck {} - -pub enum ResetSignal { - Stop, - TargetRound(u64), -} - -pub struct ResetRequest { - pub tx: oneshot::Sender, - pub signal: ResetSignal, -} - -pub struct OrderedBlocks { - pub ordered_blocks: Vec, - pub ordered_proof: LedgerInfoWithSignatures, - pub callback: StateComputerCommitCallBackType, -} - -pub type BufferItemRootType = Cursor; -pub type Sender = UnboundedSender; -pub type Receiver = UnboundedReceiver; - -pub fn create_channel() -> (Sender, Receiver) { - unbounded::() -} - -/// BufferManager handles the states of ordered blocks and -/// interacts with the execution phase, the signing phase, and -/// the persisting phase. -pub struct BufferManager { - author: Author, - - buffer: Buffer, - - // the roots point to the first *unprocessed* item. - // None means no items ready to be processed (either all processed or no item finishes previous stage) - execution_root: BufferItemRootType, - execution_schedule_phase_tx: Sender>, - execution_schedule_phase_rx: Receiver, - execution_wait_phase_tx: Sender>, - execution_wait_phase_rx: Receiver, - - signing_root: BufferItemRootType, - signing_phase_tx: Sender>, - signing_phase_rx: Receiver, - - commit_msg_tx: Arc, - reliable_broadcast: ReliableBroadcast, - commit_proof_rb_handle: Option, - - // message received from the network - commit_msg_rx: - Option>, - - // we don't hear back from the persisting phase - persisting_phase_tx: Sender>, - - block_rx: UnboundedReceiver, - reset_rx: UnboundedReceiver, - stop: bool, - - epoch_state: Arc, - - ongoing_tasks: Arc, - // Since proposal_generator is not aware of reconfiguration any more, the suffix blocks - // will not have the same timestamp as the reconfig block which violates the invariant - // that block.timestamp == state.timestamp because no txn is executed in suffix blocks. - // We change the timestamp field of the block info to maintain the invariant. - // If the executed blocks are b1 <- b2 <- r <- b4 <- b5 with timestamp t1..t5 - // we replace t5 with t3 (from reconfiguration block) since that's the last timestamp - // being updated on-chain. - end_epoch_timestamp: OnceCell, - previous_commit_time: Instant, - reset_flag: Arc, - bounded_executor: BoundedExecutor, -} - -impl BufferManager { - #[allow(clippy::too_many_arguments)] - pub fn new( - author: Author, - execution_schedule_phase_tx: Sender>, - execution_schedule_phase_rx: Receiver, - execution_wait_phase_tx: Sender>, - execution_wait_phase_rx: Receiver, - signing_phase_tx: Sender>, - signing_phase_rx: Receiver, - commit_msg_tx: Arc, - commit_msg_rx: aptos_channels::aptos_channel::Receiver< - AccountAddress, - IncomingCommitRequest, - >, - persisting_phase_tx: Sender>, - block_rx: UnboundedReceiver, - reset_rx: UnboundedReceiver, - epoch_state: Arc, - ongoing_tasks: Arc, - reset_flag: Arc, - executor: BoundedExecutor, - ) -> Self { - let buffer = Buffer::::new(); - - let rb_backoff_policy = ExponentialBackoff::from_millis(2) - .factor(50) - .max_delay(Duration::from_secs(5)); - Self { - author, - - buffer, - - execution_root: None, - execution_schedule_phase_tx, - execution_schedule_phase_rx, - execution_wait_phase_tx, - execution_wait_phase_rx, - - signing_root: None, - signing_phase_tx, - signing_phase_rx, - - reliable_broadcast: ReliableBroadcast::new( - epoch_state.verifier.get_ordered_account_addresses(), - commit_msg_tx.clone(), - rb_backoff_policy, - TimeService::real(), - Duration::from_millis(COMMIT_VOTE_BROADCAST_INTERVAL_MS), - executor.clone(), - ), - commit_proof_rb_handle: None, - commit_msg_tx, - commit_msg_rx: Some(commit_msg_rx), - - persisting_phase_tx, - - block_rx, - reset_rx, - stop: false, - - epoch_state, - ongoing_tasks, - end_epoch_timestamp: OnceCell::new(), - previous_commit_time: Instant::now(), - reset_flag, - bounded_executor: executor, - } - } - - fn do_reliable_broadcast(&self, message: CommitMessage) -> DropGuard { - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - let task = self.reliable_broadcast.broadcast( - message, - AckState::new( - self.epoch_state - .verifier - .get_ordered_account_addresses_iter(), - ), - ); - tokio::spawn(Abortable::new(task, abort_registration)); - DropGuard::new(abort_handle) - } - - fn create_new_request(&self, req: Request) -> CountedRequest { - CountedRequest::new(req, self.ongoing_tasks.clone()) - } - - fn spawn_retry_request( - mut sender: Sender, - request: T, - duration: Duration, - ) { - counters::BUFFER_MANAGER_RETRY_COUNT.inc(); - spawn_named!("retry request", async move { - tokio::time::sleep(duration).await; - sender - .send(request) - .await - .expect("Failed to send retry request"); - }); - } - - /// process incoming ordered blocks - /// push them into the buffer and update the roots if they are none. - async fn process_ordered_blocks(&mut self, ordered_blocks: OrderedBlocks) { - let OrderedBlocks { - ordered_blocks, - ordered_proof, - callback, - } = ordered_blocks; - - info!( - "Receive ordered block {}, the queue size is {}", - ordered_proof.commit_info(), - self.buffer.len() + 1, - ); - - let request = self.create_new_request(ExecutionRequest { - ordered_blocks: ordered_blocks.clone(), - lifetime_guard: self.create_new_request(()), - }); - self.execution_schedule_phase_tx - .send(request) - .await - .expect("Failed to send execution schedule request"); - - let item = BufferItem::new_ordered(ordered_blocks, ordered_proof, callback); - self.buffer.push_back(item); - } - - /// Set the execution root to the first not executed item (Ordered) and send execution request - /// Set to None if not exist - async fn advance_execution_root(&mut self) { - let cursor = self.execution_root; - self.execution_root = self - .buffer - .find_elem_from(cursor.or_else(|| *self.buffer.head_cursor()), |item| { - item.is_ordered() - }); - info!( - "Advance execution root from {:?} to {:?}", - cursor, self.execution_root - ); - if self.execution_root.is_some() && cursor == self.execution_root { - // Schedule retry. - // NOTE: probably should schedule retry for all ordered blocks, but since execution error - // is not expected nor retryable in reality, I'd rather remove retrying or do it more - // properly than complicating it here. - let ordered_blocks = self.buffer.get(&self.execution_root).get_blocks().clone(); - let request = self.create_new_request(ExecutionRequest { - ordered_blocks, - lifetime_guard: self.create_new_request(()), - }); - let sender = self.execution_schedule_phase_tx.clone(); - Self::spawn_retry_request(sender, request, Duration::from_millis(100)); - } - // Otherwise do nothing, because the execution wait phase is driven by the response of - // the execution schedule phase, which is in turn fed as soon as the ordered blocks - // come in. - } - - /// Set the signing root to the first not signed item (Executed) and send execution request - /// Set to None if not exist - async fn advance_signing_root(&mut self) { - let cursor = self.signing_root; - self.signing_root = self - .buffer - .find_elem_from(cursor.or_else(|| *self.buffer.head_cursor()), |item| { - item.is_executed() - }); - info!( - "Advance signing root from {:?} to {:?}", - cursor, self.signing_root - ); - if self.signing_root.is_some() { - let item = self.buffer.get(&self.signing_root); - let executed_item = item.unwrap_executed_ref(); - let request = self.create_new_request(SigningRequest { - ordered_ledger_info: executed_item.ordered_proof.clone(), - commit_ledger_info: executed_item.partial_commit_proof.ledger_info().clone(), - }); - if cursor == self.signing_root { - let sender = self.signing_phase_tx.clone(); - Self::spawn_retry_request(sender, request, Duration::from_millis(100)); - } else { - self.signing_phase_tx - .send(request) - .await - .expect("Failed to send signing request"); - } - } - } - - /// Pop the prefix of buffer items until (including) target_block_id - /// Send persist request. - async fn advance_head(&mut self, target_block_id: HashValue) { - let mut blocks_to_persist: Vec> = vec![]; - - while let Some(item) = self.buffer.pop_front() { - blocks_to_persist.extend( - item.get_blocks() - .iter() - .map(|eb| Arc::new(eb.clone())) - .collect::>>(), - ); - if self.signing_root == Some(item.block_id()) { - self.signing_root = None; - } - if self.execution_root == Some(item.block_id()) { - self.execution_root = None; - } - if item.block_id() == target_block_id { - let aggregated_item = item.unwrap_aggregated(); - let block = aggregated_item.executed_blocks.last().unwrap().block(); - observe_block(block.timestamp_usecs(), BlockStage::COMMIT_CERTIFIED); - // TODO: As all the validators broadcast commit votes directly to all other validators, - // the proposer do not have to broadcast commit decision again. Remove this if block. - // if we're the proposer for the block, we're responsible to broadcast the commit decision. - if block.author() == Some(self.author) { - let commit_decision = CommitMessage::Decision(CommitDecision::new( - aggregated_item.commit_proof.clone(), - )); - self.commit_proof_rb_handle - .replace(self.do_reliable_broadcast(commit_decision)); - } - let commit_proof = aggregated_item.commit_proof.clone(); - if commit_proof.ledger_info().ends_epoch() { - // the epoch ends, reset to avoid executing more blocks, execute after - // this persisting request will result in BlockNotFound - self.reset().await; - } - self.persisting_phase_tx - .send(self.create_new_request(PersistingRequest { - blocks: blocks_to_persist, - commit_ledger_info: aggregated_item.commit_proof, - // we use the last callback - // this is okay because the callback function (from BlockStore::commit) - // takes in the actual blocks and ledger info from the state computer - // the encoded values are references to the block_tree, storage, and a commit root - // the block_tree and storage are the same for all the callbacks in the current epoch - // the commit root is used in logging only. - callback: aggregated_item.callback, - })) - .await - .expect("Failed to send persist request"); - // this needs to be done after creating the persisting request to avoid it being lost - if commit_proof.ledger_info().ends_epoch() { - self.commit_msg_tx - .send_epoch_change(EpochChangeProof::new(vec![commit_proof], false)) - .await; - } - info!("Advance head to {:?}", self.buffer.head_cursor()); - self.previous_commit_time = Instant::now(); - return; - } - } - unreachable!("Aggregated item not found in the list"); - } - - /// Reset any request in buffer manager, this is important to avoid race condition with state sync. - /// Internal requests are managed with ongoing_tasks. - /// Incoming ordered blocks are pulled, it should only have existing blocks but no new blocks until reset finishes. - async fn reset(&mut self) { - self.buffer = Buffer::new(); - self.execution_root = None; - self.signing_root = None; - self.previous_commit_time = Instant::now(); - self.commit_proof_rb_handle.take(); - // purge the incoming blocks queue - while let Ok(Some(_)) = self.block_rx.try_next() {} - // Wait for ongoing tasks to finish before sending back ack. - while self.ongoing_tasks.load(Ordering::SeqCst) > 0 { - tokio::time::sleep(Duration::from_millis(10)).await; - } - } - - /// It pops everything in the buffer and if reconfig flag is set, it stops the main loop - async fn process_reset_request(&mut self, request: ResetRequest) { - let ResetRequest { tx, signal } = request; - info!("Receive reset"); - self.reset_flag.store(true, Ordering::SeqCst); - - self.stop = matches!(signal, ResetSignal::Stop); - self.reset().await; - let _ = tx.send(ResetAck::default()); - self.reset_flag.store(false, Ordering::SeqCst); - info!("Reset finishes"); - } - - async fn process_execution_schedule_response(&mut self, response: ExecutionWaitRequest) { - // pass through to the execution wait phase - let request = self.create_new_request(response); - self.execution_wait_phase_tx - .send(request) - .await - .expect("Failed to send execution wait request."); - } - - /// If the response is successful, advance the item to Executed, otherwise panic (TODO fix). - async fn process_execution_response(&mut self, response: ExecutionResponse) { - let ExecutionResponse { block_id, inner } = response; - // find the corresponding item, may not exist if a reset or aggregated happened - let current_cursor = self.buffer.find_elem_by_key(self.execution_root, block_id); - if current_cursor.is_none() { - return; - } - - let executed_blocks = match inner { - Ok(result) => result, - Err(ExecutorError::CouldNotGetData) => { - warn!("Execution error - CouldNotGetData"); - return; - }, - Err(e) => { - error!("Execution error {:?}", e); - return; - }, - }; - info!( - "Receive executed response {}", - executed_blocks.last().unwrap().block_info() - ); - let current_item = self.buffer.get(¤t_cursor); - - if current_item.block_id() != block_id { - error!( - block_id = block_id, - expected_block_id = current_item.block_id(), - "Received result for unexpected block id. Ignoring." - ); - return; - } - - // Handle reconfiguration timestamp reconciliation. - // end epoch timestamp is set to the first block that causes the reconfiguration. - // once it's set, any subsequent block commit info will be set to this timestamp. - if self.end_epoch_timestamp.get().is_none() { - let maybe_reconfig_timestamp = executed_blocks - .iter() - .find(|b| b.block_info().has_reconfiguration()) - .map(|b| b.timestamp_usecs()); - if let Some(timestamp) = maybe_reconfig_timestamp { - debug!("Reconfig happens, set epoch end timestamp to {}", timestamp); - self.end_epoch_timestamp - .set(timestamp) - .expect("epoch end timestamp should only be set once"); - } - } - - let item = self.buffer.take(¤t_cursor); - let new_item = item.advance_to_executed_or_aggregated( - executed_blocks, - &self.epoch_state.verifier, - self.end_epoch_timestamp.get().cloned(), - ); - let aggregated = new_item.is_aggregated(); - self.buffer.set(¤t_cursor, new_item); - if aggregated { - self.advance_head(block_id).await; - } - } - - /// If the signing response is successful, advance the item to Signed and broadcast commit votes. - async fn process_signing_response(&mut self, response: SigningResponse) { - let SigningResponse { - signature_result, - commit_ledger_info, - } = response; - let signature = match signature_result { - Ok(sig) => sig, - Err(e) => { - error!("Signing failed {:?}", e); - return; - }, - }; - info!( - "Receive signing response {}", - commit_ledger_info.commit_info() - ); - // find the corresponding item, may not exist if a reset or aggregated happened - let current_cursor = self - .buffer - .find_elem_by_key(self.signing_root, commit_ledger_info.commit_info().id()); - if current_cursor.is_some() { - let item = self.buffer.take(¤t_cursor); - // it is possible that we already signed this buffer item (double check after the final integration) - if item.is_executed() { - // we have found the buffer item - let mut signed_item = item.advance_to_signed(self.author, signature); - let signed_item_mut = signed_item.unwrap_signed_mut(); - let commit_vote = signed_item_mut.commit_vote.clone(); - let commit_vote = CommitMessage::Vote(commit_vote); - signed_item_mut - .rb_handle - .replace((Instant::now(), self.do_reliable_broadcast(commit_vote))); - self.buffer.set(¤t_cursor, signed_item); - } else { - self.buffer.set(¤t_cursor, item); - } - } - } - - /// process the commit vote messages - /// it scans the whole buffer for a matching blockinfo - /// if found, try advancing the item to be aggregated - fn process_commit_message(&mut self, commit_msg: IncomingCommitRequest) -> Option { - let IncomingCommitRequest { - req, - protocol, - response_sender, - } = commit_msg; - match req { - CommitMessage::Vote(vote) => { - // find the corresponding item - let author = vote.author(); - let commit_info = vote.commit_info().clone(); - info!("Receive commit vote {} from {}", commit_info, author); - let target_block_id = vote.commit_info().id(); - let current_cursor = self - .buffer - .find_elem_by_key(*self.buffer.head_cursor(), target_block_id); - if current_cursor.is_some() { - let mut item = self.buffer.take(¤t_cursor); - let new_item = match item.add_signature_if_matched(vote) { - Ok(()) => { - let response = - ConsensusMsg::CommitMessage(Box::new(CommitMessage::Ack(()))); - if let Ok(bytes) = protocol.to_bytes(&response) { - let _ = response_sender.send(Ok(bytes.into())); - } - item.try_advance_to_aggregated(&self.epoch_state.verifier) - }, - Err(e) => { - error!( - error = ?e, - author = author, - commit_info = commit_info, - "Failed to add commit vote", - ); - reply_nack(protocol, response_sender); - item - }, - }; - self.buffer.set(¤t_cursor, new_item); - if self.buffer.get(¤t_cursor).is_aggregated() { - return Some(target_block_id); - } else { - return None; - } - } else { - reply_nack(protocol, response_sender); // TODO: send_commit_vote() doesn't care about the response and this should be direct send not RPC - } - }, - CommitMessage::Decision(commit_proof) => { - let target_block_id = commit_proof.ledger_info().commit_info().id(); - info!( - "Receive commit decision {}", - commit_proof.ledger_info().commit_info() - ); - let cursor = self - .buffer - .find_elem_by_key(*self.buffer.head_cursor(), target_block_id); - if cursor.is_some() { - let item = self.buffer.take(&cursor); - let new_item = item.try_advance_to_aggregated_with_ledger_info( - commit_proof.ledger_info().clone(), - ); - let aggregated = new_item.is_aggregated(); - self.buffer.set(&cursor, new_item); - if aggregated { - let response = - ConsensusMsg::CommitMessage(Box::new(CommitMessage::Ack(()))); - if let Ok(bytes) = protocol.to_bytes(&response) { - let _ = response_sender.send(Ok(bytes.into())); - } - return Some(target_block_id); - } - } - reply_nack(protocol, response_sender); // TODO: send_commit_proof() doesn't care about the response and this should be direct send not RPC - }, - CommitMessage::Ack(_) => { - // It should be filtered out by verify, so we log errors here - error!("Unexpected ack message"); - }, - CommitMessage::Nack => { - error!("Unexpected NACK message"); - }, - } - None - } - - /// this function retries all the items until the signing root - /// note that there might be other signed items after the signing root - async fn rebroadcast_commit_votes_if_needed(&mut self) { - if self.previous_commit_time.elapsed() - < Duration::from_millis(COMMIT_VOTE_BROADCAST_INTERVAL_MS) - { - return; - } - let mut cursor = *self.buffer.head_cursor(); - let mut count = 0; - while cursor.is_some() { - { - let mut item = self.buffer.take(&cursor); - if !item.is_signed() { - self.buffer.set(&cursor, item); - break; - } - let signed_item = item.unwrap_signed_mut(); - let re_broadcast = match &signed_item.rb_handle { - None => true, - // Since we don't persist the votes, nodes that crashed would lose the votes even after send ack, - // We'll try to re-initiate the broadcast after 30s. - Some((start_time, _)) => { - start_time.elapsed() - >= Duration::from_millis(COMMIT_VOTE_REBROADCAST_INTERVAL_MS) - }, - }; - if re_broadcast { - let commit_vote = CommitMessage::Vote(signed_item.commit_vote.clone()); - signed_item - .rb_handle - .replace((Instant::now(), self.do_reliable_broadcast(commit_vote))); - count += 1; - } - self.buffer.set(&cursor, item); - } - cursor = self.buffer.get_next(&cursor); - } - if count > 0 { - info!("Start reliable broadcast {} commit votes", count); - } - } - - fn update_buffer_manager_metrics(&self) { - let mut cursor = *self.buffer.head_cursor(); - let mut pending_ordered = 0; - let mut pending_executed = 0; - let mut pending_signed = 0; - let mut pending_aggregated = 0; - - while cursor.is_some() { - match self.buffer.get(&cursor) { - BufferItem::Ordered(_) => { - pending_ordered += 1; - }, - BufferItem::Executed(_) => { - pending_executed += 1; - }, - BufferItem::Signed(_) => { - pending_signed += 1; - }, - BufferItem::Aggregated(_) => { - pending_aggregated += 1; - }, - } - cursor = self.buffer.get_next(&cursor); - } - - counters::NUM_BLOCKS_IN_PIPELINE - .with_label_values(&["ordered"]) - .set(pending_ordered as i64); - counters::NUM_BLOCKS_IN_PIPELINE - .with_label_values(&["executed"]) - .set(pending_executed as i64); - counters::NUM_BLOCKS_IN_PIPELINE - .with_label_values(&["signed"]) - .set(pending_signed as i64); - counters::NUM_BLOCKS_IN_PIPELINE - .with_label_values(&["aggregated"]) - .set(pending_aggregated as i64); - } - - pub async fn start(mut self) { - info!("Buffer manager starts."); - let (verified_commit_msg_tx, mut verified_commit_msg_rx) = create_channel(); - let mut interval = tokio::time::interval(Duration::from_millis(LOOP_INTERVAL_MS)); - let mut commit_msg_rx = self.commit_msg_rx.take().expect("commit msg rx must exist"); - let epoch_state = self.epoch_state.clone(); - let bounded_executor = self.bounded_executor.clone(); - spawn_named!("buffer manager verification", async move { - while let Some(commit_msg) = commit_msg_rx.next().await { - let tx = verified_commit_msg_tx.clone(); - let epoch_state_clone = epoch_state.clone(); - bounded_executor - .spawn(async move { - match commit_msg.req.verify(&epoch_state_clone.verifier) { - Ok(_) => { - let _ = tx.unbounded_send(commit_msg); - }, - Err(e) => warn!("Invalid commit message: {}", e), - } - }) - .await; - } - }); - while !self.stop { - // advancing the root will trigger sending requests to the pipeline - ::futures::select! { - blocks = self.block_rx.select_next_some() => { - monitor!("buffer_manager_process_ordered", { - self.process_ordered_blocks(blocks).await; - if self.execution_root.is_none() { - self.advance_execution_root().await; - }}); - }, - reset_event = self.reset_rx.select_next_some() => { - monitor!("buffer_manager_process_reset", - self.process_reset_request(reset_event).await); - }, - response = self.execution_schedule_phase_rx.select_next_some() => { - monitor!("buffer_manager_process_execution_schedule_response", { - self.process_execution_schedule_response(response).await; - })}, - response = self.execution_wait_phase_rx.select_next_some() => { - monitor!("buffer_manager_process_execution_wait_response", { - self.process_execution_response(response).await; - self.advance_execution_root().await; - if self.signing_root.is_none() { - self.advance_signing_root().await; - }}); - }, - response = self.signing_phase_rx.select_next_some() => { - monitor!("buffer_manager_process_signing_response", { - self.process_signing_response(response).await; - self.advance_signing_root().await - }) - }, - rpc_request = verified_commit_msg_rx.select_next_some() => { - monitor!("buffer_manager_process_commit_message", - if let Some(aggregated_block_id) = self.process_commit_message(rpc_request) { - self.advance_head(aggregated_block_id).await; - if self.execution_root.is_none() { - self.advance_execution_root().await; - } - if self.signing_root.is_none() { - self.advance_signing_root().await; - } - }); - } - _ = interval.tick().fuse() => { - monitor!("buffer_manager_process_interval_tick", { - self.update_buffer_manager_metrics(); - self.rebroadcast_commit_votes_if_needed().await - }); - }, - // no else branch here because interval.tick will always be available - } - } - info!("Buffer manager stops."); - } -} - -fn reply_nack(protocol: ProtocolId, response_sender: oneshot::Sender>) { - let response = ConsensusMsg::CommitMessage(Box::new(CommitMessage::Nack)); - if let Ok(bytes) = protocol.to_bytes(&response) { - let _ = response_sender.send(Ok(bytes.into())); - } -} diff --git a/consensus/src/pipeline/commit_reliable_broadcast.rs b/consensus/src/pipeline/commit_reliable_broadcast.rs deleted file mode 100644 index 3ee9606fc5ff1..0000000000000 --- a/consensus/src/pipeline/commit_reliable_broadcast.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{network::NetworkSender, network_interface::ConsensusMsg}; -use anyhow::bail; -use aptos_consensus_types::{ - common::Author, - pipeline::{commit_decision::CommitDecision, commit_vote::CommitVote}, -}; -use aptos_infallible::Mutex; -use aptos_reliable_broadcast::{BroadcastStatus, RBMessage, RBNetworkSender}; -use aptos_types::validator_verifier::ValidatorVerifier; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, sync::Arc, time::Duration}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -/// Network message for the pipeline phase -pub enum CommitMessage { - /// Vote on execution result - Vote(CommitVote), - /// Quorum proof on execution result - Decision(CommitDecision), - /// Ack on either vote or decision - Ack(()), - /// Nack is non-acknowledgement, we got your message, but it was bad/we were bad - Nack, -} - -impl CommitMessage { - /// Verify the signatures on the message - pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - match self { - CommitMessage::Vote(vote) => vote.verify(verifier), - CommitMessage::Decision(decision) => decision.verify(verifier), - CommitMessage::Ack(_) => bail!("Unexpected ack in incoming commit message"), - CommitMessage::Nack => bail!("Unexpected NACK in incoming commit message"), - } - } - - pub fn epoch(&self) -> Option { - match self { - CommitMessage::Vote(vote) => Some(vote.epoch()), - CommitMessage::Decision(decision) => Some(decision.epoch()), - _ => None, - } - } -} - -impl RBMessage for CommitMessage {} - -pub struct AckState { - validators: Mutex>, -} - -impl AckState { - pub fn new(validators: impl Iterator) -> Arc { - Arc::new(Self { - validators: Mutex::new(validators.collect()), - }) - } -} - -impl BroadcastStatus for Arc { - type Aggregated = (); - type Message = CommitMessage; - type Response = CommitMessage; - - fn add(&self, peer: Author, ack: Self::Response) -> anyhow::Result> { - match ack { - CommitMessage::Vote(_) => { - bail!("unexected Vote reply to broadcast"); - }, - CommitMessage::Decision(_) => { - bail!("unexected Decision reply to broadcast"); - }, - CommitMessage::Ack(_) => { - // okay! continue - }, - CommitMessage::Nack => { - bail!("unexected Nack reply to broadcast"); - }, - } - let mut validators = self.validators.lock(); - if validators.remove(&peer) { - if validators.is_empty() { - Ok(Some(())) - } else { - Ok(None) - } - } else { - bail!("Unknown author: {}", peer); - } - } -} - -#[async_trait] -impl RBNetworkSender for NetworkSender { - async fn send_rb_rpc( - &self, - receiver: Author, - message: CommitMessage, - timeout_duration: Duration, - ) -> anyhow::Result { - let msg = ConsensusMsg::CommitMessage(Box::new(message)); - let response = match self.send_rpc(receiver, msg, timeout_duration).await? { - ConsensusMsg::CommitMessage(resp) if matches!(*resp, CommitMessage::Ack(_)) => *resp, - _ => bail!("Invalid response to request"), - }; - - Ok(response) - } -} diff --git a/consensus/src/pipeline/decoupled_execution_utils.rs b/consensus/src/pipeline/decoupled_execution_utils.rs deleted file mode 100644 index ee873375ed45f..0000000000000 --- a/consensus/src/pipeline/decoupled_execution_utils.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::{IncomingCommitRequest, NetworkSender}, - pipeline::{ - buffer_manager::{create_channel, BufferManager, OrderedBlocks, ResetRequest}, - execution_schedule_phase::{ExecutionRequest, ExecutionSchedulePhase}, - execution_wait_phase::{ExecutionResponse, ExecutionWaitPhase, ExecutionWaitRequest}, - persisting_phase::{PersistingPhase, PersistingRequest}, - pipeline_phase::{CountedRequest, PipelinePhase}, - signing_phase::{CommitSignerProvider, SigningPhase, SigningRequest, SigningResponse}, - }, - state_replication::StateComputer, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::aptos_channel::Receiver; -use aptos_consensus_types::common::Author; -use aptos_types::{account_address::AccountAddress, epoch_state::EpochState}; -use futures::channel::mpsc::UnboundedReceiver; -use std::sync::{ - atomic::{AtomicBool, AtomicU64}, - Arc, -}; - -/// build channels and return phases and buffer manager -pub fn prepare_phases_and_buffer_manager( - author: Author, - execution_proxy: Arc, - safety_rules: Arc, - commit_msg_tx: NetworkSender, - commit_msg_rx: Receiver, - persisting_proxy: Arc, - block_rx: UnboundedReceiver, - sync_rx: UnboundedReceiver, - epoch_state: Arc, - bounded_executor: BoundedExecutor, -) -> ( - PipelinePhase, - PipelinePhase, - PipelinePhase, - PipelinePhase, - BufferManager, -) { - let reset_flag = Arc::new(AtomicBool::new(false)); - let ongoing_tasks = Arc::new(AtomicU64::new(0)); - - // Execution Phase - let (execution_schedule_phase_request_tx, execution_schedule_phase_request_rx) = - create_channel::>(); - let (execution_schedule_phase_response_tx, execution_schedule_phase_response_rx) = - create_channel::(); - let execution_schedule_phase_processor = ExecutionSchedulePhase::new(execution_proxy); - let execution_schedule_phase = PipelinePhase::new( - execution_schedule_phase_request_rx, - Some(execution_schedule_phase_response_tx), - Box::new(execution_schedule_phase_processor), - reset_flag.clone(), - ); - - let (execution_wait_phase_request_tx, execution_wait_phase_request_rx) = - create_channel::>(); - let (execution_wait_phase_response_tx, execution_wait_phase_response_rx) = - create_channel::(); - let execution_wait_phase_processor = ExecutionWaitPhase; - let execution_wait_phase = PipelinePhase::new( - execution_wait_phase_request_rx, - Some(execution_wait_phase_response_tx), - Box::new(execution_wait_phase_processor), - reset_flag.clone(), - ); - - // Signing Phase - let (signing_phase_request_tx, signing_phase_request_rx) = - create_channel::>(); - let (signing_phase_response_tx, signing_phase_response_rx) = - create_channel::(); - - let signing_phase_processor = SigningPhase::new(safety_rules); - let signing_phase = PipelinePhase::new( - signing_phase_request_rx, - Some(signing_phase_response_tx), - Box::new(signing_phase_processor), - reset_flag.clone(), - ); - - // Persisting Phase - let (persisting_phase_request_tx, persisting_phase_request_rx) = - create_channel::>(); - - let persisting_phase_processor = PersistingPhase::new(persisting_proxy); - let persisting_phase = PipelinePhase::new( - persisting_phase_request_rx, - None, - Box::new(persisting_phase_processor), - reset_flag.clone(), - ); - - ( - execution_schedule_phase, - execution_wait_phase, - signing_phase, - persisting_phase, - BufferManager::new( - author, - execution_schedule_phase_request_tx, - execution_schedule_phase_response_rx, - execution_wait_phase_request_tx, - execution_wait_phase_response_rx, - signing_phase_request_tx, - signing_phase_response_rx, - Arc::new(commit_msg_tx), - commit_msg_rx, - persisting_phase_request_tx, - block_rx, - sync_rx, - epoch_state, - ongoing_tasks, - reset_flag.clone(), - bounded_executor, - ), - ) -} diff --git a/consensus/src/pipeline/errors.rs b/consensus/src/pipeline/errors.rs deleted file mode 100644 index 746228438eceb..0000000000000 --- a/consensus/src/pipeline/errors.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_types::block_info::BlockInfo; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -#[derive(Clone, Debug, Deserialize, Error, PartialEq, Eq, Serialize)] -/// Different reasons of errors in commit phase -pub enum Error { - #[error("The block in the message, {0}, does not match expected block, {1}")] - InconsistentBlockInfo(BlockInfo, BlockInfo), - #[error("Verification Error")] - VerificationError, - #[error("Reset host dropped")] - ResetDropped, - #[error("Rand Reset host dropped")] - RandResetDropped, -} diff --git a/consensus/src/pipeline/execution_client.rs b/consensus/src/pipeline/execution_client.rs deleted file mode 100644 index b05bc0ffbdf1a..0000000000000 --- a/consensus/src/pipeline/execution_client.rs +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - error::StateSyncError, - network::{IncomingCommitRequest, IncomingRandGenRequest, NetworkSender}, - network_interface::{ConsensusMsg, ConsensusNetworkClient}, - payload_manager::PayloadManager, - pipeline::{ - buffer_manager::{OrderedBlocks, ResetAck, ResetRequest, ResetSignal}, - decoupled_execution_utils::prepare_phases_and_buffer_manager, - errors::Error, - signing_phase::CommitSignerProvider, - }, - rand::rand_gen::{ - rand_manager::RandManager, - storage::interface::RandStorage, - types::{AugmentedData, RandConfig, Share}, - }, - state_computer::ExecutionProxy, - state_replication::{StateComputer, StateComputerCommitCallBackType}, - transaction_deduper::create_transaction_deduper, - transaction_shuffler::create_transaction_shuffler, -}; -use anyhow::Result; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::{aptos_channel, message_queues::QueueStyle}; -use aptos_config::config::ConsensusConfig; -use aptos_consensus_types::{common::Author, pipelined_block::PipelinedBlock}; -use aptos_executor_types::ExecutorResult; -use aptos_infallible::RwLock; -use aptos_logger::prelude::*; -use aptos_network::{application::interface::NetworkClient, protocols::network::Event}; -use aptos_safety_rules::safety_rules_manager::load_consensus_key_from_secure_storage; -use aptos_types::{ - epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, - on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig, OnChainRandomnessConfig}, - validator_signer::ValidatorSigner, -}; -use fail::fail_point; -use futures::{ - channel::{mpsc::UnboundedSender, oneshot}, - SinkExt, -}; -use futures_channel::mpsc::unbounded; -use move_core_types::account_address::AccountAddress; -use std::sync::Arc; - -#[async_trait::async_trait] -pub trait TExecutionClient: Send + Sync { - /// Initialize the execution phase for a new epoch. - async fn start_epoch( - &self, - epoch_state: Arc, - commit_signer_provider: Arc, - payload_manager: Arc, - onchain_consensus_config: &OnChainConsensusConfig, - onchain_execution_config: &OnChainExecutionConfig, - onchain_randomness_config: &OnChainRandomnessConfig, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ); - - /// This is needed for some DAG tests. Clean this up as a TODO. - fn get_execution_channel(&self) -> Option>; - - /// Send ordered blocks to the real execution phase through the channel. - async fn finalize_order( - &self, - blocks: &[Arc], - ordered_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> ExecutorResult<()>; - - fn send_commit_msg( - &self, - peer_id: AccountAddress, - commit_msg: IncomingCommitRequest, - ) -> Result<()>; - - /// Synchronize to a commit that not present locally. - async fn sync_to(&self, target: LedgerInfoWithSignatures) -> Result<(), StateSyncError>; - - /// Shutdown the current processor at the end of the epoch. - async fn end_epoch(&self); -} - -struct BufferManagerHandle { - pub execute_tx: Option>, - pub commit_tx: Option>, - pub reset_tx_to_buffer_manager: Option>, - pub reset_tx_to_rand_manager: Option>, -} - -impl BufferManagerHandle { - pub fn new() -> Self { - Self { - execute_tx: None, - commit_tx: None, - reset_tx_to_buffer_manager: None, - reset_tx_to_rand_manager: None, - } - } - - pub fn init( - &mut self, - execute_tx: UnboundedSender, - commit_tx: aptos_channel::Sender, - reset_tx_to_buffer_manager: UnboundedSender, - reset_tx_to_rand_manager: Option>, - ) { - self.execute_tx = Some(execute_tx); - self.commit_tx = Some(commit_tx); - self.reset_tx_to_buffer_manager = Some(reset_tx_to_buffer_manager); - self.reset_tx_to_rand_manager = reset_tx_to_rand_manager; - } - - pub fn reset( - &mut self, - ) -> ( - Option>, - Option>, - ) { - let reset_tx_to_rand_manager = self.reset_tx_to_rand_manager.take(); - let reset_tx_to_buffer_manager = self.reset_tx_to_buffer_manager.take(); - self.execute_tx = None; - self.commit_tx = None; - (reset_tx_to_rand_manager, reset_tx_to_buffer_manager) - } -} - -pub struct ExecutionProxyClient { - consensus_config: ConsensusConfig, - execution_proxy: Arc, - author: Author, - self_sender: aptos_channels::UnboundedSender>, - network_sender: ConsensusNetworkClient>, - bounded_executor: BoundedExecutor, - // channels to buffer manager - handle: Arc>, - rand_storage: Arc>, -} - -impl ExecutionProxyClient { - pub fn new( - consensus_config: ConsensusConfig, - execution_proxy: Arc, - author: Author, - self_sender: aptos_channels::UnboundedSender>, - network_sender: ConsensusNetworkClient>, - bounded_executor: BoundedExecutor, - rand_storage: Arc>, - ) -> Self { - Self { - consensus_config, - execution_proxy, - author, - self_sender, - network_sender, - bounded_executor, - handle: Arc::new(RwLock::new(BufferManagerHandle::new())), - rand_storage, - } - } - - fn spawn_decoupled_execution( - &self, - commit_signer_provider: Arc, - epoch_state: Arc, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ) { - let network_sender = NetworkSender::new( - self.author, - self.network_sender.clone(), - self.self_sender.clone(), - epoch_state.verifier.clone(), - ); - - let (reset_buffer_manager_tx, reset_buffer_manager_rx) = unbounded::(); - - let (commit_msg_tx, commit_msg_rx) = - aptos_channel::new::( - QueueStyle::FIFO, - 100, - Some(&counters::BUFFER_MANAGER_MSGS), - ); - - let (execution_ready_block_tx, execution_ready_block_rx, maybe_reset_tx_to_rand_manager) = - if let Some(rand_config) = rand_config { - let (ordered_block_tx, ordered_block_rx) = unbounded::(); - let (rand_ready_block_tx, rand_ready_block_rx) = unbounded::(); - - let (reset_tx_to_rand_manager, reset_rand_manager_rx) = unbounded::(); - let consensus_key = - load_consensus_key_from_secure_storage(&self.consensus_config.safety_rules) - .expect("Failed in loading consensus key for ExecutionProxyClient."); - let signer = Arc::new(ValidatorSigner::new(self.author, consensus_key)); - - let rand_manager = RandManager::::new( - self.author, - epoch_state.clone(), - signer, - rand_config, - rand_ready_block_tx, - Arc::new(network_sender.clone()), - self.rand_storage.clone(), - self.bounded_executor.clone(), - ); - - tokio::spawn(rand_manager.start( - ordered_block_rx, - rand_msg_rx, - reset_rand_manager_rx, - self.bounded_executor.clone(), - )); - - ( - ordered_block_tx, - rand_ready_block_rx, - Some(reset_tx_to_rand_manager), - ) - } else { - let (ordered_block_tx, ordered_block_rx) = unbounded(); - (ordered_block_tx, ordered_block_rx, None) - }; - - self.handle.write().init( - execution_ready_block_tx, - commit_msg_tx, - reset_buffer_manager_tx, - maybe_reset_tx_to_rand_manager, - ); - - let ( - execution_schedule_phase, - execution_wait_phase, - signing_phase, - persisting_phase, - buffer_manager, - ) = prepare_phases_and_buffer_manager( - self.author, - self.execution_proxy.clone(), - commit_signer_provider, - network_sender, - commit_msg_rx, - self.execution_proxy.clone(), - execution_ready_block_rx, - reset_buffer_manager_rx, - epoch_state, - self.bounded_executor.clone(), - ); - - tokio::spawn(execution_schedule_phase.start()); - tokio::spawn(execution_wait_phase.start()); - tokio::spawn(signing_phase.start()); - tokio::spawn(persisting_phase.start()); - tokio::spawn(buffer_manager.start()); - } -} - -#[async_trait::async_trait] -impl TExecutionClient for ExecutionProxyClient { - async fn start_epoch( - &self, - epoch_state: Arc, - commit_signer_provider: Arc, - payload_manager: Arc, - onchain_consensus_config: &OnChainConsensusConfig, - onchain_execution_config: &OnChainExecutionConfig, - onchain_randomness_config: &OnChainRandomnessConfig, - rand_config: Option, - rand_msg_rx: aptos_channel::Receiver, - ) { - let maybe_rand_msg_tx = self.spawn_decoupled_execution( - commit_signer_provider, - epoch_state.clone(), - rand_config, - rand_msg_rx, - ); - - let transaction_shuffler = - create_transaction_shuffler(onchain_execution_config.transaction_shuffler_type()); - let block_executor_onchain_config = - onchain_execution_config.block_executor_onchain_config(); - let transaction_deduper = - create_transaction_deduper(onchain_execution_config.transaction_deduper_type()); - let randomness_enabled = onchain_consensus_config.is_vtxn_enabled() - && onchain_randomness_config.randomness_enabled(); - self.execution_proxy.new_epoch( - &epoch_state, - payload_manager, - transaction_shuffler, - block_executor_onchain_config, - transaction_deduper, - randomness_enabled, - ); - - maybe_rand_msg_tx - } - - fn get_execution_channel(&self) -> Option> { - self.handle.read().execute_tx.clone() - } - - async fn finalize_order( - &self, - blocks: &[Arc], - ordered_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - assert!(!blocks.is_empty()); - let execute_tx = self.handle.read().execute_tx.clone(); - - if execute_tx.is_none() { - debug!("Failed to send to buffer manager, maybe epoch ends"); - return Ok(()); - } - - for block in blocks { - block.set_insertion_time(); - } - - if execute_tx - .unwrap() - .send(OrderedBlocks { - ordered_blocks: blocks - .iter() - .map(|b| (**b).clone()) - .collect::>(), - ordered_proof, - callback, - }) - .await - .is_err() - { - debug!("Failed to send to buffer manager, maybe epoch ends"); - } - Ok(()) - } - - fn send_commit_msg( - &self, - peer_id: AccountAddress, - commit_msg: IncomingCommitRequest, - ) -> Result<()> { - if let Some(tx) = &self.handle.read().commit_tx { - tx.push(peer_id, commit_msg) - } else { - counters::EPOCH_MANAGER_ISSUES_DETAILS - .with_label_values(&["buffer_manager_not_started"]) - .inc(); - warn!("Buffer manager not started"); - Ok(()) - } - } - - async fn sync_to(&self, target: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - fail_point!("consensus::sync_to", |_| { - Err(anyhow::anyhow!("Injected error in sync_to").into()) - }); - - let (reset_tx_to_rand_manager, reset_tx_to_buffer_manager) = { - let handle = self.handle.read(); - ( - handle.reset_tx_to_rand_manager.clone(), - handle.reset_tx_to_buffer_manager.clone(), - ) - }; - - if let Some(mut reset_tx) = reset_tx_to_rand_manager { - let (ack_tx, ack_rx) = oneshot::channel::(); - reset_tx - .send(ResetRequest { - tx: ack_tx, - signal: ResetSignal::TargetRound(target.commit_info().round()), - }) - .await - .map_err(|_| Error::RandResetDropped)?; - ack_rx.await.map_err(|_| Error::RandResetDropped)?; - } - - if let Some(mut reset_tx) = reset_tx_to_buffer_manager { - // reset execution phase and commit phase - let (tx, rx) = oneshot::channel::(); - reset_tx - .send(ResetRequest { - tx, - signal: ResetSignal::TargetRound(target.commit_info().round()), - }) - .await - .map_err(|_| Error::ResetDropped)?; - rx.await.map_err(|_| Error::ResetDropped)?; - } - - // TODO: handle the sync error, should re-push the ordered blocks to buffer manager - // when it's reset but sync fails. - self.execution_proxy.sync_to(target).await?; - Ok(()) - } - - async fn end_epoch(&self) { - let (reset_tx_to_rand_manager, reset_tx_to_buffer_manager) = { - let mut handle = self.handle.write(); - handle.reset() - }; - - if let Some(mut tx) = reset_tx_to_rand_manager { - let (ack_tx, ack_rx) = oneshot::channel(); - tx.send(ResetRequest { - tx: ack_tx, - signal: ResetSignal::Stop, - }) - .await - .expect("[EpochManager] Fail to drop rand manager"); - ack_rx - .await - .expect("[EpochManager] Fail to drop rand manager"); - } - - if let Some(mut tx) = reset_tx_to_buffer_manager { - let (ack_tx, ack_rx) = oneshot::channel(); - tx.send(ResetRequest { - tx: ack_tx, - signal: ResetSignal::Stop, - }) - .await - .expect("[EpochManager] Fail to drop buffer manager"); - ack_rx - .await - .expect("[EpochManager] Fail to drop buffer manager"); - } - self.execution_proxy.end_epoch(); - } -} - -pub struct DummyExecutionClient; - -#[async_trait::async_trait] -impl TExecutionClient for DummyExecutionClient { - async fn start_epoch( - &self, - _epoch_state: Arc, - _commit_signer_provider: Arc, - _payload_manager: Arc, - _onchain_consensus_config: &OnChainConsensusConfig, - _onchain_execution_config: &OnChainExecutionConfig, - _onchain_randomness_config: &OnChainRandomnessConfig, - _rand_config: Option, - _rand_msg_rx: aptos_channel::Receiver, - ) { - } - - fn get_execution_channel(&self) -> Option> { - None - } - - async fn finalize_order( - &self, - _: &[Arc], - _: LedgerInfoWithSignatures, - _: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - Ok(()) - } - - fn send_commit_msg(&self, _: AccountAddress, _: IncomingCommitRequest) -> Result<()> { - Ok(()) - } - - async fn sync_to(&self, _: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - Ok(()) - } - - async fn end_epoch(&self) {} -} diff --git a/consensus/src/pipeline/execution_phase.rs b/consensus/src/pipeline/execution_phase.rs deleted file mode 100644 index 3f52473d51af8..0000000000000 --- a/consensus/src/pipeline/execution_phase.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{experimental::pipeline_phase::StatelessPipeline, state_replication::StateComputer}; -use aptos_consensus_types::executed_block::ExecutedBlock; -use aptos_crypto::HashValue; -use aptos_executor_types::{ExecutorError, ExecutorResult}; -use async_trait::async_trait; -use std::{ - fmt::{Debug, Display, Formatter}, - sync::Arc, -}; - -/// [ This class is used when consensus.decoupled = true ] -/// ExecutionPhase is a singleton that receives ordered blocks from -/// the buffer manager and execute them. After the execution is done, -/// ExecutionPhase sends the ordered blocks back to the buffer manager. -/// - -pub struct ExecutionRequest { - pub ordered_blocks: Vec, -} - -impl Debug for ExecutionRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for ExecutionRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "ExecutionRequest({:?})", self.ordered_blocks) - } -} - -pub struct ExecutionResponse { - pub block_id: HashValue, - pub inner: ExecutorResult>, -} - -pub struct ExecutionPhase { - execution_proxy: Arc, -} - -impl ExecutionPhase { - pub fn new(execution_proxy: Arc) -> Self { - Self { execution_proxy } - } -} - -#[async_trait] -impl StatelessPipeline for ExecutionPhase { - type Request = ExecutionRequest; - type Response = ExecutionResponse; - - const NAME: &'static str = "execution"; - - async fn process(&self, req: ExecutionRequest) -> ExecutionResponse { - let ExecutionRequest { ordered_blocks } = req; - - if ordered_blocks.is_empty() { - // return err when the blocks are empty - return ExecutionResponse { - block_id: HashValue::zero(), - inner: Err(ExecutorError::EmptyBlocks), - }; - } - - let block_id = ordered_blocks.last().unwrap().id(); - let mut result = vec![]; - - for b in ordered_blocks { - match self.execution_proxy.compute(b.block(), b.parent_id()).await { - Ok(compute_result) => { - result.push(ExecutedBlock::new(b.block().clone(), compute_result)); - }, - Err(e) => { - return ExecutionResponse { - block_id, - inner: Err(e), - } - }, - } - } - - ExecutionResponse { - block_id, - inner: Ok(result), - } - } -} diff --git a/consensus/src/pipeline/execution_schedule_phase.rs b/consensus/src/pipeline/execution_schedule_phase.rs deleted file mode 100644 index 96586273649c1..0000000000000 --- a/consensus/src/pipeline/execution_schedule_phase.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::{ - execution_wait_phase::ExecutionWaitRequest, - pipeline_phase::{CountedRequest, StatelessPipeline}, - }, - state_computer::PipelineExecutionResult, - state_replication::StateComputer, -}; -use aptos_consensus_types::pipelined_block::PipelinedBlock; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorError; -use aptos_logger::debug; -use async_trait::async_trait; -use futures::TryFutureExt; -use std::{ - fmt::{Debug, Display, Formatter}, - sync::Arc, -}; - -/// [ This class is used when consensus.decoupled = true ] -/// ExecutionSchedulePhase is a singleton that receives ordered blocks from -/// the buffer manager and send them to the ExecutionPipeline. - -pub struct ExecutionRequest { - pub ordered_blocks: Vec, - // Hold a CountedRequest to guarantee the executor doesn't get reset with pending tasks - // stuck in the ExecutinoPipeline. - pub lifetime_guard: CountedRequest<()>, -} - -impl Debug for ExecutionRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for ExecutionRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "ExecutionScheduleRequest({:?})", self.ordered_blocks) - } -} - -pub struct ExecutionSchedulePhase { - execution_proxy: Arc, -} - -impl ExecutionSchedulePhase { - pub fn new(execution_proxy: Arc) -> Self { - Self { execution_proxy } - } -} - -#[async_trait] -impl StatelessPipeline for ExecutionSchedulePhase { - type Request = ExecutionRequest; - type Response = ExecutionWaitRequest; - - const NAME: &'static str = "execution_schedule"; - - async fn process(&self, req: ExecutionRequest) -> ExecutionWaitRequest { - let ExecutionRequest { - ordered_blocks, - lifetime_guard, - } = req; - - if ordered_blocks.is_empty() { - return ExecutionWaitRequest { - block_id: HashValue::zero(), - fut: Box::pin(async { Err(aptos_executor_types::ExecutorError::EmptyBlocks) }), - }; - } - - let block_id = ordered_blocks.last().unwrap().id(); - - // Call schedule_compute() for each block here (not in the fut being returned) to - // make sure they are scheduled in order. - let mut futs = vec![]; - for b in &ordered_blocks { - let fut = self - .execution_proxy - .schedule_compute(b.block(), b.parent_id(), b.randomness().cloned()) - .await; - futs.push(fut) - } - - // In the future being returned, wait for the compute results in order. - // n.b. Must `spawn()` here to make sure lifetime_guard will be released even if - // ExecutionWait phase is never kicked off. - let fut = tokio::task::spawn(async move { - let mut results = vec![]; - for (block, fut) in itertools::zip_eq(ordered_blocks, futs) { - debug!("try to receive compute result for block {}", block.id()); - let PipelineExecutionResult { input_txns, result } = fut.await?; - results.push(block.set_execution_result(input_txns, result)); - } - drop(lifetime_guard); - Ok(results) - }) - .map_err(ExecutorError::internal_err) - .and_then(|res| async { res }); - - ExecutionWaitRequest { - block_id, - fut: Box::pin(fut), - } - } -} diff --git a/consensus/src/pipeline/execution_wait_phase.rs b/consensus/src/pipeline/execution_wait_phase.rs deleted file mode 100644 index 88b616ca7d142..0000000000000 --- a/consensus/src/pipeline/execution_wait_phase.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::pipeline::{buffer_item::ExecutionFut, pipeline_phase::StatelessPipeline}; -use aptos_consensus_types::pipelined_block::PipelinedBlock; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorResult; -use async_trait::async_trait; -use std::fmt::{Debug, Display, Formatter}; - -/// [ This class is used when consensus.decoupled = true ] -/// ExecutionWaitPhase is a singleton that receives scheduled execution futures -/// from ExecutionSchedulePhase and waits for the results from the ExecutionPipeline. - -pub struct ExecutionWaitRequest { - pub block_id: HashValue, - pub fut: ExecutionFut, -} - -impl Debug for ExecutionWaitRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for ExecutionWaitRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "ExecutionRequest({:?})", self.block_id) - } -} - -pub struct ExecutionResponse { - pub block_id: HashValue, - pub inner: ExecutorResult>, -} - -pub struct ExecutionWaitPhase; - -#[async_trait] -impl StatelessPipeline for ExecutionWaitPhase { - type Request = ExecutionWaitRequest; - type Response = ExecutionResponse; - - const NAME: &'static str = "execution"; - - async fn process(&self, req: ExecutionWaitRequest) -> ExecutionResponse { - let ExecutionWaitRequest { block_id, fut } = req; - - ExecutionResponse { - block_id, - inner: fut.await, - } - } -} diff --git a/consensus/src/pipeline/hashable.rs b/consensus/src/pipeline/hashable.rs deleted file mode 100644 index 800ee9063c466..0000000000000 --- a/consensus/src/pipeline/hashable.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::HashValue; - -pub trait Hashable { - fn hash(&self) -> HashValue; -} diff --git a/consensus/src/pipeline/linkedlist.rs b/consensus/src/pipeline/linkedlist.rs deleted file mode 100644 index 3353bbab7d33a..0000000000000 --- a/consensus/src/pipeline/linkedlist.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// modified from https://rust-unofficial.github.io/too-many-lists/fourth-final.html (MIT License) - -// maybe later we can move this to /common -use aptos_infallible::{Mutex, MutexGuard}; -use std::{ - cell::{Ref, RefCell, RefMut}, - rc::Rc, -}; - -pub struct List { - pub head: Link, - pub tail: Link, -} - -pub type Link = Option>>>; - -pub struct Node { - pub elem: Option, - pub next: Link, - pub prev: Link, -} - -impl Node { - pub fn new(elem: T) -> Rc> { - Rc::new(RefCell::new(Node { - elem: Some(elem), - prev: None, - next: None, - })) - } - - pub fn next(&self) -> Link { - self.next.as_ref().cloned() - } - - pub fn prev(&self) -> Link { - self.prev.as_ref().cloned() - } - - pub fn elem(&self) -> &T { - self.elem.as_ref().unwrap() - } - - pub fn elem_mut(&mut self) -> &mut T { - self.elem.as_mut().unwrap() - } -} - -impl List { - pub fn new() -> Self { - List { - head: None, - tail: None, - } - } - - pub fn push_front(&mut self, elem: T) { - let new_head = Node::new(elem); - match self.head.take() { - Some(old_head) => { - (*old_head).borrow_mut().prev = Some(new_head.clone()); - (*new_head).borrow_mut().next = Some(old_head); - self.head = Some(new_head); - } - None => { - self.tail = Some(new_head.clone()); - self.head = Some(new_head); - } - } - } - - pub fn push_back(&mut self, elem: T) { - let new_tail = Node::new(elem); - match self.tail.take() { - Some(old_tail) => { - (*old_tail).borrow_mut().next = Some(new_tail.clone()); - (*new_tail).borrow_mut().prev = Some(old_tail); - self.tail = Some(new_tail); - } - None => { - self.head = Some(new_tail.clone()); - self.tail = Some(new_tail); - } - } - } - - pub fn pop_back(&mut self) -> Option { - self.tail.take().map(|old_tail| { - match (*old_tail).borrow_mut().prev.take() { - Some(new_tail) => { - (*new_tail).borrow_mut().next.take(); - self.tail = Some(new_tail); - } - None => { - self.head.take(); - } - } - Rc::try_unwrap(old_tail) - .ok() - .unwrap() - .into_inner() - .elem - .unwrap() - }) - } - - pub fn pop_front(&mut self) -> Option { - self.head.take().map(|old_head| { - match (*old_head).borrow_mut().next.take() { - Some(new_head) => { - (*new_head).borrow_mut().prev.take(); - self.head = Some(new_head); - } - None => { - self.tail.take(); - } - } - Rc::try_unwrap(old_head) - .ok() - .unwrap() - .into_inner() - .elem - .unwrap() - }) - } - - pub fn peek_front(&self) -> Option> { - self.head - .as_ref() - .map(|node| Ref::map(node.borrow(), |node| node.elem.as_ref().unwrap())) - } - - pub fn peek_back(&self) -> Option> { - self.tail - .as_ref() - .map(|node| Ref::map(node.borrow(), |node| node.elem.as_ref().unwrap())) - } - - pub fn peek_back_mut(&mut self) -> Option> { - self.tail - .as_ref() - .map(|node| RefMut::map((**node).borrow_mut(), |node| node.elem.as_mut().unwrap())) - } - - pub fn peek_front_mut(&mut self) -> Option> { - self.head - .as_ref() - .map(|node| RefMut::map((**node).borrow_mut(), |node| node.elem.as_mut().unwrap())) - } - - pub fn into_iter(self) -> IntoIter { - IntoIter(self) - } -} - -impl Drop for List { - fn drop(&mut self) { - while self.pop_front().is_some() {} - } -} - -pub struct IntoIter(List); - -impl Iterator for IntoIter { - type Item = T; - - fn next(&mut self) -> Option { - self.0.pop_front() - } -} - -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { - self.0.pop_back() - } -} - -// utils - assuming link is not None - -pub fn get_next(link: &Link) -> Link { - (**link.as_ref().unwrap()).borrow().next() -} - -// TODO: maybe we need to make the following to macros to better enforce isolation -// e.g. (**link.as_ref().unwrap()).borrow().elem() - -pub fn get_elem(link: &Link) -> Ref { - Ref::map((**link.as_ref().unwrap()).borrow(), |borrow| borrow.elem()) -} - -pub fn take_elem(link: &Link) -> T { - let mut node = (**link.as_ref().unwrap()).borrow_mut(); - node.elem.take().unwrap() -} - -// same for this function -pub fn get_elem_mut(link: &Link) -> RefMut { - RefMut::map((**link.as_ref().unwrap()).borrow_mut(), |borrow_mut| { - borrow_mut.elem_mut() - }) -} - -pub fn set_elem(link: &Link, new_val: T) { - let mut node = (**link.as_ref().unwrap()).borrow_mut(); - node.elem.replace(new_val); -} - -pub fn find_elem bool, T>(link: Link, compare: F) -> Link { - let mut current = link; - while current.is_some() { - if compare(&get_elem(¤t)) { - return current; - } - current = get_next(¤t); - } - None -} - -pub fn link_eq(link_a: &Link, link_b: &Link) -> bool { - link_a.is_some() - && link_b.is_some() - && Rc::ptr_eq(link_a.as_ref().unwrap(), link_b.as_ref().unwrap()) -} - -// tests - -#[cfg(test)] -mod test { - use super::List; - - #[test] - fn basics() { - let mut list = List::new(); - - // Check empty list behaves right - assert_eq!(list.pop_front(), None); - - // Populate list - list.push_front(1); - list.push_front(2); - list.push_front(3); - - // Check normal removal - assert_eq!(list.pop_front(), Some(3)); - assert_eq!(list.pop_front(), Some(2)); - - // Push some more just to make sure nothing's corrupted - list.push_front(4); - list.push_front(5); - - // Check normal removal - assert_eq!(list.pop_front(), Some(5)); - assert_eq!(list.pop_front(), Some(4)); - - // Check exhaustion - assert_eq!(list.pop_front(), Some(1)); - assert_eq!(list.pop_front(), None); - - // ---- back ----- - - // Check empty list behaves right - assert_eq!(list.pop_back(), None); - - // Populate list - list.push_back(1); - list.push_back(2); - list.push_back(3); - - // Check normal removal - assert_eq!(list.pop_back(), Some(3)); - assert_eq!(list.pop_back(), Some(2)); - - // Push some more just to make sure nothing's corrupted - list.push_back(4); - list.push_back(5); - - // Check normal removal - assert_eq!(list.pop_back(), Some(5)); - assert_eq!(list.pop_back(), Some(4)); - - // Check exhaustion - assert_eq!(list.pop_back(), Some(1)); - assert_eq!(list.pop_back(), None); - } - - #[test] - fn peek() { - let mut list = List::new(); - assert!(list.peek_front().is_none()); - assert!(list.peek_back().is_none()); - assert!(list.peek_front_mut().is_none()); - assert!(list.peek_back_mut().is_none()); - - list.push_front(1); - list.push_front(2); - list.push_front(3); - - assert_eq!(&*list.peek_front().unwrap(), &3); - assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); - assert_eq!(&*list.peek_back().unwrap(), &1); - assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); - } - - #[test] - fn into_iter() { - let mut list = List::new(); - list.push_front(1); - list.push_front(2); - list.push_front(3); - - let mut iter = list.into_iter(); - assert_eq!(iter.next(), Some(3)); - assert_eq!(iter.next_back(), Some(1)); - assert_eq!(iter.next(), Some(2)); - assert_eq!(iter.next_back(), None); - assert_eq!(iter.next(), None); - } -} diff --git a/consensus/src/pipeline/mod.rs b/consensus/src/pipeline/mod.rs deleted file mode 100644 index 11269562e9cfa..0000000000000 --- a/consensus/src/pipeline/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -/* - * ┌──────────────────┐ - * │ 2. Signing Phase │ - * └──────────────▲─┬─┘ - * │ │ - * ┌────────────────────┐ │ │ ┌─────────────────────┐ - * │ 1. Execution Phase │ │ │ │ 4. Persisting Phase │ - * └─────────────────▲─┬┘ │ │ └┬─▲──────────────────┘ - * │ │ │ │ │ │ - * 0. Ordered ┌─┴─▼──┴─▼──▼─┴────┐ 3. Commit Vote ┌─────────┐ - * Blocks │ ├─────────────────► │ - * ┌─────────► Buffer Manager │ │ Network │ - * │ │ ◄─────────────────┤ │ - * ┌────┴─────┐ └─────────▲────────┘ Commit Vote └─────────┘ - * │ Ordering │ │ - * │ State │ Sync Req │ - * │ Computer ├─────────────┘ - * └──────────┘ - */ - -pub mod buffer; -pub mod buffer_item; -pub mod buffer_manager; -pub mod commit_reliable_broadcast; -pub mod decoupled_execution_utils; -pub mod errors; -pub mod execution_schedule_phase; -pub mod execution_wait_phase; -pub mod hashable; -pub mod persisting_phase; -pub mod pipeline_phase; -pub mod signing_phase; - -pub mod execution_client; -#[cfg(test)] -mod tests; diff --git a/consensus/src/pipeline/persisting_phase.rs b/consensus/src/pipeline/persisting_phase.rs deleted file mode 100644 index 8a60e4589c131..0000000000000 --- a/consensus/src/pipeline/persisting_phase.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::pipeline_phase::StatelessPipeline, - state_replication::{StateComputer, StateComputerCommitCallBackType}, -}; -use aptos_consensus_types::pipelined_block::PipelinedBlock; -use aptos_executor_types::ExecutorResult; -use aptos_types::ledger_info::LedgerInfoWithSignatures; -use async_trait::async_trait; -use std::{ - fmt::{Debug, Display, Formatter}, - sync::Arc, -}; - -/// [ This class is used when consensus.decoupled = true ] -/// PersistingPhase is a singleton that receives aggregated blocks from -/// the buffer manager and persists them. Upon success, it returns -/// a response. - -pub struct PersistingRequest { - pub blocks: Vec>, - pub commit_ledger_info: LedgerInfoWithSignatures, - pub callback: StateComputerCommitCallBackType, -} - -impl Debug for PersistingRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for PersistingRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!( - f, - "PersistingRequest({:?}, {})", - self.blocks, self.commit_ledger_info, - ) - } -} - -pub type PersistingResponse = ExecutorResult<()>; - -pub struct PersistingPhase { - persisting_handle: Arc, -} - -impl PersistingPhase { - pub fn new(persisting_handle: Arc) -> Self { - Self { persisting_handle } - } -} - -#[async_trait] -impl StatelessPipeline for PersistingPhase { - type Request = PersistingRequest; - type Response = PersistingResponse; - - const NAME: &'static str = "persisting"; - - async fn process(&self, req: PersistingRequest) -> PersistingResponse { - let PersistingRequest { - blocks, - commit_ledger_info, - callback, - } = req; - - self.persisting_handle - .commit(&blocks, commit_ledger_info, callback) - .await - } -} diff --git a/consensus/src/pipeline/pipeline_phase.rs b/consensus/src/pipeline/pipeline_phase.rs deleted file mode 100644 index 89ddbda65ed5c..0000000000000 --- a/consensus/src/pipeline/pipeline_phase.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters::BUFFER_MANAGER_PHASE_PROCESS_SECONDS, - pipeline::buffer_manager::{Receiver, Sender}, -}; -use aptos_logger::debug; -use async_trait::async_trait; -use futures::{SinkExt, StreamExt}; -use std::sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, - Arc, -}; - -#[async_trait] -pub trait StatelessPipeline: Send + Sync { - type Request; - type Response; - - const NAME: &'static str; - - async fn process(&self, req: Self::Request) -> Self::Response; -} - -struct TaskGuard { - counter: Arc, -} - -impl TaskGuard { - fn new(counter: Arc) -> Self { - counter.fetch_add(1, Ordering::SeqCst); - Self { counter } - } -} - -impl Drop for TaskGuard { - fn drop(&mut self) { - self.counter.fetch_sub(1, Ordering::SeqCst); - } -} - -pub struct CountedRequest { - req: Request, - guard: TaskGuard, -} - -impl CountedRequest { - pub fn new(req: Request, counter: Arc) -> Self { - let guard = TaskGuard::new(counter); - Self { req, guard } - } -} - -pub struct PipelinePhase { - rx: Receiver>, - maybe_tx: Option>, - processor: Box, - reset_flag: Arc, -} - -impl PipelinePhase { - pub fn new( - rx: Receiver>, - maybe_tx: Option>, - processor: Box, - reset_flag: Arc, - ) -> Self { - Self { - rx, - maybe_tx, - processor, - reset_flag, - } - } - - pub async fn start(mut self) { - // main loop - while let Some(counted_req) = self.rx.next().await { - let CountedRequest { req, guard: _guard } = counted_req; - if self.reset_flag.load(Ordering::SeqCst) { - continue; - } - let response = { - let _timer = BUFFER_MANAGER_PHASE_PROCESS_SECONDS - .with_label_values(&[T::NAME]) - .start_timer(); - self.processor.process(req).await - }; - if let Some(tx) = &mut self.maybe_tx { - if tx.send(response).await.is_err() { - debug!("Failed to send response, buffer manager probably dropped"); - break; - } - } - } - } -} diff --git a/consensus/src/pipeline/signing_phase.rs b/consensus/src/pipeline/signing_phase.rs deleted file mode 100644 index 8482b2c37fa72..0000000000000 --- a/consensus/src/pipeline/signing_phase.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::pipeline::pipeline_phase::StatelessPipeline; -use aptos_crypto::bls12381; -use aptos_safety_rules::Error; -use aptos_types::ledger_info::{LedgerInfo, LedgerInfoWithSignatures}; -use async_trait::async_trait; -use std::{ - fmt::{Debug, Display, Formatter}, - sync::Arc, -}; - -/// [ This class is used when consensus.decoupled = true ] -/// SigningPhase is a singleton that receives executed blocks from -/// the buffer manager and sign them. After getting the signature from -/// the safety rule, SigningPhase sends the signature and error (if any) back. - -pub struct SigningRequest { - pub ordered_ledger_info: LedgerInfoWithSignatures, - pub commit_ledger_info: LedgerInfo, -} - -impl Debug for SigningRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl Display for SigningRequest { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!( - f, - "SigningRequest({}, {})", - self.ordered_ledger_info, self.commit_ledger_info - ) - } -} - -pub trait CommitSignerProvider: Send + Sync { - fn sign_commit_vote( - &self, - ledger_info: LedgerInfoWithSignatures, - new_ledger_info: LedgerInfo, - ) -> Result; -} - -pub struct SigningResponse { - pub signature_result: Result, - pub commit_ledger_info: LedgerInfo, -} - -pub struct SigningPhase { - safety_rule_handle: Arc, -} - -impl SigningPhase { - pub fn new(safety_rule_handle: Arc) -> Self { - Self { safety_rule_handle } - } -} - -#[async_trait] -impl StatelessPipeline for SigningPhase { - type Request = SigningRequest; - type Response = SigningResponse; - - const NAME: &'static str = "signing"; - - async fn process(&self, req: SigningRequest) -> SigningResponse { - let SigningRequest { - ordered_ledger_info, - commit_ledger_info, - } = req; - - SigningResponse { - signature_result: self - .safety_rule_handle - .sign_commit_vote(ordered_ledger_info, commit_ledger_info.clone()), - commit_ledger_info, - } - } -} diff --git a/consensus/src/pipeline/tests/buffer_manager_tests.rs b/consensus/src/pipeline/tests/buffer_manager_tests.rs deleted file mode 100644 index 51aa56d8a289e..0000000000000 --- a/consensus/src/pipeline/tests/buffer_manager_tests.rs +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - metrics_safety_rules::MetricsSafetyRules, - network::{IncomingCommitRequest, NetworkSender}, - network_interface::{ConsensusMsg, ConsensusNetworkClient, DIRECT_SEND, RPC}, - pipeline::{ - buffer_manager::{ - create_channel, BufferManager, OrderedBlocks, Receiver, ResetAck, ResetRequest, - ResetSignal, Sender, - }, - decoupled_execution_utils::prepare_phases_and_buffer_manager, - execution_schedule_phase::ExecutionSchedulePhase, - execution_wait_phase::ExecutionWaitPhase, - persisting_phase::PersistingPhase, - pipeline_phase::PipelinePhase, - signing_phase::SigningPhase, - tests::test_utils::prepare_executed_blocks_with_ledger_info, - }, - test_utils::{ - consensus_runtime, timed_block_on, EmptyStateComputer, MockStorage, - RandomComputeResultStateComputer, - }, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::{aptos_channel, message_queues::QueueStyle}; -use aptos_config::network_id::NetworkId; -use aptos_consensus_types::{ - block::block_test_utils::certificate_for_genesis, pipelined_block::PipelinedBlock, - vote_proposal::VoteProposal, -}; -use aptos_crypto::{hash::ACCUMULATOR_PLACEHOLDER_HASH, HashValue}; -use aptos_infallible::Mutex; -use aptos_network::{ - application::{interface::NetworkClient, storage::PeersAndMetadata}, - peer_manager::{ConnectionRequestSender, PeerManagerRequestSender}, - protocols::{ - network, - network::{Event, NewNetworkSender}, - }, -}; -use aptos_safety_rules::{PersistentSafetyStorage, SafetyRulesManager}; -use aptos_secure_storage::Storage; -use aptos_types::{ - account_address::AccountAddress, - epoch_state::EpochState, - ledger_info::LedgerInfo, - validator_signer::ValidatorSigner, - validator_verifier::{random_validator_verifier, ValidatorVerifier}, - waypoint::Waypoint, -}; -use futures::{channel::oneshot, FutureExt, SinkExt, StreamExt}; -use itertools::enumerate; -use maplit::hashmap; -use std::sync::Arc; -use tokio::runtime::Runtime; - -pub fn prepare_buffer_manager( - bounded_executor: BoundedExecutor, -) -> ( - BufferManager, - Sender, - Sender, - aptos_channel::Sender, - aptos_channels::UnboundedReceiver>, - PipelinePhase, - PipelinePhase, - PipelinePhase, - PipelinePhase, - HashValue, - Vec, - Receiver, - ValidatorVerifier, -) { - let num_nodes = 1; - let channel_size = 30; - - let (signers, validators) = random_validator_verifier(num_nodes, None, false); - let signer = &signers[0]; - let author = signer.author(); - let validator_set = (&validators).into(); - - let waypoint = - Waypoint::new_epoch_boundary(&LedgerInfo::mock_genesis(Some(validator_set))).unwrap(); - - let safety_storage = PersistentSafetyStorage::initialize( - Storage::from(aptos_secure_storage::InMemoryStorage::new()), - signer.author(), - signer.private_key().clone(), - waypoint, - true, - ); - let (_, storage) = MockStorage::start_for_testing((&validators).into()); - - let safety_rules_manager = SafetyRulesManager::new_local(safety_storage); - - let mut safety_rules = MetricsSafetyRules::new(safety_rules_manager.client(), storage); - safety_rules.perform_initialize().unwrap(); - - let (network_reqs_tx, _network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - PeersAndMetadata::new(&[NetworkId::Validator]), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - - let (self_loop_tx, self_loop_rx) = aptos_channels::new_unbounded_test(); - let network = NetworkSender::new( - author, - consensus_network_client, - self_loop_tx, - validators.clone(), - ); - - let (msg_tx, msg_rx) = aptos_channel::new::( - QueueStyle::FIFO, - channel_size, - None, - ); - - let (result_tx, result_rx) = create_channel::(); - let state_computer = Arc::new(EmptyStateComputer::new(result_tx)); - - let (block_tx, block_rx) = create_channel::(); - let (buffer_reset_tx, buffer_reset_rx) = create_channel::(); - - let mocked_execution_proxy = Arc::new(RandomComputeResultStateComputer::new()); - let hash_val = mocked_execution_proxy.get_root_hash(); - - let ( - execution_schedule_phase_pipeline, - execution_wait_phase_pipeline, - signing_phase_pipeline, - persisting_phase_pipeline, - buffer_manager, - ) = prepare_phases_and_buffer_manager( - author, - mocked_execution_proxy, - Arc::new(Mutex::new(safety_rules)), - network, - msg_rx, - state_computer, - block_rx, - buffer_reset_rx, - Arc::new(EpochState { - epoch: 1, - verifier: validators.clone(), - }), - bounded_executor, - ); - - ( - buffer_manager, - block_tx, - buffer_reset_tx, - msg_tx, // channel to pass commit messages into the buffer manager - self_loop_rx, // channel to receive message from the buffer manager itself - execution_schedule_phase_pipeline, - execution_wait_phase_pipeline, - signing_phase_pipeline, - persisting_phase_pipeline, - hash_val, - signers, - result_rx, - validators, - ) -} - -pub fn launch_buffer_manager() -> ( - Sender, - Sender, - aptos_channel::Sender, - aptos_channels::UnboundedReceiver>, - HashValue, - Runtime, - Vec, - Receiver, - ValidatorVerifier, -) { - let runtime = consensus_runtime(); - - let bounded_executor: BoundedExecutor = BoundedExecutor::new(1, runtime.handle().clone()); - let ( - buffer_manager, - block_tx, - reset_tx, - msg_tx, // channel to pass commit messages into the buffer manager - self_loop_rx, // channel to receive message from the buffer manager itself - execution_schedule_phase_pipeline, - execution_wait_phase_pipeline, - signing_phase_pipeline, - persisting_phase_pipeline, - hash_val, - signers, - result_rx, - validators, - ) = prepare_buffer_manager(bounded_executor); - - runtime.spawn(execution_schedule_phase_pipeline.start()); - runtime.spawn(execution_wait_phase_pipeline.start()); - runtime.spawn(signing_phase_pipeline.start()); - runtime.spawn(persisting_phase_pipeline.start()); - runtime.spawn(buffer_manager.start()); - - ( - block_tx, - reset_tx, - msg_tx, - self_loop_rx, - hash_val, - runtime, - signers, - result_rx, - validators, - ) -} - -async fn loopback_commit_vote( - msg: Event, - msg_tx: &aptos_channel::Sender, - verifier: &ValidatorVerifier, -) { - match msg { - Event::RpcRequest(author, msg, protocol, callback) => { - if let ConsensusMsg::CommitMessage(msg) = msg { - msg.verify(verifier).unwrap(); - let request = IncomingCommitRequest { - req: *msg, - protocol, - response_sender: callback, - }; - // verify the message and send the message into self loop - msg_tx.push(author, request).ok(); - } - }, - _ => { - panic!("We are expecting a commit vote message."); - }, - }; -} - -async fn assert_results( - batches: Vec>, - result_rx: &mut Receiver, -) { - for (i, batch) in enumerate(batches) { - let OrderedBlocks { ordered_blocks, .. } = result_rx.next().await.unwrap(); - assert_eq!( - ordered_blocks.last().unwrap().id(), - batch.last().unwrap().id(), - "Inconsistent Block IDs (expected {} got {}) for {}-th block", - batch.last().unwrap().id(), - ordered_blocks.last().unwrap().id(), - i, - ); - } -} - -#[test] -fn buffer_manager_happy_path_test() { - // happy path - let ( - mut block_tx, - _reset_tx, - msg_tx, - mut self_loop_rx, - _hash_val, - runtime, - signers, - mut result_rx, - verifier, - ) = launch_buffer_manager(); - - let genesis_qc = certificate_for_genesis(); - let num_batches = 3; - let blocks_per_batch = 5; - let mut init_round = 0; - - let mut batches = vec![]; - let mut proofs = vec![]; - let mut last_proposal: Option = None; - - for _ in 0..num_batches { - let (vecblocks, li_sig, proposal) = prepare_executed_blocks_with_ledger_info( - &signers[0], - blocks_per_batch, - *ACCUMULATOR_PLACEHOLDER_HASH, - *ACCUMULATOR_PLACEHOLDER_HASH, - last_proposal, - Some(genesis_qc.clone()), - init_round, - ); - init_round += blocks_per_batch; - batches.push(vecblocks); - proofs.push(li_sig); - last_proposal = Some(proposal.last().unwrap().clone()); - } - - timed_block_on(&runtime, async move { - for i in 0..num_batches { - block_tx - .send(OrderedBlocks { - ordered_blocks: batches[i].clone(), - ordered_proof: proofs[i].clone(), - callback: Box::new(move |_, _| {}), - }) - .await - .ok(); - } - - // commit decision will be sent too, so 3 * 2 - for _ in 0..6 { - if let Some(msg) = self_loop_rx.next().await { - loopback_commit_vote(msg, &msg_tx, &verifier).await; - } - } - - // make sure the order is correct - assert_results(batches, &mut result_rx).await; - }); -} - -#[test] -fn buffer_manager_sync_test() { - // happy path - let ( - mut block_tx, - mut reset_tx, - msg_tx, - mut self_loop_rx, - _hash_val, - runtime, - signers, - mut result_rx, - verifier, - ) = launch_buffer_manager(); - - let genesis_qc = certificate_for_genesis(); - let num_batches = 100; - let blocks_per_batch = 5; - let mut init_round = 0; - - let mut batches = vec![]; - let mut proofs = vec![]; - let mut last_proposal: Option = None; - - for _ in 0..num_batches { - let (vecblocks, li_sig, proposal) = prepare_executed_blocks_with_ledger_info( - &signers[0], - blocks_per_batch, - *ACCUMULATOR_PLACEHOLDER_HASH, - *ACCUMULATOR_PLACEHOLDER_HASH, - last_proposal, - Some(genesis_qc.clone()), - init_round, - ); - init_round += blocks_per_batch; - batches.push(vecblocks); - proofs.push(li_sig); - last_proposal = Some(proposal.last().unwrap().clone()); - } - - let dropped_batches = 42; - - timed_block_on(&runtime, async move { - for i in 0..dropped_batches { - block_tx - .send(OrderedBlocks { - ordered_blocks: batches[i].clone(), - ordered_proof: proofs[i].clone(), - callback: Box::new(move |_, _| {}), - }) - .await - .ok(); - } - - // reset - let (tx, rx) = oneshot::channel::(); - - reset_tx - .send(ResetRequest { - tx, - signal: ResetSignal::TargetRound(1), - }) - .await - .ok(); - rx.await.ok(); - - // start sending back commit vote after reset, to avoid [0..dropped_batches] being sent to result_rx - tokio::spawn(async move { - while let Some(msg) = self_loop_rx.next().await { - loopback_commit_vote(msg, &msg_tx, &verifier).await; - } - }); - - for i in dropped_batches..num_batches { - block_tx - .send(OrderedBlocks { - ordered_blocks: batches[i].clone(), - ordered_proof: proofs[i].clone(), - callback: Box::new(move |_, _| {}), - }) - .await - .ok(); - } - - // we should only see batches[dropped_batches..num_batches] - assert_results(batches.drain(dropped_batches..).collect(), &mut result_rx).await; - - assert!(result_rx.next().now_or_never().is_none()); - }); -} diff --git a/consensus/src/pipeline/tests/execution_phase_tests.rs b/consensus/src/pipeline/tests/execution_phase_tests.rs deleted file mode 100644 index bd582b44b2673..0000000000000 --- a/consensus/src/pipeline/tests/execution_phase_tests.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::{ - buffer_manager::create_channel, - execution_schedule_phase::{ExecutionRequest, ExecutionSchedulePhase}, - execution_wait_phase::{ExecutionResponse, ExecutionWaitPhase}, - pipeline_phase::{CountedRequest, PipelinePhase, StatelessPipeline}, - tests::phase_tester::PhaseTester, - }, - state_replication::StateComputer, - test_utils::{consensus_runtime, RandomComputeResultStateComputer}, -}; -use aptos_consensus_types::{ - block::{block_test_utils::certificate_for_genesis, Block}, - common::Payload, - pipelined_block::PipelinedBlock, - quorum_cert::QuorumCert, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::{ExecutorError, StateComputeResult}; -use aptos_types::{ledger_info::LedgerInfo, validator_verifier::random_validator_verifier}; -use async_trait::async_trait; -use std::sync::{ - atomic::{AtomicBool, AtomicU64}, - Arc, -}; - -// ExecutionSchedulePhase and ExecutionWaitPhase chained together. -// In BufferManager they are chained through the main loop. -pub struct ExecutionPhaseForTest { - schedule_phase: ExecutionSchedulePhase, - wait_phase: ExecutionWaitPhase, -} - -impl ExecutionPhaseForTest { - pub fn new(execution_proxy: Arc) -> Self { - let schedule_phase = ExecutionSchedulePhase::new(execution_proxy); - let wait_phase = ExecutionWaitPhase; - Self { - schedule_phase, - wait_phase, - } - } -} - -#[async_trait] -impl StatelessPipeline for ExecutionPhaseForTest { - type Request = ExecutionRequest; - type Response = ExecutionResponse; - - const NAME: &'static str = "execution"; - - async fn process(&self, req: ExecutionRequest) -> ExecutionResponse { - let wait_req = self.schedule_phase.process(req).await; - self.wait_phase.process(wait_req).await - } -} - -pub fn prepare_execution_phase() -> (HashValue, ExecutionPhaseForTest) { - let execution_proxy = Arc::new(RandomComputeResultStateComputer::new()); - let random_hash_value = execution_proxy.get_root_hash(); - let execution_phase = ExecutionPhaseForTest::new(execution_proxy); - - (random_hash_value, execution_phase) -} - -fn dummy_guard() -> CountedRequest<()> { - CountedRequest::new((), Arc::new(AtomicU64::new(0))) -} - -fn add_execution_phase_test_cases( - phase_tester: &mut PhaseTester, - random_hash_value: HashValue, -) { - let genesis_qc = certificate_for_genesis(); - let (signers, _validators) = random_validator_verifier(1, None, false); - let block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc, - &signers[0], - Vec::new(), - ) - .unwrap(); - - // happy path - phase_tester.add_test_case( - ExecutionRequest { - ordered_blocks: vec![PipelinedBlock::new( - block, - vec![], - StateComputeResult::new_dummy(), - )], - lifetime_guard: dummy_guard(), - }, - Box::new(move |resp| { - assert_eq!( - resp.inner.unwrap()[0].compute_result().root_hash(), - random_hash_value - ); - }), - ); - - // empty block - phase_tester.add_test_case( - ExecutionRequest { - ordered_blocks: vec![], - lifetime_guard: dummy_guard(), - }, - Box::new(move |resp| assert!(matches!(resp.inner, Err(ExecutorError::EmptyBlocks)))), - ); - - // bad parent id - let bad_qc = QuorumCert::certificate_for_genesis_from_ledger_info( - &LedgerInfo::mock_genesis(None), - random_hash_value, - ); - let bad_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - bad_qc, - &signers[0], - Vec::new(), - ) - .unwrap(); - phase_tester.add_test_case( - ExecutionRequest { - ordered_blocks: vec![PipelinedBlock::new( - bad_block, - vec![], - StateComputeResult::new_dummy(), - )], - lifetime_guard: dummy_guard(), - }, - Box::new(move |resp| assert!(matches!(resp.inner, Err(ExecutorError::BlockNotFound(_))))), - ); -} - -#[test] -fn execution_phase_tests() { - let runtime = consensus_runtime(); - - // unit tests - let (random_hash_value, execution_phase) = prepare_execution_phase(); - let mut unit_phase_tester = PhaseTester::::new(); - add_execution_phase_test_cases(&mut unit_phase_tester, random_hash_value); - unit_phase_tester.unit_test(&execution_phase); - - // e2e tests - let (in_channel_tx, in_channel_rx) = create_channel::>(); - let (out_channel_tx, out_channel_rx) = create_channel::(); - let reset_flag = Arc::new(AtomicBool::new(false)); - - let execution_phase_pipeline = PipelinePhase::new( - in_channel_rx, - Some(out_channel_tx), - Box::new(execution_phase), - reset_flag, - ); - - runtime.spawn(execution_phase_pipeline.start()); - - let mut e2e_phase_tester = PhaseTester::::new(); - add_execution_phase_test_cases(&mut e2e_phase_tester, random_hash_value); - e2e_phase_tester.e2e_test(in_channel_tx, out_channel_rx); -} diff --git a/consensus/src/pipeline/tests/integration_tests.rs b/consensus/src/pipeline/tests/integration_tests.rs deleted file mode 100644 index 99feb270a5197..0000000000000 --- a/consensus/src/pipeline/tests/integration_tests.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// TODO: integration tests diff --git a/consensus/src/pipeline/tests/mod.rs b/consensus/src/pipeline/tests/mod.rs deleted file mode 100644 index 2d68165141586..0000000000000 --- a/consensus/src/pipeline/tests/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -mod buffer_manager_tests; -mod execution_phase_tests; -mod integration_tests; -mod ordering_state_computer_tests; -mod phase_tester; -mod signing_phase_tests; -mod test_utils; diff --git a/consensus/src/pipeline/tests/ordering_state_computer_tests.rs b/consensus/src/pipeline/tests/ordering_state_computer_tests.rs deleted file mode 100644 index 3bdf54f06eee2..0000000000000 --- a/consensus/src/pipeline/tests/ordering_state_computer_tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[test] -fn test_ordering_state_computer() { - // TODO: after changing the ordering state computer -} diff --git a/consensus/src/pipeline/tests/phase_tester.rs b/consensus/src/pipeline/tests/phase_tester.rs deleted file mode 100644 index 2ae67bec53f4c..0000000000000 --- a/consensus/src/pipeline/tests/phase_tester.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::{ - buffer_manager::{Receiver, Sender}, - pipeline_phase::{CountedRequest, StatelessPipeline}, - }, - test_utils::{consensus_runtime, timed_block_on}, -}; -use futures::{SinkExt, StreamExt}; -use std::sync::{atomic::AtomicU64, Arc}; - -pub struct PhaseTestCase { - index: usize, - input: T::Request, - judge: Box, - prompt: Option, -} - -pub struct PhaseTester { - pub cases: Vec>, -} - -impl PhaseTester { - pub fn new() -> Self { - Self { cases: vec![] } - } - - pub fn add_test_case(&mut self, input: T::Request, judge: Box) { - self.add_test_case_with_prompt(input, judge, None) - } - - pub fn add_test_case_with_prompt( - &mut self, - input: T::Request, - judge: Box, - prompt: Option, - ) { - self.cases.push(PhaseTestCase { - index: self.cases.len(), - input, - judge, - prompt, - }); - } - - // unit tests are for phase processors only, - // this function consumes the tester - pub fn unit_test(self, processor: &T) { - let runtime = consensus_runtime(); - - timed_block_on(&runtime, async move { - for PhaseTestCase { - index, - input, - judge, - prompt, - } in self.cases - { - eprint!( - "Unit Test - {}:", - prompt.unwrap_or(format!("Test {}", index)) - ); - let resp = processor.process(input).await; - judge(resp); - eprintln!(" OK",); - } - }) - } - - // e2e tests are for the pipeline phases - // this function consumes the tester - pub fn e2e_test( - self, - mut tx: Sender>, - mut rx: Receiver, - ) { - let runtime = consensus_runtime(); - - timed_block_on(&runtime, async move { - for PhaseTestCase { - index, - input, - judge, - prompt, - } in self.cases - { - eprint!( - "E2E Test - {}:", - prompt.unwrap_or(format!("Test {}", index)) - ); - tx.send(CountedRequest::new(input, Arc::new(AtomicU64::new(0)))) - .await - .ok(); - let resp = rx.next().await.unwrap(); - judge(resp); - eprintln!(" OK",); - } - }) - } -} diff --git a/consensus/src/pipeline/tests/signing_phase_tests.rs b/consensus/src/pipeline/tests/signing_phase_tests.rs deleted file mode 100644 index e5c5b534b6f0f..0000000000000 --- a/consensus/src/pipeline/tests/signing_phase_tests.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::{ - buffer_manager::{create_channel, Receiver, Sender}, - pipeline_phase::{CountedRequest, PipelinePhase}, - signing_phase::{SigningPhase, SigningRequest, SigningResponse}, - tests::{ - phase_tester::PhaseTester, - test_utils::{ - prepare_executed_blocks_with_executed_ledger_info, - prepare_executed_blocks_with_ordered_ledger_info, prepare_safety_rules, - }, - }, - }, - test_utils::consensus_runtime, -}; -use aptos_crypto::HashValue; -use aptos_safety_rules::Error; -use aptos_types::{ - aggregate_signature::AggregateSignature, - block_info::BlockInfo, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - validator_signer::ValidatorSigner, -}; -use std::sync::{atomic::AtomicBool, Arc}; - -pub fn prepare_signing_pipeline( - signing_phase: SigningPhase, -) -> ( - Sender>, - Receiver, - PipelinePhase, -) { - // e2e tests - let (in_channel_tx, in_channel_rx) = create_channel::>(); - let (out_channel_tx, out_channel_rx) = create_channel::(); - let reset_flag = Arc::new(AtomicBool::new(false)); - - let signing_phase_pipeline = PipelinePhase::new( - in_channel_rx, - Some(out_channel_tx), - Box::new(signing_phase), - reset_flag, - ); - - (in_channel_tx, out_channel_rx, signing_phase_pipeline) -} - -fn add_signing_phase_test_cases( - phase_tester: &mut PhaseTester, - signers: &[ValidatorSigner], -) { - let (vecblocks, ordered_ledger_info) = - prepare_executed_blocks_with_ordered_ledger_info(&signers[0]); - let commit_ledger_info = LedgerInfo::new( - vecblocks.last().unwrap().block_info(), - HashValue::from_u64(0xBEEF), - ); - - // happy path - phase_tester.add_test_case( - SigningRequest { - ordered_ledger_info: ordered_ledger_info.clone(), - commit_ledger_info: commit_ledger_info.clone(), - }, - Box::new(move |resp| { - assert!(resp.signature_result.is_ok()); - assert_eq!(resp.commit_ledger_info, commit_ledger_info); - }), - ); - - let (_, executed_ledger_info) = prepare_executed_blocks_with_executed_ledger_info(&signers[0]); - let inconsistent_commit_ledger_info = - LedgerInfo::new(BlockInfo::random(1), HashValue::from_u64(0xBEEF)); - - // inconsistent - phase_tester.add_test_case( - SigningRequest { - ordered_ledger_info: ordered_ledger_info.clone(), - commit_ledger_info: inconsistent_commit_ledger_info, - }, - Box::new(move |resp| { - assert!(matches!( - resp.signature_result, - Err(Error::InconsistentExecutionResult(_, _)) - )); - }), - ); - - // not ordered-only - phase_tester.add_test_case( - SigningRequest { - ordered_ledger_info: executed_ledger_info.clone(), - commit_ledger_info: executed_ledger_info.ledger_info().clone(), - }, - Box::new(move |resp| { - assert!(matches!( - resp.signature_result, - Err(Error::InvalidOrderedLedgerInfo(_)) - )); - }), - ); - - // invalid quorum - phase_tester.add_test_case( - SigningRequest { - ordered_ledger_info: LedgerInfoWithSignatures::new( - ordered_ledger_info.ledger_info().clone(), - AggregateSignature::empty(), - ), - commit_ledger_info: executed_ledger_info.ledger_info().clone(), - }, - Box::new(move |resp| { - assert!(matches!( - resp.signature_result, - Err(Error::InvalidQuorumCertificate(_)) - )); - }), - ); -} - -#[test] -fn signing_phase_tests() { - let runtime = consensus_runtime(); - - let (safety_rule_handle, signers) = prepare_safety_rules(); - - let signing_phase = SigningPhase::new(safety_rule_handle); - - // unit tests - let mut unit_phase_tester = PhaseTester::::new(); - add_signing_phase_test_cases(&mut unit_phase_tester, &signers); - unit_phase_tester.unit_test(&signing_phase); - - let (in_channel_tx, out_channel_rx, signing_phase_pipeline) = - prepare_signing_pipeline(signing_phase); - - runtime.spawn(signing_phase_pipeline.start()); - - let mut e2e_phase_tester = PhaseTester::::new(); - add_signing_phase_test_cases(&mut e2e_phase_tester, &signers); - e2e_phase_tester.e2e_test(in_channel_tx, out_channel_rx); -} diff --git a/consensus/src/pipeline/tests/test_utils.rs b/consensus/src/pipeline/tests/test_utils.rs deleted file mode 100644 index 014cd3ed08bcd..0000000000000 --- a/consensus/src/pipeline/tests/test_utils.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{metrics_safety_rules::MetricsSafetyRules, test_utils::MockStorage}; -use aptos_consensus_types::{ - block::block_test_utils::certificate_for_genesis, - common::{Payload, Round}, - pipelined_block::PipelinedBlock, - quorum_cert::QuorumCert, - vote_proposal::VoteProposal, -}; -use aptos_crypto::{hash::ACCUMULATOR_PLACEHOLDER_HASH, HashValue}; -use aptos_executor_types::StateComputeResult; -use aptos_infallible::Mutex; -use aptos_safety_rules::{ - test_utils::{make_proposal_with_parent, make_proposal_with_qc}, - PersistentSafetyStorage, SafetyRulesManager, -}; -use aptos_secure_storage::Storage; -use aptos_types::{ - ledger_info::{generate_ledger_info_with_sig, LedgerInfo, LedgerInfoWithSignatures}, - validator_signer::ValidatorSigner, - validator_verifier::random_validator_verifier, - waypoint::Waypoint, -}; -use std::sync::Arc; - -pub fn prepare_safety_rules() -> (Arc>, Vec) { - let num_nodes = 1; - - // environment setup - let (signers, validators) = random_validator_verifier(num_nodes, None, false); - let validator_set = (&validators).into(); - let signer = &signers[0]; - - let waypoint = - Waypoint::new_epoch_boundary(&LedgerInfo::mock_genesis(Some(validator_set))).unwrap(); - - let safety_storage = PersistentSafetyStorage::initialize( - Storage::from(aptos_secure_storage::InMemoryStorage::new()), - signer.author(), - signer.private_key().clone(), - waypoint, - true, - ); - let (_, storage) = MockStorage::start_for_testing((&validators).into()); - - let safety_rules_manager = SafetyRulesManager::new_local(safety_storage); - let mut safety_rules = MetricsSafetyRules::new(safety_rules_manager.client(), storage); - safety_rules.perform_initialize().unwrap(); - - (Arc::new(Mutex::new(safety_rules)), signers) -} - -// This function prioritizes using parent over init_qc -pub fn prepare_executed_blocks_with_ledger_info( - signer: &ValidatorSigner, - num_blocks: Round, - executed_hash: HashValue, - consensus_hash: HashValue, - some_parent: Option, - init_qc: Option, - init_round: Round, -) -> ( - Vec, - LedgerInfoWithSignatures, - Vec, -) { - assert!(num_blocks > 0); - - let p1 = if let Some(parent) = some_parent { - make_proposal_with_parent( - Payload::empty(false, true), - init_round, - &parent, - None, - signer, - ) - } else { - make_proposal_with_qc(init_round, init_qc.unwrap(), signer) - }; - - let mut proposals: Vec = vec![p1]; - - for i in 1..num_blocks { - println!("Generating {}", i); - let parent = proposals.last().unwrap(); - let proposal = make_proposal_with_parent( - Payload::empty(false, true), - init_round + i, - parent, - None, - signer, - ); - proposals.push(proposal); - } - - let compute_result = StateComputeResult::new( - executed_hash, - vec![], // dummy subtree - 0, - vec![], - 0, - None, - vec![], - vec![], - vec![], - ); - - let li = LedgerInfo::new( - proposals.last().unwrap().block().gen_block_info( - compute_result.root_hash(), - compute_result.version(), - compute_result.epoch_state().clone(), - ), - consensus_hash, - ); - - let li_sig = generate_ledger_info_with_sig(&[signer.clone()], li); - - let executed_blocks: Vec = proposals - .iter() - .map(|proposal| { - PipelinedBlock::new(proposal.block().clone(), vec![], compute_result.clone()) - }) - .collect(); - - (executed_blocks, li_sig, proposals) -} - -pub fn prepare_executed_blocks_with_executed_ledger_info( - signer: &ValidatorSigner, -) -> (Vec, LedgerInfoWithSignatures) { - let genesis_qc = certificate_for_genesis(); - let (executed_blocks, li_sig, _) = prepare_executed_blocks_with_ledger_info( - signer, - 1, - HashValue::random(), - HashValue::from_u64(0xBEEF), - None, - Some(genesis_qc), - 0, - ); - (executed_blocks, li_sig) -} - -pub fn prepare_executed_blocks_with_ordered_ledger_info( - signer: &ValidatorSigner, -) -> (Vec, LedgerInfoWithSignatures) { - let genesis_qc = certificate_for_genesis(); - let (executed_blocks, li_sig, _) = prepare_executed_blocks_with_ledger_info( - signer, - 1, - *ACCUMULATOR_PLACEHOLDER_HASH, - *ACCUMULATOR_PLACEHOLDER_HASH, - None, - Some(genesis_qc), - 0, - ); - (executed_blocks, li_sig) -} diff --git a/consensus/src/qc_aggregator.rs b/consensus/src/qc_aggregator.rs deleted file mode 100644 index 2f695c651927b..0000000000000 --- a/consensus/src/qc_aggregator.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pending_votes::{PendingVotes, VoteReceptionResult}, - util::time_service::TimeService, -}; -use aptos_config::config::{DelayedQcAggregatorConfig, QcAggregatorType}; -use aptos_consensus_types::{delayed_qc_msg::DelayedQcMsg, vote::Vote}; -use aptos_logger::{error, info}; -use aptos_types::{ - ledger_info::LedgerInfoWithPartialSignatures, validator_verifier::ValidatorVerifier, -}; -use futures::SinkExt; -use futures_channel::mpsc::UnboundedSender; -use std::{sync::Arc, time::Duration}; -use tokio::time::sleep; - -pub trait QcAggregator: Send + Sync { - fn handle_aggregated_qc( - &mut self, - validator_verifier: &ValidatorVerifier, - aggregated_voting_power: u128, - vote: &Vote, - li_with_sig: &LedgerInfoWithPartialSignatures, - ) -> VoteReceptionResult; -} - -struct NoDelayQcAggregator {} - -pub fn create_qc_aggregator( - qc_aggregator_type: QcAggregatorType, - time_service: Arc, - delayed_qc_tx: UnboundedSender, -) -> Box { - match qc_aggregator_type { - QcAggregatorType::NoDelay => Box::new(NoDelayQcAggregator {}), - QcAggregatorType::Delayed(delay_config) => { - let DelayedQcAggregatorConfig { - max_delay_after_round_start_ms, - aggregated_voting_power_pct_to_wait, - pct_delay_after_qc_aggregated, - } = delay_config; - Box::new(DelayedQcAggregator::new( - Duration::from_millis(max_delay_after_round_start_ms), - aggregated_voting_power_pct_to_wait, - pct_delay_after_qc_aggregated, - time_service, - delayed_qc_tx, - )) - }, - } -} - -impl QcAggregator for NoDelayQcAggregator { - fn handle_aggregated_qc( - &mut self, - validator_verifier: &ValidatorVerifier, - aggregated_voting_power: u128, - vote: &Vote, - li_with_sig: &LedgerInfoWithPartialSignatures, - ) -> VoteReceptionResult { - assert!( - aggregated_voting_power >= validator_verifier.quorum_voting_power(), - "QC aggregation should not be triggered if we don't have enough votes to form a QC" - ); - PendingVotes::aggregate_qc_now(validator_verifier, li_with_sig, vote.vote_data()) - } -} - -struct DelayedQcAggregator { - round_start_time: Duration, - max_delay_after_round_start: Duration, - aggregated_voting_power_pct_to_wait: usize, - pct_delay_after_qc_aggregated: usize, - time_service: Arc, - // True, if we already have enough vote to aggregate a QC, but we have trigged a delayed QC - // aggregation event to collect as many votes as possible. - qc_aggregation_delayed: bool, - // To send delayed QC aggregation events to the round manager. - delayed_qc_tx: UnboundedSender, -} - -impl DelayedQcAggregator { - pub fn new( - max_delay_after_round_start: Duration, - aggregated_voting_power_pct_to_wait: usize, - pct_delay_after_qc_aggregated: usize, - time_service: Arc, - delayed_qc_tx: UnboundedSender, - ) -> Self { - let round_start_time = time_service.get_current_timestamp(); - Self { - round_start_time, - max_delay_after_round_start, - aggregated_voting_power_pct_to_wait, - pct_delay_after_qc_aggregated, - time_service, - qc_aggregation_delayed: false, - delayed_qc_tx, - } - } -} - -impl QcAggregator for DelayedQcAggregator { - fn handle_aggregated_qc( - &mut self, - validator_verifier: &ValidatorVerifier, - aggregated_voting_power: u128, - vote: &Vote, - li_with_sig: &LedgerInfoWithPartialSignatures, - ) -> VoteReceptionResult { - assert!( - aggregated_voting_power >= validator_verifier.quorum_voting_power(), - "QC aggregation should not be triggered if we don't have enough votes to form a QC" - ); - let current_time = self.time_service.get_current_timestamp(); - - // If we have reached the aggregated voting power threshold, we should aggregate the QC now. - if aggregated_voting_power - >= self.aggregated_voting_power_pct_to_wait as u128 - * validator_verifier.total_voting_power() - / 100 - { - // Voting power is u128 so there is no overflow here. - info!( - "QC aggregation triggered by aggregated voting power: {}", - aggregated_voting_power - ); - return PendingVotes::aggregate_qc_now( - validator_verifier, - li_with_sig, - vote.vote_data(), - ); - } - - // If we have not reached the aggregated voting power threshold and have - // already triggered a delayed QC aggregation event, we should not trigger another - // one. - if self.qc_aggregation_delayed { - return VoteReceptionResult::VoteAddedQCDelayed(aggregated_voting_power); - } - - let time_since_round_start = current_time - self.round_start_time; - if time_since_round_start >= self.max_delay_after_round_start { - info!( - "QC aggregation triggered by time: {} ms", - time_since_round_start.as_millis() - ); - return PendingVotes::aggregate_qc_now( - validator_verifier, - li_with_sig, - vote.vote_data(), - ); - } - - let wait_time = (self.max_delay_after_round_start - time_since_round_start) - .min(time_since_round_start * self.pct_delay_after_qc_aggregated as u32 / 100); - - let delayed_qc_event = DelayedQcMsg::new(vote.clone()); - self.qc_aggregation_delayed = true; - - let mut delayed_qc_sender = self.delayed_qc_tx.clone(); - - info!( - "QC aggregation delayed by {} ms, wait time: {} ms", - time_since_round_start.as_millis(), - wait_time.as_millis() - ); - - tokio::spawn(async move { - sleep(wait_time).await; - if let Err(e) = delayed_qc_sender.send(delayed_qc_event).await { - error!("Failed to send event to round manager {:?}", e); - } - }); - - VoteReceptionResult::VoteAddedQCDelayed(aggregated_voting_power) - } -} diff --git a/consensus/src/quorum_store/batch_coordinator.rs b/consensus/src/quorum_store/batch_coordinator.rs deleted file mode 100644 index 628645c5dac36..0000000000000 --- a/consensus/src/quorum_store/batch_coordinator.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::{NetworkSender, QuorumStoreSender}, - quorum_store::{ - batch_store::{BatchStore, BatchWriter}, - counters, - proof_manager::ProofManagerCommand, - types::{Batch, PersistedValue}, - }, -}; -use anyhow::ensure; -use aptos_logger::prelude::*; -use aptos_types::PeerId; -use std::sync::Arc; -use tokio::sync::{ - mpsc::{Receiver, Sender}, - oneshot, -}; - -#[derive(Debug)] -pub enum BatchCoordinatorCommand { - Shutdown(oneshot::Sender<()>), - NewBatches(PeerId, Vec), -} - -/// The `BatchCoordinator` is responsible for coordinating the receipt and persistence of batches. -pub struct BatchCoordinator { - my_peer_id: PeerId, - network_sender: Arc, - sender_to_proof_manager: Arc>, - batch_store: Arc, - max_batch_txns: u64, - max_batch_bytes: u64, - max_total_txns: u64, - max_total_bytes: u64, -} - -impl BatchCoordinator { - pub(crate) fn new( - my_peer_id: PeerId, - network_sender: NetworkSender, - sender_to_proof_manager: Sender, - batch_store: Arc, - max_batch_txns: u64, - max_batch_bytes: u64, - max_total_txns: u64, - max_total_bytes: u64, - ) -> Self { - Self { - my_peer_id, - network_sender: Arc::new(network_sender), - sender_to_proof_manager: Arc::new(sender_to_proof_manager), - batch_store, - max_batch_txns, - max_batch_bytes, - max_total_txns, - max_total_bytes, - } - } - - fn persist_and_send_digests(&self, persist_requests: Vec) { - if persist_requests.is_empty() { - return; - } - - let batch_store = self.batch_store.clone(); - let network_sender = self.network_sender.clone(); - let sender_to_proof_manager = self.sender_to_proof_manager.clone(); - tokio::spawn(async move { - let peer_id = persist_requests[0].author(); - let batches = persist_requests - .iter() - .map(|persisted_value| persisted_value.batch_info().clone()) - .collect(); - let signed_batch_infos = batch_store.persist(persist_requests); - if !signed_batch_infos.is_empty() { - network_sender - .send_signed_batch_info_msg(signed_batch_infos, vec![peer_id]) - .await; - } - let _ = sender_to_proof_manager - .send(ProofManagerCommand::ReceiveBatches(batches)) - .await; - }); - } - - fn ensure_max_limits(&self, batches: &[Batch]) -> anyhow::Result<()> { - let mut total_txns = 0; - let mut total_bytes = 0; - for batch in batches.iter() { - ensure!( - batch.num_txns() <= self.max_batch_txns, - "Exceeds batch txn limit {} > {}", - batch.num_txns(), - self.max_batch_txns, - ); - ensure!( - batch.num_bytes() <= self.max_batch_bytes, - "Exceeds batch bytes limit {} > {}", - batch.num_bytes(), - self.max_batch_bytes, - ); - - total_txns += batch.num_txns(); - total_bytes += batch.num_bytes(); - } - ensure!( - total_txns <= self.max_total_txns, - "Exceeds total txn limit {} > {}", - total_txns, - self.max_total_txns, - ); - ensure!( - total_bytes <= self.max_total_bytes, - "Exceeds total bytes limit: {} > {}", - total_bytes, - self.max_total_bytes, - ); - - Ok(()) - } - - async fn handle_batches_msg(&mut self, author: PeerId, batches: Vec) { - if let Err(e) = self.ensure_max_limits(&batches) { - error!("Batch from {}: {}", author, e); - counters::RECEIVED_BATCH_MAX_LIMIT_FAILED.inc(); - return; - } - - let mut persist_requests = vec![]; - for batch in batches.into_iter() { - persist_requests.push(batch.into()); - } - counters::RECEIVED_BATCH_COUNT.inc_by(persist_requests.len() as u64); - if author != self.my_peer_id { - counters::RECEIVED_REMOTE_BATCH_COUNT.inc_by(persist_requests.len() as u64); - } - self.persist_and_send_digests(persist_requests); - } - - pub(crate) async fn start(mut self, mut command_rx: Receiver) { - while let Some(command) = command_rx.recv().await { - match command { - BatchCoordinatorCommand::Shutdown(ack_tx) => { - ack_tx - .send(()) - .expect("Failed to send shutdown ack to QuorumStoreCoordinator"); - break; - }, - BatchCoordinatorCommand::NewBatches(author, batches) => { - self.handle_batches_msg(author, batches).await; - }, - } - } - } -} diff --git a/consensus/src/quorum_store/batch_generator.rs b/consensus/src/quorum_store/batch_generator.rs deleted file mode 100644 index 6a868578d9f41..0000000000000 --- a/consensus/src/quorum_store/batch_generator.rs +++ /dev/null @@ -1,492 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 -use crate::{ - monitor, - network::{NetworkSender, QuorumStoreSender}, - quorum_store::{ - batch_store::BatchWriter, - counters, - quorum_store_db::QuorumStoreStorage, - types::Batch, - utils::{MempoolProxy, TimeExpirations}, - }, -}; -use aptos_config::config::QuorumStoreConfig; -use aptos_consensus_types::{ - common::{TransactionInProgress, TransactionSummary}, - proof_of_store::{BatchId, BatchInfo}, -}; -use aptos_logger::prelude::*; -use aptos_mempool::QuorumStoreRequest; -use aptos_types::{transaction::SignedTransaction, PeerId}; -use futures_channel::mpsc::Sender; -use std::{ - collections::{btree_map::Entry, BTreeMap, HashMap}, - sync::Arc, - time::{Duration, Instant}, -}; -use tokio::time::Interval; - -#[derive(Debug)] -pub enum BatchGeneratorCommand { - CommitNotification(u64, Vec), - ProofExpiration(Vec), - Shutdown(tokio::sync::oneshot::Sender<()>), -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct BackPressure { - pub txn_count: bool, - pub proof_count: bool, -} - -pub struct BatchGenerator { - epoch: u64, - my_peer_id: PeerId, - batch_id: BatchId, - db: Arc, - batch_writer: Arc, - config: QuorumStoreConfig, - mempool_proxy: MempoolProxy, - batches_in_progress: HashMap>, - txns_in_progress_sorted: BTreeMap, - batch_expirations: TimeExpirations, - latest_block_timestamp: u64, - last_end_batch_time: Instant, - // quorum store back pressure, get updated from proof manager - back_pressure: BackPressure, -} - -impl BatchGenerator { - pub(crate) fn new( - epoch: u64, - my_peer_id: PeerId, - config: QuorumStoreConfig, - db: Arc, - batch_writer: Arc, - mempool_tx: Sender, - mempool_txn_pull_timeout_ms: u64, - ) -> Self { - let batch_id = if let Some(mut id) = db - .clean_and_get_batch_id(epoch) - .expect("Could not read from db") - { - // If the node shut down mid-batch, then this increment is needed - id.increment(); - id - } else { - BatchId::new(aptos_infallible::duration_since_epoch().as_micros() as u64) - }; - debug!("Initialized with batch_id of {}", batch_id); - let mut incremented_batch_id = batch_id; - incremented_batch_id.increment(); - db.save_batch_id(epoch, incremented_batch_id) - .expect("Could not save to db"); - - Self { - epoch, - my_peer_id, - batch_id, - db, - batch_writer, - config, - mempool_proxy: MempoolProxy::new(mempool_tx, mempool_txn_pull_timeout_ms), - batches_in_progress: HashMap::new(), - txns_in_progress_sorted: BTreeMap::new(), - batch_expirations: TimeExpirations::new(), - latest_block_timestamp: 0, - last_end_batch_time: Instant::now(), - back_pressure: BackPressure { - txn_count: false, - proof_count: false, - }, - } - } - - fn create_new_batch( - &mut self, - txns: Vec, - expiry_time: u64, - bucket_start: u64, - ) -> Batch { - let batch_id = self.batch_id; - self.batch_id.increment(); - self.db - .save_batch_id(self.epoch, self.batch_id) - .expect("Could not save to db"); - - let txns_in_progress: Vec<_> = txns - .iter() - .map(|txn| { - ( - TransactionSummary::new(txn.sender(), txn.sequence_number()), - TransactionInProgress::new(txn.gas_unit_price()), - ) - }) - .collect(); - - self.insert_batch_in_progress(batch_id, txns_in_progress); - self.batch_expirations.add_item(batch_id, expiry_time); - - counters::CREATED_BATCHES_COUNT.inc(); - counters::num_txn_per_batch(bucket_start.to_string().as_str(), txns.len()); - - Batch::new( - batch_id, - txns, - self.epoch, - expiry_time, - self.my_peer_id, - bucket_start, - ) - } - - /// Push num_txns from txns into batches. If num_txns is larger than max size, then multiple - /// batches are pushed. - fn push_bucket_to_batches( - &mut self, - batches: &mut Vec, - txns: &mut Vec, - num_txns_in_bucket: usize, - expiry_time: u64, - bucket_start: u64, - total_batches_remaining: &mut u64, - ) { - let mut txns_remaining = num_txns_in_bucket; - while txns_remaining > 0 { - if *total_batches_remaining == 0 { - return; - } - let num_take_txns = std::cmp::min(self.config.sender_max_batch_txns, txns_remaining); - let mut batch_bytes_remaining = self.config.sender_max_batch_bytes as u64; - let num_batch_txns = txns - .iter() - .take(num_take_txns) - .take_while(|txn| { - let txn_bytes = txn.txn_bytes_len() as u64; - if batch_bytes_remaining.checked_sub(txn_bytes).is_some() { - batch_bytes_remaining -= txn_bytes; - true - } else { - false - } - }) - .count(); - if num_batch_txns > 0 { - let batch_txns: Vec<_> = txns.drain(0..num_batch_txns).collect(); - let batch = self.create_new_batch(batch_txns, expiry_time, bucket_start); - batches.push(batch); - *total_batches_remaining = total_batches_remaining.saturating_sub(1); - txns_remaining -= num_batch_txns; - } - } - } - - fn bucket_into_batches( - &mut self, - pulled_txns: &mut Vec, - expiry_time: u64, - ) -> Vec { - // Sort by gas, in descending order. This is a stable sort on existing mempool ordering, - // so will not reorder accounts or their sequence numbers as long as they have the same gas. - pulled_txns.sort_by_key(|txn| u64::MAX - txn.gas_unit_price()); - - let reverse_buckets_excluding_zero: Vec<_> = self - .config - .batch_buckets - .iter() - .skip(1) - .rev() - .cloned() - .collect(); - - let mut max_batches_remaining = self.config.sender_max_num_batches as u64; - let mut batches = vec![]; - for bucket_start in &reverse_buckets_excluding_zero { - if pulled_txns.is_empty() || max_batches_remaining == 0 { - return batches; - } - - // Search for key in descending gas order - let num_txns_in_bucket = match pulled_txns - .binary_search_by_key(&(u64::MAX - (*bucket_start - 1), PeerId::ZERO), |txn| { - (u64::MAX - txn.gas_unit_price(), txn.sender()) - }) { - Ok(index) => index, - Err(index) => index, - }; - if num_txns_in_bucket == 0 { - continue; - } - - self.push_bucket_to_batches( - &mut batches, - pulled_txns, - num_txns_in_bucket, - expiry_time, - *bucket_start, - &mut max_batches_remaining, - ); - } - if !pulled_txns.is_empty() && max_batches_remaining > 0 { - self.push_bucket_to_batches( - &mut batches, - pulled_txns, - pulled_txns.len(), - expiry_time, - 0, - &mut max_batches_remaining, - ); - } - batches - } - - fn insert_batch_in_progress( - &mut self, - batch_id: BatchId, - txns_in_progress: Vec<(TransactionSummary, TransactionInProgress)>, - ) { - let mut txns = vec![]; - for (summary, info) in txns_in_progress { - let txn_info = self - .txns_in_progress_sorted - .entry(summary) - .or_insert_with(|| TransactionInProgress::new(info.gas_unit_price)); - txn_info.increment(); - txn_info.gas_unit_price = info.gas_unit_price.max(txn_info.gas_unit_price); - txns.push(summary); - } - self.batches_in_progress.insert(batch_id, txns); - } - - fn remove_batch_in_progress(&mut self, batch_id: &BatchId) -> bool { - let removed = self.batches_in_progress.remove(batch_id); - match removed { - Some(txns) => { - for txn in txns { - if let Entry::Occupied(mut o) = self.txns_in_progress_sorted.entry(txn) { - let info = o.get_mut(); - if info.decrement() == 0 { - o.remove(); - } - } - } - true - }, - None => false, - } - } - - #[cfg(test)] - pub fn remove_batch_in_progress_for_test(&mut self, batch_id: &BatchId) -> bool { - self.remove_batch_in_progress(batch_id) - } - - #[cfg(test)] - pub fn txns_in_progress_sorted_len(&self) -> usize { - self.txns_in_progress_sorted.len() - } - - pub(crate) async fn handle_scheduled_pull(&mut self, max_count: u64) -> Vec { - counters::BATCH_PULL_EXCLUDED_TXNS.observe(self.txns_in_progress_sorted.len() as f64); - trace!( - "QS: excluding txs len: {:?}", - self.txns_in_progress_sorted.len() - ); - - let mut pulled_txns = self - .mempool_proxy - .pull_internal( - max_count, - self.config.sender_max_batch_bytes as u64, - self.txns_in_progress_sorted.clone(), - ) - .await - .unwrap_or_default(); - - trace!("QS: pulled_txns len: {:?}", pulled_txns.len()); - - if pulled_txns.is_empty() { - counters::PULLED_EMPTY_TXNS_COUNT.inc(); - // Quorum store metrics - counters::CREATED_EMPTY_BATCHES_COUNT.inc(); - - counters::EMPTY_BATCH_CREATION_DURATION - .observe_duration(self.last_end_batch_time.elapsed()); - self.last_end_batch_time = Instant::now(); - return vec![]; - } else { - counters::PULLED_TXNS_COUNT.inc(); - counters::PULLED_TXNS_NUM.observe(pulled_txns.len() as f64); - if pulled_txns.len() as u64 == max_count { - counters::BATCH_PULL_FULL_TXNS.observe(max_count as f64) - } - } - counters::BATCH_CREATION_DURATION.observe_duration(self.last_end_batch_time.elapsed()); - - let bucket_compute_start = Instant::now(); - let expiry_time = aptos_infallible::duration_since_epoch().as_micros() as u64 - + self.config.batch_expiry_gap_when_init_usecs; - let batches = self.bucket_into_batches(&mut pulled_txns, expiry_time); - self.last_end_batch_time = Instant::now(); - counters::BATCH_CREATION_COMPUTE_LATENCY.observe_duration(bucket_compute_start.elapsed()); - - batches - } - - pub async fn start( - mut self, - mut network_sender: NetworkSender, - mut cmd_rx: tokio::sync::mpsc::Receiver, - mut back_pressure_rx: tokio::sync::mpsc::Receiver, - mut interval: Interval, - ) { - let start = Instant::now(); - - let mut last_non_empty_pull = start; - let back_pressure_decrease_duration = - Duration::from_millis(self.config.back_pressure.decrease_duration_ms); - let back_pressure_increase_duration = - Duration::from_millis(self.config.back_pressure.increase_duration_ms); - let mut back_pressure_decrease_latest = start; - let mut back_pressure_increase_latest = start; - let mut dynamic_pull_txn_per_s = (self.config.back_pressure.dynamic_min_txn_per_s - + self.config.back_pressure.dynamic_max_txn_per_s) - / 2; - - loop { - let _timer = counters::BATCH_GENERATOR_MAIN_LOOP.start_timer(); - - tokio::select! { - Some(updated_back_pressure) = back_pressure_rx.recv() => { - self.back_pressure = updated_back_pressure; - }, - _ = interval.tick() => monitor!("batch_generator_handle_tick", { - - let tick_start = Instant::now(); - // TODO: refactor back_pressure logic into its own function - if self.back_pressure.txn_count { - // multiplicative decrease, every second - if back_pressure_decrease_latest.elapsed() >= back_pressure_decrease_duration { - back_pressure_decrease_latest = tick_start; - dynamic_pull_txn_per_s = std::cmp::max( - (dynamic_pull_txn_per_s as f64 * self.config.back_pressure.decrease_fraction) as u64, - self.config.back_pressure.dynamic_min_txn_per_s, - ); - trace!("QS: dynamic_max_pull_txn_per_s: {}", dynamic_pull_txn_per_s); - } - counters::QS_BACKPRESSURE_TXN_COUNT.observe(1.0); - counters::QS_BACKPRESSURE_DYNAMIC_MAX.observe(dynamic_pull_txn_per_s as f64); - } else { - // additive increase, every second - if back_pressure_increase_latest.elapsed() >= back_pressure_increase_duration { - back_pressure_increase_latest = tick_start; - dynamic_pull_txn_per_s = std::cmp::min( - dynamic_pull_txn_per_s + self.config.back_pressure.dynamic_min_txn_per_s, - self.config.back_pressure.dynamic_max_txn_per_s, - ); - trace!("QS: dynamic_max_pull_txn_per_s: {}", dynamic_pull_txn_per_s); - } - counters::QS_BACKPRESSURE_TXN_COUNT.observe(0.0); - counters::QS_BACKPRESSURE_DYNAMIC_MAX.observe(dynamic_pull_txn_per_s as f64); - } - if self.back_pressure.proof_count { - counters::QS_BACKPRESSURE_PROOF_COUNT.observe(1.0); - } else { - counters::QS_BACKPRESSURE_PROOF_COUNT.observe(0.0); - } - let since_last_non_empty_pull_ms = std::cmp::min( - tick_start.duration_since(last_non_empty_pull).as_millis(), - self.config.batch_generation_max_interval_ms as u128 - ) as usize; - if (!self.back_pressure.proof_count - && since_last_non_empty_pull_ms >= self.config.batch_generation_min_non_empty_interval_ms) - || since_last_non_empty_pull_ms == self.config.batch_generation_max_interval_ms { - - let dynamic_pull_max_txn = std::cmp::max( - (since_last_non_empty_pull_ms as f64 / 1000.0 * dynamic_pull_txn_per_s as f64) as u64, 1); - let pull_max_txn = std::cmp::min( - dynamic_pull_max_txn, - self.config.sender_max_total_txns as u64, - ); - let batches = self.handle_scheduled_pull(pull_max_txn).await; - if !batches.is_empty() { - last_non_empty_pull = tick_start; - - let persist_start = Instant::now(); - let mut persist_requests = vec![]; - for batch in batches.clone().into_iter() { - persist_requests.push(batch.into()); - } - self.batch_writer.persist(persist_requests); - counters::BATCH_CREATION_PERSIST_LATENCY.observe_duration(persist_start.elapsed()); - - network_sender.broadcast_batch_msg(batches).await; - } else if tick_start.elapsed() > interval.period().checked_div(2).unwrap_or(Duration::ZERO) { - // If the pull takes too long, it's also accounted as a non-empty pull to avoid pulling too often. - last_non_empty_pull = tick_start; - sample!( - SampleRate::Duration(Duration::from_secs(1)), - info!( - "QS: pull took a long time, {} ms", - tick_start.elapsed().as_millis() - ) - ); - } - } - }), - Some(cmd) = cmd_rx.recv() => monitor!("batch_generator_handle_command", { - match cmd { - BatchGeneratorCommand::CommitNotification(block_timestamp, batches) => { - trace!( - "QS: got clean request from execution, block timestamp {}", - block_timestamp - ); - assert!( - self.latest_block_timestamp <= block_timestamp, - "Decreasing block timestamp" - ); - self.latest_block_timestamp = block_timestamp; - - for batch_id in batches.iter().map(|b| b.batch_id()) { - if self.remove_batch_in_progress(&batch_id) { - counters::BATCH_IN_PROGRESS_COMMITTED.inc(); - } - } - - // Cleans up all batches that expire in timestamp <= block_timestamp. This is - // safe since clean request must occur only after execution result is certified. - for batch_id in self.batch_expirations.expire(block_timestamp) { - if self.remove_batch_in_progress(&batch_id) { - counters::BATCH_IN_PROGRESS_EXPIRED.inc(); - debug!( - "QS: logical time based expiration batch w. id {} from batches_in_progress, new size {}", - batch_id, - self.batches_in_progress.len(), - ); - } - } - }, - BatchGeneratorCommand::ProofExpiration(batch_ids) => { - for batch_id in batch_ids { - counters::BATCH_IN_PROGRESS_TIMEOUT.inc(); - debug!( - "QS: received timeout for proof of store, batch id = {}", - batch_id - ); - // Not able to gather the proof, allow transactions to be polled again. - self.remove_batch_in_progress(&batch_id); - } - } - BatchGeneratorCommand::Shutdown(ack_tx) => { - ack_tx - .send(()) - .expect("Failed to send shutdown ack"); - break; - }, - } - }) - } - } - } -} diff --git a/consensus/src/quorum_store/batch_requester.rs b/consensus/src/quorum_store/batch_requester.rs deleted file mode 100644 index 3e136c6f1efb1..0000000000000 --- a/consensus/src/quorum_store/batch_requester.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - monitor, - network::QuorumStoreSender, - quorum_store::{ - counters, - types::{BatchRequest, BatchResponse}, - }, -}; -use aptos_consensus_types::proof_of_store::{BatchInfo, ProofOfStore}; -use aptos_crypto::HashValue; -use aptos_executor_types::*; -use aptos_logger::prelude::*; -use aptos_types::{transaction::SignedTransaction, validator_verifier::ValidatorVerifier, PeerId}; -use futures::{stream::FuturesUnordered, StreamExt}; -use rand::Rng; -use std::{sync::Arc, time::Duration}; -use tokio::{sync::oneshot, time}; - -struct BatchRequesterState { - signers: Vec, - next_index: usize, - ret_tx: oneshot::Sender>>, - num_retries: usize, - retry_limit: usize, -} - -impl BatchRequesterState { - fn new( - signers: Vec, - ret_tx: oneshot::Sender>>, - retry_limit: usize, - ) -> Self { - Self { - signers, - next_index: 0, - ret_tx, - num_retries: 0, - retry_limit, - } - } - - fn next_request_peers(&mut self, num_peers: usize) -> Option> { - if self.num_retries == 0 { - let mut rng = rand::thread_rng(); - // make sure nodes request from the different set of nodes - self.next_index = rng.gen::() % self.signers.len(); - counters::SENT_BATCH_REQUEST_COUNT.inc_by(num_peers as u64); - } else { - counters::SENT_BATCH_REQUEST_RETRY_COUNT.inc_by(num_peers as u64); - } - if self.num_retries < self.retry_limit { - self.num_retries += 1; - let ret = self - .signers - .iter() - .cycle() - .skip(self.next_index) - .take(num_peers) - .cloned() - .collect(); - self.next_index = (self.next_index + num_peers) % self.signers.len(); - Some(ret) - } else { - None - } - } - - fn serve_request(self, digest: HashValue, maybe_payload: Option>) { - if let Some(payload) = maybe_payload { - trace!( - "QS: batch to oneshot, digest {}, tx {:?}", - digest, - self.ret_tx - ); - if self.ret_tx.send(Ok(payload)).is_err() { - debug!( - "Receiver of requested batch not available for digest {}", - digest - ) - }; - } else if self - .ret_tx - .send(Err(ExecutorError::CouldNotGetData)) - .is_err() - { - debug!( - "Receiver of requested batch not available for unavailable digest {}", - digest - ); - } - } -} - -pub(crate) struct BatchRequester { - epoch: u64, - my_peer_id: PeerId, - request_num_peers: usize, - retry_limit: usize, - retry_interval_ms: usize, - rpc_timeout_ms: usize, - network_sender: T, - validator_verifier: Arc, -} - -impl BatchRequester { - pub(crate) fn new( - epoch: u64, - my_peer_id: PeerId, - request_num_peers: usize, - retry_limit: usize, - retry_interval_ms: usize, - rpc_timeout_ms: usize, - network_sender: T, - validator_verifier: ValidatorVerifier, - ) -> Self { - Self { - epoch, - my_peer_id, - request_num_peers, - retry_limit, - retry_interval_ms, - rpc_timeout_ms, - network_sender, - validator_verifier: Arc::new(validator_verifier), - } - } - - pub(crate) async fn request_batch( - &self, - proof: ProofOfStore, - ret_tx: oneshot::Sender>>, - ) -> Option<(BatchInfo, Vec)> { - let digest = *proof.digest(); - let expiration = proof.expiration(); - let signers = proof.shuffled_signers(&self.validator_verifier); - let validator_verifier = self.validator_verifier.clone(); - let mut request_state = BatchRequesterState::new(signers, ret_tx, self.retry_limit); - let network_sender = self.network_sender.clone(); - let request_num_peers = self.request_num_peers; - let my_peer_id = self.my_peer_id; - let epoch = self.epoch; - let retry_interval = Duration::from_millis(self.retry_interval_ms as u64); - let rpc_timeout = Duration::from_millis(self.rpc_timeout_ms as u64); - - monitor!("batch_request", { - let mut interval = time::interval(retry_interval); - let mut futures = FuturesUnordered::new(); - let request = BatchRequest::new(my_peer_id, epoch, digest); - loop { - tokio::select! { - _ = interval.tick() => { - // send batch request to a set of peers of size request_num_peers - if let Some(request_peers) = request_state.next_request_peers(request_num_peers) { - for peer in request_peers { - futures.push(network_sender.request_batch(request.clone(), peer, rpc_timeout)); - } - } else if futures.is_empty() { - // end the loop when the futures are drained - break; - } - }, - Some(response) = futures.next() => { - match response { - Ok(BatchResponse::Batch(batch)) => { - counters::RECEIVED_BATCH_RESPONSE_COUNT.inc(); - let digest = *batch.digest(); - let batch_info = batch.batch_info().clone(); - let payload = batch.into_transactions(); - request_state.serve_request(digest, Some(payload.clone())); - return Some((batch_info, payload)); - } - // Short-circuit if the chain has moved beyond expiration - Ok(BatchResponse::NotFound(ledger_info)) => { - counters::RECEIVED_BATCH_NOT_FOUND_COUNT.inc(); - if ledger_info.commit_info().epoch() == epoch - && ledger_info.commit_info().timestamp_usecs() > expiration - && ledger_info.verify_signatures(&validator_verifier).is_ok() - { - counters::RECEIVED_BATCH_EXPIRED_COUNT.inc(); - debug!("QS: batch request expired, digest:{}", digest); - request_state.serve_request(digest, None); - return None; - } - } - Err(e) => { - counters::RECEIVED_BATCH_RESPONSE_ERROR_COUNT.inc(); - debug!("QS: batch request error, digest:{}, error:{:?}", digest, e); - } - } - }, - } - } - counters::RECEIVED_BATCH_REQUEST_TIMEOUT_COUNT.inc(); - debug!("QS: batch request timed out, digest:{}", digest); - request_state.serve_request(digest, None); - None - }) - } -} diff --git a/consensus/src/quorum_store/batch_store.rs b/consensus/src/quorum_store/batch_store.rs deleted file mode 100644 index e6a38aa2314b0..0000000000000 --- a/consensus/src/quorum_store/batch_store.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::QuorumStoreSender, - quorum_store::{ - batch_requester::BatchRequester, - counters, - quorum_store_db::QuorumStoreStorage, - types::{PersistedValue, StorageMode}, - utils::TimeExpirations, - }, -}; -use anyhow::bail; -use aptos_consensus_types::proof_of_store::{ProofOfStore, SignedBatchInfo}; -use aptos_crypto::HashValue; -use aptos_executor_types::{ExecutorError, ExecutorResult}; -use aptos_logger::prelude::*; -use aptos_types::{transaction::SignedTransaction, validator_signer::ValidatorSigner, PeerId}; -use dashmap::{ - mapref::entry::Entry::{Occupied, Vacant}, - DashMap, -}; -use fail::fail_point; -use once_cell::sync::OnceCell; -use std::{ - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, Mutex, - }, - time::Duration, -}; -use tokio::sync::oneshot; - -// Pub(crate) for testing only. -pub(crate) struct QuotaManager { - memory_balance: usize, - db_balance: usize, - batch_balance: usize, - // Recording the provided quotas for asserts. - memory_quota: usize, - db_quota: usize, - batch_quota: usize, -} - -impl QuotaManager { - pub(crate) fn new(db_quota: usize, memory_quota: usize, batch_quota: usize) -> Self { - assert!(db_quota >= memory_quota); - Self { - memory_balance: memory_quota, - db_balance: db_quota, - batch_balance: batch_quota, - memory_quota, - db_quota, - batch_quota, - } - } - - pub(crate) fn update_quota(&mut self, num_bytes: usize) -> anyhow::Result { - if self.batch_balance == 0 { - counters::EXCEEDED_BATCH_QUOTA_COUNT.inc(); - bail!("Batch quota exceeded "); - } - - if self.db_balance >= num_bytes { - self.batch_balance -= 1; - self.db_balance -= num_bytes; - - if self.memory_balance >= num_bytes { - self.memory_balance -= num_bytes; - Ok(StorageMode::MemoryAndPersisted) - } else { - Ok(StorageMode::PersistedOnly) - } - } else { - counters::EXCEEDED_STORAGE_QUOTA_COUNT.inc(); - bail!("Storage quota exceeded "); - } - } - - fn assert_quota(balance: usize, to_free: usize, quota: usize, kind: &str) { - assert!( - balance + to_free <= quota, - "Balance {} + to_free {} more than {} quota {}", - balance, - to_free, - kind, - quota, - ); - } - - pub(crate) fn free_quota(&mut self, num_bytes: usize, storage_mode: StorageMode) { - Self::assert_quota(self.batch_balance, 1, self.batch_quota, "Batch"); - self.batch_balance += 1; - - Self::assert_quota(self.db_balance, num_bytes, self.db_quota, "DB"); - self.db_balance += num_bytes; - - if matches!(storage_mode, StorageMode::MemoryAndPersisted) { - Self::assert_quota(self.memory_balance, num_bytes, self.memory_quota, "Memory"); - self.memory_balance += num_bytes; - } - } -} - -/// Provides in memory representation of stored batches (strong cache), and allows -/// efficient concurrent readers. -pub struct BatchStore { - epoch: OnceCell, - last_certified_time: AtomicU64, - db_cache: DashMap, - peer_quota: DashMap, - expirations: Mutex>, - db: Arc, - memory_quota: usize, - db_quota: usize, - batch_quota: usize, - validator_signer: ValidatorSigner, -} - -impl BatchStore { - pub(crate) fn new( - epoch: u64, - last_certified_time: u64, - db: Arc, - memory_quota: usize, - db_quota: usize, - batch_quota: usize, - validator_signer: ValidatorSigner, - ) -> Self { - let db_clone = db.clone(); - let batch_store = Self { - epoch: OnceCell::with_value(epoch), - last_certified_time: AtomicU64::new(last_certified_time), - db_cache: DashMap::new(), - peer_quota: DashMap::new(), - expirations: Mutex::new(TimeExpirations::new()), - db, - memory_quota, - db_quota, - batch_quota, - validator_signer, - }; - let db_content = db_clone - .get_all_batches() - .expect("failed to read data from db"); - let mut expired_keys = Vec::new(); - trace!( - "QS: Batchreader {} {} {}", - db_content.len(), - epoch, - last_certified_time - ); - for (digest, value) in db_content { - let expiration = value.expiration(); - - trace!( - "QS: Batchreader recovery content exp {:?}, digest {}", - expiration, - digest - ); - - if last_certified_time >= expiration { - expired_keys.push(digest); - } else { - batch_store - .insert_to_cache(&value) - .expect("Storage limit exceeded upon BatchReader construction"); - } - } - trace!( - "QS: Batchreader recovery expired keys len {}", - expired_keys.len() - ); - db_clone.delete_batches(expired_keys).unwrap(); - - batch_store - } - - fn epoch(&self) -> u64 { - *self.epoch.get().unwrap() - } - - fn free_quota(&self, value: PersistedValue) { - let mut quota_manager = self - .peer_quota - .get_mut(&value.author()) - .expect("No QuotaManager for batch author"); - quota_manager.free_quota(value.num_bytes() as usize, value.payload_storage_mode()); - } - - // Inserts a PersistedValue into the in-memory db_cache. If an entry with a higher - // value is already in the db_cache, Ok(false) is returned. If there was no entry - // Ok(true) is returned after the successful insertion. Finally, the method returns - // an error if storage quota is exceeded (if in-memory quota is exceeded, - // only the metadata is stored in the db-cache). - // Note: holds db_cache entry lock (due to DashMap), while accessing peer_quota - // DashMap. Hence, peer_quota reference should never be held while accessing the - // db_cache to avoid the deadlock (if needed, order is db_cache, then peer_quota). - pub(crate) fn insert_to_cache(&self, value: &PersistedValue) -> anyhow::Result { - let digest = *value.digest(); - let author = value.author(); - let expiration_time = value.expiration(); - - { - // Acquire dashmap internal lock on the entry corresponding to the digest. - let cache_entry = self.db_cache.entry(digest); - - if let Occupied(entry) = &cache_entry { - if entry.get().expiration() >= expiration_time { - debug!( - "QS: already have the digest with higher expiration {}", - digest - ); - return Ok(false); - } - }; - let value_to_be_stored = if self - .peer_quota - .entry(author) - .or_insert(QuotaManager::new( - self.db_quota, - self.memory_quota, - self.batch_quota, - )) - .update_quota(value.num_bytes() as usize)? - == StorageMode::PersistedOnly - { - PersistedValue::new(value.batch_info().clone(), None) - } else { - value.clone() - }; - - match cache_entry { - Occupied(entry) => { - let (k, prev_value) = entry.replace_entry(value_to_be_stored); - debug_assert!(k == digest); - self.free_quota(prev_value); - }, - Vacant(slot) => { - slot.insert(value_to_be_stored); - }, - } - } - - // Add expiration for the inserted entry, no need to be atomic w. insertion. - self.expirations - .lock() - .unwrap() - .add_item(digest, expiration_time); - Ok(true) - } - - pub(crate) fn save(&self, value: &PersistedValue) -> anyhow::Result { - let last_certified_time = self.last_certified_time(); - if value.expiration() > last_certified_time { - fail_point!("quorum_store::save", |_| { - // Skip caching and storing value to the db - Ok(false) - }); - counters::GAP_BETWEEN_BATCH_EXPIRATION_AND_CURRENT_TIME_WHEN_SAVE.observe( - Duration::from_micros(value.expiration() - last_certified_time).as_secs_f64(), - ); - - return self.insert_to_cache(value); - } - counters::NUM_BATCH_EXPIRED_WHEN_SAVE.inc(); - bail!( - "Incorrect expiration {} in epoch {}, last committed timestamp {}", - value.expiration(), - self.epoch(), - last_certified_time, - ); - } - - // pub(crate) for testing - pub(crate) fn clear_expired_payload(&self, certified_time: u64) -> Vec { - let expired_digests = self.expirations.lock().unwrap().expire(certified_time); - let mut ret = Vec::new(); - for h in expired_digests { - let removed_value = match self.db_cache.entry(h) { - Occupied(entry) => { - // We need to check up-to-date expiration again because receiving the same - // digest with a higher expiration would update the persisted value and - // effectively extend the expiration. - if entry.get().expiration() <= certified_time { - Some(entry.remove()) - } else { - None - } - }, - Vacant(_) => unreachable!("Expired entry not in cache"), - }; - // No longer holding the lock on db_cache entry. - if let Some(value) = removed_value { - self.free_quota(value); - ret.push(h); - } - } - ret - } - - fn persist_inner(&self, persist_request: PersistedValue) -> Option { - match self.save(&persist_request) { - Ok(needs_db) => { - let batch_info = persist_request.batch_info().clone(); - trace!("QS: sign digest {}", persist_request.digest()); - if needs_db { - self.db - .save_batch(persist_request) - .expect("Could not write to DB"); - } - SignedBatchInfo::new(batch_info, &self.validator_signer).ok() - }, - - Err(e) => { - debug!("QS: failed to store to cache {:?}", e); - None - }, - } - } - - pub fn update_certified_timestamp(&self, certified_time: u64) { - trace!("QS: batch reader updating time {:?}", certified_time); - let prev_time = self - .last_certified_time - .fetch_max(certified_time, Ordering::SeqCst); - // Note: prev_time may be equal to certified_time due to state-sync - // at the epoch boundary. - assert!( - prev_time <= certified_time, - "Decreasing executed block timestamp reported to BatchReader {} {}", - prev_time, - certified_time, - ); - - let expired_keys = self.clear_expired_payload(certified_time); - if let Err(e) = self.db.delete_batches(expired_keys) { - debug!("Error deleting batches: {:?}", e) - } - } - - fn last_certified_time(&self) -> u64 { - self.last_certified_time.load(Ordering::Relaxed) - } - - fn get_batch_from_db(&self, digest: &HashValue) -> ExecutorResult { - counters::GET_BATCH_FROM_DB_COUNT.inc(); - - match self.db.get_batch(digest) { - Ok(Some(value)) => Ok(value), - Ok(None) | Err(_) => { - warn!("Could not get batch from db"); - Err(ExecutorError::CouldNotGetData) - }, - } - } - - pub(crate) fn get_batch_from_local( - &self, - digest: &HashValue, - ) -> ExecutorResult { - if let Some(value) = self.db_cache.get(digest) { - if value.payload_storage_mode() == StorageMode::PersistedOnly { - self.get_batch_from_db(digest) - } else { - // Available in memory. - Ok(value.clone()) - } - } else { - Err(ExecutorError::CouldNotGetData) - } - } -} - -impl BatchWriter for BatchStore { - fn persist(&self, persist_requests: Vec) -> Vec { - let mut signed_infos = vec![]; - for persist_request in persist_requests.into_iter() { - if let Some(signed_info) = self.persist_inner(persist_request) { - signed_infos.push(signed_info); - } - } - signed_infos - } -} - -pub trait BatchReader: Send + Sync { - /// Check if the batch corresponding to the digest exists, return the batch author if true - fn exists(&self, digest: &HashValue) -> Option; - - fn get_batch( - &self, - proof: ProofOfStore, - ) -> oneshot::Receiver>>; - - fn update_certified_timestamp(&self, certified_time: u64); -} - -pub struct BatchReaderImpl { - batch_store: Arc, - batch_requester: Arc>, -} - -impl BatchReaderImpl { - pub(crate) fn new(batch_store: Arc, batch_requester: BatchRequester) -> Self { - Self { - batch_store, - batch_requester: Arc::new(batch_requester), - } - } -} - -impl BatchReader for BatchReaderImpl { - fn exists(&self, digest: &HashValue) -> Option { - self.batch_store - .get_batch_from_local(digest) - .map(|v| v.author()) - .ok() - } - - fn get_batch( - &self, - proof: ProofOfStore, - ) -> oneshot::Receiver>> { - let (tx, rx) = oneshot::channel(); - let batch_store = self.batch_store.clone(); - let batch_requester = self.batch_requester.clone(); - tokio::spawn(async move { - if let Ok(mut value) = batch_store.get_batch_from_local(proof.digest()) { - if tx - .send(Ok(value.take_payload().expect("Must have payload"))) - .is_err() - { - debug!( - "Receiver of local batch not available for digest {}", - proof.digest() - ) - }; - } else { - // Quorum store metrics - counters::MISSED_BATCHES_COUNT.inc(); - if let Some((batch_info, payload)) = batch_requester.request_batch(proof, tx).await - { - batch_store.persist(vec![PersistedValue::new(batch_info, Some(payload))]); - } - } - }); - rx - } - - fn update_certified_timestamp(&self, certified_time: u64) { - self.batch_store.update_certified_timestamp(certified_time); - } -} - -pub trait BatchWriter: Send + Sync { - fn persist(&self, persist_requests: Vec) -> Vec; -} diff --git a/consensus/src/quorum_store/counters.rs b/consensus/src/quorum_store/counters.rs deleted file mode 100644 index af22b17c92d93..0000000000000 --- a/consensus/src/quorum_store/counters.rs +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - exponential_buckets, op_counters::DurationHistogram, register_avg_counter, register_histogram, - register_histogram_vec, register_int_counter, register_int_counter_vec, Histogram, - HistogramVec, IntCounter, IntCounterVec, -}; -use once_cell::sync::Lazy; -use std::time::Duration; - -pub const GET_BATCH_LABEL: &str = "get_batch"; -pub const GET_BLOCK_RESPONSE_LABEL: &str = "get_block_response"; - -pub const REQUEST_FAIL_LABEL: &str = "fail"; -pub const REQUEST_SUCCESS_LABEL: &str = "success"; - -pub const CALLBACK_FAIL_LABEL: &str = "callback_fail"; -pub const CALLBACK_SUCCESS_LABEL: &str = "callback_success"; - -pub const POS_EXPIRED_LABEL: &str = "expired"; -pub const POS_DUPLICATE_LABEL: &str = "duplicate"; - -static TRANSACTION_COUNT_BUCKETS: Lazy> = Lazy::new(|| { - exponential_buckets( - /*start=*/ 1.5, /*factor=*/ 1.5, /*count=*/ 25, - ) - .unwrap() -}); - -static BYTE_BUCKETS: Lazy> = Lazy::new(|| { - exponential_buckets( - /*start=*/ 500.0, /*factor=*/ 1.5, /*count=*/ 25, - ) - .unwrap() -}); - -const INLINE_BATCH_COUNT_BUCKETS: &[f64] = &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]; - -// Histogram buckets that expand DEFAULT_BUCKETS with more granularity between 100-2000 ms -const QUORUM_STORE_LATENCY_BUCKETS: &[f64] = &[ - 0.005, 0.01, 0.025, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.65, 0.7, - 0.75, 1.0, 1.25, 1.5, 2.0, 2.5, 5.0, 10.0, -]; - -/// Counter for tracking latency of quorum store processing requests from consensus -/// A 'fail' result means the quorum store's callback response to consensus failed. -static QUORUM_STORE_SERVICE_LATENCY: Lazy = Lazy::new(|| { - register_histogram_vec!( - "quorum_store_service_latency_ms", - "Latency of quorum store processing request from consensus/state sync", - &["type", "result"] - ) - .unwrap() -}); - -pub fn quorum_store_service_latency(label: &'static str, result: &str, duration: Duration) { - QUORUM_STORE_SERVICE_LATENCY - .with_label_values(&[label, result]) - .observe(duration.as_secs_f64()); -} - -/// Duration of each run of the event loop. -pub static MAIN_LOOP: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_direct_mempool_main_loop", - "Duration of the each run of the event loop" - ) - .unwrap(), - ) -}); - -/// Duration of each run of the event loop. -pub static PROOF_MANAGER_MAIN_LOOP: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_proof_manager_main_loop", - "Duration of the each run of the proof manager event loop" - ) - .unwrap(), - ) -}); - -/// Duration of each run of the event loop. -pub static BATCH_GENERATOR_MAIN_LOOP: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_batch_generator_main_loop", - "Duration of the each run of the batch generator event loop" - ) - .unwrap(), - ) -}); - -////////////////////// -// NEW QUORUM STORE -////////////////////// - -/// Histograms - -/// Histogram for the number of batches per (committed) blocks. -pub static NUM_BATCH_PER_BLOCK: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_num_batch_per_block", - "Histogram for the number of batches per (committed) blocks.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -/// Histogram for the number of transactions per batch. -static NUM_TXN_PER_BATCH: Lazy = Lazy::new(|| { - register_histogram_vec!( - "quorum_store_num_txn_per_batch", - "Histogram for the number of transanctions per batch.", - &["bucket"], - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub fn num_txn_per_batch(bucket_start: &str, num: usize) { - NUM_TXN_PER_BATCH - .with_label_values(&[bucket_start]) - .observe(num as f64) -} - -/// Histogram for the number of transactions per block when pulled for consensus. -pub static BLOCK_SIZE_WHEN_PULL: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_block_size_when_pull", - "Histogram for the number of transactions per block when pulled for consensus.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub static NUM_INLINE_BATCHES: Lazy = Lazy::new(|| { - register_histogram!( - "num_inline_batches_in_block_proposal", - "Histogram for the number of inline batches in a block proposed by proof manager", - INLINE_BATCH_COUNT_BUCKETS.to_vec(), - ) - .unwrap() -}); - -pub static NUM_INLINE_TXNS: Lazy = Lazy::new(|| { - register_histogram!( - "num_inline_transactions_in_block_proposal", - "Histogram for the number of inline transactions in a block proposed by proof manager", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub static NUM_BATCHES_WITHOUT_PROOF_OF_STORE: Lazy = Lazy::new(|| { - register_histogram!( - "num_batches_without_proof_of_store", - "Histogram for the number of batches without proof of store in proof manager", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub static PROOF_QUEUE_FULLY_UTILIZED: Lazy = Lazy::new(|| { - register_histogram!( - "proof_queue_utilized_fully_in_proposal", - "Histogram for whether the proof queue is fully utilized when creating block proposal", - [0.0, 1.0].to_vec(), - ) - .unwrap() -}); - -/// Histogram for the total size of transactions per block when pulled for consensus. -pub static BLOCK_BYTES_WHEN_PULL: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_block_bytes_when_pull", - "Histogram for the total size of transactions per block when pulled for consensus.", - BYTE_BUCKETS.clone(), - ) - .unwrap() -}); - -/// Histogram for the number of proof-of-store per block when pulled for consensus. -pub static PROOF_SIZE_WHEN_PULL: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_proof_size_when_pull", - "Histogram for the number of proof-of-store per block when pulled for consensus.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub static EXCLUDED_TXNS_WHEN_PULL: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_excluded_txns_when_pull", - "Histogram for the number of transactions were considered but excluded when pulled for consensus.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -pub static BATCH_IN_PROGRESS_COMMITTED: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_batch_in_progress_committed", - "Number of batches that are removed from in progress by a commit." - ) - .unwrap() -}); - -pub static NUM_CORRUPT_BATCHES: Lazy = Lazy::new(|| { - register_int_counter!( - "corrupt_batches_in_proof_manager", - "Number of batches in proof manager for which the digest does not match" - ) - .unwrap() -}); - -pub static BATCH_IN_PROGRESS_EXPIRED: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_batch_in_progress_expired", - "Number of batches that are removed from in progress by a block timestamp expiration." - ) - .unwrap() -}); - -pub static BATCH_IN_PROGRESS_TIMEOUT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_batch_in_progress_timeout", - "Number of batches that are removed from in progress by a proof collection timeout." - ) - .unwrap() -}); - -pub static GAP_BETWEEN_BATCH_EXPIRATION_AND_CURRENT_TIME_WHEN_SAVE: Lazy = Lazy::new( - || { - register_histogram!( - "quorum_store_gap_batch_expiration_and_current_time_when_save", - "Histogram for the gaps between expiration round and the current round when saving proofs, and expiration time is lower.", - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap() - }, -); - -pub static NUM_BATCH_EXPIRED_WHEN_SAVE: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_num_batch_expired_when_save", - "Number of batches that were already expired when save is called" - ) - .unwrap() -}); - -/// Histogram for the gaps between expiration time and the current block timestamp on commit, and expiration round is lower. -pub static GAP_BETWEEN_BATCH_EXPIRATION_AND_CURRENT_TIME_WHEN_COMMIT: Lazy = Lazy::new( - || { - register_histogram!( - "quorum_store_gap_batch_expiration_and_current_time_when_commit", - "Histogram for the gaps between expiration time and the current block timestamp on commit, and expiration round is lower.", - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap() - }, -); - -pub static NUM_PROOFS_EXPIRED_WHEN_COMMIT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_num_proofs_expired_when_commit", - "Number of proofs that were expired when commit is called" - ) - .unwrap() -}); - -static POS_TO_PULL: Lazy = Lazy::new(|| { - register_histogram_vec!( - "quorum_store_pos_to_pull", - "Histogram for how long it took a PoS to go from inserted to pulled into a proposed block", - &["bucket"], - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap() -}); - -pub fn pos_to_pull(bucket: u64, secs: f64) { - POS_TO_PULL - .with_label_values(&[bucket.to_string().as_str()]) - .observe(secs) -} - -static POS_TO_COMMIT: Lazy = Lazy::new(|| { - register_histogram_vec!( - "quorum_store_pos_to_commit", - "Histogram for how long it took a PoS to go from inserted to commit notified", - &["bucket"], - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap() -}); - -pub fn pos_to_commit(bucket: u64, secs: f64) { - POS_TO_COMMIT - .with_label_values(&[bucket.to_string().as_str()]) - .observe(secs); -} - -/// Histogram for the number of total txns left after adding or cleaning batches. -pub static NUM_TOTAL_TXNS_LEFT_ON_UPDATE: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_num_total_txns_left_on_update", - "Histogram for the number of total txns left after adding or cleaning batches.", - ) -}); - -/// Histogram for the number of total batches/PoS left after adding or cleaning batches. -pub static NUM_TOTAL_PROOFS_LEFT_ON_UPDATE: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_num_total_proofs_left_on_update", - "Histogram for the number of total batches/PoS left after adding or cleaning batches.", - ) -}); - -/// Histogram for the number of local txns left after adding or cleaning batches. -pub static NUM_LOCAL_TXNS_LEFT_ON_UPDATE: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_num_local_txns_left_on_update", - "Histogram for the number of locally created txns left after adding or cleaning batches.", - ) -}); - -/// Histogram for the number of local batches/PoS left after adding or cleaning batches. -pub static NUM_LOCAL_PROOFS_LEFT_ON_UPDATE: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_num_local_proofs_left_on_update", - "Histogram for the number of locally created batches/PoS left after adding or cleaning batches.", - ) -}); - -/// Counters - -/// Count of how many times txns are pulled. -pub static PULLED_TXNS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!("quorum_store_pulled_txn_count", "Count of the pulled txns.").unwrap() -}); - -/// Histogram for the number of txns are pulled. -pub static PULLED_TXNS_NUM: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_pulled_txns_num", - "Histogram for the number of txns are pulled.", - TRANSACTION_COUNT_BUCKETS.clone() - ) - .unwrap() -}); - -/// Count of the pulled empty txns. -pub static PULLED_EMPTY_TXNS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_pulled_empty_txn_count", - "Count of the pulled empty txns." - ) - .unwrap() -}); - -/// Number of txns (equals max_count) for each time the pull for batches returns full. -pub static BATCH_PULL_FULL_TXNS: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_batch_pull_full_txns", - "Number of txns (equals max_count) for each time the pull for batches returns full.", - ) -}); - -/// Histogram for the number of txns excluded on pull for batches. -pub static BATCH_PULL_EXCLUDED_TXNS: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_batch_pull_excluded_txns", - "Histogram for the number of txns excluded on pull for batches.", - TRANSACTION_COUNT_BUCKETS.clone() - ) - .unwrap() -}); - -/// Count of the created batches since last restart. -pub static CREATED_BATCHES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_created_batch_count", - "Count of the created batches since last restart." - ) - .unwrap() -}); - -/// Count of the created empty batches since last restart. -pub static CREATED_EMPTY_BATCHES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_created_empty_batch_count", - "Count of the created empty batches since last restart." - ) - .unwrap() -}); - -/// Count of the created proof-of-store (PoS) since last restart. -static LOCAL_POS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "quorum_store_local_PoS_count", - "Count of the locally created PoS since last restart.", - &["bucket"] - ) - .unwrap() -}); - -pub fn inc_local_pos_count(bucket: u64) { - LOCAL_POS_COUNT - .with_label_values(&[bucket.to_string().as_str()]) - .inc() -} - -/// Count of the created proof-of-store (PoS) since last restart. -static REMOTE_POS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "quorum_store_remote_PoS_count", - "Count of the received PoS since last restart.", - &["bucket"] - ) - .unwrap() -}); - -pub fn inc_remote_pos_count(bucket: u64) { - REMOTE_POS_COUNT - .with_label_values(&[bucket.to_string().as_str()]) - .inc() -} - -static REJECTED_POS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "quorum_store_rejected_PoS_count", - "Count of the rejected PoS since last restart, grouped by reason.", - &["reason"] - ) - .unwrap() -}); - -pub fn inc_rejected_pos_count(reason: &str) { - REJECTED_POS_COUNT.with_label_values(&[reason]).inc(); -} - -/// Count of the received batches since last restart. -pub static RECEIVED_REMOTE_BATCH_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_remote_batch_count", - "Count of the received batches since last restart." - ) - .unwrap() -}); - -/// Count of the received batch msg since last restart. -pub static RECEIVED_BATCH_MSG_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_msg_count", - "Count of the received batch msg since last restart." - ) - .unwrap() -}); - -/// Count of the received batch since last restart. -pub static RECEIVED_BATCH_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_count", - "Count of the received end batch since last restart." - ) - .unwrap() -}); - -/// Count of the received batches that failed max limit check. -pub static RECEIVED_BATCH_MAX_LIMIT_FAILED: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_max_limit_failed", - "Count of the received batches that failed max limit check." - ) - .unwrap() -}); - -/// Count of the missed batches when execute. -pub static MISSED_BATCHES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_missed_batch_count", - "Count of the missed batches when execute." - ) - .unwrap() -}); - -/// Count of the timeout batches at the sender side. -pub static TIMEOUT_BATCHES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_timeout_batch_count", - "Count of the timeout batches at the sender side." - ) - .unwrap() -}); - -/// Count of the exceeded storage quota. -pub static EXCEEDED_STORAGE_QUOTA_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_exceeded_storage_quota_count", - "Count of the exceeded storage quota." - ) - .unwrap() -}); - -/// Count of the exceeded batch quota. -pub static EXCEEDED_BATCH_QUOTA_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_exceeded_batch_quota_count", - "Count of the exceeded batch quota." - ) - .unwrap() -}); - -/// Count of the number of batch request sent to other nodes. -pub static GET_BATCH_FROM_DB_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_get_batch_from_db_count", - "Count of the number of get batch request from QS DB." - ) - .unwrap() -}); - -/// Count of the number of batch request sent to other nodes. -pub static SENT_BATCH_REQUEST_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_sent_batch_request_count", - "Count of the number of batch request sent to other nodes." - ) - .unwrap() -}); - -/// Count of the number of batch request retry sent to other nodes. -pub static SENT_BATCH_REQUEST_RETRY_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_sent_batch_request_retry_count", - "Count of the number of batch request retry sent to other nodes." - ) - .unwrap() -}); - -/// Counters(queued,dequeued,dropped) related to batch retrieval per epoch task -pub static BATCH_RETRIEVAL_TASK_MSGS: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "aptos_quorum_store_batch_retrieval_task_msgs_count", - "Counters(queued,dequeued,dropped) related to batch retrieval task", - &["state"] - ) - .unwrap() -}); - -/// Count of the number of batch request received from other nodes. -pub static RECEIVED_BATCH_REQUEST_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_request_count", - "Count of the number of batch request received from other nodes." - ) - .unwrap() -}); - -/// Count of the number of batch request received from other nodes that is timeout. -pub static RECEIVED_BATCH_REQUEST_TIMEOUT_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_request_timeout_count", - "Count of the number of batch request received from other nodes that is timeout." - ) - .unwrap() -}); - -/// Count of the number of batches received from other nodes. -pub static RECEIVED_BATCH_RESPONSE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_response_count", - "Count of the number of batches received from other nodes." - ) - .unwrap() -}); - -/// Count of the number of batch not found responses received from other nodes. -pub static RECEIVED_BATCH_NOT_FOUND_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_not_found_count", - "Count of the number of batch not found responses received from other nodes." - ) - .unwrap() -}); - -/// Count of the number of batch expired responses received from other nodes. -pub static RECEIVED_BATCH_EXPIRED_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_expired_count", - "Count of the number of batch expired responses received from other nodes." - ) - .unwrap() -}); - -/// Count of the number of error batches received from other nodes. -pub static RECEIVED_BATCH_RESPONSE_ERROR_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "quorum_store_received_batch_response_error_count", - "Count of the number of error batches received from other nodes." - ) - .unwrap() -}); - -pub static QS_BACKPRESSURE_TXN_COUNT: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_backpressure_txn_count", - "Indicator of whether Quorum Store is backpressured due to txn count exceeding threshold.", - ) -}); - -pub static QS_BACKPRESSURE_PROOF_COUNT: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_backpressure_proof_count", - "Indicator of whether Quorum Store is backpressured due to proof count exceeding threshold." - ) -}); - -pub static QS_BACKPRESSURE_DYNAMIC_MAX: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_backpressure_dynamic_max", - "What the dynamic max is set to", - ) -}); - -/// Latencies - -/// Histogram of the time durations for batch creation. -pub static BATCH_CREATION_DURATION: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_batch_creation_duration", - "Histogram of the time durations for batch creation.", - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap(), - ) -}); - -/// Histogram of the time durations for empty batch creation. -pub static EMPTY_BATCH_CREATION_DURATION: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_empty_batch_creation_duration", - "Histogram of the time durations for empty batch creation.", - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap(), - ) -}); - -/// Histogram of the time it takes to compute bucketed batches after txns are pulled from mempool. -pub static BATCH_CREATION_COMPUTE_LATENCY: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_batch_creation_compute_latency", - "Histogram of the time it takes to compute bucketed batches after txns are pulled from mempool.", - ) - .unwrap(), - ) -}); - -/// Histogram of the time it takes to persist batches generated locally to the DB. -pub static BATCH_CREATION_PERSIST_LATENCY: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_batch_creation_persist_latency", - "Histogram of the time it takes to persist batches generated locally to the DB.", - ) - .unwrap(), - ) -}); - -/// Histogram of the time durations from created batch to created PoS. -pub static BATCH_TO_POS_DURATION: Lazy = Lazy::new(|| { - DurationHistogram::new( - register_histogram!( - "quorum_store_batch_to_PoS_duration", - "Histogram of the time durations from batch creation to PoS creation.", - QUORUM_STORE_LATENCY_BUCKETS.to_vec() - ) - .unwrap(), - ) -}); - -pub static BATCH_SUCCESSFUL_CREATION: Lazy = Lazy::new(|| { - register_avg_counter( - "quorum_store_batch_successful_creation", - "Counter for whether we are successfully creating batches", - ) -}); - -/// Number of validators for which we received signed replies -pub static BATCH_RECEIVED_REPLIES_COUNT: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_batch_received_replies_votes", - "Number of validators for which we received signed replies.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); - -/// Voting power of validators for which we received signed replies -pub static BATCH_RECEIVED_REPLIES_VOTING_POWER: Lazy = Lazy::new(|| { - register_histogram!( - "quorum_store_batch_received_replies_voting_power", - "Voting power of validators for which we received signed replies.", - TRANSACTION_COUNT_BUCKETS.clone(), - ) - .unwrap() -}); diff --git a/consensus/src/quorum_store/direct_mempool_quorum_store.rs b/consensus/src/quorum_store/direct_mempool_quorum_store.rs deleted file mode 100644 index bc887ab430901..0000000000000 --- a/consensus/src/quorum_store/direct_mempool_quorum_store.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{monitor, quorum_store::counters}; -use anyhow::Result; -use aptos_consensus_types::{ - common::{Payload, PayloadFilter, TransactionInProgress, TransactionSummary}, - request_response::{GetPayloadCommand, GetPayloadResponse}, -}; -use aptos_logger::prelude::*; -use aptos_mempool::{QuorumStoreRequest, QuorumStoreResponse}; -use aptos_types::transaction::SignedTransaction; -use futures::{ - channel::{ - mpsc::{Receiver, Sender}, - oneshot, - }, - StreamExt, -}; -use std::{ - collections::BTreeMap, - time::{Duration, Instant}, -}; -use tokio::time::timeout; - -pub struct DirectMempoolQuorumStore { - consensus_receiver: Receiver, - mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, -} - -impl DirectMempoolQuorumStore { - pub fn new( - consensus_receiver: Receiver, - mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, - ) -> Self { - Self { - consensus_receiver, - mempool_sender, - mempool_txn_pull_timeout_ms, - } - } - - async fn pull_internal( - &self, - max_items: u64, - max_bytes: u64, - return_non_full: bool, - exclude_txns: Vec, - ) -> Result, anyhow::Error> { - let (callback, callback_rcv) = oneshot::channel(); - let exclude_txns: BTreeMap<_, _> = exclude_txns - .into_iter() - .map(|txn| (txn, TransactionInProgress::new(0))) - .collect(); - let msg = QuorumStoreRequest::GetBatchRequest( - max_items, - max_bytes, - return_non_full, - false, - exclude_txns, - callback, - ); - self.mempool_sender - .clone() - .try_send(msg) - .map_err(anyhow::Error::from)?; - // wait for response - match monitor!( - "pull_txn", - timeout( - Duration::from_millis(self.mempool_txn_pull_timeout_ms), - callback_rcv - ) - .await - ) { - Err(_) => Err(anyhow::anyhow!( - "[direct_mempool_quorum_store] did not receive GetBatchResponse on time" - )), - Ok(resp) => match resp.map_err(anyhow::Error::from)?? { - QuorumStoreResponse::GetBatchResponse(txns) => Ok(txns), - _ => Err(anyhow::anyhow!( - "[direct_mempool_quorum_store] did not receive expected GetBatchResponse" - )), - }, - } - } - - async fn handle_block_request( - &self, - max_txns: u64, - max_bytes: u64, - return_non_full: bool, - payload_filter: PayloadFilter, - callback: oneshot::Sender>, - ) { - let get_batch_start_time = Instant::now(); - let exclude_txns = match payload_filter { - PayloadFilter::DirectMempool(exclude_txns) => exclude_txns, - PayloadFilter::InQuorumStore(_) => { - unreachable!("Unknown payload_filter: {}", payload_filter) - }, - PayloadFilter::Empty => Vec::new(), - }; - - let (txns, result) = match self - .pull_internal(max_txns, max_bytes, return_non_full, exclude_txns) - .await - { - Err(_) => { - error!("GetBatch failed"); - (vec![], counters::REQUEST_FAIL_LABEL) - }, - Ok(txns) => (txns, counters::REQUEST_SUCCESS_LABEL), - }; - counters::quorum_store_service_latency( - counters::GET_BATCH_LABEL, - result, - get_batch_start_time.elapsed(), - ); - - let get_block_response_start_time = Instant::now(); - let payload = Payload::DirectMempool(txns); - let result = match callback.send(Ok(GetPayloadResponse::GetPayloadResponse(payload))) { - Err(_) => { - error!("Callback failed"); - counters::CALLBACK_FAIL_LABEL - }, - Ok(_) => counters::CALLBACK_SUCCESS_LABEL, - }; - counters::quorum_store_service_latency( - counters::GET_BLOCK_RESPONSE_LABEL, - result, - get_block_response_start_time.elapsed(), - ); - } - - async fn handle_consensus_request(&self, req: GetPayloadCommand) { - match req { - GetPayloadCommand::GetPayloadRequest( - max_txns, - max_bytes, - _max_inline_txns, - _max_inline_bytes, - return_non_full, - payload_filter, - callback, - ) => { - self.handle_block_request( - max_txns, - max_bytes, - return_non_full, - payload_filter, - callback, - ) - .await; - }, - } - } - - pub async fn start(mut self) { - loop { - let _timer = counters::MAIN_LOOP.start_timer(); - ::futures::select! { - msg = self.consensus_receiver.select_next_some() => { - self.handle_consensus_request(msg).await; - }, - complete => break, - } - } - } -} diff --git a/consensus/src/quorum_store/mod.rs b/consensus/src/quorum_store/mod.rs deleted file mode 100644 index 888b62b0122c2..0000000000000 --- a/consensus/src/quorum_store/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod counters; -/// Equivalent to directly fetching blocks from mempool without a quorum store. -pub mod direct_mempool_quorum_store; - -pub(crate) mod batch_coordinator; -pub(crate) mod batch_generator; -pub(crate) mod batch_requester; -pub(crate) mod batch_store; -pub(crate) mod network_listener; -pub(crate) mod proof_coordinator; -pub(crate) mod proof_manager; -pub(crate) mod quorum_store_builder; -pub(crate) mod quorum_store_coordinator; -pub mod quorum_store_db; -pub mod types; -pub(crate) mod utils; - -mod schema; -#[cfg(test)] -mod tests; diff --git a/consensus/src/quorum_store/network_listener.rs b/consensus/src/quorum_store/network_listener.rs deleted file mode 100644 index 1d31ea808b1a0..0000000000000 --- a/consensus/src/quorum_store/network_listener.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - monitor, - quorum_store::{ - batch_coordinator::BatchCoordinatorCommand, counters, - proof_coordinator::ProofCoordinatorCommand, proof_manager::ProofManagerCommand, - }, - round_manager::VerifiedEvent, -}; -use aptos_channels::aptos_channel; -use aptos_logger::prelude::*; -use aptos_types::PeerId; -use futures::StreamExt; -use tokio::sync::mpsc::Sender; - -pub(crate) struct NetworkListener { - network_msg_rx: aptos_channel::Receiver, - proof_coordinator_tx: Sender, - remote_batch_coordinator_tx: Vec>, - proof_manager_tx: Sender, -} - -impl NetworkListener { - pub(crate) fn new( - network_msg_rx: aptos_channel::Receiver, - proof_coordinator_tx: Sender, - remote_batch_coordinator_tx: Vec>, - proof_manager_tx: Sender, - ) -> Self { - Self { - network_msg_rx, - proof_coordinator_tx, - remote_batch_coordinator_tx, - proof_manager_tx, - } - } - - pub async fn start(mut self) { - info!("QS: starting networking"); - while let Some(msg) = self.network_msg_rx.next().await { - monitor!("qs_network_listener_main_loop", { - match msg { - // TODO: does the assumption have to be that network listener is shutdown first? - VerifiedEvent::Shutdown(ack_tx) => { - info!("QS: shutdown network listener received"); - ack_tx - .send(()) - .expect("Failed to send shutdown ack to QuorumStore"); - break; - }, - VerifiedEvent::SignedBatchInfo(signed_batch_infos) => { - let cmd = ProofCoordinatorCommand::AppendSignature(*signed_batch_infos); - self.proof_coordinator_tx - .send(cmd) - .await - .expect("Could not send signed_batch_info to proof_coordinator"); - }, - VerifiedEvent::BatchMsg(batch_msg) => { - let author = batch_msg.author(); - let batches = batch_msg.take(); - counters::RECEIVED_BATCH_MSG_COUNT.inc(); - - let idx = - author.to_vec()[0] as usize % self.remote_batch_coordinator_tx.len(); - trace!( - "QS: peer_id {:?}, # network_worker {}, hashed to idx {}", - author, - self.remote_batch_coordinator_tx.len(), - idx - ); - self.remote_batch_coordinator_tx[idx] - .send(BatchCoordinatorCommand::NewBatches(author, batches)) - .await - .expect("Could not send remote batch"); - }, - VerifiedEvent::ProofOfStoreMsg(proofs) => { - let cmd = ProofManagerCommand::ReceiveProofs(*proofs); - self.proof_manager_tx - .send(cmd) - .await - .expect("could not push Proof proof_of_store"); - }, - _ => { - unreachable!() - }, - }; - }); - } - } -} diff --git a/consensus/src/quorum_store/proof_coordinator.rs b/consensus/src/quorum_store/proof_coordinator.rs deleted file mode 100644 index d0eab1129d3c6..0000000000000 --- a/consensus/src/quorum_store/proof_coordinator.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - logging::{LogEvent, LogSchema}, - monitor, - network::QuorumStoreSender, - quorum_store::{ - batch_generator::BatchGeneratorCommand, batch_store::BatchReader, counters, utils::Timeouts, - }, -}; -use aptos_consensus_types::proof_of_store::{ - BatchInfo, ProofOfStore, SignedBatchInfo, SignedBatchInfoError, SignedBatchInfoMsg, -}; -use aptos_crypto::{bls12381, HashValue}; -use aptos_logger::prelude::*; -use aptos_types::{ - aggregate_signature::PartialSignatures, validator_verifier::ValidatorVerifier, PeerId, -}; -use std::{ - collections::{hash_map::Entry, BTreeMap, HashMap}, - sync::Arc, - time::Duration, -}; -use tokio::{ - sync::{mpsc::Receiver, oneshot as TokioOneshot}, - time, -}; - -#[derive(Debug)] -pub(crate) enum ProofCoordinatorCommand { - AppendSignature(SignedBatchInfoMsg), - CommitNotification(Vec), - Shutdown(TokioOneshot::Sender<()>), -} - -struct IncrementalProofState { - info: BatchInfo, - aggregated_signature: BTreeMap, - aggregated_voting_power: u128, - self_voted: bool, - completed: bool, -} - -impl IncrementalProofState { - fn new(info: BatchInfo) -> Self { - Self { - info, - aggregated_signature: BTreeMap::new(), - aggregated_voting_power: 0, - self_voted: false, - completed: false, - } - } - - fn add_signature( - &mut self, - signed_batch_info: SignedBatchInfo, - validator_verifier: &ValidatorVerifier, - ) -> Result<(), SignedBatchInfoError> { - if signed_batch_info.batch_info() != &self.info { - return Err(SignedBatchInfoError::WrongInfo(( - signed_batch_info.batch_id().id, - self.info.batch_id().id, - ))); - } - - if self - .aggregated_signature - .contains_key(&signed_batch_info.signer()) - { - return Err(SignedBatchInfoError::DuplicatedSignature); - } - - match validator_verifier.get_voting_power(&signed_batch_info.signer()) { - Some(voting_power) => { - let signer = signed_batch_info.signer(); - if self - .aggregated_signature - .insert(signer, signed_batch_info.signature()) - .is_none() - { - self.aggregated_voting_power += voting_power as u128; - if signer == self.info.author() { - self.self_voted = true; - } - } else { - error!( - "Author already in aggregated_signatures right after rechecking: {}", - signer - ); - } - }, - None => { - error!( - "Received signature from author not in validator set: {}", - signed_batch_info.signer() - ); - return Err(SignedBatchInfoError::InvalidAuthor); - }, - } - - Ok(()) - } - - fn ready(&self, validator_verifier: &ValidatorVerifier) -> bool { - if self.aggregated_voting_power >= validator_verifier.quorum_voting_power() { - let recheck = - validator_verifier.check_voting_power(self.aggregated_signature.keys(), true); - if recheck.is_err() { - error!("Unexpected discrepancy: aggregated_voting_power is {}, while rechecking we get {:?}", self.aggregated_voting_power, recheck); - } - recheck.is_ok() - } else { - false - } - } - - fn take(&mut self, validator_verifier: &ValidatorVerifier) -> ProofOfStore { - if self.completed { - panic!("Cannot call take twice, unexpected issue occurred"); - } - self.completed = true; - - match validator_verifier - .aggregate_signatures(&PartialSignatures::new(self.aggregated_signature.clone())) - { - Ok(sig) => ProofOfStore::new(self.info.clone(), sig), - Err(e) => unreachable!("Cannot aggregate signatures on digest err = {:?}", e), - } - } - - fn batch_info(&self) -> &BatchInfo { - &self.info - } -} - -pub(crate) struct ProofCoordinator { - peer_id: PeerId, - proof_timeout_ms: usize, - digest_to_proof: HashMap, - digest_to_time: HashMap, - // to record the batch creation time - timeouts: Timeouts, - committed_batches: HashMap, - batch_reader: Arc, - batch_generator_cmd_tx: tokio::sync::mpsc::Sender, - broadcast_proofs: bool, -} - -//PoQS builder object - gather signed digest to form PoQS -impl ProofCoordinator { - pub fn new( - proof_timeout_ms: usize, - peer_id: PeerId, - batch_reader: Arc, - batch_generator_cmd_tx: tokio::sync::mpsc::Sender, - broadcast_proofs: bool, - ) -> Self { - Self { - peer_id, - proof_timeout_ms, - digest_to_proof: HashMap::new(), - digest_to_time: HashMap::new(), - timeouts: Timeouts::new(), - committed_batches: HashMap::new(), - batch_reader, - batch_generator_cmd_tx, - broadcast_proofs, - } - } - - fn init_proof( - &mut self, - signed_batch_info: &SignedBatchInfo, - ) -> Result<(), SignedBatchInfoError> { - // Check if the signed digest corresponding to our batch - if signed_batch_info.author() != self.peer_id { - return Err(SignedBatchInfoError::WrongAuthor); - } - let batch_author = self - .batch_reader - .exists(signed_batch_info.digest()) - .ok_or(SignedBatchInfoError::NotFound)?; - if batch_author != signed_batch_info.author() { - return Err(SignedBatchInfoError::WrongAuthor); - } - if self - .committed_batches - .get(signed_batch_info.batch_info()) - .is_some() - { - return Err(SignedBatchInfoError::AlreadyCommitted); - } - - self.timeouts.add( - signed_batch_info.batch_info().clone(), - self.proof_timeout_ms, - ); - self.digest_to_proof.insert( - *signed_batch_info.digest(), - IncrementalProofState::new(signed_batch_info.batch_info().clone()), - ); - self.digest_to_time - .entry(*signed_batch_info.digest()) - .or_insert(chrono::Utc::now().naive_utc().timestamp_micros() as u64); - debug!( - LogSchema::new(LogEvent::ProofOfStoreInit), - digest = signed_batch_info.digest(), - batch_id = signed_batch_info.batch_id().id, - ); - Ok(()) - } - - fn add_signature( - &mut self, - signed_batch_info: SignedBatchInfo, - validator_verifier: &ValidatorVerifier, - ) -> Result, SignedBatchInfoError> { - if !self - .digest_to_proof - .contains_key(signed_batch_info.digest()) - { - self.init_proof(&signed_batch_info)?; - } - let digest = *signed_batch_info.digest(); - if let Some(value) = self.digest_to_proof.get_mut(signed_batch_info.digest()) { - value.add_signature(signed_batch_info, validator_verifier)?; - if !value.completed && value.ready(validator_verifier) { - let proof = value.take(validator_verifier); - // quorum store measurements - let duration = chrono::Utc::now().naive_utc().timestamp_micros() as u64 - - self - .digest_to_time - .remove(&digest) - .expect("Batch created without recording the time!"); - counters::BATCH_TO_POS_DURATION.observe_duration(Duration::from_micros(duration)); - return Ok(Some(proof)); - } - } else if let Some(value) = self - .committed_batches - .get_mut(signed_batch_info.batch_info()) - { - value.add_signature(signed_batch_info, validator_verifier)?; - } else { - return Err(SignedBatchInfoError::NotFound); - } - Ok(None) - } - - fn update_counters_on_expire(state: &IncrementalProofState) { - counters::BATCH_RECEIVED_REPLIES_COUNT.observe(state.aggregated_signature.len() as f64); - counters::BATCH_RECEIVED_REPLIES_VOTING_POWER.observe(state.aggregated_voting_power as f64); - if !state.completed { - counters::BATCH_SUCCESSFUL_CREATION.observe(0.0); - } - } - - async fn expire(&mut self) { - let mut batch_ids = vec![]; - for signed_batch_info_info in self.timeouts.expire() { - if let Some(state) = self.digest_to_proof.remove(signed_batch_info_info.digest()) { - if !state.completed { - batch_ids.push(signed_batch_info_info.batch_id()); - } - - // We skip metrics if the proof did not complete and did not get a self vote, as it - // is considered a proof that was re-inited due to a very late vote. - if !state.completed && !state.self_voted { - continue; - } - - if !state.completed { - counters::TIMEOUT_BATCHES_COUNT.inc(); - info!( - LogSchema::new(LogEvent::IncrementalProofExpired), - digest = signed_batch_info_info.digest(), - self_voted = state.self_voted, - ); - } - Self::update_counters_on_expire(&state); - } else if let Some(state) = self.committed_batches.remove(&signed_batch_info_info) { - Self::update_counters_on_expire(&state); - } - } - if self - .batch_generator_cmd_tx - .send(BatchGeneratorCommand::ProofExpiration(batch_ids)) - .await - .is_err() - { - warn!("Failed to send proof expiration to batch generator"); - } - } - - pub async fn start( - mut self, - mut rx: Receiver, - mut network_sender: impl QuorumStoreSender, - validator_verifier: ValidatorVerifier, - ) { - let mut interval = time::interval(Duration::from_millis(100)); - loop { - tokio::select! { - Some(command) = rx.recv() => monitor!("proof_coordinator_handle_command", { - match command { - ProofCoordinatorCommand::Shutdown(ack_tx) => { - ack_tx - .send(()) - .expect("Failed to send shutdown ack to QuorumStore"); - break; - }, - ProofCoordinatorCommand::CommitNotification(batches) => { - for batch in batches { - let digest = batch.digest(); - if let Entry::Occupied(existing_proof) = self.digest_to_proof.entry(*digest) { - if batch == *existing_proof.get().batch_info() { - let incremental_proof = existing_proof.get(); - if incremental_proof.completed { - counters::BATCH_SUCCESSFUL_CREATION.observe(1.0); - } else { - warn!("QS: received commit notification for batch that did not complete: {}, self_voted: {}", digest, incremental_proof.self_voted); - } - let committed_proof = existing_proof.remove(); - self.committed_batches.insert(batch, committed_proof); - } - } - } - }, - ProofCoordinatorCommand::AppendSignature(signed_batch_infos) => { - let mut proofs = vec![]; - for signed_batch_info in signed_batch_infos.take().into_iter() { - let peer_id = signed_batch_info.signer(); - let digest = *signed_batch_info.digest(); - let batch_id = signed_batch_info.batch_id(); - match self.add_signature(signed_batch_info, &validator_verifier) { - Ok(result) => { - if let Some(proof) = result { - debug!( - LogSchema::new(LogEvent::ProofOfStoreReady), - digest = digest, - batch_id = batch_id.id, - ); - proofs.push(proof); - } - }, - Err(e) => { - // Can happen if we already garbage collected, the commit notification is late, or the peer is misbehaving. - if peer_id == self.peer_id { - info!("QS: could not add signature from self, digest = {}, batch_id = {}, err = {:?}", digest, batch_id, e); - } else { - debug!("QS: could not add signature from peer {}, digest = {}, batch_id = {}, err = {:?}", peer_id, digest, batch_id, e); - } - }, - } - } - if !proofs.is_empty() { - if self.broadcast_proofs { - network_sender.broadcast_proof_of_store_msg(proofs).await; - } else { - network_sender.send_proof_of_store_msg_to_self(proofs).await; - } - } - }, - } - }), - _ = interval.tick() => { - monitor!("proof_coordinator_handle_tick", self.expire().await); - } - } - } - } -} diff --git a/consensus/src/quorum_store/proof_manager.rs b/consensus/src/quorum_store/proof_manager.rs deleted file mode 100644 index 3cdac9a768ad3..0000000000000 --- a/consensus/src/quorum_store/proof_manager.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::batch_store::BatchStore; -use crate::{ - monitor, - quorum_store::{ - batch_generator::BackPressure, - counters, - utils::{BatchSortKey, ProofQueue}, - }, -}; -use aptos_consensus_types::{ - common::{Payload, PayloadFilter, ProofWithData}, - proof_of_store::{BatchInfo, ProofOfStore, ProofOfStoreMsg}, - request_response::{GetPayloadCommand, GetPayloadResponse}, -}; -use aptos_logger::prelude::*; -use aptos_types::{transaction::SignedTransaction, PeerId}; -use futures::StreamExt; -use futures_channel::mpsc::Receiver; -use rand::{seq::SliceRandom, thread_rng}; -use std::{ - cmp::min, - collections::{BTreeMap, HashMap, HashSet}, - sync::Arc, -}; - -#[derive(Debug)] -pub enum ProofManagerCommand { - ReceiveProofs(ProofOfStoreMsg), - ReceiveBatches(Vec), - CommitNotification(u64, Vec), - Shutdown(tokio::sync::oneshot::Sender<()>), -} - -pub struct BatchQueue { - batch_store: Arc, - // Queue per peer to ensure fairness between peers and priority within peer - author_to_batches: HashMap>, -} - -impl BatchQueue { - pub fn new(batch_store: Arc) -> Self { - Self { - batch_store, - author_to_batches: HashMap::new(), - } - } - - pub fn add_batches(&mut self, batches: Vec) { - for batch in batches.into_iter() { - let queue = self.author_to_batches.entry(batch.author()).or_default(); - queue.insert(BatchSortKey::from_info(&batch), batch.clone()); - } - } - - pub fn remove_batch(&mut self, batch: &BatchInfo) { - if let Some(batch_tree) = self.author_to_batches.get_mut(&batch.author()) { - batch_tree.remove(&BatchSortKey::from_info(batch)); - } - } - - pub fn remove_expired_batches(&mut self) { - let authors = self.author_to_batches.keys().cloned().collect::>(); - for author in authors { - if let Some(batch_tree) = self.author_to_batches.get_mut(&author) { - batch_tree.retain(|_batch_key, batch| !batch.is_expired()); - } - } - } - - pub fn len(&self) -> usize { - self.author_to_batches - .values() - .map(|batch_tree| batch_tree.len()) - .sum() - } - - pub fn pull_batches( - &mut self, - max_txns: u64, - max_bytes: u64, - excluded_batches: Vec, - ) -> Vec<(BatchInfo, Vec)> { - let mut result: Vec<(BatchInfo, Vec)> = vec![]; - let mut num_txns = 0; - let mut num_bytes = 0; - let mut iters = vec![]; - let mut full = false; - for (_, batches) in self.author_to_batches.iter() { - iters.push(batches.iter().rev()); - } - while !iters.is_empty() { - iters.shuffle(&mut thread_rng()); - iters.retain_mut(|iter| { - if full { - return false; - } - if let Some((_sort_key, batch)) = iter.next() { - if excluded_batches.contains(batch) { - true - } else if num_txns + batch.num_txns() <= max_txns - && num_bytes + batch.num_bytes() <= max_bytes - { - if let Ok(mut persisted_value) = - self.batch_store.get_batch_from_local(batch.digest()) - { - if let Some(txns) = persisted_value.take_payload() { - num_txns += batch.num_txns(); - num_bytes += batch.num_bytes(); - result.push((batch.clone(), txns.clone())); - } - } else { - warn!("Couldn't find a batch in local storage while creating inline block: {:?}", batch.digest()); - } - true - } else { - full = true; - false - } - } else { - false - } - }) - } - result - } -} - -pub struct ProofManager { - proofs_for_consensus: ProofQueue, - batch_queue: BatchQueue, - back_pressure_total_txn_limit: u64, - remaining_total_txn_num: u64, - back_pressure_total_proof_limit: u64, - remaining_total_proof_num: u64, - allow_batches_without_pos_in_proposal: bool, -} - -impl ProofManager { - pub fn new( - my_peer_id: PeerId, - back_pressure_total_txn_limit: u64, - back_pressure_total_proof_limit: u64, - batch_store: Arc, - allow_batches_without_pos_in_proposal: bool, - ) -> Self { - Self { - proofs_for_consensus: ProofQueue::new(my_peer_id), - batch_queue: BatchQueue::new(batch_store), - back_pressure_total_txn_limit, - remaining_total_txn_num: 0, - back_pressure_total_proof_limit, - remaining_total_proof_num: 0, - allow_batches_without_pos_in_proposal, - } - } - - pub(crate) fn receive_proofs(&mut self, proofs: Vec) { - for proof in proofs.into_iter() { - self.batch_queue.remove_batch(proof.info()); - self.proofs_for_consensus.push(proof); - } - (self.remaining_total_txn_num, self.remaining_total_proof_num) = - self.proofs_for_consensus.remaining_txns_and_proofs(); - } - - pub(crate) fn receive_batches(&mut self, batches: Vec) { - if self.allow_batches_without_pos_in_proposal { - self.batch_queue.add_batches(batches); - } - } - - pub(crate) fn handle_commit_notification( - &mut self, - block_timestamp: u64, - batches: Vec, - ) { - trace!( - "QS: got clean request from execution at block timestamp {}", - block_timestamp - ); - self.batch_queue.remove_expired_batches(); - for batch in &batches { - self.batch_queue.remove_batch(batch); - } - self.proofs_for_consensus.mark_committed(batches); - self.proofs_for_consensus - .handle_updated_block_timestamp(block_timestamp); - (self.remaining_total_txn_num, self.remaining_total_proof_num) = - self.proofs_for_consensus.remaining_txns_and_proofs(); - } - - pub(crate) fn handle_proposal_request(&mut self, msg: GetPayloadCommand) { - match msg { - GetPayloadCommand::GetPayloadRequest( - max_txns, - max_bytes, - max_inline_txns, - max_inline_bytes, - return_non_full, - filter, - callback, - ) => { - let excluded_batches: HashSet<_> = match filter { - PayloadFilter::Empty => HashSet::new(), - PayloadFilter::DirectMempool(_) => { - unreachable!() - }, - PayloadFilter::InQuorumStore(proofs) => proofs, - }; - - let (proof_block, proof_queue_fully_utilized) = self - .proofs_for_consensus - .pull_proofs(&excluded_batches, max_txns, max_bytes, return_non_full); - - counters::NUM_BATCHES_WITHOUT_PROOF_OF_STORE.observe(self.batch_queue.len() as f64); - counters::PROOF_QUEUE_FULLY_UTILIZED - .observe(if proof_queue_fully_utilized { 1.0 } else { 0.0 }); - - let mut inline_block: Vec<(BatchInfo, Vec)> = vec![]; - let cur_txns: u64 = proof_block.iter().map(|p| p.num_txns()).sum(); - let cur_bytes: u64 = proof_block.iter().map(|p| p.num_bytes()).sum(); - - if self.allow_batches_without_pos_in_proposal && proof_queue_fully_utilized { - inline_block = self.batch_queue.pull_batches( - min(max_txns - cur_txns, max_inline_txns), - min(max_bytes - cur_bytes, max_inline_bytes), - excluded_batches - .iter() - .cloned() - .chain(proof_block.iter().map(|proof| proof.info().clone())) - .collect(), - ); - } - let inline_txns = inline_block - .iter() - .map(|(_, txns)| txns.len()) - .sum::(); - counters::NUM_INLINE_BATCHES.observe(inline_block.len() as f64); - counters::NUM_INLINE_TXNS.observe(inline_txns as f64); - - let res = GetPayloadResponse::GetPayloadResponse( - if proof_block.is_empty() && inline_block.is_empty() { - Payload::empty(true, self.allow_batches_without_pos_in_proposal) - } else if inline_block.is_empty() { - trace!( - "QS: GetBlockRequest excluded len {}, block len {}", - excluded_batches.len(), - proof_block.len() - ); - Payload::InQuorumStore(ProofWithData::new(proof_block)) - } else { - trace!( - "QS: GetBlockRequest excluded len {}, block len {}, inline len {}", - excluded_batches.len(), - proof_block.len(), - inline_block.len() - ); - Payload::QuorumStoreInlineHybrid( - inline_block, - ProofWithData::new(proof_block), - None, - ) - }, - ); - match callback.send(Ok(res)) { - Ok(_) => (), - Err(err) => debug!("BlockResponse receiver not available! error {:?}", err), - } - }, - } - } - - /// return true when quorum store is back pressured - pub(crate) fn qs_back_pressure(&self) -> BackPressure { - BackPressure { - txn_count: self.remaining_total_txn_num > self.back_pressure_total_txn_limit, - proof_count: self.remaining_total_proof_num > self.back_pressure_total_proof_limit, - } - } - - pub async fn start( - mut self, - back_pressure_tx: tokio::sync::mpsc::Sender, - mut proposal_rx: Receiver, - mut proof_rx: tokio::sync::mpsc::Receiver, - ) { - let mut back_pressure = BackPressure { - txn_count: false, - proof_count: false, - }; - - loop { - let _timer = counters::PROOF_MANAGER_MAIN_LOOP.start_timer(); - - tokio::select! { - Some(msg) = proposal_rx.next() => monitor!("proof_manager_handle_proposal", { - self.handle_proposal_request(msg); - - let updated_back_pressure = self.qs_back_pressure(); - if updated_back_pressure != back_pressure { - back_pressure = updated_back_pressure; - if back_pressure_tx.send(back_pressure).await.is_err() { - debug!("Failed to send back_pressure for proposal"); - } - } - }), - Some(msg) = proof_rx.recv() => { - monitor!("proof_manager_handle_command", { - match msg { - ProofManagerCommand::Shutdown(ack_tx) => { - ack_tx - .send(()) - .expect("Failed to send shutdown ack to QuorumStore"); - break; - }, - ProofManagerCommand::ReceiveProofs(proofs) => { - self.receive_proofs(proofs.take()); - }, - ProofManagerCommand::ReceiveBatches(batches) => { - self.receive_batches(batches); - } - ProofManagerCommand::CommitNotification(block_timestamp, batches) => { - self.handle_commit_notification( - block_timestamp, - batches, - ); - }, - } - let updated_back_pressure = self.qs_back_pressure(); - if updated_back_pressure != back_pressure { - back_pressure = updated_back_pressure; - if back_pressure_tx.send(back_pressure).await.is_err() { - debug!("Failed to send back_pressure for commit notification"); - } - } - }) - } - } - } - } -} diff --git a/consensus/src/quorum_store/quorum_store_builder.rs b/consensus/src/quorum_store/quorum_store_builder.rs deleted file mode 100644 index 85ffa4c169380..0000000000000 --- a/consensus/src/quorum_store/quorum_store_builder.rs +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use super::quorum_store_db::QuorumStoreStorage; -use crate::{ - error::error_kind, - network::{IncomingBatchRetrievalRequest, NetworkSender}, - network_interface::ConsensusMsg, - payload_manager::PayloadManager, - quorum_store::{ - batch_coordinator::{BatchCoordinator, BatchCoordinatorCommand}, - batch_generator::{BackPressure, BatchGenerator, BatchGeneratorCommand}, - batch_requester::BatchRequester, - batch_store::{BatchReader, BatchReaderImpl, BatchStore}, - counters, - direct_mempool_quorum_store::DirectMempoolQuorumStore, - network_listener::NetworkListener, - proof_coordinator::{ProofCoordinator, ProofCoordinatorCommand}, - proof_manager::{ProofManager, ProofManagerCommand}, - quorum_store_coordinator::{CoordinatorCommand, QuorumStoreCoordinator}, - types::{Batch, BatchResponse}, - }, - round_manager::VerifiedEvent, -}; -use aptos_channels::{aptos_channel, message_queues::QueueStyle}; -use aptos_config::config::{QuorumStoreConfig, SecureBackend}; -use aptos_consensus_types::{common::Author, request_response::GetPayloadCommand}; -use aptos_global_constants::CONSENSUS_KEY; -use aptos_logger::prelude::*; -use aptos_mempool::QuorumStoreRequest; -use aptos_secure_storage::{KVStorage, Storage}; -use aptos_storage_interface::DbReader; -use aptos_types::{ - account_address::AccountAddress, validator_signer::ValidatorSigner, - validator_verifier::ValidatorVerifier, -}; -use futures::StreamExt; -use futures_channel::mpsc::{Receiver, Sender}; -use std::{sync::Arc, time::Duration}; - -pub enum QuorumStoreBuilder { - DirectMempool(DirectMempoolInnerBuilder), - QuorumStore(InnerBuilder), -} - -impl QuorumStoreBuilder { - pub fn init_payload_manager( - &mut self, - ) -> ( - Arc, - Option>, - ) { - match self { - QuorumStoreBuilder::DirectMempool(inner) => inner.init_payload_manager(), - QuorumStoreBuilder::QuorumStore(inner) => inner.init_payload_manager(), - } - } - - pub fn start( - self, - ) -> Option<( - Sender, - aptos_channel::Sender, - )> { - match self { - QuorumStoreBuilder::DirectMempool(inner) => { - inner.start(); - None - }, - QuorumStoreBuilder::QuorumStore(inner) => Some(inner.start()), - } - } -} - -pub struct DirectMempoolInnerBuilder { - consensus_to_quorum_store_receiver: Receiver, - quorum_store_to_mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, -} - -impl DirectMempoolInnerBuilder { - pub fn new( - consensus_to_quorum_store_receiver: Receiver, - quorum_store_to_mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, - ) -> Self { - Self { - consensus_to_quorum_store_receiver, - quorum_store_to_mempool_sender, - mempool_txn_pull_timeout_ms, - } - } - - fn init_payload_manager( - &mut self, - ) -> ( - Arc, - Option>, - ) { - (Arc::from(PayloadManager::DirectMempool), None) - } - - fn start(self) { - let quorum_store = DirectMempoolQuorumStore::new( - self.consensus_to_quorum_store_receiver, - self.quorum_store_to_mempool_sender, - self.mempool_txn_pull_timeout_ms, - ); - spawn_named!("DirectMempoolQuorumStore", quorum_store.start()); - } -} - -// TODO: push most things to config -pub struct InnerBuilder { - epoch: u64, - author: Author, - num_validators: u64, - config: QuorumStoreConfig, - consensus_to_quorum_store_receiver: Receiver, - quorum_store_to_mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, - aptos_db: Arc, - network_sender: NetworkSender, - verifier: ValidatorVerifier, - backend: SecureBackend, - coordinator_tx: Sender, - coordinator_rx: Option>, - batch_generator_cmd_tx: tokio::sync::mpsc::Sender, - batch_generator_cmd_rx: Option>, - proof_coordinator_cmd_tx: tokio::sync::mpsc::Sender, - proof_coordinator_cmd_rx: Option>, - proof_manager_cmd_tx: tokio::sync::mpsc::Sender, - proof_manager_cmd_rx: Option>, - back_pressure_tx: tokio::sync::mpsc::Sender, - back_pressure_rx: Option>, - quorum_store_storage: Arc, - quorum_store_msg_tx: aptos_channel::Sender, - quorum_store_msg_rx: Option>, - remote_batch_coordinator_cmd_tx: Vec>, - remote_batch_coordinator_cmd_rx: Vec>, - batch_store: Option>, - batch_reader: Option>, - broadcast_proofs: bool, -} - -impl InnerBuilder { - pub(crate) fn new( - epoch: u64, - author: Author, - num_validators: u64, - config: QuorumStoreConfig, - consensus_to_quorum_store_receiver: Receiver, - quorum_store_to_mempool_sender: Sender, - mempool_txn_pull_timeout_ms: u64, - aptos_db: Arc, - network_sender: NetworkSender, - verifier: ValidatorVerifier, - backend: SecureBackend, - quorum_store_storage: Arc, - broadcast_proofs: bool, - ) -> Self { - let (coordinator_tx, coordinator_rx) = futures_channel::mpsc::channel(config.channel_size); - let (batch_generator_cmd_tx, batch_generator_cmd_rx) = - tokio::sync::mpsc::channel(config.channel_size); - let (proof_coordinator_cmd_tx, proof_coordinator_cmd_rx) = - tokio::sync::mpsc::channel(config.channel_size); - let (proof_manager_cmd_tx, proof_manager_cmd_rx) = - tokio::sync::mpsc::channel(config.channel_size); - let (back_pressure_tx, back_pressure_rx) = tokio::sync::mpsc::channel(config.channel_size); - let (quorum_store_msg_tx, quorum_store_msg_rx) = - aptos_channel::new::( - QueueStyle::FIFO, - config.channel_size, - None, - ); - let mut remote_batch_coordinator_cmd_tx = Vec::new(); - let mut remote_batch_coordinator_cmd_rx = Vec::new(); - for _ in 0..config.num_workers_for_remote_batches { - let (batch_coordinator_cmd_tx, batch_coordinator_cmd_rx) = - tokio::sync::mpsc::channel(config.channel_size); - remote_batch_coordinator_cmd_tx.push(batch_coordinator_cmd_tx); - remote_batch_coordinator_cmd_rx.push(batch_coordinator_cmd_rx); - } - - Self { - epoch, - author, - num_validators, - config, - consensus_to_quorum_store_receiver, - quorum_store_to_mempool_sender, - mempool_txn_pull_timeout_ms, - aptos_db, - network_sender, - verifier, - backend, - coordinator_tx, - coordinator_rx: Some(coordinator_rx), - batch_generator_cmd_tx, - batch_generator_cmd_rx: Some(batch_generator_cmd_rx), - proof_coordinator_cmd_tx, - proof_coordinator_cmd_rx: Some(proof_coordinator_cmd_rx), - proof_manager_cmd_tx, - proof_manager_cmd_rx: Some(proof_manager_cmd_rx), - back_pressure_tx, - back_pressure_rx: Some(back_pressure_rx), - quorum_store_storage, - quorum_store_msg_tx, - quorum_store_msg_rx: Some(quorum_store_msg_rx), - remote_batch_coordinator_cmd_tx, - remote_batch_coordinator_cmd_rx, - batch_store: None, - batch_reader: None, - broadcast_proofs, - } - } - - fn create_batch_store(&mut self) -> Arc> { - let backend = &self.backend; - let storage: Storage = backend.into(); - if let Err(error) = storage.available() { - panic!("Storage is not available: {:?}", error); - } - let private_key = storage - .get(CONSENSUS_KEY) - .map(|v| v.value) - .expect("Unable to get private key"); - let signer = ValidatorSigner::new(self.author, private_key); - - let latest_ledger_info_with_sigs = self - .aptos_db - .get_latest_ledger_info() - .expect("could not get latest ledger info"); - let last_committed_timestamp = latest_ledger_info_with_sigs.commit_info().timestamp_usecs(); - - let batch_requester = BatchRequester::new( - self.epoch, - self.author, - self.config.batch_request_num_peers, - self.config.batch_request_retry_limit, - self.config.batch_request_retry_interval_ms, - self.config.batch_request_rpc_timeout_ms, - self.network_sender.clone(), - self.verifier.clone(), - ); - let batch_store = Arc::new(BatchStore::new( - self.epoch, - last_committed_timestamp, - self.quorum_store_storage.clone(), - self.config.memory_quota, - self.config.db_quota, - self.config.batch_quota, - signer, - )); - self.batch_store = Some(batch_store.clone()); - let batch_reader = Arc::new(BatchReaderImpl::new(batch_store.clone(), batch_requester)); - self.batch_reader = Some(batch_reader.clone()); - - batch_reader - } - - fn spawn_quorum_store( - mut self, - ) -> ( - Sender, - aptos_channel::Sender, - ) { - // TODO: parameter? bring back back-off? - let interval = tokio::time::interval(Duration::from_millis( - self.config.batch_generation_poll_interval_ms as u64, - )); - - let coordinator_rx = self.coordinator_rx.take().unwrap(); - let quorum_store_coordinator = QuorumStoreCoordinator::new( - self.author, - self.batch_generator_cmd_tx.clone(), - self.remote_batch_coordinator_cmd_tx.clone(), - self.proof_coordinator_cmd_tx.clone(), - self.proof_manager_cmd_tx.clone(), - self.quorum_store_msg_tx.clone(), - ); - spawn_named!( - "quorum_store_coordinator", - quorum_store_coordinator.start(coordinator_rx) - ); - - let batch_generator_cmd_rx = self.batch_generator_cmd_rx.take().unwrap(); - let back_pressure_rx = self.back_pressure_rx.take().unwrap(); - let batch_generator = BatchGenerator::new( - self.epoch, - self.author, - self.config.clone(), - self.quorum_store_storage.clone(), - self.batch_store.clone().unwrap(), - self.quorum_store_to_mempool_sender, - self.mempool_txn_pull_timeout_ms, - ); - spawn_named!( - "batch_generator", - batch_generator.start( - self.network_sender.clone(), - batch_generator_cmd_rx, - back_pressure_rx, - interval - ) - ); - - for (i, remote_batch_coordinator_cmd_rx) in - self.remote_batch_coordinator_cmd_rx.into_iter().enumerate() - { - let batch_coordinator = BatchCoordinator::new( - self.author, - self.network_sender.clone(), - self.proof_manager_cmd_tx.clone(), - self.batch_store.clone().unwrap(), - self.config.receiver_max_batch_txns as u64, - self.config.receiver_max_batch_bytes as u64, - self.config.receiver_max_total_txns as u64, - self.config.receiver_max_total_bytes as u64, - ); - #[allow(unused_variables)] - let name = format!("batch_coordinator-{}", i); - spawn_named!( - name.as_str(), - batch_coordinator.start(remote_batch_coordinator_cmd_rx) - ); - } - - let proof_coordinator_cmd_rx = self.proof_coordinator_cmd_rx.take().unwrap(); - let proof_coordinator = ProofCoordinator::new( - self.config.proof_timeout_ms, - self.author, - self.batch_reader.clone().unwrap(), - self.batch_generator_cmd_tx.clone(), - self.broadcast_proofs, - ); - spawn_named!( - "proof_coordinator", - proof_coordinator.start( - proof_coordinator_cmd_rx, - self.network_sender.clone(), - self.verifier.clone(), - ) - ); - - let proof_manager_cmd_rx = self.proof_manager_cmd_rx.take().unwrap(); - let proof_manager = ProofManager::new( - self.author, - self.config.back_pressure.backlog_txn_limit_count, - self.config - .back_pressure - .backlog_per_validator_batch_limit_count - * self.num_validators, - self.batch_store.clone().unwrap(), - self.config.allow_batches_without_pos_in_proposal, - ); - spawn_named!( - "proof_manager", - proof_manager.start( - self.back_pressure_tx.clone(), - self.consensus_to_quorum_store_receiver, - proof_manager_cmd_rx, - ) - ); - - let network_msg_rx = self.quorum_store_msg_rx.take().unwrap(); - let net = NetworkListener::new( - network_msg_rx, - self.proof_coordinator_cmd_tx.clone(), - self.remote_batch_coordinator_cmd_tx.clone(), - self.proof_manager_cmd_tx.clone(), - ); - spawn_named!("network_listener", net.start()); - - let batch_store = self.batch_store.clone().unwrap(); - let epoch = self.epoch; - let (batch_retrieval_tx, mut batch_retrieval_rx) = - aptos_channel::new::( - QueueStyle::LIFO, - 10, - Some(&counters::BATCH_RETRIEVAL_TASK_MSGS), - ); - let aptos_db_clone = self.aptos_db.clone(); - spawn_named!("batch_serve", async move { - info!(epoch = epoch, "Batch retrieval task starts"); - while let Some(rpc_request) = batch_retrieval_rx.next().await { - counters::RECEIVED_BATCH_REQUEST_COUNT.inc(); - let response = if let Ok(value) = - batch_store.get_batch_from_local(&rpc_request.req.digest()) - { - let batch: Batch = value.try_into().unwrap(); - BatchResponse::Batch(batch) - } else { - match aptos_db_clone.get_latest_ledger_info() { - Ok(ledger_info) => BatchResponse::NotFound(ledger_info), - Err(e) => { - let e = anyhow::Error::from(e); - error!(epoch = epoch, error = ?e, kind = error_kind(&e)); - continue; - }, - } - }; - - let msg = ConsensusMsg::BatchResponseV2(Box::new(response)); - let bytes = rpc_request.protocol.to_bytes(&msg).unwrap(); - if let Err(e) = rpc_request - .response_sender - .send(Ok(bytes.into())) - .map_err(|_| anyhow::anyhow!("Failed to send block retrieval response")) - { - warn!(epoch = epoch, error = ?e, kind = error_kind(&e)); - } - } - info!(epoch = epoch, "Batch retrieval task stops"); - }); - - (self.coordinator_tx, batch_retrieval_tx) - } - - fn init_payload_manager( - &mut self, - ) -> ( - Arc, - Option>, - ) { - let batch_reader = self.create_batch_store(); - - ( - Arc::from(PayloadManager::InQuorumStore( - batch_reader, - // TODO: remove after splitting out clean requests - self.coordinator_tx.clone(), - )), - Some(self.quorum_store_msg_tx.clone()), - ) - } - - fn start( - self, - ) -> ( - Sender, - aptos_channel::Sender, - ) { - self.spawn_quorum_store() - } -} diff --git a/consensus/src/quorum_store/quorum_store_coordinator.rs b/consensus/src/quorum_store/quorum_store_coordinator.rs deleted file mode 100644 index 089e74c499be8..0000000000000 --- a/consensus/src/quorum_store/quorum_store_coordinator.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - monitor, - quorum_store::{ - batch_coordinator::BatchCoordinatorCommand, batch_generator::BatchGeneratorCommand, - proof_coordinator::ProofCoordinatorCommand, proof_manager::ProofManagerCommand, - }, - round_manager::VerifiedEvent, -}; -use aptos_channels::aptos_channel; -use aptos_consensus_types::proof_of_store::BatchInfo; -use aptos_logger::prelude::*; -use aptos_types::{account_address::AccountAddress, PeerId}; -use futures::StreamExt; -use tokio::sync::{mpsc, oneshot}; - -pub enum CoordinatorCommand { - CommitNotification(u64, Vec), - Shutdown(futures_channel::oneshot::Sender<()>), -} - -pub struct QuorumStoreCoordinator { - my_peer_id: PeerId, - batch_generator_cmd_tx: mpsc::Sender, - remote_batch_coordinator_cmd_tx: Vec>, - proof_coordinator_cmd_tx: mpsc::Sender, - proof_manager_cmd_tx: mpsc::Sender, - quorum_store_msg_tx: aptos_channel::Sender, -} - -impl QuorumStoreCoordinator { - pub(crate) fn new( - my_peer_id: PeerId, - batch_generator_cmd_tx: mpsc::Sender, - remote_batch_coordinator_cmd_tx: Vec>, - proof_coordinator_cmd_tx: mpsc::Sender, - proof_manager_cmd_tx: mpsc::Sender, - quorum_store_msg_tx: aptos_channel::Sender, - ) -> Self { - Self { - my_peer_id, - batch_generator_cmd_tx, - remote_batch_coordinator_cmd_tx, - proof_coordinator_cmd_tx, - proof_manager_cmd_tx, - quorum_store_msg_tx, - } - } - - pub async fn start(self, mut rx: futures_channel::mpsc::Receiver) { - while let Some(cmd) = rx.next().await { - monitor!("quorum_store_coordinator_loop", { - match cmd { - CoordinatorCommand::CommitNotification(block_timestamp, batches) => { - // TODO: need a callback or not? - self.proof_coordinator_cmd_tx - .send(ProofCoordinatorCommand::CommitNotification(batches.clone())) - .await - .expect("Failed to send to ProofCoordinator"); - - self.proof_manager_cmd_tx - .send(ProofManagerCommand::CommitNotification( - block_timestamp, - batches.clone(), - )) - .await - .expect("Failed to send to ProofManager"); - - self.batch_generator_cmd_tx - .send(BatchGeneratorCommand::CommitNotification( - block_timestamp, - batches, - )) - .await - .expect("Failed to send to BatchGenerator"); - }, - CoordinatorCommand::Shutdown(ack_tx) => { - // Note: Shutdown is done from the back of the quorum store pipeline to the - // front, so senders are always shutdown before receivers. This avoids sending - // messages through closed channels during shutdown. - // Oneshots that send data in the reverse order of the pipeline must assume that - // the receiver could be unavailable during shutdown, and resolve this without - // panicking. - - let (network_listener_shutdown_tx, network_listener_shutdown_rx) = - oneshot::channel(); - match self.quorum_store_msg_tx.push( - self.my_peer_id, - VerifiedEvent::Shutdown(network_listener_shutdown_tx), - ) { - Ok(()) => info!("QS: shutdown network listener sent"), - Err(err) => panic!("Failed to send to NetworkListener, Err {:?}", err), - }; - network_listener_shutdown_rx - .await - .expect("Failed to stop NetworkListener"); - - let (batch_generator_shutdown_tx, batch_generator_shutdown_rx) = - oneshot::channel(); - self.batch_generator_cmd_tx - .send(BatchGeneratorCommand::Shutdown(batch_generator_shutdown_tx)) - .await - .expect("Failed to send to BatchGenerator"); - batch_generator_shutdown_rx - .await - .expect("Failed to stop BatchGenerator"); - - for remote_batch_coordinator_cmd_tx in self.remote_batch_coordinator_cmd_tx - { - let ( - remote_batch_coordinator_shutdown_tx, - remote_batch_coordinator_shutdown_rx, - ) = oneshot::channel(); - remote_batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::Shutdown( - remote_batch_coordinator_shutdown_tx, - )) - .await - .expect("Failed to send to Remote BatchCoordinator"); - remote_batch_coordinator_shutdown_rx - .await - .expect("Failed to stop Remote BatchCoordinator"); - } - - let (proof_coordinator_shutdown_tx, proof_coordinator_shutdown_rx) = - oneshot::channel(); - self.proof_coordinator_cmd_tx - .send(ProofCoordinatorCommand::Shutdown( - proof_coordinator_shutdown_tx, - )) - .await - .expect("Failed to send to ProofCoordinator"); - proof_coordinator_shutdown_rx - .await - .expect("Failed to stop ProofCoordinator"); - - let (proof_manager_shutdown_tx, proof_manager_shutdown_rx) = - oneshot::channel(); - self.proof_manager_cmd_tx - .send(ProofManagerCommand::Shutdown(proof_manager_shutdown_tx)) - .await - .expect("Failed to send to ProofManager"); - proof_manager_shutdown_rx - .await - .expect("Failed to stop ProofManager"); - - ack_tx - .send(()) - .expect("Failed to send shutdown ack from QuorumStore"); - break; - }, - } - }) - } - } -} diff --git a/consensus/src/quorum_store/quorum_store_db.rs b/consensus/src/quorum_store/quorum_store_db.rs deleted file mode 100644 index e9b8c1e7619bb..0000000000000 --- a/consensus/src/quorum_store/quorum_store_db.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::DbError, - quorum_store::{ - schema::{BatchIdSchema, BatchSchema, BATCH_CF_NAME, BATCH_ID_CF_NAME}, - types::PersistedValue, - }, -}; -use anyhow::Result; -use aptos_consensus_types::proof_of_store::BatchId; -use aptos_crypto::HashValue; -use aptos_logger::prelude::*; -use aptos_schemadb::{Options, ReadOptions, SchemaBatch, DB}; -use std::{collections::HashMap, path::Path, time::Instant}; - -pub trait QuorumStoreStorage: Sync + Send { - fn delete_batches(&self, digests: Vec) -> Result<(), DbError>; - - fn get_all_batches(&self) -> Result>; - - fn save_batch(&self, batch: PersistedValue) -> Result<(), DbError>; - - fn get_batch(&self, digest: &HashValue) -> Result, DbError>; - - fn delete_batch_id(&self, epoch: u64) -> Result<(), DbError>; - - fn clean_and_get_batch_id(&self, current_epoch: u64) -> Result, DbError>; - - fn save_batch_id(&self, epoch: u64, batch_id: BatchId) -> Result<(), DbError>; -} - -/// The name of the quorum store db file -pub const QUORUM_STORE_DB_NAME: &str = "quorumstoreDB"; - -pub struct QuorumStoreDB { - db: DB, -} - -impl QuorumStoreDB { - pub(crate) fn new + Clone>(db_root_path: P) -> Self { - let column_families = vec![BATCH_CF_NAME, BATCH_ID_CF_NAME]; - - // TODO: this fails twins tests because it assumes a unique path per process - let path = db_root_path.as_ref().join(QUORUM_STORE_DB_NAME); - let instant = Instant::now(); - let mut opts = Options::default(); - opts.create_if_missing(true); - opts.create_missing_column_families(true); - let db = DB::open(path.clone(), QUORUM_STORE_DB_NAME, column_families, &opts) - .expect("QuorumstoreDB open failed; unable to continue"); - - info!( - "Opened QuorumstoreDB at {:?} in {} ms", - path, - instant.elapsed().as_millis() - ); - - Self { db } - } -} - -impl QuorumStoreStorage for QuorumStoreDB { - fn delete_batches(&self, digests: Vec) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - for digest in digests.iter() { - trace!("QS: db delete digest {}", digest); - batch.delete::(digest)?; - } - self.db.write_schemas(batch)?; - Ok(()) - } - - fn get_all_batches(&self) -> Result> { - let mut iter = self.db.iter::(ReadOptions::default())?; - iter.seek_to_first(); - iter.map(|res| res.map_err(Into::into)) - .collect::>>() - } - - fn save_batch(&self, batch: PersistedValue) -> Result<(), DbError> { - trace!( - "QS: db persists digest {} expiration {:?}", - batch.digest(), - batch.expiration() - ); - Ok(self.db.put::(batch.digest(), &batch)?) - } - - fn get_batch(&self, digest: &HashValue) -> Result, DbError> { - Ok(self.db.get::(digest)?) - } - - fn delete_batch_id(&self, epoch: u64) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.delete::(&epoch)?; - self.db.write_schemas(batch)?; - Ok(()) - } - - fn clean_and_get_batch_id(&self, current_epoch: u64) -> Result, DbError> { - let mut iter = self.db.iter::(ReadOptions::default())?; - iter.seek_to_first(); - let epoch_batch_id = iter - .map(|res| res.map_err(Into::into)) - .collect::>>()?; - let mut ret = None; - for (epoch, batch_id) in epoch_batch_id { - assert!(current_epoch >= epoch); - if epoch < current_epoch { - self.delete_batch_id(epoch) - .expect("Could not delete from db"); - } else { - ret = Some(batch_id); - } - } - Ok(ret) - } - - fn save_batch_id(&self, epoch: u64, batch_id: BatchId) -> Result<(), DbError> { - Ok(self.db.put::(&epoch, &batch_id)?) - } -} - -pub(crate) struct MockQuorumStoreDB {} - -impl MockQuorumStoreDB { - #[cfg(test)] - pub fn new() -> Self { - Self {} - } -} - -impl QuorumStoreStorage for MockQuorumStoreDB { - fn delete_batches(&self, _: Vec) -> Result<(), DbError> { - Ok(()) - } - - fn get_all_batches(&self) -> Result> { - Ok(HashMap::new()) - } - - fn save_batch(&self, _: PersistedValue) -> Result<(), DbError> { - Ok(()) - } - - fn get_batch(&self, _: &HashValue) -> Result, DbError> { - Ok(None) - } - - fn delete_batch_id(&self, _: u64) -> Result<(), DbError> { - Ok(()) - } - - fn clean_and_get_batch_id(&self, _: u64) -> Result, DbError> { - Ok(Some(BatchId::new_for_test(0))) - } - - fn save_batch_id(&self, _: u64, _: BatchId) -> Result<(), DbError> { - Ok(()) - } -} diff --git a/consensus/src/quorum_store/schema.rs b/consensus/src/quorum_store/schema.rs deleted file mode 100644 index f6213c463c9c0..0000000000000 --- a/consensus/src/quorum_store/schema.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::quorum_store::types::PersistedValue; -use anyhow::Result; -use aptos_consensus_types::proof_of_store::BatchId; -use aptos_crypto::HashValue; -use aptos_schemadb::{ - schema::{KeyCodec, Schema, ValueCodec}, - ColumnFamilyName, -}; - -pub(crate) const BATCH_CF_NAME: ColumnFamilyName = "batch"; -pub(crate) const BATCH_ID_CF_NAME: ColumnFamilyName = "batch_ID"; - -#[derive(Debug)] -pub(crate) struct BatchSchema; - -impl Schema for BatchSchema { - type Key = HashValue; - type Value = PersistedValue; - - const COLUMN_FAMILY_NAME: aptos_schemadb::ColumnFamilyName = BATCH_CF_NAME; -} - -impl KeyCodec for HashValue { - fn encode_key(&self) -> Result> { - Ok(self.to_vec()) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(HashValue::from_slice(data)?) - } -} - -impl ValueCodec for PersistedValue { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -#[derive(Debug)] -pub(crate) struct BatchIdSchema; - -impl Schema for BatchIdSchema { - type Key = u64; - type Value = BatchId; - - const COLUMN_FAMILY_NAME: aptos_schemadb::ColumnFamilyName = BATCH_ID_CF_NAME; -} - -impl KeyCodec for u64 { - fn encode_key(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_key(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} - -impl ValueCodec for BatchId { - fn encode_value(&self) -> Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> Result { - Ok(bcs::from_bytes(data)?) - } -} diff --git a/consensus/src/quorum_store/tests/batch_generator_test.rs b/consensus/src/quorum_store/tests/batch_generator_test.rs deleted file mode 100644 index 34766a363b53a..0000000000000 --- a/consensus/src/quorum_store/tests/batch_generator_test.rs +++ /dev/null @@ -1,748 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - quorum_store::{ - batch_coordinator::BatchCoordinatorCommand, batch_generator::BatchGenerator, - batch_store::BatchWriter, quorum_store_db::MockQuorumStoreDB, types::PersistedValue, - }, - test_utils::{ - create_signed_transaction, create_vec_signed_transactions, - create_vec_signed_transactions_with_gas, - }, -}; -use aptos_config::config::QuorumStoreConfig; -use aptos_consensus_types::{ - common::{TransactionInProgress, TransactionSummary}, - proof_of_store::{BatchId, SignedBatchInfo}, -}; -use aptos_mempool::{QuorumStoreRequest, QuorumStoreResponse}; -use aptos_types::transaction::SignedTransaction; -use futures::{ - channel::mpsc::{channel, Receiver}, - StreamExt, -}; -use move_core_types::account_address::AccountAddress; -use std::{collections::BTreeMap, sync::Arc, time::Duration}; -use tokio::{sync::mpsc::channel as TokioChannel, time::timeout}; - -struct MockBatchWriter {} - -impl MockBatchWriter { - fn new() -> Self { - Self {} - } -} - -impl BatchWriter for MockBatchWriter { - fn persist(&self, _persist_requests: Vec) -> Vec { - vec![] - } -} - -#[allow(clippy::needless_collect)] -async fn queue_mempool_batch_response( - txns: Vec, - max_size: usize, - quorum_store_to_mempool_receiver: &mut Receiver, -) -> BTreeMap { - if let QuorumStoreRequest::GetBatchRequest( - _max_batch_size, - _max_bytes, - _return_non_full, - _include_gas_upgraded, - exclude_txns, - callback, - ) = timeout( - Duration::from_millis(1_000), - quorum_store_to_mempool_receiver.select_next_some(), - ) - .await - .unwrap() - { - let mut size = 0; - let mut sorted_txns = txns.clone(); - sorted_txns.sort_by_key(|txn| txn.gas_unit_price()); - let chosen_txns: Vec<_> = sorted_txns - .into_iter() - .rev() - .take_while(|txn| { - size += txn.txn_bytes_len(); - size <= max_size - }) - .collect(); - let ret: Vec<_> = chosen_txns.into_iter().rev().collect(); - callback - .send(Ok(QuorumStoreResponse::GetBatchResponse(ret))) - .unwrap(); - exclude_txns - } else { - panic!("Unexpected variant") - } -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_batch_creation() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let txn_size = 168; - let max_size = 9 * txn_size; - - let config = QuorumStoreConfig { - sender_max_total_bytes: max_size, - ..Default::default() - }; - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let mut num_txns = 0; - - let signed_txns = create_vec_signed_transactions(1); - assert_eq!(signed_txns[0].txn_bytes_len(), txn_size); - queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - // Expect Batch for 1 txn - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, data) = quorum_store_command { - assert_eq!(1, data.len()); - let data = data[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(1)); - let txns = data.into_transactions(); - assert_eq!(txns.len(), signed_txns.len()); - assert_eq!(txns, signed_txns); - } else { - panic!("Unexpected variant") - } - num_txns += 1; - - let signed_txns = create_vec_signed_transactions(10); - // Expect single exclude_txn - let exclude_txns = queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - assert_eq!(exclude_txns.len(), num_txns); - // Expect Batch for 9 (due to size limit). - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, data) = quorum_store_command { - assert_eq!(1, data.len()); - let data = data[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(2)); - let txns = data.into_transactions(); - assert_eq!(txns.len(), signed_txns.len() - 1); - // let expected: Vec<_> = signed_txns[0..9].iter().rev().cloned().collect(); - // assert_eq!(txns, expected); - } else { - panic!("Unexpected variant") - } - num_txns += 9; - - let signed_txns = create_vec_signed_transactions(9); - // Expect 1 + 9 = 10 exclude_txn - let exclude_txns = queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - assert_eq!(exclude_txns.len(), num_txns); - // Expect AppendBatch for 9 txns - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, data) = quorum_store_command { - assert_eq!(1, data.len()); - let data = data[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(3)); - let txns = data.into_transactions(); - assert_eq!(txns.len(), signed_txns.len()); - // assert_eq!(txns, signed_txns); - } else { - panic!("Unexpected variant") - } - }); - - for _ in 0..3 { - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - } - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_bucketed_batch_creation() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let txn_size = 168; - let max_size = 9 * txn_size; - - let config = QuorumStoreConfig { - sender_max_total_bytes: max_size, - ..Default::default() - }; - let buckets = config.batch_buckets.clone(); - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let mut num_txns = 0; - - let join_handle = tokio::spawn(async move { - let signed_txns = create_vec_signed_transactions_with_gas(1, buckets[1]); - assert_eq!(signed_txns[0].txn_bytes_len(), txn_size); - queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - - // Expect Batch for 1 txn - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, data) = quorum_store_command { - assert_eq!(1, data.len()); - let data = data[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(1)); - let txns = data.into_transactions(); - assert_eq!(txns.len(), signed_txns.len()); - assert_eq!(txns, signed_txns); - } else { - panic!("Unexpected variant") - } - num_txns += 1; - - let mut signed_txns = vec![]; - let bucket_0 = create_vec_signed_transactions_with_gas(3, buckets[0]); - signed_txns.append(&mut bucket_0.clone()); - let bucket_1 = create_vec_signed_transactions_with_gas(3, buckets[1]); - signed_txns.append(&mut bucket_1.clone()); - let bucket_4 = create_vec_signed_transactions_with_gas(4, buckets[4]); - signed_txns.append(&mut bucket_4.clone()); - // Expect single exclude_txn - let exclude_txns = queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - assert_eq!(exclude_txns.len(), num_txns); - // Expect Batch for 9 (due to size limit). - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, batches) = quorum_store_command { - assert_eq!(3, batches.len()); - - let data = batches[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(2)); - assert_eq!(data.batch_info().gas_bucket_start(), buckets[4]); - let txns = data.into_transactions(); - // This gas bucket should have all elements - assert_eq!(txns.len(), bucket_4.len()); - - let data = batches[1].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(3)); - assert_eq!(data.batch_info().gas_bucket_start(), buckets[1]); - let txns = data.into_transactions(); - // This gas bucket should have all elements - assert_eq!(txns.len(), bucket_1.len()); - - let data = batches[2].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(4)); - assert_eq!(data.batch_info().gas_bucket_start(), buckets[0]); - let txns = data.into_transactions(); - // As only 9 items fit, the least gas bucket has less items. - assert_eq!(txns.len(), bucket_0.len() - 1); - } else { - panic!("Unexpected variant") - } - num_txns += 9; - - let signed_txns = create_vec_signed_transactions_with_gas(9, u64::MAX); - // Expect 1 + 9 = 10 exclude_txn - let exclude_txns = queue_mempool_batch_response( - signed_txns.clone(), - max_size, - &mut quorum_store_to_mempool_rx, - ) - .await; - assert_eq!(exclude_txns.len(), num_txns); - // Expect AppendBatch for 9 txns - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, data) = quorum_store_command { - assert_eq!(1, data.len()); - let data = data[0].clone(); - assert_eq!(data.batch_id(), BatchId::new_for_test(5)); - assert_eq!( - data.batch_info().gas_bucket_start(), - buckets[buckets.len() - 1] - ); - let txns = data.into_transactions(); - assert_eq!(txns.len(), signed_txns.len()); - } else { - panic!("Unexpected variant") - } - }); - - for _ in 0..3 { - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - } - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_max_batch_txns() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let config = QuorumStoreConfig { - sender_max_batch_txns: 10, - ..Default::default() - }; - let max_batch_bytes = config.sender_max_batch_bytes; - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let signed_txns = create_vec_signed_transactions(25); - queue_mempool_batch_response( - signed_txns.clone(), - max_batch_bytes, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), 3); - assert_eq!(result[0].num_txns(), 10); - assert_eq!(result[1].num_txns(), 10); - assert_eq!(result[2].num_txns(), 5); - - assert_eq!(&result[0].clone().into_transactions(), &signed_txns[0..10]); - assert_eq!(&result[1].clone().into_transactions(), &signed_txns[10..20]); - assert_eq!(&result[2].clone().into_transactions(), &signed_txns[20..]); - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_max_batch_bytes() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let txn_bytes_len = 168; - assert_eq!( - create_vec_signed_transactions(1)[0].txn_bytes_len(), - txn_bytes_len - ); - let config = QuorumStoreConfig { - sender_max_batch_bytes: txn_bytes_len * 10, - ..Default::default() - }; - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let signed_txns = create_vec_signed_transactions(25); - queue_mempool_batch_response( - signed_txns.clone(), - txn_bytes_len * 25, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), 3); - assert_eq!(result[0].num_txns(), 10); - assert_eq!(result[1].num_txns(), 10); - assert_eq!(result[2].num_txns(), 5); - - assert_eq!(&result[0].clone().into_transactions(), &signed_txns[0..10]); - assert_eq!(&result[1].clone().into_transactions(), &signed_txns[10..20]); - assert_eq!(&result[2].clone().into_transactions(), &signed_txns[20..]); - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_max_num_batches() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let config = QuorumStoreConfig { - sender_max_batch_txns: 10, - sender_max_num_batches: 2, - ..Default::default() - }; - let max_batch_bytes = config.sender_max_batch_bytes; - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let signed_txns = create_vec_signed_transactions(25); - queue_mempool_batch_response( - signed_txns.clone(), - max_batch_bytes, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), 2); - assert_eq!(result[0].num_txns(), 10); - assert_eq!(result[1].num_txns(), 10); - - assert_eq!(&result[0].clone().into_transactions(), &signed_txns[0..10]); - assert_eq!(&result[1].clone().into_transactions(), &signed_txns[10..20]); - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_last_bucketed_batch() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let config = QuorumStoreConfig { - sender_max_batch_txns: 10, - ..Default::default() - }; - let max_batch_bytes = config.sender_max_batch_bytes; - let buckets = config.batch_buckets.clone(); - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let low_gas_txn = create_signed_transaction(1); - let high_gas_txn_other_account = create_signed_transaction(u64::MAX); - let signed_txns = vec![low_gas_txn, high_gas_txn_other_account]; - - queue_mempool_batch_response( - signed_txns.clone(), - max_batch_bytes, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), 2); - assert_eq!(result[0].num_txns(), 1); - assert_eq!(result[1].num_txns(), 1); - assert_eq!(result[0].gas_bucket_start(), buckets[buckets.len() - 1]); - assert_eq!(result[1].gas_bucket_start(), 0); - - assert_eq!(&result[0].clone().into_transactions(), &signed_txns[1..]); - assert_eq!(&result[1].clone().into_transactions(), &signed_txns[0..1]); - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_sender_max_num_batches_single_bucket() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let config = QuorumStoreConfig { - sender_max_batch_txns: 10, - sender_max_num_batches: 3, - ..Default::default() - }; - let max_batch_txns = config.sender_max_batch_txns; - let max_batch_bytes = config.sender_max_batch_bytes; - let max_num_batches = config.sender_max_num_batches; - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let signed_txns = - create_vec_signed_transactions((max_batch_txns * max_num_batches + 1) as u64); - queue_mempool_batch_response( - signed_txns.clone(), - max_batch_bytes, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), max_num_batches); - for batch in &result { - assert_eq!(batch.num_txns(), max_batch_txns as u64); - } - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test] -async fn test_sender_max_num_batches_multi_buckets() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - let (batch_coordinator_cmd_tx, mut batch_coordinator_cmd_rx) = TokioChannel(100); - - let config = QuorumStoreConfig { - sender_max_batch_txns: 10, - sender_max_num_batches: 3, - ..Default::default() - }; - let max_batch_txns = config.sender_max_batch_txns; - let max_batch_bytes = config.sender_max_batch_bytes; - let max_num_batches = config.sender_max_num_batches; - let buckets = config.batch_buckets.clone(); - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - config, - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let mut signed_txns = vec![]; - for min_gas_price in buckets.iter().take(max_num_batches) { - let mut new_txns = create_vec_signed_transactions_with_gas( - max_batch_txns as u64 + 1, - *min_gas_price + 1, - ); - signed_txns.append(&mut new_txns); - } - queue_mempool_batch_response( - signed_txns.clone(), - max_batch_bytes, - &mut quorum_store_to_mempool_rx, - ) - .await; - - let quorum_store_command = batch_coordinator_cmd_rx.recv().await.unwrap(); - if let BatchCoordinatorCommand::NewBatches(_, result) = quorum_store_command { - assert_eq!(result.len(), max_num_batches); - for (i, batch) in result.iter().enumerate() { - if i % 2 == 0 { - assert_eq!(batch.num_txns(), max_batch_txns as u64); - } else { - assert_eq!(batch.num_txns(), 1); - } - } - } else { - panic!("Unexpected variant") - } - }); - - let result = batch_generator.handle_scheduled_pull(300).await; - batch_coordinator_cmd_tx - .send(BatchCoordinatorCommand::NewBatches(author, result)) - .await - .unwrap(); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_batches_in_progress_same_txn_across_batches() { - let (quorum_store_to_mempool_tx, mut quorum_store_to_mempool_rx) = channel(1_024); - - let author = AccountAddress::random(); - let mut batch_generator = BatchGenerator::new( - 0, - author, - QuorumStoreConfig::default(), - Arc::new(MockQuorumStoreDB::new()), - Arc::new(MockBatchWriter::new()), - quorum_store_to_mempool_tx, - 1000, - ); - - let join_handle = tokio::spawn(async move { - let signed_txns = create_vec_signed_transactions(3); - let first_one: Vec<_> = signed_txns.iter().take(1).cloned().collect(); - let first_two: Vec<_> = signed_txns.iter().take(2).cloned().collect(); - let first_three: Vec<_> = signed_txns.iter().take(3).cloned().collect(); - - // Add multiple of the same txns across batches (txn1: 3 times, txn2: 2 times, txn3: 1 time) - queue_mempool_batch_response(first_one, 1024, &mut quorum_store_to_mempool_rx).await; - queue_mempool_batch_response(first_two, 1024, &mut quorum_store_to_mempool_rx).await; - queue_mempool_batch_response(first_three, 1024, &mut quorum_store_to_mempool_rx).await; - }); - - let first_one_result = batch_generator.handle_scheduled_pull(300).await; - assert_eq!(first_one_result.len(), 1); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 1); - - let first_two_result = batch_generator.handle_scheduled_pull(300).await; - assert_eq!(first_two_result.len(), 1); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 2); - - let first_three_result = batch_generator.handle_scheduled_pull(300).await; - assert_eq!(first_three_result.len(), 1); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 3); - - timeout(Duration::from_millis(10_000), join_handle) - .await - .unwrap() - .unwrap(); - - // After all batches are complete, txns_in_progress_sorted will be empty. - batch_generator - .remove_batch_in_progress_for_test(&first_three_result.first().unwrap().batch_id()); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 2); - batch_generator - .remove_batch_in_progress_for_test(&first_two_result.first().unwrap().batch_id()); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 1); - batch_generator - .remove_batch_in_progress_for_test(&first_one_result.first().unwrap().batch_id()); - assert_eq!(batch_generator.txns_in_progress_sorted_len(), 0); -} diff --git a/consensus/src/quorum_store/tests/batch_requester_test.rs b/consensus/src/quorum_store/tests/batch_requester_test.rs deleted file mode 100644 index aaf5ff6bf4528..0000000000000 --- a/consensus/src/quorum_store/tests/batch_requester_test.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::QuorumStoreSender, - quorum_store::{ - batch_requester::BatchRequester, - types::{Batch, BatchRequest, BatchResponse}, - }, -}; -use aptos_consensus_types::{ - common::Author, - proof_of_store::{BatchId, ProofOfStore, SignedBatchInfo}, -}; -use aptos_crypto::HashValue; -use aptos_types::{ - aggregate_signature::{AggregateSignature, PartialSignatures}, - block_info::BlockInfo, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - validator_signer::ValidatorSigner, - validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier}, -}; -use move_core_types::account_address::AccountAddress; -use std::time::{Duration, Instant}; - -#[derive(Clone)] -struct MockBatchRequester { - return_value: BatchResponse, -} - -impl MockBatchRequester { - fn new(return_value: BatchResponse) -> Self { - Self { return_value } - } -} - -#[async_trait::async_trait] -impl QuorumStoreSender for MockBatchRequester { - async fn send_batch_request(&self, _request: BatchRequest, _recipients: Vec) { - unimplemented!() - } - - async fn request_batch( - &self, - _request: BatchRequest, - _recipient: Author, - _timeout: Duration, - ) -> anyhow::Result { - Ok(self.return_value.clone()) - } - - async fn send_batch(&self, _batch: Batch, _recipients: Vec) { - unimplemented!() - } - - async fn send_signed_batch_info_msg( - &self, - _signed_batch_infos: Vec, - _recipients: Vec, - ) { - unimplemented!() - } - - async fn broadcast_batch_msg(&mut self, _batches: Vec) { - unimplemented!() - } - - async fn broadcast_proof_of_store_msg(&mut self, _proof_of_stores: Vec) { - unimplemented!() - } - - async fn send_proof_of_store_msg_to_self(&mut self, _proof_of_stores: Vec) { - unimplemented!() - } -} - -#[tokio::test] -async fn test_batch_request_exists() { - let batch = Batch::new( - BatchId::new_for_test(1), - vec![], - 1, - 1, - AccountAddress::random(), - 0, - ); - let batch_response = BatchResponse::Batch(batch.clone()); - - let validator_signer = ValidatorSigner::random(None); - let (tx, mut rx) = tokio::sync::oneshot::channel(); - let batch_requester = BatchRequester::new( - 1, - AccountAddress::random(), - 1, - 2, - 1_000, - 1_000, - MockBatchRequester::new(batch_response), - ValidatorVerifier::new_single(validator_signer.author(), validator_signer.public_key()), - ); - - let result = batch_requester - .request_batch( - ProofOfStore::new( - batch.batch_info().clone(), - AggregateSignature::new(vec![u8::MAX].into(), None), - ), - tx, - ) - .await; - assert!(result.is_some()); - if let Some((batch_info, _payload)) = result { - assert_eq!(batch_info, *batch.batch_info()); - } - assert!(rx.try_recv().is_ok()); -} - -fn create_ledger_info_with_timestamp( - timestamp: u64, -) -> (LedgerInfoWithSignatures, ValidatorVerifier) { - const NUM_SIGNERS: u8 = 1; - // Generate NUM_SIGNERS random signers. - let validator_signers: Vec = (0..NUM_SIGNERS) - .map(|i| ValidatorSigner::random([i; 32])) - .collect(); - let block_info = BlockInfo::new( - 1, - 1, - HashValue::random(), - HashValue::random(), - 0, - timestamp, - None, - ); - let ledger_info = LedgerInfo::new(block_info, HashValue::random()); - - // Create a map from authors to public keys with equal voting power. - let mut validator_infos = vec![]; - for validator in validator_signers.iter() { - validator_infos.push(ValidatorConsensusInfo::new( - validator.author(), - validator.public_key(), - 1, - )); - } - - // Create a map from author to signatures. - let mut partial_signature = PartialSignatures::empty(); - for validator in validator_signers.iter() { - partial_signature.add_signature(validator.author(), validator.sign(&ledger_info).unwrap()); - } - - // Let's assume our verifier needs to satisfy all NUM_SIGNERS - let validator_verifier = - ValidatorVerifier::new_with_quorum_voting_power(validator_infos, NUM_SIGNERS as u128) - .expect("Incorrect quorum size."); - let aggregated_signature = validator_verifier - .aggregate_signatures(&partial_signature) - .unwrap(); - let ledger_info_with_signatures = - LedgerInfoWithSignatures::new(ledger_info, aggregated_signature); - - (ledger_info_with_signatures, validator_verifier) -} - -#[tokio::test] -async fn test_batch_request_not_exists_not_expired() { - let retry_interval_ms = 1_000; - let expiration = 10_000; - - // Batch has not expired yet - let (ledger_info_with_signatures, validator_verifier) = - create_ledger_info_with_timestamp(expiration - 1); - - let batch = Batch::new( - BatchId::new_for_test(1), - vec![], - 1, - expiration, - AccountAddress::random(), - 0, - ); - let (tx, mut rx) = tokio::sync::oneshot::channel(); - let batch_response = BatchResponse::NotFound(ledger_info_with_signatures); - let batch_requester = BatchRequester::new( - 1, - AccountAddress::random(), - 1, - 2, - retry_interval_ms, - 1_000, - MockBatchRequester::new(batch_response), - validator_verifier, - ); - - let request_start = Instant::now(); - let result = batch_requester - .request_batch( - ProofOfStore::new( - batch.batch_info().clone(), - AggregateSignature::new(vec![u8::MAX].into(), None), - ), - tx, - ) - .await; - let request_duration = request_start.elapsed(); - assert!(result.is_none()); - assert!(rx.try_recv().is_ok()); - // Retried at least once - assert!(request_duration > Duration::from_millis(retry_interval_ms as u64)); -} - -#[tokio::test] -async fn test_batch_request_not_exists_expired() { - let retry_interval_ms = 1_000; - let expiration = 10_000; - - // Batch has expired according to the ledger info that will be returned - let (ledger_info_with_signatures, validator_verifier) = - create_ledger_info_with_timestamp(expiration + 1); - - let batch = Batch::new( - BatchId::new_for_test(1), - vec![], - 1, - expiration, - AccountAddress::random(), - 0, - ); - let (tx, mut rx) = tokio::sync::oneshot::channel(); - let batch_response = BatchResponse::NotFound(ledger_info_with_signatures); - let batch_requester = BatchRequester::new( - 1, - AccountAddress::random(), - 1, - 2, - retry_interval_ms, - 1_000, - MockBatchRequester::new(batch_response), - validator_verifier, - ); - - let request_start = Instant::now(); - let result = batch_requester - .request_batch( - ProofOfStore::new( - batch.batch_info().clone(), - AggregateSignature::new(vec![u8::MAX].into(), None), - ), - tx, - ) - .await; - let request_duration = request_start.elapsed(); - assert!(result.is_none()); - assert!(rx.try_recv().is_ok()); - // No retry because of short-circuiting of expired batch - assert!(request_duration < Duration::from_millis(retry_interval_ms as u64)); -} diff --git a/consensus/src/quorum_store/tests/batch_store_test.rs b/consensus/src/quorum_store/tests/batch_store_test.rs deleted file mode 100644 index fed469c4df93f..0000000000000 --- a/consensus/src/quorum_store/tests/batch_store_test.rs +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::quorum_store::{ - batch_store::{BatchStore, BatchWriter, QuotaManager}, - quorum_store_db::QuorumStoreDB, - types::{PersistedValue, StorageMode}, -}; -use aptos_consensus_types::proof_of_store::{BatchId, BatchInfo}; -use aptos_crypto::HashValue; -use aptos_temppath::TempPath; -use aptos_types::{ - account_address::AccountAddress, transaction::SignedTransaction, - validator_verifier::random_validator_verifier, -}; -use claims::{assert_err, assert_ok, assert_ok_eq}; -use once_cell::sync::Lazy; -use std::sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, -}; -use tokio::task::spawn_blocking; - -static TEST_REQUEST_ACCOUNT: Lazy = Lazy::new(AccountAddress::random); - -pub fn batch_store_for_test(memory_quota: usize) -> Arc { - let tmp_dir = TempPath::new(); - let db = Arc::new(QuorumStoreDB::new(&tmp_dir)); - let (signers, _validator_verifier) = random_validator_verifier(4, None, false); - - Arc::new(BatchStore::new( - 10, // epoch - 10, // last committed round - db, - memory_quota, // memory_quota - 2001, // db quota - 2001, // batch quota - signers[0].clone(), - )) -} - -fn request_for_test( - digest: &HashValue, - round: u64, - num_bytes: u64, - maybe_payload: Option>, -) -> PersistedValue { - PersistedValue::new( - BatchInfo::new( - *TEST_REQUEST_ACCOUNT, // make sure all request come from the same account - BatchId::new_for_test(1), - 10, - round, - *digest, - 10, - num_bytes, - 0, - ), - maybe_payload, - ) -} - -#[test] -fn test_insert_expire() { - let batch_store = batch_store_for_test(30); - - let digest = HashValue::random(); - - assert_ok_eq!( - batch_store.insert_to_cache(&request_for_test(&digest, 15, 10, None)), - true - ); - assert_ok_eq!( - batch_store.insert_to_cache(&request_for_test(&digest, 30, 10, None)), - true - ); - assert_ok_eq!( - batch_store.insert_to_cache(&request_for_test(&digest, 25, 10, None)), - false - ); - let expired = batch_store.clear_expired_payload(27); - assert!(expired.is_empty()); - let expired = batch_store.clear_expired_payload(29); - assert!(expired.is_empty()); - assert_eq!(batch_store.clear_expired_payload(30), vec![digest]); -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_extend_expiration_vs_save() { - let num_experiments = 2000; - let batch_store = batch_store_for_test(2001); - - let batch_store_clone1 = batch_store.clone(); - let batch_store_clone2 = batch_store.clone(); - - let digests: Vec = (0..num_experiments).map(|_| HashValue::random()).collect(); - let later_exp_values: Vec = (0..num_experiments) - .map(|i| { - // Pre-insert some of them. - if i % 2 == 0 { - assert_ok!(batch_store.save(&request_for_test( - &digests[i], - i as u64 + 30, - 1, - None - ))); - } - - request_for_test(&digests[i], i as u64 + 40, 1, None) - }) - .collect(); - - // Marshal threads to start at the same time. - let start_flag = Arc::new(AtomicUsize::new(0)); - let start_clone1 = start_flag.clone(); - let start_clone2 = start_flag.clone(); - - let save_error = Arc::new(AtomicBool::new(false)); - let save_error_clone1 = save_error.clone(); - let save_error_clone2 = save_error.clone(); - - // Thread that extends expiration by saving. - spawn_blocking(move || { - for (i, later_exp_value) in later_exp_values.into_iter().enumerate() { - // Wait until both threads are ready for next experiment. - loop { - let flag_val = start_clone1.load(Ordering::Acquire); - if flag_val == 3 * i + 1 || flag_val == 3 * i + 2 { - break; - } - } - - if batch_store_clone1.save(&later_exp_value).is_err() { - // Save in a separate flag and break so test doesn't hang. - save_error_clone1.store(true, Ordering::Release); - break; - } - start_clone1.fetch_add(1, Ordering::Relaxed); - } - }); - - // Thread that expires. - spawn_blocking(move || { - for i in 0..num_experiments { - // Wait until both threads are ready for next experiment. - loop { - let flag_val = start_clone2.load(Ordering::Acquire); - if flag_val == 3 * i + 1 - || flag_val == 3 * i + 2 - || save_error_clone2.load(Ordering::Acquire) - { - break; - } - } - - batch_store_clone2.update_certified_timestamp(i as u64 + 30); - start_clone2.fetch_add(1, Ordering::Relaxed); - } - }); - - for (i, &digest) in digests.iter().enumerate().take(num_experiments) { - // Set the conditions for experiment (both threads waiting). - while start_flag.load(Ordering::Acquire) % 3 != 0 { - assert!(!save_error.load(Ordering::Acquire)); - } - - if i % 2 == 1 { - assert_ok!(batch_store.save(&request_for_test(&digest, i as u64 + 30, 1, None))); - } - - // Unleash the threads. - start_flag.fetch_add(1, Ordering::Relaxed); - } - // Finish the experiment - while start_flag.load(Ordering::Acquire) % 3 != 0 {} - - // Expire everything, call for higher times as well. - for i in 35..50 { - batch_store.update_certified_timestamp((i + num_experiments) as u64); - } -} - -#[test] -fn test_quota_manager() { - let mut qm = QuotaManager::new(20, 10, 7); - assert_ok_eq!(qm.update_quota(5), StorageMode::MemoryAndPersisted); - assert_ok_eq!(qm.update_quota(3), StorageMode::MemoryAndPersisted); - assert_ok_eq!(qm.update_quota(2), StorageMode::MemoryAndPersisted); - assert_ok_eq!(qm.update_quota(1), StorageMode::PersistedOnly); - assert_ok_eq!(qm.update_quota(2), StorageMode::PersistedOnly); - assert_ok_eq!(qm.update_quota(7), StorageMode::PersistedOnly); - // 6 batches, fully used quotas - - // exceed storage quota. - assert_err!(qm.update_quota(2)); - - qm.free_quota(5, StorageMode::MemoryAndPersisted); - // 5 batches, available memory and db quota: 5 - - // exceed storage quota - assert_err!(qm.update_quota(6)); - assert_ok_eq!(qm.update_quota(3), StorageMode::MemoryAndPersisted); - - // exceed storage quota - assert_err!(qm.update_quota(3)); - assert_ok_eq!(qm.update_quota(1), StorageMode::MemoryAndPersisted); - // 7 batches, available memory and DB quota: 1 - - // Exceed batch quota - assert_err!(qm.update_quota(1)); - - qm.free_quota(1, StorageMode::PersistedOnly); - // 6 batches, available memory quota: 1, available DB quota: 2 - - // exceed storage quota - assert_err!(qm.update_quota(3)); - assert_ok_eq!(qm.update_quota(2), StorageMode::PersistedOnly); - // 7 batches, available memory quota: 1, available DB quota: 0 - - qm.free_quota(2, StorageMode::MemoryAndPersisted); - // 6 batches, available memory quota: 3, available DB quota: 2 - - // while there is available memory quota, DB quota isn't enough. - assert_err!(qm.update_quota(3)); - assert_ok_eq!(qm.update_quota(2), StorageMode::MemoryAndPersisted); -} - -#[test] -fn test_get_local_batch() { - let store = batch_store_for_test(30); - - let digest_1 = HashValue::random(); - let request_1 = request_for_test(&digest_1, 50, 20, Some(vec![])); - // Should be stored in memory and DB. - assert!(!store.persist(vec![request_1]).is_empty()); - - store.update_certified_timestamp(40); - - let digest_2 = HashValue::random(); - assert!(digest_2 != digest_1); - // Expiration is before 40. - let request_2_expired = request_for_test(&digest_2, 30, 20, Some(vec![])); - assert!(store.persist(vec![request_2_expired]).is_empty()); - // Proper (in the future) expiration. - let request_2 = request_for_test(&digest_2, 55, 20, Some(vec![])); - // Should be stored in DB only - assert!(!store.persist(vec![request_2]).is_empty()); - - let digest_3 = HashValue::random(); - assert!(digest_3 != digest_1); - assert!(digest_3 != digest_2); - let request_3 = request_for_test(&digest_3, 56, 1970, Some(vec![])); - // Out of quota - should not be stored - assert!(store.persist(vec![request_3.clone()]).is_empty()); - - assert_ok!(store.get_batch_from_local(&digest_1)); - assert_ok!(store.get_batch_from_local(&digest_2)); - store.update_certified_timestamp(51); - // Expired value w. digest_1. - assert_err!(store.get_batch_from_local(&digest_1)); - assert_ok!(store.get_batch_from_local(&digest_2)); - - // Value w. digest_3 was never persisted - assert_err!(store.get_batch_from_local(&digest_3)); - // Since payload is cleared, we can now persist value w. digest_3 - assert!(!store.persist(vec![request_3]).is_empty()); - assert_ok!(store.get_batch_from_local(&digest_3)); - - store.update_certified_timestamp(52); - assert_ok!(store.get_batch_from_local(&digest_2)); - assert_ok!(store.get_batch_from_local(&digest_3)); - - store.update_certified_timestamp(55); - // Expired value w. digest_2 - assert_err!(store.get_batch_from_local(&digest_2)); - assert_ok!(store.get_batch_from_local(&digest_3)); - - store.update_certified_timestamp(56); - // Expired value w. digest_3 - assert_err!(store.get_batch_from_local(&digest_1)); - assert_err!(store.get_batch_from_local(&digest_2)); - assert_err!(store.get_batch_from_local(&digest_3)); -} diff --git a/consensus/src/quorum_store/tests/direct_mempool_quorum_store_test.rs b/consensus/src/quorum_store/tests/direct_mempool_quorum_store_test.rs deleted file mode 100644 index 4c353e99bda29..0000000000000 --- a/consensus/src/quorum_store/tests/direct_mempool_quorum_store_test.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::quorum_store::direct_mempool_quorum_store::DirectMempoolQuorumStore; -use aptos_consensus_types::{ - common::PayloadFilter, - request_response::{GetPayloadCommand, GetPayloadResponse}, -}; -use aptos_mempool::{QuorumStoreRequest, QuorumStoreResponse}; -use futures::{ - channel::{mpsc, oneshot}, - StreamExt, -}; -use std::time::Duration; -use tokio::time::timeout; - -#[tokio::test(flavor = "multi_thread")] -async fn test_block_request_no_txns() { - let (quorum_store_to_mempool_sender, mut quorum_store_to_mempool_receiver) = - mpsc::channel(1_024); - let (mut consensus_to_quorum_store_sender, consensus_to_quorum_store_receiver) = - mpsc::channel(1_024); - let quorum_store = DirectMempoolQuorumStore::new( - consensus_to_quorum_store_receiver, - quorum_store_to_mempool_sender, - 10_000, - ); - let join_handle = tokio::spawn(quorum_store.start()); - - let (consensus_callback, consensus_callback_rcv) = oneshot::channel(); - consensus_to_quorum_store_sender - .try_send(GetPayloadCommand::GetPayloadRequest( - 100, - 1000, - 50, - 500, - true, - PayloadFilter::DirectMempool(vec![]), - consensus_callback, - )) - .unwrap(); - - if let QuorumStoreRequest::GetBatchRequest( - _max_batch_size, - _max_bytes, - _return_non_full, - _include_gas_upgraded, - _exclude_txns, - callback, - ) = timeout( - Duration::from_millis(1_000), - quorum_store_to_mempool_receiver.select_next_some(), - ) - .await - .unwrap() - { - callback - .send(Ok(QuorumStoreResponse::GetBatchResponse(vec![]))) - .unwrap(); - } else { - panic!("Unexpected variant") - } - - match timeout(Duration::from_millis(1_000), consensus_callback_rcv) - .await - .unwrap() - .unwrap() - .unwrap() - { - GetPayloadResponse::GetPayloadResponse(payload) => { - assert!(payload.is_empty()); - }, - } - - std::mem::drop(consensus_to_quorum_store_sender); - timeout(Duration::from_millis(1_000), join_handle) - .await - .unwrap() - .unwrap(); -} diff --git a/consensus/src/quorum_store/tests/mod.rs b/consensus/src/quorum_store/tests/mod.rs deleted file mode 100644 index 9dfc3e2930c59..0000000000000 --- a/consensus/src/quorum_store/tests/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod batch_generator_test; -mod batch_requester_test; -mod batch_store_test; -mod direct_mempool_quorum_store_test; -mod proof_coordinator_test; -mod proof_manager_test; -mod quorum_store_db_test; -mod types_test; -mod utils; diff --git a/consensus/src/quorum_store/tests/proof_coordinator_test.rs b/consensus/src/quorum_store/tests/proof_coordinator_test.rs deleted file mode 100644 index b34f10533d519..0000000000000 --- a/consensus/src/quorum_store/tests/proof_coordinator_test.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network_interface::ConsensusMsg, - quorum_store::{ - batch_store::BatchReader, - proof_coordinator::{ProofCoordinator, ProofCoordinatorCommand}, - types::Batch, - }, - test_utils::{create_vec_signed_transactions, mock_quorum_store_sender::MockQuorumStoreSender}, -}; -use aptos_consensus_types::proof_of_store::{ - BatchId, ProofOfStore, SignedBatchInfo, SignedBatchInfoMsg, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorResult; -use aptos_types::{ - transaction::SignedTransaction, validator_verifier::random_validator_verifier, PeerId, -}; -use std::sync::Arc; -use tokio::sync::{mpsc::channel, oneshot::Receiver}; - -pub struct MockBatchReader { - peer: PeerId, -} - -impl BatchReader for MockBatchReader { - fn exists(&self, _digest: &HashValue) -> Option { - Some(self.peer) - } - - fn get_batch(&self, _proof: ProofOfStore) -> Receiver>> { - unimplemented!() - } - - fn update_certified_timestamp(&self, _certified_time: u64) { - unimplemented!() - } -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_proof_coordinator_basic() { - aptos_logger::Logger::init_for_testing(); - let (signers, verifier) = random_validator_verifier(4, None, true); - let (tx, _rx) = channel(100); - let proof_coordinator = ProofCoordinator::new( - 100, - signers[0].author(), - Arc::new(MockBatchReader { - peer: signers[0].author(), - }), - tx, - true, - ); - let (proof_coordinator_tx, proof_coordinator_rx) = channel(100); - let (tx, mut rx) = channel(100); - let network_sender = MockQuorumStoreSender::new(tx); - tokio::spawn(proof_coordinator.start(proof_coordinator_rx, network_sender, verifier.clone())); - - let batch_author = signers[0].author(); - let batch_id = BatchId::new_for_test(1); - let payload = create_vec_signed_transactions(100); - let batch = Batch::new(batch_id, payload, 1, 20, batch_author, 0); - let digest = batch.digest(); - - for signer in &signers { - let signed_batch_info = SignedBatchInfo::new(batch.batch_info().clone(), signer).unwrap(); - assert!(proof_coordinator_tx - .send(ProofCoordinatorCommand::AppendSignature( - SignedBatchInfoMsg::new(vec![signed_batch_info]) - )) - .await - .is_ok()); - } - - let proof_msg = match rx.recv().await.expect("channel dropped") { - (ConsensusMsg::ProofOfStoreMsg(proof_msg), _) => *proof_msg, - msg => panic!("Expected LocalProof but received: {:?}", msg), - }; - // check normal path - assert!(proof_msg.verify(100, &verifier).is_ok()); - let proofs = proof_msg.take(); - assert_eq!(proofs[0].digest(), digest); -} diff --git a/consensus/src/quorum_store/tests/proof_manager_test.rs b/consensus/src/quorum_store/tests/proof_manager_test.rs deleted file mode 100644 index 812a854f62d9c..0000000000000 --- a/consensus/src/quorum_store/tests/proof_manager_test.rs +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::quorum_store::{ - proof_manager::ProofManager, tests::batch_store_test::batch_store_for_test, -}; -use aptos_consensus_types::{ - common::{Payload, PayloadFilter}, - proof_of_store::{BatchId, BatchInfo, ProofOfStore}, - request_response::{GetPayloadCommand, GetPayloadResponse}, -}; -use aptos_crypto::HashValue; -use aptos_types::{aggregate_signature::AggregateSignature, PeerId}; -use futures::channel::oneshot; -use std::collections::HashSet; - -fn create_proof_manager() -> ProofManager { - let batch_store = batch_store_for_test(5 * 1024 * 1024); - ProofManager::new(PeerId::random(), 10, 10, batch_store, true) -} - -fn create_proof(author: PeerId, expiration: u64, batch_sequence: u64) -> ProofOfStore { - create_proof_with_gas(author, expiration, batch_sequence, 0) -} - -fn create_proof_with_gas( - author: PeerId, - expiration: u64, - batch_sequence: u64, - gas_bucket_start: u64, -) -> ProofOfStore { - let digest = HashValue::random(); - let batch_id = BatchId::new_for_test(batch_sequence); - ProofOfStore::new( - BatchInfo::new( - author, - batch_id, - 0, - expiration, - digest, - 1, - 1, - gas_bucket_start, - ), - AggregateSignature::empty(), - ) -} - -async fn get_proposal( - proof_manager: &mut ProofManager, - max_txns: u64, - filter: &[BatchInfo], -) -> Payload { - let (callback_tx, callback_rx) = oneshot::channel(); - let filter_set = HashSet::from_iter(filter.iter().cloned()); - let req = GetPayloadCommand::GetPayloadRequest( - max_txns, - 1000000, - max_txns / 2, - 100000, - true, - PayloadFilter::InQuorumStore(filter_set), - callback_tx, - ); - proof_manager.handle_proposal_request(req); - let GetPayloadResponse::GetPayloadResponse(payload) = callback_rx.await.unwrap().unwrap(); - payload -} - -fn assert_payload_response( - payload: Payload, - expected: &[ProofOfStore], - max_txns_from_block_to_execute: Option, -) { - match payload { - Payload::InQuorumStore(proofs) => { - assert_eq!(proofs.proofs.len(), expected.len()); - for proof in proofs.proofs { - assert!(expected.contains(&proof)); - } - }, - Payload::InQuorumStoreWithLimit(proofs) => { - assert_eq!(proofs.proof_with_data.proofs.len(), expected.len()); - for proof in proofs.proof_with_data.proofs { - assert!(expected.contains(&proof)); - } - assert_eq!(proofs.max_txns_to_execute, max_txns_from_block_to_execute); - }, - Payload::QuorumStoreInlineHybrid(_inline_batches, proofs, max_txns_to_execute) => { - assert_eq!(proofs.proofs.len(), expected.len()); - for proof in proofs.proofs { - assert!(expected.contains(&proof)); - } - assert_eq!(max_txns_to_execute, max_txns_from_block_to_execute); - }, - // TODO: Check how to update this for Payload::QuorumStoreInlineHybrid - _ => panic!("Unexpected variant"), - } -} - -async fn get_proposal_and_assert( - proof_manager: &mut ProofManager, - max_txns: u64, - filter: &[BatchInfo], - expected: &[ProofOfStore], -) { - assert_payload_response( - get_proposal(proof_manager, max_txns, filter).await, - expected, - None, - ); -} - -#[tokio::test] -async fn test_block_request() { - let mut proof_manager = create_proof_manager(); - - let proof = create_proof(PeerId::random(), 10, 1); - proof_manager.receive_proofs(vec![proof.clone()]); - - get_proposal_and_assert(&mut proof_manager, 100, &[], &vec![proof]).await; -} - -#[tokio::test] -async fn test_max_txns_from_block_to_execute() { - let mut proof_manager = create_proof_manager(); - - let proof = create_proof(PeerId::random(), 10, 1); - proof_manager.receive_proofs(vec![proof.clone()]); - - let payload = get_proposal(&mut proof_manager, 100, &[]).await; - // convert payload to v2 format and assert - let max_txns_from_block_to_execute = 10; - assert_payload_response( - payload.transform_to_quorum_store_v2(Some(max_txns_from_block_to_execute)), - &vec![proof], - Some(max_txns_from_block_to_execute), - ); -} - -#[tokio::test] -async fn test_block_timestamp_expiration() { - let mut proof_manager = create_proof_manager(); - - let proof = create_proof(PeerId::random(), 10, 1); - proof_manager.receive_proofs(vec![proof.clone()]); - - proof_manager.handle_commit_notification(1, vec![]); - get_proposal_and_assert(&mut proof_manager, 100, &[], &vec![proof]).await; - - proof_manager.handle_commit_notification(20, vec![]); - get_proposal_and_assert(&mut proof_manager, 100, &[], &[]).await; -} - -#[tokio::test] -async fn test_batch_commit() { - let mut proof_manager = create_proof_manager(); - - let proof0 = create_proof(PeerId::random(), 10, 1); - proof_manager.receive_proofs(vec![proof0.clone()]); - - let proof1 = create_proof(PeerId::random(), 11, 2); - proof_manager.receive_proofs(vec![proof1.clone()]); - - proof_manager.handle_commit_notification(1, vec![proof1.info().clone()]); - get_proposal_and_assert(&mut proof_manager, 100, &[], &vec![proof0]).await; -} - -#[tokio::test] -async fn test_proposal_priority() { - let mut proof_manager = create_proof_manager(); - let peer0 = PeerId::random(); - - let peer0_proof0 = create_proof_with_gas(peer0, 10, 2, 1000); - let peer0_proof1 = create_proof_with_gas(peer0, 10, 1, 0); - proof_manager.receive_proofs(vec![peer0_proof1.clone(), peer0_proof0.clone()]); - - let peer0_proof2 = create_proof_with_gas(peer0, 10, 4, 500); - proof_manager.receive_proofs(vec![peer0_proof2.clone()]); - let peer0_proof3 = create_proof_with_gas(peer0, 10, 3, 500); - proof_manager.receive_proofs(vec![peer0_proof3.clone()]); - - // Gas bucket is the most significant prioritization - let expected = vec![peer0_proof0.clone()]; - get_proposal_and_assert(&mut proof_manager, 1, &[], &expected).await; - - // Batch sequence is prioritized next - let expected = vec![peer0_proof3.clone()]; - get_proposal_and_assert( - &mut proof_manager, - 1, - &[peer0_proof0.info().clone()], - &expected, - ) - .await; -} - -#[tokio::test] -async fn test_proposal_fairness() { - let mut proof_manager = create_proof_manager(); - let peer0 = PeerId::random(); - let peer1 = PeerId::random(); - - let mut peer0_proofs = vec![]; - for i in 0..4 { - let proof = create_proof(peer0, 10 + i, 1 + i); - proof_manager.receive_proofs(vec![proof.clone()]); - peer0_proofs.push(proof); - } - - let peer1_proof_0 = create_proof(peer1, 7, 1); - proof_manager.receive_proofs(vec![peer1_proof_0.clone()]); - - // Without filter, and large max size, all proofs are retrieved - let mut expected = peer0_proofs.clone(); - expected.push(peer1_proof_0.clone()); - get_proposal_and_assert(&mut proof_manager, 100, &[], &expected).await; - - // The first two proofs are taken fairly from each peer - get_proposal_and_assert(&mut proof_manager, 2, &[], &vec![ - peer0_proofs[0].clone(), - peer1_proof_0.clone(), - ]) - .await; - - // The next two proofs are taken from the remaining peer - let filter = vec![peer0_proofs[0].clone(), peer1_proof_0.clone()]; - let filter: Vec<_> = filter.iter().map(ProofOfStore::info).cloned().collect(); - get_proposal_and_assert(&mut proof_manager, 2, &filter, &peer0_proofs[1..3]).await; - - // The last proof is also taken from the remaining peer - let mut filter = peer0_proofs[0..3].to_vec(); - filter.push(peer1_proof_0.clone()); - let filter: Vec<_> = filter.iter().map(ProofOfStore::info).cloned().collect(); - get_proposal_and_assert(&mut proof_manager, 2, &filter, &peer0_proofs[3..4]).await; -} - -#[tokio::test] -async fn test_duplicate_batches_on_commit() { - let mut proof_manager = create_proof_manager(); - - let author = PeerId::random(); - let digest = HashValue::random(); - let batch_id = BatchId::new_for_test(1); - let batch = BatchInfo::new(author, batch_id, 0, 10, digest, 1, 1, 0); - let proof0 = ProofOfStore::new(batch.clone(), AggregateSignature::empty()); - let proof1 = ProofOfStore::new(batch.clone(), AggregateSignature::empty()); - let proof2 = ProofOfStore::new(batch.clone(), AggregateSignature::empty()); - - proof_manager.receive_proofs(vec![proof0.clone()]); - proof_manager.receive_proofs(vec![proof1.clone()]); - - // Only one copy of the batch exists - get_proposal_and_assert(&mut proof_manager, 10, &[], &vec![proof0.clone()]).await; - - // Nothing goes wrong on commits - proof_manager.handle_commit_notification(4, vec![batch.clone()]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &[]).await; - - // Before expiration, still marked as committed - proof_manager.receive_proofs(vec![proof2.clone()]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &[]).await; - - // Nothing goes wrong on expiration - proof_manager.handle_commit_notification(5, vec![]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &[]).await; - proof_manager.handle_commit_notification(12, vec![]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &[]).await; -} - -#[tokio::test] -async fn test_duplicate_batches_on_expiration() { - let mut proof_manager = create_proof_manager(); - - let author = PeerId::random(); - let digest = HashValue::random(); - let batch_id = BatchId::new_for_test(1); - let batch = BatchInfo::new(author, batch_id, 0, 10, digest, 1, 1, 0); - let proof0 = ProofOfStore::new(batch.clone(), AggregateSignature::empty()); - let proof1 = ProofOfStore::new(batch.clone(), AggregateSignature::empty()); - - proof_manager.receive_proofs(vec![proof0.clone()]); - proof_manager.receive_proofs(vec![proof1.clone()]); - - // Only one copy of the batch exists - get_proposal_and_assert(&mut proof_manager, 10, &[], &vec![proof0.clone()]).await; - - // Nothing goes wrong on expiration - proof_manager.handle_commit_notification(5, vec![]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &vec![proof0.clone()]).await; - proof_manager.handle_commit_notification(12, vec![]); - get_proposal_and_assert(&mut proof_manager, 10, &[], &[]).await; -} diff --git a/consensus/src/quorum_store/tests/quorum_store_db_test.rs b/consensus/src/quorum_store/tests/quorum_store_db_test.rs deleted file mode 100644 index 2bdc060abc4a8..0000000000000 --- a/consensus/src/quorum_store/tests/quorum_store_db_test.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - quorum_store::{ - quorum_store_db::{QuorumStoreDB, QuorumStoreStorage}, - types::{Batch, PersistedValue}, - }, - test_utils::create_vec_signed_transactions, -}; -use aptos_consensus_types::proof_of_store::BatchId; -use aptos_temppath::TempPath; -use aptos_types::account_address::AccountAddress; -use claims::assert_ok; - -#[test] -fn test_db_for_data() { - let tmp_dir = TempPath::new(); - let db = QuorumStoreDB::new(&tmp_dir); - - let source = AccountAddress::random(); - let signed_txns = create_vec_signed_transactions(100); - let persist_request_1: PersistedValue = - Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); - let clone_1 = persist_request_1.clone(); - assert!(db.save_batch(clone_1).is_ok()); - - assert_eq!( - db.get_batch(persist_request_1.digest()) - .expect("could not read from db") - .unwrap(), - persist_request_1 - ); - - let signed_txns = create_vec_signed_transactions(200); - let persist_request_2: PersistedValue = - Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); - let clone_2 = persist_request_2.clone(); - assert_ok!(db.save_batch(clone_2)); - - let signed_txns = create_vec_signed_transactions(300); - let persist_request_3: PersistedValue = - Batch::new(BatchId::new_for_test(1), signed_txns, 1, 20, source, 0).into(); - let clone_3 = persist_request_3.clone(); - assert_ok!(db.save_batch(clone_3)); - - let batches = vec![*persist_request_3.digest()]; - assert_ok!(db.delete_batches(batches)); - assert_eq!( - db.get_batch(persist_request_3.digest()) - .expect("could not read from db"), - None - ); - - let all_batches = db.get_all_batches().expect("could not read from db"); - assert_eq!(all_batches.len(), 2); - assert!(all_batches.contains_key(persist_request_1.digest())); - assert!(all_batches.contains_key(persist_request_2.digest())); -} - -#[test] -fn test_db_for_batch_id() { - let tmp_dir = TempPath::new(); - let db = QuorumStoreDB::new(&tmp_dir); - - assert!(db - .clean_and_get_batch_id(0) - .expect("could not read from db") - .is_none()); - assert_ok!(db.save_batch_id(0, BatchId::new_for_test(0))); - assert_ok!(db.save_batch_id(0, BatchId::new_for_test(4))); - assert_eq!( - db.clean_and_get_batch_id(0) - .expect("could not read from db") - .unwrap(), - BatchId::new_for_test(4) - ); - assert_ok!(db.save_batch_id(1, BatchId::new_for_test(1))); - assert_ok!(db.save_batch_id(2, BatchId::new_for_test(2))); - assert_eq!( - db.clean_and_get_batch_id(2) - .expect("could not read from db") - .unwrap(), - BatchId::new_for_test(2) - ); -} diff --git a/consensus/src/quorum_store/tests/types_test.rs b/consensus/src/quorum_store/tests/types_test.rs deleted file mode 100644 index 81bafe86959fd..0000000000000 --- a/consensus/src/quorum_store/tests/types_test.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - quorum_store::types::{Batch, BatchRequest}, - test_utils::create_vec_signed_transactions, -}; -use aptos_consensus_types::{common::BatchPayload, proof_of_store::BatchId}; -use aptos_crypto::{hash::CryptoHash, HashValue}; -use aptos_types::account_address::AccountAddress; -use claims::{assert_err, assert_ok}; - -#[test] -fn test_batch() { - let epoch = 0; - let source = AccountAddress::random(); - let signed_txns = create_vec_signed_transactions(500); - - let payload = BatchPayload::new(source, signed_txns.clone()); - let digest = payload.hash(); - - let batch_request = BatchRequest::new(source, epoch, digest); - - assert_eq!(epoch, batch_request.epoch()); - assert!(batch_request.verify(source).is_ok()); - - let batch = Batch::new( - BatchId::new_for_test(1), - signed_txns.clone(), - epoch, - 1, - source, - 0, - ); - - assert_ok!(batch.verify()); - assert_ok!(batch.verify_with_digest(digest)); - // verify should fail if the digest does not match. - assert_err!(batch.verify_with_digest(HashValue::random())); - - assert_eq!(batch.into_transactions(), signed_txns); -} diff --git a/consensus/src/quorum_store/tests/utils.rs b/consensus/src/quorum_store/tests/utils.rs deleted file mode 100644 index 922ae1d67a3af..0000000000000 --- a/consensus/src/quorum_store/tests/utils.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::quorum_store::utils::ProofQueue; -use aptos_consensus_types::proof_of_store::{BatchId, BatchInfo, ProofOfStore}; -use aptos_crypto::HashValue; -use aptos_types::{aggregate_signature::AggregateSignature, PeerId}; -use maplit::hashset; - -/// Return a ProofOfStore with minimal fields used by ProofQueue tests. -fn proof_of_store(author: PeerId, batch_id: BatchId, gas_bucket_start: u64) -> ProofOfStore { - ProofOfStore::new( - BatchInfo::new( - author, - batch_id, - 0, - 0, - HashValue::random(), - 1, - 1, - gas_bucket_start, - ), - AggregateSignature::empty(), - ) -} - -#[test] -fn test_proof_queue_sorting() { - let my_peer_id = PeerId::random(); - let mut proof_queue = ProofQueue::new(my_peer_id); - - let author_0 = PeerId::random(); - let author_1 = PeerId::random(); - - let author_0_batches = vec![ - proof_of_store(author_0, BatchId::new_for_test(0), 100), - proof_of_store(author_0, BatchId::new_for_test(1), 200), - proof_of_store(author_0, BatchId::new_for_test(2), 50), - proof_of_store(author_0, BatchId::new_for_test(3), 300), - ]; - for batch in author_0_batches { - proof_queue.push(batch); - } - let author_1_batches = vec![ - proof_of_store(author_1, BatchId::new_for_test(4), 500), - proof_of_store(author_1, BatchId::new_for_test(5), 400), - proof_of_store(author_1, BatchId::new_for_test(6), 600), - proof_of_store(author_1, BatchId::new_for_test(7), 50), - ]; - for batch in author_1_batches { - proof_queue.push(batch); - } - - // Expect: [600, 300] - let (pulled, _) = proof_queue.pull_proofs(&hashset![], 2, 2, true); - let mut count_author_0 = 0; - let mut count_author_1 = 0; - let mut prev: Option<&ProofOfStore> = None; - for batch in &pulled { - if let Some(prev) = prev { - assert!(prev.gas_bucket_start() >= batch.gas_bucket_start()); - } else { - assert_eq!(batch.gas_bucket_start(), 600); - } - if batch.author() == author_0 { - count_author_0 += 1; - } else { - count_author_1 += 1; - } - prev = Some(batch); - } - assert_eq!(count_author_0, 1); - assert_eq!(count_author_1, 1); - - // Expect: [600, 500, 300, 100] - let (pulled, _) = proof_queue.pull_proofs(&hashset![], 4, 4, true); - let mut count_author_0 = 0; - let mut count_author_1 = 0; - let mut prev: Option<&ProofOfStore> = None; - for batch in &pulled { - if let Some(prev) = prev { - assert!(prev.gas_bucket_start() >= batch.gas_bucket_start()); - } else { - assert_eq!(batch.gas_bucket_start(), 600); - } - if batch.author() == author_0 { - count_author_0 += 1; - } else { - count_author_1 += 1; - } - prev = Some(batch); - } - assert_eq!(count_author_0, 2); - assert_eq!(count_author_1, 2); -} diff --git a/consensus/src/quorum_store/types.rs b/consensus/src/quorum_store/types.rs deleted file mode 100644 index ae8f960cef920..0000000000000 --- a/consensus/src/quorum_store/types.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::ensure; -use aptos_consensus_types::{ - common::BatchPayload, - proof_of_store::{BatchId, BatchInfo}, -}; -use aptos_crypto::{hash::CryptoHash, HashValue}; -use aptos_types::{ledger_info::LedgerInfoWithSignatures, transaction::SignedTransaction, PeerId}; -use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Display, Formatter}, - ops::Deref, -}; - -#[derive(Clone, Eq, Deserialize, Serialize, PartialEq, Debug)] -pub struct PersistedValue { - info: BatchInfo, - maybe_payload: Option>, -} - -#[derive(PartialEq, Debug)] -pub(crate) enum StorageMode { - PersistedOnly, - MemoryAndPersisted, -} - -impl PersistedValue { - pub(crate) fn new(info: BatchInfo, maybe_payload: Option>) -> Self { - Self { - info, - maybe_payload, - } - } - - pub(crate) fn payload_storage_mode(&self) -> StorageMode { - match self.maybe_payload { - Some(_) => StorageMode::MemoryAndPersisted, - None => StorageMode::PersistedOnly, - } - } - - pub(crate) fn take_payload(&mut self) -> Option> { - self.maybe_payload.take() - } - - pub(crate) fn remove_payload(&mut self) { - self.maybe_payload = None; - } - - pub fn batch_info(&self) -> &BatchInfo { - &self.info - } - - pub fn payload(&self) -> &Option> { - &self.maybe_payload - } -} - -impl Deref for PersistedValue { - type Target = BatchInfo; - - fn deref(&self) -> &Self::Target { - &self.info - } -} - -impl TryFrom for Batch { - type Error = anyhow::Error; - - fn try_from(value: PersistedValue) -> Result { - let author = value.author(); - Ok(Batch { - batch_info: value.info, - payload: BatchPayload::new( - author, - value - .maybe_payload - .ok_or_else(|| anyhow::anyhow!("Payload not exist"))?, - ), - }) - } -} - -#[cfg(test)] -mod tests { - use aptos_config::config; - - #[test] - fn test_batch_payload_padding() { - use super::*; - let empty_batch_payload = BatchPayload::new(PeerId::random(), vec![]); - // We overestimate the ULEB128 encoding of the number of transactions as 128 bytes. - assert_eq!( - empty_batch_payload.num_bytes() + 127, - config::BATCH_PADDING_BYTES - ); - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Batch { - batch_info: BatchInfo, - payload: BatchPayload, -} - -impl Batch { - pub fn new( - batch_id: BatchId, - payload: Vec, - epoch: u64, - expiration: u64, - batch_author: PeerId, - gas_bucket_start: u64, - ) -> Self { - let payload = BatchPayload::new(batch_author, payload); - let batch_info = BatchInfo::new( - batch_author, - batch_id, - epoch, - expiration, - payload.hash(), - payload.num_txns() as u64, - payload.num_bytes() as u64, - gas_bucket_start, - ); - Self { - batch_info, - payload, - } - } - - pub fn verify(&self) -> anyhow::Result<()> { - ensure!( - self.payload.author() == self.author(), - "Payload author doesn't match the info" - ); - ensure!( - self.payload.hash() == *self.digest(), - "Payload hash doesn't match the digest" - ); - ensure!( - self.payload.num_txns() as u64 == self.num_txns(), - "Payload num txns doesn't match batch info" - ); - ensure!( - self.payload.num_bytes() as u64 == self.num_bytes(), - "Payload num bytes doesn't match batch info" - ); - for txn in self.payload.txns() { - ensure!( - txn.gas_unit_price() >= self.gas_bucket_start(), - "Payload gas unit price doesn't match batch info" - ) - } - Ok(()) - } - - /// Verify the batch, and that it matches the requested digest - pub fn verify_with_digest(&self, requested_digest: HashValue) -> anyhow::Result<()> { - ensure!( - requested_digest == *self.digest(), - "Response digest doesn't match the request" - ); - self.verify()?; - Ok(()) - } - - pub fn into_transactions(self) -> Vec { - self.payload.into_transactions() - } - - pub fn batch_info(&self) -> &BatchInfo { - &self.batch_info - } -} - -impl Deref for Batch { - type Target = BatchInfo; - - fn deref(&self) -> &Self::Target { - &self.batch_info - } -} - -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] -pub struct BatchRequest { - epoch: u64, - source: PeerId, - digest: HashValue, -} - -impl Display for BatchRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "BatchRequest: epoch: {}, source: {}, digest {}", - self.epoch, self.source, self.digest - ) - } -} - -impl BatchRequest { - pub fn new(source: PeerId, epoch: u64, digest: HashValue) -> Self { - Self { - epoch, - source, - digest, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn verify(&self, peer_id: PeerId) -> anyhow::Result<()> { - if self.source == peer_id { - Ok(()) - } else { - Err(anyhow::anyhow!( - "Sender mismatch: peer_id: {}, source: {}", - self.source, - peer_id - )) - } - } - - pub fn source(&self) -> PeerId { - self.source - } - - pub fn digest(&self) -> HashValue { - self.digest - } -} - -impl From for PersistedValue { - fn from(value: Batch) -> Self { - let Batch { - batch_info, - payload, - } = value; - PersistedValue::new(batch_info, Some(payload.into_transactions())) - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum BatchResponse { - Batch(Batch), - NotFound(LedgerInfoWithSignatures), -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BatchMsg { - batches: Vec, -} - -impl BatchMsg { - pub fn new(batches: Vec) -> Self { - Self { batches } - } - - pub fn verify(&self, peer_id: PeerId, max_num_batches: usize) -> anyhow::Result<()> { - ensure!(!self.batches.is_empty(), "Empty message"); - ensure!( - self.batches.len() <= max_num_batches, - "Too many batches: {} > {}", - self.batches.len(), - max_num_batches - ); - for batch in self.batches.iter() { - ensure!( - batch.author() == peer_id, - "Batch author doesn't match sender" - ); - batch.verify()? - } - Ok(()) - } - - pub fn epoch(&self) -> anyhow::Result { - ensure!(!self.batches.is_empty(), "Empty message"); - let epoch = self.batches[0].epoch(); - for batch in self.batches.iter() { - ensure!( - batch.epoch() == epoch, - "Epoch mismatch: {} != {}", - batch.epoch(), - epoch - ); - } - Ok(epoch) - } - - pub fn author(&self) -> PeerId { - self.batches[0].author() - } - - pub fn take(self) -> Vec { - self.batches - } -} diff --git a/consensus/src/quorum_store/utils.rs b/consensus/src/quorum_store/utils.rs deleted file mode 100644 index edbf63a901461..0000000000000 --- a/consensus/src/quorum_store/utils.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{monitor, quorum_store::counters}; -use aptos_consensus_types::{ - common::{TransactionInProgress, TransactionSummary}, - proof_of_store::{BatchId, BatchInfo, ProofOfStore}, -}; -use aptos_logger::prelude::*; -use aptos_mempool::{QuorumStoreRequest, QuorumStoreResponse}; -use aptos_types::{transaction::SignedTransaction, PeerId}; -use chrono::Utc; -use futures::channel::{mpsc::Sender, oneshot}; -use move_core_types::account_address::AccountAddress; -use rand::{seq::SliceRandom, thread_rng}; -use std::{ - cmp::{Ordering, Reverse}, - collections::{BTreeMap, BinaryHeap, HashMap, HashSet, VecDeque}, - hash::Hash, - time::{Duration, Instant}, -}; -use tokio::time::timeout; - -pub(crate) struct Timeouts { - timeouts: VecDeque<(i64, T)>, -} - -impl Timeouts { - pub(crate) fn new() -> Self { - Self { - timeouts: VecDeque::new(), - } - } - - pub(crate) fn add(&mut self, value: T, timeout: usize) { - let expiry = Utc::now().naive_utc().timestamp_millis() + timeout as i64; - self.timeouts.push_back((expiry, value)); - } - - pub(crate) fn expire(&mut self) -> Vec { - let cur_time = chrono::Utc::now().naive_utc().timestamp_millis(); - trace!( - "QS: expire cur time {} timeouts len {}", - cur_time, - self.timeouts.len() - ); - let num_expired = self - .timeouts - .iter() - .take_while(|(expiration_time, _)| cur_time >= *expiration_time) - .count(); - - self.timeouts - .drain(0..num_expired) - .map(|(_, h)| h) - .collect() - } -} - -pub(crate) struct TimeExpirations { - expiries: BinaryHeap<(Reverse, I)>, -} - -impl TimeExpirations { - pub(crate) fn new() -> Self { - Self { - expiries: BinaryHeap::new(), - } - } - - pub(crate) fn add_item(&mut self, item: I, expiry_time: u64) { - self.expiries.push((Reverse(expiry_time), item)); - } - - /// Expire and return items corresponding to expiration <= given certified time. - pub(crate) fn expire(&mut self, certified_time: u64) -> HashSet { - let mut ret = HashSet::new(); - while let Some((Reverse(t), _)) = self.expiries.peek() { - if *t <= certified_time { - let (_, item) = self.expiries.pop().unwrap(); - ret.insert(item); - } else { - break; - } - } - ret - } -} - -pub struct MempoolProxy { - mempool_tx: Sender, - mempool_txn_pull_timeout_ms: u64, -} - -impl MempoolProxy { - pub fn new(mempool_tx: Sender, mempool_txn_pull_timeout_ms: u64) -> Self { - Self { - mempool_tx, - mempool_txn_pull_timeout_ms, - } - } - - pub async fn pull_internal( - &self, - max_items: u64, - max_bytes: u64, - exclude_transactions: BTreeMap, - ) -> Result, anyhow::Error> { - let (callback, callback_rcv) = oneshot::channel(); - let msg = QuorumStoreRequest::GetBatchRequest( - max_items, - max_bytes, - true, - true, - exclude_transactions, - callback, - ); - self.mempool_tx - .clone() - .try_send(msg) - .map_err(anyhow::Error::from)?; - // wait for response - match monitor!( - "pull_txn", - timeout( - Duration::from_millis(self.mempool_txn_pull_timeout_ms), - callback_rcv - ) - .await - ) { - Err(_) => Err(anyhow::anyhow!( - "[quorum_store] did not receive GetBatchResponse on time" - )), - Ok(resp) => match resp.map_err(anyhow::Error::from)?? { - QuorumStoreResponse::GetBatchResponse(txns) => Ok(txns), - _ => Err(anyhow::anyhow!( - "[quorum_store] did not receive expected GetBatchResponse" - )), - }, - } - } -} - -#[derive(PartialEq, Eq, Hash, Clone)] -pub struct BatchKey { - author: PeerId, - batch_id: BatchId, -} - -impl BatchKey { - pub fn from_info(info: &BatchInfo) -> Self { - Self { - author: info.author(), - batch_id: info.batch_id(), - } - } -} - -#[derive(PartialEq, Eq, Clone, Hash)] -pub struct BatchSortKey { - batch_key: BatchKey, - gas_bucket_start: u64, -} - -impl BatchSortKey { - pub fn from_info(info: &BatchInfo) -> Self { - Self { - batch_key: BatchKey::from_info(info), - gas_bucket_start: info.gas_bucket_start(), - } - } - - pub fn author(&self) -> PeerId { - self.batch_key.author - } -} - -impl PartialOrd for BatchSortKey { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for BatchSortKey { - fn cmp(&self, other: &Self) -> Ordering { - // ascending - match self.gas_bucket_start.cmp(&other.gas_bucket_start) { - Ordering::Equal => {}, - ordering => return ordering, - } - // descending - other.batch_key.batch_id.cmp(&self.batch_key.batch_id) - } -} - -pub struct ProofQueue { - my_peer_id: PeerId, - // Queue per peer to ensure fairness between peers and priority within peer - author_to_batches: HashMap>, - // ProofOfStore and insertion_time. None if committed - batch_to_proof: HashMap>, - // Expiration index - expirations: TimeExpirations, - latest_block_timestamp: u64, - remaining_txns: u64, - remaining_proofs: u64, - remaining_local_txns: u64, - remaining_local_proofs: u64, -} - -impl ProofQueue { - pub(crate) fn new(my_peer_id: PeerId) -> Self { - Self { - my_peer_id, - author_to_batches: HashMap::new(), - batch_to_proof: HashMap::new(), - expirations: TimeExpirations::new(), - latest_block_timestamp: 0, - remaining_txns: 0, - remaining_proofs: 0, - remaining_local_txns: 0, - remaining_local_proofs: 0, - } - } - - #[inline] - fn inc_remaining(&mut self, author: &AccountAddress, num_txns: u64) { - self.remaining_txns += num_txns; - self.remaining_proofs += 1; - if *author == self.my_peer_id { - self.remaining_local_txns += num_txns; - self.remaining_local_proofs += 1; - } - } - - #[inline] - fn dec_remaining(&mut self, author: &AccountAddress, num_txns: u64) { - self.remaining_txns -= num_txns; - self.remaining_proofs -= 1; - if *author == self.my_peer_id { - self.remaining_local_txns -= num_txns; - self.remaining_local_proofs -= 1; - } - } - - pub(crate) fn push(&mut self, proof: ProofOfStore) { - if proof.expiration() < self.latest_block_timestamp { - counters::inc_rejected_pos_count(counters::POS_EXPIRED_LABEL); - return; - } - let batch_key = BatchKey::from_info(proof.info()); - if self.batch_to_proof.get(&batch_key).is_some() { - counters::inc_rejected_pos_count(counters::POS_DUPLICATE_LABEL); - return; - } - - let author = proof.author(); - let bucket = proof.gas_bucket_start(); - let num_txns = proof.num_txns(); - let expiration = proof.expiration(); - - let batch_sort_key = BatchSortKey::from_info(proof.info()); - let queue = self.author_to_batches.entry(author).or_default(); - queue.insert(batch_sort_key.clone(), proof.info().clone()); - self.expirations.add_item(batch_sort_key, expiration); - self.batch_to_proof - .insert(batch_key, Some((proof, Instant::now()))); - - if author == self.my_peer_id { - counters::inc_local_pos_count(bucket); - } else { - counters::inc_remote_pos_count(bucket); - } - - self.inc_remaining(&author, num_txns); - } - - // gets excluded and iterates over the vector returning non excluded or expired entries. - // return the vector of pulled PoS, and the size of the remaining PoS - // The flag in the second return argument is true iff the entire proof queue is fully utilized - // when pulling the proofs. If any proof from proof queue cannot be included due to size limits, - // this flag is set false. - pub(crate) fn pull_proofs( - &mut self, - excluded_batches: &HashSet, - max_txns: u64, - max_bytes: u64, - return_non_full: bool, - ) -> (Vec, bool) { - let mut ret = vec![]; - let mut cur_bytes = 0; - let mut cur_txns = 0; - let mut excluded_txns = 0; - let mut full = false; - - let mut iters = vec![]; - for (_, batches) in self.author_to_batches.iter() { - iters.push(batches.iter().rev()); - } - - while !iters.is_empty() { - iters.shuffle(&mut thread_rng()); - iters.retain_mut(|iter| { - if full { - return false; - } - if let Some((sort_key, batch)) = iter.next() { - if excluded_batches.contains(batch) { - excluded_txns += batch.num_txns(); - } else if let Some(Some((proof, insertion_time))) = - self.batch_to_proof.get(&sort_key.batch_key) - { - cur_bytes += batch.num_bytes(); - cur_txns += batch.num_txns(); - if cur_bytes > max_bytes || cur_txns > max_txns { - // Exceeded the limit for requested bytes or number of transactions. - full = true; - return false; - } - let bucket = proof.gas_bucket_start(); - ret.push(proof.clone()); - counters::pos_to_pull(bucket, insertion_time.elapsed().as_secs_f64()); - if cur_bytes == max_bytes || cur_txns == max_txns { - // Exactly the limit for requested bytes or number of transactions. - full = true; - return false; - } - } - true - } else { - false - } - }) - } - info!( - // before non full check - byte_size = cur_bytes, - block_size = cur_txns, - batch_count = ret.len(), - full = full, - return_non_full = return_non_full, - "Pull payloads from QuorumStore: internal" - ); - - if full || return_non_full { - counters::BLOCK_SIZE_WHEN_PULL.observe(cur_txns as f64); - counters::BLOCK_BYTES_WHEN_PULL.observe(cur_bytes as f64); - counters::PROOF_SIZE_WHEN_PULL.observe(ret.len() as f64); - counters::EXCLUDED_TXNS_WHEN_PULL.observe(excluded_txns as f64); - // Stable sort, so the order of proofs within an author will not change. - ret.sort_by_key(|proof| Reverse(proof.gas_bucket_start())); - (ret, !full) - } else { - (Vec::new(), !full) - } - } - - pub(crate) fn handle_updated_block_timestamp(&mut self, block_timestamp: u64) { - assert!( - self.latest_block_timestamp <= block_timestamp, - "Decreasing block timestamp" - ); - self.latest_block_timestamp = block_timestamp; - - let expired = self.expirations.expire(block_timestamp); - let mut num_expired_but_not_committed = 0; - for key in &expired { - if let Some(mut queue) = self.author_to_batches.remove(&key.author()) { - if let Some(batch) = queue.remove(key) { - if self - .batch_to_proof - .get(&key.batch_key) - .expect("Entry for unexpired batch must exist") - .is_some() - { - // non-committed proof that is expired - num_expired_but_not_committed += 1; - counters::GAP_BETWEEN_BATCH_EXPIRATION_AND_CURRENT_TIME_WHEN_COMMIT - .observe((block_timestamp - batch.expiration()) as f64); - self.dec_remaining(&batch.author(), batch.num_txns()); - } - claims::assert_some!(self.batch_to_proof.remove(&key.batch_key)); - } - if !queue.is_empty() { - self.author_to_batches.insert(key.author(), queue); - } - } - } - counters::NUM_PROOFS_EXPIRED_WHEN_COMMIT.inc_by(num_expired_but_not_committed); - } - - pub(crate) fn remaining_txns_and_proofs(&self) -> (u64, u64) { - counters::NUM_TOTAL_TXNS_LEFT_ON_UPDATE.observe(self.remaining_txns as f64); - counters::NUM_TOTAL_PROOFS_LEFT_ON_UPDATE.observe(self.remaining_proofs as f64); - counters::NUM_LOCAL_TXNS_LEFT_ON_UPDATE.observe(self.remaining_local_txns as f64); - counters::NUM_LOCAL_PROOFS_LEFT_ON_UPDATE.observe(self.remaining_local_proofs as f64); - - (self.remaining_txns, self.remaining_proofs) - } - - // Mark in the hashmap committed PoS, but keep them until they expire - pub(crate) fn mark_committed(&mut self, batches: Vec) { - for batch in batches { - let batch_key = BatchKey::from_info(&batch); - if let Some(Some((proof, insertion_time))) = self.batch_to_proof.get(&batch_key) { - counters::pos_to_commit( - proof.gas_bucket_start(), - insertion_time.elapsed().as_secs_f64(), - ); - self.dec_remaining(&batch.author(), batch.num_txns()); - } - self.batch_to_proof.insert(batch_key, None); - } - } -} diff --git a/consensus/src/rand/dkg/mod.rs b/consensus/src/rand/dkg/mod.rs deleted file mode 100644 index 7f1658608ae14..0000000000000 --- a/consensus/src/rand/dkg/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 diff --git a/consensus/src/rand/mod.rs b/consensus/src/rand/mod.rs deleted file mode 100644 index 6762a2f2b3a7a..0000000000000 --- a/consensus/src/rand/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![allow(dead_code)] - -pub mod dkg; -pub mod rand_gen; diff --git a/consensus/src/rand/rand_gen/aug_data_store.rs b/consensus/src/rand/rand_gen/aug_data_store.rs deleted file mode 100644 index 5723a60ee7231..0000000000000 --- a/consensus/src/rand/rand_gen/aug_data_store.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::rand::rand_gen::{ - storage::interface::RandStorage, - types::{ - AugData, AugDataId, AugDataSignature, CertifiedAugData, CertifiedAugDataAck, RandConfig, - TAugmentedData, - }, -}; -use anyhow::ensure; -use aptos_consensus_types::common::Author; -use aptos_logger::error; -use aptos_types::validator_signer::ValidatorSigner; -use std::{collections::HashMap, sync::Arc}; - -pub struct AugDataStore { - epoch: u64, - signer: Arc, - config: RandConfig, - data: HashMap>, - certified_data: HashMap>, - db: Arc>, -} - -impl AugDataStore { - fn filter_by_epoch( - epoch: u64, - all_data: impl Iterator, - ) -> (Vec, Vec<(AugDataId, T)>) { - let mut to_remove = vec![]; - let mut to_keep = vec![]; - for (id, data) in all_data { - if id.epoch() != epoch { - to_remove.push(data) - } else { - to_keep.push((id, data)) - } - } - (to_remove, to_keep) - } - - pub fn new( - epoch: u64, - signer: Arc, - config: RandConfig, - db: Arc>, - ) -> Self { - let all_data = db.get_all_aug_data().unwrap_or_default(); - let (to_remove, aug_data) = Self::filter_by_epoch(epoch, all_data.into_iter()); - if let Err(e) = db.remove_aug_data(to_remove) { - error!("[AugDataStore] failed to remove aug data: {:?}", e); - } - - let all_certified_data = db.get_all_certified_aug_data().unwrap_or_default(); - let (to_remove, certified_data) = - Self::filter_by_epoch(epoch, all_certified_data.into_iter()); - if let Err(e) = db.remove_certified_aug_data(to_remove) { - error!( - "[AugDataStore] failed to remove certified aug data: {:?}", - e - ); - } - - for (_, certified_data) in &certified_data { - certified_data - .data() - .augment(&config, certified_data.author()); - } - - Self { - epoch, - signer, - config, - data: aug_data - .into_iter() - .map(|(id, data)| (id.author(), data)) - .collect(), - certified_data: certified_data - .into_iter() - .map(|(id, data)| (id.author(), data)) - .collect(), - db, - } - } - - pub fn get_my_aug_data(&self) -> Option> { - self.data.get(&self.config.author()).cloned() - } - - pub fn get_my_certified_aug_data(&self) -> Option> { - self.certified_data.get(&self.config.author()).cloned() - } - - pub fn add_aug_data(&mut self, data: AugData) -> anyhow::Result { - if let Some(existing_data) = self.data.get(data.author()) { - ensure!( - existing_data == &data, - "[AugDataStore] equivocate data from {}", - data.author() - ); - } else { - self.db.save_aug_data(&data)?; - } - let sig = AugDataSignature::new(self.epoch, self.signer.sign(&data)?); - self.data.insert(*data.author(), data); - Ok(sig) - } - - pub fn add_certified_aug_data( - &mut self, - certified_data: CertifiedAugData, - ) -> anyhow::Result { - if self.certified_data.contains_key(certified_data.author()) { - return Ok(CertifiedAugDataAck::new(self.epoch)); - } - self.db.save_certified_aug_data(&certified_data)?; - certified_data - .data() - .augment(&self.config, certified_data.author()); - self.certified_data - .insert(*certified_data.author(), certified_data); - Ok(CertifiedAugDataAck::new(self.epoch)) - } -} diff --git a/consensus/src/rand/rand_gen/block_queue.rs b/consensus/src/rand/rand_gen/block_queue.rs deleted file mode 100644 index 0c5b316829e6b..0000000000000 --- a/consensus/src/rand/rand_gen/block_queue.rs +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - pipeline::buffer_manager::OrderedBlocks, -}; -use aptos_consensus_types::{common::Round, pipelined_block::PipelinedBlock}; -use aptos_reliable_broadcast::DropGuard; -use aptos_types::randomness::{RandMetadata, Randomness}; -use std::collections::{BTreeMap, HashMap}; - -/// Maintain the ordered blocks received from consensus and corresponding randomness -pub struct QueueItem { - ordered_blocks: OrderedBlocks, - offsets_by_round: HashMap, - num_undecided_blocks: usize, - broadcast_handle: Option>, -} - -impl QueueItem { - pub fn new(ordered_blocks: OrderedBlocks, broadcast_handle: Option>) -> Self { - let len = ordered_blocks.ordered_blocks.len(); - assert!(len > 0); - let offsets_by_round: HashMap = ordered_blocks - .ordered_blocks - .iter() - .enumerate() - .map(|(idx, b)| (b.round(), idx)) - .collect(); - Self { - ordered_blocks, - offsets_by_round, - num_undecided_blocks: len, - broadcast_handle, - } - } - - pub fn num_blocks(&self) -> usize { - self.blocks().len() - } - - pub fn first_round(&self) -> u64 { - self.blocks().first().unwrap().block().round() - } - - pub fn offset(&self, round: Round) -> usize { - *self.offsets_by_round.get(&round).unwrap() - } - - pub fn num_undecided(&self) -> usize { - self.num_undecided_blocks - } - - pub fn all_rand_metadata(&self) -> Vec { - self.blocks() - .iter() - .map(|block| RandMetadata::from(block.block())) - .collect() - } - - pub fn set_randomness(&mut self, round: Round, rand: Randomness) -> bool { - let offset = self.offset(round); - if !self.blocks()[offset].has_randomness() { - observe_block( - self.blocks()[offset].timestamp_usecs(), - BlockStage::RAND_ADD_DECISION, - ); - self.blocks_mut()[offset].set_randomness(rand); - self.num_undecided_blocks -= 1; - true - } else { - false - } - } - - fn blocks(&self) -> &[PipelinedBlock] { - &self.ordered_blocks.ordered_blocks - } - - fn blocks_mut(&mut self) -> &mut [PipelinedBlock] { - &mut self.ordered_blocks.ordered_blocks - } -} - -/// Maintain ordered blocks that have pending randomness -pub struct BlockQueue { - queue: BTreeMap, -} -impl BlockQueue { - pub fn new() -> Self { - Self { - queue: BTreeMap::new(), - } - } - - pub fn queue(&self) -> &BTreeMap { - &self.queue - } - - pub fn push_back(&mut self, item: QueueItem) { - for block in item.blocks() { - observe_block(block.timestamp_usecs(), BlockStage::RAND_ENTER); - } - assert!(self.queue.insert(item.first_round(), item).is_none()); - } - - /// Dequeue all ordered blocks prefix that have randomness - pub fn dequeue_rand_ready_prefix(&mut self) -> Vec { - let mut rand_ready_prefix = vec![]; - while let Some((_starting_round, item)) = self.queue.first_key_value() { - if item.num_undecided() == 0 { - let (_, item) = self.queue.pop_first().unwrap(); - for block in item.blocks() { - observe_block(block.timestamp_usecs(), BlockStage::RAND_READY); - } - let QueueItem { ordered_blocks, .. } = item; - debug_assert!(ordered_blocks - .ordered_blocks - .iter() - .all(|block| block.has_randomness())); - rand_ready_prefix.push(ordered_blocks); - } else { - break; - } - } - rand_ready_prefix - } - - /// Return the `QueueItem` that contains the given round, if exists. - pub fn item_mut(&mut self, round: Round) -> Option<&mut QueueItem> { - self.queue - .range_mut(0..=round) - .last() - .map(|(_, item)| item) - .filter(|item| item.offsets_by_round.contains_key(&round)) - } - - /// Update the corresponding block's randomness, return true if updated successfully - pub fn set_randomness(&mut self, round: Round, randomness: Randomness) -> bool { - if let Some(item) = self.item_mut(round) { - item.set_randomness(round, randomness) - } else { - false - } - } -} - -#[cfg(test)] -mod tests { - use crate::rand::rand_gen::{ - block_queue::{BlockQueue, QueueItem}, - test_utils::create_ordered_blocks, - }; - use aptos_types::randomness::Randomness; - use std::collections::HashSet; - - #[test] - fn test_queue_item() { - let single_round = vec![1]; - let mut item = QueueItem::new(create_ordered_blocks(single_round), None); - assert_eq!(item.num_blocks(), 1); - assert_eq!(item.offset(1), 0); - assert_eq!(item.num_undecided(), 1); - item.set_randomness(1, Randomness::default()); - assert_eq!(item.num_undecided(), 0); - - let multiple_rounds = vec![1, 2, 3, 5, 8, 13, 21, 34]; - let mut item = QueueItem::new(create_ordered_blocks(multiple_rounds.clone()), None); - assert_eq!(item.num_blocks(), multiple_rounds.len()); - assert_eq!(item.num_undecided(), item.num_blocks()); - for (idx, round) in multiple_rounds.iter().enumerate() { - assert_eq!(item.offset(*round), idx); - assert!(item.set_randomness(*round, Randomness::default())); - // double update doesn't reduce the count - assert!(!item.set_randomness(*round, Randomness::default())); - assert_eq!(item.num_undecided(), item.num_blocks() - idx - 1); - } - } - - #[test] - fn test_block_queue() { - let mut queue = BlockQueue::new(); - let all_rounds = vec![vec![1], vec![2, 3], vec![5, 8, 13], vec![21, 34, 55]]; - for rounds in &all_rounds { - queue.push_back(QueueItem::new(create_ordered_blocks(rounds.clone()), None)); - } - - let exists_rounds: HashSet<_> = all_rounds.iter().flatten().collect(); - - // find the right item - for round in 0..100 { - assert_eq!( - queue - .item_mut(round) - .map_or(false, |item| item.offsets_by_round.contains_key(&round)), - exists_rounds.contains(&round) - ); - } - - // update non existing round - assert!(!queue.set_randomness(10, Randomness::default())); - - // dequeue first ready one - assert!(queue.set_randomness(1, Randomness::default())); - // update twice - assert!(!queue.set_randomness(1, Randomness::default())); - assert_eq!(queue.dequeue_rand_ready_prefix().len(), 1); - - // not dequeue undecided batch - queue.set_randomness(2, Randomness::default()); - assert_eq!(queue.dequeue_rand_ready_prefix().len(), 0); - - // not dequeue undecided prefix - queue.set_randomness(5, Randomness::default()); - queue.set_randomness(8, Randomness::default()); - queue.set_randomness(13, Randomness::default()); - assert_eq!(queue.dequeue_rand_ready_prefix().len(), 0); - - queue.set_randomness(3, Randomness::default()); - assert_eq!(queue.dequeue_rand_ready_prefix().len(), 2); - - assert_eq!(queue.queue.len(), 1); - } -} diff --git a/consensus/src/rand/rand_gen/mod.rs b/consensus/src/rand/rand_gen/mod.rs deleted file mode 100644 index 0127c320dc217..0000000000000 --- a/consensus/src/rand/rand_gen/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -mod test_utils; - -pub mod block_queue; -pub mod network_messages; -pub mod rand_store; -pub mod types; - -pub mod aug_data_store; -pub mod rand_manager; -pub mod reliable_broadcast_state; -pub mod storage; diff --git a/consensus/src/rand/rand_gen/network_messages.rs b/consensus/src/rand/rand_gen/network_messages.rs deleted file mode 100644 index 4477d0b49c955..0000000000000 --- a/consensus/src/rand/rand_gen/network_messages.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::TConsensusMsg, - network_interface::ConsensusMsg, - rand::rand_gen::types::{ - AugData, AugDataSignature, CertifiedAugData, CertifiedAugDataAck, RandConfig, RandShare, - RequestShare, TAugmentedData, TShare, - }, -}; -use anyhow::bail; -use aptos_consensus_types::common::Author; -use aptos_enum_conversion_derive::EnumConversion; -use aptos_network::{protocols::network::RpcError, ProtocolId}; -use aptos_reliable_broadcast::RBMessage; -use aptos_types::epoch_state::EpochState; -use bytes::Bytes; -use futures_channel::oneshot; -use serde::{Deserialize, Serialize}; -use std::cmp::min; - -#[derive(Clone, Serialize, Deserialize, EnumConversion)] -pub enum RandMessage { - RequestShare(RequestShare), - Share(RandShare), - AugData(AugData), - AugDataSignature(AugDataSignature), - CertifiedAugData(CertifiedAugData), - CertifiedAugDataAck(CertifiedAugDataAck), -} - -impl RandMessage { - pub fn verify( - &self, - epoch_state: &EpochState, - rand_config: &RandConfig, - sender: Author, - ) -> anyhow::Result<()> { - match self { - RandMessage::RequestShare(_) => Ok(()), - RandMessage::Share(share) => share.verify(rand_config), - RandMessage::AugData(aug_data) => aug_data.verify(rand_config, sender), - RandMessage::CertifiedAugData(certified_aug_data) => { - certified_aug_data.verify(&epoch_state.verifier) - }, - _ => bail!("[RandMessage] unexpected message type"), - } - } -} - -impl RBMessage for RandMessage {} - -impl TConsensusMsg for RandMessage { - fn epoch(&self) -> u64 { - match self { - RandMessage::RequestShare(request) => request.epoch(), - RandMessage::Share(share) => share.epoch(), - RandMessage::AugData(aug_data) => aug_data.epoch(), - RandMessage::AugDataSignature(signature) => signature.epoch(), - RandMessage::CertifiedAugData(certified_aug_data) => certified_aug_data.epoch(), - RandMessage::CertifiedAugDataAck(ack) => ack.epoch(), - } - } - - fn from_network_message(msg: ConsensusMsg) -> anyhow::Result { - match msg { - ConsensusMsg::RandGenMessage(msg) => Ok(bcs::from_bytes(&msg.data)?), - _ => bail!("unexpected consensus message type {:?}", msg), - } - } - - fn into_network_message(self) -> ConsensusMsg { - ConsensusMsg::RandGenMessage(RandGenMessage { - epoch: self.epoch(), - data: bcs::to_bytes(&self).unwrap(), - }) - } -} - -#[derive(Serialize, Deserialize, Clone)] -pub struct RandGenMessage { - epoch: u64, - #[serde(with = "serde_bytes")] - data: Vec, -} - -impl RandGenMessage { - pub fn new(epoch: u64, data: Vec) -> Self { - Self { epoch, data } - } - - pub fn data(&self) -> &[u8] { - &self.data - } - - pub fn epoch(&self) -> u64 { - self.epoch - } -} - -impl core::fmt::Debug for RandGenMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RandGenMessage") - .field("epoch", &self.epoch) - .field("data", &hex::encode(&self.data[..min(20, self.data.len())])) - .finish() - } -} - -pub struct RpcRequest { - pub req: RandMessage, - pub protocol: ProtocolId, - pub response_sender: oneshot::Sender>, -} diff --git a/consensus/src/rand/rand_gen/rand_manager.rs b/consensus/src/rand/rand_gen/rand_manager.rs deleted file mode 100644 index f53d2a2cf3e1d..0000000000000 --- a/consensus/src/rand/rand_gen/rand_manager.rs +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters::RAND_QUEUE_SIZE, - logging::{LogEvent, LogSchema}, - network::{IncomingRandGenRequest, NetworkSender, TConsensusMsg}, - pipeline::buffer_manager::{OrderedBlocks, ResetAck, ResetRequest, ResetSignal}, - rand::rand_gen::{ - aug_data_store::AugDataStore, - block_queue::{BlockQueue, QueueItem}, - network_messages::{RandMessage, RpcRequest}, - rand_store::RandStore, - reliable_broadcast_state::{ - AugDataCertBuilder, CertifiedAugDataAckState, ShareAggregateState, - }, - storage::interface::RandStorage, - types::{RandConfig, RequestShare, TAugmentedData, TShare}, - }, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::aptos_channel; -use aptos_consensus_types::common::Author; -use aptos_infallible::Mutex; -use aptos_logger::{error, info, spawn_named, warn}; -use aptos_network::{protocols::network::RpcError, ProtocolId}; -use aptos_reliable_broadcast::{DropGuard, ReliableBroadcast}; -use aptos_time_service::TimeService; -use aptos_types::{ - epoch_state::EpochState, - randomness::{RandMetadata, Randomness}, - validator_signer::ValidatorSigner, -}; -use bytes::Bytes; -use futures::{ - future::{AbortHandle, Abortable}, - FutureExt, StreamExt, -}; -use futures_channel::{ - mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, - oneshot, -}; -use std::{sync::Arc, time::Duration}; -use tokio_retry::strategy::ExponentialBackoff; - -pub type Sender = UnboundedSender; -pub type Receiver = UnboundedReceiver; - -pub struct RandManager { - author: Author, - epoch_state: Arc, - stop: bool, - config: RandConfig, - reliable_broadcast: Arc, ExponentialBackoff>>, - network_sender: Arc, - - // local channel received from rand_store - decision_rx: Receiver, - // downstream channels - outgoing_blocks: Sender, - // local state - rand_store: Arc>>, - aug_data_store: AugDataStore, - block_queue: BlockQueue, -} - -impl RandManager { - pub fn new( - author: Author, - epoch_state: Arc, - signer: Arc, - config: RandConfig, - outgoing_blocks: Sender, - network_sender: Arc, - db: Arc>, - bounded_executor: BoundedExecutor, - ) -> Self { - let rb_backoff_policy = ExponentialBackoff::from_millis(2) - .factor(100) - .max_delay(Duration::from_secs(10)); - let reliable_broadcast = Arc::new(ReliableBroadcast::new( - epoch_state.verifier.get_ordered_account_addresses(), - network_sender.clone(), - rb_backoff_policy, - TimeService::real(), - Duration::from_secs(10), - bounded_executor, - )); - let (decision_tx, decision_rx) = unbounded(); - let rand_store = Arc::new(Mutex::new(RandStore::new( - epoch_state.epoch, - author, - config.clone(), - decision_tx, - ))); - let aug_data_store = AugDataStore::new(epoch_state.epoch, signer, config.clone(), db); - - Self { - author, - epoch_state, - stop: false, - config, - reliable_broadcast, - network_sender, - - decision_rx, - outgoing_blocks, - - rand_store, - aug_data_store, - block_queue: BlockQueue::new(), - } - } - - fn process_incoming_blocks(&mut self, blocks: OrderedBlocks) { - let rounds: Vec = blocks.ordered_blocks.iter().map(|b| b.round()).collect(); - info!(rounds = rounds, "Processing incoming blocks."); - let broadcast_handles: Vec<_> = blocks - .ordered_blocks - .iter() - .map(|block| RandMetadata::from(block.block())) - .map(|metadata| self.process_incoming_metadata(metadata)) - .collect(); - let queue_item = QueueItem::new(blocks, Some(broadcast_handles)); - self.block_queue.push_back(queue_item); - } - - fn process_incoming_metadata(&self, metadata: RandMetadata) -> DropGuard { - let self_share = S::generate(&self.config, metadata.clone()); - info!(LogSchema::new(LogEvent::BroadcastRandShare) - .epoch(self.epoch_state.epoch) - .author(self.author) - .round(metadata.round())); - let mut rand_store = self.rand_store.lock(); - rand_store.update_highest_known_round(metadata.round()); - rand_store - .add_share(self_share.clone()) - .expect("Add self share should succeed"); - rand_store.add_rand_metadata(metadata.clone()); - self.network_sender - .broadcast_without_self(RandMessage::::Share(self_share).into_network_message()); - self.spawn_aggregate_shares_task(metadata) - } - - fn process_ready_blocks(&mut self, ready_blocks: Vec) { - let rounds: Vec = ready_blocks - .iter() - .flat_map(|b| b.ordered_blocks.iter().map(|b3| b3.round())) - .collect(); - info!(rounds = rounds, "Processing rand-ready blocks."); - - for blocks in ready_blocks { - let _ = self.outgoing_blocks.unbounded_send(blocks); - } - } - - fn process_reset(&mut self, request: ResetRequest) { - let ResetRequest { tx, signal } = request; - let target_round = match signal { - ResetSignal::Stop => 0, - ResetSignal::TargetRound(round) => round, - }; - self.block_queue = BlockQueue::new(); - self.rand_store - .lock() - .update_highest_known_round(target_round); - self.stop = matches!(signal, ResetSignal::Stop); - let _ = tx.send(ResetAck::default()); - } - - fn process_randomness(&mut self, randomness: Randomness) { - info!( - metadata = randomness.metadata().metadata_to_sign, - "Processing decisioned randomness." - ); - if let Some(block) = self.block_queue.item_mut(randomness.round()) { - block.set_randomness(randomness.round(), randomness); - } - } - - fn process_response( - &self, - protocol: ProtocolId, - sender: oneshot::Sender>, - message: RandMessage, - ) { - let msg = message.into_network_message(); - let _ = sender.send(Ok(protocol.to_bytes(&msg).unwrap().into())); - } - - async fn verification_task( - epoch_state: Arc, - mut incoming_rpc_request: aptos_channel::Receiver, - verified_msg_tx: UnboundedSender>, - rand_config: RandConfig, - bounded_executor: BoundedExecutor, - ) { - while let Some(rand_gen_msg) = incoming_rpc_request.next().await { - let tx = verified_msg_tx.clone(); - let epoch_state_clone = epoch_state.clone(); - let config_clone = rand_config.clone(); - bounded_executor - .spawn(async move { - match bcs::from_bytes::>(rand_gen_msg.req.data()) { - Ok(msg) => { - if msg - .verify(&epoch_state_clone, &config_clone, rand_gen_msg.sender) - .is_ok() - { - let _ = tx.unbounded_send(RpcRequest { - req: msg, - protocol: rand_gen_msg.protocol, - response_sender: rand_gen_msg.response_sender, - }); - } - }, - Err(e) => { - warn!("Invalid rand gen message: {}", e); - }, - } - }) - .await; - } - } - - fn spawn_aggregate_shares_task(&self, metadata: RandMetadata) -> DropGuard { - let rb = self.reliable_broadcast.clone(); - let aggregate_state = Arc::new(ShareAggregateState::new( - self.rand_store.clone(), - metadata.clone(), - self.config.clone(), - )); - let epoch_state = self.epoch_state.clone(); - let round = metadata.round(); - let rand_store = self.rand_store.clone(); - let task = async move { - tokio::time::sleep(Duration::from_millis(300)).await; - let maybe_existing_shares = rand_store.lock().get_all_shares_authors(&metadata); - if let Some(existing_shares) = maybe_existing_shares { - let epoch = epoch_state.epoch; - let request = RequestShare::new(epoch, metadata); - let targets = epoch_state - .verifier - .get_ordered_account_addresses_iter() - .filter(|author| !existing_shares.contains(author)) - .collect::>(); - info!( - epoch = epoch, - round = round, - "[RandManager] Start broadcasting share request for {}", - targets.len(), - ); - rb.multicast(request, aggregate_state, targets).await; - info!( - epoch = epoch, - round = round, - "[RandManager] Finish broadcasting share request", - ); - } - }; - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - tokio::spawn(Abortable::new(task, abort_registration)); - DropGuard::new(abort_handle) - } - - async fn broadcast_aug_data(&mut self) -> DropGuard { - let data = self - .aug_data_store - .get_my_aug_data() - .unwrap_or_else(|| D::generate(&self.config)); - // Add it synchronously to avoid race that it sends to others but panics before it persists locally. - self.aug_data_store - .add_aug_data(data.clone()) - .expect("Add self aug data should succeed"); - let aug_ack = AugDataCertBuilder::new(data.clone(), self.epoch_state.clone()); - let rb = self.reliable_broadcast.clone(); - let rb2 = self.reliable_broadcast.clone(); - let validators = self.epoch_state.verifier.get_ordered_account_addresses(); - let maybe_existing_certified_data = self.aug_data_store.get_my_certified_aug_data(); - let phase1 = async move { - if let Some(certified_data) = maybe_existing_certified_data { - info!("[RandManager] Already have certified aug data"); - return certified_data; - } - info!("[RandManager] Start broadcasting aug data"); - info!(LogSchema::new(LogEvent::BroadcastAugData) - .author(*data.author()) - .epoch(data.epoch())); - let certified_data = rb.broadcast(data, aug_ack).await; - info!("[RandManager] Finish broadcasting aug data"); - certified_data - }; - let ack_state = Arc::new(CertifiedAugDataAckState::new(validators.into_iter())); - let task = phase1.then(|certified_data| async move { - info!(LogSchema::new(LogEvent::BroadcastCertifiedAugData) - .author(*certified_data.author()) - .epoch(certified_data.epoch())); - info!("[RandManager] Start broadcasting certified aug data"); - rb2.broadcast(certified_data, ack_state).await; - info!("[RandManager] Finish broadcasting certified aug data"); - }); - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - tokio::spawn(Abortable::new(task, abort_registration)); - DropGuard::new(abort_handle) - } - - pub async fn start( - mut self, - mut incoming_blocks: Receiver, - incoming_rpc_request: aptos_channel::Receiver, - mut reset_rx: Receiver, - bounded_executor: BoundedExecutor, - ) { - info!("RandManager started"); - let (verified_msg_tx, mut verified_msg_rx) = unbounded(); - let epoch_state = self.epoch_state.clone(); - let rand_config = self.config.clone(); - spawn_named!( - "rand manager verification", - Self::verification_task( - epoch_state, - incoming_rpc_request, - verified_msg_tx, - rand_config, - bounded_executor, - ) - ); - - let _guard = self.broadcast_aug_data().await; - let mut interval = tokio::time::interval(Duration::from_millis(5000)); - while !self.stop { - tokio::select! { - Some(blocks) = incoming_blocks.next() => { - self.process_incoming_blocks(blocks); - } - Some(reset) = reset_rx.next() => { - while matches!(incoming_blocks.try_next(), Ok(Some(_))) {} - self.process_reset(reset); - } - Some(randomness) = self.decision_rx.next() => { - self.process_randomness(randomness); - } - Some(request) = verified_msg_rx.next() => { - let RpcRequest { - req: rand_gen_msg, - protocol, - response_sender, - } = request; - match rand_gen_msg { - RandMessage::RequestShare(request) => { - let result = self.rand_store.lock().get_self_share(request.rand_metadata()); - match result { - Ok(maybe_share) => { - let share = maybe_share.unwrap_or_else(|| { - // reproduce previous share if not found - let share = S::generate(&self.config, request.rand_metadata().clone()); - self.rand_store.lock().add_share(share.clone()).expect("Add self share should succeed"); - share - }); - self.process_response(protocol, response_sender, RandMessage::Share(share)); - }, - Err(e) => { - warn!("[RandManager] Failed to get share: {}", e); - } - } - } - RandMessage::Share(share) => { - info!(LogSchema::new(LogEvent::ReceiveProactiveRandShare) - .author(self.author) - .epoch(share.epoch()) - .round(share.metadata().round()) - .remote_peer(*share.author())); - - if let Err(e) = self.rand_store.lock().add_share(share) { - warn!("[RandManager] Failed to add share: {}", e); - } - } - RandMessage::AugData(aug_data) => { - info!(LogSchema::new(LogEvent::ReceiveAugData) - .author(self.author) - .epoch(aug_data.epoch()) - .remote_peer(*aug_data.author())); - match self.aug_data_store.add_aug_data(aug_data) { - Ok(sig) => self.process_response(protocol, response_sender, RandMessage::AugDataSignature(sig)), - Err(e) => error!("[RandManager] Failed to add aug data: {}", e), - } - } - RandMessage::CertifiedAugData(certified_aug_data) => { - info!(LogSchema::new(LogEvent::ReceiveCertifiedAugData) - .author(self.author) - .epoch(certified_aug_data.epoch()) - .remote_peer(*certified_aug_data.author())); - match self.aug_data_store.add_certified_aug_data(certified_aug_data) { - Ok(ack) => self.process_response(protocol, response_sender, RandMessage::CertifiedAugDataAck(ack)), - Err(e) => error!("[RandManager] Failed to add certified aug data: {}", e), - } - } - _ => unreachable!("[RandManager] Unexpected message type after verification"), - } - } - _ = interval.tick().fuse() => { - self.observe_queue(); - }, - - } - let maybe_ready_blocks = self.block_queue.dequeue_rand_ready_prefix(); - if !maybe_ready_blocks.is_empty() { - self.process_ready_blocks(maybe_ready_blocks); - } - } - info!("RandManager stopped"); - } - - pub fn observe_queue(&self) { - let queue = &self.block_queue.queue(); - RAND_QUEUE_SIZE.set(queue.len() as i64); - } -} diff --git a/consensus/src/rand/rand_gen/rand_store.rs b/consensus/src/rand/rand_gen/rand_store.rs deleted file mode 100644 index e7c2b6ef2f91b..0000000000000 --- a/consensus/src/rand/rand_gen/rand_store.rs +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::tracing::{observe_block, BlockStage}, - rand::rand_gen::{ - rand_manager::Sender, - types::{RandConfig, RandShare, TShare}, - }, -}; -use anyhow::ensure; -use aptos_consensus_types::common::{Author, Round}; -use aptos_types::randomness::{RandMetadata, Randomness}; -use itertools::Either; -use std::collections::{BTreeMap, HashMap, HashSet}; - -const FUTURE_ROUNDS_TO_ACCEPT: u64 = 200; - -pub struct ShareAggregator { - author: Author, - shares: HashMap>, - total_weight: u64, -} - -impl ShareAggregator { - pub fn new(author: Author) -> Self { - Self { - author, - shares: HashMap::new(), - total_weight: 0, - } - } - - pub fn add_share(&mut self, weight: u64, share: RandShare) { - if self.shares.insert(*share.author(), share).is_none() { - self.total_weight += weight; - } - } - - pub fn try_aggregate( - self, - rand_config: &RandConfig, - rand_metadata: RandMetadata, - decision_tx: Sender, - ) -> Either> { - if self.total_weight < rand_config.threshold() { - return Either::Left(self); - } - // timestamp records the time when the block is created - observe_block(rand_metadata.timestamp, BlockStage::RAND_ADD_ENOUGH_SHARE); - let rand_config = rand_config.clone(); - let self_share = self - .get_self_share() - .expect("Aggregated item should have self share"); - tokio::task::spawn_blocking(move || { - decision_tx.unbounded_send(S::aggregate( - self.shares.values(), - &rand_config, - rand_metadata, - )) - }); - Either::Right(self_share) - } - - fn retain(&mut self, rand_config: &RandConfig, rand_metadata: &RandMetadata) { - self.shares - .retain(|_, share| share.metadata() == rand_metadata); - self.total_weight = self - .shares - .keys() - .map(|author| rand_config.get_peer_weight(author)) - .sum(); - } - - fn get_self_share(&self) -> Option> { - self.shares.get(&self.author).cloned() - } - - fn total_weights(&self) -> u64 { - self.total_weight - } -} - -enum RandItem { - PendingMetadata(ShareAggregator), - PendingDecision { - metadata: RandMetadata, - share_aggregator: ShareAggregator, - }, - Decided { - self_share: RandShare, - }, -} - -impl RandItem { - fn new(author: Author) -> Self { - Self::PendingMetadata(ShareAggregator::new(author)) - } - - fn total_weights(&self) -> Option { - match self { - RandItem::PendingMetadata(aggr) => Some(aggr.total_weights()), - RandItem::PendingDecision { - share_aggregator, .. - } => Some(share_aggregator.total_weights()), - RandItem::Decided { .. } => None, - } - } - - fn has_decision(&self) -> bool { - matches!(self, RandItem::Decided { .. }) - } - - fn add_share(&mut self, share: RandShare, rand_config: &RandConfig) -> anyhow::Result<()> { - match self { - RandItem::PendingMetadata(aggr) => { - aggr.add_share(rand_config.get_peer_weight(share.author()), share); - Ok(()) - }, - RandItem::PendingDecision { - metadata, - share_aggregator, - } => { - ensure!( - metadata == share.metadata(), - "[RandStore] RandShare metadata from {} mismatch with block metadata!", - share.author(), - ); - share_aggregator.add_share(rand_config.get_peer_weight(share.author()), share); - Ok(()) - }, - RandItem::Decided { .. } => Ok(()), - } - } - - fn try_aggregate(&mut self, rand_config: &RandConfig, decision_tx: Sender) { - let item = std::mem::replace(self, Self::new(Author::ONE)); - let new_item = match item { - RandItem::PendingDecision { - share_aggregator, - metadata, - } => match share_aggregator.try_aggregate(rand_config, metadata.clone(), decision_tx) { - Either::Left(share_aggregator) => Self::PendingDecision { - metadata, - share_aggregator, - }, - Either::Right(self_share) => Self::Decided { self_share }, - }, - item @ (RandItem::Decided { .. } | RandItem::PendingMetadata(_)) => item, - }; - let _ = std::mem::replace(self, new_item); - } - - fn add_metadata(&mut self, rand_config: &RandConfig, rand_metadata: RandMetadata) { - let item = std::mem::replace(self, Self::new(Author::ONE)); - let new_item = match item { - RandItem::PendingMetadata(mut share_aggregator) => { - share_aggregator.retain(rand_config, &rand_metadata); - Self::PendingDecision { - metadata: rand_metadata, - share_aggregator, - } - }, - item @ (RandItem::PendingDecision { .. } | RandItem::Decided { .. }) => item, - }; - let _ = std::mem::replace(self, new_item); - } - - fn get_all_shares_authors(&self) -> Option> { - match self { - RandItem::PendingDecision { - share_aggregator, .. - } => Some(share_aggregator.shares.keys().cloned().collect()), - RandItem::Decided { .. } => None, - RandItem::PendingMetadata(_) => { - unreachable!("Should only be called after block is added") - }, - } - } - - fn get_self_share(&self) -> Option> { - match self { - RandItem::PendingMetadata(aggr) => aggr.get_self_share(), - RandItem::PendingDecision { - share_aggregator, .. - } => share_aggregator.get_self_share(), - RandItem::Decided { self_share, .. } => Some(self_share.clone()), - } - } -} - -pub struct RandStore { - epoch: u64, - author: Author, - rand_config: RandConfig, - rand_map: BTreeMap>, - highest_known_round: u64, - decision_tx: Sender, -} - -impl RandStore { - pub fn new( - epoch: u64, - author: Author, - rand_config: RandConfig, - decision_tx: Sender, - ) -> Self { - Self { - epoch, - author, - rand_config, - rand_map: BTreeMap::new(), - highest_known_round: 0, - decision_tx, - } - } - - pub fn update_highest_known_round(&mut self, round: u64) { - self.highest_known_round = std::cmp::max(self.highest_known_round, round); - } - - pub fn add_rand_metadata(&mut self, rand_metadata: RandMetadata) { - let rand_item = self - .rand_map - .entry(rand_metadata.round()) - .or_insert_with(|| RandItem::new(self.author)); - rand_item.add_metadata(&self.rand_config, rand_metadata.clone()); - rand_item.try_aggregate(&self.rand_config, self.decision_tx.clone()); - } - - pub fn add_share(&mut self, share: RandShare) -> anyhow::Result { - ensure!( - share.metadata().epoch() == self.epoch, - "Share from different epoch" - ); - ensure!( - share.metadata().round() <= self.highest_known_round + FUTURE_ROUNDS_TO_ACCEPT, - "Share from future round" - ); - let rand_metadata = share.metadata().clone(); - let rand_item = self - .rand_map - .entry(rand_metadata.round()) - .or_insert_with(|| RandItem::new(self.author)); - rand_item.add_share(share, &self.rand_config)?; - rand_item.try_aggregate(&self.rand_config, self.decision_tx.clone()); - Ok(rand_item.has_decision()) - } - - /// This should only be called after the block is added, returns None if already decided - /// Otherwise returns existing shares' authors - pub fn get_all_shares_authors(&self, metadata: &RandMetadata) -> Option> { - self.rand_map - .get(&metadata.round()) - .and_then(|item| item.get_all_shares_authors()) - } - - pub fn get_self_share( - &mut self, - metadata: &RandMetadata, - ) -> anyhow::Result>> { - ensure!( - metadata.round() <= self.highest_known_round, - "Request share from future round {}, highest known round {}", - metadata.round(), - self.highest_known_round - ); - Ok(self - .rand_map - .get(&metadata.round()) - .and_then(|item| item.get_self_share()) - .filter(|share| share.metadata() == metadata)) - } -} - -#[cfg(test)] -mod tests { - use crate::rand::rand_gen::{ - block_queue::QueueItem, - rand_store::{RandItem, RandStore, ShareAggregator}, - test_utils::{create_ordered_blocks, create_share, create_share_for_round}, - types::{MockShare, RandConfig}, - }; - use aptos_consensus_types::common::Author; - use aptos_crypto::{bls12381, HashValue, Uniform}; - use aptos_dkg::{ - pvss::{traits::Transcript, Player, WeightedConfig}, - weighted_vuf::traits::WeightedVUF, - }; - use aptos_types::{ - dkg::{real_dkg::maybe_dk_from_bls_sk, DKGSessionMetadata, DKGTrait, DefaultDKG}, - on_chain_config::OnChainRandomnessConfig, - randomness::{RandKeys, RandMetadata, WvufPP, WVUF}, - validator_verifier::{ - ValidatorConsensusInfo, ValidatorConsensusInfoMoveStruct, ValidatorVerifier, - }, - }; - use futures::StreamExt; - use futures_channel::mpsc::unbounded; - use rand::thread_rng; - use std::str::FromStr; - - /// Captures important data items across the whole DKG-WVUF flow. - struct TestContext { - authors: Vec, - dealer_epoch: u64, - target_epoch: u64, - rand_config: RandConfig, - } - - impl TestContext { - fn new(weights: Vec, my_index: usize) -> Self { - let dealer_epoch = 0; - let target_epoch = 1; - let num_validators = weights.len(); - let mut rng = thread_rng(); - let authors: Vec<_> = (0..num_validators) - .map(|i| Author::from_str(&format!("{:x}", i)).unwrap()) - .collect(); - let private_keys: Vec = (0..num_validators) - .map(|_| bls12381::PrivateKey::generate_for_testing()) - .collect(); - let public_keys: Vec = - private_keys.iter().map(bls12381::PublicKey::from).collect(); - let dkg_decrypt_keys: Vec<::NewValidatorDecryptKey> = - private_keys - .iter() - .map(|sk| maybe_dk_from_bls_sk(sk).unwrap()) - .collect(); - let consensus_infos: Vec = (0..num_validators) - .map(|idx| { - ValidatorConsensusInfo::new( - authors[idx], - public_keys[idx].clone(), - weights[idx], - ) - }) - .collect(); - let consensus_info_move_structs = consensus_infos - .clone() - .into_iter() - .map(ValidatorConsensusInfoMoveStruct::from) - .collect::>(); - let verifier = ValidatorVerifier::new(consensus_infos.clone()); - let dkg_session_metadata = DKGSessionMetadata { - dealer_epoch: 999, - randomness_config: OnChainRandomnessConfig::default_enabled().into(), - dealer_validator_set: consensus_info_move_structs.clone(), - target_validator_set: consensus_info_move_structs.clone(), - }; - let dkg_pub_params = DefaultDKG::new_public_params(&dkg_session_metadata); - let input_secret = ::InputSecret::generate_for_testing(); - let transcript = DefaultDKG::generate_transcript( - &mut rng, - &dkg_pub_params, - &input_secret, - 0, - &private_keys[0], - ); - let (sk, pk) = DefaultDKG::decrypt_secret_share_from_transcript( - &dkg_pub_params, - &transcript, - my_index as u64, - &dkg_decrypt_keys[my_index], - ) - .unwrap(); - - let pk_shares = (0..num_validators) - .map(|id| { - transcript - .get_public_key_share(&dkg_pub_params.pvss_config.wconfig, &Player { id }) - }) - .collect::>(); - let vuf_pub_params = WvufPP::from(&dkg_pub_params.pvss_config.pp); - - let (ask, apk) = WVUF::augment_key_pair(&vuf_pub_params, sk, pk, &mut rng); - - let rand_keys = RandKeys::new(ask, apk, pk_shares, num_validators); - let weights: Vec = weights.into_iter().map(|x| x as usize).collect(); - let half_total_weights = weights.clone().into_iter().sum::() / 2; - let weighted_config = WeightedConfig::new(half_total_weights, weights).unwrap(); - let rand_config = RandConfig::new( - authors[my_index], - target_epoch, - verifier, - vuf_pub_params, - rand_keys, - weighted_config, - ); - - Self { - authors, - dealer_epoch, - target_epoch, - rand_config, - } - } - } - - #[test] - fn test_share_aggregator() { - let ctxt = TestContext::new(vec![1, 2, 3], 0); - let mut aggr = ShareAggregator::new(ctxt.authors[0]); - aggr.add_share( - 1, - create_share_for_round(ctxt.target_epoch, 1, ctxt.authors[0]), - ); - aggr.add_share( - 2, - create_share_for_round(ctxt.target_epoch, 2, ctxt.authors[1]), - ); - aggr.add_share( - 3, - create_share_for_round(ctxt.target_epoch, 1, ctxt.authors[2]), - ); - assert_eq!(aggr.shares.len(), 3); - assert_eq!(aggr.total_weight, 6); - // retain the shares with the same metadata - aggr.retain( - &ctxt.rand_config, - &RandMetadata::new(ctxt.target_epoch, 1, HashValue::zero(), 1700000000), - ); - assert_eq!(aggr.shares.len(), 2); - assert_eq!(aggr.total_weight, 4); - } - - #[tokio::test] - async fn test_rand_item() { - let ctxt = TestContext::new(vec![1, 2, 3], 1); - let (tx, _rx) = unbounded(); - let shares = vec![ - create_share_for_round(ctxt.target_epoch, 2, ctxt.authors[0]), - create_share_for_round(ctxt.target_epoch, 1, ctxt.authors[1]), - create_share_for_round(ctxt.target_epoch, 1, ctxt.authors[2]), - ]; - - let mut item = RandItem::::new(ctxt.authors[1]); - for share in shares.iter() { - item.add_share(share.clone(), &ctxt.rand_config).unwrap(); - } - assert_eq!(item.total_weights().unwrap(), 6); - item.add_metadata( - &ctxt.rand_config, - RandMetadata::new(ctxt.target_epoch, 1, HashValue::zero(), 1700000000), - ); - assert_eq!(item.total_weights().unwrap(), 5); - item.try_aggregate(&ctxt.rand_config, tx); - assert!(item.has_decision()); - - let mut item = RandItem::::new(ctxt.authors[0]); - item.add_metadata( - &ctxt.rand_config, - RandMetadata::new(ctxt.target_epoch, 2, HashValue::zero(), 1700000000), - ); - for share in shares[1..].iter() { - item.add_share(share.clone(), &ctxt.rand_config) - .unwrap_err(); - } - } - - #[tokio::test] - async fn test_rand_store() { - let ctxt = TestContext::new(vec![100; 7], 0); - let (decision_tx, mut decision_rx) = unbounded(); - let mut rand_store = RandStore::new( - ctxt.target_epoch, - ctxt.authors[1], - ctxt.rand_config.clone(), - decision_tx, - ); - - let rounds = vec![vec![1], vec![2, 3], vec![5, 8, 13]]; - let blocks_1 = QueueItem::new(create_ordered_blocks(rounds[0].clone()), None); - let blocks_2 = QueueItem::new(create_ordered_blocks(rounds[1].clone()), None); - let metadata_1 = blocks_1.all_rand_metadata(); - let metadata_2 = blocks_2.all_rand_metadata(); - - // shares come before metadata - for share in ctxt.authors[0..5] - .iter() - .map(|author| create_share(metadata_1[0].clone(), *author)) - { - rand_store.add_share(share).unwrap(); - } - assert!(decision_rx.try_next().is_err()); - for metadata in blocks_1.all_rand_metadata() { - rand_store.add_rand_metadata(metadata); - } - assert!(decision_rx.next().await.is_some()); - - // metadata come after shares - for metadata in blocks_2.all_rand_metadata() { - rand_store.add_rand_metadata(metadata); - } - assert!(decision_rx.try_next().is_err()); - - for share in ctxt.authors[1..6] - .iter() - .map(|author| create_share(metadata_2[0].clone(), *author)) - { - rand_store.add_share(share).unwrap(); - } - assert!(decision_rx.next().await.is_some()); - } -} diff --git a/consensus/src/rand/rand_gen/reliable_broadcast_state.rs b/consensus/src/rand/rand_gen/reliable_broadcast_state.rs deleted file mode 100644 index e993ee36d17bb..0000000000000 --- a/consensus/src/rand/rand_gen/reliable_broadcast_state.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::rand::rand_gen::{ - network_messages::RandMessage, - rand_store::RandStore, - types::{ - AugData, AugDataSignature, CertifiedAugData, CertifiedAugDataAck, RandConfig, RandShare, - RequestShare, TAugmentedData, TShare, - }, -}; -use anyhow::ensure; -use aptos_consensus_types::common::Author; -use aptos_infallible::Mutex; -use aptos_reliable_broadcast::BroadcastStatus; -use aptos_types::{ - aggregate_signature::PartialSignatures, epoch_state::EpochState, randomness::RandMetadata, -}; -use std::{collections::HashSet, sync::Arc}; - -pub struct AugDataCertBuilder { - epoch_state: Arc, - aug_data: AugData, - partial_signatures: Mutex, -} - -impl AugDataCertBuilder { - pub fn new(aug_data: AugData, epoch_state: Arc) -> Arc { - Arc::new(Self { - epoch_state, - aug_data, - partial_signatures: Mutex::new(PartialSignatures::empty()), - }) - } -} - -impl BroadcastStatus, RandMessage> - for Arc> -{ - type Aggregated = CertifiedAugData; - type Message = AugData; - type Response = AugDataSignature; - - fn add(&self, peer: Author, ack: Self::Response) -> anyhow::Result> { - ack.verify(peer, &self.epoch_state.verifier, &self.aug_data)?; - let mut parital_signatures_guard = self.partial_signatures.lock(); - parital_signatures_guard.add_signature(peer, ack.into_signature()); - let qc_aug_data = self - .epoch_state - .verifier - .check_voting_power(parital_signatures_guard.signatures().keys(), true) - .ok() - .map(|_| { - let aggregated_signature = self - .epoch_state - .verifier - .aggregate_signatures(&parital_signatures_guard) - .expect("Signature aggregation should succeed"); - CertifiedAugData::new(self.aug_data.clone(), aggregated_signature) - }); - Ok(qc_aug_data) - } -} - -pub struct CertifiedAugDataAckState { - validators: Mutex>, -} - -impl CertifiedAugDataAckState { - pub fn new(validators: impl Iterator) -> Self { - Self { - validators: Mutex::new(validators.collect()), - } - } -} - -impl BroadcastStatus, RandMessage> - for Arc -{ - type Aggregated = (); - type Message = CertifiedAugData; - type Response = CertifiedAugDataAck; - - fn add(&self, peer: Author, _ack: Self::Response) -> anyhow::Result> { - let mut validators_guard = self.validators.lock(); - ensure!( - validators_guard.remove(&peer), - "[RandMessage] Unknown author: {}", - peer - ); - // If receive from all validators, stop the reliable broadcast - if validators_guard.is_empty() { - Ok(Some(())) - } else { - Ok(None) - } - } -} - -pub struct ShareAggregateState { - rand_metadata: RandMetadata, - rand_store: Arc>>, - rand_config: RandConfig, -} - -impl ShareAggregateState { - pub fn new( - rand_store: Arc>>, - metadata: RandMetadata, - rand_config: RandConfig, - ) -> Self { - Self { - rand_store, - rand_metadata: metadata, - rand_config, - } - } -} - -impl BroadcastStatus, RandMessage> - for Arc> -{ - type Aggregated = (); - type Message = RequestShare; - type Response = RandShare; - - fn add(&self, peer: Author, share: Self::Response) -> anyhow::Result> { - ensure!(share.author() == &peer, "Author does not match"); - ensure!( - share.metadata() == &self.rand_metadata, - "Metadata does not match: local {:?}, received {:?}", - self.rand_metadata, - share.metadata() - ); - share.verify(&self.rand_config)?; - let mut store = self.rand_store.lock(); - let aggregated = if store.add_share(share)? { - Some(()) - } else { - None - }; - Ok(aggregated) - } -} diff --git a/consensus/src/rand/rand_gen/storage/db.rs b/consensus/src/rand/rand_gen/storage/db.rs deleted file mode 100644 index 0700d6f92563a..0000000000000 --- a/consensus/src/rand/rand_gen/storage/db.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::DbError, - rand::rand_gen::{ - storage::{ - interface::RandStorage, - schema::{ - AugDataSchema, CertifiedAugDataSchema, KeyPairSchema, AUG_DATA_CF_NAME, - CERTIFIED_AUG_DATA_CF_NAME, KEY_PAIR_CF_NAME, - }, - }, - types::{AugData, AugDataId, CertifiedAugData, TAugmentedData}, - }, -}; -use anyhow::Result; -use aptos_logger::info; -use aptos_schemadb::{schema::Schema, Options, ReadOptions, SchemaBatch, DB}; -use std::{path::Path, sync::Arc, time::Instant}; - -pub struct RandDb { - db: Arc, -} - -pub const RAND_DB_NAME: &str = "rand_db"; - -impl RandDb { - pub(crate) fn new + Clone>(db_root_path: P) -> Self { - let column_families = vec![ - KEY_PAIR_CF_NAME, - AUG_DATA_CF_NAME, - CERTIFIED_AUG_DATA_CF_NAME, - ]; - - let path = db_root_path.as_ref().join(RAND_DB_NAME); - let instant = Instant::now(); - let mut opts = Options::default(); - opts.create_if_missing(true); - opts.create_missing_column_families(true); - let db = Arc::new( - DB::open(path.clone(), RAND_DB_NAME, column_families, &opts) - .expect("RandDB open failed; unable to continue"), - ); - - info!( - "Opened RandDB at {:?} in {} ms", - path, - instant.elapsed().as_millis() - ); - - Self { db } - } - - fn commit(&self, batch: SchemaBatch) -> Result<(), DbError> { - self.db.write_schemas(batch)?; - Ok(()) - } - - fn put(&self, key: &S::Key, value: &S::Value) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - batch.put::(key, value)?; - self.commit(batch)?; - Ok(()) - } - - fn delete(&self, mut keys: impl Iterator) -> Result<(), DbError> { - let batch = SchemaBatch::new(); - keys.try_for_each(|key| batch.delete::(&key))?; - self.commit(batch) - } - - fn get_all(&self) -> Result, DbError> { - let mut iter = self.db.iter::(ReadOptions::default())?; - iter.seek_to_first(); - Ok(iter - .map(|e| match e { - Ok((k, v)) => Ok((k, v)), - Err(e) => Err(e.into()), - }) - .collect::>>()?) - } -} - -impl RandStorage for RandDb { - fn save_key_pair_bytes(&self, epoch: u64, key_pair: Vec) -> Result<()> { - Ok(self.put::(&(), &(epoch, key_pair))?) - } - - fn save_aug_data(&self, aug_data: &AugData) -> Result<()> { - Ok(self.put::>(&aug_data.id(), aug_data)?) - } - - fn save_certified_aug_data(&self, certified_aug_data: &CertifiedAugData) -> Result<()> { - Ok(self.put::>(&certified_aug_data.id(), certified_aug_data)?) - } - - fn get_key_pair_bytes(&self) -> Result)>> { - Ok(self.get_all::()?.pop().map(|(_, v)| v)) - } - - fn get_all_aug_data(&self) -> Result)>> { - Ok(self.get_all::>()?) - } - - fn get_all_certified_aug_data(&self) -> Result)>> { - Ok(self.get_all::>()?) - } - - fn remove_aug_data(&self, aug_data: Vec>) -> Result<()> { - Ok(self.delete::>(aug_data.into_iter().map(|d| d.id()))?) - } - - fn remove_certified_aug_data( - &self, - certified_aug_data: Vec>, - ) -> Result<()> { - Ok(self - .delete::>(certified_aug_data.into_iter().map(|d| d.id()))?) - } -} diff --git a/consensus/src/rand/rand_gen/storage/in_memory.rs b/consensus/src/rand/rand_gen/storage/in_memory.rs deleted file mode 100644 index cf5046f5d1a53..0000000000000 --- a/consensus/src/rand/rand_gen/storage/in_memory.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::rand::rand_gen::{ - storage::interface::RandStorage, - types::{AugData, AugDataId, CertifiedAugData, TAugmentedData}, -}; -use aptos_infallible::RwLock; -use std::collections::HashMap; - -pub struct InMemRandDb { - key_pair: RwLock)>>, - aug_data: RwLock>>, - certified_aug_data: RwLock>>, -} - -impl InMemRandDb { - pub fn new() -> Self { - Self { - key_pair: RwLock::new(None), - aug_data: RwLock::new(HashMap::new()), - certified_aug_data: RwLock::new(HashMap::new()), - } - } -} - -impl RandStorage for InMemRandDb { - fn save_key_pair_bytes(&self, epoch: u64, key_pair: Vec) -> anyhow::Result<()> { - self.key_pair.write().replace((epoch, key_pair)); - Ok(()) - } - - fn save_aug_data(&self, aug_data: &AugData) -> anyhow::Result<()> { - self.aug_data - .write() - .insert(aug_data.id(), aug_data.clone()); - Ok(()) - } - - fn save_certified_aug_data( - &self, - certified_aug_data: &CertifiedAugData, - ) -> anyhow::Result<()> { - self.certified_aug_data - .write() - .insert(certified_aug_data.id(), certified_aug_data.clone()); - Ok(()) - } - - fn get_key_pair_bytes(&self) -> anyhow::Result)>> { - Ok(self.key_pair.read().clone()) - } - - fn get_all_aug_data(&self) -> anyhow::Result)>> { - Ok(self.aug_data.read().clone().into_iter().collect()) - } - - fn get_all_certified_aug_data(&self) -> anyhow::Result)>> { - Ok(self.certified_aug_data.read().clone().into_iter().collect()) - } - - fn remove_aug_data(&self, aug_data: Vec>) -> anyhow::Result<()> { - for data in aug_data { - self.aug_data.write().remove(&data.id()); - } - Ok(()) - } - - fn remove_certified_aug_data( - &self, - certified_aug_data: Vec>, - ) -> anyhow::Result<()> { - for data in certified_aug_data { - self.certified_aug_data.write().remove(&data.id()); - } - Ok(()) - } -} diff --git a/consensus/src/rand/rand_gen/storage/interface.rs b/consensus/src/rand/rand_gen/storage/interface.rs deleted file mode 100644 index 80a391f78285e..0000000000000 --- a/consensus/src/rand/rand_gen/storage/interface.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::rand::rand_gen::types::{AugData, AugDataId, CertifiedAugData}; - -pub trait RandStorage: Send + Sync + 'static { - fn save_key_pair_bytes(&self, epoch: u64, key_pair: Vec) -> anyhow::Result<()>; - fn save_aug_data(&self, aug_data: &AugData) -> anyhow::Result<()>; - fn save_certified_aug_data( - &self, - certified_aug_data: &CertifiedAugData, - ) -> anyhow::Result<()>; - - fn get_key_pair_bytes(&self) -> anyhow::Result)>>; - fn get_all_aug_data(&self) -> anyhow::Result)>>; - fn get_all_certified_aug_data(&self) -> anyhow::Result)>>; - - fn remove_aug_data(&self, aug_data: Vec>) -> anyhow::Result<()>; - fn remove_certified_aug_data( - &self, - certified_aug_data: Vec>, - ) -> anyhow::Result<()>; -} diff --git a/consensus/src/rand/rand_gen/storage/mod.rs b/consensus/src/rand/rand_gen/storage/mod.rs deleted file mode 100644 index 856d4ab816045..0000000000000 --- a/consensus/src/rand/rand_gen/storage/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 -pub mod db; -pub mod in_memory; -pub mod interface; -mod schema; diff --git a/consensus/src/rand/rand_gen/storage/schema.rs b/consensus/src/rand/rand_gen/storage/schema.rs deleted file mode 100644 index 37b7c5b2a7e0e..0000000000000 --- a/consensus/src/rand/rand_gen/storage/schema.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::rand::rand_gen::types::{AugData, AugDataId, CertifiedAugData, TAugmentedData}; -use aptos_schemadb::{ - define_schema, - schema::{KeyCodec, Schema, ValueCodec}, - ColumnFamilyName, -}; -use std::marker::PhantomData; - -pub(crate) const KEY_PAIR_CF_NAME: ColumnFamilyName = "key_pair"; - -define_schema!(KeyPairSchema, (), (u64, Vec), KEY_PAIR_CF_NAME); - -impl KeyCodec for () { - fn encode_key(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(self)?) - } - - fn decode_key(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} - -impl ValueCodec for (u64, Vec) { - fn encode_value(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(self)?) - } - - fn decode_value(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} - -pub(crate) const AUG_DATA_CF_NAME: ColumnFamilyName = "aug_data"; -#[derive(Debug)] -pub struct AugDataSchema(PhantomData); - -impl Schema for AugDataSchema { - type Key = AugDataId; - type Value = AugData; - - const COLUMN_FAMILY_NAME: ColumnFamilyName = AUG_DATA_CF_NAME; -} - -impl KeyCodec> for AugDataId { - fn encode_key(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(self)?) - } - - fn decode_key(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} - -impl ValueCodec> for AugData { - fn encode_value(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} - -pub(crate) const CERTIFIED_AUG_DATA_CF_NAME: ColumnFamilyName = "certified_aug_data"; -#[derive(Debug)] -pub struct CertifiedAugDataSchema(PhantomData); - -impl Schema for CertifiedAugDataSchema { - type Key = AugDataId; - type Value = CertifiedAugData; - - const COLUMN_FAMILY_NAME: ColumnFamilyName = CERTIFIED_AUG_DATA_CF_NAME; -} - -impl KeyCodec> for AugDataId { - fn encode_key(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(self)?) - } - - fn decode_key(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} - -impl ValueCodec> for CertifiedAugData { - fn encode_value(&self) -> anyhow::Result> { - Ok(bcs::to_bytes(&self)?) - } - - fn decode_value(data: &[u8]) -> anyhow::Result { - Ok(bcs::from_bytes(data)?) - } -} diff --git a/consensus/src/rand/rand_gen/test_utils.rs b/consensus/src/rand/rand_gen/test_utils.rs deleted file mode 100644 index 0c2977941c823..0000000000000 --- a/consensus/src/rand/rand_gen/test_utils.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - pipeline::buffer_manager::OrderedBlocks, - rand::rand_gen::types::{MockShare, RandShare}, -}; -use aptos_consensus_types::{ - block::Block, - block_data::{BlockData, BlockType}, - common::{Author, Round}, - pipelined_block::PipelinedBlock, - quorum_cert::QuorumCert, -}; -use aptos_crypto::HashValue; -use aptos_executor_types::StateComputeResult; -use aptos_types::{ - aggregate_signature::AggregateSignature, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - randomness::RandMetadata, -}; - -pub fn create_ordered_blocks(rounds: Vec) -> OrderedBlocks { - let blocks = rounds - .into_iter() - .map(|round| { - PipelinedBlock::new( - Block::new_for_testing( - HashValue::random(), - BlockData::new_for_testing( - 1, - round, - 1, - QuorumCert::dummy(), - BlockType::Genesis, - ), - None, - ), - vec![], - StateComputeResult::new_dummy(), - ) - }) - .collect(); - OrderedBlocks { - ordered_blocks: blocks, - ordered_proof: LedgerInfoWithSignatures::new( - LedgerInfo::mock_genesis(None), - AggregateSignature::empty(), - ), - callback: Box::new(move |_, _| {}), - } -} - -pub(super) fn create_share_for_round( - epoch: u64, - round: Round, - author: Author, -) -> RandShare { - RandShare::::new( - author, - RandMetadata::new(epoch, round, HashValue::zero(), 1700000000), - MockShare, - ) -} - -pub(super) fn create_share(rand_metadata: RandMetadata, author: Author) -> RandShare { - RandShare::::new(author, rand_metadata, MockShare) -} diff --git a/consensus/src/rand/rand_gen/types.rs b/consensus/src/rand/rand_gen/types.rs deleted file mode 100644 index b48922c92f6ef..0000000000000 --- a/consensus/src/rand/rand_gen/types.rs +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::{bail, ensure}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_crypto::bls12381::Signature; -use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; -use aptos_dkg::{ - pvss::{Player, WeightedConfig}, - weighted_vuf::traits::WeightedVUF, -}; -use aptos_logger::debug; -use aptos_runtimes::spawn_rayon_thread_pool; -use aptos_types::{ - aggregate_signature::AggregateSignature, - randomness::{ - Delta, PKShare, ProofShare, RandKeys, RandMetadata, Randomness, WvufPP, APK, WVUF, - }, - validator_verifier::ValidatorVerifier, -}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use sha3::{Digest, Sha3_256}; -use std::{fmt::Debug, sync::Arc}; - -const NUM_THREADS_FOR_WVUF_DERIVATION: usize = 8; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub(super) struct MockShare; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub(super) struct MockAugData; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Share { - share: ProofShare, -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct AugmentedData { - delta: Delta, -} - -impl TShare for Share { - fn verify( - &self, - rand_config: &RandConfig, - rand_metadata: &RandMetadata, - author: &Author, - ) -> anyhow::Result<()> { - let index = *rand_config - .validator - .address_to_validator_index() - .get(author) - .unwrap(); - let maybe_apk = &rand_config.keys.certified_apks[index]; - if let Some(apk) = maybe_apk.get() { - WVUF::verify_share( - &rand_config.vuf_pp, - apk, - rand_metadata.to_bytes().as_slice(), - &self.share, - )?; - } else { - bail!( - "[RandShare] No augmented public key for validator id {}, {}", - index, - author - ); - } - Ok(()) - } - - fn generate(rand_config: &RandConfig, rand_metadata: RandMetadata) -> RandShare - where - Self: Sized, - { - let share = Share { - share: WVUF::create_share(&rand_config.keys.ask, rand_metadata.to_bytes().as_slice()), - }; - RandShare::new(rand_config.author(), rand_metadata, share) - } - - fn aggregate<'a>( - shares: impl Iterator>, - rand_config: &RandConfig, - rand_metadata: RandMetadata, - ) -> Randomness - where - Self: Sized, - { - let timer = std::time::Instant::now(); - let mut apks_and_proofs = vec![]; - for share in shares { - let id = *rand_config - .validator - .address_to_validator_index() - .get(share.author()) - .unwrap(); - let apk = rand_config.get_certified_apk(share.author()).unwrap(); // needs to have apk to verify the share - apks_and_proofs.push((Player { id }, apk.clone(), share.share().share)); - } - - let proof = WVUF::aggregate_shares(&rand_config.wconfig, &apks_and_proofs); - let pool = - spawn_rayon_thread_pool("wvuf".to_string(), Some(NUM_THREADS_FOR_WVUF_DERIVATION)); - let eval = WVUF::derive_eval( - &rand_config.wconfig, - &rand_config.vuf_pp, - rand_metadata.to_bytes().as_slice(), - &rand_config.get_all_certified_apk(), - &proof, - &pool, - ) - .expect("All APK should exist"); - debug!( - "WVUF derivation time: {} ms, number of threads: {}", - timer.elapsed().as_millis(), - NUM_THREADS_FOR_WVUF_DERIVATION - ); - let eval_bytes = bcs::to_bytes(&eval).unwrap(); - let rand_bytes = Sha3_256::digest(eval_bytes.as_slice()).to_vec(); - Randomness::new(rand_metadata.clone(), rand_bytes) - } -} - -impl TAugmentedData for AugmentedData { - fn generate(rand_config: &RandConfig) -> AugData - where - Self: Sized, - { - let delta = rand_config.get_my_delta().clone(); - rand_config - .add_certified_delta(&rand_config.author(), delta.clone()) - .expect("Add self delta should succeed"); - let data = AugmentedData { - delta: delta.clone(), - }; - AugData::new(rand_config.epoch(), rand_config.author(), data) - } - - fn augment(&self, rand_config: &RandConfig, author: &Author) { - let AugmentedData { delta } = self; - rand_config - .add_certified_delta(author, delta.clone()) - .expect("Add delta should succeed") - } - - fn verify(&self, rand_config: &RandConfig, author: &Author) -> anyhow::Result<()> { - rand_config - .derive_apk(author, self.delta.clone()) - .map(|_| ()) - } -} - -impl TShare for MockShare { - fn verify( - &self, - _rand_config: &RandConfig, - _rand_metadata: &RandMetadata, - _author: &Author, - ) -> anyhow::Result<()> { - Ok(()) - } - - fn generate(rand_config: &RandConfig, rand_metadata: RandMetadata) -> RandShare - where - Self: Sized, - { - RandShare::new(rand_config.author(), rand_metadata, Self) - } - - fn aggregate<'a>( - _shares: impl Iterator>, - _rand_config: &RandConfig, - rand_metadata: RandMetadata, - ) -> Randomness - where - Self: Sized, - { - Randomness::new(rand_metadata, vec![]) - } -} - -impl TAugmentedData for MockAugData { - fn generate(rand_config: &RandConfig) -> AugData - where - Self: Sized, - { - AugData::new(rand_config.epoch(), rand_config.author(), Self) - } - - fn augment(&self, _rand_config: &RandConfig, _author: &Author) {} - - fn verify(&self, _rand_config: &RandConfig, _author: &Author) -> anyhow::Result<()> { - Ok(()) - } -} - -pub trait TShare: - Clone + Debug + PartialEq + Send + Sync + Serialize + DeserializeOwned + 'static -{ - fn verify( - &self, - rand_config: &RandConfig, - rand_metadata: &RandMetadata, - author: &Author, - ) -> anyhow::Result<()>; - - fn generate(rand_config: &RandConfig, rand_metadata: RandMetadata) -> RandShare - where - Self: Sized; - - fn aggregate<'a>( - shares: impl Iterator>, - rand_config: &RandConfig, - rand_metadata: RandMetadata, - ) -> Randomness - where - Self: Sized; -} - -pub trait TAugmentedData: - Clone + Debug + PartialEq + Send + Sync + Serialize + DeserializeOwned + 'static -{ - fn generate(rand_config: &RandConfig) -> AugData - where - Self: Sized; - - fn augment(&self, rand_config: &RandConfig, author: &Author); - - fn verify(&self, rand_config: &RandConfig, author: &Author) -> anyhow::Result<()>; -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct ShareId { - epoch: u64, - round: Round, - author: Author, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct RandShare { - author: Author, - metadata: RandMetadata, - share: S, -} - -impl RandShare { - pub fn new(author: Author, metadata: RandMetadata, share: S) -> Self { - Self { - author, - metadata, - share, - } - } - - pub fn author(&self) -> &Author { - &self.author - } - - pub fn share(&self) -> &S { - &self.share - } - - pub fn metadata(&self) -> &RandMetadata { - &self.metadata - } - - pub fn round(&self) -> Round { - self.metadata.round() - } - - pub fn epoch(&self) -> u64 { - self.metadata.epoch() - } - - pub fn verify(&self, rand_config: &RandConfig) -> anyhow::Result<()> { - self.share.verify(rand_config, &self.metadata, &self.author) - } - - pub fn share_id(&self) -> ShareId { - ShareId { - epoch: self.epoch(), - round: self.round(), - author: self.author, - } - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct RequestShare { - epoch: u64, - rand_metadata: RandMetadata, -} - -impl RequestShare { - pub fn new(epoch: u64, rand_metadata: RandMetadata) -> Self { - Self { - epoch, - rand_metadata, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn rand_metadata(&self) -> &RandMetadata { - &self.rand_metadata - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Hash, Eq)] -pub struct AugDataId { - epoch: u64, - author: Author, -} - -impl AugDataId { - pub fn new(epoch: u64, author: Author) -> Self { - Self { epoch, author } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn author(&self) -> Author { - self.author - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] -pub struct AugData { - epoch: u64, - author: Author, - data: D, -} - -impl AugData { - pub fn new(epoch: u64, author: Author, data: D) -> Self { - Self { - epoch, - author, - data, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn id(&self) -> AugDataId { - AugDataId { - epoch: self.epoch, - author: self.author, - } - } - - pub fn author(&self) -> &Author { - &self.author - } - - pub fn verify(&self, rand_config: &RandConfig, sender: Author) -> anyhow::Result<()> { - ensure!(self.author == sender, "Invalid author"); - self.data.verify(rand_config, &self.author)?; - Ok(()) - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct AugDataSignature { - epoch: u64, - signature: Signature, -} - -impl AugDataSignature { - pub fn new(epoch: u64, signature: Signature) -> Self { - Self { epoch, signature } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn verify( - &self, - author: Author, - verifier: &ValidatorVerifier, - data: &AugData, - ) -> anyhow::Result<()> { - Ok(verifier.verify(author, data, &self.signature)?) - } - - pub fn into_signature(self) -> Signature { - self.signature - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct CertifiedAugData { - aug_data: AugData, - signatures: AggregateSignature, -} - -impl CertifiedAugData { - pub fn new(aug_data: AugData, signatures: AggregateSignature) -> Self { - Self { - aug_data, - signatures, - } - } - - pub fn epoch(&self) -> u64 { - self.aug_data.epoch() - } - - pub fn id(&self) -> AugDataId { - self.aug_data.id() - } - - pub fn author(&self) -> &Author { - self.aug_data.author() - } - - pub fn verify(&self, verifier: &ValidatorVerifier) -> anyhow::Result<()> { - verifier.verify_multi_signatures(&self.aug_data, &self.signatures)?; - Ok(()) - } - - pub fn data(&self) -> &D { - &self.aug_data.data - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct CertifiedAugDataAck { - epoch: u64, -} - -impl CertifiedAugDataAck { - pub fn new(epoch: u64) -> Self { - Self { epoch } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } -} - -#[derive(Clone)] -pub struct RandConfig { - pub author: Author, - pub epoch: u64, - pub validator: ValidatorVerifier, - // public parameters of the weighted VUF - pub vuf_pp: WvufPP, - // key shares for weighted VUF - pub keys: Arc, - // weighted config for weighted VUF - pub wconfig: WeightedConfig, -} - -impl Debug for RandConfig { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "RandConfig {{ epoch: {}, author: {}, wconfig: {:?} }}", - self.epoch, self.author, self.wconfig - ) - } -} - -impl RandConfig { - pub fn new( - author: Author, - epoch: u64, - validator: ValidatorVerifier, - vuf_pp: WvufPP, - keys: RandKeys, - wconfig: WeightedConfig, - ) -> Self { - Self { - author, - epoch, - validator, - vuf_pp, - keys: Arc::new(keys), - wconfig, - } - } - - pub fn epoch(&self) -> u64 { - self.epoch - } - - pub fn author(&self) -> Author { - self.author - } - - pub fn get_id(&self, peer: &Author) -> usize { - *self - .validator - .address_to_validator_index() - .get(peer) - .unwrap() - } - - pub fn get_certified_apk(&self, peer: &Author) -> Option<&APK> { - let index = self.get_id(peer); - self.keys.certified_apks[index].get() - } - - pub fn get_all_certified_apk(&self) -> Vec> { - self.keys - .certified_apks - .iter() - .map(|cell| cell.get().cloned()) - .collect() - } - - pub fn add_certified_apk(&self, peer: &Author, apk: APK) -> anyhow::Result<()> { - let index = self.get_id(peer); - self.keys.add_certified_apk(index, apk) - } - - fn derive_apk(&self, peer: &Author, delta: Delta) -> anyhow::Result { - let apk = WVUF::augment_pubkey(&self.vuf_pp, self.get_pk_share(peer).clone(), delta)?; - Ok(apk) - } - - pub fn add_certified_delta(&self, peer: &Author, delta: Delta) -> anyhow::Result<()> { - let apk = self.derive_apk(peer, delta)?; - self.add_certified_apk(peer, apk)?; - Ok(()) - } - - pub fn get_my_delta(&self) -> &Delta { - WVUF::get_public_delta(&self.keys.apk) - } - - pub fn get_pk_share(&self, peer: &Author) -> &PKShare { - let index = self.get_id(peer); - &self.keys.pk_shares[index] - } - - pub fn get_peer_weight(&self, peer: &Author) -> u64 { - let player = Player { - id: self.get_id(peer), - }; - self.wconfig.get_player_weight(&player) as u64 - } - - pub fn threshold(&self) -> u64 { - self.wconfig.get_threshold_weight() as u64 - } -} diff --git a/consensus/src/recovery_manager.rs b/consensus/src/recovery_manager.rs deleted file mode 100644 index 897424d4ed4a3..0000000000000 --- a/consensus/src/recovery_manager.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{BlockRetriever, BlockStore}, - counters, - error::error_kind, - monitor, - network::NetworkSender, - payload_manager::PayloadManager, - persistent_liveness_storage::{PersistentLivenessStorage, RecoveryData}, - pipeline::execution_client::TExecutionClient, - round_manager::VerifiedEvent, -}; -use anyhow::{anyhow, ensure, Context, Result}; -use aptos_channels::aptos_channel; -use aptos_consensus_types::{ - common::Author, proposal_msg::ProposalMsg, sync_info::SyncInfo, vote_msg::VoteMsg, -}; -use aptos_logger::prelude::*; -use aptos_types::{block_info::Round, epoch_state::EpochState}; -use futures::{FutureExt, StreamExt}; -use futures_channel::oneshot; -use std::{mem::Discriminant, process, sync::Arc}; - -/// If the node can't recover corresponding blocks from local storage, RecoveryManager is responsible -/// for processing the events carrying sync info and use the info to retrieve blocks from peers -pub struct RecoveryManager { - epoch_state: Arc, - network: Arc, - storage: Arc, - execution_client: Arc, - last_committed_round: Round, - max_blocks_to_request: u64, - payload_manager: Arc, -} - -impl RecoveryManager { - pub fn new( - epoch_state: Arc, - network: Arc, - storage: Arc, - execution_client: Arc, - last_committed_round: Round, - max_blocks_to_request: u64, - payload_manager: Arc, - ) -> Self { - RecoveryManager { - epoch_state, - network, - storage, - execution_client, - last_committed_round, - max_blocks_to_request, - payload_manager, - } - } - - pub async fn process_proposal_msg( - &mut self, - proposal_msg: ProposalMsg, - ) -> Result { - let author = proposal_msg.proposer(); - let sync_info = proposal_msg.sync_info(); - self.sync_up(sync_info, author).await - } - - pub async fn process_vote_msg(&mut self, vote_msg: VoteMsg) -> Result { - let author = vote_msg.vote().author(); - let sync_info = vote_msg.sync_info(); - self.sync_up(sync_info, author).await - } - - pub async fn sync_up(&mut self, sync_info: &SyncInfo, peer: Author) -> Result { - sync_info.verify(&self.epoch_state.verifier)?; - ensure!( - sync_info.highest_round() > self.last_committed_round, - "[RecoveryManager] Received sync info has lower round number than committed block" - ); - ensure!( - sync_info.epoch() == self.epoch_state.epoch, - "[RecoveryManager] Received sync info is in different epoch than committed block" - ); - let mut retriever = BlockRetriever::new( - self.network.clone(), - peer, - self.epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect(), - self.max_blocks_to_request, - ); - let recovery_data = BlockStore::fast_forward_sync( - sync_info.highest_ordered_cert(), - sync_info.highest_commit_cert(), - &mut retriever, - self.storage.clone(), - self.execution_client.clone(), - self.payload_manager.clone(), - ) - .await?; - - Ok(recovery_data) - } - - pub async fn start( - mut self, - mut event_rx: aptos_channel::Receiver< - (Author, Discriminant), - (Author, VerifiedEvent), - >, - close_rx: oneshot::Receiver>, - ) { - info!(epoch = self.epoch_state.epoch, "RecoveryManager started"); - let mut close_rx = close_rx.into_stream(); - loop { - futures::select! { - (peer_id, event) = event_rx.select_next_some() => { - let result = match event { - VerifiedEvent::ProposalMsg(proposal_msg) => { - monitor!( - "process_recovery", - self.process_proposal_msg(*proposal_msg).await - ) - } - VerifiedEvent::VoteMsg(vote_msg) => { - monitor!("process_recovery", self.process_vote_msg(*vote_msg).await) - } - VerifiedEvent::UnverifiedSyncInfo(sync_info) => { - monitor!( - "process_recovery", - self.sync_up(&sync_info, peer_id).await - ) - } - unexpected_event => Err(anyhow!("Unexpected event: {:?}", unexpected_event)), - } - .with_context(|| format!("from peer {}", peer_id)); - - match result { - Ok(_) => { - info!("Recovery finishes for epoch {}, RecoveryManager stopped. Please restart the node", self.epoch_state.epoch); - process::exit(0); - }, - Err(e) => { - counters::ERROR_COUNT.inc(); - warn!(error = ?e, kind = error_kind(&e)); - } - } - } - close_req = close_rx.select_next_some() => { - if let Ok(ack_sender) = close_req { - ack_sender.send(()).expect("[RecoveryManager] Fail to ack shutdown"); - } - break; - } - } - } - info!(epoch = self.epoch_state.epoch, "RecoveryManager stopped"); - } -} diff --git a/consensus/src/round_manager.rs b/consensus/src/round_manager.rs deleted file mode 100644 index fda3f335cea2b..0000000000000 --- a/consensus/src/round_manager.rs +++ /dev/null @@ -1,1220 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{ - tracing::{observe_block, BlockStage}, - BlockReader, BlockRetriever, BlockStore, - }, - counters, - counters::{PROPOSED_VTXN_BYTES, PROPOSED_VTXN_COUNT}, - error::{error_kind, VerifyError}, - liveness::{ - proposal_generator::ProposalGenerator, - proposer_election::ProposerElection, - round_state::{NewRoundEvent, NewRoundReason, RoundState, RoundStateLogSchema}, - unequivocal_proposer_election::UnequivocalProposerElection, - }, - logging::{LogEvent, LogSchema}, - metrics_safety_rules::MetricsSafetyRules, - monitor, - network::NetworkSender, - network_interface::ConsensusMsg, - pending_votes::VoteReceptionResult, - persistent_liveness_storage::PersistentLivenessStorage, - quorum_store::types::BatchMsg, - util::is_vtxn_expected, -}; -use anyhow::{bail, ensure, Context}; -use aptos_channels::aptos_channel; -use aptos_config::config::ConsensusConfig; -use aptos_consensus_types::{ - block::Block, - block_data::BlockType, - common::{Author, Round}, - delayed_qc_msg::DelayedQcMsg, - proof_of_store::{ProofOfStoreMsg, SignedBatchInfoMsg}, - proposal_msg::ProposalMsg, - quorum_cert::QuorumCert, - sync_info::SyncInfo, - timeout_2chain::TwoChainTimeoutCertificate, - vote::Vote, - vote_msg::VoteMsg, -}; -use aptos_infallible::{checked, Mutex}; -use aptos_logger::prelude::*; -#[cfg(test)] -use aptos_safety_rules::ConsensusState; -use aptos_safety_rules::TSafetyRules; -use aptos_types::{ - epoch_state::EpochState, - on_chain_config::{ - OnChainConsensusConfig, OnChainJWKConsensusConfig, OnChainRandomnessConfig, - ValidatorTxnConfig, - }, - validator_verifier::ValidatorVerifier, - PeerId, -}; -use fail::fail_point; -use futures::{channel::oneshot, FutureExt, StreamExt}; -use futures_channel::mpsc::UnboundedReceiver; -use serde::Serialize; -use std::{mem::Discriminant, sync::Arc, time::Duration}; -use tokio::{ - sync::oneshot as TokioOneshot, - time::{sleep, Instant}, -}; - -#[derive(Serialize, Clone)] -pub enum UnverifiedEvent { - ProposalMsg(Box), - VoteMsg(Box), - SyncInfo(Box), - BatchMsg(Box), - SignedBatchInfo(Box), - ProofOfStoreMsg(Box), -} - -pub const BACK_PRESSURE_POLLING_INTERVAL_MS: u64 = 10; - -impl UnverifiedEvent { - pub fn verify( - self, - peer_id: PeerId, - validator: &ValidatorVerifier, - quorum_store_enabled: bool, - self_message: bool, - max_num_batches: usize, - max_batch_expiry_gap_usecs: u64, - ) -> Result { - let start_time = Instant::now(); - Ok(match self { - //TODO: no need to sign and verify the proposal - UnverifiedEvent::ProposalMsg(p) => { - if !self_message { - p.verify(validator, quorum_store_enabled)?; - counters::VERIFY_MSG - .with_label_values(&["proposal"]) - .observe(start_time.elapsed().as_secs_f64()); - } - VerifiedEvent::ProposalMsg(p) - }, - UnverifiedEvent::VoteMsg(v) => { - if !self_message { - v.verify(validator)?; - counters::VERIFY_MSG - .with_label_values(&["vote"]) - .observe(start_time.elapsed().as_secs_f64()); - } - VerifiedEvent::VoteMsg(v) - }, - // sync info verification is on-demand (verified when it's used) - UnverifiedEvent::SyncInfo(s) => VerifiedEvent::UnverifiedSyncInfo(s), - UnverifiedEvent::BatchMsg(b) => { - if !self_message { - b.verify(peer_id, max_num_batches)?; - counters::VERIFY_MSG - .with_label_values(&["batch"]) - .observe(start_time.elapsed().as_secs_f64()); - } - VerifiedEvent::BatchMsg(b) - }, - UnverifiedEvent::SignedBatchInfo(sd) => { - if !self_message { - sd.verify( - peer_id, - max_num_batches, - max_batch_expiry_gap_usecs, - validator, - )?; - counters::VERIFY_MSG - .with_label_values(&["signed_batch"]) - .observe(start_time.elapsed().as_secs_f64()); - } - VerifiedEvent::SignedBatchInfo(sd) - }, - UnverifiedEvent::ProofOfStoreMsg(p) => { - if !self_message { - p.verify(max_num_batches, validator)?; - counters::VERIFY_MSG - .with_label_values(&["proof_of_store"]) - .observe(start_time.elapsed().as_secs_f64()); - } - VerifiedEvent::ProofOfStoreMsg(p) - }, - }) - } - - pub fn epoch(&self) -> anyhow::Result { - match self { - UnverifiedEvent::ProposalMsg(p) => Ok(p.epoch()), - UnverifiedEvent::VoteMsg(v) => Ok(v.epoch()), - UnverifiedEvent::SyncInfo(s) => Ok(s.epoch()), - UnverifiedEvent::BatchMsg(b) => b.epoch(), - UnverifiedEvent::SignedBatchInfo(sd) => sd.epoch(), - UnverifiedEvent::ProofOfStoreMsg(p) => p.epoch(), - } - } -} - -impl From for UnverifiedEvent { - fn from(value: ConsensusMsg) -> Self { - match value { - ConsensusMsg::ProposalMsg(m) => UnverifiedEvent::ProposalMsg(m), - ConsensusMsg::VoteMsg(m) => UnverifiedEvent::VoteMsg(m), - ConsensusMsg::SyncInfo(m) => UnverifiedEvent::SyncInfo(m), - ConsensusMsg::BatchMsg(m) => UnverifiedEvent::BatchMsg(m), - ConsensusMsg::SignedBatchInfo(m) => UnverifiedEvent::SignedBatchInfo(m), - ConsensusMsg::ProofOfStoreMsg(m) => UnverifiedEvent::ProofOfStoreMsg(m), - _ => unreachable!("Unexpected conversion"), - } - } -} - -#[derive(Debug)] -pub enum VerifiedEvent { - // network messages - ProposalMsg(Box), - VerifiedProposalMsg(Box), - VoteMsg(Box), - UnverifiedSyncInfo(Box), - BatchMsg(Box), - SignedBatchInfo(Box), - ProofOfStoreMsg(Box), - // local messages - LocalTimeout(Round), - // Shutdown the NetworkListener - Shutdown(TokioOneshot::Sender<()>), -} - -#[cfg(test)] -#[path = "round_manager_test.rs"] -mod round_manager_test; - -#[cfg(feature = "fuzzing")] -#[path = "round_manager_fuzzing.rs"] -pub mod round_manager_fuzzing; - -/// Consensus SMR is working in an event based fashion: RoundManager is responsible for -/// processing the individual events (e.g., process_new_round, process_proposal, process_vote, -/// etc.). It is exposing the async processing functions for each event type. -/// The caller is responsible for running the event loops and driving the execution via some -/// executors. -pub struct RoundManager { - epoch_state: Arc, - block_store: Arc, - round_state: RoundState, - proposer_election: UnequivocalProposerElection, - proposal_generator: ProposalGenerator, - safety_rules: Arc>, - network: Arc, - storage: Arc, - onchain_config: OnChainConsensusConfig, - vtxn_config: ValidatorTxnConfig, - buffered_proposal_tx: aptos_channel::Sender, - local_config: ConsensusConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, -} - -impl RoundManager { - pub fn new( - epoch_state: Arc, - block_store: Arc, - round_state: RoundState, - proposer_election: Arc, - proposal_generator: ProposalGenerator, - safety_rules: Arc>, - network: Arc, - storage: Arc, - onchain_config: OnChainConsensusConfig, - buffered_proposal_tx: aptos_channel::Sender, - local_config: ConsensusConfig, - randomness_config: OnChainRandomnessConfig, - jwk_consensus_config: OnChainJWKConsensusConfig, - ) -> Self { - // when decoupled execution is false, - // the counter is still static. - counters::OP_COUNTERS - .gauge("sync_only") - .set(local_config.sync_only as i64); - counters::OP_COUNTERS - .gauge("decoupled_execution") - .set(onchain_config.decoupled_execution() as i64); - let vtxn_config = onchain_config.effective_validator_txn_config(); - debug!("vtxn_config={:?}", vtxn_config); - Self { - epoch_state, - block_store, - round_state, - proposer_election: UnequivocalProposerElection::new(proposer_election), - proposal_generator, - safety_rules, - network, - storage, - onchain_config, - vtxn_config, - buffered_proposal_tx, - local_config, - randomness_config, - jwk_consensus_config, - } - } - - // TODO: Evaluate if creating a block retriever is slow and cache this if needed. - fn create_block_retriever(&self, author: Author) -> BlockRetriever { - BlockRetriever::new( - self.network.clone(), - author, - self.epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect(), - self.local_config - .max_blocks_per_sending_request(self.onchain_config.quorum_store_enabled()), - ) - } - - /// Leader: - /// - /// This event is triggered by a new quorum certificate at the previous round or a - /// timeout certificate at the previous round. In either case, if this replica is the new - /// proposer for this round, it is ready to propose and guarantee that it can create a proposal - /// that all honest replicas can vote for. While this method should only be invoked at most - /// once per round, we ensure that only at most one proposal can get generated per round to - /// avoid accidental equivocation of proposals. - /// - /// Replica: - /// - /// Do nothing - async fn process_new_round_event( - &mut self, - new_round_event: NewRoundEvent, - ) -> anyhow::Result<()> { - counters::CURRENT_ROUND.set(new_round_event.round as i64); - counters::ROUND_TIMEOUT_MS.set(new_round_event.timeout.as_millis() as i64); - match new_round_event.reason { - NewRoundReason::QCReady => { - counters::QC_ROUNDS_COUNT.inc(); - }, - NewRoundReason::Timeout => { - counters::TIMEOUT_ROUNDS_COUNT.inc(); - }, - }; - info!( - self.new_log(LogEvent::NewRound), - reason = new_round_event.reason - ); - - if self - .proposer_election - .is_valid_proposer(self.proposal_generator.author(), new_round_event.round) - { - self.log_collected_vote_stats(&new_round_event); - self.round_state.setup_leader_timeout(); - let proposal_msg = self.generate_proposal(new_round_event).await?; - #[cfg(feature = "failpoints")] - { - if self.check_whether_to_inject_reconfiguration_error() { - self.attempt_to_inject_reconfiguration_error(&proposal_msg) - .await?; - } - } - self.network.broadcast_proposal(proposal_msg).await; - counters::PROPOSALS_COUNT.inc(); - } - Ok(()) - } - - fn log_collected_vote_stats(&self, new_round_event: &NewRoundEvent) { - let prev_round_votes_for_li = new_round_event - .prev_round_votes - .iter() - .map(|(_, li_with_sig)| { - let (voting_power, votes): (Vec<_>, Vec<_>) = li_with_sig - .signatures() - .keys() - .map(|author| { - self.epoch_state - .verifier - .get_voting_power(author) - .map(|voting_power| (voting_power as u128, 1)) - .unwrap_or((0u128, 0)) - }) - .unzip(); - (voting_power.iter().sum(), votes.iter().sum()) - }) - .collect::>(); - - let (max_voting_power, max_num_votes) = prev_round_votes_for_li - .iter() - .max() - .cloned() - .unwrap_or((0, 0)); - - let (voting_powers, votes_counts): (Vec<_>, Vec<_>) = - prev_round_votes_for_li.iter().cloned().unzip(); - let conflicting_voting_power = voting_powers.into_iter().sum::() - max_voting_power; - let conflicting_num_votes = votes_counts.into_iter().sum::() - max_num_votes; - - let (timeout_voting_power, timeout_num_votes) = new_round_event - .prev_round_timeout_votes - .as_ref() - .map(|timeout_votes| { - let (voting_power, votes): (Vec<_>, Vec<_>) = timeout_votes - .signers() - .map(|author| { - self.epoch_state - .verifier - .get_voting_power(author) - .map(|voting_power| (voting_power as u128, 1)) - .unwrap_or((0u128, 0)) - }) - .unzip(); - (voting_power.iter().sum(), votes.iter().sum()) - }) - .unwrap_or((0, 0)); - - counters::PROPOSER_COLLECTED_ROUND_COUNT.inc(); - counters::PROPOSER_COLLECTED_MOST_VOTING_POWER.inc_by(max_voting_power as f64); - counters::PROPOSER_COLLECTED_CONFLICTING_VOTING_POWER - .inc_by(conflicting_voting_power as f64); - counters::PROPOSER_COLLECTED_TIMEOUT_VOTING_POWER.inc_by(timeout_voting_power as f64); - - info!( - epoch = self.epoch_state.epoch, - round = new_round_event.round, - total_voting_power = ?self.epoch_state.verifier.total_voting_power(), - max_voting_power = ?max_voting_power, - max_num_votes = max_num_votes, - conflicting_voting_power = ?conflicting_voting_power, - conflicting_num_votes = conflicting_num_votes, - timeout_voting_power = ?timeout_voting_power, - timeout_num_votes = timeout_num_votes, - "Preparing new proposal", - ); - } - - async fn generate_proposal( - &mut self, - new_round_event: NewRoundEvent, - ) -> anyhow::Result { - // Proposal generator will ensure that at most one proposal is generated per round - let sync_info = self.block_store.sync_info(); - let sender = self.network.clone(); - let callback = async move { - sender.broadcast_sync_info(sync_info).await; - } - .boxed(); - - let proposal = self - .proposal_generator - .generate_proposal(new_round_event.round, &mut self.proposer_election, callback) - .await?; - let signature = self.safety_rules.lock().sign_proposal(&proposal)?; - let signed_proposal = - Block::new_proposal_from_block_data_and_signature(proposal, signature); - observe_block(signed_proposal.timestamp_usecs(), BlockStage::SIGNED); - info!(self.new_log(LogEvent::Propose), "{}", signed_proposal); - Ok(ProposalMsg::new( - signed_proposal, - self.block_store.sync_info(), - )) - } - - /// Process the proposal message: - /// 1. ensure after processing sync info, we're at the same round as the proposal - /// 2. execute and decide whether to vote for the proposal - pub async fn process_proposal_msg(&mut self, proposal_msg: ProposalMsg) -> anyhow::Result<()> { - fail_point!("consensus::process_proposal_msg", |_| { - Err(anyhow::anyhow!("Injected error in process_proposal_msg")) - }); - - observe_block( - proposal_msg.proposal().timestamp_usecs(), - BlockStage::ROUND_MANAGER_RECEIVED, - ); - info!( - self.new_log(LogEvent::ReceiveProposal) - .remote_peer(proposal_msg.proposer()), - block_round = proposal_msg.proposal().round(), - block_hash = proposal_msg.proposal().id(), - block_parent_hash = proposal_msg.proposal().quorum_cert().certified_block().id(), - ); - - if self - .ensure_round_and_sync_up( - proposal_msg.proposal().round(), - proposal_msg.sync_info(), - proposal_msg.proposer(), - ) - .await - .context("[RoundManager] Process proposal")? - { - self.process_proposal(proposal_msg.take_proposal()).await - } else { - bail!( - "Stale proposal {}, current round {}", - proposal_msg.proposal(), - self.round_state.current_round() - ); - } - } - - pub async fn process_delayed_proposal_msg(&mut self, proposal: Block) -> anyhow::Result<()> { - if proposal.round() != self.round_state.current_round() { - bail!( - "Discarding stale delayed proposal {}, current round {}", - proposal, - self.round_state.current_round() - ); - } - - self.process_verified_proposal(proposal).await - } - - pub async fn process_delayed_qc_msg(&mut self, msg: DelayedQcMsg) -> anyhow::Result<()> { - ensure!( - msg.vote.vote_data().proposed().round() == self.round_state.current_round(), - "Discarding stale delayed QC for round {}, current round {}", - msg.vote.vote_data().proposed().round(), - self.round_state.current_round() - ); - let vote = msg.vote().clone(); - let vote_reception_result = self - .round_state - .process_delayed_qc_msg(&self.epoch_state.verifier, msg) - .await; - trace!( - "Received delayed QC message and vote reception result is {:?}", - vote_reception_result - ); - self.process_vote_reception_result(&vote, vote_reception_result) - .await - } - - /// Sync to the sync info sending from peer if it has newer certificates. - async fn sync_up(&mut self, sync_info: &SyncInfo, author: Author) -> anyhow::Result<()> { - let local_sync_info = self.block_store.sync_info(); - if sync_info.has_newer_certificates(&local_sync_info) { - info!( - self.new_log(LogEvent::ReceiveNewCertificate) - .remote_peer(author), - "Local state {}, remote state {}", local_sync_info, sync_info - ); - // Some information in SyncInfo is ahead of what we have locally. - // First verify the SyncInfo (didn't verify it in the yet). - sync_info - .verify(&self.epoch_state().verifier) - .map_err(|e| { - error!( - SecurityEvent::InvalidSyncInfoMsg, - sync_info = sync_info, - remote_peer = author, - error = ?e, - ); - VerifyError::from(e) - })?; - let result = self - .block_store - .add_certs(sync_info, self.create_block_retriever(author)) - .await; - self.process_certificates().await?; - result - } else { - Ok(()) - } - } - - /// The function makes sure that it ensures the message_round equal to what we have locally, - /// brings the missing dependencies from the QC and LedgerInfo of the given sync info and - /// update the round_state with the certificates if succeed. - /// Returns Ok(true) if the sync succeeds and the round matches so we can process further. - /// Returns Ok(false) if the message is stale. - /// Returns Error in case sync mgr failed to bring the missing dependencies. - /// We'll try to help the remote if the SyncInfo lags behind and the flag is set. - pub async fn ensure_round_and_sync_up( - &mut self, - message_round: Round, - sync_info: &SyncInfo, - author: Author, - ) -> anyhow::Result { - if message_round < self.round_state.current_round() { - return Ok(false); - } - self.sync_up(sync_info, author).await?; - ensure!( - message_round == self.round_state.current_round(), - "After sync, round {} doesn't match local {}", - message_round, - self.round_state.current_round() - ); - Ok(true) - } - - /// Process the SyncInfo sent by peers to catch up to latest state. - pub async fn process_sync_info_msg( - &mut self, - sync_info: SyncInfo, - peer: Author, - ) -> anyhow::Result<()> { - fail_point!("consensus::process_sync_info_msg", |_| { - Err(anyhow::anyhow!("Injected error in process_sync_info_msg")) - }); - info!( - self.new_log(LogEvent::ReceiveSyncInfo).remote_peer(peer), - "{}", sync_info - ); - self.ensure_round_and_sync_up(checked!((sync_info.highest_round()) + 1)?, &sync_info, peer) - .await - .context("[RoundManager] Failed to process sync info msg")?; - Ok(()) - } - - fn sync_only(&self) -> bool { - let sync_or_not = self.local_config.sync_only || self.block_store.vote_back_pressure(); - counters::OP_COUNTERS - .gauge("sync_only") - .set(sync_or_not as i64); - - sync_or_not - } - - /// The replica broadcasts a "timeout vote message", which includes the round signature, which - /// can be aggregated to a TimeoutCertificate. - /// The timeout vote message can be one of the following three options: - /// 1) In case a validator has previously voted in this round, it repeats the same vote and sign - /// a timeout. - /// 2) Otherwise vote for a NIL block and sign a timeout. - /// Note this function returns Err even if messages are broadcasted successfully because timeout - /// is considered as error. It only returns Ok(()) when the timeout is stale. - pub async fn process_local_timeout(&mut self, round: Round) -> anyhow::Result<()> { - if !self.round_state.process_local_timeout(round) { - return Ok(()); - } - - if self.sync_only() { - self.network - .broadcast_sync_info(self.block_store.sync_info()) - .await; - bail!("[RoundManager] sync_only flag is set, broadcasting SyncInfo"); - } - - let (is_nil_vote, mut timeout_vote) = match self.round_state.vote_sent() { - Some(vote) if vote.vote_data().proposed().round() == round => { - (vote.vote_data().is_for_nil(), vote) - }, - _ => { - // Didn't vote in this round yet, generate a backup vote - let nil_block = self - .proposal_generator - .generate_nil_block(round, &mut self.proposer_election)?; - info!( - self.new_log(LogEvent::VoteNIL), - "Planning to vote for a NIL block {}", nil_block - ); - counters::VOTE_NIL_COUNT.inc(); - let nil_vote = self.execute_and_vote(nil_block).await?; - (true, nil_vote) - }, - }; - - if !timeout_vote.is_timeout() { - let timeout = timeout_vote - .generate_2chain_timeout(self.block_store.highest_quorum_cert().as_ref().clone()); - let signature = self - .safety_rules - .lock() - .sign_timeout_with_qc( - &timeout, - self.block_store.highest_2chain_timeout_cert().as_deref(), - ) - .context("[RoundManager] SafetyRules signs 2-chain timeout")?; - timeout_vote.add_2chain_timeout(timeout, signature); - } - - self.round_state.record_vote(timeout_vote.clone()); - let timeout_vote_msg = VoteMsg::new(timeout_vote, self.block_store.sync_info()); - self.network.broadcast_timeout_vote(timeout_vote_msg).await; - warn!( - round = round, - remote_peer = self.proposer_election.get_valid_proposer(round), - voted_nil = is_nil_vote, - event = LogEvent::Timeout, - ); - bail!("Round {} timeout, broadcast to all peers", round); - } - - /// This function is called only after all the dependencies of the given QC have been retrieved. - async fn process_certificates(&mut self) -> anyhow::Result<()> { - let sync_info = self.block_store.sync_info(); - if let Some(new_round_event) = self.round_state.process_certificates(sync_info) { - self.process_new_round_event(new_round_event).await?; - } - Ok(()) - } - - /// This function processes a proposal for the current round: - /// 1. Filter if it's proposed by valid proposer. - /// 2. Execute and add it to a block store. - /// 3. Try to vote for it following the safety rules. - /// 4. In case a validator chooses to vote, send the vote to the representatives at the next - /// round. - async fn process_proposal(&mut self, proposal: Block) -> anyhow::Result<()> { - let author = proposal - .author() - .expect("Proposal should be verified having an author"); - - if !self.vtxn_config.enabled() - && matches!( - proposal.block_data().block_type(), - BlockType::ProposalExt(_) - ) - { - counters::UNEXPECTED_PROPOSAL_EXT_COUNT.inc(); - bail!("ProposalExt unexpected while the feature is disabled."); - } - - if let Some(vtxns) = proposal.validator_txns() { - for vtxn in vtxns { - ensure!( - is_vtxn_expected(&self.randomness_config, &self.jwk_consensus_config, vtxn), - "unexpected validator txn: {:?}", - vtxn.topic() - ); - } - } - - let (num_validator_txns, validator_txns_total_bytes): (usize, usize) = - proposal.validator_txns().map_or((0, 0), |txns| { - txns.iter().fold((0, 0), |(count_acc, size_acc), txn| { - (count_acc + 1, size_acc + txn.size_in_bytes()) - }) - }); - - let num_validator_txns = num_validator_txns as u64; - let validator_txns_total_bytes = validator_txns_total_bytes as u64; - let vtxn_count_limit = self.vtxn_config.per_block_limit_txn_count(); - let vtxn_bytes_limit = self.vtxn_config.per_block_limit_total_bytes(); - let author_hex = author.to_hex(); - PROPOSED_VTXN_COUNT - .with_label_values(&[&author_hex]) - .inc_by(num_validator_txns); - PROPOSED_VTXN_BYTES - .with_label_values(&[&author_hex]) - .inc_by(validator_txns_total_bytes); - info!( - vtxn_count_limit = vtxn_count_limit, - vtxn_count_proposed = num_validator_txns, - vtxn_bytes_limit = vtxn_bytes_limit, - vtxn_bytes_proposed = validator_txns_total_bytes, - proposer = author_hex, - "Summarizing proposed validator txns." - ); - - ensure!( - num_validator_txns <= vtxn_count_limit, - "process_proposal failed with per-block vtxn count limit exceeded: limit={}, actual={}", - self.vtxn_config.per_block_limit_txn_count(), - num_validator_txns - ); - ensure!( - validator_txns_total_bytes <= vtxn_bytes_limit, - "process_proposal failed with per-block vtxn bytes limit exceeded: limit={}, actual={}", - self.vtxn_config.per_block_limit_total_bytes(), - validator_txns_total_bytes - ); - let payload_len = proposal.payload().map_or(0, |payload| payload.len()); - let payload_size = proposal.payload().map_or(0, |payload| payload.size()); - ensure!( - num_validator_txns + payload_len as u64 <= self.local_config.max_receiving_block_txns, - "Payload len {} exceeds the limit {}", - payload_len, - self.local_config.max_receiving_block_txns, - ); - - ensure!( - validator_txns_total_bytes + payload_size as u64 - <= self.local_config.max_receiving_block_bytes, - "Payload size {} exceeds the limit {}", - payload_size, - self.local_config.max_receiving_block_bytes, - ); - - ensure!( - self.proposer_election.is_valid_proposal(&proposal), - "[RoundManager] Proposer {} for block {} is not a valid proposer for this round or created duplicate proposal", - author, - proposal, - ); - - // Validate that failed_authors list is correctly specified in the block. - let expected_failed_authors = self.proposal_generator.compute_failed_authors( - proposal.round(), - proposal.quorum_cert().certified_block().round(), - false, - &mut self.proposer_election, - ); - ensure!( - proposal.block_data().failed_authors().map_or(false, |failed_authors| *failed_authors == expected_failed_authors), - "[RoundManager] Proposal for block {} has invalid failed_authors list {:?}, expected {:?}", - proposal.round(), - proposal.block_data().failed_authors(), - expected_failed_authors, - ); - - let block_time_since_epoch = Duration::from_micros(proposal.timestamp_usecs()); - - ensure!( - block_time_since_epoch < self.round_state.current_round_deadline(), - "[RoundManager] Waiting until proposal block timestamp usecs {:?} \ - would exceed the round duration {:?}, hence will not vote for this round", - block_time_since_epoch, - self.round_state.current_round_deadline(), - ); - - observe_block(proposal.timestamp_usecs(), BlockStage::SYNCED); - if self.block_store.vote_back_pressure() { - counters::CONSENSUS_WITHOLD_VOTE_BACKPRESSURE_TRIGGERED.observe(1.0); - // In case of back pressure, we delay processing proposal. This is done by resending the - // same proposal to self after some time. Even if processing proposal is delayed, we add - // the block to the block store so that we don't need to fetch it from remote once we - // are out of the backpressure. Please note that delayed processing of proposal is not - // guaranteed to add the block to the block store if we don't get out of the backpressure - // before the timeout, so this is needed to ensure that the proposed block is added to - // the block store irrespective. Also, it is possible that delayed processing of proposal - // tries to add the same block again, which is okay as `execute_and_insert_block` call - // is idempotent. - self.block_store - .insert_ordered_block(proposal.clone()) - .await - .context("[RoundManager] Failed to execute_and_insert the block")?; - self.resend_verified_proposal_to_self( - proposal, - author, - BACK_PRESSURE_POLLING_INTERVAL_MS, - self.local_config.round_initial_timeout_ms, - ) - .await; - Ok(()) - } else { - counters::CONSENSUS_WITHOLD_VOTE_BACKPRESSURE_TRIGGERED.observe(0.0); - self.process_verified_proposal(proposal).await - } - } - - async fn resend_verified_proposal_to_self( - &self, - proposal: Block, - author: Author, - polling_interval_ms: u64, - timeout_ms: u64, - ) { - let start = Instant::now(); - let block_store = self.block_store.clone(); - let self_sender = self.buffered_proposal_tx.clone(); - let event = VerifiedEvent::VerifiedProposalMsg(Box::new(proposal)); - tokio::spawn(async move { - while start.elapsed() < Duration::from_millis(timeout_ms) { - if !block_store.vote_back_pressure() { - if let Err(e) = self_sender.push(author, event) { - warn!("Failed to send event to round manager {:?}", e); - } - break; - } - sleep(Duration::from_millis(polling_interval_ms)).await; - } - }); - } - - pub async fn process_verified_proposal(&mut self, proposal: Block) -> anyhow::Result<()> { - let proposal_round = proposal.round(); - let vote = self - .execute_and_vote(proposal) - .await - .context("[RoundManager] Process proposal")?; - self.round_state.record_vote(vote.clone()); - let vote_msg = VoteMsg::new(vote.clone(), self.block_store.sync_info()); - - if self.local_config.broadcast_vote { - info!(self.new_log(LogEvent::Vote), "{}", vote); - self.network.broadcast_vote(vote_msg).await; - } else { - let recipient = self - .proposer_election - .get_valid_proposer(proposal_round + 1); - info!( - self.new_log(LogEvent::Vote).remote_peer(recipient), - "{}", vote - ); - self.network.send_vote(vote_msg, vec![recipient]).await; - } - Ok(()) - } - - /// The function generates a VoteMsg for a given proposed_block: - /// * first execute the block and add it to the block store - /// * then verify the voting rules - /// * save the updated state to consensus DB - /// * return a VoteMsg with the LedgerInfo to be committed in case the vote gathers QC. - async fn execute_and_vote(&mut self, proposed_block: Block) -> anyhow::Result { - let executed_block = self - .block_store - .insert_ordered_block(proposed_block) - .await - .context("[RoundManager] Failed to execute_and_insert the block")?; - - // Short circuit if already voted. - ensure!( - self.round_state.vote_sent().is_none(), - "[RoundManager] Already vote on this round {}", - self.round_state.current_round() - ); - - ensure!( - !self.sync_only(), - "[RoundManager] sync_only flag is set, stop voting" - ); - - let vote_proposal = executed_block.vote_proposal(); - let vote_result = self.safety_rules.lock().construct_and_sign_vote_two_chain( - &vote_proposal, - self.block_store.highest_2chain_timeout_cert().as_deref(), - ); - let vote = vote_result.context(format!( - "[RoundManager] SafetyRules Rejected {}", - executed_block.block() - ))?; - if !executed_block.block().is_nil_block() { - observe_block(executed_block.block().timestamp_usecs(), BlockStage::VOTED); - } - - self.storage - .save_vote(&vote) - .context("[RoundManager] Fail to persist last vote")?; - - Ok(vote) - } - - /// Upon new vote: - /// 1. Ensures we're processing the vote from the same round as local round - /// 2. Filter out votes for rounds that should not be processed by this validator (to avoid - /// potential attacks). - /// 2. Add the vote to the pending votes and check whether it finishes a QC. - /// 3. Once the QC/TC successfully formed, notify the RoundState. - pub async fn process_vote_msg(&mut self, vote_msg: VoteMsg) -> anyhow::Result<()> { - fail_point!("consensus::process_vote_msg", |_| { - Err(anyhow::anyhow!("Injected error in process_vote_msg")) - }); - // Check whether this validator is a valid recipient of the vote. - if self - .ensure_round_and_sync_up( - vote_msg.vote().vote_data().proposed().round(), - vote_msg.sync_info(), - vote_msg.vote().author(), - ) - .await - .context("[RoundManager] Stop processing vote")? - { - self.process_vote(vote_msg.vote()) - .await - .context("[RoundManager] Add a new vote")?; - } - Ok(()) - } - - /// Add a vote to the pending votes. - /// If a new QC / TC is formed then - /// 1) fetch missing dependencies if required, and then - /// 2) call process_certificates(), which will start a new round in return. - async fn process_vote(&mut self, vote: &Vote) -> anyhow::Result<()> { - let round = vote.vote_data().proposed().round(); - - info!( - self.new_log(LogEvent::ReceiveVote) - .remote_peer(vote.author()), - vote = %vote, - vote_epoch = vote.vote_data().proposed().epoch(), - vote_round = vote.vote_data().proposed().round(), - vote_id = vote.vote_data().proposed().id(), - vote_state = vote.vote_data().proposed().executed_state_id(), - is_timeout = vote.is_timeout(), - ); - - if !self.local_config.broadcast_vote && !vote.is_timeout() { - // Unlike timeout votes regular votes are sent to the leaders of the next round only. - let next_round = round + 1; - ensure!( - self.proposer_election - .is_valid_proposer(self.proposal_generator.author(), next_round), - "[RoundManager] Received {}, but I am not a valid proposer for round {}, ignore.", - vote, - next_round - ); - } - - let block_id = vote.vote_data().proposed().id(); - // Check if the block already had a QC - if self - .block_store - .get_quorum_cert_for_block(block_id) - .is_some() - { - return Ok(()); - } - let vote_reception_result = self - .round_state - .insert_vote(vote, &self.epoch_state.verifier); - self.process_vote_reception_result(vote, vote_reception_result) - .await - } - - async fn process_vote_reception_result( - &mut self, - vote: &Vote, - result: VoteReceptionResult, - ) -> anyhow::Result<()> { - let round = vote.vote_data().proposed().round(); - match result { - VoteReceptionResult::NewQuorumCertificate(qc) => { - if !vote.is_timeout() { - observe_block( - qc.certified_block().timestamp_usecs(), - BlockStage::QC_AGGREGATED, - ); - } - self.new_qc_aggregated(qc, vote.author()).await - }, - VoteReceptionResult::New2ChainTimeoutCertificate(tc) => { - self.new_2chain_tc_aggregated(tc).await - }, - VoteReceptionResult::EchoTimeout(_) if !self.round_state.is_vote_timeout() => { - self.process_local_timeout(round).await - }, - VoteReceptionResult::VoteAdded(_) - | VoteReceptionResult::VoteAddedQCDelayed(_) - | VoteReceptionResult::EchoTimeout(_) - | VoteReceptionResult::DuplicateVote => Ok(()), - e => Err(anyhow::anyhow!("{:?}", e)), - } - } - - async fn new_qc_aggregated( - &mut self, - qc: Arc, - preferred_peer: Author, - ) -> anyhow::Result<()> { - let result = self - .block_store - .insert_quorum_cert(&qc, &mut self.create_block_retriever(preferred_peer)) - .await - .context("[RoundManager] Failed to process a newly aggregated QC"); - self.process_certificates().await?; - result - } - - async fn new_2chain_tc_aggregated( - &mut self, - tc: Arc, - ) -> anyhow::Result<()> { - let result = self - .block_store - .insert_2chain_timeout_certificate(tc) - .context("[RoundManager] Failed to process a newly aggregated 2-chain TC"); - self.process_certificates().await?; - result - } - - /// To jump start new round with the current certificates we have. - pub async fn init(&mut self, last_vote_sent: Option) { - let new_round_event = self - .round_state - .process_certificates(self.block_store.sync_info()) - .expect("Can not jump start a round_state from existing certificates."); - if let Some(vote) = last_vote_sent { - self.round_state.record_vote(vote); - } - if let Err(e) = self.process_new_round_event(new_round_event).await { - warn!(error = ?e, "[RoundManager] Error during start"); - } - } - - /// Inspect the current consensus state. - #[cfg(test)] - pub fn consensus_state(&mut self) -> ConsensusState { - self.safety_rules.lock().consensus_state().unwrap() - } - - #[cfg(test)] - pub fn set_safety_rules(&mut self, safety_rules: Arc>) { - self.safety_rules = safety_rules - } - - pub fn epoch_state(&self) -> &EpochState { - &self.epoch_state - } - - pub fn round_state(&self) -> &RoundState { - &self.round_state - } - - fn new_log(&self, event: LogEvent) -> LogSchema { - LogSchema::new(event) - .round(self.round_state.current_round()) - .epoch(self.epoch_state.epoch) - } - - /// Mainloop of processing messages. - pub async fn start( - mut self, - mut event_rx: aptos_channel::Receiver< - (Author, Discriminant), - (Author, VerifiedEvent), - >, - mut buffered_proposal_rx: aptos_channel::Receiver, - mut delayed_qc_rx: UnboundedReceiver, - close_rx: oneshot::Receiver>, - ) { - info!(epoch = self.epoch_state().epoch, "RoundManager started"); - let mut close_rx = close_rx.into_stream(); - loop { - tokio::select! { - biased; - close_req = close_rx.select_next_some() => { - if let Ok(ack_sender) = close_req { - ack_sender.send(()).expect("[RoundManager] Fail to ack shutdown"); - } - break; - } - delayed_qc_msg = delayed_qc_rx.select_next_some() => { - let result = monitor!( - "process_delayed_qc", - self.process_delayed_qc_msg(delayed_qc_msg).await - ); - match result { - Ok(_) => trace!(RoundStateLogSchema::new(self.round_state())), - Err(e) => { - counters::ERROR_COUNT.inc(); - warn!(error = ?e, kind = error_kind(&e), RoundStateLogSchema::new(self.round_state())); - } - } - }, - proposal = buffered_proposal_rx.select_next_some() => { - let mut proposals = vec![proposal]; - while let Some(Some(proposal)) = buffered_proposal_rx.next().now_or_never() { - proposals.push(proposal); - } - let get_round = |event: &VerifiedEvent| { - match event { - VerifiedEvent::ProposalMsg(p) => p.proposal().round(), - VerifiedEvent::VerifiedProposalMsg(p) => p.round(), - unexpected_event => unreachable!("Unexpected event {:?}", unexpected_event), - } - }; - proposals.sort_by_key(get_round); - // If the first proposal is not for the next round, we only process the last proposal. - // to avoid going through block retrieval of many garbage collected rounds. - if self.round_state.current_round() + 1 < get_round(&proposals[0]) { - proposals = vec![proposals.pop().unwrap()]; - } - for proposal in proposals { - let result = match proposal { - VerifiedEvent::ProposalMsg(proposal_msg) => { - monitor!( - "process_proposal", - self.process_proposal_msg(*proposal_msg).await - ) - } - VerifiedEvent::VerifiedProposalMsg(proposal_msg) => { - monitor!( - "process_verified_proposal", - self.process_delayed_proposal_msg(*proposal_msg).await - ) - } - unexpected_event => unreachable!("Unexpected event: {:?}", unexpected_event), - }; - let round_state = self.round_state(); - match result { - Ok(_) => trace!(RoundStateLogSchema::new(round_state)), - Err(e) => { - counters::ERROR_COUNT.inc(); - warn!(error = ?e, kind = error_kind(&e), RoundStateLogSchema::new(round_state)); - } - } - } - }, - (peer_id, event) = event_rx.select_next_some() => { - let result = match event { - VerifiedEvent::VoteMsg(vote_msg) => { - monitor!("process_vote", self.process_vote_msg(*vote_msg).await) - } - VerifiedEvent::UnverifiedSyncInfo(sync_info) => { - monitor!( - "process_sync_info", - self.process_sync_info_msg(*sync_info, peer_id).await - ) - } - VerifiedEvent::LocalTimeout(round) => monitor!( - "process_local_timeout", - self.process_local_timeout(round).await - ), - unexpected_event => unreachable!("Unexpected event: {:?}", unexpected_event), - } - .with_context(|| format!("from peer {}", peer_id)); - - let round_state = self.round_state(); - match result { - Ok(_) => trace!(RoundStateLogSchema::new(round_state)), - Err(e) => { - counters::ERROR_COUNT.inc(); - warn!(error = ?e, kind = error_kind(&e), RoundStateLogSchema::new(round_state)); - } - } - } - } - } - info!(epoch = self.epoch_state().epoch, "RoundManager stopped"); - } - - #[cfg(feature = "failpoints")] - fn check_whether_to_inject_reconfiguration_error(&self) -> bool { - fail_point!("consensus::inject_reconfiguration_error", |_| true); - false - } - - /// Given R1 <- B2 if R1 has the reconfiguration txn, we inject error on B2 if R1.round + 1 = B2.round - /// Direct suffix is checked by parent.has_reconfiguration && !parent.parent.has_reconfiguration - /// The error is injected by sending proposals to half of the validators to force a timeout. - /// - /// It's only enabled with fault injection (failpoints feature). - #[cfg(feature = "failpoints")] - async fn attempt_to_inject_reconfiguration_error( - &self, - proposal_msg: &ProposalMsg, - ) -> anyhow::Result<()> { - let block_data = proposal_msg.proposal().block_data(); - let direct_suffix = block_data.is_reconfiguration_suffix() - && !block_data - .quorum_cert() - .parent_block() - .has_reconfiguration(); - let continuous_round = - block_data.round() == block_data.quorum_cert().certified_block().round() + 1; - let should_inject = direct_suffix && continuous_round; - if should_inject { - let mut half_peers: Vec<_> = self - .epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect(); - half_peers.truncate(half_peers.len() / 2); - self.network - .send_proposal(proposal_msg.clone(), half_peers) - .await; - Err(anyhow::anyhow!("Injected error in reconfiguration suffix")) - } else { - Ok(()) - } - } -} diff --git a/consensus/src/round_manager_fuzzing.rs b/consensus/src/round_manager_fuzzing.rs deleted file mode 100644 index 8934bbb1cec49..0000000000000 --- a/consensus/src/round_manager_fuzzing.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::BlockStore, - liveness::{ - proposal_generator::{ - ChainHealthBackoffConfig, PipelineBackpressureConfig, ProposalGenerator, - }, - rotating_proposer_election::RotatingProposer, - round_state::{ExponentialTimeInterval, NewRoundEvent, NewRoundReason, RoundState}, - }, - metrics_safety_rules::MetricsSafetyRules, - network::NetworkSender, - network_interface::{ConsensusNetworkClient, DIRECT_SEND, RPC}, - payload_manager::PayloadManager, - persistent_liveness_storage::{PersistentLivenessStorage, RecoveryData}, - pipeline::execution_client::DummyExecutionClient, - round_manager::RoundManager, - test_utils::{MockPayloadManager, MockStorage}, - util::{mock_time_service::SimulatedTimeService, time_service::TimeService}, -}; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::{ - config::{ConsensusConfig, QcAggregatorType}, - network_id::NetworkId, -}; -use aptos_consensus_types::proposal_msg::ProposalMsg; -use aptos_infallible::Mutex; -use aptos_network::{ - application::{interface::NetworkClient, storage::PeersAndMetadata}, - peer_manager::{ConnectionRequestSender, PeerManagerRequestSender}, - protocols::{network, network::NewNetworkSender}, -}; -use aptos_safety_rules::{test_utils, SafetyRules, TSafetyRules}; -use aptos_types::{ - aggregate_signature::AggregateSignature, - epoch_change::EpochChangeProof, - epoch_state::EpochState, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - on_chain_config::{ - OnChainConsensusConfig, OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorSet, - ValidatorTxnConfig, - }, - validator_info::ValidatorInfo, - validator_signer::ValidatorSigner, - validator_verifier::ValidatorVerifier, -}; -use futures::{channel::mpsc, executor::block_on}; -use futures_channel::mpsc::unbounded; -use maplit::hashmap; -use once_cell::sync::Lazy; -use std::{sync::Arc, time::Duration}; -use tokio::runtime::Runtime; - -// This generates a proposal for round 1 -pub fn generate_corpus_proposal() -> Vec { - let mut round_manager = create_node_for_fuzzing(); - block_on(async { - let proposal = round_manager - .generate_proposal(NewRoundEvent { - round: 1, - reason: NewRoundReason::QCReady, - timeout: std::time::Duration::new(5, 0), - prev_round_votes: Vec::new(), - prev_round_timeout_votes: None, - }) - .await; - // serialize and return proposal - serde_json::to_vec(&proposal.unwrap()).unwrap() - }) -} - -// optimization for the fuzzer -static STATIC_RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); -static FUZZING_SIGNER: Lazy = Lazy::new(|| ValidatorSigner::from_int(1)); - -// helpers -fn build_empty_store( - storage: Arc, - initial_data: RecoveryData, -) -> Arc { - let (_commit_cb_sender, _commit_cb_receiver) = mpsc::unbounded::(); - - Arc::new(BlockStore::new( - storage, - initial_data, - Arc::new(DummyExecutionClient), - 10, // max pruned blocks in mem - Arc::new(SimulatedTimeService::new()), - 10, - Arc::from(PayloadManager::DirectMempool), - )) -} - -// helpers for safety rule initialization -fn make_initial_epoch_change_proof(signer: &ValidatorSigner) -> EpochChangeProof { - let validator_info = - ValidatorInfo::new_with_test_network_keys(signer.author(), signer.public_key(), 1, 0); - let validator_set = ValidatorSet::new(vec![validator_info]); - let li = LedgerInfo::mock_genesis(Some(validator_set)); - let lis = LedgerInfoWithSignatures::new(li, AggregateSignature::empty()); - EpochChangeProof::new(vec![lis], false) -} - -// TODO: MockStorage -> EmptyStorage -fn create_round_state() -> RoundState { - let base_timeout = std::time::Duration::new(60, 0); - let time_interval = Box::new(ExponentialTimeInterval::fixed(base_timeout)); - let (round_timeout_sender, _) = aptos_channels::new_test(1_024); - let (delayed_qc_tx, _) = unbounded(); - let time_service = Arc::new(SimulatedTimeService::new()); - - RoundState::new( - time_interval, - time_service, - round_timeout_sender, - delayed_qc_tx, - QcAggregatorType::NoDelay, - ) -} - -// Creates an RoundManager for fuzzing -fn create_node_for_fuzzing() -> RoundManager { - // signer is re-used accross fuzzing runs - let signer = FUZZING_SIGNER.clone(); - - // TODO: remove - let validator = ValidatorVerifier::new_single(signer.author(), signer.public_key()); - let validator_set = (&validator).into(); - - // TODO: EmptyStorage - let (initial_data, storage) = MockStorage::start_for_testing(validator_set); - - // TODO: remove - let proof = make_initial_epoch_change_proof(&signer); - let mut safety_rules = SafetyRules::new(test_utils::test_storage(&signer)); - safety_rules.initialize(&proof).unwrap(); - - // TODO: mock channels - let (network_reqs_tx, _network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - PeersAndMetadata::new(&[NetworkId::Validator]), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - - let (self_sender, _self_receiver) = aptos_channels::new_unbounded_test(); - - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: storage.get_validator_set().into(), - }); - let network = Arc::new(NetworkSender::new( - signer.author(), - consensus_network_client, - self_sender, - epoch_state.verifier.clone(), - )); - - // TODO: mock - let block_store = build_empty_store(storage.clone(), initial_data); - - // TODO: remove - let time_service = Arc::new(SimulatedTimeService::new()); - block_on(time_service.sleep(Duration::from_millis(1))); - - // TODO: remove - let proposal_generator = ProposalGenerator::new( - signer.author(), - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - time_service, - Duration::ZERO, - 1, - 1024, - 1, - 1024, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - ValidatorTxnConfig::default_disabled(), - true, - ); - - // - let round_state = create_round_state(); - - // TODO: have two different nodes, one for proposing, one for accepting a proposal - let proposer_election = Arc::new(RotatingProposer::new(vec![signer.author()], 1)); - - let (round_manager_tx, _) = aptos_channel::new(QueueStyle::LIFO, 1, None); - - // event processor - RoundManager::new( - epoch_state, - Arc::clone(&block_store), - round_state, - proposer_election, - proposal_generator, - Arc::new(Mutex::new(MetricsSafetyRules::new( - Box::new(safety_rules), - storage.clone(), - ))), - network, - storage, - OnChainConsensusConfig::default(), - round_manager_tx, - ConsensusConfig::default(), - OnChainRandomnessConfig::default_enabled(), - OnChainJWKConsensusConfig::default_enabled(), - ) -} - -// This functions fuzzes a Proposal protobuffer (not a ConsensusMsg) -pub fn fuzz_proposal(data: &[u8]) { - // create node - let mut round_manager = create_node_for_fuzzing(); - - let proposal: ProposalMsg = match serde_json::from_slice(data) { - Ok(xx) => xx, - Err(_) => { - if cfg!(test) { - panic!(); - } - return; - }, - }; - - let proposal = match proposal.verify_well_formed() { - Ok(_) => proposal, - Err(e) => { - println!("{:?}", e); - if cfg!(test) { - panic!(); - } - return; - }, - }; - - block_on(async move { - // TODO: make sure this obtains a vote when testing - // TODO: make sure that if this obtains a vote, it's for round 1, etc. - let _ = round_manager.process_proposal_msg(proposal).await; - }); -} - -// This test is here so that the fuzzer can be maintained -#[test] -fn test_consensus_proposal_fuzzer() { - // generate a proposal - let proposal = generate_corpus_proposal(); - // successfully parse it - fuzz_proposal(&proposal); -} diff --git a/consensus/src/round_manager_test.rs b/consensus/src/round_manager_test.rs deleted file mode 100644 index e29102378dd8f..0000000000000 --- a/consensus/src/round_manager_test.rs +++ /dev/null @@ -1,2320 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_storage::{BlockReader, BlockStore}, - liveness::{ - proposal_generator::{ - ChainHealthBackoffConfig, PipelineBackpressureConfig, ProposalGenerator, - }, - proposer_election::ProposerElection, - rotating_proposer_election::RotatingProposer, - round_state::{ExponentialTimeInterval, RoundState}, - }, - metrics_safety_rules::MetricsSafetyRules, - network::{IncomingBlockRetrievalRequest, NetworkSender}, - network_interface::{CommitMessage, ConsensusMsg, ConsensusNetworkClient, DIRECT_SEND, RPC}, - network_tests::{NetworkPlayground, TwinId}, - payload_manager::PayloadManager, - persistent_liveness_storage::RecoveryData, - pipeline::buffer_manager::OrderedBlocks, - round_manager::RoundManager, - test_utils::{ - consensus_runtime, create_vec_signed_transactions, - mock_execution_client::MockExecutionClient, timed_block_on, MockPayloadManager, - MockStorage, TreeInserter, - }, - util::time_service::{ClockTimeService, TimeService}, -}; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::{ - config::{ConsensusConfig, QcAggregatorType}, - network_id::{NetworkId, PeerNetworkId}, -}; -use aptos_consensus_types::{ - block::{ - block_test_utils::{certificate_for_genesis, gen_test_certificate}, - Block, - }, - block_retrieval::{BlockRetrievalRequest, BlockRetrievalStatus}, - common::{Author, Payload, Round}, - pipeline::commit_decision::CommitDecision, - proposal_msg::ProposalMsg, - sync_info::SyncInfo, - timeout_2chain::{TwoChainTimeout, TwoChainTimeoutWithPartialSignatures}, - vote_msg::VoteMsg, -}; -use aptos_crypto::HashValue; -use aptos_infallible::Mutex; -use aptos_logger::prelude::info; -use aptos_network::{ - application::interface::NetworkClient, - peer_manager::{conn_notifs_channel, ConnectionRequestSender, PeerManagerRequestSender}, - protocols::{ - network, - network::{Event, NetworkEvents, NewNetworkEvents, NewNetworkSender}, - wire::handshake::v1::ProtocolIdSet, - }, - transport::ConnectionMetadata, - ProtocolId, -}; -use aptos_safety_rules::{PersistentSafetyStorage, SafetyRulesManager}; -use aptos_secure_storage::Storage; -use aptos_types::{ - epoch_state::EpochState, - jwks::QuorumCertifiedUpdate, - ledger_info::LedgerInfo, - on_chain_config::{ - ConsensusAlgorithmConfig, ConsensusConfigV1, OnChainConsensusConfig, - OnChainJWKConsensusConfig, OnChainRandomnessConfig, ValidatorTxnConfig, - }, - transaction::SignedTransaction, - validator_signer::ValidatorSigner, - validator_txn::ValidatorTransaction, - validator_verifier::{generate_validator_verifier, random_validator_verifier}, - waypoint::Waypoint, -}; -use futures::{ - channel::{mpsc, oneshot}, - executor::block_on, - stream::select, - FutureExt, Stream, StreamExt, -}; -use futures_channel::mpsc::unbounded; -use maplit::hashmap; -use std::{ - iter::FromIterator, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::Duration, -}; -use tokio::{ - runtime::{Handle, Runtime}, - task::JoinHandle, - time::timeout, -}; - -/// Auxiliary struct that is setting up node environment for the test. -pub struct NodeSetup { - block_store: Arc, - round_manager: RoundManager, - storage: Arc, - signer: ValidatorSigner, - proposers: Vec, - safety_rules_manager: SafetyRulesManager, - pending_network_events: Vec>, - all_network_events: Box> + Send + Unpin>, - ordered_blocks_events: mpsc::UnboundedReceiver, - mock_execution_client: Arc, - _state_sync_receiver: mpsc::UnboundedReceiver>, - id: usize, - onchain_consensus_config: OnChainConsensusConfig, - local_consensus_config: ConsensusConfig, - onchain_randomness_config: OnChainRandomnessConfig, - onchain_jwk_consensus_config: OnChainJWKConsensusConfig, -} - -impl NodeSetup { - fn create_round_state(time_service: Arc) -> RoundState { - let base_timeout = Duration::new(60, 0); - let time_interval = Box::new(ExponentialTimeInterval::fixed(base_timeout)); - let (round_timeout_sender, _) = aptos_channels::new_test(1_024); - let (delayed_qc_tx, _) = unbounded(); - RoundState::new( - time_interval, - time_service, - round_timeout_sender, - delayed_qc_tx, - QcAggregatorType::NoDelay, - ) - } - - fn create_proposer_election(proposers: Vec) -> Arc { - Arc::new(RotatingProposer::new(proposers, 1)) - } - - fn create_nodes( - playground: &mut NetworkPlayground, - executor: Handle, - num_nodes: usize, - proposer_indices: Option>, - onchain_consensus_config: Option, - local_consensus_config: Option, - onchain_randomness_config: Option, - onchain_jwk_consensus_config: Option, - ) -> Vec { - let onchain_consensus_config = onchain_consensus_config.unwrap_or_default(); - let onchain_randomness_config = - onchain_randomness_config.unwrap_or_else(OnChainRandomnessConfig::default_if_missing); - let onchain_jwk_consensus_config = onchain_jwk_consensus_config - .unwrap_or_else(OnChainJWKConsensusConfig::default_if_missing); - let local_consensus_config = local_consensus_config.unwrap_or_default(); - let (signers, validators) = random_validator_verifier(num_nodes, None, false); - let proposers = proposer_indices - .unwrap_or_else(|| vec![0]) - .iter() - .map(|i| signers[*i].author()) - .collect::>(); - let validator_set = (&validators).into(); - let waypoint = - Waypoint::new_epoch_boundary(&LedgerInfo::mock_genesis(Some(validator_set))).unwrap(); - - let mut nodes = vec![]; - // pre-initialize the mapping to avoid race conditions (peer try to broadcast to someone not added yet) - let peers_and_metadata = playground.peer_protocols(); - for signer in signers.iter().take(num_nodes) { - let peer_id = signer.author(); - let mut conn_meta = ConnectionMetadata::mock(peer_id); - conn_meta.application_protocols = ProtocolIdSet::from_iter([ - ProtocolId::ConsensusDirectSendJson, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusRpcBcs, - ]); - let peer_network_id = PeerNetworkId::new(NetworkId::Validator, peer_id); - peers_and_metadata - .insert_connection_metadata(peer_network_id, conn_meta) - .unwrap(); - } - for (id, signer) in signers.iter().take(num_nodes).enumerate() { - let (initial_data, storage) = MockStorage::start_for_testing((&validators).into()); - - let safety_storage = PersistentSafetyStorage::initialize( - Storage::from(aptos_secure_storage::InMemoryStorage::new()), - signer.author(), - signer.private_key().clone(), - waypoint, - true, - ); - let safety_rules_manager = SafetyRulesManager::new_local(safety_storage); - - nodes.push(Self::new( - playground, - executor.clone(), - signer.to_owned(), - proposers.clone(), - storage, - initial_data, - safety_rules_manager, - id, - onchain_consensus_config.clone(), - local_consensus_config.clone(), - onchain_randomness_config.clone(), - onchain_jwk_consensus_config.clone(), - )); - } - nodes - } - - fn new( - playground: &mut NetworkPlayground, - executor: Handle, - signer: ValidatorSigner, - proposers: Vec, - storage: Arc, - initial_data: RecoveryData, - safety_rules_manager: SafetyRulesManager, - id: usize, - onchain_consensus_config: OnChainConsensusConfig, - local_consensus_config: ConsensusConfig, - onchain_randomness_config: OnChainRandomnessConfig, - onchain_jwk_consensus_config: OnChainJWKConsensusConfig, - ) -> Self { - let _entered_runtime = executor.enter(); - let epoch_state = Arc::new(EpochState { - epoch: 1, - verifier: storage.get_validator_set().into(), - }); - let validators = epoch_state.verifier.clone(); - let (network_reqs_tx, network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (consensus_tx, consensus_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = aptos_channels::new_test(8); - let (_, conn_status_rx) = conn_notifs_channel::new(); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - playground.peer_protocols(), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - let network_events = NetworkEvents::new(consensus_rx, conn_status_rx, None); - let author = signer.author(); - - let twin_id = TwinId { id, author }; - - playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx); - - let (self_sender, self_receiver) = aptos_channels::new_unbounded_test(); - let network = Arc::new(NetworkSender::new( - author, - consensus_network_client, - self_sender, - validators, - )); - - let all_network_events = Box::new(select(network_events, self_receiver)); - - let last_vote_sent = initial_data.last_vote(); - let (ordered_blocks_tx, ordered_blocks_events) = mpsc::unbounded::(); - let (state_sync_client, _state_sync_receiver) = mpsc::unbounded(); - let mock_execution_client = Arc::new(MockExecutionClient::new( - state_sync_client.clone(), - ordered_blocks_tx.clone(), - Arc::clone(&storage), - )); - let time_service = Arc::new(ClockTimeService::new(executor)); - - let block_store = Arc::new(BlockStore::new( - storage.clone(), - initial_data, - mock_execution_client.clone(), - 10, // max pruned blocks in mem - time_service.clone(), - 10, - Arc::from(PayloadManager::DirectMempool), - )); - - let proposer_election = Self::create_proposer_election(proposers.clone()); - let proposal_generator = ProposalGenerator::new( - author, - block_store.clone(), - Arc::new(MockPayloadManager::new(None)), - time_service.clone(), - Duration::ZERO, - 10, - 1000, - 5, - 500, - 10, - PipelineBackpressureConfig::new_no_backoff(), - ChainHealthBackoffConfig::new_no_backoff(), - false, - onchain_consensus_config.effective_validator_txn_config(), - true, - ); - - let round_state = Self::create_round_state(time_service); - let mut safety_rules = - MetricsSafetyRules::new(safety_rules_manager.client(), storage.clone()); - safety_rules.perform_initialize().unwrap(); - - let (round_manager_tx, _) = aptos_channel::new(QueueStyle::LIFO, 1, None); - - let mut local_config = local_consensus_config.clone(); - local_config.enable_broadcast_vote(false); - - let mut round_manager = RoundManager::new( - epoch_state, - Arc::clone(&block_store), - round_state, - proposer_election, - proposal_generator, - Arc::new(Mutex::new(safety_rules)), - network, - storage.clone(), - onchain_consensus_config.clone(), - round_manager_tx, - local_config, - onchain_randomness_config.clone(), - onchain_jwk_consensus_config.clone(), - ); - block_on(round_manager.init(last_vote_sent)); - Self { - block_store, - round_manager, - storage, - signer, - proposers, - safety_rules_manager, - pending_network_events: Vec::new(), - all_network_events, - ordered_blocks_events, - mock_execution_client, - _state_sync_receiver, - id, - onchain_consensus_config, - local_consensus_config, - onchain_randomness_config, - onchain_jwk_consensus_config, - } - } - - pub fn restart(self, playground: &mut NetworkPlayground, executor: Handle) -> Self { - let recover_data = self - .storage - .try_start() - .unwrap_or_else(|e| panic!("fail to restart due to: {}", e)); - Self::new( - playground, - executor, - self.signer, - self.proposers, - self.storage, - recover_data, - self.safety_rules_manager, - self.id, - self.onchain_consensus_config.clone(), - self.local_consensus_config.clone(), - self.onchain_randomness_config.clone(), - self.onchain_jwk_consensus_config.clone(), - ) - } - - pub fn identity_desc(&self) -> String { - format!("{} [{}]", self.id, self.signer.author()) - } - - fn poll_next_network_event(&mut self) -> Option> { - if !self.pending_network_events.is_empty() { - Some(self.pending_network_events.remove(0)) - } else { - self.all_network_events - .next() - .now_or_never() - .map(|v| v.unwrap()) - } - } - - pub async fn next_network_event(&mut self) -> Event { - if !self.pending_network_events.is_empty() { - self.pending_network_events.remove(0) - } else { - self.all_network_events.next().await.unwrap() - } - } - - pub async fn next_network_message(&mut self) -> ConsensusMsg { - match self.next_network_event().await { - Event::Message(_, msg) => msg, - Event::RpcRequest(_, msg, _, _) if matches!(msg, ConsensusMsg::CommitMessage(_)) => msg, - Event::RpcRequest(_, msg, _, _) => { - panic!( - "Unexpected event, got RpcRequest, expected Message: {:?} on node {}", - msg, - self.identity_desc() - ) - }, - _ => panic!("Unexpected Network Event"), - } - } - - pub fn no_next_msg(&mut self) { - match self.poll_next_network_event() { - Some(Event::RpcRequest(_, msg, _, _)) | Some(Event::Message(_, msg)) => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - Some(_) => panic!("Unexpected Network Event"), - None => {}, - } - } - - pub async fn next_proposal(&mut self) -> ProposalMsg { - match self.next_network_message().await { - ConsensusMsg::ProposalMsg(p) => *p, - msg => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - } - } - - pub async fn next_vote(&mut self) -> VoteMsg { - match self.next_network_message().await { - ConsensusMsg::VoteMsg(v) => *v, - msg => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - } - } - - pub async fn next_commit_decision(&mut self) -> CommitDecision { - match self.next_network_message().await { - ConsensusMsg::CommitDecisionMsg(v) => *v, - ConsensusMsg::CommitMessage(d) if matches!(*d, CommitMessage::Decision(_)) => { - match *d { - CommitMessage::Decision(d) => d, - _ => unreachable!(), - } - }, - msg => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - } - } - - pub async fn poll_block_retreival(&mut self) -> Option { - match self.poll_next_network_event() { - Some(Event::RpcRequest(_, msg, protocol, response_sender)) => match msg { - ConsensusMsg::BlockRetrievalRequest(v) => Some(IncomingBlockRetrievalRequest { - req: *v, - protocol, - response_sender, - }), - msg => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - }, - Some(Event::Message(_, msg)) => panic!( - "Unexpected Consensus Message: {:?} on node {}", - msg, - self.identity_desc() - ), - Some(_) => panic!("Unexpected Network Event"), - None => None, - } - } - - pub fn no_next_ordered(&mut self) { - if self.ordered_blocks_events.next().now_or_never().is_some() { - panic!("Unexpected Ordered Blocks Event"); - } - } - - pub async fn commit_next_ordered(&mut self, expected_rounds: &[Round]) { - info!( - "Starting commit_next_ordered to wait for {:?} on node {:?}", - expected_rounds, - self.identity_desc() - ); - let ordered_blocks = self.ordered_blocks_events.next().await.unwrap(); - let rounds = ordered_blocks - .ordered_blocks - .iter() - .map(|b| b.round()) - .collect::>(); - assert_eq!(&rounds, expected_rounds); - self.mock_execution_client - .commit_to_storage(ordered_blocks) - .await - .unwrap(); - } -} - -fn start_replying_to_block_retreival(nodes: Vec) -> ReplyingRPCHandle { - let done = Arc::new(AtomicBool::new(false)); - let mut handles = Vec::new(); - for mut node in nodes.into_iter() { - let done_clone = done.clone(); - handles.push(tokio::spawn(async move { - while !done_clone.load(Ordering::Relaxed) { - info!("Asking for RPC request on {:?}", node.identity_desc()); - let maybe_request = node.poll_block_retreival().await; - if let Some(request) = maybe_request { - info!( - "RPC request received: {:?} on {:?}", - request, - node.identity_desc() - ); - node.block_store - .process_block_retrieval(request) - .await - .unwrap(); - } else { - tokio::time::sleep(Duration::from_millis(50)).await; - } - } - node - })); - } - ReplyingRPCHandle { handles, done } -} - -struct ReplyingRPCHandle { - handles: Vec>, - done: Arc, -} - -impl ReplyingRPCHandle { - async fn join(self) -> Vec { - self.done.store(true, Ordering::Relaxed); - let mut result = Vec::new(); - for handle in self.handles.into_iter() { - result.push(handle.await.unwrap()); - } - info!( - "joined nodes in order: {:?}", - result.iter().map(|v| v.id).collect::>() - ); - result - } -} - -fn process_and_vote_on_proposal( - runtime: &Runtime, - nodes: &mut [NodeSetup], - next_proposer: usize, - down_nodes: &[usize], - process_votes: bool, - apply_commit_prev_proposer: Option, - apply_commit_on_votes: bool, - expected_round: u64, - expected_qc_ordered_round: u64, - expected_qc_committed_round: u64, -) { - info!( - "Called {} with current {} and apply commit prev {:?}", - expected_round, next_proposer, apply_commit_prev_proposer - ); - let mut num_votes = 0; - - for node in nodes.iter_mut() { - info!("Waiting on next_proposal on node {}", node.identity_desc()); - if down_nodes.contains(&node.id) { - // Drop the proposal on down nodes - timed_block_on(runtime, node.next_proposal()); - info!("Dropping proposal on down node {}", node.identity_desc()); - } else { - // Proccess proposal on other nodes - let proposal_msg = timed_block_on(runtime, node.next_proposal()); - info!("Processing proposal on {}", node.identity_desc()); - - assert_eq!(proposal_msg.proposal().round(), expected_round); - assert_eq!( - proposal_msg.sync_info().highest_ordered_round(), - expected_qc_ordered_round - ); - assert_eq!( - proposal_msg.sync_info().highest_commit_round(), - expected_qc_committed_round - ); - - timed_block_on( - runtime, - node.round_manager.process_proposal_msg(proposal_msg), - ) - .unwrap(); - info!("Finish process proposal on {}", node.identity_desc()); - num_votes += 1; - - if let Some(prev_proposer) = apply_commit_prev_proposer { - if prev_proposer != node.id && expected_round > 2 { - info!( - "Applying commit {} on node {}", - expected_round - 2, - node.identity_desc() - ); - timed_block_on(runtime, node.commit_next_ordered(&[expected_round - 2])); - } - } - } - } - - let proposer_node = nodes.get_mut(next_proposer).unwrap(); - info!( - "Fetching {} votes in round {} on node {}", - num_votes, - expected_round, - proposer_node.identity_desc() - ); - let mut votes = Vec::new(); - for _ in 0..num_votes { - votes.push(timed_block_on(runtime, proposer_node.next_vote())); - } - - info!("Processing votes on node {}", proposer_node.identity_desc()); - if process_votes { - for vote_msg in votes { - timed_block_on( - runtime, - proposer_node.round_manager.process_vote_msg(vote_msg), - ) - .unwrap(); - } - if apply_commit_prev_proposer.is_some() && expected_round > 1 && apply_commit_on_votes { - info!( - "Applying next commit {} on proposer node {}", - expected_round - 2, - proposer_node.identity_desc() - ); - timed_block_on( - runtime, - proposer_node.commit_next_ordered(&[expected_round - 1]), - ); - } - } -} - -#[test] -fn new_round_on_quorum_cert() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - let genesis = node.block_store.ordered_root(); - timed_block_on(&runtime, async { - // round 1 should start - let proposal_msg = node.next_proposal().await; - assert_eq!( - proposal_msg.proposal().quorum_cert().certified_block().id(), - genesis.id() - ); - let b1_id = proposal_msg.proposal().id(); - assert_eq!(proposal_msg.proposer(), node.signer.author()); - - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - let vote_msg = node.next_vote().await; - // Adding vote to form a QC - node.round_manager.process_vote_msg(vote_msg).await.unwrap(); - - // round 2 should start - let proposal_msg = node.next_proposal().await; - let proposal = proposal_msg.proposal(); - assert_eq!(proposal.round(), 2); - assert_eq!(proposal.parent_id(), b1_id); - assert_eq!(proposal.quorum_cert().certified_block().id(), b1_id); - }); -} - -#[test] -/// If the proposal is valid, a vote should be sent -fn vote_on_successful_proposal() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - - let genesis_qc = certificate_for_genesis(); - timed_block_on(&runtime, async { - // Start round 1 and clear the message queue - node.next_proposal().await; - - let proposal = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let proposal_id = proposal.id(); - node.round_manager.process_proposal(proposal).await.unwrap(); - let vote_msg = node.next_vote().await; - assert_eq!(vote_msg.vote().author(), node.signer.author()); - assert_eq!(vote_msg.vote().vote_data().proposed().id(), proposal_id); - let consensus_state = node.round_manager.consensus_state(); - assert_eq!(consensus_state.epoch(), 1); - assert_eq!(consensus_state.last_voted_round(), 1); - assert_eq!(consensus_state.preferred_round(), 0); - assert!(consensus_state.in_validator_set()); - }); -} - -#[test] -/// In back pressure mode, verify that the proposals are processed after we get out of back pressure. -fn delay_proposal_processing_in_sync_only() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - - let genesis_qc = certificate_for_genesis(); - timed_block_on(&runtime, async { - // Start round 1 and clear the message queue - node.next_proposal().await; - - // Set sync only to true so that new proposal processing is delayed. - node.round_manager - .block_store - .set_back_pressure_for_test(true); - let proposal = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let proposal_id = proposal.id(); - node.round_manager - .process_proposal(proposal.clone()) - .await - .unwrap(); - - // Wait for some time to ensure that the proposal was not processed - timeout(Duration::from_millis(200), node.next_vote()) - .await - .unwrap_err(); - - // Clear the sync only mode and process verified proposal and ensure it is processed now - node.round_manager - .block_store - .set_back_pressure_for_test(false); - - node.round_manager - .process_verified_proposal(proposal) - .await - .unwrap(); - - let vote_msg = node.next_vote().await; - assert_eq!(vote_msg.vote().author(), node.signer.author()); - assert_eq!(vote_msg.vote().vote_data().proposed().id(), proposal_id); - let consensus_state = node.round_manager.consensus_state(); - assert_eq!(consensus_state.epoch(), 1); - assert_eq!(consensus_state.last_voted_round(), 1); - assert_eq!(consensus_state.preferred_round(), 0); - assert!(consensus_state.in_validator_set()); - }); -} - -#[test] -/// If the proposal does not pass voting rules, -/// No votes are sent, but the block is still added to the block tree. -fn no_vote_on_old_proposal() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - let genesis_qc = certificate_for_genesis(); - let new_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let new_block_id = new_block.id(); - let old_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 2, - genesis_qc, - &node.signer, - Vec::new(), - ) - .unwrap(); - timed_block_on(&runtime, async { - // clear the message queue - node.next_proposal().await; - - node.round_manager - .process_proposal(new_block) - .await - .unwrap(); - node.round_manager - .process_proposal(old_block) - .await - .unwrap_err(); - let vote_msg = node.next_vote().await; - assert_eq!(vote_msg.vote().vote_data().proposed().id(), new_block_id); - }); -} - -#[test] -/// We don't vote for proposals that 'skips' rounds -/// After that when we then receive proposal for correct round, we vote for it -/// Basically it checks that adversary can not send proposal and skip rounds violating round_state -/// rules -fn no_vote_on_mismatch_round() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut node = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ) - .pop() - .unwrap(); - let genesis_qc = certificate_for_genesis(); - let correct_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let block_skip_round = Block::new_proposal( - Payload::empty(false, true), - 2, - 2, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - timed_block_on(&runtime, async { - let bad_proposal = ProposalMsg::new( - block_skip_round, - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), None), - ); - assert!(node - .round_manager - .process_proposal_msg(bad_proposal) - .await - .is_err()); - let good_proposal = ProposalMsg::new( - correct_block.clone(), - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), None), - ); - node.round_manager - .process_proposal_msg(good_proposal) - .await - .unwrap(); - }); -} - -#[test] -/// Ensure that after the vote messages are broadcasted upon timeout, the receivers -/// have the highest quorum certificate (carried by the SyncInfo of the vote message) -fn sync_info_carried_on_timeout_vote() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let mut node = nodes.pop().unwrap(); - - timed_block_on(&runtime, async { - let proposal_msg = node.next_proposal().await; - let block_0 = proposal_msg.proposal().clone(); - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - node.next_vote().await; - let parent_block_info = block_0.quorum_cert().certified_block(); - // Populate block_0 and a quorum certificate for block_0 on non_proposer - let block_0_quorum_cert = gen_test_certificate( - &[node.signer.clone()], - // Follow MockStateComputer implementation - block_0.gen_block_info( - parent_block_info.executed_state_id(), - parent_block_info.version(), - parent_block_info.next_epoch_state().cloned(), - ), - parent_block_info.clone(), - None, - ); - node.block_store - .insert_single_quorum_cert(block_0_quorum_cert.clone()) - .unwrap(); - - node.round_manager - .round_state - .process_certificates(SyncInfo::new( - block_0_quorum_cert.clone(), - block_0_quorum_cert.clone(), - None, - )); - node.round_manager - .process_local_timeout(2) - .await - .unwrap_err(); - let vote_msg_on_timeout = node.next_vote().await; - assert!(vote_msg_on_timeout.vote().is_timeout()); - assert_eq!( - *vote_msg_on_timeout.sync_info().highest_quorum_cert(), - block_0_quorum_cert - ); - }); -} - -#[test] -/// We don't vote for proposals that comes from proposers that are not valid proposers for round -fn no_vote_on_invalid_proposer() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 2, - None, - None, - None, - None, - None, - ); - let incorrect_proposer = nodes.pop().unwrap(); - let mut node = nodes.pop().unwrap(); - let genesis_qc = certificate_for_genesis(); - let correct_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let block_incorrect_proposer = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &incorrect_proposer.signer, - Vec::new(), - ) - .unwrap(); - timed_block_on(&runtime, async { - let bad_proposal = ProposalMsg::new( - block_incorrect_proposer, - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), None), - ); - assert!(node - .round_manager - .process_proposal_msg(bad_proposal) - .await - .is_err()); - let good_proposal = ProposalMsg::new( - correct_block.clone(), - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), None), - ); - - node.round_manager - .process_proposal_msg(good_proposal.clone()) - .await - .unwrap(); - }); -} - -#[test] -/// We allow to 'skip' round if proposal carries timeout certificate for next round -fn new_round_on_timeout_certificate() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut node = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ) - .pop() - .unwrap(); - let genesis_qc = certificate_for_genesis(); - let correct_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let block_skip_round = Block::new_proposal( - Payload::empty(false, true), - 2, - 2, - genesis_qc.clone(), - &node.signer, - vec![(1, node.signer.author())], - ) - .unwrap(); - let timeout = TwoChainTimeout::new(1, 1, genesis_qc.clone()); - let timeout_signature = timeout.sign(&node.signer).unwrap(); - - let mut tc_partial = TwoChainTimeoutWithPartialSignatures::new(timeout.clone()); - tc_partial.add(node.signer.author(), timeout, timeout_signature); - - let tc = tc_partial - .aggregate_signatures(&generate_validator_verifier(&[node.signer.clone()])) - .unwrap(); - timed_block_on(&runtime, async { - let skip_round_proposal = ProposalMsg::new( - block_skip_round, - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), Some(tc)), - ); - node.round_manager - .process_proposal_msg(skip_round_proposal) - .await - .unwrap(); - let old_good_proposal = ProposalMsg::new( - correct_block.clone(), - SyncInfo::new(genesis_qc.clone(), genesis_qc.clone(), None), - ); - assert!(node - .round_manager - .process_proposal_msg(old_good_proposal) - .await - .is_err()); - }); -} - -#[test] -/// We allow to 'skip' round if proposal carries timeout certificate for next round -fn reject_invalid_failed_authors() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut node = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ) - .pop() - .unwrap(); - let genesis_qc = certificate_for_genesis(); - - let create_timeout = |round: Round| { - let timeout = TwoChainTimeout::new(1, round, genesis_qc.clone()); - let timeout_signature = timeout.sign(&node.signer).unwrap(); - let mut tc_partial = TwoChainTimeoutWithPartialSignatures::new(timeout.clone()); - tc_partial.add(node.signer.author(), timeout, timeout_signature); - - tc_partial - .aggregate_signatures(&generate_validator_verifier(&[node.signer.clone()])) - .unwrap() - }; - - let create_proposal = |round: Round, failed_authors: Vec<(Round, Author)>| { - let block = Block::new_proposal( - Payload::empty(false, true), - round, - 2, - genesis_qc.clone(), - &node.signer, - failed_authors, - ) - .unwrap(); - ProposalMsg::new( - block, - SyncInfo::new( - genesis_qc.clone(), - genesis_qc.clone(), - if round > 1 { - Some(create_timeout(round - 1)) - } else { - None - }, - ), - ) - }; - - let extra_failed_authors_proposal = create_proposal(2, vec![(1, Author::random())]); - let missing_failed_authors_proposal = create_proposal(2, vec![]); - let wrong_failed_authors_proposal = create_proposal(2, vec![(1, Author::random())]); - let not_enough_failed_proposal = create_proposal(3, vec![(2, node.signer.author())]); - let valid_proposal = create_proposal( - 4, - (1..4).map(|i| (i as Round, node.signer.author())).collect(), - ); - - timed_block_on(&runtime, async { - assert!(node - .round_manager - .process_proposal_msg(extra_failed_authors_proposal) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal_msg(missing_failed_authors_proposal) - .await - .is_err()); - }); - - timed_block_on(&runtime, async { - assert!(node - .round_manager - .process_proposal_msg(wrong_failed_authors_proposal) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal_msg(not_enough_failed_proposal) - .await - .is_err()); - - node.round_manager - .process_proposal_msg(valid_proposal) - .await - .unwrap() - }); -} - -#[test] -fn response_on_block_retrieval() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut node = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ) - .pop() - .unwrap(); - - let genesis_qc = certificate_for_genesis(); - let block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - let block_id = block.id(); - let proposal = ProposalMsg::new(block, SyncInfo::new(genesis_qc.clone(), genesis_qc, None)); - - timed_block_on(&runtime, async { - node.round_manager - .process_proposal_msg(proposal) - .await - .unwrap(); - - // first verify that we can retrieve the block if it's in the tree - let (tx1, rx1) = oneshot::channel(); - let single_block_request = IncomingBlockRetrievalRequest { - req: BlockRetrievalRequest::new(block_id, 1), - protocol: ProtocolId::ConsensusRpcBcs, - response_sender: tx1, - }; - node.block_store - .process_block_retrieval(single_block_request) - .await - .unwrap(); - match rx1.await { - Ok(Ok(bytes)) => { - let response = match bcs::from_bytes(&bytes) { - Ok(ConsensusMsg::BlockRetrievalResponse(resp)) => *resp, - _ => panic!("block retrieval failure"), - }; - assert_eq!(response.status(), BlockRetrievalStatus::Succeeded); - assert_eq!(response.blocks().first().unwrap().id(), block_id); - }, - _ => panic!("block retrieval failure"), - } - - // verify that if a block is not there, return ID_NOT_FOUND - let (tx2, rx2) = oneshot::channel(); - let missing_block_request = IncomingBlockRetrievalRequest { - req: BlockRetrievalRequest::new(HashValue::random(), 1), - protocol: ProtocolId::ConsensusRpcBcs, - response_sender: tx2, - }; - - node.block_store - .process_block_retrieval(missing_block_request) - .await - .unwrap(); - match rx2.await { - Ok(Ok(bytes)) => { - let response = match bcs::from_bytes(&bytes) { - Ok(ConsensusMsg::BlockRetrievalResponse(resp)) => *resp, - _ => panic!("block retrieval failure"), - }; - assert_eq!(response.status(), BlockRetrievalStatus::IdNotFound); - assert!(response.blocks().is_empty()); - }, - _ => panic!("block retrieval failure"), - } - - // if asked for many blocks, return NOT_ENOUGH_BLOCKS - let (tx3, rx3) = oneshot::channel(); - let many_block_request = IncomingBlockRetrievalRequest { - req: BlockRetrievalRequest::new(block_id, 3), - protocol: ProtocolId::ConsensusRpcBcs, - response_sender: tx3, - }; - node.block_store - .process_block_retrieval(many_block_request) - .await - .unwrap(); - match rx3.await { - Ok(Ok(bytes)) => { - let response = match bcs::from_bytes(&bytes) { - Ok(ConsensusMsg::BlockRetrievalResponse(resp)) => *resp, - _ => panic!("block retrieval failure"), - }; - assert_eq!(response.status(), BlockRetrievalStatus::NotEnoughBlocks); - assert_eq!(block_id, response.blocks().first().unwrap().id()); - assert_eq!( - node.block_store.ordered_root().id(), - response.blocks().get(1).unwrap().id() - ); - }, - _ => panic!("block retrieval failure"), - } - }); -} - -#[test] -/// rebuild a node from previous storage without violating safety guarantees. -fn recover_on_restart() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut node = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ) - .pop() - .unwrap(); - let inserter = TreeInserter::new_with_store(node.signer.clone(), node.block_store.clone()); - - let genesis_qc = certificate_for_genesis(); - let mut data = Vec::new(); - let num_proposals = 100; - // insert a few successful proposals - for i in 1..=num_proposals { - let proposal = inserter.create_block_with_qc( - genesis_qc.clone(), - i, - i, - Payload::empty(false, true), - (std::cmp::max(1, i.saturating_sub(10))..i) - .map(|i| (i, inserter.signer().author())) - .collect(), - ); - let timeout = TwoChainTimeout::new(1, i - 1, genesis_qc.clone()); - let mut tc_partial = TwoChainTimeoutWithPartialSignatures::new(timeout.clone()); - tc_partial.add( - inserter.signer().author(), - timeout.clone(), - timeout.sign(inserter.signer()).unwrap(), - ); - - let tc = tc_partial - .aggregate_signatures(&generate_validator_verifier(&[node.signer.clone()])) - .unwrap(); - - data.push((proposal, tc)); - } - - timed_block_on(&runtime, async { - for (proposal, tc) in &data { - let proposal_msg = ProposalMsg::new( - proposal.clone(), - SyncInfo::new( - proposal.quorum_cert().clone(), - genesis_qc.clone(), - Some(tc.clone()), - ), - ); - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - } - }); - - // verify after restart we recover the data - node = node.restart(&mut playground, runtime.handle().clone()); - let consensus_state = node.round_manager.consensus_state(); - assert_eq!(consensus_state.epoch(), 1); - assert_eq!(consensus_state.last_voted_round(), num_proposals); - assert_eq!(consensus_state.preferred_round(), 0); - assert!(consensus_state.in_validator_set()); - for (block, _) in data { - assert!(node.block_store.block_exists(block.id())); - } -} - -#[test] -/// Generate a NIL vote extending HQC upon timeout if no votes have been sent in the round. -fn nil_vote_on_timeout() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - let genesis = node.block_store.ordered_root(); - timed_block_on(&runtime, async { - node.next_proposal().await; - // Process the outgoing vote message and verify that it contains a round signature - // and that the vote extends genesis. - node.round_manager - .process_local_timeout(1) - .await - .unwrap_err(); - let vote_msg = node.next_vote().await; - - let vote = vote_msg.vote(); - - assert!(vote.is_timeout()); - // NIL block doesn't change timestamp - assert_eq!( - vote.vote_data().proposed().timestamp_usecs(), - genesis.timestamp_usecs() - ); - assert_eq!(vote.vote_data().proposed().round(), 1); - assert_eq!( - vote.vote_data().parent().id(), - node.block_store.ordered_root().id() - ); - }); -} - -#[test] -/// If the node votes in a round, upon timeout the same vote is re-sent with a timeout signature. -fn vote_resent_on_timeout() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - timed_block_on(&runtime, async { - let proposal_msg = node.next_proposal().await; - let id = proposal_msg.proposal().id(); - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - let vote_msg = node.next_vote().await; - let vote = vote_msg.vote(); - assert!(!vote.is_timeout()); - assert_eq!(vote.vote_data().proposed().id(), id); - // Process the outgoing vote message and verify that it contains a round signature - // and that the vote is the same as above. - node.round_manager - .process_local_timeout(1) - .await - .unwrap_err(); - let timeout_vote_msg = node.next_vote().await; - let timeout_vote = timeout_vote_msg.vote(); - - assert!(timeout_vote.is_timeout()); - assert_eq!(timeout_vote.vote_data(), vote.vote_data()); - }); -} - -#[test] -#[ignore] // TODO: this test needs to be fixed! -fn sync_on_partial_newer_sync_info() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let mut node = nodes.pop().unwrap(); - runtime.spawn(playground.start()); - timed_block_on(&runtime, async { - // commit block 1 after 4 rounds - for _ in 1..=4 { - let proposal_msg = node.next_proposal().await; - - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - let vote_msg = node.next_vote().await; - // Adding vote to form a QC - node.round_manager.process_vote_msg(vote_msg).await.unwrap(); - } - let block_4 = node.next_proposal().await; - node.round_manager - .process_proposal_msg(block_4.clone()) - .await - .unwrap(); - // commit genesis and block 1 - for i in 0..2 { - node.commit_next_ordered(&[i]).await; - } - let vote_msg = node.next_vote().await; - let vote_data = vote_msg.vote().vote_data(); - let block_4_qc = gen_test_certificate( - &[node.signer.clone()], - vote_data.proposed().clone(), - vote_data.parent().clone(), - None, - ); - // Create a sync info with newer quorum cert but older commit cert - let sync_info = SyncInfo::new(block_4_qc.clone(), certificate_for_genesis(), None); - node.round_manager - .ensure_round_and_sync_up( - sync_info.highest_round() + 1, - &sync_info, - node.signer.author(), - ) - .await - .unwrap(); - // QuorumCert added - assert_eq!(*node.block_store.highest_quorum_cert(), block_4_qc); - }); -} - -#[test] -fn safety_rules_crash() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let mut node = nodes.pop().unwrap(); - runtime.spawn(playground.start()); - - fn reset_safety_rules(node: &mut NodeSetup) { - let safety_storage = PersistentSafetyStorage::initialize( - Storage::from(aptos_secure_storage::InMemoryStorage::new()), - node.signer.author(), - node.signer.private_key().clone(), - node.round_manager.consensus_state().waypoint(), - true, - ); - - node.safety_rules_manager = SafetyRulesManager::new_local(safety_storage); - let safety_rules = - MetricsSafetyRules::new(node.safety_rules_manager.client(), node.storage.clone()); - let safety_rules_container = Arc::new(Mutex::new(safety_rules)); - node.round_manager.set_safety_rules(safety_rules_container); - } - - timed_block_on(&runtime, async { - for _ in 0..2 { - let proposal_msg = node.next_proposal().await; - - reset_safety_rules(&mut node); - // construct_and_sign_vote - node.round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - - let vote_msg = node.next_vote().await; - - // sign_timeout - reset_safety_rules(&mut node); - let round = vote_msg.vote().vote_data().proposed().round(); - node.round_manager - .process_local_timeout(round) - .await - .unwrap_err(); - let vote_msg = node.next_vote().await; - - // sign proposal - reset_safety_rules(&mut node); - node.round_manager.process_vote_msg(vote_msg).await.unwrap(); - } - - // verify the last sign proposal happened - node.next_proposal().await; - }); -} - -#[test] -fn echo_timeout() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 4, - None, - None, - None, - None, - None, - ); - runtime.spawn(playground.start()); - timed_block_on(&runtime, async { - // clear the message queue - for node in &mut nodes { - node.next_proposal().await; - } - // timeout 3 nodes - for node in &mut nodes[1..] { - node.round_manager - .process_local_timeout(1) - .await - .unwrap_err(); - } - let node_0 = &mut nodes[0]; - // node 0 doesn't timeout and should echo the timeout after 2 timeout message - for i in 0..3 { - let timeout_vote = node_0.next_vote().await; - let result = node_0.round_manager.process_vote_msg(timeout_vote).await; - // first and third message should not timeout - if i == 0 || i == 2 { - assert!(result.is_ok()); - } - if i == 1 { - // timeout is an Error - assert!(result.is_err()); - } - } - - let node_1 = &mut nodes[1]; - // it receives 4 timeout messages (1 from each) and doesn't echo since it already timeout - for _ in 0..4 { - let timeout_vote = node_1.next_vote().await; - node_1 - .round_manager - .process_vote_msg(timeout_vote) - .await - .unwrap(); - } - }); -} - -#[test] -fn no_next_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 4, - None, - None, - None, - None, - None, - ); - runtime.spawn(playground.start()); - - timed_block_on(&runtime, async { - // clear the message queue - for node in &mut nodes { - node.next_proposal().await; - } - - tokio::time::sleep(Duration::from_secs(1)).await; - - for node in nodes.iter_mut() { - node.no_next_msg(); - } - tokio::time::sleep(Duration::from_secs(1)).await; - - for node in nodes.iter_mut() { - node.no_next_msg(); - } - }); -} - -#[test] -fn commit_pipeline_test() { - let runtime = consensus_runtime(); - let proposers = vec![0, 0, 0, 0, 5]; - - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 7, - Some(proposers.clone()), - None, - None, - None, - None, - ); - runtime.spawn(playground.start()); - let behind_node = 6; - for i in 0..10 { - let next_proposer = proposers[(i + 2) as usize % proposers.len()]; - let prev_proposer = proposers[(i + 1) as usize % proposers.len()]; - info!("processing {}", i); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - next_proposer, - &[behind_node], - true, - Some(prev_proposer), - true, - i + 1, - i.saturating_sub(1), - i.saturating_sub(2), - ); - - std::thread::sleep(Duration::from_secs(1)); - - for node in nodes.iter_mut() { - node.no_next_ordered(); - } - } -} - -#[test] -fn block_retrieval_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 4, - Some(vec![0, 1]), - None, - None, - None, - None, - ); - runtime.spawn(playground.start()); - - for i in 0..4 { - info!("processing {}", i); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - i as usize % 2, - &[3], - true, - None, - true, - i + 1, - i.saturating_sub(1), - 0, - ); - } - - timed_block_on(&runtime, async { - let mut behind_node = nodes.pop().unwrap(); - - // Drain the queue on other nodes - for node in nodes.iter_mut() { - let _ = node.next_proposal().await; - } - - info!( - "Processing proposals for behind node {}", - behind_node.identity_desc() - ); - let handle = start_replying_to_block_retreival(nodes); - let proposal_msg = behind_node.next_proposal().await; - behind_node - .round_manager - .process_proposal_msg(proposal_msg) - .await - .unwrap(); - - handle.join().await; - }); -} - -#[test] -pub fn forking_retrieval_test() { - let runtime = consensus_runtime(); - - let proposal_node = 0; - let behind_node = 6; - let forking_node = 5; - - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 7, - Some(vec![ - proposal_node, - proposal_node, - proposal_node, - proposal_node, - proposal_node, - forking_node, - proposal_node, - proposal_node, - ]), - None, - None, - None, - None, - ); - runtime.spawn(playground.start()); - - info!("Propose vote and commit on first block"); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node], - true, - Some(proposal_node), - true, - 1, - 0, - 0, - ); - - info!("Propose vote and commit on second block"); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node], - true, - Some(proposal_node), - true, - 2, - 0, - 0, - ); - - info!("Propose vote and commit on second block"); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node], - true, - Some(proposal_node), - true, - 3, - 1, - 0, - ); - - info!("Propose vote and commit on third (dangling) block"); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - forking_node, - &[behind_node, forking_node], - false, - Some(proposal_node), - true, - 4, - 2, - 1, - ); - - timed_block_on(&runtime, async { - println!("Insert local timeout to all nodes on next round"); - let mut timeout_votes = 0; - for node in nodes.iter_mut() { - if node.id != behind_node && node.id != forking_node { - node.round_manager - .process_local_timeout(4) - .await - .unwrap_err(); - timeout_votes += 1; - } - } - - println!("Process all local timeouts"); - for node in nodes.iter_mut() { - println!("Timeouts on {}", node.id); - for i in 0..timeout_votes { - println!("Timeout {} on {}", i, node.id); - if node.id == forking_node && (2..4).contains(&i) { - println!("Got {}", node.next_commit_decision().await); - } - - let vote_msg_on_timeout = node.next_vote().await; - assert!(vote_msg_on_timeout.vote().is_timeout()); - if node.id != behind_node { - let result = node - .round_manager - .process_vote_msg(vote_msg_on_timeout) - .await; - - if node.id == forking_node && i == 2 { - result.unwrap_err(); - } else { - result.unwrap(); - } - } - } - } - }); - - timed_block_on(&runtime, async { - for node in nodes.iter_mut() { - let vote_msg_on_timeout = node.next_vote().await; - assert!(vote_msg_on_timeout.vote().is_timeout()); - } - - println!("Got {}", nodes[forking_node].next_commit_decision().await); - }); - - info!("Create forked block"); - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node], - true, - Some(forking_node), - false, - 5, - 2, - 1, - ); - - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node, forking_node], - true, - None, - false, - 6, - 3, - 3, - ); - - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[behind_node, forking_node], - true, - None, - false, - 7, - 5, - 3, - ); - - let mut nodes = timed_block_on(&runtime, async { - let mut behind_node_obj = nodes.pop().unwrap(); - - // Drain the queue on other nodes - let mut proposals = Vec::new(); - for node in nodes.iter_mut() { - proposals.push(node.next_proposal().await); - } - - println!( - "Processing proposals for behind node {}", - behind_node_obj.identity_desc() - ); - let handle = start_replying_to_block_retreival(nodes); - let proposal_msg = behind_node_obj.next_proposal().await; - behind_node_obj - .round_manager - .process_proposal_msg(proposal_msg.clone()) - .await - .unwrap(); - - nodes = handle.join().await; - behind_node_obj.no_next_msg(); - - for (proposal, node) in proposals.into_iter().zip(nodes.iter_mut()) { - node.pending_network_events.push(Event::Message( - node.signer.author(), - ConsensusMsg::ProposalMsg(Box::new(proposal)), - )); - } - behind_node_obj.pending_network_events.push(Event::Message( - behind_node_obj.signer.author(), - ConsensusMsg::ProposalMsg(Box::new(proposal_msg)), - )); - - nodes.push(behind_node_obj); - nodes - }); - - // confirm behind node can participate in consensus after state sync - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[forking_node, behind_node], - true, - None, - false, - 8, - 6, - 3, - ); - - let next_message = timed_block_on(&runtime, nodes[proposal_node].next_network_message()); - match next_message { - ConsensusMsg::VoteMsg(_) => info!("Skip extra vote msg"), - ConsensusMsg::ProposalMsg(msg) => { - // put the message back in the queue. - // actual peer doesn't matter, it is ignored, so use self. - let peer = nodes[proposal_node].signer.author(); - nodes[proposal_node] - .pending_network_events - .push(Event::Message(peer, ConsensusMsg::ProposalMsg(msg))) - }, - _ => panic!("unexpected network message {:?}", next_message), - } - process_and_vote_on_proposal( - &runtime, - &mut nodes, - proposal_node, - &[forking_node], - true, - None, - false, - 9, - 7, - 3, - ); -} - -#[test] -/// If ProposalExt feature is disabled, ProposalExt should be rejected -/// No votes are sent, but the block is still added to the block tree. -fn no_vote_on_proposal_ext_when_feature_disabled() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - // In order to observe the votes we're going to check proposal processing on the non-proposer - // node (which will send the votes to the proposer). - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - None, - None, - None, - None, - ); - let node = &mut nodes[0]; - let genesis_qc = certificate_for_genesis(); - - let invalid_block = Block::new_proposal_ext( - vec![ValidatorTransaction::dummy(vec![0xFF]); 5], - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - let valid_block = Block::new_proposal( - Payload::empty(false, true), - 1, - 1, - genesis_qc, - &node.signer, - Vec::new(), - ) - .unwrap(); - - timed_block_on(&runtime, async { - // clear the message queue - node.next_proposal().await; - - assert!(node - .round_manager - .process_proposal(invalid_block) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal(valid_block) - .await - .is_ok()); - }); -} - -#[test] -fn no_vote_on_proposal_with_unexpected_vtxns() { - let vtxns = vec![ValidatorTransaction::ObservedJWKUpdate( - QuorumCertifiedUpdate::dummy(), - )]; - - assert_process_proposal_result( - None, - Some(OnChainJWKConsensusConfig::default_disabled()), - vtxns.clone(), - false, - ); - - assert_process_proposal_result( - None, - Some(OnChainJWKConsensusConfig::default_enabled()), - vtxns, - true, - ); -} - -/// Setup a node with default configs and an optional `Features` override. -/// Create a block, fill it with the given vtxns, and process it with the `RoundManager` from the setup. -/// Assert the processing result. -fn assert_process_proposal_result( - randomness_config: Option, - jwk_consensus_config: Option, - vtxns: Vec, - expected_result: bool, -) { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - Some(OnChainConsensusConfig::default_for_genesis()), - None, - randomness_config, - jwk_consensus_config, - ); - - let node = &mut nodes[0]; - let genesis_qc = certificate_for_genesis(); - let block = Block::new_proposal_ext( - vtxns, - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - timed_block_on(&runtime, async { - // clear the message queue - node.next_proposal().await; - - assert_eq!( - expected_result, - node.round_manager - .process_proposal(block.clone()) - .await - .is_ok() - ); - }); -} - -#[test] -/// If receiving txn num/block size limit is exceeded, ProposalExt should be rejected. -fn no_vote_on_proposal_ext_when_receiving_limit_exceeded() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - - let alg_config = ConsensusAlgorithmConfig::Jolteon { - main: ConsensusConfigV1::default(), - quorum_store_enabled: true, - }; - let vtxn_config = ValidatorTxnConfig::V1 { - per_block_limit_txn_count: 5, - per_block_limit_total_bytes: 400, - }; - - let local_config = ConsensusConfig { - max_receiving_block_txns: 10, - max_receiving_block_bytes: 800, - ..Default::default() - }; - - let randomness_config = OnChainRandomnessConfig::default_enabled(); - let mut nodes = NodeSetup::create_nodes( - &mut playground, - runtime.handle().clone(), - 1, - None, - Some(OnChainConsensusConfig::V3 { - alg: alg_config, - vtxn: vtxn_config, - }), - Some(local_config), - Some(randomness_config), - None, - ); - let node = &mut nodes[0]; - let genesis_qc = certificate_for_genesis(); - - let block_too_many_txns = Block::new_proposal_ext( - vec![], - Payload::DirectMempool(create_vec_signed_transactions(11)), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - let block_too_many_vtxns = Block::new_proposal_ext( - vec![ValidatorTransaction::dummy(vec![0xFF; 20]); 6], - Payload::DirectMempool(create_vec_signed_transactions(4)), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - let block_too_large = Block::new_proposal_ext( - vec![ValidatorTransaction::dummy(vec![0xFF; 200]); 1], // total_bytes >= 200 * 1 = 200 - Payload::DirectMempool(create_vec_signed_transactions(9)), // = total_bytes >= 69 * 9 = 621 - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - let block_vtxns_too_large = Block::new_proposal_ext( - vec![ValidatorTransaction::dummy(vec![0xFF; 200]); 5], // total_bytes >= 200 * 5 = 1000 - Payload::empty(false, true), - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - let valid_block = Block::new_proposal_ext( - vec![ValidatorTransaction::dummy(vec![0xFF; 20]); 5], // total_bytes >= 60 * 5 = 300 - Payload::DirectMempool(create_vec_signed_transactions(5)), // total_bytes >= 69 * 5 = 345 - 1, - 1, - genesis_qc.clone(), - &node.signer, - Vec::new(), - ) - .unwrap(); - - timed_block_on(&runtime, async { - // clear the message queue - node.next_proposal().await; - - assert!(node - .round_manager - .process_proposal(block_too_many_txns) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal(block_too_many_vtxns) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal(block_too_large) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal(block_vtxns_too_large) - .await - .is_err()); - - assert!(node - .round_manager - .process_proposal(valid_block) - .await - .is_ok()); - }); -} diff --git a/consensus/src/state_computer.rs b/consensus/src/state_computer.rs deleted file mode 100644 index 13f975a1ead10..0000000000000 --- a/consensus/src/state_computer.rs +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - block_preparer::BlockPreparer, - block_storage::tracing::{observe_block, BlockStage}, - counters, - error::StateSyncError, - execution_pipeline::ExecutionPipeline, - monitor, - payload_manager::PayloadManager, - state_replication::{StateComputer, StateComputerCommitCallBackType}, - transaction_deduper::TransactionDeduper, - transaction_filter::TransactionFilter, - transaction_shuffler::TransactionShuffler, - txn_notifier::TxnNotifier, -}; -use anyhow::Result; -use aptos_consensus_notifications::ConsensusNotificationSender; -use aptos_consensus_types::{block::Block, common::Round, pipelined_block::PipelinedBlock}; -use aptos_crypto::HashValue; -use aptos_executor_types::{BlockExecutorTrait, ExecutorResult, StateComputeResult}; -use aptos_infallible::RwLock; -use aptos_logger::prelude::*; -use aptos_types::{ - account_address::AccountAddress, - block_executor::config::BlockExecutorConfigFromOnchain, - contract_event::ContractEvent, - epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, - randomness::Randomness, - transaction::{SignedTransaction, Transaction}, -}; -use fail::fail_point; -use futures::{future::BoxFuture, SinkExt, StreamExt}; -use std::{boxed::Box, sync::Arc}; -use tokio::sync::Mutex as AsyncMutex; - -pub type StateComputeResultFut = BoxFuture<'static, ExecutorResult>; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct PipelineExecutionResult { - pub input_txns: Vec, - pub result: StateComputeResult, -} - -impl PipelineExecutionResult { - pub fn new(input_txns: Vec, result: StateComputeResult) -> Self { - Self { input_txns, result } - } -} - -type NotificationType = ( - Box, - Vec, - Vec, // Subscribable events, e.g. NewEpochEvent, DKGStartEvent -); - -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -struct LogicalTime { - epoch: u64, - round: Round, -} - -impl LogicalTime { - pub fn new(epoch: u64, round: Round) -> Self { - Self { epoch, round } - } -} - -#[derive(Clone)] -struct MutableState { - validators: Arc<[AccountAddress]>, - payload_manager: Arc, - transaction_shuffler: Arc, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - transaction_deduper: Arc, - is_randomness_enabled: bool, -} - -/// Basic communication with the Execution module; -/// implements StateComputer traits. -pub struct ExecutionProxy { - executor: Arc, - txn_notifier: Arc, - state_sync_notifier: Arc, - async_state_sync_notifier: aptos_channels::Sender, - write_mutex: AsyncMutex, - transaction_filter: Arc, - execution_pipeline: ExecutionPipeline, - state: RwLock>, -} - -impl ExecutionProxy { - pub fn new( - executor: Arc, - txn_notifier: Arc, - state_sync_notifier: Arc, - handle: &tokio::runtime::Handle, - txn_filter: TransactionFilter, - ) -> Self { - let (tx, mut rx) = - aptos_channels::new::(10, &counters::PENDING_STATE_SYNC_NOTIFICATION); - let notifier = state_sync_notifier.clone(); - handle.spawn(async move { - while let Some((callback, txns, subscribable_events)) = rx.next().await { - if let Err(e) = monitor!( - "notify_state_sync", - notifier.notify_new_commit(txns, subscribable_events).await - ) { - error!(error = ?e, "Failed to notify state synchronizer"); - } - - callback(); - } - }); - let execution_pipeline = ExecutionPipeline::spawn(executor.clone(), handle); - Self { - executor, - txn_notifier, - state_sync_notifier, - async_state_sync_notifier: tx, - write_mutex: AsyncMutex::new(LogicalTime::new(0, 0)), - transaction_filter: Arc::new(txn_filter), - execution_pipeline, - state: RwLock::new(None), - } - } - - fn transactions_to_commit( - &self, - executed_block: &PipelinedBlock, - validators: &[AccountAddress], - randomness_enabled: bool, - ) -> Vec { - // reconfiguration suffix don't execute - if executed_block.is_reconfiguration_suffix() { - return vec![]; - } - - let user_txns = executed_block.input_transactions().clone(); - let validator_txns = executed_block.validator_txns().cloned().unwrap_or_default(); - let metadata = if randomness_enabled { - executed_block - .block() - .new_metadata_with_randomness(validators, executed_block.randomness().cloned()) - } else { - executed_block.block().new_block_metadata(validators).into() - }; - - let input_txns = Block::combine_to_input_transactions(validator_txns, user_txns, metadata); - - // Adds StateCheckpoint/BlockEpilogue transaction if needed. - executed_block - .compute_result() - .transactions_to_commit(input_txns, executed_block.id()) - } -} - -#[async_trait::async_trait] -impl StateComputer for ExecutionProxy { - async fn schedule_compute( - &self, - // The block to be executed. - block: &Block, - // The parent block id. - parent_block_id: HashValue, - randomness: Option, - ) -> StateComputeResultFut { - let block_id = block.id(); - debug!( - block = %block, - parent_id = parent_block_id, - "Executing block", - ); - let MutableState { - validators, - payload_manager, - transaction_shuffler, - block_executor_onchain_config, - transaction_deduper, - is_randomness_enabled, - } = self - .state - .read() - .as_ref() - .cloned() - .expect("must be set within an epoch"); - - let txn_notifier = self.txn_notifier.clone(); - let transaction_generator = BlockPreparer::new( - payload_manager.clone(), - self.transaction_filter.clone(), - transaction_deduper.clone(), - transaction_shuffler.clone(), - ); - - let block_executor_onchain_config = block_executor_onchain_config.clone(); - - let timestamp = block.timestamp_usecs(); - let metadata = if is_randomness_enabled { - block.new_metadata_with_randomness(&validators, randomness) - } else { - block.new_block_metadata(&validators).into() - }; - - let fut = self - .execution_pipeline - .queue( - block.clone(), - metadata, - parent_block_id, - transaction_generator, - block_executor_onchain_config, - ) - .await; - - Box::pin(async move { - debug!( - block_id = block_id, - "Got state compute result, post processing." - ); - let pipeline_execution_result = fut.await?; - let user_txns = &pipeline_execution_result.input_txns; - let result = &pipeline_execution_result.result; - - observe_block(timestamp, BlockStage::EXECUTED); - - let compute_status = result.compute_status_for_input_txns(); - // the length of compute_status is user_txns.len() + num_vtxns + 1 due to having blockmetadata - if user_txns.len() >= compute_status.len() { - // reconfiguration suffix blocks don't have any transactions - // otherwise, this is an error - if !compute_status.is_empty() { - error!( - "Expected compute_status length and actual compute_status length mismatch! user_txns len: {}, compute_status len: {}, has_reconfiguration: {}", - user_txns.len(), - compute_status.len(), - result.has_reconfiguration(), - ); - } - } else { - let user_txn_status = &compute_status[compute_status.len() - user_txns.len()..]; - - // notify mempool about failed transaction - if let Err(e) = txn_notifier - .notify_failed_txn(user_txns, user_txn_status) - .await - { - error!( - error = ?e, "Failed to notify mempool of rejected txns", - ); - } - } - - Ok(pipeline_execution_result) - }) - } - - /// Send a successful commit. A future is fulfilled when the state is finalized. - async fn commit( - &self, - blocks: &[Arc], - finality_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - let mut latest_logical_time = self.write_mutex.lock().await; - let mut block_ids = Vec::new(); - let mut txns = Vec::new(); - let mut subscribable_txn_events = Vec::new(); - let mut payloads = Vec::new(); - let logical_time = LogicalTime::new( - finality_proof.ledger_info().epoch(), - finality_proof.ledger_info().round(), - ); - let block_timestamp = finality_proof.commit_info().timestamp_usecs(); - - let MutableState { - payload_manager, - validators, - is_randomness_enabled, - .. - } = self - .state - .read() - .as_ref() - .cloned() - .expect("must be set within an epoch"); - for block in blocks { - block_ids.push(block.id()); - - if let Some(payload) = block.block().payload() { - payloads.push(payload.clone()); - } - - txns.extend(self.transactions_to_commit(block, &validators, is_randomness_enabled)); - subscribable_txn_events.extend(block.subscribable_events()); - } - - let executor = self.executor.clone(); - let proof = finality_proof.clone(); - monitor!( - "commit_block", - tokio::task::spawn_blocking(move || { - executor - .commit_blocks_ext(block_ids, proof, false) - .expect("Failed to commit blocks"); - }) - .await - ) - .expect("spawn_blocking failed"); - - let blocks = blocks.to_vec(); - let wrapped_callback = move || { - callback(&blocks, finality_proof); - }; - self.async_state_sync_notifier - .clone() - .send((Box::new(wrapped_callback), txns, subscribable_txn_events)) - .await - .expect("Failed to send async state sync notification"); - - *latest_logical_time = logical_time; - payload_manager.notify_commit(block_timestamp, payloads); - Ok(()) - } - - /// Synchronize to a commit that not present locally. - async fn sync_to(&self, target: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - let mut latest_logical_time = self.write_mutex.lock().await; - let logical_time = - LogicalTime::new(target.ledger_info().epoch(), target.ledger_info().round()); - let block_timestamp = target.commit_info().timestamp_usecs(); - - // Before the state synchronization, we have to call finish() to free the in-memory SMT - // held by BlockExecutor to prevent memory leak. - self.executor.finish(); - - // The pipeline phase already committed beyond the target block timestamp, just return. - if *latest_logical_time >= logical_time { - warn!( - "State sync target {:?} is lower than already committed logical time {:?}", - logical_time, *latest_logical_time - ); - return Ok(()); - } - - // This is to update QuorumStore with the latest known commit in the system, - // so it can set batches expiration accordingly. - // Might be none if called in the recovery path, or between epoch stop and start. - if let Some(inner) = self.state.read().as_ref() { - inner - .payload_manager - .notify_commit(block_timestamp, Vec::new()); - } - - fail_point!("consensus::sync_to", |_| { - Err(anyhow::anyhow!("Injected error in sync_to").into()) - }); - // Here to start to do state synchronization where ChunkExecutor inside will - // process chunks and commit to Storage. However, after block execution and - // commitments, the sync state of ChunkExecutor may be not up to date so - // it is required to reset the cache of ChunkExecutor in State Sync - // when requested to sync. - let res = monitor!( - "sync_to", - self.state_sync_notifier.sync_to_target(target).await - ); - *latest_logical_time = logical_time; - - // Similarly, after the state synchronization, we have to reset the cache - // of BlockExecutor to guarantee the latest committed state is up to date. - self.executor.reset()?; - - res.map_err(|error| { - let anyhow_error: anyhow::Error = error.into(); - anyhow_error.into() - }) - } - - fn new_epoch( - &self, - epoch_state: &EpochState, - payload_manager: Arc, - transaction_shuffler: Arc, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - transaction_deduper: Arc, - randomness_enabled: bool, - ) { - *self.state.write() = Some(MutableState { - validators: epoch_state - .verifier - .get_ordered_account_addresses_iter() - .collect::>() - .into(), - payload_manager, - transaction_shuffler, - block_executor_onchain_config, - transaction_deduper, - is_randomness_enabled: randomness_enabled, - }); - } - - // Clears the epoch-specific state. Only a sync_to call is expected before calling new_epoch - // on the next epoch. - fn end_epoch(&self) { - self.state.write().take(); - } -} - -#[tokio::test] -async fn test_commit_sync_race() { - use crate::{ - error::MempoolError, transaction_deduper::create_transaction_deduper, - transaction_shuffler::create_transaction_shuffler, - }; - use aptos_config::config::transaction_filter_type::Filter; - use aptos_consensus_notifications::Error; - use aptos_executor_types::state_checkpoint_output::StateCheckpointOutput; - use aptos_infallible::Mutex; - use aptos_types::{ - aggregate_signature::AggregateSignature, - block_executor::partitioner::ExecutableBlock, - block_info::BlockInfo, - ledger_info::LedgerInfo, - on_chain_config::{TransactionDeduperType, TransactionShufflerType}, - transaction::{SignedTransaction, TransactionStatus}, - }; - - struct RecordedCommit { - time: Mutex, - } - - impl BlockExecutorTrait for RecordedCommit { - fn committed_block_id(&self) -> HashValue { - HashValue::zero() - } - - fn reset(&self) -> Result<()> { - Ok(()) - } - - fn execute_block( - &self, - _block: ExecutableBlock, - _parent_block_id: HashValue, - _onchain_config: BlockExecutorConfigFromOnchain, - ) -> ExecutorResult { - Ok(StateComputeResult::new_dummy()) - } - - fn execute_and_state_checkpoint( - &self, - _block: ExecutableBlock, - _parent_block_id: HashValue, - _onchain_config: BlockExecutorConfigFromOnchain, - ) -> ExecutorResult { - todo!() - } - - fn ledger_update( - &self, - _block_id: HashValue, - _parent_block_id: HashValue, - _state_checkpoint_output: StateCheckpointOutput, - ) -> ExecutorResult { - todo!() - } - - fn commit_blocks_ext( - &self, - _block_ids: Vec, - ledger_info_with_sigs: LedgerInfoWithSignatures, - _save_state_snapshots: bool, - ) -> ExecutorResult<()> { - *self.time.lock() = LogicalTime::new( - ledger_info_with_sigs.ledger_info().epoch(), - ledger_info_with_sigs.ledger_info().round(), - ); - Ok(()) - } - - fn finish(&self) {} - } - - #[async_trait::async_trait] - impl TxnNotifier for RecordedCommit { - async fn notify_failed_txn( - &self, - _txns: &[SignedTransaction], - _compute_results: &[TransactionStatus], - ) -> Result<(), MempoolError> { - Ok(()) - } - } - - #[async_trait::async_trait] - impl ConsensusNotificationSender for RecordedCommit { - async fn notify_new_commit( - &self, - _transactions: Vec, - _subscribable_events: Vec, - ) -> std::result::Result<(), Error> { - Ok(()) - } - - async fn sync_to_target( - &self, - target: LedgerInfoWithSignatures, - ) -> std::result::Result<(), Error> { - let logical_time = - LogicalTime::new(target.ledger_info().epoch(), target.ledger_info().round()); - if logical_time <= *self.time.lock() { - return Err(Error::NotificationError( - "Decreasing logical time".to_string(), - )); - } - *self.time.lock() = logical_time; - Ok(()) - } - } - - let callback = Box::new(move |_a: &[Arc], _b: LedgerInfoWithSignatures| {}); - let recorded_commit = Arc::new(RecordedCommit { - time: Mutex::new(LogicalTime::new(0, 0)), - }); - let generate_li = |epoch, round| { - LedgerInfoWithSignatures::new( - LedgerInfo::new( - BlockInfo::random_with_epoch(epoch, round), - HashValue::zero(), - ), - AggregateSignature::empty(), - ) - }; - let executor = ExecutionProxy::new( - recorded_commit.clone(), - recorded_commit.clone(), - recorded_commit.clone(), - &tokio::runtime::Handle::current(), - TransactionFilter::new(Filter::empty()), - ); - - executor.new_epoch( - &EpochState::empty(), - Arc::new(PayloadManager::DirectMempool), - create_transaction_shuffler(TransactionShufflerType::NoShuffling), - BlockExecutorConfigFromOnchain::new_no_block_limit(), - create_transaction_deduper(TransactionDeduperType::NoDedup), - false, - ); - executor - .commit(&[], generate_li(1, 1), callback.clone()) - .await - .unwrap(); - executor - .commit(&[], generate_li(1, 10), callback) - .await - .unwrap(); - assert!(executor.sync_to(generate_li(1, 8)).await.is_ok()); - assert_eq!(*recorded_commit.time.lock(), LogicalTime::new(1, 10)); - assert!(executor.sync_to(generate_li(2, 8)).await.is_ok()); - assert_eq!(*recorded_commit.time.lock(), LogicalTime::new(2, 8)); -} diff --git a/consensus/src/state_computer_tests.rs b/consensus/src/state_computer_tests.rs deleted file mode 100644 index 66e5b0e6b5da7..0000000000000 --- a/consensus/src/state_computer_tests.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - error::MempoolError, payload_manager::PayloadManager, state_computer::ExecutionProxy, - state_replication::StateComputer, transaction_deduper::NoOpDeduper, - transaction_filter::TransactionFilter, transaction_shuffler::NoOpShuffler, - txn_notifier::TxnNotifier, -}; -use aptos_config::config::transaction_filter_type::Filter; -use aptos_consensus_notifications::{ConsensusNotificationSender, Error}; -use aptos_consensus_types::{block::Block, block_data::BlockData, pipelined_block::PipelinedBlock}; -use aptos_crypto::HashValue; -use aptos_executor_types::{ - state_checkpoint_output::StateCheckpointOutput, BlockExecutorTrait, ExecutorResult, - StateComputeResult, -}; -use aptos_infallible::Mutex; -use aptos_types::{ - aggregate_signature::AggregateSignature, - block_executor::{config::BlockExecutorConfigFromOnchain, partitioner::ExecutableBlock}, - contract_event::ContractEvent, - epoch_state::EpochState, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - transaction::{ExecutionStatus, SignedTransaction, Transaction, TransactionStatus}, - validator_txn::ValidatorTransaction, -}; -use futures_channel::oneshot; -use std::sync::Arc; -use tokio::runtime::Handle; - -struct DummyStateSyncNotifier { - invocations: Mutex, Vec)>>, -} - -impl DummyStateSyncNotifier { - fn new() -> Self { - Self { - invocations: Mutex::new(vec![]), - } - } -} - -#[async_trait::async_trait] -impl ConsensusNotificationSender for DummyStateSyncNotifier { - async fn notify_new_commit( - &self, - transactions: Vec, - subscribable_events: Vec, - ) -> Result<(), Error> { - self.invocations - .lock() - .push((transactions, subscribable_events)); - Ok(()) - } - - async fn sync_to_target(&self, _target: LedgerInfoWithSignatures) -> Result<(), Error> { - unreachable!() - } -} - -struct DummyTxnNotifier {} - -#[async_trait::async_trait] -impl TxnNotifier for DummyTxnNotifier { - async fn notify_failed_txn( - &self, - _txns: &[SignedTransaction], - _statuses: &[TransactionStatus], - ) -> anyhow::Result<(), MempoolError> { - Ok(()) - } -} - -struct DummyBlockExecutor { - blocks_received: Mutex>, -} - -impl DummyBlockExecutor { - fn new() -> Self { - Self { - blocks_received: Mutex::new(vec![]), - } - } -} - -impl BlockExecutorTrait for DummyBlockExecutor { - fn committed_block_id(&self) -> HashValue { - HashValue::zero() - } - - fn reset(&self) -> anyhow::Result<()> { - Ok(()) - } - - fn execute_block( - &self, - _block: ExecutableBlock, - _parent_block_id: HashValue, - _onchain_config: BlockExecutorConfigFromOnchain, - ) -> ExecutorResult { - Ok(StateComputeResult::new_dummy()) - } - - fn execute_and_state_checkpoint( - &self, - block: ExecutableBlock, - _parent_block_id: HashValue, - _onchain_config: BlockExecutorConfigFromOnchain, - ) -> ExecutorResult { - self.blocks_received.lock().push(block); - Ok(StateCheckpointOutput::default()) - } - - fn ledger_update( - &self, - _block_id: HashValue, - _parent_block_id: HashValue, - _state_checkpoint_output: StateCheckpointOutput, - ) -> ExecutorResult { - Ok(StateComputeResult::new_dummy()) - } - - fn commit_blocks_ext( - &self, - _block_ids: Vec, - _ledger_info_with_sigs: LedgerInfoWithSignatures, - _save_state_snapshots: bool, - ) -> ExecutorResult<()> { - Ok(()) - } - - fn finish(&self) {} -} - -#[tokio::test] -#[cfg(test)] -async fn schedule_compute_should_discover_validator_txns() { - let executor = Arc::new(DummyBlockExecutor::new()); - - let execution_policy = ExecutionProxy::new( - executor.clone(), - Arc::new(DummyTxnNotifier {}), - Arc::new(DummyStateSyncNotifier::new()), - &Handle::current(), - TransactionFilter::new(Filter::empty()), - ); - - let validator_txn_0 = ValidatorTransaction::dummy(vec![0xFF; 99]); - let validator_txn_1 = ValidatorTransaction::dummy(vec![0xFF; 999]); - - let block = Block::new_for_testing( - HashValue::zero(), - BlockData::dummy_with_validator_txns(vec![ - validator_txn_0.clone(), - validator_txn_1.clone(), - ]), - None, - ); - - let epoch_state = EpochState::empty(); - - execution_policy.new_epoch( - &epoch_state, - Arc::new(PayloadManager::DirectMempool), - Arc::new(NoOpShuffler {}), - BlockExecutorConfigFromOnchain::new_no_block_limit(), - Arc::new(NoOpDeduper {}), - false, - ); - - // Ensure the dummy executor has received the txns. - let _ = execution_policy - .schedule_compute(&block, HashValue::zero(), None) - .await - .await; - - // Get the txns from the view of the dummy executor. - let txns = executor.blocks_received.lock()[0] - .transactions - .clone() - .into_txns(); - - let supposed_validator_txn_0 = txns[1].expect_valid().try_as_validator_txn().unwrap(); - let supposed_validator_txn_1 = txns[2].expect_valid().try_as_validator_txn().unwrap(); - assert_eq!(&validator_txn_0, supposed_validator_txn_0); - assert_eq!(&validator_txn_1, supposed_validator_txn_1); -} - -#[tokio::test] -async fn commit_should_discover_validator_txns() { - let state_sync_notifier = Arc::new(DummyStateSyncNotifier::new()); - - let execution_policy = ExecutionProxy::new( - Arc::new(DummyBlockExecutor::new()), - Arc::new(DummyTxnNotifier {}), - state_sync_notifier.clone(), - &tokio::runtime::Handle::current(), - TransactionFilter::new(Filter::empty()), - ); - - let validator_txn_0 = ValidatorTransaction::dummy(vec![0xFF; 99]); - let validator_txn_1 = ValidatorTransaction::dummy(vec![0xFF; 999]); - - let block = Block::new_for_testing( - HashValue::zero(), - BlockData::dummy_with_validator_txns(vec![ - validator_txn_0.clone(), - validator_txn_1.clone(), - ]), - None, - ); - - // Eventually 3 txns: block metadata, validator txn 0, validator txn 1. - let state_compute_result = StateComputeResult::new_dummy_with_compute_status(vec![ - TransactionStatus::Keep( - ExecutionStatus::Success - ); - 3 - ]); - - let blocks = vec![Arc::new(PipelinedBlock::new( - block, - vec![], - state_compute_result, - ))]; - let epoch_state = EpochState::empty(); - - execution_policy.new_epoch( - &epoch_state, - Arc::new(PayloadManager::DirectMempool), - Arc::new(NoOpShuffler {}), - BlockExecutorConfigFromOnchain::new_no_block_limit(), - Arc::new(NoOpDeduper {}), - false, - ); - - let (tx, rx) = oneshot::channel::<()>(); - - let callback = Box::new( - move |_a: &[Arc], _b: LedgerInfoWithSignatures| { - tx.send(()).unwrap(); - }, - ); - - let _ = execution_policy - .commit( - blocks.as_slice(), - LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()), - callback, - ) - .await; - - // Wait until state sync is notified. - let _ = rx.await; - - // Get all txns that state sync was notified with. - let (txns, _) = state_sync_notifier.invocations.lock()[0].clone(); - - let supposed_validator_txn_0 = txns[1].try_as_validator_txn().unwrap(); - let supposed_validator_txn_1 = txns[2].try_as_validator_txn().unwrap(); - assert_eq!(&validator_txn_0, supposed_validator_txn_0); - assert_eq!(&validator_txn_1, supposed_validator_txn_1); -} diff --git a/consensus/src/state_replication.rs b/consensus/src/state_replication.rs deleted file mode 100644 index 26da5fa80d163..0000000000000 --- a/consensus/src/state_replication.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::StateSyncError, - payload_manager::PayloadManager, - state_computer::{PipelineExecutionResult, StateComputeResultFut}, - transaction_deduper::TransactionDeduper, - transaction_shuffler::TransactionShuffler, -}; -use anyhow::Result; -use aptos_consensus_types::{block::Block, pipelined_block::PipelinedBlock}; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorResult; -use aptos_types::{ - block_executor::config::BlockExecutorConfigFromOnchain, epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, randomness::Randomness, -}; -use std::sync::Arc; - -pub type StateComputerCommitCallBackType = - Box], LedgerInfoWithSignatures) + Send + Sync>; - -/// While Consensus is managing proposed blocks, `StateComputer` is managing the results of the -/// (speculative) execution of their payload. -/// StateComputer is using proposed block ids for identifying the transactions. -#[async_trait::async_trait] -pub trait StateComputer: Send + Sync { - /// How to execute a sequence of transactions and obtain the next state. While some of the - /// transactions succeed, some of them can fail. - /// In case all the transactions are failed, new_state_id is equal to the previous state id. - async fn compute( - &self, - // The block that will be computed. - block: &Block, - // The parent block root hash. - parent_block_id: HashValue, - randomness: Option, - ) -> ExecutorResult { - self.schedule_compute(block, parent_block_id, randomness) - .await - .await - } - - async fn schedule_compute( - &self, - // The block that will be computed. - _block: &Block, - // The parent block root hash. - _parent_block_id: HashValue, - _randomness: Option, - ) -> StateComputeResultFut { - unimplemented!("This state computer does not support scheduling"); - } - - /// Send a successful commit. A future is fulfilled when the state is finalized. - async fn commit( - &self, - blocks: &[Arc], - finality_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> ExecutorResult<()>; - - /// Best effort state synchronization to the given target LedgerInfo. - /// In case of success (`Result::Ok`) the LI of storage is at the given target. - /// In case of failure (`Result::Error`) the LI of storage remains unchanged, and the validator - /// can assume there were no modifications to the storage made. - async fn sync_to(&self, target: LedgerInfoWithSignatures) -> Result<(), StateSyncError>; - - // Reconfigure to execute transactions for a new epoch. - fn new_epoch( - &self, - epoch_state: &EpochState, - payload_manager: Arc, - transaction_shuffler: Arc, - block_executor_onchain_config: BlockExecutorConfigFromOnchain, - transaction_deduper: Arc, - randomness_enabled: bool, - ); - - // Reconfigure to clear epoch state at end of epoch. - fn end_epoch(&self); -} diff --git a/consensus/src/test_utils/mock_execution_client.rs b/consensus/src/test_utils/mock_execution_client.rs deleted file mode 100644 index 5a1b1d724415d..0000000000000 --- a/consensus/src/test_utils/mock_execution_client.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::StateSyncError, - network::{IncomingCommitRequest, IncomingRandGenRequest}, - payload_manager::PayloadManager, - pipeline::{ - buffer_manager::OrderedBlocks, execution_client::TExecutionClient, - signing_phase::CommitSignerProvider, - }, - rand::rand_gen::types::RandConfig, - state_replication::StateComputerCommitCallBackType, - test_utils::mock_storage::MockStorage, -}; -use anyhow::{format_err, Result}; -use aptos_channels::aptos_channel; -use aptos_consensus_types::{common::Payload, pipelined_block::PipelinedBlock}; -use aptos_crypto::HashValue; -use aptos_executor_types::ExecutorResult; -use aptos_infallible::Mutex; -use aptos_logger::prelude::*; -use aptos_types::{ - epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, - on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig, OnChainRandomnessConfig}, - transaction::SignedTransaction, -}; -use futures::{channel::mpsc, SinkExt}; -use futures_channel::mpsc::UnboundedSender; -use move_core_types::account_address::AccountAddress; -use std::{collections::HashMap, sync::Arc}; - -pub struct MockExecutionClient { - state_sync_client: mpsc::UnboundedSender>, - executor_channel: UnboundedSender, - consensus_db: Arc, - block_cache: Mutex>, - payload_manager: Arc, -} - -impl MockExecutionClient { - pub fn new( - state_sync_client: mpsc::UnboundedSender>, - executor_channel: UnboundedSender, - consensus_db: Arc, - ) -> Self { - MockExecutionClient { - state_sync_client, - executor_channel, - consensus_db, - block_cache: Mutex::new(HashMap::new()), - payload_manager: Arc::from(PayloadManager::DirectMempool), - } - } - - pub async fn commit_to_storage(&self, blocks: OrderedBlocks) -> ExecutorResult<()> { - let OrderedBlocks { - ordered_blocks, - ordered_proof, - callback, - } = blocks; - - self.consensus_db - .commit_to_storage(ordered_proof.ledger_info().clone()); - // mock sending commit notif to state sync - let mut txns = vec![]; - for block in &ordered_blocks { - self.block_cache - .lock() - .remove(&block.id()) - .ok_or_else(|| format_err!("Cannot find block"))?; - let (mut payload_txns, _max_txns_from_block_to_execute) = - self.payload_manager.get_transactions(block.block()).await?; - txns.append(&mut payload_txns); - } - // they may fail during shutdown - let _ = self.state_sync_client.unbounded_send(txns); - - callback( - &ordered_blocks.into_iter().map(Arc::new).collect::>(), - ordered_proof, - ); - - Ok(()) - } -} - -#[async_trait::async_trait] -impl TExecutionClient for MockExecutionClient { - async fn start_epoch( - &self, - _epoch_state: Arc, - _commit_signer_provider: Arc, - _payload_manager: Arc, - _onchain_consensus_config: &OnChainConsensusConfig, - _onchain_execution_config: &OnChainExecutionConfig, - _onchain_randomness_config: &OnChainRandomnessConfig, - _rand_config: Option, - _rand_msg_rx: aptos_channel::Receiver, - ) { - } - - fn get_execution_channel(&self) -> Option> { - Some(self.executor_channel.clone()) - } - - async fn finalize_order( - &self, - blocks: &[Arc], - finality_proof: LedgerInfoWithSignatures, - callback: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - assert!(!blocks.is_empty()); - info!( - "MockStateComputer commit put on queue {:?}", - blocks.iter().map(|v| v.round()).collect::>() - ); - - for block in blocks { - self.block_cache.lock().insert( - block.id(), - block - .payload() - .unwrap_or(&Payload::empty(false, true)) - .clone(), - ); - } - - if self - .executor_channel - .clone() - .send(OrderedBlocks { - ordered_blocks: blocks - .iter() - .map(|b| (**b).clone()) - .collect::>(), - ordered_proof: finality_proof, - callback, - }) - .await - .is_err() - { - debug!("Failed to send to buffer manager, maybe epoch ends"); - } - - Ok(()) - } - - fn send_commit_msg( - &self, - _peer_id: AccountAddress, - _commit_msg: IncomingCommitRequest, - ) -> Result<()> { - Ok(()) - } - - async fn sync_to(&self, commit: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - debug!( - "Fake sync to block id {}", - commit.ledger_info().consensus_block_id() - ); - self.consensus_db - .commit_to_storage(commit.ledger_info().clone()); - Ok(()) - } - - async fn end_epoch(&self) {} -} diff --git a/consensus/src/test_utils/mock_payload_manager.rs b/consensus/src/test_utils/mock_payload_manager.rs deleted file mode 100644 index d28337e51ebfe..0000000000000 --- a/consensus/src/test_utils/mock_payload_manager.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::QuorumStoreError, - payload_client::{user::quorum_store_client::QuorumStoreClient, PayloadClient}, -}; -use anyhow::Result; -use aptos_consensus_types::{ - block::block_test_utils::random_payload, - common::{Payload, PayloadFilter}, - request_response::GetPayloadCommand, -}; -use aptos_types::{ - transaction::{ExecutionStatus, TransactionStatus}, - validator_txn::ValidatorTransaction, - vm_status::StatusCode, -}; -use aptos_validator_transaction_pool as vtxn_pool; -use futures::{channel::mpsc, future::BoxFuture}; -use rand::Rng; -use std::time::Duration; - -#[allow(dead_code)] -pub struct MockPayloadManager { - // used non-mocked PayloadClient to test interaction with shared mempool - _quorum_store_client: Option, -} - -impl MockPayloadManager { - pub fn new(consensus_to_quorum_store_sender: Option>) -> Self { - let quorum_store_client = - consensus_to_quorum_store_sender.map(|s| QuorumStoreClient::new(s, 1, 1.1, 100)); - Self { - _quorum_store_client: quorum_store_client, - } - } -} - -// mock transaction status on the fly -fn _mock_transaction_status(count: usize) -> Vec { - let mut statuses = vec![]; - // generate count + 1 status to mock the block metadata txn in mempool proxy - for _ in 0..=count { - let random_status = match rand::thread_rng().gen_range(0, 1000) { - 0 => TransactionStatus::Discard(StatusCode::UNKNOWN_VALIDATION_STATUS), - _ => TransactionStatus::Keep(ExecutionStatus::Success), - }; - statuses.push(random_status); - } - statuses -} - -#[async_trait::async_trait] -impl PayloadClient for MockPayloadManager { - /// The returned future is fulfilled with the vector of SignedTransactions - async fn pull_payload( - &self, - _max_poll_time: Duration, - _max_size: u64, - _max_bytes: u64, - _max_inline_size: u64, - _max_inline_bytes: u64, - _validator_txn_filter: vtxn_pool::TransactionFilter, - _user_txn_filter: PayloadFilter, - _wait_callback: BoxFuture<'static, ()>, - _pending_ordering: bool, - _pending_uncommitted_blocks: usize, - _recent_fill_fraction: f32, - ) -> Result<(Vec, Payload), QuorumStoreError> { - // generate 1k txn is too slow with coverage instrumentation - Ok(( - vec![ValidatorTransaction::dummy(vec![0xFF; 1])], - random_payload(10), - )) - } -} diff --git a/consensus/src/test_utils/mock_quorum_store_sender.rs b/consensus/src/test_utils/mock_quorum_store_sender.rs deleted file mode 100644 index affff1103b49e..0000000000000 --- a/consensus/src/test_utils/mock_quorum_store_sender.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network::QuorumStoreSender, - network_interface::ConsensusMsg, - quorum_store::types::{Batch, BatchRequest, BatchResponse}, -}; -use aptos_consensus_types::{ - common::Author, - proof_of_store::{ProofOfStore, ProofOfStoreMsg, SignedBatchInfo, SignedBatchInfoMsg}, -}; -use std::time::Duration; -use tokio::sync::mpsc::Sender; - -#[derive(Clone)] -pub struct MockQuorumStoreSender { - tx: Sender<(ConsensusMsg, Vec)>, -} - -impl MockQuorumStoreSender { - pub fn new(tx: Sender<(ConsensusMsg, Vec)>) -> Self { - Self { tx } - } -} - -#[async_trait::async_trait] -impl QuorumStoreSender for MockQuorumStoreSender { - async fn send_batch_request(&self, request: BatchRequest, recipients: Vec) { - self.tx - .send((ConsensusMsg::BatchRequestMsg(Box::new(request)), recipients)) - .await - .expect("could not send"); - } - - async fn request_batch( - &self, - _request: BatchRequest, - _recipient: Author, - _timeout: Duration, - ) -> anyhow::Result { - unimplemented!(); - } - - async fn send_batch(&self, batch: Batch, recipients: Vec) { - self.tx - .send((ConsensusMsg::BatchResponse(Box::new(batch)), recipients)) - .await - .expect("could not send"); - } - - async fn send_signed_batch_info_msg( - &self, - signed_batch_infos: Vec, - recipients: Vec, - ) { - self.tx - .send(( - ConsensusMsg::SignedBatchInfo(Box::new(SignedBatchInfoMsg::new( - signed_batch_infos, - ))), - recipients, - )) - .await - .expect("could not send"); - } - - async fn broadcast_batch_msg(&mut self, _batches: Vec) { - unimplemented!() - } - - async fn broadcast_proof_of_store_msg(&mut self, proof_of_stores: Vec) { - self.tx - .send(( - ConsensusMsg::ProofOfStoreMsg(Box::new(ProofOfStoreMsg::new(proof_of_stores))), - vec![], - )) - .await - .unwrap(); - } - - async fn send_proof_of_store_msg_to_self(&mut self, _proof_of_stores: Vec) { - unimplemented!() - } -} diff --git a/consensus/src/test_utils/mock_state_computer.rs b/consensus/src/test_utils/mock_state_computer.rs deleted file mode 100644 index aeef2a0ef6e97..0000000000000 --- a/consensus/src/test_utils/mock_state_computer.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - error::StateSyncError, - payload_manager::PayloadManager, - pipeline::buffer_manager::OrderedBlocks, - state_computer::{PipelineExecutionResult, StateComputeResultFut}, - state_replication::{StateComputer, StateComputerCommitCallBackType}, - transaction_deduper::TransactionDeduper, - transaction_shuffler::TransactionShuffler, -}; -use anyhow::Result; -use aptos_consensus_types::{block::Block, pipelined_block::PipelinedBlock}; -use aptos_crypto::HashValue; -use aptos_executor_types::{ExecutorError, ExecutorResult, StateComputeResult}; -use aptos_logger::debug; -use aptos_types::{ - block_executor::config::BlockExecutorConfigFromOnchain, epoch_state::EpochState, - ledger_info::LedgerInfoWithSignatures, randomness::Randomness, -}; -use futures::SinkExt; -use futures_channel::mpsc::UnboundedSender; -use std::sync::Arc; - -pub struct EmptyStateComputer { - executor_channel: UnboundedSender, -} - -impl EmptyStateComputer { - pub fn new(executor_channel: UnboundedSender) -> Self { - Self { executor_channel } - } -} - -#[async_trait::async_trait] -impl StateComputer for EmptyStateComputer { - async fn compute( - &self, - _block: &Block, - _parent_block_id: HashValue, - _randomness: Option, - ) -> ExecutorResult { - Ok(PipelineExecutionResult::new( - vec![], - StateComputeResult::new_dummy(), - )) - } - - async fn commit( - &self, - blocks: &[Arc], - commit: LedgerInfoWithSignatures, - call_back: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - assert!(!blocks.is_empty()); - - if self - .executor_channel - .clone() - .send(OrderedBlocks { - ordered_blocks: blocks - .iter() - .map(|b| (**b).clone()) - .collect::>(), - ordered_proof: commit, - callback: call_back, - }) - .await - .is_err() - { - debug!("Failed to send to buffer manager, maybe epoch ends"); - } - - Ok(()) - } - - async fn sync_to(&self, _commit: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - Ok(()) - } - - fn new_epoch( - &self, - _: &EpochState, - _: Arc, - _: Arc, - _: BlockExecutorConfigFromOnchain, - _: Arc, - _: bool, - ) { - } - - fn end_epoch(&self) {} -} - -/// Random Compute Result State Computer -/// When compute(), if parent id is random_compute_result_root_hash, it returns Err(Error::BlockNotFound(parent_block_id)) -/// Otherwise, it returns a dummy StateComputeResult with root hash as random_compute_result_root_hash. -pub struct RandomComputeResultStateComputer { - random_compute_result_root_hash: HashValue, -} - -impl RandomComputeResultStateComputer { - pub fn new() -> Self { - Self { - random_compute_result_root_hash: HashValue::random(), - } - } - - pub fn get_root_hash(&self) -> HashValue { - self.random_compute_result_root_hash - } -} - -#[async_trait::async_trait] -impl StateComputer for RandomComputeResultStateComputer { - async fn schedule_compute( - &self, - _block: &Block, - parent_block_id: HashValue, - _randomness: Option, - ) -> StateComputeResultFut { - // trapdoor for Execution Error - let res = if parent_block_id == self.random_compute_result_root_hash { - Err(ExecutorError::BlockNotFound(parent_block_id)) - } else { - Ok(StateComputeResult::new_dummy_with_root_hash( - self.random_compute_result_root_hash, - )) - }; - let pipeline_execution_res = res.map(|res| PipelineExecutionResult::new(vec![], res)); - Box::pin(async move { pipeline_execution_res }) - } - - async fn commit( - &self, - _blocks: &[Arc], - _commit: LedgerInfoWithSignatures, - _call_back: StateComputerCommitCallBackType, - ) -> ExecutorResult<()> { - Ok(()) - } - - async fn sync_to(&self, _commit: LedgerInfoWithSignatures) -> Result<(), StateSyncError> { - Ok(()) - } - - fn new_epoch( - &self, - _: &EpochState, - _: Arc, - _: Arc, - _: BlockExecutorConfigFromOnchain, - _: Arc, - _: bool, - ) { - } - - fn end_epoch(&self) {} -} diff --git a/consensus/src/test_utils/mock_storage.rs b/consensus/src/test_utils/mock_storage.rs deleted file mode 100644 index 8f3e67c3ccffb..0000000000000 --- a/consensus/src/test_utils/mock_storage.rs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - epoch_manager::LivenessStorageData, - persistent_liveness_storage::{ - LedgerRecoveryData, PersistentLivenessStorage, RecoveryData, RootMetadata, - }, -}; -use anyhow::Result; -use aptos_consensus_types::{ - block::Block, quorum_cert::QuorumCert, timeout_2chain::TwoChainTimeoutCertificate, vote::Vote, -}; -use aptos_crypto::HashValue; -use aptos_infallible::Mutex; -use aptos_storage_interface::DbReader; -use aptos_types::{ - aggregate_signature::AggregateSignature, - epoch_change::EpochChangeProof, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - on_chain_config::ValidatorSet, -}; -use std::{collections::HashMap, sync::Arc}; - -pub struct MockSharedStorage { - // Safety state - pub block: Mutex>, - pub qc: Mutex>, - pub lis: Mutex>, - pub last_vote: Mutex>, - - // Liveness state - pub highest_2chain_timeout_certificate: Mutex>, - pub validator_set: ValidatorSet, -} - -impl MockSharedStorage { - pub fn new(validator_set: ValidatorSet) -> Self { - MockSharedStorage { - block: Mutex::new(HashMap::new()), - qc: Mutex::new(HashMap::new()), - lis: Mutex::new(HashMap::new()), - last_vote: Mutex::new(None), - highest_2chain_timeout_certificate: Mutex::new(None), - validator_set, - } - } -} - -/// A storage that simulates the operations in-memory, used in the tests that cares about storage -/// consistency. -pub struct MockStorage { - pub shared_storage: Arc, - storage_ledger: Mutex, -} - -impl MockStorage { - pub fn new_with_ledger_info( - shared_storage: Arc, - ledger_info: LedgerInfo, - ) -> Self { - let li = if ledger_info.ends_epoch() { - ledger_info.clone() - } else { - let validator_set = Some(shared_storage.validator_set.clone()); - LedgerInfo::mock_genesis(validator_set) - }; - let lis = LedgerInfoWithSignatures::new(li, AggregateSignature::empty()); - shared_storage - .lis - .lock() - .insert(lis.ledger_info().version(), lis); - MockStorage { - shared_storage, - storage_ledger: Mutex::new(ledger_info), - } - } - - pub fn get_ledger_info(&self) -> LedgerInfo { - self.storage_ledger.lock().clone() - } - - pub fn commit_to_storage(&self, ledger: LedgerInfo) { - *self.storage_ledger.lock() = ledger; - - if let Err(e) = self.verify_consistency() { - panic!("invalid db after commit: {}", e); - } - } - - pub fn get_validator_set(&self) -> &ValidatorSet { - &self.shared_storage.validator_set - } - - pub fn get_ledger_recovery_data(&self) -> LedgerRecoveryData { - LedgerRecoveryData::new(LedgerInfoWithSignatures::new( - self.storage_ledger.lock().clone(), - AggregateSignature::empty(), - )) - } - - pub fn try_start(&self) -> Result { - let ledger_recovery_data = self.get_ledger_recovery_data(); - let mut blocks: Vec<_> = self - .shared_storage - .block - .lock() - .clone() - .into_values() - .collect(); - let quorum_certs = self - .shared_storage - .qc - .lock() - .clone() - .into_values() - .collect(); - blocks.sort_by_key(Block::round); - let last_vote = self.shared_storage.last_vote.lock().clone(); - let qc = self - .shared_storage - .highest_2chain_timeout_certificate - .lock() - .clone(); - RecoveryData::new( - last_vote, - ledger_recovery_data, - blocks, - RootMetadata::new_empty(), - quorum_certs, - qc, - ) - } - - pub fn verify_consistency(&self) -> Result<()> { - self.try_start().map(|_| ()) - } - - pub fn start_for_testing(validator_set: ValidatorSet) -> (RecoveryData, Arc) { - let shared_storage = Arc::new(MockSharedStorage::new(validator_set.clone())); - let genesis_li = LedgerInfo::mock_genesis(Some(validator_set)); - let storage = Self::new_with_ledger_info(shared_storage, genesis_li); - let recovery_data = match storage.start() { - LivenessStorageData::FullRecoveryData(recovery_data) => recovery_data, - _ => panic!("Mock storage should never fail constructing recovery data"), - }; - - (recovery_data, Arc::new(storage)) - } -} - -// A impl that always start from genesis. -impl PersistentLivenessStorage for MockStorage { - fn save_tree(&self, blocks: Vec, quorum_certs: Vec) -> Result<()> { - // When the shared storage is empty, we are expected to not able to construct an block tree - // from it. During test we will intentionally clear shared_storage to simulate the situation - // of restarting from an empty consensusDB - // info!("step 1.3.4.2.3.1"); - let should_check_for_consistency = !(self.shared_storage.block.lock().is_empty() - && self.shared_storage.qc.lock().is_empty()); - for block in blocks { - self.shared_storage.block.lock().insert(block.id(), block); - } - // info!("step 1.3.4.2.3.2"); - for qc in quorum_certs { - self.shared_storage - .qc - .lock() - .insert(qc.certified_block().id(), qc); - } - // info!("step 1.3.4.2.3.3"); - if should_check_for_consistency { - if let Err(e) = self.verify_consistency() { - panic!("invalid db after save tree: {}", e); - } - } - Ok(()) - } - - fn prune_tree(&self, block_id: Vec) -> Result<()> { - for id in block_id { - self.shared_storage.block.lock().remove(&id); - self.shared_storage.qc.lock().remove(&id); - } - if let Err(e) = self.verify_consistency() { - panic!("invalid db after prune tree: {}", e); - } - Ok(()) - } - - fn save_vote(&self, last_vote: &Vote) -> Result<()> { - self.shared_storage - .last_vote - .lock() - .replace(last_vote.clone()); - Ok(()) - } - - fn recover_from_ledger(&self) -> LedgerRecoveryData { - self.get_ledger_recovery_data() - } - - fn start(&self) -> LivenessStorageData { - match self.try_start() { - Ok(recovery_data) => LivenessStorageData::FullRecoveryData(recovery_data), - Err(_) => LivenessStorageData::PartialRecoveryData(self.recover_from_ledger()), - } - } - - fn save_highest_2chain_timeout_cert( - &self, - highest_timeout_certificate: &TwoChainTimeoutCertificate, - ) -> Result<()> { - self.shared_storage - .highest_2chain_timeout_certificate - .lock() - .replace(highest_timeout_certificate.clone()); - Ok(()) - } - - fn retrieve_epoch_change_proof(&self, version: u64) -> Result { - let lis = self - .shared_storage - .lis - .lock() - .get(&version) - .cloned() - .ok_or_else(|| anyhow::anyhow!("LedgerInfo for version not found"))?; - Ok(EpochChangeProof::new(vec![lis], false)) - } - - fn aptos_db(&self) -> Arc { - unimplemented!() - } - - fn consensus_db(&self) -> Arc { - unimplemented!() - } -} - -/// A storage that ignores any requests, used in the tests that don't care about the storage. -pub struct EmptyStorage; - -impl EmptyStorage { - pub fn new() -> Self { - Self - } - - pub fn start_for_testing() -> (RecoveryData, Arc) { - let storage = Arc::new(EmptyStorage::new()); - let recovery_data = match storage.start() { - LivenessStorageData::FullRecoveryData(recovery_data) => recovery_data, - _ => panic!("Mock storage should never fail constructing recovery data"), - }; - (recovery_data, storage) - } -} - -impl PersistentLivenessStorage for EmptyStorage { - fn save_tree(&self, _: Vec, _: Vec) -> Result<()> { - Ok(()) - } - - fn prune_tree(&self, _: Vec) -> Result<()> { - Ok(()) - } - - fn save_vote(&self, _: &Vote) -> Result<()> { - Ok(()) - } - - fn recover_from_ledger(&self) -> LedgerRecoveryData { - LedgerRecoveryData::new(LedgerInfoWithSignatures::new( - LedgerInfo::mock_genesis(None), - AggregateSignature::empty(), - )) - } - - fn start(&self) -> LivenessStorageData { - match RecoveryData::new( - None, - self.recover_from_ledger(), - vec![], - RootMetadata::new_empty(), - vec![], - None, - ) { - Ok(recovery_data) => LivenessStorageData::FullRecoveryData(recovery_data), - Err(e) => { - eprintln!("{}", e); - panic!("Construct recovery data during genesis should never fail"); - }, - } - } - - fn save_highest_2chain_timeout_cert(&self, _: &TwoChainTimeoutCertificate) -> Result<()> { - Ok(()) - } - - fn retrieve_epoch_change_proof(&self, _version: u64) -> Result { - Ok(EpochChangeProof::new(vec![], false)) - } - - fn aptos_db(&self) -> Arc { - unimplemented!() - } - - fn consensus_db(&self) -> Arc { - unimplemented!() - } -} diff --git a/consensus/src/test_utils/mod.rs b/consensus/src/test_utils/mod.rs deleted file mode 100644 index 453ee65b9b0f8..0000000000000 --- a/consensus/src/test_utils/mod.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::block_storage::{BlockReader, BlockStore}; -use aptos_consensus_types::{ - block::{block_test_utils::certificate_for_genesis, Block}, - common::{Author, Round}, - pipelined_block::PipelinedBlock, - quorum_cert::QuorumCert, - sync_info::SyncInfo, -}; -use aptos_crypto::{HashValue, PrivateKey, Uniform}; -use aptos_logger::Level; -use aptos_types::{ledger_info::LedgerInfo, validator_signer::ValidatorSigner}; -use std::{future::Future, sync::Arc, time::Duration}; -use tokio::{runtime, time::timeout}; - -#[cfg(test)] -pub mod mock_execution_client; -#[cfg(any(test, feature = "fuzzing"))] -mod mock_payload_manager; -pub mod mock_quorum_store_sender; -mod mock_state_computer; -mod mock_storage; - -use crate::{ - payload_manager::PayloadManager, pipeline::execution_client::DummyExecutionClient, - util::mock_time_service::SimulatedTimeService, -}; -use aptos_consensus_types::{block::block_test_utils::gen_test_certificate, common::Payload}; -use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519Signature}; -use aptos_types::{ - block_info::BlockInfo, - chain_id::ChainId, - transaction::{RawTransaction, Script, SignedTransaction, TransactionPayload}, -}; -pub use mock_payload_manager::MockPayloadManager; -#[cfg(test)] -pub use mock_state_computer::EmptyStateComputer; -#[cfg(test)] -pub use mock_state_computer::RandomComputeResultStateComputer; -pub use mock_storage::{EmptyStorage, MockStorage}; -use move_core_types::account_address::AccountAddress; - -pub const TEST_TIMEOUT: Duration = Duration::from_secs(60); - -pub async fn build_simple_tree() -> (Vec>, Arc) { - let mut inserter = TreeInserter::default(); - let block_store = inserter.block_store(); - let genesis = block_store.ordered_root(); - let genesis_block_id = genesis.id(); - let genesis_block = block_store - .get_block(genesis_block_id) - .expect("genesis block must exist"); - assert_eq!(block_store.len(), 1); - assert_eq!(block_store.child_links(), block_store.len() - 1); - assert!(block_store.block_exists(genesis_block.id())); - - // ╭--> A1--> A2--> A3 - // Genesis--> B1--> B2 - // ╰--> C1 - let a1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis_block, 1) - .await; - let a2 = inserter.insert_block(&a1, 2, None).await; - let a3 = inserter - .insert_block(&a2, 3, Some(genesis.block_info())) - .await; - let b1 = inserter - .insert_block_with_qc(certificate_for_genesis(), &genesis_block, 4) - .await; - let b2 = inserter.insert_block(&b1, 5, None).await; - let c1 = inserter.insert_block(&b1, 6, None).await; - - assert_eq!(block_store.len(), 7); - assert_eq!(block_store.child_links(), block_store.len() - 1); - - (vec![genesis_block, a1, a2, a3, b1, b2, c1], block_store) -} - -pub fn build_empty_tree() -> Arc { - let (initial_data, storage) = EmptyStorage::start_for_testing(); - Arc::new(BlockStore::new( - storage, - initial_data, - Arc::new(DummyExecutionClient), - 10, // max pruned blocks in mem - Arc::new(SimulatedTimeService::new()), - 10, - Arc::from(PayloadManager::DirectMempool), - )) -} - -pub struct TreeInserter { - signer: ValidatorSigner, - block_store: Arc, -} - -impl TreeInserter { - pub fn default() -> Self { - Self::new(ValidatorSigner::random(None)) - } - - pub fn new(signer: ValidatorSigner) -> Self { - let block_store = build_empty_tree(); - Self { - signer, - block_store, - } - } - - pub fn new_with_store(signer: ValidatorSigner, block_store: Arc) -> Self { - Self { - signer, - block_store, - } - } - - pub fn signer(&self) -> &ValidatorSigner { - &self.signer - } - - pub fn block_store(&self) -> Arc { - Arc::clone(&self.block_store) - } - - /// This function is generating a placeholder QC for a block's parent that is signed by a single - /// signer kept by the block store. If more sophisticated QC required, please use - /// `insert_block_with_qc`. - pub async fn insert_block( - &mut self, - parent: &PipelinedBlock, - round: Round, - committed_block: Option, - ) -> Arc { - // Node must carry a QC to its parent - let parent_qc = self.create_qc_for_block(parent, committed_block); - self.insert_block_with_qc(parent_qc, parent, round).await - } - - pub async fn insert_block_with_qc( - &mut self, - parent_qc: QuorumCert, - parent: &PipelinedBlock, - round: Round, - ) -> Arc { - self.block_store - .insert_block_with_qc(self.create_block_with_qc( - parent_qc, - parent.timestamp_usecs() + 1, - round, - Payload::empty(false, true), - vec![], - )) - .await - .unwrap() - } - - pub fn create_qc_for_block( - &self, - block: &PipelinedBlock, - committed_block: Option, - ) -> QuorumCert { - gen_test_certificate( - &[self.signer.clone()], - block.block_info(), - block.quorum_cert().certified_block().clone(), - committed_block, - ) - } - - pub fn insert_qc_for_block(&self, block: &PipelinedBlock, committed_block: Option) { - self.block_store - .insert_single_quorum_cert(self.create_qc_for_block(block, committed_block)) - .unwrap() - } - - pub fn create_block_with_qc( - &self, - parent_qc: QuorumCert, - timestamp_usecs: u64, - round: Round, - payload: Payload, - failed_authors: Vec<(Round, Author)>, - ) -> Block { - Block::new_proposal( - payload, - round, - timestamp_usecs, - parent_qc, - &self.signer, - failed_authors, - ) - .unwrap() - } -} - -pub fn placeholder_ledger_info() -> LedgerInfo { - LedgerInfo::new(BlockInfo::empty(), HashValue::zero()) -} - -pub fn placeholder_sync_info() -> SyncInfo { - SyncInfo::new(certificate_for_genesis(), certificate_for_genesis(), None) -} - -fn nocapture() -> bool { - ::std::env::args().any(|arg| arg == "--nocapture") -} - -pub fn consensus_runtime() -> runtime::Runtime { - if nocapture() { - ::aptos_logger::Logger::new().level(Level::Debug).init(); - } - - aptos_runtimes::spawn_named_runtime("consensus".into(), None) -} - -pub fn timed_block_on(runtime: &runtime::Runtime, f: F) -> ::Output -where - F: Future, -{ - runtime - .block_on(async { timeout(TEST_TIMEOUT, f).await }) - .expect("test timed out") -} - -// Creates a single test transaction for a random account -pub(crate) fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { - let private_key = Ed25519PrivateKey::generate_for_testing(); - let public_key = private_key.public_key(); - - let transaction_payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); - let raw_transaction = RawTransaction::new( - AccountAddress::random(), - 0, - transaction_payload, - 0, - gas_unit_price, - 0, - ChainId::new(10), - ); - SignedTransaction::new( - raw_transaction, - public_key, - Ed25519Signature::dummy_signature(), - ) -} - -pub(crate) fn create_vec_signed_transactions(size: u64) -> Vec { - (0..size).map(|_| create_signed_transaction(1)).collect() -} - -pub(crate) fn create_vec_signed_transactions_with_gas( - size: u64, - gas_unit_price: u64, -) -> Vec { - (0..size) - .map(|_| create_signed_transaction(gas_unit_price)) - .collect() -} diff --git a/consensus/src/transaction_deduper.rs b/consensus/src/transaction_deduper.rs deleted file mode 100644 index dc1cff4c6376e..0000000000000 --- a/consensus/src/transaction_deduper.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::txn_hash_and_authenticator_deduper::TxnHashAndAuthenticatorDeduper; -use aptos_logger::info; -use aptos_types::{on_chain_config::TransactionDeduperType, transaction::SignedTransaction}; -use std::sync::Arc; - -/// Interface to dedup transactions. The dedup filters duplicate transactions within a block. -pub trait TransactionDeduper: Send + Sync { - fn dedup(&self, txns: Vec) -> Vec; -} - -/// No Op Deduper to maintain backward compatibility -pub struct NoOpDeduper {} - -impl TransactionDeduper for NoOpDeduper { - fn dedup(&self, txns: Vec) -> Vec { - txns - } -} - -pub fn create_transaction_deduper( - deduper_type: TransactionDeduperType, -) -> Arc { - match deduper_type { - TransactionDeduperType::NoDedup => Arc::new(NoOpDeduper {}), - TransactionDeduperType::TxnHashAndAuthenticatorV1 => { - info!("Using simple hash set transaction deduper"); - Arc::new(TxnHashAndAuthenticatorDeduper::new()) - }, - } -} diff --git a/consensus/src/transaction_filter/mod.rs b/consensus/src/transaction_filter/mod.rs deleted file mode 100644 index a8d319c307cb7..0000000000000 --- a/consensus/src/transaction_filter/mod.rs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_config::config::transaction_filter_type::Filter; -use aptos_crypto::HashValue; -use aptos_types::transaction::SignedTransaction; - -pub struct TransactionFilter { - filter: Filter, -} - -impl TransactionFilter { - pub(crate) fn new(filter: Filter) -> Self { - Self { filter } - } - - pub fn filter( - &self, - block_id: HashValue, - timestamp: u64, - txns: Vec, - ) -> Vec { - // Special case for no filter to avoid unnecessary iteration through all transactions in the default case - if self.filter.is_empty() { - return txns; - } - txns.into_iter() - .filter(|txn| self.filter.allows(block_id, timestamp, txn)) - .collect() - } -} - -#[cfg(test)] -mod test { - use crate::transaction_filter::TransactionFilter; - use aptos_config::config::transaction_filter_type::Filter; - use aptos_crypto::{ed25519::Ed25519PrivateKey, HashValue, PrivateKey, SigningKey, Uniform}; - use aptos_types::{ - chain_id::ChainId, - move_utils::MemberId, - transaction::{EntryFunction, RawTransaction, SignedTransaction, TransactionPayload}, - }; - use move_core_types::account_address::AccountAddress; - - fn create_signed_transaction(function: MemberId) -> SignedTransaction { - let private_key = Ed25519PrivateKey::generate_for_testing(); - let public_key = private_key.public_key(); - let sender = AccountAddress::random(); - let sequence_number = 0; - let MemberId { - module_id, - member_id: function_id, - } = function; - - let payload = TransactionPayload::EntryFunction(EntryFunction::new( - module_id, - function_id, - vec![], - vec![], - )); - let raw_transaction = - RawTransaction::new(sender, sequence_number, payload, 0, 0, 0, ChainId::new(10)); - - SignedTransaction::new( - raw_transaction.clone(), - public_key.clone(), - private_key.sign(&raw_transaction).unwrap(), - ) - } - - fn get_transactions() -> Vec { - vec![ - create_signed_transaction(str::parse("0x1::test::add").unwrap()), - create_signed_transaction(str::parse("0x1::test::check").unwrap()), - create_signed_transaction(str::parse("0x1::test::new").unwrap()), - create_signed_transaction(str::parse("0x1::test::sub").unwrap()), - create_signed_transaction(str::parse("0x2::test2::mul").unwrap()), - create_signed_transaction(str::parse("0x3::test2::div").unwrap()), - create_signed_transaction(str::parse("0x4::test2::mod").unwrap()), - ] - } - - fn get_module_address(txn: &SignedTransaction) -> AccountAddress { - match txn.payload() { - TransactionPayload::EntryFunction(entry_func) => *entry_func.module().address(), - _ => panic!("Unexpected transaction payload"), - } - } - - fn get_module_name(txn: &SignedTransaction) -> String { - match txn.payload() { - TransactionPayload::EntryFunction(entry_func) => entry_func.module().name().to_string(), - _ => panic!("Unexpected transaction payload"), - } - } - - fn get_function_name(txn: &SignedTransaction) -> String { - match txn.payload() { - TransactionPayload::EntryFunction(entry_func) => entry_func.function().to_string(), - _ => panic!("Unexpected transaction payload"), - } - } - - #[test] - fn test_no_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let no_filter = TransactionFilter::new(Filter::empty()); - let filtered_txns = no_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns); - } - - #[test] - fn test_all_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let all_filter = TransactionFilter::new(Filter::empty().add_deny_all()); - let filtered_txns = all_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, vec![]); - } - - #[test] - fn test_block_id_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let block_id_filter = TransactionFilter::new(Filter::empty().add_deny_block_id(block_id)); - - let filtered_txns = block_id_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, vec![]); - let block_id = HashValue::random(); - let filtered_txns = block_id_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns); - } - - #[test] - fn test_block_timestamp_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - // Allows all transactions with block timestamp greater than 1000 - let block_timestamp_filter = TransactionFilter::new( - Filter::empty() - .add_allow_block_timestamp_greater_than(1000) - .add_deny_all(), - ); - - let filtered_txns = block_timestamp_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, vec![]); - let filtered_txns = block_timestamp_filter.filter(block_id, 1001, txns.clone()); - assert_eq!(filtered_txns, txns); - } - - #[test] - fn test_transaction_hash_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let transaction_hash_filter = TransactionFilter::new( - Filter::empty().add_deny_transaction_id(txns[0].clone().committed_hash()), - ); - let filtered_txns = transaction_hash_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[1..].to_vec()); - } - - #[test] - fn test_sender_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let block_list_sender_filter = TransactionFilter::new( - Filter::empty() - .add_deny_sender(txns[0].sender()) - .add_deny_sender(txns[1].sender()), - ); - let filtered_txns = block_list_sender_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[2..].to_vec()); - } - - #[test] - fn test_entry_function_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let allow_list_entry_function_filter = TransactionFilter::new( - Filter::empty() - .add_allow_entry_function( - get_module_address(&txns[0]), - get_module_name(&txns[0]), - get_function_name(&txns[0]), - ) - .add_allow_entry_function( - get_module_address(&txns[1]), - get_module_name(&txns[1]), - get_function_name(&txns[1]), - ) - .add_deny_all(), - ); - let filtered_txns = allow_list_entry_function_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[0..2].to_vec()); - - let deny_list_entry_function_filter = TransactionFilter::new( - Filter::empty() - .add_deny_entry_function( - get_module_address(&txns[0]), - get_module_name(&txns[0]), - get_function_name(&txns[0]), - ) - .add_deny_entry_function( - get_module_address(&txns[1]), - get_module_name(&txns[1]), - get_function_name(&txns[1]), - ), - ); - let filtered_txns = deny_list_entry_function_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[2..].to_vec()); - } - - #[test] - fn test_allow_list_module_address_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let allow_list_module_address_filter = TransactionFilter::new( - Filter::empty() - .add_allow_module_address(get_module_address(&txns[0])) - .add_allow_module_address(get_module_address(&txns[1])) - .add_deny_all(), - ); - let filtered_txns = allow_list_module_address_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[0..4].to_vec()); - - let block_list_module_address_filter = TransactionFilter::new( - Filter::empty() - .add_deny_module_address(get_module_address(&txns[0])) - .add_deny_module_address(get_module_address(&txns[1])), - ); - let filtered_txns = block_list_module_address_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[4..].to_vec()); - } - - #[test] - fn test_composite_allow_list_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let filter = serde_yaml::from_str::(r#" - rules: - - Allow: - Sender: f8871acf2c827d40e23b71f6ff2b9accef8dbb17709b88bd9eb95e6bb748c25a - - Allow: - ModuleAddress: "0000000000000000000000000000000000000000000000000000000000000001" - - Allow: - EntryFunction: - - "0000000000000000000000000000000000000000000000000000000000000001" - - test - - check - - Allow: - EntryFunction: - - "0000000000000000000000000000000000000000000000000000000000000001" - - test - - new - - Deny: All - "#).unwrap(); - - let allow_list_filter = TransactionFilter::new(filter); - let filtered_txns = allow_list_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[0..4].to_vec()); - } - - #[test] - fn test_composite_block_list_filter() { - let txns = get_transactions(); - let block_id = HashValue::random(); - let filter = serde_yaml::from_str::(r#" - rules: - - Deny: - Sender: f8871acf2c827d40e23b71f6ff2b9accef8dbb17709b88bd9eb95e6bb748c25a - - Deny: - ModuleAddress: "0000000000000000000000000000000000000000000000000000000000000001" - - Deny: - EntryFunction: - - "0000000000000000000000000000000000000000000000000000000000000001" - - test - - check - - Deny: - EntryFunction: - - "0000000000000000000000000000000000000000000000000000000000000001" - - test - - new - "#).unwrap(); - - let allow_list_filter = TransactionFilter::new(filter); - let filtered_txns = allow_list_filter.filter(block_id, 0, txns.clone()); - assert_eq!(filtered_txns, txns[4..].to_vec()); - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs deleted file mode 100644 index 140191b1d523f..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; -use aptos_types::transaction::{SignedTransaction, TransactionPayload}; -use move_core_types::{identifier::Identifier, language_storage::ModuleId}; - -#[derive(Eq, Hash, PartialEq)] -pub enum EntryFunKey { - EntryFun { - module: ModuleId, - function: Identifier, - }, - Exempt, -} - -impl ConflictKey for EntryFunKey { - fn extract_from(txn: &SignedTransaction) -> Self { - match txn.payload() { - TransactionPayload::EntryFunction(entry_fun) => { - let module_id = entry_fun.module(); - if module_id.address().is_special() { - // Exempt framework modules - Self::Exempt - } else { - // n.b. Generics ignored. - Self::EntryFun { - module: module_id.clone(), - function: entry_fun.function().to_owned(), - } - } - }, - TransactionPayload::Multisig(_) - | TransactionPayload::Script(_) - | TransactionPayload::ModuleBundle(_) => Self::Exempt, - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::EntryFun { .. } => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs deleted file mode 100644 index 56979f98d6d29..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_key/entry_fun_module.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; -use aptos_types::transaction::{SignedTransaction, TransactionPayload}; -use move_core_types::language_storage::ModuleId; - -#[derive(Eq, Hash, PartialEq)] -pub enum EntryFunModuleKey { - Module(ModuleId), - AnyScriptOrMultiSig, - Exempt, -} - -impl ConflictKey for EntryFunModuleKey { - fn extract_from(txn: &SignedTransaction) -> Self { - match txn.payload() { - TransactionPayload::EntryFunction(entry_fun) => { - let module_id = entry_fun.module(); - - if module_id.address().is_special() { - Self::Exempt - } else { - Self::Module(module_id.clone()) - } - }, - TransactionPayload::Multisig(..) - | TransactionPayload::Script(_) - | TransactionPayload::ModuleBundle(_) => Self::AnyScriptOrMultiSig, - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::Module(..) | Self::AnyScriptOrMultiSig => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/mod.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/mod.rs deleted file mode 100644 index 63dc3d58cedee..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_key/mod.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::TxnIdx; -use std::{collections::HashMap, hash::Hash}; - -pub(crate) mod entry_fun; -pub(crate) mod entry_fun_module; -pub(crate) mod txn_sender; - -#[cfg(test)] -pub(crate) mod test_utils; - -/// `ConflictKey::extract_from(txn)` extracts a key from a transaction. For example, -/// `TxnSenderKey::extract_from(txn)` returns the transaction sender's address. The key is used by -/// the shuffler to determine whether two close by transactions conflict with each other. -/// -/// `ConflictKey::conflict_exempt(&key)` returns if this specific key is exempt from conflict. -/// For example, we can exempt transaction sender 0x1, so that consecutive transactions sent by -/// 0x1 are not seen as a conflict by the shuffler. -pub(crate) trait ConflictKey: Eq + Hash + PartialEq { - fn extract_from(txn: &Txn) -> Self; - - fn conflict_exempt(&self) -> bool; -} - -#[derive(Clone, Copy, Debug)] -pub(crate) struct ConflictKeyId(usize); - -impl ConflictKeyId { - pub fn as_idx(&self) -> usize { - self.0 - } -} - -/// `ConflictKeyRegistry::build::()` goes through a block of transactions and -/// extract the conflict keys from each transaction. In that process, each unique conflict key is -/// assigned a unique `ConflictKeyId`, essentially a sequence number, and the registry remembers which -/// key was extracted from each transaction. After that, we can query the registry to get the key -/// represented by the id, which is 1. cheaper than calling `ConflictKey::extract_from(txn)` again; -/// 2. enables vector based `MapByKeyId` which is cheaper than a `HashMap`; and 3. eliminates the typing -/// information and easier to use in the shuffler. -#[derive(Debug)] -pub(crate) struct ConflictKeyRegistry { - id_by_txn: Vec, - is_exempt_by_id: Vec, -} - -// Provided `ConflictKeyId`s managed by `ConflictKeyRegistry`s are consecutive integers starting -// from 0, a map can be implemented based on a vector, which is cheapter than a hash map. -#[derive(Debug, Eq, PartialEq)] -pub(crate) struct MapByKeyId { - inner: Vec, -} - -impl MapByKeyId { - pub fn new(size: usize) -> Self { - let mut inner = Vec::with_capacity(size); - inner.resize_with(size, Default::default); - - Self { inner } - } - - pub fn get(&self, key_id: ConflictKeyId) -> &T { - &self.inner[key_id.as_idx()] - } - - pub fn get_mut(&mut self, key_id: ConflictKeyId) -> &mut T { - &mut self.inner[key_id.as_idx()] - } -} - -impl ConflictKeyRegistry { - pub fn build, Txn>(txns: &[Txn]) -> Self - where - K: ConflictKey, - { - let mut registry = HashMap::::new(); - let mut is_exempt_by_id = Vec::new(); - - let id_by_txn = txns - .iter() - .map(|txn| { - let key = K::extract_from(txn); - *registry.entry(key).or_insert_with_key(|key| { - is_exempt_by_id.push(key.conflict_exempt()); - ConflictKeyId(is_exempt_by_id.len() - 1) - }) - }) - .collect(); - - Self { - id_by_txn, - is_exempt_by_id, - } - } - - fn num_ids(&self) -> usize { - self.is_exempt_by_id.len() - } - - pub fn num_txns(&self) -> usize { - self.id_by_txn.len() - } - - pub fn new_map_by_id(&self) -> MapByKeyId { - MapByKeyId::new(self.num_ids()) - } - - pub fn key_id_for_txn(&self, txn_idx: TxnIdx) -> ConflictKeyId { - self.id_by_txn[txn_idx] - } - - pub fn is_conflict_exempt(&self, key_id: ConflictKeyId) -> bool { - self.is_exempt_by_id[key_id.0] - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs deleted file mode 100644 index f9b9e9e72fd22..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_key/test_utils.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::conflict_key::{ - ConflictKey, ConflictKeyId, ConflictKeyRegistry, -}; -use proptest::prelude::*; -use std::hash::Hash; - -impl ConflictKeyId { - pub fn new_for_test(idx: usize) -> Self { - Self(idx) - } -} - -impl ConflictKeyRegistry { - pub fn all_exempt(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], - is_exempt_by_id: vec![true], - } - } - - pub fn non_conflict(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: (0..num_txns).map(ConflictKeyId::new_for_test).collect(), - is_exempt_by_id: vec![false; num_txns], - } - } - - pub fn full_conflict(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], - is_exempt_by_id: vec![false], - } - } - - pub fn nums_per_key(nums_per_key: [usize; NUM_KEYS]) -> Self { - Self::nums_per_round_per_key([nums_per_key]) - } - - pub fn nums_per_round_per_key( - nums_per_round_per_key: [[usize; NUM_KEYS]; NUM_ROUNDS], - ) -> Self { - let mut seq = (0..NUM_ROUNDS).flat_map(|_| 0..NUM_KEYS); - let nums_per_key = nums_per_round_per_key.into_iter().flatten(); - - ConflictKeyRegistry { - id_by_txn: nums_per_key - .flat_map(|num| { - let s = seq.next().unwrap(); - vec![ConflictKeyId::new_for_test(s); num] - }) - .collect(), - is_exempt_by_id: vec![false; NUM_KEYS], - } - } -} - -#[derive(Debug)] -struct FakeAccount { - id: usize, -} - -impl Arbitrary for FakeAccount { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize).prop_map(|id| FakeAccount { id }).boxed() - } -} - -#[derive(Debug)] -struct FakeModule { - id: usize, -} - -impl FakeModule { - pub fn exempt(&self) -> bool { - self.id % 3 == 0 - } -} - -impl Arbitrary for FakeModule { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize).prop_map(|id| FakeModule { id }).boxed() - } -} - -#[derive(Debug)] -struct FakeEntryFun { - module: FakeModule, - id: usize, -} - -impl Arbitrary for FakeEntryFun { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), 0..3usize) - .prop_map(|(module, id)| FakeEntryFun { module, id }) - .boxed() - } -} - -#[derive(Debug)] -pub struct FakeTxn { - sender: FakeAccount, - entry_fun: FakeEntryFun, -} - -impl Arbitrary for FakeTxn { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), any::()) - .prop_map(|(sender, entry_fun)| FakeTxn { sender, entry_fun }) - .boxed() - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) struct FakeSenderKey { - id: usize, -} - -impl ConflictKey for FakeSenderKey { - fn extract_from(txn: &FakeTxn) -> Self { - Self { id: txn.sender.id } - } - - fn conflict_exempt(&self) -> bool { - false - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) enum FakeEntryFunModuleKey { - Module(usize), - Exempt, -} - -impl ConflictKey for FakeEntryFunModuleKey { - fn extract_from(txn: &FakeTxn) -> Self { - if txn.entry_fun.module.exempt() { - Self::Exempt - } else { - Self::Module(txn.entry_fun.module.id) - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::Module(..) => false, - } - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) enum FakeEntryFunKey { - EntryFun { module: usize, function: usize }, - Exempt, -} - -impl ConflictKey for FakeEntryFunKey { - fn extract_from(txn: &FakeTxn) -> Self { - if txn.entry_fun.module.exempt() { - Self::Exempt - } else { - Self::EntryFun { - module: txn.entry_fun.module.id, - function: txn.entry_fun.id, - } - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::EntryFun { .. } => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs b/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs deleted file mode 100644 index 3ec1905d869cb..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_key/txn_sender.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::conflict_key::ConflictKey; -use aptos_types::transaction::SignedTransaction; -use move_core_types::account_address::AccountAddress; - -#[derive(Eq, Hash, PartialEq)] -pub struct TxnSenderKey(AccountAddress); - -impl ConflictKey for TxnSenderKey { - fn extract_from(txn: &SignedTransaction) -> Self { - TxnSenderKey(txn.sender()) - } - - fn conflict_exempt(&self) -> bool { - false - } -} diff --git a/consensus/src/transaction_shuffler/fairness/conflict_zone.rs b/consensus/src/transaction_shuffler/fairness/conflict_zone.rs deleted file mode 100644 index 983baaadedfb9..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/conflict_zone.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::{ - conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, - TxnIdx, -}; -use std::collections::VecDeque; - -/// A sliding window of transactions (TxnIds), represented by `ConflictKeyId`s extracted from a -/// specific `ConflictKey`, managed by a specific `ConflictKeyRegistry`. -#[derive(Debug)] -pub(crate) struct ConflictZone<'a> { - sliding_window_size: usize, - sliding_window: VecDeque, - /// Number of transactions in the sliding window for each key_id. `ConflictZone::is_conflict(key)` - /// returns true is the count for `key` is greater than 0, unless the key is exempt from conflict. - counts_by_id: MapByKeyId, - key_registry: &'a ConflictKeyRegistry, -} - -impl<'a> ConflictZone<'a> { - pub fn build_zones( - key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - window_sizes: [usize; NUM_CONFLICT_ZONES], - ) -> [Self; NUM_CONFLICT_ZONES] { - itertools::zip_eq(key_registries.iter(), window_sizes) - .map(|(registry, window_size)| Self::new(registry, window_size)) - .collect::>() - .try_into() - .expect("key_registries and window_sizes must have the same length.") - } - - fn new(key_registry: &'a ConflictKeyRegistry, sliding_window_size: usize) -> Self { - Self { - sliding_window_size, - sliding_window: VecDeque::with_capacity(sliding_window_size + 1), - counts_by_id: key_registry.new_map_by_id(), - key_registry, - } - } - - pub fn is_conflict(&self, txn_idx: TxnIdx) -> bool { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if self.key_registry.is_conflict_exempt(key_id) { - false - } else { - *self.counts_by_id.get(key_id) > 0 - } - } - - /// Append a new transaction to the sliding window and - /// return the key_id that's no longer in conflict as a result if there is one. - pub fn add(&mut self, txn_idx: TxnIdx) -> Option { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - - *self.counts_by_id.get_mut(key_id) += 1; - self.sliding_window.push_back(key_id); - if self.sliding_window.len() > self.sliding_window_size { - let removed_key_id = self.sliding_window.pop_front().unwrap(); - let count = self.counts_by_id.get_mut(removed_key_id); - *count -= 1; - if *count == 0 && !self.key_registry.is_conflict_exempt(removed_key_id) { - return Some(removed_key_id); - } - } - None - } -} diff --git a/consensus/src/transaction_shuffler/fairness/mod.rs b/consensus/src/transaction_shuffler/fairness/mod.rs deleted file mode 100644 index fef6a42a67306..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::{ - fairness::{ - conflict_key::{ - entry_fun::EntryFunKey, entry_fun_module::EntryFunModuleKey, txn_sender::TxnSenderKey, - ConflictKeyRegistry, - }, - conflict_zone::ConflictZone, - pending_zone::PendingZone, - }, - TransactionShuffler, -}; -use aptos_types::transaction::SignedTransaction; -use itertools::zip_eq; -use selection_tracker::SelectionTracker; -use std::collections::BTreeSet; - -pub(crate) mod conflict_key; -mod conflict_zone; -mod pending_zone; -mod selection_tracker; - -#[cfg(test)] -mod tests; - -type TxnIdx = usize; - -#[derive(Debug)] -pub struct FairnessShuffler { - pub sender_conflict_window_size: usize, - pub module_conflict_window_size: usize, - pub entry_fun_conflict_window_size: usize, -} - -impl FairnessShuffler { - fn conflict_key_registries(txns: &[SignedTransaction]) -> [ConflictKeyRegistry; 3] { - [ - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ] - } - - fn window_sizes(&self) -> [usize; 3] { - [ - self.sender_conflict_window_size, - self.module_conflict_window_size, - self.entry_fun_conflict_window_size, - ] - } -} - -impl TransactionShuffler for FairnessShuffler { - fn shuffle(&self, txns: Vec) -> Vec { - let conflict_key_registries = Self::conflict_key_registries(&txns); - let order = - FairnessShufflerImpl::new(&conflict_key_registries, self.window_sizes()).shuffle(); - reorder(txns, &order) - } -} - -fn reorder(txns: Vec, order: &[TxnIdx]) -> Vec { - assert_eq!(txns.len(), order.len()); - order.iter().map(|idx| txns[*idx].clone()).collect() -} - -struct FairnessShufflerImpl<'a, const NUM_CONFLICT_ZONES: usize> { - conflict_zones: [ConflictZone<'a>; NUM_CONFLICT_ZONES], - pending_zones: [PendingZone<'a>; NUM_CONFLICT_ZONES], - selected_order: Vec, - selection_tracker: SelectionTracker, -} - -impl<'a, const NUM_CONFLICT_ZONES: usize> FairnessShufflerImpl<'a, NUM_CONFLICT_ZONES> { - pub fn new( - conflict_key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - window_sizes: [usize; NUM_CONFLICT_ZONES], - ) -> Self { - let num_txns = conflict_key_registries[0].num_txns(); - assert!(conflict_key_registries - .iter() - .skip(1) - .all(|r| r.num_txns() == num_txns)); - - Self { - selected_order: Vec::with_capacity(num_txns), - selection_tracker: SelectionTracker::new(num_txns), - conflict_zones: ConflictZone::build_zones(conflict_key_registries, window_sizes), - pending_zones: PendingZone::build_zones(conflict_key_registries), - } - } - - /// Spread (delay) transactions that have conflicts with adjacent previous transactions - /// according to multiple dimensions (`ConflictKey`s). Invariant is held that for each conflict - /// key, i.e. the transaction sender, the module, and the entry function, etc., the order of - /// transactions with the same key is preserved -- unless the key is exempt from conflict. - /// - /// For example, all transactions from a single sender will preserve their order; all transactions - /// from the same module will preserve their order, unless they are of the aptos framework - /// module -- p2p transfers of APT can violate this invariant. - /// - /// Each transaction comes at most once out of `self.selection_tracker.next_unselected()` for - /// both passes, that's O(2n). And each transaction comes out of each conflict zones at most - /// once, that's O(3n). In either case, the transaction is examined by going through all 3 - /// conflict zones and all 3 pending zones. So the time complexity is O(9n) = O(n). Or if we - /// consider `NUM_CONFLICT_ZONES = m`, the time complexity is O(m*m*n). - pub fn shuffle(mut self) -> Vec { - // First pass, only select transactions with no conflicts in all conflict zones - while let Some(txn_idx) = self.selection_tracker.next_unselected() { - if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { - self.select_and_select_unconflicted(txn_idx, false /* is_pending */) - } else { - self.add_pending(txn_idx); - } - } - - // Second pass, select previously pending txns in order, - // with newly un-conflicted txns jumping the line - self.selection_tracker.new_pass(); - while let Some(txn_idx) = self.selection_tracker.next_unselected() { - self.select_and_select_unconflicted(txn_idx, true /* is_pending */); - } - - self.selected_order - } - - fn select_and_select_unconflicted(&mut self, txn_idx: TxnIdx, is_pending: bool) { - let mut maybe_unconflicted = self.select(txn_idx, is_pending); - while let Some(txn_idx) = maybe_unconflicted.pop_first() { - if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { - maybe_unconflicted.extend(self.select(txn_idx, true /* is_pending */)) - } - } - } - - /// Select a transaction and return potentially un-conflicted transactions - fn select(&mut self, txn_idx: TxnIdx, is_pending: bool) -> BTreeSet { - self.selection_tracker.mark_selected(txn_idx); - self.selected_order.push(txn_idx); - if is_pending { - self.pop_pending(txn_idx); - } - - let mut maybe_unconflicted = BTreeSet::new(); - for (conflict_zone, pending_zone) in - zip_eq(&mut self.conflict_zones, &mut self.pending_zones) - { - if let Some(key_id) = conflict_zone.add(txn_idx) { - if let Some(pending) = pending_zone.first_pending_on_key(key_id) { - maybe_unconflicted.insert(pending); - } - } - } - - maybe_unconflicted - } - - fn is_conflict(&self, txn_idx: TxnIdx) -> bool { - self.conflict_zones.iter().any(|z| z.is_conflict(txn_idx)) - } - - fn is_head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { - self.pending_zones - .iter() - .any(|z| z.head_of_line_blocked(txn_idx)) - } - - fn add_pending(&mut self, txn_idx: TxnIdx) { - self.pending_zones.iter_mut().for_each(|z| z.add(txn_idx)); - } - - fn pop_pending(&mut self, txn_idx: TxnIdx) { - self.pending_zones.iter_mut().for_each(|z| z.pop(txn_idx)); - } -} - -#[cfg(test)] -mod test_utils { - use crate::transaction_shuffler::fairness::FairnessShuffler; - use proptest::prelude::*; - - impl FairnessShuffler { - pub fn new_for_test( - sender_conflict_window_size: usize, - module_conflict_window_size: usize, - entry_fun_conflict_window_size: usize, - ) -> Self { - Self { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } - } - } - - impl Arbitrary for FairnessShuffler { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize, 0..10usize, 0..10usize) - .prop_map( - |( - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - )| { - FairnessShuffler { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } - }, - ) - .boxed() - } - } -} diff --git a/consensus/src/transaction_shuffler/fairness/pending_zone.rs b/consensus/src/transaction_shuffler/fairness/pending_zone.rs deleted file mode 100644 index 8de932b65e601..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/pending_zone.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::{ - conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, - TxnIdx, -}; -use std::collections::VecDeque; - -/// A queue for each confclit Key, represented by `ConflictKeyId`s managed by `ConflictKeyRegistry`. -#[derive(Debug)] -pub(crate) struct PendingZone<'a> { - key_registry: &'a ConflictKeyRegistry, - pending_by_key: MapByKeyId>, -} - -impl<'a> PendingZone<'a> { - pub fn build_zones( - key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - ) -> [Self; NUM_CONFLICT_ZONES] { - key_registries - .iter() - .map(Self::new) - .collect::>() - .try_into() - .expect("key_registries and the return type must have the same length.") - } - - fn new(key_registry: &'a ConflictKeyRegistry) -> Self { - Self { - key_registry, - pending_by_key: key_registry.new_map_by_id(), - } - } - - pub fn add(&mut self, txn_idx: TxnIdx) { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if !self.key_registry.is_conflict_exempt(key_id) { - self.pending_by_key.get_mut(key_id).push_back(txn_idx); - } - } - - pub fn pop(&mut self, txn_idx: TxnIdx) { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if !self.key_registry.is_conflict_exempt(key_id) { - let popped = self - .pending_by_key - .get_mut(key_id) - .pop_front() - .expect("Must exist"); - assert_eq!(popped, txn_idx); - } - } - - pub fn head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if self.key_registry.is_conflict_exempt(key_id) { - false - } else { - match self.pending_by_key.get(key_id).front() { - Some(front) => *front < txn_idx, - None => false, - } - } - } - - pub fn first_pending_on_key(&self, key_id: ConflictKeyId) -> Option { - self.pending_by_key.get(key_id).front().cloned() - } -} diff --git a/consensus/src/transaction_shuffler/fairness/selection_tracker.rs b/consensus/src/transaction_shuffler/fairness/selection_tracker.rs deleted file mode 100644 index 571f817656ab8..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/selection_tracker.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::TxnIdx; - -pub struct SelectionTracker { - selected_markers: Vec, - cur_idx: usize, -} - -impl SelectionTracker { - pub fn new(num_txns: usize) -> Self { - Self { - selected_markers: vec![false; num_txns], - cur_idx: 0, - } - } - - pub fn next_unselected(&mut self) -> Option { - while self.cur_idx < self.selected_markers.len() { - let idx = self.cur_idx; - self.cur_idx += 1; - - if !self.is_selected(idx) { - return Some(idx); - } - } - None - } - - pub fn new_pass(&mut self) { - self.cur_idx = 0 - } - - pub fn mark_selected(&mut self, idx: TxnIdx) { - assert!(!self.selected_markers[idx]); - self.selected_markers[idx] = true; - } - - fn is_selected(&self, idx: TxnIdx) -> bool { - self.selected_markers[idx] - } -} diff --git a/consensus/src/transaction_shuffler/fairness/tests/manual.rs b/consensus/src/transaction_shuffler/fairness/tests/manual.rs deleted file mode 100644 index 69cd94c141216..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/tests/manual.rs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::{ - conflict_key::ConflictKeyRegistry, FairnessShuffler, FairnessShufflerImpl, -}; - -struct TestCase { - shuffler: FairnessShuffler, - conflict_key_registries: [ConflictKeyRegistry; 3], - expected_order: Vec, -} - -impl TestCase { - fn run(self) { - let Self { - shuffler, - conflict_key_registries, - expected_order, - } = self; - - let order = - FairnessShufflerImpl::new(&conflict_key_registries, shuffler.window_sizes()).shuffle(); - assert_eq!(order, expected_order); - } -} - -#[test] -fn test_all_exempt() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::all_exempt(9), - ConflictKeyRegistry::all_exempt(9), - ConflictKeyRegistry::all_exempt(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_non_conflict() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(9), - ConflictKeyRegistry::non_conflict(9), - ConflictKeyRegistry::non_conflict(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_full_conflict() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::full_conflict(9), - ConflictKeyRegistry::full_conflict(9), - ConflictKeyRegistry::full_conflict(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_modules_ignored_by_window_size() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 0, 2), - conflict_key_registries: [ - // [A0, A1, A2, ...] - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M0, M1, M1, M2, M2] - ConflictKeyRegistry::nums_per_key([4, 2, 2]), - // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M2::E0, M2::E0] - ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0], [1, 1, 4]]), - ], - // [M0::E0, M0::E1, M1::E0, M0::E0, M0::E1, M1::E0, M2::E0, M2::E0] - expected_order: vec![0, 1, 4, 2, 3, 5, 6, 7], - } - .run() -} - -#[test] -fn test_modules_and_entry_funs_ignored_by_window_size() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 0, 0), - conflict_key_registries: [ - // [A0, A1, A2, ...] - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M0, M1, M1, M1, M1] - ConflictKeyRegistry::nums_per_key([4, 4]), - // [M0::E0, M0::E0, M0::E1, M0::E1, M1::E0, M1::E0, M1::E1, M1::E1] - ConflictKeyRegistry::nums_per_key([2, 2, 2, 2]), - ], - expected_order: (0..8).collect(), - } - .run() -} - -#[test] -fn test_exempted_modules() { - // think "full block of p2p txns" - TestCase { - shuffler: FairnessShuffler::new_for_test(3, 2, 2), - conflict_key_registries: [ - // [0:A0, 1:A0, 2:A0, 3:A0, 4:A1, 5:A1, 6:A1, 7:A2, 8:A2, 9:A3] - ConflictKeyRegistry::nums_per_key([4, 3, 2, 1]), - ConflictKeyRegistry::all_exempt(10), - ConflictKeyRegistry::all_exempt(10), - ], - // [A0, A1, A2, A3, A0, A1, A2, A0, A1] - expected_order: vec![0, 4, 7, 9, 1, 5, 8, 2, 3, 6], - } - .run() -} - -#[test] -fn test_dominating_module() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 1), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(7), - // [M0, M0, M0, M1, M2, M3, M4] - ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), - ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), - ], - // [M0, M1, M0, M2, M0, M3, M4] - expected_order: vec![0, 3, 1, 4, 2, 5, 6], - } - .run() -} - -#[test] -fn test_dominating_module2() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 1), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M1, M2, M3, M4, M0] - ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), - ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), - ], - // [M0, M1, M0, M2, M0, M3, M4, M0] - expected_order: vec![0, 3, 1, 4, 2, 5, 6, 7], - } - .run() -} - -#[test] -fn test_multiple_entry_funs() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 2), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(10), - // [M0, M0, M0, M0, M1, M1, M1, M1, M2, M2] - ConflictKeyRegistry::nums_per_key([4, 4, 2]), - // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M1::E0, M1::E0, M2::E0, M2::E0] - ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0, 0], [1, 1, 4, 2]]), - ], - // [M0::E0, M1::E0, M0::E1, M2::E0, M0::E0, M1::E0, M0:E1, M2::E0, M1::E0, M1::E0] - expected_order: vec![0, 4, 1, 8, 2, 5, 3, 9, 6, 7], - } - .run() -} diff --git a/consensus/src/transaction_shuffler/fairness/tests/mod.rs b/consensus/src/transaction_shuffler/fairness/tests/mod.rs deleted file mode 100644 index c3550a41f7ccc..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod manual; -mod proptests; diff --git a/consensus/src/transaction_shuffler/fairness/tests/proptests.rs b/consensus/src/transaction_shuffler/fairness/tests/proptests.rs deleted file mode 100644 index 0195997fbf768..0000000000000 --- a/consensus/src/transaction_shuffler/fairness/tests/proptests.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::fairness::{ - conflict_key::{ - test_utils::{FakeEntryFunKey, FakeEntryFunModuleKey, FakeSenderKey, FakeTxn}, - ConflictKeyRegistry, MapByKeyId, - }, - reorder, FairnessShuffler, FairnessShufflerImpl, TxnIdx, -}; -use proptest::{collection::vec, prelude::*}; -use std::collections::BTreeSet; - -fn arb_order(num_txns: usize) -> impl Strategy> { - Just((0..num_txns).collect::>()).prop_shuffle() -} - -#[derive(Debug, Default, Eq, PartialEq)] -enum OrderOrSet { - #[default] - Empty, - Order(Vec), - Set(BTreeSet), -} - -impl OrderOrSet { - fn add(&mut self, idx: TxnIdx, is_conflict_exempt: bool) { - if self.is_empty() { - *self = if is_conflict_exempt { - Self::Set(BTreeSet::new()) - } else { - Self::Order(Vec::new()) - }; - } - - match self { - Self::Order(order) => order.push(idx), - Self::Set(set) => { - set.insert(idx); - }, - Self::Empty => unreachable!(), - } - } - - fn is_empty(&self) -> bool { - matches!(self, Self::Empty) - } -} - -fn sort_by_key( - order: impl IntoIterator, - registry: &ConflictKeyRegistry, -) -> MapByKeyId { - let mut map: MapByKeyId = registry.new_map_by_id(); - - for txn_idx in order { - let key_id = registry.key_id_for_txn(txn_idx); - let is_exempt = registry.is_conflict_exempt(key_id); - - map.get_mut(key_id).add(txn_idx, is_exempt); - } - - map -} - -fn assert_invariants(txns: &[FakeTxn], order: Vec, registry: &ConflictKeyRegistry) { - let num_txns = txns.len(); - let original_sorted = sort_by_key(0..num_txns, registry); - let result_sorted = sort_by_key(order, registry); - - assert_eq!(result_sorted, original_sorted); -} - -fn registries(txns: &[FakeTxn]) -> [ConflictKeyRegistry; 3] { - [ - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ] -} - -proptest! { - #[test] - fn test_reorder( order in (0..1000usize).prop_flat_map(arb_order) ) { - let num_txns = order.len(); - let txns = (0..num_txns).collect::>(); - - let reordered = reorder(txns, &order); - prop_assert_eq!(reordered, order); - } - - #[test] - fn test_fairness_shuffler( - txns in vec(any::(), 0..1000), - shuffler in any::(), - ) { - let registries = registries(&txns); - let order = FairnessShufflerImpl::new(®istries, shuffler.window_sizes()).shuffle(); - - for registry in ®istries { - assert_invariants(&txns, order.clone(), registry); - } - } -} diff --git a/consensus/src/transaction_shuffler/mod.rs b/consensus/src/transaction_shuffler/mod.rs deleted file mode 100644 index 5cc43f2a2e8da..0000000000000 --- a/consensus/src/transaction_shuffler/mod.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_logger::info; -use aptos_types::{ - on_chain_config::{ - TransactionShufflerType, - TransactionShufflerType::{DeprecatedSenderAwareV1, NoShuffling, SenderAwareV2}, - }, - transaction::SignedTransaction, -}; -use sender_aware::SenderAwareShuffler; -use std::sync::Arc; - -mod fairness; -mod sender_aware; - -/// Interface to shuffle transactions -pub trait TransactionShuffler: Send + Sync { - fn shuffle(&self, txns: Vec) -> Vec; -} - -/// No Op Shuffler to maintain backward compatibility -pub struct NoOpShuffler {} - -impl TransactionShuffler for NoOpShuffler { - fn shuffle(&self, txns: Vec) -> Vec { - txns - } -} - -pub fn create_transaction_shuffler( - shuffler_type: TransactionShufflerType, -) -> Arc { - match shuffler_type { - NoShuffling => { - info!("Using no-op transaction shuffling"); - Arc::new(NoOpShuffler {}) - }, - DeprecatedSenderAwareV1(_) => { - info!("Using no-op sender aware shuffling v1"); - Arc::new(NoOpShuffler {}) - }, - SenderAwareV2(conflict_window_size) => { - info!( - "Using sender aware transaction shuffling with conflict window size {}", - conflict_window_size - ); - Arc::new(SenderAwareShuffler::new(conflict_window_size as usize)) - }, - TransactionShufflerType::Fairness { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } => { - info!( - "Using fairness transaction shuffling with conflict window sizes: sender {}, module {}, entry fun {}", - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size - ); - Arc::new(fairness::FairnessShuffler { - sender_conflict_window_size: sender_conflict_window_size as usize, - module_conflict_window_size: module_conflict_window_size as usize, - entry_fun_conflict_window_size: entry_fun_conflict_window_size as usize, - }) - }, - } -} diff --git a/consensus/src/transaction_shuffler/sender_aware.rs b/consensus/src/transaction_shuffler/sender_aware.rs deleted file mode 100644 index 936a9f7e9d0a5..0000000000000 --- a/consensus/src/transaction_shuffler/sender_aware.rs +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{counters::NUM_SENDERS_IN_BLOCK, transaction_shuffler::TransactionShuffler}; -use aptos_types::transaction::SignedTransaction; -use move_core_types::account_address::AccountAddress; -use std::collections::{HashMap, VecDeque}; - -/// An implementation of transaction shuffler, which tries to spread transactions from same senders -/// in a block in order to reduce conflict. On a high level, it works as follows - It defines a -/// `conflict_window_size`, which maintains a set of senders added to the block in last `conflict_window_size` -/// transactions. When trying to select a new transaction to the block, the shuffler tries to find -/// a transaction which are not part of the conflicting senders in the window. If it does, it adds -/// the first non-conflicting transaction it finds to the block, if it doesn't then it preserves the -/// order and adds the first transaction in the remaining block. It always maintains the following -/// invariant in terms of ordering -/// 1. Relative ordering of all transactions from the same before and after shuffling is same -/// 2. Relative ordering of all transactions across different senders will also be maintained if they are -/// non-conflicting. In other words, if the input block has only one transaction per sender, the output -/// ordering will remain unchanged. -/// -/// The shuffling algorithm is O(n) and following is the pseudo code for it. -/// loop: -/// if a sender fell out of the sliding window in previous iteration, -/// then: we add the first pending transaction from that sender to the block -/// else while we have transactions to process in the original transaction order -/// take a new one, -/// if it conflicts, add to the pending set -/// else we add it to the block -/// else -/// take the first transaction from the pending transactions and add it to the block - -pub struct SenderAwareShuffler { - conflict_window_size: usize, -} - -impl TransactionShuffler for SenderAwareShuffler { - fn shuffle(&self, txns: Vec) -> Vec { - // Early return for performance reason if there are no transactions to shuffle - if txns.is_empty() { - return txns; - } - - // handle the corner case of conflict window being 0, in which case we don't do any shuffling - if self.conflict_window_size == 0 { - return txns; - } - - // maintains the intermediate state of the shuffled transactions - let mut sliding_window = SlidingWindowState::new(self.conflict_window_size, txns.len()); - let mut pending_txns = PendingTransactions::new(); - let num_transactions = txns.len(); - let mut orig_txns = VecDeque::from(txns); - let mut next_to_add = |sliding_window: &mut SlidingWindowState| -> SignedTransaction { - // First check if we have a sender dropped off of conflict window in previous step, if so, - // we try to find pending transaction from the corresponding sender and add it to the block. - if let Some(sender) = sliding_window.last_dropped_sender() { - if let Some(txn) = pending_txns.remove_pending_from_sender(sender) { - return txn; - } - } - // If we can't find any transaction from a sender dropped off of conflict window, then - // iterate through the original transactions and try to find the next candidate - while let Some(txn) = orig_txns.pop_front() { - if !sliding_window.has_conflict(&txn.sender()) { - return txn; - } - pending_txns.add_transaction(txn); - } - - // If we can't find any candidate in above steps, then lastly - // add pending transactions in the order if we can't find any other candidate - pending_txns.remove_first_pending().unwrap() - }; - while sliding_window.num_txns() < num_transactions { - let txn = next_to_add(&mut sliding_window); - sliding_window.add_transaction(txn) - } - sliding_window.finalize() - } -} - -impl SenderAwareShuffler { - pub fn new(conflict_window_size: usize) -> Self { - Self { - conflict_window_size, - } - } -} - -/// A structure to maintain a set of transactions that are pending to be added to the block indexed by -/// the sender. For a particular sender, relative ordering of transactions are maintained, -/// so that the final block preserves the ordering of transactions by sender. It also maintains a vector -/// to preserve the original order of the transactions. This is needed in case we can't find -/// any non-conflicting transactions and we need to add the first pending transaction to the block. -struct PendingTransactions { - txns_by_senders: HashMap>, - // Transactions are kept in the original order. This is not kept in sync with pending transactions, - // so this can contain a bunch of transactions that are already added to the block. - ordered_txns: VecDeque, -} - -impl PendingTransactions { - pub fn new() -> Self { - Self { - txns_by_senders: HashMap::new(), - ordered_txns: VecDeque::new(), - } - } - - pub fn add_transaction(&mut self, txn: SignedTransaction) { - self.ordered_txns.push_back(txn.clone()); - self.txns_by_senders - .entry(txn.sender()) - .or_default() - .push_back(txn); - } - - /// Removes the first pending transaction from the sender. Please note that the transaction is not - /// removed from the `ordered_txns`, so the `ordered_txns` will contain a set of transactions that - /// are removed from pending transactions already. - pub fn remove_pending_from_sender( - &mut self, - sender: AccountAddress, - ) -> Option { - self.txns_by_senders - .get_mut(&sender) - .and_then(|txns| txns.pop_front()) - } - - pub fn remove_first_pending(&mut self) -> Option { - while let Some(txn) = self.ordered_txns.pop_front() { - let sender = txn.sender(); - // We don't remove the txns from ordered_txns when remove_pending_from_sender is called. - // So it is possible that the ordered_txns has some transactions that are not pending - // anymore. - if Some(txn).as_ref() == self.txns_by_senders.get(&sender).unwrap().front() { - return self.remove_pending_from_sender(sender); - } - } - None - } -} - -/// A stateful data structure maintained by the transaction shuffler during shuffling. On a -/// high level, it maintains a sliding window of the conflicting transactions, which helps the payload -/// generator include a set of transactions which are non-conflicting with each other within a particular -/// window size. -struct SlidingWindowState { - // Please note that the start index can be negative in case the window size is larger than the - // end_index. - start_index: i64, - // Hashmap of senders to the number of transactions included in the window for the corresponding - // sender. - senders_in_window: HashMap, - // Partially ordered transactions, needs to be updated every time add_transactions is called. - txns: Vec, -} - -impl SlidingWindowState { - pub fn new(window_size: usize, num_txns: usize) -> Self { - Self { - start_index: -(window_size as i64), - senders_in_window: HashMap::new(), - txns: Vec::with_capacity(num_txns), - } - } - - /// Slides the current window. Essentially, it increments the start_index and - /// updates the senders_in_window map if start_index is greater than 0 - pub fn add_transaction(&mut self, txn: SignedTransaction) { - if self.start_index >= 0 { - // if the start_index is negative, then no sender falls out of the window. - let sender = self - .txns - .get(self.start_index as usize) - .expect("Transaction expected") - .sender(); - self.senders_in_window - .entry(sender) - .and_modify(|count| *count -= 1); - } - let count = self - .senders_in_window - .entry(txn.sender()) - .or_insert_with(|| 0); - *count += 1; - self.txns.push(txn); - self.start_index += 1; - } - - pub fn has_conflict(&self, addr: &AccountAddress) -> bool { - self.senders_in_window - .get(addr) - .map_or(false, |count| *count != 0) - } - - /// Returns the sender which was dropped off of the conflict window in previous iteration. - pub fn last_dropped_sender(&self) -> Option { - let prev_start_index = self.start_index - 1; - if prev_start_index >= 0 { - let last_sender = self.txns.get(prev_start_index as usize).unwrap().sender(); - if *self.senders_in_window.get(&last_sender).unwrap() == 0 { - return Some(last_sender); - } - } - None - } - - pub fn num_txns(&self) -> usize { - self.txns.len() - } - - pub fn finalize(self) -> Vec { - NUM_SENDERS_IN_BLOCK.set(self.senders_in_window.len() as f64); - self.txns - } -} - -#[cfg(test)] -mod tests { - use crate::transaction_shuffler::{sender_aware::SenderAwareShuffler, TransactionShuffler}; - use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, SigningKey, Uniform}; - use aptos_types::{ - chain_id::ChainId, - transaction::{RawTransaction, Script, SignedTransaction, TransactionPayload}, - }; - use move_core_types::account_address::AccountAddress; - use rand::{rngs::OsRng, Rng}; - use std::{ - collections::{HashMap, HashSet}, - time::Instant, - }; - - fn create_signed_transaction(num_transactions: usize) -> Vec { - let private_key = Ed25519PrivateKey::generate_for_testing(); - let public_key = private_key.public_key(); - let sender = AccountAddress::random(); - - let mut transactions = Vec::new(); - - for i in 0..num_transactions { - let transaction_payload = - TransactionPayload::Script(Script::new(vec![], vec![], vec![])); - let raw_transaction = RawTransaction::new( - sender, - i as u64, - transaction_payload, - 0, - 0, - 0, - ChainId::new(10), - ); - let signed_transaction = SignedTransaction::new( - raw_transaction.clone(), - public_key.clone(), - private_key.sign(&raw_transaction).unwrap(), - ); - transactions.push(signed_transaction) - } - transactions - } - - #[test] - fn test_single_user_txns() { - for num_txns in [1, 5, 50, 500] { - let txns = create_signed_transaction(num_txns); - let txn_shuffer = SenderAwareShuffler::new(10); - let optimized_txns = txn_shuffer.shuffle(txns.clone()); - assert_eq!(txns.len(), optimized_txns.len()); - // Assert that ordering is unchanged in case of single sender block - assert_eq!(txns, optimized_txns) - } - } - - #[test] - fn test_unique_sender_txns() { - for num_senders in [1, 5, 50, 500] { - let mut txns = Vec::new(); - let mut senders = Vec::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(1); - senders.push(sender_txns.first().unwrap().sender()); - txns.append(&mut sender_txns); - } - let txn_shuffer = SenderAwareShuffler::new(10); - let optimized_txns = txn_shuffer.shuffle(txns.clone()); - assert_eq!(txns.len(), optimized_txns.len()); - // Assert that the ordering is unchanged in case of unique senders txns. - assert_eq!(txns, optimized_txns) - } - } - - #[test] - fn test_perfect_shuffling() { - let num_senders = 50; - let mut txns = Vec::new(); - let mut senders = Vec::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(10); - senders.push(sender_txns.first().unwrap().sender()); - txns.append(&mut sender_txns); - } - - let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); - let optimized_txns = txn_shuffler.shuffle(txns.clone()); - assert_eq!(txns.len(), optimized_txns.len()); - let mut sender_index = 0; - for txn in optimized_txns { - assert_eq!(&txn.sender(), senders.get(sender_index).unwrap()); - sender_index = (sender_index + 1) % senders.len() - } - } - - #[test] - fn test_shuffling_benchmark() { - let num_senders = 200; - let mut txns = Vec::new(); - let mut senders = Vec::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(10); - senders.push(sender_txns.first().unwrap().sender()); - txns.append(&mut sender_txns); - } - - let now = Instant::now(); - let txn_shuffler = SenderAwareShuffler::new(32); - let optimized_txns = txn_shuffler.shuffle(txns.clone()); - println!("elapsed time is {}", now.elapsed().as_millis()); - assert_eq!(txns.len(), optimized_txns.len()); - } - - #[test] - fn test_same_sender_relative_order() { - let mut rng = OsRng; - let max_txn_per_sender = 100; - let num_senders = 100; - let mut orig_txns = Vec::new(); - let mut orig_txns_by_sender = HashMap::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); - orig_txns_by_sender.insert(sender_txns.first().unwrap().sender(), sender_txns.clone()); - orig_txns.append(&mut sender_txns); - } - let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); - let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); - let mut optimized_txns_by_sender = HashMap::new(); - for txn in optimized_txns { - optimized_txns_by_sender - .entry(txn.sender()) - .or_insert_with(Vec::new) - .push(txn); - } - - for (sender, orig_txns) in orig_txns_by_sender { - assert_eq!(optimized_txns_by_sender.get(&sender).unwrap(), &orig_txns) - } - } - - #[test] - // S1_1, S2_1, S3_1, S3_2 - // with conflict_window_size=3, should return (keep the order, fairness to early transactions): - // S1_1, S2_1, S3_1, S3_2 - fn test_3_sender_shuffling() { - let mut orig_txns = Vec::new(); - let sender1_txns = create_signed_transaction(1); - let sender2_txns = create_signed_transaction(1); - let sender3_txns = create_signed_transaction(2); - orig_txns.extend(sender1_txns.clone()); - orig_txns.extend(sender2_txns.clone()); - orig_txns.extend(sender3_txns.clone()); - let txn_shuffler = SenderAwareShuffler::new(3); - let optimized_txns = txn_shuffler.shuffle(orig_txns); - assert_eq!( - optimized_txns.first().unwrap(), - sender1_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(1).unwrap(), - sender2_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(2).unwrap(), - sender3_txns.first().unwrap() - ); - assert_eq!(optimized_txns.get(3).unwrap(), sender3_txns.get(1).unwrap()); - } - - #[test] - // S1_1, S2_1, S1_2, S3_1, S4_1, S5_1 - // with conflict_window_size=3, should return - // (we separate transactions from same sender, even if they are not consecutive): - // S1_1, S2_1, S3_1, S4_1, S1_2, S5_1 - fn test_5_sender_shuffling() { - let mut orig_txns = Vec::new(); - let sender1_txns = create_signed_transaction(2); - let sender2_txns = create_signed_transaction(1); - let sender3_txns = create_signed_transaction(1); - let sender4_txns = create_signed_transaction(1); - let sender5_txns = create_signed_transaction(1); - orig_txns.extend(sender1_txns.clone()); - orig_txns.extend(sender2_txns.clone()); - orig_txns.extend(sender3_txns.clone()); - orig_txns.extend(sender4_txns.clone()); - orig_txns.extend(sender5_txns.clone()); - let txn_shuffler = SenderAwareShuffler::new(3); - let optimized_txns = txn_shuffler.shuffle(orig_txns); - assert_eq!( - optimized_txns.first().unwrap(), - sender1_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(1).unwrap(), - sender2_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(2).unwrap(), - sender3_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(3).unwrap(), - sender4_txns.first().unwrap() - ); - assert_eq!(optimized_txns.get(4).unwrap(), sender1_txns.get(1).unwrap()); - assert_eq!( - optimized_txns.get(5).unwrap(), - sender5_txns.first().unwrap() - ); - } - - #[test] - // S1_1, S1_2, S2_1, S3_1, S3_2, S4_1, S5_1, S6_1 - // with conflict_window_size=3, should return (each batches are separated from the point they appear on): - // S1_1, S2_1, S3_1, S4_1, S1_2, S5_1, S3_2, S6_1 - fn test_6_sender_shuffling() { - let mut orig_txns = Vec::new(); - let sender1_txns = create_signed_transaction(2); - let sender2_txns = create_signed_transaction(1); - let sender3_txns = create_signed_transaction(2); - let sender4_txns = create_signed_transaction(1); - let sender5_txns = create_signed_transaction(1); - let sender6_txns = create_signed_transaction(1); - orig_txns.extend(sender1_txns.clone()); - orig_txns.extend(sender2_txns.clone()); - orig_txns.extend(sender3_txns.clone()); - orig_txns.extend(sender4_txns.clone()); - orig_txns.extend(sender5_txns.clone()); - orig_txns.extend(sender6_txns.clone()); - let txn_shuffler = SenderAwareShuffler::new(3); - let optimized_txns = txn_shuffler.shuffle(orig_txns); - assert_eq!( - optimized_txns.first().unwrap(), - sender1_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(1).unwrap(), - sender2_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(2).unwrap(), - sender3_txns.first().unwrap() - ); - assert_eq!( - optimized_txns.get(3).unwrap(), - sender4_txns.first().unwrap() - ); - assert_eq!(optimized_txns.get(4).unwrap(), sender1_txns.get(1).unwrap()); - assert_eq!( - optimized_txns.get(5).unwrap(), - sender5_txns.first().unwrap() - ); - assert_eq!(optimized_txns.get(6).unwrap(), sender3_txns.get(1).unwrap()); - assert_eq!( - optimized_txns.get(7).unwrap(), - sender6_txns.first().unwrap() - ); - } - - #[test] - fn test_random_shuffling() { - let mut rng = OsRng; - let max_senders = 50; - let max_txn_per_sender = 100; - let num_senders = rng.gen_range(1, max_senders); - let mut orig_txns = Vec::new(); - let mut senders = Vec::new(); - let mut orig_txn_set = HashSet::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); - senders.push(sender_txns.first().unwrap().sender()); - orig_txns.append(&mut sender_txns); - } - for txn in orig_txns.clone() { - orig_txn_set.insert(txn.into_raw_transaction()); - } - - let txn_shuffler = SenderAwareShuffler::new(num_senders - 1); - let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); - let mut optimized_txn_set = HashSet::new(); - assert_eq!(orig_txns.len(), optimized_txns.len()); - - for optimized_txn in optimized_txns { - assert!(orig_txn_set.contains(&optimized_txn.clone().into_raw_transaction())); - optimized_txn_set.insert(optimized_txn.into_raw_transaction()); - } - - for orig_txn in orig_txns { - assert!(optimized_txn_set.contains(&orig_txn.into_raw_transaction())); - } - } - - #[test] - fn test_shuffling_zero_conflict_window() { - let mut rng = OsRng; - let max_senders = 50; - let max_txn_per_sender = 100; - let num_senders = rng.gen_range(1, max_senders); - let mut orig_txns = Vec::new(); - let mut senders = Vec::new(); - for _ in 0..num_senders { - let mut sender_txns = create_signed_transaction(rng.gen_range(1, max_txn_per_sender)); - senders.push(sender_txns.first().unwrap().sender()); - orig_txns.append(&mut sender_txns); - } - - let txn_shuffler = SenderAwareShuffler::new(0); - let optimized_txns = txn_shuffler.shuffle(orig_txns.clone()); - assert_eq!(orig_txns.len(), optimized_txns.len()); - // Assert that the ordering is unchanged in case of unique senders txns. - assert_eq!(orig_txns, optimized_txns); - } -} diff --git a/consensus/src/twins/basic_twins_test.rs b/consensus/src/twins/basic_twins_test.rs deleted file mode 100644 index 9893efebd5cae..0000000000000 --- a/consensus/src/twins/basic_twins_test.rs +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - network_interface::ConsensusMsg, - network_tests::{NetworkPlayground, TwinId}, - test_utils::{consensus_runtime, timed_block_on}, - twins::twins_node::SMRNode, -}; -use aptos_consensus_types::{block::Block, common::Round}; -use aptos_types::on_chain_config::ProposerElectionType::{ - FixedProposer, RotatingProposer, RoundProposer, -}; -use futures::StreamExt; -use std::collections::HashMap; - -#[test] -/// This test checks that the first proposal has its parent and -/// QC pointing to the genesis block. -/// -/// Setup: -/// -/// 4 honest nodes, and 0 twins -/// -/// Run the test: -/// cargo xtest -p consensus basic_start_test -- --nocapture -fn basic_start_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let num_nodes = 4; - let num_twins = 0; - let nodes = SMRNode::start_num_nodes_with_twins( - num_nodes, - num_twins, - &mut playground, - RotatingProposer(2), - None, - ); - let genesis = Block::make_genesis_block_from_ledger_info(&nodes[0].storage.get_ledger_info()); - timed_block_on(&runtime, async { - let msg = playground - .wait_for_messages(1, NetworkPlayground::proposals_only) - .await; - let first_proposal = match &msg[0].1 { - ConsensusMsg::ProposalMsg(proposal) => proposal, - _ => panic!("Unexpected message found"), - }; - assert_eq!(first_proposal.proposal().parent_id(), genesis.id()); - assert_eq!( - first_proposal - .proposal() - .quorum_cert() - .certified_block() - .id(), - genesis.id() - ); - }); -} - -#[test] -/// This test checks that the split_network function works -/// as expected, that is: nodes in a partition with less nodes -/// than required for quorum do not commit anything. -/// -/// Setup: -/// -/// 4 honest nodes (n0, n1, n2, n3), and 0 twins. -/// Create two partitions p1=[n2], and p2=[n0, n1, n3] with -/// a proposer (n0) in p2. -/// -/// Test: -/// -/// Run consensus for enough rounds to potentially form a commit. -/// Check that n1 has no commits, and n0 has commits. -/// -/// Run the test: -/// cargo xtest -p consensus drop_config_test -- --nocapture -#[ignore] // TODO: https://github.com/aptos-labs/aptos-core/issues/8767 -fn drop_config_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let num_nodes = 4; - let num_twins = 0; - let mut nodes = SMRNode::start_num_nodes_with_twins( - num_nodes, - num_twins, - &mut playground, - FixedProposer(2), - None, - ); - - // 4 honest nodes - let n0_twin_id = nodes[0].id; - let n1_twin_id = nodes[1].id; - let n2_twin_id = nodes[2].id; - let n3_twin_id = nodes[3].id; - - assert!(playground.split_network(vec![n2_twin_id], vec![n0_twin_id, n1_twin_id, n3_twin_id])); - runtime.spawn(playground.start()); - - timed_block_on(&runtime, async { - // Check that the commit log for n0 is not empty - let node0_commit = nodes[0].commit_cb_receiver.next().await; - assert!(node0_commit.is_some()); - - // Check that the commit log for n2 is empty - let node2_commit = match nodes[2].commit_cb_receiver.try_next() { - Ok(Some(node_commit)) => Some(node_commit), - _ => None, - }; - assert!(node2_commit.is_none()); - }); -} - -#[test] -/// This test checks that the vote of a node and its twin -/// should be counted as duplicate vote (because they have -/// the same public keys) -/// -/// Setup: -/// -/// 4 honest nodes (n0, n1, n2, n3), and 1 twin (twin0) -/// Create 2 partitions, p1=[n1, n3], p2=[n0, twin0, n2] -/// -/// Test: -/// -/// Extract enough votes to potentially form commits. Check -/// that no node commits any block. This is because we need -/// 3 nodes to form a quorum and no partition has enough votes -/// (note there are 3 nodes in p2, but one of them is a twin, -/// and its vote will be counted as duplicate of n0). -/// -/// Run the test: -/// cargo xtest -p consensus twins_vote_dedup_test -- --nocapture -fn twins_vote_dedup_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let num_nodes = 4; - let num_twins = 1; - let mut nodes = SMRNode::start_num_nodes_with_twins( - num_nodes, - num_twins, - &mut playground, - RotatingProposer(2), - None, - ); - - // 4 honest nodes - let n0_twin_id = nodes[0].id; - // twin of n0 has same author as node[0] - let twin0_twin_id = nodes[4].id; - assert_eq!(n0_twin_id.author, twin0_twin_id.author); - let n1_twin_id = nodes[1].id; - let n2_twin_id = nodes[2].id; - let n3_twin_id = nodes[3].id; - - assert!(playground.split_network(vec![n1_twin_id, n3_twin_id], vec![ - twin0_twin_id, - n0_twin_id, - n2_twin_id - ],)); - runtime.spawn(playground.start()); - - timed_block_on(&runtime, async { - // No node should be able to commit because of the way partitions - // have been created - let mut commit_seen = false; - for node in &mut nodes { - if let Ok(Some(_node_commit)) = node.commit_cb_receiver.try_next() { - commit_seen = true; - } - } - assert!(!commit_seen); - }); -} - -#[test] -/// This test checks that when a node becomes a proposer, its -/// twin becomes one too. -/// -/// Setup: -/// -/// 4 honest nodes (n0, n1, n2, n3), and 2 twins (twin0, twin1) -/// Create 2 partitions, p1=[n0, n1, n2], p2=[n3, twin0, twin1] -/// Let n0 (and implicitly twin0) be proposers -/// -/// Test: -/// -/// Extract enough votes so nodes in both partitions form commits. -/// The commits should be on two different blocks -/// -/// Run the test: -/// cargo xtest -p consensus twins_proposer_test -- --nocapture -#[ignore] -fn twins_proposer_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let num_nodes = 4; - let num_twins = 2; - - // Specify round leaders - // Will default to the first node, if no leader specified for given round - let mut round_proposers: HashMap = HashMap::new(); - // Leaders are n0 (and implicitly twin0) for round 1..10 - for i in 1..10 { - round_proposers.insert(i, 0); - } - - let mut nodes = SMRNode::start_num_nodes_with_twins( - num_nodes, - num_twins, - &mut playground, - RoundProposer(HashMap::new()), - Some(round_proposers), - ); - - // 4 honest nodes - let n0_twin_id = nodes[0].id; - // twin of n0 has same author as node_authors[0] - let twin0_twin_id = nodes[4].id; - assert_eq!(n0_twin_id.author, twin0_twin_id.author); - let n1_twin_id = nodes[1].id; - // twin of n1 has same author as node_authors[1] - let twin1_twin_id = nodes[5].id; - assert_eq!(n1_twin_id.author, twin1_twin_id.author); - let n2_twin_id = nodes[2].id; - let n3_twin_id = nodes[3].id; - - // Create per round partitions - let mut round_partitions: HashMap>> = HashMap::new(); - // Round 1 to 10 partitions: [node0, node1, node2], [node3, twin0, twin1] - for i in 1..10 { - round_partitions.insert(i, vec![vec![n0_twin_id, n1_twin_id, n2_twin_id], vec![ - n3_twin_id, - twin0_twin_id, - twin1_twin_id, - ]]); - } - assert!(playground.split_network_round(&round_partitions)); - runtime.spawn(playground.start()); - - timed_block_on(&runtime, async { - let node0_commit = nodes[0].commit_cb_receiver.next().await; - let twin0_commit = nodes[4].commit_cb_receiver.next().await; - - match (node0_commit, twin0_commit) { - (Some(node0_commit_inner), Some(twin0_commit_inner)) => { - let node0_commit_id = node0_commit_inner.ledger_info().commit_info().id(); - let twin0_commit_id = twin0_commit_inner.ledger_info().commit_info().id(); - // Proposal from both node0 and twin_node0 are going to - // get committed in their respective partitions - assert_ne!(node0_commit_id, twin0_commit_id); - }, - _ => panic!("[TwinsTest] Test failed due to no commit(s)"), - } - }); -} - -#[test] -#[ignore] // TODO: https://github.com/aptos-labs/aptos-core/issues/6615 -/// This test checks that when a node and its twin are both leaders -/// for a round, only one of the two proposals gets committed -/// -/// Setup: -/// -/// Network of 4 nodes (n0, n1, n2, n3), and 1 twin (twin0) -/// -/// Test: -/// -/// Let n0 (and implicitly twin0) be proposers -/// Pull out enough votes so a commit can be formed -/// Check that the commit of n0 and twin0 matches -/// -/// Run the test: -/// cargo xtest -p consensus twins_commit_test -- --nocapture -fn twins_commit_test() { - let runtime = consensus_runtime(); - let mut playground = NetworkPlayground::new(runtime.handle().clone()); - let num_nodes = 4; - let num_twins = 1; - - // Specify round leaders - // Will default to the first node, if no leader specified for given round - let mut round_proposers: HashMap = HashMap::new(); - // Leaders are n0 and twin0 for round 1..10 - for i in 1..10 { - round_proposers.insert(i, 0); - } - - let mut nodes = SMRNode::start_num_nodes_with_twins( - num_nodes, - num_twins, - &mut playground, - RoundProposer(HashMap::new()), - Some(round_proposers), - ); - runtime.spawn(playground.start()); - - timed_block_on(&runtime, async { - let node0_commit = nodes[0].commit_cb_receiver.next().await; - let twin0_commit = nodes[4].commit_cb_receiver.next().await; - - match (node0_commit, twin0_commit) { - (Some(node0_commit_inner), Some(twin0_commit_inner)) => { - let node0_commit_id = node0_commit_inner.ledger_info().commit_info().id(); - let twin0_commit_id = twin0_commit_inner.ledger_info().commit_info().id(); - // Proposals from both node0 and twin_node0 are going to race, - // but only one of them will form a commit - assert_eq!(node0_commit_id, twin0_commit_id); - }, - _ => panic!("[TwinsTest] Test failed due to no commit(s)"), - } - }); -} diff --git a/consensus/src/twins/mod.rs b/consensus/src/twins/mod.rs deleted file mode 100644 index f35a1c4ce2848..0000000000000 --- a/consensus/src/twins/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -mod basic_twins_test; -mod twins_node; diff --git a/consensus/src/twins/twins_node.rs b/consensus/src/twins/twins_node.rs deleted file mode 100644 index 3456cd3b58357..0000000000000 --- a/consensus/src/twins/twins_node.rs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters, - epoch_manager::EpochManager, - network::NetworkTask, - network_interface::{ConsensusNetworkClient, DIRECT_SEND, RPC}, - network_tests::{NetworkPlayground, TwinId}, - payload_manager::PayloadManager, - pipeline::buffer_manager::OrderedBlocks, - quorum_store::quorum_store_db::MockQuorumStoreDB, - rand::rand_gen::storage::in_memory::InMemRandDb, - test_utils::{mock_execution_client::MockExecutionClient, MockStorage}, - util::time_service::ClockTimeService, -}; -use aptos_bounded_executor::BoundedExecutor; -use aptos_channels::{self, aptos_channel, message_queues::QueueStyle}; -use aptos_config::{ - config::{NodeConfig, WaypointConfig}, - generator::{self, ValidatorSwarm}, - network_id::{NetworkId, PeerNetworkId}, -}; -use aptos_consensus_types::common::{Author, Round}; -use aptos_event_notifications::{ReconfigNotification, ReconfigNotificationListener}; -use aptos_mempool::mocks::MockSharedMempool; -use aptos_network::{ - application::interface::{NetworkClient, NetworkServiceEvents}, - peer_manager::{conn_notifs_channel, ConnectionRequestSender, PeerManagerRequestSender}, - protocols::{ - network, - network::{NetworkEvents, NewNetworkEvents, NewNetworkSender}, - wire::handshake::v1::ProtocolIdSet, - }, - transport::ConnectionMetadata, - ProtocolId, -}; -use aptos_types::{ - ledger_info::LedgerInfoWithSignatures, - on_chain_config::{ - ConsensusConfigV1, InMemoryOnChainConfig, OnChainConfig, OnChainConfigPayload, - OnChainConsensusConfig, - ProposerElectionType::{self, RoundProposer}, - ValidatorSet, - }, - transaction::SignedTransaction, - validator_info::ValidatorInfo, - waypoint::Waypoint, -}; -use aptos_validator_transaction_pool::VTxnPoolState; -use futures::{channel::mpsc, StreamExt}; -use maplit::hashmap; -use std::{collections::HashMap, iter::FromIterator, sync::Arc}; -use tokio::runtime::Runtime; - -/// Auxiliary struct that is preparing SMR for the test -pub struct SMRNode { - pub id: TwinId, - pub storage: Arc, - pub commit_cb_receiver: mpsc::UnboundedReceiver, - _runtime: Runtime, - _shared_mempool: MockSharedMempool, - _state_sync: mpsc::UnboundedReceiver>, -} - -fn author_from_config(config: &NodeConfig) -> Author { - config.validator_network.as_ref().unwrap().peer_id() -} - -impl SMRNode { - fn start( - playground: &mut NetworkPlayground, - config: NodeConfig, - consensus_config: OnChainConsensusConfig, - storage: Arc, - twin_id: TwinId, - vtxn_pool: VTxnPoolState, - ) -> Self { - // Create a runtime for the twin - let thread_name = format!("twin-{}", twin_id.id); - let runtime = aptos_runtimes::spawn_named_runtime(thread_name, None); - let _entered_runtime = runtime.enter(); - - // Setup the network and SMR node - let (network_reqs_tx, network_reqs_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (connection_reqs_tx, _) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (consensus_tx, consensus_rx) = aptos_channel::new(QueueStyle::FIFO, 8, None); - let (_conn_mgr_reqs_tx, conn_mgr_reqs_rx) = aptos_channels::new_test(8); - let (_, conn_notifs_channel) = conn_notifs_channel::new(); - let network_sender = network::NetworkSender::new( - PeerManagerRequestSender::new(network_reqs_tx), - ConnectionRequestSender::new(connection_reqs_tx), - ); - let network_client = NetworkClient::new( - DIRECT_SEND.into(), - RPC.into(), - hashmap! {NetworkId::Validator => network_sender}, - playground.peer_protocols(), - ); - let consensus_network_client = ConsensusNetworkClient::new(network_client); - let network_events = NetworkEvents::new(consensus_rx, conn_notifs_channel, None); - let network_service_events = - NetworkServiceEvents::new(hashmap! {NetworkId::Validator => network_events}); - - playground.add_node(twin_id, consensus_tx, network_reqs_rx, conn_mgr_reqs_rx); - - let (state_sync_client, state_sync) = mpsc::unbounded(); - let (ordered_blocks_tx, mut ordered_blocks_events) = mpsc::unbounded::(); - let shared_mempool = MockSharedMempool::new(); - let (quorum_store_to_mempool_sender, _) = mpsc::channel(1_024); - - let execution_client = Arc::new(MockExecutionClient::new( - state_sync_client, - ordered_blocks_tx, - Arc::clone(&storage), - )); - let (reconfig_sender, reconfig_events) = aptos_channel::new(QueueStyle::LIFO, 1, None); - let reconfig_listener = ReconfigNotificationListener { - notification_receiver: reconfig_events, - }; - let _commit_notifier = Arc::from(PayloadManager::DirectMempool); - let mut configs = HashMap::new(); - configs.insert( - ValidatorSet::CONFIG_ID, - bcs::to_bytes(storage.get_validator_set()).unwrap(), - ); - configs.insert( - OnChainConsensusConfig::CONFIG_ID, - // Requires double serialization, check deserialize_into_config for more details - bcs::to_bytes(&bcs::to_bytes(&consensus_config).unwrap()).unwrap(), - ); - let payload = OnChainConfigPayload::new(1, InMemoryOnChainConfig::new(configs)); - - reconfig_sender - .push((), ReconfigNotification { - version: 1, - on_chain_configs: payload, - }) - .unwrap(); - - let time_service = Arc::new(ClockTimeService::new(runtime.handle().clone())); - - let (timeout_sender, timeout_receiver) = - aptos_channels::new(1_024, &counters::PENDING_ROUND_TIMEOUTS); - let (self_sender, self_receiver) = - aptos_channels::new_unbounded(&counters::PENDING_SELF_MESSAGES); - - let quorum_store_storage = Arc::new(MockQuorumStoreDB::new()); - let bounded_executor = BoundedExecutor::new(2, playground.handle()); - - let epoch_mgr = EpochManager::new( - &config, - time_service, - self_sender, - consensus_network_client, - timeout_sender, - quorum_store_to_mempool_sender, - execution_client.clone(), - storage.clone(), - quorum_store_storage, - reconfig_listener, - bounded_executor, - aptos_time_service::TimeService::real(), - vtxn_pool, - Arc::new(InMemRandDb::new()), - ); - let (network_task, network_receiver) = - NetworkTask::new(network_service_events, self_receiver); - - runtime.spawn(network_task.start()); - runtime.spawn(epoch_mgr.start(timeout_receiver, network_receiver)); - - let (commit_cb_sender, commit_cb_receiver) = mpsc::unbounded::(); - runtime.spawn(async move { - loop { - let ordered_blocks = ordered_blocks_events.next().await.unwrap(); - let commit = ordered_blocks.ordered_proof.clone(); - execution_client - .commit_to_storage(ordered_blocks) - .await - .unwrap(); - - commit_cb_sender.unbounded_send(commit.clone()).unwrap(); - } - }); - - Self { - id: twin_id, - _runtime: runtime, - commit_cb_receiver, - storage, - _shared_mempool: shared_mempool, - _state_sync: state_sync, - } - } - - /// Starts a given number of nodes and their twins - pub fn start_num_nodes_with_twins( - num_nodes: usize, - num_twins: usize, - playground: &mut NetworkPlayground, - proposer_type: ProposerElectionType, - round_proposers_idx: Option>, - ) -> Vec { - assert!(num_nodes >= num_twins); - let ValidatorSwarm { - nodes: mut node_configs, - } = generator::validator_swarm_for_testing(num_nodes); - let peers_and_metadata = playground.peer_protocols(); - node_configs.iter().for_each(|config| { - let peer_id = author_from_config(config); - let mut conn_meta = ConnectionMetadata::mock(peer_id); - conn_meta.application_protocols = ProtocolIdSet::from_iter([ - ProtocolId::ConsensusDirectSendJson, - ProtocolId::ConsensusDirectSendBcs, - ProtocolId::ConsensusRpcBcs, - ]); - let peer_network_id = PeerNetworkId::new(NetworkId::Validator, peer_id); - peers_and_metadata - .insert_connection_metadata(peer_network_id, conn_meta) - .unwrap(); - }); - - node_configs.sort_by_key(author_from_config); - let validator_set = ValidatorSet::new( - node_configs - .iter() - .enumerate() - .map(|(index, config)| { - let sr_test_config = config.consensus.safety_rules.test.as_ref().unwrap(); - ValidatorInfo::new_with_test_network_keys( - sr_test_config.author, - sr_test_config.consensus_key.as_ref().unwrap().public_key(), - 1, - index as u64, - ) - }) - .collect(), - ); - // sort by the peer id - - let proposer_type = match proposer_type { - RoundProposer(_) => { - let mut round_proposers: HashMap = HashMap::new(); - - if let Some(proposers) = round_proposers_idx { - proposers.iter().for_each(|(round, idx)| { - round_proposers.insert(*round, author_from_config(&node_configs[*idx])); - }) - } - RoundProposer(round_proposers) - }, - _ => proposer_type, - }; - - // We don't add twins to ValidatorSet or round_proposers above - // because a node with twins should be treated the same at the - // consensus level - for i in 0..num_twins { - let twin = node_configs[i].clone(); - node_configs.push(twin); - } - - let mut smr_nodes = vec![]; - - for (smr_id, mut config) in node_configs.into_iter().enumerate() { - let (_, storage) = MockStorage::start_for_testing(validator_set.clone()); - - let waypoint = Waypoint::new_epoch_boundary(&storage.get_ledger_info()) - .expect("Unable to produce waypoint with the provided LedgerInfo"); - config - .consensus - .safety_rules - .test - .as_mut() - .unwrap() - .waypoint = Some(waypoint); - config.base.waypoint = WaypointConfig::FromConfig(waypoint); - // Disable timeout in twins test to avoid flakiness - config.consensus.round_initial_timeout_ms = 2_000_000; - - let author = author_from_config(&config); - - let twin_id = TwinId { id: smr_id, author }; - - let consensus_config = OnChainConsensusConfig::V1(ConsensusConfigV1 { - proposer_election_type: proposer_type.clone(), - ..ConsensusConfigV1::default() - }); - - let vtxn_pool = VTxnPoolState::default(); - smr_nodes.push(Self::start( - playground, - config, - consensus_config, - storage, - twin_id, - vtxn_pool, - )); - } - smr_nodes - } -} diff --git a/consensus/src/txn_hash_and_authenticator_deduper.rs b/consensus/src/txn_hash_and_authenticator_deduper.rs deleted file mode 100644 index 8170c9f0be21f..0000000000000 --- a/consensus/src/txn_hash_and_authenticator_deduper.rs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - counters::{TXN_DEDUP_FILTERED, TXN_DEDUP_SECONDS}, - transaction_deduper::TransactionDeduper, -}; -use aptos_experimental_runtimes::thread_manager::optimal_min_len; -use aptos_types::transaction::SignedTransaction; -use rayon::prelude::*; -use std::collections::{HashMap, HashSet}; - -/// An implementation of TransactionDeduper. Duplicate filtering is done using the pair -/// (raw_txn.hash(), authenticator). Both the hash and signature are required because dedup -/// happens before signatures are verified and transaction prologue is checked. (So, e.g., a bad -/// transaction could contain a txn and signature that are unrelated.) If the checks are done -/// beforehand only one of the txn hash or signature would be required. -/// -/// The implementation is written to avoid and/or parallelize the most expensive operations. Below -/// are the steps: -/// 1. Mark possible duplicates (sequential): Using a helper HashMap, mark transactions with 2+ -/// (sender, seq_no) pairs as possible duplicates. If no possible duplicates, return the original -/// transactions. -/// 2. Calculate txn hashes (parallel): For all possible duplicates, calculate the txn hash. This -/// is an expensive operation. -/// 3. Filter duplicates (sequential): Using a helper HashSet with the txn hashes calculated above -/// and signatures, filter actual duplicate transactions. -/// -/// Possible future optimizations: -/// a. Note the possible duplicates in Step 1 are independent of each other, so they could be -/// grouped independently and run in parallel in Step 3. -/// b. Txn hashes are calculated at many places within a validator. A per-txn hash cache could speed -/// up dedup or later operations. -/// c. If signature verification is moved to before dedup, then only the signature has to be matched -/// for duplicates and not the hash. -pub(crate) struct TxnHashAndAuthenticatorDeduper {} - -impl TransactionDeduper for TxnHashAndAuthenticatorDeduper { - fn dedup(&self, transactions: Vec) -> Vec { - let _timer = TXN_DEDUP_SECONDS.start_timer(); - let mut seen = HashMap::new(); - let mut is_possible_duplicate = false; - let mut possible_duplicates = vec![false; transactions.len()]; - for (i, txn) in transactions.iter().enumerate() { - match seen.get(&(txn.sender(), txn.sequence_number())) { - None => { - seen.insert((txn.sender(), txn.sequence_number()), i); - }, - Some(first_index) => { - is_possible_duplicate = true; - possible_duplicates[*first_index] = true; - possible_duplicates[i] = true; - }, - } - } - if !is_possible_duplicate { - TXN_DEDUP_FILTERED.observe(0 as f64); - return transactions; - } - - let num_txns = transactions.len(); - - let hash_and_authenticators: Vec<_> = possible_duplicates - .into_par_iter() - .zip(&transactions) - .with_min_len(optimal_min_len(num_txns, 48)) - .map(|(need_hash, txn)| match need_hash { - true => Some((txn.clone().committed_hash(), txn.authenticator())), - false => None, - }) - .collect(); - - // TODO: Possibly parallelize. See struct comment. - let mut seen_hashes = HashSet::new(); - let mut num_duplicates: usize = 0; - let filtered: Vec<_> = hash_and_authenticators - .into_iter() - .zip(transactions) - .filter_map(|(maybe_hash, txn)| match maybe_hash { - None => Some(txn), - Some(hash_and_authenticator) => { - if seen_hashes.insert(hash_and_authenticator) { - Some(txn) - } else { - num_duplicates += 1; - None - } - }, - }) - .collect(); - - TXN_DEDUP_FILTERED.observe(num_duplicates as f64); - filtered - } -} - -impl TxnHashAndAuthenticatorDeduper { - pub fn new() -> Self { - Self {} - } -} - -#[cfg(test)] -mod tests { - use crate::{ - transaction_deduper::TransactionDeduper, - txn_hash_and_authenticator_deduper::TxnHashAndAuthenticatorDeduper, - }; - use aptos_cached_packages::aptos_stdlib; - use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; - use aptos_keygen::KeyGen; - use aptos_types::{ - chain_id::ChainId, - transaction::{RawTransaction, Script, SignedTransaction, TransactionPayload}, - }; - use move_core_types::account_address::AccountAddress; - use std::time::Instant; - - struct Account { - addr: AccountAddress, - /// The current private key for this account. - pub privkey: Ed25519PrivateKey, - /// The current public key for this account. - pub pubkey: Ed25519PublicKey, - } - - impl Account { - pub fn new() -> Self { - let (privkey, pubkey) = KeyGen::from_os_rng().generate_ed25519_keypair(); - Self::with_keypair(privkey, pubkey) - } - - pub fn with_keypair(privkey: Ed25519PrivateKey, pubkey: Ed25519PublicKey) -> Self { - let addr = aptos_types::account_address::from_public_key(&pubkey); - Account { - addr, - privkey, - pubkey, - } - } - } - - fn raw_txn( - payload: TransactionPayload, - sender: AccountAddress, - seq_num: u64, - gas_unit_price: u64, - ) -> RawTransaction { - RawTransaction::new( - sender, - seq_num, - payload, - 500_000, - gas_unit_price, - 0, - ChainId::new(10), - ) - } - - fn empty_txn(sender: AccountAddress, seq_num: u64, gas_unit_price: u64) -> RawTransaction { - let payload = TransactionPayload::Script(Script::new(vec![], vec![], vec![])); - raw_txn(payload, sender, seq_num, gas_unit_price) - } - - fn peer_to_peer_txn( - sender: AccountAddress, - receiver: AccountAddress, - seq_num: u64, - gas_unit_price: u64, - ) -> RawTransaction { - let payload = aptos_stdlib::aptos_coin_transfer(receiver, 1); - raw_txn(payload, sender, seq_num, gas_unit_price) - } - - fn block(refs: Vec<&SignedTransaction>) -> Vec { - refs.into_iter().cloned().collect() - } - - #[test] - fn test_single_txn() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let txn = empty_txn(sender.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey) - .unwrap() - .into_inner(); - let txns = vec![txn]; - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(txns.len(), deduped_txns.len()); - assert_eq!(txns, deduped_txns); - } - - #[test] - fn test_single_duplicate() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let txn = empty_txn(sender.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey) - .unwrap() - .into_inner(); - let txns = block(vec![&txn, &txn]); - let expected = block(vec![&txn]); - let deduped_txns = deduper.dedup(txns); - assert_eq!(expected.len(), deduped_txns.len()); - assert_eq!(expected, deduped_txns); - } - - #[test] - fn test_repeated_sequence_number() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let receiver = Account::new(); - - let txn_0a = empty_txn(sender.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey.clone()) - .unwrap() - .into_inner(); - // Different txn, same sender and sequence number. Should not be filtered. - let txn_0b = peer_to_peer_txn(sender.addr, receiver.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey) - .unwrap() - .into_inner(); - let txns = block(vec![&txn_0a, &txn_0b, &txn_0a]); - let expected = block(vec![&txn_0a, &txn_0b]); - let deduped_txns = deduper.dedup(txns); - assert_eq!(expected.len(), deduped_txns.len()); - assert_eq!(expected, deduped_txns); - } - - #[test] - fn test_bad_signer() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let bad_signer = Account::new(); - - // Txn signed by a bad signer (not the sender) - let txn_0a = empty_txn(sender.addr, 0, 100) - .sign(&bad_signer.privkey, bad_signer.pubkey.clone()) - .unwrap() - .into_inner(); - // Same txn, but signed by the correct signer (sender). Should not be filtered. - let txn_0b = empty_txn(sender.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey.clone()) - .unwrap() - .into_inner(); - let txns = block(vec![&txn_0a, &txn_0b]); - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(txns.len(), deduped_txns.len()); - assert_eq!(txns, deduped_txns); - } - - // The perf tests are simple micro-benchmarks and just output results without checking for regressions - static PERF_TXN_PER_BLOCK: usize = 10_000; - - fn measure_dedup_time( - deduper: TxnHashAndAuthenticatorDeduper, - txns: Vec, - ) -> f64 { - let start = Instant::now(); - let mut iterations = 0; - loop { - deduper.dedup(txns.clone()); - iterations += 1; - if iterations % 100 == 0 && start.elapsed().as_millis() > 2000 { - break; - } - } - let elapsed = start.elapsed(); - println!( - "elapsed: {}, iterations: {}, time per iteration: {}", - elapsed.as_secs_f64(), - iterations, - elapsed.as_secs_f64() / iterations as f64, - ); - elapsed.as_secs_f64() / iterations as f64 - } - - #[test] - fn test_performance_unique_empty_txns() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let txns: Vec<_> = (0..PERF_TXN_PER_BLOCK) - .map(|i| { - empty_txn(sender.addr, i as u64, 100) - .sign(&sender.privkey, sender.pubkey.clone()) - .unwrap() - .into_inner() - }) - .collect(); - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(txns.len(), deduped_txns.len()); - assert_eq!(txns, deduped_txns); - - measure_dedup_time(deduper, txns); - } - - #[test] - fn test_performance_duplicate_empty_txns() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let txn = empty_txn(sender.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey) - .unwrap() - .into_inner(); - let txns: Vec<_> = std::iter::repeat(txn.clone()) - .take(PERF_TXN_PER_BLOCK) - .collect(); - let expected = block(vec![&txn]); - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(expected.len(), deduped_txns.len()); - assert_eq!(expected, deduped_txns); - - measure_dedup_time(deduper, txns); - } - - #[test] - fn test_performance_unique_p2p_txns() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let receiver = Account::new(); - let txns: Vec<_> = (0..PERF_TXN_PER_BLOCK) - .map(|i| { - peer_to_peer_txn(sender.addr, receiver.addr, i as u64, 100) - .sign(&sender.privkey, sender.pubkey.clone()) - .unwrap() - .into_inner() - }) - .collect(); - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(txns.len(), deduped_txns.len()); - assert_eq!(txns, deduped_txns); - - measure_dedup_time(deduper, txns); - } - - #[test] - fn test_performance_duplicate_p2p_txns() { - let deduper = TxnHashAndAuthenticatorDeduper::new(); - - let sender = Account::new(); - let receiver = Account::new(); - let txn = peer_to_peer_txn(sender.addr, receiver.addr, 0, 100) - .sign(&sender.privkey, sender.pubkey) - .unwrap() - .into_inner(); - let txns: Vec<_> = std::iter::repeat(txn.clone()) - .take(PERF_TXN_PER_BLOCK) - .collect(); - let expected = block(vec![&txn]); - let deduped_txns = deduper.dedup(txns.clone()); - assert_eq!(expected.len(), deduped_txns.len()); - assert_eq!(expected, deduped_txns); - - measure_dedup_time(deduper, txns); - } -} diff --git a/consensus/src/txn_notifier.rs b/consensus/src/txn_notifier.rs deleted file mode 100644 index aef2c585c5915..0000000000000 --- a/consensus/src/txn_notifier.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{error::MempoolError, monitor}; -use anyhow::{format_err, Result}; -use aptos_consensus_types::common::RejectedTransactionSummary; -use aptos_mempool::QuorumStoreRequest; -use aptos_types::transaction::{SignedTransaction, TransactionStatus}; -use futures::channel::{mpsc, oneshot}; -use itertools::Itertools; -use std::time::Duration; -use tokio::time::timeout; - -/// Notification of failed transactions. -#[async_trait::async_trait] -pub trait TxnNotifier: Send + Sync { - /// Notification of txns which failed execution. (Committed txns is notified by - /// state sync.) - async fn notify_failed_txn( - &self, - txns: &[SignedTransaction], - statuses: &[TransactionStatus], - ) -> Result<(), MempoolError>; -} - -/// Execution -> Mempool notification of failed transactions. -pub struct MempoolNotifier { - consensus_to_mempool_sender: mpsc::Sender, - /// Timeout for consensus to get an ack from mempool for executed transactions (in milliseconds) - mempool_executed_txn_timeout_ms: u64, -} - -impl MempoolNotifier { - /// new - pub fn new( - consensus_to_mempool_sender: mpsc::Sender, - mempool_executed_txn_timeout_ms: u64, - ) -> Self { - Self { - consensus_to_mempool_sender, - mempool_executed_txn_timeout_ms, - } - } -} - -#[async_trait::async_trait] -impl TxnNotifier for MempoolNotifier { - async fn notify_failed_txn( - &self, - user_txns: &[SignedTransaction], - user_txn_statuses: &[TransactionStatus], - ) -> Result<(), MempoolError> { - let mut rejected_txns = vec![]; - - for (txn, status) in user_txns.iter().zip_eq(user_txn_statuses) { - if let TransactionStatus::Discard(reason) = status { - rejected_txns.push(RejectedTransactionSummary { - sender: txn.sender(), - sequence_number: txn.sequence_number(), - hash: txn.clone().committed_hash(), - reason: *reason, - }); - } - } - - if rejected_txns.is_empty() { - return Ok(()); - } - - let (callback, callback_rcv) = oneshot::channel(); - let req = QuorumStoreRequest::RejectNotification(rejected_txns, callback); - - // send to shared mempool - self.consensus_to_mempool_sender - .clone() - .try_send(req) - .map_err(anyhow::Error::from)?; - - if let Err(e) = monitor!( - "notify_mempool", - timeout( - Duration::from_millis(self.mempool_executed_txn_timeout_ms), - callback_rcv - ) - .await - ) { - Err(format_err!("[consensus] txn notifier did not receive ACK for commit notification sent to mempool on time: {:?}", e).into()) - } else { - Ok(()) - } - } -} diff --git a/consensus/src/util/db_tool.rs b/consensus/src/util/db_tool.rs deleted file mode 100644 index 5809747c4bcb7..0000000000000 --- a/consensus/src/util/db_tool.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - consensusdb::ConsensusDB, - quorum_store::{ - quorum_store_db::{QuorumStoreDB, QuorumStoreStorage}, - types::PersistedValue, - }, -}; -use anyhow::{bail, Result}; -use aptos_consensus_types::{block::Block, common::Payload, proof_of_store::ProofOfStore}; -use aptos_crypto::HashValue; -use aptos_types::transaction::{SignedTransaction, Transaction}; -use clap::Parser; -use std::{collections::HashMap, path::PathBuf}; - -#[derive(Parser)] -#[clap(about = "Dump txns from consensus db.")] -pub struct Command { - #[clap(long, value_parser)] - pub db_dir: PathBuf, - - // If None, will dump all blocks. - #[clap(long)] - pub block_id: Option, -} - -impl Command { - pub async fn run(self) -> Result<()> { - let txns = self.dump_pending_txns()?; - println!("{txns:?}"); - - Ok(()) - } - - pub fn dump_pending_txns(&self) -> Result> { - let quorum_store_db = QuorumStoreDB::new(self.db_dir.clone()); - let all_batches = quorum_store_db.get_all_batches().unwrap(); - - let consensus_db = ConsensusDB::new(self.db_dir.clone()); - let (_, _, blocks, _) = consensus_db.get_data()?; - - let mut txns = Vec::new(); - for block in blocks { - let id = block.id(); - if self.block_id.is_none() || id == self.block_id.unwrap() { - txns.extend( - extract_txns_from_block(&block, &all_batches)? - .into_iter() - .cloned() - .map(Transaction::UserTransaction), - ); - } - } - - Ok(txns) - } -} - -pub fn extract_txns_from_block<'a>( - block: &'a Block, - all_batches: &'a HashMap, -) -> anyhow::Result> { - match block.payload().as_ref() { - Some(payload) => { - let mut block_txns = Vec::new(); - - let extract_txns_from_proof_stores = move |proofs: &Vec| { - for proof in proofs { - let digest = proof.digest(); - if let Some(batch) = all_batches.get(digest) { - if let Some(txns) = batch.payload() { - block_txns.extend(txns); - } else { - bail!("Payload is not found for batch ({digest})."); - } - } else { - bail!("Batch ({digest}) is not found."); - } - } - Ok(block_txns) - }; - - match payload { - Payload::DirectMempool(_) => { - bail!("DirectMempool is not supported."); - }, - Payload::InQuorumStore(proof_with_data) => { - extract_txns_from_proof_stores(&proof_with_data.proofs) - }, - Payload::InQuorumStoreWithLimit(proof_with_data) => { - extract_txns_from_proof_stores(&proof_with_data.proof_with_data.proofs) - }, - Payload::QuorumStoreInlineHybrid(inline_batches, proof_with_data, _) => { - let mut all_txns = - extract_txns_from_proof_stores(&proof_with_data.proofs).unwrap(); - for (_, txns) in inline_batches { - all_txns.extend(txns); - } - Ok(all_txns) - }, - } - }, - None => Ok(vec![]), - } -} diff --git a/consensus/src/util/mock_time_service.rs b/consensus/src/util/mock_time_service.rs deleted file mode 100644 index 9f9a665cac48b..0000000000000 --- a/consensus/src/util/mock_time_service.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::util::time_service::{ScheduledTask, TimeService}; -use aptos_infallible::Mutex; -use aptos_logger::prelude::*; -use async_trait::async_trait; -use futures::future::AbortHandle; -use std::{sync::Arc, time::Duration}; - -/// SimulatedTimeService implements TimeService, however it does not depend on actual time -/// There are multiple ways to use it: -/// SimulatedTimeService::new will create time service that simply 'stuck' on time 0 -/// SimulatedTimeService::update_auto_advance_limit can then be used to allow time to advance up to -/// certain limit. SimulatedTimeService::auto_advance_until will create time service that will 'run' -/// until certain time limit Note that SimulatedTimeService does not actually wait for any timeouts, -/// notion of time in it is abstract. Tasks run asap as long as they are scheduled before configured -/// time limit -pub struct SimulatedTimeService { - inner: Arc>, -} - -struct SimulatedTimeServiceInner { - now: Duration, - pending: Vec<(Duration, Box)>, - time_limit: Duration, - /// Maximum duration self.now is allowed to advance to - max: Duration, -} - -#[async_trait] -impl TimeService for SimulatedTimeService { - fn run_after(&self, timeout: Duration, mut t: Box) -> AbortHandle { - let mut inner = self.inner.lock(); - let now = inner.now; - let deadline = now + timeout; - if deadline > inner.time_limit { - debug!( - "sched for deadline: {}, now: {}, limit: {}", - deadline.as_millis(), - now.as_millis(), - inner.time_limit.as_millis() - ); - inner.pending.push((deadline, t)); - } else { - debug!( - "exec deadline: {}, now: {}", - deadline.as_millis(), - now.as_millis() - ); - inner.now = deadline; - if inner.now > inner.max { - inner.now = inner.max; - } - // Perhaps this could be done better, but I think its good enough for tests... - futures::executor::block_on(t.run()); - } - let (handle, _) = AbortHandle::new_pair(); - handle - } - - fn get_current_timestamp(&self) -> Duration { - self.inner.lock().now - } - - async fn sleep(&self, t: Duration) { - let inner = self.inner.clone(); - let mut inner = inner.lock(); - inner.now += t; - if inner.now > inner.max { - inner.now = inner.max; - } - } -} - -impl Default for SimulatedTimeService { - fn default() -> Self { - Self::new() - } -} - -impl SimulatedTimeService { - /// Creates new SimulatedTimeService in disabled state (time not running) - pub fn new() -> SimulatedTimeService { - SimulatedTimeService { - inner: Arc::new(Mutex::new(SimulatedTimeServiceInner { - now: Duration::from_secs(0), - pending: vec![], - time_limit: Duration::from_secs(0), - max: Duration::from_secs(std::u64::MAX), - })), - } - } - - /// Creates new SimulatedTimeService that automatically advance time up to time_limit - pub fn auto_advance_until(time_limit: Duration) -> SimulatedTimeService { - SimulatedTimeService { - inner: Arc::new(Mutex::new(SimulatedTimeServiceInner { - now: Duration::from_secs(0), - pending: vec![], - time_limit, - max: Duration::from_secs(std::u64::MAX), - })), - } - } - - /// Update time_limit of this SimulatedTimeService instance and run pending tasks that has - /// deadline lower then new time_limit - #[allow(dead_code)] - pub fn update_auto_advance_limit(&mut self, time: Duration) { - let mut inner = self.inner.lock(); - inner.time_limit += time; - let time_limit = inner.time_limit; - let mut i = 0; - let mut drain = vec![]; - while i != inner.pending.len() { - let deadline = inner.pending[i].0; - if deadline <= time_limit { - drain.push(inner.pending.remove(i)); - } else { - i += 1; - } - } - for (_, mut t) in drain { - // probably could be done better then that, but for now I feel its good enough for tests - futures::executor::block_on(t.run()); - } - } -} - -impl Clone for SimulatedTimeService { - fn clone(&self) -> SimulatedTimeService { - SimulatedTimeService { - inner: self.inner.clone(), - } - } -} diff --git a/consensus/src/util/mod.rs b/consensus/src/util/mod.rs deleted file mode 100644 index a469f121916b2..0000000000000 --- a/consensus/src/util/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_types::{ - on_chain_config::{OnChainJWKConsensusConfig, OnChainRandomnessConfig}, - validator_txn::ValidatorTransaction, -}; - -pub mod db_tool; -#[cfg(any(test, feature = "fuzzing"))] -pub mod mock_time_service; -pub mod time_service; - -pub fn is_vtxn_expected( - randomness_config: &OnChainRandomnessConfig, - jwk_consensus_config: &OnChainJWKConsensusConfig, - vtxn: &ValidatorTransaction, -) -> bool { - match vtxn { - ValidatorTransaction::DKGResult(_) => randomness_config.randomness_enabled(), - ValidatorTransaction::ObservedJWKUpdate(_) => jwk_consensus_config.jwk_consensus_enabled(), - } -} diff --git a/consensus/src/util/time_service.rs b/consensus/src/util/time_service.rs deleted file mode 100644 index d90fd6a54c008..0000000000000 --- a/consensus/src/util/time_service.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::counters; -use aptos_logger::prelude::*; -use async_trait::async_trait; -use futures::{ - future::{AbortHandle, Abortable}, - Future, FutureExt, SinkExt, -}; -use std::{pin::Pin, time::Duration}; -use tokio::{runtime::Handle, time::sleep}; - -/// Time service is an abstraction for operations that depend on time -/// It supports implementations that can simulated time or depend on actual time -/// We can use simulated time in tests so tests can run faster and be more stable. -/// see SimulatedTime for implementation that tests should use -/// Time service also supports opportunities for future optimizations -/// For example instead of scheduling O(N) tasks in TaskExecutor we could have more optimal code -/// that only keeps single task in TaskExecutor -#[async_trait] -pub trait TimeService: Send + Sync { - /// Sends message to given sender after timeout, returns a handle that could use to cancel the task. - fn run_after(&self, timeout: Duration, task: Box) -> AbortHandle; - - /// Retrieve the current time stamp as a Duration (assuming it is on or after the UNIX_EPOCH) - fn get_current_timestamp(&self) -> Duration; - - /// Makes a future that will sleep for given Duration - /// This function guarantees that get_current_timestamp will increase at least by - /// given duration, e.g. - /// X = time_service::get_current_timestamp(); - /// time_service::sleep(Y).await; - /// Z = time_service::get_current_timestamp(); - /// assert(Z >= X + Y) - async fn sleep(&self, t: Duration); - - /// Wait until the Duration t since UNIX_EPOCH pass at least 1ms. - async fn wait_until(&self, t: Duration) { - while let Some(mut wait_duration) = t.checked_sub(self.get_current_timestamp()) { - wait_duration += Duration::from_millis(1); - counters::WAIT_DURATION_S.observe_duration(wait_duration); - self.sleep(wait_duration).await; - } - } -} - -/// This trait represents abstract task that can be submitted to TimeService::run_after -pub trait ScheduledTask: Send { - /// TimeService::run_after will run this method when time expires - /// It is expected that this function is lightweight and does not take long time to complete - fn run(&mut self) -> Pin + Send>>; -} - -/// This tasks send message to given Sender -pub struct SendTask -where - T: Send + 'static, -{ - sender: Option>, - message: Option, -} - -impl SendTask -where - T: Send + 'static, -{ - /// Makes new SendTask for given sender and message and wraps it to Box - pub fn make(sender: aptos_channels::Sender, message: T) -> Box { - Box::new(SendTask { - sender: Some(sender), - message: Some(message), - }) - } -} - -impl ScheduledTask for SendTask -where - T: Send + 'static, -{ - fn run(&mut self) -> Pin + Send>> { - let mut sender = self.sender.take().unwrap(); - let message = self.message.take().unwrap(); - let r = async move { - if let Err(e) = sender.send(message).await { - error!("Error on send: {:?}", e); - }; - }; - r.boxed() - } -} - -/// TimeService implementation that uses actual clock to schedule tasks -pub struct ClockTimeService { - executor: Handle, -} - -impl ClockTimeService { - /// Creates new TimeService that runs tasks based on actual clock - /// It needs executor to schedule internal tasks that facilitates it's work - pub fn new(executor: Handle) -> ClockTimeService { - ClockTimeService { executor } - } -} - -#[async_trait] -impl TimeService for ClockTimeService { - fn run_after(&self, timeout: Duration, mut t: Box) -> AbortHandle { - let (abort_handle, abort_registration) = AbortHandle::new_pair(); - let task = Abortable::new( - async move { - sleep(timeout).await; - t.run().await; - }, - abort_registration, - ); - self.executor.spawn(task); - abort_handle - } - - fn get_current_timestamp(&self) -> Duration { - aptos_infallible::duration_since_epoch() - } - - async fn sleep(&self, t: Duration) { - sleep(t).await - } -} - -#[tokio::test] -async fn test_time_service_abort() { - use futures::StreamExt; - - let time_service = ClockTimeService::new(tokio::runtime::Handle::current()); - let (tx, mut rx) = aptos_channels::new_test(10); - let task1 = SendTask::make(tx.clone(), 1); - let task2 = SendTask::make(tx.clone(), 2); - let handle1 = time_service.run_after(Duration::from_millis(100), task1); - let handle2 = time_service.run_after(Duration::from_millis(200), task2); - handle1.abort(); - // task 1 is aborted - assert_eq!(rx.next().await, Some(2)); - drop(tx); - assert_eq!(rx.next().await, None); - // abort an already finished task is no-op - handle2.abort(); -} diff --git a/crates/aptos-admin-service/Cargo.toml b/crates/aptos-admin-service/Cargo.toml deleted file mode 100644 index fbe96a96ff0d4..0000000000000 --- a/crates/aptos-admin-service/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "aptos-admin-service" -description = "The Admin Service" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus = { workspace = true } -aptos-consensus-types = { workspace = true } -aptos-crypto = { workspace = true } -aptos-infallible = { workspace = true } -aptos-logger = { workspace = true } -aptos-runtimes = { workspace = true } -aptos-storage-interface = { workspace = true } -aptos-types = { workspace = true } -async-mutex = { workspace = true } -bcs = { workspace = true } -futures = { workspace = true } -http = { workspace = true } -hyper = { workspace = true } -lazy_static = { workspace = true } -mime = { workspace = true } -sha256 = { workspace = true } -tokio = { workspace = true } -tokio-scoped = { workspace = true } -url = { workspace = true } - -[target.'cfg(target_os = "linux")'.dependencies] -aptos-profiler = { workspace = true } -pprof = { workspace = true } -regex = { workspace = true } -rstack-self = { workspace = true } diff --git a/crates/aptos-admin-service/src/lib.rs b/crates/aptos-admin-service/src/lib.rs deleted file mode 100644 index d408d1a2e9da0..0000000000000 --- a/crates/aptos-admin-service/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod server; - -pub use server::*; diff --git a/crates/aptos-admin-service/src/server/consensus/mod.rs b/crates/aptos-admin-service/src/server/consensus/mod.rs deleted file mode 100644 index 7d0ef62bbe57f..0000000000000 --- a/crates/aptos-admin-service/src/server/consensus/mod.rs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::{reply_with, reply_with_status, spawn_blocking}; -use anyhow::{bail, Error}; -use aptos_consensus::{ - persistent_liveness_storage::PersistentLivenessStorage, - quorum_store::quorum_store_db::QuorumStoreStorage, util::db_tool::extract_txns_from_block, -}; -use aptos_crypto::HashValue; -use aptos_logger::info; -use aptos_types::transaction::Transaction; -use http::header::{HeaderValue, CONTENT_LENGTH}; -use hyper::{Body, Request, Response, StatusCode}; -use std::{collections::HashMap, sync::Arc}; - -pub async fn handle_dump_consensus_db_request( - _req: Request, - consensus_db: Arc, -) -> hyper::Result> { - info!("Dumping consensus db."); - - match spawn_blocking(move || dump_consensus_db(consensus_db.as_ref())).await { - Ok(result) => { - info!("Finished dumping consensus db."); - let headers: Vec<(_, HeaderValue)> = - vec![(CONTENT_LENGTH, HeaderValue::from(result.len()))]; - Ok(reply_with(headers, result)) - }, - Err(e) => { - info!("Failed to dump consensus db: {e:?}"); - Ok(reply_with_status( - StatusCode::INTERNAL_SERVER_ERROR, - e.to_string(), - )) - }, - } -} - -pub async fn handle_dump_quorum_store_db_request( - req: Request, - quorum_store_db: Arc, -) -> hyper::Result> { - let query = req.uri().query().unwrap_or(""); - let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query.as_bytes()).collect(); - - let digest: Option = match query_pairs.get("digest") { - Some(val) => match val.parse() { - Ok(val) => Some(val), - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => None, - }; - - info!("Dumping quorum store db."); - - match spawn_blocking(move || dump_quorum_store_db(quorum_store_db.as_ref(), digest)).await { - Ok(result) => { - info!("Finished dumping quorum store db."); - let headers: Vec<(_, HeaderValue)> = - vec![(CONTENT_LENGTH, HeaderValue::from(result.len()))]; - Ok(reply_with(headers, result)) - }, - Err(e) => { - info!("Failed to dump quorum store db: {e:?}"); - Ok(reply_with_status( - StatusCode::INTERNAL_SERVER_ERROR, - e.to_string(), - )) - }, - } -} - -pub async fn handle_dump_block_request( - req: Request, - consensus_db: Arc, - quorum_store_db: Arc, -) -> hyper::Result> { - let query = req.uri().query().unwrap_or(""); - let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query.as_bytes()).collect(); - - let block_id: Option = match query_pairs.get("block_id") { - Some(val) => match val.parse() { - Ok(val) => Some(val), - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => None, - }; - - // TODO(grao): I'm lazy, only support this through query parameters, let me know if this need - // to be done through header. - let bcs: bool = match query_pairs.get("bcs") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => false, - }; - - if let Some(block_id) = block_id { - info!("Dumping block ({block_id:?})."); - } else { - info!("Dumping all blocks."); - } - - match spawn_blocking(move || { - if bcs { - dump_blocks_bcs(consensus_db.as_ref(), quorum_store_db.as_ref(), block_id) - .map(Into::::into) - } else { - dump_blocks(consensus_db.as_ref(), quorum_store_db.as_ref(), block_id).map(Into::into) - } - }) - .await - { - Ok(result) => { - info!("Finished dumping block(s)."); - Ok(reply_with(vec![], result)) - }, - Err(e) => { - info!("Failed to dump block(s): {e:?}"); - Ok(reply_with_status( - StatusCode::INTERNAL_SERVER_ERROR, - e.to_string(), - )) - }, - } -} - -fn dump_consensus_db(consensus_db: &dyn PersistentLivenessStorage) -> anyhow::Result { - let mut body = String::new(); - - let (last_vote, highest_tc, consensus_blocks, consensus_qcs) = - consensus_db.consensus_db().get_data()?; - - body.push_str(&format!("Last vote: \n{last_vote:?}\n\n")); - body.push_str(&format!("Highest tc: \n{highest_tc:?}\n\n")); - body.push_str("Blocks: \n"); - for block in consensus_blocks { - body.push_str(&format!( - "[id: {:?}, author: {:?}, epoch: {}, round: {:02}, parent_id: {:?}, timestamp: {}, payload: {:?}]\n\n", - block.id(), - block.author(), - block.epoch(), - block.round(), - block.parent_id(), - block.timestamp_usecs(), - block.payload(), - )); - } - body.push_str("QCs: \n"); - for qc in consensus_qcs { - body.push_str(&format!("{qc:?}\n\n")); - } - - Ok(body) -} - -fn dump_quorum_store_db( - quorum_store_db: &dyn QuorumStoreStorage, - digest: Option, -) -> anyhow::Result { - let mut body = String::new(); - - if let Some(digest) = digest { - body.push_str(&format!("{digest:?}:\n")); - body.push_str(&format!( - "{:?}", - quorum_store_db.get_batch(&digest).map_err(Error::msg)? - )); - } else { - for (digest, _batch) in quorum_store_db.get_all_batches()? { - body.push_str(&format!("{digest:?}:\n")); - } - } - - Ok(body) -} - -fn dump_blocks( - consensus_db: &dyn PersistentLivenessStorage, - quorum_store_db: &dyn QuorumStoreStorage, - block_id: Option, -) -> anyhow::Result { - let mut body = String::new(); - - let all_batches = quorum_store_db.get_all_batches()?; - - let (_, _, blocks, _) = consensus_db.consensus_db().get_data()?; - - for block in blocks { - let id = block.id(); - if block_id.is_none() || id == block_id.unwrap() { - body.push_str(&format!("Block ({id:?}): \n\n")); - match extract_txns_from_block(&block, &all_batches) { - Ok(txns) => { - body.push_str(&format!("{txns:?}")); - }, - Err(e) => { - body.push_str(&format!("Not available: {e:?}")); - }, - }; - body.push_str("\n\n"); - } - } - - if body.is_empty() { - if let Some(block_id) = block_id { - body.push_str(&format!("Done, block ({block_id:?}) is not found.")); - } else { - body.push_str("Done, no block is found."); - } - } - - Ok(body) -} - -fn dump_blocks_bcs( - consensus_db: &dyn PersistentLivenessStorage, - quorum_store_db: &dyn QuorumStoreStorage, - block_id: Option, -) -> anyhow::Result> { - let all_batches = quorum_store_db.get_all_batches()?; - - let (_, _, blocks, _) = consensus_db.consensus_db().get_data()?; - - let mut all_txns = Vec::new(); - for block in blocks { - let id = block.id(); - if block_id.is_none() || id == block_id.unwrap() { - match extract_txns_from_block(&block, &all_batches) { - Ok(txns) => { - all_txns.extend(txns.into_iter().cloned().map(Transaction::UserTransaction)); - }, - Err(e) => bail!("Failed to extract txns from block ({id:?}): {e:?}."), - }; - } - } - - bcs::to_bytes(&all_txns).map_err(Error::msg) -} diff --git a/crates/aptos-admin-service/src/server/mod.rs b/crates/aptos-admin-service/src/server/mod.rs deleted file mode 100644 index 902a8e300f3e2..0000000000000 --- a/crates/aptos-admin-service/src/server/mod.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::reply_with_status; -use aptos_config::config::{AuthenticationConfig, NodeConfig}; -use aptos_consensus::{ - persistent_liveness_storage::StorageWriteProxy, quorum_store::quorum_store_db::QuorumStoreDB, -}; -use aptos_infallible::RwLock; -use aptos_logger::info; -use aptos_storage_interface::DbReaderWriter; -use hyper::{ - service::{make_service_fn, service_fn}, - Body, Request, Response, Server, StatusCode, -}; -use std::{ - collections::HashMap, - convert::Infallible, - net::{SocketAddr, ToSocketAddrs}, - sync::Arc, -}; -use tokio::runtime::Runtime; - -mod consensus; -#[cfg(target_os = "linux")] -pub mod profiling; -#[cfg(target_os = "linux")] -mod thread_dump; -mod utils; - -#[derive(Default)] -pub struct Context { - authentication_configs: Vec, - - aptos_db: RwLock>>, - consensus_db: RwLock>>, - quorum_store_db: RwLock>>, -} - -impl Context { - fn set_aptos_db(&self, aptos_db: Arc) { - *self.aptos_db.write() = Some(aptos_db); - } - - fn set_consensus_dbs( - &self, - consensus_db: Arc, - quorum_store_db: Arc, - ) { - *self.consensus_db.write() = Some(consensus_db); - *self.quorum_store_db.write() = Some(quorum_store_db); - } -} - -pub struct AdminService { - runtime: Runtime, - context: Arc, -} - -impl AdminService { - /// Starts the admin service that listens on the configured address and handles various endpoint - /// requests. - pub fn new(node_config: &NodeConfig) -> Self { - // Fetch the service port and address - let service_port = node_config.admin_service.port; - let service_address = node_config.admin_service.address.clone(); - - // Create the admin service socket address - let address: SocketAddr = (service_address.as_str(), service_port) - .to_socket_addrs() - .unwrap_or_else(|_| { - panic!( - "Failed to parse {}:{} as address", - service_address, service_port - ) - }) - .next() - .unwrap(); - - // Create a runtime for the admin service - let runtime = aptos_runtimes::spawn_named_runtime("admin".into(), None); - - let admin_service = Self { - runtime, - context: Arc::new(Context { - authentication_configs: node_config.admin_service.authentication_configs.clone(), - ..Default::default() - }), - }; - - // TODO(grao): Consider support enabling the service through an authenticated request. - let enabled = node_config.admin_service.enabled.unwrap_or(false); - admin_service.start(address, enabled); - - admin_service - } - - pub fn set_aptos_db(&self, aptos_db: Arc) { - self.context.set_aptos_db(aptos_db) - } - - pub fn set_consensus_dbs( - &self, - consensus_db: Arc, - quorum_store_db: Arc, - ) { - self.context - .set_consensus_dbs(consensus_db, quorum_store_db) - } - - fn start(&self, address: SocketAddr, enabled: bool) { - let context = self.context.clone(); - self.runtime.spawn(async move { - let make_service = make_service_fn(move |_conn| { - let context = context.clone(); - async move { - Ok::<_, Infallible>(service_fn(move |req| { - Self::serve_requests(context.clone(), req, enabled) - })) - } - }); - - let server = Server::bind(&address).serve(make_service); - info!("Started AdminService at {address:?}, enabled: {enabled}."); - server.await - }); - } - - async fn serve_requests( - context: Arc, - req: Request, - enabled: bool, - ) -> hyper::Result> { - if !enabled { - return Ok(reply_with_status( - StatusCode::NOT_FOUND, - "AdminService is not enabled.", - )); - } - - let mut authenticated = false; - if context.authentication_configs.is_empty() { - authenticated = true; - } else { - for authentication_config in &context.authentication_configs { - match authentication_config { - AuthenticationConfig::PasscodeSha256(passcode_sha256) => { - let query = req.uri().query().unwrap_or(""); - let query_pairs: HashMap<_, _> = - url::form_urlencoded::parse(query.as_bytes()).collect(); - let passcode: Option = - query_pairs.get("passcode").map(|p| p.to_string()); - if let Some(passcode) = passcode { - if sha256::digest(passcode) == *passcode_sha256 { - authenticated = true; - } - } - }, - } - } - }; - - if !authenticated { - return Ok(reply_with_status( - StatusCode::NETWORK_AUTHENTICATION_REQUIRED, - format!("{} endpoint requires authentication.", req.uri().path()), - )); - } - - match (req.method().clone(), req.uri().path()) { - #[cfg(target_os = "linux")] - (hyper::Method::GET, "/profilez") => profiling::handle_cpu_profiling_request(req).await, - #[cfg(target_os = "linux")] - (hyper::Method::GET, "/threadz") => thread_dump::handle_thread_dump_request(req).await, - (hyper::Method::GET, "/debug/consensus/consensusdb") => { - let consensus_db = context.consensus_db.read().clone(); - if let Some(consensus_db) = consensus_db { - consensus::handle_dump_consensus_db_request(req, consensus_db).await - } else { - Ok(reply_with_status( - StatusCode::NOT_FOUND, - "Consensus db is not available.", - )) - } - }, - (hyper::Method::GET, "/debug/consensus/quorumstoredb") => { - let quorum_store_db = context.quorum_store_db.read().clone(); - if let Some(quorum_store_db) = quorum_store_db { - consensus::handle_dump_quorum_store_db_request(req, quorum_store_db).await - } else { - Ok(reply_with_status( - StatusCode::NOT_FOUND, - "Quorum store db is not available.", - )) - } - }, - (hyper::Method::GET, "/debug/consensus/block") => { - let consensus_db = context.consensus_db.read().clone(); - let quorum_store_db = context.quorum_store_db.read().clone(); - if consensus_db.is_some() && quorum_store_db.is_some() { - consensus::handle_dump_block_request( - req, - consensus_db.unwrap(), - quorum_store_db.unwrap(), - ) - .await - } else { - Ok(reply_with_status( - StatusCode::NOT_FOUND, - "Consensus db and/or quorum store db is not available.", - )) - } - }, - _ => Ok(reply_with_status(StatusCode::NOT_FOUND, "Not found.")), - } - } -} diff --git a/crates/aptos-admin-service/src/server/profiling.rs b/crates/aptos-admin-service/src/server/profiling.rs deleted file mode 100644 index 65ed481dc9c03..0000000000000 --- a/crates/aptos-admin-service/src/server/profiling.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::{reply_with, reply_with_status}; -use anyhow::{anyhow, ensure}; -use aptos_logger::info; -use async_mutex::Mutex; -use http::header::{HeaderValue, CONTENT_DISPOSITION, CONTENT_LENGTH, CONTENT_TYPE}; -use hyper::{Body, Request, Response, StatusCode}; -use lazy_static::lazy_static; -use pprof::protos::Message; -use regex::Regex; -use std::{collections::HashMap, time::Duration}; - -lazy_static! { - static ref CPU_PROFILE_MUTEX: Mutex<()> = Mutex::new(()); -} - -pub async fn handle_cpu_profiling_request(req: Request) -> hyper::Result> { - let query = req.uri().query().unwrap_or(""); - let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query.as_bytes()).collect(); - - let seconds: u64 = match query_pairs.get("seconds") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => 10, - }; - - let frequency: i32 = match query_pairs.get("frequency") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => 99, - }; - - let use_proto = match query_pairs.get("format") { - Some(format) => match format.as_ref() { - "proto" => true, - "flamegraph" => false, - _ => { - return Ok(reply_with_status( - StatusCode::BAD_REQUEST, - "Unsupported format.", - )) - }, - }, - _ => true, - }; - - match start_cpu_profiling(seconds, frequency, use_proto).await { - Ok(body) => { - let content_type = if use_proto { - mime::APPLICATION_OCTET_STREAM - } else { - mime::IMAGE_SVG - }; - let headers: Vec<(_, HeaderValue)> = vec![ - (CONTENT_LENGTH, HeaderValue::from(body.len())), - (CONTENT_DISPOSITION, HeaderValue::from_static("inline")), - ( - CONTENT_TYPE, - HeaderValue::from_str(content_type.as_ref()).unwrap(), - ), - ]; - Ok(reply_with(headers, body)) - }, - Err(e) => { - info!("Failed to generate cpu profile: {e:?}"); - Ok(reply_with_status( - StatusCode::INTERNAL_SERVER_ERROR, - e.to_string(), - )) - }, - } -} - -pub async fn start_cpu_profiling( - seconds: u64, - frequency: i32, - use_proto: bool, -) -> anyhow::Result> { - info!( - seconds = seconds, - frequency = frequency, - use_proto = use_proto, - "Starting cpu profiling." - ); - let lock = CPU_PROFILE_MUTEX.try_lock(); - ensure!(lock.is_some(), "A profiling task is already running."); - - // TODO(grao): Consolidate the code with aptos-profiler crate. - let guard = pprof::ProfilerGuard::new(frequency) - .map_err(|e| anyhow!("Failed to start cpu profiling: {e:?}."))?; - - tokio::time::sleep(Duration::from_secs(seconds)).await; - - let mut body = Vec::new(); - let report = guard - .report() - .frames_post_processor(frames_post_processor()) - .build() - .map_err(|e| anyhow!("Failed to generate cpu profiling report: {e:?}."))?; - - if use_proto { - report - .pprof() - .map_err(|e| anyhow!("Failed to generate proto report: {e:?}."))? - .write_to_vec(&mut body) - .map_err(|e| anyhow!("Failed to serialize proto report: {e:?}."))?; - } else { - report - .flamegraph(&mut body) - .map_err(|e| anyhow!("Failed to generate flamegraph report: {e:?}."))?; - } - - info!("Cpu profiling is done."); - - Ok(body) -} - -fn frames_post_processor() -> impl Fn(&mut pprof::Frames) { - let regex = Regex::new(r"^(.*)-(\d*)$").unwrap(); - - move |frames| { - if let Some((_, [name, _])) = regex.captures(&frames.thread_name).map(|c| c.extract()) { - frames.thread_name = name.to_string(); - } - } -} diff --git a/crates/aptos-admin-service/src/server/thread_dump.rs b/crates/aptos-admin-service/src/server/thread_dump.rs deleted file mode 100644 index 31d241e1dd95e..0000000000000 --- a/crates/aptos-admin-service/src/server/thread_dump.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::server::utils::{reply_with, reply_with_status}; -use anyhow::{ensure, Error}; -use aptos_logger::info; -use async_mutex::Mutex; -use http::header::{HeaderValue, CONTENT_LENGTH}; -use hyper::{Body, Request, Response, StatusCode}; -use lazy_static::lazy_static; -use rstack_self::TraceOptions; -use std::{collections::HashMap, env, process::Command}; - -lazy_static! { - static ref THREAD_DUMP_MUTEX: Mutex<()> = Mutex::new(()); -} - -static MAX_NUM_FRAMES_WITHOUT_VERBOSE: usize = 20; - -pub async fn handle_thread_dump_request(req: Request) -> hyper::Result> { - let query = req.uri().query().unwrap_or(""); - let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query.as_bytes()).collect(); - - let snapshot: bool = match query_pairs.get("snapshot") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => false, - }; - - let location: bool = match query_pairs.get("location") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => true, - }; - - let frame_ip: bool = match query_pairs.get("frame_ip") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => false, - }; - - let verbose: bool = match query_pairs.get("verbose") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(err) => return Ok(reply_with_status(StatusCode::BAD_REQUEST, err.to_string())), - }, - None => false, - }; - - info!("Starting dumping stack trace for all threads."); - match do_thread_dump(snapshot, location, frame_ip, verbose).await { - Ok(body) => { - info!("Thread dumping is done."); - let headers: Vec<(_, HeaderValue)> = - vec![(CONTENT_LENGTH, HeaderValue::from(body.len()))]; - Ok(reply_with(headers, body)) - }, - Err(e) => { - info!("Failed to dump threads: {e:?}"); - Ok(reply_with_status( - StatusCode::INTERNAL_SERVER_ERROR, - e.to_string(), - )) - }, - } -} - -async fn do_thread_dump( - snapshot: bool, - location: bool, - frame_ip: bool, - verbose: bool, -) -> anyhow::Result { - let lock = THREAD_DUMP_MUTEX.try_lock(); - ensure!(lock.is_some(), "A thread dumping task is already running."); - - let exe = env::current_exe().unwrap(); - let trace = TraceOptions::new() - .snapshot(snapshot) - .trace(Command::new(exe).arg("--stacktrace")) - .map_err(Error::msg)?; - - let mut wait_threads = Vec::new(); - let mut sleep_threads = Vec::new(); - let mut body = String::new(); - for thread in trace.threads() { - let frames = thread.frames(); - if !frames.is_empty() { - let symbols = frames[0].symbols(); - if !symbols.is_empty() { - if let Some(name) = symbols[0].name() { - if name.contains("epoll_wait") { - wait_threads.push(thread.name()); - continue; - } - - if name.contains("clock_nanosleep") { - sleep_threads.push(thread.name()); - continue; - } - } - } - } - - if frames.len() > 1 { - let symbols = frames[1].symbols(); - if !symbols.is_empty() { - if let Some(name) = symbols[0].name() { - if name.contains("futex_wait") - || name.contains("pthread_cond_wait") - || name.contains("pthread_cond_timedwait") - { - wait_threads.push(thread.name()); - continue; - } - } - } - } - - body.push_str(&format!("Thread {} ({}):\n", thread.id(), thread.name())); - for (count, frame) in frames.iter().enumerate() { - if !verbose && count >= MAX_NUM_FRAMES_WITHOUT_VERBOSE { - break; - } - - if frame_ip { - body.push_str(&format!("Frame ip: {}\n", frame.ip())); - } - for symbol in frame.symbols() { - let name = if let Some(name) = symbol.name() { - name - } else { - "(unknown)" - }; - if location { - let location = if let Some(file) = symbol.file() { - if let Some(line) = symbol.line() { - format!("{}:{line}", file.display()) - } else { - format!("{}", file.display()) - } - } else { - "".into() - }; - body.push_str(&format!("{name}\t\t{location}\n")); - } else { - body.push_str(&format!("{name}\n")); - } - } - } - body.push_str("\n\n"); - } - - body.push_str("Wait threads:"); - for wait_thread in wait_threads { - body.push_str(&format!(" {wait_thread}")); - } - body.push_str("\n\n"); - - body.push_str("Sleep threads:"); - for sleep_thread in sleep_threads { - body.push_str(&format!(" {sleep_thread}")); - } - body.push_str("\n\n"); - - Ok(body) -} diff --git a/crates/aptos-admin-service/src/server/utils.rs b/crates/aptos-admin-service/src/server/utils.rs deleted file mode 100644 index 02f0b6281b9fe..0000000000000 --- a/crates/aptos-admin-service/src/server/utils.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -#![allow(unused)] - -use anyhow::{Error, Result}; -use aptos_logger::debug; -use http::header::{HeaderName, HeaderValue}; -use hyper::{Body, Response, StatusCode}; -use std::{convert::Into, iter::IntoIterator}; - -pub const UNEXPECTED_ERROR_MESSAGE: &str = "An unexpected error was encountered!"; - -pub(crate) async fn spawn_blocking(func: F) -> Result -where - F: FnOnce() -> Result + Send + 'static, - T: Send + 'static, -{ - tokio::task::spawn_blocking(func) - .await - .map_err(Error::msg)? -} - -pub(crate) fn reply_with_status(status_code: StatusCode, message: T) -> Response -where - T: Into, -{ - reply_with_internal(status_code, [], message) -} - -pub(crate) fn reply_with(headers: H, body: T) -> Response -where - H: IntoIterator, - T: Into, -{ - reply_with_internal(StatusCode::OK, headers, body) -} - -fn reply_with_internal(status_code: StatusCode, headers: H, body: T) -> Response -where - H: IntoIterator, - T: Into, -{ - let mut builder = Response::builder().status(status_code); - for (header_name, header_value) in headers { - builder = builder.header(header_name, header_value); - } - - builder.body(body.into()).unwrap_or_else(|e| { - debug!("Error encountered when generating response: {:?}", e); - let mut response = Response::new(Body::from(UNEXPECTED_ERROR_MESSAGE)); - *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - response - }) -} diff --git a/crates/aptos-debugger/Cargo.toml b/crates/aptos-debugger/Cargo.toml deleted file mode 100644 index 463a359be7366..0000000000000 --- a/crates/aptos-debugger/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "aptos-debugger" -version = "0.1.0" -description = "Debugging tools." - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-consensus = { workspace = true } -aptos-db-tool = { workspace = true } -aptos-logger = { workspace = true } -aptos-move-debugger = { workspace = true } -aptos-push-metrics = { workspace = true } -clap = { workspace = true } -tokio = { workspace = true } diff --git a/crates/aptos-debugger/src/lib.rs b/crates/aptos-debugger/src/lib.rs deleted file mode 100644 index ecfc35d8ce81e..0000000000000 --- a/crates/aptos-debugger/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use clap::Parser; - -#[derive(Parser)] -pub enum Cmd { - #[clap(subcommand)] - AptosDb(aptos_db_tool::DBTool), - - Decode(aptos_move_debugger::bcs_txn_decoder::Command), - - DumpPendingTxns(aptos_consensus::util::db_tool::Command), - - #[clap(subcommand)] - Move(aptos_move_debugger::common::Command), -} - -impl Cmd { - pub async fn run(self) -> Result<()> { - match self { - Cmd::AptosDb(cmd) => cmd.run().await, - Cmd::Decode(cmd) => cmd.run().await, - Cmd::DumpPendingTxns(cmd) => cmd.run().await, - Cmd::Move(cmd) => cmd.run().await, - } - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Cmd::command().debug_assert() -} diff --git a/crates/aptos-debugger/src/main.rs b/crates/aptos-debugger/src/main.rs deleted file mode 100644 index 4df804804d703..0000000000000 --- a/crates/aptos-debugger/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use aptos_debugger::Cmd; -use aptos_logger::{Level, Logger}; -use aptos_push_metrics::MetricsPusher; -use clap::Parser; - -#[tokio::main] -async fn main() -> Result<()> { - Logger::new().level(Level::Info).init(); - let _mp = MetricsPusher::start(vec![]); - - Cmd::parse().run().await -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/Cargo.toml b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/Cargo.toml deleted file mode 100644 index 59c0b322a7601..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "aptos-indexer-grpc-cache-worker" -description = "Indexer gRPC worker to collect on-chain data from node gRPC and push to cache." -version = "1.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-indexer-grpc-utils = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-moving-average = { workspace = true } -aptos-protos = { workspace = true } -aptos-runtimes = { workspace = true } -async-trait = { workspace = true } -backoff = { workspace = true } -base64 = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -futures-core = { workspace = true } -once_cell = { workspace = true } -prost = { workspace = true } -redis = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -serde_yaml = { workspace = true } -tokio = { workspace = true } -tonic = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } - -[dev-dependencies] -aptos-config = { workspace = true } -reqwest = { workspace = true } -tempfile = { workspace = true } - -[features] -integration-tests = [] diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md deleted file mode 100644 index 0299c39364a72..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Indexer GRPC cache worker - -Cache worker fetches data from fullnode GRPC and push data to Cache. - -## How to run it. - -* service account json with `read` access to bucket `${file_store_bucket_name}`, e.g., `xxx.json`. - -* `SERVICE_ACCOUNT` env var pointing to service account json file. - -* Run it: `cargo run --release -- -c config.yaml` - -* Yaml Example -```yaml -health_check_port: 8083 -server_config: - fullnode_grpc_address: 0.0.0.0:50052 - file_store_config: - file_store_type: GcsFileStore - gcs_file_store_bucket_name: indexer-grpc-file-store-bucketname - redis_main_instance_address: 127.0.0.1:6379 -``` diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs deleted file mode 100644 index 7623e95a5b673..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod metrics; -pub mod worker; - -use anyhow::{Context, Result}; -use aptos_indexer_grpc_server_framework::RunnableConfig; -use aptos_indexer_grpc_utils::{config::IndexerGrpcFileStoreConfig, types::RedisUrl}; -use serde::{Deserialize, Serialize}; -use url::Url; -use worker::Worker; - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct IndexerGrpcCacheWorkerConfig { - pub fullnode_grpc_address: Url, - pub file_store_config: IndexerGrpcFileStoreConfig, - pub redis_main_instance_address: RedisUrl, - #[serde(default = "default_enable_cache_compression")] - pub enable_cache_compression: bool, -} - -const fn default_enable_cache_compression() -> bool { - false -} - -impl IndexerGrpcCacheWorkerConfig { - pub fn new( - fullnode_grpc_address: Url, - file_store_config: IndexerGrpcFileStoreConfig, - redis_main_instance_address: RedisUrl, - enable_cache_compression: bool, - ) -> Self { - Self { - fullnode_grpc_address, - file_store_config, - redis_main_instance_address, - enable_cache_compression, - } - } -} - -#[async_trait::async_trait] -impl RunnableConfig for IndexerGrpcCacheWorkerConfig { - async fn run(&self) -> Result<()> { - let mut worker = Worker::new( - self.fullnode_grpc_address.clone(), - self.redis_main_instance_address.clone(), - self.file_store_config.clone(), - self.enable_cache_compression, - ) - .await - .context("Failed to create cache worker")?; - worker - .run() - .await - .context("Failed to run cache worker") - .expect("Cache worker failed"); - Ok(()) - } - - fn get_server_name(&self) -> String { - "idxcachewrkr".to_string() - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs deleted file mode 100644 index e40dfa51dfb7e..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use aptos_indexer_grpc_cache_worker::IndexerGrpcCacheWorkerConfig; -use aptos_indexer_grpc_server_framework::ServerArgs; -use clap::Parser; - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[tokio::main] -async fn main() -> Result<()> { - let args = ServerArgs::parse(); - args.run::() - .await - .expect("Cache worker failed to run"); - Ok(()) -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/metrics.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/metrics.rs deleted file mode 100644 index 82b9d053bcd32..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/metrics.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - register_gauge, register_int_counter, register_int_counter_vec, register_int_gauge, Gauge, - IntCounter, IntCounterVec, IntGauge, -}; -use once_cell::sync::Lazy; - -/// Latest processed transaction version. -pub static LATEST_PROCESSED_VERSION: Lazy = Lazy::new(|| { - register_int_gauge!( - "indexer_grpc_cache_worker_latest_processed_version", - "Latest processed transaction version", - ) - .unwrap() -}); - -/// Number of transactions that saved into cache. -pub static PROCESSED_VERSIONS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_grpc_cache_worker_processed_versions", - "Number of transactions that have been processed by cache worker", - ) - .unwrap() -}); - -/// Number of errors that cache worker has encountered. -pub static ERROR_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_cache_worker_errors", - "Number of errors that cache worker has encountered", - &["error_type"] - ) - .unwrap() -}); - -/// Data latency for cache worker based on latest processed transaction. -pub static PROCESSED_LATENCY_IN_SECS: Lazy = Lazy::new(|| { - register_gauge!( - "indexer_grpc_cache_worker_data_latency_in_secs", - "Latency of cache worker based on latest processed transaction", - ) - .unwrap() -}); - -/// Number of transactions in each batch that cache worker has processed. -pub static PROCESSED_BATCH_SIZE: Lazy = Lazy::new(|| { - register_int_gauge!( - "indexer_grpc_cache_worker_processed_batch_size", - "Size of latest processed batch by cache worker", - ) - .unwrap() -}); - -/// Number of waits that cache worker has encountered for file store. -pub static WAIT_FOR_FILE_STORE_COUNTER: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_grpc_cache_worker_wait_for_file_store_counter", - "Number of waits that cache worker has encountered for file store", - ) - .unwrap() -}); diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/worker.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/worker.rs deleted file mode 100644 index 051adfaca691a..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/worker.rs +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::metrics::{ - ERROR_COUNT, LATEST_PROCESSED_VERSION as LATEST_PROCESSED_VERSION_OLD, PROCESSED_BATCH_SIZE, - PROCESSED_VERSIONS_COUNT, WAIT_FOR_FILE_STORE_COUNTER, -}; -use anyhow::{bail, Context, Result}; -use aptos_indexer_grpc_utils::{ - cache_operator::CacheOperator, - compression_util::{FileStoreMetadata, StorageFormat}, - config::IndexerGrpcFileStoreConfig, - counters::{log_grpc_step, IndexerGrpcStep}, - create_grpc_client, - file_store_operator::FileStoreOperator, - types::RedisUrl, -}; -use aptos_moving_average::MovingAverage; -use aptos_protos::internal::fullnode::v1::{ - stream_status::StatusType, transactions_from_node_response::Response, - GetTransactionsFromNodeRequest, TransactionsFromNodeResponse, -}; -use futures::{self, future::join_all, StreamExt}; -use prost::Message; -use tokio::task::JoinHandle; -use tracing::{error, info}; -use url::Url; - -type ChainID = u32; -type StartingVersion = u64; - -const FILE_STORE_VERSIONS_RESERVED: u64 = 150_000; -// Cache worker will wait if filestore is behind by -// `FILE_STORE_VERSIONS_RESERVED` versions -// This is pinging the cache so it's OK to be more aggressive -const CACHE_WORKER_WAIT_FOR_FILE_STORE_MS: u64 = 100; -// This is the time we wait for the file store to be ready. It should only be -// kicked off when there's no metadata in the file store. -const FILE_STORE_METADATA_WAIT_MS: u64 = 2000; - -const SERVICE_TYPE: &str = "cache_worker"; - -pub struct Worker { - /// Redis client. - redis_client: redis::Client, - /// Fullnode grpc address. - fullnode_grpc_address: Url, - /// File store config - file_store: IndexerGrpcFileStoreConfig, - /// Cache storage format. - cache_storage_format: StorageFormat, -} - -/// GRPC data status enum is to identify the data frame. -/// One stream may contain multiple batches and one batch may contain multiple data chunks. -pub(crate) enum GrpcDataStatus { - /// Ok status with processed count. - /// Each batch may contain multiple data chunks(like 1000 transactions). - /// These data chunks may be out of order. - ChunkDataOk { - num_of_transactions: u64, - task: tokio::task::JoinHandle>, - }, - /// Init signal received with start version of current stream. - /// No two `Init` signals will be sent in the same stream. - StreamInit(u64), - /// End signal received with batch end version(inclusive). - /// Start version and its number of transactions are included for current batch. - BatchEnd { - start_version: u64, - num_of_transactions: u64, - }, -} - -impl Worker { - pub async fn new( - fullnode_grpc_address: Url, - redis_main_instance_address: RedisUrl, - file_store: IndexerGrpcFileStoreConfig, - enable_cache_compression: bool, - ) -> Result { - let cache_storage_format = if enable_cache_compression { - StorageFormat::GzipCompressedProto - } else { - StorageFormat::Base64UncompressedProto - }; - let redis_client = redis::Client::open(redis_main_instance_address.0.clone()) - .with_context(|| { - format!( - "[Indexer Cache] Failed to create redis client for {}", - redis_main_instance_address - ) - })?; - Ok(Self { - redis_client, - file_store, - fullnode_grpc_address, - cache_storage_format, - }) - } - - /// The main loop of the worker is: - /// 1. Fetch metadata from file store; if not present, exit after 1 minute. - /// 2. Start the streaming RPC with version from file store or 0 if not present. - /// 3. Handle the INIT frame from TransactionsFromNodeResponse: - /// * If metadata is not present and cache is empty, start from 0. - /// * If metadata is not present and cache is not empty, crash. - /// * If metadata is present, start from file store version. - /// 4. Process the streaming response. - /// TODO: Use the ! return type when it is stable. - /// TODO: Rewrite logic to actually conform to this description - pub async fn run(&mut self) -> Result<()> { - // Re-connect if lost. - loop { - let conn = self - .redis_client - .get_tokio_connection_manager() - .await - .context("Get redis connection failed.")?; - let mut rpc_client = create_grpc_client(self.fullnode_grpc_address.clone()).await; - - // 1. Fetch metadata. - let file_store_operator: Box = self.file_store.create(); - // TODO: move chain id check somewhere around here - // This ensures that metadata is created before we start the cache worker - let mut starting_version = file_store_operator.get_latest_version().await; - while starting_version.is_none() { - starting_version = file_store_operator.get_latest_version().await; - tracing::warn!( - "[Indexer Cache] File store metadata not found. Waiting for {} ms.", - FILE_STORE_METADATA_WAIT_MS - ); - tokio::time::sleep(std::time::Duration::from_millis( - FILE_STORE_METADATA_WAIT_MS, - )) - .await; - } - - // There's a guarantee at this point that starting_version is not null - let starting_version = starting_version.unwrap(); - - let file_store_metadata = file_store_operator.get_file_store_metadata().await.unwrap(); - - tracing::info!( - service_type = SERVICE_TYPE, - "[Indexer Cache] Starting cache worker with version {}", - starting_version - ); - - // 2. Start streaming RPC. - let request = tonic::Request::new(GetTransactionsFromNodeRequest { - starting_version: Some(starting_version), - ..Default::default() - }); - - let response = rpc_client - .get_transactions_from_node(request) - .await - .with_context(|| { - format!( - "Failed to get transactions from node at starting version {}", - starting_version - ) - })?; - info!( - service_type = SERVICE_TYPE, - "[Indexer Cache] Streaming RPC started." - ); - // 3&4. Infinite streaming until error happens. Either stream ends or worker crashes. - process_streaming_response( - conn, - self.cache_storage_format, - file_store_metadata, - response.into_inner(), - ) - .await?; - - info!( - service_type = SERVICE_TYPE, - "[Indexer Cache] Streaming RPC ended." - ); - } - } -} - -async fn process_transactions_from_node_response( - response: TransactionsFromNodeResponse, - cache_operator: &mut CacheOperator, - download_start_time: std::time::Instant, -) -> Result { - let size_in_bytes = response.encoded_len(); - match response.response.unwrap() { - Response::Status(status) => { - match StatusType::try_from(status.r#type).expect("[Indexer Cache] Invalid status type.") - { - StatusType::Init => Ok(GrpcDataStatus::StreamInit(status.start_version)), - StatusType::BatchEnd => { - let start_version = status.start_version; - let num_of_transactions = status - .end_version - .expect("TransactionsFromNodeResponse status end_version is None") - - start_version - + 1; - Ok(GrpcDataStatus::BatchEnd { - start_version, - num_of_transactions, - }) - }, - StatusType::Unspecified => unreachable!("Unspecified status type."), - } - }, - Response::Data(data) => { - let transaction_len = data.transactions.len(); - let data_download_duration_in_secs = download_start_time.elapsed().as_secs_f64(); - let mut cache_operator_clone = cache_operator.clone(); - let task: JoinHandle> = tokio::spawn({ - let first_transaction = data - .transactions - .first() - .context("There were unexpectedly no transactions in the response")?; - let first_transaction_version = first_transaction.version; - let last_transaction = data - .transactions - .last() - .context("There were unexpectedly no transactions in the response")?; - let last_transaction_version = last_transaction.version; - let start_version = first_transaction.version; - let first_transaction_pb_timestamp = first_transaction.timestamp.clone(); - let last_transaction_pb_timestamp = last_transaction.timestamp.clone(); - - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::CacheWorkerReceivedTxns, - Some(start_version as i64), - Some(last_transaction_version as i64), - first_transaction_pb_timestamp.as_ref(), - last_transaction_pb_timestamp.as_ref(), - Some(data_download_duration_in_secs), - Some(size_in_bytes), - Some((last_transaction_version + 1 - first_transaction_version) as i64), - None, - ); - - let cache_update_start_time = std::time::Instant::now(); - - async move { - // Push to cache. - match cache_operator_clone - .update_cache_transactions(data.transactions) - .await - { - Ok(_) => { - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::CacheWorkerTxnsProcessed, - Some(first_transaction_version as i64), - Some(last_transaction_version as i64), - first_transaction_pb_timestamp.as_ref(), - last_transaction_pb_timestamp.as_ref(), - Some(cache_update_start_time.elapsed().as_secs_f64()), - Some(size_in_bytes), - Some( - (last_transaction_version + 1 - first_transaction_version) - as i64, - ), - None, - ); - Ok(()) - }, - Err(e) => { - ERROR_COUNT - .with_label_values(&["failed_to_update_cache_version"]) - .inc(); - bail!("Update cache with version failed: {}", e); - }, - } - } - }); - - Ok(GrpcDataStatus::ChunkDataOk { - num_of_transactions: transaction_len as u64, - task, - }) - }, - } -} - -// Setup the cache operator with init signal, including chain id and starting version from fullnode. -async fn verify_fullnode_init_signal( - cache_operator: &mut CacheOperator, - init_signal: TransactionsFromNodeResponse, - file_store_metadata: FileStoreMetadata, -) -> Result<(ChainID, StartingVersion)> { - let (fullnode_chain_id, starting_version) = match init_signal - .response - .expect("[Indexer Cache] Response type does not exist.") - { - Response::Status(status_frame) => { - match StatusType::try_from(status_frame.r#type) - .expect("[Indexer Cache] Invalid status type.") - { - StatusType::Init => (init_signal.chain_id, status_frame.start_version), - _ => { - bail!("[Indexer Cache] Streaming error: first frame is not INIT signal."); - }, - } - }, - _ => { - bail!("[Indexer Cache] Streaming error: first frame is not siganl frame."); - }, - }; - - // Guaranteed that chain id is here at this point because we already ensure that fileworker did the set up - let chain_id = cache_operator.get_chain_id().await?.unwrap(); - if chain_id != fullnode_chain_id as u64 { - bail!("[Indexer Cache] Chain ID mismatch between fullnode init signal and cache."); - } - - // It's required to start the worker with the same version as file store. - if file_store_metadata.version != starting_version { - bail!("[Indexer Cache] Starting version mismatch between filestore metadata and fullnode init signal."); - } - if file_store_metadata.chain_id != fullnode_chain_id as u64 { - bail!("[Indexer Cache] Chain id mismatch between filestore metadata and fullnode."); - } - - Ok((fullnode_chain_id, starting_version)) -} - -/// Infinite streaming processing. Retry if error happens; crash if fatal. -async fn process_streaming_response( - conn: redis::aio::ConnectionManager, - cache_storage_format: StorageFormat, - file_store_metadata: FileStoreMetadata, - mut resp_stream: impl futures_core::Stream> - + std::marker::Unpin, -) -> Result<()> { - let mut tps_calculator = MovingAverage::new(10_000); - let mut transaction_count = 0; - // 3. Set up the cache operator with init signal. - let init_signal = match resp_stream.next().await { - Some(Ok(r)) => r, - _ => { - bail!("[Indexer Cache] Streaming error: no response."); - }, - }; - let mut cache_operator = CacheOperator::new(conn, cache_storage_format); - - let (fullnode_chain_id, starting_version) = - verify_fullnode_init_signal(&mut cache_operator, init_signal, file_store_metadata) - .await - .context("[Indexer Cache] Failed to verify init signal")?; - - let mut current_version = starting_version; - let mut batch_start_time = std::time::Instant::now(); - - let mut tasks_to_run = vec![]; - // 4. Process the streaming response. - loop { - let download_start_time = std::time::Instant::now(); - let received = match resp_stream.next().await { - Some(r) => r, - _ => { - error!( - service_type = SERVICE_TYPE, - "[Indexer Cache] Streaming error: no response." - ); - ERROR_COUNT.with_label_values(&["streaming_error"]).inc(); - break; - }, - }; - // 10 batches doewnload + slowest processing& uploading task - let received: TransactionsFromNodeResponse = match received { - Ok(r) => r, - Err(err) => { - error!( - service_type = SERVICE_TYPE, - "[Indexer Cache] Streaming error: {}", err - ); - ERROR_COUNT.with_label_values(&["streaming_error"]).inc(); - break; - }, - }; - - if received.chain_id as u64 != fullnode_chain_id as u64 { - panic!("[Indexer Cache] Chain id mismatch happens during data streaming."); - } - - let size_in_bytes = received.encoded_len(); - match process_transactions_from_node_response( - received, - &mut cache_operator, - download_start_time, - ) - .await - { - Ok(status) => match status { - GrpcDataStatus::ChunkDataOk { - num_of_transactions, - task, - } => { - current_version += num_of_transactions; - transaction_count += num_of_transactions; - tps_calculator.tick_now(num_of_transactions); - - PROCESSED_VERSIONS_COUNT.inc_by(num_of_transactions); - // TODO: Reasses whether this metric useful - LATEST_PROCESSED_VERSION_OLD.set(current_version as i64); - PROCESSED_BATCH_SIZE.set(num_of_transactions as i64); - tasks_to_run.push(task); - }, - GrpcDataStatus::StreamInit(new_version) => { - error!( - current_version = new_version, - "[Indexer Cache] Init signal received twice." - ); - ERROR_COUNT.with_label_values(&["data_init_twice"]).inc(); - break; - }, - GrpcDataStatus::BatchEnd { - start_version, - num_of_transactions, - } => { - // Handle the data multithreading. - let result = join_all(tasks_to_run).await; - if result - .iter() - .any(|r| (r.is_err() || r.as_ref().unwrap().is_err())) - { - error!( - start_version = start_version, - num_of_transactions = num_of_transactions, - "[Indexer Cache] Process transactions from fullnode failed." - ); - ERROR_COUNT.with_label_values(&["response_error"]).inc(); - panic!("Error happens when processing transactions from fullnode."); - } - // Cleanup. - tasks_to_run = vec![]; - info!( - start_version = start_version, - num_of_transactions = num_of_transactions, - "[Indexer Cache] End signal received for current batch.", - ); - if current_version != start_version + num_of_transactions { - error!( - current_version = current_version, - actual_current_version = start_version + num_of_transactions, - "[Indexer Cache] End signal received with wrong version." - ); - ERROR_COUNT - .with_label_values(&["data_end_wrong_version"]) - .inc(); - break; - } - cache_operator - .update_cache_latest_version(transaction_count, current_version) - .await - .context("Failed to update the latest version in the cache")?; - transaction_count = 0; - - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::CacheWorkerBatchProcessed, - Some(start_version as i64), - Some((start_version + num_of_transactions - 1) as i64), - None, - None, - Some(batch_start_time.elapsed().as_secs_f64()), - Some(size_in_bytes), - Some(num_of_transactions as i64), - None, - ); - batch_start_time = std::time::Instant::now(); - }, - }, - Err(e) => { - error!( - start_version = current_version, - chain_id = fullnode_chain_id, - service_type = SERVICE_TYPE, - "[Indexer Cache] Process transactions from fullnode failed: {}", - e - ); - ERROR_COUNT.with_label_values(&["response_error"]).inc(); - break; - }, - } - - // Check if the file store isn't too far away - loop { - let file_store_version = cache_operator - .get_file_store_latest_version() - .await? - .unwrap(); - if file_store_version + FILE_STORE_VERSIONS_RESERVED < current_version { - tokio::time::sleep(std::time::Duration::from_millis( - CACHE_WORKER_WAIT_FOR_FILE_STORE_MS, - )) - .await; - tracing::warn!( - current_version = current_version, - file_store_version = file_store_version, - "[Indexer Cache] File store version is behind current version too much." - ); - WAIT_FOR_FILE_STORE_COUNTER.inc(); - } else { - // File store is up to date, continue cache update. - break; - } - } - } - - // It is expected that we get to this point, the upstream server disconnects - // clients after 5 minutes. - Ok(()) -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/Cargo.toml b/ecosystem/indexer-grpc/indexer-grpc-data-service/Cargo.toml deleted file mode 100644 index 3bae66c31c8a7..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "aptos-indexer-grpc-data-service" -description = "Aptos Indexer gRPC data service to serve the data from cache and file store." -version = "1.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-indexer-grpc-data-access = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-indexer-grpc-utils = { workspace = true } -# We introduce this only for sampling purpose. -aptos-logger = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-moving-average = { workspace = true } -aptos-protos = { workspace = true } -aptos-runtimes = { workspace = true } -async-trait = { workspace = true } -base64 = { workspace = true } -clap = { workspace = true } -cloud-storage = { workspace = true } -futures = { workspace = true } -once_cell = { workspace = true } -prost = { workspace = true } -redis = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -tokio-stream = { workspace = true } -tonic = { workspace = true } -tonic-reflection = { workspace = true } -tracing = { workspace = true } -uuid = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/README.md b/ecosystem/indexer-grpc/indexer-grpc-data-service/README.md deleted file mode 100644 index 11ec4d07d9d7f..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Indexer GRPC data service - -Indexer GRPC data service fetches data from both cache and file store. - -## How to run it. - -* service account json with `read` access to bucket `${file_store_bucket_name}`, e.g., `xxx.json`. - -* `SERVICE_ACCOUNT` env var pointing to service account json file. - -* Run it: `cargo run --release -- -c config.yaml` - -* Yaml Example - -```yaml -health_check_port: 8083 -server_config: - whitelisted_auth_tokens: - - "token1" - - "token2" - file_store_config: - file_store_type: GcsFileStore - gcs_file_store_bucket_name: indexer-grpc-file-store-bucketname - data_service_grpc_tls_config: - data_service_grpc_listen_address: 0.0.0.0:50052 - cert_path: /path/to/cert.cert - key_path: /path/to/key.pem - data_service_grpc_non_tls_config: - data_service_grpc_listen_address: 0.0.0.0:50051 - redis_read_replica_address: 127.0.0.1:6379 -``` - -### Config Explanation - -* `whitelisted_auth_tokens`: list of tokens that are allowed to consume the endpoint. -* `file_store_config`: GCS to archive the transactions -* `data_service_grpc_tls_config`: TLS endpoint exposed - * GPRC endpoint with TLS, i.e., https. It's ok to expose tls endpoint only. - * We introduce it here(in a non mutual-exclusive way) to avoid potential compatibility issue for clients. -* `data_service_grpc_non_tls_config`: Non-TLS endpoint exposed - * GRPC endpoint without TLS, i.e., http. It's ok to expose non-tls only. - -### HTTP2-ping-based liveness check - -Long-live connections are prune to network errors. We introduce HTTP2 ping check to actively detect if -connection is broken. - -* `HTTP2_PING_INTERVAL_DURATION`: const value for http2 ping interval; default to 60s. -* `HTTP2_PING_TIMEOUT_DURATION`: const value for http2 ping timeout; default to 10s. - -Note: this requires http2 ping support, e.g., AWS/ALB may not work(https://stackoverflow.com/questions/66818645/http2-ping-frames-over-aws-alb-grpc-keepalive-ping). - -## How to use grpc web UI -Install the tool, for example on Mac: -``` -brew install grpcui -``` - -Run the server: -``` -cargo run --release -- -c config.yaml -``` - -Open the web UI: -``` -grpcui -plaintext 127.0.0.1:50052 -``` - -The port used here should match the port used in `data_service_grpc_listen_address` in the config file. diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/config.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/config.rs deleted file mode 100644 index 0fe77472b9b21..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/config.rs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::service::RawDataServerWrapper; -use anyhow::{bail, Result}; -use aptos_indexer_grpc_server_framework::RunnableConfig; -use aptos_indexer_grpc_utils::{ - compression_util::StorageFormat, config::IndexerGrpcFileStoreConfig, types::RedisUrl, -}; -use aptos_protos::{ - indexer::v1::FILE_DESCRIPTOR_SET as INDEXER_V1_FILE_DESCRIPTOR_SET, - transaction::v1::FILE_DESCRIPTOR_SET as TRANSACTION_V1_TESTING_FILE_DESCRIPTOR_SET, - util::timestamp::FILE_DESCRIPTOR_SET as UTIL_TIMESTAMP_FILE_DESCRIPTOR_SET, -}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, net::SocketAddr}; -use tonic::{ - codec::CompressionEncoding, - codegen::InterceptedService, - metadata::{Ascii, MetadataValue}, - transport::Server, - Request, Status, -}; - -pub const SERVER_NAME: &str = "idxdatasvc"; - -// Default max response channel size. -const DEFAULT_MAX_RESPONSE_CHANNEL_SIZE: usize = 3; - -// HTTP2 ping interval and timeout. -// This can help server to garbage collect dead connections. -// tonic server: https://docs.rs/tonic/latest/tonic/transport/server/struct.Server.html#method.http2_keepalive_interval -const HTTP2_PING_INTERVAL_DURATION: std::time::Duration = std::time::Duration::from_secs(60); -const HTTP2_PING_TIMEOUT_DURATION: std::time::Duration = std::time::Duration::from_secs(10); - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct TlsConfig { - /// The address for the TLS GRPC server to listen on. - pub data_service_grpc_listen_address: SocketAddr, - pub cert_path: String, - pub key_path: String, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct NonTlsConfig { - /// The address for the TLS GRPC server to listen on. - pub data_service_grpc_listen_address: SocketAddr, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct IndexerGrpcDataServiceConfig { - /// If given, we will run a server that uses TLS. - pub data_service_grpc_tls_config: Option, - /// If given, we will run a server that does not use TLS. - pub data_service_grpc_non_tls_config: Option, - /// The size of the response channel that response can be buffered. - #[serde(default = "IndexerGrpcDataServiceConfig::default_data_service_response_channel_size")] - pub data_service_response_channel_size: usize, - /// A list of auth tokens that are allowed to access the service. - #[serde(default)] - pub whitelisted_auth_tokens: Vec, - /// If set, don't check for auth tokens. - #[serde(default)] - pub disable_auth_check: bool, - /// File store config. - pub file_store_config: IndexerGrpcFileStoreConfig, - /// Redis read replica address. - pub redis_read_replica_address: RedisUrl, - /// Support compressed cache data. - #[serde(default = "IndexerGrpcDataServiceConfig::default_enable_cache_compression")] - pub enable_cache_compression: bool, -} - -impl IndexerGrpcDataServiceConfig { - pub fn new( - data_service_grpc_tls_config: Option, - data_service_grpc_non_tls_config: Option, - data_service_response_channel_size: Option, - whitelisted_auth_tokens: Vec, - disable_auth_check: bool, - file_store_config: IndexerGrpcFileStoreConfig, - redis_read_replica_address: RedisUrl, - enable_cache_compression: bool, - ) -> Self { - Self { - data_service_grpc_tls_config, - data_service_grpc_non_tls_config, - data_service_response_channel_size: data_service_response_channel_size - .unwrap_or_else(Self::default_data_service_response_channel_size), - whitelisted_auth_tokens, - disable_auth_check, - file_store_config, - redis_read_replica_address, - enable_cache_compression, - } - } - - pub const fn default_data_service_response_channel_size() -> usize { - DEFAULT_MAX_RESPONSE_CHANNEL_SIZE - } - - pub const fn default_enable_cache_compression() -> bool { - false - } -} - -#[async_trait::async_trait] -impl RunnableConfig for IndexerGrpcDataServiceConfig { - fn validate(&self) -> Result<()> { - if self.disable_auth_check && !self.whitelisted_auth_tokens.is_empty() { - bail!("disable_auth_check is set but whitelisted_auth_tokens is not empty"); - } - if !self.disable_auth_check && self.whitelisted_auth_tokens.is_empty() { - bail!("disable_auth_check is not set but whitelisted_auth_tokens is empty"); - } - if self.data_service_grpc_non_tls_config.is_none() - && self.data_service_grpc_tls_config.is_none() - { - bail!("At least one of data_service_grpc_non_tls_config and data_service_grpc_tls_config must be set"); - } - Ok(()) - } - - async fn run(&self) -> Result<()> { - let token_set = build_auth_token_set(self.whitelisted_auth_tokens.clone()); - let disable_auth_check = self.disable_auth_check; - let authentication_inceptor = - move |req: Request<()>| -> std::result::Result, Status> { - if disable_auth_check { - return std::result::Result::Ok(req); - } - let metadata = req.metadata(); - if let Some(token) = - metadata.get(aptos_indexer_grpc_utils::constants::GRPC_AUTH_TOKEN_HEADER) - { - if token_set.contains(token) { - std::result::Result::Ok(req) - } else { - Err(Status::unauthenticated("Invalid token")) - } - } else { - Err(Status::unauthenticated("Missing token")) - } - }; - let reflection_service = tonic_reflection::server::Builder::configure() - // Note: It is critical that the file descriptor set is registered for every - // file that the top level API proto depends on recursively. If you don't, - // compilation will still succeed but reflection will fail at runtime. - // - // TODO: Add a test for this / something in build.rs, this is a big footgun. - .register_encoded_file_descriptor_set(INDEXER_V1_FILE_DESCRIPTOR_SET) - .register_encoded_file_descriptor_set(TRANSACTION_V1_TESTING_FILE_DESCRIPTOR_SET) - .register_encoded_file_descriptor_set(UTIL_TIMESTAMP_FILE_DESCRIPTOR_SET) - .build() - .map_err(|e| anyhow::anyhow!("Failed to build reflection service: {}", e))?; - - let cache_storage_format: StorageFormat = if self.enable_cache_compression { - StorageFormat::GzipCompressedProto - } else { - StorageFormat::Base64UncompressedProto - }; - // Add authentication interceptor. - let server = RawDataServerWrapper::new( - self.redis_read_replica_address.clone(), - self.file_store_config.clone(), - self.data_service_response_channel_size, - cache_storage_format, - )?; - let svc = aptos_protos::indexer::v1::raw_data_server::RawDataServer::new(server) - .send_compressed(CompressionEncoding::Gzip) - .accept_compressed(CompressionEncoding::Gzip) - .accept_compressed(CompressionEncoding::Zstd); - let svc_with_interceptor = InterceptedService::new(svc, authentication_inceptor); - - let svc_with_interceptor_clone = svc_with_interceptor.clone(); - let reflection_service_clone = reflection_service.clone(); - - let mut tasks = vec![]; - if let Some(config) = &self.data_service_grpc_non_tls_config { - let listen_address = config.data_service_grpc_listen_address; - tracing::info!( - grpc_address = listen_address.to_string().as_str(), - "[data service] starting gRPC server with non-TLS." - ); - tasks.push(tokio::spawn(async move { - Server::builder() - .http2_keepalive_interval(Some(HTTP2_PING_INTERVAL_DURATION)) - .http2_keepalive_timeout(Some(HTTP2_PING_TIMEOUT_DURATION)) - .add_service(svc_with_interceptor_clone) - .add_service(reflection_service_clone) - .serve(listen_address) - .await - .map_err(|e| anyhow::anyhow!(e)) - })); - } - if let Some(config) = &self.data_service_grpc_tls_config { - let listen_address = config.data_service_grpc_listen_address; - let cert = tokio::fs::read(config.cert_path.clone()).await?; - let key = tokio::fs::read(config.key_path.clone()).await?; - let identity = tonic::transport::Identity::from_pem(cert, key); - tracing::info!( - grpc_address = listen_address.to_string().as_str(), - "[Data Service] Starting gRPC server with TLS." - ); - tasks.push(tokio::spawn(async move { - Server::builder() - .http2_keepalive_interval(Some(HTTP2_PING_INTERVAL_DURATION)) - .http2_keepalive_timeout(Some(HTTP2_PING_TIMEOUT_DURATION)) - .tls_config(tonic::transport::ServerTlsConfig::new().identity(identity))? - .add_service(svc_with_interceptor) - .add_service(reflection_service) - .serve(listen_address) - .await - .map_err(|e| anyhow::anyhow!(e)) - })); - } - - if tasks.is_empty() { - return Err(anyhow::anyhow!("No grpc config provided")); - } - - futures::future::try_join_all(tasks).await?; - Ok(()) - } - - fn get_server_name(&self) -> String { - SERVER_NAME.to_string() - } -} - -/// Build a set of whitelisted auth tokens. Invalid tokens are ignored. -pub fn build_auth_token_set(whitelisted_auth_tokens: Vec) -> HashSet> { - whitelisted_auth_tokens - .into_iter() - .map(|token| token.parse::>()) - .filter_map(Result::ok) - .collect::>() -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/grpc_response_stream.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/grpc_response_stream.rs deleted file mode 100644 index 9e85a39553d51..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/grpc_response_stream.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::response_dispatcher::{GrpcResponseDispatcher, ResponseDispatcher}; -use aptos_indexer_grpc_data_access::StorageClient; -use aptos_protos::indexer::v1::TransactionsResponse; -use futures::Stream; -use tokio::sync::mpsc::channel; -use tonic::Status; - -/// GrpcResponseStream is a struct that provides a stream of responses to the gRPC server. -/// The response stream is backed by a channel that is filled by GrpcResponseGenerator in another thread. -/// TODO: Add generic support for other types of responses or server-side transformations. -pub struct GrpcResponseStream { - /// The channel for receiving responses from upstream clients. - inner: tokio_stream::wrappers::ReceiverStream>, -} - -impl GrpcResponseStream { - #[allow(dead_code)] - pub fn new( - starting_version: u64, - transaction_count: Option, - buffer_size: Option, - storages: &[StorageClient], - ) -> anyhow::Result { - let (channel_sender, channel_receiver) = channel(buffer_size.unwrap_or(12)); - let response_stream = Self { - inner: tokio_stream::wrappers::ReceiverStream::new(channel_receiver), - }; - let storages = storages.to_vec(); - // Start a separate thread to generate the response for the stream. - tokio::spawn(async move { - let mut response_dispatcher = GrpcResponseDispatcher::new( - starting_version, - transaction_count, - channel_sender, - storages.as_slice(), - ); - match response_dispatcher.run().await { - Ok(_) => { - tracing::info!("Response dispatcher finished successfully."); - }, - Err(e) => { - tracing::error!("Response dispatcher failed: {}", e); - }, - } - }); - Ok(response_stream) - } -} - -impl Stream for GrpcResponseStream { - type Item = Result; - - fn poll_next( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let this = self.get_mut(); - std::pin::Pin::new(&mut this.inner).poll_next(cx) - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/lib.rs deleted file mode 100644 index cd268d1367a10..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod config; -mod grpc_response_stream; -mod metrics; -mod response_dispatcher; -mod service; - -pub use config::{IndexerGrpcDataServiceConfig, NonTlsConfig, SERVER_NAME}; diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/main.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/main.rs deleted file mode 100644 index 265054ba3cddd..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use aptos_indexer_grpc_data_service::IndexerGrpcDataServiceConfig; -use aptos_indexer_grpc_server_framework::ServerArgs; -use clap::Parser; - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[tokio::main] -async fn main() -> Result<()> { - let args = ServerArgs::parse(); - args.run::().await -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/metrics.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/metrics.rs deleted file mode 100644 index a605f4a313d78..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/metrics.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{ - register_gauge_vec, register_histogram_vec, register_int_counter_vec, register_int_gauge_vec, - GaugeVec, HistogramVec, IntCounterVec, IntGaugeVec, -}; -use once_cell::sync::Lazy; - -/// Latest processed transaction version. -pub static LATEST_PROCESSED_VERSION: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "indexer_grpc_data_service_with_user_latest_processed_version", - "Latest processed transaction version", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Number of transactions that served by data service. -pub static PROCESSED_VERSIONS_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_data_service_with_user_processed_versions", - "Number of transactions that have been processed by data service", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Number of errors that data service has encountered. -pub static ERROR_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_data_service_error", - "Number of errors that data service has encountered", - &["error_type"] - ) - .unwrap() -}); - -/// Data latency for data service based on latest processed transaction based on selected processor. -pub static PROCESSED_LATENCY_IN_SECS: Lazy = Lazy::new(|| { - register_gauge_vec!( - "indexer_grpc_data_service_with_user_latest_data_latency_in_secs", - "Latency of data service based on latest processed transaction", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Data latency for data service based on latest processed transaction for all processors. -pub static PROCESSED_LATENCY_IN_SECS_ALL: Lazy = Lazy::new(|| { - register_histogram_vec!( - "indexer_grpc_data_service_latest_data_latency_in_secs_all", - "Latency of data service based on latest processed transaction", - &["request_token"] - ) - .unwrap() -}); - -/// Number of transactions in each batch that data service has processed. -pub static PROCESSED_BATCH_SIZE: Lazy = Lazy::new(|| { - register_int_gauge_vec!( - "indexer_grpc_data_service_with_user_processed_batch_size", - "Size of latest processed batch by data service", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Count of connections that data service has established. -pub static CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_data_service_connection_count_v2", - "Count of connections that data service has established", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Count of the short connections; i.e., < 10 seconds. -pub static SHORT_CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_data_service_short_connection_by_user_processor_count", - "Count of the short connections; i.e., < 10 seconds", - &["request_token", "email", "processor"], - ) - .unwrap() -}); - -/// Count of bytes transfered to the client. This only represents the bytes prepared and ready -/// to send to the client. It does not represent the bytes actually sent to the client. -pub static BYTES_READY_TO_TRANSFER_FROM_SERVER: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "indexer_grpc_data_service_bytes_ready_to_transfer_from_server", - "Count of bytes ready to transfer to the client", - &["request_token", "email", "processor"], - ) - .unwrap() -}); diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/grpc_response_dispatcher.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/grpc_response_dispatcher.rs deleted file mode 100644 index 9d2d96ad8a157..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/grpc_response_dispatcher.rs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::response_dispatcher::ResponseDispatcher; -use aptos_indexer_grpc_data_access::{ - access_trait::{StorageReadError, StorageReadStatus, StorageTransactionRead}, - StorageClient, -}; -use aptos_indexer_grpc_utils::{chunk_transactions, constants::MESSAGE_SIZE_LIMIT}; -use aptos_logger::prelude::{sample, SampleRate}; -use aptos_protos::indexer::v1::TransactionsResponse; -use std::time::Duration; -use tokio::sync::mpsc::Sender; -use tonic::Status; - -// The server will retry to send the response to the client and give up after RESPONSE_CHANNEL_SEND_TIMEOUT. -// This is to prevent the server from being occupied by a slow client. -const RESPONSE_CHANNEL_SEND_TIMEOUT: Duration = Duration::from_secs(120); -// Number of retries for fetching responses from upstream. -const FETCH_RETRY_COUNT: usize = 100; -const RETRY_BACKOFF_IN_MS: u64 = 500; -const NOT_AVAILABLE_RETRY_BACKOFF_IN_MS: u64 = 10; -const WAIT_TIME_BEFORE_CLOUSING_IN_MS: u64 = 60_000; -const RESPONSE_DISPATCH_NAME: &str = "GrpcResponseDispatcher"; - -pub struct GrpcResponseDispatcher { - next_version_to_process: u64, - transaction_count: Option, - sender: Sender>, - storages: Vec, - sender_capacity: usize, -} - -impl GrpcResponseDispatcher { - // Fetches the next batch of responses from storage. - // This is a stateless function that only fetches from storage based on current state. - async fn fetch_from_storages(&self) -> Result, StorageReadError> { - if let Some(transaction_count) = self.transaction_count { - if transaction_count == 0 { - return Ok(vec![]); - } - } - // Loop to wait for the next storage to be available. - let mut previous_storage_not_found = false; - loop { - if self.sender.is_closed() { - return Err(StorageReadError::PermenantError( - RESPONSE_DISPATCH_NAME, - anyhow::anyhow!("Sender is closed."), - )); - } - for storage in self.storages.as_slice() { - let metadata = storage.get_metadata().await?; - match storage - .get_transactions(self.next_version_to_process, None) - .await - { - Ok(StorageReadStatus::Ok(transactions)) => { - let responses = chunk_transactions(transactions, MESSAGE_SIZE_LIMIT); - return Ok(responses - .into_iter() - .map(|transactions| TransactionsResponse { - transactions, - chain_id: Some(metadata.chain_id), - }) - .collect()); - }, - Ok(StorageReadStatus::NotAvailableYet) => { - // This is fatal; it means previous storage evicts the data before the current storage has it. - if previous_storage_not_found { - return Err(StorageReadError::PermenantError( - RESPONSE_DISPATCH_NAME, - anyhow::anyhow!("Gap detected between storages."), - )); - } - // If the storage is not available yet, retry the storages. - tokio::time::sleep(Duration::from_millis( - NOT_AVAILABLE_RETRY_BACKOFF_IN_MS, - )) - .await; - break; - }, - Ok(StorageReadStatus::NotFound) => { - // Continue to the next storage. - previous_storage_not_found = true; - continue; - }, - Err(e) => { - return Err(e); - }, - } - } - - if previous_storage_not_found { - return Err(StorageReadError::PermenantError( - RESPONSE_DISPATCH_NAME, - anyhow::anyhow!("Gap detected between storages."), - )); - } - } - } - - // Based on the response from fetch_from_storages, verify and dispatch the response, and update the state. - async fn fetch_internal(&mut self) -> Result, StorageReadError> { - // TODO: add retry to TransientError. - let responses = self.fetch_from_storages().await?; - // Verify no empty response. - if responses.iter().any(|v| v.transactions.is_empty()) { - return Err(StorageReadError::TransientError( - RESPONSE_DISPATCH_NAME, - anyhow::anyhow!("Empty responses from storages."), - )); - } - - // Verify responses are consecutive and sequential. - let mut version = self.next_version_to_process; - for response in responses.iter() { - for transaction in response.transactions.iter() { - if transaction.version != version { - return Err(StorageReadError::TransientError( - RESPONSE_DISPATCH_NAME, - anyhow::anyhow!("Version mismatch in response."), - )); - } - // move to the next version. - version += 1; - } - } - let mut processed_responses = vec![]; - if let Some(transaction_count) = self.transaction_count { - // If transactions_count is specified, truncate if necessary. - let mut current_transaction_count = 0; - for response in responses.into_iter() { - if current_transaction_count == transaction_count { - break; - } - let current_response_size = response.transactions.len() as u64; - if current_transaction_count + current_response_size > transaction_count { - let remaining_transaction_count = transaction_count - current_transaction_count; - let truncated_transactions = response - .transactions - .into_iter() - .take(remaining_transaction_count as usize) - .collect(); - processed_responses.push(TransactionsResponse { - transactions: truncated_transactions, - chain_id: response.chain_id, - }); - current_transaction_count += remaining_transaction_count; - } else { - processed_responses.push(response); - current_transaction_count += current_response_size; - } - } - self.transaction_count = Some(transaction_count - current_transaction_count); - } else { - // If not, continue to fetch. - processed_responses = responses; - } - let processed_transactions_count = processed_responses - .iter() - .map(|v| v.transactions.len()) - .sum::() as u64; - self.next_version_to_process += processed_transactions_count; - Ok(processed_responses) - } -} - -#[async_trait::async_trait] -impl ResponseDispatcher for GrpcResponseDispatcher { - fn new( - starting_version: u64, - transaction_count: Option, - sender: Sender>, - storages: &[StorageClient], - ) -> Self { - let sender_capacity = sender.capacity(); - Self { - next_version_to_process: starting_version, - transaction_count, - sender, - sender_capacity, - storages: storages.to_vec(), - } - } - - async fn run(&mut self) -> anyhow::Result<()> { - loop { - match self.fetch_with_retries().await { - Ok(responses) => { - if responses.is_empty() { - break; - } - for response in responses { - self.dispatch(Ok(response)).await?; - } - }, - Err(status) => { - self.dispatch(Err(status)).await?; - anyhow::bail!("Failed to fetch transactions from storages."); - }, - } - } - if self.transaction_count.is_some() { - let start_time = std::time::Instant::now(); - loop { - if start_time.elapsed().as_millis() > WAIT_TIME_BEFORE_CLOUSING_IN_MS as u128 { - break; - } - if self.sender.capacity() == self.sender_capacity { - break; - } - tokio::time::sleep(Duration::from_millis(1000)).await; - } - } - Ok(()) - } - - async fn fetch_with_retries(&mut self) -> anyhow::Result, Status> { - for _ in 0..FETCH_RETRY_COUNT { - match self.fetch_internal().await { - Ok(responses) => { - return Ok(responses); - }, - Err(StorageReadError::TransientError(s, _e)) => { - tracing::warn!("Failed to fetch transactions from storage: {:#}", s); - tokio::time::sleep(Duration::from_millis(RETRY_BACKOFF_IN_MS)).await; - continue; - }, - Err(StorageReadError::PermenantError(s, _e)) => Err(Status::internal(format!( - "Failed to fetch transactions from storages, {:}", - s - )))?, - } - } - Err(Status::internal( - "Failed to fetch transactions from storages.", - )) - } - - async fn dispatch( - &mut self, - response: Result, - ) -> anyhow::Result<()> { - let start_time = std::time::Instant::now(); - match self - .sender - .send_timeout(response, RESPONSE_CHANNEL_SEND_TIMEOUT) - .await - { - Ok(_) => {}, - Err(e) => { - tracing::warn!("Failed to send response to downstream: {:#}", e); - return Err(anyhow::anyhow!("Failed to send response to downstream.")); - }, - }; - sample!( - SampleRate::Duration(Duration::from_secs(60)), - tracing::info!( - "[GrpcResponseDispatch] response waiting time in seconds: {}", - start_time.elapsed().as_secs_f64() - ); - ); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use aptos_indexer_grpc_data_access::MockStorageClient; - use aptos_protos::transaction::v1::Transaction; - fn create_transactions(starting_version: u64, size: usize) -> Vec { - let mut transactions = vec![]; - for i in 0..size { - transactions.push(Transaction { - version: starting_version + i as u64, - ..Default::default() - }); - } - transactions - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 4)] - async fn test_finite_stream() { - let (sender, mut receiver) = tokio::sync::mpsc::channel(100); - tokio::spawn(async move { - let first_storage_transactions = create_transactions(20, 100); - let second_storage_transactions = create_transactions(10, 20); - let third_storage_transactions = create_transactions(0, 15); - let storages = vec![ - StorageClient::MockClient(MockStorageClient::new(1, first_storage_transactions)), - StorageClient::MockClient(MockStorageClient::new(2, second_storage_transactions)), - StorageClient::MockClient(MockStorageClient::new(3, third_storage_transactions)), - ]; - let mut dispatcher = - GrpcResponseDispatcher::new(0, Some(40), sender, storages.as_slice()); - let run_result = dispatcher.run().await; - assert!(run_result.is_ok()); - }); - - let mut transactions = vec![]; - while let Some(response) = receiver.recv().await { - for transaction in response.unwrap().transactions { - transactions.push(transaction); - } - } - assert_eq!(transactions.len(), 40); - for (current_version, t) in transactions.into_iter().enumerate() { - assert_eq!(t.version, current_version as u64); - } - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 4)] - async fn test_storages_gap() { - let (sender, mut receiver) = tokio::sync::mpsc::channel(100); - tokio::spawn(async move { - let first_storage_transactions = create_transactions(30, 100); - let second_storage_transactions = create_transactions(10, 10); - let storages = vec![ - StorageClient::MockClient(MockStorageClient::new(1, first_storage_transactions)), - StorageClient::MockClient(MockStorageClient::new(2, second_storage_transactions)), - ]; - let mut dispatcher = - GrpcResponseDispatcher::new(15, Some(30), sender, storages.as_slice()); - let run_result = dispatcher.run().await; - assert!(run_result.is_err()); - }); - - let first_response = receiver.recv().await.unwrap(); - assert!(first_response.is_ok()); - let transactions_response = first_response.unwrap(); - assert!(transactions_response.transactions.len() == 5); - let second_response = receiver.recv().await.unwrap(); - // Gap is detected. - assert!(second_response.is_err()); - } - - // This test is to make sure dispatch doesn't leak memory. - #[tokio::test(flavor = "multi_thread", worker_threads = 4)] - async fn test_infinite_stream_with_client_closure() { - let (sender, mut receiver) = tokio::sync::mpsc::channel(100); - let task_result = tokio::spawn(async move { - let first_storage_transactions = create_transactions(20, 20); - let second_storage_transactions = create_transactions(10, 30); - let third_storage_transactions = create_transactions(0, 15); - let storages = vec![ - StorageClient::MockClient(MockStorageClient::new(1, first_storage_transactions)), - StorageClient::MockClient(MockStorageClient::new(2, second_storage_transactions)), - StorageClient::MockClient(MockStorageClient::new(3, third_storage_transactions)), - ]; - let mut dispatcher = GrpcResponseDispatcher::new(0, None, sender, storages.as_slice()); - dispatcher.run().await - }); - // Let the dispatcher run for 1 second. - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - let first_peek = receiver.try_recv(); - // transactions 0 - 15 - assert!(first_peek.is_ok()); - let first_response = first_peek.unwrap(); - assert!(first_response.is_ok()); - let transactions_response = first_response.unwrap(); - assert!(transactions_response.transactions.len() == 15); - let second_peek = receiver.try_recv(); - // transactions 15 - 40 - assert!(second_peek.is_ok()); - let second_response = second_peek.unwrap(); - assert!(second_response.is_ok()); - let transactions_response = second_response.unwrap(); - assert!(transactions_response.transactions.len() == 25); - let third_peek = receiver.try_recv(); - match third_peek { - Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {}, - _ => unreachable!("This is not possible."), - } - // Drop the receiver to close the channel. - drop(receiver); - let task_result = task_result.await; - - // The task should finish successfully. - assert!(task_result.is_ok()); - let task_result = task_result.unwrap(); - // The dispatcher thread should exit with error. - assert!(task_result.is_err()); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 4)] - async fn test_not_found_in_all_storages() { - let (sender, mut receiver) = tokio::sync::mpsc::channel(100); - tokio::spawn(async move { - let first_storage_transactions = create_transactions(20, 100); - let storages = vec![StorageClient::MockClient(MockStorageClient::new( - 1, - first_storage_transactions, - ))]; - let mut dispatcher = - GrpcResponseDispatcher::new(0, Some(40), sender, storages.as_slice()); - let run_result = dispatcher.run().await; - assert!(run_result.is_err()); - }); - - let first_response = receiver.recv().await.unwrap(); - assert!(first_response.is_err()); - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/mod.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/mod.rs deleted file mode 100644 index 09f62a7246680..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/response_dispatcher/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_indexer_grpc_data_access::StorageClient; -use aptos_protos::indexer::v1::TransactionsResponse; -use tokio::sync::mpsc::Sender; -use tonic::Status; - -pub mod grpc_response_dispatcher; -pub use grpc_response_dispatcher::*; - -/// ResponseDispatcher is a trait that defines the interface for dispatching responses into channel via provided sender. -#[async_trait::async_trait] -pub trait ResponseDispatcher { - fn new( - starting_version: u64, - transaction_count: Option, - sender: Sender>, - // Dispatcher is expected to fetch responses from these storages in order; - // if it fails to fetch from the first storage, it will try the second one, etc. - // StorageClient is expected to be *cheap to clone*. - storage_clients: &[StorageClient], - ) -> Self; - // Dispatch a single response to the channel. - async fn dispatch( - &mut self, - response: Result, - ) -> anyhow::Result<()>; - - // Fetch responses that need to be dispatched. TransactionsResponse might get chunked into multiple responses. - async fn fetch_with_retries(&mut self) -> anyhow::Result, Status>; - - // Run the dispatcher in a loop: fetch -> dispatch. - async fn run(&mut self) -> anyhow::Result<()>; -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/service.rs b/ecosystem/indexer-grpc/indexer-grpc-data-service/src/service.rs deleted file mode 100644 index e85d4fa761d76..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-data-service/src/service.rs +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::metrics::{ - BYTES_READY_TO_TRANSFER_FROM_SERVER, CONNECTION_COUNT, ERROR_COUNT, - LATEST_PROCESSED_VERSION as LATEST_PROCESSED_VERSION_OLD, PROCESSED_BATCH_SIZE, - PROCESSED_LATENCY_IN_SECS, PROCESSED_LATENCY_IN_SECS_ALL, PROCESSED_VERSIONS_COUNT, - SHORT_CONNECTION_COUNT, -}; -use anyhow::{Context, Result}; -use aptos_indexer_grpc_utils::{ - cache_operator::{CacheBatchGetStatus, CacheCoverageStatus, CacheOperator}, - chunk_transactions, - compression_util::{CacheEntry, StorageFormat}, - config::IndexerGrpcFileStoreConfig, - constants::{ - IndexerGrpcRequestMetadata, GRPC_AUTH_TOKEN_HEADER, GRPC_REQUEST_NAME_HEADER, - MESSAGE_SIZE_LIMIT, - }, - counters::{log_grpc_step, IndexerGrpcStep, NUM_MULTI_FETCH_OVERLAPPED_VERSIONS}, - file_store_operator::FileStoreOperator, - time_diff_since_pb_timestamp_in_secs, - types::RedisUrl, -}; -use aptos_moving_average::MovingAverage; -use aptos_protos::{ - indexer::v1::{raw_data_server::RawData, GetTransactionsRequest, TransactionsResponse}, - transaction::v1::Transaction, -}; -use futures::Stream; -use prost::Message; -use redis::Client; -use std::{ - collections::HashMap, - pin::Pin, - str::FromStr, - sync::Arc, - time::{Duration, Instant}, -}; -use tokio::sync::mpsc::{channel, error::SendTimeoutError}; -use tokio_stream::wrappers::ReceiverStream; -use tonic::{Request, Response, Status}; -use tracing::{error, info, warn}; -use uuid::Uuid; - -type ResponseStream = Pin> + Send>>; - -const MOVING_AVERAGE_WINDOW_SIZE: u64 = 10_000; -// When trying to fetch beyond the current head of cache, the server will retry after this duration. -const AHEAD_OF_CACHE_RETRY_SLEEP_DURATION_MS: u64 = 50; -// When error happens when fetching data from cache and file store, the server will retry after this duration. -// TODO(larry): fix all errors treated as transient errors. -const TRANSIENT_DATA_ERROR_RETRY_SLEEP_DURATION_MS: u64 = 1000; -// This is the time we wait for the file store to be ready. It should only be -// kicked off when there's no metadata in the file store. -const FILE_STORE_METADATA_WAIT_MS: u64 = 2000; - -// The server will retry to send the response to the client and give up after RESPONSE_CHANNEL_SEND_TIMEOUT. -// This is to prevent the server from being occupied by a slow client. -const RESPONSE_CHANNEL_SEND_TIMEOUT: Duration = Duration::from_secs(120); - -const SHORT_CONNECTION_DURATION_IN_SECS: u64 = 10; - -const REQUEST_HEADER_APTOS_EMAIL_HEADER: &str = "x-aptos-email"; -const REQUEST_HEADER_APTOS_USER_CLASSIFICATION_HEADER: &str = "x-aptos-user-classification"; -const REQUEST_HEADER_APTOS_API_KEY_NAME: &str = "x-aptos-api-key-name"; -const RESPONSE_HEADER_APTOS_CONNECTION_ID_HEADER: &str = "x-aptos-connection-id"; -const SERVICE_TYPE: &str = "data_service"; - -// Number of times to retry fetching a given txn block from the stores -pub const NUM_DATA_FETCH_RETRIES: u8 = 5; - -// Max number of tasks to reach out to TXN stores with -const MAX_FETCH_TASKS_PER_REQUEST: u64 = 5; -// The number of transactions we store per txn block; this is used to determine max num of tasks -const TRANSACTIONS_PER_STORAGE_BLOCK: u64 = 1000; - -pub struct RawDataServerWrapper { - pub redis_client: Arc, - pub file_store_config: IndexerGrpcFileStoreConfig, - pub data_service_response_channel_size: usize, - pub cache_storage_format: StorageFormat, -} - -impl RawDataServerWrapper { - pub fn new( - redis_address: RedisUrl, - file_store_config: IndexerGrpcFileStoreConfig, - data_service_response_channel_size: usize, - cache_storage_format: StorageFormat, - ) -> anyhow::Result { - Ok(Self { - redis_client: Arc::new( - redis::Client::open(redis_address.0.clone()).with_context(|| { - format!("Failed to create redis client for {}", redis_address) - })?, - ), - file_store_config, - data_service_response_channel_size, - cache_storage_format, - }) - } -} - -/// Enum to represent the status of the data fetching overall. -enum TransactionsDataStatus { - // Data fetching is successful. - Success(Vec), - // Ahead of current head of cache. - AheadOfCache, -} - -/// RawDataServerWrapper handles the get transactions requests from cache and file store. -#[tonic::async_trait] -impl RawData for RawDataServerWrapper { - type GetTransactionsStream = ResponseStream; - - /// GetTransactionsStream is a streaming GRPC endpoint: - /// 1. Fetches data from cache and file store. - /// 1.1. If the data is beyond the current head of cache, retry after a short sleep. - /// 1.2. If the data is not in cache, fetch the data from file store. - /// 1.3. If the data is not in file store, stream connection will break. - /// 1.4 If error happens, retry after a short sleep. - /// 2. Push data into channel to stream to the client. - /// 2.1. If the channel is full, do not fetch and retry after a short sleep. - async fn get_transactions( - &self, - req: Request, - ) -> Result, Status> { - // Get request identity. The request is already authenticated by the interceptor. - let request_metadata = match get_request_metadata(&req) { - Ok(request_metadata) => request_metadata, - _ => return Result::Err(Status::aborted("Invalid request token")), - }; - CONNECTION_COUNT - .with_label_values(&[ - &request_metadata.request_api_key_name, - &request_metadata.request_email, - &request_metadata.processor_name, - ]) - .inc(); - let request = req.into_inner(); - - let transactions_count = request.transactions_count; - - // Response channel to stream the data to the client. - let (tx, rx) = channel(self.data_service_response_channel_size); - let current_version = match &request.starting_version { - Some(version) => *version, - None => { - return Result::Err(Status::aborted("Starting version is not set")); - }, - }; - - let file_store_operator: Box = self.file_store_config.create(); - let file_store_operator = Arc::new(file_store_operator); - - // Adds tracing context for the request. - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceNewRequestReceived, - Some(current_version as i64), - transactions_count.map(|v| (v as i64 + current_version as i64 - 1)), - None, - None, - None, - None, - None, - Some(&request_metadata), - ); - - let redis_client = self.redis_client.clone(); - let cache_storage_format = self.cache_storage_format; - let request_metadata = Arc::new(request_metadata); - tokio::spawn({ - let request_metadata = request_metadata.clone(); - async move { - data_fetcher_task( - redis_client, - file_store_operator, - cache_storage_format, - request_metadata, - transactions_count, - tx, - current_version, - ) - .await; - } - }); - - let output_stream = ReceiverStream::new(rx); - let mut response = Response::new(Box::pin(output_stream) as Self::GetTransactionsStream); - - response.metadata_mut().insert( - RESPONSE_HEADER_APTOS_CONNECTION_ID_HEADER, - tonic::metadata::MetadataValue::from_str(&request_metadata.request_connection_id) - .unwrap(), - ); - Ok(response) - } -} - -enum DataFetchSubTaskResult { - BatchSuccess(Vec>), - Success(Vec), - NoResults, -} - -async fn get_data_with_tasks( - start_version: u64, - transactions_count: Option, - chain_id: u64, - cache_operator: &mut CacheOperator, - file_store_operator: Arc>, - request_metadata: Arc, - cache_storage_format: StorageFormat, -) -> DataFetchSubTaskResult { - let cache_coverage_status = cache_operator - .check_cache_coverage_status(start_version) - .await; - - let num_tasks_to_use = match cache_coverage_status { - Ok(CacheCoverageStatus::DataNotReady) => return DataFetchSubTaskResult::NoResults, - Ok(CacheCoverageStatus::CacheHit(_)) => 1, - Ok(CacheCoverageStatus::CacheEvicted) => match transactions_count { - None => MAX_FETCH_TASKS_PER_REQUEST, - Some(transactions_count) => { - let num_tasks = transactions_count / TRANSACTIONS_PER_STORAGE_BLOCK; - if num_tasks >= MAX_FETCH_TASKS_PER_REQUEST { - // Limit the max tasks to MAX_FETCH_TASKS_PER_REQUEST - MAX_FETCH_TASKS_PER_REQUEST - } else if num_tasks < 1 { - // Limit the min tasks to 1 - 1 - } else { - num_tasks - } - }, - }, - Err(_) => { - error!("[Data Service] Failed to get cache coverage status."); - panic!("Failed to get cache coverage status."); - }, - }; - - let mut tasks = tokio::task::JoinSet::new(); - let mut current_version = start_version; - - for _ in 0..num_tasks_to_use { - tasks.spawn({ - // TODO: arc this instead of cloning - let mut cache_operator = cache_operator.clone(); - let file_store_operator = file_store_operator.clone(); - let request_metadata = request_metadata.clone(); - async move { - get_data_in_task( - current_version, - chain_id, - &mut cache_operator, - file_store_operator, - request_metadata.clone(), - cache_storage_format, - ) - .await - } - }); - // Storage is in block of 1000: we align our current version fetch to the nearest block - current_version += TRANSACTIONS_PER_STORAGE_BLOCK; - current_version -= current_version % TRANSACTIONS_PER_STORAGE_BLOCK; - } - - let mut transactions: Vec> = vec![]; - while let Some(result) = tasks.join_next().await { - match result { - Ok(DataFetchSubTaskResult::Success(txns)) => { - transactions.push(txns); - }, - Ok(DataFetchSubTaskResult::NoResults) => {}, - Err(e) => { - error!( - error = e.to_string(), - "[Data Service] Failed to get data from cache and file store." - ); - panic!("Failed to get data from cache and file store."); - }, - Ok(_) => unreachable!("Fetching from a single task will never return a batch"), - } - } - - if transactions.is_empty() { - DataFetchSubTaskResult::NoResults - } else { - DataFetchSubTaskResult::BatchSuccess(transactions) - } -} - -async fn get_data_in_task( - start_version: u64, - chain_id: u64, - cache_operator: &mut CacheOperator, - file_store_operator: Arc>, - request_metadata: Arc, - cache_storage_format: StorageFormat, -) -> DataFetchSubTaskResult { - let current_batch_start_time = std::time::Instant::now(); - - let fetched = data_fetch( - start_version, - cache_operator, - file_store_operator, - request_metadata.clone(), - cache_storage_format, - ); - - let transaction_data = match fetched.await { - Ok(TransactionsDataStatus::Success(transactions)) => transactions, - Ok(TransactionsDataStatus::AheadOfCache) => { - info!( - start_version = start_version, - request_name = request_metadata.processor_name.as_str(), - request_email = request_metadata.request_email.as_str(), - request_api_key_name = request_metadata.request_api_key_name.as_str(), - processor_name = request_metadata.processor_name.as_str(), - connection_id = request_metadata.request_connection_id.as_str(), - request_user_classification = request_metadata.request_user_classification.as_str(), - duration_in_secs = current_batch_start_time.elapsed().as_secs_f64(), - service_type = SERVICE_TYPE, - "[Data Service] Requested data is ahead of cache. Sleeping for {} ms.", - AHEAD_OF_CACHE_RETRY_SLEEP_DURATION_MS, - ); - ahead_of_cache_data_handling().await; - // Retry after a short sleep. - return DataFetchSubTaskResult::NoResults; - }, - Err(e) => { - ERROR_COUNT.with_label_values(&["data_fetch_failed"]).inc(); - data_fetch_error_handling(e, start_version, chain_id).await; - // Retry after a short sleep. - return DataFetchSubTaskResult::NoResults; - }, - }; - DataFetchSubTaskResult::Success(transaction_data) -} - -// This is a task spawned off for servicing a users' request -async fn data_fetcher_task( - redis_client: Arc, - file_store_operator: Arc>, - cache_storage_format: StorageFormat, - request_metadata: Arc, - transactions_count: Option, - tx: tokio::sync::mpsc::Sender>, - mut current_version: u64, -) { - let mut connection_start_time = Some(std::time::Instant::now()); - let mut transactions_count = transactions_count; - - // Establish redis connection - let conn = match redis_client.get_tokio_connection_manager().await { - Ok(conn) => conn, - Err(e) => { - ERROR_COUNT - .with_label_values(&["redis_connection_failed"]) - .inc(); - // Connection will be dropped anyway, so we ignore the error here. - let _result = tx - .send_timeout( - Err(Status::unavailable( - "[Data Service] Cannot connect to Redis; please retry.", - )), - RESPONSE_CHANNEL_SEND_TIMEOUT, - ) - .await; - error!( - error = e.to_string(), - "[Data Service] Failed to get redis connection." - ); - return; - }, - }; - let mut cache_operator = CacheOperator::new(conn, cache_storage_format); - - // Validate chain id - let mut metadata = file_store_operator.get_file_store_metadata().await; - while metadata.is_none() { - metadata = file_store_operator.get_file_store_metadata().await; - tracing::warn!( - "[File worker] File store metadata not found. Waiting for {} ms.", - FILE_STORE_METADATA_WAIT_MS - ); - tokio::time::sleep(std::time::Duration::from_millis( - FILE_STORE_METADATA_WAIT_MS, - )) - .await; - } - - let metadata_chain_id = metadata.unwrap().chain_id; - - // Validate redis chain id. Must be present by the time it gets here - let chain_id = match cache_operator.get_chain_id().await { - Ok(chain_id) => chain_id.unwrap(), - Err(e) => { - ERROR_COUNT - .with_label_values(&["redis_get_chain_id_failed"]) - .inc(); - // Connection will be dropped anyway, so we ignore the error here. - let _result = tx - .send_timeout( - Err(Status::unavailable( - "[Data Service] Cannot get the chain id from redis; please retry.", - )), - RESPONSE_CHANNEL_SEND_TIMEOUT, - ) - .await; - error!( - error = e.to_string(), - "[Data Service] Failed to get chain id from redis." - ); - return; - }, - }; - - if metadata_chain_id != chain_id { - let _result = tx - .send_timeout( - Err(Status::unavailable("[Data Service] Chain ID mismatch.")), - RESPONSE_CHANNEL_SEND_TIMEOUT, - ) - .await; - error!("[Data Service] Chain ID mismatch.",); - return; - } - - // Data service metrics. - let mut tps_calculator = MovingAverage::new(MOVING_AVERAGE_WINDOW_SIZE); - - loop { - // 1. Fetch data from cache and file store. - let transaction_data = match get_data_with_tasks( - current_version, - transactions_count, - chain_id, - &mut cache_operator, - file_store_operator.clone(), - request_metadata.clone(), - cache_storage_format, - ) - .await - { - DataFetchSubTaskResult::BatchSuccess(txns) => txns, - DataFetchSubTaskResult::Success(_) => { - unreachable!("Fetching from multiple tasks will never return a single vector") - }, - DataFetchSubTaskResult::NoResults => continue, - }; - - let mut transaction_data = ensure_sequential_transactions(transaction_data); - - // TODO: Unify the truncation logic for start and end. - if let Some(count) = transactions_count { - if count == 0 { - // End the data stream. - // Since the client receives all the data it requested, we don't count it as a short connection. - connection_start_time = None; - break; - } else if (count as usize) < transaction_data.len() { - // Trim the data to the requested end version. - transaction_data.truncate(count as usize); - transactions_count = Some(0); - } else { - transactions_count = Some(count - transaction_data.len() as u64); - } - }; - // Note: this is the protobuf encoded transaction size. - let bytes_ready_to_transfer = transaction_data - .iter() - .map(|t| t.encoded_len()) - .sum::(); - BYTES_READY_TO_TRANSFER_FROM_SERVER - .with_label_values(&[ - &request_metadata.request_api_key_name, - &request_metadata.request_email, - &request_metadata.processor_name, - ]) - .inc_by(bytes_ready_to_transfer as u64); - // 2. Push the data to the response channel, i.e. stream the data to the client. - let current_batch_size = transaction_data.as_slice().len(); - let end_of_batch_version = transaction_data.as_slice().last().unwrap().version; - let resp_items = get_transactions_responses_builder(transaction_data, chain_id as u32); - let data_latency_in_secs = resp_items - .last() - .unwrap() - .transactions - .last() - .unwrap() - .timestamp - .as_ref() - .map(time_diff_since_pb_timestamp_in_secs); - - match channel_send_multiple_with_timeout(resp_items, tx.clone(), request_metadata.clone()) - .await - { - Ok(_) => { - PROCESSED_BATCH_SIZE - .with_label_values(&[ - request_metadata.request_api_key_name.as_str(), - request_metadata.request_email.as_str(), - request_metadata.processor_name.as_str(), - ]) - .set(current_batch_size as i64); - // TODO: Reasses whether this metric useful - LATEST_PROCESSED_VERSION_OLD - .with_label_values(&[ - request_metadata.request_api_key_name.as_str(), - request_metadata.request_email.as_str(), - request_metadata.processor_name.as_str(), - ]) - .set(end_of_batch_version as i64); - PROCESSED_VERSIONS_COUNT - .with_label_values(&[ - request_metadata.request_api_key_name.as_str(), - request_metadata.request_email.as_str(), - request_metadata.processor_name.as_str(), - ]) - .inc_by(current_batch_size as u64); - if let Some(data_latency_in_secs) = data_latency_in_secs { - PROCESSED_LATENCY_IN_SECS - .with_label_values(&[ - request_metadata.request_api_key_name.as_str(), - request_metadata.request_email.as_str(), - request_metadata.processor_name.as_str(), - ]) - .set(data_latency_in_secs); - PROCESSED_LATENCY_IN_SECS_ALL - .with_label_values(&[request_metadata.request_user_classification.as_str()]) - .observe(data_latency_in_secs); - } - }, - Err(SendTimeoutError::Timeout(_)) => { - warn!("[Data Service] Receiver is full; exiting."); - break; - }, - Err(SendTimeoutError::Closed(_)) => { - warn!("[Data Service] Receiver is closed; exiting."); - break; - }, - } - // 3. Update the current version and record current tps. - tps_calculator.tick_now(current_batch_size as u64); - current_version = end_of_batch_version + 1; - } - info!( - request_name = request_metadata.processor_name.as_str(), - request_email = request_metadata.request_email.as_str(), - request_api_key_name = request_metadata.request_api_key_name.as_str(), - processor_name = request_metadata.processor_name.as_str(), - connection_id = request_metadata.request_connection_id.as_str(), - request_user_classification = request_metadata.request_user_classification.as_str(), - request_user_classification = request_metadata.request_user_classification.as_str(), - service_type = SERVICE_TYPE, - "[Data Service] Client disconnected." - ); - if let Some(start_time) = connection_start_time { - if start_time.elapsed().as_secs() < SHORT_CONNECTION_DURATION_IN_SECS { - SHORT_CONNECTION_COUNT - .with_label_values(&[ - request_metadata.request_api_key_name.as_str(), - request_metadata.request_email.as_str(), - request_metadata.processor_name.as_str(), - ]) - .inc(); - } - } -} - -/// Takes in multiple batches of transactions, and: -/// 1. De-dupes in the case of overlap (but log to prom metric) -/// 2. Panics in cases of gaps -fn ensure_sequential_transactions(mut batches: Vec>) -> Vec { - // If there's only one, no sorting required - if batches.len() == 1 { - return batches.pop().unwrap(); - } - - // Sort by the first version per batch, ascending - batches.sort_by(|a, b| a.first().unwrap().version.cmp(&b.first().unwrap().version)); - let first_version = batches.first().unwrap().first().unwrap().version; - let last_version = batches.last().unwrap().last().unwrap().version; - let mut transactions: Vec = vec![]; - - let mut prev_start = None; - let mut prev_end = None; - for mut batch in batches { - let mut start_version = batch.first().unwrap().version; - let end_version = batch.last().unwrap().version; - if prev_start.is_some() { - let prev_start = prev_start.unwrap(); - let prev_end = prev_end.unwrap(); - // If this batch is fully contained within the previous batch, skip it - if prev_start <= start_version && prev_end >= end_version { - NUM_MULTI_FETCH_OVERLAPPED_VERSIONS - .with_label_values(&[SERVICE_TYPE, &"full"]) - .inc_by(end_version - start_version); - continue; - } - // If this batch overlaps with the previous batch, combine them - if prev_end >= start_version { - NUM_MULTI_FETCH_OVERLAPPED_VERSIONS - .with_label_values(&[SERVICE_TYPE, &"partial"]) - .inc_by(prev_end - start_version + 1); - tracing::debug!( - batch_first_version = first_version, - batch_last_version = last_version, - start_version = start_version, - end_version = end_version, - prev_start = ?prev_start, - prev_end = prev_end, - "[Filestore] Overlapping version data" - ); - batch.drain(0..(prev_end - start_version + 1) as usize); - start_version = batch.first().unwrap().version; - } - - // Otherwise there is a gap - if prev_end + 1 != start_version { - NUM_MULTI_FETCH_OVERLAPPED_VERSIONS - .with_label_values(&[SERVICE_TYPE, &"gap"]) - .inc_by(prev_end - start_version + 1); - - tracing::error!( - batch_first_version = first_version, - batch_last_version = last_version, - start_version = start_version, - end_version = end_version, - prev_start = ?prev_start, - prev_end = prev_end, - "[Filestore] Gaps or dupes in processing version data" - ); - panic!("[Filestore] Gaps in processing data batch_first_version: {}, batch_last_version: {}, start_version: {}, end_version: {}, prev_start: {:?}, prev_end: {:?}", - first_version, - last_version, - start_version, - end_version, - prev_start, - prev_end, - ); - } - } - - prev_start = Some(start_version); - prev_end = Some(end_version); - transactions.extend(batch); - } - - transactions -} - -/// Builds the response for the get transactions request. Partial batch is ok, i.e., a batch with transactions < 1000. -fn get_transactions_responses_builder( - transactions: Vec, - chain_id: u32, -) -> Vec { - let chunks = chunk_transactions(transactions, MESSAGE_SIZE_LIMIT); - chunks - .into_iter() - .map(|chunk| TransactionsResponse { - chain_id: Some(chain_id as u64), - transactions: chunk, - }) - .collect() -} - -// This is a CPU bound operation, so we spawn_blocking -async fn deserialize_cached_transactions( - transactions: Vec>, - storage_format: StorageFormat, -) -> anyhow::Result> { - let task = tokio::task::spawn_blocking(move || { - transactions - .into_iter() - .map(|transaction| { - let cache_entry = CacheEntry::new(transaction, storage_format); - cache_entry.into_transaction() - }) - .collect::>() - }) - .await; - task.context("Transaction bytes to CacheEntry deserialization task failed") -} - -/// Fetches data from cache or the file store. It returns the data if it is ready in the cache or file store. -/// Otherwise, it returns the status of the data fetching. -async fn data_fetch( - starting_version: u64, - cache_operator: &mut CacheOperator, - file_store_operator: Arc>, - request_metadata: Arc, - storage_format: StorageFormat, -) -> anyhow::Result { - let current_batch_start_time = std::time::Instant::now(); - let batch_get_result = cache_operator - .batch_get_encoded_proto_data(starting_version) - .await; - - match batch_get_result { - // Data is not ready yet in the cache. - Ok(CacheBatchGetStatus::NotReady) => Ok(TransactionsDataStatus::AheadOfCache), - Ok(CacheBatchGetStatus::Ok(transactions)) => { - let decoding_start_time = std::time::Instant::now(); - let size_in_bytes = transactions - .iter() - .map(|transaction| transaction.len()) - .sum::(); - let num_of_transactions = transactions.len(); - let duration_in_secs = current_batch_start_time.elapsed().as_secs_f64(); - - let transactions = - deserialize_cached_transactions(transactions, storage_format).await?; - let start_version_timestamp = transactions.first().unwrap().timestamp.as_ref(); - let end_version_timestamp = transactions.last().unwrap().timestamp.as_ref(); - - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceDataFetchedCache, - Some(starting_version as i64), - Some(starting_version as i64 + num_of_transactions as i64 - 1), - start_version_timestamp, - end_version_timestamp, - Some(duration_in_secs), - Some(size_in_bytes), - Some(num_of_transactions as i64), - Some(&request_metadata), - ); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceTxnsDecoded, - Some(starting_version as i64), - Some(starting_version as i64 + num_of_transactions as i64 - 1), - start_version_timestamp, - end_version_timestamp, - Some(decoding_start_time.elapsed().as_secs_f64()), - Some(size_in_bytes), - Some(num_of_transactions as i64), - Some(&request_metadata), - ); - - Ok(TransactionsDataStatus::Success(transactions)) - }, - Ok(CacheBatchGetStatus::EvictedFromCache) => { - let transactions = - data_fetch_from_filestore(starting_version, file_store_operator, request_metadata) - .await?; - Ok(TransactionsDataStatus::Success(transactions)) - }, - Err(e) => Err(e), - } -} - -async fn data_fetch_from_filestore( - starting_version: u64, - file_store_operator: Arc>, - request_metadata: Arc, -) -> anyhow::Result> { - // Data is evicted from the cache. Fetch from file store. - let (transactions, io_duration, decoding_duration) = file_store_operator - .get_transactions_with_durations(starting_version, NUM_DATA_FETCH_RETRIES) - .await?; - let size_in_bytes = transactions - .iter() - .map(|transaction| transaction.encoded_len()) - .sum::(); - let num_of_transactions = transactions.len(); - let start_version_timestamp = transactions.first().unwrap().timestamp.as_ref(); - let end_version_timestamp = transactions.last().unwrap().timestamp.as_ref(); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceDataFetchedFilestore, - Some(starting_version as i64), - Some(starting_version as i64 + num_of_transactions as i64 - 1), - start_version_timestamp, - end_version_timestamp, - Some(io_duration), - Some(size_in_bytes), - Some(num_of_transactions as i64), - Some(&request_metadata), - ); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceTxnsDecoded, - Some(starting_version as i64), - Some(starting_version as i64 + num_of_transactions as i64 - 1), - start_version_timestamp, - end_version_timestamp, - Some(decoding_duration), - Some(size_in_bytes), - Some(num_of_transactions as i64), - Some(&request_metadata), - ); - Ok(transactions) -} - -/// Handles the case when the data is not ready in the cache, i.e., beyond the current head. -async fn ahead_of_cache_data_handling() { - // TODO: add exponential backoff. - tokio::time::sleep(Duration::from_millis( - AHEAD_OF_CACHE_RETRY_SLEEP_DURATION_MS, - )) - .await; -} - -/// Handles data fetch errors, including cache and file store related errors. -async fn data_fetch_error_handling(err: anyhow::Error, current_version: u64, chain_id: u64) { - error!( - chain_id = chain_id, - current_version = current_version, - "[Data Service] Failed to fetch data from cache and file store. {:?}", - err - ); - tokio::time::sleep(Duration::from_millis( - TRANSIENT_DATA_ERROR_RETRY_SLEEP_DURATION_MS, - )) - .await; -} - -/// Gets the request metadata. Useful for logging. -fn get_request_metadata( - req: &Request, -) -> tonic::Result { - let request_metadata_pairs = vec![ - ("request_api_key_name", REQUEST_HEADER_APTOS_API_KEY_NAME), - ("request_email", REQUEST_HEADER_APTOS_EMAIL_HEADER), - ( - "request_user_classification", - REQUEST_HEADER_APTOS_USER_CLASSIFICATION_HEADER, - ), - ("request_token", GRPC_AUTH_TOKEN_HEADER), - ("processor_name", GRPC_REQUEST_NAME_HEADER), - ]; - let mut request_metadata_map: HashMap = request_metadata_pairs - .into_iter() - .map(|(key, value)| { - ( - key.to_string(), - req.metadata() - .get(value) - .map(|value| value.to_str().unwrap_or("unspecified").to_string()) - .unwrap_or("unspecified".to_string()), - ) - }) - .collect(); - request_metadata_map.insert( - "request_connection_id".to_string(), - Uuid::new_v4().to_string(), - ); - let request_metadata: IndexerGrpcRequestMetadata = - serde_json::from_str(&serde_json::to_string(&request_metadata_map).unwrap()).unwrap(); - // TODO: update the request name if these are internal requests. - Ok(request_metadata) -} - -async fn channel_send_multiple_with_timeout( - resp_items: Vec, - tx: tokio::sync::mpsc::Sender>, - request_metadata: Arc, -) -> Result<(), SendTimeoutError>> { - let overall_send_start_time = Instant::now(); - let overall_size_in_bytes = resp_items - .iter() - .map(|resp_item| resp_item.encoded_len()) - .sum::(); - let overall_start_txn = resp_items.first().unwrap().transactions.first().unwrap(); - let overall_end_txn = resp_items.last().unwrap().transactions.last().unwrap(); - let overall_start_version = overall_start_txn.version; - let overall_end_version = overall_end_txn.version; - let overall_start_txn_timestamp = overall_start_txn.clone().timestamp; - let overall_end_txn_timestamp = overall_end_txn.clone().timestamp; - - for resp_item in resp_items { - let send_start_time = Instant::now(); - let response_size = resp_item.encoded_len(); - let num_of_transactions = resp_item.transactions.len(); - let start_version = resp_item.transactions.first().unwrap().version; - let end_version = resp_item.transactions.last().unwrap().version; - let start_version_txn_timestamp = resp_item - .transactions - .first() - .unwrap() - .timestamp - .as_ref() - .unwrap(); - let end_version_txn_timestamp = resp_item - .transactions - .last() - .unwrap() - .timestamp - .as_ref() - .unwrap(); - - tx.send_timeout( - Result::::Ok(resp_item.clone()), - RESPONSE_CHANNEL_SEND_TIMEOUT, - ) - .await?; - - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceChunkSent, - Some(start_version as i64), - Some(end_version as i64), - Some(start_version_txn_timestamp), - Some(end_version_txn_timestamp), - Some(send_start_time.elapsed().as_secs_f64()), - Some(response_size), - Some(num_of_transactions as i64), - Some(&request_metadata), - ); - } - - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::DataServiceAllChunksSent, - Some(overall_start_version as i64), - Some(overall_end_version as i64), - overall_start_txn_timestamp.as_ref(), - overall_end_txn_timestamp.as_ref(), - Some(overall_send_start_time.elapsed().as_secs_f64()), - Some(overall_size_in_bytes), - Some((overall_end_version - overall_start_version + 1) as i64), - Some(&request_metadata), - ); - - Ok(()) -} - -#[test] -fn test_ensure_sequential_transactions_merges_and_sorts() { - let transactions1 = (1..5) - .map(|i| Transaction { - version: i, - ..Default::default() - }) - .collect(); - let transactions2 = (5..10) - .map(|i| Transaction { - version: i, - ..Default::default() - }) - .collect(); - // No overlap, just normal fetching flow - let transactions1 = ensure_sequential_transactions(vec![transactions1, transactions2]); - assert_eq!(transactions1.len(), 9); - assert_eq!(transactions1.first().unwrap().version, 1); - assert_eq!(transactions1.last().unwrap().version, 9); - - // This is a full overlap - let transactions2 = (5..7) - .map(|i| Transaction { - version: i, - ..Default::default() - }) - .collect(); - let transactions1 = ensure_sequential_transactions(vec![transactions1, transactions2]); - assert_eq!(transactions1.len(), 9); - assert_eq!(transactions1.first().unwrap().version, 1); - assert_eq!(transactions1.last().unwrap().version, 9); - - // Partial overlap - let transactions2 = (5..12) - .map(|i| Transaction { - version: i, - ..Default::default() - }) - .collect(); - let transactions1 = ensure_sequential_transactions(vec![transactions1, transactions2]); - assert_eq!(transactions1.len(), 11); - assert_eq!(transactions1.first().unwrap().version, 1); - assert_eq!(transactions1.last().unwrap().version, 11); -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/Cargo.toml b/ecosystem/indexer-grpc/indexer-grpc-file-store/Cargo.toml deleted file mode 100644 index a3713c4e950e4..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "aptos-indexer-grpc-file-store" -description = "Indexer gRPC file store saves transactions to persistent storage." -version = "1.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-indexer-grpc-utils = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-moving-average = { workspace = true } -aptos-protos = { workspace = true } -aptos-runtimes = { workspace = true } -async-trait = { workspace = true } -base64 = { workspace = true } -clap = { workspace = true } -cloud-storage = { workspace = true } -futures = { workspace = true } -futures-util = { workspace = true } -once_cell = { workspace = true } -prost = { workspace = true } -redis = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } - -[target.'cfg(unix)'.dependencies] -jemallocator = { workspace = true } diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md b/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md deleted file mode 100644 index 3704bcae7ec2a..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Indexer GRPC file store - -File store fetches data from cache and stores in Google Cloud Storage. - -## How to run it. - -* A service account json with write access to GCS. - * To bootstrap, please upload `metadata.json` to your bucket(`$FILE_STORE_BUCKET_NAME` e.g., `indexer-grpc-file-store`): - ```json - { - "chain_id": 43, - "blob_size": 1000, - "version": 0 - } - ``` - * `chain_id` is the chain to process, immutable. - * `blob_size` is the number of transactions in each blob, immutable. - * `version` is the current version of transaction to process. - -* A Redis cache running at `$REDIS_ADDRESS`, e.g., `127.0.0.1:6379` -* Example command to run: - -```yaml -health_check_port: 8082 -server_config: - file_store_config: - file_store_type: GcsFileStore - gcs_file_store_bucket_name: indexer-grpc-file-store-bucketname - redis_main_instance_address: 127.0.0.1:6379 -``` - -* Your bucket looks like: - -```bash -indexer-grpc-file-store-testnet/ - files/ - 0.json - 1000.json - ... - metadata.json -``` - -## [TEST ONLY] Run it with a local filestore - -For developing and testing locally, it might be easier to use a local filestore. - -Create a local directory to store the filestore: `mkdir test_indexer_grpc_filestore` - -Then in your config: -```yaml -... -server_config: - file_store_config: - file_store_type: LocalFileStore - local_file_store_path: test_indexer_grpc_filestore -``` diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs deleted file mode 100644 index 336a002ca9f72..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -pub mod metrics; -pub mod processor; - -use anyhow::Result; -use aptos_indexer_grpc_server_framework::RunnableConfig; -use aptos_indexer_grpc_utils::{config::IndexerGrpcFileStoreConfig, types::RedisUrl}; -use processor::Processor; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct IndexerGrpcFileStoreWorkerConfig { - pub file_store_config: IndexerGrpcFileStoreConfig, - pub redis_main_instance_address: RedisUrl, - pub enable_expensive_logging: Option, - pub chain_id: u64, - #[serde(default = "default_enable_cache_compression")] - pub enable_cache_compression: bool, -} - -const fn default_enable_cache_compression() -> bool { - false -} - -impl IndexerGrpcFileStoreWorkerConfig { - pub fn new( - file_store_config: IndexerGrpcFileStoreConfig, - redis_main_instance_address: RedisUrl, - enable_expensive_logging: Option, - chain_id: u64, - enable_cache_compression: bool, - ) -> Self { - Self { - file_store_config, - redis_main_instance_address, - enable_expensive_logging, - chain_id, - enable_cache_compression, - } - } -} - -#[async_trait::async_trait] -impl RunnableConfig for IndexerGrpcFileStoreWorkerConfig { - async fn run(&self) -> Result<()> { - let mut processor = Processor::new( - self.redis_main_instance_address.clone(), - self.file_store_config.clone(), - self.chain_id, - self.enable_cache_compression, - ) - .await - .expect("Failed to create file store processor"); - processor - .run() - .await - .expect("File store processor exited unexpectedly"); - Ok(()) - } - - fn get_server_name(&self) -> String { - "idxfilestore".to_string() - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs deleted file mode 100644 index 3e2d0671339bc..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Result; -use aptos_indexer_grpc_file_store::IndexerGrpcFileStoreWorkerConfig; -use aptos_indexer_grpc_server_framework::ServerArgs; -use clap::Parser; - -#[cfg(unix)] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[tokio::main] -async fn main() -> Result<()> { - let args = ServerArgs::parse(); - args.run::() - .await - .expect("Failed to run server"); - Ok(()) -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/metrics.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/metrics.rs deleted file mode 100644 index 7196148e01317..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/metrics.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_metrics_core::{register_int_counter, register_int_gauge, IntCounter, IntGauge}; -use once_cell::sync::Lazy; - -/// Latest version of transactions that have been stored. -pub static LATEST_PROCESSED_VERSION: Lazy = Lazy::new(|| { - register_int_gauge!( - "indexer_grpc_file_store_latest_processed_version", - "Latest version of transactions that have been stored", - ) - .unwrap() -}); - -/// Number of transactions that have been stored. -pub static PROCESSED_VERSIONS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_grpc_file_store_processed_versions", - "Number of transactions that have been stored", - ) - .unwrap() -}); - -/// Number of errors that file store has encountered. -pub static ERROR_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_grpc_file_store_errors", - "Number of errors that file store has encountered" - ) - .unwrap() -}); - -/// Number of metadata upload failures that file store has encountered. -pub static METADATA_UPLOAD_FAILURE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_grpc_file_store_metadata_upload_failures", - "Number of metadata upload failures that file store has encountered" - ) - .unwrap() -}); diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/processor.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/processor.rs deleted file mode 100644 index 82dc8b0938836..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/processor.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::metrics::{METADATA_UPLOAD_FAILURE_COUNT, PROCESSED_VERSIONS_COUNT}; -use anyhow::{ensure, Context, Result}; -use aptos_indexer_grpc_utils::{ - cache_operator::CacheOperator, - compression_util::{FileStoreMetadata, StorageFormat, FILE_ENTRY_TRANSACTION_COUNT}, - config::IndexerGrpcFileStoreConfig, - counters::{log_grpc_step, IndexerGrpcStep}, - file_store_operator::FileStoreOperator, - types::RedisUrl, -}; -use aptos_moving_average::MovingAverage; -use std::time::Duration; -use tracing::debug; - -// If the version is ahead of the cache head, retry after a short sleep. -const AHEAD_OF_CACHE_SLEEP_DURATION_IN_MILLIS: u64 = 100; -const SERVICE_TYPE: &str = "file_worker"; - -/// Processor tails the data in cache and stores the data in file store. -pub struct Processor { - cache_operator: CacheOperator, - file_store_operator: Box, - chain_id: u64, -} - -impl Processor { - pub async fn new( - redis_main_instance_address: RedisUrl, - file_store_config: IndexerGrpcFileStoreConfig, - chain_id: u64, - enable_cache_compression: bool, - ) -> Result { - let cache_storage_format = if enable_cache_compression { - StorageFormat::GzipCompressedProto - } else { - StorageFormat::Base64UncompressedProto - }; - - // Connection to redis is a hard dependency for file store processor. - let conn = redis::Client::open(redis_main_instance_address.0.clone()) - .with_context(|| { - format!( - "Create redis client for {} failed", - redis_main_instance_address.0 - ) - })? - .get_tokio_connection_manager() - .await - .with_context(|| { - format!( - "Create redis connection to {} failed.", - redis_main_instance_address.0 - ) - })?; - let mut cache_operator = CacheOperator::new(conn, cache_storage_format); - - let mut file_store_operator: Box = file_store_config.create(); - file_store_operator.verify_storage_bucket_existence().await; - let file_store_metadata: Option = - file_store_operator.get_file_store_metadata().await; - if file_store_metadata.is_none() { - // If metadata doesn't exist, create and upload it and init file store latest version in cache. - while file_store_operator - .update_file_store_metadata_with_timeout(chain_id, 0) - .await - .is_err() - { - tracing::error!( - batch_start_version = 0, - service_type = SERVICE_TYPE, - "[File worker] Failed to update file store metadata. Retrying." - ); - std::thread::sleep(std::time::Duration::from_millis(500)); - METADATA_UPLOAD_FAILURE_COUNT.inc(); - } - } - // Metadata is guaranteed to exist now - let metadata = file_store_operator.get_file_store_metadata().await.unwrap(); - - ensure!(metadata.chain_id == chain_id, "Chain ID mismatch."); - let batch_start_version = metadata.version; - // Cache config in the cache - cache_operator.cache_setup_if_needed().await?; - match cache_operator.get_chain_id().await? { - Some(id) => { - ensure!(id == chain_id, "Chain ID mismatch."); - }, - None => { - cache_operator.set_chain_id(chain_id).await?; - }, - } - cache_operator - .update_file_store_latest_version(batch_start_version) - .await?; - Ok(Self { - cache_operator, - file_store_operator, - chain_id, - }) - } - - /// Starts the processing. The steps are - /// 1. Check chain id at the beginning and every step after - /// 2. Get the batch start version from file store metadata - /// 3. Start loop - /// 3.1 Check head from cache, decide whether we need to parallel process or just wait - /// 3.2 If we're ready to process, create max of 10 threads and fetch / upload data - /// 3.3 Update file store metadata at the end of a batch - pub async fn run(&mut self) -> Result<()> { - let chain_id = self.chain_id; - - let metadata = self - .file_store_operator - .get_file_store_metadata() - .await - .unwrap(); - ensure!(metadata.chain_id == chain_id, "Chain ID mismatch."); - - let mut batch_start_version = metadata.version; - - let mut tps_calculator = MovingAverage::new(10_000); - loop { - let latest_loop_time = std::time::Instant::now(); - let cache_worker_latest = self.cache_operator.get_latest_version().await?.unwrap(); - - // batches tracks the start version of the batches to fetch. 1000 at the time - let mut batches = vec![]; - let mut start_version = batch_start_version; - while start_version + (FILE_ENTRY_TRANSACTION_COUNT) < cache_worker_latest { - batches.push(start_version); - start_version += FILE_ENTRY_TRANSACTION_COUNT; - } - - // we're too close to the head - if batches.is_empty() { - debug!( - batch_start_version = batch_start_version, - cache_worker_latest = cache_worker_latest, - "[Filestore] No enough version yet, need 1000 versions at least" - ); - tokio::time::sleep(Duration::from_millis( - AHEAD_OF_CACHE_SLEEP_DURATION_IN_MILLIS, - )) - .await; - continue; - } - - // Create thread and fetch transactions - let mut tasks = vec![]; - for start_version in batches { - let mut cache_operator_clone = self.cache_operator.clone(); - let mut file_store_operator_clone = self.file_store_operator.clone_box(); - let task = tokio::spawn(async move { - let fetch_start_time = std::time::Instant::now(); - let transactions = cache_operator_clone - .get_transactions(start_version, FILE_ENTRY_TRANSACTION_COUNT) - .await - .unwrap(); - let last_transaction = transactions.last().unwrap().clone(); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::FilestoreFetchTxns, - Some(start_version as i64), - Some((start_version + FILE_ENTRY_TRANSACTION_COUNT - 1) as i64), - None, - None, - Some(fetch_start_time.elapsed().as_secs_f64()), - None, - Some(FILE_ENTRY_TRANSACTION_COUNT as i64), - None, - ); - - let upload_start_time = std::time::Instant::now(); - let (start, end) = file_store_operator_clone - .upload_transaction_batch(chain_id, transactions) - .await - .unwrap(); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::FilestoreUploadTxns, - Some(start_version as i64), - Some((start_version + FILE_ENTRY_TRANSACTION_COUNT - 1) as i64), - None, - None, - Some(upload_start_time.elapsed().as_secs_f64()), - None, - Some(FILE_ENTRY_TRANSACTION_COUNT as i64), - None, - ); - - (start, end, last_transaction) - }); - tasks.push(task); - } - let (first_version, last_version, first_version_encoded, last_version_encoded) = - match futures::future::try_join_all(tasks).await { - Ok(mut res) => { - // Check for gaps - res.sort_by(|a, b| a.0.cmp(&b.0)); - let mut prev_start = None; - let mut prev_end = None; - - let first_version = res.first().unwrap().0; - let last_version = res.last().unwrap().1; - let first_version_encoded = res.first().unwrap().2.clone(); - let last_version_encoded = res.last().unwrap().2.clone(); - let versions: Vec = res.iter().map(|x| x.0).collect(); - for result in res { - let start = result.0; - let end = result.1; - if prev_start.is_none() { - prev_start = Some(start); - prev_end = Some(end); - } else { - if prev_end.unwrap() + 1 != start { - tracing::error!( - processed_versions = ?versions, - "[Filestore] Gaps in processing data" - ); - panic!("[Filestore] Gaps in processing data"); - } - prev_start = Some(start); - prev_end = Some(end); - } - } - - ( - first_version, - last_version, - first_version_encoded, - last_version_encoded, - ) - }, - Err(err) => panic!("Error processing transaction batches: {:?}", err), - }; - - // update next batch start version - batch_start_version = last_version + 1; - assert!( - batch_start_version % FILE_ENTRY_TRANSACTION_COUNT == 0, - "[Filestore] Batch must be multiple of 1000" - ); - let size = last_version - first_version + 1; - PROCESSED_VERSIONS_COUNT.inc_by(size); - tps_calculator.tick_now(size); - - // Update filestore metadata. First do it in cache for performance then update metadata file - let start_metadata_upload_time = std::time::Instant::now(); - self.cache_operator - .update_file_store_latest_version(batch_start_version) - .await?; - while self - .file_store_operator - .update_file_store_metadata_with_timeout(chain_id, batch_start_version) - .await - .is_err() - { - tracing::error!( - batch_start_version = batch_start_version, - "Failed to update file store metadata. Retrying." - ); - std::thread::sleep(std::time::Duration::from_millis(500)); - METADATA_UPLOAD_FAILURE_COUNT.inc(); - } - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::FilestoreUpdateMetadata, - Some(first_version as i64), - Some(last_version as i64), - None, - None, - Some(start_metadata_upload_time.elapsed().as_secs_f64()), - None, - Some(size as i64), - None, - ); - - let start_version_timestamp = first_version_encoded.timestamp; - let end_version_timestamp = last_version_encoded.timestamp; - let full_loop_duration = latest_loop_time.elapsed().as_secs_f64(); - log_grpc_step( - SERVICE_TYPE, - IndexerGrpcStep::FilestoreProcessedBatch, - Some(first_version as i64), - Some(last_version as i64), - start_version_timestamp.as_ref(), - end_version_timestamp.as_ref(), - Some(full_loop_duration), - None, - Some(size as i64), - None, - ); - } - } -} diff --git a/ecosystem/indexer-grpc/indexer-grpc-server-framework/Cargo.toml b/ecosystem/indexer-grpc/indexer-grpc-server-framework/Cargo.toml deleted file mode 100644 index a7348fbac5840..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-server-framework/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "aptos-indexer-grpc-server-framework" -description = "Indexer gRPC server framework library." -version = "1.0.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-runtimes = { workspace = true } -async-trait = { workspace = true } -backtrace = { workspace = true } -clap = { workspace = true } -futures = { workspace = true } -prometheus = { workspace = true } -serde = { workspace = true } -serde_yaml = { workspace = true } -tempfile = { workspace = true } -tokio = { workspace = true } -toml = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } -warp = { workspace = true } - -[target.'cfg(target_os = "linux")'.dependencies] -aptos-admin-service = { workspace = true } diff --git a/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md b/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md deleted file mode 100644 index 762fe0c22f455..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-server-framework/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Indexer GRPC server framework - -A boilerplate server runtime for indexer grpc infra. - -## Usage - -```rust -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ExampleConfig { - pub name: String, -} - -impl RunnableConfig for ExampleConfig { - fn run(&self) -> Result<()> { - println!("Hello, {}!", self.name); - Ok(()) - } - fn get_server_name(&self) -> String { - "srv_exp".to_string() - } -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = ServerArgs::parse(); - args.run::().await -} -``` \ No newline at end of file diff --git a/ecosystem/indexer-grpc/indexer-grpc-server-framework/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-server-framework/src/lib.rs deleted file mode 100644 index 05aae22948290..0000000000000 --- a/ecosystem/indexer-grpc/indexer-grpc-server-framework/src/lib.rs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright © Aptos Foundation - -use anyhow::{Context, Result}; -#[cfg(target_os = "linux")] -use aptos_admin_service::profiling::start_cpu_profiling; -use backtrace::Backtrace; -use clap::Parser; -use prometheus::{Encoder, TextEncoder}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -#[cfg(target_os = "linux")] -use std::convert::Infallible; -use std::{fs::File, io::Read, panic::PanicInfo, path::PathBuf, process}; -use tracing::error; -use tracing_subscriber::EnvFilter; -use warp::{http::Response, Filter}; - -/// ServerArgs bootstraps a server with all common pieces. And then triggers the run method for -/// the specific service. -#[derive(Parser)] -pub struct ServerArgs { - #[clap(short, long, value_parser)] - pub config_path: PathBuf, -} - -impl ServerArgs { - pub async fn run(&self) -> Result<()> - where - C: RunnableConfig, - { - // Set up the server. - setup_logging(None); - setup_panic_handler(); - let config = load::>(&self.config_path)?; - config - .validate() - .context("Config did not pass validation")?; - run_server_with_config(config).await - } -} - -pub async fn run_server_with_config(config: GenericConfig) -> Result<()> -where - C: RunnableConfig, -{ - let health_port = config.health_check_port; - // Start liveness and readiness probes. - let task_handler = tokio::spawn(async move { - register_probes_and_metrics_handler(health_port).await; - anyhow::Ok(()) - }); - let main_task_handler = - tokio::spawn(async move { config.run().await.expect("task should exit with Ok.") }); - tokio::select! { - res = task_handler => { - if let Err(e) = res { - error!("Probes and metrics handler panicked or was shutdown: {:?}", e); - process::exit(1); - } else { - panic!("Probes and metrics handler exited unexpectedly"); - } - }, - res = main_task_handler => { - if let Err(e) = res { - error!("Main task panicked or was shutdown: {:?}", e); - process::exit(1); - } else { - panic!("Main task exited unexpectedly"); - } - }, - } -} - -#[derive(Deserialize, Debug, Serialize)] -pub struct GenericConfig { - // Shared configuration among all services. - pub health_check_port: u16, - - // Specific configuration for each service. - pub server_config: T, -} - -#[async_trait::async_trait] -impl RunnableConfig for GenericConfig -where - T: RunnableConfig, -{ - fn validate(&self) -> Result<()> { - self.server_config.validate() - } - - async fn run(&self) -> Result<()> { - self.server_config.run().await - } - - fn get_server_name(&self) -> String { - self.server_config.get_server_name() - } -} - -/// RunnableConfig is a trait that all services must implement for their configuration. -#[async_trait::async_trait] -pub trait RunnableConfig: DeserializeOwned + Send + Sync + 'static { - // Validate the config. - fn validate(&self) -> Result<()> { - Ok(()) - } - - // Run something based on the config. - async fn run(&self) -> Result<()>; - - // Get the server name. - fn get_server_name(&self) -> String; -} - -/// Parse a yaml file into a struct. -pub fn load Deserialize<'de>>(path: &PathBuf) -> Result { - let mut file = - File::open(path).with_context(|| format!("failed to open the file at path: {:?}", path))?; - let mut contents = String::new(); - file.read_to_string(&mut contents) - .with_context(|| format!("failed to read the file at path: {:?}", path))?; - serde_yaml::from_str::(&contents).context("Unable to parse yaml file") -} - -#[derive(Debug, Serialize)] -pub struct CrashInfo { - details: String, - backtrace: String, -} - -/// Invoke to ensure process exits on a thread panic. -/// -/// Tokio's default behavior is to catch panics and ignore them. Invoking this function will -/// ensure that all subsequent thread panics (even Tokio threads) will report the -/// details/backtrace and then exit. -pub fn setup_panic_handler() { - std::panic::set_hook(Box::new(move |pi: &PanicInfo<'_>| { - handle_panic(pi); - })); -} - -// Formats and logs panic information -fn handle_panic(panic_info: &PanicInfo<'_>) { - // The Display formatter for a PanicInfo contains the message, payload and location. - let details = format!("{}", panic_info); - let backtrace = format!("{:#?}", Backtrace::new()); - let info = CrashInfo { details, backtrace }; - let crash_info = toml::to_string_pretty(&info).unwrap(); - error!("{}", crash_info); - // TODO / HACK ALARM: Write crash info synchronously via eprintln! to ensure it is written before the process exits which error! doesn't guarantee. - // This is a workaround until https://github.com/aptos-labs/aptos-core/issues/2038 is resolved. - eprintln!("{}", crash_info); - // Kill the process - process::exit(12); -} - -/// Set up logging for the server. By default we don't set a writer, in which case it -/// just logs to stdout. This can be overridden using the `make_writer` parameter. -/// This can be helpful for custom logging, e.g. logging to different files based on -/// the origin of the logging. -pub fn setup_logging(make_writer: Option Box + Send + Sync>>) { - let env_filter = EnvFilter::try_from_default_env() - .or_else(|_| EnvFilter::try_new("info")) - .unwrap(); - - let subscriber = tracing_subscriber::fmt() - .json() - .with_file(true) - .with_line_number(true) - .with_thread_ids(true) - .with_target(false) - .with_thread_names(true) - .with_env_filter(env_filter); - - match make_writer { - Some(w) => subscriber.with_writer(w).init(), - None => subscriber.init(), - } -} - -/// Register readiness and liveness probes and set up metrics endpoint. -async fn register_probes_and_metrics_handler(port: u16) { - let readiness = warp::path("readiness") - .map(move || warp::reply::with_status("ready", warp::http::StatusCode::OK)); - - let metrics_endpoint = warp::path("metrics").map(|| { - // Metrics encoding. - let metrics = aptos_metrics_core::gather(); - let mut encode_buffer = vec![]; - let encoder = TextEncoder::new(); - // If metrics encoding fails, we want to panic and crash the process. - encoder - .encode(&metrics, &mut encode_buffer) - .context("Failed to encode metrics") - .unwrap(); - - Response::builder() - .header("Content-Type", "text/plain") - .body(encode_buffer) - }); - - if cfg!(target_os = "linux") { - #[cfg(target_os = "linux")] - let profilez = warp::path("profilez").and_then(|| async move { - // TODO(grao): Consider make the parameters configurable. - Ok::<_, Infallible>(match start_cpu_profiling(10, 99, false).await { - Ok(body) => { - let response = Response::builder() - .header("Content-Length", body.len()) - .header("Content-Disposition", "inline") - .header("Content-Type", "image/svg+xml") - .body(body); - - match response { - Ok(res) => warp::reply::with_status(res, warp::http::StatusCode::OK), - Err(e) => warp::reply::with_status( - Response::new(format!("Profiling failed: {e:?}.").as_bytes().to_vec()), - warp::http::StatusCode::INTERNAL_SERVER_ERROR, - ), - } - }, - Err(e) => warp::reply::with_status( - Response::new(format!("Profiling failed: {e:?}.").as_bytes().to_vec()), - warp::http::StatusCode::INTERNAL_SERVER_ERROR, - ), - }) - }); - #[cfg(target_os = "linux")] - warp::serve(readiness.or(metrics_endpoint).or(profilez)) - .run(([0, 0, 0, 0], port)) - .await; - } else { - warp::serve(readiness.or(metrics_endpoint)) - .run(([0, 0, 0, 0], port)) - .await; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Write; - use tempfile::tempdir; - - #[derive(Clone, Debug, Deserialize, Serialize)] - #[serde(deny_unknown_fields)] - pub struct TestConfig { - test: u32, - test_name: String, - } - - #[async_trait::async_trait] - impl RunnableConfig for TestConfig { - async fn run(&self) -> Result<()> { - assert_eq!(self.test, 123); - assert_eq!(self.test_name, "test"); - Ok(()) - } - - fn get_server_name(&self) -> String { - self.test_name.clone() - } - } - - #[test] - fn test_random_config_creation() { - let dir = tempdir().expect("tempdir failure"); - - let file_path = dir.path().join("testing_yaml.yaml"); - let mut file = File::create(&file_path).expect("create failure"); - let raw_yaml_content = r#" - health_check_port: 12345 - server_config: - test: 123 - test_name: "test" - "#; - writeln!(file, "{}", raw_yaml_content).expect("write_all failure"); - - let config = load::>(&file_path).unwrap(); - assert_eq!(config.health_check_port, 12345); - assert_eq!(config.server_config.test, 123); - assert_eq!(config.server_config.test_name, "test"); - } - - #[test] - fn verify_tool() { - use clap::CommandFactory; - ServerArgs::command().debug_assert() - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/.gitignore b/ecosystem/nft-metadata-crawler-parser/.gitignore deleted file mode 100644 index a8da27a6b9cb3..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.csv -*.yaml \ No newline at end of file diff --git a/ecosystem/nft-metadata-crawler-parser/Cargo.toml b/ecosystem/nft-metadata-crawler-parser/Cargo.toml deleted file mode 100644 index d29dab05676db..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "aptos-nft-metadata-crawler-parser" -description = "NFT Metadata Crawler Parser service." -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -aptos-indexer-grpc-server-framework = { workspace = true } -aptos-metrics-core = { workspace = true } -aptos-runtimes = { workspace = true } -async-trait = { workspace = true } -backoff = { workspace = true } -base64 = { workspace = true } -bytes = { workspace = true } -chrono = { workspace = true } -clap = { workspace = true } -csv = { workspace = true } -diesel = { workspace = true, features = [ - "chrono", - "postgres", - "r2d2", - "numeric", - "serde_json", -] } -diesel_migrations = { workspace = true } -field_count = { workspace = true } -futures = { workspace = true } -google-cloud-storage = { workspace = true } -image = { workspace = true } -once_cell = { workspace = true } -regex = { workspace = true } -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -sha256 = { workspace = true } -tokio = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } -warp = { workspace = true } diff --git a/ecosystem/nft-metadata-crawler-parser/diesel.toml b/ecosystem/nft-metadata-crawler-parser/diesel.toml deleted file mode 100644 index fc51e3a3a875a..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/diesel.toml +++ /dev/null @@ -1,10 +0,0 @@ -# For documentation on how to configure this file, -# see https://diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "src/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId"] -schema = "nft_metadata_crawler" - -[migrations_directory] -dir = "migrations" diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/down.sql b/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/down.sql deleted file mode 100644 index 8fb31a8e5a6b5..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); -DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/up.sql b/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/up.sql deleted file mode 100644 index 08d6f2ac95319..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/00000000000000_diesel_initial_setup/up.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ -BEGIN - EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s - FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ -BEGIN - IF ( - NEW IS DISTINCT FROM OLD AND - NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at - ) THEN - NEW.updated_at := current_timestamp; - END IF; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/down.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/down.sql deleted file mode 100644 index bef594c7e3707..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/down.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP TABLE IF EXISTS nft_metadata_crawler.parsed_asset_uris; -DROP TABLE IF EXISTS nft_metadata_crawler.ledger_infos; -DROP SCHEMA IF EXISTS nft_metadata_crawler CASCADE; diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/up.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/up.sql deleted file mode 100644 index bcc9798f80525..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2023-09-08-001532_create_tables/up.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE SCHEMA IF NOT EXISTS nft_metadata_crawler; - -CREATE TABLE IF NOT EXISTS nft_metadata_crawler.parsed_asset_uris ( - asset_uri VARCHAR UNIQUE PRIMARY KEY NOT NULL, - raw_image_uri VARCHAR, - raw_animation_uri VARCHAR, - cdn_json_uri VARCHAR, - cdn_image_uri VARCHAR, - cdn_animation_uri VARCHAR, - json_parser_retry_count INT NOT NULL, - image_optimizer_retry_count INT NOT NULL, - animation_optimizer_retry_count INT NOT NULL, - inserted_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -CREATE TABLE IF NOT EXISTS nft_metadata_crawler.ledger_infos (chain_id BIGINT UNIQUE PRIMARY KEY NOT NULL); - -CREATE INDEX IF NOT EXISTS nft_raw_image_uri ON nft_metadata_crawler.parsed_asset_uris (raw_image_uri); -CREATE INDEX IF NOT EXISTS nft_raw_animation_uri ON nft_metadata_crawler.parsed_asset_uris (raw_animation_uri); -CREATE INDEX IF NOT EXISTS nft_inserted_at ON nft_metadata_crawler.parsed_asset_uris (inserted_at); diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/down.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/down.sql deleted file mode 100644 index 7987ebe0b2388..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE IF EXISTS nft_metadata_crawler.parsed_asset_uris DROP IF EXISTS COLUMN do_not_parse; diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/up.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/up.sql deleted file mode 100644 index e45716b423b52..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2024-01-31-221845_add_not_parsable_column/up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE IF EXISTS nft_metadata_crawler.parsed_asset_uris ADD COLUMN IF NOT EXISTS do_not_parse BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/down.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/down.sql deleted file mode 100644 index dce5cef16eabe..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE IF EXISTS nft_metadata_crawler.parsed_asset_uris DROP COLUMN IF EXISTS last_transaction_version; diff --git a/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/up.sql b/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/up.sql deleted file mode 100644 index 6d1073c489f7e..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/migrations/2024-02-08-013147_add_last_transaction_version/up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE IF EXISTS nft_metadata_crawler.parsed_asset_uris ADD COLUMN IF NOT EXISTS last_transaction_version BIGINT NOT NULL DEFAULT 0; diff --git a/ecosystem/nft-metadata-crawler-parser/src/config.rs b/ecosystem/nft-metadata-crawler-parser/src/config.rs deleted file mode 100644 index 2a23508cb27f1..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/config.rs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - utils::{ - constants::{ - DEFAULT_IMAGE_QUALITY, DEFAULT_MAX_FILE_SIZE_BYTES, DEFAULT_MAX_IMAGE_DIMENSIONS, - DEFAULT_MAX_NUM_PARSE_RETRIES, - }, - counters::{ - GOT_CONNECTION_COUNT, PARSER_FAIL_COUNT, PARSER_INVOCATIONS_COUNT, - PUBSUB_ACK_SUCCESS_COUNT, SKIP_URI_COUNT, UNABLE_TO_GET_CONNECTION_COUNT, - }, - database::{check_or_update_chain_id, establish_connection_pool, run_migrations}, - }, - worker::Worker, -}; -use aptos_indexer_grpc_server_framework::RunnableConfig; -use bytes::Bytes; -use diesel::{ - r2d2::{ConnectionManager, Pool}, - PgConnection, -}; -use google_cloud_storage::client::{Client as GCSClient, ClientConfig as GCSClientConfig}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; -use tracing::{error, info, warn}; -use warp::Filter; - -/// Structs to hold config from YAML -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ParserConfig { - pub google_application_credentials: Option, - pub bucket: String, - pub database_url: String, - pub cdn_prefix: String, - pub ipfs_prefix: String, - pub ipfs_auth_key: Option, - #[serde(default = "ParserConfig::default_max_file_size_bytes")] - pub max_file_size_bytes: u32, - #[serde(default = "ParserConfig::default_image_quality")] - pub image_quality: u8, // Quality up to 100 - #[serde(default = "ParserConfig::default_max_image_dimensions")] - pub max_image_dimensions: u32, - #[serde(default = "ParserConfig::default_max_num_parse_retries")] - pub max_num_parse_retries: i32, - #[serde(default)] - pub ack_parsed_uris: bool, - #[serde(default)] - pub uri_blacklist: Vec, - pub server_port: u16, -} - -impl ParserConfig { - pub const fn default_max_file_size_bytes() -> u32 { - DEFAULT_MAX_FILE_SIZE_BYTES - } - - pub const fn default_image_quality() -> u8 { - DEFAULT_IMAGE_QUALITY - } - - pub const fn default_max_image_dimensions() -> u32 { - DEFAULT_MAX_IMAGE_DIMENSIONS - } - - pub const fn default_max_num_parse_retries() -> i32 { - DEFAULT_MAX_NUM_PARSE_RETRIES - } -} - -#[async_trait::async_trait] -impl RunnableConfig for ParserConfig { - /// Main driver function that establishes a connection to Pubsub and parses the Pubsub entries in parallel - async fn run(&self) -> anyhow::Result<()> { - info!( - "[NFT Metadata Crawler] Starting parser with config: {:?}", - self - ); - - info!("[NFT Metadata Crawler] Connecting to database"); - let pool = establish_connection_pool(&self.database_url); - info!("[NFT Metadata Crawler] Database connection successful"); - - info!("[NFT Metadata Crawler] Running migrations"); - run_migrations(&pool); - info!("[NFT Metadata Crawler] Finished migrations"); - - if let Some(google_application_credentials) = &self.google_application_credentials { - info!( - "[NFT Metadata Crawler] Google Application Credentials path found, setting env var" - ); - std::env::set_var( - "GOOGLE_APPLICATION_CREDENTIALS", - google_application_credentials, - ); - } - - // Establish GCS client - let gcs_config = GCSClientConfig::default() - .with_auth() - .await - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to create gRPC client config" - ); - panic!(); - }); - - // Create request context - let context = Arc::new(ServerContext { - parser_config: Arc::new(self.clone()), - pool, - gcs_client: Arc::new(GCSClient::new(gcs_config)), - }); - - // Create web server - let route = warp::post() - .and(warp::path::end()) - .and(warp::body::bytes()) - .and(warp::any().map(move || context.clone())) - .and_then(handle_root); - warp::serve(route) - .run(([0, 0, 0, 0], self.server_port)) - .await; - Ok(()) - } - - fn get_server_name(&self) -> String { - "parser".to_string() - } -} - -/// Struct to hold context required for parsing -#[derive(Clone)] -pub struct ServerContext { - pub parser_config: Arc, - pub pool: Pool>, - pub gcs_client: Arc, -} - -/// Repeatedly pulls workers from Channel and perform parsing operations -async fn spawn_parser( - parser_config: Arc, - msg_base64: Bytes, - pool: Pool>, - gcs_client: Arc, -) { - PARSER_INVOCATIONS_COUNT.inc(); - let pubsub_message = String::from_utf8(msg_base64.to_vec()) - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse PubSub message" - ); - panic!(); - }) - .replace('\u{0000}', "") - .replace("\\u0000", ""); - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Received message from PubSub" - ); - - // Skips message if it does not have 5 commas (likely malformed URI) - if pubsub_message.matches(',').count() != 5 { - // Sends ack to PubSub only if ack_parsed_uris flag is true - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Number of commans != 5, skipping message" - ); - SKIP_URI_COUNT.with_label_values(&["invalid"]).inc(); - return; - } - - // Parse PubSub message - let parts: Vec<&str> = pubsub_message.split(',').collect(); - - // Perform chain id check - // If chain id is not set, set it - let mut conn = pool.get().unwrap_or_else(|e| { - error!( - pubsub_message = pubsub_message, - error = ?e, - "[NFT Metadata Crawler] Failed to get DB connection from pool"); - UNABLE_TO_GET_CONNECTION_COUNT.inc(); - panic!(); - }); - GOT_CONNECTION_COUNT.inc(); - - let grpc_chain_id = parts[4].parse::().unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse chain id from PubSub message" - ); - panic!(); - }); - - // Panic if chain id of PubSub message does not match chain id in DB - check_or_update_chain_id(&mut conn, grpc_chain_id as i64).expect("Chain id should match"); - - // Spawn worker - let last_transaction_version = parts[2].to_string().parse().unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse last transaction version from PubSub message" - ); - panic!(); - }); - let last_transaction_timestamp = - chrono::NaiveDateTime::parse_from_str(parts[3], "%Y-%m-%d %H:%M:%S %Z").unwrap_or( - chrono::NaiveDateTime::parse_from_str(parts[3], "%Y-%m-%d %H:%M:%S%.f %Z") - .unwrap_or_else(|e| { - error!( - error = ?e, - "[NFT Metadata Crawler] Failed to parse timestamp from PubSub message" - ); - panic!(); - }), - ); - let mut worker = Worker::new( - parser_config.clone(), - conn, - gcs_client.clone(), - &pubsub_message, - parts[0], - parts[1], - last_transaction_version, - last_transaction_timestamp, - parts[5].parse::().unwrap_or(false), - ); - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Starting worker" - ); - - if let Err(e) = worker.parse().await { - warn!( - pubsub_message = pubsub_message, - error = ?e, - "[NFT Metadata Crawler] Parsing failed" - ); - PARSER_FAIL_COUNT.inc(); - } - - info!( - pubsub_message = pubsub_message, - "[NFT Metadata Crawler] Worker finished" - ); -} - -/// Handles calling parser for the root endpoint -async fn handle_root( - msg: Bytes, - context: Arc, -) -> Result { - // Use spawn_blocking to run the function on a separate thread. - let _ = tokio::spawn(spawn_parser( - context.parser_config.clone(), - msg, - context.pool.clone(), - context.gcs_client.clone(), - )) - .await; - - if !context.parser_config.ack_parsed_uris { - return Ok(warp::reply::with_status( - warp::reply(), - warp::http::StatusCode::BAD_REQUEST, - )); - } - - PUBSUB_ACK_SUCCESS_COUNT.inc(); - Ok(warp::reply::with_status( - warp::reply(), - warp::http::StatusCode::OK, - )) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/lib.rs b/ecosystem/nft-metadata-crawler-parser/src/lib.rs deleted file mode 100644 index dd09816431ce0..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright © Aptos Foundation - -pub mod config; -pub mod models; -pub mod schema; -pub mod utils; -pub mod worker; - -use anyhow::Context; -use reqwest::{header, Client}; -use std::time::Duration; -use utils::constants::MAX_HEAD_REQUEST_RETRY_SECONDS; - -/// HEAD request to get MIME type and size of content -pub async fn get_uri_metadata(url: &str) -> anyhow::Result<(String, u32)> { - let client = Client::builder() - .timeout(Duration::from_secs(MAX_HEAD_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - let request = client.head(url.trim()); - let response = request.send().await?; - let headers = response.headers(); - - let mime_type = headers - .get(header::CONTENT_TYPE) - .map(|value| value.to_str().unwrap_or("text/plain")) - .unwrap_or("text/plain") - .to_string(); - let size = headers - .get(header::CONTENT_LENGTH) - .and_then(|value| value.to_str().ok()) - .and_then(|s| s.parse::().ok()) - .unwrap_or(0); - - Ok((mime_type, size)) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/main.rs b/ecosystem/nft-metadata-crawler-parser/src/main.rs deleted file mode 100644 index 58a0af6c6a3f6..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/main.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_indexer_grpc_server_framework::ServerArgs; -use aptos_nft_metadata_crawler_parser::config::ParserConfig; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = ::parse(); - args.run::().await -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/ledger_info.rs b/ecosystem/nft-metadata-crawler-parser/src/models/ledger_info.rs deleted file mode 100644 index 605c9598be543..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/ledger_info.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::schema::nft_metadata_crawler::ledger_infos; -use diesel::{ - prelude::*, - r2d2::{ConnectionManager, PooledConnection}, -}; - -#[derive(Debug, Identifiable, Insertable, Queryable)] -#[diesel(table_name = ledger_infos)] -#[diesel(primary_key(chain_id))] -pub struct LedgerInfo { - pub chain_id: i64, -} - -impl LedgerInfo { - pub fn get( - conn: &mut PooledConnection>, - ) -> diesel::QueryResult> { - ledger_infos::table - .select(ledger_infos::all_columns) - .first::(conn) - .optional() - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs b/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs deleted file mode 100644 index 4e6dd5424f49c..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation - -pub mod ledger_info; -pub mod nft_metadata_crawler_uris; -pub mod nft_metadata_crawler_uris_query; diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs b/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs deleted file mode 100644 index 25ec7a085434e..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - models::nft_metadata_crawler_uris_query::NFTMetadataCrawlerURIsQuery, - schema::nft_metadata_crawler::parsed_asset_uris, -}; -use diesel::prelude::*; -use field_count::FieldCount; -use serde::{Deserialize, Serialize}; -use tracing::warn; - -#[derive(Clone, Debug, Deserialize, FieldCount, Identifiable, Insertable, Serialize)] -#[diesel(primary_key(asset_uri))] -#[diesel(table_name = parsed_asset_uris)] -pub struct NFTMetadataCrawlerURIs { - asset_uri: String, - raw_image_uri: Option, - raw_animation_uri: Option, - cdn_json_uri: Option, - cdn_image_uri: Option, - cdn_animation_uri: Option, - json_parser_retry_count: i32, - image_optimizer_retry_count: i32, - animation_optimizer_retry_count: i32, - do_not_parse: bool, - last_transaction_version: i64, -} - -impl NFTMetadataCrawlerURIs { - pub fn new(asset_uri: &str) -> Self { - Self { - asset_uri: asset_uri.to_string(), - raw_image_uri: None, - raw_animation_uri: None, - cdn_json_uri: None, - cdn_image_uri: None, - cdn_animation_uri: None, - json_parser_retry_count: 0, - image_optimizer_retry_count: 0, - animation_optimizer_retry_count: 0, - do_not_parse: false, - last_transaction_version: 0, - } - } - - pub fn get_asset_uri(&self) -> String { - self.asset_uri.clone() - } - - pub fn set_asset_uri(&mut self, asset_uri: String) { - self.asset_uri = asset_uri; - } - - pub fn get_raw_image_uri(&self) -> Option { - let uri = self.raw_image_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] raw_image_uri is None" - ); - } - uri - } - - pub fn set_raw_image_uri(&mut self, raw_image_uri: Option) { - self.raw_image_uri = raw_image_uri; - } - - pub fn get_raw_animation_uri(&self) -> Option { - let uri = self.raw_animation_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] raw_animation_uri is None" - ); - } - uri - } - - pub fn set_raw_animation_uri(&mut self, raw_animation_uri: Option) { - self.raw_animation_uri = raw_animation_uri; - } - - pub fn get_cdn_json_uri(&self) -> Option { - let uri = self.cdn_json_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_json_uri is None" - ); - } - uri - } - - pub fn set_cdn_json_uri(&mut self, cdn_json_uri: Option) { - self.cdn_json_uri = cdn_json_uri; - } - - pub fn get_cdn_image_uri(&self) -> Option { - let uri = self.cdn_image_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_image_uri is None" - ); - } - uri - } - - pub fn set_cdn_image_uri(&mut self, cdn_image_uri: Option) { - self.cdn_image_uri = cdn_image_uri; - } - - pub fn get_cdn_animation_uri(&self) -> Option { - let uri = self.cdn_animation_uri.clone(); - if uri.is_none() { - warn!( - asset_uri = self.asset_uri, - "[NFT Metadata Crawler] cdn_animation_uri is None" - ); - } - uri - } - - pub fn set_cdn_animation_uri(&mut self, cdn_animation_uri: Option) { - self.cdn_animation_uri = cdn_animation_uri; - } - - pub fn get_json_parser_retry_count(&self) -> i32 { - self.json_parser_retry_count - } - - pub fn increment_json_parser_retry_count(&mut self) { - self.json_parser_retry_count += 1; - } - - pub fn reset_json_parser_retry_count(&mut self) { - self.json_parser_retry_count = 0; - } - - pub fn get_image_optimizer_retry_count(&self) -> i32 { - self.image_optimizer_retry_count - } - - pub fn increment_image_optimizer_retry_count(&mut self) { - self.image_optimizer_retry_count += 1; - } - - pub fn get_animation_optimizer_retry_count(&self) -> i32 { - self.animation_optimizer_retry_count - } - - pub fn increment_animation_optimizer_retry_count(&mut self) { - self.animation_optimizer_retry_count += 1; - } - - pub fn get_do_not_parse(&self) -> bool { - self.do_not_parse - } - - pub fn set_do_not_parse(&mut self, do_not_parse: bool) { - self.do_not_parse = do_not_parse; - } - - pub fn get_last_transaction_version(&self) -> i64 { - self.last_transaction_version - } - - pub fn set_last_transaction_version(&mut self, last_transaction_version: i64) { - self.last_transaction_version = last_transaction_version; - } -} - -impl From for NFTMetadataCrawlerURIs { - fn from(query: NFTMetadataCrawlerURIsQuery) -> Self { - Self { - asset_uri: query.asset_uri, - raw_image_uri: query.raw_image_uri, - raw_animation_uri: query.raw_animation_uri, - cdn_json_uri: query.cdn_json_uri, - cdn_image_uri: query.cdn_image_uri, - cdn_animation_uri: query.cdn_animation_uri, - json_parser_retry_count: query.json_parser_retry_count, - image_optimizer_retry_count: query.image_optimizer_retry_count, - animation_optimizer_retry_count: query.animation_optimizer_retry_count, - do_not_parse: query.do_not_parse, - last_transaction_version: query.last_transaction_version, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs b/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs deleted file mode 100644 index f076ba81fc9e5..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/models/nft_metadata_crawler_uris_query.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - schema::nft_metadata_crawler::parsed_asset_uris, utils::constants::MAX_RETRY_TIME_SECONDS, -}; -use backoff::{retry, ExponentialBackoff}; -use diesel::{ - prelude::*, - r2d2::{ConnectionManager, PooledConnection}, -}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tracing::error; - -#[derive(Debug, Deserialize, Identifiable, Queryable, Serialize)] -#[diesel(primary_key(asset_uri))] -#[diesel(table_name = parsed_asset_uris)] -pub struct NFTMetadataCrawlerURIsQuery { - pub asset_uri: String, - pub raw_image_uri: Option, - pub raw_animation_uri: Option, - pub cdn_json_uri: Option, - pub cdn_image_uri: Option, - pub cdn_animation_uri: Option, - pub json_parser_retry_count: i32, - pub image_optimizer_retry_count: i32, - pub animation_optimizer_retry_count: i32, - pub inserted_at: chrono::NaiveDateTime, - pub do_not_parse: bool, - pub last_transaction_version: i64, -} - -impl NFTMetadataCrawlerURIsQuery { - pub fn get_by_asset_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .find(asset_uri) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_asset_uri"); - None - }) - } - - pub fn get_by_raw_image_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - raw_image_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .filter(parsed_asset_uris::raw_image_uri.eq(raw_image_uri)) - .filter(parsed_asset_uris::asset_uri.ne(asset_uri)) - .filter(parsed_asset_uris::cdn_image_uri.is_not_null()) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_raw_image_uri"); - None - }) - } - - pub fn get_by_raw_animation_uri( - conn: &mut PooledConnection>, - asset_uri: &str, - raw_animation_uri: &str, - ) -> Option { - let mut op = || { - parsed_asset_uris::table - .filter(parsed_asset_uris::raw_animation_uri.eq(raw_animation_uri)) - .filter(parsed_asset_uris::asset_uri.ne(asset_uri)) - .filter(parsed_asset_uris::cdn_animation_uri.is_not_null()) - .first::(conn) - .optional() - .map_err(Into::into) - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - retry(backoff, &mut op).unwrap_or_else(|e| { - error!(asset_uri = asset_uri, error=?e, "Failed to get_by_raw_animation_uri"); - None - }) - } -} - -impl Default for NFTMetadataCrawlerURIsQuery { - fn default() -> Self { - Self { - asset_uri: "".to_string(), - raw_image_uri: None, - raw_animation_uri: None, - cdn_json_uri: None, - cdn_image_uri: None, - cdn_animation_uri: None, - json_parser_retry_count: 0, - image_optimizer_retry_count: 0, - animation_optimizer_retry_count: 0, - inserted_at: chrono::NaiveDateTime::default(), - do_not_parse: false, - last_transaction_version: 0, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/schema.rs b/ecosystem/nft-metadata-crawler-parser/src/schema.rs deleted file mode 100644 index f18c47ac3a4ec..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/schema.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © Aptos Foundation - -// @generated automatically by Diesel CLI. - -pub mod nft_metadata_crawler { - diesel::table! { - nft_metadata_crawler.ledger_infos (chain_id) { - chain_id -> Int8, - } - } - - diesel::table! { - nft_metadata_crawler.parsed_asset_uris (asset_uri) { - asset_uri -> Varchar, - raw_image_uri -> Nullable, - raw_animation_uri -> Nullable, - cdn_json_uri -> Nullable, - cdn_image_uri -> Nullable, - cdn_animation_uri -> Nullable, - json_parser_retry_count -> Int4, - image_optimizer_retry_count -> Int4, - animation_optimizer_retry_count -> Int4, - inserted_at -> Timestamp, - do_not_parse -> Bool, - last_transaction_version -> Int8, - } - } - - diesel::allow_tables_to_appear_in_same_query!(ledger_infos, parsed_asset_uris,); -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs deleted file mode 100644 index 543d20b7af31c..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/constants.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © Aptos Foundation - -/// Maximum retry time for exponential backoff (2 sec = 3-4 retries) -pub const MAX_RETRY_TIME_SECONDS: u64 = 2; - -/// Allocate 15 seconds for a HEAD request -pub const MAX_HEAD_REQUEST_RETRY_SECONDS: u64 = 15; - -/// Allocate 30 seconds for downloading large JSON files -pub const MAX_JSON_REQUEST_RETRY_SECONDS: u64 = 30; - -/// Allocate 90 seconds for downloading large image files -pub const MAX_IMAGE_REQUEST_RETRY_SECONDS: u64 = 90; - -/// Max number of retries for a given asset_uri -pub const DEFAULT_MAX_NUM_PARSE_RETRIES: i32 = 3; - -/// Default 15 MB maximum file size for files to be downloaded -pub const DEFAULT_MAX_FILE_SIZE_BYTES: u32 = 15_000_000; - -/// Default 100% image quality for image optimization -pub const DEFAULT_IMAGE_QUALITY: u8 = 100; - -/// Default 4096 maximum image dimensions for image optimization -pub const DEFAULT_MAX_IMAGE_DIMENSIONS: u32 = 4_096; - -/// Default IPFS gateway auth param key -pub const IPFS_AUTH_KEY: &str = "pinataGatewayToken"; diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs deleted file mode 100644 index 3eaa98ca19ab5..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/counters.rs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright © Aptos Foundation - -use aptos_metrics_core::{ - register_int_counter, register_int_counter_vec, IntCounter, IntCounterVec, -}; -use once_cell::sync::Lazy; - -// OVERALL METRICS - -/// Number of times the NFT Metadata Crawler Parser has been invoked -pub static PARSER_INVOCATIONS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_invocation_count", - "Number of times the parser has been invoked", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has completed successfully -pub static PARSER_SUCCESSES_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_success_count", - "Number of times the parser has completed successfully", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed -pub static PARSER_FAIL_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_fail_count", - "Number of times the parser has failed", - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has received a URI marked as not to parse -pub static DO_NOT_PARSE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_do_not_parse_count", - "Number of times the parser received a URI marked as not to parse", - ) - .unwrap() -}); - -// PUBSUB METRICS - -/// Number of times a PubSub message has successfully been ACK'd -pub static PUBSUB_ACK_SUCCESS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_pubsub_ack_success_count", - "Number of times a PubSub message has successfully been ACK'd", - ) - .unwrap() -}); - -// POSTGRES METRICS - -/// Number of times the connection pool has timed out when trying to get a connection -pub static UNABLE_TO_GET_CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_connection_pool_err", - "Number of times the connection pool has timed out when trying to get a connection" - ) - .unwrap() -}); - -/// Number of times the connection pool got a connection -pub static GOT_CONNECTION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "indexer_connection_pool_ok", - "Number of times the connection pool got a connection" - ) - .unwrap() -}); - -// DEDUPLICATION METRICS - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate asset URI -pub static DUPLICATE_ASSET_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_asset_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate asset URI" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate raw image URI -pub static DUPLICATE_RAW_IMAGE_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_raw_image_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate raw image URI" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has found a duplicate raw animation URI -pub static DUPLICATE_RAW_ANIMATION_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_duplicate_raw_animation_uri_count", - "Number of times the NFT Metadata Crawler Parser has found a duplicate raw animation URI" - ) - .unwrap() -}); - -// URI PARSER METRICS - -/// Number of URIs skipped because of matches on the URI skip list -pub static SKIP_URI_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_skip_uri_count", - "Number of URIs skipped because of matches on the URI skip list", - &["reason"] - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has invocated the URI Parser -pub static PARSE_URI_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_parse_uri_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the URI Parser" - ) - .unwrap() -}); - -/// Number of times a given URI type has been parsed -pub static PARSE_URI_TYPE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_parse_uri_type_count", - "Number of times a given URI type has been parsed", - &["uri_type"] - ) - .unwrap() -}); - -// JSON PARSER METRICS - -/// Number of times the NFT Metadata Crawler has invocated the JSON Parser -pub static PARSE_JSON_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_parse_json_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the JSON Parser" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has been able to parse a JSON -pub static SUCCESSFULLY_PARSED_JSON_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_parsed_json_count", - "Number of times the NFT Metadata Crawler Parser has been able to parse a JSON" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed to parse a JSON and the error type -pub static FAILED_TO_PARSE_JSON_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_failed_to_parse_json_count", - "Number of times the NFT Metadata Crawler Parser has failed to parse a JSON", - &["error_type"] - ) - .unwrap() -}); - -// IMAGE OPTIMIZER METRICS - -/// Number of times the NFT Metadata Crawler Parser has invocated the Image Optimizer for an image -pub static OPTIMIZE_IMAGE_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_optimize_image_invocation_count", - "Number of times the NFT Metadata Crawler Parser has invocated the Image Optimizer for an image" - ) - .unwrap() -}); - -/// Number of times a given image type has been optimized -pub static OPTIMIZE_IMAGE_TYPE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_optimize_image_type_count", - "Number of times a given image type has been optimized", - &["image_type"] - ) - .unwrap() -}); - -/// Number of times the Image Optimizer has been able to optimize an image -pub static SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_optimized_image_count", - "Number of times the Image Optimizer has been able to optimize an image" - ) - .unwrap() -}); - -/// Number of times the Image Optimizer has failed to optimize an image and the error type -pub static FAILED_TO_OPTIMIZE_IMAGE_COUNT: Lazy = Lazy::new(|| { - register_int_counter_vec!( - "nft_metadata_crawler_parser_failed_to_optimize_image_count", - "Number of times the NFT Metadata Crawler Parser has failed to optimize an image and the error type", - &["error_type"] - ) - .unwrap() -}); - -/// GCS METRICS - -/// Number of times the NFT Metadata Crawler Parser has attempted to upload to GCS -pub static GCS_UPLOAD_INVOCATION_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_gcs_upload_invocation_count", - "Number of times the NFT Metadata Crawler Parser has attempted to upload to GCS" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has successfully uploaded to GCS -pub static SUCCESSFULLY_UPLOADED_TO_GCS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_successfully_uploaded_to_gcs_count", - "Number of times the NFT Metadata Crawler Parser has successfully uploaded to GCS" - ) - .unwrap() -}); - -/// Number of times the NFT Metadata Crawler Parser has failed to upload to GCS -pub static FAILED_TO_UPLOAD_TO_GCS_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "nft_metadata_crawler_parser_failed_to_upload_to_gcs_count", - "Number of times the NFT Metadata Crawler Parser has failed to upload to GCS" - ) - .unwrap() -}); diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs deleted file mode 100644 index 9466748d2d91d..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/database.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - models::{ledger_info::LedgerInfo, nft_metadata_crawler_uris::NFTMetadataCrawlerURIs}, - schema, -}; -use anyhow::Context; -use diesel::{ - r2d2::{ConnectionManager, Pool, PooledConnection}, - upsert::excluded, - ExpressionMethods, PgConnection, RunQueryDsl, -}; -use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; -use tracing::{debug, info}; - -pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); - -/// Establishes a connection pool to Postgres -pub fn establish_connection_pool(database_url: &str) -> Pool> { - let manager = ConnectionManager::::new(database_url); - Pool::builder() - .build(manager) - .expect("Failed to create pool.") -} - -/// Runs database migrations -pub fn run_migrations(pool: &Pool>) { - pool.get() - .expect("[NFT Metadata Crawler] Could not get connection for migrations") - .run_pending_migrations(MIGRATIONS) - .expect("[NFT Metadata Crawler] migrations failed!"); -} - -/// Upserts URIs into database -pub fn upsert_uris( - conn: &mut PooledConnection>, - entry: &NFTMetadataCrawlerURIs, - ltv: i64, -) -> anyhow::Result { - use schema::nft_metadata_crawler::parsed_asset_uris::dsl::*; - - let query = diesel::insert_into(schema::nft_metadata_crawler::parsed_asset_uris::table) - .values(entry) - .on_conflict(asset_uri) - .do_update() - .set(( - raw_image_uri.eq(excluded(raw_image_uri)), - raw_animation_uri.eq(excluded(raw_animation_uri)), - cdn_json_uri.eq(excluded(cdn_json_uri)), - cdn_image_uri.eq(excluded(cdn_image_uri)), - cdn_animation_uri.eq(excluded(cdn_animation_uri)), - image_optimizer_retry_count.eq(excluded(image_optimizer_retry_count)), - json_parser_retry_count.eq(excluded(json_parser_retry_count)), - animation_optimizer_retry_count.eq(excluded(animation_optimizer_retry_count)), - inserted_at.eq(excluded(inserted_at)), - do_not_parse.eq(excluded(do_not_parse)), - last_transaction_version.eq(ltv), - )); - - let debug_query = diesel::debug_query::(&query).to_string(); - debug!("Executing Query: {}", debug_query); - query.execute(conn).context(debug_query) -} - -/// Verify the chain id from PubSub against the database. -pub fn check_or_update_chain_id( - conn: &mut PooledConnection>, - pubsub_chain_id: i64, -) -> anyhow::Result { - info!("[NFT Metadata Crawler] Checking if chain id is correct"); - - let maybe_existing_chain_id = LedgerInfo::get(conn)?.map(|li| li.chain_id); - - match maybe_existing_chain_id { - Some(chain_id) => { - anyhow::ensure!(chain_id == pubsub_chain_id, "[NFT Metadata Crawler] Wrong chain detected! Trying to index chain {} now but existing data is for chain {}", pubsub_chain_id, chain_id); - info!( - chain_id = chain_id, - "[NFT Metadata Crawler] Chain id matches! Continue to index...", - ); - Ok(chain_id as u64) - }, - None => { - info!( - chain_id = pubsub_chain_id, - "[NFT Metadata Crawler] Adding chain id to db, continue to index.." - ); - insert_chain_id(conn, pubsub_chain_id).map(|_| pubsub_chain_id as u64) - }, - } -} - -/// Updates chain id in database -fn insert_chain_id( - conn: &mut PooledConnection>, - grpc_chain_id: i64, -) -> anyhow::Result { - let query = - diesel::insert_into(schema::nft_metadata_crawler::ledger_infos::table).values(LedgerInfo { - chain_id: grpc_chain_id, - }); - - let debug_query = diesel::debug_query::(&query).to_string(); - debug!("Executing Query: {}", debug_query); - query.execute(conn).context(debug_query) -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs deleted file mode 100644 index b615aeb96fd31..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/gcs.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::utils::{ - constants::MAX_RETRY_TIME_SECONDS, - counters::{ - FAILED_TO_UPLOAD_TO_GCS_COUNT, GCS_UPLOAD_INVOCATION_COUNT, - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use google_cloud_storage::{ - client::Client, - http::objects::upload::{Media, UploadObjectRequest, UploadType}, -}; -use image::ImageFormat; -use serde_json::Value; -use std::time::Duration; - -/// Writes JSON Value to GCS -pub async fn write_json_to_gcs( - bucket: &str, - uri: &str, - json: &Value, - client: &Client, -) -> anyhow::Result { - GCS_UPLOAD_INVOCATION_COUNT.inc(); - let hashed_uri = sha256::digest(uri); - let filename = format!("cdn/{}.json", hashed_uri); - let json_string = json.to_string(); - let json_bytes = json_string.into_bytes(); - - let upload_type = UploadType::Simple(Media { - name: filename.clone().into(), - content_type: "application/json".into(), - content_length: Some(json_bytes.len() as u64), - }); - - let op = || { - async { - Ok(client - .upload_object( - &UploadObjectRequest { - bucket: bucket.to_string(), - ..Default::default() - }, - json_bytes.clone(), - &upload_type, - ) - .await - .context("Error uploading JSON to GCS")?) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(_) => { - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT.inc(); - Ok(filename) - }, - Err(e) => { - FAILED_TO_UPLOAD_TO_GCS_COUNT.inc(); - Err(e) - }, - } -} - -/// Infers file type and writes image to GCS -pub async fn write_image_to_gcs( - img_format: ImageFormat, - bucket: &str, - uri: &str, - buffer: Vec, - client: &Client, -) -> anyhow::Result { - GCS_UPLOAD_INVOCATION_COUNT.inc(); - let hashed_uri = sha256::digest(uri); - let extension = match img_format { - ImageFormat::Gif | ImageFormat::Avif | ImageFormat::Png => img_format - .extensions_str() - .last() - .expect("ImageFormat should have at least one extension") - .to_string(), - _ => "jpeg".to_string(), - }; - - let filename = format!("cdn/{}.{}", hashed_uri, extension); - let upload_type = UploadType::Simple(Media { - name: filename.clone().into(), - content_type: format!("image/{}", extension).into(), - content_length: Some(buffer.len() as u64), - }); - - let op = || { - async { - Ok(client - .upload_object( - &UploadObjectRequest { - bucket: bucket.to_string(), - ..Default::default() - }, - buffer.clone(), - &upload_type, - ) - .await - .context("Error uploading image to GCS")?) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(_) => { - SUCCESSFULLY_UPLOADED_TO_GCS_COUNT.inc(); - Ok(filename) - }, - Err(e) => { - FAILED_TO_UPLOAD_TO_GCS_COUNT.inc(); - Err(e) - }, - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs deleted file mode 100644 index e9e48fd2b6b8e..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/image_optimizer.rs +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - get_uri_metadata, - utils::{ - constants::{MAX_IMAGE_REQUEST_RETRY_SECONDS, MAX_RETRY_TIME_SECONDS}, - counters::{ - FAILED_TO_OPTIMIZE_IMAGE_COUNT, OPTIMIZE_IMAGE_INVOCATION_COUNT, - SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT, - }, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use image::{ - imageops::{resize, FilterType}, - DynamicImage, GenericImageView, ImageBuffer, ImageFormat, ImageOutputFormat, -}; -use reqwest::Client; -use std::{ - cmp::{max, min}, - io::Cursor, - time::Duration, -}; -use tracing::{info, warn}; - -pub struct ImageOptimizer; - -impl ImageOptimizer { - /// Resizes and optimizes image from input URI. - /// Returns new image as a byte array and its format. - pub async fn optimize( - uri: &str, - max_file_size_bytes: u32, - image_quality: u8, - max_image_dimensions: u32, - ) -> anyhow::Result<(Vec, ImageFormat)> { - OPTIMIZE_IMAGE_INVOCATION_COUNT.inc(); - let (_, size) = get_uri_metadata(uri).await?; - if size > max_file_size_bytes { - FAILED_TO_OPTIMIZE_IMAGE_COUNT - .with_label_values(&["Image file too large"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "Image optimizer received file too large: {} bytes, skipping", - size - ))); - } - - let op = || { - async { - info!(image_uri = uri, "Sending request for image"); - - let client = Client::builder() - .timeout(Duration::from_secs(MAX_IMAGE_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - - let response = client - .get(uri.trim()) - .send() - .await - .context("Failed to get image")?; - - let img_bytes = response - .bytes() - .await - .context("Failed to load image bytes")?; - - let format = - image::guess_format(&img_bytes).context("Failed to guess image format")?; - - match format { - ImageFormat::Gif | ImageFormat::Avif => Ok((img_bytes.to_vec(), format)), - _ => { - let img = image::load_from_memory(&img_bytes) - .context(format!("Failed to load image from memory: {} bytes", size))?; - let (nwidth, nheight) = Self::calculate_dimensions_with_ration( - min(max(img.width(), img.height()), max_image_dimensions), - img.width(), - img.height(), - ); - let resized_image = - resize(&img.to_rgba8(), nwidth, nheight, FilterType::Gaussian); - Ok(Self::to_image_bytes(resized_image, image_quality)?) - }, - } - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(result) => { - SUCCESSFULLY_OPTIMIZED_IMAGE_COUNT.inc(); - Ok(result) - }, - Err(e) => { - FAILED_TO_OPTIMIZE_IMAGE_COUNT - .with_label_values(&["other"]) - .inc(); - Err(e) - }, - } - } - - /// Calculate new dimensions given a goal size while maintaining original aspect ratio - fn calculate_dimensions_with_ration(goal: u32, width: u32, height: u32) -> (u32, u32) { - if width == 0 || height == 0 { - return (0, 0); - } - - if width > height { - let new_width = goal; - let new_height = (goal as f64 * (height as f64 / width as f64)).round() as u32; - (new_width, new_height) - } else { - let new_height = goal; - let new_width = (goal as f64 * (width as f64 / height as f64)).round() as u32; - (new_width, new_height) - } - } - - /// Checks if an image has any transparent pixels - fn has_transparent_pixels(img: &DynamicImage) -> bool { - let (width, height) = img.dimensions(); - for x in 0..width { - for y in 0..height { - if img.get_pixel(x, y)[3] < 255 { - return true; - } - } - } - false - } - - /// Converts image to image bytes vector - fn to_image_bytes( - image_buffer: ImageBuffer, Vec>, - image_quality: u8, - ) -> anyhow::Result<(Vec, ImageFormat)> { - let dynamic_image = DynamicImage::ImageRgba8(image_buffer); - let mut byte_store = Cursor::new(Vec::new()); - let mut encode_format = ImageOutputFormat::Jpeg(image_quality); - let mut output_format = ImageFormat::Jpeg; - if Self::has_transparent_pixels(&dynamic_image) { - encode_format = ImageOutputFormat::Png; - output_format = ImageFormat::Png; - } - - match dynamic_image.write_to(&mut byte_store, encode_format) { - Ok(_) => Ok((byte_store.into_inner(), output_format)), - Err(e) => { - warn!(error = ?e, "[NFT Metadata Crawler] Error converting image to bytes: {} bytes", dynamic_image.as_bytes().len()); - Err(anyhow::anyhow!(e)) - }, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs deleted file mode 100644 index 234db7a0aa4de..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/json_parser.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - get_uri_metadata, - utils::{ - constants::{MAX_JSON_REQUEST_RETRY_SECONDS, MAX_RETRY_TIME_SECONDS}, - counters::{ - FAILED_TO_PARSE_JSON_COUNT, PARSE_JSON_INVOCATION_COUNT, SUCCESSFULLY_PARSED_JSON_COUNT, - }, - }, -}; -use anyhow::Context; -use backoff::{future::retry, ExponentialBackoff}; -use futures::FutureExt; -use image::ImageFormat; -use reqwest::Client; -use serde_json::Value; -use std::time::Duration; -use tracing::info; - -pub struct JSONParser; - -impl JSONParser { - /// Parses JSON from input URI. - /// Returns the underlying raw image URI, raw animation URI, and JSON. - pub async fn parse( - uri: String, - max_file_size_bytes: u32, - ) -> anyhow::Result<(Option, Option, Value)> { - PARSE_JSON_INVOCATION_COUNT.inc(); - let (mime, size) = get_uri_metadata(&uri).await?; - if ImageFormat::from_mime_type(&mime).is_some() { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["found image instead"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "JSON parser received image file: {}, skipping", - mime - ))); - } else if size > max_file_size_bytes { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["json file too large"]) - .inc(); - return Err(anyhow::anyhow!(format!( - "JSON parser received file too large: {} bytes, skipping", - size - ))); - } - - let op = || { - async { - info!(asset_uri = uri, "Sending request for asset_uri"); - - let client = Client::builder() - .timeout(Duration::from_secs(MAX_JSON_REQUEST_RETRY_SECONDS)) - .build() - .context("Failed to build reqwest client")?; - - let response = client - .get(uri.trim()) - .send() - .await - .context("Failed to get JSON")?; - - let parsed_json = response - .json::() - .await - .context("Failed to parse JSON")?; - - let raw_image_uri = parsed_json["image"].as_str().map(|s| s.to_string()); - let raw_animation_uri = - parsed_json["animation_url"].as_str().map(|s| s.to_string()); - - Ok((raw_image_uri, raw_animation_uri, parsed_json)) - } - .boxed() - }; - - let backoff = ExponentialBackoff { - max_elapsed_time: Some(Duration::from_secs(MAX_RETRY_TIME_SECONDS)), - ..Default::default() - }; - - match retry(backoff, op).await { - Ok(result) => { - SUCCESSFULLY_PARSED_JSON_COUNT.inc(); - Ok(result) - }, - Err(e) => { - FAILED_TO_PARSE_JSON_COUNT - .with_label_values(&["other"]) - .inc(); - Err(e) - }, - } - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs deleted file mode 100644 index 4e01fb20351e7..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Aptos Foundation - -pub mod constants; -pub mod counters; -pub mod database; -pub mod gcs; -pub mod image_optimizer; -pub mod json_parser; -pub mod uri_parser; diff --git a/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs b/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs deleted file mode 100644 index f4e0c84da829d..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/utils/uri_parser.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::utils::{ - constants::IPFS_AUTH_KEY, - counters::{PARSE_URI_INVOCATION_COUNT, PARSE_URI_TYPE_COUNT}, -}; -use regex::{Captures, Regex}; -use url::Url; - -pub struct URIParser; - -impl URIParser { - /// Attempts to parse IPFS URI to use dedicated gateway. - /// Returns the original URI if parsing fails. - pub fn parse( - ipfs_prefix: &str, - uri: &str, - ipfs_auth_key: Option<&str>, - ) -> anyhow::Result { - PARSE_URI_INVOCATION_COUNT.inc(); - if uri.contains("arweave.net") { - PARSE_URI_TYPE_COUNT.with_label_values(&["arweave"]).inc(); - return Ok(uri.to_string()); - } - - let modified_uri = if uri.starts_with("ipfs://") { - uri.replace("ipfs://", "https://ipfs.com/ipfs/") - } else { - uri.to_string() - }; - - let ipfs_auth_param = if ipfs_auth_key.is_some() { - Some(format!("?{}={}", IPFS_AUTH_KEY, ipfs_auth_key.unwrap())) - } else { - None - }; - - // Expects the following format for provided URIs `ipfs/{CID}/{path}` - let re = Regex::new(r"^(ipfs/)(?P[a-zA-Z0-9]+)(?P/.*)?$")?; - - // Expects the following format for provided URIs `https://{CID}.ipfs.com/{path}` - let redir_re = Regex::new(r"https:\/\/(?P[^\.]+)\.ipfs\.[^\/]+(?P\/.+)?")?; - - let path = Url::parse(&modified_uri)? - .path_segments() - .map(|segments| segments.collect::>().join("/")); - - if let Some(captures) = re - .captures(&path.unwrap_or_default()) - .or_else(|| redir_re.captures(&modified_uri)) - { - return Self::format_capture(captures, ipfs_prefix, ipfs_auth_param); - } - Err(anyhow::anyhow!("Invalid IPFS URI")) - } - - /// Formats a capture group into a URI. - fn format_capture( - captures: Captures<'_>, - ipfs_prefix: &str, - ipfs_auth_param: Option, - ) -> anyhow::Result { - let cid = captures["cid"].to_string(); - let path = captures.name("path").map(|m| m.as_str().to_string()); - - PARSE_URI_TYPE_COUNT.with_label_values(&["ipfs"]).inc(); - Ok(format!( - "{}{}{}{}", - ipfs_prefix, - cid, - path.unwrap_or_default(), - ipfs_auth_param.unwrap_or_default() - )) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const IPFS_PREFIX: &str = "https://testipfsprefix.com/ipfs/"; - const IPFS_AUTH: &str = "token"; - const CID: &str = "testcid"; - const PATH: &str = "testpath"; - - #[test] - fn test_parse_ipfs_uri() { - let test_ipfs_uri = format!("ipfs://{}/{}", CID, PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri, Some(IPFS_AUTH)).unwrap(); - assert_eq!( - parsed_uri, - format!("{IPFS_PREFIX}{CID}/{PATH}?{IPFS_AUTH_KEY}={IPFS_AUTH}") - ); - - // Path is optional for IPFS URIs - let test_ipfs_uri_no_path = format!("ipfs://{}/{}", CID, ""); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri_no_path, None).unwrap(); - assert_eq!(parsed_uri, format!("{}{}/{}", IPFS_PREFIX, CID, "")); - - // IPFS URIs must contain a CID, expect error here - let test_ipfs_uri_no_cid = format!("ipfs://{}/{}", "", PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_uri_no_cid, None); - assert!(parsed_uri.is_err()); - } - - #[test] - fn test_parse_public_gateway_uri() { - let test_public_gateway_uri = format!("https://ipfs.io/ipfs/{}/{}", CID, PATH); - let parsed_uri = - URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri, Some(IPFS_AUTH)).unwrap(); - assert_eq!( - parsed_uri, - format!("{IPFS_PREFIX}{CID}/{PATH}?{IPFS_AUTH_KEY}={IPFS_AUTH}") - ); - - // Path is optional for public gateway URIs - let test_public_gateway_uri_no_path = format!("https://ipfs.io/ipfs/{}/{}", CID, ""); - let parsed_uri = - URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri_no_path, None).unwrap(); - assert_eq!(parsed_uri, format!("{}{}/{}", IPFS_PREFIX, CID, "")); - - // Some submitted URIs are in the redirected format - let test_ipfs_redirect = format!("https://{}.ipfs.re.dir.io/{}", CID, PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_ipfs_redirect, None).unwrap(); - assert_eq!(parsed_uri, format!("{IPFS_PREFIX}{CID}/{PATH}")); - - // Public gateway URIs must contain a CID, expect error here - let test_public_gateway_uri_no_cid = format!("https://ipfs.io/ipfs/{}/{}", "", PATH); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_public_gateway_uri_no_cid, None); - assert!(parsed_uri.is_err()); - } - - #[test] - fn test_parse_non_ipfs_uri_fail() { - // Expects an error if parsing a non-IPFS URI - let test_non_ipfs_uri = "https://tesetnotipfsuri.com/notipfspath.json".to_string(); - let parsed_uri = URIParser::parse(IPFS_PREFIX, &test_non_ipfs_uri, None); - assert!(parsed_uri.is_err()); - } -} diff --git a/ecosystem/nft-metadata-crawler-parser/src/worker.rs b/ecosystem/nft-metadata-crawler-parser/src/worker.rs deleted file mode 100644 index 1c985910af4e9..0000000000000 --- a/ecosystem/nft-metadata-crawler-parser/src/worker.rs +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright © Aptos Foundation - -use crate::{ - config::ParserConfig, - models::{ - nft_metadata_crawler_uris::NFTMetadataCrawlerURIs, - nft_metadata_crawler_uris_query::NFTMetadataCrawlerURIsQuery, - }, - utils::{ - counters::{ - DUPLICATE_ASSET_URI_COUNT, DUPLICATE_RAW_ANIMATION_URI_COUNT, - DUPLICATE_RAW_IMAGE_URI_COUNT, OPTIMIZE_IMAGE_TYPE_COUNT, PARSER_SUCCESSES_COUNT, - PARSE_URI_TYPE_COUNT, SKIP_URI_COUNT, - }, - database::upsert_uris, - gcs::{write_image_to_gcs, write_json_to_gcs}, - image_optimizer::ImageOptimizer, - json_parser::JSONParser, - uri_parser::URIParser, - }, -}; -use diesel::{ - r2d2::{ConnectionManager, PooledConnection}, - PgConnection, -}; -use google_cloud_storage::client::Client as GCSClient; -use image::ImageFormat; -use serde_json::Value; -use std::sync::Arc; -use tracing::{error, info, warn}; -use url::Url; - -/// Stuct that represents a parser for a single entry from queue -pub struct Worker { - config: Arc, - conn: PooledConnection>, - gcs_client: Arc, - pubsub_message: String, - model: NFTMetadataCrawlerURIs, - asset_data_id: String, - asset_uri: String, - last_transaction_version: i32, - last_transaction_timestamp: chrono::NaiveDateTime, - force: bool, -} - -impl Worker { - pub fn new( - config: Arc, - conn: PooledConnection>, - gcs_client: Arc, - pubsub_message: &str, - asset_data_id: &str, - asset_uri: &str, - last_transaction_version: i32, - last_transaction_timestamp: chrono::NaiveDateTime, - force: bool, - ) -> Self { - let model = NFTMetadataCrawlerURIs::new(asset_uri); - let worker = Self { - config, - conn, - gcs_client, - pubsub_message: pubsub_message.to_string(), - model, - asset_data_id: asset_data_id.to_string(), - asset_uri: asset_uri.to_string(), - last_transaction_version, - last_transaction_timestamp, - force, - }; - worker.log_info("Created worker"); - worker - } - - /// Main parsing flow - pub async fn parse(&mut self) -> anyhow::Result<()> { - // Deduplicate asset_uri - // Exit if not force or if asset_uri has already been parsed - let prev_model = - NFTMetadataCrawlerURIsQuery::get_by_asset_uri(&mut self.conn, &self.asset_uri); - if let Some(pm) = prev_model { - DUPLICATE_ASSET_URI_COUNT.inc(); - self.model = pm.into(); - if !self.force && self.model.get_do_not_parse() { - self.log_info("asset_uri has been marked as do_not_parse, skipping parse"); - SKIP_URI_COUNT.with_label_values(&["do_not_parse"]).inc(); - self.upsert(); - return Ok(()); - } - } - - // Check asset_uri against the URI blacklist - if self.is_blacklisted_uri(&self.asset_uri.clone()) { - self.log_info("Found match in URI blacklist, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - SKIP_URI_COUNT.with_label_values(&["blacklist"]).inc(); - return Ok(()); - } - - // Skip if asset_uri is not a valid URI, do not write invalid URI to Postgres - if Url::parse(&self.asset_uri).is_err() { - self.log_info("URI is invalid, skipping parse, marking as do_not_parse"); - self.model.set_do_not_parse(true); - SKIP_URI_COUNT.with_label_values(&["invalid"]).inc(); - return Ok(()); - } - - if self.force || self.model.get_cdn_json_uri().is_none() { - // Parse asset_uri - self.log_info("Parsing asset_uri"); - let json_uri = URIParser::parse( - &self.config.ipfs_prefix, - &self.model.get_asset_uri(), - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse asset_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - self.model.get_asset_uri() - }); - - // Parse JSON for raw_image_uri and raw_animation_uri - self.log_info("Starting JSON parsing"); - let (raw_image_uri, raw_animation_uri, json) = - JSONParser::parse(json_uri, self.config.max_file_size_bytes) - .await - .unwrap_or_else(|e| { - // Increment retry count if JSON parsing fails - self.log_warn("JSON parsing failed", Some(&e)); - self.model.increment_json_parser_retry_count(); - (None, None, Value::Null) - }); - - self.model.set_raw_image_uri(raw_image_uri); - self.model.set_raw_animation_uri(raw_animation_uri); - - // Save parsed JSON to GCS - if json != Value::Null { - self.log_info("Writing JSON to GCS"); - let cdn_json_uri_result = write_json_to_gcs( - &self.config.bucket, - &self.asset_uri, - &json, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_json_uri_result.as_ref() { - self.log_warn( - "Failed to write JSON to GCS, maybe upload timed out?", - Some(e), - ); - } - - let cdn_json_uri = cdn_json_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_json_uri(cdn_json_uri); - } - - // Commit model to Postgres - self.log_info("Committing JSON to Postgres"); - self.upsert(); - } - - // Should I optimize image? - // if force: true - // else if cdn_image_uri already exists: false - // else: perform lookup - // if found: set cdn_image_uri, false - // else: true - let should_optimize_image = if self.force { - true - } else if self.model.get_cdn_image_uri().is_some() { - false - } else { - self.model.get_raw_image_uri().map_or(true, |uri| { - match NFTMetadataCrawlerURIsQuery::get_by_raw_image_uri( - &mut self.conn, - &self.asset_uri, - &uri, - ) { - Some(uris) => { - self.log_info("Duplicate raw_image_uri found"); - DUPLICATE_RAW_IMAGE_URI_COUNT.inc(); - self.model.set_cdn_image_uri(uris.cdn_image_uri); - self.upsert(); - false - }, - None => true, - } - }) - }; - - if should_optimize_image { - // Parse raw_image_uri, use asset_uri if parsing fails - self.log_info("Parsing raw_image_uri"); - let raw_image_uri = self - .model - .get_raw_image_uri() - .unwrap_or(self.model.get_asset_uri()); - - // Check raw_image_uri against the URI blacklist - if self.is_blacklisted_uri(&raw_image_uri) { - self.log_info("Found match in URI blacklist, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - SKIP_URI_COUNT.with_label_values(&["blacklist"]).inc(); - return Ok(()); - } - - let img_uri = URIParser::parse( - &self.config.ipfs_prefix, - &raw_image_uri, - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse raw_image_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - raw_image_uri.clone() - }); - - // Resize and optimize image - self.log_info("Starting image optimization"); - OPTIMIZE_IMAGE_TYPE_COUNT - .with_label_values(&["image"]) - .inc(); - let (image, format) = ImageOptimizer::optimize( - &img_uri, - self.config.max_file_size_bytes, - self.config.image_quality, - self.config.max_image_dimensions, - ) - .await - .unwrap_or_else(|e| { - // Increment retry count if image is None - self.log_warn("Image optimization failed", Some(&e)); - self.model.increment_image_optimizer_retry_count(); - (vec![], ImageFormat::Png) - }); - - // Save resized and optimized image to GCS - if !image.is_empty() { - self.log_info("Writing image to GCS"); - let cdn_image_uri_result = write_image_to_gcs( - format, - &self.config.bucket, - &raw_image_uri, - image, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_image_uri_result.as_ref() { - self.log_warn( - "Failed to write image to GCS, maybe upload timed out?", - Some(e), - ); - } - - let cdn_image_uri = cdn_image_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_image_uri(cdn_image_uri); - self.model.reset_json_parser_retry_count(); - } - - // Commit model to Postgres - self.log_info("Committing image to Postgres"); - self.upsert(); - } - - // Should I optimize animation? - // if force: true - // else if cdn_animation_uri already exists: false - // else: perform lookup - // if found: set cdn_animation_uri, false - // else: true - let raw_animation_uri_option = if self.force { - self.model.get_raw_animation_uri() - } else if self.model.get_cdn_animation_uri().is_some() { - None - } else { - self.model.get_raw_animation_uri().and_then(|uri| { - match NFTMetadataCrawlerURIsQuery::get_by_raw_animation_uri( - &mut self.conn, - &self.asset_uri, - &uri, - ) { - Some(uris) => { - self.log_info("Duplicate raw_image_uri found"); - DUPLICATE_RAW_ANIMATION_URI_COUNT.inc(); - self.model.set_cdn_animation_uri(uris.cdn_animation_uri); - self.upsert(); - None - }, - None => Some(uri), - } - }) - }; - - // If raw_animation_uri_option is None, skip - if let Some(raw_animation_uri) = raw_animation_uri_option { - self.log_info("Parsing raw_animation_uri"); - let animation_uri = URIParser::parse( - &self.config.ipfs_prefix, - &raw_animation_uri, - self.config.ipfs_auth_key.as_deref(), - ) - .unwrap_or_else(|_| { - self.log_warn("Failed to parse raw_animation_uri", None); - PARSE_URI_TYPE_COUNT.with_label_values(&["other"]).inc(); - raw_animation_uri.clone() - }); - - // Resize and optimize animation - self.log_info("Starting animation optimization"); - OPTIMIZE_IMAGE_TYPE_COUNT - .with_label_values(&["animation"]) - .inc(); - let (animation, format) = ImageOptimizer::optimize( - &animation_uri, - self.config.max_file_size_bytes, - self.config.image_quality, - self.config.max_image_dimensions, - ) - .await - .unwrap_or_else(|e| { - // Increment retry count if animation is None - self.log_warn("Animation optimization failed", Some(&e)); - self.model.increment_animation_optimizer_retry_count(); - (vec![], ImageFormat::Png) - }); - - // Save resized and optimized animation to GCS - if !animation.is_empty() { - self.log_info("Writing animation to GCS"); - let cdn_animation_uri_result = write_image_to_gcs( - format, - &self.config.bucket, - &raw_animation_uri, - animation, - &self.gcs_client, - ) - .await; - - if let Err(e) = cdn_animation_uri_result.as_ref() { - self.log_error("Failed to write animation to GCS", e); - } - - let cdn_animation_uri = cdn_animation_uri_result - .map(|value| format!("{}{}", self.config.cdn_prefix, value)) - .ok(); - self.model.set_cdn_animation_uri(cdn_animation_uri); - } - - // Commit model to Postgres - self.log_info("Committing animation to Postgres"); - self.upsert(); - } - - if self.model.get_json_parser_retry_count() >= self.config.max_num_parse_retries - || self.model.get_image_optimizer_retry_count() >= self.config.max_num_parse_retries - || self.model.get_animation_optimizer_retry_count() >= self.config.max_num_parse_retries - { - self.log_info("Retry count exceeded, marking as do_not_parse"); - self.model.set_do_not_parse(true); - self.upsert(); - } - - PARSER_SUCCESSES_COUNT.inc(); - Ok(()) - } - - fn upsert(&mut self) { - upsert_uris( - &mut self.conn, - &self.model, - self.last_transaction_version as i64, - ) - .unwrap_or_else(|e| { - self.log_error("Commit to Postgres failed", &e); - panic!(); - }); - } - - fn is_blacklisted_uri(&mut self, uri: &str) -> bool { - self.config - .uri_blacklist - .iter() - .any(|blacklist_uri| uri.contains(blacklist_uri)) - } - - fn log_info(&self, message: &str) { - info!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - "[NFT Metadata Crawler] {}", - message - ); - } - - fn log_warn(&self, message: &str, e: Option<&anyhow::Error>) { - warn!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - error = ?e, - "[NFT Metadata Crawler] {}", - message - ); - } - - fn log_error(&self, message: &str, e: &anyhow::Error) { - error!( - pubsub_message = self.pubsub_message, - asset_data_id = self.asset_data_id, - asset_uri = self.asset_uri, - last_transaction_version = self.last_transaction_version, - last_transaction_timestamp = self.last_transaction_timestamp.to_string(), - error = ?e, - "[NFT Metadata Crawler] {}", - message - ); - } -} diff --git a/testsuite/generate-format/Cargo.toml b/testsuite/generate-format/Cargo.toml deleted file mode 100644 index a0a66edd90aed..0000000000000 --- a/testsuite/generate-format/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "generate-format" -description = "Aptos core type checker to ensure compatibility" -version = "0.1.0" - -# Workspace inherited keys -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -publish = { workspace = true } -repository = { workspace = true } -rust-version = { workspace = true } - -[dependencies] -aptos-api-types = { workspace = true } -aptos-config = { workspace = true } -aptos-consensus = { workspace = true, features = ["fuzzing"] } -aptos-consensus-types = { workspace = true } -aptos-crypto = { workspace = true } -aptos-crypto-derive = { workspace = true } -aptos-network = { workspace = true } -aptos-types = { workspace = true } -bcs = { workspace = true } -clap = { workspace = true } -move-core-types = { workspace = true, features = ["fuzzing"] } -rand = { workspace = true } -serde = { workspace = true } -serde-reflection = { workspace = true } -serde_yaml = { workspace = true } - -[[bin]] -name = "compute" -path = "src/compute.rs" -test = false diff --git a/testsuite/generate-format/README.md b/testsuite/generate-format/README.md deleted file mode 100644 index 8932730f8f883..0000000000000 --- a/testsuite/generate-format/README.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -id: generate-format -title: Generate Format -custom_edit_url: https://github.com/aptos-labs/aptos-core/edit/main/testsuite/generate-format/README.md ---- - -`generate-format` hosts the Aptos core type checker to ensure compatibility and uses -[`serde-reflection`](https://github.com/aptos-labs/serde-reflection) to properly track type changes over time. - -## How to make a change - -When you introduce a new struct, enum, variant, or other type, ensure you make changes to the following files: -- [x] api.rs -- [x] aptos.rs -- [x] consensus.rs - -as well as -- [x] api.yaml -- [x] aptos.yaml -- [x] consensus.yaml - -## Example -As an example, we will walk through a real-life example to demonstrate how to make the appropriate changes. -Feel free to follow along here: https://github.com/aptos-labs/aptos-core/pull/10755/files - -Suppose you're adding a new `secp256r1_ecdsa` crypto library with new structs for the following keys and signatures: -- `PublicKey` -- `Signature` -- `PrivateKey` - -In addition, you are creating a set of structs to support WebAuthn transactions, -such as `PartialAuthenticatorAssertionResponse` and `AssertionSignature`. - -Below we'll walk through the necessary changes to ensure these types are tracked appropriately - -### Crypto library changes - -The following changes should be made to support `secp256r1_ecdsa` keys and signatures properly - -In the following files -- [x] api.rs -- [x] aptos.rs -- [x] consensus.rs - -add `tracer.trace_value` for secp256r1_ecdsa - -```rust -fn trace_crypto_values(tracer: &mut Tracer, samples: &mut Samples) -> Result<()> { - ... - // Add tracing for secp256r1_ecdsa keys and sigs - tracer.trace_value(samples, &secp256r1_ecdsa_private_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_public_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_signature)?; - - Ok(()) -} -``` - -> Note: You may also want to add the `key_name` macro if you are using a different name than your struct name - -```rust -#[key_name("Secp256r1EcdsaPrivateKey")] -pub struct PublicKey {...} -``` - -Additionally, in the following files -- [x] api.yaml -- [x] aptos.yaml -- [x] consensus.yaml - -add the following yaml - -```yaml -... -Secp256r1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaSignature: - NEWTYPESTRUCT: BYTES -... -``` - -### Struct and enum changes - -`Secp256r1Ecdsa` was also added as an enum variant on `AnyPublicKey`, so the appropriate updates must be made to the `AnyPublicKey` enum: - -```yaml -AnyPublicKey: - ENUM: - ... - 2: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey -``` - -Additionally, a new enum variant - `WebAuthn` - was added to `AnySignature` to support WebAuthn signatures. -The `WebAuthn` variant takes in a `PartialAuthenticatorAssertionResponse` struct. - -```yaml -AnySignature: - ENUM: - ... - 2: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse -PartialAuthenticatorAssertionResponse: - STRUCT: - - signature: - TYPENAME: AssertionSignature - - authenticator_data: BYTES - - client_data_json: BYTES -AssertionSignature: - ENUM: - 0: - Secp256r1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256r1EcdsaSignature -``` - -Ensure that the changes above are synchronized across all of these files: -- [x] api.yaml -- [x] aptos.yaml -- [x] consensus.yaml - -> Note: Because `[api|aptos|consensus].rs`, are already tracking the `AnyPublicKey` and `AnySignature` struct, no further tracers are necessary here - -Additionally, ensure that `enums` are tracked correctly across `[api|aptos|consensus].rs` -```rust -fn get_registry(){ - ... - tracer.trace_type::(&samples)?; - ... -} -``` - -Lastly ensure that the struct has the appropriate serde macros if needed. In this case, we want to serialize -`PartialAuthenticatorAssertionResponse`'s `authenticator_data` and `client_data_json`'s `Vec` to `BYTES` -so we will need to add the `serde_bytes` macro, like so - -```rust -/// `PartialAuthenticatorAssertionResponse` includes a subset of the fields returned from -/// an [`AuthenticatorAssertionResponse`](passkey_types::webauthn::AuthenticatorAssertionResponse) -/// -/// See -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] - -pub struct PartialAuthenticatorAssertionResponse { - /// This attribute contains the raw signature returned from the authenticator. - /// NOTE: Many signatures returned from WebAuthn assertions are not raw signatures. - /// As an example, secp256r1_ecdsa signatures are encoded as an [ASN.1 DER Ecdsa-Sig_value](https://www.w3.org/TR/webauthn-3/#sctn-signature-attestation-types) - /// If the signature is encoded, the client is expected to convert the encoded signature - /// into a raw signature before including it in the transaction - signature: AssertionSignature, - /// This attribute contains the authenticator data returned by the authenticator. - /// See [`AuthenticatorData`](passkey_types::ctap2::AuthenticatorData). - #[serde(with = "serde_bytes")] - authenticator_data: Vec, - /// This attribute contains the JSON byte serialization of [`CollectedClientData`](CollectedClientData) passed to the - /// authenticator by the client in order to generate this credential. The exact JSON serialization - /// MUST be preserved, as the hash of the serialized client data has been computed over it. - #[serde(with = "serde_bytes")] - client_data_json: Vec, -} -``` diff --git a/testsuite/generate-format/src/api.rs b/testsuite/generate-format/src/api.rs deleted file mode 100644 index 6d22991341573..0000000000000 --- a/testsuite/generate-format/src/api.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::{ - bls12381, - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - hash::{CryptoHasher as _, TestOnlyHasher}, - multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}, - secp256k1_ecdsa, secp256r1_ecdsa, - traits::{SigningKey, Uniform}, -}; -use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; -use aptos_types::{ - access_path::{AccessPath, Path}, - account_config::{CoinStoreResource, DepositEvent, WithdrawEvent}, - block_metadata_ext::BlockMetadataExt, - contract_event, event, - state_store::{ - state_key::StateKey, - state_value::{PersistedStateValueMetadata, StateValueMetadata}, - }, - transaction, - transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator}, - validator_txn::ValidatorTransaction, - vm_status::AbortLocation, - write_set, -}; -use move_core_types::language_storage; -use rand::{rngs::StdRng, SeedableRng}; -use serde::{Deserialize, Serialize}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; - -/// Default output file. -pub fn output_file() -> Option<&'static str> { - Some("tests/staged/api.yaml") -} - -/// This aims at signing canonically serializable BCS data -#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] -struct TestAptosCrypto(String); - -/// Record sample values for crypto types used by transactions. -fn trace_crypto_values(tracer: &mut Tracer, samples: &mut Samples) -> Result<()> { - let mut hasher = TestOnlyHasher::default(); - hasher.update(b"Test message"); - let hashed_message = hasher.finish(); - - let message = TestAptosCrypto("Hello, World".to_string()); - - let mut rng: StdRng = SeedableRng::from_seed([0; 32]); - let private_key = Ed25519PrivateKey::generate(&mut rng); - let public_key: Ed25519PublicKey = (&private_key).into(); - let signature = private_key.sign(&message).unwrap(); - - tracer.trace_value(samples, &hashed_message)?; - tracer.trace_value(samples, &public_key)?; - tracer.trace_value::(samples, &public_key.clone().into())?; - tracer.trace_value(samples, &signature)?; - tracer.trace_value::(samples, &signature.clone().into())?; - - let secp256k1_private_key = secp256k1_ecdsa::PrivateKey::generate(&mut rng); - let secp256k1_public_key = aptos_crypto::PrivateKey::public_key(&secp256k1_private_key); - let secp256k1_signature = secp256k1_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256k1_private_key)?; - tracer.trace_value(samples, &secp256k1_public_key)?; - tracer.trace_value(samples, &secp256k1_signature)?; - - let secp256r1_ecdsa_private_key = secp256r1_ecdsa::PrivateKey::generate(&mut rng); - let secp256r1_ecdsa_public_key = - aptos_crypto::PrivateKey::public_key(&secp256r1_ecdsa_private_key); - let secp256r1_ecdsa_signature = secp256r1_ecdsa_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256r1_ecdsa_private_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_public_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_signature)?; - - let bls12381_private_key = bls12381::PrivateKey::generate(&mut rng); - let bls12381_public_key = bls12381::PublicKey::from(&bls12381_private_key); - let bls12381_signature = bls12381_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &bls12381_private_key)?; - tracer.trace_value(samples, &bls12381_public_key)?; - tracer.trace_value(samples, &bls12381_signature)?; - - crate::trace_keyless_structs(tracer, samples, public_key, signature)?; - - Ok(()) -} - -pub fn get_registry() -> Result { - let mut tracer = - Tracer::new(TracerConfig::default().is_human_readable(bcs::is_human_readable())); - let mut samples = Samples::new(); - // 1. Record samples for types with custom deserializers. - trace_crypto_values(&mut tracer, &mut samples)?; - tracer.trace_value(&mut samples, &event::EventKey::random())?; - tracer.trace_value(&mut samples, &write_set::WriteOp::Deletion { - metadata: StateValueMetadata::none(), - })?; - - // 2. Trace the main entry point(s) + every enum separately. - // stdlib types - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // events - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // writeset - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // api types - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // output types - tracer.trace_type::(&samples)?; - - // aliases within StructTag - tracer.ignore_aliases("StructTag", &["type_params"])?; - - tracer.registry() -} diff --git a/testsuite/generate-format/src/aptos.rs b/testsuite/generate-format/src/aptos.rs deleted file mode 100644 index 3d0d4800cca2a..0000000000000 --- a/testsuite/generate-format/src/aptos.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::{ - bls12381, - ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, - hash::{CryptoHasher as _, TestOnlyHasher}, - multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}, - secp256k1_ecdsa, secp256r1_ecdsa, - traits::{SigningKey, Uniform}, -}; -use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; -use aptos_types::{ - block_metadata_ext::BlockMetadataExt, - contract_event, event, - state_store::{ - state_key::StateKey, - state_value::{PersistedStateValueMetadata, StateValueMetadata}, - }, - transaction, - validator_txn::ValidatorTransaction, - write_set, -}; -use move_core_types::language_storage; -use rand::{rngs::StdRng, SeedableRng}; -use serde::{Deserialize, Serialize}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; - -/// Default output file. -pub fn output_file() -> Option<&'static str> { - Some("tests/staged/aptos.yaml") -} - -/// This aims at signing canonically serializable BCS data -#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] -struct TestAptosCrypto(String); - -/// Record sample values for crypto types used by transactions. -fn trace_crypto_values(tracer: &mut Tracer, samples: &mut Samples) -> Result<()> { - let mut hasher = TestOnlyHasher::default(); - hasher.update(b"Test message"); - let hashed_message = hasher.finish(); - - let message = TestAptosCrypto("Hello, World".to_string()); - - let mut rng: StdRng = SeedableRng::from_seed([0; 32]); - let private_key = Ed25519PrivateKey::generate(&mut rng); - let public_key: Ed25519PublicKey = (&private_key).into(); - let signature = private_key.sign(&message).unwrap(); - - tracer.trace_value(samples, &hashed_message)?; - tracer.trace_value(samples, &public_key)?; - tracer.trace_value::(samples, &public_key.clone().into())?; - tracer.trace_value(samples, &signature)?; - tracer.trace_value::(samples, &signature.clone().into())?; - - let secp256k1_private_key = secp256k1_ecdsa::PrivateKey::generate(&mut rng); - let secp256k1_public_key = aptos_crypto::PrivateKey::public_key(&secp256k1_private_key); - let secp256k1_signature = secp256k1_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256k1_private_key)?; - tracer.trace_value(samples, &secp256k1_public_key)?; - tracer.trace_value(samples, &secp256k1_signature)?; - - let secp256r1_ecdsa_private_key = secp256r1_ecdsa::PrivateKey::generate(&mut rng); - let secp256r1_ecdsa_public_key = - aptos_crypto::PrivateKey::public_key(&secp256r1_ecdsa_private_key); - let secp256r1_ecdsa_signature = secp256r1_ecdsa_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256r1_ecdsa_private_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_public_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_signature)?; - - let bls12381_private_key = bls12381::PrivateKey::generate(&mut rng); - let bls12381_public_key = bls12381::PublicKey::from(&bls12381_private_key); - let bls12381_signature = bls12381_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &bls12381_private_key)?; - tracer.trace_value(samples, &bls12381_public_key)?; - tracer.trace_value(samples, &bls12381_signature)?; - - crate::trace_keyless_structs(tracer, samples, public_key, signature)?; - - Ok(()) -} - -pub fn get_registry() -> Result { - let mut tracer = - Tracer::new(TracerConfig::default().is_human_readable(bcs::is_human_readable())); - let mut samples = Samples::new(); - // 1. Record samples for types with custom deserializers. - trace_crypto_values(&mut tracer, &mut samples)?; - tracer.trace_value(&mut samples, &event::EventKey::random())?; - tracer.trace_value(&mut samples, &write_set::WriteOp::Deletion { - metadata: StateValueMetadata::none(), - })?; - - // 2. Trace the main entry point(s) + every enum separately. - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // aliases within StructTag - tracer.ignore_aliases("StructTag", &["type_params"])?; - - tracer.registry() -} diff --git a/testsuite/generate-format/src/compute.rs b/testsuite/generate-format/src/compute.rs deleted file mode 100644 index a6458c0a7dfab..0000000000000 --- a/testsuite/generate-format/src/compute.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use clap::Parser; -use generate_format::Corpus; -use std::{fs::File, io::Write}; - -#[derive(Debug, Parser)] -#[clap( - name = "Aptos format generator", - about = "Trace serde (de)serialization to generate format descriptions for Aptos types" -)] -struct Options { - #[clap(long, value_enum, default_value_t = Corpus::Aptos, ignore_case = true)] - corpus: Corpus, - - #[clap(long)] - record: bool, -} - -fn main() { - let options = Options::parse(); - - let registry = options.corpus.get_registry(); - let output_file = options.corpus.output_file(); - - let content = serde_yaml::to_string(®istry).unwrap(); - if options.record { - match output_file { - Some(path) => { - let mut f = File::create("testsuite/generate-format/".to_string() + path).unwrap(); - write!(f, "{}", content).unwrap(); - }, - None => panic!("Corpus {:?} doesn't record formats on disk", options.corpus), - } - } else { - println!("{}", content); - } -} - -#[test] -fn verify_tool() { - use clap::CommandFactory; - Options::command().debug_assert() -} diff --git a/testsuite/generate-format/src/consensus.rs b/testsuite/generate-format/src/consensus.rs deleted file mode 100644 index 0a36c8fc9f099..0000000000000 --- a/testsuite/generate-format/src/consensus.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::{ - bls12381, - ed25519::Ed25519PrivateKey, - multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}, - secp256k1_ecdsa, secp256r1_ecdsa, - traits::{SigningKey, Uniform}, - PrivateKey, -}; -use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; -use aptos_types::{ - block_metadata_ext::BlockMetadataExt, - contract_event, event, - state_store::{ - state_key::StateKey, - state_value::{PersistedStateValueMetadata, StateValueMetadata}, - }, - transaction, - validator_txn::ValidatorTransaction, - write_set, -}; -use move_core_types::language_storage; -use rand::{rngs::StdRng, SeedableRng}; -use serde::{Deserialize, Serialize}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; - -/// Return a relative path to start tracking changes in commits. -pub fn output_file() -> Option<&'static str> { - Some("tests/staged/consensus.yaml") -} - -/// This aims at signing canonically serializable BCS data -#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] -struct TestAptosCrypto(String); - -/// Record sample values for crypto types used by consensus. -fn trace_crypto_values(tracer: &mut Tracer, samples: &mut Samples) -> Result<()> { - let message = TestAptosCrypto("Hello, World".to_string()); - - let mut rng: StdRng = SeedableRng::from_seed([0; 32]); - - let private_key = Ed25519PrivateKey::generate(&mut rng); - let public_key = private_key.public_key(); - let signature = private_key.sign(&message).unwrap(); - - let bls_private_key = bls12381::PrivateKey::generate(&mut rng); - let bls_public_key = bls_private_key.public_key(); - let bls_signature = bls_private_key.sign(&message).unwrap(); - - tracer.trace_value(samples, &public_key)?; - tracer.trace_value(samples, &signature)?; - tracer.trace_value(samples, &bls_public_key)?; - tracer.trace_value(samples, &bls_signature)?; - tracer.trace_value::(samples, &public_key.clone().into())?; - tracer.trace_value::(samples, &signature.clone().into())?; - - let secp256k1_private_key = secp256k1_ecdsa::PrivateKey::generate(&mut rng); - let secp256k1_public_key = aptos_crypto::PrivateKey::public_key(&secp256k1_private_key); - let secp256k1_signature = secp256k1_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256k1_private_key)?; - tracer.trace_value(samples, &secp256k1_public_key)?; - tracer.trace_value(samples, &secp256k1_signature)?; - - let secp256r1_ecdsa_private_key = secp256r1_ecdsa::PrivateKey::generate(&mut rng); - let secp256r1_ecdsa_public_key = PrivateKey::public_key(&secp256r1_ecdsa_private_key); - let secp256r1_ecdsa_signature = secp256r1_ecdsa_private_key.sign(&message).unwrap(); - tracer.trace_value(samples, &secp256r1_ecdsa_private_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_public_key)?; - tracer.trace_value(samples, &secp256r1_ecdsa_signature)?; - - crate::trace_keyless_structs(tracer, samples, public_key, signature)?; - - Ok(()) -} - -/// Create a registry for consensus types. -pub fn get_registry() -> Result { - let mut tracer = - Tracer::new(TracerConfig::default().is_human_readable(bcs::is_human_readable())); - let mut samples = Samples::new(); - // 1. Record samples for types with custom deserializers. - trace_crypto_values(&mut tracer, &mut samples)?; - tracer.trace_value( - &mut samples, - &aptos_consensus_types::block::Block::make_genesis_block(), - )?; - tracer.trace_value(&mut samples, &event::EventKey::random())?; - tracer.trace_value(&mut samples, &write_set::WriteOp::Deletion { - metadata: StateValueMetadata::none(), - })?; - - // 2. Trace the main entry point(s) + every enum separately. - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // aliases within StructTag - tracer.ignore_aliases("StructTag", &["type_params"])?; - - tracer.registry() -} diff --git a/testsuite/generate-format/src/lib.rs b/testsuite/generate-format/src/lib.rs deleted file mode 100644 index a2e2add7c3faa..0000000000000 --- a/testsuite/generate-format/src/lib.rs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -//! How and where to record the Serde format of interesting Aptos types. -//! See API documentation with `cargo doc -p serde-reflection --open` - -use aptos_crypto::ed25519::{Ed25519PublicKey, Ed25519Signature}; -use aptos_types::{ - keyless, - keyless::{EphemeralCertificate, Groth16Proof, IdCommitment, Pepper, ZeroKnowledgeSig}, - transaction::authenticator::{EphemeralPublicKey, EphemeralSignature}, -}; -use clap::{Parser, ValueEnum}; -use serde_reflection::{Registry, Samples, Tracer}; -use std::fmt::{Display, Formatter}; - -/// Rest API types -mod api; -/// Aptos transactions. -mod aptos; -/// Consensus messages. -mod consensus; -/// Analyze Serde formats to detect certain patterns. -mod linter; -/// Move ABI. -mod move_abi; -/// Network messages. -mod network; - -pub use linter::lint_bcs_format; - -#[derive(Debug, Parser, Clone, Copy, ValueEnum)] -/// A corpus of Rust types to trace, and optionally record on disk. -pub enum Corpus { - API, - Aptos, - Consensus, - Network, - MoveABI, -} - -impl Corpus { - /// Compute the registry of formats. - pub fn get_registry(self) -> Registry { - let result = match self { - Corpus::API => api::get_registry(), - Corpus::Aptos => aptos::get_registry(), - Corpus::Consensus => consensus::get_registry(), - Corpus::Network => network::get_registry(), - Corpus::MoveABI => move_abi::get_registry(), - }; - match result { - Ok(registry) => registry, - Err(error) => { - panic!("{}:{}", error, error.explanation()); - }, - } - } - - /// Where to record this corpus on disk. - pub fn output_file(self) -> Option<&'static str> { - match self { - Corpus::API => api::output_file(), - Corpus::Aptos => aptos::output_file(), - Corpus::Consensus => consensus::output_file(), - Corpus::Network => network::output_file(), - Corpus::MoveABI => move_abi::output_file(), - } - } -} - -impl Display for Corpus { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Corpus::API => "API", - Corpus::Aptos => "Aptos", - Corpus::Consensus => "Consensus", - Corpus::Network => "Network", - Corpus::MoveABI => "MoveABI", - }) - } -} - -pub(crate) fn trace_keyless_structs( - tracer: &mut Tracer, - samples: &mut Samples, - public_key: Ed25519PublicKey, - signature: Ed25519Signature, -) -> serde_reflection::Result<()> { - let keyless_public_key = keyless::KeylessPublicKey { - iss_val: "".to_string(), - idc: IdCommitment::new_from_preimage(&Pepper::from_number(2), "", "", "").unwrap(), - }; - let keyless_signature = keyless::KeylessSignature { - cert: EphemeralCertificate::ZeroKnowledgeSig(ZeroKnowledgeSig { - proof: Groth16Proof::dummy_proof().into(), - exp_horizon_secs: 0, - extra_field: None, - override_aud_val: None, - training_wheels_signature: None, - }), - jwt_header_json: "".to_string(), - exp_date_secs: 0, - ephemeral_pubkey: EphemeralPublicKey::Ed25519 { public_key }, - ephemeral_signature: EphemeralSignature::Ed25519 { signature }, - }; - tracer.trace_value(samples, &keyless_public_key)?; - tracer.trace_value(samples, &keyless_signature)?; - - Ok(()) -} diff --git a/testsuite/generate-format/src/linter.rs b/testsuite/generate-format/src/linter.rs deleted file mode 100644 index cf5fa626e2e83..0000000000000 --- a/testsuite/generate-format/src/linter.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use serde_reflection::{ContainerFormat, Error, Format, FormatHolder, Result}; - -/// Verify that a Serde format is compatible with BCS and follows best practices. -pub fn lint_bcs_format(format: &ContainerFormat) -> Result<()> { - if is_empty_container(format) { - return Err(Error::Custom("Please avoid 0-sized containers".into())); - } - format.visit(&mut |f| { - use Format::*; - match f { - F32 | F64 | Char => Err(Error::Custom(format!("BCS does not support type {:?}", f))), - Seq(inner) => match inner.as_ref() { - U8 => Err(Error::Custom( - "Please use `#[serde(with = \"serde_bytes\")` on `Vec` objects.".into(), - )), - e if is_empty(e) => Err(Error::Custom(format!( - "Sequences with empty content are not allowed: {:?}", - f - ))), - _ => Ok(()), - }, - Map { key, value } => { - if is_empty(key) && is_empty(value) { - Err(Error::Custom(format!( - "Maps with empty keys and values are not allowed: {:?}", - f - ))) - } else { - Ok(()) - } - }, - _ => Ok(()), - } - }) -} - -fn is_empty(format: &Format) -> bool { - use Format::*; - match format { - Unit => true, - TupleArray { content, size } => *size == 0 || is_empty(content.as_ref()), - Tuple(formats) => formats.iter().all(is_empty), - _ => false, - } -} - -fn is_empty_container(format: &ContainerFormat) -> bool { - use ContainerFormat::*; - match format { - UnitStruct => true, - NewTypeStruct(inner) => is_empty(inner.as_ref()), - TupleStruct(formats) => formats.iter().all(is_empty), - Struct(formats) => formats.iter().map(|named| &named.value).all(is_empty), - Enum(_) => false, - } -} diff --git a/testsuite/generate-format/src/move_abi.rs b/testsuite/generate-format/src/move_abi.rs deleted file mode 100644 index f07d8b27fb45e..0000000000000 --- a/testsuite/generate-format/src/move_abi.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_types::transaction; -use move_core_types::language_storage; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; - -/// Default output file. -pub fn output_file() -> Option<&'static str> { - Some("tests/staged/move_abi.yaml") -} - -pub fn get_registry() -> Result { - let mut tracer = - Tracer::new(TracerConfig::default().is_human_readable(bcs::is_human_readable())); - let samples = Samples::new(); - // 1. Record samples for types with custom deserializers. - - // 2. Trace the main entry point(s) + every enum separately. - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - // aliases within StructTag - tracer.ignore_aliases("StructTag", &["type_params"])?; - - tracer.registry() -} diff --git a/testsuite/generate-format/src/network.rs b/testsuite/generate-format/src/network.rs deleted file mode 100644 index da5474299f3ec..0000000000000 --- a/testsuite/generate-format/src/network.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use aptos_crypto::{ - traits::Uniform, - x25519::{PrivateKey, PublicKey}, -}; -use aptos_network::protocols::wire::{handshake, messaging}; -use aptos_types::network_address as address; -use rand::{rngs::StdRng, SeedableRng}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; -use std::str::FromStr; - -/// Return a relative path to start tracking changes in commits. -pub fn output_file() -> Option<&'static str> { - Some("tests/staged/network.yaml") -} - -/// Record sample values for crypto types used by network. -fn trace_crypto_values(tracer: &mut Tracer, samples: &mut Samples) -> Result<()> { - let mut rng: StdRng = SeedableRng::from_seed([0; 32]); - let private_key = PrivateKey::generate(&mut rng); - let public_key: PublicKey = (&private_key).into(); - - tracer.trace_value(samples, &public_key)?; - Ok(()) -} - -/// Create a registry of network data formats. -pub fn get_registry() -> Result { - let mut tracer = - Tracer::new(TracerConfig::default().is_human_readable(bcs::is_human_readable())); - let mut samples = Samples::new(); - // 1. Record samples for types with custom deserializers. - trace_crypto_values(&mut tracer, &mut samples)?; - tracer.trace_value( - &mut samples, - &address::DnsName::from_str("example.com").unwrap(), - )?; - tracer.trace_value(&mut samples, &address::NetworkAddress::mock())?; - - // 2. Trace the main entry point(s) + every enum separately. - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; - - tracer.registry() -} diff --git a/testsuite/generate-format/tests/detect_format_change.rs b/testsuite/generate-format/tests/detect_format_change.rs deleted file mode 100644 index 88d2c49f6320a..0000000000000 --- a/testsuite/generate-format/tests/detect_format_change.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use clap::ValueEnum; -use generate_format::Corpus; -use serde_reflection::Registry; -use std::collections::{btree_map::Entry, BTreeMap}; - -#[test] -fn analyze_serde_formats() { - let mut all_corpuses = BTreeMap::new(); - - for corpus in Corpus::value_variants() { - // Compute the Serde formats of this corpus by analyzing the codebase. - let registry = corpus.get_registry(); - - // If the corpus was recorded on disk, test that the formats have not changed since then. - if let Some(path) = corpus.output_file() { - let content = std::fs::read_to_string(path).unwrap(); - let expected = serde_yaml::from_str::(content.as_str()).unwrap(); - assert_registry_has_not_changed( - &(*corpus).to_string(), - path, - registry.clone(), - expected, - ); - } - - // Test that the definitions in all corpus are unique and pass the linter. - for (key, value) in registry { - assert_eq!( - generate_format::lint_bcs_format(&value), - Ok(()), - "In corpus {}: lint error while analyzing {}", - corpus, - key - ); - - match all_corpuses.entry(key.clone()) { - Entry::Vacant(e) => { - e.insert(value); - } - Entry::Occupied(e) => assert_eq!( - e.get(), - &value, - "Type {} in corpus {} differs with previous definition in another corpus: {:?} vs {:?}", - key, - corpus, - e.get(), - &value, - ), - } - } - } -} - -fn message(name: &str) -> String { - format!( - r#" -You may run `cargo run -p generate-format -- --corpus {} --record` to refresh the records. -Please verify the changes to the recorded file(s) and consider tagging your pull-request as `breaking`."#, - name - ) -} - -fn assert_registry_has_not_changed(name: &str, path: &str, registry: Registry, expected: Registry) { - for (key, value) in expected.iter() { - assert_eq!( - Some(value), - registry.get(key), - r#" ----- -The recorded format for type `{}` was removed or does not match the recorded value in {}.{} ----- -"#, - key, - path, - message(name), - ); - } - - for key in registry.keys() { - assert!( - expected.contains_key(key), - r#" ----- -Type `{}` was added and has no recorded format in {} yet.{} ----- -"#, - key, - path, - message(name), - ); - } -} - -#[test] -fn test_we_can_detect_changes_in_yaml() { - let yaml1 = r#"--- -Person: - ENUM: - 0: - NickName: - NEWTYPE: - STR -"#; - - let yaml2 = r#"--- -Person: - ENUM: - 0: - NickName: - NEWTYPE: - STR - 1: - FullName: UNIT -"#; - - let value1 = serde_yaml::from_str::(yaml1).unwrap(); - let value2 = serde_yaml::from_str::(yaml2).unwrap(); - assert_ne!(value1, value2); - assert_ne!(value1.get("Person").unwrap(), value2.get("Person").unwrap()); -} diff --git a/testsuite/generate-format/tests/linter.rs b/testsuite/generate-format/tests/linter.rs deleted file mode 100644 index 586b9a6c10805..0000000000000 --- a/testsuite/generate-format/tests/linter.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © Aptos Foundation -// Parts of the project are originally copyright © Meta Platforms, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use generate_format::lint_bcs_format; -use serde_reflection::{ContainerFormat, Format, Result}; - -fn test_newtypestruct_with_format(f: Format) -> Result<()> { - lint_bcs_format(&ContainerFormat::NewTypeStruct(Box::new(f))) -} - -#[test] -fn test_lint_bcs_format() { - use Format::*; - - assert!(lint_bcs_format(&ContainerFormat::UnitStruct).is_err()); - - assert!(lint_bcs_format(&ContainerFormat::TupleStruct(vec![])).is_err()); - - assert!(lint_bcs_format(&ContainerFormat::TupleStruct(vec![Unit, U32])).is_ok()); - - assert!(test_newtypestruct_with_format(Unit).is_err()); - - assert!(test_newtypestruct_with_format(Seq(Box::new(Unit))).is_err()); - - assert!(test_newtypestruct_with_format(Map { - key: Box::new(Unit), - value: Box::new(Unit) - }) - .is_err()); - - assert!(test_newtypestruct_with_format(Seq(Box::new(Tuple(Vec::new())))).is_err()); - - assert!(test_newtypestruct_with_format(Seq(Box::new(Tuple(vec![Unit])))).is_err()); - - assert!(test_newtypestruct_with_format(Seq(Box::new(Tuple(vec![Unit, U32])))).is_ok()); -} diff --git a/testsuite/generate-format/tests/staged/api.yaml b/testsuite/generate-format/tests/staged/api.yaml deleted file mode 100644 index 350cdd4314c38..0000000000000 --- a/testsuite/generate-format/tests/staged/api.yaml +++ /dev/null @@ -1,821 +0,0 @@ ---- -AbortInfo: - STRUCT: - - reason_name: STR - - description: STR -AbortLocation: - ENUM: - 0: - Module: - NEWTYPE: - TYPENAME: ModuleId - 1: - Script: UNIT -AccessPath: - STRUCT: - - address: - TYPENAME: AccountAddress - - path: BYTES -AccountAddress: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -AccountAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - SingleKey: - STRUCT: - - authenticator: - TYPENAME: SingleKeyAuthenticator - 3: - MultiKey: - STRUCT: - - authenticator: - TYPENAME: MultiKeyAuthenticator -AggregateSignature: - STRUCT: - - validator_bitmask: - TYPENAME: BitVec - - sig: - OPTION: - TYPENAME: Signature -Any: - STRUCT: - - type_name: STR - - data: BYTES -AnyPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256k1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256k1EcdsaPublicKey - 2: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey - 3: - Keyless: - STRUCT: - - public_key: - TYPENAME: KeylessPublicKey -AnySignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - Secp256k1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256k1EcdsaSignature - 2: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse - 3: - Keyless: - STRUCT: - - signature: - TYPENAME: KeylessSignature -AssertionSignature: - ENUM: - 0: - Secp256r1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256r1EcdsaSignature -BitVec: - STRUCT: - - inner: BYTES -BlockMetadata: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 -BlockMetadataExt: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: BlockMetadata - 1: - V1: - NEWTYPE: - TYPENAME: BlockMetadataWithRandomness -BlockMetadataWithRandomness: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 - - randomness: - OPTION: - TYPENAME: Randomness -ChainId: - NEWTYPESTRUCT: U8 -ChangeSet: - STRUCT: - - write_set: - TYPENAME: WriteSet - - events: - SEQ: - TYPENAME: ContractEvent -CoinStoreResource: - STRUCT: - - coin: U64 - - frozen: BOOL - - deposit_events: - TYPENAME: EventHandle - - withdraw_events: - TYPENAME: EventHandle -ContractEvent: - ENUM: - 0: - V1: - NEWTYPE: - TYPENAME: ContractEventV1 - 1: - V2: - NEWTYPE: - TYPENAME: ContractEventV2 -ContractEventV1: - STRUCT: - - key: - TYPENAME: EventKey - - sequence_number: U64 - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -ContractEventV2: - STRUCT: - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -DKGTranscript: - STRUCT: - - metadata: - TYPENAME: DKGTranscriptMetadata - - transcript_bytes: BYTES -DKGTranscriptMetadata: - STRUCT: - - epoch: U64 - - author: - TYPENAME: AccountAddress -DepositEvent: - STRUCT: - - amount: U64 -DeprecatedPayload: - STRUCT: - - dummy_value: U64 -Ed25519PublicKey: - NEWTYPESTRUCT: BYTES -Ed25519Signature: - NEWTYPESTRUCT: BYTES -EntryFunction: - STRUCT: - - module: - TYPENAME: ModuleId - - function: - TYPENAME: Identifier - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: BYTES -EphemeralCertificate: - ENUM: - 0: - ZeroKnowledgeSig: - NEWTYPE: - TYPENAME: ZeroKnowledgeSig - 1: - OpenIdSig: - NEWTYPE: - TYPENAME: OpenIdSig -EphemeralPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey -EphemeralSignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse -EventHandle: - STRUCT: - - count: U64 - - key: - TYPENAME: EventKey -EventKey: - STRUCT: - - creation_number: U64 - - account_address: - TYPENAME: AccountAddress -ExecutionStatus: - ENUM: - 0: - Success: UNIT - 1: - OutOfGas: UNIT - 2: - MoveAbort: - STRUCT: - - location: - TYPENAME: AbortLocation - - code: U64 - - info: - OPTION: - TYPENAME: AbortInfo - 3: - ExecutionFailure: - STRUCT: - - location: - TYPENAME: AbortLocation - - function: U16 - - code_offset: U16 - 4: - MiscellaneousError: - NEWTYPE: - OPTION: U64 -G1Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -G2Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 64 -Groth16Proof: - STRUCT: - - a: - TYPENAME: G1Bytes - - b: - TYPENAME: G2Bytes - - c: - TYPENAME: G1Bytes -HashValue: - STRUCT: - - hash: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -IdCommitment: - NEWTYPESTRUCT: BYTES -Identifier: - NEWTYPESTRUCT: STR -JWKMoveStruct: - STRUCT: - - variant: - TYPENAME: Any -KeylessPublicKey: - STRUCT: - - iss_val: STR - - idc: - TYPENAME: IdCommitment -KeylessSignature: - STRUCT: - - cert: - TYPENAME: EphemeralCertificate - - jwt_header_json: STR - - exp_date_secs: U64 - - ephemeral_pubkey: - TYPENAME: EphemeralPublicKey - - ephemeral_signature: - TYPENAME: EphemeralSignature -ModuleId: - STRUCT: - - address: - TYPENAME: AccountAddress - - name: - TYPENAME: Identifier -MultiEd25519PublicKey: - NEWTYPESTRUCT: BYTES -MultiEd25519Signature: - NEWTYPESTRUCT: BYTES -MultiKey: - STRUCT: - - public_keys: - SEQ: - TYPENAME: AnyPublicKey - - signatures_required: U8 -MultiKeyAuthenticator: - STRUCT: - - public_keys: - TYPENAME: MultiKey - - signatures: - SEQ: - TYPENAME: AnySignature - - signatures_bitmap: - TYPENAME: BitVec -Multisig: - STRUCT: - - multisig_address: - TYPENAME: AccountAddress - - transaction_payload: - OPTION: - TYPENAME: MultisigTransactionPayload -MultisigTransactionPayload: - ENUM: - 0: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction -OpenIdSig: - STRUCT: - - jwt_sig: BYTES - - jwt_payload_json: STR - - uid_key: STR - - epk_blinder: BYTES - - pepper: - TYPENAME: Pepper - - idc_aud_val: - OPTION: STR -PartialAuthenticatorAssertionResponse: - STRUCT: - - signature: - TYPENAME: AssertionSignature - - authenticator_data: BYTES - - client_data_json: BYTES -Path: - ENUM: - 0: - Code: - NEWTYPE: - TYPENAME: ModuleId - 1: - Resource: - NEWTYPE: - TYPENAME: StructTag - 2: - ResourceGroup: - NEWTYPE: - TYPENAME: StructTag -Pepper: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 31 -PrivateKey: - NEWTYPESTRUCT: BYTES -ProviderJWKs: - STRUCT: - - issuer: BYTES - - version: U64 - - jwks: - SEQ: - TYPENAME: JWKMoveStruct -PublicKey: - NEWTYPESTRUCT: BYTES -QuorumCertifiedUpdate: - STRUCT: - - update: - TYPENAME: ProviderJWKs - - multi_sig: - TYPENAME: AggregateSignature -RandMetadata: - STRUCT: - - metadata_to_sign: - TYPENAME: RandMetadataToSign - - block_id: - TYPENAME: HashValue - - timestamp: U64 -RandMetadataToSign: - STRUCT: - - epoch: U64 - - round: U64 -Randomness: - STRUCT: - - metadata: - TYPENAME: RandMetadata - - randomness: BYTES -RawTransaction: - STRUCT: - - sender: - TYPENAME: AccountAddress - - sequence_number: U64 - - payload: - TYPENAME: TransactionPayload - - max_gas_amount: U64 - - gas_unit_price: U64 - - expiration_timestamp_secs: U64 - - chain_id: - TYPENAME: ChainId -Script: - STRUCT: - - code: BYTES - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: - TYPENAME: TransactionArgument -Secp256k1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Signature: - NEWTYPESTRUCT: BYTES -SignedTransaction: - STRUCT: - - raw_txn: - TYPENAME: RawTransaction - - authenticator: - TYPENAME: TransactionAuthenticator -SingleKeyAuthenticator: - STRUCT: - - public_key: - TYPENAME: AnyPublicKey - - signature: - TYPENAME: AnySignature -StateKey: - ENUM: - 0: - AccessPath: - NEWTYPE: - TYPENAME: AccessPath - 1: - TableItem: - STRUCT: - - handle: - TYPENAME: TableHandle - - key: BYTES - 2: - Raw: - NEWTYPE: BYTES -StateValueMetadata: - ENUM: - 0: - V0: - STRUCT: - - deposit: U64 - - creation_time_usecs: U64 - 1: - V1: - STRUCT: - - slot_deposit: U64 - - bytes_deposit: U64 - - creation_time_usecs: U64 -StructTag: - STRUCT: - - address: - TYPENAME: AccountAddress - - module: - TYPENAME: Identifier - - name: - TYPENAME: Identifier - - type_args: - SEQ: - TYPENAME: TypeTag -TableHandle: - NEWTYPESTRUCT: - TYPENAME: AccountAddress -Transaction: - ENUM: - 0: - UserTransaction: - NEWTYPE: - TYPENAME: SignedTransaction - 1: - GenesisTransaction: - NEWTYPE: - TYPENAME: WriteSetPayload - 2: - BlockMetadata: - NEWTYPE: - TYPENAME: BlockMetadata - 3: - StateCheckpoint: - NEWTYPE: - TYPENAME: HashValue - 4: - ValidatorTransaction: - NEWTYPE: - TYPENAME: ValidatorTransaction - 5: - BlockMetadataExt: - NEWTYPE: - TYPENAME: BlockMetadataExt -TransactionArgument: - ENUM: - 0: - U8: - NEWTYPE: U8 - 1: - U64: - NEWTYPE: U64 - 2: - U128: - NEWTYPE: U128 - 3: - Address: - NEWTYPE: - TYPENAME: AccountAddress - 4: - U8Vector: - NEWTYPE: BYTES - 5: - Bool: - NEWTYPE: BOOL - 6: - U16: - NEWTYPE: U16 - 7: - U32: - NEWTYPE: U32 - 8: - U256: - NEWTYPE: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -TransactionAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - MultiAgent: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - 3: - FeePayer: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - - fee_payer_address: - TYPENAME: AccountAddress - - fee_payer_signer: - TYPENAME: AccountAuthenticator - 4: - SingleSender: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator -TransactionData: - ENUM: - 0: - OnChain: - NEWTYPE: - TYPENAME: TransactionOnChainData - 1: - Pending: - NEWTYPE: - TYPENAME: SignedTransaction -TransactionInfo: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: TransactionInfoV0 -TransactionInfoV0: - STRUCT: - - gas_used: U64 - - status: - TYPENAME: ExecutionStatus - - transaction_hash: - TYPENAME: HashValue - - event_root_hash: - TYPENAME: HashValue - - state_change_hash: - TYPENAME: HashValue - - state_checkpoint_hash: - OPTION: - TYPENAME: HashValue - - state_cemetery_hash: - OPTION: - TYPENAME: HashValue -TransactionOnChainData: - STRUCT: - - version: U64 - - transaction: - TYPENAME: Transaction - - info: - TYPENAME: TransactionInfo - - events: - SEQ: - TYPENAME: ContractEvent - - accumulator_root_hash: - TYPENAME: HashValue - - changes: - TYPENAME: WriteSet -TransactionPayload: - ENUM: - 0: - Script: - NEWTYPE: - TYPENAME: Script - 1: - ModuleBundle: - NEWTYPE: - TYPENAME: DeprecatedPayload - 2: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction - 3: - Multisig: - NEWTYPE: - TYPENAME: Multisig -TypeTag: - ENUM: - 0: - bool: UNIT - 1: - u8: UNIT - 2: - u64: UNIT - 3: - u128: UNIT - 4: - address: UNIT - 5: - signer: UNIT - 6: - vector: - NEWTYPE: - TYPENAME: TypeTag - 7: - struct: - NEWTYPE: - TYPENAME: StructTag - 8: - u16: UNIT - 9: - u32: UNIT - 10: - u256: UNIT -ValidatorTransaction: - ENUM: - 0: - DKGResult: - NEWTYPE: - TYPENAME: DKGTranscript - 1: - ObservedJWKUpdate: - NEWTYPE: - TYPENAME: QuorumCertifiedUpdate -WithdrawEvent: - STRUCT: - - amount: U64 -WriteOp: - ENUM: - 0: - Creation: - NEWTYPE: BYTES - 1: - Modification: - NEWTYPE: BYTES - 2: - Deletion: UNIT - 3: - CreationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 4: - ModificationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 5: - DeletionWithMetadata: - STRUCT: - - metadata: - TYPENAME: StateValueMetadata -WriteSet: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: WriteSetV0 -WriteSetMut: - STRUCT: - - write_set: - MAP: - KEY: - TYPENAME: StateKey - VALUE: - TYPENAME: WriteOp -WriteSetPayload: - ENUM: - 0: - Direct: - NEWTYPE: - TYPENAME: ChangeSet - 1: - Script: - STRUCT: - - execute_as: - TYPENAME: AccountAddress - - script: - TYPENAME: Script -WriteSetV0: - NEWTYPESTRUCT: - TYPENAME: WriteSetMut -ZKP: - ENUM: - 0: - Groth16: - NEWTYPE: - TYPENAME: Groth16Proof -ZeroKnowledgeSig: - STRUCT: - - proof: - TYPENAME: ZKP - - exp_horizon_secs: U64 - - extra_field: - OPTION: STR - - override_aud_val: - OPTION: STR - - training_wheels_signature: - OPTION: - TYPENAME: EphemeralSignature diff --git a/testsuite/generate-format/tests/staged/aptos.yaml b/testsuite/generate-format/tests/staged/aptos.yaml deleted file mode 100644 index b3790517f5636..0000000000000 --- a/testsuite/generate-format/tests/staged/aptos.yaml +++ /dev/null @@ -1,703 +0,0 @@ ---- -AccessPath: - STRUCT: - - address: - TYPENAME: AccountAddress - - path: BYTES -AccountAddress: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -AccountAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - SingleKey: - STRUCT: - - authenticator: - TYPENAME: SingleKeyAuthenticator - 3: - MultiKey: - STRUCT: - - authenticator: - TYPENAME: MultiKeyAuthenticator -AggregateSignature: - STRUCT: - - validator_bitmask: - TYPENAME: BitVec - - sig: - OPTION: - TYPENAME: Signature -Any: - STRUCT: - - type_name: STR - - data: BYTES -AnyPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256k1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256k1EcdsaPublicKey - 2: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey - 3: - Keyless: - STRUCT: - - public_key: - TYPENAME: KeylessPublicKey -AnySignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - Secp256k1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256k1EcdsaSignature - 2: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse - 3: - Keyless: - STRUCT: - - signature: - TYPENAME: KeylessSignature -AssertionSignature: - ENUM: - 0: - Secp256r1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256r1EcdsaSignature -BitVec: - STRUCT: - - inner: BYTES -BlockMetadata: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 -BlockMetadataExt: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: BlockMetadata - 1: - V1: - NEWTYPE: - TYPENAME: BlockMetadataWithRandomness -BlockMetadataWithRandomness: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 - - randomness: - OPTION: - TYPENAME: Randomness -ChainId: - NEWTYPESTRUCT: U8 -ChangeSet: - STRUCT: - - write_set: - TYPENAME: WriteSet - - events: - SEQ: - TYPENAME: ContractEvent -ContractEvent: - ENUM: - 0: - V1: - NEWTYPE: - TYPENAME: ContractEventV1 - 1: - V2: - NEWTYPE: - TYPENAME: ContractEventV2 -ContractEventV1: - STRUCT: - - key: - TYPENAME: EventKey - - sequence_number: U64 - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -ContractEventV2: - STRUCT: - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -DKGTranscript: - STRUCT: - - metadata: - TYPENAME: DKGTranscriptMetadata - - transcript_bytes: BYTES -DKGTranscriptMetadata: - STRUCT: - - epoch: U64 - - author: - TYPENAME: AccountAddress -DeprecatedPayload: - STRUCT: - - dummy_value: U64 -Ed25519PublicKey: - NEWTYPESTRUCT: BYTES -Ed25519Signature: - NEWTYPESTRUCT: BYTES -EntryFunction: - STRUCT: - - module: - TYPENAME: ModuleId - - function: - TYPENAME: Identifier - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: BYTES -EphemeralCertificate: - ENUM: - 0: - ZeroKnowledgeSig: - NEWTYPE: - TYPENAME: ZeroKnowledgeSig - 1: - OpenIdSig: - NEWTYPE: - TYPENAME: OpenIdSig -EphemeralPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey -EphemeralSignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse -EventKey: - STRUCT: - - creation_number: U64 - - account_address: - TYPENAME: AccountAddress -G1Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -G2Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 64 -Groth16Proof: - STRUCT: - - a: - TYPENAME: G1Bytes - - b: - TYPENAME: G2Bytes - - c: - TYPENAME: G1Bytes -HashValue: - STRUCT: - - hash: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -IdCommitment: - NEWTYPESTRUCT: BYTES -Identifier: - NEWTYPESTRUCT: STR -JWKMoveStruct: - STRUCT: - - variant: - TYPENAME: Any -KeylessPublicKey: - STRUCT: - - iss_val: STR - - idc: - TYPENAME: IdCommitment -KeylessSignature: - STRUCT: - - cert: - TYPENAME: EphemeralCertificate - - jwt_header_json: STR - - exp_date_secs: U64 - - ephemeral_pubkey: - TYPENAME: EphemeralPublicKey - - ephemeral_signature: - TYPENAME: EphemeralSignature -ModuleId: - STRUCT: - - address: - TYPENAME: AccountAddress - - name: - TYPENAME: Identifier -MultiEd25519PublicKey: - NEWTYPESTRUCT: BYTES -MultiEd25519Signature: - NEWTYPESTRUCT: BYTES -MultiKey: - STRUCT: - - public_keys: - SEQ: - TYPENAME: AnyPublicKey - - signatures_required: U8 -MultiKeyAuthenticator: - STRUCT: - - public_keys: - TYPENAME: MultiKey - - signatures: - SEQ: - TYPENAME: AnySignature - - signatures_bitmap: - TYPENAME: BitVec -Multisig: - STRUCT: - - multisig_address: - TYPENAME: AccountAddress - - transaction_payload: - OPTION: - TYPENAME: MultisigTransactionPayload -MultisigTransactionPayload: - ENUM: - 0: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction -OpenIdSig: - STRUCT: - - jwt_sig: BYTES - - jwt_payload_json: STR - - uid_key: STR - - epk_blinder: BYTES - - pepper: - TYPENAME: Pepper - - idc_aud_val: - OPTION: STR -PartialAuthenticatorAssertionResponse: - STRUCT: - - signature: - TYPENAME: AssertionSignature - - authenticator_data: BYTES - - client_data_json: BYTES -Pepper: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 31 -PrivateKey: - NEWTYPESTRUCT: BYTES -ProviderJWKs: - STRUCT: - - issuer: BYTES - - version: U64 - - jwks: - SEQ: - TYPENAME: JWKMoveStruct -PublicKey: - NEWTYPESTRUCT: BYTES -QuorumCertifiedUpdate: - STRUCT: - - update: - TYPENAME: ProviderJWKs - - multi_sig: - TYPENAME: AggregateSignature -RandMetadata: - STRUCT: - - metadata_to_sign: - TYPENAME: RandMetadataToSign - - block_id: - TYPENAME: HashValue - - timestamp: U64 -RandMetadataToSign: - STRUCT: - - epoch: U64 - - round: U64 -Randomness: - STRUCT: - - metadata: - TYPENAME: RandMetadata - - randomness: BYTES -RawTransaction: - STRUCT: - - sender: - TYPENAME: AccountAddress - - sequence_number: U64 - - payload: - TYPENAME: TransactionPayload - - max_gas_amount: U64 - - gas_unit_price: U64 - - expiration_timestamp_secs: U64 - - chain_id: - TYPENAME: ChainId -Script: - STRUCT: - - code: BYTES - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: - TYPENAME: TransactionArgument -Secp256k1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Signature: - NEWTYPESTRUCT: BYTES -SignedTransaction: - STRUCT: - - raw_txn: - TYPENAME: RawTransaction - - authenticator: - TYPENAME: TransactionAuthenticator -SingleKeyAuthenticator: - STRUCT: - - public_key: - TYPENAME: AnyPublicKey - - signature: - TYPENAME: AnySignature -StateKey: - ENUM: - 0: - AccessPath: - NEWTYPE: - TYPENAME: AccessPath - 1: - TableItem: - STRUCT: - - handle: - TYPENAME: TableHandle - - key: BYTES - 2: - Raw: - NEWTYPE: BYTES -StateValueMetadata: - ENUM: - 0: - V0: - STRUCT: - - deposit: U64 - - creation_time_usecs: U64 - 1: - V1: - STRUCT: - - slot_deposit: U64 - - bytes_deposit: U64 - - creation_time_usecs: U64 -StructTag: - STRUCT: - - address: - TYPENAME: AccountAddress - - module: - TYPENAME: Identifier - - name: - TYPENAME: Identifier - - type_args: - SEQ: - TYPENAME: TypeTag -TableHandle: - NEWTYPESTRUCT: - TYPENAME: AccountAddress -Transaction: - ENUM: - 0: - UserTransaction: - NEWTYPE: - TYPENAME: SignedTransaction - 1: - GenesisTransaction: - NEWTYPE: - TYPENAME: WriteSetPayload - 2: - BlockMetadata: - NEWTYPE: - TYPENAME: BlockMetadata - 3: - StateCheckpoint: - NEWTYPE: - TYPENAME: HashValue - 4: - ValidatorTransaction: - NEWTYPE: - TYPENAME: ValidatorTransaction - 5: - BlockMetadataExt: - NEWTYPE: - TYPENAME: BlockMetadataExt -TransactionArgument: - ENUM: - 0: - U8: - NEWTYPE: U8 - 1: - U64: - NEWTYPE: U64 - 2: - U128: - NEWTYPE: U128 - 3: - Address: - NEWTYPE: - TYPENAME: AccountAddress - 4: - U8Vector: - NEWTYPE: BYTES - 5: - Bool: - NEWTYPE: BOOL - 6: - U16: - NEWTYPE: U16 - 7: - U32: - NEWTYPE: U32 - 8: - U256: - NEWTYPE: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -TransactionAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - MultiAgent: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - 3: - FeePayer: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - - fee_payer_address: - TYPENAME: AccountAddress - - fee_payer_signer: - TYPENAME: AccountAuthenticator - 4: - SingleSender: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator -TransactionPayload: - ENUM: - 0: - Script: - NEWTYPE: - TYPENAME: Script - 1: - ModuleBundle: - NEWTYPE: - TYPENAME: DeprecatedPayload - 2: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction - 3: - Multisig: - NEWTYPE: - TYPENAME: Multisig -TypeTag: - ENUM: - 0: - bool: UNIT - 1: - u8: UNIT - 2: - u64: UNIT - 3: - u128: UNIT - 4: - address: UNIT - 5: - signer: UNIT - 6: - vector: - NEWTYPE: - TYPENAME: TypeTag - 7: - struct: - NEWTYPE: - TYPENAME: StructTag - 8: - u16: UNIT - 9: - u32: UNIT - 10: - u256: UNIT -ValidatorTransaction: - ENUM: - 0: - DKGResult: - NEWTYPE: - TYPENAME: DKGTranscript - 1: - ObservedJWKUpdate: - NEWTYPE: - TYPENAME: QuorumCertifiedUpdate -WriteOp: - ENUM: - 0: - Creation: - NEWTYPE: BYTES - 1: - Modification: - NEWTYPE: BYTES - 2: - Deletion: UNIT - 3: - CreationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 4: - ModificationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 5: - DeletionWithMetadata: - STRUCT: - - metadata: - TYPENAME: StateValueMetadata -WriteSet: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: WriteSetV0 -WriteSetMut: - STRUCT: - - write_set: - MAP: - KEY: - TYPENAME: StateKey - VALUE: - TYPENAME: WriteOp -WriteSetPayload: - ENUM: - 0: - Direct: - NEWTYPE: - TYPENAME: ChangeSet - 1: - Script: - STRUCT: - - execute_as: - TYPENAME: AccountAddress - - script: - TYPENAME: Script -WriteSetV0: - NEWTYPESTRUCT: - TYPENAME: WriteSetMut -ZKP: - ENUM: - 0: - Groth16: - NEWTYPE: - TYPENAME: Groth16Proof -ZeroKnowledgeSig: - STRUCT: - - proof: - TYPENAME: ZKP - - exp_horizon_secs: U64 - - extra_field: - OPTION: STR - - override_aud_val: - OPTION: STR - - training_wheels_signature: - OPTION: - TYPENAME: EphemeralSignature diff --git a/testsuite/generate-format/tests/staged/consensus.yaml b/testsuite/generate-format/tests/staged/consensus.yaml deleted file mode 100644 index 17bc09bc8763c..0000000000000 --- a/testsuite/generate-format/tests/staged/consensus.yaml +++ /dev/null @@ -1,1135 +0,0 @@ ---- -AccessPath: - STRUCT: - - address: - TYPENAME: AccountAddress - - path: BYTES -AccountAddress: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -AccountAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - SingleKey: - STRUCT: - - authenticator: - TYPENAME: SingleKeyAuthenticator - 3: - MultiKey: - STRUCT: - - authenticator: - TYPENAME: MultiKeyAuthenticator -AggregateSignature: - STRUCT: - - validator_bitmask: - TYPENAME: BitVec - - sig: - OPTION: - TYPENAME: Signature -AggregateSignatureWithRounds: - STRUCT: - - sig: - TYPENAME: AggregateSignature - - rounds: - SEQ: U64 -Any: - STRUCT: - - type_name: STR - - data: BYTES -AnyPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256k1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256k1EcdsaPublicKey - 2: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey - 3: - Keyless: - STRUCT: - - public_key: - TYPENAME: KeylessPublicKey -AnySignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - Secp256k1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256k1EcdsaSignature - 2: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse - 3: - Keyless: - STRUCT: - - signature: - TYPENAME: KeylessSignature -AssertionSignature: - ENUM: - 0: - Secp256r1Ecdsa: - STRUCT: - - signature: - TYPENAME: Secp256r1EcdsaSignature -Batch: - STRUCT: - - batch_info: - TYPENAME: BatchInfo - - payload: - TYPENAME: BatchPayload -BatchId: - STRUCT: - - id: U64 - - nonce: U64 -BatchInfo: - STRUCT: - - author: - TYPENAME: AccountAddress - - batch_id: - TYPENAME: BatchId - - epoch: U64 - - expiration: U64 - - digest: - TYPENAME: HashValue - - num_txns: U64 - - num_bytes: U64 - - gas_bucket_start: U64 -BatchMsg: - STRUCT: - - batches: - SEQ: - TYPENAME: Batch -BatchPayload: - STRUCT: - - author: - TYPENAME: AccountAddress - - txns: - SEQ: - TYPENAME: SignedTransaction -BatchRequest: - STRUCT: - - epoch: U64 - - source: - TYPENAME: AccountAddress - - digest: - TYPENAME: HashValue -BatchResponse: - ENUM: - 0: - Batch: - NEWTYPE: - TYPENAME: Batch - 1: - NotFound: - NEWTYPE: - TYPENAME: LedgerInfoWithSignatures -BitVec: - STRUCT: - - inner: BYTES -Block: - STRUCT: - - block_data: - TYPENAME: BlockData - - signature: - OPTION: - TYPENAME: Signature -BlockData: - STRUCT: - - epoch: U64 - - round: U64 - - timestamp_usecs: U64 - - quorum_cert: - TYPENAME: QuorumCert - - block_type: - TYPENAME: BlockType -BlockInfo: - STRUCT: - - epoch: U64 - - round: U64 - - id: - TYPENAME: HashValue - - executed_state_id: - TYPENAME: HashValue - - version: U64 - - timestamp_usecs: U64 - - next_epoch_state: - OPTION: - TYPENAME: EpochState -BlockMetadata: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 -BlockMetadataExt: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: BlockMetadata - 1: - V1: - NEWTYPE: - TYPENAME: BlockMetadataWithRandomness -BlockMetadataWithRandomness: - STRUCT: - - id: - TYPENAME: HashValue - - epoch: U64 - - round: U64 - - proposer: - TYPENAME: AccountAddress - - previous_block_votes_bitvec: BYTES - - failed_proposer_indices: - SEQ: U32 - - timestamp_usecs: U64 - - randomness: - OPTION: - TYPENAME: Randomness -BlockRetrievalRequest: - STRUCT: - - block_id: - TYPENAME: HashValue - - num_blocks: U64 - - target_block_id: - OPTION: - TYPENAME: HashValue -BlockRetrievalResponse: - STRUCT: - - status: - TYPENAME: BlockRetrievalStatus - - blocks: - SEQ: - TYPENAME: Block -BlockRetrievalStatus: - ENUM: - 0: - Succeeded: UNIT - 1: - IdNotFound: UNIT - 2: - NotEnoughBlocks: UNIT - 3: - SucceededWithTarget: UNIT -BlockType: - ENUM: - 0: - Proposal: - STRUCT: - - payload: - TYPENAME: Payload - - author: - TYPENAME: AccountAddress - - failed_authors: - SEQ: - TUPLE: - - U64 - - TYPENAME: AccountAddress - 1: - NilBlock: - STRUCT: - - failed_authors: - SEQ: - TUPLE: - - U64 - - TYPENAME: AccountAddress - 2: - Genesis: UNIT - 3: - ProposalExt: - NEWTYPE: - TYPENAME: ProposalExt -ChainId: - NEWTYPESTRUCT: U8 -ChangeSet: - STRUCT: - - write_set: - TYPENAME: WriteSet - - events: - SEQ: - TYPENAME: ContractEvent -CommitDecision: - STRUCT: - - ledger_info: - TYPENAME: LedgerInfoWithSignatures -CommitMessage: - ENUM: - 0: - Vote: - NEWTYPE: - TYPENAME: CommitVote - 1: - Decision: - NEWTYPE: - TYPENAME: CommitDecision - 2: - Ack: - NEWTYPE: UNIT - 3: - Nack: UNIT -CommitVote: - STRUCT: - - author: - TYPENAME: AccountAddress - - ledger_info: - TYPENAME: LedgerInfo - - signature: - TYPENAME: Signature -ConsensusMsg: - ENUM: - 0: - BlockRetrievalRequest: - NEWTYPE: - TYPENAME: BlockRetrievalRequest - 1: - BlockRetrievalResponse: - NEWTYPE: - TYPENAME: BlockRetrievalResponse - 2: - EpochRetrievalRequest: - NEWTYPE: - TYPENAME: EpochRetrievalRequest - 3: - ProposalMsg: - NEWTYPE: - TYPENAME: ProposalMsg - 4: - SyncInfo: - NEWTYPE: - TYPENAME: SyncInfo - 5: - EpochChangeProof: - NEWTYPE: - TYPENAME: EpochChangeProof - 6: - VoteMsg: - NEWTYPE: - TYPENAME: VoteMsg - 7: - CommitVoteMsg: - NEWTYPE: - TYPENAME: CommitVote - 8: - CommitDecisionMsg: - NEWTYPE: - TYPENAME: CommitDecision - 9: - BatchMsg: - NEWTYPE: - TYPENAME: BatchMsg - 10: - BatchRequestMsg: - NEWTYPE: - TYPENAME: BatchRequest - 11: - BatchResponse: - NEWTYPE: - TYPENAME: Batch - 12: - SignedBatchInfo: - NEWTYPE: - TYPENAME: SignedBatchInfoMsg - 13: - ProofOfStoreMsg: - NEWTYPE: - TYPENAME: ProofOfStoreMsg - 14: - DAGMessage: - NEWTYPE: - TYPENAME: DAGNetworkMessage - 15: - CommitMessage: - NEWTYPE: - TYPENAME: CommitMessage - 16: - RandGenMessage: - NEWTYPE: - TYPENAME: RandGenMessage - 17: - BatchResponseV2: - NEWTYPE: - TYPENAME: BatchResponse -ContractEvent: - ENUM: - 0: - V1: - NEWTYPE: - TYPENAME: ContractEventV1 - 1: - V2: - NEWTYPE: - TYPENAME: ContractEventV2 -ContractEventV1: - STRUCT: - - key: - TYPENAME: EventKey - - sequence_number: U64 - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -ContractEventV2: - STRUCT: - - type_tag: - TYPENAME: TypeTag - - event_data: BYTES -DAGNetworkMessage: - STRUCT: - - epoch: U64 - - data: BYTES -DKGTranscript: - STRUCT: - - metadata: - TYPENAME: DKGTranscriptMetadata - - transcript_bytes: BYTES -DKGTranscriptMetadata: - STRUCT: - - epoch: U64 - - author: - TYPENAME: AccountAddress -DeprecatedPayload: - STRUCT: - - dummy_value: U64 -Ed25519PublicKey: - NEWTYPESTRUCT: BYTES -Ed25519Signature: - NEWTYPESTRUCT: BYTES -EntryFunction: - STRUCT: - - module: - TYPENAME: ModuleId - - function: - TYPENAME: Identifier - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: BYTES -EphemeralCertificate: - ENUM: - 0: - ZeroKnowledgeSig: - NEWTYPE: - TYPENAME: ZeroKnowledgeSig - 1: - OpenIdSig: - NEWTYPE: - TYPENAME: OpenIdSig -EphemeralPublicKey: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - 1: - Secp256r1Ecdsa: - STRUCT: - - public_key: - TYPENAME: Secp256r1EcdsaPublicKey -EphemeralSignature: - ENUM: - 0: - Ed25519: - STRUCT: - - signature: - TYPENAME: Ed25519Signature - 1: - WebAuthn: - STRUCT: - - signature: - TYPENAME: PartialAuthenticatorAssertionResponse -EpochChangeProof: - STRUCT: - - ledger_info_with_sigs: - SEQ: - TYPENAME: LedgerInfoWithSignatures - - more: BOOL -EpochRetrievalRequest: - STRUCT: - - start_epoch: U64 - - end_epoch: U64 -EpochState: - STRUCT: - - epoch: U64 - - verifier: - TYPENAME: ValidatorVerifier -EventKey: - STRUCT: - - creation_number: U64 - - account_address: - TYPENAME: AccountAddress -G1Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -G2Bytes: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 64 -Groth16Proof: - STRUCT: - - a: - TYPENAME: G1Bytes - - b: - TYPENAME: G2Bytes - - c: - TYPENAME: G1Bytes -HashValue: - STRUCT: - - hash: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -IdCommitment: - NEWTYPESTRUCT: BYTES -Identifier: - NEWTYPESTRUCT: STR -JWKMoveStruct: - STRUCT: - - variant: - TYPENAME: Any -KeylessPublicKey: - STRUCT: - - iss_val: STR - - idc: - TYPENAME: IdCommitment -KeylessSignature: - STRUCT: - - cert: - TYPENAME: EphemeralCertificate - - jwt_header_json: STR - - exp_date_secs: U64 - - ephemeral_pubkey: - TYPENAME: EphemeralPublicKey - - ephemeral_signature: - TYPENAME: EphemeralSignature -LedgerInfo: - STRUCT: - - commit_info: - TYPENAME: BlockInfo - - consensus_data_hash: - TYPENAME: HashValue -LedgerInfoWithSignatures: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: LedgerInfoWithV0 -LedgerInfoWithV0: - STRUCT: - - ledger_info: - TYPENAME: LedgerInfo - - signatures: - TYPENAME: AggregateSignature -ModuleId: - STRUCT: - - address: - TYPENAME: AccountAddress - - name: - TYPENAME: Identifier -MultiEd25519PublicKey: - NEWTYPESTRUCT: BYTES -MultiEd25519Signature: - NEWTYPESTRUCT: BYTES -MultiKey: - STRUCT: - - public_keys: - SEQ: - TYPENAME: AnyPublicKey - - signatures_required: U8 -MultiKeyAuthenticator: - STRUCT: - - public_keys: - TYPENAME: MultiKey - - signatures: - SEQ: - TYPENAME: AnySignature - - signatures_bitmap: - TYPENAME: BitVec -Multisig: - STRUCT: - - multisig_address: - TYPENAME: AccountAddress - - transaction_payload: - OPTION: - TYPENAME: MultisigTransactionPayload -MultisigTransactionPayload: - ENUM: - 0: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction -OpenIdSig: - STRUCT: - - jwt_sig: BYTES - - jwt_payload_json: STR - - uid_key: STR - - epk_blinder: BYTES - - pepper: - TYPENAME: Pepper - - idc_aud_val: - OPTION: STR -PartialAuthenticatorAssertionResponse: - STRUCT: - - signature: - TYPENAME: AssertionSignature - - authenticator_data: BYTES - - client_data_json: BYTES -Payload: - ENUM: - 0: - DirectMempool: - NEWTYPE: - SEQ: - TYPENAME: SignedTransaction - 1: - InQuorumStore: - NEWTYPE: - TYPENAME: ProofWithData - 2: - InQuorumStoreWithLimit: - NEWTYPE: - TYPENAME: ProofWithDataWithTxnLimit - 3: - QuorumStoreInlineHybrid: - TUPLE: - - SEQ: - TUPLE: - - TYPENAME: BatchInfo - - SEQ: - TYPENAME: SignedTransaction - - TYPENAME: ProofWithData - - OPTION: U64 -Pepper: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 31 -ProofOfStore: - STRUCT: - - info: - TYPENAME: BatchInfo - - multi_signature: - TYPENAME: AggregateSignature -ProofOfStoreMsg: - STRUCT: - - proofs: - SEQ: - TYPENAME: ProofOfStore -ProofWithData: - STRUCT: - - proofs: - SEQ: - TYPENAME: ProofOfStore -ProofWithDataWithTxnLimit: - STRUCT: - - proof_with_data: - TYPENAME: ProofWithData - - max_txns_to_execute: - OPTION: U64 -ProposalExt: - ENUM: - 0: - V0: - STRUCT: - - validator_txns: - SEQ: - TYPENAME: ValidatorTransaction - - payload: - TYPENAME: Payload - - author: - TYPENAME: AccountAddress - - failed_authors: - SEQ: - TUPLE: - - U64 - - TYPENAME: AccountAddress -ProposalMsg: - STRUCT: - - proposal: - TYPENAME: Block - - sync_info: - TYPENAME: SyncInfo -ProviderJWKs: - STRUCT: - - issuer: BYTES - - version: U64 - - jwks: - SEQ: - TYPENAME: JWKMoveStruct -PublicKey: - NEWTYPESTRUCT: BYTES -QuorumCert: - STRUCT: - - vote_data: - TYPENAME: VoteData - - signed_ledger_info: - TYPENAME: LedgerInfoWithSignatures -QuorumCertifiedUpdate: - STRUCT: - - update: - TYPENAME: ProviderJWKs - - multi_sig: - TYPENAME: AggregateSignature -RandGenMessage: - STRUCT: - - epoch: U64 - - data: BYTES -RandMetadata: - STRUCT: - - metadata_to_sign: - TYPENAME: RandMetadataToSign - - block_id: - TYPENAME: HashValue - - timestamp: U64 -RandMetadataToSign: - STRUCT: - - epoch: U64 - - round: U64 -Randomness: - STRUCT: - - metadata: - TYPENAME: RandMetadata - - randomness: BYTES -RawTransaction: - STRUCT: - - sender: - TYPENAME: AccountAddress - - sequence_number: U64 - - payload: - TYPENAME: TransactionPayload - - max_gas_amount: U64 - - gas_unit_price: U64 - - expiration_timestamp_secs: U64 - - chain_id: - TYPENAME: ChainId -Script: - STRUCT: - - code: BYTES - - ty_args: - SEQ: - TYPENAME: TypeTag - - args: - SEQ: - TYPENAME: TransactionArgument -Secp256k1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256k1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPrivateKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaPublicKey: - NEWTYPESTRUCT: BYTES -Secp256r1EcdsaSignature: - NEWTYPESTRUCT: BYTES -Signature: - NEWTYPESTRUCT: BYTES -SignedBatchInfo: - STRUCT: - - info: - TYPENAME: BatchInfo - - signer: - TYPENAME: AccountAddress - - signature: - TYPENAME: Signature -SignedBatchInfoMsg: - STRUCT: - - signed_infos: - SEQ: - TYPENAME: SignedBatchInfo -SignedTransaction: - STRUCT: - - raw_txn: - TYPENAME: RawTransaction - - authenticator: - TYPENAME: TransactionAuthenticator -SingleKeyAuthenticator: - STRUCT: - - public_key: - TYPENAME: AnyPublicKey - - signature: - TYPENAME: AnySignature -StateKey: - ENUM: - 0: - AccessPath: - NEWTYPE: - TYPENAME: AccessPath - 1: - TableItem: - STRUCT: - - handle: - TYPENAME: TableHandle - - key: BYTES - 2: - Raw: - NEWTYPE: BYTES -StateValueMetadata: - ENUM: - 0: - V0: - STRUCT: - - deposit: U64 - - creation_time_usecs: U64 - 1: - V1: - STRUCT: - - slot_deposit: U64 - - bytes_deposit: U64 - - creation_time_usecs: U64 -StructTag: - STRUCT: - - address: - TYPENAME: AccountAddress - - module: - TYPENAME: Identifier - - name: - TYPENAME: Identifier - - type_args: - SEQ: - TYPENAME: TypeTag -SyncInfo: - STRUCT: - - highest_quorum_cert: - TYPENAME: QuorumCert - - highest_ordered_cert: - OPTION: - TYPENAME: QuorumCert - - highest_commit_cert: - TYPENAME: QuorumCert - - highest_2chain_timeout_cert: - OPTION: - TYPENAME: TwoChainTimeoutCertificate -TableHandle: - NEWTYPESTRUCT: - TYPENAME: AccountAddress -Transaction: - ENUM: - 0: - UserTransaction: - NEWTYPE: - TYPENAME: SignedTransaction - 1: - GenesisTransaction: - NEWTYPE: - TYPENAME: WriteSetPayload - 2: - BlockMetadata: - NEWTYPE: - TYPENAME: BlockMetadata - 3: - StateCheckpoint: - NEWTYPE: - TYPENAME: HashValue - 4: - ValidatorTransaction: - NEWTYPE: - TYPENAME: ValidatorTransaction - 5: - BlockMetadataExt: - NEWTYPE: - TYPENAME: BlockMetadataExt -TransactionArgument: - ENUM: - 0: - U8: - NEWTYPE: U8 - 1: - U64: - NEWTYPE: U64 - 2: - U128: - NEWTYPE: U128 - 3: - Address: - NEWTYPE: - TYPENAME: AccountAddress - 4: - U8Vector: - NEWTYPE: BYTES - 5: - Bool: - NEWTYPE: BOOL - 6: - U16: - NEWTYPE: U16 - 7: - U32: - NEWTYPE: U32 - 8: - U256: - NEWTYPE: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -TransactionAuthenticator: - ENUM: - 0: - Ed25519: - STRUCT: - - public_key: - TYPENAME: Ed25519PublicKey - - signature: - TYPENAME: Ed25519Signature - 1: - MultiEd25519: - STRUCT: - - public_key: - TYPENAME: MultiEd25519PublicKey - - signature: - TYPENAME: MultiEd25519Signature - 2: - MultiAgent: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - 3: - FeePayer: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator - - secondary_signer_addresses: - SEQ: - TYPENAME: AccountAddress - - secondary_signers: - SEQ: - TYPENAME: AccountAuthenticator - - fee_payer_address: - TYPENAME: AccountAddress - - fee_payer_signer: - TYPENAME: AccountAuthenticator - 4: - SingleSender: - STRUCT: - - sender: - TYPENAME: AccountAuthenticator -TransactionPayload: - ENUM: - 0: - Script: - NEWTYPE: - TYPENAME: Script - 1: - ModuleBundle: - NEWTYPE: - TYPENAME: DeprecatedPayload - 2: - EntryFunction: - NEWTYPE: - TYPENAME: EntryFunction - 3: - Multisig: - NEWTYPE: - TYPENAME: Multisig -TwoChainTimeout: - STRUCT: - - epoch: U64 - - round: U64 - - quorum_cert: - TYPENAME: QuorumCert -TwoChainTimeoutCertificate: - STRUCT: - - timeout: - TYPENAME: TwoChainTimeout - - signatures_with_rounds: - TYPENAME: AggregateSignatureWithRounds -TypeTag: - ENUM: - 0: - bool: UNIT - 1: - u8: UNIT - 2: - u64: UNIT - 3: - u128: UNIT - 4: - address: UNIT - 5: - signer: UNIT - 6: - vector: - NEWTYPE: - TYPENAME: TypeTag - 7: - struct: - NEWTYPE: - TYPENAME: StructTag - 8: - u16: UNIT - 9: - u32: UNIT - 10: - u256: UNIT -ValidatorConsensusInfo: - STRUCT: - - address: - TYPENAME: AccountAddress - - public_key: - TYPENAME: PublicKey - - voting_power: U64 -ValidatorTransaction: - ENUM: - 0: - DKGResult: - NEWTYPE: - TYPENAME: DKGTranscript - 1: - ObservedJWKUpdate: - NEWTYPE: - TYPENAME: QuorumCertifiedUpdate -ValidatorVerifier: - STRUCT: - - validator_infos: - SEQ: - TYPENAME: ValidatorConsensusInfo -Vote: - STRUCT: - - vote_data: - TYPENAME: VoteData - - author: - TYPENAME: AccountAddress - - ledger_info: - TYPENAME: LedgerInfo - - signature: - TYPENAME: Signature - - two_chain_timeout: - OPTION: - TUPLE: - - TYPENAME: TwoChainTimeout - - TYPENAME: Signature -VoteData: - STRUCT: - - proposed: - TYPENAME: BlockInfo - - parent: - TYPENAME: BlockInfo -VoteMsg: - STRUCT: - - vote: - TYPENAME: Vote - - sync_info: - TYPENAME: SyncInfo -WriteOp: - ENUM: - 0: - Creation: - NEWTYPE: BYTES - 1: - Modification: - NEWTYPE: BYTES - 2: - Deletion: UNIT - 3: - CreationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 4: - ModificationWithMetadata: - STRUCT: - - data: BYTES - - metadata: - TYPENAME: StateValueMetadata - 5: - DeletionWithMetadata: - STRUCT: - - metadata: - TYPENAME: StateValueMetadata -WriteSet: - ENUM: - 0: - V0: - NEWTYPE: - TYPENAME: WriteSetV0 -WriteSetMut: - STRUCT: - - write_set: - MAP: - KEY: - TYPENAME: StateKey - VALUE: - TYPENAME: WriteOp -WriteSetPayload: - ENUM: - 0: - Direct: - NEWTYPE: - TYPENAME: ChangeSet - 1: - Script: - STRUCT: - - execute_as: - TYPENAME: AccountAddress - - script: - TYPENAME: Script -WriteSetV0: - NEWTYPESTRUCT: - TYPENAME: WriteSetMut -ZKP: - ENUM: - 0: - Groth16: - NEWTYPE: - TYPENAME: Groth16Proof -ZeroKnowledgeSig: - STRUCT: - - proof: - TYPENAME: ZKP - - exp_horizon_secs: U64 - - extra_field: - OPTION: STR - - override_aud_val: - OPTION: STR - - training_wheels_signature: - OPTION: - TYPENAME: EphemeralSignature diff --git a/testsuite/generate-format/tests/staged/move_abi.yaml b/testsuite/generate-format/tests/staged/move_abi.yaml deleted file mode 100644 index 8cd6dcd8b77ec..0000000000000 --- a/testsuite/generate-format/tests/staged/move_abi.yaml +++ /dev/null @@ -1,94 +0,0 @@ ---- -AccountAddress: - NEWTYPESTRUCT: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 -ArgumentABI: - STRUCT: - - name: STR - - type_tag: - TYPENAME: TypeTag -EntryABI: - ENUM: - 0: - TransactionScript: - NEWTYPE: - TYPENAME: TransactionScriptABI - 1: - EntryFunction: - NEWTYPE: - TYPENAME: ScriptFunctionABI -Identifier: - NEWTYPESTRUCT: STR -ModuleId: - STRUCT: - - address: - TYPENAME: AccountAddress - - name: - TYPENAME: Identifier -ScriptFunctionABI: - STRUCT: - - name: STR - - module_name: - TYPENAME: ModuleId - - doc: STR - - ty_args: - SEQ: - TYPENAME: TypeArgumentABI - - args: - SEQ: - TYPENAME: ArgumentABI -StructTag: - STRUCT: - - address: - TYPENAME: AccountAddress - - module: - TYPENAME: Identifier - - name: - TYPENAME: Identifier - - type_args: - SEQ: - TYPENAME: TypeTag -TransactionScriptABI: - STRUCT: - - name: STR - - doc: STR - - code: BYTES - - ty_args: - SEQ: - TYPENAME: TypeArgumentABI - - args: - SEQ: - TYPENAME: ArgumentABI -TypeArgumentABI: - STRUCT: - - name: STR -TypeTag: - ENUM: - 0: - bool: UNIT - 1: - u8: UNIT - 2: - u64: UNIT - 3: - u128: UNIT - 4: - address: UNIT - 5: - signer: UNIT - 6: - vector: - NEWTYPE: - TYPENAME: TypeTag - 7: - struct: - NEWTYPE: - TYPENAME: StructTag - 8: - u16: UNIT - 9: - u32: UNIT - 10: - u256: UNIT diff --git a/testsuite/generate-format/tests/staged/network.yaml b/testsuite/generate-format/tests/staged/network.yaml deleted file mode 100644 index a2d569515c3c9..0000000000000 --- a/testsuite/generate-format/tests/staged/network.yaml +++ /dev/null @@ -1,199 +0,0 @@ ---- -BitVec: - STRUCT: - - inner: BYTES -ChainId: - NEWTYPESTRUCT: U8 -DirectSendMsg: - STRUCT: - - protocol_id: - TYPENAME: ProtocolId - - priority: U8 - - raw_msg: BYTES -DnsName: - NEWTYPESTRUCT: STR -ErrorCode: - ENUM: - 0: - ParsingError: - NEWTYPE: - TYPENAME: ParsingErrorType - 1: - NotSupported: - NEWTYPE: - TYPENAME: NotSupportedType -HandshakeMsg: - STRUCT: - - supported_protocols: - MAP: - KEY: - TYPENAME: MessagingProtocolVersion - VALUE: - TYPENAME: ProtocolIdSet - - chain_id: - TYPENAME: ChainId - - network_id: - TYPENAME: NetworkId -MessagingProtocolVersion: - ENUM: - 0: - V1: UNIT -NetworkAddress: - NEWTYPESTRUCT: BYTES -NetworkId: - ENUM: - 0: - validator: UNIT - 1: - public: UNIT - 2: - private: - NEWTYPE: STR - 3: - vfn: UNIT - 4: - new_public: UNIT -NetworkMessage: - ENUM: - 0: - Error: - NEWTYPE: - TYPENAME: ErrorCode - 1: - RpcRequest: - NEWTYPE: - TYPENAME: RpcRequest - 2: - RpcResponse: - NEWTYPE: - TYPENAME: RpcResponse - 3: - DirectSendMsg: - NEWTYPE: - TYPENAME: DirectSendMsg -NotSupportedType: - ENUM: - 0: - RpcRequest: - NEWTYPE: - TYPENAME: ProtocolId - 1: - DirectSendMsg: - NEWTYPE: - TYPENAME: ProtocolId -ParsingErrorType: - STRUCT: - - message: U8 - - protocol: U8 -Protocol: - ENUM: - 0: - Ip4: - NEWTYPE: - TUPLEARRAY: - CONTENT: U8 - SIZE: 4 - 1: - Ip6: - NEWTYPE: - TUPLEARRAY: - CONTENT: U8 - SIZE: 16 - 2: - Dns: - NEWTYPE: - TYPENAME: DnsName - 3: - Dns4: - NEWTYPE: - TYPENAME: DnsName - 4: - Dns6: - NEWTYPE: - TYPENAME: DnsName - 5: - Tcp: - NEWTYPE: U16 - 6: - Memory: - NEWTYPE: U16 - 7: - NoiseIK: - NEWTYPE: - TYPENAME: PublicKey - 8: - Handshake: - NEWTYPE: U8 -ProtocolId: - ENUM: - 0: - ConsensusRpcBcs: UNIT - 1: - ConsensusDirectSendBcs: UNIT - 2: - MempoolDirectSend: UNIT - 3: - StateSyncDirectSend: UNIT - 4: - DiscoveryDirectSend: UNIT - 5: - HealthCheckerRpc: UNIT - 6: - ConsensusDirectSendJson: UNIT - 7: - ConsensusRpcJson: UNIT - 8: - StorageServiceRpc: UNIT - 9: - MempoolRpc: UNIT - 10: - PeerMonitoringServiceRpc: UNIT - 11: - ConsensusRpcCompressed: UNIT - 12: - ConsensusDirectSendCompressed: UNIT - 13: - NetbenchDirectSend: UNIT - 14: - NetbenchRpc: UNIT - 15: - DKGDirectSendCompressed: UNIT - 16: - DKGDirectSendBcs: UNIT - 17: - DKGDirectSendJson: UNIT - 18: - DKGRpcCompressed: UNIT - 19: - DKGRpcBcs: UNIT - 20: - DKGRpcJson: UNIT - 21: - JWKConsensusDirectSendCompressed: UNIT - 22: - JWKConsensusDirectSendBcs: UNIT - 23: - JWKConsensusDirectSendJson: UNIT - 24: - JWKConsensusRpcCompressed: UNIT - 25: - JWKConsensusRpcBcs: UNIT - 26: - JWKConsensusRpcJson: UNIT -ProtocolIdSet: - NEWTYPESTRUCT: - TYPENAME: BitVec -PublicKey: - NEWTYPESTRUCT: BYTES -RpcRequest: - STRUCT: - - protocol_id: - TYPENAME: ProtocolId - - request_id: U32 - - priority: U8 - - raw_request: BYTES -RpcResponse: - STRUCT: - - request_id: U32 - - priority: U8 - - raw_response: BYTES From 224dd2836cfb03f0e254b24031db1f2fe245dd7e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 24 Apr 2024 16:25:55 +0300 Subject: [PATCH 062/147] aptos-network: fix up noise imports in tests --- network/framework/src/noise/stream.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/network/framework/src/noise/stream.rs b/network/framework/src/noise/stream.rs index b81f45de626c0..496667c2cdd2f 100644 --- a/network/framework/src/noise/stream.rs +++ b/network/framework/src/noise/stream.rs @@ -525,6 +525,7 @@ where mod test { use super::*; use crate::{ + noise::crypto::{NoiseSession, MAX_SIZE_NOISE_MSG}, noise::{AntiReplayTimestamps, HandshakeAuthMode, NoiseUpgrader}, testutils::fake_socket::{ReadOnlyTestSocket, ReadWriteTestSocket}, }; @@ -627,7 +628,7 @@ mod test { fake_socket.set_trailing(); // setup a NoiseStream with a dummy state - let noise_session = noise::NoiseSession::new_for_testing(); + let noise_session = NoiseSession::new_for_testing(); let mut peer = NoiseStream::new(fake_socket, noise_session); // make sure we error and we don't continuously read @@ -670,11 +671,11 @@ mod test { let ((client, _client_public), (server, server_public)) = build_peers(); let (mut client, mut server) = perform_handshake(client, server_public, server); - let buf_send = [1; noise::MAX_SIZE_NOISE_MSG]; + let buf_send = [1; MAX_SIZE_NOISE_MSG]; block_on(client.write_all(&buf_send)).unwrap(); block_on(client.flush()).unwrap(); - let mut buf_receive = [0; noise::MAX_SIZE_NOISE_MSG]; + let mut buf_receive = [0; MAX_SIZE_NOISE_MSG]; block_on(server.read_exact(&mut buf_receive)).unwrap(); assert_eq!(&buf_receive[..], &buf_send[..]); } From 8b252989ac866b956d8b7f85253733e62b150acf Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 29 Apr 2024 14:57:58 -0700 Subject: [PATCH 063/147] experimiental/runtimes: remove tokio dep --- Cargo.lock | 1 - experimental/runtimes/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dce1af3b5f30f..c25a7357109bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1313,7 +1313,6 @@ dependencies = [ "num_cpus", "once_cell", "rayon", - "tokio", ] [[package]] diff --git a/experimental/runtimes/Cargo.toml b/experimental/runtimes/Cargo.toml index cbaeb04ecb0f2..662a6b1704209 100644 --- a/experimental/runtimes/Cargo.toml +++ b/experimental/runtimes/Cargo.toml @@ -19,4 +19,3 @@ libc = { workspace = true } num_cpus = { workspace = true } once_cell = { workspace = true } rayon = { workspace = true } -tokio = { workspace = true } From 8319a2b3647452dabe624ac5b7e3de650b8bad45 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 29 Apr 2024 13:55:46 -0700 Subject: [PATCH 064/147] aptos-dkg: relax dep on aptos-experimental-runtimes It's only used in test code, move it to dev-dependencies --- crates/aptos-dkg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aptos-dkg/Cargo.toml b/crates/aptos-dkg/Cargo.toml index b123d4ad77a51..4e62513ddc790 100644 --- a/crates/aptos-dkg/Cargo.toml +++ b/crates/aptos-dkg/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" anyhow = { workspace = true } aptos-crypto = { workspace = true } aptos-crypto-derive = { workspace = true } -aptos-runtimes = { workspace = true } bcs = { workspace = true } bellman = { workspace = true } blst = { workspace = true } @@ -33,6 +32,7 @@ sha3 = { workspace = true } static_assertions = { workspace = true } [dev-dependencies] +aptos-runtimes = { workspace = true } num_cpus = { workspace = true } [features] From bd3e72f861a2353c247d3cebb83e0782a99ba898 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 23 Apr 2024 10:03:14 +0300 Subject: [PATCH 065/147] aptos-logger: cut out async logging --- Cargo.lock | 3 - crates/aptos-logger/Cargo.toml | 3 - crates/aptos-logger/src/aptos_logger.rs | 308 ++---------------- crates/aptos-logger/src/counters.rs | 27 -- crates/aptos-logger/src/lib.rs | 5 +- .../aptos-logger/src/telemetry_log_writer.rs | 59 ---- crates/aptos-logger/tests/logger.rs | 5 +- .../tests/logger_custom_format.rs | 1 - .../tests/tracing_translation_tests.rs | 1 - 9 files changed, 21 insertions(+), 391 deletions(-) delete mode 100644 crates/aptos-logger/src/telemetry_log_writer.rs diff --git a/Cargo.lock b/Cargo.lock index d604ef464dce0..846865660b12a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2310,12 +2310,10 @@ version = "0.1.0" dependencies = [ "aptos-infallible", "aptos-log-derive", - "aptos-node-identity", "backtrace", "chrono", "console-subscriber", "erased-serde", - "futures", "hostname", "once_cell", "pretty_assertions", @@ -2324,7 +2322,6 @@ dependencies = [ "serde_json", "strum 0.24.1", "strum_macros 0.24.3", - "tokio", "tracing", "tracing-subscriber 0.3.18", ] diff --git a/crates/aptos-logger/Cargo.toml b/crates/aptos-logger/Cargo.toml index 61bc086fcea05..e9554494cb9b2 100644 --- a/crates/aptos-logger/Cargo.toml +++ b/crates/aptos-logger/Cargo.toml @@ -17,12 +17,10 @@ rust-version = { workspace = true } [dependencies] aptos-infallible = { workspace = true } aptos-log-derive = { workspace = true } -aptos-node-identity = { workspace = true } backtrace = { workspace = true } chrono = { workspace = true } console-subscriber = { workspace = true, optional = true } erased-serde = { workspace = true } -futures = { workspace = true } hostname = { workspace = true } once_cell = { workspace = true } prometheus = { workspace = true } @@ -30,7 +28,6 @@ serde = { workspace = true } serde_json = { workspace = true } strum = { workspace = true } strum_macros = { workspace = true } -tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/crates/aptos-logger/src/aptos_logger.rs b/crates/aptos-logger/src/aptos_logger.rs index c327fd82579b6..4e6a5bbcef899 100644 --- a/crates/aptos-logger/src/aptos_logger.rs +++ b/crates/aptos-logger/src/aptos_logger.rs @@ -6,19 +6,13 @@ //! (e.g. Logstash) use crate::{ - counters::{ - PROCESSED_STRUCT_LOG_COUNT, STRUCT_LOG_PARSE_ERROR_COUNT, STRUCT_LOG_QUEUE_ERROR_COUNT, - }, + counters::{STRUCT_LOG_PARSE_ERROR_COUNT, STRUCT_LOG_QUEUE_ERROR_COUNT}, logger::Logger, - sample, - sample::SampleRate, - telemetry_log_writer::{TelemetryLog, TelemetryLogWriter}, - Event, Filter, Key, Level, LevelFilter, Metadata, + Event, Filter, Key, Level, Metadata, }; use aptos_infallible::RwLock; use backtrace::Backtrace; use chrono::{SecondsFormat, Utc}; -use futures::channel; use once_cell::sync::Lazy; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::{ @@ -29,11 +23,9 @@ use std::{ ops::{Deref, DerefMut}, str::FromStr, sync::{self, Arc}, - thread, time::Duration, }; use strum_macros::EnumString; -use tokio::time; const RUST_LOG: &str = "RUST_LOG"; pub const RUST_LOG_TELEMETRY: &str = "RUST_LOG_TELEMETRY"; @@ -41,8 +33,6 @@ const RUST_LOG_FORMAT: &str = "RUST_LOG_FORMAT"; /// Default size of log write channel, if the channel is full, logs will be dropped pub const CHANNEL_SIZE: usize = 10000; const FLUSH_TIMEOUT: Duration = Duration::from_secs(5); -const FILTER_REFRESH_INTERVAL: Duration = - Duration::from_secs(5 /* minutes */ * 60 /* seconds */); /// Note: To disable length limits, set `RUST_LOG_FIELD_MAX_LEN` to -1. const RUST_LOG_FIELD_MAX_LEN_ENV_VAR: &str = "RUST_LOG_FIELD_MAX_LEN"; @@ -203,8 +193,8 @@ impl LogEntry { let hostname = HOSTNAME.as_deref(); let namespace = NAMESPACE.as_deref(); - let peer_id = aptos_node_identity::peer_id_as_str(); - let chain_id = aptos_node_identity::chain_id().map(|chain_id| chain_id.id()); + let peer_id = None; + let chain_id = None; let backtrace = if enable_backtrace && matches!(metadata.level(), Level::Error) { let mut backtrace = Backtrace::new(); @@ -287,9 +277,6 @@ pub struct AptosDataBuilder { remote_level: Level, telemetry_level: Level, printer: Option>, - remote_log_tx: Option>, - is_async: bool, - enable_telemetry_flush: bool, custom_format: Option Result>, } @@ -304,9 +291,6 @@ impl AptosDataBuilder { remote_level: Level::Info, telemetry_level: Level::Warn, printer: Some(Box::new(StdoutWriter::new())), - remote_log_tx: None, - is_async: false, - enable_telemetry_flush: true, custom_format: None, } } @@ -346,24 +330,6 @@ impl AptosDataBuilder { self } - pub fn remote_log_tx( - &mut self, - remote_log_tx: channel::mpsc::Sender, - ) -> &mut Self { - self.remote_log_tx = Some(remote_log_tx); - self - } - - pub fn is_async(&mut self, is_async: bool) -> &mut Self { - self.is_async = is_async; - self - } - - pub fn enable_telemetry_flush(&mut self, enable_telemetry_flush: bool) -> &mut Self { - self.enable_telemetry_flush = enable_telemetry_flush; - self - } - pub fn custom_format( &mut self, format: fn(&LogEntry) -> Result, @@ -376,7 +342,8 @@ impl AptosDataBuilder { self.build(); } - fn build_filter(&self) -> FilterTuple { + fn build_filter(&self) -> Filter { + // Keep this block same as upstream, which also builds a telemetry filter let local_filter = { let mut filter_builder = Filter::builder(); @@ -388,26 +355,8 @@ impl AptosDataBuilder { filter_builder.build() }; - let telemetry_filter = { - let mut filter_builder = Filter::builder(); - - if self.is_async && self.remote_log_tx.is_some() { - if env::var(RUST_LOG_TELEMETRY).is_ok() { - filter_builder.with_env(RUST_LOG_TELEMETRY); - } else { - filter_builder.filter_level(self.telemetry_level.into()); - } - } else { - filter_builder.filter_level(LevelFilter::Off); - } - - filter_builder.build() - }; - FilterTuple { - local_filter, - telemetry_filter, - } + local_filter } fn build_logger(&mut self) -> Arc { @@ -421,40 +370,13 @@ impl AptosDataBuilder { } } - if self.is_async { - let (sender, receiver) = sync::mpsc::sync_channel(self.channel_size); - let mut remote_tx = None; - if let Some(tx) = &self.remote_log_tx { - remote_tx = Some(tx.clone()); - } - - let logger = Arc::new(AptosData { - enable_backtrace: self.enable_backtrace, - sender: Some(sender), - printer: None, - filter: RwLock::new(filter), - enable_telemetry_flush: self.enable_telemetry_flush, - formatter: self.custom_format.take().unwrap_or(text_format), - }); - let service = LoggerService { - receiver, - printer: self.printer.take(), - facade: logger.clone(), - remote_tx, - }; - - thread::spawn(move || service.run()); - logger - } else { - Arc::new(AptosData { - enable_backtrace: self.enable_backtrace, - sender: None, - printer: self.printer.take(), - filter: RwLock::new(filter), - enable_telemetry_flush: self.enable_telemetry_flush, - formatter: self.custom_format.take().unwrap_or(text_format), - }) - } + Arc::new(AptosData { + enable_backtrace: self.enable_backtrace, + sender: None, + printer: self.printer.take(), + filter: RwLock::new(filter), + formatter: self.custom_format.take().unwrap_or(text_format), + }) } pub fn build(&mut self) -> Arc { @@ -471,26 +393,11 @@ impl AptosDataBuilder { } } -/// A combination of `Filter`s to control where logs are written -pub struct FilterTuple { - /// The local printer `Filter` to control what is logged in text output - local_filter: Filter, - /// The logging `Filter` to control what is sent to telemetry service - telemetry_filter: Filter, -} - -impl FilterTuple { - fn enabled(&self, metadata: &Metadata) -> bool { - self.local_filter.enabled(metadata) || self.telemetry_filter.enabled(metadata) - } -} - pub struct AptosData { enable_backtrace: bool, sender: Option>, printer: Option>, - filter: RwLock, - enable_telemetry_flush: bool, + filter: RwLock, pub(crate) formatter: fn(&LogEntry) -> Result, } @@ -508,7 +415,6 @@ impl AptosData { // Create the Aptos Data Builder let mut builder = Self::builder(); builder - .is_async(false) .enable_backtrace() .printer(Box::new(StdoutWriter::new())); @@ -520,16 +426,8 @@ impl AptosData { builder.build(); } - pub fn set_filter(&self, filter_tuple: FilterTuple) { - *self.filter.write() = filter_tuple; - } - pub fn set_local_filter(&self, filter: Filter) { - self.filter.write().local_filter = filter; - } - - pub fn set_telemetry_filter(&self, filter: Filter) { - self.filter.write().telemetry_filter = filter; + *self.filter.write() = filter; } fn send_entry(&self, entry: LogEntry) { @@ -587,79 +485,6 @@ enum LoggerServiceEvent { Flush(sync::mpsc::SyncSender<()>), } -/// A service for running a log listener, that will continually export logs through a local printer -/// or to a `AptosData` for external logging. -struct LoggerService { - receiver: sync::mpsc::Receiver, - printer: Option>, - facade: Arc, - remote_tx: Option>, -} - -impl LoggerService { - pub fn run(mut self) { - let mut telemetry_writer = self.remote_tx.take().map(TelemetryLogWriter::new); - - for event in &self.receiver { - match event { - LoggerServiceEvent::LogEntry(entry) => { - PROCESSED_STRUCT_LOG_COUNT.inc(); - - if let Some(printer) = &mut self.printer { - if self - .facade - .filter - .read() - .local_filter - .enabled(&entry.metadata) - { - let s = (self.facade.formatter)(&entry).expect("Unable to format"); - printer.write_buferred(s); - } - } - - if let Some(writer) = &mut telemetry_writer { - if self - .facade - .filter - .read() - .telemetry_filter - .enabled(&entry.metadata) - { - let s = (self.facade.formatter)(&entry).expect("Unable to format"); - let _ = writer.write(s); - } - } - }, - LoggerServiceEvent::Flush(sender) => { - // Flush is only done on TelemetryLogWriter - if let Some(writer) = &mut telemetry_writer { - if self.facade.enable_telemetry_flush { - match writer.flush() { - Ok(rx) => { - if let Err(err) = rx.recv_timeout(FLUSH_TIMEOUT) { - sample!( - SampleRate::Duration(Duration::from_secs(60)), - eprintln!("Timed out flushing telemetry: {}", err) - ); - } - }, - Err(err) => { - sample!( - SampleRate::Duration(Duration::from_secs(60)), - eprintln!("Failed to flush telemetry: {}", err) - ); - }, - } - } - } - let _ = sender.send(()); - }, - } - } - } -} - /// A trait encapsulating the operations required for writing logs. pub trait Writer: Send + Sync { /// Write the log. @@ -768,47 +593,14 @@ fn json_format(entry: &LogEntry) -> Result { } } -/// Periodically rebuilds the filter and replaces the current logger filter. -/// This is useful for dynamically changing log levels at runtime via existing -/// environment variables such as `RUST_LOG_TELEMETRY`. -pub struct LoggerFilterUpdater { - logger: Arc, - logger_builder: AptosDataBuilder, -} - -impl LoggerFilterUpdater { - pub fn new(logger: Arc, logger_builder: AptosDataBuilder) -> Self { - Self { - logger, - logger_builder, - } - } - - pub async fn run(self) { - let mut interval = time::interval(FILTER_REFRESH_INTERVAL); - loop { - interval.tick().await; - - self.update_filter(); - } - } - - fn update_filter(&self) { - // TODO: check for change to env var before rebuilding filter. - let filter = self.logger_builder.build_filter(); - self.logger.set_filter(filter); - } -} - #[cfg(test)] mod tests { - use super::{AptosData, LogEntry}; + use super::LogEntry; use crate::{ - aptos_logger::{json_format, TruncatedLogString, RUST_LOG_TELEMETRY}, + aptos_logger::{json_format, TruncatedLogString}, debug, error, info, logger::Logger, - trace, warn, AptosDataBuilder, Event, Key, KeyValue, Level, LoggerFilterUpdater, Metadata, - Schema, Value, Visitor, + trace, warn, Event, Key, KeyValue, Level, Metadata, Schema, Value, Visitor, }; use chrono::{DateTime, Utc}; #[cfg(test)] @@ -1025,68 +817,6 @@ mod tests { } } - fn new_async_logger() -> (AptosDataBuilder, Arc) { - let mut logger_builder = AptosDataBuilder::new(); - let (remote_log_tx, _) = futures::channel::mpsc::channel(10); - let logger = logger_builder - .remote_log_tx(remote_log_tx) - .is_async(true) - .build_logger(); - (logger_builder, logger) - } - - #[test] - fn test_logger_filter_updater() { - let (logger_builder, logger) = new_async_logger(); - let debug_metadata = &Metadata::new(Level::Debug, "target", "module_path", "source_path"); - - assert!(!logger - .filter - .read() - .telemetry_filter - .enabled(debug_metadata)); - - std::env::set_var(RUST_LOG_TELEMETRY, "debug"); - - let updater = LoggerFilterUpdater::new(logger.clone(), logger_builder); - updater.update_filter(); - - assert!(logger - .filter - .read() - .telemetry_filter - .enabled(debug_metadata)); - - std::env::set_var(RUST_LOG_TELEMETRY, "debug;hyper=off"); // log values should be separated by commas not semicolons. - updater.update_filter(); - - assert!(!logger - .filter - .read() - .telemetry_filter - .enabled(debug_metadata)); - - std::env::set_var(RUST_LOG_TELEMETRY, "debug,hyper=off"); // log values should be separated by commas not semicolons. - updater.update_filter(); - - assert!(logger - .filter - .read() - .telemetry_filter - .enabled(debug_metadata)); - - assert!(!logger - .filter - .read() - .telemetry_filter - .enabled(&Metadata::new( - Level::Error, - "target", - "hyper", - "source_path" - ))); - } - #[test] fn test_log_event_truncation() { let log_entry = LogEntry::new( diff --git a/crates/aptos-logger/src/counters.rs b/crates/aptos-logger/src/counters.rs index ce08a960fad1d..eea788cf65c8f 100644 --- a/crates/aptos-logger/src/counters.rs +++ b/crates/aptos-logger/src/counters.rs @@ -11,15 +11,6 @@ pub static STRUCT_LOG_COUNT: Lazy = Lazy::new(|| { register_int_counter!("aptos_struct_log_count", "Count of the struct logs.").unwrap() }); -/// Count of struct logs processed, but not necessarily sent -pub static PROCESSED_STRUCT_LOG_COUNT: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_struct_log_processed_count", - "Count of the struct logs received by the sender." - ) - .unwrap() -}); - /// Metric for when we fail to log during sending to the queue pub static STRUCT_LOG_QUEUE_ERROR_COUNT: Lazy = Lazy::new(|| { register_int_counter!( @@ -36,21 +27,3 @@ pub static STRUCT_LOG_PARSE_ERROR_COUNT: Lazy = Lazy::new(|| { ) .unwrap() }); - -/// Counter for failed log ingest writes (see also: aptos-telemetry for sender metrics) -pub static APTOS_LOG_INGEST_WRITER_FULL: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_log_ingest_writer_full", - "Number of log ingest writes that failed due to channel full" - ) - .unwrap() -}); - -/// Counter for failed log ingest writes (see also: aptos-telemetry for sender metrics) -pub static APTOS_LOG_INGEST_WRITER_DISCONNECTED: Lazy = Lazy::new(|| { - register_int_counter!( - "aptos_log_ingest_writer_disconnected", - "Number of log ingest writes that failed due to channel disconnected" - ) - .unwrap() -}); diff --git a/crates/aptos-logger/src/lib.rs b/crates/aptos-logger/src/lib.rs index ee16f140b269b..ee05068aed597 100644 --- a/crates/aptos-logger/src/lib.rs +++ b/crates/aptos-logger/src/lib.rs @@ -152,14 +152,11 @@ mod logger; mod macros; mod metadata; pub mod sample; -pub mod telemetry_log_writer; pub mod tracing_adapter; mod security; -pub use crate::aptos_logger::{ - AptosData as Logger, AptosDataBuilder, LoggerFilterUpdater, Writer, CHANNEL_SIZE, -}; +pub use crate::aptos_logger::{AptosData as Logger, AptosDataBuilder, Writer, CHANNEL_SIZE}; pub use aptos_log_derive::Schema; pub use event::Event; pub use filter::{Filter, LevelFilter}; diff --git a/crates/aptos-logger/src/telemetry_log_writer.rs b/crates/aptos-logger/src/telemetry_log_writer.rs deleted file mode 100644 index e73503b47851e..0000000000000 --- a/crates/aptos-logger/src/telemetry_log_writer.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::counters::{APTOS_LOG_INGEST_WRITER_DISCONNECTED, APTOS_LOG_INGEST_WRITER_FULL}; -use futures::channel; -use std::{ - io::{Error, ErrorKind}, - sync, -}; - -#[derive(Debug)] -pub enum TelemetryLog { - Log(String), - Flush(sync::mpsc::SyncSender<()>), -} - -#[derive(Debug)] -pub(crate) struct TelemetryLogWriter { - tx: channel::mpsc::Sender, -} - -impl TelemetryLogWriter { - pub fn new(tx: channel::mpsc::Sender) -> Self { - Self { tx } - } -} - -impl TelemetryLogWriter { - pub fn write(&mut self, log: String) -> std::io::Result { - let len = log.len(); - match self.tx.try_send(TelemetryLog::Log(log)) { - Ok(_) => Ok(len), - Err(err) => { - if err.is_full() { - APTOS_LOG_INGEST_WRITER_FULL.inc_by(len as u64); - Err(Error::new(ErrorKind::WouldBlock, "Channel full")) - } else { - APTOS_LOG_INGEST_WRITER_DISCONNECTED.inc_by(len as u64); - Err(Error::new(ErrorKind::ConnectionRefused, "Disconnected")) - } - }, - } - } - - #[allow(dead_code)] - pub fn flush(&mut self) -> std::io::Result> { - let (tx, rx) = sync::mpsc::sync_channel(1); - match self.tx.try_send(TelemetryLog::Flush(tx)) { - Ok(_) => Ok(rx), - Err(err) => { - if err.is_full() { - Err(Error::new(ErrorKind::WouldBlock, "Channel full")) - } else { - Err(Error::new(ErrorKind::ConnectionRefused, "Disconnected")) - } - }, - } - } -} diff --git a/crates/aptos-logger/tests/logger.rs b/crates/aptos-logger/tests/logger.rs index d0d8cbf5e5e65..c27f54e70030a 100644 --- a/crates/aptos-logger/tests/logger.rs +++ b/crates/aptos-logger/tests/logger.rs @@ -25,10 +25,7 @@ impl Writer for VecWriter { fn verify_end_to_end() { let writer = VecWriter::default(); let logs = writer.logs.clone(); - AptosData::builder() - .is_async(false) - .printer(Box::new(writer)) - .build(); + AptosData::builder().printer(Box::new(writer)).build(); assert_eq!(logs.read().len(), 0); info!("Hello"); diff --git a/crates/aptos-logger/tests/logger_custom_format.rs b/crates/aptos-logger/tests/logger_custom_format.rs index 279e36138f171..8f4f7649a8289 100644 --- a/crates/aptos-logger/tests/logger_custom_format.rs +++ b/crates/aptos-logger/tests/logger_custom_format.rs @@ -26,7 +26,6 @@ fn test_custom_formatter() { let writer = VecWriter::default(); let logs = writer.logs.clone(); AptosData::builder() - .is_async(false) .printer(Box::new(writer)) .custom_format(|entry| { use std::fmt::Write; diff --git a/crates/aptos-logger/tests/tracing_translation_tests.rs b/crates/aptos-logger/tests/tracing_translation_tests.rs index da277b0efb31c..14a1e6cc81501 100644 --- a/crates/aptos-logger/tests/tracing_translation_tests.rs +++ b/crates/aptos-logger/tests/tracing_translation_tests.rs @@ -41,7 +41,6 @@ fn verify_tracing_kvs() { let writer = VecWriter::default(); let logs = writer.logs.clone(); AptosData::builder() - .is_async(false) .tokio_console_port(None) .printer(Box::new(writer.write_to_stderr(false))) .build(); From 27c8c965c119cdecad830866ff4a5c3fafaf8b89 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sun, 5 May 2024 17:35:35 -0700 Subject: [PATCH 066/147] add reversion to Writer trait, enable epoch reversion --- storage/aptosdb/src/db/aptosdb_test.rs | 25 ++- .../aptosdb/src/db/include/aptosdb_writer.rs | 206 +++++++++--------- storage/storage-interface/src/lib.rs | 11 + types/src/block_info.rs | 4 + 4 files changed, 135 insertions(+), 111 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 761e7dd6be61f..669dcde57ef12 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -25,7 +25,7 @@ use aptos_crypto::{ }; use aptos_executor_types::StateComputeResult; use aptos_proptest_helpers::ValueGenerator; -use aptos_storage_interface::{DbReader, ExecutedTrees, Order}; +use aptos_storage_interface::{DbReader, DbReaderWriter, DbWriter, ExecutedTrees, Order}; use aptos_temppath::TempPath; use aptos_types::{ chain_id::ChainId, @@ -124,13 +124,15 @@ fn test_pruner_config() { assert_eq!(state_merkle_pruner.is_pruner_enabled(), enable); assert_eq!(state_merkle_pruner.get_prune_window(), 20); - let ledger_pruner = - LedgerPrunerManager::new(Arc::clone(&aptos_db.ledger_db), LedgerPrunerConfig { + let ledger_pruner = LedgerPrunerManager::new( + Arc::clone(&aptos_db.ledger_db), + LedgerPrunerConfig { enable, prune_window: 100, batch_size: 1, user_pruning_window_offset: 0, - }); + }, + ); assert_eq!(ledger_pruner.is_pruner_enabled(), enable); assert_eq!(ledger_pruner.get_prune_window(), 100); } @@ -300,6 +302,7 @@ fn test_revert_nth_commit() { aptos_logger::Logger::new().init(); let tmp_dir = TempPath::new(); let db = AptosDB::new_for_test(&tmp_dir); + let mut cur_ver: Version = 0; let mut in_memory_state = db.buffered_state().lock().current_state().clone(); let _ancestor = in_memory_state.base.clone(); @@ -320,6 +323,7 @@ fn test_revert_nth_commit() { let mut blockheight = 0; for (txns_to_commit, ledger_info_with_sigs) in &blocks { + println!("Blockheight: {}", blockheight); let first_version = cur_ver; update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); db.save_transactions_for_test( @@ -332,11 +336,14 @@ fn test_revert_nth_commit() { ) .unwrap(); - committed_blocks.insert(blockheight, Commit { - hash: ledger_info_with_sigs.commit_info().executed_state_id(), - info: ledger_info_with_sigs.clone(), - first_version, - }); + committed_blocks.insert( + blockheight, + Commit { + hash: ledger_info_with_sigs.commit_info().executed_state_id(), + info: ledger_info_with_sigs.clone(), + first_version, + }, + ); commit_versions.push(cur_ver); cur_ver += txns_to_commit.len() as u64; blockheight += 1; diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 1d0134cda70cf..6a823b93e5d46 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -199,6 +199,110 @@ impl DbWriter for AptosDB { Ok(()) }) } + + /// Revert a commit. + fn revert_commit( + &self, + version_to_revert: Version, + latest_version: Version, + new_root_hash: HashValue, + ledger_info_with_sigs: LedgerInfoWithSignatures, + ) -> Result<()> { + let _timer = OTHER_TIMERS_SECONDS + .with_label_values(&["revert_commit"]) + .start_timer(); + // Revert the ledger commit progress + let ledger_batch = SchemaBatch::new(); + ledger_batch.put::( + &DbMetadataKey::LedgerCommitProgress, + &DbMetadataValue::Version(version_to_revert - 1), + )?; + self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + + // Revert the overall commit progress + let ledger_batch = SchemaBatch::new(); + ledger_batch.put::( + &DbMetadataKey::OverallCommitProgress, + &DbMetadataValue::Version(version_to_revert - 1), + )?; + self.ledger_db.metadata_db().write_schemas(ledger_batch)?; + + let temp_position = Position::from_postorder_index(latest_version)?; + + // Revert the transaction accumulator + let batch = SchemaBatch::new(); + self.ledger_db + .transaction_accumulator_db() + .revert_transaction_accumulator(version_to_revert - 1, &batch, temp_position)?; + self.ledger_db + .transaction_accumulator_db() + .write_schemas(batch)?; + + // Revert the transaction info + let batch = SchemaBatch::new(); + self.ledger_db + .transaction_info_db() + .delete_transaction_info(version_to_revert, &batch)?; + let batch = SchemaBatch::new(); + self.ledger_db.transaction_info_db().write_schemas(batch)?; + + // Revert the events + let batch = SchemaBatch::new(); + self.ledger_db + .event_db() + .delete_events(version_to_revert, &batch)?; + self.ledger_db.event_db().write_schemas(batch)?; + + // Revert the transaction auxiliary data + let batch = SchemaBatch::new(); + TransactionAuxiliaryDataDb::prune(version_to_revert - 1, latest_version, &batch)?; + let batch = SchemaBatch::new(); + self.ledger_db + .transaction_auxiliary_data_db() + .write_schemas(batch)?; + + // Revert the write set + let batch = SchemaBatch::new(); + WriteSetDb::prune(version_to_revert - 1, latest_version, &batch)?; + self.ledger_db.transaction_db().prune_transactions( + version_to_revert - 1, + latest_version, + &batch, + )?; + + // Revert the state kv and ledger metadata + self.state_store + .state_kv_db + .revert_state_kv_and_ledger_metadata(version_to_revert)?; + + // Get the epoch of the version_to_revert + let target_epoch = self + .ledger_db + .metadata_db() + .get_epoch(version_to_revert)?; + + // Set the epoch to the target epoch + ledger_info_with_sigs + .ledger_info() + .commit_info() + .to_owned() + .set_epoch(target_epoch); + + //Set the Version + ledger_info_with_sigs + .ledger_info() + .to_owned() + .set_version(version_to_revert - 1); + + // Update the latest ledger info if provided + self.commit_ledger_info( + version_to_revert - 1, + new_root_hash, + Some(&ledger_info_with_sigs), + )?; + + Ok(()) + } } impl AptosDB { @@ -563,17 +667,6 @@ impl AptosDB { new_root_hash, expected_root_hash, ); - let current_epoch = self - .ledger_db - .metadata_db() - .get_latest_ledger_info_option() - .map_or(0, |li| li.ledger_info().next_block_epoch()); - ensure!( - x.ledger_info().epoch() == current_epoch, - "Gap in epoch history. Trying to put in LedgerInfo in epoch: {}, current epoch: {}", - x.ledger_info().epoch(), - current_epoch, - ); self.ledger_db .metadata_db() @@ -632,95 +725,4 @@ impl AptosDB { Ok(()) } - - /// Revert a commit. - pub fn revert_commit( - &self, - version_to_revert: Version, - latest_version: Version, - new_root_hash: HashValue, - ledger_info_with_sigs: LedgerInfoWithSignatures, - ) -> Result<()> { - // The state and version after commit will always be one less than the version to revert - let _timer = OTHER_TIMERS_SECONDS - .with_label_values(&["revert_commit"]) - .start_timer(); - // Revert the ledger commit progress - let ledger_batch = SchemaBatch::new(); - ledger_batch.put::( - &DbMetadataKey::LedgerCommitProgress, - &DbMetadataValue::Version(version_to_revert - 1), - )?; - self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - - // Revert the overall commit progress - let ledger_batch = SchemaBatch::new(); - ledger_batch.put::( - &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(version_to_revert - 1), - )?; - self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - - let temp_position = Position::from_postorder_index(latest_version)?; - - // Revert the transaction accumulator - let batch = SchemaBatch::new(); - self.ledger_db - .transaction_accumulator_db() - .revert_transaction_accumulator(version_to_revert - 1, &batch, temp_position)?; - self.ledger_db - .transaction_accumulator_db() - .write_schemas(batch)?; - - // Revert the transaction info - let batch = SchemaBatch::new(); - self.ledger_db - .transaction_info_db() - .delete_transaction_info(version_to_revert, &batch)?; - let batch = SchemaBatch::new(); - self.ledger_db.transaction_info_db().write_schemas(batch)?; - - // Revert the events - let batch = SchemaBatch::new(); - self.ledger_db - .event_db() - .delete_events(version_to_revert, &batch)?; - self.ledger_db.event_db().write_schemas(batch)?; - - // Revert the transaction auxiliary data - let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(version_to_revert - 1, latest_version, &batch)?; - let batch = SchemaBatch::new(); - self.ledger_db - .transaction_auxiliary_data_db() - .write_schemas(batch)?; - - // Revert the write set - let batch = SchemaBatch::new(); - WriteSetDb::prune(version_to_revert - 1, latest_version, &batch)?; - self.ledger_db.transaction_db().prune_transactions( - version_to_revert - 1, - latest_version, - &batch, - )?; - - // Revert the state kv and ledger metadata - self.state_store - .state_kv_db - .revert_state_kv_and_ledger_metadata(version_to_revert)?; - - //Update the version of `ledger_info_with_sigs` - let _ledger_info = ledger_info_with_sigs - .ledger_info() - .to_owned() - .set_version(version_to_revert - 1); - - // Update the latest ledger info if provided - self.commit_ledger_info( - version_to_revert - 1, - new_root_hash, - Some(&ledger_info_with_sigs), - )?; - Ok(()) - } } diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index 4ec4442659a96..2397415557589 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -572,6 +572,17 @@ pub trait DbWriter: Send + Sync { ) -> Result<()> { unimplemented!() } + + /// Revert a commit. + fn revert_commit( + &self, + version_to_revert: Version, + latest_version: Version, + new_root_hash: HashValue, + ledger_info_with_sigs: LedgerInfoWithSignatures, + ) -> Result<()> { + unimplemented!() + } } #[derive(Clone)] diff --git a/types/src/block_info.rs b/types/src/block_info.rs index b0a8a3a612b9c..010301a3c447e 100644 --- a/types/src/block_info.rs +++ b/types/src/block_info.rs @@ -155,6 +155,10 @@ impl BlockInfo { self.epoch } + pub fn set_epoch(&mut self, epoch: u64) { + self.epoch = epoch; + } + pub fn executed_state_id(&self) -> HashValue { self.executed_state_id } From 7d807dcbd8796e760d272db2fd7903e6d8449730 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 6 May 2024 10:32:25 -0700 Subject: [PATCH 067/147] add new commit reversion method --- .../aptosdb/src/db/include/aptosdb_writer.rs | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 6a823b93e5d46..5eefda4135977 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -218,7 +218,7 @@ impl DbWriter for AptosDB { &DbMetadataValue::Version(version_to_revert - 1), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; - + // Revert the overall commit progress let ledger_batch = SchemaBatch::new(); ledger_batch.put::( @@ -276,11 +276,8 @@ impl DbWriter for AptosDB { .revert_state_kv_and_ledger_metadata(version_to_revert)?; // Get the epoch of the version_to_revert - let target_epoch = self - .ledger_db - .metadata_db() - .get_epoch(version_to_revert)?; - + let target_epoch = self.ledger_db.metadata_db().get_epoch(version_to_revert)?; + // Set the epoch to the target epoch ledger_info_with_sigs .ledger_info() @@ -288,14 +285,14 @@ impl DbWriter for AptosDB { .to_owned() .set_epoch(target_epoch); - //Set the Version + //Set the Version ledger_info_with_sigs .ledger_info() .to_owned() .set_version(version_to_revert - 1); // Update the latest ledger info if provided - self.commit_ledger_info( + self.commit_reversion_ledger_info( version_to_revert - 1, new_root_hash, Some(&ledger_info_with_sigs), @@ -658,6 +655,39 @@ impl AptosDB { let ledger_batch = SchemaBatch::new(); + // If expected ledger info is provided, verify result root hash and save the ledger info. + if let Some(x) = ledger_info_with_sigs { + let expected_root_hash = x.ledger_info().transaction_accumulator_hash(); + ensure!( + new_root_hash == expected_root_hash, + "Root hash calculated doesn't match expected. {:?} vs {:?}", + new_root_hash, + expected_root_hash, + ); + self.ledger_db + .metadata_db() + .put_ledger_info(x, &ledger_batch)?; + } + + ledger_batch.put::( + &DbMetadataKey::OverallCommitProgress, + &DbMetadataValue::Version(last_version), + )?; + self.ledger_db.metadata_db().write_schemas(ledger_batch) + } + + fn commit_reversion_ledger_info( + &self, + last_version: Version, + new_root_hash: HashValue, + ledger_info_with_sigs: Option<&LedgerInfoWithSignatures>, + ) -> Result<()> { + let _timer = OTHER_TIMERS_SECONDS + .with_label_values(&["commit_ledger_info"]) + .start_timer(); + + let ledger_batch = SchemaBatch::new(); + // If expected ledger info is provided, verify result root hash and save the ledger info. if let Some(x) = ledger_info_with_sigs { let expected_root_hash = x.ledger_info().transaction_accumulator_hash(); From 02d323642073d8175f6025336756f3ecced72cb9 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 17 May 2024 23:50:58 -0400 Subject: [PATCH 068/147] chore: remove logging. --- Cargo.lock | 1949 +++++++++++++++++++-------------------- api/src/context.rs | 1 - api/src/transactions.rs | 3 - 3 files changed, 947 insertions(+), 1006 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 507c34afb516f..413af6f3defdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ name = "abstract-domain-derive" version = "0.1.0" dependencies = [ "move-stackless-bytecode", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", @@ -81,23 +81,23 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.8" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.11", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.11", "once_cell", "version_check", "zerocopy", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -120,9 +120,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-tzdata" @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "aptos-abstract-gas-usage" @@ -425,7 +425,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "bytes", - "clap 4.5.4", + "clap 4.4.12", "csv", "futures", "itertools 0.10.5", @@ -543,7 +543,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "criterion", "dashmap", "itertools 0.10.5", @@ -592,7 +592,7 @@ name = "aptos-cargo-cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "clap-verbosity-flag", "determinator", "env_logger", @@ -780,8 +780,8 @@ name = "aptos-crypto-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -881,7 +881,7 @@ dependencies = [ "bcs 0.1.4", "byteorder", "claims", - "clap 4.5.4", + "clap 4.4.12", "dashmap", "either", "itertools 0.10.5", @@ -953,7 +953,7 @@ dependencies = [ "aptos-vm", "async-trait", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "itertools 0.10.5", "owo-colors", "tokio", @@ -1047,8 +1047,8 @@ name = "aptos-enum-conversion-derive" version = "0.0.3" dependencies = [ "anyhow", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", "trybuild", ] @@ -1166,7 +1166,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "chrono", - "clap 4.5.4", + "clap 4.4.12", "derivative", "indicatif", "itertools 0.10.5", @@ -1203,7 +1203,7 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "crossbeam-channel", "ctrlc", "dashmap", @@ -1330,7 +1330,7 @@ dependencies = [ "aptos-faucet-core", "aptos-logger", "aptos-sdk", - "clap 4.5.4", + "clap 4.4.12", "tokio", ] @@ -1346,7 +1346,7 @@ dependencies = [ "aptos-sdk", "async-trait", "captcha", - "clap 4.5.4", + "clap 4.4.12", "deadpool-redis", "enum_dispatch", "futures", @@ -1388,7 +1388,7 @@ dependencies = [ "anyhow", "aptos-faucet-core", "aptos-logger", - "clap 4.5.4", + "clap 4.4.12", "tokio", ] @@ -1400,7 +1400,7 @@ dependencies = [ "aptos-logger", "aptos-node-checker", "aptos-sdk", - "clap 4.5.4", + "clap 4.4.12", "env_logger", "futures", "gcp-bigquery-client", @@ -1441,7 +1441,7 @@ dependencies = [ "bulletproofs", "byteorder", "claims", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "curve25519-dalek-ng", "either", @@ -1559,7 +1559,7 @@ dependencies = [ "aptos-package-builder", "aptos-types", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "move-core-types", "move-model", "tempfile", @@ -1630,7 +1630,7 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", - "clap 4.5.4", + "clap 4.4.12", "diesel", "diesel_migrations", "field_count", @@ -1660,7 +1660,7 @@ dependencies = [ "backtrace", "base64 0.13.1", "bytes", - "clap 4.5.4", + "clap 4.4.12", "cloud-storage", "dashmap", "enum_dispatch", @@ -1671,7 +1671,7 @@ dependencies = [ "itertools 0.10.5", "once_cell", "prometheus", - "prost 0.12.4", + "prost 0.12.3", "redis", "redis-test", "serde", @@ -1803,7 +1803,7 @@ dependencies = [ "backtrace", "base64 0.13.1", "chrono", - "clap 4.5.4", + "clap 4.4.12", "cloud-storage", "flate2", "futures", @@ -1812,7 +1812,7 @@ dependencies = [ "itertools 0.10.5", "once_cell", "prometheus", - "prost 0.12.4", + "prost 0.12.3", "redis", "redis-test", "ripemd", @@ -2049,8 +2049,8 @@ dependencies = [ name = "aptos-log-derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -2186,7 +2186,7 @@ dependencies = [ "aptos-gas-schedule", "aptos-types", "aptos-vm", - "clap 4.5.4", + "clap 4.4.12", "move-cli", "move-package", "move-prover", @@ -2399,7 +2399,7 @@ dependencies = [ "aptos-logger", "aptos-network", "aptos-types", - "clap 4.5.4", + "clap 4.4.12", "futures", "serde", "tokio", @@ -2447,7 +2447,7 @@ dependencies = [ "aptos-sdk", "aptos-transaction-emitter-lib", "async-trait", - "clap 4.5.4", + "clap 4.4.12", "const_format", "env_logger", "futures", @@ -2494,8 +2494,8 @@ dependencies = [ name = "aptos-num-variants" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -2521,7 +2521,7 @@ dependencies = [ "aptos-mempool", "aptos-storage-interface", "aptos-types", - "clap 4.5.4", + "clap 4.4.12", ] [[package]] @@ -2638,8 +2638,8 @@ version = "1.3.0" dependencies = [ "futures-core", "pbjson", - "prost 0.12.4", - "prost-types 0.12.4", + "prost 0.12.3", + "prost-types 0.12.3", "serde", "tonic 0.11.0", ] @@ -2716,7 +2716,7 @@ dependencies = [ "aptos-types", "bcs 0.1.4", "bytes", - "clap 4.5.4", + "clap 4.4.12", "futures", "hex", "move-binary-format", @@ -2856,7 +2856,7 @@ dependencies = [ "aptos-framework", "aptos-types", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "heck 0.4.1", "move-core-types", "once_cell", @@ -3158,7 +3158,7 @@ dependencies = [ "aptos-types", "aptos-vm", "aptos-vm-logging", - "clap 4.5.4", + "clap 4.4.12", "criterion", "criterion-cpu-time", "num_cpus", @@ -3176,7 +3176,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "aptos-transaction-emitter-lib", - "clap 4.5.4", + "clap 4.4.12", "futures", "rand 0.7.3", "tokio", @@ -3198,7 +3198,7 @@ dependencies = [ "aptos-transaction-generator-lib", "aptos-types", "async-trait", - "clap 4.5.4", + "clap 4.4.12", "futures", "itertools 0.10.5", "once_cell", @@ -3220,7 +3220,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "async-trait", - "clap 4.5.4", + "clap 4.4.12", "move-binary-format", "once_cell", "rand 0.7.3", @@ -3244,7 +3244,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "datatest-stable", "hex", "move-binary-format", @@ -3492,7 +3492,7 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "glob", "move-binary-format", "move-core-types", @@ -3600,9 +3600,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "ark-bls12-381" @@ -3691,7 +3691,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.36", + "quote 1.0.35", "syn 1.0.109", ] @@ -3703,8 +3703,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -3768,8 +3768,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -3814,7 +3814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c6368f9ae5c6ec403ca910327ae0c9437b0a85255b6950c90d497e6177f6e5e" dependencies = [ "proc-macro-hack", - "quote 1.0.36", + "quote 1.0.35", "syn 1.0.109", ] @@ -3886,27 +3886,28 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.1", + "event-listener 4.0.2", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.11.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ + "async-lock 3.2.0", "async-task", "concurrent-queue", - "fastrand 2.0.2", - "futures-lite 2.3.0", + "fastrand 2.0.1", + "futures-lite 2.1.0", "slab", ] @@ -3916,12 +3917,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.1", + "async-channel 2.1.1", "async-executor", - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.2.2", + "async-lock 3.2.0", "blocking", - "futures-lite 2.3.0", + "futures-lite 2.1.0", "once_cell", ] @@ -3947,18 +3948,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.2.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.1.0", "parking", - "polling 3.6.0", - "rustix 0.38.32", + "polling 3.3.1", + "rustix 0.38.28", "slab", "tracing", "windows-sys 0.52.0", @@ -3975,12 +3976,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 4.0.2", + "event-listener-strategy", "pin-project-lite", ] @@ -4006,19 +4007,19 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.32", + "rustix 0.38.28", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -4027,13 +4028,13 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.3.2", + "async-io 2.2.2", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix 0.38.28", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -4083,26 +4084,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -4129,27 +4130,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "auto_impl" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro-error", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" @@ -4162,7 +4164,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", + "http", "http-body", "hyper", "itoa", @@ -4193,7 +4195,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", + "http", "http-body", "hyper", "itoa", @@ -4219,7 +4221,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", + "http", "http-body", "mime", "tower-layer", @@ -4235,7 +4237,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", + "http", "http-body", "mime", "rustversion", @@ -4264,7 +4266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.14", + "getrandom 0.2.11", "instant", "pin-project-lite", "rand 0.8.5", @@ -4273,9 +4275,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -4306,15 +4308,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.7" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-url" @@ -4322,7 +4324,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", ] [[package]] @@ -4333,15 +4335,24 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-cookies" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" +checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" dependencies = [ "lalrpop", "lalrpop-util", "regex", ] +[[package]] +name = "basic-toml" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +dependencies = [ + "serde", +] + [[package]] name = "bcs" version = "0.1.4" @@ -4391,16 +4402,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "bigdecimal" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9324c8014cd04590682b34f1e9448d38f0674d0f7b2dc553331016ef0e4e9ebc" +checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" dependencies = [ "autocfg", "libm", @@ -4432,12 +4443,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "regex", "rustc-hash", "shlex", - "syn 2.0.60", + "syn 2.0.47", ] [[package]] @@ -4463,9 +4474,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitmaps" @@ -4582,12 +4593,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.2.1", - "async-lock 3.3.0", + "async-channel 2.1.1", + "async-lock 3.2.0", "async-task", - "fastrand 2.0.2", + "fastrand 2.0.1", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.1.0", "piper", "tracing", ] @@ -4633,9 +4644,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", "serde", @@ -4663,9 +4674,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -4714,15 +4725,15 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" @@ -4732,9 +4743,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -4781,9 +4792,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" dependencies = [ "serde", ] @@ -4835,9 +4846,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.94" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", "libc", @@ -4854,9 +4865,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.8" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" dependencies = [ "smallvec", "target-lexicon", @@ -4868,12 +4879,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_block" version = "0.1.1" @@ -4882,9 +4887,9 @@ checksum = "18758054972164c3264f7c8386f5fc6da6114cb46b619fd365d4e3b2dc3ae487" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", @@ -4892,14 +4897,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] name = "chrono-tz" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7" dependencies = [ "chrono", "chrono-tz-build", @@ -4925,9 +4930,9 @@ checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" [[package]] name = "ciborium" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -4936,18 +4941,18 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", - "half 2.4.1", + "half 1.8.2", ] [[package]] @@ -5009,39 +5014,39 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.1", + "textwrap 0.16.0", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", - "clap_derive 4.5.4", + "clap_derive 4.4.7", ] [[package]] name = "clap-verbosity-flag" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" +checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" dependencies = [ - "clap 4.5.4", + "clap 4.4.12", "log", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.0", - "strsim 0.11.1", + "clap_lex 0.6.0", + "strsim 0.10.0", ] [[package]] @@ -5052,21 +5057,21 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ - "heck 0.5.0", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "heck 0.4.1", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -5080,9 +5085,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clear_on_drop" @@ -5162,9 +5167,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.7" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "futures-core", @@ -5185,15 +5190,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.52.0", + "windows-sys 0.45.0", ] [[package]] @@ -5265,8 +5270,8 @@ version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "unicode-xid 0.2.4", ] @@ -5288,6 +5293,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "cookie" version = "0.17.0" @@ -5295,7 +5311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ "aes-gcm", - "base64 0.21.7", + "base64 0.21.5", "hkdf 0.12.4", "hmac 0.12.1", "percent-encoding", @@ -5308,12 +5324,12 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.20.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" +checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" dependencies = [ - "cookie", - "idna 0.3.0", + "cookie 0.16.2", + "idna 0.2.3", "log", "publicsuffix", "serde", @@ -5352,9 +5368,9 @@ dependencies = [ [[package]] name = "coset" -version = "0.3.7" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8aad850c1f86daa47e812913051eb5a26c4d9fb4242a89178bf99b946e4e3c" +checksum = "99c214bbc5c8b4518856d79cae4d323feaa881ecf3e31b5af6572bb5313c11d5" dependencies = [ "ciborium", "ciborium-io", @@ -5371,18 +5387,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] @@ -5435,10 +5451,11 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" dependencies = [ + "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -5448,46 +5465,54 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ + "autocfg", + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" dependencies = [ + "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] [[package]] name = "crossterm" @@ -5621,34 +5646,34 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.4" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" dependencies = [ - "nix 0.28.0", + "nix 0.27.1", "windows-sys 0.52.0", ] [[package]] name = "curl" -version = "0.4.46" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.5.6", - "windows-sys 0.52.0", + "socket2 0.4.10", + "winapi 0.3.9", ] [[package]] name = "curl-sys" -version = "0.4.72+curl-8.6.0" +version = "0.4.70+curl-8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" dependencies = [ "cc", "libc", @@ -5657,7 +5682,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -5699,12 +5724,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -5715,24 +5740,24 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "strsim 0.10.0", - "syn 2.0.60", + "syn 2.0.47", ] [[package]] @@ -5742,19 +5767,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.36", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core 0.20.8", - "quote 1.0.36", - "syn 2.0.60", + "darling_core 0.20.3", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -5847,9 +5872,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid 0.9.6", "pem-rfc7468 0.7.0", @@ -5878,8 +5903,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -5889,9 +5914,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -5901,8 +5926,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "rustc_version", "syn 1.0.109", ] @@ -5926,18 +5951,18 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.4.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" +checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" [[package]] name = "diesel" -version = "2.1.6" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" dependencies = [ "bigdecimal", - "bitflags 2.5.0", + "bitflags 2.4.1", "byteorder", "chrono", "diesel_derives", @@ -5952,14 +5977,14 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" dependencies = [ "diesel_table_macro_syntax", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -5979,7 +6004,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.60", + "syn 2.0.47", ] [[package]] @@ -6138,7 +6163,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der 0.7.8", "digest 0.10.7", "elliptic-curve", "rfc6979", @@ -6185,9 +6210,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -6229,30 +6254,30 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "enum_dispatch" -version = "0.3.13" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "env_logger" -version = "0.10.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -6355,7 +6380,7 @@ dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", "impl-rlp", - "scale-info 2.11.2", + "scale-info 2.10.0", "tiny-keccak", ] @@ -6390,7 +6415,7 @@ dependencies = [ "hash256-std-hasher", "parity-scale-codec 3.6.9", "rlp", - "scale-info 2.11.2", + "scale-info 2.10.0", "serde", "sha3 0.10.8", "trie-root", @@ -6436,7 +6461,7 @@ dependencies = [ "impl-codec 0.6.0", "impl-rlp", "primitive-types 0.12.2", - "scale-info 2.11.2", + "scale-info 2.10.0", "uint", ] @@ -6465,20 +6490,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" dependencies = [ "concurrent-queue", "parking", @@ -6491,17 +6505,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" -dependencies = [ - "event-listener 5.3.0", + "event-listener 4.0.2", "pin-project-lite", ] @@ -6532,7 +6536,7 @@ version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" dependencies = [ - "auto_impl 1.2.0", + "auto_impl 1.1.0", "environmental", "ethereum 0.15.0", "evm-core 0.41.0", @@ -6542,7 +6546,7 @@ dependencies = [ "parity-scale-codec 3.6.9", "primitive-types 0.12.2", "rlp", - "scale-info 2.11.2", + "scale-info 2.10.0", "serde", "sha3 0.10.8", ] @@ -6568,7 +6572,7 @@ checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec 3.6.9", "primitive-types 0.12.2", - "scale-info 2.11.2", + "scale-info 2.10.0", "serde", ] @@ -6628,7 +6632,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" dependencies = [ - "auto_impl 1.2.0", + "auto_impl 1.1.0", "environmental", "evm-core 0.41.0", "primitive-types 0.12.2", @@ -6677,15 +6681,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" dependencies = [ "simd-adler32", ] @@ -6727,7 +6731,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1320970ff3b1c1cacc6a38e8cdb1aced955f29627697cd992c5ded82eb646a8" dependencies = [ - "quote 1.0.36", + "quote 1.0.35", "syn 1.0.109", ] @@ -6769,7 +6773,7 @@ checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", - "half 2.4.1", + "half 2.2.1", "typenum", ] @@ -6963,11 +6967,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ - "fastrand 2.0.2", + "fastrand 2.0.1", "futures-core", "futures-io", "parking", @@ -6980,9 +6984,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -6999,9 +7003,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.3" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" @@ -7130,9 +7134,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -7143,11 +7147,11 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ - "opaque-debug 0.3.1", + "opaque-debug 0.3.0", "polyval", ] @@ -7183,10 +7187,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", - "bstr 1.9.1", + "bstr 1.9.0", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -7214,14 +7218,14 @@ dependencies = [ [[package]] name = "goldenfile" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d5c44265baec620ea19c97b4ce9f068e28f8c3d7faccc483f02968b5e3c587" +checksum = "e4a67453a3b358bd8213aedafd4feed75eecab9fb04bed26ba6fdf94694be560" dependencies = [ "scopeguard", "similar-asserts", "tempfile", - "yansi 1.0.1", + "yansi 1.0.0-rc.1", ] [[package]] @@ -7231,7 +7235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "931bedb2264cb00f914b0a6a5c304e34865c34306632d3932e0951a073e4a67d" dependencies = [ "async-trait", - "base64 0.21.7", + "base64 0.21.5", "google-cloud-metadata", "google-cloud-token", "home", @@ -7264,7 +7268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c57ca1d971d7c6f852c02eda4e87e88b1247b6ed8be9fa5b2768c68b0f2ca5" dependencies = [ "async-stream", - "base64 0.21.7", + "base64 0.21.5", "bytes", "futures-util", "google-cloud-auth", @@ -7322,11 +7326,11 @@ dependencies = [ [[package]] name = "guppy" -version = "0.17.5" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34e99a7734579b834a076ef11789783c153c6eb5fb3520ed15bc41f483f0f317" +checksum = "114a100a9aa9f4c468a7b9e96626cdab267bb652660d8408e8f6d56d4c310edd" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.7", "camino", "cargo_metadata 0.18.1", "cfg-if", @@ -7334,8 +7338,8 @@ dependencies = [ "fixedbitset 0.4.2", "guppy-summaries", "guppy-workspace-hack", - "indexmap 2.2.6", - "itertools 0.12.1", + "indexmap 2.1.0", + "itertools 0.12.0", "nested", "once_cell", "pathdiff", @@ -7382,8 +7386,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.12", - "indexmap 2.2.6", + "http", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -7392,17 +7396,16 @@ dependencies = [ [[package]] name = "half" -version = "1.8.3" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "half" -version = "2.4.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ - "cfg-if", "crunchy", ] @@ -7447,7 +7450,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.8", + "ahash 0.7.7", ] [[package]] @@ -7456,7 +7459,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.7", ] [[package]] @@ -7465,7 +7468,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.7", "allocator-api2", ] @@ -7475,7 +7478,7 @@ version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", "byteorder", "flate2", "nom", @@ -7488,10 +7491,10 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", "bytes", "headers-core", - "http 0.2.12", + "http", "httpdate", "mime", "sha1", @@ -7503,7 +7506,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http 0.2.12", + "http", ] [[package]] @@ -7521,12 +7524,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -7538,9 +7535,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -7656,20 +7653,9 @@ checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.1.0" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -7683,7 +7669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.12", + "http", "pin-project-lite", ] @@ -7714,7 +7700,7 @@ dependencies = [ "assert-json-diff", "async-object-pool", "async-trait", - "base64 0.21.7", + "base64 0.21.5", "basic-cookies", "crossbeam-utils", "form_urlencoded", @@ -7759,13 +7745,13 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.12", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -7778,7 +7764,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http 0.2.12", + "http", "hyper", "log", "rustls 0.20.9", @@ -7794,7 +7780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.12", + "http", "hyper", "rustls 0.21.10", "tokio", @@ -7828,9 +7814,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -7855,6 +7841,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.3.0" @@ -7877,15 +7874,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.3", "same-file", "walkdir", "winapi-util", @@ -7907,13 +7904,14 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", "color_quant", + "num-rational 0.4.1", "num-traits", "png", ] @@ -7960,8 +7958,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -7994,8 +7992,8 @@ checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" dependencies = [ "anyhow", "proc-macro-hack", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -8005,8 +8003,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", ] [[package]] @@ -8022,9 +8020,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -8055,13 +8053,13 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ - "ahash 0.8.11", - "clap 4.5.4", + "ahash 0.8.7", + "clap 4.4.12", "crossbeam-channel", "crossbeam-utils", "dashmap", "env_logger", - "indexmap 2.2.6", + "indexmap 2.1.0", "is-terminal", "itoa", "log", @@ -8099,7 +8097,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" dependencies = [ - "ahash 0.7.8", + "ahash 0.7.7", "dashmap", "hashbrown 0.12.3", "once_cell", @@ -8121,7 +8119,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.3.3", "libc", "windows-sys 0.48.0", ] @@ -8143,12 +8141,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ - "hermit-abi 0.3.9", - "libc", + "hermit-abi 0.3.3", + "rustix 0.38.28", "windows-sys 0.52.0", ] @@ -8172,7 +8170,7 @@ dependencies = [ "encoding_rs", "event-listener 2.5.3", "futures-lite 1.13.0", - "http 0.2.12", + "http", "log", "mime", "once_cell", @@ -8205,18 +8203,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jemalloc-sys" @@ -8240,18 +8238,18 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.30" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -8276,7 +8274,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", "pem 1.1.1", "ring 0.16.20", "serde", @@ -8301,9 +8299,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -8319,33 +8317,33 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.20.2" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" dependencies = [ "ascii-canvas", "bit-set", + "diff", "ena", - "itertools 0.11.0", + "is-terminal", + "itertools 0.10.5", "lalrpop-util", "petgraph 0.6.4", - "pico-args", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.6.29", "string_cache", "term", "tiny-keccak", "unicode-xid 0.2.4", - "walkdir", ] [[package]] name = "lalrpop-util" -version = "0.20.2" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" dependencies = [ - "regex-automata 0.4.6", + "regex", ] [[package]] @@ -8440,9 +8438,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libfuzzer-sys" @@ -8469,12 +8467,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-sys 0.48.0", ] [[package]] @@ -8485,9 +8483,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.10+1.61.0" +version = "0.1.9+1.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" +checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" dependencies = [ "cc", "libc", @@ -8495,12 +8493,13 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "libc", + "redox_syscall 0.4.1", ] [[package]] @@ -8580,9 +8579,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -8610,16 +8609,16 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "listener" version = "0.1.0" dependencies = [ "bytes", - "clap 4.5.4", + "clap 4.4.12", "tokio", ] @@ -8635,9 +8634,9 @@ dependencies = [ [[package]] name = "lodepng" -version = "3.10.1" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a42d298694b14401847de29abd44adf278b42e989e516deac7b72018400002d8" +checksum = "b00f56ff9bcd5721ab172b73eac8a7d4e9439f47a98581e666178dbe7df97e13" dependencies = [ "crc32fast", "fallible_collections", @@ -8648,9 +8647,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ "serde", "value-bag", @@ -8749,6 +8748,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "matchit" version = "0.5.0" @@ -8763,9 +8768,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -8814,8 +8819,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", ] [[package]] @@ -8857,9 +8862,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", "simd-adler32", @@ -8867,9 +8872,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", @@ -8905,8 +8910,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -8930,7 +8935,7 @@ dependencies = [ "anyhow", "aptos-framework", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "move-binary-format", ] @@ -8966,7 +8971,7 @@ name = "move-analyzer" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "crossbeam", "derivative", @@ -9078,7 +9083,7 @@ name = "move-bytecode-viewer" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "crossterm 0.26.1", "move-binary-format", "move-bytecode-source-map", @@ -9096,7 +9101,7 @@ dependencies = [ "anyhow", "bcs 0.1.4", "bytes", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "colored", "datatest-stable", @@ -9159,7 +9164,7 @@ version = "0.0.1" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "datatest-stable", "difference", @@ -9198,7 +9203,7 @@ dependencies = [ "abstract-domain-derive", "anyhow", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -9220,7 +9225,7 @@ dependencies = [ "move-stackless-bytecode", "move-stdlib", "move-symbol-pool", - "num 0.4.2", + "num 0.4.1", "once_cell", "petgraph 0.6.4", "serde", @@ -9248,7 +9253,7 @@ dependencies = [ "ethnum", "hashbrown 0.14.3", "hex", - "num 0.4.2", + "num 0.4.1", "once_cell", "primitive-types 0.10.1", "proptest", @@ -9269,7 +9274,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "codespan", "colored", "move-binary-format", @@ -9287,7 +9292,7 @@ name = "move-disassembler" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "colored", "move-binary-format", "move-bytecode-source-map", @@ -9314,7 +9319,7 @@ dependencies = [ "move-model", "move-prover", "move-prover-test-utils", - "num 0.4.2", + "num 0.4.1", "once_cell", "regex", "serde", @@ -9361,7 +9366,7 @@ name = "move-explain" version = "0.1.0" dependencies = [ "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "move-command-line-common", "move-core-types", ] @@ -9372,7 +9377,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "move-binary-format", "move-bytecode-source-map", "move-bytecode-verifier", @@ -9456,7 +9461,7 @@ dependencies = [ "move-ir-types", "move-prover-test-utils", "move-symbol-pool", - "num 0.4.2", + "num 0.4.1", "num-traits", "once_cell", "regex", @@ -9470,7 +9475,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.5.4", + "clap 4.4.12", "colored", "datatest-stable", "dirs-next", @@ -9511,7 +9516,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.5.4", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -9533,7 +9538,7 @@ dependencies = [ "move-prover-bytecode-pipeline", "move-prover-test-utils", "move-stackless-bytecode", - "num 0.4.2", + "num 0.4.1", "once_cell", "pretty", "rand 0.8.5", @@ -9565,7 +9570,7 @@ dependencies = [ "move-model", "move-prover-bytecode-pipeline", "move-stackless-bytecode", - "num 0.4.2", + "num 0.4.1", "once_cell", "pretty", "rand 0.8.5", @@ -9584,7 +9589,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.5.4", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -9597,7 +9602,7 @@ dependencies = [ "move-model", "move-stackless-bytecode", "move-stackless-bytecode-test-utils", - "num 0.4.2", + "num 0.4.1", "once_cell", "pretty", "rand 0.8.5", @@ -9657,7 +9662,7 @@ dependencies = [ "move-ir-to-bytecode", "move-model", "move-stackless-bytecode-test-utils", - "num 0.4.2", + "num 0.4.1", "once_cell", "paste", "petgraph 0.5.1", @@ -9744,7 +9749,7 @@ version = "0.1.0" dependencies = [ "anyhow", "atty", - "clap 4.5.4", + "clap 4.4.12", "codespan", "codespan-reporting", "datatest-stable", @@ -9766,7 +9771,7 @@ dependencies = [ "move-prover-test-utils", "move-stackless-bytecode", "move-stdlib", - "num 0.4.2", + "num 0.4.1", "once_cell", "pretty", "primitive-types 0.10.1", @@ -9786,7 +9791,7 @@ name = "move-transactional-test-runner" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "colored", "datatest-stable", "difference", @@ -9822,7 +9827,7 @@ dependencies = [ "anyhow", "better_any", "bytes", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "colored", "datatest-stable", @@ -9958,7 +9963,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 0.2.12", + "http", "httparse", "log", "memchr", @@ -10008,9 +10013,9 @@ checksum = "ca2b420f638f07fe83056b55ea190bb815f609ec5a35e7017884a10f78839c9e" [[package]] name = "new_debug_unreachable" -version = "1.0.6" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" @@ -10029,20 +10034,8 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "libc", -] - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "cfg-if", - "cfg_aliases", "libc", ] @@ -10085,9 +10078,9 @@ dependencies = [ [[package]] name = "ntest" -version = "0.9.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cd16a2e6992865367e7ca50cd6953d09daaed93641421168733a1274afadd6" +checksum = "da8ec6d2b73d45307e926f5af46809768581044384637af6b3f3fe7c3c88f512" dependencies = [ "ntest_test_cases", "ntest_timeout", @@ -10095,24 +10088,24 @@ dependencies = [ [[package]] name = "ntest_test_cases" -version = "0.9.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197eff6c12b80ff5de6173e438fa3c1340a9e708118c1626e690f65aee1e5332" +checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "ntest_timeout" -version = "0.9.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef492b5cf80f90c050b287e747228a1fa6517e9d754f364b5a7e0e038e49a25f" +checksum = "066b468120587a402f0b47d8f80035c921f6a46f8209efd0632a89a16f5188a4" dependencies = [ - "proc-macro-crate 2.0.2", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10151,12 +10144,12 @@ dependencies = [ [[package]] name = "num" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ "num-bigint 0.4.4", - "num-complex 0.4.5", + "num-complex 0.4.4", "num-integer", "num-iter", "num-rational 0.4.1", @@ -10225,27 +10218,21 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10261,18 +10248,19 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ + "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg", "num-integer", @@ -10305,9 +10293,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -10319,15 +10307,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.3.3", "libc", ] [[package]] name = "num_threads" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] @@ -10346,7 +10334,7 @@ checksum = "60080faccd4ca50ad0b801b2be686136376b13f691f6eac84817e40973b2e1bb" dependencies = [ "anyhow", "itertools 0.10.5", - "num 0.4.2", + "num 0.4.1", ] [[package]] @@ -10378,17 +10366,17 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -10403,9 +10391,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -10416,9 +10404,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -10469,8 +10457,8 @@ checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10482,8 +10470,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10564,8 +10552,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10575,9 +10563,9 @@ version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 2.0.2", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro-crate 2.0.1", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -10683,11 +10671,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "ciborium", "coset", "data-encoding", - "indexmap 2.2.6", + "indexmap 2.1.0", "rand 0.8.5", "serde", "serde_json", @@ -10782,9 +10770,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -10793,9 +10781,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -10803,22 +10791,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -10842,7 +10830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.2.6", + "indexmap 2.1.0", ] [[package]] @@ -10892,37 +10880,31 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -10937,7 +10919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand 2.0.1", "futures-io", ] @@ -10958,7 +10940,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.9", + "der 0.7.8", "pkcs8 0.10.2", "spki 0.7.3", ] @@ -10980,15 +10962,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", + "der 0.7.8", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "plotters" @@ -11020,9 +11002,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.13" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -11041,10 +11023,10 @@ dependencies = [ "async-trait", "bytes", "chrono", - "cookie", + "cookie 0.17.0", "futures-util", "headers", - "http 0.2.12", + "http", "hyper", "mime", "multer", @@ -11078,10 +11060,10 @@ version = "1.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" dependencies = [ - "proc-macro-crate 2.0.2", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro-crate 2.0.1", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -11103,7 +11085,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "serde_yaml 0.9.34+deprecated", + "serde_yaml 0.9.30", "thiserror", "tokio", "url", @@ -11116,12 +11098,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "274cf13f710999977a3c1e396c2a5000d104075a7127ce6470fbdae4706be621" dependencies = [ "darling 0.14.4", - "http 0.2.12", + "http", "indexmap 1.9.3", "mime", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "regex", "syn 1.0.109", "thiserror", @@ -11145,28 +11127,27 @@ dependencies = [ [[package]] name = "polling" -version = "3.6.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.3.9", "pin-project-lite", - "rustix 0.38.32", + "rustix 0.38.28", "tracing", "windows-sys 0.52.0", ] [[package]] name = "polyval" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.1", + "opaque-debug 0.3.0", "universal-hash", ] @@ -11185,8 +11166,8 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597c3287a549da151aca6ada2795ecde089c7527bd5093114e8e0e1c3f0e52b1" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -11312,12 +11293,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ - "proc-macro2 1.0.81", - "syn 2.0.60", + "proc-macro2 1.0.75", + "syn 2.0.47", ] [[package]] @@ -11365,7 +11346,7 @@ dependencies = [ "fixed-hash 0.8.0", "impl-codec 0.6.0", "impl-rlp", - "scale-info 2.11.2", + "scale-info 2.10.0", "uint", ] @@ -11381,9 +11362,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" dependencies = [ "toml_datetime", "toml_edit 0.20.2", @@ -11396,8 +11377,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", "version_check", ] @@ -11408,8 +11389,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "version_check", ] @@ -11436,9 +11417,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" dependencies = [ "unicode-ident", ] @@ -11485,7 +11466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "811031bea65e5a401fb2e1f37d802cca6601e204ac463809a3189352d13b78a5" dependencies = [ "chrono", - "itertools 0.12.1", + "itertools 0.12.0", "once_cell", "regex", ] @@ -11498,13 +11479,13 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.5.0", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.2", "rusty-fork", "tempfile", "unarray", @@ -11527,8 +11508,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -11544,12 +11525,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", - "prost-derive 0.12.4", + "prost-derive 0.12.3", ] [[package]] @@ -11560,22 +11541,22 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", - "itertools 0.12.1", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "itertools 0.11.0", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -11589,11 +11570,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ - "prost 0.12.4", + "prost 0.12.3", ] [[package]] @@ -11627,7 +11608,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.5.4", + "clap 4.4.12", "codespan-reporting", "hex", "itertools 0.10.5", @@ -11638,7 +11619,7 @@ dependencies = [ "move-prover-boogie-backend", "move-prover-bytecode-pipeline", "move-stackless-bytecode", - "num 0.4.2", + "num 0.4.1", "plotters", "serde", "serde_json", @@ -11670,11 +11651,11 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.6" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" dependencies = [ - "bitflags 2.5.0", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -11750,11 +11731,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.81", + "proc-macro2 1.0.75", ] [[package]] @@ -11840,7 +11821,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.11", ] [[package]] @@ -11890,9 +11871,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -11900,9 +11881,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -11960,11 +11941,11 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.11", "libredox", "thiserror", ] @@ -11984,21 +11965,21 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -12012,13 +11993,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.2", ] [[package]] @@ -12029,25 +12010,25 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", "bytes", - "cookie", + "cookie 0.16.2", "cookie_store", "encoding_rs", "futures-core", "futures-util", "h2", - "http 0.2.12", + "http", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -12066,7 +12047,6 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -12084,13 +12064,13 @@ dependencies = [ [[package]] name = "reqwest-middleware" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" +checksum = "88a3e86aa6053e59030e7ce2d2a3b258dd08fc2d337d52f73f6cb480f5858690" dependencies = [ "anyhow", "async-trait", - "http 0.2.12", + "http", "reqwest", "serde", "task-local-extensions", @@ -12107,8 +12087,8 @@ dependencies = [ "async-trait", "chrono", "futures", - "getrandom 0.2.14", - "http 0.2.12", + "getrandom 0.2.11", + "http", "hyper", "parking_lot 0.11.2", "reqwest", @@ -12149,9 +12129,9 @@ dependencies = [ [[package]] name = "rfc7239" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b106a85eeb5b0336d16d6a20eab857f92861d4fbb1eb9a239866fb98fb6a1063" +checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" dependencies = [ "uncased", ] @@ -12182,17 +12162,16 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", - "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.11", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -12221,8 +12200,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -12295,8 +12274,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" dependencies = [ "cfg-if", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "rustc_version", "syn 1.0.109", ] @@ -12358,14 +12337,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.12", "windows-sys 0.52.0", ] @@ -12388,7 +12367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.7", "rustls-webpki 0.101.7", "sct", ] @@ -12400,9 +12379,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -12447,7 +12426,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", ] [[package]] @@ -12456,15 +12435,15 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" @@ -12472,26 +12451,26 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", + "ring 0.17.7", "untrusted 0.9.0", ] [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ - "ring 0.17.8", + "ring 0.17.7", "rustls-pki-types", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rusty-fork" @@ -12507,9 +12486,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -12535,15 +12514,15 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "bitvec 1.0.1", "cfg-if", "derive_more", "parity-scale-codec 3.6.9", - "scale-info-derive 2.11.2", + "scale-info-derive 2.10.0", ] [[package]] @@ -12553,20 +12532,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "scale-info-derive" -version = "2.11.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -12606,7 +12585,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -12623,7 +12602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.7.9", + "der 0.7.8", "generic-array 0.14.7", "pkcs8 0.10.2", "serdect", @@ -12633,9 +12612,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -12646,9 +12625,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -12656,9 +12635,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" dependencies = [ "serde", ] @@ -12668,7 +12647,7 @@ name = "sender" version = "0.1.0" dependencies = [ "bytes", - "clap 4.5.4", + "clap 4.4.12", "event-listener 2.5.3", "quanta", "tokio", @@ -12676,9 +12655,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" dependencies = [ "serde_derive", ] @@ -12745,28 +12724,28 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ - "half 1.8.3", + "half 1.8.2", "serde", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -12795,13 +12774,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -12827,17 +12806,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.7.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.7", + "base64 0.21.5", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.6", + "indexmap 2.1.0", "serde", - "serde_derive", "serde_json", "serde_with_macros", "time", @@ -12845,14 +12823,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.7.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ - "darling 0.20.8", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "darling 0.20.3", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -12869,11 +12847,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.34+deprecated" +version = "0.9.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -12927,7 +12905,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.1", + "opaque-debug 0.3.0", ] [[package]] @@ -12963,7 +12941,7 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.1", + "opaque-debug 0.3.0", ] [[package]] @@ -13006,9 +12984,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "signal-hook" @@ -13064,9 +13042,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.5.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" dependencies = [ "bstr 0.2.17", "unicode-segmentation", @@ -13179,15 +13157,15 @@ dependencies = [ [[package]] name = "smallbitvec" -version = "2.5.3" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3fc564a4b53fd1e8589628efafe57602d91bde78be18186b5f61e8faea470" +checksum = "75ce4f9dc4a41b4c3476cc925f1efb11b66df373a8fde5d4b8915fa91b5d995e" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smawk" @@ -13224,8 +13202,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -13241,12 +13219,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -13278,7 +13256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der 0.7.8", ] [[package]] @@ -13334,12 +13312,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "structopt" version = "0.3.26" @@ -13359,8 +13331,8 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -13386,8 +13358,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "rustversion", "syn 1.0.109", ] @@ -13399,10 +13371,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "rustversion", - "syn 2.0.60", + "syn 2.0.47", ] [[package]] @@ -13457,19 +13429,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.60" +version = "2.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "unicode-ident", ] @@ -13540,15 +13512,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "target-spec" -version = "3.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8e795b1824524d13cdf04f73cf8b4f244ce86c96b4d2a83a6ca1a753d2752" +checksum = "48b81540ee78bd9de9f7dca2378f264cf1f4193da6e2d09b54c0d595131a48f1" dependencies = [ "cfg-expr", "guppy-workspace-hack", @@ -13568,13 +13540,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", - "fastrand 2.0.2", - "rustix 0.38.32", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.28", "windows-sys 0.52.0", ] @@ -13642,9 +13615,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -13653,9 +13626,9 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", "test-case-core", ] @@ -13663,9 +13636,9 @@ dependencies = [ name = "test-generation" version = "0.1.0" dependencies = [ - "clap 4.5.4", + "clap 4.4.12", "crossbeam-channel", - "getrandom 0.2.14", + "getrandom 0.2.11", "hex", "itertools 0.10.5", "module-generation", @@ -13689,7 +13662,7 @@ name = "testdiff" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.4.12", "itertools 0.10.5", "once_cell", "walkdir", @@ -13727,35 +13700,35 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ "cfg-if", "once_cell", @@ -13772,14 +13745,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "itoa", "libc", - "num-conv", "num_threads", "powerfmt", "serde", @@ -13795,11 +13767,10 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ - "num-conv", "time-core", ] @@ -13858,9 +13829,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -13870,7 +13841,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.6", + "socket2 0.5.5", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -13892,9 +13863,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -13952,9 +13923,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -13963,9 +13934,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", @@ -13976,9 +13947,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", @@ -14023,18 +13994,6 @@ dependencies = [ "toml_edit 0.19.15", ] -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.20.2", -] - [[package]] name = "toml_datetime" version = "0.6.3" @@ -14062,7 +14021,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -14075,9 +14034,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", + "indexmap 2.1.0", "toml_datetime", "winnow", ] @@ -14090,12 +14047,12 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum 0.6.20", - "base64 0.21.7", + "base64 0.21.5", "bytes", "futures-core", "futures-util", "h2", - "http 0.2.12", + "http", "http-body", "hyper", "hyper-timeout", @@ -14119,17 +14076,17 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.7", + "base64 0.21.5", "bytes", "flate2", "h2", - "http 0.2.12", + "http", "http-body", "hyper", "hyper-timeout", "percent-encoding", "pin-project", - "prost 0.12.4", + "prost 0.12.3", "rustls-native-certs 0.7.0", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -14149,8 +14106,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "548c227bd5c0fae5925812c4ec6c66ffcfced23ea370cb823f4d18f0fc1cb6a7" dependencies = [ - "prost 0.12.4", - "prost-types 0.12.4", + "prost 0.12.3", + "prost-types 0.12.3", "tokio", "tokio-stream", "tonic 0.11.0", @@ -14186,7 +14143,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 0.2.12", + "http", "http-body", "http-range-header", "pin-project-lite", @@ -14213,8 +14170,8 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", + "proc-macro2 1.0.75", + "quote 1.0.35", "syn 1.0.109", ] @@ -14236,9 +14193,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -14349,17 +14306,17 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.91" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad7eb6319ebadebca3dacf1f85a93bc54b73dd81b9036795f73de7ddfe27d5a" +checksum = "ee6b2fc10a33500845468aa7c06567a9226581b24f03c6f891e6dda2dc9a1c8b" dependencies = [ + "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", - "toml 0.8.2", ] [[package]] @@ -14377,14 +14334,14 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "rand 0.8.5", @@ -14408,9 +14365,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typeshare" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99877a66770b25072a67101e80790fd7fe9ed9eff8c69ffb81e9bf5cbea7733f" +checksum = "f44d1a2f454cb35fbe05b218c410792697e76bd868f48d3a418f2cd1a7d527d6" dependencies = [ "chrono", "serde", @@ -14420,12 +14377,12 @@ dependencies = [ [[package]] name = "typeshare-annotation" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecce25dea8aeaadc44909f4c1226d22d84512fccd07d22447ecbad176bc09545" +checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9" dependencies = [ - "quote 1.0.36", - "syn 2.0.60", + "quote 1.0.35", + "syn 1.0.109", ] [[package]] @@ -14451,9 +14408,9 @@ dependencies = [ [[package]] name = "tzdb_data" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1889fdffac09d65c1d95c42d5202e9b21ad8c758f426e9fe09088817ea998d6" +checksum = "629555d2921f3f0dc0de98699415a8b2b61dfcd3a0b082a327f7ed748bbb2b76" dependencies = [ "tz-rs", ] @@ -14484,9 +14441,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "uncased" -version = "0.9.10" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" dependencies = [ "version_check", ] @@ -14552,9 +14509,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -14570,18 +14527,18 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -14613,9 +14570,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "untrusted" @@ -14666,15 +14623,16 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utcnow" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "493ace370ee8579788f83a4f0eef89183c527b7551b4ad71364fac10371872b7" +checksum = "b10d49a98e3bbd9b73084a7b15f96c5b2136d5a2e2799b99d19a2774d8519d1f" dependencies = [ + "autocfg", "const_fn", "errno", "js-sys", "libc", - "rustix 0.38.32", + "rustix 0.38.28", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", "winapi 0.3.9", @@ -14694,9 +14652,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.8.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "valuable" @@ -14706,9 +14664,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.8.1" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" [[package]] name = "variant_count" @@ -14716,7 +14674,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "quote 1.0.36", + "quote 1.0.35", "syn 1.0.109", ] @@ -14755,9 +14713,9 @@ checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.5.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -14774,15 +14732,15 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" +checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", - "http 0.2.12", + "http", "hyper", "log", "mime", @@ -14790,13 +14748,14 @@ dependencies = [ "multer", "percent-encoding", "pin-project", - "rustls-pemfile 2.1.2", + "rustls-pemfile 1.0.4", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.24.1", + "tokio-stream", "tokio-tungstenite", "tokio-util", "tower-service", @@ -14835,17 +14794,11 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -14853,24 +14806,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -14880,38 +14833,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ - "quote 1.0.36", + "quote 1.0.35", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" dependencies = [ "futures-util", "js-sys", @@ -14937,9 +14890,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -14951,15 +14904,15 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.8", + "ring 0.17.7", "untrusted 0.9.0", ] [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "which" @@ -14970,17 +14923,16 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.28", ] [[package]] name = "whoami" -version = "1.5.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" dependencies = [ - "redox_syscall 0.4.1", - "wasite", + "wasm-bindgen", "web-sys", ] @@ -14992,9 +14944,9 @@ checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "wildmatch" -version = "2.3.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939e59c1bc731542357fdaad98b209ef78c8743d652bb61439d16b16a79eb025" +checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb" [[package]] name = "winapi" @@ -15039,7 +14991,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.0", ] [[package]] @@ -15066,7 +15018,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.0", ] [[package]] @@ -15101,18 +15053,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "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]] @@ -15129,9 +15080,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" @@ -15147,9 +15098,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" @@ -15165,15 +15116,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" @@ -15189,9 +15134,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" @@ -15207,9 +15152,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" @@ -15225,9 +15170,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" @@ -15243,15 +15188,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.40" +version = "0.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" dependencies = [ "memchr", ] @@ -15293,13 +15238,13 @@ dependencies = [ [[package]] name = "xattr" -version = "1.3.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" dependencies = [ "libc", - "linux-raw-sys 0.4.13", - "rustix 0.38.32", + "linux-raw-sys 0.4.12", + "rustix 0.38.28", ] [[package]] @@ -15319,9 +15264,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yansi" -version = "1.0.1" +version = "1.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" [[package]] name = "yup-oauth2" @@ -15333,7 +15278,7 @@ dependencies = [ "async-trait", "base64 0.13.1", "futures", - "http 0.2.12", + "http", "hyper", "hyper-rustls 0.23.2", "itertools 0.10.5", @@ -15376,9 +15321,9 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -15396,9 +15341,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.81", - "quote 1.0.36", - "syn 2.0.60", + "proc-macro2 1.0.75", + "quote 1.0.35", + "syn 2.0.47", ] [[package]] @@ -15422,9 +15367,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", "pkg-config", diff --git a/api/src/context.rs b/api/src/context.rs index 0c8726b1b7b94..1fff25068c081 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -781,7 +781,6 @@ impl Context { hash: HashValue, ledger_version: u64, ) -> Result> { - println!("get_transaction_by_hash hash: {:?} {:?}", hash, ledger_version); self.db .get_transaction_by_hash(hash, ledger_version, true)? .map(|t| self.convert_into_transaction_on_chain_data(t)) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index 5db4876e11979..a914ec567c487 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -698,7 +698,6 @@ impl TransactionsApi { let accept_type = accept_type.clone(); let ledger_info = api_spawn_blocking(move || context.get_latest_ledger_info()).await?; - println!("ledger_info: {:?}", ledger_info); let txn_data = self .get_by_hash(hash.into(), &ledger_info) @@ -713,7 +712,6 @@ impl TransactionsApi { })? .context(format!("Failed to find transaction with hash: {}", hash)) .map_err(|_| transaction_not_found_by_hash(hash, &ledger_info))?; - println!("txn_data: {:?}", txn_data); let api = self.clone(); api_spawn_blocking(move || api.get_transaction_inner(&accept_type, txn_data, &ledger_info)) .await @@ -838,7 +836,6 @@ impl TransactionsApi { .await .context("Failed to join task to read transaction by hash")? .context("Failed to read transaction by hash from DB")?; - println!("from_db: {:?}", from_db); Ok(match from_db { None => { let res = self From 99cf0d3c2811d90c93e6c04f98e2cd0316c79731 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Sat, 18 May 2024 00:17:37 -0400 Subject: [PATCH 069/147] fix: trying miscellaneous state view id. --- execution/executor/src/block_executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execution/executor/src/block_executor.rs b/execution/executor/src/block_executor.rs index e6b872b9d2c58..b42b42b2ac01d 100644 --- a/execution/executor/src/block_executor.rs +++ b/execution/executor/src/block_executor.rs @@ -229,7 +229,7 @@ where .start_timer(); info!("next_version: {}", parent_output.next_version()); CachedStateView::new( - StateViewId::BlockExecution { block_id }, + StateViewId::Miscellaneous, Arc::clone(&self.db.reader), parent_output.next_version(), parent_output.state().current.clone(), From 451a9042ce405dc0405c5755248cdbe416a0aa29 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 1 Jun 2024 01:17:51 +0300 Subject: [PATCH 070/147] Remove calls to AptosDataBuilder::is_async Some tests still had these. --- peer-monitoring-service/server/src/tests.rs | 1 - state-sync/data-streaming-service/src/tests/utils.rs | 1 - state-sync/storage-service/server/src/tests/utils.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/peer-monitoring-service/server/src/tests.rs b/peer-monitoring-service/server/src/tests.rs index 71473c8f5d9cf..617a176b95df7 100644 --- a/peer-monitoring-service/server/src/tests.rs +++ b/peer-monitoring-service/server/src/tests.rs @@ -622,7 +622,6 @@ impl MockClient { /// Initializes the Aptos logger for tests pub fn initialize_logger() { aptos_logger::Logger::builder() - .is_async(false) .level(Level::Debug) .build(); } diff --git a/state-sync/data-streaming-service/src/tests/utils.rs b/state-sync/data-streaming-service/src/tests/utils.rs index fbf0810efd9c9..eb72562e6ac93 100644 --- a/state-sync/data-streaming-service/src/tests/utils.rs +++ b/state-sync/data-streaming-service/src/tests/utils.rs @@ -1041,7 +1041,6 @@ async fn determine_target_ledger_info( /// Initializes the Aptos logger for tests pub fn initialize_logger() { aptos_logger::Logger::builder() - .is_async(false) .level(Level::Info) .build(); } diff --git a/state-sync/storage-service/server/src/tests/utils.rs b/state-sync/storage-service/server/src/tests/utils.rs index ffb4db8f23729..6d05be7018f9e 100644 --- a/state-sync/storage-service/server/src/tests/utils.rs +++ b/state-sync/storage-service/server/src/tests/utils.rs @@ -507,7 +507,6 @@ pub async fn get_transactions_with_proof( /// Initializes the Aptos logger for tests pub fn initialize_logger() { aptos_logger::Logger::builder() - .is_async(false) .level(Level::Debug) .build(); } From ed341dc21fe1048799ff980b11cf78906ebd0bf3 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sat, 1 Jun 2024 15:49:24 +0300 Subject: [PATCH 071/147] aptos-db: remove reversion helper method AptosDB::commit_reversion_ledger_info does the same thing as commit_ledger_info, just use that method instead. --- .../aptosdb/src/db/include/aptosdb_writer.rs | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 5eefda4135977..80d8b94f0744a 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -1,6 +1,6 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::ledger_db::{transaction_db::TransactionDb, write_set_db::WriteSetDb}; +use crate::ledger_db::write_set_db::WriteSetDb; use aptos_types::proof::position::Position; impl DbWriter for AptosDB { @@ -292,7 +292,7 @@ impl DbWriter for AptosDB { .set_version(version_to_revert - 1); // Update the latest ledger info if provided - self.commit_reversion_ledger_info( + self.commit_ledger_info( version_to_revert - 1, new_root_hash, Some(&ledger_info_with_sigs), @@ -676,40 +676,6 @@ impl AptosDB { self.ledger_db.metadata_db().write_schemas(ledger_batch) } - fn commit_reversion_ledger_info( - &self, - last_version: Version, - new_root_hash: HashValue, - ledger_info_with_sigs: Option<&LedgerInfoWithSignatures>, - ) -> Result<()> { - let _timer = OTHER_TIMERS_SECONDS - .with_label_values(&["commit_ledger_info"]) - .start_timer(); - - let ledger_batch = SchemaBatch::new(); - - // If expected ledger info is provided, verify result root hash and save the ledger info. - if let Some(x) = ledger_info_with_sigs { - let expected_root_hash = x.ledger_info().transaction_accumulator_hash(); - ensure!( - new_root_hash == expected_root_hash, - "Root hash calculated doesn't match expected. {:?} vs {:?}", - new_root_hash, - expected_root_hash, - ); - - self.ledger_db - .metadata_db() - .put_ledger_info(x, &ledger_batch)?; - } - - ledger_batch.put::( - &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(last_version), - )?; - self.ledger_db.metadata_db().write_schemas(ledger_batch) - } - fn post_commit( &self, txns_to_commit: &[TransactionToCommit], From b2fa010ba241a31adfd09bfea37c80b0c35009c8 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sun, 2 Jun 2024 01:15:17 +0300 Subject: [PATCH 072/147] storage-interface: add FinalityView This wrapper is meant to represent the finalized state for the API. --- .../storage-interface/src/finality_view.rs | 73 +++++++++++++++++++ storage/storage-interface/src/lib.rs | 1 + 2 files changed, 74 insertions(+) create mode 100644 storage/storage-interface/src/finality_view.rs diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs new file mode 100644 index 0000000000000..961e38bb7e12d --- /dev/null +++ b/storage/storage-interface/src/finality_view.rs @@ -0,0 +1,73 @@ +use std::sync::RwLock; + +use aptos_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version}; + +use crate::{AptosDbError, DbReader, Result}; + +/// A wrapper over [`DbReader`], representing ledger at the end of a block +/// as the latest ledger and its version as the latest transaction version. +pub struct FinalityView { + reader: Db, + finalized_ledger_info: RwLock>, +} + +impl FinalityView { + pub fn new(reader: Db) -> Self { + Self { + reader, + finalized_ledger_info: RwLock::new(None), + } + } +} + +impl FinalityView { + /// Updates the information on the latest finalized block's ledger. + pub fn set_finalized_ledger_info(&self, ledger_info: LedgerInfoWithSignatures) -> Result<()> { + // Sanity checks: finalization should not be set on an empty database, + // the finality version should not exceed the latest committed. + match self.reader.get_latest_state_checkpoint_version()? { + None => return Err(AptosDbError::Other("no ledger states to finalize".into())), + Some(ver) => { + let fin_version = ledger_info.ledger_info().version(); + if fin_version > ver { + return Err(AptosDbError::Other(format!( + "finality version {fin_version} exceeds committed version {ver}" + ))); + } + }, + } + + let mut fin_legder_info = self.finalized_ledger_info.write().unwrap(); + *fin_legder_info = Some(ledger_info); + Ok(()) + } +} + +impl DbReader for FinalityView { + fn get_read_delegatee(&self) -> &dyn DbReader { + &self.reader + } + + fn get_latest_ledger_info_option(&self) -> Result> { + let fin_ledger_info = self.finalized_ledger_info.read().unwrap(); + Ok(fin_ledger_info.clone()) + } + + fn get_latest_version(&self) -> Result { + let fin_ledger_info = self.finalized_ledger_info.read().unwrap(); + fin_ledger_info + .as_ref() + .map(|li| li.ledger_info().version()) + .ok_or_else(|| AptosDbError::NotFound("finalized version".into())) + } + + fn get_latest_state_checkpoint_version(&self) -> Result> { + let fin_ledger_info = self.finalized_ledger_info.read().unwrap(); + let version = fin_ledger_info + .as_ref() + .map(|li| li.ledger_info().version()); + Ok(version) + } + + // TODO: override any other methods needed to maintain the illusion. +} diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index 2397415557589..4f4b254d501cf 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -45,6 +45,7 @@ pub mod block_info; pub mod cached_state_view; pub mod errors; mod executed_trees; +pub mod finality_view; mod metrics; #[cfg(any(test, feature = "fuzzing"))] pub mod mock; From 0edea03a94c1d5682ed880ef78880ec0f09f374b Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Sun, 2 Jun 2024 02:01:29 +0300 Subject: [PATCH 073/147] storage-interface: tests for FinalityView --- .../storage-interface/src/finality_view.rs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 961e38bb7e12d..a980aef20b652 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -71,3 +71,48 @@ impl DbReader for FinalityView { // TODO: override any other methods needed to maintain the illusion. } + +#[cfg(test)] +mod tests { + use aptos_types::{aggregate_signature::AggregateSignature, ledger_info::LedgerInfo}; + + use super::*; + use crate::mock::MockDbReaderWriter; + + #[test] + fn test_get_latest_ledger_info() { + let view = FinalityView::new(MockDbReaderWriter); + let ledger_info = view.get_latest_ledger_info_option().unwrap(); + assert_eq!(ledger_info, None); + let fin_ledger_info = + LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); + view.set_finalized_ledger_info(fin_ledger_info.clone()) + .unwrap(); + let ledger_info = view.get_latest_ledger_info().unwrap(); + assert_eq!(ledger_info, fin_ledger_info); + } + + #[test] + fn test_get_latest_version() { + let view = FinalityView::new(MockDbReaderWriter); + let res = view.get_latest_version(); + assert!(res.is_err()); + let fin_ledger_info = + LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); + view.set_finalized_ledger_info(fin_ledger_info).unwrap(); + let version = view.get_latest_version().unwrap(); + assert_eq!(version, 0); + } + + #[test] + fn test_get_latest_state_checkpoint_version() { + let view = FinalityView::new(MockDbReaderWriter); + let version = view.get_latest_state_checkpoint_version().unwrap(); + assert_eq!(version, None); + let fin_ledger_info = + LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); + view.set_finalized_ledger_info(fin_ledger_info).unwrap(); + let version = view.get_latest_state_checkpoint_version().unwrap(); + assert_eq!(version, Some(0)); + } +} From 9089a3b0bb692623331fe1ad7c75f6eb21acf2e9 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 3 Jun 2024 09:39:19 +0200 Subject: [PATCH 074/147] storage-interface: blanket impl DbReader for Arc --- storage/storage-interface/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index 4f4b254d501cf..b359cda9ad9d4 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -484,6 +484,12 @@ pub trait DbReader: Send + Sync { } } +impl DbReader for Arc { + fn get_read_delegatee(&self) -> &dyn DbReader { + self + } +} + impl MoveStorage for &dyn DbReader { fn fetch_resource_by_version( &self, From 44a86dae7ff54f5a20c042abf1da23e38870f617 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 4 Jun 2024 17:20:11 +0200 Subject: [PATCH 075/147] update set ledger info method, update tests --- .../storage-interface/src/finality_view.rs | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index a980aef20b652..c1d6462d9a079 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -1,6 +1,12 @@ use std::sync::RwLock; -use aptos_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version}; +use aptos_crypto::HashValue; +use aptos_types::{ + aggregate_signature, + block_info::BlockInfo, + ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, + transaction::Version, +}; use crate::{AptosDbError, DbReader, Result}; @@ -22,21 +28,33 @@ impl FinalityView { impl FinalityView { /// Updates the information on the latest finalized block's ledger. - pub fn set_finalized_ledger_info(&self, ledger_info: LedgerInfoWithSignatures) -> Result<()> { + pub fn set_finalized_ledger_info(&self, height: u64) -> Result<()> { + let (_start_ver, end_ver, block_event) = self.get_block_info_by_height(height)?; + let block_info = BlockInfo::new( + block_event.epoch(), + block_event.round(), + block_event.hash()?, + self.get_accumulator_root_hash(end_ver)?, + end_ver, + block_event.proposed_time(), + None, + ); // Sanity checks: finalization should not be set on an empty database, // the finality version should not exceed the latest committed. match self.reader.get_latest_state_checkpoint_version()? { None => return Err(AptosDbError::Other("no ledger states to finalize".into())), Some(ver) => { - let fin_version = ledger_info.ledger_info().version(); - if fin_version > ver { + if end_ver > ver { return Err(AptosDbError::Other(format!( - "finality version {fin_version} exceeds committed version {ver}" + "finality version {end_ver} exceeds committed version {ver}" ))); } }, } - + let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); // we don't use consensus_data_hash + let aggregate_signature = aggregate_signature::AggregateSignature::empty(); // we don't use + // aggregate_signatures + let ledger_info = LedgerInfoWithSignatures::new(ledger_info, aggregate_signature); let mut fin_legder_info = self.finalized_ledger_info.write().unwrap(); *fin_legder_info = Some(ledger_info); Ok(()) @@ -84,10 +102,10 @@ mod tests { let view = FinalityView::new(MockDbReaderWriter); let ledger_info = view.get_latest_ledger_info_option().unwrap(); assert_eq!(ledger_info, None); + let blockheight = 1; let fin_ledger_info = LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); - view.set_finalized_ledger_info(fin_ledger_info.clone()) - .unwrap(); + view.set_finalized_ledger_info(blockheight).unwrap(); let ledger_info = view.get_latest_ledger_info().unwrap(); assert_eq!(ledger_info, fin_ledger_info); } @@ -97,9 +115,8 @@ mod tests { let view = FinalityView::new(MockDbReaderWriter); let res = view.get_latest_version(); assert!(res.is_err()); - let fin_ledger_info = - LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); - view.set_finalized_ledger_info(fin_ledger_info).unwrap(); + let blockheight = 0; + view.set_finalized_ledger_info(blockheight).unwrap(); let version = view.get_latest_version().unwrap(); assert_eq!(version, 0); } @@ -111,8 +128,8 @@ mod tests { assert_eq!(version, None); let fin_ledger_info = LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); - view.set_finalized_ledger_info(fin_ledger_info).unwrap(); - let version = view.get_latest_state_checkpoint_version().unwrap(); - assert_eq!(version, Some(0)); + // view.set_finalized_ledger_info(fin_ledger_info).unwrap(); + // let version = view.get_latest_state_checkpoint_version().unwrap(); + // assert_eq!(version, Some(0)); } } From 54ab11bf239b315b1141dd266d0e569d7e7eb6aa Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 5 Jun 2024 10:24:33 +0200 Subject: [PATCH 076/147] update tests update set method --- .../storage-interface/src/finality_view.rs | 84 ++++++++++++++++--- storage/storage-interface/src/mock.rs | 25 ++++++ 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index c1d6462d9a079..67376eac59f52 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -29,12 +29,12 @@ impl FinalityView { impl FinalityView { /// Updates the information on the latest finalized block's ledger. pub fn set_finalized_ledger_info(&self, height: u64) -> Result<()> { - let (_start_ver, end_ver, block_event) = self.get_block_info_by_height(height)?; + let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; let block_info = BlockInfo::new( block_event.epoch(), block_event.round(), block_event.hash()?, - self.get_accumulator_root_hash(end_ver)?, + self.reader.get_accumulator_root_hash(end_ver)?, end_ver, block_event.proposed_time(), None, @@ -100,14 +100,75 @@ mod tests { #[test] fn test_get_latest_ledger_info() { let view = FinalityView::new(MockDbReaderWriter); + let ledger_info = view.get_latest_ledger_info_option().unwrap(); assert_eq!(ledger_info, None); let blockheight = 1; - let fin_ledger_info = - LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); + + // Set the finalized ledger info view.set_finalized_ledger_info(blockheight).unwrap(); - let ledger_info = view.get_latest_ledger_info().unwrap(); - assert_eq!(ledger_info, fin_ledger_info); + + // Capture the block event once + let (_start_ver, end_ver, block_event) = + view.get_block_info_by_height(blockheight).unwrap(); + let block_hash = block_event.hash().unwrap(); // Used to verify hash is generated + + let block_info = BlockInfo::new( + block_event.epoch(), + block_event.round(), + block_hash, + HashValue::zero(), + end_ver, + block_event.proposed_time(), + None, + ); + let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); + let expected_ledger_info = + LedgerInfoWithSignatures::new(ledger_info, AggregateSignature::empty()); + + // Get the latest ledger info after setting it + let ledger_info = view.get_latest_ledger_info_option().unwrap().unwrap(); + + // We assert every field except the hash because there is no way + // to predict an expected hash value + assert_eq!( + ledger_info.ledger_info().commit_info().epoch(), + expected_ledger_info.ledger_info().commit_info().epoch() + ); + assert_eq!( + ledger_info.ledger_info().commit_info().round(), + expected_ledger_info.ledger_info().commit_info().round() + ); + assert_eq!( + ledger_info.ledger_info().commit_info().version(), + expected_ledger_info.ledger_info().commit_info().version() + ); + assert_eq!( + ledger_info.ledger_info().commit_info().timestamp_usecs(), + expected_ledger_info + .ledger_info() + .commit_info() + .timestamp_usecs() + ); + assert_eq!( + ledger_info.ledger_info().commit_info().executed_state_id(), + expected_ledger_info + .ledger_info() + .commit_info() + .executed_state_id() + ); + assert_eq!( + ledger_info.ledger_info().commit_info().next_epoch_state(), + expected_ledger_info + .ledger_info() + .commit_info() + .next_epoch_state() + ); + assert_eq!( + ledger_info.ledger_info().consensus_data_hash(), + expected_ledger_info.ledger_info().consensus_data_hash() + ); + assert_eq!(ledger_info.signatures(), expected_ledger_info.signatures()); } #[test] @@ -115,10 +176,10 @@ mod tests { let view = FinalityView::new(MockDbReaderWriter); let res = view.get_latest_version(); assert!(res.is_err()); - let blockheight = 0; + let blockheight = 1; view.set_finalized_ledger_info(blockheight).unwrap(); let version = view.get_latest_version().unwrap(); - assert_eq!(version, 0); + assert_eq!(version, 1); } #[test] @@ -128,8 +189,9 @@ mod tests { assert_eq!(version, None); let fin_ledger_info = LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); - // view.set_finalized_ledger_info(fin_ledger_info).unwrap(); - // let version = view.get_latest_state_checkpoint_version().unwrap(); - // assert_eq!(version, Some(0)); + let blockheight = 1; + view.set_finalized_ledger_info(1).unwrap(); + let version = view.get_latest_state_checkpoint_version().unwrap(); + assert_eq!(version, Some(1)); } } diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs index ccb34471d8dc1..e413b043dec78 100644 --- a/storage/storage-interface/src/mock.rs +++ b/storage/storage-interface/src/mock.rs @@ -5,6 +5,7 @@ //! This module provides mock dbreader for tests. use crate::{errors::AptosDbError, DbReader, DbWriter, Result}; +use aptos_crypto::HashValue; use aptos_types::{ account_address::AccountAddress, account_config::AccountResource, @@ -66,6 +67,30 @@ impl DbReader for MockDbReaderWriter { .get_state_value_by_version(state_key, version)? .map(|value| (version, value))) } + + fn get_block_info_by_height( + &self, + height: u64, + ) -> Result<(Version, Version, aptos_types::account_config::NewBlockEvent)> { + Ok(( + 0, + 1, + aptos_types::account_config::NewBlockEvent::new( + AccountAddress::random(), + 0, + 0, + height, + vec![], + AccountAddress::random(), + vec![], + 0, + ), + )) + } + + fn get_accumulator_root_hash(&self, version: Version) -> Result { + Ok(HashValue::zero()) + } } fn get_mock_account_state() -> AccountState { From fc037ca238c6b730269214cab9b36fcf4de3dde6 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 5 Jun 2024 11:38:59 +0200 Subject: [PATCH 077/147] storage-interface: fixed up finality_view tests No randomness allows us to verify the hash. --- .../storage-interface/src/finality_view.rs | 53 +++---------------- storage/storage-interface/src/mock.rs | 6 +-- 2 files changed, 11 insertions(+), 48 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 67376eac59f52..7cc3e9efe29bb 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -30,10 +30,12 @@ impl FinalityView { /// Updates the information on the latest finalized block's ledger. pub fn set_finalized_ledger_info(&self, height: u64) -> Result<()> { let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; + let block_hash = block_event.hash()?; + dbg!(block_hash); let block_info = BlockInfo::new( block_event.epoch(), block_event.round(), - block_event.hash()?, + block_hash, self.reader.get_accumulator_root_hash(end_ver)?, end_ver, block_event.proposed_time(), @@ -99,6 +101,9 @@ mod tests { #[test] fn test_get_latest_ledger_info() { + // If the mock is changed to be stateful, this should be ref-counted + // and shared with the view. + let mock = MockDbReaderWriter; let view = FinalityView::new(MockDbReaderWriter); let ledger_info = view.get_latest_ledger_info_option().unwrap(); @@ -110,7 +115,7 @@ mod tests { // Capture the block event once let (_start_ver, end_ver, block_event) = - view.get_block_info_by_height(blockheight).unwrap(); + mock.get_block_info_by_height(blockheight).unwrap(); let block_hash = block_event.hash().unwrap(); // Used to verify hash is generated let block_info = BlockInfo::new( @@ -129,46 +134,7 @@ mod tests { // Get the latest ledger info after setting it let ledger_info = view.get_latest_ledger_info_option().unwrap().unwrap(); - // We assert every field except the hash because there is no way - // to predict an expected hash value - assert_eq!( - ledger_info.ledger_info().commit_info().epoch(), - expected_ledger_info.ledger_info().commit_info().epoch() - ); - assert_eq!( - ledger_info.ledger_info().commit_info().round(), - expected_ledger_info.ledger_info().commit_info().round() - ); - assert_eq!( - ledger_info.ledger_info().commit_info().version(), - expected_ledger_info.ledger_info().commit_info().version() - ); - assert_eq!( - ledger_info.ledger_info().commit_info().timestamp_usecs(), - expected_ledger_info - .ledger_info() - .commit_info() - .timestamp_usecs() - ); - assert_eq!( - ledger_info.ledger_info().commit_info().executed_state_id(), - expected_ledger_info - .ledger_info() - .commit_info() - .executed_state_id() - ); - assert_eq!( - ledger_info.ledger_info().commit_info().next_epoch_state(), - expected_ledger_info - .ledger_info() - .commit_info() - .next_epoch_state() - ); - assert_eq!( - ledger_info.ledger_info().consensus_data_hash(), - expected_ledger_info.ledger_info().consensus_data_hash() - ); - assert_eq!(ledger_info.signatures(), expected_ledger_info.signatures()); + assert_eq!(ledger_info, expected_ledger_info); } #[test] @@ -187,9 +153,6 @@ mod tests { let view = FinalityView::new(MockDbReaderWriter); let version = view.get_latest_state_checkpoint_version().unwrap(); assert_eq!(version, None); - let fin_ledger_info = - LedgerInfoWithSignatures::new(LedgerInfo::dummy(), AggregateSignature::empty()); - let blockheight = 1; view.set_finalized_ledger_info(1).unwrap(); let version = view.get_latest_state_checkpoint_version().unwrap(); assert_eq!(version, Some(1)); diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs index e413b043dec78..d9156a7150760 100644 --- a/storage/storage-interface/src/mock.rs +++ b/storage/storage-interface/src/mock.rs @@ -76,19 +76,19 @@ impl DbReader for MockDbReaderWriter { 0, 1, aptos_types::account_config::NewBlockEvent::new( - AccountAddress::random(), + AccountAddress::ONE, 0, 0, height, vec![], - AccountAddress::random(), + AccountAddress::TWO, vec![], 0, ), )) } - fn get_accumulator_root_hash(&self, version: Version) -> Result { + fn get_accumulator_root_hash(&self, _version: Version) -> Result { Ok(HashValue::zero()) } } From 9b2099448027caa0408bf9fce870278f44694e60 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 5 Jun 2024 12:45:23 +0200 Subject: [PATCH 078/147] storage-interface: fix DbReader impl for Arc get_read_delegatee needs to deref to get the vtable for the pointee type. --- storage/storage-interface/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index b359cda9ad9d4..ee80b269eff46 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -484,9 +484,9 @@ pub trait DbReader: Send + Sync { } } -impl DbReader for Arc { +impl DbReader for Arc { fn get_read_delegatee(&self) -> &dyn DbReader { - self + &**self } } From d1a2d26568a8804b2d73d4a25b5545c8e604a6fc Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 5 Jun 2024 12:55:07 +0200 Subject: [PATCH 079/147] storage-interface: rename setter for FinalityView FinalityView::set_finalized_block_height better describes the argument. --- storage/storage-interface/src/finality_view.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 7cc3e9efe29bb..07b649d230fac 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -27,8 +27,8 @@ impl FinalityView { } impl FinalityView { - /// Updates the information on the latest finalized block's ledger. - pub fn set_finalized_ledger_info(&self, height: u64) -> Result<()> { + /// Updates the information on the latest finalized block at the specified height. + pub fn set_finalized_block_height(&self, height: u64) -> Result<()> { let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; let block_hash = block_event.hash()?; dbg!(block_hash); @@ -111,7 +111,7 @@ mod tests { let blockheight = 1; // Set the finalized ledger info - view.set_finalized_ledger_info(blockheight).unwrap(); + view.set_finalized_block_height(blockheight).unwrap(); // Capture the block event once let (_start_ver, end_ver, block_event) = @@ -143,7 +143,7 @@ mod tests { let res = view.get_latest_version(); assert!(res.is_err()); let blockheight = 1; - view.set_finalized_ledger_info(blockheight).unwrap(); + view.set_finalized_block_height(blockheight).unwrap(); let version = view.get_latest_version().unwrap(); assert_eq!(version, 1); } @@ -153,7 +153,7 @@ mod tests { let view = FinalityView::new(MockDbReaderWriter); let version = view.get_latest_state_checkpoint_version().unwrap(); assert_eq!(version, None); - view.set_finalized_ledger_info(1).unwrap(); + view.set_finalized_block_height(1).unwrap(); let version = view.get_latest_state_checkpoint_version().unwrap(); assert_eq!(version, Some(1)); } From dfbb57061ea22822b681547bb468ef6f2131f557 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 6 Jun 2024 11:20:13 +0200 Subject: [PATCH 080/147] storage-interface: remove a dbg printout --- storage/storage-interface/src/finality_view.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 07b649d230fac..cd31f2037df5a 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -31,7 +31,6 @@ impl FinalityView { pub fn set_finalized_block_height(&self, height: u64) -> Result<()> { let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; let block_hash = block_event.hash()?; - dbg!(block_hash); let block_info = BlockInfo::new( block_event.epoch(), block_event.round(), From 3797a33aa633dc6cd3dc6063692fc924cf7b3630 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 6 Jun 2024 11:31:07 +0200 Subject: [PATCH 081/147] Revert "fix: debugging transaction_by hash issue." This reverts commit 7f3e0e5c93d2b208f421445e3fd7ecdad8d0a4dd. --- api/src/transactions.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/api/src/transactions.rs b/api/src/transactions.rs index a914ec567c487..5b9256e30c593 100644 --- a/api/src/transactions.rs +++ b/api/src/transactions.rs @@ -837,15 +837,11 @@ impl TransactionsApi { .context("Failed to join task to read transaction by hash")? .context("Failed to read transaction by hash from DB")?; Ok(match from_db { - None => { - let res = self + None => self .context .get_pending_transaction_by_hash(hash) .await? - .map(|t| t.into()); - println!("pending_transaction: {:?}", res); - res - }, + .map(|t| t.into()), _ => from_db.map(|t| t.into()), }) } From 1c33fd19f4e05637eebd5dab148a7cb98abdf666 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 6 Jun 2024 16:58:17 +0200 Subject: [PATCH 082/147] storage-interface: finality_view tests ret Result Use ? for terser, idiomatic code that can be cribbed for production. --- .../storage-interface/src/finality_view.rs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index cd31f2037df5a..d1e2de769442e 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -99,23 +99,23 @@ mod tests { use crate::mock::MockDbReaderWriter; #[test] - fn test_get_latest_ledger_info() { + fn test_get_latest_ledger_info() -> anyhow::Result<()> { // If the mock is changed to be stateful, this should be ref-counted // and shared with the view. let mock = MockDbReaderWriter; let view = FinalityView::new(MockDbReaderWriter); - let ledger_info = view.get_latest_ledger_info_option().unwrap(); + let ledger_info = view.get_latest_ledger_info_option()?; assert_eq!(ledger_info, None); let blockheight = 1; // Set the finalized ledger info - view.set_finalized_block_height(blockheight).unwrap(); + view.set_finalized_block_height(blockheight)?; // Capture the block event once let (_start_ver, end_ver, block_event) = - mock.get_block_info_by_height(blockheight).unwrap(); - let block_hash = block_event.hash().unwrap(); // Used to verify hash is generated + mock.get_block_info_by_height(blockheight)?; + let block_hash = block_event.hash()?; // Used to verify hash is generated let block_info = BlockInfo::new( block_event.epoch(), @@ -131,29 +131,33 @@ mod tests { LedgerInfoWithSignatures::new(ledger_info, AggregateSignature::empty()); // Get the latest ledger info after setting it - let ledger_info = view.get_latest_ledger_info_option().unwrap().unwrap(); + let ledger_info = view.get_latest_ledger_info_option()?.unwrap(); assert_eq!(ledger_info, expected_ledger_info); + + Ok(()) } #[test] - fn test_get_latest_version() { + fn test_get_latest_version() -> anyhow::Result<()> { let view = FinalityView::new(MockDbReaderWriter); let res = view.get_latest_version(); assert!(res.is_err()); let blockheight = 1; - view.set_finalized_block_height(blockheight).unwrap(); - let version = view.get_latest_version().unwrap(); + view.set_finalized_block_height(blockheight)?; + let version = view.get_latest_version()?; assert_eq!(version, 1); + Ok(()) } #[test] - fn test_get_latest_state_checkpoint_version() { + fn test_get_latest_state_checkpoint_version() -> Result<()> { let view = FinalityView::new(MockDbReaderWriter); - let version = view.get_latest_state_checkpoint_version().unwrap(); + let version = view.get_latest_state_checkpoint_version()?; assert_eq!(version, None); - view.set_finalized_block_height(1).unwrap(); - let version = view.get_latest_state_checkpoint_version().unwrap(); + view.set_finalized_block_height(1)?; + let version = view.get_latest_state_checkpoint_version()?; assert_eq!(version, Some(1)); + Ok(()) } } From dee7114f9901f1a732003bd16fbca79a5b91bb0e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 6 Jun 2024 18:40:00 +0200 Subject: [PATCH 083/147] storage-interface: latest state vew test Draft a test for FinalityView, checking that the latest_state_checkpoint_view extension method gets us the view into finalized state. StateView support in the mock is needed to complete the test. --- storage/storage-interface/src/finality_view.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index d1e2de769442e..755ce71dbb9d0 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -93,10 +93,12 @@ impl DbReader for FinalityView { #[cfg(test)] mod tests { + use std::sync::Arc; + use aptos_types::{aggregate_signature::AggregateSignature, ledger_info::LedgerInfo}; use super::*; - use crate::mock::MockDbReaderWriter; + use crate::{mock::MockDbReaderWriter, state_view::LatestDbStateCheckpointView as _}; #[test] fn test_get_latest_ledger_info() -> anyhow::Result<()> { @@ -160,4 +162,14 @@ mod tests { assert_eq!(version, Some(1)); Ok(()) } + + #[test] + fn test_latest_state_checkpoint_view() -> anyhow::Result<()> { + let view = Arc::new(FinalityView::new(MockDbReaderWriter)); + let reader: Arc = view.clone(); + view.set_finalized_block_height(0)?; + let _latest_state_view = reader.latest_state_checkpoint_view()?; + // TODO: get some states available from the mock + Ok(()) + } } From b5688df029897ec82a4870872726a21f83af8ca3 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 7 Jun 2024 10:24:08 +0200 Subject: [PATCH 084/147] storage-interface: clean up and comment In FinalityView::set_finalized_block_height, remove the outdated sanity check, add comments explaining why we can use the empty hash and no signatures specifically for Movement. --- .../storage-interface/src/finality_view.rs | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 755ce71dbb9d0..5df7a2c7e22e5 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -40,21 +40,13 @@ impl FinalityView { block_event.proposed_time(), None, ); - // Sanity checks: finalization should not be set on an empty database, - // the finality version should not exceed the latest committed. - match self.reader.get_latest_state_checkpoint_version()? { - None => return Err(AptosDbError::Other("no ledger states to finalize".into())), - Some(ver) => { - if end_ver > ver { - return Err(AptosDbError::Other(format!( - "finality version {end_ver} exceeds committed version {ver}" - ))); - } - }, - } - let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); // we don't use consensus_data_hash - let aggregate_signature = aggregate_signature::AggregateSignature::empty(); // we don't use - // aggregate_signatures + // FinalityView is created for Movement, where we don't use the consensus hash + // or the ledger info signatures. So we leave them empty here and can still construct + // a valid ledger info for the view. + // In a more general implementation, this API could accept LedgerInfoWithSignatures + // which is either preserved from an earlier version, or fudged like in our case. + let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); + let aggregate_signature = aggregate_signature::AggregateSignature::empty(); let ledger_info = LedgerInfoWithSignatures::new(ledger_info, aggregate_signature); let mut fin_legder_info = self.finalized_ledger_info.write().unwrap(); *fin_legder_info = Some(ledger_info); From 1731cce36e3810d4034240deeefffd3745f8dedc Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 7 Jun 2024 13:18:07 +0200 Subject: [PATCH 085/147] storage: improve FinalityView set method For set_finalized_block_height, retrieve ledger info from the corresponding epoch or use the latest. --- .../storage-interface/src/finality_view.rs | 64 ++++--------------- storage/storage-interface/src/mock.rs | 40 ++++++++++-- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index 5df7a2c7e22e5..a8bae11e5ff56 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -1,12 +1,6 @@ use std::sync::RwLock; -use aptos_crypto::HashValue; -use aptos_types::{ - aggregate_signature, - block_info::BlockInfo, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, - transaction::Version, -}; +use aptos_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version}; use crate::{AptosDbError, DbReader, Result}; @@ -27,27 +21,15 @@ impl FinalityView { } impl FinalityView { - /// Updates the information on the latest finalized block at the specified height. + /// Updates the view for the latest finalized block at the specified height. pub fn set_finalized_block_height(&self, height: u64) -> Result<()> { - let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; - let block_hash = block_event.hash()?; - let block_info = BlockInfo::new( - block_event.epoch(), - block_event.round(), - block_hash, - self.reader.get_accumulator_root_hash(end_ver)?, - end_ver, - block_event.proposed_time(), - None, - ); - // FinalityView is created for Movement, where we don't use the consensus hash - // or the ledger info signatures. So we leave them empty here and can still construct - // a valid ledger info for the view. - // In a more general implementation, this API could accept LedgerInfoWithSignatures - // which is either preserved from an earlier version, or fudged like in our case. - let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); - let aggregate_signature = aggregate_signature::AggregateSignature::empty(); - let ledger_info = LedgerInfoWithSignatures::new(ledger_info, aggregate_signature); + let (start_ver, _, _) = self.reader.get_block_info_by_height(height)?; + let ledger_info = match self.reader.get_epoch_ending_ledger_info(start_ver) { + Ok(li) => li, + Err(AptosDbError::NotFound(_)) => self.reader.get_latest_ledger_info()?, + Err(e) => return Err(e), + }; + let mut fin_legder_info = self.finalized_ledger_info.write().unwrap(); *fin_legder_info = Some(ledger_info); Ok(()) @@ -87,16 +69,13 @@ impl DbReader for FinalityView { mod tests { use std::sync::Arc; - use aptos_types::{aggregate_signature::AggregateSignature, ledger_info::LedgerInfo}; + use aptos_crypto::HashValue; use super::*; use crate::{mock::MockDbReaderWriter, state_view::LatestDbStateCheckpointView as _}; #[test] fn test_get_latest_ledger_info() -> anyhow::Result<()> { - // If the mock is changed to be stateful, this should be ref-counted - // and shared with the view. - let mock = MockDbReaderWriter; let view = FinalityView::new(MockDbReaderWriter); let ledger_info = view.get_latest_ledger_info_option()?; @@ -106,28 +85,13 @@ mod tests { // Set the finalized ledger info view.set_finalized_block_height(blockheight)?; - // Capture the block event once - let (_start_ver, end_ver, block_event) = - mock.get_block_info_by_height(blockheight)?; - let block_hash = block_event.hash()?; // Used to verify hash is generated - - let block_info = BlockInfo::new( - block_event.epoch(), - block_event.round(), - block_hash, - HashValue::zero(), - end_ver, - block_event.proposed_time(), - None, - ); - let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); - let expected_ledger_info = - LedgerInfoWithSignatures::new(ledger_info, AggregateSignature::empty()); - // Get the latest ledger info after setting it let ledger_info = view.get_latest_ledger_info_option()?.unwrap(); - assert_eq!(ledger_info, expected_ledger_info); + assert_eq!( + ledger_info.ledger_info().commit_info().id(), + HashValue::new([1; HashValue::LENGTH]), + ); Ok(()) } diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs index d9156a7150760..1af11e461ad61 100644 --- a/storage/storage-interface/src/mock.rs +++ b/storage/storage-interface/src/mock.rs @@ -8,15 +8,20 @@ use crate::{errors::AptosDbError, DbReader, DbWriter, Result}; use aptos_crypto::HashValue; use aptos_types::{ account_address::AccountAddress, - account_config::AccountResource, + account_config::{AccountResource, NewBlockEvent}, account_state::AccountState, + aggregate_signature::AggregateSignature, + block_info::BlockInfo, + epoch_state::EpochState, event::EventHandle, + ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, proof::SparseMerkleProofExt, state_store::{ state_key::{StateKey, StateKeyInner}, state_value::StateValue, }, transaction::Version, + validator_verifier::ValidatorVerifier, }; use move_core_types::move_resource::MoveResource; @@ -68,14 +73,37 @@ impl DbReader for MockDbReaderWriter { .map(|value| (version, value))) } - fn get_block_info_by_height( - &self, - height: u64, - ) -> Result<(Version, Version, aptos_types::account_config::NewBlockEvent)> { + fn get_epoch_ending_ledger_info(&self, known_version: u64) -> Result { + if known_version == 0 { + let next_epoch_state = EpochState { + epoch: 1, + verifier: ValidatorVerifier::new(vec![]), + }; + let block_info = BlockInfo::new( + 0, + 0, + HashValue::new([1; HashValue::LENGTH]), + HashValue::zero(), + 1, + 1717757545265, + Some(next_epoch_state), + ); + Ok(LedgerInfoWithSignatures::new( + LedgerInfo::new(block_info, HashValue::zero()), + AggregateSignature::empty(), + )) + } else { + Err(AptosDbError::NotFound(format!( + "mock ledger info for version {known_version}" + ))) + } + } + + fn get_block_info_by_height(&self, height: u64) -> Result<(Version, Version, NewBlockEvent)> { Ok(( 0, 1, - aptos_types::account_config::NewBlockEvent::new( + NewBlockEvent::new( AccountAddress::ONE, 0, 0, From 38dcecc2fb987586017e92de778feff6651f8986 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 7 Jun 2024 14:01:06 +0200 Subject: [PATCH 086/147] storage: more FinalityView testing Exercise the DbStateView instance returned from latest_state_checkpoint_view. No testing yet if the state matches expectations for the finalized version. --- storage/storage-interface/src/finality_view.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index a8bae11e5ff56..a2cbd62ad305e 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -70,6 +70,7 @@ mod tests { use std::sync::Arc; use aptos_crypto::HashValue; + use aptos_types::state_store::{state_key::StateKey, TStateView as _}; use super::*; use crate::{mock::MockDbReaderWriter, state_view::LatestDbStateCheckpointView as _}; @@ -123,9 +124,12 @@ mod tests { fn test_latest_state_checkpoint_view() -> anyhow::Result<()> { let view = Arc::new(FinalityView::new(MockDbReaderWriter)); let reader: Arc = view.clone(); - view.set_finalized_block_height(0)?; - let _latest_state_view = reader.latest_state_checkpoint_view()?; - // TODO: get some states available from the mock + view.set_finalized_block_height(1)?; + let latest_state_view = reader.latest_state_checkpoint_view()?; + // TODO: modify mock so we get different states for different versions + let key = StateKey::raw(vec![1]); + let value = latest_state_view.get_state_value(&key)?.unwrap(); + assert_eq!(value.bytes(), &vec![1]); Ok(()) } } From bf51211444855e7d42de27212d1e712a02bba3ab Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 7 Jun 2024 15:19:07 +0200 Subject: [PATCH 087/147] Revert "storage: improve FinalityView set method" This reverts commit 1731cce36e3810d4034240deeefffd3745f8dedc. --- .../storage-interface/src/finality_view.rs | 62 +++++++++++++++---- storage/storage-interface/src/mock.rs | 40 ++---------- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/storage/storage-interface/src/finality_view.rs b/storage/storage-interface/src/finality_view.rs index a2cbd62ad305e..eafaec939277f 100644 --- a/storage/storage-interface/src/finality_view.rs +++ b/storage/storage-interface/src/finality_view.rs @@ -1,6 +1,12 @@ use std::sync::RwLock; -use aptos_types::{ledger_info::LedgerInfoWithSignatures, transaction::Version}; +use aptos_crypto::HashValue; +use aptos_types::{ + aggregate_signature::AggregateSignature, + block_info::BlockInfo, + ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, + transaction::Version, +}; use crate::{AptosDbError, DbReader, Result}; @@ -21,15 +27,27 @@ impl FinalityView { } impl FinalityView { - /// Updates the view for the latest finalized block at the specified height. + /// Updates the information on the latest finalized block at the specified height. pub fn set_finalized_block_height(&self, height: u64) -> Result<()> { - let (start_ver, _, _) = self.reader.get_block_info_by_height(height)?; - let ledger_info = match self.reader.get_epoch_ending_ledger_info(start_ver) { - Ok(li) => li, - Err(AptosDbError::NotFound(_)) => self.reader.get_latest_ledger_info()?, - Err(e) => return Err(e), - }; - + let (_start_ver, end_ver, block_event) = self.reader.get_block_info_by_height(height)?; + let block_hash = block_event.hash()?; + let block_info = BlockInfo::new( + block_event.epoch(), + block_event.round(), + block_hash, + self.reader.get_accumulator_root_hash(end_ver)?, + end_ver, + block_event.proposed_time(), + None, + ); + // FinalityView is created for Movement, where we don't use the consensus hash + // or the ledger info signatures. So we leave them empty here and can still construct + // a valid ledger info for the view. + // In a more general implementation, this API could accept LedgerInfoWithSignatures + // which is either preserved from an earlier version, or fudged like in our case. + let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); + let aggregate_signature = AggregateSignature::empty(); + let ledger_info = LedgerInfoWithSignatures::new(ledger_info, aggregate_signature); let mut fin_legder_info = self.finalized_ledger_info.write().unwrap(); *fin_legder_info = Some(ledger_info); Ok(()) @@ -77,6 +95,9 @@ mod tests { #[test] fn test_get_latest_ledger_info() -> anyhow::Result<()> { + // If the mock is changed to be stateful, this should be ref-counted + // and shared with the view. + let mock = MockDbReaderWriter; let view = FinalityView::new(MockDbReaderWriter); let ledger_info = view.get_latest_ledger_info_option()?; @@ -86,13 +107,28 @@ mod tests { // Set the finalized ledger info view.set_finalized_block_height(blockheight)?; + // Capture the block event once + let (_start_ver, end_ver, block_event) = + mock.get_block_info_by_height(blockheight)?; + let block_hash = block_event.hash()?; // Used to verify hash is generated + + let block_info = BlockInfo::new( + block_event.epoch(), + block_event.round(), + block_hash, + HashValue::zero(), + end_ver, + block_event.proposed_time(), + None, + ); + let ledger_info = LedgerInfo::new(block_info, HashValue::zero()); + let expected_ledger_info = + LedgerInfoWithSignatures::new(ledger_info, AggregateSignature::empty()); + // Get the latest ledger info after setting it let ledger_info = view.get_latest_ledger_info_option()?.unwrap(); - assert_eq!( - ledger_info.ledger_info().commit_info().id(), - HashValue::new([1; HashValue::LENGTH]), - ); + assert_eq!(ledger_info, expected_ledger_info); Ok(()) } diff --git a/storage/storage-interface/src/mock.rs b/storage/storage-interface/src/mock.rs index 1af11e461ad61..d9156a7150760 100644 --- a/storage/storage-interface/src/mock.rs +++ b/storage/storage-interface/src/mock.rs @@ -8,20 +8,15 @@ use crate::{errors::AptosDbError, DbReader, DbWriter, Result}; use aptos_crypto::HashValue; use aptos_types::{ account_address::AccountAddress, - account_config::{AccountResource, NewBlockEvent}, + account_config::AccountResource, account_state::AccountState, - aggregate_signature::AggregateSignature, - block_info::BlockInfo, - epoch_state::EpochState, event::EventHandle, - ledger_info::{LedgerInfo, LedgerInfoWithSignatures}, proof::SparseMerkleProofExt, state_store::{ state_key::{StateKey, StateKeyInner}, state_value::StateValue, }, transaction::Version, - validator_verifier::ValidatorVerifier, }; use move_core_types::move_resource::MoveResource; @@ -73,37 +68,14 @@ impl DbReader for MockDbReaderWriter { .map(|value| (version, value))) } - fn get_epoch_ending_ledger_info(&self, known_version: u64) -> Result { - if known_version == 0 { - let next_epoch_state = EpochState { - epoch: 1, - verifier: ValidatorVerifier::new(vec![]), - }; - let block_info = BlockInfo::new( - 0, - 0, - HashValue::new([1; HashValue::LENGTH]), - HashValue::zero(), - 1, - 1717757545265, - Some(next_epoch_state), - ); - Ok(LedgerInfoWithSignatures::new( - LedgerInfo::new(block_info, HashValue::zero()), - AggregateSignature::empty(), - )) - } else { - Err(AptosDbError::NotFound(format!( - "mock ledger info for version {known_version}" - ))) - } - } - - fn get_block_info_by_height(&self, height: u64) -> Result<(Version, Version, NewBlockEvent)> { + fn get_block_info_by_height( + &self, + height: u64, + ) -> Result<(Version, Version, aptos_types::account_config::NewBlockEvent)> { Ok(( 0, 1, - NewBlockEvent::new( + aptos_types::account_config::NewBlockEvent::new( AccountAddress::ONE, 0, 0, From b2be48ead99727f51cab7e24d81cb09284166463 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Fri, 7 Jun 2024 18:43:19 +0200 Subject: [PATCH 088/147] api: expose AccountsApi::get_account_inner Access to account state is convenient to test with. --- api/src/accounts.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/api/src/accounts.rs b/api/src/accounts.rs index 4cfa4bc398de8..a6cd2a16ed7b4 100644 --- a/api/src/accounts.rs +++ b/api/src/accounts.rs @@ -67,12 +67,7 @@ impl AccountsApi { self.context .check_api_output_enabled("Get account", &accept_type)?; - let context = self.context.clone(); - api_spawn_blocking(move || { - let account = Account::new(context, address.0, ledger_version.0, None, None)?; - account.account(&accept_type) - }) - .await + self.get_account_inner(accept_type, address.0, ledger_version.0).await } /// Get account resources @@ -180,6 +175,22 @@ impl AccountsApi { } } +impl AccountsApi { + pub async fn get_account_inner( + &self, + accept_type: AcceptType, + address: Address, + ledger_version: Option, + ) -> BasicResultWith404 { + let context = self.context.clone(); + api_spawn_blocking(move || { + let account = Account::new(context, address, ledger_version, None, None)?; + account.account(&accept_type) + }) + .await + } +} + /// A struct representing Account related lookups for resources and modules pub struct Account { context: Arc, From 27c5f780ec881d93dda2879e303323399e958d80 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Wed, 12 Jun 2024 11:36:35 +0200 Subject: [PATCH 089/147] chore: api_connection_config should be public. --- crates/aptos-faucet/core/src/server/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/aptos-faucet/core/src/server/run.rs b/crates/aptos-faucet/core/src/server/run.rs index ace8bfce84ecd..6f56d75ef7239 100644 --- a/crates/aptos-faucet/core/src/server/run.rs +++ b/crates/aptos-faucet/core/src/server/run.rs @@ -323,7 +323,7 @@ impl Run { #[derive(Clone, Debug, Parser)] pub struct RunSimple { #[clap(flatten)] - api_connection_config: ApiConnectionConfig, + pub api_connection_config: ApiConnectionConfig, /// What address to listen on. #[clap(long, default_value = "0.0.0.0")] From 44e32af3fe83993c1fc0bb6355b2c2b3474d3ab8 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 13 Jun 2024 16:28:54 +0200 Subject: [PATCH 090/147] fix: pin diesel. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 413af6f3defdf..050208d25dc87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5957,9 +5957,9 @@ checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" [[package]] name = "diesel" -version = "2.1.4" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" +checksum = "d98235fdc2f355d330a8244184ab6b4b33c28679c0b4158f63138e51d6cf7e88" dependencies = [ "bigdecimal", "bitflags 2.4.1", diff --git a/Cargo.toml b/Cargo.toml index 66c6ce779e090..9ac14ff5da0bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -466,7 +466,7 @@ debug-ignore = { version = "1.0.3", features = ["serde"] } derivative = "2.2.0" determinator = "0.12.0" derive_more = "0.99.11" -diesel = "2.1" +diesel = "2.1.1" diesel-async = { version = "0.4", features = ["postgres", "tokio"] } diesel_migrations = { version = "2.1.0", features = ["postgres"] } digest = "0.9.0" From 16cf1490c99e42b02fecc90a154716860239af09 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Thu, 13 Jun 2024 16:34:41 +0200 Subject: [PATCH 091/147] fix: pin diesel. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9ac14ff5da0bd..53b639e00cd9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -466,7 +466,7 @@ debug-ignore = { version = "1.0.3", features = ["serde"] } derivative = "2.2.0" determinator = "0.12.0" derive_more = "0.99.11" -diesel = "2.1.1" +diesel = "=2.1.1" diesel-async = { version = "0.4", features = ["postgres", "tokio"] } diesel_migrations = { version = "2.1.0", features = ["postgres"] } digest = "0.9.0" From 496f5600b22de86ed99ab4bb122c9644338d71fb Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 14 Jun 2024 17:13:26 +0200 Subject: [PATCH 092/147] fix: checking write table output. --- crates/indexer/src/models/move_tables.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/indexer/src/models/move_tables.rs b/crates/indexer/src/models/move_tables.rs index 73a4629018268..12732d31dee1c 100644 --- a/crates/indexer/src/models/move_tables.rs +++ b/crates/indexer/src/models/move_tables.rs @@ -7,6 +7,7 @@ use crate::{ util::{hash_str, standardize_address}, }; use aptos_api_types::{DeleteTableItem, WriteTableItem}; +use aptos_logger::debug; use field_count::FieldCount; use serde::{Deserialize, Serialize}; @@ -56,6 +57,10 @@ impl TableItem { transaction_version: i64, transaction_block_height: i64, ) -> (Self, CurrentTableItem) { + debug!( + "Decoding WriteTableItem: {:?}", + write_table_item + ); ( Self { transaction_version, From bd2d1beb9f738e29cac1ac6396946a9c946d6dcd Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 20 Jun 2024 15:38:14 +0300 Subject: [PATCH 093/147] aptos-db: revert_commit improvements Pass LedgerInfoWithSignatures by reference like in other methods. Do not try to set epoch and version on the LedgerInfo in a way that does not work. Instead, it's expected to be correct for the latest pre-revert version. --- storage/aptosdb/src/db/aptosdb_test.rs | 26 ++++++------ .../aptosdb/src/db/include/aptosdb_writer.rs | 42 ++++++++----------- storage/storage-interface/src/lib.rs | 2 +- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 669dcde57ef12..11ca3a552eea2 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -246,7 +246,7 @@ fn create_signed_transaction(gas_unit_price: u64) -> SignedTransaction { } #[test] -fn test_revert_last_commit() { +fn test_revert_single_commit() { aptos_logger::Logger::new().init(); let tmp_dir = TempPath::new(); @@ -276,25 +276,23 @@ fn test_revert_last_commit() { assert_eq!(db.get_latest_version().unwrap(), expected_version); // Get the latest ledger info before revert - let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); - let root_hash = db - .get_latest_ledger_info() - .unwrap() + let latest_ledger_info_before_revert = blocks[1].1.clone(); + let root_hash = latest_ledger_info_before_revert .ledger_info() .commit_info() .executed_state_id(); + let version_to_revert = latest_ledger_info_before_revert.ledger_info().version() + 1; // Revert the last commit db.revert_commit( - db.get_latest_version().unwrap(), + version_to_revert, db.get_latest_version().unwrap(), // In this case the last commit and version to commit are same root_hash, // the hash is also the lastest - latest_ledger_info_before_revert, + &latest_ledger_info_before_revert, ) .unwrap(); - let exepcted_version = cur_ver - 2; - assert_eq!(db.get_latest_version().unwrap(), exepcted_version); + assert_eq!(db.get_latest_version().unwrap(), version_to_revert - 1); } #[test] @@ -323,7 +321,6 @@ fn test_revert_nth_commit() { let mut blockheight = 0; for (txns_to_commit, ledger_info_with_sigs) in &blocks { - println!("Blockheight: {}", blockheight); let first_version = cur_ver; update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); db.save_transactions_for_test( @@ -356,15 +353,16 @@ fn test_revert_nth_commit() { // Get the 3rd block back from the latest block let revert_block_num = blockheight - 3; let revert = committed_blocks.get(&revert_block_num).unwrap(); + let pre_revert_ledger_info = committed_blocks[&(revert_block_num - 1)].info.clone(); // Get the version to revert to - let version_to_revert = revert.first_version - 1; + let version_to_revert = revert.first_version; db.revert_commit( version_to_revert, db.get_latest_version().unwrap(), - revert.hash.clone(), - revert.info.clone(), + pre_revert_ledger_info.commit_info().executed_state_id(), + &pre_revert_ledger_info, ) .unwrap(); @@ -406,7 +404,7 @@ fn test_revert_commit_should_fail_with_wrong_hash() { last_committed_version, last_committed_version.clone(), // In this case the last commit and version to commit are the same HashValue::random(), // A wrong hash - latest_ledger_info_before_revert, + &latest_ledger_info_before_revert, ); assert!(result.is_err()); } diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 80d8b94f0744a..029cda0d04cc4 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -206,16 +206,24 @@ impl DbWriter for AptosDB { version_to_revert: Version, latest_version: Version, new_root_hash: HashValue, - ledger_info_with_sigs: LedgerInfoWithSignatures, + ledger_info_with_sigs: &LedgerInfoWithSignatures, ) -> Result<()> { let _timer = OTHER_TIMERS_SECONDS .with_label_values(&["revert_commit"]) .start_timer(); + + let target_version = version_to_revert.checked_sub(1).expect("cannot revert genesis"); + + ensure!( + ledger_info_with_sigs.ledger_info().version() == target_version, + "LedgerInfo is not for version {}", target_version, + ); + // Revert the ledger commit progress let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::LedgerCommitProgress, - &DbMetadataValue::Version(version_to_revert - 1), + &DbMetadataValue::Version(target_version), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -223,7 +231,7 @@ impl DbWriter for AptosDB { let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::OverallCommitProgress, - &DbMetadataValue::Version(version_to_revert - 1), + &DbMetadataValue::Version(target_version), )?; self.ledger_db.metadata_db().write_schemas(ledger_batch)?; @@ -233,7 +241,7 @@ impl DbWriter for AptosDB { let batch = SchemaBatch::new(); self.ledger_db .transaction_accumulator_db() - .revert_transaction_accumulator(version_to_revert - 1, &batch, temp_position)?; + .revert_transaction_accumulator(target_version, &batch, temp_position)?; self.ledger_db .transaction_accumulator_db() .write_schemas(batch)?; @@ -255,7 +263,7 @@ impl DbWriter for AptosDB { // Revert the transaction auxiliary data let batch = SchemaBatch::new(); - TransactionAuxiliaryDataDb::prune(version_to_revert - 1, latest_version, &batch)?; + TransactionAuxiliaryDataDb::prune(target_version, latest_version, &batch)?; let batch = SchemaBatch::new(); self.ledger_db .transaction_auxiliary_data_db() @@ -263,9 +271,9 @@ impl DbWriter for AptosDB { // Revert the write set let batch = SchemaBatch::new(); - WriteSetDb::prune(version_to_revert - 1, latest_version, &batch)?; + WriteSetDb::prune(target_version, latest_version, &batch)?; self.ledger_db.transaction_db().prune_transactions( - version_to_revert - 1, + target_version, latest_version, &batch, )?; @@ -275,25 +283,9 @@ impl DbWriter for AptosDB { .state_kv_db .revert_state_kv_and_ledger_metadata(version_to_revert)?; - // Get the epoch of the version_to_revert - let target_epoch = self.ledger_db.metadata_db().get_epoch(version_to_revert)?; - - // Set the epoch to the target epoch - ledger_info_with_sigs - .ledger_info() - .commit_info() - .to_owned() - .set_epoch(target_epoch); - - //Set the Version - ledger_info_with_sigs - .ledger_info() - .to_owned() - .set_version(version_to_revert - 1); - - // Update the latest ledger info if provided + // Update the provided ledger info self.commit_ledger_info( - version_to_revert - 1, + target_version, new_root_hash, Some(&ledger_info_with_sigs), )?; diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index ee80b269eff46..c30cc4ac7d784 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -586,7 +586,7 @@ pub trait DbWriter: Send + Sync { version_to_revert: Version, latest_version: Version, new_root_hash: HashValue, - ledger_info_with_sigs: LedgerInfoWithSignatures, + ledger_info_with_sigs: &LedgerInfoWithSignatures, ) -> Result<()> { unimplemented!() } From c7c987cb1fccadac7baad4e5edf55240aa3004ca Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 20 Jun 2024 15:58:28 +0300 Subject: [PATCH 094/147] aptos-db: simplify signature of revert_commit All necessary information can be obtained from either the LedgerInfoWithSignatures, or the current database state. --- storage/aptosdb/src/db/aptosdb_test.rs | 66 ++----------------- .../aptosdb/src/db/include/aptosdb_writer.rs | 20 +++--- storage/storage-interface/src/lib.rs | 5 +- 3 files changed, 13 insertions(+), 78 deletions(-) diff --git a/storage/aptosdb/src/db/aptosdb_test.rs b/storage/aptosdb/src/db/aptosdb_test.rs index 11ca3a552eea2..f5846ecba677e 100644 --- a/storage/aptosdb/src/db/aptosdb_test.rs +++ b/storage/aptosdb/src/db/aptosdb_test.rs @@ -277,22 +277,12 @@ fn test_revert_single_commit() { // Get the latest ledger info before revert let latest_ledger_info_before_revert = blocks[1].1.clone(); - let root_hash = latest_ledger_info_before_revert - .ledger_info() - .commit_info() - .executed_state_id(); - let version_to_revert = latest_ledger_info_before_revert.ledger_info().version() + 1; + let version_to_revert_to = latest_ledger_info_before_revert.commit_info().version(); // Revert the last commit - db.revert_commit( - version_to_revert, - db.get_latest_version().unwrap(), // In this case the last commit and version to commit are same - root_hash, // the hash is also the lastest - &latest_ledger_info_before_revert, - ) - .unwrap(); + db.revert_commit(&latest_ledger_info_before_revert).unwrap(); - assert_eq!(db.get_latest_version().unwrap(), version_to_revert - 1); + assert_eq!(db.get_latest_version().unwrap(), version_to_revert_to); } #[test] @@ -311,7 +301,6 @@ fn test_revert_nth_commit() { #[derive(Debug)] struct Commit { - hash: HashValue, info: LedgerInfoWithSignatures, first_version: Version, } @@ -336,7 +325,6 @@ fn test_revert_nth_commit() { committed_blocks.insert( blockheight, Commit { - hash: ledger_info_with_sigs.commit_info().executed_state_id(), info: ledger_info_with_sigs.clone(), first_version, }, @@ -358,57 +346,11 @@ fn test_revert_nth_commit() { // Get the version to revert to let version_to_revert = revert.first_version; - db.revert_commit( - version_to_revert, - db.get_latest_version().unwrap(), - pre_revert_ledger_info.commit_info().executed_state_id(), - &pre_revert_ledger_info, - ) - .unwrap(); + db.revert_commit(&pre_revert_ledger_info).unwrap(); assert_eq!(db.get_latest_version().unwrap(), version_to_revert - 1); } -#[test] -fn test_revert_commit_should_fail_with_wrong_hash() { - aptos_logger::Logger::new().init(); - - let tmp_dir = TempPath::new(); - let db = AptosDB::new_for_test(&tmp_dir); - - let mut cur_ver: Version = 0; - let mut in_memory_state = db.buffered_state().lock().current_state().clone(); - let _ancestor = in_memory_state.base.clone(); - let mut val_generator = ValueGenerator::new(); - let blocks = val_generator.generate(arb_blocks_to_commit()); - for (txns_to_commit, ledger_info_with_sigs) in &blocks { - update_in_memory_state(&mut in_memory_state, txns_to_commit.as_slice()); - db.save_transactions_for_test( - txns_to_commit, - cur_ver, /* first_version */ - cur_ver.checked_sub(1), - Some(ledger_info_with_sigs), - true, /* sync_commit */ - in_memory_state.clone(), - ) - .unwrap(); - cur_ver += txns_to_commit.len() as u64; - } - - // Get the latest ledger info before revert - let latest_ledger_info_before_revert = db.get_latest_ledger_info().unwrap(); - let last_committed_version = latest_ledger_info_before_revert.ledger_info().version(); - - // Revert the last commit - let result = db.revert_commit( - last_committed_version, - last_committed_version.clone(), // In this case the last commit and version to commit are the same - HashValue::random(), // A wrong hash - &latest_ledger_info_before_revert, - ); - assert!(result.is_err()); -} - pub fn test_state_merkle_pruning_impl( input: Vec<(Vec, LedgerInfoWithSignatures)>, ) { diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index 029cda0d04cc4..d7fd163af8cdc 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -203,21 +203,14 @@ impl DbWriter for AptosDB { /// Revert a commit. fn revert_commit( &self, - version_to_revert: Version, - latest_version: Version, - new_root_hash: HashValue, ledger_info_with_sigs: &LedgerInfoWithSignatures, ) -> Result<()> { let _timer = OTHER_TIMERS_SECONDS .with_label_values(&["revert_commit"]) .start_timer(); - let target_version = version_to_revert.checked_sub(1).expect("cannot revert genesis"); - - ensure!( - ledger_info_with_sigs.ledger_info().version() == target_version, - "LedgerInfo is not for version {}", target_version, - ); + let latest_version = self.get_latest_version()?; + let target_version = ledger_info_with_sigs.ledger_info().version(); // Revert the ledger commit progress let ledger_batch = SchemaBatch::new(); @@ -247,18 +240,20 @@ impl DbWriter for AptosDB { .write_schemas(batch)?; // Revert the transaction info + // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); self.ledger_db .transaction_info_db() - .delete_transaction_info(version_to_revert, &batch)?; + .delete_transaction_info(target_version + 1, &batch)?; let batch = SchemaBatch::new(); self.ledger_db.transaction_info_db().write_schemas(batch)?; // Revert the events + // FIXME: need to delete the range to current latest? let batch = SchemaBatch::new(); self.ledger_db .event_db() - .delete_events(version_to_revert, &batch)?; + .delete_events(target_version + 1, &batch)?; self.ledger_db.event_db().write_schemas(batch)?; // Revert the transaction auxiliary data @@ -281,9 +276,10 @@ impl DbWriter for AptosDB { // Revert the state kv and ledger metadata self.state_store .state_kv_db - .revert_state_kv_and_ledger_metadata(version_to_revert)?; + .revert_state_kv_and_ledger_metadata(target_version)?; // Update the provided ledger info + let new_root_hash = ledger_info_with_sigs.commit_info().executed_state_id(); self.commit_ledger_info( target_version, new_root_hash, diff --git a/storage/storage-interface/src/lib.rs b/storage/storage-interface/src/lib.rs index c30cc4ac7d784..208ec1d3a7c82 100644 --- a/storage/storage-interface/src/lib.rs +++ b/storage/storage-interface/src/lib.rs @@ -580,12 +580,9 @@ pub trait DbWriter: Send + Sync { unimplemented!() } - /// Revert a commit. + /// Revert to committed state referred to with the ledger information. fn revert_commit( &self, - version_to_revert: Version, - latest_version: Version, - new_root_hash: HashValue, ledger_info_with_sigs: &LedgerInfoWithSignatures, ) -> Result<()> { unimplemented!() From 6cb064032de8166c2a9d440cd608cd86432b8c80 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 20 Jun 2024 16:01:32 +0300 Subject: [PATCH 095/147] aptos-db: fix excessive batching Update both progress values in the ledger metadata schema in one batch. --- storage/aptosdb/src/db/include/aptosdb_writer.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/aptosdb/src/db/include/aptosdb_writer.rs b/storage/aptosdb/src/db/include/aptosdb_writer.rs index d7fd163af8cdc..40a59e4077d98 100644 --- a/storage/aptosdb/src/db/include/aptosdb_writer.rs +++ b/storage/aptosdb/src/db/include/aptosdb_writer.rs @@ -212,20 +212,21 @@ impl DbWriter for AptosDB { let latest_version = self.get_latest_version()?; let target_version = ledger_info_with_sigs.ledger_info().version(); - // Revert the ledger commit progress let ledger_batch = SchemaBatch::new(); + + // Revert the ledger commit progress ledger_batch.put::( &DbMetadataKey::LedgerCommitProgress, &DbMetadataValue::Version(target_version), )?; - self.ledger_db.metadata_db().write_schemas(ledger_batch)?; // Revert the overall commit progress - let ledger_batch = SchemaBatch::new(); ledger_batch.put::( &DbMetadataKey::OverallCommitProgress, &DbMetadataValue::Version(target_version), )?; + + // Write ledger metadata db changes self.ledger_db.metadata_db().write_schemas(ledger_batch)?; let temp_position = Position::from_postorder_index(latest_version)?; From 6ce927e5978016db9508015f6a1442f02e36e4a5 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 24 Jun 2024 15:09:56 +0300 Subject: [PATCH 096/147] vm-genesis: prevent division by zero Rewrite the computation for rewards rate to avoid division by zero when the epoch duration exceeds one year. --- aptos-move/vm-genesis/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aptos-move/vm-genesis/src/lib.rs b/aptos-move/vm-genesis/src/lib.rs index 1dfe710dad109..916780bb45067 100644 --- a/aptos-move/vm-genesis/src/lib.rs +++ b/aptos-move/vm-genesis/src/lib.rs @@ -417,11 +417,12 @@ fn initialize( // Calculate the per-epoch rewards rate, represented as 2 separate ints (numerator and // denominator). let rewards_rate_denominator = 1_000_000_000; - let num_epochs_in_a_year = NUM_SECONDS_PER_YEAR / genesis_config.epoch_duration_secs; - // Multiplication before division to minimize rounding errors due to integer division. - let rewards_rate_numerator = (genesis_config.rewards_apy_percentage * rewards_rate_denominator - / 100) - / num_epochs_in_a_year; + // Fold division of the percentage by 100 into multiplication with the denominator + // to minimize rounding errors due to integer division. + let rewards_rate_numerator = genesis_config.rewards_apy_percentage + * (rewards_rate_denominator / 100) + * genesis_config.epoch_duration_secs + / NUM_SECONDS_PER_YEAR; // Block timestamps are in microseconds and epoch_interval is used to check if a block timestamp // has crossed into a new epoch. So epoch_interval also needs to be in micro seconds. From 3439ad6287cc4bef38df80d7975656256c0f2013 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:14:46 +0100 Subject: [PATCH 097/147] fix: aptos-telemetry. --- Cargo.toml | 29 - crates/aptos-telemetry/src/cli_metrics.rs | 67 ++ crates/aptos-telemetry/src/constants.rs | 45 ++ crates/aptos-telemetry/src/core_metrics.rs | 189 +++++ crates/aptos-telemetry/src/lib.rs | 16 + crates/aptos-telemetry/src/metrics.rs | 119 +++ crates/aptos-telemetry/src/network_metrics.rs | 107 +++ crates/aptos-telemetry/src/sender.rs | 675 ++++++++++++++++++ crates/aptos-telemetry/src/service.rs | 554 ++++++++++++++ .../aptos-telemetry/src/system_information.rs | 173 +++++ .../src/telemetry_log_sender.rs | 147 ++++ crates/aptos-telemetry/src/utils.rs | 68 ++ 12 files changed, 2160 insertions(+), 29 deletions(-) create mode 100644 crates/aptos-telemetry/src/cli_metrics.rs create mode 100644 crates/aptos-telemetry/src/constants.rs create mode 100644 crates/aptos-telemetry/src/core_metrics.rs create mode 100644 crates/aptos-telemetry/src/lib.rs create mode 100644 crates/aptos-telemetry/src/metrics.rs create mode 100644 crates/aptos-telemetry/src/network_metrics.rs create mode 100644 crates/aptos-telemetry/src/sender.rs create mode 100644 crates/aptos-telemetry/src/service.rs create mode 100644 crates/aptos-telemetry/src/system_information.rs create mode 100644 crates/aptos-telemetry/src/telemetry_log_sender.rs create mode 100644 crates/aptos-telemetry/src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index a2b42fbebee64..f757f5a34b8e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,12 +78,9 @@ members = [ "crates/aptos-retrier", "crates/aptos-runtimes", "crates/aptos-speculative-state-helper", -<<<<<<< HEAD -======= "crates/aptos-system-utils", "crates/aptos-telemetry", "crates/aptos-telemetry-service", ->>>>>>> upstream/main "crates/aptos-temppath", "crates/aptos-time-service", "crates/aptos-warp-webserver", @@ -104,12 +101,6 @@ members = [ "crates/validator-transaction-pool", "devtools/aptos-cargo-cli", "dkg", -<<<<<<< HEAD - "ecosystem/indexer-grpc/indexer-grpc-data-access", - "ecosystem/indexer-grpc/indexer-grpc-fullnode", - "ecosystem/indexer-grpc/indexer-grpc-table-info", - "ecosystem/indexer-grpc/indexer-grpc-utils", -======= "ecosystem/indexer-grpc/indexer-grpc-cache-worker", "ecosystem/indexer-grpc/indexer-grpc-data-service", "ecosystem/indexer-grpc/indexer-grpc-file-store", @@ -121,7 +112,6 @@ members = [ "ecosystem/indexer-grpc/indexer-grpc-utils", "ecosystem/indexer-grpc/transaction-filter", "ecosystem/nft-metadata-crawler-parser", ->>>>>>> upstream/main "ecosystem/node-checker", "ecosystem/node-checker/fn-check-client", "execution/block-partitioner", @@ -182,13 +172,10 @@ members = [ "testsuite/fuzzer", "testsuite/fuzzer/fuzz", "testsuite/module-publish", -<<<<<<< HEAD -======= "testsuite/smoke-test", "testsuite/testcases", "third_party/move/evm/exec-utils", "third_party/move/evm/extract-ethereum-abi", ->>>>>>> upstream/main # third_party/move "third_party/move/extensions/async/move-async-vm", "third_party/move/extensions/move-table-extension", @@ -341,13 +328,9 @@ aptos-github-client = { path = "crates/aptos-github-client" } aptos-global-constants = { path = "config/global-constants" } aptos-id-generator = { path = "crates/aptos-id-generator" } aptos-indexer = { path = "crates/indexer" } -<<<<<<< HEAD -aptos-indexer-grpc-data-access = { path = "ecosystem/indexer-grpc/indexer-grpc-data-access" } -======= aptos-indexer-grpc-cache-worker = { path = "ecosystem/indexer-grpc/indexer-grpc-cache-worker" } aptos-indexer-grpc-data-service = { path = "ecosystem/indexer-grpc/indexer-grpc-data-service" } aptos-indexer-grpc-file-store = { path = "ecosystem/indexer-grpc/indexer-grpc-file-store" } ->>>>>>> upstream/main aptos-indexer-grpc-fullnode = { path = "ecosystem/indexer-grpc/indexer-grpc-fullnode" } aptos-indexer-grpc-in-memory-cache-benchmark = { path = "ecosystem/indexer-grpc/indexer-grpc-in-memory-cache-benchmark" } aptos-indexer-grpc-table-info = { path = "ecosystem/indexer-grpc/indexer-grpc-table-info" } @@ -415,13 +398,10 @@ aptos-storage-service-client = { path = "state-sync/storage-service/client" } aptos-storage-service-notifications = { path = "state-sync/inter-component/storage-service-notifications" } aptos-storage-service-types = { path = "state-sync/storage-service/types" } aptos-storage-service-server = { path = "state-sync/storage-service/server" } -<<<<<<< HEAD -======= aptos-system-utils = { path = "crates/aptos-system-utils" } aptos-transaction-filter = { path = "ecosystem/indexer-grpc/transaction-filter" } aptos-telemetry = { path = "crates/aptos-telemetry" } aptos-telemetry-service = { path = "crates/aptos-telemetry-service" } ->>>>>>> upstream/main aptos-temppath = { path = "crates/aptos-temppath" } aptos-testcases = { path = "testsuite/testcases" } aptos-time-service = { path = "crates/aptos-time-service", features = [ @@ -529,11 +509,7 @@ derivation-path = "0.2.0" derive_builder = "0.20.0" determinator = "0.12.0" derive_more = "0.99.11" -<<<<<<< HEAD diesel = "=2.1.1" -diesel-async = { version = "0.4", features = ["postgres", "tokio"] } -======= -diesel = "2.1" # Use the crate version once this feature gets released on crates.io: # https://github.com/weiznich/diesel_async/commit/e165e8c96a6c540ebde2d6d7c52df5c5620a4bf1 diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "d02798c67065d763154d7272dd0c09b39757d0f2", features = [ @@ -542,7 +518,6 @@ diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "d0 "bb8", "tokio", ] } ->>>>>>> upstream/main diesel_migrations = { version = "2.1.0", features = ["postgres"] } difference = "2.0.0" digest = "0.9.0" @@ -789,14 +764,10 @@ walkdir = "2.3.3" warp = { version = "0.3.5", features = ["tls"] } warp-reverse-proxy = "1.0.0" which = "4.2.5" -<<<<<<< HEAD x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek", branch = "zeroize_v1" } #Set to this version to be compatible with Sovereign Labs zeroize = "1.6.0" #Set to this version to be compatible with Sovereign Labs -======= whoami = "1.5.0" -x25519-dalek = "1.2.0" z3tracer = "0.8.0" ->>>>>>> upstream/main # MOVE DEPENDENCIES move-abigen = { path = "third_party/move/move-prover/move-abigen" } diff --git a/crates/aptos-telemetry/src/cli_metrics.rs b/crates/aptos-telemetry/src/cli_metrics.rs new file mode 100644 index 0000000000000..453149164280f --- /dev/null +++ b/crates/aptos-telemetry/src/cli_metrics.rs @@ -0,0 +1,67 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{service, utils}; +use aptos_logger::debug; +use aptos_telemetry_service::types::telemetry::TelemetryEvent; +use std::{collections::BTreeMap, time::Duration}; + +/// CLI metrics event name +const APTOS_CLI_METRICS: &str = "APTOS_CLI_METRICS"; + +/// Core metric keys +const COMMAND: &str = "command"; +const LATENCY: &str = "latency"; +const SUCCESS: &str = "success"; +const ERROR: &str = "error"; + +/// Collects and sends the build information via telemetry +pub async fn send_cli_telemetry_event( + mut build_information: BTreeMap, + command: String, + latency: Duration, + success: bool, + error: Option<&str>, +) { + // Collection information about the cli command + collect_cli_info(command, latency, success, error, &mut build_information); + + // Create a new telemetry event + let telemetry_event = TelemetryEvent { + name: APTOS_CLI_METRICS.into(), + params: build_information, + }; + + // TODO(joshlind): can we find a better way of identifying each CLI user? + let user_id = uuid::Uuid::new_v4().to_string(); + + // Send the event (we block on the join handle to ensure the + // event is processed before terminating the cli command). + let join_handle = + service::send_telemetry_event_with_ip(user_id, "NO_CHAIN".into(), None, telemetry_event) + .await; + if let Err(error) = join_handle.await { + debug!( + "Failed to send telemetry event with join error: {:?}", + error + ); + } +} + +/// Collects the cli info and appends it to the given map +pub(crate) fn collect_cli_info( + command: String, + latency: Duration, + success: bool, + error: Option<&str>, + build_information: &mut BTreeMap, +) { + build_information.insert(COMMAND.into(), command); + build_information.insert(LATENCY.into(), latency.as_millis().to_string()); + build_information.insert(SUCCESS.into(), success.to_string()); + utils::insert_optional_value( + build_information, + ERROR, + error.map(|inner| inner.to_string()), + ); +} diff --git a/crates/aptos-telemetry/src/constants.rs b/crates/aptos-telemetry/src/constants.rs new file mode 100644 index 0000000000000..abadb85d847bc --- /dev/null +++ b/crates/aptos-telemetry/src/constants.rs @@ -0,0 +1,45 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +/// A collection of constants and default values for configuring telemetry components + +// Environment variables +pub(crate) const ENV_APTOS_DISABLE_TELEMETRY: &str = "APTOS_DISABLE_TELEMETRY"; +pub(crate) const ENV_APTOS_FORCE_ENABLE_TELEMETRY: &str = "APTOS_FORCE_ENABLE_TELEMETRY"; +pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_METRICS: &str = + "APTOS_DISABLE_TELEMETRY_PUSH_METRICS"; +pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_LOGS: &str = "APTOS_DISABLE_TELEMETRY_PUSH_LOGS"; +pub(crate) const ENV_APTOS_DISABLE_TELEMETRY_PUSH_EVENTS: &str = + "APTOS_DISABLE_TELEMETRY_PUSH_EVENTS"; +pub(crate) const ENV_APTOS_DISABLE_PROMETHEUS_NODE_METRICS: &str = + "APTOS_DISABLE_PROMETHEUS_NODE_METRICS"; +pub(crate) const ENV_APTOS_DISABLE_LOG_ENV_POLLING: &str = "APTOS_DISABLE_LOG_ENV_POLLING"; + +pub(crate) const ENV_GA_MEASUREMENT_ID: &str = "GA_MEASUREMENT_ID"; +pub(crate) const ENV_GA_API_SECRET: &str = "GA_API_SECRET"; +pub(crate) const ENV_TELEMETRY_SERVICE_URL: &str = "TELEMETRY_SERVICE_URL"; + +// Default Google Analytic values. +// TODO: Rotate these periodically. +pub(crate) const APTOS_GA_MEASUREMENT_ID: &str = "G-ZX4L6WPCFZ"; +pub(crate) const APTOS_GA_API_SECRET: &str = "ArtslKPTTjeiMi1n-IR39g"; + +// Useful URLS. +// Note: the measurement protocol requires HTTPS. +// See: https://developers.google.com/analytics/devguides/collection/protocol/v1/reference#transport +pub(crate) const GA4_URL: &str = "https://www.google-analytics.com/mp/collect"; +pub(crate) const HTTPBIN_URL: &str = "https://httpbin.org/ip"; +pub(crate) const TELEMETRY_SERVICE_URL: &str = "https://telemetry.aptoslabs.com"; +pub(crate) const MAINNET_TELEMETRY_SERVICE_URL: &str = "https://telemetry.mainnet.aptoslabs.com"; + +// Frequencies for the various metrics and pushes +pub(crate) const NODE_BUILD_INFO_FREQ_SECS: u64 = 60 * 60; // 60 minutes +pub(crate) const NODE_CORE_METRICS_FREQ_SECS: u64 = 30; // 30 seconds +pub(crate) const NODE_NETWORK_METRICS_FREQ_SECS: u64 = 60; // 1 minute +pub(crate) const NODE_SYS_INFO_FREQ_SECS: u64 = 5 * 60; // 5 minutes +pub(crate) const NODE_CONFIG_FREQ_SECS: u64 = 60 * 60; // 60 minutes + +// TODO: consider making this interval configurable +pub(crate) const PROMETHEUS_PUSH_METRICS_FREQ_SECS: u64 = 15; // 15 seconds +pub(crate) const CHAIN_ACCESS_CHECK_FREQ_SECS: u64 = 30 * 60; // 30 minutes +pub(crate) const LOG_ENV_POLL_FREQ_SECS: u64 = 5 * 60; // 5 minutes diff --git a/crates/aptos-telemetry/src/core_metrics.rs b/crates/aptos-telemetry/src/core_metrics.rs new file mode 100644 index 0000000000000..b836b0ab1ece2 --- /dev/null +++ b/crates/aptos-telemetry/src/core_metrics.rs @@ -0,0 +1,189 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{utils, utils::sum_all_histogram_counts}; +use aptos_config::config::NodeConfig; +use aptos_state_sync_driver::metrics::StorageSynchronizerOperations; +use aptos_telemetry_service::types::telemetry::TelemetryEvent; +use prometheus::core::Collector; +use std::collections::BTreeMap; + +/// Core metrics event name +const APTOS_NODE_CORE_METRICS: &str = "APTOS_NODE_CORE_METRICS"; + +/// Core metric keys +const CONSENSUS_LAST_COMMITTED_ROUND: &str = "consensus_last_committed_round"; +const CONSENSUS_PROPOSALS_COUNT: &str = "consensus_proposals_count"; +const CONSENSUS_TIMEOUT_COUNT: &str = "consensus_timeout_count"; +const MEMPOOL_CORE_MEMPOOL_INDEX_SIZE: &str = "mempool_core_mempool_index_size"; +const REST_RESPONSE_COUNT: &str = "rest_response_count"; +const ROLE_TYPE: &str = "role_type"; +const STATE_SYNC_BOOTSTRAP_MODE: &str = "state_sync_bootstrap_mode"; +const STATE_SYNC_CODE_VERSION: &str = "state_sync_code_version"; +const STATE_SYNC_CONTINUOUS_SYNC_MODE: &str = "state_sync_continuous_sync_mode"; +const STATE_SYNC_SYNCED_VERSION: &str = "state_sync_synced_version"; +const STATE_SYNC_SYNCED_EPOCH: &str = "state_sync_synced_epoch"; +const STORAGE_LEDGER_VERSION: &str = "storage_ledger_version"; +const STORAGE_MIN_READABLE_LEDGER_VERSION: &str = "storage_min_readable_ledger_version"; +const STORAGE_MIN_READABLE_STATE_MERKLE_VERSION: &str = "storage_min_readable_state_merkle_version"; +const STORAGE_MIN_READABLE_STATE_KV_VERSION: &str = "storage_min_readable_state_kv_version"; +const TELEMETRY_FAILURE_COUNT: &str = "telemetry_failure_count"; +const TELEMETRY_SUCCESS_COUNT: &str = "telemetry_success_count"; + +/// Collects and sends the build information via telemetry +pub(crate) async fn create_core_metric_telemetry_event(node_config: &NodeConfig) -> TelemetryEvent { + // Collect the core metrics + let core_metrics = get_core_metrics(node_config); + + // Create and return a new telemetry event + TelemetryEvent { + name: APTOS_NODE_CORE_METRICS.into(), + params: core_metrics, + } +} + +/// Used to expose core metrics for the node +pub fn get_core_metrics(node_config: &NodeConfig) -> BTreeMap { + let mut core_metrics: BTreeMap = BTreeMap::new(); + collect_core_metrics(&mut core_metrics, node_config); + core_metrics +} + +/// Collects the core metrics and appends them to the given map +fn collect_core_metrics(core_metrics: &mut BTreeMap, node_config: &NodeConfig) { + // Collect the core metrics for each component + collect_consensus_metrics(core_metrics); + collect_mempool_metrics(core_metrics); + collect_rest_metrics(core_metrics); + collect_state_sync_metrics(core_metrics, node_config); + collect_storage_metrics(core_metrics); + collect_telemetry_metrics(core_metrics); + + // Collect the node role + let node_role_type = node_config.base.role; + core_metrics.insert(ROLE_TYPE.into(), node_role_type.as_str().into()); +} + +/// Collects the consensus metrics and appends it to the given map +fn collect_consensus_metrics(core_metrics: &mut BTreeMap) { + core_metrics.insert( + CONSENSUS_PROPOSALS_COUNT.into(), + aptos_consensus::counters::PROPOSALS_COUNT.get().to_string(), + ); + core_metrics.insert( + CONSENSUS_LAST_COMMITTED_ROUND.into(), + aptos_consensus::counters::LAST_COMMITTED_ROUND + .get() + .to_string(), + ); + core_metrics.insert( + CONSENSUS_TIMEOUT_COUNT.into(), + aptos_consensus::counters::TIMEOUT_COUNT.get().to_string(), + ); + //TODO(joshlind): add block tracing and back pressure! +} + +/// Collects the mempool metrics and appends it to the given map +fn collect_mempool_metrics(core_metrics: &mut BTreeMap) { + core_metrics.insert( + MEMPOOL_CORE_MEMPOOL_INDEX_SIZE.into(), + aptos_mempool::counters::CORE_MEMPOOL_INDEX_SIZE + .with_label_values(&["system_ttl"]) + .get() + .to_string(), + ); +} + +/// Collects the REST metrics and appends it to the given map +fn collect_rest_metrics(core_metrics: &mut BTreeMap) { + let rest_response_metrics = aptos_api::metrics::RESPONSE_STATUS.collect(); + let rest_response_count = sum_all_histogram_counts(&rest_response_metrics); + core_metrics.insert(REST_RESPONSE_COUNT.into(), rest_response_count.to_string()); +} + +/// Collects the state sync metrics and appends it to the given map +fn collect_state_sync_metrics( + core_metrics: &mut BTreeMap, + node_config: &NodeConfig, +) { + let state_sync_driver_config = node_config.state_sync.state_sync_driver; + + // Get the state sync code version + core_metrics.insert(STATE_SYNC_CODE_VERSION.into(), "2".into()); + + core_metrics.insert( + STATE_SYNC_SYNCED_EPOCH.into(), + aptos_state_sync_driver::metrics::STORAGE_SYNCHRONIZER_OPERATIONS + .with_label_values(&[StorageSynchronizerOperations::SyncedEpoch.get_label()]) + .get() + .to_string(), + ); + core_metrics.insert( + STATE_SYNC_SYNCED_VERSION.into(), + aptos_state_sync_driver::metrics::STORAGE_SYNCHRONIZER_OPERATIONS + .with_label_values(&[StorageSynchronizerOperations::Synced.get_label()]) + .get() + .to_string(), + ); + core_metrics.insert( + STATE_SYNC_BOOTSTRAP_MODE.into(), + state_sync_driver_config + .bootstrapping_mode + .to_label() + .into(), + ); + core_metrics.insert( + STATE_SYNC_CONTINUOUS_SYNC_MODE.into(), + state_sync_driver_config + .continuous_syncing_mode + .to_label() + .into(), + ); +} + +/// Collects the storage metrics and appends it to the given map +fn collect_storage_metrics(core_metrics: &mut BTreeMap) { + core_metrics.insert( + STORAGE_LEDGER_VERSION.into(), + aptos_db::metrics::LEDGER_VERSION.get().to_string(), + ); + core_metrics.insert( + STORAGE_MIN_READABLE_LEDGER_VERSION.into(), + aptos_db::metrics::PRUNER_VERSIONS + .with_label_values(&["ledger_pruner", "min_readable"]) + .get() + .to_string(), + ); + core_metrics.insert( + STORAGE_MIN_READABLE_STATE_MERKLE_VERSION.into(), + aptos_db::metrics::PRUNER_VERSIONS + .with_label_values(&["state_merkle_pruner", "min_readable"]) + .get() + .to_string(), + ); + core_metrics.insert( + STORAGE_MIN_READABLE_STATE_KV_VERSION.into(), + aptos_db::metrics::PRUNER_VERSIONS + .with_label_values(&["state_kv_pruner", "min_readable"]) + .get() + .to_string(), + ); + // TODO(joshlind): add storage latencies! +} + +/// Collects the telemetry metrics and appends it to the given map +fn collect_telemetry_metrics(core_metrics: &mut BTreeMap) { + let telemetry_failure_metrics = crate::metrics::APTOS_TELEMETRY_FAILURE.collect(); + let telemetry_failure_count = utils::sum_all_gauges(&telemetry_failure_metrics); + core_metrics.insert( + TELEMETRY_FAILURE_COUNT.into(), + telemetry_failure_count.to_string(), + ); + + let telemetry_success_metrics = crate::metrics::APTOS_TELEMETRY_SUCCESS.collect(); + let telemetry_success_count = utils::sum_all_gauges(&telemetry_success_metrics); + core_metrics.insert( + TELEMETRY_SUCCESS_COUNT.into(), + telemetry_success_count.to_string(), + ); +} diff --git a/crates/aptos-telemetry/src/lib.rs b/crates/aptos-telemetry/src/lib.rs new file mode 100644 index 0000000000000..37c6e69295b3b --- /dev/null +++ b/crates/aptos-telemetry/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +mod constants; +mod core_metrics; +mod metrics; +mod network_metrics; +mod sender; +mod telemetry_log_sender; + +pub mod cli_metrics; +pub mod service; +pub mod system_information; +pub mod utils; diff --git a/crates/aptos-telemetry/src/metrics.rs b/crates/aptos-telemetry/src/metrics.rs new file mode 100644 index 0000000000000..018ce6e706973 --- /dev/null +++ b/crates/aptos-telemetry/src/metrics.rs @@ -0,0 +1,119 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_metrics_core::{ + register_int_counter, register_int_counter_vec, IntCounter, IntCounterVec, +}; +use once_cell::sync::Lazy; + +/// Counter for successful telemetry events sent from Telemetry Sender to Telemetry Service +pub(crate) static APTOS_TELEMETRY_SERVICE_SUCCESS: Lazy = Lazy::new(|| { + register_int_counter_vec!( + "aptos_telemetry_service_success", + "Number of telemetry events successfully sent to telemetry service", + &["event_name"] + ) + .unwrap() +}); + +/// Counter for failed telemetry events sent from Telemetry Sender to Telemetry Service +pub(crate) static APTOS_TELEMETRY_SERVICE_FAILURE: Lazy = Lazy::new(|| { + register_int_counter_vec!( + "aptos_telemetry_service_failure", + "Number of telemetry events that failed to send to telemetry service", + &["event_name"] + ) + .unwrap() +}); + +/// Counter for successful telemetry events sent to GA +/// /// TODO: Clean up when cleaning up telemetry exporter to GA +pub(crate) static APTOS_TELEMETRY_SUCCESS: Lazy = Lazy::new(|| { + register_int_counter_vec!( + "aptos_telemetry_success", + "Number of telemetry events successfully sent", + &["event_name"] + ) + .unwrap() +}); + +/// Counter for failed telemetry events sent to GA +/// TODO: Clean up when cleaning up telemetry exporter to GA +pub(crate) static APTOS_TELEMETRY_FAILURE: Lazy = Lazy::new(|| { + register_int_counter_vec!( + "aptos_telemetry_failure", + "Number of telemetry events that failed to send", + &["event_name"] + ) + .unwrap() +}); + +/// Increments the number of successful telemetry events sent to GA +pub(crate) fn increment_telemetry_successes(event_name: &str) { + APTOS_TELEMETRY_SUCCESS + .with_label_values(&[event_name]) + .inc(); +} + +/// Increments the number of failed telemetry events sent to GA +pub(crate) fn increment_telemetry_failures(event_name: &str) { + APTOS_TELEMETRY_FAILURE + .with_label_values(&[event_name]) + .inc(); +} + +/// Increments the number of successful telemetry events sent to Telemetry service +pub(crate) fn increment_telemetry_service_successes(event_name: &str) { + APTOS_TELEMETRY_SERVICE_SUCCESS + .with_label_values(&[event_name]) + .inc(); +} + +/// Increments the number of failed telemetry events sent to Telemetry service +pub(crate) fn increment_telemetry_service_failures(event_name: &str) { + APTOS_TELEMETRY_SERVICE_FAILURE + .with_label_values(&[event_name]) + .inc(); +} + +/// Counter for successful log ingest events sent to Telemetry Service +pub(crate) static APTOS_LOG_INGEST_SUCCESS: Lazy = Lazy::new(|| { + register_int_counter!( + "aptos_log_ingest_success", + "Number of log ingest events successfully sent" + ) + .unwrap() +}); + +/// Counter for successful log ingest events sent to Telemetry Service +pub(crate) static APTOS_LOG_INGEST_TOO_LARGE: Lazy = Lazy::new(|| { + register_int_counter!( + "aptos_log_ingest_too_large", + "Number of log ingest events that were too large" + ) + .unwrap() +}); + +/// Counter for failed log ingest events sent to Telemetry Service +pub(crate) static APTOS_LOG_INGEST_FAILURE: Lazy = Lazy::new(|| { + register_int_counter!( + "aptos_log_ingest_failure", + "Number of log ingest events that failed to send" + ) + .unwrap() +}); + +/// Increments the number of successful log ingest events sent to Telemetry Service +pub(crate) fn increment_log_ingest_successes_by(v: u64) { + APTOS_LOG_INGEST_SUCCESS.inc_by(v); +} + +/// Increments the number of ignored log ingest events because too large +pub(crate) fn increment_log_ingest_too_large_by(v: u64) { + APTOS_LOG_INGEST_TOO_LARGE.inc_by(v); +} + +/// Increments the number of failed log ingest events +pub(crate) fn increment_log_ingest_failures_by(v: u64) { + APTOS_LOG_INGEST_FAILURE.inc_by(v); +} diff --git a/crates/aptos-telemetry/src/network_metrics.rs b/crates/aptos-telemetry/src/network_metrics.rs new file mode 100644 index 0000000000000..442d1655cdf28 --- /dev/null +++ b/crates/aptos-telemetry/src/network_metrics.rs @@ -0,0 +1,107 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils; +use aptos_telemetry_service::types::telemetry::TelemetryEvent; +use prometheus::core::Collector; +use std::collections::BTreeMap; + +/// Network metrics event name +const APTOS_NODE_NETWORK_METRICS: &str = "APTOS_NODE_NETWORK_METRICS"; + +/// Network metric keys +const NETWORK_INBOUND_CONNECTIONS: &str = "network_inbound_connections"; +const NETWORK_INBOUND_MESSAGE_SUM: &str = "network_inbound_message_sum"; +const NETWORK_INBOUND_TRAFFIC_SUM: &str = "network_inbound_traffic_sum"; +const NETWORK_OUTBOUND_CONNECTIONS: &str = "network_outbound_connections"; +const NETWORK_OUTBOUND_MESSAGE_SUM: &str = "network_outbound_message_sum"; +const NETWORK_OUTBOUND_TRAFFIC_SUM: &str = "network_outbound_traffic_sum"; + +/// Collects and sends the build information via telemetry +pub(crate) async fn create_network_metric_telemetry_event() -> TelemetryEvent { + // Collect the network metrics + let network_metrics = get_network_metrics(); + + // Create and return a new telemetry event + TelemetryEvent { + name: APTOS_NODE_NETWORK_METRICS.into(), + params: network_metrics, + } +} + +/// Used to expose network metrics for the node +pub fn get_network_metrics() -> BTreeMap { + let mut network_metrics: BTreeMap = BTreeMap::new(); + collect_network_metrics(&mut network_metrics); + network_metrics +} + +/// Collects the network metrics and appends them to the given map +fn collect_network_metrics(network_metrics: &mut BTreeMap) { + collect_connection_metrics(network_metrics); + collect_message_and_traffic_metrics(network_metrics); +} + +/// Collects the connection metrics and appends them to the given map +fn collect_connection_metrics(network_metrics: &mut BTreeMap) { + // Calculate the number of inbound and outbound connections + let mut inbound_connection_count: f64 = 0.0; + let mut outbound_connection_count: f64 = 0.0; + for metric_family in aptos_network::counters::APTOS_CONNECTIONS.collect() { + for metric in metric_family.get_metric() { + // TODO(joshlind): avoid matching on strings that can change! + for label in metric.get_label() { + if label.get_name() == "direction" { + if label.get_value() == "inbound" { + inbound_connection_count += metric.get_gauge().get_value(); + } else if label.get_value() == "outbound" { + outbound_connection_count += metric.get_gauge().get_value(); + } + } + } + } + } + + // Update the connection metrics + network_metrics.insert( + NETWORK_INBOUND_CONNECTIONS.into(), + inbound_connection_count.to_string(), + ); + network_metrics.insert( + NETWORK_OUTBOUND_CONNECTIONS.into(), + outbound_connection_count.to_string(), + ); +} + +/// Collects the message and traffic metrics and appends them to the given map +fn collect_message_and_traffic_metrics(network_metrics: &mut BTreeMap) { + // Calculate the inbound messages and traffic + let inbound_metric_families = + aptos_network::counters::NETWORK_APPLICATION_INBOUND_METRIC.collect(); + let network_inbound_message_sum = utils::sum_all_histogram_counts(&inbound_metric_families); + let network_inbound_traffic_sum = utils::sum_all_histogram_sums(&inbound_metric_families); + + // Calculate the outbound messages and traffic + let outbound_metric_families = + aptos_network::counters::NETWORK_APPLICATION_OUTBOUND_METRIC.collect(); + let network_outbound_message_sum = utils::sum_all_histogram_counts(&outbound_metric_families); + let network_outbound_traffic_sum = utils::sum_all_histogram_sums(&outbound_metric_families); + + // Update the metrics + network_metrics.insert( + NETWORK_INBOUND_MESSAGE_SUM.into(), + network_inbound_message_sum.to_string(), + ); + network_metrics.insert( + NETWORK_INBOUND_TRAFFIC_SUM.into(), + network_inbound_traffic_sum.to_string(), + ); + network_metrics.insert( + NETWORK_OUTBOUND_MESSAGE_SUM.into(), + network_outbound_message_sum.to_string(), + ); + network_metrics.insert( + NETWORK_OUTBOUND_TRAFFIC_SUM.into(), + network_outbound_traffic_sum.to_string(), + ); +} diff --git a/crates/aptos-telemetry/src/sender.rs b/crates/aptos-telemetry/src/sender.rs new file mode 100644 index 0000000000000..2bcf89ed45e8c --- /dev/null +++ b/crates/aptos-telemetry/src/sender.rs @@ -0,0 +1,675 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::metrics::{self, increment_log_ingest_failures_by, increment_log_ingest_successes_by}; +use anyhow::{anyhow, Error, Result}; +use aptos_config::config::{NodeConfig, RoleType}; +use aptos_crypto::{ + noise::{self, NoiseConfig}, + x25519, +}; +use aptos_infallible::{Mutex, RwLock}; +use aptos_logger::debug; +use aptos_telemetry_service::types::{ + auth::{AuthRequest, AuthResponse}, + response::IndexResponse, + telemetry::TelemetryDump, +}; +use aptos_types::{chain_id::ChainId, PeerId}; +use flate2::{write::GzEncoder, Compression}; +use prometheus::{default_registry, Registry}; +use reqwest::{header::CONTENT_ENCODING, Response, StatusCode, Url}; +use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, RequestBuilder}; +use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; +use std::{io::Write, sync::Arc, time::Duration}; +use uuid::Uuid; + +pub const DEFAULT_VERSION_PATH_BASE: &str = "api/v1/"; + +pub const PROMETHEUS_PUSH_METRICS_TIMEOUT_SECS: u64 = 8; +pub const TELEMETRY_SERVICE_TOTAL_RETRY_DURATION_SECS: u64 = 10; + +struct AuthContext { + noise_config: Option, + token: RwLock>, + server_public_key: Mutex>, +} + +impl AuthContext { + fn new(node_config: &NodeConfig) -> Self { + Self { + noise_config: node_config.get_identity_key().map(NoiseConfig::new), + token: RwLock::new(None), + server_public_key: Mutex::new(None), + } + } +} + +#[derive(Clone)] +pub(crate) struct TelemetrySender { + base_url: Url, + version_path_base: String, + chain_id: ChainId, + peer_id: PeerId, + role_type: RoleType, + client: ClientWithMiddleware, + auth_context: Arc, + uuid: Uuid, +} + +impl TelemetrySender { + pub fn new(base_url: Url, chain_id: ChainId, node_config: &NodeConfig) -> Self { + let retry_policy = ExponentialBackoff::builder().build_with_total_retry_duration( + Duration::from_secs(TELEMETRY_SERVICE_TOTAL_RETRY_DURATION_SECS), + ); + + let reqwest_client = reqwest::Client::new(); + let client = ClientBuilder::new(reqwest_client) + .with(RetryTransientMiddleware::new_with_policy(retry_policy)) + .build(); + + let version_path_base = match base_url.path() { + "/" => DEFAULT_VERSION_PATH_BASE.to_string(), + path => { + if !path.ends_with('/') { + format!("{}/", path) + } else { + path.to_string() + } + }, + }; + + Self { + base_url, + version_path_base, + chain_id, + peer_id: node_config.get_peer_id().unwrap_or(PeerId::ZERO), + role_type: node_config.base.role, + client, + auth_context: Arc::new(AuthContext::new(node_config)), + uuid: uuid::Uuid::new_v4(), + } + } + + pub fn build_path(&self, path: &str) -> Result { + Ok(self.base_url.join(&self.version_path_base)?.join(path)?) + } + + // sends an authenticated request to the telemetry service, automatically adding an auth token + // This function does not work with streaming bodies at the moment and will panic if you try so. + pub async fn send_authenticated_request( + &self, + request_builder: RequestBuilder, + ) -> Result { + let token = self.get_auth_token().await?; + + let request = request_builder + .try_clone() + .expect("Could not clone request_builder") + .bearer_auth(token) + .build()?; + + let mut response = self.client.execute(request).await?; + + // do 1 retry if the first attempt failed + if response.status() == StatusCode::UNAUTHORIZED { + // looks like request failed due to auth error. Let's get a new a fresh token. If this fails again we'll just return the error. + self.reset_token(); + let token = self.get_auth_token().await?; + let request = request_builder.bearer_auth(token).build()?; + response = self.client.execute(request).await?; + } + Ok(response) + } + + pub(crate) async fn push_prometheus_metrics( + &self, + registry: &Registry, + ) -> Result<(), anyhow::Error> { + debug!("Sending Prometheus Metrics"); + + let scraped_metrics = + prometheus::TextEncoder::new().encode_to_string(®istry.gather())?; + + let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); + gzip_encoder.write_all(scraped_metrics.as_bytes())?; + let compressed_bytes = gzip_encoder.finish()?; + + let response = self + .send_authenticated_request( + self.client + .post(self.build_path("ingest/metrics")?) + .header(CONTENT_ENCODING, "gzip") + .body(compressed_bytes) + .timeout(Duration::from_secs(PROMETHEUS_PUSH_METRICS_TIMEOUT_SECS)), + ) + .await; + + match response { + Err(e) => Err(anyhow!("Prometheus Metrics push failed: {}", e)), + Ok(response) => { + if response.status().is_success() { + Ok(()) + } else { + Err(anyhow!( + "Prometheus Metrics push failed with response: {}, body: {}", + response.status(), + response + .text() + .await + .unwrap_or_else(|_| "empty body".to_string()), + )) + } + }, + } + } + + pub(crate) async fn try_push_prometheus_metrics(&self) { + self.push_prometheus_metrics(default_registry()) + .await + .map_or_else( + |e| debug!("Failed to push Prometheus Metrics: {}", e), + |_| debug!("Prometheus Metrics pushed successfully."), + ); + } + + pub async fn try_send_logs(&self, batch: Vec) { + if let Ok(json) = serde_json::to_string(&batch) { + let len = json.len(); + + match self.post_logs(json.as_bytes()).await { + Ok(_) => { + increment_log_ingest_successes_by(batch.len() as u64); + debug!("Sent log of length: {}", len); + }, + Err(error) => { + increment_log_ingest_failures_by(batch.len() as u64); + debug!("Failed send log of length: {} with error: {}", len, error); + }, + } + } else { + debug!("Failed json serde of batch: {:?}", batch); + } + } + + async fn post_logs(&self, json: &[u8]) -> Result { + debug!("Sending logs"); + + let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); + gzip_encoder.write_all(json)?; + let compressed_bytes = gzip_encoder.finish()?; + + // Send the request and wait for a response + let response = self + .send_authenticated_request( + self.client + .post(self.build_path("ingest/logs")?) + .header(CONTENT_ENCODING, "gzip") + .body(compressed_bytes), + ) + .await?; + + // Process the result + error_for_status_with_body(response).await + } + + pub async fn try_send_custom_metrics(&self, event_name: String, telemetry_dump: TelemetryDump) { + match self.post_custom_metrics(&telemetry_dump.clone()).await { + Ok(_) => { + metrics::increment_telemetry_service_successes(&event_name); + debug!("Custom metrics with name {} sent successfully.", event_name); + }, + Err(e) => { + metrics::increment_telemetry_service_failures(&event_name); + debug!("Failed to send custom metrics: {}", e); + }, + } + } + + async fn post_custom_metrics( + &self, + telemetry_dump: &TelemetryDump, + ) -> Result { + // Send the request and wait for a response + let response = self + .send_authenticated_request( + self.client + .post(self.build_path("ingest/custom-event")?) + .json::(telemetry_dump), + ) + .await?; + + error_for_status_with_body(response).await + } + + async fn get_auth_token(&self) -> Result { + // Try to read the token holding a read lock + let token = { self.auth_context.token.read().as_ref().cloned() }; + match token { + Some(token) => Ok(token), + None => { + let token = self.authenticate().await?; + *self.auth_context.token.write() = Some(token.clone()); + Ok(token) + }, + } + } + + async fn get_public_key_from_server(&self) -> Result { + let response = self.client.get(self.build_path("")?).send().await?; + + match error_for_status_with_body(response).await { + Ok(response) => { + let response_payload = response.json::().await?; + Ok(response_payload.public_key) + }, + Err(err) => Err(anyhow!("Error getting server public key. {}", err)), + } + } + + async fn server_public_key(&self) -> Result { + let server_public_key = { *self.auth_context.server_public_key.lock() }; + match server_public_key { + Some(key) => Ok(key), + None => { + let public_key = self.get_public_key_from_server().await?; + *self.auth_context.server_public_key.lock() = Some(public_key); + Ok(public_key) + }, + } + } + + fn reset_token(&self) { + *self.auth_context.token.write() = None; + *self.auth_context.server_public_key.lock() = None; + } + + pub async fn authenticate(&self) -> Result { + let noise_config = match &self.auth_context.noise_config { + Some(config) => config, + None => return Err(anyhow!("Cannot send telemetry without private key")), + }; + let server_public_key = self.server_public_key().await?; + + // buffer to first noise handshake message + let mut client_noise_msg = vec![0; noise::handshake_init_msg_len(0)]; + + // build the prologue (chain_id | peer_id | server_public_key) + const CHAIN_ID_LENGTH: usize = 1; + const ID_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH; + const PROLOGUE_SIZE: usize = CHAIN_ID_LENGTH + PeerId::LENGTH + x25519::PUBLIC_KEY_SIZE; + let mut prologue = [0; PROLOGUE_SIZE]; + prologue[..CHAIN_ID_LENGTH].copy_from_slice(&[self.chain_id.id()]); + prologue[CHAIN_ID_LENGTH..ID_SIZE].copy_from_slice(self.peer_id.as_ref()); + prologue[ID_SIZE..PROLOGUE_SIZE].copy_from_slice(server_public_key.as_slice()); + + let mut rng = rand::rngs::OsRng; + + // craft first handshake message (-> e, es, s, ss) + let initiator_state = noise_config + .initiate_connection( + &mut rng, + &prologue, + server_public_key, + None, + &mut client_noise_msg, + ) + .unwrap(); + + let auth_request = AuthRequest { + chain_id: self.chain_id, + peer_id: self.peer_id, + role_type: self.role_type, + server_public_key, + handshake_msg: client_noise_msg, + run_uuid: self.uuid, + }; + + let response = self + .client + .post(self.build_path("auth")?) + .json::(&auth_request) + .send() + .await?; + + let resp = match error_for_status_with_body(response).await { + Ok(response) => Ok(response.json::().await?), + Err(err) => { + debug!( + "[telemetry-client] Error sending authentication request: {}", + err, + ); + Err(anyhow!("error {}", err)) + }, + }?; + + let (response_payload, _) = noise_config + .finalize_connection(initiator_state, resp.handshake_msg.as_slice()) + .unwrap(); + + let jwt = String::from_utf8(response_payload)?; + + Ok(jwt) + } + + pub(crate) async fn check_chain_access(&self, chain_id: ChainId) -> bool { + debug!("checking chain access for chain id {}", chain_id); + + match self.try_check_chain_access(chain_id).await { + Ok(response) => match error_for_status_with_body(response).await { + Ok(response) => response.json::().await.unwrap_or(true), + Err(e) => { + debug!("Unable to check chain access {}", e); + true + }, + }, + Err(e) => { + debug!("Unable to check chain access {}", e); + true + }, + } + } + + async fn try_check_chain_access(&self, chain_id: ChainId) -> Result { + self.client + .get(self.build_path(&format!("chain-access/{}", chain_id))?) + .send() + .await + .map_err(|e| anyhow!("error sending request {}", e)) + } + + pub(crate) async fn get_telemetry_log_env(&self) -> Option { + let response = self + .send_authenticated_request( + self.client.get( + self.build_path("config/env/telemetry-log") + .expect("unable to build telemetry path for config/env/telemetry-log"), + ), + ) + .await; + + match response { + Ok(response) => match error_for_status_with_body(response).await { + Ok(response) => response.json::>().await.unwrap_or_default(), + Err(e) => { + debug!("Unable to get telemetry log env: {}", e); + None + }, + }, + Err(e) => { + debug!("Unable to check chain access {}", e); + None + }, + } + } +} + +async fn error_for_status_with_body(response: Response) -> Result { + if response.status().is_client_error() || response.status().is_server_error() { + Err(anyhow!( + "HTTP status error ({}) for url ({}): {}", + response.status(), + response.url().clone(), + response.text().await?, + )) + } else { + Ok(response) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::metrics::{APTOS_TELEMETRY_SERVICE_FAILURE, APTOS_TELEMETRY_SERVICE_SUCCESS}; + use aptos_crypto::Uniform; + use aptos_telemetry_service::types::telemetry::TelemetryEvent; + use httpmock::MockServer; + use prometheus::{register_int_counter_vec_with_registry, Registry}; + use std::{ + collections::BTreeMap, + time::{SystemTime, UNIX_EPOCH}, + }; + + #[tokio::test] + async fn test_server_public_key() { + let mut rng = rand::thread_rng(); + let private_key = x25519::PrivateKey::generate(&mut rng); + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("GET").path("/api/v1/"); + then.status(200).json_body_obj(&IndexResponse { + public_key: private_key.public_key(), + }); + }); + + let node_config = NodeConfig::default(); + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &node_config, + ); + + let result1 = client.server_public_key().await; + let result2 = client.server_public_key().await; + + mock.assert(); + + // Should call the server once and cache the key + assert_eq!(mock.hits(), 1); + assert!(result1.is_ok()); + assert_eq!(result1.unwrap(), private_key.public_key()); + assert!(result2.is_ok()); + assert_eq!(result2.unwrap(), private_key.public_key()); + + client.reset_token(); + + let result3 = client.server_public_key().await; + assert_eq!(mock.hits(), 2); + assert!(result3.is_ok()); + assert_eq!(result3.unwrap(), private_key.public_key()); + } + + #[tokio::test] + async fn test_post_custom_metrics() { + let mut telemetry_event = TelemetryEvent { + name: "sample-event".into(), + params: BTreeMap::new(), + }; + telemetry_event + .params + .insert("key-1".into(), "value-1".into()); + let telemetry_dump = TelemetryDump { + client_id: "client-1".into(), + user_id: "user-1".into(), + timestamp_micros: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros() + .to_string(), + events: vec![], + }; + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST") + .header("Authorization", "Bearer SECRET_JWT_TOKEN") + .path("/api/v1/ingest/custom-event") + .json_body_obj(&telemetry_dump); + then.status(200); + }); + + let node_config = NodeConfig::default(); + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &node_config, + ); + { + *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); + } + + let result = client.post_custom_metrics(&telemetry_dump).await; + + mock.assert(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_try_send_metrics_retry_unauthorized() { + let event_name = "sample-event"; + let mut telemetry_event = TelemetryEvent { + name: event_name.into(), + params: BTreeMap::new(), + }; + telemetry_event + .params + .insert("key-1".into(), "value-1".into()); + let telemetry_dump = TelemetryDump { + client_id: "client-1".into(), + user_id: "user-1".into(), + timestamp_micros: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros() + .to_string(), + events: vec![], + }; + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST").path("/api/v1/ingest/custom-event"); + then.status(401); + }); + + let node_config = NodeConfig::default(); + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &node_config, + ); + { + *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); + } + + client + .try_send_custom_metrics(event_name.into(), telemetry_dump) + .await; + + mock.assert_hits(1); + assert_eq!( + APTOS_TELEMETRY_SERVICE_SUCCESS + .with_label_values(&[event_name]) + .get(), + 0 + ); + assert_eq!( + APTOS_TELEMETRY_SERVICE_FAILURE + .with_label_values(&[event_name]) + .get(), + 1 + ); + } + + #[tokio::test] + async fn test_push_prometheus_metrics() { + // Initialize a local prometheus registry + // Using the global registry will conflict will other tests that increment counters + let test_registry = Registry::default(); + + let counter = register_int_counter_vec_with_registry!( + "aptos_telemetry_service_success", + "Number of telemetry events successfully sent to telemetry service", + &["event_name"], + test_registry + ) + .unwrap(); + + counter.with_label_values(&["test-event"]).inc(); + + let scraped_metrics = prometheus::TextEncoder::new() + .encode_to_string(&test_registry.gather()) + .unwrap(); + + let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); + gzip_encoder.write_all(scraped_metrics.as_bytes()).unwrap(); + let expected_compressed_bytes = gzip_encoder.finish().unwrap(); + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST") + .header("Authorization", "Bearer SECRET_JWT_TOKEN") + .path("/api/v1/ingest/metrics") + .body(String::from_utf8_lossy(&expected_compressed_bytes)); + then.status(200); + }); + + let node_config = NodeConfig::default(); + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &node_config, + ); + { + *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); + } + + let result = client.push_prometheus_metrics(&test_registry).await; + + mock.assert(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_post_logs() { + let batch = vec!["log1".to_string(), "log2".to_string()]; + let json = serde_json::to_string(&batch); + assert!(json.is_ok()); + + let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::default()); + gzip_encoder.write_all(json.unwrap().as_bytes()).unwrap(); + let expected_compressed_bytes = gzip_encoder.finish().unwrap(); + + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST") + .header("Authorization", "Bearer SECRET_JWT_TOKEN") + .path("/api/v1/ingest/logs") + .body(String::from_utf8_lossy(&expected_compressed_bytes)); + then.status(200); + }); + + let node_config = NodeConfig::default(); + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &node_config, + ); + { + *client.auth_context.token.write() = Some("SECRET_JWT_TOKEN".into()); + } + + client.try_send_logs(batch).await; + + mock.assert(); + } + + #[tokio::test] + async fn test_check_chain_access() { + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("GET").path("/api/v1/chain-access/24"); + then.status(200).json_body(true); + }); + + let client = TelemetrySender::new( + Url::parse(&server.base_url()).expect("unable to parse base url"), + ChainId::default(), + &NodeConfig::default(), + ); + assert!(client.check_chain_access(ChainId::new(24)).await); + + mock.assert(); + } +} diff --git a/crates/aptos-telemetry/src/service.rs b/crates/aptos-telemetry/src/service.rs new file mode 100644 index 0000000000000..8a2d064a3ec1f --- /dev/null +++ b/crates/aptos-telemetry/src/service.rs @@ -0,0 +1,554 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use crate::{ + constants::*, core_metrics::create_core_metric_telemetry_event, metrics, + network_metrics::create_network_metric_telemetry_event, sender::TelemetrySender, + system_information::create_system_info_telemetry_event, + telemetry_log_sender::TelemetryLogSender, utils::create_build_info_telemetry_event, +}; +use aptos_config::config::NodeConfig; +use aptos_logger::{ + aptos_logger::RUST_LOG_TELEMETRY, prelude::*, telemetry_log_writer::TelemetryLog, + LoggerFilterUpdater, +}; +use aptos_telemetry_service::types::telemetry::{TelemetryDump, TelemetryEvent}; +use aptos_types::chain_id::ChainId; +use futures::channel::mpsc::{self, Receiver}; +use once_cell::sync::Lazy; +use rand::Rng; +use rand_core::OsRng; +use reqwest::Url; +use serde::Deserialize; +use std::{ + collections::BTreeMap, + env, + future::Future, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; +use tokio::{runtime::Runtime, task::JoinHandle, time}; +use uuid::Uuid; + +// The chain ID key +const CHAIN_ID_KEY: &str = "CHAIN_ID"; +// The IP address key +const IP_ADDRESS_KEY: &str = "IP_ADDRESS"; +// The telemetry token key +const TELEMETRY_TOKEN_KEY: &str = "TELEMETRY_TOKEN"; +// The default for unknown metric values +const UNKNOWN_METRIC_VALUE: &str = "UNKNOWN"; + +const APTOS_NODE_CONFIG_EVENT_NAME: &str = "APTOS_NODE_CONFIG"; + +/// The random token presented by the node to connect all +/// telemetry events. +/// TODO(joshlind): leverage real authentication! +static TELEMETRY_TOKEN: Lazy = Lazy::new(|| { + let mut rng = OsRng; + let token = rng.gen::(); + format!("TOKEN_{:?}", token) +}); + +/// Returns true iff telemetry is disabled +#[inline] +pub fn telemetry_is_disabled() -> bool { + env::var(ENV_APTOS_DISABLE_TELEMETRY).is_ok() +} + +/// Flag to force enabling/disabling of telemetry +#[inline] +fn force_enable_telemetry() -> bool { + env::var(ENV_APTOS_FORCE_ENABLE_TELEMETRY).is_ok() +} + +/// Flag to control enabling/disabling prometheus push metrics +#[inline] +fn enable_prometheus_push_metrics() -> bool { + force_enable_telemetry() + || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_METRICS).is_ok()) +} + +#[inline] +fn enable_prometheus_node_metrics() -> bool { + env::var(ENV_APTOS_DISABLE_PROMETHEUS_NODE_METRICS).is_err() +} + +/// Flag to control enabling/disabling push logs +#[inline] +fn enable_push_logs() -> bool { + force_enable_telemetry() + || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_LOGS).is_ok()) +} + +/// Flag to control enabling/disabling telemetry push events +#[inline] +fn enable_push_custom_events() -> bool { + force_enable_telemetry() + || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_TELEMETRY_PUSH_EVENTS).is_ok()) +} + +#[inline] +fn enable_log_env_polling() -> bool { + force_enable_telemetry() + || !(telemetry_is_disabled() || env::var(ENV_APTOS_DISABLE_LOG_ENV_POLLING).is_ok()) +} + +/// Starts the telemetry service and returns the execution runtime. +/// Note: The service will not be created if telemetry is disabled. +pub fn start_telemetry_service( + node_config: NodeConfig, + chain_id: ChainId, + build_info: BTreeMap, + remote_log_rx: Option>, + logger_filter_update_job: Option, +) -> Option { + if enable_prometheus_node_metrics() { + aptos_node_resource_metrics::register_node_metrics_collector(); + } + + // Don't start the service if telemetry has been disabled + if telemetry_is_disabled() { + warn!("Aptos telemetry is disabled!"); + return None; + } + + // Create the telemetry runtime + let telemetry_runtime = aptos_runtimes::spawn_named_runtime("telemetry".into(), None); + telemetry_runtime.handle().spawn(spawn_telemetry_service( + node_config, + chain_id, + build_info, + remote_log_rx, + logger_filter_update_job, + )); + + Some(telemetry_runtime) +} + +async fn spawn_telemetry_service( + node_config: NodeConfig, + chain_id: ChainId, + build_info: BTreeMap, + remote_log_rx: Option>, + logger_filter_update_job: Option, +) { + let telemetry_svc_url = env::var(ENV_TELEMETRY_SERVICE_URL).unwrap_or_else(|_| { + if chain_id == ChainId::mainnet() { + MAINNET_TELEMETRY_SERVICE_URL.into() + } else { + TELEMETRY_SERVICE_URL.into() + } + }); + + let base_url = Url::parse(&telemetry_svc_url).unwrap_or_else(|err| { + warn!( + "Unable to parse telemetry service URL {}. Make sure {} is unset or is set properly: {}. Defaulting to {}.", + telemetry_svc_url, + ENV_TELEMETRY_SERVICE_URL, err, TELEMETRY_SERVICE_URL + ); + Url::parse(TELEMETRY_SERVICE_URL) + .expect("unable to parse telemetry service default URL") + }); + + let telemetry_sender = TelemetrySender::new(base_url, chain_id, &node_config); + + if !force_enable_telemetry() && !telemetry_sender.check_chain_access(chain_id).await { + warn!( + "Aptos telemetry is not sent to the telemetry service because the service is not configured for chain ID {}", + chain_id + ); + // Spawn the custom event sender to send to GA4 only. + // This is a temporary workaround while we deprecate and remove GA4 completely. + let peer_id = fetch_peer_id(&node_config); + let handle = tokio::spawn(custom_event_sender( + None, + peer_id, + chain_id, + node_config.clone(), + build_info.clone(), + )); + info!("Telemetry service for GA4 started!"); + + // Check for chain access periodically in case the service is configured later + let mut interval = time::interval(Duration::from_secs(CHAIN_ACCESS_CHECK_FREQ_SECS)); + loop { + interval.tick().await; + if telemetry_sender.check_chain_access(chain_id).await { + handle.abort(); + info!("Aptos telemetry service is now configured for Chain ID {}. Starting telemetry service...", chain_id); + break; + } + } + } + + try_spawn_log_sender(telemetry_sender.clone(), remote_log_rx); + try_spawn_metrics_sender(telemetry_sender.clone()); + try_spawn_custom_event_sender(node_config, telemetry_sender.clone(), chain_id, build_info); + try_spawn_log_env_poll_task(telemetry_sender); + + // Run the logger filter update job within the telemetry runtime. + if let Some(job) = logger_filter_update_job { + tokio::spawn(job.run()); + } + + info!("Telemetry service started!"); +} + +fn try_spawn_log_env_poll_task(sender: TelemetrySender) { + if enable_log_env_polling() { + tokio::spawn(async move { + let original_value = env::var(RUST_LOG_TELEMETRY).ok(); + let mut interval = time::interval(Duration::from_secs(LOG_ENV_POLL_FREQ_SECS)); + loop { + interval.tick().await; + if let Some(env) = sender.get_telemetry_log_env().await { + info!( + "Updating {} env variable: previous value: {:?}, new value: {}", + RUST_LOG_TELEMETRY, + env::var(RUST_LOG_TELEMETRY).ok(), + env + ); + env::set_var(RUST_LOG_TELEMETRY, env) + } else if let Some(ref value) = original_value { + env::set_var(RUST_LOG_TELEMETRY, value) + } else { + env::remove_var(RUST_LOG_TELEMETRY) + } + } + }); + } +} + +fn try_spawn_custom_event_sender( + node_config: NodeConfig, + telemetry_sender: TelemetrySender, + chain_id: ChainId, + build_info: BTreeMap, +) { + if enable_push_custom_events() { + // Spawn the custom event sender + let peer_id = fetch_peer_id(&node_config); + tokio::spawn(custom_event_sender( + Some(telemetry_sender), + peer_id, + chain_id, + node_config, + build_info, + )); + } +} + +fn try_spawn_metrics_sender(telemetry_sender: TelemetrySender) { + if enable_prometheus_push_metrics() { + tokio::spawn(async move { + // Periodically send ALL prometheus metrics (This replaces the previous core and network metrics implementation) + let mut interval = + time::interval(Duration::from_secs(PROMETHEUS_PUSH_METRICS_FREQ_SECS)); + loop { + interval.tick().await; + telemetry_sender.try_push_prometheus_metrics().await; + } + }); + } +} + +fn try_spawn_log_sender( + telemetry_sender: TelemetrySender, + remote_log_rx: Option>, +) { + if enable_push_logs() { + if let Some(rx) = remote_log_rx { + let telemetry_log_sender = TelemetryLogSender::new(telemetry_sender); + tokio::spawn(telemetry_log_sender.start(rx)); + } + } +} + +/// Returns the peer id given the node config. +/// Returns UNKNOWN otherwise. +fn fetch_peer_id(node_config: &NodeConfig) -> String { + match node_config.get_peer_id() { + Some(peer_id) => peer_id.to_string(), + None => UNKNOWN_METRIC_VALUE.into(), + } +} + +async fn run_function_periodically(interval_seconds: u64, function_to_run: impl Fn() -> Fut) +where + Fut: Future, +{ + let mut interval = time::interval(Duration::from_secs(interval_seconds)); + loop { + interval.tick().await; + function_to_run().await; + } +} + +/// Spawns the dedicated telemetry service that operates periodically +async fn custom_event_sender( + telemetry_sender: Option, + peer_id: String, + chain_id: ChainId, + node_config: NodeConfig, + build_info: BTreeMap, +) { + futures::future::join5( + // Periodically send build information + run_function_periodically(NODE_BUILD_INFO_FREQ_SECS, || { + send_build_information( + peer_id.clone(), + chain_id.to_string(), + build_info.clone(), + telemetry_sender.clone(), + ) + }), + // Periodically send system information + run_function_periodically(NODE_SYS_INFO_FREQ_SECS, || { + send_system_information( + peer_id.clone(), + chain_id.to_string(), + telemetry_sender.clone(), + ) + }), + // Periodically send node core metrics + run_function_periodically(NODE_CORE_METRICS_FREQ_SECS, || { + send_node_core_metrics( + peer_id.clone(), + chain_id.to_string(), + &node_config, + telemetry_sender.clone(), + ) + }), + // Periodically send node network metrics + run_function_periodically(NODE_NETWORK_METRICS_FREQ_SECS, || { + send_node_network_metrics( + peer_id.clone(), + chain_id.to_string(), + telemetry_sender.clone(), + ) + }), + run_function_periodically(NODE_CONFIG_FREQ_SECS, || { + send_node_config( + peer_id.clone(), + chain_id.to_string(), + &node_config, + telemetry_sender.clone(), + ) + }), + ) + .await; +} + +/// Collects and sends the build information via telemetry +async fn send_build_information( + peer_id: String, + chain_id: String, + build_info: BTreeMap, + telemetry_sender: Option, +) { + let telemetry_event = create_build_info_telemetry_event(build_info).await; + send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; +} + +/// Collects and sends the core node metrics via telemetry +async fn send_node_config( + peer_id: String, + chain_id: String, + node_config: &NodeConfig, + telemetry_sender: Option, +) { + let node_config: BTreeMap = serde_json::to_value(node_config) + .map(|value| { + value + .as_object() + .map(|obj| { + obj.into_iter() + .map(|(k, v)| (k.clone(), v.to_string())) + .collect::>() + }) + .unwrap_or_default() + }) + .unwrap_or_default(); + + let telemetry_event = TelemetryEvent { + name: APTOS_NODE_CONFIG_EVENT_NAME.into(), + params: node_config, + }; + send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; +} + +/// Collects and sends the core node metrics via telemetry +async fn send_node_core_metrics( + peer_id: String, + chain_id: String, + node_config: &NodeConfig, + telemetry_sender: Option, +) { + let telemetry_event = create_core_metric_telemetry_event(node_config).await; + send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; +} + +/// Collects and sends the node network metrics via telemetry +async fn send_node_network_metrics( + peer_id: String, + chain_id: String, + telemetry_sender: Option, +) { + let telemetry_event = create_network_metric_telemetry_event().await; + send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; +} + +/// Collects and sends the system information via telemetry +async fn send_system_information( + peer_id: String, + chain_id: String, + telemetry_sender: Option, +) { + let telemetry_event = create_system_info_telemetry_event().await; + send_telemetry_event_with_ip(peer_id, chain_id, telemetry_sender, telemetry_event).await; +} + +/// Fetches the IP address and sends the given telemetry event +/// along with the IP address. Also sends a randomly generated +/// token to help correlate metrics across events. +pub(crate) async fn send_telemetry_event_with_ip( + peer_id: String, + chain_id: String, + telemetry_sender: Option, + telemetry_event: TelemetryEvent, +) -> JoinHandle<()> { + // Update the telemetry event with the ip address and random token + let TelemetryEvent { name, mut params } = telemetry_event; + params.insert(IP_ADDRESS_KEY.to_string(), get_origin_ip().await); + params.insert(TELEMETRY_TOKEN_KEY.to_string(), TELEMETRY_TOKEN.clone()); + params.insert(CHAIN_ID_KEY.into(), chain_id); + let telemetry_event = TelemetryEvent { name, params }; + + // Send the telemetry event + send_telemetry_event(peer_id, telemetry_sender, telemetry_event).await +} + +/// Gets the IP origin of the machine by pinging a url. +/// If none is found, returns UNKNOWN. +async fn get_origin_ip() -> String { + let resp = reqwest::get(HTTPBIN_URL).await; + match resp { + Ok(json) => match json.json::().await { + Ok(origin_ip) => origin_ip.origin, + Err(_) => UNKNOWN_METRIC_VALUE.into(), + }, + Err(_) => UNKNOWN_METRIC_VALUE.into(), + } +} + +/// Sends the given event and params to the telemetry endpoint +async fn send_telemetry_event( + peer_id: String, + telemetry_sender: Option, + telemetry_event: TelemetryEvent, +) -> JoinHandle<()> { + // Parse the Google analytics env variables + let api_secret = + env::var(ENV_GA_API_SECRET).unwrap_or_else(|_| APTOS_GA_API_SECRET.to_string()); + let measurement_id = + env::var(ENV_GA_MEASUREMENT_ID).unwrap_or_else(|_| APTOS_GA_MEASUREMENT_ID.to_string()); + + // Create and send the telemetry dump + let event_name = telemetry_event.name.clone(); + let timestamp_micros = match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(duration) => duration.as_micros().to_string(), + Err(_) => UNKNOWN_METRIC_VALUE.into(), + }; + let telemetry_dump = TelemetryDump { + client_id: Uuid::new_v4().to_string(), // We generate a random client id for each request + user_id: peer_id, + timestamp_micros, + events: vec![telemetry_event], + }; + if telemetry_sender.is_none() { + // telemetry_sender is None for Aptos CLI. + spawn_event_sender_to_google_analytics( + api_secret, + measurement_id, + event_name, + telemetry_dump, + ) + } else { + // Aptos nodes send their metrics to aptos-telemetry-service crate. + spawn_event_sender_to_telemetry_service(event_name, telemetry_sender, telemetry_dump) + } +} + +/// Spawns the telemetry event sender on a new thread to avoid blocking +fn spawn_event_sender_to_telemetry_service( + event_name: String, + telemetry_sender: Option, + telemetry_dump: TelemetryDump, +) -> JoinHandle<()> { + tokio::spawn(async move { + telemetry_sender + .unwrap() + .try_send_custom_metrics(event_name, telemetry_dump) + .await; + }) +} + +/// Spawns the telemetry event sender on a new thread to avoid blocking +fn spawn_event_sender_to_google_analytics( + api_secret: String, + measurement_id: String, + event_name: String, + telemetry_dump: TelemetryDump, +) -> JoinHandle<()> { + tokio::spawn(async move { + // Create a request client + let client = reqwest::Client::new(); + + // Send the request and wait for a response + let send_result = client + .post(format!( + "{}?&measurement_id={}&api_secret={}", + GA4_URL, measurement_id, api_secret + )) + .json::(&telemetry_dump) + .send() + .await; + + // Process the response + match send_result { + Ok(response) => { + let status_code = response.status(); + if status_code.is_success() { + debug!( + "Sent telemetry event {}, data: {:?}", + event_name, &telemetry_dump + ); + metrics::increment_telemetry_successes(&event_name); + } else { + debug!( + "Failed to send telemetry event! Status: {}, event: {}.", + response.status(), + event_name + ); + debug!("Failed telemetry response: {:?}", response.text().await); + metrics::increment_telemetry_failures(&event_name); + } + }, + Err(error) => { + debug!( + "Failed to send telemetry event: {}. Error: {:?}", + event_name, error + ); + metrics::increment_telemetry_failures(&event_name); + }, + } + }) +} + +/// A json struct useful for fetching the machine origin/IP +#[derive(Deserialize)] +struct OriginIP { + origin: String, +} diff --git a/crates/aptos-telemetry/src/system_information.rs b/crates/aptos-telemetry/src/system_information.rs new file mode 100644 index 0000000000000..62cd6a8b067ac --- /dev/null +++ b/crates/aptos-telemetry/src/system_information.rs @@ -0,0 +1,173 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils; +use aptos_infallible::Mutex; +use aptos_telemetry_service::types::telemetry::TelemetryEvent; +use once_cell::sync::Lazy; +use std::collections::BTreeMap; +use sysinfo::{CpuExt, DiskExt, System, SystemExt}; + +/// System information event name +const APTOS_NODE_SYSTEM_INFORMATION: &str = "APTOS_NODE_SYSTEM_INFORMATION"; + +/// System information keys +const CPU_BRAND: &str = "cpu_brand"; +const CPU_COUNT: &str = "cpu_count"; +const CPU_CORE_COUNT: &str = "cpu_core_count"; +const CPU_FREQUENCY: &str = "cpu_frequency"; +const CPU_NAME: &str = "cpu_name"; +const CPU_VENDOR_ID: &str = "cpu_vendor_id"; +const DISK_AVAILABLE_SPACE: &str = "disk_available_space"; +const DISK_COUNT: &str = "disk_count"; +const DISK_FILE_SYSTEM: &str = "disk_file_system"; +const DISK_NAME: &str = "disk_name"; +const DISK_TOTAL_SPACE: &str = "disk_total_space"; +const DISK_TYPE: &str = "disk_type"; +const MEMORY_AVAILABLE: &str = "memory_available"; +const MEMORY_TOTAL: &str = "memory_total"; +const MEMORY_USED: &str = "memory_used"; +const SYSTEM_HOST_NAME: &str = "system_host_name"; +const SYSTEM_KERNEL_VERSION: &str = "system_kernel_version"; +const SYSTEM_NAME: &str = "system_name"; +const SYSTEM_OS_VERSION: &str = "system_os_version"; + +/// Global system singleton (to avoid recreations) +pub static GLOBAL_SYSTEM: Lazy> = Lazy::new(|| Mutex::new(System::new_all())); + +/// Collects and sends the build information via telemetry +pub(crate) async fn create_system_info_telemetry_event() -> TelemetryEvent { + // Collect the system information + let system_information = get_system_information(); + + // Create and return a new telemetry event + TelemetryEvent { + name: APTOS_NODE_SYSTEM_INFORMATION.into(), + params: system_information, + } +} + +/// Used to expose system information +pub fn get_system_information() -> BTreeMap { + let mut system_information: BTreeMap = BTreeMap::new(); + collect_system_info(&mut system_information); + system_information +} + +/// Collects the system info and appends it to the given map +pub(crate) fn collect_system_info(system_information: &mut BTreeMap) { + // Note: this might be expensive, so it shouldn't be done often + GLOBAL_SYSTEM.lock().refresh_system(); + GLOBAL_SYSTEM.lock().refresh_disks(); + + // Collect relevant and available system information + collect_cpu_info(system_information, &GLOBAL_SYSTEM); + collect_disk_info(system_information, &GLOBAL_SYSTEM); + collect_memory_info(system_information, &GLOBAL_SYSTEM); + collect_sys_info(system_information, &GLOBAL_SYSTEM); +} + +/// Collects the cpu info and appends it to the given map +fn collect_cpu_info( + system_information: &mut BTreeMap, + system: &Lazy>, +) { + // Collect the number of CPUs and cores + let system_lock = system.lock(); + let cpus = system_lock.cpus(); + system_information.insert(CPU_COUNT.into(), cpus.len().to_string()); + utils::insert_optional_value( + system_information, + CPU_CORE_COUNT, + system_lock + .physical_core_count() + .map(|count| count.to_string()), + ); + + // Collect the overall CPU info + let global_cpu = system_lock.global_cpu_info(); + system_information.insert(CPU_BRAND.into(), global_cpu.brand().into()); + system_information.insert(CPU_FREQUENCY.into(), global_cpu.frequency().to_string()); + system_information.insert(CPU_NAME.into(), global_cpu.name().into()); + system_information.insert(CPU_VENDOR_ID.into(), global_cpu.vendor_id().into()); +} + +/// Collects the disk info and appends it to the given map +fn collect_disk_info( + system_information: &mut BTreeMap, + system: &Lazy>, +) { + // Collect the number of disks + let system_lock = system.lock(); + let disks = system_lock.disks(); + utils::insert_optional_value( + system_information, + DISK_COUNT, + Some(disks.len().to_string()), + ); + + // If there's no disks found, return. + if disks.is_empty() { + return; + } + + // Identify the index of the largest disk + let mut largest_disk_index = 0; + let mut largest_disk_size = 0; + for (index, disk) in disks.iter().enumerate() { + let disk_size = disk.total_space(); + if disk_size > largest_disk_size { + largest_disk_index = index; + largest_disk_size = disk_size; + } + } + + // Collect the information for the largest disk + let disk = &disks[largest_disk_index]; + system_information.insert( + DISK_AVAILABLE_SPACE.into(), + disk.available_space().to_string(), + ); + system_information.insert(DISK_FILE_SYSTEM.into(), format!("{:?}", disk.file_system())); + system_information.insert(DISK_NAME.into(), format!("{:?}", disk.name())); + system_information.insert(DISK_TOTAL_SPACE.into(), disk.total_space().to_string()); + system_information.insert(DISK_TYPE.into(), format!("{:?}", disk.type_())); +} + +/// Collects the memory info and appends it to the given map +fn collect_memory_info( + system_information: &mut BTreeMap, + system: &Lazy>, +) { + // Collect the information for the memory + let system_lock = system.lock(); + system_information.insert( + MEMORY_AVAILABLE.into(), + system_lock.available_memory().to_string(), + ); + system_information.insert(MEMORY_TOTAL.into(), system_lock.total_memory().to_string()); + system_information.insert(MEMORY_USED.into(), system_lock.used_memory().to_string()); +} + +/// Collects the sys info and appends it to the given map +fn collect_sys_info( + system_information: &mut BTreeMap, + system: &Lazy>, +) { + utils::insert_optional_value( + system_information, + SYSTEM_HOST_NAME, + system.lock().host_name(), + ); + utils::insert_optional_value( + system_information, + SYSTEM_KERNEL_VERSION, + system.lock().kernel_version(), + ); + utils::insert_optional_value(system_information, SYSTEM_NAME, system.lock().name()); + utils::insert_optional_value( + system_information, + SYSTEM_OS_VERSION, + system.lock().long_os_version(), + ); +} diff --git a/crates/aptos-telemetry/src/telemetry_log_sender.rs b/crates/aptos-telemetry/src/telemetry_log_sender.rs new file mode 100644 index 0000000000000..ded83421fd298 --- /dev/null +++ b/crates/aptos-telemetry/src/telemetry_log_sender.rs @@ -0,0 +1,147 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{metrics::increment_log_ingest_too_large_by, sender::TelemetrySender}; +use aptos_logger::{prelude::*, telemetry_log_writer::TelemetryLog}; +use futures::{channel::mpsc, StreamExt}; +use std::time::Duration; +use tokio::time::interval; +use tokio_stream::wrappers::IntervalStream; + +const MAX_BYTES: usize = 128 * 1024; +const MAX_BATCH_TIME: Duration = Duration::from_secs(5); + +/// Buffered +pub(crate) struct TelemetryLogSender { + sender: TelemetrySender, + batch: Vec, + max_bytes: usize, + current_bytes: usize, +} + +impl TelemetryLogSender { + pub fn new(sender: TelemetrySender) -> Self { + Self { + // TODO: use an existing sender? + sender, + batch: Vec::new(), + max_bytes: MAX_BYTES, + current_bytes: 0, + } + } + + fn drain_batch(&mut self) -> Vec { + let batch: Vec<_> = self.batch.drain(..).collect(); + self.current_bytes = 0; + batch + } + + pub(crate) fn add_to_batch(&mut self, log: String) -> Option> { + if log.len() > self.max_bytes { + warn!("Log ignored, size: {}", log.len()); + increment_log_ingest_too_large_by(1); + return None; + } + + self.current_bytes += log.len(); + self.batch.push(log); + + if self.current_bytes > self.max_bytes { + return Some(self.drain_batch()); + } + None + } + + pub async fn handle_next_log(&mut self, log: TelemetryLog) { + match log { + TelemetryLog::Log(log) => { + if let Some(batch) = self.add_to_batch(log) { + self.sender.try_send_logs(batch).await; + } + }, + TelemetryLog::Flush(tx) => { + self.flush_batch().await; + let _ = tx.send(()); + }, + } + } + + pub async fn flush_batch(&mut self) { + if !self.batch.is_empty() { + let drained = self.drain_batch(); + self.sender.try_send_logs(drained).await; + } + } + + pub async fn start(mut self, mut rx: mpsc::Receiver) { + debug!("Started Telemetry Log Sender"); + let mut interval = IntervalStream::new(interval(MAX_BATCH_TIME)).fuse(); + + loop { + ::futures::select! { + log = rx.select_next_some() => { + self.handle_next_log(log).await; + }, + _ = interval.select_next_some() => { + self.flush_batch().await; + }, + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + sender::TelemetrySender, + telemetry_log_sender::{TelemetryLogSender, MAX_BYTES}, + }; + use aptos_config::config::NodeConfig; + use aptos_types::chain_id::ChainId; + use reqwest::Url; + + #[tokio::test] + async fn test_add_to_batch() { + let telemetry_sender = TelemetrySender::new( + Url::parse("https://telemetry.svc").expect("unable to parse url"), + ChainId::test(), + &NodeConfig::default(), + ); + let mut sender = TelemetryLogSender::new(telemetry_sender); + + for _i in 0..2 { + // Large batch should not be allowed + let batch = sender.add_to_batch("a".repeat(MAX_BYTES + 1)); + assert!(batch.is_none()); + + // Batch is flushed before reaching size + let to_send = vec!["test"]; + let batch = sender.add_to_batch(to_send[0].to_string()); + assert!(batch.is_none()); + let batch = sender.drain_batch(); + assert_eq!(batch.len(), 1); + assert_eq!(batch, to_send); + + // Create batch that reaches max bytes + let bytes_per_string = 11; + let mut num_strings = (MAX_BYTES + 1) / bytes_per_string; + if (MAX_BYTES + 1) % bytes_per_string != 0 { + num_strings += 1; + } + let to_send: Vec<_> = (0..num_strings).map(|i| format!("{:11}", i)).collect(); + to_send.iter().enumerate().for_each(|(i, s)| { + // Large batch should not be allowed + let batch = sender.add_to_batch("a".repeat(MAX_BYTES + 1)); + assert!(batch.is_none()); + + let batch = sender.add_to_batch(s.clone()); + if i == (num_strings - 1) { + assert!(batch.is_some()); + assert_eq!(batch.unwrap(), to_send); + } else { + assert!(batch.is_none()); + } + }) + } + } +} diff --git a/crates/aptos-telemetry/src/utils.rs b/crates/aptos-telemetry/src/utils.rs new file mode 100644 index 0000000000000..e7f9340353c70 --- /dev/null +++ b/crates/aptos-telemetry/src/utils.rs @@ -0,0 +1,68 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use aptos_telemetry_service::types::telemetry::TelemetryEvent; +use prometheus::proto::MetricFamily; +use std::collections::BTreeMap; + +/// Build information event name +const APTOS_NODE_BUILD_INFORMATION: &str = "APTOS_NODE_BUILD_INFORMATION"; +/// Build information keys +pub const BUILD_CHAIN_ID: &str = "build_chain_id"; + +/// Collects and sends the build information via telemetry +pub(crate) async fn create_build_info_telemetry_event( + build_info: BTreeMap, +) -> TelemetryEvent { + // Create and return a new telemetry event + TelemetryEvent { + name: APTOS_NODE_BUILD_INFORMATION.into(), + params: build_info, + } +} + +/// Inserts an optional value into the given map iff the value exists +pub(crate) fn insert_optional_value( + map: &mut BTreeMap, + key: &str, + value: Option, +) { + if let Some(value) = value { + map.insert(key.to_string(), value); + } +} + +/// Sums all gauge counts in the given set of metric families +pub fn sum_all_gauges(metric_families: &Vec) -> f64 { + let mut gauge_sum = 0.0; + for metric_family in metric_families { + for metric in metric_family.get_metric() { + gauge_sum += metric.get_gauge().get_value(); + } + } + gauge_sum +} + +/// Sums all histogram sample counts in the given set of metric families +pub fn sum_all_histogram_counts(metric_families: &Vec) -> f64 { + let mut count_sum = 0.0; + for metric_family in metric_families { + for metric in metric_family.get_metric() { + count_sum += metric.get_histogram().get_sample_count() as f64 + } + } + count_sum +} + +/// Sums all histogram sample sums in the given set of metric families +pub fn sum_all_histogram_sums(metric_families: &Vec) -> f64 { + let mut count_sum = 0.0; + for metric_family in metric_families { + for metric in metric_family.get_metric() { + count_sum += metric.get_histogram().get_sample_sum() + } + } + count_sum +} From 6f7e6cce0df18f877e632db0d8dac75de4e61456 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:25:05 +0100 Subject: [PATCH 098/147] fix: restoring aptos-admin-service. --- Cargo.toml | 9 +++++++++ crates/aptos-admin-service/src/lib.rs | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 crates/aptos-admin-service/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index f757f5a34b8e7..293c839e3460a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,11 +37,15 @@ members = [ "aptos-move/package-builder", "aptos-move/vm-genesis", "aptos-move/writeset-transaction-generator", + "aptos-node", "aptos-utils", "config", "config/global-constants", + "consensus", "consensus/consensus-types", "consensus/safety-rules", + "crates/aptos", + "crates/aptos-admin-service", "crates/aptos-api-tester", "crates/aptos-bcs-utils", "crates/aptos-bitvec", @@ -61,6 +65,7 @@ members = [ "crates/aptos-github-client", "crates/aptos-id-generator", "crates/aptos-infallible", + "crates/aptos-inspection-service", "crates/aptos-jwk-consensus", "crates/aptos-keygen", "crates/aptos-ledger", @@ -267,7 +272,9 @@ rust-version = "1.78.0" [workspace.dependencies] # Internal crate dependencies. # Please do not add any test features here: they should be declared by the individual crate. +aptos = { path = "crates/aptos" } aptos-accumulator = { path = "storage/accumulator" } +aptos-admin-service = { path = "crates/aptos-admin-service" } aptos-aggregator = { path = "aptos-move/aptos-aggregator" } aptos-api = { path = "api" } aptos-api-test-context = { path = "api/test-context" } @@ -284,6 +291,7 @@ aptos-channels = { path = "crates/channel" } aptos-cli-common = { path = "crates/aptos-cli-common" } aptos-collections = { path = "crates/aptos-collections" } aptos-compression = { path = "crates/aptos-compression" } +aptos-consensus = { path = "consensus" } aptos-consensus-notifications = { path = "state-sync/inter-component/consensus-notifications" } aptos-consensus-types = { path = "consensus/consensus-types" } aptos-config = { path = "config" } @@ -336,6 +344,7 @@ aptos-indexer-grpc-in-memory-cache-benchmark = { path = "ecosystem/indexer-grpc/ aptos-indexer-grpc-table-info = { path = "ecosystem/indexer-grpc/indexer-grpc-table-info" } aptos-indexer-grpc-utils = { path = "ecosystem/indexer-grpc/indexer-grpc-utils" } aptos-infallible = { path = "crates/aptos-infallible" } +aptos-inspection-service = { path = "crates/aptos-inspection-service" } aptos-jellyfish-merkle = { path = "storage/jellyfish-merkle" } aptos-jwk-consensus = { path = "crates/aptos-jwk-consensus" } aptos-jwk-utils = { path = "crates/jwk-utils" } diff --git a/crates/aptos-admin-service/src/lib.rs b/crates/aptos-admin-service/src/lib.rs new file mode 100644 index 0000000000000..d408d1a2e9da0 --- /dev/null +++ b/crates/aptos-admin-service/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod server; + +pub use server::*; From 552af67def2e3d30b0a8679359235d97282d6277 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:25:56 +0100 Subject: [PATCH 099/147] fix: aptos-inspection-service. --- .../src/inspection_client.rs | 108 +++++++ crates/aptos-inspection-service/src/lib.rs | 7 + .../src/server/configuration.rs | 29 ++ .../src/server/index.rs | 35 +++ .../src/server/json_encoder.rs | 217 +++++++++++++ .../src/server/metrics.rs | 38 +++ .../src/server/mod.rs | 185 +++++++++++ .../src/server/system_information.rs | 42 +++ .../src/server/tests.rs | 286 ++++++++++++++++++ .../src/server/utils.rs | 130 ++++++++ 10 files changed, 1077 insertions(+) create mode 100644 crates/aptos-inspection-service/src/inspection_client.rs create mode 100644 crates/aptos-inspection-service/src/lib.rs create mode 100644 crates/aptos-inspection-service/src/server/configuration.rs create mode 100644 crates/aptos-inspection-service/src/server/index.rs create mode 100644 crates/aptos-inspection-service/src/server/json_encoder.rs create mode 100644 crates/aptos-inspection-service/src/server/metrics.rs create mode 100644 crates/aptos-inspection-service/src/server/mod.rs create mode 100644 crates/aptos-inspection-service/src/server/system_information.rs create mode 100644 crates/aptos-inspection-service/src/server/tests.rs create mode 100644 crates/aptos-inspection-service/src/server/utils.rs diff --git a/crates/aptos-inspection-service/src/inspection_client.rs b/crates/aptos-inspection-service/src/inspection_client.rs new file mode 100644 index 0000000000000..3cddb49c5c72d --- /dev/null +++ b/crates/aptos-inspection-service/src/inspection_client.rs @@ -0,0 +1,108 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use reqwest::Url; +use std::collections::HashMap; + +/// A simple metric value enum (to represent different value types) +#[derive(Clone, Debug)] +pub enum MetricValue { + I64(i64), + F64(f64), + I64orF64(i64, f64), +} + +impl MetricValue { + /// Convert the value to i64 + pub fn to_i64(&self) -> Result { + match self { + MetricValue::I64(v) => Ok(*v), + MetricValue::F64(v) => Err(anyhow::format_err!("Value not i64: {}", v)), + MetricValue::I64orF64(v, _) => Ok(*v), + } + } + + /// Convert the value to f64 + pub fn to_f64(&self) -> Result { + match self { + MetricValue::I64(v) => Err(anyhow::format_err!("Value not f64: {}", v)), + MetricValue::F64(v) => Ok(*v), + MetricValue::I64orF64(_, v) => Ok(*v), + } + } +} + +/// A simple inspection client for querying metrics from a node +pub struct InspectionClient { + client: reqwest::Client, + url: Url, +} + +impl InspectionClient { + /// Create a new client from the given url + pub fn new(url: Url) -> Self { + let client = reqwest::Client::new(); + Self { client, url } + } + + /// Get an i64 metric value from the node + pub async fn get_node_metric_i64>(&self, metric: S) -> Result> { + let node_metrics = self.get_forge_metrics().await?; + node_metrics + .get(metric.as_ref()) + .map_or(Ok(None), |v| v.to_i64().map(Some)) + } + + /// Retrieves all node metrics for a given metric name + pub async fn get_node_metric_with_name( + &self, + metric_name: &str, + ) -> Result>> { + let metrics = self.get_forge_metrics().await?; + let search_string = format!("{}{{", metric_name); + + // Filter out all metrics that don't start with the search string + let result: HashMap<_, _> = metrics + .iter() + .filter_map(|(key, value)| { + if key.starts_with(&search_string) { + Some((key.clone(), value.clone())) + } else { + None + } + }) + .collect(); + + // Return None if the result is empty + if result.is_empty() { + Ok(None) + } else { + Ok(Some(result)) + } + } + + /// Fetches and returns all node metrics by pinging the forge_metrics endpoint + pub async fn get_forge_metrics(&self) -> Result> { + let mut url = self.url.clone(); + url.set_path("forge_metrics"); + + // Fetch the metrics from the node + let response = self.client.get(url).send().await?; + response + .json::>() + .await? + .into_iter() + .map(|(k, v)| match (v.parse::(), v.parse::()) { + (Ok(v), Err(_)) => Ok((k, MetricValue::I64(v))), + (Err(_), Ok(v)) => Ok((k, MetricValue::F64(v))), + (Ok(iv), Ok(fv)) => Ok((k, MetricValue::I64orF64(iv, fv))), + (Err(_), Err(_)) => Err(anyhow::format_err!( + "Failed to parse stat value to i64 or f64 {}: {}", + &k, + &v + )), + }) + .collect() + } +} diff --git a/crates/aptos-inspection-service/src/lib.rs b/crates/aptos-inspection-service/src/lib.rs new file mode 100644 index 0000000000000..04b550bd4adb3 --- /dev/null +++ b/crates/aptos-inspection-service/src/lib.rs @@ -0,0 +1,7 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod inspection_client; +pub mod server; + +pub use server::*; diff --git a/crates/aptos-inspection-service/src/server/configuration.rs b/crates/aptos-inspection-service/src/server/configuration.rs new file mode 100644 index 0000000000000..8b76471d99787 --- /dev/null +++ b/crates/aptos-inspection-service/src/server/configuration.rs @@ -0,0 +1,29 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::server::utils::CONTENT_TYPE_TEXT; +use aptos_config::config::NodeConfig; +use hyper::{Body, StatusCode}; + +// The message to display when the configuration endpoint is disabled +pub const CONFIGURATION_DISABLED_MESSAGE: &str = + "This endpoint is disabled! Enable it in the node config at inspection_service.expose_configuration: true"; + +/// Handles a new configuration request +pub fn handle_configuration_request(node_config: &NodeConfig) -> (StatusCode, Body, String) { + // Only return configuration if the endpoint is enabled + let (status_code, body) = if node_config.inspection_service.expose_configuration { + // We format the configuration using debug formatting. This is important to + // prevent secret/private keys from being serialized and leaked (i.e., + // all secret keys are marked with SilentDisplay and SilentDebug). + let encoded_configuration = format!("{:?}", node_config); + (StatusCode::OK, Body::from(encoded_configuration)) + } else { + ( + StatusCode::FORBIDDEN, + Body::from(CONFIGURATION_DISABLED_MESSAGE), + ) + }; + + (status_code, body, CONTENT_TYPE_TEXT.into()) +} diff --git a/crates/aptos-inspection-service/src/server/index.rs b/crates/aptos-inspection-service/src/server/index.rs new file mode 100644 index 0000000000000..ef8ccfea8f24a --- /dev/null +++ b/crates/aptos-inspection-service/src/server/index.rs @@ -0,0 +1,35 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + server::utils::CONTENT_TYPE_TEXT, CONFIGURATION_PATH, FORGE_METRICS_PATH, JSON_METRICS_PATH, + METRICS_PATH, PEER_INFORMATION_PATH, SYSTEM_INFORMATION_PATH, +}; +use hyper::{Body, StatusCode}; + +/// Handles a new index request +pub fn handle_index_request() -> (StatusCode, Body, String) { + ( + StatusCode::OK, + Body::from(get_index_response()), + CONTENT_TYPE_TEXT.into(), + ) +} + +/// Returns the response for the index page. The response +/// simply lists a welcome message and all available endpoints. +fn get_index_response() -> String { + let mut index_response: Vec = Vec::new(); + + // Add the list of available endpoints + index_response.push("Welcome to the Aptos Inspection Service!".into()); + index_response.push("The following endpoints are available:".into()); + index_response.push(format!("\t- {}", CONFIGURATION_PATH)); + index_response.push(format!("\t- {}", FORGE_METRICS_PATH)); + index_response.push(format!("\t- {}", JSON_METRICS_PATH)); + index_response.push(format!("\t- {}", METRICS_PATH)); + index_response.push(format!("\t- {}", PEER_INFORMATION_PATH)); + index_response.push(format!("\t- {}", SYSTEM_INFORMATION_PATH)); + + index_response.join("\n") // Separate each entry with a newline +} diff --git a/crates/aptos-inspection-service/src/server/json_encoder.rs b/crates/aptos-inspection-service/src/server/json_encoder.rs new file mode 100644 index 0000000000000..ff29edccf71c9 --- /dev/null +++ b/crates/aptos-inspection-service/src/server/json_encoder.rs @@ -0,0 +1,217 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::utils::CONTENT_TYPE_JSON; +use aptos_logger::error; +use prometheus::{ + proto::{LabelPair, Metric, MetricFamily, MetricType}, + Encoder, Result, +}; +use std::{collections::HashMap, io::Write}; + +// TODO: figure out if we really need all metric endpoints... + +/// An implementation of an [`Encoder`](::Encoder) that converts a `MetricFamily` proto message +/// into `fbagent` json. +/// +/// This implementation converts metric{dimensions,...} -> value to a flat string with a value. +/// e.g., `"requests{method="GET", service="accounts"} -> 8` into `requests.GET.account -> 8`. +/// For now, it ignores timestamps (if set on the metric). +#[derive(Debug, Default)] +pub struct JsonEncoder; + +impl Encoder for JsonEncoder { + fn encode(&self, metric_families: &[MetricFamily], writer: &mut W) -> Result<()> { + let mut encoded_metrics: HashMap = HashMap::new(); + + // Go through each metric family and encode it + for metric_family in metric_families { + let name = metric_family.get_name(); + let metric_type = metric_family.get_field_type(); + for metric in metric_family.get_metric() { + match metric_type { + MetricType::COUNTER => { + encoded_metrics.insert( + flatten_metric_with_labels(name, metric), + metric.get_counter().get_value(), + ); + }, + MetricType::GAUGE => { + encoded_metrics.insert( + flatten_metric_with_labels(name, metric), + metric.get_gauge().get_value(), + ); + }, + MetricType::HISTOGRAM => { + // write the sum and counts + let h = metric.get_histogram(); + encoded_metrics.insert( + flatten_metric_with_labels(&format!("{}_count", name), metric), + h.get_sample_count() as f64, + ); + encoded_metrics.insert( + flatten_metric_with_labels(&format!("{}_sum", name), metric), + h.get_sample_sum(), + ); + }, + _ => { + // Do nothing (not supported) + }, + } + } + } + + // Write the encoded metrics to the writer + match serde_json::to_string(&encoded_metrics) { + Ok(json_encoded_metrics) => { + writer.write_all(json_encoded_metrics.as_bytes())?; + }, + Err(error) => { + error!("Failed to JSON encode the metrics! Error: {}", error); + }, + }; + + Ok(()) + } + + fn format_type(&self) -> &str { + CONTENT_TYPE_JSON + } +} + +/** +This method takes Prometheus metrics with dimensions (represented as label:value tags) +and converts it into a dot-separated string. + +Example: +Prometheus metric: error_count{method: "get_account", error="connection_error"} +Result: error_count.get_account.connection_error + +If the set of labels is empty, only the name is returned +Example: +Prometheus metric: errors +Result: errors + +This is useful when exporting metric data to flat time series. +*/ +fn flatten_metric_with_labels(name: &str, metric: &Metric) -> String { + // If the metric has no labels, return the name + let name_string = String::from(name); + if metric.get_label().is_empty() { + return name_string; + } + + // Join the values of the labels with "." + let values: Vec<&str> = metric + .get_label() + .iter() + .map(LabelPair::get_value) + .filter(|&x| !x.is_empty()) + .collect(); + let values = values.join("."); + + // If the values are empty, return the name + if values.is_empty() { + return name_string; + } + + // Otherwise, return the name with the values + format!("{}.{}", name_string, values) +} + +#[cfg(test)] +mod tests { + use super::*; + use prometheus::{ + core::{Collector, Metric}, + IntCounter, IntCounterVec, Opts, + }; + use serde_json::Value; + + #[test] + fn test_flatten_labels() { + // Generate a counter for testing + let counter_name_1 = "counter_1"; + let counter_1 = IntCounter::new(counter_name_1, "Test counter 1").unwrap(); + + // Flatten the metric and check the result + let flattened_metric = flatten_metric_with_labels(counter_name_1, &counter_1.metric()); + assert_eq!(flattened_metric, counter_name_1.to_string()); + + // Generate another counter for testing + let counter_name_2 = "counter_2"; + let counter_2 = + IntCounterVec::new(Opts::new(counter_name_2, "Test counter 2"), &["label_me"]).unwrap(); + + // Flatten the metric (without a label) and check the result + let flattened_metric = flatten_metric_with_labels( + counter_name_2, + &counter_2.with_label_values(&[""]).metric(), + ); + assert_eq!(flattened_metric, counter_name_2.to_string()); + + // Flatten the metric (with a label) and check the result + let flattened_metric = flatten_metric_with_labels( + counter_name_2, + &counter_2.with_label_values(&["hello"]).metric(), + ); + assert_eq!(flattened_metric, "counter_2.hello".to_string()); + + // Generate another counter for testing + let another_counter_2 = + IntCounterVec::new(Opts::new(counter_name_2, "Example counter for testing"), &[ + "label_me", + "label_me_too", + ]) + .unwrap(); + + // Flatten a mismatched metric (without a label) and check the result + let counter_name_3 = "counter_3"; + let flattened_metric = flatten_metric_with_labels( + counter_name_3, + &another_counter_2.with_label_values(&["", ""]).metric(), + ); + assert_eq!(flattened_metric, counter_name_3.to_string()); + + // Flatten a mismatched metric (with a label) and check the result + let flattened_metric = flatten_metric_with_labels( + counter_name_3, + &another_counter_2 + .with_label_values(&["hello", "world"]) + .metric(), + ); + assert_eq!(flattened_metric, "counter_3.hello.world"); + } + + #[test] + fn test_encoder() { + // Generate a counter for testing + let counter = IntCounterVec::new(Opts::new("testing_count", "Test Counter"), &[ + "method", "result", + ]) + .unwrap(); + + // Add test data to the counter + counter.with_label_values(&["get", "302"]).inc(); + counter.with_label_values(&["get", "302"]).inc(); + counter.with_label_values(&["get", "404"]).inc(); + counter.with_label_values(&["put", ""]).inc(); + + // Get the counter data and JSON encode it + let metric_family = counter.collect(); + let mut data_writer = Vec::::new(); + let res = JsonEncoder.encode(&metric_family, &mut data_writer); + assert!(res.is_ok()); + + // Decode the JSON and check the result + let decoded_value: Value = serde_json::from_slice(&data_writer).unwrap(); + let expected_json: &str = r#" + { + "testing_count.get.302": 2.0, + "testing_count.get.404": 1.0, + "testing_count.put": 1.0 + }"#; + let expected_value: Value = serde_json::from_str(expected_json).unwrap(); + assert_eq!(decoded_value, expected_value); + } +} diff --git a/crates/aptos-inspection-service/src/server/metrics.rs b/crates/aptos-inspection-service/src/server/metrics.rs new file mode 100644 index 0000000000000..a762688cd9525 --- /dev/null +++ b/crates/aptos-inspection-service/src/server/metrics.rs @@ -0,0 +1,38 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::server::{ + json_encoder::JsonEncoder, + utils, + utils::{CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT}, +}; +use hyper::{Body, StatusCode}; +use prometheus::TextEncoder; + +/// Handles a new forge metrics request +pub fn handle_forge_metrics() -> (StatusCode, Body, String) { + // Get and encode the metrics + let metrics = utils::get_all_metrics(); + let encoded_metrics = match serde_json::to_string(&metrics) { + Ok(encoded_metrics) => encoded_metrics, + Err(error) => format!("Failed to get forge metrics! Error: {}", error), + }; + + ( + StatusCode::OK, + Body::from(encoded_metrics), + CONTENT_TYPE_JSON.into(), + ) +} + +/// Handles a new metrics request (with JSON encoding) +pub fn handle_json_metrics_request() -> (StatusCode, Body, String) { + let buffer = utils::get_encoded_metrics(JsonEncoder); + (StatusCode::OK, Body::from(buffer), CONTENT_TYPE_JSON.into()) +} + +/// Handles a new metrics request (with text encoding) +pub fn handle_metrics_request() -> (StatusCode, Body, String) { + let buffer = utils::get_encoded_metrics(TextEncoder::new()); + (StatusCode::OK, Body::from(buffer), CONTENT_TYPE_TEXT.into()) +} diff --git a/crates/aptos-inspection-service/src/server/mod.rs b/crates/aptos-inspection-service/src/server/mod.rs new file mode 100644 index 0000000000000..c352d4de373a8 --- /dev/null +++ b/crates/aptos-inspection-service/src/server/mod.rs @@ -0,0 +1,185 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::server::utils::CONTENT_TYPE_TEXT; +use aptos_config::config::NodeConfig; +use aptos_data_client::client::AptosDataClient; +use aptos_logger::debug; +use aptos_network::application::storage::PeersAndMetadata; +use hyper::{ + service::{make_service_fn, service_fn}, + Body, Method, Request, Response, Server, StatusCode, +}; +use std::{ + convert::Infallible, + net::{SocketAddr, ToSocketAddrs}, + sync::Arc, + thread, +}; + +mod configuration; +mod index; +mod json_encoder; +mod metrics; +mod peer_information; +mod system_information; +pub mod utils; + +#[cfg(test)] +mod tests; + +// The list of endpoints offered by the inspection service +pub const CONFIGURATION_PATH: &str = "/configuration"; +pub const FORGE_METRICS_PATH: &str = "/forge_metrics"; +pub const INDEX_PATH: &str = "/"; +pub const JSON_METRICS_PATH: &str = "/json_metrics"; +pub const METRICS_PATH: &str = "/metrics"; +pub const PEER_INFORMATION_PATH: &str = "/peer_information"; +pub const SYSTEM_INFORMATION_PATH: &str = "/system_information"; + +// Useful string constants +pub const HEADER_CONTENT_TYPE: &str = "Content-Type"; +pub const INVALID_ENDPOINT_MESSAGE: &str = "The requested endpoint is invalid!"; +pub const UNEXPECTED_ERROR_MESSAGE: &str = "An unexpected error was encountered!"; + +/// Starts the inspection service that listens on the configured +/// address and handles various endpoint requests. +pub fn start_inspection_service( + node_config: NodeConfig, + aptos_data_client: AptosDataClient, + peers_and_metadata: Arc, +) { + // Fetch the service port and address + let service_port = node_config.inspection_service.port; + let service_address = node_config.inspection_service.address.clone(); + + // Create the inspection service socket address + let address: SocketAddr = (service_address.as_str(), service_port) + .to_socket_addrs() + .unwrap_or_else(|_| { + panic!( + "Failed to parse {}:{} as address", + service_address, service_port + ) + }) + .next() + .unwrap(); + + // Create a runtime for the inspection service + let runtime = aptos_runtimes::spawn_named_runtime("inspection".into(), None); + + // Spawn the inspection service + thread::spawn(move || { + // Create the service function that handles the endpoint requests + let make_service = make_service_fn(move |_conn| { + let node_config = node_config.clone(); + let aptos_data_client = aptos_data_client.clone(); + let peers_and_metadata = peers_and_metadata.clone(); + async move { + Ok::<_, Infallible>(service_fn(move |request| { + serve_requests( + request, + node_config.clone(), + aptos_data_client.clone(), + peers_and_metadata.clone(), + ) + })) + } + }); + + // Start and block on the server + runtime + .block_on(async { + let server = Server::bind(&address).serve(make_service); + server.await + }) + .unwrap(); + }); +} + +/// A simple helper function that handles each endpoint request +async fn serve_requests( + req: Request, + node_config: NodeConfig, + aptos_data_client: AptosDataClient, + peers_and_metadata: Arc, +) -> Result, hyper::Error> { + // Process the request and get the response components + let (status_code, body, content_type) = match req.uri().path() { + CONFIGURATION_PATH => { + // /configuration + // Exposes the node configuration + configuration::handle_configuration_request(&node_config) + }, + FORGE_METRICS_PATH => { + // /forge_metrics + // Exposes forge encoded metrics + metrics::handle_forge_metrics() + }, + INDEX_PATH => { + // / + // Exposes the index and list of available endpoints + index::handle_index_request() + }, + JSON_METRICS_PATH => { + // /json_metrics + // Exposes JSON encoded metrics + metrics::handle_json_metrics_request() + }, + METRICS_PATH => { + // /metrics + // Exposes text encoded metrics + metrics::handle_metrics_request() + }, + PEER_INFORMATION_PATH => { + // /peer_information + // Exposes the peer information + peer_information::handle_peer_information_request( + &node_config, + aptos_data_client, + peers_and_metadata, + ) + }, + SYSTEM_INFORMATION_PATH => { + // /system_information + // Exposes the system and build information + system_information::handle_system_information_request(node_config) + }, + _ => { + // Handle the invalid path + ( + StatusCode::NOT_FOUND, + Body::from(INVALID_ENDPOINT_MESSAGE), + CONTENT_TYPE_TEXT.into(), + ) + }, + }; + + // Create a response builder + let response_builder = Response::builder() + .header(HEADER_CONTENT_TYPE, content_type) + .status(status_code); + + // Build the response based on the request methods + let response = match *req.method() { + Method::HEAD => response_builder.body(Body::empty()), // Return only the headers + Method::GET => response_builder.body(body), // Include the response body + _ => { + // Invalid method found + Response::builder() + .status(StatusCode::METHOD_NOT_ALLOWED) + .body(Body::empty()) + }, + }; + + // Return the processed response + Ok(response.unwrap_or_else(|error| { + // Log the internal error + debug!("Error encountered when generating response: {:?}", error); + + // Return a failure response + let mut response = Response::new(Body::from(UNEXPECTED_ERROR_MESSAGE)); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + response + })) +} diff --git a/crates/aptos-inspection-service/src/server/system_information.rs b/crates/aptos-inspection-service/src/server/system_information.rs new file mode 100644 index 0000000000000..ac603084b0abd --- /dev/null +++ b/crates/aptos-inspection-service/src/server/system_information.rs @@ -0,0 +1,42 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::server::utils::{CONTENT_TYPE_JSON, CONTENT_TYPE_TEXT}; +use aptos_build_info::build_information; +use aptos_config::config::NodeConfig; +use hyper::{Body, StatusCode}; + +// The message to display when the system information endpoint is disabled +pub const SYS_INFO_DISABLED_MESSAGE: &str = + "This endpoint is disabled! Enable it in the node config at inspection_service.expose_system_information: true"; + +/// Handles a new system information request +pub fn handle_system_information_request(node_config: NodeConfig) -> (StatusCode, Body, String) { + // Only return system information if the endpoint is enabled + if node_config.inspection_service.expose_system_information { + ( + StatusCode::OK, + Body::from(get_system_information_json()), + CONTENT_TYPE_JSON.into(), + ) + } else { + ( + StatusCode::FORBIDDEN, + Body::from(SYS_INFO_DISABLED_MESSAGE), + CONTENT_TYPE_TEXT.into(), + ) + } +} + +/// Returns a simple JSON formatted string with system information +fn get_system_information_json() -> String { + // Get the system and build information + let mut system_information = aptos_telemetry::system_information::get_system_information(); + system_information.extend(build_information!()); + + // Return the system information as a JSON string + match serde_json::to_string(&system_information) { + Ok(system_information) => system_information, + Err(error) => format!("Failed to get system information! Error: {}", error), + } +} diff --git a/crates/aptos-inspection-service/src/server/tests.rs b/crates/aptos-inspection-service/src/server/tests.rs new file mode 100644 index 0000000000000..219201ec5f56a --- /dev/null +++ b/crates/aptos-inspection-service/src/server/tests.rs @@ -0,0 +1,286 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + server::{ + configuration::CONFIGURATION_DISABLED_MESSAGE, + peer_information::PEER_INFO_DISABLED_MESSAGE, serve_requests, + system_information::SYS_INFO_DISABLED_MESSAGE, utils::get_all_metrics, + }, + CONFIGURATION_PATH, FORGE_METRICS_PATH, INDEX_PATH, JSON_METRICS_PATH, METRICS_PATH, + PEER_INFORMATION_PATH, SYSTEM_INFORMATION_PATH, +}; +use aptos_config::config::{AptosDataClientConfig, BaseConfig, NodeConfig}; +use aptos_data_client::client::AptosDataClient; +use aptos_network::application::{interface::NetworkClient, storage::PeersAndMetadata}; +use aptos_storage_interface::DbReader; +use aptos_storage_service_client::StorageServiceClient; +use aptos_time_service::TimeService; +use assert_approx_eq::assert_approx_eq; +use futures::executor::block_on; +use hyper::{body, Body, Method, Request, Response, StatusCode}; +use once_cell::sync::Lazy; +use prometheus::{proto::MetricFamily, register_int_counter, Counter, IntCounter, Opts, Registry}; +use rusty_fork::rusty_fork_test; +use std::{collections::HashMap, io::read_to_string, string::String, sync::Arc}; + +// This metrics counter only exists in this test context; the rest of the +// system's metrics counters don't exist, so we need to add this for tests. +const INT_COUNTER_NAME: &str = "INT_COUNTER"; +static INT_COUNTER: Lazy = + Lazy::new(|| register_int_counter!(INT_COUNTER_NAME, "An integer counter").unwrap()); + +#[tokio::test] +async fn test_inspect_configuration() { + // Create a validator config + let mut node_config = NodeConfig::get_default_validator_config(); + + // Disable the configuration endpoint and ping it + node_config.inspection_service.expose_configuration = false; + let mut response = send_get_request_to_path(&node_config, CONFIGURATION_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + + // Verify that the response contains an error + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response_body, CONFIGURATION_DISABLED_MESSAGE); + + // Enable the configuration endpoint and ping it + node_config.inspection_service.expose_configuration = true; + let mut response = send_get_request_to_path(&node_config, CONFIGURATION_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains("NodeConfig")); + assert!(response_body_string.contains("InspectionServiceConfig")); + assert!(response_body_string.contains("expose_configuration: true")); +} + +#[tokio::test] +async fn test_inspect_forge_metrics() { + // Create a VFN config + let config = NodeConfig::get_default_vfn_config(); + + // Increment a counter and get the forge metrics + INT_COUNTER.inc(); + let mut response = send_get_request_to_path(&config, FORGE_METRICS_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains(INT_COUNTER_NAME)); +} + +#[tokio::test] +async fn test_inspect_index() { + // Create a PFN config + let config = NodeConfig::get_default_pfn_config(); + + // Ping the index + let mut response = send_get_request_to_path(&config, INDEX_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string: String = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains all the endpoints + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains(CONFIGURATION_PATH)); + assert!(response_body_string.contains(FORGE_METRICS_PATH)); + assert!(response_body_string.contains(JSON_METRICS_PATH)); + assert!(response_body_string.contains(METRICS_PATH)); + assert!(response_body_string.contains(PEER_INFORMATION_PATH)); + assert!(response_body_string.contains(SYSTEM_INFORMATION_PATH)); +} + +#[tokio::test] +async fn test_inspect_json_metrics() { + // Create a validator config + let config = NodeConfig::get_default_validator_config(); + + // Increment a counter and get the JSON metrics + INT_COUNTER.inc(); + let mut response = send_get_request_to_path(&config, JSON_METRICS_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains(INT_COUNTER_NAME)); +} + +#[tokio::test] +async fn test_inspect_metrics() { + // Create a validator config + let config = NodeConfig::get_default_validator_config(); + + // Increment a counter and get the metrics + INT_COUNTER.inc(); + let mut response = send_get_request_to_path(&config, METRICS_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains(INT_COUNTER_NAME)); +} + +#[tokio::test] +async fn test_inspect_system_information() { + // Create a validator node config + let mut config = NodeConfig::get_default_validator_config(); + + // Disable the system information endpoint and ping it + config.inspection_service.expose_system_information = false; + let mut response = send_get_request_to_path(&config, SYSTEM_INFORMATION_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + + // Verify that the response contains an error + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response_body, SYS_INFO_DISABLED_MESSAGE); + + // Enable the system information endpoint and ping it + config.inspection_service.expose_system_information = true; + let mut response = send_get_request_to_path(&config, SYSTEM_INFORMATION_PATH).await; + let response_body = body::to_bytes(response.body_mut()).await.unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains("build_commit_hash")); + assert!(response_body_string.contains("cpu_count")); + assert!(response_body_string.contains("memory_available")); +} + +#[tokio::test] +async fn test_inspect_peer_information() { + // Create a validator node config + let mut config = NodeConfig::get_default_validator_config(); + + // Disable the peer information endpoint and ping it + config.inspection_service.expose_peer_information = false; + let mut response = send_get_request_to_path(&config, PEER_INFORMATION_PATH).await; + let response_body = block_on(body::to_bytes(response.body_mut())).unwrap(); + + // Verify that the response contains an error + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!(response_body, PEER_INFO_DISABLED_MESSAGE); + + // Enable the peer information endpoint and ping it + config.inspection_service.expose_peer_information = true; + let mut response = send_get_request_to_path(&config, PEER_INFORMATION_PATH).await; + let response_body = block_on(body::to_bytes(response.body_mut())).unwrap(); + let response_body_string = read_to_string(response_body.as_ref()).unwrap(); + + // Verify that the response contains the expected information + assert_eq!(response.status(), StatusCode::OK); + assert!(response_body_string.contains("Number of peers")); + assert!(response_body_string.contains("Registered networks")); + assert!(response_body_string.contains("Peers and network IDs")); + assert!(response_body_string.contains("State sync metadata")); +} + +rusty_fork_test! { +#[test] +fn test_gather_metrics() { + // Increment the counter + let iterations = 12; + for _ in 0..iterations { + INT_COUNTER.inc(); + } + + // Fetch the metrics and verify that a new entry was added + let all_metrics = get_all_metrics(); + assert_eq!(all_metrics.len(), 1); + + // Verify that the counter has the expected value + for (metric, value) in get_all_metrics() { + if metric.starts_with(INT_COUNTER_NAME) { + assert_eq!(value, iterations.to_string()); + return; + } + } + panic!("Metric {} not found", INT_COUNTER_NAME); +} +} + +rusty_fork_test! { +#[test] +fn test_get_all_metrics() { + // Increment the counter + INT_COUNTER.inc(); + + // Verify that the metrics map only has one entry + let metrics = get_all_metrics(); + assert_eq!(metrics.len(), 1); + + // Verify that the counter has the expected value + let counter_value = metrics.values().next().unwrap().parse::().unwrap(); + assert_eq!(counter_value, 1); +} +} + +#[test] +fn test_publish_metrics() { + // Create a counter metric + let counter_opts = Opts::new("test_counter", "test counter help"); + let counter = Counter::with_opts(counter_opts).unwrap(); + + // Register the counter metric + let register = Registry::new(); + register.register(Box::new(counter.clone())).unwrap(); + + // Increment the counter and verify that the metric families are updated + counter.inc(); + let metric_families = register.gather(); + assert_eq!(metric_families.len(), 1); + + // Verify that the metric family has the expected values + let metric_family: &MetricFamily = metric_families.first().unwrap(); + assert_eq!("test counter help", metric_family.get_help()); + assert_eq!("test_counter", metric_family.get_name()); + + // Verify that the metric has the expected value + let metrics = metric_family.get_metric(); + assert_eq!(metrics.len(), 1); + assert_approx_eq!(1.0, metrics.first().unwrap().get_counter().get_value()); +} + +// Exercise the serve_requests() handler with a GET request to the given path +async fn send_get_request_to_path(config: &NodeConfig, endpoint: &str) -> Response { + // Build the URI + let uri = format!("http://127.0.0.1:9201{}", endpoint); + + // Create the peers and metadata + let peers_and_metadata = PeersAndMetadata::new(&[]); + + // Create the data client + let network_client = + NetworkClient::new(vec![], vec![], HashMap::new(), peers_and_metadata.clone()); + let (aptos_data_client, _) = AptosDataClient::new( + AptosDataClientConfig::default(), + BaseConfig::default(), + TimeService::mock(), + Arc::new(MockDatabaseReader {}), + StorageServiceClient::new(network_client), + None, + ); + + // Serve the request + serve_requests( + Request::builder() + .uri(uri) + .method(Method::GET) + .body(Body::from("")) + .unwrap(), + config.clone(), + aptos_data_client, + peers_and_metadata, + ) + .await + .unwrap() +} + +/// A simple mock database reader +pub struct MockDatabaseReader {} +impl DbReader for MockDatabaseReader {} diff --git a/crates/aptos-inspection-service/src/server/utils.rs b/crates/aptos-inspection-service/src/server/utils.rs new file mode 100644 index 0000000000000..c7f7d293faf0a --- /dev/null +++ b/crates/aptos-inspection-service/src/server/utils.rs @@ -0,0 +1,130 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_logger::{error, warn}; +use aptos_metrics_core::{register_int_counter_vec, IntCounterVec}; +use once_cell::sync::Lazy; +use prometheus::{ + proto::{MetricFamily, MetricType}, + Encoder, +}; +use std::collections::HashMap; + +// Useful string constants +pub const CONTENT_TYPE_JSON: &str = "application/json"; +pub const CONTENT_TYPE_TEXT: &str = "text/plain"; + +/// Counter for the number of metrics in various states +pub static NUM_METRICS: Lazy = Lazy::new(|| { + register_int_counter_vec!("aptos_metrics", "Number of metrics in certain states", &[ + "type" + ]) + .unwrap() +}); + +/// A simple utility function that returns all metrics as a HashMap +pub fn get_all_metrics() -> HashMap { + let metric_families = get_metric_families(); + get_metrics_map(metric_families) +} + +/// A simple utility function that encodes the metrics using the given encoder +pub fn get_encoded_metrics(encoder: impl Encoder) -> Vec { + // Gather and encode the metrics + let metric_families = get_metric_families(); + let mut encoded_buffer = vec![]; + if let Err(error) = encoder.encode(&metric_families, &mut encoded_buffer) { + error!("Failed to encode metrics! Error: {}", error); + return vec![]; + } + + // Update the total metric bytes counter + NUM_METRICS + .with_label_values(&["total_bytes"]) + .inc_by(encoded_buffer.len() as u64); + + encoded_buffer +} + +/// A simple utility function that returns all metric families +fn get_metric_families() -> Vec { + let metric_families = aptos_metrics_core::gather(); + let mut total: u64 = 0; + let mut families_over_1000: u64 = 0; + + // Take metrics of metric gathering so we know possible overhead of this process + for metric_family in &metric_families { + let family_count = metric_family.get_metric().len(); + if family_count > 1000 { + families_over_1000 = families_over_1000.saturating_add(1); + let name = metric_family.get_name(); + warn!( + count = family_count, + metric_family = name, + "Metric Family '{}' over 1000 dimensions '{}'", + name, + family_count + ); + } + total = total.saturating_add(family_count as u64); + } + + // These metrics will be reported on the next pull, rather than create a new family + NUM_METRICS.with_label_values(&["total"]).inc_by(total); + NUM_METRICS + .with_label_values(&["families_over_1000"]) + .inc_by(families_over_1000); + + metric_families +} + +/// A simple utility function that parses and collects all metrics +/// associated with the given families. +fn get_metrics_map(metric_families: Vec) -> HashMap { + // TODO: use an existing metric encoder (same as used by prometheus/metric-server) + let mut all_metrics = HashMap::new(); + + // Process each metric family + for metric_family in metric_families { + let values: Vec<_> = match metric_family.get_field_type() { + MetricType::COUNTER => metric_family + .get_metric() + .iter() + .map(|m| m.get_counter().get_value().to_string()) + .collect(), + MetricType::GAUGE => metric_family + .get_metric() + .iter() + .map(|m| m.get_gauge().get_value().to_string()) + .collect(), + MetricType::SUMMARY => { + error!("Unsupported Metric 'SUMMARY'"); + vec![] + }, + MetricType::UNTYPED => { + error!("Unsupported Metric 'UNTYPED'"); + vec![] + }, + MetricType::HISTOGRAM => metric_family + .get_metric() + .iter() + .map(|m| m.get_histogram().get_sample_count().to_string()) + .collect(), + }; + let metric_names = metric_family.get_metric().iter().map(|m| { + let label_strings: Vec = m + .get_label() + .iter() + .map(|l| format!("{}={}", l.get_name(), l.get_value())) + .collect(); + let labels_string = format!("{{{}}}", label_strings.join(",")); + format!("{}{}", metric_family.get_name(), labels_string) + }); + + for (name, value) in metric_names.zip(values.into_iter()) { + all_metrics.insert(name, value); + } + } + + all_metrics +} From 298e3a811fb27833a26b5672faf4f616945b4bf4 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:32:13 +0100 Subject: [PATCH 100/147] fix: restore aptos-debugger. --- Cargo.toml | 5 ++ aptos-move/aptos-debugger/example-txn.txt | 1 + .../src/bin/remote-gas-profiler.rs | 75 +++++++++++++++++++ aptos-move/aptos-debugger/src/lib.rs | 8 ++ 4 files changed, 89 insertions(+) create mode 100644 aptos-move/aptos-debugger/example-txn.txt create mode 100644 aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs create mode 100644 aptos-move/aptos-debugger/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 293c839e3460a..aecd4d30c88e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ members = [ "crates/aptos-log-derive", "crates/aptos-logger", "crates/aptos-metrics-core", + "aptos-move/aptos-debugger", "crates/aptos-network-checker", "crates/aptos-node-identity", "crates/aptos-openapi", @@ -115,6 +116,7 @@ members = [ "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/indexer-grpc-table-info", "ecosystem/indexer-grpc/indexer-grpc-utils", + "ecosystem/indexer-grpc/indexer-grpc-server-framework", "ecosystem/indexer-grpc/transaction-filter", "ecosystem/nft-metadata-crawler-parser", "ecosystem/node-checker", @@ -343,6 +345,7 @@ aptos-indexer-grpc-fullnode = { path = "ecosystem/indexer-grpc/indexer-grpc-full aptos-indexer-grpc-in-memory-cache-benchmark = { path = "ecosystem/indexer-grpc/indexer-grpc-in-memory-cache-benchmark" } aptos-indexer-grpc-table-info = { path = "ecosystem/indexer-grpc/indexer-grpc-table-info" } aptos-indexer-grpc-utils = { path = "ecosystem/indexer-grpc/indexer-grpc-utils" } +aptos-indexer-grpc-server-framework = { path = "ecosystem/indexer-grpc/indexer-grpc-server-framework" } aptos-infallible = { path = "crates/aptos-infallible" } aptos-inspection-service = { path = "crates/aptos-inspection-service" } aptos-jellyfish-merkle = { path = "storage/jellyfish-merkle" } @@ -358,6 +361,7 @@ aptos-mempool = { path = "mempool" } aptos-mempool-notifications = { path = "state-sync/inter-component/mempool-notifications" } aptos-memsocket = { path = "network/memsocket" } aptos-metrics-core = { path = "crates/aptos-metrics-core" } +aptos-move-debugger = { path = "aptos-move/aptos-debugger" } aptos-move-examples = { path = "aptos-move/move-examples" } aptos-move-e2e-benchmark = { path = "aptos-move/e2e-benchmark" } aptos-mvhashmap = { path = "aptos-move/mvhashmap" } @@ -368,6 +372,7 @@ aptos-network-benchmark = { path = "network/benchmark" } aptos-network-builder = { path = "network/builder" } aptos-network-checker = { path = "crates/aptos-network-checker" } aptos-network-discovery = { path = "network/discovery" } +aptos-node = { path = "aptos-node" } aptos-node-checker = { path = "ecosystem/node-checker" } aptos-node-identity = { path = "crates/aptos-node-identity" } aptos-node-resource-metrics = { path = "crates/node-resource-metrics" } diff --git a/aptos-move/aptos-debugger/example-txn.txt b/aptos-move/aptos-debugger/example-txn.txt new file mode 100644 index 0000000000000..64e4acd6c7e20 --- /dev/null +++ b/aptos-move/aptos-debugger/example-txn.txt @@ -0,0 +1 @@ +[65, 2, 62, 205, 111, 255, 182, 248, 10, 156, 99, 251, 214, 36, 49, 107, 225, 63, 1, 93, 30, 222, 207, 61, 178, 136, 229, 64, 145, 48, 55, 131, 4, 1, 0, 0, 0, 0, 0, 0, 2, 253, 249, 242, 150, 39, 16, 224, 114, 46, 38, 148, 6, 20, 25, 220, 21, 148, 64, 90, 94, 71, 138, 29, 35, 35, 80, 6, 154, 50, 83, 255, 148, 7, 119, 97, 114, 107, 97, 100, 101, 12, 109, 105, 110, 116, 95, 119, 97, 114, 107, 97, 100, 101, 0, 0, 160, 134, 1, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 190, 30, 118, 100, 0, 0, 0, 0, 2, 0, 32, 243, 52, 118, 7, 1, 74, 17, 119, 172, 46, 175, 211, 29, 153, 62, 59, 25, 207, 198, 210, 25, 238, 108, 2, 67, 153, 41, 80, 108, 107, 247, 63, 64, 69, 72, 73, 1, 249, 2, 60, 227, 143, 119, 179, 185, 250, 22, 94, 11, 27, 241, 3, 223, 177, 205, 77, 207, 121, 199, 94, 239, 117, 96, 102, 42, 169, 227, 80, 250, 182, 132, 103, 45, 174, 196, 87, 101, 7, 178, 68, 153, 241, 207, 83, 160, 220, 4, 0, 19, 38, 244, 109, 43, 113, 212, 12, 9] diff --git a/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs b/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs new file mode 100644 index 0000000000000..cc4c02aee3608 --- /dev/null +++ b/aptos-move/aptos-debugger/src/bin/remote-gas-profiler.rs @@ -0,0 +1,75 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{bail, Result}; +use aptos_move_debugger::aptos_debugger::AptosDebugger; +use aptos_rest_client::Client; +use aptos_types::transaction::Transaction; +use aptos_vm::AptosVM; +use clap::{Parser, Subcommand}; +use std::path::{Path, PathBuf}; +use url::Url; + +#[derive(Subcommand)] +pub enum Target { + /// Use full node's rest api as query endpoint. + Rest { endpoint: String }, + /// Use a local db instance to serve as query endpoint. + DB { path: PathBuf }, +} + +#[derive(Parser)] +pub struct Args { + #[clap(subcommand)] + target: Target, + + #[clap(long)] + version: u64, +} + +#[tokio::main] +async fn main() -> Result<()> { + // Parse the commandline args + let args = Args::parse(); + let version = args.version; + + // Initialize the debugger + aptos_logger::Logger::new().init(); + AptosVM::set_concurrency_level_once(1); + + let debugger = match args.target { + Target::Rest { endpoint } => { + AptosDebugger::rest_client(Client::new(Url::parse(&endpoint)?))? + }, + Target::DB { path } => AptosDebugger::db(path)?, + }; + + // Execute the transaction w/ the gas profiler + let (txn, _txn_info) = debugger + .get_committed_transaction_at_version(version) + .await?; + + let txn = match txn { + Transaction::UserTransaction(txn) => txn, + _ => bail!("not a user transaction"), + }; + + let (_status, output, gas_log) = + debugger.execute_transaction_at_version_with_gas_profiler(version, txn)?; + + let txn_output = + output.try_materialize_into_transaction_output(&debugger.state_view_at_version(version))?; + + // Show results to the user + println!("{:#?}", txn_output); + + let report_path = Path::new("gas-profiling").join(format!("txn-{}", version)); + gas_log.generate_html_report( + &report_path, + format!("Gas Report - Transaction {}", version), + )?; + + println!("Gas profiling report saved to {}.", report_path.display()); + + Ok(()) +} diff --git a/aptos-move/aptos-debugger/src/lib.rs b/aptos-move/aptos-debugger/src/lib.rs new file mode 100644 index 0000000000000..1079cd1ab8e38 --- /dev/null +++ b/aptos-move/aptos-debugger/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod aptos_debugger; +pub mod bcs_txn_decoder; +pub mod common; +pub mod execute_past_transactions; +pub mod execute_pending_block; From 3fbd2abb80191fc6a0874d8901ef5e612ca1da6d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:33:15 +0100 Subject: [PATCH 101/147] fix: grpc cache workers. --- .../indexer-grpc-cache-worker/README.md | 22 +++++++ .../indexer-grpc-cache-worker/src/lib.rs | 66 +++++++++++++++++++ .../indexer-grpc-cache-worker/src/main.rs | 20 ++++++ 3 files changed, 108 insertions(+) create mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md create mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs create mode 100644 ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md new file mode 100644 index 0000000000000..0299c39364a72 --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/README.md @@ -0,0 +1,22 @@ +# Indexer GRPC cache worker + +Cache worker fetches data from fullnode GRPC and push data to Cache. + +## How to run it. + +* service account json with `read` access to bucket `${file_store_bucket_name}`, e.g., `xxx.json`. + +* `SERVICE_ACCOUNT` env var pointing to service account json file. + +* Run it: `cargo run --release -- -c config.yaml` + +* Yaml Example +```yaml +health_check_port: 8083 +server_config: + fullnode_grpc_address: 0.0.0.0:50052 + file_store_config: + file_store_type: GcsFileStore + gcs_file_store_bucket_name: indexer-grpc-file-store-bucketname + redis_main_instance_address: 127.0.0.1:6379 +``` diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs new file mode 100644 index 0000000000000..7623e95a5b673 --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/lib.rs @@ -0,0 +1,66 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod metrics; +pub mod worker; + +use anyhow::{Context, Result}; +use aptos_indexer_grpc_server_framework::RunnableConfig; +use aptos_indexer_grpc_utils::{config::IndexerGrpcFileStoreConfig, types::RedisUrl}; +use serde::{Deserialize, Serialize}; +use url::Url; +use worker::Worker; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct IndexerGrpcCacheWorkerConfig { + pub fullnode_grpc_address: Url, + pub file_store_config: IndexerGrpcFileStoreConfig, + pub redis_main_instance_address: RedisUrl, + #[serde(default = "default_enable_cache_compression")] + pub enable_cache_compression: bool, +} + +const fn default_enable_cache_compression() -> bool { + false +} + +impl IndexerGrpcCacheWorkerConfig { + pub fn new( + fullnode_grpc_address: Url, + file_store_config: IndexerGrpcFileStoreConfig, + redis_main_instance_address: RedisUrl, + enable_cache_compression: bool, + ) -> Self { + Self { + fullnode_grpc_address, + file_store_config, + redis_main_instance_address, + enable_cache_compression, + } + } +} + +#[async_trait::async_trait] +impl RunnableConfig for IndexerGrpcCacheWorkerConfig { + async fn run(&self) -> Result<()> { + let mut worker = Worker::new( + self.fullnode_grpc_address.clone(), + self.redis_main_instance_address.clone(), + self.file_store_config.clone(), + self.enable_cache_compression, + ) + .await + .context("Failed to create cache worker")?; + worker + .run() + .await + .context("Failed to run cache worker") + .expect("Cache worker failed"); + Ok(()) + } + + fn get_server_name(&self) -> String { + "idxcachewrkr".to_string() + } +} diff --git a/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs new file mode 100644 index 0000000000000..e40dfa51dfb7e --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-cache-worker/src/main.rs @@ -0,0 +1,20 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use aptos_indexer_grpc_cache_worker::IndexerGrpcCacheWorkerConfig; +use aptos_indexer_grpc_server_framework::ServerArgs; +use clap::Parser; + +#[cfg(unix)] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +#[tokio::main] +async fn main() -> Result<()> { + let args = ServerArgs::parse(); + args.run::() + .await + .expect("Cache worker failed to run"); + Ok(()) +} From f2b642a4cd02e9aa75f79baad559c625fcba907f Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:33:59 +0100 Subject: [PATCH 102/147] fix: indexer-grpc-file-store. --- .../indexer-grpc-file-store/README.md | 56 ++++++++++++++++ .../indexer-grpc-file-store/src/lib.rs | 67 +++++++++++++++++++ .../indexer-grpc-file-store/src/main.rs | 20 ++++++ 3 files changed, 143 insertions(+) create mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/README.md create mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs create mode 100644 ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md b/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md new file mode 100644 index 0000000000000..3704bcae7ec2a --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-file-store/README.md @@ -0,0 +1,56 @@ +# Indexer GRPC file store + +File store fetches data from cache and stores in Google Cloud Storage. + +## How to run it. + +* A service account json with write access to GCS. + * To bootstrap, please upload `metadata.json` to your bucket(`$FILE_STORE_BUCKET_NAME` e.g., `indexer-grpc-file-store`): + ```json + { + "chain_id": 43, + "blob_size": 1000, + "version": 0 + } + ``` + * `chain_id` is the chain to process, immutable. + * `blob_size` is the number of transactions in each blob, immutable. + * `version` is the current version of transaction to process. + +* A Redis cache running at `$REDIS_ADDRESS`, e.g., `127.0.0.1:6379` +* Example command to run: + +```yaml +health_check_port: 8082 +server_config: + file_store_config: + file_store_type: GcsFileStore + gcs_file_store_bucket_name: indexer-grpc-file-store-bucketname + redis_main_instance_address: 127.0.0.1:6379 +``` + +* Your bucket looks like: + +```bash +indexer-grpc-file-store-testnet/ + files/ + 0.json + 1000.json + ... + metadata.json +``` + +## [TEST ONLY] Run it with a local filestore + +For developing and testing locally, it might be easier to use a local filestore. + +Create a local directory to store the filestore: `mkdir test_indexer_grpc_filestore` + +Then in your config: +```yaml +... +server_config: + file_store_config: + file_store_type: LocalFileStore + local_file_store_path: test_indexer_grpc_filestore +``` diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs new file mode 100644 index 0000000000000..336a002ca9f72 --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/lib.rs @@ -0,0 +1,67 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod metrics; +pub mod processor; + +use anyhow::Result; +use aptos_indexer_grpc_server_framework::RunnableConfig; +use aptos_indexer_grpc_utils::{config::IndexerGrpcFileStoreConfig, types::RedisUrl}; +use processor::Processor; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct IndexerGrpcFileStoreWorkerConfig { + pub file_store_config: IndexerGrpcFileStoreConfig, + pub redis_main_instance_address: RedisUrl, + pub enable_expensive_logging: Option, + pub chain_id: u64, + #[serde(default = "default_enable_cache_compression")] + pub enable_cache_compression: bool, +} + +const fn default_enable_cache_compression() -> bool { + false +} + +impl IndexerGrpcFileStoreWorkerConfig { + pub fn new( + file_store_config: IndexerGrpcFileStoreConfig, + redis_main_instance_address: RedisUrl, + enable_expensive_logging: Option, + chain_id: u64, + enable_cache_compression: bool, + ) -> Self { + Self { + file_store_config, + redis_main_instance_address, + enable_expensive_logging, + chain_id, + enable_cache_compression, + } + } +} + +#[async_trait::async_trait] +impl RunnableConfig for IndexerGrpcFileStoreWorkerConfig { + async fn run(&self) -> Result<()> { + let mut processor = Processor::new( + self.redis_main_instance_address.clone(), + self.file_store_config.clone(), + self.chain_id, + self.enable_cache_compression, + ) + .await + .expect("Failed to create file store processor"); + processor + .run() + .await + .expect("File store processor exited unexpectedly"); + Ok(()) + } + + fn get_server_name(&self) -> String { + "idxfilestore".to_string() + } +} diff --git a/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs new file mode 100644 index 0000000000000..3e2d0671339bc --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-file-store/src/main.rs @@ -0,0 +1,20 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use aptos_indexer_grpc_file_store::IndexerGrpcFileStoreWorkerConfig; +use aptos_indexer_grpc_server_framework::ServerArgs; +use clap::Parser; + +#[cfg(unix)] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +#[tokio::main] +async fn main() -> Result<()> { + let args = ServerArgs::parse(); + args.run::() + .await + .expect("Failed to run server"); + Ok(()) +} From e059ce0d096e7f9eb7b6ff236b9335bce65ff1e1 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:34:45 +0100 Subject: [PATCH 103/147] fix: indexer-grpc-integration-tests. --- .../indexer-grpc-integration-tests/src/lib.rs | 8 ++++++++ .../indexer-grpc-integration-tests/src/tests/mod.rs | 5 +++++ 2 files changed, 13 insertions(+) create mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs create mode 100644 ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs new file mode 100644 index 0000000000000..41eeab05b7c79 --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +// We hide these tests behind a feature flag because these are not standard unit tests, +// these are integration tests that rely on a variety of outside pieces such as a local +// testnet and a running Redis instance. +#[cfg(feature = "integration-tests")] +mod tests; diff --git a/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs new file mode 100644 index 0000000000000..b102b93ad164a --- /dev/null +++ b/ecosystem/indexer-grpc/indexer-grpc-integration-tests/src/tests/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(test)] +mod fullnode_tests; From 5a80be9995ab51a60a35ff08f6164d977a15ded4 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:36:08 +0100 Subject: [PATCH 104/147] fix: aptos-debugger. --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index aecd4d30c88e4..51883cd96fc04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ members = [ "crates/aptos-compression", "crates/aptos-crypto", "crates/aptos-crypto-derive", + "crates/aptos-debugger", "crates/aptos-dkg", "crates/aptos-drop-helper", "crates/aptos-enum-conversion-derive", @@ -306,6 +307,7 @@ aptos-db = { path = "storage/aptosdb" } aptos-db-indexer = { path = "storage/indexer" } aptos-db-indexer-schemas = { path = "storage/indexer_schemas" } aptos-db-tool = { path = "storage/db-tool" } +aptos-debugger = { path = "crates/aptos-debugger" } aptos-dkg = { path = "crates/aptos-dkg" } aptos-dkg-runtime = { path = "dkg" } aptos-drop-helper = { path = "crates/aptos-drop-helper" } From 69e2a7ee29c6eccf1c3541f00faa200c29be6bcf Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:38:59 +0100 Subject: [PATCH 105/147] fix: aptos-forge. --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 51883cd96fc04..7fd230335f981 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,6 +177,8 @@ members = [ "testsuite/dos/http_test", "testsuite/dos/listener", "testsuite/dos/sender", + "testsuite/forge", + "testsuite/forge-cli", "testsuite/fuzzer", "testsuite/fuzzer/fuzz", "testsuite/module-publish", @@ -326,6 +328,7 @@ aptos-faucet-cli = { path = "crates/aptos-faucet/cli" } aptos-faucet-core = { path = "crates/aptos-faucet/core" } aptos-faucet-service = { path = "crates/aptos-faucet/service" } aptos-faucet-metrics-server = { path = "crates/aptos-faucet/metrics-server" } +aptos-forge = { path = "testsuite/forge" } aptos-fallible = { path = "crates/fallible" } aptos-framework = { path = "aptos-move/framework" } fuzzer = { path = "testsuite/fuzzer" } From a28b76e0633669d54a5382782c2fd135e16e9b1e Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:39:41 +0100 Subject: [PATCH 106/147] fix: aptos-forge. --- .../src/backend/k8s/chaos/cpu_stress.yaml | 16 + .../backend/k8s/chaos/network_bandwidth.yaml | 17 + .../src/backend/k8s/chaos/network_delay.yaml | 35 ++ .../src/backend/k8s/chaos/network_loss.yaml | 25 ++ .../backend/k8s/chaos/network_partition.yaml | 22 + .../forge/src/backend/k8s/chaos_schema.rs | 63 +++ testsuite/forge/src/backend/k8s/constants.rs | 54 +++ testsuite/forge/src/backend/k8s/kube_api.rs | 361 ++++++++++++++++ testsuite/forge/src/backend/k8s/mod.rs | 195 +++++++++ .../forge/src/backend/k8s/stateful_set.rs | 398 ++++++++++++++++++ testsuite/forge/src/backend/local/mod.rs | 218 ++++++++++ testsuite/forge/src/backend/mod.rs | 9 + testsuite/forge/src/github.rs | 67 +++ testsuite/forge/src/interface/chaos.rs | 126 ++++++ testsuite/forge/src/interface/factory.rs | 28 ++ testsuite/forge/src/interface/mod.rs | 54 +++ testsuite/forge/src/interface/test.rs | 66 +++ testsuite/forge/src/lib.rs | 31 ++ testsuite/forge/src/slack.rs | 40 ++ testsuite/forge/src/test_utils/mod.rs | 4 + 20 files changed, 1829 insertions(+) create mode 100644 testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml create mode 100644 testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml create mode 100644 testsuite/forge/src/backend/k8s/chaos/network_delay.yaml create mode 100644 testsuite/forge/src/backend/k8s/chaos/network_loss.yaml create mode 100644 testsuite/forge/src/backend/k8s/chaos/network_partition.yaml create mode 100644 testsuite/forge/src/backend/k8s/chaos_schema.rs create mode 100644 testsuite/forge/src/backend/k8s/constants.rs create mode 100644 testsuite/forge/src/backend/k8s/kube_api.rs create mode 100644 testsuite/forge/src/backend/k8s/mod.rs create mode 100644 testsuite/forge/src/backend/k8s/stateful_set.rs create mode 100644 testsuite/forge/src/backend/local/mod.rs create mode 100644 testsuite/forge/src/backend/mod.rs create mode 100644 testsuite/forge/src/github.rs create mode 100644 testsuite/forge/src/interface/chaos.rs create mode 100644 testsuite/forge/src/interface/factory.rs create mode 100644 testsuite/forge/src/interface/mod.rs create mode 100644 testsuite/forge/src/interface/test.rs create mode 100644 testsuite/forge/src/lib.rs create mode 100644 testsuite/forge/src/slack.rs create mode 100644 testsuite/forge/src/test_utils/mod.rs diff --git a/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml b/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml new file mode 100644 index 0000000000000..64d771ce5a0ee --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos/cpu_stress.yaml @@ -0,0 +1,16 @@ +apiVersion: chaos-mesh.org/v1alpha1 +kind: StressChaos +metadata: + namespace: {namespace} + name: {name} +spec: + mode: all + selector: + namespaces: + - {namespace} + expressionSelectors: + - {{ key: app.kubernetes.io/instance, operator: In, values: [{instance_labels}] }} + stressors: + cpu: + workers: {num_workers} + load: {load_per_worker} \ No newline at end of file diff --git a/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml b/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml new file mode 100644 index 0000000000000..5c81f759e38b8 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos/network_bandwidth.yaml @@ -0,0 +1,17 @@ +kind: NetworkChaos +apiVersion: chaos-mesh.org/v1alpha1 +metadata: + namespace: {namespace} + name: forge-namespace-{rate}mbps-bandwidth +spec: + action: bandwidth + mode: all + selector: + namespaces: + - {namespace} + labelSelectors: + app.kubernetes.io/name: validator + bandwidth: + rate: "{rate}mbps" + limit: {limit} + buffer: {buffer} diff --git a/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml b/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml new file mode 100644 index 0000000000000..20571fbcd6840 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos/network_delay.yaml @@ -0,0 +1,35 @@ +kind: NetworkChaos +apiVersion: chaos-mesh.org/v1alpha1 +metadata: + namespace: {namespace} + name: {name} +spec: + selector: + namespaces: + - {namespace} + expressionSelectors: + - {{ key: app.kubernetes.io/instance, operator: In, values: [{instance_labels}] }} + mode: all + action: delay + delay: + latency: "{latency_ms}ms" + correlation: "{correlation_percentage}" + jitter: "{jitter_ms}ms" + # Indicates the direction of target packets. Available values include: + # from (the packets from target) + # to (the packets to target) + # both ( the packets from or to target) + # This parameter makes Chaos only take effect for a specific direction of packets. + direction: both + target: + # For delay NetworkChaos, always use "from" direction and always target an entire namespace + # This is because Forge submits API requests to k8s Services, whereas Chaos Mesh applies the chaos + # via netem on the PodIP directly. + # This also hinges on the fact that the Forge workloads (txn-emitter and overall test runner) exist + # in a separate k8s Namespace, so they are not affected by the below selector. + selector: + namespaces: + - {namespace} + expressionSelectors: + - {{ key: app.kubernetes.io/instance, operator: In, values: [{target_instance_labels}] }} + mode: all diff --git a/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml b/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml new file mode 100644 index 0000000000000..9914efe8dbb98 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos/network_loss.yaml @@ -0,0 +1,25 @@ + +kind: NetworkChaos +apiVersion: chaos-mesh.org/v1alpha1 +metadata: + namespace: {namespace} + name: forge-namespace-{loss_percentage}loss-{correlation_percentage}correlation +spec: + selector: + namespaces: + - {namespace} + labelSelectors: + app.kubernetes.io/name: validator + mode: all + action: loss + loss: + loss: "{loss_percentage}" + correlation: "{correlation_percentage}" + direction: both + target: + selector: + namespaces: + - {namespace} + labelSelectors: + app.kubernetes.io/name: validator + mode: all diff --git a/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml b/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml new file mode 100644 index 0000000000000..2f30ea92ccb76 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos/network_partition.yaml @@ -0,0 +1,22 @@ +kind: NetworkChaos +apiVersion: chaos-mesh.org/v1alpha1 +metadata: + namespace: {namespace} + name: forge-namespace-{partition_percentage}-percent-partition +spec: + selector: + namespaces: + - {namespace} + labelSelectors: + app.kubernetes.io/name: validator + mode: all + action: partition + direction: both + target: + selector: + namespaces: + - {namespace} + expressionSelectors: + - {{ key: app.kubernetes.io/instance, operator: In, values: [validator-0, validator-1, validator-2] }} + mode: fixed-percent + value: "{partition_percentage}" diff --git a/testsuite/forge/src/backend/k8s/chaos_schema.rs b/testsuite/forge/src/backend/k8s/chaos_schema.rs new file mode 100644 index 0000000000000..c628fbc7b2cb9 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/chaos_schema.rs @@ -0,0 +1,63 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use kube::CustomResource; +use serde::{Deserialize, Serialize}; + +pub enum Chaos { + Network(NetworkChaos), + Stress(StressChaos), +} + +#[derive(CustomResource, Deserialize, Default, Serialize, Clone, Debug)] +#[kube( + group = "chaos-mesh.org", + version = "v1alpha1", + kind = "NetworkChaos", + status = "ChaosStatus", + plural = "networkchaos", + namespaced, + schema = "disabled" +)] +pub struct NetworkChaosSpec {} + +#[derive(CustomResource, Default, Serialize, Deserialize, Clone, Debug)] +#[kube( + group = "chaos-mesh.org", + version = "v1alpha1", + kind = "StressChaos", + status = "ChaosStatus", + plural = "stresschaos", + namespaced, + schema = "disabled" +)] +pub struct StressChaosSpec {} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct ChaosStatus { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conditions: Option>, +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct ChaosCondition { + #[serde(rename = "type")] + pub r#type: ChaosConditionType, + + pub status: ConditionStatus, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub enum ConditionStatus { + False, + True, + Unknown, +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub enum ChaosConditionType { + Selected, + AllInjected, + AllRecovered, + Paused, +} diff --git a/testsuite/forge/src/backend/k8s/constants.rs b/testsuite/forge/src/backend/k8s/constants.rs new file mode 100644 index 0000000000000..4457483f9d03b --- /dev/null +++ b/testsuite/forge/src/backend/k8s/constants.rs @@ -0,0 +1,54 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +/// A collection of constants and default values for configuring various Forge components. + +// These are test keys for forge ephemeral networks. Do not use these elsewhere! +pub const DEFAULT_ROOT_KEY: &str = + "48136DF3174A3DE92AFDB375FFE116908B69FF6FAB9B1410E548A33FEA1D159D"; +pub const DEFAULT_ROOT_PRIV_KEY: &str = + "E25708D90C72A53B400B27FC7602C4D546C7B7469FA6E12544F0EBFB2F16AE19"; + +// Seed to generate keys for forge tests. +pub const FORGE_KEY_SEED: &str = "80000"; + +// binaries expected to be present on test runner +pub const HELM_BIN: &str = "helm"; +pub const KUBECTL_BIN: &str = "kubectl"; + +// helm release names and helm chart paths +pub const APTOS_NODE_HELM_RELEASE_NAME: &str = "aptos-node"; +pub const GENESIS_HELM_RELEASE_NAME: &str = "genesis"; +pub const APTOS_NODE_HELM_CHART_PATH: &str = "terraform/helm/aptos-node"; +pub const GENESIS_HELM_CHART_PATH: &str = "terraform/helm/genesis"; + +// cleanup namespaces after 30 minutes unless "keep = true" +pub const NAMESPACE_CLEANUP_THRESHOLD_SECS: u64 = 1800; +// Leave a buffer of around 20 minutes for test provisioning and cleanup to be done before cleaning +// up underlying resources. +pub const NAMESPACE_CLEANUP_DURATION_BUFFER_SECS: u64 = 1200; +pub const POD_CLEANUP_THRESHOLD_SECS: u64 = 86400; +pub const MANAGEMENT_CONFIGMAP_PREFIX: &str = "forge-management"; + +// this is the port on the validator service itself, as opposed to 80 on the validator haproxy service +pub const NODE_METRIC_PORT: u32 = 9101; +pub const REST_API_SERVICE_PORT: u32 = 8080; +pub const REST_API_HAPROXY_SERVICE_PORT: u32 = 80; +// when we interact with the node over port-forward +pub const LOCALHOST: &str = "127.0.0.1"; + +// kubernetes service names +pub const VALIDATOR_SERVICE_SUFFIX: &str = "validator"; +pub const FULLNODE_SERVICE_SUFFIX: &str = "fullnode"; +pub const VALIDATOR_HAPROXY_SERVICE_SUFFIX: &str = "validator-lb"; +pub const FULLNODE_HAPROXY_SERVICE_SUFFIX: &str = "fullnode-lb"; +pub const HAPROXY_SERVICE_SUFFIX: &str = "lb"; + +// kubernetes resource names for validator 0, which may be used for templating +pub const VALIDATOR_0_STATEFUL_SET_NAME: &str = "aptos-node-0-validator"; +pub const VALIDATOR_0_GENESIS_SECRET_PREFIX: &str = "aptos-node-0-genesis"; +pub const VALIDATOR_0_DATA_PERSISTENT_VOLUME_CLAIM_PREFIX: &str = "aptos-node-0-validator"; + +// metadata about the cluster +pub const DEFAULT_TEST_SUITE_NAME: &str = "unknown-testsuite"; +pub const DEFAULT_USERNAME: &str = "unknown-username"; diff --git a/testsuite/forge/src/backend/k8s/kube_api.rs b/testsuite/forge/src/backend/k8s/kube_api.rs new file mode 100644 index 0000000000000..3ff3103fdff28 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/kube_api.rs @@ -0,0 +1,361 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use kube::{ + api::{Api, PostParams}, + client::Client as K8sClient, + Error as KubeError, Resource as ApiResource, +}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt::Debug; + +// Create kube API wrapper traits such that they are testable + +#[derive(Clone)] +pub struct K8sApi { + api: Api, +} + +impl K8sApi +where + K: ApiResource, +{ + pub fn from_client(kube_client: K8sClient, kube_namespace: Option) -> Self + where + ::DynamicType: Default, + { + if let Some(kube_namespace) = kube_namespace { + K8sApi { + api: Api::namespaced(kube_client, &kube_namespace), + } + } else { + K8sApi { + api: Api::all(kube_client), + } + } + } +} + +#[async_trait] +pub trait ReadWrite: Send + Sync { + async fn get(&self, name: &str) -> Result; + async fn create(&self, pp: &PostParams, k: &K) -> Result; +} + +// Implement the traits for K8sApi + +#[async_trait] +impl ReadWrite for K8sApi +where + K: k8s_openapi::Resource + Send + Sync + Clone + DeserializeOwned + Serialize + Debug, +{ + async fn get(&self, name: &str) -> Result { + self.api.get(name).await + } + + async fn create(&self, pp: &PostParams, k: &K) -> Result { + self.api.create(pp, k).await + } +} + +#[cfg(test)] +pub mod mocks { + use super::*; + use crate::Result; + use async_trait::async_trait; + use hyper::StatusCode; + use k8s_openapi::api::{ + apps::v1::StatefulSet, + core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Secret, Service}, + }; + use kube::{api::PostParams, error::ErrorResponse, Error as KubeError}; + + // Mock StatefulSet API + + pub struct MockStatefulSetApi { + stateful_set: StatefulSet, + } + + impl MockStatefulSetApi { + pub fn from_stateful_set(stateful_set: StatefulSet) -> Self { + MockStatefulSetApi { stateful_set } + } + } + + #[async_trait] + impl ReadWrite for MockStatefulSetApi { + async fn get(&self, name: &str) -> Result { + if self.stateful_set.metadata.name == Some(name.to_string()) { + return Ok(self.stateful_set.clone()); + } + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "StatefulSet with name {} could not be found in {:?}", + name, self.stateful_set + ), + reason: "not_found".to_string(), + code: 404, + })); + } + + async fn create( + &self, + _pp: &PostParams, + stateful_set: &StatefulSet, + ) -> Result { + if self.stateful_set.metadata.name == stateful_set.metadata.name { + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "StatefulSet with same name already exists in {:?}", + self.stateful_set + ), + reason: "already_exists".to_string(), + code: 409, + })); + } + Ok(self.stateful_set.clone()) + } + } + + // Mock Pod API + + pub struct MockPodApi { + pod: Pod, + } + + impl MockPodApi { + pub fn from_pod(pod: Pod) -> Self { + MockPodApi { pod } + } + } + + #[async_trait] + impl ReadWrite for MockPodApi { + async fn get(&self, _name: &str) -> Result { + Ok(self.pod.clone()) + } + + async fn create(&self, _pp: &PostParams, _pod: &Pod) -> Result { + Ok(self.pod.clone()) + } + } + + // Mock ConfigMap API + + pub struct MockConfigMapApi { + config_map: ConfigMap, + } + + impl MockConfigMapApi { + pub fn from_config_map(config_map: ConfigMap) -> Self { + MockConfigMapApi { config_map } + } + } + + #[async_trait] + impl ReadWrite for MockConfigMapApi { + async fn get(&self, name: &str) -> Result { + if self.config_map.metadata.name == Some(name.to_string()) { + return Ok(self.config_map.clone()); + } + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "ConfigMap with name {} could not be found in {:?}", + name, self.config_map + ), + reason: "not_found".to_string(), + code: 404, + })); + } + + async fn create( + &self, + _pp: &PostParams, + config_map: &ConfigMap, + ) -> Result { + if self.config_map.metadata.name == config_map.metadata.name { + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "ConfigMap with same name already exists in {:?}", + self.config_map + ), + reason: "already_exists".to_string(), + code: 409, + })); + } + Ok(self.config_map.clone()) + } + } + + // Mock PersistentVolumeClaim API + + pub struct MockPersistentVolumeClaimApi { + persistent_volume_claim: PersistentVolumeClaim, + } + + impl MockPersistentVolumeClaimApi { + pub fn from_persistent_volume_claim( + persistent_volume_claim: PersistentVolumeClaim, + ) -> Self { + MockPersistentVolumeClaimApi { + persistent_volume_claim, + } + } + } + + #[async_trait] + impl ReadWrite for MockPersistentVolumeClaimApi { + async fn get(&self, name: &str) -> Result { + if self.persistent_volume_claim.metadata.name == Some(name.to_string()) { + return Ok(self.persistent_volume_claim.clone()); + } + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "PersistentVolumeClaim with name {} could not be found in {:?}", + name, self.persistent_volume_claim + ), + reason: "not_found".to_string(), + code: 404, + })); + } + + async fn create( + &self, + _pp: &PostParams, + persistent_volume_claim: &PersistentVolumeClaim, + ) -> Result { + if self.persistent_volume_claim.metadata.name == persistent_volume_claim.metadata.name { + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "PersistentVolumeClaim with same name already exists in {:?}", + self.persistent_volume_claim + ), + reason: "already_exists".to_string(), + code: 409, + })); + } + Ok(self.persistent_volume_claim.clone()) + } + } + + // Mock Service API + + pub struct MockServiceApi { + service: Service, + } + + impl MockServiceApi { + pub fn from_service(service: Service) -> Self { + MockServiceApi { service } + } + } + + #[async_trait] + impl ReadWrite for MockServiceApi { + async fn get(&self, name: &str) -> Result { + if self.service.metadata.name == Some(name.to_string()) { + return Ok(self.service.clone()); + } + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "Service with name {} could not be found in {:?}", + name, self.service + ), + reason: "not_found".to_string(), + code: 404, + })); + } + + async fn create(&self, _pp: &PostParams, service: &Service) -> Result { + if self.service.metadata.name == service.metadata.name { + return Err(KubeError::Api(ErrorResponse { + status: "failed".to_string(), + message: format!( + "Service with same name already exists in {:?}", + self.service + ), + reason: "already_exists".to_string(), + code: 409, + })); + } + Ok(self.service.clone()) + } + } + + // Mock Service API + pub struct MockSecretApi { + secret: Option, + } + + impl MockSecretApi { + pub fn from_secret(secret: Option) -> Self { + MockSecretApi { secret } + } + } + + #[async_trait] + impl ReadWrite for MockSecretApi { + async fn get(&self, _name: &str) -> Result { + match self.secret { + Some(ref s) => Ok(s.clone()), + None => Err(KubeError::Api(ErrorResponse { + status: "status".to_string(), + message: "message".to_string(), + reason: "reason".to_string(), + code: 404, + })), + } + } + + async fn create(&self, _pp: &PostParams, secret: &Secret) -> Result { + return Ok(secret.clone()); + } + } + + // Mock API that always fails to create a new Namespace + + pub struct FailedNamespacesApi { + status_code: u16, + } + + impl FailedNamespacesApi { + pub fn from_status_code(status_code: u16) -> Self { + FailedNamespacesApi { status_code } + } + } + + #[async_trait] + impl ReadWrite for FailedNamespacesApi { + async fn get(&self, _name: &str) -> Result { + let status = StatusCode::from_u16(self.status_code).unwrap(); + Err(KubeError::Api(ErrorResponse { + status: status.to_string(), + code: status.as_u16(), + message: "Failed to get namespace".to_string(), + reason: "Failed to parse error data".into(), + })) + } + + async fn create( + &self, + _pp: &PostParams, + _namespace: &Namespace, + ) -> Result { + let status = StatusCode::from_u16(self.status_code).unwrap(); + Err(KubeError::Api(ErrorResponse { + status: status.to_string(), + code: status.as_u16(), + message: "Failed to create namespace".to_string(), + reason: "Failed to parse error data".into(), + })) + } + } +} diff --git a/testsuite/forge/src/backend/k8s/mod.rs b/testsuite/forge/src/backend/k8s/mod.rs new file mode 100644 index 0000000000000..105495ca66f68 --- /dev/null +++ b/testsuite/forge/src/backend/k8s/mod.rs @@ -0,0 +1,195 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{Factory, GenesisConfig, GenesisConfigFn, NodeConfigFn, Result, Swarm, Version}; +use anyhow::bail; +use aptos_logger::info; +use rand::rngs::StdRng; +use std::{convert::TryInto, num::NonZeroUsize, time::Duration}; + +pub mod chaos; +pub mod chaos_schema; +mod cluster_helper; +pub mod constants; +mod fullnode; +pub mod kube_api; +pub mod node; +pub mod prometheus; +mod stateful_set; +mod swarm; + +use aptos_sdk::crypto::ed25519::ED25519_PRIVATE_KEY_LENGTH; +pub use cluster_helper::*; +pub use constants::*; +pub use fullnode::*; +#[cfg(test)] +pub use kube_api::mocks::*; +pub use kube_api::*; +pub use node::K8sNode; +pub use stateful_set::*; +pub use swarm::*; + +pub struct K8sFactory { + root_key: [u8; ED25519_PRIVATE_KEY_LENGTH], + image_tag: String, + upgrade_image_tag: String, + kube_namespace: String, + use_port_forward: bool, + reuse: bool, + keep: bool, + enable_haproxy: bool, +} + +impl K8sFactory { + pub fn new( + kube_namespace: String, + image_tag: String, + upgrade_image_tag: String, + use_port_forward: bool, + reuse: bool, + keep: bool, + enable_haproxy: bool, + ) -> Result { + let root_key: [u8; ED25519_PRIVATE_KEY_LENGTH] = + hex::decode(DEFAULT_ROOT_PRIV_KEY)?.try_into().unwrap(); + + match kube_namespace.as_str() { + "default" => { + info!("Using the default kubernetes namespace"); + }, + s if s.starts_with("forge") => { + info!("Using forge namespace: {}", s); + }, + _ => { + bail!( + "Invalid kubernetes namespace provided: {}. Use forge-*", + kube_namespace + ); + }, + } + + Ok(Self { + root_key, + image_tag, + upgrade_image_tag, + kube_namespace, + use_port_forward, + reuse, + keep, + enable_haproxy, + }) + } +} + +#[async_trait::async_trait] +impl Factory for K8sFactory { + fn versions<'a>(&'a self) -> Box + 'a> { + let version = vec![ + Version::new(0, self.image_tag.clone()), + Version::new(1, self.upgrade_image_tag.clone()), + ]; + Box::new(version.into_iter()) + } + + async fn launch_swarm( + &self, + _rng: &mut StdRng, + num_validators: NonZeroUsize, + num_fullnodes: usize, + init_version: &Version, + genesis_version: &Version, + genesis_config: Option<&GenesisConfig>, + cleanup_duration: Duration, + genesis_config_fn: Option, + node_config_fn: Option, + existing_db_tag: Option, + ) -> Result> { + let genesis_modules_path = match genesis_config { + Some(config) => match config { + GenesisConfig::Bundle(_) => { + bail!("k8s forge backend does not support raw bytes as genesis modules. please specify a path instead") + }, + GenesisConfig::Path(path) => Some(path.clone()), + }, + None => None, + }; + + let kube_client = create_k8s_client().await?; + let (new_era, validators, fullnodes) = if self.reuse { + let (validators, fullnodes) = match collect_running_nodes( + &kube_client, + self.kube_namespace.clone(), + self.use_port_forward, + self.enable_haproxy, + ) + .await + { + Ok(res) => res, + Err(e) => { + bail!(e); + }, + }; + let new_era = None; // TODO: get the actual era + (new_era, validators, fullnodes) + } else { + // clear the cluster of resources + delete_k8s_resources(kube_client.clone(), &self.kube_namespace).await?; + // create the forge-management configmap before installing anything + create_management_configmap(self.kube_namespace.clone(), self.keep, cleanup_duration) + .await?; + if let Some(existing_db_tag) = existing_db_tag { + // TODO(prod-eng): For now we are managing PVs out of forge, and bind them manually + // with the volume. Going forward we should consider automate this process. + + // The previously claimed PVs are in Released stage once the corresponding PVC is + // gone. We reset its status to Available so they can be reused later. + reset_persistent_volumes(&kube_client).await?; + + // We return early here if there are not enough PVs to claim. + check_persistent_volumes( + kube_client, + num_validators.get() + num_fullnodes, + existing_db_tag, + ) + .await?; + } + // try installing testnet resources, but clean up if it fails + match install_testnet_resources( + self.kube_namespace.clone(), + num_validators.get(), + num_fullnodes, + format!("{}", init_version), + format!("{}", genesis_version), + genesis_modules_path, + self.use_port_forward, + self.enable_haproxy, + genesis_config_fn, + node_config_fn, + ) + .await + { + Ok(res) => (Some(res.0), res.1, res.2), + Err(e) => { + uninstall_testnet_resources(self.kube_namespace.clone()).await?; + bail!(e); + }, + } + }; + + let swarm = K8sSwarm::new( + &self.root_key, + &self.image_tag, + &self.upgrade_image_tag, + &self.kube_namespace, + validators, + fullnodes, + self.keep, + new_era, + self.use_port_forward, + ) + .await + .unwrap(); + Ok(Box::new(swarm)) + } +} diff --git a/testsuite/forge/src/backend/k8s/stateful_set.rs b/testsuite/forge/src/backend/k8s/stateful_set.rs new file mode 100644 index 0000000000000..46079e451162d --- /dev/null +++ b/testsuite/forge/src/backend/k8s/stateful_set.rs @@ -0,0 +1,398 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{create_k8s_client, k8s_wait_nodes_strategy, K8sApi, ReadWrite, Result, KUBECTL_BIN}; +use again::RetryPolicy; +use anyhow::bail; +use aptos_logger::info; +use json_patch::{Patch as JsonPatch, PatchOperation, ReplaceOperation}; +use k8s_openapi::api::{apps::v1::StatefulSet, core::v1::Pod}; +use kube::{ + api::{Api, Patch, PatchParams}, + client::Client as K8sClient, + ResourceExt, +}; +use serde_json::{json, Value}; +use std::{process::Command, sync::Arc, time::Duration}; +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("{0}")] +enum WorkloadScalingError { + RetryableError(String), + FinalError(String), +} + +pub struct KubeImage { + pub name: String, + pub tag: String, +} + +pub fn get_stateful_set_image(stateful_set: &StatefulSet) -> Result { + let s = stateful_set + .spec + .as_ref() + .expect("Failed to get StatefulSet spec") + .template + .spec + .as_ref() + .expect("Failed to get StatefulSet pod spec") + .containers[0] + .image + .as_ref() + .expect("Failed to get StatefulSet image") + .split(':') + .collect::>(); + + Ok(KubeImage { + name: s[0].to_string(), + tag: s[1].to_string(), + }) +} + +/// Waits for a single K8s StatefulSet to be ready +pub async fn wait_stateful_set( + kube_client: &K8sClient, + kube_namespace: &str, + sts_name: &str, + desired_replicas: u64, + retry_policy: RetryPolicy, +) -> Result<()> { + let stateful_set_api = Arc::new(K8sApi::::from_client( + kube_client.clone(), + Some(kube_namespace.to_string()), + )); + let pod_api = Arc::new(K8sApi::::from_client( + kube_client.clone(), + Some(kube_namespace.to_string()), + )); + retry_policy + .retry_if( + move || { + check_stateful_set_status( + stateful_set_api.clone(), + pod_api.clone(), + sts_name, + desired_replicas, + ) + }, + |e: &WorkloadScalingError| matches!(e, WorkloadScalingError::RetryableError(_)), + ) + .await?; + + Ok(()) +} + +/// Checks the status of a single K8s StatefulSet. Also inspects the pods to make sure they are all ready. +async fn check_stateful_set_status( + stateful_set_api: Arc>, + pod_api: Arc>, + sts_name: &str, + desired_replicas: u64, +) -> Result<(), WorkloadScalingError> { + match stateful_set_api.get(sts_name).await { + Ok(s) => { + let sts_name = &s.name(); + // get the StatefulSet status + if let Some(sts_status) = s.status { + let ready_replicas = sts_status.ready_replicas.unwrap_or(0) as u64; + let replicas = sts_status.replicas as u64; + if ready_replicas == replicas && replicas == desired_replicas { + info!( + "StatefulSet {} has scaled to {}", + sts_name, desired_replicas + ); + return Ok(()); + } + info!( + "StatefulSet {} has {}/{} replicas", + sts_name, ready_replicas, desired_replicas + ); + } + let pod_name = format!("{}-0", sts_name); + // Get the StatefulSet's Pod status + if let Some(status) = pod_api + .get(&pod_name) + .await + .map_err(|e| WorkloadScalingError::RetryableError(e.to_string()))? + .status + { + if let Some(ref container_statuses) = status.container_statuses { + if let Some(container_status) = container_statuses.last() { + if let Some(state) = &container_status.state { + if let Some(waiting) = &state.waiting { + if let Some(waiting_reason) = &waiting.reason { + match waiting_reason.as_str() { + "ImagePullBackOff" => { + info!("Pod {} has ImagePullBackOff", &pod_name); + return Err(WorkloadScalingError::FinalError( + "ImagePullBackOff".to_string(), + )); + }, + "CrashLoopBackOff" => { + info!("Pod {} has CrashLoopBackOff", &pod_name); + return Err(WorkloadScalingError::FinalError( + "CrashLoopBackOff".to_string(), + )); + }, + "ErrImagePull" => { + info!("Pod {} has ErrImagePull", &pod_name); + return Err(WorkloadScalingError::FinalError( + "ErrImagePull".to_string(), + )); + }, + _ => { + info!("Waiting for pod {}", &pod_name); + return Err(WorkloadScalingError::RetryableError( + format!("Waiting for pod {}", &pod_name), + )); + }, + } + } + } + } + } + } + if let Some(phase) = status.phase.as_ref() { + info!("Pod {} at phase {}", &pod_name, phase) + } + Err(WorkloadScalingError::RetryableError(format!( + "Retry due to pod {} status {:?}", + &pod_name, status + ))) + } else { + Err(WorkloadScalingError::FinalError(format!( + "Pod {} status not found", + &pod_name + ))) + } + }, + Err(e) => { + info!("Failed to get StatefulSet: {}", e); + Err(WorkloadScalingError::RetryableError(format!( + "Failed to get StatefulSet: {}", + e + ))) + }, + } +} + +/// Given the name of a node's StatefulSet, sets the node's image tag. Assumes that the StatefulSet has only one container +/// Note that this function will not wait for the StatefulSet to be ready. +pub async fn set_stateful_set_image_tag( + stateful_set_name: String, + container_name: String, + image_tag: String, + kube_namespace: String, +) -> Result<()> { + let kube_client: K8sClient = create_k8s_client().await?; + let sts_api: Api = Api::namespaced(kube_client.clone(), &kube_namespace); + let sts = sts_api.get(&stateful_set_name).await?; + let image_repo = get_stateful_set_image(&sts)?.name; + + // replace the image tag + let new_image = format!("{}:{}", &image_repo, &image_tag); + + // set the image using kubectl + // patching the node spec may not work + Command::new(KUBECTL_BIN) + .args([ + "-n", + &kube_namespace, + "set", + "image", + &format!("statefulset/{}", &stateful_set_name), + &format!("{}={}", &container_name, &new_image), + ]) + .status() + .expect("Failed to set image for StatefulSet"); + + Ok(()) +} + +/// Scales the given StatefulSet to the given number of replicas +pub async fn scale_stateful_set_replicas( + sts_name: &str, + kube_namespace: &str, + replica_num: u64, +) -> Result<()> { + let kube_client = create_k8s_client().await?; + let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); + let pp = PatchParams::apply("forge").force(); + let patch = serde_json::json!({ + "apiVersion": "apps/v1", + "kind": "StatefulSet", + "metadata": { + "name": sts_name, + }, + "spec": { + "replicas": replica_num, + } + }); + let patch = Patch::Apply(&patch); + stateful_set_api.patch(sts_name, &pp, &patch).await?; + // retry for ~5 min at a fixed interval + let retry_policy = RetryPolicy::fixed(Duration::from_secs(10)).with_max_retries(6 * 5); + wait_stateful_set( + &kube_client, + kube_namespace, + sts_name, + replica_num, + retry_policy, + ) + .await?; + + Ok(()) +} + +pub async fn set_identity( + sts_name: &str, + kube_namespace: &str, + k8s_secret_name: &str, +) -> Result<()> { + let kube_client = create_k8s_client().await?; + let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); + let patch_op = PatchOperation::Replace(ReplaceOperation { + // The json path below should match `terraform/helm/aptos-node/templates/validator.yaml`. + path: "/spec/template/spec/volumes/1/secret/secretName".to_string(), + value: json!(k8s_secret_name), + }); + let patch: Patch = Patch::Json(JsonPatch(vec![patch_op])); + let pp = PatchParams::apply("forge"); + stateful_set_api.patch(sts_name, &pp, &patch).await?; + Ok(()) +} + +pub async fn get_identity(sts_name: &str, kube_namespace: &str) -> Result { + let kube_client = create_k8s_client().await?; + let stateful_set_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); + let sts = stateful_set_api.get(sts_name).await?; + // The json path below should match `terraform/helm/aptos-node/templates/validator.yaml`. + let secret_name = sts.spec.unwrap().template.spec.unwrap().volumes.unwrap()[1] + .secret + .clone() + .unwrap() + .secret_name + .unwrap(); + Ok(secret_name) +} + +pub async fn check_for_container_restart( + kube_client: &K8sClient, + kube_namespace: &str, + sts_name: &str, +) -> Result<()> { + aptos_retrier::retry_async(k8s_wait_nodes_strategy(), || { + let pod_api: Api = Api::namespaced(kube_client.clone(), kube_namespace); + Box::pin(async move { + // Get the StatefulSet's Pod status + let pod_name = format!("{}-0", sts_name); + if let Some(status) = pod_api.get_status(&pod_name).await?.status { + if let Some(container_statuses) = status.container_statuses { + for container_status in container_statuses { + if container_status.restart_count > 0 { + bail!( + "Container {} in pod {} restarted {} times ", + container_status.name, + &pod_name, + container_status.restart_count + ); + } + } + return Ok(()); + } + // In case of no restarts, k8 apis returns no container statuses + Ok(()) + } else { + bail!("Can't query the pod status for {}", sts_name) + } + }) + }) + .await +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{MockPodApi, MockStatefulSetApi}; + use k8s_openapi::{ + api::{ + apps::v1::{StatefulSet, StatefulSetSpec, StatefulSetStatus}, + core::v1::{ContainerState, ContainerStateWaiting, ContainerStatus, PodStatus}, + }, + apimachinery::pkg::apis::meta::v1::ObjectMeta, + }; + + #[tokio::test] + async fn test_check_stateful_set_status() { + // mock a StatefulSet with 0/1 replicas + // this should then mean we check the underlying pod to see what's up + let stateful_set_api = Arc::new(MockStatefulSetApi::from_stateful_set(StatefulSet { + metadata: ObjectMeta { + name: Some("test-stateful-set".to_string()), + ..ObjectMeta::default() + }, + spec: Some(StatefulSetSpec { + replicas: Some(1), + ..StatefulSetSpec::default() + }), + status: Some(StatefulSetStatus { + replicas: 1, + ready_replicas: Some(0), + ..StatefulSetStatus::default() + }), + })); + + // we should retry if the pod status is not explicitly bad + let pod_default_api = Arc::new(MockPodApi::from_pod(Pod { + status: Some(PodStatus::default()), + ..Pod::default() + })); + let ret = check_stateful_set_status( + stateful_set_api.clone(), + pod_default_api.clone(), + "test-stateful-set", + 1, + ) + .await; + assert!(matches!( + ret.err(), + Some(WorkloadScalingError::RetryableError(_)) + )); + + // the pod explicitly has a bad status, so we should fail fast + let pod_default_api = Arc::new(MockPodApi::from_pod(Pod { + metadata: ObjectMeta { + name: Some("test-stateful-set-0".to_string()), + ..ObjectMeta::default() + }, + status: Some(PodStatus { + container_statuses: Some(vec![ContainerStatus { + name: "test-container".to_string(), + restart_count: 0, + state: Some(ContainerState { + waiting: Some(ContainerStateWaiting { + reason: Some("CrashLoopBackOff".to_string()), + ..ContainerStateWaiting::default() + }), + ..ContainerState::default() + }), + ..ContainerStatus::default() + }]), + ..PodStatus::default() + }), + ..Pod::default() + })); + let ret = check_stateful_set_status( + stateful_set_api.clone(), + pod_default_api.clone(), + "test-stateful-set", + 1, + ) + .await; + assert!(matches!( + ret.err(), + Some(WorkloadScalingError::FinalError(_)) + )); + } +} diff --git a/testsuite/forge/src/backend/local/mod.rs b/testsuite/forge/src/backend/local/mod.rs new file mode 100644 index 0000000000000..ae533eb1d8162 --- /dev/null +++ b/testsuite/forge/src/backend/local/mod.rs @@ -0,0 +1,218 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{Factory, GenesisConfig, GenesisConfigFn, NodeConfigFn, Result, Swarm, Version}; +use anyhow::{bail, Context}; +use aptos_config::config::{NodeConfig, OverrideNodeConfig}; +use aptos_framework::ReleaseBundle; +use aptos_genesis::builder::{InitConfigFn, InitGenesisConfigFn, InitGenesisStakeFn}; +use aptos_infallible::Mutex; +use rand::rngs::StdRng; +use std::{ + collections::HashMap, + num::NonZeroUsize, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, +}; + +mod cargo; +mod node; +mod swarm; +pub use self::swarm::ActiveNodesGuard; +pub use cargo::cargo_build_common_args; +pub use node::LocalNode; +pub use swarm::{LocalSwarm, SwarmDirectory}; + +#[derive(Clone, Debug)] +pub struct LocalVersion { + bin: PathBuf, + version: Version, +} + +impl LocalVersion { + pub fn new(bin: PathBuf, version: Version) -> Self { + Self { bin, version } + } + + pub fn bin(&self) -> &Path { + &self.bin + } + + pub fn version(&self) -> Version { + self.version.clone() + } +} + +pub struct LocalFactory { + versions: Arc>, + swarm_dir: Option, +} + +impl LocalFactory { + pub fn new(versions: HashMap, swarm_dir: Option) -> Self { + Self { + versions: Arc::new(versions), + swarm_dir, + } + } + + pub fn from_workspace(swarm_dir: Option) -> Result { + let mut versions = HashMap::new(); + let new_version = cargo::get_aptos_node_binary_from_worktree().map(|(revision, bin)| { + let version = Version::new(usize::max_value(), revision); + LocalVersion { bin, version } + })?; + + versions.insert(new_version.version.clone(), new_version); + Ok(Self::new(versions, swarm_dir)) + } + + pub fn from_revision(revision: &str) -> Result { + let mut versions = HashMap::new(); + let new_version = + cargo::get_aptos_node_binary_at_revision(revision).map(|(revision, bin)| { + let version = Version::new(usize::max_value(), revision); + LocalVersion { bin, version } + })?; + + versions.insert(new_version.version.clone(), new_version); + Ok(Self::new(versions, None)) + } + + pub fn with_revision_and_workspace(revision: &str) -> Result { + let workspace = cargo::get_aptos_node_binary_from_worktree().map(|(revision, bin)| { + let version = Version::new(usize::max_value(), revision); + LocalVersion { bin, version } + })?; + let revision = + cargo::get_aptos_node_binary_at_revision(revision).map(|(revision, bin)| { + let version = Version::new(usize::min_value(), revision); + LocalVersion { bin, version } + })?; + + let mut versions = HashMap::new(); + versions.insert(workspace.version(), workspace); + versions.insert(revision.version(), revision); + Ok(Self::new(versions, None)) + } + + /// Create a LocalFactory with a aptos-node version built at the tip of upstream/main and the + /// current workspace, suitable for compatibility testing. + pub fn with_upstream_and_workspace() -> Result { + let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?; + Self::with_revision_and_workspace(&upstream_main) + } + + /// Create a LocalFactory with a aptos-node version built at merge-base of upstream/main and the + /// current workspace, suitable for compatibility testing. + pub fn with_upstream_merge_base_and_workspace() -> Result { + let upstream_main = cargo::git_get_upstream_remote().map(|r| format!("{}/main", r))?; + let merge_base = cargo::git_merge_base(upstream_main)?; + Self::with_revision_and_workspace(&merge_base) + } + + pub async fn new_swarm_with_version( + &self, + rng: R, + number_of_validators: NonZeroUsize, + number_of_fullnodes: usize, + version: &Version, + genesis_framework: Option, + init_config: Option, + vfn_config: Option, + init_genesis_stake: Option, + init_genesis_config: Option, + guard: ActiveNodesGuard, + ) -> Result + where + R: ::rand::RngCore + ::rand::CryptoRng, + { + let swarmdir = self.swarm_dir.clone().map(|sd| PathBuf::from(sd.as_str())); + // Build the swarm + let mut swarm = LocalSwarm::build( + rng, + number_of_validators, + self.versions.clone(), + Some(version.clone()), + init_config, + init_genesis_stake, + init_genesis_config, + swarmdir, + genesis_framework, + guard, + )?; + + // Launch the swarm + swarm + .launch() + .await + .with_context(|| format!("Swarm logs can be found here: {}", swarm.logs_location()))?; + + let vfn_config = vfn_config.unwrap_or_else(NodeConfig::get_default_vfn_config); + let vfn_override_config = OverrideNodeConfig::new_with_default_base(vfn_config); + + // Add and launch the fullnodes + let validator_peer_ids = swarm.validators().map(|v| v.peer_id()).collect::>(); + for validator_peer_id in validator_peer_ids.iter().take(number_of_fullnodes) { + let _ = swarm + .add_validator_fullnode(version, vfn_override_config.clone(), *validator_peer_id) + .unwrap(); + } + swarm.wait_all_alive(Duration::from_secs(60)).await?; + + Ok(swarm) + } +} + +#[async_trait::async_trait] +impl Factory for LocalFactory { + fn versions<'a>(&'a self) -> Box + 'a> { + Box::new(self.versions.keys().cloned()) + } + + async fn launch_swarm( + &self, + rng: &mut StdRng, + num_validators: NonZeroUsize, + num_fullnodes: usize, + version: &Version, + _genesis_version: &Version, + genesis_config: Option<&GenesisConfig>, + _cleanup_duration: Duration, + _genesis_config_fn: Option, + _node_config_fn: Option, + _existing_db_tag: Option, + ) -> Result> { + let framework = match genesis_config { + Some(config) => match config { + GenesisConfig::Bundle(bundle) => Some(bundle.clone()), + GenesisConfig::Path(_) => { + bail!("local forge backend does not support flattened dir for genesis") + }, + }, + None => None, + }; + + // no guarding, as this code path is not used in parallel + let guard = ActiveNodesGuard::grab(1, Arc::new(Mutex::new(0))).await; + + let swarm = self + .new_swarm_with_version( + rng, + num_validators, + num_fullnodes, + version, + framework, + None, + None, + None, + None, + guard, + ) + .await?; + + Ok(Box::new(swarm)) + } +} diff --git a/testsuite/forge/src/backend/mod.rs b/testsuite/forge/src/backend/mod.rs new file mode 100644 index 0000000000000..24b996e75ca45 --- /dev/null +++ b/testsuite/forge/src/backend/mod.rs @@ -0,0 +1,9 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod local; +pub use local::{LocalNode, *}; + +mod k8s; +pub use k8s::{K8sNode, *}; diff --git a/testsuite/forge/src/github.rs b/testsuite/forge/src/github.rs new file mode 100644 index 0000000000000..300419dfb33b8 --- /dev/null +++ b/testsuite/forge/src/github.rs @@ -0,0 +1,67 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use anyhow::{anyhow, format_err, Result}; +use reqwest::{header::USER_AGENT, Url}; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct CommitInfo { + pub sha: String, + pub commit: GitCommitInfo, +} + +#[derive(Debug, Deserialize)] +pub struct GitCommitInfo { + pub author: Author, + pub message: String, +} + +#[derive(Debug, Deserialize)] +pub struct Author { + pub name: String, + pub email: String, +} + +pub struct GitHub { + client: reqwest::blocking::Client, +} + +impl GitHub { + pub fn new() -> GitHub { + let client = reqwest::blocking::Client::new(); + GitHub { client } + } + + /// repo in format owner/repo_name + /// sha can be long or short hash, or branch name + /// Paging is not implemented yet + pub fn get_commits(&self, repo: &str, sha: &str) -> Result> { + let url = format!("https://api.github.com/repos/{}/commits?sha={}", repo, sha); + let url: Url = url.parse().map_err(|e| { + anyhow!( + "Failed to parse github url: {:?}\n, resulted in Error:{}", + url, + e + ) + })?; + let request = self.client.get(url); + let response = request + .header(USER_AGENT, "aptos-forge") + .send() + .map_err(|e| format_err!("Failed to query github: {:?}", e))?; + let response: Vec = response + .json() + .map_err(|e| format_err!("Failed to parse github response: {:?}", e))?; + Ok(response) + } +} + +impl Default for GitHub { + fn default() -> Self { + Self::new() + } +} diff --git a/testsuite/forge/src/interface/chaos.rs b/testsuite/forge/src/interface/chaos.rs new file mode 100644 index 0000000000000..cb2375f643182 --- /dev/null +++ b/testsuite/forge/src/interface/chaos.rs @@ -0,0 +1,126 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_sdk::types::PeerId; +use std::fmt::{Display, Formatter}; + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub enum SwarmChaos { + Delay(SwarmNetworkDelay), + Partition(SwarmNetworkPartition), + Bandwidth(SwarmNetworkBandwidth), + Loss(SwarmNetworkLoss), + NetEm(SwarmNetEm), + CpuStress(SwarmCpuStress), +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmNetworkDelay { + pub group_network_delays: Vec, +} + +impl Display for SwarmNetworkDelay { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "Delay nodes {:?}", self.group_network_delays) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct GroupNetworkDelay { + pub name: String, + pub source_nodes: Vec, + pub target_nodes: Vec, + pub latency_ms: u64, + pub jitter_ms: u64, + pub correlation_percentage: u64, +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmNetworkPartition { + pub partition_percentage: u64, +} + +impl Display for SwarmNetworkPartition { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "Partition {} nodes", self.partition_percentage) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmNetworkBandwidth { + pub group_network_bandwidths: Vec, +} + +impl Display for SwarmNetworkBandwidth { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "Bandwidth nodes {:?}", self.group_network_bandwidths) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct GroupNetworkBandwidth { + pub name: String, + /// Rate in megabytes per second + pub rate: u64, + pub limit: u64, + pub buffer: u64, +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmNetworkLoss { + pub loss_percentage: u64, + pub correlation_percentage: u64, +} + +impl Display for SwarmNetworkLoss { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "Loss on all nodes: loss {}, correlation {},", + self.loss_percentage, self.correlation_percentage, + ) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmNetEm { + pub group_netems: Vec, +} + +impl Display for SwarmNetEm { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "NetEm nodes {:?}", self.group_netems) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct GroupNetEm { + pub name: String, + pub source_nodes: Vec, + pub target_nodes: Vec, + pub delay_latency_ms: u64, + pub delay_jitter_ms: u64, + pub delay_correlation_percentage: u64, + pub loss_percentage: u64, + pub loss_correlation_percentage: u64, + pub rate_in_mbps: u64, +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct SwarmCpuStress { + pub group_cpu_stresses: Vec, +} + +impl Display for SwarmCpuStress { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "CpuStress nodes {:?}", self.group_cpu_stresses) + } +} + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct GroupCpuStress { + pub name: String, + pub target_nodes: Vec, + pub num_workers: u64, + pub load_per_worker: u64, +} diff --git a/testsuite/forge/src/interface/factory.rs b/testsuite/forge/src/interface/factory.rs new file mode 100644 index 0000000000000..8af6ab1de8b4b --- /dev/null +++ b/testsuite/forge/src/interface/factory.rs @@ -0,0 +1,28 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::{GenesisConfig, Swarm, Version}; +use crate::{GenesisConfigFn, NodeConfigFn, Result}; +use rand::rngs::StdRng; +use std::{num::NonZeroUsize, time::Duration}; + +/// Trait used to represent a interface for constructing a launching new networks +#[async_trait::async_trait] +pub trait Factory { + fn versions<'a>(&'a self) -> Box + 'a>; + + async fn launch_swarm( + &self, + rng: &mut StdRng, + num_validators: NonZeroUsize, + num_fullnodes: usize, + version: &Version, + genesis_version: &Version, + genesis_modules: Option<&GenesisConfig>, + cleanup_duration: Duration, + genesis_config_fn: Option, + node_config_fn: Option, + existing_db_tag: Option, + ) -> Result>; +} diff --git a/testsuite/forge/src/interface/mod.rs b/testsuite/forge/src/interface/mod.rs new file mode 100644 index 0000000000000..5a6507219e0ef --- /dev/null +++ b/testsuite/forge/src/interface/mod.rs @@ -0,0 +1,54 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod admin; +pub use admin::*; +mod aptos; +pub use self::aptos::*; +mod network; +pub use network::*; +mod test; +pub use test::*; +mod factory; +pub use factory::*; +mod swarm; +pub use swarm::*; +mod chaos; +pub use chaos::*; +mod node; +pub use node::*; +mod chain_info; +pub mod prometheus_metrics; + +use aptos_framework::ReleaseBundle; +pub use chain_info::*; + +/// A wrapper around a usize in order to represent an opaque version of a Node. +/// +/// It is intended that backends will be able to take this opaque version identifier and lookup the +/// appropriate version information internally to be able to determine the version of node software +/// to use. +/// +/// It's expected that `Version`s returned by querying a `Factory` or a `Swarm` will be sort-able +/// such that they'll be ordered with older versions first, e.g. older -> newer. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Version(usize, String); + +impl Version { + pub fn new(version: usize, display_string: String) -> Self { + Self(version, display_string) + } +} + +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.1) + } +} + +#[derive(Clone)] +pub enum GenesisConfig { + Bundle(ReleaseBundle), + Path(String), +} diff --git a/testsuite/forge/src/interface/test.rs b/testsuite/forge/src/interface/test.rs new file mode 100644 index 0000000000000..72c78e6a64514 --- /dev/null +++ b/testsuite/forge/src/interface/test.rs @@ -0,0 +1,66 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use rand::SeedableRng; + +/// Whether a test is expected to fail or not +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ShouldFail { + No, + Yes, + YesWithMessage(&'static str), +} + +/// Represents a Test in Forge +/// +/// This is meant to be a super trait of the other test interfaces. +pub trait Test: Send + Sync { + /// Returns the name of the Test + fn name(&self) -> &'static str; + + /// Indicates if the Test should be ignored + fn ignored(&self) -> bool { + false + } + + /// Indicates if the Test should fail + fn should_fail(&self) -> ShouldFail { + ShouldFail::No + } +} + +impl Test for &T { + fn name(&self) -> &'static str { + (**self).name() + } + + fn ignored(&self) -> bool { + (**self).ignored() + } + + fn should_fail(&self) -> ShouldFail { + (**self).should_fail() + } +} + +#[derive(Debug)] +pub struct CoreContext { + rng: ::rand::rngs::StdRng, +} + +impl CoreContext { + pub fn new(rng: ::rand::rngs::StdRng) -> Self { + Self { rng } + } + + pub fn from_rng(rng: R) -> Self { + Self { + rng: ::rand::rngs::StdRng::from_rng(rng).unwrap(), + } + } + + pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng { + &mut self.rng + } +} diff --git a/testsuite/forge/src/lib.rs b/testsuite/forge/src/lib.rs new file mode 100644 index 0000000000000..bdd8ec3cc6eeb --- /dev/null +++ b/testsuite/forge/src/lib.rs @@ -0,0 +1,31 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Forge is a framework for writing and running end-to-end tests in Aptos + +pub use anyhow::Result; + +mod interface; +pub use interface::*; + +mod runner; +pub use runner::*; + +mod backend; +pub use aptos_transaction_emitter_lib::*; +pub use aptos_transaction_generator_lib::*; +pub use backend::*; + +mod report; +pub use report::*; + +mod github; +pub use github::*; + +mod slack; +pub use slack::*; + +pub mod success_criteria; + +pub mod test_utils; diff --git a/testsuite/forge/src/slack.rs b/testsuite/forge/src/slack.rs new file mode 100644 index 0000000000000..45b97dfc764b3 --- /dev/null +++ b/testsuite/forge/src/slack.rs @@ -0,0 +1,40 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use anyhow::{bail, format_err, Result}; +use reqwest::{self, Url}; +use serde_json::{self, json}; + +pub struct SlackClient { + client: reqwest::blocking::Client, +} + +impl SlackClient { + pub fn new() -> Self { + let client = reqwest::blocking::Client::new(); + Self { client } + } + + pub fn send_message(&self, url: &Url, msg: &str) -> Result<()> { + let msg = json!({ "text": msg }); + let msg = serde_json::to_string(&msg) + .map_err(|e| format_err!("Failed to serialize message for slack: {:?}", e))?; + let request = self.client.post(url.clone()).body(msg); + let response = request + .send() + .map_err(|e| format_err!("Failed to send slack message: {:?}", e))?; + if !response.status().is_success() { + bail!("Slack service returned error code: {}", response.status()) + } + Ok(()) + } +} + +impl Default for SlackClient { + fn default() -> Self { + Self::new() + } +} diff --git a/testsuite/forge/src/test_utils/mod.rs b/testsuite/forge/src/test_utils/mod.rs new file mode 100644 index 0000000000000..6a09962387803 --- /dev/null +++ b/testsuite/forge/src/test_utils/mod.rs @@ -0,0 +1,4 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +pub mod consensus_utils; From 0e582c6e0e1a90cc9f4c97c293c0ce318ea1acdf Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:41:46 +0100 Subject: [PATCH 107/147] fix: aptos-release-builder. --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7fd230335f981..0bfd4e95825bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "aptos-move/aptos-gas-schedule-updator", "aptos-move/aptos-memory-usage-tracker", "aptos-move/aptos-native-interface", + "aptos-move/aptos-release-builder", "aptos-move/aptos-resource-viewer", "aptos-move/aptos-sdk-builder", "aptos-move/aptos-transaction-benchmarks", @@ -396,6 +397,7 @@ aptos-protos = { path = "protos/rust" } aptos-proxy = { path = "crates/proxy" } aptos-push-metrics = { path = "crates/aptos-push-metrics" } aptos-rate-limiter = { path = "crates/aptos-rate-limiter" } +aptos-release-builder = { path = "aptos-move/aptos-release-builder" } aptos-reliable-broadcast = { path = "crates/reliable-broadcast" } aptos-resource-viewer = { path = "aptos-move/aptos-resource-viewer" } aptos-rest-client = { path = "crates/aptos-rest-client" } From e799acadc884841adcfb90f71304ce61e24294bf Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 08:45:00 +0100 Subject: [PATCH 108/147] fix: aptos-rosetta. --- Cargo.toml | 3 + crates/aptos-rosetta-cli/Cargo.toml | 26 + crates/aptos-rosetta-cli/README.md | 7 + crates/aptos-rosetta-cli/src/account.rs | 75 ++ crates/aptos-rosetta-cli/src/block.rs | 54 ++ crates/aptos-rosetta-cli/src/common.rs | 116 +++ crates/aptos-rosetta-cli/src/construction.rs | 348 +++++++ crates/aptos-rosetta-cli/src/main.rs | 51 + crates/aptos-rosetta-cli/src/network.rs | 79 ++ crates/aptos-rosetta/README.md | 106 ++ crates/aptos-rosetta/aptos.ros | 146 +++ crates/aptos-rosetta/rosetta_cli.json | 70 ++ crates/aptos-rosetta/src/block.rs | 213 ++++ crates/aptos-rosetta/src/client.rs | 909 ++++++++++++++++++ crates/aptos-rosetta/src/error.rs | 353 +++++++ crates/aptos-rosetta/src/lib.rs | 240 +++++ crates/aptos-rosetta/src/main.rs | 286 ++++++ crates/aptos-rosetta/src/network.rs | 172 ++++ crates/aptos-rosetta/src/types/identifiers.rs | 558 +++++++++++ crates/aptos-rosetta/src/types/mod.rs | 14 + crates/aptos-rosetta/src/types/move_types.rs | 246 +++++ crates/aptos-rosetta/src/types/requests.rs | 569 +++++++++++ 22 files changed, 4641 insertions(+) create mode 100644 crates/aptos-rosetta-cli/Cargo.toml create mode 100644 crates/aptos-rosetta-cli/README.md create mode 100644 crates/aptos-rosetta-cli/src/account.rs create mode 100644 crates/aptos-rosetta-cli/src/block.rs create mode 100644 crates/aptos-rosetta-cli/src/common.rs create mode 100644 crates/aptos-rosetta-cli/src/construction.rs create mode 100644 crates/aptos-rosetta-cli/src/main.rs create mode 100644 crates/aptos-rosetta-cli/src/network.rs create mode 100644 crates/aptos-rosetta/README.md create mode 100644 crates/aptos-rosetta/aptos.ros create mode 100644 crates/aptos-rosetta/rosetta_cli.json create mode 100644 crates/aptos-rosetta/src/block.rs create mode 100644 crates/aptos-rosetta/src/client.rs create mode 100644 crates/aptos-rosetta/src/error.rs create mode 100644 crates/aptos-rosetta/src/lib.rs create mode 100644 crates/aptos-rosetta/src/main.rs create mode 100644 crates/aptos-rosetta/src/network.rs create mode 100644 crates/aptos-rosetta/src/types/identifiers.rs create mode 100644 crates/aptos-rosetta/src/types/mod.rs create mode 100644 crates/aptos-rosetta/src/types/move_types.rs create mode 100644 crates/aptos-rosetta/src/types/requests.rs diff --git a/Cargo.toml b/Cargo.toml index 0bfd4e95825bd..6aefcd6625b68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,8 @@ members = [ "crates/aptos-rate-limiter", "crates/aptos-rest-client", "crates/aptos-retrier", + "crates/aptos-rosetta", + "crates/aptos-rosetta-cli", "crates/aptos-runtimes", "crates/aptos-speculative-state-helper", "crates/aptos-system-utils", @@ -403,6 +405,7 @@ aptos-resource-viewer = { path = "aptos-move/aptos-resource-viewer" } aptos-rest-client = { path = "crates/aptos-rest-client" } aptos-retrier = { path = "crates/aptos-retrier" } aptos-rocksdb-options = { path = "storage/rocksdb-options" } +aptos-rosetta = { path = "crates/aptos-rosetta" } aptos-runtimes = { path = "crates/aptos-runtimes" } aptos-safety-rules = { path = "consensus/safety-rules" } aptos-schemadb = { path = "storage/schemadb" } diff --git a/crates/aptos-rosetta-cli/Cargo.toml b/crates/aptos-rosetta-cli/Cargo.toml new file mode 100644 index 0000000000000..ff37ec922ae04 --- /dev/null +++ b/crates/aptos-rosetta-cli/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "aptos-rosetta-cli" +description = "Aptos Rosetta CLI for testing" +version = "0.0.1" + +# Workspace inherited keys +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +publish = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +aptos = { workspace = true } +aptos-logger = { workspace = true } +aptos-rosetta = { workspace = true } +aptos-types = { workspace = true } +clap = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true } +url = { workspace = true } + diff --git a/crates/aptos-rosetta-cli/README.md b/crates/aptos-rosetta-cli/README.md new file mode 100644 index 0000000000000..35242587b1b87 --- /dev/null +++ b/crates/aptos-rosetta-cli/README.md @@ -0,0 +1,7 @@ +## Rosetta CLI + +The Rosetta CLI is used for testing and is not used for any production use case. The CLI uses the same client as the end-to-end tests to ensure consistency but with the caveat that it will panic for cases that aren't expected. + +The intent of this CLI is to let developers that know about the Aptos ecosystem simply call Rosetta APIs while actively developing on [Rosetta](https://en.wikipedia.org/wiki/Rosetta_(software). + +You can get more help about the commands with `aptos-rosetta-cli --help` diff --git a/crates/aptos-rosetta-cli/src/account.rs b/crates/aptos-rosetta-cli/src/account.rs new file mode 100644 index 0000000000000..0f734884b8445 --- /dev/null +++ b/crates/aptos-rosetta-cli/src/account.rs @@ -0,0 +1,75 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{format_output, BlockArgs, NetworkArgs, UrlArgs}; +use aptos_rosetta::{ + common::native_coin, + types::{AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier}, +}; +use aptos_types::account_address::AccountAddress; +use clap::{Parser, Subcommand}; + +/// Account APIs +/// +/// Used for pulling state of an account at a point in time +/// +/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html) +#[derive(Debug, Subcommand)] +pub enum AccountCommand { + Balance(AccountBalanceCommand), +} + +impl AccountCommand { + pub async fn execute(self) -> anyhow::Result { + match self { + AccountCommand::Balance(inner) => format_output(inner.execute().await), + } + } +} + +/// Retrieve the balance for an account +/// +/// [API Spec](https://www.rosetta-api.org/docs/AccountApi.html#accountbalance) +#[derive(Debug, Parser)] +pub struct AccountBalanceCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + block_args: BlockArgs, + /// Whether to filter the currency to the native coin + #[clap(long)] + filter_currency: bool, + /// Account to list the balance + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + account: AccountAddress, + /// Whether to show the amount of stake instead of the normal balance + #[clap(long)] + stake_amount: bool, +} + +impl AccountBalanceCommand { + #[allow(clippy::manual_retain)] + pub async fn execute(self) -> anyhow::Result { + let account_identifier = if self.stake_amount { + AccountIdentifier::total_stake_account(self.account) + } else { + AccountIdentifier::base_account(self.account) + }; + + let client = self.url_args.client(); + client + .account_balance(&AccountBalanceRequest { + network_identifier: self.network_args.network_identifier(), + account_identifier, + block_identifier: self.block_args.into(), + currencies: if self.filter_currency { + Some(vec![native_coin()]) + } else { + None + }, + }) + .await + } +} diff --git a/crates/aptos-rosetta-cli/src/block.rs b/crates/aptos-rosetta-cli/src/block.rs new file mode 100644 index 0000000000000..d22f35be8125f --- /dev/null +++ b/crates/aptos-rosetta-cli/src/block.rs @@ -0,0 +1,54 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{format_output, BlockArgs, NetworkArgs, UrlArgs}; +use aptos_rosetta::types::{BlockRequest, BlockRequestMetadata, BlockResponse}; +use clap::{Parser, Subcommand}; + +/// Block APIs +/// +/// Used for pulling blocks from the blockchain +/// +/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html) +#[derive(Debug, Subcommand)] +pub enum BlockCommand { + Get(GetBlockCommand), +} + +impl BlockCommand { + pub async fn execute(self) -> anyhow::Result { + match self { + BlockCommand::Get(inner) => format_output(inner.execute().await), + } + } +} + +/// Get a block by transaction hash or version +/// +/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block) +#[derive(Debug, Parser)] +pub struct GetBlockCommand { + #[clap(flatten)] + block_args: BlockArgs, + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, +} + +impl GetBlockCommand { + pub async fn execute(self) -> anyhow::Result { + let metadata = self + .block_args + .keep_all_transactions + .map(|inner| BlockRequestMetadata { + keep_empty_transactions: Some(inner), + }); + let request = BlockRequest { + network_identifier: self.network_args.network_identifier(), + block_identifier: self.block_args.into(), + metadata, + }; + self.url_args.client().block(&request).await + } +} diff --git a/crates/aptos-rosetta-cli/src/common.rs b/crates/aptos-rosetta-cli/src/common.rs new file mode 100644 index 0000000000000..dd55263365f1d --- /dev/null +++ b/crates/aptos-rosetta-cli/src/common.rs @@ -0,0 +1,116 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{account, block, construction, network}; +use aptos_rosetta::{ + client::RosettaClient, + types::{NetworkIdentifier, NetworkRequest, PartialBlockIdentifier}, +}; +use aptos_types::chain_id::ChainId; +use clap::Parser; +use serde::Serialize; + +/// Aptos Rosetta CLI +/// +/// Provides an implementation of [Rosetta](https://www.rosetta-api.org/docs/Reference.html) on Aptos. +#[derive(Debug, Parser)] +#[clap(name = "aptos-rosetta-cli", author, version, propagate_version = true)] +pub enum RosettaCliArgs { + #[clap(subcommand)] + Account(account::AccountCommand), + #[clap(subcommand)] + Block(block::BlockCommand), + #[clap(subcommand)] + Construction(construction::ConstructionCommand), + #[clap(subcommand)] + Network(network::NetworkCommand), +} + +impl RosettaCliArgs { + pub async fn execute(self) -> anyhow::Result { + use RosettaCliArgs::*; + match self { + Account(inner) => inner.execute().await, + Block(inner) => inner.execute().await, + Construction(inner) => inner.execute().await, + Network(inner) => inner.execute().await, + } + } +} + +/// Format output to a human readable form +pub fn format_output(input: anyhow::Result) -> anyhow::Result { + input.map(|value| serde_json::to_string_pretty(&value).unwrap()) +} + +#[derive(Debug, Parser)] +pub struct UrlArgs { + /// URL for the Aptos Rosetta API. e.g. http://localhost:8082 + #[clap(long, default_value = "http://localhost:8082")] + rosetta_api_url: url::Url, +} + +impl UrlArgs { + /// Retrieve a [`RosettaClient`] + pub fn client(self) -> RosettaClient { + RosettaClient::new(self.rosetta_api_url) + } +} + +#[derive(Debug, Parser)] +pub struct NetworkArgs { + /// ChainId to be used for the server e.g. TESTNET + #[clap(long, default_value_t = ChainId::test())] + pub chain_id: ChainId, +} + +impl NetworkArgs { + pub fn network_identifier(self) -> NetworkIdentifier { + self.chain_id.into() + } + + pub fn network_request(self) -> NetworkRequest { + NetworkRequest { + network_identifier: self.network_identifier(), + } + } +} + +/// Wrapper so that it's easy to tell that the output is an error +#[derive(Serialize)] +pub struct ErrorWrapper { + pub error: String, +} + +/// Arguments for requesting a block +#[derive(Debug, Parser)] +pub struct BlockArgs { + /// The height of the block to request + #[clap(long)] + block_index: Option, + /// The hash of the block to request + #[clap(long)] + block_hash: Option, + + #[clap(long)] + pub keep_all_transactions: Option, +} + +impl From for Option { + fn from(args: BlockArgs) -> Self { + if args.block_index.is_none() && args.block_hash.is_none() { + None + } else { + Some(PartialBlockIdentifier { + index: args.block_index, + hash: args.block_hash, + }) + } + } +} + +#[test] +fn verify_tool() { + use clap::CommandFactory; + RosettaCliArgs::command().debug_assert() +} diff --git a/crates/aptos-rosetta-cli/src/construction.rs b/crates/aptos-rosetta-cli/src/construction.rs new file mode 100644 index 0000000000000..c0b6485fc157a --- /dev/null +++ b/crates/aptos-rosetta-cli/src/construction.rs @@ -0,0 +1,348 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{format_output, NetworkArgs, UrlArgs}; +use aptos::common::types::{EncodingOptions, PrivateKeyInputOptions, ProfileOptions}; +use aptos_logger::info; +use aptos_rosetta::types::TransactionIdentifier; +use aptos_types::account_address::AccountAddress; +use clap::{Parser, Subcommand}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +/// Construction commands +/// +/// At a high level, this provides the full E2E commands provided by the construction API for +/// Rosetta. This can be used for testing to ensure everything works properly +#[derive(Debug, Subcommand)] +pub enum ConstructionCommand { + CreateAccount(CreateAccountCommand), + SetOperator(SetOperatorCommand), + SetVoter(SetVoterCommand), + Transfer(TransferCommand), + CreateStakePool(CreateStakePoolCommand), +} + +impl ConstructionCommand { + pub async fn execute(self) -> anyhow::Result { + use ConstructionCommand::*; + match self { + CreateAccount(inner) => format_output(inner.execute().await), + SetOperator(inner) => format_output(inner.execute().await), + SetVoter(inner) => format_output(inner.execute().await), + Transfer(inner) => format_output(inner.execute().await), + CreateStakePool(inner) => format_output(inner.execute().await), + } + } +} + +#[derive(Debug, Parser)] +pub struct TransactionArgs { + /// Number of seconds from now to expire + /// + /// If not provided, it will default to 60 seconds + #[clap(long, default_value_t = 60)] + expiry_offset_secs: i64, + /// Sequence number for transaction + /// + /// If not provided, the Rosetta server will pull from onchain + #[clap(long)] + sequence_number: Option, + /// Maximum gas amount for a transaction + /// + /// If not provided, the Rosetta server will estimate it + #[clap(long)] + max_gas: Option, + /// Gas price per unit of gas + /// + /// If not provided, the Rosetta server will estimate it + #[clap(long)] + gas_price: Option, +} + +impl TransactionArgs { + /// Calculate expiry time given the offset seconds + pub fn expiry_time(&self) -> anyhow::Result { + let offset = self.expiry_offset_secs; + if offset > 0 { + Ok( + (SystemTime::now().duration_since(UNIX_EPOCH)? + + Duration::from_secs(offset as u64)) + .as_secs(), + ) + } else { + Ok((SystemTime::now().duration_since(UNIX_EPOCH)? + - Duration::from_secs((-offset) as u64)) + .as_secs()) + } + } +} + +/// Creates an account using Rosetta, no funds will be transferred +/// +/// EncodingOptions are here so we can allow using the BCS encoded mint key +#[derive(Debug, Parser)] +pub struct CreateAccountCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + encoding_options: EncodingOptions, + #[clap(flatten)] + profile_options: ProfileOptions, + #[clap(flatten)] + private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + txn_args: TransactionArgs, + /// The sending account, since the private key doesn't always match the + /// AccountAddress if it rotates + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + sender: Option, + /// The new account (TODO: Maybe we want to take in the public key instead) + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + new_account: AccountAddress, +} + +impl CreateAccountCommand { + pub async fn execute(self) -> anyhow::Result { + info!("Create account: {:?}", self); + let client = self.url_args.client(); + let network_identifier = self.network_args.network_identifier(); + let private_key = self + .private_key_options + .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; + + client + .create_account( + &network_identifier, + &private_key, + self.new_account, + self.txn_args.expiry_time()?, + self.txn_args.sequence_number, + self.txn_args.max_gas, + self.txn_args.gas_price, + ) + .await + } +} + +/// Transfer coins via Rosetta +/// +/// Only the native coin is allowed for now. It will create the account if it +/// does not exist +#[derive(Debug, Parser)] +pub struct TransferCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + encoding_options: EncodingOptions, + #[clap(flatten)] + profile_options: ProfileOptions, + #[clap(flatten)] + private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + txn_args: TransactionArgs, + /// The sending account, since the private key doesn't always match the + /// AccountAddress if it rotates + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + sender: Option, + /// The receiving account + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + receiver: AccountAddress, + /// The amount of coins to send + #[clap(long)] + amount: u64, +} + +impl TransferCommand { + pub async fn execute(self) -> anyhow::Result { + info!("Transfer {:?}", self); + let client = self.url_args.client(); + let network_identifier = self.network_args.network_identifier(); + let private_key = self + .private_key_options + .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; + + client + .transfer( + &network_identifier, + &private_key, + self.receiver, + self.amount, + self.txn_args.expiry_time()?, + self.txn_args.sequence_number, + self.txn_args.max_gas, + self.txn_args.gas_price, + ) + .await + } +} + +/// Set operator +/// +/// +#[derive(Debug, Parser)] +pub struct SetOperatorCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + encoding_options: EncodingOptions, + #[clap(flatten)] + profile_options: ProfileOptions, + #[clap(flatten)] + private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + txn_args: TransactionArgs, + /// The sending account, since the private key doesn't always match the + /// AccountAddress if it rotates + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + sender: Option, + /// The old operator of the stake pool + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + old_operator: Option, + /// The new operator of the stake pool + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + new_operator: AccountAddress, +} + +impl SetOperatorCommand { + pub async fn execute(self) -> anyhow::Result { + info!("Set operator {:?}", self); + let client = self.url_args.client(); + let network_identifier = self.network_args.network_identifier(); + let private_key = self + .private_key_options + .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; + + client + .set_operator( + &network_identifier, + &private_key, + self.old_operator, + self.new_operator, + self.txn_args.expiry_time()?, + self.txn_args.sequence_number, + self.txn_args.max_gas, + self.txn_args.gas_price, + ) + .await + } +} + +/// Set voter +/// +/// +#[derive(Debug, Parser)] +pub struct SetVoterCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + encoding_options: EncodingOptions, + #[clap(flatten)] + profile_options: ProfileOptions, + #[clap(flatten)] + private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + txn_args: TransactionArgs, + /// The sending account, since the private key doesn't always match the + /// AccountAddress if it rotates + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + sender: Option, + /// The operator of the stake pool + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + operator: Option, + /// The new voter for the stake pool + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + new_voter: AccountAddress, +} + +impl SetVoterCommand { + pub async fn execute(self) -> anyhow::Result { + info!("Set voter {:?}", self); + let client = self.url_args.client(); + let network_identifier = self.network_args.network_identifier(); + let private_key = self + .private_key_options + .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; + + client + .set_voter( + &network_identifier, + &private_key, + self.operator, + self.new_voter, + self.txn_args.expiry_time()?, + self.txn_args.sequence_number, + self.txn_args.max_gas, + self.txn_args.gas_price, + ) + .await + } +} + +/// Initialize stake amount +/// +/// +#[derive(Debug, Parser)] +pub struct CreateStakePoolCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, + #[clap(flatten)] + encoding_options: EncodingOptions, + #[clap(flatten)] + profile_options: ProfileOptions, + #[clap(flatten)] + private_key_options: PrivateKeyInputOptions, + #[clap(flatten)] + txn_args: TransactionArgs, + /// The sending account, since the private key doesn't always match the + /// AccountAddress if it rotates + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + sender: Option, + /// Operator + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + operator: Option, + /// Voter + #[clap(long, value_parser = aptos::common::types::load_account_arg)] + voter: Option, + /// Amount + #[clap(long)] + amount: Option, + /// Commission percentage + #[clap(long)] + commission_percentage: Option, +} + +impl CreateStakePoolCommand { + pub async fn execute(self) -> anyhow::Result { + info!("CreateStakePool {:?}", self); + let client = self.url_args.client(); + let network_identifier = self.network_args.network_identifier(); + let private_key = self + .private_key_options + .extract_private_key(self.encoding_options.encoding, &self.profile_options)?; + + client + .create_stake_pool( + &network_identifier, + &private_key, + self.operator, + self.voter, + self.amount, + self.commission_percentage, + self.txn_args.expiry_time()?, + self.txn_args.sequence_number, + self.txn_args.max_gas, + self.txn_args.gas_price, + ) + .await + } +} diff --git a/crates/aptos-rosetta-cli/src/main.rs b/crates/aptos-rosetta-cli/src/main.rs new file mode 100644 index 0000000000000..b62d63dbbf297 --- /dev/null +++ b/crates/aptos-rosetta-cli/src/main.rs @@ -0,0 +1,51 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Aptos Rosetta CLI +//! +//! Why have an Aptos version of the Rosetta CLI? +//! +//! The Rosetta CLI doesn't build on my Mac easily and I just wanted something simple to test out +//! the POST requests +//! +//! Why have a separate CLI? +//! +//! We want users to use the Aptos CLI over the Rosetta CLI because of the added complexity of a +//! proxy server. So, we split it out so general users aren't confused. +//! +//! TODO: Make Aptos CLI framework common among multiple CLIs + +#![forbid(unsafe_code)] + +mod account; +mod block; +mod common; +mod construction; +mod network; + +use crate::common::{ErrorWrapper, RosettaCliArgs}; +use aptos_logger::Level; +use clap::Parser; +use std::process::exit; + +#[tokio::main] +async fn main() { + let mut logger = aptos_logger::Logger::new(); + logger.channel_size(1000).is_async(false).level(Level::Warn); + logger.build(); + + let args: RosettaCliArgs = RosettaCliArgs::parse(); + + let result = args.execute().await; + + match result { + Ok(value) => println!("{}", value), + Err(error) => { + let error = ErrorWrapper { + error: error.to_string(), + }; + println!("{}", serde_json::to_string_pretty(&error).unwrap()); + exit(-1) + }, + } +} diff --git a/crates/aptos-rosetta-cli/src/network.rs b/crates/aptos-rosetta-cli/src/network.rs new file mode 100644 index 0000000000000..4d6710876115a --- /dev/null +++ b/crates/aptos-rosetta-cli/src/network.rs @@ -0,0 +1,79 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::common::{format_output, NetworkArgs, UrlArgs}; +use aptos_rosetta::types::{NetworkListResponse, NetworkOptionsResponse, NetworkStatusResponse}; +use clap::{Parser, Subcommand}; + +/// Network APIs +/// +/// Used to get status of the current network and what is supported on the API +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html) +#[derive(Debug, Subcommand)] +pub enum NetworkCommand { + List(NetworkListCommand), + Options(NetworkOptionsCommand), + Status(NetworkStatusCommand), +} + +impl NetworkCommand { + pub async fn execute(self) -> anyhow::Result { + match self { + NetworkCommand::List(inner) => format_output(inner.execute().await), + NetworkCommand::Options(inner) => format_output(inner.execute().await), + NetworkCommand::Status(inner) => format_output(inner.execute().await), + } + } +} + +/// Get list of available networks +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networklist) +#[derive(Debug, Parser)] +pub struct NetworkListCommand { + #[clap(flatten)] + url_args: UrlArgs, +} + +impl NetworkListCommand { + pub async fn execute(self) -> anyhow::Result { + self.url_args.client().network_list().await + } +} + +/// Get network options +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) +#[derive(Debug, Parser)] +pub struct NetworkOptionsCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, +} + +impl NetworkOptionsCommand { + pub async fn execute(self) -> anyhow::Result { + let request = self.network_args.network_request(); + self.url_args.client().network_options(&request).await + } +} + +/// Get network status +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkstatus) +#[derive(Debug, Parser)] +pub struct NetworkStatusCommand { + #[clap(flatten)] + network_args: NetworkArgs, + #[clap(flatten)] + url_args: UrlArgs, +} + +impl NetworkStatusCommand { + pub async fn execute(self) -> anyhow::Result { + let request = self.network_args.network_request(); + self.url_args.client().network_status(&request).await + } +} diff --git a/crates/aptos-rosetta/README.md b/crates/aptos-rosetta/README.md new file mode 100644 index 0000000000000..2ad917da87827 --- /dev/null +++ b/crates/aptos-rosetta/README.md @@ -0,0 +1,106 @@ +# Aptos Rosetta Implementation + +This implementation is built for running a local proxy against +a local fullnode. However, for testing purposes, this can be used +against an external REST endpoint. + +## Architecture + +[Rosetta](https://en.wikipedia.org/wiki/Rosetta_(software)) works as a sidecar to an Aptos fullnode. Rosetta then proxies the Rosetta standard +API calls to underlying Aptos REST API calls and builds the appropriate data. + + +## Running Rosetta + +The `aptos-rosetta` binary can run in three modes: +1. `online` -> This runs a local fullnode and blocks the Aptos REST API from outside access, using it only as a local proxy for Rosetta APIs. +2. `offline` -> This runs a Rosetta server that is not connected to the blockchain. Only commands listed as `offline` work with this mode. +3. `online-remote` -> This runs a Rosetta instance that connects to a remote fullnode e.g. a public fullnode. Please keep in mind that since this proxies APIs, it can fail due to throttling and network errors between the servers. + + +## Features supported + +### Balances +* Only the native `APT` is supported. +* Staking balances are also supported, with the sub-account with the name of `stake`, and only with `0x1::staking_contract` stake pools. +* Balances are loaded from the live API `get_account_resources`; and if the `block` has been pruned, it will error out. +* All balances are provided the balance at the end of a `block`. + + +### Blocks + +Blocks support reading the following operations: + + * `create_account` -> When an account is created. + * `withdraw` -> When a balance is withdrawn from an account. + * `deposit` -> When a balance is deposited to an account. + * `fee` -> The gas fee associated with running a transaction. + * `set_operator` -> Switching a `0x1::staking_contract` operator to a new operator. + * `set_voter` -> Switching a `0x1::staking_contract` voter to a new voter. + +Here are some exceptions: + + * Not all operators can be parsed from `failed transactions`. + * Set operator will have the stake balance in its metadata. + +All transactions are parsed from the events provided by the AptosFramework. There are a few exceptions to this that use the transaction payload, but only for errors. + +Block hash is `:` and not actually a hash. + +### Constructing transactions + +More specifics can be found here: https://www.rosetta-api.org/docs/flow.html#construction-api + +All inputs to the API must be done in the `ConstructionPreprocessRequest`. This allows you to set +the sequence number, expiry time, gas parameters, and the public keys to sign the transaction. + +Note: Currently only single signer is supported at this time. + +The general flow is that you provide these inputs and follow the flow of APIs. The Metadata call +will do a simulation of the transaction and tell you the estimated gas fee for that transaction. It +will also fail the transaction before paying gas if the transaction cannot work. Once all the payloads +are built and combined, you must sign the transaction with the Ed25519 key that matches the PublicKey +provided in the `ConstructinoPreProcessRequest`. + +#### Create Account +* Accounts can be created with just the `create_account` operation alone. + +#### Transfers +* Transfers occur as a combination of a `withdraw` and a `deposit`. This has the side effect of creating the receiver if it doesn't exist. +* Transfers support only APT at this moment. + +#### Set Operator +* A staking contract stake pool can change its operator. +* If no operator is provided, it will attempt to find the first operator in the stake pool. + +#### Set Voter +* A staking contract stake pool can chage its voter. +* If no operator is provided, it will attempt to find the first operator in the stake pool. + +## Data types +All data types must hide `null` values from the output JSON. Additionally, u64s must be +encoded as strings in any metadata fields. + +## Errors + +All errors are 500s and have error codes that are static and must not change. To add more errors, +add new codes and associated data. The error details must not show in the network options call and +are all provided as Option for that reason. + +## Time + +All timestamps are valid except for the first two timestamps. These are generally 0, so they are set to +January 1st 2000 if they're older than that. + +## Mempool APIs + +Mempool APIs are currently not supported. + +## CLI testing + +The [Rosetta CLI](https://www.rosetta-api.org/docs/rosetta_cli.html) can be run with the [rosetta_cli.json](./rosetta_cli.json) +file to run the automated checks. Additionally, the [aptos.ros](./aptos.ros) +file uses the Rosetta CLI DSL to describe the possible operations that +can be run. + +Additionally, we have our `aptos-rosetta-cli` crate for local testing. diff --git a/crates/aptos-rosetta/aptos.ros b/crates/aptos-rosetta/aptos.ros new file mode 100644 index 0000000000000..e1d45ad24f443 --- /dev/null +++ b/crates/aptos-rosetta/aptos.ros @@ -0,0 +1,146 @@ +// A configuration script for the Rosetta CLI provided by the Rosetta Spec +// Create account workflow (only 1 at a time) +create_account(1){ + create_account{ + // Testing network + create_account.network = {"network": "TESTING", "blockchain": "aptos"}; + + // Generate key and derive the address, saving it locally + key = generate_key({"curve_type": "edwards25519"}); + account = derive({ + "network_identifier": {{create_account.network}}, + "public_key": {{key.public_key}} + }); + save_account({ + "account_identifier": {{account.account_identifier}}, + "keypair": {{key}} + }); + + // Find the "faucet" loaded account + currency = { + "symbol": "APT", + "decimals": 8, + "metadata": { + "move_type": "0x1::aptos_coin::AptosCoin" + } + }; + print_message({"Find faucet with at least balance to create accounts": "10000000"}); + loaded_account = find_balance({ + "minimum_balance": { + "value": "10000000", + "currency": {{currency}} + } + }); + + // Make a create account call + print_message({"Create account":{{account.account_identifier.address}}, "With account":{{loaded_account.account_identifier.address}}}); + create_account.operations = [ + { + "operation_identifier": {"index": 0}, + "type": "create_account", + "account": {{account.account_identifier}}, + "metadata":{ + "sender": {{loaded_account.account_identifier}} + } + } + ]; + create_account.confirmation_depth = 2; + } +} + +// Request funds from the "faucet" (only one at a time) +request_funds(1){ + find_account{ + currency = { + "symbol": "APT", + "decimals": 8, + "metadata": { + "move_type": "0x1::aptos_coin::AptosCoin" + } + }; + print_message({"Find faucet with at least balance": "0"}); + random_account = find_balance({ + "minimum_balance": { + "value": "0", + "currency": {{currency}} + }, + "create_limit":1 + }); + }, + request{ + print_message({"Requesting funds from":{{random_account.account_identifier.address}}}); + loaded_account = find_balance({ + "account_identifier": {{random_account.account_identifier}}, + "minimum_balance": { + "value": "10000000", + "currency": {{currency}} + } + }); + } +} + +// Transfer money between accounts +transfer(50){ + transfer{ + transfer.network = {"network":"TESTING", "blockchain":"aptos"}; + currency = { + "symbol": "APT", + "decimals": 8, + "metadata": { + "move_type": "0x1::aptos_coin::AptosCoin" + } + }; + + // Find someone who can send money + print_message({"Find someone to send money with balance minimum": "10000000"}); + sender = find_balance({ + "minimum_balance":{ + "value": "10000000", + "currency": {{currency}} + } + }); + + // Set the receiver_amount as some value <= sender.balance - max_fee + max_fee = "2200"; + available_amount = {{sender.balance.value}} - {{max_fee}}; + receiver_amount = random_number({"minimum": "1", "maximum": {{available_amount}}}); + sender_amount = "0" - {{receiver_amount}}; + + + // Find receiver and construct operations + print_message({"Find someone to receive money with balance minimum": "0"}); + receiver = find_balance({ + "not_account_identifier": [{{sender.account_identifier}}], + "minimum_balance": { + "value": "0", + "currency": {{currency}} + }, + "create_limit": 100, + "create_probability": 50 + }); + + print_message({"Transfer funds": {"sender":{{sender.account_identifier.address}}, "receiver":{{receiver.account_identifier.address}}, "amount":{{receiver_amount}}}}); + + transfer.operations = [ + { + "operation_identifier": {"index": 0}, + "type":"withdraw", + "account": {{sender.account_identifier}}, + "amount": { + "value": {{sender_amount}}, + "currency": {{currency}} + } + }, + { + "operation_identifier": {"index": 1}, + "type": "deposit", + "account": {{receiver.account_identifier}}, + "amount": { + "value": {{receiver_amount}}, + "currency": {{currency}} + } + } + ]; + transfer.confirmation_depth = 2; + } +} diff --git a/crates/aptos-rosetta/rosetta_cli.json b/crates/aptos-rosetta/rosetta_cli.json new file mode 100644 index 0000000000000..862fe002321cf --- /dev/null +++ b/crates/aptos-rosetta/rosetta_cli.json @@ -0,0 +1,70 @@ +{ + "network": { + "blockchain": "aptos", + "network": "TESTING" + }, + "online_url": "http://localhost:8082", + "data_directory": "data", + "http_timeout": 30, + "max_retries": 5, + "retry_elapsed_time": 1, + "max_online_connections": 500, + "max_sync_concurrency": 10, + "tip_delay": 30, + "log_configuration": false, + "compression_disabled": false, + "memory_limit_disabled": false, + "error_stack_trace_disabled": false, + "coin_supported": false, + "construction": { + "offline_url": "http://localhost:8083", + "max_offline_connections": 500, + "stale_depth": 100, + "broadcast_limit": 5, + "ignore_broadcast_failures": false, + "clear_broadcasts": false, + "broadcast_behind_tip": false, + "block_broadcast_limit": 50, + "rebroadcast_all": false, + "constructor_dsl_file": "aptos.ros", + "status_port": 9090, + "force_retry": false, + "end_conditions": { + "create_account": 10, + "transfer": 20 + }, + "prefunded_accounts": [ + ] + }, + "data": { + "active_reconciliation_concurrency": 16, + "inactive_reconciliation_concurrency": 4, + "inactive_reconciliation_frequency": 250, + "log_blocks": false, + "log_transactions": false, + "log_balance_changes": false, + "log_reconciliations": false, + "ignore_reconciliation_error": false, + "historical_balance_disabled": false, + "exempt_accounts": "", + "bootstrap_balances": "", + "interesting_accounts": "", + "reconciliation_disabled": false, + "reconciliation_drain_disabled": false, + "inactive_discrepancy_search_disabled": false, + "balance_tracking_disabled": false, + "coin_tracking_disabled": false, + "status_port": 9090, + "results_output_file": "", + "pruning_disabled": false, + "initial_balance_fetch_disabled": false, + "end_conditions": { + "tip": true, + "reconciliation_coverage": { + "coverage": 0.95, + "tip": true + } + } + }, + "perf": null +} diff --git a/crates/aptos-rosetta/src/block.rs b/crates/aptos-rosetta/src/block.rs new file mode 100644 index 0000000000000..6ee61a6f218a4 --- /dev/null +++ b/crates/aptos-rosetta/src/block.rs @@ -0,0 +1,213 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{ + check_network, get_block_index_from_request, get_timestamp, handle_request, with_context, + BlockHash, Y2K_MS, + }, + error::ApiResult, + types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction}, + RosettaContext, +}; +use aptos_logger::{debug, trace}; +use aptos_types::chain_id::ChainId; +use std::sync::Arc; +use warp::Filter; + +pub fn block_route( + server_context: RosettaContext, +) -> impl Filter + Clone { + warp::path!("block") + .and(warp::post()) + .and(warp::body::json()) + .and(with_context(server_context)) + .and_then(handle_request(block)) +} + +/// Retrieves a block (in this case a single transaction) given it's identifier. +/// +/// Our implementation allows for by `index`, which is the ledger `version` or by +/// transaction `hash`. +/// +/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block) +async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult { + debug!("/block"); + trace!( + request = ?request, + server_context = ?server_context, + "/block", + ); + + check_network(request.network_identifier, &server_context)?; + + // Retrieve by block or by hash, both or neither is not allowed + let block_index = + get_block_index_from_request(&server_context, request.block_identifier).await?; + + let (parent_transaction, block) = get_block_by_index( + server_context.block_cache()?.as_ref(), + block_index, + server_context.chain_id, + ) + .await?; + + let keep_empty_transactions = request + .metadata + .as_ref() + .and_then(|inner| inner.keep_empty_transactions) + .unwrap_or_default(); + let block = build_block( + &server_context, + parent_transaction, + block, + server_context.chain_id, + keep_empty_transactions, + ) + .await?; + + Ok(BlockResponse { block }) +} + +/// Build up the transaction, which should contain the `operations` as the change set +async fn build_block( + server_context: &RosettaContext, + parent_block_identifier: BlockIdentifier, + block: aptos_rest_client::aptos_api_types::BcsBlock, + chain_id: ChainId, + keep_empty_transactions: bool, +) -> ApiResult { + // note: timestamps are in microseconds, so we convert to milliseconds + let timestamp = get_timestamp(block.block_timestamp); + let block_identifier = BlockIdentifier::from_block(&block, chain_id); + + // Convert the transactions and build the block + let mut transactions: Vec = Vec::new(); + // TODO: Parallelize these and then sort at end + if let Some(txns) = block.transactions { + for txn in txns { + let transaction = Transaction::from_transaction(server_context, txn).await?; + if keep_empty_transactions || !transaction.operations.is_empty() { + transactions.push(transaction) + } + } + } + + // Ensure the transactions are sorted in order + transactions.sort_by(|first, second| first.metadata.version.0.cmp(&second.metadata.version.0)); + + Ok(Block { + block_identifier, + parent_block_identifier, + timestamp, + transactions, + }) +} + +/// Retrieves a block by its index +async fn get_block_by_index( + block_cache: &BlockRetriever, + block_height: u64, + chain_id: ChainId, +) -> ApiResult<( + BlockIdentifier, + aptos_rest_client::aptos_api_types::BcsBlock, +)> { + let block = block_cache.get_block_by_height(block_height, true).await?; + + // For the genesis block, we populate parent_block_identifier with the + // same genesis block. Refer to + // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block + if block_height == 0 { + Ok((BlockIdentifier::from_block(&block, chain_id), block)) + } else { + // Retrieve the previous block's identifier + let prev_block = block_cache + .get_block_by_height(block_height - 1, false) + .await?; + let prev_block_id = BlockIdentifier::from_block(&prev_block, chain_id); + + // Retrieve the current block + Ok((prev_block_id, block)) + } +} + +#[derive(Clone, Debug)] +pub struct BlockInfo { + /// Block identifier (block hash & block height) + pub block_id: BlockIdentifier, + /// Milliseconds timestamp + pub timestamp: u64, + /// Last version in block for getting state + pub last_version: u64, +} + +impl BlockInfo { + pub fn from_block( + block: &aptos_rest_client::aptos_api_types::BcsBlock, + chain_id: ChainId, + ) -> BlockInfo { + BlockInfo { + block_id: BlockIdentifier::from_block(block, chain_id), + timestamp: get_timestamp(block.block_timestamp), + last_version: block.last_version, + } + } +} + +/// A cache of [`BlockInfo`] to allow us to keep track of the block boundaries +#[derive(Debug)] +pub struct BlockRetriever { + page_size: u16, + rest_client: Arc, +} + +impl BlockRetriever { + pub fn new(page_size: u16, rest_client: Arc) -> Self { + BlockRetriever { + page_size, + rest_client, + } + } + + pub async fn get_block_info_by_height( + &self, + height: u64, + chain_id: ChainId, + ) -> ApiResult { + // Genesis block is hardcoded + if height == 0 { + return Ok(BlockInfo { + block_id: BlockIdentifier { + index: 0, + hash: BlockHash::new(chain_id, 0).to_string(), + }, + timestamp: Y2K_MS, + last_version: 0, + }); + } + + let block = self.get_block_by_height(height, false).await?; + Ok(BlockInfo::from_block(&block, chain_id)) + } + + pub async fn get_block_by_height( + &self, + height: u64, + with_transactions: bool, + ) -> ApiResult { + if with_transactions { + Ok(self + .rest_client + .get_full_block_by_height_bcs(height, self.page_size) + .await? + .into_inner()) + } else { + Ok(self + .rest_client + .get_block_by_height_bcs(height, false) + .await? + .into_inner()) + } + } +} diff --git a/crates/aptos-rosetta/src/client.rs b/crates/aptos-rosetta/src/client.rs new file mode 100644 index 0000000000000..ad7975f616047 --- /dev/null +++ b/crates/aptos-rosetta/src/client.rs @@ -0,0 +1,909 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::native_coin, + types::{ + AccountBalanceRequest, AccountBalanceResponse, AccountIdentifier, BlockRequest, + BlockResponse, ConstructionCombineRequest, ConstructionCombineResponse, + ConstructionDeriveRequest, ConstructionDeriveResponse, ConstructionHashRequest, + ConstructionMetadata, ConstructionMetadataRequest, ConstructionMetadataResponse, + ConstructionParseRequest, ConstructionParseResponse, ConstructionPayloadsRequest, + ConstructionPayloadsResponse, ConstructionPreprocessRequest, + ConstructionPreprocessResponse, ConstructionSubmitRequest, ConstructionSubmitResponse, + Error, MetadataRequest, NetworkIdentifier, NetworkListResponse, NetworkOptionsResponse, + NetworkRequest, NetworkStatusResponse, Operation, PreprocessMetadata, PublicKey, Signature, + SignatureType, TransactionIdentifier, TransactionIdentifierResponse, + }, +}; +use anyhow::anyhow; +use aptos_crypto::{ + ed25519::Ed25519PrivateKey, PrivateKey, SigningKey, ValidCryptoMaterialStringExt, +}; +use aptos_rest_client::aptos_api_types::mime_types::JSON; +use aptos_types::{account_address::AccountAddress, transaction::RawTransaction}; +use reqwest::{header::CONTENT_TYPE, Client as ReqwestClient}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{collections::HashMap, convert::TryInto, fmt::Debug, str::FromStr}; +use url::Url; + +/// Client for testing & interacting with a Rosetta service +#[derive(Debug, Clone)] +pub struct RosettaClient { + address: Url, + inner: ReqwestClient, +} + +impl RosettaClient { + pub fn new(address: Url) -> RosettaClient { + RosettaClient { + address, + inner: ReqwestClient::new(), + } + } + + pub async fn account_balance( + &self, + request: &AccountBalanceRequest, + ) -> anyhow::Result { + self.make_call("account/balance", request).await + } + + pub async fn block(&self, request: &BlockRequest) -> anyhow::Result { + self.make_call("block", request).await + } + + pub async fn combine( + &self, + request: &ConstructionCombineRequest, + ) -> anyhow::Result { + self.make_call("construction/combine", request).await + } + + pub async fn derive( + &self, + request: &ConstructionDeriveRequest, + ) -> anyhow::Result { + self.make_call("construction/derive", request).await + } + + pub async fn hash( + &self, + request: &ConstructionHashRequest, + ) -> anyhow::Result { + self.make_call("construction/hash", request).await + } + + pub async fn metadata( + &self, + request: &ConstructionMetadataRequest, + ) -> anyhow::Result { + self.make_call("construction/metadata", request).await + } + + pub async fn parse( + &self, + request: &ConstructionParseRequest, + ) -> anyhow::Result { + self.make_call("construction/parse", request).await + } + + pub async fn payloads( + &self, + request: &ConstructionPayloadsRequest, + ) -> anyhow::Result { + self.make_call("construction/payloads", request).await + } + + pub async fn preprocess( + &self, + request: &ConstructionPreprocessRequest, + ) -> anyhow::Result { + self.make_call("construction/preprocess", request).await + } + + pub async fn submit( + &self, + request: &ConstructionSubmitRequest, + ) -> anyhow::Result { + self.make_call("construction/submit", request).await + } + + pub async fn network_list(&self) -> anyhow::Result { + self.make_call("network/list", &MetadataRequest {}).await + } + + pub async fn network_options( + &self, + request: &NetworkRequest, + ) -> anyhow::Result { + self.make_call("network/options", request).await + } + + pub async fn network_status( + &self, + request: &NetworkRequest, + ) -> anyhow::Result { + self.make_call("network/status", request).await + } + + async fn make_call<'a, I: Serialize + Debug, O: DeserializeOwned>( + &'a self, + path: &'static str, + request: &'a I, + ) -> anyhow::Result { + let response = self + .inner + .post(self.address.join(path)?) + .header(CONTENT_TYPE, JSON) + .body(serde_json::to_string(request)?) + .send() + .await?; + if !response.status().is_success() { + let error: Error = response.json().await?; + return Err(anyhow!("Failed API with: {:?}", error)); + } + + Ok(response.json().await?) + } + + pub async fn create_account( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + new_account: AccountAddress, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A create account transaction is just a Create account operation + let operations = vec![Operation::create_account(0, None, new_account, sender)]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + false, + ) + .await + } + + pub async fn transfer( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + receiver: AccountAddress, + amount: u64, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A transfer operation is made up of a withdraw and a deposit + let operations = vec![ + Operation::withdraw( + 0, + None, + AccountIdentifier::base_account(sender), + native_coin(), + amount, + ), + Operation::deposit( + 1, + None, + AccountIdentifier::base_account(receiver), + native_coin(), + amount, + ), + ]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + false, + ) + .await + } + + pub async fn set_operator( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + old_operator: Option, + new_operator: AccountAddress, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A transfer operation is made up of a withdraw and a deposit + let operations = vec![Operation::set_operator( + 0, + None, + sender, + old_operator.map(AccountIdentifier::base_account), + AccountIdentifier::base_account(new_operator), + None, + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + old_operator.is_none(), + ) + .await + } + + pub async fn set_voter( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + operator: Option, + new_voter: AccountAddress, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A transfer operation is made up of a withdraw and a deposit + let operations = vec![Operation::set_voter( + 0, + None, + sender, + operator.map(AccountIdentifier::base_account), + AccountIdentifier::base_account(new_voter), + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + operator.is_none(), + ) + .await + } + + pub async fn reset_lockup( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + operator: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A transfer operation is made up of a withdraw and a deposit + let operations = vec![Operation::reset_lockup( + 0, + None, + sender, + operator.map(AccountIdentifier::base_account), + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + operator.is_none(), + ) + .await + } + + pub async fn update_commission( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + operator: Option, + new_commission_percentage: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + let operations = vec![Operation::update_commission( + 0, + None, + sender, + operator.map(AccountIdentifier::base_account), + new_commission_percentage, + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + operator.is_none(), + ) + .await + } + + pub async fn unlock_stake( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + operator: Option, + amount: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + let operations = vec![Operation::unlock_stake( + 0, + None, + sender, + operator.map(AccountIdentifier::base_account), + amount, + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + operator.is_none(), + ) + .await + } + + pub async fn distribute_staking_rewards( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + operator: AccountAddress, + staker: AccountAddress, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + let operations = vec![Operation::distribute_staking_rewards( + 0, + None, + sender, + AccountIdentifier::base_account(operator), + AccountIdentifier::base_account(staker), + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + false, + ) + .await + } + + pub async fn create_stake_pool( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + new_operator: Option, + new_voter: Option, + stake_amount: Option, + commission_percentage: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + // A transfer operation is made up of a withdraw and a deposit + + let operations = vec![Operation::create_stake_pool( + 0, + None, + sender, + new_operator, + new_voter, + stake_amount, + commission_percentage, + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + true, + ) + .await + } + + pub async fn add_delegated_stake( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + pool_address: AccountAddress, + amount: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let delegator = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + + let mut keys = HashMap::new(); + keys.insert(delegator, private_key); + + let operations = vec![Operation::add_delegated_stake( + 0, + None, + delegator, + AccountIdentifier::base_account(pool_address), + amount, + )]; + + self.submit_operations( + delegator, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + true, + ) + .await + } + + pub async fn unlock_delegated_stake( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + pool_address: AccountAddress, + amount: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let delegator = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(delegator, private_key); + + let operations = vec![Operation::unlock_delegated_stake( + 0, + None, + delegator, + AccountIdentifier::base_account(pool_address), + amount, + )]; + + self.submit_operations( + delegator, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + true, + ) + .await + } + + pub async fn withdraw_undelegated_stake( + &self, + network_identifier: &NetworkIdentifier, + private_key: &Ed25519PrivateKey, + pool_address: AccountAddress, + amount: Option, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + ) -> anyhow::Result { + let sender = self + .get_account_address(network_identifier.clone(), private_key) + .await?; + let mut keys = HashMap::new(); + keys.insert(sender, private_key); + + let operations = vec![Operation::withdraw_undelegated_stake( + 0, + None, + sender, + AccountIdentifier::base_account(pool_address), + amount, + )]; + + self.submit_operations( + sender, + network_identifier.clone(), + &keys, + operations, + expiry_time_secs, + sequence_number, + max_gas, + gas_unit_price, + false, + ) + .await + } + + /// Retrieves the account address from the derivation path if there isn't an overriding account specified + async fn get_account_address( + &self, + network_identifier: NetworkIdentifier, + private_key: &Ed25519PrivateKey, + ) -> anyhow::Result { + Ok(self + .derive_account(network_identifier, private_key.public_key().try_into()?) + .await? + .account_address()?) + } + + /// Submits the operations to the blockchain + async fn submit_operations( + &self, + sender: AccountAddress, + network_identifier: NetworkIdentifier, + keys: &HashMap, + operations: Vec, + expiry_time_secs: u64, + sequence_number: Option, + max_gas: Option, + gas_unit_price: Option, + // Parsed operations won't match given operations + parse_not_same: bool, + ) -> anyhow::Result { + // Retrieve txn metadata + let (metadata, public_keys) = self + .metadata_for_ops( + sender, + network_identifier.clone(), + operations.clone(), + max_gas, + gas_unit_price, + expiry_time_secs, + sequence_number, + keys, + ) + .await?; + + // Should have a fee in the native coin + let suggested_fee = metadata.suggested_fee.first().expect("Expected fee"); + let expected_fee = u64::from_str(&suggested_fee.value).expect("Expected u64 for fee"); + assert_eq!( + suggested_fee.currency, + native_coin(), + "Fee should always be the native coin" + ); + assert!( + metadata.metadata.max_gas_amount.0 * metadata.metadata.gas_price_per_unit.0 + >= expected_fee + ); + + // Build the transaction, sign it, and submit it + let response = self + .unsigned_transaction( + network_identifier.clone(), + operations.clone(), + metadata.metadata, + public_keys, + parse_not_same, + ) + .await?; + let signed_txn = self + .sign_transaction( + network_identifier.clone(), + keys, + response, + operations, + parse_not_same, + ) + .await?; + self.submit_transaction(network_identifier, signed_txn) + .await + } + + /// Derives an [`AccountAddress`] from the [`PublicKey`] + async fn derive_account( + &self, + network_identifier: NetworkIdentifier, + public_key: PublicKey, + ) -> anyhow::Result { + Ok(self + .derive(&ConstructionDeriveRequest { + network_identifier, + public_key, + }) + .await? + .account_identifier) + } + + /// Retrieves the metadata for the set of operations + async fn metadata_for_ops( + &self, + sender: AccountAddress, + network_identifier: NetworkIdentifier, + operations: Vec, + max_gas: Option, + gas_unit_price: Option, + expiry_time_secs: u64, + sequence_number: Option, + keys: &HashMap, + ) -> anyhow::Result<(ConstructionMetadataResponse, Vec)> { + // Request the given operation with the given gas constraints + let preprocess_response = self + .preprocess(&ConstructionPreprocessRequest { + network_identifier: network_identifier.clone(), + operations, + metadata: Some(PreprocessMetadata { + expiry_time_secs: Some(expiry_time_secs.into()), + sequence_number: sequence_number.map(|inner| inner.into()), + max_gas_amount: max_gas.map(|inner| inner.into()), + gas_price: gas_unit_price.map(|inner| inner.into()), + public_keys: Some(vec![keys + .get(&sender) + .unwrap() + .public_key() + .try_into() + .unwrap()]), + gas_price_multiplier: None, + gas_price_priority: None, + }), + }) + .await?; + + // Process the required public keys + let mut public_keys = Vec::new(); + for account in preprocess_response.required_public_keys { + if let Some(key) = keys.get(&account.account_address()?) { + public_keys.push(key.public_key().try_into()?); + } else { + return Err(anyhow!("No public key found for account")); + } + } + + // Request the metadata + self.metadata(&ConstructionMetadataRequest { + network_identifier, + options: preprocess_response.options, + }) + .await + .map(|response| (response, public_keys)) + } + + /// Build an unsigned transaction + async fn unsigned_transaction( + &self, + network_identifier: NetworkIdentifier, + operations: Vec, + metadata: ConstructionMetadata, + public_keys: Vec, + parse_not_same: bool, + ) -> anyhow::Result { + // Build the unsigned transaction + let payloads = self + .payloads(&ConstructionPayloadsRequest { + network_identifier: network_identifier.clone(), + operations: operations.clone(), + metadata: Some(metadata), + public_keys: Some(public_keys), + }) + .await?; + + // Verify that we can parse the transaction + let response = self + .parse(&ConstructionParseRequest { + network_identifier, + signed: false, + transaction: payloads.unsigned_transaction.clone(), + }) + .await?; + + if response.account_identifier_signers.is_some() { + Err(anyhow!("Signers were in the unsigned transaction!")) + } else if !parse_not_same && operations != response.operations { + Err(anyhow!( + "Operations were not parsed to be the same as input! Expected {:?} Got {:?}", + operations, + response.operations + )) + } else { + Ok(payloads) + } + } + + /// Signs a transaction and combines it with an unsigned transaction + async fn sign_transaction( + &self, + network_identifier: NetworkIdentifier, + keys: &HashMap, + unsigned_response: ConstructionPayloadsResponse, + operations: Vec, + parse_not_same: bool, + ) -> anyhow::Result { + let mut signatures = Vec::new(); + let mut signers: Vec = Vec::new(); + + // Sign the unsigned transaction + let unsigned_transaction: RawTransaction = bcs::from_bytes(&hex::decode( + unsigned_response.unsigned_transaction.clone(), + )?)?; + let signing_message = hex::encode(unsigned_transaction.signing_message().unwrap()); + + // Sign the payload if it matches the unsigned transaction + for payload in unsigned_response.payloads.into_iter() { + let account = &payload.account_identifier; + let private_key = keys + .get(&account.account_address()?) + .expect("Should have a private key"); + signers.push(account.clone()); + + assert_eq!(signing_message, payload.hex_bytes); + let txn_signature = private_key.sign(&unsigned_transaction).unwrap(); + signatures.push(Signature { + signing_payload: payload, + public_key: private_key.public_key().try_into()?, + signature_type: SignatureType::Ed25519, + hex_bytes: txn_signature.to_encoded_string()?, + }); + } + + // Build the signed transaction + let signed_response = self + .combine(&ConstructionCombineRequest { + network_identifier: network_identifier.clone(), + unsigned_transaction: unsigned_response.unsigned_transaction, + signatures, + }) + .await?; + + // Verify transaction can be parsed properly + let response = self + .parse(&ConstructionParseRequest { + network_identifier, + signed: true, + transaction: signed_response.signed_transaction.clone(), + }) + .await?; + + // Signers must match exactly + if let Some(parsed_signers) = response.account_identifier_signers { + if signers != parsed_signers { + return Err(anyhow!( + "Signers don't match Expected: {:?} Got: {:?}", + signers, + parsed_signers + )); + } + } else { + return Err(anyhow!("Signers were in the unsigned transaction!")); + } + + // Operations must match exactly + if !parse_not_same && operations != response.operations { + Err(anyhow!( + "Operations were not parsed to be the same as input! Expected {:?} Got {:?}", + operations, + response.operations + )) + } else { + Ok(signed_response.signed_transaction) + } + } + + /// Submit a transaction to the blockchain + async fn submit_transaction( + &self, + network_identifier: NetworkIdentifier, + signed_transaction: String, + ) -> anyhow::Result { + Ok(self + .submit(&ConstructionSubmitRequest { + network_identifier, + signed_transaction, + }) + .await? + .transaction_identifier) + } +} diff --git a/crates/aptos-rosetta/src/error.rs b/crates/aptos-rosetta/src/error.rs new file mode 100644 index 0000000000000..3f0ca50462bee --- /dev/null +++ b/crates/aptos-rosetta/src/error.rs @@ -0,0 +1,353 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{types, types::ErrorDetails}; +use aptos_rest_client::{aptos_api_types::AptosErrorCode, error::RestError}; +use hex::FromHexError; +use move_core_types::account_address::AccountAddressParseError; +use serde::{Deserialize, Serialize}; +use std::fmt::Formatter; +use warp::{http::StatusCode, reply::Reply}; + +pub type ApiResult = Result; + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub enum ApiError { + TransactionIsPending, + NetworkIdentifierMismatch, + ChainIdMismatch, + DeserializationFailed(Option), + InvalidTransferOperations(Option<&'static str>), + InvalidSignatureType, + InvalidMaxGasFees, + MaxGasFeeTooLow(Option), + InvalidGasMultiplier, + GasEstimationFailed(Option), + InvalidOperations(Option), + MissingPayloadMetadata, + UnsupportedCurrency(Option), + UnsupportedSignatureCount(Option), + NodeIsOffline, + TransactionParseError(Option), + InternalError(Option), + CoinTypeFailedToBeFetched(Option), + + // Below here are codes directly from the REST API + AccountNotFound(Option), + ResourceNotFound(Option), + ModuleNotFound(Option), + StructFieldNotFound(Option), + VersionNotFound(Option), + TransactionNotFound(Option), + TableItemNotFound(Option), + BlockNotFound(Option), + StateValueNotFound(Option), + VersionPruned(Option), + BlockPruned(Option), + InvalidInput(Option), + InvalidTransactionUpdate(Option), + SequenceNumberTooOld(Option), + VmError(Option), + MempoolIsFull(Option), +} + +impl std::fmt::Display for ApiError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl std::error::Error for ApiError {} + +impl ApiError { + pub fn all() -> Vec { + use ApiError::*; + vec![ + TransactionIsPending, + NetworkIdentifierMismatch, + ChainIdMismatch, + DeserializationFailed(None), + InvalidTransferOperations(None), + InvalidSignatureType, + InvalidMaxGasFees, + MaxGasFeeTooLow(None), + InvalidGasMultiplier, + GasEstimationFailed(None), + InvalidOperations(None), + MissingPayloadMetadata, + UnsupportedCurrency(None), + UnsupportedSignatureCount(None), + NodeIsOffline, + TransactionParseError(None), + InternalError(None), + CoinTypeFailedToBeFetched(None), + AccountNotFound(None), + ResourceNotFound(None), + ModuleNotFound(None), + StructFieldNotFound(None), + VersionNotFound(None), + TransactionNotFound(None), + TableItemNotFound(None), + BlockNotFound(None), + StateValueNotFound(None), + VersionPruned(None), + BlockPruned(None), + InvalidInput(None), + InvalidTransactionUpdate(None), + SequenceNumberTooOld(None), + VmError(None), + MempoolIsFull(None), + ] + } + + pub fn code(&self) -> u32 { + use ApiError::*; + match self { + TransactionIsPending => 1, + NetworkIdentifierMismatch => 2, + ChainIdMismatch => 3, + DeserializationFailed(_) => 4, + InvalidTransferOperations(_) => 5, + InvalidSignatureType => 6, + InvalidMaxGasFees => 7, + MaxGasFeeTooLow(_) => 8, + InvalidGasMultiplier => 9, + InvalidOperations(_) => 10, + MissingPayloadMetadata => 11, + UnsupportedCurrency(_) => 12, + UnsupportedSignatureCount(_) => 13, + NodeIsOffline => 14, + TransactionParseError(_) => 15, + GasEstimationFailed(_) => 16, + InternalError(_) => 17, + AccountNotFound(_) => 18, + ResourceNotFound(_) => 19, + ModuleNotFound(_) => 20, + StructFieldNotFound(_) => 21, + VersionNotFound(_) => 22, + TransactionNotFound(_) => 23, + TableItemNotFound(_) => 24, + BlockNotFound(_) => 25, + VersionPruned(_) => 26, + BlockPruned(_) => 27, + InvalidInput(_) => 28, + InvalidTransactionUpdate(_) => 29, + SequenceNumberTooOld(_) => 30, + VmError(_) => 31, + MempoolIsFull(_) => 32, + CoinTypeFailedToBeFetched(_) => 33, + StateValueNotFound(_) => 34, + } + } + + pub fn retriable(&self) -> bool { + use ApiError::*; + matches!( + self, + AccountNotFound(_) + | BlockNotFound(_) + | MempoolIsFull(_) + | GasEstimationFailed(_) + | CoinTypeFailedToBeFetched(_) + ) + } + + pub fn status_code(&self) -> StatusCode { + // Per Rosetta guidelines, all errors are 500s + StatusCode::INTERNAL_SERVER_ERROR + } + + /// This value must be fixed, so it's all static strings + pub fn message(&self) -> &'static str { + match self { + ApiError::TransactionIsPending => "Transaction is pending", + ApiError::NetworkIdentifierMismatch => "Network identifier doesn't match", + ApiError::ChainIdMismatch => "Chain Id doesn't match", + ApiError::DeserializationFailed(_) => "Deserialization failed", + ApiError::InvalidTransferOperations(_) => "Invalid operations for a transfer", + ApiError::AccountNotFound(_) => "Account not found", + ApiError::InvalidSignatureType => "Invalid signature type", + ApiError::InvalidMaxGasFees => "Invalid max gas fee", + ApiError::MaxGasFeeTooLow(_) => "Max fee is lower than the estimated cost of the transaction", + ApiError::InvalidGasMultiplier => "Invalid gas multiplier", + ApiError::InvalidOperations(_) => "Invalid operations", + ApiError::MissingPayloadMetadata => "Payload metadata is missing", + ApiError::UnsupportedCurrency(_) => "Currency is unsupported", + ApiError::UnsupportedSignatureCount(_) => "Number of signatures is not supported", + ApiError::NodeIsOffline => "This API is unavailable for the node because he's offline", + ApiError::BlockNotFound(_) => "Block is missing events", + ApiError::StateValueNotFound(_) => "StateValue not found.", + ApiError::TransactionParseError(_) => "Transaction failed to parse", + ApiError::InternalError(_) => "Internal error", + ApiError::CoinTypeFailedToBeFetched(_) => "Faileed to retrieve the coin type information, please retry", + ApiError::ResourceNotFound(_) => "Resource not found", + ApiError::ModuleNotFound(_) => "Module not found", + ApiError::StructFieldNotFound(_) => "Struct field not found", + ApiError::VersionNotFound(_) => "Version not found", + ApiError::TransactionNotFound(_) => "Transaction not found", + ApiError::TableItemNotFound(_) => "Table item not found", + ApiError::VersionPruned(_) => "Version pruned", + ApiError::BlockPruned(_) => "Block pruned", + ApiError::InvalidInput(_) => "Invalid input", + ApiError::InvalidTransactionUpdate(_) => "Invalid transaction update. Can only update gas unit price", + ApiError::SequenceNumberTooOld(_) => "Sequence number too old. Please create a new transaction with an updated sequence number", + ApiError::VmError(_) => "Transaction submission failed due to VM error", + ApiError::MempoolIsFull(_) => "Mempool is full all accounts", + ApiError::GasEstimationFailed(_) => "Gas estimation failed", + } + } + + pub fn details(self) -> Option { + match self { + ApiError::DeserializationFailed(inner) => inner, + ApiError::InvalidTransferOperations(inner) => inner.map(|inner| inner.to_string()), + ApiError::UnsupportedCurrency(inner) => inner, + ApiError::UnsupportedSignatureCount(inner) => inner.map(|inner| inner.to_string()), + ApiError::TransactionParseError(inner) => inner, + ApiError::InvalidOperations(inner) => inner, + ApiError::InternalError(inner) => inner, + ApiError::CoinTypeFailedToBeFetched(inner) => inner, + ApiError::AccountNotFound(inner) => inner, + ApiError::ResourceNotFound(inner) => inner, + ApiError::ModuleNotFound(inner) => inner, + ApiError::StructFieldNotFound(inner) => inner, + ApiError::VersionNotFound(inner) => inner, + ApiError::TransactionNotFound(inner) => inner, + ApiError::TableItemNotFound(inner) => inner, + ApiError::BlockNotFound(inner) => inner, + ApiError::VersionPruned(inner) => inner, + ApiError::BlockPruned(inner) => inner, + ApiError::InvalidInput(inner) => inner, + ApiError::InvalidTransactionUpdate(inner) => inner, + ApiError::SequenceNumberTooOld(inner) => inner, + ApiError::VmError(inner) => inner, + ApiError::MempoolIsFull(inner) => inner, + ApiError::GasEstimationFailed(inner) => inner, + ApiError::MaxGasFeeTooLow(inner) => inner, + _ => None, + } + .map(|details| ErrorDetails { details }) + } + + pub fn deserialization_failed(type_: &str) -> ApiError { + ApiError::DeserializationFailed(Some(type_.to_string())) + } + + pub fn into_error(self) -> types::Error { + self.into() + } +} + +impl From for types::Error { + fn from(error: ApiError) -> Self { + let message = error.message().to_string(); + let code = error.code(); + let retriable = error.retriable(); + let details = error.details(); + types::Error { + message, + code, + retriable, + details, + } + } +} + +impl From for ApiError { + fn from(err: RestError) -> Self { + match err { + RestError::Api(err) => match err.error.error_code { + AptosErrorCode::AccountNotFound => { + ApiError::AccountNotFound(Some(err.error.message)) + }, + AptosErrorCode::ResourceNotFound => { + ApiError::ResourceNotFound(Some(err.error.message)) + }, + AptosErrorCode::ModuleNotFound => ApiError::ModuleNotFound(Some(err.error.message)), + AptosErrorCode::StructFieldNotFound => { + ApiError::StructFieldNotFound(Some(err.error.message)) + }, + AptosErrorCode::VersionNotFound => { + ApiError::VersionNotFound(Some(err.error.message)) + }, + AptosErrorCode::TransactionNotFound => { + ApiError::TransactionNotFound(Some(err.error.message)) + }, + AptosErrorCode::TableItemNotFound => { + ApiError::TableItemNotFound(Some(err.error.message)) + }, + AptosErrorCode::BlockNotFound => ApiError::BlockNotFound(Some(err.error.message)), + AptosErrorCode::StateValueNotFound => { + ApiError::StateValueNotFound(Some(err.error.message)) + }, + AptosErrorCode::VersionPruned => ApiError::VersionPruned(Some(err.error.message)), + AptosErrorCode::BlockPruned => ApiError::BlockPruned(Some(err.error.message)), + AptosErrorCode::InvalidInput => ApiError::InvalidInput(Some(err.error.message)), + AptosErrorCode::InvalidTransactionUpdate => { + ApiError::InvalidInput(Some(err.error.message)) + }, + AptosErrorCode::SequenceNumberTooOld => { + ApiError::SequenceNumberTooOld(Some(err.error.message)) + }, + AptosErrorCode::VmError => ApiError::VmError(Some(err.error.message)), + AptosErrorCode::HealthCheckFailed => { + ApiError::InternalError(Some(err.error.message)) + }, + AptosErrorCode::MempoolIsFull => ApiError::MempoolIsFull(Some(err.error.message)), + AptosErrorCode::WebFrameworkError => { + ApiError::InternalError(Some(err.error.message)) + }, + AptosErrorCode::BcsNotSupported => ApiError::InvalidInput(Some(err.error.message)), + AptosErrorCode::InternalError => ApiError::InternalError(Some(err.error.message)), + AptosErrorCode::ApiDisabled => ApiError::InternalError(Some(err.error.message)), + }, + RestError::Bcs(_) => ApiError::DeserializationFailed(None), + RestError::Json(_) => ApiError::DeserializationFailed(None), + RestError::Http(status_code, err) => ApiError::InternalError(Some(format!( + "Failed internal API call with HTTP code {}: {:#}", + status_code, err + ))), + RestError::UrlParse(err) => ApiError::InternalError(Some(err.to_string())), + RestError::Timeout(err) => ApiError::InternalError(Some(err.to_string())), + RestError::Unknown(err) => ApiError::InternalError(Some(err.to_string())), + } + } +} + +impl From for ApiError { + fn from(err: AccountAddressParseError) -> Self { + ApiError::DeserializationFailed(Some(err.to_string())) + } +} + +impl From for ApiError { + fn from(err: FromHexError) -> Self { + ApiError::DeserializationFailed(Some(err.to_string())) + } +} + +impl From for ApiError { + fn from(err: bcs::Error) -> Self { + ApiError::DeserializationFailed(Some(err.to_string())) + } +} + +impl From for ApiError { + fn from(err: anyhow::Error) -> Self { + ApiError::InternalError(Some(err.to_string())) + } +} + +impl From for ApiError { + fn from(err: std::num::ParseIntError) -> Self { + ApiError::DeserializationFailed(Some(err.to_string())) + } +} + +impl warp::reject::Reject for ApiError {} + +impl Reply for ApiError { + fn into_response(self) -> warp::reply::Response { + warp::reply::json(&self.into_error()).into_response() + } +} diff --git a/crates/aptos-rosetta/src/lib.rs b/crates/aptos-rosetta/src/lib.rs new file mode 100644 index 0000000000000..a23693081e59f --- /dev/null +++ b/crates/aptos-rosetta/src/lib.rs @@ -0,0 +1,240 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Aptos Rosetta API +//! +//! [Rosetta API Spec](https://www.rosetta-api.org/docs/Reference.html) + +use crate::{ + block::BlockRetriever, + common::{handle_request, with_context}, + error::{ApiError, ApiResult}, + types::Store, +}; +use aptos_config::config::ApiConfig; +use aptos_logger::{debug, warn}; +use aptos_types::{account_address::AccountAddress, chain_id::ChainId}; +use aptos_warp_webserver::{logger, Error, WebServer}; +use std::{collections::BTreeMap, convert::Infallible, sync::Arc}; +use tokio::task::JoinHandle; +use warp::{ + http::{HeaderValue, Method, StatusCode}, + reply, Filter, Rejection, Reply, +}; + +mod account; +mod block; +mod construction; +mod network; + +pub mod client; +pub mod common; +pub mod error; +pub mod types; + +pub const NODE_VERSION: &str = "0.1"; +pub const ROSETTA_VERSION: &str = "1.4.12"; + +/// Rosetta API context for use on all APIs +#[derive(Clone, Debug)] +pub struct RosettaContext { + /// A rest client to connect to a fullnode + rest_client: Option>, + /// ChainId of the chain to connect to + pub chain_id: ChainId, + /// Block index cache + pub block_cache: Option>, + pub owner_addresses: Vec, + pub pool_address_to_owner: BTreeMap, +} + +impl RosettaContext { + pub async fn new( + rest_client: Option>, + chain_id: ChainId, + block_cache: Option>, + owner_addresses: Vec, + ) -> Self { + let mut pool_address_to_owner = BTreeMap::new(); + if let Some(ref rest_client) = rest_client { + // We have to now fill in all of the mappings of owner to pool address + for owner_address in owner_addresses.iter() { + if let Ok(store) = rest_client + .get_account_resource_bcs::( + *owner_address, + "0x1::staking_contract::Store", + ) + .await + { + let store = store.into_inner(); + let pool_addresses: Vec<_> = store + .staking_contracts + .iter() + .map(|(_, pool)| pool.pool_address) + .collect(); + for pool_address in pool_addresses { + pool_address_to_owner.insert(pool_address, *owner_address); + } + } else { + warn!("Did not find a pool for owner: {}", owner_address); + } + } + } + + RosettaContext { + rest_client, + chain_id, + block_cache, + owner_addresses, + pool_address_to_owner, + } + } + + fn rest_client(&self) -> ApiResult> { + if let Some(ref client) = self.rest_client { + Ok(client.clone()) + } else { + Err(ApiError::NodeIsOffline) + } + } + + fn block_cache(&self) -> ApiResult> { + if let Some(ref block_cache) = self.block_cache { + Ok(block_cache.clone()) + } else { + Err(ApiError::NodeIsOffline) + } + } +} + +/// Creates HTTP server (warp-based) for Rosetta +pub fn bootstrap( + chain_id: ChainId, + api_config: ApiConfig, + rest_client: Option, + owner_addresses: Vec, +) -> anyhow::Result { + let runtime = aptos_runtimes::spawn_named_runtime("rosetta".into(), None); + + debug!("Starting up Rosetta server with {:?}", api_config); + + runtime.spawn(bootstrap_async( + chain_id, + api_config, + rest_client, + owner_addresses, + )); + Ok(runtime) +} + +/// Creates HTTP server for Rosetta in an async context +pub async fn bootstrap_async( + chain_id: ChainId, + api_config: ApiConfig, + rest_client: Option, + owner_addresses: Vec, +) -> anyhow::Result> { + debug!("Starting up Rosetta server with {:?}", api_config); + + if let Some(ref client) = rest_client { + assert_eq!( + chain_id.id(), + client + .get_ledger_information() + .await + .expect("Should successfully get ledger information from Rest API on bootstap") + .into_inner() + .chain_id, + "Failed to match Rosetta chain Id to upstream server" + ); + } + + let api = WebServer::from(api_config.clone()); + let handle = tokio::spawn(async move { + // If it's Online mode, add the block cache + let rest_client = rest_client.map(Arc::new); + let block_cache = rest_client.as_ref().map(|rest_client| { + Arc::new(BlockRetriever::new( + api_config.max_transactions_page_size, + rest_client.clone(), + )) + }); + + let context = + RosettaContext::new(rest_client.clone(), chain_id, block_cache, owner_addresses).await; + api.serve(routes(context)).await; + }); + Ok(handle) +} + +/// Collection of all routes for the server +pub fn routes( + context: RosettaContext, +) -> impl Filter + Clone { + account::routes(context.clone()) + .or(block::block_route(context.clone())) + .or(construction::combine_route(context.clone())) + .or(construction::derive_route(context.clone())) + .or(construction::hash_route(context.clone())) + .or(construction::metadata_route(context.clone())) + .or(construction::parse_route(context.clone())) + .or(construction::payloads_route(context.clone())) + .or(construction::preprocess_route(context.clone())) + .or(construction::submit_route(context.clone())) + .or(network::list_route(context.clone())) + .or(network::options_route(context.clone())) + .or(network::status_route(context.clone())) + .or(health_check_route(context)) + .with( + warp::cors() + .allow_any_origin() + .allow_methods(vec![Method::GET, Method::POST]) + .allow_headers(vec![warp::http::header::CONTENT_TYPE]), + ) + .with(logger()) + .recover(handle_rejection) +} + +/// Handle error codes from warp +async fn handle_rejection(err: Rejection) -> Result { + debug!("Failed with: {:?}", err); + let body = reply::json(&Error::new( + StatusCode::INTERNAL_SERVER_ERROR, + format!("unexpected error: {:?}", err), + )); + let mut rep = reply::with_status(body, StatusCode::INTERNAL_SERVER_ERROR).into_response(); + rep.headers_mut() + .insert("access-control-allow-origin", HeaderValue::from_static("*")); + Ok(rep) +} + +/// These parameters are directly passed onto the underlying rest server for a healthcheck +#[derive(serde::Deserialize)] +struct HealthCheckParams { + pub duration_secs: Option, +} + +/// Default amount of time the fullnode is accepted to be behind (arbitrarily it's 5 minutes) +const HEALTH_CHECK_DEFAULT_SECS: u64 = 300; + +pub fn health_check_route( + server_context: RosettaContext, +) -> impl Filter + Clone { + warp::path!("-" / "healthy") + .and(warp::path::end()) + .and(warp::query().map(move |params: HealthCheckParams| params)) + .and(with_context(server_context)) + .and_then(handle_request(health_check)) +} + +/// Calls the underlying REST health check +async fn health_check( + params: HealthCheckParams, + server_context: RosettaContext, +) -> ApiResult<&'static str> { + let rest_client = server_context.rest_client()?; + let duration_secs = params.duration_secs.unwrap_or(HEALTH_CHECK_DEFAULT_SECS); + rest_client.health_check(duration_secs).await?; + + Ok("aptos-node:ok") +} diff --git a/crates/aptos-rosetta/src/main.rs b/crates/aptos-rosetta/src/main.rs new file mode 100644 index 0000000000000..d267b0eabb65c --- /dev/null +++ b/crates/aptos-rosetta/src/main.rs @@ -0,0 +1,286 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +use aptos_config::config::{ApiConfig, DEFAULT_MAX_PAGE_SIZE}; +use aptos_logger::prelude::*; +use aptos_node::AptosNodeArgs; +use aptos_rosetta::bootstrap; +use aptos_sdk::move_types::account_address::AccountAddress; +use aptos_types::chain_id::ChainId; +use clap::Parser; +use std::{ + fs::read_to_string, + net::SocketAddr, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, + time::Duration, +}; +use tokio::time::Instant; + +/// Poll every 100 ms +const DEFAULT_REST_API_WAIT_INTERVAL_MS: u64 = 100; +/// Log failures every 10 seconds +const LOG_INTERVAL_MS: u64 = 10_000; + +#[tokio::main] +async fn main() { + let args: CommandArgs = CommandArgs::parse(); + + match args { + CommandArgs::OnlineRemote(_) => { + println!("aptos-rosetta: Starting Rosetta in Online remote (no local full node) mode") + }, + CommandArgs::Online(_) => { + println!("aptos-rosetta: Starting Rosetta in Online (with local full node) mode") + }, + CommandArgs::Offline(_) => println!("aptos-rosetta: Starting Rosetta in Offline mode"), + } + + // If we're in online mode, we run a full node side by side, the fullnode sets up the logger + let _maybe_node = if let CommandArgs::Online(OnlineLocalArgs { + ref node_args, + ref online_args, + }) = args + { + println!("aptos-rosetta: Starting local full node"); + let node_args = node_args.clone(); + let runtime = thread::spawn(move || node_args.run()); + + // Wait and ensure the node is running on the URL + let client = aptos_rest_client::Client::new(online_args.rest_api_url.clone()); + let start = Instant::now(); + loop { + match client.get_index_bcs().await { + Ok(_) => { + break; + }, + Err(err) => { + sample!( + SampleRate::Duration(Duration::from_millis(LOG_INTERVAL_MS)), + println!( + "aptos-rosetta: Full node REST API isn't responding yet. You should check the node logs. It's been waiting {} seconds. Error: {:?}", + start.elapsed().as_secs(), + err + ) + ); + tokio::time::sleep(Duration::from_millis(DEFAULT_REST_API_WAIT_INTERVAL_MS)) + .await; + }, + } + } + + println!("aptos-rosetta: Local full node started successfully"); + Some(runtime) + } else { + // If we aren't running a full node, set up the logger now + aptos_logger::Logger::new().init(); + None + }; + + println!("aptos-rosetta: Starting rosetta"); + // Ensure runtime for Rosetta is up and running + let _rosetta = bootstrap( + args.chain_id(), + args.api_config(), + args.rest_client(), + args.owner_addresses(), + ) + .expect("aptos-rosetta: Should bootstrap rosetta server"); + + println!("aptos-rosetta: Rosetta started"); + // Run until there is an interrupt + let term = Arc::new(AtomicBool::new(false)); + while !term.load(Ordering::Acquire) { + std::thread::park(); + } +} + +/// A trait to provide common values from both online and offline mode +trait ServerArgs { + /// Retrieve the API config for the local server + fn api_config(&self) -> ApiConfig; + + /// Retrieve the optional rest client for the local server + fn rest_client(&self) -> Option; + + /// Retrieve the chain id + fn chain_id(&self) -> ChainId; + + /// Retrieve owner addresses + fn owner_addresses(&self) -> Vec; +} + +/// Aptos Rosetta API Server +/// +/// Provides an implementation of [Rosetta](https://www.rosetta-api.org/docs/Reference.html) on Aptos. +#[derive(Debug, Parser)] +#[clap(name = "aptos-rosetta", author, version, propagate_version = true)] +pub enum CommandArgs { + /// Run a local online server that connects to a fullnode endpoint + OnlineRemote(OnlineRemoteArgs), + /// Run a local full node in tandem with Rosetta + Online(OnlineLocalArgs), + /// Run a local online server that doesn't connect to a fullnode endpoint + Offline(OfflineArgs), +} + +impl ServerArgs for CommandArgs { + fn api_config(&self) -> ApiConfig { + match self { + CommandArgs::OnlineRemote(args) => args.api_config(), + CommandArgs::Offline(args) => args.api_config(), + CommandArgs::Online(args) => args.api_config(), + } + } + + fn rest_client(&self) -> Option { + match self { + CommandArgs::OnlineRemote(args) => args.rest_client(), + CommandArgs::Offline(args) => args.rest_client(), + CommandArgs::Online(args) => args.rest_client(), + } + } + + fn chain_id(&self) -> ChainId { + match self { + CommandArgs::OnlineRemote(args) => args.chain_id(), + CommandArgs::Offline(args) => args.chain_id(), + CommandArgs::Online(args) => args.chain_id(), + } + } + + fn owner_addresses(&self) -> Vec { + match self { + CommandArgs::OnlineRemote(args) => args.owner_addresses(), + CommandArgs::Offline(args) => args.owner_addresses(), + CommandArgs::Online(args) => args.owner_addresses(), + } + } +} + +#[derive(Debug, Parser)] +pub struct OfflineArgs { + /// Listen address for the server. e.g. 0.0.0.0:8082 + #[clap(long, default_value = "0.0.0.0:8082")] + listen_address: SocketAddr, + /// Path to TLS cert for HTTPS support + #[clap(long)] + tls_cert_path: Option, + /// Path to TLS key for HTTPS support + #[clap(long)] + tls_key_path: Option, + /// Limit to content length on all requests + #[clap(long)] + content_length_limit: Option, + /// ChainId to be used for the server e.g. TESTNET + #[clap(long, default_value_t = ChainId::test())] + chain_id: ChainId, + /// Page size for transactions APIs, must match the downstream node + /// + /// This can be configured to change performance characteristics + #[clap(long, default_value_t = DEFAULT_MAX_PAGE_SIZE)] + transactions_page_size: u16, +} + +impl ServerArgs for OfflineArgs { + fn api_config(&self) -> ApiConfig { + ApiConfig { + enabled: true, + address: self.listen_address, + tls_cert_path: self.tls_cert_path.clone(), + tls_key_path: self.tls_key_path.clone(), + content_length_limit: self.content_length_limit, + max_transactions_page_size: self.transactions_page_size, + ..Default::default() + } + } + + fn rest_client(&self) -> Option { + None + } + + fn chain_id(&self) -> ChainId { + self.chain_id + } + + fn owner_addresses(&self) -> Vec { + vec![] + } +} + +#[derive(Debug, Parser)] +pub struct OnlineRemoteArgs { + #[clap(flatten)] + offline_args: OfflineArgs, + /// URL for the Aptos REST API. e.g. https://fullnode.devnet.aptoslabs.com + #[clap(long, default_value = "http://localhost:8080")] + rest_api_url: url::Url, + /// Owner addresses file as a YAML file with a list + #[clap(long, value_parser)] + owner_address_file: Option, +} + +impl ServerArgs for OnlineRemoteArgs { + fn api_config(&self) -> ApiConfig { + self.offline_args.api_config() + } + + fn rest_client(&self) -> Option { + Some(aptos_rest_client::Client::new(self.rest_api_url.clone())) + } + + fn chain_id(&self) -> ChainId { + self.offline_args.chain_id + } + + fn owner_addresses(&self) -> Vec { + if let Some(ref path) = self.owner_address_file { + serde_yaml::from_str( + &read_to_string(path.as_path()).expect("Failed to read owner address file"), + ) + .expect("Owner address file is in an invalid format") + } else { + vec![] + } + } +} + +#[derive(Debug, Parser)] +pub struct OnlineLocalArgs { + #[clap(flatten)] + online_args: OnlineRemoteArgs, + #[clap(flatten)] + node_args: AptosNodeArgs, +} + +impl ServerArgs for OnlineLocalArgs { + fn api_config(&self) -> ApiConfig { + self.online_args.offline_args.api_config() + } + + fn rest_client(&self) -> Option { + Some(aptos_rest_client::Client::new( + self.online_args.rest_api_url.clone(), + )) + } + + fn chain_id(&self) -> ChainId { + self.online_args.offline_args.chain_id + } + + fn owner_addresses(&self) -> Vec { + self.online_args.owner_addresses() + } +} + +#[test] +fn verify_tool() { + use clap::CommandFactory; + CommandArgs::command().debug_assert() +} diff --git a/crates/aptos-rosetta/src/network.rs b/crates/aptos-rosetta/src/network.rs new file mode 100644 index 0000000000000..926ee55192c38 --- /dev/null +++ b/crates/aptos-rosetta/src/network.rs @@ -0,0 +1,172 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{check_network, handle_request, with_context, with_empty_request}, + error::ApiError, + types::{ + Allow, MetadataRequest, NetworkListResponse, NetworkOptionsResponse, NetworkRequest, + NetworkStatusResponse, OperationStatusType, OperationType, Version, + }, + RosettaContext, NODE_VERSION, ROSETTA_VERSION, +}; +use aptos_logger::{debug, trace}; +use warp::Filter; + +pub fn list_route( + server_context: RosettaContext, +) -> impl Filter + Clone { + warp::path!("network" / "list") + .and(warp::post()) + .and(with_empty_request()) + .and(with_context(server_context)) + .and_then(handle_request(network_list)) +} + +pub fn options_route( + server_context: RosettaContext, +) -> impl Filter + Clone { + warp::path!("network" / "options") + .and(warp::post()) + .and(warp::body::json()) + .and(with_context(server_context)) + .and_then(handle_request(network_options)) +} + +pub fn status_route( + server_context: RosettaContext, +) -> impl Filter + Clone { + warp::path!("network" / "status") + .and(warp::post()) + .and(warp::body::json()) + .and(with_context(server_context)) + .and_then(handle_request(network_status)) +} + +/// List [`NetworkIdentifier`]s supported by this proxy aka [`ChainId`]s +/// +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networklist) +async fn network_list( + _empty: MetadataRequest, + server_context: RosettaContext, +) -> Result { + debug!("/network/list"); + trace!( + server_context = ?server_context, + "network_list", + ); + + let response = NetworkListResponse { + network_identifiers: vec![server_context.chain_id.into()], + }; + + Ok(response) +} + +/// Get Network options +/// +/// This lists out all errors, operations, and statuses, along with versioning information. +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) +async fn network_options( + request: NetworkRequest, + server_context: RosettaContext, +) -> Result { + debug!("/network/options"); + trace!( + request = ?request, + server_context = ?server_context, + "network_options", + ); + + check_network(request.network_identifier, &server_context)?; + + let version = Version { + rosetta_version: ROSETTA_VERSION.to_string(), + // TODO: Get from node via REST API + node_version: NODE_VERSION.to_string(), + middleware_version: "0.1.0".to_string(), + }; + + let operation_statuses = OperationStatusType::all() + .into_iter() + .map(|status| status.into()) + .collect(); + let operation_types = OperationType::all() + .into_iter() + .map(|op| op.to_string()) + .collect(); + let errors = ApiError::all() + .into_iter() + .map(|err| err.into_error()) + .collect(); + + let allow = Allow { + operation_statuses, + operation_types, + errors, + historical_balance_lookup: true, + timestamp_start_index: 2, + call_methods: vec![], + balance_exemptions: vec![], + mempool_coins: false, + }; + + let response = NetworkOptionsResponse { version, allow }; + + Ok(response) +} + +/// Get network status including the latest state +/// +/// This should respond with the latest ledger version, timestamp, and genesis information +/// +/// [API Spec](https://www.rosetta-api.org/docs/NetworkApi.html#networkoptions) +async fn network_status( + request: NetworkRequest, + server_context: RosettaContext, +) -> Result { + debug!("/network/status"); + trace!( + request = ?request, + server_context = ?server_context, + "network_status", + ); + + check_network(request.network_identifier, &server_context)?; + let chain_id = server_context.chain_id; + let rest_client = server_context.rest_client()?; + let block_cache = server_context.block_cache()?; + let genesis_block_identifier = block_cache + .get_block_info_by_height(0, chain_id) + .await? + .block_id; + let response = rest_client.get_ledger_information().await?; + let state = response.state(); + + // Get the oldest block + let oldest_block_identifier = block_cache + .get_block_info_by_height(state.oldest_block_height, chain_id) + .await? + .block_id; + + // Get the latest block + let current_block = block_cache + .get_block_info_by_height(state.block_height, chain_id) + .await?; + let current_block_identifier = current_block.block_id; + + let response = NetworkStatusResponse { + current_block_identifier, + current_block_timestamp: current_block.timestamp, + genesis_block_identifier, + oldest_block_identifier, + sync_status: None, + peers: vec![], + }; + + Ok(response) +} diff --git a/crates/aptos-rosetta/src/types/identifiers.rs b/crates/aptos-rosetta/src/types/identifiers.rs new file mode 100644 index 0000000000000..e6ac9a79b908a --- /dev/null +++ b/crates/aptos-rosetta/src/types/identifiers.rs @@ -0,0 +1,558 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Identifiers for the Rosetta spec +//! +//! [Spec](https://www.rosetta-api.org/docs/api_identifiers.html) + +use crate::{ + common::{to_hex_lower, BlockHash, BLOCKCHAIN}, + error::{ApiError, ApiResult}, +}; +use aptos_types::{ + account_address::AccountAddress, chain_id::ChainId, transaction::TransactionInfo, +}; +use serde::{Deserialize, Serialize}; +use std::{ + convert::{TryFrom, TryInto}, + str::FromStr, +}; + +/// Account identifier, specified as a hex encoded account address (with leading 0x) +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/AccountIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AccountIdentifier { + /// Hex encoded AccountAddress beginning with 0x + pub address: String, + /// Sub account only used for staking + #[serde(skip_serializing_if = "Option::is_none")] + pub sub_account: Option, +} + +impl AccountIdentifier { + /// Convert [`AccountIdentifier`] to an [`AccountAddress`] + pub fn account_address(&self) -> ApiResult { + str_to_account_address(self.address.as_str()) + } + + pub fn pool_address(&self) -> ApiResult> { + if let Some(sub_account) = &self.sub_account { + if let Some(metadata) = &sub_account.metadata { + return str_to_account_address(metadata.pool_address.as_str()).map(Some); + } + } + + Ok(None) + } + + pub fn base_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: None, + } + } + + pub fn total_stake_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_total_stake()), + } + } + + pub fn pending_active_stake_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_pending_active_stake()), + } + } + + pub fn active_stake_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_active_stake()), + } + } + + pub fn pending_inactive_stake_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_pending_inactive_stake()), + } + } + + pub fn inactive_stake_account(address: AccountAddress) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_inactive_stake()), + } + } + + pub fn operator_stake_account( + address: AccountAddress, + operator_address: AccountAddress, + ) -> Self { + AccountIdentifier { + address: to_hex_lower(&address), + sub_account: Some(SubAccountIdentifier::new_operator_stake(operator_address)), + } + } + + pub fn is_base_account(&self) -> bool { + self.sub_account.is_none() + } + + pub fn is_total_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_total_stake() + } else { + false + } + } + + pub fn is_pending_active_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_pending_active_stake() + } else { + false + } + } + + pub fn is_active_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_active_stake() + } else { + false + } + } + + pub fn is_pending_inactive_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_pending_inactive_stake() + } else { + false + } + } + + pub fn is_inactive_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_inactive_stake() + } else { + false + } + } + + pub fn is_delegator_active_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_delegator_active_stake() + } else { + false + } + } + + pub fn is_delegator_inactive_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_delegator_inactive_stake() + } else { + false + } + } + + pub fn is_delegator_pending_inactive_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + inner.is_delegator_pending_inactive_stake() + } else { + false + } + } + + pub fn is_operator_stake(&self) -> bool { + if let Some(ref inner) = self.sub_account { + !(inner.is_total_stake() + || inner.is_active_stake() + || inner.is_pending_active_stake() + || inner.is_inactive_stake() + || inner.is_pending_inactive_stake()) + } else { + false + } + } + + pub fn operator_address(&self) -> ApiResult { + if let Some(ref inner) = self.sub_account { + inner.operator_address() + } else { + Err(ApiError::InternalError(Some( + "Can't get operator address of a non-operator stake account".to_string(), + ))) + } + } +} + +fn str_to_account_address(address: &str) -> Result { + AccountAddress::from_str(address) + .map_err(|_| ApiError::InvalidInput(Some("Invalid account address".to_string()))) +} + +/// There are two types of SubAccountIdentifiers +/// 1. `stake` which is the total stake +/// 2. `stake-` which is the stake on the operator +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct SubAccountIdentifier { + /// Hex encoded AccountAddress beginning with 0x + pub address: String, + /// Metadata only used for delegated staking + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +const STAKE: &str = "stake"; +const PENDING_ACTIVE_STAKE: &str = "pending_active_stake"; +const ACTIVE_STAKE: &str = "active_stake"; +const PENDING_INACTIVE_STAKE: &str = "pending_inactive_stake"; +const INACTIVE_STAKE: &str = "inactive_stake"; +const ACCOUNT_SEPARATOR: char = '-'; + +impl SubAccountIdentifier { + pub fn new_total_stake() -> SubAccountIdentifier { + SubAccountIdentifier { + address: STAKE.to_string(), + metadata: None, + } + } + + pub fn new_pending_active_stake() -> SubAccountIdentifier { + SubAccountIdentifier { + address: PENDING_ACTIVE_STAKE.to_string(), + metadata: None, + } + } + + pub fn new_active_stake() -> SubAccountIdentifier { + SubAccountIdentifier { + address: ACTIVE_STAKE.to_string(), + metadata: None, + } + } + + pub fn new_pending_inactive_stake() -> SubAccountIdentifier { + SubAccountIdentifier { + address: PENDING_INACTIVE_STAKE.to_string(), + metadata: None, + } + } + + pub fn new_inactive_stake() -> SubAccountIdentifier { + SubAccountIdentifier { + address: INACTIVE_STAKE.to_string(), + metadata: None, + } + } + + pub fn new_delegated_total_stake(pool: &str) -> SubAccountIdentifier { + SubAccountIdentifier { + address: STAKE.to_string(), + metadata: Some(SubAccountIdentifierMetadata::new_pool_address( + AccountAddress::from_str(pool).unwrap(), + )), + } + } + + pub fn new_delegated_active_stake(pool: &str) -> SubAccountIdentifier { + SubAccountIdentifier { + address: ACTIVE_STAKE.to_string(), + metadata: Some(SubAccountIdentifierMetadata::new_pool_address( + AccountAddress::from_str(pool).unwrap(), + )), + } + } + + pub fn new_delegated_pending_inactive_stake(pool: &str) -> SubAccountIdentifier { + SubAccountIdentifier { + address: PENDING_INACTIVE_STAKE.to_string(), + metadata: Some(SubAccountIdentifierMetadata::new_pool_address( + AccountAddress::from_str(pool).unwrap(), + )), + } + } + + pub fn new_delegated_inactive_stake(pool: &str) -> SubAccountIdentifier { + SubAccountIdentifier { + address: INACTIVE_STAKE.to_string(), + metadata: Some(SubAccountIdentifierMetadata::new_pool_address( + AccountAddress::from_str(pool).unwrap(), + )), + } + } + + pub fn new_operator_stake(operator: AccountAddress) -> SubAccountIdentifier { + SubAccountIdentifier { + address: format!("{}-{}", STAKE, to_hex_lower(&operator)), + metadata: None, + } + } + + pub fn is_total_stake(&self) -> bool { + self.address.as_str() == STAKE + } + + pub fn is_pending_active_stake(&self) -> bool { + self.address.as_str() == PENDING_ACTIVE_STAKE + } + + pub fn is_active_stake(&self) -> bool { + self.address.as_str() == ACTIVE_STAKE && self.metadata.is_none() + } + + pub fn is_pending_inactive_stake(&self) -> bool { + self.address.as_str() == PENDING_INACTIVE_STAKE && self.metadata.is_none() + } + + pub fn is_inactive_stake(&self) -> bool { + self.address.as_str() == INACTIVE_STAKE && self.metadata.is_none() + } + + pub fn is_delegator_active_stake(&self) -> bool { + self.address.as_str() == ACTIVE_STAKE && self.metadata.is_some() + } + + pub fn is_delegator_inactive_stake(&self) -> bool { + self.address.as_str() == INACTIVE_STAKE && self.metadata.is_some() + } + + pub fn is_delegator_pending_inactive_stake(&self) -> bool { + self.address.as_str() == PENDING_INACTIVE_STAKE && self.metadata.is_some() + } + + pub fn operator_address(&self) -> ApiResult { + let mut parts = self.address.split(ACCOUNT_SEPARATOR); + + if let Some(stake) = parts.next() { + if stake == STAKE { + if let Some(operator) = parts.next() { + return str_to_account_address(operator); + } + } + } + + Err(ApiError::InvalidInput(Some(format!( + "Sub account isn't an operator address {:?}", + self + )))) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct SubAccountIdentifierMetadata { + /// Hex encoded Pool beginning with 0x + pub pool_address: String, +} + +impl SubAccountIdentifierMetadata { + pub fn new_pool_address(pool_address: AccountAddress) -> Self { + SubAccountIdentifierMetadata { + pool_address: to_hex_lower(&pool_address), + } + } +} + +/// Identifier for a "block". In aptos, we use a transaction model, so the index +/// represents multiple transactions in a "block" grouping of transactions +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/BlockIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct BlockIdentifier { + /// Block index, which points to a txn at the beginning of a "block" + pub index: u64, + /// Accumulator hash at the beginning of the block + pub hash: String, +} + +impl BlockIdentifier { + pub fn from_block( + block: &aptos_rest_client::aptos_api_types::BcsBlock, + chain_id: ChainId, + ) -> BlockIdentifier { + BlockIdentifier { + index: block.block_height, + hash: BlockHash::new(chain_id, block.block_height).to_string(), + } + } +} + +/// Identifier for this specific network deployment +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct NetworkIdentifier { + /// Blockchain name, should always be `aptos` and be hardcoded + pub blockchain: String, + /// Network name which we use ChainId for it + pub network: String, +} + +impl NetworkIdentifier { + pub fn chain_id(&self) -> ApiResult { + self.try_into() + } +} + +impl TryFrom<&NetworkIdentifier> for ChainId { + type Error = ApiError; + + fn try_from(network_identifier: &NetworkIdentifier) -> Result { + ChainId::from_str(network_identifier.network.trim()) + .map_err(|err| ApiError::InvalidInput(Some(err.to_string()))) + } +} + +impl From for NetworkIdentifier { + fn from(chain_id: ChainId) -> Self { + NetworkIdentifier { + blockchain: BLOCKCHAIN.to_string(), + network: chain_id.to_string(), + } + } +} + +/// Identifies a specific [`crate::types::Operation`] within a `Transaction` +/// +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/OperationIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct OperationIdentifier { + /// The unique index of the operation within a transaction + /// + /// It must be 0 to n within the transaction. + pub index: u64, +} + +/// Partial block identifier for querying by version or by hash. Both should not be +/// provided at the same time. +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/PartialBlockIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct PartialBlockIdentifier { + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option, + /// Hash of the block + #[serde(skip_serializing_if = "Option::is_none")] + pub hash: Option, +} + +impl PartialBlockIdentifier { + pub fn latest() -> Self { + Self { + index: None, + hash: None, + } + } + + pub fn by_hash(hash: String) -> Self { + Self { + index: None, + hash: Some(hash), + } + } + + pub fn block_index(index: u64) -> Self { + Self { + index: Some(index), + hash: None, + } + } +} + +/// TransactionIdentifier to represent a transaction by hash +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/TransactionIdentifier.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TransactionIdentifier { + /// The hash of the transaction so it can be looked up in mempool + pub hash: String, +} + +impl From<&TransactionInfo> for TransactionIdentifier { + fn from(txn: &TransactionInfo) -> Self { + TransactionIdentifier { + hash: to_hex_lower(&txn.transaction_hash()), + } + } +} + +impl From for TransactionIdentifier { + fn from(hash: aptos_crypto::HashValue) -> Self { + TransactionIdentifier { + hash: to_hex_lower(&hash), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_account_id() { + let account = AccountAddress::ONE; + let operator = AccountAddress::ZERO; + + let base_account = AccountIdentifier::base_account(account); + let total_stake_account = AccountIdentifier::total_stake_account(account); + let operator_stake_account = AccountIdentifier::operator_stake_account(account, operator); + let active_stake_account = AccountIdentifier::active_stake_account(account); + let pending_active_stake_account = AccountIdentifier::pending_active_stake_account(account); + let inactive_stake_account = AccountIdentifier::inactive_stake_account(account); + let pending_inactive_stake_account = + AccountIdentifier::pending_inactive_stake_account(account); + + assert!(base_account.is_base_account()); + assert!(!operator_stake_account.is_base_account()); + assert!(!total_stake_account.is_base_account()); + assert!(!active_stake_account.is_base_account()); + assert!(!pending_active_stake_account.is_base_account()); + assert!(!inactive_stake_account.is_base_account()); + assert!(!pending_inactive_stake_account.is_base_account()); + + assert!(!base_account.is_operator_stake()); + assert!(operator_stake_account.is_operator_stake()); + assert!(!total_stake_account.is_operator_stake()); + + assert!(!base_account.is_total_stake()); + assert!(!operator_stake_account.is_total_stake()); + assert!(total_stake_account.is_total_stake()); + + assert!(active_stake_account.is_active_stake()); + assert!(pending_active_stake_account.is_pending_active_stake()); + assert!(inactive_stake_account.is_inactive_stake()); + assert!(pending_inactive_stake_account.is_pending_inactive_stake()); + + assert_eq!(Ok(account), base_account.account_address()); + assert_eq!(Ok(account), operator_stake_account.account_address()); + assert_eq!(Ok(account), total_stake_account.account_address()); + assert_eq!(Ok(account), active_stake_account.account_address()); + assert_eq!(Ok(account), pending_active_stake_account.account_address()); + assert_eq!(Ok(account), inactive_stake_account.account_address()); + assert_eq!( + Ok(account), + pending_inactive_stake_account.account_address() + ); + + assert!(base_account.operator_address().is_err()); + assert_eq!(Ok(operator), operator_stake_account.operator_address()); + assert!(total_stake_account.operator_address().is_err()); + } + + #[test] + fn test_sub_account_id() { + let stake = SubAccountIdentifier::new_total_stake(); + assert!(stake.is_total_stake()); + + let operator_address = AccountAddress::ZERO; + let operator = SubAccountIdentifier::new_operator_stake(operator_address); + assert!(!operator.is_total_stake()); + assert_eq!(Ok(operator_address), operator.operator_address()); + + assert!(stake.operator_address().is_err()); + } +} diff --git a/crates/aptos-rosetta/src/types/mod.rs b/crates/aptos-rosetta/src/types/mod.rs new file mode 100644 index 0000000000000..6d644695c34e3 --- /dev/null +++ b/crates/aptos-rosetta/src/types/mod.rs @@ -0,0 +1,14 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod identifiers; +mod misc; +mod move_types; +mod objects; +mod requests; + +pub use identifiers::*; +pub use misc::*; +pub use move_types::*; +pub use objects::*; +pub use requests::*; diff --git a/crates/aptos-rosetta/src/types/move_types.rs b/crates/aptos-rosetta/src/types/move_types.rs new file mode 100644 index 0000000000000..1701e5ceaa295 --- /dev/null +++ b/crates/aptos-rosetta/src/types/move_types.rs @@ -0,0 +1,246 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Types and identifiers for parsing Move pub structs and types + +use crate::AccountAddress; +use aptos_types::event::EventHandle; +use serde::{Deserialize, Serialize}; + +pub const ACCOUNT_MODULE: &str = "account"; +pub const APTOS_ACCOUNT_MODULE: &str = "aptos_account"; +pub const APTOS_COIN_MODULE: &str = "aptos_coin"; +pub const COIN_MODULE: &str = "coin"; +pub const STAKE_MODULE: &str = "stake"; +pub const STAKING_PROXY_MODULE: &str = "staking_proxy"; +pub const STAKING_CONTRACT_MODULE: &str = "staking_contract"; +pub const VESTING_MODULE: &str = "vesting"; +pub const DELEGATION_POOL_MODULE: &str = "delegation_pool"; + +pub const ACCOUNT_RESOURCE: &str = "Account"; +pub const APTOS_COIN_RESOURCE: &str = "AptosCoin"; +pub const COIN_INFO_RESOURCE: &str = "CoinInfo"; +pub const COIN_STORE_RESOURCE: &str = "CoinStore"; +pub const STAKE_POOL_RESOURCE: &str = "StakePool"; +pub const STAKING_CONTRACT_RESOURCE: &str = "StakingContract"; +pub const STORE_RESOURCE: &str = "Store"; +pub const STAKING_GROUP_UPDATE_COMMISSION_RESOURCE: &str = "StakingGroupUpdateCommissionEvent"; +pub const VESTING_RESOURCE: &str = "Vesting"; +pub const DELEGATION_POOL_RESOURCE: &str = "DelegationPool"; +pub const WITHDRAW_STAKE_EVENT: &str = "WithdrawStakeEvent"; + +pub const CREATE_ACCOUNT_FUNCTION: &str = "create_account"; +pub const TRANSFER_FUNCTION: &str = "transfer"; + +// Staking Contract +pub const RESET_LOCKUP_FUNCTION: &str = "reset_lockup"; +pub const CREATE_STAKING_CONTRACT_FUNCTION: &str = "create_staking_contract"; +pub const SWITCH_OPERATOR_WITH_SAME_COMMISSION_FUNCTION: &str = + "switch_operator_with_same_commission"; +pub const UPDATE_VOTER_FUNCTION: &str = "update_voter"; +pub const UNLOCK_STAKE_FUNCTION: &str = "unlock_stake"; +// TODO fix the typo in function name. commision -> commission +pub const UPDATE_COMMISSION_FUNCTION: &str = "update_commision"; +pub const DISTRIBUTE_STAKING_REWARDS_FUNCTION: &str = "distribute"; + +// Delegation Pool Contract +pub const DELEGATION_POOL_ADD_STAKE_FUNCTION: &str = "add_stake"; +pub const DELEGATION_POOL_UNLOCK_FUNCTION: &str = "unlock"; +pub const DELEGATION_POOL_WITHDRAW_FUNCTION: &str = "withdraw"; + +pub const DECIMALS_FIELD: &str = "decimal"; +pub const DEPOSIT_EVENTS_FIELD: &str = "deposit_events"; +pub const WITHDRAW_EVENTS_FIELD: &str = "withdraw_events"; +pub const SET_OPERATOR_EVENTS_FIELD: &str = "set_operator_events"; +pub const SEQUENCE_NUMBER_FIELD: &str = "sequence_number"; +pub const SYMBOL_FIELD: &str = "symbol"; + +// Staking Contract +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct StakingContract { + pub principal: u64, + pub pool_address: AccountAddress, + pub owner_cap: Capability, + pub commission_percentage: u64, + pub distribution_pool: Pool, + pub signer_cap: Capability, +} + +impl StakingContract { + pub fn get_balance(&self, account_address: &AccountAddress) -> Option { + self.distribution_pool.get_balance(account_address) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Store { + pub staking_contracts: Vec<(AccountAddress, StakingContract)>, + pub create_staking_contract_events: EventHandle, + pub update_voter_events: EventHandle, + pub reset_lockup_events: EventHandle, + pub add_stake_events: EventHandle, + pub request_commission_events: EventHandle, + pub unlock_stake_events: EventHandle, + pub switch_operator_events: EventHandle, + pub add_distribution_events: EventHandle, + pub distribute_events: EventHandle, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct StakingGroupUpdateCommissionEvent { + pub update_commission_events: EventHandle, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UpdateCommissionEvent { + pub staker: AccountAddress, + pub operator: AccountAddress, + pub old_commission_percentage: u64, + pub new_commission_percentage: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreateStakingContractEvent { + pub operator: AccountAddress, + pub voter: AccountAddress, + pub pool_address: AccountAddress, + pub principal: u64, + pub commission_percentage: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UpdateVoterEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub old_voter: AccountAddress, + pub new_voter: AccountAddress, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ResetLockupEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AddStakeEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub amount: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RequestCommissionEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub accumulated_rewards: u64, + pub commission_amount: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UnlockStakeEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub amount: u64, + pub commission_paid: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SwitchOperatorEvent { + pub old_operator: AccountAddress, + pub new_operator: AccountAddress, + pub pool_address: AccountAddress, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AddDistributionEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub amount: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DistributeEvent { + pub operator: AccountAddress, + pub pool_address: AccountAddress, + pub recipient: AccountAddress, + pub amount: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Pool { + pub shareholders_limit: u64, + pub total_coins: u64, + pub total_shares: u64, + pub shares: Vec<(AccountAddress, u64)>, + pub shareholders: Vec, + pub scaling_factor: u64, +} + +impl Pool { + pub fn get_balance(&self, account_address: &AccountAddress) -> Option { + self.shares + .iter() + .find(|(address, _)| address == account_address) + .map(|(_, shares)| (*shares * self.total_coins) / self.total_shares) + } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub struct Capability { + pub pool_address: AccountAddress, +} + +// Delegation Pool Contract +#[derive(Debug, Serialize, Deserialize)] +pub struct SharesPool { + pub shareholders_limit: u64, + pub total_coins: u64, + pub total_shares: u64, + pub shares: Vec<(AccountAddress, u64)>, + pub shareholders: Vec, + pub scaling_factor: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ObservedLockupCycle { + pub index: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DelegationPool { + pub active_shares: SharesPool, + pub observed_lockup_cycle: ObservedLockupCycle, + pub inactive_shares: Vec<(ObservedLockupCycle, SharesPool)>, + pub pending_withdrawals: Vec<(AccountAddress, ObservedLockupCycle)>, + pub stake_pool_signer_cap: Capability, + pub total_coins_inactive: u64, + pub operator_commission_percentage: u64, + + pub add_stake_events: EventHandle, + pub reactivate_stake_events: EventHandle, + pub unlock_stake_events: EventHandle, + pub withdraw_stake_events: EventHandle, + pub distribute_commission_events: EventHandle, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AddDelegationEvent { + pub pool_address: AccountAddress, + pub delegator_address: AccountAddress, + pub amount_added: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UndelegationEvent { + pub pool_address: AccountAddress, + pub delegator_address: AccountAddress, + pub amount_unlocked: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WithdrawUndelegedEvent { + pub pool_address: AccountAddress, + pub delegator_address: AccountAddress, + pub amount_withdrawn: u64, +} diff --git a/crates/aptos-rosetta/src/types/requests.rs b/crates/aptos-rosetta/src/types/requests.rs new file mode 100644 index 0000000000000..32d74addcff11 --- /dev/null +++ b/crates/aptos-rosetta/src/types/requests.rs @@ -0,0 +1,569 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + types::{ + AccountIdentifier, Allow, Amount, Block, BlockIdentifier, Currency, InternalOperation, + NetworkIdentifier, Operation, PartialBlockIdentifier, Peer, PublicKey, Signature, + SigningPayload, SyncStatus, Transaction, TransactionIdentifier, Version, + }, + AccountAddress, ApiError, +}; +use aptos_rest_client::aptos_api_types::U64; +use aptos_types::{ + chain_id::ChainId, + transaction::{RawTransaction, SignedTransaction}, +}; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +/// Request for an account's currency balance either now, or historically +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/AccountBalanceRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AccountBalanceRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Account identifier describing the account address + pub account_identifier: AccountIdentifier, + /// For historical balance lookups by either hash or version + #[serde(skip_serializing_if = "Option::is_none")] + pub block_identifier: Option, + /// For filtering which currencies to show + #[serde(skip_serializing_if = "Option::is_none")] + pub currencies: Option>, +} + +/// Response with the version associated and the balances of the account +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/AccountBalanceResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AccountBalanceResponse { + /// Block containing the balance + pub block_identifier: BlockIdentifier, + /// Balances of all known currencies + pub balances: Vec, + /// Metadata of account, must have sequence number + pub metadata: AccountBalanceMetadata, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AccountBalanceMetadata { + /// Sequence number of the account + pub sequence_number: U64, + #[serde(skip_serializing_if = "Option::is_none")] + pub operators: Option>, + pub lockup_expiration_time_utc: U64, +} +/// Reqyest a block (version) on the account +/// +/// With neither value for PartialBlockIdentifier, get the latest version +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/BlockRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct BlockRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// A set of search parameters (latest, by hash, or by index) + #[serde(skip_serializing_if = "Option::is_none")] + pub block_identifier: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct BlockRequestMetadata { + #[serde(skip_serializing_if = "Option::is_none")] + pub keep_empty_transactions: Option, +} + +impl BlockRequest { + fn new(chain_id: ChainId, block_identifier: Option) -> Self { + Self { + network_identifier: chain_id.into(), + block_identifier, + metadata: None, + } + } + + pub fn latest(chain_id: ChainId) -> Self { + Self::new(chain_id, None) + } + + pub fn by_hash(chain_id: ChainId, hash: String) -> Self { + Self::new(chain_id, Some(PartialBlockIdentifier::by_hash(hash))) + } + + pub fn by_index(chain_id: ChainId, index: u64) -> Self { + Self::new(chain_id, Some(PartialBlockIdentifier::block_index(index))) + } + + pub fn with_empty_transactions(mut self) -> Self { + self.metadata = Some(BlockRequestMetadata { + keep_empty_transactions: Some(true), + }); + self + } +} + +/// Response that will always have a valid block populated +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/BlockResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct BlockResponse { + /// The block requested. This should always be populated for a given valid version + pub block: Block, +} + +/// Request to combine signatures and an unsigned transaction for submission as a +/// [`aptos_types::transaction::SignedTransaction`] +/// +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionCombineRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionCombineRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// A hex encoded, BCS encoded, [`aptos_types::transaction::RawTransaction`] + pub unsigned_transaction: String, + /// Set of signatures with SigningPayloads to combine + pub signatures: Vec, +} + +/// Response of signed transaction for submission +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionCombineResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionCombineResponse { + /// A hex encoded, BCS encoded, [`aptos_types::transaction::SignedTransaction`] + pub signed_transaction: String, +} + +/// Request to derive an account from a public key +/// +/// This should be able to run without a running full node connection, but note that +/// this will not work with accounts that have rotated their public key. It should +/// only be used when an account is being created. +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionDeriveRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionDeriveRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Public key to derive an [`aptos_types::account_address::AccountAddress`] from + pub public_key: PublicKey, +} + +/// Response of derived account from a public key +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionDeriveResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionDeriveResponse { + /// The account identifier of the account if the [`aptos_types::account_address::AccountAddress`] can be derived. + /// + /// This will always return a value, though it might not match onchain information. + pub account_identifier: AccountIdentifier, +} + +/// Request to hash a transaction +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionHashRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionHashRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// A hex encoded, BCS encoded, [`aptos_types::transaction::SignedTransaction`] + pub signed_transaction: String, +} + +/// Request to retrieve all information needed for constructing a transaction from the blockchain +/// +/// A running full node is required for this API +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionMetadataRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionMetadataRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Information telling which metadata to lookup onchain + /// + /// This comes verbatim from a preprocess request + pub options: MetadataOptions, +} + +/// A set of operations to tell us which metadata to lookup onchain +/// +/// This is built from Preprocess, and is copied verbatim to the metadata request +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MetadataOptions { + /// The operation to run at a high level (e.g. CreateAccount/Transfer) + pub internal_operation: InternalOperation, + /// Maximum total gas units willing to pay for the transaction + #[serde(skip_serializing_if = "Option::is_none")] + pub max_gas_amount: Option, + /// Multiplier how much more willing to pay for the fees + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price_per_unit: Option, + /// Unix timestamp of expiry time + #[serde(skip_serializing_if = "Option::is_none")] + pub expiry_time_secs: Option, + /// Sequence number of the request + #[serde(skip_serializing_if = "Option::is_none")] + pub sequence_number: Option, + /// Public keys to sign simulated transaction. Must be present if max_gas_amount is not provided + #[serde(skip_serializing_if = "Option::is_none")] + pub public_keys: Option>, + /// Taking the estimated gas price, and multiplying it + /// times this number divided by 100 e.g. 120 is 120% + /// of the estimated price + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price_multiplier: Option, + /// Gas price priority. If the priority is low, it will + /// use a deprioritized price. If it's normal, it will use the estimated + /// price, and if it's high, it will use the prioritized price. + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price_priority: Option, +} + +/// Response with network specific data for constructing a transaction +/// +/// In this case, sequence number must be pulled from onchain. +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionMetadataResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionMetadataResponse { + /// Metadata that will be passed to Payloads to create a transaction + pub metadata: ConstructionMetadata, + /// A suggested gas fee based on the current state of the network + pub suggested_fee: Vec, +} + +/// Metadata required to construct a transaction +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionMetadata { + /// Sequence number of the sending account + pub sequence_number: U64, + /// Maximum gas willing to pay for the transaction + pub max_gas_amount: U64, + /// Multiplier e.g. how much each unit of gas is worth in the native coin + pub gas_price_per_unit: U64, + /// Unix timestamp of expiry time, defaults to 30 seconds from the payload request + #[serde(skip_serializing_if = "Option::is_none")] + pub expiry_time_secs: Option, + /// Because we need information from metadata to have the real operation + /// We don't have to parse any fields in the `Payloads` call + pub internal_operation: InternalOperation, +} + +/// Request to parse a signed or unsigned transaction into operations +/// +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionParseRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionParseRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Whether the transaction is a [`aptos_types::transaction::SignedTransaction`] + /// or a [`aptos_types::transaction::RawTransaction`] + pub signed: bool, + /// A hex encoded, BCS encoded [`aptos_types::transaction::SignedTransaction`] + /// or a [`aptos_types::transaction::RawTransaction`] + pub transaction: String, +} + +/// Response with operations in a transaction blob +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionParseResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionParseResponse { + /// The set of [`Operation`] that happened during the transaction + pub operations: Vec, + /// The signers of the transaction, if it was a [`aptos_types::transaction::SignedTransaction`] + #[serde(skip_serializing_if = "Option::is_none")] + pub account_identifier_signers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionParseMetadata { + #[serde(skip_serializing_if = "Option::is_none")] + pub unsigned_transaction: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub signed_transaction: Option, +} + +/// Request to build payloads from the operations to sign +/// +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPayloadsRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionPayloadsRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// The set of [`Operation`] that describes the [`InternalOperation`] to execute + pub operations: Vec, + /// Required information for building a [`aptos_types::transaction::RawTransaction`] + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, + /// Public keys of those who will sign the eventual [`aptos_types::transaction::SignedTransaction`] + #[serde(skip_serializing_if = "Option::is_none")] + pub public_keys: Option>, +} + +/// Response with generated payloads to be signed +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPayloadsResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionPayloadsResponse { + /// A hex encoded, BCS encoded [`aptos_types::transaction::RawTransaction`] + /// containing the [`Operation`]s + pub unsigned_transaction: String, + /// Payloads describing who and what to sign + pub payloads: Vec, +} + +/// Request to get options for a [`ConstructionMetadataRequest`] +/// +/// This should be able to run without a running full node connection +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPreprocessRequest.html) +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct ConstructionPreprocessRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Operations that make up an `InternalOperation` + pub operations: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +/// This object holds all the possible "changes" to payloads +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct PreprocessMetadata { + /// Expiry time of the transaction in unix epoch seconds + #[serde(skip_serializing_if = "Option::is_none")] + pub expiry_time_secs: Option, + /// Sequence number to use for this transaction + #[serde(skip_serializing_if = "Option::is_none")] + pub sequence_number: Option, + /// Max gas amount for this transaction + #[serde(skip_serializing_if = "Option::is_none")] + pub max_gas_amount: Option, + /// Gas unit price for this transaction + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price: Option, + /// Public keys used for this transaction + #[serde(skip_serializing_if = "Option::is_none")] + pub public_keys: Option>, + /// Taking the estimated gas price, and multiplying it + /// times this number divided by 100 e.g. 120 is 120% + /// of the estimated price + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price_multiplier: Option, + /// Gas price priority. If the priority is low, it will + /// use a deprioritized price. If it's normal, it will use the estimated + /// price, and if it's high, it will use the prioritized price. + #[serde(skip_serializing_if = "Option::is_none")] + pub gas_price_priority: Option, +} + +/// A gas price priority for what gas price to use +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +pub enum GasPricePriority { + Low, + #[default] + Normal, + High, +} + +impl GasPricePriority { + pub fn as_str(&self) -> &'static str { + match self { + GasPricePriority::Low => "low", + GasPricePriority::Normal => "normal", + GasPricePriority::High => "high", + } + } +} + +impl Display for GasPricePriority { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for GasPricePriority { + type Err = ApiError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().trim() { + "low" => Ok(Self::Low), + "normal" => Ok(Self::Normal), + "high" => Ok(Self::High), + _ => Err(ApiError::InvalidInput(Some(format!( + "{} is an invalid gas price priority", + s + )))), + } + } +} + +impl Serialize for GasPricePriority { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_str().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for GasPricePriority { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let str = ::deserialize(deserializer)?; + Self::from_str(&str).map_err(|err| D::Error::custom(err.to_string())) + } +} + +/// Response for direct input into a [`ConstructionMetadataRequest`] +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionPreprocessResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionPreprocessResponse { + /// Metadata to be sent verbatim to the Metadata API + pub options: MetadataOptions, + /// List of who needs to be signing this transaction + pub required_public_keys: Vec, +} + +/// Request to submit a signed transaction +/// +/// A running full node is required for this API +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionSubmitRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionSubmitRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// A hex encoded, BCS encoded [`aptos_types::transaction::SignedTransaction`] + pub signed_transaction: String, +} + +/// Response containing transaction identifier of submitted transaction +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/ConstructionSubmitResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConstructionSubmitResponse { + /// Hash of the submitted [`aptos_types::transaction::SignedTransaction`] + pub transaction_identifier: TransactionIdentifier, +} + +/// Request for all transactions in mempool +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MempoolRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, +} + +/// Response of all transactions in mempool +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MempoolResponse { + /// Hash of the transactions in mempool + pub transaction_identifiers: Vec, +} + +/// Request for a transaction in mempool by hash +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolTransactionRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MempoolTransactionRequest { + /// Network identifier describing the blockchain and the chain id + pub network_identifier: NetworkIdentifier, + /// Hash of a transaction to lookup in mempool + pub transaction_identifier: TransactionIdentifier, +} + +/// Response of an estimate of the transaction in mempool +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/MempoolTransactionResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MempoolTransactionResponse { + /// The transaction in mempool + pub transaction: Transaction, +} + +/// Metadata request for a placeholder when no other fields exist +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/MetadataRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MetadataRequest {} + +/// Response of all networks that this endpoint supports +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkListResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct NetworkListResponse { + /// List of networks supported by this Rosetta instance + pub network_identifiers: Vec, +} + +/// Response with all versioning and implementation specific fields +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkOptionsResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct NetworkOptionsResponse { + /// Software versions + pub version: Version, + /// Specifics about what is allowed on this server + pub allow: Allow, +} + +/// A generic request for network APIs +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkRequest.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct NetworkRequest { + pub network_identifier: NetworkIdentifier, +} + +/// Response with information about the current network state +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/NetworkStatusResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct NetworkStatusResponse { + /// Current block identifier + pub current_block_identifier: BlockIdentifier, + /// Current block timestamp in milliseconds + pub current_block_timestamp: u64, + /// Genesis block + pub genesis_block_identifier: BlockIdentifier, + /// Oldest version that is available after pruning. Assumed to be genesis block if not present + pub oldest_block_identifier: BlockIdentifier, + /// Sync status if a node needs to catch up + #[serde(skip_serializing_if = "Option::is_none")] + pub sync_status: Option, + /// Connected peers + pub peers: Vec, +} + +/// Response with a transaction that was hashed or submitted +/// +/// [API Spec](https://www.rosetta-api.org/docs/models/TransactionIdentifierResponse.html) +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TransactionIdentifierResponse { + /// Hash of the transaction + pub transaction_identifier: TransactionIdentifier, +} From 23cb2eba38c5d9f233e15aafa74446b2a4575b9e Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 2 Jul 2024 09:03:20 +0100 Subject: [PATCH 109/147] feat(fin): update to match DbReader changes. --- Cargo.lock | 3872 ++++++++--------- aptos-move/aptos-vm/Cargo.toml | 1 + .../sharded_executor_service.rs | 11 +- .../framework/aptos-framework/doc/account.md | 120 +- .../aptos-framework/doc/aggregator_v2.md | 167 +- .../aptos-framework/doc/aptos_account.md | 492 ++- .../aptos-framework/doc/aptos_coin.md | 2 +- .../aptos-framework/doc/aptos_governance.md | 399 +- .../framework/aptos-framework/doc/block.md | 224 +- .../framework/aptos-framework/doc/coin.md | 2532 +++++++++-- .../aptos-framework/doc/config_buffer.md | 2 - .../aptos-framework/doc/consensus_config.md | 20 +- .../aptos-framework/doc/delegation_pool.md | 1392 +++++- .../framework/aptos-framework/doc/dkg.md | 5 +- .../aptos-framework/doc/execution_config.md | 19 +- .../aptos-framework/doc/fungible_asset.md | 1436 +++++- .../aptos-framework/doc/gas_schedule.md | 126 +- .../framework/aptos-framework/doc/genesis.md | 14 +- .../doc/governance_proposal.md | 2 +- .../doc/jwk_consensus_config.md | 34 +- .../framework/aptos-framework/doc/jwks.md | 35 +- .../aptos-framework/doc/keyless_account.md | 406 +- .../aptos-framework/doc/managed_coin.md | 9 +- .../aptos-framework/doc/multisig_account.md | 562 ++- .../framework/aptos-framework/doc/object.md | 404 +- .../framework/aptos-framework/doc/overview.md | 4 + .../doc/primary_fungible_store.md | 242 +- .../aptos-framework/doc/randomness.md | 45 +- .../aptos-framework/doc/randomness_config.md | 107 +- .../aptos-framework/doc/reconfiguration.md | 49 +- .../doc/reconfiguration_with_dkg.md | 53 +- .../aptos-framework/doc/resource_account.md | 10 +- .../framework/aptos-framework/doc/stake.md | 917 +++- .../aptos-framework/doc/staking_config.md | 56 +- .../aptos-framework/doc/staking_contract.md | 631 ++- .../aptos-framework/doc/storage_gas.md | 3 +- .../doc/transaction_context.md | 1013 ++++- .../aptos-framework/doc/transaction_fee.md | 130 +- .../doc/transaction_validation.md | 91 +- .../framework/aptos-framework/doc/version.md | 20 +- .../framework/aptos-framework/doc/vesting.md | 637 ++- .../framework/aptos-framework/doc/voting.md | 253 +- .../src/aptos_framework_sdk_builder.rs | 462 +- crates/aptos-logger/Cargo.toml | 3 + crates/aptos-logger/src/aptos_logger.rs | 177 +- crates/aptos-logger/src/counters.rs | 21 +- crates/aptos-logger/src/lib.rs | 5 +- .../aptos-logger/src/telemetry_log_writer.rs | 59 + crates/aptos-logger/tests/logger.rs | 5 +- .../tests/logger_custom_format.rs | 1 + .../tests/tracing_translation_tests.rs | 1 + .../storage-interface/src/finality_view.rs | 2 +- .../src/data/four_region_link_stats.csv | 13 + .../src/data/two_region_link_stats.csv | 3 + .../tests/forge-local-compatibility.rs | 23 + .../tests/forge-local-performance.rs | 34 + types/Cargo.toml | 4 +- 57 files changed, 13877 insertions(+), 3483 deletions(-) create mode 100644 crates/aptos-logger/src/telemetry_log_writer.rs create mode 100644 testsuite/testcases/src/data/four_region_link_stats.csv create mode 100644 testsuite/testcases/src/data/two_region_link_stats.csv create mode 100644 testsuite/testcases/tests/forge-local-compatibility.rs create mode 100644 testsuite/testcases/tests/forge-local-performance.rs diff --git a/Cargo.lock b/Cargo.lock index 88a6c7aeb8798..9b643f5da2ade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,11 +13,6 @@ name = "abstract-domain-derive" version = "0.1.0" dependencies = [ "move-stackless-bytecode", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", "syn 1.0.109", @@ -32,14 +27,13 @@ dependencies = [ "num-bigint 0.3.3", "num-integer", "num-traits", ->>>>>>> upstream/main ] [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -62,9 +56,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -98,39 +92,34 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -<<<<<<< HEAD -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -======= version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" ->>>>>>> upstream/main dependencies = [ "cfg-if", - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", + "serde", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -143,9 +132,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -179,70 +168,66 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] -name = "anyhow" -version = "1.0.79" +name = "antidote" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" -<<<<<<< HEAD -======= -dependencies = [ - "backtrace", -] +checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" [[package]] -name = "approx" -version = "0.5.1" +name = "anyhow" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" dependencies = [ - "num-traits", + "backtrace", ] [[package]] @@ -289,7 +274,7 @@ dependencies = [ "bcs 0.1.4", "bollard", "chrono", - "clap 4.4.14", + "clap 4.5.8", "clap_complete", "dashmap", "diesel", @@ -335,7 +320,6 @@ dependencies = [ "url", "version-compare", ] ->>>>>>> upstream/main [[package]] name = "aptos-abstract-gas-usage" @@ -361,8 +345,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-admin-service" version = "0.1.0" dependencies = [ @@ -377,7 +359,7 @@ dependencies = [ "aptos-system-utils 0.1.0", "aptos-types", "bcs 0.1.4", - "http", + "http 0.2.12", "hyper", "sha256", "tokio", @@ -385,7 +367,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-aggregator" version = "0.1.0" dependencies = [ @@ -569,7 +550,7 @@ dependencies = [ "async-trait", "bcs 0.1.4", "bytes", - "clap 4.4.12", + "clap 4.5.8", "csv", "futures", "itertools 0.12.1", @@ -577,7 +558,7 @@ dependencies = [ "move-bytecode-verifier", "num_cpus", "once_cell", - "pin-project", + "pin-project 1.1.5", "proptest", "rand 0.7.3", "regex", @@ -589,7 +570,7 @@ dependencies = [ "tokio", "tokio-io-timeout", "tokio-stream", - "tokio-util", + "tokio-util 0.7.11", "warp", ] @@ -667,7 +648,7 @@ dependencies = [ "move-vm-types", "num_cpus", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "proptest", "proptest-derive", "rand 0.7.3", @@ -685,7 +666,7 @@ dependencies = [ "aptos-metrics-core", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "criterion", "dashmap", "itertools 0.12.1", @@ -733,13 +714,9 @@ name = "aptos-cargo-cli" version = "0.1.0" dependencies = [ "anyhow", -<<<<<<< HEAD - "clap 4.4.12", -======= "camino", "chrono", - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "clap-verbosity-flag", "determinator", "env_logger", @@ -762,39 +739,19 @@ dependencies = [ ] [[package]] -name = "aptos-collections" -version = "0.1.0" +name = "aptos-cli-common" +version = "1.0.0" +dependencies = [ + "anstyle", + "clap 4.5.8", + "clap_complete", +] [[package]] -<<<<<<< HEAD -======= -name = "aptos-comparison-testing" +name = "aptos-collections" version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-framework", - "aptos-language-e2e-tests", - "aptos-rest-client", - "aptos-types", - "aptos-validator-interface", - "aptos-vm", - "bcs 0.1.4", - "clap 4.4.14", - "futures", - "itertools 0.12.1", - "move-compiler", - "move-core-types", - "move-model", - "move-package", - "rocksdb", - "serde", - "tempfile", - "tokio", - "url", -] [[package]] ->>>>>>> upstream/main name = "aptos-compression" version = "0.1.0" dependencies = [ @@ -842,8 +799,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-consensus" version = "0.1.0" dependencies = [ @@ -892,7 +847,7 @@ dependencies = [ "bytes", "chrono", "claims", - "clap 4.4.14", + "clap 4.5.8", "dashmap", "enum_dispatch", "fail", @@ -928,7 +883,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-consensus-notifications" version = "0.1.0" dependencies = [ @@ -958,14 +912,10 @@ dependencies = [ "aptos-short-hex-str", "aptos-types", "bcs 0.1.4", -<<<<<<< HEAD - "itertools 0.10.5", -======= "fail", "futures", "itertools 0.12.1", "mini-moka", ->>>>>>> upstream/main "mirai-annotations", "once_cell", "proptest", @@ -1010,15 +960,11 @@ dependencies = [ "byteorder", "bytes", "criterion", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "curve25519-dalek-ng", "digest 0.9.0", -<<<<<<< HEAD - "ed25519-dalek", -======= "ed25519-dalek 1.0.1", "ff", ->>>>>>> upstream/main "hex", "hkdf 0.10.0", "libsecp256k1", @@ -1055,13 +1001,8 @@ name = "aptos-crypto-derive" version = "0.0.3" dependencies = [ "anyhow", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -1095,7 +1036,7 @@ dependencies = [ "itertools 0.12.1", "maplit", "mockall", - "ordered-float", + "ordered-float 3.9.2", "rand 0.8.5", "serde", "thiserror", @@ -1160,12 +1101,8 @@ dependencies = [ "bcs 0.1.4", "byteorder", "claims", -<<<<<<< HEAD - "clap 4.4.12", -======= - "clap 4.4.14", + "clap 4.5.8", "crossbeam-channel", ->>>>>>> upstream/main "dashmap", "either", "hex", @@ -1241,20 +1178,12 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", -<<<<<<< HEAD - "clap 4.4.12", - "itertools 0.10.5", - "owo-colors", -======= - "clap 4.4.14", + "clap 4.5.8", "itertools 0.12.1", ->>>>>>> upstream/main "tokio", ] [[package]] -<<<<<<< HEAD -======= name = "aptos-debugger" version = "0.1.0" dependencies = [ @@ -1264,13 +1193,12 @@ dependencies = [ "aptos-logger", "aptos-move-debugger", "aptos-push-metrics", - "clap 4.4.14", + "clap 4.5.8", "jemallocator", "tokio", ] [[package]] ->>>>>>> upstream/main name = "aptos-dkg" version = "0.1.0" dependencies = [ @@ -1354,12 +1282,7 @@ name = "aptos-enum-conversion-derive" version = "0.0.3" dependencies = [ "anyhow", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "quote", ->>>>>>> upstream/main "syn 1.0.109", "trybuild", ] @@ -1476,15 +1399,10 @@ dependencies = [ "async-trait", "bcs 0.1.4", "chrono", - "clap 4.4.12", + "clap 4.5.8", "derivative", -<<<<<<< HEAD - "indicatif", - "itertools 0.10.5", -======= "indicatif 0.15.0", "itertools 0.12.1", ->>>>>>> upstream/main "jemallocator", "move-core-types", "num_cpus", @@ -1514,7 +1432,7 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "crossbeam-channel", "ctrlc", "dashmap", @@ -1594,7 +1512,7 @@ dependencies = [ "aptos-vm", "aptos-vm-logging", "aptos-vm-types", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "move-core-types", "once_cell", "rayon", @@ -1627,7 +1545,7 @@ dependencies = [ "aptos-faucet-core", "aptos-logger", "aptos-sdk", - "clap 4.4.12", + "clap 4.5.8", "tokio", ] @@ -1643,7 +1561,7 @@ dependencies = [ "aptos-sdk", "async-trait", "captcha", - "clap 4.4.12", + "clap 4.5.8", "deadpool-redis", "enum_dispatch", "futures", @@ -1684,7 +1602,7 @@ dependencies = [ "anyhow", "aptos-faucet-core", "aptos-logger", - "clap 4.4.12", + "clap 4.5.8", "tokio", ] @@ -1696,7 +1614,7 @@ dependencies = [ "aptos-logger", "aptos-node-checker", "aptos-sdk", - "clap 4.4.12", + "clap 4.5.8", "env_logger", "futures", "gcp-bigquery-client", @@ -1707,8 +1625,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-forge" version = "0.0.0" dependencies = [ @@ -1735,7 +1651,7 @@ dependencies = [ "aptos-transaction-generator-lib", "async-trait", "chrono", - "clap 4.4.14", + "clap 4.5.8", "either", "futures", "hex", @@ -1775,7 +1691,7 @@ dependencies = [ "aptos-testcases", "async-trait", "chrono", - "clap 4.4.14", + "clap 4.5.8", "futures", "jemallocator", "once_cell", @@ -1788,7 +1704,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-framework" version = "0.1.0" dependencies = [ @@ -1816,7 +1731,7 @@ dependencies = [ "bulletproofs", "byteorder", "claims", - "clap 4.4.12", + "clap 4.5.8", "codespan-reporting", "curve25519-dalek-ng", "either", @@ -1868,31 +1783,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= -name = "aptos-gas-calibration" -version = "0.1.0" -dependencies = [ - "anyhow", - "aptos-abstract-gas-usage", - "aptos-cached-packages", - "aptos-framework", - "aptos-gas-algebra", - "aptos-gas-schedule", - "aptos-language-e2e-tests", - "aptos-types", - "bcs 0.1.4", - "clap 4.4.14", - "float-cmp", - "move-binary-format", - "move-core-types", - "move-ir-compiler", - "nalgebra", - "walkdir", -] - -[[package]] ->>>>>>> upstream/main name = "aptos-gas-meter" version = "0.1.0" dependencies = [ @@ -1947,7 +1837,7 @@ dependencies = [ "aptos-package-builder", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "move-core-types", "move-model", "tempfile", @@ -2017,10 +1907,6 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", -<<<<<<< HEAD - "clap 4.4.12", -======= ->>>>>>> upstream/main "diesel", "diesel_migrations", "field_count", @@ -2034,45 +1920,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "aptos-indexer-grpc-data-access" -version = "1.0.0" -dependencies = [ - "anyhow", - "aptos-metrics-core", - "aptos-protos", - "async-trait", - "backoff", - "backtrace", - "base64 0.13.1", - "bytes", - "clap 4.4.12", - "cloud-storage", - "dashmap", - "enum_dispatch", - "futures", - "futures-core", - "futures-util", - "google-cloud-storage", - "itertools 0.10.5", - "once_cell", - "prometheus", - "prost 0.12.3", - "redis", - "redis-test", - "serde", - "serde_json", - "serde_yaml 0.8.26", - "tempfile", - "thiserror", - "tokio", - "tokio-util", - "toml 0.7.8", - "tonic 0.11.0", - "tracing", - "tracing-subscriber 0.3.18", - "warp", -======= name = "aptos-indexer-grpc-cache-worker" version = "1.0.0" dependencies = [ @@ -2084,12 +1931,12 @@ dependencies = [ "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "aptos-protos 1.3.0", "async-trait", - "clap 4.4.14", + "clap 4.5.8", "futures", "futures-core", "jemallocator", "once_cell", - "prost 0.12.3", + "prost 0.12.6", "redis", "reqwest", "serde", @@ -2112,11 +1959,11 @@ dependencies = [ "aptos-protos 1.3.0", "aptos-transaction-filter", "async-trait", - "clap 4.4.14", + "clap 4.5.8", "futures", "jemallocator", "once_cell", - "prost 0.12.3", + "prost 0.12.6", "redis", "serde", "serde_json", @@ -2138,7 +1985,7 @@ dependencies = [ "aptos-metrics-core", "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "async-trait", - "clap 4.4.14", + "clap 4.5.8", "futures", "jemallocator", "once_cell", @@ -2146,7 +1993,6 @@ dependencies = [ "serde", "tokio", "tracing", ->>>>>>> upstream/main ] [[package]] @@ -2171,9 +2017,9 @@ dependencies = [ "aptos-mempool", "aptos-mempool-notifications", "aptos-metrics-core", - "aptos-moving-average", + "aptos-moving-average 0.1.0 (git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=4801acae7aea30d7e96bbfbe5ec5b04056dfa4cf)", "aptos-proptest-helpers", - "aptos-protos", + "aptos-protos 1.3.0", "aptos-runtimes", "aptos-sdk", "aptos-secure-storage", @@ -2205,8 +2051,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-indexer-grpc-in-memory-cache-benchmark" version = "0.1.0" dependencies = [ @@ -2255,7 +2099,7 @@ dependencies = [ "aptos-system-utils 0.1.0", "async-trait", "backtrace", - "clap 4.4.14", + "clap 4.5.8", "prometheus", "serde", "serde_yaml 0.8.26", @@ -2268,7 +2112,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-indexer-grpc-table-info" version = "1.0.0" dependencies = [ @@ -2281,12 +2124,6 @@ dependencies = [ "aptos-indexer-grpc-utils", "aptos-logger", "aptos-mempool", -<<<<<<< HEAD - "aptos-metrics-core", - "aptos-protos", - "aptos-rocksdb-options", -======= ->>>>>>> upstream/main "aptos-runtimes", "aptos-schemadb", "aptos-storage-interface", @@ -2302,7 +2139,7 @@ dependencies = [ "tempfile", "tokio", "tokio-stream", - "tokio-util", + "tokio-util 0.7.11", "tonic 0.11.0", ] @@ -2312,15 +2149,11 @@ version = "1.0.0" dependencies = [ "anyhow", "aptos-metrics-core", - "aptos-protos", + "aptos-protos 1.3.0", "async-trait", "backoff", "base64 0.13.1", "chrono", -<<<<<<< HEAD - "clap 4.4.12", -======= ->>>>>>> upstream/main "cloud-storage", "dashmap", "futures", @@ -2328,14 +2161,14 @@ dependencies = [ "lz4", "once_cell", "prometheus", - "prost 0.12.3", + "prost 0.12.6", "redis", "redis-test", "ripemd", "serde", "serde_json", "tokio", - "tokio-util 0.7.10", + "tokio-util 0.7.11", "tonic 0.11.0", "tracing", "url", @@ -2346,8 +2179,6 @@ name = "aptos-infallible" version = "0.1.0" [[package]] -<<<<<<< HEAD -======= name = "aptos-inspection-service" version = "0.1.0" dependencies = [ @@ -2375,7 +2206,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-jellyfish-merkle" version = "0.1.0" dependencies = [ @@ -2445,7 +2275,7 @@ version = "0.1.0" dependencies = [ "anyhow", "aptos-types", - "http", + "http 0.2.12", "move-core-types", "reqwest", "serde", @@ -2519,16 +2349,9 @@ dependencies = [ "ark-serialize", "ark-std", "bcs 0.1.4", -<<<<<<< HEAD - "byteorder", - "curve25519-dalek", - "ff 0.13.0", - "group 0.13.0", -======= "blstrs", "derivation-path", "ed25519-dalek 1.0.1", ->>>>>>> upstream/main "hex", "hmac 0.12.1", "jsonwebtoken 8.3.0", @@ -2649,13 +2472,8 @@ dependencies = [ name = "aptos-log-derive" version = "0.1.0" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -2665,10 +2483,12 @@ version = "0.1.0" dependencies = [ "aptos-infallible", "aptos-log-derive", + "aptos-node-identity", "backtrace", "chrono", "console-subscriber", "erased-serde", + "futures", "hostname", "once_cell", "pretty_assertions", @@ -2677,6 +2497,7 @@ dependencies = [ "serde_json", "strum 0.24.1", "strum_macros 0.24.3", + "tokio", "tracing", "tracing-subscriber 0.3.18", ] @@ -2770,8 +2591,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-move-debugger" version = "0.1.0" dependencies = [ @@ -2788,7 +2607,7 @@ dependencies = [ "aptos-vm-logging", "aptos-vm-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.5.8", "itertools 0.12.1", "regex", "reqwest", @@ -2797,7 +2616,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-move-e2e-benchmark" version = "0.1.0" dependencies = [ @@ -2817,7 +2635,7 @@ dependencies = [ "aptos-gas-schedule", "aptos-types", "aptos-vm", - "clap 4.4.12", + "clap 4.5.8", "move-cli", "move-package", "move-prover", @@ -2855,8 +2673,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-moving-average" version = "0.1.0" source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" @@ -2865,7 +2681,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-mvhashmap" version = "0.1.0" dependencies = [ @@ -2914,10 +2729,10 @@ dependencies = [ "aptos-types", "bytes", "futures", - "pin-project", + "pin-project 1.1.5", "serde", "tokio", - "tokio-util", + "tokio-util 0.7.11", "url", ] @@ -2953,8 +2768,8 @@ dependencies = [ "itertools 0.12.1", "maplit", "once_cell", - "ordered-float", - "pin-project", + "ordered-float 3.9.2", + "pin-project 1.1.5", "proptest", "proptest-derive", "rand 0.7.3", @@ -2968,12 +2783,8 @@ dependencies = [ "thiserror", "tokio", "tokio-retry", -<<<<<<< HEAD - "tokio-util", -======= "tokio-stream", - "tokio-util 0.7.10", ->>>>>>> upstream/main + "tokio-util 0.7.11", ] [[package]] @@ -3027,7 +2838,7 @@ dependencies = [ "aptos-logger", "aptos-network", "aptos-types", - "clap 4.4.12", + "clap 4.5.8", "futures", "serde", "tokio", @@ -3061,8 +2872,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-nft-metadata-crawler-parser" version = "0.1.0" dependencies = [ @@ -3073,7 +2882,7 @@ dependencies = [ "backoff", "bytes", "chrono", - "clap 4.4.14", + "clap 4.5.8", "diesel", "diesel_migrations", "field_count", @@ -3149,7 +2958,7 @@ dependencies = [ "aptos-validator-transaction-pool", "aptos-vm", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.5.8", "either", "fail", "futures", @@ -3167,7 +2976,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-node-checker" version = "0.1.1" dependencies = [ @@ -3180,13 +2988,7 @@ dependencies = [ "aptos-sdk", "aptos-transaction-emitter-lib", "async-trait", -<<<<<<< HEAD - "clap 4.4.12", - "const_format", - "env_logger", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "futures", "once_cell", "poem", @@ -3230,13 +3032,8 @@ dependencies = [ name = "aptos-num-variants" version = "0.1.0" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -3262,7 +3059,7 @@ dependencies = [ "aptos-mempool", "aptos-storage-interface", "aptos-types", - "clap 4.4.12", + "clap 4.5.8", ] [[package]] @@ -3389,27 +3186,24 @@ version = "1.3.0" dependencies = [ "futures-core", "pbjson", - "prost 0.12.3", + "prost 0.12.6", "serde", "tonic 0.11.0", ] [[package]] -<<<<<<< HEAD -======= name = "aptos-protos" version = "1.3.0" source = "git+https://github.com/aptos-labs/aptos-core.git?tag=aptos-node-v1.12.1#4b9a2593facaee92b28df2e99b2773a7e4f930f5" dependencies = [ "futures-core", "pbjson", - "prost 0.12.3", + "prost 0.12.6", "serde", "tonic 0.11.0", ] [[package]] ->>>>>>> upstream/main name = "aptos-proxy" version = "0.1.0" dependencies = [ @@ -3434,12 +3228,9 @@ dependencies = [ "aptos-logger", "aptos-metrics-core", "futures", - "pin-project", + "pin-project 1.1.5", "tokio", -<<<<<<< HEAD - "tokio-util", -======= - "tokio-util 0.7.10", + "tokio-util 0.7.11", ] [[package]] @@ -3461,7 +3252,7 @@ dependencies = [ "aptos-temppath", "aptos-types", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.5.8", "futures", "git2 0.16.1", "handlebars", @@ -3478,7 +3269,6 @@ dependencies = [ "strum_macros 0.24.3", "tokio", "url", ->>>>>>> upstream/main ] [[package]] @@ -3527,12 +3317,7 @@ dependencies = [ "aptos-types", "bcs 0.1.4", "bytes", -<<<<<<< HEAD - "clap 4.4.12", - "futures", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "hex", "move-core-types", "reqwest", @@ -3560,8 +3345,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-rosetta" version = "0.0.1" dependencies = [ @@ -3578,7 +3361,7 @@ dependencies = [ "aptos-types", "aptos-warp-webserver", "bcs 0.1.4", - "clap 4.4.14", + "clap 4.5.8", "futures", "hex", "itertools 0.12.1", @@ -3602,7 +3385,7 @@ dependencies = [ "aptos-logger", "aptos-rosetta", "aptos-types", - "clap 4.4.14", + "clap 4.5.8", "serde", "serde_json", "tokio", @@ -3610,7 +3393,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-runtimes" version = "0.1.0" dependencies = [ @@ -3720,7 +3502,7 @@ dependencies = [ "aptos-framework", "aptos-types", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "heck 0.4.1", "move-core-types", "once_cell", @@ -3739,12 +3521,7 @@ dependencies = [ "aptos-config", "aptos-logger", "aptos-metrics-core", -<<<<<<< HEAD - "aptos-protos", - "aptos-retrier", -======= "aptos-protos 1.3.0", ->>>>>>> upstream/main "bcs 0.1.4", "crossbeam-channel", "once_cell", @@ -3796,7 +3573,7 @@ dependencies = [ "aptos-infallible", "claims", "crossbeam", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "proptest", "proptest-derive", "rayon", @@ -3869,7 +3646,7 @@ dependencies = [ "crossbeam-channel", "dashmap", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "proptest", "proptest-derive", "rayon", @@ -3962,7 +3739,7 @@ dependencies = [ "anyhow", "aptos-profiler 0.1.0", "async-mutex", - "http", + "http 0.2.12", "hyper", "lazy_static", "mime", @@ -3982,7 +3759,7 @@ dependencies = [ "anyhow", "aptos-profiler 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)", "async-mutex", - "http", + "http 0.2.12", "hyper", "lazy_static", "mime", @@ -4012,8 +3789,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-telemetry" version = "0.1.0" dependencies = [ @@ -4068,7 +3843,7 @@ dependencies = [ "bcs 0.1.4", "chrono", "claims", - "clap 4.4.14", + "clap 4.5.8", "debug-ignore", "flate2", "futures", @@ -4094,7 +3869,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-temppath" version = "0.1.0" dependencies = [ @@ -4103,8 +3877,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "aptos-testcases" version = "0.0.0" dependencies = [ @@ -4135,14 +3907,13 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "aptos-time-service" version = "0.1.0" dependencies = [ "aptos-infallible", "enum_dispatch", "futures", - "pin-project", + "pin-project 1.1.5", "thiserror", "tokio", "tokio-test", @@ -4164,7 +3935,7 @@ dependencies = [ "aptos-types", "aptos-vm", "aptos-vm-logging", - "clap 4.4.12", + "clap 4.5.8", "criterion", "criterion-cpu-time", "num_cpus", @@ -4180,7 +3951,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "aptos-transaction-emitter-lib", - "clap 4.4.12", + "clap 4.5.8", "futures", "rand 0.7.3", "tokio", @@ -4202,7 +3973,7 @@ dependencies = [ "aptos-transaction-generator-lib", "aptos-types", "async-trait", - "clap 4.4.12", + "clap 4.5.8", "futures", "itertools 0.12.1", "once_cell", @@ -4222,7 +3993,7 @@ dependencies = [ "aptos-protos 1.3.0", "derive_builder", "lz4", - "prost 0.12.3", + "prost 0.12.6", "serde", "serde_json", "serde_yaml 0.8.26", @@ -4239,7 +4010,7 @@ dependencies = [ "aptos-logger", "aptos-sdk", "async-trait", - "clap 4.4.12", + "clap 4.5.8", "move-binary-format", "once_cell", "rand 0.7.3", @@ -4264,12 +4035,8 @@ dependencies = [ "aptos-vm", "aptos-vm-genesis", "bcs 0.1.4", -<<<<<<< HEAD - "clap 4.4.12", -======= - "clap 4.4.14", + "clap 4.5.8", "codespan-reporting", ->>>>>>> upstream/main "datatest-stable", "hex", "move-binary-format", @@ -4316,7 +4083,7 @@ dependencies = [ "derivative", "fixed", "fxhash", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "hex", "itertools 0.12.1", "jsonwebtoken 8.3.0", @@ -4447,12 +4214,8 @@ dependencies = [ "claims", "crossbeam-channel", "derive_more", -<<<<<<< HEAD - "fail 0.5.1", -======= "fail", "futures", ->>>>>>> upstream/main "hex", "move-binary-format", "move-core-types", @@ -4470,23 +4233,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= -name = "aptos-vm-benchmarks" -version = "0.1.0" -dependencies = [ - "aptos-cached-packages", - "aptos-framework", - "aptos-language-e2e-tests", - "aptos-types", - "bcs 0.1.4", - "clap 4.4.14", - "move-binary-format", - "move-core-types", -] - -[[package]] ->>>>>>> upstream/main name = "aptos-vm-genesis" version = "0.1.0" dependencies = [ @@ -4537,7 +4283,7 @@ dependencies = [ "aptos-types", "aptos-vm", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "glob", "move-binary-format", "move-core-types", @@ -4634,9 +4380,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ark-bls12-381" @@ -4711,7 +4457,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "paste", "rayon", @@ -4735,15 +4481,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -4798,7 +4539,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint 0.4.4", + "num-bigint 0.4.6", ] [[package]] @@ -4807,13 +4548,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -4908,6 +4644,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "assert_approx_eq" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd" + [[package]] name = "assert_unordered" version = "0.3.5" @@ -4930,28 +4672,39 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 4.0.2", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ - "async-lock 3.2.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.1.0", + "fastrand 2.1.0", + "futures-lite 2.3.0", "slab", ] @@ -4961,12 +4714,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.1.1", + "async-channel 2.3.1", "async-executor", - "async-io 2.2.2", - "async-lock 3.2.0", + "async-io 2.3.3", + "async-lock 3.4.0", "blocking", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "once_cell", ] @@ -4992,18 +4745,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.2.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "parking", - "polling 3.3.1", - "rustix 0.38.28", + "polling 3.7.2", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -5020,15 +4773,24 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.2", + "event-listener 5.3.1", "event-listener-strategy", "pin-project-lite", ] +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "async-object-pool" version = "0.1.4" @@ -5051,43 +4813,37 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.28", + "rustix 0.38.34", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ - "async-io 2.2.2", - "async-lock 2.8.0", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.28", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5134,38 +4890,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "async-task" -version = "4.6.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -5192,33 +4936,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -5231,7 +4958,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -5246,7 +4973,7 @@ dependencies = [ "sync_wrapper", "tokio", "tower", - "tower-http", + "tower-http 0.3.5", "tower-layer", "tower-service", ] @@ -5262,7 +4989,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -5288,7 +5015,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "mime", "tower-layer", @@ -5304,7 +5031,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "mime", "rustversion", @@ -5333,7 +5060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.11", + "getrandom 0.2.15", "instant", "pin-project-lite", "rand 0.8.5", @@ -5342,9 +5069,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -5375,28 +5102,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64" -version = "0.22.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -<<<<<<< HEAD -checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569" -dependencies = [ - "base64 0.21.5", -] -======= checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" ->>>>>>> upstream/main [[package]] name = "base64ct" @@ -5406,9 +5120,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-cookies" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" +checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" dependencies = [ "lalrpop", "lalrpop-util", @@ -5416,12 +5130,15 @@ dependencies = [ ] [[package]] -name = "basic-toml" -version = "0.1.8" +name = "bb8" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188" dependencies = [ - "serde", + "async-trait", + "futures-util", + "parking_lot 0.12.3", + "tokio", ] [[package]] @@ -5482,25 +5199,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] name = "bigdecimal" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" dependencies = [ "autocfg", "libm", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", "serde", @@ -5521,25 +5233,18 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", -<<<<<<< HEAD - "peeking_take_while", - "prettyplease", - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "regex", "rustc-hash", "shlex", - "syn 2.0.47", + "syn 2.0.68", ] [[package]] @@ -5557,6 +5262,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -5565,9 +5276,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmaps" @@ -5691,25 +5402,22 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel 2.1.1", - "async-lock 3.2.0", + "async-channel 2.3.1", "async-task", - "fastrand 2.0.1", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -5733,6 +5441,46 @@ dependencies = [ "subtle", ] +[[package]] +name = "bollard" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03db470b3c0213c47e978da93200259a1eb4dae2e5512cba9955e2b540a6fc6" +dependencies = [ + "base64 0.21.7", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "http 0.2.12", + "hyper", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util 0.7.11", + "url", + "winapi 0.3.9", +] + +[[package]] +name = "bollard-stubs" +version = "1.43.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58071e8fd9ec1e930efd28e3a90c1251015872a2ce49f81f36421b86466932e" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + [[package]] name = "bstr" version = "0.2.17" @@ -5746,9 +5494,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -5776,9 +5524,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -5827,15 +5575,15 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" [[package]] name = "byteorder" @@ -5845,9 +5593,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -5874,7 +5622,7 @@ name = "calc-dep-sizes" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.5.8", "futures", "move-binary-format", "move-core-types", @@ -5884,9 +5632,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -5907,9 +5655,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -5961,12 +5709,13 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -5980,9 +5729,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -5995,10 +5744,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "chrono" -version = "0.4.31" +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -6006,14 +5761,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "chrono-tz" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" dependencies = [ "chrono", "chrono-tz-build", @@ -6022,9 +5777,9 @@ dependencies = [ [[package]] name = "chrono-tz-build" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" dependencies = [ "parse-zoneinfo", "phf", @@ -6039,9 +5794,9 @@ checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -6050,18 +5805,18 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 1.8.2", + "half 2.4.1", ] [[package]] @@ -6085,9 +5840,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -6123,39 +5878,48 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.0", + "textwrap 0.16.1", ] [[package]] name = "clap" -version = "4.4.12" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", - "clap_derive 4.4.7", + "clap_derive 4.5.8", ] [[package]] name = "clap-verbosity-flag" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" +checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" dependencies = [ - "clap 4.4.12", + "clap 4.5.8", "log", ] [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", - "clap_lex 0.6.0", - "strsim 0.10.0", + "clap_lex 0.7.1", + "strsim 0.11.1", +] + +[[package]] +name = "clap_complete" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d598e88f6874d4b888ed40c71efbcbf4076f1dfbae128a08a8c9e45f710605d" +dependencies = [ + "clap 4.5.8", ] [[package]] @@ -6166,32 +5930,21 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ - "heck 0.4.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -6205,9 +5958,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clear_on_drop" @@ -6271,9 +6024,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colored" @@ -6287,38 +6040,38 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "futures-core", "memchr", "pin-project-lite", "tokio", - "tokio-util", + "tokio-util 0.7.11", ] [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -6328,8 +6081,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" dependencies = [ "futures-core", - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.6", + "prost-types 0.12.6", "tonic 0.11.0", "tracing-core", ] @@ -6346,8 +6099,8 @@ dependencies = [ "futures-task", "hdrhistogram", "humantime", - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.6", + "prost-types 0.12.6", "serde", "serde_json", "thread_local", @@ -6373,9 +6126,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_fn" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" [[package]] name = "const_format" @@ -6392,15 +6145,9 @@ version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "unicode-xid 0.2.4", -======= "proc-macro2", "quote", "unicode-xid", ->>>>>>> upstream/main ] [[package]] @@ -6421,17 +6168,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - [[package]] name = "cookie" version = "0.17.0" @@ -6439,7 +6175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ "aes-gcm", - "base64 0.21.5", + "base64 0.21.7", "hkdf 0.12.4", "hmac 0.12.1", "percent-encoding", @@ -6452,12 +6188,12 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.16.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ - "cookie 0.16.2", - "idna 0.2.3", + "cookie", + "idna 0.3.0", "log", "publicsuffix", "serde", @@ -6496,9 +6232,9 @@ dependencies = [ [[package]] name = "coset" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c214bbc5c8b4518856d79cae4d323feaa881ecf3e31b5af6572bb5313c11d5" +checksum = "ff8aad850c1f86daa47e812913051eb5a26c4d9fb4242a89178bf99b946e4e3c" dependencies = [ "ciborium", "ciborium-io", @@ -6515,18 +6251,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -6579,11 +6315,10 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -6593,54 +6328,46 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -6652,7 +6379,7 @@ dependencies = [ "crossterm_winapi", "libc", "mio", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "signal-hook", "signal-hook-mio", "winapi 0.3.9", @@ -6668,7 +6395,7 @@ dependencies = [ "crossterm_winapi", "libc", "mio", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "signal-hook", "signal-hook-mio", "winapi 0.3.9", @@ -6774,34 +6501,34 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", + "nix 0.28.0", "windows-sys 0.52.0", ] [[package]] name = "curl" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.4.10", - "winapi 0.3.9", + "socket2 0.5.7", + "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.70+curl-8.5.0" +version = "0.4.73+curl-8.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" +checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d" dependencies = [ "cc", "libc", @@ -6810,7 +6537,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6827,19 +6554,16 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -6853,11 +6577,10 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] ->>>>>>> upstream/main name = "curve25519-dalek-ng" version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6871,6 +6594,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.14.4" @@ -6893,8 +6626,6 @@ dependencies = [ [[package]] name = "darling_core" -<<<<<<< HEAD -======= version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" @@ -6909,20 +6640,14 @@ dependencies = [ [[package]] name = "darling_core" ->>>>>>> upstream/main version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "strsim 0.10.0", "syn 1.0.109", ] @@ -6935,16 +6660,10 @@ checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "strsim 0.10.0", - "syn 2.0.47", -======= "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] @@ -6956,7 +6675,6 @@ dependencies = [ "darling_core 0.13.4", "quote", "syn 1.0.109", ->>>>>>> upstream/main ] [[package]] @@ -6976,15 +6694,9 @@ version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ -<<<<<<< HEAD - "darling_core 0.20.3", - "quote 1.0.35", - "syn 2.0.47", -======= "darling_core 0.20.9", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -6994,17 +6706,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "datatest-stable" @@ -7042,9 +6754,9 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" dependencies = [ "tokio", ] @@ -7054,6 +6766,9 @@ name = "debug-ignore" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe7ed1d93f4553003e20b629abe9085e1e81b1429520f897f8f8860bc6dfc21" +dependencies = [ + "serde", +] [[package]] name = "debugid" @@ -7077,9 +6792,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid 0.9.6", "pem-rfc7468 0.7.0", @@ -7108,13 +6823,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -7124,14 +6834,9 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] @@ -7152,7 +6857,7 @@ dependencies = [ "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] @@ -7162,26 +6867,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "rustc_version", - "syn 1.0.109", + "syn 2.0.68", ] [[package]] @@ -7195,7 +6894,7 @@ dependencies = [ "guppy", "guppy-workspace-hack", "once_cell", - "petgraph 0.6.4", + "petgraph 0.6.5", "rayon", "serde", "toml 0.5.11", @@ -7203,9 +6902,9 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.4.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" [[package]] name = "diesel" @@ -7214,12 +6913,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98235fdc2f355d330a8244184ab6b4b33c28679c0b4158f63138e51d6cf7e88" dependencies = [ "bigdecimal", - "bitflags 2.4.1", + "bitflags 2.6.0", "byteorder", "chrono", "diesel_derives", "itoa", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", "pq-sys", @@ -7228,8 +6927,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "diesel-async" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7270,22 +6967,15 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "diesel_derives" -version = "2.1.2" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" dependencies = [ "diesel_table_macro_syntax", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -7305,7 +6995,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.47", + "syn 2.0.68", ] [[package]] @@ -7374,6 +7064,15 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -7384,6 +7083,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -7420,8 +7131,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] -<<<<<<< HEAD -======= name = "dw" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7449,7 +7158,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] ->>>>>>> upstream/main name = "e2e-move-tests" version = "0.1.0" dependencies = [ @@ -7490,7 +7198,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.8", + "der 0.7.9", "digest 0.10.7", "elliptic-curve", "rfc6979", @@ -7508,14 +7216,24 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 3.2.0", + "ed25519 1.5.3", "rand 0.7.3", "serde", "serde_bytes", @@ -7523,6 +7241,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "serde", + "sha2 0.10.8", + "signature 2.2.0", + "subtle", + "zeroize", +] + [[package]] name = "ed25519-dalek-bip32" version = "0.2.0" @@ -7530,16 +7263,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" dependencies = [ "derivation-path", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "hmac 0.12.1", "sha2 0.10.8", ] [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -7566,9 +7299,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -7587,36 +7320,30 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -7648,9 +7375,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -7782,9 +7509,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.2" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -7793,11 +7520,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 4.0.2", + "event-listener 5.3.1", "pin-project-lite", ] @@ -7874,33 +7601,14 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "evm-runtime" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" -dependencies = [ - "auto_impl 1.1.0", - "environmental", - "evm-core 0.41.0", - "primitive-types 0.12.2", - "sha3 0.10.8", -] - -[[package]] -name = "fail" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" -======= name = "exr" -version = "1.71.0" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", - "half 2.2.1", + "half 2.4.1", "lebe", "miniz_oxide", "rayon-core", @@ -7911,11 +7619,10 @@ dependencies = [ [[package]] name = "extract-ethereum-abi" version = "0.1.0" ->>>>>>> upstream/main dependencies = [ "anyhow", "atty", - "clap 4.4.14", + "clap 4.5.8", "codespan-reporting", "move-to-yul", "serde_json", @@ -7932,6 +7639,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fallible_collections" version = "0.4.9" @@ -7952,15 +7665,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fdeflate" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -7979,8 +7692,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "ff_derive" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7998,12 +7709,11 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] ->>>>>>> upstream/main name = "field_count" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -8060,7 +7770,7 @@ checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048" dependencies = [ "az", "bytemuck", - "half 2.2.1", + "half 2.4.1", "typenum", ] @@ -8102,9 +7812,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -8135,6 +7845,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -8147,9 +7866,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ -<<<<<<< HEAD - "foreign-types-shared", -======= "foreign-types-shared 0.1.1", ] @@ -8171,8 +7887,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -8181,6 +7896,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -8279,11 +8000,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-core", "futures-io", "parking", @@ -8296,15 +8017,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -8321,9 +8036,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -8382,6 +8097,49 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "gcemeta" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d460327b24cc34c86d53d60a90e9e6044817f7906ebd9baa5c3d0ee13e1ecf" +dependencies = [ + "bytes", + "hyper", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "gcloud-sdk" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a24376e7850e7864bb326debc5765a1dda4fc47603c22e2bc0ebf30ff59141b" +dependencies = [ + "async-trait", + "chrono", + "futures", + "gcemeta", + "hyper", + "jsonwebtoken 8.3.0", + "once_cell", + "prost 0.11.9", + "prost-types 0.11.9", + "reqwest", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic 0.9.2", + "tower", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "gcp-bigquery-client" version = "0.16.8" @@ -8462,9 +8220,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -8475,19 +8233,29 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", "polyval", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "git2" @@ -8502,6 +8270,21 @@ dependencies = [ "url", ] +[[package]] +name = "git2" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "glob" version = "0.3.1" @@ -8515,19 +8298,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", - "bstr 1.9.0", + "bstr 1.9.1", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] name = "globwalk" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "ignore", "walkdir", ] @@ -8546,14 +8329,14 @@ dependencies = [ [[package]] name = "goldenfile" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a67453a3b358bd8213aedafd4feed75eecab9fb04bed26ba6fdf94694be560" +checksum = "a0d5c44265baec620ea19c97b4ce9f068e28f8c3d7faccc483f02968b5e3c587" dependencies = [ "scopeguard", "similar-asserts", "tempfile", - "yansi 1.0.0-rc.1", + "yansi 1.0.1", ] [[package]] @@ -8563,7 +8346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "931bedb2264cb00f914b0a6a5c304e34865c34306632d3932e0951a073e4a67d" dependencies = [ "async-trait", - "base64 0.21.5", + "base64 0.21.7", "google-cloud-metadata", "google-cloud-token", "home", @@ -8579,27 +8362,73 @@ dependencies = [ ] [[package]] -name = "google-cloud-metadata" -version = "0.3.2" +name = "google-cloud-gax" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +checksum = "f8bdaaa4bc036e8318274d1b25f0f2265b3e95418b765fd1ea1c7ef938fd69bd" dependencies = [ - "reqwest", + "google-cloud-token", + "http 0.2.12", "thiserror", "tokio", + "tokio-retry", + "tonic 0.9.2", + "tower", + "tracing", ] [[package]] -name = "google-cloud-storage" -version = "0.13.1" +name = "google-cloud-googleapis" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22c57ca1d971d7c6f852c02eda4e87e88b1247b6ed8be9fa5b2768c68b0f2ca5" +checksum = "2a3b24a3f57be08afc02344e693afb55e48172c9c2ab86ff3fdb8efff550e4b9" dependencies = [ - "async-stream", - "base64 0.21.5", - "bytes", - "futures-util", - "google-cloud-auth", + "prost 0.11.9", + "prost-types 0.11.9", + "tonic 0.9.2", +] + +[[package]] +name = "google-cloud-metadata" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +dependencies = [ + "reqwest", + "thiserror", + "tokio", +] + +[[package]] +name = "google-cloud-pubsub" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095b104502b6e1abbad9b9768af944b9202e032dbc7f0947d3c30d4191761071" +dependencies = [ + "async-channel 1.9.0", + "async-stream", + "google-cloud-auth", + "google-cloud-gax", + "google-cloud-googleapis", + "google-cloud-token", + "prost-types 0.11.9", + "thiserror", + "tokio", + "tokio-util 0.7.11", + "tracing", +] + +[[package]] +name = "google-cloud-storage" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22c57ca1d971d7c6f852c02eda4e87e88b1247b6ed8be9fa5b2768c68b0f2ca5" +dependencies = [ + "async-stream", + "base64 0.21.7", + "bytes", + "futures-util", + "google-cloud-auth", "google-cloud-metadata", "google-cloud-token", "hex", @@ -8647,11 +8476,7 @@ version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34e99a7734579b834a076ef11789783c153c6eb5fb3520ed15bc41f483f0f317" dependencies = [ -<<<<<<< HEAD - "ahash 0.8.7", -======= "ahash 0.8.11", ->>>>>>> upstream/main "camino", "cargo_metadata 0.18.1", "cfg-if", @@ -8659,12 +8484,12 @@ dependencies = [ "fixedbitset 0.4.2", "guppy-summaries", "guppy-workspace-hack", - "indexmap 2.2.5", + "indexmap 2.2.6", "itertools 0.12.1", "nested", "once_cell", "pathdiff", - "petgraph 0.6.4", + "petgraph 0.6.5", "rayon", "semver", "serde", @@ -8707,26 +8532,27 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.5", + "http 0.2.12", + "indexmap 2.2.6", "slab", "tokio", - "tokio-util", + "tokio-util 0.7.11", "tracing", ] [[package]] name = "half" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ + "cfg-if", "crunchy", ] @@ -8765,7 +8591,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -8774,24 +8600,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ -<<<<<<< HEAD - "ahash 0.8.7", -======= "ahash 0.8.11", ->>>>>>> upstream/main ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ -<<<<<<< HEAD - "ahash 0.8.7", -======= "ahash 0.8.11", ->>>>>>> upstream/main "allocator-api2", ] @@ -8801,7 +8619,7 @@ version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "byteorder", "flate2", "nom", @@ -8814,10 +8632,10 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "headers-core", - "http", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -8829,7 +8647,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.12", ] [[package]] @@ -8864,9 +8682,15 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -8982,9 +8806,20 @@ checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -8998,7 +8833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] @@ -9010,9 +8845,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -9029,7 +8864,7 @@ dependencies = [ "assert-json-diff", "async-object-pool", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "basic-cookies", "crossbeam-utils", "form_urlencoded", @@ -9065,22 +8900,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -9093,7 +8928,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http", + "http 0.2.12", "hyper", "log", "rustls 0.20.9", @@ -9109,10 +8944,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.12", "hyper", "log", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", @@ -9143,11 +8978,24 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project 1.1.5", + "tokio", +] + [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -9172,17 +9020,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -9205,15 +9042,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "same-file", "walkdir", "winapi-util", @@ -9235,16 +9072,20 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", - "num-rational 0.4.1", + "exr", + "gif", + "jpeg-decoder", "num-traits", "png", + "qoi", + "tiff", ] [[package]] @@ -9262,7 +9103,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.6.9", + "parity-scale-codec 3.6.11", ] [[package]] @@ -9298,13 +9139,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -9327,30 +9163,12 @@ checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" dependencies = [ "anyhow", "proc-macro-hack", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] -<<<<<<< HEAD -name = "include_dir_macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" -dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", -] - -[[package]] -======= ->>>>>>> upstream/main name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9363,12 +9181,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] @@ -9380,10 +9198,23 @@ checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ "console", "lazy_static", - "number_prefix", + "number_prefix 0.3.0", "regex", ] +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix 0.4.0", + "portable-atomic", + "unicode-width", +] + [[package]] name = "indoc" version = "1.0.9" @@ -9396,18 +9227,13 @@ version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ -<<<<<<< HEAD - "ahash 0.8.7", - "clap 4.4.12", -======= "ahash 0.8.11", - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "crossbeam-channel", "crossbeam-utils", "dashmap", "env_logger", - "indexmap 2.2.5", + "indexmap 2.2.6", "is-terminal", "itoa", "log", @@ -9429,9 +9255,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -9445,11 +9271,11 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab388864246d58a276e60e7569a833d9cc4cd75c66e5ca77c177dad38e59996" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "dashmap", "hashbrown 0.12.3", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", ] [[package]] @@ -9467,7 +9293,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -9489,12 +9315,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.3", - "rustix 0.38.28", + "hermit-abi 0.3.9", + "libc", "windows-sys 0.52.0", ] @@ -9504,6 +9330,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "isahc" version = "1.7.2" @@ -9518,7 +9350,7 @@ dependencies = [ "encoding_rs", "event-listener 2.5.3", "futures-lite 1.13.0", - "http", + "http 0.2.12", "log", "mime", "once_cell", @@ -9560,9 +9392,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jemalloc-sys" @@ -9586,22 +9418,53 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-patch" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" +dependencies = [ + "serde", + "serde_json", + "treediff", +] + +[[package]] +name = "jsonpath_lib" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" +dependencies = [ + "log", + "serde", + "serde_json", +] + [[package]] name = "jsonwebtoken" version = "7.2.0" @@ -9622,7 +9485,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "pem 1.1.1", "ring 0.16.20", "serde", @@ -9645,18 +9508,40 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "k8s-openapi" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8de9873b904e74b3533f77493731ee26742418077503683db44e1b3c54aa5c" +dependencies = [ + "base64 0.13.1", + "bytes", + "chrono", + "serde", + "serde-value", + "serde_json", +] + +[[package]] +name = "kanal" +version = "0.1.0-pre8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05d55519627edaf7fd0f29981f6dc03fb52df3f5b257130eb8d0bf2801ea1d7" +dependencies = [ + "futures-core", + "lock_api", +] + [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] [[package]] -<<<<<<< HEAD -======= name = "kube" version = "0.65.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9680,7 +9565,7 @@ dependencies = [ "dirs-next", "either", "futures", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.23.2", @@ -9689,7 +9574,7 @@ dependencies = [ "k8s-openapi", "kube-core", "pem 1.1.1", - "pin-project 1.1.3", + "pin-project 1.1.5", "rustls 0.20.9", "rustls-pemfile 0.2.1", "serde", @@ -9711,7 +9596,7 @@ checksum = "c52b6ab05d160691083430f6f431707a4e05b64903f2ffa0095ee5efde759117" dependencies = [ "chrono", "form_urlencoded", - "http", + "http 0.2.12", "json-patch", "k8s-openapi", "once_cell", @@ -9734,7 +9619,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "kv-log-macro" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9745,33 +9629,33 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.12" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", - "diff", "ena", - "is-terminal", - "itertools 0.10.5", + "itertools 0.11.0", "lalrpop-util", - "petgraph 0.6.4", + "petgraph 0.6.5", + "pico-args", "regex", - "regex-syntax 0.6.29", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", "unicode-xid", + "walkdir", ] [[package]] name = "lalrpop-util" -version = "0.19.12" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex", + "regex-automata 0.4.7", ] [[package]] @@ -9798,11 +9682,11 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin 0.9.8", ] [[package]] @@ -9811,6 +9695,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "ledger-apdu" version = "0.10.0" @@ -9856,9 +9746,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libfuzzer-sys" @@ -9879,18 +9769,20 @@ checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", + "libssh2-sys", "libz-sys", + "openssl-sys", "pkg-config", ] [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.5", ] [[package]] @@ -9901,9 +9793,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.9+1.58.0" +version = "0.1.10+1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" dependencies = [ "cc", "libc", @@ -9911,13 +9803,12 @@ dependencies = [ [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "libc", - "redox_syscall 0.4.1", ] [[package]] @@ -9984,6 +9875,20 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + [[package]] name = "libtest-mimic" version = "0.5.2" @@ -9997,9 +9902,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -10027,24 +9932,24 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "listener" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.12", + "clap 4.5.8", "tokio", ] [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -10052,9 +9957,9 @@ dependencies = [ [[package]] name = "lodepng" -version = "3.9.2" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f56ff9bcd5721ab172b73eac8a7d4e9439f47a98581e666178dbe7df97e13" +checksum = "a42d298694b14401847de29abd44adf278b42e989e516deac7b72018400002d8" dependencies = [ "crc32fast", "fallible_collections", @@ -10065,9 +9970,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "serde", "value-bag", @@ -10112,21 +10017,12 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -======= name = "macros" version = "0.1.0" source = "git+https://github.com/niroco/diesel_async_migrations?rev=11f331b73c5cfcc894380074f748d8fda710ac12#11f331b73c5cfcc894380074f748d8fda710ac12" dependencies = [ "proc-macro2", "quote", ->>>>>>> upstream/main ] [[package]] @@ -10150,12 +10046,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.5.0" @@ -10168,6 +10058,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.7.4" @@ -10185,12 +10085,12 @@ dependencies = [ [[package]] name = "memory-stats" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f79cf9964c5c9545493acda1263f1912f8d2c56c8a2ffee2606cb960acaacc" +checksum = "c73f5c649995a115e1a0220b35e4df0a1294500477f97a91d0660fb5abeb574a" dependencies = [ "libc", - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] @@ -10221,13 +10121,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" dependencies = [ "migrations_internals", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main ] [[package]] @@ -10238,9 +10133,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -10269,9 +10164,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -10279,9 +10174,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -10317,13 +10212,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -10347,7 +10237,7 @@ dependencies = [ "anyhow", "aptos-framework", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "move-binary-format", ] @@ -10380,34 +10270,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "move-analyzer" -version = "1.0.0" -dependencies = [ - "anyhow", - "clap 4.4.12", - "codespan-reporting", - "crossbeam", - "derivative", - "dunce", - "im", - "lsp-server", - "lsp-types", - "move-command-line-common", - "move-compiler", - "move-ir-types", - "move-package", - "move-symbol-pool", - "petgraph 0.5.1", - "serde", - "serde_json", - "tempfile", - "url", -] - -[[package]] -======= ->>>>>>> upstream/main name = "move-async-vm" version = "0.1.0" dependencies = [ @@ -10505,7 +10367,7 @@ name = "move-bytecode-viewer" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.8", "crossterm 0.26.1", "move-binary-format", "move-bytecode-source-map", @@ -10519,13 +10381,7 @@ name = "move-cli" version = "0.1.0" dependencies = [ "anyhow", -<<<<<<< HEAD - "bcs 0.1.4", - "bytes", - "clap 4.4.12", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "codespan-reporting", "colored", "datatest-stable", @@ -10573,7 +10429,7 @@ version = "0.0.1" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "codespan-reporting", "datatest-stable", "hex", @@ -10609,12 +10465,7 @@ dependencies = [ "abstract-domain-derive", "anyhow", "bcs 0.1.4", -<<<<<<< HEAD - "clap 4.4.12", - "codespan", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "codespan-reporting", "datatest-stable", "ethnum", @@ -10635,7 +10486,7 @@ dependencies = [ "move-stackless-bytecode", "move-stdlib", "move-symbol-pool", - "num 0.4.1", + "num 0.4.3", "once_cell", "petgraph 0.5.1", "walkdir", @@ -10665,9 +10516,9 @@ dependencies = [ "bcs 0.1.4", "bytes", "ethnum", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "hex", - "num 0.4.1", + "num 0.4.3", "once_cell", "primitive-types 0.10.1", "proptest", @@ -10688,7 +10539,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "codespan", "colored", "move-binary-format", @@ -10705,7 +10556,7 @@ name = "move-disassembler" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.12", + "clap 4.5.8", "colored", "move-binary-format", "move-bytecode-source-map", @@ -10721,7 +10572,7 @@ name = "move-docgen" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.14", + "clap 4.5.8", "codespan", "codespan-reporting", "datatest-stable", @@ -10774,7 +10625,7 @@ name = "move-explain" version = "0.1.0" dependencies = [ "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "move-command-line-common", "move-core-types", ] @@ -10785,7 +10636,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bcs 0.1.4", - "clap 4.4.12", + "clap 4.5.8", "move-binary-format", "move-bytecode-source-map", "move-bytecode-verifier", @@ -10863,7 +10714,7 @@ dependencies = [ "move-ir-types", "move-prover-test-utils", "move-symbol-pool", - "num 0.4.1", + "num 0.4.3", "num-traits", "once_cell", "regex", @@ -10875,12 +10726,7 @@ name = "move-package" version = "0.1.0" dependencies = [ "anyhow", -<<<<<<< HEAD - "bcs 0.1.4", - "clap 4.4.12", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "colored", "datatest-stable", "evm-exec-utils", @@ -10919,12 +10765,7 @@ version = "0.1.0" dependencies = [ "anyhow", "atty", -<<<<<<< HEAD - "clap 4.4.12", - "codespan", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "codespan-reporting", "datatest-stable", "itertools 0.12.1", @@ -10967,7 +10808,7 @@ dependencies = [ "move-model", "move-prover-bytecode-pipeline", "move-stackless-bytecode", - "num 0.4.1", + "num 0.4.3", "once_cell", "pretty", "rand 0.7.3", @@ -10983,13 +10824,6 @@ version = "0.1.0" dependencies = [ "abstract-domain-derive", "anyhow", -<<<<<<< HEAD - "async-trait", - "atty", - "clap 4.4.12", - "codespan", -======= ->>>>>>> upstream/main "codespan-reporting", "datatest-stable", "itertools 0.12.1", @@ -11043,7 +10877,7 @@ dependencies = [ "move-core-types", "move-model", "move-stackless-bytecode-test-utils", - "num 0.4.1", + "num 0.4.3", "paste", "petgraph 0.5.1", ] @@ -11125,7 +10959,7 @@ version = "0.1.0" dependencies = [ "anyhow", "atty", - "clap 4.4.12", + "clap 4.5.8", "codespan", "codespan-reporting", "datatest-stable", @@ -11156,12 +10990,7 @@ name = "move-transactional-test-runner" version = "0.1.0" dependencies = [ "anyhow", -<<<<<<< HEAD - "clap 4.4.12", - "colored", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "datatest-stable", "difference", "move-binary-format", @@ -11193,12 +11022,7 @@ version = "0.1.0" dependencies = [ "anyhow", "better_any", -<<<<<<< HEAD - "bytes", - "clap 4.4.12", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "codespan-reporting", "colored", "datatest-stable", @@ -11261,7 +11085,7 @@ dependencies = [ "better_any", "bytes", "fail", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "hex", "lazy_static", "lru 0.7.8", @@ -11272,7 +11096,7 @@ dependencies = [ "move-ir-compiler", "move-vm-types", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "proptest", "serde", "sha3 0.9.1", @@ -11331,7 +11155,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 0.2.12", "httparse", "log", "memchr", @@ -11342,56 +11166,25 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= -name = "nalgebra" -version = "0.32.3" +name = "named-lock" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex 0.4.4", - "num-rational 0.4.1", - "num-traits", - "simba", - "typenum", + "libc", + "once_cell", + "parking_lot 0.12.3", + "thiserror", + "widestring", + "winapi 0.3.9", ] [[package]] -name = "nalgebra-macros" -version = "0.2.1" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] ->>>>>>> upstream/main -name = "named-lock" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "libc", - "once_cell", - "parking_lot 0.12.1", - "thiserror", - "widestring", - "winapi 0.3.9", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -11430,9 +11223,9 @@ checksum = "ca2b420f638f07fe83056b55ea190bb815f609ec5a35e7017884a10f78839c9e" [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" @@ -11451,8 +11244,20 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -11495,9 +11300,9 @@ dependencies = [ [[package]] name = "ntest" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8ec6d2b73d45307e926f5af46809768581044384637af6b3f3fe7c3c88f512" +checksum = "fb183f0a1da7a937f672e5ee7b7edb727bf52b8a52d531374ba8ebb9345c0330" dependencies = [ "ntest_test_cases", "ntest_timeout", @@ -11505,34 +11310,24 @@ dependencies = [ [[package]] name = "ntest_test_cases" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22" +checksum = "16d0d3f2a488592e5368ebbe996e7f1d44aa13156efad201f5b4d84e150eaa93" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] name = "ntest_timeout" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066b468120587a402f0b47d8f80035c921f6a46f8209efd0632a89a16f5188a4" +checksum = "fcc7c92f190c97f79b4a332f5e81dcf68c8420af2045c936c9be0bc9de6f63b5" dependencies = [ - "proc-macro-crate 1.3.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= + "proc-macro-crate 2.0.2", "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -11571,15 +11366,15 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint 0.4.4", - "num-complex 0.4.4", + "num-bigint 0.4.6", + "num-complex 0.4.6", "num-integer", "num-iter", - "num-rational 0.4.1", + "num-rational 0.4.2", "num-traits", ] @@ -11608,11 +11403,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -11645,26 +11439,27 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -11680,19 +11475,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -11705,7 +11499,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] @@ -11724,21 +11518,20 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -11750,15 +11543,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -11769,6 +11562,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "number_range" version = "0.3.2" @@ -11777,14 +11576,14 @@ checksum = "60080faccd4ca50ad0b801b2be686136376b13f691f6eac84817e40973b2e1bb" dependencies = [ "anyhow", "itertools 0.10.5", - "num 0.4.1", + "num 0.4.3", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] @@ -11809,19 +11608,19 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -11834,15 +11633,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -11853,9 +11646,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -11863,6 +11656,21 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -11885,24 +11693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" dependencies = [ "aliasable", -<<<<<<< HEAD - "ouroboros_macro 0.15.6", -] - -[[package]] -name = "ouroboros_macro" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f2cb802b5bdfdf52f1ffa0b54ce105e4d346e91990dd571f86c91321ad49e2" -dependencies = [ - "Inflector", - "proc-macro-error", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", -======= "ouroboros_macro", ->>>>>>> upstream/main ] [[package]] @@ -11913,13 +11704,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -11981,9 +11767,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "a1b5927e4a9ae8d6cdb6a69e4e04a0ec73381a358e21b8a576f44769f34e7c24" dependencies = [ "arrayvec 0.7.4", "bitvec 1.0.1", @@ -12000,13 +11786,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ "proc-macro-crate 1.3.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -12016,14 +11797,9 @@ version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate 2.0.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= + "proc-macro-crate 2.0.2", "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -12046,12 +11822,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -12070,22 +11846,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "parse-zoneinfo" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" dependencies = [ "regex", ] @@ -12129,11 +11905,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "ciborium", "coset", "data-encoding", - "indexmap 2.2.5", + "indexmap 2.2.6", "rand 0.8.5", "serde", "serde_json", @@ -12161,9 +11937,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -12174,6 +11950,16 @@ dependencies = [ "camino", ] +[[package]] +name = "pathsearch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da983bc5e582ab17179c190b4b66c7d76c5943a69c6d34df2a2b6bf8a2977b05" +dependencies = [ + "anyhow", + "libc", +] + [[package]] name = "pbjson" version = "0.5.1" @@ -12239,9 +12025,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -12250,9 +12036,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -12260,28 +12046,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -12300,12 +12080,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.2.5", + "indexmap 2.2.6", ] [[package]] @@ -12355,16 +12135,28 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" -version = "1.1.3" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + +[[package]] +name = "pin-project" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ -<<<<<<< HEAD - "pin-project-internal", -======= - "pin-project-internal 1.1.3", + "pin-project-internal 1.1.5", ] [[package]] @@ -12376,31 +12168,24 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ->>>>>>> upstream/main ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -12410,12 +12195,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-io", ] @@ -12436,7 +12221,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.8", + "der 0.7.9", "pkcs8 0.10.2", "spki 0.7.3", ] @@ -12458,21 +12243,21 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", + "der 0.7.9", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -12483,24 +12268,24 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -12519,15 +12304,15 @@ dependencies = [ "async-trait", "bytes", "chrono", - "cookie 0.17.0", + "cookie", "futures-util", "headers", - "http", + "http 0.2.12", "hyper", "mime", "multer", "nix 0.27.1", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "percent-encoding", "pin-project-lite", "poem-derive", @@ -12545,7 +12330,7 @@ dependencies = [ "tokio", "tokio-rustls 0.24.1", "tokio-stream", - "tokio-util", + "tokio-util 0.7.11", "tracing", "wildmatch", ] @@ -12556,16 +12341,10 @@ version = "1.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" dependencies = [ - "proc-macro-crate 2.0.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= + "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -12587,7 +12366,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "serde_yaml 0.9.30", + "serde_yaml 0.9.34+deprecated", "thiserror", "tokio", "url", @@ -12600,17 +12379,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "274cf13f710999977a3c1e396c2a5000d104075a7127ce6470fbdae4706be621" dependencies = [ "darling 0.14.4", - "http", + "http 0.2.12", "indexmap 1.9.3", "mime", "proc-macro-crate 1.3.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "regex", "syn 1.0.109", "thiserror", @@ -12634,27 +12408,28 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.28", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", "universal-hash", ] @@ -12673,16 +12448,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597c3287a549da151aca6ada2795ecde089c7527bd5093114e8e0e1c3f0e52b1" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "poseidon-ark" version = "0.0.1" @@ -12693,6 +12469,48 @@ dependencies = [ "ark-std", ] +[[package]] +name = "postgres-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" +dependencies = [ + "futures", + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +dependencies = [ + "base64 0.21.7", + "byteorder", + "bytes", + "fallible-iterator", + "hmac 0.12.1", + "md-5", + "memchr", + "rand 0.8.5", + "sha2 0.10.8", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -12713,7 +12531,7 @@ dependencies = [ "log", "nix 0.26.4", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "protobuf", "protobuf-codegen-pure", "smallvec", @@ -12811,17 +12629,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "syn 2.0.47", -======= "csv", "encode_unicode 1.0.0", "is-terminal", "lazy_static", "term", "unicode-width", ->>>>>>> upstream/main ] [[package]] @@ -12872,9 +12685,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ "toml_datetime", "toml_edit 0.20.2", @@ -12887,13 +12700,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", "version_check", ] @@ -12904,13 +12712,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "version_check", ] @@ -12928,29 +12731,14 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -<<<<<<< HEAD -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.75" -======= -version = "1.0.76" ->>>>>>> upstream/main +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] -<<<<<<< HEAD -======= name = "processor" version = "1.0.0" source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" @@ -12964,7 +12752,7 @@ dependencies = [ "bcs 0.1.4", "bigdecimal", "chrono", - "clap 4.4.14", + "clap 4.5.8", "diesel", "diesel-async 0.4.1 (git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2)", "diesel_async_migrations", @@ -12985,8 +12773,8 @@ dependencies = [ "once_cell", "postgres-native-tls", "prometheus", - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.6", + "prost-types 0.12.6", "regex", "serde", "serde_json", @@ -13003,7 +12791,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "procfs" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -13026,18 +12813,31 @@ checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df" [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "thiserror", ] +[[package]] +name = "prometheus-http-query" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae2f6a3f14ff35c16b51ac796d1dc73c15ad6472c48836c6c467f6d52266648" +dependencies = [ + "reqwest", + "serde", + "serde_json", + "time", + "url", +] + [[package]] name = "prometheus-parse" version = "0.2.5" @@ -13052,19 +12852,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.1", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -13076,13 +12876,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -13098,12 +12893,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.3", + "prost-derive 0.12.6", ] [[package]] @@ -13114,33 +12909,22 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -13154,11 +12938,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost 0.12.3", + "prost 0.12.6", ] [[package]] @@ -13192,7 +12976,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.4.12", + "clap 4.5.8", "codespan-reporting", "itertools 0.12.1", "log", @@ -13229,15 +13013,24 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "memchr", "unicase", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "qstring" version = "0.7.2" @@ -13290,21 +13083,17 @@ checksum = "347e1a588d1de074eeb3c00eadff93db4db65aeb62aee852b1efd0949fe65b6c" dependencies = [ "ahash 0.8.11", "equivalent", - "hashbrown 0.14.3", - "parking_lot 0.12.1", + "hashbrown 0.14.5", + "parking_lot 0.12.3", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", -======= "proc-macro2", ->>>>>>> upstream/main ] [[package]] @@ -13314,7 +13103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "scheduled-thread-pool", ] @@ -13390,7 +13179,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", ] [[package]] @@ -13430,17 +13219,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "raw-cpuid" -version = "10.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -======= name = "random_word" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -13451,17 +13229,10 @@ dependencies = [ ] [[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] ->>>>>>> upstream/main name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -13495,7 +13266,7 @@ dependencies = [ "ryu", "sha1_smol", "tokio", - "tokio-util", + "tokio-util 0.7.11", "url", ] @@ -13527,53 +13298,56 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "libredox", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -13587,13 +13361,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", ] [[package]] @@ -13604,25 +13378,26 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "async-compression", + "base64 0.21.7", "bytes", - "cookie 0.16.2", + "cookie", "cookie_store", "encoding_rs", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -13636,35 +13411,36 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util", + "tokio-util 0.7.11", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] [[package]] name = "reqwest-middleware" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a3e86aa6053e59030e7ce2d2a3b258dd08fc2d337d52f73f6cb480f5858690" +checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" dependencies = [ "anyhow", "async-trait", - "http", + "http 0.2.12", "reqwest", "serde", "task-local-extensions", @@ -13681,8 +13457,8 @@ dependencies = [ "async-trait", "chrono", "futures", - "getrandom 0.2.11", - "http", + "getrandom 0.2.15", + "http 0.2.12", "hyper", "parking_lot 0.11.2", "reqwest", @@ -13723,18 +13499,18 @@ dependencies = [ [[package]] name = "rfc7239" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087317b3cf7eb481f13bd9025d729324b7cd068d6f470e2d76d049e191f5ba47" +checksum = "b106a85eeb5b0336d16d6a20eab857f92861d4fbb1eb9a239866fb98fb6a1063" dependencies = [ "uncased", ] [[package]] name = "rgb" -version = "0.8.37" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" dependencies = [ "bytemuck", ] @@ -13756,16 +13532,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -13793,13 +13570,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -13854,6 +13626,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstack" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7df9d3ebd4f17b52e6134efe2fa20021c80688cbe823d481a729a993b730493" +dependencies = [ + "cfg-if", + "dw", + "lazy_static", + "libc", + "log", +] + +[[package]] +name = "rstack-self" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd5030da3aba0ec731502f74ec38e63798eea6bc8b8ba5972129afe3eababd2" +dependencies = [ + "antidote", + "backtrace", + "bincode", + "lazy_static", + "libc", + "rstack", + "serde", +] + [[package]] name = "rstest" version = "0.15.0" @@ -13873,22 +13673,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" dependencies = [ "cfg-if", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "rustc_version", "syn 1.0.109", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -13941,14 +13736,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -13966,12 +13761,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] @@ -13983,7 +13778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.4", "subtle", @@ -13992,15 +13787,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.7" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "log", "once_cell", - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -14032,15 +13827,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -<<<<<<< HEAD -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" -======= version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" ->>>>>>> upstream/main dependencies = [ "base64 0.13.1", ] @@ -14051,7 +13840,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -14070,13 +13859,23 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustls-webpki" +version = "0.100.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -14086,16 +13885,16 @@ version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -14111,9 +13910,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -14144,25 +13943,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd" dependencies = [ "proc-macro-crate 1.3.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "scale-info-derive" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -14181,7 +13963,17 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot 0.12.3", +] + +[[package]] +name = "scoped-futures" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" +dependencies = [ + "cfg-if", + "pin-utils", ] [[package]] @@ -14202,7 +13994,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -14219,7 +14011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", - "der 0.7.8", + "der 0.7.9", "generic-array 0.14.7", "pkcs8 0.10.2", "serdect", @@ -14227,13 +14019,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secret-vault-value" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f8cfb86d2019f64a4cfb49e499f401f406fbec946c1ffeea9d0504284347de" +dependencies = [ + "prost 0.12.6", + "prost-types 0.12.6", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -14242,19 +14047,50 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "self-replace" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525db198616b2bcd0f245daf7bfd8130222f7ee6af9ff9984c19a61bf1160c55" +dependencies = [ + "fastrand 1.9.0", + "tempfile", + "windows-sys 0.48.0", +] + +[[package]] +name = "self_update" +version = "0.39.0" +source = "git+https://github.com/banool/self_update.git?rev=8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b#8306158ad0fd5b9d4766a3c6bf967e7ef0ea5c4b" +dependencies = [ + "hyper", + "indicatif 0.17.8", + "log", + "quick-xml 0.23.1", + "regex", + "reqwest", + "self-replace", + "semver", + "serde_json", + "tempfile", + "urlencoding", + "zip", + "zipsign-api", +] + [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -14264,22 +14100,16 @@ name = "sender" version = "0.1.0" dependencies = [ "bytes", - "clap 4.4.12", + "clap 4.5.8", "event-listener 2.5.3", "tokio", ] [[package]] name = "serde" -<<<<<<< HEAD -version = "1.0.194" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" -======= -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" ->>>>>>> upstream/main +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -14326,16 +14156,26 @@ name = "serde-reflection" version = "0.3.5" source = "git+https://github.com/aptos-labs/serde-reflection?rev=73b6bbf748334b71ff6d7d09d06a29e3062ca075#73b6bbf748334b71ff6d7d09d06a29e3062ca075" dependencies = [ - "once_cell", + "once_cell", + "serde", + "thiserror", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", "serde", - "thiserror", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] @@ -14346,44 +14186,28 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ - "half 1.8.2", + "half 1.8.3", "serde", ] [[package]] name = "serde_derive" -<<<<<<< HEAD -version = "1.0.194" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" -dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "serde_json" -<<<<<<< HEAD -version = "1.0.110" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" -======= -version = "1.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" ->>>>>>> upstream/main +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -14412,26 +14236,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -14450,16 +14268,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "079f3a42cd87588d924ed95b533f8d30a483388c4e400ab736a7058e34f16169" dependencies = [ - "base64 0.21.5", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -14467,21 +14286,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "bc03aad67c1d26b7de277d51c86892e7d9a0110a2fe44bf6b26cc569fba302d6" dependencies = [ -<<<<<<< HEAD - "darling 0.20.3", - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -14498,11 +14310,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -14530,8 +14342,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "server-framework" version = "1.0.0" source = "git+https://github.com/aptos-labs/aptos-indexer-processors.git?rev=5244b84fa5ed872e5280dc8df032d744d62ad29d#5244b84fa5ed872e5280dc8df032d744d62ad29d" @@ -14540,7 +14350,7 @@ dependencies = [ "aptos-system-utils 0.1.0 (git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93)", "async-trait", "backtrace", - "clap 4.4.14", + "clap 4.5.8", "futures", "prometheus", "serde", @@ -14554,7 +14364,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -14581,7 +14390,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -14595,6 +14404,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2 0.10.8", + "tokio", +] + [[package]] name = "sha3" version = "0.8.2" @@ -14617,7 +14439,7 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -14637,7 +14459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c0ea0c68418544f725eba5401a5b965a2263254c92458d04aeae74e9d88ff4e" dependencies = [ "const_format", - "git2", + "git2 0.15.0", "is_debug", "time", "tzdb", @@ -14660,9 +14482,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" @@ -14687,9 +14509,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -14718,9 +14540,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" dependencies = [ "bstr 0.2.17", "unicode-segmentation", @@ -14753,7 +14575,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "thiserror", "time", @@ -14833,15 +14655,15 @@ dependencies = [ [[package]] name = "smallbitvec" -version = "2.5.1" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ce4f9dc4a41b4c3476cc925f1efb11b66df373a8fde5d4b8915fa91b5d995e" +checksum = "fcc3fc564a4b53fd1e8589628efafe57602d91bde78be18186b5f61e8faea470" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" @@ -14850,8 +14672,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] -<<<<<<< HEAD -======= name = "smoke-test" version = "0.1.0" dependencies = [ @@ -14916,7 +14736,6 @@ dependencies = [ ] [[package]] ->>>>>>> upstream/main name = "smt2parser" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -14945,13 +14764,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -14967,12 +14781,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -14986,6 +14800,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -15004,7 +14821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.8", + "der 0.7.9", ] [[package]] @@ -15043,11 +14860,22 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "phf_shared 0.10.0", "precomputed-hash", ] +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.8.0" @@ -15085,13 +14913,8 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "syn 1.0.109", ] @@ -15100,6 +14923,9 @@ name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] [[package]] name = "strum" @@ -15112,9 +14938,9 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" @@ -15123,13 +14949,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "rustversion", "syn 1.0.109", ] @@ -15141,14 +14962,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] @@ -15160,16 +14977,15 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", ->>>>>>> upstream/main "rustversion", - "syn 2.0.47", + "syn 2.0.68", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "subtle-ng" @@ -15206,29 +15022,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "unicode-ident", ] [[package]] name = "syn" -version = "2.0.47" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", -======= "proc-macro2", "quote", ->>>>>>> upstream/main "unicode-ident", ] @@ -15288,9 +15094,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -15299,9 +15105,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "target-spec" @@ -15327,22 +15133,21 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall 0.4.1", - "rustix 0.38.28", + "fastrand 2.1.0", + "rustix 0.38.34", "windows-sys 0.52.0", ] [[package]] name = "tera" -version = "1.19.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" dependencies = [ "chrono", "chrono-tz", @@ -15402,15 +15207,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -15419,15 +15218,9 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", "test-case-core", ] @@ -15435,9 +15228,9 @@ dependencies = [ name = "test-generation" version = "0.1.0" dependencies = [ - "clap 4.4.12", + "clap 4.5.8", "crossbeam-channel", - "getrandom 0.2.11", + "getrandom 0.2.15", "hex", "itertools 0.12.1", "module-generation", @@ -15461,12 +15254,7 @@ name = "testdiff" version = "0.1.0" dependencies = [ "anyhow", -<<<<<<< HEAD - "clap 4.4.12", - "itertools 0.10.5", -======= - "clap 4.4.14", ->>>>>>> upstream/main + "clap 4.5.8", "once_cell", "regex", "walkdir", @@ -15504,9 +15292,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" @@ -15523,22 +15311,16 @@ version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -15553,15 +15335,27 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -15577,10 +15371,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -15624,9 +15419,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -15639,19 +15434,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -15669,19 +15464,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -15694,13 +15483,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot 0.12.3", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.8.5", + "socket2 0.5.7", + "tokio", + "tokio-util 0.7.11", + "whoami", +] + [[package]] name = "tokio-retry" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" dependencies = [ - "pin-project", + "pin-project 1.1.5", "rand 0.8.5", "tokio", ] @@ -15722,7 +15537,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.10", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -15732,20 +15558,26 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ -<<<<<<< HEAD - "rustls 0.22.4", -======= - "rustls 0.23.7", ->>>>>>> upstream/main + "rustls 0.23.10", "rustls-pki-types", "tokio", ] +[[package]] +name = "tokio-scoped" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4beb8ba13bc53ac53ce1d52b42f02e5d8060f0f42138862869beb769722b256" +dependencies = [ + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -15754,9 +15586,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", @@ -15767,9 +15599,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -15779,9 +15611,23 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -15789,7 +15635,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -15814,6 +15659,18 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -15829,7 +15686,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -15842,7 +15699,9 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -15853,34 +15712,32 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ + "async-stream", "async-trait", "axum 0.6.20", - "base64 0.21.5", + "base64 0.21.7", "bytes", + "flate2", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", "percent-encoding", - "pin-project", + "pin-project 1.1.5", "prost 0.11.9", + "rustls-native-certs 0.6.3", + "rustls-pemfile 1.0.4", "tokio", -<<<<<<< HEAD -======= "tokio-rustls 0.24.1", ->>>>>>> upstream/main "tokio-stream", "tower", "tower-layer", "tower-service", "tracing", -<<<<<<< HEAD -======= "webpki-roots 0.23.1", ->>>>>>> upstream/main ] [[package]] @@ -15891,30 +15748,21 @@ dependencies = [ "async-stream", "async-trait", "axum 0.6.20", -<<<<<<< HEAD - "base64 0.21.5", -======= "base64 0.22.1", ->>>>>>> upstream/main "bytes", "flate2", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", "percent-encoding", - "pin-project", - "prost 0.12.3", + "pin-project 1.1.5", + "prost 0.12.6", "rustls-native-certs 0.7.0", -<<<<<<< HEAD "rustls-pemfile 2.1.2", - "rustls-pki-types", -======= - "rustls-pemfile 2.1.1", - "strum 0.26.2", + "strum 0.26.3", "strum_macros 0.26.4", ->>>>>>> upstream/main "tokio", "tokio-rustls 0.26.0", "tokio-stream", @@ -15931,8 +15779,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "548c227bd5c0fae5925812c4ec6c66ffcfced23ea370cb823f4d18f0fc1cb6a7" dependencies = [ - "prost 0.12.3", - "prost-types 0.12.3", + "prost 0.12.6", + "prost-types 0.12.6", "tokio", "tokio-stream", "tonic 0.11.0", @@ -15947,12 +15795,32 @@ dependencies = [ "futures-core", "futures-util", "indexmap 1.9.3", - "pin-project", + "pin-project 1.1.5", "pin-project-lite", "rand 0.8.5", "slab", "tokio", - "tokio-util", + "tokio-util 0.7.11", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body", + "http-range-header", + "pin-project-lite", "tower-layer", "tower-service", "tracing", @@ -15968,7 +15836,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http", + "http 0.2.12", "http-body", "http-range-header", "pin-project-lite", @@ -15990,16 +15858,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -<<<<<<< HEAD -name = "trace" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad0c048e114d19d1140662762bfdb10682f3bc806d8be18af846600214dd9af" -dependencies = [ - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 1.0.109", -======= name = "tower-util" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -16009,7 +15867,6 @@ dependencies = [ "futures-util", "pin-project 0.4.30", "tower-service", ->>>>>>> upstream/main ] [[package]] @@ -16030,15 +15887,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] @@ -16057,7 +15908,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project", + "pin-project 1.1.5", "tracing", ] @@ -16113,14 +15964,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "trie-root" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" -dependencies = [ - "hash-db 0.16.0", -======= name = "trait-set" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -16138,7 +15981,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" dependencies = [ "serde_json", ->>>>>>> upstream/main ] [[package]] @@ -16153,9 +15995,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" dependencies = [ "serde", "stable_deref_trait", @@ -16169,17 +16011,16 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.87" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6b2fc10a33500845468aa7c06567a9226581b24f03c6f891e6dda2dc9a1c8b" +checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ - "basic-toml", "glob", - "once_cell", "serde", "serde_derive", "serde_json", "termcolor", + "toml 0.8.2", ] [[package]] @@ -16197,14 +16038,14 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.1.0", "httparse", "log", "rand 0.8.5", @@ -16228,9 +16069,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typeshare" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44d1a2f454cb35fbe05b218c410792697e76bd868f48d3a418f2cd1a7d527d6" +checksum = "04f17399b76c2e743d58eac0635d7686e9c00f48cd4776f00695d9882a7d3187" dependencies = [ "chrono", "serde", @@ -16240,12 +16081,12 @@ dependencies = [ [[package]] name = "typeshare-annotation" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9" +checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.68", ] [[package]] @@ -16271,9 +16112,9 @@ dependencies = [ [[package]] name = "tzdb_data" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629555d2921f3f0dc0de98699415a8b2b61dfcd3a0b082a327f7ed748bbb2b76" +checksum = "d1889fdffac09d65c1d95c42d5202e9b21ad8c758f426e9fe09088817ea998d6" dependencies = [ "tz-rs", ] @@ -16304,13 +16145,19 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "uncased" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" dependencies = [ "version_check", ] +[[package]] +name = "unescape" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" + [[package]] name = "unic-char-property" version = "0.9.0" @@ -16372,9 +16219,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -16390,24 +16237,30 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -16427,9 +16280,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -16462,9 +16315,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -16480,16 +16333,15 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utcnow" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d49a98e3bbd9b73084a7b15f96c5b2136d5a2e2799b99d19a2774d8519d1f" +checksum = "493ace370ee8579788f83a4f0eef89183c527b7551b4ad71364fac10371872b7" dependencies = [ - "autocfg", "const_fn", "errno", "js-sys", "libc", - "rustix 0.38.28", + "rustix 0.38.34", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", "winapi 0.3.9", @@ -16503,15 +16355,19 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.6.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +dependencies = [ + "getrandom 0.2.15", + "serde", +] [[package]] name = "valuable" @@ -16521,9 +16377,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.3" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" [[package]] name = "variant_count" @@ -16547,6 +16403,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "version_check" version = "0.9.4" @@ -16564,15 +16426,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -16589,32 +16451,31 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", - "http", + "http 0.2.12", "hyper", "log", "mime", "mime_guess", "multer", "percent-encoding", - "pin-project", - "rustls-pemfile 1.0.4", + "pin-project 1.1.5", + "rustls-pemfile 2.1.2", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls 0.24.1", - "tokio-stream", + "tokio-rustls 0.25.0", "tokio-tungstenite", - "tokio-util", + "tokio-util 0.7.11", "tower-service", "tracing", ] @@ -16653,9 +16514,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -16663,30 +16524,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -16696,9 +16551,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -16706,34 +16561,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -16759,9 +16608,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -16773,15 +16622,30 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +dependencies = [ + "rustls-webpki 0.100.3", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "weezl" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -16792,14 +16656,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.28", + "rustix 0.38.34", ] [[package]] name = "whoami" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ "redox_syscall 0.4.1", "wasite", @@ -16814,9 +16678,9 @@ checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "wildmatch" -version = "2.3.0" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb" +checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19" [[package]] name = "winapi" @@ -16842,11 +16706,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] @@ -16861,7 +16725,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -16888,7 +16752,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -16923,17 +16787,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 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", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -16950,9 +16815,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -16968,9 +16833,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -16986,9 +16851,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -17004,9 +16875,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -17022,9 +16893,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -17040,9 +16911,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -17058,15 +16929,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.32" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -17101,20 +16972,20 @@ name = "x25519-dalek" version = "1.2.0" source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "zeroize", ] [[package]] name = "xattr" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.12", - "rustix 0.38.28", + "linux-raw-sys 0.4.14", + "rustix 0.38.34", ] [[package]] @@ -17134,9 +17005,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yansi" -version = "1.0.0-rc.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yup-oauth2" @@ -17146,9 +17017,9 @@ checksum = "b61da40aeb0907a65f7fb5c1de83c5a224d6a9ebb83bf918588a2bb744d636b8" dependencies = [ "anyhow", "async-trait", - "base64 0.21.6", + "base64 0.21.7", "futures", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.24.2", "itertools 0.12.1", @@ -17178,35 +17049,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", ->>>>>>> upstream/main + "syn 2.0.68", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -17217,14 +17082,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ -<<<<<<< HEAD - "proc-macro2 1.0.75", - "quote 1.0.35", - "syn 2.0.47", -======= "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.68", ] [[package]] @@ -17248,33 +17108,41 @@ checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea" dependencies = [ "ed25519-dalek 2.1.1", "thiserror", ->>>>>>> upstream/main ] [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/aptos-move/aptos-vm/Cargo.toml b/aptos-move/aptos-vm/Cargo.toml index fab2dd9e83131..c2755c5530282 100644 --- a/aptos-move/aptos-vm/Cargo.toml +++ b/aptos-move/aptos-vm/Cargo.toml @@ -44,6 +44,7 @@ claims = { workspace = true } crossbeam-channel = { workspace = true } derive_more = { workspace = true } fail = { workspace = true } +futures = { workspace = true } hex = { workspace = true } move-binary-format = { workspace = true } move-core-types = { workspace = true } diff --git a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs index 9c2f7ddd9e3bc..e968f2416d47c 100644 --- a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs +++ b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs @@ -30,6 +30,7 @@ use aptos_types::{ }, }; use aptos_vm_logging::disable_speculative_logging; +use futures::{channel::oneshot, executor::block_on}; use move_core_types::vm_status::VMStatus; use std::sync::Arc; @@ -104,6 +105,8 @@ impl ShardedExecutorService { state_view: &S, config: BlockExecutorConfig, ) -> Result, VMStatus> { + let (callback, callback_receiver) = oneshot::channel(); + let cross_shard_state_view = Arc::new(CrossShardStateView::create_cross_shard_state_view( state_view, &transactions, @@ -121,8 +124,8 @@ impl ShardedExecutorService { .into_iter() .map(|txn| txn.into_txn().into_txn()) .collect(); + let executor_thread_pool_clone = executor_thread_pool.clone(); - let mut outputs = Ok(vec![]); executor_thread_pool.clone().scope(|s| { s.spawn(move |_| { CrossShardCommitReceiver::start( @@ -157,13 +160,15 @@ impl ShardedExecutorService { // Send a self message to stop the cross-shard commit receiver. cross_shard_client_clone.send_global_msg(CrossShardMsg::StopMsg); } - s.spawn(move |_| { + callback.send(ret).unwrap(); + executor_thread_pool_clone.spawn(move || { // Explicit async drop drop(signature_verified_transactions); }); }); }); - outputs + + block_on(callback_receiver).unwrap() } fn execute_block( diff --git a/aptos-move/framework/aptos-framework/doc/account.md b/aptos-move/framework/aptos-framework/doc/account.md index d17c58a984c9c..2a45490a386f4 100644 --- a/aptos-move/framework/aptos-framework/doc/account.md +++ b/aptos-move/framework/aptos-framework/doc/account.md @@ -99,6 +99,7 @@ use 0x1::ed25519; use 0x1::error; use 0x1::event; +use 0x1::features; use 0x1::from_bcs; use 0x1::guid; use 0x1::hash; @@ -900,7 +901,7 @@ Only called during genesis to initialize system resources for this module. -

    fun create_account_if_does_not_exist(account_address: address)
    +
    public fun create_account_if_does_not_exist(account_address: address)
     
    @@ -909,7 +910,7 @@ Only called during genesis to initialize system resources for this module. Implementation -
    fun create_account_if_does_not_exist(account_address: address) {
    +
    public fun create_account_if_does_not_exist(account_address: address) {
         if (!exists<Account>(account_address)) {
             create_account(account_address);
         }
    @@ -1264,11 +1265,17 @@ to rotate his address to Alice's address in the first place.
         if (from_scheme == ED25519_SCHEME) {
             let from_pk = ed25519::new_unvalidated_public_key_from_bytes(from_public_key_bytes);
             let from_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&from_pk);
    -        assert!(account_resource.authentication_key == from_auth_key, error::unauthenticated(EWRONG_CURRENT_PUBLIC_KEY));
    +        assert!(
    +            account_resource.authentication_key == from_auth_key,
    +            error::unauthenticated(EWRONG_CURRENT_PUBLIC_KEY)
    +        );
         } else if (from_scheme == MULTI_ED25519_SCHEME) {
             let from_pk = multi_ed25519::new_unvalidated_public_key_from_bytes(from_public_key_bytes);
             let from_auth_key = multi_ed25519::unvalidated_public_key_to_authentication_key(&from_pk);
    -        assert!(account_resource.authentication_key == from_auth_key, error::unauthenticated(EWRONG_CURRENT_PUBLIC_KEY));
    +        assert!(
    +            account_resource.authentication_key == from_auth_key,
    +            error::unauthenticated(EWRONG_CURRENT_PUBLIC_KEY)
    +        );
         } else {
             abort error::invalid_argument(EINVALID_SCHEME)
         };
    @@ -1283,8 +1290,18 @@ to rotate his address to Alice's address in the first place.
         };
     
         // Assert the challenges signed by the current and new keys are valid
    -    assert_valid_rotation_proof_signature_and_get_auth_key(from_scheme, from_public_key_bytes, cap_rotate_key, &challenge);
    -    let new_auth_key = assert_valid_rotation_proof_signature_and_get_auth_key(to_scheme, to_public_key_bytes, cap_update_table, &challenge);
    +    assert_valid_rotation_proof_signature_and_get_auth_key(
    +        from_scheme,
    +        from_public_key_bytes,
    +        cap_rotate_key,
    +        &challenge
    +    );
    +    let new_auth_key = assert_valid_rotation_proof_signature_and_get_auth_key(
    +        to_scheme,
    +        to_public_key_bytes,
    +        cap_update_table,
    +        &challenge
    +    );
     
         // Update the `OriginatingAddress` table.
         update_auth_key_and_originating_address_table(addr, account_resource, new_auth_key);
    @@ -1322,7 +1339,10 @@ to rotate his address to Alice's address in the first place.
         // Check that there exists a rotation capability offer at the offerer's account resource for the delegate.
         let delegate_address = signer::address_of(delegate_signer);
         let offerer_account_resource = borrow_global<Account>(rotation_cap_offerer_address);
    -    assert!(option::contains(&offerer_account_resource.rotation_capability_offer.for, &delegate_address), error::not_found(ENO_SUCH_ROTATION_CAPABILITY_OFFER));
    +    assert!(
    +        option::contains(&offerer_account_resource.rotation_capability_offer.for, &delegate_address),
    +        error::not_found(ENO_SUCH_ROTATION_CAPABILITY_OFFER)
    +    );
     
         let curr_auth_key = from_bcs::to_address(offerer_account_resource.authentication_key);
         let challenge = RotationProofChallenge {
    @@ -1333,11 +1353,20 @@ to rotate his address to Alice's address in the first place.
         };
     
         // Verifies that the `RotationProofChallenge` from above is signed under the new public key that we are rotating to.        l
    -    let new_auth_key = assert_valid_rotation_proof_signature_and_get_auth_key(new_scheme, new_public_key_bytes, cap_update_table, &challenge);
    +    let new_auth_key = assert_valid_rotation_proof_signature_and_get_auth_key(
    +        new_scheme,
    +        new_public_key_bytes,
    +        cap_update_table,
    +        &challenge
    +    );
     
         // Update the `OriginatingAddress` table, so we can find the originating address using the new address.
         let offerer_account_resource = borrow_global_mut<Account>(rotation_cap_offerer_address);
    -    update_auth_key_and_originating_address_table(rotation_cap_offerer_address, offerer_account_resource, new_auth_key);
    +    update_auth_key_and_originating_address_table(
    +        rotation_cap_offerer_address,
    +        offerer_account_resource,
    +        new_auth_key
    +    );
     }
     
    @@ -1400,17 +1429,29 @@ offer, calling this function will replace the previous recipient_addressif (account_scheme == ED25519_SCHEME) { let pubkey = ed25519::new_unvalidated_public_key_from_bytes(account_public_key_bytes); let expected_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&pubkey); - assert!(account_resource.authentication_key == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); + assert!( + account_resource.authentication_key == expected_auth_key, + error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY) + ); let rotation_capability_sig = ed25519::new_signature_from_bytes(rotation_capability_sig_bytes); - assert!(ed25519::signature_verify_strict_t(&rotation_capability_sig, &pubkey, proof_challenge), error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE)); + assert!( + ed25519::signature_verify_strict_t(&rotation_capability_sig, &pubkey, proof_challenge), + error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE) + ); } else if (account_scheme == MULTI_ED25519_SCHEME) { let pubkey = multi_ed25519::new_unvalidated_public_key_from_bytes(account_public_key_bytes); let expected_auth_key = multi_ed25519::unvalidated_public_key_to_authentication_key(&pubkey); - assert!(account_resource.authentication_key == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); + assert!( + account_resource.authentication_key == expected_auth_key, + error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY) + ); let rotation_capability_sig = multi_ed25519::new_signature_from_bytes(rotation_capability_sig_bytes); - assert!(multi_ed25519::signature_verify_strict_t(&rotation_capability_sig, &pubkey, proof_challenge), error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE)); + assert!( + multi_ed25519::signature_verify_strict_t(&rotation_capability_sig, &pubkey, proof_challenge), + error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE) + ); } else { abort error::invalid_argument(EINVALID_SCHEME) }; @@ -1502,7 +1543,10 @@ Revoke the rotation capability offer given to to_be_revoked_recipient_addr assert!(exists_at(to_be_revoked_address), error::not_found(EACCOUNT_DOES_NOT_EXIST)); let addr = signer::address_of(account); let account_resource = borrow_global_mut<Account>(addr); - assert!(option::contains(&account_resource.rotation_capability_offer.for, &to_be_revoked_address), error::not_found(ENO_SUCH_ROTATION_CAPABILITY_OFFER)); + assert!( + option::contains(&account_resource.rotation_capability_offer.for, &to_be_revoked_address), + error::not_found(ENO_SUCH_ROTATION_CAPABILITY_OFFER) + ); revoke_any_rotation_capability(account); }
    @@ -1669,7 +1713,10 @@ has a signer capability offer from accoun assert!(exists_at(to_be_revoked_address), error::not_found(EACCOUNT_DOES_NOT_EXIST)); let addr = signer::address_of(account); let account_resource = borrow_global_mut<Account>(addr); - assert!(option::contains(&account_resource.signer_capability_offer.for, &to_be_revoked_address), error::not_found(ENO_SUCH_SIGNER_CAPABILITY)); + assert!( + option::contains(&account_resource.signer_capability_offer.for, &to_be_revoked_address), + error::not_found(ENO_SUCH_SIGNER_CAPABILITY) + ); revoke_any_signer_capability(account); }
    @@ -1727,7 +1774,10 @@ at the offerer's address. // Check if there's an existing signer capability offer from the offerer. let account_resource = borrow_global<Account>(offerer_address); let addr = signer::address_of(account); - assert!(option::contains(&account_resource.signer_capability_offer.for, &addr), error::not_found(ENO_SUCH_SIGNER_CAPABILITY)); + assert!( + option::contains(&account_resource.signer_capability_offer.for, &addr), + error::not_found(ENO_SUCH_SIGNER_CAPABILITY) + ); create_signer(offerer_address) } @@ -1753,16 +1803,27 @@ Helper functions for authentication key rotation. Implementation -
    fun assert_valid_rotation_proof_signature_and_get_auth_key(scheme: u8, public_key_bytes: vector<u8>, signature: vector<u8>, challenge: &RotationProofChallenge): vector<u8> {
    +
    fun assert_valid_rotation_proof_signature_and_get_auth_key(
    +    scheme: u8,
    +    public_key_bytes: vector<u8>,
    +    signature: vector<u8>,
    +    challenge: &RotationProofChallenge
    +): vector<u8> {
         if (scheme == ED25519_SCHEME) {
             let pk = ed25519::new_unvalidated_public_key_from_bytes(public_key_bytes);
             let sig = ed25519::new_signature_from_bytes(signature);
    -        assert!(ed25519::signature_verify_strict_t(&sig, &pk, *challenge), std::error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE));
    +        assert!(
    +            ed25519::signature_verify_strict_t(&sig, &pk, *challenge),
    +            std::error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE)
    +        );
             ed25519::unvalidated_public_key_to_authentication_key(&pk)
         } else if (scheme == MULTI_ED25519_SCHEME) {
             let pk = multi_ed25519::new_unvalidated_public_key_from_bytes(public_key_bytes);
             let sig = multi_ed25519::new_signature_from_bytes(signature);
    -        assert!(multi_ed25519::signature_verify_strict_t(&sig, &pk, *challenge), std::error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE));
    +        assert!(
    +            multi_ed25519::signature_verify_strict_t(&sig, &pk, *challenge),
    +            std::error::invalid_argument(EINVALID_PROOF_OF_KNOWLEDGE)
    +        );
             multi_ed25519::unvalidated_public_key_to_authentication_key(&pk)
         } else {
             abort error::invalid_argument(EINVALID_SCHEME)
    @@ -1812,18 +1873,23 @@ in the event of key recovery.
             // for interoperability because different dapps can implement this in different ways.
             // If the account with address b calls this function with two valid signatures, it will abort at this step,
             // because address b is not the account's originating address.
    -        assert!(originating_addr == table::remove(address_map, curr_auth_key), error::not_found(EINVALID_ORIGINATING_ADDRESS));
    +        assert!(
    +            originating_addr == table::remove(address_map, curr_auth_key),
    +            error::not_found(EINVALID_ORIGINATING_ADDRESS)
    +        );
         };
     
         // Set `OriginatingAddress[new_auth_key] = originating_address`.
         let new_auth_key = from_bcs::to_address(new_auth_key_vector);
         table::add(address_map, new_auth_key, originating_addr);
     
    -    event::emit<KeyRotation>(KeyRotation {
    -        account: originating_addr,
    -        old_authentication_key: account_resource.authentication_key,
    -        new_authentication_key: new_auth_key_vector,
    -    });
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(KeyRotation {
    +            account: originating_addr,
    +            old_authentication_key: account_resource.authentication_key,
    +            new_authentication_key: new_auth_key_vector,
    +        });
    +    };
         event::emit_event<KeyRotationEvent>(
             &mut account_resource.key_rotation_events,
             KeyRotationEvent {
    @@ -2320,7 +2386,7 @@ OriginatingAddress does not exist under @aptos_framework before the
     ### Function `create_account_if_does_not_exist`
     
     
    -
    fun create_account_if_does_not_exist(account_address: address)
    +
    public fun create_account_if_does_not_exist(account_address: address)
     
    @@ -2974,7 +3040,7 @@ The value of signer_capability_offer.for of Account resource under the signer is let addr = signer::address_of(account); let account_resource = global<Account>(address); aborts_if !exists<Account>(address); - // This enforces high-level requirement 3 of the create_signer module: + // This enforces high-level requirement 3 of the create_signer module: aborts_if !option::spec_contains(account_resource.signer_capability_offer.for,addr); }
    diff --git a/aptos-move/framework/aptos-framework/doc/aggregator_v2.md b/aptos-move/framework/aptos-framework/doc/aggregator_v2.md index 787882cd4179e..842ad22ecdab3 100644 --- a/aptos-move/framework/aptos-framework/doc/aggregator_v2.md +++ b/aptos-move/framework/aptos-framework/doc/aggregator_v2.md @@ -13,6 +13,16 @@ doing try_sub(X,3 dependency. However, reading the aggregator value (i.e. calling read(X)) is a resource-intensive operation that also reduced parallelism, and should be avoided as much as possible. +If you need to capture the value, without revealing it, use snapshot function instead, +which has no parallelism impact. + +From parallelism considerations, there are three different levels of effects: +* enable full parallelism (cannot create conflicts): +max_value, create_*, snapshot, derive_string_concat +* enable speculative parallelism (generally parallel via branch prediction) +try_add, add, try_sub, sub, is_at_least +* create read/write conflicts, as if you were using a regular field +read, read_snapshot, read_derived_string - [Struct `Aggregator`](#0x1_aggregator_v2_Aggregator) @@ -21,11 +31,15 @@ operation that also reduced parallelism, and should be avoided as much as possib - [Constants](#@Constants_0) - [Function `max_value`](#0x1_aggregator_v2_max_value) - [Function `create_aggregator`](#0x1_aggregator_v2_create_aggregator) +- [Function `create_aggregator_with_value`](#0x1_aggregator_v2_create_aggregator_with_value) - [Function `create_unbounded_aggregator`](#0x1_aggregator_v2_create_unbounded_aggregator) +- [Function `create_unbounded_aggregator_with_value`](#0x1_aggregator_v2_create_unbounded_aggregator_with_value) - [Function `try_add`](#0x1_aggregator_v2_try_add) - [Function `add`](#0x1_aggregator_v2_add) - [Function `try_sub`](#0x1_aggregator_v2_try_sub) - [Function `sub`](#0x1_aggregator_v2_sub) +- [Function `is_at_least_impl`](#0x1_aggregator_v2_is_at_least_impl) +- [Function `is_at_least`](#0x1_aggregator_v2_is_at_least) - [Function `read`](#0x1_aggregator_v2_read) - [Function `snapshot`](#0x1_aggregator_v2_snapshot) - [Function `create_snapshot`](#0x1_aggregator_v2_create_snapshot) @@ -40,6 +54,7 @@ operation that also reduced parallelism, and should be avoided as much as possib - [Function `create_unbounded_aggregator`](#@Specification_1_create_unbounded_aggregator) - [Function `try_add`](#@Specification_1_try_add) - [Function `try_sub`](#@Specification_1_try_sub) + - [Function `is_at_least_impl`](#@Specification_1_is_at_least_impl) - [Function `read`](#@Specification_1_read) - [Function `snapshot`](#@Specification_1_snapshot) - [Function `create_snapshot`](#@Specification_1_create_snapshot) @@ -48,6 +63,7 @@ operation that also reduced parallelism, and should be avoided as much as possib
    use 0x1::error;
    +use 0x1::features;
     use 0x1::string;
     
    @@ -278,6 +294,32 @@ EAGGREGATOR_ELEMENT_TYPE_NOT_SUPPORTED raised if called with a different type. + + + + +## Function `create_aggregator_with_value` + + + +
    public fun create_aggregator_with_value<IntElement: copy, drop>(start_value: IntElement, max_value: IntElement): aggregator_v2::Aggregator<IntElement>
    +
    + + + +
    +Implementation + + +
    public fun create_aggregator_with_value<IntElement: copy + drop>(start_value: IntElement, max_value: IntElement): Aggregator<IntElement> {
    +    let aggregator = create_aggregator(max_value);
    +    add(&mut aggregator, start_value);
    +    aggregator
    +}
    +
    + + +
    @@ -305,6 +347,32 @@ EAGGREGATOR_ELEMENT_TYPE_NOT_SUPPORTED raised if called with a different type. + + + + +## Function `create_unbounded_aggregator_with_value` + + + +
    public fun create_unbounded_aggregator_with_value<IntElement: copy, drop>(start_value: IntElement): aggregator_v2::Aggregator<IntElement>
    +
    + + + +
    +Implementation + + +
    public fun create_unbounded_aggregator_with_value<IntElement: copy + drop>(start_value: IntElement): Aggregator<IntElement> {
    +    let aggregator = create_unbounded_aggregator();
    +    add(&mut aggregator, start_value);
    +    aggregator
    +}
    +
    + + +
    @@ -314,6 +382,8 @@ EAGGREGATOR_ELEMENT_TYPE_NOT_SUPPORTED raised if called with a different type. Adds value to aggregator. If addition would exceed the max_value, false is returned, and aggregator value is left unchanged. +Parallelism info: This operation enables speculative parallelism. +
    public fun try_add<IntElement>(aggregator: &mut aggregator_v2::Aggregator<IntElement>, value: IntElement): bool
     
    @@ -335,6 +405,10 @@ If addition would exceed the max_value, false is returned, a ## Function `add` +Adds value to aggregator, unconditionally. +If addition would exceed the max_value, EAGGREGATOR_OVERFLOW exception will be thrown. + +Parallelism info: This operation enables speculative parallelism.
    public fun add<IntElement>(aggregator: &mut aggregator_v2::Aggregator<IntElement>, value: IntElement)
    @@ -362,6 +436,8 @@ If addition would exceed the max_value, false is returned, a
     Subtracts value from aggregator.
     If subtraction would result in a negative value, false is returned, and aggregator value is left unchanged.
     
    +Parallelism info: This operation enables speculative parallelism.
    +
     
     
    public fun try_sub<IntElement>(aggregator: &mut aggregator_v2::Aggregator<IntElement>, value: IntElement): bool
     
    @@ -384,6 +460,8 @@ If subtraction would result in a negative value, false is re ## Function `sub` +Parallelism info: This operation enables speculative parallelism. +
    public fun sub<IntElement>(aggregator: &mut aggregator_v2::Aggregator<IntElement>, value: IntElement)
     
    @@ -401,6 +479,61 @@ If subtraction would result in a negative value, false is re + + + + +## Function `is_at_least_impl` + + + +
    fun is_at_least_impl<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>, min_amount: IntElement): bool
    +
    + + + +
    +Implementation + + +
    native fun is_at_least_impl<IntElement>(aggregator: &Aggregator<IntElement>, min_amount: IntElement): bool;
    +
    + + + +
    + + + +## Function `is_at_least` + +Returns true if aggregator value is larger than or equal to the given min_amount, false otherwise. + +This operation is more efficient and much more parallelization friendly than calling read(agg) > min_amount. +Until traits are deployed, is_at_most/is_equal utility methods can be derived from this one (assuming +1 doesn't overflow): +- for is_at_most(agg, max_amount), you can do !is_at_least(max_amount + 1) +- for is_equal(agg, value), you can do is_at_least(value) && !is_at_least(value + 1) + +Parallelism info: This operation enables speculative parallelism. + + +
    public fun is_at_least<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>, min_amount: IntElement): bool
    +
    + + + +
    +Implementation + + +
    public fun is_at_least<IntElement>(aggregator: &Aggregator<IntElement>, min_amount: IntElement): bool {
    +    assert!(features::aggregator_v2_is_at_least_api_enabled(), EAGGREGATOR_API_V2_NOT_ENABLED);
    +    is_at_least_impl(aggregator, min_amount)
    +}
    +
    + + +
    @@ -409,8 +542,14 @@ If subtraction would result in a negative value, false is re Returns a value stored in this aggregator. Note: This operation is resource-intensive, and reduces parallelism. -(Especially if called in a transaction that also modifies the aggregator, -or has other read/write conflicts) +If you need to capture the value, without revealing it, use snapshot function instead, +which has no parallelism impact. +If called in a transaction that also modifies the aggregator, or has other read/write conflicts, +it will sequentialize that transaction. (i.e. up to concurrency_level times slower) +If called in a separate transaction (i.e. after transaction that modifies aggregator), it might be +up to two times slower. + +Parallelism info: This operation *prevents* speculative parallelism.
    public fun read<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>): IntElement
    @@ -436,6 +575,8 @@ or has other read/write conflicts)
     Returns a wrapper of a current value of an aggregator
     Unlike read(), it is fast and avoids sequential dependencies.
     
    +Parallelism info: This operation enables parallelism.
    +
     
     
    public fun snapshot<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>): aggregator_v2::AggregatorSnapshot<IntElement>
     
    @@ -486,6 +627,8 @@ Note: This operation is resource-intensive, and reduces parallelism. (Especially if called in a transaction that also modifies the aggregator, or has other read/write conflicts) +Parallelism info: This operation *prevents* speculative parallelism. +
    public fun read_snapshot<IntElement>(snapshot: &aggregator_v2::AggregatorSnapshot<IntElement>): IntElement
     
    @@ -512,6 +655,8 @@ Note: This operation is resource-intensive, and reduces parallelism. (Especially if called in a transaction that also modifies the aggregator, or has other read/write conflicts) +Parallelism info: This operation *prevents* speculative parallelism. +
    public fun read_derived_string(snapshot: &aggregator_v2::DerivedStringSnapshot): string::String
     
    @@ -562,6 +707,8 @@ snapshot passed needs to have integer type - currently supported types are u64 a Raises EUNSUPPORTED_AGGREGATOR_SNAPSHOT_TYPE if called with another type. If length of prefix and suffix together exceed 256 bytes, ECONCAT_STRING_LENGTH_TOO_LARGE is raised. +Parallelism info: This operation enables parallelism. +
    public fun derive_string_concat<IntElement>(before: string::String, snapshot: &aggregator_v2::AggregatorSnapshot<IntElement>, after: string::String): aggregator_v2::DerivedStringSnapshot
     
    @@ -691,6 +838,22 @@ DEPRECATED, use derive_string_concat() instead. always raises EAGGREGATOR_FUNCTI +
    pragma opaque;
    +
    + + + + + +### Function `is_at_least_impl` + + +
    fun is_at_least_impl<IntElement>(aggregator: &aggregator_v2::Aggregator<IntElement>, min_amount: IntElement): bool
    +
    + + + +
    pragma opaque;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/aptos_account.md b/aptos-move/framework/aptos-framework/doc/aptos_account.md index 4dfe00fa0c476..7ce465a7d176a 100644 --- a/aptos-move/framework/aptos-framework/doc/aptos_account.md +++ b/aptos-move/framework/aptos-framework/doc/aptos_account.md @@ -7,7 +7,7 @@ - [Resource `DirectTransferConfig`](#0x1_aptos_account_DirectTransferConfig) - [Struct `DirectCoinTransferConfigUpdatedEvent`](#0x1_aptos_account_DirectCoinTransferConfigUpdatedEvent) -- [Struct `AllowDirectTransfers`](#0x1_aptos_account_AllowDirectTransfers) +- [Struct `DirectCoinTransferConfigUpdated`](#0x1_aptos_account_DirectCoinTransferConfigUpdated) - [Constants](#@Constants_0) - [Function `create_account`](#0x1_aptos_account_create_account) - [Function `batch_transfer`](#0x1_aptos_account_batch_transfer) @@ -19,6 +19,12 @@ - [Function `assert_account_is_registered_for_apt`](#0x1_aptos_account_assert_account_is_registered_for_apt) - [Function `set_allow_direct_coin_transfers`](#0x1_aptos_account_set_allow_direct_coin_transfers) - [Function `can_receive_direct_coin_transfers`](#0x1_aptos_account_can_receive_direct_coin_transfers) +- [Function `register_apt`](#0x1_aptos_account_register_apt) +- [Function `fungible_transfer_only`](#0x1_aptos_account_fungible_transfer_only) +- [Function `is_fungible_balance_at_least`](#0x1_aptos_account_is_fungible_balance_at_least) +- [Function `burn_from_fungible_store`](#0x1_aptos_account_burn_from_fungible_store) +- [Function `ensure_primary_fungible_store_exists`](#0x1_aptos_account_ensure_primary_fungible_store_exists) +- [Function `primary_fungible_store_address`](#0x1_aptos_account_primary_fungible_store_address) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) @@ -32,6 +38,10 @@ - [Function `assert_account_is_registered_for_apt`](#@Specification_1_assert_account_is_registered_for_apt) - [Function `set_allow_direct_coin_transfers`](#@Specification_1_set_allow_direct_coin_transfers) - [Function `can_receive_direct_coin_transfers`](#@Specification_1_can_receive_direct_coin_transfers) + - [Function `register_apt`](#@Specification_1_register_apt) + - [Function `fungible_transfer_only`](#@Specification_1_fungible_transfer_only) + - [Function `is_fungible_balance_at_least`](#@Specification_1_is_fungible_balance_at_least) + - [Function `burn_from_fungible_store`](#@Specification_1_burn_from_fungible_store)
    use 0x1::account;
    @@ -40,6 +50,10 @@
     use 0x1::create_signer;
     use 0x1::error;
     use 0x1::event;
    +use 0x1::features;
    +use 0x1::fungible_asset;
    +use 0x1::object;
    +use 0x1::primary_fungible_store;
     use 0x1::signer;
     
    @@ -109,14 +123,14 @@ Event emitted when an account's direct coins transfer config is updated. - + -## Struct `AllowDirectTransfers` +## Struct `DirectCoinTransferConfigUpdated`
    #[event]
    -struct AllowDirectTransfers has drop, store
    +struct DirectCoinTransferConfigUpdated has drop, store
     
    @@ -215,8 +229,8 @@ Basic account creation methods.
    public entry fun create_account(auth_key: address) {
    -    let signer = account::create_account(auth_key);
    -    coin::register<AptosCoin>(&signer);
    +    let account_signer = account::create_account(auth_key);
    +    register_apt(&account_signer);
     }
     
    @@ -279,12 +293,17 @@ This would create the recipient account first, which also registers it to receiv if (!account::exists_at(to)) { create_account(to) }; - // Resource accounts can be created without registering them to receive APT. - // This conveniently does the registration if necessary. - if (!coin::is_account_registered<AptosCoin>(to)) { - coin::register<AptosCoin>(&create_signer(to)); - }; - coin::transfer<AptosCoin>(source, to, amount) + + if (features::operations_default_to_fa_apt_store_enabled()) { + fungible_transfer_only(source, to, amount) + } else { + // Resource accounts can be created without registering them to receive APT. + // This conveniently does the registration if necessary. + if (!coin::is_account_registered<AptosCoin>(to)) { + coin::register<AptosCoin>(&create_signer(to)); + }; + coin::transfer<AptosCoin>(source, to, amount) + } }
    @@ -374,9 +393,9 @@ This would create the recipient account first and register it to receive the Coi if (!account::exists_at(to)) { create_account(to); spec { - assert coin::is_account_registered<AptosCoin>(to); + assert coin::spec_is_account_registered<AptosCoin>(to); assume aptos_std::type_info::type_of<CoinType>() == aptos_std::type_info::type_of<AptosCoin>() ==> - coin::is_account_registered<CoinType>(to); + coin::spec_is_account_registered<CoinType>(to); }; }; if (!coin::is_account_registered<CoinType>(to)) { @@ -469,8 +488,10 @@ Set whether account can receiv }; direct_transfer_config.allow_arbitrary_coin_transfers = allow; - emit( - AllowDirectTransfers { account: addr, new_allow_direct_transfers: allow }); + + if (std::features::module_event_migration_enabled()) { + emit(DirectCoinTransferConfigUpdated { account: addr, new_allow_direct_transfers: allow }); + }; emit_event( &mut direct_transfer_config.update_coin_transfer_events, DirectCoinTransferConfigUpdatedEvent { new_allow_direct_transfers: allow }); @@ -479,8 +500,9 @@ Set whether account can receiv allow_arbitrary_coin_transfers: allow, update_coin_transfer_events: new_event_handle<DirectCoinTransferConfigUpdatedEvent>(account), }; - emit( - AllowDirectTransfers { account: addr, new_allow_direct_transfers: allow }); + if (std::features::module_event_migration_enabled()) { + emit(DirectCoinTransferConfigUpdated { account: addr, new_allow_direct_transfers: allow }); + }; emit_event( &mut direct_transfer_config.update_coin_transfer_events, DirectCoinTransferConfigUpdatedEvent { new_allow_direct_transfers: allow }); @@ -521,6 +543,188 @@ By default, this returns true if an account has not explicitly set whether the c + + + + +## Function `register_apt` + + + +
    public(friend) fun register_apt(account_signer: &signer)
    +
    + + + +
    +Implementation + + +
    public(friend) fun register_apt(account_signer: &signer) {
    +    if (features::new_accounts_default_to_fa_apt_store_enabled()) {
    +        ensure_primary_fungible_store_exists(signer::address_of(account_signer));
    +    } else {
    +        coin::register<AptosCoin>(account_signer);
    +    }
    +}
    +
    + + + +
    + + + +## Function `fungible_transfer_only` + +APT Primary Fungible Store specific specialized functions, +Utilized internally once migration of APT to FungibleAsset is complete. +Convenient function to transfer APT to a recipient account that might not exist. +This would create the recipient APT PFS first, which also registers it to receive APT, before transferring. +TODO: once migration is complete, rename to just "transfer_only" and make it an entry function (for cheapest way +to transfer APT) - if we want to allow APT PFS without account itself + + +
    fun fungible_transfer_only(source: &signer, to: address, amount: u64)
    +
    + + + +
    +Implementation + + +
    fun fungible_transfer_only(
    +    source: &signer, to: address, amount: u64
    +) {
    +    let sender_store = ensure_primary_fungible_store_exists(signer::address_of(source));
    +    let recipient_store = ensure_primary_fungible_store_exists(to);
    +
    +    // use internal APIs, as they skip:
    +    // - owner, frozen and dispatchable checks
    +    // as APT cannot be frozen or have dispatch, and PFS cannot be transfered
    +    // (PFS could potentially be burned. regular transfer would permanently unburn the store.
    +    // Ignoring the check here has the equivalent of unburning, transfers, and then burning again)
    +    fungible_asset::deposit_internal(recipient_store, fungible_asset::withdraw_internal(sender_store, amount));
    +}
    +
    + + + +
    + + + +## Function `is_fungible_balance_at_least` + +Is balance from APT Primary FungibleStore at least the given amount + + +
    public(friend) fun is_fungible_balance_at_least(account: address, amount: u64): bool
    +
    + + + +
    +Implementation + + +
    public(friend) fun is_fungible_balance_at_least(account: address, amount: u64): bool {
    +    let store_addr = primary_fungible_store_address(account);
    +    fungible_asset::is_address_balance_at_least(store_addr, amount)
    +}
    +
    + + + +
    + + + +## Function `burn_from_fungible_store` + +Burn from APT Primary FungibleStore + + +
    public(friend) fun burn_from_fungible_store(ref: &fungible_asset::BurnRef, account: address, amount: u64)
    +
    + + + +
    +Implementation + + +
    public(friend) fun burn_from_fungible_store(
    +    ref: &BurnRef,
    +    account: address,
    +    amount: u64,
    +) {
    +    // Skip burning if amount is zero. This shouldn't error out as it's called as part of transaction fee burning.
    +    if (amount != 0) {
    +        let store_addr = primary_fungible_store_address(account);
    +        fungible_asset::address_burn_from(ref, store_addr, amount);
    +    };
    +}
    +
    + + + +
    + + + +## Function `ensure_primary_fungible_store_exists` + +Ensure that APT Primary FungibleStore exists (and create if it doesn't) + + +
    fun ensure_primary_fungible_store_exists(owner: address): address
    +
    + + + +
    +Implementation + + +
    inline fun ensure_primary_fungible_store_exists(owner: address): address {
    +    let store_addr = primary_fungible_store_address(owner);
    +    if (fungible_asset::store_exists(store_addr)) {
    +        store_addr
    +    } else {
    +        object::object_address(&primary_fungible_store::create_primary_store(owner, object::address_to_object<Metadata>(@aptos_fungible_asset)))
    +    }
    +}
    +
    + + + +
    + + + +## Function `primary_fungible_store_address` + +Address of APT Primary Fungible Store + + +
    fun primary_fungible_store_address(account: address): address
    +
    + + + +
    +Implementation + + +
    inline fun primary_fungible_store_address(account: address): address {
    +    object::create_user_derived_object_address(account, @aptos_fungible_asset)
    +}
    +
    + + +
    @@ -625,10 +829,9 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to
    // This enforces high-level requirement 1:
    +pragma aborts_if_is_partial;
     include CreateAccountAbortsIf;
     ensures exists<account::Account>(auth_key);
    -// This enforces high-level requirement 2:
    -ensures exists<coin::CoinStore<AptosCoin>>(auth_key);
     
    @@ -715,7 +918,8 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to -
    let account_addr_source = signer::address_of(source);
    +
    pragma verify = false;
    +let account_addr_source = signer::address_of(source);
     requires account_addr_source != to;
     include CreateAccountTransferAbortsIf;
     include GuidAbortsIf<AptosCoin>;
    @@ -770,9 +974,7 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to
     aborts_if exists i in 0..len(recipients):
         account::exists_at(recipients[i]) && !exists<coin::CoinStore<CoinType>>(recipients[i]) && global<account::Account>(recipients[i]).guid_creation_num + 2 > MAX_U64;
     aborts_if exists i in 0..len(recipients):
    -    !coin::is_account_registered<CoinType>(recipients[i]) && !type_info::spec_is_struct<CoinType>();
    -aborts_if exists i in 0..len(recipients):
    -    !coin::is_account_registered<CoinType>(recipients[i]) && !can_receive_direct_coin_transfers(recipients[i]);
    +    !coin::spec_is_account_registered<CoinType>(recipients[i]) && !type_info::spec_is_struct<CoinType>();
     
    @@ -788,7 +990,8 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to -
    let account_addr_source = signer::address_of(from);
    +
    pragma verify = false;
    +let account_addr_source = signer::address_of(from);
     requires account_addr_source != to;
     include CreateAccountTransferAbortsIf;
     include WithdrawAbortsIf<CoinType>;
    @@ -802,88 +1005,6 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to
     
     
     
    -
    -
    -
    -
    -
    schema CreateAccountTransferAbortsIf {
    -    to: address;
    -    aborts_if !account::exists_at(to) && length_judgment(to);
    -    aborts_if !account::exists_at(to) && (to == @vm_reserved || to == @aptos_framework || to == @aptos_token);
    -}
    -
    - - - - - - - -
    schema WithdrawAbortsIf<CoinType> {
    -    from: &signer;
    -    amount: u64;
    -    let account_addr_source = signer::address_of(from);
    -    let coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    -    let balance_source = coin_store_source.coin.value;
    -    aborts_if !exists<coin::CoinStore<CoinType>>(account_addr_source);
    -    aborts_if coin_store_source.frozen;
    -    aborts_if balance_source < amount;
    -}
    -
    - - - - - - - -
    schema GuidAbortsIf<CoinType> {
    -    to: address;
    -    let acc = global<account::Account>(to);
    -    aborts_if account::exists_at(to) && !exists<coin::CoinStore<CoinType>>(to) && acc.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
    -    aborts_if account::exists_at(to) && !exists<coin::CoinStore<CoinType>>(to) && acc.guid_creation_num + 2 > MAX_U64;
    -}
    -
    - - - - - - - -
    schema RegistCoinAbortsIf<CoinType> {
    -    to: address;
    -    aborts_if !coin::is_account_registered<CoinType>(to) && !type_info::spec_is_struct<CoinType>();
    -    aborts_if exists<aptos_framework::account::Account>(to)
    -        && !coin::is_account_registered<CoinType>(to) && !can_receive_direct_coin_transfers(to);
    -    aborts_if type_info::type_of<CoinType>() != type_info::type_of<AptosCoin>()
    -        && !coin::is_account_registered<CoinType>(to) && !can_receive_direct_coin_transfers(to);
    -}
    -
    - - - - - - - -
    schema TransferEnsures<CoinType> {
    -    to: address;
    -    account_addr_source: address;
    -    amount: u64;
    -    let if_exist_account = exists<account::Account>(to);
    -    let if_exist_coin = exists<coin::CoinStore<CoinType>>(to);
    -    let coin_store_to = global<coin::CoinStore<CoinType>>(to);
    -    let coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    -    let post p_coin_store_to = global<coin::CoinStore<CoinType>>(to);
    -    let post p_coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    -    ensures coin_store_source.coin.value - amount == p_coin_store_source.coin.value;
    -    ensures if_exist_account && if_exist_coin ==> coin_store_to.coin.value + amount == p_coin_store_to.coin.value;
    -}
    -
    - - - ### Function `deposit_coins` @@ -895,7 +1016,8 @@ Limit the address of auth_key is not @vm_reserved / @aptos_framework / @aptos_to -
    include CreateAccountTransferAbortsIf;
    +
    pragma verify = false;
    +include CreateAccountTransferAbortsIf;
     include GuidAbortsIf<CoinType>;
     include RegistCoinAbortsIf<CoinType>;
     let if_exist_coin = exists<coin::CoinStore<CoinType>>(to);
    @@ -939,8 +1061,9 @@ Check if the address existed.
     Check if the AptosCoin under the address existed.
     
     
    -
    aborts_if !account::exists_at(addr);
    -aborts_if !coin::is_account_registered<AptosCoin>(addr);
    +
    pragma aborts_if_is_partial;
    +aborts_if !account::exists_at(addr);
    +aborts_if !coin::spec_is_account_registered<AptosCoin>(addr);
     
    @@ -956,10 +1079,7 @@ Check if the AptosCoin under the address existed. -
    let addr = signer::address_of(account);
    -include !exists<DirectTransferConfig>(addr) ==> account::NewEventHandleAbortsIf;
    -// This enforces high-level requirement 4:
    -ensures global<DirectTransferConfig>(addr).allow_arbitrary_coin_transfers == allow;
    +
    pragma verify = false;
     
    @@ -985,4 +1105,148 @@ Check if the AptosCoin under the address existed.
    + + + +### Function `register_apt` + + +
    public(friend) fun register_apt(account_signer: &signer)
    +
    + + + + +
    pragma verify = false;
    +
    + + + + + +### Function `fungible_transfer_only` + + +
    fun fungible_transfer_only(source: &signer, to: address, amount: u64)
    +
    + + + + +
    pragma verify = false;
    +
    + + + + + +### Function `is_fungible_balance_at_least` + + +
    public(friend) fun is_fungible_balance_at_least(account: address, amount: u64): bool
    +
    + + + + +
    pragma verify = false;
    +
    + + + + + +### Function `burn_from_fungible_store` + + +
    public(friend) fun burn_from_fungible_store(ref: &fungible_asset::BurnRef, account: address, amount: u64)
    +
    + + + + +
    pragma verify = false;
    +
    + + + + + + + +
    schema CreateAccountTransferAbortsIf {
    +    to: address;
    +    aborts_if !account::exists_at(to) && length_judgment(to);
    +    aborts_if !account::exists_at(to) && (to == @vm_reserved || to == @aptos_framework || to == @aptos_token);
    +}
    +
    + + + + + + + +
    schema WithdrawAbortsIf<CoinType> {
    +    from: &signer;
    +    amount: u64;
    +    let account_addr_source = signer::address_of(from);
    +    let coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    +    let balance_source = coin_store_source.coin.value;
    +    aborts_if !exists<coin::CoinStore<CoinType>>(account_addr_source);
    +    aborts_if coin_store_source.frozen;
    +    aborts_if balance_source < amount;
    +}
    +
    + + + + + + + +
    schema GuidAbortsIf<CoinType> {
    +    to: address;
    +    let acc = global<account::Account>(to);
    +    aborts_if account::exists_at(to) && !exists<coin::CoinStore<CoinType>>(to) && acc.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
    +    aborts_if account::exists_at(to) && !exists<coin::CoinStore<CoinType>>(to) && acc.guid_creation_num + 2 > MAX_U64;
    +}
    +
    + + + + + + + +
    schema RegistCoinAbortsIf<CoinType> {
    +    to: address;
    +    aborts_if !coin::spec_is_account_registered<CoinType>(to) && !type_info::spec_is_struct<CoinType>();
    +    aborts_if exists<aptos_framework::account::Account>(to);
    +    aborts_if type_info::type_of<CoinType>() != type_info::type_of<AptosCoin>();
    +}
    +
    + + + + + + + +
    schema TransferEnsures<CoinType> {
    +    to: address;
    +    account_addr_source: address;
    +    amount: u64;
    +    let if_exist_account = exists<account::Account>(to);
    +    let if_exist_coin = exists<coin::CoinStore<CoinType>>(to);
    +    let coin_store_to = global<coin::CoinStore<CoinType>>(to);
    +    let coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    +    let post p_coin_store_to = global<coin::CoinStore<CoinType>>(to);
    +    let post p_coin_store_source = global<coin::CoinStore<CoinType>>(account_addr_source);
    +    ensures coin_store_source.coin.value - amount == p_coin_store_source.coin.value;
    +    ensures if_exist_account && if_exist_coin ==> coin_store_to.coin.value + amount == p_coin_store_to.coin.value;
    +}
    +
    + + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/aptos_coin.md b/aptos-move/framework/aptos-framework/doc/aptos_coin.md index 172f2f2ce4178..30f3eae067ed8 100644 --- a/aptos-move/framework/aptos-framework/doc/aptos_coin.md +++ b/aptos-move/framework/aptos-framework/doc/aptos_coin.md @@ -286,6 +286,7 @@ and accounts have been initialized during genesis. Can only be called during genesis for tests to grant mint capability to aptos framework and core resources accounts. +Expects account and APT store to be registered before calling.
    public(friend) fun configure_accounts_for_test(aptos_framework: &signer, core_resources: &signer, mint_cap: coin::MintCapability<aptos_coin::AptosCoin>)
    @@ -305,7 +306,6 @@ accounts.
         system_addresses::assert_aptos_framework(aptos_framework);
     
         // Mint the core resource account AptosCoin for gas so it can execute system transactions.
    -    coin::register<AptosCoin>(core_resources);
         let coins = coin::mint<AptosCoin>(
             18446744073709551615,
             &mint_cap,
    diff --git a/aptos-move/framework/aptos-framework/doc/aptos_governance.md b/aptos-move/framework/aptos-framework/doc/aptos_governance.md
    index 0c192d0da68cc..4daf73a12bded 100644
    --- a/aptos-move/framework/aptos-framework/doc/aptos_governance.md
    +++ b/aptos-move/framework/aptos-framework/doc/aptos_governance.md
    @@ -26,6 +26,9 @@ on a proposal multiple times as long as the total voting power of these votes do
     -  [Struct `CreateProposalEvent`](#0x1_aptos_governance_CreateProposalEvent)
     -  [Struct `VoteEvent`](#0x1_aptos_governance_VoteEvent)
     -  [Struct `UpdateConfigEvent`](#0x1_aptos_governance_UpdateConfigEvent)
    +-  [Struct `CreateProposal`](#0x1_aptos_governance_CreateProposal)
    +-  [Struct `Vote`](#0x1_aptos_governance_Vote)
    +-  [Struct `UpdateConfig`](#0x1_aptos_governance_UpdateConfig)
     -  [Constants](#@Constants_0)
     -  [Function `store_signer_cap`](#0x1_aptos_governance_store_signer_cap)
     -  [Function `initialize`](#0x1_aptos_governance_initialize)
    @@ -39,6 +42,8 @@ on a proposal multiple times as long as the total voting power of these votes do
     -  [Function `create_proposal`](#0x1_aptos_governance_create_proposal)
     -  [Function `create_proposal_v2`](#0x1_aptos_governance_create_proposal_v2)
     -  [Function `create_proposal_v2_impl`](#0x1_aptos_governance_create_proposal_v2_impl)
    +-  [Function `batch_vote`](#0x1_aptos_governance_batch_vote)
    +-  [Function `batch_partial_vote`](#0x1_aptos_governance_batch_partial_vote)
     -  [Function `vote`](#0x1_aptos_governance_vote)
     -  [Function `partial_vote`](#0x1_aptos_governance_partial_vote)
     -  [Function `vote_internal`](#0x1_aptos_governance_vote_internal)
    @@ -72,6 +77,8 @@ on a proposal multiple times as long as the total voting power of these votes do
         -  [Function `create_proposal`](#@Specification_1_create_proposal)
         -  [Function `create_proposal_v2`](#@Specification_1_create_proposal_v2)
         -  [Function `create_proposal_v2_impl`](#@Specification_1_create_proposal_v2_impl)
    +    -  [Function `batch_vote`](#@Specification_1_batch_vote)
    +    -  [Function `batch_partial_vote`](#@Specification_1_batch_partial_vote)
         -  [Function `vote`](#@Specification_1_vote)
         -  [Function `partial_vote`](#@Specification_1_partial_vote)
         -  [Function `vote_internal`](#@Specification_1_vote_internal)
    @@ -113,6 +120,7 @@ on a proposal multiple times as long as the total voting power of these votes do
     use 0x1::system_addresses;
     use 0x1::table;
     use 0x1::timestamp;
    +use 0x1::vector;
     use 0x1::voting;
     
    @@ -461,6 +469,153 @@ Event emitted when the governance configs are updated. +
    +Fields + + +
    +
    +min_voting_threshold: u128 +
    +
    + +
    +
    +required_proposer_stake: u64 +
    +
    + +
    +
    +voting_duration_secs: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `CreateProposal` + +Event emitted when a proposal is created. + + +
    #[event]
    +struct CreateProposal has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposer: address +
    +
    + +
    +
    +stake_pool: address +
    +
    + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +execution_hash: vector<u8> +
    +
    + +
    +
    +proposal_metadata: simple_map::SimpleMap<string::String, vector<u8>> +
    +
    + +
    +
    + + +
    + + + +## Struct `Vote` + +Event emitted when there's a vote on a proposa; + + +
    #[event]
    +struct Vote has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    +stake_pool: address +
    +
    + +
    +
    +num_votes: u64 +
    +
    + +
    +
    +should_pass: bool +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateConfig` + +Event emitted when the governance configs are updated. + + +
    #[event]
    +struct UpdateConfig has drop, store
    +
    + + +
    Fields @@ -688,7 +843,10 @@ Stores the signer capability for a given address. system_addresses::assert_framework_reserved(signer_address); if (!exists<GovernanceResponsbility>(@aptos_framework)) { - move_to(aptos_framework, GovernanceResponsbility { signer_caps: simple_map::create<address, SignerCapability>() }); + move_to( + aptos_framework, + GovernanceResponsbility { signer_caps: simple_map::create<address, SignerCapability>() } + ); }; let signer_caps = &mut borrow_global_mut<GovernanceResponsbility>(@aptos_framework).signer_caps; @@ -780,6 +938,15 @@ AptosGovernance. governance_config.min_voting_threshold = min_voting_threshold; governance_config.required_proposer_stake = required_proposer_stake; + if (std::features::module_event_migration_enabled()) { + event::emit( + UpdateConfig { + min_voting_threshold, + required_proposer_stake, + voting_duration_secs + }, + ) + }; let events = borrow_global_mut<GovernanceEvents>(@aptos_framework); event::emit_event<UpdateConfigEvent>( &mut events.update_config_events, @@ -954,10 +1121,16 @@ Note: a stake pool's voting power on a proposal could increase over time(e.g. re Implementation -
    public fun get_remaining_voting_power(stake_pool: address, proposal_id: u64): u64 acquires VotingRecords, VotingRecordsV2 {
    +
    public fun get_remaining_voting_power(
    +    stake_pool: address,
    +    proposal_id: u64
    +): u64 acquires VotingRecords, VotingRecordsV2 {
         assert_voting_initialization();
     
    -    let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id);
    +    let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>(
    +        @aptos_framework,
    +        proposal_id
    +    );
         let lockup_until = stake::get_lockup_secs(stake_pool);
         // The voter's stake needs to be locked up at least as long as the proposal's expiration.
         // Also no one can vote on a expired proposal.
    @@ -1046,7 +1219,14 @@ only the exact script with matching hash can be successfully executed.
         metadata_hash: vector<u8>,
         is_multi_step_proposal: bool,
     ) acquires GovernanceConfig, GovernanceEvents {
    -    create_proposal_v2_impl(proposer, stake_pool, execution_hash, metadata_location, metadata_hash, is_multi_step_proposal);
    +    create_proposal_v2_impl(
    +        proposer,
    +        stake_pool,
    +        execution_hash,
    +        metadata_location,
    +        metadata_hash,
    +        is_multi_step_proposal
    +    );
     }
     
    @@ -1082,7 +1262,10 @@ Return proposal_id when a proposal is successfully created. is_multi_step_proposal: bool, ): u64 acquires GovernanceConfig, GovernanceEvents { let proposer_address = signer::address_of(proposer); - assert!(stake::get_delegated_voter(stake_pool) == proposer_address, error::invalid_argument(ENOT_DELEGATED_VOTER)); + assert!( + stake::get_delegated_voter(stake_pool) == proposer_address, + error::invalid_argument(ENOT_DELEGATED_VOTER) + ); // The proposer's stake needs to be at least the required bond amount. let governance_config = borrow_global<GovernanceConfig>(@aptos_framework); @@ -1127,6 +1310,17 @@ Return proposal_id when a proposal is successfully created. is_multi_step_proposal, ); + if (std::features::module_event_migration_enabled()) { + event::emit( + CreateProposal { + proposal_id, + proposer: proposer_address, + stake_pool, + execution_hash, + proposal_metadata, + }, + ); + }; let events = borrow_global_mut<GovernanceEvents>(@aptos_framework); event::emit_event<CreateProposalEvent>( &mut events.create_proposal_events, @@ -1144,6 +1338,71 @@ Return proposal_id when a proposal is successfully created. +
    + + + +## Function `batch_vote` + +Vote on proposal with proposal_id and all voting power from multiple stake_pools. + + +
    public entry fun batch_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, should_pass: bool)
    +
    + + + +
    +Implementation + + +
    public entry fun batch_vote(
    +    voter: &signer,
    +    stake_pools: vector<address>,
    +    proposal_id: u64,
    +    should_pass: bool,
    +) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
    +    vector::for_each(stake_pools, |stake_pool| {
    +        vote_internal(voter, stake_pool, proposal_id, MAX_U64, should_pass);
    +    });
    +}
    +
    + + + +
    + + + +## Function `batch_partial_vote` + +Batch vote on proposal with proposal_id and specified voting power from multiple stake_pools. + + +
    public entry fun batch_partial_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, voting_power: u64, should_pass: bool)
    +
    + + + +
    +Implementation + + +
    public entry fun batch_partial_vote(
    +    voter: &signer,
    +    stake_pools: vector<address>,
    +    proposal_id: u64,
    +    voting_power: u64,
    +    should_pass: bool,
    +) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
    +    vector::for_each(stake_pools, |stake_pool| {
    +        vote_internal(voter, stake_pool, proposal_id, voting_power, should_pass);
    +    });
    +}
    +
    + + +
    @@ -1167,7 +1426,7 @@ Vote on proposal with proposal_id and all voting power from s stake_pool: address, proposal_id: u64, should_pass: bool, -) acquires ApprovedExecutionHashes, GovernanceEvents, VotingRecords, VotingRecordsV2 { +) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents { vote_internal(voter, stake_pool, proposal_id, MAX_U64, should_pass); }
    @@ -1198,7 +1457,7 @@ Vote on proposal with proposal_id and specified voting power from < proposal_id: u64, voting_power: u64, should_pass: bool, -) acquires ApprovedExecutionHashes, GovernanceEvents, VotingRecords, VotingRecordsV2 { +) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents { vote_internal(voter, stake_pool, proposal_id, voting_power, should_pass); }
    @@ -1232,12 +1491,15 @@ cannot vote on the proposal even after partial governance voting is enabled. proposal_id: u64, voting_power: u64, should_pass: bool, -) acquires ApprovedExecutionHashes, GovernanceEvents, VotingRecords, VotingRecordsV2 { +) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents { let voter_address = signer::address_of(voter); assert!(stake::get_delegated_voter(stake_pool) == voter_address, error::invalid_argument(ENOT_DELEGATED_VOTER)); // The voter's stake needs to be locked up at least as long as the proposal's expiration. - let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id); + let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>( + @aptos_framework, + proposal_id + ); assert!( stake::get_lockup_secs(stake_pool) >= proposal_expiration, error::invalid_argument(EINSUFFICIENT_STAKE_LOCKUP), @@ -1246,7 +1508,7 @@ cannot vote on the proposal even after partial governance voting is enabled. // If a stake pool has already voted on a proposal before partial governance voting is enabled, // `get_remaining_voting_power` returns 0. let staking_pool_voting_power = get_remaining_voting_power(stake_pool, proposal_id); - voting_power= min(voting_power, staking_pool_voting_power); + voting_power = min(voting_power, staking_pool_voting_power); // Short-circuit if the voter has no voting power. assert!(voting_power > 0, error::invalid_argument(ENO_VOTING_POWER)); @@ -1276,6 +1538,17 @@ cannot vote on the proposal even after partial governance voting is enabled. table::add(&mut voting_records.votes, record_key, true); }; + if (std::features::module_event_migration_enabled()) { + event::emit( + Vote { + proposal_id, + voter: voter_address, + stake_pool, + num_votes: voting_power, + should_pass, + }, + ); + }; let events = borrow_global_mut<GovernanceEvents>(@aptos_framework); event::emit_event<VoteEvent>( &mut events.vote_events, @@ -1382,7 +1655,10 @@ than yes). Implementation -
    public fun resolve(proposal_id: u64, signer_address: address): signer acquires ApprovedExecutionHashes, GovernanceResponsbility {
    +
    public fun resolve(
    +    proposal_id: u64,
    +    signer_address: address
    +): signer acquires ApprovedExecutionHashes, GovernanceResponsbility {
         voting::resolve<GovernanceProposal>(@aptos_framework, proposal_id);
         remove_approved_hash(proposal_id);
         get_signer(signer_address)
    @@ -1409,7 +1685,11 @@ Resolve a successful multi-step proposal. This would fail if the proposal is not
     Implementation
     
     
    -
    public fun resolve_multi_step_proposal(proposal_id: u64, signer_address: address, next_execution_hash: vector<u8>): signer acquires GovernanceResponsbility, ApprovedExecutionHashes {
    +
    public fun resolve_multi_step_proposal(
    +    proposal_id: u64,
    +    signer_address: address,
    +    next_execution_hash: vector<u8>
    +): signer acquires GovernanceResponsbility, ApprovedExecutionHashes {
         voting::resolve_proposal_v2<GovernanceProposal>(@aptos_framework, proposal_id, next_execution_hash);
         // If the current step is the last step of this multi-step proposal,
         // we will remove the execution hash from the ApprovedExecutionHashes map.
    @@ -1692,7 +1972,10 @@ Return a signer for making changes to 0x1 as part of on-chain governance proposa
     Implementation
     
     
    -
    fun create_proposal_metadata(metadata_location: vector<u8>, metadata_hash: vector<u8>): SimpleMap<String, vector<u8>> {
    +
    fun create_proposal_metadata(
    +    metadata_location: vector<u8>,
    +    metadata_hash: vector<u8>
    +): SimpleMap<String, vector<u8>> {
         assert!(string::length(&utf8(metadata_location)) <= 256, error::invalid_argument(EMETADATA_LOCATION_TOO_LONG));
         assert!(string::length(&utf8(metadata_hash)) <= 256, error::invalid_argument(EMETADATA_HASH_TOO_LONG));
     
    @@ -2198,77 +2481,35 @@ The same as spec of 
     
    -
    +### Function `batch_vote`
     
     
    -
    schema CreateProposalAbortsIf {
    -    proposer: &signer;
    -    stake_pool: address;
    -    execution_hash: vector<u8>;
    -    metadata_location: vector<u8>;
    -    metadata_hash: vector<u8>;
    -    include VotingGetDelegatedVoterAbortsIf { sign: proposer };
    -    include AbortsIfNotGovernanceConfig;
    -    include GetVotingPowerAbortsIf { pool_address: stake_pool };
    -    let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
    -    let allow_validator_set_change = staking_config.allow_validator_set_change;
    -    let stake_pool_res = global<stake::StakePool>(stake_pool);
    -    let stake_balance_0 = stake_pool_res.active.value + stake_pool_res.pending_active.value + stake_pool_res.pending_inactive.value;
    -    let stake_balance_1 = stake_pool_res.active.value + stake_pool_res.pending_inactive.value;
    -    let stake_balance_2 = 0;
    -    let governance_config = global<GovernanceConfig>(@aptos_framework);
    -    let required_proposer_stake = governance_config.required_proposer_stake;
    -    // This enforces high-level requirement 2:
    -    aborts_if allow_validator_set_change && stake_balance_0 < required_proposer_stake;
    -    aborts_if !allow_validator_set_change && stake::spec_is_current_epoch_validator(stake_pool) && stake_balance_1 < required_proposer_stake;
    -    aborts_if !allow_validator_set_change && !stake::spec_is_current_epoch_validator(stake_pool) && stake_balance_2 < required_proposer_stake;
    -    aborts_if !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
    -    let current_time = timestamp::spec_now_seconds();
    -    let proposal_expiration = current_time + governance_config.voting_duration_secs;
    -    aborts_if stake_pool_res.locked_until_secs < proposal_expiration;
    -    include CreateProposalMetadataAbortsIf;
    -    let addr = aptos_std::type_info::type_of<AptosCoin>().account_address;
    -    aborts_if !exists<coin::CoinInfo<AptosCoin>>(addr);
    -    let maybe_supply = global<coin::CoinInfo<AptosCoin>>(addr).supply;
    -    let supply = option::spec_borrow(maybe_supply);
    -    let total_supply = aptos_framework::optional_aggregator::optional_aggregator_value(supply);
    -    let early_resolution_vote_threshold_value = total_supply / 2 + 1;
    -    aborts_if option::spec_is_some(maybe_supply) && governance_config.min_voting_threshold > early_resolution_vote_threshold_value;
    -    aborts_if len(execution_hash) == 0;
    -    aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
    -    let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
    -    let proposal_id = voting_forum.next_proposal_id;
    -    aborts_if proposal_id + 1 > MAX_U64;
    -    let post post_voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
    -    let post post_next_proposal_id = post_voting_forum.next_proposal_id;
    -    ensures post_next_proposal_id == proposal_id + 1;
    -    aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
    -    aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
    -    aborts_if table::spec_contains(voting_forum.proposals,proposal_id);
    -    ensures table::spec_contains(post_voting_forum.proposals, proposal_id);
    -    aborts_if !exists<GovernanceEvents>(@aptos_framework);
    -}
    +
    public entry fun batch_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, should_pass: bool)
     
    - +
    pragma verify = false;
    +
    -
    schema VotingGetDelegatedVoterAbortsIf {
    -    stake_pool: address;
    -    sign: signer;
    -    let addr = signer::address_of(sign);
    -    let stake_pool_res = global<stake::StakePool>(stake_pool);
    -    aborts_if !exists<stake::StakePool>(stake_pool);
    -    aborts_if stake_pool_res.delegated_voter != addr;
    -}
    +
    +
    +
    +### Function `batch_partial_vote`
    +
    +
    +
    public entry fun batch_partial_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, voting_power: u64, should_pass: bool)
    +
    + + + + +
    pragma verify = false;
     
    @@ -2682,10 +2923,10 @@ Address @aptos_framework must exist ApprovedExecutionHashes and GovernancePropos -
    pragma verify_duration_estimate = 600;
    +
    pragma verify = false;
     aborts_if !system_addresses::is_aptos_framework_address(signer::address_of(aptos_framework));
     include reconfiguration_with_dkg::FinishRequirement {
    -    account: aptos_framework
    +    framework: aptos_framework
     };
     include stake::GetReconfigStartTimeRequirement;
     include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply;
    @@ -2709,10 +2950,10 @@ Address @aptos_framework must exist ApprovedExecutionHashes and GovernancePropos
     
     
     
    -
    pragma verify_duration_estimate = 600;
    +
    pragma verify = false;
     let address = signer::address_of(aptos_framework);
     include reconfiguration_with_dkg::FinishRequirement {
    -    account: aptos_framework
    +    framework: aptos_framework
     };
     
    @@ -2758,11 +2999,11 @@ Signer address must be @aptos_framework. Address @aptos_framework must exist GovernanceConfig and GovernanceEvents. -
    pragma verify_duration_estimate = 600;
    +
    pragma verify = false;
     let addr = signer::address_of(aptos_framework);
     aborts_if addr != @aptos_framework;
     include reconfiguration_with_dkg::FinishRequirement {
    -    account: aptos_framework
    +    framework: aptos_framework
     };
     include stake::GetReconfigStartTimeRequirement;
     include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply;
    diff --git a/aptos-move/framework/aptos-framework/doc/block.md b/aptos-move/framework/aptos-framework/doc/block.md
    index 6e25b59ea1d9c..f5c4430007732 100644
    --- a/aptos-move/framework/aptos-framework/doc/block.md
    +++ b/aptos-move/framework/aptos-framework/doc/block.md
    @@ -10,6 +10,8 @@ This module defines a struct storing the metadata of the block and new block eve
     -  [Resource `CommitHistory`](#0x1_block_CommitHistory)
     -  [Struct `NewBlockEvent`](#0x1_block_NewBlockEvent)
     -  [Struct `UpdateEpochIntervalEvent`](#0x1_block_UpdateEpochIntervalEvent)
    +-  [Struct `NewBlock`](#0x1_block_NewBlock)
    +-  [Struct `UpdateEpochInterval`](#0x1_block_UpdateEpochInterval)
     -  [Constants](#@Constants_0)
     -  [Function `initialize`](#0x1_block_initialize)
     -  [Function `initialize_commit_history`](#0x1_block_initialize_commit_history)
    @@ -225,6 +227,112 @@ Event emitted when a proposal is created.
     
     
     
    +
    +Fields + + +
    +
    +old_epoch_interval: u64 +
    +
    + +
    +
    +new_epoch_interval: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `NewBlock` + +Should be in-sync with NewBlockEvent rust struct in new_block.rs + + +
    #[event]
    +struct NewBlock has drop, store
    +
    + + + +
    +Fields + + +
    +
    +hash: address +
    +
    + +
    +
    +epoch: u64 +
    +
    + +
    +
    +round: u64 +
    +
    + +
    +
    +height: u64 +
    +
    + +
    +
    +previous_block_votes_bitvec: vector<u8> +
    +
    + +
    +
    +proposer: address +
    +
    + +
    +
    +failed_proposer_indices: vector<u64> +
    +
    + +
    +
    +time_microseconds: u64 +
    +
    + On-chain time during the block at the given height +
    +
    + + +
    + + + +## Struct `UpdateEpochInterval` + +Event emitted when a proposal is created. + + +
    #[event]
    +struct UpdateEpochInterval has drop, store
    +
    + + +
    Fields @@ -401,6 +509,11 @@ Can only be called as part of the Aptos governance proposal process established let old_epoch_interval = block_resource.epoch_interval; block_resource.epoch_interval = new_epoch_interval; + if (std::features::module_event_migration_enabled()) { + event::emit( + UpdateEpochInterval { old_epoch_interval, new_epoch_interval }, + ); + }; event::emit_event<UpdateEpochIntervalEvent>( &mut block_resource.update_epoch_interval_events, UpdateEpochIntervalEvent { old_epoch_interval, new_epoch_interval }, @@ -480,6 +593,7 @@ Return epoch interval in seconds. let block_metadata_ref = borrow_global_mut<BlockResource>(@aptos_framework); block_metadata_ref.height = event::counter(&block_metadata_ref.new_block_events); + // Emit both event v1 and v2 for compatibility. Eventually only module events will be kept. let new_block_event = NewBlockEvent { hash, epoch, @@ -490,7 +604,17 @@ Return epoch interval in seconds. failed_proposer_indices, time_microseconds: timestamp, }; - emit_new_block_event(vm, &mut block_metadata_ref.new_block_events, new_block_event); + let new_block_event_v2 = NewBlock { + hash, + epoch, + round, + height: block_metadata_ref.height, + previous_block_votes_bitvec, + proposer, + failed_proposer_indices, + time_microseconds: timestamp, + }; + emit_new_block_event(vm, &mut block_metadata_ref.new_block_events, new_block_event, new_block_event_v2); if (features::collect_and_distribute_gas_fees()) { // Assign the fees collected from the previous block to the previous block proposer. @@ -635,7 +759,7 @@ Get the current block height Emit the event and update height and global timestamp -
    fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent)
    +
    fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent, new_block_event_v2: block::NewBlock)
     
    @@ -644,7 +768,12 @@ Emit the event and update height and global timestamp Implementation -
    fun emit_new_block_event(vm: &signer, event_handle: &mut EventHandle<NewBlockEvent>, new_block_event: NewBlockEvent) acquires CommitHistory {
    +
    fun emit_new_block_event(
    +    vm: &signer,
    +    event_handle: &mut EventHandle<NewBlockEvent>,
    +    new_block_event: NewBlockEvent,
    +    new_block_event_v2: NewBlock
    +) acquires CommitHistory {
         if (exists<CommitHistory>(@aptos_framework)) {
             let commit_history_ref = borrow_global_mut<CommitHistory>(@aptos_framework);
             let idx = commit_history_ref.next_idx;
    @@ -662,6 +791,9 @@ Emit the event and update height and global timestamp
             event::counter(event_handle) == new_block_event.height,
             error::invalid_argument(ENUM_NEW_BLOCK_EVENTS_DOES_NOT_MATCH_BLOCK_HEIGHT),
         );
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(new_block_event_v2);
    +    };
         event::emit_event<NewBlockEvent>(event_handle, new_block_event);
     }
     
    @@ -702,6 +834,16 @@ reconfiguration event. proposer: @vm_reserved, failed_proposer_indices: vector::empty(), time_microseconds: 0, + }, + NewBlock { + hash: genesis_id, + epoch: 0, + round: 0, + height: 0, + previous_block_votes_bitvec: vector::empty(), + proposer: @vm_reserved, + failed_proposer_indices: vector::empty(), + time_microseconds: 0, } ); } @@ -745,6 +887,16 @@ new block event for WriteSetPayload. proposer: @vm_reserved, failed_proposer_indices: vector::empty(), time_microseconds: timestamp::now_microseconds(), + }, + NewBlock { + hash: fake_block_hash, + epoch: reconfiguration::current_epoch(), + round: MAX_U64, + height: block_metadata_ref.height, + previous_block_votes_bitvec: vector::empty(), + proposer: @vm_reserved, + failed_proposer_indices: vector::empty(), + time_microseconds: timestamp::now_microseconds(), } ); } @@ -936,70 +1088,6 @@ The number of new events created does not exceed MAX_U64. - - - - -
    schema BlockRequirement {
    -    vm: signer;
    -    hash: address;
    -    epoch: u64;
    -    round: u64;
    -    proposer: address;
    -    failed_proposer_indices: vector<u64>;
    -    previous_block_votes_bitvec: vector<u8>;
    -    timestamp: u64;
    -    requires chain_status::is_operating();
    -    requires system_addresses::is_vm(vm);
    -    // This enforces high-level requirement 4:
    -    requires proposer == @vm_reserved || stake::spec_is_current_epoch_validator(proposer);
    -    requires (proposer == @vm_reserved) ==> (timestamp::spec_now_microseconds() == timestamp);
    -    requires (proposer != @vm_reserved) ==> (timestamp::spec_now_microseconds() < timestamp);
    -    requires exists<stake::ValidatorFees>(@aptos_framework);
    -    requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
    -    include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply;
    -    include staking_config::StakingRewardsConfigRequirement;
    -}
    -
    - - - - - - - -
    schema Initialize {
    -    aptos_framework: signer;
    -    epoch_interval_microsecs: u64;
    -    let addr = signer::address_of(aptos_framework);
    -    // This enforces high-level requirement 2:
    -    aborts_if addr != @aptos_framework;
    -    aborts_if epoch_interval_microsecs == 0;
    -    aborts_if exists<BlockResource>(addr);
    -    aborts_if exists<CommitHistory>(addr);
    -    ensures exists<BlockResource>(addr);
    -    ensures exists<CommitHistory>(addr);
    -    ensures global<BlockResource>(addr).height == 0;
    -}
    -
    - - - - - - - -
    schema NewEventHandle {
    -    aptos_framework: signer;
    -    let addr = signer::address_of(aptos_framework);
    -    let account = global<account::Account>(addr);
    -    aborts_if !exists<account::Account>(addr);
    -    aborts_if account.guid_creation_num + 2 > MAX_U64;
    -}
    -
    - - - ### Function `update_epoch_interval_microsecs` @@ -1136,7 +1224,7 @@ The BlockResource existed under the @aptos_framework. ### Function `emit_new_block_event` -
    fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent)
    +
    fun emit_new_block_event(vm: &signer, event_handle: &mut event::EventHandle<block::NewBlockEvent>, new_block_event: block::NewBlockEvent, new_block_event_v2: block::NewBlock)
     
    diff --git a/aptos-move/framework/aptos-framework/doc/coin.md b/aptos-move/framework/aptos-framework/doc/coin.md index ba2b7fd254cd0..c5ff5884262a7 100644 --- a/aptos-move/framework/aptos-framework/doc/coin.md +++ b/aptos-move/framework/aptos-framework/doc/coin.md @@ -11,16 +11,48 @@ This module provides the foundation for typesafe Coins. - [Resource `CoinStore`](#0x1_coin_CoinStore) - [Resource `SupplyConfig`](#0x1_coin_SupplyConfig) - [Resource `CoinInfo`](#0x1_coin_CoinInfo) -- [Struct `DepositEvent`](#0x1_coin_DepositEvent) +- [Struct `CoinDeposit`](#0x1_coin_CoinDeposit) +- [Struct `CoinWithdraw`](#0x1_coin_CoinWithdraw) - [Struct `Deposit`](#0x1_coin_Deposit) -- [Struct `WithdrawEvent`](#0x1_coin_WithdrawEvent) - [Struct `Withdraw`](#0x1_coin_Withdraw) +- [Struct `DepositEvent`](#0x1_coin_DepositEvent) +- [Struct `WithdrawEvent`](#0x1_coin_WithdrawEvent) +- [Struct `CoinEventHandleDeletion`](#0x1_coin_CoinEventHandleDeletion) +- [Struct `PairCreation`](#0x1_coin_PairCreation) +- [Resource `MigrationFlag`](#0x1_coin_MigrationFlag) - [Struct `MintCapability`](#0x1_coin_MintCapability) - [Struct `FreezeCapability`](#0x1_coin_FreezeCapability) - [Struct `BurnCapability`](#0x1_coin_BurnCapability) +- [Resource `CoinConversionMap`](#0x1_coin_CoinConversionMap) +- [Resource `PairedCoinType`](#0x1_coin_PairedCoinType) +- [Resource `PairedFungibleAssetRefs`](#0x1_coin_PairedFungibleAssetRefs) +- [Struct `MintRefReceipt`](#0x1_coin_MintRefReceipt) +- [Struct `TransferRefReceipt`](#0x1_coin_TransferRefReceipt) +- [Struct `BurnRefReceipt`](#0x1_coin_BurnRefReceipt) - [Resource `Ghost$supply`](#0x1_coin_Ghost$supply) - [Resource `Ghost$aggregate_supply`](#0x1_coin_Ghost$aggregate_supply) - [Constants](#@Constants_0) +- [Function `paired_metadata`](#0x1_coin_paired_metadata) +- [Function `create_coin_conversion_map`](#0x1_coin_create_coin_conversion_map) +- [Function `create_pairing`](#0x1_coin_create_pairing) +- [Function `is_apt`](#0x1_coin_is_apt) +- [Function `create_and_return_paired_metadata_if_not_exist`](#0x1_coin_create_and_return_paired_metadata_if_not_exist) +- [Function `ensure_paired_metadata`](#0x1_coin_ensure_paired_metadata) +- [Function `paired_coin`](#0x1_coin_paired_coin) +- [Function `coin_to_fungible_asset`](#0x1_coin_coin_to_fungible_asset) +- [Function `fungible_asset_to_coin`](#0x1_coin_fungible_asset_to_coin) +- [Function `assert_paired_metadata_exists`](#0x1_coin_assert_paired_metadata_exists) +- [Function `paired_mint_ref_exists`](#0x1_coin_paired_mint_ref_exists) +- [Function `get_paired_mint_ref`](#0x1_coin_get_paired_mint_ref) +- [Function `return_paired_mint_ref`](#0x1_coin_return_paired_mint_ref) +- [Function `paired_transfer_ref_exists`](#0x1_coin_paired_transfer_ref_exists) +- [Function `get_paired_transfer_ref`](#0x1_coin_get_paired_transfer_ref) +- [Function `return_paired_transfer_ref`](#0x1_coin_return_paired_transfer_ref) +- [Function `paired_burn_ref_exists`](#0x1_coin_paired_burn_ref_exists) +- [Function `get_paired_burn_ref`](#0x1_coin_get_paired_burn_ref) +- [Function `convert_and_take_paired_burn_ref`](#0x1_coin_convert_and_take_paired_burn_ref) +- [Function `return_paired_burn_ref`](#0x1_coin_return_paired_burn_ref) +- [Function `borrow_paired_burn_ref`](#0x1_coin_borrow_paired_burn_ref) - [Function `initialize_supply_config`](#0x1_coin_initialize_supply_config) - [Function `allow_supply_upgrades`](#0x1_coin_allow_supply_upgrades) - [Function `initialize_aggregatable_coin`](#0x1_coin_initialize_aggregatable_coin) @@ -28,8 +60,13 @@ This module provides the foundation for typesafe Coins. - [Function `drain_aggregatable_coin`](#0x1_coin_drain_aggregatable_coin) - [Function `merge_aggregatable_coin`](#0x1_coin_merge_aggregatable_coin) - [Function `collect_into_aggregatable_coin`](#0x1_coin_collect_into_aggregatable_coin) +- [Function `calculate_amount_to_withdraw`](#0x1_coin_calculate_amount_to_withdraw) +- [Function `maybe_convert_to_fungible_store`](#0x1_coin_maybe_convert_to_fungible_store) +- [Function `migrate_to_fungible_store`](#0x1_coin_migrate_to_fungible_store) - [Function `coin_address`](#0x1_coin_coin_address) - [Function `balance`](#0x1_coin_balance) +- [Function `is_balance_at_least`](#0x1_coin_is_balance_at_least) +- [Function `coin_balance`](#0x1_coin_coin_balance) - [Function `is_coin_initialized`](#0x1_coin_is_coin_initialized) - [Function `is_coin_store_frozen`](#0x1_coin_is_coin_store_frozen) - [Function `is_account_registered`](#0x1_coin_is_account_registered) @@ -37,9 +74,11 @@ This module provides the foundation for typesafe Coins. - [Function `symbol`](#0x1_coin_symbol) - [Function `decimals`](#0x1_coin_decimals) - [Function `supply`](#0x1_coin_supply) +- [Function `coin_supply`](#0x1_coin_coin_supply) - [Function `burn`](#0x1_coin_burn) - [Function `burn_from`](#0x1_coin_burn_from) - [Function `deposit`](#0x1_coin_deposit) +- [Function `migrated_primary_fungible_store_exists`](#0x1_coin_migrated_primary_fungible_store_exists) - [Function `force_deposit`](#0x1_coin_force_deposit) - [Function `destroy_zero`](#0x1_coin_destroy_zero) - [Function `extract`](#0x1_coin_extract) @@ -60,10 +99,14 @@ This module provides the foundation for typesafe Coins. - [Function `destroy_freeze_cap`](#0x1_coin_destroy_freeze_cap) - [Function `destroy_mint_cap`](#0x1_coin_destroy_mint_cap) - [Function `destroy_burn_cap`](#0x1_coin_destroy_burn_cap) +- [Function `mint_internal`](#0x1_coin_mint_internal) +- [Function `burn_internal`](#0x1_coin_burn_internal) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) - [Struct `AggregatableCoin`](#@Specification_1_AggregatableCoin) + - [Function `coin_to_fungible_asset`](#@Specification_1_coin_to_fungible_asset) + - [Function `fungible_asset_to_coin`](#@Specification_1_fungible_asset_to_coin) - [Function `initialize_supply_config`](#@Specification_1_initialize_supply_config) - [Function `allow_supply_upgrades`](#@Specification_1_allow_supply_upgrades) - [Function `initialize_aggregatable_coin`](#@Specification_1_initialize_aggregatable_coin) @@ -71,6 +114,7 @@ This module provides the foundation for typesafe Coins. - [Function `drain_aggregatable_coin`](#@Specification_1_drain_aggregatable_coin) - [Function `merge_aggregatable_coin`](#@Specification_1_merge_aggregatable_coin) - [Function `collect_into_aggregatable_coin`](#@Specification_1_collect_into_aggregatable_coin) + - [Function `maybe_convert_to_fungible_store`](#@Specification_1_maybe_convert_to_fungible_store) - [Function `coin_address`](#@Specification_1_coin_address) - [Function `balance`](#@Specification_1_balance) - [Function `is_coin_initialized`](#@Specification_1_is_coin_initialized) @@ -79,6 +123,7 @@ This module provides the foundation for typesafe Coins. - [Function `symbol`](#@Specification_1_symbol) - [Function `decimals`](#@Specification_1_decimals) - [Function `supply`](#@Specification_1_supply) + - [Function `coin_supply`](#@Specification_1_coin_supply) - [Function `burn`](#@Specification_1_burn) - [Function `burn_from`](#@Specification_1_burn_from) - [Function `deposit`](#@Specification_1_deposit) @@ -97,18 +142,27 @@ This module provides the foundation for typesafe Coins. - [Function `register`](#@Specification_1_register) - [Function `transfer`](#@Specification_1_transfer) - [Function `withdraw`](#@Specification_1_withdraw) + - [Function `mint_internal`](#@Specification_1_mint_internal) + - [Function `burn_internal`](#@Specification_1_burn_internal)
    use 0x1::account;
     use 0x1::aggregator;
     use 0x1::aggregator_factory;
    +use 0x1::create_signer;
     use 0x1::error;
     use 0x1::event;
    +use 0x1::features;
    +use 0x1::fungible_asset;
    +use 0x1::guid;
    +use 0x1::object;
     use 0x1::option;
     use 0x1::optional_aggregator;
    +use 0x1::primary_fungible_store;
     use 0x1::signer;
     use 0x1::string;
     use 0x1::system_addresses;
    +use 0x1::table;
     use 0x1::type_info;
     
    @@ -298,14 +352,15 @@ Information about a specific coin type. Stored on the creator of the coin's acco
    - + -## Struct `DepositEvent` +## Struct `CoinDeposit` -Event emitted when some amount of a coin is deposited into an account. +Module event emitted when some amount of a coin is deposited into an account. -
    struct DepositEvent has drop, store
    +
    #[event]
    +struct CoinDeposit has drop, store
     
    @@ -316,6 +371,18 @@ Event emitted when some amount of a coin is deposited into an account.
    +coin_type: string::String +
    +
    + +
    +
    +account: address +
    +
    + +
    +
    amount: u64
    @@ -326,14 +393,15 @@ Event emitted when some amount of a coin is deposited into an account. - + -## Struct `Deposit` +## Struct `CoinWithdraw` +Module event emitted when some amount of a coin is withdrawn from an account.
    #[event]
    -struct Deposit<CoinType> has drop, store
    +struct CoinWithdraw has drop, store
     
    @@ -344,6 +412,12 @@ Event emitted when some amount of a coin is deposited into an account.
    +coin_type: string::String +
    +
    + +
    +
    account: address
    @@ -360,14 +434,15 @@ Event emitted when some amount of a coin is deposited into an account. - + -## Struct `WithdrawEvent` +## Struct `Deposit` -Event emitted when some amount of a coin is withdrawn from an account. -
    struct WithdrawEvent has drop, store
    +
    #[event]
    +#[deprecated]
    +struct Deposit<CoinType> has drop, store
     
    @@ -378,6 +453,12 @@ Event emitted when some amount of a coin is withdrawn from an account.
    +account: address +
    +
    + +
    +
    amount: u64
    @@ -395,6 +476,7 @@ Event emitted when some amount of a coin is withdrawn from an account.
    #[event]
    +#[deprecated]
     struct Withdraw<CoinType> has drop, store
     
    @@ -422,14 +504,14 @@ Event emitted when some amount of a coin is withdrawn from an account. - + -## Struct `MintCapability` +## Struct `DepositEvent` -Capability required to mint coins. +Event emitted when some amount of a coin is deposited into an account. -
    struct MintCapability<CoinType> has copy, store
    +
    struct DepositEvent has drop, store
     
    @@ -440,7 +522,7 @@ Capability required to mint coins.
    -dummy_field: bool +amount: u64
    @@ -450,14 +532,14 @@ Capability required to mint coins. - + -## Struct `FreezeCapability` +## Struct `WithdrawEvent` -Capability required to freeze a coin store. +Event emitted when some amount of a coin is withdrawn from an account. -
    struct FreezeCapability<CoinType> has copy, store
    +
    struct WithdrawEvent has drop, store
     
    @@ -468,7 +550,7 @@ Capability required to freeze a coin store.
    -dummy_field: bool +amount: u64
    @@ -478,14 +560,15 @@ Capability required to freeze a coin store. - + -## Struct `BurnCapability` +## Struct `CoinEventHandleDeletion` -Capability required to burn coins. +Module event emitted when the event handles related to coin store is deleted. -
    struct BurnCapability<CoinType> has copy, store
    +
    #[event]
    +struct CoinEventHandleDeletion has drop, store
     
    @@ -496,7 +579,19 @@ Capability required to burn coins.
    -dummy_field: bool +event_handle_creation_address: address +
    +
    + +
    +
    +deleted_deposit_event_handle_creation_number: u64 +
    +
    + +
    +
    +deleted_withdraw_event_handle_creation_number: u64
    @@ -506,13 +601,15 @@ Capability required to burn coins. - + -## Resource `Ghost$supply` +## Struct `PairCreation` +Module event emitted when a new pair of coin and fungible asset is created. -
    struct Ghost$supply<CoinType> has copy, drop, store, key
    +
    #[event]
    +struct PairCreation has drop, store
     
    @@ -523,7 +620,13 @@ Capability required to burn coins.
    -v: num +coin_type: type_info::TypeInfo +
    +
    + +
    +
    +fungible_asset_metadata_address: address
    @@ -533,13 +636,15 @@ Capability required to burn coins. - + -## Resource `Ghost$aggregate_supply` +## Resource `MigrationFlag` +The flag the existence of which indicates the primary fungible store is created by the migration from CoinStore. -
    struct Ghost$aggregate_supply<CoinType> has copy, drop, store, key
    +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct MigrationFlag has key
     
    @@ -550,7 +655,7 @@ Capability required to burn coins.
    -v: num +dummy_field: bool
    @@ -560,179 +665,1314 @@ Capability required to burn coins. - - -## Constants - + - +## Struct `MintCapability` -Maximum possible aggregatable coin value. +Capability required to mint coins. -
    const MAX_U64: u128 = 18446744073709551615;
    +
    struct MintCapability<CoinType> has copy, store
     
    - +
    +Fields -Maximum possible coin supply. +
    +
    +dummy_field: bool +
    +
    + +
    +
    -
    const MAX_U128: u128 = 340282366920938463463374607431768211455;
    -
    +
    + - +## Struct `FreezeCapability` -The value of aggregatable coin used for transaction fees redistribution does not fit in u64. +Capability required to freeze a coin store. -
    const EAGGREGATABLE_COIN_VALUE_TOO_LARGE: u64 = 14;
    +
    struct FreezeCapability<CoinType> has copy, store
     
    - +
    +Fields -Address of account which is used to initialize a coin CoinType doesn't match the deployer of module +
    +
    +dummy_field: bool +
    +
    -
    const ECOIN_INFO_ADDRESS_MISMATCH: u64 = 1;
    -
    +
    +
    +
    - + -CoinType is already initialized as a coin +## Struct `BurnCapability` +Capability required to burn coins. -
    const ECOIN_INFO_ALREADY_PUBLISHED: u64 = 2;
    +
    +
    struct BurnCapability<CoinType> has copy, store
     
    - +
    +Fields -CoinType hasn't been initialized as a coin +
    +
    +dummy_field: bool +
    +
    -
    const ECOIN_INFO_NOT_PUBLISHED: u64 = 3;
    -
    +
    +
    +
    - + -Name of the coin is too long +## Resource `CoinConversionMap` +The mapping between coin and fungible asset. -
    const ECOIN_NAME_TOO_LONG: u64 = 12;
    +
    +
    struct CoinConversionMap has key
     
    - +
    +Fields -Deprecated. Account already has CoinStore registered for CoinType +
    +
    +coin_to_fungible_asset_map: table::Table<type_info::TypeInfo, object::Object<fungible_asset::Metadata>> +
    +
    -
    const ECOIN_STORE_ALREADY_PUBLISHED: u64 = 4;
    -
    +
    +
    +
    - + -Account hasn't registered CoinStore for CoinType +## Resource `PairedCoinType` +The paired coin type info stored in fungible asset metadata object. -
    const ECOIN_STORE_NOT_PUBLISHED: u64 = 5;
    +
    +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct PairedCoinType has key
     
    - +
    +Fields -Cannot upgrade the total supply of coins to different implementation. +
    +
    +type: type_info::TypeInfo +
    +
    + +
    +
    -
    const ECOIN_SUPPLY_UPGRADE_NOT_SUPPORTED: u64 = 11;
    -
    +
    + - +## Resource `PairedFungibleAssetRefs` -Symbol of the coin is too long +The refs of the paired fungible asset. -
    const ECOIN_SYMBOL_TOO_LONG: u64 = 13;
    +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct PairedFungibleAssetRefs has key
     
    - +
    +Fields -Cannot destroy non-zero coins +
    +
    +mint_ref_opt: option::Option<fungible_asset::MintRef> +
    +
    -
    const EDESTRUCTION_OF_NONZERO_TOKEN: u64 = 7;
    -
    +
    +
    +transfer_ref_opt: option::Option<fungible_asset::TransferRef> +
    +
    +
    +
    +burn_ref_opt: option::Option<fungible_asset::BurnRef> +
    +
    +
    +
    - -CoinStore is frozen. Coins cannot be deposited or withdrawn +
    + -
    const EFROZEN: u64 = 10;
    +## Struct `MintRefReceipt`
    +
    +The hot potato receipt for flash borrowing MintRef.
    +
    +
    +
    struct MintRefReceipt
     
    - +
    +Fields -Not enough coins to complete transaction +
    +
    +metadata: object::Object<fungible_asset::Metadata> +
    +
    + +
    +
    -
    const EINSUFFICIENT_BALANCE: u64 = 6;
    -
    +
    + - +## Struct `TransferRefReceipt` -Coin amount cannot be zero +The hot potato receipt for flash borrowing TransferRef. -
    const EZERO_COIN_AMOUNT: u64 = 9;
    +
    struct TransferRefReceipt
     
    - +
    +Fields +
    +
    +metadata: object::Object<fungible_asset::Metadata> +
    +
    + +
    +
    -
    const MAX_COIN_NAME_LENGTH: u64 = 32;
    -
    +
    + - +## Struct `BurnRefReceipt` +The hot potato receipt for flash borrowing BurnRef. -
    const MAX_COIN_SYMBOL_LENGTH: u64 = 10;
    +
    struct BurnRefReceipt
     
    +
    +Fields + + +
    +
    +metadata: object::Object<fungible_asset::Metadata> +
    +
    + +
    +
    + + +
    + + + +## Resource `Ghost$supply` + + + +
    struct Ghost$supply<CoinType> has copy, drop, store, key
    +
    + + + +
    +Fields + + +
    +
    +v: num +
    +
    + +
    +
    + + +
    + + + +## Resource `Ghost$aggregate_supply` + + + +
    struct Ghost$aggregate_supply<CoinType> has copy, drop, store, key
    +
    + + + +
    +Fields + + +
    +
    +v: num +
    +
    + +
    +
    + + +
    + + + +## Constants + + + + +Maximum possible aggregatable coin value. + + +
    const MAX_U64: u128 = 18446744073709551615;
    +
    + + + + + +Maximum possible coin supply. + + +
    const MAX_U128: u128 = 340282366920938463463374607431768211455;
    +
    + + + + + +Not enough coins to complete transaction + + +
    const EINSUFFICIENT_BALANCE: u64 = 6;
    +
    + + + + + +The value of aggregatable coin used for transaction fees redistribution does not fit in u64. + + +
    const EAGGREGATABLE_COIN_VALUE_TOO_LARGE: u64 = 14;
    +
    + + + + + +APT pairing is not eanbled yet. + + +
    const EAPT_PAIRING_IS_NOT_ENABLED: u64 = 28;
    +
    + + + + + +The BurnRef does not exist. + + +
    const EBURN_REF_NOT_FOUND: u64 = 25;
    +
    + + + + + +The BurnRefReceipt does not match the BurnRef to be returned. + + +
    const EBURN_REF_RECEIPT_MISMATCH: u64 = 24;
    +
    + + + + + +The coin converison map is not created yet. + + +
    const ECOIN_CONVERSION_MAP_NOT_FOUND: u64 = 27;
    +
    + + + + + +Address of account which is used to initialize a coin CoinType doesn't match the deployer of module + + +
    const ECOIN_INFO_ADDRESS_MISMATCH: u64 = 1;
    +
    + + + + + +CoinType is already initialized as a coin + + +
    const ECOIN_INFO_ALREADY_PUBLISHED: u64 = 2;
    +
    + + + + + +CoinType hasn't been initialized as a coin + + +
    const ECOIN_INFO_NOT_PUBLISHED: u64 = 3;
    +
    + + + + + +Name of the coin is too long + + +
    const ECOIN_NAME_TOO_LONG: u64 = 12;
    +
    + + + + + +Deprecated. Account already has CoinStore registered for CoinType + + +
    const ECOIN_STORE_ALREADY_PUBLISHED: u64 = 4;
    +
    + + + + + +Account hasn't registered CoinStore for CoinType + + +
    const ECOIN_STORE_NOT_PUBLISHED: u64 = 5;
    +
    + + + + + +Cannot upgrade the total supply of coins to different implementation. + + +
    const ECOIN_SUPPLY_UPGRADE_NOT_SUPPORTED: u64 = 11;
    +
    + + + + + +Symbol of the coin is too long + + +
    const ECOIN_SYMBOL_TOO_LONG: u64 = 13;
    +
    + + + + + +The feature of migration from coin to fungible asset is not enabled. + + +
    const ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED: u64 = 18;
    +
    + + + + + +The coin type from the map does not match the calling function type argument. + + +
    const ECOIN_TYPE_MISMATCH: u64 = 17;
    +
    + + + + + +Cannot destroy non-zero coins + + +
    const EDESTRUCTION_OF_NONZERO_TOKEN: u64 = 7;
    +
    + + + + + +CoinStore is frozen. Coins cannot be deposited or withdrawn + + +
    const EFROZEN: u64 = 10;
    +
    + + + + + +The migration process from coin to fungible asset is not enabled yet. + + +
    const EMIGRATION_FRAMEWORK_NOT_ENABLED: u64 = 26;
    +
    + + + + + +The MintRef does not exist. + + +
    const EMINT_REF_NOT_FOUND: u64 = 21;
    +
    + + + + + +The MintRefReceipt does not match the MintRef to be returned. + + +
    const EMINT_REF_RECEIPT_MISMATCH: u64 = 20;
    +
    + + + + + +Error regarding paired coin type of the fungible asset metadata. + + +
    const EPAIRED_COIN: u64 = 15;
    +
    + + + + + +Error regarding paired fungible asset metadata of a coin type. + + +
    const EPAIRED_FUNGIBLE_ASSET: u64 = 16;
    +
    + + + + + +PairedFungibleAssetRefs resource does not exist. + + +
    const EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND: u64 = 19;
    +
    + + + + + +The TransferRef does not exist. + + +
    const ETRANSFER_REF_NOT_FOUND: u64 = 23;
    +
    + + + + + +The TransferRefReceipt does not match the TransferRef to be returned. + + +
    const ETRANSFER_REF_RECEIPT_MISMATCH: u64 = 22;
    +
    + + + + + + + +
    const MAX_COIN_NAME_LENGTH: u64 = 32;
    +
    + + + + + + + +
    const MAX_COIN_SYMBOL_LENGTH: u64 = 10;
    +
    + + + + + +## Function `paired_metadata` + +Get the paired fungible asset metadata object of a coin type. If not exist, return option::none(). + + +
    #[view]
    +public fun paired_metadata<CoinType>(): option::Option<object::Object<fungible_asset::Metadata>>
    +
    + + + +
    +Implementation + + +
    public fun paired_metadata<CoinType>(): Option<Object<Metadata>> acquires CoinConversionMap {
    +    if (exists<CoinConversionMap>(@aptos_framework) && features::coin_to_fungible_asset_migration_feature_enabled(
    +    )) {
    +        let map = &borrow_global<CoinConversionMap>(@aptos_framework).coin_to_fungible_asset_map;
    +        let type = type_info::type_of<CoinType>();
    +        if (table::contains(map, type)) {
    +            return option::some(*table::borrow(map, type))
    +        }
    +    };
    +    option::none()
    +}
    +
    + + + +
    + + + +## Function `create_coin_conversion_map` + + + +
    public entry fun create_coin_conversion_map(aptos_framework: &signer)
    +
    + + + +
    +Implementation + + +
    public entry fun create_coin_conversion_map(aptos_framework: &signer) {
    +    system_addresses::assert_aptos_framework(aptos_framework);
    +    if (!exists<CoinConversionMap>(@aptos_framework)) {
    +        move_to(aptos_framework, CoinConversionMap {
    +            coin_to_fungible_asset_map: table::new(),
    +        })
    +    };
    +}
    +
    + + + +
    + + + +## Function `create_pairing` + +Create APT pairing by passing AptosCoin. + + +
    public entry fun create_pairing<CoinType>(aptos_framework: &signer)
    +
    + + + +
    +Implementation + + +
    public entry fun create_pairing<CoinType>(
    +    aptos_framework: &signer
    +) acquires CoinConversionMap, CoinInfo {
    +    system_addresses::assert_aptos_framework(aptos_framework);
    +    create_and_return_paired_metadata_if_not_exist<CoinType>(true);
    +}
    +
    + + + +
    + + + +## Function `is_apt` + + + +
    fun is_apt<CoinType>(): bool
    +
    + + + +
    +Implementation + + +
    inline fun is_apt<CoinType>(): bool {
    +    type_info::type_name<CoinType>() == string::utf8(b"0x1::aptos_coin::AptosCoin")
    +}
    +
    + + + +
    + + + +## Function `create_and_return_paired_metadata_if_not_exist` + + + +
    fun create_and_return_paired_metadata_if_not_exist<CoinType>(allow_apt_creation: bool): object::Object<fungible_asset::Metadata>
    +
    + + + +
    +Implementation + + +
    inline fun create_and_return_paired_metadata_if_not_exist<CoinType>(allow_apt_creation: bool): Object<Metadata> {
    +    assert!(
    +        features::coin_to_fungible_asset_migration_feature_enabled(),
    +        error::invalid_state(EMIGRATION_FRAMEWORK_NOT_ENABLED)
    +    );
    +    assert!(exists<CoinConversionMap>(@aptos_framework), error::not_found(ECOIN_CONVERSION_MAP_NOT_FOUND));
    +    let map = borrow_global_mut<CoinConversionMap>(@aptos_framework);
    +    let type = type_info::type_of<CoinType>();
    +    if (!table::contains(&map.coin_to_fungible_asset_map, type)) {
    +        let is_apt = is_apt<CoinType>();
    +        assert!(!is_apt || allow_apt_creation, error::invalid_state(EAPT_PAIRING_IS_NOT_ENABLED));
    +        let metadata_object_cref =
    +            if (is_apt) {
    +                object::create_sticky_object_at_address(@aptos_framework, @aptos_fungible_asset)
    +            } else {
    +                object::create_named_object(
    +                    &create_signer::create_signer(@aptos_fungible_asset),
    +                    *string::bytes(&type_info::type_name<CoinType>())
    +                )
    +            };
    +        primary_fungible_store::create_primary_store_enabled_fungible_asset(
    +            &metadata_object_cref,
    +            option::map(coin_supply<CoinType>(), |_| MAX_U128),
    +            name<CoinType>(),
    +            symbol<CoinType>(),
    +            decimals<CoinType>(),
    +            string::utf8(b""),
    +            string::utf8(b""),
    +        );
    +
    +        let metadata_object_signer = &object::generate_signer(&metadata_object_cref);
    +        let type = type_info::type_of<CoinType>();
    +        move_to(metadata_object_signer, PairedCoinType { type });
    +        let metadata_obj = object::object_from_constructor_ref(&metadata_object_cref);
    +
    +        table::add(&mut map.coin_to_fungible_asset_map, type, metadata_obj);
    +        event::emit(PairCreation {
    +            coin_type: type,
    +            fungible_asset_metadata_address: object_address(&metadata_obj)
    +        });
    +
    +        // Generates all three refs
    +        let mint_ref = fungible_asset::generate_mint_ref(&metadata_object_cref);
    +        let transfer_ref = fungible_asset::generate_transfer_ref(&metadata_object_cref);
    +        let burn_ref = fungible_asset::generate_burn_ref(&metadata_object_cref);
    +        move_to(metadata_object_signer,
    +            PairedFungibleAssetRefs {
    +                mint_ref_opt: option::some(mint_ref),
    +                transfer_ref_opt: option::some(transfer_ref),
    +                burn_ref_opt: option::some(burn_ref),
    +            }
    +        );
    +    };
    +    *table::borrow(&map.coin_to_fungible_asset_map, type)
    +}
    +
    + + + +
    + + + +## Function `ensure_paired_metadata` + +Get the paired fungible asset metadata object of a coin type, create if not exist. + + +
    public(friend) fun ensure_paired_metadata<CoinType>(): object::Object<fungible_asset::Metadata>
    +
    + + + +
    +Implementation + + +
    public(friend) fun ensure_paired_metadata<CoinType>(): Object<Metadata> acquires CoinConversionMap, CoinInfo {
    +    create_and_return_paired_metadata_if_not_exist<CoinType>(false)
    +}
    +
    + + + +
    + + + +## Function `paired_coin` + +Get the paired coin type of a fungible asset metadata object. + + +
    #[view]
    +public fun paired_coin(metadata: object::Object<fungible_asset::Metadata>): option::Option<type_info::TypeInfo>
    +
    + + + +
    +Implementation + + +
    public fun paired_coin(metadata: Object<Metadata>): Option<TypeInfo> acquires PairedCoinType {
    +    let metadata_addr = object::object_address(&metadata);
    +    if (exists<PairedCoinType>(metadata_addr)) {
    +        option::some(borrow_global<PairedCoinType>(metadata_addr).type)
    +    } else {
    +        option::none()
    +    }
    +}
    +
    + + + +
    + + + +## Function `coin_to_fungible_asset` + +Conversion from coin to fungible asset + + +
    public fun coin_to_fungible_asset<CoinType>(coin: coin::Coin<CoinType>): fungible_asset::FungibleAsset
    +
    + + + +
    +Implementation + + +
    public fun coin_to_fungible_asset<CoinType>(
    +    coin: Coin<CoinType>
    +): FungibleAsset acquires CoinConversionMap, CoinInfo {
    +    let metadata = ensure_paired_metadata<CoinType>();
    +    let amount = burn_internal(coin);
    +    fungible_asset::mint_internal(metadata, amount)
    +}
    +
    + + + +
    + + + +## Function `fungible_asset_to_coin` + +Conversion from fungible asset to coin. Not public to push the migration to FA. + + +
    fun fungible_asset_to_coin<CoinType>(fungible_asset: fungible_asset::FungibleAsset): coin::Coin<CoinType>
    +
    + + + +
    +Implementation + + +
    fun fungible_asset_to_coin<CoinType>(
    +    fungible_asset: FungibleAsset
    +): Coin<CoinType> acquires CoinInfo, PairedCoinType {
    +    let metadata_addr = object::object_address(&fungible_asset::metadata_from_asset(&fungible_asset));
    +    assert!(
    +        object::object_exists<PairedCoinType>(metadata_addr),
    +        error::not_found(EPAIRED_COIN)
    +    );
    +    let coin_type_info = borrow_global<PairedCoinType>(metadata_addr).type;
    +    assert!(coin_type_info == type_info::type_of<CoinType>(), error::invalid_argument(ECOIN_TYPE_MISMATCH));
    +    let amount = fungible_asset::burn_internal(fungible_asset);
    +    mint_internal<CoinType>(amount)
    +}
    +
    + + + +
    + + + +## Function `assert_paired_metadata_exists` + + + +
    fun assert_paired_metadata_exists<CoinType>(): object::Object<fungible_asset::Metadata>
    +
    + + + +
    +Implementation + + +
    inline fun assert_paired_metadata_exists<CoinType>(): Object<Metadata> {
    +    let metadata_opt = paired_metadata<CoinType>();
    +    assert!(option::is_some(&metadata_opt), error::not_found(EPAIRED_FUNGIBLE_ASSET));
    +    option::destroy_some(metadata_opt)
    +}
    +
    + + + +
    + + + +## Function `paired_mint_ref_exists` + +Check whether MintRef has not been taken. + + +
    #[view]
    +public fun paired_mint_ref_exists<CoinType>(): bool
    +
    + + + +
    +Implementation + + +
    public fun paired_mint_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    option::is_some(&borrow_global<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt)
    +}
    +
    + + + +
    + + + +## Function `get_paired_mint_ref` + +Get the MintRef of paired fungible asset of a coin type from MintCapability. + + +
    public fun get_paired_mint_ref<CoinType>(_: &coin::MintCapability<CoinType>): (fungible_asset::MintRef, coin::MintRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun get_paired_mint_ref<CoinType>(
    +    _: &MintCapability<CoinType>
    +): (MintRef, MintRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    let mint_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt;
    +    assert!(option::is_some(mint_ref_opt), error::not_found(EMINT_REF_NOT_FOUND));
    +    (option::extract(mint_ref_opt), MintRefReceipt { metadata })
    +}
    +
    + + + +
    + + + +## Function `return_paired_mint_ref` + +Return the MintRef with the hot potato receipt. + + +
    public fun return_paired_mint_ref(mint_ref: fungible_asset::MintRef, receipt: coin::MintRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun return_paired_mint_ref(mint_ref: MintRef, receipt: MintRefReceipt) acquires PairedFungibleAssetRefs {
    +    let MintRefReceipt { metadata } = receipt;
    +    assert!(
    +        fungible_asset::mint_ref_metadata(&mint_ref) == metadata,
    +        error::invalid_argument(EMINT_REF_RECEIPT_MISMATCH)
    +    );
    +    let metadata_addr = object_address(&metadata);
    +    let mint_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt;
    +    option::fill(mint_ref_opt, mint_ref);
    +}
    +
    + + + +
    + + + +## Function `paired_transfer_ref_exists` + +Check whether TransferRef still exists. + + +
    #[view]
    +public fun paired_transfer_ref_exists<CoinType>(): bool
    +
    + + + +
    +Implementation + + +
    public fun paired_transfer_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    option::is_some(&borrow_global<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt)
    +}
    +
    + + + +
    + + + +## Function `get_paired_transfer_ref` + +Get the TransferRef of paired fungible asset of a coin type from FreezeCapability. + + +
    public fun get_paired_transfer_ref<CoinType>(_: &coin::FreezeCapability<CoinType>): (fungible_asset::TransferRef, coin::TransferRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun get_paired_transfer_ref<CoinType>(
    +    _: &FreezeCapability<CoinType>
    +): (TransferRef, TransferRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    let transfer_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt;
    +    assert!(option::is_some(transfer_ref_opt), error::not_found(ETRANSFER_REF_NOT_FOUND));
    +    (option::extract(transfer_ref_opt), TransferRefReceipt { metadata })
    +}
    +
    + + + +
    + + + +## Function `return_paired_transfer_ref` + +Return the TransferRef with the hot potato receipt. + + +
    public fun return_paired_transfer_ref(transfer_ref: fungible_asset::TransferRef, receipt: coin::TransferRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun return_paired_transfer_ref(
    +    transfer_ref: TransferRef,
    +    receipt: TransferRefReceipt
    +) acquires PairedFungibleAssetRefs {
    +    let TransferRefReceipt { metadata } = receipt;
    +    assert!(
    +        fungible_asset::transfer_ref_metadata(&transfer_ref) == metadata,
    +        error::invalid_argument(ETRANSFER_REF_RECEIPT_MISMATCH)
    +    );
    +    let metadata_addr = object_address(&metadata);
    +    let transfer_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt;
    +    option::fill(transfer_ref_opt, transfer_ref);
    +}
    +
    + + + +
    + + + +## Function `paired_burn_ref_exists` + +Check whether BurnRef has not been taken. + + +
    #[view]
    +public fun paired_burn_ref_exists<CoinType>(): bool
    +
    + + + +
    +Implementation + + +
    public fun paired_burn_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    option::is_some(&borrow_global<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt)
    +}
    +
    + + + +
    + + + +## Function `get_paired_burn_ref` + +Get the BurnRef of paired fungible asset of a coin type from BurnCapability. + + +
    public fun get_paired_burn_ref<CoinType>(_: &coin::BurnCapability<CoinType>): (fungible_asset::BurnRef, coin::BurnRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun get_paired_burn_ref<CoinType>(
    +    _: &BurnCapability<CoinType>
    +): (BurnRef, BurnRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    let burn_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
    +    assert!(option::is_some(burn_ref_opt), error::not_found(EBURN_REF_NOT_FOUND));
    +    (option::extract(burn_ref_opt), BurnRefReceipt { metadata })
    +}
    +
    + + + +
    + + + +## Function `convert_and_take_paired_burn_ref` + + + +
    public fun convert_and_take_paired_burn_ref<CoinType>(burn_cap: coin::BurnCapability<CoinType>): fungible_asset::BurnRef
    +
    + + + +
    +Implementation + + +
    public fun convert_and_take_paired_burn_ref<CoinType>(
    +    burn_cap: BurnCapability<CoinType>
    +): BurnRef acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    destroy_burn_cap(burn_cap);
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    let burn_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
    +    assert!(option::is_some(burn_ref_opt), error::not_found(EBURN_REF_NOT_FOUND));
    +    option::extract(burn_ref_opt)
    +}
    +
    + + + +
    + + + +## Function `return_paired_burn_ref` + +Return the BurnRef with the hot potato receipt. + + +
    public fun return_paired_burn_ref(burn_ref: fungible_asset::BurnRef, receipt: coin::BurnRefReceipt)
    +
    + + + +
    +Implementation + + +
    public fun return_paired_burn_ref(
    +    burn_ref: BurnRef,
    +    receipt: BurnRefReceipt
    +) acquires PairedFungibleAssetRefs {
    +    let BurnRefReceipt { metadata } = receipt;
    +    assert!(
    +        fungible_asset::burn_ref_metadata(&burn_ref) == metadata,
    +        error::invalid_argument(EBURN_REF_RECEIPT_MISMATCH)
    +    );
    +    let metadata_addr = object_address(&metadata);
    +    let burn_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
    +    option::fill(burn_ref_opt, burn_ref);
    +}
    +
    + + + +
    + + + +## Function `borrow_paired_burn_ref` + + + +
    fun borrow_paired_burn_ref<CoinType>(_: &coin::BurnCapability<CoinType>): &fungible_asset::BurnRef
    +
    + + + +
    +Implementation + + +
    inline fun borrow_paired_burn_ref<CoinType>(
    +    _: &BurnCapability<CoinType>
    +): &BurnRef acquires CoinConversionMap, PairedFungibleAssetRefs {
    +    let metadata = assert_paired_metadata_exists<CoinType>();
    +    let metadata_addr = object_address(&metadata);
    +    assert!(exists<PairedFungibleAssetRefs>(metadata_addr), error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND));
    +    let burn_ref_opt = &mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
    +    assert!(option::is_some(burn_ref_opt), error::not_found(EBURN_REF_NOT_FOUND));
    +    option::borrow(burn_ref_opt)
    +}
    +
    + + + +
    + ## Function `initialize_supply_config` @@ -823,7 +2063,199 @@ only be called by Aptos Framework (0x1) account for now because of create_ Returns true if the value of aggregatable coin is zero. -
    public(friend) fun is_aggregatable_coin_zero<CoinType>(coin: &coin::AggregatableCoin<CoinType>): bool
    +
    public(friend) fun is_aggregatable_coin_zero<CoinType>(coin: &coin::AggregatableCoin<CoinType>): bool
    +
    + + + +
    +Implementation + + +
    public(friend) fun is_aggregatable_coin_zero<CoinType>(coin: &AggregatableCoin<CoinType>): bool {
    +    let amount = aggregator::read(&coin.value);
    +    amount == 0
    +}
    +
    + + + +
    + + + +## Function `drain_aggregatable_coin` + +Drains the aggregatable coin, setting it to zero and returning a standard coin. + + +
    public(friend) fun drain_aggregatable_coin<CoinType>(coin: &mut coin::AggregatableCoin<CoinType>): coin::Coin<CoinType>
    +
    + + + +
    +Implementation + + +
    public(friend) fun drain_aggregatable_coin<CoinType>(coin: &mut AggregatableCoin<CoinType>): Coin<CoinType> {
    +    spec {
    +        // TODO: The data invariant is not properly assumed from CollectedFeesPerBlock.
    +        assume aggregator::spec_get_limit(coin.value) == MAX_U64;
    +    };
    +    let amount = aggregator::read(&coin.value);
    +    assert!(amount <= MAX_U64, error::out_of_range(EAGGREGATABLE_COIN_VALUE_TOO_LARGE));
    +    spec {
    +        update aggregate_supply<CoinType> = aggregate_supply<CoinType> - amount;
    +    };
    +    aggregator::sub(&mut coin.value, amount);
    +    spec {
    +        update supply<CoinType> = supply<CoinType> + amount;
    +    };
    +    Coin<CoinType> {
    +        value: (amount as u64),
    +    }
    +}
    +
    + + + +
    + + + +## Function `merge_aggregatable_coin` + +Merges coin into aggregatable coin (dst_coin). + + +
    public(friend) fun merge_aggregatable_coin<CoinType>(dst_coin: &mut coin::AggregatableCoin<CoinType>, coin: coin::Coin<CoinType>)
    +
    + + + +
    +Implementation + + +
    public(friend) fun merge_aggregatable_coin<CoinType>(
    +    dst_coin: &mut AggregatableCoin<CoinType>,
    +    coin: Coin<CoinType>
    +) {
    +    spec {
    +        update supply<CoinType> = supply<CoinType> - coin.value;
    +    };
    +    let Coin { value } = coin;
    +    let amount = (value as u128);
    +    spec {
    +        update aggregate_supply<CoinType> = aggregate_supply<CoinType> + amount;
    +    };
    +    aggregator::add(&mut dst_coin.value, amount);
    +}
    +
    + + + +
    + + + +## Function `collect_into_aggregatable_coin` + +Collects a specified amount of coin form an account into aggregatable coin. + + +
    public(friend) fun collect_into_aggregatable_coin<CoinType>(account_addr: address, amount: u64, dst_coin: &mut coin::AggregatableCoin<CoinType>)
    +
    + + + +
    +Implementation + + +
    public(friend) fun collect_into_aggregatable_coin<CoinType>(
    +    account_addr: address,
    +    amount: u64,
    +    dst_coin: &mut AggregatableCoin<CoinType>,
    +) acquires CoinStore, CoinConversionMap, CoinInfo, PairedCoinType {
    +    // Skip collecting if amount is zero.
    +    if (amount == 0) {
    +        return
    +    };
    +
    +    let (coin_amount_to_collect, fa_amount_to_collect) = calculate_amount_to_withdraw<CoinType>(
    +        account_addr,
    +        amount
    +    );
    +    let coin = if (coin_amount_to_collect > 0) {
    +        let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    +        extract(&mut coin_store.coin, coin_amount_to_collect)
    +    } else {
    +        zero()
    +    };
    +    if (fa_amount_to_collect > 0) {
    +        let store_addr = primary_fungible_store::primary_store_address(
    +            account_addr,
    +            option::destroy_some(paired_metadata<CoinType>())
    +        );
    +        let fa = fungible_asset::withdraw_internal(store_addr, fa_amount_to_collect);
    +        merge(&mut coin, fungible_asset_to_coin<CoinType>(fa));
    +    };
    +    merge_aggregatable_coin(dst_coin, coin);
    +}
    +
    + + + +
    + + + +## Function `calculate_amount_to_withdraw` + + + +
    fun calculate_amount_to_withdraw<CoinType>(account_addr: address, amount: u64): (u64, u64)
    +
    + + + +
    +Implementation + + +
    inline fun calculate_amount_to_withdraw<CoinType>(
    +    account_addr: address,
    +    amount: u64
    +): (u64, u64) {
    +    let coin_balance = coin_balance<CoinType>(account_addr);
    +    if (coin_balance >= amount) {
    +        (amount, 0)
    +    } else {
    +        let metadata = paired_metadata<CoinType>();
    +        if (option::is_some(&metadata) && primary_fungible_store::primary_store_exists(
    +            account_addr,
    +            option::destroy_some(metadata)
    +        ))
    +            (coin_balance, amount - coin_balance)
    +        else
    +            abort error::invalid_argument(EINSUFFICIENT_BALANCE)
    +    }
    +}
    +
    + + + +
    + + + +## Function `maybe_convert_to_fungible_store` + + + +
    fun maybe_convert_to_fungible_store<CoinType>(account: address)
     
    @@ -832,9 +2264,47 @@ Returns true if the value of aggregatable coin is zero. Implementation -
    public(friend) fun is_aggregatable_coin_zero<CoinType>(coin: &AggregatableCoin<CoinType>): bool {
    -    let amount = aggregator::read(&coin.value);
    -    amount == 0
    +
    fun maybe_convert_to_fungible_store<CoinType>(account: address) acquires CoinStore, CoinConversionMap, CoinInfo {
    +    if (!features::coin_to_fungible_asset_migration_feature_enabled()) {
    +        abort error::unavailable(ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED)
    +    };
    +    assert!(is_coin_initialized<CoinType>(), error::invalid_argument(ECOIN_INFO_NOT_PUBLISHED));
    +
    +    let metadata = ensure_paired_metadata<CoinType>();
    +    let store = primary_fungible_store::ensure_primary_store_exists(account, metadata);
    +    let store_address = object::object_address(&store);
    +    if (exists<CoinStore<CoinType>>(account)) {
    +        let CoinStore<CoinType> { coin, frozen, deposit_events, withdraw_events } = move_from<CoinStore<CoinType>>(
    +            account
    +        );
    +        event::emit(
    +            CoinEventHandleDeletion {
    +                event_handle_creation_address: guid::creator_address(
    +                    event::guid(&deposit_events)
    +                ),
    +                deleted_deposit_event_handle_creation_number: guid::creation_num(event::guid(&deposit_events)),
    +                deleted_withdraw_event_handle_creation_number: guid::creation_num(event::guid(&withdraw_events))
    +            }
    +        );
    +        event::destroy_handle(deposit_events);
    +        event::destroy_handle(withdraw_events);
    +        if (coin.value == 0) {
    +            destroy_zero(coin);
    +        } else {
    +            fungible_asset::deposit(store, coin_to_fungible_asset(coin));
    +        };
    +        // Note:
    +        // It is possible the primary fungible store may already exist before this function call.
    +        // In this case, if the account owns a frozen CoinStore and an unfrozen primary fungible store, this
    +        // function would convert and deposit the rest coin into the primary store and freeze it to make the
    +        // `frozen` semantic as consistent as possible.
    +        if (frozen != fungible_asset::is_frozen(store)) {
    +            fungible_asset::set_frozen_flag_internal(store, frozen);
    +        }
    +    };
    +    if (!exists<MigrationFlag>(store_address)) {
    +        move_to(&create_signer::create_signer(store_address), MigrationFlag {});
    +    }
     }
     
    @@ -842,14 +2312,14 @@ Returns true if the value of aggregatable coin is zero. - + -## Function `drain_aggregatable_coin` +## Function `migrate_to_fungible_store` -Drains the aggregatable coin, setting it to zero and returning a standard coin. +Voluntarily migrate to fungible store for CoinType if not yet. -
    public(friend) fun drain_aggregatable_coin<CoinType>(coin: &mut coin::AggregatableCoin<CoinType>): coin::Coin<CoinType>
    +
    public entry fun migrate_to_fungible_store<CoinType>(account: &signer)
     
    @@ -858,23 +2328,10 @@ Drains the aggregatable coin, setting it to zero and returning a standard coin. Implementation -
    public(friend) fun drain_aggregatable_coin<CoinType>(coin: &mut AggregatableCoin<CoinType>): Coin<CoinType> {
    -    spec {
    -        // TODO: The data invariant is not properly assumed from CollectedFeesPerBlock.
    -        assume aggregator::spec_get_limit(coin.value) == MAX_U64;
    -    };
    -    let amount = aggregator::read(&coin.value);
    -    assert!(amount <= MAX_U64, error::out_of_range(EAGGREGATABLE_COIN_VALUE_TOO_LARGE));
    -    spec {
    -        update aggregate_supply<CoinType> = aggregate_supply<CoinType> - amount;
    -    };
    -    aggregator::sub(&mut coin.value, amount);
    -    spec {
    -        update supply<CoinType> = supply<CoinType> + amount;
    -    };
    -    Coin<CoinType> {
    -        value: (amount as u64),
    -    }
    +
    public entry fun migrate_to_fungible_store<CoinType>(
    +    account: &signer
    +) acquires CoinStore, CoinConversionMap, CoinInfo {
    +    maybe_convert_to_fungible_store<CoinType>(signer::address_of(account));
     }
     
    @@ -882,14 +2339,14 @@ Drains the aggregatable coin, setting it to zero and returning a standard coin. - + -## Function `merge_aggregatable_coin` +## Function `coin_address` -Merges coin into aggregatable coin (dst_coin). +A helper function that returns the address of CoinType. -
    public(friend) fun merge_aggregatable_coin<CoinType>(dst_coin: &mut coin::AggregatableCoin<CoinType>, coin: coin::Coin<CoinType>)
    +
    fun coin_address<CoinType>(): address
     
    @@ -898,19 +2355,9 @@ Merges coin into aggregatable coin ( Implementation -
    public(friend) fun merge_aggregatable_coin<CoinType>(
    -    dst_coin: &mut AggregatableCoin<CoinType>,
    -    coin: Coin<CoinType>
    -) {
    -    spec {
    -        update supply<CoinType> = supply<CoinType> - coin.value;
    -    };
    -    let Coin { value } = coin;
    -    let amount = (value as u128);
    -    spec {
    -        update aggregate_supply<CoinType> = aggregate_supply<CoinType> + amount;
    -    };
    -    aggregator::add(&mut dst_coin.value, amount);
    +
    fun coin_address<CoinType>(): address {
    +    let type_info = type_info::type_of<CoinType>();
    +    type_info::account_address(&type_info)
     }
     
    @@ -918,14 +2365,15 @@ Merges coin into aggregatable coin ( - + -## Function `collect_into_aggregatable_coin` +## Function `balance` -Collects a specified amount of coin form an account into aggregatable coin. +Returns the balance of owner for provided CoinType and its paired FA if exists. -
    public(friend) fun collect_into_aggregatable_coin<CoinType>(account_addr: address, amount: u64, dst_coin: &mut coin::AggregatableCoin<CoinType>)
    +
    #[view]
    +public fun balance<CoinType>(owner: address): u64
     
    @@ -934,19 +2382,14 @@ Collects a specified amount of coin form an account into aggregatable coin. Implementation -
    public(friend) fun collect_into_aggregatable_coin<CoinType>(
    -    account_addr: address,
    -    amount: u64,
    -    dst_coin: &mut AggregatableCoin<CoinType>,
    -) acquires CoinStore {
    -    // Skip collecting if amount is zero.
    -    if (amount == 0) {
    -        return
    -    };
    -
    -    let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    -    let coin = extract(&mut coin_store.coin, amount);
    -    merge_aggregatable_coin(dst_coin, coin);
    +
    public fun balance<CoinType>(owner: address): u64 acquires CoinConversionMap, CoinStore {
    +    let paired_metadata = paired_metadata<CoinType>();
    +    coin_balance<CoinType>(owner) + if (option::is_some(&paired_metadata)) {
    +        primary_fungible_store::balance(
    +            owner,
    +            option::extract(&mut paired_metadata)
    +        )
    +    } else { 0 }
     }
     
    @@ -954,14 +2397,15 @@ Collects a specified amount of coin form an account into aggregatable coin. - + -## Function `coin_address` +## Function `is_balance_at_least` -A helper function that returns the address of CoinType. +Returns whether the balance of owner for provided CoinType and its paired FA is >= amount. -
    fun coin_address<CoinType>(): address
    +
    #[view]
    +public fun is_balance_at_least<CoinType>(owner: address, amount: u64): bool
     
    @@ -970,9 +2414,21 @@ A helper function that returns the address of CoinType. Implementation -
    fun coin_address<CoinType>(): address {
    -    let type_info = type_info::type_of<CoinType>();
    -    type_info::account_address(&type_info)
    +
    public fun is_balance_at_least<CoinType>(owner: address, amount: u64): bool acquires CoinConversionMap, CoinStore {
    +    let coin_balance = coin_balance<CoinType>(owner);
    +    if (coin_balance >= amount) {
    +        return true
    +    };
    +
    +    let paired_metadata = paired_metadata<CoinType>();
    +    let left_amount = amount - coin_balance;
    +    if (option::is_some(&paired_metadata)) {
    +        primary_fungible_store::is_balance_at_least(
    +            owner,
    +            option::extract(&mut paired_metadata),
    +            left_amount
    +        )
    +    } else { false }
     }
     
    @@ -980,15 +2436,13 @@ A helper function that returns the address of CoinType. - + -## Function `balance` +## Function `coin_balance` -Returns the balance of owner for provided CoinType. -
    #[view]
    -public fun balance<CoinType>(owner: address): u64
    +
    fun coin_balance<CoinType>(owner: address): u64
     
    @@ -997,12 +2451,12 @@ Returns the balance of owner for provided CoinType. Implementation -
    public fun balance<CoinType>(owner: address): u64 acquires CoinStore {
    -    assert!(
    -        is_account_registered<CoinType>(owner),
    -        error::not_found(ECOIN_STORE_NOT_PUBLISHED),
    -    );
    -    borrow_global<CoinStore<CoinType>>(owner).coin.value
    +
    inline fun coin_balance<CoinType>(owner: address): u64 {
    +    if (exists<CoinStore<CoinType>>(owner)) {
    +        borrow_global<CoinStore<CoinType>>(owner).coin.value
    +    } else {
    +        0
    +    }
     }
     
    @@ -1053,7 +2507,9 @@ Returns true is account_addr has frozen the CoinStore or if Implementation -
    public fun is_coin_store_frozen<CoinType>(account_addr: address): bool acquires CoinStore {
    +
    public fun is_coin_store_frozen<CoinType>(
    +    account_addr: address
    +): bool acquires CoinStore, CoinConversionMap {
         if (!is_account_registered<CoinType>(account_addr)) {
             return true
         };
    @@ -1084,8 +2540,16 @@ Returns true if account_addr is registered to r
     Implementation
     
     
    -
    public fun is_account_registered<CoinType>(account_addr: address): bool {
    -    exists<CoinStore<CoinType>>(account_addr)
    +
    public fun is_account_registered<CoinType>(account_addr: address): bool acquires CoinConversionMap {
    +    assert!(is_coin_initialized<CoinType>(), error::invalid_argument(ECOIN_INFO_NOT_PUBLISHED));
    +    if (exists<CoinStore<CoinType>>(account_addr)) {
    +        true
    +    } else {
    +        let paired_metadata_opt = paired_metadata<CoinType>();
    +        (option::is_some(
    +            &paired_metadata_opt
    +        ) && migrated_primary_fungible_store_exists(account_addr, option::destroy_some(paired_metadata_opt)))
    +    }
     }
     
    @@ -1190,7 +2654,42 @@ Returns the amount of coin in existence. Implementation -
    public fun supply<CoinType>(): Option<u128> acquires CoinInfo {
    +
    public fun supply<CoinType>(): Option<u128> acquires CoinInfo, CoinConversionMap {
    +    let coin_supply = coin_supply<CoinType>();
    +    let metadata = paired_metadata<CoinType>();
    +    if (option::is_some(&metadata)) {
    +        let fungible_asset_supply = fungible_asset::supply(option::extract(&mut metadata));
    +        if (option::is_some(&coin_supply)) {
    +            let supply = option::borrow_mut(&mut coin_supply);
    +            *supply = *supply + option::destroy_some(fungible_asset_supply);
    +        };
    +    };
    +    coin_supply
    +}
    +
    + + + + + + + +## Function `coin_supply` + +Returns the amount of coin in existence. + + +
    #[view]
    +public fun coin_supply<CoinType>(): option::Option<u128>
    +
    + + + +
    +Implementation + + +
    public fun coin_supply<CoinType>(): Option<u128> acquires CoinInfo {
         let maybe_supply = &borrow_global<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
         if (option::is_some(maybe_supply)) {
             // We do track supply, in this case read from optional aggregator.
    @@ -1224,21 +2723,8 @@ The capability _cap should be passed as a reference to Implementation
     
     
    -
    public fun burn<CoinType>(
    -    coin: Coin<CoinType>,
    -    _cap: &BurnCapability<CoinType>,
    -) acquires CoinInfo {
    -    spec {
    -        update supply<CoinType> = supply<CoinType> - coin.value;
    -    };
    -    let Coin { value: amount } = coin;
    -    assert!(amount > 0, error::invalid_argument(EZERO_COIN_AMOUNT));
    -
    -    let maybe_supply = &mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
    -    if (option::is_some(maybe_supply)) {
    -        let supply = option::borrow_mut(maybe_supply);
    -        optional_aggregator::sub(supply, (amount as u128));
    -    }
    +
    public fun burn<CoinType>(coin: Coin<CoinType>, _cap: &BurnCapability<CoinType>) acquires CoinInfo {
    +    burn_internal(coin);
     }
     
    @@ -1270,15 +2756,28 @@ Note: This bypasses CoinStore::frozen -- coins within a frozen CoinStore can be account_addr: address, amount: u64, burn_cap: &BurnCapability<CoinType>, -) acquires CoinInfo, CoinStore { +) acquires CoinInfo, CoinStore, CoinConversionMap, PairedFungibleAssetRefs { // Skip burning if amount is zero. This shouldn't error out as it's called as part of transaction fee burning. if (amount == 0) { return }; - let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr); - let coin_to_burn = extract(&mut coin_store.coin, amount); - burn(coin_to_burn, burn_cap); + let (coin_amount_to_burn, fa_amount_to_burn) = calculate_amount_to_withdraw<CoinType>( + account_addr, + amount + ); + if (coin_amount_to_burn > 0) { + let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr); + let coin_to_burn = extract(&mut coin_store.coin, coin_amount_to_burn); + burn(coin_to_burn, burn_cap); + }; + if (fa_amount_to_burn > 0) { + fungible_asset::burn_from( + borrow_paired_burn_ref(burn_cap), + primary_fungible_store::primary_store(account_addr, option::destroy_some(paired_metadata<CoinType>())), + fa_amount_to_burn + ); + }; }
    @@ -1302,24 +2801,68 @@ Deposit the coin balance into the recipient's account and emit an event. Implementation -
    public fun deposit<CoinType>(account_addr: address, coin: Coin<CoinType>) acquires CoinStore {
    -    assert!(
    -        is_account_registered<CoinType>(account_addr),
    -        error::not_found(ECOIN_STORE_NOT_PUBLISHED),
    -    );
    +
    public fun deposit<CoinType>(
    +    account_addr: address,
    +    coin: Coin<CoinType>
    +) acquires CoinStore, CoinConversionMap, CoinInfo {
    +    if (exists<CoinStore<CoinType>>(account_addr)) {
    +        let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    +        assert!(
    +            !coin_store.frozen,
    +            error::permission_denied(EFROZEN),
    +        );
    +        if (std::features::module_event_migration_enabled()) {
    +            event::emit(
    +                CoinDeposit { coin_type: type_name<CoinType>(), account: account_addr, amount: coin.value }
    +            );
    +        };
    +        event::emit_event<DepositEvent>(
    +            &mut coin_store.deposit_events,
    +            DepositEvent { amount: coin.value },
    +        );
    +        merge(&mut coin_store.coin, coin);
    +    } else {
    +        let metadata = paired_metadata<CoinType>();
    +        if (option::is_some(&metadata) && migrated_primary_fungible_store_exists(
    +            account_addr,
    +            option::destroy_some(metadata)
    +        )) {
    +            primary_fungible_store::deposit(account_addr, coin_to_fungible_asset(coin));
    +        } else {
    +            abort error::not_found(ECOIN_STORE_NOT_PUBLISHED)
    +        };
    +    }
    +}
    +
    + + + +
    + + + +## Function `migrated_primary_fungible_store_exists` + + + +
    fun migrated_primary_fungible_store_exists(account_address: address, metadata: object::Object<fungible_asset::Metadata>): bool
    +
    + + + +
    +Implementation - let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr); - assert!( - !coin_store.frozen, - error::permission_denied(EFROZEN), - ); - event::emit_event<DepositEvent>( - &mut coin_store.deposit_events, - DepositEvent { amount: coin.value }, - ); - event::emit(Deposit<CoinType> { account: account_addr, amount: coin.value }); - merge(&mut coin_store.coin, coin); +
    inline fun migrated_primary_fungible_store_exists(
    +    account_address: address,
    +    metadata: Object<Metadata>
    +): bool {
    +    let primary_store_address = primary_fungible_store::primary_store_address<Metadata>(account_address, metadata);
    +    fungible_asset::store_exists(primary_store_address) && (
    +        // migration flag is needed, until we start defaulting new accounts to APT PFS
    +        features::new_accounts_default_to_fa_apt_store_enabled() || exists<MigrationFlag>(primary_store_address)
    +    )
     }
     
    @@ -1344,15 +2887,27 @@ This is for internal use only and doesn't emit an DepositEvent. Implementation -
    public(friend) fun force_deposit<CoinType>(account_addr: address, coin: Coin<CoinType>) acquires CoinStore {
    -    assert!(
    -        is_account_registered<CoinType>(account_addr),
    -        error::not_found(ECOIN_STORE_NOT_PUBLISHED),
    -    );
    -
    -    let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    -
    -    merge(&mut coin_store.coin, coin);
    +
    public(friend) fun force_deposit<CoinType>(
    +    account_addr: address,
    +    coin: Coin<CoinType>
    +) acquires CoinStore, CoinConversionMap, CoinInfo {
    +    if (exists<CoinStore<CoinType>>(account_addr)) {
    +        let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    +        merge(&mut coin_store.coin, coin);
    +    } else {
    +        let metadata = paired_metadata<CoinType>();
    +        if (option::is_some(&metadata) && migrated_primary_fungible_store_exists(
    +            account_addr,
    +            option::destroy_some(metadata)
    +        )) {
    +            let fa = coin_to_fungible_asset(coin);
    +            let metadata = fungible_asset::asset_metadata(&fa);
    +            let store = primary_fungible_store::primary_store(account_addr, metadata);
    +            fungible_asset::deposit_internal(object::object_address(&store), fa);
    +        } else {
    +            abort error::not_found(ECOIN_STORE_NOT_PUBLISHED)
    +        }
    +    }
     }
     
    @@ -1746,31 +3301,7 @@ Returns minted Coin. amount: u64, _cap: &MintCapability<CoinType>, ): Coin<CoinType> acquires CoinInfo { - if (amount == 0) { - return Coin<CoinType> { - value: 0 - } - }; - - let maybe_supply = &mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply; - if (option::is_some(maybe_supply)) { - let supply = option::borrow_mut(maybe_supply); - spec { - use aptos_framework::optional_aggregator; - use aptos_framework::aggregator; - assume optional_aggregator::is_parallelizable(supply) ==> (aggregator::spec_aggregator_get_val( - option::borrow(supply.aggregator) - ) - + amount <= aggregator::spec_get_limit(option::borrow(supply.aggregator))); - assume !optional_aggregator::is_parallelizable(supply) ==> - (option::borrow(supply.integer).value + amount <= option::borrow(supply.integer).limit); - }; - optional_aggregator::add(supply, (amount as u128)); - }; - spec { - update supply<CoinType> = supply<CoinType> + amount; - }; - Coin<CoinType> { value: amount } + mint_internal<CoinType>(amount) }
    @@ -1793,7 +3324,7 @@ Returns minted Coin. Implementation -
    public fun register<CoinType>(account: &signer) {
    +
    public fun register<CoinType>(account: &signer) acquires CoinConversionMap {
         let account_addr = signer::address_of(account);
         // Short-circuit and do nothing if account is already registered for CoinType.
         if (is_account_registered<CoinType>(account_addr)) {
    @@ -1835,7 +3366,7 @@ Transfers amount of coins CoinType from fromsigner,
         to: address,
         amount: u64,
    -) acquires CoinStore {
    +) acquires CoinStore, CoinConversionMap, CoinInfo, PairedCoinType {
         let coin = withdraw<CoinType>(from, amount);
         deposit(to, coin);
     }
    @@ -1889,26 +3420,43 @@ Withdraw specified amount of coin CoinType from the si
     
    public fun withdraw<CoinType>(
         account: &signer,
         amount: u64,
    -): Coin<CoinType> acquires CoinStore {
    +): Coin<CoinType> acquires CoinStore, CoinConversionMap, CoinInfo, PairedCoinType {
         let account_addr = signer::address_of(account);
    -    assert!(
    -        is_account_registered<CoinType>(account_addr),
    -        error::not_found(ECOIN_STORE_NOT_PUBLISHED),
    -    );
    -
    -    let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    -    assert!(
    -        !coin_store.frozen,
    -        error::permission_denied(EFROZEN),
    -    );
     
    -    event::emit_event<WithdrawEvent>(
    -        &mut coin_store.withdraw_events,
    -        WithdrawEvent { amount },
    +    let (coin_amount_to_withdraw, fa_amount_to_withdraw) = calculate_amount_to_withdraw<CoinType>(
    +        account_addr,
    +        amount
         );
    -    event::emit(Withdraw<CoinType> { account: account_addr, amount });
    -
    -    extract(&mut coin_store.coin, amount)
    +    let withdrawn_coin = if (coin_amount_to_withdraw > 0) {
    +        let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
    +        assert!(
    +            !coin_store.frozen,
    +            error::permission_denied(EFROZEN),
    +        );
    +        if (std::features::module_event_migration_enabled()) {
    +            event::emit(
    +                CoinWithdraw {
    +                    coin_type: type_name<CoinType>(), account: account_addr, amount: coin_amount_to_withdraw
    +                }
    +            );
    +        };
    +        event::emit_event<WithdrawEvent>(
    +            &mut coin_store.withdraw_events,
    +            WithdrawEvent { amount: coin_amount_to_withdraw },
    +        );
    +        extract(&mut coin_store.coin, coin_amount_to_withdraw)
    +    } else {
    +        zero()
    +    };
    +    if (fa_amount_to_withdraw > 0) {
    +        let fa = primary_fungible_store::withdraw(
    +            account,
    +            option::destroy_some(paired_metadata<CoinType>()),
    +            fa_amount_to_withdraw
    +        );
    +        merge(&mut withdrawn_coin, fungible_asset_to_coin(fa));
    +    };
    +    withdrawn_coin
     }
     
    @@ -1948,12 +3496,62 @@ Create a new Coin<CoinType> -## Function `destroy_freeze_cap` +## Function `destroy_freeze_cap` + +Destroy a freeze capability. Freeze capability is dangerous and therefore should be destroyed if not used. + + +
    public fun destroy_freeze_cap<CoinType>(freeze_cap: coin::FreezeCapability<CoinType>)
    +
    + + + +
    +Implementation + + +
    public fun destroy_freeze_cap<CoinType>(freeze_cap: FreezeCapability<CoinType>) {
    +    let FreezeCapability<CoinType> {} = freeze_cap;
    +}
    +
    + + + +
    + + + +## Function `destroy_mint_cap` + +Destroy a mint capability. + + +
    public fun destroy_mint_cap<CoinType>(mint_cap: coin::MintCapability<CoinType>)
    +
    + + + +
    +Implementation + + +
    public fun destroy_mint_cap<CoinType>(mint_cap: MintCapability<CoinType>) {
    +    let MintCapability<CoinType> {} = mint_cap;
    +}
    +
    + + + +
    + + + +## Function `destroy_burn_cap` -Destroy a freeze capability. Freeze capability is dangerous and therefore should be destroyed if not used. +Destroy a burn capability. -
    public fun destroy_freeze_cap<CoinType>(freeze_cap: coin::FreezeCapability<CoinType>)
    +
    public fun destroy_burn_cap<CoinType>(burn_cap: coin::BurnCapability<CoinType>)
     
    @@ -1962,8 +3560,8 @@ Destroy a freeze capability. Freeze capability is dangerous and therefore should Implementation -
    public fun destroy_freeze_cap<CoinType>(freeze_cap: FreezeCapability<CoinType>) {
    -    let FreezeCapability<CoinType> {} = freeze_cap;
    +
    public fun destroy_burn_cap<CoinType>(burn_cap: BurnCapability<CoinType>) {
    +    let BurnCapability<CoinType> {} = burn_cap;
     }
     
    @@ -1971,14 +3569,13 @@ Destroy a freeze capability. Freeze capability is dangerous and therefore should
    - + -## Function `destroy_mint_cap` +## Function `mint_internal` -Destroy a mint capability. -
    public fun destroy_mint_cap<CoinType>(mint_cap: coin::MintCapability<CoinType>)
    +
    fun mint_internal<CoinType>(amount: u64): coin::Coin<CoinType>
     
    @@ -1987,8 +3584,32 @@ Destroy a mint capability. Implementation -
    public fun destroy_mint_cap<CoinType>(mint_cap: MintCapability<CoinType>) {
    -    let MintCapability<CoinType> {} = mint_cap;
    +
    fun mint_internal<CoinType>(amount: u64): Coin<CoinType> acquires CoinInfo {
    +    if (amount == 0) {
    +        return Coin<CoinType> {
    +            value: 0
    +        }
    +    };
    +
    +    let maybe_supply = &mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
    +    if (option::is_some(maybe_supply)) {
    +        let supply = option::borrow_mut(maybe_supply);
    +        spec {
    +            use aptos_framework::optional_aggregator;
    +            use aptos_framework::aggregator;
    +            assume optional_aggregator::is_parallelizable(supply) ==> (aggregator::spec_aggregator_get_val(
    +                option::borrow(supply.aggregator)
    +            )
    +                + amount <= aggregator::spec_get_limit(option::borrow(supply.aggregator)));
    +            assume !optional_aggregator::is_parallelizable(supply) ==>
    +                (option::borrow(supply.integer).value + amount <= option::borrow(supply.integer).limit);
    +        };
    +        optional_aggregator::add(supply, (amount as u128));
    +    };
    +    spec {
    +        update supply<CoinType> = supply<CoinType> + amount;
    +    };
    +    Coin<CoinType> { value: amount }
     }
     
    @@ -1996,14 +3617,13 @@ Destroy a mint capability. - + -## Function `destroy_burn_cap` +## Function `burn_internal` -Destroy a burn capability. -
    public fun destroy_burn_cap<CoinType>(burn_cap: coin::BurnCapability<CoinType>)
    +
    fun burn_internal<CoinType>(coin: coin::Coin<CoinType>): u64
     
    @@ -2012,8 +3632,19 @@ Destroy a burn capability. Implementation -
    public fun destroy_burn_cap<CoinType>(burn_cap: BurnCapability<CoinType>) {
    -    let BurnCapability<CoinType> {} = burn_cap;
    +
    fun burn_internal<CoinType>(coin: Coin<CoinType>): u64 acquires CoinInfo {
    +    spec {
    +        update supply<CoinType> = supply<CoinType> - coin.value;
    +    };
    +    let Coin { value: amount } = coin;
    +    if (amount != 0) {
    +        let maybe_supply = &mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
    +        if (option::is_some(maybe_supply)) {
    +            let supply = option::borrow_mut(maybe_supply);
    +            optional_aggregator::sub(supply, (amount as u128));
    +        };
    +    };
    +    amount
     }
     
    @@ -2087,7 +3718,7 @@ Destroy a burn capability. 7 -It should always be possible to (1) determine if a coin exists, and (2) determine if a user registered an account with a particular coin. If a coin exists, it should always be possible to request the following +It should always be possible to (1) determine if a coin exists, and (2) determine if a user registered an account with a particular coin. If a coin exists, it should always be possible to request the following information of the coin: (1) Name, (2) Symbol, and (3) Supply. Low The following functions should never abort: (1) is_coin_initialized, and (2) is_account_registered. The following functions should not abort if the coin exists: (1) name, (2) symbol, and (3) supply. Formally Verified in corresponding functions: is_coin_initialized, is_account_registered, name, symbol and supply. @@ -2125,10 +3756,7 @@ Destroy a burn capability. global aggregate_supply<CoinType>: num; apply TotalSupplyTracked<CoinType> to *<CoinType> except - initialize, initialize_internal, initialize_with_parallelizable_supply; -// This enforces high-level requirement 4 and high-level requirement 9: -apply TotalSupplyNoChange<CoinType> to *<CoinType> except mint, - burn, burn_from, initialize, initialize_internal, initialize_with_parallelizable_supply; +initialize, initialize_internal, initialize_with_parallelizable_supply;
    @@ -2139,7 +3767,7 @@ Destroy a burn capability.
    fun spec_fun_supply_tracked<CoinType>(val: u64, supply: Option<OptionalAggregator>): bool {
        option::spec_is_some(supply) ==> val == optional_aggregator::optional_aggregator_value
    -           (option::spec_borrow(supply))
    +       (option::spec_borrow(supply))
     }
     
    @@ -2186,6 +3814,64 @@ Destroy a burn capability. + + + + +
    fun spec_is_account_registered<CoinType>(account_addr: address): bool {
    +   let paired_metadata_opt = spec_paired_metadata<CoinType>();
    +   exists<CoinStore<CoinType>>(account_addr) || (option::spec_is_some(
    +       paired_metadata_opt
    +   ) && primary_fungible_store::spec_primary_store_exists(account_addr, option::spec_borrow(paired_metadata_opt)))
    +}
    +
    + + + + + + + +
    schema CoinSubAbortsIf<CoinType> {
    +    amount: u64;
    +    let addr = type_info::type_of<CoinType>().account_address;
    +    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
    +    include (option::is_some(
    +        maybe_supply
    +    )) ==> optional_aggregator::SubAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
    +}
    +
    + + + + + + + +
    schema CoinAddAbortsIf<CoinType> {
    +    amount: u64;
    +    let addr = type_info::type_of<CoinType>().account_address;
    +    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
    +    include (option::is_some(
    +        maybe_supply
    +    )) ==> optional_aggregator::AddAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
    +}
    +
    + + + + + + + +
    schema AbortsIfNotExistCoinInfo<CoinType> {
    +    let addr = type_info::type_of<CoinType>().account_address;
    +    aborts_if !exists<CoinInfo<CoinType>>(addr);
    +}
    +
    + + + ### Struct `AggregatableCoin` @@ -2212,6 +3898,40 @@ Destroy a burn capability. + + +### Function `coin_to_fungible_asset` + + +
    public fun coin_to_fungible_asset<CoinType>(coin: coin::Coin<CoinType>): fungible_asset::FungibleAsset
    +
    + + + + +
    pragma verify = false;
    +let addr = type_info::type_of<CoinType>().account_address;
    +modifies global<CoinInfo<CoinType>>(addr);
    +
    + + + + + +### Function `fungible_asset_to_coin` + + +
    fun fungible_asset_to_coin<CoinType>(fungible_asset: fungible_asset::FungibleAsset): coin::Coin<CoinType>
    +
    + + + + +
    pragma verify = false;
    +
    + + + ### Function `initialize_supply_config` @@ -2267,7 +3987,7 @@ Can only be updated by @aptos_framework. -
    include system_addresses::AbortsIfNotAptosFramework{account: aptos_framework};
    +
    include system_addresses::AbortsIfNotAptosFramework { account: aptos_framework };
     include aggregator_factory::CreateAggregatorInternalAbortsIf;
     
    @@ -2324,7 +4044,7 @@ Can only be updated by @aptos_framework. + coin.value > aggregator::spec_get_limit(aggr); aborts_if aggregator::spec_aggregator_get_val(aggr) + coin.value > MAX_U128; -ensures aggregator::spec_aggregator_get_val(aggr)+ coin.value == aggregator::spec_aggregator_get_val(p_aggr); +ensures aggregator::spec_aggregator_get_val(aggr) + coin.value == aggregator::spec_aggregator_get_val(p_aggr);
    @@ -2340,7 +4060,8 @@ Can only be updated by @aptos_framework. -
    let aggr = dst_coin.value;
    +
    pragma verify = false;
    +let aggr = dst_coin.value;
     let post p_aggr = dst_coin.value;
     let coin_store = global<CoinStore<CoinType>>(account_addr);
     let post p_coin_store = global<CoinStore<CoinType>>(account_addr);
    @@ -2350,12 +4071,44 @@ Can only be updated by @aptos_framework.
         + amount > aggregator::spec_get_limit(aggr);
     aborts_if amount > 0 && aggregator::spec_aggregator_get_val(aggr)
         + amount > MAX_U128;
    -ensures aggregator::spec_aggregator_get_val(aggr)+ amount == aggregator::spec_aggregator_get_val(p_aggr);
    +ensures aggregator::spec_aggregator_get_val(aggr) + amount == aggregator::spec_aggregator_get_val(p_aggr);
     ensures coin_store.coin.value - amount == p_coin_store.coin.value;
     
    + + +### Function `maybe_convert_to_fungible_store` + + +
    fun maybe_convert_to_fungible_store<CoinType>(account: address)
    +
    + + + + +
    pragma verify = false;
    +modifies global<CoinInfo<CoinType>>(account);
    +modifies global<CoinStore<CoinType>>(account);
    +
    + + + + + + + +
    schema DepositAbortsIf<CoinType> {
    +    account_addr: address;
    +    let coin_store = global<CoinStore<CoinType>>(account_addr);
    +    aborts_if !exists<CoinStore<CoinType>>(account_addr);
    +    aborts_if coin_store.frozen;
    +}
    +
    + + + ### Function `coin_address` @@ -2387,7 +4140,7 @@ Get address by reflection. -
    // This enforces high-level requirement 6:
    +
    pragma verify = false;
     aborts_if !exists<CoinStore<CoinType>>(owner);
     ensures result == global<CoinStore<CoinType>>(owner).coin.value;
     
    @@ -2424,7 +4177,7 @@ Get address by reflection. -
    // This enforces high-level requirement 5 and high-level requirement 7:
    +
    pragma aborts_if_is_partial;
     aborts_if false;
     
    @@ -2442,40 +4195,21 @@ Get address by reflection. - - - -
    schema CoinSubAbortsIf<CoinType> {
    -    amount: u64;
    -    let addr =  type_info::type_of<CoinType>().account_address;
    -    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
    -    include (option::is_some(maybe_supply)) ==> optional_aggregator::SubAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
    -}
    -
    - - - - - - - -
    schema CoinAddAbortsIf<CoinType> {
    -    amount: u64;
    -    let addr =  type_info::type_of<CoinType>().account_address;
    -    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
    -    include (option::is_some(maybe_supply)) ==> optional_aggregator::AddAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
    -}
    -
    - - - - - + -
    schema AbortsIfNotExistCoinInfo<CoinType> {
    -    let addr = type_info::type_of<CoinType>().account_address;
    -    aborts_if !exists<CoinInfo<CoinType>>(addr);
    +
    fun spec_paired_metadata<CoinType>(): Option<Object<Metadata>> {
    +   if (exists<CoinConversionMap>(@aptos_framework)) {
    +       let map = global<CoinConversionMap>(@aptos_framework).coin_to_fungible_asset_map;
    +       if (table::spec_contains(map, type_info::type_of<CoinType>())) {
    +           let metadata = table::spec_get(map, type_info::type_of<CoinType>());
    +           option::spec_some(metadata)
    +       } else {
    +           option::spec_none()
    +       }
    +   } else {
    +       option::spec_none()
    +   }
     }
     
    @@ -2546,6 +4280,23 @@ Get address by reflection. +
    pragma verify = false;
    +
    + + + + + +### Function `coin_supply` + + +
    #[view]
    +public fun coin_supply<CoinType>(): option::Option<u128>
    +
    + + + +
    let coin_addr = type_info::type_of<CoinType>().account_address;
     // This enforces high-level requirement 7:
     aborts_if !exists<CoinInfo<CoinType>>(coin_addr);
    @@ -2572,7 +4323,8 @@ Get address by reflection.
     
     
     
    -
    let addr =  type_info::type_of<CoinType>().account_address;
    +
    pragma verify = false;
    +let addr = type_info::type_of<CoinType>().account_address;
     modifies global<CoinInfo<CoinType>>(addr);
     include AbortsIfNotExistCoinInfo<CoinType>;
     aborts_if coin.value == 0;
    @@ -2593,7 +4345,8 @@ Get address by reflection.
     
     
     
    -
    let addr = type_info::type_of<CoinType>().account_address;
    +
    pragma verify = false;
    +let addr = type_info::type_of<CoinType>().account_address;
     let coin_store = global<CoinStore<CoinType>>(account_addr);
     let post post_coin_store = global<CoinStore<CoinType>>(account_addr);
     modifies global<CoinInfo<CoinType>>(addr);
    @@ -2610,7 +4363,7 @@ Get address by reflection.
     let post post_value = optional_aggregator::optional_aggregator_value(post_supply);
     aborts_if option::spec_is_some(maybe_supply) && value < amount;
     ensures post_coin_store.coin.value == coin_store.coin.value - amount;
    -// This enforces high-level requirement 5 of the managed_coin module:
    +// This enforces high-level requirement 5 of the managed_coin module:
     ensures if (option::spec_is_some(maybe_supply)) {
         post_value == value - amount
     } else {
    @@ -2633,24 +4386,13 @@ Get address by reflection.
     account_addr is not frozen.
     
     
    -
    modifies global<CoinInfo<CoinType>>(account_addr);
    +
    pragma verify = false;
    +modifies global<CoinInfo<CoinType>>(account_addr);
     // This enforces high-level requirement 8:
     include DepositAbortsIf<CoinType>;
    -ensures global<CoinStore<CoinType>>(account_addr).coin.value == old(global<CoinStore<CoinType>>(account_addr)).coin.value + coin.value;
    -
    - - - - - - - -
    schema DepositAbortsIf<CoinType> {
    -    account_addr: address;
    -    let coin_store = global<CoinStore<CoinType>>(account_addr);
    -    aborts_if !exists<CoinStore<CoinType>>(account_addr);
    -    aborts_if coin_store.frozen;
    -}
    +ensures global<CoinStore<CoinType>>(account_addr).coin.value == old(
    +    global<CoinStore<CoinType>>(account_addr)
    +).coin.value + coin.value;
     
    @@ -2666,9 +4408,12 @@ Get address by reflection. -
    modifies global<CoinStore<CoinType>>(account_addr);
    +
    pragma verify = false;
    +modifies global<CoinStore<CoinType>>(account_addr);
     aborts_if !exists<CoinStore<CoinType>>(account_addr);
    -ensures global<CoinStore<CoinType>>(account_addr).coin.value == old(global<CoinStore<CoinType>>(account_addr)).coin.value + coin.value;
    +ensures global<CoinStore<CoinType>>(account_addr).coin.value == old(
    +    global<CoinStore<CoinType>>(account_addr)
    +).coin.value + coin.value;
     
    @@ -2737,7 +4482,7 @@ The value of zero_coin must be 0. -
    pragma opaque;
    +
    pragma verify = false;
     modifies global<CoinStore<CoinType>>(account_addr);
     // This enforces high-level requirement 6:
     aborts_if !exists<CoinStore<CoinType>>(account_addr);
    @@ -2759,7 +4504,7 @@ The value of zero_coin must be 0.
     
     
     
    -
    pragma opaque;
    +
    pragma verify = false;
     modifies global<CoinStore<CoinType>>(account_addr);
     // This enforces high-level requirement 6:
     aborts_if !exists<CoinStore<CoinType>>(account_addr);
    @@ -2842,7 +4587,7 @@ The creator of CoinType must be @aptos_framework.
     
    let addr = signer::address_of(account);
     aborts_if addr != @aptos_framework;
     aborts_if monitor_supply && !exists<aggregator_factory::AggregatorFactory>(@aptos_framework);
    -include InitializeInternalSchema<CoinType>{
    +include InitializeInternalSchema<CoinType> {
         name: name.bytes,
         symbol: symbol.bytes
     };
    @@ -2850,27 +4595,6 @@ The creator of CoinType must be @aptos_framework.
     
    -Make sure name and symbol are legal length. -Only the creator of CoinType can initialize. - - - - - -
    schema InitializeInternalSchema<CoinType> {
    -    account: signer;
    -    name: vector<u8>;
    -    symbol: vector<u8>;
    -    let account_addr = signer::address_of(account);
    -    let coin_address = type_info::type_of<CoinType>().account_address;
    -    aborts_if coin_address != account_addr;
    -    aborts_if exists<CoinInfo<CoinType>>(account_addr);
    -    aborts_if len(name) > MAX_COIN_NAME_LENGTH;
    -    aborts_if len(symbol) > MAX_COIN_SYMBOL_LENGTH;
    -}
    -
    - - @@ -2883,7 +4607,7 @@ Only the creator of CoinType can initialize. -
    include InitializeInternalSchema<CoinType>{
    +
    include InitializeInternalSchema<CoinType> {
         name: name.bytes,
         symbol: symbol.bytes
     };
    @@ -2895,7 +4619,7 @@ Only the creator of CoinType can initialize.
     modifies global<CoinInfo<CoinType>>(account_addr);
     aborts_if monitor_supply && parallelizable
         && !exists<aggregator_factory::AggregatorFactory>(@aptos_framework);
    -// This enforces high-level requirement 2 of the managed_coin module:
    +// This enforces high-level requirement 2 of the managed_coin module:
     ensures exists<CoinInfo<CoinType>>(account_addr)
         && coin_info.name == name
         && coin_info.symbol == symbol
    @@ -2943,9 +4667,6 @@ Only the creator of CoinType can initialize.
     
     
    let addr = type_info::type_of<CoinType>().account_address;
     modifies global<CoinInfo<CoinType>>(addr);
    -aborts_if (amount != 0) && !exists<CoinInfo<CoinType>>(addr);
    -ensures supply<CoinType> == old(supply<CoinType>) + amount;
    -ensures result.value == amount;
     
    @@ -2963,14 +4684,7 @@ An account can only be registered once. Updating Account.guid_creation_num will not overflow. -
    let account_addr = signer::address_of(account);
    -let acc = global<account::Account>(account_addr);
    -aborts_if !exists<CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
    -aborts_if !exists<CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 > MAX_U64;
    -// This enforces high-level requirement 5:
    -aborts_if !exists<CoinStore<CoinType>>(account_addr) && !exists<account::Account>(account_addr);
    -aborts_if !exists<CoinStore<CoinType>>(account_addr) && !type_info::spec_is_struct<CoinType>();
    -ensures exists<CoinStore<CoinType>>(account_addr);
    +
    pragma verify = false;
     
    @@ -2989,7 +4703,8 @@ Updating Account.guid_creation_num will not overflow. from account sufficient balance. -
    let account_addr_from = signer::address_of(from);
    +
    pragma verify = false;
    +let account_addr_from = signer::address_of(from);
     let coin_store_from = global<CoinStore<CoinType>>(account_addr_from);
     let post coin_store_post_from = global<CoinStore<CoinType>>(account_addr_from);
     let coin_store_to = global<CoinStore<CoinType>>(to);
    @@ -3002,7 +4717,7 @@ Updating Account.guid_creation_num will not overflow.
     aborts_if coin_store_to.frozen;
     aborts_if coin_store_from.coin.value < amount;
     ensures account_addr_from != to ==> coin_store_post_from.coin.value ==
    -         coin_store_from.coin.value - amount;
    +    coin_store_from.coin.value - amount;
     ensures account_addr_from != to ==> coin_store_post_to.coin.value == coin_store_to.coin.value + amount;
     ensures account_addr_from == to ==> coin_store_post_from.coin.value == coin_store_from.coin.value;
     
    @@ -3021,14 +4736,15 @@ Updating Account.guid_creation_num will not overflow. Account is not frozen and sufficient balance. -
    include WithdrawAbortsIf<CoinType>;
    +
    pragma verify = false;
    +include WithdrawAbortsIf<CoinType>;
     modifies global<CoinStore<CoinType>>(account_addr);
     let account_addr = signer::address_of(account);
     let coin_store = global<CoinStore<CoinType>>(account_addr);
     let balance = coin_store.coin.value;
     let post coin_post = global<CoinStore<CoinType>>(account_addr).coin.value;
     ensures coin_post == balance - amount;
    -ensures result == Coin<CoinType>{value: amount};
    +ensures result == Coin<CoinType> { value: amount };
     
    @@ -3052,4 +4768,42 @@ Account is not frozen and sufficient balance.
    + + + +### Function `mint_internal` + + +
    fun mint_internal<CoinType>(amount: u64): coin::Coin<CoinType>
    +
    + + + + +
    let addr = type_info::type_of<CoinType>().account_address;
    +modifies global<CoinInfo<CoinType>>(addr);
    +aborts_if (amount != 0) && !exists<CoinInfo<CoinType>>(addr);
    +ensures supply<CoinType> == old(supply<CoinType>) + amount;
    +ensures result.value == amount;
    +
    + + + + + +### Function `burn_internal` + + +
    fun burn_internal<CoinType>(coin: coin::Coin<CoinType>): u64
    +
    + + + + +
    pragma verify = false;
    +let addr = type_info::type_of<CoinType>().account_address;
    +modifies global<CoinInfo<CoinType>>(addr);
    +
    + + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/config_buffer.md b/aptos-move/framework/aptos-framework/doc/config_buffer.md index 025b452a56585..a4ee3dd11301d 100644 --- a/aptos-move/framework/aptos-framework/doc/config_buffer.md +++ b/aptos-move/framework/aptos-framework/doc/config_buffer.md @@ -316,7 +316,6 @@ Typically used in X::on_new_epoch() where X is an on-chaon config.
    schema OnNewEpochAbortsIf<T> {
         let type_name = type_info::type_name<T>();
    -    aborts_if spec_fun_does_exist<T>(type_name) && !exists<T>(@aptos_framework);
         let configs = global<PendingConfigs>(@aptos_framework);
         include spec_fun_does_exist<T>(type_name) ==> any::UnpackAbortsIf<T> {
             x: simple_map::spec_get(configs.configs, type_name)
    @@ -332,7 +331,6 @@ Typically used in X::on_new_epoch() where X is an on-chaon config.
     
     
    schema OnNewEpochRequirement<T> {
         let type_name = type_info::type_name<T>();
    -    requires spec_fun_does_exist<T>(type_name) ==> exists<T>(@aptos_framework);
         let configs = global<PendingConfigs>(@aptos_framework);
         include spec_fun_does_exist<T>(type_name) ==> any::UnpackRequirement<T> {
             x: simple_map::spec_get(configs.configs, type_name)
    diff --git a/aptos-move/framework/aptos-framework/doc/consensus_config.md b/aptos-move/framework/aptos-framework/doc/consensus_config.md
    index d8f9352427a76..ddceb292dbded 100644
    --- a/aptos-move/framework/aptos-framework/doc/consensus_config.md
    +++ b/aptos-move/framework/aptos-framework/doc/consensus_config.md
    @@ -180,7 +180,7 @@ aptos_framework::aptos_governance::reconfigure(&framework_signer);
     Only used in reconfigurations to apply the pending ConsensusConfig, if there is any.
     
     
    -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -189,9 +189,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires ConsensusConfig { +
    public(friend) fun on_new_epoch(framework: &signer) acquires ConsensusConfig {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<ConsensusConfig>()) {
    -        *borrow_global_mut<ConsensusConfig>(@aptos_framework) = config_buffer::extract();
    +        let new_config = config_buffer::extract<ConsensusConfig>();
    +        if (exists<ConsensusConfig>(@aptos_framework)) {
    +            *borrow_global_mut<ConsensusConfig>(@aptos_framework) = new_config;
    +        } else {
    +            move_to(framework, new_config);
    +        };
         }
     }
     
    @@ -350,7 +356,7 @@ When setting now time must be later than last_reconfiguration_time. aborts_if !exists<ConsensusConfig>(@aptos_framework); // This enforces high-level requirement 3: aborts_if !(len(config) > 0); -requires chain_status::is_operating(); +requires chain_status::is_genesis(); requires timestamp::spec_now_microseconds() >= reconfiguration::last_reconfiguration_time(); requires exists<stake::ValidatorFees>(@aptos_framework); requires exists<CoinInfo<AptosCoin>>(@aptos_framework); @@ -380,13 +386,15 @@ When setting now time must be later than last_reconfiguration_time. ### Function `on_new_epoch` -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    -
    include config_buffer::OnNewEpochAbortsIf<ConsensusConfig>;
    +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<ConsensusConfig>;
    +aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/delegation_pool.md b/aptos-move/framework/aptos-framework/doc/delegation_pool.md index 05d6785b8e13e..9ce29ab22430e 100644 --- a/aptos-move/framework/aptos-framework/doc/delegation_pool.md +++ b/aptos-move/framework/aptos-framework/doc/delegation_pool.md @@ -123,17 +123,30 @@ transferred to A - [Resource `GovernanceRecords`](#0x1_delegation_pool_GovernanceRecords) - [Resource `BeneficiaryForOperator`](#0x1_delegation_pool_BeneficiaryForOperator) - [Resource `NextCommissionPercentage`](#0x1_delegation_pool_NextCommissionPercentage) +- [Resource `DelegationPoolAllowlisting`](#0x1_delegation_pool_DelegationPoolAllowlisting) +- [Struct `AddStake`](#0x1_delegation_pool_AddStake) - [Struct `AddStakeEvent`](#0x1_delegation_pool_AddStakeEvent) +- [Struct `ReactivateStake`](#0x1_delegation_pool_ReactivateStake) - [Struct `ReactivateStakeEvent`](#0x1_delegation_pool_ReactivateStakeEvent) +- [Struct `UnlockStake`](#0x1_delegation_pool_UnlockStake) - [Struct `UnlockStakeEvent`](#0x1_delegation_pool_UnlockStakeEvent) +- [Struct `WithdrawStake`](#0x1_delegation_pool_WithdrawStake) - [Struct `WithdrawStakeEvent`](#0x1_delegation_pool_WithdrawStakeEvent) - [Struct `DistributeCommissionEvent`](#0x1_delegation_pool_DistributeCommissionEvent) - [Struct `DistributeCommission`](#0x1_delegation_pool_DistributeCommission) +- [Struct `Vote`](#0x1_delegation_pool_Vote) - [Struct `VoteEvent`](#0x1_delegation_pool_VoteEvent) +- [Struct `CreateProposal`](#0x1_delegation_pool_CreateProposal) - [Struct `CreateProposalEvent`](#0x1_delegation_pool_CreateProposalEvent) +- [Struct `DelegateVotingPower`](#0x1_delegation_pool_DelegateVotingPower) - [Struct `DelegateVotingPowerEvent`](#0x1_delegation_pool_DelegateVotingPowerEvent) - [Struct `SetBeneficiaryForOperator`](#0x1_delegation_pool_SetBeneficiaryForOperator) - [Struct `CommissionPercentageChange`](#0x1_delegation_pool_CommissionPercentageChange) +- [Struct `EnableDelegatorsAllowlisting`](#0x1_delegation_pool_EnableDelegatorsAllowlisting) +- [Struct `DisableDelegatorsAllowlisting`](#0x1_delegation_pool_DisableDelegatorsAllowlisting) +- [Struct `AllowlistDelegator`](#0x1_delegation_pool_AllowlistDelegator) +- [Struct `RemoveDelegatorFromAllowlist`](#0x1_delegation_pool_RemoveDelegatorFromAllowlist) +- [Struct `EvictDelegator`](#0x1_delegation_pool_EvictDelegator) - [Constants](#@Constants_0) - [Function `owner_cap_exists`](#0x1_delegation_pool_owner_cap_exists) - [Function `get_owned_pool_address`](#0x1_delegation_pool_get_owned_pool_address) @@ -152,8 +165,12 @@ transferred to A - [Function `calculate_and_update_voter_total_voting_power`](#0x1_delegation_pool_calculate_and_update_voter_total_voting_power) - [Function `calculate_and_update_remaining_voting_power`](#0x1_delegation_pool_calculate_and_update_remaining_voting_power) - [Function `calculate_and_update_delegator_voter`](#0x1_delegation_pool_calculate_and_update_delegator_voter) +- [Function `calculate_and_update_voting_delegation`](#0x1_delegation_pool_calculate_and_update_voting_delegation) - [Function `get_expected_stake_pool_address`](#0x1_delegation_pool_get_expected_stake_pool_address) - [Function `min_remaining_secs_for_commission_change`](#0x1_delegation_pool_min_remaining_secs_for_commission_change) +- [Function `allowlisting_enabled`](#0x1_delegation_pool_allowlisting_enabled) +- [Function `delegator_allowlisted`](#0x1_delegation_pool_delegator_allowlisted) +- [Function `get_delegators_allowlist`](#0x1_delegation_pool_get_delegators_allowlist) - [Function `initialize_delegation_pool`](#0x1_delegation_pool_initialize_delegation_pool) - [Function `beneficiary_for_operator`](#0x1_delegation_pool_beneficiary_for_operator) - [Function `enable_partial_governance_voting`](#0x1_delegation_pool_enable_partial_governance_voting) @@ -164,6 +181,8 @@ transferred to A - [Function `assert_min_active_balance`](#0x1_delegation_pool_assert_min_active_balance) - [Function `assert_min_pending_inactive_balance`](#0x1_delegation_pool_assert_min_pending_inactive_balance) - [Function `assert_partial_governance_voting_enabled`](#0x1_delegation_pool_assert_partial_governance_voting_enabled) +- [Function `assert_allowlisting_enabled`](#0x1_delegation_pool_assert_allowlisting_enabled) +- [Function `assert_delegator_allowlisted`](#0x1_delegation_pool_assert_delegator_allowlisted) - [Function `coins_to_redeem_to_ensure_min_stake`](#0x1_delegation_pool_coins_to_redeem_to_ensure_min_stake) - [Function `coins_to_transfer_to_ensure_min_stake`](#0x1_delegation_pool_coins_to_transfer_to_ensure_min_stake) - [Function `retrieve_stake_pool_owner`](#0x1_delegation_pool_retrieve_stake_pool_owner) @@ -179,13 +198,20 @@ transferred to A - [Function `calculate_total_voting_power`](#0x1_delegation_pool_calculate_total_voting_power) - [Function `calculate_and_update_delegator_voter_internal`](#0x1_delegation_pool_calculate_and_update_delegator_voter_internal) - [Function `calculate_and_update_delegated_votes`](#0x1_delegation_pool_calculate_and_update_delegated_votes) +- [Function `borrow_mut_delegators_allowlist`](#0x1_delegation_pool_borrow_mut_delegators_allowlist) - [Function `set_operator`](#0x1_delegation_pool_set_operator) - [Function `set_beneficiary_for_operator`](#0x1_delegation_pool_set_beneficiary_for_operator) - [Function `update_commission_percentage`](#0x1_delegation_pool_update_commission_percentage) - [Function `set_delegated_voter`](#0x1_delegation_pool_set_delegated_voter) - [Function `delegate_voting_power`](#0x1_delegation_pool_delegate_voting_power) +- [Function `enable_delegators_allowlisting`](#0x1_delegation_pool_enable_delegators_allowlisting) +- [Function `disable_delegators_allowlisting`](#0x1_delegation_pool_disable_delegators_allowlisting) +- [Function `allowlist_delegator`](#0x1_delegation_pool_allowlist_delegator) +- [Function `remove_delegator_from_allowlist`](#0x1_delegation_pool_remove_delegator_from_allowlist) +- [Function `evict_delegator`](#0x1_delegation_pool_evict_delegator) - [Function `add_stake`](#0x1_delegation_pool_add_stake) - [Function `unlock`](#0x1_delegation_pool_unlock) +- [Function `unlock_internal`](#0x1_delegation_pool_unlock_internal) - [Function `reactivate_stake`](#0x1_delegation_pool_reactivate_stake) - [Function `withdraw`](#0x1_delegation_pool_withdraw) - [Function `withdraw_internal`](#0x1_delegation_pool_withdraw_internal) @@ -225,6 +251,7 @@ transferred to A use 0x1::stake; use 0x1::staking_config; use 0x1::table; +use 0x1::table_with_length; use 0x1::timestamp; use 0x1::vector;
    @@ -416,7 +443,7 @@ Capability that represents ownership over privileged operations on the underlyin ## Struct `VoteDelegation` -Track delgated voter of each delegator. +Track delegated voter of each delegator.
    struct VoteDelegation has copy, drop, store
    @@ -456,7 +483,7 @@ Track delgated voter of each delegator.
     
     ## Struct `DelegatedVotes`
     
    -Track total voteing power of each voter.
    +Track total voting power of each voter.
     
     
     
    struct DelegatedVotes has copy, drop, store
    @@ -621,6 +648,82 @@ This struct should be stored in the delegation pool resource account.
     
    + + + + +## Resource `DelegationPoolAllowlisting` + +Tracks a delegation pool's allowlist of delegators. +If allowlisting is enabled, existing delegators are not implicitly allowlisted and they can be individually +evicted later by the pool owner. + + +
    struct DelegationPoolAllowlisting has key
    +
    + + + +
    +Fields + + +
    +
    +allowlist: smart_table::SmartTable<address, bool> +
    +
    + +
    +
    + + +
    + + + +## Struct `AddStake` + + + +
    #[event]
    +struct AddStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator_address: address +
    +
    + +
    +
    +amount_added: u64 +
    +
    + +
    +
    +add_stake_fee: u64 +
    +
    + +
    +
    + +
    @@ -666,6 +769,46 @@ This struct should be stored in the delegation pool resource account.
    + + + + +## Struct `ReactivateStake` + + + +
    #[event]
    +struct ReactivateStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator_address: address +
    +
    + +
    +
    +amount_reactivated: u64 +
    +
    + +
    +
    + +
    @@ -705,6 +848,46 @@ This struct should be stored in the delegation pool resource account.
    + + + + +## Struct `UnlockStake` + + + +
    #[event]
    +struct UnlockStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator_address: address +
    +
    + +
    +
    +amount_unlocked: u64 +
    +
    + +
    +
    + +
    @@ -744,6 +927,46 @@ This struct should be stored in the delegation pool resource account.
    + + + + +## Struct `WithdrawStake` + + + +
    #[event]
    +struct WithdrawStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator_address: address +
    +
    + +
    +
    +amount_withdrawn: u64 +
    +
    + +
    +
    + +
    @@ -791,7 +1014,8 @@ This struct should be stored in the delegation pool resource account. -
    struct DistributeCommissionEvent has drop, store
    +
    #[event]
    +struct DistributeCommissionEvent has drop, store
     
    @@ -880,6 +1104,58 @@ This struct should be stored in the delegation pool resource account.
    + + + + +## Struct `Vote` + + + +
    #[event]
    +struct Vote has drop, store
    +
    + + + +
    +Fields + + +
    +
    +voter: address +
    +
    + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +delegation_pool: address +
    +
    + +
    +
    +num_votes: u64 +
    +
    + +
    +
    +should_pass: bool +
    +
    + +
    +
    + +
    @@ -931,6 +1207,46 @@ This struct should be stored in the delegation pool resource account.
    + + + + +## Struct `CreateProposal` + + + +
    #[event]
    +struct CreateProposal has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    +delegation_pool: address +
    +
    + +
    +
    + +
    @@ -939,7 +1255,234 @@ This struct should be stored in the delegation pool resource account. -
    struct CreateProposalEvent has drop, store
    +
    struct CreateProposalEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +proposal_id: u64 +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    +delegation_pool: address +
    +
    + +
    +
    + + +
    + + + +## Struct `DelegateVotingPower` + + + +
    #[event]
    +struct DelegateVotingPower has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator: address +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    + + +
    + + + +## Struct `DelegateVotingPowerEvent` + + + +
    struct DelegateVotingPowerEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +delegator: address +
    +
    + +
    +
    +voter: address +
    +
    + +
    +
    + + +
    + + + +## Struct `SetBeneficiaryForOperator` + + + +
    #[event]
    +struct SetBeneficiaryForOperator has drop, store
    +
    + + + +
    +Fields + + +
    +
    +operator: address +
    +
    + +
    +
    +old_beneficiary: address +
    +
    + +
    +
    +new_beneficiary: address +
    +
    + +
    +
    + + +
    + + + +## Struct `CommissionPercentageChange` + + + +
    #[event]
    +struct CommissionPercentageChange has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +owner: address +
    +
    + +
    +
    +commission_percentage_next_lockup_cycle: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `EnableDelegatorsAllowlisting` + + + +
    #[event]
    +struct EnableDelegatorsAllowlisting has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `DisableDelegatorsAllowlisting` + + + +
    #[event]
    +struct DisableDelegatorsAllowlisting has drop, store
     
    @@ -950,19 +1493,7 @@ This struct should be stored in the delegation pool resource account.
    -proposal_id: u64 -
    -
    - -
    -
    -voter: address -
    -
    - -
    -
    -delegation_pool: address +pool_address: address
    @@ -972,13 +1503,14 @@ This struct should be stored in the delegation pool resource account. - + -## Struct `DelegateVotingPowerEvent` +## Struct `AllowlistDelegator` -
    struct DelegateVotingPowerEvent has drop, store
    +
    #[event]
    +struct AllowlistDelegator has drop, store
     
    @@ -995,13 +1527,7 @@ This struct should be stored in the delegation pool resource account.
    -delegator: address -
    -
    - -
    -
    -voter: address +delegator_address: address
    @@ -1011,14 +1537,14 @@ This struct should be stored in the delegation pool resource account. - + -## Struct `SetBeneficiaryForOperator` +## Struct `RemoveDelegatorFromAllowlist`
    #[event]
    -struct SetBeneficiaryForOperator has drop, store
    +struct RemoveDelegatorFromAllowlist has drop, store
     
    @@ -1029,19 +1555,13 @@ This struct should be stored in the delegation pool resource account.
    -operator: address -
    -
    - -
    -
    -old_beneficiary: address +pool_address: address
    -new_beneficiary: address +delegator_address: address
    @@ -1051,14 +1571,14 @@ This struct should be stored in the delegation pool resource account. - + -## Struct `CommissionPercentageChange` +## Struct `EvictDelegator`
    #[event]
    -struct CommissionPercentageChange has drop, store
    +struct EvictDelegator has drop, store
     
    @@ -1075,13 +1595,7 @@ This struct should be stored in the delegation pool resource account.
    -owner: address -
    -
    - -
    -
    -commission_percentage_next_lockup_cycle: u64 +delegator_address: address
    @@ -1194,6 +1708,26 @@ The stake pool has already voted on the proposal before enabling partial governa + + +Cannot evict an allowlisted delegator, should remove them from the allowlist first. + + +
    const ECANNOT_EVICT_ALLOWLISTED_DELEGATOR: u64 = 26;
    +
    + + + + + +Cannot unlock the accumulated active stake of NULL_SHAREHOLDER(0x0). + + +
    const ECANNOT_UNLOCK_NULL_SHAREHOLDER: u64 = 27;
    +
    + + + Changing operator commission rate in delegation pool is not supported. @@ -1224,6 +1758,26 @@ Delegation pool does not exist at the provided pool address. + + +Delegators allowlisting should be enabled to perform this operation. + + +
    const EDELEGATORS_ALLOWLISTING_NOT_ENABLED: u64 = 24;
    +
    + + + + + +Delegators allowlisting is not supported. + + +
    const EDELEGATORS_ALLOWLISTING_NOT_SUPPORTED: u64 = 23;
    +
    + + + Delegator's active balance cannot be less than MIN_COINS_ON_SHARES_POOL. @@ -1234,6 +1788,16 @@ Delegator's active balance cannot be less than + +Cannot add/reactivate stake unless being allowlisted by the pool owner. + + +
    const EDELEGATOR_NOT_ALLOWLISTED: u64 = 25;
    +
    + + + Delegator's pending_inactive balance cannot be less than MIN_COINS_ON_SHARES_POOL. @@ -1266,7 +1830,7 @@ There is not enough active stake on the stake pool to unlock< -Chaning beneficiaries for operators is not supported. +Changing beneficiaries for operators is not supported.
    const EOPERATOR_BENEFICIARY_CHANGE_NOT_SUPPORTED: u64 = 19;
    @@ -1362,7 +1926,7 @@ Maximum operator percentage fee(of double digit precision): 22.85% is represente
     Minimum coins to exist on a shares pool at all times.
     Enforced per delegator for both active and pending_inactive pools.
     This constraint ensures the share price cannot overly increase and lead to
    -substantial loses when buying shares (can lose at most 1 share which may
    +substantial losses when buying shares (can lose at most 1 share which may
     be worth a lot if current share price is high).
     This constraint is not enforced on inactive pools as they only allow redeems
     (can lose at most 1 coin regardless of current share price).
    @@ -1487,7 +2051,7 @@ Return whether a delegation pool exists at supplied address addr.
     
     ## Function `partial_governance_voting_enabled`
     
    -Return whether a delegation pool has already enabled partial govnernance voting.
    +Return whether a delegation pool has already enabled partial governance voting.
     
     
     
    #[view]
    @@ -1580,7 +2144,9 @@ Return the operator commission percentage set on the delegation pool pool_
     Implementation
     
     
    -
    public fun operator_commission_percentage(pool_address: address): u64 acquires DelegationPool, NextCommissionPercentage {
    +
    public fun operator_commission_percentage(
    +    pool_address: address
    +): u64 acquires DelegationPool, NextCommissionPercentage {
         assert_delegation_pool_exists(pool_address);
         if (is_next_commission_percentage_effective(pool_address)) {
             operator_commission_percentage_next_lockup_cycle(pool_address)
    @@ -1611,7 +2177,9 @@ Return the operator commission percentage for the next lockup cycle.
     Implementation
     
     
    -
    public fun operator_commission_percentage_next_lockup_cycle(pool_address: address): u64 acquires DelegationPool, NextCommissionPercentage {
    +
    public fun operator_commission_percentage_next_lockup_cycle(
    +    pool_address: address
    +): u64 acquires DelegationPool, NextCommissionPercentage {
         assert_delegation_pool_exists(pool_address);
         if (exists<NextCommissionPercentage>(pool_address)) {
             borrow_global<NextCommissionPercentage>(pool_address).commission_percentage_next_lockup_cycle
    @@ -1758,7 +2326,10 @@ in each of its individual states: (active,inactive,Implementation
     
     
    -
    public fun get_stake(pool_address: address, delegator_address: address): (u64, u64, u64) acquires DelegationPool, BeneficiaryForOperator {
    +
    public fun get_stake(
    +    pool_address: address,
    +    delegator_address: address
    +): (u64, u64, u64) acquires DelegationPool, BeneficiaryForOperator {
         assert_delegation_pool_exists(pool_address);
         let pool = borrow_global<DelegationPool>(pool_address);
         let (
    @@ -1840,7 +2411,10 @@ extracted-fee = (amount - extracted-fee) * reward-rate% * (100% - operator-commi
     Implementation
     
     
    -
    public fun get_add_stake_fee(pool_address: address, amount: u64): u64 acquires DelegationPool, NextCommissionPercentage {
    +
    public fun get_add_stake_fee(
    +    pool_address: address,
    +    amount: u64
    +): u64 acquires DelegationPool, NextCommissionPercentage {
         if (stake::is_current_epoch_validator(pool_address)) {
             let (rewards_rate, rewards_rate_denominator) = staking_config::get_reward_rate(&staking_config::get());
             if (rewards_rate_denominator > 0) {
    @@ -1905,7 +2479,10 @@ latest state.
     Implementation
     
     
    -
    public fun calculate_and_update_voter_total_voting_power(pool_address: address, voter: address): u64 acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public fun calculate_and_update_voter_total_voting_power(
    +    pool_address: address,
    +    voter: address
    +): u64 acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert_partial_governance_voting_enabled(pool_address);
         // Delegation pool need to be synced to explain rewards(which could change the coin amount) and
         // commission(which could cause share transfer).
    @@ -1939,7 +2516,11 @@ latest state.
     Implementation
     
     
    -
    public fun calculate_and_update_remaining_voting_power(pool_address: address, voter_address: address, proposal_id: u64): u64 acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public fun calculate_and_update_remaining_voting_power(
    +    pool_address: address,
    +    voter_address: address,
    +    proposal_id: u64
    +): u64 acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert_partial_governance_voting_enabled(pool_address);
         // If the whole stake pool has no voting power(e.g. it has already voted before partial
         // governance voting flag is enabled), the delegator also has no voting power.
    @@ -1975,7 +2556,10 @@ latest state.
     Implementation
     
     
    -
    public fun calculate_and_update_delegator_voter(pool_address: address, delegator_address: address): address acquires DelegationPool, GovernanceRecords {
    +
    public fun calculate_and_update_delegator_voter(
    +    pool_address: address,
    +    delegator_address: address
    +): address acquires DelegationPool, GovernanceRecords {
         assert_partial_governance_voting_enabled(pool_address);
         calculate_and_update_delegator_voter_internal(
             borrow_global<DelegationPool>(pool_address),
    @@ -1987,6 +2571,42 @@ latest state.
     
     
     
    +
    +
    +
    +
    +## Function `calculate_and_update_voting_delegation`
    +
    +Return the current state of a voting delegation of a delegator in a delegation pool.
    +
    +
    +
    #[view]
    +public fun calculate_and_update_voting_delegation(pool_address: address, delegator_address: address): (address, address, u64)
    +
    + + + +
    +Implementation + + +
    public fun calculate_and_update_voting_delegation(
    +    pool_address: address,
    +    delegator_address: address
    +): (address, address, u64) acquires DelegationPool, GovernanceRecords {
    +    assert_partial_governance_voting_enabled(pool_address);
    +    let vote_delegation = update_and_borrow_mut_delegator_vote_delegation(
    +        borrow_global<DelegationPool>(pool_address),
    +        borrow_global_mut<GovernanceRecords>(pool_address),
    +        delegator_address
    +    );
    +
    +    (vote_delegation.voter, vote_delegation.pending_voter, vote_delegation.last_locked_until_secs)
    +}
    +
    + + +
    @@ -2042,6 +2662,100 @@ Return the minimum remaining time in seconds for commission change, which is one + + + + +## Function `allowlisting_enabled` + +Return whether allowlisting is enabled for the provided delegation pool. + + +
    #[view]
    +public fun allowlisting_enabled(pool_address: address): bool
    +
    + + + +
    +Implementation + + +
    public fun allowlisting_enabled(pool_address: address): bool {
    +    assert_delegation_pool_exists(pool_address);
    +    exists<DelegationPoolAllowlisting>(pool_address)
    +}
    +
    + + + +
    + + + +## Function `delegator_allowlisted` + +Return whether the provided delegator is allowlisted. +A delegator is allowlisted if: +- allowlisting is disabled on the pool +- delegator is part of the allowlist + + +
    #[view]
    +public fun delegator_allowlisted(pool_address: address, delegator_address: address): bool
    +
    + + + +
    +Implementation + + +
    public fun delegator_allowlisted(
    +    pool_address: address,
    +    delegator_address: address,
    +): bool acquires DelegationPoolAllowlisting {
    +    if (!allowlisting_enabled(pool_address)) { return true };
    +    smart_table::contains(freeze(borrow_mut_delegators_allowlist(pool_address)), delegator_address)
    +}
    +
    + + + +
    + + + +## Function `get_delegators_allowlist` + +Return allowlist or revert if allowlisting is not enabled for the provided delegation pool. + + +
    #[view]
    +public fun get_delegators_allowlist(pool_address: address): vector<address>
    +
    + + + +
    +Implementation + + +
    public fun get_delegators_allowlist(
    +    pool_address: address,
    +): vector<address> acquires DelegationPoolAllowlisting {
    +    assert_allowlisting_enabled(pool_address);
    +
    +    let allowlist = vector[];
    +    smart_table::for_each_ref(freeze(borrow_mut_delegators_allowlist(pool_address)), |delegator, _v| {
    +        vector::push_back(&mut allowlist, *delegator);
    +    });
    +    allowlist
    +}
    +
    + + +
    @@ -2108,8 +2822,9 @@ Ownership over setting the operator/voter is granted to owner who h // save delegation pool ownership and resource account address (inner stake pool address) on `owner` move_to(owner, DelegationPoolOwnership { pool_address }); - // All delegation pool enable partial governace voting by default once the feature flag is enabled. - if (features::partial_governance_voting_enabled() && features::delegation_pool_partial_governance_voting_enabled()) { + // All delegation pool enable partial governance voting by default once the feature flag is enabled. + if (features::partial_governance_voting_enabled( + ) && features::delegation_pool_partial_governance_voting_enabled()) { enable_partial_governance_voting(pool_address); } } @@ -2154,7 +2869,7 @@ Return the beneficiary address of the operator. ## Function `enable_partial_governance_voting` Enable partial governance voting on a stake pool. The voter of this stake pool will be managed by this module. -THe existing voter will be replaced. The function is permissionless. +The existing voter will be replaced. The function is permissionless.
    public entry fun enable_partial_governance_voting(pool_address: address)
    @@ -2170,7 +2885,10 @@ THe existing voter will be replaced. The function is permissionless.
         pool_address: address,
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert!(features::partial_governance_voting_enabled(), error::invalid_state(EDISABLED_FUNCTION));
    -    assert!(features::delegation_pool_partial_governance_voting_enabled(), error::invalid_state(EDISABLED_FUNCTION));
    +    assert!(
    +        features::delegation_pool_partial_governance_voting_enabled(),
    +        error::invalid_state(EDISABLED_FUNCTION)
    +    );
         assert_delegation_pool_exists(pool_address);
         // synchronize delegation and stake pools before any user operation.
         synchronize_delegation_pool(pool_address);
    @@ -2217,13 +2935,23 @@ Vote on a proposal with a voter's voting power. To successfully vote, the follow
     Implementation
     
     
    -
    public entry fun vote(voter: &signer, pool_address: address, proposal_id: u64, voting_power: u64, should_pass: bool) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun vote(
    +    voter: &signer,
    +    pool_address: address,
    +    proposal_id: u64,
    +    voting_power: u64,
    +    should_pass: bool
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert_partial_governance_voting_enabled(pool_address);
         // synchronize delegation and stake pools before any user operation.
         synchronize_delegation_pool(pool_address);
     
         let voter_address = signer::address_of(voter);
    -    let remaining_voting_power = calculate_and_update_remaining_voting_power(pool_address, voter_address, proposal_id);
    +    let remaining_voting_power = calculate_and_update_remaining_voting_power(
    +        pool_address,
    +        voter_address,
    +        proposal_id
    +    );
         if (voting_power > remaining_voting_power) {
             voting_power = remaining_voting_power;
         };
    @@ -2238,6 +2966,18 @@ Vote on a proposal with a voter's voting power. To successfully vote, the follow
         let pool_signer = retrieve_stake_pool_owner(borrow_global<DelegationPool>(pool_address));
         aptos_governance::partial_vote(&pool_signer, pool_address, proposal_id, voting_power, should_pass);
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            Vote {
    +                voter: voter_address,
    +                proposal_id,
    +                delegation_pool: pool_address,
    +                num_votes: voting_power,
    +                should_pass,
    +            }
    +        );
    +    };
    +
         event::emit_event(
             &mut governance_records.vote_events,
             VoteEvent {
    @@ -2304,6 +3044,17 @@ voting power in THIS delegation pool must be not less than the minimum required
         );
     
         let governance_records = borrow_global_mut<GovernanceRecords>(pool_address);
    +
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            CreateProposal {
    +                proposal_id,
    +                voter: voter_addr,
    +                delegation_pool: pool_address,
    +            }
    +        );
    +    };
    +
         event::emit_event(
             &mut governance_records.create_proposal_events,
             CreateProposalEvent {
    @@ -2394,11 +3145,67 @@ voting power in THIS delegation pool must be not less than the minimum required
     
     
     
    -## Function `assert_min_pending_inactive_balance`
    +## Function `assert_min_pending_inactive_balance`
    +
    +
    +
    +
    fun assert_min_pending_inactive_balance(pool: &delegation_pool::DelegationPool, delegator_address: address)
    +
    + + + +
    +Implementation + + +
    fun assert_min_pending_inactive_balance(pool: &DelegationPool, delegator_address: address) {
    +    let balance = pool_u64::balance(pending_inactive_shares_pool(pool), delegator_address);
    +    assert!(
    +        balance >= MIN_COINS_ON_SHARES_POOL,
    +        error::invalid_argument(EDELEGATOR_PENDING_INACTIVE_BALANCE_TOO_LOW)
    +    );
    +}
    +
    + + + +
    + + + +## Function `assert_partial_governance_voting_enabled` + + + +
    fun assert_partial_governance_voting_enabled(pool_address: address)
    +
    + + + +
    +Implementation + + +
    fun assert_partial_governance_voting_enabled(pool_address: address) {
    +    assert_delegation_pool_exists(pool_address);
    +    assert!(
    +        partial_governance_voting_enabled(pool_address),
    +        error::invalid_state(EPARTIAL_GOVERNANCE_VOTING_NOT_ENABLED)
    +    );
    +}
    +
    + + + +
    + + + +## Function `assert_allowlisting_enabled` -
    fun assert_min_pending_inactive_balance(pool: &delegation_pool::DelegationPool, delegator_address: address)
    +
    fun assert_allowlisting_enabled(pool_address: address)
     
    @@ -2407,12 +3214,8 @@ voting power in THIS delegation pool must be not less than the minimum required Implementation -
    fun assert_min_pending_inactive_balance(pool: &DelegationPool, delegator_address: address) {
    -    let balance = pool_u64::balance(pending_inactive_shares_pool(pool), delegator_address);
    -    assert!(
    -        balance >= MIN_COINS_ON_SHARES_POOL,
    -        error::invalid_argument(EDELEGATOR_PENDING_INACTIVE_BALANCE_TOO_LOW)
    -    );
    +
    fun assert_allowlisting_enabled(pool_address: address) {
    +    assert!(allowlisting_enabled(pool_address), error::invalid_state(EDELEGATORS_ALLOWLISTING_NOT_ENABLED));
     }
     
    @@ -2420,13 +3223,13 @@ voting power in THIS delegation pool must be not less than the minimum required - + -## Function `assert_partial_governance_voting_enabled` +## Function `assert_delegator_allowlisted` -
    fun assert_partial_governance_voting_enabled(pool_address: address)
    +
    fun assert_delegator_allowlisted(pool_address: address, delegator_address: address)
     
    @@ -2435,9 +3238,14 @@ voting power in THIS delegation pool must be not less than the minimum required Implementation -
    fun assert_partial_governance_voting_enabled(pool_address: address) {
    -    assert_delegation_pool_exists(pool_address);
    -    assert!(partial_governance_voting_enabled(pool_address), error::invalid_state(EPARTIAL_GOVERNANCE_VOTING_NOT_ENABLED));
    +
    fun assert_delegator_allowlisted(
    +    pool_address: address,
    +    delegator_address: address,
    +) acquires DelegationPoolAllowlisting {
    +    assert!(
    +        delegator_allowlisted(pool_address, delegator_address),
    +        error::permission_denied(EDELEGATOR_NOT_ALLOWLISTED)
    +    );
     }
     
    @@ -2703,7 +3511,11 @@ Borrow the mutable used voting power of a voter on a proposal. Implementation -
    inline fun borrow_mut_used_voting_power(governance_records: &mut GovernanceRecords, voter: address, proposal_id: u64): &mut u64 {
    +
    inline fun borrow_mut_used_voting_power(
    +    governance_records: &mut GovernanceRecords,
    +    voter: address,
    +    proposal_id: u64
    +): &mut u64 {
         let votes = &mut governance_records.votes;
         let key = VotingRecordKey {
             proposal_id,
    @@ -2735,7 +3547,7 @@ Update VoteDelegation of a delegator to up-to-date then borrow_mut it.
     
     
    fun update_and_borrow_mut_delegator_vote_delegation(
         pool: &DelegationPool,
    -    governance_records :&mut GovernanceRecords,
    +    governance_records: &mut GovernanceRecords,
         delegator: address
     ): &mut VoteDelegation {
         let pool_address = get_pool_address(pool);
    @@ -2754,9 +3566,9 @@ Update VoteDelegation of a delegator to up-to-date then borrow_mut it.
     
         let vote_delegation = smart_table::borrow_mut(vote_delegation_table, delegator);
         // A lockup period has passed since last time `vote_delegation` was updated. Pending voter takes effect.
    -    if (vote_delegation.last_locked_until_secs < locked_until_secs &&
    -        vote_delegation.voter != vote_delegation.pending_voter) {
    +    if (vote_delegation.last_locked_until_secs < locked_until_secs) {
             vote_delegation.voter = vote_delegation.pending_voter;
    +        vote_delegation.last_locked_until_secs = locked_until_secs;
         };
         vote_delegation
     }
    @@ -2784,7 +3596,7 @@ Update DelegatedVotes of a voter to up-to-date then borrow_mut it.
     
     
    fun update_and_borrow_mut_delegated_votes(
         pool: &DelegationPool,
    -    governance_records :&mut GovernanceRecords,
    +    governance_records: &mut GovernanceRecords,
         voter: address
     ): &mut DelegatedVotes {
         let pool_address = get_pool_address(pool);
    @@ -2891,7 +3703,11 @@ Update VoteDelegation of a delegator to up-to-date then return the latest voter.
     Implementation
     
     
    -
    fun calculate_and_update_delegator_voter_internal(pool: &DelegationPool, governance_records: &mut GovernanceRecords, delegator: address): address {
    +
    fun calculate_and_update_delegator_voter_internal(
    +    pool: &DelegationPool,
    +    governance_records: &mut GovernanceRecords,
    +    delegator: address
    +): address {
         let vote_delegation = update_and_borrow_mut_delegator_vote_delegation(pool, governance_records, delegator);
         vote_delegation.voter
     }
    @@ -2917,7 +3733,11 @@ Update DelegatedVotes of a voter to up-to-date then return the total voting powe
     Implementation
     
     
    -
    fun calculate_and_update_delegated_votes(pool: &DelegationPool, governance_records: &mut GovernanceRecords, voter: address): u64 {
    +
    fun calculate_and_update_delegated_votes(
    +    pool: &DelegationPool,
    +    governance_records: &mut GovernanceRecords,
    +    voter: address
    +): u64 {
         let delegated_votes = update_and_borrow_mut_delegated_votes(pool, governance_records, voter);
         calculate_total_voting_power(pool, delegated_votes)
     }
    @@ -2925,6 +3745,32 @@ Update DelegatedVotes of a voter to up-to-date then return the total voting powe
     
     
     
    +
    +
    +
    +
    +## Function `borrow_mut_delegators_allowlist`
    +
    +
    +
    +
    fun borrow_mut_delegators_allowlist(pool_address: address): &mut smart_table::SmartTable<address, bool>
    +
    + + + +
    +Implementation + + +
    inline fun borrow_mut_delegators_allowlist(
    +    pool_address: address
    +): &mut SmartTable<address, bool> acquires DelegationPoolAllowlisting {
    +    &mut borrow_global_mut<DelegationPoolAllowlisting>(pool_address).allowlist
    +}
    +
    + + +
    @@ -2964,7 +3810,7 @@ Allows an owner to change the operator of the underlying stake pool. ## Function `set_beneficiary_for_operator` Allows an operator to change its beneficiary. Any existing unpaid commission rewards will be paid to the new -beneficiary. To ensures payment to the current beneficiary, one should first call synchronize_delegation_pool +beneficiary. To ensure payment to the current beneficiary, one should first call synchronize_delegation_pool before switching the beneficiary. An operator can set one beneficiary for delegation pools, not a separate one for each pool. @@ -2978,7 +3824,10 @@ one for each pool. Implementation -
    public entry fun set_beneficiary_for_operator(operator: &signer, new_beneficiary: address) acquires BeneficiaryForOperator {
    +
    public entry fun set_beneficiary_for_operator(
    +    operator: &signer,
    +    new_beneficiary: address
    +) acquires BeneficiaryForOperator {
         assert!(features::operator_beneficiary_change_enabled(), std::error::invalid_state(
             EOPERATOR_BENEFICIARY_CHANGE_NOT_SUPPORTED
         ));
    @@ -3090,7 +3939,10 @@ Allows an owner to change the delegated voter of the underlying stake pool.
         new_voter: address
     ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         // No one can change delegated_voter once the partial governance voting feature is enabled.
    -    assert!(!features::delegation_pool_partial_governance_voting_enabled(), error::invalid_state(EDEPRECATED_FUNCTION));
    +    assert!(
    +        !features::delegation_pool_partial_governance_voting_enabled(),
    +        error::invalid_state(EDEPRECATED_FUNCTION)
    +    );
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
    @@ -3133,10 +3985,10 @@ this change won't take effects until the next lockup period.
         let delegation_pool = borrow_global<DelegationPool>(pool_address);
         let governance_records = borrow_global_mut<GovernanceRecords>(pool_address);
         let delegator_vote_delegation = update_and_borrow_mut_delegator_vote_delegation(
    -            delegation_pool,
    -            governance_records,
    -            delegator_address
    -        );
    +        delegation_pool,
    +        governance_records,
    +        delegator_address
    +    );
         let pending_voter: address = delegator_vote_delegation.pending_voter;
     
         // No need to update if the voter doesn't really change.
    @@ -3162,6 +4014,14 @@ this change won't take effects until the next lockup period.
                 new_delegated_votes.active_shares_next_lockup + active_shares;
         };
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(DelegateVotingPower {
    +            pool_address,
    +            delegator: delegator_address,
    +            voter: new_voter,
    +        })
    +    };
    +
         event::emit_event(&mut governance_records.delegate_voting_power_events, DelegateVotingPowerEvent {
             pool_address,
             delegator: delegator_address,
    @@ -3172,6 +4032,191 @@ this change won't take effects until the next lockup period.
     
     
     
    +
    +
    +
    +
    +## Function `enable_delegators_allowlisting`
    +
    +Enable delegators allowlisting as the pool owner.
    +
    +
    +
    public entry fun enable_delegators_allowlisting(owner: &signer)
    +
    + + + +
    +Implementation + + +
    public entry fun enable_delegators_allowlisting(
    +    owner: &signer,
    +) acquires DelegationPoolOwnership, DelegationPool {
    +    assert!(
    +        features::delegation_pool_allowlisting_enabled(),
    +        error::invalid_state(EDELEGATORS_ALLOWLISTING_NOT_SUPPORTED)
    +    );
    +
    +    let pool_address = get_owned_pool_address(signer::address_of(owner));
    +    if (allowlisting_enabled(pool_address)) { return };
    +
    +    let pool_signer = retrieve_stake_pool_owner(borrow_global<DelegationPool>(pool_address));
    +    move_to(&pool_signer, DelegationPoolAllowlisting { allowlist: smart_table::new<address, bool>() });
    +
    +    event::emit(EnableDelegatorsAllowlisting { pool_address });
    +}
    +
    + + + +
    + + + +## Function `disable_delegators_allowlisting` + +Disable delegators allowlisting as the pool owner. The existing allowlist will be emptied. + + +
    public entry fun disable_delegators_allowlisting(owner: &signer)
    +
    + + + +
    +Implementation + + +
    public entry fun disable_delegators_allowlisting(
    +    owner: &signer,
    +) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
    +    let pool_address = get_owned_pool_address(signer::address_of(owner));
    +    assert_allowlisting_enabled(pool_address);
    +
    +    let DelegationPoolAllowlisting { allowlist } = move_from<DelegationPoolAllowlisting>(pool_address);
    +    // if the allowlist becomes too large, the owner can always remove some delegators
    +    smart_table::destroy(allowlist);
    +
    +    event::emit(DisableDelegatorsAllowlisting { pool_address });
    +}
    +
    + + + +
    + + + +## Function `allowlist_delegator` + +Allowlist a delegator as the pool owner. + + +
    public entry fun allowlist_delegator(owner: &signer, delegator_address: address)
    +
    + + + +
    +Implementation + + +
    public entry fun allowlist_delegator(
    +    owner: &signer,
    +    delegator_address: address,
    +) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
    +    let pool_address = get_owned_pool_address(signer::address_of(owner));
    +    assert_allowlisting_enabled(pool_address);
    +
    +    if (delegator_allowlisted(pool_address, delegator_address)) { return };
    +
    +    smart_table::add(borrow_mut_delegators_allowlist(pool_address), delegator_address, true);
    +
    +    event::emit(AllowlistDelegator { pool_address, delegator_address });
    +}
    +
    + + + +
    + + + +## Function `remove_delegator_from_allowlist` + +Remove a delegator from the allowlist as the pool owner, but do not unlock their stake. + + +
    public entry fun remove_delegator_from_allowlist(owner: &signer, delegator_address: address)
    +
    + + + +
    +Implementation + + +
    public entry fun remove_delegator_from_allowlist(
    +    owner: &signer,
    +    delegator_address: address,
    +) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
    +    let pool_address = get_owned_pool_address(signer::address_of(owner));
    +    assert_allowlisting_enabled(pool_address);
    +
    +    if (!delegator_allowlisted(pool_address, delegator_address)) { return };
    +
    +    smart_table::remove(borrow_mut_delegators_allowlist(pool_address), delegator_address);
    +
    +    event::emit(RemoveDelegatorFromAllowlist { pool_address, delegator_address });
    +}
    +
    + + + +
    + + + +## Function `evict_delegator` + +Evict a delegator that is not allowlisted by unlocking their entire stake. + + +
    public entry fun evict_delegator(owner: &signer, delegator_address: address)
    +
    + + + +
    +Implementation + + +
    public entry fun evict_delegator(
    +    owner: &signer,
    +    delegator_address: address,
    +) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
    +    let pool_address = get_owned_pool_address(signer::address_of(owner));
    +    assert_allowlisting_enabled(pool_address);
    +    assert!(
    +        !delegator_allowlisted(pool_address, delegator_address),
    +        error::invalid_state(ECANNOT_EVICT_ALLOWLISTED_DELEGATOR)
    +    );
    +
    +    // synchronize pool in order to query latest balance of delegator
    +    synchronize_delegation_pool(pool_address);
    +
    +    let pool = borrow_global<DelegationPool>(pool_address);
    +    if (get_delegator_active_shares(pool, delegator_address) == 0) { return };
    +
    +    unlock_internal(delegator_address, pool_address, pool_u64::balance(&pool.active_shares, delegator_address));
    +
    +    event::emit(EvictDelegator { pool_address, delegator_address });
    +}
    +
    + + +
    @@ -3190,9 +4235,17 @@ Add amount of coins to the delegation pool pool_addressImplementation -
    public entry fun add_stake(delegator: &signer, pool_address: address, amount: u64) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun add_stake(
    +    delegator: &signer,
    +    pool_address: address,
    +    amount: u64
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
         // short-circuit if amount to add is 0 so no event is emitted
         if (amount == 0) { return };
    +
    +    let delegator_address = signer::address_of(delegator);
    +    assert_delegator_allowlisted(pool_address, delegator_address);
    +
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
     
    @@ -3200,7 +4253,6 @@ Add amount of coins to the delegation pool pool_addresslet add_stake_fee = get_add_stake_fee(pool_address, amount);
     
         let pool = borrow_global_mut<DelegationPool>(pool_address);
    -    let delegator_address = signer::address_of(delegator);
     
         // stake the entire amount to the stake pool
         aptos_account::transfer(delegator, pool_address, amount);
    @@ -3216,6 +4268,17 @@ Add amount of coins to the delegation pool pool_addressto appreciate all shares on the active pool atomically
         buy_in_active_shares(pool, NULL_SHAREHOLDER, add_stake_fee);
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            AddStake {
    +                pool_address,
    +                delegator_address,
    +                amount_added: amount,
    +                add_stake_fee,
    +            },
    +        );
    +    };
    +
         event::emit_event(
             &mut pool.add_stake_events,
             AddStakeEvent {
    @@ -3249,20 +4312,53 @@ at most how much active stake there is on the stake pool.
     Implementation
     
     
    -
    public entry fun unlock(delegator: &signer, pool_address: address, amount: u64) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun unlock(
    +    delegator: &signer,
    +    pool_address: address,
    +    amount: u64
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         // short-circuit if amount to unlock is 0 so no event is emitted
         if (amount == 0) { return };
     
    -    // fail unlock of more stake than `active` on the stake pool
    -    let (active, _, _, _) = stake::get_stake(pool_address);
    -    assert!(amount <= active, error::invalid_argument(ENOT_ENOUGH_ACTIVE_STAKE_TO_UNLOCK));
    -
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
     
    -    let pool = borrow_global_mut<DelegationPool>(pool_address);
         let delegator_address = signer::address_of(delegator);
    +    unlock_internal(delegator_address, pool_address, amount);
    +}
    +
    + + + + + + + +## Function `unlock_internal` + + + +
    fun unlock_internal(delegator_address: address, pool_address: address, amount: u64)
    +
    + + + +
    +Implementation + + +
    fun unlock_internal(
    +    delegator_address: address,
    +    pool_address: address,
    +    amount: u64
    +) acquires DelegationPool, GovernanceRecords {
    +    assert!(delegator_address != NULL_SHAREHOLDER, error::invalid_argument(ECANNOT_UNLOCK_NULL_SHAREHOLDER));
    +
    +    // fail unlock of more stake than `active` on the stake pool
    +    let (active, _, _, _) = stake::get_stake(pool_address);
    +    assert!(amount <= active, error::invalid_argument(ENOT_ENOUGH_ACTIVE_STAKE_TO_UNLOCK));
     
    +    let pool = borrow_global_mut<DelegationPool>(pool_address);
         amount = coins_to_transfer_to_ensure_min_stake(
             &pool.active_shares,
             pending_inactive_shares_pool(pool),
    @@ -3276,6 +4372,16 @@ at most how much active stake there is on the stake pool.
         buy_in_pending_inactive_shares(pool, delegator_address, amount);
         assert_min_pending_inactive_balance(pool, delegator_address);
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            UnlockStake {
    +                pool_address,
    +                delegator_address,
    +                amount_unlocked: amount,
    +            },
    +        );
    +    };
    +
         event::emit_event(
             &mut pool.unlock_stake_events,
             UnlockStakeEvent {
    @@ -3307,15 +4413,21 @@ Move amount of coins from pending_inactive to active.
     Implementation
     
     
    -
    public entry fun reactivate_stake(delegator: &signer, pool_address: address, amount: u64) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun reactivate_stake(
    +    delegator: &signer,
    +    pool_address: address,
    +    amount: u64
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
         // short-circuit if amount to reactivate is 0 so no event is emitted
         if (amount == 0) { return };
    +
    +    let delegator_address = signer::address_of(delegator);
    +    assert_delegator_allowlisted(pool_address, delegator_address);
    +
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
     
         let pool = borrow_global_mut<DelegationPool>(pool_address);
    -    let delegator_address = signer::address_of(delegator);
    -
         amount = coins_to_transfer_to_ensure_min_stake(
             pending_inactive_shares_pool(pool),
             &pool.active_shares,
    @@ -3330,6 +4442,16 @@ Move amount of coins from pending_inactive to active.
         buy_in_active_shares(pool, delegator_address, amount);
         assert_min_active_balance(pool, delegator_address);
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            ReactivateStake {
    +                pool_address,
    +                delegator_address,
    +                amount_reactivated: amount,
    +            },
    +        );
    +    };
    +
         event::emit_event(
             &mut pool.reactivate_stake_events,
             ReactivateStakeEvent {
    @@ -3361,7 +4483,11 @@ Withdraw amount of owned inactive stake from the delegation pool at
     Implementation
     
     
    -
    public entry fun withdraw(delegator: &signer, pool_address: address, amount: u64) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun withdraw(
    +    delegator: &signer,
    +    pool_address: address,
    +    amount: u64
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert!(amount > 0, error::invalid_argument(EWITHDRAW_ZERO_STAKE));
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
    @@ -3388,7 +4514,11 @@ Withdraw amount of owned inactive stake from the delegation pool at
     Implementation
     
     
    -
    fun withdraw_internal(pool: &mut DelegationPool, delegator_address: address, amount: u64) acquires GovernanceRecords {
    +
    fun withdraw_internal(
    +    pool: &mut DelegationPool,
    +    delegator_address: address,
    +    amount: u64
    +) acquires GovernanceRecords {
         // TODO: recycle storage when a delegator fully exits the delegation pool.
         // short-circuit if amount to withdraw is 0 so no event is emitted
         if (amount == 0) { return };
    @@ -3437,6 +4567,16 @@ Withdraw amount of owned inactive stake from the delegation pool at
         let (_, inactive, _, _) = stake::get_stake(pool_address);
         pool.total_coins_inactive = inactive;
     
    +    if (features::module_event_migration_enabled()) {
    +        event::emit(
    +            WithdrawStake {
    +                pool_address,
    +                delegator_address,
    +                amount_withdrawn: amount,
    +            },
    +        );
    +    };
    +
         event::emit_event(
             &mut pool.withdraw_stake_events,
             WithdrawStakeEvent {
    @@ -3586,7 +4726,7 @@ deposited coins_amount. This function doesn't make any coin transfe
         pool: &mut DelegationPool,
         shareholder: address,
         coins_amount: u64,
    -): u128 acquires GovernanceRecords{
    +): u128 acquires GovernanceRecords {
         let new_shares = pool_u64::amount_to_shares(&pool.active_shares, coins_amount);
         // No need to buy 0 shares.
         if (new_shares == 0) { return 0 };
    @@ -3779,7 +4919,12 @@ escape inactivation when current lockup ends.
         let pool_address = get_pool_address(pool);
         // Only redeem shares from the pending_inactive pool at `lockup_cycle` == current OLC.
         if (partial_governance_voting_enabled(pool_address) && lockup_cycle.index == pool.observed_lockup_cycle.index) {
    -        update_governanace_records_for_redeem_pending_inactive_shares(pool, pool_address, shares_to_redeem, shareholder);
    +        update_governanace_records_for_redeem_pending_inactive_shares(
    +            pool,
    +            pool_address,
    +            shares_to_redeem,
    +            shareholder
    +        );
         };
     
         let inactive_shares = table::borrow_mut(&mut pool.inactive_shares, lockup_cycle);
    @@ -3896,7 +5041,9 @@ shares pools, assign commission to operator and eventually prepare delegation po
     Implementation
     
     
    -
    public entry fun synchronize_delegation_pool(pool_address: address) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
    +
    public entry fun synchronize_delegation_pool(
    +    pool_address: address
    +) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
         assert_delegation_pool_exists(pool_address);
         let pool = borrow_global_mut<DelegationPool>(pool_address);
         let (
    @@ -3932,7 +5079,11 @@ shares pools, assign commission to operator and eventually prepare delegation po
         // reward operator its commission out of uncommitted active rewards (`add_stake` fees already excluded)
         buy_in_active_shares(pool, beneficiary_for_operator(stake::get_operator(pool_address)), commission_active);
         // reward operator its commission out of uncommitted pending_inactive rewards
    -    buy_in_pending_inactive_shares(pool, beneficiary_for_operator(stake::get_operator(pool_address)), commission_pending_inactive);
    +    buy_in_pending_inactive_shares(
    +        pool,
    +        beneficiary_for_operator(stake::get_operator(pool_address)),
    +        commission_pending_inactive
    +    );
     
         event::emit_event(
             &mut pool.distribute_commission_events,
    @@ -3971,7 +5122,9 @@ shares pools, assign commission to operator and eventually prepare delegation po
         };
     
         if (is_next_commission_percentage_effective(pool_address)) {
    -        pool.operator_commission_percentage = borrow_global<NextCommissionPercentage>(pool_address).commission_percentage_next_lockup_cycle;
    +        pool.operator_commission_percentage = borrow_global<NextCommissionPercentage>(
    +            pool_address
    +        ).commission_percentage_next_lockup_cycle;
         }
     }
     
    @@ -3996,17 +5149,26 @@ shares pools, assign commission to operator and eventually prepare delegation po
    inline fun assert_and_update_proposal_used_voting_power(
    -    governance_records: &mut GovernanceRecords, pool_address : address, proposal_id : u64, voting_power: u64
    +    governance_records: &mut GovernanceRecords, pool_address: address, proposal_id: u64, voting_power: u64
     ) {
         let stake_pool_remaining_voting_power = aptos_governance::get_remaining_voting_power(pool_address, proposal_id);
    -    let stake_pool_used_voting_power = aptos_governance::get_voting_power(pool_address) - stake_pool_remaining_voting_power;
    -    let proposal_used_voting_power = smart_table::borrow_mut_with_default(&mut governance_records.votes_per_proposal, proposal_id, 0);
    +    let stake_pool_used_voting_power = aptos_governance::get_voting_power(
    +        pool_address
    +    ) - stake_pool_remaining_voting_power;
    +    let proposal_used_voting_power = smart_table::borrow_mut_with_default(
    +        &mut governance_records.votes_per_proposal,
    +        proposal_id,
    +        0
    +    );
         // A edge case: Before enabling partial governance voting on a delegation pool, the delegation pool has
         // a voter which can vote with all voting power of this delegation pool. If the voter votes on a proposal after
         // partial governance voting flag is enabled, the delegation pool doesn't have enough voting power on this
         // proposal for all the delegators. To be fair, no one can vote on this proposal through this delegation pool.
         // To detect this case, check if the stake pool had used voting power not through delegation_pool module.
    -    assert!(stake_pool_used_voting_power == *proposal_used_voting_power, error::invalid_argument(EALREADY_VOTED_BEFORE_ENABLE_PARTIAL_VOTING));
    +    assert!(
    +        stake_pool_used_voting_power == *proposal_used_voting_power,
    +        error::invalid_argument(EALREADY_VOTED_BEFORE_ENABLE_PARTIAL_VOTING)
    +    );
         *proposal_used_voting_power = *proposal_used_voting_power + voting_power;
     }
     
    @@ -4032,7 +5194,7 @@ shares pools, assign commission to operator and eventually prepare delegation po
    fun update_governance_records_for_buy_in_active_shares(
         pool: &DelegationPool, pool_address: address, new_shares: u128, shareholder: address
    -) acquires GovernanceRecords{
    +) acquires GovernanceRecords {
         // <active shares> of <shareholder> += <new_shares> ---->
         // <active shares> of <current voter of shareholder> += <new_shares>
         // <active shares> of <next voter of shareholder> += <new_shares>
    diff --git a/aptos-move/framework/aptos-framework/doc/dkg.md b/aptos-move/framework/aptos-framework/doc/dkg.md
    index caac04c05e90a..9db68a09dd5d3 100644
    --- a/aptos-move/framework/aptos-framework/doc/dkg.md
    +++ b/aptos-move/framework/aptos-framework/doc/dkg.md
    @@ -467,8 +467,9 @@ Return the dealer epoch of a DKGS
     
     
     
    -
    aborts_if !exists<DKGState>(@aptos_framework);
    -aborts_if option::is_none(global<DKGState>(@aptos_framework).in_progress);
    +
    requires exists<DKGState>(@aptos_framework);
    +requires option::is_some(global<DKGState>(@aptos_framework).in_progress);
    +aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/execution_config.md b/aptos-move/framework/aptos-framework/doc/execution_config.md index 1eaef3114f706..06be205963675 100644 --- a/aptos-move/framework/aptos-framework/doc/execution_config.md +++ b/aptos-move/framework/aptos-framework/doc/execution_config.md @@ -149,7 +149,7 @@ aptos_framework::aptos_governance::reconfigure(&framework_signer); Only used in reconfigurations to apply the pending ExecutionConfig, if there is any. -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -158,10 +158,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires ExecutionConfig { +
    public(friend) fun on_new_epoch(framework: &signer) acquires ExecutionConfig {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<ExecutionConfig>()) {
             let config = config_buffer::extract<ExecutionConfig>();
    -        *borrow_global_mut<ExecutionConfig>(@aptos_framework) = config;
    +        if (exists<ExecutionConfig>(@aptos_framework)) {
    +            *borrow_global_mut<ExecutionConfig>(@aptos_framework) = config;
    +        } else {
    +            move_to(framework, config);
    +        };
         }
     }
     
    @@ -198,7 +203,7 @@ When setting now time must be later than last_reconfiguration_time.
    pragma verify_duration_estimate = 600;
     let addr = signer::address_of(account);
     include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply;
    -requires chain_status::is_operating();
    +requires chain_status::is_genesis();
     requires exists<stake::ValidatorFees>(@aptos_framework);
     requires exists<staking_config::StakingRewardsConfig>(@aptos_framework);
     requires len(config) > 0;
    @@ -232,13 +237,15 @@ When setting now time must be later than last_reconfiguration_time.
     ### Function `on_new_epoch`
     
     
    -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    -
    include config_buffer::OnNewEpochAbortsIf<ExecutionConfig>;
    +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<ExecutionConfig>;
    +aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/fungible_asset.md b/aptos-move/framework/aptos-framework/doc/fungible_asset.md index 4a4b54fba8a79..25a863d8482c9 100644 --- a/aptos-move/framework/aptos-framework/doc/fungible_asset.md +++ b/aptos-move/framework/aptos-framework/doc/fungible_asset.md @@ -10,11 +10,15 @@ metadata object can be any object that equipped with 0x1::aggregator_v2; +use 0x1::create_signer; use 0x1::error; use 0x1::event; use 0x1::features; +use 0x1::function_info; use 0x1::object; use 0x1::option; use 0x1::signer; @@ -154,7 +190,7 @@ Metadata of a Fungible asset
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    -struct Metadata has key
    +struct Metadata has copy, drop, key
     
    @@ -201,6 +237,36 @@ Metadata of a Fungible asset
    + + + + +## Resource `Untransferable` + +Defines a FungibleAsset, such that all FungibleStores stores are untransferable at +the object layer. + + +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct Untransferable has key
    +
    + + + +
    +Fields + + +
    +
    +dummy_field: bool +
    +
    + +
    +
    + +
    @@ -242,6 +308,75 @@ The store object that holds fungible assets of a specific type associated with a
    + + + + +## Resource `DispatchFunctionStore` + + + +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct DispatchFunctionStore has key
    +
    + + + +
    +Fields + + +
    +
    +withdraw_function: option::Option<function_info::FunctionInfo> +
    +
    + +
    +
    +deposit_function: option::Option<function_info::FunctionInfo> +
    +
    + +
    +
    +derived_balance_function: option::Option<function_info::FunctionInfo> +
    +
    + +
    +
    + + +
    + + + +## Resource `ConcurrentFungibleBalance` + +The store object that holds concurrent fungible asset balance. + + +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct ConcurrentFungibleBalance has key
    +
    + + + +
    +Fields + + +
    +
    +balance: aggregator_v2::Aggregator<u64> +
    +
    + The balance of the fungible metadata. +
    +
    + +
    @@ -348,6 +483,34 @@ BurnRef can be used to burn fungible assets from a given holder account. +
    +Fields + + +
    +
    +metadata: object::Object<fungible_asset::Metadata> +
    +
    + +
    +
    + + +
    + + + +## Struct `MutateMetadataRef` + +MutateMetadataRef can be used to directly modify the fungible asset's Metadata. + + +
    struct MutateMetadataRef has drop, store
    +
    + + +
    Fields @@ -609,12 +772,12 @@ Maximum possible coin supply. - + -Insufficient balance to withdraw or transfer. +Trying to re-register dispatch hook on a fungible asset. -
    const EINSUFFICIENT_BALANCE: u64 = 4;
    +
    const EALREADY_REGISTERED: u64 = 29;
     
    @@ -639,6 +802,16 @@ Cannot destroy non-empty fungible assets. + + +Cannot register dispatch hook for APT. + + +
    const EAPT_NOT_DISPATCHABLE: u64 = 31;
    +
    + + + Cannot destroy fungible stores with a non-zero balance. @@ -669,6 +842,16 @@ Burn ref and store do not match. + + +Flag for Concurrent Supply not enabled + + +
    const ECONCURRENT_BALANCE_NOT_ENABLED: u64 = 32;
    +
    + + + Flag for Concurrent Supply not enabled @@ -689,6 +872,26 @@ Decimals is over the maximum of 32 + + +Provided deposit function type doesn't meet the signature requirement. + + +
    const EDEPOSIT_FUNCTION_SIGNATURE_MISMATCH: u64 = 26;
    +
    + + + + + +Provided derived_balance function type doesn't meet the signature requirement. + + +
    const EDERIVED_BALANCE_FUNCTION_SIGNATURE_MISMATCH: u64 = 27;
    +
    + + + Fungible asset and store do not match. @@ -709,6 +912,47 @@ Fungible asset do not match when merging. + + +Fungible metadata does not exist on this account. + + +
    const EFUNGIBLE_METADATA_EXISTENCE: u64 = 30;
    +
    + + + + + +Flag for the existence of fungible store. + + +
    const EFUNGIBLE_STORE_EXISTENCE: u64 = 23;
    +
    + + + + + +Insufficient balance to withdraw or transfer. + + +
    const EINSUFFICIENT_BALANCE: u64 = 4;
    +
    + + + + + +Invalid withdraw/deposit on dispatchable token. The specified token has a dispatchable function hook. +Need to invoke dispatchable_fungible_asset::withdraw/deposit to perform transfer. + + +
    const EINVALID_DISPATCHABLE_OPERATIONS: u64 = 28;
    +
    + + + The fungible asset's supply has exceeded maximum. @@ -721,7 +965,7 @@ The fungible asset's supply has exceeded maximum. -The mint ref and the the store do not match. +The mint ref and the store do not match.
    const EMINT_REF_AND_STORE_MISMATCH: u64 = 7;
    @@ -739,6 +983,16 @@ Name of the fungible asset metadata is too long
     
     
     
    +
    +
    +Account is not the owner of metadata object.
    +
    +
    +
    const ENOT_METADATA_OWNER: u64 = 24;
    +
    + + + Account is not the store's owner. @@ -829,6 +1083,16 @@ URI for the icon of the fungible asset metadata is too long + + +Provided withdraw function type doesn't meet the signature requirement. + + +
    const EWITHDRAW_FUNCTION_SIGNATURE_MISMATCH: u64 = 25;
    +
    + + + @@ -865,6 +1129,78 @@ URI for the icon of the fungible asset metadata is too long + + +## Function `default_to_concurrent_fungible_supply` + + + +
    fun default_to_concurrent_fungible_supply(): bool
    +
    + + + +
    +Implementation + + +
    inline fun default_to_concurrent_fungible_supply(): bool {
    +    features::concurrent_fungible_assets_enabled()
    +}
    +
    + + + +
    + + + +## Function `allow_upgrade_to_concurrent_fungible_balance` + + + +
    fun allow_upgrade_to_concurrent_fungible_balance(): bool
    +
    + + + +
    +Implementation + + +
    inline fun allow_upgrade_to_concurrent_fungible_balance(): bool {
    +    features::concurrent_fungible_balance_enabled()
    +}
    +
    + + + +
    + + + +## Function `default_to_concurrent_fungible_balance` + + + +
    fun default_to_concurrent_fungible_balance(): bool
    +
    + + + +
    +Implementation + + +
    inline fun default_to_concurrent_fungible_balance(): bool {
    +    features::default_to_concurrent_fungible_balance_enabled()
    +}
    +
    + + + +
    + ## Function `add_fungibility` @@ -913,7 +1249,7 @@ if option::some(MAX_U128) is used, it is treated as unlimited supply. } ); - if (features::concurrent_fungible_assets_enabled()) { + if (default_to_concurrent_fungible_supply()) { let unlimited = option::is_none(&maximum_supply); move_to(metadata_object_signer, ConcurrentSupply { current: if (unlimited) { @@ -937,15 +1273,14 @@ if option::some(MAX_U128) is used, it is treated as unlimited supply.
    - + -## Function `generate_mint_ref` +## Function `set_untransferable` -Creates a mint ref that can be used to mint fungible assets from the given fungible object's constructor ref. -This can only be called at object creation time as constructor_ref is only available then. +Set that only untransferable stores can be created for this fungible asset. -
    public fun generate_mint_ref(constructor_ref: &object::ConstructorRef): fungible_asset::MintRef
    +
    public fun set_untransferable(constructor_ref: &object::ConstructorRef)
     
    @@ -954,9 +1289,11 @@ This can only be called at object creation time as constructor_ref is only avail Implementation -
    public fun generate_mint_ref(constructor_ref: &ConstructorRef): MintRef {
    -    let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
    -    MintRef { metadata }
    +
    public fun set_untransferable(constructor_ref: &ConstructorRef) {
    +    let metadata_addr = object::address_from_constructor_ref(constructor_ref);
    +    assert!(exists<Metadata>(metadata_addr), error::not_found(EFUNGIBLE_METADATA_EXISTENCE));
    +    let metadata_signer = &object::generate_signer(constructor_ref);
    +    move_to(metadata_signer, Untransferable {});
     }
     
    @@ -964,15 +1301,15 @@ This can only be called at object creation time as constructor_ref is only avail - + -## Function `generate_burn_ref` +## Function `is_untransferable` -Creates a burn ref that can be used to burn fungible assets from the given fungible object's constructor ref. -This can only be called at object creation time as constructor_ref is only available then. +Returns true if the FA is untransferable. -
    public fun generate_burn_ref(constructor_ref: &object::ConstructorRef): fungible_asset::BurnRef
    +
    #[view]
    +public fun is_untransferable<T: key>(metadata: object::Object<T>): bool
     
    @@ -981,8 +1318,178 @@ This can only be called at object creation time as constructor_ref is only avail Implementation -
    public fun generate_burn_ref(constructor_ref: &ConstructorRef): BurnRef {
    -    let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
    +
    public fun is_untransferable<T: key>(metadata: Object<T>): bool {
    +    exists<Untransferable>(object::object_address(&metadata))
    +}
    +
    + + + + + + + +## Function `register_dispatch_functions` + +Create a fungible asset store whose transfer rule would be overloaded by the provided function. + + +
    public(friend) fun register_dispatch_functions(constructor_ref: &object::ConstructorRef, withdraw_function: option::Option<function_info::FunctionInfo>, deposit_function: option::Option<function_info::FunctionInfo>, derived_balance_function: option::Option<function_info::FunctionInfo>)
    +
    + + + +
    +Implementation + + +
    public(friend) fun register_dispatch_functions(
    +    constructor_ref: &ConstructorRef,
    +    withdraw_function: Option<FunctionInfo>,
    +    deposit_function: Option<FunctionInfo>,
    +    derived_balance_function: Option<FunctionInfo>,
    +) {
    +    // Verify that caller type matches callee type so wrongly typed function cannot be registered.
    +    option::for_each_ref(&withdraw_function, |withdraw_function| {
    +        let dispatcher_withdraw_function_info = function_info::new_function_info_from_address(
    +            @aptos_framework,
    +            string::utf8(b"dispatchable_fungible_asset"),
    +            string::utf8(b"dispatchable_withdraw"),
    +        );
    +
    +        assert!(
    +            function_info::check_dispatch_type_compatibility(
    +                &dispatcher_withdraw_function_info,
    +                withdraw_function
    +            ),
    +            error::invalid_argument(
    +                EWITHDRAW_FUNCTION_SIGNATURE_MISMATCH
    +            )
    +        );
    +    });
    +
    +    option::for_each_ref(&deposit_function, |deposit_function| {
    +        let dispatcher_deposit_function_info = function_info::new_function_info_from_address(
    +            @aptos_framework,
    +            string::utf8(b"dispatchable_fungible_asset"),
    +            string::utf8(b"dispatchable_deposit"),
    +        );
    +        // Verify that caller type matches callee type so wrongly typed function cannot be registered.
    +        assert!(
    +            function_info::check_dispatch_type_compatibility(
    +                &dispatcher_deposit_function_info,
    +                deposit_function
    +            ),
    +            error::invalid_argument(
    +                EDEPOSIT_FUNCTION_SIGNATURE_MISMATCH
    +            )
    +        );
    +    });
    +
    +    option::for_each_ref(&derived_balance_function, |balance_function| {
    +        let dispatcher_derived_balance_function_info = function_info::new_function_info_from_address(
    +            @aptos_framework,
    +            string::utf8(b"dispatchable_fungible_asset"),
    +            string::utf8(b"dispatchable_derived_balance"),
    +        );
    +        // Verify that caller type matches callee type so wrongly typed function cannot be registered.
    +        assert!(
    +            function_info::check_dispatch_type_compatibility(
    +                &dispatcher_derived_balance_function_info,
    +                balance_function
    +            ),
    +            error::invalid_argument(
    +                EDERIVED_BALANCE_FUNCTION_SIGNATURE_MISMATCH
    +            )
    +        );
    +    });
    +
    +    // Cannot register hook for APT.
    +    assert!(
    +        object::address_from_constructor_ref(constructor_ref) != @aptos_fungible_asset,
    +        error::permission_denied(EAPT_NOT_DISPATCHABLE)
    +    );
    +    assert!(
    +        !object::can_generate_delete_ref(constructor_ref),
    +        error::invalid_argument(EOBJECT_IS_DELETABLE)
    +    );
    +    assert!(
    +        !exists<DispatchFunctionStore>(
    +            object::address_from_constructor_ref(constructor_ref)
    +        ),
    +        error::already_exists(EALREADY_REGISTERED)
    +    );
    +    assert!(
    +        exists<Metadata>(
    +            object::address_from_constructor_ref(constructor_ref)
    +        ),
    +        error::not_found(EFUNGIBLE_METADATA_EXISTENCE),
    +    );
    +
    +    let store_obj = &object::generate_signer(constructor_ref);
    +
    +    // Store the overload function hook.
    +    move_to<DispatchFunctionStore>(
    +        store_obj,
    +        DispatchFunctionStore {
    +            withdraw_function,
    +            deposit_function,
    +            derived_balance_function,
    +        }
    +    );
    +}
    +
    + + + +
    + + + +## Function `generate_mint_ref` + +Creates a mint ref that can be used to mint fungible assets from the given fungible object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
    public fun generate_mint_ref(constructor_ref: &object::ConstructorRef): fungible_asset::MintRef
    +
    + + + +
    +Implementation + + +
    public fun generate_mint_ref(constructor_ref: &ConstructorRef): MintRef {
    +    let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
    +    MintRef { metadata }
    +}
    +
    + + + +
    + + + +## Function `generate_burn_ref` + +Creates a burn ref that can be used to burn fungible assets from the given fungible object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
    public fun generate_burn_ref(constructor_ref: &object::ConstructorRef): fungible_asset::BurnRef
    +
    + + + +
    +Implementation + + +
    public fun generate_burn_ref(constructor_ref: &ConstructorRef): BurnRef {
    +    let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
         BurnRef { metadata }
     }
     
    @@ -1017,6 +1524,34 @@ This can only be called at object creation time as constructor_ref is only avail +
    + + + +## Function `generate_mutate_metadata_ref` + +Creates a mutate metadata ref that can be used to change the metadata information of fungible assets from the +given fungible object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
    public fun generate_mutate_metadata_ref(constructor_ref: &object::ConstructorRef): fungible_asset::MutateMetadataRef
    +
    + + + +
    +Implementation + + +
    public fun generate_mutate_metadata_ref(constructor_ref: &ConstructorRef): MutateMetadataRef {
    +    let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
    +    MutateMetadataRef { metadata }
    +}
    +
    + + +
    @@ -1171,6 +1706,84 @@ Get the decimals from the metadata object. + + + + +## Function `icon_uri` + +Get the icon uri from the metadata object. + + +
    #[view]
    +public fun icon_uri<T: key>(metadata: object::Object<T>): string::String
    +
    + + + +
    +Implementation + + +
    public fun icon_uri<T: key>(metadata: Object<T>): String acquires Metadata {
    +    borrow_fungible_metadata(&metadata).icon_uri
    +}
    +
    + + + +
    + + + +## Function `project_uri` + +Get the project uri from the metadata object. + + +
    #[view]
    +public fun project_uri<T: key>(metadata: object::Object<T>): string::String
    +
    + + + +
    +Implementation + + +
    public fun project_uri<T: key>(metadata: Object<T>): String acquires Metadata {
    +    borrow_fungible_metadata(&metadata).project_uri
    +}
    +
    + + + +
    + + + +## Function `metadata` + +Get the metadata struct from the metadata object. + + +
    #[view]
    +public fun metadata<T: key>(metadata: object::Object<T>): fungible_asset::Metadata
    +
    + + + +
    +Implementation + + +
    public fun metadata<T: key>(metadata: Object<T>): Metadata acquires Metadata {
    +    *borrow_fungible_metadata(&metadata)
    +}
    +
    + + +
    @@ -1191,12 +1804,63 @@ Return whether the provided address has a store initialized.
    public fun store_exists(store: address): bool {
    +    store_exists_inline(store)
    +}
    +
    + + + + + + + +## Function `store_exists_inline` + +Return whether the provided address has a store initialized. + + +
    fun store_exists_inline(store: address): bool
    +
    + + + +
    +Implementation + + +
    inline fun store_exists_inline(store: address): bool {
         exists<FungibleStore>(store)
     }
     
    +
    + + + +## Function `concurrent_fungible_balance_exists_inline` + +Return whether the provided address has a concurrent fungible balance initialized, +at the fungible store address. + + +
    fun concurrent_fungible_balance_exists_inline(store: address): bool
    +
    + + + +
    +Implementation + + +
    inline fun concurrent_fungible_balance_exists_inline(store: address): bool {
    +    exists<ConcurrentFungibleBalance>(store)
    +}
    +
    + + +
    @@ -1292,9 +1956,16 @@ Get the balance of a given store. Implementation -
    public fun balance<T: key>(store: Object<T>): u64 acquires FungibleStore {
    -    if (store_exists(object::object_address(&store))) {
    -        borrow_store_resource(&store).balance
    +
    public fun balance<T: key>(store: Object<T>): u64 acquires FungibleStore, ConcurrentFungibleBalance {
    +    let store_addr = object::object_address(&store);
    +    if (store_exists_inline(store_addr)) {
    +        let store_balance = borrow_store_resource(&store).balance;
    +        if (store_balance == 0 && concurrent_fungible_balance_exists_inline(store_addr)) {
    +            let balance_resource = borrow_global<ConcurrentFungibleBalance>(store_addr);
    +            aggregator_v2::read(&balance_resource.balance)
    +        } else {
    +            store_balance
    +        }
         } else {
             0
         }
    @@ -1303,6 +1974,68 @@ Get the balance of a given store.
     
     
     
    +
    +
    +
    +
    +## Function `is_balance_at_least`
    +
    +Check whether the balance of a given store is >= amount.
    +
    +
    +
    #[view]
    +public fun is_balance_at_least<T: key>(store: object::Object<T>, amount: u64): bool
    +
    + + + +
    +Implementation + + +
    public fun is_balance_at_least<T: key>(store: Object<T>, amount: u64): bool acquires FungibleStore, ConcurrentFungibleBalance {
    +    let store_addr = object::object_address(&store);
    +    is_address_balance_at_least(store_addr, amount)
    +}
    +
    + + + +
    + + + +## Function `is_address_balance_at_least` + +Check whether the balance of a given store is >= amount. + + +
    public(friend) fun is_address_balance_at_least(store_addr: address, amount: u64): bool
    +
    + + + +
    +Implementation + + +
    public(friend) fun is_address_balance_at_least(store_addr: address, amount: u64): bool acquires FungibleStore, ConcurrentFungibleBalance {
    +    if (store_exists_inline(store_addr)) {
    +        let store_balance = borrow_global<FungibleStore>(store_addr).balance;
    +        if (store_balance == 0 && concurrent_fungible_balance_exists_inline(store_addr)) {
    +            let balance_resource = borrow_global<ConcurrentFungibleBalance>(store_addr);
    +            aggregator_v2::is_at_least(&balance_resource.balance, amount)
    +        } else {
    +            store_balance >= amount
    +        }
    +    } else {
    +        amount == 0
    +    }
    +}
    +
    + + +
    @@ -1325,7 +2058,186 @@ If the store has not been created, we default to returning false so deposits can
    public fun is_frozen<T: key>(store: Object<T>): bool acquires FungibleStore {
    -    store_exists(object::object_address(&store)) && borrow_store_resource(&store).frozen
    +    let store_addr = object::object_address(&store);
    +    store_exists_inline(store_addr) && borrow_global<FungibleStore>(store_addr).frozen
    +}
    +
    + + + + + + + +## Function `is_store_dispatchable` + +Return whether a fungible asset type is dispatchable. + + +
    #[view]
    +public fun is_store_dispatchable<T: key>(store: object::Object<T>): bool
    +
    + + + +
    +Implementation + + +
    public fun is_store_dispatchable<T: key>(store: Object<T>): bool acquires FungibleStore {
    +    let fa_store = borrow_store_resource(&store);
    +    let metadata_addr = object::object_address(&fa_store.metadata);
    +    exists<DispatchFunctionStore>(metadata_addr)
    +}
    +
    + + + +
    + + + +## Function `deposit_dispatch_function` + + + +
    public fun deposit_dispatch_function<T: key>(store: object::Object<T>): option::Option<function_info::FunctionInfo>
    +
    + + + +
    +Implementation + + +
    public fun deposit_dispatch_function<T: key>(store: Object<T>): Option<FunctionInfo> acquires FungibleStore, DispatchFunctionStore {
    +    let fa_store = borrow_store_resource(&store);
    +    let metadata_addr = object::object_address(&fa_store.metadata);
    +    if(exists<DispatchFunctionStore>(metadata_addr)) {
    +        borrow_global<DispatchFunctionStore>(metadata_addr).deposit_function
    +    } else {
    +        option::none()
    +    }
    +}
    +
    + + + +
    + + + +## Function `has_deposit_dispatch_function` + + + +
    fun has_deposit_dispatch_function(metadata: object::Object<fungible_asset::Metadata>): bool
    +
    + + + +
    +Implementation + + +
    fun has_deposit_dispatch_function(metadata: Object<Metadata>): bool acquires DispatchFunctionStore {
    +    let metadata_addr = object::object_address(&metadata);
    +    // Short circuit on APT for better perf
    +    if(metadata_addr != @aptos_fungible_asset && exists<DispatchFunctionStore>(metadata_addr)) {
    +        option::is_some(&borrow_global<DispatchFunctionStore>(metadata_addr).deposit_function)
    +    } else {
    +        false
    +    }
    +}
    +
    + + + +
    + + + +## Function `withdraw_dispatch_function` + + + +
    public fun withdraw_dispatch_function<T: key>(store: object::Object<T>): option::Option<function_info::FunctionInfo>
    +
    + + + +
    +Implementation + + +
    public fun withdraw_dispatch_function<T: key>(store: Object<T>): Option<FunctionInfo> acquires FungibleStore, DispatchFunctionStore {
    +    let fa_store = borrow_store_resource(&store);
    +    let metadata_addr = object::object_address(&fa_store.metadata);
    +    if(exists<DispatchFunctionStore>(metadata_addr)) {
    +        borrow_global<DispatchFunctionStore>(metadata_addr).withdraw_function
    +    } else {
    +        option::none()
    +    }
    +}
    +
    + + + +
    + + + +## Function `has_withdraw_dispatch_function` + + + +
    fun has_withdraw_dispatch_function(metadata: object::Object<fungible_asset::Metadata>): bool
    +
    + + + +
    +Implementation + + +
    fun has_withdraw_dispatch_function(metadata: Object<Metadata>): bool acquires DispatchFunctionStore {
    +    let metadata_addr = object::object_address(&metadata);
    +    // Short circuit on APT for better perf
    +    if (metadata_addr != @aptos_fungible_asset && exists<DispatchFunctionStore>(metadata_addr)) {
    +        option::is_some(&borrow_global<DispatchFunctionStore>(metadata_addr).withdraw_function)
    +    } else {
    +        false
    +    }
    +}
    +
    + + + +
    + + + +## Function `derived_balance_dispatch_function` + + + +
    public(friend) fun derived_balance_dispatch_function<T: key>(store: object::Object<T>): option::Option<function_info::FunctionInfo>
    +
    + + + +
    +Implementation + + +
    public(friend) fun derived_balance_dispatch_function<T: key>(store: Object<T>): Option<FunctionInfo> acquires FungibleStore, DispatchFunctionStore {
    +    let fa_store = borrow_store_resource(&store);
    +    let metadata_addr = object::object_address(&fa_store.metadata);
    +    if (exists<DispatchFunctionStore>(metadata_addr)) {
    +        borrow_global<DispatchFunctionStore>(metadata_addr).derived_balance_function
    +    } else {
    +        option::none()
    +    }
     }
     
    @@ -1373,7 +2285,32 @@ Get the underlying metadata object from the mint_ref_metadata(ref: &MintRef): Object<Metadata> { +
    public fun mint_ref_metadata(ref: &MintRef): Object<Metadata> {
    +    ref.metadata
    +}
    +
    + + + +
    + + + +## Function `transfer_ref_metadata` + +Get the underlying metadata object from the TransferRef. + + +
    public fun transfer_ref_metadata(ref: &fungible_asset::TransferRef): object::Object<fungible_asset::Metadata>
    +
    + + + +
    +Implementation + + +
    public fun transfer_ref_metadata(ref: &TransferRef): Object<Metadata> {
         ref.metadata
     }
     
    @@ -1382,14 +2319,14 @@ Get the underlying metadata object from the + -## Function `transfer_ref_metadata` +## Function `burn_ref_metadata` -Get the underlying metadata object from the TransferRef. +Get the underlying metadata object from the BurnRef. -
    public fun transfer_ref_metadata(ref: &fungible_asset::TransferRef): object::Object<fungible_asset::Metadata>
    +
    public fun burn_ref_metadata(ref: &fungible_asset::BurnRef): object::Object<fungible_asset::Metadata>
     
    @@ -1398,7 +2335,7 @@ Get the underlying metadata object from the transfer_ref_metadata(ref: &TransferRef): Object<Metadata> { +
    public fun burn_ref_metadata(ref: &BurnRef): Object<Metadata> {
         ref.metadata
     }
     
    @@ -1407,14 +2344,14 @@ Get the underlying metadata object from the + -## Function `burn_ref_metadata` +## Function `object_from_metadata_ref` -Get the underlying metadata object from the BurnRef. +Get the underlying metadata object from the MutateMetadataRef. -
    public fun burn_ref_metadata(ref: &fungible_asset::BurnRef): object::Object<fungible_asset::Metadata>
    +
    public fun object_from_metadata_ref(ref: &fungible_asset::MutateMetadataRef): object::Object<fungible_asset::Metadata>
     
    @@ -1423,7 +2360,7 @@ Get the underlying metadata object from the burn_ref_metadata(ref: &BurnRef): Object<Metadata> { +
    public fun object_from_metadata_ref(ref: &MutateMetadataRef): Object<Metadata> {
         ref.metadata
     }
     
    @@ -1454,7 +2391,7 @@ Note: it does not move the underlying object. from: Object<T>, to: Object<T>, amount: u64, -) acquires FungibleStore { +) acquires FungibleStore, DispatchFunctionStore, ConcurrentFungibleBalance { let fa = withdraw(sender, from, amount); deposit(to, fa); } @@ -1491,6 +2428,17 @@ Applications can use this to create multiple stores for isolating fungible asset balance: 0, frozen: false, }); + + if (is_untransferable(metadata)) { + object::set_untransferable(constructor_ref); + }; + + if (default_to_concurrent_fungible_balance()) { + move_to(store_obj, ConcurrentFungibleBalance { + balance: aggregator_v2::create_unbounded_aggregator(), + }); + }; + object::object_from_constructor_ref<FungibleStore>(constructor_ref) }
    @@ -1515,12 +2463,18 @@ Used to delete a store. Requires the store to be completely empty prior to remo Implementation -
    public fun remove_store(delete_ref: &DeleteRef) acquires FungibleStore, FungibleAssetEvents {
    +
    public fun remove_store(delete_ref: &DeleteRef) acquires FungibleStore, FungibleAssetEvents, ConcurrentFungibleBalance {
         let store = &object::object_from_delete_ref<FungibleStore>(delete_ref);
         let addr = object::object_address(store);
         let FungibleStore { metadata: _, balance, frozen: _ }
             = move_from<FungibleStore>(addr);
         assert!(balance == 0, error::permission_denied(EBALANCE_IS_NOT_ZERO));
    +
    +    if (concurrent_fungible_balance_exists_inline(addr)) {
    +        let ConcurrentFungibleBalance { balance } = move_from<ConcurrentFungibleBalance>(addr);
    +        assert!(aggregator_v2::read(&balance) == 0, error::permission_denied(EBALANCE_IS_NOT_ZERO));
    +    };
    +
         // Cleanup deprecated event handles if exist.
         if (exists<FungibleAssetEvents>(addr)) {
             let FungibleAssetEvents {
    @@ -1531,7 +2485,7 @@ Used to delete a store.  Requires the store to be completely empty prior to remo
             event::destroy_handle(deposit_events);
             event::destroy_handle(withdraw_events);
             event::destroy_handle(frozen_events);
    -    }
    +    };
     }
     
    @@ -1559,15 +2513,82 @@ Withdraw amount of the fungible asset from store by th owner: &signer, store: Object<T>, amount: u64, -): FungibleAsset acquires FungibleStore { - assert!(object::owns(store, signer::address_of(owner)), error::permission_denied(ENOT_STORE_OWNER)); - assert!(!is_frozen(store), error::invalid_argument(ESTORE_IS_FROZEN)); +): FungibleAsset acquires FungibleStore, DispatchFunctionStore, ConcurrentFungibleBalance { + withdraw_sanity_check(owner, store, true); withdraw_internal(object::object_address(&store), amount) }
    +
    + + + +## Function `withdraw_sanity_check` + +Check the permission for withdraw operation. + + +
    public(friend) fun withdraw_sanity_check<T: key>(owner: &signer, store: object::Object<T>, abort_on_dispatch: bool)
    +
    + + + +
    +Implementation + + +
    public(friend) fun withdraw_sanity_check<T: key>(
    +    owner: &signer,
    +    store: Object<T>,
    +    abort_on_dispatch: bool,
    +) acquires FungibleStore, DispatchFunctionStore {
    +    assert!(object::owns(store, signer::address_of(owner)), error::permission_denied(ENOT_STORE_OWNER));
    +    let fa_store = borrow_store_resource(&store);
    +    assert!(
    +        !abort_on_dispatch || !has_withdraw_dispatch_function(fa_store.metadata),
    +        error::invalid_argument(EINVALID_DISPATCHABLE_OPERATIONS)
    +    );
    +    assert!(!fa_store.frozen, error::permission_denied(ESTORE_IS_FROZEN));
    +}
    +
    + + + +
    + + + +## Function `deposit_sanity_check` + +Deposit amount of the fungible asset to store. + + +
    public fun deposit_sanity_check<T: key>(store: object::Object<T>, abort_on_dispatch: bool)
    +
    + + + +
    +Implementation + + +
    public fun deposit_sanity_check<T: key>(
    +    store: Object<T>,
    +    abort_on_dispatch: bool
    +) acquires FungibleStore, DispatchFunctionStore {
    +    let fa_store = borrow_store_resource(&store);
    +    assert!(
    +        !abort_on_dispatch || !has_deposit_dispatch_function(fa_store.metadata),
    +        error::invalid_argument(EINVALID_DISPATCHABLE_OPERATIONS)
    +    );
    +    assert!(!fa_store.frozen, error::permission_denied(ESTORE_IS_FROZEN));
    +}
    +
    + + +
    @@ -1586,9 +2607,9 @@ Deposit amount of the fungible asset to store. Implementation -
    public fun deposit<T: key>(store: Object<T>, fa: FungibleAsset) acquires FungibleStore {
    -    assert!(!is_frozen(store), error::invalid_argument(ESTORE_IS_FROZEN));
    -    deposit_internal(store, fa);
    +
    public fun deposit<T: key>(store: Object<T>, fa: FungibleAsset) acquires FungibleStore, DispatchFunctionStore, ConcurrentFungibleBalance {
    +    deposit_sanity_check(store, true);
    +    deposit_internal(object::object_address(&store), fa);
     }
     
    @@ -1613,10 +2634,36 @@ Mint the specified amount of the fungible asset.
    public fun mint(ref: &MintRef, amount: u64): FungibleAsset acquires Supply, ConcurrentSupply {
    -    assert!(amount > 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
         let metadata = ref.metadata;
    -    increase_supply(&metadata, amount);
    +    mint_internal(metadata, amount)
    +}
    +
    + + + + + + + +## Function `mint_internal` + +CAN ONLY BE CALLED BY coin.move for migration. + + +
    public(friend) fun mint_internal(metadata: object::Object<fungible_asset::Metadata>, amount: u64): fungible_asset::FungibleAsset
    +
    + + + +
    +Implementation + +
    public(friend) fun mint_internal(
    +    metadata: Object<Metadata>,
    +    amount: u64
    +): FungibleAsset acquires Supply, ConcurrentSupply {
    +    increase_supply(&metadata, amount);
         FungibleAsset {
             metadata,
             amount
    @@ -1645,8 +2692,9 @@ Mint the specified amount of the fungible asset to a destination st
     
     
     
    public fun mint_to<T: key>(ref: &MintRef, store: Object<T>, amount: u64)
    -acquires FungibleStore, Supply, ConcurrentSupply {
    -    deposit(store, mint(ref, amount));
    +acquires FungibleStore, Supply, ConcurrentSupply, DispatchFunctionStore, ConcurrentFungibleBalance {
    +    deposit_sanity_check(store, false);
    +    deposit_internal(object::object_address(&store), mint(ref, amount));
     }
     
    @@ -1679,6 +2727,33 @@ Enable/disable a store's ability to do direct transfers of the fungible asset. ref.metadata == store_metadata(store), error::invalid_argument(ETRANSFER_REF_AND_STORE_MISMATCH), ); + set_frozen_flag_internal(store, frozen) +} +
    + + + +
    + + + +## Function `set_frozen_flag_internal` + + + +
    public(friend) fun set_frozen_flag_internal<T: key>(store: object::Object<T>, frozen: bool)
    +
    + + + +
    +Implementation + + +
    public(friend) fun set_frozen_flag_internal<T: key>(
    +    store: Object<T>,
    +    frozen: bool
    +) acquires FungibleStore {
         let store_addr = object::object_address(&store);
         borrow_global_mut<FungibleStore>(store_addr).frozen = frozen;
     
    @@ -1707,12 +2782,43 @@ Burns a fungible asset
     
     
     
    public fun burn(ref: &BurnRef, fa: FungibleAsset) acquires Supply, ConcurrentSupply {
    +    assert!(
    +        ref.metadata == metadata_from_asset(&fa),
    +        error::invalid_argument(EBURN_REF_AND_FUNGIBLE_ASSET_MISMATCH)
    +    );
    +    burn_internal(fa);
    +}
    +
    + + + +
    + + + +## Function `burn_internal` + +CAN ONLY BE CALLED BY coin.move for migration. + + +
    public(friend) fun burn_internal(fa: fungible_asset::FungibleAsset): u64
    +
    + + + +
    +Implementation + + +
    public(friend) fun burn_internal(
    +    fa: FungibleAsset
    +): u64 acquires Supply, ConcurrentSupply {
         let FungibleAsset {
             metadata,
    -        amount,
    +        amount
         } = fa;
    -    assert!(ref.metadata == metadata, error::invalid_argument(EBURN_REF_AND_FUNGIBLE_ASSET_MISMATCH));
         decrease_supply(&metadata, amount);
    +    amount
     }
     
    @@ -1740,10 +2846,37 @@ Burn the amount of the fungible asset from the given store. ref: &BurnRef, store: Object<T>, amount: u64 -) acquires FungibleStore, Supply, ConcurrentSupply { - let metadata = ref.metadata; - assert!(metadata == store_metadata(store), error::invalid_argument(EBURN_REF_AND_STORE_MISMATCH)); - let store_addr = object::object_address(&store); +) acquires FungibleStore, Supply, ConcurrentSupply, ConcurrentFungibleBalance { + // ref metadata match is checked in burn() call + burn(ref, withdraw_internal(object::object_address(&store), amount)); +} +
    + + + + + + + +## Function `address_burn_from` + + + +
    public(friend) fun address_burn_from(ref: &fungible_asset::BurnRef, store_addr: address, amount: u64)
    +
    + + + +
    +Implementation + + +
    public(friend) fun address_burn_from(
    +    ref: &BurnRef,
    +    store_addr: address,
    +    amount: u64
    +) acquires FungibleStore, Supply, ConcurrentSupply, ConcurrentFungibleBalance {
    +    // ref metadata match is checked in burn() call
         burn(ref, withdraw_internal(store_addr, amount));
     }
     
    @@ -1772,7 +2905,7 @@ Withdraw amount of the fungible asset from the store i ref: &TransferRef, store: Object<T>, amount: u64 -): FungibleAsset acquires FungibleStore { +): FungibleAsset acquires FungibleStore, ConcurrentFungibleBalance { assert!( ref.metadata == store_metadata(store), error::invalid_argument(ETRANSFER_REF_AND_STORE_MISMATCH), @@ -1805,12 +2938,12 @@ Deposit the fungible asset into the store ignoring frozenTransferRef, store: Object<T>, fa: FungibleAsset -) acquires FungibleStore { +) acquires FungibleStore, ConcurrentFungibleBalance { assert!( ref.metadata == fa.metadata, error::invalid_argument(ETRANSFER_REF_AND_FUNGIBLE_ASSET_MISMATCH) ); - deposit_internal(store, fa); + deposit_internal(object::object_address(&store), fa); }
    @@ -1839,7 +2972,7 @@ Transfer amount of the fungible asset with FungibleStore { +) acquires FungibleStore, ConcurrentFungibleBalance { let fa = withdraw_with_ref(transfer_ref, from, amount); deposit_with_ref(transfer_ref, to, fa); } @@ -1847,6 +2980,55 @@ Transfer amount of the fungible asset with + +## Function `mutate_metadata` + +Mutate specified fields of the fungible asset's Metadata. + + +
    public fun mutate_metadata(metadata_ref: &fungible_asset::MutateMetadataRef, name: option::Option<string::String>, symbol: option::Option<string::String>, decimals: option::Option<u8>, icon_uri: option::Option<string::String>, project_uri: option::Option<string::String>)
    +
    + + + +
    +Implementation + + +
    public fun mutate_metadata(
    +    metadata_ref: &MutateMetadataRef,
    +    name: Option<String>,
    +    symbol: Option<String>,
    +    decimals: Option<u8>,
    +    icon_uri: Option<String>,
    +    project_uri: Option<String>,
    +) acquires Metadata {
    +    let metadata_address = object::object_address(&metadata_ref.metadata);
    +    let mutable_metadata = borrow_global_mut<Metadata>(metadata_address);
    +
    +    if (option::is_some(&name)){
    +        mutable_metadata.name = option::extract(&mut name);
    +    };
    +    if (option::is_some(&symbol)){
    +        mutable_metadata.symbol = option::extract(&mut symbol);
    +    };
    +    if (option::is_some(&decimals)){
    +        mutable_metadata.decimals = option::extract(&mut decimals);
    +    };
    +    if (option::is_some(&icon_uri)){
    +        mutable_metadata.icon_uri = option::extract(&mut icon_uri);
    +    };
    +    if (option::is_some(&project_uri)){
    +        mutable_metadata.project_uri = option::extract(&mut project_uri);
    +    };
    +}
    +
    + + +
    @@ -1968,7 +3150,7 @@ Destroy an empty fungible asset. -
    fun deposit_internal<T: key>(store: object::Object<T>, fa: fungible_asset::FungibleAsset)
    +
    public(friend) fun deposit_internal(store_addr: address, fa: fungible_asset::FungibleAsset)
     
    @@ -1977,17 +3159,22 @@ Destroy an empty fungible asset. Implementation -
    fun deposit_internal<T: key>(store: Object<T>, fa: FungibleAsset) acquires FungibleStore {
    +
    public(friend) fun deposit_internal(store_addr: address, fa: FungibleAsset) acquires FungibleStore, ConcurrentFungibleBalance {
         let FungibleAsset { metadata, amount } = fa;
         if (amount == 0) return;
     
    -    let store_metadata = store_metadata(store);
    -    assert!(metadata == store_metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_STORE_MISMATCH));
    -    let store_addr = object::object_address(&store);
    +    assert!(exists<FungibleStore>(store_addr), error::not_found(EFUNGIBLE_STORE_EXISTENCE));
         let store = borrow_global_mut<FungibleStore>(store_addr);
    -    store.balance = store.balance + amount;
    +    assert!(metadata == store.metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_STORE_MISMATCH));
    +
    +    if (store.balance == 0 && concurrent_fungible_balance_exists_inline(store_addr)) {
    +        let balance_resource = borrow_global_mut<ConcurrentFungibleBalance>(store_addr);
    +        aggregator_v2::add(&mut balance_resource.balance, amount);
    +    } else {
    +        store.balance = store.balance + amount;
    +    };
     
    -    event::emit<Deposit>(Deposit { store: store_addr, amount });
    +    event::emit(Deposit { store: store_addr, amount });
     }
     
    @@ -2002,7 +3189,7 @@ Destroy an empty fungible asset. Extract amount of the fungible asset from store. -
    fun withdraw_internal(store_addr: address, amount: u64): fungible_asset::FungibleAsset
    +
    public(friend) fun withdraw_internal(store_addr: address, amount: u64): fungible_asset::FungibleAsset
     
    @@ -2011,18 +3198,28 @@ Extract amount of the fungible asset from store. Implementation -
    fun withdraw_internal(
    +
    public(friend) fun withdraw_internal(
         store_addr: address,
         amount: u64,
    -): FungibleAsset acquires FungibleStore {
    -    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
    -    let store = borrow_global_mut<FungibleStore>(store_addr);
    -    assert!(store.balance >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));
    -    store.balance = store.balance - amount;
    +): FungibleAsset acquires FungibleStore, ConcurrentFungibleBalance {
    +    assert!(exists<FungibleStore>(store_addr), error::not_found(EFUNGIBLE_STORE_EXISTENCE));
     
    +    let store = borrow_global_mut<FungibleStore>(store_addr);
         let metadata = store.metadata;
    -    event::emit<Withdraw>(Withdraw { store: store_addr, amount });
    +    if (amount != 0) {
    +        if (store.balance == 0 && concurrent_fungible_balance_exists_inline(store_addr)) {
    +            let balance_resource = borrow_global_mut<ConcurrentFungibleBalance>(store_addr);
    +            assert!(
    +                aggregator_v2::try_sub(&mut balance_resource.balance, amount),
    +                error::invalid_argument(EINSUFFICIENT_BALANCE)
    +            );
    +        } else {
    +            assert!(store.balance >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));
    +            store.balance = store.balance - amount;
    +        };
     
    +        event::emit<Withdraw>(Withdraw { store: store_addr, amount });
    +    };
         FungibleAsset { metadata, amount }
     }
     
    @@ -2048,7 +3245,9 @@ Increase the supply of a fungible asset by minting.
    fun increase_supply<T: key>(metadata: &Object<T>, amount: u64) acquires Supply, ConcurrentSupply {
    -    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
    +    if (amount == 0) {
    +        return
    +    };
         let metadata_address = object::object_address(metadata);
     
         if (exists<ConcurrentSupply>(metadata_address)) {
    @@ -2068,7 +3267,7 @@ Increase the supply of a fungible asset by minting.
             };
             supply.current = supply.current + (amount as u128);
         } else {
    -        assert!(false, error::not_found(ESUPPLY_NOT_FOUND));
    +        abort error::not_found(ESUPPLY_NOT_FOUND)
         }
     }
     
    @@ -2094,7 +3293,9 @@ Decrease the supply of a fungible asset by burning.
    fun decrease_supply<T: key>(metadata: &Object<T>, amount: u64) acquires Supply, ConcurrentSupply {
    -    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
    +    if (amount == 0) {
    +        return
    +    };
         let metadata_address = object::object_address(metadata);
     
         if (exists<ConcurrentSupply>(metadata_address)) {
    @@ -2192,7 +3393,9 @@ Decrease the supply of a fungible asset by burning.
     
     
     
    inline fun borrow_store_resource<T: key>(store: &Object<T>): &FungibleStore acquires FungibleStore {
    -    borrow_global<FungibleStore>(object::object_address(store))
    +    let store_addr = object::object_address(store);
    +    assert!(exists<FungibleStore>(store_addr), error::not_found(EFUNGIBLE_STORE_EXISTENCE));
    +    borrow_global<FungibleStore>(store_addr)
     }
     
    @@ -2220,7 +3423,10 @@ Decrease the supply of a fungible asset by burning. ) acquires Supply { let metadata_object_address = object::address_from_extend_ref(ref); let metadata_object_signer = object::generate_signer_for_extending(ref); - assert!(features::concurrent_fungible_assets_enabled(), error::invalid_argument(ECONCURRENT_SUPPLY_NOT_ENABLED)); + assert!( + features::concurrent_fungible_assets_enabled(), + error::invalid_argument(ECONCURRENT_SUPPLY_NOT_ENABLED) + ); assert!(exists<Supply>(metadata_object_address), error::not_found(ESUPPLY_NOT_FOUND)); let Supply { current, @@ -2230,20 +3436,82 @@ Decrease the supply of a fungible asset by burning. let unlimited = option::is_none(&maximum); let supply = ConcurrentSupply { current: if (unlimited) { - aggregator_v2::create_unbounded_aggregator() + aggregator_v2::create_unbounded_aggregator_with_value(current) } else { - aggregator_v2::create_aggregator(option::extract(&mut maximum)) + aggregator_v2::create_aggregator_with_value(current, option::extract(&mut maximum)) }, }; - // update current state: - aggregator_v2::add(&mut supply.current, current); move_to(&metadata_object_signer, supply); }
    + + + + +## Function `upgrade_store_to_concurrent` + + + +
    public entry fun upgrade_store_to_concurrent<T: key>(owner: &signer, store: object::Object<T>)
    +
    + + + +
    +Implementation + + +
    public entry fun upgrade_store_to_concurrent<T: key>(
    +    owner: &signer,
    +    store: Object<T>,
    +) acquires FungibleStore {
    +    assert!(object::owns(store, signer::address_of(owner)), error::permission_denied(ENOT_STORE_OWNER));
    +    assert!(!is_frozen(store), error::invalid_argument(ESTORE_IS_FROZEN));
    +    assert!(allow_upgrade_to_concurrent_fungible_balance(), error::invalid_argument(ECONCURRENT_BALANCE_NOT_ENABLED));
    +    ensure_store_upgraded_to_concurrent_internal(object::object_address(&store));
    +}
    +
    + + + +
    + + + +## Function `ensure_store_upgraded_to_concurrent_internal` + +Ensure a known FungibleStore has ConcurrentFungibleBalance. + + +
    fun ensure_store_upgraded_to_concurrent_internal(fungible_store_address: address)
    +
    + + + +
    +Implementation + + +
    fun ensure_store_upgraded_to_concurrent_internal(
    +    fungible_store_address: address,
    +) acquires FungibleStore {
    +    if (exists<ConcurrentFungibleBalance>(fungible_store_address)) {
    +        return
    +    };
    +    let store = borrow_global_mut<FungibleStore>(fungible_store_address);
    +    let balance = aggregator_v2::create_unbounded_aggregator_with_value(store.balance);
    +    store.balance = 0;
    +    let object_signer = create_signer::create_signer(fungible_store_address);
    +    move_to(&object_signer, ConcurrentFungibleBalance { balance });
    +}
    +
    + + +
    @@ -2336,7 +3604,7 @@ Decrease the supply of a fungible asset by burning. 10 -A store should only be deleted if it's balance is zero. +A store should only be deleted if its balance is zero. Medium The remove_store function validates the store's balance and removes the store under the object address. Audited that aborts if the balance of the store is not zero. Audited that store is removed from the object address. diff --git a/aptos-move/framework/aptos-framework/doc/gas_schedule.md b/aptos-move/framework/aptos-framework/doc/gas_schedule.md index f7ab0fc7dd063..ea4a92886698f 100644 --- a/aptos-move/framework/aptos-framework/doc/gas_schedule.md +++ b/aptos-move/framework/aptos-framework/doc/gas_schedule.md @@ -14,6 +14,7 @@ it costs to execute Move on the network. - [Function `initialize`](#0x1_gas_schedule_initialize) - [Function `set_gas_schedule`](#0x1_gas_schedule_set_gas_schedule) - [Function `set_for_next_epoch`](#0x1_gas_schedule_set_for_next_epoch) +- [Function `set_for_next_epoch_check_hash`](#0x1_gas_schedule_set_for_next_epoch_check_hash) - [Function `on_new_epoch`](#0x1_gas_schedule_on_new_epoch) - [Function `set_storage_gas_config`](#0x1_gas_schedule_set_storage_gas_config) - [Function `set_storage_gas_config_for_next_epoch`](#0x1_gas_schedule_set_storage_gas_config_for_next_epoch) @@ -23,12 +24,15 @@ it costs to execute Move on the network. - [Function `initialize`](#@Specification_1_initialize) - [Function `set_gas_schedule`](#@Specification_1_set_gas_schedule) - [Function `set_for_next_epoch`](#@Specification_1_set_for_next_epoch) + - [Function `set_for_next_epoch_check_hash`](#@Specification_1_set_for_next_epoch_check_hash) - [Function `on_new_epoch`](#@Specification_1_on_new_epoch) - [Function `set_storage_gas_config`](#@Specification_1_set_storage_gas_config) - [Function `set_storage_gas_config_for_next_epoch`](#@Specification_1_set_storage_gas_config_for_next_epoch) -
    use 0x1::chain_status;
    +
    use 0x1::aptos_hash;
    +use 0x1::bcs;
    +use 0x1::chain_status;
     use 0x1::config_buffer;
     use 0x1::error;
     use 0x1::reconfiguration;
    @@ -158,6 +162,15 @@ The provided gas schedule bytes are empty or invalid
     
     
     
    +
    +
    +
    +
    +
    const EINVALID_GAS_SCHEDULE_HASH: u64 = 3;
    +
    + + + ## Function `initialize` @@ -244,6 +257,8 @@ TODO: update all the tests that reference this function, then disable this funct ## Function `set_for_next_epoch` Set the gas schedule for the next epoch, typically called by on-chain governance. +Abort if the version of the given schedule is lower than the current version. + Example usage: ``` aptos_framework::gas_schedule::set_for_next_epoch(&framework_signer, some_gas_schedule_blob); @@ -260,10 +275,66 @@ aptos_framework::aptos_governance::reconfigure(&framework_signer); Implementation -
    public fun set_for_next_epoch(aptos_framework: &signer, gas_schedule_blob: vector<u8>) {
    +
    public fun set_for_next_epoch(aptos_framework: &signer, gas_schedule_blob: vector<u8>) acquires GasScheduleV2 {
         system_addresses::assert_aptos_framework(aptos_framework);
         assert!(!vector::is_empty(&gas_schedule_blob), error::invalid_argument(EINVALID_GAS_SCHEDULE));
         let new_gas_schedule: GasScheduleV2 = from_bytes(gas_schedule_blob);
    +    if (exists<GasScheduleV2>(@aptos_framework)) {
    +        let cur_gas_schedule = borrow_global<GasScheduleV2>(@aptos_framework);
    +        assert!(
    +            new_gas_schedule.feature_version >= cur_gas_schedule.feature_version,
    +            error::invalid_argument(EINVALID_GAS_FEATURE_VERSION)
    +        );
    +    };
    +    config_buffer::upsert(new_gas_schedule);
    +}
    +
    + + + + + + + +## Function `set_for_next_epoch_check_hash` + +Set the gas schedule for the next epoch, typically called by on-chain governance. +Abort if the version of the given schedule is lower than the current version. +Require a hash of the old gas schedule to be provided and will abort if the hashes mismatch. + + +
    public fun set_for_next_epoch_check_hash(aptos_framework: &signer, old_gas_schedule_hash: vector<u8>, new_gas_schedule_blob: vector<u8>)
    +
    + + + +
    +Implementation + + +
    public fun set_for_next_epoch_check_hash(
    +    aptos_framework: &signer,
    +    old_gas_schedule_hash: vector<u8>,
    +    new_gas_schedule_blob: vector<u8>
    +) acquires GasScheduleV2 {
    +    system_addresses::assert_aptos_framework(aptos_framework);
    +    assert!(!vector::is_empty(&new_gas_schedule_blob), error::invalid_argument(EINVALID_GAS_SCHEDULE));
    +
    +    let new_gas_schedule: GasScheduleV2 = from_bytes(new_gas_schedule_blob);
    +    if (exists<GasScheduleV2>(@aptos_framework)) {
    +        let cur_gas_schedule = borrow_global<GasScheduleV2>(@aptos_framework);
    +        assert!(
    +            new_gas_schedule.feature_version >= cur_gas_schedule.feature_version,
    +            error::invalid_argument(EINVALID_GAS_FEATURE_VERSION)
    +        );
    +        let cur_gas_schedule_bytes = bcs::to_bytes(cur_gas_schedule);
    +        let cur_gas_schedule_hash = aptos_hash::sha3_512(cur_gas_schedule_bytes);
    +        assert!(
    +            cur_gas_schedule_hash == old_gas_schedule_hash,
    +            error::invalid_argument(EINVALID_GAS_SCHEDULE_HASH)
    +        );
    +    };
    +
         config_buffer::upsert(new_gas_schedule);
     }
     
    @@ -279,7 +350,7 @@ aptos_framework::aptos_governance::reconfigure(&framework_signer); Only used in reconfigurations to apply the pending GasScheduleV2, if there is any. -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -288,11 +359,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires GasScheduleV2 { +
    public(friend) fun on_new_epoch(framework: &signer) acquires GasScheduleV2 {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<GasScheduleV2>()) {
    -        let new_gas_schedule: GasScheduleV2 = config_buffer::extract<GasScheduleV2>();
    -        let gas_schedule = borrow_global_mut<GasScheduleV2>(@aptos_framework);
    -        *gas_schedule = new_gas_schedule;
    +        let new_gas_schedule = config_buffer::extract<GasScheduleV2>();
    +        if (exists<GasScheduleV2>(@aptos_framework)) {
    +            *borrow_global_mut<GasScheduleV2>(@aptos_framework) = new_gas_schedule;
    +        } else {
    +            move_to(framework, new_gas_schedule);
    +        }
         }
     }
     
    @@ -452,6 +527,7 @@ Only used in reconfigurations to apply the pending stake::ValidatorFees>(@aptos_framework); requires exists<CoinInfo<AptosCoin>>(@aptos_framework); +requires chain_status::is_genesis(); include transaction_fee::RequiresCollectedFeesPerValueLeqBlockAptosSupply; include staking_config::StakingRewardsConfigRequirement; // This enforces high-level requirement 2: @@ -479,10 +555,38 @@ Only used in reconfigurations to apply the pending config_buffer::SetForNextEpochAbortsIf { +
    include system_addresses::AbortsIfNotAptosFramework{ account: aptos_framework };
    +include config_buffer::SetForNextEpochAbortsIf {
         account: aptos_framework,
         config: gas_schedule_blob
     };
    +let new_gas_schedule = util::spec_from_bytes<GasScheduleV2>(gas_schedule_blob);
    +let cur_gas_schedule = global<GasScheduleV2>(@aptos_framework);
    +aborts_if exists<GasScheduleV2>(@aptos_framework) && new_gas_schedule.feature_version < cur_gas_schedule.feature_version;
    +
    + + + + + +### Function `set_for_next_epoch_check_hash` + + +
    public fun set_for_next_epoch_check_hash(aptos_framework: &signer, old_gas_schedule_hash: vector<u8>, new_gas_schedule_blob: vector<u8>)
    +
    + + + + +
    include system_addresses::AbortsIfNotAptosFramework{ account: aptos_framework };
    +include config_buffer::SetForNextEpochAbortsIf {
    +    account: aptos_framework,
    +    config: new_gas_schedule_blob
    +};
    +let new_gas_schedule = util::spec_from_bytes<GasScheduleV2>(new_gas_schedule_blob);
    +let cur_gas_schedule = global<GasScheduleV2>(@aptos_framework);
    +aborts_if exists<GasScheduleV2>(@aptos_framework) && new_gas_schedule.feature_version < cur_gas_schedule.feature_version;
    +aborts_if exists<GasScheduleV2>(@aptos_framework) && (!features::spec_sha_512_and_ripemd_160_enabled() || aptos_hash::spec_sha3_512_internal(bcs::serialize(cur_gas_schedule)) != old_gas_schedule_hash);
     
    @@ -492,13 +596,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() +
    public(friend) fun on_new_epoch(framework: &signer)
     
    -
    include config_buffer::OnNewEpochAbortsIf<GasScheduleV2>;
    +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<GasScheduleV2>;
    +aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/genesis.md b/aptos-move/framework/aptos-framework/doc/genesis.md index e8311ec9ab142..5fcac753d7489 100644 --- a/aptos-move/framework/aptos-framework/doc/genesis.md +++ b/aptos-move/framework/aptos-framework/doc/genesis.md @@ -36,6 +36,7 @@
    use 0x1::account;
     use 0x1::aggregator_factory;
    +use 0x1::aptos_account;
     use 0x1::aptos_coin;
     use 0x1::aptos_governance;
     use 0x1::block;
    @@ -389,6 +390,10 @@ Genesis step 2: Initialize Aptos coin.
     
     
    fun initialize_aptos_coin(aptos_framework: &signer) {
         let (burn_cap, mint_cap) = aptos_coin::initialize(aptos_framework);
    +
    +    coin::create_coin_conversion_map(aptos_framework);
    +    coin::create_pairing<AptosCoin>(aptos_framework);
    +
         // Give stake module MintCapability<AptosCoin> so it can mint rewards.
         stake::store_aptos_coin_mint_cap(aptos_framework, mint_cap);
         // Give transaction_fee module BurnCapability<AptosCoin> so it can burn gas.
    @@ -423,6 +428,10 @@ Only called for testnets and e2e tests.
         core_resources_auth_key: vector<u8>,
     ) {
         let (burn_cap, mint_cap) = aptos_coin::initialize(aptos_framework);
    +
    +    coin::create_coin_conversion_map(aptos_framework);
    +    coin::create_pairing<AptosCoin>(aptos_framework);
    +
         // Give stake module MintCapability<AptosCoin> so it can mint rewards.
         stake::store_aptos_coin_mint_cap(aptos_framework, mint_cap);
         // Give transaction_fee module BurnCapability<AptosCoin> so it can burn gas.
    @@ -432,6 +441,7 @@ Only called for testnets and e2e tests.
     
         let core_resources = account::create_account(@core_resources);
         account::rotate_authentication_key_internal(&core_resources, core_resources_auth_key);
    +    aptos_account::register_apt(&core_resources); // registers APT store
         aptos_coin::configure_accounts_for_test(aptos_framework, &core_resources, mint_cap);
     }
     
    @@ -920,7 +930,7 @@ The last step of genesis. 2 Addresses ranging from 0x0 - 0xa should be reserved for the framework and part of aptos governance. Critical -The function genesis::initialize calls account::create_framework_reserved_account for addresses 0x0, 0x2, 0x3, 0x4, ..., 0xa which creates an account and authentication_key for them. This should be formally the reserved addresses, and at the end of the function, an Account resource exists. +The function genesis::initialize calls account::create_framework_reserved_account for addresses 0x0, 0x2, 0x3, 0x4, ..., 0xa which creates an account and authentication_key for them. This should be formally verified by ensuring that at the beginning of the genesis::initialize function no Account resource exists for the reserved addresses, and at the end of the function, an Account resource exists. Formally verified via initialize. @@ -928,7 +938,7 @@ The last step of genesis. 3 The Aptos coin should be initialized during genesis and only the Aptos framework account should own the mint and burn capabilities for the APT token. Critical -Both mint and burn capabilities are wrapped inside the stake::AptosCoinCapabilities and +Both mint and burn capabilities are wrapped inside the stake::AptosCoinCapabilities and transaction_fee::AptosCoinCapabilities resources which are stored under the aptos framework account. Formally verified via initialize_aptos_coin. diff --git a/aptos-move/framework/aptos-framework/doc/governance_proposal.md b/aptos-move/framework/aptos-framework/doc/governance_proposal.md index b3016f00f6935..e67b67aad759d 100644 --- a/aptos-move/framework/aptos-framework/doc/governance_proposal.md +++ b/aptos-move/framework/aptos-framework/doc/governance_proposal.md @@ -137,7 +137,7 @@ Useful for AptosGovernance to create an empty proposal as proof. 2 The governance proposal module should only be accessible to the aptos governance. Medium -Both create_proposal and create_empty_proposal functions are only available to the friend module +Both create_proposal and create_empty_proposal functions are only available to the friend module aptos_framework::aptos_governance. Enforced via friend module relationship. diff --git a/aptos-move/framework/aptos-framework/doc/jwk_consensus_config.md b/aptos-move/framework/aptos-framework/doc/jwk_consensus_config.md index bda3f43cdf523..b2f0f012886d9 100644 --- a/aptos-move/framework/aptos-framework/doc/jwk_consensus_config.md +++ b/aptos-move/framework/aptos-framework/doc/jwk_consensus_config.md @@ -17,6 +17,8 @@ Structs and functions related to JWK consensus configurations. - [Function `new_off`](#0x1_jwk_consensus_config_new_off) - [Function `new_v1`](#0x1_jwk_consensus_config_new_v1) - [Function `new_oidc_provider`](#0x1_jwk_consensus_config_new_oidc_provider) +- [Specification](#@Specification_1) + - [Function `on_new_epoch`](#@Specification_1_on_new_epoch)
    use 0x1::config_buffer;
    @@ -235,7 +237,7 @@ aptos_governance::reconfigure(&framework_signer);
     Only used in reconfigurations to apply the pending JWKConsensusConfig, if there is any.
     
     
    -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -244,10 +246,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires JWKConsensusConfig { +
    public(friend) fun on_new_epoch(framework: &signer) acquires JWKConsensusConfig {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<JWKConsensusConfig>()) {
             let new_config = config_buffer::extract<JWKConsensusConfig>();
    -        borrow_global_mut<JWKConsensusConfig>(@aptos_framework).variant = new_config.variant;
    +        if (exists<JWKConsensusConfig>(@aptos_framework)) {
    +            *borrow_global_mut<JWKConsensusConfig>(@aptos_framework) = new_config;
    +        } else {
    +            move_to(framework, new_config);
    +        };
         }
     }
     
    @@ -345,5 +352,26 @@ Construct an + +## Specification + + + + +### Function `on_new_epoch` + + +
    public(friend) fun on_new_epoch(framework: &signer)
    +
    + + + + +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<JWKConsensusConfig>;
    +aborts_if false;
    +
    + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/jwks.md b/aptos-move/framework/aptos-framework/doc/jwks.md index 211afabce8bc5..e0c09f7fa7214 100644 --- a/aptos-move/framework/aptos-framework/doc/jwks.md +++ b/aptos-move/framework/aptos-framework/doc/jwks.md @@ -55,6 +55,8 @@ have a simple layout which is easily accessible in Rust. - [Function `upsert_jwk`](#0x1_jwks_upsert_jwk) - [Function `remove_jwk`](#0x1_jwks_remove_jwk) - [Function `apply_patch`](#0x1_jwks_apply_patch) +- [Specification](#@Specification_1) + - [Function `on_new_epoch`](#@Specification_1_on_new_epoch)
    use 0x1::chain_status;
    @@ -920,7 +922,7 @@ aptos_framework::aptos_governance::reconfigure(&framework_signer);
     Only used in reconfigurations to apply the pending SupportedOIDCProviders, if there is any.
     
     
    -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -929,9 +931,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires SupportedOIDCProviders { +
    public(friend) fun on_new_epoch(framework: &signer) acquires SupportedOIDCProviders {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<SupportedOIDCProviders>()) {
    -        *borrow_global_mut<SupportedOIDCProviders>(@aptos_framework) = config_buffer::extract();
    +        let new_config = config_buffer::extract<SupportedOIDCProviders>();
    +        if (exists<SupportedOIDCProviders>(@aptos_framework)) {
    +            *borrow_global_mut<SupportedOIDCProviders>(@aptos_framework) = new_config;
    +        } else {
    +            move_to(framework, new_config);
    +        }
         }
     }
     
    @@ -1642,5 +1650,26 @@ Maintains the sorted-by-issuer invariant in + +## Specification + + + + +### Function `on_new_epoch` + + +
    public(friend) fun on_new_epoch(framework: &signer)
    +
    + + + + +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<SupportedOIDCProviders>;
    +aborts_if false;
    +
    + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/keyless_account.md b/aptos-move/framework/aptos-framework/doc/keyless_account.md index 781b5895219b0..6a5ec753b84b3 100644 --- a/aptos-move/framework/aptos-framework/doc/keyless_account.md +++ b/aptos-move/framework/aptos-framework/doc/keyless_account.md @@ -13,15 +13,29 @@ This module is responsible for configuring keyless blockchain accounts which wer - [Constants](#@Constants_0) - [Function `new_groth16_verification_key`](#0x1_keyless_account_new_groth16_verification_key) - [Function `new_configuration`](#0x1_keyless_account_new_configuration) +- [Function `validate_groth16_vk`](#0x1_keyless_account_validate_groth16_vk) - [Function `update_groth16_verification_key`](#0x1_keyless_account_update_groth16_verification_key) - [Function `update_configuration`](#0x1_keyless_account_update_configuration) - [Function `update_training_wheels`](#0x1_keyless_account_update_training_wheels) - [Function `update_max_exp_horizon`](#0x1_keyless_account_update_max_exp_horizon) - [Function `remove_all_override_auds`](#0x1_keyless_account_remove_all_override_auds) - [Function `add_override_aud`](#0x1_keyless_account_add_override_aud) - - -
    use 0x1::option;
    +-  [Function `set_groth16_verification_key_for_next_epoch`](#0x1_keyless_account_set_groth16_verification_key_for_next_epoch)
    +-  [Function `set_configuration_for_next_epoch`](#0x1_keyless_account_set_configuration_for_next_epoch)
    +-  [Function `update_training_wheels_for_next_epoch`](#0x1_keyless_account_update_training_wheels_for_next_epoch)
    +-  [Function `update_max_exp_horizon_for_next_epoch`](#0x1_keyless_account_update_max_exp_horizon_for_next_epoch)
    +-  [Function `remove_all_override_auds_for_next_epoch`](#0x1_keyless_account_remove_all_override_auds_for_next_epoch)
    +-  [Function `add_override_aud_for_next_epoch`](#0x1_keyless_account_add_override_aud_for_next_epoch)
    +-  [Function `on_new_epoch`](#0x1_keyless_account_on_new_epoch)
    +-  [Specification](#@Specification_1)
    +
    +
    +
    use 0x1::bn254_algebra;
    +use 0x1::chain_status;
    +use 0x1::config_buffer;
    +use 0x1::crypto_algebra;
    +use 0x1::ed25519;
    +use 0x1::option;
     use 0x1::signer;
     use 0x1::string;
     use 0x1::system_addresses;
    @@ -65,7 +79,7 @@ The 288-byte Groth16 verification key (VK) for the ZK relation that implements k
     
     
     
    #[resource_group_member(#[group = 0x1::keyless_account::Group])]
    -struct Groth16VerificationKey has store, key
    +struct Groth16VerificationKey has drop, store, key
     
    @@ -118,7 +132,7 @@ The 288-byte Groth16 verification key (VK) for the ZK relation that implements k
    #[resource_group_member(#[group = 0x1::keyless_account::Group])]
    -struct Configuration has store, key
    +struct Configuration has copy, drop, store, key
     
    @@ -189,6 +203,26 @@ The 288-byte Groth16 verification key (VK) for the ZK relation that implements k ## Constants + + +A serialized BN254 G1 point is invalid. + + +
    const E_INVALID_BN254_G1_SERIALIZATION: u64 = 2;
    +
    + + + + + +A serialized BN254 G2 point is invalid. + + +
    const E_INVALID_BN254_G2_SERIALIZATION: u64 = 3;
    +
    + + + The training wheels PK needs to be 32 bytes long. @@ -274,12 +308,48 @@ The training wheels PK needs to be 32 bytes long. +
    + + + +## Function `validate_groth16_vk` + +Pre-validate the VK to actively-prevent incorrect VKs from being set on-chain. + + +
    fun validate_groth16_vk(vk: &keyless_account::Groth16VerificationKey)
    +
    + + + +
    +Implementation + + +
    fun validate_groth16_vk(vk: &Groth16VerificationKey) {
    +    // Could be leveraged to speed up the VM deserialization of the VK by 2x, since it can assume the points are valid.
    +    assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G1, bn254_algebra::FormatG1Compr>(&vk.alpha_g1)), E_INVALID_BN254_G1_SERIALIZATION);
    +    assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.beta_g2)), E_INVALID_BN254_G2_SERIALIZATION);
    +    assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.gamma_g2)), E_INVALID_BN254_G2_SERIALIZATION);
    +    assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G2, bn254_algebra::FormatG2Compr>(&vk.delta_g2)), E_INVALID_BN254_G2_SERIALIZATION);
    +    for (i in 0..vector::length(&vk.gamma_abc_g1)) {
    +        assert!(option::is_some(&crypto_algebra::deserialize<bn254_algebra::G1, bn254_algebra::FormatG1Compr>(vector::borrow(&vk.gamma_abc_g1, i))), E_INVALID_BN254_G1_SERIALIZATION);
    +    };
    +}
    +
    + + +
    ## Function `update_groth16_verification_key` +Sets the Groth16 verification key, only callable during genesis. To call during governance proposals, use +set_groth16_verification_key_for_next_epoch. + +WARNING: See set_groth16_verification_key_for_next_epoch for caveats.
    public fun update_groth16_verification_key(fx: &signer, vk: keyless_account::Groth16VerificationKey)
    @@ -291,19 +361,10 @@ The training wheels PK needs to be 32 bytes long.
     Implementation
     
     
    -
    public fun update_groth16_verification_key(fx: &signer, vk: Groth16VerificationKey) acquires Groth16VerificationKey {
    +
    public fun update_groth16_verification_key(fx: &signer, vk: Groth16VerificationKey) {
         system_addresses::assert_aptos_framework(fx);
    -
    -    if (exists<Groth16VerificationKey>(signer::address_of(fx))) {
    -        let Groth16VerificationKey {
    -            alpha_g1: _,
    -            beta_g2: _,
    -            gamma_g2: _,
    -            delta_g2: _,
    -            gamma_abc_g1: _
    -        } = move_from<Groth16VerificationKey>(signer::address_of(fx));
    -    };
    -
    +    chain_status::assert_genesis();
    +    // There should not be a previous resource set here.
         move_to(fx, vk);
     }
     
    @@ -316,6 +377,10 @@ The training wheels PK needs to be 32 bytes long. ## Function `update_configuration` +Sets the keyless configuration, only callable during genesis. To call during governance proposals, use +set_configuration_for_next_epoch. + +WARNING: See set_configuration_for_next_epoch for caveats.
    public fun update_configuration(fx: &signer, config: keyless_account::Configuration)
    @@ -327,22 +392,10 @@ The training wheels PK needs to be 32 bytes long.
     Implementation
     
     
    -
    public fun update_configuration(fx: &signer, config: Configuration) acquires Configuration {
    +
    public fun update_configuration(fx: &signer, config: Configuration) {
         system_addresses::assert_aptos_framework(fx);
    -
    -    if (exists<Configuration>(signer::address_of(fx))) {
    -        let Configuration {
    -            override_aud_vals: _,
    -            max_signatures_per_txn: _,
    -            max_exp_horizon_secs: _,
    -            training_wheels_pubkey: _,
    -            max_commited_epk_bytes: _,
    -            max_iss_val_bytes: _,
    -            max_extra_field_bytes: _,
    -            max_jwt_header_b64_bytes: _,
    -        } = move_from<Configuration>(signer::address_of(fx));
    -    };
    -
    +    chain_status::assert_genesis();
    +    // There should not be a previous resource set here.
         move_to(fx, config);
     }
     
    @@ -357,7 +410,8 @@ The training wheels PK needs to be 32 bytes long. -
    public fun update_training_wheels(fx: &signer, pk: option::Option<vector<u8>>)
    +
    #[deprecated]
    +public fun update_training_wheels(fx: &signer, pk: option::Option<vector<u8>>)
     
    @@ -368,6 +422,8 @@ The training wheels PK needs to be 32 bytes long.
    public fun update_training_wheels(fx: &signer, pk: Option<vector<u8>>) acquires Configuration {
         system_addresses::assert_aptos_framework(fx);
    +    chain_status::assert_genesis();
    +
         if (option::is_some(&pk)) {
             assert!(vector::length(option::borrow(&pk)) == 32, E_TRAINING_WHEELS_PK_WRONG_SIZE)
         };
    @@ -387,7 +443,8 @@ The training wheels PK needs to be 32 bytes long.
     
     
     
    -
    public fun update_max_exp_horizon(fx: &signer, max_exp_horizon_secs: u64)
    +
    #[deprecated]
    +public fun update_max_exp_horizon(fx: &signer, max_exp_horizon_secs: u64)
     
    @@ -398,6 +455,7 @@ The training wheels PK needs to be 32 bytes long.
    public fun update_max_exp_horizon(fx: &signer, max_exp_horizon_secs: u64) acquires Configuration {
         system_addresses::assert_aptos_framework(fx);
    +    chain_status::assert_genesis();
     
         let config = borrow_global_mut<Configuration>(signer::address_of(fx));
         config.max_exp_horizon_secs = max_exp_horizon_secs;
    @@ -414,7 +472,8 @@ The training wheels PK needs to be 32 bytes long.
     
     
     
    -
    public fun remove_all_override_auds(fx: &signer)
    +
    #[deprecated]
    +public fun remove_all_override_auds(fx: &signer)
     
    @@ -425,6 +484,7 @@ The training wheels PK needs to be 32 bytes long.
    public fun remove_all_override_auds(fx: &signer) acquires Configuration {
         system_addresses::assert_aptos_framework(fx);
    +    chain_status::assert_genesis();
     
         let config = borrow_global_mut<Configuration>(signer::address_of(fx));
         config.override_aud_vals = vector[];
    @@ -441,7 +501,8 @@ The training wheels PK needs to be 32 bytes long.
     
     
     
    -
    public fun add_override_aud(fx: &signer, aud: string::String)
    +
    #[deprecated]
    +public fun add_override_aud(fx: &signer, aud: string::String)
     
    @@ -452,6 +513,7 @@ The training wheels PK needs to be 32 bytes long.
    public fun add_override_aud(fx: &signer, aud: String) acquires Configuration {
         system_addresses::assert_aptos_framework(fx);
    +    chain_status::assert_genesis();
     
         let config = borrow_global_mut<Configuration>(signer::address_of(fx));
         vector::push_back(&mut config.override_aud_vals, aud);
    @@ -462,5 +524,277 @@ The training wheels PK needs to be 32 bytes long.
     
     
     
    +
    +
    +## Function `set_groth16_verification_key_for_next_epoch`
    +
    +Queues up a change to the Groth16 verification key. The change will only be effective after reconfiguration.
    +Only callable via governance proposal.
    +
    +WARNING: To mitigate against DoS attacks, a VK change should be done together with a training wheels PK change,
    +so that old ZKPs for the old VK cannot be replayed as potentially-valid ZKPs.
    +
    +WARNING: If a malicious key is set, this would lead to stolen funds.
    +
    +
    +
    public fun set_groth16_verification_key_for_next_epoch(fx: &signer, vk: keyless_account::Groth16VerificationKey)
    +
    + + + +
    +Implementation + + +
    public fun set_groth16_verification_key_for_next_epoch(fx: &signer, vk: Groth16VerificationKey) {
    +    system_addresses::assert_aptos_framework(fx);
    +    config_buffer::upsert<Groth16VerificationKey>(vk);
    +}
    +
    + + + +
    + + + +## Function `set_configuration_for_next_epoch` + +Queues up a change to the keyless configuration. The change will only be effective after reconfiguration. Only +callable via governance proposal. + +WARNING: A malicious Configuration could lead to DoS attacks, create liveness issues, or enable a malicious +recovery service provider to phish users' accounts. + + +
    public fun set_configuration_for_next_epoch(fx: &signer, config: keyless_account::Configuration)
    +
    + + + +
    +Implementation + + +
    public fun set_configuration_for_next_epoch(fx: &signer, config: Configuration) {
    +    system_addresses::assert_aptos_framework(fx);
    +    config_buffer::upsert<Configuration>(config);
    +}
    +
    + + + +
    + + + +## Function `update_training_wheels_for_next_epoch` + +Convenience method to queue up a change to the training wheels PK. The change will only be effective after +reconfiguration. Only callable via governance proposal. + +WARNING: If a malicious key is set, this *could* lead to stolen funds. + + +
    public fun update_training_wheels_for_next_epoch(fx: &signer, pk: option::Option<vector<u8>>)
    +
    + + + +
    +Implementation + + +
    public fun update_training_wheels_for_next_epoch(fx: &signer, pk: Option<vector<u8>>) acquires Configuration {
    +    system_addresses::assert_aptos_framework(fx);
    +
    +    // If a PK is being set, validate it first.
    +    if (option::is_some(&pk)) {
    +        let bytes = *option::borrow(&pk);
    +        let vpk = ed25519::new_validated_public_key_from_bytes(bytes);
    +        assert!(option::is_some(&vpk), E_TRAINING_WHEELS_PK_WRONG_SIZE)
    +    };
    +
    +    let config = if (config_buffer::does_exist<Configuration>()) {
    +        config_buffer::extract<Configuration>()
    +    } else {
    +        *borrow_global<Configuration>(signer::address_of(fx))
    +    };
    +
    +    config.training_wheels_pubkey = pk;
    +
    +    set_configuration_for_next_epoch(fx, config);
    +}
    +
    + + + +
    + + + +## Function `update_max_exp_horizon_for_next_epoch` + +Convenience method to queues up a change to the max expiration horizon. The change will only be effective after +reconfiguration. Only callable via governance proposal. + + +
    public fun update_max_exp_horizon_for_next_epoch(fx: &signer, max_exp_horizon_secs: u64)
    +
    + + + +
    +Implementation + + +
    public fun update_max_exp_horizon_for_next_epoch(fx: &signer, max_exp_horizon_secs: u64) acquires Configuration {
    +    system_addresses::assert_aptos_framework(fx);
    +
    +    let config = if (config_buffer::does_exist<Configuration>()) {
    +        config_buffer::extract<Configuration>()
    +    } else {
    +        *borrow_global<Configuration>(signer::address_of(fx))
    +    };
    +
    +    config.max_exp_horizon_secs = max_exp_horizon_secs;
    +
    +    set_configuration_for_next_epoch(fx, config);
    +}
    +
    + + + +
    + + + +## Function `remove_all_override_auds_for_next_epoch` + +Convenience method to queue up clearing the set of override aud's. The change will only be effective after +reconfiguration. Only callable via governance proposal. + +WARNING: When no override aud is set, recovery of keyless accounts associated with applications that disappeared +is no longer possible. + + +
    public fun remove_all_override_auds_for_next_epoch(fx: &signer)
    +
    + + + +
    +Implementation + + +
    public fun remove_all_override_auds_for_next_epoch(fx: &signer) acquires Configuration {
    +    system_addresses::assert_aptos_framework(fx);
    +
    +    let config = if (config_buffer::does_exist<Configuration>()) {
    +        config_buffer::extract<Configuration>()
    +    } else {
    +        *borrow_global<Configuration>(signer::address_of(fx))
    +    };
    +
    +    config.override_aud_vals = vector[];
    +
    +    set_configuration_for_next_epoch(fx, config);
    +}
    +
    + + + +
    + + + +## Function `add_override_aud_for_next_epoch` + +Convenience method to queue up an append to to the set of override aud's. The change will only be effective +after reconfiguration. Only callable via governance proposal. + +WARNING: If a malicious override aud is set, this *could* lead to stolen funds. + + +
    public fun add_override_aud_for_next_epoch(fx: &signer, aud: string::String)
    +
    + + + +
    +Implementation + + +
    public fun add_override_aud_for_next_epoch(fx: &signer, aud: String) acquires Configuration {
    +    system_addresses::assert_aptos_framework(fx);
    +
    +    let config = if (config_buffer::does_exist<Configuration>()) {
    +        config_buffer::extract<Configuration>()
    +    } else {
    +        *borrow_global<Configuration>(signer::address_of(fx))
    +    };
    +
    +    vector::push_back(&mut config.override_aud_vals, aud);
    +
    +    set_configuration_for_next_epoch(fx, config);
    +}
    +
    + + + +
    + + + +## Function `on_new_epoch` + +Only used in reconfigurations to apply the queued up configuration changes, if there are any. + + +
    public(friend) fun on_new_epoch(fx: &signer)
    +
    + + + +
    +Implementation + + +
    public(friend) fun on_new_epoch(fx: &signer) acquires Groth16VerificationKey, Configuration {
    +    system_addresses::assert_aptos_framework(fx);
    +
    +    if (config_buffer::does_exist<Groth16VerificationKey>()) {
    +        let vk = config_buffer::extract();
    +        if (exists<Groth16VerificationKey>(@aptos_framework)) {
    +            *borrow_global_mut<Groth16VerificationKey>(@aptos_framework) = vk;
    +        } else {
    +            move_to(fx, vk);
    +        }
    +    };
    +
    +    if (config_buffer::does_exist<Configuration>()) {
    +        let config = config_buffer::extract();
    +        if (exists<Configuration>(@aptos_framework)) {
    +            *borrow_global_mut<Configuration>(@aptos_framework) = config;
    +        } else {
    +            move_to(fx, config);
    +        }
    +    };
    +}
    +
    + + + +
    + + + +## Specification + + + +
    pragma verify=false;
    +
    + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/managed_coin.md b/aptos-move/framework/aptos-framework/doc/managed_coin.md index ded5b6a28e6b2..8ae37e71a1f9c 100644 --- a/aptos-move/framework/aptos-framework/doc/managed_coin.md +++ b/aptos-move/framework/aptos-framework/doc/managed_coin.md @@ -324,7 +324,8 @@ Required if user wants to start accepting deposits of CoinType in h -
    let account_addr = signer::address_of(account);
    +
    pragma verify = false;
    +let account_addr = signer::address_of(account);
     aborts_if !exists<Capabilities<CoinType>>(account_addr);
     let coin_store = global<coin::CoinStore<CoinType>>(account_addr);
     let balance = coin_store.coin.value;
    @@ -381,7 +382,8 @@ The Capabilities should not exist in the signer address.
     The dst_addr should not be frozen.
     
     
    -
    let account_addr = signer::address_of(account);
    +
    pragma verify = false;
    +let account_addr = signer::address_of(account);
     // This enforces high-level requirement 3:
     aborts_if !exists<Capabilities<CoinType>>(account_addr);
     let addr = type_info::type_of<CoinType>().account_address;
    @@ -410,7 +412,8 @@ An account can only be registered once.
     Updating Account.guid_creation_num will not overflow.
     
     
    -
    let account_addr = signer::address_of(account);
    +
    pragma verify = false;
    +let account_addr = signer::address_of(account);
     let acc = global<account::Account>(account_addr);
     aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
     aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 > MAX_U64;
    diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md
    index 5a0a588ef6b0b..a5fcc8ab9b835 100644
    --- a/aptos-move/framework/aptos-framework/doc/multisig_account.md
    +++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md
    @@ -48,14 +48,23 @@ and implement the governance voting logic on top.
     -  [Struct `MultisigAccountCreationMessage`](#0x1_multisig_account_MultisigAccountCreationMessage)
     -  [Struct `MultisigAccountCreationWithAuthKeyRevocationMessage`](#0x1_multisig_account_MultisigAccountCreationWithAuthKeyRevocationMessage)
     -  [Struct `AddOwnersEvent`](#0x1_multisig_account_AddOwnersEvent)
    +-  [Struct `AddOwners`](#0x1_multisig_account_AddOwners)
     -  [Struct `RemoveOwnersEvent`](#0x1_multisig_account_RemoveOwnersEvent)
    +-  [Struct `RemoveOwners`](#0x1_multisig_account_RemoveOwners)
     -  [Struct `UpdateSignaturesRequiredEvent`](#0x1_multisig_account_UpdateSignaturesRequiredEvent)
    +-  [Struct `UpdateSignaturesRequired`](#0x1_multisig_account_UpdateSignaturesRequired)
     -  [Struct `CreateTransactionEvent`](#0x1_multisig_account_CreateTransactionEvent)
    +-  [Struct `CreateTransaction`](#0x1_multisig_account_CreateTransaction)
     -  [Struct `VoteEvent`](#0x1_multisig_account_VoteEvent)
    +-  [Struct `Vote`](#0x1_multisig_account_Vote)
     -  [Struct `ExecuteRejectedTransactionEvent`](#0x1_multisig_account_ExecuteRejectedTransactionEvent)
    +-  [Struct `ExecuteRejectedTransaction`](#0x1_multisig_account_ExecuteRejectedTransaction)
     -  [Struct `TransactionExecutionSucceededEvent`](#0x1_multisig_account_TransactionExecutionSucceededEvent)
    +-  [Struct `TransactionExecutionSucceeded`](#0x1_multisig_account_TransactionExecutionSucceeded)
     -  [Struct `TransactionExecutionFailedEvent`](#0x1_multisig_account_TransactionExecutionFailedEvent)
    +-  [Struct `TransactionExecutionFailed`](#0x1_multisig_account_TransactionExecutionFailed)
     -  [Struct `MetadataUpdatedEvent`](#0x1_multisig_account_MetadataUpdatedEvent)
    +-  [Struct `MetadataUpdated`](#0x1_multisig_account_MetadataUpdated)
     -  [Constants](#@Constants_0)
     -  [Function `metadata`](#0x1_multisig_account_metadata)
     -  [Function `num_signatures_required`](#0x1_multisig_account_num_signatures_required)
    @@ -495,6 +504,40 @@ Event emitted when new owners are added to the multisig account.
     
    + + + + +## Struct `AddOwners` + + + +
    #[event]
    +struct AddOwners has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +owners_added: vector<address> +
    +
    + +
    +
    + +
    @@ -523,6 +566,40 @@ Event emitted when new owners are removed from the multisig account.
    + + + + +## Struct `RemoveOwners` + + + +
    #[event]
    +struct RemoveOwners has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +owners_removed: vector<address> +
    +
    + +
    +
    + +
    @@ -557,6 +634,46 @@ Event emitted when the number of signatures required is updated. + + + + +## Struct `UpdateSignaturesRequired` + + + +
    #[event]
    +struct UpdateSignaturesRequired has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +old_num_signatures_required: u64 +
    +
    + +
    +
    +new_num_signatures_required: u64 +
    +
    + +
    +
    + +
    @@ -597,6 +714,52 @@ Event emitted when a transaction is created. + + + + +## Struct `CreateTransaction` + + + +
    #[event]
    +struct CreateTransaction has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +creator: address +
    +
    + +
    +
    +sequence_number: u64 +
    +
    + +
    +
    +transaction: multisig_account::MultisigTransaction +
    +
    + +
    +
    + +
    @@ -637,6 +800,52 @@ Event emitted when an owner approves or rejects a transaction. + + + + +## Struct `Vote` + + + +
    #[event]
    +struct Vote has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +owner: address +
    +
    + +
    +
    +sequence_number: u64 +
    +
    + +
    +
    +approved: bool +
    +
    + +
    +
    + +
    @@ -678,6 +887,52 @@ number of signatures required. + + + + +## Struct `ExecuteRejectedTransaction` + + + +
    #[event]
    +struct ExecuteRejectedTransaction has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +sequence_number: u64 +
    +
    + +
    +
    +num_rejections: u64 +
    +
    + +
    +
    +executor: address +
    +
    + +
    +
    + +
    @@ -724,6 +979,58 @@ Event emitted when a transaction is executed. + + + + +## Struct `TransactionExecutionSucceeded` + + + +
    #[event]
    +struct TransactionExecutionSucceeded has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +executor: address +
    +
    + +
    +
    +sequence_number: u64 +
    +
    + +
    +
    +transaction_payload: vector<u8> +
    +
    + +
    +
    +num_approvals: u64 +
    +
    + +
    +
    + +
    @@ -776,6 +1083,64 @@ Event emitted when a transaction's execution failed. + + + + +## Struct `TransactionExecutionFailed` + + + +
    #[event]
    +struct TransactionExecutionFailed has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +executor: address +
    +
    + +
    +
    +sequence_number: u64 +
    +
    + +
    +
    +transaction_payload: vector<u8> +
    +
    + +
    +
    +num_approvals: u64 +
    +
    + +
    +
    +execution_error: multisig_account::ExecutionError +
    +
    + +
    +
    + +
    @@ -810,6 +1175,46 @@ Event emitted when a transaction's metadata is updated. + + + + +## Struct `MetadataUpdated` + + + +
    #[event]
    +struct MetadataUpdated has drop, store
    +
    + + + +
    +Fields + + +
    +
    +multisig_account: address +
    +
    + +
    +
    +old_metadata: simple_map::SimpleMap<string::String, vector<u8>> +
    +
    + +
    +
    +new_metadata: simple_map::SimpleMap<string::String, vector<u8>> +
    +
    + +
    +
    + +
    @@ -1008,6 +1413,16 @@ Transaction payload cannot be empty. + + +Provided target function does not match the payload stored in the on-chain transaction. + + +
    const EPAYLOAD_DOES_NOT_MATCH: u64 = 2010;
    +
    + + + Provided target function does not match the hash stored in the on-chain transaction. @@ -1193,7 +1608,9 @@ Return all pending transactions. Implementation -
    public fun get_pending_transactions(multisig_account: address): vector<MultisigTransaction> acquires MultisigAccount {
    +
    public fun get_pending_transactions(
    +    multisig_account: address
    +): vector<MultisigTransaction> acquires MultisigAccount {
         let pending_transactions: vector<MultisigTransaction> = vector[];
         let multisig_account = borrow_global<MultisigAccount>(multisig_account);
         let i = multisig_account.last_executed_sequence_number + 1;
    @@ -2222,6 +2639,15 @@ maliciously alter the number of signatures required.
         };
     
         if (emit_event) {
    +        if (std::features::module_event_migration_enabled()) {
    +            emit(
    +                MetadataUpdated {
    +                    multisig_account: multisig_address,
    +                    old_metadata,
    +                    new_metadata: multisig_account_resource.metadata,
    +                }
    +            )
    +        };
             emit_event(
                 &mut multisig_account_resource.metadata_updated_events,
                 MetadataUpdatedEvent {
    @@ -2261,8 +2687,7 @@ Create a multisig transaction, which will have one approval initially (from the
         assert!(vector::length(&payload) > 0, error::invalid_argument(EPAYLOAD_CANNOT_BE_EMPTY));
     
         assert_multisig_account_exists(multisig_account);
    -    let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account);
    -    assert_is_owner_internal(owner, multisig_account_resource);
    +    assert_is_owner(owner, multisig_account);
     
         let creator = address_of(owner);
         let transaction = MultisigTransaction {
    @@ -2272,7 +2697,7 @@ Create a multisig transaction, which will have one approval initially (from the
             creator,
             creation_time_secs: now_seconds(),
         };
    -    add_transaction(creator, multisig_account_resource, transaction);
    +    add_transaction(creator, multisig_account, transaction);
     }
     
    @@ -2307,8 +2732,7 @@ to provide the full payload, which will be validated against the hash stored on- assert!(vector::length(&payload_hash) == 32, error::invalid_argument(EINVALID_PAYLOAD_HASH)); assert_multisig_account_exists(multisig_account); - let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account); - assert_is_owner_internal(owner, multisig_account_resource); + assert_is_owner(owner, multisig_account); let creator = address_of(owner); let transaction = MultisigTransaction { @@ -2318,7 +2742,7 @@ to provide the full payload, which will be validated against the hash stored on- creator, creation_time_secs: now_seconds(), }; - add_transaction(creator, multisig_account_resource, transaction); + add_transaction(creator, multisig_account, transaction); }
    @@ -2416,6 +2840,16 @@ will continue to be an accessible entry point. simple_map::add(votes, owner_addr, approved); }; + if (std::features::module_event_migration_enabled()) { + emit( + Vote { + multisig_account, + owner: owner_addr, + sequence_number, + approved, + } + ); + }; emit_event( &mut multisig_account_resource.vote_events, VoteEvent { @@ -2510,10 +2944,11 @@ Remove the next transaction if it has sufficient owner rejections. multisig_account: address, ) acquires MultisigAccount { assert_multisig_account_exists(multisig_account); - let sequence_number = last_resolved_sequence_number(multisig_account) + 1; + assert_is_owner(owner, multisig_account); + let sequence_number = last_resolved_sequence_number(multisig_account) + 1; let owner_addr = address_of(owner); - if(features::multisig_v2_enhancement_feature_enabled()) { + if (features::multisig_v2_enhancement_feature_enabled()) { // Implicitly vote for rejection if the owner has not voted for rejection yet. if (!has_voted_for_rejection(multisig_account, sequence_number, owner_addr)) { reject_transaction(owner, multisig_account, sequence_number); @@ -2527,6 +2962,16 @@ Remove the next transaction if it has sufficient owner rejections. error::invalid_state(ENOT_ENOUGH_REJECTIONS), ); + if (std::features::module_event_migration_enabled()) { + emit( + ExecuteRejectedTransaction { + multisig_account, + sequence_number, + num_rejections, + executor: address_of(owner), + } + ); + }; emit_event( &mut multisig_account_resource.execute_rejected_transaction_events, ExecuteRejectedTransactionEvent { @@ -2602,7 +3047,7 @@ Transaction payload is optional if it's already stored on chain for the transact let sequence_number = last_resolved_sequence_number(multisig_account) + 1; assert_transaction_exists(multisig_account, sequence_number); - if(features::multisig_v2_enhancement_feature_enabled()) { + if (features::multisig_v2_enhancement_feature_enabled()) { assert!( can_execute(address_of(owner), multisig_account, sequence_number), error::invalid_argument(ENOT_ENOUGH_APPROVALS), @@ -2626,6 +3071,19 @@ Transaction payload is optional if it's already stored on chain for the transact error::invalid_argument(EPAYLOAD_DOES_NOT_MATCH_HASH), ); }; + + // If the transaction payload is stored on chain and there is a provided payload, + // verify that the provided payload matches the stored payload. + if (features::abort_if_multisig_payload_mismatch_enabled() + && option::is_some(&transaction.payload) + && !vector::is_empty(&payload) + ) { + let stored_payload = option::borrow(&transaction.payload); + assert!( + payload == *stored_payload, + error::invalid_argument(EPAYLOAD_DOES_NOT_MATCH), + ); + } }
    @@ -2657,6 +3115,17 @@ This function is private so no other code can call this beside the VM itself as ) acquires MultisigAccount { let num_approvals = transaction_execution_cleanup_common(executor, multisig_account); let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account); + if (std::features::module_event_migration_enabled()) { + emit( + TransactionExecutionSucceeded { + multisig_account, + sequence_number: multisig_account_resource.last_executed_sequence_number, + transaction_payload, + num_approvals, + executor, + } + ); + }; emit_event( &mut multisig_account_resource.execute_transaction_events, TransactionExecutionSucceededEvent { @@ -2698,6 +3167,18 @@ This function is private so no other code can call this beside the VM itself as ) acquires MultisigAccount { let num_approvals = transaction_execution_cleanup_common(executor, multisig_account); let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account); + if (std::features::module_event_migration_enabled()) { + emit( + TransactionExecutionFailed { + multisig_account, + executor, + sequence_number: multisig_account_resource.last_executed_sequence_number, + transaction_payload, + num_approvals, + execution_error, + } + ); + }; emit_event( &mut multisig_account_resource.transaction_execution_failed_events, TransactionExecutionFailedEvent { @@ -2737,7 +3218,17 @@ This function is private so no other code can call this beside the VM itself as let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account); let (num_approvals, _) = remove_executed_transaction(multisig_account_resource); - if(features::multisig_v2_enhancement_feature_enabled() && implicit_approval) { + if (features::multisig_v2_enhancement_feature_enabled() && implicit_approval) { + if (std::features::module_event_migration_enabled()) { + emit( + Vote { + multisig_account, + owner: executor, + sequence_number, + approved: true, + } + ); + }; num_approvals = num_approvals + 1; emit_event( &mut multisig_account_resource.vote_events, @@ -2790,7 +3281,7 @@ This function is private so no other code can call this beside the VM itself as -
    fun add_transaction(creator: address, multisig_account: &mut multisig_account::MultisigAccount, transaction: multisig_account::MultisigTransaction)
    +
    fun add_transaction(creator: address, multisig_account: address, transaction: multisig_account::MultisigTransaction)
     
    @@ -2799,23 +3290,33 @@ This function is private so no other code can call this beside the VM itself as Implementation -
    fun add_transaction(creator: address, multisig_account: &mut MultisigAccount, transaction: MultisigTransaction) {
    -    if(features::multisig_v2_enhancement_feature_enabled()) {
    -        let num_pending_transactions = multisig_account.next_sequence_number - (multisig_account.last_executed_sequence_number + 1);
    +
    inline fun add_transaction(
    +    creator: address,
    +    multisig_account: address,
    +    transaction: MultisigTransaction
    +) {
    +    if (features::multisig_v2_enhancement_feature_enabled()) {
             assert!(
    -            num_pending_transactions < MAX_PENDING_TRANSACTIONS,
    +            available_transaction_queue_capacity(multisig_account) > 0,
                 error::invalid_state(EMAX_PENDING_TRANSACTIONS_EXCEEDED)
             );
         };
     
    +    let multisig_account_resource = borrow_global_mut<MultisigAccount>(multisig_account);
    +
         // The transaction creator also automatically votes for the transaction.
         simple_map::add(&mut transaction.votes, creator, true);
     
    -    let sequence_number = multisig_account.next_sequence_number;
    -    multisig_account.next_sequence_number = sequence_number + 1;
    -    table::add(&mut multisig_account.transactions, sequence_number, transaction);
    +    let sequence_number = multisig_account_resource.next_sequence_number;
    +    multisig_account_resource.next_sequence_number = sequence_number + 1;
    +    table::add(&mut multisig_account_resource.transactions, sequence_number, transaction);
    +    if (std::features::module_event_migration_enabled()) {
    +        emit(
    +            CreateTransaction { multisig_account: multisig_account, creator, sequence_number, transaction }
    +        );
    +    };
         emit_event(
    -        &mut multisig_account.create_transaction_events,
    +        &mut multisig_account_resource.create_transaction_events,
             CreateTransactionEvent { creator, sequence_number, transaction },
         );
     }
    @@ -3203,6 +3704,9 @@ Add new owners, remove owners to remove, update signatures required.
                 &multisig_account_ref_mut.owners,
                 multisig_address
             );
    +        if (std::features::module_event_migration_enabled()) {
    +            emit(AddOwners { multisig_account: multisig_address, owners_added: new_owners });
    +        };
             emit_event(
                 &mut multisig_account_ref_mut.add_owners_events,
                 AddOwnersEvent { owners_added: new_owners }
    @@ -3224,6 +3728,11 @@ Add new owners, remove owners to remove, update signatures required.
             });
             // Only emit event if owner(s) actually removed.
             if (vector::length(&owners_removed) > 0) {
    +            if (std::features::module_event_migration_enabled()) {
    +                emit(
    +                    RemoveOwners { multisig_account: multisig_address, owners_removed }
    +                );
    +            };
                 emit_event(
                     &mut multisig_account_ref_mut.remove_owners_events,
                     RemoveOwnersEvent { owners_removed }
    @@ -3244,6 +3753,15 @@ Add new owners, remove owners to remove, update signatures required.
             if (new_num_signatures_required != old_num_signatures_required) {
                 multisig_account_ref_mut.num_signatures_required =
                     new_num_signatures_required;
    +            if (std::features::module_event_migration_enabled()) {
    +                emit(
    +                    UpdateSignaturesRequired {
    +                        multisig_account: multisig_address,
    +                        old_num_signatures_required,
    +                        new_num_signatures_required,
    +                    }
    +                );
    +            };
                 emit_event(
                     &mut multisig_account_ref_mut.update_signature_required_events,
                     UpdateSignaturesRequiredEvent {
    @@ -3374,7 +3892,7 @@ Add new owners, remove owners to remove, update signatures required.
     12
     Performing any changes on the list of owners such as adding new owners, removing owners, swapping owners should ensure that the number of required signature, for the multi-signature account remains valid.
     Critical
    -The following function as used to modify the owners list and the required signature of the swap_owner, swap_owners, swap_owners_and_update_signatures_required, update_signatures_required. All of these functions use update_owner_schema function to process these changes, the function validates the owner list while adding and verifies that the account has enough required signatures and updates the owner's schema.
    +The following function as used to modify the owners list and the required signature of the account: add_owner, add_owners, add_owners_and_update_signatures_required, remove_owner, remove_owners, swap_owner, swap_owners, swap_owners_and_update_signatures_required, update_signatures_required. All of these functions use update_owner_schema function to process these changes, the function validates the owner list while adding and verifies that the account has enough required signatures and updates the owner's schema.
     Audited that the owners are added successfully. (add_owner, add_owners, add_owners_and_update_signatures_required, swap_owner, swap_owners, swap_owners_and_update_signatures_required, update_owner_schema) Audited that the owners are removed successfully. (remove_owner, remove_owners, swap_owner, swap_owners, swap_owners_and_update_signatures_required, update_owner_schema) Audited that the num_signatures_required is updated successfully. (add_owners_and_update_signatures_required, swap_owners_and_update_signatures_required, update_signatures_required, update_owner_schema)
     
     
    @@ -3382,7 +3900,7 @@ Add new owners, remove owners to remove, update signatures required.
     13
     The creation of a transaction should be limited to an account owner, which should be automatically considered a voter; additionally, the account's sequence should increase monotonically.
     Critical
    -The following functions can only be called by the owners of the account and create a transaction and uses add_transaction function to gives approval on behalf of the creator and increments the create_transaction.
    +The following functions can only be called by the owners of the account and create a transaction and uses add_transaction function to gives approval on behalf of the creator and increments the next_sequence_number and finally adds the transaction to the MultsigAccount: create_transaction_with_hash, create_transaction.
     Audited it aborts if the caller is not in the owner's list of the account. (create_transaction_with_hash, create_transaction) Audited that the transaction is successfully stored in the MultisigAccount.(create_transaction_with_hash, create_transaction, add_transaction) Audited that the creators voted to approve the transaction. (create_transaction_with_hash, create_transaction, add_transaction) Audited that the next_sequence_number increases monotonically. (create_transaction_with_hash, create_transaction, add_transaction)
     
     
    diff --git a/aptos-move/framework/aptos-framework/doc/object.md b/aptos-move/framework/aptos-framework/doc/object.md
    index f6fedad987f5c..bba128592ff31 100644
    --- a/aptos-move/framework/aptos-framework/doc/object.md
    +++ b/aptos-move/framework/aptos-framework/doc/object.md
    @@ -3,7 +3,7 @@
     
     # Module `0x1::object`
     
    -This defines the Move object model with the the following properties:
    +This defines the Move object model with the following properties:
     - Simplified storage interface that supports a heterogeneous collection of resources to be
     stored together. This enables data types to share a common core data layer (e.g., tokens),
     while having richer extensions (e.g., concert ticket, sword).
    @@ -23,6 +23,7 @@ make it so that a reference to a global object can be returned from a function.
     
     -  [Resource `ObjectCore`](#0x1_object_ObjectCore)
     -  [Resource `TombStone`](#0x1_object_TombStone)
    +-  [Resource `Untransferable`](#0x1_object_Untransferable)
     -  [Struct `ObjectGroup`](#0x1_object_ObjectGroup)
     -  [Struct `Object`](#0x1_object_Object)
     -  [Struct `ConstructorRef`](#0x1_object_ConstructorRef)
    @@ -32,12 +33,15 @@ make it so that a reference to a global object can be returned from a function.
     -  [Struct `LinearTransferRef`](#0x1_object_LinearTransferRef)
     -  [Struct `DeriveRef`](#0x1_object_DeriveRef)
     -  [Struct `TransferEvent`](#0x1_object_TransferEvent)
    +-  [Struct `Transfer`](#0x1_object_Transfer)
     -  [Constants](#@Constants_0)
    +-  [Function `is_untransferable`](#0x1_object_is_untransferable)
     -  [Function `is_burnt`](#0x1_object_is_burnt)
     -  [Function `address_to_object`](#0x1_object_address_to_object)
     -  [Function `is_object`](#0x1_object_is_object)
     -  [Function `object_exists`](#0x1_object_object_exists)
     -  [Function `create_object_address`](#0x1_object_create_object_address)
    +-  [Function `create_user_derived_object_address_impl`](#0x1_object_create_user_derived_object_address_impl)
     -  [Function `create_user_derived_object_address`](#0x1_object_create_user_derived_object_address)
     -  [Function `create_guid_object_address`](#0x1_object_create_guid_object_address)
     -  [Function `exists_at`](#0x1_object_exists_at)
    @@ -47,6 +51,7 @@ make it so that a reference to a global object can be returned from a function.
     -  [Function `create_user_derived_object`](#0x1_object_create_user_derived_object)
     -  [Function `create_object`](#0x1_object_create_object)
     -  [Function `create_sticky_object`](#0x1_object_create_sticky_object)
    +-  [Function `create_sticky_object_at_address`](#0x1_object_create_sticky_object_at_address)
     -  [Function `create_object_from_account`](#0x1_object_create_object_from_account)
     -  [Function `create_object_from_object`](#0x1_object_create_object_from_object)
     -  [Function `create_object_from_guid`](#0x1_object_create_object_from_guid)
    @@ -67,6 +72,7 @@ make it so that a reference to a global object can be returned from a function.
     -  [Function `generate_signer_for_extending`](#0x1_object_generate_signer_for_extending)
     -  [Function `address_from_extend_ref`](#0x1_object_address_from_extend_ref)
     -  [Function `disable_ungated_transfer`](#0x1_object_disable_ungated_transfer)
    +-  [Function `set_untransferable`](#0x1_object_set_untransferable)
     -  [Function `enable_ungated_transfer`](#0x1_object_enable_ungated_transfer)
     -  [Function `generate_linear_transfer_ref`](#0x1_object_generate_linear_transfer_ref)
     -  [Function `transfer_with_ref`](#0x1_object_transfer_with_ref)
    @@ -82,11 +88,13 @@ make it so that a reference to a global object can be returned from a function.
     -  [Function `owner`](#0x1_object_owner)
     -  [Function `is_owner`](#0x1_object_is_owner)
     -  [Function `owns`](#0x1_object_owns)
    +-  [Function `root_owner`](#0x1_object_root_owner)
     -  [Specification](#@Specification_1)
         -  [High-level Requirements](#high-level-req)
         -  [Module-level Specification](#module-level-spec)
         -  [Function `address_to_object`](#@Specification_1_address_to_object)
         -  [Function `create_object_address`](#@Specification_1_create_object_address)
    +    -  [Function `create_user_derived_object_address_impl`](#@Specification_1_create_user_derived_object_address_impl)
         -  [Function `create_user_derived_object_address`](#@Specification_1_create_user_derived_object_address)
         -  [Function `create_guid_object_address`](#@Specification_1_create_guid_object_address)
         -  [Function `exists_at`](#@Specification_1_exists_at)
    @@ -96,17 +104,20 @@ make it so that a reference to a global object can be returned from a function.
         -  [Function `create_user_derived_object`](#@Specification_1_create_user_derived_object)
         -  [Function `create_object`](#@Specification_1_create_object)
         -  [Function `create_sticky_object`](#@Specification_1_create_sticky_object)
    +    -  [Function `create_sticky_object_at_address`](#@Specification_1_create_sticky_object_at_address)
         -  [Function `create_object_from_account`](#@Specification_1_create_object_from_account)
         -  [Function `create_object_from_object`](#@Specification_1_create_object_from_object)
         -  [Function `create_object_from_guid`](#@Specification_1_create_object_from_guid)
         -  [Function `create_object_internal`](#@Specification_1_create_object_internal)
         -  [Function `generate_delete_ref`](#@Specification_1_generate_delete_ref)
    +    -  [Function `generate_transfer_ref`](#@Specification_1_generate_transfer_ref)
         -  [Function `object_from_constructor_ref`](#@Specification_1_object_from_constructor_ref)
         -  [Function `create_guid`](#@Specification_1_create_guid)
         -  [Function `new_event_handle`](#@Specification_1_new_event_handle)
         -  [Function `object_from_delete_ref`](#@Specification_1_object_from_delete_ref)
         -  [Function `delete`](#@Specification_1_delete)
         -  [Function `disable_ungated_transfer`](#@Specification_1_disable_ungated_transfer)
    +    -  [Function `set_untransferable`](#@Specification_1_set_untransferable)
         -  [Function `enable_ungated_transfer`](#@Specification_1_enable_ungated_transfer)
         -  [Function `generate_linear_transfer_ref`](#@Specification_1_generate_linear_transfer_ref)
         -  [Function `transfer_with_ref`](#@Specification_1_transfer_with_ref)
    @@ -121,6 +132,7 @@ make it so that a reference to a global object can be returned from a function.
         -  [Function `owner`](#@Specification_1_owner)
         -  [Function `is_owner`](#@Specification_1_is_owner)
         -  [Function `owns`](#@Specification_1_owns)
    +    -  [Function `root_owner`](#@Specification_1_root_owner)
     
     
     
    use 0x1::account;
    @@ -214,6 +226,35 @@ This is added to objects that are burnt (ownership transferred to BURN_ADDRESS).
     
     
     
    +
    +
    +
    +
    +## Resource `Untransferable`
    +
    +The existence of this renders all TransferRefs irrelevant. The object cannot be moved.
    +
    +
    +
    #[resource_group_member(#[group = 0x1::object::ObjectGroup])]
    +struct Untransferable has key
    +
    + + + +
    +Fields + + +
    +
    +dummy_field: bool +
    +
    + +
    +
    + +
    @@ -464,8 +505,48 @@ Used to create derived objects from a given objects. Emitted whenever the object's owner field is changed. +
    struct TransferEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +object: address +
    +
    + +
    +
    +from: address +
    +
    + +
    +
    +to: address +
    +
    + +
    +
    + + +
    + + + +## Struct `Transfer` + +Emitted whenever the object's owner field is changed. + +
    #[event]
    -struct TransferEvent has drop, store
    +struct Transfer has drop, store
     
    @@ -593,6 +674,16 @@ Cannot reclaim objects that weren't burnt. + + +Object is untransferable any operations that might result in a transfer are disallowed. + + +
    const EOBJECT_NOT_TRANSFERRABLE: u64 = 9;
    +
    + + + The resource is not stored at the specified address. @@ -673,6 +764,31 @@ derivation to produce an object address. + + +## Function `is_untransferable` + + + +
    #[view]
    +public fun is_untransferable<T: key>(object: object::Object<T>): bool
    +
    + + + +
    +Implementation + + +
    public fun is_untransferable<T: key>(object: Object<T>): bool {
    +    exists<Untransferable>(object.inner)
    +}
    +
    + + + +
    + ## Function `is_burnt` @@ -801,6 +917,28 @@ Derives an object address from source material: sha3_256([creator address | seed + + + + +## Function `create_user_derived_object_address_impl` + + + +
    fun create_user_derived_object_address_impl(source: address, derive_from: address): address
    +
    + + + +
    +Implementation + + +
    native fun create_user_derived_object_address_impl(source: address, derive_from: address): address;
    +
    + + +
    @@ -820,10 +958,14 @@ Derives an object address from the source address and an object: sha3_256([sourc
    public fun create_user_derived_object_address(source: address, derive_from: address): address {
    -    let bytes = bcs::to_bytes(&source);
    -    vector::append(&mut bytes, bcs::to_bytes(&derive_from));
    -    vector::push_back(&mut bytes, OBJECT_DERIVED_SCHEME);
    -    from_bcs::to_address(hash::sha3_256(bytes))
    +    if (std::features::object_native_derived_address_enabled()) {
    +        create_user_derived_object_address_impl(source, derive_from)
    +    } else {
    +        let bytes = bcs::to_bytes(&source);
    +        vector::append(&mut bytes, bcs::to_bytes(&derive_from));
    +        vector::push_back(&mut bytes, OBJECT_DERIVED_SCHEME);
    +        from_bcs::to_address(hash::sha3_256(bytes))
    +    }
     }
     
    @@ -1039,6 +1181,34 @@ Same as create_object except the object to be created will be undel + + + + +## Function `create_sticky_object_at_address` + +Create a sticky object at a specific address. Only used by aptos_framework::coin. + + +
    public(friend) fun create_sticky_object_at_address(owner_address: address, object_address: address): object::ConstructorRef
    +
    + + + +
    +Implementation + + +
    public(friend) fun create_sticky_object_at_address(
    +    owner_address: address,
    +    object_address: address,
    +): ConstructorRef {
    +    create_object_internal(owner_address, object_address, false)
    +}
    +
    + + +
    @@ -1243,6 +1413,7 @@ Generates the TransferRef, which can be used to manage object transfers.
    public fun generate_transfer_ref(ref: &ConstructorRef): TransferRef {
    +    assert!(!exists<Untransferable>(ref.self), error::permission_denied(EOBJECT_NOT_TRANSFERRABLE));
         TransferRef { self: ref.self }
     }
     
    @@ -1496,7 +1667,7 @@ Removes from the specified Object from global storage. Implementation -
    public fun delete(ref: DeleteRef) acquires ObjectCore {
    +
    public fun delete(ref: DeleteRef) acquires Untransferable, ObjectCore {
         let object_core = move_from<ObjectCore>(ref.self);
         let ObjectCore {
             guid_creation_num: _,
    @@ -1504,6 +1675,11 @@ Removes from the specified Object from global storage.
             allow_ungated_transfer: _,
             transfer_events,
         } = object_core;
    +
    +    if (exists<Untransferable>(ref.self)) {
    +      let Untransferable {} = move_from<Untransferable>(ref.self);
    +    };
    +
         event::destroy_handle(transfer_events);
     }
     
    @@ -1586,6 +1762,34 @@ Disable direct transfer, transfers can only be triggered via a TransferRef + + + + +## Function `set_untransferable` + +Prevent moving of the object + + +
    public fun set_untransferable(ref: &object::ConstructorRef)
    +
    + + + +
    +Implementation + + +
    public fun set_untransferable(ref: &ConstructorRef) acquires ObjectCore {
    +    let object = borrow_global_mut<ObjectCore>(ref.self);
    +    object.allow_ungated_transfer = false;
    +    let object_signer = generate_signer(ref);
    +    move_to(&object_signer, Untransferable {});
    +}
    +
    + + +
    @@ -1605,6 +1809,7 @@ Enable direct transfer.
    public fun enable_ungated_transfer(ref: &TransferRef) acquires ObjectCore {
    +    assert!(!exists<Untransferable>(ref.self), error::permission_denied(EOBJECT_NOT_TRANSFERRABLE));
         let object = borrow_global_mut<ObjectCore>(ref.self);
         object.allow_ungated_transfer = true;
     }
    @@ -1632,6 +1837,7 @@ time of generation is the owner at the time of transferring.
     
     
     
    public fun generate_linear_transfer_ref(ref: &TransferRef): LinearTransferRef acquires ObjectCore {
    +    assert!(!exists<Untransferable>(ref.self), error::permission_denied(EOBJECT_NOT_TRANSFERRABLE));
         let owner = owner(Object<ObjectCore> { inner: ref.self });
         LinearTransferRef {
             self: ref.self,
    @@ -1660,19 +1866,28 @@ Transfer to the destination address using a LinearTransferRef.
     Implementation
     
     
    -
    public fun transfer_with_ref(ref: LinearTransferRef, to: address) acquires ObjectCore {
    +
    public fun transfer_with_ref(ref: LinearTransferRef, to: address) acquires ObjectCore, TombStone {
    +    assert!(!exists<Untransferable>(ref.self), error::permission_denied(EOBJECT_NOT_TRANSFERRABLE));
    +
    +    // Undo soft burn if present as we don't want the original owner to be able to reclaim by calling unburn later.
    +    if (exists<TombStone>(ref.self)) {
    +        let TombStone { original_owner: _ } = move_from<TombStone>(ref.self);
    +    };
    +
         let object = borrow_global_mut<ObjectCore>(ref.self);
         assert!(
             object.owner == ref.owner,
             error::permission_denied(ENOT_OBJECT_OWNER),
         );
    -    event::emit(
    -        TransferEvent {
    -            object: ref.self,
    -            from: object.owner,
    -            to,
    -        },
    -    );
    +    if (std::features::module_event_migration_enabled()) {
    +        event::emit(
    +            Transfer {
    +                object: ref.self,
    +                from: object.owner,
    +                to,
    +            },
    +        );
    +    };
         event::emit_event(
             &mut object.transfer_events,
             TransferEvent {
    @@ -1800,13 +2015,15 @@ hierarchy.
     
    inline fun transfer_raw_inner(object: address, to: address) acquires ObjectCore {
         let object_core = borrow_global_mut<ObjectCore>(object);
         if (object_core.owner != to) {
    -        event::emit(
    -            TransferEvent {
    -                object,
    -                from: object_core.owner,
    -                to,
    -            },
    -        );
    +        if (std::features::module_event_migration_enabled()) {
    +            event::emit(
    +                Transfer {
    +                    object,
    +                    from: object_core.owner,
    +                    to,
    +                },
    +            );
    +        };
             event::emit_event(
                 &mut object_core.transfer_events,
                 TransferEvent {
    @@ -1888,9 +2105,7 @@ objects may have cyclic dependencies.
         let count = 0;
         while (owner != current_address) {
             count = count + 1;
    -        if (std::features::max_object_nesting_check_enabled()) {
    -            assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING))
    -        };
    +        assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING));
             // At this point, the first object exists and so the more likely case is that the
             // object's owner is not an object. So we return a more sensible error.
             assert!(
    @@ -2092,9 +2307,7 @@ Return true if the provided address has indirect or direct ownership of the prov
         let count = 0;
         while (owner != current_address) {
             count = count + 1;
    -        if (std::features::max_object_nesting_check_enabled()) {
    -            assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING))
    -        };
    +        assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING));
             if (!exists<ObjectCore>(current_address)) {
                 return false
             };
    @@ -2108,6 +2321,36 @@ Return true if the provided address has indirect or direct ownership of the prov
     
     
     
    +
    +
    +
    +
    +## Function `root_owner`
    +
    +Returns the root owner of an object. As objects support nested ownership, it can be useful
    +to determine the identity of the starting point of ownership.
    +
    +
    +
    public fun root_owner<T: key>(object: object::Object<T>): address
    +
    + + + +
    +Implementation + + +
    public fun root_owner<T: key>(object: Object<T>): address acquires ObjectCore {
    +    let obj_owner = owner(object);
    +    while (is_object(obj_owner)) {
    +        obj_owner = owner(address_to_object<ObjectCore>(obj_owner));
    +    };
    +    obj_owner
    +}
    +
    + + +
    @@ -2235,6 +2478,32 @@ Return true if the provided address has indirect or direct ownership of the prov + + + + +
    fun spec_create_user_derived_object_address_impl(source: address, derive_from: address): address;
    +
    + + + + + +### Function `create_user_derived_object_address_impl` + + +
    fun create_user_derived_object_address_impl(source: address, derive_from: address): address
    +
    + + + + +
    pragma opaque;
    +ensures [abstract] result == spec_create_user_derived_object_address_impl(source, derive_from);
    +
    + + + ### Function `create_user_derived_object_address` @@ -2460,6 +2729,22 @@ Return true if the provided address has indirect or direct ownership of the prov + + +### Function `create_sticky_object_at_address` + + +
    public(friend) fun create_sticky_object_at_address(owner_address: address, object_address: address): object::ConstructorRef
    +
    + + + + +
    pragma verify = false;
    +
    + + + ### Function `create_object_from_account` @@ -2637,6 +2922,25 @@ Return true if the provided address has indirect or direct ownership of the prov + + +### Function `generate_transfer_ref` + + +
    public fun generate_transfer_ref(ref: &object::ConstructorRef): object::TransferRef
    +
    + + + + +
    aborts_if exists<Untransferable>(ref.self);
    +ensures result == TransferRef {
    +    self: ref.self,
    +};
    +
    + + + ### Function `object_from_constructor_ref` @@ -2759,6 +3063,25 @@ Return true if the provided address has indirect or direct ownership of the prov + + +### Function `set_untransferable` + + +
    public fun set_untransferable(ref: &object::ConstructorRef)
    +
    + + + + +
    aborts_if !exists<ObjectCore>(ref.self);
    +aborts_if exists<Untransferable>(ref.self);
    +ensures exists<Untransferable>(ref.self);
    +ensures global<ObjectCore>(ref.self).allow_ungated_transfer == false;
    +
    + + + ### Function `enable_ungated_transfer` @@ -2770,7 +3093,8 @@ Return true if the provided address has indirect or direct ownership of the prov -
    aborts_if !exists<ObjectCore>(ref.self);
    +
    aborts_if exists<Untransferable>(ref.self);
    +aborts_if !exists<ObjectCore>(ref.self);
     ensures global<ObjectCore>(ref.self).allow_ungated_transfer == true;
     
    @@ -2787,7 +3111,8 @@ Return true if the provided address has indirect or direct ownership of the prov -
    aborts_if !exists<ObjectCore>(ref.self);
    +
    aborts_if exists<Untransferable>(ref.self);
    +aborts_if !exists<ObjectCore>(ref.self);
     let owner = global<ObjectCore>(ref.self).owner;
     ensures result == LinearTransferRef {
         self: ref.self,
    @@ -2808,7 +3133,8 @@ Return true if the provided address has indirect or direct ownership of the prov
     
     
     
    -
    let object = global<ObjectCore>(ref.self);
    +
    aborts_if exists<Untransferable>(ref.self);
    +let object = global<ObjectCore>(ref.self);
     aborts_if !exists<ObjectCore>(ref.self);
     // This enforces high-level requirement 5:
     aborts_if object.owner != ref.owner;
    @@ -3027,6 +3353,22 @@ Return true if the provided address has indirect or direct ownership of the prov
     
     
     
    +
    +
    +### Function `root_owner`
    +
    +
    +
    public fun root_owner<T: key>(object: object::Object<T>): address
    +
    + + + + +
    pragma aborts_if_is_partial;
    +
    + + + diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md index 04ae633199d93..314baa3612ba9 100644 --- a/aptos-move/framework/aptos-framework/doc/overview.md +++ b/aptos-move/framework/aptos-framework/doc/overview.md @@ -28,9 +28,11 @@ This is the reference documentation of the Aptos framework. - [`0x1::consensus_config`](consensus_config.md#0x1_consensus_config) - [`0x1::create_signer`](create_signer.md#0x1_create_signer) - [`0x1::delegation_pool`](delegation_pool.md#0x1_delegation_pool) +- [`0x1::dispatchable_fungible_asset`](dispatchable_fungible_asset.md#0x1_dispatchable_fungible_asset) - [`0x1::dkg`](dkg.md#0x1_dkg) - [`0x1::event`](event.md#0x1_event) - [`0x1::execution_config`](execution_config.md#0x1_execution_config) +- [`0x1::function_info`](function_info.md#0x1_function_info) - [`0x1::fungible_asset`](fungible_asset.md#0x1_fungible_asset) - [`0x1::gas_schedule`](gas_schedule.md#0x1_gas_schedule) - [`0x1::genesis`](genesis.md#0x1_genesis) @@ -46,7 +48,9 @@ This is the reference documentation of the Aptos framework. - [`0x1::optional_aggregator`](optional_aggregator.md#0x1_optional_aggregator) - [`0x1::primary_fungible_store`](primary_fungible_store.md#0x1_primary_fungible_store) - [`0x1::randomness`](randomness.md#0x1_randomness) +- [`0x1::randomness_api_v0_config`](randomness_api_v0_config.md#0x1_randomness_api_v0_config) - [`0x1::randomness_config`](randomness_config.md#0x1_randomness_config) +- [`0x1::randomness_config_seqnum`](randomness_config_seqnum.md#0x1_randomness_config_seqnum) - [`0x1::reconfiguration`](reconfiguration.md#0x1_reconfiguration) - [`0x1::reconfiguration_state`](reconfiguration_state.md#0x1_reconfiguration_state) - [`0x1::reconfiguration_with_dkg`](reconfiguration_with_dkg.md#0x1_reconfiguration_with_dkg) diff --git a/aptos-move/framework/aptos-framework/doc/primary_fungible_store.md b/aptos-move/framework/aptos-framework/doc/primary_fungible_store.md index b6a28093d1057..28b1374e4f850 100644 --- a/aptos-move/framework/aptos-framework/doc/primary_fungible_store.md +++ b/aptos-move/framework/aptos-framework/doc/primary_fungible_store.md @@ -13,7 +13,7 @@ The transfer flow works as below: 2. The fungible asset metadata object calls ensure_primary_store_exists to ensure that both the sender's and the recipient's primary stores exist. If either doesn't, it will be created. 3. The fungible asset metadata object calls withdraw on the sender's primary store to withdraw amount of -fungible asset from it. This emits an withdraw event. +fungible asset from it. This emits a withdraw event. 4. The fungible asset metadata object calls deposit on the recipient's primary store to deposit amount of fungible asset to it. This emits an deposit event. @@ -25,11 +25,17 @@ fungible asset to it. This emits an deposit event. - [Function `primary_store_address`](#0x1_primary_fungible_store_primary_store_address) - [Function `primary_store`](#0x1_primary_fungible_store_primary_store) - [Function `primary_store_exists`](#0x1_primary_fungible_store_primary_store_exists) +- [Function `primary_store_address_inlined`](#0x1_primary_fungible_store_primary_store_address_inlined) +- [Function `primary_store_inlined`](#0x1_primary_fungible_store_primary_store_inlined) +- [Function `primary_store_exists_inlined`](#0x1_primary_fungible_store_primary_store_exists_inlined) - [Function `balance`](#0x1_primary_fungible_store_balance) +- [Function `is_balance_at_least`](#0x1_primary_fungible_store_is_balance_at_least) - [Function `is_frozen`](#0x1_primary_fungible_store_is_frozen) - [Function `withdraw`](#0x1_primary_fungible_store_withdraw) - [Function `deposit`](#0x1_primary_fungible_store_deposit) +- [Function `force_deposit`](#0x1_primary_fungible_store_force_deposit) - [Function `transfer`](#0x1_primary_fungible_store_transfer) +- [Function `transfer_assert_minimum_deposit`](#0x1_primary_fungible_store_transfer_assert_minimum_deposit) - [Function `mint`](#0x1_primary_fungible_store_mint) - [Function `burn`](#0x1_primary_fungible_store_burn) - [Function `set_frozen_flag`](#0x1_primary_fungible_store_set_frozen_flag) @@ -42,7 +48,8 @@ fungible asset to it. This emits an deposit event. - [Module-level Specification](#module-level-spec) -
    use 0x1::fungible_asset;
    +
    use 0x1::dispatchable_fungible_asset;
    +use 0x1::fungible_asset;
     use 0x1::object;
     use 0x1::option;
     use 0x1::signer;
    @@ -149,10 +156,11 @@ Ensure that the primary store object for the given address exists. If it doesn't
         owner: address,
         metadata: Object<T>,
     ): Object<FungibleStore> acquires DeriveRefPod {
    -    if (!primary_store_exists(owner, metadata)) {
    -        create_primary_store(owner, metadata)
    +    let store_addr = primary_store_address(owner, metadata);
    +    if (fungible_asset::store_exists(store_addr)) {
    +        object::address_to_object(store_addr)
         } else {
    -        primary_store(owner, metadata)
    +        create_primary_store(owner, metadata)
         }
     }
     
    @@ -185,7 +193,6 @@ Create a primary store object to hold fungible asset for the given address. object::address_to_object<Metadata>(metadata_addr); let derive_ref = &borrow_global<DeriveRefPod>(metadata_addr).metadata_derive_ref; let constructor_ref = &object::create_user_derived_object(owner_addr, derive_ref); - // Disable ungated transfer as deterministic stores shouldn't be transferrable. let transfer_ref = &object::generate_transfer_ref(constructor_ref); object::disable_ungated_transfer(transfer_ref); @@ -276,6 +283,86 @@ Return whether the given account's primary store exists. + + + + +## Function `primary_store_address_inlined` + +Get the address of the primary store for the given account. +Use instead of the corresponding view functions for dispatchable hooks to avoid circular dependencies of modules. + + +
    public fun primary_store_address_inlined<T: key>(owner: address, metadata: object::Object<T>): address
    +
    + + + +
    +Implementation + + +
    public inline fun primary_store_address_inlined<T: key>(owner: address, metadata: Object<T>): address {
    +    let metadata_addr = object::object_address(&metadata);
    +    object::create_user_derived_object_address(owner, metadata_addr)
    +}
    +
    + + + +
    + + + +## Function `primary_store_inlined` + +Get the primary store object for the given account. +Use instead of the corresponding view functions for dispatchable hooks to avoid circular dependencies of modules. + + +
    public fun primary_store_inlined<T: key>(owner: address, metadata: object::Object<T>): object::Object<fungible_asset::FungibleStore>
    +
    + + + +
    +Implementation + + +
    public inline fun primary_store_inlined<T: key>(owner: address, metadata: Object<T>): Object<FungibleStore> {
    +    let store = primary_store_address_inlined(owner, metadata);
    +    object::address_to_object(store)
    +}
    +
    + + + +
    + + + +## Function `primary_store_exists_inlined` + +Return whether the given account's primary store exists. +Use instead of the corresponding view functions for dispatchable hooks to avoid circular dependencies of modules. + + +
    public fun primary_store_exists_inlined<T: key>(account: address, metadata: object::Object<T>): bool
    +
    + + + +
    +Implementation + + +
    public inline fun primary_store_exists_inlined<T: key>(account: address, metadata: Object<T>): bool {
    +    fungible_asset::store_exists(primary_store_address_inlined(account, metadata))
    +}
    +
    + + +
    @@ -306,6 +393,35 @@ Get the balance of account's p + + + + +## Function `is_balance_at_least` + + + +
    #[view]
    +public fun is_balance_at_least<T: key>(account: address, metadata: object::Object<T>, amount: u64): bool
    +
    + + + +
    +Implementation + + +
    public fun is_balance_at_least<T: key>(account: address, metadata: Object<T>, amount: u64): bool {
    +    if (primary_store_exists(account, metadata)) {
    +        fungible_asset::is_balance_at_least(primary_store(account, metadata), amount)
    +    } else {
    +        amount == 0
    +    }
    +}
    +
    + + +
    @@ -354,11 +470,11 @@ Withdraw amount of fungible asset from the given account's primary Implementation -
    public fun withdraw<T: key>(owner: &signer, metadata: Object<T>, amount: u64): FungibleAsset {
    -    let store = primary_store(signer::address_of(owner), metadata);
    +
    public fun withdraw<T: key>(owner: &signer, metadata: Object<T>, amount: u64): FungibleAsset acquires DeriveRefPod {
    +    let store = ensure_primary_store_exists(signer::address_of(owner), metadata);
         // Check if the store object has been burnt or not. If so, unburn it first.
         may_be_unburn(owner, store);
    -    fungible_asset::withdraw(owner, store, amount)
    +    dispatchable_fungible_asset::withdraw(owner, store, amount)
     }
     
    @@ -385,7 +501,34 @@ Deposit fungible asset fa to the given account's primary store.
    public fun deposit(owner: address, fa: FungibleAsset) acquires DeriveRefPod {
         let metadata = fungible_asset::asset_metadata(&fa);
         let store = ensure_primary_store_exists(owner, metadata);
    -    fungible_asset::deposit(store, fa);
    +    dispatchable_fungible_asset::deposit(store, fa);
    +}
    +
    + + + + + + + +## Function `force_deposit` + +Deposit fungible asset fa to the given account's primary store. + + +
    public(friend) fun force_deposit(owner: address, fa: fungible_asset::FungibleAsset)
    +
    + + + +
    +Implementation + + +
    public(friend) fun force_deposit(owner: address, fa: FungibleAsset) acquires DeriveRefPod {
    +    let metadata = fungible_asset::asset_metadata(&fa);
    +    let store = ensure_primary_store_exists(owner, metadata);
    +    fungible_asset::deposit_internal(object::object_address(&store), fa);
     }
     
    @@ -419,7 +562,49 @@ Transfer amount of fungible asset from sender's primary store to re // Check if the sender store object has been burnt or not. If so, unburn it first. may_be_unburn(sender, sender_store); let recipient_store = ensure_primary_store_exists(recipient, metadata); - fungible_asset::transfer(sender, sender_store, recipient_store, amount); + dispatchable_fungible_asset::transfer(sender, sender_store, recipient_store, amount); +} +
    + + + + + + + +## Function `transfer_assert_minimum_deposit` + +Transfer amount of fungible asset from sender's primary store to receiver's primary store. +Use the minimum deposit assertion api to make sure receipient will receive a minimum amount of fund. + + +
    public entry fun transfer_assert_minimum_deposit<T: key>(sender: &signer, metadata: object::Object<T>, recipient: address, amount: u64, expected: u64)
    +
    + + + +
    +Implementation + + +
    public entry fun transfer_assert_minimum_deposit<T: key>(
    +    sender: &signer,
    +    metadata: Object<T>,
    +    recipient: address,
    +    amount: u64,
    +    expected: u64,
    +) acquires DeriveRefPod {
    +    let sender_store = ensure_primary_store_exists(signer::address_of(sender), metadata);
    +    // Check if the sender store object has been burnt or not. If so, unburn it first.
    +    may_be_unburn(sender, sender_store);
    +    let recipient_store = ensure_primary_store_exists(recipient, metadata);
    +    dispatchable_fungible_asset::transfer_assert_minimum_deposit(
    +        sender,
    +        sender_store,
    +        recipient_store,
    +        amount,
    +        expected
    +    );
     }
     
    @@ -638,7 +823,7 @@ Transfer amount of FA from the primary store of from t 1 Creating a fungible asset with primary store support should initiate a derived reference and store it under the metadata object. Medium -The function create_primary_store_enabled_fungible_asset makes an existing object, fungible, via for the object and then stores it under the object address. +The function create_primary_store_enabled_fungible_asset makes an existing object, fungible, via the fungible_asset::add_fungibility function and initializes the DeriveRefPod resource by generating a DeriveRef for the object and then stores it under the object address. Audited that the DeriveRefPod has been properly initialized and stored under the metadata object. @@ -678,7 +863,7 @@ Transfer amount of FA from the primary store of from t 6 The action of depositing a fungible asset of the same type as the store should never fail if the store is not frozen. Medium -The function deposit fetches the owner's store, if it doesn't exist it will be created, and then deposits the fungible asset to it. The function deposit_with_ref fetches the owner's store, if it doesn't exist Depositing fails if the metadata of the FungibleStore and FungibleAsset differs. +The function deposit fetches the owner's store, if it doesn't exist it will be created, and then deposits the fungible asset to it. The function deposit_with_ref fetches the owner's store, if it doesn't exist it will be created, and then deposit the fungible asset via the fungible_asset::deposit_with_ref function. Depositing fails if the metadata of the FungibleStore and FungibleAsset differs. Audited that it aborts if the store is frozen (deposit). Audited that the balance of the store is increased by the deposit amount (deposit, deposit_with_ref). Audited that it aborts if the metadata of the store and the asset differs (deposit, deposit_with_ref). @@ -686,7 +871,7 @@ Transfer amount of FA from the primary store of from t 7 Withdrawing should only be allowed to the owner of an existing store with sufficient balance. Critical -The withdraw function fetches the owner's store via the primary_store function and then calls store. The withdraw_with_ref function fetches the store of the owner via primary_store function and calls the and the balance of the store. +The withdraw function fetches the owner's store via the primary_store function and then calls fungible_asset::withdraw which validates the owner of the store, checks the frozen status and the balance of the store. The withdraw_with_ref function fetches the store of the owner via primary_store function and calls the fungible_asset::withdraw_with_ref which validates transfer_ref's metadata with the withdrawing stores metadata, and the balance of the store. Audited that it aborts if the owner doesn't own the store (withdraw). Audited that it aborts if the store is frozen (withdraw). Audited that it aborts if the transfer ref's metadata doesn't match the withdrawing store's metadata (withdraw_with_ref). Audited that it aborts if the store doesn't have sufficient balance. Audited that the store is not burned. Audited that the balance of the store is decreased by the amount withdrawn. @@ -694,7 +879,7 @@ Transfer amount of FA from the primary store of from t 8 Only the fungible store owner is allowed to unburn a burned store. High -The function may_be_unburn checks if the store is burned and then proceeds to call +The function may_be_unburn checks if the store is burned and then proceeds to call object::unburn which ensures that the owner of the object matches the address of the signer. Audited that the store is unburned successfully. @@ -702,7 +887,7 @@ Transfer amount of FA from the primary store of from t 9 Only the owner of a primary store can transfer its balance to any recipient's primary store. High -The function transfer fetches sender and recipient's primary stores, if the sender's store is withdraws the assets from the sender's store and then deposits to the recipient's store. The function transfer_with_ref fetches the sender's and recipient's stores and calls the the asset to the recipient with the ref. +The function transfer fetches sender and recipient's primary stores, if the sender's store is burned it unburns the store and calls the fungile_asset::transfer to proceed with the transfer, which first withdraws the assets from the sender's store and then deposits to the recipient's store. The function transfer_with_ref fetches the sender's and recipient's stores and calls the fungible_asset::transfer_with_ref function which withdraws the asset with the ref from the sender and deposits the asset to the recipient with the ref. Audited the deposit and withdraw (transfer). Audited the deposit_with_ref and withdraw_with_ref (transfer_with_ref). Audited that the store balance of the sender is decreased by the specified amount and its added to the recipients store. (transfer, transfer_with_ref) Audited that the sender's store is not burned (transfer). @@ -727,7 +912,7 @@ Transfer amount of FA from the primary store of from t Setting the frozen flag of a store is only allowed with a valid reference. High The function set_frozen_flag fetches the primary store and calls fungible_asset::set_frozen_flag which validates the TransferRef's metadata with the store's metadata and then updates the frozen flag. -Audited that it aborts if the store's metadata is not same as the the TransferRef's metadata. Audited that the status of the frozen flag is updated correctly. +Audited that it aborts if the store's metadata is not same as the TransferRef's metadata. Audited that the status of the frozen flag is updated correctly. @@ -744,4 +929,27 @@ Transfer amount of FA from the primary store of from t
    + + + + + +
    fun spec_primary_store_exists<T: key>(account: address, metadata: Object<T>): bool {
    +   fungible_asset::store_exists(spec_primary_store_address(account, metadata))
    +}
    +
    + + + + + + + +
    fun spec_primary_store_address<T: key>(owner: address, metadata: Object<T>): address {
    +   let metadata_addr = object::object_address(metadata);
    +   object::spec_create_user_derived_object_address(owner, metadata_addr)
    +}
    +
    + + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/randomness.md b/aptos-move/framework/aptos-framework/doc/randomness.md index 85ca49b5b99cc..bb90390ae5d3f 100644 --- a/aptos-move/framework/aptos-framework/doc/randomness.md +++ b/aptos-move/framework/aptos-framework/doc/randomness.md @@ -49,6 +49,7 @@ Security holds under the same proof-of-stake assumption that secures the Aptos n - [Function `u64_integer`](#@Specification_1_u64_integer) - [Function `u128_integer`](#@Specification_1_u128_integer) - [Function `u256_integer`](#@Specification_1_u256_integer) + - [Function `u256_integer_internal`](#@Specification_1_u256_integer_internal) - [Function `u8_range`](#@Specification_1_u8_range) - [Function `u64_range`](#@Specification_1_u64_range) - [Function `u256_range`](#@Specification_1_u256_range) @@ -188,13 +189,13 @@ Event emitted every time a public randomness API in this module is called. - + Randomness APIs calls must originate from a private entry function with -#[randomness] annotation. Otherwise, test-and-abort attacks are possible. +#[randomness] annotation. Otherwise, malicious users can bias randomness result. -
    const E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT: u64 = 1;
    +
    const E_API_USE_IS_BIASIBLE: u64 = 1;
     
    @@ -281,7 +282,7 @@ of the hash function).
    fun next_32_bytes(): vector<u8> acquires PerBlockRandomness {
    -    assert!(is_unbiasable(), E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT);
    +    assert!(is_unbiasable(), E_API_USE_IS_BIASIBLE);
     
         let input = DST;
         let randomness = borrow_global<PerBlockRandomness>(@aptos_framework);
    @@ -494,11 +495,6 @@ Generates an u128 uniformly at random.
         let i = 0;
         let ret: u128 = 0;
         while (i < 16) {
    -        spec {
    -            // TODO: Prove these with proper loop invaraints.
    -            assume ret * 256 + 255 <= MAX_U256;
    -            assume len(raw) > 0;
    -        };
             ret = ret * 256 + (vector::pop_back(&mut raw) as u128);
             i = i + 1;
         };
    @@ -560,11 +556,6 @@ Generates a u256 uniformly at random.
         let i = 0;
         let ret: u256 = 0;
         while (i < 32) {
    -        spec {
    -            // TODO: Prove these with proper loop invaraints.
    -            assume ret * 256 + 255 <= MAX_U256;
    -            assume len(raw) > 0;
    -        };
             ret = ret * 256 + (vector::pop_back(&mut raw) as u256);
             i = i + 1;
         };
    @@ -949,7 +940,7 @@ Compute (a + b) % m, assuming m >= 1, 0 <= a < m, 0&
     ## Function `fetch_and_increment_txn_counter`
     
     Fetches and increments a transaction-specific 32-byte randomness-related counter.
    -Aborts with E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT if randomness is not unbiasable.
    +Aborts with E_API_USE_SUSCEPTIBLE_TO_TEST_AND_ABORT if randomness is not unbiasable.
     
     
     
    fun fetch_and_increment_txn_counter(): vector<u8>
    @@ -1176,13 +1167,32 @@ function as its payload.
     
     
     
    -
    pragma unroll = 32;
    +
    pragma verify_duration_estimate = 300;
    +pragma unroll = 32;
     include NextBlobAbortsIf;
     ensures [abstract] result == spec_u256_integer();
     
    + + +### Function `u256_integer_internal` + + +
    fun u256_integer_internal(): u256
    +
    + + + + +
    pragma verify_duration_estimate = 300;
    +pragma unroll = 32;
    +include NextBlobAbortsIf;
    +
    + + + @@ -1223,7 +1233,8 @@ function as its payload. -
    include NextBlobAbortsIf;
    +
    pragma verify_duration_estimate = 120;
    +include NextBlobAbortsIf;
     aborts_if min_incl >= max_excl;
     ensures result >= min_incl && result < max_excl;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/randomness_config.md b/aptos-move/framework/aptos-framework/doc/randomness_config.md index 2d1c420046ce0..b658b03cdd6c0 100644 --- a/aptos-move/framework/aptos-framework/doc/randomness_config.md +++ b/aptos-move/framework/aptos-framework/doc/randomness_config.md @@ -9,6 +9,7 @@ Structs and functions for on-chain randomness configurations. - [Resource `RandomnessConfig`](#0x1_randomness_config_RandomnessConfig) - [Struct `ConfigOff`](#0x1_randomness_config_ConfigOff) - [Struct `ConfigV1`](#0x1_randomness_config_ConfigV1) +- [Struct `ConfigV2`](#0x1_randomness_config_ConfigV2) - [Constants](#@Constants_0) - [Function `initialize`](#0x1_randomness_config_initialize) - [Function `set_for_next_epoch`](#0x1_randomness_config_set_for_next_epoch) @@ -16,8 +17,10 @@ Structs and functions for on-chain randomness configurations. - [Function `enabled`](#0x1_randomness_config_enabled) - [Function `new_off`](#0x1_randomness_config_new_off) - [Function `new_v1`](#0x1_randomness_config_new_v1) +- [Function `new_v2`](#0x1_randomness_config_new_v2) - [Function `current`](#0x1_randomness_config_current) - [Specification](#@Specification_1) + - [Function `on_new_epoch`](#@Specification_1_on_new_epoch) - [Function `current`](#@Specification_1_current) @@ -121,6 +124,46 @@ A randomness config variant indicating the feature is enabled. + + + + +## Struct `ConfigV2` + +A randomness config variant indicating the feature is enabled with fast path. + + +
    struct ConfigV2 has copy, drop, store
    +
    + + + +
    +Fields + + +
    +
    +secrecy_threshold: fixed_point64::FixedPoint64 +
    +
    + Any validator subset should not be able to reconstruct randomness if subset_power / total_power <= secrecy_threshold, +
    +
    +reconstruction_threshold: fixed_point64::FixedPoint64 +
    +
    + Any validator subset should be able to reconstruct randomness if subset_power / total_power > reconstruction_threshold. +
    +
    +fast_path_secrecy_threshold: fixed_point64::FixedPoint64 +
    +
    + Any validator subset should not be able to reconstruct randomness via the fast path if subset_power / total_power <= fast_path_secrecy_threshold, +
    +
    + +
    @@ -198,7 +241,7 @@ This can be called by on-chain governance to update on-chain consensus configs f Only used in reconfigurations to apply the pending RandomnessConfig, if there is any. -
    public(friend) fun on_new_epoch()
    +
    public(friend) fun on_new_epoch(framework: &signer)
     
    @@ -207,10 +250,15 @@ Only used in reconfigurations to apply the pending on_new_epoch() acquires RandomnessConfig { +
    public(friend) fun on_new_epoch(framework: &signer) acquires RandomnessConfig {
    +    system_addresses::assert_aptos_framework(framework);
         if (config_buffer::does_exist<RandomnessConfig>()) {
             let new_config = config_buffer::extract<RandomnessConfig>();
    -        borrow_global_mut<RandomnessConfig>(@aptos_framework).variant = new_config.variant;
    +        if (exists<RandomnessConfig>(@aptos_framework)) {
    +            *borrow_global_mut<RandomnessConfig>(@aptos_framework) = new_config;
    +        } else {
    +            move_to(framework, new_config);
    +        }
         }
     }
     
    @@ -308,6 +356,41 @@ Create a Con + + + + +## Function `new_v2` + +Create a ConfigV2 variant. + + +
    public fun new_v2(secrecy_threshold: fixed_point64::FixedPoint64, reconstruction_threshold: fixed_point64::FixedPoint64, fast_path_secrecy_threshold: fixed_point64::FixedPoint64): randomness_config::RandomnessConfig
    +
    + + + +
    +Implementation + + +
    public fun new_v2(
    +    secrecy_threshold: FixedPoint64,
    +    reconstruction_threshold: FixedPoint64,
    +    fast_path_secrecy_threshold: FixedPoint64,
    +): RandomnessConfig {
    +    RandomnessConfig {
    +        variant: copyable_any::pack( ConfigV2 {
    +            secrecy_threshold,
    +            reconstruction_threshold,
    +            fast_path_secrecy_threshold,
    +        } )
    +    }
    +}
    +
    + + +
    @@ -344,6 +427,24 @@ Get the currently effective randomness configuration object. ## Specification + + +### Function `on_new_epoch` + + +
    public(friend) fun on_new_epoch(framework: &signer)
    +
    + + + + +
    requires @aptos_framework == std::signer::address_of(framework);
    +include config_buffer::OnNewEpochRequirement<RandomnessConfig>;
    +aborts_if false;
    +
    + + + ### Function `current` diff --git a/aptos-move/framework/aptos-framework/doc/reconfiguration.md b/aptos-move/framework/aptos-framework/doc/reconfiguration.md index 8bc6fe963b08b..3415e0a820de1 100644 --- a/aptos-move/framework/aptos-framework/doc/reconfiguration.md +++ b/aptos-move/framework/aptos-framework/doc/reconfiguration.md @@ -8,6 +8,7 @@ to synchronize configuration changes for the validators. - [Struct `NewEpochEvent`](#0x1_reconfiguration_NewEpochEvent) +- [Struct `NewEpoch`](#0x1_reconfiguration_NewEpoch) - [Resource `Configuration`](#0x1_reconfiguration_Configuration) - [Resource `DisableReconfiguration`](#0x1_reconfiguration_DisableReconfiguration) - [Constants](#@Constants_0) @@ -57,7 +58,39 @@ with new configuration information. This is also called a "reconfiguration event" -
    struct NewEpochEvent has drop, store
    +
    #[event]
    +struct NewEpochEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +epoch: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `NewEpoch` + +Event that signals consensus to start a new epoch, +with new configuration information. This is also called a +"reconfiguration event" + + +
    #[event]
    +struct NewEpoch has drop, store
     
    @@ -388,6 +421,13 @@ Signal validators to start using new configuration. Must be called from friend c }; config_ref.epoch = config_ref.epoch + 1; + if (std::features::module_event_migration_enabled()) { + event::emit( + NewEpoch { + epoch: config_ref.epoch, + }, + ); + }; event::emit_event<NewEpochEvent>( &mut config_ref.events, NewEpochEvent { @@ -473,6 +513,13 @@ reconfiguration event. assert!(config_ref.epoch == 0 && config_ref.last_reconfiguration_time == 0, error::invalid_state(ECONFIGURATION)); config_ref.epoch = 1; + if (std::features::module_event_migration_enabled()) { + event::emit( + NewEpoch { + epoch: config_ref.epoch, + }, + ); + }; event::emit_event<NewEpochEvent>( &mut config_ref.events, NewEpochEvent { diff --git a/aptos-move/framework/aptos-framework/doc/reconfiguration_with_dkg.md b/aptos-move/framework/aptos-framework/doc/reconfiguration_with_dkg.md index f64d57763b192..005fd6f824902 100644 --- a/aptos-move/framework/aptos-framework/doc/reconfiguration_with_dkg.md +++ b/aptos-move/framework/aptos-framework/doc/reconfiguration_with_dkg.md @@ -22,11 +22,15 @@ Reconfiguration with DKG helper functions. use 0x1::gas_schedule; use 0x1::jwk_consensus_config; use 0x1::jwks; +use 0x1::keyless_account; use 0x1::option; +use 0x1::randomness_api_v0_config; use 0x1::randomness_config; +use 0x1::randomness_config_seqnum; use 0x1::reconfiguration; use 0x1::reconfiguration_state; use 0x1::stake; +use 0x1::system_addresses; use 0x1::validator_consensus_info; use 0x1::version;
    @@ -83,7 +87,7 @@ Re-enable validator set changes. Run the default reconfiguration to enter the new epoch. -
    public(friend) fun finish(account: &signer)
    +
    public(friend) fun finish(framework: &signer)
     
    @@ -92,16 +96,20 @@ Run the default reconfiguration to enter the new epoch. Implementation -
    public(friend) fun finish(account: &signer) {
    -    dkg::try_clear_incomplete_session(account);
    -    consensus_config::on_new_epoch();
    -    execution_config::on_new_epoch();
    -    gas_schedule::on_new_epoch();
    -    std::version::on_new_epoch();
    -    jwk_consensus_config::on_new_epoch();
    -    jwks::on_new_epoch();
    -    randomness_config::on_new_epoch();
    -    features::on_new_epoch(account);
    +
    public(friend) fun finish(framework: &signer) {
    +    system_addresses::assert_aptos_framework(framework);
    +    dkg::try_clear_incomplete_session(framework);
    +    consensus_config::on_new_epoch(framework);
    +    execution_config::on_new_epoch(framework);
    +    gas_schedule::on_new_epoch(framework);
    +    std::version::on_new_epoch(framework);
    +    features::on_new_epoch(framework);
    +    jwk_consensus_config::on_new_epoch(framework);
    +    jwks::on_new_epoch(framework);
    +    keyless_account::on_new_epoch(framework);
    +    randomness_config_seqnum::on_new_epoch(framework);
    +    randomness_config::on_new_epoch(framework);
    +    randomness_api_v0_config::on_new_epoch(framework);
         reconfiguration::reconfigure();
     }
     
    @@ -177,14 +185,15 @@ Abort if no DKG is in progress. ### Function `finish` -
    public(friend) fun finish(account: &signer)
    +
    public(friend) fun finish(framework: &signer)
     
    -
    pragma verify_duration_estimate = 600;
    +
    pragma verify_duration_estimate = 1500;
     include FinishRequirement;
    +aborts_if false;
     
    @@ -194,8 +203,8 @@ Abort if no DKG is in progress.
    schema FinishRequirement {
    -    account: signer;
    -    requires signer::address_of(account) == @aptos_framework;
    +    framework: signer;
    +    requires signer::address_of(framework) == @aptos_framework;
         requires chain_status::is_operating();
         requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
         include staking_config::StakingRewardsConfigRequirement;
    @@ -207,7 +216,13 @@ Abort if no DKG is in progress.
         include config_buffer::OnNewEpochRequirement<execution_config::ExecutionConfig>;
         include config_buffer::OnNewEpochRequirement<consensus_config::ConsensusConfig>;
         include config_buffer::OnNewEpochRequirement<jwks::SupportedOIDCProviders>;
    -    aborts_if false;
    +    include config_buffer::OnNewEpochRequirement<randomness_config::RandomnessConfig>;
    +    include config_buffer::OnNewEpochRequirement<randomness_config_seqnum::RandomnessConfigSeqNum>;
    +    include config_buffer::OnNewEpochRequirement<randomness_api_v0_config::AllowCustomMaxGasFlag>;
    +    include config_buffer::OnNewEpochRequirement<randomness_api_v0_config::RequiredGasDeposit>;
    +    include config_buffer::OnNewEpochRequirement<jwk_consensus_config::JWKConsensusConfig>;
    +    include config_buffer::OnNewEpochRequirement<keyless_account::Configuration>;
    +    include config_buffer::OnNewEpochRequirement<keyless_account::Groth16VerificationKey>;
     }
     
    @@ -224,8 +239,10 @@ Abort if no DKG is in progress. -
    pragma verify_duration_estimate = 600;
    -include FinishRequirement;
    +
    pragma verify_duration_estimate = 1500;
    +include FinishRequirement {
    +    framework: account
    +};
     requires dkg::has_incomplete_session();
     aborts_if false;
     
    diff --git a/aptos-move/framework/aptos-framework/doc/resource_account.md b/aptos-move/framework/aptos-framework/doc/resource_account.md index beee8959b49cb..fa171c0b03cdc 100644 --- a/aptos-move/framework/aptos-framework/doc/resource_account.md +++ b/aptos-move/framework/aptos-framework/doc/resource_account.md @@ -354,7 +354,10 @@ the SignerCapability. let resource_addr = signer::address_of(resource); let (resource_signer_cap, empty_container) = { let container = borrow_global_mut<Container>(source_addr); - assert!(simple_map::contains_key(&container.store, &resource_addr), error::invalid_argument(EUNAUTHORIZED_NOT_OWNER)); + assert!( + simple_map::contains_key(&container.store, &resource_addr), + error::invalid_argument(EUNAUTHORIZED_NOT_OWNER) + ); let (_resource_addr, signer_cap) = simple_map::remove(&mut container.store, &resource_addr); (signer_cap, simple_map::length(&container.store) == 0) }; @@ -499,13 +502,14 @@ the SignerCapability. -
    let source_addr = signer::address_of(origin);
    +
    pragma verify = false;
    +let source_addr = signer::address_of(origin);
     let resource_addr = account::spec_create_resource_address(source_addr, seed);
     let coin_store_resource = global<coin::CoinStore<AptosCoin>>(resource_addr);
     include aptos_account::WithdrawAbortsIf<AptosCoin>{from: origin, amount: fund_amount};
     include aptos_account::GuidAbortsIf<AptosCoin>{to: resource_addr};
     include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIfWithoutAccountLimit;
    -aborts_if coin::is_account_registered<AptosCoin>(resource_addr) && coin_store_resource.frozen;
    +aborts_if coin::spec_is_account_registered<AptosCoin>(resource_addr) && coin_store_resource.frozen;
     // This enforces high-level requirement 3:
     ensures exists<aptos_framework::coin::CoinStore<AptosCoin>>(resource_addr);
     
    diff --git a/aptos-move/framework/aptos-framework/doc/stake.md b/aptos-move/framework/aptos-framework/doc/stake.md index b6193d4b3eb22..5a199ef91f69d 100644 --- a/aptos-move/framework/aptos-framework/doc/stake.md +++ b/aptos-move/framework/aptos-framework/doc/stake.md @@ -33,17 +33,29 @@ or if their stake drops below the min required, they would get removed at the en - [Struct `IndividualValidatorPerformance`](#0x1_stake_IndividualValidatorPerformance) - [Resource `ValidatorPerformance`](#0x1_stake_ValidatorPerformance) - [Struct `RegisterValidatorCandidateEvent`](#0x1_stake_RegisterValidatorCandidateEvent) +- [Struct `RegisterValidatorCandidate`](#0x1_stake_RegisterValidatorCandidate) - [Struct `SetOperatorEvent`](#0x1_stake_SetOperatorEvent) +- [Struct `SetOperator`](#0x1_stake_SetOperator) - [Struct `AddStakeEvent`](#0x1_stake_AddStakeEvent) +- [Struct `AddStake`](#0x1_stake_AddStake) - [Struct `ReactivateStakeEvent`](#0x1_stake_ReactivateStakeEvent) +- [Struct `ReactivateStake`](#0x1_stake_ReactivateStake) - [Struct `RotateConsensusKeyEvent`](#0x1_stake_RotateConsensusKeyEvent) +- [Struct `RotateConsensusKey`](#0x1_stake_RotateConsensusKey) - [Struct `UpdateNetworkAndFullnodeAddressesEvent`](#0x1_stake_UpdateNetworkAndFullnodeAddressesEvent) +- [Struct `UpdateNetworkAndFullnodeAddresses`](#0x1_stake_UpdateNetworkAndFullnodeAddresses) - [Struct `IncreaseLockupEvent`](#0x1_stake_IncreaseLockupEvent) +- [Struct `IncreaseLockup`](#0x1_stake_IncreaseLockup) - [Struct `JoinValidatorSetEvent`](#0x1_stake_JoinValidatorSetEvent) +- [Struct `JoinValidatorSet`](#0x1_stake_JoinValidatorSet) - [Struct `DistributeRewardsEvent`](#0x1_stake_DistributeRewardsEvent) +- [Struct `DistributeRewards`](#0x1_stake_DistributeRewards) - [Struct `UnlockStakeEvent`](#0x1_stake_UnlockStakeEvent) +- [Struct `UnlockStake`](#0x1_stake_UnlockStake) - [Struct `WithdrawStakeEvent`](#0x1_stake_WithdrawStakeEvent) +- [Struct `WithdrawStake`](#0x1_stake_WithdrawStake) - [Struct `LeaveValidatorSetEvent`](#0x1_stake_LeaveValidatorSetEvent) +- [Struct `LeaveValidatorSet`](#0x1_stake_LeaveValidatorSet) - [Resource `ValidatorFees`](#0x1_stake_ValidatorFees) - [Resource `AllowedValidators`](#0x1_stake_AllowedValidators) - [Resource `Ghost$ghost_valid_perf`](#0x1_stake_Ghost$ghost_valid_perf) @@ -602,6 +614,34 @@ This allows the Stake module to mint rewards to stakers. +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    + + +
    + + + +## Struct `RegisterValidatorCandidate` + + + +
    #[event]
    +struct RegisterValidatorCandidate has drop, store
    +
    + + +
    Fields @@ -620,11 +660,446 @@ This allows the Stake module to mint rewards to stakers. -## Struct `SetOperatorEvent` +## Struct `SetOperatorEvent` + + + +
    struct SetOperatorEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_operator: address +
    +
    + +
    +
    +new_operator: address +
    +
    + +
    +
    + + +
    + + + +## Struct `SetOperator` + + + +
    #[event]
    +struct SetOperator has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_operator: address +
    +
    + +
    +
    +new_operator: address +
    +
    + +
    +
    + + +
    + + + +## Struct `AddStakeEvent` + + + +
    struct AddStakeEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount_added: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `AddStake` + + + +
    #[event]
    +struct AddStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount_added: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `ReactivateStakeEvent` + + + +
    struct ReactivateStakeEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `ReactivateStake` + + + +
    #[event]
    +struct ReactivateStake has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +amount: u64 +
    +
    + +
    +
    + + +
    + + + +## Struct `RotateConsensusKeyEvent` + + + +
    struct RotateConsensusKeyEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_consensus_pubkey: vector<u8> +
    +
    + +
    +
    +new_consensus_pubkey: vector<u8> +
    +
    + +
    +
    + + +
    + + + +## Struct `RotateConsensusKey` + + + +
    #[event]
    +struct RotateConsensusKey has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_consensus_pubkey: vector<u8> +
    +
    + +
    +
    +new_consensus_pubkey: vector<u8> +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateNetworkAndFullnodeAddressesEvent` + + + +
    struct UpdateNetworkAndFullnodeAddressesEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_network_addresses: vector<u8> +
    +
    + +
    +
    +new_network_addresses: vector<u8> +
    +
    + +
    +
    +old_fullnode_addresses: vector<u8> +
    +
    + +
    +
    +new_fullnode_addresses: vector<u8> +
    +
    + +
    +
    + + +
    + + + +## Struct `UpdateNetworkAndFullnodeAddresses` + + + +
    #[event]
    +struct UpdateNetworkAndFullnodeAddresses has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_network_addresses: vector<u8> +
    +
    + +
    +
    +new_network_addresses: vector<u8> +
    +
    + +
    +
    +old_fullnode_addresses: vector<u8> +
    +
    + +
    +
    +new_fullnode_addresses: vector<u8> +
    +
    + +
    +
    + + +
    + + + +## Struct `IncreaseLockupEvent` + + + +
    struct IncreaseLockupEvent has drop, store
    +
    + + + +
    +Fields + + +
    +
    +pool_address: address +
    +
    + +
    +
    +old_locked_until_secs: u64 +
    +
    + +
    +
    +new_locked_until_secs: u64 +
    +
    + +
    +
    + + +
    + +## Struct `IncreaseLockup` -
    struct SetOperatorEvent has drop, store
    +
    +
    +
    #[event]
    +struct IncreaseLockup has drop, store
     
    @@ -641,13 +1116,13 @@ This allows the Stake module to mint rewards to stakers.